commit b60439152b2a6b11f1147f08340d8974ab8c27e0 Author: Simbiot Harness Date: Tue Sep 29 15:28:50 2026 +0000 Simbiot Harness v0.1: тактовая лестница на Strands + CQRS/ES-спина - harness/es.py: append-only event store + проекции (образец — кооп-портал) - harness/levels.py: уровни 1с/1м/1ч/1д, контекст ∝ масштабу - harness/scheduler.py: уровневые потоки, идемпотентные тики, absorbed-стабы - harness/isolated_runner.py + _llm_worker.py: LLM-тик в сабпроцессе (глобальный asyncio-рантайм Strands не переживает 2+ create_harness) - cli.py: init/task/tasks/status/events + requeue (написан самим минутным агентом) - 9router: отдельный ключ sk-simbiot, модель auto (glm-5.3 первым) diff --git a/__pycache__/cli.cpython-312.pyc b/__pycache__/cli.cpython-312.pyc new file mode 100644 index 0000000..4c1d888 Binary files /dev/null and b/__pycache__/cli.cpython-312.pyc differ diff --git a/architecture.html b/architecture.html new file mode 100644 index 0000000..fb5323e --- /dev/null +++ b/architecture.html @@ -0,0 +1,426 @@ + + + + + +Simbiot Harness — архитектура v0.1 (ревью) + + + +
+ +
+
Архитектура · для ревью
+

Simbiot Harness

+
Принципиально новый агентный харнес на CQRS + Event Sourcing: агент живёт в тактах разного масштаба — секунда, минута, час, день, неделя, месяц, квартал, год — как человек. Стартовая точка — strands-agents/harness-sdk (Apache-2.0): его исполнительное ядро, поверх — наша временная ось и событийная спина.
+
+ CQRS + Event Sourcing + Strands Harness SDK + 9router — LLM-шлюз + Python 3.12 · SQLite WAL + v0.1 · ревью пройдено 29.09 +
+
+ + +
+
01 / СУТЬ
+

Что мы строим

+

Все существующие харнесы (Strands, Hermes, OpenClaw) — пассивны: они просыпаются, когда пришёл внешний запрос, и не имеют собственной временной оси. Simbiot Harness — экзистенциальный: он тикает сам.

+

Харнес — это не «функция, которую вызывают», а живой процесс с ритмами. Восемь вложенных циклов бьют одновременно: секундный — как сердцебиение, минутный — как реакция, часовой — как рабочий поток, дневной — как план дня, недельный — как ретро, месячный — как цели, квартальный — как стратегия, годовой — как идентичность. Чем крупнее цикл, тем больше контекста он может себе позволить и тем крупнее задачу он вправе взять.

+

Всё, что происходит — тики, решения, наблюдения, память, трата бюджета — это события в append-only сторе. Состояние харнеса не хранится, а выводится replay'ем событий. Выключили процесс — включили — он продолжил с того же места, потому что его «личность» — это события, а не RAM.

+
Одной фразой: Strands даёт нам тело (агентный цикл, инструменты, хуки). Simbiot Harness даёт ему метаболизм — время, память и ритм — на событийной спине.
+
+ + +
+
02 / ПРИНЦИПЫ
+

Шесть принципов

+
+
P-1

Всё есть событие

Тик, взятая задача, дайджест, трасса каждого tool-call агента — события. Event store — единственный источник правды. Никакого «состояния в RAM».

+
P-2

Тактовая лестница

8 уровней: 1с → 1м → 1ч → 1д → 1н → 1мес → 1кв → 1г. У каждого — свой мандат, бюджет и модель. Мелкое решает мелкое, крупное — крупное.

+
P-3

Контекст ∝ масштабу

Секундный цикл не зовёт LLM вовсе, минутный получает 4K токенов, дневной — 128K, годовой — до 1M. Задача S-класса не занимает слот годового цикла.

+
P-4

Непрерываемость

Запущенная задача не прерывается тиками. Если исполнитель уровня занят — тик поглощается (skip), а не копится в очередь. Работа важнее метронома.

+
P-5

Переваривание

Верхние циклы едят выжимку нижних: дневной читает не сырые минуты, а часовые дайджесты. Пирамида консолидации памяти — как сон у человека.

+
P-6

Воспроизводимость

Стор можно переиграть с нуля — и харнес, и память, и решения соберутся заново. Отладка агента = чтение потока событий, а не логов.

+
+
+ + +
+
03 / ЛЕСТНИЦА
+

Тактовая лестница

+

Каждый уровень — отдельная «личность»: свой мандат (что ему можно), свой бюджет контекста, своя модель и свой класс задач. Цикл может взять задачу своего класса и ниже; задача выше класса — ожидает уровень повыше.

+ + + + + + + + + + +
ЦиклМандатМозг (9router)КонтекстЗадачиАналогия у человека
1 секРефлекс: телеметрия, watchdog, heartbeat, алертыбез LLM — детерминированный код0микро замер, триггерСердцебиение
1 минПульс: триаж входящих, быстрые ответы, мелкие фиксыglm-5.3-flash≤ 4KS ≤ 5 мин работыРеакция в разговоре
1 часДайджест: сборка часа, батч мелочей, проверка прогрессаglm-5.3-flash≤ 32KM ≤ 1 чРабочий поток
1 деньМанифест: план дня, выбор задач, дневной отчётglm-5.3 (full)≤ 128KL ≤ 1 деньПлан на день
1 неделяРетро: разбор недели, обучение, обновление скилловglm-5.3 · effort: high≤ 200KXL неделяНедельное ретро
1 месяцЦели: бюджет, OKR, ранги, накопленияfull≤ 256KXXL месяцМесячная цель
1 кварталСтратегия: пересмотр направления, ставкиpro-слот*≤ 512KXXL кварталКвартальная стратегия
1 годИдентичность: миссия, «зачем существую», самоопределениеpro-слот*≤ 1MXXL годЖизненный выбор
+

* pro-слот — зарезервировано под сильную модель (Claude Opus как личный брейн / Gemini 3 Pro через KIE): годовой цикл тикает 1 раз, дорогой запуск раз в год — приемлемо.

+
+ + +
+
04 / ДИАГРАММА
+

Архитектура

+
+ + + + + + + + + + CQRS — ПЛОСКОСТЬ КОМАНД И ЗАПРОСОВ + + + ИНТЕРФЕЙСЫ + Telegram · Matrix · Web + cron · webhook · человек + + + COMMAND SIDE + валидация · идемпотентность + submit_task · set_policy + + + EVENT STORE + APPEND-ONLY · СПИНА + events(aggregate, id, + type, data, version) + CycleTicked · TaskClaimed + DigestWritten · BudgetSpent + replay → вся «личность» + харнеса восстанавливается + SQLite · WAL + + + ПРОЕКЦИИ + task_board · digest_* + metrics · agent_state + (перестраиваются replay'ем) + + + QUERY + API · дашборд + человек смотрит + + + + APPEND + + STREAM→ + + + + ВРЕМЕННАЯ ПЛОСКОСТЬ — ТАКТОВАЯ ЛЕСТНИЦА + + + SCHEDULER + 8 уровней бьют по границам + тактов: 1с 1м 1ч 1д 1н + 1мес 1кв 1г + тик занят → absorbed + (skip, не очередь) + asyncio · один процесс + + + CYCLE RUNNERS + по одному исполнителю + на уровень → сериализация, + задача не прерывается + мандат + бюджет + + класс задач — из конфига + уровня (P-2, P-3) + + + STRANDS RUNTIME + create_harness() на каждый тик: + agent loop · tools · MCP + session (задача) · effort + context_manager · guardrails + HOOKS → каждая стадия цикла + агента пишет трассу событием + + + LLM ШЛЮЗ + 9router: flash / full / pro + модель задаётся уровнем + цикла — не задачей + + + ИНСТРУМЕНТЫ + shell · files · web · MCP · скиллы + + + TICK + + + LLM + + + + + + CycleTicked → APPEND + + + читает контекст: дайджесты нижних уровней + задачи класса + + + HOOKS: трасса агента → события + + +
РИС. 1 — Две плоскости. CQRS: интерфейсы → команды → event store → проекции → запросы. Временная: планировщик → исполнители циклов → Strands-агент → LLM. Замкнутый контур: события порождают циклы, циклы порождают события.
+
+
+ + +
+
05 / ТИК
+

Жизненный цикл тика

+

Один тик уровня — от границы такта до записи результата. Ключевое правило — непрерываемость: занятый исполнитель поглощает тик.

+
+
Граница тактаScheduler ловит момент 1с/1м/…/1г. Эмитит событие CycleTicked{level, seq}.
+
Исполнитель свободен?Занят → TickAbsorbed: детерминированная дайджест-заглушка (без LLM), очередь не копим. Дорога (v0.2+): срочная реакция сможет прервать бегущую задачу или поставить её на паузу.
+
Сборка контекстаМандат уровня + идентичность + память уровня + выжимка нижних (дайджесты) + задачи доступного класса. В пределах бюджета P-3.
+
Выбор задачиЕсть подходящая задача → TaskClaimed + сессия. Нет → цикл делает мандатную работу (дайджест/наблюдение) или проходит впустую — это честно.
+
Strands-циклcreate_harness(model, effort, tools уровня) → invoke. Каждый шаг — hook → событие. Задача не прерывается никакими тиками.
+
ЗавершениеTaskCompleted / TaskFailed (+ политика ретраев и промоушена выше). Результат → событие с ссылкой на артефакт.
+
ВыжимкаRunner пишет DigestWritten{level, period} — сырое остаётся в трассе, в пирамиду идёт сжатое.
+
+
Non-preemption на практике: у каждого уровня ровно один исполнитель (worker). Это даёт сериализацию бесплатно: день не может «отложить» час. Долгие L-задачи идут в фоне сессии, дневной цикл на следующих тиках лишь проверяет их статус — и пока они живы, новых L не берёт.
+
+ + +
+
06 / ПАМЯТЬ
+

Контекстная пирамида

+

Контекст цикла — не «всё, что случилось», а многослойная сборка. Сырые данные живут в трассе событий; наверх поднимаются только выжимки. Это главный эконом токенов: наш фактический опыт — 3.1 млрд prompt-токенов за 14 дней в основном из-за перечитывания сырых контекстов.

+
+
ГОД · 1M
Идентичность: миссия, ценности, «кто я». Пишется раз в год, кэшируется во все нижние уровни.
+
КВАРТАЛ · 512K
Ставки: 3–5 стратегических направлений + критерии их проверки.
+
МЕСЯЦ · 256K
Цели: OKR, бюджет, лимиты расходов на уровень.
+
НЕДЕЛЯ · 200K
Уроки: что сработало/нет, обновления скиллов и политики.
+
ДЕНЬ · 128K
Манифест дня: план + часовые дайджесты (не сырые минуты!) + доска задач L-класса.
+
ЧАС · 32K
Дайджест часа: сжатые минутные логи, статус фоновых задач, триаж входящих.
+
МИНУТА · 4K
Пульс: последние входящие, алерты секундного уровня, S-задачи.
+
СЕК · 0
Рефлекс: без LLM. Телеметрия и правила. Дёшево тикать каждую секунду.
+
+

Сборка контекста дневного цикла: идентичность (кэш) + квартальные ставки + месячные цели + недельные уроки + дайджесты 24 часов + доска задач. Сырые трассы не поднимаются выше своего уровня никогда — вверх идёт только сжатое.

+
+ + +
+
07 / СОБЫТИЯ
+

Событийная модель

+

Стор — по образцу кооп-портала (aggregate, aggregate_id, event_type, data, timestamp, version). События иммутабельны; тестовые — единственное исключение при чистке.

+
+
+
Aggregate: cycle
+
    +
  • CycleTicked {level, seq} — граница такта
  • +
  • TickAbsorbed {reason: busy|skipped}
  • +
  • DigestWritten {level, period, summary_ref}
  • +
+
+
+
Aggregate: task
+
    +
  • TaskSubmitted {title, size S/M/L/XL, source}
  • +
  • TaskClaimed {level, session_id}
  • +
  • TaskCompleted {result_ref} / TaskFailed {error}
  • +
  • TaskPromoted {from, to} — не по зубам уровню
  • +
+
+
+
Aggregate: harness
+
    +
  • PolicyChanged {budgets, mandates}
  • +
  • BudgetSpent {level, tokens, $}
  • +
  • LevelPaused / LevelResumed
  • +
+
+
+
Aggregate: trace
+
    +
  • InvocationStarted / ToolCalled / ToolResult — из hooks Strands: BeforeInvocationEvent, Before/AfterToolCallEvent
  • +
  • Полная трасса агентного цикла = поток событий. Дебаг = читать стор.
  • +
+
+
+

Проекции (query side)

+
    +
  • task_board — готовые/взятые/закрытые задачи по классам; who-can-claim-what
  • +
  • digest_{level} — последние выжимки уровня (источник для контекстов уровней выше)
  • +
  • metrics — тики/поглощения/расходы по уровням (видно стоимость ритма)
  • +
  • agent_state — чем занят каждый уровень сейчас
  • +
+
+ + +
+
08 / STRANDS
+

Маппинг на Strands Harness SDK

+

Берём SDK как исполнительное ядро — не форкаем, а собираем поверх. Всё, чего нет в Strands (время, событийная спина, консолидация) — наш слой.

+ + + + + + + + + + + +
Наш концептМеханизм StrandsКомментарий
Cycle Runner уровняcreate_harness() на тикФабрика с конфигом уровня: model, effort, tools, instructions
Мандат уровняinstructions / system promptСобственный слой поверх HARNESS_CONTRACT
Бюджет контекстаcontext_manager + sessionagentic-режим для день+, sliding window для минут
Непрерываемостьbackground tasks + session idЗадача = сессия; тики не рвут её, цикл опрашивает
Трасса в сторhooks: add_hook(BeforeInvocationEvent, BeforeToolCallEvent…)Наш плагин: каждый хук → append_event. Трасса = события
Инструментыbuiltin_tools + mcp_servers + skills dirshell/read/write/edit/web — по уровню: минуте не даём shell, дню — даём
Модель по уровнюmodel: provider/name, effort: auto…highЧерез наш 9router-провайдер: flash/full/pro
Осторожностьinterventions (guardrails)Необратимые действия — через подтверждение или только на высоких уровнях
Долгая памятьmemory storesБэкенд — наши проекции digest_*, не файлы
+
+
Берём из Strands как есть
+
    +
  • Agent loop: лимиты ходов, токен-бюджеты, stop reasons
  • +
  • Hooks — типизированные события жизненного цикла
  • +
  • Сессии, structured output, MCP, мультитул-оркестрация
  • +
  • Guardrails/interventions, context offloader
  • +
+
+
Принципиально своё (новое)
+
    +
  • Временная ось: тактовая лестница — в Strands нет никакого «времени»
  • +
  • Событийная спина: у Strands сессии = файлы; у нас — append-only стор + replay
  • +
  • Каскад консолидации памяти (digestion) — у Strands нет иерархии памяти
  • +
  • Классы задач ↔ уровни цикла — планировщик как у человека
  • +
+
+
+
+ + +
+
09 / СТЕК
+

Стек и размещение

+
    +
  • Python 3.12, один процесс-демон (systemd), asyncio-планировщик — как simbiotd, но на событийной спине
  • +
  • pip install strands-harness (Apache-2.0) + наш пакет simbiot-harness: scheduler, runners, es-спина, проекции, hooks-плагин
  • +
  • Event store: SQLite WAL — проверенный образец кооп-портала (events, projections, projection_state.json); идемпотентные команды; чистые тестовые события после E2E
  • +
  • LLM: 9router (glm-5.3-flash / glm-5.3 / pro-слот) — своя модель на уровень, бюджет через BudgetSpent
  • +
  • Интерфейсы v0.1: CLI + Telegram (таски от человека); Matrix/Web — после ревью
  • +
  • Деплой: портал-бокс рядом с simbiotd; порт не нужен — только systemd-юнит и локальный CLI
  • +
+

Объём v0.1 (что строим первым)

+
    +
  • Уровни 1с + 1м + 1ч + 1д (рефлекс без LLM уже дёшев); неделя/месяц — v0.2, квартал/год — v0.3
  • +
  • Задачи S/M/L + промоушен классов, непрерываемость, дайджесты часа
  • +
  • E2E: человек кидает задачу в TG → минутный взял S → дневной собрал манифест → отчёт → метрики стоимости ритма в проекции
  • +
+
+ + +
+
10 / РЕШЕНИЯ
+

Ревью пройдено — решения

+

Прошли с владельцем 29.09.2026. Все шесть вопросов закрыты.

+ + + + + + + + +
ВопросРешение
Стоимость ритма (~$1–3/день за «жизнь» без задач)✅ Принято — норма
Пропущенные такты✅ Поглощённый тик пишет детерминированную дайджест-заглушку (без LLM). Дальше (v0.2+): срочная реакция сможет прервать бегущую задачу или поставить её на паузу
Один исполнитель на уровень✅ Принято; N воркеров минутного уровня — v0.2 при необходимости
Идемпотентность тиков✅ UNIQUE(level, seq); пропущенное время не навёрстывается
Pro-слот для квартала/года✅ Принято
Имя и место✅ Simbiot Harness — отдельный репо-продукт (отдельный продукт = отдельный репо)
+
Следующий шаг: сборка v0.1 — уровни 1с + 1м + 1ч + 1д, задачи S/M/L, ES-спина, дайджесты, hooks-плагин Strands, E2E: таска из Telegram → минутный цикл → дневной манифест → отчёт.
+
+ +
+ SIMBIOT HARNESS · ARCHITECTURE v0.1 + РЕВЬЮ ПРОЙДЕНО · 29.09.2026 + STRANDS HARNESS SDK · APACHE-2.0 +
+
+ + \ No newline at end of file diff --git a/cli.py b/cli.py new file mode 100644 index 0000000..8ae230f --- /dev/null +++ b/cli.py @@ -0,0 +1,71 @@ +"""Simbiot Harness — CLI: таски, статус, ручные тики.""" +import argparse +import json +import sys +import os + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from harness import commands, es + + +def main(): + ap = argparse.ArgumentParser(prog="simbiot", description="Simbiot Harness CLI") + sub = ap.add_subparsers(dest="cmd", required=True) + + p_task = sub.add_parser("task", help="подать задачу") + p_task.add_argument("title") + p_task.add_argument("-d", "--details", default="") + p_task.add_argument("-m", "--minutes", type=int, default=5, + help="оценка длительности (класс S≤5м M≤60м L≤8ч XL)") + + p_pol = sub.add_parser("policy", help="задать политику") + p_pol.add_argument("--identity", default=None) + p_pol.add_argument("--cpu-threshold", type=int, default=None) + + p_rq = sub.add_parser("requeue", help="вернуть failed-задачу в очередь") + p_rq.add_argument("task_id") + p_rq.add_argument("-r", "--reason", default="manual requeue") + + sub.add_parser("status", help="состояние харнеса") + sub.add_parser("tasks", help="доска задач") + + p_ev = sub.add_parser("events", help="хвост стора") + p_ev.add_argument("-n", type=int, default=15) + p_ev.add_argument("--after", type=int, default=0) + + p_init = sub.add_parser("init", help="инициализация стора") + p_init.add_argument("--identity", default="Simbiot Harness — ритмичный агент кооператива") + + args = ap.parse_args() + + if args.cmd == "init": + es.init_db() + commands.set_policy(identity=args.identity) + print("OK: стор инициализирован, политика записана") + elif args.cmd == "task": + tid, size = commands.submit_task(args.title, args.details, args.minutes) + print(f"OK: задача {tid} [{size}] подана") + elif args.cmd == "policy": + pol = commands.set_policy(identity=args.identity, cpu_alert_threshold=args.cpu_threshold) + print("OK:", json.dumps(pol, ensure_ascii=False)) + elif args.cmd == "requeue": + es.init_db() + tid = commands.requeue_task(args.task_id, args.reason) + print(f"OK: задача {tid} возвращена в очередь") + elif args.cmd == "status": + es.init_db() + print(json.dumps(commands.status(), ensure_ascii=False, indent=1)) + elif args.cmd == "tasks": + es.init_db() + for t in commands.list_tasks(): + print(f"{t['id']} [{t['size']:2}] {t['status']:7} {t['title'][:70]}") + elif args.cmd == "events": + es.init_db() + for ev in es.get_events(args.after, args.n): + d = ev["data"][:110] + print(f"{ev['id']:4} {ev['timestamp'][11:19]} {ev['aggregate']:8} {ev['event_type']:16} {d}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/harness/__init__.py b/harness/__init__.py new file mode 100644 index 0000000..61e8a09 --- /dev/null +++ b/harness/__init__.py @@ -0,0 +1,5 @@ +"""Simbiot Harness — тактовая лестница на CQRS + Event Sourcing. + +Уровни 1с/1м/1ч/1д (v0.1). Стартовая точка — strands-agents/harness-sdk. +""" +__version__ = "0.1.0" \ No newline at end of file diff --git a/harness/__pycache__/__init__.cpython-312.pyc b/harness/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..13b6a46 Binary files /dev/null and b/harness/__pycache__/__init__.cpython-312.pyc differ diff --git a/harness/__pycache__/_llm_worker.cpython-312.pyc b/harness/__pycache__/_llm_worker.cpython-312.pyc new file mode 100644 index 0000000..dd367f1 Binary files /dev/null and b/harness/__pycache__/_llm_worker.cpython-312.pyc differ diff --git a/harness/__pycache__/commands.cpython-312.pyc b/harness/__pycache__/commands.cpython-312.pyc new file mode 100644 index 0000000..3370ef9 Binary files /dev/null and b/harness/__pycache__/commands.cpython-312.pyc differ diff --git a/harness/__pycache__/es.cpython-312.pyc b/harness/__pycache__/es.cpython-312.pyc new file mode 100644 index 0000000..0b6a243 Binary files /dev/null and b/harness/__pycache__/es.cpython-312.pyc differ diff --git a/harness/__pycache__/isolated_runner.cpython-312.pyc b/harness/__pycache__/isolated_runner.cpython-312.pyc new file mode 100644 index 0000000..f3ec7de Binary files /dev/null and b/harness/__pycache__/isolated_runner.cpython-312.pyc differ diff --git a/harness/__pycache__/levels.cpython-312.pyc b/harness/__pycache__/levels.cpython-312.pyc new file mode 100644 index 0000000..dba2ddd Binary files /dev/null and b/harness/__pycache__/levels.cpython-312.pyc differ diff --git a/harness/__pycache__/runtime.cpython-312.pyc b/harness/__pycache__/runtime.cpython-312.pyc new file mode 100644 index 0000000..c187164 Binary files /dev/null and b/harness/__pycache__/runtime.cpython-312.pyc differ diff --git a/harness/__pycache__/scheduler.cpython-312.pyc b/harness/__pycache__/scheduler.cpython-312.pyc new file mode 100644 index 0000000..69bca44 Binary files /dev/null and b/harness/__pycache__/scheduler.cpython-312.pyc differ diff --git a/harness/_llm_worker.py b/harness/_llm_worker.py new file mode 100644 index 0000000..882d695 --- /dev/null +++ b/harness/_llm_worker.py @@ -0,0 +1,68 @@ +"""Воркер одного LLM-тика (запускается сабпроцессом из isolated_runner). + +Читает payload из stdin, гоняет create_harness() один раз и умирает — +каждый тик получает чистый asyncio-рантайм Strands. +Трасса пишется напрямую в event store (es доступен). +Результат — в JSON-файл (path в payload['result_path']); stdout — только лог. +""" +import json +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + +def _emit(payload, obj): + rp = payload.get("result_path") + if rp: + try: + with open(rp, "w") as f: + json.dump(obj, f, ensure_ascii=False) + except OSError: + pass + + +def main(): + payload = json.loads(sys.stdin.read()) + + from harness import es + from harness.runtime import setup_provider, _wire_trace_hooks + + setup_provider() + from strands_harness import create_harness + + trace_id = payload.get("trace_prefix") or "tick" + try: + agent = create_harness( + model=f"openai/{payload['model']}", + effort=payload.get("effort", "auto"), + instructions=payload.get("instructions", ""), + session=None, + builtin_tools=("read", "write", "edit", "shell") if payload.get("can_shell") else [], + memory=False, + skills=False, + ) + agent = _wire_trace_hooks(agent, es, trace_id) + result = agent(payload.get("prompt", "")) + usage = {} + try: + msgs = getattr(agent, "messages", None) or [] + for m in reversed(msgs): + u = (m or {}).get("message", {}).get("usage") + if u: + usage = { + "prompt_tokens": u.get("inputTokens") or u.get("prompt_tokens"), + "completion_tokens": u.get("outputTokens") or u.get("completion_tokens"), + } + break + except Exception: + pass + _emit(payload, {"ok": True, "text": str(result), "usage": usage, "trace_id": trace_id}) + except Exception as e: + import traceback + _emit(payload, {"ok": False, "error": f"{e}\n{traceback.format_exc()[-800:]}"}) + # орфан-воркер после убийства родителя может умереть на печати — не критично + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/harness/commands.py b/harness/commands.py new file mode 100644 index 0000000..50a59d2 --- /dev/null +++ b/harness/commands.py @@ -0,0 +1,67 @@ +"""Simbiot Harness — command side (CQRS): валидация + события. + +Задачи подаются через CLI/Telegram/файл-инбокс; каждая команда +валидируется и только потом пишет событие. +""" +import os +import secrets +from datetime import datetime, timezone + +from . import es +from .levels import size_class + + +def submit_task(title: str, details: str = "", minutes: int = 5, source: str = "cli"): + """SubmitTask: класс по оценке длительности. Идемпотентно по id.""" + task_id = f"t-{datetime.now(timezone.utc).strftime('%y%m%d%H%M%S')}-{secrets.token_hex(3)}" + size = size_class(minutes) + es.append_event("task", task_id, "TaskSubmitted", { + "id": task_id, "title": title, "details": details, + "size": size, "est_minutes": minutes, "source": source, + }) + return task_id, size + + +def requeue_task(task_id: str, reason: str = "manual requeue"): + """Вернуть failed/прерванную задачу в очередь (TaskPromoted → ready).""" + model = es.run_projections() + t = model["tasks"].get(task_id) + if t is None: + raise ValueError(f"задача {task_id} не найдена") + if t["status"] not in ("failed", "done"): + raise ValueError(f"задача {task_id} в статусе {t['status']} — requeue не нужен") + es.append_event("task", task_id, "TaskPromoted", {"to": t["size"], "reason": reason}) + return task_id + + +def set_policy(identity: str = None, cpu_alert_threshold: int = None): + """PolicyChanged: идентичность + пороги. Мержим поверх текущей политики.""" + model = es.run_projections() + current = dict(model.get("policy") or {}) + patch = {} + if identity is not None: + patch["identity"] = identity + if cpu_alert_threshold is not None: + patch["cpu_alert_threshold"] = cpu_alert_threshold + current.update(patch) + es.append_event("harness", "policy", "PolicyChanged", current) + return current + + +def list_tasks(): + model = es.run_projections() + out = [] + for t in model["tasks"].values(): + out.append(t) + return out + + +def status(): + model = es.run_projections() + return { + "cycle_state": model["cycle_state"], + "tasks_ready": sum(1 for t in model["tasks"].values() if t["status"] == "ready"), + "tasks_done": sum(1 for t in model["tasks"].values() if t["status"] == "done"), + "digests": len(model["digests"]), + "spend": model.get("spend", [])[-8:], + } \ No newline at end of file diff --git a/harness/es.py b/harness/es.py new file mode 100644 index 0000000..d73cf5c --- /dev/null +++ b/harness/es.py @@ -0,0 +1,251 @@ +"""Simbiot Harness — ES-спина: append-only event store + проекции. + +Образец — кооп-портал (aggregate, aggregate_id, event_type, data, version). +Идемпотентные команды; тики защищены UNIQUE(level, seq). +""" +import json +import os +import sqlite3 +import threading +import time +from datetime import datetime, timezone + +DB_PATH = os.environ.get("SIMBIOT_DB", os.path.join(os.path.dirname(__file__), "harness.db")) + +# RLock обязателен: rebuild_projections держит лок и зовёт +# run_projections, который берёт его же — обычный Lock давил сам себя. +_LOCK = threading.RLock() +_read_model = None # инкрементальный read model (глобал для инкрементальных проекций) + + +def _conn(): + db = sqlite3.connect(DB_PATH, timeout=30) + db.execute("PRAGMA journal_mode=WAL") + db.execute("PRAGMA busy_timeout=30000") + db.row_factory = sqlite3.Row + return db + + +def init_db(): + with _LOCK: + db = _conn() + db.executescript( + """ + CREATE TABLE IF NOT EXISTS events ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + aggregate TEXT NOT NULL, + aggregate_id TEXT NOT NULL, + event_type TEXT NOT NULL, + data TEXT NOT NULL, + timestamp TEXT NOT NULL, + version INTEGER NOT NULL + ); + CREATE UNIQUE INDEX IF NOT EXISTS idx_events_agg + ON events(aggregate, aggregate_id, version); + CREATE TABLE IF NOT EXISTS projection_meta ( + name TEXT PRIMARY KEY, + last_event INTEGER NOT NULL, + updated_at TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS read_model ( + name TEXT PRIMARY KEY, + model TEXT NOT NULL, + updated_at TEXT NOT NULL + ); + """ + ) + # идемпотентность тиков: один уровень — один seq + db.executescript( + """ + CREATE UNIQUE INDEX IF NOT EXISTS idx_cycle_tick + ON events(aggregate, aggregate_id) WHERE aggregate='cycle'; + """ + ) + # aggregate_id для cycle = "level:seq", поэтому общий уникальный индекс + # (aggregate, aggregate_id) уже даёт UNIQUE(level, seq). + db.commit() + db.close() + + +def append_event(aggregate: str, aggregate_id: str, event_type: str, data: dict): + """Append-only запись события. Возвращает (event_id, version) или None при дубликате.""" + with _LOCK: + db = _conn() + row = db.execute( + "SELECT MAX(version) AS v FROM events WHERE aggregate=? AND aggregate_id=?", + (aggregate, aggregate_id), + ).fetchone() + version = (row["v"] or 0) + 1 + ts = datetime.now(timezone.utc).isoformat() + try: + cur = db.execute( + "INSERT INTO events (aggregate,aggregate_id,event_type,data,timestamp,version) VALUES (?,?,?,?,?,?)", + (aggregate, aggregate_id, event_type, + json.dumps(data, ensure_ascii=False), ts, version), + ) + db.commit() + eid = cur.lastrowid + except sqlite3.IntegrityError: + db.close() + return None # дубль (тик с тем же level:seq) — идемпотентно пропускаем + db.close() + return (eid, version) + + +def append_tick(level: str, seq: int): + """CycleTicked идемпотентно: aggregate_id = 'level:seq' → UNIQUE даёт защиту.""" + return append_event("cycle", f"{level}:{seq}", "CycleTicked", {"level": level, "seq": seq}) + + +def get_events(after_id: int = 0, limit: int = 1000): + db = _conn() + rows = db.execute( + "SELECT * FROM events WHERE id > ? ORDER BY id LIMIT ?", (after_id, limit) + ).fetchall() + db.close() + return [dict(r) for r in rows] + + +# ────────────────────────── ПРОЕКЦИИ ────────────────────────── + +def _default_model(): + return { + "tasks": {}, # task_id → {title, size, status, level, session_id, result_ref, ...} + "digests": {}, # (level, period) → {summary_ref, text} + "cycle_state": {}, # level → {busy, current_task, last_tick} + "metrics": [], # [{level, ticks, absorbed, llm_ticks, tokens, cost}] + "agents": {}, # level → {model, calls} + } + + +def _load_meta(db, name): + row = db.execute("SELECT last_event FROM projection_meta WHERE name=?", (name,)).fetchone() + return row["last_event"] if row else 0 + + +def _save_meta(db, name, last_event): + db.execute( + "INSERT INTO projection_meta (name, last_event, updated_at) VALUES (?,?,?) " + "ON CONFLICT(name) DO UPDATE SET last_event=excluded.last_event, updated_at=excluded.updated_at", + (name, last_event, datetime.now(timezone.utc).isoformat()), + ) + + +def _apply_event(model: dict, ev: dict): + etype, data = ev["event_type"], json.loads(ev["data"]) + aid = ev["aggregate_id"] + + if etype == "TaskSubmitted": + model["tasks"][aid] = { + "id": aid, "title": data.get("title", ""), "size": data.get("size", "M"), + "status": "ready", "level": None, "session_id": None, + "result_ref": None, "created": ev["timestamp"], + } + elif etype == "TaskClaimed": + t = model["tasks"].get(aid) + if t: + t.update(status="running", level=data.get("level"), session_id=data.get("session_id")) + elif etype == "TaskCompleted": + t = model["tasks"].get(aid) + if t: + t.update(status="done", result_ref=data.get("result_ref"), finished=ev["timestamp"]) + elif etype == "TaskFailed": + t = model["tasks"].get(aid) + if t: + t.update(status="failed", error=data.get("error"), finished=ev["timestamp"]) + elif etype == "TaskPromoted": + t = model["tasks"].get(aid) + if t: + t.update(status="ready", level=None, session_id=None, + promoted_to=data.get("to"), promotions=(t.get("promotions") or 0) + 1) + elif etype == "DigestWritten": + key = f"{data['level']}:{data['period']}" + model["digests"][key] = { + "level": data["level"], "period": data["period"], + "summary_ref": data.get("summary_ref"), + "text": data.get("text", ""), + "ts": ev["timestamp"], + } + elif etype == "CycleTicked": + level = data.get("level", "?") + cs = model["cycle_state"].setdefault(level, {"busy": False, "current_task": None, "ticks": 0}) + cs["ticks"] = cs.get("ticks", 0) + 1 + cs["last_tick"] = ev["timestamp"] + elif etype == "TickAbsorbed": + level = data.get("level", "?") + cs = model["cycle_state"].setdefault(level, {"busy": False, "current_task": None, "ticks": 0}) + cs["absorbed"] = cs.get("absorbed", 0) + 1 + elif etype == "LevelPaused": + model["cycle_state"].setdefault(data["level"], {})["paused"] = True + elif etype == "LevelResumed": + model["cycle_state"].setdefault(data["level"], {})["paused"] = False + elif etype == "PolicyChanged": + model.setdefault("policy", {}).update(data) + elif etype == "BudgetSpent": + model.setdefault("spend", []).append(data) + # trace-события (InvocationStarted/ToolCalled/ToolResult) не материализуем — они для дебага + + +def _load_model(db): + """Персистентный read model: без него каждый новый процесс + стартовал с пустой моделью при уже продвинутом last_event — + и терял TaskSubmitted/PolicyChanged навсегда.""" + row = db.execute("SELECT model FROM read_model WHERE name='main'").fetchone() + if row: + try: + return json.loads(row["model"]) + except (ValueError, TypeError): + pass + return None + + +def _save_model(db, model): + db.execute( + "INSERT INTO read_model (name, model, updated_at) VALUES ('main', ?, ?) " + "ON CONFLICT(name) DO UPDATE SET model=excluded.model, updated_at=excluded.updated_at", + (json.dumps(model, ensure_ascii=False, default=str), datetime.now(timezone.utc).isoformat()), + ) + + +def run_projections(): + """Инкрементальная проекция с персистентным read model.""" + global _read_model + with _LOCK: + db = _conn() + last = _load_meta(db, "main") + rows = db.execute("SELECT * FROM events WHERE id > ? ORDER BY id", (last,)).fetchall() + if _read_model is None: + _read_model = _load_model(db) or _default_model() + if not rows: + db.close() + return _read_model + for r in rows: + _apply_event(_read_model, dict(r)) + _save_meta(db, "main", rows[-1]["id"]) + _save_model(db, _read_model) + db.commit() + db.close() + return _read_model + + +def rebuild_projections(): + """Полный replay с нуля (проекции одноразовые, стор — истина).""" + global _read_model + with _LOCK: + db = _conn() + db.executescript( + """ + CREATE TABLE IF NOT EXISTS projection_meta ( + name TEXT PRIMARY KEY, last_event INTEGER NOT NULL, updated_at TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS read_model ( + name TEXT PRIMARY KEY, model TEXT NOT NULL, updated_at TEXT NOT NULL + ); + DELETE FROM projection_meta; + DELETE FROM read_model; + """ + ) + db.commit() + db.close() + _read_model = None + return run_projections() \ No newline at end of file diff --git a/harness/isolated_runner.py b/harness/isolated_runner.py new file mode 100644 index 0000000..4c25152 --- /dev/null +++ b/harness/isolated_runner.py @@ -0,0 +1,70 @@ +"""Simbiot Harness — изолированный раннер LLM-тика. + +Strands держит глобальный asyncio-рантайм: несколько create_harness() +в одном процессе валит его («cannot schedule new futures after shutdown»). +Решение — каждый LLM-тик в ОТДЕЛЬНОМ сабпроцессе: чистый asyncio-рантайм +на тик, полная изоляция, ноль утечек. Демон живёт вечно. +""" +import json +import os +import subprocess +import sys + +_HARNESS_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +_VENV_PY = os.path.join(_HARNESS_DIR, "venv", "bin", "python") +_WORKER = os.path.join(_HARNESS_DIR, "harness", "_llm_worker.py") +MARKER = "@@SIMBIOT_RESULT@@" + + +def run_llm_tick(level_cfg, prompt: str, session_id=None, trace_prefix=None, timeout=None): + """Запускает LLM-тик в сабпроцессе. Возвращает (text, usage, trace_id). + + События трассы воркер пишет в стор сам (у него есть es). + Протокол: воркер пишет результат в JSON-ФАЙЛ (путь передан в env); + stdout/stderr только для лога — библиотечный шум strands не мешает. + """ + import tempfile + result_path = tempfile.mktemp(prefix="simbiot_tick_", suffix=".json", dir="/tmp") + payload = { + "model": level_cfg.model, + "effort": level_cfg.effort, + "instructions": level_cfg.mandate, + "prompt": prompt, + "session_id": None, # сессии пока выключены: .agent/sessions растёт без очистки + "trace_prefix": trace_prefix, + "can_shell": level_cfg.can_shell, + "ctx_tokens": level_cfg.ctx_tokens, + "result_path": result_path, + } + env = dict(os.environ) + env["SIMBIOT_DB"] = env.get("SIMBIOT_DB", os.path.join(_HARNESS_DIR, "harness", "harness.db")) + if timeout is None: + # таймаут чуть меньше интервала уровня, чтобы тик не наезжал на границу + timeout = max(120, min(int(level_cfg.every * 0.8), 900)) + try: + proc = subprocess.run( + [_VENV_PY, _WORKER], + input=json.dumps(payload).encode(), + capture_output=True, + timeout=timeout, + cwd=_HARNESS_DIR, + env=env, + ) + finally: + pass + out = None + if os.path.exists(result_path): + try: + with open(result_path) as f: + out = json.load(f) + except ValueError: + out = None + try: + os.unlink(result_path) + except OSError: + pass + if out is None: + return None, {"error": f"worker result file missing: stdout={(proc.stdout or b'')[:200]!r} stderr={(proc.stderr or b'').decode(errors='replace')[-300:]}"}, None + if not out.get("ok"): + return None, {"error": (out.get("error") or "unknown")[:500]}, None + return out.get("text", ""), out.get("usage", {}), out.get("trace_id") \ No newline at end of file diff --git a/harness/levels.py b/harness/levels.py new file mode 100644 index 0000000..67aed1c --- /dev/null +++ b/harness/levels.py @@ -0,0 +1,80 @@ +"""Simbiot Harness — уровни и конфигурация тактовой лестницы. + +Контекст ∝ масштабу: чем крупнее цикл, тем больше контекст и модель. +""" +from dataclasses import dataclass, field + +SEC = 1 +MIN = 60 +HOUR = 3600 +DAY = 86400 +WEEK = 7 * DAY +MONTH = 30 * DAY +QUARTER = 91 * DAY +YEAR = 366 * DAY # округляем год как 366 дней для простоты выравнивания + + +@dataclass +class CycleLevel: + name: str + label: str + every: int # секунд между тиками + uses_llm: bool + model: str # модель 9router (или "none") + effort: str # effort Strands: auto/low/medium/high/off + ctx_tokens: int # бюджет контекста уровня + task_classes: tuple # какие классы задач может брать + mandate: str # мандат уровня (в промпт) + can_shell: bool # инструменты уровня + + def seq(self, ts: int) -> int: + """Номер такта уровня для unix-ts (для идемпотентности тиков).""" + return ts // self.every + + +LEVELS = { + "sec": CycleLevel( + name="sec", label="Секунда · рефлекс", every=SEC, uses_llm=False, + model="none", effort="off", ctx_tokens=0, task_classes=(), + mandate=("Ты — рефлекторный уровень харнеса. Работает без LLM: телеметрия, " + "heartbeat, алерты. Никаких решений — только наблюдение."), + can_shell=False, + ), + "min": CycleLevel( + name="min", label="Минута · пульс", every=MIN, uses_llm=True, + model="auto", effort="low", ctx_tokens=4_000, task_classes=("S",), + mandate=("Ты — минутный пульс харнеса Simbiot. Триаж входящих, быстрые ответы, " + "мелкие фиксы (S-задачи до 5 минут). Отвечай кратко, не углубляйся. " + "Если задача больше твоего класса — откажись и предложи промоушен."), + can_shell=True, + ), + "hour": CycleLevel( + name="hour", label="Час · поток", every=HOUR, uses_llm=True, + model="auto", effort="medium", ctx_tokens=32_000, task_classes=("S", "M"), + mandate=("Ты — часовой поток харнеса Simbiot. Дайджест часа: что произошло, " + "статусы фоновых задач, батч мелочей (S/M). Консолидируй минутные " + "события в выжимку — сырое наверх не поднимай."), + can_shell=True, + ), + "day": CycleLevel( + name="day", label="День · манифест", every=DAY, uses_llm=True, + model="auto", effort="high", ctx_tokens=128_000, task_classes=("S", "M", "L"), + mandate=("Ты — дневной цикл харнеса Simbiot. Манифест дня: план, выбор задач " + "L-класса, дневной отчёт. Думай глубоко, планируй на день вперёд."), + can_shell=True, + ), +} + +# v0.2+: week/month/quarter/year — включаются по мере готовности +FUTURE_LEVELS = ("week", "month", "quarter", "year") + + +def size_class(minutes: int) -> str: + """Класс задачи по оценке длительности.""" + if minutes <= 5: + return "S" + if minutes <= 60: + return "M" + if minutes <= 8 * 60: + return "L" + return "XL" \ No newline at end of file diff --git a/harness/runtime.py b/harness/runtime.py new file mode 100644 index 0000000..63b0926 --- /dev/null +++ b/harness/runtime.py @@ -0,0 +1,130 @@ +"""Simbiot Harness — Strands runtime-адаптер. + +create_harness() на тик: модель/effort из конфига уровня. +Hooks → трасса в event store (агрегат trace). +""" +import json +import os +import uuid +from datetime import datetime, timezone + +_HARNESS_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +def _router_key(): + env = os.environ.get("ROUTER_KEY", "") + if not env: + path = os.path.join(_HARNESS_DIR, ".env") + if os.path.exists(path): + for line in open(path): + if line.startswith("ROUTER_KEY="): + env = line.split("=", 1)[1].strip() + break + return env + + +def setup_provider(): + """9router как openai-совместимый провайдер strands.""" + key = _router_key() + if not key: + raise RuntimeError("ROUTER_KEY не найден (.env)") + os.environ["OPENAI_API_KEY"] = key + os.environ["OPENAI_BASE_URL"] = os.environ.get("ROUTER_BASE_URL", "http://127.0.0.1:20128/v1") + + +def _wire_trace_hooks(agent, es, trace_id: str): + """Каждая стадия агентного цикла → событие агрегата trace.""" + from strands import Agent + from strands.hooks import BeforeInvocationEvent, BeforeToolCallEvent, AfterToolCallEvent + + seq = {"n": 0} + + def _trace(kind: str, payload: dict): + seq["n"] += 1 + es.append_event("trace", f"{trace_id}:{seq['n']}", kind, payload) + + def on_before_invocation(event): + _trace("InvocationStarted", {"trace_id": trace_id, "ts": _now()}) + + def on_before_tool(event): + tool = getattr(event, "tool_use", None) or {} + _trace("ToolCalled", { + "tool": tool.get("name", "?"), + "input": json.dumps(tool.get("input", {}), ensure_ascii=False, default=str)[:4000], + }) + + def on_after_tool(event): + try: + res = str(getattr(event, "result", ""))[:4000] + except Exception: + res = "" + _trace("ToolResult", {"tool": "?", "result": res}) + + agent.add_hook(on_before_invocation, BeforeInvocationEvent) + agent.add_hook(on_before_tool, BeforeToolCallEvent) + agent.add_hook(on_after_tool, AfterToolCallEvent) + return agent + + +def _now(): + return datetime.now(timezone.utc).isoformat() + + +def build_agent(level_cfg, session_id=None, extra_instructions=""): + """Собирает Strands-агента для тика уровня. None если уровень без LLM.""" + if not level_cfg.uses_llm: + return None + setup_provider() + from strands_harness import create_harness + + instructions = level_cfg.mandate + ("\n\n" + extra_instructions if extra_instructions else "") + agent = create_harness( + model=f"openai/{level_cfg.model}", + effort=level_cfg.effort, + instructions=instructions, + session={"id": session_id} if session_id else None, + builtin_tools=("read", "write", "edit", "shell") if level_cfg.can_shell else [], + memory=False, + skills=False, + ) + return agent + + +def run_cycle(level_cfg, es, prompt: str, session_id=None, trace_prefix=None): + """Прогон тика уровня с LLM и трассой. Возвращает (text, usage_dict, trace_id).""" + setup_provider() + from strands_harness import create_harness + + trace_id = trace_prefix or uuid.uuid4().hex[:12] + instructions = level_cfg.mandate + agent = create_harness( + model=f"openai/{level_cfg.model}", + effort=level_cfg.effort, + instructions=instructions, + session={"id": session_id} if session_id else None, + builtin_tools=("read", "write", "edit", "shell") if level_cfg.can_shell else [], + memory=False, + skills=False, + ) + agent = _wire_trace_hooks(agent, es, trace_id) + result = agent(prompt) + # AgentResult: текст из message, usage из metrics.accumulated_usage + # (старый разбор agent.messages молчал → BudgetSpent всегда был пуст). + text = str(result) + usage = _extract_usage(result) + return text, usage, trace_id + + +def _extract_usage(result): + """Usage из AgentResult.metrics.accumulated_usage (input/output/total).""" + try: + u = getattr(getattr(result, "metrics", None), "accumulated_usage", None) + if u: + return { + "prompt_tokens": u.get("inputTokens"), + "completion_tokens": u.get("outputTokens"), + "total_tokens": u.get("totalTokens"), + } + except Exception: + pass + return {} \ No newline at end of file diff --git a/harness/scheduler.py b/harness/scheduler.py new file mode 100644 index 0000000..b0f82f4 --- /dev/null +++ b/harness/scheduler.py @@ -0,0 +1,236 @@ +"""Simbiot Harness — scheduler: тактовая лестница + cycle runners. + +Правила: +- тик уровня идемпотентен (UNIQUE level:seq); +- один исполнитель на уровень → сериализация; +- занятый уровень поглощает тик (TickAbsorbed) и пишет + детерминированную дайджест-заглушку — без LLM; +- запущенная задача не прерывается. +""" +import json +import os +import time +import traceback +from datetime import datetime, timezone, timedelta + +from . import es +from .levels import LEVELS +from . import runtime + +# интервал демона +POLL = 1.0 +# уровни, включённые в v0.1 (порядок для лога) +ACTIVE = ["sec", "min", "hour", "day"] + +# состояние исполнителей уровня: level → {"busy": bool, "task": id|None} +_runners = {} + + +def _now(): + return datetime.now(timezone.utc) + + +def _log(msg: str): + print(f"[{datetime.now().strftime('%H:%M:%S')}] {msg}", flush=True) + + +def tick_number(level_cfg, ts: int): + return ts // level_cfg.every + + +def emit_tick(level_cfg, seq: int): + """CycleTicked идемпотентно. Возвращает True если это новый тик.""" + r = es.append_tick(level_cfg.name, seq) + return r is not None + + +def deterministic_digest_stub(level_cfg, seq: int, reason: str): + """Заглушка дайджеста для поглощённого тика — детерминированно, без LLM.""" + period = datetime.fromtimestamp(seq * level_cfg.every, tz=timezone.utc) + es.append_event("cycle", f"stub:{level_cfg.name}:{seq}", "DigestWritten", { + "level": level_cfg.name, + "period": period.isoformat(), + "summary_ref": None, + "text": f"[absorbed:{reason}] уровень {level_cfg.name} был занят/пропущен; такт {seq}", + "stub": True, + }) + + +def find_task(level_cfg, model: dict): + """Ищет ready-задачу доступного классу уровня. Возвращает task_id или None.""" + classes = level_cfg.task_classes + for tid, t in model["tasks"].items(): + if t["status"] == "ready" and t.get("size") in classes: + return tid + return None + + +def build_context(level_cfg, model: dict, task=None): + """Сборка контекста тика: мандат + память уровня + выжимки нижних + задача.""" + parts = [level_cfg.mandate] + + # идентичность/цели v0.1 — статично из policy (если есть) + policy = model.get("policy") + if policy and policy.get("identity"): + parts.append("ИДЕНТИЧНОСТЬ: " + policy["identity"]) + + # выжимки нижних уровней: последние дайджесты + order = ["min", "hour", "day"] + idx = order.index(level_cfg.name) if level_cfg.name in order else 0 + lower = order[:idx] + digests = [] + for lvl in lower: + for key, d in model["digests"].items(): + if d["level"] == lvl and not d.get("stub"): + digests.append(d) + digests.sort(key=lambda d: d["ts"], reverse=True) + if digests: + parts.append("ВЫЖИМКИ НИЖНИХ УРОВНЕЙ (последние, сжатые):") + for d in digests[:6]: + parts.append(f"- [{d['level']}] {d['text'][:400]}") + + # доска задач + tasks = [t for t in model["tasks"].values() if t["status"] in ("ready", "running")] + if tasks: + lines = [f"- {t['id']}: {t['title'][:80]} [{t['size']}] {t['status']}" for t in tasks[:15]] + parts.append("ДОСКА ЗАДАЧ:\n" + "\n".join(lines)) + + if task: + parts.append(f"ТЕКУЩАЯ ЗАДАЧА: {task['id']}: {task['title']}\nВыполни её и выдай результат.") + + return "\n\n".join(parts) + + +def run_level_tick(level_cfg, seq: int, model: dict): + """Один тик уровня с LLM: дайджест + (опц.) задача. Возвращает текст. + + LLM-тик гоняется в изолированном сабпроцессе (isolated_runner): + у Strands глобальный asyncio-рантайм, второй create_harness() + в том же процессе валит его («cannot schedule new futures»). + """ + from . import isolated_runner + + task_id = find_task(level_cfg, model) + task = model["tasks"].get(task_id) if task_id else None + if task: + es.append_event("task", task_id, "TaskClaimed", { + "level": level_cfg.name, "session_id": f"s-{level_cfg.name}-{seq}", + }) + prompt = build_context(level_cfg, model, task) + text, usage, trace_id = isolated_runner.run_llm_tick( + level_cfg, prompt, + session_id=f"s-{level_cfg.name}-{seq}", + trace_prefix=f"{level_cfg.name}-{seq}", + ) + if text is None: + # воркер упал — задача не выполнена, откатываем claim + if task: + es.append_event("task", task_id, "TaskFailed", { + "error": str(usage.get("error", "llm worker failed"))[:500], + }) + raise RuntimeError(str(usage.get("error", "llm worker failed"))) + # дайджест тика + period = datetime.fromtimestamp(seq * level_cfg.every, tz=timezone.utc) + es.append_event("cycle", f"digest:{level_cfg.name}:{seq}", "DigestWritten", { + "level": level_cfg.name, "period": period.isoformat(), + "summary_ref": trace_id, "text": (text or "")[:2000], + }) + if usage: + es.append_event("harness", "budget", "BudgetSpent", { + "level": level_cfg.name, **{k: v for k, v in usage.items() if k != "error"}, + }) + if task: + es.append_event("task", task_id, "TaskCompleted", { + "result_ref": trace_id, "answer": (text or "")[:2000], + }) + return text, task_id + + +def process_level(level_cfg, model: dict): + """Тик одного уровня: занят? → absorb; иначе работа.""" + st = _runners.setdefault(level_cfg.name, {"busy": False, "task": None}) + if st["busy"]: + seq = tick_number(level_cfg, int(time.time())) + deterministic_digest_stub(level_cfg, seq, "busy") + es.append_event("cycle", f"absorbed:{level_cfg.name}:{seq}", "TickAbsorbed", + {"level": level_cfg.name, "reason": "busy"}) + return "absorbed" + + seq = tick_number(level_cfg, int(time.time())) + if not emit_tick(level_cfg, seq): + return "already" # тик этого такта уже был (идемпотентность) + + st["busy"] = True + try: + if not level_cfg.uses_llm: + # секундный уровень: детерминированный рефлекс без LLM + text, task_id = _reflex_tick(level_cfg, seq, model) + return f"reflex ok {task_id or ''}".strip() + text, task_id = run_level_tick(level_cfg, seq, model) + return f"ran (task={task_id or '-'})" + except Exception as e: + es.append_event("harness", "errors", "CycleError", { + "level": level_cfg.name, "error": str(e)[:1000], + "trace": traceback.format_exc()[-2000:], + }) + return "error: " + str(e)[:120] + finally: + st["busy"] = False + + +def _reflex_tick(level_cfg, seq: int, model: dict): + """Секундный рефлекс: телеметрия + алерты, без LLM.""" + import os + load = None + try: + load = os.getloadavg()[0] + except Exception: + pass + period = datetime.fromtimestamp(seq * level_cfg.every, tz=timezone.utc) + payload = {"level": "sec", "seq": seq, "load1": load, + "ts": period.isoformat()} + # алерт-порог из policy (по умолчанию 8 — как simbiotd) + thr = (model.get("policy") or {}).get("cpu_alert_threshold", 8) + if load is not None and load >= thr: + payload["alert"] = f"load {load:.2f} >= {thr}" + es.append_event("cycle", f"reflex:{seq}", "ReflexObserved", payload) + return payload, None + + +def main_loop(): + """Демон: уровни тикают независимо, каждый в своём потоке. + LLM-тик одного уровня больше не блокирует остальные + (раньше минутный тик замораживал и сек-рефлекс, и час).""" + import threading + + es.init_db() + _log("Simbiot Harness запущен; уровни: " + ", ".join(ACTIVE)) + last_seq = {} + threads = {} + while True: + for name in ACTIVE: + cfg = LEVELS[name] + seq = tick_number(cfg, int(time.time())) + if last_seq.get(name) == seq: + continue # этого такта ещё не было — ждём границу + last_seq[name] = seq + if cfg.uses_llm and _runners.get(name, {}).get("busy"): + # уровень ещё работает в предыдущем такте — тик поглощается, + # но остальные уровни продолжают жить + continue + t = threads.get(name) + if t is not None and t.is_alive(): + continue + def _run(cfg=cfg, seq=seq): + model = es.run_projections() + state = process_level(cfg, model) + if state not in ("already",): + _log(f"{cfg.name} #{seq}: {state}") + t = threading.Thread(target=_run, daemon=True, name=f"tick-{name}") + threads[name] = t + t.start() + time.sleep(POLL) + + +if __name__ == "__main__": + main_loop() \ No newline at end of file diff --git a/venv/bin/Activate.ps1 b/venv/bin/Activate.ps1 new file mode 100644 index 0000000..b49d77b --- /dev/null +++ b/venv/bin/Activate.ps1 @@ -0,0 +1,247 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/venv/bin/__pycache__/jp.cpython-312.pyc b/venv/bin/__pycache__/jp.cpython-312.pyc new file mode 100644 index 0000000..5397390 Binary files /dev/null and b/venv/bin/__pycache__/jp.cpython-312.pyc differ diff --git a/venv/bin/activate b/venv/bin/activate new file mode 100644 index 0000000..1648672 --- /dev/null +++ b/venv/bin/activate @@ -0,0 +1,70 @@ +# This file must be used with "source bin/activate" *from bash* +# You cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # Call hash to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + hash -r 2> /dev/null + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +# on Windows, a path can contain colons and backslashes and has to be converted: +if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then + # transform D:\path\to\venv to /d/path/to/venv on MSYS + # and to /cygdrive/d/path/to/venv on Cygwin + export VIRTUAL_ENV=$(cygpath /root/simbiot-harness/venv) +else + # use the path as-is + export VIRTUAL_ENV=/root/simbiot-harness/venv +fi + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/"bin":$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1='(venv) '"${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT='(venv) ' + export VIRTUAL_ENV_PROMPT +fi + +# Call hash to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +hash -r 2> /dev/null diff --git a/venv/bin/activate.csh b/venv/bin/activate.csh new file mode 100644 index 0000000..c13843f --- /dev/null +++ b/venv/bin/activate.csh @@ -0,0 +1,27 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. + +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV /root/simbiot-harness/venv + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/"bin":$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = '(venv) '"$prompt" + setenv VIRTUAL_ENV_PROMPT '(venv) ' +endif + +alias pydoc python -m pydoc + +rehash diff --git a/venv/bin/activate.fish b/venv/bin/activate.fish new file mode 100644 index 0000000..b1d0dd6 --- /dev/null +++ b/venv/bin/activate.fish @@ -0,0 +1,69 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/). You cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + set -e _OLD_FISH_PROMPT_OVERRIDE + # prevents error when using nested fish instances (Issue #93858) + if functions -q _old_fish_prompt + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV /root/simbiot-harness/venv + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/"bin $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) '(venv) ' (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + set -gx VIRTUAL_ENV_PROMPT '(venv) ' +end diff --git a/venv/bin/cffi-gen-src b/venv/bin/cffi-gen-src new file mode 100755 index 0000000..2e055c9 --- /dev/null +++ b/venv/bin/cffi-gen-src @@ -0,0 +1,6 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +import sys +from cffi._cffi_gen_src import run +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(run()) diff --git a/venv/bin/httpx b/venv/bin/httpx new file mode 100755 index 0000000..8a772c4 --- /dev/null +++ b/venv/bin/httpx @@ -0,0 +1,6 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +import sys +from httpx import main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(main()) diff --git a/venv/bin/httpx2 b/venv/bin/httpx2 new file mode 100755 index 0000000..29c1f1d --- /dev/null +++ b/venv/bin/httpx2 @@ -0,0 +1,6 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +import sys +from httpx2._main import main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(main()) diff --git a/venv/bin/idna b/venv/bin/idna new file mode 100755 index 0000000..899b4d1 --- /dev/null +++ b/venv/bin/idna @@ -0,0 +1,6 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +import sys +from idna.cli import main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(main()) diff --git a/venv/bin/jp.py b/venv/bin/jp.py new file mode 100755 index 0000000..177e61e --- /dev/null +++ b/venv/bin/jp.py @@ -0,0 +1,54 @@ +#!/root/simbiot-harness/venv/bin/python3.12 + +import sys +import json +import argparse +from pprint import pformat + +import jmespath +from jmespath import exceptions + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('expression') + parser.add_argument('-f', '--filename', + help=('The filename containing the input data. ' + 'If a filename is not given then data is ' + 'read from stdin.')) + parser.add_argument('--ast', action='store_true', + help=('Pretty print the AST, do not search the data.')) + args = parser.parse_args() + expression = args.expression + if args.ast: + # Only print the AST + expression = jmespath.compile(args.expression) + sys.stdout.write(pformat(expression.parsed)) + sys.stdout.write('\n') + return 0 + if args.filename: + with open(args.filename, 'r') as f: + data = json.load(f) + else: + data = sys.stdin.read() + data = json.loads(data) + try: + sys.stdout.write(json.dumps( + jmespath.search(expression, data), indent=4, ensure_ascii=False)) + sys.stdout.write('\n') + except exceptions.ArityError as e: + sys.stderr.write("invalid-arity: %s\n" % e) + return 1 + except exceptions.JMESPathTypeError as e: + sys.stderr.write("invalid-type: %s\n" % e) + return 1 + except exceptions.UnknownFunctionError as e: + sys.stderr.write("unknown-function: %s\n" % e) + return 1 + except exceptions.ParseError as e: + sys.stderr.write("syntax-error: %s\n" % e) + return 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/venv/bin/jsonschema b/venv/bin/jsonschema new file mode 100755 index 0000000..1215c1e --- /dev/null +++ b/venv/bin/jsonschema @@ -0,0 +1,6 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +import sys +from jsonschema.cli import main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(main()) diff --git a/venv/bin/mcp b/venv/bin/mcp new file mode 100755 index 0000000..dcc5505 --- /dev/null +++ b/venv/bin/mcp @@ -0,0 +1,6 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +import sys +from mcp.cli import app +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(app()) diff --git a/venv/bin/monty b/venv/bin/monty new file mode 100755 index 0000000..5f0f875 Binary files /dev/null and b/venv/bin/monty differ diff --git a/venv/bin/opentelemetry-bootstrap b/venv/bin/opentelemetry-bootstrap new file mode 100755 index 0000000..dd535f5 --- /dev/null +++ b/venv/bin/opentelemetry-bootstrap @@ -0,0 +1,6 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +import sys +from opentelemetry.instrumentation.bootstrap import run +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(run()) diff --git a/venv/bin/opentelemetry-instrument b/venv/bin/opentelemetry-instrument new file mode 100755 index 0000000..720c538 --- /dev/null +++ b/venv/bin/opentelemetry-instrument @@ -0,0 +1,6 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +import sys +from opentelemetry.instrumentation.auto_instrumentation import run +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(run()) diff --git a/venv/bin/pip b/venv/bin/pip new file mode 100755 index 0000000..4d4ab60 --- /dev/null +++ b/venv/bin/pip @@ -0,0 +1,8 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip3 b/venv/bin/pip3 new file mode 100755 index 0000000..4d4ab60 --- /dev/null +++ b/venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip3.12 b/venv/bin/pip3.12 new file mode 100755 index 0000000..4d4ab60 --- /dev/null +++ b/venv/bin/pip3.12 @@ -0,0 +1,8 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/py-spy b/venv/bin/py-spy new file mode 100755 index 0000000..f35d48b Binary files /dev/null and b/venv/bin/py-spy differ diff --git a/venv/bin/pydantic-monty b/venv/bin/pydantic-monty new file mode 100755 index 0000000..2315898 --- /dev/null +++ b/venv/bin/pydantic-monty @@ -0,0 +1,6 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +import sys +from pydantic_monty._cli import main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(main()) diff --git a/venv/bin/python b/venv/bin/python new file mode 120000 index 0000000..11b9d88 --- /dev/null +++ b/venv/bin/python @@ -0,0 +1 @@ +python3.12 \ No newline at end of file diff --git a/venv/bin/python3 b/venv/bin/python3 new file mode 120000 index 0000000..11b9d88 --- /dev/null +++ b/venv/bin/python3 @@ -0,0 +1 @@ +python3.12 \ No newline at end of file diff --git a/venv/bin/python3.12 b/venv/bin/python3.12 new file mode 120000 index 0000000..dc92e12 --- /dev/null +++ b/venv/bin/python3.12 @@ -0,0 +1 @@ +/usr/bin/python3.12 \ No newline at end of file diff --git a/venv/bin/uvicorn b/venv/bin/uvicorn new file mode 100755 index 0000000..c1d2a1b --- /dev/null +++ b/venv/bin/uvicorn @@ -0,0 +1,6 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +import sys +from uvicorn.main import main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(main()) diff --git a/venv/bin/watchmedo b/venv/bin/watchmedo new file mode 100755 index 0000000..7a230a1 --- /dev/null +++ b/venv/bin/watchmedo @@ -0,0 +1,6 @@ +#!/root/simbiot-harness/venv/bin/python3.12 +import sys +from watchdog.watchmedo import main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(main()) diff --git a/venv/lib/python3.12/site-packages/__pycache__/six.cpython-312.pyc b/venv/lib/python3.12/site-packages/__pycache__/six.cpython-312.pyc new file mode 100644 index 0000000..e2913d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/__pycache__/six.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc b/venv/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc new file mode 100644 index 0000000..5b9a184 Binary files /dev/null and b/venv/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/_cffi_backend.cpython-312-x86_64-linux-gnu.so b/venv/lib/python3.12/site-packages/_cffi_backend.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000..c10f359 Binary files /dev/null and b/venv/lib/python3.12/site-packages/_cffi_backend.cpython-312-x86_64-linux-gnu.so differ diff --git a/venv/lib/python3.12/site-packages/_yaml/__init__.py b/venv/lib/python3.12/site-packages/_yaml/__init__.py new file mode 100644 index 0000000..7baa8c4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/_yaml/__init__.py @@ -0,0 +1,33 @@ +# This is a stub package designed to roughly emulate the _yaml +# extension module, which previously existed as a standalone module +# and has been moved into the `yaml` package namespace. +# It does not perfectly mimic its old counterpart, but should get +# close enough for anyone who's relying on it even when they shouldn't. +import yaml + +# in some circumstances, the yaml module we imoprted may be from a different version, so we need +# to tread carefully when poking at it here (it may not have the attributes we expect) +if not getattr(yaml, '__with_libyaml__', False): + from sys import version_info + + exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError + raise exc("No module named '_yaml'") +else: + from yaml._yaml import * + import warnings + warnings.warn( + 'The _yaml extension module is now located at yaml._yaml' + ' and its location is subject to change. To use the' + ' LibYAML-based parser and emitter, import from `yaml`:' + ' `from yaml import CLoader as Loader, CDumper as Dumper`.', + DeprecationWarning + ) + del warnings + # Don't `del yaml` here because yaml is actually an existing + # namespace member of _yaml. + +__name__ = '_yaml' +# If the module is top-level (i.e. not a part of any specific package) +# then the attribute should be set to ''. +# https://docs.python.org/3.8/library/types.html +__package__ = '' diff --git a/venv/lib/python3.12/site-packages/_yaml/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/_yaml/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..915b3c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/_yaml/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/METADATA new file mode 100644 index 0000000..eb5a088 --- /dev/null +++ b/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/METADATA @@ -0,0 +1,295 @@ +Metadata-Version: 2.4 +Name: annotated-types +Version: 0.8.0 +Summary: Reusable constraint types to use with typing.Annotated +Project-URL: Homepage, https://github.com/annotated-types/annotated-types +Project-URL: Source, https://github.com/annotated-types/annotated-types +Project-URL: Changelog, https://github.com/annotated-types/annotated-types/releases +Author-email: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>, Samuel Colvin , Zac Hatfield-Dodds +License-Expression: MIT +License-File: LICENSE +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Console +Classifier: Environment :: MacOS X +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Unix +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Typing :: Typed +Requires-Python: >=3.10 +Description-Content-Type: text/markdown + +# annotated-types + +[![CI](https://github.com/annotated-types/annotated-types/workflows/CI/badge.svg?event=push)](https://github.com/annotated-types/annotated-types/actions?query=event%3Apush+branch%3Amain+workflow%3ACI) +[![pypi](https://img.shields.io/pypi/v/annotated-types.svg)](https://pypi.python.org/pypi/annotated-types) +[![versions](https://img.shields.io/pypi/pyversions/annotated-types.svg)](https://github.com/annotated-types/annotated-types) +[![license](https://img.shields.io/github/license/annotated-types/annotated-types.svg)](https://github.com/annotated-types/annotated-types/blob/main/LICENSE) + +[PEP-593](https://peps.python.org/pep-0593/) added `typing.Annotated` as a way of +adding context-specific metadata to existing types, and specifies that +`Annotated[T, x]` _should_ be treated as `T` by any tool or library without special +logic for `x`. + +This package provides metadata objects which can be used to represent common +constraints such as upper and lower bounds on scalar values and collection sizes, +a `Predicate` marker for runtime checks, and +descriptions of how we intend these metadata to be interpreted. In some cases, +we also note alternative representations which do not require this package. + +## Install + +```bash +pip install annotated-types +``` + +## Examples + +```python +from typing import Annotated +from annotated_types import Gt, Len, Predicate + +class MyClass: + age: Annotated[int, Gt(18)] # Valid: 19, 20, ... + # Invalid: 17, 18, "19", 19.0, ... + factors: list[Annotated[int, Predicate(is_prime)]] # Valid: 2, 3, 5, 7, 11, ... + # Invalid: 4, 8, -2, 5.0, "prime", ... + + my_list: Annotated[list[int], Len(0, 10)] # Valid: [], [10, 20, 30, 40, 50] + # Invalid: (1, 2), ["abc"], [0] * 20 +``` + +## Documentation + +_While `annotated-types` avoids runtime checks for performance, users should not +construct invalid combinations such as `MultipleOf("non-numeric")` or `Annotated[int, Len(3)]`. +Downstream implementors may choose to raise an error, emit a warning, silently ignore +a metadata item, etc., if the metadata objects described below are used with an +incompatible type - or for any other reason!_ + +### Gt, Ge, Lt, Le + +Express inclusive and/or exclusive bounds on orderable values - which may be numbers, +dates, times, strings, sets, etc. Note that the boundary value need not be of the +same type that was annotated, so long as they can be compared: `Annotated[int, Gt(1.5)]` +is fine, for example, and implies that the value is an integer x such that `x > 1.5`. + +We suggest that implementors may also interpret `functools.partial(operator.le, 1.5)` +as being equivalent to `Gt(1.5)`, for users who wish to avoid a runtime dependency on +the `annotated-types` package. + +To be explicit, these types have the following meanings: + +* `Gt(x)` - value must be "Greater Than" `x` - equivalent to exclusive minimum +* `Ge(x)` - value must be "Greater than or Equal" to `x` - equivalent to inclusive minimum +* `Lt(x)` - value must be "Less Than" `x` - equivalent to exclusive maximum +* `Le(x)` - value must be "Less than or Equal" to `x` - equivalent to inclusive maximum + +### Interval + +`Interval(gt, ge, lt, le)` allows you to specify an upper and lower bound with a single +metadata object. `None` attributes should be ignored, and non-`None` attributes +treated as per the single bounds above. + +### MultipleOf + +`MultipleOf(multiple_of=x)` might be interpreted in two ways: + +1. Python semantics, implying `value % multiple_of == 0`, or +2. [JSONschema semantics](https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.2.1), + where `int(value / multiple_of) == value / multiple_of`. + +We encourage users to be aware of these two common interpretations and their +distinct behaviours, especially since very large or non-integer numbers make +it easy to cause silent data corruption due to floating-point imprecision. + +We encourage libraries to carefully document which interpretation they implement. + +### MinLen, MaxLen, Len + +`Len()` implies that `min_length <= len(value) <= max_length` - lower and upper bounds are inclusive. + +As well as `Len()` which can optionally include upper and lower bounds, we also +provide `MinLen(x)` and `MaxLen(y)` which are equivalent to `Len(min_length=x)` +and `Len(max_length=y)` respectively. + +`Len`, `MinLen`, and `MaxLen` may be used with any type which supports `len(value)`. + +Examples of usage: + +* `Annotated[list, MaxLen(10)]` (or `Annotated[list, Len(max_length=10)]`) - list must have a length of 10 or less +* `Annotated[str, MaxLen(10)]` - string must have a length of 10 or less +* `Annotated[list, MinLen(3)]` (or `Annotated[list, Len(min_length=3)]`) - list must have a length of 3 or more +* `Annotated[list, Len(4, 6)]` - list must have a length of 4, 5, or 6 +* `Annotated[list, Len(8, 8)]` - list must have a length of exactly 8 + +#### Changed in v0.4.0 + +* `min_inclusive` has been renamed to `min_length`, no change in meaning +* `max_exclusive` has been renamed to `max_length`, upper bound is now **inclusive** instead of **exclusive** +* The recommendation that slices are interpreted as `Len` has been removed due to ambiguity and different semantic + meaning of the upper bound in slices vs. `Len` + +See [issue #23](https://github.com/annotated-types/annotated-types/issues/23) for discussion. + +### Timezone + +`Timezone` can be used with a `datetime` or a `time` to express which timezones +are allowed. `Annotated[datetime, Timezone(None)]` must be a naive datetime. +`Timezone[...]` ([literal ellipsis](https://docs.python.org/3/library/constants.html#Ellipsis)) +expresses that any timezone-aware datetime is allowed. You may also pass a specific +timezone string or [`tzinfo`](https://docs.python.org/3/library/datetime.html#tzinfo-objects) +object such as `Timezone(timezone.utc)` or `Timezone("Africa/Abidjan")` to express that you only +allow a specific timezone, though we note that this is often a symptom of fragile design. + +#### Changed in v0.x.x + +* `Timezone` accepts [`tzinfo`](https://docs.python.org/3/library/datetime.html#tzinfo-objects) objects instead of + `timezone`, extending compatibility to [`zoneinfo`](https://docs.python.org/3/library/zoneinfo.html) and third party libraries. + +### Unit + +`Unit(unit: str)` expresses that the annotated numeric value is the magnitude of +a quantity with the specified unit. For example, `Annotated[float, Unit("m/s")]` +would be a float representing a velocity in meters per second. + +Please note that `annotated_types` itself makes no attempt to parse or validate +the unit string in any way. That is left entirely to downstream libraries, +such as [`pint`](https://pint.readthedocs.io) or +[`astropy.units`](https://docs.astropy.org/en/stable/units/). + +An example of how a library might use this metadata: + +```python +from annotated_types import Unit +from typing import Annotated, TypeVar, Callable, Any, get_origin, get_args + +# given a type annotated with a unit: +Meters = Annotated[float, Unit("m")] + + +# you can cast the annotation to a specific unit type with any +# callable that accepts a string and returns the desired type +T = TypeVar("T") +def cast_unit(tp: Any, unit_cls: Callable[[str], T]) -> T | None: + if get_origin(tp) is Annotated: + for arg in get_args(tp): + if isinstance(arg, Unit): + return unit_cls(arg.unit) + return None + + +# using `pint` +import pint +pint_unit = cast_unit(Meters, pint.Unit) + + +# using `astropy.units` +import astropy.units as u +astropy_unit = cast_unit(Meters, u.Unit) +``` + +### Predicate + +`Predicate(func: Callable)` expresses that `func(value)` is truthy for valid values. +Users should prefer the statically inspectable metadata above, but if you need +the full power and flexibility of arbitrary runtime predicates... here it is. + +For some common constraints, we provide generic types: + +* `LowerCase = Annotated[T, Predicate(str.islower)]` +* `UpperCase = Annotated[T, Predicate(str.isupper)]` +* `IsDigit = Annotated[T, Predicate(str.isdigit)]` +* `IsFinite = Annotated[T, Predicate(math.isfinite)]` +* `IsNotFinite = Annotated[T, Predicate(Not(math.isfinite))]` +* `IsNan = Annotated[T, Predicate(math.isnan)]` +* `IsNotNan = Annotated[T, Predicate(Not(math.isnan))]` +* `IsInfinite = Annotated[T, Predicate(math.isinf)]` +* `IsNotInfinite = Annotated[T, Predicate(Not(math.isinf))]` + +so that you can write e.g. `x: IsFinite[float] = 2.0` instead of the longer +(but exactly equivalent) `x: Annotated[float, Predicate(math.isfinite)] = 2.0`. + +Some libraries might have special logic to handle known or understandable predicates, +for example by checking for `str.isdigit` and using its presence to both call custom +logic to enforce digit-only strings, and customise some generated external schema. +Users are therefore encouraged to avoid indirection like `lambda s: s.lower()`, in +favor of introspectable methods such as `str.lower` or `re.compile("pattern").search`. + +To enable basic negation of commonly used predicates like `math.isnan` without introducing introspection that makes it impossible for implementers to introspect the predicate we provide a `Not` wrapper that simply negates the predicate in an introspectable manner. Several of the predicates listed above are created in this manner. + +We do not specify what behaviour should be expected for predicates that raise +an exception. For example `Annotated[int, Predicate(str.isdigit)]` might silently +skip invalid constraints, or statically raise an error; or it might try calling it +and then propagate or discard the resulting +`TypeError: descriptor 'isdigit' for 'str' objects doesn't apply to a 'int' object` +exception. We encourage libraries to document the behaviour they choose. + +### Doc + +`doc()` can be used to add documentation information in `Annotated`, for function and method parameters, variables, class attributes, return types, and any place where `Annotated` can be used. + +It expects a value that can be statically analyzed, as the main use case is for static analysis, editors, documentation generators, and similar tools. + +It returns a `DocInfo` class with a single attribute `documentation` containing the value passed to `doc()`. + +This is the early adopter's alternative form of the [`typing-doc` proposal](https://github.com/tiangolo/fastapi/blob/typing-doc/typing_doc.md). + +### Integrating downstream types with `GroupedMetadata` + +Implementers may choose to provide a convenience wrapper that groups multiple pieces of metadata. +This can help reduce verbosity and cognitive overhead for users. +For example, an implementer like Pydantic might provide a `Field` or `Meta` type that accepts keyword arguments and transforms these into low-level metadata: + +```python +from dataclasses import dataclass +from typing import Iterator +from annotated_types import GroupedMetadata, Ge + +@dataclass +class Field(GroupedMetadata): + ge: int | None = None + description: str | None = None + + def __iter__(self) -> Iterator[object]: + # Iterating over a GroupedMetadata object should yield annotated-types + # constraint metadata objects which describe it as fully as possible, + # and may include other unknown objects too. + if self.ge is not None: + yield Ge(self.ge) + if self.description is not None: + yield Description(self.description) +``` + +Libraries consuming annotated-types constraints should check for `GroupedMetadata` and unpack it by iterating over the object and treating the results as if they had been "unpacked" in the `Annotated` type. The same logic should be applied to the [PEP 646 `Unpack` type](https://peps.python.org/pep-0646/), so that `Annotated[T, Field(...)]`, `Annotated[T, Unpack[Field(...)]]` and `Annotated[T, *Field(...)]` are all treated consistently. + +Libraries consuming annotated-types should also ignore any metadata they do not recongize that came from unpacking a `GroupedMetadata`, just like they ignore unrecognized metadata in `Annotated` itself. + +Our own `annotated_types.Interval` class is a `GroupedMetadata` which unpacks itself into `Gt`, `Lt`, etc., so this is not an abstract concern. Similarly, `annotated_types.Len` is a `GroupedMetadata` which unpacks itself into `MinLen` (optionally) and `MaxLen`. + +### Consuming metadata + +We intend to not be prescriptive as to _how_ the metadata and constraints are used, but as an example of how one might parse constraints from types annotations see our [implementation in `test_main.py`](https://github.com/annotated-types/annotated-types/blob/f59cf6d1b5255a0fe359b93896759a180bec30ae/tests/test_main.py#L94-L103). + +It is up to the implementer to determine how this metadata is used. +You could use the metadata for runtime type checking, for generating schemas or to generate example data, amongst other use cases. + +## Design & History + +This package was designed at the PyCon 2022 sprints by the maintainers of Pydantic +and Hypothesis, with the goal of making it as easy as possible for end-users to +provide more informative annotations for use by runtime libraries. + +It is deliberately minimal, and following PEP-593 allows considerable downstream +discretion in what (if anything!) they choose to support. Nonetheless, we expect +that staying simple and covering _only_ the most common use-cases will give users +and maintainers the best experience we can. If you'd like more constraints for your +types - follow our lead, by defining them and documenting them downstream! diff --git a/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/RECORD new file mode 100644 index 0000000..3bcb37f --- /dev/null +++ b/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/RECORD @@ -0,0 +1,10 @@ +annotated_types-0.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +annotated_types-0.8.0.dist-info/METADATA,sha256=YUmFsnj2Abjvhj-CeDuZHMOtpLifWS63HHxIxfLvIpg,15009 +annotated_types-0.8.0.dist-info/RECORD,, +annotated_types-0.8.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87 +annotated_types-0.8.0.dist-info/licenses/LICENSE,sha256=_hBJiEsaDZNCkB6I4H8ykl0ksxIdmXK2poBfuYJLCV0,1083 +annotated_types/__init__.py,sha256=pxBKTUObJ6n3T8C-I2ubobeDHmBEAmgCogWrwSmKm8g,13273 +annotated_types/__pycache__/__init__.cpython-312.pyc,, +annotated_types/__pycache__/test_cases.cpython-312.pyc,, +annotated_types/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +annotated_types/test_cases.py,sha256=2GdHKstXuBzpf3iXKCjmvFZpyk2zP9Hm9kXGGDhGBo4,6310 diff --git a/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/WHEEL new file mode 100644 index 0000000..7401812 --- /dev/null +++ b/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.31.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..d99323a --- /dev/null +++ b/venv/lib/python3.12/site-packages/annotated_types-0.8.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 the contributors + +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. diff --git a/venv/lib/python3.12/site-packages/annotated_types/__init__.py b/venv/lib/python3.12/site-packages/annotated_types/__init__.py new file mode 100644 index 0000000..dcb35c5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/annotated_types/__init__.py @@ -0,0 +1,416 @@ +import math +import types +from collections.abc import Callable, Iterator +from dataclasses import dataclass +from datetime import tzinfo +from types import EllipsisType +from typing import ( + TYPE_CHECKING, + Annotated, + Any, + Literal, + Protocol, + SupportsFloat, + SupportsIndex, + TypeVar, + Union, + runtime_checkable, +) + +__all__ = ( + 'BaseMetadata', + 'GroupedMetadata', + 'Gt', + 'Ge', + 'Lt', + 'Le', + 'Interval', + 'MultipleOf', + 'MinLen', + 'MaxLen', + 'Len', + 'Timezone', + 'Predicate', + 'LowerCase', + 'UpperCase', + 'IsDigits', + 'IsFinite', + 'IsNotFinite', + 'IsNan', + 'IsNotNan', + 'IsInfinite', + 'IsNotInfinite', + 'doc', + 'DocInfo', + '__version__', +) + +__version__ = '0.8.0' + + +T = TypeVar('T') + + +# arguments that start with __ are considered +# positional only +# see https://peps.python.org/pep-0484/#positional-only-arguments + + +class SupportsGt(Protocol): + def __gt__(self: T, __other: T) -> bool: + ... + + +class SupportsGe(Protocol): + def __ge__(self: T, __other: T) -> bool: + ... + + +class SupportsLt(Protocol): + def __lt__(self: T, __other: T) -> bool: + ... + + +class SupportsLe(Protocol): + def __le__(self: T, __other: T) -> bool: + ... + + +class SupportsMod(Protocol): + def __mod__(self: T, __other: T) -> T: + ... + + +class SupportsDiv(Protocol): + def __div__(self: T, __other: T) -> T: + ... + + +class BaseMetadata: + """Base class for all metadata. + + This exists mainly so that implementers + can do `isinstance(..., BaseMetadata)` while traversing field annotations. + """ + + __slots__ = () + + +@dataclass(frozen=True, slots=True) +class Gt(BaseMetadata): + """Gt(gt=x) implies that the value must be greater than x. + + It can be used with any type that supports the ``>`` operator, + including numbers, dates and times, strings, sets, and so on. + """ + + gt: SupportsGt + + +@dataclass(frozen=True, slots=True) +class Ge(BaseMetadata): + """Ge(ge=x) implies that the value must be greater than or equal to x. + + It can be used with any type that supports the ``>=`` operator, + including numbers, dates and times, strings, sets, and so on. + """ + + ge: SupportsGe + + +@dataclass(frozen=True, slots=True) +class Lt(BaseMetadata): + """Lt(lt=x) implies that the value must be less than x. + + It can be used with any type that supports the ``<`` operator, + including numbers, dates and times, strings, sets, and so on. + """ + + lt: SupportsLt + + +@dataclass(frozen=True, slots=True) +class Le(BaseMetadata): + """Le(le=x) implies that the value must be less than or equal to x. + + It can be used with any type that supports the ``<=`` operator, + including numbers, dates and times, strings, sets, and so on. + """ + + le: SupportsLe + + +@runtime_checkable +class GroupedMetadata(Protocol): + """A grouping of multiple objects, like typing.Unpack. + + `GroupedMetadata` on its own is not metadata and has no meaning. + All of the constraints and metadata should be fully expressable + in terms of the `BaseMetadata`'s returned by `GroupedMetadata.__iter__()`. + + Concrete implementations should override `GroupedMetadata.__iter__()` + to add their own metadata. + For example: + + >>> @dataclass + >>> class Field(GroupedMetadata): + >>> gt: float | None = None + >>> description: str | None = None + ... + >>> def __iter__(self) -> Iterable[object]: + >>> if self.gt is not None: + >>> yield Gt(self.gt) + >>> if self.description is not None: + >>> yield Description(self.gt) + + Also see the implementation of `Interval` below for an example. + + Parsers should recognize this and unpack it so that it can be used + both with and without unpacking: + + - `Annotated[int, Field(...)]` (parser must unpack Field) + - `Annotated[int, *Field(...)]` (PEP-646) + """ # noqa: trailing-whitespace + + @property + def __is_annotated_types_grouped_metadata__(self) -> Literal[True]: + return True + + def __iter__(self) -> Iterator[object]: + ... + + if not TYPE_CHECKING: + __slots__ = () # allow subclasses to use slots + + def __init_subclass__(cls, *args: Any, **kwargs: Any) -> None: + # Basic ABC like functionality without the complexity of an ABC + super().__init_subclass__(*args, **kwargs) + if cls.__iter__ is GroupedMetadata.__iter__: + raise TypeError("Can't subclass GroupedMetadata without implementing __iter__") + + def __iter__(self) -> Iterator[object]: # noqa: F811 + raise NotImplementedError # more helpful than "None has no attribute..." type errors + + +@dataclass(frozen=True, kw_only=True, slots=True) +class Interval(GroupedMetadata): + """Interval can express inclusive or exclusive bounds with a single object. + + It accepts keyword arguments ``gt``, ``ge``, ``lt``, and/or ``le``, which + are interpreted the same way as the single-bound constraints. + """ + + gt: SupportsGt | None = None + ge: SupportsGe | None = None + lt: SupportsLt | None = None + le: SupportsLe | None = None + + def __iter__(self) -> Iterator[BaseMetadata]: + """Unpack an Interval into zero or more single-bounds.""" + if self.gt is not None: + yield Gt(self.gt) + if self.ge is not None: + yield Ge(self.ge) + if self.lt is not None: + yield Lt(self.lt) + if self.le is not None: + yield Le(self.le) + + +@dataclass(frozen=True, slots=True) +class MultipleOf(BaseMetadata): + """MultipleOf(multiple_of=x) might be interpreted in two ways: + + 1. Python semantics, implying ``value % multiple_of == 0``, or + 2. JSONschema semantics, where ``int(value / multiple_of) == value / multiple_of`` + + We encourage users to be aware of these two common interpretations, + and libraries to carefully document which they implement. + """ + + multiple_of: SupportsDiv | SupportsMod + + +@dataclass(frozen=True, slots=True) +class MinLen(BaseMetadata): + """ + MinLen() implies minimum inclusive length, + e.g. ``len(value) >= min_length``. + """ + + min_length: Annotated[int, Ge(0)] + + +@dataclass(frozen=True, slots=True) +class MaxLen(BaseMetadata): + """ + MaxLen() implies maximum inclusive length, + e.g. ``len(value) <= max_length``. + """ + + max_length: Annotated[int, Ge(0)] + + +@dataclass(frozen=True, slots=True) +class Len(GroupedMetadata): + """ + Len() implies that ``min_length <= len(value) <= max_length``. + + Upper bound may be omitted or ``None`` to indicate no upper length bound. + """ + + min_length: Annotated[int, Ge(0)] = 0 + max_length: Annotated[int, Ge(0)] | None = None + + def __iter__(self) -> Iterator[BaseMetadata]: + """Unpack a Len into zero or more single-bounds.""" + if self.min_length > 0: + yield MinLen(self.min_length) + if self.max_length is not None: + yield MaxLen(self.max_length) + + +@dataclass(frozen=True, slots=True) +class Timezone(BaseMetadata): + """Timezone(tz=...) requires a datetime to be aware (or ``tz=None``, naive). + + ``Annotated[datetime, Timezone(None)]`` must be a naive datetime. + ``Timezone(...)`` (the ellipsis literal) expresses that the datetime must be + tz-aware but any timezone is allowed. + + You may also pass a specific timezone string or tzinfo object such as + ``Timezone(timezone.utc)`` or ``Timezone("Africa/Abidjan")`` to express that + you only allow a specific timezone, though we note that this is often + a symptom of poor design. + """ + + tz: str | tzinfo | EllipsisType | None + + +@dataclass(frozen=True, slots=True) +class Unit(BaseMetadata): + """Indicates that the value is a physical quantity with the specified unit. + + It is intended for usage with numeric types, where the value represents the + magnitude of the quantity. For example, ``distance: Annotated[float, Unit('m')]`` + or ``speed: Annotated[float, Unit('m/s')]``. + + Interpretation of the unit string is left to the discretion of the consumer. + It is suggested to follow conventions established by python libraries that work + with physical quantities, such as + + - ``pint`` : + - ``astropy.units``: + + For indicating a quantity with a certain dimensionality but without a specific unit + it is recommended to use square brackets, e.g. `Annotated[float, Unit('[time]')]`. + Note, however, ``annotated_types`` itself makes no use of the unit string. + """ + + unit: str + + +@dataclass(frozen=True, slots=True) +class Predicate(BaseMetadata): + """``Predicate(func: Callable)`` implies `func(value)` is truthy for valid values. + + Users should prefer statically inspectable metadata, but if you need the full + power and flexibility of arbitrary runtime predicates... here it is. + + We provide a few predefined predicates for common string constraints: + ``LowerCase = Predicate(str.islower)``, ``UpperCase = Predicate(str.isupper)``, and + ``IsDigits = Predicate(str.isdigit)``. Users are encouraged to use methods which + can be given special handling, and avoid indirection like ``lambda s: s.lower()``. + + Some libraries might have special logic to handle certain predicates, e.g. by + checking for `str.isdigit` and using its presence to both call custom logic to + enforce digit-only strings, and customise some generated external schema. + + We do not specify what behaviour should be expected for predicates that raise + an exception. For example `Annotated[int, Predicate(str.isdigit)]` might silently + skip invalid constraints, or statically raise an error; or it might try calling it + and then propagate or discard the resulting exception. + """ + + func: Callable[[Any], bool] + + def __repr__(self) -> str: + if getattr(self.func, "__name__", "") == "": + return f"{self.__class__.__name__}({self.func!r})" + if isinstance(self.func, (types.MethodType, types.BuiltinMethodType)) and ( + namespace := getattr(self.func.__self__, "__name__", None) + ): + return f"{self.__class__.__name__}({namespace}.{self.func.__name__})" + if isinstance(self.func, type(str.isascii)): # method descriptor + return f"{self.__class__.__name__}({self.func.__qualname__})" + return f"{self.__class__.__name__}({self.func.__name__})" + + +@dataclass +class Not: + func: Callable[[Any], bool] + + def __call__(self, __v: Any) -> bool: + return not self.func(__v) + + +_StrType = TypeVar("_StrType", bound=str) + +LowerCase = Annotated[_StrType, Predicate(str.islower)] +""" +Return True if the string is a lowercase string, False otherwise. + +A string is lowercase if all cased characters in the string are lowercase and there is at least one cased character in the string. +""" # noqa: E501 +UpperCase = Annotated[_StrType, Predicate(str.isupper)] +""" +Return True if the string is an uppercase string, False otherwise. + +A string is uppercase if all cased characters in the string are uppercase and there is at least one cased character in the string. +""" # noqa: E501 +IsDigit = Annotated[_StrType, Predicate(str.isdigit)] +IsDigits = IsDigit # type: ignore # plural for backwards compatibility, see #63 +""" +Return True if the string is a digit string, False otherwise. + +A string is a digit string if all characters in the string are digits and there is at least one character in the string. +""" # noqa: E501 +IsAscii = Annotated[_StrType, Predicate(str.isascii)] +""" +Return True if all characters in the string are ASCII, False otherwise. + +ASCII characters have code points in the range U+0000-U+007F. Empty string is ASCII too. +""" + +_NumericType = TypeVar('_NumericType', bound=Union[SupportsFloat, SupportsIndex]) +IsFinite = Annotated[_NumericType, Predicate(math.isfinite)] +"""Return True if x is neither an infinity nor a NaN, and False otherwise.""" +IsNotFinite = Annotated[_NumericType, Predicate(Not(math.isfinite))] +"""Return True if x is one of infinity or NaN, and False otherwise""" +IsNan = Annotated[_NumericType, Predicate(math.isnan)] +"""Return True if x is a NaN (not a number), and False otherwise.""" +IsNotNan = Annotated[_NumericType, Predicate(Not(math.isnan))] +"""Return True if x is anything but NaN (not a number), and False otherwise.""" +IsInfinite = Annotated[_NumericType, Predicate(math.isinf)] +"""Return True if x is a positive or negative infinity, and False otherwise.""" +IsNotInfinite = Annotated[_NumericType, Predicate(Not(math.isinf))] +"""Return True if x is neither a positive or negative infinity, and False otherwise.""" + +try: + # PEP 727 – Documentation in Annotated Metadata + from typing_extensions import Doc # type: ignore[attr-defined] +except ImportError: + + @dataclass(frozen=True, slots=True) + class Doc: # type: ignore [no-redef] + """ " + The return value of doc(), mainly to be used by tools that want to extract the + Annotated documentation at runtime. + """ + + documentation: str + """The documentation string passed to doc().""" + + +DocInfo = Doc # backwards compatibility +doc = Doc diff --git a/venv/lib/python3.12/site-packages/annotated_types/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/annotated_types/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a592e82 Binary files /dev/null and b/venv/lib/python3.12/site-packages/annotated_types/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/annotated_types/__pycache__/test_cases.cpython-312.pyc b/venv/lib/python3.12/site-packages/annotated_types/__pycache__/test_cases.cpython-312.pyc new file mode 100644 index 0000000..ad15fd4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/annotated_types/__pycache__/test_cases.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/annotated_types/py.typed b/venv/lib/python3.12/site-packages/annotated_types/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/annotated_types/test_cases.py b/venv/lib/python3.12/site-packages/annotated_types/test_cases.py new file mode 100644 index 0000000..fba3c25 --- /dev/null +++ b/venv/lib/python3.12/site-packages/annotated_types/test_cases.py @@ -0,0 +1,146 @@ +import math +from collections.abc import Iterable, Iterator +from datetime import date, datetime, timedelta, timezone +from decimal import Decimal +from typing import Annotated, Any, NamedTuple + +import annotated_types as at + + +class Case(NamedTuple): + """ + A test case for `annotated_types`. + """ + + annotation: Any + valid_cases: Iterable[Any] + invalid_cases: Iterable[Any] + + +def cases() -> Iterable[Case]: + # Gt, Ge, Lt, Le + yield Case(Annotated[int, at.Gt(4)], (5, 6, 1000), (4, 0, -1)) + yield Case(Annotated[float, at.Gt(0.5)], (0.6, 0.7, 0.8, 0.9), (0.5, 0.0, -0.1)) + yield Case( + Annotated[datetime, at.Gt(datetime(2000, 1, 1))], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + [datetime(2000, 1, 1), datetime(1999, 12, 31)], + ) + yield Case( + Annotated[datetime, at.Gt(date(2000, 1, 1))], + [date(2000, 1, 2), date(2000, 1, 3)], + [date(2000, 1, 1), date(1999, 12, 31)], + ) + yield Case( + Annotated[datetime, at.Gt(Decimal('1.123'))], + [Decimal('1.1231'), Decimal('123')], + [Decimal('1.123'), Decimal('0')], + ) + + yield Case(Annotated[int, at.Ge(4)], (4, 5, 6, 1000, 4), (0, -1)) + yield Case(Annotated[float, at.Ge(0.5)], (0.5, 0.6, 0.7, 0.8, 0.9), (0.4, 0.0, -0.1)) + yield Case( + Annotated[datetime, at.Ge(datetime(2000, 1, 1))], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + [datetime(1998, 1, 1), datetime(1999, 12, 31)], + ) + + yield Case(Annotated[int, at.Lt(4)], (0, -1), (4, 5, 6, 1000, 4)) + yield Case(Annotated[float, at.Lt(0.5)], (0.4, 0.0, -0.1), (0.5, 0.6, 0.7, 0.8, 0.9)) + yield Case( + Annotated[datetime, at.Lt(datetime(2000, 1, 1))], + [datetime(1999, 12, 31), datetime(1999, 12, 31)], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + ) + + yield Case(Annotated[int, at.Le(4)], (4, 0, -1), (5, 6, 1000)) + yield Case(Annotated[float, at.Le(0.5)], (0.5, 0.0, -0.1), (0.6, 0.7, 0.8, 0.9)) + yield Case( + Annotated[datetime, at.Le(datetime(2000, 1, 1))], + [datetime(2000, 1, 1), datetime(1999, 12, 31)], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + ) + + # Interval + yield Case(Annotated[int, at.Interval(gt=4)], (5, 6, 1000), (4, 0, -1)) + yield Case(Annotated[int, at.Interval(gt=4, lt=10)], (5, 6), (4, 10, 1000, 0, -1)) + yield Case(Annotated[float, at.Interval(ge=0.5, le=1)], (0.5, 0.9, 1), (0.49, 1.1)) + yield Case( + Annotated[datetime, at.Interval(gt=datetime(2000, 1, 1), le=datetime(2000, 1, 3))], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + [datetime(2000, 1, 1), datetime(2000, 1, 4)], + ) + + yield Case(Annotated[int, at.MultipleOf(multiple_of=3)], (0, 3, 9), (1, 2, 4)) + yield Case(Annotated[float, at.MultipleOf(multiple_of=0.5)], (0, 0.5, 1, 1.5), (0.4, 1.1)) + + # lengths + + yield Case(Annotated[str, at.MinLen(3)], ('123', '1234', 'x' * 10), ('', '1', '12')) + yield Case(Annotated[str, at.Len(3)], ('123', '1234', 'x' * 10), ('', '1', '12')) + yield Case(Annotated[list[int], at.MinLen(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2])) + yield Case(Annotated[list[int], at.Len(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2])) + + yield Case(Annotated[str, at.MaxLen(4)], ('', '1234'), ('12345', 'x' * 10)) + yield Case(Annotated[str, at.Len(0, 4)], ('', '1234'), ('12345', 'x' * 10)) + yield Case(Annotated[list[str], at.MaxLen(4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10)) + yield Case(Annotated[list[str], at.Len(0, 4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10)) + + yield Case(Annotated[str, at.Len(3, 5)], ('123', '12345'), ('', '1', '12', '123456', 'x' * 10)) + yield Case(Annotated[str, at.Len(3, 3)], ('123',), ('12', '1234')) + + yield Case(Annotated[dict[int, int], at.Len(2, 3)], [{1: 1, 2: 2}], [{}, {1: 1}, {1: 1, 2: 2, 3: 3, 4: 4}]) + yield Case(Annotated[set[int], at.Len(2, 3)], ({1, 2}, {1, 2, 3}), (set(), {1}, {1, 2, 3, 4})) + yield Case(Annotated[tuple[int, ...], at.Len(2, 3)], ((1, 2), (1, 2, 3)), ((), (1,), (1, 2, 3, 4))) + + # Timezone + + yield Case( + Annotated[datetime, at.Timezone(None)], [datetime(2000, 1, 1)], [datetime(2000, 1, 1, tzinfo=timezone.utc)] + ) + yield Case( + Annotated[datetime, at.Timezone(...)], [datetime(2000, 1, 1, tzinfo=timezone.utc)], [datetime(2000, 1, 1)] + ) + yield Case( + Annotated[datetime, at.Timezone(timezone.utc)], + [datetime(2000, 1, 1, tzinfo=timezone.utc)], + [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))], + ) + yield Case( + Annotated[datetime, at.Timezone('Europe/London')], + [datetime(2000, 1, 1, tzinfo=timezone(timedelta(0), name='Europe/London'))], + [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))], + ) + + # Quantity + + yield Case(Annotated[float, at.Unit(unit='m')], (5, 4.2), ('5m', '4.2m')) + + # predicate types + + yield Case(at.LowerCase[str], ['abc', 'foobar'], ['', 'A', 'Boom']) + yield Case(at.UpperCase[str], ['ABC', 'DEFO'], ['', 'a', 'abc', 'AbC']) + yield Case(at.IsDigit[str], ['123'], ['', 'ab', 'a1b2']) + yield Case(at.IsAscii[str], ['123', 'foo bar'], ['£100', '😊', 'whatever 👀']) + + yield Case(Annotated[int, at.Predicate(lambda x: x % 2 == 0)], [0, 2, 4], [1, 3, 5]) + + yield Case(at.IsFinite[float], [1.23], [math.nan, math.inf, -math.inf]) + yield Case(at.IsNotFinite[float], [math.nan, math.inf], [1.23]) + yield Case(at.IsNan[float], [math.nan], [1.23, math.inf]) + yield Case(at.IsNotNan[float], [1.23, math.inf], [math.nan]) + yield Case(at.IsInfinite[float], [math.inf], [math.nan, 1.23]) + yield Case(at.IsNotInfinite[float], [math.nan, 1.23], [math.inf]) + + # check stacked predicates + yield Case(at.IsInfinite[Annotated[float, at.Predicate(lambda x: x > 0)]], [math.inf], [-math.inf, 1.23, math.nan]) + + # doc + yield Case(Annotated[int, at.doc("A number")], [1, 2], []) + + # custom GroupedMetadata + class MyCustomGroupedMetadata(at.GroupedMetadata): + def __iter__(self) -> Iterator[at.Predicate]: + yield at.Predicate(lambda x: float(x).is_integer()) + + yield Case(Annotated[float, MyCustomGroupedMetadata()], [0, 2.0], [0.01, 1.5]) diff --git a/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/METADATA b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/METADATA new file mode 100644 index 0000000..13734ec --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/METADATA @@ -0,0 +1,108 @@ +Metadata-Version: 2.4 +Name: anyio +Version: 4.15.1 +Summary: High-level concurrency and networking framework on top of asyncio or Trio +Author-email: Alex Grönholm +License-Expression: MIT +Project-URL: Documentation, https://anyio.readthedocs.io/en/latest/ +Project-URL: Changelog, https://anyio.readthedocs.io/en/stable/versionhistory.html +Project-URL: Source code, https://github.com/agronholm/anyio +Project-URL: Issue tracker, https://github.com/agronholm/anyio/issues +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Framework :: AnyIO +Classifier: Typing :: Typed +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: 3.15 +Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta +Requires-Python: >=3.10 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: exceptiongroup>=1.0.2; python_version < "3.11" +Requires-Dist: idna>=2.8 +Requires-Dist: typing_extensions>=4.16.0; python_version < "3.15" +Provides-Extra: trio +Requires-Dist: trio>=0.32.0; extra == "trio" +Dynamic: license-file + +.. image:: https://github.com/agronholm/anyio/actions/workflows/test.yml/badge.svg + :target: https://github.com/agronholm/anyio/actions/workflows/test.yml + :alt: Build Status +.. image:: https://coveralls.io/repos/github/agronholm/anyio/badge.svg?branch=master + :target: https://coveralls.io/github/agronholm/anyio?branch=master + :alt: Code Coverage +.. image:: https://readthedocs.org/projects/anyio/badge/?version=latest + :target: https://anyio.readthedocs.io/en/latest/?badge=latest + :alt: Documentation +.. image:: https://badges.gitter.im/gitterHQ/gitter.svg + :target: https://gitter.im/python-trio/AnyIO + :alt: Gitter chat +.. image:: https://tidelift.com/badges/package/pypi/anyio + :target: https://tidelift.com/subscription/pkg/pypi-anyio + :alt: Tidelift + +AnyIO is an asynchronous networking and concurrency library that works on top of either asyncio_ or +Trio_. It implements Trio-like `structured concurrency`_ (SC) on top of asyncio and works in harmony +with the native SC of Trio itself. + +Applications and libraries written against AnyIO's API will run unmodified on either asyncio_ or +Trio_. AnyIO can also be adopted into a library or application incrementally – bit by bit, no full +refactoring necessary. It will blend in with the native libraries of your chosen backend. + +To find out why you might want to use AnyIO's APIs instead of asyncio's, you can read about it +`here `_. + +Documentation +------------- + +View full documentation at: https://anyio.readthedocs.io/ + +Features +-------- + +AnyIO offers the following functionality: + +* Task groups (nurseries_ in trio terminology) +* High-level networking (TCP, UDP and UNIX sockets) + + * `Happy eyeballs`_ algorithm for TCP connections (more robust than that of asyncio on Python + 3.8) + * async/await style UDP sockets (unlike asyncio where you still have to use Transports and + Protocols) + +* A versatile API for byte streams and object streams +* Inter-task synchronization and communication (locks, conditions, events, semaphores, object + streams and futures) +* Worker threads +* Subprocesses +* Subinterpreter support for code parallelization (on Python 3.13 and later) +* Asynchronous file I/O (using worker threads) +* Signal handling +* Asynchronous versions of the functools_ and itertools_ modules + +AnyIO also comes with its own pytest_ plugin which also supports asynchronous fixtures. +It even works with the popular Hypothesis_ library. + +.. _asyncio: https://docs.python.org/3/library/asyncio.html +.. _Trio: https://github.com/python-trio/trio +.. _structured concurrency: https://en.wikipedia.org/wiki/Structured_concurrency +.. _nurseries: https://trio.readthedocs.io/en/stable/reference-core.html#nurseries-and-spawning +.. _Happy eyeballs: https://en.wikipedia.org/wiki/Happy_Eyeballs +.. _pytest: https://docs.pytest.org/en/latest/ +.. _functools: https://docs.python.org/3/library/functools.html +.. _itertools: https://docs.python.org/3/library/itertools.html +.. _Hypothesis: https://hypothesis.works/ + +Security contact information +---------------------------- + +To report a security vulnerability, please use the `Tidelift security contact`_. +Tidelift will coordinate the fix and disclosure. + +.. _Tidelift security contact: https://tidelift.com/security diff --git a/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/RECORD new file mode 100644 index 0000000..3946aab --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/RECORD @@ -0,0 +1,100 @@ +anyio-4.15.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +anyio-4.15.1.dist-info/METADATA,sha256=BDrstXGvcYnopsUo9djjJUXxss36r3YdUebVtn6jyoE,4733 +anyio-4.15.1.dist-info/RECORD,, +anyio-4.15.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91 +anyio-4.15.1.dist-info/entry_points.txt,sha256=_d6Yu6uiaZmNe0CydowirE9Cmg7zUL2g08tQpoS3Qvc,39 +anyio-4.15.1.dist-info/licenses/LICENSE,sha256=U2GsncWPLvX9LpsJxoKXwX8ElQkJu8gCO9uC6s8iwrA,1081 +anyio-4.15.1.dist-info/top_level.txt,sha256=QglSMiWX8_5dpoVAEIHdEYzvqFMdSYWmCj6tYw2ITkQ,6 +anyio/__init__.py,sha256=OfroYRjIUw_JFj0Zwh1JAaajMQg4VqtoAt0XoOHDRwE,7602 +anyio/__pycache__/__init__.cpython-312.pyc,, +anyio/__pycache__/_lazyimport.cpython-312.pyc,, +anyio/__pycache__/from_thread.cpython-312.pyc,, +anyio/__pycache__/functools.cpython-312.pyc,, +anyio/__pycache__/itertools.cpython-312.pyc,, +anyio/__pycache__/lowlevel.cpython-312.pyc,, +anyio/__pycache__/pytest_plugin.cpython-312.pyc,, +anyio/__pycache__/to_interpreter.cpython-312.pyc,, +anyio/__pycache__/to_process.cpython-312.pyc,, +anyio/__pycache__/to_thread.cpython-312.pyc,, +anyio/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/_backends/__pycache__/__init__.cpython-312.pyc,, +anyio/_backends/__pycache__/_asyncio.cpython-312.pyc,, +anyio/_backends/__pycache__/_trio.cpython-312.pyc,, +anyio/_backends/_asyncio.py,sha256=Vqy7O1ymmEpxTiPzehSuBCVTziGALNV-BbsIRaKXsg4,107767 +anyio/_backends/_trio.py,sha256=Gs8XzX0IeBvje8IEZtJmpNylKCUAChKv7I_YZi3wlmg,45715 +anyio/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/_core/__pycache__/__init__.cpython-312.pyc,, +anyio/_core/__pycache__/_asyncio_selector_thread.cpython-312.pyc,, +anyio/_core/__pycache__/_concurrency_utils.cpython-312.pyc,, +anyio/_core/__pycache__/_contextmanagers.cpython-312.pyc,, +anyio/_core/__pycache__/_eventloop.cpython-312.pyc,, +anyio/_core/__pycache__/_exceptions.cpython-312.pyc,, +anyio/_core/__pycache__/_fileio.cpython-312.pyc,, +anyio/_core/__pycache__/_futures.cpython-312.pyc,, +anyio/_core/__pycache__/_resources.cpython-312.pyc,, +anyio/_core/__pycache__/_signals.cpython-312.pyc,, +anyio/_core/__pycache__/_sockets.cpython-312.pyc,, +anyio/_core/__pycache__/_streams.cpython-312.pyc,, +anyio/_core/__pycache__/_subprocesses.cpython-312.pyc,, +anyio/_core/__pycache__/_synchronization.cpython-312.pyc,, +anyio/_core/__pycache__/_tasks.cpython-312.pyc,, +anyio/_core/__pycache__/_tempfile.cpython-312.pyc,, +anyio/_core/__pycache__/_testing.cpython-312.pyc,, +anyio/_core/__pycache__/_typedattr.cpython-312.pyc,, +anyio/_core/_asyncio_selector_thread.py,sha256=2PdxFM3cs02Kp6BSppbvmRT7q7asreTW5FgBxEsflBo,5626 +anyio/_core/_concurrency_utils.py,sha256=kvRXMrnDqgQTBkZc-f279-lBnZM4YbSToXqhnAUCfSc,4522 +anyio/_core/_contextmanagers.py,sha256=YInBCabiEeS-UaP_Jdxa1CaFC71ETPW8HZTHIM8Rsc8,7215 +anyio/_core/_eventloop.py,sha256=ByZUeJD9alMfcyTseRo5IzTO0IltEul_Gyq9iqSjqDk,6658 +anyio/_core/_exceptions.py,sha256=B4ZhLkBQT4tI6vShv4-GJui492IJsoIDTw1FQJEkmAI,5618 +anyio/_core/_fileio.py,sha256=q--ft9h5Y_FRLzhAnoYrXf3tOuZ8ZyIN2A9mCfMbZRc,34163 +anyio/_core/_futures.py,sha256=YQq5uhnCnKQMQD62tOLfzEm7dUXY3GQorJdH6ghfgIQ,6177 +anyio/_core/_resources.py,sha256=Ld5duCe9NwgPfI7EIOh3CMIBYQ7rq6ZQEa7CFS0u9Ec,450 +anyio/_core/_signals.py,sha256=mjTBB2hTKNPRlU0IhnijeQedpWOGERDiMjSlJQsFrug,1016 +anyio/_core/_sockets.py,sha256=C8b-3w5H9n0Fo4g7X8WJvk0m6CGh6NVAAFtieBGHK3g,35289 +anyio/_core/_streams.py,sha256=FczFwIgDpnkK0bODWJXMpsUJYdvAD04kaUaGzJU8DK0,1806 +anyio/_core/_subprocesses.py,sha256=TMBuGNILz-47w5t9vayJ1GpCpDT6LxsMohJDFCewIRc,7938 +anyio/_core/_synchronization.py,sha256=ggsd74QPd846faA2HQeT07ONQ5HQ1S78gy8mvB3qCrw,21658 +anyio/_core/_tasks.py,sha256=J2dhM_Jq3Grsfg_enNWpRz2SxfM15uLt-pEfvoQq3uA,15391 +anyio/_core/_tempfile.py,sha256=IgoBCFaPhrFvsF9KHcCcrnCRn-UqMfw0WRdSDWgxAiI,19726 +anyio/_core/_testing.py,sha256=ZRucLh5kqhiFWAJMMbd8yZ6uIvz0rq3WwL72ldosxAU,2340 +anyio/_core/_typedattr.py,sha256=gt9Dzn-IL0QW14aHPDD8PAtq95FYKNNupHyf37xZGPQ,2607 +anyio/_lazyimport.py,sha256=LCjnYanIfYE9G-Dv_EsqNLDuVRppNicWZjCTfS9-sJU,6082 +anyio/abc/__init__.py,sha256=XIVvCI2c6oyaOcqXe94k2rzU5_s7-ZNnlxGTk34H4T0,3050 +anyio/abc/__pycache__/__init__.cpython-312.pyc,, +anyio/abc/__pycache__/_eventloop.cpython-312.pyc,, +anyio/abc/__pycache__/_resources.cpython-312.pyc,, +anyio/abc/__pycache__/_sockets.cpython-312.pyc,, +anyio/abc/__pycache__/_streams.cpython-312.pyc,, +anyio/abc/__pycache__/_subprocesses.cpython-312.pyc,, +anyio/abc/__pycache__/_tasks.cpython-312.pyc,, +anyio/abc/__pycache__/_testing.cpython-312.pyc,, +anyio/abc/_eventloop.py,sha256=0-UwgB351rkocCt8m3fjTmcNx2Fxn52nUoujG5DzMvU,11127 +anyio/abc/_resources.py,sha256=hrb0LXUgHSp3i8G6W8JJchdzKXgg1m0T-L7udXmv2y0,900 +anyio/abc/_sockets.py,sha256=jQWnPX7iDIDyvyvVkbuFhjezw4oI2WggzbW6Vl2-eqs,13162 +anyio/abc/_streams.py,sha256=IMeOPPutFXRbyeAutxV1HJe7FUQZYp2qKrJJpP1DCv0,7620 +anyio/abc/_subprocesses.py,sha256=CDqU1p0HuBS0pXUzBW_hbfhihTJRM67n__7qZmoPU84,2123 +anyio/abc/_tasks.py,sha256=HBPpT72aOy0bwMAxBCKmnXsR328wCYRjYm3iaVw2fQo,7070 +anyio/abc/_testing.py,sha256=A9Y8RAClIPn1A0aNHBN8YoaRy_MKOpbFGEbTHAXgB6U,2145 +anyio/from_thread.py,sha256=iZZbfcNySi4wAcx1_cxAQE4J0YZC88qRMIYqQ6SF9sc,19415 +anyio/functools.py,sha256=T4JS8IXq-x1S0Lbo2owF8l9fza2KypO147QLeyz4cjs,11797 +anyio/itertools.py,sha256=TS7_UCa2Y3cggtaLlWHbjOyf6QhPfQUdPduNW-ddXTE,16255 +anyio/lowlevel.py,sha256=QyWkvfetbCH0mHkqv1yxGwOaiZ7fyxhpZkHWOXPV5JU,6280 +anyio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/pytest_plugin.py,sha256=oLXyOstvRZW3VVbqIODhZsPt4jSz2UGh44YeG2nJXx0,14516 +anyio/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/streams/__pycache__/__init__.cpython-312.pyc,, +anyio/streams/__pycache__/buffered.cpython-312.pyc,, +anyio/streams/__pycache__/file.cpython-312.pyc,, +anyio/streams/__pycache__/memory.cpython-312.pyc,, +anyio/streams/__pycache__/stapled.cpython-312.pyc,, +anyio/streams/__pycache__/text.cpython-312.pyc,, +anyio/streams/__pycache__/tls.cpython-312.pyc,, +anyio/streams/buffered.py,sha256=u7hCD8SNrYHcutG6K5wEiy1F88-pizgjvEFM22Kq2Cw,6746 +anyio/streams/file.py,sha256=6jujI2m-QJITqqKFamrupX_DNsU7y2Fz3omLZxOLuY0,4524 +anyio/streams/memory.py,sha256=rpxNzDMag9PCOynBqN64KyuAfAPB41vFyRP-Npo-lE0,10793 +anyio/streams/stapled.py,sha256=UJ2Rgt0NUmM1VovRNOoZoic960lcbDjb3vhfZg3b_Z0,4845 +anyio/streams/text.py,sha256=BcVAGJw1VRvtIqnv-o0Rb0pwH7p8vwlvl21xHq522ag,5765 +anyio/streams/tls.py,sha256=fqLmxvULFO9rvxGyJ33VPrOIN7E3eiU1q90BkmKigQ8,15818 +anyio/to_interpreter.py,sha256=ATnoT6kei9wDmQZtXTA3SsY4CK7XKr8Ta4_x5i6pbTw,7100 +anyio/to_process.py,sha256=p6n8j0nreJUl1DEkR_9PL6KRoKtNqFxORTn8qZBvn94,9850 +anyio/to_thread.py,sha256=8MPVrrQq8ttGvV21cSd68rMto5bhxJ0-i9CupXag5Gs,2750 diff --git a/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/WHEEL b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/WHEEL new file mode 100644 index 0000000..13013d7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (84.0.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/entry_points.txt new file mode 100644 index 0000000..44dd9bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[pytest11] +anyio = anyio.pytest_plugin diff --git a/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/licenses/LICENSE new file mode 100644 index 0000000..104eebf --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/licenses/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2018 Alex Grönholm + +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. diff --git a/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/top_level.txt new file mode 100644 index 0000000..c77c069 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio-4.15.1.dist-info/top_level.txt @@ -0,0 +1 @@ +anyio diff --git a/venv/lib/python3.12/site-packages/anyio/__init__.py b/venv/lib/python3.12/site-packages/anyio/__init__.py new file mode 100644 index 0000000..30a3a16 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/__init__.py @@ -0,0 +1,154 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from ._lazyimport import ( + fix_package_names, + install_lazy_importer, + set_deprecated_aliases, +) + +if TYPE_CHECKING or not install_lazy_importer(): + from ._core._concurrency_utils import amap as amap + from ._core._concurrency_utils import as_completed as as_completed + from ._core._concurrency_utils import gather as gather + from ._core._contextmanagers import ( + AsyncContextManagerMixin as AsyncContextManagerMixin, + ) + from ._core._contextmanagers import ContextManagerMixin as ContextManagerMixin + from ._core._eventloop import current_time as current_time + from ._core._eventloop import get_all_backends as get_all_backends + from ._core._eventloop import get_available_backends as get_available_backends + from ._core._eventloop import get_cancelled_exc_class as get_cancelled_exc_class + from ._core._eventloop import run as run + from ._core._eventloop import sleep as sleep + from ._core._eventloop import sleep_forever as sleep_forever + from ._core._eventloop import sleep_until as sleep_until + from ._core._exceptions import BrokenResourceError as BrokenResourceError + from ._core._exceptions import BrokenWorkerInterpreter as BrokenWorkerInterpreter + from ._core._exceptions import BrokenWorkerProcess as BrokenWorkerProcess + from ._core._exceptions import BusyResourceError as BusyResourceError + from ._core._exceptions import ClosedResourceError as ClosedResourceError + from ._core._exceptions import ConnectionFailed as ConnectionFailed + from ._core._exceptions import DelimiterNotFound as DelimiterNotFound + from ._core._exceptions import EndOfStream as EndOfStream + from ._core._exceptions import FutureAlreadyFinished as FutureAlreadyFinished + from ._core._exceptions import FutureCancelled as FutureCancelled + from ._core._exceptions import FutureFailed as FutureFailed + from ._core._exceptions import FutureNotFinished as FutureNotFinished + from ._core._exceptions import IncompleteRead as IncompleteRead + from ._core._exceptions import NoEventLoopError as NoEventLoopError + from ._core._exceptions import RunFinishedError as RunFinishedError + from ._core._exceptions import TaskCancelled as TaskCancelled + from ._core._exceptions import TaskFailed as TaskFailed + from ._core._exceptions import TaskNotFinished as TaskNotFinished + from ._core._exceptions import ( + TypedAttributeLookupError as TypedAttributeLookupError, + ) + from ._core._exceptions import WouldBlock as WouldBlock + from ._core._fileio import AsyncFile as AsyncFile + from ._core._fileio import Path as Path + from ._core._fileio import open_file as open_file + from ._core._fileio import wrap_file as wrap_file + from ._core._futures import Future as Future + from ._core._resources import aclose_forcefully as aclose_forcefully + from ._core._signals import open_signal_receiver as open_signal_receiver + from ._core._sockets import TCPConnectable as TCPConnectable + from ._core._sockets import UNIXConnectable as UNIXConnectable + from ._core._sockets import as_connectable as as_connectable + from ._core._sockets import connect_tcp as connect_tcp + from ._core._sockets import connect_unix as connect_unix + from ._core._sockets import ( + create_connected_udp_socket as create_connected_udp_socket, + ) + from ._core._sockets import ( + create_connected_unix_datagram_socket as create_connected_unix_datagram_socket, + ) + from ._core._sockets import create_tcp_listener as create_tcp_listener + from ._core._sockets import create_udp_socket as create_udp_socket + from ._core._sockets import ( + create_unix_datagram_socket as create_unix_datagram_socket, + ) + from ._core._sockets import create_unix_listener as create_unix_listener + from ._core._sockets import getaddrinfo as getaddrinfo + from ._core._sockets import getnameinfo as getnameinfo + from ._core._sockets import notify_closing as notify_closing + from ._core._sockets import wait_readable as wait_readable + from ._core._sockets import wait_socket_readable as wait_socket_readable + from ._core._sockets import wait_socket_writable as wait_socket_writable + from ._core._sockets import wait_writable as wait_writable + from ._core._streams import ( + create_memory_object_stream as create_memory_object_stream, + ) + from ._core._subprocesses import open_process as open_process + from ._core._subprocesses import run_process as run_process + from ._core._synchronization import CapacityLimiter as CapacityLimiter + from ._core._synchronization import ( + CapacityLimiterStatistics as CapacityLimiterStatistics, + ) + from ._core._synchronization import Condition as Condition + from ._core._synchronization import ConditionStatistics as ConditionStatistics + from ._core._synchronization import Event as Event + from ._core._synchronization import EventStatistics as EventStatistics + from ._core._synchronization import Lock as Lock + from ._core._synchronization import LockStatistics as LockStatistics + from ._core._synchronization import ResourceGuard as ResourceGuard + from ._core._synchronization import Semaphore as Semaphore + from ._core._synchronization import SemaphoreStatistics as SemaphoreStatistics + from ._core._tasks import TASK_STATUS_IGNORED as TASK_STATUS_IGNORED + from ._core._tasks import CancelScope as CancelScope + from ._core._tasks import TaskHandle as TaskHandle + from ._core._tasks import create_task_group as create_task_group + from ._core._tasks import current_effective_deadline as current_effective_deadline + from ._core._tasks import fail_after as fail_after + from ._core._tasks import fail_at as fail_at + from ._core._tasks import move_on_after as move_on_after + from ._core._tasks import move_on_at as move_on_at + from ._core._tempfile import NamedTemporaryFile as NamedTemporaryFile + from ._core._tempfile import SpooledTemporaryFile as SpooledTemporaryFile + from ._core._tempfile import TemporaryDirectory as TemporaryDirectory + from ._core._tempfile import TemporaryFile as TemporaryFile + from ._core._tempfile import gettempdir as gettempdir + from ._core._tempfile import gettempdirb as gettempdirb + from ._core._tempfile import mkdtemp as mkdtemp + from ._core._tempfile import mkstemp as mkstemp + from ._core._testing import TaskInfo as TaskInfo + from ._core._testing import get_current_task as get_current_task + from ._core._testing import get_running_tasks as get_running_tasks + from ._core._testing import wait_all_tasks_blocked as wait_all_tasks_blocked + from ._core._typedattr import TypedAttributeProvider as TypedAttributeProvider + from ._core._typedattr import TypedAttributeSet as TypedAttributeSet + from ._core._typedattr import typed_attribute as typed_attribute + + # ruff: isort: off + from . import ( + abc as abc, + ) + from . import ( + from_thread as from_thread, + ) + from . import ( + functools as functools, + ) + from . import ( + itertools as itertools, + ) + from . import ( + lowlevel as lowlevel, + ) + from . import ( + to_interpreter as to_interpreter, + ) + from . import ( + to_process as to_process, + ) + from . import ( + to_thread as to_thread, + ) + + fix_package_names() + set_deprecated_aliases( + { + "BrokenWorkerIntepreter": "anyio.BrokenWorkerInterpreter", + } + ) diff --git a/venv/lib/python3.12/site-packages/anyio/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ac7f029 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/__pycache__/_lazyimport.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/__pycache__/_lazyimport.cpython-312.pyc new file mode 100644 index 0000000..ba68d91 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/__pycache__/_lazyimport.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/__pycache__/from_thread.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/__pycache__/from_thread.cpython-312.pyc new file mode 100644 index 0000000..87482ce Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/__pycache__/from_thread.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/__pycache__/functools.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/__pycache__/functools.cpython-312.pyc new file mode 100644 index 0000000..fff9890 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/__pycache__/functools.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/__pycache__/itertools.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/__pycache__/itertools.cpython-312.pyc new file mode 100644 index 0000000..10e40ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/__pycache__/itertools.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/__pycache__/lowlevel.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/__pycache__/lowlevel.cpython-312.pyc new file mode 100644 index 0000000..0fb52f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/__pycache__/lowlevel.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/__pycache__/pytest_plugin.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/__pycache__/pytest_plugin.cpython-312.pyc new file mode 100644 index 0000000..42005ef Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/__pycache__/pytest_plugin.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/__pycache__/to_interpreter.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/__pycache__/to_interpreter.cpython-312.pyc new file mode 100644 index 0000000..d1c5b0e Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/__pycache__/to_interpreter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/__pycache__/to_process.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/__pycache__/to_process.cpython-312.pyc new file mode 100644 index 0000000..7dc8b3b Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/__pycache__/to_process.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/__pycache__/to_thread.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/__pycache__/to_thread.cpython-312.pyc new file mode 100644 index 0000000..54b9a8d Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/__pycache__/to_thread.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_backends/__init__.py b/venv/lib/python3.12/site-packages/anyio/_backends/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/anyio/_backends/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_backends/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..03726fa Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_backends/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_backends/__pycache__/_asyncio.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_backends/__pycache__/_asyncio.cpython-312.pyc new file mode 100644 index 0000000..5b27e9d Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_backends/__pycache__/_asyncio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_backends/__pycache__/_trio.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_backends/__pycache__/_trio.cpython-312.pyc new file mode 100644 index 0000000..8eb86b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_backends/__pycache__/_trio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py b/venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py new file mode 100644 index 0000000..7eba257 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py @@ -0,0 +1,3201 @@ +from __future__ import annotations + +import array +import asyncio +import concurrent.futures +import math +import os +import socket +import sys +import threading +import weakref +from asyncio import ( + AbstractEventLoop, + CancelledError, + all_tasks, + create_task, + current_task, + get_running_loop, + sleep, +) +from asyncio.base_events import _run_until_complete_cb # type: ignore[attr-defined] +from collections import OrderedDict, deque +from collections.abc import ( + AsyncGenerator, + AsyncIterator, + Awaitable, + Callable, + Collection, + Coroutine, + Iterable, + Sequence, +) +from concurrent.futures import Future +from contextlib import AbstractContextManager, suppress +from contextvars import Context, copy_context +from dataclasses import dataclass, field +from functools import partial, wraps +from inspect import ( + CORO_RUNNING, + CORO_SUSPENDED, + getcoroutinestate, +) +from io import IOBase +from os import PathLike +from queue import Queue +from signal import Signals +from socket import AddressFamily, SocketKind +from threading import Thread +from types import CodeType, TracebackType +from typing import ( + IO, + TYPE_CHECKING, + Any, + Literal, + ParamSpec, + TypeVar, + cast, +) +from weakref import WeakKeyDictionary + +from .. import ( + CapacityLimiterStatistics, + EventStatistics, + LockStatistics, + TaskInfo, + abc, +) +from .._core._eventloop import ( + claim_worker_thread, + set_current_async_library, + threadlocals, +) +from .._core._exceptions import ( + BrokenResourceError, + BusyResourceError, + ClosedResourceError, + EndOfStream, + RunFinishedError, + WouldBlock, +) +from .._core._sockets import convert_ipv6_sockaddr +from .._core._streams import create_memory_object_stream +from .._core._synchronization import ( + CapacityLimiter as BaseCapacityLimiter, +) +from .._core._synchronization import Event as BaseEvent +from .._core._synchronization import Lock as BaseLock +from .._core._synchronization import ( + ResourceGuard, + SemaphoreStatistics, +) +from .._core._synchronization import Semaphore as BaseSemaphore +from .._core._tasks import CancelScope as BaseCancelScope +from .._core._tasks import TaskHandle +from ..abc import ( + AsyncBackend, + IPSockAddrType, + SocketListener, + UDPPacketType, + UNIXDatagramPacketType, +) +from ..abc._tasks import call_for_coroutine, get_callable_name, get_coro_name +from ..lowlevel import RunVar, _run_vars + +if TYPE_CHECKING: + from _typeshed import FileDescriptorLike + + from ..abc._eventloop import StrOrBytesPath + from ..streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream +else: + FileDescriptorLike = object + +if sys.version_info >= (3, 11): + from asyncio import Runner + from typing import Self, TypeVarTuple, Unpack +else: + import contextvars + import enum + import signal + from asyncio import coroutines, events, exceptions, tasks + + from exceptiongroup import BaseExceptionGroup + from typing_extensions import Self, TypeVarTuple, Unpack + + class _State(enum.Enum): + CREATED = "created" + INITIALIZED = "initialized" + CLOSED = "closed" + + class Runner: + # Copied from CPython 3.11 + def __init__( + self, + *, + debug: bool | None = None, + loop_factory: Callable[[], AbstractEventLoop] | None = None, + ): + self._state = _State.CREATED + self._debug = debug + self._loop_factory = loop_factory + self._loop: AbstractEventLoop | None = None + self._context = None + self._interrupt_count = 0 + self._set_event_loop = False + + def __enter__(self) -> Self: + self._lazy_init() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """Shutdown and close event loop.""" + loop = self._loop + if self._state is not _State.INITIALIZED or loop is None: + return + try: + _cancel_all_tasks(loop) + loop.run_until_complete(loop.shutdown_asyncgens()) + if hasattr(loop, "shutdown_default_executor"): + loop.run_until_complete(loop.shutdown_default_executor()) + else: + loop.run_until_complete(_shutdown_default_executor(loop)) + finally: + if self._set_event_loop: + events.set_event_loop(None) + loop.close() + self._loop = None + self._state = _State.CLOSED + + def get_loop(self) -> AbstractEventLoop: + """Return embedded event loop.""" + self._lazy_init() + return self._loop + + def run(self, coro: Coroutine[T_Retval], *, context=None) -> T_Retval: + """Run a coroutine inside the embedded event loop.""" + if not coroutines.iscoroutine(coro): + raise ValueError(f"a coroutine was expected, got {coro!r}") + + if events._get_running_loop() is not None: + # fail fast with short traceback + raise RuntimeError( + "Runner.run() cannot be called from a running event loop" + ) + + self._lazy_init() + + if context is None: + context = self._context + task = context.run(self._loop.create_task, coro) + + if ( + threading.current_thread() is threading.main_thread() + and signal.getsignal(signal.SIGINT) is signal.default_int_handler + ): + sigint_handler = partial(self._on_sigint, main_task=task) + try: + signal.signal(signal.SIGINT, sigint_handler) + except ValueError: + # `signal.signal` may throw if `threading.main_thread` does + # not support signals (e.g. embedded interpreter with signals + # not registered - see gh-91880) + sigint_handler = None + else: + sigint_handler = None + + self._interrupt_count = 0 + try: + return self._loop.run_until_complete(task) + except exceptions.CancelledError: + if self._interrupt_count > 0: + uncancel = getattr(task, "uncancel", None) + if uncancel is not None and uncancel() == 0: + raise KeyboardInterrupt # noqa: B904 + raise # CancelledError + finally: + if ( + sigint_handler is not None + and signal.getsignal(signal.SIGINT) is sigint_handler + ): + signal.signal(signal.SIGINT, signal.default_int_handler) + + def _lazy_init(self) -> None: + if self._state is _State.CLOSED: + raise RuntimeError("Runner is closed") + if self._state is _State.INITIALIZED: + return + if self._loop_factory is None: + self._loop = events.new_event_loop() + if not self._set_event_loop: + # Call set_event_loop only once to avoid calling + # attach_loop multiple times on child watchers + events.set_event_loop(self._loop) + self._set_event_loop = True + else: + self._loop = self._loop_factory() + if self._debug is not None: + self._loop.set_debug(self._debug) + self._context = contextvars.copy_context() + self._state = _State.INITIALIZED + + def _on_sigint(self, signum, frame, main_task: asyncio.Task) -> None: + self._interrupt_count += 1 + if self._interrupt_count == 1 and not main_task.done(): + main_task.cancel() + # wakeup loop if it is blocked by select() with long timeout + self._loop.call_soon_threadsafe(lambda: None) + return + raise KeyboardInterrupt() + + def _cancel_all_tasks(loop: AbstractEventLoop) -> None: + to_cancel = tasks.all_tasks(loop) + if not to_cancel: + return + + for task in to_cancel: + task.cancel() + + loop.run_until_complete(tasks.gather(*to_cancel, return_exceptions=True)) + + for task in to_cancel: + if task.cancelled(): + continue + if task.exception() is not None: + loop.call_exception_handler( + { + "message": "unhandled exception during asyncio.run() shutdown", + "exception": task.exception(), + "task": task, + } + ) + + async def _shutdown_default_executor(loop: AbstractEventLoop) -> None: + """Schedule the shutdown of the default executor.""" + + def _do_shutdown(future: asyncio.futures.Future) -> None: + try: + loop._default_executor.shutdown(wait=True) # type: ignore[attr-defined] + loop.call_soon_threadsafe(future.set_result, None) + except Exception as ex: + loop.call_soon_threadsafe(future.set_exception, ex) + + loop._executor_shutdown_called = True + if loop._default_executor is None: + return + future = loop.create_future() + thread = threading.Thread(target=_do_shutdown, args=(future,)) + thread.start() + try: + await future + finally: + thread.join() + + +T_Retval = TypeVar("T_Retval") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) +PosArgsT = TypeVarTuple("PosArgsT") +P = ParamSpec("P") + +_root_task: RunVar[asyncio.Task[Any] | None] = RunVar("_root_task") + + +def find_root_task() -> asyncio.Task: + root_task = _root_task.get(None) + if root_task is not None and not root_task.done(): + return root_task + + # Look for a task that has been started via run_until_complete() + for task in all_tasks(): + if task._callbacks and not task.done(): + for cb, context in task._callbacks: + if ( + cb is _run_until_complete_cb + or getattr(cb, "__module__", None) == "uvloop.loop" + ): + _root_task.set(task) + + def _unset(t: asyncio.Task[Any]) -> None: + if vars := _run_vars.get(t.get_loop()): + vars.pop(_root_task, None) + + # Register a callback to break the task -> loop -> _run_var[loop][_root_task] -> task cycle + # Also run it in its own context to not create another reference. + # We can't use RunVar.reset() here since these are called synchronously + # and thus lowlevel.current_token() (which RunVar.reset() depends on) fails. + task.add_done_callback(_unset, context=context) + return task + + # Look up the topmost task in the AnyIO task tree, if possible + task = cast(asyncio.Task, current_task()) + state = _task_states.get(task) + if state: + cancel_scope = state.cancel_scope + while cancel_scope and cancel_scope._parent_scope is not None: + cancel_scope = cancel_scope._parent_scope + + if cancel_scope is not None: + return cast(asyncio.Task, cancel_scope._host_task) + + return task + + +def _task_started(task: asyncio.Task) -> bool: + """Return ``True`` if the task has been started and has not finished.""" + # The task coro should never be None here, as we never add finished tasks to the + # task list + coro = task.get_coro() + assert coro is not None + return getcoroutinestate(coro) in (CORO_RUNNING, CORO_SUSPENDED) + + +# +# Timeouts and cancellation +# + + +def is_anyio_cancellation(exc: CancelledError) -> bool: + # Sometimes third party frameworks catch a CancelledError and raise a new one, so as + # a workaround we have to look at the previous ones in __context__ too for a + # matching cancel message + while True: + if ( + exc.args + and isinstance(exc.args[0], str) + and exc.args[0].startswith("Cancelled via cancel scope ") + ): + return True + + if isinstance(exc.__context__, CancelledError): + exc = exc.__context__ + continue + + return False + + +class CancelScope(BaseCancelScope): + __slots__ = ( + "_active", + "_cancel_called", + "_cancel_handle", + "_cancel_reason", + "_cancelled_caught", + "_child_scopes", + "_deadline", + "_host_task", + "_parent_scope", + "_pending_uncancellations", + "_shield", + "_tasks", + "_timeout_handle", + ) + + def __new__(cls, *, deadline: float = math.inf, shield: bool = False) -> Self: + return object.__new__(cls) + + def __init__(self, deadline: float = math.inf, shield: bool = False): + self._deadline = deadline + self._shield = shield + self._parent_scope: CancelScope | None = None + self._child_scopes: set[CancelScope] = set() + self._cancel_called = False + self._cancel_reason: str | None = None + self._cancelled_caught = False + self._active = False + self._timeout_handle: asyncio.TimerHandle | None = None + self._cancel_handle: asyncio.Handle | None = None + self._tasks: set[asyncio.Task] = set() + self._host_task: asyncio.Task | None = None + if sys.version_info >= (3, 11): + self._pending_uncancellations: int | None = 0 + else: + self._pending_uncancellations = None + + def __enter__(self) -> Self: + if self._active: + raise RuntimeError( + "Each CancelScope may only be used for a single 'with' block" + ) + + self._host_task = host_task = cast(asyncio.Task, current_task()) + self._tasks.add(host_task) + try: + task_state = _task_states[host_task] + except KeyError: + task_state = TaskState(None, self) + _task_states[host_task] = task_state + else: + self._parent_scope = task_state.cancel_scope + task_state.cancel_scope = self + if self._parent_scope is not None: + # If using an eager task factory, the parent scope may not even contain + # the host task + self._parent_scope._child_scopes.add(self) + self._parent_scope._tasks.discard(host_task) + + self._timeout() + self._active = True + + # Start cancelling the host task if the scope was cancelled before entering + if self._cancel_called: + self._deliver_cancellation(self) + + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + del exc_tb + + if not self._active: + raise RuntimeError("This cancel scope is not active") + if current_task() is not self._host_task: + raise RuntimeError( + "Attempted to exit cancel scope in a different task than it was " + "entered in" + ) + + assert self._host_task is not None + host_task_state = _task_states.get(self._host_task) + if host_task_state is None or host_task_state.cancel_scope is not self: + raise RuntimeError( + "Attempted to exit a cancel scope that isn't the current tasks's " + "current cancel scope" + ) + + try: + self._active = False + if self._timeout_handle: + self._timeout_handle.cancel() + self._timeout_handle = None + + self._tasks.remove(self._host_task) + if self._parent_scope is not None: + self._parent_scope._child_scopes.remove(self) + self._parent_scope._tasks.add(self._host_task) + + host_task_state.cancel_scope = self._parent_scope + + # Restart the cancellation effort in the closest visible, cancelled parent + # scope if necessary + self._restart_cancellation_in_parent() + + # We only swallow the exception iff it was an AnyIO CancelledError, either + # directly as exc_val or inside an exception group and there are no cancelled + # parent cancel scopes visible to us here + if self._cancel_called and not self._parent_cancellation_is_visible_to_us: + # For each level-cancel() call made on the host task, call uncancel() + while self._pending_uncancellations: + self._host_task.uncancel() + self._pending_uncancellations -= 1 + + # Update cancelled_caught and check for exceptions we must not swallow + if isinstance(exc_val, BaseExceptionGroup): + cancelleds_caught, remaining = exc_val.split( + lambda exc: ( + isinstance(exc, CancelledError) + and is_anyio_cancellation(exc) + ) + ) + + if cancelleds_caught is None: + return False + + self._cancelled_caught = True + + if remaining is None: + return True + + context = remaining.__context__ + try: + # Preserve __cause__ and __suppress_context__ by avoiding `raise + # ... from ...` + raise remaining + finally: + # Preserve __context__ + remaining.__context__ = context + del context + else: + if isinstance(exc_val, CancelledError) and is_anyio_cancellation( + exc_val + ): + self._cancelled_caught = True + return True + else: + return False + else: + if self._pending_uncancellations: + assert self._parent_scope is not None + assert self._parent_scope._pending_uncancellations is not None + self._parent_scope._pending_uncancellations += ( + self._pending_uncancellations + ) + self._pending_uncancellations = 0 + + return False + finally: + self._host_task = None + del exc_val + + @property + def _effectively_cancelled(self) -> bool: + cancel_scope: CancelScope | None = self + while cancel_scope is not None: + if cancel_scope._cancel_called: + return True + + if cancel_scope.shield: + return False + + cancel_scope = cancel_scope._parent_scope + + return False + + @property + def _parent_cancellation_is_visible_to_us(self) -> bool: + return ( + self._parent_scope is not None + and not self.shield + and self._parent_scope._effectively_cancelled + ) + + def _timeout(self) -> None: + if self._deadline != math.inf: + loop = get_running_loop() + if loop.time() >= self._deadline: + self.cancel("deadline exceeded") + else: + self._timeout_handle = loop.call_at(self._deadline, self._timeout) + + def _deliver_cancellation(self, origin: CancelScope) -> bool: + """ + Deliver cancellation to directly contained tasks and nested cancel scopes. + + Schedule another run at the end if we still have tasks eligible for + cancellation. + + :param origin: the cancel scope that originated the cancellation + :return: ``True`` if the delivery needs to be retried on the next cycle + + """ + should_retry = False + current = current_task() + for task in self._tasks: + # Always skip tasks that are already done (see issue #1111) + if task.done(): + continue + + should_retry = True + if task._must_cancel: # type: ignore[attr-defined] + continue + + # The task is eligible for cancellation if it has started + if task is not current and (task is self._host_task or _task_started(task)): + waiter = task._fut_waiter # type: ignore[attr-defined] + if not isinstance(waiter, asyncio.Future) or not waiter.done(): + task.cancel(origin._cancel_reason) + if ( + task is origin._host_task + and origin._pending_uncancellations is not None + ): + origin._pending_uncancellations += 1 + + # Deliver cancellation to child scopes that aren't shielded or running their own + # cancellation callbacks + for scope in self._child_scopes: + if not scope._shield and not scope.cancel_called: + should_retry = scope._deliver_cancellation(origin) or should_retry + + # Schedule another callback if there are still tasks left + if origin is self: + if should_retry: + self._cancel_handle = get_running_loop().call_soon( + self._deliver_cancellation, origin + ) + else: + self._cancel_handle = None + + return should_retry + + def _restart_cancellation_in_parent(self) -> None: + """ + Restart the cancellation effort in the closest directly cancelled parent scope. + + """ + scope = self._parent_scope + while scope is not None: + if scope._cancel_called: + if scope._cancel_handle is None: + scope._deliver_cancellation(scope) + + break + + # No point in looking beyond any shielded scope + if scope._shield: + break + + scope = scope._parent_scope + + def _reparent(self, new_parent: CancelScope) -> None: + """ + Move this active scope from its current parent to ``new_parent``. + + Used by :meth:`TaskGroup.start` to move a task that has just called + ``task_status.started()`` into the target task group's cancel scope. + """ + if self._parent_scope is new_parent: + return + + if self._parent_scope is not None: + self._parent_scope._child_scopes.discard(self) + + self._parent_scope = new_parent + new_parent._child_scopes.add(self) + + # If the new parent (or an ancestor) is already cancelled, (re)start the + # delivery loop to ensure we're cancelled at next checkpoint like Trio. + self._restart_cancellation_in_parent() + + def cancel(self, reason: str | None = None) -> None: + if not self._cancel_called: + if self._timeout_handle: + self._timeout_handle.cancel() + self._timeout_handle = None + + self._cancel_called = True + self._cancel_reason = f"Cancelled via cancel scope {id(self):x}" + if task := current_task(): + self._cancel_reason += f" by {task}" + + if reason: + self._cancel_reason += f"; reason: {reason}" + + if self._host_task is not None: + self._deliver_cancellation(self) + + @property + def deadline(self) -> float: + return self._deadline + + @deadline.setter + def deadline(self, value: float) -> None: + self._deadline = float(value) + if self._timeout_handle is not None: + self._timeout_handle.cancel() + self._timeout_handle = None + + if self._active and not self._cancel_called: + self._timeout() + + @property + def cancel_called(self) -> bool: + return self._cancel_called + + @property + def cancelled_caught(self) -> bool: + return self._cancelled_caught + + @property + def shield(self) -> bool: + return self._shield + + @shield.setter + def shield(self, value: bool) -> None: + if self._shield != value: + self._shield = value + if not value: + self._restart_cancellation_in_parent() + + +# +# Task states +# + + +class TaskState: + """ + Encapsulates auxiliary task information that cannot be added to the Task instance + itself because there are no guarantees about its implementation. + """ + + __slots__ = "__weakref__", "cancel_scope", "parent_id" + + def __init__(self, parent_id: int | None, cancel_scope: CancelScope | None): + self.parent_id = parent_id + self.cancel_scope = cancel_scope + + +_task_states: WeakKeyDictionary[asyncio.Task, TaskState] = WeakKeyDictionary() + + +# +# Task groups +# + + +class _AsyncioTaskStatus(abc.TaskStatus[T_contra]): + def __init__( + self, + future: asyncio.Future, + parent_id: int, + target_scope: CancelScope, + spawn_scope: CancelScope, + ): + self._future = future + self._parent_id = parent_id + # The eventual parent scope for this spawn_scope + # (after task_status.started() has been called) + self._target_scope = target_scope + # The task's own cancel scope, also held by its TaskHandle + self._spawn_scope = spawn_scope + + def started(self, value: T_contra | None = None) -> None: + task = cast(asyncio.Task, current_task()) + _task_states[task].parent_id = self._parent_id + if self._future.done(): + if not self._future.cancelled(): + raise RuntimeError("called 'started' twice on the same task status") + else: + # Caller of start() was cancelled, nothing to reparent + return + + self._future.set_result(value) + self._spawn_scope._reparent(self._target_scope) + + +if sys.version_info >= (3, 12): + _eager_task_factory_code: CodeType | None = asyncio.eager_task_factory.__code__ +else: + _eager_task_factory_code = None + + +class TaskGroup(abc.TaskGroup): + def __init__(self) -> None: + self.cancel_scope: CancelScope = CancelScope() + self._entered = False + self._exceptions: list[BaseException] = [] + self._tasks: set[asyncio.Task] = set() + self._on_completed_fut: asyncio.Future[None] | None = None + + async def __aenter__(self) -> Self: + if self._entered: + raise RuntimeError("TaskGroup cannot be entered more than once") + + self._entered = True + + self.cancel_scope.__enter__() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + try: + if exc_val is not None: + self.cancel_scope.cancel() + if not isinstance(exc_val, CancelledError): + self._exceptions.append(exc_val) + + loop = get_running_loop() + try: + if self._tasks: + with CancelScope() as wait_scope: + while self._tasks: + self._on_completed_fut = loop.create_future() + + try: + await self._on_completed_fut + except CancelledError as exc: + # Shield the scope against further cancellation attempts, + # as they're not productive (#695) + wait_scope.shield = True + self.cancel_scope.cancel() + + # Set exc_val from the cancellation exception if it was + # previously unset. However, we should not replace a native + # cancellation exception with one raise by a cancel scope. + if exc_val is None or ( + isinstance(exc_val, CancelledError) + and not is_anyio_cancellation(exc) + ): + exc_val = exc + + self._on_completed_fut = None + else: + # If there are no child tasks to wait on, run at least one checkpoint + # anyway + await AsyncIOBackend.cancel_shielded_checkpoint() + + if self._exceptions: + # The exception that got us here should already have been + # added to self._exceptions so it's ok to break exception + # chaining and avoid adding a "During handling of above..." + # for each nesting level. + raise BaseExceptionGroup( + "unhandled errors in a TaskGroup", self._exceptions + ) from None + elif exc_val: + raise exc_val + except BaseException as exc: + if self.cancel_scope.__exit__(type(exc), exc, exc.__traceback__): + return True + + raise + + return self.cancel_scope.__exit__(exc_type, exc_val, exc_tb) + finally: + del exc_val, exc_tb, self._exceptions + + def _spawn( + self, + coro: Coroutine[Any, Any, T_co], + name: object, + task_status: _AsyncioTaskStatus | None = None, + ) -> TaskHandle[T_co]: + task_status_future = task_status._future if task_status is not None else None + + def task_done(_task: asyncio.Task) -> None: + if sys.version_info >= (3, 14) and self.cancel_scope._host_task is not None: + asyncio.future_discard_from_awaited_by( + _task, self.cancel_scope._host_task + ) + + task_state = _task_states[_task] + assert task_state.cancel_scope is not None + assert _task in task_state.cancel_scope._tasks + task_state.cancel_scope._tasks.remove(_task) + self._tasks.remove(task) + del _task_states[_task] + + if self._on_completed_fut is not None and not self._tasks: + try: + self._on_completed_fut.set_result(None) + except asyncio.InvalidStateError: + pass + + try: + exc = _task.exception() + except CancelledError as e: + while isinstance(e.__context__, CancelledError): + e = e.__context__ + + exc = e + + if exc is not None: + # The future can only be in the cancelled state if the host task was + # cancelled, so return immediately instead of adding one more + # CancelledError to the exceptions list + if task_status_future is not None and task_status_future.cancelled(): + return + + if task_status_future is None or task_status_future.done(): + if not isinstance(exc, CancelledError): + self._exceptions.append(exc) + + if not self.cancel_scope._effectively_cancelled: + self.cancel_scope.cancel() + else: + task_status_future.set_exception(exc) + elif task_status_future is not None and not task_status_future.done(): + task_status_future.set_exception( + RuntimeError("Child exited without calling task_status.started()") + ) + + if task_status_future is not None: + parent_id = id(current_task()) + caller_state = _task_states.get(cast(asyncio.Task, current_task())) + if caller_state is not None and caller_state.cancel_scope is not None: + initial_scope = caller_state.cancel_scope + else: + # The caller is an unmanaged task (no task state) + initial_scope = self.cancel_scope + else: + parent_id = id(self.cancel_scope._host_task) + initial_scope = self.cancel_scope + + spawn_scope = task_status._spawn_scope if task_status is not None else None + handle = TaskHandle(coro, name, cancel_scope=spawn_scope) + loop = asyncio.get_running_loop() + wrapper_coro = handle._run_coro() + try: + if ( + (factory := loop.get_task_factory()) + and getattr(factory, "__code__", None) is _eager_task_factory_code + and (closure := getattr(factory, "__closure__", None)) + ): + custom_task_constructor = closure[0].cell_contents + task = custom_task_constructor( + wrapper_coro, loop=loop, name=handle.name + ) + else: + task = loop.create_task(wrapper_coro, name=handle.name) + except BaseException: + with suppress(BaseException): + wrapper_coro.close() + + with suppress(BaseException): + coro.close() + + raise + + # Make the spawned task inherit the initial cancel scope + _task_states[task] = TaskState(parent_id=parent_id, cancel_scope=initial_scope) + initial_scope._tasks.add(task) + self._tasks.add(task) + if sys.version_info >= (3, 14) and self.cancel_scope._host_task is not None: + asyncio.future_add_to_awaited_by(task, self.cancel_scope._host_task) + + task.add_done_callback(task_done) + return handle + + def create_task( + self, + coro: Coroutine[Any, Any, T_co], + *, + name: object = None, + context: Context | None = None, + ) -> TaskHandle[T_co]: + if not isinstance(coro, Coroutine): + raise TypeError(f"expected a coroutine, got {coro.__class__.__qualname__}") + + if not self._entered or not self.cancel_scope._active: + coro.close() + raise RuntimeError( + "This task group is not active; no new tasks can be started." + ) + + final_name = get_coro_name(coro, name) + + if context is not None: + return context.run(self._spawn, coro, name=final_name) + else: + return self._spawn(coro, name=final_name) + + async def start( + self, + func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], + *args: Unpack[PosArgsT], + name: object = None, + return_handle: Literal[False, True] = False, + ) -> Any: + if not self._entered or not self.cancel_scope._active: + raise RuntimeError( + "This task group is not active; no new tasks can be started." + ) + + # Until task_status.started() is called, a task spawned via start() belongs to + # the *caller's* cancel scope, not the target group's, so cancelling the group + # does not cancel a task that hasn't reported startup yet. The + # task_status.started() call moves the task's cancel scope to this task group's + # cancel scope. + # + # The caller may be an unmanaged task (no _task_states entry), in which case + # fall back to the group's own scope. + future: asyncio.Future = asyncio.Future() + final_name = get_callable_name(func, name) + task_status: _AsyncioTaskStatus[Any] = _AsyncioTaskStatus( + future, id(self.cancel_scope._host_task), self.cancel_scope, CancelScope() + ) + coro = call_for_coroutine(func, args, task_status=task_status) + handle = self._spawn(coro, final_name, task_status) + + # If the task raises an exception after sending a start value without a switch + # point between, the task group is cancelled and this method never proceeds to + # process the completed future. That's why we have to have a shielded cancel + # scope here. + try: + await future + except BaseException: + if handle.status is TaskHandle.Status.PENDING: + # Cancel the task and wait for it to exit before returning + handle.cancel() + with CancelScope(shield=True): + await handle.wait() + + raise + + if return_handle: + handle._start_value = future.result() + return handle + else: + return future.result() + + +# +# Threads +# + +_Retval_Queue_Type = tuple[T_Retval | None, BaseException | None] + + +class WorkerThread(Thread): + MAX_IDLE_TIME = 10 # seconds + + def __init__( + self, + root_task: asyncio.Task, + workers: set[WorkerThread], + idle_workers: deque[WorkerThread], + ): + kwargs: dict[str, Any] = {} + if sys.version_info >= (3, 14): + kwargs["context"] = Context() + + super().__init__(name="AnyIO worker thread", **kwargs) + self.root_task = root_task + self.workers = workers + self.idle_workers = idle_workers + self.loop = root_task._loop + self.queue: Queue[ + tuple[Context, Callable, tuple, asyncio.Future, CancelScope] | None + ] = Queue(2) + self.idle_since = AsyncIOBackend.current_time() + self.stopping = False + + def _report_result( + self, future: asyncio.Future, result: Any, exc: BaseException | None + ) -> None: + self.idle_since = AsyncIOBackend.current_time() + if not self.stopping: + self.idle_workers.append(self) + + if not future.cancelled(): + if exc is not None: + if isinstance(exc, StopIteration): + new_exc = RuntimeError("coroutine raised StopIteration") + new_exc.__cause__ = exc + exc = new_exc + + future.set_exception(exc) + else: + future.set_result(result) + + def run(self) -> None: + with claim_worker_thread(AsyncIOBackend, self.loop): + while True: + item = self.queue.get() + if item is None: + # Shutdown command received + return + + context, func, args, future, cancel_scope = item + if not future.cancelled(): + result = None + exception: BaseException | None = None + threadlocals.current_cancel_scope = cancel_scope + try: + result = context.run(func, *args) + except BaseException as exc: + exception = exc + finally: + del threadlocals.current_cancel_scope + + try: + self.loop.call_soon_threadsafe( + self._report_result, future, result, exception + ) + except RuntimeError: + if not self.loop.is_closed(): + raise + + del result, exception + + self.queue.task_done() + del item, context, func, args, future, cancel_scope + + def stop(self, f: asyncio.Task | None = None) -> None: + self.stopping = True + self.queue.put_nowait(None) + self.workers.discard(self) + try: + self.idle_workers.remove(self) + except ValueError: + pass + + +_threadpool_idle_workers: RunVar[deque[WorkerThread]] = RunVar( + "_threadpool_idle_workers" +) +_threadpool_workers: RunVar[set[WorkerThread]] = RunVar("_threadpool_workers") + + +# +# Subprocesses +# + + +@dataclass(eq=False) +class StreamReaderWrapper(abc.ByteReceiveStream): + _stream: asyncio.StreamReader + + async def receive(self, max_bytes: int = 65536) -> bytes: + if max_bytes < 1: + raise ValueError("max_bytes must be a positive integer") + + data = await self._stream.read(max_bytes) + if data: + return data + else: + raise EndOfStream + + async def aclose(self) -> None: + self._stream.set_exception(ClosedResourceError()) + await AsyncIOBackend.checkpoint() + + +@dataclass(eq=False) +class StreamWriterWrapper(abc.ByteSendStream): + _stream: asyncio.StreamWriter + _closed: bool = field(init=False, default=False) + + async def send(self, item: bytes) -> None: + await AsyncIOBackend.checkpoint_if_cancelled() + stream_paused = self._stream._protocol._paused # type: ignore[attr-defined] + try: + self._stream.write(item) + await self._stream.drain() + except (ConnectionResetError, BrokenPipeError, RuntimeError) as exc: + # If closed by us and/or the peer: + # * on stdlib, drain() raises ConnectionResetError or BrokenPipeError + # * on uvloop and Winloop, write() eventually starts raising RuntimeError + if self._closed: + raise ClosedResourceError from exc + elif self._stream.is_closing(): + raise BrokenResourceError from exc + + raise + + if not stream_paused: + await AsyncIOBackend.cancel_shielded_checkpoint() + + async def aclose(self) -> None: + self._closed = True + self._stream.close() + await AsyncIOBackend.checkpoint() + + +@dataclass(eq=False) +class Process(abc.Process): + _process: asyncio.subprocess.Process + _stdin: StreamWriterWrapper | None + _stdout: StreamReaderWrapper | None + _stderr: StreamReaderWrapper | None + _exited: asyncio.Event + _transport: asyncio.SubprocessTransport + + async def aclose(self) -> None: + with CancelScope(shield=True) as scope: + # We need to close the underlying pipe_transports as well to allow a + # process blocking on full buffers to receive SIGPIPE and exit. + if self._stdin: + await self._stdin.aclose() + if pipe := self._transport.get_pipe_transport(0): + pipe.close() + if self._stdout: + await self._stdout.aclose() + if pipe := self._transport.get_pipe_transport(1): + pipe.close() + if self._stderr: + await self._stderr.aclose() + if pipe := self._transport.get_pipe_transport(2): + pipe.close() + + scope.shield = False + try: + await self.wait() + except BaseException: + scope.shield = True + # Closing the transport on asyncio also handles sending kill + self._transport.close() + await self.wait() + raise + + async def wait(self) -> int: + await self._exited.wait() + assert self._process.returncode is not None + return self._process.returncode + + def terminate(self) -> None: + self._process.terminate() + + def kill(self) -> None: + self._process.kill() + + def send_signal(self, signal: int) -> None: + self._process.send_signal(signal) + + @property + def pid(self) -> int: + return self._process.pid + + @property + def returncode(self) -> int | None: + return self._process.returncode + + @property + def stdin(self) -> abc.ByteSendStream | None: + return self._stdin + + @property + def stdout(self) -> abc.ByteReceiveStream | None: + return self._stdout + + @property + def stderr(self) -> abc.ByteReceiveStream | None: + return self._stderr + + +def _forcibly_shutdown_process_pool_on_exit( + workers: set[Process], _task: object +) -> None: + """ + Forcibly shuts down worker processes belonging to this event loop.""" + child_watcher: asyncio.AbstractChildWatcher | None = None # type: ignore[name-defined] + if sys.version_info < (3, 12): + try: + child_watcher = asyncio.get_event_loop_policy().get_child_watcher() + except NotImplementedError: + pass + + # Close as much as possible (w/o async/await) to avoid warnings + for process in workers.copy(): + if process.returncode is not None: + continue + + process._stdin._stream._transport.close() # type: ignore[union-attr] + process._stdout._stream._transport.close() # type: ignore[union-attr] + process._stderr._stream._transport.close() # type: ignore[union-attr] + process.kill() + if child_watcher: + child_watcher.remove_child_handler(process.pid) + + +async def _shutdown_process_pool_on_exit(workers: set[abc.Process]) -> None: + """ + Shuts down worker processes belonging to this event loop. + + NOTE: this only works when the event loop was started using asyncio.run() or + anyio.run(). + + """ + process: abc.Process + try: + await sleep(math.inf) + except asyncio.CancelledError: + workers = workers.copy() + for process in workers: + if process.returncode is None: + process.kill() + + for process in workers: + await process.aclose() + + +# +# Sockets and networking +# + + +class StreamProtocol(asyncio.Protocol): + read_queue: deque[bytes] + read_event: asyncio.Event + write_event: asyncio.Event + exception: Exception | None = None + is_at_eof: bool = False + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + self.read_queue = deque() + self.read_event = asyncio.Event() + self.write_event = asyncio.Event() + self.write_event.set() + cast(asyncio.Transport, transport).set_write_buffer_limits(0) + + def connection_lost(self, exc: Exception | None) -> None: + if exc: + self.exception = exc + + self.read_event.set() + self.write_event.set() + + def data_received(self, data: bytes) -> None: + # ProactorEventloop sometimes sends bytearray instead of bytes + self.read_queue.append(bytes(data)) + self.read_event.set() + + def eof_received(self) -> bool | None: + self.is_at_eof = True + self.read_event.set() + return True + + def pause_writing(self) -> None: + self.write_event = asyncio.Event() + + def resume_writing(self) -> None: + self.write_event.set() + + +class DatagramProtocol(asyncio.DatagramProtocol): + read_queue: deque[tuple[bytes, IPSockAddrType]] + read_event: asyncio.Event + write_event: asyncio.Event + closed_event: asyncio.Event + exception: Exception | None = None + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + self.read_queue = deque(maxlen=100) # arbitrary value + self.read_event = asyncio.Event() + self.write_event = asyncio.Event() + self.closed_event = asyncio.Event() + self.write_event.set() + + def connection_lost(self, exc: Exception | None) -> None: + self.read_event.set() + self.write_event.set() + self.closed_event.set() + + def datagram_received(self, data: bytes, addr: IPSockAddrType) -> None: + addr = convert_ipv6_sockaddr(addr) + self.read_queue.append((data, addr)) + self.read_event.set() + + def error_received(self, exc: Exception) -> None: + self.exception = exc + + def pause_writing(self) -> None: + self.write_event.clear() + + def resume_writing(self) -> None: + self.write_event.set() + + +class SocketStream(abc.SocketStream): + def __init__(self, transport: asyncio.Transport, protocol: StreamProtocol): + self._transport = transport + self._protocol = protocol + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + self._closed = False + + @property + def _raw_socket(self) -> socket.socket: + return self._transport.get_extra_info("socket") + + async def receive(self, max_bytes: int = 65536) -> bytes: + if max_bytes < 1: + raise ValueError("max_bytes must be a positive integer") + + with self._receive_guard: + if ( + not self._protocol.read_event.is_set() + and not self._transport.is_closing() + and not self._protocol.is_at_eof + ): + self._transport.resume_reading() + await self._protocol.read_event.wait() + self._transport.pause_reading() + else: + await AsyncIOBackend.checkpoint() + + try: + chunk = self._protocol.read_queue.popleft() + except IndexError: + if self._closed: + raise ClosedResourceError from None + elif self._protocol.exception: + raise BrokenResourceError from self._protocol.exception + else: + raise EndOfStream from None + + if len(chunk) > max_bytes: + # Split the oversized chunk + chunk, leftover = chunk[:max_bytes], chunk[max_bytes:] + self._protocol.read_queue.appendleft(leftover) + + # If the read queue is empty, clear the flag so that the next call will + # block until data is available + if not self._protocol.read_queue: + self._protocol.read_event.clear() + + return chunk + + async def send(self, item: bytes) -> None: + with self._send_guard: + await AsyncIOBackend.checkpoint() + + if self._closed: + raise ClosedResourceError + elif self._protocol.exception is not None: + raise BrokenResourceError from self._protocol.exception + + try: + self._transport.write(item) + except RuntimeError as exc: + if self._transport.is_closing(): + raise BrokenResourceError from exc + else: + raise + + await self._protocol.write_event.wait() + + async def send_eof(self) -> None: + try: + self._transport.write_eof() + except OSError: + pass + + async def aclose(self) -> None: + self._closed = True + if not self._transport.is_closing(): + try: + self._transport.write_eof() + except OSError: + pass + + self._transport.close() + await sleep(0) + self._transport.abort() + + +class _RawSocketMixin: + _receive_future: asyncio.Future | None = None + _send_future: asyncio.Future | None = None + _closing = False + + def __init__(self, raw_socket: socket.socket): + self.__raw_socket = raw_socket + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + @property + def _raw_socket(self) -> socket.socket: + return self.__raw_socket + + def _wait_until_readable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future: + def callback(f: object) -> None: + del self._receive_future + loop.remove_reader(self.__raw_socket) + + f = self._receive_future = asyncio.Future() + loop.add_reader(self.__raw_socket, f.set_result, None) + f.add_done_callback(callback) + return f + + def _wait_until_writable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future: + def callback(f: object) -> None: + del self._send_future + loop.remove_writer(self.__raw_socket) + + f = self._send_future = asyncio.Future() + loop.add_writer(self.__raw_socket, f.set_result, None) + f.add_done_callback(callback) + return f + + async def aclose(self) -> None: + if not self._closing: + self._closing = True + if self.__raw_socket.fileno() != -1: + self.__raw_socket.close() + + if self._receive_future and not self._receive_future.done(): + self._receive_future.set_result(None) + if self._send_future and not self._send_future.done(): + self._send_future.set_result(None) + + +class UNIXSocketStream(_RawSocketMixin, abc.UNIXSocketStream): + async def send_eof(self) -> None: + with self._send_guard: + self._raw_socket.shutdown(socket.SHUT_WR) + + async def receive(self, max_bytes: int = 65536) -> bytes: + if max_bytes < 1: + raise ValueError("max_bytes must be a positive integer") + + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + data = self._raw_socket.recv(max_bytes) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + if not data: + raise EndOfStream + + return data + + async def send(self, item: bytes) -> None: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._send_guard: + view = memoryview(item) + while view: + try: + bytes_sent = self._raw_socket.send(view) + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + view = view[bytes_sent:] + + async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: + if not isinstance(msglen, int) or msglen < 0: + raise ValueError("msglen must be a non-negative integer") + if not isinstance(maxfds, int) or maxfds < 1: + raise ValueError("maxfds must be a positive integer") + + loop = get_running_loop() + fds = array.array("i") + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + message, ancdata, _flags, _addr = self._raw_socket.recvmsg( + msglen, socket.CMSG_LEN(maxfds * fds.itemsize) + ) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + if not message and not ancdata: + raise EndOfStream + + break + + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS: + raise RuntimeError( + f"Received unexpected ancillary data; message = {message!r}, " + f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}" + ) + + fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) + + return message, list(fds) + + async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: + if not message: + raise ValueError("message must not be empty") + if not fds: + raise ValueError("fds must not be empty") + + loop = get_running_loop() + filenos: list[int] = [] + for fd in fds: + if isinstance(fd, int): + filenos.append(fd) + elif isinstance(fd, IOBase): + filenos.append(fd.fileno()) + + fdarray = array.array("i", filenos) + await AsyncIOBackend.checkpoint() + with self._send_guard: + while True: + try: + # The ignore can be removed after mypy picks up + # https://github.com/python/typeshed/pull/5545 + self._raw_socket.sendmsg( + [message], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, fdarray)] + ) + break + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + + +class TCPSocketListener(abc.SocketListener): + _accept_scope: CancelScope | None = None + _closed = False + + def __init__(self, raw_socket: socket.socket): + self.__raw_socket = raw_socket + self._loop = cast(asyncio.BaseEventLoop, get_running_loop()) + self._accept_guard = ResourceGuard("accepting connections from") + + @property + def _raw_socket(self) -> socket.socket: + return self.__raw_socket + + async def accept(self) -> abc.SocketStream: + if self._closed: + raise ClosedResourceError + + with self._accept_guard: + await AsyncIOBackend.checkpoint() + with CancelScope() as self._accept_scope: + try: + client_sock, _addr = await self._loop.sock_accept(self._raw_socket) + except asyncio.CancelledError: + # Workaround for https://bugs.python.org/issue41317 + try: + self._loop.remove_reader(self._raw_socket) + except (ValueError, NotImplementedError): + pass + + if self._closed: + raise ClosedResourceError from None + + raise + finally: + self._accept_scope = None + + client_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + transport, protocol = await self._loop.connect_accepted_socket( + StreamProtocol, client_sock + ) + return SocketStream(transport, protocol) + + async def aclose(self) -> None: + if self._closed: + return + + self._closed = True + if self._accept_scope: + # Workaround for https://bugs.python.org/issue41317 + try: + self._loop.remove_reader(self._raw_socket) + except (ValueError, NotImplementedError): + pass + + self._accept_scope.cancel() + await sleep(0) + + self._raw_socket.close() + + +class UNIXSocketListener(abc.SocketListener): + def __init__(self, raw_socket: socket.socket): + self.__raw_socket = raw_socket + self._loop = get_running_loop() + self._accept_guard = ResourceGuard("accepting connections from") + self._closed = False + + async def accept(self) -> abc.SocketStream: + await AsyncIOBackend.checkpoint() + with self._accept_guard: + while True: + try: + client_sock, _ = self.__raw_socket.accept() + client_sock.setblocking(False) + return UNIXSocketStream(client_sock) + except BlockingIOError: + f: asyncio.Future = asyncio.Future() + self._loop.add_reader(self.__raw_socket, f.set_result, None) + f.add_done_callback( + lambda _: self._loop.remove_reader(self.__raw_socket) + ) + await f + except OSError as exc: + if self._closed: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + + async def aclose(self) -> None: + self._closed = True + self.__raw_socket.close() + + @property + def _raw_socket(self) -> socket.socket: + return self.__raw_socket + + +class UDPSocket(abc.UDPSocket): + def __init__( + self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol + ): + self._transport = transport + self._protocol = protocol + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + self._closed = False + + @property + def _raw_socket(self) -> socket.socket: + return self._transport.get_extra_info("socket") + + async def aclose(self) -> None: + self._closed = True + if not self._transport.is_closing(): + self._transport.close() + + await self._protocol.closed_event.wait() + + async def receive(self) -> tuple[bytes, IPSockAddrType]: + with self._receive_guard: + await AsyncIOBackend.checkpoint() + + # If the buffer is empty, ask for more data + if not self._protocol.read_queue and not self._transport.is_closing(): + self._protocol.read_event.clear() + await self._protocol.read_event.wait() + + try: + return self._protocol.read_queue.popleft() + except IndexError: + if self._closed: + raise ClosedResourceError from None + else: + raise BrokenResourceError from None + + async def send(self, item: UDPPacketType) -> None: + with self._send_guard: + await AsyncIOBackend.checkpoint() + await self._protocol.write_event.wait() + if self._closed: + raise ClosedResourceError + elif self._transport.is_closing(): + raise BrokenResourceError + else: + self._transport.sendto(*item) + + +class ConnectedUDPSocket(abc.ConnectedUDPSocket): + def __init__( + self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol + ): + self._transport = transport + self._protocol = protocol + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + self._closed = False + + @property + def _raw_socket(self) -> socket.socket: + return self._transport.get_extra_info("socket") + + async def aclose(self) -> None: + self._closed = True + if not self._transport.is_closing(): + self._transport.close() + + await self._protocol.closed_event.wait() + + async def receive(self) -> bytes: + with self._receive_guard: + await AsyncIOBackend.checkpoint() + + # If the buffer is empty, ask for more data + if not self._protocol.read_queue and not self._transport.is_closing(): + self._protocol.read_event.clear() + await self._protocol.read_event.wait() + + try: + packet = self._protocol.read_queue.popleft() + except IndexError: + if self._closed: + raise ClosedResourceError from None + else: + raise BrokenResourceError from None + + return packet[0] + + async def send(self, item: bytes) -> None: + with self._send_guard: + await AsyncIOBackend.checkpoint() + await self._protocol.write_event.wait() + if self._closed: + raise ClosedResourceError + elif self._transport.is_closing(): + raise BrokenResourceError + else: + self._transport.sendto(item) + + +class UNIXDatagramSocket(_RawSocketMixin, abc.UNIXDatagramSocket): + async def receive(self) -> UNIXDatagramPacketType: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + data = self._raw_socket.recvfrom(65536) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return data + + async def send(self, item: UNIXDatagramPacketType) -> None: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._send_guard: + while True: + try: + self._raw_socket.sendto(*item) + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return + + +class ConnectedUNIXDatagramSocket(_RawSocketMixin, abc.ConnectedUNIXDatagramSocket): + async def receive(self) -> bytes: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + data = self._raw_socket.recv(65536) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return data + + async def send(self, item: bytes) -> None: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._send_guard: + while True: + try: + self._raw_socket.send(item) + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return + + +_read_events: RunVar[dict[int, asyncio.Future[bool]]] = RunVar("read_events") +_write_events: RunVar[dict[int, asyncio.Future[bool]]] = RunVar("write_events") + + +# +# Synchronization +# + + +class Event(BaseEvent): + __slots__ = ("_event",) + + def __new__(cls) -> Self: + return object.__new__(cls) + + def __init__(self) -> None: + self._event = asyncio.Event() + + def set(self) -> None: + self._event.set() + + def is_set(self) -> bool: + return self._event.is_set() + + async def wait(self) -> None: + if self.is_set(): + await AsyncIOBackend.checkpoint() + else: + await self._event.wait() + + def statistics(self) -> EventStatistics: + return EventStatistics(len(self._event._waiters)) + + +class Lock(BaseLock): + __slots__ = "_fast_acquire", "_owner_task", "_waiters" + + def __new__(cls, *, fast_acquire: bool = False) -> Self: + return object.__new__(cls) + + def __init__(self, *, fast_acquire: bool = False) -> None: + self._fast_acquire = fast_acquire + self._owner_task: asyncio.Task | None = None + self._waiters: deque[tuple[asyncio.Task, asyncio.Future]] = deque() + + async def acquire(self) -> None: + task = cast(asyncio.Task, current_task()) + if self._owner_task is None and not self._waiters: + await AsyncIOBackend.checkpoint_if_cancelled() + self._owner_task = task + + # Unless on the "fast path", yield control of the event loop so that other + # tasks can run too + if not self._fast_acquire: + try: + await AsyncIOBackend.cancel_shielded_checkpoint() + except CancelledError: + self.release() + raise + + return + + if self._owner_task == task: + raise RuntimeError("Attempted to acquire an already held Lock") + + fut: asyncio.Future[None] = asyncio.Future() + item = task, fut + self._waiters.append(item) + try: + await fut + except CancelledError: + if fut.cancelled(): + try: + self._waiters.remove(item) + except ValueError: + pass + else: + self.release() + + raise + + def acquire_nowait(self) -> None: + task = cast(asyncio.Task, current_task()) + if self._owner_task is None and not self._waiters: + self._owner_task = task + return + + if self._owner_task is task: + raise RuntimeError("Attempted to acquire an already held Lock") + + raise WouldBlock + + def locked(self) -> bool: + return self._owner_task is not None + + def release(self) -> None: + if self._owner_task != current_task(): + raise RuntimeError("The current task is not holding this lock") + + # A cancelled waiter that already received ownership removes itself from + # _waiters before calling release(); any cancelled waiter still queued here + # was cancelled before being woken, so drop it. + while self._waiters: + task, fut = self._waiters.popleft() + if fut.cancelled(): + continue + + self._owner_task = task + fut.set_result(None) + return + + self._owner_task = None + + def statistics(self) -> LockStatistics: + task_info = AsyncIOTaskInfo(self._owner_task) if self._owner_task else None + return LockStatistics(self.locked(), task_info, len(self._waiters)) + + +class Semaphore(BaseSemaphore): + __slots__ = "_fast_acquire", "_max_value", "_value", "_waiters" + + def __new__( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> Self: + return object.__new__(cls) + + def __init__( + self, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ): + super().__init__(initial_value, max_value=max_value) + self._value = initial_value + self._max_value = max_value + self._fast_acquire = fast_acquire + self._waiters: deque[asyncio.Future[None]] = deque() + + async def acquire(self) -> None: + if self._value > 0 and not self._waiters: + await AsyncIOBackend.checkpoint_if_cancelled() + self._value -= 1 + + # Unless on the "fast path", yield control of the event loop so that other + # tasks can run too + if not self._fast_acquire: + try: + await AsyncIOBackend.cancel_shielded_checkpoint() + except CancelledError: + self.release() + raise + + return + + fut: asyncio.Future[None] = asyncio.Future() + self._waiters.append(fut) + try: + await fut + except CancelledError: + if fut.cancelled(): + try: + self._waiters.remove(fut) + except ValueError: + pass + else: + self.release() + + raise + + def acquire_nowait(self) -> None: + if self._value == 0: + raise WouldBlock + + self._value -= 1 + + def release(self) -> None: + if self._max_value is not None and self._value == self._max_value: + raise ValueError("semaphore released too many times") + + while self._waiters: + fut = self._waiters.popleft() + if fut.cancelled(): + continue + + fut.set_result(None) + return + + self._value += 1 + + @property + def value(self) -> int: + return self._value + + @property + def max_value(self) -> int | None: + return self._max_value + + def statistics(self) -> SemaphoreStatistics: + return SemaphoreStatistics(len(self._waiters)) + + +class CapacityLimiter(BaseCapacityLimiter): + __slots__ = "_borrowers", "_total_tokens", "_wait_queue" + + def __new__(cls, total_tokens: float) -> Self: + return object.__new__(cls) + + def __init__(self, total_tokens: float): + self._total_tokens: float = 0 + self._borrowers: set[Any] = set() + self._wait_queue: OrderedDict[Any, asyncio.Event] = OrderedDict() + self.total_tokens = total_tokens + + async def __aenter__(self) -> None: + await self.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + @property + def total_tokens(self) -> float: + return self._total_tokens + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + if not isinstance(value, int) and not math.isinf(value): + raise TypeError("total_tokens must be an int or math.inf") + + if value < 0: + raise ValueError("total_tokens must be >= 0") + + self._total_tokens = value + + # Notify waiting tasks that they have acquired the limiter while + # there is spare capacity. + while self._wait_queue and len(self._borrowers) < self._total_tokens: + borrower, event = self._wait_queue.popitem(last=False) + self._borrowers.add(borrower) + event.set() + + @property + def borrowed_tokens(self) -> int: + return len(self._borrowers) + + @property + def available_tokens(self) -> float: + return self._total_tokens - len(self._borrowers) + + def _notify_next_waiter(self) -> None: + """Hand a free token to the next task in line, if any.""" + if self._wait_queue and len(self._borrowers) < self._total_tokens: + borrower, event = self._wait_queue.popitem(last=False) + self._borrowers.add(borrower) + event.set() + + def acquire_nowait(self) -> None: + self.acquire_on_behalf_of_nowait(current_task()) + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + if borrower in self._borrowers: + raise RuntimeError( + "this borrower is already holding one of this CapacityLimiter's tokens" + ) + + if self._wait_queue or len(self._borrowers) >= self._total_tokens: + raise WouldBlock + + self._borrowers.add(borrower) + + async def acquire(self) -> None: + return await self.acquire_on_behalf_of(current_task()) + + async def acquire_on_behalf_of(self, borrower: object) -> None: + await AsyncIOBackend.checkpoint_if_cancelled() + try: + self.acquire_on_behalf_of_nowait(borrower) + except WouldBlock: + event = asyncio.Event() + self._wait_queue[borrower] = event + try: + await event.wait() + except BaseException: + self._wait_queue.pop(borrower, None) + if event.is_set(): + self._borrowers.discard(borrower) + self._notify_next_waiter() + + raise + else: + try: + await AsyncIOBackend.cancel_shielded_checkpoint() + except BaseException: + self.release() + raise + + def release(self) -> None: + self.release_on_behalf_of(current_task()) + + def release_on_behalf_of(self, borrower: object) -> None: + try: + self._borrowers.remove(borrower) + except KeyError: + raise RuntimeError( + "this borrower isn't holding any of this CapacityLimiter's tokens" + ) from None + + self._notify_next_waiter() + + def statistics(self) -> CapacityLimiterStatistics: + return CapacityLimiterStatistics( + self.borrowed_tokens, + self.total_tokens, + tuple(self._borrowers), + len(self._wait_queue), + ) + + +_default_thread_limiter: RunVar[CapacityLimiter] = RunVar("_default_thread_limiter") + + +# +# Operating system signals +# + + +class _SignalReceiver: + def __init__(self, signals: tuple[Signals, ...]): + self._signals = signals + self._loop = get_running_loop() + self._signal_queue: deque[Signals] = deque() + self._future: asyncio.Future = asyncio.Future() + self._handled_signals: set[Signals] = set() + + def _deliver(self, signum: Signals) -> None: + self._signal_queue.append(signum) + if not self._future.done(): + self._future.set_result(None) + + def __enter__(self) -> Self: + for sig in set(self._signals): + self._loop.add_signal_handler(sig, self._deliver, sig) + self._handled_signals.add(sig) + + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + for sig in self._handled_signals: + self._loop.remove_signal_handler(sig) + + def __aiter__(self) -> _SignalReceiver: + return self + + async def __anext__(self) -> Signals: + await AsyncIOBackend.checkpoint() + if not self._signal_queue: + self._future = asyncio.Future() + await self._future + + return self._signal_queue.popleft() + + +# +# Testing and debugging +# + + +class AsyncIOTaskInfo(TaskInfo): + def __init__(self, task: asyncio.Task): + task_state = _task_states.get(task) + if task_state is None: + parent_id = None + else: + parent_id = task_state.parent_id + + coro = task.get_coro() + assert coro is not None, "created TaskInfo from a completed Task" + super().__init__(id(task), parent_id, task.get_name(), coro) + self._task = weakref.ref(task) + + def has_pending_cancellation(self) -> bool: + if not (task := self._task()): + # If the task isn't around anymore, it won't have a pending cancellation + return False + + if task._must_cancel or ( # type: ignore[attr-defined] + isinstance(task._fut_waiter, asyncio.Future) # type: ignore[attr-defined] + and task._fut_waiter.cancelled() # type: ignore[attr-defined] + ): + return True + + if task_state := _task_states.get(task): + if cancel_scope := task_state.cancel_scope: + return cancel_scope._effectively_cancelled + + return False + + +class TestRunner(abc.TestRunner): + _send_stream: MemoryObjectSendStream[tuple[Awaitable[Any], asyncio.Future[Any]]] + + def __init__( + self, + *, + debug: bool | None = None, + use_uvloop: bool = False, + loop_factory: Callable[[], AbstractEventLoop] | None = None, + ) -> None: + if use_uvloop and loop_factory is None: + if sys.platform != "win32": + import uvloop + + loop_factory = uvloop.new_event_loop + else: + import winloop + + loop_factory = winloop.new_event_loop + + self._runner = Runner(debug=debug, loop_factory=loop_factory) + self._exceptions: list[BaseException] = [] + self._runner_task: asyncio.Task | None = None + + def __enter__(self) -> Self: + self._runner.__enter__() + self.get_loop().set_exception_handler(self._exception_handler) + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self._runner.__exit__(exc_type, exc_val, exc_tb) + + def get_loop(self) -> AbstractEventLoop: + return self._runner.get_loop() + + def is_running(self) -> bool: + try: + asyncio.get_running_loop() + return True + except RuntimeError: + return False + + def _exception_handler( + self, loop: asyncio.AbstractEventLoop, context: dict[str, Any] + ) -> None: + if isinstance(context.get("exception"), Exception): + self._exceptions.append(context["exception"]) + else: + loop.default_exception_handler(context) + + def _raise_async_exceptions(self) -> None: + # Re-raise any exceptions raised in asynchronous callbacks + if self._exceptions: + exceptions, self._exceptions = self._exceptions, [] + if len(exceptions) == 1: + raise exceptions[0] + elif exceptions: + raise BaseExceptionGroup( + "Multiple exceptions occurred in asynchronous callbacks", exceptions + ) + + async def _run_tests_and_fixtures( + self, + receive_stream: MemoryObjectReceiveStream[ + tuple[Awaitable[T_Retval], asyncio.Future[T_Retval]] + ], + ) -> None: + from _pytest.outcomes import OutcomeException + + with receive_stream, self._send_stream: + async for coro, future in receive_stream: + try: + retval = await coro + except CancelledError as exc: + if not future.cancelled(): + future.cancel(*exc.args) + + raise + except BaseException as exc: + if not future.cancelled(): + future.set_exception(exc) + + if not isinstance(exc, (Exception, OutcomeException)): + raise + else: + if not future.cancelled(): + future.set_result(retval) + + async def _call_in_runner_task( + self, + func: Callable[P, Awaitable[T_Retval]], + /, + *args: P.args, + **kwargs: P.kwargs, + ) -> T_Retval: + if not self._runner_task: + self._send_stream, receive_stream = create_memory_object_stream[ + tuple[Awaitable[Any], asyncio.Future] + ](1) + self._runner_task = self.get_loop().create_task( + self._run_tests_and_fixtures(receive_stream) + ) + + coro = func(*args, **kwargs) + future: asyncio.Future[T_Retval] = self.get_loop().create_future() + self._send_stream.send_nowait((coro, future)) + return await future + + def run_asyncgen_fixture( + self, + fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]], + kwargs: dict[str, Any], + ) -> Iterable[T_Retval]: + asyncgen = fixture_func(**kwargs) + fixturevalue: T_Retval = self.get_loop().run_until_complete( + self._call_in_runner_task(asyncgen.asend, None) + ) + self._raise_async_exceptions() + + yield fixturevalue + + try: + self.get_loop().run_until_complete( + self._call_in_runner_task(asyncgen.asend, None) + ) + except StopAsyncIteration: + self._raise_async_exceptions() + else: + self.get_loop().run_until_complete(asyncgen.aclose()) + raise RuntimeError("Async generator fixture did not stop") + + def run_fixture( + self, + fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]], + kwargs: dict[str, Any], + ) -> T_Retval: + retval = self.get_loop().run_until_complete( + self._call_in_runner_task(fixture_func, **kwargs) + ) + self._raise_async_exceptions() + return retval + + def run_test( + self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] + ) -> None: + from _pytest.outcomes import OutcomeException + + try: + self.get_loop().run_until_complete( + self._call_in_runner_task(test_func, **kwargs) + ) + except Exception as exc: + self._exceptions.append(exc) + except OutcomeException: + raise + except BaseException: + # A BaseException (e.g. KeyboardInterrupt, SystemExit) interrupted the event loop before + # the test completed. Cancel _runner_task so it does not resume when the event + # loop is re-entered during async generator fixture teardown. + if self._runner_task is not None and not self._runner_task.done(): + self._runner_task.cancel() + self._send_stream.close() + try: + self.get_loop().run_until_complete(self._runner_task) + except CancelledError: + pass + finally: + self._runner_task = None + raise + self._raise_async_exceptions() + + +class _ProcessStreamProtocol(asyncio.subprocess.SubprocessStreamProtocol): + """ + A subprocess protocol that allows us to be notified of ``process_exited`` + + asyncio's own ``Process.wait()`` only resolves once every pipe transport has + disconnected so to get same semantics as on trio and uvloop we need this. + """ + + def __init__(self) -> None: + # Match the standard factory for asyncio.create_process + super().__init__(limit=2**16, loop=asyncio.get_running_loop()) + self.exited = asyncio.Event() + + def process_exited(self) -> None: + super().process_exited() + self.exited.set() + + +class AsyncIOBackend(AsyncBackend): + @classmethod + def run( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + options: dict[str, Any], + ) -> T_Retval: + @wraps(func) + async def wrapper() -> T_Retval: + task = cast(asyncio.Task, current_task()) + task.set_name(get_callable_name(func)) + _task_states[task] = TaskState(None, None) + + try: + return await func(*args) + finally: + del _task_states[task] + + debug = options.get("debug", None) + loop_factory = options.get("loop_factory", None) + if loop_factory is None and options.get("use_uvloop", False): + if sys.platform != "win32": + import uvloop + + loop_factory = uvloop.new_event_loop + else: + import winloop + + loop_factory = winloop.new_event_loop + + with Runner(debug=debug, loop_factory=loop_factory) as runner: + return runner.run(wrapper()) + + @classmethod + def current_token(cls) -> object: + return get_running_loop() + + @classmethod + def current_time(cls) -> float: + return get_running_loop().time() + + @classmethod + def cancelled_exception_class(cls) -> type[BaseException]: + return CancelledError + + @classmethod + async def checkpoint(cls) -> None: + await sleep(0) + + @classmethod + async def checkpoint_if_cancelled(cls) -> None: + task = current_task() + if task is None: + return + + try: + cancel_scope = _task_states[task].cancel_scope + except KeyError: + return + + while cancel_scope: + if cancel_scope.cancel_called: + await sleep(0) + elif cancel_scope.shield: + break + else: + cancel_scope = cancel_scope._parent_scope + + @classmethod + async def cancel_shielded_checkpoint(cls) -> None: + with CancelScope(shield=True): + await sleep(0) + + @classmethod + async def sleep(cls, delay: float) -> None: + await sleep(delay) + + @classmethod + def create_cancel_scope( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + return CancelScope(deadline=deadline, shield=shield) + + @classmethod + def current_effective_deadline(cls) -> float: + if (task := current_task()) is None: + return math.inf + + try: + cancel_scope = _task_states[task].cancel_scope + except KeyError: + return math.inf + + deadline = math.inf + while cancel_scope: + deadline = min(deadline, cancel_scope.deadline) + if cancel_scope._cancel_called: + deadline = -math.inf + break + elif cancel_scope.shield: + break + else: + cancel_scope = cancel_scope._parent_scope + + return deadline + + @classmethod + def create_task_group(cls) -> abc.TaskGroup: + return TaskGroup() + + @classmethod + def create_event(cls) -> BaseEvent: + return Event() + + @classmethod + def create_lock(cls, *, fast_acquire: bool) -> BaseLock: + return Lock(fast_acquire=fast_acquire) + + @classmethod + def create_semaphore( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> BaseSemaphore: + return Semaphore(initial_value, max_value=max_value, fast_acquire=fast_acquire) + + @classmethod + def create_capacity_limiter(cls, total_tokens: float) -> BaseCapacityLimiter: + return CapacityLimiter(total_tokens) + + @classmethod + async def run_sync_in_worker_thread( # type: ignore[return] + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + abandon_on_cancel: bool = False, + limiter: BaseCapacityLimiter | None = None, + ) -> T_Retval: + await cls.checkpoint() + + # If this is the first run in this event loop thread, set up the necessary + # variables + try: + idle_workers = _threadpool_idle_workers.get() + workers = _threadpool_workers.get() + except LookupError: + idle_workers = deque() + workers = set() + _threadpool_idle_workers.set(idle_workers) + _threadpool_workers.set(workers) + + async with limiter or cls.current_default_thread_limiter(): + with CancelScope(shield=not abandon_on_cancel) as scope: + future = asyncio.Future[T_Retval]() + root_task = find_root_task() + if not idle_workers: + worker = WorkerThread(root_task, workers, idle_workers) + worker.start() + workers.add(worker) + root_task.add_done_callback(worker.stop, context=Context()) + else: + worker = idle_workers.pop() + + # Prune any other workers that have been idle for MAX_IDLE_TIME + # seconds or longer + now = cls.current_time() + while idle_workers: + if ( + now - idle_workers[0].idle_since + < WorkerThread.MAX_IDLE_TIME + ): + break + + expired_worker = idle_workers.popleft() + expired_worker.root_task.remove_done_callback( + expired_worker.stop + ) + expired_worker.stop() + + context = copy_context() + context.run(set_current_async_library, None) + if abandon_on_cancel or scope._parent_scope is None: + worker_scope = scope + else: + worker_scope = scope._parent_scope + + worker.queue.put_nowait((context, func, args, future, worker_scope)) + return await future + + @classmethod + def check_cancelled(cls) -> None: + scope: CancelScope | None = threadlocals.current_cancel_scope + while scope is not None: + if scope.cancel_called: + raise CancelledError(f"Cancelled via cancel scope {id(scope):x}") + + if scope.shield: + return + + scope = scope._parent_scope + + @classmethod + def run_async_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_co: + async def task_wrapper() -> T_co: + __tracebackhide__ = True + if scope is not None: + task = cast(asyncio.Task, current_task()) + _task_states[task] = TaskState(None, scope) + scope._tasks.add(task) + try: + return await func(*args) + except CancelledError as exc: + raise concurrent.futures.CancelledError(str(exc)) from None + finally: + if scope is not None: + scope._tasks.discard(task) + + loop = cast( + "AbstractEventLoop", token or threadlocals.current_token.native_token + ) + if loop.is_closed(): + raise RunFinishedError + + context = copy_context() + context.run(set_current_async_library, "asyncio") + scope = getattr(threadlocals, "current_cancel_scope", None) + f: concurrent.futures.Future[T_co] = context.run( + asyncio.run_coroutine_threadsafe, task_wrapper(), loop=loop + ) + return f.result() + + @classmethod + def run_sync_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + @wraps(func) + def wrapper() -> None: + try: + set_current_async_library("asyncio") + f.set_result(func(*args)) + except BaseException as exc: + f.set_exception(exc) + if not isinstance(exc, Exception): + raise + + loop = cast( + "AbstractEventLoop", token or threadlocals.current_token.native_token + ) + if loop.is_closed(): + raise RunFinishedError + + f: concurrent.futures.Future[T_Retval] = Future() + loop.call_soon_threadsafe(wrapper) + return f.result() + + @classmethod + async def open_process( + cls, + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + **kwargs: Any, + ) -> Process: + await cls.checkpoint() + if isinstance(command, PathLike): + command = os.fspath(command) + + # Use loop.subprocess_shell()/subprocess_exec() rather than their + # asyncio.create_subprocess_*() counterparts to get access to + # transport/protocol. + loop = asyncio.get_running_loop() + if isinstance(command, (str, bytes)): + transport, protocol = await loop.subprocess_shell( + _ProcessStreamProtocol, + command, + stdin=stdin, + stdout=stdout, + stderr=stderr, + **kwargs, + ) + else: + transport, protocol = await loop.subprocess_exec( + _ProcessStreamProtocol, + *command, + stdin=stdin, + stdout=stdout, + stderr=stderr, + **kwargs, + ) + + process = asyncio.subprocess.Process(transport, protocol, loop) + stdin_stream = StreamWriterWrapper(process.stdin) if process.stdin else None + stdout_stream = StreamReaderWrapper(process.stdout) if process.stdout else None + stderr_stream = StreamReaderWrapper(process.stderr) if process.stderr else None + return Process( + process, + stdin_stream, + stdout_stream, + stderr_stream, + protocol.exited, + transport, + ) + + @classmethod + def setup_process_pool_exit_at_shutdown(cls, workers: set[abc.Process]) -> None: + create_task( + _shutdown_process_pool_on_exit(workers), + name="AnyIO process pool shutdown task", + ) + find_root_task().add_done_callback( + partial(_forcibly_shutdown_process_pool_on_exit, workers) # type:ignore[arg-type] + ) + + @classmethod + async def connect_tcp( + cls, host: str, port: int, local_address: IPSockAddrType | None = None + ) -> abc.SocketStream: + transport, protocol = cast( + tuple[asyncio.Transport, StreamProtocol], + await get_running_loop().create_connection( + StreamProtocol, host, port, local_addr=local_address + ), + ) + transport.pause_reading() + return SocketStream(transport, protocol) + + @classmethod + async def connect_unix(cls, path: str | bytes) -> abc.UNIXSocketStream: + await cls.checkpoint() + loop = get_running_loop() + raw_socket = socket.socket(socket.AF_UNIX) + raw_socket.setblocking(False) + while True: + try: + raw_socket.connect(path) + except BlockingIOError: + f: asyncio.Future = asyncio.Future() + loop.add_writer(raw_socket, f.set_result, None) + f.add_done_callback(lambda _: loop.remove_writer(raw_socket)) + await f + except BaseException: + raw_socket.close() + raise + else: + return UNIXSocketStream(raw_socket) + + @classmethod + def create_tcp_listener(cls, sock: socket.socket) -> SocketListener: + return TCPSocketListener(sock) + + @classmethod + def create_unix_listener(cls, sock: socket.socket) -> SocketListener: + return UNIXSocketListener(sock) + + @classmethod + async def create_udp_socket( + cls, + family: AddressFamily, + local_address: IPSockAddrType | None, + remote_address: IPSockAddrType | None, + reuse_port: bool, + ) -> UDPSocket | ConnectedUDPSocket: + transport, protocol = await get_running_loop().create_datagram_endpoint( + DatagramProtocol, + local_addr=local_address, + remote_addr=remote_address, + family=family, + reuse_port=reuse_port, + ) + if protocol.exception: + transport.close() + raise protocol.exception + + if not remote_address: + return UDPSocket(transport, protocol) + else: + return ConnectedUDPSocket(transport, protocol) + + @classmethod + async def create_unix_datagram_socket( # type: ignore[override] + cls, raw_socket: socket.socket, remote_path: str | bytes | None + ) -> abc.UNIXDatagramSocket | abc.ConnectedUNIXDatagramSocket: + await cls.checkpoint() + loop = get_running_loop() + + if remote_path: + while True: + try: + raw_socket.connect(remote_path) + except BlockingIOError: + f: asyncio.Future = asyncio.Future() + loop.add_writer(raw_socket, f.set_result, None) + f.add_done_callback(lambda _: loop.remove_writer(raw_socket)) + await f + except BaseException: + raw_socket.close() + raise + else: + return ConnectedUNIXDatagramSocket(raw_socket) + else: + return UNIXDatagramSocket(raw_socket) + + @classmethod + async def getaddrinfo( + cls, + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, + ) -> Sequence[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], + ] + ]: + return await get_running_loop().getaddrinfo( + host, port, family=family, type=type, proto=proto, flags=flags + ) + + @classmethod + async def getnameinfo( + cls, sockaddr: IPSockAddrType, flags: int = 0 + ) -> tuple[str, str]: + return await get_running_loop().getnameinfo(sockaddr, flags) + + @classmethod + async def wait_readable(cls, obj: FileDescriptorLike) -> None: + try: + read_events = _read_events.get() + except LookupError: + read_events = {} + _read_events.set(read_events) + + fd = obj if isinstance(obj, int) else obj.fileno() + if read_events.get(fd): + raise BusyResourceError("reading from") + + loop = get_running_loop() + fut: asyncio.Future[bool] = loop.create_future() + + def cb() -> None: + try: + del read_events[fd] + except KeyError: + pass + else: + remove_reader(fd) + + try: + fut.set_result(True) + except asyncio.InvalidStateError: + pass + + try: + loop.add_reader(fd, cb) + except NotImplementedError: + from anyio._core._asyncio_selector_thread import get_selector + + selector = get_selector() + selector.add_reader(fd, cb) + remove_reader = selector.remove_reader + else: + remove_reader = loop.remove_reader + + read_events[fd] = fut + try: + success = await fut + finally: + try: + del read_events[fd] + except KeyError: + pass + else: + remove_reader(fd) + + if not success: + raise ClosedResourceError + + @classmethod + async def wait_writable(cls, obj: FileDescriptorLike) -> None: + try: + write_events = _write_events.get() + except LookupError: + write_events = {} + _write_events.set(write_events) + + fd = obj if isinstance(obj, int) else obj.fileno() + if write_events.get(fd): + raise BusyResourceError("writing to") + + loop = get_running_loop() + fut: asyncio.Future[bool] = loop.create_future() + + def cb() -> None: + try: + del write_events[fd] + except KeyError: + pass + else: + remove_writer(fd) + + try: + fut.set_result(True) + except asyncio.InvalidStateError: + pass + + try: + loop.add_writer(fd, cb) + except NotImplementedError: + from anyio._core._asyncio_selector_thread import get_selector + + selector = get_selector() + selector.add_writer(fd, cb) + remove_writer = selector.remove_writer + else: + remove_writer = loop.remove_writer + + write_events[fd] = fut + try: + success = await fut + finally: + try: + del write_events[fd] + except KeyError: + pass + else: + remove_writer(fd) + + if not success: + raise ClosedResourceError + + @classmethod + def notify_closing(cls, obj: FileDescriptorLike) -> None: + fd = obj if isinstance(obj, int) else obj.fileno() + loop = get_running_loop() + + try: + write_events = _write_events.get() + except LookupError: + pass + else: + try: + fut = write_events.pop(fd) + except KeyError: + pass + else: + try: + fut.set_result(False) + except asyncio.InvalidStateError: + pass + + try: + loop.remove_writer(fd) + except NotImplementedError: + from anyio._core._asyncio_selector_thread import get_selector + + get_selector().remove_writer(fd) + + try: + read_events = _read_events.get() + except LookupError: + pass + else: + try: + fut = read_events.pop(fd) + except KeyError: + pass + else: + try: + fut.set_result(False) + except asyncio.InvalidStateError: + pass + + try: + loop.remove_reader(fd) + except NotImplementedError: + from anyio._core._asyncio_selector_thread import get_selector + + get_selector().remove_reader(fd) + + @classmethod + async def wrap_listener_socket(cls, sock: socket.socket) -> SocketListener: + if hasattr(socket, "AF_UNIX") and sock.family == socket.AF_UNIX: + return UNIXSocketListener(sock) + + return TCPSocketListener(sock) + + @classmethod + async def wrap_stream_socket(cls, sock: socket.socket) -> SocketStream: + transport, protocol = await get_running_loop().create_connection( + StreamProtocol, sock=sock + ) + return SocketStream(transport, protocol) + + @classmethod + async def wrap_unix_stream_socket(cls, sock: socket.socket) -> UNIXSocketStream: + return UNIXSocketStream(sock) + + @classmethod + async def wrap_udp_socket(cls, sock: socket.socket) -> UDPSocket: + transport, protocol = await get_running_loop().create_datagram_endpoint( + DatagramProtocol, sock=sock + ) + return UDPSocket(transport, protocol) + + @classmethod + async def wrap_connected_udp_socket(cls, sock: socket.socket) -> ConnectedUDPSocket: + transport, protocol = await get_running_loop().create_datagram_endpoint( + DatagramProtocol, sock=sock + ) + return ConnectedUDPSocket(transport, protocol) + + @classmethod + async def wrap_unix_datagram_socket(cls, sock: socket.socket) -> UNIXDatagramSocket: + return UNIXDatagramSocket(sock) + + @classmethod + async def wrap_connected_unix_datagram_socket( + cls, sock: socket.socket + ) -> ConnectedUNIXDatagramSocket: + return ConnectedUNIXDatagramSocket(sock) + + @classmethod + def current_default_thread_limiter(cls) -> CapacityLimiter: + try: + return _default_thread_limiter.get() + except LookupError: + limiter = CapacityLimiter(40) + _default_thread_limiter.set(limiter) + return limiter + + @classmethod + def open_signal_receiver( + cls, *signals: Signals + ) -> AbstractContextManager[AsyncIterator[Signals]]: + return _SignalReceiver(signals) + + @classmethod + def get_current_task(cls) -> TaskInfo: + return AsyncIOTaskInfo(current_task()) # type: ignore[arg-type] + + @classmethod + def get_running_tasks(cls) -> Sequence[TaskInfo]: + return [AsyncIOTaskInfo(task) for task in all_tasks() if not task.done()] + + @classmethod + async def wait_all_tasks_blocked(cls) -> None: + await cls.checkpoint() + this_task = current_task() + while True: + for task in all_tasks(): + if task is this_task: + continue + + waiter = task._fut_waiter # type: ignore[attr-defined] + if waiter is None or waiter.done(): + await sleep(0.1) + break + else: + return + + @classmethod + def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: + return TestRunner(**options) + + +backend_class = AsyncIOBackend diff --git a/venv/lib/python3.12/site-packages/anyio/_backends/_trio.py b/venv/lib/python3.12/site-packages/anyio/_backends/_trio.py new file mode 100644 index 0000000..2e67786 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_backends/_trio.py @@ -0,0 +1,1473 @@ +from __future__ import annotations + +import array +import math +import os +import socket +import sys +import types +import weakref +from collections.abc import ( + AsyncGenerator, + AsyncIterator, + Awaitable, + Callable, + Collection, + Coroutine, + Iterable, + Sequence, +) +from contextlib import AbstractContextManager +from contextvars import Context +from dataclasses import dataclass +from functools import partial, wraps +from io import IOBase +from os import PathLike +from signal import Signals +from socket import AddressFamily, SocketKind +from types import TracebackType +from typing import ( + IO, + TYPE_CHECKING, + Any, + Generic, + Literal, + NoReturn, + ParamSpec, + TypeVar, + cast, + overload, +) + +import trio.from_thread +import trio.lowlevel +from outcome import Error, Outcome, Value +from trio.lowlevel import ( + current_root_task, + current_task, + notify_closing, + wait_readable, + wait_writable, +) +from trio.socket import SocketType as TrioSocketType +from trio.to_thread import run_sync + +from .. import ( + CapacityLimiterStatistics, + EventStatistics, + LockStatistics, + RunFinishedError, + TaskInfo, + WouldBlock, + abc, +) +from .._core._eventloop import claim_worker_thread +from .._core._exceptions import ( + BrokenResourceError, + BusyResourceError, + ClosedResourceError, + EndOfStream, +) +from .._core._sockets import convert_ipv6_sockaddr +from .._core._streams import create_memory_object_stream +from .._core._synchronization import ( + CapacityLimiter as BaseCapacityLimiter, +) +from .._core._synchronization import Event as BaseEvent +from .._core._synchronization import Lock as BaseLock +from .._core._synchronization import ( + ResourceGuard, + SemaphoreStatistics, +) +from .._core._synchronization import Semaphore as BaseSemaphore +from .._core._tasks import CancelScope as BaseCancelScope +from .._core._tasks import TaskHandle +from ..abc import IPSockAddrType, UDPPacketType, UNIXDatagramPacketType +from ..abc._eventloop import AsyncBackend, StrOrBytesPath +from ..abc._tasks import T_contra, call_for_coroutine, get_callable_name, get_coro_name + +if TYPE_CHECKING: + from _typeshed import FileDescriptorLike + + from ..streams.memory import MemoryObjectSendStream + +if sys.version_info < (3, 15): + from typing_extensions import sentinel + +if sys.version_info >= (3, 11): + from typing import Self, TypeVarTuple, Unpack +else: + from exceptiongroup import BaseExceptionGroup + from typing_extensions import Self, TypeVarTuple, Unpack + +T = TypeVar("T") +T_Retval = TypeVar("T_Retval") +T_co = TypeVar("T_co", covariant=True) +T_SockAddr = TypeVar("T_SockAddr", str, IPSockAddrType) +PosArgsT = TypeVarTuple("PosArgsT") +P = ParamSpec("P") + + +def ensure_returns_coro( + func: Callable[P, Awaitable[T_Retval]], +) -> Callable[P, Coroutine[Any, Any, T_Retval]]: + @wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, T_Retval]: + awaitable = func(*args, **kwargs) + # Check the common case first. + if isinstance(awaitable, Coroutine): + return awaitable + elif not isinstance(awaitable, Awaitable): + # The user violated the type annotations. Still, we should pass this on to + # Trio so it can raise with an appropriate message. + return awaitable + else: + + @wraps(func) + async def inner_wrapper() -> T_Retval: + return await awaitable + + return inner_wrapper() + + return wrapper + + +# +# Event loop +# + +RunVar = trio.lowlevel.RunVar + + +# +# Timeouts and cancellation +# + + +class CancelScope(BaseCancelScope): + __slots__ = ("__original",) + + def __new__( + cls, original: trio.CancelScope | None = None, **kwargs: object + ) -> Self: + return object.__new__(cls) + + def __init__(self, original: trio.CancelScope | None = None, **kwargs: Any) -> None: + self.__original = original or trio.CancelScope(**kwargs) + + def __enter__(self) -> Self: + self.__original.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + return self.__original.__exit__(exc_type, exc_val, exc_tb) + + def cancel(self, reason: str | None = None) -> None: + self.__original.cancel(reason) + + @property + def deadline(self) -> float: + return self.__original.deadline + + @deadline.setter + def deadline(self, value: float) -> None: + self.__original.deadline = value + + @property + def cancel_called(self) -> bool: + return self.__original.cancel_called + + @property + def cancelled_caught(self) -> bool: + return self.__original.cancelled_caught + + @property + def shield(self) -> bool: + return self.__original.shield + + @shield.setter + def shield(self, value: bool) -> None: + self.__original.shield = value + + +# +# Task groups +# + +empty_start_value = sentinel("empty_start_value") + + +class _TrioTaskStatus(abc.TaskStatus[T_contra], Generic[T_contra]): + early_start_value: T_contra | object = empty_start_value + real_task_status: trio.TaskStatus[T_contra | None] | None = None + + def started(self, value: T_contra | None = None) -> None: + if self.real_task_status is None: + if self.early_start_value is not empty_start_value: + raise RuntimeError("called 'started' twice on the same task status") + + self.early_start_value = value + else: + self.real_task_status.started(value) + + +class TaskGroup(abc.TaskGroup): + def __init__(self) -> None: + self._entered = False + self._active = False + self._nursery_manager = trio.open_nursery(strict_exception_groups=True) + self.cancel_scope = None # type: ignore[assignment] + + async def __aenter__(self) -> Self: + if self._entered: + raise RuntimeError("TaskGroup cannot be entered more than once") + + self._entered = True + self._active = True + self._nursery = await self._nursery_manager.__aenter__() + self.cancel_scope = CancelScope(self._nursery.cancel_scope) + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + try: + # trio.Nursery.__exit__ returns bool; .open_nursery has wrong type + return await self._nursery_manager.__aexit__(exc_type, exc_val, exc_tb) # type: ignore[return-value] + except BaseExceptionGroup as exc: + if not exc.split(trio.Cancelled)[1]: + raise trio.Cancelled._create() from exc + + raise + finally: + del exc_val, exc_tb + self._active = False + + def _check_active(self, coro: Coroutine | None = None) -> None: + if not self._active: + if coro is not None: + coro.close() + + raise RuntimeError( + "This task group is not active; no new tasks can be started." + ) + + def create_task( + self, + coro: Coroutine[Any, Any, T_co], + *, + name: object = None, + context: Context | None = None, + ) -> TaskHandle[T_co]: + if not isinstance(coro, Coroutine): + raise TypeError(f"expected a coroutine, got {coro.__class__.__qualname__}") + + self._check_active(coro) + final_name = get_coro_name(coro, name) + handle = TaskHandle(coro, final_name) + if context is not None: + context.run( + partial(self._nursery.start_soon, handle._run_coro, name=handle.name) + ) + else: + self._nursery.start_soon(handle._run_coro, name=handle.name) + + return handle + + async def start( + self, + func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], + *args: Unpack[PosArgsT], + name: object = None, + return_handle: Literal[False, True] = False, + ) -> Any: + handle: TaskHandle[T_co] + + async def run_coro_with_task_status( + *, task_status: trio.TaskStatus[Any] + ) -> None: + nonlocal handle + wrapper_task_status = _TrioTaskStatus() + coro = call_for_coroutine(func, args, task_status=wrapper_task_status) + if wrapper_task_status.early_start_value is not empty_start_value: + task_status.started(wrapper_task_status.early_start_value) + else: + wrapper_task_status.real_task_status = task_status + + handle = TaskHandle(coro, final_name) + await handle._run_coro() + + self._check_active() + final_name = get_callable_name(func, name) + start_value = await self._nursery.start( + run_coro_with_task_status, name=final_name + ) + if return_handle: + handle._start_value = start_value + return handle + else: + return start_value + + +# +# Subprocesses +# + + +@dataclass(eq=False) +class ReceiveStreamWrapper(abc.ByteReceiveStream): + _stream: trio.abc.ReceiveStream + + async def receive(self, max_bytes: int | None = None) -> bytes: + if max_bytes is not None and max_bytes < 1: + raise ValueError("max_bytes must be a positive integer") + + try: + data = await self._stream.receive_some(max_bytes) + except trio.ClosedResourceError as exc: + raise ClosedResourceError from exc.__cause__ + except trio.BrokenResourceError as exc: + raise BrokenResourceError from exc.__cause__ + + if data: + return bytes(data) + else: + raise EndOfStream + + async def aclose(self) -> None: + await self._stream.aclose() + + +@dataclass(eq=False) +class SendStreamWrapper(abc.ByteSendStream): + _stream: trio.abc.SendStream + + async def send(self, item: bytes) -> None: + try: + await self._stream.send_all(item) + except trio.ClosedResourceError as exc: + raise ClosedResourceError from exc.__cause__ + except trio.BrokenResourceError as exc: + raise BrokenResourceError from exc.__cause__ + + async def aclose(self) -> None: + await self._stream.aclose() + + +@dataclass(eq=False) +class Process(abc.Process): + _process: trio.Process + _stdin: abc.ByteSendStream | None + _stdout: abc.ByteReceiveStream | None + _stderr: abc.ByteReceiveStream | None + + async def aclose(self) -> None: + with CancelScope(shield=True): + if self._stdin: + await self._stdin.aclose() + if self._stdout: + await self._stdout.aclose() + if self._stderr: + await self._stderr.aclose() + + try: + await self.wait() + except BaseException: + self.kill() + with CancelScope(shield=True): + await self.wait() + raise + + async def wait(self) -> int: + return await self._process.wait() + + def terminate(self) -> None: + self._process.terminate() + + def kill(self) -> None: + self._process.kill() + + def send_signal(self, signal: Signals) -> None: + self._process.send_signal(signal) + + @property + def pid(self) -> int: + return self._process.pid + + @property + def returncode(self) -> int | None: + return self._process.returncode + + @property + def stdin(self) -> abc.ByteSendStream | None: + return self._stdin + + @property + def stdout(self) -> abc.ByteReceiveStream | None: + return self._stdout + + @property + def stderr(self) -> abc.ByteReceiveStream | None: + return self._stderr + + +class _ProcessPoolShutdownInstrument(trio.abc.Instrument): + def after_run(self) -> None: + super().after_run() + + +current_default_worker_process_limiter: trio.lowlevel.RunVar = RunVar( + "current_default_worker_process_limiter" +) + + +async def _shutdown_process_pool(workers: set[abc.Process]) -> None: + try: + await trio.sleep(math.inf) + except trio.Cancelled: + for process in workers: + if process.returncode is None: + process.kill() + + with CancelScope(shield=True): + for process in workers: + await process.aclose() + + +# +# Sockets and networking +# + + +class _TrioSocketMixin(Generic[T_SockAddr]): + def __init__(self, trio_socket: TrioSocketType) -> None: + self._trio_socket = trio_socket + self._closed = False + + def _check_closed(self) -> None: + if self._closed: + raise ClosedResourceError + if self._trio_socket.fileno() < 0: + raise BrokenResourceError + + @property + def _raw_socket(self) -> socket.socket: + return self._trio_socket._sock # type: ignore[attr-defined] + + async def aclose(self) -> None: + if self._trio_socket.fileno() >= 0: + self._closed = True + self._trio_socket.close() + + def _convert_socket_error(self, exc: BaseException) -> NoReturn: + if isinstance(exc, trio.ClosedResourceError): + raise ClosedResourceError from exc + elif self._trio_socket.fileno() < 0 and self._closed: + raise ClosedResourceError from None + elif isinstance(exc, OSError): + raise BrokenResourceError from exc + else: + raise exc + + +class SocketStream(_TrioSocketMixin, abc.SocketStream): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self, max_bytes: int = 65536) -> bytes: + if max_bytes < 1: + raise ValueError("max_bytes must be a positive integer") + + with self._receive_guard: + try: + data = await self._trio_socket.recv(max_bytes) + except BaseException as exc: + self._convert_socket_error(exc) + + if data: + return data + else: + raise EndOfStream + + async def send(self, item: bytes) -> None: + with self._send_guard: + view = memoryview(item) + while view: + try: + bytes_sent = await self._trio_socket.send(view) + except BaseException as exc: + self._convert_socket_error(exc) + + view = view[bytes_sent:] + + async def send_eof(self) -> None: + self._trio_socket.shutdown(socket.SHUT_WR) + + +class UNIXSocketStream(SocketStream, abc.UNIXSocketStream): + async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: + if not isinstance(msglen, int) or msglen < 0: + raise ValueError("msglen must be a non-negative integer") + if not isinstance(maxfds, int) or maxfds < 1: + raise ValueError("maxfds must be a positive integer") + + fds = array.array("i") + await trio.lowlevel.checkpoint() + with self._receive_guard: + while True: + try: + message, ancdata, _flags, _addr = await self._trio_socket.recvmsg( + msglen, socket.CMSG_LEN(maxfds * fds.itemsize) + ) + except BaseException as exc: + self._convert_socket_error(exc) + else: + if not message and not ancdata: + raise EndOfStream + + break + + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS: + raise RuntimeError( + f"Received unexpected ancillary data; message = {message!r}, " + f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}" + ) + + fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) + + return message, list(fds) + + async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: + if not message: + raise ValueError("message must not be empty") + if not fds: + raise ValueError("fds must not be empty") + + filenos: list[int] = [] + for fd in fds: + if isinstance(fd, int): + filenos.append(fd) + elif isinstance(fd, IOBase): + filenos.append(fd.fileno()) + + fdarray = array.array("i", filenos) + await trio.lowlevel.checkpoint() + with self._send_guard: + while True: + try: + await self._trio_socket.sendmsg( + [message], + [ + ( + socket.SOL_SOCKET, + socket.SCM_RIGHTS, + fdarray, + ) + ], + ) + break + except BaseException as exc: + self._convert_socket_error(exc) + + +class TCPSocketListener(_TrioSocketMixin, abc.SocketListener): + def __init__(self, raw_socket: socket.socket): + super().__init__(trio.socket.from_stdlib_socket(raw_socket)) + self._accept_guard = ResourceGuard("accepting connections from") + + async def accept(self) -> SocketStream: + with self._accept_guard: + try: + trio_socket, _addr = await self._trio_socket.accept() + except BaseException as exc: + self._convert_socket_error(exc) + + trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + return SocketStream(trio_socket) + + +class UNIXSocketListener(_TrioSocketMixin, abc.SocketListener): + def __init__(self, raw_socket: socket.socket): + super().__init__(trio.socket.from_stdlib_socket(raw_socket)) + self._accept_guard = ResourceGuard("accepting connections from") + + async def accept(self) -> UNIXSocketStream: + with self._accept_guard: + try: + trio_socket, _addr = await self._trio_socket.accept() + except BaseException as exc: + self._convert_socket_error(exc) + + return UNIXSocketStream(trio_socket) + + +class UDPSocket(_TrioSocketMixin[IPSockAddrType], abc.UDPSocket): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> tuple[bytes, IPSockAddrType]: + with self._receive_guard: + try: + data, addr = await self._trio_socket.recvfrom(65536) + return data, convert_ipv6_sockaddr(addr) + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: UDPPacketType) -> None: + with self._send_guard: + try: + await self._trio_socket.sendto(*item) + except BaseException as exc: + self._convert_socket_error(exc) + + +class ConnectedUDPSocket(_TrioSocketMixin[IPSockAddrType], abc.ConnectedUDPSocket): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> bytes: + with self._receive_guard: + try: + return await self._trio_socket.recv(65536) + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: bytes) -> None: + with self._send_guard: + try: + await self._trio_socket.send(item) + except BaseException as exc: + self._convert_socket_error(exc) + + +class UNIXDatagramSocket(_TrioSocketMixin[str], abc.UNIXDatagramSocket): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> UNIXDatagramPacketType: + with self._receive_guard: + try: + data, addr = await self._trio_socket.recvfrom(65536) + return data, addr + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: UNIXDatagramPacketType) -> None: + with self._send_guard: + try: + await self._trio_socket.sendto(*item) + except BaseException as exc: + self._convert_socket_error(exc) + + +class ConnectedUNIXDatagramSocket( + _TrioSocketMixin[str], abc.ConnectedUNIXDatagramSocket +): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> bytes: + with self._receive_guard: + try: + return await self._trio_socket.recv(65536) + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: bytes) -> None: + with self._send_guard: + try: + await self._trio_socket.send(item) + except BaseException as exc: + self._convert_socket_error(exc) + + +# +# Synchronization +# + + +class Event(BaseEvent): + __slots__ = ("__original",) + + def __new__(cls) -> Self: + return object.__new__(cls) + + def __init__(self) -> None: + self.__original = trio.Event() + + def is_set(self) -> bool: + return self.__original.is_set() + + async def wait(self) -> None: + return await self.__original.wait() + + def statistics(self) -> EventStatistics: + orig_statistics = self.__original.statistics() + return EventStatistics(tasks_waiting=orig_statistics.tasks_waiting) + + def set(self) -> None: + self.__original.set() + + +class Lock(BaseLock): + __slots__ = "__original", "_fast_acquire" + + def __new__(cls, *, fast_acquire: bool = False) -> Self: + return object.__new__(cls) + + def __init__(self, *, fast_acquire: bool = False) -> None: + self._fast_acquire = fast_acquire + self.__original = trio.Lock() + + @staticmethod + def _convert_runtime_error_msg(exc: RuntimeError) -> None: + if exc.args == ("attempt to re-acquire an already held Lock",): + exc.args = ("Attempted to acquire an already held Lock",) + + async def acquire(self) -> None: + if not self._fast_acquire: + try: + await self.__original.acquire() + except RuntimeError as exc: + self._convert_runtime_error_msg(exc) + raise + + return + + # This is the "fast path" where we don't let other tasks run + await trio.lowlevel.checkpoint_if_cancelled() + try: + self.__original.acquire_nowait() + except trio.WouldBlock: + await self.__original._lot.park() + except RuntimeError as exc: + self._convert_runtime_error_msg(exc) + raise + + def acquire_nowait(self) -> None: + try: + self.__original.acquire_nowait() + except trio.WouldBlock: + raise WouldBlock from None + except RuntimeError as exc: + self._convert_runtime_error_msg(exc) + raise + + def locked(self) -> bool: + return self.__original.locked() + + def release(self) -> None: + self.__original.release() + + def statistics(self) -> LockStatistics: + orig_statistics = self.__original.statistics() + owner = TrioTaskInfo(orig_statistics.owner) if orig_statistics.owner else None + return LockStatistics( + orig_statistics.locked, owner, orig_statistics.tasks_waiting + ) + + +class Semaphore(BaseSemaphore): + __slots__ = ("__original",) + + def __new__( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> Self: + return object.__new__(cls) + + def __init__( + self, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> None: + super().__init__(initial_value, max_value=max_value, fast_acquire=fast_acquire) + self.__original = trio.Semaphore(initial_value, max_value=max_value) + + async def acquire(self) -> None: + if not self._fast_acquire: + await self.__original.acquire() + return + + # This is the "fast path" where we don't let other tasks run + await trio.lowlevel.checkpoint_if_cancelled() + try: + self.__original.acquire_nowait() + except trio.WouldBlock: + await self.__original._lot.park() + + def acquire_nowait(self) -> None: + try: + self.__original.acquire_nowait() + except trio.WouldBlock: + raise WouldBlock from None + + @property + def max_value(self) -> int | None: + return self.__original.max_value + + @property + def value(self) -> int: + return self.__original.value + + def release(self) -> None: + self.__original.release() + + def statistics(self) -> SemaphoreStatistics: + orig_statistics = self.__original.statistics() + return SemaphoreStatistics(orig_statistics.tasks_waiting) + + +class CapacityLimiter(BaseCapacityLimiter): + __slots__ = ("__original",) + + def __new__( + cls, + total_tokens: float | None = None, + *, + original: trio.CapacityLimiter | None = None, + ) -> Self: + return object.__new__(cls) + + def __init__( + self, + total_tokens: float | None = None, + *, + original: trio.CapacityLimiter | None = None, + ) -> None: + if original is not None: + self.__original = original + else: + assert total_tokens is not None + self.__original = trio.CapacityLimiter(total_tokens) + + async def __aenter__(self) -> None: + return await self.__original.__aenter__() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.__original.__aexit__(exc_type, exc_val, exc_tb) + + @property + def total_tokens(self) -> float: + return self.__original.total_tokens + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + self.__original.total_tokens = value + + @property + def borrowed_tokens(self) -> int: + return self.__original.borrowed_tokens + + @property + def available_tokens(self) -> float: + return self.__original.available_tokens + + def acquire_nowait(self) -> None: + try: + self.__original.acquire_nowait() + except trio.WouldBlock: + raise WouldBlock from None + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + try: + self.__original.acquire_on_behalf_of_nowait(borrower) + except trio.WouldBlock: + raise WouldBlock from None + + async def acquire(self) -> None: + await self.__original.acquire() + + async def acquire_on_behalf_of(self, borrower: object) -> None: + await self.__original.acquire_on_behalf_of(borrower) + + def release(self) -> None: + return self.__original.release() + + def release_on_behalf_of(self, borrower: object) -> None: + return self.__original.release_on_behalf_of(borrower) + + def statistics(self) -> CapacityLimiterStatistics: + orig = self.__original.statistics() + return CapacityLimiterStatistics( + borrowed_tokens=orig.borrowed_tokens, + total_tokens=orig.total_tokens, + borrowers=tuple(orig.borrowers), + tasks_waiting=orig.tasks_waiting, + ) + + +_capacity_limiter_wrapper: trio.lowlevel.RunVar = RunVar("_capacity_limiter_wrapper") + + +# +# Signal handling +# + + +class _SignalReceiver: + _iterator: AsyncIterator[int] + + def __init__(self, signals: tuple[Signals, ...]): + self._signals = signals + + def __enter__(self) -> Self: + self._cm = trio.open_signal_receiver(*self._signals) + self._iterator = self._cm.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + return self._cm.__exit__(exc_type, exc_val, exc_tb) + + def __aiter__(self) -> _SignalReceiver: + return self + + async def __anext__(self) -> Signals: + signum = await self._iterator.__anext__() + return Signals(signum) + + +# +# Testing and debugging +# + + +class TestRunner(abc.TestRunner): + def __init__(self, **options: Any) -> None: + from queue import Queue + + self._call_queue: Queue[Callable[[], object]] = Queue() + self._send_stream: ( + MemoryObjectSendStream[tuple[Awaitable[Any], list[Outcome]]] | None + ) = None + self._options = options + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> None: + if self._send_stream: + self._send_stream.close() + while self._send_stream is not None: + self._call_queue.get()() + + def is_running(self) -> bool: + return trio.lowlevel.in_trio_task() + + async def _run_tests_and_fixtures(self) -> None: + self._send_stream, receive_stream = create_memory_object_stream[ + tuple[Awaitable[Any], list[Outcome]] + ](1) + with receive_stream: + async for awaitable, outcome_holder in receive_stream: + try: + retval = await awaitable + except BaseException as exc: + outcome_holder.append(Error(exc)) + else: + outcome_holder.append(Value(retval)) + + def _main_task_finished(self, outcome: object) -> None: + self._send_stream = None + + def _call_in_runner_task( + self, + func: Callable[P, Awaitable[T_Retval]], + /, + *args: P.args, + **kwargs: P.kwargs, + ) -> T_Retval: + if self._send_stream is None: + trio.lowlevel.start_guest_run( + self._run_tests_and_fixtures, + run_sync_soon_threadsafe=self._call_queue.put, + done_callback=self._main_task_finished, + **self._options, + ) + while self._send_stream is None: + self._call_queue.get()() + + outcome_holder: list[Outcome] = [] + self._send_stream.send_nowait((func(*args, **kwargs), outcome_holder)) + while not outcome_holder: + self._call_queue.get()() + + return outcome_holder[0].unwrap() + + def run_asyncgen_fixture( + self, + fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]], + kwargs: dict[str, Any], + ) -> Iterable[T_Retval]: + asyncgen = fixture_func(**kwargs) + fixturevalue: T_Retval = self._call_in_runner_task(asyncgen.asend, None) + + yield fixturevalue + + try: + self._call_in_runner_task(asyncgen.asend, None) + except StopAsyncIteration: + pass + else: + self._call_in_runner_task(asyncgen.aclose) + raise RuntimeError("Async generator fixture did not stop") + + def run_fixture( + self, + fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]], + kwargs: dict[str, Any], + ) -> T_Retval: + return self._call_in_runner_task(fixture_func, **kwargs) + + def run_test( + self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] + ) -> None: + self._call_in_runner_task(test_func, **kwargs) + + +class TrioTaskInfo(TaskInfo): + def __init__(self, task: trio.lowlevel.Task): + parent_id = None + if task.parent_nursery and task.parent_nursery.parent_task: + parent_id = id(task.parent_nursery.parent_task) + + super().__init__(id(task), parent_id, task.name, task.coro) + self._task = weakref.proxy(task) + + def has_pending_cancellation(self) -> bool: + try: + return self._task._cancel_status.effectively_cancelled + except ReferenceError: + # If the task is no longer around, it surely doesn't have a cancellation + # pending + return False + + +class TrioBackend(AsyncBackend): + @classmethod + def run( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + options: dict[str, Any], + ) -> T_Retval: + assert not kwargs, "unreachable, and not supported by Trio" + return trio.run(ensure_returns_coro(func), *args, **options) + + @classmethod + def current_token(cls) -> object: + return trio.lowlevel.current_trio_token() + + @classmethod + def current_time(cls) -> float: + return trio.current_time() + + @classmethod + def cancelled_exception_class(cls) -> type[BaseException]: + return trio.Cancelled + + @classmethod + async def checkpoint(cls) -> None: + await trio.lowlevel.checkpoint() + + @classmethod + async def checkpoint_if_cancelled(cls) -> None: + await trio.lowlevel.checkpoint_if_cancelled() + + @classmethod + async def cancel_shielded_checkpoint(cls) -> None: + await trio.lowlevel.cancel_shielded_checkpoint() + + @classmethod + async def sleep(cls, delay: float) -> None: + await trio.sleep(delay) + + @classmethod + def create_cancel_scope( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> BaseCancelScope: + return CancelScope(deadline=deadline, shield=shield) + + @classmethod + def current_effective_deadline(cls) -> float: + return trio.current_effective_deadline() + + @classmethod + def create_task_group(cls) -> abc.TaskGroup: + return TaskGroup() + + @classmethod + def create_event(cls) -> BaseEvent: + return Event() + + @classmethod + def create_lock(cls, *, fast_acquire: bool) -> Lock: + return Lock(fast_acquire=fast_acquire) + + @classmethod + def create_semaphore( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> BaseSemaphore: + return Semaphore(initial_value, max_value=max_value, fast_acquire=fast_acquire) + + @classmethod + def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter: + return CapacityLimiter(total_tokens) + + @classmethod + async def run_sync_in_worker_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + abandon_on_cancel: bool = False, + limiter: BaseCapacityLimiter | None = None, + ) -> T_Retval: + def wrapper() -> T_Retval: + with claim_worker_thread(TrioBackend, token): + return func(*args) + + token = TrioBackend.current_token() + return await run_sync( + wrapper, + abandon_on_cancel=abandon_on_cancel, + limiter=cast(trio.CapacityLimiter, limiter), + ) + + @classmethod + def check_cancelled(cls) -> None: + trio.from_thread.check_cancelled() + + @classmethod + def run_async_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_co: + trio_token = cast("trio.lowlevel.TrioToken | None", token) + try: + return trio.from_thread.run(func, *args, trio_token=trio_token) + except trio.RunFinishedError: + raise RunFinishedError from None + + @classmethod + def run_sync_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + trio_token = cast("trio.lowlevel.TrioToken | None", token) + try: + return trio.from_thread.run_sync(func, *args, trio_token=trio_token) + except trio.RunFinishedError: + raise RunFinishedError from None + + @classmethod + async def open_process( + cls, + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + **kwargs: Any, + ) -> Process: + def convert_item(item: StrOrBytesPath) -> str: + str_or_bytes = os.fspath(item) + if isinstance(str_or_bytes, str): + return str_or_bytes + else: + return os.fsdecode(str_or_bytes) + + if isinstance(command, (str, bytes, PathLike)): + process = await trio.lowlevel.open_process( + convert_item(command), + stdin=stdin, + stdout=stdout, + stderr=stderr, + shell=True, + **kwargs, + ) + else: + process = await trio.lowlevel.open_process( + [convert_item(item) for item in command], + stdin=stdin, + stdout=stdout, + stderr=stderr, + shell=False, + **kwargs, + ) + + stdin_stream = SendStreamWrapper(process.stdin) if process.stdin else None + stdout_stream = ReceiveStreamWrapper(process.stdout) if process.stdout else None + stderr_stream = ReceiveStreamWrapper(process.stderr) if process.stderr else None + return Process(process, stdin_stream, stdout_stream, stderr_stream) + + @classmethod + def setup_process_pool_exit_at_shutdown(cls, workers: set[abc.Process]) -> None: + trio.lowlevel.spawn_system_task(_shutdown_process_pool, workers) + + @classmethod + async def connect_tcp( + cls, host: str, port: int, local_address: IPSockAddrType | None = None + ) -> SocketStream: + family = socket.AF_INET6 if ":" in host else socket.AF_INET + trio_socket = trio.socket.socket(family) + trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + if local_address: + await trio_socket.bind(local_address) + + try: + await trio_socket.connect((host, port)) + except BaseException: + trio_socket.close() + raise + + return SocketStream(trio_socket) + + @classmethod + async def connect_unix(cls, path: str | bytes) -> abc.UNIXSocketStream: + trio_socket = trio.socket.socket(socket.AF_UNIX) + try: + await trio_socket.connect(path) + except BaseException: + trio_socket.close() + raise + + return UNIXSocketStream(trio_socket) + + @classmethod + def create_tcp_listener(cls, sock: socket.socket) -> abc.SocketListener: + return TCPSocketListener(sock) + + @classmethod + def create_unix_listener(cls, sock: socket.socket) -> abc.SocketListener: + return UNIXSocketListener(sock) + + @classmethod + async def create_udp_socket( + cls, + family: socket.AddressFamily, + local_address: IPSockAddrType | None, + remote_address: IPSockAddrType | None, + reuse_port: bool, + ) -> UDPSocket | ConnectedUDPSocket: + trio_socket = trio.socket.socket(family=family, type=socket.SOCK_DGRAM) + + if reuse_port: + trio_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + + if local_address: + await trio_socket.bind(local_address) + + if remote_address: + await trio_socket.connect(remote_address) + return ConnectedUDPSocket(trio_socket) + else: + return UDPSocket(trio_socket) + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket.socket, remote_path: None + ) -> abc.UNIXDatagramSocket: ... + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket.socket, remote_path: str | bytes + ) -> abc.ConnectedUNIXDatagramSocket: ... + + @classmethod + async def create_unix_datagram_socket( + cls, raw_socket: socket.socket, remote_path: str | bytes | None + ) -> abc.UNIXDatagramSocket | abc.ConnectedUNIXDatagramSocket: + trio_socket = trio.socket.from_stdlib_socket(raw_socket) + + if remote_path: + await trio_socket.connect(remote_path) + return ConnectedUNIXDatagramSocket(trio_socket) + else: + return UNIXDatagramSocket(trio_socket) + + @classmethod + async def getaddrinfo( + cls, + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, + ) -> Sequence[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], + ] + ]: + return await trio.socket.getaddrinfo(host, port, family, type, proto, flags) + + @classmethod + async def getnameinfo( + cls, sockaddr: IPSockAddrType, flags: int = 0 + ) -> tuple[str, str]: + return await trio.socket.getnameinfo(sockaddr, flags) + + @classmethod + async def wait_readable(cls, obj: FileDescriptorLike) -> None: + try: + await wait_readable(obj) + except trio.ClosedResourceError as exc: + raise ClosedResourceError().with_traceback(exc.__traceback__) from None + except trio.BusyResourceError: + raise BusyResourceError("reading from") from None + + @classmethod + async def wait_writable(cls, obj: FileDescriptorLike) -> None: + try: + await wait_writable(obj) + except trio.ClosedResourceError as exc: + raise ClosedResourceError().with_traceback(exc.__traceback__) from None + except trio.BusyResourceError: + raise BusyResourceError("writing to") from None + + @classmethod + def notify_closing(cls, obj: FileDescriptorLike) -> None: + notify_closing(obj) + + @classmethod + async def wrap_listener_socket(cls, sock: socket.socket) -> abc.SocketListener: + if hasattr(socket, "AF_UNIX") and sock.family == socket.AF_UNIX: + return UNIXSocketListener(sock) + + return TCPSocketListener(sock) + + @classmethod + async def wrap_stream_socket(cls, sock: socket.socket) -> SocketStream: + trio_sock = trio.socket.from_stdlib_socket(sock) + return SocketStream(trio_sock) + + @classmethod + async def wrap_unix_stream_socket(cls, sock: socket.socket) -> UNIXSocketStream: + trio_sock = trio.socket.from_stdlib_socket(sock) + return UNIXSocketStream(trio_sock) + + @classmethod + async def wrap_udp_socket(cls, sock: socket.socket) -> UDPSocket: + trio_sock = trio.socket.from_stdlib_socket(sock) + return UDPSocket(trio_sock) + + @classmethod + async def wrap_connected_udp_socket(cls, sock: socket.socket) -> ConnectedUDPSocket: + trio_sock = trio.socket.from_stdlib_socket(sock) + return ConnectedUDPSocket(trio_sock) + + @classmethod + async def wrap_unix_datagram_socket(cls, sock: socket.socket) -> UNIXDatagramSocket: + trio_sock = trio.socket.from_stdlib_socket(sock) + return UNIXDatagramSocket(trio_sock) + + @classmethod + async def wrap_connected_unix_datagram_socket( + cls, sock: socket.socket + ) -> ConnectedUNIXDatagramSocket: + trio_sock = trio.socket.from_stdlib_socket(sock) + return ConnectedUNIXDatagramSocket(trio_sock) + + @classmethod + def current_default_thread_limiter(cls) -> CapacityLimiter: + try: + return _capacity_limiter_wrapper.get() + except LookupError: + limiter = CapacityLimiter( + original=trio.to_thread.current_default_thread_limiter() + ) + _capacity_limiter_wrapper.set(limiter) + return limiter + + @classmethod + def open_signal_receiver( + cls, *signals: Signals + ) -> AbstractContextManager[AsyncIterator[Signals]]: + return _SignalReceiver(signals) + + @classmethod + def get_current_task(cls) -> TaskInfo: + task = current_task() + return TrioTaskInfo(task) + + @classmethod + def get_running_tasks(cls) -> Sequence[TaskInfo]: + root_task = current_root_task() + assert root_task + task_infos = [TrioTaskInfo(root_task)] + nurseries = root_task.child_nurseries + while nurseries: + new_nurseries: list[trio.Nursery] = [] + for nursery in nurseries: + for task in nursery.child_tasks: + task_infos.append(TrioTaskInfo(task)) + new_nurseries.extend(task.child_nurseries) + + nurseries = new_nurseries + + return task_infos + + @classmethod + async def wait_all_tasks_blocked(cls) -> None: + from trio.testing import wait_all_tasks_blocked + + await wait_all_tasks_blocked() + + @classmethod + def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: + return TestRunner(**options) + + +backend_class = TrioBackend diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__init__.py b/venv/lib/python3.12/site-packages/anyio/_core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..22abc60 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_asyncio_selector_thread.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_asyncio_selector_thread.cpython-312.pyc new file mode 100644 index 0000000..c52ed01 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_asyncio_selector_thread.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_concurrency_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_concurrency_utils.cpython-312.pyc new file mode 100644 index 0000000..4a8c38f Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_concurrency_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_contextmanagers.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_contextmanagers.cpython-312.pyc new file mode 100644 index 0000000..432a560 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_contextmanagers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_eventloop.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_eventloop.cpython-312.pyc new file mode 100644 index 0000000..775c8d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_eventloop.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_exceptions.cpython-312.pyc new file mode 100644 index 0000000..bb2aac6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_fileio.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_fileio.cpython-312.pyc new file mode 100644 index 0000000..4d479a4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_fileio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_futures.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_futures.cpython-312.pyc new file mode 100644 index 0000000..9809a7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_futures.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_resources.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_resources.cpython-312.pyc new file mode 100644 index 0000000..59cb98d Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_resources.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_signals.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_signals.cpython-312.pyc new file mode 100644 index 0000000..9659440 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_signals.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_sockets.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_sockets.cpython-312.pyc new file mode 100644 index 0000000..49afcb1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_sockets.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_streams.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_streams.cpython-312.pyc new file mode 100644 index 0000000..26b4d65 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_streams.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_subprocesses.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_subprocesses.cpython-312.pyc new file mode 100644 index 0000000..b1ada5d Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_subprocesses.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_synchronization.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_synchronization.cpython-312.pyc new file mode 100644 index 0000000..63fee77 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_synchronization.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_tasks.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_tasks.cpython-312.pyc new file mode 100644 index 0000000..7fe9525 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_tasks.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_tempfile.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_tempfile.cpython-312.pyc new file mode 100644 index 0000000..6fde8ab Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_tempfile.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_testing.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_testing.cpython-312.pyc new file mode 100644 index 0000000..a314cd1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_testing.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_typedattr.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_typedattr.cpython-312.pyc new file mode 100644 index 0000000..ac76f30 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/_core/__pycache__/_typedattr.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_asyncio_selector_thread.py b/venv/lib/python3.12/site-packages/anyio/_core/_asyncio_selector_thread.py new file mode 100644 index 0000000..9f35bae --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_asyncio_selector_thread.py @@ -0,0 +1,167 @@ +from __future__ import annotations + +import asyncio +import socket +import threading +from collections.abc import Callable +from selectors import EVENT_READ, EVENT_WRITE, DefaultSelector +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from _typeshed import FileDescriptorLike + +_selector_lock = threading.Lock() +_selector: Selector | None = None + + +class Selector: + def __init__(self) -> None: + self._thread = threading.Thread(target=self.run, name="AnyIO socket selector") + self._selector = DefaultSelector() + self._send, self._receive = socket.socketpair() + self._send.setblocking(False) + self._receive.setblocking(False) + # This somewhat reduces the amount of memory wasted queueing up data + # for wakeups. With these settings, maximum number of 1-byte sends + # before getting BlockingIOError: + # Linux 4.8: 6 + # macOS (darwin 15.5): 1 + # Windows 10: 525347 + # Windows you're weird. (And on Windows setting SNDBUF to 0 makes send + # blocking, even on non-blocking sockets, so don't do that.) + self._receive.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1) + self._send.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1) + # On Windows this is a TCP socket so this might matter. On other + # platforms this fails b/c AF_UNIX sockets aren't actually TCP. + try: + self._send.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + except OSError: + pass + + self._selector.register(self._receive, EVENT_READ) + self._closed = False + + def start(self) -> None: + self._thread.start() + threading._register_atexit(self._stop) # type: ignore[attr-defined] + + def _stop(self) -> None: + global _selector + self._closed = True + self._notify_self() + self._send.close() + self._thread.join() + self._selector.unregister(self._receive) + self._receive.close() + self._selector.close() + _selector = None + assert not self._selector.get_map(), ( + "selector still has registered file descriptors after shutdown" + ) + + def _notify_self(self) -> None: + try: + self._send.send(b"\x00") + except BlockingIOError: + pass + + def add_reader(self, fd: FileDescriptorLike, callback: Callable[[], Any]) -> None: + loop = asyncio.get_running_loop() + try: + key = self._selector.get_key(fd) + except KeyError: + self._selector.register(fd, EVENT_READ, {EVENT_READ: (loop, callback)}) + else: + if EVENT_READ in key.data: + raise ValueError( + "this file descriptor is already registered for reading" + ) + + key.data[EVENT_READ] = loop, callback + self._selector.modify(fd, key.events | EVENT_READ, key.data) + + self._notify_self() + + def add_writer(self, fd: FileDescriptorLike, callback: Callable[[], Any]) -> None: + loop = asyncio.get_running_loop() + try: + key = self._selector.get_key(fd) + except KeyError: + self._selector.register(fd, EVENT_WRITE, {EVENT_WRITE: (loop, callback)}) + else: + if EVENT_WRITE in key.data: + raise ValueError( + "this file descriptor is already registered for writing" + ) + + key.data[EVENT_WRITE] = loop, callback + self._selector.modify(fd, key.events | EVENT_WRITE, key.data) + + self._notify_self() + + def remove_reader(self, fd: FileDescriptorLike) -> bool: + try: + key = self._selector.get_key(fd) + except KeyError: + return False + + if new_events := key.events ^ EVENT_READ: + del key.data[EVENT_READ] + self._selector.modify(fd, new_events, key.data) + else: + self._selector.unregister(fd) + + return True + + def remove_writer(self, fd: FileDescriptorLike) -> bool: + try: + key = self._selector.get_key(fd) + except KeyError: + return False + + if new_events := key.events ^ EVENT_WRITE: + del key.data[EVENT_WRITE] + self._selector.modify(fd, new_events, key.data) + else: + self._selector.unregister(fd) + + return True + + def run(self) -> None: + while not self._closed: + for key, events in self._selector.select(): + if key.fileobj is self._receive: + try: + while self._receive.recv(4096): + pass + except BlockingIOError: + pass + + continue + + if events & EVENT_READ: + loop, callback = key.data[EVENT_READ] + self.remove_reader(key.fd) + try: + loop.call_soon_threadsafe(callback) + except RuntimeError: + pass # the loop was already closed + + if events & EVENT_WRITE: + loop, callback = key.data[EVENT_WRITE] + self.remove_writer(key.fd) + try: + loop.call_soon_threadsafe(callback) + except RuntimeError: + pass # the loop was already closed + + +def get_selector() -> Selector: + global _selector + + with _selector_lock: + if _selector is None: + _selector = Selector() + _selector.start() + + return _selector diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_concurrency_utils.py b/venv/lib/python3.12/site-packages/anyio/_core/_concurrency_utils.py new file mode 100644 index 0000000..f66229a --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_concurrency_utils.py @@ -0,0 +1,156 @@ +from __future__ import annotations + +from inspect import iscoroutine + +__all__ = ("amap", "as_completed", "gather") + +from collections.abc import AsyncGenerator, Awaitable, Callable, Coroutine, Iterable +from contextlib import asynccontextmanager +from typing import TYPE_CHECKING, Any, TypeVar, overload + +from ..abc._tasks import get_coro_name +from ._exceptions import BrokenResourceError +from ._streams import create_memory_object_stream +from ._tasks import TaskHandle, create_task_group + +if TYPE_CHECKING: + from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream + +R = TypeVar("R") +S = TypeVar("S") +T = TypeVar("T") +U = TypeVar("U") +V = TypeVar("V") +W = TypeVar("W") + + +@overload +async def gather( + coro1: Coroutine[Any, Any, R], coro2: Coroutine[Any, Any, S], / +) -> tuple[R, S]: ... + + +@overload +async def gather( + coro1: Coroutine[Any, Any, R], + coro2: Coroutine[Any, Any, S], + coro3: Coroutine[Any, Any, T], + /, +) -> tuple[R, S, T]: ... + + +@overload +async def gather( + coro1: Coroutine[Any, Any, R], + coro2: Coroutine[Any, Any, S], + coro3: Coroutine[Any, Any, T], + coro4: Coroutine[Any, Any, U], + /, +) -> tuple[R, S, T, U]: ... + + +@overload +async def gather( + coro1: Coroutine[Any, Any, R], + coro2: Coroutine[Any, Any, S], + coro3: Coroutine[Any, Any, T], + coro4: Coroutine[Any, Any, U], + coro5: Coroutine[Any, Any, V], + /, +) -> tuple[R, S, T, U, V]: ... + + +@overload +async def gather( + coro1: Coroutine[Any, Any, R], + coro2: Coroutine[Any, Any, S], + coro3: Coroutine[Any, Any, T], + coro4: Coroutine[Any, Any, U], + coro5: Coroutine[Any, Any, V], + coro6: Coroutine[Any, Any, W], + /, +) -> tuple[R, S, T, U, V, W]: ... + + +# handle arbitrary length if awaitables are all of the same type +@overload +async def gather(*coros: Coroutine[Any, Any, R]) -> tuple[R, ...]: ... + + +async def gather(*coros: Coroutine[Any, Any, Any]) -> tuple[Any, ...]: + """ + Run coroutines concurrently in a task group. The order of result values corresponds + to the order of coroutines passed. + + :param coros: coroutine objects to run as tasks + :return: task results for each argument in the same order as they were passed + + """ + handles: list[TaskHandle[Any]] = [] + async with create_task_group() as tg: + handles.extend(tg.create_task(coro) for coro in coros) + + return tuple(h.return_value for h in handles) + + +@asynccontextmanager +async def as_completed( + *awaitables: Awaitable[R], +) -> AsyncGenerator[MemoryObjectReceiveStream[TaskHandle[R]]]: + """ + Run awaitable objects concurrently in a task group, returning an iterator which can + be used to get finished task handles in the order they complete. + + :param awaitables: awaitable objects to run as tasks + :return: MemoryObjectReceiveStream for iterating over task handles as tasks complete. + + """ + if not awaitables: + raise ValueError("as_completed() takes at least one awaitable") + + send, recv = create_memory_object_stream[TaskHandle[R]](len(awaitables)) + task_handles: list[TaskHandle[R]] = [] + + async def runner( + awaitable: Awaitable[R], + index: int, + _send: MemoryObjectSendStream[TaskHandle[R]], + ) -> R: + async with _send: + try: + return await awaitable + finally: + try: + _send.send_nowait(task_handles[index]) + except BrokenResourceError: + pass + + async with recv, create_task_group() as tg: + async with send: + for i, awaitable in enumerate(awaitables): + name = get_coro_name(awaitable) if iscoroutine(awaitable) else None + task_handles.append( + tg.start_soon(runner, awaitable, i, send.clone(), name=name) + ) + try: + yield recv + finally: + tg.cancel_scope.cancel() + + +async def amap( + func: Callable[[T], Coroutine[Any, Any, R]], args: Iterable[T] +) -> list[R]: + """ + Run the given coroutine function concurrently for multiple argument values. + + :param func: a coroutine function that takes a single argument + :param args: a sequence of argument values to pass to ``func`` + :return: task results for each argument in the same order as they were passed + + """ + handles: list[TaskHandle[R]] = [] + async with create_task_group() as tg: + handles.extend(tg.start_soon(func, arg) for arg in args) + + return [h.return_value for h in handles] diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_contextmanagers.py b/venv/lib/python3.12/site-packages/anyio/_core/_contextmanagers.py new file mode 100644 index 0000000..302f32b --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_contextmanagers.py @@ -0,0 +1,200 @@ +from __future__ import annotations + +from abc import abstractmethod +from contextlib import AbstractAsyncContextManager, AbstractContextManager +from inspect import isasyncgen, iscoroutine, isgenerator +from types import TracebackType +from typing import Protocol, TypeVar, cast, final + +_T_co = TypeVar("_T_co", covariant=True) +_ExitT_co = TypeVar("_ExitT_co", covariant=True, bound="bool | None") + + +class _SupportsCtxMgr(Protocol[_T_co, _ExitT_co]): + def __contextmanager__(self) -> AbstractContextManager[_T_co, _ExitT_co]: ... + + +class _SupportsAsyncCtxMgr(Protocol[_T_co, _ExitT_co]): + def __asynccontextmanager__( + self, + ) -> AbstractAsyncContextManager[_T_co, _ExitT_co]: ... + + +class ContextManagerMixin: + """ + Mixin class providing context manager functionality via a generator-based + implementation. + + This class allows you to implement a context manager via :meth:`__contextmanager__` + which should return a generator. The mechanics are meant to mirror those of + :func:`@contextmanager `. + + .. note:: Classes using this mix-in are not reentrant as context managers, meaning + that once you enter it, you can't re-enter before first exiting it. + + .. seealso:: :doc:`contextmanagers` + """ + + __cm: AbstractContextManager[object, bool | None] | None = None + + @final + def __enter__(self: _SupportsCtxMgr[_T_co, bool | None]) -> _T_co: + # Needed for mypy to assume self still has the __cm member + assert isinstance(self, ContextManagerMixin) + if self.__cm is not None: + raise RuntimeError( + f"this {self.__class__.__qualname__} has already been entered" + ) + + cm = self.__contextmanager__() + if not isinstance(cm, AbstractContextManager): + if isgenerator(cm): + raise TypeError( + "__contextmanager__() returned a generator object instead of " + "a context manager. Did you forget to add the @contextmanager " + "decorator?" + ) + + raise TypeError( + f"__contextmanager__() did not return a context manager object, " + f"but {cm.__class__!r}" + ) + + if cm is self: + raise TypeError( + f"{self.__class__.__qualname__}.__contextmanager__() returned " + f"self. Did you forget to add the @contextmanager decorator and a " + f"'yield' statement?" + ) + + value = cm.__enter__() + self.__cm = cm + return value + + @final + def __exit__( + self: _SupportsCtxMgr[object, _ExitT_co], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> _ExitT_co: + # Needed for mypy to assume self still has the __cm member + assert isinstance(self, ContextManagerMixin) + if self.__cm is None: + raise RuntimeError( + f"this {self.__class__.__qualname__} has not been entered yet" + ) + + # Prevent circular references + cm = self.__cm + del self.__cm + + return cast(_ExitT_co, cm.__exit__(exc_type, exc_val, exc_tb)) + + @abstractmethod + def __contextmanager__(self) -> AbstractContextManager[object, bool | None]: + """ + Implement your context manager logic here. + + This method **must** be decorated with + :func:`@contextmanager `. + + .. note:: Remember that the ``yield`` will raise any exception raised in the + enclosed context block, so use a ``finally:`` block to clean up resources! + + :return: a context manager object + """ + + +class AsyncContextManagerMixin: + """ + Mixin class providing async context manager functionality via a generator-based + implementation. + + This class allows you to implement a context manager via + :meth:`__asynccontextmanager__`. The mechanics are meant to mirror those of + :func:`@asynccontextmanager `. + + .. note:: Classes using this mix-in are not reentrant as context managers, meaning + that once you enter it, you can't re-enter before first exiting it. + + .. seealso:: :doc:`contextmanagers` + """ + + __cm: AbstractAsyncContextManager[object, bool | None] | None = None + + @final + async def __aenter__(self: _SupportsAsyncCtxMgr[_T_co, bool | None]) -> _T_co: + # Needed for mypy to assume self still has the __cm member + assert isinstance(self, AsyncContextManagerMixin) + if self.__cm is not None: + raise RuntimeError( + f"this {self.__class__.__qualname__} has already been entered" + ) + + cm = self.__asynccontextmanager__() + if not isinstance(cm, AbstractAsyncContextManager): + if isasyncgen(cm): + raise TypeError( + "__asynccontextmanager__() returned an async generator instead of " + "an async context manager. Did you forget to add the " + "@asynccontextmanager decorator?" + ) + elif iscoroutine(cm): + cm.close() + raise TypeError( + "__asynccontextmanager__() returned a coroutine object instead of " + "an async context manager. Did you forget to add the " + "@asynccontextmanager decorator and a 'yield' statement?" + ) + + raise TypeError( + f"__asynccontextmanager__() did not return an async context manager, " + f"but {cm.__class__!r}" + ) + + if cm is self: + raise TypeError( + f"{self.__class__.__qualname__}.__asynccontextmanager__() returned " + f"self. Did you forget to add the @asynccontextmanager decorator and a " + f"'yield' statement?" + ) + + value = await cm.__aenter__() + self.__cm = cm + return value + + @final + async def __aexit__( + self: _SupportsAsyncCtxMgr[object, _ExitT_co], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> _ExitT_co: + assert isinstance(self, AsyncContextManagerMixin) + if self.__cm is None: + raise RuntimeError( + f"this {self.__class__.__qualname__} has not been entered yet" + ) + + # Prevent circular references + cm = self.__cm + del self.__cm + + return cast(_ExitT_co, await cm.__aexit__(exc_type, exc_val, exc_tb)) + + @abstractmethod + def __asynccontextmanager__( + self, + ) -> AbstractAsyncContextManager[object, bool | None]: + """ + Implement your async context manager logic here. + + This method **must** be decorated with + :func:`@asynccontextmanager `. + + .. note:: Remember that the ``yield`` will raise any exception raised in the + enclosed context block, so use a ``finally:`` block to clean up resources! + + :return: an async context manager object + """ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_eventloop.py b/venv/lib/python3.12/site-packages/anyio/_core/_eventloop.py new file mode 100644 index 0000000..a3e2ab1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_eventloop.py @@ -0,0 +1,240 @@ +from __future__ import annotations + +import math +import sys +import threading +from collections.abc import Awaitable, Callable, Generator +from contextlib import contextmanager +from contextvars import Token +from importlib import import_module +from typing import TYPE_CHECKING, Any, TypeVar + +from ._exceptions import NoEventLoopError + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +sniffio: Any +try: + import sniffio +except ModuleNotFoundError: + sniffio = None + +if TYPE_CHECKING: + from ..abc import AsyncBackend + +# This must be updated when new backends are introduced +BACKENDS = "asyncio", "trio" + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + +threadlocals = threading.local() +loaded_backends: dict[str, type[AsyncBackend]] = {} + + +def run( + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + backend: str = "asyncio", + backend_options: dict[str, Any] | None = None, +) -> T_Retval: + """ + Run the given coroutine function in an asynchronous event loop. + + The current thread must not be already running an event loop. + + :param func: a coroutine function + :param args: positional arguments to ``func`` + :param backend: name of the asynchronous event loop implementation – currently + either ``asyncio`` or ``trio`` + :param backend_options: keyword arguments to call the backend ``run()`` + implementation with (documented :ref:`here `) + :return: the return value of the coroutine function + :raises RuntimeError: if an asynchronous event loop is already running in this + thread + :raises LookupError: if the named backend is not found + + """ + if asynclib_name := current_async_library(): + raise RuntimeError(f"Already running {asynclib_name} in this thread") + + try: + async_backend = get_async_backend(backend) + except ImportError as exc: + if backend in BACKENDS: + raise LookupError( + f"Backend {backend!r} is not available. " + f"Install it with: pip install anyio[{backend}]" + ) from exc + + raise LookupError(f"No such backend: {backend}") from exc + + token = None + if asynclib_name is None: + # Since we're in control of the event loop, we can cache the name of the async + # library + token = set_current_async_library(backend) + + try: + backend_options = backend_options or {} + return async_backend.run(func, args, {}, backend_options) + finally: + reset_current_async_library(token) + + +async def sleep(delay: float) -> None: + """ + Pause the current task for the specified duration. + + :param delay: the duration, in seconds + + """ + return await get_async_backend().sleep(delay) + + +async def sleep_forever() -> None: + """ + Pause the current task until it's cancelled. + + This is a shortcut for ``sleep(math.inf)``. + + .. versionadded:: 3.1 + + """ + await sleep(math.inf) + + +async def sleep_until(deadline: float) -> None: + """ + Pause the current task until the given time. + + :param deadline: the absolute time to wake up at (according to the internal + monotonic clock of the event loop) + + .. versionadded:: 3.1 + + """ + now = current_time() + await sleep(max(deadline - now, 0)) + + +def current_time() -> float: + """ + Return the current value of the event loop's internal clock. + + :return: the clock value (seconds) + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().current_time() + + +def get_all_backends() -> tuple[str, ...]: + """Return a tuple of the names of all built-in backends.""" + return BACKENDS + + +def get_available_backends() -> tuple[str, ...]: + """ + Test for the availability of built-in backends. + + :return a tuple of the built-in backend names that were successfully imported + + .. versionadded:: 4.12 + + """ + available_backends: list[str] = [] + for backend_name in get_all_backends(): + try: + get_async_backend(backend_name) + except ImportError: + continue + + available_backends.append(backend_name) + + return tuple(available_backends) + + +def get_cancelled_exc_class() -> type[BaseException]: + """ + Return the current async library's cancellation exception class. + + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().cancelled_exception_class() + + +# +# Private API +# + + +@contextmanager +def claim_worker_thread( + backend_class: type[AsyncBackend], token: object +) -> Generator[Any, None, None]: + from ..lowlevel import EventLoopToken + + threadlocals.current_token = EventLoopToken(backend_class, token) + try: + yield + finally: + del threadlocals.current_token + + +def get_async_backend(asynclib_name: str | None = None) -> type[AsyncBackend]: + if asynclib_name is None: + asynclib_name = current_async_library() + if not asynclib_name: + raise NoEventLoopError( + f"Not currently running on any asynchronous event loop. " + f"Available async backends: {', '.join(get_all_backends())}" + ) + + # We use our own dict instead of sys.modules to get the already imported back-end + # class because the appropriate modules in sys.modules could potentially be only + # partially initialized + try: + return loaded_backends[asynclib_name] + except KeyError: + module = import_module(f"anyio._backends._{asynclib_name}") + loaded_backends[asynclib_name] = module.backend_class + return module.backend_class + + +def current_async_library() -> str | None: + if sniffio is None: + # If sniffio is not installed, we assume we're either running asyncio or nothing + import asyncio + + try: + asyncio.get_running_loop() + return "asyncio" + except RuntimeError: + pass + else: + try: + return sniffio.current_async_library() + except sniffio.AsyncLibraryNotFoundError: + pass + + return None + + +def set_current_async_library(asynclib_name: str | None) -> Token | None: + # no-op if sniffio is not installed + if sniffio is None: + return None + + return sniffio.current_async_library_cvar.set(asynclib_name) + + +def reset_current_async_library(token: Token | None) -> None: + if token is not None: + sniffio.current_async_library_cvar.reset(token) diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_exceptions.py b/venv/lib/python3.12/site-packages/anyio/_core/_exceptions.py new file mode 100644 index 0000000..7d9165f --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_exceptions.py @@ -0,0 +1,205 @@ +from __future__ import annotations + +import sys +from collections.abc import Generator +from textwrap import dedent +from typing import Any + +if sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup + + +class BrokenResourceError(Exception): + """ + Raised when trying to use a resource that has been rendered unusable due to external + causes (e.g. a send stream whose peer has disconnected). + """ + + +class BrokenWorkerProcess(Exception): + """ + Raised by :meth:`~anyio.to_process.run_sync` if the worker process terminates abruptly or + otherwise misbehaves. + """ + + +class BrokenWorkerInterpreter(Exception): + """ + Raised by :meth:`~anyio.to_interpreter.run_sync` if an unexpected exception is + raised in the subinterpreter. + """ + + def __init__(self, excinfo: Any): + # This was adapted from concurrent.futures.interpreter.ExecutionFailed + msg = excinfo.formatted + if not msg: + if excinfo.type and excinfo.msg: + msg = f"{excinfo.type.__name__}: {excinfo.msg}" + else: + msg = excinfo.type.__name__ or excinfo.msg + + super().__init__(msg) + self.excinfo = excinfo + + def __str__(self) -> str: + try: + formatted = self.excinfo.errdisplay + except Exception: + return super().__str__() + else: + return dedent( + f""" + {super().__str__()} + + Uncaught in the interpreter: + + {formatted} + """.strip() + ) + + +class BusyResourceError(Exception): + """ + Raised when two tasks are trying to read from or write to the same resource + concurrently. + """ + + def __init__(self, action: str): + super().__init__(f"Another task is already {action} this resource") + + +class ClosedResourceError(Exception): + """Raised when trying to use a resource that has been closed.""" + + +class ConnectionFailed(OSError): + """ + Raised when a connection attempt fails. + + .. note:: This class inherits from :exc:`OSError` for backwards compatibility. + """ + + +def iterate_exceptions( + exception: BaseException, +) -> Generator[BaseException, None, None]: + if isinstance(exception, BaseExceptionGroup): + for exc in exception.exceptions: + yield from iterate_exceptions(exc) + else: + yield exception + + +class DelimiterNotFound(Exception): + """ + Raised during + :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the + maximum number of bytes has been read without the delimiter being found. + """ + + def __init__(self, max_bytes: int) -> None: + super().__init__( + f"The delimiter was not found among the first {max_bytes} bytes" + ) + + +class EndOfStream(Exception): + """ + Raised when trying to read from a stream that has been closed from the other end. + """ + + +class IncompleteRead(Exception): + """ + Raised during + :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_exactly` or + :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the + connection is closed before the requested amount of bytes has been read. + """ + + def __init__(self) -> None: + super().__init__( + "The stream was closed before the read operation could be completed" + ) + + +class TypedAttributeLookupError(LookupError): + """ + Raised by :meth:`~anyio.TypedAttributeProvider.extra` when the given typed attribute + is not found and no default value has been given. + """ + + +class WouldBlock(Exception): + """Raised by ``X_nowait`` functions if ``X()`` would block.""" + + +class NoEventLoopError(RuntimeError): + """ + Raised by several functions that require an event loop to be running in the current + thread when there is no running event loop. + + This is also raised by :func:`.from_thread.run` and :func:`.from_thread.run_sync` + if not calling from an AnyIO worker thread, and no ``token`` was passed. + """ + + +class RunFinishedError(RuntimeError): + """ + Raised by :func:`.from_thread.run` and :func:`.from_thread.run_sync` if the event + loop associated with the explicitly passed token has already finished. + """ + + def __init__(self) -> None: + super().__init__( + "The event loop associated with the given token has already finished" + ) + + +class TaskFailed(Exception): + """ + Raised when awaiting on, or attempting to access the return value of, a + :class:`.TaskHandle` that raised an exception. + """ + + +class TaskCancelled(TaskFailed): + """ + Raised when awaiting on, or attempting to access the return value of, a + :class:`.TaskHandle` that was cancelled. + """ + + +class TaskNotFinished(Exception): + """ + Raised when attempting to access the return value or exception of a + :class:`.TaskHandle` that is still pending completion. + """ + + +class FutureFailed(Exception): + """ + Raised when awaiting on, or attempting to access the return value of, a + :class:`.Future` that raised an exception. + """ + + +class FutureCancelled(FutureFailed): + """ + Raised when attempting to access the return value or exception of a + :class:`.Future` that was cancelled. + """ + + +class FutureNotFinished(Exception): + """ + Raised when attempting to access the return value or exception of a + :class:`.Future` that is still pending completion. + """ + + +class FutureAlreadyFinished(Exception): + """ + Raised when attempting set a result of or await a + :class:`.Future` that has already completed. + """ diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_fileio.py b/venv/lib/python3.12/site-packages/anyio/_core/_fileio.py new file mode 100644 index 0000000..498eef4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_fileio.py @@ -0,0 +1,1045 @@ +from __future__ import annotations + +import os +import pathlib +import sys +from collections.abc import ( + AsyncIterator, + Callable, + Iterable, + Iterator, + Sequence, +) +from dataclasses import dataclass +from functools import partial +from os import PathLike +from typing import ( + IO, + TYPE_CHECKING, + Any, + AnyStr, + ClassVar, + Final, + Generic, + TypeVar, + overload, +) + +from .. import to_thread +from ..abc import AsyncResource +from ._synchronization import CapacityLimiter + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self + +if sys.version_info >= (3, 14): + from pathlib.types import PathInfo + +if TYPE_CHECKING: + from types import ModuleType + + from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer +else: + ReadableBuffer = OpenBinaryMode = OpenTextMode = WriteableBuffer = object + + +T = TypeVar("T", bound="Path") + + +class AsyncFile(AsyncResource, Generic[AnyStr]): + """ + An asynchronous file object. + + This class wraps a standard file object and provides async friendly versions of the + following blocking methods (where available on the original file object): + + * read + * read1 + * readline + * readlines + * readinto + * readinto1 + * write + * writelines + * truncate + * seek + * tell + * flush + + All other methods are directly passed through. + + This class supports the asynchronous context manager protocol which closes the + underlying file at the end of the context block. + + This class also supports asynchronous iteration:: + + async with await open_file(...) as f: + async for line in f: + print(line) + """ + + def __init__( + self, fp: IO[AnyStr], *, limiter: CapacityLimiter | None = None + ) -> None: + if limiter is not None and not isinstance(limiter, CapacityLimiter): + raise TypeError( + f"limiter must be a CapacityLimiter or None, not " + f"{limiter.__class__.__name__}" + ) + + self._fp: Any = fp + self._limiter = limiter + + def __getattr__(self, name: str) -> object: + return getattr(self._fp, name) + + @property + def limiter(self) -> CapacityLimiter | None: + """The capacity limiter used by this file object, if not the global limiter.""" + return self._limiter + + @property + def wrapped(self) -> IO[AnyStr]: + """The wrapped file object.""" + return self._fp + + async def __aiter__(self) -> AsyncIterator[AnyStr]: + while True: + line = await self.readline() + if line: + yield line + else: + break + + async def aclose(self) -> None: + return await to_thread.run_sync(self._fp.close, limiter=self._limiter) + + async def read(self, size: int = -1) -> AnyStr: + return await to_thread.run_sync(self._fp.read, size, limiter=self._limiter) + + async def read1(self: AsyncFile[bytes], size: int = -1) -> bytes: + return await to_thread.run_sync(self._fp.read1, size, limiter=self._limiter) + + async def readline(self) -> AnyStr: + return await to_thread.run_sync(self._fp.readline, limiter=self._limiter) + + async def readlines(self) -> list[AnyStr]: + return await to_thread.run_sync(self._fp.readlines, limiter=self._limiter) + + async def readinto(self: AsyncFile[bytes], b: WriteableBuffer) -> int: + return await to_thread.run_sync(self._fp.readinto, b, limiter=self._limiter) + + async def readinto1(self: AsyncFile[bytes], b: WriteableBuffer) -> int: + return await to_thread.run_sync(self._fp.readinto1, b, limiter=self._limiter) + + @overload + async def write(self: AsyncFile[bytes], b: ReadableBuffer) -> int: ... + + @overload + async def write(self: AsyncFile[str], b: str) -> int: ... + + async def write(self, b: ReadableBuffer | str) -> int: + return await to_thread.run_sync(self._fp.write, b, limiter=self._limiter) + + @overload + async def writelines( + self: AsyncFile[bytes], lines: Iterable[ReadableBuffer] + ) -> None: ... + + @overload + async def writelines(self: AsyncFile[str], lines: Iterable[str]) -> None: ... + + async def writelines(self, lines: Iterable[ReadableBuffer] | Iterable[str]) -> None: + return await to_thread.run_sync( + self._fp.writelines, lines, limiter=self._limiter + ) + + async def truncate(self, size: int | None = None) -> int: + return await to_thread.run_sync(self._fp.truncate, size, limiter=self._limiter) + + async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int: + return await to_thread.run_sync( + self._fp.seek, offset, whence, limiter=self._limiter + ) + + async def tell(self) -> int: + return await to_thread.run_sync(self._fp.tell, limiter=self._limiter) + + async def flush(self) -> None: + return await to_thread.run_sync(self._fp.flush, limiter=self._limiter) + + +@overload +async def open_file( + file: str | PathLike[str] | int, + mode: OpenBinaryMode, + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] | None = ..., + *, + limiter: CapacityLimiter | None = ..., +) -> AsyncFile[bytes]: ... + + +@overload +async def open_file( + file: str | PathLike[str] | int, + mode: OpenTextMode = ..., + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] | None = ..., + *, + limiter: CapacityLimiter | None = ..., +) -> AsyncFile[str]: ... + + +async def open_file( + file: str | PathLike[str] | int, + mode: str = "r", + buffering: int = -1, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + closefd: bool = True, + opener: Callable[[str, int], int] | None = None, + *, + limiter: CapacityLimiter | None = None, +) -> AsyncFile[Any]: + """ + Open a file asynchronously. + + Except for ``limiter``, the arguments are exactly the same as for the builtin :func:`open`. + + :param limiter: an optional capacity limiter to use with the file + instead of the default one + :return: an asynchronous file object + + .. versionchanged:: 4.14.0 + Added the ``limiter`` keyword argument. + + """ + fp = await to_thread.run_sync( + open, + file, + mode, + buffering, + encoding, + errors, + newline, + closefd, + opener, + limiter=limiter, + ) + return AsyncFile(fp, limiter=limiter) + + +def wrap_file( + file: IO[AnyStr], *, limiter: CapacityLimiter | None = None +) -> AsyncFile[AnyStr]: + """ + Wrap an existing file as an asynchronous file. + + :param file: an existing file-like object + :param limiter: an optional capacity limiter to use with the file + instead of the default one + :return: an asynchronous file object + + .. versionchanged:: 4.14.0 + Added the ``limiter`` keyword argument. + + """ + return AsyncFile(file, limiter=limiter) + + +@dataclass(eq=False) +class _PathIterator(AsyncIterator[T]): + iterator: Iterator[PathLike[str]] + limiter: CapacityLimiter | None + # This was added to ensure that iterating over a subclass of Path yields instances + # of that subclass rather than the base Path class. + path_cls: type[T] + + async def __anext__(self) -> T: + nextval = await to_thread.run_sync( + next, self.iterator, None, abandon_on_cancel=True, limiter=self.limiter + ) + if nextval is None: + raise StopAsyncIteration from None + + return self.path_cls(nextval, limiter=self.limiter) + + +class Path: + """ + An asynchronous version of :class:`pathlib.Path`. + + This class cannot be substituted for :class:`pathlib.Path` or + :class:`pathlib.PurePath`, but it is compatible with the :class:`os.PathLike` + interface. + + It implements the Python 3.10 version of :class:`pathlib.Path` interface, except for + the deprecated :meth:`~pathlib.Path.link_to` method. + + Some methods may be unavailable or have limited functionality, based on the Python + version: + + * :meth:`~pathlib.Path.copy` (available on Python 3.14 or later) + * :meth:`~pathlib.Path.copy_into` (available on Python 3.14 or later) + * :meth:`~pathlib.Path.from_uri` (available on Python 3.13 or later) + * :meth:`~pathlib.PurePath.full_match` (available on Python 3.13 or later) + * :attr:`~pathlib.Path.info` (available on Python 3.14 or later) + * :meth:`~pathlib.Path.is_junction` (available on Python 3.12 or later) + * :meth:`~pathlib.PurePath.match` (the ``case_sensitive`` parameter is only + available on Python 3.13 or later) + * :meth:`~pathlib.Path.move` (available on Python 3.14 or later) + * :meth:`~pathlib.Path.move_into` (available on Python 3.14 or later) + * :meth:`~pathlib.PurePath.relative_to` (the ``walk_up`` parameter is only available + on Python 3.12 or later) + * :meth:`~pathlib.Path.walk` (available on Python 3.12 or later) + + Any methods that do disk I/O need to be awaited on. These methods are: + + * :meth:`~pathlib.Path.absolute` + * :meth:`~pathlib.Path.chmod` + * :meth:`~pathlib.Path.cwd` + * :meth:`~pathlib.Path.exists` + * :meth:`~pathlib.Path.expanduser` + * :meth:`~pathlib.Path.group` + * :meth:`~pathlib.Path.hardlink_to` + * :meth:`~pathlib.Path.home` + * :meth:`~pathlib.Path.is_block_device` + * :meth:`~pathlib.Path.is_char_device` + * :meth:`~pathlib.Path.is_dir` + * :meth:`~pathlib.Path.is_fifo` + * :meth:`~pathlib.Path.is_file` + * :meth:`~pathlib.Path.is_junction` + * :meth:`~pathlib.Path.is_mount` + * :meth:`~pathlib.Path.is_socket` + * :meth:`~pathlib.Path.is_symlink` + * :meth:`~pathlib.Path.lchmod` + * :meth:`~pathlib.Path.lstat` + * :meth:`~pathlib.Path.mkdir` + * :meth:`~pathlib.Path.open` + * :meth:`~pathlib.Path.owner` + * :meth:`~pathlib.Path.read_bytes` + * :meth:`~pathlib.Path.read_text` + * :meth:`~pathlib.Path.readlink` + * :meth:`~pathlib.Path.rename` + * :meth:`~pathlib.Path.replace` + * :meth:`~pathlib.Path.resolve` + * :meth:`~pathlib.Path.rmdir` + * :meth:`~pathlib.Path.samefile` + * :meth:`~pathlib.Path.stat` + * :meth:`~pathlib.Path.symlink_to` + * :meth:`~pathlib.Path.touch` + * :meth:`~pathlib.Path.unlink` + * :meth:`~pathlib.Path.walk` + * :meth:`~pathlib.Path.write_bytes` + * :meth:`~pathlib.Path.write_text` + + Additionally, the following methods return an async iterator yielding + :class:`~.Path` objects: + + * :meth:`~pathlib.Path.glob` + * :meth:`~pathlib.Path.iterdir` + * :meth:`~pathlib.Path.rglob` + + .. versionchanged:: 4.14.0 + Added the ``limiter`` keyword argument. + """ + + __slots__ = "__weakref__", "_limiter", "_path" + + __weakref__: Any + + def __init__( + self, *args: str | PathLike[str], limiter: CapacityLimiter | None = None + ) -> None: + if limiter is not None and not isinstance(limiter, CapacityLimiter): + raise TypeError( + f"limiter must be a CapacityLimiter or None, not " + f"{limiter.__class__.__name__}" + ) + + self._path: Final[pathlib.Path] = pathlib.Path(*args) + self._limiter = limiter + + def __fspath__(self) -> str: + return self._path.__fspath__() + + if sys.version_info >= (3, 15): + + def __vfspath__(self) -> str: + return self._path.__vfspath__() + + def __str__(self) -> str: + return self._path.__str__() + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.as_posix()!r})" + + def __bytes__(self) -> bytes: + return self._path.__bytes__() + + def __hash__(self) -> int: + return self._path.__hash__() + + def __eq__(self, other: object) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__eq__(target) + + def __lt__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__lt__(target) + + def __le__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__le__(target) + + def __gt__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__gt__(target) + + def __ge__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__ge__(target) + + def __truediv__(self, other: str | PathLike[str]) -> Self: + return type(self)(self._path / other, limiter=self._limiter) + + def __rtruediv__(self, other: str | PathLike[str]) -> Self: + return type(self)(other, limiter=self._limiter) / self + + @property + def limiter(self) -> CapacityLimiter | None: + """The capacity limiter used by this path, if not the global limiter.""" + return self._limiter + + @property + def parts(self) -> tuple[str, ...]: + return self._path.parts + + @property + def drive(self) -> str: + return self._path.drive + + @property + def root(self) -> str: + return self._path.root + + @property + def anchor(self) -> str: + return self._path.anchor + + @property + def parents(self) -> Sequence[Self]: + return tuple(type(self)(p, limiter=self._limiter) for p in self._path.parents) + + @property + def parent(self) -> Self: + return type(self)(self._path.parent, limiter=self._limiter) + + @property + def name(self) -> str: + return self._path.name + + @property + def suffix(self) -> str: + return self._path.suffix + + @property + def suffixes(self) -> list[str]: + return self._path.suffixes + + @property + def stem(self) -> str: + return self._path.stem + + async def absolute(self) -> Self: + path = await to_thread.run_sync(self._path.absolute, limiter=self._limiter) + return type(self)(path, limiter=self._limiter) + + def as_posix(self) -> str: + return self._path.as_posix() + + def as_uri(self) -> str: + return self._path.as_uri() + + if sys.version_info >= (3, 13): + parser: ClassVar[ModuleType] = pathlib.Path.parser + + @classmethod + def from_uri(cls, uri: str, *, limiter: CapacityLimiter | None = None) -> Self: + return cls(pathlib.Path.from_uri(uri), limiter=limiter) + + def full_match( + self, path_pattern: str, *, case_sensitive: bool | None = None + ) -> bool: + return self._path.full_match(path_pattern, case_sensitive=case_sensitive) + + def match( + self, path_pattern: str, *, case_sensitive: bool | None = None + ) -> bool: + return self._path.match(path_pattern, case_sensitive=case_sensitive) + else: + + def match(self, path_pattern: str) -> bool: + return self._path.match(path_pattern) + + if sys.version_info >= (3, 14): + + @property + def info(self) -> PathInfo: + return self._path.info + + async def copy( + self, + target: str | os.PathLike[str], + *, + follow_symlinks: bool = True, + preserve_metadata: bool = False, + ) -> Self: + func = partial( + self._path.copy, + follow_symlinks=follow_symlinks, + preserve_metadata=preserve_metadata, + ) + return type(self)( + await to_thread.run_sync( + func, pathlib.Path(target), limiter=self._limiter + ), + limiter=self._limiter, + ) + + async def copy_into( + self, + target_dir: str | os.PathLike[str], + *, + follow_symlinks: bool = True, + preserve_metadata: bool = False, + ) -> Self: + func = partial( + self._path.copy_into, + follow_symlinks=follow_symlinks, + preserve_metadata=preserve_metadata, + ) + return type(self)( + await to_thread.run_sync( + func, pathlib.Path(target_dir), limiter=self._limiter + ), + limiter=self._limiter, + ) + + async def move(self, target: str | os.PathLike[str]) -> Self: + # Upstream does not handle anyio.Path properly as a PathLike + target = pathlib.Path(target) + return type(self)( + await to_thread.run_sync( + self._path.move, target, limiter=self._limiter + ), + limiter=self._limiter, + ) + + async def move_into( + self, + target_dir: str | os.PathLike[str], + ) -> Self: + return type(self)( + await to_thread.run_sync( + self._path.move_into, target_dir, limiter=self._limiter + ), + limiter=self._limiter, + ) + + def is_relative_to(self, other: str | PathLike[str]) -> bool: + try: + self.relative_to(other) + return True + except ValueError: + return False + + async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None: + func = partial(os.chmod, follow_symlinks=follow_symlinks) + return await to_thread.run_sync(func, self._path, mode, limiter=self._limiter) + + @classmethod + async def cwd(cls, *, limiter: CapacityLimiter | None = None) -> Self: + path = await to_thread.run_sync(pathlib.Path.cwd, limiter=limiter) + return cls(path, limiter=limiter) + + if sys.version_info >= (3, 12): + + async def exists(self, *, follow_symlinks: bool = True) -> bool: + return await to_thread.run_sync( + partial(self._path.exists, follow_symlinks=follow_symlinks), + abandon_on_cancel=True, + limiter=self._limiter, + ) + + else: + + async def exists(self) -> bool: + return await to_thread.run_sync( + self._path.exists, abandon_on_cancel=True, limiter=self._limiter + ) + + async def expanduser(self) -> Self: + return type(self)( + await to_thread.run_sync( + self._path.expanduser, abandon_on_cancel=True, limiter=self._limiter + ), + limiter=self._limiter, + ) + + if sys.version_info < (3, 12): + # Python 3.11 and earlier + def glob(self, pattern: str) -> AsyncIterator[Self]: + gen = self._path.glob(pattern) + return _PathIterator(gen, self._limiter, type(self)) + elif (3, 12) <= sys.version_info < (3, 13): + # changed in Python 3.12: + # - The case_sensitive parameter was added. + def glob( + self, + pattern: str, + *, + case_sensitive: bool | None = None, + ) -> AsyncIterator[Self]: + gen = self._path.glob(pattern, case_sensitive=case_sensitive) + return _PathIterator(gen, self._limiter, type(self)) + elif sys.version_info >= (3, 13): + # Changed in Python 3.13: + # - The recurse_symlinks parameter was added. + # - The pattern parameter accepts a path-like object. + def glob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block + self, + pattern: str | PathLike[str], + *, + case_sensitive: bool | None = None, + recurse_symlinks: bool = False, + ) -> AsyncIterator[Self]: + gen = self._path.glob( + pattern, # type: ignore[arg-type] + case_sensitive=case_sensitive, + recurse_symlinks=recurse_symlinks, + ) + return _PathIterator(gen, self._limiter, type(self)) + + if sys.version_info >= (3, 13): + + async def group(self, *, follow_symlinks: bool = True) -> str: + return await to_thread.run_sync( + partial(self._path.group, follow_symlinks=follow_symlinks), + abandon_on_cancel=True, + limiter=self._limiter, + ) + + else: + + async def group(self) -> str: + return await to_thread.run_sync( + self._path.group, abandon_on_cancel=True, limiter=self._limiter + ) + + async def hardlink_to( + self, target: str | bytes | PathLike[str] | PathLike[bytes] + ) -> None: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(os.link, target, self, limiter=self._limiter) + + @classmethod + async def home(cls, *, limiter: CapacityLimiter | None = None) -> Self: + home_path = await to_thread.run_sync(pathlib.Path.home, limiter=limiter) + return cls(home_path, limiter=limiter) + + def is_absolute(self) -> bool: + return self._path.is_absolute() + + async def is_block_device(self) -> bool: + return await to_thread.run_sync( + self._path.is_block_device, abandon_on_cancel=True, limiter=self._limiter + ) + + async def is_char_device(self) -> bool: + return await to_thread.run_sync( + self._path.is_char_device, abandon_on_cancel=True, limiter=self._limiter + ) + + if sys.version_info >= (3, 13): + + async def is_dir(self, *, follow_symlinks: bool = True) -> bool: + return await to_thread.run_sync( + partial(self._path.is_dir, follow_symlinks=follow_symlinks), + abandon_on_cancel=True, + limiter=self._limiter, + ) + + else: + + async def is_dir(self) -> bool: + return await to_thread.run_sync( + self._path.is_dir, abandon_on_cancel=True, limiter=self._limiter + ) + + async def is_fifo(self) -> bool: + return await to_thread.run_sync( + self._path.is_fifo, abandon_on_cancel=True, limiter=self._limiter + ) + + if sys.version_info >= (3, 13): + + async def is_file(self, *, follow_symlinks: bool = True) -> bool: + return await to_thread.run_sync( + partial(self._path.is_file, follow_symlinks=follow_symlinks), + abandon_on_cancel=True, + limiter=self._limiter, + ) + + else: + + async def is_file(self) -> bool: + return await to_thread.run_sync( + self._path.is_file, abandon_on_cancel=True, limiter=self._limiter + ) + + if sys.version_info >= (3, 12): + + async def is_junction(self) -> bool: + return await to_thread.run_sync( + self._path.is_junction, limiter=self._limiter + ) + + async def is_mount(self) -> bool: + return await to_thread.run_sync( + os.path.ismount, self._path, abandon_on_cancel=True, limiter=self._limiter + ) + + if sys.version_info < (3, 15): + + def is_reserved(self) -> bool: + return self._path.is_reserved() + + async def is_socket(self) -> bool: + return await to_thread.run_sync( + self._path.is_socket, abandon_on_cancel=True, limiter=self._limiter + ) + + async def is_symlink(self) -> bool: + return await to_thread.run_sync( + self._path.is_symlink, abandon_on_cancel=True, limiter=self._limiter + ) + + async def iterdir(self) -> AsyncIterator[Self]: + gen = ( + self._path.iterdir() + if sys.version_info < (3, 13) + else await to_thread.run_sync( + self._path.iterdir, abandon_on_cancel=True, limiter=self._limiter + ) + ) + async for path in _PathIterator(gen, self._limiter, type(self)): + yield path + + def joinpath(self, *args: str | PathLike[str]) -> Self: + return type(self)(self._path.joinpath(*args), limiter=self._limiter) + + async def lchmod(self, mode: int) -> None: + await to_thread.run_sync(self._path.lchmod, mode, limiter=self._limiter) + + async def lstat(self) -> os.stat_result: + return await to_thread.run_sync( + self._path.lstat, abandon_on_cancel=True, limiter=self._limiter + ) + + async def mkdir( + self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False + ) -> None: + await to_thread.run_sync( + self._path.mkdir, mode, parents, exist_ok, limiter=self._limiter + ) + + @overload + async def open( + self, + mode: OpenBinaryMode, + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + ) -> AsyncFile[bytes]: ... + + @overload + async def open( + self, + mode: OpenTextMode = ..., + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + ) -> AsyncFile[str]: ... + + async def open( + self, + mode: str = "r", + buffering: int = -1, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + ) -> AsyncFile[Any]: + fp = await to_thread.run_sync( + self._path.open, + mode, + buffering, + encoding, + errors, + newline, + limiter=self._limiter, + ) + return AsyncFile(fp, limiter=self._limiter) + + if sys.version_info >= (3, 13): + + async def owner(self, *, follow_symlinks: bool = True) -> str: + return await to_thread.run_sync( + partial(self._path.owner, follow_symlinks=follow_symlinks), + abandon_on_cancel=True, + limiter=self._limiter, + ) + + else: + + async def owner(self) -> str: + return await to_thread.run_sync( + self._path.owner, abandon_on_cancel=True, limiter=self._limiter + ) + + async def read_bytes(self) -> bytes: + return await to_thread.run_sync(self._path.read_bytes, limiter=self._limiter) + + if sys.version_info >= (3, 13): + + async def read_text( + self, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + ) -> str: + return await to_thread.run_sync( + self._path.read_text, + encoding, + errors, + newline, + limiter=self._limiter, + ) + + else: + + async def read_text( + self, encoding: str | None = None, errors: str | None = None + ) -> str: + return await to_thread.run_sync( + self._path.read_text, encoding, errors, limiter=self._limiter + ) + + if sys.version_info >= (3, 12): + + def relative_to( + self, *other: str | PathLike[str], walk_up: bool = False + ) -> Self: + # relative_to() should work with any PathLike but it doesn't + others = [pathlib.Path(other) for other in other] + return type(self)( + self._path.relative_to(*others, walk_up=walk_up), limiter=self._limiter + ) + + else: + + def relative_to(self, *other: str | PathLike[str]) -> Self: + return type(self)(self._path.relative_to(*other), limiter=self._limiter) + + async def readlink(self) -> Self: + target = await to_thread.run_sync( + os.readlink, self._path, limiter=self._limiter + ) + return type(self)(target, limiter=self._limiter) + + async def rename(self, target: str | pathlib.PurePath | Path) -> Self: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(self._path.rename, target, limiter=self._limiter) + return type(self)(target, limiter=self._limiter) + + async def replace(self, target: str | pathlib.PurePath | Path) -> Self: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(self._path.replace, target, limiter=self._limiter) + return type(self)(target, limiter=self._limiter) + + async def resolve(self, strict: bool = False) -> Self: + func = partial(self._path.resolve, strict=strict) + return type(self)( + await to_thread.run_sync( + func, abandon_on_cancel=True, limiter=self._limiter + ), + limiter=self._limiter, + ) + + if sys.version_info < (3, 12): + # Pre Python 3.12 + def rglob(self, pattern: str) -> AsyncIterator[Self]: + gen = self._path.rglob(pattern) + return _PathIterator(gen, self._limiter, type(self)) + elif (3, 12) <= sys.version_info < (3, 13): + # Changed in Python 3.12: + # - The case_sensitive parameter was added. + def rglob( + self, pattern: str, *, case_sensitive: bool | None = None + ) -> AsyncIterator[Self]: + gen = self._path.rglob(pattern, case_sensitive=case_sensitive) + return _PathIterator(gen, self._limiter, type(self)) + elif sys.version_info >= (3, 13): + # Changed in Python 3.13: + # - The recurse_symlinks parameter was added. + # - The pattern parameter accepts a path-like object. + def rglob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block + self, + pattern: str | PathLike[str], + *, + case_sensitive: bool | None = None, + recurse_symlinks: bool = False, + ) -> AsyncIterator[Self]: + gen = self._path.rglob( + pattern, # type: ignore[arg-type] + case_sensitive=case_sensitive, + recurse_symlinks=recurse_symlinks, + ) + return _PathIterator(gen, self._limiter, type(self)) + + async def rmdir(self) -> None: + await to_thread.run_sync(self._path.rmdir, limiter=self._limiter) + + async def samefile(self, other_path: str | PathLike[str]) -> bool: + if isinstance(other_path, Path): + other_path = other_path._path + + return await to_thread.run_sync( + self._path.samefile, + other_path, + abandon_on_cancel=True, + limiter=self._limiter, + ) + + async def stat(self, *, follow_symlinks: bool = True) -> os.stat_result: + func = partial(os.stat, follow_symlinks=follow_symlinks) + return await to_thread.run_sync( + func, self._path, abandon_on_cancel=True, limiter=self._limiter + ) + + async def symlink_to( + self, + target: str | bytes | PathLike[str] | PathLike[bytes], + target_is_directory: bool = False, + ) -> None: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync( + self._path.symlink_to, target, target_is_directory, limiter=self._limiter + ) + + async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None: + await to_thread.run_sync( + self._path.touch, mode, exist_ok, limiter=self._limiter + ) + + async def unlink(self, missing_ok: bool = False) -> None: + try: + await to_thread.run_sync(self._path.unlink, limiter=self._limiter) + except FileNotFoundError: + if not missing_ok: + raise + + if sys.version_info >= (3, 12): + + async def walk( + self, + top_down: bool = True, + on_error: Callable[[OSError], object] | None = None, + follow_symlinks: bool = False, + ) -> AsyncIterator[tuple[Self, list[str], list[str]]]: + def get_next_value() -> tuple[pathlib.Path, list[str], list[str]] | None: + try: + return next(gen) + except StopIteration: + return None + + gen = self._path.walk(top_down, on_error, follow_symlinks) + while True: + value = await to_thread.run_sync(get_next_value, limiter=self._limiter) + if value is None: + return + + root, dirs, paths = value + yield type(self)(root, limiter=self._limiter), dirs, paths + + def with_name(self, name: str) -> Self: + return type(self)(self._path.with_name(name), limiter=self._limiter) + + if sys.version_info < (3, 13): + # Backport pathlib's Python>=3.13 behavior for empty stems on paths with non-empty suffixes. + # See: https://github.com/python/cpython/pull/114612 + def with_stem(self, stem: str) -> Self: + suffix = self._path.suffix + if not suffix: + return self.with_name(stem) + elif not stem: + # If the suffix is non-empty, we can't make the stem empty. + raise ValueError(f"{self!r} has a non-empty suffix") + else: + return self.with_name(stem + suffix) + else: + + def with_stem(self, stem: str) -> Self: + return type(self)(self._path.with_stem(stem), limiter=self._limiter) + + def with_suffix(self, suffix: str) -> Self: + return type(self)(self._path.with_suffix(suffix), limiter=self._limiter) + + def with_segments(self, *pathsegments: str | PathLike[str]) -> Self: + return type(self)(*pathsegments, limiter=self._limiter) + + async def write_bytes(self, data: ReadableBuffer) -> int: + return await to_thread.run_sync( + self._path.write_bytes, data, limiter=self._limiter + ) + + async def write_text( + self, + data: str, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + ) -> int: + return await to_thread.run_sync( + self._path.write_text, + data, + encoding, + errors, + newline, + limiter=self._limiter, + ) + + +PathLike.register(Path) diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_futures.py b/venv/lib/python3.12/site-packages/anyio/_core/_futures.py new file mode 100644 index 0000000..7730c53 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_futures.py @@ -0,0 +1,191 @@ +from __future__ import annotations + +from collections.abc import Generator +from enum import Enum, auto +from typing import Any, Generic, TypeVar + +from ._exceptions import ( + FutureAlreadyFinished, + FutureCancelled, + FutureFailed, + FutureNotFinished, +) +from ._synchronization import Event + +T = TypeVar("T") + + +class Future(Generic[T]): + """ + An awaitable object that works similar to a :class:`asyncio.Future` but + with similar characteristics to a :class:`.TaskHandle`. + """ + + class Status(Enum): + """ + The status of a future handle. + + .. attribute:: PENDING + + The future has not finished yet. + .. attribute:: FINISHED + + The future has finished with a return value. + .. attribute:: CANCELLED + + The future was cancelled and has finished since. + .. attribute:: FAILED + + The future raised an exception. + """ + + PENDING = auto() + FINISHED = auto() + CANCELLED = auto() + FAILED = auto() + + __slots__ = ( + "_cancelled", + "_exception", + "_finished_event", + "_name", + "_return_value", + ) + _return_value: T + + def __init__(self, *, name: str | None = None) -> None: + self._finished_event = Event() + self._exception: BaseException | None = None + self._cancelled: bool = False + self._name = name + + def _check_pending(self) -> None: + """Shortcut for checking if a Future is pending + + :raises FutureAlreadyFinished: if future was already given a result or exception. + :raises FutureCancelled: if future has been cancelled previously. + """ + match self.status: + case Future.Status.PENDING: + return + case Future.Status.FINISHED: + raise FutureAlreadyFinished("future has already finished") + case Future.Status.FAILED: + raise FutureAlreadyFinished("future already failed") + case Future.Status.CANCELLED: + raise FutureCancelled("future was cancelled") + + async def wait(self) -> None: + """ + Waits for the future to finish. + + This method will attempt to wait for a result or exception + """ + await self._finished_event.wait() + + def cancel(self) -> None: + """Cancels a pending `.Future` object + + Does nothing if the Future was already finished. + """ + if self.status is Future.Status.PENDING: + self._cancelled = True + self._finished_event.set() + + @property + def exception(self) -> BaseException | None: + """ + The exception value of a `.Future` + + :raises FutureNotFinished: if future is still pending + :raises FutureCancelled: if future was cancelled + :returns: None if future succeeds with a result sent instead + otherwise this will be an exception + """ + + match self.status: + case Future.Status.PENDING: + raise FutureNotFinished("the future has not finished yet") + case Future.Status.FINISHED: + return None + case Future.Status.CANCELLED: + raise FutureCancelled("the future was cancelled") + case Future.Status.FAILED: + return self._exception + + @exception.setter + def exception(self, exception: BaseException) -> None: + """Send exception for a `.Future` object + + :raises FutureAlreadyFinished: if future was already given a result or exception. + :raises FutureCancelled: if future has been cancelled previously. + """ + self._check_pending() + self._exception = exception + self._finished_event.set() + + @property + def return_value(self) -> T: + """ + The return value of the future. + + :raises FutureNotFinished: if the future has not finished yet + :raises FutureCancelled: if the future was cancelled + :raises FutureFailed: if the future raised an exception + + """ + match self.status: + case Future.Status.PENDING: + raise FutureNotFinished("the future has not finished yet") + case Future.Status.FINISHED: + return self._return_value + case Future.Status.CANCELLED: + raise FutureCancelled("the future was cancelled") + case Future.Status.FAILED: + raise FutureFailed( + "the future raised an exception" + ) from self._exception + + @return_value.setter + def return_value(self, value: T) -> None: + """ + Send pending result for a `.Future` object + + :raises FutureAlreadyFinished: if future was already given a result or exception. + :raises FutureCancelled: if future has been cancelled previously. + """ + self._check_pending() + self._return_value = value + self._finished_event.set() + + @property + def status(self) -> Future.Status: + """ + The current status of a future. + + Every future starts in the :attr:`~Future.Status.PENDING` state. + If a future is cancelled while in this state, it will transition to the + :attr:`Future.Status.CANCELLED` state. When the task finishes, it will + transition to one of the three final states ( + :attr:`Future.Status.FINISHED`, :attr:`Future.Status.FAILED`, or + :attr:`Future.Status.CANCELLED`) depending on the exception the task + raised, if any. No other status transitions will happen. + """ + if not self._finished_event.is_set(): + return Future.Status.PENDING + elif self._cancelled: + return Future.Status.CANCELLED + elif self._exception is not None: + return Future.Status.FAILED + else: + return Future.Status.FINISHED + + def __await__(self) -> Generator[Any, Any, T]: + yield from self.wait().__await__() + return self.return_value + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} {self.status.name.lower()} " + f"name={self._name!r}>" + ) diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_resources.py b/venv/lib/python3.12/site-packages/anyio/_core/_resources.py new file mode 100644 index 0000000..6ca35d9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_resources.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from ..abc import AsyncResource # noqa: TC001 +from ._tasks import CancelScope + + +async def aclose_forcefully(resource: AsyncResource) -> None: + """ + Close an asynchronous resource in a cancelled scope. + + Doing this closes the resource without waiting on anything. + + :param resource: the resource to close + + """ + with CancelScope() as scope: + scope.cancel() + await resource.aclose() diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_signals.py b/venv/lib/python3.12/site-packages/anyio/_core/_signals.py new file mode 100644 index 0000000..e24c79e --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_signals.py @@ -0,0 +1,29 @@ +from __future__ import annotations + +from collections.abc import AsyncIterator +from contextlib import AbstractContextManager +from signal import Signals + +from ._eventloop import get_async_backend + + +def open_signal_receiver( + *signals: Signals, +) -> AbstractContextManager[AsyncIterator[Signals]]: + """ + Start receiving operating system signals. + + :param signals: signals to receive (e.g. ``signal.SIGINT``) + :return: an asynchronous context manager for an asynchronous iterator which yields + signal numbers + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. warning:: Windows does not support signals natively so it is best to avoid + relying on this in cross-platform applications. + + .. warning:: On asyncio, this permanently replaces any previous signal handler for + the given signals, as set via :meth:`~asyncio.loop.add_signal_handler`. + + """ + return get_async_backend().open_signal_receiver(*signals) diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_sockets.py b/venv/lib/python3.12/site-packages/anyio/_core/_sockets.py new file mode 100644 index 0000000..14ee6e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_sockets.py @@ -0,0 +1,1011 @@ +from __future__ import annotations + +import errno +import os +import socket +import ssl +import stat +import sys +from collections.abc import Awaitable +from dataclasses import dataclass +from ipaddress import IPv4Address, IPv6Address, ip_address +from os import PathLike, chmod +from socket import AddressFamily, SocketKind +from typing import TYPE_CHECKING, Any, Literal, cast, overload + +from .. import ConnectionFailed, to_thread +from ..abc import ( + ByteStreamConnectable, + ConnectedUDPSocket, + ConnectedUNIXDatagramSocket, + IPAddressType, + IPSockAddrType, + SocketListener, + SocketStream, + UDPSocket, + UNIXDatagramSocket, + UNIXSocketStream, +) +from ..streams.stapled import MultiListener +from ..streams.tls import TLSConnectable, TLSStream +from ._eventloop import get_async_backend +from ._resources import aclose_forcefully +from ._synchronization import Event +from ._tasks import create_task_group, move_on_after + +if TYPE_CHECKING: + from _typeshed import FileDescriptorLike +else: + FileDescriptorLike = object + +if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + +if sys.version_info < (3, 13): + from typing_extensions import deprecated +else: + from warnings import deprecated + +IPPROTO_IPV6 = getattr(socket, "IPPROTO_IPV6", 41) # https://bugs.python.org/issue29515 + +AnyIPAddressFamily = Literal[ + AddressFamily.AF_UNSPEC, AddressFamily.AF_INET, AddressFamily.AF_INET6 +] +IPAddressFamily = Literal[AddressFamily.AF_INET, AddressFamily.AF_INET6] + + +def idna2008_resolve(host: str) -> bytes: + try: + return host.encode("ascii") + except UnicodeEncodeError: + import idna + + return idna.encode(host, uts46=True) + + +# tls_hostname given +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + local_port: int | None = ..., + ssl_context: ssl.SSLContext | None = ..., + tls_standard_compatible: bool = ..., + tls_hostname: str, + happy_eyeballs_delay: float = ..., +) -> TLSStream: ... + + +# ssl_context given +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + local_port: int | None = ..., + ssl_context: ssl.SSLContext, + tls_standard_compatible: bool = ..., + tls_hostname: str | None = ..., + happy_eyeballs_delay: float = ..., +) -> TLSStream: ... + + +# tls=True +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + local_port: int | None = ..., + tls: Literal[True], + ssl_context: ssl.SSLContext | None = ..., + tls_standard_compatible: bool = ..., + tls_hostname: str | None = ..., + happy_eyeballs_delay: float = ..., +) -> TLSStream: ... + + +# tls=False +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + local_port: int | None = ..., + tls: Literal[False], + ssl_context: ssl.SSLContext | None = ..., + tls_standard_compatible: bool = ..., + tls_hostname: str | None = ..., + happy_eyeballs_delay: float = ..., +) -> SocketStream: ... + + +# No TLS arguments +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + local_port: int | None = ..., + happy_eyeballs_delay: float = ..., +) -> SocketStream: ... + + +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = None, + local_port: int | None = None, + tls: bool = False, + ssl_context: ssl.SSLContext | None = None, + tls_standard_compatible: bool = True, + tls_hostname: str | None = None, + happy_eyeballs_delay: float = 0.25, +) -> SocketStream | TLSStream: + """ + Connect to a host using the TCP protocol. + + This function implements the stateless version of the Happy Eyeballs algorithm (RFC + 6555). If ``remote_host`` is a host name that resolves to multiple IP addresses, + each one is tried until one connection attempt succeeds. If the first attempt does + not connected within 250 milliseconds, a second attempt is started using the next + address in the list, and so on. On IPv6 enabled systems, an IPv6 address (if + available) is tried first. + + When the connection has been established, a TLS handshake will be done if either + ``ssl_context`` or ``tls_hostname`` is not ``None``, or if ``tls`` is ``True``. + + :param remote_host: the IP address or host name to connect to + :param remote_port: port on the target host to connect to + :param local_host: the interface address or name to bind the socket to before + connecting + :param local_port: the local port to bind to (requires ``local_host`` to also be + set) + :param tls: ``True`` to do a TLS handshake with the connected stream and return a + :class:`~anyio.streams.tls.TLSStream` instead + :param ssl_context: the SSL context object to use (if omitted, a default context is + created) + :param tls_standard_compatible: If ``True``, performs the TLS shutdown handshake + before closing the stream and requires that the server does this as well. + Otherwise, :exc:`~ssl.SSLEOFError` may be raised during reads from the stream. + Some protocols, such as HTTP, require this option to be ``False``. + See :meth:`~ssl.SSLContext.wrap_socket` for details. + :param tls_hostname: host name to check the server certificate against (defaults to + the value of ``remote_host``) + :param happy_eyeballs_delay: delay (in seconds) before starting the next connection + attempt + :return: a socket stream object if no TLS handshake was done, otherwise a TLS stream + :raises ConnectionFailed: if the connection fails + + """ + # Placed here due to https://github.com/python/mypy/issues/7057 + connected_stream: SocketStream | None = None + + async def try_connect(remote_host: str, event: Event) -> None: + nonlocal connected_stream + try: + stream = await asynclib.connect_tcp(remote_host, remote_port, local_address) + except OSError as exc: + oserrors.append(exc) + return + else: + if connected_stream is None: + connected_stream = stream + tg.cancel_scope.cancel() + else: + await stream.aclose() + finally: + event.set() + + asynclib = get_async_backend() + local_address: IPSockAddrType | None = None + family = socket.AF_UNSPEC + if local_host: + gai_res = await getaddrinfo(str(local_host), local_port) + family, *_, local_address = gai_res[0] + + target_host = str(remote_host) + try: + addr_obj = ip_address(remote_host) + except ValueError: + addr_obj = None + + if addr_obj is not None: + if isinstance(addr_obj, IPv6Address): + target_addrs = [(socket.AF_INET6, addr_obj.compressed)] + else: + target_addrs = [(socket.AF_INET, addr_obj.compressed)] + else: + # getaddrinfo() will raise an exception if name resolution fails + gai_res = await getaddrinfo( + target_host, remote_port, family=family, type=socket.SOCK_STREAM + ) + + # Organize the list so that the first address is an IPv6 address (if available) + # and the second one is an IPv4 addresses. The rest can be in whatever order. + v6_found = v4_found = False + target_addrs = [] + for af, *_, sa in gai_res: + if af == socket.AF_INET6 and not v6_found: + v6_found = True + target_addrs.insert(0, (af, sa[0])) + elif af == socket.AF_INET and not v4_found and v6_found: + v4_found = True + target_addrs.insert(1, (af, sa[0])) + else: + target_addrs.append((af, sa[0])) + + oserrors: list[OSError] = [] + try: + async with create_task_group() as tg: + for _af, addr in target_addrs: + event = Event() + tg.start_soon(try_connect, addr, event) + with move_on_after(happy_eyeballs_delay): + await event.wait() + + if connected_stream is None: + cause = ( + oserrors[0] + if len(oserrors) == 1 + else ExceptionGroup("multiple connection attempts failed", oserrors) + ) + raise OSError("All connection attempts failed") from cause + finally: + oserrors.clear() + + if tls or tls_hostname or ssl_context: + try: + return await TLSStream.wrap( + connected_stream, + server_side=False, + hostname=tls_hostname or str(remote_host), + ssl_context=ssl_context, + standard_compatible=tls_standard_compatible, + ) + except BaseException: + await aclose_forcefully(connected_stream) + raise + + return connected_stream + + +async def connect_unix(path: str | bytes | PathLike[Any]) -> UNIXSocketStream: + """ + Connect to the given UNIX socket. + + Not available on Windows. + + :param path: path to the socket + :return: a socket stream object + :raises ConnectionFailed: if the connection fails + + """ + path = os.fspath(path) + return await get_async_backend().connect_unix(path) + + +async def create_tcp_listener( + *, + local_host: IPAddressType | None = None, + local_port: int = 0, + family: AnyIPAddressFamily = socket.AddressFamily.AF_UNSPEC, + backlog: int = 65536, + reuse_port: bool = False, +) -> MultiListener[SocketStream]: + """ + Create a TCP socket listener. + + :param local_port: port number to listen on + :param local_host: IP address of the interface to listen on. If omitted, listen on + all IPv4 and IPv6 interfaces. To listen on all interfaces on a specific address + family, use ``0.0.0.0`` for IPv4 or ``::`` for IPv6. + :param family: address family (used if ``local_host`` was omitted) + :param backlog: maximum number of queued incoming connections (up to a maximum of + 2**16, or 65536) + :param reuse_port: ``True`` to allow multiple sockets to bind to the same + address/port (not supported on Windows) + :return: a multi-listener object containing one or more socket listeners + :raises OSError: if there's an error creating a socket, or binding to one or more + interfaces failed + + """ + asynclib = get_async_backend() + backlog = min(backlog, 65536) + local_host = str(local_host) if local_host is not None else None + + def setup_raw_socket( + fam: AddressFamily, + bind_addr: tuple[str, int] | tuple[str, int, int, int], + *, + v6only: bool = True, + ) -> socket.socket: + sock = socket.socket(fam) + try: + sock.setblocking(False) + + if fam == AddressFamily.AF_INET6: + sock.setsockopt(IPPROTO_IPV6, socket.IPV6_V6ONLY, v6only) + + # For Windows, enable exclusive address use. For others, enable address + # reuse. + if sys.platform == "win32": + sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) + else: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + if reuse_port: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + + # Workaround for #554 + if fam == socket.AF_INET6 and "%" in bind_addr[0]: + addr, scope_id = bind_addr[0].split("%", 1) + bind_addr = (addr, bind_addr[1], 0, int(scope_id)) + + sock.bind(bind_addr) + sock.listen(backlog) + except BaseException: + sock.close() + raise + + return sock + + # We passing type=0 on non-Windows platforms as a workaround for a uvloop bug + # where we don't get the correct scope ID for IPv6 link-local addresses when passing + # type=socket.SOCK_STREAM to getaddrinfo(): + # https://github.com/MagicStack/uvloop/issues/539 + gai_res = await getaddrinfo( + local_host, + local_port, + family=family, + type=socket.SOCK_STREAM if sys.platform == "win32" else 0, + flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, + ) + + # The set comprehension is here to work around a glibc bug: + # https://sourceware.org/bugzilla/show_bug.cgi?id=14969 + sockaddrs = sorted({res for res in gai_res if res[1] == SocketKind.SOCK_STREAM}) + + # Special case for dual-stack binding on the "any" interface + if ( + local_host is None + and family == AddressFamily.AF_UNSPEC + and socket.has_dualstack_ipv6() + and any(fam == AddressFamily.AF_INET6 for fam, *_ in gai_res) + ): + raw_socket = setup_raw_socket( + AddressFamily.AF_INET6, ("::", local_port), v6only=False + ) + listener = asynclib.create_tcp_listener(raw_socket) + return MultiListener([listener]) + + errors: list[OSError] = [] + try: + for _ in range(len(sockaddrs)): + listeners: list[SocketListener] = [] + bound_ephemeral_port = local_port + try: + for fam, *_, sockaddr in sockaddrs: + sockaddr = sockaddr[0], bound_ephemeral_port, *sockaddr[2:] + raw_socket = setup_raw_socket(fam, sockaddr) + + # Store the assigned port if an ephemeral port was requested, so + # we'll bind to the same port on all interfaces + if local_port == 0 and len(gai_res) > 1: + bound_ephemeral_port = raw_socket.getsockname()[1] + + listeners.append(asynclib.create_tcp_listener(raw_socket)) + except BaseException as exc: + for listener in listeners: + await listener.aclose() + + # If an ephemeral port was requested but binding the assigned port + # failed for another interface, rotate the address list and try again + if ( + isinstance(exc, OSError) + and exc.errno == errno.EADDRINUSE + and local_port == 0 + and bound_ephemeral_port + ): + errors.append(exc) + sockaddrs.append(sockaddrs.pop(0)) + continue + + raise + + return MultiListener(listeners) + + raise OSError( + f"Could not create {len(sockaddrs)} listeners with a consistent port" + ) from ExceptionGroup("Several bind attempts failed", errors) + finally: + del errors # Prevent reference cycles + + +async def create_unix_listener( + path: str | bytes | PathLike[Any], + *, + mode: int | None = None, + backlog: int = 65536, +) -> SocketListener: + """ + Create a UNIX socket listener. + + Not available on Windows. + + :param path: path of the socket + :param mode: permissions to set on the socket + :param backlog: maximum number of queued incoming connections (up to a maximum of + 2**16, or 65536) + :return: a listener object + + .. versionchanged:: 3.0 + If a socket already exists on the file system in the given path, it will be + removed first. + + """ + backlog = min(backlog, 65536) + raw_socket = await setup_unix_local_socket(path, mode, socket.SOCK_STREAM) + try: + raw_socket.listen(backlog) + return get_async_backend().create_unix_listener(raw_socket) + except BaseException: + raw_socket.close() + raise + + +async def create_udp_socket( + family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC, + *, + local_host: IPAddressType | None = None, + local_port: int = 0, + reuse_port: bool = False, +) -> UDPSocket: + """ + Create a UDP socket. + + If ``port`` has been given, the socket will be bound to this port on the local + machine, making this socket suitable for providing UDP based services. + + :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically + determined from ``local_host`` if omitted + :param local_host: IP address or host name of the local interface to bind to + :param local_port: local port to bind to + :param reuse_port: ``True`` to allow multiple sockets to bind to the same + address/port (not supported on Windows) + :return: a UDP socket + + """ + if family is AddressFamily.AF_UNSPEC and not local_host: + raise ValueError('Either "family" or "local_host" must be given') + + if local_host: + gai_res = await getaddrinfo( + str(local_host), + local_port, + family=family, + type=socket.SOCK_DGRAM, + flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, + ) + family = cast(AnyIPAddressFamily, gai_res[0][0]) + local_address = gai_res[0][-1] + elif family is AddressFamily.AF_INET6: + local_address = ("::", 0) + else: + local_address = ("0.0.0.0", 0) + + sock = await get_async_backend().create_udp_socket( + family, local_address, None, reuse_port + ) + return cast(UDPSocket, sock) + + +async def create_connected_udp_socket( + remote_host: IPAddressType, + remote_port: int, + *, + family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC, + local_host: IPAddressType | None = None, + local_port: int = 0, + reuse_port: bool = False, +) -> ConnectedUDPSocket: + """ + Create a connected UDP socket. + + Connected UDP sockets can only communicate with the specified remote host/port, an + any packets sent from other sources are dropped. + + :param remote_host: remote host to set as the default target + :param remote_port: port on the remote host to set as the default target + :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically + determined from ``local_host`` or ``remote_host`` if omitted + :param local_host: IP address or host name of the local interface to bind to + :param local_port: local port to bind to + :param reuse_port: ``True`` to allow multiple sockets to bind to the same + address/port (not supported on Windows) + :return: a connected UDP socket + + """ + local_address = None + if local_host: + gai_res = await getaddrinfo( + str(local_host), + local_port, + family=family, + type=socket.SOCK_DGRAM, + flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, + ) + family = cast(AnyIPAddressFamily, gai_res[0][0]) + local_address = gai_res[0][-1] + + gai_res = await getaddrinfo( + str(remote_host), remote_port, family=family, type=socket.SOCK_DGRAM + ) + family = cast(AnyIPAddressFamily, gai_res[0][0]) + remote_address = gai_res[0][-1] + + sock = await get_async_backend().create_udp_socket( + family, local_address, remote_address, reuse_port + ) + return cast(ConnectedUDPSocket, sock) + + +async def create_unix_datagram_socket( + *, + local_path: None | str | bytes | PathLike[Any] = None, + local_mode: int | None = None, +) -> UNIXDatagramSocket: + """ + Create a UNIX datagram socket. + + Not available on Windows. + + If ``local_path`` has been given, the socket will be bound to this path, making this + socket suitable for receiving datagrams from other processes. Other processes can + send datagrams to this socket only if ``local_path`` is set. + + If a socket already exists on the file system in the ``local_path``, it will be + removed first. + + :param local_path: the path on which to bind to + :param local_mode: permissions to set on the local socket + :return: a UNIX datagram socket + + """ + raw_socket = await setup_unix_local_socket( + local_path, local_mode, socket.SOCK_DGRAM + ) + return await get_async_backend().create_unix_datagram_socket(raw_socket, None) + + +async def create_connected_unix_datagram_socket( + remote_path: str | bytes | PathLike[Any], + *, + local_path: None | str | bytes | PathLike[Any] = None, + local_mode: int | None = None, +) -> ConnectedUNIXDatagramSocket: + """ + Create a connected UNIX datagram socket. + + Connected datagram sockets can only communicate with the specified remote path. + + If ``local_path`` has been given, the socket will be bound to this path, making + this socket suitable for receiving datagrams from other processes. Other processes + can send datagrams to this socket only if ``local_path`` is set. + + If a socket already exists on the file system in the ``local_path``, it will be + removed first. + + :param remote_path: the path to set as the default target + :param local_path: the path on which to bind to + :param local_mode: permissions to set on the local socket + :return: a connected UNIX datagram socket + + """ + remote_path = os.fspath(remote_path) + raw_socket = await setup_unix_local_socket( + local_path, local_mode, socket.SOCK_DGRAM + ) + return await get_async_backend().create_unix_datagram_socket( + raw_socket, remote_path + ) + + +async def getaddrinfo( + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, +) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int]]]: + """ + Look up a numeric IP address given a host name. + + Internationalized domain names are translated according to the (non-transitional) + IDNA 2008 standard. + + .. note:: 4-tuple IPv6 socket addresses are automatically converted to 2-tuples of + (host, port), unlike what :func:`socket.getaddrinfo` does. + + :param host: host name + :param port: port number + :param family: socket family (`'AF_INET``, ...) + :param type: socket type (``SOCK_STREAM``, ...) + :param proto: protocol number + :param flags: flags to pass to upstream ``getaddrinfo()`` + :return: list of tuples containing (family, type, proto, canonname, sockaddr) + + .. seealso:: :func:`socket.getaddrinfo` + + """ + # Handle unicode hostnames + encoded_host = idna2008_resolve(host) if isinstance(host, str) else host + gai_res = await get_async_backend().getaddrinfo( + encoded_host, port, family=family, type=type, proto=proto, flags=flags + ) + return [ + (family, type, proto, canonname, convert_ipv6_sockaddr(sockaddr)) + for family, type, proto, canonname, sockaddr in gai_res + # filter out IPv6 results when IPv6 is disabled + if not isinstance(sockaddr[0], int) + ] + + +def getnameinfo(sockaddr: IPSockAddrType, flags: int = 0) -> Awaitable[tuple[str, str]]: + """ + Look up the host name of an IP address. + + :param sockaddr: socket address (e.g. (ipaddress, port) for IPv4) + :param flags: flags to pass to upstream ``getnameinfo()`` + :return: a tuple of (host name, service name) + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. seealso:: :func:`socket.getnameinfo` + + """ + return get_async_backend().getnameinfo(sockaddr, flags) + + +@deprecated("This function is deprecated; use `wait_readable` instead") +def wait_socket_readable(sock: socket.socket) -> Awaitable[None]: + """ + .. deprecated:: 4.7.0 + Use :func:`wait_readable` instead. + + Wait until the given socket has data to be read. + + .. warning:: Only use this on raw sockets that have not been wrapped by any higher + level constructs like socket streams! + + :param sock: a socket object + :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the + socket to become readable + :raises ~anyio.BusyResourceError: if another task is already waiting for the socket + to become readable + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().wait_readable(sock.fileno()) + + +@deprecated("This function is deprecated; use `wait_writable` instead") +def wait_socket_writable(sock: socket.socket) -> Awaitable[None]: + """ + .. deprecated:: 4.7.0 + Use :func:`wait_writable` instead. + + Wait until the given socket can be written to. + + This does **NOT** work on Windows when using the asyncio backend with a proactor + event loop (default on py3.8+). + + .. warning:: Only use this on raw sockets that have not been wrapped by any higher + level constructs like socket streams! + + :param sock: a socket object + :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the + socket to become writable + :raises ~anyio.BusyResourceError: if another task is already waiting for the socket + to become writable + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().wait_writable(sock.fileno()) + + +def wait_readable(obj: FileDescriptorLike) -> Awaitable[None]: + """ + Wait until the given object has data to be read. + + On Unix systems, ``obj`` must either be an integer file descriptor, or else an + object with a ``.fileno()`` method which returns an integer file descriptor. Any + kind of file descriptor can be passed, though the exact semantics will depend on + your kernel. For example, this probably won't do anything useful for on-disk files. + + On Windows systems, ``obj`` must either be an integer ``SOCKET`` handle, or else an + object with a ``.fileno()`` method which returns an integer ``SOCKET`` handle. File + descriptors aren't supported, and neither are handles that refer to anything besides + a ``SOCKET``. + + On backends where this functionality is not natively provided (asyncio + ``ProactorEventLoop`` on Windows), it is provided using a separate selector thread + which is set to shut down when the interpreter shuts down. + + .. warning:: Don't use this on raw sockets that have been wrapped by any higher + level constructs like socket streams! + + :param obj: an object with a ``.fileno()`` method or an integer handle + :raises ~anyio.ClosedResourceError: if the object was closed while waiting for the + object to become readable + :raises ~anyio.BusyResourceError: if another task is already waiting for the object + to become readable + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().wait_readable(obj) + + +def wait_writable(obj: FileDescriptorLike) -> Awaitable[None]: + """ + Wait until the given object can be written to. + + :param obj: an object with a ``.fileno()`` method or an integer handle + :raises ~anyio.ClosedResourceError: if the object was closed while waiting for the + object to become writable + :raises ~anyio.BusyResourceError: if another task is already waiting for the object + to become writable + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. seealso:: See the documentation of :func:`wait_readable` for the definition of + ``obj`` and notes on backend compatibility. + + .. warning:: Don't use this on raw sockets that have been wrapped by any higher + level constructs like socket streams! + + """ + return get_async_backend().wait_writable(obj) + + +def notify_closing(obj: FileDescriptorLike) -> None: + """ + Call this before closing a file descriptor (on Unix) or socket (on + Windows). This will cause any `wait_readable` or `wait_writable` + calls on the given object to immediately wake up and raise + `~anyio.ClosedResourceError`. + + This doesn't actually close the object – you still have to do that + yourself afterwards. Also, you want to be careful to make sure no + new tasks start waiting on the object in between when you call this + and when it's actually closed. So to close something properly, you + usually want to do these steps in order: + + 1. Explicitly mark the object as closed, so that any new attempts + to use it will abort before they start. + 2. Call `notify_closing` to wake up any already-existing users. + 3. Actually close the object. + + It's also possible to do them in a different order if that's more + convenient, *but only if* you make sure not to have any checkpoints in + between the steps. This way they all happen in a single atomic + step, so other tasks won't be able to tell what order they happened + in anyway. + + :param obj: an object with a ``.fileno()`` method or an integer handle + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + get_async_backend().notify_closing(obj) + + +# +# Private API +# + + +def convert_ipv6_sockaddr( + sockaddr: tuple[str, int, int, int] | tuple[str, int], +) -> tuple[str, int]: + """ + Convert a 4-tuple IPv6 socket address to a 2-tuple (address, port) format. + + If the scope ID is nonzero, it is added to the address, separated with ``%``. + Otherwise the flow id and scope id are simply cut off from the tuple. + Any other kinds of socket addresses are returned as-is. + + :param sockaddr: the result of :meth:`~socket.socket.getsockname` + :return: the converted socket address + + """ + # This is more complicated than it should be because of MyPy + if isinstance(sockaddr, tuple) and len(sockaddr) == 4: + host, port, _flowinfo, scope_id = sockaddr + if scope_id: + # PyPy (as of v7.3.11) leaves the interface name in the result, so + # we discard it and only get the scope ID from the end + # (https://foss.heptapod.net/pypy/pypy/-/issues/3938) + host = host.split("%")[0] + + # Add scope_id to the address + return f"{host}%{scope_id}", port + else: + return host, port + else: + return sockaddr + + +async def setup_unix_local_socket( + path: None | str | bytes | PathLike[Any], + mode: int | None, + socktype: int, +) -> socket.socket: + """ + Create a UNIX local socket object, deleting the socket at the given path if it + exists. + + Not available on Windows. + + :param path: path of the socket + :param mode: permissions to set on the socket + :param socktype: socket.SOCK_STREAM or socket.SOCK_DGRAM + + """ + path_str: str | None + if path is not None: + path_str = os.fsdecode(path) + + # Linux abstract namespace sockets aren't backed by a concrete file so skip stat call + if not path_str.startswith("\0"): + # Copied from pathlib... + try: + stat_result = os.stat(path) + except OSError as e: + if e.errno not in ( + errno.ENOENT, + errno.ENOTDIR, + errno.EBADF, + errno.ELOOP, + ): + raise + else: + if stat.S_ISSOCK(stat_result.st_mode): + os.unlink(path) + else: + path_str = None + + raw_socket = socket.socket(socket.AF_UNIX, socktype) + raw_socket.setblocking(False) + + if path_str is not None: + try: + await to_thread.run_sync(raw_socket.bind, path_str, abandon_on_cancel=True) + if mode is not None: + await to_thread.run_sync(chmod, path_str, mode, abandon_on_cancel=True) + except BaseException: + raw_socket.close() + raise + + return raw_socket + + +@dataclass +class TCPConnectable(ByteStreamConnectable): + """ + Connects to a TCP server at the given host and port. + + :param host: host name or IP address of the server + :param port: TCP port number of the server + """ + + host: str | IPv4Address | IPv6Address + port: int + + def __post_init__(self) -> None: + if self.port < 1 or self.port > 65535: + raise ValueError("TCP port number out of range") + + @override + async def connect(self) -> SocketStream: + try: + return await connect_tcp(self.host, self.port) + except OSError as exc: + raise ConnectionFailed( + f"error connecting to {self.host}:{self.port}: {exc}" + ) from exc + + +@dataclass +class UNIXConnectable(ByteStreamConnectable): + """ + Connects to a UNIX domain socket at the given path. + + :param path: the file system path of the socket + """ + + path: str | bytes | PathLike[str] | PathLike[bytes] + + @override + async def connect(self) -> UNIXSocketStream: + try: + return await connect_unix(self.path) + except OSError as exc: + raise ConnectionFailed(f"error connecting to {self.path!r}: {exc}") from exc + + +def as_connectable( + remote: ByteStreamConnectable + | tuple[str | IPv4Address | IPv6Address, int] + | str + | bytes + | PathLike[str], + /, + *, + tls: bool = False, + ssl_context: ssl.SSLContext | None = None, + tls_hostname: str | None = None, + tls_standard_compatible: bool = True, +) -> ByteStreamConnectable: + """ + Return a byte stream connectable from the given object. + + If a bytestream connectable is given, it is returned unchanged. + If a tuple of (host, port) is given, a TCP connectable is returned. + If a string or bytes path is given, a UNIX connectable is returned. + + If ``tls=True``, the connectable will be wrapped in a + :class:`~.streams.tls.TLSConnectable`. + + :param remote: a connectable, a tuple of (host, port) or a path to a UNIX socket + :param tls: if ``True``, wrap the plaintext connectable in a + :class:`~.streams.tls.TLSConnectable`, using the provided TLS settings) + :param ssl_context: if ``tls=True``, the SSLContext object to use (if not provided, + a secure default will be created) + :param tls_hostname: if ``tls=True``, host name of the server to use for checking + the server certificate (defaults to the host portion of the address for TCP + connectables) + :param tls_standard_compatible: if ``False`` and ``tls=True``, makes the TLS stream + skip the closing handshake when closing the connection, so it won't raise an + exception if the server does the same + + """ + connectable: TCPConnectable | UNIXConnectable | TLSConnectable + if isinstance(remote, ByteStreamConnectable): + return remote + elif isinstance(remote, tuple) and len(remote) == 2: + connectable = TCPConnectable(*remote) + elif isinstance(remote, (str, bytes, PathLike)): + connectable = UNIXConnectable(remote) + else: + raise TypeError(f"cannot convert {remote!r} to a connectable") + + if tls: + if not tls_hostname and isinstance(connectable, TCPConnectable): + tls_hostname = str(connectable.host) + + connectable = TLSConnectable( + connectable, + ssl_context=ssl_context, + hostname=tls_hostname, + standard_compatible=tls_standard_compatible, + ) + + return connectable diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_streams.py b/venv/lib/python3.12/site-packages/anyio/_core/_streams.py new file mode 100644 index 0000000..2b9c7df --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_streams.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +import math +from typing import TypeVar +from warnings import warn + +from ..streams.memory import ( + MemoryObjectReceiveStream, + MemoryObjectSendStream, + _MemoryObjectStreamState, +) + +T_Item = TypeVar("T_Item") + + +class create_memory_object_stream( + tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]], +): + """ + Create a memory object stream. + + The stream's item type can be annotated like + :func:`create_memory_object_stream[T_Item]`. + + :param max_buffer_size: number of items held in the buffer until ``send()`` starts + blocking + :param item_type: old way of marking the streams with the right generic type for + static typing (does nothing on AnyIO 4) + + .. deprecated:: 4.0 + Use ``create_memory_object_stream[YourItemType](...)`` instead. + :return: a tuple of (send stream, receive stream) + + """ + + def __new__( # type: ignore[misc] + cls, max_buffer_size: float = 0, item_type: object = None + ) -> tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]]: + if max_buffer_size != math.inf and not isinstance(max_buffer_size, int): + raise ValueError("max_buffer_size must be either an integer or math.inf") + if max_buffer_size < 0: + raise ValueError("max_buffer_size cannot be negative") + if item_type is not None: + warn( + "The item_type argument has been deprecated in AnyIO 4.0. " + "Use create_memory_object_stream[YourItemType](...) instead.", + DeprecationWarning, + stacklevel=2, + ) + + state = _MemoryObjectStreamState[T_Item](max_buffer_size) + return (MemoryObjectSendStream(state), MemoryObjectReceiveStream(state)) diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_subprocesses.py b/venv/lib/python3.12/site-packages/anyio/_core/_subprocesses.py new file mode 100644 index 0000000..53184a2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_subprocesses.py @@ -0,0 +1,196 @@ +from __future__ import annotations + +from collections.abc import AsyncIterable, Iterable, Mapping, Sequence +from io import BytesIO +from os import PathLike +from subprocess import PIPE, CalledProcessError, CompletedProcess +from typing import IO, Any, TypeAlias, cast + +from ..abc import Process # noqa: TC001 +from ._eventloop import get_async_backend +from ._tasks import create_task_group + +StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes] + + +async def run_process( + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + input: bytes | None = None, + stdin: int | IO[Any] | None = None, + stdout: int | IO[Any] | None = PIPE, + stderr: int | IO[Any] | None = PIPE, + check: bool = True, + cwd: StrOrBytesPath | None = None, + env: Mapping[str, str] | None = None, + startupinfo: Any = None, + creationflags: int = 0, + start_new_session: bool = False, + pass_fds: Sequence[int] = (), + user: str | int | None = None, + group: str | int | None = None, + extra_groups: Iterable[str | int] | None = None, + umask: int = -1, +) -> CompletedProcess[bytes]: + """ + Run an external command in a subprocess and wait until it completes. + + .. seealso:: :func:`subprocess.run` + + :param command: either a string to pass to the shell, or an iterable of strings + containing the executable name or path and its arguments + :param input: bytes passed to the standard input of the subprocess + :param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + a file-like object, or `None`; ``input`` overrides this + :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + a file-like object, or `None` + :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + :data:`subprocess.STDOUT`, a file-like object, or `None` + :param check: if ``True``, raise :exc:`~subprocess.CalledProcessError` if the + process terminates with a return code other than 0 + :param cwd: If not ``None``, change the working directory to this before running the + command + :param env: if not ``None``, this mapping replaces the inherited environment + variables from the parent process + :param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used + to specify process startup parameters (Windows only) + :param creationflags: flags that can be used to control the creation of the + subprocess (see :class:`subprocess.Popen` for the specifics) + :param start_new_session: if ``true`` the setsid() system call will be made in the + child process prior to the execution of the subprocess. (POSIX only) + :param pass_fds: sequence of file descriptors to keep open between the parent and + child processes. (POSIX only) + :param user: effective user to run the process as (Python >= 3.9, POSIX only) + :param group: effective group to run the process as (Python >= 3.9, POSIX only) + :param extra_groups: supplementary groups to set in the subprocess (Python >= 3.9, + POSIX only) + :param umask: if not negative, this umask is applied in the child process before + running the given command (Python >= 3.9, POSIX only) + :return: an object representing the completed process + :raises ~subprocess.CalledProcessError: if ``check`` is ``True`` and the process + exits with a nonzero return code + + """ + + async def drain_stream(stream: AsyncIterable[bytes], index: int) -> None: + buffer = BytesIO() + async for chunk in stream: + buffer.write(chunk) + + stream_contents[index] = buffer.getvalue() + + if stdin is not None and input is not None: + raise ValueError("only one of stdin and input is allowed") + + async with await open_process( + command, + stdin=PIPE if input else stdin, + stdout=stdout, + stderr=stderr, + cwd=cwd, + env=env, + startupinfo=startupinfo, + creationflags=creationflags, + start_new_session=start_new_session, + pass_fds=pass_fds, + user=user, + group=group, + extra_groups=extra_groups, + umask=umask, + ) as process: + stream_contents: list[bytes | None] = [None, None] + async with create_task_group() as tg: + if process.stdout: + tg.start_soon(drain_stream, process.stdout, 0) + + if process.stderr: + tg.start_soon(drain_stream, process.stderr, 1) + + if process.stdin and input: + await process.stdin.send(input) + await process.stdin.aclose() + + await process.wait() + + output, errors = stream_contents + if check and process.returncode != 0: + raise CalledProcessError(cast(int, process.returncode), command, output, errors) + + return CompletedProcess(command, cast(int, process.returncode), output, errors) + + +async def open_process( + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + stdin: int | IO[Any] | None = PIPE, + stdout: int | IO[Any] | None = PIPE, + stderr: int | IO[Any] | None = PIPE, + cwd: StrOrBytesPath | None = None, + env: Mapping[str, str] | None = None, + startupinfo: Any = None, + creationflags: int = 0, + start_new_session: bool = False, + pass_fds: Sequence[int] = (), + user: str | int | None = None, + group: str | int | None = None, + extra_groups: Iterable[str | int] | None = None, + umask: int = -1, +) -> Process: + """ + Start an external command in a subprocess. + + .. seealso:: :class:`subprocess.Popen` + + :param command: either a string to pass to the shell, or an iterable of strings + containing the executable name or path and its arguments + :param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, a + file-like object, or ``None`` + :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + a file-like object, or ``None`` + :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + :data:`subprocess.STDOUT`, a file-like object, or ``None`` + :param cwd: If not ``None``, the working directory is changed before executing + :param env: If env is not ``None``, it must be a mapping that defines the + environment variables for the new process + :param creationflags: flags that can be used to control the creation of the + subprocess (see :class:`subprocess.Popen` for the specifics) + :param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used + to specify process startup parameters (Windows only) + :param start_new_session: if ``true`` the setsid() system call will be made in the + child process prior to the execution of the subprocess. (POSIX only) + :param pass_fds: sequence of file descriptors to keep open between the parent and + child processes. (POSIX only) + :param user: effective user to run the process as (POSIX only) + :param group: effective group to run the process as (POSIX only) + :param extra_groups: supplementary groups to set in the subprocess (POSIX only) + :param umask: if not negative, this umask is applied in the child process before + running the given command (POSIX only) + :return: an asynchronous process object + + """ + kwargs: dict[str, Any] = {} + if user is not None: + kwargs["user"] = user + + if group is not None: + kwargs["group"] = group + + if extra_groups is not None: + kwargs["extra_groups"] = extra_groups + + if umask >= 0: + kwargs["umask"] = umask + + return await get_async_backend().open_process( + command, + stdin=stdin, + stdout=stdout, + stderr=stderr, + cwd=cwd, + env=env, + startupinfo=startupinfo, + creationflags=creationflags, + start_new_session=start_new_session, + pass_fds=pass_fds, + **kwargs, + ) diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_synchronization.py b/venv/lib/python3.12/site-packages/anyio/_core/_synchronization.py new file mode 100644 index 0000000..cb988c6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_synchronization.py @@ -0,0 +1,778 @@ +from __future__ import annotations + +import math +import sys +from collections import deque +from collections.abc import Callable +from dataclasses import dataclass +from types import TracebackType +from typing import TypeVar + +from ..lowlevel import checkpoint_if_cancelled +from ._eventloop import get_async_backend +from ._exceptions import BusyResourceError, NoEventLoopError +from ._tasks import CancelScope +from ._testing import TaskInfo, get_current_task + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self + +T = TypeVar("T") + + +@dataclass(frozen=True) +class EventStatistics: + """ + :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Event.wait` + """ + + tasks_waiting: int + + +@dataclass(frozen=True) +class CapacityLimiterStatistics: + """ + :ivar int borrowed_tokens: number of tokens currently borrowed by tasks + :ivar float total_tokens: total number of available tokens + :ivar tuple borrowers: tasks or other objects currently holding tokens borrowed from + this limiter + :ivar int tasks_waiting: number of tasks waiting on + :meth:`~.CapacityLimiter.acquire` or + :meth:`~.CapacityLimiter.acquire_on_behalf_of` + """ + + borrowed_tokens: int + total_tokens: float + borrowers: tuple[object, ...] + tasks_waiting: int + + +@dataclass(frozen=True) +class LockStatistics: + """ + :ivar bool locked: flag indicating if this lock is locked or not + :ivar ~anyio.TaskInfo owner: task currently holding the lock (or ``None`` if the + lock is not held by any task) + :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Lock.acquire` + """ + + locked: bool + owner: TaskInfo | None + tasks_waiting: int + + +@dataclass(frozen=True) +class ConditionStatistics: + """ + :ivar int tasks_waiting: number of tasks blocked on :meth:`~.Condition.wait` + :ivar ~anyio.LockStatistics lock_statistics: statistics of the underlying + :class:`~.Lock` + """ + + tasks_waiting: int + lock_statistics: LockStatistics + + +@dataclass(frozen=True) +class SemaphoreStatistics: + """ + :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Semaphore.acquire` + + """ + + tasks_waiting: int + + +class Event: + __slots__ = ("__weakref__",) + + def __new__(cls) -> Event: + try: + return get_async_backend().create_event() + except NoEventLoopError: + return EventAdapter() + + def set(self) -> None: + """Set the flag, notifying all listeners.""" + raise NotImplementedError + + def is_set(self) -> bool: + """Return ``True`` if the flag is set, ``False`` if not.""" + raise NotImplementedError + + async def wait(self) -> None: + """ + Wait until the flag has been set. + + If the flag has already been set when this method is called, it returns + immediately. + + """ + raise NotImplementedError + + def statistics(self) -> EventStatistics: + """Return statistics about the current state of this event.""" + raise NotImplementedError + + +class EventAdapter(Event): + __slots__ = "_internal_event", "_is_set" + + def __new__(cls) -> Self: + return object.__new__(cls) + + def __init__(self) -> None: + self._internal_event: Event | None = None + self._is_set = False + + @property + def _event(self) -> Event: + if self._internal_event is None: + self._internal_event = get_async_backend().create_event() + if self._is_set: + self._internal_event.set() + + return self._internal_event + + def set(self) -> None: + if self._internal_event is None: + self._is_set = True + else: + self._event.set() + + def is_set(self) -> bool: + if self._internal_event is None: + return self._is_set + + return self._internal_event.is_set() + + async def wait(self) -> None: + await self._event.wait() + + def statistics(self) -> EventStatistics: + if self._internal_event is None: + return EventStatistics(tasks_waiting=0) + + return self._internal_event.statistics() + + +class Lock: + __slots__ = ("__weakref__",) + + def __new__(cls, *, fast_acquire: bool = False) -> Lock: + try: + return get_async_backend().create_lock(fast_acquire=fast_acquire) + except NoEventLoopError: + return LockAdapter(fast_acquire=fast_acquire) + + async def __aenter__(self) -> None: + await self.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + async def acquire(self) -> None: + """Acquire the lock.""" + raise NotImplementedError + + def acquire_nowait(self) -> None: + """ + Acquire the lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + raise NotImplementedError + + def release(self) -> None: + """Release the lock.""" + raise NotImplementedError + + def locked(self) -> bool: + """Return True if the lock is currently held.""" + raise NotImplementedError + + def statistics(self) -> LockStatistics: + """ + Return statistics about the current state of this lock. + + .. versionadded:: 3.0 + """ + raise NotImplementedError + + +class LockAdapter(Lock): + __slots__ = "_fast_acquire", "_internal_lock" + + def __new__(cls, *, fast_acquire: bool = False) -> Self: + return object.__new__(cls) + + def __init__(self, *, fast_acquire: bool = False): + self._internal_lock: Lock | None = None + self._fast_acquire = fast_acquire + + @property + def _lock(self) -> Lock: + if self._internal_lock is None: + self._internal_lock = get_async_backend().create_lock( + fast_acquire=self._fast_acquire + ) + + return self._internal_lock + + async def __aenter__(self) -> None: + await self._lock.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self._internal_lock is not None: + self._internal_lock.release() + + async def acquire(self) -> None: + """Acquire the lock.""" + await self._lock.acquire() + + def acquire_nowait(self) -> None: + """ + Acquire the lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + self._lock.acquire_nowait() + + def release(self) -> None: + """Release the lock.""" + self._lock.release() + + def locked(self) -> bool: + """Return True if the lock is currently held.""" + return self._lock.locked() + + def statistics(self) -> LockStatistics: + """ + Return statistics about the current state of this lock. + + .. versionadded:: 3.0 + + """ + if self._internal_lock is None: + return LockStatistics(False, None, 0) + + return self._internal_lock.statistics() + + +class Condition: + __slots__ = "__weakref__", "_lock", "_owner_task", "_waiters" + + def __init__(self, lock: Lock | None = None): + self._owner_task: TaskInfo | None = None + self._lock = lock or Lock() + self._waiters: deque[Event] = deque() + + async def __aenter__(self) -> None: + await self.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + def _check_acquired(self) -> None: + if self._owner_task != get_current_task(): + raise RuntimeError("The current task is not holding the underlying lock") + + async def acquire(self) -> None: + """Acquire the underlying lock.""" + await self._lock.acquire() + self._owner_task = get_current_task() + + def acquire_nowait(self) -> None: + """ + Acquire the underlying lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + self._lock.acquire_nowait() + self._owner_task = get_current_task() + + def release(self) -> None: + """Release the underlying lock.""" + self._lock.release() + + def locked(self) -> bool: + """Return True if the lock is set.""" + return self._lock.locked() + + def notify(self, n: int = 1) -> None: + """Notify exactly n listeners.""" + self._check_acquired() + for _ in range(n): + try: + event = self._waiters.popleft() + except IndexError: + break + + event.set() + + def notify_all(self) -> None: + """Notify all the listeners.""" + self._check_acquired() + for event in self._waiters: + event.set() + + self._waiters.clear() + + async def wait(self) -> None: + """Wait for a notification.""" + await checkpoint_if_cancelled() + self._check_acquired() + event = Event() + self._waiters.append(event) + self.release() + try: + await event.wait() + except BaseException: + if not event.is_set(): + self._waiters.remove(event) + elif self._waiters: + # This task was notified by could not act on it, so pass + # it on to the next task + self._waiters.popleft().set() + + raise + finally: + with CancelScope(shield=True): + await self.acquire() + + async def wait_for(self, predicate: Callable[[], T]) -> T: + """ + Wait until a predicate becomes true. + + :param predicate: a callable that returns a truthy value when the condition is + met + :return: the result of the predicate + + .. versionadded:: 4.11.0 + + """ + while not (result := predicate()): + await self.wait() + + return result + + def statistics(self) -> ConditionStatistics: + """ + Return statistics about the current state of this condition. + + .. versionadded:: 3.0 + """ + return ConditionStatistics(len(self._waiters), self._lock.statistics()) + + +class Semaphore: + __slots__ = "__weakref__", "_fast_acquire" + + def __new__( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> Semaphore: + try: + return get_async_backend().create_semaphore( + initial_value, max_value=max_value, fast_acquire=fast_acquire + ) + except NoEventLoopError: + return SemaphoreAdapter(initial_value, max_value=max_value) + + def __init__( + self, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ): + if not isinstance(initial_value, int): + raise TypeError("initial_value must be an integer") + if initial_value < 0: + raise ValueError("initial_value must be >= 0") + if max_value is not None: + if not isinstance(max_value, int): + raise TypeError("max_value must be an integer or None") + if max_value < initial_value: + raise ValueError( + "max_value must be equal to or higher than initial_value" + ) + + self._fast_acquire = fast_acquire + + async def __aenter__(self) -> Self: + await self.acquire() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + async def acquire(self) -> None: + """Decrement the semaphore value, blocking if necessary.""" + raise NotImplementedError + + def acquire_nowait(self) -> None: + """ + Acquire the underlying lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + raise NotImplementedError + + def release(self) -> None: + """Increment the semaphore value.""" + raise NotImplementedError + + @property + def value(self) -> int: + """The current value of the semaphore.""" + raise NotImplementedError + + @property + def max_value(self) -> int | None: + """The maximum value of the semaphore.""" + raise NotImplementedError + + def statistics(self) -> SemaphoreStatistics: + """ + Return statistics about the current state of this semaphore. + + .. versionadded:: 3.0 + """ + raise NotImplementedError + + +class SemaphoreAdapter(Semaphore): + __slots__ = "_initial_value", "_internal_semaphore", "_max_value" + + def __new__( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> Self: + return object.__new__(cls) + + def __init__( + self, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> None: + super().__init__(initial_value, max_value=max_value, fast_acquire=fast_acquire) + self._internal_semaphore: Semaphore | None = None + self._initial_value = initial_value + self._max_value = max_value + + @property + def _semaphore(self) -> Semaphore: + if self._internal_semaphore is None: + self._internal_semaphore = get_async_backend().create_semaphore( + self._initial_value, max_value=self._max_value + ) + + return self._internal_semaphore + + async def acquire(self) -> None: + await self._semaphore.acquire() + + def acquire_nowait(self) -> None: + self._semaphore.acquire_nowait() + + def release(self) -> None: + self._semaphore.release() + + @property + def value(self) -> int: + if self._internal_semaphore is None: + return self._initial_value + + return self._semaphore.value + + @property + def max_value(self) -> int | None: + return self._max_value + + def statistics(self) -> SemaphoreStatistics: + if self._internal_semaphore is None: + return SemaphoreStatistics(tasks_waiting=0) + + return self._semaphore.statistics() + + +class CapacityLimiter: + __slots__ = ("__weakref__",) + + def __new__(cls, total_tokens: float) -> CapacityLimiter: + try: + return get_async_backend().create_capacity_limiter(total_tokens) + except NoEventLoopError: + return CapacityLimiterAdapter(total_tokens) + + async def __aenter__(self) -> None: + raise NotImplementedError + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + raise NotImplementedError + + @property + def total_tokens(self) -> float: + """ + The total number of tokens available for borrowing. + + This is a read-write property. If the total number of tokens is increased, the + proportionate number of tasks waiting on this limiter will be granted their + tokens. + + .. versionchanged:: 3.0 + The property is now writable. + .. versionchanged:: 4.12 + The value can now be set to 0. + + """ + raise NotImplementedError + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + raise NotImplementedError + + @property + def borrowed_tokens(self) -> int: + """The number of tokens that have currently been borrowed.""" + raise NotImplementedError + + @property + def available_tokens(self) -> float: + """The number of tokens currently available to be borrowed""" + raise NotImplementedError + + def acquire_nowait(self) -> None: + """ + Acquire a token for the current task without waiting for one to become + available. + + :raises ~anyio.WouldBlock: if there are no tokens available for borrowing + + """ + raise NotImplementedError + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + """ + Acquire a token without waiting for one to become available. + + :param borrower: the entity borrowing a token + :raises ~anyio.WouldBlock: if there are no tokens available for borrowing + + """ + raise NotImplementedError + + async def acquire(self) -> None: + """ + Acquire a token for the current task, waiting if necessary for one to become + available. + + """ + raise NotImplementedError + + async def acquire_on_behalf_of(self, borrower: object) -> None: + """ + Acquire a token, waiting if necessary for one to become available. + + :param borrower: the entity borrowing a token + + """ + raise NotImplementedError + + def release(self) -> None: + """ + Release the token held by the current task. + + :raises RuntimeError: if the current task has not borrowed a token from this + limiter. + + """ + raise NotImplementedError + + def release_on_behalf_of(self, borrower: object) -> None: + """ + Release the token held by the given borrower. + + :raises RuntimeError: if the borrower has not borrowed a token from this + limiter. + + """ + raise NotImplementedError + + def statistics(self) -> CapacityLimiterStatistics: + """ + Return statistics about the current state of this limiter. + + .. versionadded:: 3.0 + + """ + raise NotImplementedError + + +class CapacityLimiterAdapter(CapacityLimiter): + __slots__ = "_internal_limiter", "_total_tokens" + + def __new__(cls, total_tokens: float) -> Self: + return object.__new__(cls) + + def __init__(self, total_tokens: float) -> None: + self._internal_limiter: CapacityLimiter | None = None + self.total_tokens = total_tokens + + @property + def _limiter(self) -> CapacityLimiter: + if self._internal_limiter is None: + self._internal_limiter = get_async_backend().create_capacity_limiter( + self._total_tokens + ) + + return self._internal_limiter + + async def __aenter__(self) -> None: + await self._limiter.__aenter__() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + return await self._limiter.__aexit__(exc_type, exc_val, exc_tb) + + @property + def total_tokens(self) -> float: + if self._internal_limiter is None: + return self._total_tokens + + return self._internal_limiter.total_tokens + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + if not isinstance(value, int) and not math.isinf(value): + raise TypeError("total_tokens must be an int or math.inf") + elif value < 0: + raise ValueError("total_tokens must be >= 0") + + if self._internal_limiter is None: + self._total_tokens = value + return + + self._limiter.total_tokens = value + + @property + def borrowed_tokens(self) -> int: + if self._internal_limiter is None: + return 0 + + return self._internal_limiter.borrowed_tokens + + @property + def available_tokens(self) -> float: + if self._internal_limiter is None: + return self._total_tokens + + return self._internal_limiter.available_tokens + + def acquire_nowait(self) -> None: + self._limiter.acquire_nowait() + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + self._limiter.acquire_on_behalf_of_nowait(borrower) + + async def acquire(self) -> None: + await self._limiter.acquire() + + async def acquire_on_behalf_of(self, borrower: object) -> None: + await self._limiter.acquire_on_behalf_of(borrower) + + def release(self) -> None: + self._limiter.release() + + def release_on_behalf_of(self, borrower: object) -> None: + self._limiter.release_on_behalf_of(borrower) + + def statistics(self) -> CapacityLimiterStatistics: + if self._internal_limiter is None: + return CapacityLimiterStatistics( + borrowed_tokens=0, + total_tokens=self.total_tokens, + borrowers=(), + tasks_waiting=0, + ) + + return self._internal_limiter.statistics() + + +class ResourceGuard: + """ + A context manager for ensuring that a resource is only used by a single task at a + time. + + Entering this context manager while the previous has not exited it yet will trigger + :exc:`BusyResourceError`. + + :param action: the action to guard against (visible in the :exc:`BusyResourceError` + when triggered, e.g. "Another task is already {action} this resource") + + .. versionadded:: 4.1 + """ + + __slots__ = "__weakref__", "_guarded", "action" + + def __init__(self, action: str = "using"): + self.action: str = action + self._guarded = False + + def __enter__(self) -> None: + if self._guarded: + raise BusyResourceError(self.action) + + self._guarded = True + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self._guarded = False diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_tasks.py b/venv/lib/python3.12/site-packages/anyio/_core/_tasks.py new file mode 100644 index 0000000..044eda6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_tasks.py @@ -0,0 +1,464 @@ +from __future__ import annotations + +import math +import sys +from collections.abc import ( + Coroutine, + Generator, +) +from contextlib import contextmanager +from enum import Enum, auto +from inspect import iscoroutine +from types import TracebackType +from typing import Any, Generic, final + +from ..abc import TaskGroup, TaskStatus +from ._eventloop import get_async_backend, get_cancelled_exc_class +from ._exceptions import TaskCancelled, TaskFailed, TaskNotFinished + +if sys.version_info >= (3, 13): + from typing import TypeVar +else: + from typing_extensions import TypeVar + +if sys.version_info >= (3, 11): + from typing import Never, Self, TypeVarTuple +else: + from typing_extensions import Never, Self, TypeVarTuple + +T = TypeVar("T") +T_co = TypeVar("T_co", covariant=True) +T_startval_co = TypeVar("T_startval_co", covariant=True, default=Never) +PosArgsT = TypeVarTuple("PosArgsT") + + +class _IgnoredTaskStatus(TaskStatus[object]): + def started(self, value: object = None) -> None: + pass + + +TASK_STATUS_IGNORED = _IgnoredTaskStatus() + + +class CancelScope: + """ + Wraps a unit of work that can be made separately cancellable. + + :param deadline: The time (clock value) when this scope is cancelled automatically + :param shield: ``True`` to shield the cancel scope from external cancellation + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + """ + + __slots__ = ("__weakref__",) + + def __new__( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + return get_async_backend().create_cancel_scope(shield=shield, deadline=deadline) + + def cancel(self, reason: str | None = None) -> None: + """ + Cancel this scope immediately. + + :param reason: a message describing the reason for the cancellation + + """ + raise NotImplementedError + + @property + def deadline(self) -> float: + """ + The time (clock value) when this scope is cancelled automatically. + + Will be ``float('inf')`` if no timeout has been set. + + """ + raise NotImplementedError + + @deadline.setter + def deadline(self, value: float) -> None: + raise NotImplementedError + + @property + def cancel_called(self) -> bool: + """``True`` if :meth:`cancel` has been called.""" + raise NotImplementedError + + @property + def cancelled_caught(self) -> bool: + """ + ``True`` if this scope suppressed a cancellation exception it itself raised. + + This is typically used to check if any work was interrupted, or to see if the + scope was cancelled due to its deadline being reached. The value will, however, + only be ``True`` if the cancellation was triggered by the scope itself (and not + an outer scope). + + """ + raise NotImplementedError + + @property + def shield(self) -> bool: + """ + ``True`` if this scope is shielded from external cancellation. + + While a scope is shielded, it will not receive cancellations from outside. + + """ + raise NotImplementedError + + @shield.setter + def shield(self, value: bool) -> None: + raise NotImplementedError + + def __enter__(self) -> Self: + raise NotImplementedError + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + raise NotImplementedError + + +@contextmanager +def fail_at( + deadline: float | None, shield: bool = False, reason: str | None = None +) -> Generator[CancelScope, None, None]: + """ + Create a context manager which raises a :class:`TimeoutError` if the code in the + enclosing context does not finish by the given deadline. + + :param deadline: the deadline before raising the exception, or + ``None`` to disable the timeout + :param shield: ``True`` to shield the cancel scope from external cancellation + :param reason: explanation for timeout to add to the message of a raised `TimeoutError` + :return: a context manager that yields a cancel scope + :rtype: :class:`~typing.ContextManager`\\[:class:`~anyio.CancelScope`\\] + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. versionadded:: 4.15.0 + + """ + current_time = get_async_backend().current_time + effective_deadline = math.inf if deadline is None else deadline + with get_async_backend().create_cancel_scope( + deadline=effective_deadline, shield=shield + ) as cancel_scope: + yield cancel_scope + + if cancel_scope.cancelled_caught and current_time() >= cancel_scope.deadline: + raise TimeoutError(reason) if reason else TimeoutError + + +@contextmanager +def fail_after( + delay: float | None, shield: bool = False, reason: str | None = None +) -> Generator[CancelScope, None, None]: + """ + Create a context manager which raises a :class:`TimeoutError` if the code in the + enclosing context block does not finish in time. + + :param delay: maximum allowed time (in seconds) before raising the exception, or + ``None`` to disable the timeout + :param shield: ``True`` to shield the cancel scope from external cancellation + :param reason: explanation for timeout to add to the message of a raised `TimeoutError` + :return: a context manager that yields a cancel scope + :rtype: :class:`~typing.ContextManager`\\[:class:`~anyio.CancelScope`\\] + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + current_time = get_async_backend().current_time + deadline = (current_time() + delay) if delay is not None else math.inf + with fail_at(deadline, shield=shield, reason=reason) as scope: + yield scope + + +def move_on_at(deadline: float | None, shield: bool = False) -> CancelScope: + """ + Create a cancel scope with a deadline that expires after the given delay. + + :param deadline: the deadline before exiting the context block, or + ``None`` to disable the timeout + :param shield: ``True`` to shield the cancel scope from external cancellation + :return: a cancel scope + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. versionadded:: 4.15.0 + + """ + return get_async_backend().create_cancel_scope( + deadline=deadline if deadline is not None else math.inf, shield=shield + ) + + +def move_on_after(delay: float | None, shield: bool = False) -> CancelScope: + """ + Create a cancel scope with a deadline that expires after the given delay. + + :param delay: maximum allowed time (in seconds) before exiting the context block, or + ``None`` to disable the timeout + :param shield: ``True`` to shield the cancel scope from external cancellation + :return: a cancel scope + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. note:: Unlike with :func:`fail_after`, the timer starts when this function is + called, not when the context manager is entered. This will be changed in v5.0. + + """ + deadline = ( + (get_async_backend().current_time() + delay) if delay is not None else math.inf + ) + return get_async_backend().create_cancel_scope(deadline=deadline, shield=shield) + + +def current_effective_deadline() -> float: + """ + Return the nearest deadline among all the cancel scopes effective for the current + task. + + :return: a clock value from the event loop's internal clock (or ``float('inf')`` if + there is no deadline in effect, or ``float('-inf')`` if the current scope has + been cancelled) + :rtype: float + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().current_effective_deadline() + + +def create_task_group() -> TaskGroup: + """ + Create a task group. + + :return: a task group + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().create_task_group() + + +@final +class TaskHandle(Generic[T_co, T_startval_co]): + """ + Returned from the task-spawning methods of :class:`TaskGroup`. Can be awaited on to + get the return value of the task (or the raised exception). If the task was + terminated by a :exc:`BaseException`, :exc:`TaskFailed` will be raised (or its + subclass :exc:`TaskCancelled` if the task was cancelled). + + .. versionadded:: 4.14.0 + """ + + class Status(Enum): + """ + The status of a task handle. + + .. attribute:: PENDING + + The task has not finished yet. + .. attribute:: FINISHED + + The task has finished with a return value. + .. attribute:: CANCELLING + + The task has been cancelled but has not finished yet. + .. attribute:: CANCELLED + + The task was cancelled and has finished since. + .. attribute:: FAILED + + The task raised an exception. + """ + + PENDING = auto() + FINISHED = auto() + CANCELLING = auto() + CANCELLED = auto() + FAILED = auto() + + __slots__ = ( + "__weakref__", + "_cancel_scope", + "_coro", + "_exception", + "_finished_event", + "_name", + "_return_value", + "_start_value", + ) + + _return_value: T_co + _start_value: T_startval_co + + def __init__( + self, + coro: Coroutine[Any, Any, T_co], + name: object, + cancel_scope: CancelScope | None = None, + ) -> None: + from ._synchronization import Event + + self._coro = coro + self._cancel_scope = cancel_scope if cancel_scope is not None else CancelScope() + self._finished_event = Event() + self._exception: BaseException | None = None + + if name is not None: + self._name = str(name) + elif iscoroutine(coro): + self._name = coro.__qualname__ + else: + self._name = str(coro) # coroutine-like object (e.g. asend() objects) + + async def _run_coro(self) -> None: + __tracebackhide__ = True + + with self._cancel_scope: + try: + retval = await self._coro + except BaseException as exc: + self._exception = exc + raise + else: + self._return_value = retval + finally: + self._finished_event.set() + del self # Break the reference cycle + + def cancel(self) -> None: + """ + Set the task to a cancelled state. + + This will interrupt any interruptible asynchronous operation, and will cause + any further awaits on this task to get immediately cancelled, unless done in + a shielded cancel scope. + + If the task has already finished, this method has no effect. + """ + if not self._finished_event.is_set(): + self._cancel_scope.cancel() + + @property + def coro(self) -> Coroutine[Any, Any, T_co]: + """ + The coroutine object that was passed to one of the task-spawning methods in + :class:`TaskGroup`. + """ + return self._coro + + @property + def status(self) -> TaskHandle.Status: + """ + The current status of the task. + + Every task starts in the :attr:`~TaskHandle.Status.PENDING` state. + If a task is cancelled while in this state, it will transition to the + :attr:`~TaskHandle.Status.CANCELLING` state. When the task finishes, it will + transition to one of the three final states ( + :attr:`~TaskHandle.Status.FINISHED`, :attr:`~TaskHandle.Status.FAILED`, or + :attr:`~TaskHandle.Status.CANCELLING`) depending on the exception the task + raised, if any. No other status transitions will happen. + """ + if not self._finished_event.is_set(): + if self._cancel_scope.cancel_called: + return TaskHandle.Status.CANCELLING + else: + return TaskHandle.Status.PENDING + elif self._exception is not None: + if isinstance(self._exception, get_cancelled_exc_class()): + return TaskHandle.Status.CANCELLED + else: + return TaskHandle.Status.FAILED + else: + return TaskHandle.Status.FINISHED + + @property + def name(self) -> str: + """The name of the task.""" + return self._name + + @property + def exception(self) -> BaseException | None: + """ + The exception raised by the task, or ``None`` if it finished without raising. + + :raises TaskNotFinished: if the task has not finished yet + :raises TaskCancelled: if the task was cancelled + + """ + match self.status: + case TaskHandle.Status.PENDING: + raise TaskNotFinished("the task has not finished yet") + case TaskHandle.Status.FINISHED: + return None + case TaskHandle.Status.CANCELLING: + raise TaskCancelled("the task was cancelled") + case TaskHandle.Status.CANCELLED: + raise TaskCancelled("the task was cancelled") from self._exception + case TaskHandle.Status.FAILED: + return self._exception + + @property + def return_value(self) -> T_co: + """ + The return value of the task. + + :raises TaskNotFinished: if the task has not finished yet + :raises TaskCancelled: if the task was cancelled + :raises TaskFailed: if the task raised an exception + + """ + match self.status: + case TaskHandle.Status.PENDING: + raise TaskNotFinished("the task has not finished yet") + case TaskHandle.Status.FINISHED: + return self._return_value + case TaskHandle.Status.CANCELLING: + raise TaskCancelled("the task was cancelled") + case TaskHandle.Status.CANCELLED: + raise TaskCancelled("the task was cancelled") from self._exception + case TaskHandle.Status.FAILED: + raise TaskFailed("the task raised an exception") from self._exception + + @property + def start_value(self) -> T_startval_co: + """ + The value passed to :meth:`task_status.started() <.abc.TaskStatus.started>`, + + :raises RuntimeError: if the task was not started with :meth:`TaskGroup.start() + <.abc.TaskGroup.start>` + """ + try: + return self._start_value + except AttributeError: + raise RuntimeError( + "the task was not started with TaskGroup.start()" + ) from None + + async def wait(self) -> None: + """ + Wait for the task to finish. + + This method will return as soon as the task has finished, no matter how it + happened. + """ + await self._finished_event.wait() + + def __await__(self) -> Generator[Any, Any, T_co]: + yield from self._finished_event.wait().__await__() + return self.return_value + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} {self.status.name.lower()} " + f"name={self._name!r} coro={self._coro!r}>" + ) diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_tempfile.py b/venv/lib/python3.12/site-packages/anyio/_core/_tempfile.py new file mode 100644 index 0000000..9603086 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_tempfile.py @@ -0,0 +1,615 @@ +from __future__ import annotations + +import os +import sys +import tempfile +from collections.abc import Iterable +from io import BytesIO, TextIOWrapper +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + AnyStr, + Generic, + overload, +) + +from .. import to_thread +from .._core._fileio import AsyncFile +from ..lowlevel import checkpoint_if_cancelled +from ._tasks import CancelScope + +if TYPE_CHECKING: + from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer + + +class TemporaryFile(Generic[AnyStr]): + """ + An asynchronous temporary file that is automatically created and cleaned up. + + This class provides an asynchronous context manager interface to a temporary file. + The file is created using Python's standard `tempfile.TemporaryFile` function in a + background thread, and is wrapped as an asynchronous file using `AsyncFile`. + + :param mode: The mode in which the file is opened. Defaults to "w+b". + :param buffering: The buffering policy (-1 means the default buffering). + :param encoding: The encoding used to decode or encode the file. Only applicable in + text mode. + :param newline: Controls how universal newlines mode works (only applicable in text + mode). + :param suffix: The suffix for the temporary file name. + :param prefix: The prefix for the temporary file name. + :param dir: The directory in which the temporary file is created. + :param errors: The error handling scheme used for encoding/decoding errors. + """ + + _async_file: AsyncFile[AnyStr] + + @overload + def __init__( + self: TemporaryFile[bytes], + mode: OpenBinaryMode = ..., + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + *, + errors: str | None = ..., + ): ... + @overload + def __init__( + self: TemporaryFile[str], + mode: OpenTextMode, + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + *, + errors: str | None = ..., + ): ... + + def __init__( + self, + mode: OpenTextMode | OpenBinaryMode = "w+b", + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + *, + errors: str | None = None, + ) -> None: + self.mode = mode + self.buffering = buffering + self.encoding = encoding + self.newline = newline + self.suffix: str | None = suffix + self.prefix: str | None = prefix + self.dir: str | None = dir + self.errors = errors + + async def __aenter__(self) -> AsyncFile[AnyStr]: + fp = await to_thread.run_sync( + lambda: tempfile.TemporaryFile( + self.mode, + self.buffering, + self.encoding, + self.newline, + self.suffix, + self.prefix, + self.dir, + errors=self.errors, + ) + ) + self._async_file = AsyncFile(fp) + return self._async_file + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + await self._async_file.aclose() + + +class NamedTemporaryFile(Generic[AnyStr]): + """ + An asynchronous named temporary file that is automatically created and cleaned up. + + This class provides an asynchronous context manager for a temporary file with a + visible name in the file system. It uses Python's standard + :func:`~tempfile.NamedTemporaryFile` function and wraps the file object with + :class:`AsyncFile` for asynchronous operations. + + :param mode: The mode in which the file is opened. Defaults to "w+b". + :param buffering: The buffering policy (-1 means the default buffering). + :param encoding: The encoding used to decode or encode the file. Only applicable in + text mode. + :param newline: Controls how universal newlines mode works (only applicable in text + mode). + :param suffix: The suffix for the temporary file name. + :param prefix: The prefix for the temporary file name. + :param dir: The directory in which the temporary file is created. + :param delete: Whether to delete the file when it is closed. + :param errors: The error handling scheme used for encoding/decoding errors. + :param delete_on_close: (Python 3.12+) Whether to delete the file on close. + """ + + _async_file: AsyncFile[AnyStr] + + @overload + def __init__( + self: NamedTemporaryFile[bytes], + mode: OpenBinaryMode = ..., + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + delete: bool = ..., + *, + errors: str | None = ..., + delete_on_close: bool = ..., + ): ... + @overload + def __init__( + self: NamedTemporaryFile[str], + mode: OpenTextMode, + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + delete: bool = ..., + *, + errors: str | None = ..., + delete_on_close: bool = ..., + ): ... + + def __init__( + self, + mode: OpenBinaryMode | OpenTextMode = "w+b", + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + delete: bool = True, + *, + errors: str | None = None, + delete_on_close: bool = True, + ) -> None: + self._params: dict[str, Any] = { + "mode": mode, + "buffering": buffering, + "encoding": encoding, + "newline": newline, + "suffix": suffix, + "prefix": prefix, + "dir": dir, + "delete": delete, + "errors": errors, + } + if sys.version_info >= (3, 12): + self._params["delete_on_close"] = delete_on_close + + async def __aenter__(self) -> AsyncFile[AnyStr]: + fp = await to_thread.run_sync( + lambda: tempfile.NamedTemporaryFile(**self._params) + ) + self._async_file = AsyncFile(fp) + return self._async_file + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + await self._async_file.aclose() + + +class SpooledTemporaryFile(AsyncFile[AnyStr]): + """ + An asynchronous spooled temporary file that starts in memory and is spooled to disk. + + This class provides an asynchronous interface to a spooled temporary file, much like + Python's standard :class:`~tempfile.SpooledTemporaryFile`. It supports asynchronous + write operations and provides a method to force a rollover to disk. + + :param max_size: Maximum size in bytes before the file is rolled over to disk. + :param mode: The mode in which the file is opened. Defaults to "w+b". + :param buffering: The buffering policy (-1 means the default buffering). + :param encoding: The encoding used to decode or encode the file (text mode only). + :param newline: Controls how universal newlines mode works (text mode only). + :param suffix: The suffix for the temporary file name. + :param prefix: The prefix for the temporary file name. + :param dir: The directory in which the temporary file is created. + :param errors: The error handling scheme used for encoding/decoding errors. + """ + + _rolled: bool = False + + @overload + def __init__( + self: SpooledTemporaryFile[bytes], + max_size: int = ..., + mode: OpenBinaryMode = ..., + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + *, + errors: str | None = ..., + ): ... + @overload + def __init__( + self: SpooledTemporaryFile[str], + max_size: int = ..., + mode: OpenTextMode = ..., + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + *, + errors: str | None = ..., + ): ... + + def __init__( + self, + max_size: int = 0, + mode: OpenBinaryMode | OpenTextMode = "w+b", + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + *, + errors: str | None = None, + ) -> None: + self._tempfile_params: dict[str, Any] = { + "mode": mode, + "buffering": buffering, + "encoding": encoding, + "newline": newline, + "suffix": suffix, + "prefix": prefix, + "dir": dir, + "errors": errors, + } + self._max_size = max_size + if "b" in mode: + super().__init__(BytesIO()) # type: ignore[arg-type] + else: + super().__init__( + TextIOWrapper( # type: ignore[arg-type] + BytesIO(), + encoding=encoding, + errors=errors, + newline=newline, + write_through=True, + ) + ) + + async def aclose(self) -> None: + if not self._rolled: + self._fp.close() + return + + await super().aclose() + + async def _check(self) -> None: + if self._rolled or self._fp.tell() <= self._max_size: + return + + await self.rollover() + + async def rollover(self) -> None: + if self._rolled: + return + + self._rolled = True + buffer = self._fp + buffer.seek(0) + self._fp = await to_thread.run_sync( + lambda: tempfile.TemporaryFile(**self._tempfile_params) + ) + await self.write(buffer.read()) + buffer.close() + + @property + def closed(self) -> bool: + return self._fp.closed + + async def read(self, size: int = -1) -> AnyStr: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.read(size) + + return await super().read(size) # type: ignore[return-value] + + async def read1(self: SpooledTemporaryFile[bytes], size: int = -1) -> bytes: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.read1(size) + + return await super().read1(size) + + async def readline(self) -> AnyStr: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.readline() + + return await super().readline() # type: ignore[return-value] + + async def readlines(self) -> list[AnyStr]: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.readlines() + + return await super().readlines() # type: ignore[return-value] + + async def readinto(self: SpooledTemporaryFile[bytes], b: WriteableBuffer) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.readinto(b) + + return await super().readinto(b) + + async def readinto1(self: SpooledTemporaryFile[bytes], b: WriteableBuffer) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.readinto1(b) + + return await super().readinto1(b) + + async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.seek(offset, whence) + + return await super().seek(offset, whence) + + async def tell(self) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.tell() + + return await super().tell() + + async def truncate(self, size: int | None = None) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.truncate(size) + + return await super().truncate(size) + + @overload + async def write(self: SpooledTemporaryFile[bytes], b: ReadableBuffer) -> int: ... + @overload + async def write(self: SpooledTemporaryFile[str], b: str) -> int: ... + + async def write(self, b: ReadableBuffer | str) -> int: + """ + Asynchronously write data to the spooled temporary file. + + If the file has not yet been rolled over, the data is written synchronously, + and a rollover is triggered if the size exceeds the maximum size. + + :param s: The data to write. + :return: The number of bytes written. + :raises RuntimeError: If the underlying file is not initialized. + + """ + if not self._rolled: + await checkpoint_if_cancelled() + result = self._fp.write(b) + await self._check() + return result + + return await super().write(b) # type: ignore[misc] + + @overload + async def writelines( + self: SpooledTemporaryFile[bytes], lines: Iterable[ReadableBuffer] + ) -> None: ... + @overload + async def writelines( + self: SpooledTemporaryFile[str], lines: Iterable[str] + ) -> None: ... + + async def writelines(self, lines: Iterable[str] | Iterable[ReadableBuffer]) -> None: + """ + Asynchronously write a list of lines to the spooled temporary file. + + If the file has not yet been rolled over, the lines are written synchronously, + and a rollover is triggered if the size exceeds the maximum size. + + :param lines: An iterable of lines to write. + :raises RuntimeError: If the underlying file is not initialized. + + """ + if not self._rolled: + await checkpoint_if_cancelled() + result = self._fp.writelines(lines) + await self._check() + return result + + return await super().writelines(lines) # type: ignore[misc] + + +class TemporaryDirectory(Generic[AnyStr]): + """ + An asynchronous temporary directory that is created and cleaned up automatically. + + This class provides an asynchronous context manager for creating a temporary + directory. It wraps Python's standard :class:`~tempfile.TemporaryDirectory` to + perform directory creation and cleanup operations in a background thread. + + :param suffix: Suffix to be added to the temporary directory name. + :param prefix: Prefix to be added to the temporary directory name. + :param dir: The parent directory where the temporary directory is created. + :param ignore_cleanup_errors: Whether to ignore errors during cleanup + :param delete: Whether to delete the directory upon closing (Python 3.12+). + """ + + def __init__( + self, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: AnyStr | None = None, + *, + ignore_cleanup_errors: bool = False, + delete: bool = True, + ) -> None: + self.suffix: AnyStr | None = suffix + self.prefix: AnyStr | None = prefix + self.dir: AnyStr | None = dir + self.ignore_cleanup_errors = ignore_cleanup_errors + self.delete = delete + + self._tempdir: tempfile.TemporaryDirectory | None = None + + async def __aenter__(self) -> str: + params: dict[str, Any] = { + "suffix": self.suffix, + "prefix": self.prefix, + "dir": self.dir, + "ignore_cleanup_errors": self.ignore_cleanup_errors, + } + if sys.version_info >= (3, 12): + params["delete"] = self.delete + + self._tempdir = await to_thread.run_sync( + lambda: tempfile.TemporaryDirectory(**params) + ) + return await to_thread.run_sync(self._tempdir.__enter__) + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + if self._tempdir is not None: + with CancelScope(shield=True): + await to_thread.run_sync( + self._tempdir.__exit__, exc_type, exc_value, traceback + ) + + async def cleanup(self) -> None: + if self._tempdir is not None: + await to_thread.run_sync(self._tempdir.cleanup) + + +@overload +async def mkstemp( + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + text: bool = False, +) -> tuple[int, str]: ... + + +@overload +async def mkstemp( + suffix: bytes | None = None, + prefix: bytes | None = None, + dir: bytes | None = None, + text: bool = False, +) -> tuple[int, bytes]: ... + + +async def mkstemp( + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: AnyStr | None = None, + text: bool = False, +) -> tuple[int, str | bytes]: + """ + Asynchronously create a temporary file and return an OS-level handle and the file + name. + + This function wraps `tempfile.mkstemp` and executes it in a background thread. + + :param suffix: Suffix to be added to the file name. + :param prefix: Prefix to be added to the file name. + :param dir: Directory in which the temporary file is created. + :param text: Whether the file is opened in text mode. + :return: A tuple containing the file descriptor and the file name. + + """ + return await to_thread.run_sync(tempfile.mkstemp, suffix, prefix, dir, text) + + +@overload +async def mkdtemp( + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, +) -> str: ... + + +@overload +async def mkdtemp( + suffix: bytes | None = None, + prefix: bytes | None = None, + dir: bytes | None = None, +) -> bytes: ... + + +async def mkdtemp( + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: AnyStr | None = None, +) -> str | bytes: + """ + Asynchronously create a temporary directory and return its path. + + This function wraps `tempfile.mkdtemp` and executes it in a background thread. + + :param suffix: Suffix to be added to the directory name. + :param prefix: Prefix to be added to the directory name. + :param dir: Parent directory where the temporary directory is created. + :return: The path of the created temporary directory. + + """ + return await to_thread.run_sync(tempfile.mkdtemp, suffix, prefix, dir) + + +async def gettempdir() -> str: + """ + Asynchronously return the name of the directory used for temporary files. + + This function wraps `tempfile.gettempdir` and executes it in a background thread. + + :return: The path of the temporary directory as a string. + + """ + return await to_thread.run_sync(tempfile.gettempdir) + + +async def gettempdirb() -> bytes: + """ + Asynchronously return the name of the directory used for temporary files in bytes. + + This function wraps `tempfile.gettempdirb` and executes it in a background thread. + + :return: The path of the temporary directory as bytes. + + """ + return await to_thread.run_sync(tempfile.gettempdirb) diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_testing.py b/venv/lib/python3.12/site-packages/anyio/_core/_testing.py new file mode 100644 index 0000000..fa2c391 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_testing.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +from collections.abc import Awaitable, Generator +from typing import Any, cast + +from ._eventloop import get_async_backend + + +class TaskInfo: + """ + Represents an asynchronous task. + + :ivar int id: the unique identifier of the task + :ivar parent_id: the identifier of the parent task, if any + :vartype parent_id: Optional[int] + :ivar str name: the description of the task (if any) + :ivar ~collections.abc.Coroutine coro: the coroutine object of the task + """ + + __slots__ = "_name", "coro", "id", "name", "parent_id" + + def __init__( + self, + id: int, + parent_id: int | None, + name: str | None, + coro: Generator[Any, Any, Any] | Awaitable[Any], + ): + func = get_current_task + self._name = f"{func.__module__}.{func.__qualname__}" + self.id: int = id + self.parent_id: int | None = parent_id + self.name: str | None = name + self.coro: Generator[Any, Any, Any] | Awaitable[Any] = coro + + def __eq__(self, other: object) -> bool: + if isinstance(other, TaskInfo): + return self.id == other.id + + return NotImplemented + + def __hash__(self) -> int: + return hash(self.id) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}(id={self.id!r}, name={self.name!r})" + + def has_pending_cancellation(self) -> bool: + """ + Return ``True`` if the task has a cancellation pending, ``False`` otherwise. + + """ + return False + + +def get_current_task() -> TaskInfo: + """ + Return the current task. + + :return: a representation of the current task + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().get_current_task() + + +def get_running_tasks() -> list[TaskInfo]: + """ + Return a list of running tasks in the current event loop. + + :return: a list of task info objects + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return cast("list[TaskInfo]", get_async_backend().get_running_tasks()) + + +async def wait_all_tasks_blocked() -> None: + """Wait until all other tasks are waiting for something.""" + await get_async_backend().wait_all_tasks_blocked() diff --git a/venv/lib/python3.12/site-packages/anyio/_core/_typedattr.py b/venv/lib/python3.12/site-packages/anyio/_core/_typedattr.py new file mode 100644 index 0000000..59d3da9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_core/_typedattr.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +import sys +from collections.abc import Callable, Mapping +from typing import Any, TypeVar, final, overload + +from ._exceptions import TypedAttributeLookupError + +if sys.version_info < (3, 15): + from typing_extensions import sentinel + +T_Attr = TypeVar("T_Attr") +T_Default = TypeVar("T_Default") +undefined = sentinel("undefined") + + +def typed_attribute() -> Any: + """Return a unique object, used to mark typed attributes.""" + return object() + + +class TypedAttributeSet: + """ + Superclass for typed attribute collections. + + Checks that every public attribute of every subclass has a type annotation. + """ + + def __init_subclass__(cls) -> None: + annotations: dict[str, Any] = getattr(cls, "__annotations__", {}) + for attrname in dir(cls): + if not attrname.startswith("_") and attrname not in annotations: + raise TypeError( + f"Attribute {attrname!r} is missing its type annotation" + ) + + super().__init_subclass__() + + +class TypedAttributeProvider: + """Base class for classes that wish to provide typed extra attributes.""" + + @property + def extra_attributes(self) -> Mapping[T_Attr, Callable[[], T_Attr]]: + """ + A mapping of the extra attributes to callables that return the corresponding + values. + + If the provider wraps another provider, the attributes from that wrapper should + also be included in the returned mapping (but the wrapper may override the + callables from the wrapped instance). + + """ + return {} + + @overload + def extra(self, attribute: T_Attr) -> T_Attr: ... + + @overload + def extra(self, attribute: T_Attr, default: T_Default) -> T_Attr | T_Default: ... + + @final + def extra(self, attribute: Any, default: object = undefined) -> object: + """ + extra(attribute, default=undefined) + + Return the value of the given typed extra attribute. + + :param attribute: the attribute (member of a :class:`~TypedAttributeSet`) to + look for + :param default: the value that should be returned if no value is found for the + attribute + :raises ~anyio.TypedAttributeLookupError: if the search failed and no default + value was given + + """ + try: + getter = self.extra_attributes[attribute] + except KeyError: + if default is undefined: + raise TypedAttributeLookupError("Attribute not found") from None + else: + return default + + return getter() diff --git a/venv/lib/python3.12/site-packages/anyio/_lazyimport.py b/venv/lib/python3.12/site-packages/anyio/_lazyimport.py new file mode 100644 index 0000000..dc0a4fa --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/_lazyimport.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +__all__ = ( + "fix_package_names", + "install_lazy_importer", + "set_deprecated_aliases", +) + +import ast +import inspect +import sys +import warnings +from importlib import import_module +from types import ModuleType +from typing import Any + + +def install_lazy_importer() -> bool: + module_globals = sys._getframe(1).f_globals + module_name = module_globals["__name__"] + module_prefix = module_name + "." + module = sys.modules[module_name] + lazy_map, deprecated_aliases, submodule_names = _build_lazy_map(module) + names = sorted(lazy_map) + + # Delete symbols that are not part of the API + del module_globals["TYPE_CHECKING"] + del module_globals["install_lazy_importer"] + + if not lazy_map and not deprecated_aliases: + return False + + def __getattr__(name: str) -> Any: + if new_name := deprecated_aliases.get(name): + emit_deprecation_warning(module_name, name, new_name) + target_mod, target_attr = new_name.rsplit(".", 1) + elif name in submodule_names: + target_mod, target_attr = "." + name, "" + else: + try: + target_mod, target_attr = lazy_map[name] + except KeyError: + raise AttributeError( + f"module {module_name!r} has no attribute {name!r}" + ) from None + + imported = import_module(target_mod, module_name) + value = getattr(imported, target_attr) if target_attr else imported + + # patch the module name to match + if ( + getattr(value, "__module__", "").startswith(module_prefix) + and name not in deprecated_aliases + ): + value.__module__ = module_name + + module_globals[name] = value + return value + + def __dir__() -> list[str]: + return names + + module_globals["__dir__"] = __dir__ + module_globals["__getattr__"] = __getattr__ + module_globals.pop("fix_package_names", None) + module_globals.pop("set_deprecated_aliases", None) + return True + + +def fix_package_names() -> None: + module_globals = sys._getframe(1).f_globals + module_prefix = module_globals["__name__"] + "." + del module_globals[fix_package_names.__name__] + for value in module_globals.values(): + if modname := getattr(value, "__module__", ""): + if modname.startswith(module_prefix): + parts = modname.split(".") + value.__module__ = ".".join( + part for part in parts if not part.startswith("_") + ) + + +def emit_deprecation_warning(module_name: str, name: str, target: str) -> None: + warnings.warn( + f"The {module_name}.{name} alias is deprecated, use {target} instead.", + DeprecationWarning, + stacklevel=3, + ) + + +def set_deprecated_aliases(aliases: dict[str, str]) -> None: + module_globals = sys._getframe(1).f_globals + module_name = module_globals["__name__"] + del module_globals[set_deprecated_aliases.__name__] + + def __getattr__(name: str) -> Any: + try: + target = aliases[name] + except KeyError: + raise AttributeError( + f"module {module_name!r} has no attribute {name!r}" + ) from None + + emit_deprecation_warning(module_name, name, target) + target_modname, attrname = target.rsplit(".", 1) + module = import_module(target_modname) + return getattr(module, attrname) + + sys.modules[module_name].__dict__["__getattr__"] = __getattr__ + + +def _build_lazy_map( + module: ModuleType, +) -> tuple[dict[str, tuple[str, str]], dict[str, str], list[str]]: + try: + source = inspect.getsource(module) + except OSError: + return {}, {}, [] + + tree = compile(source, module.__file__ or "", "exec", ast.PyCF_ONLY_AST) + assert isinstance(tree, ast.Module) + out: dict[str, tuple[str, str]] = {} + deprecated_aliases: dict[str, str] = {} + submodule_names: list[str] = [] + + for node in tree.body: + if not isinstance(node, ast.If) or not _is_type_checking_block(node.test): + continue + + for stmt in node.body: + match stmt: + case ast.ImportFrom(): + if stmt.module is None: + submodule_names.extend(alias.name for alias in stmt.names) + else: + base = "." * stmt.level + (stmt.module or "") + for alias in stmt.names: + if alias.name == "*": + raise RuntimeError("star imports not supported") + + exported = alias.asname or alias.name + out[exported] = (base, alias.name) + case ast.Expr() if isinstance(stmt.value, ast.Call): + call = stmt.value + if ( + isinstance(call.func, ast.Name) + and call.func.id == "set_deprecated_aliases" + ): + arg0 = call.args[0] + assert isinstance(arg0, ast.Dict) + for key, value in zip(arg0.keys, arg0.values, strict=True): + assert isinstance(key, ast.Constant) + assert isinstance(key.value, str) + assert isinstance(value, ast.Constant) + assert isinstance(value.value, str) + deprecated_aliases[key.value] = value.value + + return out, deprecated_aliases, submodule_names + + +def _is_type_checking_block(test: ast.AST) -> bool: + if not isinstance(test, ast.BoolOp): + return False + + subtest = test.values[0] + match subtest: + case ast.Name(): + return subtest.id == "TYPE_CHECKING" + case ast.Attribute(): + return ( + isinstance(subtest.value, ast.Name) + and subtest.value.id == "typing" + and subtest.attr == "TYPE_CHECKING" + ) + case _: + return False diff --git a/venv/lib/python3.12/site-packages/anyio/abc/__init__.py b/venv/lib/python3.12/site-packages/anyio/abc/__init__.py new file mode 100644 index 0000000..ba738f8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/abc/__init__.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .._lazyimport import ( + fix_package_names, + install_lazy_importer, + set_deprecated_aliases, +) + +if TYPE_CHECKING or not install_lazy_importer(): + from ._eventloop import AsyncBackend as AsyncBackend + from ._resources import AsyncResource as AsyncResource + from ._sockets import ConnectedUDPSocket as ConnectedUDPSocket + from ._sockets import ConnectedUNIXDatagramSocket as ConnectedUNIXDatagramSocket + from ._sockets import IPAddressType as IPAddressType + from ._sockets import IPSockAddrType as IPSockAddrType + from ._sockets import SocketAttribute as SocketAttribute + from ._sockets import SocketListener as SocketListener + from ._sockets import SocketStream as SocketStream + from ._sockets import UDPPacketType as UDPPacketType + from ._sockets import UDPSocket as UDPSocket + from ._sockets import UNIXDatagramPacketType as UNIXDatagramPacketType + from ._sockets import UNIXDatagramSocket as UNIXDatagramSocket + from ._sockets import UNIXSocketStream as UNIXSocketStream + from ._streams import AnyByteReceiveStream as AnyByteReceiveStream + from ._streams import AnyByteSendStream as AnyByteSendStream + from ._streams import AnyByteStream as AnyByteStream + from ._streams import AnyByteStreamConnectable as AnyByteStreamConnectable + from ._streams import ( + AnyUnreliableByteReceiveStream as AnyUnreliableByteReceiveStream, + ) + from ._streams import AnyUnreliableByteSendStream as AnyUnreliableByteSendStream + from ._streams import AnyUnreliableByteStream as AnyUnreliableByteStream + from ._streams import ByteReceiveStream as ByteReceiveStream + from ._streams import ByteSendStream as ByteSendStream + from ._streams import ByteStream as ByteStream + from ._streams import ByteStreamConnectable as ByteStreamConnectable + from ._streams import Listener as Listener + from ._streams import ObjectReceiveStream as ObjectReceiveStream + from ._streams import ObjectSendStream as ObjectSendStream + from ._streams import ObjectStream as ObjectStream + from ._streams import ObjectStreamConnectable as ObjectStreamConnectable + from ._streams import UnreliableObjectReceiveStream as UnreliableObjectReceiveStream + from ._streams import UnreliableObjectSendStream as UnreliableObjectSendStream + from ._streams import UnreliableObjectStream as UnreliableObjectStream + from ._subprocesses import Process as Process + from ._tasks import TaskGroup as TaskGroup + from ._tasks import TaskStatus as TaskStatus + from ._testing import TestRunner as TestRunner + + fix_package_names() + set_deprecated_aliases( + { + "CapacityLimiter": "anyio.CapacityLimiter", + "Condition": "anyio.Condition", + "Event": "anyio.Event", + "Lock": "anyio.Lock", + "Semaphore": "anyio.Semaphore", + "CancelScope": "anyio.CancelScope", + "BlockingPortal": "anyio.from_thread.BlockingPortal", + } + ) diff --git a/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..50bd2a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_eventloop.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_eventloop.cpython-312.pyc new file mode 100644 index 0000000..ecd95af Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_eventloop.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_resources.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_resources.cpython-312.pyc new file mode 100644 index 0000000..b8bc35a Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_resources.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_sockets.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_sockets.cpython-312.pyc new file mode 100644 index 0000000..2d7f392 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_sockets.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_streams.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_streams.cpython-312.pyc new file mode 100644 index 0000000..3acb86b Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_streams.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_subprocesses.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_subprocesses.cpython-312.pyc new file mode 100644 index 0000000..5772fdf Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_subprocesses.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_tasks.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_tasks.cpython-312.pyc new file mode 100644 index 0000000..74b7699 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_tasks.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_testing.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_testing.cpython-312.pyc new file mode 100644 index 0000000..1d31cc2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/abc/__pycache__/_testing.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/abc/_eventloop.py b/venv/lib/python3.12/site-packages/anyio/abc/_eventloop.py new file mode 100644 index 0000000..a3cbed2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/abc/_eventloop.py @@ -0,0 +1,428 @@ +from __future__ import annotations + +import math +import sys +from abc import ABCMeta, abstractmethod +from collections.abc import ( + AsyncIterator, + Awaitable, + Callable, + Coroutine, + Iterable, + Mapping, + Sequence, +) +from contextlib import AbstractContextManager +from os import PathLike +from signal import Signals +from socket import AddressFamily, SocketKind, socket +from typing import ( + IO, + TYPE_CHECKING, + Any, + TypeAlias, + TypeVar, + overload, +) + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if TYPE_CHECKING: + from _typeshed import FileDescriptorLike + + from .._core._synchronization import CapacityLimiter, Event, Lock, Semaphore + from .._core._tasks import CancelScope + from .._core._testing import TaskInfo + from ._sockets import ( + ConnectedUDPSocket, + ConnectedUNIXDatagramSocket, + IPSockAddrType, + SocketListener, + SocketStream, + UDPSocket, + UNIXDatagramSocket, + UNIXSocketStream, + ) + from ._subprocesses import Process + from ._tasks import TaskGroup + from ._testing import TestRunner + +T_Retval = TypeVar("T_Retval") +T_co = TypeVar("T_co", covariant=True) +PosArgsT = TypeVarTuple("PosArgsT") +StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes] + + +class AsyncBackend(metaclass=ABCMeta): + @classmethod + @abstractmethod + def run( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + options: dict[str, Any], + ) -> T_Retval: + """ + Run the given coroutine function in an asynchronous event loop. + + The current thread must not be already running an event loop. + + :param func: a coroutine function + :param args: positional arguments to ``func`` + :param kwargs: positional arguments to ``func`` + :param options: keyword arguments to call the backend ``run()`` implementation + with + :return: the return value of the coroutine function + """ + + @classmethod + @abstractmethod + def current_token(cls) -> object: + """ + Return an object that allows other threads to run code inside the event loop. + + :return: a token object, specific to the event loop running in the current + thread + """ + + @classmethod + @abstractmethod + def current_time(cls) -> float: + """ + Return the current value of the event loop's internal clock. + + :return: the clock value (seconds) + """ + + @classmethod + @abstractmethod + def cancelled_exception_class(cls) -> type[BaseException]: + """Return the exception class that is raised in a task if it's cancelled.""" + + @classmethod + @abstractmethod + async def checkpoint(cls) -> None: + """ + Check if the task has been cancelled, and allow rescheduling of other tasks. + + This is effectively the same as running :meth:`checkpoint_if_cancelled` and then + :meth:`cancel_shielded_checkpoint`. + """ + + @classmethod + async def checkpoint_if_cancelled(cls) -> None: + """ + Check if the current task group has been cancelled. + + This will check if the task has been cancelled, but will not allow other tasks + to be scheduled if not. + + """ + if cls.current_effective_deadline() == -math.inf: + await cls.checkpoint() + + @classmethod + async def cancel_shielded_checkpoint(cls) -> None: + """ + Allow the rescheduling of other tasks. + + This will give other tasks the opportunity to run, but without checking if the + current task group has been cancelled, unlike with :meth:`checkpoint`. + + """ + with cls.create_cancel_scope(shield=True): + await cls.sleep(0) + + @classmethod + @abstractmethod + async def sleep(cls, delay: float) -> None: + """ + Pause the current task for the specified duration. + + :param delay: the duration, in seconds + """ + + @classmethod + @abstractmethod + def create_cancel_scope( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + pass + + @classmethod + @abstractmethod + def current_effective_deadline(cls) -> float: + """ + Return the nearest deadline among all the cancel scopes effective for the + current task. + + :return: + - a clock value from the event loop's internal clock + - ``inf`` if there is no deadline in effect + - ``-inf`` if the current scope has been cancelled + :rtype: float + """ + + @classmethod + @abstractmethod + def create_task_group(cls) -> TaskGroup: + pass + + @classmethod + @abstractmethod + def create_event(cls) -> Event: + pass + + @classmethod + @abstractmethod + def create_lock(cls, *, fast_acquire: bool) -> Lock: + pass + + @classmethod + @abstractmethod + def create_semaphore( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> Semaphore: + pass + + @classmethod + @abstractmethod + def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter: + pass + + @classmethod + @abstractmethod + async def run_sync_in_worker_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + abandon_on_cancel: bool = False, + limiter: CapacityLimiter | None = None, + ) -> T_Retval: + pass + + @classmethod + @abstractmethod + def check_cancelled(cls) -> None: + pass + + @classmethod + @abstractmethod + def run_async_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_co: + pass + + @classmethod + @abstractmethod + def run_sync_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + pass + + @classmethod + @abstractmethod + async def open_process( + cls, + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + cwd: StrOrBytesPath | None = None, + env: Mapping[str, str] | None = None, + startupinfo: Any = None, + creationflags: int = 0, + start_new_session: bool = False, + pass_fds: Sequence[int] = (), + user: str | int | None = None, + group: str | int | None = None, + extra_groups: Iterable[str | int] | None = None, + umask: int = -1, + **kwargs: Any, + ) -> Process: + pass + + @classmethod + @abstractmethod + def setup_process_pool_exit_at_shutdown(cls, workers: set[Process]) -> None: + pass + + @classmethod + @abstractmethod + async def connect_tcp( + cls, host: str, port: int, local_address: IPSockAddrType | None = None + ) -> SocketStream: + pass + + @classmethod + @abstractmethod + async def connect_unix(cls, path: str | bytes) -> UNIXSocketStream: + pass + + @classmethod + @abstractmethod + def create_tcp_listener(cls, sock: socket) -> SocketListener: + pass + + @classmethod + @abstractmethod + def create_unix_listener(cls, sock: socket) -> SocketListener: + pass + + @classmethod + @abstractmethod + async def create_udp_socket( + cls, + family: AddressFamily, + local_address: IPSockAddrType | None, + remote_address: IPSockAddrType | None, + reuse_port: bool, + ) -> UDPSocket | ConnectedUDPSocket: + pass + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket, remote_path: None + ) -> UNIXDatagramSocket: ... + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket, remote_path: str | bytes + ) -> ConnectedUNIXDatagramSocket: ... + + @classmethod + @abstractmethod + async def create_unix_datagram_socket( + cls, raw_socket: socket, remote_path: str | bytes | None + ) -> UNIXDatagramSocket | ConnectedUNIXDatagramSocket: + pass + + @classmethod + @abstractmethod + async def getaddrinfo( + cls, + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, + ) -> Sequence[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], + ] + ]: + pass + + @classmethod + @abstractmethod + async def getnameinfo( + cls, sockaddr: IPSockAddrType, flags: int = 0 + ) -> tuple[str, str]: + pass + + @classmethod + @abstractmethod + async def wait_readable(cls, obj: FileDescriptorLike) -> None: + pass + + @classmethod + @abstractmethod + async def wait_writable(cls, obj: FileDescriptorLike) -> None: + pass + + @classmethod + @abstractmethod + def notify_closing(cls, obj: FileDescriptorLike) -> None: + pass + + @classmethod + @abstractmethod + async def wrap_listener_socket(cls, sock: socket) -> SocketListener: + pass + + @classmethod + @abstractmethod + async def wrap_stream_socket(cls, sock: socket) -> SocketStream: + pass + + @classmethod + @abstractmethod + async def wrap_unix_stream_socket(cls, sock: socket) -> UNIXSocketStream: + pass + + @classmethod + @abstractmethod + async def wrap_udp_socket(cls, sock: socket) -> UDPSocket: + pass + + @classmethod + @abstractmethod + async def wrap_connected_udp_socket(cls, sock: socket) -> ConnectedUDPSocket: + pass + + @classmethod + @abstractmethod + async def wrap_unix_datagram_socket(cls, sock: socket) -> UNIXDatagramSocket: + pass + + @classmethod + @abstractmethod + async def wrap_connected_unix_datagram_socket( + cls, sock: socket + ) -> ConnectedUNIXDatagramSocket: + pass + + @classmethod + @abstractmethod + def current_default_thread_limiter(cls) -> CapacityLimiter: + pass + + @classmethod + @abstractmethod + def open_signal_receiver( + cls, *signals: Signals + ) -> AbstractContextManager[AsyncIterator[Signals]]: + pass + + @classmethod + @abstractmethod + def get_current_task(cls) -> TaskInfo: + pass + + @classmethod + @abstractmethod + def get_running_tasks(cls) -> Sequence[TaskInfo]: + pass + + @classmethod + @abstractmethod + async def wait_all_tasks_blocked(cls) -> None: + pass + + @classmethod + @abstractmethod + def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: + pass diff --git a/venv/lib/python3.12/site-packages/anyio/abc/_resources.py b/venv/lib/python3.12/site-packages/anyio/abc/_resources.py new file mode 100644 index 0000000..9373f71 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/abc/_resources.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +import sys +from abc import ABCMeta, abstractmethod +from types import TracebackType +from typing import TypeVar + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self + +T = TypeVar("T") + + +class AsyncResource(metaclass=ABCMeta): + """ + Abstract base class for all closeable asynchronous resources. + + Works as an asynchronous context manager which returns the instance itself on enter, + and calls :meth:`aclose` on exit. + """ + + __slots__ = () + + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.aclose() + + @abstractmethod + async def aclose(self) -> None: + """Close the resource.""" diff --git a/venv/lib/python3.12/site-packages/anyio/abc/_sockets.py b/venv/lib/python3.12/site-packages/anyio/abc/_sockets.py new file mode 100644 index 0000000..d6a1139 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/abc/_sockets.py @@ -0,0 +1,401 @@ +from __future__ import annotations + +import errno +import socket +from abc import abstractmethod +from collections.abc import Callable, Collection, Mapping +from contextlib import AsyncExitStack +from io import IOBase +from ipaddress import IPv4Address, IPv6Address +from socket import AddressFamily +from typing import TYPE_CHECKING, Any, TypeAlias, TypeVar + +from .._core._eventloop import get_async_backend +from .._core._typedattr import ( + TypedAttributeProvider, + TypedAttributeSet, + typed_attribute, +) +from ._streams import ByteStream, Listener, UnreliableObjectStream + +if TYPE_CHECKING: + from ._tasks import TaskGroup + +IPAddressType: TypeAlias = str | IPv4Address | IPv6Address +IPSockAddrType: TypeAlias = tuple[str, int] +SockAddrType: TypeAlias = IPSockAddrType | str +UDPPacketType: TypeAlias = tuple[bytes, IPSockAddrType] +UNIXDatagramPacketType: TypeAlias = tuple[bytes, str] +T_Retval = TypeVar("T_Retval") + + +def _validate_socket( + sock_or_fd: socket.socket | int, + sock_type: socket.SocketKind, + addr_family: socket.AddressFamily = socket.AF_UNSPEC, + *, + require_connected: bool = False, + require_bound: bool = False, +) -> socket.socket: + if isinstance(sock_or_fd, int): + try: + sock = socket.socket(fileno=sock_or_fd) + except OSError as exc: + if exc.errno == errno.ENOTSOCK: + raise ValueError( + "the file descriptor does not refer to a socket" + ) from exc + elif require_connected: + raise ValueError("the socket must be connected") from exc + elif require_bound: + raise ValueError("the socket must be bound to a local address") from exc + else: + raise + elif isinstance(sock_or_fd, socket.socket): + sock = sock_or_fd + else: + raise TypeError( + f"expected an int or socket, got {type(sock_or_fd).__qualname__} instead" + ) + + try: + if require_connected: + try: + sock.getpeername() + except OSError as exc: + raise ValueError("the socket must be connected") from exc + + if require_bound: + try: + if sock.family in (socket.AF_INET, socket.AF_INET6): + bound_addr = sock.getsockname()[1] + else: + bound_addr = sock.getsockname() + except OSError: + bound_addr = None + + if not bound_addr: + raise ValueError("the socket must be bound to a local address") + + if addr_family != socket.AF_UNSPEC and sock.family != addr_family: + raise ValueError( + f"address family mismatch: expected {addr_family.name}, got " + f"{sock.family.name}" + ) + + if sock.type != sock_type: + raise ValueError( + f"socket type mismatch: expected {sock_type.name}, got {sock.type.name}" + ) + except BaseException: + # Avoid ResourceWarning from the locally constructed socket object + if isinstance(sock_or_fd, int): + sock.detach() + + raise + + sock.setblocking(False) + return sock + + +class SocketAttribute(TypedAttributeSet): + """ + .. attribute:: family + :type: socket.AddressFamily + + the address family of the underlying socket + + .. attribute:: local_address + :type: tuple[str, int] | str + + the local address the underlying socket is connected to + + .. attribute:: local_port + :type: int + + for IP based sockets, the local port the underlying socket is bound to + + .. attribute:: raw_socket + :type: socket.socket + + the underlying stdlib socket object + + .. attribute:: remote_address + :type: tuple[str, int] | str + + the remote address the underlying socket is connected to + + .. attribute:: remote_port + :type: int + + for IP based sockets, the remote port the underlying socket is connected to + """ + + family: AddressFamily = typed_attribute() + local_address: SockAddrType = typed_attribute() + local_port: int = typed_attribute() + raw_socket: socket.socket = typed_attribute() + remote_address: SockAddrType = typed_attribute() + remote_port: int = typed_attribute() + + +class _SocketProvider(TypedAttributeProvider): + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + from .._core._sockets import convert_ipv6_sockaddr as convert + + attributes: dict[Any, Callable[[], Any]] = { + SocketAttribute.family: lambda: self._raw_socket.family, + SocketAttribute.local_address: lambda: convert( + self._raw_socket.getsockname() + ), + SocketAttribute.raw_socket: lambda: self._raw_socket, + } + try: + peername: tuple[str, int] | None = convert(self._raw_socket.getpeername()) + except OSError: + peername = None + + # Provide the remote address for connected sockets + if peername is not None: + attributes[SocketAttribute.remote_address] = lambda: peername + + # Provide local and remote ports for IP based sockets + if self._raw_socket.family in (AddressFamily.AF_INET, AddressFamily.AF_INET6): + attributes[SocketAttribute.local_port] = lambda: ( + self._raw_socket.getsockname()[1] + ) + if peername is not None: + remote_port = peername[1] + attributes[SocketAttribute.remote_port] = lambda: remote_port + + return attributes + + @property + @abstractmethod + def _raw_socket(self) -> socket.socket: + pass + + +class SocketStream(ByteStream, _SocketProvider): + """ + Transports bytes over a socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @classmethod + async def from_socket(cls, sock_or_fd: socket.socket | int) -> SocketStream: + """ + Wrap an existing socket object or file descriptor as a socket stream. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must already be connected. + + :param sock_or_fd: a socket object or file descriptor + :return: a socket stream + + """ + sock = _validate_socket(sock_or_fd, socket.SOCK_STREAM, require_connected=True) + return await get_async_backend().wrap_stream_socket(sock) + + +class UNIXSocketStream(SocketStream): + @classmethod + async def from_socket(cls, sock_or_fd: socket.socket | int) -> UNIXSocketStream: + """ + Wrap an existing socket object or file descriptor as a UNIX socket stream. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must already be connected. + + :param sock_or_fd: a socket object or file descriptor + :return: a UNIX socket stream + + """ + sock = _validate_socket( + sock_or_fd, socket.SOCK_STREAM, socket.AF_UNIX, require_connected=True + ) + return await get_async_backend().wrap_unix_stream_socket(sock) + + @abstractmethod + async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: + """ + Send file descriptors along with a message to the peer. + + :param message: a non-empty bytestring + :param fds: a collection of files (either numeric file descriptors or open file + or socket objects) + """ + + @abstractmethod + async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: + """ + Receive file descriptors along with a message from the peer. + + :param msglen: length of the message to expect from the peer + :param maxfds: maximum number of file descriptors to expect from the peer + :return: a tuple of (message, file descriptors) + """ + + +class SocketListener(Listener[SocketStream], _SocketProvider): + """ + Listens to incoming socket connections. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @classmethod + async def from_socket( + cls, + sock_or_fd: socket.socket | int, + ) -> SocketListener: + """ + Wrap an existing socket object or file descriptor as a socket listener. + + The newly created listener takes ownership of the socket being passed in. + + :param sock_or_fd: a socket object or file descriptor + :return: a socket listener + + """ + sock = _validate_socket(sock_or_fd, socket.SOCK_STREAM, require_bound=True) + return await get_async_backend().wrap_listener_socket(sock) + + @abstractmethod + async def accept(self) -> SocketStream: + """Accept an incoming connection.""" + + async def serve( + self, + handler: Callable[[SocketStream], Any], + task_group: TaskGroup | None = None, + ) -> None: + from .. import create_task_group + + async with AsyncExitStack() as stack: + if task_group is None: + task_group = await stack.enter_async_context(create_task_group()) + + while True: + stream = await self.accept() + task_group.start_soon(handler, stream) + + +class UDPSocket(UnreliableObjectStream[UDPPacketType], _SocketProvider): + """ + Represents an unconnected UDP socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @classmethod + async def from_socket(cls, sock_or_fd: socket.socket | int) -> UDPSocket: + """ + Wrap an existing socket object or file descriptor as a UDP socket. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must be bound to a local address. + + :param sock_or_fd: a socket object or file descriptor + :return: a UDP socket + + """ + sock = _validate_socket(sock_or_fd, socket.SOCK_DGRAM, require_bound=True) + return await get_async_backend().wrap_udp_socket(sock) + + async def sendto(self, data: bytes, host: str, port: int) -> None: + """ + Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, (host, port))). + + """ + return await self.send((data, (host, port))) + + +class ConnectedUDPSocket(UnreliableObjectStream[bytes], _SocketProvider): + """ + Represents an connected UDP socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @classmethod + async def from_socket(cls, sock_or_fd: socket.socket | int) -> ConnectedUDPSocket: + """ + Wrap an existing socket object or file descriptor as a connected UDP socket. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must already be connected. + + :param sock_or_fd: a socket object or file descriptor + :return: a connected UDP socket + + """ + sock = _validate_socket( + sock_or_fd, + socket.SOCK_DGRAM, + require_connected=True, + ) + return await get_async_backend().wrap_connected_udp_socket(sock) + + +class UNIXDatagramSocket( + UnreliableObjectStream[UNIXDatagramPacketType], _SocketProvider +): + """ + Represents an unconnected Unix datagram socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @classmethod + async def from_socket( + cls, + sock_or_fd: socket.socket | int, + ) -> UNIXDatagramSocket: + """ + Wrap an existing socket object or file descriptor as a UNIX datagram + socket. + + The newly created socket wrapper takes ownership of the socket being passed in. + + :param sock_or_fd: a socket object or file descriptor + :return: a UNIX datagram socket + + """ + sock = _validate_socket(sock_or_fd, socket.SOCK_DGRAM, socket.AF_UNIX) + return await get_async_backend().wrap_unix_datagram_socket(sock) + + async def sendto(self, data: bytes, path: str) -> None: + """Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, path)).""" + return await self.send((data, path)) + + +class ConnectedUNIXDatagramSocket(UnreliableObjectStream[bytes], _SocketProvider): + """ + Represents a connected Unix datagram socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @classmethod + async def from_socket( + cls, + sock_or_fd: socket.socket | int, + ) -> ConnectedUNIXDatagramSocket: + """ + Wrap an existing socket object or file descriptor as a connected UNIX datagram + socket. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must already be connected. + + :param sock_or_fd: a socket object or file descriptor + :return: a connected UNIX datagram socket + + """ + sock = _validate_socket( + sock_or_fd, socket.SOCK_DGRAM, socket.AF_UNIX, require_connected=True + ) + return await get_async_backend().wrap_connected_unix_datagram_socket(sock) diff --git a/venv/lib/python3.12/site-packages/anyio/abc/_streams.py b/venv/lib/python3.12/site-packages/anyio/abc/_streams.py new file mode 100644 index 0000000..daf335d --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/abc/_streams.py @@ -0,0 +1,237 @@ +from __future__ import annotations + +from abc import ABCMeta, abstractmethod +from collections.abc import Callable +from typing import TYPE_CHECKING, Any, Generic, TypeAlias, TypeVar + +from .._core._exceptions import EndOfStream +from .._core._typedattr import TypedAttributeProvider +from ._resources import AsyncResource + +if TYPE_CHECKING: + from ._tasks import TaskGroup + +T_Item = TypeVar("T_Item") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class UnreliableObjectReceiveStream( + AsyncResource, TypedAttributeProvider, Generic[T_co] +): + """ + An interface for receiving objects. + + This interface makes no guarantees that the received messages arrive in the order in + which they were sent, or that no messages are missed. + + Asynchronously iterating over objects of this type will yield objects matching the + given type parameter. + """ + + def __aiter__(self) -> UnreliableObjectReceiveStream[T_co]: + return self + + async def __anext__(self) -> T_co: + try: + return await self.receive() + except EndOfStream: + raise StopAsyncIteration from None + + @abstractmethod + async def receive(self) -> T_co: + """ + Receive the next item. + + :raises ~anyio.ClosedResourceError: if the receive stream has been explicitly + closed + :raises ~anyio.EndOfStream: if this stream has been closed from the other end + :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable + due to external causes + """ + + +class UnreliableObjectSendStream( + AsyncResource, TypedAttributeProvider, Generic[T_contra] +): + """ + An interface for sending objects. + + This interface makes no guarantees that the messages sent will reach the + recipient(s) in the same order in which they were sent, or at all. + """ + + @abstractmethod + async def send(self, item: T_contra) -> None: + """ + Send an item to the peer(s). + + :param item: the item to send + :raises ~anyio.ClosedResourceError: if the send stream has been explicitly + closed + :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable + due to external causes + """ + + +class UnreliableObjectStream( + UnreliableObjectReceiveStream[T_Item], UnreliableObjectSendStream[T_Item] +): + """ + A bidirectional message stream which does not guarantee the order or reliability of + message delivery. + """ + + +class ObjectReceiveStream(UnreliableObjectReceiveStream[T_co]): + """ + A receive message stream which guarantees that messages are received in the same + order in which they were sent, and that no messages are missed. + """ + + +class ObjectSendStream(UnreliableObjectSendStream[T_contra]): + """ + A send message stream which guarantees that messages are delivered in the same order + in which they were sent, without missing any messages in the middle. + """ + + +class ObjectStream( + ObjectReceiveStream[T_Item], + ObjectSendStream[T_Item], + UnreliableObjectStream[T_Item], +): + """ + A bidirectional message stream which guarantees the order and reliability of message + delivery. + """ + + @abstractmethod + async def send_eof(self) -> None: + """ + Send an end-of-file indication to the peer. + + You should not try to send any further data to this stream after calling this + method. This method is idempotent (does nothing on successive calls). + """ + + +class ByteReceiveStream(AsyncResource, TypedAttributeProvider): + """ + An interface for receiving bytes from a single peer. + + Iterating this byte stream will yield a byte string of arbitrary length, but no more + than 65536 bytes. + """ + + def __aiter__(self) -> ByteReceiveStream: + return self + + async def __anext__(self) -> bytes: + try: + return await self.receive() + except EndOfStream: + raise StopAsyncIteration from None + + @abstractmethod + async def receive(self, max_bytes: int = 65536) -> bytes: + """ + Receive at most ``max_bytes`` bytes from the peer. + + .. note:: Implementers of this interface should not return an empty + :class:`bytes` object, and users should ignore them. + + :param max_bytes: maximum number of bytes to receive (must be a positive + integer) + :return: the received bytes + :raises ValueError: if ``max_bytes`` is less than 1 + :raises ~anyio.EndOfStream: if this stream has been closed from the other end + """ + + +class ByteSendStream(AsyncResource, TypedAttributeProvider): + """An interface for sending bytes to a single peer.""" + + @abstractmethod + async def send(self, item: bytes) -> None: + """ + Send the given bytes to the peer. + + :param item: the bytes to send + """ + + +class ByteStream(ByteReceiveStream, ByteSendStream): + """A bidirectional byte stream.""" + + @abstractmethod + async def send_eof(self) -> None: + """ + Send an end-of-file indication to the peer. + + You should not try to send any further data to this stream after calling this + method. This method is idempotent (does nothing on successive calls). + """ + + +#: Type alias for all unreliable bytes-oriented receive streams. +AnyUnreliableByteReceiveStream: TypeAlias = ( + UnreliableObjectReceiveStream[bytes] | ByteReceiveStream +) +#: Type alias for all unreliable bytes-oriented send streams. +AnyUnreliableByteSendStream: TypeAlias = ( + UnreliableObjectSendStream[bytes] | ByteSendStream +) +#: Type alias for all unreliable bytes-oriented streams. +AnyUnreliableByteStream: TypeAlias = UnreliableObjectStream[bytes] | ByteStream +#: Type alias for all bytes-oriented receive streams. +AnyByteReceiveStream: TypeAlias = ObjectReceiveStream[bytes] | ByteReceiveStream +#: Type alias for all bytes-oriented send streams. +AnyByteSendStream: TypeAlias = ObjectSendStream[bytes] | ByteSendStream +#: Type alias for all bytes-oriented streams. +AnyByteStream: TypeAlias = ObjectStream[bytes] | ByteStream + + +class Listener(AsyncResource, TypedAttributeProvider, Generic[T_co]): + """An interface for objects that let you accept incoming connections.""" + + @abstractmethod + async def serve( + self, handler: Callable[[T_co], Any], task_group: TaskGroup | None = None + ) -> None: + """ + Accept incoming connections as they come in and start tasks to handle them. + + :param handler: a callable that will be used to handle each accepted connection + :param task_group: the task group that will be used to start tasks for handling + each accepted connection (if omitted, an ad-hoc task group will be created) + """ + + +class ObjectStreamConnectable(Generic[T_co], metaclass=ABCMeta): + @abstractmethod + async def connect(self) -> ObjectStream[T_co]: + """ + Connect to the remote endpoint. + + :return: an object stream connected to the remote end + :raises ConnectionFailed: if the connection fails + """ + + +class ByteStreamConnectable(metaclass=ABCMeta): + @abstractmethod + async def connect(self) -> ByteStream: + """ + Connect to the remote endpoint. + + :return: a bytestream connected to the remote end + :raises ConnectionFailed: if the connection fails + """ + + +#: Type alias for all connectables returning bytestreams or bytes-oriented object streams +AnyByteStreamConnectable: TypeAlias = ( + ObjectStreamConnectable[bytes] | ByteStreamConnectable +) diff --git a/venv/lib/python3.12/site-packages/anyio/abc/_subprocesses.py b/venv/lib/python3.12/site-packages/anyio/abc/_subprocesses.py new file mode 100644 index 0000000..555f15f --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/abc/_subprocesses.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +from abc import abstractmethod +from signal import Signals +from typing import TYPE_CHECKING + +from ._resources import AsyncResource + +if TYPE_CHECKING: + from ._streams import ByteReceiveStream, ByteSendStream + + +class Process(AsyncResource): + """An asynchronous version of :class:`subprocess.Popen`.""" + + @abstractmethod + async def wait(self) -> int: + """ + Wait until the process exits. + + :return: the exit code of the process + """ + + @abstractmethod + def terminate(self) -> None: + """ + Terminates the process, gracefully if possible. + + On Windows, this calls ``TerminateProcess()``. + On POSIX systems, this sends ``SIGTERM`` to the process. + + .. seealso:: :meth:`subprocess.Popen.terminate` + """ + + @abstractmethod + def kill(self) -> None: + """ + Kills the process. + + On Windows, this calls ``TerminateProcess()``. + On POSIX systems, this sends ``SIGKILL`` to the process. + + .. seealso:: :meth:`subprocess.Popen.kill` + """ + + @abstractmethod + def send_signal(self, signal: Signals) -> None: + """ + Send a signal to the subprocess. + + .. seealso:: :meth:`subprocess.Popen.send_signal` + + :param signal: the signal number (e.g. :data:`signal.SIGHUP`) + """ + + @property + @abstractmethod + def pid(self) -> int: + """The process ID of the process.""" + + @property + @abstractmethod + def returncode(self) -> int | None: + """ + The return code of the process. If the process has not yet terminated, this will + be ``None``. + """ + + @property + @abstractmethod + def stdin(self) -> ByteSendStream | None: + """The stream for the standard input of the process.""" + + @property + @abstractmethod + def stdout(self) -> ByteReceiveStream | None: + """The stream for the standard output of the process.""" + + @property + @abstractmethod + def stderr(self) -> ByteReceiveStream | None: + """The stream for the standard error output of the process.""" diff --git a/venv/lib/python3.12/site-packages/anyio/abc/_tasks.py b/venv/lib/python3.12/site-packages/anyio/abc/_tasks.py new file mode 100644 index 0000000..0a019e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/abc/_tasks.py @@ -0,0 +1,223 @@ +from __future__ import annotations + +import sys +from abc import ABCMeta, abstractmethod +from collections.abc import Callable, Coroutine +from contextvars import Context +from types import TracebackType +from typing import TYPE_CHECKING, Any, Literal, Protocol, final, overload + +if sys.version_info >= (3, 13): + from typing import TypeVar +else: + from typing_extensions import TypeVar + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if TYPE_CHECKING: + from .._core._tasks import CancelScope, TaskHandle + +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True, default=None) +PosArgsT = TypeVarTuple("PosArgsT") + + +def get_coro_name(coro: Coroutine[Any, Any, object], override: object = None) -> str: + if override is not None: + return str(override) + + try: + cr_frame = coro.cr_frame # type: ignore[attr-defined] + module = cr_frame.f_globals["__name__"] + except (AttributeError, KeyError): + module = None + + qualname = getattr(coro, "__qualname__", None) + return ".".join([x for x in (module, qualname) if x]) + + +def get_callable_name(func: Callable, override: object = None) -> str: + if override is not None: + return str(override) + + module = getattr(func, "__module__", None) + qualname = getattr(func, "__qualname__", None) + return ".".join([x for x in (module, qualname) if x]) + + +def call_for_coroutine( + func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], + args: tuple[Unpack[PosArgsT]], + **kwargs: Any, +) -> Coroutine[Any, Any, T_co]: + """ + Call the given function with the given positional and keyword arguments. + + :return: the resulting coroutine + :raises TypeError: if the return value was not a coroutine object + + """ + coro = func(*args, **kwargs) + if not isinstance(coro, Coroutine): + prefix = f"{func.__module__}." if hasattr(func, "__module__") else "" + raise TypeError( + f"Expected {prefix}{func.__qualname__}() to return a coroutine, but " + f"the return value ({coro!r}) is not a coroutine object" + ) + + return coro + + +class TaskStatus(Protocol[T_contra]): + @overload + def started(self: TaskStatus[None]) -> None: ... + + @overload + def started(self, value: T_contra) -> None: ... + + def started(self, value: T_contra | None = None) -> None: + """ + Signal that the task has started. + + :param value: object passed back to the starter of the task + """ + + +class TaskGroup(metaclass=ABCMeta): + """ + Groups several asynchronous tasks together. + + :ivar cancel_scope: the cancel scope inherited by all child tasks + :vartype cancel_scope: CancelScope + + .. note:: On asyncio, support for eager task factories is considered to be + **experimental**. In particular, they don't follow the usual semantics of new + tasks being scheduled on the next iteration of the event loop, and may thus + cause unexpected behavior in code that wasn't written with such semantics in + mind. + """ + + cancel_scope: CancelScope + + def cancel(self, reason: str | None = None) -> None: + """ + Cancel this task group's cancel scope immediately. + + This is a shortcut for calling ``.cancel_scope.cancel()`` on the task group. + + :param reason: a message describing the reason for the cancellation + + .. versionadded:: 4.14.0 + + """ + self.cancel_scope.cancel(reason) + + @abstractmethod + def create_task( + self, + coro: Coroutine[Any, Any, T_co], + *, + name: object = None, + context: Context | None = None, + ) -> TaskHandle[T_co]: + """ + Create a new task from a coroutine object and schedule it to run. + + :param coro: a coroutine object + :param name: optional name to give the task + :param context: optional context to run the task in + :return: a task handle + + .. versionadded:: 4.14.0 + """ + + @final + def start_soon( + self, + func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> TaskHandle[T_co]: + """ + Start a new task in this task group. + + :param func: a coroutine function + :param args: positional arguments to call the function with + :param name: name of the task, for the purposes of introspection and debugging + :return: a task handle + + .. versionadded:: 3.0 + .. versionchanged:: 4.14.0 + This method now returns a task handle. + + """ + final_name = get_callable_name(func, name) + return self.create_task(call_for_coroutine(func, args), name=final_name) + + @overload + async def start( + self, + func: Callable[..., Coroutine[Any, Any, T_co]], + *args: object, + name: object = None, + return_handle: Literal[False] = ..., + ) -> Any: ... + + @overload + async def start( + self, + func: Callable[..., Coroutine[Any, Any, T_co]], + *args: object, + name: object = None, + return_handle: Literal[True], + ) -> TaskHandle[T_co, Any]: ... + + @abstractmethod + async def start( + self, + func: Callable[..., Coroutine[Any, Any, T_co]], + *args: object, + name: object = None, + return_handle: Literal[False, True] = False, + ) -> Any: + """ + Start a new task and wait until it signals for readiness. + + The target callable must accept a keyword argument ``task_status`` (of type + :class:`TaskStatus`). Awaiting on this method will return whatever was passed to + ``task_status.started()`` (``None`` by default). + + .. note:: The :class:`TaskStatus` class is generic, and the type argument should + indicate the type of the value that will be passed to + ``task_status.started()``. + + :param func: a coroutine function that accepts the ``task_status`` keyword + argument + :param args: positional arguments to call the function with + :param name: an optional name for the task, for introspection and debugging + :param return_handle: if ``True``, return a :class:`TaskHandle` which also + contains the start value in ``start_value`` + :return: the value passed to ``task_status.started()`` + :raises RuntimeError: if the task finishes without calling + ``task_status.started()`` + + .. seealso:: :ref:`start_initialize` + + .. versionadded:: 3.0 + """ + + @abstractmethod + async def __aenter__(self) -> TaskGroup: + """Enter the task group context and allow starting new tasks.""" + + @abstractmethod + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + """Exit the task group context waiting for all tasks to finish.""" diff --git a/venv/lib/python3.12/site-packages/anyio/abc/_testing.py b/venv/lib/python3.12/site-packages/anyio/abc/_testing.py new file mode 100644 index 0000000..b18d835 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/abc/_testing.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +import sys +import types +from abc import ABCMeta, abstractmethod +from collections.abc import AsyncGenerator, Callable, Coroutine, Iterable +from typing import Any, TypeVar + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self + +_T = TypeVar("_T") + + +class TestRunner(metaclass=ABCMeta): + """ + Encapsulates a running event loop. Every call made through this object will use the + same event loop. + """ + + def __enter__(self) -> Self: + return self + + @abstractmethod + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> bool | None: ... + + @abstractmethod + def run_asyncgen_fixture( + self, + fixture_func: Callable[..., AsyncGenerator[_T, Any]], + kwargs: dict[str, Any], + ) -> Iterable[_T]: + """ + Run an async generator fixture. + + :param fixture_func: the fixture function + :param kwargs: keyword arguments to call the fixture function with + :return: an iterator yielding the value yielded from the async generator + """ + + @abstractmethod + def run_fixture( + self, + fixture_func: Callable[..., Coroutine[Any, Any, _T]], + kwargs: dict[str, Any], + ) -> _T: + """ + Run an async fixture. + + :param fixture_func: the fixture function + :param kwargs: keyword arguments to call the fixture function with + :return: the return value of the fixture function + """ + + @abstractmethod + def run_test( + self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] + ) -> None: + """ + Run an async test function. + + :param test_func: the test function + :param kwargs: keyword arguments to call the test function with + """ + + @abstractmethod + def is_running(self) -> bool: + """ + Check if the test runner is running. + + :return: ``True`` if the coroutine is currently being run, ``False`` otherwise. + """ diff --git a/venv/lib/python3.12/site-packages/anyio/from_thread.py b/venv/lib/python3.12/site-packages/anyio/from_thread.py new file mode 100644 index 0000000..f163d81 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/from_thread.py @@ -0,0 +1,588 @@ +from __future__ import annotations + +__all__ = ( + "BlockingPortal", + "BlockingPortalProvider", + "check_cancelled", + "run", + "run_sync", + "start_blocking_portal", +) + +import sys +from collections.abc import Awaitable, Callable, Coroutine, Generator +from concurrent.futures import Future +from contextlib import ( + AbstractAsyncContextManager, + AbstractContextManager, + contextmanager, +) +from contextvars import Context +from dataclasses import dataclass, field +from functools import partial +from inspect import isawaitable +from threading import Lock, Thread, current_thread, get_ident +from types import TracebackType +from typing import ( + Any, + Generic, + TypeVar, + cast, + overload, +) + +from ._core._eventloop import ( + get_cancelled_exc_class, + threadlocals, +) +from ._core._eventloop import run as run_eventloop +from ._core._exceptions import NoEventLoopError +from ._core._synchronization import Event +from ._core._tasks import CancelScope, create_task_group +from .abc._tasks import TaskStatus +from .lowlevel import EventLoopToken, current_token + +if sys.version_info >= (3, 11): + from typing import Self, TypeVarTuple, Unpack +else: + from typing_extensions import Self, TypeVarTuple, Unpack + +T_Retval = TypeVar("T_Retval") +T_co = TypeVar("T_co", covariant=True) +PosArgsT = TypeVarTuple("PosArgsT") + + +def _token_or_error(token: EventLoopToken | None) -> EventLoopToken: + if token is not None: + return token + + try: + return threadlocals.current_token + except AttributeError: + raise NoEventLoopError( + "Not running inside an AnyIO worker thread, and no event loop token was " + "provided" + ) from None + + +def run( + func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], + *args: Unpack[PosArgsT], + token: EventLoopToken | None = None, +) -> T_co: + """ + Call a coroutine function from a worker thread. + + :param func: a coroutine function + :param args: positional arguments for the callable + :param token: an event loop token to use to get back to the event loop thread + (required if calling this function from outside an AnyIO worker thread) + :return: the return value of the coroutine function + :raises MissingTokenError: if no token was provided and called from outside an + AnyIO worker thread + :raises RunFinishedError: if the event loop tied to ``token`` is no longer running + + .. versionchanged:: 4.11.0 + Added the ``token`` parameter. + + """ + explicit_token = token is not None + token = _token_or_error(token) + return token.backend_class.run_async_from_thread( + func, args, token=token.native_token if explicit_token else None + ) + + +def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + token: EventLoopToken | None = None, +) -> T_Retval: + """ + Call a function in the event loop thread from a worker thread. + + :param func: a callable + :param args: positional arguments for the callable + :param token: an event loop token to use to get back to the event loop thread + (required if calling this function from outside an AnyIO worker thread) + :return: the return value of the callable + :raises MissingTokenError: if no token was provided and called from outside an + AnyIO worker thread + :raises RunFinishedError: if the event loop tied to ``token`` is no longer running + + .. versionchanged:: 4.11.0 + Added the ``token`` parameter. + + """ + explicit_token = token is not None + token = _token_or_error(token) + return token.backend_class.run_sync_from_thread( + func, args, token=token.native_token if explicit_token else None + ) + + +class _BlockingAsyncContextManager(AbstractContextManager, Generic[T_co]): + _enter_future: Future[T_co] + _exit_future: Future[bool | None] + _exit_event: Event + _exit_exc_info: tuple[ + type[BaseException] | None, BaseException | None, TracebackType | None + ] = (None, None, None) + + def __init__( + self, async_cm: AbstractAsyncContextManager[T_co], portal: BlockingPortal + ): + self._async_cm = async_cm + self._portal = portal + + async def run_async_cm(self) -> bool | None: + try: + self._exit_event = Event() + value = await self._async_cm.__aenter__() + except BaseException as exc: + self._enter_future.set_exception(exc) + raise + else: + self._enter_future.set_result(value) + + try: + # Wait for the sync context manager to exit. + # This next statement can raise `get_cancelled_exc_class()` if + # something went wrong in a task group in this async context + # manager. + await self._exit_event.wait() + finally: + # In case of cancellation, it could be that we end up here before + # `_BlockingAsyncContextManager.__exit__` is called, and an + # `_exit_exc_info` has been set. + result = await self._async_cm.__aexit__(*self._exit_exc_info) + + return result + + def __enter__(self) -> T_co: + self._enter_future = Future() + self._exit_future = self._portal.start_task_soon(self.run_async_cm) + return self._enter_future.result() + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + /, + ) -> bool | None: + self._exit_exc_info = exc_type, exc_value, traceback + self._portal.call(self._exit_event.set) + return self._exit_future.result() + + +class _BlockingPortalTaskStatus(TaskStatus): + def __init__(self, future: Future): + self._future = future + + def started(self, value: object = None) -> None: + self._future.set_result(value) + + +class BlockingPortal: + """ + An object that lets external threads run code in an asynchronous event loop. + + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + """ + + def __init__(self) -> None: + self._token = current_token() + self._event_loop_thread_id: int | None = get_ident() + self._stop_event = Event() + self._task_group = create_task_group() + + async def __aenter__(self) -> Self: + await self._task_group.__aenter__() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + await self.stop() + return await self._task_group.__aexit__(exc_type, exc_val, exc_tb) + + def _check_running(self) -> None: + if self._event_loop_thread_id is None: + raise RuntimeError("This portal is not running") + if self._event_loop_thread_id == get_ident(): + raise RuntimeError( + "This method cannot be called from the event loop thread" + ) + + async def sleep_until_stopped(self) -> None: + """Sleep until :meth:`stop` is called.""" + await self._stop_event.wait() + + async def stop(self, cancel_remaining: bool = False) -> None: + """ + Signal the portal to shut down. + + This marks the portal as no longer accepting new calls and exits from + :meth:`sleep_until_stopped`. + + :param cancel_remaining: ``True`` to cancel all the remaining tasks, ``False`` + to let them finish before returning + + """ + self._event_loop_thread_id = None + self._stop_event.set() + if cancel_remaining: + self._task_group.cancel_scope.cancel("the blocking portal is shutting down") + + async def _call_func( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + future: Future[T_Retval], + ) -> None: + event_loop_thread_id = self._event_loop_thread_id + + def callback(f: Future[T_Retval]) -> None: + if f.cancelled(): + if event_loop_thread_id == get_ident(): + scope.cancel("the future was cancelled") + elif event_loop_thread_id is not None: + run_sync( + scope.cancel, "the future was cancelled", token=self._token + ) + + try: + retval_or_awaitable = func(*args, **kwargs) + if isawaitable(retval_or_awaitable): + with CancelScope() as scope: + future.add_done_callback(callback) + retval = await retval_or_awaitable + else: + retval = retval_or_awaitable + except get_cancelled_exc_class(): + future.cancel() + future.set_running_or_notify_cancel() + except BaseException as exc: + if not future.cancelled(): + future.set_exception(exc) + + # Let base exceptions fall through + if not isinstance(exc, Exception): + raise + else: + if not future.cancelled(): + future.set_result(retval) + finally: + scope = None # type: ignore[assignment] + + def _spawn_task_from_thread( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + name: object, + future: Future[T_Retval], + ) -> None: + """ + Spawn a new task using the given callable. + + :param func: a callable + :param args: positional arguments to be passed to the callable + :param kwargs: keyword arguments to be passed to the callable + :param name: name of the task (will be coerced to a string if not ``None``) + :param future: a future that will resolve to the return value of the callable, + or the exception raised during its execution + + """ + run_sync( + partial(self._task_group.start_soon, name=name), + self._call_func, + func, + args, + kwargs, + future, + token=self._token, + ) + + @overload + def call( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + ) -> T_Retval: ... + + @overload + def call( + self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] + ) -> T_Retval: ... + + def call( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + *args: Unpack[PosArgsT], + ) -> T_Retval: + """ + Call the given function in the event loop thread. + + If the callable returns a coroutine object, it is awaited on. + + :param func: any callable + :raises RuntimeError: if the portal is not running or if this method is called + from within the event loop thread + + """ + return cast(T_Retval, self.start_task_soon(func, *args).result()) + + @overload + def start_task_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> Future[T_Retval]: ... + + @overload + def start_task_soon( + self, + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + name: object = None, + ) -> Future[T_Retval]: ... + + def start_task_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + *args: Unpack[PosArgsT], + name: object = None, + ) -> Future[T_Retval]: + """ + Start a task in the portal's task group. + + The task will be run inside a cancel scope which can be cancelled by cancelling + the returned future. + + :param func: the target function + :param args: positional arguments passed to ``func`` + :param name: name of the task (will be coerced to a string if not ``None``) + :return: a future that resolves with the return value of the callable if the + task completes successfully, or with the exception raised in the task + :raises RuntimeError: if the portal is not running or if this method is called + from within the event loop thread + :rtype: concurrent.futures.Future[T_Retval] + + .. versionadded:: 3.0 + + """ + self._check_running() + f: Future[T_Retval] = Future() + self._spawn_task_from_thread(func, args, {}, name, f) + return f + + def start_task( + self, + func: Callable[..., Awaitable[T_Retval]], + *args: object, + name: object = None, + ) -> tuple[Future[T_Retval], Any]: + """ + Start a task in the portal's task group and wait until it signals for readiness. + + This method works the same way as :meth:`.abc.TaskGroup.start`. + + :param func: the target function + :param args: positional arguments passed to ``func`` + :param name: name of the task (will be coerced to a string if not ``None``) + :return: a tuple of (future, task_status_value) where the ``task_status_value`` + is the value passed to ``task_status.started()`` from within the target + function + :rtype: tuple[concurrent.futures.Future[T_Retval], Any] + + .. versionadded:: 3.0 + + """ + + def task_done(future: Future[T_Retval]) -> None: + if not task_status_future.done(): + if future.cancelled(): + task_status_future.cancel() + elif future.exception(): + task_status_future.set_exception(future.exception()) + else: + exc = RuntimeError( + "Task exited without calling task_status.started()" + ) + task_status_future.set_exception(exc) + + self._check_running() + task_status_future: Future = Future() + task_status = _BlockingPortalTaskStatus(task_status_future) + f: Future = Future() + f.add_done_callback(task_done) + self._spawn_task_from_thread(func, args, {"task_status": task_status}, name, f) + return f, task_status_future.result() + + def wrap_async_context_manager( + self, cm: AbstractAsyncContextManager[T_co] + ) -> AbstractContextManager[T_co]: + """ + Wrap an async context manager as a synchronous context manager via this portal. + + Spawns a task that will call both ``__aenter__()`` and ``__aexit__()``, stopping + in the middle until the synchronous context manager exits. + + :param cm: an asynchronous context manager + :return: a synchronous context manager + + .. versionadded:: 2.1 + + """ + return _BlockingAsyncContextManager(cm, self) + + +@dataclass +class BlockingPortalProvider: + """ + A manager for a blocking portal. Used as a context manager. The first thread to + enter this context manager causes a blocking portal to be started with the specific + parameters, and the last thread to exit causes the portal to be shut down. Thus, + there will be exactly one blocking portal running in this context as long as at + least one thread has entered this context manager. + + The parameters are the same as for :func:`~anyio.run`. + + :param backend: name of the backend + :param backend_options: backend options + + .. versionadded:: 4.4 + """ + + backend: str = "asyncio" + backend_options: dict[str, Any] | None = None + _lock: Lock = field(init=False, default_factory=Lock) + _leases: int = field(init=False, default=0) + _portal: BlockingPortal = field(init=False) + _portal_cm: AbstractContextManager[BlockingPortal] | None = field( + init=False, default=None + ) + + def __enter__(self) -> BlockingPortal: + with self._lock: + if self._portal_cm is None: + self._portal_cm = start_blocking_portal( + self.backend, self.backend_options + ) + self._portal = self._portal_cm.__enter__() + + self._leases += 1 + return self._portal + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + portal_cm: AbstractContextManager[BlockingPortal] | None = None + with self._lock: + assert self._portal_cm + assert self._leases > 0 + self._leases -= 1 + if not self._leases: + portal_cm = self._portal_cm + self._portal_cm = None + del self._portal + + if portal_cm: + portal_cm.__exit__(None, None, None) + + +@contextmanager +def start_blocking_portal( + backend: str = "asyncio", + backend_options: dict[str, Any] | None = None, + *, + name: str | None = None, +) -> Generator[BlockingPortal, Any, None]: + """ + Start a new event loop in a new thread and run a blocking portal in its main task. + + The parameters are the same as for :func:`~anyio.run`. + + :param backend: name of the backend + :param backend_options: backend options + :param name: name of the thread + :return: a context manager that yields a blocking portal + + .. versionchanged:: 3.0 + Usage as a context manager is now required. + + """ + + async def run_portal() -> None: + async with BlockingPortal() as portal_: + if name is None: + current_thread().name = f"{backend}-portal-{id(portal_):x}" + + future.set_result(portal_) + await portal_.sleep_until_stopped() + + def run_blocking_portal() -> None: + if future.set_running_or_notify_cancel(): + try: + run_eventloop( + run_portal, backend=backend, backend_options=backend_options + ) + except BaseException as exc: + if not future.done(): + future.set_exception(exc) + + future: Future[BlockingPortal] = Future() + kwargs: dict[str, Any] = {} + if sys.version_info >= (3, 14): + kwargs["context"] = Context() + + thread = Thread(target=run_blocking_portal, daemon=True, name=name, **kwargs) + thread.start() + try: + cancel_remaining_tasks = False + portal = future.result() + try: + yield portal + except BaseException: + cancel_remaining_tasks = True + raise + finally: + try: + portal.call(portal.stop, cancel_remaining_tasks) + except RuntimeError: + pass + finally: + thread.join() + + +def check_cancelled() -> None: + """ + Check if the cancel scope of the host task's running the current worker thread has + been cancelled. + + If the host task's current cancel scope has indeed been cancelled, the + backend-specific cancellation exception will be raised. + + :raises RuntimeError: if the current thread was not spawned by + :func:`.to_thread.run_sync` + + """ + try: + token: EventLoopToken = threadlocals.current_token + except AttributeError: + raise NoEventLoopError( + "This function can only be called inside an AnyIO worker thread" + ) from None + + token.backend_class.check_cancelled() diff --git a/venv/lib/python3.12/site-packages/anyio/functools.py b/venv/lib/python3.12/site-packages/anyio/functools.py new file mode 100644 index 0000000..b0bdfb4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/functools.py @@ -0,0 +1,400 @@ +from __future__ import annotations + +__all__ = ( + "AsyncCacheInfo", + "AsyncCacheParameters", + "AsyncLRUCacheWrapper", + "cache", + "lru_cache", + "reduce", +) + +import functools +from collections import OrderedDict +from collections.abc import ( + AsyncIterable, + Awaitable, + Callable, + Coroutine, + Hashable, + Iterable, +) +from functools import update_wrapper +from inspect import iscoroutinefunction +from typing import ( + Any, + Generic, + NamedTuple, + ParamSpec, + TypedDict, + TypeVar, + cast, + final, + overload, +) +from weakref import WeakKeyDictionary + +from ._core._eventloop import current_time +from ._core._synchronization import Lock +from .lowlevel import RunVar, checkpoint + +T = TypeVar("T") +S = TypeVar("S") +P = ParamSpec("P") +lru_cache_items: RunVar[ + WeakKeyDictionary[ + AsyncLRUCacheWrapper[Any, Any], + OrderedDict[ + Hashable, + tuple[_InitialMissingType, Lock, float | None] + | tuple[Any, None, float | None], + ], + ] +] = RunVar("lru_cache_items") + + +class _InitialMissingType: + pass + + +initial_missing: _InitialMissingType = _InitialMissingType() + + +class AsyncCacheInfo(NamedTuple): + hits: int + misses: int + maxsize: int | None + currsize: int + ttl: int | None + + +class AsyncCacheParameters(TypedDict): + maxsize: int | None + typed: bool + always_checkpoint: bool + ttl: int | None + + +class _LRUMethodWrapper(Generic[T]): + def __init__(self, wrapper: AsyncLRUCacheWrapper[..., T], instance: object): + self.__wrapper = wrapper + self.__instance = instance + + def cache_info(self) -> AsyncCacheInfo: + return self.__wrapper.cache_info() + + def cache_parameters(self) -> AsyncCacheParameters: + return self.__wrapper.cache_parameters() + + def cache_clear(self) -> None: + self.__wrapper.cache_clear() + + async def __call__(self, *args: Any, **kwargs: Any) -> T: + if self.__instance is None: + return await self.__wrapper(*args, **kwargs) + + return await self.__wrapper(self.__instance, *args, **kwargs) + + +@final +class AsyncLRUCacheWrapper(Generic[P, T]): + def __init__( + self, + func: Callable[P, Awaitable[T]], + maxsize: int | None, + typed: bool, + always_checkpoint: bool, + ttl: int | None, + ): + self.__wrapped__ = func + self._hits: int = 0 + self._misses: int = 0 + self._maxsize = max(maxsize, 0) if maxsize is not None else None + self._currsize: int = 0 + self._typed = typed + self._always_checkpoint = always_checkpoint + self._ttl = ttl + update_wrapper(self, func) + + def cache_info(self) -> AsyncCacheInfo: + return AsyncCacheInfo( + self._hits, self._misses, self._maxsize, self._currsize, self._ttl + ) + + def cache_parameters(self) -> AsyncCacheParameters: + return { + "maxsize": self._maxsize, + "typed": self._typed, + "always_checkpoint": self._always_checkpoint, + "ttl": self._ttl, + } + + def cache_clear(self) -> None: + if cache := lru_cache_items.get(None): + cache.pop(self, None) + self._hits = self._misses = self._currsize = 0 + + async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + # Easy case first: if maxsize == 0, no caching is done + if self._maxsize == 0: + value = await self.__wrapped__(*args, **kwargs) + self._misses += 1 + return value + + # The key is constructed as a flat tuple to avoid memory overhead + key: tuple[Any, ...] = args + if kwargs: + # initial_missing is used as a separator + key += (initial_missing,) + sum(kwargs.items(), ()) + + if self._typed: + key += tuple(type(arg) for arg in args) + if kwargs: + key += (initial_missing,) + tuple(type(val) for val in kwargs.values()) + + try: + cache = lru_cache_items.get() + except LookupError: + cache = WeakKeyDictionary() + lru_cache_items.set(cache) + + try: + cache_entry = cache[self] + except KeyError: + cache_entry = cache[self] = OrderedDict() + + cached_value: T | _InitialMissingType + try: + cached_value, lock, expires_at = cache_entry[key] + except KeyError: + # We're the first task to call this function + cached_value, lock, expires_at = ( + initial_missing, + Lock(fast_acquire=not self._always_checkpoint), + None, + ) + cache_entry[key] = cached_value, lock, expires_at + + if lock is None: + if expires_at is not None and current_time() >= expires_at: + self._currsize -= 1 + cached_value, lock, expires_at = ( + initial_missing, + Lock(fast_acquire=not self._always_checkpoint), + None, + ) + cache_entry[key] = cached_value, lock, expires_at + else: + # The value was already cached + self._hits += 1 + cache_entry.move_to_end(key) + if self._always_checkpoint: + await checkpoint() + + return cast(T, cached_value) + + async with lock: + # Check if another task filled the cache while we acquired the lock + if (cached_value := cache_entry[key][0]) is initial_missing: + self._misses += 1 + if self._maxsize is not None and self._currsize >= self._maxsize: + cache_entry.popitem(last=False) + else: + self._currsize += 1 + + value = await self.__wrapped__(*args, **kwargs) + expires_at = ( + current_time() + self._ttl if self._ttl is not None else None + ) + cache_entry[key] = value, None, expires_at + else: + # Another task filled the cache while we were waiting for the lock + self._hits += 1 + cache_entry.move_to_end(key) + value = cast(T, cached_value) + + return value + + def __get__( + self, instance: object, owner: type | None = None + ) -> _LRUMethodWrapper[T]: + wrapper = _LRUMethodWrapper(self, instance) + update_wrapper(wrapper, self.__wrapped__) + return wrapper + + +class _LRUCacheWrapper: + def __init__( + self, maxsize: int | None, typed: bool, always_checkpoint: bool, ttl: int | None + ): + self._maxsize = maxsize + self._typed = typed + self._always_checkpoint = always_checkpoint + self._ttl = ttl + + @overload + def __call__( # type: ignore[overload-overlap] + self, func: Callable[P, Coroutine[Any, Any, T]], / + ) -> AsyncLRUCacheWrapper[P, T]: ... + + @overload + def __call__( + self, func: Callable[..., T], / + ) -> functools._lru_cache_wrapper[T]: ... + + def __call__( + self, f: Callable[P, Coroutine[Any, Any, T]] | Callable[..., T], / + ) -> AsyncLRUCacheWrapper[P, T] | functools._lru_cache_wrapper[T]: + if iscoroutinefunction(f): + return AsyncLRUCacheWrapper( + f, self._maxsize, self._typed, self._always_checkpoint, self._ttl + ) + + return functools.lru_cache(maxsize=self._maxsize, typed=self._typed)(f) # type: ignore[arg-type] + + +@overload +def cache( # type: ignore[overload-overlap] + func: Callable[P, Coroutine[Any, Any, T]], / +) -> AsyncLRUCacheWrapper[P, T]: ... + + +@overload +def cache(func: Callable[..., T], /) -> functools._lru_cache_wrapper[T]: ... + + +def cache(func: Callable[..., Any] | Callable[P, Coroutine[Any, Any, Any]], /) -> Any: + """ + A convenient shortcut for :func:`lru_cache` with ``maxsize=None``. + + This is the asynchronous equivalent to :func:`functools.cache`. + + """ + return lru_cache(maxsize=None)(func) + + +@overload +def lru_cache( + *, + maxsize: int | None = ..., + typed: bool = ..., + always_checkpoint: bool = ..., + ttl: int | None = ..., +) -> _LRUCacheWrapper: ... + + +@overload +def lru_cache( # type: ignore[overload-overlap] + func: Callable[P, Coroutine[Any, Any, T]], / +) -> AsyncLRUCacheWrapper[P, T]: ... + + +@overload +def lru_cache(func: Callable[..., T], /) -> functools._lru_cache_wrapper[T]: ... + + +def lru_cache( + func: Callable[..., Coroutine[Any, Any, Any]] | Callable[..., Any] | None = None, + /, + *, + maxsize: int | None = 128, + typed: bool = False, + always_checkpoint: bool = False, + ttl: int | None = None, +) -> Any: + """ + An asynchronous version of :func:`functools.lru_cache`. + + If a synchronous function is passed, the standard library + :func:`functools.lru_cache` is applied instead. + + :param always_checkpoint: if ``True``, every call to the cached function will be + guaranteed to yield control to the event loop at least once + :param ttl: time in seconds after which to invalidate cache entries + + .. note:: Caches and locks are managed on a per-event loop basis. + + """ + if func is None: + return _LRUCacheWrapper(maxsize, typed, always_checkpoint, ttl) + + if not callable(func): + raise TypeError("the first argument must be callable") + + return _LRUCacheWrapper(maxsize, typed, always_checkpoint, ttl)(func) + + +@overload +async def reduce( + function: Callable[[T, S], Awaitable[T]], + iterable: Iterable[S] | AsyncIterable[S], + /, + initial: T, +) -> T: ... + + +@overload +async def reduce( + function: Callable[[T, T], Awaitable[T]], + iterable: Iterable[T] | AsyncIterable[T], + /, +) -> T: ... + + +async def reduce( # type: ignore[misc] + function: Callable[[T, T], Awaitable[T]] | Callable[[T, S], Awaitable[T]], + iterable: Iterable[T] | Iterable[S] | AsyncIterable[T] | AsyncIterable[S], + /, + initial: T | _InitialMissingType = initial_missing, +) -> T: + """ + Asynchronous version of :func:`functools.reduce`. + + :param function: a coroutine function that takes two arguments: the accumulated + value and the next element from the iterable + :param iterable: an iterable or async iterable + :param initial: the initial value (if missing, the first element of the iterable is + used as the initial value) + + """ + element: Any + function_called = False + if isinstance(iterable, AsyncIterable): + async_it = iterable.__aiter__() + if initial is initial_missing: + try: + value = cast(T, await async_it.__anext__()) + except StopAsyncIteration: + raise TypeError( + "reduce() of empty sequence with no initial value" + ) from None + else: + value = cast(T, initial) + + async for element in async_it: + value = await function(value, element) + function_called = True + elif isinstance(iterable, Iterable): + it = iter(iterable) + if initial is initial_missing: + try: + value = cast(T, next(it)) + except StopIteration: + raise TypeError( + "reduce() of empty sequence with no initial value" + ) from None + else: + value = cast(T, initial) + + for element in it: + value = await function(value, element) + function_called = True + else: + raise TypeError("reduce() argument 2 must be an iterable or async iterable") + + # Make sure there is at least one checkpoint, even if an empty iterable and an + # initial value were given + if not function_called: + await checkpoint() + + return value diff --git a/venv/lib/python3.12/site-packages/anyio/itertools.py b/venv/lib/python3.12/site-packages/anyio/itertools.py new file mode 100644 index 0000000..2a10cfd --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/itertools.py @@ -0,0 +1,629 @@ +from __future__ import annotations + +__all__ = ( + "Chain", + "accumulate", + "batched", + "combinations", + "combinations_with_replacement", + "compress", + "count", + "cycle", + "dropwhile", + "filterfalse", + "groupby", + "islice", + "pairwise", + "permutations", + "product", + "repeat", + "starmap", + "takewhile", + "tee", + "zip_longest", +) + +import itertools +import operator +import sys +from collections.abc import ( + AsyncGenerator, + AsyncIterable, + AsyncIterator, + Awaitable, + Callable, + Iterable, + Iterator, +) +from dataclasses import dataclass, field +from typing import Any, Generic, TypeVar, cast, overload + +from ._core._synchronization import Lock +from ._core._tasks import CancelScope +from .lowlevel import cancel_shielded_checkpoint, checkpoint, checkpoint_if_cancelled + +if sys.version_info < (3, 15): + from typing_extensions import sentinel + +T = TypeVar("T") +R = TypeVar("R") +_tee_end = sentinel("_tee_end") + + +@dataclass(eq=False) +class _IterableAsyncIterator(AsyncIterator[T]): + iterator: Iterator[T] + + async def __anext__(self) -> T: + await checkpoint_if_cancelled() + try: + result = next(self.iterator) + except StopIteration: + await cancel_shielded_checkpoint() + raise StopAsyncIteration from None + + await cancel_shielded_checkpoint() + return result + + +def _iterate(iterable: Iterable[T] | AsyncIterable[T]) -> AsyncIterator[T]: + if isinstance(iterable, AsyncIterator): + return iterable + + if isinstance(iterable, AsyncIterable): + return iterable.__aiter__() + + return _IterableAsyncIterator(iter(iterable)) + + +@dataclass(eq=False) +class _TeeLink(Generic[T]): + value: object | None = None + next: _TeeLink[T] | None = None + filled: bool = False + + +@dataclass(eq=False) +class _TeeState(Generic[T]): + iterator: AsyncIterator[T] + lock: Lock = field(default_factory=Lock) + + async def fill(self, link: _TeeLink[T]) -> bool: + if link.filled: + return False + + async with self.lock: + if link.filled: + return True + + link.value = await anext(self.iterator, _tee_end) + if link.value is not _tee_end: + link.next = _TeeLink() + + link.filled = True + return True + + +class _TeeAsyncIterator(AsyncIterator[T]): + _state: _TeeState[T] + _link: _TeeLink[T] + _element_yielded: bool + + def __init__( + self, iterable: Iterable[T] | AsyncIterable[T] | _TeeAsyncIterator[T] + ) -> None: + if isinstance(iterable, _TeeAsyncIterator): + self._state = iterable._state + self._link = iterable._link + else: + self._state = _TeeState(_iterate(iterable)) + self._link = _TeeLink() + + self._element_yielded = False + + async def __anext__(self) -> T: + had_yieldpoint = await self._state.fill(self._link) + if self._link.value is _tee_end: + if not self._element_yielded: + await checkpoint() + + raise StopAsyncIteration + + if not had_yieldpoint: + await checkpoint_if_cancelled() + + self._element_yielded = True + value = cast(T, self._link.value) + next_link = self._link.next + assert next_link is not None + self._link = next_link + if not had_yieldpoint: + await cancel_shielded_checkpoint() + + return value + + +async def _operator_add(x: T, y: T) -> T: + return operator.add(x, y) + + +async def accumulate( + iterable: Iterable[T] | AsyncIterable[T], + function: Callable[[T, T], Awaitable[T]] = _operator_add, + *, + initial: T | None = None, +) -> AsyncGenerator[T, None]: + iterator = _iterate(iterable) + if initial is None: + try: + total = await anext(iterator) + except StopAsyncIteration: + await checkpoint() + return + else: + await checkpoint_if_cancelled() + total = initial + await cancel_shielded_checkpoint() + + yield total + + async for element in iterator: + total = await function(total, element) + yield total + + +async def batched( + iterable: Iterable[T] | AsyncIterable[T], n: int, *, strict: bool = False +) -> AsyncGenerator[tuple[T, ...], None]: + if n < 1: + raise ValueError("n must be at least one") + + iterator = _iterate(iterable) + + while True: + batch: list[T] = [] + for _ in range(n): + try: + batch.append(await anext(iterator)) + except StopAsyncIteration: + if not batch: + await checkpoint() + return + if strict: + raise ValueError("batched(): incomplete batch") from None + + yield tuple(batch) + return + + yield tuple(batch) + + +class Chain: + def __call__( + self, *iterables: Iterable[T] | AsyncIterable[T] + ) -> AsyncGenerator[T, None]: + return self.from_iterable(iterables) + + async def from_iterable( + self, + iterables: ( + Iterable[Iterable[T] | AsyncIterable[T]] + | AsyncIterable[Iterable[T] | AsyncIterable[T]] + ), + ) -> AsyncGenerator[T, None]: + element_yielded = False + outer_iter = _iterate(iterables) + + try: + async for iterable in outer_iter: + async for element in _iterate(iterable): + element_yielded = True + yield element + finally: + aclose = getattr(outer_iter, "aclose", None) + if aclose is not None: + with CancelScope(shield=True): + await aclose() + + if not element_yielded: + await checkpoint() + + +chain: Chain = Chain() + + +async def combinations( + iterable: Iterable[T] | AsyncIterable[T], r: int +) -> AsyncGenerator[tuple[T, ...], None]: + pool: list[T] = [element async for element in _iterate(iterable)] + async for combination in _iterate(itertools.combinations(pool, r)): + yield combination + + +async def combinations_with_replacement( + iterable: Iterable[T] | AsyncIterable[T], r: int +) -> AsyncGenerator[tuple[T, ...], None]: + pool: list[T] = [element async for element in _iterate(iterable)] + async for combination in _iterate(itertools.combinations_with_replacement(pool, r)): + yield combination + + +async def compress( + data: Iterable[T] | AsyncIterable[T], + selectors: Iterable[object] | AsyncIterable[object], +) -> AsyncGenerator[T, None]: + data_iterator = _iterate(data) + selector_iterator = _iterate(selectors) + element_yielded = False + + while True: + try: + datum = await anext(data_iterator) + selector = await anext(selector_iterator) + except StopAsyncIteration: + if not element_yielded: + await checkpoint() + + return + + if selector: + element_yielded = True + yield datum + + +async def count(start: int = 0, step: int = 1) -> AsyncGenerator[int, None]: + n = start + while True: + await checkpoint_if_cancelled() + value = n + n += step + await cancel_shielded_checkpoint() + yield value + + +async def cycle( + iterable: Iterable[T] | AsyncIterable[T], +) -> AsyncGenerator[T, None]: + saved: list[T] = [] + async for element in _iterate(iterable): + saved.append(element) + yield element + + if not saved: + await checkpoint() + return + + while True: + for element in saved: + await checkpoint() + yield element + + +async def dropwhile( + predicate: Callable[[T], Awaitable[object]], + iterable: Iterable[T] | AsyncIterable[T], +) -> AsyncGenerator[T, None]: + element_yielded = False + dropping = True + + async for element in _iterate(iterable): + if dropping and await predicate(element): + continue + + dropping = False + element_yielded = True + yield element + + if not element_yielded: + await checkpoint() + + +async def filterfalse( + predicate: Callable[[T], Awaitable[object]], + iterable: Iterable[T] | AsyncIterable[T], +) -> AsyncGenerator[T, None]: + element_yielded = False + + async for element in _iterate(iterable): + if not await predicate(element): + element_yielded = True + yield element + + if not element_yielded: + await checkpoint() + + +@overload +def groupby( + iterable: Iterable[T] | AsyncIterable[T], +) -> AsyncGenerator[tuple[T, list[T]], None]: ... + + +@overload +def groupby( + iterable: Iterable[T] | AsyncIterable[T], + key: Callable[[T], Awaitable[R]], +) -> AsyncGenerator[tuple[R, list[T]], None]: ... + + +async def groupby( + iterable: Iterable[T] | AsyncIterable[T], + key: Callable[[T], Awaitable[object]] | None = None, +) -> AsyncGenerator[tuple[object, list[T]], None]: + iterator = _iterate(iterable) + try: + element = await anext(iterator) + except StopAsyncIteration: + await checkpoint() + return + + group_key = element if key is None else await key(element) + values = [element] + + async for element in iterator: + next_key = element if key is None else await key(element) + if next_key != group_key: + completed_group = group_key, values + group_key = next_key + values = [element] + yield completed_group + else: + values.append(element) + + yield group_key, values + + +@overload +def islice( + iterable: Iterable[T] | AsyncIterable[T], + stop: int | None, + /, +) -> AsyncGenerator[T, None]: ... + + +@overload +def islice( + iterable: Iterable[T] | AsyncIterable[T], + start: int | None, + stop: int | None, + step: int | None = 1, + /, +) -> AsyncGenerator[T, None]: ... + + +async def islice( + iterable: Iterable[T] | AsyncIterable[T], + *args: int | None, +) -> AsyncGenerator[T, None]: + if not args: + raise TypeError("islice expected at least 2 arguments, got 1") + if len(args) > 3: + raise TypeError(f"islice expected at most 4 arguments, got {len(args) + 1}") + + slice_args = slice(*args) + + start_message = ( + "Indices for islice() must be None or an integer: 0 <= x <= sys.maxsize." + ) + stop_message = ( + "Stop argument for islice() must be None or an integer: 0 <= x <= sys.maxsize." + ) + step_message = "Step for islice() must be a positive integer or None." + + def normalize_index(value: object, message: str) -> int: + try: + index = operator.index(cast(Any, value)) + except TypeError: + raise ValueError(message) from None + + if index < 0 or index > sys.maxsize: + raise ValueError(message) + + return index + + start = ( + 0 + if slice_args.start is None + else normalize_index(slice_args.start, start_message) + ) + stop = ( + None + if slice_args.stop is None + else normalize_index(slice_args.stop, stop_message) + ) + step = ( + 1 if slice_args.step is None else normalize_index(slice_args.step, step_message) + ) + + if step <= 0: + raise ValueError(step_message) + + if stop == 0 or start == stop: + await checkpoint() + return + + iterator = _iterate(iterable) + index = 0 + element_yielded = False + + while stop is None or index < stop: + try: + element = await anext(iterator) + except StopAsyncIteration: + if not element_yielded: + await checkpoint() + + return + + if index >= start and (index - start) % step == 0: + index += 1 + element_yielded = True + yield element + else: + index += 1 + + if not element_yielded: + await checkpoint() + + +async def pairwise( + iterable: Iterable[T] | AsyncIterable[T], +) -> AsyncGenerator[tuple[T, T], None]: + iterator = _iterate(iterable) + try: + previous = await anext(iterator) + except StopAsyncIteration: + await checkpoint() + return + + element_yielded = False + async for element in iterator: + element_yielded = True + pair = (previous, element) + previous = element + yield pair + + if not element_yielded: + await checkpoint() + + +async def permutations( + iterable: Iterable[T] | AsyncIterable[T], r: int | None = None +) -> AsyncGenerator[tuple[T, ...], None]: + pool: list[T] = [element async for element in _iterate(iterable)] + n = len(pool) + if r is None: + r = n + elif not isinstance(r, int): + raise TypeError("Expected int as r") + elif r < 0: + raise ValueError("r must be non-negative") + + async for permutation in _iterate(itertools.permutations(pool, r)): + yield permutation + + +async def product( + *iterables: Iterable[T] | AsyncIterable[T], repeat: int = 1 +) -> AsyncGenerator[tuple[T, ...], None]: + repeat = operator.index(repeat) + if repeat < 0: + raise ValueError("repeat argument cannot be negative") + + pools: list[tuple[T, ...]] = [] + for iterable in iterables: + pool: list[T] = [element async for element in _iterate(iterable)] + pools.append(tuple(pool)) + + async for value in _iterate(itertools.product(*pools, repeat=repeat)): + yield value + + +async def repeat(element: T, times: int | None = None) -> AsyncGenerator[T, None]: + if times is None: + while True: + await checkpoint() + yield element + + remaining = operator.index(cast(Any, times)) + if remaining <= 0: + await checkpoint() + return + + while remaining > 0: + await checkpoint_if_cancelled() + remaining -= 1 + await cancel_shielded_checkpoint() + yield element + + +async def starmap( + function: Callable[..., Awaitable[R]], + iterable: ( + Iterable[Iterable[object] | AsyncIterable[object]] + | AsyncIterable[Iterable[object] | AsyncIterable[object]] + ), +) -> AsyncGenerator[R, None]: + result_yielded = False + + async for args_iterable in _iterate(iterable): + args = [element async for element in _iterate(args_iterable)] + result_yielded = True + yield await function(*args) + + if not result_yielded: + await checkpoint() + + +def tee( + iterable: Iterable[T] | AsyncIterable[T], n: int = 2 +) -> tuple[AsyncIterator[T], ...]: + n = operator.index(cast(Any, n)) + if n < 0: + raise ValueError("n must be >= 0") + if n == 0: + return () + + iterator = _TeeAsyncIterator(iterable) + iterators: list[AsyncIterator[T]] = [iterator] + iterators.extend(_TeeAsyncIterator(iterator) for _ in range(n - 1)) + return tuple(iterators) + + +async def takewhile( + predicate: Callable[[T], Awaitable[object]], + iterable: Iterable[T] | AsyncIterable[T], +) -> AsyncGenerator[T, None]: + element_yielded = False + + async for element in _iterate(iterable): + if not await predicate(element): + if not element_yielded: + await checkpoint() + + return + + element_yielded = True + yield element + + if not element_yielded: + await checkpoint() + + +async def zip_longest( + *iterables: Iterable[object] | AsyncIterable[object], + fillvalue: object = None, +) -> AsyncGenerator[tuple[object, ...], None]: + iterators = [_iterate(iterable) for iterable in iterables] + num_active = len(iterators) + if not num_active: + await checkpoint() + return + + active = [True] * num_active + tuple_yielded = False + + while True: + values: list[object] = [] + for index, iterator in enumerate(iterators): + if not active[index]: + values.append(fillvalue) + continue + + try: + value = await anext(iterator) + except StopAsyncIteration: + active[index] = False + num_active -= 1 + if not num_active: + if not tuple_yielded: + await checkpoint() + + return + + value = fillvalue + + values.append(value) + + tuple_yielded = True + yield tuple(values) diff --git a/venv/lib/python3.12/site-packages/anyio/lowlevel.py b/venv/lib/python3.12/site-packages/anyio/lowlevel.py new file mode 100644 index 0000000..361b2eb --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/lowlevel.py @@ -0,0 +1,228 @@ +from __future__ import annotations + +__all__ = ( + "EventLoopToken", + "RunVar", + "RunvarToken", + "cancel_shielded_checkpoint", + "checkpoint", + "checkpoint_if_cancelled", + "current_token", +) + +import enum +from dataclasses import dataclass +from types import TracebackType +from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar, final, overload +from weakref import WeakKeyDictionary + +from ._core._eventloop import get_async_backend + +if TYPE_CHECKING: + from .abc import AsyncBackend + +T = TypeVar("T") +D = TypeVar("D") + + +async def checkpoint() -> None: + """ + Check for cancellation and allow the scheduler to switch to another task. + + Equivalent to (but more efficient than):: + + await checkpoint_if_cancelled() + await cancel_shielded_checkpoint() + + .. versionadded:: 3.0 + + """ + await get_async_backend().checkpoint() + + +async def checkpoint_if_cancelled() -> None: + """ + Enter a checkpoint if the enclosing cancel scope has been cancelled. + + This does not allow the scheduler to switch to a different task. + + .. versionadded:: 3.0 + + """ + await get_async_backend().checkpoint_if_cancelled() + + +async def cancel_shielded_checkpoint() -> None: + """ + Allow the scheduler to switch to another task but without checking for cancellation. + + Equivalent to (but potentially more efficient than):: + + with CancelScope(shield=True): + await checkpoint() + + .. versionadded:: 3.0 + + """ + await get_async_backend().cancel_shielded_checkpoint() + + +@final +@dataclass(frozen=True, repr=False) +class EventLoopToken: + """ + An opaque object that holds a reference to an event loop. + + .. versionadded:: 4.11.0 + """ + + backend_class: type[AsyncBackend] + native_token: object + + +def current_token() -> EventLoopToken: + """ + Return a token object that can be used to call code in the current event loop from + another thread. + + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. versionadded:: 4.11.0 + + """ + backend_class = get_async_backend() + raw_token = backend_class.current_token() + return EventLoopToken(backend_class, raw_token) + + +_run_vars: WeakKeyDictionary[object, dict[RunVar[Any], Any]] = WeakKeyDictionary() + + +class _NoValueSet(enum.Enum): + NO_VALUE_SET = enum.auto() + + +class RunvarToken(Generic[T]): + """ + A token that can be used to restore a :class:`RunVar` to its previous value. + + Returned by :meth:`RunVar.set`. Can be used as a context manager to automatically + reset the variable on exit, or passed directly to :meth:`RunVar.reset`. + """ + + __slots__ = "_redeemed", "_value", "_var" + + def __init__(self, var: RunVar[T], value: T | Literal[_NoValueSet.NO_VALUE_SET]): + self._var = var + self._value: T | Literal[_NoValueSet.NO_VALUE_SET] = value + self._redeemed = False + + def __enter__(self) -> RunvarToken[T]: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self._var.reset(self) + + +class RunVar(Generic[T]): + """ + Like a :class:`~contextvars.ContextVar`, except scoped to the running event loop. + + Can be used as a context manager, Just like :class:`~contextvars.ContextVar`, that + will reset the variable to its previous value when the context block is exited. + """ + + __slots__ = "_default", "_name" + + NO_VALUE_SET: Literal[_NoValueSet.NO_VALUE_SET] = _NoValueSet.NO_VALUE_SET + + def __init__( + self, name: str, default: T | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET + ): + self._name = name + self._default = default + + @property + def _current_vars(self) -> dict[RunVar[T], T]: + native_token = current_token().native_token + try: + return _run_vars[native_token] + except KeyError: + run_vars = _run_vars[native_token] = {} + return run_vars + + @overload + def get(self, default: D) -> T | D: ... + + @overload + def get(self) -> T: ... + + def get( + self, default: D | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET + ) -> T | D: + """ + Return the current value of this run variable. + + :param default: a fallback value to return if no value has been set + :return: the current value, the provided default, or the variable's own default + :raises LookupError: if no value is set and no default is available + + """ + try: + return self._current_vars[self] + except KeyError: + if default is not RunVar.NO_VALUE_SET: + return default + elif self._default is not RunVar.NO_VALUE_SET: + return self._default + + raise LookupError( + f'Run variable "{self._name}" has no value and no default set' + ) + + def set(self, value: T) -> RunvarToken[T]: + """ + Set the value of this run variable for the current event loop. + + :param value: the new value + :return: a token that can be used to restore the previous value + + """ + current_vars = self._current_vars + token = RunvarToken(self, current_vars.get(self, RunVar.NO_VALUE_SET)) + current_vars[self] = value + return token + + def reset(self, token: RunvarToken[T]) -> None: + """ + Restore this run variable to the value it held before the matching :meth:`set`. + + :param token: the token returned by :meth:`set` + :raises ValueError: if the token belongs to a different :class:`RunVar` or the token + has already been used + + """ + if token._var is not self: + raise ValueError("This token does not belong to this RunVar") + + if token._redeemed: + raise ValueError("This token has already been used") + + if token._value is _NoValueSet.NO_VALUE_SET: + try: + del self._current_vars[self] + except KeyError: + pass + else: + self._current_vars[self] = token._value + + token._redeemed = True + + def __repr__(self) -> str: + return f"" diff --git a/venv/lib/python3.12/site-packages/anyio/py.typed b/venv/lib/python3.12/site-packages/anyio/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/anyio/pytest_plugin.py b/venv/lib/python3.12/site-packages/anyio/pytest_plugin.py new file mode 100644 index 0000000..d6238cd --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/pytest_plugin.py @@ -0,0 +1,404 @@ +from __future__ import annotations + +import dataclasses +import socket +import sys +from collections.abc import Callable, Generator, Iterator +from contextlib import ExitStack, contextmanager +from inspect import isasyncgenfunction, iscoroutinefunction, ismethod +from typing import TYPE_CHECKING, Any, cast + +import pytest +from _pytest.fixtures import FuncFixtureInfo, SubRequest +from _pytest.outcomes import Exit +from _pytest.scope import Scope + +from . import get_available_backends +from ._core._eventloop import ( + current_async_library, + get_async_backend, + reset_current_async_library, + set_current_async_library, +) +from ._core._exceptions import iterate_exceptions + +if TYPE_CHECKING: + from .abc import TestRunner + +if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup + +if TYPE_CHECKING: + # pytest >= 9.2 keeps CallSpec2 as a TYPE_CHECKING-only alias of CallSpec + from _pytest.python import CallSpec2 as CallSpec +else: + try: + # c.f. https://github.com/pytest-dev/pytest/pull/14742 + # pytest >= 9.2 + from _pytest.python import CallSpec + except ImportError: + from _pytest.python import CallSpec2 as CallSpec + +_current_runner: TestRunner | None = None +_runner_stack: ExitStack | None = None +_runner_leases = 0 + + +def extract_backend_and_options(backend: object) -> tuple[str, dict[str, Any]]: + if isinstance(backend, str): + return backend, {} + elif isinstance(backend, tuple) and len(backend) == 2: + if isinstance(backend[0], str) and isinstance(backend[1], dict): + return cast(tuple[str, dict[str, Any]], backend) + + raise TypeError("anyio_backend must be either a string or tuple of (string, dict)") + + +@contextmanager +def get_runner( + backend_name: str, backend_options: dict[str, Any] +) -> Iterator[TestRunner]: + global _current_runner, _runner_leases, _runner_stack + if _current_runner is None: + asynclib = get_async_backend(backend_name) + _runner_stack = ExitStack() + if current_async_library() is None: + # Since we're in control of the event loop, we can cache the name of the + # async library + token = set_current_async_library(backend_name) + _runner_stack.callback(reset_current_async_library, token) + + backend_options = backend_options or {} + _current_runner = _runner_stack.enter_context( + asynclib.create_test_runner(backend_options) + ) + + _runner_leases += 1 + try: + yield _current_runner + finally: + _runner_leases -= 1 + if not _runner_leases: + assert _runner_stack is not None + _runner_stack.close() + _runner_stack = _current_runner = None + + +def pytest_addoption(parser: pytest.Parser) -> None: + group = parser.getgroup("anyio") + parser.addini( + "anyio_mode", + default="strict", + help='AnyIO plugin mode (either "strict" or "auto")', + ) + group.addoption( + "--anyio-mode", + default=None, + dest="anyio_mode", + help=""" + 'auto' - All async test functions will be handled by AnyIO pytest plugin + 'strict' - Disabling autoprocessing(useful when anyio tests + need to coexist with other async test plugins) + """, + ) + + +def _is_auto_mode(config: pytest.Config, mode: str) -> bool: + return ( + config.getoption(mode, default=None) == "auto" or config.getini(mode) == "auto" + ) + + +def pytest_configure(config: pytest.Config) -> None: + config.addinivalue_line( + "markers", + "anyio: mark the (coroutine function) test to be run asynchronously via anyio.", + ) + if ( + _is_auto_mode(config, "anyio_mode") + and config.pluginmanager.has_plugin("asyncio") + and _is_auto_mode(config, "asyncio_mode") + ): + config.issue_config_time_warning( + pytest.PytestConfigWarning( + "AnyIO auto mode has been enabled together with pytest-asyncio auto " + "mode. This may cause unexpected behavior." + ), + 1, + ) + + +@pytest.hookimpl(hookwrapper=True) +def pytest_fixture_setup(fixturedef: Any, request: Any) -> Generator[Any]: + def wrapper(anyio_backend: Any, request: SubRequest, **kwargs: Any) -> Any: + # Rebind any fixture methods to the request instance + if ( + request.instance + and ismethod(func) + and type(func.__self__) is type(request.instance) + ): + local_func = func.__func__.__get__(request.instance) + else: + local_func = func + + backend_name, backend_options = extract_backend_and_options(anyio_backend) + if has_backend_arg: + kwargs["anyio_backend"] = anyio_backend + + if has_request_arg: + kwargs["request"] = request + + with get_runner(backend_name, backend_options) as runner: + # re-entrant call into the test runner detected. this happens when an async fixture + # is dynamically requested via request.getfixturevalue() from inside a running async + # test or fixture. on asyncio this raises RuntimeError: This event loop is already + # running, on trio the runner deadlocks - the host loop blocks waiting for the + # coroutine to return, but the coroutine is waiting for the host loop. raising here + # prevents the hang and gives a consistent error across backends. + if runner.is_running(): + raise RuntimeError( + "Cannot schedule a coroutine in the test runner while another is already running; " + "likely caused by request.getfixturevalue() on an async fixture." + ) + + if isasyncgenfunction(local_func): + yield from runner.run_asyncgen_fixture(local_func, kwargs) + else: + yield runner.run_fixture(local_func, kwargs) + + # Only apply this to coroutine functions and async generator functions in requests + # that involve the anyio_backend fixture + func = fixturedef.func + if isasyncgenfunction(func) or iscoroutinefunction(func): + if "anyio_backend" in request.fixturenames: + fixturedef.func = wrapper + original_argname = fixturedef.argnames + + if not (has_backend_arg := "anyio_backend" in fixturedef.argnames): + fixturedef.argnames += ("anyio_backend",) + + if not (has_request_arg := "request" in fixturedef.argnames): + fixturedef.argnames += ("request",) + + try: + return (yield) + finally: + fixturedef.func = func + fixturedef.argnames = original_argname + + return (yield) + + +@pytest.hookimpl(tryfirst=True) +def pytest_pycollect_makeitem( + collector: pytest.Module | pytest.Class, name: str, obj: object +) -> None: + if collector.istestfunction(obj, name): + inner_func = obj.hypothesis.inner_test if hasattr(obj, "hypothesis") else obj + if iscoroutinefunction(inner_func): + anyio_auto_mode = _is_auto_mode(collector.config, "anyio_mode") + marker = collector.get_closest_marker("anyio") + own_markers = getattr(obj, "pytestmark", ()) + if ( + anyio_auto_mode + or marker + or any(marker.name == "anyio" for marker in own_markers) + ): + pytest.mark.usefixtures("anyio_backend")(obj) + + +def pytest_collection_finish(session: pytest.Session) -> None: + for i, item in reversed(list(enumerate(session.items))): + if ( + isinstance(item, pytest.Function) + and iscoroutinefunction(item.function) + and item.get_closest_marker("anyio") is not None + and "anyio_backend" not in item.fixturenames + ): + new_items = [] + try: + cs_fields = {f.name for f in dataclasses.fields(CallSpec)} + except TypeError: + cs_fields = set() + + for param_index, backend in enumerate(get_available_backends()): + if "_arg2scope" in cs_fields: # pytest >= 8 + callspec = CallSpec( + params={"anyio_backend": backend}, + indices={"anyio_backend": param_index}, + _arg2scope={"anyio_backend": Scope.Module}, + _idlist=[backend], + marks=[], + ) + else: # pytest 7.x + callspec = CallSpec( # type: ignore[call-arg] + funcargs={}, + params={"anyio_backend": backend}, + indices={"anyio_backend": param_index}, + arg2scope={"anyio_backend": Scope.Module}, + idlist=[backend], + marks=[], + ) + + fi = item._fixtureinfo + new_names_closure = list(fi.names_closure) + if "anyio_backend" not in new_names_closure: + new_names_closure.append("anyio_backend") + + new_fixtureinfo = FuncFixtureInfo( + argnames=fi.argnames, + initialnames=fi.initialnames, + names_closure=new_names_closure, + name2fixturedefs=fi.name2fixturedefs, + ) + new_item = pytest.Function.from_parent( + item.parent, + name=f"{item.originalname}[{backend}]", + callspec=callspec, + callobj=item.obj, + fixtureinfo=new_fixtureinfo, + keywords=item.keywords, + originalname=item.originalname, + ) + new_items.append(new_item) + + session.items[i : i + 1] = new_items + + +@pytest.hookimpl(tryfirst=True) +def pytest_pyfunc_call(pyfuncitem: Any) -> bool | None: + def run_with_hypothesis(**kwargs: Any) -> None: + with get_runner(backend_name, backend_options) as runner: + runner.run_test(original_func, kwargs) + + backend = pyfuncitem.funcargs.get("anyio_backend") + if backend: + backend_name, backend_options = extract_backend_and_options(backend) + + if hasattr(pyfuncitem.obj, "hypothesis"): + # Wrap the inner test function unless it's already wrapped + original_func = pyfuncitem.obj.hypothesis.inner_test + if original_func.__qualname__ != run_with_hypothesis.__qualname__: + if iscoroutinefunction(original_func): + pyfuncitem.obj.hypothesis.inner_test = run_with_hypothesis + + return None + + if iscoroutinefunction(pyfuncitem.obj): + funcargs = pyfuncitem.funcargs + testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} + with get_runner(backend_name, backend_options) as runner: + try: + runner.run_test(pyfuncitem.obj, testargs) + except ExceptionGroup as excgrp: + for exc in iterate_exceptions(excgrp): + if isinstance(exc, (Exit, KeyboardInterrupt, SystemExit)): + raise exc from excgrp + + raise + + return True + + return None + + +@pytest.fixture(scope="module", params=get_available_backends()) +def anyio_backend(request: Any) -> Any: + return request.param + + +@pytest.fixture +def anyio_backend_name(anyio_backend: Any) -> str: + if isinstance(anyio_backend, str): + return anyio_backend + else: + return anyio_backend[0] + + +@pytest.fixture +def anyio_backend_options(anyio_backend: Any) -> dict[str, Any]: + if isinstance(anyio_backend, str): + return {} + else: + return anyio_backend[1] + + +class FreePortFactory: + """ + Manages port generation based on specified socket kind, ensuring no duplicate + ports are generated. + + This class provides functionality for generating available free ports on the + system. It is initialized with a specific socket kind and can generate ports + for given address families while avoiding reuse of previously generated ports. + + Users should not instantiate this class directly, but use the + ``free_tcp_port_factory`` and ``free_udp_port_factory`` fixtures instead. For simple + uses cases, ``free_tcp_port`` and ``free_udp_port`` can be used instead. + """ + + def __init__(self, kind: socket.SocketKind) -> None: + self._kind = kind + self._generated = set[int]() + + @property + def kind(self) -> socket.SocketKind: + """ + The type of socket connection (e.g., :data:`~socket.SOCK_STREAM` or + :data:`~socket.SOCK_DGRAM`) used to bind for checking port availability + + """ + return self._kind + + def __call__(self, family: socket.AddressFamily | None = None) -> int: + """ + Return an unbound port for the given address family. + + :param family: if omitted, both IPv4 and IPv6 addresses will be tried + :return: a port number + + """ + if family is not None: + families = [family] + else: + families = [socket.AF_INET] + if socket.has_ipv6: + families.append(socket.AF_INET6) + + while True: + port = 0 + with ExitStack() as stack: + for fam in families: + sock = stack.enter_context(socket.socket(fam, self._kind)) + addr = "::1" if fam == socket.AF_INET6 else "127.0.0.1" + try: + sock.bind((addr, port)) + except OSError: + break + + if not port: + port = sock.getsockname()[1] + else: + if port not in self._generated: + self._generated.add(port) + return port + + +@pytest.fixture(scope="session") +def free_tcp_port_factory() -> FreePortFactory: + return FreePortFactory(socket.SOCK_STREAM) + + +@pytest.fixture(scope="session") +def free_udp_port_factory() -> FreePortFactory: + return FreePortFactory(socket.SOCK_DGRAM) + + +@pytest.fixture +def free_tcp_port(free_tcp_port_factory: Callable[[], int]) -> int: + return free_tcp_port_factory() + + +@pytest.fixture +def free_udp_port(free_udp_port_factory: Callable[[], int]) -> int: + return free_udp_port_factory() diff --git a/venv/lib/python3.12/site-packages/anyio/streams/__init__.py b/venv/lib/python3.12/site-packages/anyio/streams/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..28b9c11 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/buffered.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/buffered.cpython-312.pyc new file mode 100644 index 0000000..9ce7d46 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/buffered.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/file.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/file.cpython-312.pyc new file mode 100644 index 0000000..00a737c Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/file.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/memory.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/memory.cpython-312.pyc new file mode 100644 index 0000000..0d85be4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/memory.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/stapled.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/stapled.cpython-312.pyc new file mode 100644 index 0000000..4e64b3e Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/stapled.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/text.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/text.cpython-312.pyc new file mode 100644 index 0000000..bf39e38 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/tls.cpython-312.pyc b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/tls.cpython-312.pyc new file mode 100644 index 0000000..a2513b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/anyio/streams/__pycache__/tls.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/anyio/streams/buffered.py b/venv/lib/python3.12/site-packages/anyio/streams/buffered.py new file mode 100644 index 0000000..a3b07f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/streams/buffered.py @@ -0,0 +1,201 @@ +from __future__ import annotations + +__all__ = ( + "BufferedByteReceiveStream", + "BufferedByteStream", + "BufferedConnectable", +) + +import sys +from collections.abc import Callable, Iterable, Mapping +from dataclasses import dataclass, field +from typing import Any, SupportsIndex + +from .. import ClosedResourceError, DelimiterNotFound, EndOfStream, IncompleteRead +from ..abc import ( + AnyByteReceiveStream, + AnyByteStream, + AnyByteStreamConnectable, + ByteReceiveStream, + ByteStream, + ByteStreamConnectable, +) + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + + +@dataclass(eq=False) +class BufferedByteReceiveStream(ByteReceiveStream): + """ + Wraps any bytes-based receive stream and uses a buffer to provide sophisticated + receiving capabilities in the form of a byte stream. + """ + + receive_stream: AnyByteReceiveStream + _buffer: bytearray = field(init=False, default_factory=bytearray) + _closed: bool = field(init=False, default=False) + + async def aclose(self) -> None: + await self.receive_stream.aclose() + self._closed = True + + @property + def buffer(self) -> bytes: + """The bytes currently in the buffer.""" + return bytes(self._buffer) + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return self.receive_stream.extra_attributes + + def feed_data(self, data: Iterable[SupportsIndex], /) -> None: + """ + Append data directly into the buffer. + + Any data in the buffer will be consumed by receive operations before receiving + anything from the wrapped stream. + + :param data: the data to append to the buffer (can be bytes or anything else + that supports ``__index__()``) + + """ + self._buffer.extend(data) + + async def receive(self, max_bytes: int = 65536) -> bytes: + if max_bytes < 1: + raise ValueError("max_bytes must be a positive integer") + + if self._closed: + raise ClosedResourceError + + if self._buffer: + chunk = bytes(self._buffer[:max_bytes]) + del self._buffer[:max_bytes] + return chunk + elif isinstance(self.receive_stream, ByteReceiveStream): + return await self.receive_stream.receive(max_bytes) + else: + # With a bytes-oriented object stream, we need to handle any surplus bytes + # we get from the receive() call + chunk = await self.receive_stream.receive() + if len(chunk) > max_bytes: + # Save the surplus bytes in the buffer + self._buffer.extend(chunk[max_bytes:]) + return chunk[:max_bytes] + else: + return chunk + + async def receive_exactly(self, nbytes: int) -> bytes: + """ + Read exactly the given amount of bytes from the stream. + + :param nbytes: the number of bytes to read + :return: the bytes read + :raises ~anyio.IncompleteRead: if the stream was closed before the requested + amount of bytes could be read from the stream + + """ + while True: + remaining = nbytes - len(self._buffer) + if remaining <= 0: + retval = self._buffer[:nbytes] + del self._buffer[:nbytes] + return bytes(retval) + + try: + if isinstance(self.receive_stream, ByteReceiveStream): + chunk = await self.receive_stream.receive(remaining) + else: + chunk = await self.receive_stream.receive() + except EndOfStream as exc: + raise IncompleteRead from exc + + self._buffer.extend(chunk) + + async def receive_until(self, delimiter: bytes, max_bytes: int) -> bytes: + """ + Read from the stream until the delimiter is found or max_bytes have been read. + + :param delimiter: the marker to look for in the stream + :param max_bytes: maximum number of bytes that will be read before raising + :exc:`~anyio.DelimiterNotFound` + :return: the bytes read (not including the delimiter) + :raises ~anyio.IncompleteRead: if the stream was closed before the delimiter + was found + :raises ~anyio.DelimiterNotFound: if the delimiter is not found within the + bytes read up to the maximum allowed + + """ + delimiter_size = len(delimiter) + offset = 0 + while True: + # Check if the delimiter can be found in the current buffer + index = self._buffer.find(delimiter, offset) + if index >= 0: + found = self._buffer[:index] + del self._buffer[: index + len(delimiter) :] + return bytes(found) + + # Check if the buffer is already at or over the limit + if len(self._buffer) >= max_bytes: + raise DelimiterNotFound(max_bytes) + + # Read more data into the buffer from the socket + try: + data = await self.receive_stream.receive() + except EndOfStream as exc: + raise IncompleteRead from exc + + # Move the offset forward and add the new data to the buffer + offset = max(len(self._buffer) - delimiter_size + 1, 0) + self._buffer.extend(data) + + +class BufferedByteStream(BufferedByteReceiveStream, ByteStream): + """ + A full-duplex variant of :class:`BufferedByteReceiveStream`. All writes are passed + through to the wrapped stream as-is. + """ + + def __init__(self, stream: AnyByteStream): + """ + :param stream: the stream to be wrapped + + """ + super().__init__(stream) + self._stream = stream + + @override + async def send_eof(self) -> None: + await self._stream.send_eof() + + @override + async def send(self, item: bytes) -> None: + await self._stream.send(item) + + +class BufferedConnectable(ByteStreamConnectable): + """ + Wraps a byte stream connectable to produce :class:`BufferedByteStream` connections. + + Use this when you want the streams returned by :meth:`connect` to have the buffered + receive API (e.g. :meth:`~BufferedByteReceiveStream.receive_exactly` and + :meth:`~BufferedByteReceiveStream.receive_until`). + + :param connectable: the byte stream connectable to wrap + """ + + def __init__(self, connectable: AnyByteStreamConnectable): + """ + :param connectable: the connectable to wrap + + """ + self.connectable = connectable + + @override + async def connect(self) -> BufferedByteStream: + stream = await self.connectable.connect() + return BufferedByteStream(stream) diff --git a/venv/lib/python3.12/site-packages/anyio/streams/file.py b/venv/lib/python3.12/site-packages/anyio/streams/file.py new file mode 100644 index 0000000..b9d7ca4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/streams/file.py @@ -0,0 +1,157 @@ +from __future__ import annotations + +__all__ = ( + "FileReadStream", + "FileStreamAttribute", + "FileWriteStream", +) + +from collections.abc import Callable, Mapping +from io import SEEK_SET, UnsupportedOperation +from os import PathLike +from pathlib import Path +from typing import IO, Any + +from .. import ( + BrokenResourceError, + ClosedResourceError, + EndOfStream, + TypedAttributeSet, + to_thread, + typed_attribute, +) +from ..abc import ByteReceiveStream, ByteSendStream + + +class FileStreamAttribute(TypedAttributeSet): + #: the open file descriptor + file: IO[bytes] = typed_attribute() + #: the path of the file on the file system, if available (file must be a real file) + path: Path = typed_attribute() + #: the file number, if available (file must be a real file or a TTY) + fileno: int = typed_attribute() + + +class _BaseFileStream: + def __init__(self, file: IO[bytes]): + self._file = file + + async def aclose(self) -> None: + await to_thread.run_sync(self._file.close) + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + attributes: dict[Any, Callable[[], Any]] = { + FileStreamAttribute.file: lambda: self._file, + } + + if hasattr(self._file, "name"): + attributes[FileStreamAttribute.path] = lambda: Path(self._file.name) + + try: + self._file.fileno() + except UnsupportedOperation: + pass + else: + attributes[FileStreamAttribute.fileno] = lambda: self._file.fileno() + + return attributes + + +class FileReadStream(_BaseFileStream, ByteReceiveStream): + """ + A byte stream that reads from a file in the file system. + + :param file: a file that has been opened for reading in binary mode + + .. versionadded:: 3.0 + """ + + @classmethod + async def from_path(cls, path: str | PathLike[str]) -> FileReadStream: + """ + Create a file read stream by opening the given file. + + :param path: path of the file to read from + + """ + file = await to_thread.run_sync(Path(path).open, "rb") + return cls(file) + + async def receive(self, max_bytes: int = 65536) -> bytes: + if max_bytes < 1: + raise ValueError("max_bytes must be a positive integer") + + try: + data = await to_thread.run_sync(self._file.read, max_bytes) + except ValueError: + raise ClosedResourceError from None + except OSError as exc: + raise BrokenResourceError from exc + + if data: + return data + else: + raise EndOfStream + + async def seek(self, position: int, whence: int = SEEK_SET) -> int: + """ + Seek the file to the given position. + + .. seealso:: :meth:`io.IOBase.seek` + + .. note:: Not all file descriptors are seekable. + + :param position: position to seek the file to + :param whence: controls how ``position`` is interpreted + :return: the new absolute position + :raises OSError: if the file is not seekable + + """ + return await to_thread.run_sync(self._file.seek, position, whence) + + async def tell(self) -> int: + """ + Return the current stream position. + + .. note:: Not all file descriptors are seekable. + + :return: the current absolute position + :raises OSError: if the file is not seekable + + """ + return await to_thread.run_sync(self._file.tell) + + +class FileWriteStream(_BaseFileStream, ByteSendStream): + """ + A byte stream that writes to a file in the file system. + + :param file: a file that has been opened for writing in binary mode + + .. versionadded:: 3.0 + """ + + @classmethod + async def from_path( + cls, path: str | PathLike[str], append: bool = False + ) -> FileWriteStream: + """ + Create a file write stream by opening the given file for writing. + + :param path: path of the file to write to + :param append: if ``True``, open the file for appending; if ``False``, any + existing file at the given path will be truncated + + """ + mode = "ab" if append else "wb" + file = await to_thread.run_sync(Path(path).open, mode) + return cls(file) + + async def send(self, item: bytes) -> None: + try: + await to_thread.run_sync(self._file.write, item) + except ValueError: + raise ClosedResourceError from None + except OSError as exc: + raise BrokenResourceError from exc diff --git a/venv/lib/python3.12/site-packages/anyio/streams/memory.py b/venv/lib/python3.12/site-packages/anyio/streams/memory.py new file mode 100644 index 0000000..329a75e --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/streams/memory.py @@ -0,0 +1,326 @@ +from __future__ import annotations + +__all__ = ( + "MemoryObjectReceiveStream", + "MemoryObjectSendStream", + "MemoryObjectStreamStatistics", +) + +import warnings +from collections import OrderedDict, deque +from dataclasses import dataclass, field +from types import TracebackType +from typing import Generic, NamedTuple, TypeVar + +from .._core._exceptions import ( + BrokenResourceError, + ClosedResourceError, + EndOfStream, + WouldBlock, +) +from .._core._synchronization import Event +from .._core._testing import TaskInfo, get_current_task +from ..abc import ObjectReceiveStream, ObjectSendStream +from ..lowlevel import checkpoint + +T_Item = TypeVar("T_Item") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class MemoryObjectStreamStatistics(NamedTuple): + current_buffer_used: int #: number of items stored in the buffer + #: maximum number of items that can be stored on this stream (or :data:`math.inf`) + max_buffer_size: float + open_send_streams: int #: number of unclosed clones of the send stream + open_receive_streams: int #: number of unclosed clones of the receive stream + #: number of tasks blocked on :meth:`MemoryObjectSendStream.send` + tasks_waiting_send: int + #: number of tasks blocked on :meth:`MemoryObjectReceiveStream.receive` + tasks_waiting_receive: int + + +@dataclass(eq=False) +class _MemoryObjectItemReceiver(Generic[T_Item]): + task_info: TaskInfo = field(init=False, default_factory=get_current_task) + item: T_Item = field(init=False) + + def __repr__(self) -> str: + # When item is not defined, we get following error with default __repr__: + # AttributeError: 'MemoryObjectItemReceiver' object has no attribute 'item' + item = getattr(self, "item", None) + return f"{self.__class__.__name__}(task_info={self.task_info}, item={item!r})" + + +@dataclass(eq=False) +class _MemoryObjectStreamState(Generic[T_Item]): + max_buffer_size: float = field() + buffer: deque[T_Item] = field(init=False, default_factory=deque) + open_send_channels: int = field(init=False, default=0) + open_receive_channels: int = field(init=False, default=0) + waiting_receivers: OrderedDict[Event, _MemoryObjectItemReceiver[T_Item]] = field( + init=False, default_factory=OrderedDict + ) + waiting_senders: OrderedDict[Event, T_Item] = field( + init=False, default_factory=OrderedDict + ) + + def statistics(self) -> MemoryObjectStreamStatistics: + return MemoryObjectStreamStatistics( + len(self.buffer), + self.max_buffer_size, + self.open_send_channels, + self.open_receive_channels, + len(self.waiting_senders), + len(self.waiting_receivers), + ) + + +@dataclass(eq=False) +class MemoryObjectReceiveStream(ObjectReceiveStream[T_co], Generic[T_co]): + _state: _MemoryObjectStreamState[T_co] + _closed: bool = field(init=False, default=False) + + def __post_init__(self) -> None: + self._state.open_receive_channels += 1 + + def receive_nowait(self) -> T_co: + """ + Receive the next item if it can be done without waiting. + + :return: the received item + :raises ~anyio.ClosedResourceError: if this send stream has been closed + :raises ~anyio.EndOfStream: if the buffer is empty and this stream has been + closed from the sending end + :raises ~anyio.WouldBlock: if there are no items in the buffer and no tasks + waiting to send + + """ + if self._closed: + raise ClosedResourceError + + if self._state.waiting_senders: + # Get the item from the next sender + send_event, item = self._state.waiting_senders.popitem(last=False) + self._state.buffer.append(item) + send_event.set() + + if self._state.buffer: + return self._state.buffer.popleft() + elif not self._state.open_send_channels: + raise EndOfStream + + raise WouldBlock + + async def receive(self) -> T_co: + await checkpoint() + try: + return self.receive_nowait() + except WouldBlock: + # Add ourselves in the queue + receive_event = Event() + receiver = _MemoryObjectItemReceiver[T_co]() + self._state.waiting_receivers[receive_event] = receiver + + try: + await receive_event.wait() + finally: + self._state.waiting_receivers.pop(receive_event, None) + + try: + return receiver.item + except AttributeError: + raise EndOfStream from None + + def clone(self) -> MemoryObjectReceiveStream[T_co]: + """ + Create a clone of this receive stream. + + Each clone can be closed separately. Only when all clones have been closed will + the receiving end of the memory stream be considered closed by the sending ends. + + :return: the cloned stream + + """ + if self._closed: + raise ClosedResourceError + + return MemoryObjectReceiveStream(_state=self._state) + + def close(self) -> None: + """ + Close the stream. + + This works the exact same way as :meth:`aclose`, but is provided as a special + case for the benefit of synchronous callbacks. + + """ + if not self._closed: + self._closed = True + self._state.open_receive_channels -= 1 + if self._state.open_receive_channels == 0: + send_events = list(self._state.waiting_senders.keys()) + for event in send_events: + event.set() + + async def aclose(self) -> None: + self.close() + + def statistics(self) -> MemoryObjectStreamStatistics: + """ + Return statistics about the current state of this stream. + + .. versionadded:: 3.0 + """ + return self._state.statistics() + + def __enter__(self) -> MemoryObjectReceiveStream[T_co]: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def __del__(self) -> None: + if not self._closed: + warnings.warn( + f"Unclosed <{self.__class__.__name__} at {id(self):x}>", + ResourceWarning, + stacklevel=1, + source=self, + ) + + +@dataclass(eq=False) +class MemoryObjectSendStream(ObjectSendStream[T_contra], Generic[T_contra]): + _state: _MemoryObjectStreamState[T_contra] + _closed: bool = field(init=False, default=False) + + def __post_init__(self) -> None: + self._state.open_send_channels += 1 + + def send_nowait(self, item: T_contra) -> None: + """ + Send an item immediately if it can be done without waiting. + + :param item: the item to send + :raises ~anyio.ClosedResourceError: if this send stream has been closed + :raises ~anyio.BrokenResourceError: if the stream has been closed from the + receiving end + :raises ~anyio.WouldBlock: if the buffer is full and there are no tasks waiting + to receive + + """ + if self._closed: + raise ClosedResourceError + if not self._state.open_receive_channels: + raise BrokenResourceError + + while self._state.waiting_receivers: + receive_event, receiver = self._state.waiting_receivers.popitem(last=False) + if not receiver.task_info.has_pending_cancellation(): + receiver.item = item + receive_event.set() + return + + if len(self._state.buffer) < self._state.max_buffer_size: + self._state.buffer.append(item) + else: + raise WouldBlock + + async def send(self, item: T_contra) -> None: + """ + Send an item to the stream. + + If the buffer is full, this method blocks until there is again room in the + buffer or the item can be sent directly to a receiver. + + :param item: the item to send + :raises ~anyio.ClosedResourceError: if this send stream has been closed + :raises ~anyio.BrokenResourceError: if the stream has been closed from the + receiving end + + """ + await checkpoint() + try: + self.send_nowait(item) + except WouldBlock: + # Wait until there's someone on the receiving end + send_event = Event() + self._state.waiting_senders[send_event] = item + try: + await send_event.wait() + except BaseException: + self._state.waiting_senders.pop(send_event, None) + raise + + if send_event in self._state.waiting_senders: + del self._state.waiting_senders[send_event] + raise BrokenResourceError from None + + def clone(self) -> MemoryObjectSendStream[T_contra]: + """ + Create a clone of this send stream. + + Each clone can be closed separately. Only when all clones have been closed will + the sending end of the memory stream be considered closed by the receiving ends. + + :return: the cloned stream + + """ + if self._closed: + raise ClosedResourceError + + return MemoryObjectSendStream(_state=self._state) + + def close(self) -> None: + """ + Close the stream. + + This works the exact same way as :meth:`aclose`, but is provided as a special + case for the benefit of synchronous callbacks. + + """ + if not self._closed: + self._closed = True + self._state.open_send_channels -= 1 + if self._state.open_send_channels == 0: + receive_events = list(self._state.waiting_receivers.keys()) + self._state.waiting_receivers.clear() + for event in receive_events: + event.set() + + async def aclose(self) -> None: + self.close() + + def statistics(self) -> MemoryObjectStreamStatistics: + """ + Return statistics about the current state of this stream. + + .. versionadded:: 3.0 + """ + return self._state.statistics() + + def __enter__(self) -> MemoryObjectSendStream[T_contra]: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def __del__(self) -> None: + if not self._closed: + warnings.warn( + f"Unclosed <{self.__class__.__name__} at {id(self):x}>", + ResourceWarning, + stacklevel=1, + source=self, + ) diff --git a/venv/lib/python3.12/site-packages/anyio/streams/stapled.py b/venv/lib/python3.12/site-packages/anyio/streams/stapled.py new file mode 100644 index 0000000..9a75345 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/streams/stapled.py @@ -0,0 +1,160 @@ +from __future__ import annotations + +__all__ = ( + "MultiListener", + "StapledByteStream", + "StapledObjectStream", +) + +from collections.abc import Callable, Mapping, Sequence +from dataclasses import dataclass +from typing import Any, Generic, TypeVar + +from ..abc import ( + ByteReceiveStream, + ByteSendStream, + ByteStream, + Listener, + ObjectReceiveStream, + ObjectSendStream, + ObjectStream, + TaskGroup, +) + +T_Item = TypeVar("T_Item") +T_Stream = TypeVar("T_Stream") + + +@dataclass(eq=False) +class StapledByteStream(ByteStream): + """ + Combines two byte streams into a single, bidirectional byte stream. + + Extra attributes will be provided from both streams, with the receive stream + providing the values in case of a conflict. + + :param ByteSendStream send_stream: the sending byte stream + :param ByteReceiveStream receive_stream: the receiving byte stream + """ + + send_stream: ByteSendStream + receive_stream: ByteReceiveStream + + async def receive(self, max_bytes: int = 65536) -> bytes: + if max_bytes < 1: + raise ValueError("max_bytes must be a positive integer") + + return await self.receive_stream.receive(max_bytes) + + async def send(self, item: bytes) -> None: + await self.send_stream.send(item) + + async def send_eof(self) -> None: + await self.send_stream.aclose() + + async def aclose(self) -> None: + await self.send_stream.aclose() + await self.receive_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self.send_stream.extra_attributes, + **self.receive_stream.extra_attributes, + } + + +@dataclass(eq=False) +class StapledObjectStream(ObjectStream[T_Item], Generic[T_Item]): + """ + Combines two object streams into a single, bidirectional object stream. + + Extra attributes will be provided from both streams, with the receive stream + providing the values in case of a conflict. + + :param ObjectSendStream send_stream: the sending object stream + :param ObjectReceiveStream receive_stream: the receiving object stream + """ + + send_stream: ObjectSendStream[T_Item] + receive_stream: ObjectReceiveStream[T_Item] + + async def receive(self) -> T_Item: + return await self.receive_stream.receive() + + async def send(self, item: T_Item) -> None: + await self.send_stream.send(item) + + def send_nowait(self, item: T_Item) -> None: + try: + send_nowait = self.send_stream.send_nowait # type: ignore[attr-defined] + except AttributeError as exc: + raise NotImplementedError( + f"'send_nowait' method not implemented in {type(self.send_stream)}" + ) from exc + + send_nowait(item) + + async def send_eof(self) -> None: + await self.send_stream.aclose() + + async def aclose(self) -> None: + await self.send_stream.aclose() + await self.receive_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self.send_stream.extra_attributes, + **self.receive_stream.extra_attributes, + } + + +@dataclass(eq=False) +class MultiListener(Listener[T_Stream], Generic[T_Stream]): + """ + Combines multiple listeners into one, serving connections from all of them at once. + + Any MultiListeners in the given collection of listeners will have their listeners + moved into this one. + + Extra attributes are provided from each listener, with each successive listener + overriding any conflicting attributes from the previous one. + + :param listeners: listeners to serve + :type listeners: Sequence[Listener[T_Stream]] + """ + + listeners: Sequence[Listener[T_Stream]] + + def __post_init__(self) -> None: + listeners: list[Listener[T_Stream]] = [] + for listener in self.listeners: + if isinstance(listener, MultiListener): + listeners.extend(listener.listeners) + del listener.listeners[:] # type: ignore[attr-defined] + else: + listeners.append(listener) + + self.listeners = listeners + + async def serve( + self, handler: Callable[[T_Stream], Any], task_group: TaskGroup | None = None + ) -> None: + from .. import create_task_group + + async with create_task_group() as tg: + for listener in self.listeners: + tg.start_soon(listener.serve, handler, task_group) + + async def aclose(self) -> None: + for listener in self.listeners: + await listener.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + attributes: dict = {} + for listener in self.listeners: + attributes.update(listener.extra_attributes) + + return attributes diff --git a/venv/lib/python3.12/site-packages/anyio/streams/text.py b/venv/lib/python3.12/site-packages/anyio/streams/text.py new file mode 100644 index 0000000..296cd25 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/streams/text.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +__all__ = ( + "TextConnectable", + "TextReceiveStream", + "TextSendStream", + "TextStream", +) + +import codecs +import sys +from collections.abc import Callable, Mapping +from dataclasses import InitVar, dataclass, field +from typing import Any + +from ..abc import ( + AnyByteReceiveStream, + AnyByteSendStream, + AnyByteStream, + AnyByteStreamConnectable, + ObjectReceiveStream, + ObjectSendStream, + ObjectStream, + ObjectStreamConnectable, +) + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + + +@dataclass(eq=False) +class TextReceiveStream(ObjectReceiveStream[str]): + """ + Stream wrapper that decodes bytes to strings using the given encoding. + + Decoding is done using :class:`~codecs.IncrementalDecoder` which returns any + completely received unicode characters as soon as they come in. + + :param transport_stream: any bytes-based receive stream + :param encoding: character encoding to use for decoding bytes to strings (defaults + to ``utf-8``) + :param errors: handling scheme for decoding errors (defaults to ``strict``; see the + `codecs module documentation`_ for a comprehensive list of options) + + .. _codecs module documentation: + https://docs.python.org/3/library/codecs.html#codec-objects + """ + + transport_stream: AnyByteReceiveStream + encoding: InitVar[str] = "utf-8" + errors: InitVar[str] = "strict" + _decoder: codecs.IncrementalDecoder = field(init=False) + + def __post_init__(self, encoding: str, errors: str) -> None: + decoder_class = codecs.getincrementaldecoder(encoding) + self._decoder = decoder_class(errors=errors) + + async def receive(self) -> str: + while True: + chunk = await self.transport_stream.receive() + decoded = self._decoder.decode(chunk) + if decoded: + return decoded + + async def aclose(self) -> None: + await self.transport_stream.aclose() + self._decoder.reset() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return self.transport_stream.extra_attributes + + +@dataclass(eq=False) +class TextSendStream(ObjectSendStream[str]): + """ + Sends strings to the wrapped stream as bytes using the given encoding. + + :param AnyByteSendStream transport_stream: any bytes-based send stream + :param str encoding: character encoding to use for encoding strings to bytes + (defaults to ``utf-8``) + :param str errors: handling scheme for encoding errors (defaults to ``strict``; see + the `codecs module documentation`_ for a comprehensive list of options) + + .. _codecs module documentation: + https://docs.python.org/3/library/codecs.html#codec-objects + """ + + transport_stream: AnyByteSendStream + encoding: InitVar[str] = "utf-8" + errors: str = "strict" + _encoder: Callable[..., tuple[bytes, int]] = field(init=False) + + def __post_init__(self, encoding: str) -> None: + self._encoder = codecs.getencoder(encoding) + + async def send(self, item: str) -> None: + encoded = self._encoder(item, self.errors)[0] + await self.transport_stream.send(encoded) + + async def aclose(self) -> None: + await self.transport_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return self.transport_stream.extra_attributes + + +@dataclass(eq=False) +class TextStream(ObjectStream[str]): + """ + A bidirectional stream that decodes bytes to strings on receive and encodes strings + to bytes on send. + + Extra attributes will be provided from both streams, with the receive stream + providing the values in case of a conflict. + + :param AnyByteStream transport_stream: any bytes-based stream + :param str encoding: character encoding to use for encoding/decoding strings to/from + bytes (defaults to ``utf-8``) + :param str errors: handling scheme for encoding errors (defaults to ``strict``; see + the `codecs module documentation`_ for a comprehensive list of options) + + .. _codecs module documentation: + https://docs.python.org/3/library/codecs.html#codec-objects + """ + + transport_stream: AnyByteStream + encoding: InitVar[str] = "utf-8" + errors: InitVar[str] = "strict" + _receive_stream: TextReceiveStream = field(init=False) + _send_stream: TextSendStream = field(init=False) + + def __post_init__(self, encoding: str, errors: str) -> None: + self._receive_stream = TextReceiveStream( + self.transport_stream, encoding=encoding, errors=errors + ) + self._send_stream = TextSendStream( + self.transport_stream, encoding=encoding, errors=errors + ) + + async def receive(self) -> str: + return await self._receive_stream.receive() + + async def send(self, item: str) -> None: + await self._send_stream.send(item) + + async def send_eof(self) -> None: + await self.transport_stream.send_eof() + + async def aclose(self) -> None: + await self._send_stream.aclose() + await self._receive_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self._send_stream.extra_attributes, + **self._receive_stream.extra_attributes, + } + + +class TextConnectable(ObjectStreamConnectable[str]): + def __init__(self, connectable: AnyByteStreamConnectable): + """ + :param connectable: the bytestream endpoint to wrap + + """ + self.connectable = connectable + + @override + async def connect(self) -> TextStream: + stream = await self.connectable.connect() + return TextStream(stream) diff --git a/venv/lib/python3.12/site-packages/anyio/streams/tls.py b/venv/lib/python3.12/site-packages/anyio/streams/tls.py new file mode 100644 index 0000000..b9ddf1d --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/streams/tls.py @@ -0,0 +1,436 @@ +from __future__ import annotations + +__all__ = ( + "TLSAttribute", + "TLSConnectable", + "TLSListener", + "TLSStream", +) + +import logging +import re +import ssl +import sys +from collections.abc import Callable, Mapping +from dataclasses import dataclass +from functools import wraps +from ssl import SSLContext +from typing import Any, TypeAlias, TypeVar + +from .. import ( + BrokenResourceError, + EndOfStream, + aclose_forcefully, + get_cancelled_exc_class, + to_thread, +) +from .._core._typedattr import TypedAttributeSet, typed_attribute +from ..abc import ( + AnyByteStream, + AnyByteStreamConnectable, + ByteStream, + ByteStreamConnectable, + Listener, + TaskGroup, +) + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") +_PCTRTT: TypeAlias = tuple[tuple[str, str], ...] +_PCTRTTT: TypeAlias = tuple[_PCTRTT, ...] + + +class TLSAttribute(TypedAttributeSet): + """Contains Transport Layer Security related attributes.""" + + #: the selected ALPN protocol + alpn_protocol: str | None = typed_attribute() + #: the channel binding for type ``tls-unique`` + channel_binding_tls_unique: bytes = typed_attribute() + #: the selected cipher + cipher: tuple[str, str, int] = typed_attribute() + #: the peer certificate in dictionary form (see :meth:`ssl.SSLSocket.getpeercert` + # for more information) + peer_certificate: None | (dict[str, str | _PCTRTTT | _PCTRTT]) = typed_attribute() + #: the peer certificate in binary form + peer_certificate_binary: bytes | None = typed_attribute() + #: ``True`` if this is the server side of the connection + server_side: bool = typed_attribute() + #: ciphers shared by the client during the TLS handshake (``None`` if this is the + #: client side) + shared_ciphers: list[tuple[str, str, int]] | None = typed_attribute() + #: the :class:`~ssl.SSLObject` used for encryption + ssl_object: ssl.SSLObject = typed_attribute() + #: ``True`` if this stream does (and expects) a closing TLS handshake when the + #: stream is being closed + standard_compatible: bool = typed_attribute() + #: the TLS protocol version (e.g. ``TLSv1.2``) + tls_version: str = typed_attribute() + + +@dataclass(eq=False) +class TLSStream(ByteStream): + """ + A stream wrapper that encrypts all sent data and decrypts received data. + + This class has no public initializer; use :meth:`wrap` instead. + All extra attributes from :class:`~TLSAttribute` are supported. + + :var AnyByteStream transport_stream: the wrapped stream + + """ + + transport_stream: AnyByteStream + standard_compatible: bool + _ssl_object: ssl.SSLObject + _read_bio: ssl.MemoryBIO + _write_bio: ssl.MemoryBIO + + @classmethod + async def wrap( + cls, + transport_stream: AnyByteStream, + *, + server_side: bool | None = None, + hostname: str | None = None, + ssl_context: ssl.SSLContext | None = None, + standard_compatible: bool = True, + ) -> TLSStream: + """ + Wrap an existing stream with Transport Layer Security. + + This performs a TLS handshake with the peer. + + :param transport_stream: a bytes-transporting stream to wrap + :param server_side: ``True`` if this is the server side of the connection, + ``False`` if this is the client side (if omitted, will be set to ``False`` + if ``hostname`` has been provided, ``False`` otherwise). Used only to create + a default context when an explicit context has not been provided. + :param hostname: host name of the peer (if host name checking is desired) + :param ssl_context: the SSLContext object to use (if not provided, a secure + default will be created) + :param standard_compatible: if ``False``, skip the closing handshake when + closing the connection, and don't raise an exception if the peer does the + same + :raises ~ssl.SSLError: if the TLS handshake fails + + """ + if server_side is None: + server_side = not hostname + + if not ssl_context: + purpose = ( + ssl.Purpose.CLIENT_AUTH if server_side else ssl.Purpose.SERVER_AUTH + ) + ssl_context = ssl.create_default_context(purpose) + + # Re-enable detection of unexpected EOFs if it was disabled by Python + if hasattr(ssl, "OP_IGNORE_UNEXPECTED_EOF"): + ssl_context.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF + + bio_in = ssl.MemoryBIO() + bio_out = ssl.MemoryBIO() + + # Resolve international host names using IDNA 2008. + # Otherwise wrap_bio() would resolve them with IDNA 2003. + if hostname is not None: + from .._core._sockets import idna2008_resolve + + server_hostname: bytes | None = idna2008_resolve(hostname) + else: + server_hostname = None + + # External SSLContext implementations may do blocking I/O in wrap_bio(), + # but the standard library implementation won't + if type(ssl_context) is ssl.SSLContext: + ssl_object = ssl_context.wrap_bio( + bio_in, + bio_out, + server_side=server_side, + server_hostname=server_hostname, + ) + else: + ssl_object = await to_thread.run_sync( + ssl_context.wrap_bio, + bio_in, + bio_out, + server_side, + server_hostname, + None, + ) + + wrapper = cls( + transport_stream=transport_stream, + standard_compatible=standard_compatible, + _ssl_object=ssl_object, + _read_bio=bio_in, + _write_bio=bio_out, + ) + await wrapper._call_sslobject_method(ssl_object.do_handshake) + return wrapper + + async def _call_sslobject_method( + self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] + ) -> T_Retval: + while True: + try: + result = func(*args) + except ssl.SSLWantReadError: + try: + # Flush any pending writes first + if self._write_bio.pending: + await self.transport_stream.send(self._write_bio.read()) + + data = await self.transport_stream.receive() + except EndOfStream: + self._read_bio.write_eof() + except OSError as exc: + self._read_bio.write_eof() + self._write_bio.write_eof() + raise BrokenResourceError from exc + else: + self._read_bio.write(data) + except ssl.SSLWantWriteError: + await self.transport_stream.send(self._write_bio.read()) + except ssl.SSLSyscallError as exc: + self._read_bio.write_eof() + self._write_bio.write_eof() + raise BrokenResourceError from exc + except ssl.SSLError as exc: + self._read_bio.write_eof() + self._write_bio.write_eof() + if isinstance(exc, ssl.SSLEOFError) or ( + exc.strerror and "UNEXPECTED_EOF_WHILE_READING" in exc.strerror + ): + if self.standard_compatible: + raise BrokenResourceError from exc + else: + raise EndOfStream from None + + raise + else: + # Flush any pending writes first + if self._write_bio.pending: + await self.transport_stream.send(self._write_bio.read()) + + return result + + async def unwrap(self) -> tuple[AnyByteStream, bytes]: + """ + Does the TLS closing handshake. + + :return: a tuple of (wrapped byte stream, bytes left in the read buffer) + + """ + await self._call_sslobject_method(self._ssl_object.unwrap) + self._read_bio.write_eof() + self._write_bio.write_eof() + return self.transport_stream, self._read_bio.read() + + async def aclose(self) -> None: + if self.standard_compatible: + try: + await self.unwrap() + except BaseException: + await aclose_forcefully(self.transport_stream) + raise + + await self.transport_stream.aclose() + + async def receive(self, max_bytes: int = 65536) -> bytes: + if max_bytes < 1: + raise ValueError("max_bytes must be a positive integer") + + data = await self._call_sslobject_method(self._ssl_object.read, max_bytes) + if not data: + raise EndOfStream + + return data + + async def send(self, item: bytes) -> None: + await self._call_sslobject_method(self._ssl_object.write, item) + + async def send_eof(self) -> None: + tls_version = self.extra(TLSAttribute.tls_version) + match = re.match(r"TLSv(\d+)(?:\.(\d+))?", tls_version) + if match: + major, minor = int(match.group(1)), int(match.group(2) or 0) + if (major, minor) < (1, 3): + raise NotImplementedError( + f"send_eof() requires at least TLSv1.3; current " + f"session uses {tls_version}" + ) + + raise NotImplementedError( + "send_eof() has not yet been implemented for TLS streams" + ) + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self.transport_stream.extra_attributes, + TLSAttribute.alpn_protocol: self._ssl_object.selected_alpn_protocol, + TLSAttribute.channel_binding_tls_unique: ( + self._ssl_object.get_channel_binding + ), + TLSAttribute.cipher: self._ssl_object.cipher, + TLSAttribute.peer_certificate: lambda: self._ssl_object.getpeercert(False), + TLSAttribute.peer_certificate_binary: lambda: self._ssl_object.getpeercert( + True + ), + TLSAttribute.server_side: lambda: self._ssl_object.server_side, + TLSAttribute.shared_ciphers: lambda: ( + self._ssl_object.shared_ciphers() + if self._ssl_object.server_side + else None + ), + TLSAttribute.standard_compatible: lambda: self.standard_compatible, + TLSAttribute.ssl_object: lambda: self._ssl_object, + TLSAttribute.tls_version: self._ssl_object.version, + } + + +@dataclass(eq=False) +class TLSListener(Listener[TLSStream]): + """ + A convenience listener that wraps another listener and auto-negotiates a TLS session + on every accepted connection. + + If the TLS handshake times out or raises an exception, + :meth:`handle_handshake_error` is called to do whatever post-mortem processing is + deemed necessary. + + Supports only the :attr:`~TLSAttribute.standard_compatible` extra attribute. + + :param Listener listener: the listener to wrap + :param ssl_context: the SSL context object + :param standard_compatible: a flag passed through to :meth:`TLSStream.wrap` + :param handshake_timeout: time limit for the TLS handshake + (passed to :func:`~anyio.fail_after`) + """ + + listener: Listener[Any] + ssl_context: ssl.SSLContext + standard_compatible: bool = True + handshake_timeout: float = 30 + + @staticmethod + async def handle_handshake_error(exc: BaseException, stream: AnyByteStream) -> None: + """ + Handle an exception raised during the TLS handshake. + + This method does 3 things: + + #. Forcefully closes the original stream + #. Logs the exception (unless it was a cancellation exception) using the + ``anyio.streams.tls`` logger + #. Reraises the exception if it was a base exception or a cancellation exception + + :param exc: the exception + :param stream: the original stream + + """ + await aclose_forcefully(stream) + + # Log all except cancellation exceptions + if not isinstance(exc, get_cancelled_exc_class()): + # CPython (as of 3.11.5) returns incorrect `sys.exc_info()` here when using + # any asyncio implementation, so we explicitly pass the exception to log + # (https://github.com/python/cpython/issues/108668). Trio does not have this + # issue because it works around the CPython bug. + logging.getLogger(__name__).exception( + "Error during TLS handshake", exc_info=exc + ) + + # Only reraise base exceptions and cancellation exceptions + if not isinstance(exc, Exception) or isinstance(exc, get_cancelled_exc_class()): + raise # noqa: PLE0704 + + async def serve( + self, + handler: Callable[[TLSStream], Any], + task_group: TaskGroup | None = None, + ) -> None: + @wraps(handler) + async def handler_wrapper(stream: AnyByteStream) -> None: + from .. import fail_after + + try: + with fail_after(self.handshake_timeout): + wrapped_stream = await TLSStream.wrap( + stream, + ssl_context=self.ssl_context, + standard_compatible=self.standard_compatible, + ) + except BaseException as exc: + await self.handle_handshake_error(exc, stream) + else: + await handler(wrapped_stream) + + await self.listener.serve(handler_wrapper, task_group) + + async def aclose(self) -> None: + await self.listener.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + TLSAttribute.standard_compatible: lambda: self.standard_compatible, + } + + +class TLSConnectable(ByteStreamConnectable): + """ + Wraps another connectable and does TLS negotiation after a successful connection. + + :param connectable: the connectable to wrap + :param hostname: host name of the server (if host name checking is desired) + :param ssl_context: the SSLContext object to use (if not provided, a secure default + will be created) + :param standard_compatible: if ``False``, skip the closing handshake when closing + the connection, and don't raise an exception if the server does the same + """ + + def __init__( + self, + connectable: AnyByteStreamConnectable, + *, + hostname: str | None = None, + ssl_context: ssl.SSLContext | None = None, + standard_compatible: bool = True, + ) -> None: + self.connectable = connectable + self.ssl_context: SSLContext = ssl_context or ssl.create_default_context( + ssl.Purpose.SERVER_AUTH + ) + if not isinstance(self.ssl_context, ssl.SSLContext): + raise TypeError( + "ssl_context must be an instance of ssl.SSLContext, not " + f"{type(self.ssl_context).__name__}" + ) + self.hostname = hostname + self.standard_compatible = standard_compatible + + @override + async def connect(self) -> TLSStream: + stream = await self.connectable.connect() + try: + return await TLSStream.wrap( + stream, + hostname=self.hostname, + ssl_context=self.ssl_context, + standard_compatible=self.standard_compatible, + ) + except BaseException: + await aclose_forcefully(stream) + raise diff --git a/venv/lib/python3.12/site-packages/anyio/to_interpreter.py b/venv/lib/python3.12/site-packages/anyio/to_interpreter.py new file mode 100644 index 0000000..54ef4a8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/to_interpreter.py @@ -0,0 +1,246 @@ +from __future__ import annotations + +__all__ = ( + "current_default_interpreter_limiter", + "run_sync", +) + +import atexit +import os +import sys +from collections import deque +from collections.abc import Callable +from typing import Any, Final, TypeVar + +from . import current_time, to_thread +from ._core._exceptions import BrokenWorkerInterpreter +from ._core._synchronization import CapacityLimiter +from .lowlevel import RunVar + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if sys.version_info >= (3, 14): + from concurrent.interpreters import ExecutionFailed, create + + def _interp_call( + func: Callable[..., Any], args: tuple[Any, ...] + ) -> tuple[Any, bool]: + try: + retval = func(*args) + except BaseException as exc: + return exc, True + else: + return retval, False + + class _Worker: + last_used: float = 0 + + def __init__(self) -> None: + self._interpreter = create() + + def destroy(self) -> None: + self._interpreter.close() + + def call( + self, + func: Callable[..., T_Retval], + args: tuple[Any, ...], + ) -> T_Retval: + try: + res, is_exception = self._interpreter.call(_interp_call, func, args) + except ExecutionFailed as exc: + raise BrokenWorkerInterpreter(exc.excinfo) from exc + + if is_exception: + raise res + + return res +elif sys.version_info >= (3, 13): + import _interpqueues + import _interpreters + + UNBOUND: Final = 2 # I have no clue how this works, but it was used in the stdlib + FMT_UNPICKLED: Final = 0 + FMT_PICKLED: Final = 1 + QUEUE_PICKLE_ARGS: Final = (FMT_PICKLED, UNBOUND) + QUEUE_UNPICKLE_ARGS: Final = (FMT_UNPICKLED, UNBOUND) + + _run_func = compile( + """ +import _interpqueues +from _interpreters import NotShareableError +from pickle import loads, dumps, HIGHEST_PROTOCOL + +QUEUE_PICKLE_ARGS = (1, 2) +QUEUE_UNPICKLE_ARGS = (0, 2) + +item = _interpqueues.get(queue_id)[0] +try: + func, args = loads(item) + retval = func(*args) +except BaseException as exc: + is_exception = True + retval = exc +else: + is_exception = False + +try: + _interpqueues.put(queue_id, (retval, is_exception), *QUEUE_UNPICKLE_ARGS) +except NotShareableError: + retval = dumps(retval, HIGHEST_PROTOCOL) + _interpqueues.put(queue_id, (retval, is_exception), *QUEUE_PICKLE_ARGS) + """, + "", + "exec", + ) + + class _Worker: + last_used: float = 0 + + def __init__(self) -> None: + self._interpreter_id = _interpreters.create() + self._queue_id = _interpqueues.create(1, *QUEUE_UNPICKLE_ARGS) + _interpreters.set___main___attrs( + self._interpreter_id, {"queue_id": self._queue_id} + ) + + def destroy(self) -> None: + _interpqueues.destroy(self._queue_id) + _interpreters.destroy(self._interpreter_id) + + def call( + self, + func: Callable[..., T_Retval], + args: tuple[Any, ...], + ) -> T_Retval: + import pickle + + item = pickle.dumps((func, args), pickle.HIGHEST_PROTOCOL) + _interpqueues.put(self._queue_id, item, *QUEUE_PICKLE_ARGS) + exc_info = _interpreters.exec(self._interpreter_id, _run_func) + if exc_info: + raise BrokenWorkerInterpreter(exc_info) + + res = _interpqueues.get(self._queue_id) + (res, is_exception), fmt = res[:2] + if fmt == FMT_PICKLED: + res = pickle.loads(res) + + if is_exception: + raise res + + return res +else: + + class _Worker: + last_used: float = 0 + + def __init__(self) -> None: + raise RuntimeError("subinterpreters require at least Python 3.13") + + def call( + self, + func: Callable[..., T_Retval], + args: tuple[Any, ...], + ) -> T_Retval: + raise NotImplementedError + + def destroy(self) -> None: + pass + + +DEFAULT_CPU_COUNT: Final = 8 # this is just an arbitrarily selected value +MAX_WORKER_IDLE_TIME = ( + 30 # seconds a subinterpreter can be idle before becoming eligible for pruning +) + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + +_idle_workers = RunVar[deque[_Worker]]("_available_workers") +_default_interpreter_limiter = RunVar[CapacityLimiter]("_default_interpreter_limiter") + + +def _stop_workers(workers: deque[_Worker]) -> None: + for worker in workers: + worker.destroy() + + workers.clear() + + +async def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + limiter: CapacityLimiter | None = None, +) -> T_Retval: + """ + Call the given function with the given arguments in a subinterpreter. + + .. warning:: On Python 3.13, the :mod:`concurrent.interpreters` module was not yet + available, so the code path for that Python version relies on an undocumented, + private API. As such, it is recommended to not rely on this function for anything + mission-critical on Python 3.13. + + :param func: a callable + :param args: the positional arguments for the callable + :param limiter: capacity limiter to use to limit the total number of subinterpreters + running (if omitted, the default limiter is used) + :return: the result of the call + :raises BrokenWorkerInterpreter: if there's an internal error in a subinterpreter + + """ + if limiter is None: + limiter = current_default_interpreter_limiter() + + try: + idle_workers = _idle_workers.get() + except LookupError: + idle_workers = deque() + _idle_workers.set(idle_workers) + atexit.register(_stop_workers, idle_workers) + + async with limiter: + try: + worker = idle_workers.pop() + except IndexError: + worker = _Worker() + + try: + return await to_thread.run_sync( + worker.call, + func, + args, + limiter=limiter, + ) + finally: + # Prune workers that have been idle for too long + now = current_time() + while idle_workers: + if now - idle_workers[0].last_used <= MAX_WORKER_IDLE_TIME: + break + + await to_thread.run_sync(idle_workers.popleft().destroy, limiter=limiter) + + worker.last_used = current_time() + idle_workers.append(worker) + + +def current_default_interpreter_limiter() -> CapacityLimiter: + """ + Return the capacity limiter used by default to limit the number of concurrently + running subinterpreters. + + Defaults to the number of CPU cores. + + :return: a capacity limiter object + + """ + try: + return _default_interpreter_limiter.get() + except LookupError: + limiter = CapacityLimiter(os.cpu_count() or DEFAULT_CPU_COUNT) + _default_interpreter_limiter.set(limiter) + return limiter diff --git a/venv/lib/python3.12/site-packages/anyio/to_process.py b/venv/lib/python3.12/site-packages/anyio/to_process.py new file mode 100644 index 0000000..584128e --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/to_process.py @@ -0,0 +1,269 @@ +from __future__ import annotations + +__all__ = ( + "current_default_process_limiter", + "process_worker", + "run_sync", +) + +import os +import pickle +import runpy +import subprocess +import sys +from collections import deque +from collections.abc import Callable +from types import ModuleType +from typing import TypeVar, cast + +from ._core._eventloop import current_time, get_async_backend, get_cancelled_exc_class +from ._core._exceptions import BrokenWorkerProcess +from ._core._subprocesses import open_process +from ._core._synchronization import CapacityLimiter +from ._core._tasks import CancelScope, fail_after +from .abc import ByteReceiveStream, ByteSendStream, Process +from .lowlevel import RunVar, checkpoint_if_cancelled +from .streams.buffered import BufferedByteReceiveStream + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +WORKER_MAX_IDLE_TIME = 300 # 5 minutes + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + +_process_pool_workers: RunVar[set[Process]] = RunVar("_process_pool_workers") +_process_pool_idle_workers: RunVar[deque[tuple[Process, float]]] = RunVar( + "_process_pool_idle_workers" +) +_default_process_limiter: RunVar[CapacityLimiter] = RunVar("_default_process_limiter") + + +async def run_sync( # type: ignore[return] + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + cancellable: bool = False, + limiter: CapacityLimiter | None = None, +) -> T_Retval: + """ + Call the given function with the given arguments in a worker process. + + If the ``cancellable`` option is enabled and the task waiting for its completion is + cancelled, the worker process running it will be abruptly terminated using SIGKILL + (or ``terminateProcess()`` on Windows). + + :param func: a callable + :param args: positional arguments for the callable + :param cancellable: ``True`` to allow cancellation of the operation while it's + running + :param limiter: capacity limiter to use to limit the total amount of processes + running (if omitted, the default limiter is used) + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + :return: an awaitable that yields the return value of the function. + + """ + + async def send_raw_command(pickled_cmd: bytes) -> object: + try: + await stdin.send(pickled_cmd) + response = await buffered.receive_until(b"\n", 50) + status, length = response.split(b" ") + if status not in (b"RETURN", b"EXCEPTION"): + raise RuntimeError( + f"Worker process returned unexpected response: {response!r}" + ) + + pickled_response = await buffered.receive_exactly(int(length)) + except BaseException as exc: + workers.discard(process) + try: + process.kill() + with CancelScope(shield=True): + await process.aclose() + except ProcessLookupError: + pass + + if isinstance(exc, get_cancelled_exc_class()): + raise + else: + raise BrokenWorkerProcess from exc + + retval = pickle.loads(pickled_response) + if status == b"EXCEPTION": + assert isinstance(retval, BaseException) + raise retval + else: + return retval + + # First pickle the request before trying to reserve a worker process + await checkpoint_if_cancelled() + request = pickle.dumps(("run", func, args), protocol=pickle.HIGHEST_PROTOCOL) + + # If this is the first run in this event loop thread, set up the necessary variables + try: + workers = _process_pool_workers.get() + idle_workers = _process_pool_idle_workers.get() + except LookupError: + workers = set() + idle_workers = deque() + _process_pool_workers.set(workers) + _process_pool_idle_workers.set(idle_workers) + get_async_backend().setup_process_pool_exit_at_shutdown(workers) + + async with limiter or current_default_process_limiter(): + # Pop processes from the pool (starting from the most recently used) until we + # find one that hasn't exited yet + process: Process + while idle_workers: + process, _idle_since = idle_workers.pop() + if process.returncode is None: + stdin = cast(ByteSendStream, process.stdin) + buffered = BufferedByteReceiveStream( + cast(ByteReceiveStream, process.stdout) + ) + + # Prune any other workers that have been idle for WORKER_MAX_IDLE_TIME + # seconds or longer + now = current_time() + killed_processes: list[Process] = [] + while idle_workers: + if now - idle_workers[0][1] < WORKER_MAX_IDLE_TIME: + break + + process_to_kill, _idle_since = idle_workers.popleft() + process_to_kill.kill() + workers.remove(process_to_kill) + killed_processes.append(process_to_kill) + + with CancelScope(shield=True): + for killed_process in killed_processes: + await killed_process.aclose() + + break + + workers.remove(process) + else: + command = [sys.executable, "-u", "-m", __name__] + process = await open_process( + command, stdin=subprocess.PIPE, stdout=subprocess.PIPE + ) + try: + stdin = cast(ByteSendStream, process.stdin) + buffered = BufferedByteReceiveStream( + cast(ByteReceiveStream, process.stdout) + ) + with fail_after(20): + message = await buffered.receive(6) + + if message != b"READY\n": + raise BrokenWorkerProcess( + f"Worker process returned unexpected response: {message!r}" + ) + + main_module_path = getattr(sys.modules["__main__"], "__file__", None) + pickled = pickle.dumps( + ("init", sys.path, main_module_path), + protocol=pickle.HIGHEST_PROTOCOL, + ) + await send_raw_command(pickled) + except (BrokenWorkerProcess, get_cancelled_exc_class()): + raise + except BaseException as exc: + process.kill() + raise BrokenWorkerProcess( + "Error during worker process initialization" + ) from exc + + workers.add(process) + + with CancelScope(shield=not cancellable): + try: + return cast(T_Retval, await send_raw_command(request)) + finally: + if process in workers: + idle_workers.append((process, current_time())) + + +def current_default_process_limiter() -> CapacityLimiter: + """ + Return the capacity limiter that is used by default to limit the number of worker + processes. + + :return: a capacity limiter object + + """ + try: + return _default_process_limiter.get() + except LookupError: + limiter = CapacityLimiter(os.cpu_count() or 2) + _default_process_limiter.set(limiter) + return limiter + + +def process_worker() -> None: + # Redirect standard streams to os.devnull so that user code won't interfere with the + # parent-worker communication + stdin = sys.stdin + stdout = sys.stdout + sys.stdin = open(os.devnull) + sys.stdout = open(os.devnull, "w") + sys.stderr = open(os.devnull, "w") + + stdout.buffer.write(b"READY\n") + while True: + retval = exception = None + try: + command, *args = pickle.load(stdin.buffer) + except EOFError: + return + except BaseException as exc: + exception = exc + else: + if command == "run": + func, args = args + try: + retval = func(*args) + except BaseException as exc: + exception = exc + elif command == "init": + main_module_path: str | None + sys.path, main_module_path = args + del sys.modules["__main__"] + if main_module_path and os.path.isfile(main_module_path): + # Load the parent's main module but as __mp_main__ instead of + # __main__ (like multiprocessing does) to avoid infinite recursion + try: + main = ModuleType("__mp_main__") + main_content = runpy.run_path( + main_module_path, run_name="__mp_main__" + ) + main.__dict__.update(main_content) + sys.modules["__main__"] = sys.modules["__mp_main__"] = main + except BaseException as exc: + exception = exc + try: + if exception is not None: + status = b"EXCEPTION" + pickled = pickle.dumps(exception, pickle.HIGHEST_PROTOCOL) + else: + status = b"RETURN" + pickled = pickle.dumps(retval, pickle.HIGHEST_PROTOCOL) + except BaseException as exc: + exception = exc + status = b"EXCEPTION" + pickled = pickle.dumps(exc, pickle.HIGHEST_PROTOCOL) + + stdout.buffer.write(b"%s %d\n" % (status, len(pickled))) + stdout.buffer.write(pickled) + + # Respect SIGTERM + if isinstance(exception, SystemExit): + raise exception + + +if __name__ == "__main__": + process_worker() diff --git a/venv/lib/python3.12/site-packages/anyio/to_thread.py b/venv/lib/python3.12/site-packages/anyio/to_thread.py new file mode 100644 index 0000000..5cdb725 --- /dev/null +++ b/venv/lib/python3.12/site-packages/anyio/to_thread.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +__all__ = ( + "current_default_thread_limiter", + "run_sync", +) + +import sys +from collections.abc import Callable +from typing import TYPE_CHECKING, TypeVar +from warnings import warn + +from ._core._eventloop import get_async_backend + +if TYPE_CHECKING: + from ._core._synchronization import CapacityLimiter + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + + +async def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + abandon_on_cancel: bool = False, + cancellable: bool | None = None, + limiter: CapacityLimiter | None = None, +) -> T_Retval: + """ + Call the given function with the given arguments in a worker thread. + + If the ``abandon_on_cancel`` option is enabled and the task waiting for its + completion is cancelled, the thread will still run its course but its + return value (or any raised exception) will be ignored. + + :param func: a callable + :param args: positional arguments for the callable + :param abandon_on_cancel: ``True`` to abandon the thread (leaving it to run + unchecked on own) if the host task is cancelled, ``False`` to ignore + cancellations in the host task until the operation has completed in the worker + thread + :param cancellable: deprecated alias of ``abandon_on_cancel``; will override + ``abandon_on_cancel`` if both parameters are passed + :param limiter: capacity limiter to use to limit the total amount of threads running + (if omitted, the default limiter is used) + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + :return: an awaitable that yields the return value of the function. + + """ + if cancellable is not None: + abandon_on_cancel = cancellable + warn( + "The `cancellable=` keyword argument to `anyio.to_thread.run_sync` is " + "deprecated since AnyIO 4.1.0; use `abandon_on_cancel=` instead", + DeprecationWarning, + stacklevel=2, + ) + + return await get_async_backend().run_sync_in_worker_thread( + func, args, abandon_on_cancel=abandon_on_cancel, limiter=limiter + ) + + +def current_default_thread_limiter() -> CapacityLimiter: + """ + Return the capacity limiter that is used by default to limit the number of + concurrent threads. + + :return: a capacity limiter object + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().current_default_thread_limiter() diff --git a/venv/lib/python3.12/site-packages/attr/__init__.py b/venv/lib/python3.12/site-packages/attr/__init__.py new file mode 100644 index 0000000..5c6e065 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/__init__.py @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: MIT + +""" +Classes Without Boilerplate +""" + +from functools import partial +from typing import Callable, Literal, Protocol + +from . import converters, exceptions, filters, setters, validators +from ._cmp import cmp_using +from ._config import get_run_validators, set_run_validators +from ._funcs import asdict, assoc, astuple, has, resolve_types +from ._make import ( + NOTHING, + Attribute, + Converter, + Factory, + _Nothing, + attrib, + attrs, + evolve, + fields, + fields_dict, + make_class, + validate, +) +from ._next_gen import define, field, frozen, mutable +from ._version_info import VersionInfo + + +s = attributes = attrs +ib = attr = attrib +dataclass = partial(attrs, auto_attribs=True) # happy Easter ;) + + +class AttrsInstance(Protocol): + pass + + +NothingType = Literal[_Nothing.NOTHING] + +__all__ = [ + "NOTHING", + "Attribute", + "AttrsInstance", + "Converter", + "Factory", + "NothingType", + "asdict", + "assoc", + "astuple", + "attr", + "attrib", + "attributes", + "attrs", + "cmp_using", + "converters", + "define", + "evolve", + "exceptions", + "field", + "fields", + "fields_dict", + "filters", + "frozen", + "get_run_validators", + "has", + "ib", + "make_class", + "mutable", + "resolve_types", + "s", + "set_run_validators", + "setters", + "validate", + "validators", +] + + +def _make_getattr(mod_name: str) -> Callable: + """ + Create a metadata proxy for packaging information that uses *mod_name* in + its warnings and errors. + """ + + def __getattr__(name: str) -> str: + if name not in ("__version__", "__version_info__"): + msg = f"module {mod_name} has no attribute {name}" + raise AttributeError(msg) + + from importlib.metadata import metadata + + meta = metadata("attrs") + + if name == "__version_info__": + return VersionInfo._from_version_string(meta["version"]) + + return meta["version"] + + return __getattr__ + + +__getattr__ = _make_getattr(__name__) diff --git a/venv/lib/python3.12/site-packages/attr/__init__.pyi b/venv/lib/python3.12/site-packages/attr/__init__.pyi new file mode 100644 index 0000000..758decf --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/__init__.pyi @@ -0,0 +1,389 @@ +import enum +import sys + +from typing import ( + Any, + Callable, + Generic, + Literal, + Mapping, + Protocol, + Sequence, + TypeVar, + overload, +) + +# `import X as X` is required to make these public +from . import converters as converters +from . import exceptions as exceptions +from . import filters as filters +from . import setters as setters +from . import validators as validators +from ._cmp import cmp_using as cmp_using +from ._typing_compat import AttrsInstance_ +from ._version_info import VersionInfo +from attrs import ( + define as define, + field as field, + mutable as mutable, + frozen as frozen, + _EqOrderType, + _ValidatorType, + _ConverterType, + _ReprArgType, + _OnSetAttrType, + _OnSetAttrArgType, + _FieldTransformer, + _ValidatorArgType, +) + +if sys.version_info >= (3, 10): + from typing import TypeGuard, TypeAlias +else: + from typing_extensions import TypeGuard, TypeAlias + +if sys.version_info >= (3, 11): + from typing import dataclass_transform +else: + from typing_extensions import dataclass_transform + +__version__: str +__version_info__: VersionInfo +__title__: str +__description__: str +__url__: str +__uri__: str +__author__: str +__email__: str +__license__: str +__copyright__: str + +_T = TypeVar("_T") +_C = TypeVar("_C", bound=type) + +_FilterType = Callable[["Attribute[_T]", _T], bool] + +# We subclass this here to keep the protocol's qualified name clean. +class AttrsInstance(AttrsInstance_, Protocol): + pass + +_A = TypeVar("_A", bound=type[AttrsInstance]) + +class _Nothing(enum.Enum): + NOTHING = enum.auto() + +NOTHING = _Nothing.NOTHING +NothingType: TypeAlias = Literal[_Nothing.NOTHING] + +# NOTE: Factory lies about its return type to make this possible: +# `x: List[int] # = Factory(list)` +# Work around mypy issue #4554 in the common case by using an overload. + +@overload +def Factory(factory: Callable[[], _T]) -> _T: ... +@overload +def Factory( + factory: Callable[[Any], _T], + takes_self: Literal[True], +) -> _T: ... +@overload +def Factory( + factory: Callable[[], _T], + takes_self: Literal[False], +) -> _T: ... + +In = TypeVar("In") +Out = TypeVar("Out") + +class Converter(Generic[In, Out]): + @overload + def __init__(self, converter: Callable[[In], Out]) -> None: ... + @overload + def __init__( + self, + converter: Callable[[In, AttrsInstance, Attribute], Out], + *, + takes_self: Literal[True], + takes_field: Literal[True], + ) -> None: ... + @overload + def __init__( + self, + converter: Callable[[In, Attribute], Out], + *, + takes_field: Literal[True], + ) -> None: ... + @overload + def __init__( + self, + converter: Callable[[In, AttrsInstance], Out], + *, + takes_self: Literal[True], + ) -> None: ... + +class Attribute(Generic[_T]): + name: str + default: _T | None + validator: _ValidatorType[_T] | None + repr: _ReprArgType + cmp: _EqOrderType + eq: _EqOrderType + order: _EqOrderType + hash: bool | None + init: bool + converter: Converter | None + metadata: dict[Any, Any] + type: type[_T] | None + kw_only: bool + on_setattr: _OnSetAttrType + alias: str | None + + def evolve(self, **changes: Any) -> "Attribute[Any]": ... + +# NOTE: We had several choices for the annotation to use for type arg: +# 1) Type[_T] +# - Pros: Handles simple cases correctly +# - Cons: Might produce less informative errors in the case of conflicting +# TypeVars e.g. `attr.ib(default='bad', type=int)` +# 2) Callable[..., _T] +# - Pros: Better error messages than #1 for conflicting TypeVars +# - Cons: Terrible error messages for validator checks. +# e.g. attr.ib(type=int, validator=validate_str) +# -> error: Cannot infer function type argument +# 3) type (and do all of the work in the mypy plugin) +# - Pros: Simple here, and we could customize the plugin with our own errors. +# - Cons: Would need to write mypy plugin code to handle all the cases. +# We chose option #1. + +# `attr` lies about its return type to make the following possible: +# attr() -> Any +# attr(8) -> int +# attr(validator=) -> Whatever the callable expects. +# This makes this type of assignments possible: +# x: int = attr(8) +# +# This form catches explicit None or no default but with no other arguments +# returns Any. +@overload +def attrib( + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + type: None = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def attrib( + default: None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + type: type[_T] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def attrib( + default: _T, + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + type: type[_T] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def attrib( + default: _T | None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + type: object = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., +) -> Any: ... +@overload +@dataclass_transform(order_default=True, field_specifiers=(attrib, field)) +def attrs( + maybe_cls: _C, + these: dict[str, Any] | None = ..., + repr_ns: str | None = ..., + repr: bool = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., + unsafe_hash: bool | None = ..., +) -> _C: ... +@overload +@dataclass_transform(order_default=True, field_specifiers=(attrib, field)) +def attrs( + maybe_cls: None = ..., + these: dict[str, Any] | None = ..., + repr_ns: str | None = ..., + repr: bool = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., + unsafe_hash: bool | None = ..., +) -> Callable[[_C], _C]: ... +def fields(cls: type[AttrsInstance] | AttrsInstance) -> Any: ... +def fields_dict(cls: type[AttrsInstance]) -> dict[str, Attribute[Any]]: ... +def validate(inst: AttrsInstance) -> None: ... +def resolve_types( + cls: _A, + globalns: dict[str, Any] | None = ..., + localns: dict[str, Any] | None = ..., + attribs: list[Attribute[Any]] | None = ..., + include_extras: bool = ..., +) -> _A: ... + +# TODO: add support for returning a proper attrs class from the mypy plugin +# we use Any instead of _CountingAttr so that e.g. `make_class('Foo', +# [attr.ib()])` is valid +def make_class( + name: str, + attrs: list[str] | tuple[str, ...] | dict[str, Any], + bases: tuple[type, ...] = ..., + class_body: dict[str, Any] | None = ..., + repr_ns: str | None = ..., + repr: bool = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + collect_by_mro: bool = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., +) -> type: ... + +# _funcs -- + +# TODO: add support for returning TypedDict from the mypy plugin +# FIXME: asdict/astuple do not honor their factory args. Waiting on one of +# these: +# https://github.com/python/mypy/issues/4236 +# https://github.com/python/typing/issues/253 +# XXX: remember to fix attrs.asdict/astuple too! +def asdict( + inst: AttrsInstance, + recurse: bool = ..., + filter: _FilterType[Any] | None = ..., + dict_factory: type[Mapping[Any, Any]] = ..., + retain_collection_types: bool = ..., + value_serializer: Callable[[type, Attribute[Any], Any], Any] | None = ..., + tuple_keys: bool | None = ..., +) -> dict[str, Any]: ... + +# TODO: add support for returning NamedTuple from the mypy plugin +def astuple( + inst: AttrsInstance, + recurse: bool = ..., + filter: _FilterType[Any] | None = ..., + tuple_factory: type[Sequence[Any]] = ..., + retain_collection_types: bool = ..., +) -> tuple[Any, ...]: ... +def has(cls: type) -> TypeGuard[type[AttrsInstance]]: ... +def assoc(inst: _T, **changes: Any) -> _T: ... +def evolve(inst: _T, **changes: Any) -> _T: ... + +# _config -- + +def set_run_validators(run: bool) -> None: ... +def get_run_validators() -> bool: ... + +# aliases -- + +s = attributes = attrs +ib = attr = attrib +dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;) diff --git a/venv/lib/python3.12/site-packages/attr/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/attr/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5a93406 Binary files /dev/null and b/venv/lib/python3.12/site-packages/attr/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attr/__pycache__/_cmp.cpython-312.pyc b/venv/lib/python3.12/site-packages/attr/__pycache__/_cmp.cpython-312.pyc new file mode 100644 index 0000000..f82142d Binary files /dev/null and b/venv/lib/python3.12/site-packages/attr/__pycache__/_cmp.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attr/__pycache__/_compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/attr/__pycache__/_compat.cpython-312.pyc new file mode 100644 index 0000000..c7e80ac Binary files /dev/null and b/venv/lib/python3.12/site-packages/attr/__pycache__/_compat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attr/__pycache__/_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/attr/__pycache__/_config.cpython-312.pyc new file mode 100644 index 0000000..7dfb4f7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/attr/__pycache__/_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attr/__pycache__/_funcs.cpython-312.pyc b/venv/lib/python3.12/site-packages/attr/__pycache__/_funcs.cpython-312.pyc new file mode 100644 index 0000000..b783191 Binary files /dev/null and b/venv/lib/python3.12/site-packages/attr/__pycache__/_funcs.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attr/__pycache__/_make.cpython-312.pyc b/venv/lib/python3.12/site-packages/attr/__pycache__/_make.cpython-312.pyc new file mode 100644 index 0000000..57022fd Binary files /dev/null and b/venv/lib/python3.12/site-packages/attr/__pycache__/_make.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attr/__pycache__/_next_gen.cpython-312.pyc b/venv/lib/python3.12/site-packages/attr/__pycache__/_next_gen.cpython-312.pyc new file mode 100644 index 0000000..90b48a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/attr/__pycache__/_next_gen.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attr/__pycache__/_version_info.cpython-312.pyc b/venv/lib/python3.12/site-packages/attr/__pycache__/_version_info.cpython-312.pyc new file mode 100644 index 0000000..8429bb1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/attr/__pycache__/_version_info.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attr/__pycache__/converters.cpython-312.pyc b/venv/lib/python3.12/site-packages/attr/__pycache__/converters.cpython-312.pyc new file mode 100644 index 0000000..917d913 Binary files /dev/null and b/venv/lib/python3.12/site-packages/attr/__pycache__/converters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attr/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/attr/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..6e2c1ee Binary files /dev/null and b/venv/lib/python3.12/site-packages/attr/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attr/__pycache__/filters.cpython-312.pyc b/venv/lib/python3.12/site-packages/attr/__pycache__/filters.cpython-312.pyc new file mode 100644 index 0000000..48f8fdc Binary files /dev/null and b/venv/lib/python3.12/site-packages/attr/__pycache__/filters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attr/__pycache__/setters.cpython-312.pyc b/venv/lib/python3.12/site-packages/attr/__pycache__/setters.cpython-312.pyc new file mode 100644 index 0000000..018ee71 Binary files /dev/null and b/venv/lib/python3.12/site-packages/attr/__pycache__/setters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attr/__pycache__/validators.cpython-312.pyc b/venv/lib/python3.12/site-packages/attr/__pycache__/validators.cpython-312.pyc new file mode 100644 index 0000000..73210ff Binary files /dev/null and b/venv/lib/python3.12/site-packages/attr/__pycache__/validators.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attr/_cmp.py b/venv/lib/python3.12/site-packages/attr/_cmp.py new file mode 100644 index 0000000..09bab49 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_cmp.py @@ -0,0 +1,160 @@ +# SPDX-License-Identifier: MIT + + +import functools +import types + +from ._make import __ne__ + + +_operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="} + + +def cmp_using( + eq=None, + lt=None, + le=None, + gt=None, + ge=None, + require_same_type=True, + class_name="Comparable", +): + """ + Create a class that can be passed into `attrs.field`'s ``eq``, ``order``, + and ``cmp`` arguments to customize field comparison. + + The resulting class will have a full set of ordering methods if at least + one of ``{lt, le, gt, ge}`` and ``eq`` are provided. + + Args: + eq (typing.Callable | None): + Callable used to evaluate equality of two objects. + + lt (typing.Callable | None): + Callable used to evaluate whether one object is less than another + object. + + le (typing.Callable | None): + Callable used to evaluate whether one object is less than or equal + to another object. + + gt (typing.Callable | None): + Callable used to evaluate whether one object is greater than + another object. + + ge (typing.Callable | None): + Callable used to evaluate whether one object is greater than or + equal to another object. + + require_same_type (bool): + When `True`, equality and ordering methods will return + `NotImplemented` if objects are not of the same type. + + class_name (str | None): Name of class. Defaults to "Comparable". + + See `comparison` for more details. + + .. versionadded:: 21.1.0 + """ + + body = { + "__slots__": ["value"], + "__init__": _make_init(), + "_requirements": [], + "_is_comparable_to": _is_comparable_to, + } + + # Add operations. + num_order_functions = 0 + has_eq_function = False + + if eq is not None: + has_eq_function = True + body["__eq__"] = _make_operator("eq", eq) + body["__ne__"] = __ne__ + + if lt is not None: + num_order_functions += 1 + body["__lt__"] = _make_operator("lt", lt) + + if le is not None: + num_order_functions += 1 + body["__le__"] = _make_operator("le", le) + + if gt is not None: + num_order_functions += 1 + body["__gt__"] = _make_operator("gt", gt) + + if ge is not None: + num_order_functions += 1 + body["__ge__"] = _make_operator("ge", ge) + + type_ = types.new_class( + class_name, (object,), {}, lambda ns: ns.update(body) + ) + + # Add same type requirement. + if require_same_type: + type_._requirements.append(_check_same_type) + + # Add total ordering if at least one operation was defined. + if 0 < num_order_functions < 4: + if not has_eq_function: + # functools.total_ordering requires __eq__ to be defined, + # so raise early error here to keep a nice stack. + msg = "eq must be define is order to complete ordering from lt, le, gt, ge." + raise ValueError(msg) + type_ = functools.total_ordering(type_) + + return type_ + + +def _make_init(): + """ + Create __init__ method. + """ + + def __init__(self, value): + """ + Initialize object with *value*. + """ + self.value = value + + return __init__ + + +def _make_operator(name, func): + """ + Create operator method. + """ + + def method(self, other): + if not self._is_comparable_to(other): + return NotImplemented + + result = func(self.value, other.value) + if result is NotImplemented: + return NotImplemented + + return result + + method.__name__ = f"__{name}__" + method.__doc__ = ( + f"Return a {_operation_names[name]} b. Computed by attrs." + ) + + return method + + +def _is_comparable_to(self, other): + """ + Check whether `other` is comparable to `self`. + """ + return all(func(self, other) for func in self._requirements) + + +def _check_same_type(self, other): + """ + Return True if *self* and *other* are of the same type, False otherwise. + """ + return other.value.__class__ is self.value.__class__ diff --git a/venv/lib/python3.12/site-packages/attr/_cmp.pyi b/venv/lib/python3.12/site-packages/attr/_cmp.pyi new file mode 100644 index 0000000..cc7893b --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_cmp.pyi @@ -0,0 +1,13 @@ +from typing import Any, Callable + +_CompareWithType = Callable[[Any, Any], bool] + +def cmp_using( + eq: _CompareWithType | None = ..., + lt: _CompareWithType | None = ..., + le: _CompareWithType | None = ..., + gt: _CompareWithType | None = ..., + ge: _CompareWithType | None = ..., + require_same_type: bool = ..., + class_name: str = ..., +) -> type: ... diff --git a/venv/lib/python3.12/site-packages/attr/_compat.py b/venv/lib/python3.12/site-packages/attr/_compat.py new file mode 100644 index 0000000..bc68ed9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_compat.py @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: MIT + +import inspect +import platform +import sys +import threading + +from collections.abc import Mapping, Sequence # noqa: F401 +from typing import _GenericAlias + + +PYPY = platform.python_implementation() == "PyPy" +PY_3_10_PLUS = sys.version_info[:2] >= (3, 10) +PY_3_11_PLUS = sys.version_info[:2] >= (3, 11) +PY_3_12_PLUS = sys.version_info[:2] >= (3, 12) +PY_3_13_PLUS = sys.version_info[:2] >= (3, 13) +PY_3_14_PLUS = sys.version_info[:2] >= (3, 14) + + +if PY_3_14_PLUS: + import annotationlib + + # We request forward-ref annotations to not break in the presence of + # forward references. + + def _get_annotations(cls): + return annotationlib.get_annotations( + cls, format=annotationlib.Format.FORWARDREF + ) + +else: + + def _get_annotations(cls): + """ + Get annotations for *cls*. + """ + return cls.__dict__.get("__annotations__", {}) + + +class _AnnotationExtractor: + """ + Extract type annotations from a callable, returning None whenever there + is none. + """ + + __slots__ = ["sig"] + + def __init__(self, callable): + try: + self.sig = inspect.signature(callable) + except (ValueError, TypeError): # inspect failed + self.sig = None + + def get_first_param_type(self): + """ + Return the type annotation of the first argument if it's not empty. + """ + if not self.sig: + return None + + params = list(self.sig.parameters.values()) + if params and params[0].annotation is not inspect.Parameter.empty: + return params[0].annotation + + return None + + def get_return_type(self): + """ + Return the return type if it's not empty. + """ + if ( + self.sig + and self.sig.return_annotation is not inspect.Signature.empty + ): + return self.sig.return_annotation + + return None + + +# Thread-local global to track attrs instances which are already being repr'd. +# This is needed because there is no other (thread-safe) way to pass info +# about the instances that are already being repr'd through the call stack +# in order to ensure we don't perform infinite recursion. +# +# For instance, if an instance contains a dict which contains that instance, +# we need to know that we're already repr'ing the outside instance from within +# the dict's repr() call. +# +# This lives here rather than in _make.py so that the functions in _make.py +# don't have a direct reference to the thread-local in their globals dict. +# If they have such a reference, it breaks cloudpickle. +repr_context = threading.local() + + +def get_generic_base(cl): + """If this is a generic class (A[str]), return the generic base for it.""" + if cl.__class__ is _GenericAlias: + return cl.__origin__ + return None diff --git a/venv/lib/python3.12/site-packages/attr/_config.py b/venv/lib/python3.12/site-packages/attr/_config.py new file mode 100644 index 0000000..4b25772 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_config.py @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: MIT + +__all__ = ["get_run_validators", "set_run_validators"] + +_run_validators = True + + +def set_run_validators(run): + """ + Set whether or not validators are run. By default, they are run. + + .. deprecated:: 21.3.0 It will not be removed, but it also will not be + moved to new ``attrs`` namespace. Use `attrs.validators.set_disabled()` + instead. + """ + if not isinstance(run, bool): + msg = "'run' must be bool." + raise TypeError(msg) + global _run_validators + _run_validators = run + + +def get_run_validators(): + """ + Return whether or not validators are run. + + .. deprecated:: 21.3.0 It will not be removed, but it also will not be + moved to new ``attrs`` namespace. Use `attrs.validators.get_disabled()` + instead. + """ + return _run_validators diff --git a/venv/lib/python3.12/site-packages/attr/_funcs.py b/venv/lib/python3.12/site-packages/attr/_funcs.py new file mode 100644 index 0000000..1adb500 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_funcs.py @@ -0,0 +1,497 @@ +# SPDX-License-Identifier: MIT + + +import copy + +from ._compat import get_generic_base +from ._make import _OBJ_SETATTR, NOTHING, fields +from .exceptions import AttrsAttributeNotFoundError + + +_ATOMIC_TYPES = frozenset( + { + type(None), + bool, + int, + float, + str, + complex, + bytes, + type(...), + type, + range, + property, + } +) + + +def asdict( + inst, + recurse=True, + filter=None, + dict_factory=dict, + retain_collection_types=False, + value_serializer=None, +): + """ + Return the *attrs* attribute values of *inst* as a dict. + + Optionally recurse into other *attrs*-decorated classes. + + Args: + inst: Instance of an *attrs*-decorated class. + + recurse (bool): Recurse into classes that are also *attrs*-decorated. + + filter (~typing.Callable): + A callable whose return code determines whether an attribute or + element is included (`True`) or dropped (`False`). Is called with + the `attrs.Attribute` as the first argument and the value as the + second argument. + + dict_factory (~typing.Callable): + A callable to produce dictionaries from. For example, to produce + ordered dictionaries instead of normal Python dictionaries, pass in + ``collections.OrderedDict``. + + retain_collection_types (bool): + Do not convert to `list` when encountering an attribute whose type + is `tuple` or `set`. Only meaningful if *recurse* is `True`. + + value_serializer (typing.Callable | None): + A hook that is called for every attribute or dict key/value. It + receives the current instance, field and value and must return the + (updated) value. The hook is run *after* the optional *filter* has + been applied. + + Returns: + Return type of *dict_factory*. + + Raises: + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + .. versionadded:: 16.0.0 *dict_factory* + .. versionadded:: 16.1.0 *retain_collection_types* + .. versionadded:: 20.3.0 *value_serializer* + .. versionadded:: 21.3.0 + If a dict has a collection for a key, it is serialized as a tuple. + """ + attrs = fields(inst.__class__) + rv = dict_factory() + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + + if value_serializer is not None: + v = value_serializer(inst, a, v) + + if recurse is True: + value_type = type(v) + if value_type in _ATOMIC_TYPES: + rv[a.name] = v + elif has(value_type): + rv[a.name] = asdict( + v, + recurse=True, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + elif issubclass(value_type, (tuple, list, set, frozenset)): + cf = value_type if retain_collection_types is True else list + items = [ + _asdict_anything( + i, + is_key=False, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + for i in v + ] + try: + rv[a.name] = cf(items) + except TypeError: + if not issubclass(cf, tuple): + raise + # Workaround for TypeError: cf.__new__() missing 1 required + # positional argument (which appears, for a namedturle) + rv[a.name] = cf(*items) + elif issubclass(value_type, dict): + df = dict_factory + rv[a.name] = df( + ( + _asdict_anything( + kk, + is_key=True, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + _asdict_anything( + vv, + is_key=False, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + ) + for kk, vv in v.items() + ) + else: + rv[a.name] = v + else: + rv[a.name] = v + return rv + + +def _asdict_anything( + val, + is_key, + filter, + dict_factory, + retain_collection_types, + value_serializer, +): + """ + ``asdict`` only works on attrs instances, this works on anything. + """ + val_type = type(val) + if val_type in _ATOMIC_TYPES: + rv = val + if value_serializer is not None: + rv = value_serializer(None, None, rv) + elif getattr(val_type, "__attrs_attrs__", None) is not None: + # Attrs class. + rv = asdict( + val, + recurse=True, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + elif issubclass(val_type, (tuple, list, set, frozenset)): + if retain_collection_types is True: + cf = val.__class__ + elif is_key: + cf = tuple + else: + cf = list + + rv = cf( + [ + _asdict_anything( + i, + is_key=False, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + for i in val + ] + ) + elif issubclass(val_type, dict): + df = dict_factory + rv = df( + ( + _asdict_anything( + kk, + is_key=True, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + _asdict_anything( + vv, + is_key=False, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + ) + for kk, vv in val.items() + ) + else: + rv = val + if value_serializer is not None: + rv = value_serializer(None, None, rv) + + return rv + + +def astuple( + inst, + recurse=True, + filter=None, + tuple_factory=tuple, + retain_collection_types=False, +): + """ + Return the *attrs* attribute values of *inst* as a tuple. + + Optionally recurse into other *attrs*-decorated classes. + + Args: + inst: Instance of an *attrs*-decorated class. + + recurse (bool): + Recurse into classes that are also *attrs*-decorated. + + filter (~typing.Callable): + A callable whose return code determines whether an attribute or + element is included (`True`) or dropped (`False`). Is called with + the `attrs.Attribute` as the first argument and the value as the + second argument. + + tuple_factory (~typing.Callable): + A callable to produce tuples from. For example, to produce lists + instead of tuples. + + retain_collection_types (bool): + Do not convert to `list` or `dict` when encountering an attribute + which type is `tuple`, `dict` or `set`. Only meaningful if + *recurse* is `True`. + + Returns: + Return type of *tuple_factory* + + Raises: + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + .. versionadded:: 16.2.0 + """ + attrs = fields(inst.__class__) + rv = [] + retain = retain_collection_types # Very long. :/ + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + value_type = type(v) + if recurse is True: + if value_type in _ATOMIC_TYPES: + rv.append(v) + elif has(value_type): + rv.append( + astuple( + v, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + ) + elif issubclass(value_type, (tuple, list, set, frozenset)): + cf = v.__class__ if retain is True else list + items = [ + ( + astuple( + j, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(j.__class__) + else j + ) + for j in v + ] + try: + rv.append(cf(items)) + except TypeError: + if not issubclass(cf, tuple): + raise + # Workaround for TypeError: cf.__new__() missing 1 required + # positional argument (which appears, for a namedturle) + rv.append(cf(*items)) + elif issubclass(value_type, dict): + df = value_type if retain is True else dict + rv.append( + df( + ( + ( + astuple( + kk, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(kk.__class__) + else kk + ), + ( + astuple( + vv, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(vv.__class__) + else vv + ), + ) + for kk, vv in v.items() + ) + ) + else: + rv.append(v) + else: + rv.append(v) + + return rv if tuple_factory is list else tuple_factory(rv) + + +def has(cls): + """ + Check whether *cls* is a class with *attrs* attributes. + + Args: + cls (type): Class to introspect. + + Raises: + TypeError: If *cls* is not a class. + + Returns: + bool: + """ + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is not None: + return True + + # No attrs, maybe it's a specialized generic (A[str])? + generic_base = get_generic_base(cls) + if generic_base is not None: + generic_attrs = getattr(generic_base, "__attrs_attrs__", None) + if generic_attrs is not None: + # Stick it on here for speed next time. + cls.__attrs_attrs__ = generic_attrs + return generic_attrs is not None + return False + + +def assoc(inst, **changes): + """ + Copy *inst* and apply *changes*. + + This is different from `evolve` that applies the changes to the arguments + that create the new instance. + + `evolve`'s behavior is preferable, but there are `edge cases`_ where it + doesn't work. Therefore `assoc` is deprecated, but will not be removed. + + .. _`edge cases`: https://github.com/python-attrs/attrs/issues/251 + + Args: + inst: Instance of a class with *attrs* attributes. + + changes: Keyword changes in the new copy. + + Returns: + A copy of inst with *changes* incorporated. + + Raises: + attrs.exceptions.AttrsAttributeNotFoundError: + If *attr_name* couldn't be found on *cls*. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + .. deprecated:: 17.1.0 + Use `attrs.evolve` instead if you can. This function will not be + removed du to the slightly different approach compared to + `attrs.evolve`, though. + """ + new = copy.copy(inst) + attrs = fields(inst.__class__) + for k, v in changes.items(): + a = getattr(attrs, k, NOTHING) + if a is NOTHING: + msg = f"{k} is not an attrs attribute on {new.__class__}." + raise AttrsAttributeNotFoundError(msg) + _OBJ_SETATTR(new, k, v) + return new + + +def resolve_types( + cls, globalns=None, localns=None, attribs=None, include_extras=True +): + """ + Resolve any strings and forward annotations in type annotations. + + This is only required if you need concrete types in :class:`Attribute`'s + *type* field. In other words, you don't need to resolve your types if you + only use them for static type checking. + + With no arguments, names will be looked up in the module in which the class + was created. If this is not what you want, for example, if the name only + exists inside a method, you may pass *globalns* or *localns* to specify + other dictionaries in which to look up these names. See the docs of + `typing.get_type_hints` for more details. + + Args: + cls (type): Class to resolve. + + globalns (dict | None): Dictionary containing global variables. + + localns (dict | None): Dictionary containing local variables. + + attribs (list | None): + List of attribs for the given class. This is necessary when calling + from inside a ``field_transformer`` since *cls* is not an *attrs* + class yet. + + include_extras (bool): + Resolve more accurately, if possible. Pass ``include_extras`` to + ``typing.get_hints``, if supported by the typing module. On + supported Python versions (3.9+), this resolves the types more + accurately. + + Raises: + TypeError: If *cls* is not a class. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class and you didn't pass any attribs. + + NameError: If types cannot be resolved because of missing variables. + + Returns: + *cls* so you can use this function also as a class decorator. Please + note that you have to apply it **after** `attrs.define`. That means the + decorator has to come in the line **before** `attrs.define`. + + .. versionadded:: 20.1.0 + .. versionadded:: 21.1.0 *attribs* + .. versionadded:: 23.1.0 *include_extras* + """ + # Since calling get_type_hints is expensive we cache whether we've + # done it already. + if getattr(cls, "__attrs_types_resolved__", None) != cls: + import typing + + kwargs = { + "globalns": globalns, + "localns": localns, + "include_extras": include_extras, + } + + hints = typing.get_type_hints(cls, **kwargs) + for field in fields(cls) if attribs is None else attribs: + if field.name in hints: + # Since fields have been frozen we must work around it. + _OBJ_SETATTR(field, "type", hints[field.name]) + # We store the class we resolved so that subclasses know they haven't + # been resolved. + cls.__attrs_types_resolved__ = cls + + # Return the class so you can use it as a decorator too. + return cls diff --git a/venv/lib/python3.12/site-packages/attr/_make.py b/venv/lib/python3.12/site-packages/attr/_make.py new file mode 100644 index 0000000..4b32d6a --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_make.py @@ -0,0 +1,3406 @@ +# SPDX-License-Identifier: MIT + +from __future__ import annotations + +import abc +import contextlib +import copy +import enum +import inspect +import itertools +import linecache +import sys +import types +import unicodedata +import weakref + +from collections.abc import Callable, Mapping +from functools import cached_property +from typing import Any, NamedTuple, TypeVar + +# We need to import _compat itself in addition to the _compat members to avoid +# having the thread-local in the globals here. +from . import _compat, _config, setters +from ._compat import ( + PY_3_10_PLUS, + PY_3_11_PLUS, + PY_3_13_PLUS, + _AnnotationExtractor, + _get_annotations, + get_generic_base, +) +from .exceptions import ( + DefaultAlreadySetError, + FrozenInstanceError, + NotAnAttrsClassError, + UnannotatedAttributeError, +) + + +# This is used at least twice, so cache it here. +_OBJ_SETATTR = object.__setattr__ +_INIT_FACTORY_PAT = "__attr_factory_%s" +_CLASSVAR_PREFIXES = ( + "typing.ClassVar", + "t.ClassVar", + "ClassVar", + "typing_extensions.ClassVar", +) +# we don't use a double-underscore prefix because that triggers +# name mangling when trying to create a slot for the field +# (when slots=True) +_HASH_CACHE_FIELD = "_attrs_cached_hash" + +_EMPTY_METADATA_SINGLETON = types.MappingProxyType({}) + +# Unique object for unequivocal getattr() defaults. +_SENTINEL = object() + +_DEFAULT_ON_SETATTR = setters.pipe(setters.convert, setters.validate) + + +class _Nothing(enum.Enum): + """ + Sentinel to indicate the lack of a value when `None` is ambiguous. + + If extending attrs, you can use ``typing.Literal[NOTHING]`` to show + that a value may be ``NOTHING``. + + .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False. + .. versionchanged:: 22.2.0 ``NOTHING`` is now an ``enum.Enum`` variant. + """ + + NOTHING = enum.auto() + + def __repr__(self): + return "NOTHING" + + def __bool__(self): + return False + + +NOTHING = _Nothing.NOTHING +""" +Sentinel to indicate the lack of a value when `None` is ambiguous. + +When using in 3rd party code, use `attrs.NothingType` for type annotations. +""" + + +class _CacheHashWrapper(int): + """ + An integer subclass that pickles / copies as None + + This is used for non-slots classes with ``cache_hash=True``, to avoid + serializing a potentially (even likely) invalid hash value. Since `None` + is the default value for uncalculated hashes, whenever this is copied, + the copy's value for the hash should automatically reset. + + See GH #613 for more details. + """ + + def __reduce__(self, _none_constructor=type(None), _args=()): # noqa: B008 + return _none_constructor, _args + + +def attrib( + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=None, + init=True, + metadata=None, + type=None, + converter=None, + factory=None, + kw_only=None, + eq=None, + order=None, + on_setattr=None, + alias=None, +): + """ + Create a new field / attribute on a class. + + Identical to `attrs.field`, except it's not keyword-only. + + Consider using `attrs.field` in new code (``attr.ib`` will *never* go away, + though). + + .. warning:: + + Does **nothing** unless the class is also decorated with + `attr.s` (or similar)! + + + .. versionadded:: 15.2.0 *convert* + .. versionadded:: 16.3.0 *metadata* + .. versionchanged:: 17.1.0 *validator* can be a ``list`` now. + .. versionchanged:: 17.1.0 + *hash* is `None` and therefore mirrors *eq* by default. + .. versionadded:: 17.3.0 *type* + .. deprecated:: 17.4.0 *convert* + .. versionadded:: 17.4.0 + *converter* as a replacement for the deprecated *convert* to achieve + consistency with other noun-based arguments. + .. versionadded:: 18.1.0 + ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``. + .. versionadded:: 18.2.0 *kw_only* + .. versionchanged:: 19.2.0 *convert* keyword argument removed. + .. versionchanged:: 19.2.0 *repr* also accepts a custom callable. + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.3.0 *kw_only* backported to Python 2 + .. versionchanged:: 21.1.0 + *eq*, *order*, and *cmp* also accept a custom callable + .. versionchanged:: 21.1.0 *cmp* undeprecated + .. versionadded:: 22.2.0 *alias* + .. versionchanged:: 25.4.0 + *kw_only* can now be None, and its default is also changed from False to + None. + """ + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq, order, True + ) + + if hash is not None and hash is not True and hash is not False: + msg = "Invalid value for hash. Must be True, False, or None." + raise TypeError(msg) + + if factory is not None: + if default is not NOTHING: + msg = ( + "The `default` and `factory` arguments are mutually exclusive." + ) + raise ValueError(msg) + if not callable(factory): + msg = "The `factory` argument must be a callable." + raise ValueError(msg) + default = Factory(factory) + + if metadata is None: + metadata = {} + + # Apply syntactic sugar by auto-wrapping. + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + if validator and isinstance(validator, (list, tuple)): + validator = and_(*validator) + + if converter and isinstance(converter, (list, tuple)): + converter = pipe(*converter) + + return _CountingAttr( + default=default, + validator=validator, + repr=repr, + cmp=None, + hash=hash, + init=init, + converter=converter, + metadata=metadata, + type=type, + kw_only=kw_only, + eq=eq, + eq_key=eq_key, + order=order, + order_key=order_key, + on_setattr=on_setattr, + alias=alias, + ) + + +def _compile_and_eval( + script: str, + globs: dict[str, Any] | None, + locs: Mapping[str, object] | None = None, + filename: str = "", +) -> None: + """ + Evaluate the script with the given global (globs) and local (locs) + variables. + """ + bytecode = compile(script, filename, "exec") + eval(bytecode, globs, locs) + + +def _linecache_and_compile( + script: str, + filename: str, + globs: dict[str, Any] | None, + locals: Mapping[str, object] | None = None, +) -> dict[str, Any]: + """ + Cache the script with _linecache_, compile it and return the _locals_. + """ + + locs = {} if locals is None else locals + + # In order of debuggers like PDB being able to step through the code, + # we add a fake linecache entry. + count = 1 + base_filename = filename + while True: + linecache_tuple = ( + len(script), + None, + script.splitlines(True), + filename, + ) + old_val = linecache.cache.setdefault(filename, linecache_tuple) + if old_val == linecache_tuple: + break + + filename = f"{base_filename[:-1]}-{count}>" + count += 1 + + _compile_and_eval(script, globs, locs, filename) + + return locs + + +def _make_attr_tuple_class(cls_name: str, attr_names: list[str]) -> type: + """ + Create a tuple subclass to hold `Attribute`s for an `attrs` class. + + The subclass is a bare tuple with properties for names. + + class MyClassAttributes(tuple): + __slots__ = () + x = property(itemgetter(0)) + """ + attr_class_name = f"{cls_name}Attributes" + body = {} + for i, attr_name in enumerate(attr_names): + + def getter(self, i=i): + return self[i] + + body[attr_name] = property(getter) + return type(attr_class_name, (tuple,), body) + + +# Tuple class for extracted attributes from a class definition. +# `base_attrs` is a subset of `attrs`. +class _Attributes(NamedTuple): + attrs: type + base_attrs: list[Attribute] + base_attrs_map: dict[str, type] + + +def _is_class_var(annot): + """ + Check whether *annot* is a typing.ClassVar. + + The string comparison hack is used to avoid evaluating all string + annotations which would put attrs-based classes at a performance + disadvantage compared to plain old classes. + """ + annot = str(annot) + + # Annotation can be quoted. + if annot.startswith(("'", '"')) and annot.endswith(("'", '"')): + annot = annot[1:-1] + + return annot.startswith(_CLASSVAR_PREFIXES) + + +def _has_own_attribute(cls, attrib_name): + """ + Check whether *cls* defines *attrib_name* (and doesn't just inherit it). + """ + return attrib_name in cls.__dict__ + + +def _collect_base_attrs( + cls, taken_attr_names +) -> tuple[list[Attribute], dict[str, type]]: + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in reversed(cls.__mro__[1:-1]): + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.inherited or a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) # noqa: PLW2901 + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + # For each name, only keep the freshest definition i.e. the furthest at the + # back. base_attr_map is fine because it gets overwritten with every new + # instance. + filtered = [] + seen = set() + for a in reversed(base_attrs): + if a.name in seen: + continue + filtered.insert(0, a) + seen.add(a.name) + + return filtered, base_attr_map + + +def _collect_base_attrs_broken(cls, taken_attr_names): + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + + N.B. *taken_attr_names* will be mutated. + + Adhere to the old incorrect behavior. + + Notably it collects from the front and considers inherited attributes which + leads to the buggy behavior reported in #428. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in cls.__mro__[1:-1]: + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) # noqa: PLW2901 + taken_attr_names.add(a.name) + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + return base_attrs, base_attr_map + + +def _transform_attrs( + cls, + these, + auto_attribs, + kw_only, + collect_by_mro, + field_transformer, +) -> _Attributes: + """ + Transform all `_CountingAttr`s on a class into `Attribute`s. + + If *these* is passed, use that and don't look for them on the class. + + If *collect_by_mro* is True, collect them in the correct MRO order, + otherwise use the old -- incorrect -- order. See #428. + + Return an `_Attributes`. + """ + cd = cls.__dict__ + anns = _get_annotations(cls) + + if these is not None: + ca_list = list(these.items()) + elif auto_attribs is True: + ca_names = { + name + for name, attr in cd.items() + if attr.__class__ is _CountingAttr + } + ca_list = [] + annot_names = set() + for attr_name, type in anns.items(): + if _is_class_var(type): + continue + annot_names.add(attr_name) + a = cd.get(attr_name, NOTHING) + + if a.__class__ is not _CountingAttr: + a = attrib(a) + ca_list.append((attr_name, a)) + + unannotated = ca_names - annot_names + if unannotated: + raise UnannotatedAttributeError( + "The following `attr.ib`s lack a type annotation: " + + ", ".join( + sorted(unannotated, key=lambda n: cd.get(n).counter) + ) + + "." + ) + else: + ca_list = sorted( + ( + (name, attr) + for name, attr in cd.items() + if attr.__class__ is _CountingAttr + ), + key=lambda e: e[1].counter, + ) + + fca = Attribute.from_counting_attr + no = ClassProps.KeywordOnly.NO + own_attrs = [ + fca( + attr_name, + ca, + kw_only is not no, + anns.get(attr_name), + ) + for attr_name, ca in ca_list + ] + + if collect_by_mro: + base_attrs, base_attr_map = _collect_base_attrs( + cls, {a.name for a in own_attrs} + ) + else: + base_attrs, base_attr_map = _collect_base_attrs_broken( + cls, {a.name for a in own_attrs} + ) + + if kw_only is ClassProps.KeywordOnly.FORCE: + own_attrs = [a.evolve(kw_only=True) for a in own_attrs] + base_attrs = [a.evolve(kw_only=True) for a in base_attrs] + + attrs = base_attrs + own_attrs + + # Resolve default field alias before executing field_transformer, so that + # the transformer receives fully populated Attribute objects with usable + # alias values. + for a in attrs: + if not a.alias: + # Evolve is very slow, so we hold our nose and do it dirty. + _OBJ_SETATTR.__get__(a)("alias", _default_init_alias_for(a.name)) + _OBJ_SETATTR.__get__(a)("alias_is_default", True) + + if field_transformer is not None: + attrs = tuple(field_transformer(cls, attrs)) + + # Check attr order after executing the field_transformer. + # Mandatory vs non-mandatory attr order only matters when they are part of + # the __init__ signature and when they aren't kw_only (which are moved to + # the end and can be mandatory or non-mandatory in any order, as they will + # be specified as keyword args anyway). Check the order of those attrs: + had_default = False + for a in (a for a in attrs if a.init is not False and a.kw_only is False): + if had_default is True and a.default is NOTHING: + msg = f"No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: {a!r}" + raise ValueError(msg) + + if had_default is False and a.default is not NOTHING: + had_default = True + + # Resolve default field alias for any new attributes that the + # field_transformer may have added without setting an alias. + for a in attrs: + if not a.alias: + _OBJ_SETATTR.__get__(a)("alias", _default_init_alias_for(a.name)) + _OBJ_SETATTR.__get__(a)("alias_is_default", True) + + # Create AttrsClass *after* applying the field_transformer since it may + # add or remove attributes! + attr_names = [a.name for a in attrs] + AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) + + return _Attributes(AttrsClass(attrs), base_attrs, base_attr_map) + + +def _make_cached_property_getattr(cached_properties, original_getattr, cls): + lines = [ + # Wrapped to get `__class__` into closure cell for super() + # (It will be replaced with the newly constructed class after construction). + "def wrapper(_cls):", + " __class__ = _cls", + " def __getattr__(self, item, cached_properties=cached_properties, original_getattr=original_getattr, _cached_setattr_get=_cached_setattr_get):", + " func = cached_properties.get(item)", + " if func is not None:", + " result = func(self)", + " _setter = _cached_setattr_get(self)", + " _setter(item, result)", + " return result", + ] + if original_getattr is not None: + lines.append( + " return original_getattr(self, item)", + ) + else: + lines.extend( + [ + " try:", + " return super().__getattribute__(item)", + " except AttributeError:", + " if not hasattr(super(), '__getattr__'):", + " raise", + " return super().__getattr__(item)", + " original_error = f\"'{self.__class__.__name__}' object has no attribute '{item}'\"", + " raise AttributeError(original_error)", + ] + ) + + lines.extend( + [ + " return __getattr__", + "__getattr__ = wrapper(_cls)", + ] + ) + + unique_filename = _generate_unique_filename(cls, "getattr") + + glob = { + "cached_properties": cached_properties, + "_cached_setattr_get": _OBJ_SETATTR.__get__, + "original_getattr": original_getattr, + } + + return _linecache_and_compile( + "\n".join(lines), unique_filename, glob, locals={"_cls": cls} + )["__getattr__"] + + +def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + if isinstance(self, BaseException) and name in ( + "__cause__", + "__context__", + "__traceback__", + "__suppress_context__", + "__notes__", + ): + BaseException.__setattr__(self, name, value) + return + + raise FrozenInstanceError + + +def _frozen_delattrs(self, name): + """ + Attached to frozen classes as __delattr__. + """ + if isinstance(self, BaseException) and name == "__notes__": + BaseException.__delattr__(self, name) + return + + raise FrozenInstanceError + + +def evolve(*args, **changes): + """ + Create a new instance, based on the first positional argument with + *changes* applied. + + .. tip:: + + On Python 3.13 and later, you can also use `copy.replace` instead. + + Args: + + inst: + Instance of a class with *attrs* attributes. *inst* must be passed + as a positional argument. + + changes: + Keyword changes in the new copy. + + Returns: + A copy of inst with *changes* incorporated. + + Raises: + TypeError: + If *attr_name* couldn't be found in the class ``__init__``. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + .. versionadded:: 17.1.0 + .. deprecated:: 23.1.0 + It is now deprecated to pass the instance using the keyword argument + *inst*. It will raise a warning until at least April 2024, after which + it will become an error. Always pass the instance as a positional + argument. + .. versionchanged:: 24.1.0 + *inst* can't be passed as a keyword argument anymore. + """ + try: + (inst,) = args + except ValueError: + msg = ( + f"evolve() takes 1 positional argument, but {len(args)} were given" + ) + raise TypeError(msg) from None + + cls = inst.__class__ + attrs = fields(cls) + for a in attrs: + if not a.init: + continue + attr_name = a.name # To deal with private attributes. + init_name = a.alias + if init_name not in changes: + changes[init_name] = getattr(inst, attr_name) + + return cls(**changes) + + +class _ClassBuilder: + """ + Iteratively build *one* class. + """ + + __slots__ = ( + "_add_method_dunders", + "_attr_names", + "_attrs", + "_base_attr_map", + "_base_names", + "_cache_hash", + "_cls", + "_cls_dict", + "_delete_attribs", + "_frozen", + "_has_custom_setattr", + "_has_post_init", + "_has_pre_init", + "_is_exc", + "_on_setattr", + "_pre_init_has_args", + "_repr_added", + "_script_snippets", + "_slots", + "_weakref_slot", + "_wrote_own_setattr", + ) + + def __init__( + self, + cls: type, + these, + auto_attribs: bool, + props: ClassProps, + has_custom_setattr: bool, + ): + attrs, base_attrs, base_map = _transform_attrs( + cls, + these, + auto_attribs, + props.kw_only, + props.collected_fields_by_mro, + props.field_transformer, + ) + + self._cls = cls + self._cls_dict = dict(cls.__dict__) if props.is_slotted else {} + self._attrs = attrs + self._base_names = {a.name for a in base_attrs} + self._base_attr_map = base_map + self._attr_names = tuple(a.name for a in attrs) + self._slots = props.is_slotted + self._frozen = props.is_frozen + self._weakref_slot = props.has_weakref_slot + self._cache_hash = ( + props.hashability is ClassProps.Hashability.HASHABLE_CACHED + ) + self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False)) + self._pre_init_has_args = False + if self._has_pre_init: + # Check if the pre init method has more arguments than just `self` + # We want to pass arguments if pre init expects arguments + pre_init_func = cls.__attrs_pre_init__ + pre_init_signature = inspect.signature(pre_init_func) + self._pre_init_has_args = len(pre_init_signature.parameters) > 1 + self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False)) + self._delete_attribs = not bool(these) + self._is_exc = props.is_exception + self._on_setattr = props.on_setattr_hook + + self._has_custom_setattr = has_custom_setattr + self._wrote_own_setattr = False + + self._cls_dict["__attrs_attrs__"] = self._attrs + self._cls_dict["__attrs_props__"] = props + + if props.is_frozen: + self._cls_dict["__setattr__"] = _frozen_setattrs + self._cls_dict["__delattr__"] = _frozen_delattrs + + self._wrote_own_setattr = True + elif self._on_setattr in ( + _DEFAULT_ON_SETATTR, + setters.validate, + setters.convert, + ): + has_validator = has_converter = False + for a in attrs: + if a.validator is not None: + has_validator = True + if a.converter is not None: + has_converter = True + + if has_validator and has_converter: + break + if ( + ( + self._on_setattr == _DEFAULT_ON_SETATTR + and not (has_validator or has_converter) + ) + or (self._on_setattr == setters.validate and not has_validator) + or (self._on_setattr == setters.convert and not has_converter) + ): + # If class-level on_setattr is set to convert + validate, but + # there's no field to convert or validate, pretend like there's + # no on_setattr. + self._on_setattr = None + + if props.added_pickling: + ( + self._cls_dict["__getstate__"], + self._cls_dict["__setstate__"], + ) = self._make_getstate_setstate() + + # tuples of script, globs, hook + self._script_snippets: list[ + tuple[str, dict, Callable[[dict, dict], Any]] + ] = [] + self._repr_added = False + + # We want to only do this check once; in 99.9% of cases these + # exist. + if not hasattr(self._cls, "__module__") or not hasattr( + self._cls, "__qualname__" + ): + self._add_method_dunders = self._add_method_dunders_safe + else: + self._add_method_dunders = self._add_method_dunders_unsafe + + def __repr__(self): + return f"<_ClassBuilder(cls={self._cls.__name__})>" + + def _eval_snippets(self) -> None: + """ + Evaluate any registered snippets in one go. + """ + script = "\n".join([snippet[0] for snippet in self._script_snippets]) + globs = {} + for _, snippet_globs, _ in self._script_snippets: + globs.update(snippet_globs) + + locs = _linecache_and_compile( + script, + _generate_unique_filename(self._cls, "methods"), + globs, + ) + + for _, _, hook in self._script_snippets: + hook(self._cls_dict, locs) + + def build_class(self): + """ + Finalize class based on the accumulated configuration. + + Builder cannot be used after calling this method. + """ + self._eval_snippets() + if self._slots is True: + cls = self._create_slots_class() + self._cls.__attrs_base_of_slotted__ = weakref.ref(cls) + else: + cls = self._patch_original_class() + if PY_3_10_PLUS: + cls = abc.update_abstractmethods(cls) + + # The method gets only called if it's not inherited from a base class. + # _has_own_attribute does NOT work properly for classmethods. + if ( + getattr(cls, "__attrs_init_subclass__", None) + and "__attrs_init_subclass__" not in cls.__dict__ + ): + cls.__attrs_init_subclass__() + + return cls + + def _patch_original_class(self): + """ + Apply accumulated methods and return the class. + """ + cls = self._cls + base_names = self._base_names + + # Clean class of attribute definitions (`attr.ib()`s). + if self._delete_attribs: + for name in self._attr_names: + if ( + name not in base_names + and getattr(cls, name, _SENTINEL) is not _SENTINEL + ): + # An AttributeError can happen if a base class defines a + # class variable and we want to set an attribute with the + # same name by using only a type annotation. + with contextlib.suppress(AttributeError): + delattr(cls, name) + + # Attach our dunder methods. + for name, value in self._cls_dict.items(): + setattr(cls, name, value) + + # If we've inherited an attrs __setattr__ and don't write our own, + # reset it to object's. + if not self._wrote_own_setattr and getattr( + cls, "__attrs_own_setattr__", False + ): + cls.__attrs_own_setattr__ = False + + if not self._has_custom_setattr: + cls.__setattr__ = _OBJ_SETATTR + + return cls + + def _create_slots_class(self): + """ + Build and return a new class with a `__slots__` attribute. + """ + cd = { + k: v + for k, v in self._cls_dict.items() + if k not in (*tuple(self._attr_names), "__dict__", "__weakref__") + } + + # 3.14.0rc2+ + if hasattr(sys, "_clear_type_descriptors"): + sys._clear_type_descriptors(self._cls) + + # If our class doesn't have its own implementation of __setattr__ + # (either from the user or by us), check the bases, if one of them has + # an attrs-made __setattr__, that needs to be reset. We don't walk the + # MRO because we only care about our immediate base classes. + # XXX: This can be confused by subclassing a slotted attrs class with + # XXX: a non-attrs class and subclass the resulting class with an attrs + # XXX: class. See `test_slotted_confused` for details. For now that's + # XXX: OK with us. + if not self._wrote_own_setattr: + cd["__attrs_own_setattr__"] = False + + if not self._has_custom_setattr: + for base_cls in self._cls.__bases__: + if base_cls.__dict__.get("__attrs_own_setattr__", False): + cd["__setattr__"] = _OBJ_SETATTR + break + + # Traverse the MRO to collect existing slots + # and check for an existing __weakref__. + existing_slots = {} + weakref_inherited = False + for base_cls in self._cls.__mro__[1:-1]: + if base_cls.__dict__.get("__weakref__", None) is not None: + weakref_inherited = True + existing_slots.update( + { + name: getattr(base_cls, name) + for name in getattr(base_cls, "__slots__", []) + } + ) + + base_names = set(self._base_names) + + names = self._attr_names + if ( + self._weakref_slot + and "__weakref__" not in getattr(self._cls, "__slots__", ()) + and "__weakref__" not in names + and not weakref_inherited + ): + names += ("__weakref__",) + + cached_properties = { + name: cached_prop.func + for name, cached_prop in cd.items() + if isinstance(cached_prop, cached_property) + } + + # Collect methods with a `__class__` reference that are shadowed in the new class. + # To know to update them. + additional_closure_functions_to_update = [] + if cached_properties: + class_annotations = _get_annotations(self._cls) + for name, func in cached_properties.items(): + # Add cached properties to names for slotting. + names += (name,) + # Clear out function from class to avoid clashing. + del cd[name] + additional_closure_functions_to_update.append(func) + annotation = inspect.signature(func).return_annotation + if annotation is not inspect.Parameter.empty: + class_annotations[name] = annotation + + original_getattr = cd.get("__getattr__") + if original_getattr is not None: + additional_closure_functions_to_update.append(original_getattr) + + cd["__getattr__"] = _make_cached_property_getattr( + cached_properties, original_getattr, self._cls + ) + + # We only add the names of attributes that aren't inherited. + # Setting __slots__ to inherited attributes wastes memory. + slot_names = [name for name in names if name not in base_names] + + # There are slots for attributes from current class + # that are defined in parent classes. + # As their descriptors may be overridden by a child class, + # we collect them here and update the class dict + reused_slots = { + slot: slot_descriptor + for slot, slot_descriptor in existing_slots.items() + if slot in slot_names + } + slot_names = [name for name in slot_names if name not in reused_slots] + cd.update(reused_slots) + if self._cache_hash: + slot_names.append(_HASH_CACHE_FIELD) + + cd["__slots__"] = tuple(slot_names) + + cd["__qualname__"] = self._cls.__qualname__ + + # Create new class based on old class and our methods. + cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd) + + # The following is a fix for + # . + # If a method mentions `__class__` or uses the no-arg super(), the + # compiler will bake a reference to the class in the method itself + # as `method.__closure__`. Since we replace the class with a + # clone, we rewrite these references so it keeps working. + for item in itertools.chain( + cls.__dict__.values(), additional_closure_functions_to_update + ): + if isinstance(item, (classmethod, staticmethod)): + # Class- and staticmethods hide their functions inside. + # These might need to be rewritten as well. + closure_cells = getattr(item.__func__, "__closure__", None) + elif isinstance(item, property): + # Workaround for property `super()` shortcut (PY3-only). + # There is no universal way for other descriptors. + closure_cells = getattr(item.fget, "__closure__", None) + else: + closure_cells = getattr(item, "__closure__", None) + + if not closure_cells: # Catch None or the empty list. + continue + for cell in closure_cells: + try: + match = cell.cell_contents is self._cls + except ValueError: # noqa: PERF203 + # ValueError: Cell is empty + pass + else: + if match: + cell.cell_contents = cls + return cls + + def add_repr(self, ns): + script, globs = _make_repr_script(self._attrs, ns) + + def _attach_repr(cls_dict, globs): + cls_dict["__repr__"] = self._add_method_dunders(globs["__repr__"]) + + self._script_snippets.append((script, globs, _attach_repr)) + self._repr_added = True + return self + + def add_str(self): + if not self._repr_added: + msg = "__str__ can only be generated if a __repr__ exists." + raise ValueError(msg) + + def __str__(self): + return self.__repr__() + + self._cls_dict["__str__"] = self._add_method_dunders(__str__) + return self + + def _make_getstate_setstate(self): + """ + Create custom __setstate__ and __getstate__ methods. + """ + # __weakref__ is not writable. + state_attr_names = tuple( + an for an in self._attr_names if an != "__weakref__" + ) + + def slots_getstate(self): + """ + Automatically created by attrs. + """ + return {name: getattr(self, name) for name in state_attr_names} + + hash_caching_enabled = self._cache_hash + + def slots_setstate(self, state): + """ + Automatically created by attrs. + """ + __bound_setattr = _OBJ_SETATTR.__get__(self) + if isinstance(state, tuple): + # Backward compatibility with attrs instances pickled with + # attrs versions before v22.2.0 which stored tuples. + for name, value in zip(state_attr_names, state): + __bound_setattr(name, value) + else: + for name in state_attr_names: + if name in state: + __bound_setattr(name, state[name]) + + # The hash code cache is not included when the object is + # serialized, but it still needs to be initialized to None to + # indicate that the first call to __hash__ should be a cache + # miss. + if hash_caching_enabled: + __bound_setattr(_HASH_CACHE_FIELD, None) + + return slots_getstate, slots_setstate + + def make_unhashable(self): + self._cls_dict["__hash__"] = None + return self + + def add_hash(self): + script, globs = _make_hash_script( + self._cls, + self._attrs, + frozen=self._frozen, + cache_hash=self._cache_hash, + ) + + def attach_hash(cls_dict: dict, locs: dict) -> None: + cls_dict["__hash__"] = self._add_method_dunders(locs["__hash__"]) + + self._script_snippets.append((script, globs, attach_hash)) + + return self + + def add_init(self): + script, globs, annotations = _make_init_script( + self._cls, + self._attrs, + self._has_pre_init, + self._pre_init_has_args, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr, + attrs_init=False, + ) + + def _attach_init(cls_dict, globs): + init = globs["__init__"] + init.__annotations__ = annotations + cls_dict["__init__"] = self._add_method_dunders(init) + + self._script_snippets.append((script, globs, _attach_init)) + + return self + + def add_replace(self): + self._cls_dict["__replace__"] = self._add_method_dunders(evolve) + return self + + def add_match_args(self): + self._cls_dict["__match_args__"] = tuple( + field.name + for field in self._attrs + if field.init and not field.kw_only + ) + + def add_attrs_init(self): + script, globs, annotations = _make_init_script( + self._cls, + self._attrs, + self._has_pre_init, + self._pre_init_has_args, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr, + attrs_init=True, + ) + + def _attach_attrs_init(cls_dict, globs): + init = globs["__attrs_init__"] + init.__annotations__ = annotations + cls_dict["__attrs_init__"] = self._add_method_dunders(init) + + self._script_snippets.append((script, globs, _attach_attrs_init)) + + return self + + def add_eq(self): + cd = self._cls_dict + + script, globs = _make_eq_script(self._attrs) + + def _attach_eq(cls_dict, globs): + cls_dict["__eq__"] = self._add_method_dunders(globs["__eq__"]) + + self._script_snippets.append((script, globs, _attach_eq)) + + cd["__ne__"] = __ne__ + + return self + + def add_order(self): + cd = self._cls_dict + + cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = ( + self._add_method_dunders(meth) + for meth in _make_order(self._cls, self._attrs) + ) + + return self + + def add_setattr(self): + sa_attrs = {} + for a in self._attrs: + on_setattr = a.on_setattr or self._on_setattr + if on_setattr and on_setattr is not setters.NO_OP: + sa_attrs[a.name] = a, on_setattr + + if not sa_attrs: + return self + + if self._has_custom_setattr: + # We need to write a __setattr__ but there already is one! + msg = "Can't combine custom __setattr__ with on_setattr hooks." + raise ValueError(msg) + + # docstring comes from _add_method_dunders + def __setattr__(self, name, val): + try: + a, hook = sa_attrs[name] + except KeyError: + nval = val + else: + nval = hook(self, a, val) + + _OBJ_SETATTR(self, name, nval) + + self._cls_dict["__attrs_own_setattr__"] = True + self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__) + self._wrote_own_setattr = True + + return self + + def _add_method_dunders_unsafe(self, method: Callable) -> Callable: + """ + Add __module__ and __qualname__ to a *method*. + """ + method.__module__ = self._cls.__module__ + + method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}" + + method.__doc__ = ( + f"Method generated by attrs for class {self._cls.__qualname__}." + ) + + return method + + def _add_method_dunders_safe(self, method: Callable) -> Callable: + """ + Add __module__ and __qualname__ to a *method* if possible. + """ + with contextlib.suppress(AttributeError): + method.__module__ = self._cls.__module__ + + with contextlib.suppress(AttributeError): + method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}" + + with contextlib.suppress(AttributeError): + method.__doc__ = f"Method generated by attrs for class {self._cls.__qualname__}." + + return method + + +def _determine_attrs_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + msg = "Don't mix `cmp` with `eq' and `order`." + raise ValueError(msg) + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + return cmp, cmp + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq = default_eq + + if order is None: + order = eq + + if eq is False and order is True: + msg = "`order` can only be True if `eq` is True too." + raise ValueError(msg) + + return eq, order + + +def _determine_attrib_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + msg = "Don't mix `cmp` with `eq' and `order`." + raise ValueError(msg) + + def decide_callable_or_boolean(value): + """ + Decide whether a key function is used. + """ + if callable(value): + value, key = True, value + else: + key = None + return value, key + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + cmp, cmp_key = decide_callable_or_boolean(cmp) + return cmp, cmp_key, cmp, cmp_key + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq, eq_key = default_eq, None + else: + eq, eq_key = decide_callable_or_boolean(eq) + + if order is None: + order, order_key = eq, eq_key + else: + order, order_key = decide_callable_or_boolean(order) + + if eq is False and order is True: + msg = "`order` can only be True if `eq` is True too." + raise ValueError(msg) + + return eq, eq_key, order, order_key + + +def _determine_whether_to_implement( + cls, flag, auto_detect, dunders, default=True +): + """ + Check whether we should implement a set of methods for *cls*. + + *flag* is the argument passed into @attr.s like 'init', *auto_detect* the + same as passed into @attr.s and *dunders* is a tuple of attribute names + whose presence signal that the user has implemented it themselves. + + Return *default* if no reason for either for or against is found. + """ + if flag is True or flag is False: + return flag + + if flag is None and auto_detect is False: + return default + + # Logically, flag is None and auto_detect is True here. + for dunder in dunders: + if _has_own_attribute(cls, dunder): + return False + + return default + + +def attrs( + maybe_cls=None, + these=None, + repr_ns=None, + repr=None, + cmp=None, + hash=None, + init=None, + slots=False, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=False, + kw_only=False, + cache_hash=False, + auto_exc=False, + eq=None, + order=None, + auto_detect=False, + collect_by_mro=False, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, + match_args=True, + unsafe_hash=None, + force_kw_only=True, +): + r""" + A class decorator that adds :term:`dunder methods` according to the + specified attributes using `attr.ib` or the *these* argument. + + Consider using `attrs.define` / `attrs.frozen` in new code (``attr.s`` will + *never* go away, though). + + Args: + repr_ns (str): + When using nested classes, there was no way in Python 2 to + automatically detect that. This argument allows to set a custom + name for a more meaningful ``repr`` output. This argument is + pointless in Python 3 and is therefore deprecated. + + .. caution:: + Refer to `attrs.define` for the rest of the parameters, but note that they + can have different defaults. + + Notably, leaving *on_setattr* as `None` will **not** add any hooks. + + .. versionadded:: 16.0.0 *slots* + .. versionadded:: 16.1.0 *frozen* + .. versionadded:: 16.3.0 *str* + .. versionadded:: 16.3.0 Support for ``__attrs_post_init__``. + .. versionchanged:: 17.1.0 + *hash* supports `None` as value which is also the default now. + .. versionadded:: 17.3.0 *auto_attribs* + .. versionchanged:: 18.1.0 + If *these* is passed, no attributes are deleted from the class body. + .. versionchanged:: 18.1.0 If *these* is ordered, the order is retained. + .. versionadded:: 18.2.0 *weakref_slot* + .. deprecated:: 18.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a + `DeprecationWarning` if the classes compared are subclasses of + each other. ``__eq`` and ``__ne__`` never tried to compared subclasses + to each other. + .. versionchanged:: 19.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider + subclasses comparable anymore. + .. versionadded:: 18.2.0 *kw_only* + .. versionadded:: 18.2.0 *cache_hash* + .. versionadded:: 19.1.0 *auto_exc* + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *auto_detect* + .. versionadded:: 20.1.0 *collect_by_mro* + .. versionadded:: 20.1.0 *getstate_setstate* + .. versionadded:: 20.1.0 *on_setattr* + .. versionadded:: 20.3.0 *field_transformer* + .. versionchanged:: 21.1.0 + ``init=False`` injects ``__attrs_init__`` + .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__`` + .. versionchanged:: 21.1.0 *cmp* undeprecated + .. versionadded:: 21.3.0 *match_args* + .. versionadded:: 22.2.0 + *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance). + .. deprecated:: 24.1.0 *repr_ns* + .. versionchanged:: 24.1.0 + Instances are not compared as tuples of attributes anymore, but using a + big ``and`` condition. This is faster and has more correct behavior for + uncomparable values like `math.nan`. + .. versionadded:: 24.1.0 + If a class has an *inherited* classmethod called + ``__attrs_init_subclass__``, it is executed after the class is created. + .. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*. + .. versionchanged:: 25.4.0 + *kw_only* now only applies to attributes defined in the current class, + and respects attribute-level ``kw_only=False`` settings. + .. versionadded:: 25.4.0 *force_kw_only* + """ + if repr_ns is not None: + import warnings + + warnings.warn( + DeprecationWarning( + "The `repr_ns` argument is deprecated and will be removed in or after August 2025." + ), + stacklevel=2, + ) + + eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None) + + # unsafe_hash takes precedence due to PEP 681. + if unsafe_hash is not None: + hash = unsafe_hash + + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + def wrap(cls): + nonlocal hash + is_frozen = frozen or _has_frozen_base_class(cls) + is_exc = auto_exc is True and issubclass(cls, BaseException) + has_own_setattr = auto_detect and _has_own_attribute( + cls, "__setattr__" + ) + + if has_own_setattr and is_frozen: + msg = "Can't freeze a class with a custom __setattr__." + raise ValueError(msg) + + eq = not is_exc and _determine_whether_to_implement( + cls, eq_, auto_detect, ("__eq__", "__ne__") + ) + + Hashability = ClassProps.Hashability + + if is_exc: + hashability = Hashability.LEAVE_ALONE + elif hash is True: + hashability = ( + Hashability.HASHABLE_CACHED + if cache_hash + else Hashability.HASHABLE + ) + elif hash is False: + hashability = Hashability.LEAVE_ALONE + elif hash is None: + if auto_detect is True and _has_own_attribute(cls, "__hash__"): + hashability = Hashability.LEAVE_ALONE + elif eq is True and is_frozen is True: + hashability = ( + Hashability.HASHABLE_CACHED + if cache_hash + else Hashability.HASHABLE + ) + elif eq is False: + hashability = Hashability.LEAVE_ALONE + else: + hashability = Hashability.UNHASHABLE + else: + msg = "Invalid value for hash. Must be True, False, or None." + raise TypeError(msg) + + KeywordOnly = ClassProps.KeywordOnly + if kw_only: + kwo = KeywordOnly.FORCE if force_kw_only else KeywordOnly.YES + else: + kwo = KeywordOnly.NO + + props = ClassProps( + is_exception=is_exc, + is_frozen=is_frozen, + is_slotted=slots, + collected_fields_by_mro=collect_by_mro, + added_init=_determine_whether_to_implement( + cls, init, auto_detect, ("__init__",) + ), + added_repr=_determine_whether_to_implement( + cls, repr, auto_detect, ("__repr__",) + ), + added_eq=eq, + added_ordering=not is_exc + and _determine_whether_to_implement( + cls, + order_, + auto_detect, + ("__lt__", "__le__", "__gt__", "__ge__"), + ), + hashability=hashability, + added_match_args=match_args, + kw_only=kwo, + has_weakref_slot=weakref_slot, + added_str=str, + added_pickling=_determine_whether_to_implement( + cls, + getstate_setstate, + auto_detect, + ("__getstate__", "__setstate__"), + default=slots, + ), + on_setattr_hook=on_setattr, + field_transformer=field_transformer, + ) + + if not props.is_hashable and cache_hash: + msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled." + raise TypeError(msg) + + builder = _ClassBuilder( + cls, + these, + auto_attribs=auto_attribs, + props=props, + has_custom_setattr=has_own_setattr, + ) + + if props.added_repr: + builder.add_repr(repr_ns) + + if props.added_str: + builder.add_str() + + if props.added_eq: + builder.add_eq() + if props.added_ordering: + builder.add_order() + + if not frozen: + builder.add_setattr() + + if props.is_hashable: + builder.add_hash() + elif props.hashability is Hashability.UNHASHABLE: + builder.make_unhashable() + + if props.added_init: + builder.add_init() + else: + builder.add_attrs_init() + if cache_hash: + msg = "Invalid value for cache_hash. To use hash caching, init must be True." + raise TypeError(msg) + + if PY_3_13_PLUS and not _has_own_attribute(cls, "__replace__"): + builder.add_replace() + + if ( + PY_3_10_PLUS + and match_args + and not _has_own_attribute(cls, "__match_args__") + ): + builder.add_match_args() + + return builder.build_class() + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but `None` if used as `@attrs()`. + if maybe_cls is None: + return wrap + + return wrap(maybe_cls) + + +_attrs = attrs +""" +Internal alias so we can use it in functions that take an argument called +*attrs*. +""" + + +def _has_frozen_base_class(cls): + """ + Check whether *cls* has a frozen ancestor by looking at its + __setattr__. + """ + return cls.__setattr__ is _frozen_setattrs + + +def _generate_unique_filename(cls: type, func_name: str) -> str: + """ + Create a "filename" suitable for a function being generated. + """ + return ( + f"" + ) + + +def _make_hash_script( + cls: type, attrs: list[Attribute], frozen: bool, cache_hash: bool +) -> tuple[str, dict]: + attrs = tuple( + a for a in attrs if a.hash is True or (a.hash is None and a.eq is True) + ) + + tab = " " + + type_hash = hash(_generate_unique_filename(cls, "hash")) + # If eq is custom generated, we need to include the functions in globs + globs = {} + + hash_def = "def __hash__(self" + hash_func = "hash((" + closing_braces = "))" + if not cache_hash: + hash_def += "):" + else: + hash_def += ", *" + + hash_def += ", _cache_wrapper=__import__('attr._make')._make._CacheHashWrapper):" + hash_func = "_cache_wrapper(" + hash_func + closing_braces += ")" + + method_lines = [hash_def] + + def append_hash_computation_lines(prefix, indent): + """ + Generate the code for actually computing the hash code. + Below this will either be returned directly or used to compute + a value which is then cached, depending on the value of cache_hash + """ + + method_lines.extend( + [ + indent + prefix + hash_func, + indent + f" {type_hash},", + ] + ) + + for a in attrs: + if a.eq_key: + cmp_name = f"_{a.name}_key" + globs[cmp_name] = a.eq_key + method_lines.append( + indent + f" {cmp_name}(self.{a.name})," + ) + else: + method_lines.append(indent + f" self.{a.name},") + + method_lines.append(indent + " " + closing_braces) + + if cache_hash: + method_lines.append(tab + f"if self.{_HASH_CACHE_FIELD} is None:") + if frozen: + append_hash_computation_lines( + f"object.__setattr__(self, '{_HASH_CACHE_FIELD}', ", tab * 2 + ) + method_lines.append(tab * 2 + ")") # close __setattr__ + else: + append_hash_computation_lines( + f"self.{_HASH_CACHE_FIELD} = ", tab * 2 + ) + method_lines.append(tab + f"return self.{_HASH_CACHE_FIELD}") + else: + append_hash_computation_lines("return ", tab) + + script = "\n".join(method_lines) + return script, globs + + +def _add_hash(cls: type, attrs: list[Attribute]): + """ + Add a hash method to *cls*. + """ + script, globs = _make_hash_script( + cls, attrs, frozen=False, cache_hash=False + ) + _compile_and_eval( + script, globs, filename=_generate_unique_filename(cls, "__hash__") + ) + cls.__hash__ = globs["__hash__"] + return cls + + +def __ne__(self, other): + """ + Check equality and either forward a NotImplemented or + return the result negated. + """ + result = self.__eq__(other) + if result is NotImplemented: + return NotImplemented + + return not result + + +def _make_eq_script(attrs: list) -> tuple[str, dict]: + """ + Create __eq__ method for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.eq] + + lines = [ + "def __eq__(self, other):", + " if other.__class__ is not self.__class__:", + " return NotImplemented", + ] + + globs = {} + if attrs: + lines.append(" return (") + for a in attrs: + if a.eq_key: + cmp_name = f"_{a.name}_key" + # Add the key function to the global namespace + # of the evaluated function. + globs[cmp_name] = a.eq_key + lines.append( + f" {cmp_name}(self.{a.name}) == {cmp_name}(other.{a.name})" + ) + else: + lines.append(f" self.{a.name} == other.{a.name}") + if a is not attrs[-1]: + lines[-1] = f"{lines[-1]} and" + lines.append(" )") + else: + lines.append(" return True") + + script = "\n".join(lines) + + return script, globs + + +def _make_order(cls, attrs): + """ + Create ordering methods for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.order] + + def attrs_to_tuple(obj): + """ + Save us some typing. + """ + return tuple( + key(value) if key else value + for value, key in ( + (getattr(obj, a.name), a.order_key) for a in attrs + ) + ) + + def __lt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) < attrs_to_tuple(other) + + return NotImplemented + + def __le__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) <= attrs_to_tuple(other) + + return NotImplemented + + def __gt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) > attrs_to_tuple(other) + + return NotImplemented + + def __ge__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) >= attrs_to_tuple(other) + + return NotImplemented + + return __lt__, __le__, __gt__, __ge__ + + +def _add_eq(cls, attrs=None): + """ + Add equality methods to *cls* with *attrs*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + script, globs = _make_eq_script(attrs) + _compile_and_eval( + script, globs, filename=_generate_unique_filename(cls, "__eq__") + ) + cls.__eq__ = globs["__eq__"] + cls.__ne__ = __ne__ + + return cls + + +def _make_repr_script(attrs, ns) -> tuple[str, dict]: + """ + Create the source and globs for a __repr__ and return it. + """ + # Figure out which attributes to include, and which function to use to + # format them. The a.repr value can be either bool or a custom + # callable. + attr_names_with_reprs = tuple( + (a.name, (repr if a.repr is True else a.repr), a.init) + for a in attrs + if a.repr is not False + ) + globs = { + name + "_repr": r for name, r, _ in attr_names_with_reprs if r != repr + } + globs["_compat"] = _compat + globs["AttributeError"] = AttributeError + globs["NOTHING"] = NOTHING + attribute_fragments = [] + for name, r, i in attr_names_with_reprs: + accessor = ( + "self." + name if i else 'getattr(self, "' + name + '", NOTHING)' + ) + fragment = ( + "%s={%s!r}" % (name, accessor) + if r == repr + else "%s={%s_repr(%s)}" % (name, name, accessor) + ) + attribute_fragments.append(fragment) + repr_fragment = ", ".join(attribute_fragments) + + if ns is None: + cls_name_fragment = '{self.__class__.__qualname__.rsplit(">.", 1)[-1]}' + else: + cls_name_fragment = ns + ".{self.__class__.__name__}" + + lines = [ + "def __repr__(self):", + " try:", + " already_repring = _compat.repr_context.already_repring", + " except AttributeError:", + " already_repring = {id(self),}", + " _compat.repr_context.already_repring = already_repring", + " else:", + " if id(self) in already_repring:", + " return '...'", + " else:", + " already_repring.add(id(self))", + " try:", + f" return f'{cls_name_fragment}({repr_fragment})'", + " finally:", + " already_repring.remove(id(self))", + ] + + return "\n".join(lines), globs + + +def _add_repr(cls, ns=None, attrs=None): + """ + Add a repr method to *cls*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + script, globs = _make_repr_script(attrs, ns) + _compile_and_eval( + script, globs, filename=_generate_unique_filename(cls, "__repr__") + ) + cls.__repr__ = globs["__repr__"] + return cls + + +def fields(cls): + """ + Return the tuple of *attrs* attributes for a class or instance. + + The tuple also allows accessing the fields by their names (see below for + examples). + + Args: + cls (type): Class or instance to introspect. + + Raises: + TypeError: If *cls* is neither a class nor an *attrs* instance. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + Returns: + tuple (with name accessors) of `attrs.Attribute` + + .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields + by name. + .. versionchanged:: 23.1.0 Add support for generic classes. + .. versionchanged:: 26.1.0 Add support for instances. + """ + generic_base = get_generic_base(cls) + + if generic_base is None and not isinstance(cls, type): + type_ = type(cls) + if getattr(type_, "__attrs_attrs__", None) is None: + msg = "Passed object must be a class or attrs instance." + raise TypeError(msg) + + return fields(type_) + + attrs = getattr(cls, "__attrs_attrs__", None) + + if attrs is None: + if generic_base is not None: + attrs = getattr(generic_base, "__attrs_attrs__", None) + if attrs is not None: + # Even though this is global state, stick it on here to speed + # it up. We rely on `cls` being cached for this to be + # efficient. + cls.__attrs_attrs__ = attrs + return attrs + msg = f"{cls!r} is not an attrs-decorated class." + raise NotAnAttrsClassError(msg) + + return attrs + + +def fields_dict(cls): + """ + Return an ordered dictionary of *attrs* attributes for a class, whose keys + are the attribute names. + + Args: + cls (type): Class to introspect. + + Raises: + TypeError: If *cls* is not a class. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + Returns: + dict[str, attrs.Attribute]: Dict of attribute name to definition + + .. versionadded:: 18.1.0 + """ + if not isinstance(cls, type): + msg = "Passed object must be a class." + raise TypeError(msg) + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is None: + msg = f"{cls!r} is not an attrs-decorated class." + raise NotAnAttrsClassError(msg) + return {a.name: a for a in attrs} + + +def validate(inst): + """ + Validate all attributes on *inst* that have a validator. + + Leaves all exceptions through. + + Args: + inst: Instance of a class with *attrs* attributes. + """ + if _config._run_validators is False: + return + + for a in fields(inst.__class__): + v = a.validator + if v is not None: + v(inst, a, getattr(inst, a.name)) + + +def _is_slot_attr(a_name, base_attr_map): + """ + Check if the attribute name comes from a slot class. + """ + cls = base_attr_map.get(a_name) + return cls and "__slots__" in cls.__dict__ + + +def _make_init_script( + cls, + attrs, + pre_init, + pre_init_has_args, + post_init, + frozen, + slots, + cache_hash, + base_attr_map, + is_exc, + cls_on_setattr, + attrs_init, +) -> tuple[str, dict, dict]: + has_cls_on_setattr = ( + cls_on_setattr is not None and cls_on_setattr is not setters.NO_OP + ) + + if frozen and has_cls_on_setattr: + msg = "Frozen classes can't use on_setattr." + raise ValueError(msg) + + needs_cached_setattr = cache_hash or frozen + filtered_attrs = [] + attr_dict = {} + for a in attrs: + if not a.init and a.default is NOTHING: + continue + + filtered_attrs.append(a) + attr_dict[a.name] = a + + if a.on_setattr is not None: + if frozen is True and a.on_setattr is not setters.NO_OP: + msg = "Frozen classes can't use on_setattr." + raise ValueError(msg) + + needs_cached_setattr = True + elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP: + needs_cached_setattr = True + + script, globs, annotations = _attrs_to_init_script( + filtered_attrs, + frozen, + slots, + pre_init, + pre_init_has_args, + post_init, + cache_hash, + base_attr_map, + is_exc, + needs_cached_setattr, + has_cls_on_setattr, + "__attrs_init__" if attrs_init else "__init__", + ) + if cls.__module__ in sys.modules: + # This makes typing.get_type_hints(CLS.__init__) resolve string types. + globs.update(sys.modules[cls.__module__].__dict__) + + globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict}) + + if needs_cached_setattr: + # Save the lookup overhead in __init__ if we need to circumvent + # setattr hooks. + globs["_cached_setattr_get"] = _OBJ_SETATTR.__get__ + + return script, globs, annotations + + +def _setattr(attr_name: str, value_var: str, has_on_setattr: bool) -> str: + """ + Use the cached object.setattr to set *attr_name* to *value_var*. + """ + return f"_setattr('{attr_name}', {value_var})" + + +def _setattr_with_converter( + attr_name: str, value_var: str, has_on_setattr: bool, converter: Converter +) -> str: + """ + Use the cached object.setattr to set *attr_name* to *value_var*, but run + its converter first. + """ + return f"_setattr('{attr_name}', {converter._fmt_converter_call(attr_name, value_var)})" + + +def _assign(attr_name: str, value: str, has_on_setattr: bool) -> str: + """ + Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise + relegate to _setattr. + """ + if has_on_setattr: + return _setattr(attr_name, value, True) + + return f"self.{attr_name} = {value}" + + +def _assign_with_converter( + attr_name: str, value_var: str, has_on_setattr: bool, converter: Converter +) -> str: + """ + Unless *attr_name* has an on_setattr hook, use normal assignment after + conversion. Otherwise relegate to _setattr_with_converter. + """ + if has_on_setattr: + return _setattr_with_converter(attr_name, value_var, True, converter) + + return f"self.{attr_name} = {converter._fmt_converter_call(attr_name, value_var)}" + + +def _determine_setters( + frozen: bool, slots: bool, base_attr_map: dict[str, type] +): + """ + Determine the correct setter functions based on whether a class is frozen + and/or slotted. + """ + if frozen is True: + if slots is True: + return (), _setattr, _setattr_with_converter + + # Dict frozen classes assign directly to __dict__. + # But only if the attribute doesn't come from an ancestor slot + # class. + # Note _inst_dict will be used again below if cache_hash is True + + def fmt_setter( + attr_name: str, value_var: str, has_on_setattr: bool + ) -> str: + if _is_slot_attr(attr_name, base_attr_map): + return _setattr(attr_name, value_var, has_on_setattr) + + return f"_inst_dict['{attr_name}'] = {value_var}" + + def fmt_setter_with_converter( + attr_name: str, + value_var: str, + has_on_setattr: bool, + converter: Converter, + ) -> str: + if has_on_setattr or _is_slot_attr(attr_name, base_attr_map): + return _setattr_with_converter( + attr_name, value_var, has_on_setattr, converter + ) + + return f"_inst_dict['{attr_name}'] = {converter._fmt_converter_call(attr_name, value_var)}" + + return ( + ("_inst_dict = self.__dict__",), + fmt_setter, + fmt_setter_with_converter, + ) + + # Not frozen -- we can just assign directly. + return (), _assign, _assign_with_converter + + +def _attrs_to_init_script( + attrs: list[Attribute], + is_frozen: bool, + is_slotted: bool, + call_pre_init: bool, + pre_init_has_args: bool, + call_post_init: bool, + does_cache_hash: bool, + base_attr_map: dict[str, type], + is_exc: bool, + needs_cached_setattr: bool, + has_cls_on_setattr: bool, + method_name: str, +) -> tuple[str, dict, dict]: + """ + Return a script of an initializer for *attrs*, a dict of globals, and + annotations for the initializer. + + The globals are required by the generated script. + """ + lines = ["self.__attrs_pre_init__()"] if call_pre_init else [] + + if needs_cached_setattr: + lines.append( + # Circumvent the __setattr__ descriptor to save one lookup per + # assignment. Note _setattr will be used again below if + # does_cache_hash is True. + "_setattr = _cached_setattr_get(self)" + ) + + extra_lines, fmt_setter, fmt_setter_with_converter = _determine_setters( + is_frozen, is_slotted, base_attr_map + ) + lines.extend(extra_lines) + + args = [] # Parameters in the definition of __init__ + pre_init_args = [] # Parameters in the call to __attrs_pre_init__ + kw_only_args = [] # Used for both 'args' and 'pre_init_args' above + attrs_to_validate = [] + + # This is a dictionary of names to validator and converter callables. + # Injecting this into __init__ globals lets us avoid lookups. + names_for_globals = {} + annotations = {"return": None} + + for a in attrs: + if a.validator: + attrs_to_validate.append(a) + + attr_name = a.name + has_on_setattr = a.on_setattr is not None or ( + a.on_setattr is not setters.NO_OP and has_cls_on_setattr + ) + # a.alias is set to maybe-mangled attr_name in _ClassBuilder if not + # explicitly provided + arg_name = a.alias + + has_factory = isinstance(a.default, Factory) + maybe_self = "self" if has_factory and a.default.takes_self else "" + + if a.converter is not None and not isinstance(a.converter, Converter): + converter = Converter(a.converter) + else: + converter = a.converter + + if a.init is False: + if has_factory: + init_factory_name = _INIT_FACTORY_PAT % (a.name,) + if converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + init_factory_name + f"({maybe_self})", + has_on_setattr, + converter, + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append( + fmt_setter( + attr_name, + init_factory_name + f"({maybe_self})", + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + elif converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + f"attr_dict['{attr_name}'].default", + has_on_setattr, + converter, + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append( + fmt_setter( + attr_name, + f"attr_dict['{attr_name}'].default", + has_on_setattr, + ) + ) + elif a.default is not NOTHING and not has_factory: + arg = f"{arg_name}=attr_dict['{attr_name}'].default" + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + pre_init_args.append(arg_name) + + if converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr, converter + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + elif has_factory: + arg = f"{arg_name}=NOTHING" + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + pre_init_args.append(arg_name) + lines.append(f"if {arg_name} is not NOTHING:") + + init_factory_name = _INIT_FACTORY_PAT % (a.name,) + if converter is not None: + lines.append( + " " + + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr, converter + ) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter_with_converter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + converter, + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append( + " " + fmt_setter(attr_name, arg_name, has_on_setattr) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + else: + if a.kw_only: + kw_only_args.append(arg_name) + else: + args.append(arg_name) + pre_init_args.append(arg_name) + + if converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr, converter + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + if a.init is True: + if a.type is not None and converter is None: + annotations[arg_name] = a.type + elif converter is not None and converter._first_param_type: + # Use the type from the converter if present. + annotations[arg_name] = converter._first_param_type + + if attrs_to_validate: # we can skip this if there are no validators. + names_for_globals["_config"] = _config + lines.append("if _config._run_validators is True:") + for a in attrs_to_validate: + val_name = "__attr_validator_" + a.name + attr_name = "__attr_" + a.name + lines.append(f" {val_name}(self, {attr_name}, self.{a.name})") + names_for_globals[val_name] = a.validator + names_for_globals[attr_name] = a + + if call_post_init: + lines.append("self.__attrs_post_init__()") + + # Because this is set only after __attrs_post_init__ is called, a crash + # will result if post-init tries to access the hash code. This seemed + # preferable to setting this beforehand, in which case alteration to field + # values during post-init combined with post-init accessing the hash code + # would result in silent bugs. + if does_cache_hash: + if is_frozen: + if is_slotted: + init_hash_cache = f"_setattr('{_HASH_CACHE_FIELD}', None)" + else: + init_hash_cache = f"_inst_dict['{_HASH_CACHE_FIELD}'] = None" + else: + init_hash_cache = f"self.{_HASH_CACHE_FIELD} = None" + lines.append(init_hash_cache) + + # For exceptions we rely on BaseException.__init__ for proper + # initialization. + if is_exc: + vals = ",".join(f"self.{a.name}" for a in attrs if a.init) + + lines.append(f"BaseException.__init__(self, {vals})") + + args = ", ".join(args) + pre_init_args = ", ".join(pre_init_args) + if kw_only_args: + # leading comma & kw_only args + args += f"{', ' if args else ''}*, {', '.join(kw_only_args)}" + pre_init_kw_only_args = ", ".join( + [ + f"{kw_arg_name}={kw_arg_name}" + # We need to remove the defaults from the kw_only_args. + for kw_arg_name in (kwa.split("=")[0] for kwa in kw_only_args) + ] + ) + pre_init_args += ", " if pre_init_args else "" + pre_init_args += pre_init_kw_only_args + + if call_pre_init and pre_init_has_args: + # If pre init method has arguments, pass the values given to __init__. + lines[0] = f"self.__attrs_pre_init__({pre_init_args})" + + # Python <3.12 doesn't allow backslashes in f-strings. + NL = "\n " + return ( + f"""def {method_name}(self, {args}): + {NL.join(lines) if lines else "pass"} +""", + names_for_globals, + annotations, + ) + + +def _default_init_alias_for(name: str) -> str: + """ + The default __init__ parameter name for a field. + + This performs private-name adjustment via leading-unscore stripping, + and is the default value of Attribute.alias if not provided. + """ + + return name.lstrip("_") + + +class Attribute: + """ + *Read-only* representation of an attribute. + + .. warning:: + + You should never instantiate this class yourself. + + The class has *all* arguments of `attr.ib` (except for ``factory`` which is + only syntactic sugar for ``default=Factory(...)`` plus the following: + + - ``name`` (`str`): The name of the attribute. + - ``alias`` (`str`): The __init__ parameter name of the attribute, after + any explicit overrides and default private-attribute-name handling. + - ``alias_is_default`` (`bool`): Whether the ``alias`` was automatically + generated (``True``) or explicitly provided by the user (``False``). + - ``inherited`` (`bool`): Whether or not that attribute has been inherited + from a base class. + - ``eq_key`` and ``order_key`` (`typing.Callable` or `None`): The + callables that are used for comparing and ordering objects by this + attribute, respectively. These are set by passing a callable to + `attr.ib`'s ``eq``, ``order``, or ``cmp`` arguments. See also + :ref:`comparison customization `. + + Instances of this class are frequently used for introspection purposes + like: + + - `fields` returns a tuple of them. + - Validators get them passed as the first argument. + - The :ref:`field transformer ` hook receives a list of + them. + - The ``alias`` property exposes the __init__ parameter name of the field, + with any overrides and default private-attribute handling applied. + + + .. versionadded:: 20.1.0 *inherited* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.2.0 *inherited* is not taken into account for + equality checks and hashing anymore. + .. versionadded:: 21.1.0 *eq_key* and *order_key* + .. versionadded:: 22.2.0 *alias* + .. versionadded:: 26.1.0 *alias_is_default* + + For the full version history of the fields, see `attr.ib`. + """ + + # These slots must NOT be reordered because we use them later for + # instantiation. + __slots__ = ( # noqa: RUF023 + "name", + "default", + "validator", + "repr", + "eq", + "eq_key", + "order", + "order_key", + "hash", + "init", + "metadata", + "type", + "converter", + "kw_only", + "inherited", + "on_setattr", + "alias", + "alias_is_default", + ) + + def __init__( + self, + name, + default, + validator, + repr, + cmp, # XXX: unused, remove along with other cmp code. + hash, + init, + inherited, + metadata=None, + type=None, + converter=None, + kw_only=False, + eq=None, + eq_key=None, + order=None, + order_key=None, + on_setattr=None, + alias=None, + alias_is_default=None, + ): + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq_key or eq, order_key or order, True + ) + + # Cache this descriptor here to speed things up later. + bound_setattr = _OBJ_SETATTR.__get__(self) + + # Despite the big red warning, people *do* instantiate `Attribute` + # themselves. + bound_setattr("name", name) + bound_setattr("default", default) + bound_setattr("validator", validator) + bound_setattr("repr", repr) + bound_setattr("eq", eq) + bound_setattr("eq_key", eq_key) + bound_setattr("order", order) + bound_setattr("order_key", order_key) + bound_setattr("hash", hash) + bound_setattr("init", init) + bound_setattr("converter", converter) + bound_setattr( + "metadata", + ( + types.MappingProxyType(dict(metadata)) # Shallow copy + if metadata + else _EMPTY_METADATA_SINGLETON + ), + ) + bound_setattr("type", type) + bound_setattr("kw_only", kw_only) + bound_setattr("inherited", inherited) + bound_setattr("on_setattr", on_setattr) + bound_setattr("alias", alias) + bound_setattr( + "alias_is_default", + alias is None if alias_is_default is None else alias_is_default, + ) + + def __setattr__(self, name, value): + raise FrozenInstanceError + + @classmethod + def from_counting_attr( + cls, name: str, ca: _CountingAttr, kw_only: bool, type=None + ): + # The 'kw_only' argument is the class-level setting, and is used if the + # attribute itself does not explicitly set 'kw_only'. + # type holds the annotated value. deal with conflicts: + if type is None: + type = ca.type + elif ca.type is not None: + msg = f"Type annotation and type argument cannot both be present for '{name}'." + raise ValueError(msg) + return cls( + name, + ca._default, + ca._validator, + ca.repr, + None, + ca.hash, + ca.init, + False, + ca.metadata, + type, + ca.converter, + kw_only if ca.kw_only is None else ca.kw_only, + ca.eq, + ca.eq_key, + ca.order, + ca.order_key, + ca.on_setattr, + ca.alias, + ca.alias is None, + ) + + # Don't use attrs.evolve since fields(Attribute) doesn't work + def evolve(self, **changes): + """ + Copy *self* and apply *changes*. + + This works similarly to `attrs.evolve` but that function does not work + with :class:`attrs.Attribute`. + + It is mainly meant to be used for `transform-fields`. + + .. versionadded:: 20.3.0 + """ + new = copy.copy(self) + + new._setattrs(changes.items()) + + if "alias" in changes and "alias_is_default" not in changes: + # Explicit alias provided -- no longer the default. + _OBJ_SETATTR.__get__(new)("alias_is_default", False) + elif ( + "name" in changes + and "alias" not in changes + # Don't auto-generate alias if the user picked picked the old one. + and self.alias_is_default + ): + # Name changed, alias was auto-generated -- update it. + _OBJ_SETATTR.__get__(new)( + "alias", _default_init_alias_for(new.name) + ) + + return new + + # Don't use _add_pickle since fields(Attribute) doesn't work + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple( + getattr(self, name) if name != "metadata" else dict(self.metadata) + for name in self.__slots__ + ) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + if len(state) < len(self.__slots__): + # Pre-26.1.0 pickle without alias_is_default -- infer it + # heuristically. + state_dict = dict(zip(self.__slots__, state)) + alias_is_default = state_dict.get( + "alias" + ) is None or state_dict.get("alias") == _default_init_alias_for( + state_dict["name"] + ) + state = (*state, alias_is_default) + + self._setattrs(zip(self.__slots__, state)) + + def _setattrs(self, name_values_pairs): + bound_setattr = _OBJ_SETATTR.__get__(self) + for name, value in name_values_pairs: + if name != "metadata": + bound_setattr(name, value) + else: + bound_setattr( + name, + ( + types.MappingProxyType(dict(value)) + if value + else _EMPTY_METADATA_SINGLETON + ), + ) + + +_a = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=(name != "alias_is_default"), + cmp=None, + eq=True, + order=False, + hash=(name != "metadata"), + init=True, + inherited=False, + alias=_default_init_alias_for(name), + ) + for name in Attribute.__slots__ +] + +Attribute = _add_hash( + _add_eq( + _add_repr(Attribute, attrs=_a), + attrs=[a for a in _a if a.name != "inherited"], + ), + attrs=[a for a in _a if a.hash and a.name != "inherited"], +) + + +class _CountingAttr: + """ + Intermediate representation of attributes that uses a counter to preserve + the order in which the attributes have been defined. + + *Internal* data structure of the attrs library. Running into is most + likely the result of a bug like a forgotten `@attr.s` decorator. + """ + + __slots__ = ( + "_default", + "_validator", + "alias", + "converter", + "counter", + "eq", + "eq_key", + "hash", + "init", + "kw_only", + "metadata", + "on_setattr", + "order", + "order_key", + "repr", + "type", + ) + __attrs_attrs__ = ( + *tuple( + Attribute( + name=name, + alias=_default_init_alias_for(name), + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=True, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ) + for name in ( + "counter", + "_default", + "repr", + "eq", + "order", + "hash", + "init", + "on_setattr", + "alias", + ) + ), + Attribute( + name="metadata", + alias="metadata", + default=None, + validator=None, + repr=True, + cmp=None, + hash=False, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ), + ) + cls_counter = 0 + + def __init__( + self, + default, + validator, + repr, + cmp, + hash, + init, + converter, + metadata, + type, + kw_only, + eq, + eq_key, + order, + order_key, + on_setattr, + alias, + ): + _CountingAttr.cls_counter += 1 + self.counter = _CountingAttr.cls_counter + self._default = default + self._validator = validator + self.converter = converter + self.repr = repr + self.eq = eq + self.eq_key = eq_key + self.order = order + self.order_key = order_key + self.hash = hash + self.init = init + self.metadata = metadata + self.type = type + self.kw_only = kw_only + self.on_setattr = on_setattr + self.alias = alias + + def validator(self, meth): + """ + Decorator that adds *meth* to the list of validators. + + Returns *meth* unchanged. + + .. versionadded:: 17.1.0 + """ + if self._validator is None: + self._validator = meth + else: + self._validator = and_(self._validator, meth) + return meth + + def default(self, meth): + """ + Decorator that allows to set the default for an attribute. + + Returns *meth* unchanged. + + Raises: + DefaultAlreadySetError: If default has been set before. + + .. versionadded:: 17.1.0 + """ + if self._default is not NOTHING: + raise DefaultAlreadySetError + + self._default = Factory(meth, takes_self=True) + + return meth + + +_CountingAttr = _add_eq(_add_repr(_CountingAttr)) + + +class ClassProps: + """ + Effective class properties as derived from parameters to `attr.s()` or + `define()` decorators. + + This is the same data structure that *attrs* uses internally to decide how + to construct the final class. + + Warning: + + This feature is currently **experimental** and is not covered by our + strict backwards-compatibility guarantees. + + + Attributes: + is_exception (bool): + Whether the class is treated as an exception class. + + is_slotted (bool): + Whether the class is `slotted `. + + has_weakref_slot (bool): + Whether the class has a slot for weak references. + + is_frozen (bool): + Whether the class is frozen. + + kw_only (KeywordOnly): + Whether / how the class enforces keyword-only arguments on the + ``__init__`` method. + + collected_fields_by_mro (bool): + Whether the class fields were collected by method resolution order. + That is, correctly but unlike `dataclasses`. + + added_init (bool): + Whether the class has an *attrs*-generated ``__init__`` method. + + added_repr (bool): + Whether the class has an *attrs*-generated ``__repr__`` method. + + added_eq (bool): + Whether the class has *attrs*-generated equality methods. + + added_ordering (bool): + Whether the class has *attrs*-generated ordering methods. + + hashability (Hashability): How `hashable ` the class is. + + added_match_args (bool): + Whether the class supports positional `match ` over its + fields. + + added_str (bool): + Whether the class has an *attrs*-generated ``__str__`` method. + + added_pickling (bool): + Whether the class has *attrs*-generated ``__getstate__`` and + ``__setstate__`` methods for `pickle`. + + on_setattr_hook (Callable[[Any, Attribute[Any], Any], Any] | None): + The class's ``__setattr__`` hook. + + field_transformer (Callable[[Attribute[Any]], Attribute[Any]] | None): + The class's `field transformers `. + + .. versionadded:: 25.4.0 + """ + + class Hashability(enum.Enum): + """ + The hashability of a class. + + .. versionadded:: 25.4.0 + """ + + HASHABLE = "hashable" + """Write a ``__hash__``.""" + HASHABLE_CACHED = "hashable_cache" + """Write a ``__hash__`` and cache the hash.""" + UNHASHABLE = "unhashable" + """Set ``__hash__`` to ``None``.""" + LEAVE_ALONE = "leave_alone" + """Don't touch ``__hash__``.""" + + class KeywordOnly(enum.Enum): + """ + How attributes should be treated regarding keyword-only parameters. + + .. versionadded:: 25.4.0 + """ + + NO = "no" + """Attributes are not keyword-only.""" + YES = "yes" + """Attributes in current class without kw_only=False are keyword-only.""" + FORCE = "force" + """All attributes are keyword-only.""" + + __slots__ = ( # noqa: RUF023 -- order matters for __init__ + "is_exception", + "is_slotted", + "has_weakref_slot", + "is_frozen", + "kw_only", + "collected_fields_by_mro", + "added_init", + "added_repr", + "added_eq", + "added_ordering", + "hashability", + "added_match_args", + "added_str", + "added_pickling", + "on_setattr_hook", + "field_transformer", + ) + + def __init__( + self, + is_exception, + is_slotted, + has_weakref_slot, + is_frozen, + kw_only, + collected_fields_by_mro, + added_init, + added_repr, + added_eq, + added_ordering, + hashability, + added_match_args, + added_str, + added_pickling, + on_setattr_hook, + field_transformer, + ): + self.is_exception = is_exception + self.is_slotted = is_slotted + self.has_weakref_slot = has_weakref_slot + self.is_frozen = is_frozen + self.kw_only = kw_only + self.collected_fields_by_mro = collected_fields_by_mro + self.added_init = added_init + self.added_repr = added_repr + self.added_eq = added_eq + self.added_ordering = added_ordering + self.hashability = hashability + self.added_match_args = added_match_args + self.added_str = added_str + self.added_pickling = added_pickling + self.on_setattr_hook = on_setattr_hook + self.field_transformer = field_transformer + + @property + def is_hashable(self): + return ( + self.hashability is ClassProps.Hashability.HASHABLE + or self.hashability is ClassProps.Hashability.HASHABLE_CACHED + ) + + +_cas = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + alias=_default_init_alias_for(name), + ) + for name in ClassProps.__slots__ +] + +ClassProps = _add_eq(_add_repr(ClassProps, attrs=_cas), attrs=_cas) + + +class Factory: + """ + Stores a factory callable. + + If passed as the default value to `attrs.field`, the factory is used to + generate a new value. + + Args: + factory (typing.Callable): + A callable that takes either none or exactly one mandatory + positional argument depending on *takes_self*. + + takes_self (bool): + Pass the partially initialized instance that is being initialized + as a positional argument. + + .. versionadded:: 17.1.0 *takes_self* + """ + + __slots__ = ("factory", "takes_self") + + def __init__(self, factory, takes_self=False): + self.factory = factory + self.takes_self = takes_self + + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple(getattr(self, name) for name in self.__slots__) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + for name, value in zip(self.__slots__, state): + setattr(self, name, value) + + +_f = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + ) + for name in Factory.__slots__ +] + +Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f) + + +class Converter: + """ + Stores a converter callable. + + Allows for the wrapped converter to take additional arguments. The + arguments are passed in the order they are documented. + + Args: + converter (Callable): A callable that converts the passed value. + + takes_self (bool): + Pass the partially initialized instance that is being initialized + as a positional argument. (default: `False`) + + takes_field (bool): + Pass the field definition (an :class:`Attribute`) into the + converter as a positional argument. (default: `False`) + + .. versionadded:: 24.1.0 + """ + + __slots__ = ( + "__call__", + "_first_param_type", + "_global_name", + "converter", + "takes_field", + "takes_self", + ) + + def __init__(self, converter, *, takes_self=False, takes_field=False): + self.converter = converter + self.takes_self = takes_self + self.takes_field = takes_field + + ex = _AnnotationExtractor(converter) + self._first_param_type = ex.get_first_param_type() + + if not (self.takes_self or self.takes_field): + self.__call__ = lambda value, _, __: self.converter(value) + elif self.takes_self and not self.takes_field: + self.__call__ = lambda value, instance, __: self.converter( + value, instance + ) + elif not self.takes_self and self.takes_field: + self.__call__ = lambda value, __, field: self.converter( + value, field + ) + else: + self.__call__ = self.converter + + rt = ex.get_return_type() + if rt is not None: + self.__call__.__annotations__["return"] = rt + + @staticmethod + def _get_global_name(attr_name: str) -> str: + """ + Return the name that a converter for an attribute name *attr_name* + would have. + """ + return f"__attr_converter_{attr_name}" + + def _fmt_converter_call(self, attr_name: str, value_var: str) -> str: + """ + Return a string that calls the converter for an attribute name + *attr_name* and the value in variable named *value_var* according to + `self.takes_self` and `self.takes_field`. + """ + if not (self.takes_self or self.takes_field): + return f"{self._get_global_name(attr_name)}({value_var})" + + if self.takes_self and self.takes_field: + return f"{self._get_global_name(attr_name)}({value_var}, self, attr_dict['{attr_name}'])" + + if self.takes_self: + return f"{self._get_global_name(attr_name)}({value_var}, self)" + + return f"{self._get_global_name(attr_name)}({value_var}, attr_dict['{attr_name}'])" + + def __getstate__(self): + """ + Return a dict containing only converter and takes_self -- the rest gets + computed when loading. + """ + return { + "converter": self.converter, + "takes_self": self.takes_self, + "takes_field": self.takes_field, + } + + def __setstate__(self, state): + """ + Load instance from state. + """ + self.__init__(**state) + + +_f = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + ) + for name in ("converter", "takes_self", "takes_field") +] + +Converter = _add_hash( + _add_eq(_add_repr(Converter, attrs=_f), attrs=_f), attrs=_f +) + + +def make_class( + name, attrs, bases=(object,), class_body=None, **attributes_arguments +): + r""" + A quick way to create a new class called *name* with *attrs*. + + .. note:: + + ``make_class()`` is a thin wrapper around `attr.s`, not `attrs.define` + which means that it doesn't come with some of the improved defaults. + + For example, if you want the same ``on_setattr`` behavior as in + `attrs.define`, you have to pass the hooks yourself: ``make_class(..., + on_setattr=setters.pipe(setters.convert, setters.validate)`` + + .. warning:: + + It is *your* duty to ensure that the class name and the attribute names + are valid identifiers. ``make_class()`` will *not* validate them for + you. + + Args: + name (str): The name for the new class. + + attrs (list | dict): + A list of names or a dictionary of mappings of names to `attr.ib`\ + s / `attrs.field`\ s. + + The order is deduced from the order of the names or attributes + inside *attrs*. Otherwise the order of the definition of the + attributes is used. + + bases (tuple[type, ...]): Classes that the new class will subclass. + + class_body (dict): + An optional dictionary of class attributes for the new class. + + attributes_arguments: Passed unmodified to `attr.s`. + + Returns: + type: A new class with *attrs*. + + .. versionadded:: 17.1.0 *bases* + .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained. + .. versionchanged:: 23.2.0 *class_body* + .. versionchanged:: 25.2.0 Class names can now be unicode. + """ + # Class identifiers are converted into the normal form NFKC while parsing + name = unicodedata.normalize("NFKC", name) + + if isinstance(attrs, dict): + cls_dict = attrs + elif isinstance(attrs, (list, tuple)): + cls_dict = {a: attrib() for a in attrs} + else: + msg = "attrs argument must be a dict or a list." + raise TypeError(msg) + + pre_init = cls_dict.pop("__attrs_pre_init__", None) + post_init = cls_dict.pop("__attrs_post_init__", None) + user_init = cls_dict.pop("__init__", None) + + body = {} + if class_body is not None: + body.update(class_body) + if pre_init is not None: + body["__attrs_pre_init__"] = pre_init + if post_init is not None: + body["__attrs_post_init__"] = post_init + if user_init is not None: + body["__init__"] = user_init + + type_ = types.new_class(name, bases, {}, lambda ns: ns.update(body)) + + # For pickling to work, the __module__ variable needs to be set to the + # frame where the class is created. Bypass this step in environments where + # sys._getframe is not defined (Jython for example) or sys._getframe is not + # defined for arguments greater than 0 (IronPython). + with contextlib.suppress(AttributeError, ValueError): + type_.__module__ = sys._getframe(1).f_globals.get( + "__name__", "__main__" + ) + + # We do it here for proper warnings with meaningful stacklevel. + cmp = attributes_arguments.pop("cmp", None) + ( + attributes_arguments["eq"], + attributes_arguments["order"], + ) = _determine_attrs_eq_order( + cmp, + attributes_arguments.get("eq"), + attributes_arguments.get("order"), + True, + ) + + cls = _attrs(these=cls_dict, **attributes_arguments)(type_) + # Only add type annotations now or "_attrs()" will complain: + cls.__annotations__ = { + k: v.type for k, v in cls_dict.items() if v.type is not None + } + return cls + + +# These are required by within this module so we define them here and merely +# import into .validators / .converters. + + +@attrs(slots=True, unsafe_hash=True) +class _AndValidator: + """ + Compose many validators to a single one. + """ + + _validators = attrib() + + def __call__(self, inst, attr, value): + for v in self._validators: + v(inst, attr, value) + + +def and_(*validators): + """ + A validator that composes multiple validators into one. + + When called on a value, it runs all wrapped validators. + + Args: + validators (~collections.abc.Iterable[typing.Callable]): + Arbitrary number of validators. + + .. versionadded:: 17.1.0 + """ + vals = [] + for validator in validators: + vals.extend( + validator._validators + if isinstance(validator, _AndValidator) + else [validator] + ) + + return _AndValidator(tuple(vals)) + + +def pipe(*converters): + """ + A converter that composes multiple converters into one. + + When called on a value, it runs all wrapped converters, returning the + *last* value. + + Type annotations will be inferred from the wrapped converters', if they + have any. + + converters (~collections.abc.Iterable[typing.Callable]): + Arbitrary number of converters. + + .. versionadded:: 20.1.0 + """ + + return_instance = any(isinstance(c, Converter) for c in converters) + + if return_instance: + + def pipe_converter(val, inst, field): + for c in converters: + val = ( + c(val, inst, field) if isinstance(c, Converter) else c(val) + ) + + return val + + else: + + def pipe_converter(val): + for c in converters: + val = c(val) + + return val + + if not converters: + # If the converter list is empty, pipe_converter is the identity. + A = TypeVar("A") + pipe_converter.__annotations__.update({"val": A, "return": A}) + else: + # Get parameter type from first converter. + t = _AnnotationExtractor(converters[0]).get_first_param_type() + if t: + pipe_converter.__annotations__["val"] = t + + last = converters[-1] + if not PY_3_11_PLUS and isinstance(last, Converter): + last = last.__call__ + + # Get return type from last converter. + rt = _AnnotationExtractor(last).get_return_type() + if rt: + pipe_converter.__annotations__["return"] = rt + + if return_instance: + return Converter(pipe_converter, takes_self=True, takes_field=True) + return pipe_converter diff --git a/venv/lib/python3.12/site-packages/attr/_next_gen.py b/venv/lib/python3.12/site-packages/attr/_next_gen.py new file mode 100644 index 0000000..4ccd0da --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_next_gen.py @@ -0,0 +1,674 @@ +# SPDX-License-Identifier: MIT + +""" +These are keyword-only APIs that call `attr.s` and `attr.ib` with different +default values. +""" + +from functools import partial + +from . import setters +from ._funcs import asdict as _asdict +from ._funcs import astuple as _astuple +from ._make import ( + _DEFAULT_ON_SETATTR, + NOTHING, + _frozen_setattrs, + attrib, + attrs, +) +from .exceptions import NotAnAttrsClassError, UnannotatedAttributeError + + +def define( + maybe_cls=None, + *, + these=None, + repr=None, + unsafe_hash=None, + hash=None, + init=None, + slots=True, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=None, + kw_only=False, + cache_hash=False, + auto_exc=True, + eq=None, + order=False, + auto_detect=True, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, + match_args=True, + force_kw_only=False, +): + r""" + A class decorator that adds :term:`dunder methods` according to + :term:`fields ` specified using :doc:`type annotations `, + `field()` calls, or the *these* argument. + + Since *attrs* patches or replaces an existing class, you cannot use + `object.__init_subclass__` with *attrs* classes, because it runs too early. + As a replacement, you can define ``__attrs_init_subclass__`` on your class. + It will be called by *attrs* classes that subclass it after they're + created. See also :ref:`init-subclass`. + + Args: + slots (bool): + Create a :term:`slotted class ` that's more + memory-efficient. Slotted classes are generally superior to the + default dict classes, but have some gotchas you should know about, + so we encourage you to read the :term:`glossary entry `. + + auto_detect (bool): + Instead of setting the *init*, *repr*, *eq*, and *hash* arguments + explicitly, assume they are set to True **unless any** of the + involved methods for one of the arguments is implemented in the + *current* class (meaning, it is *not* inherited from some base + class). + + So, for example by implementing ``__eq__`` on a class yourself, + *attrs* will deduce ``eq=False`` and will create *neither* + ``__eq__`` *nor* ``__ne__`` (but Python classes come with a + sensible ``__ne__`` by default, so it *should* be enough to only + implement ``__eq__`` in most cases). + + Passing :data:`True` or :data:`False` to *init*, *repr*, *eq*, or *hash* + overrides whatever *auto_detect* would determine. + + auto_exc (bool): + If the class subclasses `BaseException` (which implicitly includes + any subclass of any exception), the following happens to behave + like a well-behaved Python exception class: + + - the values for *eq*, *order*, and *hash* are ignored and the + instances compare and hash by the instance's ids [#]_ , + - all attributes that are either passed into ``__init__`` or have a + default value are additionally available as a tuple in the + ``args`` attribute, + - the value of *str* is ignored leaving ``__str__`` to base + classes. + + .. [#] + Note that *attrs* will *not* remove existing implementations of + ``__hash__`` or the equality methods. It just won't add own + ones. + + on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]): + A callable that is run whenever the user attempts to set an + attribute (either by assignment like ``i.x = 42`` or by using + `setattr` like ``setattr(i, "x", 42)``). It receives the same + arguments as validators: the instance, the attribute that is being + modified, and the new value. + + If no exception is raised, the attribute is set to the return value + of the callable. + + If a list of callables is passed, they're automatically wrapped in + an `attrs.setters.pipe`. + + If left None, the default behavior is to run converters and + validators whenever an attribute is set. + + init (bool): + Create a ``__init__`` method that initializes the *attrs* + attributes. Leading underscores are stripped for the argument name, + unless an alias is set on the attribute. + + .. seealso:: + `init` shows advanced ways to customize the generated + ``__init__`` method, including executing code before and after. + + repr(bool): + Create a ``__repr__`` method with a human readable representation + of *attrs* attributes. + + str (bool): + Create a ``__str__`` method that is identical to ``__repr__``. This + is usually not necessary except for `Exception`\ s. + + eq (bool | None): + If True or None (default), add ``__eq__`` and ``__ne__`` methods + that check two instances for equality. + + .. seealso:: + `comparison` describes how to customize the comparison behavior + going as far comparing NumPy arrays. + + order (bool | None): + If True, add ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` + methods that behave like *eq* above and allow instances to be + ordered. + + They compare the instances as if they were tuples of their *attrs* + attributes if and only if the types of both classes are + *identical*. + + If `None` mirror value of *eq*. + + .. seealso:: `comparison` + + unsafe_hash (bool | None): + If None (default), the ``__hash__`` method is generated according + how *eq* and *frozen* are set. + + 1. If *both* are True, *attrs* will generate a ``__hash__`` for + you. + 2. If *eq* is True and *frozen* is False, ``__hash__`` will be set + to None, marking it unhashable (which it is). + 3. If *eq* is False, ``__hash__`` will be left untouched meaning + the ``__hash__`` method of the base class will be used. If the + base class is `object`, this means it will fall back to id-based + hashing. + + Although not recommended, you can decide for yourself and force + *attrs* to create one (for example, if the class is immutable even + though you didn't freeze it programmatically) by passing True or + not. Both of these cases are rather special and should be used + carefully. + + .. seealso:: + + - Our documentation on `hashing`, + - Python's documentation on `object.__hash__`, + - and the `GitHub issue that led to the default \ behavior + `_ for more + details. + + hash (bool | None): + Deprecated alias for *unsafe_hash*. *unsafe_hash* takes precedence. + + cache_hash (bool): + Ensure that the object's hash code is computed only once and stored + on the object. If this is set to True, hashing must be either + explicitly or implicitly enabled for this class. If the hash code + is cached, avoid any reassignments of fields involved in hash code + computation or mutations of the objects those fields point to after + object creation. If such changes occur, the behavior of the + object's hash code is undefined. + + frozen (bool): + Make instances immutable after initialization. If someone attempts + to modify a frozen instance, `attrs.exceptions.FrozenInstanceError` + is raised. + + .. note:: + + 1. This is achieved by installing a custom ``__setattr__`` + method on your class, so you can't implement your own. + + 2. True immutability is impossible in Python. + + 3. This *does* have a minor a runtime performance `impact + ` when initializing new instances. In other + words: ``__init__`` is slightly slower with ``frozen=True``. + + 4. If a class is frozen, you cannot modify ``self`` in + ``__attrs_post_init__`` or a self-written ``__init__``. You + can circumvent that limitation by using + ``object.__setattr__(self, "attribute_name", value)``. + + 5. Subclasses of a frozen class are frozen too. + + kw_only (bool): + Make attributes keyword-only in the generated ``__init__`` (if + *init* is False, this parameter is ignored). Attributes that + explicitly set ``kw_only=False`` are not affected; base class + attributes are also not affected. + + Also see *force_kw_only*. + + weakref_slot (bool): + Make instances weak-referenceable. This has no effect unless + *slots* is True. + + field_transformer (~typing.Callable | None): + A function that is called with the original class object and all + fields right before *attrs* finalizes the class. You can use this, + for example, to automatically add converters or validators to + fields based on their types. + + .. seealso:: `transform-fields` + + match_args (bool): + If True (default), set ``__match_args__`` on the class to support + :pep:`634` (*Structural Pattern Matching*). It is a tuple of all + non-keyword-only ``__init__`` parameter names on Python 3.10 and + later. Ignored on older Python versions. + + collect_by_mro (bool): + If True, *attrs* collects attributes from base classes correctly + according to the `method resolution order + `_. If False, *attrs* + will mimic the (wrong) behavior of `dataclasses` and :pep:`681`. + + See also `issue #428 + `_. + + force_kw_only (bool): + A back-compat flag for restoring pre-25.4.0 behavior. If True and + ``kw_only=True``, all attributes are made keyword-only, including + base class attributes, and those set to ``kw_only=False`` at the + attribute level. Defaults to False. + + See also `issue #980 + `_. + + getstate_setstate (bool | None): + .. note:: + + This is usually only interesting for slotted classes and you + should probably just set *auto_detect* to True. + + If True, ``__getstate__`` and ``__setstate__`` are generated and + attached to the class. This is necessary for slotted classes to be + pickleable. If left None, it's True by default for slotted classes + and False for dict classes. + + If *auto_detect* is True, and *getstate_setstate* is left None, and + **either** ``__getstate__`` or ``__setstate__`` is detected + directly on the class (meaning: not inherited), it is set to False + (this is usually what you want). + + auto_attribs (bool | None): + If True, look at type annotations to determine which attributes to + use, like `dataclasses`. If False, it will only look for explicit + :func:`field` class attributes, like classic *attrs*. + + If left None, it will guess: + + 1. If any attributes are annotated and no unannotated + `attrs.field`\ s are found, it assumes *auto_attribs=True*. + 2. Otherwise it assumes *auto_attribs=False* and tries to collect + `attrs.field`\ s. + + If *attrs* decides to look at type annotations, **all** fields + **must** be annotated. If *attrs* encounters a field that is set to + a :func:`field` / `attr.ib` but lacks a type annotation, an + `attrs.exceptions.UnannotatedAttributeError` is raised. Use + ``field_name: typing.Any = field(...)`` if you don't want to set a + type. + + .. warning:: + + For features that use the attribute name to create decorators + (for example, :ref:`validators `), you still *must* + assign :func:`field` / `attr.ib` to them. Otherwise Python will + either not find the name or try to use the default value to + call, for example, ``validator`` on it. + + Attributes annotated as `typing.ClassVar`, and attributes that are + neither annotated nor set to an `field()` are **ignored**. + + these (dict[str, object]): + A dictionary of name to the (private) return value of `field()` + mappings. This is useful to avoid the definition of your attributes + within the class body because you can't (for example, if you want + to add ``__repr__`` methods to Django models) or don't want to. + + If *these* is not `None`, *attrs* will *not* search the class body + for attributes and will *not* remove any attributes from it. + + The order is deduced from the order of the attributes inside + *these*. + + Arguably, this is a rather obscure feature. + + .. versionadded:: 20.1.0 + .. versionchanged:: 21.3.0 Converters are also run ``on_setattr``. + .. versionadded:: 22.2.0 + *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance). + .. versionchanged:: 24.1.0 + Instances are not compared as tuples of attributes anymore, but using a + big ``and`` condition. This is faster and has more correct behavior for + uncomparable values like `math.nan`. + .. versionadded:: 24.1.0 + If a class has an *inherited* classmethod called + ``__attrs_init_subclass__``, it is executed after the class is created. + .. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*. + .. versionadded:: 24.3.0 + Unless already present, a ``__replace__`` method is automatically + created for `copy.replace` (Python 3.13+ only). + .. versionchanged:: 25.4.0 + *kw_only* now only applies to attributes defined in the current class, + and respects attribute-level ``kw_only=False`` settings. + .. versionadded:: 25.4.0 + Added *force_kw_only* to go back to the previous *kw_only* behavior. + + .. note:: + + The main differences to the classic `attr.s` are: + + - Automatically detect whether or not *auto_attribs* should be `True` + (c.f. *auto_attribs* parameter). + - Converters and validators run when attributes are set by default -- + if *frozen* is `False`. + - *slots=True* + + Usually, this has only upsides and few visible effects in everyday + programming. But it *can* lead to some surprising behaviors, so + please make sure to read :term:`slotted classes`. + + - *auto_exc=True* + - *auto_detect=True* + - *order=False* + - *force_kw_only=False* + - Some options that were only relevant on Python 2 or were kept around + for backwards-compatibility have been removed. + + """ + + def do_it(cls, auto_attribs): + return attrs( + maybe_cls=cls, + these=these, + repr=repr, + hash=hash, + unsafe_hash=unsafe_hash, + init=init, + slots=slots, + frozen=frozen, + weakref_slot=weakref_slot, + str=str, + auto_attribs=auto_attribs, + kw_only=kw_only, + cache_hash=cache_hash, + auto_exc=auto_exc, + eq=eq, + order=order, + auto_detect=auto_detect, + collect_by_mro=True, + getstate_setstate=getstate_setstate, + on_setattr=on_setattr, + field_transformer=field_transformer, + match_args=match_args, + force_kw_only=force_kw_only, + ) + + def wrap(cls): + """ + Making this a wrapper ensures this code runs during class creation. + + We also ensure that frozen-ness of classes is inherited. + """ + nonlocal frozen, on_setattr + + had_on_setattr = on_setattr not in (None, setters.NO_OP) + + # By default, mutable classes convert & validate on setattr. + if frozen is False and on_setattr is None: + on_setattr = _DEFAULT_ON_SETATTR + + # However, if we subclass a frozen class, we inherit the immutability + # and disable on_setattr. + for base_cls in cls.__bases__: + if base_cls.__setattr__ is _frozen_setattrs: + if had_on_setattr: + msg = "Frozen classes can't use on_setattr (frozen-ness was inherited)." + raise ValueError(msg) + + on_setattr = setters.NO_OP + break + + if auto_attribs is not None: + return do_it(cls, auto_attribs) + + try: + return do_it(cls, True) + except UnannotatedAttributeError: + return do_it(cls, False) + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but `None` if used as `@attrs()`. + if maybe_cls is None: + return wrap + + return wrap(maybe_cls) + + +mutable = define +frozen = partial(define, frozen=True, on_setattr=None) + + +def field( + *, + default=NOTHING, + validator=None, + repr=True, + hash=None, + init=True, + metadata=None, + type=None, + converter=None, + factory=None, + kw_only=None, + eq=None, + order=None, + on_setattr=None, + alias=None, +): + """ + Create a new :term:`field` / :term:`attribute` on a class. + + .. warning:: + + Does **nothing** unless the class is also decorated with + `attrs.define` (or similar)! + + Args: + default: + A value that is used if an *attrs*-generated ``__init__`` is used + and no value is passed while instantiating or the attribute is + excluded using ``init=False``. + + If the value is an instance of `attrs.Factory`, its callable will + be used to construct a new value (useful for mutable data types + like lists or dicts). + + If a default is not set (or set manually to `attrs.NOTHING`), a + value *must* be supplied when instantiating; otherwise a + `TypeError` will be raised. + + .. seealso:: `defaults` + + factory (~typing.Callable): + Syntactic sugar for ``default=attr.Factory(factory)``. + + validator (~typing.Callable | list[~typing.Callable]): + Callable that is called by *attrs*-generated ``__init__`` methods + after the instance has been initialized. They receive the + initialized instance, the :func:`~attrs.Attribute`, and the passed + value. + + The return value is *not* inspected so the validator has to throw + an exception itself. + + If a `list` is passed, its items are treated as validators and must + all pass. + + Validators can be globally disabled and re-enabled using + `attrs.validators.get_disabled` / `attrs.validators.set_disabled`. + + The validator can also be set using decorator notation as shown + below. + + .. seealso:: :ref:`validators` + + repr (bool | ~typing.Callable): + Include this attribute in the generated ``__repr__`` method. If + True, include the attribute; if False, omit it. By default, the + built-in ``repr()`` function is used. To override how the attribute + value is formatted, pass a ``callable`` that takes a single value + and returns a string. Note that the resulting string is used as-is, + which means it will be used directly *instead* of calling + ``repr()`` (the default). + + eq (bool | ~typing.Callable): + If True (default), include this attribute in the generated + ``__eq__`` and ``__ne__`` methods that check two instances for + equality. To override how the attribute value is compared, pass a + callable that takes a single value and returns the value to be + compared. + + .. seealso:: `comparison` + + order (bool | ~typing.Callable): + If True (default), include this attributes in the generated + ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. To + override how the attribute value is ordered, pass a callable that + takes a single value and returns the value to be ordered. + + .. seealso:: `comparison` + + hash (bool | None): + Include this attribute in the generated ``__hash__`` method. If + None (default), mirror *eq*'s value. This is the correct behavior + according the Python spec. Setting this value to anything else + than None is *discouraged*. + + .. seealso:: `hashing` + + init (bool): + Include this attribute in the generated ``__init__`` method. + + It is possible to set this to False and set a default value. In + that case this attributed is unconditionally initialized with the + specified default value or factory. + + .. seealso:: `init` + + converter (typing.Callable | Converter): + A callable that is called by *attrs*-generated ``__init__`` methods + to convert attribute's value to the desired format. + + If a vanilla callable is passed, it is given the passed-in value as + the only positional argument. It is possible to receive additional + arguments by wrapping the callable in a `Converter`. + + Either way, the returned value will be used as the new value of the + attribute. The value is converted before being passed to the + validator, if any. + + .. seealso:: :ref:`converters` + + metadata (dict | None): + An arbitrary mapping, to be used by third-party code. + + .. seealso:: `extending-metadata`. + + type (type): + The type of the attribute. Nowadays, the preferred method to + specify the type is using a variable annotation (see :pep:`526`). + This argument is provided for backwards-compatibility and for usage + with `make_class`. Regardless of the approach used, the type will + be stored on ``Attribute.type``. + + Please note that *attrs* doesn't do anything with this metadata by + itself. You can use it as part of your own code or for `static type + checking `. + + kw_only (bool | None): + Make this attribute keyword-only in the generated ``__init__`` (if + *init* is False, this parameter is ignored). If None (default), + mirror the setting from `attrs.define`. + + on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]): + Allows to overwrite the *on_setattr* setting from `attr.s`. If left + None, the *on_setattr* value from `attr.s` is used. Set to + `attrs.setters.NO_OP` to run **no** `setattr` hooks for this + attribute -- regardless of the setting in `define()`. + + alias (str | None): + Override this attribute's parameter name in the generated + ``__init__`` method. If left None, default to ``name`` stripped + of leading underscores. See `private-attributes`. + + .. versionadded:: 20.1.0 + .. versionchanged:: 21.1.0 + *eq*, *order*, and *cmp* also accept a custom callable + .. versionadded:: 22.2.0 *alias* + .. versionadded:: 23.1.0 + The *type* parameter has been re-added; mostly for `attrs.make_class`. + Please note that type checkers ignore this metadata. + .. versionchanged:: 25.4.0 + *kw_only* can now be None, and its default is also changed from False to + None. + + .. seealso:: + + `attr.ib` + """ + return attrib( + default=default, + validator=validator, + repr=repr, + hash=hash, + init=init, + metadata=metadata, + type=type, + converter=converter, + factory=factory, + kw_only=kw_only, + eq=eq, + order=order, + on_setattr=on_setattr, + alias=alias, + ) + + +def asdict(inst, *, recurse=True, filter=None, value_serializer=None): + """ + Same as `attr.asdict`, except that collections types are always retained + and dict is always used as *dict_factory*. + + .. versionadded:: 21.3.0 + """ + return _asdict( + inst=inst, + recurse=recurse, + filter=filter, + value_serializer=value_serializer, + retain_collection_types=True, + ) + + +def astuple(inst, *, recurse=True, filter=None): + """ + Same as `attr.astuple`, except that collections types are always retained + and `tuple` is always used as the *tuple_factory*. + + .. versionadded:: 21.3.0 + """ + return _astuple( + inst=inst, recurse=recurse, filter=filter, retain_collection_types=True + ) + + +def inspect(cls): + """ + Inspect the class and return its effective build parameters. + + Warning: + This feature is currently **experimental** and is not covered by our + strict backwards-compatibility guarantees. + + Args: + cls: The *attrs*-decorated class to inspect. + + Returns: + The effective build parameters of the class. + + Raises: + NotAnAttrsClassError: If the class is not an *attrs*-decorated class. + + .. versionadded:: 25.4.0 + """ + try: + return cls.__dict__["__attrs_props__"] + except KeyError: + msg = f"{cls!r} is not an attrs-decorated class." + raise NotAnAttrsClassError(msg) from None diff --git a/venv/lib/python3.12/site-packages/attr/_typing_compat.pyi b/venv/lib/python3.12/site-packages/attr/_typing_compat.pyi new file mode 100644 index 0000000..ca7b71e --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_typing_compat.pyi @@ -0,0 +1,15 @@ +from typing import Any, ClassVar, Protocol + +# MYPY is a special constant in mypy which works the same way as `TYPE_CHECKING`. +MYPY = False + +if MYPY: + # A protocol to be able to statically accept an attrs class. + class AttrsInstance_(Protocol): + __attrs_attrs__: ClassVar[Any] + +else: + # For type checkers without plug-in support use an empty protocol that + # will (hopefully) be combined into a union. + class AttrsInstance_(Protocol): + pass diff --git a/venv/lib/python3.12/site-packages/attr/_version_info.py b/venv/lib/python3.12/site-packages/attr/_version_info.py new file mode 100644 index 0000000..27f1888 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_version_info.py @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: MIT + + +from functools import total_ordering + +from ._funcs import astuple +from ._make import attrib, attrs + + +@total_ordering +@attrs(eq=False, order=False, slots=True, frozen=True) +class VersionInfo: + """ + A version object that can be compared to tuple of length 1--4: + + >>> attr.VersionInfo(19, 1, 0, "final") <= (19, 2) + True + >>> attr.VersionInfo(19, 1, 0, "final") < (19, 1, 1) + True + >>> vi = attr.VersionInfo(19, 2, 0, "final") + >>> vi < (19, 1, 1) + False + >>> vi < (19,) + False + >>> vi == (19, 2,) + True + >>> vi == (19, 2, 1) + False + + .. versionadded:: 19.2 + """ + + year = attrib(type=int) + minor = attrib(type=int) + micro = attrib(type=int) + releaselevel = attrib(type=str) + + @classmethod + def _from_version_string(cls, s): + """ + Parse *s* and return a _VersionInfo. + """ + v = s.split(".") + if len(v) == 3: + v.append("final") + + return cls( + year=int(v[0]), minor=int(v[1]), micro=int(v[2]), releaselevel=v[3] + ) + + def _ensure_tuple(self, other): + """ + Ensure *other* is a tuple of a valid length. + + Returns a possibly transformed *other* and ourselves as a tuple of + the same length as *other*. + """ + + if self.__class__ is other.__class__: + other = astuple(other) + + if not isinstance(other, tuple): + raise NotImplementedError + + if not (1 <= len(other) <= 4): + raise NotImplementedError + + return astuple(self)[: len(other)], other + + def __eq__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + return us == them + + def __lt__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + # Since alphabetically "dev0" < "final" < "post1" < "post2", we don't + # have to do anything special with releaselevel for now. + return us < them + + def __hash__(self): + return hash((self.year, self.minor, self.micro, self.releaselevel)) diff --git a/venv/lib/python3.12/site-packages/attr/_version_info.pyi b/venv/lib/python3.12/site-packages/attr/_version_info.pyi new file mode 100644 index 0000000..45ced08 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_version_info.pyi @@ -0,0 +1,9 @@ +class VersionInfo: + @property + def year(self) -> int: ... + @property + def minor(self) -> int: ... + @property + def micro(self) -> int: ... + @property + def releaselevel(self) -> str: ... diff --git a/venv/lib/python3.12/site-packages/attr/converters.py b/venv/lib/python3.12/site-packages/attr/converters.py new file mode 100644 index 0000000..0a79dee --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/converters.py @@ -0,0 +1,162 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful converters. +""" + +import typing + +from ._compat import _AnnotationExtractor +from ._make import NOTHING, Converter, Factory, pipe + + +__all__ = [ + "default_if_none", + "optional", + "pipe", + "to_bool", +] + + +def optional(converter): + """ + A converter that allows an attribute to be optional. An optional attribute + is one which can be set to `None`. + + Type annotations will be inferred from the wrapped converter's, if it has + any. + + Args: + converter (typing.Callable): + the converter that is used for non-`None` values. + + .. versionadded:: 17.1.0 + """ + + if isinstance(converter, Converter): + + def optional_converter(val, inst, field): + if val is None: + return None + return converter(val, inst, field) + + else: + + def optional_converter(val): + if val is None: + return None + return converter(val) + + xtr = _AnnotationExtractor(converter) + + t = xtr.get_first_param_type() + if t: + optional_converter.__annotations__["val"] = typing.Optional[t] + + rt = xtr.get_return_type() + if rt: + optional_converter.__annotations__["return"] = typing.Optional[rt] + + if isinstance(converter, Converter): + return Converter(optional_converter, takes_self=True, takes_field=True) + + return optional_converter + + +def default_if_none(default=NOTHING, factory=None): + """ + A converter that allows to replace `None` values by *default* or the result + of *factory*. + + Args: + default: + Value to be used if `None` is passed. Passing an instance of + `attrs.Factory` is supported, however the ``takes_self`` option is + *not*. + + factory (typing.Callable): + A callable that takes no parameters whose result is used if `None` + is passed. + + Raises: + TypeError: If **neither** *default* or *factory* is passed. + + TypeError: If **both** *default* and *factory* are passed. + + ValueError: + If an instance of `attrs.Factory` is passed with + ``takes_self=True``. + + .. versionadded:: 18.2.0 + """ + if default is NOTHING and factory is None: + msg = "Must pass either `default` or `factory`." + raise TypeError(msg) + + if default is not NOTHING and factory is not None: + msg = "Must pass either `default` or `factory` but not both." + raise TypeError(msg) + + if factory is not None: + default = Factory(factory) + + if isinstance(default, Factory): + if default.takes_self: + msg = "`takes_self` is not supported by default_if_none." + raise ValueError(msg) + + def default_if_none_converter(val): + if val is not None: + return val + + return default.factory() + + else: + + def default_if_none_converter(val): + if val is not None: + return val + + return default + + return default_if_none_converter + + +def to_bool(val): + """ + Convert "boolean" strings (for example, from environment variables) to real + booleans. + + Values mapping to `True`: + + - ``True`` + - ``"true"`` / ``"t"`` + - ``"yes"`` / ``"y"`` + - ``"on"`` + - ``"1"`` + - ``1`` + + Values mapping to `False`: + + - ``False`` + - ``"false"`` / ``"f"`` + - ``"no"`` / ``"n"`` + - ``"off"`` + - ``"0"`` + - ``0`` + + Raises: + ValueError: For any other value. + + .. versionadded:: 21.3.0 + """ + if isinstance(val, str): + val = val.lower() + + if val in (True, "true", "t", "yes", "y", "on", "1", 1): + return True + if val in (False, "false", "f", "no", "n", "off", "0", 0): + return False + + msg = f"Cannot convert value to bool: {val!r}" + raise ValueError(msg) diff --git a/venv/lib/python3.12/site-packages/attr/converters.pyi b/venv/lib/python3.12/site-packages/attr/converters.pyi new file mode 100644 index 0000000..12bd0c4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/converters.pyi @@ -0,0 +1,19 @@ +from typing import Callable, Any, overload + +from attrs import _ConverterType, _CallableConverterType + +@overload +def pipe(*validators: _CallableConverterType) -> _CallableConverterType: ... +@overload +def pipe(*validators: _ConverterType) -> _ConverterType: ... +@overload +def optional(converter: _CallableConverterType) -> _CallableConverterType: ... +@overload +def optional(converter: _ConverterType) -> _ConverterType: ... +@overload +def default_if_none(default: Any) -> _CallableConverterType: ... +@overload +def default_if_none( + *, factory: Callable[[], Any] +) -> _CallableConverterType: ... +def to_bool(val: str | int | bool) -> bool: ... diff --git a/venv/lib/python3.12/site-packages/attr/exceptions.py b/venv/lib/python3.12/site-packages/attr/exceptions.py new file mode 100644 index 0000000..a207df4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/exceptions.py @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: MIT + +from __future__ import annotations + + +class FrozenError(AttributeError): + """ + A frozen/immutable instance or attribute have been attempted to be + modified. + + It mirrors the behavior of ``namedtuples`` by using the same error message + and subclassing `AttributeError`. + + .. versionadded:: 20.1.0 + """ + + def __init__(self): + msg = "can't set attribute" + super().__init__(msg) + self.msg = msg + + +class FrozenInstanceError(FrozenError): + """ + A frozen instance has been attempted to be modified. + + .. versionadded:: 16.1.0 + """ + + +class FrozenAttributeError(FrozenError): + """ + A frozen attribute has been attempted to be modified. + + .. versionadded:: 20.1.0 + """ + + +class AttrsAttributeNotFoundError(ValueError): + """ + An *attrs* function couldn't find an attribute that the user asked for. + + .. versionadded:: 16.2.0 + """ + + +class NotAnAttrsClassError(ValueError): + """ + A non-*attrs* class has been passed into an *attrs* function. + + .. versionadded:: 16.2.0 + """ + + +class DefaultAlreadySetError(RuntimeError): + """ + A default has been set when defining the field and is attempted to be reset + using the decorator. + + .. versionadded:: 17.1.0 + """ + + +class UnannotatedAttributeError(RuntimeError): + """ + A class with ``auto_attribs=True`` has a field without a type annotation. + + .. versionadded:: 17.3.0 + """ + + +class PythonTooOldError(RuntimeError): + """ + It was attempted to use an *attrs* feature that requires a newer Python + version. + + .. versionadded:: 18.2.0 + """ + + +class NotCallableError(TypeError): + """ + A field requiring a callable has been set with a value that is not + callable. + + .. versionadded:: 19.2.0 + """ + + def __init__(self, msg, value): + super(TypeError, self).__init__(msg, value) + self.msg = msg + self.value = value + + def __str__(self): + return str(self.msg) diff --git a/venv/lib/python3.12/site-packages/attr/exceptions.pyi b/venv/lib/python3.12/site-packages/attr/exceptions.pyi new file mode 100644 index 0000000..f268011 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/exceptions.pyi @@ -0,0 +1,17 @@ +from typing import Any + +class FrozenError(AttributeError): + msg: str = ... + +class FrozenInstanceError(FrozenError): ... +class FrozenAttributeError(FrozenError): ... +class AttrsAttributeNotFoundError(ValueError): ... +class NotAnAttrsClassError(ValueError): ... +class DefaultAlreadySetError(RuntimeError): ... +class UnannotatedAttributeError(RuntimeError): ... +class PythonTooOldError(RuntimeError): ... + +class NotCallableError(TypeError): + msg: str = ... + value: Any = ... + def __init__(self, msg: str, value: Any) -> None: ... diff --git a/venv/lib/python3.12/site-packages/attr/filters.py b/venv/lib/python3.12/site-packages/attr/filters.py new file mode 100644 index 0000000..689b170 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/filters.py @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful filters for `attrs.asdict` and `attrs.astuple`. +""" + +from ._make import Attribute + + +def _split_what(what): + """ + Returns a tuple of `frozenset`s of classes and attributes. + """ + return ( + frozenset(cls for cls in what if isinstance(cls, type)), + frozenset(cls for cls in what if isinstance(cls, str)), + frozenset(cls for cls in what if isinstance(cls, Attribute)), + ) + + +def include(*what): + """ + Create a filter that only allows *what*. + + Args: + what (list[type, str, attrs.Attribute]): + What to include. Can be a type, a name, or an attribute. + + Returns: + Callable: + A callable that can be passed to `attrs.asdict`'s and + `attrs.astuple`'s *filter* argument. + + .. versionchanged:: 23.1.0 Accept strings with field names. + """ + cls, names, attrs = _split_what(what) + + def include_(attribute, value): + return ( + value.__class__ in cls + or attribute.name in names + or attribute in attrs + ) + + return include_ + + +def exclude(*what): + """ + Create a filter that does **not** allow *what*. + + Args: + what (list[type, str, attrs.Attribute]): + What to exclude. Can be a type, a name, or an attribute. + + Returns: + Callable: + A callable that can be passed to `attrs.asdict`'s and + `attrs.astuple`'s *filter* argument. + + .. versionchanged:: 23.3.0 Accept field name string as input argument + """ + cls, names, attrs = _split_what(what) + + def exclude_(attribute, value): + return not ( + value.__class__ in cls + or attribute.name in names + or attribute in attrs + ) + + return exclude_ diff --git a/venv/lib/python3.12/site-packages/attr/filters.pyi b/venv/lib/python3.12/site-packages/attr/filters.pyi new file mode 100644 index 0000000..974abdc --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/filters.pyi @@ -0,0 +1,6 @@ +from typing import Any + +from . import Attribute, _FilterType + +def include(*what: type | str | Attribute[Any]) -> _FilterType[Any]: ... +def exclude(*what: type | str | Attribute[Any]) -> _FilterType[Any]: ... diff --git a/venv/lib/python3.12/site-packages/attr/py.typed b/venv/lib/python3.12/site-packages/attr/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/attr/setters.py b/venv/lib/python3.12/site-packages/attr/setters.py new file mode 100644 index 0000000..78b0839 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/setters.py @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly used hooks for on_setattr. +""" + +from . import _config +from .exceptions import FrozenAttributeError + + +def pipe(*setters): + """ + Run all *setters* and return the return value of the last one. + + .. versionadded:: 20.1.0 + """ + + def wrapped_pipe(instance, attrib, new_value): + rv = new_value + + for setter in setters: + rv = setter(instance, attrib, rv) + + return rv + + return wrapped_pipe + + +def frozen(_, __, ___): + """ + Prevent an attribute to be modified. + + .. versionadded:: 20.1.0 + """ + raise FrozenAttributeError + + +def validate(instance, attrib, new_value): + """ + Run *attrib*'s validator on *new_value* if it has one. + + .. versionadded:: 20.1.0 + """ + if _config._run_validators is False: + return new_value + + v = attrib.validator + if not v: + return new_value + + v(instance, attrib, new_value) + + return new_value + + +def convert(instance, attrib, new_value): + """ + Run *attrib*'s converter -- if it has one -- on *new_value* and return the + result. + + .. versionadded:: 20.1.0 + """ + c = attrib.converter + if c: + # This can be removed once we drop 3.8 and use attrs.Converter instead. + from ._make import Converter + + if not isinstance(c, Converter): + return c(new_value) + + return c(new_value, instance, attrib) + + return new_value + + +# Sentinel for disabling class-wide *on_setattr* hooks for certain attributes. +# Sphinx's autodata stopped working, so the docstring is inlined in the API +# docs. +NO_OP = object() diff --git a/venv/lib/python3.12/site-packages/attr/setters.pyi b/venv/lib/python3.12/site-packages/attr/setters.pyi new file mode 100644 index 0000000..73abf36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/setters.pyi @@ -0,0 +1,20 @@ +from typing import Any, NewType, NoReturn, TypeVar + +from . import Attribute +from attrs import _OnSetAttrType + +_T = TypeVar("_T") + +def frozen( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> NoReturn: ... +def pipe(*setters: _OnSetAttrType) -> _OnSetAttrType: ... +def validate(instance: Any, attribute: Attribute[_T], new_value: _T) -> _T: ... + +# convert is allowed to return Any, because they can be chained using pipe. +def convert( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> Any: ... + +_NoOpType = NewType("_NoOpType", object) +NO_OP: _NoOpType diff --git a/venv/lib/python3.12/site-packages/attr/validators.py b/venv/lib/python3.12/site-packages/attr/validators.py new file mode 100644 index 0000000..0b1a294 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/validators.py @@ -0,0 +1,750 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful validators. +""" + +import operator +import re + +from contextlib import contextmanager +from re import Pattern + +from ._config import get_run_validators, set_run_validators +from ._make import _AndValidator, and_, attrib, attrs +from .converters import default_if_none +from .exceptions import NotCallableError + + +__all__ = [ + "and_", + "deep_iterable", + "deep_mapping", + "disabled", + "ge", + "get_disabled", + "gt", + "in_", + "instance_of", + "is_callable", + "le", + "lt", + "matches_re", + "max_len", + "min_len", + "not_", + "optional", + "or_", + "set_disabled", +] + + +def set_disabled(disabled): + """ + Globally disable or enable running validators. + + By default, they are run. + + Args: + disabled (bool): If `True`, disable running all validators. + + .. warning:: + + This function is not thread-safe! + + .. versionadded:: 21.3.0 + """ + set_run_validators(not disabled) + + +def get_disabled(): + """ + Return a bool indicating whether validators are currently disabled or not. + + Returns: + bool:`True` if validators are currently disabled. + + .. versionadded:: 21.3.0 + """ + return not get_run_validators() + + +@contextmanager +def disabled(): + """ + Context manager that disables running validators within its context. + + .. warning:: + + This context manager is not thread-safe! + + .. versionadded:: 21.3.0 + .. versionchanged:: 26.1.0 The contextmanager is nestable. + """ + prev = get_run_validators() + set_run_validators(False) + try: + yield + finally: + set_run_validators(prev) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _InstanceOfValidator: + type = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not isinstance(value, self.type): + msg = f"'{attr.name}' must be {self.type!r} (got {value!r} that is a {value.__class__!r})." + raise TypeError( + msg, + attr, + self.type, + value, + ) + + def __repr__(self): + return f"" + + +def instance_of(type): + """ + A validator that raises a `TypeError` if the initializer is called with a + wrong type for this particular attribute (checks are performed using + `isinstance` therefore it's also valid to pass a tuple of types). + + Args: + type (type | tuple[type]): The type to check for. + + Raises: + TypeError: + With a human readable error message, the attribute (of type + `attrs.Attribute`), the expected type, and the value it got. + """ + return _InstanceOfValidator(type) + + +@attrs(repr=False, frozen=True, slots=True) +class _MatchesReValidator: + pattern = attrib() + match_func = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.match_func(value): + msg = f"'{attr.name}' must match regex {self.pattern.pattern!r} ({value!r} doesn't)" + raise ValueError( + msg, + attr, + self.pattern, + value, + ) + + def __repr__(self): + return f"" + + +def matches_re(regex, flags=0, func=None): + r""" + A validator that raises `ValueError` if the initializer is called with a + string that doesn't match *regex*. + + Args: + regex (str, re.Pattern): + A regex string or precompiled pattern to match against + + flags (int): + Flags that will be passed to the underlying re function (default 0) + + func (typing.Callable): + Which underlying `re` function to call. Valid options are + `re.fullmatch`, `re.search`, and `re.match`; the default `None` + means `re.fullmatch`. For performance reasons, the pattern is + always precompiled using `re.compile`. + + .. versionadded:: 19.2.0 + .. versionchanged:: 21.3.0 *regex* can be a pre-compiled pattern. + """ + valid_funcs = (re.fullmatch, None, re.search, re.match) + if func not in valid_funcs: + msg = "'func' must be one of {}.".format( + ", ".join( + sorted((e and e.__name__) or "None" for e in set(valid_funcs)) + ) + ) + raise ValueError(msg) + + if isinstance(regex, Pattern): + if flags: + msg = "'flags' can only be used with a string pattern; pass flags to re.compile() instead" + raise TypeError(msg) + pattern = regex + else: + pattern = re.compile(regex, flags) + + if func is re.match: + match_func = pattern.match + elif func is re.search: + match_func = pattern.search + else: + match_func = pattern.fullmatch + + return _MatchesReValidator(pattern, match_func) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _OptionalValidator: + validator = attrib() + + def __call__(self, inst, attr, value): + if value is None: + return + + self.validator(inst, attr, value) + + def __repr__(self): + return f"" + + +def optional(validator): + """ + A validator that makes an attribute optional. An optional attribute is one + which can be set to `None` in addition to satisfying the requirements of + the sub-validator. + + Args: + validator + (typing.Callable | tuple[typing.Callable] | list[typing.Callable]): + A validator (or validators) that is used for non-`None` values. + + .. versionadded:: 15.1.0 + .. versionchanged:: 17.1.0 *validator* can be a list of validators. + .. versionchanged:: 23.1.0 *validator* can also be a tuple of validators. + """ + if isinstance(validator, (list, tuple)): + return _OptionalValidator(_AndValidator(validator)) + + return _OptionalValidator(validator) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _InValidator: + options = attrib() + _original_options = attrib(hash=False) + + def __call__(self, inst, attr, value): + try: + in_options = value in self.options + except TypeError: # e.g. `1 in "abc"` + in_options = False + + if not in_options: + msg = f"'{attr.name}' must be in {self._original_options!r} (got {value!r})" + raise ValueError( + msg, + attr, + self._original_options, + value, + ) + + def __repr__(self): + return f"" + + +def in_(options): + """ + A validator that raises a `ValueError` if the initializer is called with a + value that does not belong in the *options* provided. + + The check is performed using ``value in options``, so *options* has to + support that operation. + + To keep the validator hashable, dicts, lists, and sets are transparently + transformed into a `tuple`. + + Args: + options: Allowed options. + + Raises: + ValueError: + With a human readable error message, the attribute (of type + `attrs.Attribute`), the expected options, and the value it got. + + .. versionadded:: 17.1.0 + .. versionchanged:: 22.1.0 + The ValueError was incomplete until now and only contained the human + readable error message. Now it contains all the information that has + been promised since 17.1.0. + .. versionchanged:: 24.1.0 + *options* that are a list, dict, or a set are now transformed into a + tuple to keep the validator hashable. + """ + repr_options = options + if isinstance(options, (list, dict, set)): + options = tuple(options) + + return _InValidator(options, repr_options) + + +@attrs(repr=False, slots=False, unsafe_hash=True) +class _IsCallableValidator: + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not callable(value): + message = ( + "'{name}' must be callable " + "(got {value!r} that is a {actual!r})." + ) + raise NotCallableError( + msg=message.format( + name=attr.name, value=value, actual=value.__class__ + ), + value=value, + ) + + def __repr__(self): + return "" + + +def is_callable(): + """ + A validator that raises a `attrs.exceptions.NotCallableError` if the + initializer is called with a value for this particular attribute that is + not callable. + + .. versionadded:: 19.1.0 + + Raises: + attrs.exceptions.NotCallableError: + With a human readable error message containing the attribute + (`attrs.Attribute`) name, and the value it got. + """ + return _IsCallableValidator() + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _DeepIterable: + member_validator = attrib(validator=is_callable()) + iterable_validator = attrib( + default=None, validator=optional(is_callable()) + ) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.iterable_validator is not None: + self.iterable_validator(inst, attr, value) + + for member in value: + self.member_validator(inst, attr, member) + + def __repr__(self): + iterable_identifier = ( + "" + if self.iterable_validator is None + else f" {self.iterable_validator!r}" + ) + return ( + f"" + ) + + +def deep_iterable(member_validator, iterable_validator=None): + """ + A validator that performs deep validation of an iterable. + + Args: + member_validator: Validator(s) to apply to iterable members. + + iterable_validator: + Validator(s) to apply to iterable itself (optional). + + Raises + TypeError: if any sub-validators fail + + .. versionadded:: 19.1.0 + + .. versionchanged:: 25.4.0 + *member_validator* and *iterable_validator* can now be a list or tuple + of validators. + """ + if isinstance(member_validator, (list, tuple)): + member_validator = and_(*member_validator) + if isinstance(iterable_validator, (list, tuple)): + iterable_validator = and_(*iterable_validator) + return _DeepIterable(member_validator, iterable_validator) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _DeepMapping: + key_validator = attrib(validator=optional(is_callable())) + value_validator = attrib(validator=optional(is_callable())) + mapping_validator = attrib(validator=optional(is_callable())) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.mapping_validator is not None: + self.mapping_validator(inst, attr, value) + + for key in value: + if self.key_validator is not None: + self.key_validator(inst, attr, key) + if self.value_validator is not None: + self.value_validator(inst, attr, value[key]) + + def __repr__(self): + return f"" + + +def deep_mapping( + key_validator=None, value_validator=None, mapping_validator=None +): + """ + A validator that performs deep validation of a dictionary. + + All validators are optional, but at least one of *key_validator* or + *value_validator* must be provided. + + Args: + key_validator: Validator(s) to apply to dictionary keys. + + value_validator: Validator(s) to apply to dictionary values. + + mapping_validator: + Validator(s) to apply to top-level mapping attribute. + + .. versionadded:: 19.1.0 + + .. versionchanged:: 25.4.0 + *key_validator* and *value_validator* are now optional, but at least one + of them must be provided. + + .. versionchanged:: 25.4.0 + *key_validator*, *value_validator*, and *mapping_validator* can now be a + list or tuple of validators. + + Raises: + TypeError: If any sub-validator fails on validation. + + ValueError: + If neither *key_validator* nor *value_validator* is provided on + instantiation. + """ + if key_validator is None and value_validator is None: + msg = ( + "At least one of key_validator or value_validator must be provided" + ) + raise ValueError(msg) + + if isinstance(key_validator, (list, tuple)): + key_validator = and_(*key_validator) + if isinstance(value_validator, (list, tuple)): + value_validator = and_(*value_validator) + if isinstance(mapping_validator, (list, tuple)): + mapping_validator = and_(*mapping_validator) + + return _DeepMapping(key_validator, value_validator, mapping_validator) + + +@attrs(repr=False, frozen=True, slots=True) +class _NumberValidator: + bound = attrib() + compare_op = attrib() + compare_func = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.compare_func(value, self.bound): + msg = f"'{attr.name}' must be {self.compare_op} {self.bound}: {value}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def lt(val): + """ + A validator that raises `ValueError` if the initializer is called with a + number larger or equal to *val*. + + The validator uses `operator.lt` to compare the values. + + Args: + val: Exclusive upper bound for values. + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, "<", operator.lt) + + +def le(val): + """ + A validator that raises `ValueError` if the initializer is called with a + number greater than *val*. + + The validator uses `operator.le` to compare the values. + + Args: + val: Inclusive upper bound for values. + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, "<=", operator.le) + + +def ge(val): + """ + A validator that raises `ValueError` if the initializer is called with a + number smaller than *val*. + + The validator uses `operator.ge` to compare the values. + + Args: + val: Inclusive lower bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, ">=", operator.ge) + + +def gt(val): + """ + A validator that raises `ValueError` if the initializer is called with a + number smaller or equal to *val*. + + The validator uses `operator.gt` to compare the values. + + Args: + val: Exclusive lower bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, ">", operator.gt) + + +@attrs(repr=False, frozen=True, slots=True) +class _MaxLengthValidator: + max_length = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if len(value) > self.max_length: + msg = f"Length of '{attr.name}' must be <= {self.max_length}: {len(value)}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def max_len(length): + """ + A validator that raises `ValueError` if the initializer is called + with a string or iterable that is longer than *length*. + + Args: + length (int): Maximum length of the string or iterable + + .. versionadded:: 21.3.0 + """ + return _MaxLengthValidator(length) + + +@attrs(repr=False, frozen=True, slots=True) +class _MinLengthValidator: + min_length = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if len(value) < self.min_length: + msg = f"Length of '{attr.name}' must be >= {self.min_length}: {len(value)}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def min_len(length): + """ + A validator that raises `ValueError` if the initializer is called + with a string or iterable that is shorter than *length*. + + Args: + length (int): Minimum length of the string or iterable + + .. versionadded:: 22.1.0 + """ + return _MinLengthValidator(length) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _SubclassOfValidator: + type = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not issubclass(value, self.type): + msg = f"'{attr.name}' must be a subclass of {self.type!r} (got {value!r})." + raise TypeError( + msg, + attr, + self.type, + value, + ) + + def __repr__(self): + return f"" + + +def _subclass_of(type): + """ + A validator that raises a `TypeError` if the initializer is called with a + wrong type for this particular attribute (checks are performed using + `issubclass` therefore it's also valid to pass a tuple of types). + + Args: + type (type | tuple[type, ...]): The type(s) to check for. + + Raises: + TypeError: + With a human readable error message, the attribute (of type + `attrs.Attribute`), the expected type, and the value it got. + """ + return _SubclassOfValidator(type) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _NotValidator: + validator = attrib() + msg = attrib( + converter=default_if_none( + "not_ validator child '{validator!r}' " + "did not raise a captured error" + ) + ) + exc_types = attrib( + validator=deep_iterable( + member_validator=_subclass_of(Exception), + iterable_validator=instance_of(tuple), + ), + ) + + def __call__(self, inst, attr, value): + try: + self.validator(inst, attr, value) + except self.exc_types: + pass # suppress error to invert validity + else: + raise ValueError( + self.msg.format( + validator=self.validator, + exc_types=self.exc_types, + ), + attr, + self.validator, + value, + self.exc_types, + ) + + def __repr__(self): + return f"" + + +def not_(validator, *, msg=None, exc_types=(ValueError, TypeError)): + """ + A validator that wraps and logically 'inverts' the validator passed to it. + It will raise a `ValueError` if the provided validator *doesn't* raise a + `ValueError` or `TypeError` (by default), and will suppress the exception + if the provided validator *does*. + + Intended to be used with existing validators to compose logic without + needing to create inverted variants, for example, ``not_(in_(...))``. + + Args: + validator: A validator to be logically inverted. + + msg (str): + Message to raise if validator fails. Formatted with keys + ``exc_types`` and ``validator``. + + exc_types (tuple[type, ...]): + Exception type(s) to capture. Other types raised by child + validators will not be intercepted and pass through. + + Raises: + ValueError: + With a human readable error message, the attribute (of type + `attrs.Attribute`), the validator that failed to raise an + exception, the value it got, and the expected exception types. + + .. versionadded:: 22.2.0 + """ + try: + exc_types = tuple(exc_types) + except TypeError: + exc_types = (exc_types,) + return _NotValidator(validator, msg, exc_types) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _OrValidator: + validators = attrib() + + def __call__(self, inst, attr, value): + for v in self.validators: + try: + v(inst, attr, value) + except Exception: # noqa: BLE001, PERF203, S112 + continue + else: + return + + msg = f"None of {self.validators!r} satisfied for value {value!r}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def or_(*validators): + """ + A validator that composes multiple validators into one. + + When called on a value, it runs all wrapped validators until one of them is + satisfied. + + Args: + validators (~collections.abc.Iterable[typing.Callable]): + Arbitrary number of validators. + + Raises: + ValueError: + If no validator is satisfied. Raised with a human-readable error + message listing all the wrapped validators and the value that + failed all of them. + + .. versionadded:: 24.1.0 + """ + vals = [] + for v in validators: + vals.extend(v.validators if isinstance(v, _OrValidator) else [v]) + + return _OrValidator(tuple(vals)) diff --git a/venv/lib/python3.12/site-packages/attr/validators.pyi b/venv/lib/python3.12/site-packages/attr/validators.pyi new file mode 100644 index 0000000..18fb112 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/validators.pyi @@ -0,0 +1,140 @@ +from types import UnionType +from typing import ( + Any, + AnyStr, + Callable, + Container, + ContextManager, + Iterable, + Mapping, + Match, + Pattern, + TypeVar, + overload, +) + +from attrs import _ValidatorType +from attrs import _ValidatorArgType + +_T = TypeVar("_T") +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") +_T4 = TypeVar("_T4") +_T5 = TypeVar("_T5") +_T6 = TypeVar("_T6") +_I = TypeVar("_I", bound=Iterable) +_K = TypeVar("_K") +_V = TypeVar("_V") +_M = TypeVar("_M", bound=Mapping) + +def set_disabled(run: bool) -> None: ... +def get_disabled() -> bool: ... +def disabled() -> ContextManager[None]: ... + +# To be more precise on instance_of use some overloads. +# If there are more than 3 items in the tuple then we fall back to Any +@overload +def instance_of(type: type[_T]) -> _ValidatorType[_T]: ... +@overload +def instance_of(type: tuple[type[_T]]) -> _ValidatorType[_T]: ... +@overload +def instance_of( + type: tuple[type[_T1], type[_T2]], +) -> _ValidatorType[_T1 | _T2]: ... +@overload +def instance_of( + type: tuple[type[_T1], type[_T2], type[_T3]], +) -> _ValidatorType[_T1 | _T2 | _T3]: ... +@overload +def instance_of(type: tuple[type, ...]) -> _ValidatorType[Any]: ... +@overload +def instance_of(type: UnionType) -> _ValidatorType[Any]: ... +def optional( + validator: ( + _ValidatorType[_T] + | list[_ValidatorType[_T]] + | tuple[_ValidatorType[_T], ...] + ), +) -> _ValidatorType[_T | None]: ... +def in_(options: Container[_T]) -> _ValidatorType[_T]: ... +def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ... +def matches_re( + regex: Pattern[AnyStr] | AnyStr, + flags: int = ..., + func: Callable[[AnyStr, AnyStr, int], Match[AnyStr] | None] | None = ..., +) -> _ValidatorType[AnyStr]: ... +def deep_iterable( + member_validator: _ValidatorArgType[_T], + iterable_validator: _ValidatorArgType[_I] | None = ..., +) -> _ValidatorType[_I]: ... +@overload +def deep_mapping( + key_validator: _ValidatorArgType[_K], + value_validator: _ValidatorArgType[_V] | None = ..., + mapping_validator: _ValidatorArgType[_M] | None = ..., +) -> _ValidatorType[_M]: ... +@overload +def deep_mapping( + key_validator: _ValidatorArgType[_K] | None = ..., + value_validator: _ValidatorArgType[_V] = ..., + mapping_validator: _ValidatorArgType[_M] | None = ..., +) -> _ValidatorType[_M]: ... +def is_callable() -> _ValidatorType[_T]: ... +def lt(val: _T) -> _ValidatorType[_T]: ... +def le(val: _T) -> _ValidatorType[_T]: ... +def ge(val: _T) -> _ValidatorType[_T]: ... +def gt(val: _T) -> _ValidatorType[_T]: ... +def max_len(length: int) -> _ValidatorType[_T]: ... +def min_len(length: int) -> _ValidatorType[_T]: ... +def not_( + validator: _ValidatorType[_T], + *, + msg: str | None = None, + exc_types: type[Exception] | Iterable[type[Exception]] = ..., +) -> _ValidatorType[_T]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], +) -> _ValidatorType[_T1 | _T2]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], + __v3: _ValidatorType[_T3], +) -> _ValidatorType[_T1 | _T2 | _T3]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], + __v3: _ValidatorType[_T3], + __v4: _ValidatorType[_T4], +) -> _ValidatorType[_T1 | _T2 | _T3 | _T4]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], + __v3: _ValidatorType[_T3], + __v4: _ValidatorType[_T4], + __v5: _ValidatorType[_T5], +) -> _ValidatorType[_T1 | _T2 | _T3 | _T4 | _T5]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], + __v3: _ValidatorType[_T3], + __v4: _ValidatorType[_T4], + __v5: _ValidatorType[_T5], + __v6: _ValidatorType[_T6], +) -> _ValidatorType[_T1 | _T2 | _T3 | _T4 | _T5 | _T6]: ... +@overload +def or_( + __v1: _ValidatorType[Any], + __v2: _ValidatorType[Any], + __v3: _ValidatorType[Any], + __v4: _ValidatorType[Any], + __v5: _ValidatorType[Any], + __v6: _ValidatorType[Any], + *validators: _ValidatorType[Any], +) -> _ValidatorType[Any]: ... diff --git a/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/METADATA new file mode 100644 index 0000000..5cf16a0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/METADATA @@ -0,0 +1,199 @@ +Metadata-Version: 2.4 +Name: attrs +Version: 26.1.0 +Summary: Classes Without Boilerplate +Project-URL: Documentation, https://www.attrs.org/ +Project-URL: Changelog, https://www.attrs.org/en/stable/changelog.html +Project-URL: GitHub, https://github.com/python-attrs/attrs +Project-URL: Funding, https://github.com/sponsors/hynek +Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi +Author-email: Hynek Schlawack +License-Expression: MIT +License-File: LICENSE +Keywords: attribute,boilerplate,class +Classifier: Development Status :: 5 - Production/Stable +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Typing :: Typed +Requires-Python: >=3.9 +Description-Content-Type: text/markdown + +

+ + attrs + +

+ + +*attrs* is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka [dunder methods](https://www.attrs.org/en/latest/glossary.html#term-dunder-methods)). +Trusted by NASA for [Mars missions since 2020](https://github.com/readme/featured/nasa-ingenuity-helicopter)! + +Its main goal is to help you to write **concise** and **correct** software without slowing down your code. + + +## Sponsors + +*attrs* would not be possible without our [amazing sponsors](https://github.com/sponsors/hynek). +Especially those generously supporting us at the *The Organization* tier and higher: + + + +

+ + + + + + + + + + + +

+ + + +

+ Please consider joining them to help make attrs’s maintenance more sustainable! +

+ + + +## Example + +*attrs* gives you a class decorator and a way to declaratively define the attributes on that class: + + + +```pycon +>>> from attrs import asdict, define, make_class, Factory + +>>> @define +... class SomeClass: +... a_number: int = 42 +... list_of_numbers: list[int] = Factory(list) +... +... def hard_math(self, another_number): +... return self.a_number + sum(self.list_of_numbers) * another_number + + +>>> sc = SomeClass(1, [1, 2, 3]) +>>> sc +SomeClass(a_number=1, list_of_numbers=[1, 2, 3]) + +>>> sc.hard_math(3) +19 +>>> sc == SomeClass(1, [1, 2, 3]) +True +>>> sc != SomeClass(2, [3, 2, 1]) +True + +>>> asdict(sc) +{'a_number': 1, 'list_of_numbers': [1, 2, 3]} + +>>> SomeClass() +SomeClass(a_number=42, list_of_numbers=[]) + +>>> C = make_class("C", ["a", "b"]) +>>> C("foo", "bar") +C(a='foo', b='bar') +``` + +After *declaring* your attributes, *attrs* gives you: + +- a concise and explicit overview of the class's attributes, +- a nice human-readable `__repr__`, +- equality-checking methods, +- an initializer, +- and much more, + +*without* writing dull boilerplate code again and again and *without* runtime performance penalties. + +--- + +This example uses *attrs*'s modern APIs that have been introduced in version 20.1.0, and the *attrs* package import name that has been added in version 21.3.0. +The classic APIs (`@attr.s`, `attr.ib`, plus their serious-business aliases) and the `attr` package import name will remain **indefinitely**. + +Check out [*On The Core API Names*](https://www.attrs.org/en/latest/names.html) for an in-depth explanation! + + +### Hate Type Annotations!? + +No problem! +Types are entirely **optional** with *attrs*. +Simply assign `attrs.field()` to the attributes instead of annotating them with types: + +```python +from attrs import define, field + +@define +class SomeClass: + a_number = field(default=42) + list_of_numbers = field(factory=list) +``` + + +## Data Classes + +On the tin, *attrs* might remind you of `dataclasses` (and indeed, `dataclasses` [are a descendant](https://hynek.me/articles/import-attrs/) of *attrs*). +In practice it does a lot more and is more flexible. +For instance, it allows you to define [special handling of NumPy arrays for equality checks](https://www.attrs.org/en/stable/comparison.html#customization), allows more ways to [plug into the initialization process](https://www.attrs.org/en/stable/init.html#hooking-yourself-into-initialization), has a replacement for `__init_subclass__`, and allows for stepping through the generated methods using a debugger. + +For more details, please refer to our [comparison page](https://www.attrs.org/en/stable/why.html#data-classes), but generally speaking, we are more likely to commit crimes against nature to make things work that one would expect to work, but that are quite complicated in practice. + + +## Project Information + +- [**Changelog**](https://www.attrs.org/en/stable/changelog.html) +- [**Documentation**](https://www.attrs.org/) +- [**PyPI**](https://pypi.org/project/attrs/) +- [**Source Code**](https://github.com/python-attrs/attrs) +- [**Contributing**](https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md) +- [**Third-party Extensions**](https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs) +- **Get Help**: use the `python-attrs` tag on [Stack Overflow](https://stackoverflow.com/questions/tagged/python-attrs) + + +### *attrs* for Enterprise + +Available as part of the [Tidelift Subscription](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek). + +The maintainers of *attrs* and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. +Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. + +## Release Information + +### Backwards-incompatible Changes + +- Field aliases are now resolved *before* calling `field_transformer`, so transformers receive fully populated `Attribute` objects with usable `alias` values instead of `None`. + The new `Attribute.alias_is_default` flag indicates whether the alias was auto-generated (`True`) or explicitly set by the user (`False`). + [#1509](https://github.com/python-attrs/attrs/issues/1509) + + +### Changes + +- Fix type annotations for `attrs.validators.optional()`, so it no longer rejects tuples with more than one validator. + [#1496](https://github.com/python-attrs/attrs/issues/1496) +- The `attrs.validators.disabled()` contextmanager can now be nested. + [#1513](https://github.com/python-attrs/attrs/issues/1513) +- Frozen classes can set `on_setattr=attrs.setters.NO_OP` in addition to `None`. + [#1515](https://github.com/python-attrs/attrs/issues/1515) +- It's now possible to pass *attrs* **instances** in addition to *attrs* **classes** to `attrs.fields()`. + [#1529](https://github.com/python-attrs/attrs/issues/1529) + + + +--- + +[Full changelog →](https://www.attrs.org/en/stable/changelog.html) diff --git a/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/RECORD new file mode 100644 index 0000000..2cf8b83 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/RECORD @@ -0,0 +1,55 @@ +attr/__init__.py,sha256=fOYIvt1eGSqQre4uCS3sJWKZ0mwAuC8UD6qba5OS9_U,2057 +attr/__init__.pyi,sha256=pVGImAUVovq2_TYl_r_HIYnGlyOaoCuEhxo-EvsnnSc,11325 +attr/__pycache__/__init__.cpython-312.pyc,, +attr/__pycache__/_cmp.cpython-312.pyc,, +attr/__pycache__/_compat.cpython-312.pyc,, +attr/__pycache__/_config.cpython-312.pyc,, +attr/__pycache__/_funcs.cpython-312.pyc,, +attr/__pycache__/_make.cpython-312.pyc,, +attr/__pycache__/_next_gen.cpython-312.pyc,, +attr/__pycache__/_version_info.cpython-312.pyc,, +attr/__pycache__/converters.cpython-312.pyc,, +attr/__pycache__/exceptions.cpython-312.pyc,, +attr/__pycache__/filters.cpython-312.pyc,, +attr/__pycache__/setters.cpython-312.pyc,, +attr/__pycache__/validators.cpython-312.pyc,, +attr/_cmp.py,sha256=3Nn1TjxllUYiX_nJoVnEkXoDk0hM1DYKj5DE7GZe4i0,4117 +attr/_cmp.pyi,sha256=U-_RU_UZOyPUEQzXE6RMYQQcjkZRY25wTH99sN0s7MM,368 +attr/_compat.py,sha256=x0g7iEUOnBVJC72zyFCgb1eKqyxS-7f2LGnNyZ_r95s,2829 +attr/_config.py,sha256=dGq3xR6fgZEF6UBt_L0T-eUHIB4i43kRmH0P28sJVw8,843 +attr/_funcs.py,sha256=Ix5IETTfz5F01F-12MF_CSFomIn2h8b67EVVz2gCtBE,16479 +attr/_make.py,sha256=H7OH2eWS5CnBzLUjNFE1WymfPrmF1r8fv2RPdt9MuYA,106129 +attr/_next_gen.py,sha256=BQtCUlzwg2gWHTYXBQvrEYBnzBUrDvO57u0Py6UCPhc,26274 +attr/_typing_compat.pyi,sha256=XDP54TUn-ZKhD62TOQebmzrwFyomhUCoGRpclb6alRA,469 +attr/_version_info.py,sha256=w4R-FYC3NK_kMkGUWJlYP4cVAlH9HRaC-um3fcjYkHM,2222 +attr/_version_info.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209 +attr/converters.py,sha256=GlDeOzPeTFgeBBLbj9G57Ez5lAk68uhSALRYJ_exe84,3861 +attr/converters.pyi,sha256=orU2bff-VjQa2kMDyvnMQV73oJT2WRyQuw4ZR1ym1bE,643 +attr/exceptions.py,sha256=b4vMbnoQ3VpwWZhqrYi_ssXVCK8o2c4HQSS09cSUM9o,1990 +attr/exceptions.pyi,sha256=zZq8bCUnKAy9mDtBEw42ZhPhAUIHoTKedDQInJD883M,539 +attr/filters.py,sha256=ZBiKWLp3R0LfCZsq7X11pn9WX8NslS2wXM4jsnLOGc8,1795 +attr/filters.pyi,sha256=3J5BG-dTxltBk1_-RuNRUHrv2qu1v8v4aDNAQ7_mifA,208 +attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attr/setters.py,sha256=5-dcT63GQK35ONEzSgfXCkbB7pPkaR-qv15mm4PVSzQ,1617 +attr/setters.pyi,sha256=NnVkaFU1BB4JB8E4JuXyrzTUgvtMpj8p3wBdJY7uix4,584 +attr/validators.py,sha256=m3QRzZTANr4f2C4eVdUoFg11NgXWak8Wat4qQTGhvcs,21553 +attr/validators.pyi,sha256=gM1ZmHaBckyYWI2EirpRNzqm3B19cw5Iq6B4Kno9YCM,4087 +attrs-26.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +attrs-26.1.0.dist-info/METADATA,sha256=TNQOaQ8jvzfLytNO_WdY4GLfHfB8hoM_fjzpW_H6OMw,8754 +attrs-26.1.0.dist-info/RECORD,, +attrs-26.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87 +attrs-26.1.0.dist-info/licenses/LICENSE,sha256=iCEVyV38KvHutnFPjsbVy8q_Znyv-HKfQkINpj9xTp8,1109 +attrs/__init__.py,sha256=RxaAZNwYiEh-fcvHLZNpQ_DWKni73M_jxEPEftiq1Zc,1183 +attrs/__init__.pyi,sha256=2gV79g9UxJppGSM48hAZJ6h_MHb70dZoJL31ZNJeZYI,9416 +attrs/__pycache__/__init__.cpython-312.pyc,, +attrs/__pycache__/converters.cpython-312.pyc,, +attrs/__pycache__/exceptions.cpython-312.pyc,, +attrs/__pycache__/filters.cpython-312.pyc,, +attrs/__pycache__/setters.cpython-312.pyc,, +attrs/__pycache__/validators.cpython-312.pyc,, +attrs/converters.py,sha256=8kQljrVwfSTRu8INwEk8SI0eGrzmWftsT7rM0EqyohM,76 +attrs/exceptions.py,sha256=ACCCmg19-vDFaDPY9vFl199SPXCQMN_bENs4DALjzms,76 +attrs/filters.py,sha256=VOUMZug9uEU6dUuA0dF1jInUK0PL3fLgP0VBS5d-CDE,73 +attrs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attrs/setters.py,sha256=eL1YidYQV3T2h9_SYIZSZR1FAcHGb1TuCTy0E0Lv2SU,73 +attrs/validators.py,sha256=xcy6wD5TtTkdCG1f4XWbocPSO0faBjk5IfVJfP6SUj0,76 diff --git a/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/WHEEL new file mode 100644 index 0000000..b1b94fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.29.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..2bd6453 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Hynek Schlawack and the attrs contributors + +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. diff --git a/venv/lib/python3.12/site-packages/attrs/__init__.py b/venv/lib/python3.12/site-packages/attrs/__init__.py new file mode 100644 index 0000000..dc1ce4b --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/__init__.py @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: MIT + +from attr import ( + NOTHING, + Attribute, + AttrsInstance, + Converter, + Factory, + NothingType, + _make_getattr, + assoc, + cmp_using, + define, + evolve, + field, + fields, + fields_dict, + frozen, + has, + make_class, + mutable, + resolve_types, + validate, +) +from attr._make import ClassProps +from attr._next_gen import asdict, astuple, inspect + +from . import converters, exceptions, filters, setters, validators + + +__all__ = [ + "NOTHING", + "Attribute", + "AttrsInstance", + "ClassProps", + "Converter", + "Factory", + "NothingType", + "__author__", + "__copyright__", + "__description__", + "__doc__", + "__email__", + "__license__", + "__title__", + "__url__", + "__version__", + "__version_info__", + "asdict", + "assoc", + "astuple", + "cmp_using", + "converters", + "define", + "evolve", + "exceptions", + "field", + "fields", + "fields_dict", + "filters", + "frozen", + "has", + "inspect", + "make_class", + "mutable", + "resolve_types", + "setters", + "validate", + "validators", +] + +__getattr__ = _make_getattr(__name__) diff --git a/venv/lib/python3.12/site-packages/attrs/__init__.pyi b/venv/lib/python3.12/site-packages/attrs/__init__.pyi new file mode 100644 index 0000000..6364bac --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/__init__.pyi @@ -0,0 +1,314 @@ +import sys + +from typing import ( + Any, + Callable, + Mapping, + Sequence, + overload, + TypeVar, +) + +# Because we need to type our own stuff, we have to make everything from +# attr explicitly public too. +from attr import __author__ as __author__ +from attr import __copyright__ as __copyright__ +from attr import __description__ as __description__ +from attr import __email__ as __email__ +from attr import __license__ as __license__ +from attr import __title__ as __title__ +from attr import __url__ as __url__ +from attr import __version__ as __version__ +from attr import __version_info__ as __version_info__ +from attr import assoc as assoc +from attr import Attribute as Attribute +from attr import AttrsInstance as AttrsInstance +from attr import cmp_using as cmp_using +from attr import converters as converters +from attr import Converter as Converter +from attr import evolve as evolve +from attr import exceptions as exceptions +from attr import Factory as Factory +from attr import fields as fields +from attr import fields_dict as fields_dict +from attr import filters as filters +from attr import has as has +from attr import make_class as make_class +from attr import NOTHING as NOTHING +from attr import resolve_types as resolve_types +from attr import setters as setters +from attr import validate as validate +from attr import validators as validators +from attr import attrib, asdict as asdict, astuple as astuple +from attr import NothingType as NothingType + +if sys.version_info >= (3, 11): + from typing import dataclass_transform +else: + from typing_extensions import dataclass_transform + +_T = TypeVar("_T") +_C = TypeVar("_C", bound=type) + +_EqOrderType = bool | Callable[[Any], Any] +_ValidatorType = Callable[[Any, "Attribute[_T]", _T], Any] +_CallableConverterType = Callable[[Any], Any] +_ConverterType = _CallableConverterType | Converter[Any, Any] +_ReprType = Callable[[Any], str] +_ReprArgType = bool | _ReprType +_OnSetAttrType = Callable[[Any, "Attribute[Any]", Any], Any] +_OnSetAttrArgType = _OnSetAttrType | list[_OnSetAttrType] | setters._NoOpType +_FieldTransformer = Callable[ + [type, list["Attribute[Any]"]], list["Attribute[Any]"] +] +# FIXME: in reality, if multiple validators are passed they must be in a list +# or tuple, but those are invariant and so would prevent subtypes of +# _ValidatorType from working when passed in a list or tuple. +_ValidatorArgType = _ValidatorType[_T] | Sequence[_ValidatorType[_T]] + +@overload +def field( + *, + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool | None = ..., + eq: bool | None = ..., + order: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def field( + *, + default: None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType, ...] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def field( + *, + default: _T, + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType, ...] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def field( + *, + default: _T | None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType, ...] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> Any: ... +@overload +@dataclass_transform(field_specifiers=(attrib, field)) +def define( + maybe_cls: _C, + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> _C: ... +@overload +@dataclass_transform(field_specifiers=(attrib, field)) +def define( + maybe_cls: None = ..., + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> Callable[[_C], _C]: ... + +mutable = define + +@overload +@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field)) +def frozen( + maybe_cls: _C, + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> _C: ... +@overload +@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field)) +def frozen( + maybe_cls: None = ..., + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> Callable[[_C], _C]: ... + +class ClassProps: + # XXX: somehow when defining/using enums Mypy starts looking at our own + # (untyped) code and causes tons of errors. + Hashability: Any + KeywordOnly: Any + + is_exception: bool + is_slotted: bool + has_weakref_slot: bool + is_frozen: bool + # kw_only: ClassProps.KeywordOnly + kw_only: Any + collected_fields_by_mro: bool + added_init: bool + added_repr: bool + added_eq: bool + added_ordering: bool + # hashability: ClassProps.Hashability + hashability: Any + added_match_args: bool + added_str: bool + added_pickling: bool + on_setattr_hook: _OnSetAttrType | None + field_transformer: Callable[[Attribute[Any]], Attribute[Any]] | None + + def __init__( + self, + is_exception: bool, + is_slotted: bool, + has_weakref_slot: bool, + is_frozen: bool, + # kw_only: ClassProps.KeywordOnly + kw_only: Any, + collected_fields_by_mro: bool, + added_init: bool, + added_repr: bool, + added_eq: bool, + added_ordering: bool, + # hashability: ClassProps.Hashability + hashability: Any, + added_match_args: bool, + added_str: bool, + added_pickling: bool, + on_setattr_hook: _OnSetAttrType, + field_transformer: Callable[[Attribute[Any]], Attribute[Any]], + ) -> None: ... + @property + def is_hashable(self) -> bool: ... + +def inspect(cls: type) -> ClassProps: ... diff --git a/venv/lib/python3.12/site-packages/attrs/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/attrs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b089c11 Binary files /dev/null and b/venv/lib/python3.12/site-packages/attrs/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attrs/__pycache__/converters.cpython-312.pyc b/venv/lib/python3.12/site-packages/attrs/__pycache__/converters.cpython-312.pyc new file mode 100644 index 0000000..92f3aec Binary files /dev/null and b/venv/lib/python3.12/site-packages/attrs/__pycache__/converters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attrs/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/attrs/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..14c2a4a Binary files /dev/null and b/venv/lib/python3.12/site-packages/attrs/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attrs/__pycache__/filters.cpython-312.pyc b/venv/lib/python3.12/site-packages/attrs/__pycache__/filters.cpython-312.pyc new file mode 100644 index 0000000..8519fff Binary files /dev/null and b/venv/lib/python3.12/site-packages/attrs/__pycache__/filters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attrs/__pycache__/setters.cpython-312.pyc b/venv/lib/python3.12/site-packages/attrs/__pycache__/setters.cpython-312.pyc new file mode 100644 index 0000000..efc9ede Binary files /dev/null and b/venv/lib/python3.12/site-packages/attrs/__pycache__/setters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attrs/__pycache__/validators.cpython-312.pyc b/venv/lib/python3.12/site-packages/attrs/__pycache__/validators.cpython-312.pyc new file mode 100644 index 0000000..edfaa78 Binary files /dev/null and b/venv/lib/python3.12/site-packages/attrs/__pycache__/validators.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/attrs/converters.py b/venv/lib/python3.12/site-packages/attrs/converters.py new file mode 100644 index 0000000..7821f6c --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/converters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.converters import * # noqa: F403 diff --git a/venv/lib/python3.12/site-packages/attrs/exceptions.py b/venv/lib/python3.12/site-packages/attrs/exceptions.py new file mode 100644 index 0000000..3323f9d --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/exceptions.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.exceptions import * # noqa: F403 diff --git a/venv/lib/python3.12/site-packages/attrs/filters.py b/venv/lib/python3.12/site-packages/attrs/filters.py new file mode 100644 index 0000000..3080f48 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/filters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.filters import * # noqa: F403 diff --git a/venv/lib/python3.12/site-packages/attrs/py.typed b/venv/lib/python3.12/site-packages/attrs/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/attrs/setters.py b/venv/lib/python3.12/site-packages/attrs/setters.py new file mode 100644 index 0000000..f3d73bb --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/setters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.setters import * # noqa: F403 diff --git a/venv/lib/python3.12/site-packages/attrs/validators.py b/venv/lib/python3.12/site-packages/attrs/validators.py new file mode 100644 index 0000000..037e124 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/validators.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.validators import * # noqa: F403 diff --git a/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/LICENSE b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/LICENSE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/METADATA b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/METADATA new file mode 100644 index 0000000..ccc96f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/METADATA @@ -0,0 +1,182 @@ +Metadata-Version: 2.1 +Name: boto3 +Version: 1.43.104 +Summary: The AWS SDK for Python (Boto3) +Home-page: https://github.com/boto/boto3 +Author: Amazon Web Services +License: Apache-2.0 +Project-URL: Documentation, https://docs.aws.amazon.com/boto3/latest/ +Project-URL: Source, https://github.com/boto/boto3 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta +Requires-Python: >= 3.10 +License-File: LICENSE +License-File: NOTICE +Requires-Dist: botocore (<1.44.0,>=1.43.104) +Requires-Dist: jmespath (<2.0.0,>=0.7.1) +Requires-Dist: s3transfer (<0.20.0,>=0.19.0) +Provides-Extra: crt +Requires-Dist: botocore[crt] (<2.0a0,>=1.21.0) ; extra == 'crt' + +=============================== +Boto3 - The AWS SDK for Python +=============================== + +|Version| |Python| |License| + +Boto3 is the Amazon Web Services (AWS) Software Development Kit (SDK) for +Python, which allows Python developers to write software that makes use +of services like Amazon S3 and Amazon EC2. You can find the latest, most +up to date, documentation at our `doc site`_, including a list of +services that are supported. + +Boto3 is maintained and published by `Amazon Web Services`_. + +Boto (pronounced boh-toh) was named after the fresh water dolphin native to the Amazon river. The name was chosen by the author of the original Boto library, Mitch Garnaat, as a reference to the company. + +Notices +------- + +On 2026-04-29, support for Python 3.9 ended for Boto3. This follows the +Python Software Foundation `end of support `__ +for the runtime which occurred on 2025-10-31. + +For more information on deprecations, see this +`blog post `__. + +.. _boto: https://docs.pythonboto.org/ +.. _`doc site`: https://docs.aws.amazon.com/boto3/latest/ +.. _`Amazon Web Services`: https://aws.amazon.com/what-is-aws/ +.. |Python| image:: https://img.shields.io/pypi/pyversions/boto3.svg?style=flat + :target: https://pypi.python.org/pypi/boto3/ + :alt: Python Versions +.. |Version| image:: https://img.shields.io/pypi/v/boto3.svg?style=flat + :target: https://pypi.python.org/pypi/boto3/ + :alt: Package Version +.. |License| image:: https://img.shields.io/pypi/l/boto3.svg?style=flat + :target: https://github.com/boto/boto3/blob/develop/LICENSE + :alt: License + +Getting Started +--------------- +Assuming that you have a supported version of Python installed, you can first +set up your environment with: + +.. code-block:: sh + + $ python -m venv .venv + ... + $ . .venv/bin/activate + +Then, you can install boto3 from PyPI with: + +.. code-block:: sh + + $ python -m pip install boto3 + +or install from source with: + +.. code-block:: sh + + $ git clone https://github.com/boto/boto3.git + $ cd boto3 + $ python -m pip install -r requirements.txt + $ python -m pip install -e . + + +Using Boto3 +~~~~~~~~~~~~~~ +After installing boto3 + +Next, set up credentials (in e.g. ``~/.aws/credentials``): + +.. code-block:: ini + + [default] + aws_access_key_id = YOUR_KEY + aws_secret_access_key = YOUR_SECRET + +Then, set up a default region (in e.g. ``~/.aws/config``): + +.. code-block:: ini + + [default] + region = us-east-1 + +Other credential configuration methods can be found `here `__ + +Then, from a Python interpreter: + +.. code-block:: python + + >>> import boto3 + >>> s3 = boto3.resource('s3') + >>> for bucket in s3.buckets.all(): + print(bucket.name) + +Running Tests +~~~~~~~~~~~~~ +You can run tests in all supported Python versions using ``tox``. By default, +it will run all of the unit and functional tests, but you can also specify your own +``pytest`` options. Note that this requires that you have all supported +versions of Python installed, otherwise you must pass ``-e`` or run the +``pytest`` command directly: + +.. code-block:: sh + + $ tox + $ tox -- unit/test_session.py + $ tox -e py26,py33 -- integration/ + +You can also run individual tests with your default Python version: + +.. code-block:: sh + + $ pytest tests/unit + + +Getting Help +------------ + +We use GitHub issues for tracking bugs and feature requests and have limited +bandwidth to address them. Please use these community resources for getting +help: + +* Ask a question on `Stack Overflow `__ and tag it with `boto3 `__ +* Open a support ticket with `AWS Support `__ +* If it turns out that you may have found a bug, please `open an issue `__ + + +Contributing +------------ + +We value feedback and contributions from our community. Whether it's a bug report, new feature, correction, or additional documentation, we welcome your issues and pull requests. Please read through this `CONTRIBUTING `__ document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your contribution. + + +Maintenance and Support for SDK Major Versions +---------------------------------------------- + +Boto3 was made generally available on 06/22/2015 and is currently in the full support phase of the availability life cycle. + +For information about maintenance and support for SDK major versions and their underlying dependencies, see the following in the AWS SDKs and Tools Shared Configuration and Credentials Reference Guide: + +* `AWS SDKs and Tools Maintenance Policy `__ +* `AWS SDKs and Tools Version Support Matrix `__ + + +More Resources +-------------- + +* `NOTICE `__ +* `Changelog `__ +* `License `__ diff --git a/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/NOTICE b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/NOTICE new file mode 100644 index 0000000..eff609f --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/NOTICE @@ -0,0 +1,2 @@ +boto3 +Copyright 2013-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/RECORD b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/RECORD new file mode 100644 index 0000000..7069adf --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/RECORD @@ -0,0 +1,102 @@ +boto3-1.43.104.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +boto3-1.43.104.dist-info/LICENSE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174 +boto3-1.43.104.dist-info/METADATA,sha256=g1Sf7l2JJ7yabCtk4JBkuvjwrO5pHAPzITFtX_niRC4,6571 +boto3-1.43.104.dist-info/NOTICE,sha256=BPseYUhKeBDxugm7QrwByljJrzOSfXxaIVVuTE0cf6Q,83 +boto3-1.43.104.dist-info/RECORD,, +boto3-1.43.104.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91 +boto3-1.43.104.dist-info/top_level.txt,sha256=MP6_SI1GcPseXodd3Ykt5F_mCBsrUksiziLxjEZKGUU,6 +boto3/__init__.py,sha256=eYyxgRWh3FhBi7tS9MdJqxzLBEHxngBtEwr1Sjj5HQI,3368 +boto3/__pycache__/__init__.cpython-312.pyc,, +boto3/__pycache__/compat.cpython-312.pyc,, +boto3/__pycache__/crt.cpython-312.pyc,, +boto3/__pycache__/exceptions.cpython-312.pyc,, +boto3/__pycache__/session.cpython-312.pyc,, +boto3/__pycache__/utils.cpython-312.pyc,, +boto3/compat.py,sha256=YlQVXOvdJN39m13gS8WfVJqHbzCw3fElnbYrt1beNYg,3202 +boto3/crt.py,sha256=x_jZj-QMI5ZD0D2tu91ggqnsGp4pIYEHVHWqVlfckcw,7254 +boto3/data/cloudformation/2010-05-15/resources-1.json,sha256=5mFVKJVtbVoHyPdHSyNfZ5mpkgCAws5PhnveSu4qzdI,5110 +boto3/data/cloudwatch/2010-08-01/resources-1.json,sha256=q4AgE8F4pbscd-2U3NYSGAzK55zpMyOQGr83JUxbZXI,11690 +boto3/data/dynamodb/2012-08-10/resources-1.json,sha256=hBLa1Jt7bdT557U9A7UcSi8SCpONKzdbtDRTzjM1-Y0,3849 +boto3/data/ec2/2014-10-01/resources-1.json,sha256=tMG1AMYP2ksnPWY6-3l8DB-EhKsSNtAO9YHhvHqBKu0,68469 +boto3/data/ec2/2015-03-01/resources-1.json,sha256=tMG1AMYP2ksnPWY6-3l8DB-EhKsSNtAO9YHhvHqBKu0,68469 +boto3/data/ec2/2015-04-15/resources-1.json,sha256=tMG1AMYP2ksnPWY6-3l8DB-EhKsSNtAO9YHhvHqBKu0,68469 +boto3/data/ec2/2015-10-01/resources-1.json,sha256=SOfYX2c1KgvnxMO2FCdJpV42rJWNMwVhlFAXhvUPTzA,76564 +boto3/data/ec2/2016-04-01/resources-1.json,sha256=SOfYX2c1KgvnxMO2FCdJpV42rJWNMwVhlFAXhvUPTzA,76564 +boto3/data/ec2/2016-09-15/resources-1.json,sha256=SOfYX2c1KgvnxMO2FCdJpV42rJWNMwVhlFAXhvUPTzA,76564 +boto3/data/ec2/2016-11-15/resources-1.json,sha256=vx7YiL-sUvBFeo4SZ81G7Qa2Hy-y6xY4z2YlSx7_wEw,76922 +boto3/data/glacier/2012-06-01/resources-1.json,sha256=GT5qWQLGeXtrHgTDNG23Mrpyweg6O0Udgd139BuNTVs,19940 +boto3/data/iam/2010-05-08/resources-1.json,sha256=PsOT9yBqSJtluBFHCVRsg6k6Ly2VkSYODnYxSl0DVOc,50357 +boto3/data/s3/2006-03-01/resources-1.json,sha256=VeKALhMRqv7fyDHMLOM5_RzXUEuDdg_n6OIRi3sdB-o,37204 +boto3/data/sns/2010-03-31/resources-1.json,sha256=7zmKQhafgsRDu4U1yiw3NXHz-zJhHKrOmtuoYlxQP-s,9091 +boto3/data/sqs/2012-11-05/resources-1.json,sha256=LRIIr5BId3UDeuBfLn-vRiWsSZCM9_ynqdxF8uzHgy8,6545 +boto3/docs/__init__.py,sha256=QM5OK8DS8ugKJE2HfI1BY9gFRvj7p7RyGQVN1MLLZII,1844 +boto3/docs/__pycache__/__init__.cpython-312.pyc,, +boto3/docs/__pycache__/action.cpython-312.pyc,, +boto3/docs/__pycache__/attr.cpython-312.pyc,, +boto3/docs/__pycache__/base.cpython-312.pyc,, +boto3/docs/__pycache__/client.cpython-312.pyc,, +boto3/docs/__pycache__/collection.cpython-312.pyc,, +boto3/docs/__pycache__/docstring.cpython-312.pyc,, +boto3/docs/__pycache__/method.cpython-312.pyc,, +boto3/docs/__pycache__/resource.cpython-312.pyc,, +boto3/docs/__pycache__/service.cpython-312.pyc,, +boto3/docs/__pycache__/subresource.cpython-312.pyc,, +boto3/docs/__pycache__/utils.cpython-312.pyc,, +boto3/docs/__pycache__/waiter.cpython-312.pyc,, +boto3/docs/action.py,sha256=mCW9IUvZS1eStA0DrSqD1B_hZBz6YTdrQmbI5d2Jzbo,8122 +boto3/docs/attr.py,sha256=BnG3tR1KKQvvY58aeJiWQ5W5DiMnJ_9jUjmG6tDbFiU,2500 +boto3/docs/base.py,sha256=nOrQSCeUSIZPkn-I59o7CfjEthgdkpCt_rXtE9zQnXc,2103 +boto3/docs/client.py,sha256=HeNMMm0oKClpkzY1yyVT_JbSFkGF92n7Nnv2J5u3bJg,1003 +boto3/docs/collection.py,sha256=l8x2qW1HHnQsRDbR0yeUnaOGbgo2oAqxDyhyrbf5bes,11296 +boto3/docs/docstring.py,sha256=oPugaubdAXY6aNa-kXGI51lP1xE2s4AnfTsLhibf7-E,2511 +boto3/docs/method.py,sha256=MFX6L3SzXoL8Jz1fkuDLZ-OXMMnKuIBI2kkA8-NRvNg,2725 +boto3/docs/resource.py,sha256=jsFszXfdvnCX7hVxPyARqPU7H4c5D_zfUBi4N8vybZw,15134 +boto3/docs/service.py,sha256=5yOWLSrSVz0CO-5NBtI8Rfjx_SrzrvPQwlXvgWoo4uw,8514 +boto3/docs/subresource.py,sha256=WkEA4qmQbrN7Oz9ofypJOPfATXIzjwampAi2m430NbE,5766 +boto3/docs/utils.py,sha256=4FdurbZ61bAQFCRroBPk7ClHuqoYYajUJm1W33p4CSs,3019 +boto3/docs/waiter.py,sha256=EW0DF9XDtbAVzxUZj3kI20fCoTJJnF9ZjaBRrCILBws,5165 +boto3/dynamodb/__init__.py,sha256=GkSq-WxXWfVHu1SEcMrlJbzkfw9ACgF3UdCL6fPpTmY,562 +boto3/dynamodb/__pycache__/__init__.cpython-312.pyc,, +boto3/dynamodb/__pycache__/conditions.cpython-312.pyc,, +boto3/dynamodb/__pycache__/table.cpython-312.pyc,, +boto3/dynamodb/__pycache__/transform.cpython-312.pyc,, +boto3/dynamodb/__pycache__/types.cpython-312.pyc,, +boto3/dynamodb/conditions.py,sha256=AHaDXW0ri0hGMU_MevrElBl8K1Ls9Tm5NYW5UPbf7-4,15028 +boto3/dynamodb/table.py,sha256=ui8oL634pE6UdMiN6Mz50wAjRQkCF1plq9XsbUEgbWw,6340 +boto3/dynamodb/transform.py,sha256=JnW5ZzPIfxEcDszSvXKUZmp_1rw445tsddS3FG--JwA,12909 +boto3/dynamodb/types.py,sha256=ch0vIKaAYexjL42S_OJWyvjWMcb0UbNrmkKGcz76O3c,9541 +boto3/ec2/__init__.py,sha256=GkSq-WxXWfVHu1SEcMrlJbzkfw9ACgF3UdCL6fPpTmY,562 +boto3/ec2/__pycache__/__init__.cpython-312.pyc,, +boto3/ec2/__pycache__/createtags.cpython-312.pyc,, +boto3/ec2/__pycache__/deletetags.cpython-312.pyc,, +boto3/ec2/createtags.py,sha256=pUPJOYn7m0Jcch9UL-DEVGgbQHoyAemECPBhzyBx28c,1577 +boto3/ec2/deletetags.py,sha256=KaYcqSt8FFM_TW0g0pZ14qDjVnmRCPV0sMe6DprEtvo,1217 +boto3/examples/cloudfront.rst,sha256=K-sBWZxoLjABCZHrqAZs57cYefwPmDir03pm6PE_mh4,1390 +boto3/examples/s3.rst,sha256=a3mbSl7EbNbwd2GKYlP9nXrTHZItZVQRdMG3gamZtSo,5528 +boto3/exceptions.py,sha256=drkrpwrC-gQBeaoPlgq4FQFRjd4qom4JHeKhGpsGUm4,4187 +boto3/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +boto3/resources/__pycache__/__init__.cpython-312.pyc,, +boto3/resources/__pycache__/action.cpython-312.pyc,, +boto3/resources/__pycache__/base.cpython-312.pyc,, +boto3/resources/__pycache__/collection.cpython-312.pyc,, +boto3/resources/__pycache__/factory.cpython-312.pyc,, +boto3/resources/__pycache__/model.cpython-312.pyc,, +boto3/resources/__pycache__/params.cpython-312.pyc,, +boto3/resources/__pycache__/response.cpython-312.pyc,, +boto3/resources/action.py,sha256=vPfVHVgXiGqhwpgRSCC7lSsY3vGjgsSiYhXa14CMAqw,9600 +boto3/resources/base.py,sha256=c0dqK8P-9H3nHSIWmtj6eRSBvilXAdT4D0Pos2ARX-A,4929 +boto3/resources/collection.py,sha256=aVifZoUVHUarGF9S4Ih8qBUfdqKKOBAEd0BaISKaLio,19113 +boto3/resources/factory.py,sha256=T76teDpkRJfbsNejkCpRUK3k1N16LflkR5BByf2ofns,22707 +boto3/resources/model.py,sha256=RTE9rs3vKAvxNI2LMYkOfzZ8Cgy2-zpS4XXjuvd9I3g,20340 +boto3/resources/params.py,sha256=u0D2-il6JbXpUus8NDj3K9ww3047l89lF69QnxwIX5A,6107 +boto3/resources/response.py,sha256=aIATkyer_rl5qsp-OFCxe36whvY4JzjgNc9qN-vYMxg,11638 +boto3/s3/__init__.py,sha256=GkSq-WxXWfVHu1SEcMrlJbzkfw9ACgF3UdCL6fPpTmY,562 +boto3/s3/__pycache__/__init__.cpython-312.pyc,, +boto3/s3/__pycache__/constants.cpython-312.pyc,, +boto3/s3/__pycache__/inject.cpython-312.pyc,, +boto3/s3/__pycache__/transfer.cpython-312.pyc,, +boto3/s3/constants.py,sha256=L0K_cNKUKZRwVNQpBaelkI1Tc-brOCkeW-JtgK7YhJo,718 +boto3/s3/inject.py,sha256=ErLQfQV715eeD2rqk2sJx-kyoes6WVuJk4l83RFe0BE,30629 +boto3/s3/transfer.py,sha256=dlUHApjiqnsShxA7DJ-uWRZVsGk2hkGwsFmriJBCmgE,19010 +boto3/session.py,sha256=wRNiPqv0ahQ4rzCUBRPN1UuU7ebXtolHQ9X34dGLmng,22106 +boto3/utils.py,sha256=8secEXlszf5Drh4V_IuhUyCSuzY3fHEtO_-Wn-RPFxo,3005 diff --git a/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/WHEEL b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/WHEEL new file mode 100644 index 0000000..dcfdc6e --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.1.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/top_level.txt new file mode 100644 index 0000000..30ddf82 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3-1.43.104.dist-info/top_level.txt @@ -0,0 +1 @@ +boto3 diff --git a/venv/lib/python3.12/site-packages/boto3/__init__.py b/venv/lib/python3.12/site-packages/boto3/__init__.py new file mode 100644 index 0000000..004d380 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/__init__.py @@ -0,0 +1,107 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import logging +from logging import NullHandler + +from boto3.compat import _warn_deprecated_python +from boto3.session import Session + +__author__ = 'Amazon Web Services' +__version__ = '1.43.104' + + +# The default Boto3 session; autoloaded when needed. +DEFAULT_SESSION = None + + +def setup_default_session(**kwargs): + """ + Set up a default session, passing through any parameters to the session + constructor. There is no need to call this unless you wish to pass custom + parameters, because a default session will be created for you. + """ + global DEFAULT_SESSION + DEFAULT_SESSION = Session(**kwargs) + + +def set_stream_logger(name='boto3', level=logging.DEBUG, format_string=None): + """ + Add a stream handler for the given name and level to the logging module. + By default, this logs all boto3 messages to ``stdout``. + + >>> import boto3 + >>> boto3.set_stream_logger('boto3.resources', logging.INFO) + + For debugging purposes a good choice is to set the stream logger to ``''`` + which is equivalent to saying "log everything". + + .. WARNING:: + Be aware that when logging anything from ``'botocore'`` the full wire + trace will appear in your logs. If your payloads contain sensitive data + this should not be used in production. + + :type name: string + :param name: Log name + :type level: int + :param level: Logging level, e.g. ``logging.INFO`` + :type format_string: str + :param format_string: Log message format + """ + if format_string is None: + format_string = "%(asctime)s %(name)s [%(levelname)s] %(message)s" + + logger = logging.getLogger(name) + logger.setLevel(level) + handler = logging.StreamHandler() + handler.setLevel(level) + formatter = logging.Formatter(format_string) + handler.setFormatter(formatter) + logger.addHandler(handler) + + +def _get_default_session(): + """ + Get the default session, creating one if needed. + + :rtype: :py:class:`~boto3.session.Session` + :return: The default session + """ + if DEFAULT_SESSION is None: + setup_default_session() + _warn_deprecated_python() + + return DEFAULT_SESSION + + +def client(*args, **kwargs): + """ + Create a low-level service client by name using the default session. + + See :py:meth:`boto3.session.Session.client`. + """ + return _get_default_session().client(*args, **kwargs) + + +def resource(*args, **kwargs): + """ + Create a resource service client by name using the default session. + + See :py:meth:`boto3.session.Session.resource`. + """ + return _get_default_session().resource(*args, **kwargs) + + +# Set up do-nothing logging like a library is supposed to. +# https://docs.python.org/3.3/howto/logging.html#configuring-logging-for-a-library +logging.getLogger('boto3').addHandler(NullHandler()) diff --git a/venv/lib/python3.12/site-packages/boto3/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..621e85d Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/__pycache__/compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/__pycache__/compat.cpython-312.pyc new file mode 100644 index 0000000..8e1efcf Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/__pycache__/compat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/__pycache__/crt.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/__pycache__/crt.cpython-312.pyc new file mode 100644 index 0000000..5e967ea Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/__pycache__/crt.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..2adc830 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/__pycache__/session.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/__pycache__/session.cpython-312.pyc new file mode 100644 index 0000000..ae8cfbe Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/__pycache__/session.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..752f263 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/compat.py b/venv/lib/python3.12/site-packages/boto3/compat.py new file mode 100644 index 0000000..44a6431 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/compat.py @@ -0,0 +1,97 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import sys +import os +import errno +import socket +import warnings + +from boto3.exceptions import PythonDeprecationWarning + +from s3transfer.manager import TransferConfig + +# In python3, socket.error is OSError, which is too general +# for what we want (i.e FileNotFoundError is a subclass of OSError). +# In py3 all the socket related errors are in a newly created +# ConnectionError +SOCKET_ERROR = ConnectionError + +_APPEND_MODE_CHAR = 'a' + +import collections.abc as collections_abc + + +TRANSFER_CONFIG_SUPPORTS_CRT = hasattr(TransferConfig, 'UNSET_DEFAULT') + + +if sys.platform.startswith('win'): + def rename_file(current_filename, new_filename): + try: + os.remove(new_filename) + except OSError as e: + if not e.errno == errno.ENOENT: + # We only want to a ignore trying to remove + # a file that does not exist. If it fails + # for any other reason we should be propagating + # that exception. + raise + os.rename(current_filename, new_filename) +else: + rename_file = os.rename + + +def filter_python_deprecation_warnings(): + """ + Invoking this filter acknowledges your runtime will soon be deprecated + at which time you will stop receiving all updates to your client. + """ + warnings.filterwarnings( + 'ignore', + message=".*Boto3 will no longer support Python.*", + category=PythonDeprecationWarning, + module=r".*boto3\.compat" + ) + + +def _warn_deprecated_python(): + """Use this template for future deprecation campaigns as needed.""" + py_39_params = { + 'date': 'April 29, 2026', + 'blog_link': ( + 'https://aws.amazon.com/blogs/developer/' + 'python-support-policy-updates-for-aws-sdks-and-tools/' + ) + } + deprecated_versions = { + # Example template for future deprecations + (3, 9): py_39_params, + } + py_version = sys.version_info[:2] + + if py_version in deprecated_versions: + params = deprecated_versions[py_version] + warning = ( + "Boto3 will no longer support Python {}.{} " + "starting {}. To continue receiving service updates, " + "bug fixes, and security updates please upgrade to Python 3.10 or " + "later. More information can be found here: {}" + ).format(py_version[0], py_version[1], params['date'], params['blog_link']) + warnings.warn(warning, PythonDeprecationWarning) + + +def is_append_mode(fileobj): + return ( + hasattr(fileobj, 'mode') and + isinstance(fileobj.mode, str) and + _APPEND_MODE_CHAR in fileobj.mode + ) diff --git a/venv/lib/python3.12/site-packages/boto3/crt.py b/venv/lib/python3.12/site-packages/boto3/crt.py new file mode 100644 index 0000000..fb30401 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/crt.py @@ -0,0 +1,217 @@ +# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +""" +This file contains private functionality for interacting with the AWS +Common Runtime library (awscrt) in boto3. + +All code contained within this file is for internal usage within this +project and is not intended for external consumption. All interfaces +contained within are subject to abrupt breaking changes. +""" + +import logging +import threading + +import botocore.exceptions +from botocore.session import Session +from s3transfer.crt import ( + BotocoreCRTCredentialsWrapper, + BotocoreCRTRequestSerializer, + CRTTransferManager, + acquire_crt_s3_process_lock, + create_s3_crt_client, +) + +from boto3.compat import TRANSFER_CONFIG_SUPPORTS_CRT +from boto3.exceptions import InvalidCrtTransferConfigError +from boto3.s3.constants import CRT_TRANSFER_CLIENT + +logger = logging.getLogger(__name__) + +# Singletons for CRT-backed transfers +CRT_S3_CLIENT = None +BOTOCORE_CRT_SERIALIZER = None + +CLIENT_CREATION_LOCK = threading.Lock() +PROCESS_LOCK_NAME = 'boto3' + + +_ALLOWED_CRT_TRANSFER_CONFIG_OPTIONS = { + 'multipart_threshold', + 'max_concurrency', + 'max_request_concurrency', + 'multipart_chunksize', + 'preferred_transfer_client', +} + + +def _create_crt_client(session, config, region_name, cred_provider): + """Create a CRT S3 Client for file transfer. + + Instantiating many of these may lead to degraded performance or + system resource exhaustion. + """ + create_crt_client_kwargs = { + 'region': region_name, + 'use_ssl': True, + 'crt_credentials_provider': cred_provider, + } + return create_s3_crt_client(**create_crt_client_kwargs) + + +def _create_crt_request_serializer(session, region_name): + return BotocoreCRTRequestSerializer( + session, {'region_name': region_name, 'endpoint_url': None} + ) + + +def _create_crt_s3_client( + session, config, region_name, credentials, lock, **kwargs +): + """Create boto3 wrapper class to manage crt lock reference and S3 client.""" + cred_wrapper = BotocoreCRTCredentialsWrapper(credentials) + cred_provider = cred_wrapper.to_crt_credentials_provider() + return CRTS3Client( + _create_crt_client(session, config, region_name, cred_provider), + lock, + region_name, + cred_wrapper, + ) + + +def _initialize_crt_transfer_primatives(client, config): + lock = acquire_crt_s3_process_lock(PROCESS_LOCK_NAME) + if lock is None: + # If we're unable to acquire the lock, we cannot + # use the CRT in this process and should default to + # the classic s3transfer manager. + return None, None + + session = Session() + region_name = client.meta.region_name + credentials = client._get_credentials() + + serializer = _create_crt_request_serializer(session, region_name) + s3_client = _create_crt_s3_client( + session, config, region_name, credentials, lock + ) + return serializer, s3_client + + +def get_crt_s3_client(client, config): + global CRT_S3_CLIENT + global BOTOCORE_CRT_SERIALIZER + + with CLIENT_CREATION_LOCK: + if CRT_S3_CLIENT is None: + serializer, s3_client = _initialize_crt_transfer_primatives( + client, config + ) + BOTOCORE_CRT_SERIALIZER = serializer + CRT_S3_CLIENT = s3_client + + return CRT_S3_CLIENT + + +class CRTS3Client: + """ + This wrapper keeps track of our underlying CRT client, the lock used to + acquire it and the region we've used to instantiate the client. + + Due to limitations in the existing CRT interfaces, we can only make calls + in a single region and does not support redirects. We track the region to + ensure we don't use the CRT client when a successful request cannot be made. + """ + + def __init__(self, crt_client, process_lock, region, cred_provider): + self.crt_client = crt_client + self.process_lock = process_lock + self.region = region + self.cred_provider = cred_provider + + +def is_crt_compatible_request(client, crt_s3_client): + """ + Boto3 client must use same signing region and credentials + as the CRT_S3_CLIENT singleton. Otherwise fallback to classic. + """ + if crt_s3_client is None: + return False + + boto3_creds = client._get_credentials() + if boto3_creds is None: + return False + + is_same_identity = compare_identity( + boto3_creds.get_frozen_credentials(), crt_s3_client.cred_provider + ) + is_same_region = client.meta.region_name == crt_s3_client.region + return is_same_region and is_same_identity + + +def compare_identity(boto3_creds, crt_s3_creds): + try: + crt_creds = crt_s3_creds() + except botocore.exceptions.NoCredentialsError: + return False + + is_matching_identity = ( + boto3_creds.access_key == crt_creds.access_key_id + and boto3_creds.secret_key == crt_creds.secret_access_key + and boto3_creds.token == crt_creds.session_token + ) + return is_matching_identity + + +def _validate_crt_transfer_config(config): + if config is None: + return + # CRT client can also be configured via `AUTO_RESOLVE_TRANSFER_CLIENT` + # but it predates this validation. We only validate against CRT client + # configured via `CRT_TRANSFER_CLIENT` to preserve compatibility. + if config.preferred_transfer_client != CRT_TRANSFER_CLIENT: + return + invalid_crt_args = [] + for param in config.DEFAULTS.keys(): + val = config.get_deep_attr(param) + if ( + param not in _ALLOWED_CRT_TRANSFER_CONFIG_OPTIONS + and val is not config.UNSET_DEFAULT + ): + invalid_crt_args.append(param) + if len(invalid_crt_args) > 0: + raise InvalidCrtTransferConfigError( + "The following transfer config options are invalid " + "when preferred_transfer_client is set to crt: " + f"{', '.join(invalid_crt_args)}`" + ) + + +def create_crt_transfer_manager(client, config): + """Create a CRTTransferManager for optimized data transfer.""" + crt_s3_client = get_crt_s3_client(client, config) + if is_crt_compatible_request(client, crt_s3_client): + crt_transfer_manager_kwargs = { + 'crt_s3_client': crt_s3_client.crt_client, + 'crt_request_serializer': BOTOCORE_CRT_SERIALIZER, + } + if TRANSFER_CONFIG_SUPPORTS_CRT: + _validate_crt_transfer_config(config) + crt_transfer_manager_kwargs['config'] = config + if not TRANSFER_CONFIG_SUPPORTS_CRT and config: + logger.warning( + 'Using TransferConfig with CRT client requires ' + 's3transfer >= 0.16.0, configured values will be ignored.' + ) + return CRTTransferManager(**crt_transfer_manager_kwargs) + return None diff --git a/venv/lib/python3.12/site-packages/boto3/data/cloudformation/2010-05-15/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/cloudformation/2010-05-15/resources-1.json new file mode 100644 index 0000000..fd43937 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/cloudformation/2010-05-15/resources-1.json @@ -0,0 +1,195 @@ +{ + "service": { + "actions": { + "CreateStack": { + "request": { "operation": "CreateStack" }, + "resource": { + "type": "Stack", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "StackName" } + ] + } + } + }, + "has": { + "Event": { + "resource": { + "type": "Event", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Stack": { + "resource": { + "type": "Stack", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + } + }, + "hasMany": { + "Stacks": { + "request": { "operation": "DescribeStacks" }, + "resource": { + "type": "Stack", + "identifiers": [ + { "target": "Name", "source": "response", "path": "Stacks[].StackName" } + ], + "path": "Stacks[]" + } + } + } + }, + "resources": { + "Event": { + "identifiers": [ + { + "name": "Id", + "memberName": "EventId" + } + ], + "shape": "StackEvent" + }, + "Stack": { + "identifiers": [ + { + "name": "Name", + "memberName": "StackName" + } + ], + "shape": "Stack", + "load": { + "request": { + "operation": "DescribeStacks", + "params": [ + { "target": "StackName", "source": "identifier", "name": "Name" } + ] + }, + "path": "Stacks[0]" + }, + "actions": { + "CancelUpdate": { + "request": { + "operation": "CancelUpdateStack", + "params": [ + { "target": "StackName", "source": "identifier", "name": "Name" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteStack", + "params": [ + { "target": "StackName", "source": "identifier", "name": "Name" } + ] + } + }, + "Update": { + "request": { + "operation": "UpdateStack", + "params": [ + { "target": "StackName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "has": { + "Resource": { + "resource": { + "type": "StackResource", + "identifiers": [ + { "target": "StackName", "source": "identifier", "name": "Name" }, + { "target": "LogicalId", "source": "input" } + ] + } + } + }, + "hasMany": { + "Events": { + "request": { + "operation": "DescribeStackEvents", + "params": [ + { "target": "StackName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Event", + "identifiers": [ + { "target": "Id", "source": "response", "path": "StackEvents[].EventId" } + ], + "path": "StackEvents[]" + } + }, + "ResourceSummaries": { + "request": { + "operation": "ListStackResources", + "params": [ + { "target": "StackName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "StackResourceSummary", + "identifiers": [ + { "target": "LogicalId", "source": "response", "path": "StackResourceSummaries[].LogicalResourceId" }, + { "target": "StackName", "source": "requestParameter", "path": "StackName" } + ], + "path": "StackResourceSummaries[]" + } + } + } + }, + "StackResource": { + "identifiers": [ + { "name": "StackName" }, + { + "name": "LogicalId", + "memberName": "LogicalResourceId" + } + ], + "shape": "StackResourceDetail", + "load": { + "request": { + "operation": "DescribeStackResource", + "params": [ + { "target": "LogicalResourceId", "source": "identifier", "name": "LogicalId" }, + { "target": "StackName", "source": "identifier", "name": "StackName" } + ] + }, + "path": "StackResourceDetail" + }, + "has": { + "Stack": { + "resource": { + "type": "Stack", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "StackName" } + ] + } + } + } + }, + "StackResourceSummary": { + "identifiers": [ + { "name": "StackName" }, + { + "name": "LogicalId", + "memberName": "LogicalResourceId" + } + ], + "shape": "StackResourceSummary", + "has": { + "Resource": { + "resource": { + "type": "StackResource", + "identifiers": [ + { "target": "LogicalId", "source": "identifier", "name": "LogicalId" }, + { "target": "StackName", "source": "identifier", "name": "StackName" } + ] + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/cloudwatch/2010-08-01/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/cloudwatch/2010-08-01/resources-1.json new file mode 100644 index 0000000..e0746d0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/cloudwatch/2010-08-01/resources-1.json @@ -0,0 +1,334 @@ +{ + "service": { + "has": { + "Alarm": { + "resource": { + "type": "Alarm", + "identifiers": [ + { + "target": "Name", + "source": "input" + } + ] + } + }, + "Metric": { + "resource": { + "type": "Metric", + "identifiers": [ + { + "target": "Namespace", + "source": "input" + }, + { + "target": "Name", + "source": "input" + } + ] + } + } + }, + "hasMany": { + "Alarms": { + "request": { "operation": "DescribeAlarms" }, + "resource": { + "type": "Alarm", + "identifiers": [ + { + "target": "Name", + "source": "response", + "path": "MetricAlarms[].AlarmName" + } + ], + "path": "MetricAlarms[]" + } + }, + "Metrics": { + "request": { "operation": "ListMetrics" }, + "resource": { + "type": "Metric", + "identifiers": [ + { + "target": "Namespace", + "source": "response", + "path": "Metrics[].Namespace" + }, + { + "target": "Name", + "source": "response", + "path": "Metrics[].MetricName" + } + ], + "path": "Metrics[]" + } + } + } + }, + "resources": { + "Alarm": { + "identifiers": [ + { + "name": "Name", + "memberName": "AlarmName" + } + ], + "shape": "MetricAlarm", + "load": { + "request": { + "operation": "DescribeAlarms", + "params": [ + { + "target": "AlarmNames[0]", + "source": "identifier", + "name": "Name" + } + ] + }, + "path": "MetricAlarms[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteAlarms", + "params": [ + { + "target": "AlarmNames[0]", + "source": "identifier", + "name": "Name" + } + ] + } + }, + "DescribeHistory": { + "request": { + "operation": "DescribeAlarmHistory", + "params": [ + { + "target": "AlarmName", + "source": "identifier", + "name": "Name" + } + ] + } + }, + "DisableActions": { + "request": { + "operation": "DisableAlarmActions", + "params": [ + { + "target": "AlarmNames[0]", + "source": "identifier", + "name": "Name" + } + ] + } + }, + "EnableActions": { + "request": { + "operation": "EnableAlarmActions", + "params": [ + { + "target": "AlarmNames[0]", + "source": "identifier", + "name": "Name" + } + ] + } + }, + "SetState": { + "request": { + "operation": "SetAlarmState", + "params": [ + { + "target": "AlarmName", + "source": "identifier", + "name": "Name" + } + ] + } + } + }, + "batchActions": { + "Delete": { + "request": { + "operation": "DeleteAlarms", + "params": [ + { + "target": "AlarmNames[]", + "source": "identifier", + "name": "Name" + } + ] + } + }, + "DisableActions": { + "request": { + "operation": "DisableAlarmActions", + "params": [ + { + "target": "AlarmNames[]", + "source": "identifier", + "name": "Name" + } + ] + } + }, + "EnableActions": { + "request": { + "operation": "EnableAlarmActions", + "params": [ + { + "target": "AlarmNames[]", + "source": "identifier", + "name": "Name" + } + ] + } + } + }, + "has": { + "Metric": { + "resource": { + "type": "Metric", + "identifiers": [ + { + "target": "Namespace", + "source": "data", + "path": "Namespace" + }, + { + "target": "Name", + "source": "data", + "path": "MetricName" + } + ] + } + } + } + }, + "Metric": { + "identifiers": [ + { + "name": "Namespace", + "memberName": "Namespace" + }, + { + "name": "Name", + "memberName": "MetricName" + } + ], + "shape": "Metric", + "load": { + "request": { + "operation": "ListMetrics", + "params": [ + { + "target": "MetricName", + "source": "identifier", + "name": "Name" + }, + { + "target": "Namespace", + "source": "identifier", + "name": "Namespace" + } + ] + }, + "path": "Metrics[0]" + }, + "actions": { + "GetStatistics": { + "request": { + "operation": "GetMetricStatistics", + "params": [ + { + "target": "Namespace", + "source": "identifier", + "name": "Namespace" + }, + { + "target": "MetricName", + "source": "identifier", + "name": "Name" + } + ] + } + }, + "PutAlarm": { + "request": { + "operation": "PutMetricAlarm", + "params": [ + { + "target": "Namespace", + "source": "identifier", + "name": "Namespace" + }, + { + "target": "MetricName", + "source": "identifier", + "name": "Name" + } + ] + }, + "resource": { + "type": "Alarm", + "identifiers": [ + { + "target": "Name", + "source": "requestParameter", + "path": "AlarmName" + } + ] + } + }, + "PutData": { + "request": { + "operation": "PutMetricData", + "params": [ + { + "target": "Namespace", + "source": "identifier", + "name": "Namespace" + }, + { + "target": "MetricData[].MetricName", + "source": "identifier", + "name": "Name" + } + ] + } + } + }, + "hasMany": { + "Alarms": { + "request": { + "operation": "DescribeAlarmsForMetric", + "params": [ + { + "target": "Namespace", + "source": "identifier", + "name": "Namespace" + }, + { + "target": "MetricName", + "source": "identifier", + "name": "Name" + } + ] + }, + "resource": { + "type": "Alarm", + "identifiers": [ + { + "target": "Name", + "source": "response", + "path": "MetricAlarms[].AlarmName" + } + ], + "path": "MetricAlarms[]" + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/dynamodb/2012-08-10/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/dynamodb/2012-08-10/resources-1.json new file mode 100644 index 0000000..b79994e --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/dynamodb/2012-08-10/resources-1.json @@ -0,0 +1,150 @@ +{ + "service": { + "actions": { + "BatchGetItem": { + "request": { "operation": "BatchGetItem" } + }, + "BatchWriteItem": { + "request": { "operation": "BatchWriteItem" } + }, + "CreateTable": { + "request": { "operation": "CreateTable" }, + "resource": { + "type": "Table", + "identifiers": [ + { "target": "Name", "source": "response", "path": "TableDescription.TableName" } + ], + "path": "TableDescription" + } + } + }, + "has": { + "Table": { + "resource": { + "type": "Table", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + } + }, + "hasMany": { + "Tables": { + "request": { "operation": "ListTables" }, + "resource": { + "type": "Table", + "identifiers": [ + { "target": "Name", "source": "response", "path": "TableNames[]" } + ] + } + } + } + }, + "resources": { + "Table": { + "identifiers": [ + { + "name": "Name", + "memberName": "TableName" + } + ], + "shape": "TableDescription", + "load": { + "request": { + "operation": "DescribeTable", + "params": [ + { "target": "TableName", "source": "identifier", "name": "Name" } + ] + }, + "path": "Table" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteTable", + "params": [ + { "target": "TableName", "source": "identifier", "name": "Name" } + ] + } + }, + "DeleteItem": { + "request": { + "operation": "DeleteItem", + "params": [ + { "target": "TableName", "source": "identifier", "name": "Name" } + ] + } + }, + "GetItem": { + "request": { + "operation": "GetItem", + "params": [ + { "target": "TableName", "source": "identifier", "name": "Name" } + ] + } + }, + "PutItem": { + "request": { + "operation": "PutItem", + "params": [ + { "target": "TableName", "source": "identifier", "name": "Name" } + ] + } + }, + "Query": { + "request": { + "operation": "Query", + "params": [ + { "target": "TableName", "source": "identifier", "name": "Name" } + ] + } + }, + "Scan": { + "request": { + "operation": "Scan", + "params": [ + { "target": "TableName", "source": "identifier", "name": "Name" } + ] + } + }, + "Update": { + "request": { + "operation": "UpdateTable", + "params": [ + { "target": "TableName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Table", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "Name" } + ], + "path": "TableDescription" + } + }, + "UpdateItem": { + "request": { + "operation": "UpdateItem", + "params": [ + { "target": "TableName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "waiters":{ + "Exists": { + "waiterName": "TableExists", + "params": [ + { "target": "TableName", "source": "identifier", "name": "Name" } + ] + }, + "NotExists": { + "waiterName": "TableNotExists", + "params": [ + { "target": "TableName", "source": "identifier", "name": "Name" } + ] + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/ec2/2014-10-01/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/ec2/2014-10-01/resources-1.json new file mode 100644 index 0000000..8ccf160 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/ec2/2014-10-01/resources-1.json @@ -0,0 +1,2289 @@ +{ + "service": { + "actions": { + "CreateDhcpOptions": { + "request": { "operation": "CreateDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions.DhcpOptionsId" } + ], + "path": "DhcpOptions" + } + }, + "CreateInstances": { + "request": { "operation": "RunInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateInternetGateway": { + "request": { "operation": "CreateInternetGateway" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateway.InternetGatewayId" } + ], + "path": "InternetGateway" + } + }, + "CreateKeyPair": { + "request": { "operation": "CreateKeyPair" }, + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ] + } + }, + "CreateNetworkAcl": { + "request": { "operation": "CreateNetworkAcl" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateNetworkInterface": { + "request": { "operation": "CreateNetworkInterface" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreatePlacementGroup": { + "request": { "operation": "CreatePlacementGroup" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "GroupName" } + ] + } + }, + "CreateRouteTable": { + "request": { "operation": "CreateRouteTable" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { "operation": "CreateSecurityGroup" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSnapshot": { + "request": { "operation": "CreateSnapshot" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateSubnet": { + "request": { "operation": "CreateSubnet" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { "operation": "CreateTags" }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "requestParameter", "path": "Resources[]" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "CreateVolume": { + "request": { "operation": "CreateVolume" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VolumeId" } + ], + "path": "@" + } + }, + "CreateVpc": { + "request": { "operation": "CreateVpc" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpc.VpcId" } + ], + "path": "Vpc" + } + }, + "CreateVpcPeeringConnection": { + "request": { "operation": "CreateVpcPeeringConnection" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + }, + "DisassociateRouteTable": { + "request": { "operation": "DisassociateRouteTable" } + }, + "ImportKeyPair": { + "request": { "operation": "ImportKeyPair" }, + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ] + } + }, + "RegisterImage": { + "request": { "operation": "RegisterImage" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Instance": { + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "InternetGateway": { + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "NetworkAcl": { + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "NetworkInterface": { + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "RouteTableAssociation": { + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "SecurityGroup": { + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Snapshot": { + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "VpcPeeringConnection": { + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + } + }, + "hasMany": { + "DhcpOptionsSets": { + "request": { "operation": "DescribeDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions[].DhcpOptionsId" } + ], + "path": "DhcpOptions[]" + } + }, + "Images": { + "request": { "operation": "DescribeImages" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Images[].ImageId" } + ], + "path": "Images[]" + } + }, + "Instances": { + "request": { "operation": "DescribeInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { "operation": "DescribeInternetGateways" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "KeyPairs": { + "request": { "operation": "DescribeKeyPairs" }, + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyPairs[].KeyName" } + ], + "path": "KeyPairs[]" + } + }, + "NetworkAcls": { + "request": { "operation": "DescribeNetworkAcls" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { "operation": "DescribeNetworkInterfaces" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "PlacementGroups": { + "request": { "operation": "DescribePlacementGroups" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "response", "path": "PlacementGroups[].GroupName" } + ], + "path": "PlacementGroups[]" + } + }, + "RouteTables": { + "request": { "operation": "DescribeRouteTables" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { "operation": "DescribeSecurityGroups" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Snapshots": { + "request": { "operation": "DescribeSnapshots" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + }, + "Subnets": { + "request": { "operation": "DescribeSubnets" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + }, + "Volumes": { + "request": { "operation": "DescribeVolumes" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + }, + "VpcPeeringConnections": { + "request": { "operation": "DescribeVpcPeeringConnections" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Vpcs": { + "request": { "operation": "DescribeVpcs" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpcs[].VpcId" } + ], + "path": "Vpcs[]" + } + } + } + }, + "resources": { + "DhcpOptions": { + "identifiers": [ + { + "name": "Id", + "memberName": "DhcpOptionsId" + } + ], + "shape": "DhcpOptions", + "load": { + "request": { + "operation": "DescribeDhcpOptions", + "params": [ + { "target": "DhcpOptionsIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "DhcpOptions[0]" + }, + "actions": { + "AssociateWithVpc": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Image": { + "identifiers": [ + { + "name": "Id", + "memberName": "ImageId" + } + ], + "shape": "Image", + "load": { + "request": { + "operation": "DescribeImages", + "params": [ + { "target": "ImageIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Images[0]" + }, + "actions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Deregister": { + "request": { + "operation": "DeregisterImage", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Instance": { + "identifiers": [ + { + "name": "Id", + "memberName": "InstanceId" + } + ], + "shape": "Instance", + "load": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Reservations[0].Instances[0]" + }, + "actions": { + "AttachClassicLinkVpc": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachVolume": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ConsoleOutput": { + "request": { + "operation": "GetConsoleOutput", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateImage": { + "request": { + "operation": "CreateImage", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkVpc": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachVolume": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "PasswordData": { + "request": { + "operation": "GetPasswordData", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ReportStatus": { + "request": { + "operation": "ReportInstanceStatus", + "params": [ + { "target": "Instances[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetKernel": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "kernel" } + ] + } + }, + "ResetRamdisk": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "ramdisk" } + ] + } + }, + "ResetSourceDestCheck": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "sourceDestCheck" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "batchActions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "InstanceExists", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Running": { + "waiterName": "InstanceRunning", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Stopped": { + "waiterName": "InstanceStopped", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Terminated": { + "waiterName": "InstanceTerminated", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + } + }, + "has": { + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "data", "path": "ImageId" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "data", "path": "KeyName" } + ] + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "data", "path": "Placement.GroupName" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Volumes": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.instance-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + } + } + }, + "InternetGateway": { + "identifiers": [ + { + "name": "Id", + "memberName": "InternetGatewayId" + } + ], + "shape": "InternetGateway", + "load": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "InternetGatewayIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "InternetGateways[0]" + }, + "actions": { + "AttachToVpc": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromVpc": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "KeyPair": { + "identifiers": [ + { + "name": "Name", + "memberName": "KeyName" + } + ], + "shape": "KeyPairInfo", + "load": { + "request": { + "operation": "DescribeKeyPairs", + "params": [ + { "target": "KeyNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "KeyPairs[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteKeyPair", + "params": [ + { "target": "KeyName", "source": "identifier", "name": "Name" } + ] + } + } + } + }, + "NetworkAcl": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkAclId" + } + ], + "shape": "NetworkAcl", + "load": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "NetworkAclIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkAcls[0]" + }, + "actions": { + "CreateEntry": { + "request": { + "operation": "CreateNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkAcl", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "DeleteEntry": { + "request": { + "operation": "DeleteNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceAssociation": { + "request": { + "operation": "ReplaceNetworkAclAssociation", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceEntry": { + "request": { + "operation": "ReplaceNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "NetworkInterface": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkInterfaceId" + } + ], + "shape": "NetworkInterface", + "load": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "NetworkInterfaceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkInterfaces[0]" + }, + "actions": { + "AssignPrivateIpAddresses": { + "request": { + "operation": "AssignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Attach": { + "request": { + "operation": "AttachNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Detach": { + "request": { + "operation": "DetachNetworkInterface", + "params": [ + { "target": "AttachmentId", "source": "data", "path": "Attachment.AttachmentId" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "UnassignPrivateIpAddresses": { + "request": { + "operation": "UnassignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "PlacementGroup": { + "identifiers": [ + { + "name": "Name", + "memberName": "GroupName" + } + ], + "shape": "PlacementGroup", + "load": { + "request": { + "operation": "DescribePlacementGroups", + "params": [ + { "target": "GroupNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "PlacementGroups[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeletePlacementGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "placement-group-name" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + } + } + }, + "RouteTable": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableId" + } + ], + "shape": "RouteTable", + "load": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "RouteTableIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "RouteTables[0]" + }, + "actions": { + "AssociateWithSubnet": { + "request": { + "operation": "AssociateRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "AssociationId" } + ] + } + }, + "CreateRoute": { + "request": { + "operation": "CreateRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Associations": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "RouteTableIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[0].Associations[].RouteTableAssociationId" } + ], + "path": "RouteTables[0].Associations[]" + } + } + } + }, + "RouteTableAssociation": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableAssociationId" + } + ], + "shape": "RouteTableAssociation", + "actions": { + "Delete": { + "request": { + "operation": "DisassociateRouteTable", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceSubnet": { + "request": { + "operation": "ReplaceRouteTableAssociation", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NewAssociationId" } + ] + } + } + }, + "has": { + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RouteTableId" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + } + } + }, + "SecurityGroup": { + "identifiers": [ + { + "name": "Id", + "memberName": "GroupId" + } + ], + "shape": "SecurityGroup", + "load": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "GroupIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "SecurityGroups[0]" + }, + "actions": { + "AuthorizeEgress": { + "request": { + "operation": "AuthorizeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "AuthorizeIngress": { + "request": { + "operation": "AuthorizeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSecurityGroup", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeEgress": { + "request": { + "operation": "RevokeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeIngress": { + "request": { + "operation": "RevokeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Snapshot": { + "identifiers": [ + { + "name": "Id", + "memberName": "SnapshotId" + } + ], + "shape": "Snapshot", + "load": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "SnapshotIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Snapshots[0]" + }, + "actions": { + "Copy": { + "request": { + "operation": "CopySnapshot", + "params": [ + { "target": "SourceSnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSnapshot", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifySnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Completed": { + "waiterName": "SnapshotCompleted", + "params": [ + { "target": "SnapshotIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Snapshots[]" + } + }, + "has": { + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VolumeId" } + ] + } + } + } + }, + "Subnet": { + "identifiers": [ + { + "name": "Id", + "memberName": "SubnetId" + } + ], + "shape": "Subnet", + "load": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "SubnetIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Subnets[0]" + }, + "actions": { + "CreateInstances": { + "request": { + "operation": "RunInstances", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateNetworkInterface": { + "request": { + "operation": "CreateNetworkInterface", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSubnet", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + } + } + }, + "Tag": { + "identifiers": [ + { + "name": "ResourceId", + "memberName": "ResourceId" + }, + { + "name": "Key", + "memberName": "Key" + }, + { + "name": "Value", + "memberName": "Value" + } + ], + "shape": "TagDescription", + "load": { + "request": { + "operation": "DescribeTags", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "key" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Key" }, + { "target": "Filters[1].Name", "source": "string", "value": "value" }, + { "target": "Filters[1].Values[0]", "source": "identifier", "name": "Value" } + ] + }, + "path": "Tags[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[0].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[0].Value", "source": "identifier", "name": "Value" } + ] + } + } + }, + "batchActions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[*].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[*].Value", "source": "identifier", "name": "Value" } + ] + } + } + } + }, + "Volume": { + "identifiers": [ + { + "name": "Id", + "memberName": "VolumeId" + } + ], + "shape": "Volume", + "load": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Volumes[0]" + }, + "actions": { + "AttachToInstance": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateSnapshot": { + "request": { + "operation": "CreateSnapshot", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeStatus": { + "request": { + "operation": "DescribeVolumeStatus", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromInstance": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableIo": { + "request": { + "operation": "EnableVolumeIO", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "hasMany": { + "Snapshots": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "volume-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + } + } + }, + "Vpc": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcId" + } + ], + "shape": "Vpc", + "load": { + "request": { + "operation": "DescribeVpcs", + "params": [ + { "target": "VpcIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Vpcs[0]" + }, + "actions": { + "AssociateDhcpOptions": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachClassicLinkInstance": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachInternetGateway": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateNetworkAcl": { + "request": { + "operation": "CreateNetworkAcl", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateRouteTable": { + "request": { + "operation": "CreateRouteTable", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { + "operation": "CreateSecurityGroup", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSubnet": { + "request": { + "operation": "CreateSubnet", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkInstance": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachInternetGateway": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DisableClassicLink": { + "request": { + "operation": "DisableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableClassicLink": { + "request": { + "operation": "EnableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "RequestVpcPeeringConnection": { + "request": { + "operation": "CreateVpcPeeringConnection", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "data", "path": "DhcpOptionsId" } + ] + } + } + }, + "hasMany": { + "AcceptedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "accepter-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "NetworkAcls": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "RequestedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "requester-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "RouteTables": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Subnets": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + } + } + }, + "VpcPeeringConnection": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcPeeringConnectionId" + } + ], + "shape": "VpcPeeringConnection", + "load": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "VpcPeeringConnectionIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "VpcPeeringConnections[0]" + }, + "actions": { + "Accept": { + "request": { + "operation": "AcceptVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reject": { + "request": { + "operation": "RejectVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "AccepterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "AccepterVpcInfo.VpcId" } + ] + } + }, + "RequesterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RequesterVpcInfo.VpcId" } + ] + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/ec2/2015-03-01/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/ec2/2015-03-01/resources-1.json new file mode 100644 index 0000000..8ccf160 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/ec2/2015-03-01/resources-1.json @@ -0,0 +1,2289 @@ +{ + "service": { + "actions": { + "CreateDhcpOptions": { + "request": { "operation": "CreateDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions.DhcpOptionsId" } + ], + "path": "DhcpOptions" + } + }, + "CreateInstances": { + "request": { "operation": "RunInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateInternetGateway": { + "request": { "operation": "CreateInternetGateway" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateway.InternetGatewayId" } + ], + "path": "InternetGateway" + } + }, + "CreateKeyPair": { + "request": { "operation": "CreateKeyPair" }, + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ] + } + }, + "CreateNetworkAcl": { + "request": { "operation": "CreateNetworkAcl" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateNetworkInterface": { + "request": { "operation": "CreateNetworkInterface" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreatePlacementGroup": { + "request": { "operation": "CreatePlacementGroup" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "GroupName" } + ] + } + }, + "CreateRouteTable": { + "request": { "operation": "CreateRouteTable" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { "operation": "CreateSecurityGroup" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSnapshot": { + "request": { "operation": "CreateSnapshot" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateSubnet": { + "request": { "operation": "CreateSubnet" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { "operation": "CreateTags" }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "requestParameter", "path": "Resources[]" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "CreateVolume": { + "request": { "operation": "CreateVolume" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VolumeId" } + ], + "path": "@" + } + }, + "CreateVpc": { + "request": { "operation": "CreateVpc" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpc.VpcId" } + ], + "path": "Vpc" + } + }, + "CreateVpcPeeringConnection": { + "request": { "operation": "CreateVpcPeeringConnection" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + }, + "DisassociateRouteTable": { + "request": { "operation": "DisassociateRouteTable" } + }, + "ImportKeyPair": { + "request": { "operation": "ImportKeyPair" }, + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ] + } + }, + "RegisterImage": { + "request": { "operation": "RegisterImage" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Instance": { + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "InternetGateway": { + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "NetworkAcl": { + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "NetworkInterface": { + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "RouteTableAssociation": { + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "SecurityGroup": { + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Snapshot": { + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "VpcPeeringConnection": { + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + } + }, + "hasMany": { + "DhcpOptionsSets": { + "request": { "operation": "DescribeDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions[].DhcpOptionsId" } + ], + "path": "DhcpOptions[]" + } + }, + "Images": { + "request": { "operation": "DescribeImages" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Images[].ImageId" } + ], + "path": "Images[]" + } + }, + "Instances": { + "request": { "operation": "DescribeInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { "operation": "DescribeInternetGateways" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "KeyPairs": { + "request": { "operation": "DescribeKeyPairs" }, + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyPairs[].KeyName" } + ], + "path": "KeyPairs[]" + } + }, + "NetworkAcls": { + "request": { "operation": "DescribeNetworkAcls" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { "operation": "DescribeNetworkInterfaces" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "PlacementGroups": { + "request": { "operation": "DescribePlacementGroups" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "response", "path": "PlacementGroups[].GroupName" } + ], + "path": "PlacementGroups[]" + } + }, + "RouteTables": { + "request": { "operation": "DescribeRouteTables" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { "operation": "DescribeSecurityGroups" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Snapshots": { + "request": { "operation": "DescribeSnapshots" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + }, + "Subnets": { + "request": { "operation": "DescribeSubnets" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + }, + "Volumes": { + "request": { "operation": "DescribeVolumes" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + }, + "VpcPeeringConnections": { + "request": { "operation": "DescribeVpcPeeringConnections" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Vpcs": { + "request": { "operation": "DescribeVpcs" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpcs[].VpcId" } + ], + "path": "Vpcs[]" + } + } + } + }, + "resources": { + "DhcpOptions": { + "identifiers": [ + { + "name": "Id", + "memberName": "DhcpOptionsId" + } + ], + "shape": "DhcpOptions", + "load": { + "request": { + "operation": "DescribeDhcpOptions", + "params": [ + { "target": "DhcpOptionsIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "DhcpOptions[0]" + }, + "actions": { + "AssociateWithVpc": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Image": { + "identifiers": [ + { + "name": "Id", + "memberName": "ImageId" + } + ], + "shape": "Image", + "load": { + "request": { + "operation": "DescribeImages", + "params": [ + { "target": "ImageIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Images[0]" + }, + "actions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Deregister": { + "request": { + "operation": "DeregisterImage", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Instance": { + "identifiers": [ + { + "name": "Id", + "memberName": "InstanceId" + } + ], + "shape": "Instance", + "load": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Reservations[0].Instances[0]" + }, + "actions": { + "AttachClassicLinkVpc": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachVolume": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ConsoleOutput": { + "request": { + "operation": "GetConsoleOutput", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateImage": { + "request": { + "operation": "CreateImage", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkVpc": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachVolume": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "PasswordData": { + "request": { + "operation": "GetPasswordData", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ReportStatus": { + "request": { + "operation": "ReportInstanceStatus", + "params": [ + { "target": "Instances[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetKernel": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "kernel" } + ] + } + }, + "ResetRamdisk": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "ramdisk" } + ] + } + }, + "ResetSourceDestCheck": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "sourceDestCheck" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "batchActions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "InstanceExists", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Running": { + "waiterName": "InstanceRunning", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Stopped": { + "waiterName": "InstanceStopped", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Terminated": { + "waiterName": "InstanceTerminated", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + } + }, + "has": { + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "data", "path": "ImageId" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "data", "path": "KeyName" } + ] + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "data", "path": "Placement.GroupName" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Volumes": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.instance-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + } + } + }, + "InternetGateway": { + "identifiers": [ + { + "name": "Id", + "memberName": "InternetGatewayId" + } + ], + "shape": "InternetGateway", + "load": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "InternetGatewayIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "InternetGateways[0]" + }, + "actions": { + "AttachToVpc": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromVpc": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "KeyPair": { + "identifiers": [ + { + "name": "Name", + "memberName": "KeyName" + } + ], + "shape": "KeyPairInfo", + "load": { + "request": { + "operation": "DescribeKeyPairs", + "params": [ + { "target": "KeyNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "KeyPairs[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteKeyPair", + "params": [ + { "target": "KeyName", "source": "identifier", "name": "Name" } + ] + } + } + } + }, + "NetworkAcl": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkAclId" + } + ], + "shape": "NetworkAcl", + "load": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "NetworkAclIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkAcls[0]" + }, + "actions": { + "CreateEntry": { + "request": { + "operation": "CreateNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkAcl", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "DeleteEntry": { + "request": { + "operation": "DeleteNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceAssociation": { + "request": { + "operation": "ReplaceNetworkAclAssociation", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceEntry": { + "request": { + "operation": "ReplaceNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "NetworkInterface": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkInterfaceId" + } + ], + "shape": "NetworkInterface", + "load": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "NetworkInterfaceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkInterfaces[0]" + }, + "actions": { + "AssignPrivateIpAddresses": { + "request": { + "operation": "AssignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Attach": { + "request": { + "operation": "AttachNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Detach": { + "request": { + "operation": "DetachNetworkInterface", + "params": [ + { "target": "AttachmentId", "source": "data", "path": "Attachment.AttachmentId" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "UnassignPrivateIpAddresses": { + "request": { + "operation": "UnassignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "PlacementGroup": { + "identifiers": [ + { + "name": "Name", + "memberName": "GroupName" + } + ], + "shape": "PlacementGroup", + "load": { + "request": { + "operation": "DescribePlacementGroups", + "params": [ + { "target": "GroupNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "PlacementGroups[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeletePlacementGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "placement-group-name" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + } + } + }, + "RouteTable": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableId" + } + ], + "shape": "RouteTable", + "load": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "RouteTableIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "RouteTables[0]" + }, + "actions": { + "AssociateWithSubnet": { + "request": { + "operation": "AssociateRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "AssociationId" } + ] + } + }, + "CreateRoute": { + "request": { + "operation": "CreateRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Associations": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "RouteTableIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[0].Associations[].RouteTableAssociationId" } + ], + "path": "RouteTables[0].Associations[]" + } + } + } + }, + "RouteTableAssociation": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableAssociationId" + } + ], + "shape": "RouteTableAssociation", + "actions": { + "Delete": { + "request": { + "operation": "DisassociateRouteTable", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceSubnet": { + "request": { + "operation": "ReplaceRouteTableAssociation", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NewAssociationId" } + ] + } + } + }, + "has": { + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RouteTableId" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + } + } + }, + "SecurityGroup": { + "identifiers": [ + { + "name": "Id", + "memberName": "GroupId" + } + ], + "shape": "SecurityGroup", + "load": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "GroupIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "SecurityGroups[0]" + }, + "actions": { + "AuthorizeEgress": { + "request": { + "operation": "AuthorizeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "AuthorizeIngress": { + "request": { + "operation": "AuthorizeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSecurityGroup", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeEgress": { + "request": { + "operation": "RevokeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeIngress": { + "request": { + "operation": "RevokeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Snapshot": { + "identifiers": [ + { + "name": "Id", + "memberName": "SnapshotId" + } + ], + "shape": "Snapshot", + "load": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "SnapshotIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Snapshots[0]" + }, + "actions": { + "Copy": { + "request": { + "operation": "CopySnapshot", + "params": [ + { "target": "SourceSnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSnapshot", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifySnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Completed": { + "waiterName": "SnapshotCompleted", + "params": [ + { "target": "SnapshotIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Snapshots[]" + } + }, + "has": { + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VolumeId" } + ] + } + } + } + }, + "Subnet": { + "identifiers": [ + { + "name": "Id", + "memberName": "SubnetId" + } + ], + "shape": "Subnet", + "load": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "SubnetIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Subnets[0]" + }, + "actions": { + "CreateInstances": { + "request": { + "operation": "RunInstances", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateNetworkInterface": { + "request": { + "operation": "CreateNetworkInterface", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSubnet", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + } + } + }, + "Tag": { + "identifiers": [ + { + "name": "ResourceId", + "memberName": "ResourceId" + }, + { + "name": "Key", + "memberName": "Key" + }, + { + "name": "Value", + "memberName": "Value" + } + ], + "shape": "TagDescription", + "load": { + "request": { + "operation": "DescribeTags", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "key" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Key" }, + { "target": "Filters[1].Name", "source": "string", "value": "value" }, + { "target": "Filters[1].Values[0]", "source": "identifier", "name": "Value" } + ] + }, + "path": "Tags[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[0].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[0].Value", "source": "identifier", "name": "Value" } + ] + } + } + }, + "batchActions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[*].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[*].Value", "source": "identifier", "name": "Value" } + ] + } + } + } + }, + "Volume": { + "identifiers": [ + { + "name": "Id", + "memberName": "VolumeId" + } + ], + "shape": "Volume", + "load": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Volumes[0]" + }, + "actions": { + "AttachToInstance": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateSnapshot": { + "request": { + "operation": "CreateSnapshot", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeStatus": { + "request": { + "operation": "DescribeVolumeStatus", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromInstance": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableIo": { + "request": { + "operation": "EnableVolumeIO", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "hasMany": { + "Snapshots": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "volume-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + } + } + }, + "Vpc": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcId" + } + ], + "shape": "Vpc", + "load": { + "request": { + "operation": "DescribeVpcs", + "params": [ + { "target": "VpcIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Vpcs[0]" + }, + "actions": { + "AssociateDhcpOptions": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachClassicLinkInstance": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachInternetGateway": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateNetworkAcl": { + "request": { + "operation": "CreateNetworkAcl", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateRouteTable": { + "request": { + "operation": "CreateRouteTable", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { + "operation": "CreateSecurityGroup", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSubnet": { + "request": { + "operation": "CreateSubnet", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkInstance": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachInternetGateway": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DisableClassicLink": { + "request": { + "operation": "DisableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableClassicLink": { + "request": { + "operation": "EnableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "RequestVpcPeeringConnection": { + "request": { + "operation": "CreateVpcPeeringConnection", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "data", "path": "DhcpOptionsId" } + ] + } + } + }, + "hasMany": { + "AcceptedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "accepter-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "NetworkAcls": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "RequestedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "requester-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "RouteTables": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Subnets": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + } + } + }, + "VpcPeeringConnection": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcPeeringConnectionId" + } + ], + "shape": "VpcPeeringConnection", + "load": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "VpcPeeringConnectionIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "VpcPeeringConnections[0]" + }, + "actions": { + "Accept": { + "request": { + "operation": "AcceptVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reject": { + "request": { + "operation": "RejectVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "AccepterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "AccepterVpcInfo.VpcId" } + ] + } + }, + "RequesterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RequesterVpcInfo.VpcId" } + ] + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/ec2/2015-04-15/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/ec2/2015-04-15/resources-1.json new file mode 100644 index 0000000..8ccf160 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/ec2/2015-04-15/resources-1.json @@ -0,0 +1,2289 @@ +{ + "service": { + "actions": { + "CreateDhcpOptions": { + "request": { "operation": "CreateDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions.DhcpOptionsId" } + ], + "path": "DhcpOptions" + } + }, + "CreateInstances": { + "request": { "operation": "RunInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateInternetGateway": { + "request": { "operation": "CreateInternetGateway" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateway.InternetGatewayId" } + ], + "path": "InternetGateway" + } + }, + "CreateKeyPair": { + "request": { "operation": "CreateKeyPair" }, + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ] + } + }, + "CreateNetworkAcl": { + "request": { "operation": "CreateNetworkAcl" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateNetworkInterface": { + "request": { "operation": "CreateNetworkInterface" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreatePlacementGroup": { + "request": { "operation": "CreatePlacementGroup" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "GroupName" } + ] + } + }, + "CreateRouteTable": { + "request": { "operation": "CreateRouteTable" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { "operation": "CreateSecurityGroup" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSnapshot": { + "request": { "operation": "CreateSnapshot" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateSubnet": { + "request": { "operation": "CreateSubnet" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { "operation": "CreateTags" }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "requestParameter", "path": "Resources[]" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "CreateVolume": { + "request": { "operation": "CreateVolume" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VolumeId" } + ], + "path": "@" + } + }, + "CreateVpc": { + "request": { "operation": "CreateVpc" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpc.VpcId" } + ], + "path": "Vpc" + } + }, + "CreateVpcPeeringConnection": { + "request": { "operation": "CreateVpcPeeringConnection" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + }, + "DisassociateRouteTable": { + "request": { "operation": "DisassociateRouteTable" } + }, + "ImportKeyPair": { + "request": { "operation": "ImportKeyPair" }, + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ] + } + }, + "RegisterImage": { + "request": { "operation": "RegisterImage" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Instance": { + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "InternetGateway": { + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "NetworkAcl": { + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "NetworkInterface": { + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "RouteTableAssociation": { + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "SecurityGroup": { + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Snapshot": { + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "VpcPeeringConnection": { + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + } + }, + "hasMany": { + "DhcpOptionsSets": { + "request": { "operation": "DescribeDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions[].DhcpOptionsId" } + ], + "path": "DhcpOptions[]" + } + }, + "Images": { + "request": { "operation": "DescribeImages" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Images[].ImageId" } + ], + "path": "Images[]" + } + }, + "Instances": { + "request": { "operation": "DescribeInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { "operation": "DescribeInternetGateways" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "KeyPairs": { + "request": { "operation": "DescribeKeyPairs" }, + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyPairs[].KeyName" } + ], + "path": "KeyPairs[]" + } + }, + "NetworkAcls": { + "request": { "operation": "DescribeNetworkAcls" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { "operation": "DescribeNetworkInterfaces" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "PlacementGroups": { + "request": { "operation": "DescribePlacementGroups" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "response", "path": "PlacementGroups[].GroupName" } + ], + "path": "PlacementGroups[]" + } + }, + "RouteTables": { + "request": { "operation": "DescribeRouteTables" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { "operation": "DescribeSecurityGroups" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Snapshots": { + "request": { "operation": "DescribeSnapshots" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + }, + "Subnets": { + "request": { "operation": "DescribeSubnets" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + }, + "Volumes": { + "request": { "operation": "DescribeVolumes" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + }, + "VpcPeeringConnections": { + "request": { "operation": "DescribeVpcPeeringConnections" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Vpcs": { + "request": { "operation": "DescribeVpcs" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpcs[].VpcId" } + ], + "path": "Vpcs[]" + } + } + } + }, + "resources": { + "DhcpOptions": { + "identifiers": [ + { + "name": "Id", + "memberName": "DhcpOptionsId" + } + ], + "shape": "DhcpOptions", + "load": { + "request": { + "operation": "DescribeDhcpOptions", + "params": [ + { "target": "DhcpOptionsIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "DhcpOptions[0]" + }, + "actions": { + "AssociateWithVpc": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Image": { + "identifiers": [ + { + "name": "Id", + "memberName": "ImageId" + } + ], + "shape": "Image", + "load": { + "request": { + "operation": "DescribeImages", + "params": [ + { "target": "ImageIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Images[0]" + }, + "actions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Deregister": { + "request": { + "operation": "DeregisterImage", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Instance": { + "identifiers": [ + { + "name": "Id", + "memberName": "InstanceId" + } + ], + "shape": "Instance", + "load": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Reservations[0].Instances[0]" + }, + "actions": { + "AttachClassicLinkVpc": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachVolume": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ConsoleOutput": { + "request": { + "operation": "GetConsoleOutput", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateImage": { + "request": { + "operation": "CreateImage", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkVpc": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachVolume": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "PasswordData": { + "request": { + "operation": "GetPasswordData", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ReportStatus": { + "request": { + "operation": "ReportInstanceStatus", + "params": [ + { "target": "Instances[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetKernel": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "kernel" } + ] + } + }, + "ResetRamdisk": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "ramdisk" } + ] + } + }, + "ResetSourceDestCheck": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "sourceDestCheck" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "batchActions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "InstanceExists", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Running": { + "waiterName": "InstanceRunning", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Stopped": { + "waiterName": "InstanceStopped", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Terminated": { + "waiterName": "InstanceTerminated", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + } + }, + "has": { + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "data", "path": "ImageId" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "data", "path": "KeyName" } + ] + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "data", "path": "Placement.GroupName" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Volumes": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.instance-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + } + } + }, + "InternetGateway": { + "identifiers": [ + { + "name": "Id", + "memberName": "InternetGatewayId" + } + ], + "shape": "InternetGateway", + "load": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "InternetGatewayIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "InternetGateways[0]" + }, + "actions": { + "AttachToVpc": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromVpc": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "KeyPair": { + "identifiers": [ + { + "name": "Name", + "memberName": "KeyName" + } + ], + "shape": "KeyPairInfo", + "load": { + "request": { + "operation": "DescribeKeyPairs", + "params": [ + { "target": "KeyNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "KeyPairs[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteKeyPair", + "params": [ + { "target": "KeyName", "source": "identifier", "name": "Name" } + ] + } + } + } + }, + "NetworkAcl": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkAclId" + } + ], + "shape": "NetworkAcl", + "load": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "NetworkAclIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkAcls[0]" + }, + "actions": { + "CreateEntry": { + "request": { + "operation": "CreateNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkAcl", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "DeleteEntry": { + "request": { + "operation": "DeleteNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceAssociation": { + "request": { + "operation": "ReplaceNetworkAclAssociation", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceEntry": { + "request": { + "operation": "ReplaceNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "NetworkInterface": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkInterfaceId" + } + ], + "shape": "NetworkInterface", + "load": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "NetworkInterfaceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkInterfaces[0]" + }, + "actions": { + "AssignPrivateIpAddresses": { + "request": { + "operation": "AssignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Attach": { + "request": { + "operation": "AttachNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Detach": { + "request": { + "operation": "DetachNetworkInterface", + "params": [ + { "target": "AttachmentId", "source": "data", "path": "Attachment.AttachmentId" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "UnassignPrivateIpAddresses": { + "request": { + "operation": "UnassignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "PlacementGroup": { + "identifiers": [ + { + "name": "Name", + "memberName": "GroupName" + } + ], + "shape": "PlacementGroup", + "load": { + "request": { + "operation": "DescribePlacementGroups", + "params": [ + { "target": "GroupNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "PlacementGroups[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeletePlacementGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "placement-group-name" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + } + } + }, + "RouteTable": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableId" + } + ], + "shape": "RouteTable", + "load": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "RouteTableIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "RouteTables[0]" + }, + "actions": { + "AssociateWithSubnet": { + "request": { + "operation": "AssociateRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "AssociationId" } + ] + } + }, + "CreateRoute": { + "request": { + "operation": "CreateRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Associations": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "RouteTableIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[0].Associations[].RouteTableAssociationId" } + ], + "path": "RouteTables[0].Associations[]" + } + } + } + }, + "RouteTableAssociation": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableAssociationId" + } + ], + "shape": "RouteTableAssociation", + "actions": { + "Delete": { + "request": { + "operation": "DisassociateRouteTable", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceSubnet": { + "request": { + "operation": "ReplaceRouteTableAssociation", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NewAssociationId" } + ] + } + } + }, + "has": { + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RouteTableId" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + } + } + }, + "SecurityGroup": { + "identifiers": [ + { + "name": "Id", + "memberName": "GroupId" + } + ], + "shape": "SecurityGroup", + "load": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "GroupIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "SecurityGroups[0]" + }, + "actions": { + "AuthorizeEgress": { + "request": { + "operation": "AuthorizeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "AuthorizeIngress": { + "request": { + "operation": "AuthorizeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSecurityGroup", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeEgress": { + "request": { + "operation": "RevokeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeIngress": { + "request": { + "operation": "RevokeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Snapshot": { + "identifiers": [ + { + "name": "Id", + "memberName": "SnapshotId" + } + ], + "shape": "Snapshot", + "load": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "SnapshotIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Snapshots[0]" + }, + "actions": { + "Copy": { + "request": { + "operation": "CopySnapshot", + "params": [ + { "target": "SourceSnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSnapshot", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifySnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Completed": { + "waiterName": "SnapshotCompleted", + "params": [ + { "target": "SnapshotIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Snapshots[]" + } + }, + "has": { + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VolumeId" } + ] + } + } + } + }, + "Subnet": { + "identifiers": [ + { + "name": "Id", + "memberName": "SubnetId" + } + ], + "shape": "Subnet", + "load": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "SubnetIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Subnets[0]" + }, + "actions": { + "CreateInstances": { + "request": { + "operation": "RunInstances", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateNetworkInterface": { + "request": { + "operation": "CreateNetworkInterface", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSubnet", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + } + } + }, + "Tag": { + "identifiers": [ + { + "name": "ResourceId", + "memberName": "ResourceId" + }, + { + "name": "Key", + "memberName": "Key" + }, + { + "name": "Value", + "memberName": "Value" + } + ], + "shape": "TagDescription", + "load": { + "request": { + "operation": "DescribeTags", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "key" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Key" }, + { "target": "Filters[1].Name", "source": "string", "value": "value" }, + { "target": "Filters[1].Values[0]", "source": "identifier", "name": "Value" } + ] + }, + "path": "Tags[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[0].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[0].Value", "source": "identifier", "name": "Value" } + ] + } + } + }, + "batchActions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[*].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[*].Value", "source": "identifier", "name": "Value" } + ] + } + } + } + }, + "Volume": { + "identifiers": [ + { + "name": "Id", + "memberName": "VolumeId" + } + ], + "shape": "Volume", + "load": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Volumes[0]" + }, + "actions": { + "AttachToInstance": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateSnapshot": { + "request": { + "operation": "CreateSnapshot", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeStatus": { + "request": { + "operation": "DescribeVolumeStatus", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromInstance": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableIo": { + "request": { + "operation": "EnableVolumeIO", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "hasMany": { + "Snapshots": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "volume-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + } + } + }, + "Vpc": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcId" + } + ], + "shape": "Vpc", + "load": { + "request": { + "operation": "DescribeVpcs", + "params": [ + { "target": "VpcIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Vpcs[0]" + }, + "actions": { + "AssociateDhcpOptions": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachClassicLinkInstance": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachInternetGateway": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateNetworkAcl": { + "request": { + "operation": "CreateNetworkAcl", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateRouteTable": { + "request": { + "operation": "CreateRouteTable", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { + "operation": "CreateSecurityGroup", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSubnet": { + "request": { + "operation": "CreateSubnet", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkInstance": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachInternetGateway": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DisableClassicLink": { + "request": { + "operation": "DisableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableClassicLink": { + "request": { + "operation": "EnableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "RequestVpcPeeringConnection": { + "request": { + "operation": "CreateVpcPeeringConnection", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "data", "path": "DhcpOptionsId" } + ] + } + } + }, + "hasMany": { + "AcceptedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "accepter-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "NetworkAcls": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "RequestedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "requester-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "RouteTables": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Subnets": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + } + } + }, + "VpcPeeringConnection": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcPeeringConnectionId" + } + ], + "shape": "VpcPeeringConnection", + "load": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "VpcPeeringConnectionIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "VpcPeeringConnections[0]" + }, + "actions": { + "Accept": { + "request": { + "operation": "AcceptVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reject": { + "request": { + "operation": "RejectVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "AccepterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "AccepterVpcInfo.VpcId" } + ] + } + }, + "RequesterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RequesterVpcInfo.VpcId" } + ] + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/ec2/2015-10-01/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/ec2/2015-10-01/resources-1.json new file mode 100644 index 0000000..4831a36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/ec2/2015-10-01/resources-1.json @@ -0,0 +1,2567 @@ +{ + "service": { + "actions": { + "CreateDhcpOptions": { + "request": { "operation": "CreateDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions.DhcpOptionsId" } + ], + "path": "DhcpOptions" + } + }, + "CreateInstances": { + "request": { "operation": "RunInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateInternetGateway": { + "request": { "operation": "CreateInternetGateway" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateway.InternetGatewayId" } + ], + "path": "InternetGateway" + } + }, + "CreateKeyPair": { + "request": { "operation": "CreateKeyPair" }, + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ], + "path": "@" + } + }, + "CreateNetworkAcl": { + "request": { "operation": "CreateNetworkAcl" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateNetworkInterface": { + "request": { "operation": "CreateNetworkInterface" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreatePlacementGroup": { + "request": { "operation": "CreatePlacementGroup" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "GroupName" } + ] + } + }, + "CreateRouteTable": { + "request": { "operation": "CreateRouteTable" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { "operation": "CreateSecurityGroup" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSnapshot": { + "request": { "operation": "CreateSnapshot" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateSubnet": { + "request": { "operation": "CreateSubnet" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { "operation": "CreateTags" } + }, + "CreateVolume": { + "request": { "operation": "CreateVolume" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VolumeId" } + ], + "path": "@" + } + }, + "CreateVpc": { + "request": { "operation": "CreateVpc" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpc.VpcId" } + ], + "path": "Vpc" + } + }, + "CreateVpcPeeringConnection": { + "request": { "operation": "CreateVpcPeeringConnection" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + }, + "DisassociateRouteTable": { + "request": { "operation": "DisassociateRouteTable" } + }, + "ImportKeyPair": { + "request": { "operation": "ImportKeyPair" }, + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ] + } + }, + "RegisterImage": { + "request": { "operation": "RegisterImage" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Instance": { + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "InternetGateway": { + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "NetworkAcl": { + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "NetworkInterface": { + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "RouteTableAssociation": { + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "SecurityGroup": { + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Snapshot": { + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "VpcPeeringConnection": { + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + } + }, + "hasMany": { + "ClassicAddresses": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "domain" }, + { "target": "Filters[0].Values[0]", "source": "string", "value": "standard" } + ] + }, + "resource": { + "type": "ClassicAddress", + "identifiers": [ + { "target": "PublicIp", "source": "response", "path": "Addresses[].PublicIp" } + ], + "path": "Addresses[]" + } + }, + "DhcpOptionsSets": { + "request": { "operation": "DescribeDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions[].DhcpOptionsId" } + ], + "path": "DhcpOptions[]" + } + }, + "Images": { + "request": { "operation": "DescribeImages" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Images[].ImageId" } + ], + "path": "Images[]" + } + }, + "Instances": { + "request": { "operation": "DescribeInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { "operation": "DescribeInternetGateways" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "KeyPairs": { + "request": { "operation": "DescribeKeyPairs" }, + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyPairs[].KeyName" } + ], + "path": "KeyPairs[]" + } + }, + "NetworkAcls": { + "request": { "operation": "DescribeNetworkAcls" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { "operation": "DescribeNetworkInterfaces" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "PlacementGroups": { + "request": { "operation": "DescribePlacementGroups" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "response", "path": "PlacementGroups[].GroupName" } + ], + "path": "PlacementGroups[]" + } + }, + "RouteTables": { + "request": { "operation": "DescribeRouteTables" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { "operation": "DescribeSecurityGroups" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Snapshots": { + "request": { "operation": "DescribeSnapshots" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + }, + "Subnets": { + "request": { "operation": "DescribeSubnets" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + }, + "Volumes": { + "request": { "operation": "DescribeVolumes" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + }, + "VpcAddresses": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "domain" }, + { "target": "Filters[0].Values[0]", "source": "string", "value": "vpc" } + ] + }, + "resource": { + "type": "VpcAddress", + "identifiers": [ + { "target": "AllocationId", "source": "response", "path": "Addresses[].AllocationId" } + ], + "path": "Addresses[]" + } + }, + "VpcPeeringConnections": { + "request": { "operation": "DescribeVpcPeeringConnections" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Vpcs": { + "request": { "operation": "DescribeVpcs" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpcs[].VpcId" } + ], + "path": "Vpcs[]" + } + } + } + }, + "resources": { + "ClassicAddress": { + "identifiers": [ + { + "name": "PublicIp" + } + ], + "shape": "Address", + "load": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "PublicIps[]", "source": "identifier", "name": "PublicIp" } + ] + }, + "path": "Addresses[0]" + }, + "actions": { + "Associate": { + "request": { + "operation": "AssociateAddress", + "params": [ + { "target": "PublicIp", "source": "identifier", "name": "PublicIp" } + ] + } + }, + "Disassociate": { + "request": { + "operation": "DisassociateAddress", + "params": [ + { "target": "PublicIp", "source": "data", "path": "PublicIp" } + ] + } + }, + "Release": { + "request": { + "operation": "ReleaseAddress", + "params": [ + { "target": "PublicIp", "source": "data", "path": "PublicIp" } + ] + } + } + } + }, + "DhcpOptions": { + "identifiers": [ + { + "name": "Id", + "memberName": "DhcpOptionsId" + } + ], + "shape": "DhcpOptions", + "load": { + "request": { + "operation": "DescribeDhcpOptions", + "params": [ + { "target": "DhcpOptionsIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "DhcpOptions[0]" + }, + "actions": { + "AssociateWithVpc": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Image": { + "identifiers": [ + { + "name": "Id", + "memberName": "ImageId" + } + ], + "shape": "Image", + "load": { + "request": { + "operation": "DescribeImages", + "params": [ + { "target": "ImageIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Images[0]" + }, + "actions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Deregister": { + "request": { + "operation": "DeregisterImage", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Instance": { + "identifiers": [ + { + "name": "Id", + "memberName": "InstanceId" + } + ], + "shape": "Instance", + "load": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Reservations[0].Instances[0]" + }, + "actions": { + "AttachClassicLinkVpc": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachVolume": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ConsoleOutput": { + "request": { + "operation": "GetConsoleOutput", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateImage": { + "request": { + "operation": "CreateImage", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkVpc": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachVolume": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "PasswordData": { + "request": { + "operation": "GetPasswordData", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ReportStatus": { + "request": { + "operation": "ReportInstanceStatus", + "params": [ + { "target": "Instances[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetKernel": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "kernel" } + ] + } + }, + "ResetRamdisk": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "ramdisk" } + ] + } + }, + "ResetSourceDestCheck": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "sourceDestCheck" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "batchActions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "InstanceExists", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Running": { + "waiterName": "InstanceRunning", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Stopped": { + "waiterName": "InstanceStopped", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Terminated": { + "waiterName": "InstanceTerminated", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + } + }, + "has": { + "ClassicAddress": { + "resource": { + "type": "ClassicAddress", + "identifiers": [ + { "target": "PublicIp", "source": "data", "path": "PublicIpAddress" } + ] + } + }, + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "data", "path": "ImageId" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "data", "path": "KeyName" } + ] + } + }, + "NetworkInterfaces": { + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "data", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "data", "path": "Placement.GroupName" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Volumes": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.instance-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + }, + "VpcAddresses": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "instance-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcAddress", + "identifiers": [ + { "target": "AllocationId", "source": "response", "path": "Addresses[].AllocationId" } + ], + "path": "Addresses[]" + } + } + } + }, + "InternetGateway": { + "identifiers": [ + { + "name": "Id", + "memberName": "InternetGatewayId" + } + ], + "shape": "InternetGateway", + "load": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "InternetGatewayIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "InternetGateways[0]" + }, + "actions": { + "AttachToVpc": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromVpc": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "KeyPair": { + "identifiers": [ + { + "name": "Name", + "memberName": "KeyName" + } + ], + "shape": "KeyPair", + "actions": { + "Delete": { + "request": { + "operation": "DeleteKeyPair", + "params": [ + { "target": "KeyName", "source": "identifier", "name": "Name" } + ] + } + } + } + }, + "KeyPairInfo": { + "identifiers": [ + { + "name": "Name", + "memberName": "KeyName" + } + ], + "shape": "KeyPairInfo", + "load": { + "request": { + "operation": "DescribeKeyPairs", + "params": [ + { "target": "KeyNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "KeyPairs[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteKeyPair", + "params": [ + { "target": "KeyName", "source": "identifier", "name": "Name" } + ] + } + } + } + }, + "NetworkAcl": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkAclId" + } + ], + "shape": "NetworkAcl", + "load": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "NetworkAclIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkAcls[0]" + }, + "actions": { + "CreateEntry": { + "request": { + "operation": "CreateNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkAcl", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "DeleteEntry": { + "request": { + "operation": "DeleteNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceAssociation": { + "request": { + "operation": "ReplaceNetworkAclAssociation", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceEntry": { + "request": { + "operation": "ReplaceNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "NetworkInterface": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkInterfaceId" + } + ], + "shape": "NetworkInterface", + "load": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "NetworkInterfaceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkInterfaces[0]" + }, + "actions": { + "AssignPrivateIpAddresses": { + "request": { + "operation": "AssignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Attach": { + "request": { + "operation": "AttachNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Detach": { + "request": { + "operation": "DetachNetworkInterface", + "params": [ + { "target": "AttachmentId", "source": "data", "path": "Attachment.AttachmentId" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "UnassignPrivateIpAddresses": { + "request": { + "operation": "UnassignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Association": { + "resource": { + "type": "NetworkInterfaceAssociation", + "identifiers": [ + { "target": "Id", "source": "data", "path": "Association.AssociationId" } + ], + "path": "Association" + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "NetworkInterfaceAssociation": { + "identifiers": [ + { + "name": "Id" + } + ], + "shape": "InstanceNetworkInterfaceAssociation", + "load": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "association.association-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkInterfaces[0].Association" + }, + "actions": { + "Delete": { + "request": { + "operation": "DisassociateAddress", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Address": { + "resource": { + "type": "VpcAddress", + "identifiers": [ + { "target": "AllocationId", "source": "data", "path": "AllocationId" } + ] + } + } + } + }, + "PlacementGroup": { + "identifiers": [ + { + "name": "Name", + "memberName": "GroupName" + } + ], + "shape": "PlacementGroup", + "load": { + "request": { + "operation": "DescribePlacementGroups", + "params": [ + { "target": "GroupNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "PlacementGroups[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeletePlacementGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "placement-group-name" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + } + } + }, + "Route": { + "identifiers": [ + { "name": "RouteTableId" }, + { + "name": "DestinationCidrBlock", + "memberName": "DestinationCidrBlock" + } + ], + "shape": "Route", + "actions": { + "Delete": { + "request": { + "operation": "DeleteRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "RouteTableId" }, + { "target": "DestinationCidrBlock", "source": "identifier", "name": "DestinationCidrBlock" } + ] + } + }, + "Replace": { + "request": { + "operation": "ReplaceRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "RouteTableId" }, + { "target": "DestinationCidrBlock", "source": "identifier", "name": "DestinationCidrBlock" } + ] + } + } + }, + "has": { + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "identifier", "name": "RouteTableId" } + ] + } + } + } + }, + "RouteTable": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableId" + } + ], + "shape": "RouteTable", + "load": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "RouteTableIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "RouteTables[0]" + }, + "actions": { + "AssociateWithSubnet": { + "request": { + "operation": "AssociateRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "AssociationId" } + ] + } + }, + "CreateRoute": { + "request": { + "operation": "CreateRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Route", + "identifiers": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" }, + { "target": "DestinationCidrBlock", "source": "requestParameter", "path": "DestinationCidrBlock" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Routes": { + "resource": { + "type": "Route", + "identifiers": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" }, + { "target": "DestinationCidrBlock", "source": "data", "path": "Routes[].DestinationCidrBlock" } + ], + "path": "Routes[]" + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Associations": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "RouteTableIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[0].Associations[].RouteTableAssociationId" } + ], + "path": "RouteTables[0].Associations[]" + } + } + } + }, + "RouteTableAssociation": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableAssociationId" + } + ], + "shape": "RouteTableAssociation", + "actions": { + "Delete": { + "request": { + "operation": "DisassociateRouteTable", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceSubnet": { + "request": { + "operation": "ReplaceRouteTableAssociation", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NewAssociationId" } + ] + } + } + }, + "has": { + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RouteTableId" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + } + } + }, + "SecurityGroup": { + "identifiers": [ + { + "name": "Id", + "memberName": "GroupId" + } + ], + "shape": "SecurityGroup", + "load": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "GroupIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "SecurityGroups[0]" + }, + "actions": { + "AuthorizeEgress": { + "request": { + "operation": "AuthorizeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "AuthorizeIngress": { + "request": { + "operation": "AuthorizeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSecurityGroup", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeEgress": { + "request": { + "operation": "RevokeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeIngress": { + "request": { + "operation": "RevokeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Snapshot": { + "identifiers": [ + { + "name": "Id", + "memberName": "SnapshotId" + } + ], + "shape": "Snapshot", + "load": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "SnapshotIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Snapshots[0]" + }, + "actions": { + "Copy": { + "request": { + "operation": "CopySnapshot", + "params": [ + { "target": "SourceSnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSnapshot", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifySnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Completed": { + "waiterName": "SnapshotCompleted", + "params": [ + { "target": "SnapshotIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Snapshots[]" + } + }, + "has": { + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VolumeId" } + ] + } + } + } + }, + "Subnet": { + "identifiers": [ + { + "name": "Id", + "memberName": "SubnetId" + } + ], + "shape": "Subnet", + "load": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "SubnetIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Subnets[0]" + }, + "actions": { + "CreateInstances": { + "request": { + "operation": "RunInstances", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateNetworkInterface": { + "request": { + "operation": "CreateNetworkInterface", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSubnet", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + } + } + }, + "Tag": { + "identifiers": [ + { + "name": "ResourceId", + "memberName": "ResourceId" + }, + { + "name": "Key", + "memberName": "Key" + }, + { + "name": "Value", + "memberName": "Value" + } + ], + "shape": "TagDescription", + "load": { + "request": { + "operation": "DescribeTags", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "key" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Key" }, + { "target": "Filters[1].Name", "source": "string", "value": "value" }, + { "target": "Filters[1].Values[0]", "source": "identifier", "name": "Value" } + ] + }, + "path": "Tags[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[0].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[0].Value", "source": "identifier", "name": "Value" } + ] + } + } + }, + "batchActions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[*].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[*].Value", "source": "identifier", "name": "Value" } + ] + } + } + } + }, + "Volume": { + "identifiers": [ + { + "name": "Id", + "memberName": "VolumeId" + } + ], + "shape": "Volume", + "load": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Volumes[0]" + }, + "actions": { + "AttachToInstance": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateSnapshot": { + "request": { + "operation": "CreateSnapshot", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeStatus": { + "request": { + "operation": "DescribeVolumeStatus", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromInstance": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableIo": { + "request": { + "operation": "EnableVolumeIO", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "hasMany": { + "Snapshots": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "volume-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + } + } + }, + "Vpc": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcId" + } + ], + "shape": "Vpc", + "load": { + "request": { + "operation": "DescribeVpcs", + "params": [ + { "target": "VpcIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Vpcs[0]" + }, + "actions": { + "AssociateDhcpOptions": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachClassicLinkInstance": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachInternetGateway": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateNetworkAcl": { + "request": { + "operation": "CreateNetworkAcl", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateRouteTable": { + "request": { + "operation": "CreateRouteTable", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { + "operation": "CreateSecurityGroup", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSubnet": { + "request": { + "operation": "CreateSubnet", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkInstance": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachInternetGateway": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DisableClassicLink": { + "request": { + "operation": "DisableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableClassicLink": { + "request": { + "operation": "EnableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "RequestVpcPeeringConnection": { + "request": { + "operation": "CreateVpcPeeringConnection", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "data", "path": "DhcpOptionsId" } + ] + } + } + }, + "hasMany": { + "AcceptedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "accepter-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "NetworkAcls": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "RequestedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "requester-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "RouteTables": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Subnets": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + } + } + }, + "VpcPeeringConnection": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcPeeringConnectionId" + } + ], + "shape": "VpcPeeringConnection", + "load": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "VpcPeeringConnectionIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "VpcPeeringConnections[0]" + }, + "actions": { + "Accept": { + "request": { + "operation": "AcceptVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reject": { + "request": { + "operation": "RejectVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "VpcPeeringConnectionExists", + "params": [ + { "target": "VpcPeeringConnectionIds[]", "source": "identifier", "name": "Id" } + ], + "path": "VpcPeeringConnections[0]" + } + }, + "has": { + "AccepterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "AccepterVpcInfo.VpcId" } + ] + } + }, + "RequesterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RequesterVpcInfo.VpcId" } + ] + } + } + } + }, + "VpcAddress": { + "identifiers": [ + { + "name": "AllocationId" + } + ], + "shape": "Address", + "load": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "AllocationIds[0]", "source": "identifier", "name": "AllocationId" } + ] + }, + "path": "Addresses[0]" + }, + "actions": { + "Associate": { + "request": { + "operation": "AssociateAddress", + "params": [ + { "target": "AllocationId", "source": "identifier", "name": "AllocationId" } + ] + } + }, + "Release": { + "request": { + "operation": "ReleaseAddress", + "params": [ + { "target": "AllocationId", "source": "data", "path": "AllocationId" } + ] + } + } + }, + "has": { + "Association": { + "resource": { + "type": "NetworkInterfaceAssociation", + "identifiers": [ + { "target": "Id", "source": "data", "path": "AssociationId" } + ] + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/ec2/2016-04-01/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/ec2/2016-04-01/resources-1.json new file mode 100644 index 0000000..4831a36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/ec2/2016-04-01/resources-1.json @@ -0,0 +1,2567 @@ +{ + "service": { + "actions": { + "CreateDhcpOptions": { + "request": { "operation": "CreateDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions.DhcpOptionsId" } + ], + "path": "DhcpOptions" + } + }, + "CreateInstances": { + "request": { "operation": "RunInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateInternetGateway": { + "request": { "operation": "CreateInternetGateway" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateway.InternetGatewayId" } + ], + "path": "InternetGateway" + } + }, + "CreateKeyPair": { + "request": { "operation": "CreateKeyPair" }, + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ], + "path": "@" + } + }, + "CreateNetworkAcl": { + "request": { "operation": "CreateNetworkAcl" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateNetworkInterface": { + "request": { "operation": "CreateNetworkInterface" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreatePlacementGroup": { + "request": { "operation": "CreatePlacementGroup" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "GroupName" } + ] + } + }, + "CreateRouteTable": { + "request": { "operation": "CreateRouteTable" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { "operation": "CreateSecurityGroup" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSnapshot": { + "request": { "operation": "CreateSnapshot" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateSubnet": { + "request": { "operation": "CreateSubnet" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { "operation": "CreateTags" } + }, + "CreateVolume": { + "request": { "operation": "CreateVolume" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VolumeId" } + ], + "path": "@" + } + }, + "CreateVpc": { + "request": { "operation": "CreateVpc" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpc.VpcId" } + ], + "path": "Vpc" + } + }, + "CreateVpcPeeringConnection": { + "request": { "operation": "CreateVpcPeeringConnection" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + }, + "DisassociateRouteTable": { + "request": { "operation": "DisassociateRouteTable" } + }, + "ImportKeyPair": { + "request": { "operation": "ImportKeyPair" }, + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ] + } + }, + "RegisterImage": { + "request": { "operation": "RegisterImage" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Instance": { + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "InternetGateway": { + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "NetworkAcl": { + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "NetworkInterface": { + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "RouteTableAssociation": { + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "SecurityGroup": { + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Snapshot": { + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "VpcPeeringConnection": { + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + } + }, + "hasMany": { + "ClassicAddresses": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "domain" }, + { "target": "Filters[0].Values[0]", "source": "string", "value": "standard" } + ] + }, + "resource": { + "type": "ClassicAddress", + "identifiers": [ + { "target": "PublicIp", "source": "response", "path": "Addresses[].PublicIp" } + ], + "path": "Addresses[]" + } + }, + "DhcpOptionsSets": { + "request": { "operation": "DescribeDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions[].DhcpOptionsId" } + ], + "path": "DhcpOptions[]" + } + }, + "Images": { + "request": { "operation": "DescribeImages" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Images[].ImageId" } + ], + "path": "Images[]" + } + }, + "Instances": { + "request": { "operation": "DescribeInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { "operation": "DescribeInternetGateways" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "KeyPairs": { + "request": { "operation": "DescribeKeyPairs" }, + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyPairs[].KeyName" } + ], + "path": "KeyPairs[]" + } + }, + "NetworkAcls": { + "request": { "operation": "DescribeNetworkAcls" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { "operation": "DescribeNetworkInterfaces" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "PlacementGroups": { + "request": { "operation": "DescribePlacementGroups" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "response", "path": "PlacementGroups[].GroupName" } + ], + "path": "PlacementGroups[]" + } + }, + "RouteTables": { + "request": { "operation": "DescribeRouteTables" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { "operation": "DescribeSecurityGroups" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Snapshots": { + "request": { "operation": "DescribeSnapshots" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + }, + "Subnets": { + "request": { "operation": "DescribeSubnets" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + }, + "Volumes": { + "request": { "operation": "DescribeVolumes" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + }, + "VpcAddresses": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "domain" }, + { "target": "Filters[0].Values[0]", "source": "string", "value": "vpc" } + ] + }, + "resource": { + "type": "VpcAddress", + "identifiers": [ + { "target": "AllocationId", "source": "response", "path": "Addresses[].AllocationId" } + ], + "path": "Addresses[]" + } + }, + "VpcPeeringConnections": { + "request": { "operation": "DescribeVpcPeeringConnections" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Vpcs": { + "request": { "operation": "DescribeVpcs" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpcs[].VpcId" } + ], + "path": "Vpcs[]" + } + } + } + }, + "resources": { + "ClassicAddress": { + "identifiers": [ + { + "name": "PublicIp" + } + ], + "shape": "Address", + "load": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "PublicIps[]", "source": "identifier", "name": "PublicIp" } + ] + }, + "path": "Addresses[0]" + }, + "actions": { + "Associate": { + "request": { + "operation": "AssociateAddress", + "params": [ + { "target": "PublicIp", "source": "identifier", "name": "PublicIp" } + ] + } + }, + "Disassociate": { + "request": { + "operation": "DisassociateAddress", + "params": [ + { "target": "PublicIp", "source": "data", "path": "PublicIp" } + ] + } + }, + "Release": { + "request": { + "operation": "ReleaseAddress", + "params": [ + { "target": "PublicIp", "source": "data", "path": "PublicIp" } + ] + } + } + } + }, + "DhcpOptions": { + "identifiers": [ + { + "name": "Id", + "memberName": "DhcpOptionsId" + } + ], + "shape": "DhcpOptions", + "load": { + "request": { + "operation": "DescribeDhcpOptions", + "params": [ + { "target": "DhcpOptionsIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "DhcpOptions[0]" + }, + "actions": { + "AssociateWithVpc": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Image": { + "identifiers": [ + { + "name": "Id", + "memberName": "ImageId" + } + ], + "shape": "Image", + "load": { + "request": { + "operation": "DescribeImages", + "params": [ + { "target": "ImageIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Images[0]" + }, + "actions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Deregister": { + "request": { + "operation": "DeregisterImage", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Instance": { + "identifiers": [ + { + "name": "Id", + "memberName": "InstanceId" + } + ], + "shape": "Instance", + "load": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Reservations[0].Instances[0]" + }, + "actions": { + "AttachClassicLinkVpc": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachVolume": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ConsoleOutput": { + "request": { + "operation": "GetConsoleOutput", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateImage": { + "request": { + "operation": "CreateImage", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkVpc": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachVolume": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "PasswordData": { + "request": { + "operation": "GetPasswordData", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ReportStatus": { + "request": { + "operation": "ReportInstanceStatus", + "params": [ + { "target": "Instances[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetKernel": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "kernel" } + ] + } + }, + "ResetRamdisk": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "ramdisk" } + ] + } + }, + "ResetSourceDestCheck": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "sourceDestCheck" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "batchActions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "InstanceExists", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Running": { + "waiterName": "InstanceRunning", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Stopped": { + "waiterName": "InstanceStopped", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Terminated": { + "waiterName": "InstanceTerminated", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + } + }, + "has": { + "ClassicAddress": { + "resource": { + "type": "ClassicAddress", + "identifiers": [ + { "target": "PublicIp", "source": "data", "path": "PublicIpAddress" } + ] + } + }, + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "data", "path": "ImageId" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "data", "path": "KeyName" } + ] + } + }, + "NetworkInterfaces": { + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "data", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "data", "path": "Placement.GroupName" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Volumes": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.instance-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + }, + "VpcAddresses": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "instance-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcAddress", + "identifiers": [ + { "target": "AllocationId", "source": "response", "path": "Addresses[].AllocationId" } + ], + "path": "Addresses[]" + } + } + } + }, + "InternetGateway": { + "identifiers": [ + { + "name": "Id", + "memberName": "InternetGatewayId" + } + ], + "shape": "InternetGateway", + "load": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "InternetGatewayIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "InternetGateways[0]" + }, + "actions": { + "AttachToVpc": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromVpc": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "KeyPair": { + "identifiers": [ + { + "name": "Name", + "memberName": "KeyName" + } + ], + "shape": "KeyPair", + "actions": { + "Delete": { + "request": { + "operation": "DeleteKeyPair", + "params": [ + { "target": "KeyName", "source": "identifier", "name": "Name" } + ] + } + } + } + }, + "KeyPairInfo": { + "identifiers": [ + { + "name": "Name", + "memberName": "KeyName" + } + ], + "shape": "KeyPairInfo", + "load": { + "request": { + "operation": "DescribeKeyPairs", + "params": [ + { "target": "KeyNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "KeyPairs[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteKeyPair", + "params": [ + { "target": "KeyName", "source": "identifier", "name": "Name" } + ] + } + } + } + }, + "NetworkAcl": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkAclId" + } + ], + "shape": "NetworkAcl", + "load": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "NetworkAclIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkAcls[0]" + }, + "actions": { + "CreateEntry": { + "request": { + "operation": "CreateNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkAcl", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "DeleteEntry": { + "request": { + "operation": "DeleteNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceAssociation": { + "request": { + "operation": "ReplaceNetworkAclAssociation", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceEntry": { + "request": { + "operation": "ReplaceNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "NetworkInterface": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkInterfaceId" + } + ], + "shape": "NetworkInterface", + "load": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "NetworkInterfaceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkInterfaces[0]" + }, + "actions": { + "AssignPrivateIpAddresses": { + "request": { + "operation": "AssignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Attach": { + "request": { + "operation": "AttachNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Detach": { + "request": { + "operation": "DetachNetworkInterface", + "params": [ + { "target": "AttachmentId", "source": "data", "path": "Attachment.AttachmentId" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "UnassignPrivateIpAddresses": { + "request": { + "operation": "UnassignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Association": { + "resource": { + "type": "NetworkInterfaceAssociation", + "identifiers": [ + { "target": "Id", "source": "data", "path": "Association.AssociationId" } + ], + "path": "Association" + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "NetworkInterfaceAssociation": { + "identifiers": [ + { + "name": "Id" + } + ], + "shape": "InstanceNetworkInterfaceAssociation", + "load": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "association.association-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkInterfaces[0].Association" + }, + "actions": { + "Delete": { + "request": { + "operation": "DisassociateAddress", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Address": { + "resource": { + "type": "VpcAddress", + "identifiers": [ + { "target": "AllocationId", "source": "data", "path": "AllocationId" } + ] + } + } + } + }, + "PlacementGroup": { + "identifiers": [ + { + "name": "Name", + "memberName": "GroupName" + } + ], + "shape": "PlacementGroup", + "load": { + "request": { + "operation": "DescribePlacementGroups", + "params": [ + { "target": "GroupNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "PlacementGroups[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeletePlacementGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "placement-group-name" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + } + } + }, + "Route": { + "identifiers": [ + { "name": "RouteTableId" }, + { + "name": "DestinationCidrBlock", + "memberName": "DestinationCidrBlock" + } + ], + "shape": "Route", + "actions": { + "Delete": { + "request": { + "operation": "DeleteRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "RouteTableId" }, + { "target": "DestinationCidrBlock", "source": "identifier", "name": "DestinationCidrBlock" } + ] + } + }, + "Replace": { + "request": { + "operation": "ReplaceRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "RouteTableId" }, + { "target": "DestinationCidrBlock", "source": "identifier", "name": "DestinationCidrBlock" } + ] + } + } + }, + "has": { + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "identifier", "name": "RouteTableId" } + ] + } + } + } + }, + "RouteTable": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableId" + } + ], + "shape": "RouteTable", + "load": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "RouteTableIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "RouteTables[0]" + }, + "actions": { + "AssociateWithSubnet": { + "request": { + "operation": "AssociateRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "AssociationId" } + ] + } + }, + "CreateRoute": { + "request": { + "operation": "CreateRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Route", + "identifiers": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" }, + { "target": "DestinationCidrBlock", "source": "requestParameter", "path": "DestinationCidrBlock" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Routes": { + "resource": { + "type": "Route", + "identifiers": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" }, + { "target": "DestinationCidrBlock", "source": "data", "path": "Routes[].DestinationCidrBlock" } + ], + "path": "Routes[]" + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Associations": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "RouteTableIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[0].Associations[].RouteTableAssociationId" } + ], + "path": "RouteTables[0].Associations[]" + } + } + } + }, + "RouteTableAssociation": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableAssociationId" + } + ], + "shape": "RouteTableAssociation", + "actions": { + "Delete": { + "request": { + "operation": "DisassociateRouteTable", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceSubnet": { + "request": { + "operation": "ReplaceRouteTableAssociation", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NewAssociationId" } + ] + } + } + }, + "has": { + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RouteTableId" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + } + } + }, + "SecurityGroup": { + "identifiers": [ + { + "name": "Id", + "memberName": "GroupId" + } + ], + "shape": "SecurityGroup", + "load": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "GroupIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "SecurityGroups[0]" + }, + "actions": { + "AuthorizeEgress": { + "request": { + "operation": "AuthorizeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "AuthorizeIngress": { + "request": { + "operation": "AuthorizeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSecurityGroup", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeEgress": { + "request": { + "operation": "RevokeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeIngress": { + "request": { + "operation": "RevokeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Snapshot": { + "identifiers": [ + { + "name": "Id", + "memberName": "SnapshotId" + } + ], + "shape": "Snapshot", + "load": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "SnapshotIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Snapshots[0]" + }, + "actions": { + "Copy": { + "request": { + "operation": "CopySnapshot", + "params": [ + { "target": "SourceSnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSnapshot", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifySnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Completed": { + "waiterName": "SnapshotCompleted", + "params": [ + { "target": "SnapshotIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Snapshots[]" + } + }, + "has": { + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VolumeId" } + ] + } + } + } + }, + "Subnet": { + "identifiers": [ + { + "name": "Id", + "memberName": "SubnetId" + } + ], + "shape": "Subnet", + "load": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "SubnetIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Subnets[0]" + }, + "actions": { + "CreateInstances": { + "request": { + "operation": "RunInstances", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateNetworkInterface": { + "request": { + "operation": "CreateNetworkInterface", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSubnet", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + } + } + }, + "Tag": { + "identifiers": [ + { + "name": "ResourceId", + "memberName": "ResourceId" + }, + { + "name": "Key", + "memberName": "Key" + }, + { + "name": "Value", + "memberName": "Value" + } + ], + "shape": "TagDescription", + "load": { + "request": { + "operation": "DescribeTags", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "key" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Key" }, + { "target": "Filters[1].Name", "source": "string", "value": "value" }, + { "target": "Filters[1].Values[0]", "source": "identifier", "name": "Value" } + ] + }, + "path": "Tags[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[0].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[0].Value", "source": "identifier", "name": "Value" } + ] + } + } + }, + "batchActions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[*].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[*].Value", "source": "identifier", "name": "Value" } + ] + } + } + } + }, + "Volume": { + "identifiers": [ + { + "name": "Id", + "memberName": "VolumeId" + } + ], + "shape": "Volume", + "load": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Volumes[0]" + }, + "actions": { + "AttachToInstance": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateSnapshot": { + "request": { + "operation": "CreateSnapshot", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeStatus": { + "request": { + "operation": "DescribeVolumeStatus", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromInstance": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableIo": { + "request": { + "operation": "EnableVolumeIO", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "hasMany": { + "Snapshots": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "volume-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + } + } + }, + "Vpc": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcId" + } + ], + "shape": "Vpc", + "load": { + "request": { + "operation": "DescribeVpcs", + "params": [ + { "target": "VpcIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Vpcs[0]" + }, + "actions": { + "AssociateDhcpOptions": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachClassicLinkInstance": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachInternetGateway": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateNetworkAcl": { + "request": { + "operation": "CreateNetworkAcl", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateRouteTable": { + "request": { + "operation": "CreateRouteTable", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { + "operation": "CreateSecurityGroup", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSubnet": { + "request": { + "operation": "CreateSubnet", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkInstance": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachInternetGateway": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DisableClassicLink": { + "request": { + "operation": "DisableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableClassicLink": { + "request": { + "operation": "EnableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "RequestVpcPeeringConnection": { + "request": { + "operation": "CreateVpcPeeringConnection", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "data", "path": "DhcpOptionsId" } + ] + } + } + }, + "hasMany": { + "AcceptedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "accepter-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "NetworkAcls": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "RequestedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "requester-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "RouteTables": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Subnets": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + } + } + }, + "VpcPeeringConnection": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcPeeringConnectionId" + } + ], + "shape": "VpcPeeringConnection", + "load": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "VpcPeeringConnectionIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "VpcPeeringConnections[0]" + }, + "actions": { + "Accept": { + "request": { + "operation": "AcceptVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reject": { + "request": { + "operation": "RejectVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "VpcPeeringConnectionExists", + "params": [ + { "target": "VpcPeeringConnectionIds[]", "source": "identifier", "name": "Id" } + ], + "path": "VpcPeeringConnections[0]" + } + }, + "has": { + "AccepterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "AccepterVpcInfo.VpcId" } + ] + } + }, + "RequesterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RequesterVpcInfo.VpcId" } + ] + } + } + } + }, + "VpcAddress": { + "identifiers": [ + { + "name": "AllocationId" + } + ], + "shape": "Address", + "load": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "AllocationIds[0]", "source": "identifier", "name": "AllocationId" } + ] + }, + "path": "Addresses[0]" + }, + "actions": { + "Associate": { + "request": { + "operation": "AssociateAddress", + "params": [ + { "target": "AllocationId", "source": "identifier", "name": "AllocationId" } + ] + } + }, + "Release": { + "request": { + "operation": "ReleaseAddress", + "params": [ + { "target": "AllocationId", "source": "data", "path": "AllocationId" } + ] + } + } + }, + "has": { + "Association": { + "resource": { + "type": "NetworkInterfaceAssociation", + "identifiers": [ + { "target": "Id", "source": "data", "path": "AssociationId" } + ] + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/ec2/2016-09-15/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/ec2/2016-09-15/resources-1.json new file mode 100644 index 0000000..4831a36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/ec2/2016-09-15/resources-1.json @@ -0,0 +1,2567 @@ +{ + "service": { + "actions": { + "CreateDhcpOptions": { + "request": { "operation": "CreateDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions.DhcpOptionsId" } + ], + "path": "DhcpOptions" + } + }, + "CreateInstances": { + "request": { "operation": "RunInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateInternetGateway": { + "request": { "operation": "CreateInternetGateway" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateway.InternetGatewayId" } + ], + "path": "InternetGateway" + } + }, + "CreateKeyPair": { + "request": { "operation": "CreateKeyPair" }, + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ], + "path": "@" + } + }, + "CreateNetworkAcl": { + "request": { "operation": "CreateNetworkAcl" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateNetworkInterface": { + "request": { "operation": "CreateNetworkInterface" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreatePlacementGroup": { + "request": { "operation": "CreatePlacementGroup" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "GroupName" } + ] + } + }, + "CreateRouteTable": { + "request": { "operation": "CreateRouteTable" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { "operation": "CreateSecurityGroup" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSnapshot": { + "request": { "operation": "CreateSnapshot" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateSubnet": { + "request": { "operation": "CreateSubnet" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { "operation": "CreateTags" } + }, + "CreateVolume": { + "request": { "operation": "CreateVolume" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VolumeId" } + ], + "path": "@" + } + }, + "CreateVpc": { + "request": { "operation": "CreateVpc" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpc.VpcId" } + ], + "path": "Vpc" + } + }, + "CreateVpcPeeringConnection": { + "request": { "operation": "CreateVpcPeeringConnection" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + }, + "DisassociateRouteTable": { + "request": { "operation": "DisassociateRouteTable" } + }, + "ImportKeyPair": { + "request": { "operation": "ImportKeyPair" }, + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ] + } + }, + "RegisterImage": { + "request": { "operation": "RegisterImage" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Instance": { + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "InternetGateway": { + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "NetworkAcl": { + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "NetworkInterface": { + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "RouteTableAssociation": { + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "SecurityGroup": { + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Snapshot": { + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "VpcPeeringConnection": { + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + } + }, + "hasMany": { + "ClassicAddresses": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "domain" }, + { "target": "Filters[0].Values[0]", "source": "string", "value": "standard" } + ] + }, + "resource": { + "type": "ClassicAddress", + "identifiers": [ + { "target": "PublicIp", "source": "response", "path": "Addresses[].PublicIp" } + ], + "path": "Addresses[]" + } + }, + "DhcpOptionsSets": { + "request": { "operation": "DescribeDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions[].DhcpOptionsId" } + ], + "path": "DhcpOptions[]" + } + }, + "Images": { + "request": { "operation": "DescribeImages" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Images[].ImageId" } + ], + "path": "Images[]" + } + }, + "Instances": { + "request": { "operation": "DescribeInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { "operation": "DescribeInternetGateways" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "KeyPairs": { + "request": { "operation": "DescribeKeyPairs" }, + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyPairs[].KeyName" } + ], + "path": "KeyPairs[]" + } + }, + "NetworkAcls": { + "request": { "operation": "DescribeNetworkAcls" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { "operation": "DescribeNetworkInterfaces" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "PlacementGroups": { + "request": { "operation": "DescribePlacementGroups" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "response", "path": "PlacementGroups[].GroupName" } + ], + "path": "PlacementGroups[]" + } + }, + "RouteTables": { + "request": { "operation": "DescribeRouteTables" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { "operation": "DescribeSecurityGroups" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Snapshots": { + "request": { "operation": "DescribeSnapshots" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + }, + "Subnets": { + "request": { "operation": "DescribeSubnets" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + }, + "Volumes": { + "request": { "operation": "DescribeVolumes" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + }, + "VpcAddresses": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "domain" }, + { "target": "Filters[0].Values[0]", "source": "string", "value": "vpc" } + ] + }, + "resource": { + "type": "VpcAddress", + "identifiers": [ + { "target": "AllocationId", "source": "response", "path": "Addresses[].AllocationId" } + ], + "path": "Addresses[]" + } + }, + "VpcPeeringConnections": { + "request": { "operation": "DescribeVpcPeeringConnections" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Vpcs": { + "request": { "operation": "DescribeVpcs" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpcs[].VpcId" } + ], + "path": "Vpcs[]" + } + } + } + }, + "resources": { + "ClassicAddress": { + "identifiers": [ + { + "name": "PublicIp" + } + ], + "shape": "Address", + "load": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "PublicIps[]", "source": "identifier", "name": "PublicIp" } + ] + }, + "path": "Addresses[0]" + }, + "actions": { + "Associate": { + "request": { + "operation": "AssociateAddress", + "params": [ + { "target": "PublicIp", "source": "identifier", "name": "PublicIp" } + ] + } + }, + "Disassociate": { + "request": { + "operation": "DisassociateAddress", + "params": [ + { "target": "PublicIp", "source": "data", "path": "PublicIp" } + ] + } + }, + "Release": { + "request": { + "operation": "ReleaseAddress", + "params": [ + { "target": "PublicIp", "source": "data", "path": "PublicIp" } + ] + } + } + } + }, + "DhcpOptions": { + "identifiers": [ + { + "name": "Id", + "memberName": "DhcpOptionsId" + } + ], + "shape": "DhcpOptions", + "load": { + "request": { + "operation": "DescribeDhcpOptions", + "params": [ + { "target": "DhcpOptionsIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "DhcpOptions[0]" + }, + "actions": { + "AssociateWithVpc": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Image": { + "identifiers": [ + { + "name": "Id", + "memberName": "ImageId" + } + ], + "shape": "Image", + "load": { + "request": { + "operation": "DescribeImages", + "params": [ + { "target": "ImageIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Images[0]" + }, + "actions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Deregister": { + "request": { + "operation": "DeregisterImage", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Instance": { + "identifiers": [ + { + "name": "Id", + "memberName": "InstanceId" + } + ], + "shape": "Instance", + "load": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Reservations[0].Instances[0]" + }, + "actions": { + "AttachClassicLinkVpc": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachVolume": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ConsoleOutput": { + "request": { + "operation": "GetConsoleOutput", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateImage": { + "request": { + "operation": "CreateImage", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkVpc": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachVolume": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "PasswordData": { + "request": { + "operation": "GetPasswordData", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ReportStatus": { + "request": { + "operation": "ReportInstanceStatus", + "params": [ + { "target": "Instances[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetKernel": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "kernel" } + ] + } + }, + "ResetRamdisk": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "ramdisk" } + ] + } + }, + "ResetSourceDestCheck": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "sourceDestCheck" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "batchActions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "InstanceExists", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Running": { + "waiterName": "InstanceRunning", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Stopped": { + "waiterName": "InstanceStopped", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Terminated": { + "waiterName": "InstanceTerminated", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + } + }, + "has": { + "ClassicAddress": { + "resource": { + "type": "ClassicAddress", + "identifiers": [ + { "target": "PublicIp", "source": "data", "path": "PublicIpAddress" } + ] + } + }, + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "data", "path": "ImageId" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "data", "path": "KeyName" } + ] + } + }, + "NetworkInterfaces": { + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "data", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "data", "path": "Placement.GroupName" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Volumes": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.instance-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + }, + "VpcAddresses": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "instance-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcAddress", + "identifiers": [ + { "target": "AllocationId", "source": "response", "path": "Addresses[].AllocationId" } + ], + "path": "Addresses[]" + } + } + } + }, + "InternetGateway": { + "identifiers": [ + { + "name": "Id", + "memberName": "InternetGatewayId" + } + ], + "shape": "InternetGateway", + "load": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "InternetGatewayIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "InternetGateways[0]" + }, + "actions": { + "AttachToVpc": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromVpc": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "KeyPair": { + "identifiers": [ + { + "name": "Name", + "memberName": "KeyName" + } + ], + "shape": "KeyPair", + "actions": { + "Delete": { + "request": { + "operation": "DeleteKeyPair", + "params": [ + { "target": "KeyName", "source": "identifier", "name": "Name" } + ] + } + } + } + }, + "KeyPairInfo": { + "identifiers": [ + { + "name": "Name", + "memberName": "KeyName" + } + ], + "shape": "KeyPairInfo", + "load": { + "request": { + "operation": "DescribeKeyPairs", + "params": [ + { "target": "KeyNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "KeyPairs[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteKeyPair", + "params": [ + { "target": "KeyName", "source": "identifier", "name": "Name" } + ] + } + } + } + }, + "NetworkAcl": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkAclId" + } + ], + "shape": "NetworkAcl", + "load": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "NetworkAclIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkAcls[0]" + }, + "actions": { + "CreateEntry": { + "request": { + "operation": "CreateNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkAcl", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "DeleteEntry": { + "request": { + "operation": "DeleteNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceAssociation": { + "request": { + "operation": "ReplaceNetworkAclAssociation", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceEntry": { + "request": { + "operation": "ReplaceNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "NetworkInterface": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkInterfaceId" + } + ], + "shape": "NetworkInterface", + "load": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "NetworkInterfaceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkInterfaces[0]" + }, + "actions": { + "AssignPrivateIpAddresses": { + "request": { + "operation": "AssignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Attach": { + "request": { + "operation": "AttachNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Detach": { + "request": { + "operation": "DetachNetworkInterface", + "params": [ + { "target": "AttachmentId", "source": "data", "path": "Attachment.AttachmentId" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "UnassignPrivateIpAddresses": { + "request": { + "operation": "UnassignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Association": { + "resource": { + "type": "NetworkInterfaceAssociation", + "identifiers": [ + { "target": "Id", "source": "data", "path": "Association.AssociationId" } + ], + "path": "Association" + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "NetworkInterfaceAssociation": { + "identifiers": [ + { + "name": "Id" + } + ], + "shape": "InstanceNetworkInterfaceAssociation", + "load": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "association.association-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkInterfaces[0].Association" + }, + "actions": { + "Delete": { + "request": { + "operation": "DisassociateAddress", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Address": { + "resource": { + "type": "VpcAddress", + "identifiers": [ + { "target": "AllocationId", "source": "data", "path": "AllocationId" } + ] + } + } + } + }, + "PlacementGroup": { + "identifiers": [ + { + "name": "Name", + "memberName": "GroupName" + } + ], + "shape": "PlacementGroup", + "load": { + "request": { + "operation": "DescribePlacementGroups", + "params": [ + { "target": "GroupNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "PlacementGroups[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeletePlacementGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "placement-group-name" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + } + } + }, + "Route": { + "identifiers": [ + { "name": "RouteTableId" }, + { + "name": "DestinationCidrBlock", + "memberName": "DestinationCidrBlock" + } + ], + "shape": "Route", + "actions": { + "Delete": { + "request": { + "operation": "DeleteRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "RouteTableId" }, + { "target": "DestinationCidrBlock", "source": "identifier", "name": "DestinationCidrBlock" } + ] + } + }, + "Replace": { + "request": { + "operation": "ReplaceRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "RouteTableId" }, + { "target": "DestinationCidrBlock", "source": "identifier", "name": "DestinationCidrBlock" } + ] + } + } + }, + "has": { + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "identifier", "name": "RouteTableId" } + ] + } + } + } + }, + "RouteTable": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableId" + } + ], + "shape": "RouteTable", + "load": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "RouteTableIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "RouteTables[0]" + }, + "actions": { + "AssociateWithSubnet": { + "request": { + "operation": "AssociateRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "AssociationId" } + ] + } + }, + "CreateRoute": { + "request": { + "operation": "CreateRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Route", + "identifiers": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" }, + { "target": "DestinationCidrBlock", "source": "requestParameter", "path": "DestinationCidrBlock" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Routes": { + "resource": { + "type": "Route", + "identifiers": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" }, + { "target": "DestinationCidrBlock", "source": "data", "path": "Routes[].DestinationCidrBlock" } + ], + "path": "Routes[]" + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Associations": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "RouteTableIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[0].Associations[].RouteTableAssociationId" } + ], + "path": "RouteTables[0].Associations[]" + } + } + } + }, + "RouteTableAssociation": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableAssociationId" + } + ], + "shape": "RouteTableAssociation", + "actions": { + "Delete": { + "request": { + "operation": "DisassociateRouteTable", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceSubnet": { + "request": { + "operation": "ReplaceRouteTableAssociation", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NewAssociationId" } + ] + } + } + }, + "has": { + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RouteTableId" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + } + } + }, + "SecurityGroup": { + "identifiers": [ + { + "name": "Id", + "memberName": "GroupId" + } + ], + "shape": "SecurityGroup", + "load": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "GroupIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "SecurityGroups[0]" + }, + "actions": { + "AuthorizeEgress": { + "request": { + "operation": "AuthorizeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "AuthorizeIngress": { + "request": { + "operation": "AuthorizeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSecurityGroup", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeEgress": { + "request": { + "operation": "RevokeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeIngress": { + "request": { + "operation": "RevokeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Snapshot": { + "identifiers": [ + { + "name": "Id", + "memberName": "SnapshotId" + } + ], + "shape": "Snapshot", + "load": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "SnapshotIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Snapshots[0]" + }, + "actions": { + "Copy": { + "request": { + "operation": "CopySnapshot", + "params": [ + { "target": "SourceSnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSnapshot", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifySnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Completed": { + "waiterName": "SnapshotCompleted", + "params": [ + { "target": "SnapshotIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Snapshots[]" + } + }, + "has": { + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VolumeId" } + ] + } + } + } + }, + "Subnet": { + "identifiers": [ + { + "name": "Id", + "memberName": "SubnetId" + } + ], + "shape": "Subnet", + "load": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "SubnetIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Subnets[0]" + }, + "actions": { + "CreateInstances": { + "request": { + "operation": "RunInstances", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateNetworkInterface": { + "request": { + "operation": "CreateNetworkInterface", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSubnet", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + } + } + }, + "Tag": { + "identifiers": [ + { + "name": "ResourceId", + "memberName": "ResourceId" + }, + { + "name": "Key", + "memberName": "Key" + }, + { + "name": "Value", + "memberName": "Value" + } + ], + "shape": "TagDescription", + "load": { + "request": { + "operation": "DescribeTags", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "key" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Key" }, + { "target": "Filters[1].Name", "source": "string", "value": "value" }, + { "target": "Filters[1].Values[0]", "source": "identifier", "name": "Value" } + ] + }, + "path": "Tags[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[0].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[0].Value", "source": "identifier", "name": "Value" } + ] + } + } + }, + "batchActions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[*].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[*].Value", "source": "identifier", "name": "Value" } + ] + } + } + } + }, + "Volume": { + "identifiers": [ + { + "name": "Id", + "memberName": "VolumeId" + } + ], + "shape": "Volume", + "load": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Volumes[0]" + }, + "actions": { + "AttachToInstance": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateSnapshot": { + "request": { + "operation": "CreateSnapshot", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeStatus": { + "request": { + "operation": "DescribeVolumeStatus", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromInstance": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableIo": { + "request": { + "operation": "EnableVolumeIO", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "hasMany": { + "Snapshots": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "volume-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + } + } + }, + "Vpc": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcId" + } + ], + "shape": "Vpc", + "load": { + "request": { + "operation": "DescribeVpcs", + "params": [ + { "target": "VpcIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Vpcs[0]" + }, + "actions": { + "AssociateDhcpOptions": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachClassicLinkInstance": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachInternetGateway": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateNetworkAcl": { + "request": { + "operation": "CreateNetworkAcl", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateRouteTable": { + "request": { + "operation": "CreateRouteTable", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { + "operation": "CreateSecurityGroup", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSubnet": { + "request": { + "operation": "CreateSubnet", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkInstance": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachInternetGateway": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DisableClassicLink": { + "request": { + "operation": "DisableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableClassicLink": { + "request": { + "operation": "EnableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "RequestVpcPeeringConnection": { + "request": { + "operation": "CreateVpcPeeringConnection", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "data", "path": "DhcpOptionsId" } + ] + } + } + }, + "hasMany": { + "AcceptedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "accepter-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "NetworkAcls": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "RequestedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "requester-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "RouteTables": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Subnets": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + } + } + }, + "VpcPeeringConnection": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcPeeringConnectionId" + } + ], + "shape": "VpcPeeringConnection", + "load": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "VpcPeeringConnectionIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "VpcPeeringConnections[0]" + }, + "actions": { + "Accept": { + "request": { + "operation": "AcceptVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reject": { + "request": { + "operation": "RejectVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "VpcPeeringConnectionExists", + "params": [ + { "target": "VpcPeeringConnectionIds[]", "source": "identifier", "name": "Id" } + ], + "path": "VpcPeeringConnections[0]" + } + }, + "has": { + "AccepterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "AccepterVpcInfo.VpcId" } + ] + } + }, + "RequesterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RequesterVpcInfo.VpcId" } + ] + } + } + } + }, + "VpcAddress": { + "identifiers": [ + { + "name": "AllocationId" + } + ], + "shape": "Address", + "load": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "AllocationIds[0]", "source": "identifier", "name": "AllocationId" } + ] + }, + "path": "Addresses[0]" + }, + "actions": { + "Associate": { + "request": { + "operation": "AssociateAddress", + "params": [ + { "target": "AllocationId", "source": "identifier", "name": "AllocationId" } + ] + } + }, + "Release": { + "request": { + "operation": "ReleaseAddress", + "params": [ + { "target": "AllocationId", "source": "data", "path": "AllocationId" } + ] + } + } + }, + "has": { + "Association": { + "resource": { + "type": "NetworkInterfaceAssociation", + "identifiers": [ + { "target": "Id", "source": "data", "path": "AssociationId" } + ] + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/ec2/2016-11-15/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/ec2/2016-11-15/resources-1.json new file mode 100644 index 0000000..9872201 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/ec2/2016-11-15/resources-1.json @@ -0,0 +1,2582 @@ +{ + "service": { + "actions": { + "CreateDhcpOptions": { + "request": { "operation": "CreateDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions.DhcpOptionsId" } + ], + "path": "DhcpOptions" + } + }, + "CreateInstances": { + "request": { "operation": "RunInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateInternetGateway": { + "request": { "operation": "CreateInternetGateway" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateway.InternetGatewayId" } + ], + "path": "InternetGateway" + } + }, + "CreateKeyPair": { + "request": { "operation": "CreateKeyPair" }, + "resource": { + "type": "KeyPair", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ], + "path": "@" + } + }, + "CreateNetworkAcl": { + "request": { "operation": "CreateNetworkAcl" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateNetworkInterface": { + "request": { "operation": "CreateNetworkInterface" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreatePlacementGroup": { + "request": { "operation": "CreatePlacementGroup" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "GroupName" } + ] + } + }, + "CreateRouteTable": { + "request": { "operation": "CreateRouteTable" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { "operation": "CreateSecurityGroup" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSnapshot": { + "request": { "operation": "CreateSnapshot" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateSubnet": { + "request": { "operation": "CreateSubnet" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { "operation": "CreateTags" } + }, + "CreateVolume": { + "request": { "operation": "CreateVolume" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VolumeId" } + ], + "path": "@" + } + }, + "CreateVpc": { + "request": { "operation": "CreateVpc" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpc.VpcId" } + ], + "path": "Vpc" + } + }, + "CreateVpcPeeringConnection": { + "request": { "operation": "CreateVpcPeeringConnection" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + }, + "DisassociateRouteTable": { + "request": { "operation": "DisassociateRouteTable" } + }, + "ImportKeyPair": { + "request": { "operation": "ImportKeyPair" }, + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyName" } + ] + } + }, + "RegisterImage": { + "request": { "operation": "RegisterImage" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Instance": { + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "InternetGateway": { + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "NetworkAcl": { + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "NetworkInterface": { + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "RouteTableAssociation": { + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "SecurityGroup": { + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Snapshot": { + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + }, + "VpcPeeringConnection": { + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + } + }, + "hasMany": { + "ClassicAddresses": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "domain" }, + { "target": "Filters[0].Values[0]", "source": "string", "value": "standard" } + ] + }, + "resource": { + "type": "ClassicAddress", + "identifiers": [ + { "target": "PublicIp", "source": "response", "path": "Addresses[].PublicIp" } + ], + "path": "Addresses[]" + } + }, + "DhcpOptionsSets": { + "request": { "operation": "DescribeDhcpOptions" }, + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "response", "path": "DhcpOptions[].DhcpOptionsId" } + ], + "path": "DhcpOptions[]" + } + }, + "Images": { + "request": { "operation": "DescribeImages" }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Images[].ImageId" } + ], + "path": "Images[]" + } + }, + "Instances": { + "request": { "operation": "DescribeInstances" }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { "operation": "DescribeInternetGateways" }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "KeyPairs": { + "request": { "operation": "DescribeKeyPairs" }, + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "response", "path": "KeyPairs[].KeyName" } + ], + "path": "KeyPairs[]" + } + }, + "NetworkAcls": { + "request": { "operation": "DescribeNetworkAcls" }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { "operation": "DescribeNetworkInterfaces" }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "PlacementGroups": { + "request": { "operation": "DescribePlacementGroups" }, + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "response", "path": "PlacementGroups[].GroupName" } + ], + "path": "PlacementGroups[]" + } + }, + "RouteTables": { + "request": { "operation": "DescribeRouteTables" }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { "operation": "DescribeSecurityGroups" }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Snapshots": { + "request": { "operation": "DescribeSnapshots" }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + }, + "Subnets": { + "request": { "operation": "DescribeSubnets" }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + }, + "Volumes": { + "request": { "operation": "DescribeVolumes" }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + }, + "VpcAddresses": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "domain" }, + { "target": "Filters[0].Values[0]", "source": "string", "value": "vpc" } + ] + }, + "resource": { + "type": "VpcAddress", + "identifiers": [ + { "target": "AllocationId", "source": "response", "path": "Addresses[].AllocationId" } + ], + "path": "Addresses[]" + } + }, + "VpcPeeringConnections": { + "request": { "operation": "DescribeVpcPeeringConnections" }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Vpcs": { + "request": { "operation": "DescribeVpcs" }, + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Vpcs[].VpcId" } + ], + "path": "Vpcs[]" + } + } + } + }, + "resources": { + "ClassicAddress": { + "identifiers": [ + { + "name": "PublicIp" + } + ], + "shape": "Address", + "load": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "PublicIps[]", "source": "identifier", "name": "PublicIp" } + ] + }, + "path": "Addresses[0]" + }, + "actions": { + "Associate": { + "request": { + "operation": "AssociateAddress", + "params": [ + { "target": "PublicIp", "source": "identifier", "name": "PublicIp" } + ] + } + }, + "Disassociate": { + "request": { + "operation": "DisassociateAddress", + "params": [ + { "target": "PublicIp", "source": "data", "path": "PublicIp" } + ] + } + }, + "Release": { + "request": { + "operation": "ReleaseAddress", + "params": [ + { "target": "PublicIp", "source": "data", "path": "PublicIp" } + ] + } + } + } + }, + "DhcpOptions": { + "identifiers": [ + { + "name": "Id", + "memberName": "DhcpOptionsId" + } + ], + "shape": "DhcpOptions", + "load": { + "request": { + "operation": "DescribeDhcpOptions", + "params": [ + { "target": "DhcpOptionsIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "DhcpOptions[0]" + }, + "actions": { + "AssociateWithVpc": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteDhcpOptions", + "params": [ + { "target": "DhcpOptionsId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Image": { + "identifiers": [ + { + "name": "Id", + "memberName": "ImageId" + } + ], + "shape": "Image", + "load": { + "request": { + "operation": "DescribeImages", + "params": [ + { "target": "ImageIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Images[0]" + }, + "actions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Deregister": { + "request": { + "operation": "DeregisterImage", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetImageAttribute", + "params": [ + { "target": "ImageId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "ImageExists", + "params": [ + { "target": "ImageIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Images[0]" + } + } + }, + "Instance": { + "identifiers": [ + { + "name": "Id", + "memberName": "InstanceId" + } + ], + "shape": "Instance", + "load": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Reservations[0].Instances[0]" + }, + "actions": { + "AttachClassicLinkVpc": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachVolume": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ConsoleOutput": { + "request": { + "operation": "GetConsoleOutput", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateImage": { + "request": { + "operation": "CreateImage", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "response", "path": "ImageId" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkVpc": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachVolume": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "PasswordData": { + "request": { + "operation": "GetPasswordData", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ReportStatus": { + "request": { + "operation": "ReportInstanceStatus", + "params": [ + { "target": "Instances[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetKernel": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "kernel" } + ] + } + }, + "ResetRamdisk": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "ramdisk" } + ] + } + }, + "ResetSourceDestCheck": { + "request": { + "operation": "ResetInstanceAttribute", + "params": [ + { "target": "InstanceId", "source": "identifier", "name": "Id" }, + { "target": "Attribute", "source": "string", "value": "sourceDestCheck" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[0]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "batchActions": { + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Monitor": { + "request": { + "operation": "MonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Reboot": { + "request": { + "operation": "RebootInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Start": { + "request": { + "operation": "StartInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Stop": { + "request": { + "operation": "StopInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Terminate": { + "request": { + "operation": "TerminateInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "Unmonitor": { + "request": { + "operation": "UnmonitorInstances", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "InstanceExists", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Running": { + "waiterName": "InstanceRunning", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Stopped": { + "waiterName": "InstanceStopped", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + }, + "Terminated": { + "waiterName": "InstanceTerminated", + "params": [ + { "target": "InstanceIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Reservations[0].Instances[0]" + } + }, + "has": { + "ClassicAddress": { + "resource": { + "type": "ClassicAddress", + "identifiers": [ + { "target": "PublicIp", "source": "data", "path": "PublicIpAddress" } + ] + } + }, + "Image": { + "resource": { + "type": "Image", + "identifiers": [ + { "target": "Id", "source": "data", "path": "ImageId" } + ] + } + }, + "KeyPair": { + "resource": { + "type": "KeyPairInfo", + "identifiers": [ + { "target": "Name", "source": "data", "path": "KeyName" } + ] + } + }, + "NetworkInterfaces": { + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "data", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "PlacementGroup": { + "resource": { + "type": "PlacementGroup", + "identifiers": [ + { "target": "Name", "source": "data", "path": "Placement.GroupName" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Volumes": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.instance-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Volumes[].VolumeId" } + ], + "path": "Volumes[]" + } + }, + "VpcAddresses": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "instance-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcAddress", + "identifiers": [ + { "target": "AllocationId", "source": "response", "path": "Addresses[].AllocationId" } + ], + "path": "Addresses[]" + } + } + } + }, + "InternetGateway": { + "identifiers": [ + { + "name": "Id", + "memberName": "InternetGatewayId" + } + ], + "shape": "InternetGateway", + "load": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "InternetGatewayIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "InternetGateways[0]" + }, + "actions": { + "AttachToVpc": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromVpc": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "InternetGatewayId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "KeyPair": { + "identifiers": [ + { + "name": "Name", + "memberName": "KeyName" + } + ], + "shape": "KeyPair", + "actions": { + "Delete": { + "request": { + "operation": "DeleteKeyPair", + "params": [ + { "target": "KeyName", "source": "identifier", "name": "Name" } + ] + } + } + } + }, + "KeyPairInfo": { + "identifiers": [ + { + "name": "Name", + "memberName": "KeyName" + } + ], + "shape": "KeyPairInfo", + "load": { + "request": { + "operation": "DescribeKeyPairs", + "params": [ + { "target": "KeyNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "KeyPairs[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteKeyPair", + "params": [ + { "target": "KeyName", "source": "identifier", "name": "Name" } + ] + } + } + } + }, + "NetworkAcl": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkAclId" + } + ], + "shape": "NetworkAcl", + "load": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "NetworkAclIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkAcls[0]" + }, + "actions": { + "CreateEntry": { + "request": { + "operation": "CreateNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkAcl", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "DeleteEntry": { + "request": { + "operation": "DeleteNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceAssociation": { + "request": { + "operation": "ReplaceNetworkAclAssociation", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceEntry": { + "request": { + "operation": "ReplaceNetworkAclEntry", + "params": [ + { "target": "NetworkAclId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "NetworkInterface": { + "identifiers": [ + { + "name": "Id", + "memberName": "NetworkInterfaceId" + } + ], + "shape": "NetworkInterface", + "load": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "NetworkInterfaceIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkInterfaces[0]" + }, + "actions": { + "AssignPrivateIpAddresses": { + "request": { + "operation": "AssignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Attach": { + "request": { + "operation": "AttachNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteNetworkInterface", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "Detach": { + "request": { + "operation": "DetachNetworkInterface", + "params": [ + { "target": "AttachmentId", "source": "data", "path": "Attachment.AttachmentId" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetNetworkInterfaceAttribute", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + }, + "UnassignPrivateIpAddresses": { + "request": { + "operation": "UnassignPrivateIpAddresses", + "params": [ + { "target": "NetworkInterfaceId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Association": { + "resource": { + "type": "NetworkInterfaceAssociation", + "identifiers": [ + { "target": "Id", "source": "data", "path": "Association.AssociationId" } + ], + "path": "Association" + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "NetworkInterfaceAssociation": { + "identifiers": [ + { + "name": "Id" + } + ], + "shape": "InstanceNetworkInterfaceAssociation", + "load": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "association.association-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "NetworkInterfaces[0].Association" + }, + "actions": { + "Delete": { + "request": { + "operation": "DisassociateAddress", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Address": { + "resource": { + "type": "VpcAddress", + "identifiers": [ + { "target": "AllocationId", "source": "data", "path": "AllocationId" } + ] + } + } + } + }, + "PlacementGroup": { + "identifiers": [ + { + "name": "Name", + "memberName": "GroupName" + } + ], + "shape": "PlacementGroup", + "load": { + "request": { + "operation": "DescribePlacementGroups", + "params": [ + { "target": "GroupNames[0]", "source": "identifier", "name": "Name" } + ] + }, + "path": "PlacementGroups[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeletePlacementGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "placement-group-name" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + } + } + }, + "Route": { + "identifiers": [ + { "name": "RouteTableId" }, + { + "name": "DestinationCidrBlock", + "memberName": "DestinationCidrBlock" + } + ], + "shape": "Route", + "actions": { + "Delete": { + "request": { + "operation": "DeleteRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "RouteTableId" }, + { "target": "DestinationCidrBlock", "source": "identifier", "name": "DestinationCidrBlock" } + ] + } + }, + "Replace": { + "request": { + "operation": "ReplaceRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "RouteTableId" }, + { "target": "DestinationCidrBlock", "source": "identifier", "name": "DestinationCidrBlock" } + ] + } + } + }, + "has": { + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "identifier", "name": "RouteTableId" } + ] + } + } + } + }, + "RouteTable": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableId" + } + ], + "shape": "RouteTable", + "load": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "RouteTableIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "RouteTables[0]" + }, + "actions": { + "AssociateWithSubnet": { + "request": { + "operation": "AssociateRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "AssociationId" } + ] + } + }, + "CreateRoute": { + "request": { + "operation": "CreateRoute", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Route", + "identifiers": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" }, + { "target": "DestinationCidrBlock", "source": "requestParameter", "path": "DestinationCidrBlock" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteRouteTable", + "params": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Associations": { + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "data", "path": "Associations[].RouteTableAssociationId" } + ], + "path": "Associations[]" + } + }, + "Routes": { + "resource": { + "type": "Route", + "identifiers": [ + { "target": "RouteTableId", "source": "identifier", "name": "Id" }, + { "target": "DestinationCidrBlock", "source": "data", "path": "Routes[].DestinationCidrBlock" } + ], + "path": "Routes[]" + } + }, + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + } + }, + "RouteTableAssociation": { + "identifiers": [ + { + "name": "Id", + "memberName": "RouteTableAssociationId" + } + ], + "shape": "RouteTableAssociation", + "actions": { + "Delete": { + "request": { + "operation": "DisassociateRouteTable", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + } + }, + "ReplaceSubnet": { + "request": { + "operation": "ReplaceRouteTableAssociation", + "params": [ + { "target": "AssociationId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTableAssociation", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NewAssociationId" } + ] + } + } + }, + "has": { + "RouteTable": { + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RouteTableId" } + ] + } + }, + "Subnet": { + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "data", "path": "SubnetId" } + ] + } + } + } + }, + "SecurityGroup": { + "identifiers": [ + { + "name": "Id", + "memberName": "GroupId" + } + ], + "shape": "SecurityGroup", + "load": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "GroupIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "SecurityGroups[0]" + }, + "actions": { + "AuthorizeEgress": { + "request": { + "operation": "AuthorizeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "AuthorizeIngress": { + "request": { + "operation": "AuthorizeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSecurityGroup", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeEgress": { + "request": { + "operation": "RevokeSecurityGroupEgress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + }, + "RevokeIngress": { + "request": { + "operation": "RevokeSecurityGroupIngress", + "params": [ + { "target": "GroupId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "Snapshot": { + "identifiers": [ + { + "name": "Id", + "memberName": "SnapshotId" + } + ], + "shape": "Snapshot", + "load": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "SnapshotIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Snapshots[0]" + }, + "actions": { + "Copy": { + "request": { + "operation": "CopySnapshot", + "params": [ + { "target": "SourceSnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSnapshot", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifySnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + }, + "ResetAttribute": { + "request": { + "operation": "ResetSnapshotAttribute", + "params": [ + { "target": "SnapshotId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Completed": { + "waiterName": "SnapshotCompleted", + "params": [ + { "target": "SnapshotIds[]", "source": "identifier", "name": "Id" } + ], + "path": "Snapshots[]" + } + }, + "has": { + "Volume": { + "resource": { + "type": "Volume", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VolumeId" } + ] + } + } + } + }, + "Subnet": { + "identifiers": [ + { + "name": "Id", + "memberName": "SubnetId" + } + ], + "shape": "Subnet", + "load": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "SubnetIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Subnets[0]" + }, + "actions": { + "CreateInstances": { + "request": { + "operation": "RunInstances", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Instances[].InstanceId" } + ], + "path": "Instances[]" + } + }, + "CreateNetworkInterface": { + "request": { + "operation": "CreateNetworkInterface", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterface.NetworkInterfaceId" } + ], + "path": "NetworkInterface" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSubnet", + "params": [ + { "target": "SubnetId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "VpcId" } + ] + } + } + }, + "hasMany": { + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "subnet-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + } + } + }, + "Tag": { + "identifiers": [ + { + "name": "ResourceId", + "memberName": "ResourceId" + }, + { + "name": "Key", + "memberName": "Key" + }, + { + "name": "Value", + "memberName": "Value" + } + ], + "shape": "TagDescription", + "load": { + "request": { + "operation": "DescribeTags", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "key" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Key" }, + { "target": "Filters[1].Name", "source": "string", "value": "value" }, + { "target": "Filters[1].Values[0]", "source": "identifier", "name": "Value" } + ] + }, + "path": "Tags[0]" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[0].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[0].Value", "source": "identifier", "name": "Value" } + ] + } + } + }, + "batchActions": { + "Delete": { + "request": { + "operation": "DeleteTags", + "params": [ + { "target": "Resources[]", "source": "identifier", "name": "ResourceId" }, + { "target": "Tags[*].Key", "source": "identifier", "name": "Key" }, + { "target": "Tags[*].Value", "source": "identifier", "name": "Value" } + ] + } + } + } + }, + "Volume": { + "identifiers": [ + { + "name": "Id", + "memberName": "VolumeId" + } + ], + "shape": "Volume", + "load": { + "request": { + "operation": "DescribeVolumes", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Volumes[0]" + }, + "actions": { + "AttachToInstance": { + "request": { + "operation": "AttachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateSnapshot": { + "request": { + "operation": "CreateSnapshot", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SnapshotId" } + ], + "path": "@" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeStatus": { + "request": { + "operation": "DescribeVolumeStatus", + "params": [ + { "target": "VolumeIds[0]", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachFromInstance": { + "request": { + "operation": "DetachVolume", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableIo": { + "request": { + "operation": "EnableVolumeIO", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVolumeAttribute", + "params": [ + { "target": "VolumeId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "hasMany": { + "Snapshots": { + "request": { + "operation": "DescribeSnapshots", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "volume-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Snapshot", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Snapshots[].SnapshotId" } + ], + "path": "Snapshots[]" + } + } + } + }, + "Vpc": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcId" + } + ], + "shape": "Vpc", + "load": { + "request": { + "operation": "DescribeVpcs", + "params": [ + { "target": "VpcIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "Vpcs[0]" + }, + "actions": { + "AssociateDhcpOptions": { + "request": { + "operation": "AssociateDhcpOptions", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachClassicLinkInstance": { + "request": { + "operation": "AttachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "AttachInternetGateway": { + "request": { + "operation": "AttachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "CreateNetworkAcl": { + "request": { + "operation": "CreateNetworkAcl", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcl.NetworkAclId" } + ], + "path": "NetworkAcl" + } + }, + "CreateRouteTable": { + "request": { + "operation": "CreateRouteTable", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTable.RouteTableId" } + ], + "path": "RouteTable" + } + }, + "CreateSecurityGroup": { + "request": { + "operation": "CreateSecurityGroup", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "GroupId" } + ] + } + }, + "CreateSubnet": { + "request": { + "operation": "CreateSubnet", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnet.SubnetId" } + ], + "path": "Subnet" + } + }, + "CreateTags": { + "request": { + "operation": "CreateTags", + "params": [ + { "target": "Resources[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Tag", + "identifiers": [ + { "target": "ResourceId", "source": "identifier", "name": "Id" }, + { "target": "Key", "source": "requestParameter", "path": "Tags[].Key" }, + { "target": "Value", "source": "requestParameter", "path": "Tags[].Value" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DescribeAttribute": { + "request": { + "operation": "DescribeVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachClassicLinkInstance": { + "request": { + "operation": "DetachClassicLinkVpc", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DetachInternetGateway": { + "request": { + "operation": "DetachInternetGateway", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "DisableClassicLink": { + "request": { + "operation": "DisableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "EnableClassicLink": { + "request": { + "operation": "EnableVpcClassicLink", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "ModifyAttribute": { + "request": { + "operation": "ModifyVpcAttribute", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + } + }, + "RequestVpcPeeringConnection": { + "request": { + "operation": "CreateVpcPeeringConnection", + "params": [ + { "target": "VpcId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnection.VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnection" + } + } + }, + "waiters": { + "Available": { + "waiterName": "VpcAvailable", + "params": [ + { "target": "VpcIds[]", "source": "identifier", "name": "Id" } + ] + }, + "Exists": { + "waiterName": "VpcExists", + "params": [ + { "target": "VpcIds[]", "source": "identifier", "name": "Id" } + ] + } + }, + "has": { + "DhcpOptions": { + "resource": { + "type": "DhcpOptions", + "identifiers": [ + { "target": "Id", "source": "data", "path": "DhcpOptionsId" } + ] + } + } + }, + "hasMany": { + "AcceptedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "accepter-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "Instances": { + "request": { + "operation": "DescribeInstances", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Instance", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Reservations[].Instances[].InstanceId" } + ], + "path": "Reservations[].Instances[]" + } + }, + "InternetGateways": { + "request": { + "operation": "DescribeInternetGateways", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "attachment.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "InternetGateway", + "identifiers": [ + { "target": "Id", "source": "response", "path": "InternetGateways[].InternetGatewayId" } + ], + "path": "InternetGateways[]" + } + }, + "NetworkAcls": { + "request": { + "operation": "DescribeNetworkAcls", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkAcl", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkAcls[].NetworkAclId" } + ], + "path": "NetworkAcls[]" + } + }, + "NetworkInterfaces": { + "request": { + "operation": "DescribeNetworkInterfaces", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "NetworkInterface", + "identifiers": [ + { "target": "Id", "source": "response", "path": "NetworkInterfaces[].NetworkInterfaceId" } + ], + "path": "NetworkInterfaces[]" + } + }, + "RequestedVpcPeeringConnections": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "requester-vpc-info.vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "VpcPeeringConnection", + "identifiers": [ + { "target": "Id", "source": "response", "path": "VpcPeeringConnections[].VpcPeeringConnectionId" } + ], + "path": "VpcPeeringConnections[]" + } + }, + "RouteTables": { + "request": { + "operation": "DescribeRouteTables", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "RouteTable", + "identifiers": [ + { "target": "Id", "source": "response", "path": "RouteTables[].RouteTableId" } + ], + "path": "RouteTables[]" + } + }, + "SecurityGroups": { + "request": { + "operation": "DescribeSecurityGroups", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "SecurityGroup", + "identifiers": [ + { "target": "Id", "source": "response", "path": "SecurityGroups[].GroupId" } + ], + "path": "SecurityGroups[]" + } + }, + "Subnets": { + "request": { + "operation": "DescribeSubnets", + "params": [ + { "target": "Filters[0].Name", "source": "string", "value": "vpc-id" }, + { "target": "Filters[0].Values[0]", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Subnet", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Subnets[].SubnetId" } + ], + "path": "Subnets[]" + } + } + } + }, + "VpcPeeringConnection": { + "identifiers": [ + { + "name": "Id", + "memberName": "VpcPeeringConnectionId" + } + ], + "shape": "VpcPeeringConnection", + "load": { + "request": { + "operation": "DescribeVpcPeeringConnections", + "params": [ + { "target": "VpcPeeringConnectionIds[0]", "source": "identifier", "name": "Id" } + ] + }, + "path": "VpcPeeringConnections[0]" + }, + "actions": { + "Accept": { + "request": { + "operation": "AcceptVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Reject": { + "request": { + "operation": "RejectVpcPeeringConnection", + "params": [ + { "target": "VpcPeeringConnectionId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "VpcPeeringConnectionExists", + "params": [ + { "target": "VpcPeeringConnectionIds[]", "source": "identifier", "name": "Id" } + ], + "path": "VpcPeeringConnections[0]" + } + }, + "has": { + "AccepterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "AccepterVpcInfo.VpcId" } + ] + } + }, + "RequesterVpc": { + "resource": { + "type": "Vpc", + "identifiers": [ + { "target": "Id", "source": "data", "path": "RequesterVpcInfo.VpcId" } + ] + } + } + } + }, + "VpcAddress": { + "identifiers": [ + { + "name": "AllocationId" + } + ], + "shape": "Address", + "load": { + "request": { + "operation": "DescribeAddresses", + "params": [ + { "target": "AllocationIds[0]", "source": "identifier", "name": "AllocationId" } + ] + }, + "path": "Addresses[0]" + }, + "actions": { + "Associate": { + "request": { + "operation": "AssociateAddress", + "params": [ + { "target": "AllocationId", "source": "identifier", "name": "AllocationId" } + ] + } + }, + "Release": { + "request": { + "operation": "ReleaseAddress", + "params": [ + { "target": "AllocationId", "source": "data", "path": "AllocationId" } + ] + } + } + }, + "has": { + "Association": { + "resource": { + "type": "NetworkInterfaceAssociation", + "identifiers": [ + { "target": "Id", "source": "data", "path": "AssociationId" } + ] + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/glacier/2012-06-01/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/glacier/2012-06-01/resources-1.json new file mode 100644 index 0000000..d1ed48f --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/glacier/2012-06-01/resources-1.json @@ -0,0 +1,581 @@ +{ + "service": { + "actions": { + "CreateVault": { + "request": { + "operation": "CreateVault", + "params": [ + { "target": "accountId", "source": "string", "value": "-" } + ] + }, + "resource": { + "type": "Vault", + "identifiers": [ + { "target": "AccountId", "source": "requestParameter", "path": "accountId" }, + { "target": "Name", "source": "requestParameter", "path": "vaultName" } + ] + } + } + }, + "has": { + "Account": { + "resource": { + "type": "Account", + "identifiers": [ + { "target": "Id", "source": "input" } + ] + } + } + }, + "hasMany": { + "Vaults": { + "request": { + "operation": "ListVaults", + "params": [ + { "target": "accountId", "source": "string", "value": "-" } + ] + }, + "resource": { + "type": "Vault", + "identifiers": [ + { "target": "AccountId", "source": "requestParameter", "path": "accountId" }, + { "target": "Name", "source": "response", "path": "VaultList[].VaultName" } + ], + "path": "VaultList[]" + } + } + } + }, + "resources": { + "Account": { + "identifiers": [ + { "name": "Id" } + ], + "actions": { + "CreateVault": { + "request": { + "operation": "CreateVault", + "params": [ + { "target": "accountId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Vault", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "Id" }, + { "target": "Name", "source": "requestParameter", "path": "vaultName" } + ] + } + } + }, + "has": { + "Vault": { + "resource": { + "type": "Vault", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "Id" }, + { "target": "Name", "source": "input" } + ] + } + } + }, + "hasMany": { + "Vaults": { + "request": { + "operation": "ListVaults", + "params": [ + { "target": "accountId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Vault", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "Id" }, + { "target": "Name", "source": "response", "path": "VaultList[].VaultName" } + ], + "path": "VaultList[]" + } + } + } + }, + "Archive": { + "identifiers": [ + { "name": "AccountId" }, + { "name": "VaultName" }, + { "name": "Id" } + ], + "actions": { + "Delete": { + "request": { + "operation": "DeleteArchive", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "VaultName" }, + { "target": "archiveId", "source": "identifier", "name": "Id" } + ] + } + }, + "InitiateArchiveRetrieval": { + "request": { + "operation": "InitiateJob", + "params": [ + { "target": "vaultName", "source": "identifier", "name": "VaultName" }, + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "jobParameters.Type", "source": "string", "value": "archive-retrieval" }, + { "target": "jobParameters.ArchiveId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Job", + "identifiers": [ + { "target": "Id", "source": "response", "path": "jobId" }, + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "VaultName" } + ] + } + } + }, + "has": { + "Vault": { + "resource": { + "type": "Vault", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "Name", "source": "identifier", "name": "VaultName" } + ] + } + } + } + }, + "Job": { + "identifiers": [ + { "name": "AccountId" }, + { "name": "VaultName" }, + { + "name": "Id", + "memberName": "JobId" + } + ], + "shape": "GlacierJobDescription", + "load": { + "request": { + "operation": "DescribeJob", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "VaultName" }, + { "target": "jobId", "source": "identifier", "name": "Id" } + ] + }, + "path": "@" + }, + "actions": { + "GetOutput": { + "request": { + "operation": "GetJobOutput", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "VaultName" }, + { "target": "jobId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vault": { + "resource": { + "type": "Vault", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "Name", "source": "identifier", "name": "VaultName" } + ] + } + } + } + }, + "MultipartUpload": { + "identifiers": [ + { "name": "AccountId" }, + { "name": "VaultName" }, + { + "name": "Id", + "memberName": "MultipartUploadId" + } + ], + "shape": "UploadListElement", + "actions": { + "Abort": { + "request": { + "operation": "AbortMultipartUpload", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "VaultName" }, + { "target": "uploadId", "source": "identifier", "name": "Id" } + ] + } + }, + "Complete": { + "request": { + "operation": "CompleteMultipartUpload", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "VaultName" }, + { "target": "uploadId", "source": "identifier", "name": "Id" } + ] + } + }, + "Parts": { + "request": { + "operation": "ListParts", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "VaultName" }, + { "target": "uploadId", "source": "identifier", "name": "Id" } + ] + } + }, + "UploadPart": { + "request": { + "operation": "UploadMultipartPart", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "VaultName" }, + { "target": "uploadId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Vault": { + "resource": { + "type": "Vault", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "Name", "source": "identifier", "name": "VaultName" } + ] + } + } + } + }, + "Notification": { + "identifiers": [ + { "name": "AccountId" }, + { "name": "VaultName" } + ], + "shape": "VaultNotificationConfig", + "load": { + "request": { + "operation": "GetVaultNotifications", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "VaultName" } + ] + }, + "path": "vaultNotificationConfig" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteVaultNotifications", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "VaultName" } + ] + } + }, + "Set": { + "request": { + "operation": "SetVaultNotifications", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "VaultName" } + ] + } + } + }, + "has": { + "Vault": { + "resource": { + "type": "Vault", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "Name", "source": "identifier", "name": "VaultName" } + ] + } + } + } + }, + "Vault": { + "identifiers": [ + { "name": "AccountId" }, + { + "name": "Name", + "memberName": "VaultName" + } + ], + "shape": "DescribeVaultOutput", + "load": { + "request": { + "operation": "DescribeVault", + "params": [ + { "target": "vaultName", "source": "identifier", "name": "Name" }, + { "target": "accountId", "source": "identifier", "name": "AccountId" } + ] + }, + "path": "@" + }, + "actions": { + "Create": { + "request": { + "operation": "CreateVault", + "params": [ + { "target": "vaultName", "source": "identifier", "name": "Name" }, + { "target": "accountId", "source": "identifier", "name": "AccountId" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteVault", + "params": [ + { "target": "vaultName", "source": "identifier", "name": "Name" }, + { "target": "accountId", "source": "identifier", "name": "AccountId" } + ] + } + }, + "InitiateInventoryRetrieval": { + "request": { + "operation": "InitiateJob", + "params": [ + { "target": "vaultName", "source": "identifier", "name": "Name" }, + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "jobParameters.Type", "source": "string", "value": "inventory-retrieval" } + ] + }, + "resource": { + "type": "Job", + "identifiers": [ + { "target": "Id", "source": "response", "path": "jobId" }, + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" } + ] + } + }, + "InitiateMultipartUpload": { + "request": { + "operation": "InitiateMultipartUpload", + "params": [ + { "target": "vaultName", "source": "identifier", "name": "Name" }, + { "target": "accountId", "source": "identifier", "name": "AccountId" } + ] + }, + "resource": { + "type": "MultipartUpload", + "identifiers": [ + { "target": "Id", "source": "response", "path": "uploadId" }, + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" } + ] + } + }, + "UploadArchive": { + "request": { + "operation": "UploadArchive", + "params": [ + { "target": "vaultName", "source": "identifier", "name": "Name" }, + { "target": "accountId", "source": "identifier", "name": "AccountId" } + ] + }, + "resource": { + "type": "Archive", + "identifiers": [ + { "target": "Id", "source": "response", "path": "archiveId" }, + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "has": { + "Account": { + "resource": { + "type": "Account", + "identifiers": [ + { "target": "Id", "source": "identifier", "name": "AccountId" } + ] + } + }, + "Archive": { + "resource": { + "type": "Archive", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "input" } + ] + } + }, + "Job": { + "resource": { + "type": "Job", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "input" } + ] + } + }, + "MultipartUpload": { + "resource": { + "type": "MultipartUpload", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "input" } + ] + } + }, + "Notification": { + "resource": { + "type": "Notification", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "hasMany": { + "CompletedJobs": { + "request": { + "operation": "ListJobs", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "Name" }, + { "target": "completed", "source": "string", "value": "true" } + ] + }, + "resource": { + "type": "Job", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "response", "path": "JobList[].JobId" } + ], + "path": "JobList[]" + } + }, + "FailedJobs": { + "request": { + "operation": "ListJobs", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "Name" }, + { "target": "statuscode", "source": "string", "value": "Failed" } + ] + }, + "resource": { + "type": "Job", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "response", "path": "JobList[].JobId" } + ], + "path": "JobList[]" + } + }, + "Jobs": { + "request": { + "operation": "ListJobs", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Job", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "response", "path": "JobList[].JobId" } + ], + "path": "JobList[]" + } + }, + "JobsInProgress": { + "request": { + "operation": "ListJobs", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "Name" }, + { "target": "statuscode", "source": "string", "value": "InProgress" } + ] + }, + "resource": { + "type": "Job", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "response", "path": "JobList[].JobId" } + ], + "path": "JobList[]" + } + }, + "MultipartUplaods": { + "request": { + "operation": "ListMultipartUploads", + "params": [ + { "target": "vaultName", "source": "identifier", "name": "Name" }, + { "target": "accountId", "source": "identifier", "name": "AccountId" } + ] + }, + "resource": { + "type": "MultipartUpload", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "response", "path": "UploadsList[].MultipartUploadId" } + ], + "path": "UploadsList[]" + } + }, + "MultipartUploads": { + "request": { + "operation": "ListMultipartUploads", + "params": [ + { "target": "vaultName", "source": "identifier", "name": "Name" }, + { "target": "accountId", "source": "identifier", "name": "AccountId" } + ] + }, + "resource": { + "type": "MultipartUpload", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "response", "path": "UploadsList[].MultipartUploadId" } + ], + "path": "UploadsList[]" + } + }, + "SucceededJobs": { + "request": { + "operation": "ListJobs", + "params": [ + { "target": "accountId", "source": "identifier", "name": "AccountId" }, + { "target": "vaultName", "source": "identifier", "name": "Name" }, + { "target": "statuscode", "source": "string", "value": "Succeeded" } + ] + }, + "resource": { + "type": "Job", + "identifiers": [ + { "target": "AccountId", "source": "identifier", "name": "AccountId" }, + { "target": "VaultName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "response", "path": "JobList[].JobId" } + ], + "path": "JobList[]" + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/iam/2010-05-08/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/iam/2010-05-08/resources-1.json new file mode 100644 index 0000000..59d1855 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/iam/2010-05-08/resources-1.json @@ -0,0 +1,1721 @@ +{ + "service": { + "actions": { + "ChangePassword": { + "request": { "operation": "ChangePassword" } + }, + "CreateAccountAlias": { + "request": { "operation": "CreateAccountAlias" } + }, + "CreateAccountPasswordPolicy": { + "request": { "operation": "UpdateAccountPasswordPolicy" }, + "resource": { + "type": "AccountPasswordPolicy", + "identifiers": [ ] + } + }, + "CreateGroup": { + "request": { "operation": "CreateGroup" }, + "resource": { + "type": "Group", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "GroupName" } + ], + "path": "Group" + } + }, + "CreateInstanceProfile": { + "request": { "operation": "CreateInstanceProfile" }, + "resource": { + "type": "InstanceProfile", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "InstanceProfileName" } + ], + "path": "InstanceProfile" + } + }, + "CreatePolicy": { + "request": { "operation": "CreatePolicy" }, + "resource": { + "type": "Policy", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "Policy.Arn" } + ] + } + }, + "CreateRole": { + "request": { "operation": "CreateRole" }, + "resource": { + "type": "Role", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "RoleName" } + ], + "path": "Role" + } + }, + "CreateSamlProvider": { + "request": { "operation": "CreateSAMLProvider" }, + "resource": { + "type": "SamlProvider", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "SAMLProviderArn" } + ] + } + }, + "CreateServerCertificate": { + "request": { "operation": "UploadServerCertificate" }, + "resource": { + "type": "ServerCertificate", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "ServerCertificateName" } + ] + } + }, + "CreateSigningCertificate": { + "request": { "operation": "UploadSigningCertificate" }, + "resource": { + "type": "SigningCertificate", + "identifiers": [ + { "target": "Id", "source": "response", "path": "Certificate.CertificateId" } + ], + "path": "Certificate" + } + }, + "CreateUser": { + "request": { "operation": "CreateUser" }, + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "UserName" } + ], + "path": "User" + } + }, + "CreateVirtualMfaDevice": { + "request": { "operation": "CreateVirtualMFADevice" }, + "resource": { + "type": "VirtualMfaDevice", + "identifiers": [ + { "target": "SerialNumber", "source": "response", "path": "VirtualMFADevice.SerialNumber" } + ], + "path": "VirtualMFADevice" + } + } + }, + "has": { + "AccountPasswordPolicy": { + "resource": { + "type": "AccountPasswordPolicy", + "identifiers": [ ] + } + }, + "AccountSummary": { + "resource": { + "type": "AccountSummary", + "identifiers": [ ] + } + }, + "CurrentUser": { + "resource": { + "type": "CurrentUser", + "identifiers": [ ] + } + }, + "Group": { + "resource": { + "type": "Group", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "InstanceProfile": { + "resource": { + "type": "InstanceProfile", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "Policy": { + "resource": { + "type": "Policy", + "identifiers": [ + { "target": "PolicyArn", "source": "input" } + ] + } + }, + "Role": { + "resource": { + "type": "Role", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "SamlProvider": { + "resource": { + "type": "SamlProvider", + "identifiers": [ + { "target": "Arn", "source": "input" } + ] + } + }, + "ServerCertificate": { + "resource": { + "type": "ServerCertificate", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "User": { + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + }, + "VirtualMfaDevice": { + "resource": { + "type": "VirtualMfaDevice", + "identifiers": [ + { "target": "SerialNumber", "source": "input" } + ] + } + } + }, + "hasMany": { + "Groups": { + "request": { "operation": "ListGroups" }, + "resource": { + "type": "Group", + "identifiers": [ + { "target": "Name", "source": "response", "path": "Groups[].GroupName" } + ], + "path": "Groups[]" + } + }, + "InstanceProfiles": { + "request": { "operation": "ListInstanceProfiles" }, + "resource": { + "type": "InstanceProfile", + "identifiers": [ + { "target": "Name", "source": "response", "path": "InstanceProfiles[].InstanceProfileName" } + ], + "path": "InstanceProfiles[]" + } + }, + "Policies": { + "request": { "operation": "ListPolicies" }, + "resource": { + "type": "Policy", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "Policies[].Arn" } + ], + "path": "Policies[]" + } + }, + "Roles": { + "request": { "operation": "ListRoles" }, + "resource": { + "type": "Role", + "identifiers": [ + { "target": "Name", "source": "response", "path": "Roles[].RoleName" } + ], + "path": "Roles[]" + } + }, + "SamlProviders": { + "request": { "operation": "ListSAMLProviders" }, + "resource": { + "type": "SamlProvider", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "SAMLProviderList[].Arn" } + ] + } + }, + "ServerCertificates": { + "request": { "operation": "ListServerCertificates" }, + "resource": { + "type": "ServerCertificate", + "identifiers": [ + { "target": "Name", "source": "response", "path": "ServerCertificateMetadataList[].ServerCertificateName" } + ] + } + }, + "Users": { + "request": { "operation": "ListUsers" }, + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "response", "path": "Users[].UserName" } + ], + "path": "Users[]" + } + }, + "VirtualMfaDevices": { + "request": { "operation": "ListVirtualMFADevices" }, + "resource": { + "type": "VirtualMfaDevice", + "identifiers": [ + { "target": "SerialNumber", "source": "response", "path": "VirtualMFADevices[].SerialNumber" } + ], + "path": "VirtualMFADevices[]" + } + } + } + }, + "resources": { + "AccessKey": { + "identifiers": [ + { + "name": "UserName", + "memberName": "UserName" + }, + { + "name": "Id", + "memberName": "AccessKeyId" + } + ], + "shape": "AccessKeyMetadata", + "actions": { + "Activate": { + "request": { + "operation": "UpdateAccessKey", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "AccessKeyId", "source": "identifier", "name": "Id" }, + { "target": "Status", "source": "string", "value": "Active" } + ] + } + }, + "Deactivate": { + "request": { + "operation": "UpdateAccessKey", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "AccessKeyId", "source": "identifier", "name": "Id" }, + { "target": "Status", "source": "string", "value": "Inactive" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteAccessKey", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "AccessKeyId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "User": { + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "UserName" } + ] + } + } + } + }, + "AccessKeyPair": { + "identifiers": [ + { + "name": "UserName", + "memberName": "UserName" + }, + { + "name": "Id", + "memberName": "AccessKeyId" + }, + { + "name": "Secret", + "memberName": "SecretAccessKey" + } + ], + "shape": "AccessKey", + "actions": { + "Activate": { + "request": { + "operation": "UpdateAccessKey", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "AccessKeyId", "source": "identifier", "name": "Id" }, + { "target": "Status", "source": "string", "value": "Active" } + ] + } + }, + "Deactivate": { + "request": { + "operation": "UpdateAccessKey", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "AccessKeyId", "source": "identifier", "name": "Id" }, + { "target": "Status", "source": "string", "value": "Inactive" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteAccessKey", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "AccessKeyId", "source": "identifier", "name": "Id" } + ] + } + } + } + }, + "AccountPasswordPolicy": { + "identifiers": [ ], + "shape": "PasswordPolicy", + "load": { + "request": { "operation": "GetAccountPasswordPolicy" }, + "path": "PasswordPolicy" + }, + "actions": { + "Delete": { + "request": { "operation": "DeleteAccountPasswordPolicy" } + }, + "Update": { + "request": { "operation": "UpdateAccountPasswordPolicy" } + } + } + }, + "AccountSummary": { + "identifiers": [ ], + "shape": "GetAccountSummaryResponse", + "load": { + "request": { "operation": "GetAccountSummary" }, + "path": "@" + } + }, + "AssumeRolePolicy": { + "identifiers": [ + { "name": "RoleName" } + ], + "actions": { + "Update": { + "request": { + "operation": "UpdateAssumeRolePolicy", + "params": [ + { "target": "RoleName", "source": "identifier", "name": "RoleName" } + ] + } + } + }, + "has": { + "Role": { + "resource": { + "type": "Role", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "RoleName" } + ] + } + } + } + }, + "CurrentUser": { + "identifiers": [ ], + "shape": "User", + "load": { + "request": { "operation": "GetUser" }, + "path": "User" + }, + "has": { + "User": { + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "data", "path": "UserName" } + ] + } + } + }, + "hasMany": { + "AccessKeys": { + "request": { "operation": "ListAccessKeys" }, + "resource": { + "type": "AccessKey", + "identifiers": [ + { "target": "UserName", "source": "response", "path": "AccessKeyMetadata[].UserName" }, + { "target": "Id", "source": "response", "path": "AccessKeyMetadata[].AccessKeyId" } + ], + "path": "AccessKeyMetadata[]" + } + }, + "MfaDevices": { + "request": { "operation": "ListMFADevices" }, + "resource": { + "type": "MfaDevice", + "identifiers": [ + { "target": "UserName", "source": "response", "path": "MFADevices[].UserName" }, + { "target": "SerialNumber", "source": "response", "path": "MFADevices[].SerialNumber" } + ], + "path": "MFADevices[]" + } + }, + "SigningCertificates": { + "request": { "operation": "ListSigningCertificates" }, + "resource": { + "type": "SigningCertificate", + "identifiers": [ + { "target": "UserName", "source": "response", "path": "Certificates[].UserName" }, + { "target": "Id", "source": "response", "path": "Certificates[].CertificateId" } + ], + "path": "Certificates[]" + } + } + } + }, + "Group": { + "identifiers": [ + { + "name": "Name", + "memberName": "GroupName" + } + ], + "shape": "Group", + "load": { + "request": { + "operation": "GetGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + }, + "path": "Group" + }, + "actions": { + "AddUser": { + "request": { + "operation": "AddUserToGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + } + }, + "AttachPolicy": { + "request": { + "operation": "AttachGroupPolicy", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + } + }, + "Create": { + "request": { + "operation": "CreateGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Group", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "GroupName" } + ], + "path": "Group" + } + }, + "CreatePolicy": { + "request": { + "operation": "PutGroupPolicy", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "GroupPolicy", + "identifiers": [ + { "target": "GroupName", "source": "identifier", "name": "Name" }, + { "target": "Name", "source": "requestParameter", "path": "PolicyName" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + } + }, + "DetachPolicy": { + "request": { + "operation": "DetachGroupPolicy", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + } + }, + "RemoveUser": { + "request": { + "operation": "RemoveUserFromGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + } + }, + "Update": { + "request": { + "operation": "UpdateGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Group", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "NewGroupName" } + ] + } + } + }, + "has": { + "Policy": { + "resource": { + "type": "GroupPolicy", + "identifiers": [ + { "target": "GroupName", "source": "identifier", "name": "Name" }, + { "target": "Name", "source": "input" } + ] + } + } + }, + "hasMany": { + "AttachedPolicies": { + "request": { + "operation": "ListAttachedGroupPolicies", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Policy", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "AttachedPolicies[].PolicyArn" } + ] + } + }, + "Policies": { + "request": { + "operation": "ListGroupPolicies", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "GroupPolicy", + "identifiers": [ + { "target": "GroupName", "source": "identifier", "name": "Name" }, + { "target": "Name", "source": "response", "path": "PolicyNames[]" } + ] + } + }, + "Users": { + "request": { + "operation": "GetGroup", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "response", "path": "Users[].UserName" } + ], + "path": "Users[]" + } + } + } + }, + "GroupPolicy": { + "identifiers": [ + { + "name": "GroupName", + "memberName": "GroupName" + }, + { + "name": "Name", + "memberName": "PolicyName" + } + ], + "shape": "GetGroupPolicyResponse", + "load": { + "request": { + "operation": "GetGroupPolicy", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "GroupName" }, + { "target": "PolicyName", "source": "identifier", "name": "Name" } + ] + }, + "path": "@" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteGroupPolicy", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "GroupName" }, + { "target": "PolicyName", "source": "identifier", "name": "Name" } + ] + } + }, + "Put": { + "request": { + "operation": "PutGroupPolicy", + "params": [ + { "target": "GroupName", "source": "identifier", "name": "GroupName" }, + { "target": "PolicyName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "has": { + "Group": { + "resource": { + "type": "Group", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "GroupName" } + ] + } + } + } + }, + "InstanceProfile": { + "identifiers": [ + { + "name": "Name", + "memberName": "InstanceProfileName" + } + ], + "shape": "InstanceProfile", + "load": { + "request": { + "operation": "GetInstanceProfile", + "params": [ + { "target": "InstanceProfileName", "source": "identifier", "name": "Name" } + ] + }, + "path": "InstanceProfile" + }, + "actions": { + "AddRole": { + "request": { + "operation": "AddRoleToInstanceProfile", + "params": [ + { "target": "InstanceProfileName", "source": "identifier", "name": "Name" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteInstanceProfile", + "params": [ + { "target": "InstanceProfileName", "source": "identifier", "name": "Name" } + ] + } + }, + "RemoveRole": { + "request": { + "operation": "RemoveRoleFromInstanceProfile", + "params": [ + { "target": "InstanceProfileName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "has": { + "Roles": { + "resource": { + "type": "Role", + "identifiers": [ + { "target": "Name", "source": "data", "path": "Roles[].RoleName" } + ], + "path": "Roles[]" + } + } + } + }, + "LoginProfile": { + "identifiers": [ + { + "name": "UserName", + "memberName": "UserName" + } + ], + "shape": "LoginProfile", + "load": { + "request": { + "operation": "GetLoginProfile", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" } + ] + }, + "path": "LoginProfile" + }, + "actions": { + "Create": { + "request": { + "operation": "CreateLoginProfile", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" } + ] + }, + "resource": { + "type": "LoginProfile", + "identifiers": [ + { "target": "UserName", "source": "response", "path": "LoginProfile.UserName" } + ], + "path": "LoginProfile" + } + }, + "Delete": { + "request": { + "operation": "DeleteLoginProfile", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" } + ] + } + }, + "Update": { + "request": { + "operation": "UpdateLoginProfile", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" } + ] + } + } + }, + "has": { + "User": { + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "UserName" } + ] + } + } + } + }, + "MfaDevice": { + "identifiers": [ + { + "name": "UserName", + "memberName": "UserName" + }, + { + "name": "SerialNumber", + "memberName": "SerialNumber" + } + ], + "shape": "MFADevice", + "actions": { + "Associate": { + "request": { + "operation": "EnableMFADevice", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "SerialNumber", "source": "identifier", "name": "SerialNumber" } + ] + } + }, + "Disassociate": { + "request": { + "operation": "DeactivateMFADevice", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "SerialNumber", "source": "identifier", "name": "SerialNumber" } + ] + } + }, + "Resync": { + "request": { + "operation": "ResyncMFADevice", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "SerialNumber", "source": "identifier", "name": "SerialNumber" } + ] + } + } + }, + "has": { + "User": { + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "UserName" } + ] + } + } + } + }, + "Policy": { + "identifiers": [ + { + "name": "Arn", + "memberName": "Arn" + } + ], + "shape": "Policy", + "load": { + "request": { + "operation": "GetPolicy", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" } + ] + }, + "path": "Policy" + }, + "actions": { + "AttachGroup": { + "request": { + "operation": "AttachGroupPolicy", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "AttachRole": { + "request": { + "operation": "AttachRolePolicy", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "AttachUser": { + "request": { + "operation": "AttachUserPolicy", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "CreateVersion": { + "request": { + "operation": "CreatePolicyVersion", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" } + ] + }, + "resource": { + "type": "PolicyVersion", + "identifiers": [ + { "target": "Arn", "source": "identifier", "name": "Arn" }, + { "target": "VersionId", "source": "response", "path": "PolicyVersion.VersionId" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeletePolicy", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "DetachGroup": { + "request": { + "operation": "DetachGroupPolicy", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "DetachRole": { + "request": { + "operation": "DetachRolePolicy", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "DetachUser": { + "request": { + "operation": "DetachUserPolicy", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" } + ] + } + } + }, + "has": { + "DefaultVersion": { + "resource": { + "type": "PolicyVersion", + "identifiers": [ + { "target": "Arn", "source": "identifier", "name": "Arn" }, + { "target": "VersionId", "source": "data", "path": "DefaultVersionId" } + ] + } + } + }, + "hasMany": { + "AttachedGroups": { + "request": { + "operation": "ListEntitiesForPolicy", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" }, + { "target": "EntityFilter", "source": "string", "value": "Group" } + ] + }, + "resource": { + "type": "Group", + "identifiers": [ + { "target": "Name", "source": "response", "path": "PolicyGroups[].GroupName" } + ] + } + }, + "AttachedRoles": { + "request": { + "operation": "ListEntitiesForPolicy", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" }, + { "target": "EntityFilter", "source": "string", "value": "Role" } + ] + }, + "resource": { + "type": "Role", + "identifiers": [ + { "target": "Name", "source": "response", "path": "PolicyRoles[].RoleName" } + ] + } + }, + "AttachedUsers": { + "request": { + "operation": "ListEntitiesForPolicy", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" }, + { "target": "EntityFilter", "source": "string", "value": "User" } + ] + }, + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "response", "path": "PolicyUsers[].UserName" } + ] + } + }, + "Versions": { + "request": { + "operation": "ListPolicyVersions", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" } + ] + }, + "resource": { + "type": "PolicyVersion", + "identifiers": [ + { "target": "Arn", "source": "identifier", "name": "Arn" }, + { "target": "VersionId", "source": "response", "path": "Versions[].VersionId" } + ], + "path": "Versions[]" + } + } + } + }, + "PolicyVersion": { + "identifiers": [ + { "name": "Arn" }, + { "name": "VersionId" } + ], + "shape": "PolicyVersion", + "load": { + "request": { + "operation": "GetPolicyVersion", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" }, + { "target": "VersionId", "source": "identifier", "name": "VersionId" } + ] + }, + "path": "PolicyVersion" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeletePolicyVersion", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" }, + { "target": "VersionId", "source": "identifier", "name": "VersionId" } + ] + } + }, + "SetAsDefault": { + "request": { + "operation": "SetDefaultPolicyVersion", + "params": [ + { "target": "PolicyArn", "source": "identifier", "name": "Arn" }, + { "target": "VersionId", "source": "identifier", "name": "VersionId" } + ] + } + } + } + }, + "Role": { + "identifiers": [ + { + "name": "Name", + "memberName": "RoleName" + } + ], + "shape": "Role", + "load": { + "request": { + "operation": "GetRole", + "params": [ + { "target": "RoleName", "source": "identifier", "name": "Name" } + ] + }, + "path": "Role" + }, + "actions": { + "AttachPolicy": { + "request": { + "operation": "AttachRolePolicy", + "params": [ + { "target": "RoleName", "source": "identifier", "name": "Name" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteRole", + "params": [ + { "target": "RoleName", "source": "identifier", "name": "Name" } + ] + } + }, + "DetachPolicy": { + "request": { + "operation": "DetachRolePolicy", + "params": [ + { "target": "RoleName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "has": { + "AssumeRolePolicy": { + "resource": { + "type": "AssumeRolePolicy", + "identifiers": [ + { "target": "RoleName", "source": "identifier", "name": "Name" } + ] + } + }, + "Policy": { + "resource": { + "type": "RolePolicy", + "identifiers": [ + { "target": "RoleName", "source": "identifier", "name": "Name" }, + { "target": "Name", "source": "input" } + ] + } + } + }, + "hasMany": { + "AttachedPolicies": { + "request": { + "operation": "ListAttachedRolePolicies", + "params": [ + { "target": "RoleName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Policy", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "AttachedPolicies[].PolicyArn" } + ] + } + }, + "InstanceProfiles": { + "request": { + "operation": "ListInstanceProfilesForRole", + "params": [ + { "target": "RoleName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "InstanceProfile", + "identifiers": [ + { "target": "Name", "source": "response", "path": "InstanceProfiles[].InstanceProfileName" } + ], + "path": "InstanceProfiles[]" + } + }, + "Policies": { + "request": { + "operation": "ListRolePolicies", + "params": [ + { "target": "RoleName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "RolePolicy", + "identifiers": [ + { "target": "RoleName", "source": "identifier", "name": "Name" }, + { "target": "Name", "source": "response", "path": "PolicyNames[]" } + ] + } + } + } + }, + "RolePolicy": { + "identifiers": [ + { + "name": "RoleName", + "memberName": "RoleName" + }, + { + "name": "Name", + "memberName": "PolicyName" + } + ], + "shape": "GetRolePolicyResponse", + "load": { + "request": { + "operation": "GetRolePolicy", + "params": [ + { "target": "RoleName", "source": "identifier", "name": "RoleName" }, + { "target": "PolicyName", "source": "identifier", "name": "Name" } + ] + }, + "path": "@" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteRolePolicy", + "params": [ + { "target": "RoleName", "source": "identifier", "name": "RoleName" }, + { "target": "PolicyName", "source": "identifier", "name": "Name" } + ] + } + }, + "Put": { + "request": { + "operation": "PutRolePolicy", + "params": [ + { "target": "RoleName", "source": "identifier", "name": "RoleName" }, + { "target": "PolicyName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "has": { + "Role": { + "resource": { + "type": "Role", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "RoleName" } + ] + } + } + } + }, + "SamlProvider": { + "identifiers": [ + { "name": "Arn" } + ], + "shape": "GetSAMLProviderResponse", + "load": { + "request": { + "operation": "GetSAMLProvider", + "params": [ + { "target": "SAMLProviderArn", "source": "identifier", "name": "Arn" } + ] + }, + "path": "@" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteSAMLProvider", + "params": [ + { "target": "SAMLProviderArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "Update": { + "request": { + "operation": "UpdateSAMLProvider", + "params": [ + { "target": "SAMLProviderArn", "source": "identifier", "name": "Arn" } + ] + } + } + } + }, + "ServerCertificate": { + "identifiers": [ + { "name": "Name" } + ], + "shape": "ServerCertificate", + "load": { + "request": { + "operation": "GetServerCertificate", + "params": [ + { "target": "ServerCertificateName", "source": "identifier", "name": "Name" } + ] + }, + "path": "ServerCertificate" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteServerCertificate", + "params": [ + { "target": "ServerCertificateName", "source": "identifier", "name": "Name" } + ] + } + }, + "Update": { + "request": { + "operation": "UpdateServerCertificate", + "params": [ + { "target": "ServerCertificateName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "ServerCertificate", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "NewServerCertificateName" } + ] + } + } + } + }, + "SigningCertificate": { + "identifiers": [ + { + "name": "UserName", + "memberName": "UserName" + }, + { + "name": "Id", + "memberName": "CertificateId" + } + ], + "shape": "SigningCertificate", + "actions": { + "Activate": { + "request": { + "operation": "UpdateSigningCertificate", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "CertificateId", "source": "identifier", "name": "Id" }, + { "target": "Status", "source": "string", "value": "Active" } + ] + } + }, + "Deactivate": { + "request": { + "operation": "UpdateSigningCertificate", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "CertificateId", "source": "identifier", "name": "Id" }, + { "target": "Status", "source": "string", "value": "Inactive" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteSigningCertificate", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "CertificateId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "User": { + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "UserName" } + ] + } + } + } + }, + "User": { + "identifiers": [ + { + "name": "Name", + "memberName": "UserName" + } + ], + "shape": "User", + "load": { + "request": { + "operation": "GetUser", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + }, + "path": "User" + }, + "actions": { + "AddGroup": { + "request": { + "operation": "AddUserToGroup", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + } + }, + "AttachPolicy": { + "request": { + "operation": "AttachUserPolicy", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + } + }, + "Create": { + "request": { + "operation": "CreateUser", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "UserName" } + ], + "path": "User" + } + }, + "CreateAccessKeyPair": { + "request": { + "operation": "CreateAccessKey", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "AccessKeyPair", + "identifiers": [ + { "target": "UserName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "response", "path": "AccessKey.AccessKeyId" }, + { "target": "Secret", "source": "response", "path": "AccessKey.SecretAccessKey" } + ], + "path": "AccessKey" + } + }, + "CreateLoginProfile": { + "request": { + "operation": "CreateLoginProfile", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "LoginProfile", + "identifiers": [ + { "target": "UserName", "source": "response", "path": "LoginProfile.UserName" } + ], + "path": "LoginProfile" + } + }, + "CreatePolicy": { + "request": { + "operation": "PutUserPolicy", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "UserPolicy", + "identifiers": [ + { "target": "UserName", "source": "identifier", "name": "Name" }, + { "target": "Name", "source": "requestParameter", "path": "PolicyName" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteUser", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + } + }, + "DetachPolicy": { + "request": { + "operation": "DetachUserPolicy", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + } + }, + "EnableMfa": { + "request": { + "operation": "EnableMFADevice", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "MfaDevice", + "identifiers": [ + { "target": "UserName", "source": "identifier", "name": "Name" }, + { "target": "SerialNumber", "source": "requestParameter", "path": "SerialNumber" } + ] + } + }, + "RemoveGroup": { + "request": { + "operation": "RemoveUserFromGroup", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + } + }, + "Update": { + "request": { + "operation": "UpdateUser", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "NewUserName" } + ] + } + } + }, + "has": { + "AccessKey": { + "resource": { + "type": "AccessKey", + "identifiers": [ + { "target": "UserName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "input" } + ] + } + }, + "LoginProfile": { + "resource": { + "type": "LoginProfile", + "identifiers": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + } + }, + "MfaDevice": { + "resource": { + "type": "MfaDevice", + "identifiers": [ + { "target": "UserName", "source": "identifier", "name": "Name" }, + { "target": "SerialNumber", "source": "input" } + ] + } + }, + "Policy": { + "resource": { + "type": "UserPolicy", + "identifiers": [ + { "target": "UserName", "source": "identifier", "name": "Name" }, + { "target": "Name", "source": "input" } + ] + } + }, + "SigningCertificate": { + "resource": { + "type": "SigningCertificate", + "identifiers": [ + { "target": "UserName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "input" } + ] + } + } + }, + "hasMany": { + "AccessKeys": { + "request": { + "operation": "ListAccessKeys", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "AccessKey", + "identifiers": [ + { "target": "UserName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "response", "path": "AccessKeyMetadata[].AccessKeyId" } + ], + "path": "AccessKeyMetadata[]" + } + }, + "AttachedPolicies": { + "request": { + "operation": "ListAttachedUserPolicies", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Policy", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "AttachedPolicies[].PolicyArn" } + ] + } + }, + "Groups": { + "request": { + "operation": "ListGroupsForUser", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Group", + "identifiers": [ + { "target": "Name", "source": "response", "path": "Groups[].GroupName" } + ], + "path": "Groups[]" + } + }, + "MfaDevices": { + "request": { + "operation": "ListMFADevices", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "MfaDevice", + "identifiers": [ + { "target": "UserName", "source": "identifier", "name": "Name" }, + { "target": "SerialNumber", "source": "response", "path": "MFADevices[].SerialNumber" } + ], + "path": "MFADevices[]" + } + }, + "Policies": { + "request": { + "operation": "ListUserPolicies", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "UserPolicy", + "identifiers": [ + { "target": "UserName", "source": "identifier", "name": "Name" }, + { "target": "Name", "source": "response", "path": "PolicyNames[]" } + ] + } + }, + "SigningCertificates": { + "request": { + "operation": "ListSigningCertificates", + "params": [ + { "target": "UserName", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "SigningCertificate", + "identifiers": [ + { "target": "UserName", "source": "identifier", "name": "Name" }, + { "target": "Id", "source": "response", "path": "Certificates[].CertificateId" } + ], + "path": "Certificates[]" + } + } + } + }, + "UserPolicy": { + "identifiers": [ + { + "name": "UserName", + "memberName": "UserName" + }, + { + "name": "Name", + "memberName": "PolicyName" + } + ], + "shape": "GetUserPolicyResponse", + "load": { + "request": { + "operation": "GetUserPolicy", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "PolicyName", "source": "identifier", "name": "Name" } + ] + }, + "path": "@" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteUserPolicy", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "PolicyName", "source": "identifier", "name": "Name" } + ] + } + }, + "Put": { + "request": { + "operation": "PutUserPolicy", + "params": [ + { "target": "UserName", "source": "identifier", "name": "UserName" }, + { "target": "PolicyName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "has": { + "User": { + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "UserName" } + ] + } + } + } + }, + "VirtualMfaDevice": { + "identifiers": [ + { + "name": "SerialNumber", + "memberName": "SerialNumber" + } + ], + "shape": "VirtualMFADevice", + "actions": { + "Delete": { + "request": { + "operation": "DeleteVirtualMFADevice", + "params": [ + { "target": "SerialNumber", "source": "identifier", "name": "SerialNumber" } + ] + } + } + }, + "has": { + "User": { + "resource": { + "type": "User", + "identifiers": [ + { "target": "Name", "source": "data", "path": "User.UserName" } + ] + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/s3/2006-03-01/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/s3/2006-03-01/resources-1.json new file mode 100644 index 0000000..f1e88c6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/s3/2006-03-01/resources-1.json @@ -0,0 +1,1249 @@ +{ + "service": { + "actions": { + "CreateBucket": { + "request": { "operation": "CreateBucket" }, + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "requestParameter", "path": "Bucket" } + ] + } + } + }, + "has": { + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "input" } + ] + } + } + }, + "hasMany": { + "Buckets": { + "request": { "operation": "ListBuckets" }, + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "response", "path": "Buckets[].Name" } + ], + "path": "Buckets[]" + } + } + } + }, + "resources": { + "Bucket": { + "identifiers": [ + { "name": "Name" } + ], + "shape": "Bucket", + "actions": { + "Create": { + "request": { + "operation": "CreateBucket", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "Name" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteBucket", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "Name" } + ] + } + }, + "DeleteObjects": { + "request": { + "operation": "DeleteObjects", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "Name" } + ] + } + }, + "PutObject": { + "request": { + "operation": "PutObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "Object", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" }, + { "target": "Key", "source": "requestParameter", "path": "Key" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "BucketExists", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "Name" } + ] + }, + "NotExists": { + "waiterName": "BucketNotExists", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "Name" } + ] + } + }, + "has": { + "Acl": { + "resource": { + "type": "BucketAcl", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" } + ] + } + }, + "Cors": { + "resource": { + "type": "BucketCors", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" } + ] + } + }, + "Lifecycle": { + "resource": { + "type": "BucketLifecycle", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" } + ] + } + }, + "LifecycleConfiguration": { + "resource": { + "type": "BucketLifecycleConfiguration", + "identifiers": [ + { + "target": "BucketName", + "source": "identifier", + "name": "Name" + } + ] + } + }, + "Logging": { + "resource": { + "type": "BucketLogging", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" } + ] + } + }, + "Notification": { + "resource": { + "type": "BucketNotification", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" } + ] + } + }, + "Object": { + "resource": { + "type": "Object", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" }, + { "target": "Key", "source": "input" } + ] + } + }, + "Policy": { + "resource": { + "type": "BucketPolicy", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" } + ] + } + }, + "RequestPayment": { + "resource": { + "type": "BucketRequestPayment", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" } + ] + } + }, + "Tagging": { + "resource": { + "type": "BucketTagging", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" } + ] + } + }, + "Versioning": { + "resource": { + "type": "BucketVersioning", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" } + ] + } + }, + "Website": { + "resource": { + "type": "BucketWebsite", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" } + ] + } + } + }, + "hasMany": { + "MultipartUploads": { + "request": { + "operation": "ListMultipartUploads", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "MultipartUpload", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" }, + { "target": "ObjectKey", "source": "response", "path": "Uploads[].Key" }, + { "target": "Id", "source": "response", "path": "Uploads[].UploadId" } + ], + "path": "Uploads[]" + } + }, + "ObjectVersions": { + "request": { + "operation": "ListObjectVersions", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "ObjectVersion", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" }, + { "target": "ObjectKey", "source": "response", "path": "[Versions,DeleteMarkers]|[].Key" }, + { "target": "Id", "source": "response", "path": "[Versions,DeleteMarkers]|[].VersionId" } + ], + "path": "[Versions,DeleteMarkers]|[]" + } + }, + "Objects": { + "request": { + "operation": "ListObjects", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "Name" } + ] + }, + "resource": { + "type": "ObjectSummary", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "Name" }, + { "target": "Key", "source": "response", "path": "Contents[].Key" } + ], + "path": "Contents[]" + } + } + } + }, + "BucketAcl": { + "identifiers": [ + { "name": "BucketName" } + ], + "shape": "GetBucketAclOutput", + "load": { + "request": { + "operation": "GetBucketAcl", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + }, + "path": "@" + }, + "actions": { + "Put": { + "request": { + "operation": "PutBucketAcl", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + } + }, + "has": { + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "BucketName" } + ] + } + } + } + }, + "BucketCors": { + "identifiers": [ + { "name": "BucketName" } + ], + "shape": "GetBucketCorsOutput", + "load": { + "request": { + "operation": "GetBucketCors", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + }, + "path": "@" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteBucketCors", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + }, + "Put": { + "request": { + "operation": "PutBucketCors", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + } + }, + "has": { + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "BucketName" } + ] + } + } + } + }, + "BucketLifecycle": { + "identifiers": [ + { "name": "BucketName" } + ], + "shape": "GetBucketLifecycleOutput", + "load": { + "request": { + "operation": "GetBucketLifecycle", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + }, + "path": "@" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteBucketLifecycle", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + }, + "Put": { + "request": { + "operation": "PutBucketLifecycle", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + } + }, + "has": { + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "BucketName" } + ] + } + } + } + }, + "BucketLifecycleConfiguration": { + "identifiers": [ + { + "name": "BucketName" + } + ], + "shape": "GetBucketLifecycleConfigurationOutput", + "load": { + "request": { + "operation": "GetBucketLifecycleConfiguration", + "params": [ + { + "target": "Bucket", + "source": "identifier", + "name": "BucketName" + } + ] + }, + "path": "@" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteBucketLifecycle", + "params": [ + { + "target": "Bucket", + "source": "identifier", + "name": "BucketName" + } + ] + } + }, + "Put": { + "request": { + "operation": "PutBucketLifecycleConfiguration", + "params": [ + { + "target": "Bucket", + "source": "identifier", + "name": "BucketName" + } + ] + } + } + }, + "has": { + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { + "target": "Name", + "source": "identifier", + "name": "BucketName" + } + ] + } + } + } + }, + "BucketLogging": { + "identifiers": [ + { "name": "BucketName" } + ], + "shape": "GetBucketLoggingOutput", + "load": { + "request": { + "operation": "GetBucketLogging", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + }, + "path": "@" + }, + "actions": { + "Put": { + "request": { + "operation": "PutBucketLogging", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + } + }, + "has": { + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "BucketName" } + ] + } + } + } + }, + "BucketNotification": { + "identifiers": [ + { "name": "BucketName" } + ], + "shape": "NotificationConfiguration", + "load": { + "request": { + "operation": "GetBucketNotificationConfiguration", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + }, + "path": "@" + }, + "actions": { + "Put": { + "request": { + "operation": "PutBucketNotificationConfiguration", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + } + }, + "has": { + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "BucketName" } + ] + } + } + } + }, + "BucketPolicy": { + "identifiers": [ + { "name": "BucketName" } + ], + "shape": "GetBucketPolicyOutput", + "load": { + "request": { + "operation": "GetBucketPolicy", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + }, + "path": "@" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteBucketPolicy", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + }, + "Put": { + "request": { + "operation": "PutBucketPolicy", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + } + }, + "has": { + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "BucketName" } + ] + } + } + } + }, + "BucketRequestPayment": { + "identifiers": [ + { "name": "BucketName" } + ], + "shape": "GetBucketRequestPaymentOutput", + "load": { + "request": { + "operation": "GetBucketRequestPayment", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + }, + "path": "@" + }, + "actions": { + "Put": { + "request": { + "operation": "PutBucketRequestPayment", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + } + }, + "has": { + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "BucketName" } + ] + } + } + } + }, + "BucketTagging": { + "identifiers": [ + { "name": "BucketName" } + ], + "shape": "GetBucketTaggingOutput", + "load": { + "request": { + "operation": "GetBucketTagging", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + }, + "path": "@" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteBucketTagging", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + }, + "Put": { + "request": { + "operation": "PutBucketTagging", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + } + }, + "has": { + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "BucketName" } + ] + } + } + } + }, + "BucketVersioning": { + "identifiers": [ + { "name": "BucketName" } + ], + "shape": "GetBucketVersioningOutput", + "load": { + "request": { + "operation": "GetBucketVersioning", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + }, + "path": "@" + }, + "actions": { + "Enable": { + "request": { + "operation": "PutBucketVersioning", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "VersioningConfiguration.Status", "source": "string", "value": "Enabled" } + ] + } + }, + "Put": { + "request": { + "operation": "PutBucketVersioning", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + }, + "Suspend": { + "request": { + "operation": "PutBucketVersioning", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "VersioningConfiguration.Status", "source": "string", "value": "Suspended" } + ] + } + } + }, + "has": { + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "BucketName" } + ] + } + } + } + }, + "BucketWebsite": { + "identifiers": [ + { "name": "BucketName" } + ], + "shape": "GetBucketWebsiteOutput", + "load": { + "request": { + "operation": "GetBucketWebsite", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + }, + "path": "@" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteBucketWebsite", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + }, + "Put": { + "request": { + "operation": "PutBucketWebsite", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" } + ] + } + } + }, + "has": { + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "BucketName" } + ] + } + } + } + }, + "MultipartUpload": { + "identifiers": [ + { "name": "BucketName" }, + { "name": "ObjectKey" }, + { "name": "Id" } + ], + "shape": "MultipartUpload", + "actions": { + "Abort": { + "request": { + "operation": "AbortMultipartUpload", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" }, + { "target": "UploadId", "source": "identifier", "name": "Id" } + ] + } + }, + "Complete": { + "request": { + "operation": "CompleteMultipartUpload", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" }, + { "target": "UploadId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "Object", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" } + ] + } + } + }, + "has": { + "Object": { + "resource": { + "type": "Object", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" } + ] + } + }, + "Part": { + "resource": { + "type": "MultipartUploadPart", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "ObjectKey", "source": "identifier", "name": "ObjectKey" }, + { "target": "MultipartUploadId", "source": "identifier", "name": "Id" }, + { "target": "PartNumber", "source": "input" } + ] + } + } + }, + "hasMany": { + "Parts": { + "request": { + "operation": "ListParts", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" }, + { "target": "UploadId", "source": "identifier", "name": "Id" } + ] + }, + "resource": { + "type": "MultipartUploadPart", + "identifiers": [ + { "target": "BucketName", "source": "requestParameter", "path": "Bucket" }, + { "target": "ObjectKey", "source": "requestParameter", "path": "Key" }, + { "target": "MultipartUploadId", "source": "requestParameter", "path": "UploadId" }, + { "target": "PartNumber", "source": "response", "path": "Parts[].PartNumber" } + ], + "path": "Parts[]" + } + } + } + }, + "MultipartUploadPart": { + "identifiers": [ + { "name": "BucketName" }, + { "name": "ObjectKey" }, + { "name": "MultipartUploadId" }, + { + "name": "PartNumber", + "type": "integer", + "memberName": "PartNumber" + } + ], + "shape": "Part", + "actions": { + "CopyFrom": { + "request": { + "operation": "UploadPartCopy", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" }, + { "target": "UploadId", "source": "identifier", "name": "MultipartUploadId" }, + { "target": "PartNumber", "source": "identifier", "name": "PartNumber" } + ] + } + }, + "Upload": { + "request": { + "operation": "UploadPart", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" }, + { "target": "UploadId", "source": "identifier", "name": "MultipartUploadId" }, + { "target": "PartNumber", "source": "identifier", "name": "PartNumber" } + ] + } + } + }, + "has": { + "MultipartUpload": { + "resource": { + "type": "MultipartUpload", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "ObjectKey", "source": "identifier", "name": "ObjectKey" }, + { "target": "Id", "source": "identifier", "name": "MultipartUploadId" } + ] + } + } + } + }, + "Object": { + "identifiers": [ + { "name": "BucketName" }, + { "name": "Key" } + ], + "shape": "HeadObjectOutput", + "load": { + "request": { + "operation": "HeadObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + }, + "path": "@" + }, + "actions": { + "CopyFrom": { + "request": { + "operation": "CopyObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + } + }, + "Get": { + "request": { + "operation": "GetObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + } + }, + "InitiateMultipartUpload": { + "request": { + "operation": "CreateMultipartUpload", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + }, + "resource": { + "type": "MultipartUpload", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "ObjectKey", "source": "identifier", "name": "Key" }, + { "target": "Id", "source": "response", "path": "UploadId" } + ] + } + }, + "Put": { + "request": { + "operation": "PutObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + } + }, + "RestoreObject": { + "request": { + "operation": "RestoreObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + } + } + }, + "batchActions": { + "Delete": { + "request": { + "operation": "DeleteObjects", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Delete.Objects[].Key", "source": "identifier", "name": "Key" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "ObjectExists", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + }, + "NotExists": { + "waiterName": "ObjectNotExists", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + } + }, + "has": { + "Acl": { + "resource": { + "type": "ObjectAcl", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "ObjectKey", "source": "identifier", "name": "Key" } + ] + } + }, + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "BucketName" } + ] + } + }, + "MultipartUpload": { + "resource": { + "type": "MultipartUpload", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "ObjectKey", "source": "identifier", "name": "Key" }, + { "target": "Id", "source": "input" } + ] + } + }, + "Version": { + "resource": { + "type": "ObjectVersion", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "ObjectKey", "source": "identifier", "name": "Key" }, + { "target": "Id", "source": "input" } + ] + } + } + } + }, + "ObjectAcl": { + "identifiers": [ + { "name": "BucketName" }, + { "name": "ObjectKey" } + ], + "shape": "GetObjectAclOutput", + "load": { + "request": { + "operation": "GetObjectAcl", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" } + ] + }, + "path": "@" + }, + "actions": { + "Put": { + "request": { + "operation": "PutObjectAcl", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" } + ] + } + } + }, + "has": { + "Object": { + "resource": { + "type": "Object", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" } + ] + } + } + } + }, + "ObjectSummary": { + "identifiers": [ + { "name": "BucketName" }, + { "name": "Key" } + ], + "shape": "Object", + "actions": { + "CopyFrom": { + "request": { + "operation": "CopyObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + } + }, + "Get": { + "request": { + "operation": "GetObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + } + }, + "InitiateMultipartUpload": { + "request": { + "operation": "CreateMultipartUpload", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + }, + "resource": { + "type": "MultipartUpload", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "ObjectKey", "source": "identifier", "name": "Key" }, + { "target": "Id", "source": "response", "path": "UploadId" } + ] + } + }, + "Put": { + "request": { + "operation": "PutObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + } + }, + "RestoreObject": { + "request": { + "operation": "RestoreObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + } + } + }, + "batchActions": { + "Delete": { + "request": { + "operation": "DeleteObjects", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Delete.Objects[].Key", "source": "identifier", "name": "Key" } + ] + } + } + }, + "waiters": { + "Exists": { + "waiterName": "ObjectExists", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + }, + "NotExists": { + "waiterName": "ObjectNotExists", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + } + }, + "has": { + "Acl": { + "resource": { + "type": "ObjectAcl", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "ObjectKey", "source": "identifier", "name": "Key" } + ] + } + }, + "Bucket": { + "resource": { + "type": "Bucket", + "identifiers": [ + { "target": "Name", "source": "identifier", "name": "BucketName" } + ] + } + }, + "MultipartUpload": { + "resource": { + "type": "MultipartUpload", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "ObjectKey", "source": "identifier", "name": "Key" }, + { "target": "Id", "source": "input" } + ] + } + }, + "Object": { + "resource": { + "type": "Object", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "Key" } + ] + } + }, + "Version": { + "resource": { + "type": "ObjectVersion", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "ObjectKey", "source": "identifier", "name": "Key" }, + { "target": "Id", "source": "input" } + ] + } + } + } + }, + "ObjectVersion": { + "identifiers": [ + { "name": "BucketName" }, + { "name": "ObjectKey" }, + { "name": "Id" } + ], + "shape": "ObjectVersion", + "actions": { + "Delete": { + "request": { + "operation": "DeleteObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" }, + { "target": "VersionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Get": { + "request": { + "operation": "GetObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" }, + { "target": "VersionId", "source": "identifier", "name": "Id" } + ] + } + }, + "Head": { + "request": { + "operation": "HeadObject", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" }, + { "target": "VersionId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "batchActions": { + "Delete": { + "request": { + "operation": "DeleteObjects", + "params": [ + { "target": "Bucket", "source": "identifier", "name": "BucketName" }, + { "target": "Delete.Objects[*].Key", "source": "identifier", "name": "ObjectKey" }, + { "target": "Delete.Objects[*].VersionId", "source": "identifier", "name": "Id" } + ] + } + } + }, + "has": { + "Object": { + "resource": { + "type": "Object", + "identifiers": [ + { "target": "BucketName", "source": "identifier", "name": "BucketName" }, + { "target": "Key", "source": "identifier", "name": "ObjectKey" } + ] + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/sns/2010-03-31/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/sns/2010-03-31/resources-1.json new file mode 100644 index 0000000..cee300a --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/sns/2010-03-31/resources-1.json @@ -0,0 +1,327 @@ +{ + "service": { + "actions": { + "CreatePlatformApplication": { + "request": { "operation": "CreatePlatformApplication" }, + "resource": { + "type": "PlatformApplication", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "PlatformApplicationArn" } + ] + } + }, + "CreateTopic": { + "request": { "operation": "CreateTopic" }, + "resource": { + "type": "Topic", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "TopicArn" } + ] + } + } + }, + "has": { + "PlatformApplication": { + "resource": { + "type": "PlatformApplication", + "identifiers": [ + { "target": "Arn", "source": "input" } + ] + } + }, + "PlatformEndpoint": { + "resource": { + "type": "PlatformEndpoint", + "identifiers": [ + { "target": "Arn", "source": "input" } + ] + } + }, + "Subscription": { + "resource": { + "type": "Subscription", + "identifiers": [ + { "target": "Arn", "source": "input" } + ] + } + }, + "Topic": { + "resource": { + "type": "Topic", + "identifiers": [ + { "target": "Arn", "source": "input" } + ] + } + } + }, + "hasMany": { + "PlatformApplications": { + "request": { "operation": "ListPlatformApplications" }, + "resource": { + "type": "PlatformApplication", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "PlatformApplications[].PlatformApplicationArn" } + ] + } + }, + "Subscriptions": { + "request": { "operation": "ListSubscriptions" }, + "resource": { + "type": "Subscription", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "Subscriptions[].SubscriptionArn" } + ] + } + }, + "Topics": { + "request": { "operation": "ListTopics" }, + "resource": { + "type": "Topic", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "Topics[].TopicArn" } + ] + } + } + } + }, + "resources": { + "PlatformApplication": { + "identifiers": [ + { "name": "Arn" } + ], + "shape": "GetPlatformApplicationAttributesResponse", + "load": { + "request": { + "operation": "GetPlatformApplicationAttributes", + "params": [ + { "target": "PlatformApplicationArn", "source": "identifier", "name": "Arn" } + ] + }, + "path": "@" + }, + "actions": { + "CreatePlatformEndpoint": { + "request": { + "operation": "CreatePlatformEndpoint", + "params": [ + { "target": "PlatformApplicationArn", "source": "identifier", "name": "Arn" } + ] + }, + "resource": { + "type": "PlatformEndpoint", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "EndpointArn" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeletePlatformApplication", + "params": [ + { "target": "PlatformApplicationArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "SetAttributes": { + "request": { + "operation": "SetPlatformApplicationAttributes", + "params": [ + { "target": "PlatformApplicationArn", "source": "identifier", "name": "Arn" } + ] + } + } + }, + "hasMany": { + "Endpoints": { + "request": { + "operation": "ListEndpointsByPlatformApplication", + "params": [ + { "target": "PlatformApplicationArn", "source": "identifier", "name": "Arn" } + ] + }, + "resource": { + "type": "PlatformEndpoint", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "Endpoints[].EndpointArn" } + ] + } + } + } + }, + "PlatformEndpoint": { + "identifiers": [ + { "name": "Arn" } + ], + "shape": "GetEndpointAttributesResponse", + "load": { + "request": { + "operation": "GetEndpointAttributes", + "params": [ + { "target": "EndpointArn", "source": "identifier", "name": "Arn" } + ] + }, + "path": "@" + }, + "actions": { + "Delete": { + "request": { + "operation": "DeleteEndpoint", + "params": [ + { "target": "EndpointArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "Publish": { + "request": { + "operation": "Publish", + "params": [ + { "target": "TargetArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "SetAttributes": { + "request": { + "operation": "SetEndpointAttributes", + "params": [ + { "target": "EndpointArn", "source": "identifier", "name": "Arn" } + ] + } + } + } + }, + "Subscription": { + "identifiers": [ + { "name": "Arn" } + ], + "shape": "GetSubscriptionAttributesResponse", + "load": { + "request": { + "operation": "GetSubscriptionAttributes", + "params": [ + { "target": "SubscriptionArn", "source": "identifier", "name": "Arn" } + ] + }, + "path": "@" + }, + "actions": { + "Delete": { + "request": { + "operation": "Unsubscribe", + "params": [ + { "target": "SubscriptionArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "SetAttributes": { + "request": { + "operation": "SetSubscriptionAttributes", + "params": [ + { "target": "SubscriptionArn", "source": "identifier", "name": "Arn" } + ] + } + } + } + }, + "Topic": { + "identifiers": [ + { "name": "Arn" } + ], + "shape": "GetTopicAttributesResponse", + "load": { + "request": { + "operation": "GetTopicAttributes", + "params": [ + { "target": "TopicArn", "source": "identifier", "name": "Arn" } + ] + }, + "path": "@" + }, + "actions": { + "AddPermission": { + "request": { + "operation": "AddPermission", + "params": [ + { "target": "TopicArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "ConfirmSubscription": { + "request": { + "operation": "ConfirmSubscription", + "params": [ + { "target": "TopicArn", "source": "identifier", "name": "Arn" } + ] + }, + "resource": { + "type": "Subscription", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "SubscriptionArn" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteTopic", + "params": [ + { "target": "TopicArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "Publish": { + "request": { + "operation": "Publish", + "params": [ + { "target": "TopicArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "RemovePermission": { + "request": { + "operation": "RemovePermission", + "params": [ + { "target": "TopicArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "SetAttributes": { + "request": { + "operation": "SetTopicAttributes", + "params": [ + { "target": "TopicArn", "source": "identifier", "name": "Arn" } + ] + } + }, + "Subscribe": { + "request": { + "operation": "Subscribe", + "params": [ + { "target": "TopicArn", "source": "identifier", "name": "Arn" } + ] + }, + "resource": { + "type": "Subscription", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "SubscriptionArn" } + ] + } + } + }, + "hasMany": { + "Subscriptions": { + "request": { + "operation": "ListSubscriptionsByTopic", + "params": [ + { "target": "TopicArn", "source": "identifier", "name": "Arn" } + ] + }, + "resource": { + "type": "Subscription", + "identifiers": [ + { "target": "Arn", "source": "response", "path": "Subscriptions[].SubscriptionArn" } + ] + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/data/sqs/2012-11-05/resources-1.json b/venv/lib/python3.12/site-packages/boto3/data/sqs/2012-11-05/resources-1.json new file mode 100644 index 0000000..b1e74ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/data/sqs/2012-11-05/resources-1.json @@ -0,0 +1,232 @@ +{ + "service": { + "actions": { + "CreateQueue": { + "request": { "operation": "CreateQueue" }, + "resource": { + "type": "Queue", + "identifiers": [ + { "target": "Url", "source": "response", "path": "QueueUrl" } + ] + } + }, + "GetQueueByName": { + "request": { "operation": "GetQueueUrl" }, + "resource": { + "type": "Queue", + "identifiers": [ + { "target": "Url", "source": "response", "path": "QueueUrl" } + ] + } + } + }, + "has": { + "Queue": { + "resource": { + "type": "Queue", + "identifiers": [ + { "target": "Url", "source": "input" } + ] + } + } + }, + "hasMany": { + "Queues": { + "request": { "operation": "ListQueues" }, + "resource": { + "type": "Queue", + "identifiers": [ + { "target": "Url", "source": "response", "path": "QueueUrls[]" } + ] + } + } + } + }, + "resources": { + "Message": { + "identifiers": [ + { "name": "QueueUrl" }, + { + "name": "ReceiptHandle", + "memberName": "ReceiptHandle" + } + ], + "shape": "Message", + "actions": { + "ChangeVisibility": { + "request": { + "operation": "ChangeMessageVisibility", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "QueueUrl" }, + { "target": "ReceiptHandle", "source": "identifier", "name": "ReceiptHandle" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteMessage", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "QueueUrl" }, + { "target": "ReceiptHandle", "source": "identifier", "name": "ReceiptHandle" } + ] + } + } + }, + "batchActions": { + "Delete": { + "request": { + "operation": "DeleteMessageBatch", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "QueueUrl" }, + { "target": "Entries[*].Id", "source": "data", "path": "MessageId" }, + { "target": "Entries[*].ReceiptHandle", "source": "identifier", "name": "ReceiptHandle" } + ] + } + } + }, + "has": { + "Queue": { + "resource": { + "type": "Queue", + "identifiers": [ + { "target": "Url", "source": "identifier", "name": "QueueUrl" } + ] + } + } + } + }, + "Queue": { + "identifiers": [ + { "name": "Url" } + ], + "shape": "GetQueueAttributesResult", + "load": { + "request": { + "operation": "GetQueueAttributes", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" }, + { "target": "AttributeNames[]", "source": "string", "value": "All" } + ] + }, + "path": "@" + }, + "actions": { + "AddPermission": { + "request": { + "operation": "AddPermission", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" } + ] + } + }, + "ChangeMessageVisibilityBatch": { + "request": { + "operation": "ChangeMessageVisibilityBatch", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" } + ] + } + }, + "Delete": { + "request": { + "operation": "DeleteQueue", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" } + ] + } + }, + "DeleteMessages": { + "request": { + "operation": "DeleteMessageBatch", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" } + ] + } + }, + "Purge": { + "request": { + "operation": "PurgeQueue", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" } + ] + } + }, + "ReceiveMessages": { + "request": { + "operation": "ReceiveMessage", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" } + ] + }, + "resource": { + "type": "Message", + "identifiers": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" }, + { "target": "ReceiptHandle", "source": "response", "path": "Messages[].ReceiptHandle" } + ], + "path": "Messages[]" + } + }, + "RemovePermission": { + "request": { + "operation": "RemovePermission", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" } + ] + } + }, + "SendMessage": { + "request": { + "operation": "SendMessage", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" } + ] + } + }, + "SendMessages": { + "request": { + "operation": "SendMessageBatch", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" } + ] + } + }, + "SetAttributes": { + "request": { + "operation": "SetQueueAttributes", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" } + ] + } + } + }, + "has": { + "Message": { + "resource": { + "type": "Message", + "identifiers": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" }, + { "target": "ReceiptHandle", "source": "input" } + ] + } + } + }, + "hasMany": { + "DeadLetterSourceQueues": { + "request": { + "operation": "ListDeadLetterSourceQueues", + "params": [ + { "target": "QueueUrl", "source": "identifier", "name": "Url" } + ] + }, + "resource": { + "type": "Queue", + "identifiers": [ + { "target": "Url", "source": "response", "path": "queueUrls[]" } + ] + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__init__.py b/venv/lib/python3.12/site-packages/boto3/docs/__init__.py new file mode 100644 index 0000000..504d6c2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/docs/__init__.py @@ -0,0 +1,51 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import os + +from botocore.docs import DEPRECATED_SERVICE_NAMES + +from boto3.docs.service import ServiceDocumenter + + +def generate_docs(root_dir, session): + """Generates the reference documentation for botocore + + This will go through every available AWS service and output ReSTructured + text files documenting each service. + + :param root_dir: The directory to write the reference files to. Each + service's reference documentation is located at + root_dir/reference/services/service-name.rst + + :param session: The boto3 session + """ + services_doc_path = os.path.join(root_dir, 'reference', 'services') + if not os.path.exists(services_doc_path): + os.makedirs(services_doc_path) + + # Prevents deprecated service names from being generated in docs. + available_services = [ + service + for service in session.get_available_services() + if service not in DEPRECATED_SERVICE_NAMES + ] + + for service_name in available_services: + docs = ServiceDocumenter( + service_name, session, services_doc_path + ).document_service() + service_doc_path = os.path.join( + services_doc_path, f"{service_name}.rst" + ) + with open(service_doc_path, 'wb') as f: + f.write(docs) diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c02e3b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/action.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/action.cpython-312.pyc new file mode 100644 index 0000000..1263c77 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/action.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/attr.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/attr.cpython-312.pyc new file mode 100644 index 0000000..2d16ba8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/attr.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..729cc4d Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/client.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/client.cpython-312.pyc new file mode 100644 index 0000000..592bd9b Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/client.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/collection.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/collection.cpython-312.pyc new file mode 100644 index 0000000..57c51de Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/collection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/docstring.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/docstring.cpython-312.pyc new file mode 100644 index 0000000..2f99620 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/docstring.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/method.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/method.cpython-312.pyc new file mode 100644 index 0000000..91e4138 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/method.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/resource.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/resource.cpython-312.pyc new file mode 100644 index 0000000..c35afa5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/resource.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/service.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/service.cpython-312.pyc new file mode 100644 index 0000000..e03ed60 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/service.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/subresource.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/subresource.cpython-312.pyc new file mode 100644 index 0000000..8c0e468 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/subresource.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..9c11d33 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/waiter.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/waiter.cpython-312.pyc new file mode 100644 index 0000000..0cca2d2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/docs/__pycache__/waiter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/docs/action.py b/venv/lib/python3.12/site-packages/boto3/docs/action.py new file mode 100644 index 0000000..44a9908 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/docs/action.py @@ -0,0 +1,214 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import os + +from botocore import xform_name +from botocore.docs.bcdoc.restdoc import DocumentStructure +from botocore.docs.method import ( + document_custom_method, + document_model_driven_method, +) +from botocore.model import OperationModel +from botocore.utils import get_service_module_name + +from boto3.docs.base import NestedDocumenter +from boto3.docs.method import document_model_driven_resource_method +from boto3.docs.utils import ( + add_resource_type_overview, + get_resource_ignore_params, + get_resource_public_actions, +) + +PUT_DATA_WARNING_MESSAGE = """ +.. warning:: + It is recommended to use the :py:meth:`put_metric_data` + :doc:`client method <../../cloudwatch/client/put_metric_data>` + instead. If you would still like to use this resource method, + please make sure that ``MetricData[].MetricName`` is equal to + the metric resource's ``name`` attribute. +""" + +WARNING_MESSAGES = { + "Metric": {"put_data": PUT_DATA_WARNING_MESSAGE}, +} + +IGNORE_PARAMS = {"Metric": {"put_data": ["Namespace"]}} + + +class ActionDocumenter(NestedDocumenter): + def document_actions(self, section): + modeled_actions_list = self._resource_model.actions + modeled_actions = {} + for modeled_action in modeled_actions_list: + modeled_actions[modeled_action.name] = modeled_action + resource_actions = get_resource_public_actions( + self._resource.__class__ + ) + self.member_map['actions'] = sorted(resource_actions) + add_resource_type_overview( + section=section, + resource_type='Actions', + description=( + 'Actions call operations on resources. They may ' + 'automatically handle the passing in of arguments set ' + 'from identifiers and some attributes.' + ), + intro_link='actions_intro', + ) + resource_warnings = WARNING_MESSAGES.get(self._resource_name, {}) + for action_name in sorted(resource_actions): + # Create a new DocumentStructure for each action and add contents. + action_doc = DocumentStructure(action_name, target='html') + breadcrumb_section = action_doc.add_new_section('breadcrumb') + breadcrumb_section.style.ref(self._resource_class_name, 'index') + breadcrumb_section.write(f' / Action / {action_name}') + action_doc.add_title_section(action_name) + warning_message = resource_warnings.get(action_name) + if warning_message is not None: + action_doc.add_new_section("warning").write(warning_message) + action_section = action_doc.add_new_section( + action_name, + context={'qualifier': f'{self.class_name}.'}, + ) + if action_name in ['load', 'reload'] and self._resource_model.load: + document_load_reload_action( + section=action_section, + action_name=action_name, + resource_name=self._resource_name, + event_emitter=self._resource.meta.client.meta.events, + load_model=self._resource_model.load, + service_model=self._service_model, + ) + elif action_name in modeled_actions: + document_action( + section=action_section, + resource_name=self._resource_name, + event_emitter=self._resource.meta.client.meta.events, + action_model=modeled_actions[action_name], + service_model=self._service_model, + ) + else: + document_custom_method( + action_section, action_name, resource_actions[action_name] + ) + # Write actions in individual/nested files. + # Path: /reference/services///.rst + actions_dir_path = os.path.join( + self._root_docs_path, + f'{self._service_name}', + f'{self._resource_sub_path}', + ) + action_doc.write_to_file(actions_dir_path, action_name) + + +def document_action( + section, + resource_name, + event_emitter, + action_model, + service_model, + include_signature=True, +): + """Documents a resource action + + :param section: The section to write to + + :param resource_name: The name of the resource + + :param event_emitter: The event emitter to use to emit events + + :param action_model: The model of the action + + :param service_model: The model of the service + + :param include_signature: Whether or not to include the signature. + It is useful for generating docstrings. + """ + operation_model = service_model.operation_model( + action_model.request.operation + ) + ignore_params = IGNORE_PARAMS.get(resource_name, {}).get( + action_model.name, + get_resource_ignore_params(action_model.request.params), + ) + example_return_value = 'response' + if action_model.resource: + example_return_value = xform_name(action_model.resource.type) + example_resource_name = xform_name(resource_name) + if service_model.service_name == resource_name: + example_resource_name = resource_name + example_prefix = ( + f'{example_return_value} = {example_resource_name}.{action_model.name}' + ) + full_action_name = ( + f"{section.context.get('qualifier', '')}{action_model.name}" + ) + document_model_driven_resource_method( + section=section, + method_name=full_action_name, + operation_model=operation_model, + event_emitter=event_emitter, + method_description=operation_model.documentation, + example_prefix=example_prefix, + exclude_input=ignore_params, + resource_action_model=action_model, + include_signature=include_signature, + ) + + +def document_load_reload_action( + section, + action_name, + resource_name, + event_emitter, + load_model, + service_model, + include_signature=True, +): + """Documents the resource load action + + :param section: The section to write to + + :param action_name: The name of the loading action should be load or reload + + :param resource_name: The name of the resource + + :param event_emitter: The event emitter to use to emit events + + :param load_model: The model of the load action + + :param service_model: The model of the service + + :param include_signature: Whether or not to include the signature. + It is useful for generating docstrings. + """ + description = ( + f'Calls :py:meth:`{get_service_module_name(service_model)}.Client.' + f'{xform_name(load_model.request.operation)}` to update the attributes of the ' + f'{resource_name} resource. Note that the load and reload methods are ' + 'the same method and can be used interchangeably.' + ) + example_resource_name = xform_name(resource_name) + if service_model.service_name == resource_name: + example_resource_name = resource_name + example_prefix = f'{example_resource_name}.{action_name}' + full_action_name = f"{section.context.get('qualifier', '')}{action_name}" + document_model_driven_method( + section=section, + method_name=full_action_name, + operation_model=OperationModel({}, service_model), + event_emitter=event_emitter, + method_description=description, + example_prefix=example_prefix, + include_signature=include_signature, + ) diff --git a/venv/lib/python3.12/site-packages/boto3/docs/attr.py b/venv/lib/python3.12/site-packages/boto3/docs/attr.py new file mode 100644 index 0000000..a968da2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/docs/attr.py @@ -0,0 +1,72 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from botocore.docs.params import ResponseParamsDocumenter + +from boto3.docs.utils import get_identifier_description + + +class ResourceShapeDocumenter(ResponseParamsDocumenter): + EVENT_NAME = 'resource-shape' + + +def document_attribute( + section, + service_name, + resource_name, + attr_name, + event_emitter, + attr_model, + include_signature=True, +): + if include_signature: + full_attr_name = f"{section.context.get('qualifier', '')}{attr_name}" + section.style.start_sphinx_py_attr(full_attr_name) + # Note that an attribute may have one, may have many, or may have no + # operations that back the resource's shape. So we just set the + # operation_name to the resource name if we ever to hook in and modify + # a particular attribute. + ResourceShapeDocumenter( + service_name=service_name, + operation_name=resource_name, + event_emitter=event_emitter, + ).document_params(section=section, shape=attr_model) + + +def document_identifier( + section, + resource_name, + identifier_model, + include_signature=True, +): + if include_signature: + full_identifier_name = ( + f"{section.context.get('qualifier', '')}{identifier_model.name}" + ) + section.style.start_sphinx_py_attr(full_identifier_name) + description = get_identifier_description( + resource_name, identifier_model.name + ) + section.write(f'*(string)* {description}') + + +def document_reference(section, reference_model, include_signature=True): + if include_signature: + full_reference_name = ( + f"{section.context.get('qualifier', '')}{reference_model.name}" + ) + section.style.start_sphinx_py_attr(full_reference_name) + reference_type = f'(:py:class:`{reference_model.resource.type}`) ' + section.write(reference_type) + section.include_doc_string( + f'The related {reference_model.name} if set, otherwise ``None``.' + ) diff --git a/venv/lib/python3.12/site-packages/boto3/docs/base.py b/venv/lib/python3.12/site-packages/boto3/docs/base.py new file mode 100644 index 0000000..ee49646 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/docs/base.py @@ -0,0 +1,51 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from botocore.compat import OrderedDict + + +class BaseDocumenter: + def __init__(self, resource): + self._resource = resource + self._client = self._resource.meta.client + self._resource_model = self._resource.meta.resource_model + self._service_model = self._client.meta.service_model + self._resource_name = self._resource.meta.resource_model.name + self._service_name = self._service_model.service_name + self._service_docs_name = self._client.__class__.__name__ + self.member_map = OrderedDict() + self.represents_service_resource = ( + self._service_name == self._resource_name + ) + self._resource_class_name = self._resource_name + if self._resource_name == self._service_name: + self._resource_class_name = 'ServiceResource' + + @property + def class_name(self): + return f'{self._service_docs_name}.{self._resource_name}' + + +class NestedDocumenter(BaseDocumenter): + def __init__(self, resource, root_docs_path): + super().__init__(resource) + self._root_docs_path = root_docs_path + self._resource_sub_path = self._resource_name.lower() + if self._resource_name == self._service_name: + self._resource_sub_path = 'service-resource' + + @property + def class_name(self): + resource_class_name = self._resource_name + if self._resource_name == self._service_name: + resource_class_name = 'ServiceResource' + return f'{self._service_docs_name}.{resource_class_name}' diff --git a/venv/lib/python3.12/site-packages/boto3/docs/client.py b/venv/lib/python3.12/site-packages/boto3/docs/client.py new file mode 100644 index 0000000..b199085 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/docs/client.py @@ -0,0 +1,24 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from botocore.docs.client import ClientDocumenter + + +class Boto3ClientDocumenter(ClientDocumenter): + def _add_client_creation_example(self, section): + section.style.start_codeblock() + section.style.new_line() + section.write('import boto3') + section.style.new_line() + section.style.new_line() + section.write(f'client = boto3.client(\'{self._service_name}\')') + section.style.end_codeblock() diff --git a/venv/lib/python3.12/site-packages/boto3/docs/collection.py b/venv/lib/python3.12/site-packages/boto3/docs/collection.py new file mode 100644 index 0000000..9f6c23f --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/docs/collection.py @@ -0,0 +1,290 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import os + +from botocore import xform_name +from botocore.docs.bcdoc.restdoc import DocumentStructure +from botocore.docs.method import get_instance_public_methods +from botocore.docs.utils import DocumentedShape + +from boto3.docs.base import NestedDocumenter +from boto3.docs.method import document_model_driven_resource_method +from boto3.docs.utils import ( + add_resource_type_overview, + get_resource_ignore_params, +) + + +class CollectionDocumenter(NestedDocumenter): + def document_collections(self, section): + collections = self._resource.meta.resource_model.collections + collections_list = [] + add_resource_type_overview( + section=section, + resource_type='Collections', + description=( + 'Collections provide an interface to iterate over and ' + 'manipulate groups of resources. ' + ), + intro_link='guide_collections', + ) + self.member_map['collections'] = collections_list + for collection in collections: + collections_list.append(collection.name) + # Create a new DocumentStructure for each collection and add contents. + collection_doc = DocumentStructure(collection.name, target='html') + breadcrumb_section = collection_doc.add_new_section('breadcrumb') + breadcrumb_section.style.ref(self._resource_class_name, 'index') + breadcrumb_section.write(f' / Collection / {collection.name}') + collection_doc.add_title_section(collection.name) + collection_section = collection_doc.add_new_section( + collection.name, + context={'qualifier': f'{self.class_name}.'}, + ) + self._document_collection(collection_section, collection) + + # Write collections in individual/nested files. + # Path: /reference/services///.rst + collections_dir_path = os.path.join( + self._root_docs_path, + f'{self._service_name}', + f'{self._resource_sub_path}', + ) + collection_doc.write_to_file(collections_dir_path, collection.name) + + def _document_collection(self, section, collection): + methods = get_instance_public_methods( + getattr(self._resource, collection.name) + ) + document_collection_object(section, collection) + batch_actions = {} + for batch_action in collection.batch_actions: + batch_actions[batch_action.name] = batch_action + + for method in sorted(methods): + method_section = section.add_new_section(method) + if method in batch_actions: + document_batch_action( + section=method_section, + resource_name=self._resource_name, + event_emitter=self._resource.meta.client.meta.events, + batch_action_model=batch_actions[method], + collection_model=collection, + service_model=self._resource.meta.client.meta.service_model, + ) + else: + document_collection_method( + section=method_section, + resource_name=self._resource_name, + action_name=method, + event_emitter=self._resource.meta.client.meta.events, + collection_model=collection, + service_model=self._resource.meta.client.meta.service_model, + ) + + +def document_collection_object( + section, + collection_model, + include_signature=True, +): + """Documents a collection resource object + + :param section: The section to write to + + :param collection_model: The model of the collection + + :param include_signature: Whether or not to include the signature. + It is useful for generating docstrings. + """ + if include_signature: + full_collection_name = ( + f"{section.context.get('qualifier', '')}{collection_model.name}" + ) + section.style.start_sphinx_py_attr(full_collection_name) + section.include_doc_string( + f'A collection of {collection_model.resource.type} resources.' + ) + section.include_doc_string( + f'A {collection_model.resource.type} Collection will include all ' + f'resources by default, and extreme caution should be taken when ' + f'performing actions on all resources.' + ) + + +def document_batch_action( + section, + resource_name, + event_emitter, + batch_action_model, + service_model, + collection_model, + include_signature=True, +): + """Documents a collection's batch action + + :param section: The section to write to + + :param resource_name: The name of the resource + + :param action_name: The name of collection action. Currently only + can be all, filter, limit, or page_size + + :param event_emitter: The event emitter to use to emit events + + :param batch_action_model: The model of the batch action + + :param collection_model: The model of the collection + + :param service_model: The model of the service + + :param include_signature: Whether or not to include the signature. + It is useful for generating docstrings. + """ + operation_model = service_model.operation_model( + batch_action_model.request.operation + ) + ignore_params = get_resource_ignore_params( + batch_action_model.request.params + ) + + example_return_value = 'response' + if batch_action_model.resource: + example_return_value = xform_name(batch_action_model.resource.type) + + example_resource_name = xform_name(resource_name) + if service_model.service_name == resource_name: + example_resource_name = resource_name + example_prefix = f'{example_return_value} = {example_resource_name}.{collection_model.name}.{batch_action_model.name}' + document_model_driven_resource_method( + section=section, + method_name=batch_action_model.name, + operation_model=operation_model, + event_emitter=event_emitter, + method_description=operation_model.documentation, + example_prefix=example_prefix, + exclude_input=ignore_params, + resource_action_model=batch_action_model, + include_signature=include_signature, + ) + + +def document_collection_method( + section, + resource_name, + action_name, + event_emitter, + collection_model, + service_model, + include_signature=True, +): + """Documents a collection method + + :param section: The section to write to + + :param resource_name: The name of the resource + + :param action_name: The name of collection action. Currently only + can be all, filter, limit, or page_size + + :param event_emitter: The event emitter to use to emit events + + :param collection_model: The model of the collection + + :param service_model: The model of the service + + :param include_signature: Whether or not to include the signature. + It is useful for generating docstrings. + """ + operation_model = service_model.operation_model( + collection_model.request.operation + ) + + underlying_operation_members = [] + if operation_model.input_shape: + underlying_operation_members = operation_model.input_shape.members + + example_resource_name = xform_name(resource_name) + if service_model.service_name == resource_name: + example_resource_name = resource_name + + custom_action_info_dict = { + 'all': { + 'method_description': ( + f'Creates an iterable of all {collection_model.resource.type} ' + f'resources in the collection.' + ), + 'example_prefix': f'{xform_name(collection_model.resource.type)}_iterator = {example_resource_name}.{collection_model.name}.all', + 'exclude_input': underlying_operation_members, + }, + 'filter': { + 'method_description': ( + f'Creates an iterable of all {collection_model.resource.type} ' + f'resources in the collection filtered by kwargs passed to ' + f'method. A {collection_model.resource.type} collection will ' + f'include all resources by default if no filters are provided, ' + f'and extreme caution should be taken when performing actions ' + f'on all resources.' + ), + 'example_prefix': f'{xform_name(collection_model.resource.type)}_iterator = {example_resource_name}.{collection_model.name}.filter', + 'exclude_input': get_resource_ignore_params( + collection_model.request.params + ), + }, + 'limit': { + 'method_description': ( + f'Creates an iterable up to a specified amount of ' + f'{collection_model.resource.type} resources in the collection.' + ), + 'example_prefix': f'{xform_name(collection_model.resource.type)}_iterator = {example_resource_name}.{collection_model.name}.limit', + 'include_input': [ + DocumentedShape( + name='count', + type_name='integer', + documentation=( + 'The limit to the number of resources in the iterable.' + ), + ) + ], + 'exclude_input': underlying_operation_members, + }, + 'page_size': { + 'method_description': ( + f'Creates an iterable of all {collection_model.resource.type} ' + f'resources in the collection, but limits the number of ' + f'items returned by each service call by the specified amount.' + ), + 'example_prefix': f'{xform_name(collection_model.resource.type)}_iterator = {example_resource_name}.{collection_model.name}.page_size', + 'include_input': [ + DocumentedShape( + name='count', + type_name='integer', + documentation=( + 'The number of items returned by each service call' + ), + ) + ], + 'exclude_input': underlying_operation_members, + }, + } + if action_name in custom_action_info_dict: + action_info = custom_action_info_dict[action_name] + document_model_driven_resource_method( + section=section, + method_name=action_name, + operation_model=operation_model, + event_emitter=event_emitter, + resource_action_model=collection_model, + include_signature=include_signature, + **action_info, + ) diff --git a/venv/lib/python3.12/site-packages/boto3/docs/docstring.py b/venv/lib/python3.12/site-packages/boto3/docs/docstring.py new file mode 100644 index 0000000..daf6787 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/docs/docstring.py @@ -0,0 +1,77 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from botocore.docs.docstring import LazyLoadedDocstring + +from boto3.docs.action import document_action, document_load_reload_action +from boto3.docs.attr import ( + document_attribute, + document_identifier, + document_reference, +) +from boto3.docs.collection import ( + document_batch_action, + document_collection_method, + document_collection_object, +) +from boto3.docs.subresource import document_sub_resource +from boto3.docs.waiter import document_resource_waiter + + +class ActionDocstring(LazyLoadedDocstring): + def _write_docstring(self, *args, **kwargs): + document_action(*args, **kwargs) + + +class LoadReloadDocstring(LazyLoadedDocstring): + def _write_docstring(self, *args, **kwargs): + document_load_reload_action(*args, **kwargs) + + +class SubResourceDocstring(LazyLoadedDocstring): + def _write_docstring(self, *args, **kwargs): + document_sub_resource(*args, **kwargs) + + +class AttributeDocstring(LazyLoadedDocstring): + def _write_docstring(self, *args, **kwargs): + document_attribute(*args, **kwargs) + + +class IdentifierDocstring(LazyLoadedDocstring): + def _write_docstring(self, *args, **kwargs): + document_identifier(*args, **kwargs) + + +class ReferenceDocstring(LazyLoadedDocstring): + def _write_docstring(self, *args, **kwargs): + document_reference(*args, **kwargs) + + +class CollectionDocstring(LazyLoadedDocstring): + def _write_docstring(self, *args, **kwargs): + document_collection_object(*args, **kwargs) + + +class CollectionMethodDocstring(LazyLoadedDocstring): + def _write_docstring(self, *args, **kwargs): + document_collection_method(*args, **kwargs) + + +class BatchActionDocstring(LazyLoadedDocstring): + def _write_docstring(self, *args, **kwargs): + document_batch_action(*args, **kwargs) + + +class ResourceWaiterDocstring(LazyLoadedDocstring): + def _write_docstring(self, *args, **kwargs): + document_resource_waiter(*args, **kwargs) diff --git a/venv/lib/python3.12/site-packages/boto3/docs/method.py b/venv/lib/python3.12/site-packages/boto3/docs/method.py new file mode 100644 index 0000000..a5b3b25 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/docs/method.py @@ -0,0 +1,77 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from botocore.docs.method import document_model_driven_method + + +def document_model_driven_resource_method( + section, + method_name, + operation_model, + event_emitter, + method_description=None, + example_prefix=None, + include_input=None, + include_output=None, + exclude_input=None, + exclude_output=None, + document_output=True, + resource_action_model=None, + include_signature=True, +): + document_model_driven_method( + section=section, + method_name=method_name, + operation_model=operation_model, + event_emitter=event_emitter, + method_description=method_description, + example_prefix=example_prefix, + include_input=include_input, + include_output=include_output, + exclude_input=exclude_input, + exclude_output=exclude_output, + document_output=document_output, + include_signature=include_signature, + ) + + # If this action returns a resource modify the return example to + # appropriately reflect that. + if resource_action_model.resource: + if 'return' in section.available_sections: + section.delete_section('return') + resource_type = resource_action_model.resource.type + + new_return_section = section.add_new_section('return') + return_resource_type = ( + f'{operation_model.service_model.service_name}.{resource_type}' + ) + + return_type = f':py:class:`{return_resource_type}`' + return_description = f'{resource_type} resource' + + if _method_returns_resource_list(resource_action_model.resource): + return_type = f'list({return_type})' + return_description = f'A list of {resource_type} resources' + + new_return_section.style.new_line() + new_return_section.write(f':rtype: {return_type}') + new_return_section.style.new_line() + new_return_section.write(f':returns: {return_description}') + new_return_section.style.new_line() + + +def _method_returns_resource_list(resource): + for identifier in resource.identifiers: + if identifier.path and '[]' in identifier.path: + return True + + return False diff --git a/venv/lib/python3.12/site-packages/boto3/docs/resource.py b/venv/lib/python3.12/site-packages/boto3/docs/resource.py new file mode 100644 index 0000000..2e3464e --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/docs/resource.py @@ -0,0 +1,354 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import os + +from botocore import xform_name +from botocore.docs.bcdoc.restdoc import DocumentStructure +from botocore.docs.utils import get_official_service_name + +from boto3.docs.action import ActionDocumenter +from boto3.docs.attr import ( + document_attribute, + document_identifier, + document_reference, +) +from boto3.docs.base import BaseDocumenter +from boto3.docs.collection import CollectionDocumenter +from boto3.docs.subresource import SubResourceDocumenter +from boto3.docs.utils import ( + add_resource_type_overview, + get_identifier_args_for_signature, + get_identifier_description, + get_identifier_values_for_example, +) +from boto3.docs.waiter import WaiterResourceDocumenter + + +class ResourceDocumenter(BaseDocumenter): + def __init__(self, resource, botocore_session, root_docs_path): + super().__init__(resource) + self._botocore_session = botocore_session + self._root_docs_path = root_docs_path + self._resource_sub_path = self._resource_name.lower() + if self._resource_name == self._service_name: + self._resource_sub_path = 'service-resource' + + def document_resource(self, section): + self._add_title(section) + self._add_resource_note(section) + self._add_intro(section) + self._add_identifiers(section) + self._add_attributes(section) + self._add_references(section) + self._add_actions(section) + self._add_sub_resources(section) + self._add_collections(section) + self._add_waiters(section) + + def _add_title(self, section): + title_section = section.add_new_section('title') + title_section.style.h2(self._resource_name) + + def _add_intro(self, section): + identifier_names = [] + if self._resource_model.identifiers: + for identifier in self._resource_model.identifiers: + identifier_names.append(identifier.name) + + # Write out the class signature. + class_args = get_identifier_args_for_signature(identifier_names) + start_class = section.add_new_section('start_class') + start_class.style.start_sphinx_py_class( + class_name=f'{self.class_name}({class_args})' + ) + + # Add as short description about the resource + description_section = start_class.add_new_section('description') + self._add_description(description_section) + + # Add an example of how to instantiate the resource + example_section = start_class.add_new_section('example') + self._add_example(example_section, identifier_names) + + # Add the description for the parameters to instantiate the + # resource. + param_section = start_class.add_new_section('params') + self._add_params_description(param_section, identifier_names) + + end_class = section.add_new_section('end_class') + end_class.style.end_sphinx_py_class() + + def _add_description(self, section): + official_service_name = get_official_service_name(self._service_model) + section.write( + f'A resource representing an {official_service_name} {self._resource_name}' + ) + + def _add_example(self, section, identifier_names): + section.style.start_codeblock() + section.style.new_line() + section.write('import boto3') + section.style.new_line() + section.style.new_line() + section.write( + f'{self._service_name} = boto3.resource(\'{self._service_name}\')' + ) + section.style.new_line() + example_values = get_identifier_values_for_example(identifier_names) + section.write( + f'{xform_name(self._resource_name)} = {self._service_name}.{self._resource_name}({example_values})' + ) + section.style.end_codeblock() + + def _add_params_description(self, section, identifier_names): + for identifier_name in identifier_names: + description = get_identifier_description( + self._resource_name, identifier_name + ) + section.write(f':type {identifier_name}: string') + section.style.new_line() + section.write(f':param {identifier_name}: {description}') + section.style.new_line() + + def _add_overview_of_member_type(self, section, resource_member_type): + section.style.new_line() + section.write( + f'These are the resource\'s available {resource_member_type}:' + ) + section.style.new_line() + section.style.toctree() + for member in self.member_map[resource_member_type]: + section.style.tocitem(f'{member}') + + def _add_identifiers(self, section): + identifiers = self._resource.meta.resource_model.identifiers + section = section.add_new_section('identifiers') + member_list = [] + if identifiers: + self.member_map['identifiers'] = member_list + add_resource_type_overview( + section=section, + resource_type='Identifiers', + description=( + 'Identifiers are properties of a resource that are ' + 'set upon instantiation of the resource.' + ), + intro_link='identifiers_attributes_intro', + ) + for identifier in identifiers: + member_list.append(identifier.name) + # Create a new DocumentStructure for each identifier and add contents. + identifier_doc = DocumentStructure(identifier.name, target='html') + breadcrumb_section = identifier_doc.add_new_section('breadcrumb') + breadcrumb_section.style.ref(self._resource_class_name, 'index') + breadcrumb_section.write(f' / Identifier / {identifier.name}') + identifier_doc.add_title_section(identifier.name) + identifier_section = identifier_doc.add_new_section( + identifier.name, + context={'qualifier': f'{self.class_name}.'}, + ) + document_identifier( + section=identifier_section, + resource_name=self._resource_name, + identifier_model=identifier, + ) + # Write identifiers in individual/nested files. + # Path: /reference/services///.rst + identifiers_dir_path = os.path.join( + self._root_docs_path, + f'{self._service_name}', + f'{self._resource_sub_path}', + ) + identifier_doc.write_to_file(identifiers_dir_path, identifier.name) + + if identifiers: + self._add_overview_of_member_type(section, 'identifiers') + + def _add_attributes(self, section): + service_model = self._resource.meta.client.meta.service_model + attributes = {} + if self._resource.meta.resource_model.shape: + shape = service_model.shape_for( + self._resource.meta.resource_model.shape + ) + attributes = self._resource.meta.resource_model.get_attributes( + shape + ) + section = section.add_new_section('attributes') + attribute_list = [] + if attributes: + add_resource_type_overview( + section=section, + resource_type='Attributes', + description=( + 'Attributes provide access' + ' to the properties of a resource. Attributes are lazy-' + 'loaded the first time one is accessed via the' + ' :py:meth:`load` method.' + ), + intro_link='identifiers_attributes_intro', + ) + self.member_map['attributes'] = attribute_list + for attr_name in sorted(attributes): + _, attr_shape = attributes[attr_name] + attribute_list.append(attr_name) + # Create a new DocumentStructure for each attribute and add contents. + attribute_doc = DocumentStructure(attr_name, target='html') + breadcrumb_section = attribute_doc.add_new_section('breadcrumb') + breadcrumb_section.style.ref(self._resource_class_name, 'index') + breadcrumb_section.write(f' / Attribute / {attr_name}') + attribute_doc.add_title_section(attr_name) + attribute_section = attribute_doc.add_new_section( + attr_name, + context={'qualifier': f'{self.class_name}.'}, + ) + document_attribute( + section=attribute_section, + service_name=self._service_name, + resource_name=self._resource_name, + attr_name=attr_name, + event_emitter=self._resource.meta.client.meta.events, + attr_model=attr_shape, + ) + # Write attributes in individual/nested files. + # Path: /reference/services///.rst + attributes_dir_path = os.path.join( + self._root_docs_path, + f'{self._service_name}', + f'{self._resource_sub_path}', + ) + attribute_doc.write_to_file(attributes_dir_path, attr_name) + if attributes: + self._add_overview_of_member_type(section, 'attributes') + + def _add_references(self, section): + section = section.add_new_section('references') + references = self._resource.meta.resource_model.references + reference_list = [] + if references: + add_resource_type_overview( + section=section, + resource_type='References', + description=( + 'References are related resource instances that have ' + 'a belongs-to relationship.' + ), + intro_link='references_intro', + ) + self.member_map['references'] = reference_list + for reference in references: + reference_list.append(reference.name) + # Create a new DocumentStructure for each reference and add contents. + reference_doc = DocumentStructure(reference.name, target='html') + breadcrumb_section = reference_doc.add_new_section('breadcrumb') + breadcrumb_section.style.ref(self._resource_class_name, 'index') + breadcrumb_section.write(f' / Reference / {reference.name}') + reference_doc.add_title_section(reference.name) + reference_section = reference_doc.add_new_section( + reference.name, + context={'qualifier': f'{self.class_name}.'}, + ) + document_reference( + section=reference_section, + reference_model=reference, + ) + # Write references in individual/nested files. + # Path: /reference/services///.rst + references_dir_path = os.path.join( + self._root_docs_path, + f'{self._service_name}', + f'{self._resource_sub_path}', + ) + reference_doc.write_to_file(references_dir_path, reference.name) + if references: + self._add_overview_of_member_type(section, 'references') + + def _add_actions(self, section): + section = section.add_new_section('actions') + actions = self._resource.meta.resource_model.actions + if actions: + documenter = ActionDocumenter(self._resource, self._root_docs_path) + documenter.member_map = self.member_map + documenter.document_actions(section) + self._add_overview_of_member_type(section, 'actions') + + def _add_sub_resources(self, section): + section = section.add_new_section('sub-resources') + sub_resources = self._resource.meta.resource_model.subresources + if sub_resources: + documenter = SubResourceDocumenter( + self._resource, self._root_docs_path + ) + documenter.member_map = self.member_map + documenter.document_sub_resources(section) + self._add_overview_of_member_type(section, 'sub-resources') + + def _add_collections(self, section): + section = section.add_new_section('collections') + collections = self._resource.meta.resource_model.collections + if collections: + documenter = CollectionDocumenter( + self._resource, self._root_docs_path + ) + documenter.member_map = self.member_map + documenter.document_collections(section) + self._add_overview_of_member_type(section, 'collections') + + def _add_waiters(self, section): + section = section.add_new_section('waiters') + waiters = self._resource.meta.resource_model.waiters + if waiters: + service_waiter_model = self._botocore_session.get_waiter_model( + self._service_name + ) + documenter = WaiterResourceDocumenter( + self._resource, service_waiter_model, self._root_docs_path + ) + documenter.member_map = self.member_map + documenter.document_resource_waiters(section) + self._add_overview_of_member_type(section, 'waiters') + + def _add_resource_note(self, section): + section = section.add_new_section('feature-freeze') + section.style.start_note() + section.write( + "Before using anything on this page, please refer to the resources " + ":doc:`user guide <../../../../guide/resources>` for the most recent " + "guidance on using resources." + ) + section.style.end_note() + + +class ServiceResourceDocumenter(ResourceDocumenter): + @property + def class_name(self): + return f'{self._service_docs_name}.ServiceResource' + + def _add_title(self, section): + title_section = section.add_new_section('title') + title_section.style.h2('Service Resource') + + def _add_description(self, section): + official_service_name = get_official_service_name(self._service_model) + section.write(f'A resource representing {official_service_name}') + + def _add_example(self, section, identifier_names): + section.style.start_codeblock() + section.style.new_line() + section.write('import boto3') + section.style.new_line() + section.style.new_line() + section.write( + f'{self._service_name} = boto3.resource(\'{self._service_name}\')' + ) + section.style.end_codeblock() diff --git a/venv/lib/python3.12/site-packages/boto3/docs/service.py b/venv/lib/python3.12/site-packages/boto3/docs/service.py new file mode 100644 index 0000000..0415868 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/docs/service.py @@ -0,0 +1,201 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import os + +from botocore.docs.bcdoc.restdoc import DocumentStructure +from botocore.docs.service import ServiceDocumenter as BaseServiceDocumenter +from botocore.exceptions import DataNotFoundError + +import boto3 +from boto3.docs.client import Boto3ClientDocumenter +from boto3.docs.resource import ResourceDocumenter, ServiceResourceDocumenter +from boto3.utils import ServiceContext + + +class ServiceDocumenter(BaseServiceDocumenter): + # The path used to find examples + EXAMPLE_PATH = os.path.join(os.path.dirname(boto3.__file__), 'examples') + + def __init__(self, service_name, session, root_docs_path): + super().__init__( + service_name=service_name, + # I know that this is an internal attribute, but the botocore session + # is needed to load the paginator and waiter models. + session=session._session, + root_docs_path=root_docs_path, + ) + self._boto3_session = session + self._client = self._boto3_session.client(service_name) + self._service_resource = None + if self._service_name in self._boto3_session.get_available_resources(): + self._service_resource = self._boto3_session.resource(service_name) + self.sections = [ + 'title', + 'client', + 'paginators', + 'waiters', + 'resources', + 'examples', + 'context-params', + ] + self._root_docs_path = root_docs_path + self._USER_GUIDE_LINK = ( + 'https://docs.aws.amazon.com/boto3/latest/guide/resources.html' + ) + + def document_service(self): + """Documents an entire service. + + :returns: The reStructured text of the documented service. + """ + doc_structure = DocumentStructure( + self._service_name, section_names=self.sections, target='html' + ) + self.title(doc_structure.get_section('title')) + + self.client_api(doc_structure.get_section('client')) + self.paginator_api(doc_structure.get_section('paginators')) + self.waiter_api(doc_structure.get_section('waiters')) + if self._service_resource: + self.resource_section(doc_structure.get_section('resources')) + self._document_examples(doc_structure.get_section('examples')) + context_params_section = doc_structure.get_section('context-params') + self.client_context_params(context_params_section) + return doc_structure.flush_structure() + + def client_api(self, section): + examples = None + try: + examples = self.get_examples(self._service_name) + except DataNotFoundError: + pass + + Boto3ClientDocumenter( + self._client, self._root_docs_path, examples + ).document_client(section) + + def resource_section(self, section): + section.style.h2('Resources') + section.style.new_line() + section.write( + 'Resources are available in boto3 via the ' + '``resource`` method. For more detailed instructions ' + 'and examples on the usage of resources, see the ' + 'resources ' + ) + section.style.external_link( + title='user guide', + link=self._USER_GUIDE_LINK, + ) + section.write('.') + section.style.new_line() + section.style.new_line() + section.write('The available resources are:') + section.style.new_line() + section.style.toctree() + self._document_service_resource(section) + self._document_resources(section) + + def _document_service_resource(self, section): + # Create a new DocumentStructure for each Service Resource and add contents. + service_resource_doc = DocumentStructure( + 'service-resource', target='html' + ) + breadcrumb_section = service_resource_doc.add_new_section('breadcrumb') + breadcrumb_section.style.ref( + self._client.__class__.__name__, f'../../{self._service_name}' + ) + breadcrumb_section.write(' / Resource / ServiceResource') + ServiceResourceDocumenter( + self._service_resource, self._session, self._root_docs_path + ).document_resource(service_resource_doc) + # Write collections in individual/nested files. + # Path: /reference/services///.rst + resource_name = self._service_resource.meta.resource_model.name + if resource_name == self._service_name: + resource_name = 'service-resource' + service_resource_dir_path = os.path.join( + self._root_docs_path, + f'{self._service_name}', + f'{resource_name.lower()}', + ) + service_resource_doc.write_to_file(service_resource_dir_path, 'index') + section.style.tocitem(f'{self._service_name}/{resource_name}/index') + + def _document_resources(self, section): + temp_identifier_value = 'foo' + loader = self._session.get_component('data_loader') + json_resource_model = loader.load_service_model( + self._service_name, 'resources-1' + ) + service_model = self._service_resource.meta.client.meta.service_model + for resource_name in json_resource_model['resources']: + resource_model = json_resource_model['resources'][resource_name] + resource_cls = ( + self._boto3_session.resource_factory.load_from_definition( + resource_name=resource_name, + single_resource_json_definition=resource_model, + service_context=ServiceContext( + service_name=self._service_name, + resource_json_definitions=json_resource_model[ + 'resources' + ], + service_model=service_model, + service_waiter_model=None, + ), + ) + ) + identifiers = resource_cls.meta.resource_model.identifiers + args = [] + for _ in identifiers: + args.append(temp_identifier_value) + resource = resource_cls(*args, client=self._client) + # Create a new DocumentStructure for each Resource and add contents. + resource_name = resource.meta.resource_model.name.lower() + resource_doc = DocumentStructure(resource_name, target='html') + breadcrumb_section = resource_doc.add_new_section('breadcrumb') + breadcrumb_section.style.ref( + self._client.__class__.__name__, f'../../{self._service_name}' + ) + breadcrumb_section.write( + f' / Resource / {resource.meta.resource_model.name}' + ) + ResourceDocumenter( + resource, self._session, self._root_docs_path + ).document_resource( + resource_doc.add_new_section(resource.meta.resource_model.name) + ) + # Write collections in individual/nested files. + # Path: /reference/services///.rst + service_resource_dir_path = os.path.join( + self._root_docs_path, + f'{self._service_name}', + f'{resource_name}', + ) + resource_doc.write_to_file(service_resource_dir_path, 'index') + section.style.tocitem( + f'{self._service_name}/{resource_name}/index' + ) + + def _get_example_file(self): + return os.path.realpath( + os.path.join(self.EXAMPLE_PATH, f"{self._service_name}.rst") + ) + + def _document_examples(self, section): + examples_file = self._get_example_file() + if os.path.isfile(examples_file): + section.style.h2('Examples') + section.style.new_line() + with open(examples_file) as f: + section.write(f.read()) diff --git a/venv/lib/python3.12/site-packages/boto3/docs/subresource.py b/venv/lib/python3.12/site-packages/boto3/docs/subresource.py new file mode 100644 index 0000000..a28203d --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/docs/subresource.py @@ -0,0 +1,145 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import os + +from botocore import xform_name +from botocore.docs.bcdoc.restdoc import DocumentStructure +from botocore.utils import get_service_module_name + +from boto3.docs.base import NestedDocumenter +from boto3.docs.utils import ( + add_resource_type_overview, + get_identifier_args_for_signature, + get_identifier_description, + get_identifier_values_for_example, +) + + +class SubResourceDocumenter(NestedDocumenter): + def document_sub_resources(self, section): + add_resource_type_overview( + section=section, + resource_type='Sub-resources', + description=( + 'Sub-resources are methods that create a new instance of a' + ' child resource. This resource\'s identifiers get passed' + ' along to the child.' + ), + intro_link='subresources_intro', + ) + sub_resources = sorted( + self._resource.meta.resource_model.subresources, + key=lambda sub_resource: sub_resource.name, + ) + sub_resources_list = [] + self.member_map['sub-resources'] = sub_resources_list + for sub_resource in sub_resources: + sub_resources_list.append(sub_resource.name) + # Create a new DocumentStructure for each sub_resource and add contents. + sub_resource_doc = DocumentStructure( + sub_resource.name, target='html' + ) + breadcrumb_section = sub_resource_doc.add_new_section('breadcrumb') + breadcrumb_section.style.ref(self._resource_class_name, 'index') + breadcrumb_section.write(f' / Sub-Resource / {sub_resource.name}') + sub_resource_doc.add_title_section(sub_resource.name) + sub_resource_section = sub_resource_doc.add_new_section( + sub_resource.name, + context={'qualifier': f'{self.class_name}.'}, + ) + document_sub_resource( + section=sub_resource_section, + resource_name=self._resource_name, + sub_resource_model=sub_resource, + service_model=self._service_model, + ) + + # Write sub_resources in individual/nested files. + # Path: /reference/services///.rst + sub_resources_dir_path = os.path.join( + self._root_docs_path, + f'{self._service_name}', + f'{self._resource_sub_path}', + ) + sub_resource_doc.write_to_file( + sub_resources_dir_path, sub_resource.name + ) + + +def document_sub_resource( + section, + resource_name, + sub_resource_model, + service_model, + include_signature=True, +): + """Documents a resource action + + :param section: The section to write to + + :param resource_name: The name of the resource + + :param sub_resource_model: The model of the subresource + + :param service_model: The model of the service + + :param include_signature: Whether or not to include the signature. + It is useful for generating docstrings. + """ + identifiers_needed = [] + for identifier in sub_resource_model.resource.identifiers: + if identifier.source == 'input': + identifiers_needed.append(xform_name(identifier.target)) + + if include_signature: + signature_args = get_identifier_args_for_signature(identifiers_needed) + full_sub_resource_name = ( + f"{section.context.get('qualifier', '')}{sub_resource_model.name}" + ) + section.style.start_sphinx_py_method( + full_sub_resource_name, signature_args + ) + + method_intro_section = section.add_new_section('method-intro') + description = f'Creates a {sub_resource_model.resource.type} resource.' + method_intro_section.include_doc_string(description) + example_section = section.add_new_section('example') + example_values = get_identifier_values_for_example(identifiers_needed) + example_resource_name = xform_name(resource_name) + if service_model.service_name == resource_name: + example_resource_name = resource_name + example = f'{xform_name(sub_resource_model.resource.type)} = {example_resource_name}.{sub_resource_model.name}({example_values})' + example_section.style.start_codeblock() + example_section.write(example) + example_section.style.end_codeblock() + + param_section = section.add_new_section('params') + for identifier in identifiers_needed: + description = get_identifier_description( + sub_resource_model.name, identifier + ) + param_section.write(f':type {identifier}: string') + param_section.style.new_line() + param_section.write(f':param {identifier}: {description}') + param_section.style.new_line() + + return_section = section.add_new_section('return') + return_section.style.new_line() + return_section.write( + f':rtype: :py:class:`{get_service_module_name(service_model)}.{sub_resource_model.resource.type}`' + ) + return_section.style.new_line() + return_section.write( + f':returns: A {sub_resource_model.resource.type} resource' + ) + return_section.style.new_line() diff --git a/venv/lib/python3.12/site-packages/boto3/docs/utils.py b/venv/lib/python3.12/site-packages/boto3/docs/utils.py new file mode 100644 index 0000000..5af95b3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/docs/utils.py @@ -0,0 +1,85 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import inspect + +import jmespath +from botocore.docs.utils import DocumentModifiedShape # noqa: F401 + + +def get_resource_ignore_params(params): + """Helper method to determine which parameters to ignore for actions + + :returns: A list of the parameter names that does not need to be + included in a resource's method call for documentation purposes. + """ + ignore_params = [] + for param in params: + result = jmespath.compile(param.target) + current = result.parsed + # Use JMESPath to find the left most element in the target expression + # which will be the parameter to ignore in the action call. + while current['children']: + current = current['children'][0] + # Make sure the parameter we are about to ignore is a field. + # If it is not, we should ignore the result to avoid false positives. + if current['type'] == 'field': + ignore_params.append(current['value']) + return ignore_params + + +def is_resource_action(action_handle): + return inspect.isfunction(action_handle) + + +def get_resource_public_actions(resource_class): + resource_class_members = inspect.getmembers(resource_class) + resource_methods = {} + for name, member in resource_class_members: + if not name.startswith('_'): + if not name[0].isupper(): + if not name.startswith('wait_until'): + if is_resource_action(member): + resource_methods[name] = member + return resource_methods + + +def get_identifier_values_for_example(identifier_names): + return ','.join([f'\'{identifier}\'' for identifier in identifier_names]) + + +def get_identifier_args_for_signature(identifier_names): + return ','.join(identifier_names) + + +def get_identifier_description(resource_name, identifier_name): + return ( + f"The {resource_name}'s {identifier_name} identifier. " + f"This **must** be set." + ) + + +def add_resource_type_overview( + section, resource_type, description, intro_link=None +): + section.style.new_line() + section.style.h3(resource_type) + section.style.new_line() + section.style.new_line() + section.write(description) + section.style.new_line() + if intro_link is not None: + section.write( + f'For more information about {resource_type.lower()} refer to the ' + f':ref:`Resources Introduction Guide<{intro_link}>`.' + ) + section.style.new_line() diff --git a/venv/lib/python3.12/site-packages/boto3/docs/waiter.py b/venv/lib/python3.12/site-packages/boto3/docs/waiter.py new file mode 100644 index 0000000..4713ce7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/docs/waiter.py @@ -0,0 +1,130 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import os + +from botocore import xform_name +from botocore.docs.bcdoc.restdoc import DocumentStructure +from botocore.docs.method import document_model_driven_method +from botocore.utils import get_service_module_name + +from boto3.docs.base import NestedDocumenter +from boto3.docs.utils import ( + add_resource_type_overview, + get_resource_ignore_params, +) + + +class WaiterResourceDocumenter(NestedDocumenter): + def __init__(self, resource, service_waiter_model, root_docs_path): + super().__init__(resource, root_docs_path) + self._service_waiter_model = service_waiter_model + + def document_resource_waiters(self, section): + waiters = self._resource.meta.resource_model.waiters + add_resource_type_overview( + section=section, + resource_type='Waiters', + description=( + 'Waiters provide an interface to wait for a resource' + ' to reach a specific state.' + ), + intro_link='waiters_intro', + ) + waiter_list = [] + self.member_map['waiters'] = waiter_list + for waiter in waiters: + waiter_list.append(waiter.name) + # Create a new DocumentStructure for each waiter and add contents. + waiter_doc = DocumentStructure(waiter.name, target='html') + breadcrumb_section = waiter_doc.add_new_section('breadcrumb') + breadcrumb_section.style.ref(self._resource_class_name, 'index') + breadcrumb_section.write(f' / Waiter / {waiter.name}') + waiter_doc.add_title_section(waiter.name) + waiter_section = waiter_doc.add_new_section( + waiter.name, + context={'qualifier': f'{self.class_name}.'}, + ) + document_resource_waiter( + section=waiter_section, + resource_name=self._resource_name, + event_emitter=self._resource.meta.client.meta.events, + service_model=self._service_model, + resource_waiter_model=waiter, + service_waiter_model=self._service_waiter_model, + ) + # Write waiters in individual/nested files. + # Path: /reference/services///.rst + waiters_dir_path = os.path.join( + self._root_docs_path, + f'{self._service_name}', + f'{self._resource_sub_path}', + ) + waiter_doc.write_to_file(waiters_dir_path, waiter.name) + + +def document_resource_waiter( + section, + resource_name, + event_emitter, + service_model, + resource_waiter_model, + service_waiter_model, + include_signature=True, +): + waiter_model = service_waiter_model.get_waiter( + resource_waiter_model.waiter_name + ) + operation_model = service_model.operation_model(waiter_model.operation) + + ignore_params = get_resource_ignore_params(resource_waiter_model.params) + service_module_name = get_service_module_name(service_model) + description = ( + 'Waits until this {} is {}. This method calls ' + ':py:meth:`{}.Waiter.{}.wait` which polls ' + ':py:meth:`{}.Client.{}` every {} seconds until ' + 'a successful state is reached. An error is raised ' + 'after {} failed checks.'.format( + resource_name, + ' '.join(resource_waiter_model.name.split('_')[2:]), + service_module_name, + xform_name(resource_waiter_model.waiter_name), + service_module_name, + xform_name(waiter_model.operation), + waiter_model.delay, + waiter_model.max_attempts, + ) + ) + example_prefix = ( + f'{xform_name(resource_name)}.{resource_waiter_model.name}' + ) + full_waiter_name = ( + f"{section.context.get('qualifier', '')}{resource_waiter_model.name}" + ) + document_model_driven_method( + section=section, + method_name=full_waiter_name, + operation_model=operation_model, + event_emitter=event_emitter, + example_prefix=example_prefix, + method_description=description, + exclude_input=ignore_params, + include_signature=include_signature, + ) + if 'return' in section.available_sections: + # Waiters do not return anything so we should remove + # any sections that may document the underlying return + # value of the client method. + return_section = section.get_section('return') + return_section.clear_text() + return_section.remove_all_sections() + return_section.write(':returns: None') diff --git a/venv/lib/python3.12/site-packages/boto3/dynamodb/__init__.py b/venv/lib/python3.12/site-packages/boto3/dynamodb/__init__.py new file mode 100644 index 0000000..6001b27 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/dynamodb/__init__.py @@ -0,0 +1,12 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. diff --git a/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..91318c6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/conditions.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/conditions.cpython-312.pyc new file mode 100644 index 0000000..141e991 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/conditions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/table.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/table.cpython-312.pyc new file mode 100644 index 0000000..be2c216 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/table.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/transform.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/transform.cpython-312.pyc new file mode 100644 index 0000000..4e233ef Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/transform.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/types.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/types.cpython-312.pyc new file mode 100644 index 0000000..a30ea0c Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/dynamodb/__pycache__/types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/dynamodb/conditions.py b/venv/lib/python3.12/site-packages/boto3/dynamodb/conditions.py new file mode 100644 index 0000000..8307489 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/dynamodb/conditions.py @@ -0,0 +1,461 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import re +from collections import namedtuple + +from boto3.exceptions import ( + DynamoDBNeedsConditionError, + DynamoDBNeedsKeyConditionError, + DynamoDBOperationNotSupportedError, +) + +ATTR_NAME_REGEX = re.compile(r'[^.\[\]]+(?![^\[]*\])') + + +class ConditionBase: + expression_format = '' + expression_operator = '' + has_grouped_values = False + + def __init__(self, *values): + self._values = values + + def __and__(self, other): + if not isinstance(other, ConditionBase): + raise DynamoDBOperationNotSupportedError('AND', other) + return And(self, other) + + def __or__(self, other): + if not isinstance(other, ConditionBase): + raise DynamoDBOperationNotSupportedError('OR', other) + return Or(self, other) + + def __invert__(self): + return Not(self) + + def get_expression(self): + return { + 'format': self.expression_format, + 'operator': self.expression_operator, + 'values': self._values, + } + + def __eq__(self, other): + if isinstance(other, type(self)): + if self._values == other._values: + return True + return False + + def __ne__(self, other): + return not self.__eq__(other) + + +class AttributeBase: + def __init__(self, name): + self.name = name + + def __and__(self, value): + raise DynamoDBOperationNotSupportedError('AND', self) + + def __or__(self, value): + raise DynamoDBOperationNotSupportedError('OR', self) + + def __invert__(self): + raise DynamoDBOperationNotSupportedError('NOT', self) + + def eq(self, value): + """Creates a condition where the attribute is equal to the value. + + :param value: The value that the attribute is equal to. + """ + return Equals(self, value) + + def lt(self, value): + """Creates a condition where the attribute is less than the value. + + :param value: The value that the attribute is less than. + """ + return LessThan(self, value) + + def lte(self, value): + """Creates a condition where the attribute is less than or equal to the + value. + + :param value: The value that the attribute is less than or equal to. + """ + return LessThanEquals(self, value) + + def gt(self, value): + """Creates a condition where the attribute is greater than the value. + + :param value: The value that the attribute is greater than. + """ + return GreaterThan(self, value) + + def gte(self, value): + """Creates a condition where the attribute is greater than or equal to + the value. + + :param value: The value that the attribute is greater than or equal to. + """ + return GreaterThanEquals(self, value) + + def begins_with(self, value): + """Creates a condition where the attribute begins with the value. + + :param value: The value that the attribute begins with. + """ + return BeginsWith(self, value) + + def between(self, low_value, high_value): + """Creates a condition where the attribute is greater than or equal + to the low value and less than or equal to the high value. + + :param low_value: The value that the attribute is greater than or equal to. + :param high_value: The value that the attribute is less than or equal to. + """ + return Between(self, low_value, high_value) + + def __eq__(self, other): + return isinstance(other, type(self)) and self.name == other.name + + def __ne__(self, other): + return not self.__eq__(other) + + +class ConditionAttributeBase(ConditionBase, AttributeBase): + """This base class is for conditions that can have attribute methods. + + One example is the Size condition. To complete a condition, you need + to apply another AttributeBase method like eq(). + """ + + def __init__(self, *values): + ConditionBase.__init__(self, *values) + # This is assuming the first value to the condition is the attribute + # in which can be used to generate its attribute base. + AttributeBase.__init__(self, values[0].name) + + def __eq__(self, other): + return ConditionBase.__eq__(self, other) and AttributeBase.__eq__( + self, other + ) + + def __ne__(self, other): + return not self.__eq__(other) + + +class ComparisonCondition(ConditionBase): + expression_format = '{0} {operator} {1}' + + +class Equals(ComparisonCondition): + expression_operator = '=' + + +class NotEquals(ComparisonCondition): + expression_operator = '<>' + + +class LessThan(ComparisonCondition): + expression_operator = '<' + + +class LessThanEquals(ComparisonCondition): + expression_operator = '<=' + + +class GreaterThan(ComparisonCondition): + expression_operator = '>' + + +class GreaterThanEquals(ComparisonCondition): + expression_operator = '>=' + + +class In(ComparisonCondition): + expression_operator = 'IN' + has_grouped_values = True + + +class Between(ConditionBase): + expression_operator = 'BETWEEN' + expression_format = '{0} {operator} {1} AND {2}' + + +class BeginsWith(ConditionBase): + expression_operator = 'begins_with' + expression_format = '{operator}({0}, {1})' + + +class Contains(ConditionBase): + expression_operator = 'contains' + expression_format = '{operator}({0}, {1})' + + +class Size(ConditionAttributeBase): + expression_operator = 'size' + expression_format = '{operator}({0})' + + +class AttributeType(ConditionBase): + expression_operator = 'attribute_type' + expression_format = '{operator}({0}, {1})' + + +class AttributeExists(ConditionBase): + expression_operator = 'attribute_exists' + expression_format = '{operator}({0})' + + +class AttributeNotExists(ConditionBase): + expression_operator = 'attribute_not_exists' + expression_format = '{operator}({0})' + + +class And(ConditionBase): + expression_operator = 'AND' + expression_format = '({0} {operator} {1})' + + +class Or(ConditionBase): + expression_operator = 'OR' + expression_format = '({0} {operator} {1})' + + +class Not(ConditionBase): + expression_operator = 'NOT' + expression_format = '({operator} {0})' + + +class Key(AttributeBase): + pass + + +class Attr(AttributeBase): + """Represents an DynamoDB item's attribute.""" + + def ne(self, value): + """Creates a condition where the attribute is not equal to the value + + :param value: The value that the attribute is not equal to. + """ + return NotEquals(self, value) + + def is_in(self, value): + """Creates a condition where the attribute is in the value, + + :type value: list + :param value: The value that the attribute is in. + """ + return In(self, value) + + def exists(self): + """Creates a condition where the attribute exists.""" + return AttributeExists(self) + + def not_exists(self): + """Creates a condition where the attribute does not exist.""" + return AttributeNotExists(self) + + def contains(self, value): + """Creates a condition where the attribute contains the value. + + :param value: The value the attribute contains. + """ + return Contains(self, value) + + def size(self): + """Creates a condition for the attribute size. + + Note another AttributeBase method must be called on the returned + size condition to be a valid DynamoDB condition. + """ + return Size(self) + + def attribute_type(self, value): + """Creates a condition for the attribute type. + + :param value: The type of the attribute. + """ + return AttributeType(self, value) + + +BuiltConditionExpression = namedtuple( + 'BuiltConditionExpression', + [ + 'condition_expression', + 'attribute_name_placeholders', + 'attribute_value_placeholders', + ], +) + + +class ConditionExpressionBuilder: + """This class is used to build condition expressions with placeholders""" + + def __init__(self): + self._name_count = 0 + self._value_count = 0 + self._name_placeholder = 'n' + self._value_placeholder = 'v' + + def _get_name_placeholder(self): + return f"#{self._name_placeholder}{self._name_count}" + + def _get_value_placeholder(self): + return f":{self._value_placeholder}{self._value_count}" + + def reset(self): + """Resets the placeholder name and values""" + self._name_count = 0 + self._value_count = 0 + + def build_expression(self, condition, is_key_condition=False): + """Builds the condition expression and the dictionary of placeholders. + + :type condition: ConditionBase + :param condition: A condition to be built into a condition expression + string with any necessary placeholders. + + :type is_key_condition: Boolean + :param is_key_condition: True if the expression is for a + KeyConditionExpression. False otherwise. + + :rtype: (string, dict, dict) + :returns: Will return a string representing the condition with + placeholders inserted where necessary, a dictionary of + placeholders for attribute names, and a dictionary of + placeholders for attribute values. Here is a sample return value: + + ('#n0 = :v0', {'#n0': 'myattribute'}, {':v1': 'myvalue'}) + """ + if not isinstance(condition, ConditionBase): + raise DynamoDBNeedsConditionError(condition) + attribute_name_placeholders = {} + attribute_value_placeholders = {} + condition_expression = self._build_expression( + condition, + attribute_name_placeholders, + attribute_value_placeholders, + is_key_condition=is_key_condition, + ) + return BuiltConditionExpression( + condition_expression=condition_expression, + attribute_name_placeholders=attribute_name_placeholders, + attribute_value_placeholders=attribute_value_placeholders, + ) + + def _build_expression( + self, + condition, + attribute_name_placeholders, + attribute_value_placeholders, + is_key_condition, + ): + expression_dict = condition.get_expression() + replaced_values = [] + for value in expression_dict['values']: + # Build the necessary placeholders for that value. + # Placeholders are built for both attribute names and values. + replaced_value = self._build_expression_component( + value, + attribute_name_placeholders, + attribute_value_placeholders, + condition.has_grouped_values, + is_key_condition, + ) + replaced_values.append(replaced_value) + # Fill out the expression using the operator and the + # values that have been replaced with placeholders. + return expression_dict['format'].format( + *replaced_values, operator=expression_dict['operator'] + ) + + def _build_expression_component( + self, + value, + attribute_name_placeholders, + attribute_value_placeholders, + has_grouped_values, + is_key_condition, + ): + # Continue to recurse if the value is a ConditionBase in order + # to extract out all parts of the expression. + if isinstance(value, ConditionBase): + return self._build_expression( + value, + attribute_name_placeholders, + attribute_value_placeholders, + is_key_condition, + ) + # If it is not a ConditionBase, we can recurse no further. + # So we check if it is an attribute and add placeholders for + # its name + elif isinstance(value, AttributeBase): + if is_key_condition and not isinstance(value, Key): + raise DynamoDBNeedsKeyConditionError( + f'Attribute object {value.name} is of type {type(value)}. ' + f'KeyConditionExpression only supports Attribute objects ' + f'of type Key' + ) + return self._build_name_placeholder( + value, attribute_name_placeholders + ) + # If it is anything else, we treat it as a value and thus placeholders + # are needed for the value. + else: + return self._build_value_placeholder( + value, attribute_value_placeholders, has_grouped_values + ) + + def _build_name_placeholder(self, value, attribute_name_placeholders): + attribute_name = value.name + # Figure out which parts of the attribute name that needs replacement. + attribute_name_parts = ATTR_NAME_REGEX.findall(attribute_name) + + # Add a temporary placeholder for each of these parts. + placeholder_format = ATTR_NAME_REGEX.sub('%s', attribute_name) + str_format_args = [] + for part in attribute_name_parts: + name_placeholder = self._get_name_placeholder() + self._name_count += 1 + str_format_args.append(name_placeholder) + # Add the placeholder and value to dictionary of name placeholders. + attribute_name_placeholders[name_placeholder] = part + # Replace the temporary placeholders with the designated placeholders. + return placeholder_format % tuple(str_format_args) + + def _build_value_placeholder( + self, value, attribute_value_placeholders, has_grouped_values=False + ): + # If the values are grouped, we need to add a placeholder for + # each element inside of the actual value. + if has_grouped_values: + placeholder_list = [] + for v in value: + value_placeholder = self._get_value_placeholder() + self._value_count += 1 + placeholder_list.append(value_placeholder) + attribute_value_placeholders[value_placeholder] = v + # Assuming the values are grouped by parenthesis. + # IN is the currently the only one that uses this so it maybe + # needed to be changed in future. + return f"({', '.join(placeholder_list)})" + # Otherwise, treat the value as a single value that needs only + # one placeholder. + else: + value_placeholder = self._get_value_placeholder() + self._value_count += 1 + attribute_value_placeholders[value_placeholder] = value + return value_placeholder diff --git a/venv/lib/python3.12/site-packages/boto3/dynamodb/table.py b/venv/lib/python3.12/site-packages/boto3/dynamodb/table.py new file mode 100644 index 0000000..d937b4d --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/dynamodb/table.py @@ -0,0 +1,167 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import logging + +logger = logging.getLogger(__name__) + + +def register_table_methods(base_classes, **kwargs): + base_classes.insert(0, TableResource) + + +# This class can be used to add any additional methods we want +# onto a table resource. Ideally to avoid creating a new +# base class for every method we can just update this +# class instead. Just be sure to move the bulk of the +# actual method implementation to another class. +class TableResource: + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def batch_writer(self, overwrite_by_pkeys=None): + """Create a batch writer object. + + This method creates a context manager for writing + objects to Amazon DynamoDB in batch. + + The batch writer will automatically handle buffering and sending items + in batches. In addition, the batch writer will also automatically + handle any unprocessed items and resend them as needed. All you need + to do is call ``put_item`` for any items you want to add, and + ``delete_item`` for any items you want to delete. + + Example usage:: + + with table.batch_writer() as batch: + for _ in range(1000000): + batch.put_item(Item={'HashKey': '...', + 'Otherstuff': '...'}) + # You can also delete_items in a batch. + batch.delete_item(Key={'HashKey': 'SomeHashKey'}) + + :type overwrite_by_pkeys: list(string) + :param overwrite_by_pkeys: De-duplicate request items in buffer + if match new request item on specified primary keys. i.e + ``["partition_key1", "sort_key2", "sort_key3"]`` + + """ + return BatchWriter( + self.name, self.meta.client, overwrite_by_pkeys=overwrite_by_pkeys + ) + + +class BatchWriter: + """Automatically handle batch writes to DynamoDB for a single table.""" + + def __init__( + self, table_name, client, flush_amount=25, overwrite_by_pkeys=None + ): + """ + + :type table_name: str + :param table_name: The name of the table. The class handles + batch writes to a single table. + + :type client: ``botocore.client.Client`` + :param client: A botocore client. Note this client + **must** have the dynamodb customizations applied + to it for transforming AttributeValues into the + wire protocol. What this means in practice is that + you need to use a client that comes from a DynamoDB + resource if you're going to instantiate this class + directly, i.e + ``boto3.resource('dynamodb').Table('foo').meta.client``. + + :type flush_amount: int + :param flush_amount: The number of items to keep in + a local buffer before sending a batch_write_item + request to DynamoDB. + + :type overwrite_by_pkeys: list(string) + :param overwrite_by_pkeys: De-duplicate request items in buffer + if match new request item on specified primary keys. i.e + ``["partition_key1", "sort_key2", "sort_key3"]`` + + """ + self._table_name = table_name + self._client = client + self._items_buffer = [] + self._flush_amount = flush_amount + self._overwrite_by_pkeys = overwrite_by_pkeys + + def put_item(self, Item): + self._add_request_and_process({'PutRequest': {'Item': Item}}) + + def delete_item(self, Key): + self._add_request_and_process({'DeleteRequest': {'Key': Key}}) + + def _add_request_and_process(self, request): + if self._overwrite_by_pkeys: + self._remove_dup_pkeys_request_if_any(request) + self._items_buffer.append(request) + self._flush_if_needed() + + def _remove_dup_pkeys_request_if_any(self, request): + pkey_values_new = self._extract_pkey_values(request) + for item in self._items_buffer: + if self._extract_pkey_values(item) == pkey_values_new: + self._items_buffer.remove(item) + logger.debug( + "With overwrite_by_pkeys enabled, skipping request:%s", + item, + ) + + def _extract_pkey_values(self, request): + if request.get('PutRequest'): + return [ + request['PutRequest']['Item'][key] + for key in self._overwrite_by_pkeys + ] + elif request.get('DeleteRequest'): + return [ + request['DeleteRequest']['Key'][key] + for key in self._overwrite_by_pkeys + ] + return None + + def _flush_if_needed(self): + if len(self._items_buffer) >= self._flush_amount: + self._flush() + + def _flush(self): + items_to_send = self._items_buffer[: self._flush_amount] + self._items_buffer = self._items_buffer[self._flush_amount :] + response = self._client.batch_write_item( + RequestItems={self._table_name: items_to_send} + ) + unprocessed_items = response['UnprocessedItems'] + if not unprocessed_items: + unprocessed_items = {} + item_list = unprocessed_items.get(self._table_name, []) + # Any unprocessed_items are immediately added to the + # next batch we send. + self._items_buffer.extend(item_list) + logger.debug( + "Batch write sent %s, unprocessed: %s", + len(items_to_send), + len(self._items_buffer), + ) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, tb): + # When we exit, we need to keep flushing whatever's left + # until there's nothing left in our items buffer. + while self._items_buffer: + self._flush() diff --git a/venv/lib/python3.12/site-packages/boto3/dynamodb/transform.py b/venv/lib/python3.12/site-packages/boto3/dynamodb/transform.py new file mode 100644 index 0000000..3944f31 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/dynamodb/transform.py @@ -0,0 +1,343 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import copy + +from boto3.compat import collections_abc +from boto3.docs.utils import DocumentModifiedShape +from boto3.dynamodb.conditions import ConditionBase, ConditionExpressionBuilder +from boto3.dynamodb.types import TypeDeserializer, TypeSerializer + + +def register_high_level_interface(base_classes, **kwargs): + base_classes.insert(0, DynamoDBHighLevelResource) + + +class _ForgetfulDict(dict): + """A dictionary that discards any items set on it. For use as `memo` in + `copy.deepcopy()` when every instance of a repeated object in the deepcopied + data structure should result in a separate copy. + """ + + def __setitem__(self, key, value): + pass + + +def copy_dynamodb_params(params, **kwargs): + return copy.deepcopy(params, memo=_ForgetfulDict()) + + +class DynamoDBHighLevelResource: + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Apply handler that creates a copy of the user provided dynamodb + # item such that it can be modified. + self.meta.client.meta.events.register( + 'provide-client-params.dynamodb', + copy_dynamodb_params, + unique_id='dynamodb-create-params-copy', + ) + + self._injector = TransformationInjector() + # Apply the handler that generates condition expressions including + # placeholders. + self.meta.client.meta.events.register( + 'before-parameter-build.dynamodb', + self._injector.inject_condition_expressions, + unique_id='dynamodb-condition-expression', + ) + + # Apply the handler that serializes the request from python + # types to dynamodb types. + self.meta.client.meta.events.register( + 'before-parameter-build.dynamodb', + self._injector.inject_attribute_value_input, + unique_id='dynamodb-attr-value-input', + ) + + # Apply the handler that deserializes the response from dynamodb + # types to python types. + self.meta.client.meta.events.register( + 'after-call.dynamodb', + self._injector.inject_attribute_value_output, + unique_id='dynamodb-attr-value-output', + ) + + # Apply the documentation customizations to account for + # the transformations. + attr_value_shape_docs = DocumentModifiedShape( + 'AttributeValue', + new_type='valid DynamoDB type', + new_description=( + '- The value of the attribute. The valid value types are ' + 'listed in the ' + ':ref:`DynamoDB Reference Guide`.' + ), + new_example_value=( + '\'string\'|123|Binary(b\'bytes\')|True|None|set([\'string\'])' + '|set([123])|set([Binary(b\'bytes\')])|[]|{}' + ), + ) + + key_expression_shape_docs = DocumentModifiedShape( + 'KeyExpression', + new_type=( + 'condition from :py:class:`boto3.dynamodb.conditions.Key` ' + 'method' + ), + new_description=( + 'The condition(s) a key(s) must meet. Valid conditions are ' + 'listed in the ' + ':ref:`DynamoDB Reference Guide`.' + ), + new_example_value='Key(\'mykey\').eq(\'myvalue\')', + ) + + con_expression_shape_docs = DocumentModifiedShape( + 'ConditionExpression', + new_type=( + 'condition from :py:class:`boto3.dynamodb.conditions.Attr` ' + 'method' + ), + new_description=( + 'The condition(s) an attribute(s) must meet. Valid conditions ' + 'are listed in the ' + ':ref:`DynamoDB Reference Guide`.' + ), + new_example_value='Attr(\'myattribute\').eq(\'myvalue\')', + ) + + self.meta.client.meta.events.register( + 'docs.*.dynamodb.*.complete-section', + attr_value_shape_docs.replace_documentation_for_matching_shape, + unique_id='dynamodb-attr-value-docs', + ) + + self.meta.client.meta.events.register( + 'docs.*.dynamodb.*.complete-section', + key_expression_shape_docs.replace_documentation_for_matching_shape, + unique_id='dynamodb-key-expression-docs', + ) + + self.meta.client.meta.events.register( + 'docs.*.dynamodb.*.complete-section', + con_expression_shape_docs.replace_documentation_for_matching_shape, + unique_id='dynamodb-cond-expression-docs', + ) + + +class TransformationInjector: + """Injects the transformations into the user provided parameters.""" + + def __init__( + self, + transformer=None, + condition_builder=None, + serializer=None, + deserializer=None, + ): + self._transformer = transformer + if transformer is None: + self._transformer = ParameterTransformer() + + self._condition_builder = condition_builder + if condition_builder is None: + self._condition_builder = ConditionExpressionBuilder() + + self._serializer = serializer + if serializer is None: + self._serializer = TypeSerializer() + + self._deserializer = deserializer + if deserializer is None: + self._deserializer = TypeDeserializer() + + def inject_condition_expressions(self, params, model, **kwargs): + """Injects the condition expression transformation into the parameters + + This injection includes transformations for ConditionExpression shapes + and KeyExpression shapes. It also handles any placeholder names and + values that are generated when transforming the condition expressions. + """ + self._condition_builder.reset() + generated_names = {} + generated_values = {} + + # Create and apply the Condition Expression transformation. + transformation = ConditionExpressionTransformation( + self._condition_builder, + placeholder_names=generated_names, + placeholder_values=generated_values, + is_key_condition=False, + ) + self._transformer.transform( + params, model.input_shape, transformation, 'ConditionExpression' + ) + + # Create and apply the Key Condition Expression transformation. + transformation = ConditionExpressionTransformation( + self._condition_builder, + placeholder_names=generated_names, + placeholder_values=generated_values, + is_key_condition=True, + ) + self._transformer.transform( + params, model.input_shape, transformation, 'KeyExpression' + ) + + expr_attr_names_input = 'ExpressionAttributeNames' + expr_attr_values_input = 'ExpressionAttributeValues' + + # Now that all of the condition expression transformation are done, + # update the placeholder dictionaries in the request. + if expr_attr_names_input in params: + params[expr_attr_names_input].update(generated_names) + else: + if generated_names: + params[expr_attr_names_input] = generated_names + + if expr_attr_values_input in params: + params[expr_attr_values_input].update(generated_values) + else: + if generated_values: + params[expr_attr_values_input] = generated_values + + def inject_attribute_value_input(self, params, model, **kwargs): + """Injects DynamoDB serialization into parameter input""" + self._transformer.transform( + params, + model.input_shape, + self._serializer.serialize, + 'AttributeValue', + ) + + def inject_attribute_value_output(self, parsed, model, **kwargs): + """Injects DynamoDB deserialization into responses""" + if model.output_shape is not None: + self._transformer.transform( + parsed, + model.output_shape, + self._deserializer.deserialize, + 'AttributeValue', + ) + + +class ConditionExpressionTransformation: + """Provides a transformation for condition expressions + + The ``ParameterTransformer`` class can call this class directly + to transform the condition expressions in the parameters provided. + """ + + def __init__( + self, + condition_builder, + placeholder_names, + placeholder_values, + is_key_condition=False, + ): + self._condition_builder = condition_builder + self._placeholder_names = placeholder_names + self._placeholder_values = placeholder_values + self._is_key_condition = is_key_condition + + def __call__(self, value): + if isinstance(value, ConditionBase): + # Create a conditional expression string with placeholders + # for the provided condition. + built_expression = self._condition_builder.build_expression( + value, is_key_condition=self._is_key_condition + ) + + self._placeholder_names.update( + built_expression.attribute_name_placeholders + ) + self._placeholder_values.update( + built_expression.attribute_value_placeholders + ) + + return built_expression.condition_expression + # Use the user provided value if it is not a ConditonBase object. + return value + + +class ParameterTransformer: + """Transforms the input to and output from botocore based on shape""" + + def transform(self, params, model, transformation, target_shape): + """Transforms the dynamodb input to or output from botocore + + It applies a specified transformation whenever a specific shape name + is encountered while traversing the parameters in the dictionary. + + :param params: The parameters structure to transform. + :param model: The operation model. + :param transformation: The function to apply the parameter + :param target_shape: The name of the shape to apply the + transformation to + """ + self._transform_parameters(model, params, transformation, target_shape) + + def _transform_parameters( + self, model, params, transformation, target_shape + ): + type_name = model.type_name + if type_name in ('structure', 'map', 'list'): + getattr(self, f'_transform_{type_name}')( + model, params, transformation, target_shape + ) + + def _transform_structure( + self, model, params, transformation, target_shape + ): + if not isinstance(params, collections_abc.Mapping): + return + for param in params: + if param in model.members: + member_model = model.members[param] + member_shape = member_model.name + if member_shape == target_shape: + params[param] = transformation(params[param]) + else: + self._transform_parameters( + member_model, + params[param], + transformation, + target_shape, + ) + + def _transform_map(self, model, params, transformation, target_shape): + if not isinstance(params, collections_abc.Mapping): + return + value_model = model.value + value_shape = value_model.name + for key, value in params.items(): + if value_shape == target_shape: + params[key] = transformation(value) + else: + self._transform_parameters( + value_model, params[key], transformation, target_shape + ) + + def _transform_list(self, model, params, transformation, target_shape): + if not isinstance(params, collections_abc.MutableSequence): + return + member_model = model.member + member_shape = member_model.name + for i, item in enumerate(params): + if member_shape == target_shape: + params[i] = transformation(item) + else: + self._transform_parameters( + member_model, params[i], transformation, target_shape + ) diff --git a/venv/lib/python3.12/site-packages/boto3/dynamodb/types.py b/venv/lib/python3.12/site-packages/boto3/dynamodb/types.py new file mode 100644 index 0000000..f358b12 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/dynamodb/types.py @@ -0,0 +1,310 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from decimal import ( + Clamped, + Context, + Decimal, + Inexact, + Overflow, + Rounded, + Underflow, +) + +from boto3.compat import collections_abc + +STRING = 'S' +NUMBER = 'N' +BINARY = 'B' +STRING_SET = 'SS' +NUMBER_SET = 'NS' +BINARY_SET = 'BS' +NULL = 'NULL' +BOOLEAN = 'BOOL' +MAP = 'M' +LIST = 'L' + + +DYNAMODB_CONTEXT = Context( + Emin=-128, + Emax=126, + prec=38, + traps=[Clamped, Overflow, Inexact, Rounded, Underflow], +) + + +BINARY_TYPES = (bytearray, bytes) + + +class Binary: + """A class for representing Binary in dynamodb + + Especially for Python 2, use this class to explicitly specify + binary data for item in DynamoDB. It is essentially a wrapper around + binary. Unicode and Python 3 string types are not allowed. + """ + + def __init__(self, value): + if not isinstance(value, BINARY_TYPES): + types = ', '.join([str(t) for t in BINARY_TYPES]) + raise TypeError(f'Value must be of the following types: {types}') + self.value = value + + def __eq__(self, other): + if isinstance(other, Binary): + return self.value == other.value + return self.value == other + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return f'Binary({self.value!r})' + + def __str__(self): + return self.value + + def __bytes__(self): + return self.value + + def __hash__(self): + return hash(self.value) + + +class TypeSerializer: + """This class serializes Python data types to DynamoDB types.""" + + def serialize(self, value): + """The method to serialize the Python data types. + + :param value: A python value to be serialized to DynamoDB. Here are + the various conversions: + + Python DynamoDB + ------ -------- + None {'NULL': True} + True/False {'BOOL': True/False} + int/Decimal {'N': str(value)} + string {'S': string} + Binary/bytearray/bytes (py3 only) {'B': bytes} + set([int/Decimal]) {'NS': [str(value)]} + set([string]) {'SS': [string]) + set([Binary/bytearray/bytes]) {'BS': [bytes]} + list {'L': list} + dict {'M': dict} + + For types that involve numbers, it is recommended that ``Decimal`` + objects are used to be able to round-trip the Python type. + For types that involve binary, it is recommended that ``Binary`` + objects are used to be able to round-trip the Python type. + + :rtype: dict + :returns: A dictionary that represents a dynamoDB data type. These + dictionaries can be directly passed to botocore methods. + """ + dynamodb_type = self._get_dynamodb_type(value) + serializer = getattr(self, f'_serialize_{dynamodb_type}'.lower()) + return {dynamodb_type: serializer(value)} + + def _get_dynamodb_type(self, value): + dynamodb_type = None + + if self._is_null(value): + dynamodb_type = NULL + + elif self._is_boolean(value): + dynamodb_type = BOOLEAN + + elif self._is_number(value): + dynamodb_type = NUMBER + + elif self._is_string(value): + dynamodb_type = STRING + + elif self._is_binary(value): + dynamodb_type = BINARY + + elif self._is_type_set(value, self._is_number): + dynamodb_type = NUMBER_SET + + elif self._is_type_set(value, self._is_string): + dynamodb_type = STRING_SET + + elif self._is_type_set(value, self._is_binary): + dynamodb_type = BINARY_SET + + elif self._is_map(value): + dynamodb_type = MAP + + elif self._is_listlike(value): + dynamodb_type = LIST + + else: + msg = f'Unsupported type "{type(value)}" for value "{value}"' + raise TypeError(msg) + + return dynamodb_type + + def _is_null(self, value): + if value is None: + return True + return False + + def _is_boolean(self, value): + if isinstance(value, bool): + return True + return False + + def _is_number(self, value): + if isinstance(value, (int, Decimal)): + return True + elif isinstance(value, float): + raise TypeError( + 'Float types are not supported. Use Decimal types instead.' + ) + return False + + def _is_string(self, value): + if isinstance(value, str): + return True + return False + + def _is_binary(self, value): + if isinstance(value, (Binary, bytearray, bytes)): + return True + return False + + def _is_set(self, value): + if isinstance(value, collections_abc.Set): + return True + return False + + def _is_type_set(self, value, type_validator): + if self._is_set(value): + if False not in map(type_validator, value): + return True + return False + + def _is_map(self, value): + if isinstance(value, collections_abc.Mapping): + return True + return False + + def _is_listlike(self, value): + if isinstance(value, (list, tuple)): + return True + return False + + def _serialize_null(self, value): + return True + + def _serialize_bool(self, value): + return value + + def _serialize_n(self, value): + number = str(DYNAMODB_CONTEXT.create_decimal(value)) + if number in ['Infinity', 'NaN']: + raise TypeError('Infinity and NaN not supported') + return number + + def _serialize_s(self, value): + return value + + def _serialize_b(self, value): + if isinstance(value, Binary): + value = value.value + return value + + def _serialize_ss(self, value): + return [self._serialize_s(s) for s in value] + + def _serialize_ns(self, value): + return [self._serialize_n(n) for n in value] + + def _serialize_bs(self, value): + return [self._serialize_b(b) for b in value] + + def _serialize_l(self, value): + return [self.serialize(v) for v in value] + + def _serialize_m(self, value): + return {k: self.serialize(v) for k, v in value.items()} + + +class TypeDeserializer: + """This class deserializes DynamoDB types to Python types.""" + + def deserialize(self, value): + """The method to deserialize the DynamoDB data types. + + :param value: A DynamoDB value to be deserialized to a pythonic value. + Here are the various conversions: + + DynamoDB Python + -------- ------ + {'NULL': True} None + {'BOOL': True/False} True/False + {'N': str(value)} Decimal(str(value)) + {'S': string} string + {'B': bytes} Binary(bytes) + {'NS': [str(value)]} set([Decimal(str(value))]) + {'SS': [string]} set([string]) + {'BS': [bytes]} set([bytes]) + {'L': list} list + {'M': dict} dict + + :returns: The pythonic value of the DynamoDB type. + """ + + if not value: + raise TypeError( + 'Value must be a nonempty dictionary whose key ' + 'is a valid dynamodb type.' + ) + dynamodb_type = list(value.keys())[0] + try: + deserializer = getattr( + self, f'_deserialize_{dynamodb_type}'.lower() + ) + except AttributeError: + raise TypeError(f'Dynamodb type {dynamodb_type} is not supported') + return deserializer(value[dynamodb_type]) + + def _deserialize_null(self, value): + return None + + def _deserialize_bool(self, value): + return value + + def _deserialize_n(self, value): + return DYNAMODB_CONTEXT.create_decimal(value) + + def _deserialize_s(self, value): + return value + + def _deserialize_b(self, value): + return Binary(value) + + def _deserialize_ns(self, value): + return set(map(self._deserialize_n, value)) + + def _deserialize_ss(self, value): + return set(map(self._deserialize_s, value)) + + def _deserialize_bs(self, value): + return set(map(self._deserialize_b, value)) + + def _deserialize_l(self, value): + return [self.deserialize(v) for v in value] + + def _deserialize_m(self, value): + return {k: self.deserialize(v) for k, v in value.items()} diff --git a/venv/lib/python3.12/site-packages/boto3/ec2/__init__.py b/venv/lib/python3.12/site-packages/boto3/ec2/__init__.py new file mode 100644 index 0000000..6001b27 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/ec2/__init__.py @@ -0,0 +1,12 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. diff --git a/venv/lib/python3.12/site-packages/boto3/ec2/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/ec2/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5a8137f Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/ec2/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/ec2/__pycache__/createtags.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/ec2/__pycache__/createtags.cpython-312.pyc new file mode 100644 index 0000000..7924d14 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/ec2/__pycache__/createtags.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/ec2/__pycache__/deletetags.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/ec2/__pycache__/deletetags.cpython-312.pyc new file mode 100644 index 0000000..465d89e Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/ec2/__pycache__/deletetags.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/ec2/createtags.py b/venv/lib/python3.12/site-packages/boto3/ec2/createtags.py new file mode 100644 index 0000000..ec0ff1a --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/ec2/createtags.py @@ -0,0 +1,40 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + + +def inject_create_tags(event_name, class_attributes, **kwargs): + """This injects a custom create_tags method onto the ec2 service resource + + This is needed because the resource model is not able to express + creating multiple tag resources based on the fact you can apply a set + of tags to multiple ec2 resources. + """ + class_attributes['create_tags'] = create_tags + + +def create_tags(self, **kwargs): + # Call the client method + self.meta.client.create_tags(**kwargs) + resources = kwargs.get('Resources', []) + tags = kwargs.get('Tags', []) + tag_resources = [] + + # Generate all of the tag resources that just were created with the + # preceding client call. + for resource in resources: + for tag in tags: + # Add each tag from the tag set for each resource to the list + # that is returned by the method. + tag_resource = self.Tag(resource, tag['Key'], tag['Value']) + tag_resources.append(tag_resource) + return tag_resources diff --git a/venv/lib/python3.12/site-packages/boto3/ec2/deletetags.py b/venv/lib/python3.12/site-packages/boto3/ec2/deletetags.py new file mode 100644 index 0000000..19876d0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/ec2/deletetags.py @@ -0,0 +1,37 @@ +# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from boto3.resources.action import CustomModeledAction + + +def inject_delete_tags(event_emitter, **kwargs): + action_model = { + 'request': { + 'operation': 'DeleteTags', + 'params': [ + { + 'target': 'Resources[0]', + 'source': 'identifier', + 'name': 'Id', + } + ], + } + } + action = CustomModeledAction( + 'delete_tags', action_model, delete_tags, event_emitter + ) + action.inject(**kwargs) + + +def delete_tags(self, **kwargs): + kwargs['Resources'] = [self.id] + return self.meta.client.delete_tags(**kwargs) diff --git a/venv/lib/python3.12/site-packages/boto3/examples/cloudfront.rst b/venv/lib/python3.12/site-packages/boto3/examples/cloudfront.rst new file mode 100644 index 0000000..ddec198 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/examples/cloudfront.rst @@ -0,0 +1,35 @@ +Generate a signed URL for Amazon CloudFront +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example shows how to generate a signed URL for Amazon CloudFront. +Note that you will need the ``cryptography`` `library `__ to follow this example:: + + import datetime + + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes + from cryptography.hazmat.primitives import serialization + from cryptography.hazmat.primitives.asymmetric import padding + from botocore.signers import CloudFrontSigner + + + def rsa_signer(message): + with open('path/to/key.pem', 'rb') as key_file: + private_key = serialization.load_pem_private_key( + key_file.read(), + password=None, + backend=default_backend() + ) + return private_key.sign(message, padding.PKCS1v15(), hashes.SHA1()) + + key_id = 'AKIAIOSFODNN7EXAMPLE' + url = 'http://d2949o5mkkp72v.cloudfront.net/hello.txt' + expire_date = datetime.datetime(2017, 1, 1) + + cloudfront_signer = CloudFrontSigner(key_id, rsa_signer) + + # Create a signed url that will be valid until the specific expiry date + # provided using a canned policy. + signed_url = cloudfront_signer.generate_presigned_url( + url, date_less_than=expire_date) + print(signed_url) diff --git a/venv/lib/python3.12/site-packages/boto3/examples/s3.rst b/venv/lib/python3.12/site-packages/boto3/examples/s3.rst new file mode 100644 index 0000000..ec56238 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/examples/s3.rst @@ -0,0 +1,185 @@ +List objects in an Amazon S3 bucket +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example shows how to use an Amazon S3 bucket resource to list +the objects in the bucket. + +.. code-block:: python + + import boto3 + + s3 = boto3.resource('s3') + bucket = s3.Bucket('amzn-s3-demo-bucket') + for obj in bucket.objects.all(): + print(obj.key) + + +List top-level common prefixes in Amazon S3 bucket +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This example shows how to list all of the top-level common prefixes in an +Amazon S3 bucket: + +.. code-block:: python + + import boto3 + + client = boto3.client('s3') + paginator = client.get_paginator('list_objects') + result = paginator.paginate(Bucket='amzn-s3-demo-bucket', Delimiter='/') + for prefix in result.search('CommonPrefixes'): + print(prefix.get('Prefix')) + + +Restore Glacier objects in an Amazon S3 bucket +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example shows how to initiate restoration of glacier objects in +an Amazon S3 bucket, determine if a restoration is on-going, and determine if a +restoration is finished. + +.. code-block:: python + + import boto3 + + s3 = boto3.resource('s3') + bucket = s3.Bucket('amzn-s3-demo-bucket') + for obj_sum in bucket.objects.all(): + obj = s3.Object(obj_sum.bucket_name, obj_sum.key) + if obj.storage_class == 'GLACIER': + # Try to restore the object if the storage class is glacier and + # the object does not have a completed or ongoing restoration + # request. + if obj.restore is None: + print('Submitting restoration request: %s' % obj.key) + obj.restore_object(RestoreRequest={'Days': 1}) + # Print out objects whose restoration is on-going + elif 'ongoing-request="true"' in obj.restore: + print('Restoration in-progress: %s' % obj.key) + # Print out objects whose restoration is complete + elif 'ongoing-request="false"' in obj.restore: + print('Restoration complete: %s' % obj.key) + + +Uploading/downloading files using SSE KMS +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This example shows how to use SSE-KMS to upload objects using +server side encryption with a key managed by KMS. + +We can either use the default KMS master key, or create a +custom key in AWS and use it to encrypt the object by passing in its +key id. + +With KMS, nothing else needs to be provided for getting the +object; S3 already knows how to decrypt the object. + + +.. code-block:: python + + import boto3 + import os + + BUCKET = 'amzn-s3-demo-bucket' + s3 = boto3.client('s3') + keyid = '' + + print("Uploading S3 object with SSE-KMS") + s3.put_object(Bucket=BUCKET, + Key='encrypt-key', + Body=b'foobar', + ServerSideEncryption='aws:kms', + # Optional: SSEKMSKeyId + SSEKMSKeyId=keyid) + print("Done") + + # Getting the object: + print("Getting S3 object...") + response = s3.get_object(Bucket=BUCKET, + Key='encrypt-key') + print("Done, response body:") + print(response['Body'].read()) + + +Uploading/downloading files using SSE Customer Keys +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This example shows how to use SSE-C to upload objects using +server side encryption with a customer provided key. + +First, we'll need a 32 byte key. For this example, we'll +randomly generate a key but you can use any 32 byte key +you want. Remember, you must the same key to download +the object. If you lose the encryption key, you lose +the object. + +Also note how we don't have to provide the SSECustomerKeyMD5. +Boto3 will automatically compute this value for us. + + +.. code-block:: python + + import boto3 + import os + + BUCKET = 'amzn-s3-demo-bucket' + KEY = os.urandom(32) + s3 = boto3.client('s3') + + print("Uploading S3 object with SSE-C") + s3.put_object(Bucket=BUCKET, + Key='encrypt-key', + Body=b'foobar', + SSECustomerKey=KEY, + SSECustomerAlgorithm='AES256') + print("Done") + + # Getting the object: + print("Getting S3 object...") + # Note how we're using the same ``KEY`` we + # created earlier. + response = s3.get_object(Bucket=BUCKET, + Key='encrypt-key', + SSECustomerKey=KEY, + SSECustomerAlgorithm='AES256') + print("Done, response body:") + print(response['Body'].read()) + + +Downloading a specific version of an S3 object +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This example shows how to download a specific version of an +S3 object. + +.. code-block:: python + + import boto3 + s3 = boto3.client('s3') + + s3.download_file( + "amzn-s3-demo-bucket", "key-name", "tmp.txt", + ExtraArgs={"VersionId": "my-version-id"} + ) + + +Filter objects by last modified time using JMESPath +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This example shows how to filter objects by last modified time +using JMESPath. + +.. code-block:: python + + import boto3 + s3 = boto3.client("s3") + + s3_paginator = s3.get_paginator('list_objects_v2') + s3_iterator = s3_paginator.paginate(Bucket='amzn-s3-demo-bucket') + + filtered_iterator = s3_iterator.search( + "Contents[?to_string(LastModified)>='\"2022-01-05 08:05:37+00:00\"'].Key" + ) + + for key_data in filtered_iterator: + print(key_data) diff --git a/venv/lib/python3.12/site-packages/boto3/exceptions.py b/venv/lib/python3.12/site-packages/boto3/exceptions.py new file mode 100644 index 0000000..1dbe37d --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/exceptions.py @@ -0,0 +1,130 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +# All exceptions in this class should subclass from Boto3Error. +import botocore.exceptions + + +# All exceptions should subclass from Boto3Error in this module. +class Boto3Error(Exception): + """Base class for all Boto3 errors.""" + + +class ResourceLoadException(Boto3Error): + pass + + +# NOTE: This doesn't appear to be used anywhere. +# It's probably safe to remove this. +class NoVersionFound(Boto3Error): + pass + + +# We're subclassing from botocore.exceptions.DataNotFoundError +# to keep backwards compatibility with anyone that was catching +# this low level Botocore error before this exception was +# introduced in boto3. +# Same thing for ResourceNotExistsError below. +class UnknownAPIVersionError( + Boto3Error, botocore.exceptions.DataNotFoundError +): + def __init__(self, service_name, bad_api_version, available_api_versions): + msg = ( + f"The '{service_name}' resource does not support an API version of: {bad_api_version}\n" + f"Valid API versions are: {available_api_versions}" + ) + # Not using super because we don't want the DataNotFoundError + # to be called, it has a different __init__ signature. + Boto3Error.__init__(self, msg) + + +class ResourceNotExistsError( + Boto3Error, botocore.exceptions.DataNotFoundError +): + """Raised when you attempt to create a resource that does not exist.""" + + def __init__(self, service_name, available_services, has_low_level_client): + msg = ( + "The '{}' resource does not exist.\n" + "The available resources are:\n" + " - {}\n".format( + service_name, '\n - '.join(available_services) + ) + ) + if has_low_level_client: + msg = ( + f"{msg}\nConsider using a boto3.client('{service_name}') " + f"instead of a resource for '{service_name}'" + ) + # Not using super because we don't want the DataNotFoundError + # to be called, it has a different __init__ signature. + Boto3Error.__init__(self, msg) + + +class RetriesExceededError(Boto3Error): + def __init__(self, last_exception, msg='Max Retries Exceeded'): + super().__init__(msg) + self.last_exception = last_exception + + +class S3TransferFailedError(Boto3Error): + pass + + +class S3UploadFailedError(Boto3Error): + pass + + +class DynamoDBOperationNotSupportedError(Boto3Error): + """Raised for operations that are not supported for an operand.""" + + def __init__(self, operation, value): + msg = ( + f'{operation} operation cannot be applied to value {value} of type ' + f'{type(value)} directly. Must use AttributeBase object methods ' + f'(i.e. Attr().eq()). to generate ConditionBase instances first.' + ) + Exception.__init__(self, msg) + + +# FIXME: Backward compatibility +DynanmoDBOperationNotSupportedError = DynamoDBOperationNotSupportedError + + +class DynamoDBNeedsConditionError(Boto3Error): + """Raised when input is not a condition""" + + def __init__(self, value): + msg = ( + f'Expecting a ConditionBase object. Got {value} of type {type(value)}. ' + f'Use AttributeBase object methods (i.e. Attr().eq()). to ' + f'generate ConditionBase instances.' + ) + Exception.__init__(self, msg) + + +class DynamoDBNeedsKeyConditionError(Boto3Error): + pass + + +class PythonDeprecationWarning(Warning): + """ + Python version being used is scheduled to become unsupported + in an future release. See warning for specifics. + """ + + pass + + +class InvalidCrtTransferConfigError(Boto3Error): + pass diff --git a/venv/lib/python3.12/site-packages/boto3/resources/__init__.py b/venv/lib/python3.12/site-packages/boto3/resources/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5f8bc13 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/action.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/action.cpython-312.pyc new file mode 100644 index 0000000..40d42ca Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/action.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..285c8f9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/collection.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/collection.cpython-312.pyc new file mode 100644 index 0000000..a370685 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/collection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/factory.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/factory.cpython-312.pyc new file mode 100644 index 0000000..dc1fc50 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/factory.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/model.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/model.cpython-312.pyc new file mode 100644 index 0000000..e7f8f6b Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/params.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/params.cpython-312.pyc new file mode 100644 index 0000000..4173dfd Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/response.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/response.cpython-312.pyc new file mode 100644 index 0000000..2c0c8fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/resources/__pycache__/response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/resources/action.py b/venv/lib/python3.12/site-packages/boto3/resources/action.py new file mode 100644 index 0000000..7c7d839 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/resources/action.py @@ -0,0 +1,257 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import logging + +from botocore import xform_name + +from boto3.docs.docstring import ActionDocstring +from boto3.utils import inject_attribute + +from .model import Action +from .params import create_request_parameters +from .response import RawHandler, ResourceHandler + +logger = logging.getLogger(__name__) + + +class ServiceAction: + """ + A class representing a callable action on a resource, for example + ``sqs.get_queue_by_name(...)`` or ``s3.Bucket('foo').delete()``. + The action may construct parameters from existing resource identifiers + and may return either a raw response or a new resource instance. + + :type action_model: :py:class`~boto3.resources.model.Action` + :param action_model: The action model. + + :type factory: ResourceFactory + :param factory: The factory that created the resource class to which + this action is attached. + + :type service_context: :py:class:`~boto3.utils.ServiceContext` + :param service_context: Context about the AWS service + """ + + def __init__(self, action_model, factory=None, service_context=None): + self._action_model = action_model + + # In the simplest case we just return the response, but if a + # resource is defined, then we must create these before returning. + resource_response_model = action_model.resource + if resource_response_model: + self._response_handler = ResourceHandler( + search_path=resource_response_model.path, + factory=factory, + resource_model=resource_response_model, + service_context=service_context, + operation_name=action_model.request.operation, + ) + else: + self._response_handler = RawHandler(action_model.path) + + def __call__(self, parent, *args, **kwargs): + """ + Perform the action's request operation after building operation + parameters and build any defined resources from the response. + + :type parent: :py:class:`~boto3.resources.base.ServiceResource` + :param parent: The resource instance to which this action is attached. + :rtype: dict or ServiceResource or list(ServiceResource) + :return: The response, either as a raw dict or resource instance(s). + """ + operation_name = xform_name(self._action_model.request.operation) + + # First, build predefined params and then update with the + # user-supplied kwargs, which allows overriding the pre-built + # params if needed. + params = create_request_parameters(parent, self._action_model.request) + params.update(kwargs) + + logger.debug( + 'Calling %s:%s with %r', + parent.meta.service_name, + operation_name, + params, + ) + + response = getattr(parent.meta.client, operation_name)(*args, **params) + + logger.debug('Response: %r', response) + + return self._response_handler(parent, params, response) + + +class BatchAction(ServiceAction): + """ + An action which operates on a batch of items in a collection, typically + a single page of results from the collection's underlying service + operation call. For example, this allows you to delete up to 999 + S3 objects in a single operation rather than calling ``.delete()`` on + each one individually. + + :type action_model: :py:class`~boto3.resources.model.Action` + :param action_model: The action model. + + :type factory: ResourceFactory + :param factory: The factory that created the resource class to which + this action is attached. + + :type service_context: :py:class:`~boto3.utils.ServiceContext` + :param service_context: Context about the AWS service + """ + + def __call__(self, parent, *args, **kwargs): + """ + Perform the batch action's operation on every page of results + from the collection. + + :type parent: + :py:class:`~boto3.resources.collection.ResourceCollection` + :param parent: The collection iterator to which this action + is attached. + :rtype: list(dict) + :return: A list of low-level response dicts from each call. + """ + service_name = None + client = None + responses = [] + operation_name = xform_name(self._action_model.request.operation) + + # Unlike the simple action above, a batch action must operate + # on batches (or pages) of items. So we get each page, construct + # the necessary parameters and call the batch operation. + for page in parent.pages(): + params = {} + for index, resource in enumerate(page): + # There is no public interface to get a service name + # or low-level client from a collection, so we get + # these from the first resource in the collection. + if service_name is None: + service_name = resource.meta.service_name + if client is None: + client = resource.meta.client + + create_request_parameters( + resource, + self._action_model.request, + params=params, + index=index, + ) + + if not params: + # There are no items, no need to make a call. + break + + params.update(kwargs) + + logger.debug( + 'Calling %s:%s with %r', service_name, operation_name, params + ) + + response = getattr(client, operation_name)(*args, **params) + + logger.debug('Response: %r', response) + + responses.append(self._response_handler(parent, params, response)) + + return responses + + +class WaiterAction: + """ + A class representing a callable waiter action on a resource, for example + ``s3.Bucket('foo').wait_until_bucket_exists()``. + The waiter action may construct parameters from existing resource + identifiers. + + :type waiter_model: :py:class`~boto3.resources.model.Waiter` + :param waiter_model: The action waiter. + :type waiter_resource_name: string + :param waiter_resource_name: The name of the waiter action for the + resource. It usually begins with a + ``wait_until_`` + """ + + def __init__(self, waiter_model, waiter_resource_name): + self._waiter_model = waiter_model + self._waiter_resource_name = waiter_resource_name + + def __call__(self, parent, *args, **kwargs): + """ + Perform the wait operation after building operation + parameters. + + :type parent: :py:class:`~boto3.resources.base.ServiceResource` + :param parent: The resource instance to which this action is attached. + """ + client_waiter_name = xform_name(self._waiter_model.waiter_name) + + # First, build predefined params and then update with the + # user-supplied kwargs, which allows overriding the pre-built + # params if needed. + params = create_request_parameters(parent, self._waiter_model) + params.update(kwargs) + + logger.debug( + 'Calling %s:%s with %r', + parent.meta.service_name, + self._waiter_resource_name, + params, + ) + + client = parent.meta.client + waiter = client.get_waiter(client_waiter_name) + response = waiter.wait(**params) + + logger.debug('Response: %r', response) + + +class CustomModeledAction: + """A custom, modeled action to inject into a resource.""" + + def __init__(self, action_name, action_model, function, event_emitter): + """ + :type action_name: str + :param action_name: The name of the action to inject, e.g. + 'delete_tags' + + :type action_model: dict + :param action_model: A JSON definition of the action, as if it were + part of the resource model. + + :type function: function + :param function: The function to perform when the action is called. + The first argument should be 'self', which will be the resource + the function is to be called on. + + :type event_emitter: :py:class:`botocore.hooks.BaseEventHooks` + :param event_emitter: The session event emitter. + """ + self.name = action_name + self.model = action_model + self.function = function + self.emitter = event_emitter + + def inject(self, class_attributes, service_context, event_name, **kwargs): + resource_name = event_name.rsplit(".")[-1] + action = Action(self.name, self.model, {}) + self.function.__name__ = self.name + self.function.__doc__ = ActionDocstring( + resource_name=resource_name, + event_emitter=self.emitter, + action_model=action, + service_model=service_context.service_model, + include_signature=False, + ) + inject_attribute(class_attributes, self.name, self.function) diff --git a/venv/lib/python3.12/site-packages/boto3/resources/base.py b/venv/lib/python3.12/site-packages/boto3/resources/base.py new file mode 100644 index 0000000..81ee220 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/resources/base.py @@ -0,0 +1,149 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import logging + +import boto3 + +logger = logging.getLogger(__name__) + + +class ResourceMeta: + """ + An object containing metadata about a resource. + """ + + def __init__( + self, + service_name, + identifiers=None, + client=None, + data=None, + resource_model=None, + ): + #: (``string``) The service name, e.g. 's3' + self.service_name = service_name + + if identifiers is None: + identifiers = [] + #: (``list``) List of identifier names + self.identifiers = identifiers + + #: (:py:class:`~botocore.client.BaseClient`) Low-level Botocore client + self.client = client + #: (``dict``) Loaded resource data attributes + self.data = data + + # The resource model for that resource + self.resource_model = resource_model + + def __repr__(self): + return f'ResourceMeta(\'{self.service_name}\', identifiers={self.identifiers})' + + def __eq__(self, other): + # Two metas are equal if their components are all equal + if other.__class__.__name__ != self.__class__.__name__: + return False + + return self.__dict__ == other.__dict__ + + def copy(self): + """ + Create a copy of this metadata object. + """ + params = self.__dict__.copy() + service_name = params.pop('service_name') + return ResourceMeta(service_name, **params) + + +class ServiceResource: + """ + A base class for resources. + + :type client: botocore.client + :param client: A low-level Botocore client instance + """ + + meta = None + """ + Stores metadata about this resource instance, such as the + ``service_name``, the low-level ``client`` and any cached ``data`` + from when the instance was hydrated. For example:: + + # Get a low-level client from a resource instance + client = resource.meta.client + response = client.operation(Param='foo') + + # Print the resource instance's service short name + print(resource.meta.service_name) + + See :py:class:`ResourceMeta` for more information. + """ + + def __init__(self, *args, **kwargs): + # Always work on a copy of meta, otherwise we would affect other + # instances of the same subclass. + self.meta = self.meta.copy() + + # Create a default client if none was passed + if kwargs.get('client') is not None: + self.meta.client = kwargs.get('client') + else: + self.meta.client = boto3.client(self.meta.service_name) + + # Allow setting identifiers as positional arguments in the order + # in which they were defined in the ResourceJSON. + for i, value in enumerate(args): + setattr(self, f"_{self.meta.identifiers[i]}", value) + + # Allow setting identifiers via keyword arguments. Here we need + # extra logic to ignore other keyword arguments like ``client``. + for name, value in kwargs.items(): + if name == 'client': + continue + + if name not in self.meta.identifiers: + raise ValueError(f'Unknown keyword argument: {name}') + + setattr(self, f"_{name}", value) + + # Validate that all identifiers have been set. + for identifier in self.meta.identifiers: + if getattr(self, identifier) is None: + raise ValueError(f'Required parameter {identifier} not set') + + def __repr__(self): + identifiers = [ + f'{identifier}={repr(getattr(self, identifier))}' + for identifier in self.meta.identifiers + ] + return f"{self.__class__.__name__}({', '.join(identifiers)})" + + def __eq__(self, other): + # Should be instances of the same resource class + if other.__class__.__name__ != self.__class__.__name__: + return False + + # Each of the identifiers should have the same value in both + # instances, e.g. two buckets need the same name to be equal. + for identifier in self.meta.identifiers: + if getattr(self, identifier) != getattr(other, identifier): + return False + + return True + + def __hash__(self): + identifiers = [] + for identifier in self.meta.identifiers: + identifiers.append(getattr(self, identifier)) + return hash((self.__class__.__name__, tuple(identifiers))) diff --git a/venv/lib/python3.12/site-packages/boto3/resources/collection.py b/venv/lib/python3.12/site-packages/boto3/resources/collection.py new file mode 100644 index 0000000..5d4c9e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/resources/collection.py @@ -0,0 +1,566 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import copy +import logging + +from botocore import xform_name +from botocore.utils import merge_dicts + +from ..docs import docstring +from .action import BatchAction +from .params import create_request_parameters +from .response import ResourceHandler + +logger = logging.getLogger(__name__) + + +class ResourceCollection: + """ + Represents a collection of resources, which can be iterated through, + optionally with filtering. Collections automatically handle pagination + for you. + + See :ref:`guide_collections` for a high-level overview of collections, + including when remote service requests are performed. + + :type model: :py:class:`~boto3.resources.model.Collection` + :param model: Collection model + :type parent: :py:class:`~boto3.resources.base.ServiceResource` + :param parent: The collection's parent resource + :type handler: :py:class:`~boto3.resources.response.ResourceHandler` + :param handler: The resource response handler used to create resource + instances + """ + + def __init__(self, model, parent, handler, **kwargs): + self._model = model + self._parent = parent + self._py_operation_name = xform_name(model.request.operation) + self._handler = handler + self._params = copy.deepcopy(kwargs) + + def __repr__(self): + return '{}({}, {})'.format( + self.__class__.__name__, + self._parent, + f'{self._parent.meta.service_name}.{self._model.resource.type}', + ) + + def __iter__(self): + """ + A generator which yields resource instances after doing the + appropriate service operation calls and handling any pagination + on your behalf. + + Page size, item limit, and filter parameters are applied + if they have previously been set. + + >>> bucket = s3.Bucket('boto3') + >>> for obj in bucket.objects.all(): + ... print(obj.key) + 'key1' + 'key2' + + """ + limit = self._params.get('limit', None) + + count = 0 + for page in self.pages(): + for item in page: + yield item + + # If the limit is set and has been reached, then + # we stop processing items here. + count += 1 + if limit is not None and count >= limit: + return + + def _clone(self, **kwargs): + """ + Create a clone of this collection. This is used by the methods + below to provide a chainable interface that returns copies + rather than the original. This allows things like: + + >>> base = collection.filter(Param1=1) + >>> query1 = base.filter(Param2=2) + >>> query2 = base.filter(Param3=3) + >>> query1.params + {'Param1': 1, 'Param2': 2} + >>> query2.params + {'Param1': 1, 'Param3': 3} + + :rtype: :py:class:`ResourceCollection` + :return: A clone of this resource collection + """ + params = copy.deepcopy(self._params) + merge_dicts(params, kwargs, append_lists=True) + clone = self.__class__( + self._model, self._parent, self._handler, **params + ) + return clone + + def pages(self): + """ + A generator which yields pages of resource instances after + doing the appropriate service operation calls and handling + any pagination on your behalf. Non-paginated calls will + return a single page of items. + + Page size, item limit, and filter parameters are applied + if they have previously been set. + + >>> bucket = s3.Bucket('boto3') + >>> for page in bucket.objects.pages(): + ... for obj in page: + ... print(obj.key) + 'key1' + 'key2' + + :rtype: list(:py:class:`~boto3.resources.base.ServiceResource`) + :return: List of resource instances + """ + client = self._parent.meta.client + cleaned_params = self._params.copy() + limit = cleaned_params.pop('limit', None) + page_size = cleaned_params.pop('page_size', None) + params = create_request_parameters(self._parent, self._model.request) + merge_dicts(params, cleaned_params, append_lists=True) + + # Is this a paginated operation? If so, we need to get an + # iterator for the various pages. If not, then we simply + # call the operation and return the result as a single + # page in a list. For non-paginated results, we just ignore + # the page size parameter. + if client.can_paginate(self._py_operation_name): + logger.debug( + 'Calling paginated %s:%s with %r', + self._parent.meta.service_name, + self._py_operation_name, + params, + ) + paginator = client.get_paginator(self._py_operation_name) + pages = paginator.paginate( + PaginationConfig={'MaxItems': limit, 'PageSize': page_size}, + **params, + ) + else: + logger.debug( + 'Calling %s:%s with %r', + self._parent.meta.service_name, + self._py_operation_name, + params, + ) + pages = [getattr(client, self._py_operation_name)(**params)] + + # Now that we have a page iterator or single page of results + # we start processing and yielding individual items. + count = 0 + for page in pages: + page_items = [] + for item in self._handler(self._parent, params, page): + page_items.append(item) + + # If the limit is set and has been reached, then + # we stop processing items here. + count += 1 + if limit is not None and count >= limit: + break + + yield page_items + + # Stop reading pages if we've reached out limit + if limit is not None and count >= limit: + break + + def all(self): + """ + Get all items from the collection, optionally with a custom + page size and item count limit. + + This method returns an iterable generator which yields + individual resource instances. Example use:: + + # Iterate through items + >>> for queue in sqs.queues.all(): + ... print(queue.url) + 'https://url1' + 'https://url2' + + # Convert to list + >>> queues = list(sqs.queues.all()) + >>> len(queues) + 2 + """ + return self._clone() + + def filter(self, **kwargs): + """ + Get items from the collection, passing keyword arguments along + as parameters to the underlying service operation, which are + typically used to filter the results. + + This method returns an iterable generator which yields + individual resource instances. Example use:: + + # Iterate through items + >>> for queue in sqs.queues.filter(Param='foo'): + ... print(queue.url) + 'https://url1' + 'https://url2' + + # Convert to list + >>> queues = list(sqs.queues.filter(Param='foo')) + >>> len(queues) + 2 + + :rtype: :py:class:`ResourceCollection` + """ + return self._clone(**kwargs) + + def limit(self, count): + """ + Return at most this many resources. + + >>> for bucket in s3.buckets.limit(5): + ... print(bucket.name) + 'bucket1' + 'bucket2' + 'bucket3' + 'bucket4' + 'bucket5' + + :type count: int + :param count: Return no more than this many items + :rtype: :py:class:`ResourceCollection` + """ + return self._clone(limit=count) + + def page_size(self, count): + """ + Fetch at most this many resources per service request. + + >>> for obj in s3.Bucket('boto3').objects.page_size(100): + ... print(obj.key) + + :type count: int + :param count: Fetch this many items per request + :rtype: :py:class:`ResourceCollection` + """ + return self._clone(page_size=count) + + +class CollectionManager: + """ + A collection manager provides access to resource collection instances, + which can be iterated and filtered. The manager exposes some + convenience functions that are also found on resource collections, + such as :py:meth:`~ResourceCollection.all` and + :py:meth:`~ResourceCollection.filter`. + + Get all items:: + + >>> for bucket in s3.buckets.all(): + ... print(bucket.name) + + Get only some items via filtering:: + + >>> for queue in sqs.queues.filter(QueueNamePrefix='AWS'): + ... print(queue.url) + + Get whole pages of items: + + >>> for page in s3.Bucket('boto3').objects.pages(): + ... for obj in page: + ... print(obj.key) + + A collection manager is not iterable. You **must** call one of the + methods that return a :py:class:`ResourceCollection` before trying + to iterate, slice, or convert to a list. + + See the :ref:`guide_collections` guide for a high-level overview + of collections, including when remote service requests are performed. + + :type collection_model: :py:class:`~boto3.resources.model.Collection` + :param model: Collection model + + :type parent: :py:class:`~boto3.resources.base.ServiceResource` + :param parent: The collection's parent resource + + :type factory: :py:class:`~boto3.resources.factory.ResourceFactory` + :param factory: The resource factory to create new resources + + :type service_context: :py:class:`~boto3.utils.ServiceContext` + :param service_context: Context about the AWS service + """ + + # The class to use when creating an iterator + _collection_cls = ResourceCollection + + def __init__(self, collection_model, parent, factory, service_context): + self._model = collection_model + operation_name = self._model.request.operation + self._parent = parent + + search_path = collection_model.resource.path + self._handler = ResourceHandler( + search_path=search_path, + factory=factory, + resource_model=collection_model.resource, + service_context=service_context, + operation_name=operation_name, + ) + + def __repr__(self): + return '{}({}, {})'.format( + self.__class__.__name__, + self._parent, + f'{self._parent.meta.service_name}.{self._model.resource.type}', + ) + + def iterator(self, **kwargs): + """ + Get a resource collection iterator from this manager. + + :rtype: :py:class:`ResourceCollection` + :return: An iterable representing the collection of resources + """ + return self._collection_cls( + self._model, self._parent, self._handler, **kwargs + ) + + # Set up some methods to proxy ResourceCollection methods + def all(self): + return self.iterator() + + all.__doc__ = ResourceCollection.all.__doc__ + + def filter(self, **kwargs): + return self.iterator(**kwargs) + + filter.__doc__ = ResourceCollection.filter.__doc__ + + def limit(self, count): + return self.iterator(limit=count) + + limit.__doc__ = ResourceCollection.limit.__doc__ + + def page_size(self, count): + return self.iterator(page_size=count) + + page_size.__doc__ = ResourceCollection.page_size.__doc__ + + def pages(self): + return self.iterator().pages() + + pages.__doc__ = ResourceCollection.pages.__doc__ + + +class CollectionFactory: + """ + A factory to create new + :py:class:`CollectionManager` and :py:class:`ResourceCollection` + subclasses from a :py:class:`~boto3.resources.model.Collection` + model. These subclasses include methods to perform batch operations. + """ + + def load_from_definition( + self, resource_name, collection_model, service_context, event_emitter + ): + """ + Loads a collection from a model, creating a new + :py:class:`CollectionManager` subclass + with the correct properties and methods, named based on the service + and resource name, e.g. ec2.InstanceCollectionManager. It also + creates a new :py:class:`ResourceCollection` subclass which is used + by the new manager class. + + :type resource_name: string + :param resource_name: Name of the resource to look up. For services, + this should match the ``service_name``. + + :type service_context: :py:class:`~boto3.utils.ServiceContext` + :param service_context: Context about the AWS service + + :type event_emitter: :py:class:`~botocore.hooks.HierarchialEmitter` + :param event_emitter: An event emitter + + :rtype: Subclass of :py:class:`CollectionManager` + :return: The collection class. + """ + attrs = {} + collection_name = collection_model.name + + # Create the batch actions for a collection + self._load_batch_actions( + attrs, + resource_name, + collection_model, + service_context.service_model, + event_emitter, + ) + # Add the documentation to the collection class's methods + self._load_documented_collection_methods( + attrs=attrs, + resource_name=resource_name, + collection_model=collection_model, + service_model=service_context.service_model, + event_emitter=event_emitter, + base_class=ResourceCollection, + ) + + if service_context.service_name == resource_name: + cls_name = ( + f'{service_context.service_name}.{collection_name}Collection' + ) + else: + cls_name = f'{service_context.service_name}.{resource_name}.{collection_name}Collection' + + collection_cls = type(str(cls_name), (ResourceCollection,), attrs) + + # Add the documentation to the collection manager's methods + self._load_documented_collection_methods( + attrs=attrs, + resource_name=resource_name, + collection_model=collection_model, + service_model=service_context.service_model, + event_emitter=event_emitter, + base_class=CollectionManager, + ) + attrs['_collection_cls'] = collection_cls + cls_name += 'Manager' + + return type(str(cls_name), (CollectionManager,), attrs) + + def _load_batch_actions( + self, + attrs, + resource_name, + collection_model, + service_model, + event_emitter, + ): + """ + Batch actions on the collection become methods on both + the collection manager and iterators. + """ + for action_model in collection_model.batch_actions: + snake_cased = xform_name(action_model.name) + attrs[snake_cased] = self._create_batch_action( + resource_name, + snake_cased, + action_model, + collection_model, + service_model, + event_emitter, + ) + + def _load_documented_collection_methods( + factory_self, + attrs, + resource_name, + collection_model, + service_model, + event_emitter, + base_class, + ): + # The base class already has these methods defined. However + # the docstrings are generic and not based for a particular service + # or resource. So we override these methods by proxying to the + # base class's builtin method and adding a docstring + # that pertains to the resource. + + # A collection's all() method. + def all(self): + return base_class.all(self) + + all.__doc__ = docstring.CollectionMethodDocstring( + resource_name=resource_name, + action_name='all', + event_emitter=event_emitter, + collection_model=collection_model, + service_model=service_model, + include_signature=False, + ) + attrs['all'] = all + + # The collection's filter() method. + def filter(self, **kwargs): + return base_class.filter(self, **kwargs) + + filter.__doc__ = docstring.CollectionMethodDocstring( + resource_name=resource_name, + action_name='filter', + event_emitter=event_emitter, + collection_model=collection_model, + service_model=service_model, + include_signature=False, + ) + attrs['filter'] = filter + + # The collection's limit method. + def limit(self, count): + return base_class.limit(self, count) + + limit.__doc__ = docstring.CollectionMethodDocstring( + resource_name=resource_name, + action_name='limit', + event_emitter=event_emitter, + collection_model=collection_model, + service_model=service_model, + include_signature=False, + ) + attrs['limit'] = limit + + # The collection's page_size method. + def page_size(self, count): + return base_class.page_size(self, count) + + page_size.__doc__ = docstring.CollectionMethodDocstring( + resource_name=resource_name, + action_name='page_size', + event_emitter=event_emitter, + collection_model=collection_model, + service_model=service_model, + include_signature=False, + ) + attrs['page_size'] = page_size + + def _create_batch_action( + factory_self, + resource_name, + snake_cased, + action_model, + collection_model, + service_model, + event_emitter, + ): + """ + Creates a new method which makes a batch operation request + to the underlying service API. + """ + action = BatchAction(action_model) + + def batch_action(self, *args, **kwargs): + return action(self, *args, **kwargs) + + batch_action.__name__ = str(snake_cased) + batch_action.__doc__ = docstring.BatchActionDocstring( + resource_name=resource_name, + event_emitter=event_emitter, + batch_action_model=action_model, + service_model=service_model, + collection_model=collection_model, + include_signature=False, + ) + return batch_action diff --git a/venv/lib/python3.12/site-packages/boto3/resources/factory.py b/venv/lib/python3.12/site-packages/boto3/resources/factory.py new file mode 100644 index 0000000..5dab336 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/resources/factory.py @@ -0,0 +1,601 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import logging +from functools import partial + +from ..docs import docstring +from ..exceptions import ResourceLoadException +from .action import ServiceAction, WaiterAction +from .base import ResourceMeta, ServiceResource +from .collection import CollectionFactory +from .model import ResourceModel +from .response import ResourceHandler, build_identifiers + +logger = logging.getLogger(__name__) + + +class ResourceFactory: + """ + A factory to create new :py:class:`~boto3.resources.base.ServiceResource` + classes from a :py:class:`~boto3.resources.model.ResourceModel`. There are + two types of lookups that can be done: one on the service itself (e.g. an + SQS resource) and another on models contained within the service (e.g. an + SQS Queue resource). + """ + + def __init__(self, emitter): + self._collection_factory = CollectionFactory() + self._emitter = emitter + + def load_from_definition( + self, resource_name, single_resource_json_definition, service_context + ): + """ + Loads a resource from a model, creating a new + :py:class:`~boto3.resources.base.ServiceResource` subclass + with the correct properties and methods, named based on the service + and resource name, e.g. EC2.Instance. + + :type resource_name: string + :param resource_name: Name of the resource to look up. For services, + this should match the ``service_name``. + + :type single_resource_json_definition: dict + :param single_resource_json_definition: + The loaded json of a single service resource or resource + definition. + + :type service_context: :py:class:`~boto3.utils.ServiceContext` + :param service_context: Context about the AWS service + + :rtype: Subclass of :py:class:`~boto3.resources.base.ServiceResource` + :return: The service or resource class. + """ + logger.debug( + 'Loading %s:%s', service_context.service_name, resource_name + ) + + # Using the loaded JSON create a ResourceModel object. + resource_model = ResourceModel( + resource_name, + single_resource_json_definition, + service_context.resource_json_definitions, + ) + + # Do some renaming of the shape if there was a naming collision + # that needed to be accounted for. + shape = None + if resource_model.shape: + shape = service_context.service_model.shape_for( + resource_model.shape + ) + resource_model.load_rename_map(shape) + + # Set some basic info + meta = ResourceMeta( + service_context.service_name, resource_model=resource_model + ) + attrs = { + 'meta': meta, + } + + # Create and load all of attributes of the resource class based + # on the models. + + # Identifiers + self._load_identifiers( + attrs=attrs, + meta=meta, + resource_name=resource_name, + resource_model=resource_model, + ) + + # Load/Reload actions + self._load_actions( + attrs=attrs, + resource_name=resource_name, + resource_model=resource_model, + service_context=service_context, + ) + + # Attributes that get auto-loaded + self._load_attributes( + attrs=attrs, + meta=meta, + resource_name=resource_name, + resource_model=resource_model, + service_context=service_context, + ) + + # Collections and their corresponding methods + self._load_collections( + attrs=attrs, + resource_model=resource_model, + service_context=service_context, + ) + + # References and Subresources + self._load_has_relations( + attrs=attrs, + resource_name=resource_name, + resource_model=resource_model, + service_context=service_context, + ) + + # Waiter resource actions + self._load_waiters( + attrs=attrs, + resource_name=resource_name, + resource_model=resource_model, + service_context=service_context, + ) + + # Create the name based on the requested service and resource + cls_name = resource_name + if service_context.service_name == resource_name: + cls_name = 'ServiceResource' + cls_name = f"{service_context.service_name}.{cls_name}" + + base_classes = [ServiceResource] + if self._emitter is not None: + self._emitter.emit( + f'creating-resource-class.{cls_name}', + class_attributes=attrs, + base_classes=base_classes, + service_context=service_context, + ) + return type(str(cls_name), tuple(base_classes), attrs) + + def _load_identifiers(self, attrs, meta, resource_model, resource_name): + """ + Populate required identifiers. These are arguments without which + the resource cannot be used. Identifiers become arguments for + operations on the resource. + """ + for identifier in resource_model.identifiers: + meta.identifiers.append(identifier.name) + attrs[identifier.name] = self._create_identifier( + identifier, resource_name + ) + + def _load_actions( + self, attrs, resource_name, resource_model, service_context + ): + """ + Actions on the resource become methods, with the ``load`` method + being a special case which sets internal data for attributes, and + ``reload`` is an alias for ``load``. + """ + if resource_model.load: + attrs['load'] = self._create_action( + action_model=resource_model.load, + resource_name=resource_name, + service_context=service_context, + is_load=True, + ) + attrs['reload'] = attrs['load'] + + for action in resource_model.actions: + attrs[action.name] = self._create_action( + action_model=action, + resource_name=resource_name, + service_context=service_context, + ) + + def _load_attributes( + self, attrs, meta, resource_name, resource_model, service_context + ): + """ + Load resource attributes based on the resource shape. The shape + name is referenced in the resource JSON, but the shape itself + is defined in the Botocore service JSON, hence the need for + access to the ``service_model``. + """ + if not resource_model.shape: + return + + shape = service_context.service_model.shape_for(resource_model.shape) + + identifiers = { + i.member_name: i + for i in resource_model.identifiers + if i.member_name + } + attributes = resource_model.get_attributes(shape) + for name, (orig_name, member) in attributes.items(): + if name in identifiers: + prop = self._create_identifier_alias( + resource_name=resource_name, + identifier=identifiers[name], + member_model=member, + service_context=service_context, + ) + else: + prop = self._create_autoload_property( + resource_name=resource_name, + name=orig_name, + snake_cased=name, + member_model=member, + service_context=service_context, + ) + attrs[name] = prop + + def _load_collections(self, attrs, resource_model, service_context): + """ + Load resource collections from the model. Each collection becomes + a :py:class:`~boto3.resources.collection.CollectionManager` instance + on the resource instance, which allows you to iterate and filter + through the collection's items. + """ + for collection_model in resource_model.collections: + attrs[collection_model.name] = self._create_collection( + resource_name=resource_model.name, + collection_model=collection_model, + service_context=service_context, + ) + + def _load_has_relations( + self, attrs, resource_name, resource_model, service_context + ): + """ + Load related resources, which are defined via a ``has`` + relationship but conceptually come in two forms: + + 1. A reference, which is a related resource instance and can be + ``None``, such as an EC2 instance's ``vpc``. + 2. A subresource, which is a resource constructor that will always + return a resource instance which shares identifiers/data with + this resource, such as ``s3.Bucket('name').Object('key')``. + """ + for reference in resource_model.references: + # This is a dangling reference, i.e. we have all + # the data we need to create the resource, so + # this instance becomes an attribute on the class. + attrs[reference.name] = self._create_reference( + reference_model=reference, + resource_name=resource_name, + service_context=service_context, + ) + + for subresource in resource_model.subresources: + # This is a sub-resource class you can create + # by passing in an identifier, e.g. s3.Bucket(name). + attrs[subresource.name] = self._create_class_partial( + subresource_model=subresource, + resource_name=resource_name, + service_context=service_context, + ) + + self._create_available_subresources_command( + attrs, resource_model.subresources + ) + + def _create_available_subresources_command(self, attrs, subresources): + _subresources = [subresource.name for subresource in subresources] + _subresources = sorted(_subresources) + + def get_available_subresources(factory_self): + """ + Returns a list of all the available sub-resources for this + Resource. + + :returns: A list containing the name of each sub-resource for this + resource + :rtype: list of str + """ + return _subresources + + attrs['get_available_subresources'] = get_available_subresources + + def _load_waiters( + self, attrs, resource_name, resource_model, service_context + ): + """ + Load resource waiters from the model. Each waiter allows you to + wait until a resource reaches a specific state by polling the state + of the resource. + """ + for waiter in resource_model.waiters: + attrs[waiter.name] = self._create_waiter( + resource_waiter_model=waiter, + resource_name=resource_name, + service_context=service_context, + ) + + def _create_identifier(factory_self, identifier, resource_name): + """ + Creates a read-only property for identifier attributes. + """ + + def get_identifier(self): + # The default value is set to ``None`` instead of + # raising an AttributeError because when resources are + # instantiated a check is made such that none of the + # identifiers have a value ``None``. If any are ``None``, + # a more informative user error than a generic AttributeError + # is raised. + return getattr(self, f"_{identifier.name}", None) + + get_identifier.__name__ = str(identifier.name) + get_identifier.__doc__ = docstring.IdentifierDocstring( + resource_name=resource_name, + identifier_model=identifier, + include_signature=False, + ) + + return property(get_identifier) + + def _create_identifier_alias( + factory_self, resource_name, identifier, member_model, service_context + ): + """ + Creates a read-only property that aliases an identifier. + """ + + def get_identifier(self): + return getattr(self, f"_{identifier.name}", None) + + get_identifier.__name__ = str(identifier.member_name) + get_identifier.__doc__ = docstring.AttributeDocstring( + service_name=service_context.service_name, + resource_name=resource_name, + attr_name=identifier.member_name, + event_emitter=factory_self._emitter, + attr_model=member_model, + include_signature=False, + ) + + return property(get_identifier) + + def _create_autoload_property( + factory_self, + resource_name, + name, + snake_cased, + member_model, + service_context, + ): + """ + Creates a new property on the resource to lazy-load its value + via the resource's ``load`` method (if it exists). + """ + + # The property loader will check to see if this resource has already + # been loaded and return the cached value if possible. If not, then + # it first checks to see if it CAN be loaded (raise if not), then + # calls the load before returning the value. + def property_loader(self): + if self.meta.data is None: + if hasattr(self, 'load'): + self.load() + else: + raise ResourceLoadException( + f'{self.__class__.__name__} has no load method' + ) + + return self.meta.data.get(name) + + property_loader.__name__ = str(snake_cased) + property_loader.__doc__ = docstring.AttributeDocstring( + service_name=service_context.service_name, + resource_name=resource_name, + attr_name=snake_cased, + event_emitter=factory_self._emitter, + attr_model=member_model, + include_signature=False, + ) + + return property(property_loader) + + def _create_waiter( + factory_self, resource_waiter_model, resource_name, service_context + ): + """ + Creates a new wait method for each resource where both a waiter and + resource model is defined. + """ + waiter = WaiterAction( + resource_waiter_model, + waiter_resource_name=resource_waiter_model.name, + ) + + def do_waiter(self, *args, **kwargs): + waiter(self, *args, **kwargs) + + do_waiter.__name__ = str(resource_waiter_model.name) + do_waiter.__doc__ = docstring.ResourceWaiterDocstring( + resource_name=resource_name, + event_emitter=factory_self._emitter, + service_model=service_context.service_model, + resource_waiter_model=resource_waiter_model, + service_waiter_model=service_context.service_waiter_model, + include_signature=False, + ) + return do_waiter + + def _create_collection( + factory_self, resource_name, collection_model, service_context + ): + """ + Creates a new property on the resource to lazy-load a collection. + """ + cls = factory_self._collection_factory.load_from_definition( + resource_name=resource_name, + collection_model=collection_model, + service_context=service_context, + event_emitter=factory_self._emitter, + ) + + def get_collection(self): + return cls( + collection_model=collection_model, + parent=self, + factory=factory_self, + service_context=service_context, + ) + + get_collection.__name__ = str(collection_model.name) + get_collection.__doc__ = docstring.CollectionDocstring( + collection_model=collection_model, include_signature=False + ) + return property(get_collection) + + def _create_reference( + factory_self, reference_model, resource_name, service_context + ): + """ + Creates a new property on the resource to lazy-load a reference. + """ + # References are essentially an action with no request + # or response, so we can re-use the response handlers to + # build up resources from identifiers and data members. + handler = ResourceHandler( + search_path=reference_model.resource.path, + factory=factory_self, + resource_model=reference_model.resource, + service_context=service_context, + ) + + # Are there any identifiers that need access to data members? + # This is important when building the resource below since + # it requires the data to be loaded. + needs_data = any( + i.source == 'data' for i in reference_model.resource.identifiers + ) + + def get_reference(self): + # We need to lazy-evaluate the reference to handle circular + # references between resources. We do this by loading the class + # when first accessed. + # This is using a *response handler* so we need to make sure + # our data is loaded (if possible) and pass that data into + # the handler as if it were a response. This allows references + # to have their data loaded properly. + if needs_data and self.meta.data is None and hasattr(self, 'load'): + self.load() + return handler(self, {}, self.meta.data) + + get_reference.__name__ = str(reference_model.name) + get_reference.__doc__ = docstring.ReferenceDocstring( + reference_model=reference_model, include_signature=False + ) + return property(get_reference) + + def _create_class_partial( + factory_self, subresource_model, resource_name, service_context + ): + """ + Creates a new method which acts as a functools.partial, passing + along the instance's low-level `client` to the new resource + class' constructor. + """ + name = subresource_model.resource.type + + def create_resource(self, *args, **kwargs): + # We need a new method here because we want access to the + # instance's client. + positional_args = [] + + # We lazy-load the class to handle circular references. + json_def = service_context.resource_json_definitions.get(name, {}) + resource_cls = factory_self.load_from_definition( + resource_name=name, + single_resource_json_definition=json_def, + service_context=service_context, + ) + + # Assumes that identifiers are in order, which lets you do + # e.g. ``sqs.Queue('foo').Message('bar')`` to create a new message + # linked with the ``foo`` queue and which has a ``bar`` receipt + # handle. If we did kwargs here then future positional arguments + # would lead to failure. + identifiers = subresource_model.resource.identifiers + if identifiers is not None: + for identifier, value in build_identifiers(identifiers, self): + positional_args.append(value) + + return partial( + resource_cls, *positional_args, client=self.meta.client + )(*args, **kwargs) + + create_resource.__name__ = str(name) + create_resource.__doc__ = docstring.SubResourceDocstring( + resource_name=resource_name, + sub_resource_model=subresource_model, + service_model=service_context.service_model, + include_signature=False, + ) + return create_resource + + def _create_action( + factory_self, + action_model, + resource_name, + service_context, + is_load=False, + ): + """ + Creates a new method which makes a request to the underlying + AWS service. + """ + # Create the action in in this closure but before the ``do_action`` + # method below is invoked, which allows instances of the resource + # to share the ServiceAction instance. + action = ServiceAction( + action_model, factory=factory_self, service_context=service_context + ) + + # A resource's ``load`` method is special because it sets + # values on the resource instead of returning the response. + if is_load: + # We need a new method here because we want access to the + # instance via ``self``. + def do_action(self, *args, **kwargs): + response = action(self, *args, **kwargs) + self.meta.data = response + + # Create the docstring for the load/reload methods. + lazy_docstring = docstring.LoadReloadDocstring( + action_name=action_model.name, + resource_name=resource_name, + event_emitter=factory_self._emitter, + load_model=action_model, + service_model=service_context.service_model, + include_signature=False, + ) + else: + # We need a new method here because we want access to the + # instance via ``self``. + def do_action(self, *args, **kwargs): + response = action(self, *args, **kwargs) + + if hasattr(self, 'load'): + # Clear cached data. It will be reloaded the next + # time that an attribute is accessed. + # TODO: Make this configurable in the future? + self.meta.data = None + + return response + + lazy_docstring = docstring.ActionDocstring( + resource_name=resource_name, + event_emitter=factory_self._emitter, + action_model=action_model, + service_model=service_context.service_model, + include_signature=False, + ) + + do_action.__name__ = str(action_model.name) + do_action.__doc__ = lazy_docstring + return do_action diff --git a/venv/lib/python3.12/site-packages/boto3/resources/model.py b/venv/lib/python3.12/site-packages/boto3/resources/model.py new file mode 100644 index 0000000..6b64996 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/resources/model.py @@ -0,0 +1,630 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +""" +The models defined in this file represent the resource JSON description +format and provide a layer of abstraction from the raw JSON. The advantages +of this are: + +* Pythonic interface (e.g. ``action.request.operation``) +* Consumers need not change for minor JSON changes (e.g. renamed field) + +These models are used both by the resource factory to generate resource +classes as well as by the documentation generator. +""" + +import logging + +from botocore import xform_name + +logger = logging.getLogger(__name__) + + +class Identifier: + """ + A resource identifier, given by its name. + + :type name: string + :param name: The name of the identifier + """ + + def __init__(self, name, member_name=None): + #: (``string``) The name of the identifier + self.name = name + self.member_name = member_name + + +class Action: + """ + A service operation action. + + :type name: string + :param name: The name of the action + :type definition: dict + :param definition: The JSON definition + :type resource_defs: dict + :param resource_defs: All resources defined in the service + """ + + def __init__(self, name, definition, resource_defs): + self._definition = definition + + #: (``string``) The name of the action + self.name = name + #: (:py:class:`Request`) This action's request or ``None`` + self.request = None + if 'request' in definition: + self.request = Request(definition.get('request', {})) + #: (:py:class:`ResponseResource`) This action's resource or ``None`` + self.resource = None + if 'resource' in definition: + self.resource = ResponseResource( + definition.get('resource', {}), resource_defs + ) + #: (``string``) The JMESPath search path or ``None`` + self.path = definition.get('path') + + +class DefinitionWithParams: + """ + An item which has parameters exposed via the ``params`` property. + A request has an operation and parameters, while a waiter has + a name, a low-level waiter name and parameters. + + :type definition: dict + :param definition: The JSON definition + """ + + def __init__(self, definition): + self._definition = definition + + @property + def params(self): + """ + Get a list of auto-filled parameters for this request. + + :type: list(:py:class:`Parameter`) + """ + params = [] + + for item in self._definition.get('params', []): + params.append(Parameter(**item)) + + return params + + +class Parameter: + """ + An auto-filled parameter which has a source and target. For example, + the ``QueueUrl`` may be auto-filled from a resource's ``url`` identifier + when making calls to ``queue.receive_messages``. + + :type target: string + :param target: The destination parameter name, e.g. ``QueueUrl`` + :type source_type: string + :param source_type: Where the source is defined. + :type source: string + :param source: The source name, e.g. ``Url`` + """ + + def __init__( + self, target, source, name=None, path=None, value=None, **kwargs + ): + #: (``string``) The destination parameter name + self.target = target + #: (``string``) Where the source is defined + self.source = source + #: (``string``) The name of the source, if given + self.name = name + #: (``string``) The JMESPath query of the source + self.path = path + #: (``string|int|float|bool``) The source constant value + self.value = value + + # Complain if we encounter any unknown values. + if kwargs: + logger.warning('Unknown parameter options found: %s', kwargs) + + +class Request(DefinitionWithParams): + """ + A service operation action request. + + :type definition: dict + :param definition: The JSON definition + """ + + def __init__(self, definition): + super().__init__(definition) + + #: (``string``) The name of the low-level service operation + self.operation = definition.get('operation') + + +class Waiter(DefinitionWithParams): + """ + An event waiter specification. + + :type name: string + :param name: Name of the waiter + :type definition: dict + :param definition: The JSON definition + """ + + PREFIX = 'WaitUntil' + + def __init__(self, name, definition): + super().__init__(definition) + + #: (``string``) The name of this waiter + self.name = name + + #: (``string``) The name of the underlying event waiter + self.waiter_name = definition.get('waiterName') + + +class ResponseResource: + """ + A resource response to create after performing an action. + + :type definition: dict + :param definition: The JSON definition + :type resource_defs: dict + :param resource_defs: All resources defined in the service + """ + + def __init__(self, definition, resource_defs): + self._definition = definition + self._resource_defs = resource_defs + + #: (``string``) The name of the response resource type + self.type = definition.get('type') + + #: (``string``) The JMESPath search query or ``None`` + self.path = definition.get('path') + + @property + def identifiers(self): + """ + A list of resource identifiers. + + :type: list(:py:class:`Identifier`) + """ + identifiers = [] + + for item in self._definition.get('identifiers', []): + identifiers.append(Parameter(**item)) + + return identifiers + + @property + def model(self): + """ + Get the resource model for the response resource. + + :type: :py:class:`ResourceModel` + """ + return ResourceModel( + self.type, self._resource_defs[self.type], self._resource_defs + ) + + +class Collection(Action): + """ + A group of resources. See :py:class:`Action`. + + :type name: string + :param name: The name of the collection + :type definition: dict + :param definition: The JSON definition + :type resource_defs: dict + :param resource_defs: All resources defined in the service + """ + + @property + def batch_actions(self): + """ + Get a list of batch actions supported by the resource type + contained in this action. This is a shortcut for accessing + the same information through the resource model. + + :rtype: list(:py:class:`Action`) + """ + return self.resource.model.batch_actions + + +class ResourceModel: + """ + A model representing a resource, defined via a JSON description + format. A resource has identifiers, attributes, actions, + sub-resources, references and collections. For more information + on resources, see :ref:`guide_resources`. + + :type name: string + :param name: The name of this resource, e.g. ``sqs`` or ``Queue`` + :type definition: dict + :param definition: The JSON definition + :type resource_defs: dict + :param resource_defs: All resources defined in the service + """ + + def __init__(self, name, definition, resource_defs): + self._definition = definition + self._resource_defs = resource_defs + self._renamed = {} + + #: (``string``) The name of this resource + self.name = name + #: (``string``) The service shape name for this resource or ``None`` + self.shape = definition.get('shape') + + def load_rename_map(self, shape=None): + """ + Load a name translation map given a shape. This will set + up renamed values for any collisions, e.g. if the shape, + an action, and a subresource all are all named ``foo`` + then the resource will have an action ``foo``, a subresource + named ``Foo`` and a property named ``foo_attribute``. + This is the order of precedence, from most important to + least important: + + * Load action (resource.load) + * Identifiers + * Actions + * Subresources + * References + * Collections + * Waiters + * Attributes (shape members) + + Batch actions are only exposed on collections, so do not + get modified here. Subresources use upper camel casing, so + are unlikely to collide with anything but other subresources. + + Creates a structure like this:: + + renames = { + ('action', 'id'): 'id_action', + ('collection', 'id'): 'id_collection', + ('attribute', 'id'): 'id_attribute' + } + + # Get the final name for an action named 'id' + name = renames.get(('action', 'id'), 'id') + + :type shape: botocore.model.Shape + :param shape: The underlying shape for this resource. + """ + # Meta is a reserved name for resources + names = {'meta'} + self._renamed = {} + + if self._definition.get('load'): + names.add('load') + + for item in self._definition.get('identifiers', []): + self._load_name_with_category(names, item['name'], 'identifier') + + for name in self._definition.get('actions', {}): + self._load_name_with_category(names, name, 'action') + + for name, ref in self._get_has_definition().items(): + # Subresources require no data members, just typically + # identifiers and user input. + data_required = False + for identifier in ref['resource']['identifiers']: + if identifier['source'] == 'data': + data_required = True + break + + if not data_required: + self._load_name_with_category( + names, name, 'subresource', snake_case=False + ) + else: + self._load_name_with_category(names, name, 'reference') + + for name in self._definition.get('hasMany', {}): + self._load_name_with_category(names, name, 'collection') + + for name in self._definition.get('waiters', {}): + self._load_name_with_category( + names, Waiter.PREFIX + name, 'waiter' + ) + + if shape is not None: + for name in shape.members.keys(): + self._load_name_with_category(names, name, 'attribute') + + def _load_name_with_category(self, names, name, category, snake_case=True): + """ + Load a name with a given category, possibly renaming it + if that name is already in use. The name will be stored + in ``names`` and possibly be set up in ``self._renamed``. + + :type names: set + :param names: Existing names (Python attributes, properties, or + methods) on the resource. + :type name: string + :param name: The original name of the value. + :type category: string + :param category: The value type, such as 'identifier' or 'action' + :type snake_case: bool + :param snake_case: True (default) if the name should be snake cased. + """ + if snake_case: + name = xform_name(name) + + if name in names: + logger.debug('Renaming %s %s %s', self.name, category, name) + self._renamed[(category, name)] = f"{name}_{category}" + name += f"_{category}" + + if name in names: + # This isn't good, let's raise instead of trying to keep + # renaming this value. + raise ValueError( + f'Problem renaming {self.name} {category} to {name}!' + ) + + names.add(name) + + def _get_name(self, category, name, snake_case=True): + """ + Get a possibly renamed value given a category and name. This + uses the rename map set up in ``load_rename_map``, so that + method must be called once first. + + :type category: string + :param category: The value type, such as 'identifier' or 'action' + :type name: string + :param name: The original name of the value + :type snake_case: bool + :param snake_case: True (default) if the name should be snake cased. + :rtype: string + :return: Either the renamed value if it is set, otherwise the + original name. + """ + if snake_case: + name = xform_name(name) + + return self._renamed.get((category, name), name) + + def get_attributes(self, shape): + """ + Get a dictionary of attribute names to original name and shape + models that represent the attributes of this resource. Looks + like the following: + + { + 'some_name': ('SomeName', ) + } + + :type shape: botocore.model.Shape + :param shape: The underlying shape for this resource. + :rtype: dict + :return: Mapping of resource attributes. + """ + attributes = {} + identifier_names = [i.name for i in self.identifiers] + + for name, member in shape.members.items(): + snake_cased = xform_name(name) + if snake_cased in identifier_names: + # Skip identifiers, these are set through other means + continue + snake_cased = self._get_name( + 'attribute', snake_cased, snake_case=False + ) + attributes[snake_cased] = (name, member) + + return attributes + + @property + def identifiers(self): + """ + Get a list of resource identifiers. + + :type: list(:py:class:`Identifier`) + """ + identifiers = [] + + for item in self._definition.get('identifiers', []): + name = self._get_name('identifier', item['name']) + member_name = item.get('memberName', None) + if member_name: + member_name = self._get_name('attribute', member_name) + identifiers.append(Identifier(name, member_name)) + + return identifiers + + @property + def load(self): + """ + Get the load action for this resource, if it is defined. + + :type: :py:class:`Action` or ``None`` + """ + action = self._definition.get('load') + + if action is not None: + action = Action('load', action, self._resource_defs) + + return action + + @property + def actions(self): + """ + Get a list of actions for this resource. + + :type: list(:py:class:`Action`) + """ + actions = [] + + for name, item in self._definition.get('actions', {}).items(): + name = self._get_name('action', name) + actions.append(Action(name, item, self._resource_defs)) + + return actions + + @property + def batch_actions(self): + """ + Get a list of batch actions for this resource. + + :type: list(:py:class:`Action`) + """ + actions = [] + + for name, item in self._definition.get('batchActions', {}).items(): + name = self._get_name('batch_action', name) + actions.append(Action(name, item, self._resource_defs)) + + return actions + + def _get_has_definition(self): + """ + Get a ``has`` relationship definition from a model, where the + service resource model is treated special in that it contains + a relationship to every resource defined for the service. This + allows things like ``s3.Object('bucket-name', 'key')`` to + work even though the JSON doesn't define it explicitly. + + :rtype: dict + :return: Mapping of names to subresource and reference + definitions. + """ + if self.name not in self._resource_defs: + # This is the service resource, so let us expose all of + # the defined resources as subresources. + definition = {} + + for name, resource_def in self._resource_defs.items(): + # It's possible for the service to have renamed a + # resource or to have defined multiple names that + # point to the same resource type, so we need to + # take that into account. + found = False + has_items = self._definition.get('has', {}).items() + for has_name, has_def in has_items: + if has_def.get('resource', {}).get('type') == name: + definition[has_name] = has_def + found = True + + if not found: + # Create a relationship definition and attach it + # to the model, such that all identifiers must be + # supplied by the user. It will look something like: + # + # { + # 'resource': { + # 'type': 'ResourceName', + # 'identifiers': [ + # {'target': 'Name1', 'source': 'input'}, + # {'target': 'Name2', 'source': 'input'}, + # ... + # ] + # } + # } + # + fake_has = {'resource': {'type': name, 'identifiers': []}} + + for identifier in resource_def.get('identifiers', []): + fake_has['resource']['identifiers'].append( + {'target': identifier['name'], 'source': 'input'} + ) + + definition[name] = fake_has + else: + definition = self._definition.get('has', {}) + + return definition + + def _get_related_resources(self, subresources): + """ + Get a list of sub-resources or references. + + :type subresources: bool + :param subresources: ``True`` to get sub-resources, ``False`` to + get references. + :rtype: list(:py:class:`Action`) + """ + resources = [] + + for name, definition in self._get_has_definition().items(): + if subresources: + name = self._get_name('subresource', name, snake_case=False) + else: + name = self._get_name('reference', name) + action = Action(name, definition, self._resource_defs) + + data_required = False + for identifier in action.resource.identifiers: + if identifier.source == 'data': + data_required = True + break + + if subresources and not data_required: + resources.append(action) + elif not subresources and data_required: + resources.append(action) + + return resources + + @property + def subresources(self): + """ + Get a list of sub-resources. + + :type: list(:py:class:`Action`) + """ + return self._get_related_resources(True) + + @property + def references(self): + """ + Get a list of reference resources. + + :type: list(:py:class:`Action`) + """ + return self._get_related_resources(False) + + @property + def collections(self): + """ + Get a list of collections for this resource. + + :type: list(:py:class:`Collection`) + """ + collections = [] + + for name, item in self._definition.get('hasMany', {}).items(): + name = self._get_name('collection', name) + collections.append(Collection(name, item, self._resource_defs)) + + return collections + + @property + def waiters(self): + """ + Get a list of waiters for this resource. + + :type: list(:py:class:`Waiter`) + """ + waiters = [] + + for name, item in self._definition.get('waiters', {}).items(): + name = self._get_name('waiter', Waiter.PREFIX + name) + waiters.append(Waiter(name, item)) + + return waiters diff --git a/venv/lib/python3.12/site-packages/boto3/resources/params.py b/venv/lib/python3.12/site-packages/boto3/resources/params.py new file mode 100644 index 0000000..725256f --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/resources/params.py @@ -0,0 +1,167 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import re + +import jmespath +from botocore import xform_name + +from ..exceptions import ResourceLoadException + +INDEX_RE = re.compile(r'\[(.*)\]$') + + +def get_data_member(parent, path): + """ + Get a data member from a parent using a JMESPath search query, + loading the parent if required. If the parent cannot be loaded + and no data is present then an exception is raised. + + :type parent: ServiceResource + :param parent: The resource instance to which contains data we + are interested in. + :type path: string + :param path: The JMESPath expression to query + :raises ResourceLoadException: When no data is present and the + resource cannot be loaded. + :returns: The queried data or ``None``. + """ + # Ensure the parent has its data loaded, if possible. + if parent.meta.data is None: + if hasattr(parent, 'load'): + parent.load() + else: + raise ResourceLoadException( + f'{parent.__class__.__name__} has no load method!' + ) + + return jmespath.search(path, parent.meta.data) + + +def create_request_parameters(parent, request_model, params=None, index=None): + """ + Handle request parameters that can be filled in from identifiers, + resource data members or constants. + + By passing ``params``, you can invoke this method multiple times and + build up a parameter dict over time, which is particularly useful + for reverse JMESPath expressions that append to lists. + + :type parent: ServiceResource + :param parent: The resource instance to which this action is attached. + :type request_model: :py:class:`~boto3.resources.model.Request` + :param request_model: The action request model. + :type params: dict + :param params: If set, then add to this existing dict. It is both + edited in-place and returned. + :type index: int + :param index: The position of an item within a list + :rtype: dict + :return: Pre-filled parameters to be sent to the request operation. + """ + if params is None: + params = {} + + for param in request_model.params: + source = param.source + target = param.target + + if source == 'identifier': + # Resource identifier, e.g. queue.url + value = getattr(parent, xform_name(param.name)) + elif source == 'data': + # If this is a data member then it may incur a load + # action before returning the value. + value = get_data_member(parent, param.path) + elif source in ['string', 'integer', 'boolean']: + # These are hard-coded values in the definition + value = param.value + elif source == 'input': + # This is provided by the user, so ignore it here + continue + else: + raise NotImplementedError(f'Unsupported source type: {source}') + + build_param_structure(params, target, value, index) + + return params + + +def build_param_structure(params, target, value, index=None): + """ + This method provides a basic reverse JMESPath implementation that + lets you go from a JMESPath-like string to a possibly deeply nested + object. The ``params`` are mutated in-place, so subsequent calls + can modify the same element by its index. + + >>> build_param_structure(params, 'test[0]', 1) + >>> print(params) + {'test': [1]} + + >>> build_param_structure(params, 'foo.bar[0].baz', 'hello world') + >>> print(params) + {'test': [1], 'foo': {'bar': [{'baz': 'hello, world'}]}} + + """ + pos = params + parts = target.split('.') + + # First, split into parts like 'foo', 'bar[0]', 'baz' and process + # each piece. It can either be a list or a dict, depending on if + # an index like `[0]` is present. We detect this via a regular + # expression, and keep track of where we are in params via the + # pos variable, walking down to the last item. Once there, we + # set the value. + for i, part in enumerate(parts): + # Is it indexing an array? + result = INDEX_RE.search(part) + if result: + if result.group(1): + if result.group(1) == '*': + part = part[:-3] + else: + # We have an explicit index + index = int(result.group(1)) + part = part[: -len(f"{index}[]")] + else: + # Index will be set after we know the proper part + # name and that it's a list instance. + index = None + part = part[:-2] + + if part not in pos or not isinstance(pos[part], list): + pos[part] = [] + + # This means we should append, e.g. 'foo[]' + if index is None: + index = len(pos[part]) + + while len(pos[part]) <= index: + # Assume it's a dict until we set the final value below + pos[part].append({}) + + # Last item? Set the value, otherwise set the new position + if i == len(parts) - 1: + pos[part][index] = value + else: + # The new pos is the *item* in the array, not the array! + pos = pos[part][index] + else: + if part not in pos: + pos[part] = {} + + # Last item? Set the value, otherwise set the new position + if i == len(parts) - 1: + pos[part] = value + else: + pos = pos[part] diff --git a/venv/lib/python3.12/site-packages/boto3/resources/response.py b/venv/lib/python3.12/site-packages/boto3/resources/response.py new file mode 100644 index 0000000..a27190a --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/resources/response.py @@ -0,0 +1,316 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import jmespath +from botocore import xform_name + +from .params import get_data_member + + +def all_not_none(iterable): + """ + Return True if all elements of the iterable are not None (or if the + iterable is empty). This is like the built-in ``all``, except checks + against None, so 0 and False are allowable values. + """ + for element in iterable: + if element is None: + return False + return True + + +def build_identifiers(identifiers, parent, params=None, raw_response=None): + """ + Builds a mapping of identifier names to values based on the + identifier source location, type, and target. Identifier + values may be scalars or lists depending on the source type + and location. + + :type identifiers: list + :param identifiers: List of :py:class:`~boto3.resources.model.Parameter` + definitions + :type parent: ServiceResource + :param parent: The resource instance to which this action is attached. + :type params: dict + :param params: Request parameters sent to the service. + :type raw_response: dict + :param raw_response: Low-level operation response. + :rtype: list + :return: An ordered list of ``(name, value)`` identifier tuples. + """ + results = [] + + for identifier in identifiers: + source = identifier.source + target = identifier.target + + if source == 'response': + value = jmespath.search(identifier.path, raw_response) + elif source == 'requestParameter': + value = jmespath.search(identifier.path, params) + elif source == 'identifier': + value = getattr(parent, xform_name(identifier.name)) + elif source == 'data': + # If this is a data member then it may incur a load + # action before returning the value. + value = get_data_member(parent, identifier.path) + elif source == 'input': + # This value is set by the user, so ignore it here + continue + else: + raise NotImplementedError(f'Unsupported source type: {source}') + + results.append((xform_name(target), value)) + + return results + + +def build_empty_response(search_path, operation_name, service_model): + """ + Creates an appropriate empty response for the type that is expected, + based on the service model's shape type. For example, a value that + is normally a list would then return an empty list. A structure would + return an empty dict, and a number would return None. + + :type search_path: string + :param search_path: JMESPath expression to search in the response + :type operation_name: string + :param operation_name: Name of the underlying service operation. + :type service_model: :ref:`botocore.model.ServiceModel` + :param service_model: The Botocore service model + :rtype: dict, list, or None + :return: An appropriate empty value + """ + response = None + + operation_model = service_model.operation_model(operation_name) + shape = operation_model.output_shape + + if search_path: + # Walk the search path and find the final shape. For example, given + # a path of ``foo.bar[0].baz``, we first find the shape for ``foo``, + # then the shape for ``bar`` (ignoring the indexing), and finally + # the shape for ``baz``. + for item in search_path.split('.'): + item = item.strip('[0123456789]$') + + if shape.type_name == 'structure': + shape = shape.members[item] + elif shape.type_name == 'list': + shape = shape.member + else: + raise NotImplementedError( + f'Search path hits shape type {shape.type_name} from {item}' + ) + + # Anything not handled here is set to None + if shape.type_name == 'structure': + response = {} + elif shape.type_name == 'list': + response = [] + elif shape.type_name == 'map': + response = {} + + return response + + +class RawHandler: + """ + A raw action response handler. This passed through the response + dictionary, optionally after performing a JMESPath search if one + has been defined for the action. + + :type search_path: string + :param search_path: JMESPath expression to search in the response + :rtype: dict + :return: Service response + """ + + def __init__(self, search_path): + self.search_path = search_path + + def __call__(self, parent, params, response): + """ + :type parent: ServiceResource + :param parent: The resource instance to which this action is attached. + :type params: dict + :param params: Request parameters sent to the service. + :type response: dict + :param response: Low-level operation response. + """ + # TODO: Remove the '$' check after JMESPath supports it + if self.search_path and self.search_path != '$': + response = jmespath.search(self.search_path, response) + + return response + + +class ResourceHandler: + """ + Creates a new resource or list of new resources from the low-level + response based on the given response resource definition. + + :type search_path: string + :param search_path: JMESPath expression to search in the response + + :type factory: ResourceFactory + :param factory: The factory that created the resource class to which + this action is attached. + + :type resource_model: :py:class:`~boto3.resources.model.ResponseResource` + :param resource_model: Response resource model. + + :type service_context: :py:class:`~boto3.utils.ServiceContext` + :param service_context: Context about the AWS service + + :type operation_name: string + :param operation_name: Name of the underlying service operation, if it + exists. + + :rtype: ServiceResource or list + :return: New resource instance(s). + """ + + def __init__( + self, + search_path, + factory, + resource_model, + service_context, + operation_name=None, + ): + self.search_path = search_path + self.factory = factory + self.resource_model = resource_model + self.operation_name = operation_name + self.service_context = service_context + + def __call__(self, parent, params, response): + """ + :type parent: ServiceResource + :param parent: The resource instance to which this action is attached. + :type params: dict + :param params: Request parameters sent to the service. + :type response: dict + :param response: Low-level operation response. + """ + resource_name = self.resource_model.type + json_definition = self.service_context.resource_json_definitions.get( + resource_name + ) + + # Load the new resource class that will result from this action. + resource_cls = self.factory.load_from_definition( + resource_name=resource_name, + single_resource_json_definition=json_definition, + service_context=self.service_context, + ) + raw_response = response + search_response = None + + # Anytime a path is defined, it means the response contains the + # resource's attributes, so resource_data gets set here. It + # eventually ends up in resource.meta.data, which is where + # the attribute properties look for data. + if self.search_path: + search_response = jmespath.search(self.search_path, raw_response) + + # First, we parse all the identifiers, then create the individual + # response resources using them. Any identifiers that are lists + # will have one item consumed from the front of the list for each + # resource that is instantiated. Items which are not a list will + # be set as the same value on each new resource instance. + identifiers = dict( + build_identifiers( + self.resource_model.identifiers, parent, params, raw_response + ) + ) + + # If any of the identifiers is a list, then the response is plural + plural = [v for v in identifiers.values() if isinstance(v, list)] + + if plural: + response = [] + + # The number of items in an identifier that is a list will + # determine how many resource instances to create. + for i in range(len(plural[0])): + # Response item data is *only* available if a search path + # was given. This prevents accidentally loading unrelated + # data that may be in the response. + response_item = None + if search_response: + response_item = search_response[i] + response.append( + self.handle_response_item( + resource_cls, parent, identifiers, response_item + ) + ) + elif all_not_none(identifiers.values()): + # All identifiers must always exist, otherwise the resource + # cannot be instantiated. + response = self.handle_response_item( + resource_cls, parent, identifiers, search_response + ) + else: + # The response should be empty, but that may mean an + # empty dict, list, or None based on whether we make + # a remote service call and what shape it is expected + # to return. + response = None + if self.operation_name is not None: + # A remote service call was made, so try and determine + # its shape. + response = build_empty_response( + self.search_path, + self.operation_name, + self.service_context.service_model, + ) + + return response + + def handle_response_item( + self, resource_cls, parent, identifiers, resource_data + ): + """ + Handles the creation of a single response item by setting + parameters and creating the appropriate resource instance. + + :type resource_cls: ServiceResource subclass + :param resource_cls: The resource class to instantiate. + :type parent: ServiceResource + :param parent: The resource instance to which this action is attached. + :type identifiers: dict + :param identifiers: Map of identifier names to value or values. + :type resource_data: dict or None + :param resource_data: Data for resource attributes. + :rtype: ServiceResource + :return: New resource instance. + """ + kwargs = { + 'client': parent.meta.client, + } + + for name, value in identifiers.items(): + # If value is a list, then consume the next item + if isinstance(value, list): + value = value.pop(0) + + kwargs[name] = value + + resource = resource_cls(**kwargs) + + if resource_data is not None: + resource.meta.data = resource_data + + return resource diff --git a/venv/lib/python3.12/site-packages/boto3/s3/__init__.py b/venv/lib/python3.12/site-packages/boto3/s3/__init__.py new file mode 100644 index 0000000..6001b27 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/s3/__init__.py @@ -0,0 +1,12 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. diff --git a/venv/lib/python3.12/site-packages/boto3/s3/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/s3/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5f9f757 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/s3/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/s3/__pycache__/constants.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/s3/__pycache__/constants.cpython-312.pyc new file mode 100644 index 0000000..6262d91 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/s3/__pycache__/constants.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/s3/__pycache__/inject.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/s3/__pycache__/inject.cpython-312.pyc new file mode 100644 index 0000000..fade736 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/s3/__pycache__/inject.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/s3/__pycache__/transfer.cpython-312.pyc b/venv/lib/python3.12/site-packages/boto3/s3/__pycache__/transfer.cpython-312.pyc new file mode 100644 index 0000000..7d9bd66 Binary files /dev/null and b/venv/lib/python3.12/site-packages/boto3/s3/__pycache__/transfer.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/boto3/s3/constants.py b/venv/lib/python3.12/site-packages/boto3/s3/constants.py new file mode 100644 index 0000000..bd38778 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/s3/constants.py @@ -0,0 +1,18 @@ +# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + + +# TransferConfig preferred_transfer_client settings +CLASSIC_TRANSFER_CLIENT = "classic" +CRT_TRANSFER_CLIENT = "crt" +AUTO_RESOLVE_TRANSFER_CLIENT = "auto" diff --git a/venv/lib/python3.12/site-packages/boto3/s3/inject.py b/venv/lib/python3.12/site-packages/boto3/s3/inject.py new file mode 100644 index 0000000..770a3aa --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/s3/inject.py @@ -0,0 +1,958 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import copy as python_copy +import logging +from functools import partial + +from botocore.exceptions import ClientError + +from boto3 import utils +from boto3.compat import is_append_mode +from boto3.s3.transfer import ( + ProgressCallbackInvoker, + S3Transfer, + TransferConfig, + create_transfer_manager, +) + +try: + from botocore.context import with_current_context +except ImportError: + from functools import wraps + + def with_current_context(hook=None): + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return wrapper + + return decorator + + +try: + from botocore.useragent import register_feature_id +except ImportError: + + def register_feature_id(feature_id): + pass + + +logger = logging.getLogger(__name__) + + +def inject_s3_transfer_methods(class_attributes, **kwargs): + utils.inject_attribute(class_attributes, 'upload_file', upload_file) + utils.inject_attribute(class_attributes, 'download_file', download_file) + utils.inject_attribute(class_attributes, 'copy', copy) + utils.inject_attribute(class_attributes, 'upload_fileobj', upload_fileobj) + utils.inject_attribute( + class_attributes, 'download_fileobj', download_fileobj + ) + + +def inject_bucket_methods(class_attributes, **kwargs): + utils.inject_attribute(class_attributes, 'load', bucket_load) + utils.inject_attribute(class_attributes, 'upload_file', bucket_upload_file) + utils.inject_attribute( + class_attributes, 'download_file', bucket_download_file + ) + utils.inject_attribute(class_attributes, 'copy', bucket_copy) + utils.inject_attribute( + class_attributes, 'upload_fileobj', bucket_upload_fileobj + ) + utils.inject_attribute( + class_attributes, 'download_fileobj', bucket_download_fileobj + ) + + +def inject_object_methods(class_attributes, **kwargs): + utils.inject_attribute(class_attributes, 'upload_file', object_upload_file) + utils.inject_attribute( + class_attributes, 'download_file', object_download_file + ) + utils.inject_attribute(class_attributes, 'copy', object_copy) + utils.inject_attribute( + class_attributes, 'upload_fileobj', object_upload_fileobj + ) + utils.inject_attribute( + class_attributes, 'download_fileobj', object_download_fileobj + ) + + +def inject_object_summary_methods(class_attributes, **kwargs): + utils.inject_attribute(class_attributes, 'load', object_summary_load) + + +def bucket_load(self, *args, **kwargs): + """ + Calls s3.Client.list_buckets() to update the attributes of the Bucket + resource. + """ + # The docstring above is phrased this way to match what the autogenerated + # docs produce. + + # We can't actually get the bucket's attributes from a HeadBucket, + # so we need to use a ListBuckets and search for our bucket. + # However, we may fail if we lack permissions to ListBuckets + # or the bucket is in another account. In which case, creation_date + # will be None. + self.meta.data = {} + try: + response = self.meta.client.list_buckets() + for bucket_data in response['Buckets']: + if bucket_data['Name'] == self.name: + self.meta.data = bucket_data + break + except ClientError as e: + if not e.response.get('Error', {}).get('Code') == 'AccessDenied': + raise + + +def object_summary_load(self, *args, **kwargs): + """ + Calls s3.Client.head_object to update the attributes of the ObjectSummary + resource. + """ + response = self.meta.client.head_object( + Bucket=self.bucket_name, Key=self.key + ) + if 'ContentLength' in response: + response['Size'] = response.pop('ContentLength') + self.meta.data = response + + +@with_current_context(partial(register_feature_id, 'S3_TRANSFER')) +def upload_file( + self, Filename, Bucket, Key, ExtraArgs=None, Callback=None, Config=None +): + """Upload a file to an S3 object. + + Usage:: + + import boto3 + s3 = boto3.client('s3') + s3.upload_file('/tmp/hello.txt', 'amzn-s3-demo-bucket', 'hello.txt') + + Similar behavior as S3Transfer's upload_file() method, except that + argument names are capitalized. Detailed examples can be found at + :ref:`S3Transfer's Usage `. + + :type Filename: str + :param Filename: The path to the file to upload. + + :type Bucket: str + :param Bucket: The name of the bucket to upload to. + + :type Key: str + :param Key: The name of the key to upload to. + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed upload arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_UPLOAD_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the upload. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + transfer. + """ + with S3Transfer(self, Config) as transfer: + return transfer.upload_file( + filename=Filename, + bucket=Bucket, + key=Key, + extra_args=ExtraArgs, + callback=Callback, + ) + + +@with_current_context(partial(register_feature_id, 'S3_TRANSFER')) +def download_file( + self, Bucket, Key, Filename, ExtraArgs=None, Callback=None, Config=None +): + """Download an S3 object to a file. + + Usage:: + + import boto3 + s3 = boto3.client('s3') + s3.download_file('amzn-s3-demo-bucket', 'hello.txt', '/tmp/hello.txt') + + Similar behavior as S3Transfer's download_file() method, + except that parameters are capitalized. Detailed examples can be found at + :ref:`S3Transfer's Usage `. + + :type Bucket: str + :param Bucket: The name of the bucket to download from. + + :type Key: str + :param Key: The name of the key to download from. + + :type Filename: str + :param Filename: The path to the file to download to. + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed download arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_DOWNLOAD_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the download. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + transfer. + """ + with S3Transfer(self, Config) as transfer: + return transfer.download_file( + bucket=Bucket, + key=Key, + filename=Filename, + extra_args=ExtraArgs, + callback=Callback, + ) + + +def bucket_upload_file( + self, Filename, Key, ExtraArgs=None, Callback=None, Config=None +): + """Upload a file to an S3 object. + + Usage:: + + import boto3 + s3 = boto3.resource('s3') + s3.Bucket('amzn-s3-demo-bucket').upload_file('/tmp/hello.txt', 'hello.txt') + + Similar behavior as S3Transfer's upload_file() method, + except that parameters are capitalized. Detailed examples can be found at + :ref:`S3Transfer's Usage `. + + :type Filename: str + :param Filename: The path to the file to upload. + + :type Key: str + :param Key: The name of the key to upload to. + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed upload arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_UPLOAD_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the upload. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + transfer. + """ + return self.meta.client.upload_file( + Filename=Filename, + Bucket=self.name, + Key=Key, + ExtraArgs=ExtraArgs, + Callback=Callback, + Config=Config, + ) + + +def bucket_download_file( + self, Key, Filename, ExtraArgs=None, Callback=None, Config=None +): + """Download an S3 object to a file. + + Usage:: + + import boto3 + s3 = boto3.resource('s3') + s3.Bucket('amzn-s3-demo-bucket').download_file('hello.txt', '/tmp/hello.txt') + + Similar behavior as S3Transfer's download_file() method, + except that parameters are capitalized. Detailed examples can be found at + :ref:`S3Transfer's Usage `. + + :type Key: str + :param Key: The name of the key to download from. + + :type Filename: str + :param Filename: The path to the file to download to. + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed download arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_DOWNLOAD_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the download. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + transfer. + """ + return self.meta.client.download_file( + Bucket=self.name, + Key=Key, + Filename=Filename, + ExtraArgs=ExtraArgs, + Callback=Callback, + Config=Config, + ) + + +def object_upload_file( + self, Filename, ExtraArgs=None, Callback=None, Config=None +): + """Upload a file to an S3 object. + + Usage:: + + import boto3 + s3 = boto3.resource('s3') + s3.Object('amzn-s3-demo-bucket', 'hello.txt').upload_file('/tmp/hello.txt') + + Similar behavior as S3Transfer's upload_file() method, + except that parameters are capitalized. Detailed examples can be found at + :ref:`S3Transfer's Usage `. + + :type Filename: str + :param Filename: The path to the file to upload. + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed upload arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_UPLOAD_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the upload. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + transfer. + """ + return self.meta.client.upload_file( + Filename=Filename, + Bucket=self.bucket_name, + Key=self.key, + ExtraArgs=ExtraArgs, + Callback=Callback, + Config=Config, + ) + + +def object_download_file( + self, Filename, ExtraArgs=None, Callback=None, Config=None +): + """Download an S3 object to a file. + + Usage:: + + import boto3 + s3 = boto3.resource('s3') + s3.Object('amzn-s3-demo-bucket', 'hello.txt').download_file('/tmp/hello.txt') + + Similar behavior as S3Transfer's download_file() method, + except that parameters are capitalized. Detailed examples can be found at + :ref:`S3Transfer's Usage `. + + :type Filename: str + :param Filename: The path to the file to download to. + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed download arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_DOWNLOAD_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the download. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + transfer. + """ + return self.meta.client.download_file( + Bucket=self.bucket_name, + Key=self.key, + Filename=Filename, + ExtraArgs=ExtraArgs, + Callback=Callback, + Config=Config, + ) + + +@with_current_context(partial(register_feature_id, 'S3_TRANSFER')) +def copy( + self, + CopySource, + Bucket, + Key, + ExtraArgs=None, + Callback=None, + SourceClient=None, + Config=None, +): + """Copy an object from one S3 location to another. + + This is a managed transfer which will perform a multipart copy in + multiple threads if necessary. + + Usage:: + + import boto3 + s3 = boto3.resource('s3') + copy_source = { + 'Bucket': 'amzn-s3-demo-bucket1', + 'Key': 'mykey' + } + s3.meta.client.copy(copy_source, 'amzn-s3-demo-bucket2', 'otherkey') + + :type CopySource: dict + :param CopySource: The name of the source bucket, key name of the + source object, and optional version ID of the source object. The + dictionary format is: + ``{'Bucket': 'bucket', 'Key': 'key', 'VersionId': 'id'}``. Note + that the ``VersionId`` key is optional and may be omitted. + + :type Bucket: str + :param Bucket: The name of the bucket to copy to + + :type Key: str + :param Key: The name of the key to copy to + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed copy arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_COPY_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the copy. + + :type SourceClient: botocore or boto3 Client + :param SourceClient: The client to be used for operations that + may happen at the source object. For example, this client is + used for the head_object that determines the size of the copy. + If no client is provided, the current client is used as the + client for the source object. The current client still + requires IAM permissions to access both buckets. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + copy. + """ + subscribers = None + if Callback is not None: + subscribers = [ProgressCallbackInvoker(Callback)] + + config = Config + if config is None: + config = TransferConfig() + + # copy is not supported in the CRT + new_config = python_copy.copy(config) + new_config.preferred_transfer_client = "classic" + + with create_transfer_manager(self, new_config) as manager: + future = manager.copy( + copy_source=CopySource, + bucket=Bucket, + key=Key, + extra_args=ExtraArgs, + subscribers=subscribers, + source_client=SourceClient, + ) + return future.result() + + +def bucket_copy( + self, + CopySource, + Key, + ExtraArgs=None, + Callback=None, + SourceClient=None, + Config=None, +): + """Copy an object from one S3 location to an object in this bucket. + + This is a managed transfer which will perform a multipart copy in + multiple threads if necessary. + + Usage:: + + import boto3 + s3 = boto3.resource('s3') + copy_source = { + 'Bucket': 'amzn-s3-demo-bucket1', + 'Key': 'mykey' + } + bucket = s3.Bucket('amzn-s3-demo-bucket2') + bucket.copy(copy_source, 'otherkey') + + :type CopySource: dict + :param CopySource: The name of the source bucket, key name of the + source object, and optional version ID of the source object. The + dictionary format is: + ``{'Bucket': 'bucket', 'Key': 'key', 'VersionId': 'id'}``. Note + that the ``VersionId`` key is optional and may be omitted. + + :type Key: str + :param Key: The name of the key to copy to + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed copy arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_COPY_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the copy. + + :type SourceClient: botocore or boto3 Client + :param SourceClient: The client to be used for operations that + may happen at the source object. For example, this client is + used for the head_object that determines the size of the copy. + If no client is provided, the current client is used as the + client for the source object. The current client still + requires IAM permissions to access both buckets. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + copy. + """ + return self.meta.client.copy( + CopySource=CopySource, + Bucket=self.name, + Key=Key, + ExtraArgs=ExtraArgs, + Callback=Callback, + SourceClient=SourceClient, + Config=Config, + ) + + +def object_copy( + self, + CopySource, + ExtraArgs=None, + Callback=None, + SourceClient=None, + Config=None, +): + """Copy an object from one S3 location to this object. + + This is a managed transfer which will perform a multipart copy in + multiple threads if necessary. + + Usage:: + + import boto3 + s3 = boto3.resource('s3') + copy_source = { + 'Bucket': 'amzn-s3-demo-bucket1', + 'Key': 'mykey' + } + bucket = s3.Bucket('amzn-s3-demo-bucket2') + obj = bucket.Object('otherkey') + obj.copy(copy_source) + + :type CopySource: dict + :param CopySource: The name of the source bucket, key name of the + source object, and optional version ID of the source object. The + dictionary format is: + ``{'Bucket': 'bucket', 'Key': 'key', 'VersionId': 'id'}``. Note + that the ``VersionId`` key is optional and may be omitted. + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed copy arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_COPY_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the copy. + + :type SourceClient: botocore or boto3 Client + :param SourceClient: The client to be used for operations that + may happen at the source object. For example, this client is + used for the head_object that determines the size of the copy. + If no client is provided, the current client is used as the + client for the source object. The current client still + requires IAM permissions to access both buckets. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + copy. + """ + return self.meta.client.copy( + CopySource=CopySource, + Bucket=self.bucket_name, + Key=self.key, + ExtraArgs=ExtraArgs, + Callback=Callback, + SourceClient=SourceClient, + Config=Config, + ) + + +@with_current_context(partial(register_feature_id, 'S3_TRANSFER')) +def upload_fileobj( + self, Fileobj, Bucket, Key, ExtraArgs=None, Callback=None, Config=None +): + """Upload a file-like object to S3. + + The file-like object must be in binary mode. + + This is a managed transfer which will perform a multipart upload in + multiple threads if necessary. + + Usage:: + + import boto3 + s3 = boto3.client('s3') + + with open('filename', 'rb') as data: + s3.upload_fileobj(data, 'amzn-s3-demo-bucket', 'mykey') + + :type Fileobj: a file-like object + :param Fileobj: A file-like object to upload. At a minimum, it must + implement the `read` method, and must return bytes. + + :type Bucket: str + :param Bucket: The name of the bucket to upload to. + + :type Key: str + :param Key: The name of the key to upload to. + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed upload arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_UPLOAD_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the upload. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + upload. + """ + if not hasattr(Fileobj, 'read'): + raise ValueError('Fileobj must implement read') + + subscribers = None + if Callback is not None: + subscribers = [ProgressCallbackInvoker(Callback)] + + config = Config + if config is None: + config = TransferConfig() + + with create_transfer_manager(self, config) as manager: + future = manager.upload( + fileobj=Fileobj, + bucket=Bucket, + key=Key, + extra_args=ExtraArgs, + subscribers=subscribers, + ) + return future.result() + + +def bucket_upload_fileobj( + self, Fileobj, Key, ExtraArgs=None, Callback=None, Config=None +): + """Upload a file-like object to this bucket. + + The file-like object must be in binary mode. + + This is a managed transfer which will perform a multipart upload in + multiple threads if necessary. + + Usage:: + + import boto3 + s3 = boto3.resource('s3') + bucket = s3.Bucket('amzn-s3-demo-bucket') + + with open('filename', 'rb') as data: + bucket.upload_fileobj(data, 'mykey') + + :type Fileobj: a file-like object + :param Fileobj: A file-like object to upload. At a minimum, it must + implement the `read` method, and must return bytes. + + :type Key: str + :param Key: The name of the key to upload to. + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed upload arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_UPLOAD_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the upload. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + upload. + """ + return self.meta.client.upload_fileobj( + Fileobj=Fileobj, + Bucket=self.name, + Key=Key, + ExtraArgs=ExtraArgs, + Callback=Callback, + Config=Config, + ) + + +def object_upload_fileobj( + self, Fileobj, ExtraArgs=None, Callback=None, Config=None +): + """Upload a file-like object to this object. + + The file-like object must be in binary mode. + + This is a managed transfer which will perform a multipart upload in + multiple threads if necessary. + + Usage:: + + import boto3 + s3 = boto3.resource('s3') + bucket = s3.Bucket('amzn-s3-demo-bucket') + obj = bucket.Object('mykey') + + with open('filename', 'rb') as data: + obj.upload_fileobj(data) + + :type Fileobj: a file-like object + :param Fileobj: A file-like object to upload. At a minimum, it must + implement the `read` method, and must return bytes. + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed upload arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_UPLOAD_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the upload. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + upload. + """ + return self.meta.client.upload_fileobj( + Fileobj=Fileobj, + Bucket=self.bucket_name, + Key=self.key, + ExtraArgs=ExtraArgs, + Callback=Callback, + Config=Config, + ) + + +def disable_threading_if_append_mode(config, fileobj): + """Set `TransferConfig.use_threads` to `False` if file-like + object is in append mode. + + :type config: boto3.s3.transfer.TransferConfig + :param config: The transfer configuration to be used when performing the + download. + + :type fileobj: A file-like object + :param fileobj: A file-like object to inspect for append mode. + """ + if is_append_mode(fileobj): + config.use_threads = False + logger.warning( + 'A single thread will be used because the provided file object ' + 'is in append mode. Writes may always be appended to the end of ' + 'the file regardless of seek position, so a single thread must be ' + 'used to ensure sequential writes.' + ) + + +@with_current_context(partial(register_feature_id, 'S3_TRANSFER')) +def download_fileobj( + self, Bucket, Key, Fileobj, ExtraArgs=None, Callback=None, Config=None +): + """Download an object from S3 to a file-like object. + + The file-like object must be in binary mode. + + This is a managed transfer which will perform a multipart download in + multiple threads if necessary. + + Usage:: + + import boto3 + s3 = boto3.client('s3') + + with open('filename', 'wb') as data: + s3.download_fileobj('amzn-s3-demo-bucket', 'mykey', data) + + :type Bucket: str + :param Bucket: The name of the bucket to download from. + + :type Key: str + :param Key: The name of the key to download from. + + :type Fileobj: a file-like object + :param Fileobj: A file-like object to download into. At a minimum, it must + implement the `write` method and must accept bytes. + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed download arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_DOWNLOAD_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the download. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + download. + """ + if not hasattr(Fileobj, 'write'): + raise ValueError('Fileobj must implement write') + + subscribers = None + if Callback is not None: + subscribers = [ProgressCallbackInvoker(Callback)] + + config = Config + if config is None: + config = TransferConfig() + + new_config = python_copy.copy(config) + disable_threading_if_append_mode(new_config, Fileobj) + + with create_transfer_manager(self, new_config) as manager: + future = manager.download( + bucket=Bucket, + key=Key, + fileobj=Fileobj, + extra_args=ExtraArgs, + subscribers=subscribers, + ) + return future.result() + + +def bucket_download_fileobj( + self, Key, Fileobj, ExtraArgs=None, Callback=None, Config=None +): + """Download an object from this bucket to a file-like-object. + + The file-like object must be in binary mode. + + This is a managed transfer which will perform a multipart download in + multiple threads if necessary. + + Usage:: + + import boto3 + s3 = boto3.resource('s3') + bucket = s3.Bucket('amzn-s3-demo-bucket') + + with open('filename', 'wb') as data: + bucket.download_fileobj('mykey', data) + + :type Fileobj: a file-like object + :param Fileobj: A file-like object to download into. At a minimum, it must + implement the `write` method and must accept bytes. + + :type Key: str + :param Key: The name of the key to download from. + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed download arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_DOWNLOAD_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the download. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + download. + """ + return self.meta.client.download_fileobj( + Bucket=self.name, + Key=Key, + Fileobj=Fileobj, + ExtraArgs=ExtraArgs, + Callback=Callback, + Config=Config, + ) + + +def object_download_fileobj( + self, Fileobj, ExtraArgs=None, Callback=None, Config=None +): + """Download this object from S3 to a file-like object. + + The file-like object must be in binary mode. + + This is a managed transfer which will perform a multipart download in + multiple threads if necessary. + + Usage:: + + import boto3 + s3 = boto3.resource('s3') + bucket = s3.Bucket('amzn-s3-demo-bucket') + obj = bucket.Object('mykey') + + with open('filename', 'wb') as data: + obj.download_fileobj(data) + + :type Fileobj: a file-like object + :param Fileobj: A file-like object to download into. At a minimum, it must + implement the `write` method and must accept bytes. + + :type ExtraArgs: dict + :param ExtraArgs: Extra arguments that may be passed to the + client operation. For allowed download arguments see + :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_DOWNLOAD_ARGS`. + + :type Callback: function + :param Callback: A method which takes a number of bytes transferred to + be periodically called during the download. + + :type Config: boto3.s3.transfer.TransferConfig + :param Config: The transfer configuration to be used when performing the + download. + """ + return self.meta.client.download_fileobj( + Bucket=self.bucket_name, + Key=self.key, + Fileobj=Fileobj, + ExtraArgs=ExtraArgs, + Callback=Callback, + Config=Config, + ) diff --git a/venv/lib/python3.12/site-packages/boto3/s3/transfer.py b/venv/lib/python3.12/site-packages/boto3/s3/transfer.py new file mode 100644 index 0000000..9880ec3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/s3/transfer.py @@ -0,0 +1,516 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Abstractions over S3's upload/download operations. + +This module provides high level abstractions for efficient +uploads/downloads. It handles several things for the user: + +* Automatically switching to multipart transfers when + a file is over a specific size threshold +* Uploading/downloading a file in parallel +* Progress callbacks to monitor transfers +* Retries. While botocore handles retries for streaming uploads, + it is not possible for it to handle retries for streaming + downloads. This module handles retries for both cases so + you don't need to implement any retry logic yourself. + +This module has a reasonable set of defaults. It also allows you +to configure many aspects of the transfer process including: + +* Multipart threshold size +* Max parallel downloads +* Socket timeouts +* Retry amounts + +There is no support for s3->s3 multipart copies at this +time. + + +.. _ref_s3transfer_usage: + +Usage +===== + +The simplest way to use this module is: + +.. code-block:: python + + client = boto3.client('s3', 'us-west-2') + transfer = S3Transfer(client) + # Upload /tmp/myfile to s3://bucket/key + transfer.upload_file('/tmp/myfile', 'bucket', 'key') + + # Download s3://bucket/key to /tmp/myfile + transfer.download_file('bucket', 'key', '/tmp/myfile') + +The ``upload_file`` and ``download_file`` methods also accept +``**kwargs``, which will be forwarded through to the corresponding +client operation. Here are a few examples using ``upload_file``:: + + # Making the object public + transfer.upload_file('/tmp/myfile', 'bucket', 'key', + extra_args={'ACL': 'public-read'}) + + # Setting metadata + transfer.upload_file('/tmp/myfile', 'bucket', 'key', + extra_args={'Metadata': {'a': 'b', 'c': 'd'}}) + + # Setting content type + transfer.upload_file('/tmp/myfile.json', 'bucket', 'key', + extra_args={'ContentType': "application/json"}) + + +The ``S3Transfer`` class also supports progress callbacks so you can +provide transfer progress to users. Both the ``upload_file`` and +``download_file`` methods take an optional ``callback`` parameter. +Here's an example of how to print a simple progress percentage +to the user: + +.. code-block:: python + + class ProgressPercentage(object): + def __init__(self, filename): + self._filename = filename + self._size = float(os.path.getsize(filename)) + self._seen_so_far = 0 + self._lock = threading.Lock() + + def __call__(self, bytes_amount): + # To simplify we'll assume this is hooked up + # to a single filename. + with self._lock: + self._seen_so_far += bytes_amount + percentage = (self._seen_so_far / self._size) * 100 + sys.stdout.write( + "\r%s %s / %s (%.2f%%)" % ( + self._filename, self._seen_so_far, self._size, + percentage)) + sys.stdout.flush() + + + transfer = S3Transfer(boto3.client('s3', 'us-west-2')) + # Upload /tmp/myfile to s3://bucket/key and print upload progress. + transfer.upload_file('/tmp/myfile', 'bucket', 'key', + callback=ProgressPercentage('/tmp/myfile')) + + + +You can also provide a TransferConfig object to the S3Transfer +object that gives you more fine grained control over the +transfer. For example: + +.. code-block:: python + + client = boto3.client('s3', 'us-west-2') + config = TransferConfig( + multipart_threshold=8 * 1024 * 1024, + max_concurrency=10, + num_download_attempts=10, + ) + transfer = S3Transfer(client, config) + transfer.upload_file('/tmp/foo', 'bucket', 'key') + + +""" + +import logging +import threading +from os import PathLike, fspath, getpid + +from botocore.compat import HAS_CRT +from botocore.exceptions import ClientError, MissingDependencyException +from s3transfer.exceptions import ( + RetriesExceededError as S3TransferRetriesExceededError, +) +from s3transfer.futures import NonThreadedExecutor +from s3transfer.manager import TransferConfig as S3TransferConfig +from s3transfer.manager import TransferManager +from s3transfer.subscribers import BaseSubscriber +from s3transfer.utils import OSUtils + +import boto3.s3.constants as constants +from boto3.compat import TRANSFER_CONFIG_SUPPORTS_CRT +from boto3.exceptions import ( + RetriesExceededError, + S3UploadFailedError, +) + +if HAS_CRT: + import awscrt.s3 + + from boto3.crt import create_crt_transfer_manager + +KB = 1024 +MB = KB * KB + +logger = logging.getLogger(__name__) + + +def create_transfer_manager(client, config, osutil=None): + """Creates a transfer manager based on configuration + + :type client: boto3.client + :param client: The S3 client to use + + :type config: boto3.s3.transfer.TransferConfig + :param config: The transfer config to use + + :type osutil: s3transfer.utils.OSUtils + :param osutil: The os utility to use + + :rtype: s3transfer.manager.TransferManager + :returns: A transfer manager based on parameters provided + """ + if _should_use_crt(config): + crt_transfer_manager = create_crt_transfer_manager(client, config) + if crt_transfer_manager is not None: + logger.debug( + "Using CRT client. pid: %s, thread: %s", + getpid(), + threading.get_ident(), + ) + return crt_transfer_manager + + # If we don't resolve something above, fallback to the default. + logger.debug( + "Using default client. pid: %s, thread: %s", + getpid(), + threading.get_ident(), + ) + return _create_default_transfer_manager(client, config, osutil) + + +def _should_use_crt(config): + # This feature requires awscrt>=0.19.18 + has_min_crt = HAS_CRT and has_minimum_crt_version((0, 19, 18)) + is_optimized_instance = has_min_crt and awscrt.s3.is_optimized_for_system() + pref_transfer_client = config.preferred_transfer_client.lower() + + if ( + pref_transfer_client == constants.CRT_TRANSFER_CLIENT + and not has_min_crt + ): + msg = ( + "CRT transfer client is configured but is missing minimum CRT " + f"version. CRT installed: {HAS_CRT}" + ) + if HAS_CRT: + msg += f", with version: {awscrt.__version__}" + raise MissingDependencyException(msg=msg) + + if ( + is_optimized_instance + and pref_transfer_client == constants.AUTO_RESOLVE_TRANSFER_CLIENT + ) or pref_transfer_client == constants.CRT_TRANSFER_CLIENT: + logger.debug( + "Attempting to use CRTTransferManager. Config settings may be ignored." + ) + return True + + logger.debug( + "Opting out of CRT Transfer Manager. " + "Preferred client: %s, CRT available: %s, Instance Optimized: %s", + pref_transfer_client, + HAS_CRT, + is_optimized_instance, + ) + return False + + +def has_minimum_crt_version(minimum_version): + """Not intended for use outside boto3.""" + if not HAS_CRT: + return False + + crt_version_str = awscrt.__version__ + try: + crt_version_ints = map(int, crt_version_str.split(".")) + crt_version_tuple = tuple(crt_version_ints) + except (TypeError, ValueError): + return False + + return crt_version_tuple >= minimum_version + + +def _create_default_transfer_manager(client, config, osutil): + """Create the default TransferManager implementation for s3transfer.""" + executor_cls = None + if not config.use_threads: + executor_cls = NonThreadedExecutor + return TransferManager(client, config, osutil, executor_cls) + + +class TransferConfig(S3TransferConfig): + ALIAS = { + 'max_concurrency': 'max_request_concurrency', + 'max_io_queue': 'max_io_queue_size', + } + DEFAULTS = { + 'multipart_threshold': 8 * MB, + 'max_concurrency': 10, + 'max_request_concurrency': 10, + 'multipart_chunksize': 8 * MB, + 'num_download_attempts': 5, + 'max_io_queue': 100, + 'max_io_queue_size': 100, + 'io_chunksize': 256 * KB, + 'use_threads': True, + 'max_bandwidth': None, + 'preferred_transfer_client': constants.AUTO_RESOLVE_TRANSFER_CLIENT, + } + + def __init__( + self, + multipart_threshold=None, + max_concurrency=None, + multipart_chunksize=None, + num_download_attempts=None, + max_io_queue=None, + io_chunksize=None, + use_threads=None, + max_bandwidth=None, + preferred_transfer_client=None, + ): + """Configuration object for managed S3 transfers + + :param multipart_threshold: The transfer size threshold for which + multipart uploads, downloads, and copies will automatically be + triggered. + + :param max_concurrency: The maximum number of threads that will be + making requests to perform a transfer. If ``use_threads`` is + set to ``False``, the value provided is ignored as the transfer + will only ever use the current thread. + + :param multipart_chunksize: The partition size of each part for a + multipart transfer. + + :param num_download_attempts: The number of download attempts that + will be retried upon errors with downloading an object in S3. + Note that these retries account for errors that occur when + streaming down the data from s3 (i.e. socket errors and read + timeouts that occur after receiving an OK response from s3). + Other retryable exceptions such as throttling errors and 5xx + errors are already retried by botocore (this default is 5). This + does not take into account the number of exceptions retried by + botocore. Note: This value is ignored when resolved transfer + manager type is CRTTransferManager. + + :param max_io_queue: The maximum amount of read parts that can be + queued in memory to be written for a download. The size of each + of these read parts is at most the size of ``io_chunksize``. + Note: This value is ignored when resolved transfer manager type + is CRTTransferManager. + + :param io_chunksize: The max size of each chunk in the io queue. + Currently, this is size used when ``read`` is called on the + downloaded stream as well. Note: This value is ignored when + resolved transfer manager type is CRTTransferManager. + + :param use_threads: If True, threads will be used when performing + S3 transfers. If False, no threads will be used in + performing transfers; all logic will be run in the current thread. + Note: This value is ignored when resolved transfer manager type is + CRTTransferManager. + + :param max_bandwidth: The maximum bandwidth that will be consumed + in uploading and downloading file content. The value is an integer + in terms of bytes per second. Note: This value is ignored when + resolved transfer manager type is CRTTransferManager. + + :param preferred_transfer_client: String specifying preferred transfer + client for transfer operations. + + Current supported settings are: + * auto (default) - Use the CRTTransferManager when calls + are made with supported environment and settings. + * classic - Only use the origin S3TransferManager with + requests. Disables possible CRT upgrade on requests. + * crt - Only use the CRTTransferManager with requests. + """ + init_args = { + 'multipart_threshold': multipart_threshold, + 'max_concurrency': max_concurrency, + 'multipart_chunksize': multipart_chunksize, + 'num_download_attempts': num_download_attempts, + 'max_io_queue': max_io_queue, + 'io_chunksize': io_chunksize, + 'use_threads': use_threads, + 'max_bandwidth': max_bandwidth, + 'preferred_transfer_client': preferred_transfer_client, + } + resolved = self._resolve_init_args(init_args) + super().__init__( + multipart_threshold=resolved['multipart_threshold'], + max_request_concurrency=resolved['max_concurrency'], + multipart_chunksize=resolved['multipart_chunksize'], + num_download_attempts=resolved['num_download_attempts'], + max_io_queue_size=resolved['max_io_queue'], + io_chunksize=resolved['io_chunksize'], + max_bandwidth=resolved['max_bandwidth'], + ) + # Some of the argument names are not the same as the inherited + # S3TransferConfig so we add aliases so you can still access the + # old version of the names. + for alias in self.ALIAS: + setattr( + self, + alias, + object.__getattribute__(self, self.ALIAS[alias]), + ) + self.use_threads = resolved['use_threads'] + self.preferred_transfer_client = resolved['preferred_transfer_client'] + + def __setattr__(self, name, value): + # If the alias name is used, make sure we set the name that it points + # to as that is what actually is used in governing the TransferManager. + if name in self.ALIAS: + super().__setattr__(self.ALIAS[name], value) + # Always set the value of the actual name provided. + super().__setattr__(name, value) + + def __getattribute__(self, item): + value = object.__getattribute__(self, item) + if not TRANSFER_CONFIG_SUPPORTS_CRT: + return value + defaults = object.__getattribute__(self, 'DEFAULTS') + if item not in defaults: + return value + if value is self.UNSET_DEFAULT: + return defaults[item] + return value + + def _resolve_init_args(self, init_args): + resolved = {} + for init_arg, val in init_args.items(): + if val is not None: + resolved[init_arg] = val + elif TRANSFER_CONFIG_SUPPORTS_CRT: + resolved[init_arg] = self.UNSET_DEFAULT + else: + resolved[init_arg] = self.DEFAULTS[init_arg] + return resolved + + +class S3Transfer: + ALLOWED_DOWNLOAD_ARGS = TransferManager.ALLOWED_DOWNLOAD_ARGS + ALLOWED_UPLOAD_ARGS = TransferManager.ALLOWED_UPLOAD_ARGS + ALLOWED_COPY_ARGS = TransferManager.ALLOWED_COPY_ARGS + + def __init__(self, client=None, config=None, osutil=None, manager=None): + if not client and not manager: + raise ValueError( + 'Either a boto3.Client or s3transfer.manager.TransferManager ' + 'must be provided' + ) + if manager and any([client, config, osutil]): + raise ValueError( + 'Manager cannot be provided with client, config, ' + 'nor osutil. These parameters are mutually exclusive.' + ) + if config is None: + config = TransferConfig() + if osutil is None: + osutil = OSUtils() + if manager: + self._manager = manager + else: + self._manager = create_transfer_manager(client, config, osutil) + + def upload_file( + self, filename, bucket, key, callback=None, extra_args=None + ): + """Upload a file to an S3 object. + + Variants have also been injected into S3 client, Bucket and Object. + You don't have to use S3Transfer.upload_file() directly. + + .. seealso:: + :py:meth:`S3.Client.upload_file` + :py:meth:`S3.Client.upload_fileobj` + """ + if isinstance(filename, PathLike): + filename = fspath(filename) + if not isinstance(filename, str): + raise ValueError('Filename must be a string or a path-like object') + + subscribers = self._get_subscribers(callback) + future = self._manager.upload( + filename, bucket, key, extra_args, subscribers + ) + try: + future.result() + # If a client error was raised, add the backwards compatibility layer + # that raises a S3UploadFailedError. These specific errors were only + # ever thrown for upload_parts but now can be thrown for any related + # client error. + except ClientError as e: + raise S3UploadFailedError( + f"Failed to upload {filename} to {bucket}/{key}: {e}" + ) + + def download_file( + self, bucket, key, filename, extra_args=None, callback=None + ): + """Download an S3 object to a file. + + Variants have also been injected into S3 client, Bucket and Object. + You don't have to use S3Transfer.download_file() directly. + + .. seealso:: + :py:meth:`S3.Client.download_file` + :py:meth:`S3.Client.download_fileobj` + """ + if isinstance(filename, PathLike): + filename = fspath(filename) + if not isinstance(filename, str): + raise ValueError('Filename must be a string or a path-like object') + + subscribers = self._get_subscribers(callback) + future = self._manager.download( + bucket, key, filename, extra_args, subscribers + ) + try: + future.result() + # This is for backwards compatibility where when retries are + # exceeded we need to throw the same error from boto3 instead of + # s3transfer's built in RetriesExceededError as current users are + # catching the boto3 one instead of the s3transfer exception to do + # their own retries. + except S3TransferRetriesExceededError as e: + raise RetriesExceededError(e.last_exception) + + def _get_subscribers(self, callback): + if not callback: + return None + return [ProgressCallbackInvoker(callback)] + + def __enter__(self): + return self + + def __exit__(self, *args): + self._manager.__exit__(*args) + + +class ProgressCallbackInvoker(BaseSubscriber): + """A back-compat wrapper to invoke a provided callback via a subscriber + + :param callback: A callable that takes a single positional argument for + how many bytes were transferred. + """ + + def __init__(self, callback): + self._callback = callback + + def on_progress(self, bytes_transferred, **kwargs): + self._callback(bytes_transferred) diff --git a/venv/lib/python3.12/site-packages/boto3/session.py b/venv/lib/python3.12/site-packages/boto3/session.py new file mode 100644 index 0000000..df97859 --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/session.py @@ -0,0 +1,574 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import copy +import os + +import botocore.session +from botocore.client import Config +from botocore.exceptions import ( + DataNotFoundError, + NoCredentialsError, + UnknownServiceError, +) + +import boto3 +import boto3.utils +from boto3.exceptions import ResourceNotExistsError, UnknownAPIVersionError + +from .resources.factory import ResourceFactory + + +class Session: + """ + A session stores configuration state and allows you to create service + clients and resources. + + :type aws_access_key_id: string + :param aws_access_key_id: AWS access key ID + :type aws_secret_access_key: string + :param aws_secret_access_key: AWS secret access key + :type aws_session_token: string + :param aws_session_token: AWS temporary session token + :type region_name: string + :param region_name: Default region when creating new connections + :type botocore_session: botocore.session.Session + :param botocore_session: Use this Botocore session instead of creating + a new default one. + :type profile_name: string + :param profile_name: The name of a profile to use. If not given, then + the default profile is used. + :type aws_account_id: string + :param aws_account_id: AWS account ID + """ + + def __init__( + self, + aws_access_key_id=None, + aws_secret_access_key=None, + aws_session_token=None, + region_name=None, + botocore_session=None, + profile_name=None, + aws_account_id=None, + ): + if botocore_session is not None: + self._session = botocore_session + else: + # Create a new default session + self._session = botocore.session.get_session() + + # Setup custom user-agent string if it isn't already customized + if self._session.user_agent_name == 'Botocore': + botocore_info = f'Botocore/{self._session.user_agent_version}' + if self._session.user_agent_extra: + self._session.user_agent_extra += f" {botocore_info}" + else: + self._session.user_agent_extra = botocore_info + self._session.user_agent_name = 'Boto3' + self._session.user_agent_version = boto3.__version__ + + if profile_name is not None: + self._session.set_config_variable('profile', profile_name) + + credentials_kwargs = { + "aws_access_key_id": aws_access_key_id, + "aws_secret_access_key": aws_secret_access_key, + "aws_session_token": aws_session_token, + "aws_account_id": aws_account_id, + } + + if any(credentials_kwargs.values()): + if self._account_id_set_without_credentials(**credentials_kwargs): + raise NoCredentialsError() + + if aws_account_id is None: + del credentials_kwargs["aws_account_id"] + + self._session.set_credentials(*credentials_kwargs.values()) + + if region_name is not None: + self._session.set_config_variable('region', region_name) + + self.resource_factory = ResourceFactory( + self._session.get_component('event_emitter') + ) + self._setup_loader() + self._register_default_handlers() + + def __repr__(self): + return '{}(region_name={})'.format( + self.__class__.__name__, + repr(self._session.get_config_variable('region')), + ) + + @property + def profile_name(self): + """ + The **read-only** profile name. + """ + return self._session.profile or 'default' + + @property + def region_name(self): + """ + The **read-only** region name. + """ + return self._session.get_config_variable('region') + + @property + def events(self): + """ + The event emitter for a session + """ + return self._session.get_component('event_emitter') + + @property + def available_profiles(self): + """ + The profiles available to the session credentials + """ + return self._session.available_profiles + + def _setup_loader(self): + """ + Setup loader paths so that we can load resources. + """ + self._loader = self._session.get_component('data_loader') + self._loader.search_paths.append( + os.path.join(os.path.dirname(__file__), 'data') + ) + + def get_available_services(self): + """ + Get a list of available services that can be loaded as low-level + clients via :py:meth:`Session.client`. + + :rtype: list + :return: List of service names + """ + return self._session.get_available_services() + + def get_available_resources(self): + """ + Get a list of available services that can be loaded as resource + clients via :py:meth:`Session.resource`. + + :rtype: list + :return: List of service names + """ + return self._loader.list_available_services(type_name='resources-1') + + def get_available_partitions(self): + """Lists the available partitions + + :rtype: list + :return: Returns a list of partition names (e.g., ["aws", "aws-cn"]) + """ + return self._session.get_available_partitions() + + def get_available_regions( + self, service_name, partition_name='aws', allow_non_regional=False + ): + """Lists the region and endpoint names of a particular partition. + + The list of regions returned by this method are regions that are + explicitly known by the client to exist and is not comprehensive. A + region not returned in this list may still be available for the + provided service. + + :type service_name: string + :param service_name: Name of a service to list endpoint for (e.g., s3). + + :type partition_name: string + :param partition_name: Name of the partition to limit endpoints to. + (e.g., aws for the public AWS endpoints, aws-cn for AWS China + endpoints, aws-us-gov for AWS GovCloud (US) Endpoints, etc.) + + :type allow_non_regional: bool + :param allow_non_regional: Set to True to include endpoints that are + not regional endpoints (e.g., s3-external-1, + fips-us-gov-west-1, etc). + + :return: Returns a list of endpoint names (e.g., ["us-east-1"]). + """ + return self._session.get_available_regions( + service_name=service_name, + partition_name=partition_name, + allow_non_regional=allow_non_regional, + ) + + def get_credentials(self): + """ + Return the :class:`botocore.credentials.Credentials` object + associated with this session. If the credentials have not + yet been loaded, this will attempt to load them. If they + have already been loaded, this will return the cached + credentials. + """ + return self._session.get_credentials() + + def get_partition_for_region(self, region_name): + """Lists the partition name of a particular region. + + :type region_name: string + :param region_name: Name of the region to list partition for (e.g., + us-east-1). + + :rtype: string + :return: Returns the respective partition name (e.g., aws). + """ + return self._session.get_partition_for_region(region_name) + + def client( + self, + service_name, + region_name=None, + api_version=None, + use_ssl=True, + verify=None, + endpoint_url=None, + aws_access_key_id=None, + aws_secret_access_key=None, + aws_session_token=None, + config=None, + aws_account_id=None, + ): + """ + Create a low-level service client by name. + + :type service_name: string + :param service_name: The name of a service, e.g. 's3' or 'ec2'. You + can get a list of available services via + :py:meth:`get_available_services`. + + :type region_name: string + :param region_name: The name of the region associated with the client. + A client is associated with a single region. + + :type api_version: string + :param api_version: The API version to use. By default, botocore will + use the latest API version when creating a client. You only need + to specify this parameter if you want to use a previous API version + of the client. + + :type use_ssl: boolean + :param use_ssl: Whether or not to use SSL. By default, SSL is used. + Note that not all services support non-ssl connections. + + :type verify: boolean/string + :param verify: Whether or not to verify SSL certificates. By default + SSL certificates are verified. You can provide the following + values: + + * False - do not validate SSL certificates. SSL will still be + used (unless use_ssl is False), but SSL certificates + will not be verified. + * path/to/cert/bundle.pem - A filename of the CA cert bundle to + uses. You can specify this argument if you want to use a + different CA cert bundle than the one used by botocore. + + :type endpoint_url: string + :param endpoint_url: The complete URL to use for the constructed + client. Normally, botocore will automatically construct the + appropriate URL to use when communicating with a service. You + can specify a complete URL (including the "http/https" scheme) + to override this behavior. If this value is provided, + then ``use_ssl`` is ignored. + + :type aws_access_key_id: string + :param aws_access_key_id: The access key to use when creating + the client. This is entirely optional, and if not provided, + the credentials configured for the session will automatically + be used. You only need to provide this argument if you want + to override the credentials used for this specific client. + + :type aws_secret_access_key: string + :param aws_secret_access_key: The secret key to use when creating + the client. Same semantics as aws_access_key_id above. + + :type aws_session_token: string + :param aws_session_token: The session token to use when creating + the client. Same semantics as aws_access_key_id above. + + :type config: botocore.client.Config + :param config: Advanced client configuration options. If region_name + is specified in the client config, its value will take precedence + over environment variables and configuration values, but not over + a region_name value passed explicitly to the method. See + `botocore config documentation + `_ + for more details. + + :type aws_account_id: string + :param aws_account_id: The account id to use when creating + the client. Same semantics as aws_access_key_id above. + + :return: Service client instance + + """ + create_client_kwargs = { + 'region_name': region_name, + 'api_version': api_version, + 'use_ssl': use_ssl, + 'verify': verify, + 'endpoint_url': endpoint_url, + 'aws_access_key_id': aws_access_key_id, + 'aws_secret_access_key': aws_secret_access_key, + 'aws_session_token': aws_session_token, + 'config': config, + 'aws_account_id': aws_account_id, + } + if aws_account_id is None: + # Remove aws_account_id for arbitrary + # botocore version mismatches in AWS Lambda. + del create_client_kwargs['aws_account_id'] + + return self._session.create_client( + service_name, **create_client_kwargs + ) + + def resource( + self, + service_name, + region_name=None, + api_version=None, + use_ssl=True, + verify=None, + endpoint_url=None, + aws_access_key_id=None, + aws_secret_access_key=None, + aws_session_token=None, + config=None, + ): + """ + Create a resource service client by name. + + :type service_name: string + :param service_name: The name of a service, e.g. 's3' or 'ec2'. You + can get a list of available services via + :py:meth:`get_available_resources`. + + :type region_name: string + :param region_name: The name of the region associated with the client. + A client is associated with a single region. + + :type api_version: string + :param api_version: The API version to use. By default, botocore will + use the latest API version when creating a client. You only need + to specify this parameter if you want to use a previous API version + of the client. + + :type use_ssl: boolean + :param use_ssl: Whether or not to use SSL. By default, SSL is used. + Note that not all services support non-ssl connections. + + :type verify: boolean/string + :param verify: Whether or not to verify SSL certificates. By default + SSL certificates are verified. You can provide the following + values: + + * False - do not validate SSL certificates. SSL will still be + used (unless use_ssl is False), but SSL certificates + will not be verified. + * path/to/cert/bundle.pem - A filename of the CA cert bundle to + uses. You can specify this argument if you want to use a + different CA cert bundle than the one used by botocore. + + :type endpoint_url: string + :param endpoint_url: The complete URL to use for the constructed + client. Normally, botocore will automatically construct the + appropriate URL to use when communicating with a service. You + can specify a complete URL (including the "http/https" scheme) + to override this behavior. If this value is provided, + then ``use_ssl`` is ignored. + + :type aws_access_key_id: string + :param aws_access_key_id: The access key to use when creating + the client. This is entirely optional, and if not provided, + the credentials configured for the session will automatically + be used. You only need to provide this argument if you want + to override the credentials used for this specific client. + + :type aws_secret_access_key: string + :param aws_secret_access_key: The secret key to use when creating + the client. Same semantics as aws_access_key_id above. + + :type aws_session_token: string + :param aws_session_token: The session token to use when creating + the client. Same semantics as aws_access_key_id above. + + :type config: botocore.client.Config + :param config: Advanced client configuration options. If region_name + is specified in the client config, its value will take precedence + over environment variables and configuration values, but not over + a region_name value passed explicitly to the method. If + user_agent_extra is specified in the client config, it overrides + the default user_agent_extra provided by the resource API. See + `botocore config documentation + `_ + for more details. + + :return: Subclass of :py:class:`~boto3.resources.base.ServiceResource` + """ + try: + resource_model = self._loader.load_service_model( + service_name, 'resources-1', api_version + ) + except UnknownServiceError: + available = self.get_available_resources() + has_low_level_client = ( + service_name in self.get_available_services() + ) + raise ResourceNotExistsError( + service_name, available, has_low_level_client + ) + except DataNotFoundError: + # This is because we've provided an invalid API version. + available_api_versions = self._loader.list_api_versions( + service_name, 'resources-1' + ) + raise UnknownAPIVersionError( + service_name, api_version, ', '.join(available_api_versions) + ) + + if api_version is None: + # Even though botocore's load_service_model() can handle + # using the latest api_version if not provided, we need + # to track this api_version in boto3 in order to ensure + # we're pairing a resource model with a client model + # of the same API version. It's possible for the latest + # API version of a resource model in boto3 to not be + # the same API version as a service model in botocore. + # So we need to look up the api_version if one is not + # provided to ensure we load the same API version of the + # client. + # + # Note: This is relying on the fact that + # loader.load_service_model(..., api_version=None) + # and loader.determine_latest_version(..., 'resources-1') + # both load the same api version of the file. + api_version = self._loader.determine_latest_version( + service_name, 'resources-1' + ) + + # Creating a new resource instance requires the low-level client + # and service model, the resource version and resource JSON data. + # We pass these to the factory and get back a class, which is + # instantiated on top of the low-level client. + if config is not None: + if config.user_agent_extra is None: + config = copy.deepcopy(config) + config.user_agent_extra = 'Resource' + else: + config = Config(user_agent_extra='Resource') + client = self.client( + service_name, + region_name=region_name, + api_version=api_version, + use_ssl=use_ssl, + verify=verify, + endpoint_url=endpoint_url, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + config=config, + ) + service_model = client.meta.service_model + + # Create a ServiceContext object to serve as a reference to + # important read-only information about the general service. + service_context = boto3.utils.ServiceContext( + service_name=service_name, + service_model=service_model, + resource_json_definitions=resource_model['resources'], + service_waiter_model=boto3.utils.LazyLoadedWaiterModel( + self._session, service_name, api_version + ), + ) + + # Create the service resource class. + cls = self.resource_factory.load_from_definition( + resource_name=service_name, + single_resource_json_definition=resource_model['service'], + service_context=service_context, + ) + + return cls(client=client) + + def _register_default_handlers(self): + # S3 customizations + self._session.register( + 'creating-client-class.s3', + boto3.utils.lazy_call( + 'boto3.s3.inject.inject_s3_transfer_methods' + ), + ) + self._session.register( + 'creating-resource-class.s3.Bucket', + boto3.utils.lazy_call('boto3.s3.inject.inject_bucket_methods'), + ) + self._session.register( + 'creating-resource-class.s3.Object', + boto3.utils.lazy_call('boto3.s3.inject.inject_object_methods'), + ) + self._session.register( + 'creating-resource-class.s3.ObjectSummary', + boto3.utils.lazy_call( + 'boto3.s3.inject.inject_object_summary_methods' + ), + ) + + # DynamoDb customizations + self._session.register( + 'creating-resource-class.dynamodb', + boto3.utils.lazy_call( + 'boto3.dynamodb.transform.register_high_level_interface' + ), + unique_id='high-level-dynamodb', + ) + self._session.register( + 'creating-resource-class.dynamodb.Table', + boto3.utils.lazy_call( + 'boto3.dynamodb.table.register_table_methods' + ), + unique_id='high-level-dynamodb-table', + ) + + # EC2 Customizations + self._session.register( + 'creating-resource-class.ec2.ServiceResource', + boto3.utils.lazy_call('boto3.ec2.createtags.inject_create_tags'), + ) + + self._session.register( + 'creating-resource-class.ec2.Instance', + boto3.utils.lazy_call( + 'boto3.ec2.deletetags.inject_delete_tags', + event_emitter=self.events, + ), + ) + + def _account_id_set_without_credentials( + self, + *, + aws_account_id, + aws_access_key_id, + aws_secret_access_key, + **kwargs, + ): + if aws_account_id is None: + return False + elif aws_access_key_id is None or aws_secret_access_key is None: + return True + return False diff --git a/venv/lib/python3.12/site-packages/boto3/utils.py b/venv/lib/python3.12/site-packages/boto3/utils.py new file mode 100644 index 0000000..289bbdf --- /dev/null +++ b/venv/lib/python3.12/site-packages/boto3/utils.py @@ -0,0 +1,90 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# https://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from collections import namedtuple +from importlib import import_module + +_ServiceContext = namedtuple( + 'ServiceContext', + [ + 'service_name', + 'service_model', + 'service_waiter_model', + 'resource_json_definitions', + ], +) + + +class ServiceContext(_ServiceContext): + """Provides important service-wide, read-only information about a service + + :type service_name: str + :param service_name: The name of the service + + :type service_model: :py:class:`botocore.model.ServiceModel` + :param service_model: The model of the service. + + :type service_waiter_model: :py:class:`botocore.waiter.WaiterModel` or + a waiter model-like object such as + :py:class:`boto3.utils.LazyLoadedWaiterModel` + :param service_waiter_model: The waiter model of the service. + + :type resource_json_definitions: dict + :param resource_json_definitions: The loaded json models of all resource + shapes for a service. It is equivalient of loading a + ``resource-1.json`` and retrieving the value at the key "resources". + """ + + pass + + +def lazy_call(full_name, **kwargs): + parent_kwargs = kwargs + + def _handler(**kwargs): + module, function_name = full_name.rsplit('.', 1) + module = import_module(module) + kwargs.update(parent_kwargs) + return getattr(module, function_name)(**kwargs) + + return _handler + + +def inject_attribute(class_attributes, name, value): + if name in class_attributes: + raise RuntimeError( + f'Cannot inject class attribute "{name}", attribute ' + f'already exists in class dict.' + ) + else: + class_attributes[name] = value + + +class LazyLoadedWaiterModel: + """A lazily loaded waiter model + + This does not load the service waiter model until an attempt is made + to retrieve the waiter model for a specific waiter. This is helpful + in docstring generation where we do not need to actually need to grab + the waiter-2.json until it is accessed through a ``get_waiter`` call + when the docstring is generated/accessed. + """ + + def __init__(self, bc_session, service_name, api_version): + self._session = bc_session + self._service_name = service_name + self._api_version = api_version + + def get_waiter(self, waiter_name): + return self._session.get_waiter_model( + self._service_name, self._api_version + ).get_waiter(waiter_name) diff --git a/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/LICENSE.txt b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/LICENSE.txt new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/LICENSE.txt @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/METADATA b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/METADATA new file mode 100644 index 0000000..703e4ec --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/METADATA @@ -0,0 +1,146 @@ +Metadata-Version: 2.1 +Name: botocore +Version: 1.43.104 +Summary: Low-level, data-driven core of boto 3. +Home-page: https://github.com/boto/botocore +Author: Amazon Web Services +License: Apache-2.0 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: Natural Language :: English +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta +Requires-Python: >= 3.10 +License-File: LICENSE.txt +License-File: NOTICE +Requires-Dist: jmespath (<2.0.0,>=0.7.1) +Requires-Dist: python-dateutil (<3.0.0,>=2.1) +Requires-Dist: urllib3 (!=2.2.0,<3,>=1.25.4) +Provides-Extra: crt +Requires-Dist: awscrt (==0.36.0) ; extra == 'crt' + +botocore +======== + +|Version| |Python| |License| + +A low-level interface to a growing number of Amazon Web Services. The +botocore package is the foundation for the +`AWS CLI `__ as well as +`boto3 `__. + +Botocore is maintained and published by `Amazon Web Services`_. + +Notices +------- + +On 2026-04-29, support for Python 3.9 ended for Botocore. This follows the +Python Software Foundation `end of support `__ +for the runtime which occurred on 2025-10-31. + +For more information, see this `blog post `__. + +.. _`Amazon Web Services`: https://aws.amazon.com/what-is-aws/ +.. |Python| image:: https://img.shields.io/pypi/pyversions/botocore.svg?style=flat + :target: https://pypi.python.org/pypi/botocore/ + :alt: Python Versions +.. |Version| image:: http://img.shields.io/pypi/v/botocore.svg?style=flat + :target: https://pypi.python.org/pypi/botocore/ + :alt: Package Version +.. |License| image:: http://img.shields.io/pypi/l/botocore.svg?style=flat + :target: https://github.com/boto/botocore/blob/develop/LICENSE.txt + :alt: License + +Getting Started +--------------- +Assuming that you have Python and ``virtualenv`` installed, set up your environment and install the required dependencies like this or you can install the library using ``pip``: + +.. code-block:: sh + + $ git clone https://github.com/boto/botocore.git + $ cd botocore + $ python -m venv .venv + ... + $ source .venv/bin/activate + $ python -m pip install -r requirements.txt + $ python -m pip install -e . + +.. code-block:: sh + + $ pip install botocore + +Using Botocore +~~~~~~~~~~~~~~ +After installing botocore + +Next, set up credentials (in e.g. ``~/.aws/credentials``): + +.. code-block:: ini + + [default] + aws_access_key_id = YOUR_KEY + aws_secret_access_key = YOUR_SECRET + +Then, set up a default region (in e.g. ``~/.aws/config``): + +.. code-block:: ini + + [default] + region=us-east-1 + +Other credentials configuration method can be found `here `__ + +Then, from a Python interpreter: + +.. code-block:: python + + >>> import botocore.session + >>> session = botocore.session.get_session() + >>> client = session.create_client('ec2') + >>> print(client.describe_instances()) + + +Getting Help +------------ + +We use GitHub issues for tracking bugs and feature requests and have limited +bandwidth to address them. Please use these community resources for getting +help. Please note many of the same resources available for ``boto3`` are +applicable for ``botocore``: + +* Ask a question on `Stack Overflow `__ and tag it with `boto3 `__ +* Open a support ticket with `AWS Support `__ +* If it turns out that you may have found a bug, please `open an issue `__ + + +Contributing +------------ + +We value feedback and contributions from our community. Whether it's a bug report, new feature, correction, or additional documentation, we welcome your issues and pull requests. Please read through this `CONTRIBUTING `__ document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your contribution. + + +Maintenance and Support for SDK Major Versions +---------------------------------------------- + +Botocore was made generally available on 06/22/2015 and is currently in the full support phase of the availability life cycle. + +For information about maintenance and support for SDK major versions and their underlying dependencies, see the following in the AWS SDKs and Tools Reference Guide: + +* `AWS SDKs and Tools Maintenance Policy `__ +* `AWS SDKs and Tools Version Support Matrix `__ + + +More Resources +-------------- + +* `NOTICE `__ +* `Changelog `__ +* `License `__ diff --git a/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/NOTICE b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/NOTICE new file mode 100644 index 0000000..edcc3cd --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/NOTICE @@ -0,0 +1,60 @@ +Botocore +Copyright 2012-2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +---- + +Botocore includes vendorized parts of the requests python library for backwards compatibility. + +Requests License +================ + +Copyright 2013 Kenneth Reitz + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +Botocore includes vendorized parts of the urllib3 library for backwards compatibility. + +Urllib3 License +=============== + +This is the MIT license: http://www.opensource.org/licenses/mit-license.php + +Copyright 2008-2011 Andrey Petrov and contributors (see CONTRIBUTORS.txt), +Modifications copyright 2012 Kenneth Reitz. + +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. + +Bundle of CA Root Certificates +============================== + +***** BEGIN LICENSE BLOCK ***** +This Source Code Form is subject to the terms of the +Mozilla Public License, v. 2.0. If a copy of the MPL +was not distributed with this file, You can obtain +one at http://mozilla.org/MPL/2.0/. + +***** END LICENSE BLOCK ***** diff --git a/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/RECORD b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/RECORD new file mode 100644 index 0000000..d3ab40e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/RECORD @@ -0,0 +1,2090 @@ +botocore-1.43.104.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +botocore-1.43.104.dist-info/LICENSE.txt,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174 +botocore-1.43.104.dist-info/METADATA,sha256=a2CCDlWVunBHtMtCsP5ohE5ES-4_9KirRyvZK6_BICQ,5570 +botocore-1.43.104.dist-info/NOTICE,sha256=HRxabz1oyxH0-tGvqGp0UNAobxXBdu8OoEjyVbRtlbA,2467 +botocore-1.43.104.dist-info/RECORD,, +botocore-1.43.104.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91 +botocore-1.43.104.dist-info/top_level.txt,sha256=IdlNr9dnwi3lQt66dKnShE5HBUhIqBFqJmVhm11aijk,9 +botocore/__init__.py,sha256=4sh9Ph87kG50j0Wf7vK6CbLMz8gc0XVcMcPOp2K9RNE,11430 +botocore/__pycache__/__init__.cpython-312.pyc,, +botocore/__pycache__/args.cpython-312.pyc,, +botocore/__pycache__/auth.cpython-312.pyc,, +botocore/__pycache__/awsrequest.cpython-312.pyc,, +botocore/__pycache__/client.cpython-312.pyc,, +botocore/__pycache__/compat.cpython-312.pyc,, +botocore/__pycache__/compress.cpython-312.pyc,, +botocore/__pycache__/config.cpython-312.pyc,, +botocore/__pycache__/configloader.cpython-312.pyc,, +botocore/__pycache__/configprovider.cpython-312.pyc,, +botocore/__pycache__/context.cpython-312.pyc,, +botocore/__pycache__/credentials.cpython-312.pyc,, +botocore/__pycache__/discovery.cpython-312.pyc,, +botocore/__pycache__/endpoint.cpython-312.pyc,, +botocore/__pycache__/endpoint_provider.cpython-312.pyc,, +botocore/__pycache__/errorfactory.cpython-312.pyc,, +botocore/__pycache__/eventstream.cpython-312.pyc,, +botocore/__pycache__/exceptions.cpython-312.pyc,, +botocore/__pycache__/handlers.cpython-312.pyc,, +botocore/__pycache__/history.cpython-312.pyc,, +botocore/__pycache__/hooks.cpython-312.pyc,, +botocore/__pycache__/httpchecksum.cpython-312.pyc,, +botocore/__pycache__/httpsession.cpython-312.pyc,, +botocore/__pycache__/loaders.cpython-312.pyc,, +botocore/__pycache__/model.cpython-312.pyc,, +botocore/__pycache__/monitoring.cpython-312.pyc,, +botocore/__pycache__/paginate.cpython-312.pyc,, +botocore/__pycache__/parsers.cpython-312.pyc,, +botocore/__pycache__/plugin.cpython-312.pyc,, +botocore/__pycache__/regions.cpython-312.pyc,, +botocore/__pycache__/response.cpython-312.pyc,, +botocore/__pycache__/retryhandler.cpython-312.pyc,, +botocore/__pycache__/serialize.cpython-312.pyc,, +botocore/__pycache__/session.cpython-312.pyc,, +botocore/__pycache__/signers.cpython-312.pyc,, +botocore/__pycache__/stub.cpython-312.pyc,, +botocore/__pycache__/tokens.cpython-312.pyc,, +botocore/__pycache__/translate.cpython-312.pyc,, +botocore/__pycache__/useragent.cpython-312.pyc,, +botocore/__pycache__/utils.cpython-312.pyc,, +botocore/__pycache__/validate.cpython-312.pyc,, +botocore/__pycache__/waiter.cpython-312.pyc,, +botocore/args.py,sha256=_4Ox-nJsaKhy179J1djHE_1klKHRn9sd6WJ5j51eQ2w,41200 +botocore/auth.py,sha256=BxOUdzi6vcgo0RYrvgnBTfE1smEI4CQvvdwtLFLokbY,46191 +botocore/awsrequest.py,sha256=hdCuAosVSpkdsQeMcJh0QMKQxVucNPZuiuQFPeXSLC8,23375 +botocore/cacert.pem,sha256=7ZPWNGI21PAnj2F_UkUJHzfQAYBqSc5CzgrVvshY0o4,259837 +botocore/client.py,sha256=Dnt5DMtqoso0gDxoeKrq19uRCO7k4_ugqmYAi2R5Z_k,54779 +botocore/compat.py,sha256=5PGk-Hh7OVi6_7U8BmEifsaTn-scP-ahWp6hdUAKJ5s,11769 +botocore/compress.py,sha256=Qxl9IGVCpZ2gQG5KE_iqWQgbXvj_I70jA5yNtgZL1iY,4533 +botocore/config.py,sha256=P_iLn1r0sZpBeS5WlpGBNeC_xkAR3LSEP7UVkiI5AU8,20566 +botocore/configloader.py,sha256=NTejI7b9UGUXBv2uKiPaXH19Lgl30LY5ujZkXRcFpHs,10039 +botocore/configprovider.py,sha256=88VotdQld6g02s_FlKcf2voBhuDHSNzy80yMQtq5DxI,39084 +botocore/context.py,sha256=XQRDA0YPajIjb6zQres-QQNOtV0L7B6UDfBHrza4W9E,3782 +botocore/credentials.py,sha256=OPlVYcVTx-dtnabSPTCnedz7BAOiVMuYVi6S4SzAsOg,101908 +botocore/crt/__init__.py,sha256=kCXQL93gdg5yBQJOTp7YFLl9wYNy4tV_5TAyJq0asD0,1006 +botocore/crt/__pycache__/__init__.cpython-312.pyc,, +botocore/crt/__pycache__/auth.cpython-312.pyc,, +botocore/crt/auth.py,sha256=ReT3jB_J6H-0wD3fogG4uuyrwwka14d7UJZ5weujftU,25076 +botocore/data/_retry.json,sha256=0dIGY-kUA1xaYn9-2YHgunpCeettHM3hKEYoTOirc6o,7270 +botocore/data/accessanalyzer/2019-11-01/endpoint-rule-set-1.json.gz,sha256=enOKCs3qlagZtKd-3nvxwd2HjK3m4Uy-CTE6QgUI-Dk,1237 +botocore/data/accessanalyzer/2019-11-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/accessanalyzer/2019-11-01/paginators-1.json,sha256=8cCsaqYmzBJj1naqrZSRYos_QGyOVec_G_9xuLEApG8,1908 +botocore/data/accessanalyzer/2019-11-01/paginators-1.sdk-extras.json,sha256=nwsOcoMZ1GDzrFfMc6_Gx0tNRKwt5b8XddGiZmkAS2s,600 +botocore/data/accessanalyzer/2019-11-01/service-2.json.gz,sha256=DNF3b5eAQ6nuIDCG6kqss30YfyCiJOIn_2np3h2ZUo8,28270 +botocore/data/account-access/2018-05-10/endpoint-rule-set-1.json.gz,sha256=V_21E4DtXdTjapJYxgFM-Kp9Dm3PKHgdjRYB5Xz8WTM,838 +botocore/data/account-access/2018-05-10/paginators-1.json,sha256=Juey8OM7WgotqzH16KjfgSNkalDSMc9eqv5TS8NFPgY,365 +botocore/data/account-access/2018-05-10/service-2.json.gz,sha256=Af1PwIDAAWJnXVlO-IT91iq7PlOcQEeyFe0VO6D824s,4360 +botocore/data/account-access/2018-05-10/waiters-2.json,sha256=Vm7e-iw2q3xettBZtFuD8aRcJPeLEeqieNlvQ3oXdKc,581 +botocore/data/account/2021-02-01/endpoint-rule-set-1.json.gz,sha256=cB-WEQbfM9mcVZOkdHmHWcwT2uREBat52vQobKyl8LQ,1379 +botocore/data/account/2021-02-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/account/2021-02-01/paginators-1.json,sha256=TCku1Qs1la1Ggv8u8dKSYP2E5i5sWpmhRmL4zSR87RQ,185 +botocore/data/account/2021-02-01/service-2.json.gz,sha256=XD4Gs8eJWS3RGvOqI1skWlAsk4jLNKnJWP-dfx-E0uo,6661 +botocore/data/account/2021-02-01/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/acm-pca/2017-08-22/endpoint-rule-set-1.json.gz,sha256=UEt9luuDb3-KiUUMD73kg1B3JiNgivPY0IpGw9cUW8g,1234 +botocore/data/acm-pca/2017-08-22/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/acm-pca/2017-08-22/paginators-1.json,sha256=q2wFRetchlBt43qtOCTJ_Qw49u-LnRgmPdEn1j_j50A,537 +botocore/data/acm-pca/2017-08-22/service-2.json.gz,sha256=q_1IGjuySPmu-ekn3lN1afZn73XTcOVsX93OhXgFMWk,24978 +botocore/data/acm-pca/2017-08-22/waiters-2.json,sha256=PH8MS91fUjXWbhd08bgVhUC-_SQPmLrJOGkGo6j6jaU,1759 +botocore/data/acm/2015-12-08/endpoint-rule-set-1.json.gz,sha256=PVctgJ8C-GY3hZTb4Pxot39gpIFdDnphKDmznBXVbQo,1247 +botocore/data/acm/2015-12-08/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/acm/2015-12-08/paginators-1.json,sha256=scJrOJQn-qpECzel_s5AUb7FJSHDWVNWlLDmMe0-9w4,1295 +botocore/data/acm/2015-12-08/service-2.json.gz,sha256=LLkIKw2K1qzF3NM5-im5GI4b3nKxeB4wZPj4oo5w6Dc,25840 +botocore/data/acm/2015-12-08/waiters-2.json,sha256=_J8sVJWdQCFtBGp9xfdlY-K09oprIfa8fAzXREulzgg,3197 +botocore/data/agent-registry-control/2025-12-01/endpoint-rule-set-1.json.gz,sha256=pEurf8L8CTtVxutZeMvzo59GCPs7Y4xnIfGb8cfSXFE,479 +botocore/data/agent-registry-control/2025-12-01/paginators-1.json,sha256=2jNO3Y5Q2CQX9behmo2Ur7mfut20Vc1XYDaZ-r1nUTk,367 +botocore/data/agent-registry-control/2025-12-01/service-2.json.gz,sha256=aZLXsdmiEshKbqfF1ggGe9g5cNsuQz0EPjDHYItZFw4,15805 +botocore/data/agent-registry-control/2025-12-01/waiters-2.json,sha256=jL2Euazflw5KQ7AbnVcKncsCr5iQ7WDOMHE4sABPay4,1392 +botocore/data/agent-registry/2025-12-01/endpoint-rule-set-1.json.gz,sha256=QplQIZNjTDa_tDveZPKFXMZCI1NJVYfSNhtszBBG4j8,430 +botocore/data/agent-registry/2025-12-01/paginators-1.json,sha256=oynbLcS7nBWBCJsoGkbKyOLLiBV7R2ASljopQEDgn6E,213 +botocore/data/agent-registry/2025-12-01/service-2.json.gz,sha256=6X4Be93vwvYz7Zdw3WEd1He4T7tpqurNA4Sx8Gu7Q7I,5635 +botocore/data/aiops/2018-05-10/endpoint-rule-set-1.json.gz,sha256=860V9qqBXXK4MvH42_7U6USMx62nLPerC61fHAZ-oew,1296 +botocore/data/aiops/2018-05-10/paginators-1.json,sha256=f0tLATJ2XSci2en4kooSFQ-htod7hyA7nf54-4ycaIg,209 +botocore/data/aiops/2018-05-10/service-2.json.gz,sha256=fI6dPQw4aLblYWeLRfkfQmGJB2c4xCgeCL-nGYbsQwI,6128 +botocore/data/aiops/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/amp/2020-08-01/endpoint-rule-set-1.json.gz,sha256=BzcGfYXBiRd2wi4qZiYuf-UW8eNxY2hBakUGjjVcIYQ,1145 +botocore/data/amp/2020-08-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/amp/2020-08-01/paginators-1.json,sha256=gOCl7CM-ELiCZZ9BzIfEbHwzBmxizZraTadvic-_kI8,717 +botocore/data/amp/2020-08-01/service-2.json.gz,sha256=rVTfvna8VzW1C52WBR5SOASqZLWtBQaCER47LdQyWO4,19558 +botocore/data/amp/2020-08-01/waiters-2.json,sha256=eeRO0PVcJh6MPtsVwtDu4vW0caXV1XwqJAdBHvUnfzY,3387 +botocore/data/amplify/2017-07-25/endpoint-rule-set-1.json.gz,sha256=29EsbFcXGNi2LTR9Pn7rrveqiNxIMsSzeNG6_KqW5XA,1149 +botocore/data/amplify/2017-07-25/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/amplify/2017-07-25/paginators-1.json,sha256=XJ4xwNrUExhAxy-8K8JJAPnBhdRZO7FB6NGTrgr_qZQ,685 +botocore/data/amplify/2017-07-25/service-2.json.gz,sha256=OdcND4FU0Y5RMagISmbdEyXrmvILmWTD804NGhWEGq4,17616 +botocore/data/amplifybackend/2020-08-11/endpoint-rule-set-1.json.gz,sha256=yN8Y3FKY4Dy4V0NBpJ-unILwURxSC6ziQIGJQUpz_Pc,1154 +botocore/data/amplifybackend/2020-08-11/paginators-1.json,sha256=0JG13-2KlCwca-Pwz7d5Mp3WIttu4BpwDusqxMXF9XY,186 +botocore/data/amplifybackend/2020-08-11/service-2.json.gz,sha256=e4T_a5KKHyslFke2wV19EYiU6xixjg5KPK54dalPEB8,10990 +botocore/data/amplifyuibuilder/2021-08-11/endpoint-rule-set-1.json.gz,sha256=JSvap61bjx5EGYTomR-IpdYwpfueTZQuPIOISe5P4f4,1156 +botocore/data/amplifyuibuilder/2021-08-11/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/amplifyuibuilder/2021-08-11/paginators-1.json,sha256=idtki67MCJcfs_brVKsvknxJbZtDfS-IK3cakM1IFCI,1063 +botocore/data/amplifyuibuilder/2021-08-11/service-2.json.gz,sha256=kM6Ly29PJMiial2zuJ_9yTa309vyMVmB5YaDyI3GXmU,15634 +botocore/data/amplifyuibuilder/2021-08-11/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/apigateway/2015-07-09/endpoint-rule-set-1.json.gz,sha256=PjVQA33A6v1dbACrUG04YXiA-qhsiVfs0MiQpXIdNyc,1151 +botocore/data/apigateway/2015-07-09/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/apigateway/2015-07-09/paginators-1.json,sha256=gwAb1K7CkHdC49pAfwZMgaT18Hm1r5qDK1m_6m-Ki9w,2913 +botocore/data/apigateway/2015-07-09/service-2.json.gz,sha256=77LztwElTPJzGnmgYhqt1OxDjZAqSHK3yShEFVcOXJw,39749 +botocore/data/apigatewaymanagementapi/2018-11-29/endpoint-rule-set-1.json.gz,sha256=mSXM_og9vY37PcGAKAswVCvyRPef6Nga_AyPN8d2goU,1151 +botocore/data/apigatewaymanagementapi/2018-11-29/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/apigatewaymanagementapi/2018-11-29/service-2.json.gz,sha256=Hq1sM2wE8-GYwaUCgm6WxRawACuBkxsLHiCAITp7JIM,1444 +botocore/data/apigatewayv2/2018-11-29/endpoint-rule-set-1.json.gz,sha256=PjVQA33A6v1dbACrUG04YXiA-qhsiVfs0MiQpXIdNyc,1151 +botocore/data/apigatewayv2/2018-11-29/paginators-1.json,sha256=uQijp2s8aofeR6MXY0KDrgYJXF8Dk2P34Lbw6H9utSk,2457 +botocore/data/apigatewayv2/2018-11-29/service-2.json.gz,sha256=ZG3QtesWv40zsYjbtCNwUcpZIomgztd4pwlA3pWEUwQ,53309 +botocore/data/appconfig/2019-10-09/endpoint-rule-set-1.json.gz,sha256=rlSWMxdfxQ-qZX9V6kzpNpuBHRQmVRa7_OWRpKVZaSg,1230 +botocore/data/appconfig/2019-10-09/examples-1.json,sha256=lm2meYHY2djHXZ_3lYZa2PxELHhVDtZdMkVw4IWCI8Y,25502 +botocore/data/appconfig/2019-10-09/paginators-1.json,sha256=ppbgndgaiJUyRCfgCtIp9OZRZYSK96tzwDHXEk4hA6g,1874 +botocore/data/appconfig/2019-10-09/service-2.json.gz,sha256=Fx2seRA1fOXGW3gOtlScIsJ_xa9G9bOb7Mw6sy0Nrlw,24857 +botocore/data/appconfig/2019-10-09/waiters-2.json,sha256=1_6Y2OdolOE882ZkaU3E11-WALKr3g4cj_KZwxW_jmc,1217 +botocore/data/appconfigdata/2021-11-11/endpoint-rule-set-1.json.gz,sha256=CX0FGoyl0llYjpSol5-WmpmvQ4Oh7c56821WCFFJRRE,1235 +botocore/data/appconfigdata/2021-11-11/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/appconfigdata/2021-11-11/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/appconfigdata/2021-11-11/service-2.json.gz,sha256=7-5GUV2_20MK9NR6I5IX2IARjEYbuWETfNs5lhlPEes,3114 +botocore/data/appfabric/2023-05-19/endpoint-rule-set-1.json.gz,sha256=JZLsVgTrb0KrMwFkPXGkP85nKDHdnz-_gGYlnovWJ9Y,1296 +botocore/data/appfabric/2023-05-19/paginators-1.json,sha256=AceDN9kDs832sLebyXTQMYza-dMZ8m2hsVyzbqxUXnQ,745 +botocore/data/appfabric/2023-05-19/service-2.json.gz,sha256=YMmOZipr66Gwn2UMgyozJ9ktywLlg7G8Fa0JcrH8nM4,8601 +botocore/data/appfabric/2023-05-19/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/appflow/2020-08-23/endpoint-rule-set-1.json.gz,sha256=WCNWhyi5Di32lLwmPOUT3wkz89NyHZ0N3fGKNiGjT_U,1149 +botocore/data/appflow/2020-08-23/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/appflow/2020-08-23/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/appflow/2020-08-23/service-2.json.gz,sha256=DQsyU0oKfyWAuy9AJ-KX9h8QzMYNhET4v0acnUF3UPI,32997 +botocore/data/appintegrations/2020-07-29/endpoint-rule-set-1.json.gz,sha256=0B_2qK1pgGbeHxp8FKcazCOyk29SxEvlyNV5GMKIKyY,1153 +botocore/data/appintegrations/2020-07-29/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/appintegrations/2020-07-29/paginators-1.json,sha256=BYTiBgFZxnU-sQgufFZqZnJtClnQxZqlwuhNGC6J1vw,1147 +botocore/data/appintegrations/2020-07-29/service-2.json.gz,sha256=S3suC4Jg1iUdDOovznMGWHBfS3-Tvvr7o6eFB7ij_8c,7577 +botocore/data/application-autoscaling/2016-02-06/endpoint-rule-set-1.json.gz,sha256=bjdEA97bafZp2Uakt9RT6HWZ2YKdOpu3D0iMvpiF5AY,1244 +botocore/data/application-autoscaling/2016-02-06/examples-1.json,sha256=_IICzVD2rqZHmWHwRCsR313_WXRitdmWhlhDtSzomVE,8473 +botocore/data/application-autoscaling/2016-02-06/paginators-1.json,sha256=Yg5NHu8W50qc_r8JCtkNGMbKd861R4w8wQFdrbV0rR0,751 +botocore/data/application-autoscaling/2016-02-06/service-2.json.gz,sha256=u6Mz_EBJNKaSyY7tY661BufA0LAzST7Im7d68ct2AWw,24599 +botocore/data/application-insights/2018-11-25/endpoint-rule-set-1.json.gz,sha256=QVTyanu41CDuSiy9w4maRa4uj2fT_9M4IZeMws6DR2k,1158 +botocore/data/application-insights/2018-11-25/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/application-insights/2018-11-25/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/application-insights/2018-11-25/service-2.json.gz,sha256=HSOJqqCQAkAFLdMukB-H2ktfhdZ2BR7XtVekmJc48bU,12494 +botocore/data/application-signals/2024-04-15/endpoint-rule-set-1.json.gz,sha256=dAcgCVjUl_56Aq7ijTll42qMzh9tEnfwfVCKDG-0ae8,840 +botocore/data/application-signals/2024-04-15/paginators-1.json,sha256=sbxyOQ4TNJZ8Agrj_gWNSyvecut5zgOImRDwW5dayC8,1840 +botocore/data/application-signals/2024-04-15/paginators-1.sdk-extras.json,sha256=CL85gfnAb2jfMtjg_KuEmio7YfbCdZrRfy1NB0MuqWM,1445 +botocore/data/application-signals/2024-04-15/service-2.json.gz,sha256=ix-rKW8ergynIyeBG6SnHuIoHxaOCIYgcUHN05qgpag,35103 +botocore/data/application-signals/2024-04-15/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/applicationcostprofiler/2020-09-10/endpoint-rule-set-1.json.gz,sha256=-rapJfGcRvmsjAMWPfUVaCa6BKI5VF2AQEKaaA0j0D0,1164 +botocore/data/applicationcostprofiler/2020-09-10/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/applicationcostprofiler/2020-09-10/paginators-1.json,sha256=2by8SKjvkqf2tkVd1NxlMiNsOoEUr6V3LekGj4k4yWg,205 +botocore/data/applicationcostprofiler/2020-09-10/service-2.json.gz,sha256=0bnM5iZMTiPOxXXItHXn-hvgsUkDLBra29VVzj9VhyQ,2850 +botocore/data/appmesh/2018-10-01/endpoint-rule-set-1.json.gz,sha256=tmZZhUYzEPVqHvMO05HSds0R-1Z0Qg6ZBCxPkiXebvo,1289 +botocore/data/appmesh/2018-10-01/examples-1.json,sha256=IKnIAQr_hsb-b42MXo7jKoBKd1lTzVS0bsbWMSTIwg8,41 +botocore/data/appmesh/2018-10-01/paginators-1.json,sha256=-TPoHMW78DG37BJz5SNi67CsUIs4PTTccyUhlXtMBm4,665 +botocore/data/appmesh/2018-10-01/service-2.json.gz,sha256=nBQC8f8tQyOxi2RGk4vwb0RvduIdWoqFu_BN-XDT_sA,7902 +botocore/data/appmesh/2019-01-25/endpoint-rule-set-1.json.gz,sha256=4MUDEuS0-9OJk2yWcGyYLMY-l8ZYL3b5ErNAbPLiLts,1149 +botocore/data/appmesh/2019-01-25/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/appmesh/2019-01-25/paginators-1.json,sha256=z6PCEVS0COSk5Nf9KXgXsZ3I9gcq9whv7yonh8s1YMM,1334 +botocore/data/appmesh/2019-01-25/service-2.json.gz,sha256=qgFIb2jERBiR7BGJhEKcJZuGHSaIJY7claTQGtVJOyk,23271 +botocore/data/apprunner/2020-05-15/endpoint-rule-set-1.json.gz,sha256=9FXfBGiAXmC3Xvu9zx7y1iQXF3PBpFnzqZ2mAxXKf4Q,1149 +botocore/data/apprunner/2020-05-15/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/apprunner/2020-05-15/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/apprunner/2020-05-15/service-2.json.gz,sha256=ZEUj8VkY8E_w4OF4rW6fcGTujvUBpg7S1p5vJ7EWmKs,19689 +botocore/data/appstream/2016-12-01/endpoint-rule-set-1.json.gz,sha256=eon80oVZqlT_7vnc8k1KoX3sMK1JDE3vda_G4K79BWk,1242 +botocore/data/appstream/2016-12-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/appstream/2016-12-01/paginators-1.json,sha256=agUpesJpo5f8dC0kH0m0asMYWn4N4MUHae5LK8W0Hwo,1584 +botocore/data/appstream/2016-12-01/service-2.json.gz,sha256=6su0KKDaQAe2KXXlj1W5HBq7UhFT319ACUT7dA8Hepo,41613 +botocore/data/appstream/2016-12-01/waiters-2.json,sha256=XZ1LQBLoJ56YEhaTqi2Bs5XKhax6pr9LRsQVIo7kHck,1245 +botocore/data/appsync/2017-07-25/endpoint-rule-set-1.json.gz,sha256=eppj2Y30R1O-4u18jE2EDhzJCB1ZcBvT3nvQ0wfpcCQ,1149 +botocore/data/appsync/2017-07-25/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/appsync/2017-07-25/paginators-1.json,sha256=dFnt5T5D3rtFs2xAFiCK0lj-5A8p_ZNysOMx2vA-vFo,2052 +botocore/data/appsync/2017-07-25/service-2.json.gz,sha256=8Q0YfvhkTxuStNiigBvBoq1aU1dAdLULxLvIlzTGkPA,31441 +botocore/data/arc-region-switch/2022-07-26/endpoint-rule-set-1.json.gz,sha256=gpEHCtUEZIHyFar_uFAMERJRzvjLRfER92_xzfLo6Q4,1320 +botocore/data/arc-region-switch/2022-07-26/paginators-1.json,sha256=lKHxyZYMAgmMltw8ywaulwzexlUupI41dKsN_5RKmng,1577 +botocore/data/arc-region-switch/2022-07-26/paginators-1.sdk-extras.json,sha256=_vu9QPrvC62Ulelk6yrVIW2sDUNffoAanPR3mfx48Xc,759 +botocore/data/arc-region-switch/2022-07-26/service-2.json.gz,sha256=Q0OAx22C_45nADL5H6gMC2UPDcbk6-prbL_9L7Eyh7E,16199 +botocore/data/arc-region-switch/2022-07-26/waiters-2.json,sha256=LCN4G74d_dY3Oed3NEBHvbMUvXZLg5Km0ioiJte6nW4,1499 +botocore/data/arc-zonal-shift/2022-10-30/endpoint-rule-set-1.json.gz,sha256=hLLe_xbXZL_0Rj3gNm9S00E4MjfQzDZ5GrcErsiXmYE,1305 +botocore/data/arc-zonal-shift/2022-10-30/paginators-1.json,sha256=wx99_DrI6RWKkZuUiP1HQ1xacRiIoUsgPuxVHGpvZGU,515 +botocore/data/arc-zonal-shift/2022-10-30/service-2.json.gz,sha256=OhR9j0T3_No7fZjHCzLZBFUj1C4qeHYmaLgvqfqo-OQ,10676 +botocore/data/arc-zonal-shift/2022-10-30/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/artifact/2018-05-10/endpoint-rule-set-1.json.gz,sha256=Osv0MY58ho87z9GSMtEEbCrdGO6L5W0stNWv4iRzOpU,1378 +botocore/data/artifact/2018-05-10/paginators-1.json,sha256=SOsISH3KfwJT49TwsroCLXisnSdGqbkR-bufB1h6HlQ,895 +botocore/data/artifact/2018-05-10/service-2.json.gz,sha256=DwdyMfyHe2vHaW--gl9aIcT1Vol1Kal4bg6v1cKl3KU,6460 +botocore/data/artifact/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/athena/2017-05-18/endpoint-rule-set-1.json.gz,sha256=grCuBlhatKMj0vPeeQXzkimS6rDyVMceGi2AT-IJGN8,1147 +botocore/data/athena/2017-05-18/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/athena/2017-05-18/paginators-1.json,sha256=lLXYrCWDDFVhjAdFEhKyoc0-zEe2YYUM4nR9vXRBDgE,1330 +botocore/data/athena/2017-05-18/service-2.json.gz,sha256=xSGenGM_BlFfqYp7pqG0O7pt-GX_iJWv902iTqsLr9o,33896 +botocore/data/auditmanager/2017-07-25/endpoint-rule-set-1.json.gz,sha256=ZClztHelYOp04ikS-VpWKoCTvpkfakrx_H4sAUH3D9c,1152 +botocore/data/auditmanager/2017-07-25/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/auditmanager/2017-07-25/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/auditmanager/2017-07-25/service-2.json.gz,sha256=AYNj8gPjr6TuS0OUeKLWXV4llwuCako_KHSVwlOQH0A,27948 +botocore/data/autoscaling-plans/2018-01-06/endpoint-rule-set-1.json.gz,sha256=KgKR54Q1khWEIaBluBt7oZ7n1etiHXtvrnjI5BxYpsg,1239 +botocore/data/autoscaling-plans/2018-01-06/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/autoscaling-plans/2018-01-06/paginators-1.json,sha256=Au_RY0jJAvQZ-sAmZQk8FXYyrw1rDVD4YILlb6sDxh8,389 +botocore/data/autoscaling-plans/2018-01-06/service-2.json.gz,sha256=bYQOoWGt6Oibg38UB7RkJNXwyajAnvQ71Uyy0W9BCPo,9106 +botocore/data/autoscaling/2011-01-01/endpoint-rule-set-1.json.gz,sha256=Dt07PeaXUKGhHjRW6eO9K8NN4cX8ZErAhdZPPU8cgdw,1236 +botocore/data/autoscaling/2011-01-01/examples-1.json,sha256=-VLit9j2MnCph5AkDejxys_Iqt3JaUweEkC1B0_37j4,54289 +botocore/data/autoscaling/2011-01-01/paginators-1.json,sha256=hM_o0QSb61rvEQvua3IVpSLBUVCEy2BcwdQv1D_wSXk,2033 +botocore/data/autoscaling/2011-01-01/paginators-1.sdk-extras.json,sha256=FWBD5vKeS-MHcMzdipl2xKN3ddQu81Dk19sMd_82lKs,177 +botocore/data/autoscaling/2011-01-01/service-2.json.gz,sha256=K_3_-TVAD01b1ILk0ZQHJerM9uUZ38QrUP0xKXyOXm0,66248 +botocore/data/b2bi/2022-06-23/endpoint-rule-set-1.json.gz,sha256=CdfxLozyf-bJHcyJV6dXSlIaVJyIN7syEZVVZ5MhRUo,1296 +botocore/data/b2bi/2022-06-23/paginators-1.json,sha256=7ttS6Z0bHTlax4HX4atDWB9qbLUxoE9OTzdYeT62jiE,697 +botocore/data/b2bi/2022-06-23/service-2.json.gz,sha256=gBcgqh6D38x4sPZpYBDyv5wxpbHX94_y95QmS-IC-dc,19765 +botocore/data/b2bi/2022-06-23/waiters-2.json,sha256=QMq6U9zbKkK3L-Tn-wU5690g011Rd4U2AUe93h__Arg,451 +botocore/data/backup-gateway/2021-01-01/endpoint-rule-set-1.json.gz,sha256=COjfvSq4TPGZ7dJbrbkoDZUdndVnmj7ApksDPSLlzAE,1154 +botocore/data/backup-gateway/2021-01-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/backup-gateway/2021-01-01/paginators-1.json,sha256=SBncJ16jo9My_HRd-t9A7KPTxlId0ZP7A9JGuJ8tsiA,531 +botocore/data/backup-gateway/2021-01-01/service-2.json.gz,sha256=r4sMSt5sXWUBhcUMdXSgvF2yFGyGrg4qL7APPeYW1_s,7566 +botocore/data/backup/2018-11-15/endpoint-rule-set-1.json.gz,sha256=33UPEayYLh1OxRb9ZgjKhGFjcHpzUqID8hwOppqspfc,1147 +botocore/data/backup/2018-11-15/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/backup/2018-11-15/paginators-1.json,sha256=TjHKWue6HadvYdVIVPSnuaHP5MDFqCk1Aoi8e0v6ulU,4547 +botocore/data/backup/2018-11-15/paginators-1.sdk-extras.json,sha256=5L_0CmNaoKhsX9pvnJgxZ31pDmYLjQF2BoJhpyhUuws,208 +botocore/data/backup/2018-11-15/service-2.json.gz,sha256=hjKKksMR6cRZhPdtKlKAA8nCKOo07UWGlAV10ft_kz4,68157 +botocore/data/backupsearch/2018-05-10/endpoint-rule-set-1.json.gz,sha256=TrGTDCBc4K8rgaHd6t-oWOfkd5mI80-GW47DbAtXa5M,837 +botocore/data/backupsearch/2018-05-10/paginators-1.json,sha256=yHP69vTZn8UYtEwENmKyNQAb4VJ4_dvo2jF_qze7r4Y,707 +botocore/data/backupsearch/2018-05-10/service-2.json.gz,sha256=LJlDB_g7C_dLbGW0nXSTLbQk0jg92JiVRr6lpAcAr8E,7506 +botocore/data/backupsearch/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/batch/2016-08-10/endpoint-rule-set-1.json.gz,sha256=hIaiIxZ55lNfNqQMboepezqYV9VGtOftPH9p_4lumZg,1266 +botocore/data/batch/2016-08-10/examples-1.json,sha256=OVGvwREzgw_LYc8FpiMwLMNKVBoPq2uadWkT4icK_aM,20292 +botocore/data/batch/2016-08-10/paginators-1.json,sha256=C6GIAnxX0gGN7QakpZpoJl60UuKZBwV5Bp_Hc9HllWs,1790 +botocore/data/batch/2016-08-10/service-2.json.gz,sha256=CnC-0_lrXpCu6oWVmsI9n4QrXmwNODJ0A3ORNA11nP0,74726 +botocore/data/bcm-dashboards/2025-08-18/endpoint-rule-set-1.json.gz,sha256=0Ha1w1RGfnnTnmgtxZPUNfMkvTftrwRpDjvIDXVmPyc,903 +botocore/data/bcm-dashboards/2025-08-18/paginators-1.json,sha256=rmWg3V-3JLKmpdBf7NM5tNOzybDtdw52soENJsOQoRs,369 +botocore/data/bcm-dashboards/2025-08-18/service-2.json.gz,sha256=UfwgKj6g3bCV2jsr8g1ryA3TMs1i8ff-AfPmcunk6zA,8907 +botocore/data/bcm-dashboards/2025-08-18/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/bcm-data-exports/2023-11-26/endpoint-rule-set-1.json.gz,sha256=3Qe9YFAW7h9NIpmle_UMD_FoJSWrtq4kGj5u8EnP-Yc,1192 +botocore/data/bcm-data-exports/2023-11-26/paginators-1.json,sha256=O6FqSUDC5izLwZBKGsqYvMoy2ROOd85-Hb7II57VJoY,509 +botocore/data/bcm-data-exports/2023-11-26/service-2.json.gz,sha256=q1-WL9CU_5laey6Sdofv2o4xT8TRGZYmnofD7GnCPMk,5329 +botocore/data/bcm-pricing-calculator/2024-06-19/endpoint-rule-set-1.json.gz,sha256=ATPfjzCwBH9cw09E3fYEeto4DK_3oA9f7l5xLwao2Zw,908 +botocore/data/bcm-pricing-calculator/2024-06-19/paginators-1.json,sha256=2_8le9lK1cG14GXeXUw1W0WMTbfb0tRtdFstFj36z9U,1783 +botocore/data/bcm-pricing-calculator/2024-06-19/service-2.json.gz,sha256=TEH7xw153k0TvDaaSUN6osOWD5Bjsa15Cq8FqOJlc9c,13412 +botocore/data/bcm-pricing-calculator/2024-06-19/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/bcm-recommended-actions/2024-11-14/endpoint-rule-set-1.json.gz,sha256=chak5F28cbYJ2X_9YqqOorMqp-Tngy015PyB4uikpKw,909 +botocore/data/bcm-recommended-actions/2024-11-14/paginators-1.json,sha256=IVWKoMWh9816owS8FS9WRNbQSsxNHHf2zdJs9WlFSZc,207 +botocore/data/bcm-recommended-actions/2024-11-14/service-2.json.gz,sha256=foWteRKRfFLLJ_00ZluZ331UyekpoS2lFR5nV2boPAw,2575 +botocore/data/bcm-recommended-actions/2024-11-14/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/bedrock-agent-runtime/2023-07-26/endpoint-rule-set-1.json.gz,sha256=b9FOa5bvGkcYYf7h8uP-D3GZrEvM8ArbrHAmDLkO3Ak,1309 +botocore/data/bedrock-agent-runtime/2023-07-26/paginators-1.json,sha256=C9qRMLkjskdop4L8GtJ_lTOeTAbdbUZxrB96tbMXE54,1344 +botocore/data/bedrock-agent-runtime/2023-07-26/paginators-1.sdk-extras.json,sha256=pTkYapptXTqJqkdaW-BtMU5clcvuIMpBrZsT72LGBsg,163 +botocore/data/bedrock-agent-runtime/2023-07-26/service-2.json.gz,sha256=6wYVQ5U1ehyVGo0jDfy6ft08eTrLBpuFwGSzCQTYEFo,60942 +botocore/data/bedrock-agent-runtime/2023-07-26/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/bedrock-agent/2023-06-05/endpoint-rule-set-1.json.gz,sha256=4h7fGVCUhO-NSlIx7_4MciWRboX6rq9si9g9qoqFTO4,1303 +botocore/data/bedrock-agent/2023-06-05/paginators-1.json,sha256=U9J38CVTSn0a9vYDMsY1oVdFtmBNvQbJX_Q3od3G6yE,2687 +botocore/data/bedrock-agent/2023-06-05/service-2.json.gz,sha256=P_ZXe2dFllHptDPP_4xdE9nO1EqhWmN-W-oAQHHAl-4,67343 +botocore/data/bedrock-agent/2023-06-05/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/bedrock-agentcore-control/2023-06-05/endpoint-rule-set-1.json.gz,sha256=6A3mmZhStutm9OszvZjX3E-jzbELdFyZfRThldUqYHs,1312 +botocore/data/bedrock-agentcore-control/2023-06-05/paginators-1.json,sha256=ZHHz1I9bx3CnLTZqBthVadzpIOnt249f6XujSvlkDok,6887 +botocore/data/bedrock-agentcore-control/2023-06-05/paginators-1.sdk-extras.json,sha256=LyfXxNjZeJaPLSrQdwhTB_W9ewP8LkH16FPrlVvtooQ,250 +botocore/data/bedrock-agentcore-control/2023-06-05/service-2.json.gz,sha256=j2lCIKZ00cdy6_ixKnVQUgZmczB7OFep6I9gTZizCV4,130787 +botocore/data/bedrock-agentcore-control/2023-06-05/waiters-2.json,sha256=s98ZhpnDshd4HJf5myBnhj5S6MPAYEsZGN70BHMzmVs,3961 +botocore/data/bedrock-agentcore/2024-02-28/endpoint-rule-set-1.json.gz,sha256=c2u_nr84ePsYSb1yVnuAB6CBJHDTCzugftxvlROJR_M,1306 +botocore/data/bedrock-agentcore/2024-02-28/paginators-1.json,sha256=V-PV3DtsvDh3y7f5bbOn9aRagO-jrHabJCKgqrN2E7E,1933 +botocore/data/bedrock-agentcore/2024-02-28/service-2.json.gz,sha256=W-FeO-pRHteCTCgQHOK03jvbjSLav7Jpux7mvCuZfyI,68450 +botocore/data/bedrock-agentcore/2024-02-28/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/bedrock-data-automation-runtime/2024-06-13/endpoint-rule-set-1.json.gz,sha256=F_vYtnBazghCpeqkf27rj_26IuPla__YL4MpKF4koGY,1317 +botocore/data/bedrock-data-automation-runtime/2024-06-13/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/bedrock-data-automation-runtime/2024-06-13/service-2.json.gz,sha256=OKTJFI_RrYlRg2pp4RthQzgsXXMerBd-lN9LeGWk4CI,3514 +botocore/data/bedrock-data-automation/2023-07-26/endpoint-rule-set-1.json.gz,sha256=Tv_FvfQn-lFO_3rcmKn_lYr76qhdvm9T7GIevX5_2zs,1310 +botocore/data/bedrock-data-automation/2023-07-26/paginators-1.json,sha256=mPdM-3U-DZmAa5KviTcH7pdhwmBAO3aIgn5eLpy3we8,912 +botocore/data/bedrock-data-automation/2023-07-26/service-2.json.gz,sha256=X8blRXz0kQMRtbZm5I6cH8y847OL3n62bGmEVXaagws,10470 +botocore/data/bedrock-runtime/2023-09-30/endpoint-rule-set-1.json.gz,sha256=oxEo6lwIVBqJYBODq-v_FtQ2equKIFnibLxS0JnNaC4,1305 +botocore/data/bedrock-runtime/2023-09-30/paginators-1.json,sha256=f2V5o6U1eaWP23rP4Qybme3wfj71oUfX79uvhvsR-T0,203 +botocore/data/bedrock-runtime/2023-09-30/service-2.json.gz,sha256=GPKH-88-x-ScqmUT99P-2GxJQOeTi6C5bUSJaM4fRCg,30556 +botocore/data/bedrock-runtime/2023-09-30/waiters-2.json,sha256=tj1ZnaqhwmJkUEQlwH7wm1SqY3lg1BvZDfzfPaIgNrY,38 +botocore/data/bedrock/2023-04-20/endpoint-rule-set-1.json.gz,sha256=CpNAsvbtQqBBNHHxyjRgWB675XF1FlhTaChGfTyavfs,1298 +botocore/data/bedrock/2023-04-20/paginators-1.json,sha256=RBNIRPlcl_Pa_pobqLSqalDY_2LlP48hgozNZ-ECaKE,3575 +botocore/data/bedrock/2023-04-20/service-2.json.gz,sha256=8R20jesaf9yJLxC4NfS_A-XLQbvb6SU9LJhStTvFZVI,85393 +botocore/data/bedrock/2023-04-20/waiters-2.json,sha256=tj1ZnaqhwmJkUEQlwH7wm1SqY3lg1BvZDfzfPaIgNrY,38 +botocore/data/billing/2023-09-07/endpoint-rule-set-1.json.gz,sha256=eGhwc-UqgOiGFVsVXeZFy9IZq1C332BACzNvqRjhqic,1538 +botocore/data/billing/2023-09-07/paginators-1.json,sha256=qZLYbAERooSkGNTMZAUYom-hCxDtvjpOvNT2BoB07aw,1328 +botocore/data/billing/2023-09-07/paginators-1.sdk-extras.json,sha256=BLE1hJCibH8lezkPZzytuiCmumhuzM3KI8Kg_3FveHk,230 +botocore/data/billing/2023-09-07/service-2.json.gz,sha256=gvy2G74nSLuLaiG24fS-DfAv3Okv90kuQOcNlf33I-M,15834 +botocore/data/billing/2023-09-07/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/billingconductor/2021-07-30/endpoint-rule-set-1.json.gz,sha256=Fb6Lvpc8I18-rkYNZ7-snrYAaRE_bwQ0vT3GozK9TYE,1311 +botocore/data/billingconductor/2021-07-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/billingconductor/2021-07-30/paginators-1.json,sha256=C1lDM7aIG0KK8L7HotZs6eXvTQLuxzETH2wAHQdDzqI,2192 +botocore/data/billingconductor/2021-07-30/service-2.json.gz,sha256=hVEITmFyTpL3PqPAAEockVtSq85DknUMdVTO-kMvWpE,17575 +botocore/data/billingconductor/2021-07-30/waiters-2.json,sha256=sAGuGxokCpXh7GUF-AzqqNR6DLDE-wgRMhjNJb41AHc,36 +botocore/data/braket/2019-09-01/endpoint-rule-set-1.json.gz,sha256=fZ8yq5AJ6Fhs252tc4GFvva82OotO97eLFdPIfNAHvw,1147 +botocore/data/braket/2019-09-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/braket/2019-09-01/paginators-1.json,sha256=7oQATkyhA3sCLqboUPW6D7XGmWs8prCwyfxvjbvFu8c,691 +botocore/data/braket/2019-09-01/service-2.json.gz,sha256=bGWGXcBzjxcswj0FssMkVVn3vPUVwMFUczK9VivJT10,11487 +botocore/data/budgets/2016-10-20/endpoint-rule-set-1.json.gz,sha256=rtPaOnSBJJJ-GFv4Nho_UOALWTnE63ZTGVFxsfCWcBA,1791 +botocore/data/budgets/2016-10-20/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/budgets/2016-10-20/paginators-1.json,sha256=4lIRhlnV70H90OPO79aAX2cps42vBAkZcxWDSS40zis,1512 +botocore/data/budgets/2016-10-20/service-2.json.gz,sha256=uMDISeCK9dU0eLh6Wl7Rrm-NZlZZsqEQHbDwQ4KD9eU,14694 +botocore/data/ce/2017-10-25/endpoint-rule-set-1.json.gz,sha256=Cvqw-XCVDCmcs0tTMEI39EA1IiQVde1qDDqxsPoFFxA,1937 +botocore/data/ce/2017-10-25/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/ce/2017-10-25/paginators-1.json,sha256=Ky9BBLdDrEphFKJFV-wK5EN9nalaUevMp1k_V3f_1Xw,2558 +botocore/data/ce/2017-10-25/paginators-1.sdk-extras.json,sha256=PtomDOLRX3ei-nf22GLErTLGutc5yPaMxM3vHtgo0m0,462 +botocore/data/ce/2017-10-25/service-2.json.gz,sha256=S2xWcLFr7HWplZay_d564fX-aj08LarUeW3kbeLYaqs,43998 +botocore/data/chatbot/2017-10-11/endpoint-rule-set-1.json.gz,sha256=3vER010W5PuSXWaxL2a1I47j3zbE8m77oJFS5fUEXvQ,1295 +botocore/data/chatbot/2017-10-11/paginators-1.json,sha256=dyTPHZL8UEdw0bi3HPFXTYPfk9gRppvCy5ZRh2Vmysw,1723 +botocore/data/chatbot/2017-10-11/service-2.json.gz,sha256=ZlG5H6Vo8pLFOuNuB-h4mntETTRcvzaY2MThnceRLAI,10732 +botocore/data/chime-sdk-identity/2021-04-20/endpoint-rule-set-1.json.gz,sha256=w57-BeQA4eQd2LZdNv8TbAOnmLHoi-YByFlkFQApPIw,1154 +botocore/data/chime-sdk-identity/2021-04-20/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/chime-sdk-identity/2021-04-20/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/chime-sdk-identity/2021-04-20/service-2.json.gz,sha256=q-0YC2znIpO8qSE6GKgCOeTQ8PtVSg5eIN9a4rGj6w0,8137 +botocore/data/chime-sdk-media-pipelines/2021-07-15/endpoint-rule-set-1.json.gz,sha256=4huO4gWHBRc13qcImYPB8PLZMIquHuk6lU4hg1YKy5I,1158 +botocore/data/chime-sdk-media-pipelines/2021-07-15/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/chime-sdk-media-pipelines/2021-07-15/service-2.json.gz,sha256=3Zb6RFJu7j80_qfPpGftwEiaqfBTmI9bXeO7RgSTtJ4,18799 +botocore/data/chime-sdk-meetings/2021-07-15/endpoint-rule-set-1.json.gz,sha256=nOKjVRMkwhrR0u_nhXcLljSSi3wGmIw3sUGCXZT6dmc,1154 +botocore/data/chime-sdk-meetings/2021-07-15/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/chime-sdk-meetings/2021-07-15/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/chime-sdk-meetings/2021-07-15/service-2.json.gz,sha256=dD5fFYXtXgg9348bsoXfh_xASC3f3fBeiaQc1Yrf4JI,11608 +botocore/data/chime-sdk-messaging/2021-05-15/endpoint-rule-set-1.json.gz,sha256=A03O70yrVXksyY8NLcpxNTPP-uChU6QZz-GP6A4KLz8,1154 +botocore/data/chime-sdk-messaging/2021-05-15/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/chime-sdk-messaging/2021-05-15/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/chime-sdk-messaging/2021-05-15/service-2.json.gz,sha256=qoud6ZjmdjfafZc7pOMzBFpNMQMcLitvYZPAaEe8VFw,16422 +botocore/data/chime-sdk-voice/2022-08-03/endpoint-rule-set-1.json.gz,sha256=nwq8_oVDifCkkv63T3P7fFnDgkBWRQzA0Wl_ZIGMXnM,1301 +botocore/data/chime-sdk-voice/2022-08-03/paginators-1.json,sha256=28096cSFWwRSuJQMmk9A3HNyMAH8wFdjz3F_5pukB8Q,373 +botocore/data/chime-sdk-voice/2022-08-03/service-2.json.gz,sha256=CwckiiX7yn8ibA3Wbgk-Noihkk8jtnlcnXJ178wQhf4,24199 +botocore/data/chime/2018-05-01/endpoint-rule-set-1.json.gz,sha256=ktjIijwF5u6n5VAVa5VZMykowY5AWSs29URWIrlPxew,1303 +botocore/data/chime/2018-05-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/chime/2018-05-01/paginators-1.json,sha256=eU07vcRnjLd-9RmN_aGGPffN0ZXkpMRFYD_XbcyDy3A,343 +botocore/data/chime/2018-05-01/service-2.json.gz,sha256=ajGgbMoovMMQeP49K64Qn89KeN1fWRxTWR5E5hP12ng,15342 +botocore/data/cleanrooms/2022-02-17/endpoint-rule-set-1.json.gz,sha256=rzFMvyn-Al9lSgM6Idqzr6Q_Ii6uLdw7ehsmioIbBdY,1300 +botocore/data/cleanrooms/2022-02-17/paginators-1.json,sha256=X5UMV2DYkuftkW91WWSVb8hNkAgLmc4mseqcTEApB6M,4540 +botocore/data/cleanrooms/2022-02-17/service-2.json.gz,sha256=SOxSHjvcQbmxdT_Ej3YgvzJ6f0O8w_2MI1JpexXCMow,57048 +botocore/data/cleanrooms/2022-02-17/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/cleanroomsml/2023-09-06/endpoint-rule-set-1.json.gz,sha256=pOV8jn7nH4JGHnfLnKyiceC6aMF73nRLJ5S8EKpFXXE,1302 +botocore/data/cleanroomsml/2023-09-06/paginators-1.json,sha256=-tYvoBkilgzoays9JIwkGktzi_n9C3PGlVvsTol8n7o,3170 +botocore/data/cleanroomsml/2023-09-06/service-2.json.gz,sha256=7QToBzOSQ0qZYQmfbQ-esMOh5slMSIeQDhtk-jVd118,30792 +botocore/data/cleanroomsml/2023-09-06/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/cloud9/2017-09-23/endpoint-rule-set-1.json.gz,sha256=8OQlC5Vip8NGiIyM80wciYnb5amFg1sDWJbpc7jbN7c,1149 +botocore/data/cloud9/2017-09-23/examples-1.json,sha256=Jbbei88MR8S4MFnfmPKNTEk_b1NdqqM5R6P781A23JY,9183 +botocore/data/cloud9/2017-09-23/paginators-1.json,sha256=lET7E3FWErLA8In260otKfr3_9oVSr5OTO1zcrBi28w,380 +botocore/data/cloud9/2017-09-23/service-2.json.gz,sha256=zEikzmJCtTLzDqeBKq-5zRnZPCsBSUEZXjtuROOln2U,6051 +botocore/data/cloudcontrol/2021-09-30/endpoint-rule-set-1.json.gz,sha256=uaJywUHYMzS70EIxnoVjeyR7BSrFtF5w999tb_3K5r0,1154 +botocore/data/cloudcontrol/2021-09-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudcontrol/2021-09-30/paginators-1.json,sha256=Xh6wJghPx6VpGNTTEdpRQIsrJuVeyY5FQNpNLpUdkhc,392 +botocore/data/cloudcontrol/2021-09-30/paginators-1.sdk-extras.json,sha256=9NbQ8xHg5ztdpvYFDl15_74F30ZNPFnSFDxismgvSMg,143 +botocore/data/cloudcontrol/2021-09-30/service-2.json.gz,sha256=nQoPWm-EtLZsLUFhOAmEGgERODoD0RcxjWR_p3KLduM,6492 +botocore/data/cloudcontrol/2021-09-30/waiters-2.json,sha256=US_tyuvbMcXS6IrVB8D817Gg3pGKdCuooDJKz4Ta56U,738 +botocore/data/clouddirectory/2016-05-10/endpoint-rule-set-1.json.gz,sha256=LnPtAb0AKOmBnpy98igo4k7uVmZj5lAEVro1wrI3Hzw,1398 +botocore/data/clouddirectory/2016-05-10/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/clouddirectory/2016-05-10/paginators-1.json,sha256=y8GPuHURJmdagJ3QAI5mxkAzKvdCZwcnfYt3Z-qwgAU,2808 +botocore/data/clouddirectory/2016-05-10/service-2.json.gz,sha256=nZjaR-vrjuTpvcM_oRKERrFRZsj5yzf5wo_jRknMenU,22958 +botocore/data/clouddirectory/2017-01-11/endpoint-rule-set-1.json.gz,sha256=PQBR_ez2_QMvKC1wOxhh4QQzbvqLeLHf5zsuvUdTj04,1239 +botocore/data/clouddirectory/2017-01-11/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/clouddirectory/2017-01-11/paginators-1.json,sha256=gIqmeqo-8lsyEDEVDFvc1RJfd0T7c9xN6SdMnxGvSpw,3342 +botocore/data/clouddirectory/2017-01-11/service-2.json.gz,sha256=aoNxffLZyIVm-wNMuGLdTy6kuNtZRDYt-99Jxy23uo4,24082 +botocore/data/cloudformation/2010-05-15/endpoint-rule-set-1.json.gz,sha256=lajaGCxh3tGxxQk7sdi6lXIbEHTXHfioQ_RszyZUs1I,1237 +botocore/data/cloudformation/2010-05-15/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudformation/2010-05-15/paginators-1.json,sha256=5vuz2flCJ55esh0heh10vkEXY2Md8-otAcr6l0Wd35g,3906 +botocore/data/cloudformation/2010-05-15/service-2.json.gz,sha256=Tb7amSfgfFGgsuI2Ow-cw2COG4TVQSVp4z1qNP1qxv8,85771 +botocore/data/cloudformation/2010-05-15/waiters-2.json,sha256=0fp18QpYZmIii-mio63vhFFVyREj4UBFHZbi-F2o4cE,11014 +botocore/data/cloudfront-keyvaluestore/2022-07-26/endpoint-rule-set-1.json.gz,sha256=iLLs3jT55Blhne-pp1SVTm2d99o8HGaLc20fBZf6Tts,2209 +botocore/data/cloudfront-keyvaluestore/2022-07-26/paginators-1.json,sha256=2wyrpgvniacM8xlFDnHQiCR0KVEAVJxBEpWFBcrB4Z0,180 +botocore/data/cloudfront-keyvaluestore/2022-07-26/service-2.json.gz,sha256=ZopccjNbd936YR5WSVYcU6R38stec5TUeF-4UaNpcOo,2220 +botocore/data/cloudfront/2014-05-31/endpoint-rule-set-1.json.gz,sha256=HXtY4JIzWJ1hogeRCZyYRX2SEgew_8bPGDiVRd_634c,1839 +botocore/data/cloudfront/2014-05-31/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2014-05-31/service-2.json.gz,sha256=W1-IFZY69lhmSR4Fu3PZuWCtV_up667Jxhs203cSa4o,15298 +botocore/data/cloudfront/2014-05-31/waiters-2.json,sha256=jzREqDxfIg2KbmPYOmDoYgDvy8mWAEK0w_NmEoCqhHI,1184 +botocore/data/cloudfront/2014-10-21/endpoint-rule-set-1.json.gz,sha256=HXtY4JIzWJ1hogeRCZyYRX2SEgew_8bPGDiVRd_634c,1839 +botocore/data/cloudfront/2014-10-21/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2014-10-21/service-2.json.gz,sha256=wI7OG9yPzL01H3hesV3gx5aCWW7D1yJLbewaC_R2hDg,15887 +botocore/data/cloudfront/2014-10-21/waiters-2.json,sha256=jzREqDxfIg2KbmPYOmDoYgDvy8mWAEK0w_NmEoCqhHI,1184 +botocore/data/cloudfront/2014-11-06/endpoint-rule-set-1.json.gz,sha256=HXtY4JIzWJ1hogeRCZyYRX2SEgew_8bPGDiVRd_634c,1839 +botocore/data/cloudfront/2014-11-06/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2014-11-06/service-2.json.gz,sha256=O_bBg72GcLGQcgbuJAah0TQZizFNGgauOZL1DZkX70o,15959 +botocore/data/cloudfront/2014-11-06/waiters-2.json,sha256=jzREqDxfIg2KbmPYOmDoYgDvy8mWAEK0w_NmEoCqhHI,1184 +botocore/data/cloudfront/2015-04-17/endpoint-rule-set-1.json.gz,sha256=HXtY4JIzWJ1hogeRCZyYRX2SEgew_8bPGDiVRd_634c,1839 +botocore/data/cloudfront/2015-04-17/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2015-04-17/service-2.json.gz,sha256=YuL70Fw616qfD579bL0xmRFjIx00O8JBCv2mR3C5UhA,16213 +botocore/data/cloudfront/2015-04-17/waiters-2.json,sha256=jzREqDxfIg2KbmPYOmDoYgDvy8mWAEK0w_NmEoCqhHI,1184 +botocore/data/cloudfront/2015-07-27/endpoint-rule-set-1.json.gz,sha256=HXtY4JIzWJ1hogeRCZyYRX2SEgew_8bPGDiVRd_634c,1839 +botocore/data/cloudfront/2015-07-27/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2015-07-27/service-2.json.gz,sha256=zdKoWK4SkMI3ux6gniygpkeziXOVo1Y6IIv6tisKSmI,16702 +botocore/data/cloudfront/2015-07-27/waiters-2.json,sha256=jzREqDxfIg2KbmPYOmDoYgDvy8mWAEK0w_NmEoCqhHI,1184 +botocore/data/cloudfront/2015-09-17/endpoint-rule-set-1.json.gz,sha256=HXtY4JIzWJ1hogeRCZyYRX2SEgew_8bPGDiVRd_634c,1839 +botocore/data/cloudfront/2015-09-17/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2015-09-17/service-2.json.gz,sha256=D1Zm2dFGZsByhHGjv5FCrYjkI2WKI5yJxpBpn3cE-1c,15890 +botocore/data/cloudfront/2015-09-17/waiters-2.json,sha256=jzREqDxfIg2KbmPYOmDoYgDvy8mWAEK0w_NmEoCqhHI,1184 +botocore/data/cloudfront/2016-01-13/endpoint-rule-set-1.json.gz,sha256=HXtY4JIzWJ1hogeRCZyYRX2SEgew_8bPGDiVRd_634c,1839 +botocore/data/cloudfront/2016-01-13/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2016-01-13/service-2.json.gz,sha256=_c-SRcPqP01mnbslU5-ePyMxtHwY7jorBvKsLbRqXJM,16358 +botocore/data/cloudfront/2016-01-13/waiters-2.json,sha256=jzREqDxfIg2KbmPYOmDoYgDvy8mWAEK0w_NmEoCqhHI,1184 +botocore/data/cloudfront/2016-01-28/endpoint-rule-set-1.json.gz,sha256=cNSngLZCOtUAAEhZn0bMZGVRHTjMt40lJ-u7A-kKRkk,1574 +botocore/data/cloudfront/2016-01-28/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudfront/2016-01-28/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2016-01-28/service-2.json.gz,sha256=up5xBalmWX48_34MkntyIczc_UkupKZhEKMFHhQ4paI,16279 +botocore/data/cloudfront/2016-01-28/waiters-2.json,sha256=jzREqDxfIg2KbmPYOmDoYgDvy8mWAEK0w_NmEoCqhHI,1184 +botocore/data/cloudfront/2016-08-01/endpoint-rule-set-1.json.gz,sha256=cNSngLZCOtUAAEhZn0bMZGVRHTjMt40lJ-u7A-kKRkk,1574 +botocore/data/cloudfront/2016-08-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudfront/2016-08-01/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2016-08-01/service-2.json.gz,sha256=MfqASmzUOjiILKKqDDDWW2XGKSAZ1PawOejCesqKuZs,17725 +botocore/data/cloudfront/2016-08-01/waiters-2.json,sha256=jzREqDxfIg2KbmPYOmDoYgDvy8mWAEK0w_NmEoCqhHI,1184 +botocore/data/cloudfront/2016-08-20/endpoint-rule-set-1.json.gz,sha256=cNSngLZCOtUAAEhZn0bMZGVRHTjMt40lJ-u7A-kKRkk,1574 +botocore/data/cloudfront/2016-08-20/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2016-08-20/service-2.json.gz,sha256=CN0DXviD9_q3kt8arBHHa_b_s3IJpVKw4x9HpWVyzTI,18123 +botocore/data/cloudfront/2016-08-20/waiters-2.json,sha256=jzREqDxfIg2KbmPYOmDoYgDvy8mWAEK0w_NmEoCqhHI,1184 +botocore/data/cloudfront/2016-09-07/endpoint-rule-set-1.json.gz,sha256=cNSngLZCOtUAAEhZn0bMZGVRHTjMt40lJ-u7A-kKRkk,1574 +botocore/data/cloudfront/2016-09-07/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudfront/2016-09-07/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2016-09-07/service-2.json.gz,sha256=TvA6gSo6n9v60Mxt4lZ1t9nhAkqtyuNOlwQoL2OIRx8,18444 +botocore/data/cloudfront/2016-09-07/waiters-2.json,sha256=jzREqDxfIg2KbmPYOmDoYgDvy8mWAEK0w_NmEoCqhHI,1184 +botocore/data/cloudfront/2016-09-29/endpoint-rule-set-1.json.gz,sha256=cNSngLZCOtUAAEhZn0bMZGVRHTjMt40lJ-u7A-kKRkk,1574 +botocore/data/cloudfront/2016-09-29/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudfront/2016-09-29/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2016-09-29/service-2.json.gz,sha256=zmNTODXs08dkWxLNs2y7Q2iHOP6umizD7Z0MeyTk-LE,27522 +botocore/data/cloudfront/2016-09-29/waiters-2.json,sha256=jzREqDxfIg2KbmPYOmDoYgDvy8mWAEK0w_NmEoCqhHI,1184 +botocore/data/cloudfront/2016-11-25/endpoint-rule-set-1.json.gz,sha256=cNSngLZCOtUAAEhZn0bMZGVRHTjMt40lJ-u7A-kKRkk,1574 +botocore/data/cloudfront/2016-11-25/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudfront/2016-11-25/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2016-11-25/service-2.json.gz,sha256=cVJVj7WIy_tyLaNvz_YfBs7uxTD6FJuZ_sL4mLKqW5U,27955 +botocore/data/cloudfront/2016-11-25/waiters-2.json,sha256=jzREqDxfIg2KbmPYOmDoYgDvy8mWAEK0w_NmEoCqhHI,1184 +botocore/data/cloudfront/2017-03-25/endpoint-rule-set-1.json.gz,sha256=cNSngLZCOtUAAEhZn0bMZGVRHTjMt40lJ-u7A-kKRkk,1574 +botocore/data/cloudfront/2017-03-25/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2017-03-25/service-2.json.gz,sha256=VjoiX7Pev0Au2HlAXt-IcEfMgL5TOfRdS_nW5rw1_Zk,29088 +botocore/data/cloudfront/2017-03-25/waiters-2.json,sha256=JboqzXjlni8p-wiVKBz1jRj-mFpkryqueCgI1hD7WPA,1184 +botocore/data/cloudfront/2017-10-30/endpoint-rule-set-1.json.gz,sha256=cNSngLZCOtUAAEhZn0bMZGVRHTjMt40lJ-u7A-kKRkk,1574 +botocore/data/cloudfront/2017-10-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudfront/2017-10-30/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2017-10-30/service-2.json.gz,sha256=1l4os6TzyurLddZlybaUKVHOowYATBe1AspmgCHv_nk,34767 +botocore/data/cloudfront/2017-10-30/waiters-2.json,sha256=JboqzXjlni8p-wiVKBz1jRj-mFpkryqueCgI1hD7WPA,1184 +botocore/data/cloudfront/2018-06-18/endpoint-rule-set-1.json.gz,sha256=cNSngLZCOtUAAEhZn0bMZGVRHTjMt40lJ-u7A-kKRkk,1574 +botocore/data/cloudfront/2018-06-18/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudfront/2018-06-18/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2018-06-18/service-2.json.gz,sha256=671544pDSbW9nIRPja7z3SfOySJKrDVaJLG290DWrzw,35482 +botocore/data/cloudfront/2018-06-18/waiters-2.json,sha256=JboqzXjlni8p-wiVKBz1jRj-mFpkryqueCgI1hD7WPA,1184 +botocore/data/cloudfront/2018-11-05/endpoint-rule-set-1.json.gz,sha256=cNSngLZCOtUAAEhZn0bMZGVRHTjMt40lJ-u7A-kKRkk,1574 +botocore/data/cloudfront/2018-11-05/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudfront/2018-11-05/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2018-11-05/service-2.json.gz,sha256=CnyX1Yh_aZtURQehDb_MNS77epDzz-12HSYBhh-7hwA,36144 +botocore/data/cloudfront/2018-11-05/waiters-2.json,sha256=JboqzXjlni8p-wiVKBz1jRj-mFpkryqueCgI1hD7WPA,1184 +botocore/data/cloudfront/2019-03-26/endpoint-rule-set-1.json.gz,sha256=cNSngLZCOtUAAEhZn0bMZGVRHTjMt40lJ-u7A-kKRkk,1574 +botocore/data/cloudfront/2019-03-26/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudfront/2019-03-26/paginators-1.json,sha256=I7u4h1MFflBvFJemcrLHSn7uOrEeDFc7ecWGqwDxGF8,1126 +botocore/data/cloudfront/2019-03-26/service-2.json.gz,sha256=bHSP38aF1FvptTcNsT7DL5UBCWfsDq3rcOrjHngB-0Q,37652 +botocore/data/cloudfront/2019-03-26/waiters-2.json,sha256=qt7oBhQ-B52-397Q88q0EJoFpDWuOZM7CZpaFhX1xgM,1184 +botocore/data/cloudfront/2020-05-31/endpoint-rule-set-1.json.gz,sha256=HRwpAlTUdUujRXmPJbmCIfgfo58EhuEJs2Kf8QaWLrc,1657 +botocore/data/cloudfront/2020-05-31/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudfront/2020-05-31/paginators-1.json,sha256=ASaoIzYBXx4dR4prrVpgqLWxB-zUZvsQjdqkdGbsstY,3642 +botocore/data/cloudfront/2020-05-31/service-2.json.gz,sha256=U1ui5ZHDkB9isxh88gF0IdYSvRNEv3jV3MBs9dQ68j4,90937 +botocore/data/cloudfront/2020-05-31/waiters-2.json,sha256=EBQKgBvTcueQ7pUpl3XfCyr-KY39mO_EedGvn21OpZg,1568 +botocore/data/cloudhsm/2014-05-30/endpoint-rule-set-1.json.gz,sha256=svXthStfARp-uQYMNvzryKqAk6Y36XFi5vvzmSAlYZ4,1150 +botocore/data/cloudhsm/2014-05-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudhsm/2014-05-30/paginators-1.json,sha256=pe-X06JkfqlENEk-25nE_w_q3QQXkdMnQ5cOG1NPi6E,409 +botocore/data/cloudhsm/2014-05-30/service-2.json.gz,sha256=5yjbZzX7lH3ZEpz6zi_Tr4EkxXAcyA37X_VpV-WGRAI,5581 +botocore/data/cloudhsmv2/2017-04-28/endpoint-rule-set-1.json.gz,sha256=Gh8A2CETWYxrd6H4q0wdu-Gujy5kY-pGiSOEEAoftf8,1242 +botocore/data/cloudhsmv2/2017-04-28/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudhsmv2/2017-04-28/paginators-1.json,sha256=VvCnjrdoGz3Lb-gi5YSOAhhAHzB50i0vIks0GaA2nS0,512 +botocore/data/cloudhsmv2/2017-04-28/service-2.json.gz,sha256=LjuMEX7NjoHY4MCyReqBzL6ccCZ7VxYr5H5nj8Od7_M,8056 +botocore/data/cloudsearch/2011-02-01/endpoint-rule-set-1.json.gz,sha256=aXS0t7CAotdre1a8mPC0u7hZwdKKf3A3ZrqMXmYoqmk,1149 +botocore/data/cloudsearch/2011-02-01/service-2.json.gz,sha256=Mkf5AFeEwp_kObo8-NjnJpMr2HL84JTWxrHpQuMbbFQ,9599 +botocore/data/cloudsearch/2013-01-01/endpoint-rule-set-1.json.gz,sha256=FlPZreXWQI9SsPIRmZp_jqU0W7QUyUHPd44SAuhU8jQ,1151 +botocore/data/cloudsearch/2013-01-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudsearch/2013-01-01/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/cloudsearch/2013-01-01/service-2.json.gz,sha256=SxPOpNLEaxMLCow_XHBiVK3eTGsUXhD2Z2QhCJszTdY,12107 +botocore/data/cloudsearchdomain/2013-01-01/endpoint-rule-set-1.json.gz,sha256=NnJpOcWcfBAxZHb24zkshG7vNtNoB0SgyHCANtLpgfo,1155 +botocore/data/cloudsearchdomain/2013-01-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudsearchdomain/2013-01-01/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/cloudsearchdomain/2013-01-01/service-2.json.gz,sha256=KJIKdFmPnn8FzENOAiGULHcckdI4HHQp0M94tb5m90Q,9123 +botocore/data/cloudtrail-data/2021-08-11/endpoint-rule-set-1.json.gz,sha256=LKY_rwdgZu1AWTEHEwBB3oHYKJbm6-pOoiITjlMVxrk,1304 +botocore/data/cloudtrail-data/2021-08-11/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/cloudtrail-data/2021-08-11/service-2.json.gz,sha256=IIWgFXJ2CVHUAte1TDUQ5Up34789cnMSSy9wBKcXe6M,2165 +botocore/data/cloudtrail/2013-11-01/endpoint-rule-set-1.json.gz,sha256=8Ms1wMVbpUTCZR3uYjRFWe0nUFp0n-ac3rB_ryt32jg,1232 +botocore/data/cloudtrail/2013-11-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudtrail/2013-11-01/paginators-1.json,sha256=o6vvum0HuZOUnwcmSutAt1JcJBwzSpHooZx75aRRQc0,1070 +botocore/data/cloudtrail/2013-11-01/service-2.json.gz,sha256=IeftpnJsA6tmCQSVTM4uOfldH4eJxTJhowMN66mkAaE,46005 +botocore/data/cloudwatch/2010-08-01/endpoint-rule-set-1.json.gz,sha256=jW3dKdPYZz0Jluy5rpsgpPtNHEgkcHudvfeZuLAYSn8,1415 +botocore/data/cloudwatch/2010-08-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cloudwatch/2010-08-01/paginators-1.json,sha256=0I3T15IQ8hiX8YZ_IDPZXLraE5CIu2BTKNlWhBAVx_c,1325 +botocore/data/cloudwatch/2010-08-01/service-2.json.gz,sha256=vDCPkz_tJegOOXt0ECKY2eGPbAHcG6wocFX7MpG7SVo,58379 +botocore/data/cloudwatch/2010-08-01/waiters-2.json,sha256=j2NBvR8rxqLY_xWZ-ihdF-evPhZI0urGplLxxxIpy2o,1301 +botocore/data/cloudwatchomni/2025-01-01/endpoint-rule-set-1.json.gz,sha256=IvUPnkp-MMO1eycShsYcn9PutmSfQj-4HQt45HQ_mcc,839 +botocore/data/cloudwatchomni/2025-01-01/paginators-1.json,sha256=7-91WCTbjF8TvF2Xux16l_6MIo__mIr0a7TzuuGTBkY,2550 +botocore/data/cloudwatchomni/2025-01-01/service-2.json.gz,sha256=Eigf0bl0oDGeu5513s6eKI24Ner3R5_I0n9l4OCRsw8,33082 +botocore/data/codeartifact/2018-09-22/endpoint-rule-set-1.json.gz,sha256=5E9lqjjARsWn41wNcKa8YDJOlrrP3v3g0Ggxql0NWqU,1151 +botocore/data/codeartifact/2018-09-22/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/codeartifact/2018-09-22/paginators-1.json,sha256=I3MlPdEGK-hCFxJnNpPrpWkJSBaM9dhFiZ4uo0AoR8o,1747 +botocore/data/codeartifact/2018-09-22/paginators-1.sdk-extras.json,sha256=kNVDIOe3C5yL0xTWSrW2xDchpno4Xozz60DY53uxNEA,444 +botocore/data/codeartifact/2018-09-22/service-2.json.gz,sha256=lgS7gkDfDDZW_zMmNFekbMcD8BwHvTWIgQsbZrSEZQY,23321 +botocore/data/codebuild/2016-10-06/endpoint-rule-set-1.json.gz,sha256=pAtyPWiBtBbhQ-iKUlc7WCS-mL8Ul5K1LtsEEJLvyME,1149 +botocore/data/codebuild/2016-10-06/examples-1.json,sha256=_-tVq2XM1YDuzv78VwIj_WjyXHu-yrIPyxzTtTbdFJ8,9778 +botocore/data/codebuild/2016-10-06/paginators-1.json,sha256=jNb-seZLb_i52B9YQgWA8t6xpsFoHQEiAkYGnScwEz4,2448 +botocore/data/codebuild/2016-10-06/service-2.json.gz,sha256=tzzlcw94si6Q2ZvDFtTYMLMPZ5vOgepSjOqd6IBN_yM,49073 +botocore/data/codecatalyst/2022-09-28/endpoint-rule-set-1.json.gz,sha256=mMStndZg-CgiILX1dkcmCsclkQYgUVhc70TJPEM6LZ0,849 +botocore/data/codecatalyst/2022-09-28/paginators-1.json,sha256=TuEQ6NVw_F_LgmG-TurtBCvFTRFRP8DWHseWdO8DNRk,1637 +botocore/data/codecatalyst/2022-09-28/service-2.json.gz,sha256=RFQ7VSDKs2KbornJc8Ost3eWrnEmtFq8dzcxEB9FRek,13989 +botocore/data/codecatalyst/2022-09-28/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/codecommit/2015-04-13/endpoint-rule-set-1.json.gz,sha256=dOmRdhBbX1K6s2tx-t5m5XcWxonqHHC7ogb8PqviPF4,1151 +botocore/data/codecommit/2015-04-13/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/codecommit/2015-04-13/paginators-1.json,sha256=HT07NvgXh_mL4TaRpmKoq2t2YTPRofOdVBemPC1arMA,1480 +botocore/data/codecommit/2015-04-13/service-2.json.gz,sha256=AnozD5nBA6tVcf1Vj8Dff5zu9zWTeLdplekgvnDAfxs,42861 +botocore/data/codeconnections/2023-12-01/endpoint-rule-set-1.json.gz,sha256=ZE0hA3P0QfmSiJOueMxjtCSRCc6bnRKJwlQNTPj2Dbg,1302 +botocore/data/codeconnections/2023-12-01/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/codeconnections/2023-12-01/service-2.json.gz,sha256=C-h3ccAnSqHSzGWLYOCQrHKGqN6DhHu2L185lULGDi0,10017 +botocore/data/codedeploy/2014-10-06/endpoint-rule-set-1.json.gz,sha256=8AXWT0Rt2GpnwbQuwx9yYbpLoYYsPIHJfwuidi_H9rI,1150 +botocore/data/codedeploy/2014-10-06/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/codedeploy/2014-10-06/paginators-1.json,sha256=riyMuhePXvzjx3lAoHiIaOi0U6v2lCVd65qX4UWPoxo,1313 +botocore/data/codedeploy/2014-10-06/service-2.json.gz,sha256=aYbOWjSWjxkQXTbScRql9YDRSeKI-q23dSPejDS3VNw,32394 +botocore/data/codedeploy/2014-10-06/waiters-2.json,sha256=OARBxBeZTRUui1WztkVtUn7Q2lAh3-Bemczgk455MGQ,662 +botocore/data/codeguru-reviewer/2019-09-19/endpoint-rule-set-1.json.gz,sha256=y7RGTwwOGKK3z3il-RsN0-b-13-77SE45KhGnxmh5IU,1157 +botocore/data/codeguru-reviewer/2019-09-19/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/codeguru-reviewer/2019-09-19/paginators-1.json,sha256=0bkbq9IDAtNTQOShBQuJVNtb8xgFFUYNdzOcl3ri_DM,223 +botocore/data/codeguru-reviewer/2019-09-19/service-2.json.gz,sha256=mGSvjMH4tuK79eXGDks7ljwLZ2RlUZsZzLzmP16vAKw,11785 +botocore/data/codeguru-reviewer/2019-09-19/waiters-2.json,sha256=0jf0N7KHQV4qYAOPKBKNdiExhxEvojmGQ2Jzrc9lYR4,1733 +botocore/data/codeguru-security/2018-05-10/endpoint-rule-set-1.json.gz,sha256=kZDDnZHaAFxxZvcOZf9TokXi4SYTfj58wq5anNmWJ54,1306 +botocore/data/codeguru-security/2018-05-10/paginators-1.json,sha256=nwCp854x7Q4pjInZgk9mpYoj9BiFf09ekRTXObmU4GQ,522 +botocore/data/codeguru-security/2018-05-10/service-2.json.gz,sha256=ohVM3JpephRDtHZlFBYZS-4jTNR6N1YQWroL3yLMhcA,7989 +botocore/data/codeguru-security/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/codeguruprofiler/2019-07-18/endpoint-rule-set-1.json.gz,sha256=AkNLlzZMnbKh8bTiKYSoKy7zbJBXcKwsnpJWz2fup24,1157 +botocore/data/codeguruprofiler/2019-07-18/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/codeguruprofiler/2019-07-18/paginators-1.json,sha256=d7DXbQ-GmZLDQRjjpAO-vzvm7OEA-pNKfPUyA9rgaag,195 +botocore/data/codeguruprofiler/2019-07-18/service-2.json.gz,sha256=8AqvkTPwoR_pT8g3HhYVz8wPoAZvI2mMoFAEU-Rncf0,14592 +botocore/data/codepipeline/2015-07-09/endpoint-rule-set-1.json.gz,sha256=b4n_RbpD8N4YAWXAhipWgjkvhHLjYOnCgL5mQGvkivk,1151 +botocore/data/codepipeline/2015-07-09/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/codepipeline/2015-07-09/paginators-1.json,sha256=qvL1MMRjFuod_E5JuAfBuSUwPrMBCxO6zsfl4mEDYH8,1386 +botocore/data/codepipeline/2015-07-09/service-2.json.gz,sha256=_DgMsasEucDOsM33xFiyanWNk3Bd_IeF_1zyw2114yc,36738 +botocore/data/codestar-connections/2019-12-01/endpoint-rule-set-1.json.gz,sha256=U3EHcdPyZQ9QNmteUaitbrienpz5oP70q-YZ8kiie-U,1154 +botocore/data/codestar-connections/2019-12-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/codestar-connections/2019-12-01/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/codestar-connections/2019-12-01/service-2.json.gz,sha256=XivXE5zAsr9nDKcpu0nVcWwOppyyPXfy6KuF8D1lA5k,9897 +botocore/data/codestar-notifications/2019-10-15/endpoint-rule-set-1.json.gz,sha256=YpFdQfeneubg4pcopUpdvvtsL6Olm5U-Ag7HgUqgG94,1157 +botocore/data/codestar-notifications/2019-10-15/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/codestar-notifications/2019-10-15/paginators-1.json,sha256=bD6rBB54kEd5ns5mM8KWWE2Gfs6rNkRWTLyvKHai9OA,531 +botocore/data/codestar-notifications/2019-10-15/service-2.json.gz,sha256=uNYXVLWdHMtglTRw5T4dZIott5rcq9TdD_XOsJsFY8Y,5570 +botocore/data/cognito-identity/2014-06-30/endpoint-rule-set-1.json.gz,sha256=xK7juV5Td9Kmr4aknw6YMjZBc6rZTpZ4MySUNcNeTHE,1348 +botocore/data/cognito-identity/2014-06-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cognito-identity/2014-06-30/paginators-1.json,sha256=iRnVNYNjXj4riBW6sjwmAF2p9fSX2MkfoM5W_Y9_tkE,197 +botocore/data/cognito-identity/2014-06-30/service-2.json.gz,sha256=DGdJxgWqeLKAqMCkGgUTRHLC4BzlenSUnLysGCS0TC8,10267 +botocore/data/cognito-idp/2016-04-18/endpoint-rule-set-1.json.gz,sha256=UpZYAljao4sxHMp_FfIt9Lpw5vYPzI-EAyNNK5RgPoI,1344 +botocore/data/cognito-idp/2016-04-18/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cognito-idp/2016-04-18/paginators-1.json,sha256=RdlZ6K9kobwkGd7v3X15eeU_1MTpNNWwHD35RwB4keA,1527 +botocore/data/cognito-idp/2016-04-18/service-2.json.gz,sha256=DOWV7YJDsce8TGN_qNEY1SmU4241sau_BSDuhCfMAqc,125970 +botocore/data/cognito-sync/2014-06-30/endpoint-rule-set-1.json.gz,sha256=vLTs8op2mhuUThDdK7t9tvGvbYOPnNsGwurZ3peazDk,1153 +botocore/data/cognito-sync/2014-06-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/cognito-sync/2014-06-30/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/cognito-sync/2014-06-30/service-2.json.gz,sha256=xJcGMlvbJQodPVQDH4E3q4nBKu7iiNV2Ye__59NTeQY,7342 +botocore/data/comprehend/2017-11-27/endpoint-rule-set-1.json.gz,sha256=Uawl8uL-U6EJOMuIqzEViynuKaz4nM-8qGRvYSnfoh0,1150 +botocore/data/comprehend/2017-11-27/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/comprehend/2017-11-27/paginators-1.json,sha256=aCozRajzUb4wblnxzb_bTJlztnDFC3PnwItAMek2WtY,2033 +botocore/data/comprehend/2017-11-27/service-2.json.gz,sha256=lgpLnc6m3Ungb-4gyC1ikQxR9bbAoMEgKLXZGpI2AC0,43058 +botocore/data/comprehendmedical/2018-10-30/endpoint-rule-set-1.json.gz,sha256=DI2ngMiVvOlrr2fATNeHj6veXaoNoFw92UpyMLjPnMI,1155 +botocore/data/comprehendmedical/2018-10-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/comprehendmedical/2018-10-30/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/comprehendmedical/2018-10-30/service-2.json.gz,sha256=K1VRo1XFf2Y__I-EWvk0r8v4oA0dMjnLCv4PTLqZWzc,10354 +botocore/data/compute-optimizer-automation/2025-09-22/endpoint-rule-set-1.json.gz,sha256=dBy5q34IH9IyFgwzXHTHPmwoYVxNlqhkVr_u-VGCqQg,1302 +botocore/data/compute-optimizer-automation/2025-09-22/paginators-1.json,sha256=25BBuPwHP0Glc_fbh2x7tYdAxRQLlh5OqzXISl_Du1g,1680 +botocore/data/compute-optimizer-automation/2025-09-22/service-2.json.gz,sha256=uuNlQs_aEiUfbhuLwZoZ-QrBCQKczajcHGUc-XVnGAU,11938 +botocore/data/compute-optimizer-automation/2025-09-22/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/compute-optimizer/2019-11-01/endpoint-rule-set-1.json.gz,sha256=Gf70LJkuteLK2HxILeiclwLiBoRyYdm_HfcxhTxGs1s,1156 +botocore/data/compute-optimizer/2019-11-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/compute-optimizer/2019-11-01/paginators-1.json,sha256=FBFfvnKfuzo8mWExsEqu3Gy9-nKsBwTxjoRuqT-_oU0,1022 +botocore/data/compute-optimizer/2019-11-01/service-2.json.gz,sha256=uGRTqjk8nMjHLOjxbt9r4dwFOJEAjI3Iynsj9lRg5-I,41973 +botocore/data/config/2014-11-12/endpoint-rule-set-1.json.gz,sha256=4beiT5lSsi_jjo709-h2b3T2EDnDAazhPDoGcqeE3Bo,1231 +botocore/data/config/2014-11-12/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/config/2014-11-12/paginators-1.json,sha256=4BsSN1BFphBX8ipWrJydPbP2V5-pBjY1Jyw0vAnhCqo,6655 +botocore/data/config/2014-11-12/service-2.json.gz,sha256=n99udTdPVdq5nBvKPD_BYdN6xvlrZGy1zhMCn3p8jNE,69305 +botocore/data/connect-contact-lens/2020-08-21/endpoint-rule-set-1.json.gz,sha256=auJfrA5VZ4D9JDCxx0311zb2jhRVwxoUxsIySoRLZmw,1152 +botocore/data/connect-contact-lens/2020-08-21/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/connect-contact-lens/2020-08-21/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/connect-contact-lens/2020-08-21/service-2.json.gz,sha256=0UnDUoDPCMFbf74yv84v9Ry-Z0zJdHceU6QHMl7C1pg,3762 +botocore/data/connect/2017-08-08/endpoint-rule-set-1.json.gz,sha256=EH3qiLLIsZ4NtmE4RpEHyBDxmaD3NezjSIfgfhvUs6Y,1230 +botocore/data/connect/2017-08-08/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/connect/2017-08-08/paginators-1.json,sha256=r-14F4UBnqIN34oI-7m1p-qwch-IiHpANnv1bZE1I3Y,18180 +botocore/data/connect/2017-08-08/service-2.json.gz,sha256=vfnpQT9Kn5UbUXauryYLjJZcGtfeBOl94p0uG6U4FzU,224660 +botocore/data/connectcampaigns/2021-01-30/endpoint-rule-set-1.json.gz,sha256=6BYPZPiN61WKQ9cUEXuN1eJfBHTlGqDv3uMi2OZoZc4,1156 +botocore/data/connectcampaigns/2021-01-30/paginators-1.json,sha256=0u4LcBZFpshvXnakuryTCgfVdLeSI-dpWmlZds4eVWs,199 +botocore/data/connectcampaigns/2021-01-30/service-2.json.gz,sha256=7yYXhXm0lbkcdMi39LSOvH84xzzeBtDuI-u3wrQH4WM,5276 +botocore/data/connectcampaignsv2/2024-04-23/endpoint-rule-set-1.json.gz,sha256=5rpTa4YhBZa2b1drcAEqT0HFrh23OxobE7qBPZQ9fY4,1306 +botocore/data/connectcampaignsv2/2024-04-23/paginators-1.json,sha256=LMkaF55JCeS3JzOF7c4eW3ZqvVCDHNHTmJCpGQ8wpi0,394 +botocore/data/connectcampaignsv2/2024-04-23/service-2.json.gz,sha256=c566qWdDE_kqbTbVPJsVjmelnLTTkSF-zoH6ge7YG1A,10490 +botocore/data/connectcases/2022-10-03/endpoint-rule-set-1.json.gz,sha256=Log0Y7xwm5l9t1box4HVEJWHe-rnfGDkvAr3En6ekBA,1293 +botocore/data/connectcases/2022-10-03/paginators-1.json,sha256=CBQn85TwOMroUn8rqB689UtbJkuBCAX371eSdVbZRAc,694 +botocore/data/connectcases/2022-10-03/paginators-1.sdk-extras.json,sha256=V-Z6VQ22pyeSs8orbHV6TP1FU7febzQNgjAyzLwnyIc,160 +botocore/data/connectcases/2022-10-03/service-2.json.gz,sha256=-in7Rneb_vAIImuKpJT8SPCWUXjrkgOZ9sZdwIPCO1E,20281 +botocore/data/connectcases/2022-10-03/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/connecthealth/2025-01-29/endpoint-rule-set-1.json.gz,sha256=az4ByBNw8JdPRJaaFHmx6dxdW5E8CBCmADXjE11IKaU,837 +botocore/data/connecthealth/2025-01-29/paginators-1.json,sha256=q3okpGlYsMCRGlDc0QLdNUAEvWTnkRLvqWcIie2dw90,357 +botocore/data/connecthealth/2025-01-29/service-2.json.gz,sha256=qpc6WEIHW-Gi7Fts6oKjQ5mkKfKUAQoh3JvLgXCvD4I,8130 +botocore/data/connecthealth/2025-01-29/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/connectparticipant/2018-09-07/endpoint-rule-set-1.json.gz,sha256=JPDdXTgZXwM3Ln8upDjDejy6vuvOhFzUpXVFM91l9bU,1238 +botocore/data/connectparticipant/2018-09-07/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/connectparticipant/2018-09-07/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/connectparticipant/2018-09-07/service-2.json.gz,sha256=1T_G5UJQyTqfHQqMY3G6CufuDK-BbwWk5lp_auKRUoU,8854 +botocore/data/controlcatalog/2018-05-10/endpoint-rule-set-1.json.gz,sha256=t0t0mtEKF2fB8FYAw6rl8xzPPZ5h4ANHljKCBrI0mZ0,1302 +botocore/data/controlcatalog/2018-05-10/paginators-1.json,sha256=4NXGNZRHFcTeivBOzG6lJUAPu3RKCzcLAhvgtDKjG4g,863 +botocore/data/controlcatalog/2018-05-10/service-2.json.gz,sha256=duakzzTWRDpO3d4z2kMqXmL1g8v_RVxjSs3Mz5qXkAE,7796 +botocore/data/controlcatalog/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/controltower/2018-05-10/endpoint-rule-set-1.json.gz,sha256=XwCrWE45wMHnwBTAZx4uzGJvc5_ai_lwVaXDg_JziTE,1151 +botocore/data/controltower/2018-05-10/paginators-1.json,sha256=fnUyaumVMU4LxD6VRfutlQ549Lr8SBKeWce0mqgA0uM,1081 +botocore/data/controltower/2018-05-10/service-2.json.gz,sha256=oCJjgJHtATH4NqhxQrmAHz8vGCbk-fnAWvZde8Q18VE,14049 +botocore/data/controltower/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/cost-optimization-hub/2022-07-26/endpoint-rule-set-1.json.gz,sha256=LTE8U_BC2vJSiUKH_NplgrcT_Vp1vhNE4URzjJuLYHI,1309 +botocore/data/cost-optimization-hub/2022-07-26/paginators-1.json,sha256=nc9cjDY9Czbo6fxvbnoL0-JpxcIuCbeBgscEFYlPAuE,721 +botocore/data/cost-optimization-hub/2022-07-26/paginators-1.sdk-extras.json,sha256=O4h78RRWkbXvEDne3NyxM_npO-yI2KU8QL1jnJnfiwE,382 +botocore/data/cost-optimization-hub/2022-07-26/service-2.json.gz,sha256=UuSrR1NymLo7FHpROPxxTOfH5OjCx9t5aY09EOWhGqs,11126 +botocore/data/cost-optimization-hub/2022-07-26/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/cur/2017-01-06/endpoint-rule-set-1.json.gz,sha256=_XQkUTrBK4N7osrge54dO9vnglxythOyP9qOJPkEeWs,1145 +botocore/data/cur/2017-01-06/examples-1.json,sha256=NyOJJuDWe_rnuUTIp9cdvnw0GfJCK2aaDMW8Qkyf2Mg,2874 +botocore/data/cur/2017-01-06/paginators-1.json,sha256=svrnnDA-WDB_TSjNDhx_3bXmieM10GBn4TRFNlZNPHg,209 +botocore/data/cur/2017-01-06/service-2.json.gz,sha256=oY4uAjfcfK2HF_5Xw2fv2iZGVnYDJBeyoR44FNgHPyo,3835 +botocore/data/customer-profiles/2020-08-15/endpoint-rule-set-1.json.gz,sha256=CfABLLibh0WpLrs7y_HMoqxT-noNsIMUxB1G1SsUM_Y,1148 +botocore/data/customer-profiles/2020-08-15/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/customer-profiles/2020-08-15/paginators-1.json,sha256=xlNtUxOVnS_7cwQpKZyDX6BlBgRzlnDgsmCcmcURvbU,2418 +botocore/data/customer-profiles/2020-08-15/paginators-1.sdk-extras.json,sha256=1f1Dqw3cc1YvdqBJdmPMcEzzDehsm62v9MCIxPzxXlU,240 +botocore/data/customer-profiles/2020-08-15/service-2.json.gz,sha256=4ZqGJ3ufESbF3kycW9KHu-tKxq5kBJFT5hli4hlwwnI,64096 +botocore/data/databrew/2017-07-25/endpoint-rule-set-1.json.gz,sha256=Wxni_1pGBSzI8W5yRj4URAGqFgheWisfDlRusaMSOUM,1210 +botocore/data/databrew/2017-07-25/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/databrew/2017-07-25/paginators-1.json,sha256=i_5ZTxjwAyOvq_e_Etz8L97TB_O2FHjLsAkKFbGJf8U,1316 +botocore/data/databrew/2017-07-25/service-2.json.gz,sha256=LZRkOdayEVc3HX6ViSc7m-Sg2E8A0SBqWQkDw4IEedM,20293 +botocore/data/dataexchange/2017-07-25/endpoint-rule-set-1.json.gz,sha256=oOzM_IqQLZpAJ0eUfBm9aEQOjD1ITeUHIsNWO8fdVuc,1152 +botocore/data/dataexchange/2017-07-25/paginators-1.json,sha256=bmlL4xVyx7gej5sWbNSYqvUOmgM9e3H9YZXsJC4Qh-I,1204 +botocore/data/dataexchange/2017-07-25/service-2.json.gz,sha256=9oY-j0q5Q6WMu-X_6NiaSe9XVRk3dCVL_ZnjboFUPzk,17531 +botocore/data/dataexchange/2017-07-25/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/datapipeline/2012-10-29/endpoint-rule-set-1.json.gz,sha256=321Pw2Q58UKzG1TGIYgxYUm6CELne__d0SfJIGO_f5w,1151 +botocore/data/datapipeline/2012-10-29/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/datapipeline/2012-10-29/paginators-1.json,sha256=JdrA68aI3fnPWh2_ecOxC5DtcFz4OkiO8GvsBkzOgUw,554 +botocore/data/datapipeline/2012-10-29/service-2.json.gz,sha256=kXAh4OsKv-Qas_dvB4fAx8GUIhHLRxRz31mB6Rvk13c,9609 +botocore/data/datasync/2018-11-09/endpoint-rule-set-1.json.gz,sha256=N1DdIg2x2CE6SALEJLl_1Y-Gkrx6Z8LJa7GSUGKclmE,1150 +botocore/data/datasync/2018-11-09/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/datasync/2018-11-09/paginators-1.json,sha256=OTxZXjoijv0y3dCqSgmbXidkpsOxdvViwEvduFFVp7g,842 +botocore/data/datasync/2018-11-09/service-2.json.gz,sha256=fX91TgMbPBZERtAnCLS3YCrzfmGjt-zolemC7jRFOnk,41529 +botocore/data/datazone/2018-05-10/endpoint-rule-set-1.json.gz,sha256=no9MkrJQnRzOwSnBbupfHqiVoVhoZABCqXGXoFxZxbQ,1126 +botocore/data/datazone/2018-05-10/paginators-1.json,sha256=tQWLtBxeCOJJE6zi4jZNUG40uzWHLGPMxUtDvTa_-yo,6657 +botocore/data/datazone/2018-05-10/paginators-1.sdk-extras.json,sha256=QFlwjsMb1_P--nZveHlXUkKQWkav_M60iEpGYEXQ5Sk,392 +botocore/data/datazone/2018-05-10/service-2.json.gz,sha256=KEySd7ZrBYXurdyEwvFRchXC6qm28meqGGkaGTZ3Kp4,99247 +botocore/data/datazone/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/dax/2017-04-19/endpoint-rule-set-1.json.gz,sha256=tT_DQJA-efVNfotOxrZNNTTfl-N-Ch0DRYpWZCqxz8Y,1145 +botocore/data/dax/2017-04-19/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/dax/2017-04-19/paginators-1.json,sha256=OOhBXs1nXQbwQO2dybisWoE6M5Z7WrPyQUCAyGgfEiA,1175 +botocore/data/dax/2017-04-19/service-2.json.gz,sha256=5PhnguaXl6ZAIRkRq1YWk3oxk6DsnGkC0RFCrKq5kSA,10264 +botocore/data/deadline/2023-10-12/endpoint-rule-set-1.json.gz,sha256=KH71DZjVXay0YHNit22Q6xGpATw3fndw720c_8e7_RU,1295 +botocore/data/deadline/2023-10-12/paginators-1.json,sha256=XCsdW-w85IIaGdTTdTrK-vSuJOPNSoSMlFAXrklFMXE,5099 +botocore/data/deadline/2023-10-12/paginators-1.sdk-extras.json,sha256=hFPg_wsYLg1pXtVqU63jGPKL7Fc9mW9vLLloldy_b04,218 +botocore/data/deadline/2023-10-12/service-2.json.gz,sha256=Im2NoziN-a5R46kWAVEQaw7ns9G3L7z0Jp9HGiqg1NY,57036 +botocore/data/deadline/2023-10-12/waiters-2.json,sha256=4aXuIpgoJOfjw_V1mj1Jtc2O_DyYTB0EJZTU0MB6pPM,6602 +botocore/data/detective/2018-10-26/endpoint-rule-set-1.json.gz,sha256=Rn7rAEQ3JuuUzMNfqN354SYJHQG9AfKELDpnRSClWzw,1496 +botocore/data/detective/2018-10-26/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/detective/2018-10-26/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/detective/2018-10-26/service-2.json.gz,sha256=bUW5acvfWDPDaBCaCz-3KmLBa2Ec7Uz8ww4A9lvbngo,13190 +botocore/data/devicefarm/2015-06-23/endpoint-rule-set-1.json.gz,sha256=dN1BU75XwmqVjWMlKkbr88mASkYj8cLataz84nYrX90,1150 +botocore/data/devicefarm/2015-06-23/examples-1.json,sha256=ph2IehoxWkjr60w1Itx_H2XRMVKQ9J1WHbDDdS2-i6Q,42721 +botocore/data/devicefarm/2015-06-23/paginators-1.json,sha256=dsBpWrsUYvlphjtWSswDS3BYoWFzpq3sqwpOK4ER5vA,2870 +botocore/data/devicefarm/2015-06-23/service-2.json.gz,sha256=8VR8qA0PA1p4PtWH8jS11YO0Z7-bmNsf8UpNJ4wqCfQ,32783 +botocore/data/devops-agent/2026-01-01/endpoint-rule-set-1.json.gz,sha256=WPsEzDG1TUp_CQc7XgXNek2hCVOXvVHjlqPaCMIBB_Y,834 +botocore/data/devops-agent/2026-01-01/paginators-1.json,sha256=vX6mRK0n7b2tUNqG48i33oUnHd4EC4sdiK-Y_YgnLO4,1959 +botocore/data/devops-agent/2026-01-01/service-2.json.gz,sha256=B3bLn9Of5XJ-q1NW4wmxitNmushg7bTt44OmT2LyWgY,39703 +botocore/data/devops-guru/2020-12-01/endpoint-rule-set-1.json.gz,sha256=6xKMDOwHwKq0mr1ZEPAxHzoFFQ3PQ8YZF5sL6lKPjYI,1153 +botocore/data/devops-guru/2020-12-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/devops-guru/2020-12-01/paginators-1.json,sha256=L8a_Vi9F4QUZiw34P5LPuf6ELhTE3_rKfSJTiF-Jsrw,3043 +botocore/data/devops-guru/2020-12-01/service-2.json.gz,sha256=t_hipOqemlgNUcuLiJ1ejfol0z1NKiI1oAsF3Lxd6VI,25068 +botocore/data/directconnect/2012-10-25/endpoint-rule-set-1.json.gz,sha256=5R8k3QMLtixigAz4mW1lsBH7ocSwSi1yU-I6bRJ7Ojs,1153 +botocore/data/directconnect/2012-10-25/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/directconnect/2012-10-25/paginators-1.json,sha256=xeMiI713ZrL0L4eTYXOT8iXsmsiguus1SZdRE7OWYCo,643 +botocore/data/directconnect/2012-10-25/service-2.json.gz,sha256=VihRCChdPwJwxEyJFPEV7SEJd8emm3K6oyGZTu_ByUA,27287 +botocore/data/discovery/2015-11-01/endpoint-rule-set-1.json.gz,sha256=SDGoyEJzhvuYPK4X1C0KgywDmuwpeXBCV9g5Xr3pYuk,1150 +botocore/data/discovery/2015-11-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/discovery/2015-11-01/paginators-1.json,sha256=9TAcWsEEH768Rt1ArlrAzFDXYkp82xhdZ5Kh5LVrkmw,1221 +botocore/data/discovery/2015-11-01/service-2.json.gz,sha256=yjMAnI4TK0Ft6Tb-NR4PZ_kvUeIl79YwawP_JgjD90E,19020 +botocore/data/dlm/2018-01-12/endpoint-rule-set-1.json.gz,sha256=COobRfynh1BUO7eHzM5cZJ2Z3KTmyT4Egzs2u9XJUJY,1230 +botocore/data/dlm/2018-01-12/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/dlm/2018-01-12/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/dlm/2018-01-12/service-2.json.gz,sha256=SFWlylVg-EUwyXp7qmJmTikcI3dkcZ0hF1vSkuhWIGA,12266 +botocore/data/dms/2016-01-01/endpoint-rule-set-1.json.gz,sha256=oCcwtmSkdjSToetMvD_mgOku6xNkkKwmw179QMoGyrE,1300 +botocore/data/dms/2016-01-01/examples-1.json,sha256=vV_0L6caRIbPqk4IOCZVqNc0xcbN77GsWwY3KaK0SA0,35747 +botocore/data/dms/2016-01-01/paginators-1.json,sha256=8X1HzmVYDUKuiYfiTM_NSKghY4YY9CshhiEc8DWd0Gc,2864 +botocore/data/dms/2016-01-01/service-2.json.gz,sha256=iLDcCfm3632_uNO91IeYgeEZbzlA2u7qQtxX1pFhfc0,89848 +botocore/data/dms/2016-01-01/waiters-2.json,sha256=XhLhNX8aQ-0Jm93hi3Us58ffGt4VtLC2790k-bseP6Q,18071 +botocore/data/docdb-elastic/2022-11-28/endpoint-rule-set-1.json.gz,sha256=oqdao_IfW0aXSQVmduH4-Q1tN698-355fgYUJC8Ik28,1302 +botocore/data/docdb-elastic/2022-11-28/paginators-1.json,sha256=G7BrLkcnoebH5opU6J0VCq3MfoxNBz1fR1-o5H0zFno,562 +botocore/data/docdb-elastic/2022-11-28/service-2.json.gz,sha256=OhZZ6v2es4cbOnJOGmFqYPdPVmM_lXontshp2WN5TTI,7623 +botocore/data/docdb/2014-10-31/endpoint-rule-set-1.json.gz,sha256=ki46vDPa8WyC8k6qbe4F-hDPls-pV63TZRzt4FeSd2s,1230 +botocore/data/docdb/2014-10-31/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/docdb/2014-10-31/paginators-1.json,sha256=Lc8FwQvudtu-XOnFfOh-qM6pOrsnlRajew2PKY6ZtZk,2318 +botocore/data/docdb/2014-10-31/service-2.json.gz,sha256=_5aNVUgEhcjIeWve9Wg8ZAZ35O07E0o1UMW6-eyh2Kk,35633 +botocore/data/docdb/2014-10-31/service-2.sdk-extras.json,sha256=U_PgxwtPhWl8ZwLlxYiXD4ZQ4iy605x4miYT38nMvnM,561 +botocore/data/docdb/2014-10-31/waiters-2.json,sha256=8bYoMOMz2Tb0aGdtlPhvlMel075q1n7BRnCpQ-Bcc1c,2398 +botocore/data/drs/2020-02-26/endpoint-rule-set-1.json.gz,sha256=FotUJwMfS6YqduFWZVnyeIktuNDGER0TfZJq9mx4rps,1145 +botocore/data/drs/2020-02-26/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/drs/2020-02-26/paginators-1.json,sha256=TdUWQprqjvN3Ys38itsqdDQcE_cvx_VWNnOKUr9wBbY,2649 +botocore/data/drs/2020-02-26/service-2.json.gz,sha256=F2tKTTkar_GbSio2NurGjQSSdG7auUvzrECBzkrHe7w,26337 +botocore/data/ds-data/2023-05-31/endpoint-rule-set-1.json.gz,sha256=-ekP-f3qwi9AYIAET5d4oOUm-tMYTzQv8zoVFDzysLU,1295 +botocore/data/ds-data/2023-05-31/paginators-1.json,sha256=584legW_1CS0O-xK6_nd_ykAX1uQpbsHJyqh7qMGBGs,989 +botocore/data/ds-data/2023-05-31/paginators-1.sdk-extras.json,sha256=hMmJO2oml7dz_QZQiHrgSlhB_pocyqPxnhOMS3EU6Zg,959 +botocore/data/ds-data/2023-05-31/service-2.json.gz,sha256=kLqa1C3oBWzqgkqtWGM7pPn3VKPaYd5rDxjh-5GauDk,7833 +botocore/data/ds/2015-04-16/endpoint-rule-set-1.json.gz,sha256=0_dk-Mh7mIl6VWrxUa3pT_3SM1rhC1Wqcau6v6uz1ws,1144 +botocore/data/ds/2015-04-16/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/ds/2015-04-16/paginators-1.json,sha256=yxY5Cm7p39vcMgdVs5pLlw3VkElaF6EQcTG7L5lY2cU,2555 +botocore/data/ds/2015-04-16/service-2.json.gz,sha256=pajrHUWXFJOMCyQOBNbb-ggmyMAOghZhhcqvxFN75Zk,33084 +botocore/data/ds/2015-04-16/waiters-2.json,sha256=0Wmt13KRi2wlleWgvUUHMnHbKBgJ0dKVvzJPbV1_qDQ,566 +botocore/data/dsql/2018-05-10/endpoint-rule-set-1.json.gz,sha256=t5s390wLjEDPhtNsWr4fu-zHknbYGYDc7AXmi3iw16M,831 +botocore/data/dsql/2018-05-10/paginators-1.json,sha256=HOY0350M6Xtgeo6CZ8Z60yKrcEEBrCQmJLoCuZmGULo,347 +botocore/data/dsql/2018-05-10/service-2.json.gz,sha256=mMRJ2BJVQp78d6yBgbY1KTjsRIltNMr9BYXc6-Mu5Us,8706 +botocore/data/dsql/2018-05-10/waiters-2.json,sha256=d4X8pLDe8Rre3XAZLreGYaLBbyILZo10PLmvBC-RPec,1285 +botocore/data/dynamodb/2011-12-05/endpoint-rule-set-1.json.gz,sha256=Jj9Jtz39s6NR566Arf3LaVEQwtAhQNV1RgWqr2dcXrc,1343 +botocore/data/dynamodb/2011-12-05/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/dynamodb/2012-08-10/endpoint-rule-set-1.json.gz,sha256=7lY9h7tVfws23HH2D1AtLP4K3FYzqYZnvXfLgUWPorw,4089 +botocore/data/dynamodb/2012-08-10/examples-1.json,sha256=cZ5PBzQtSA9b1ZN39RffvUM54Tqf_h5-AQA7zSBVK4Q,16947 +botocore/data/dynamodb/2012-08-10/paginators-1.json,sha256=U84oi-heJVXxjHM1enODt6qI5J117zh0YoM4BHwZZ18,1103 +botocore/data/dynamodb/2012-08-10/service-2.json.gz,sha256=cq21WkyARw-zulnLcftURx-n5xQdKGCOWhZZIxagNKs,88094 +botocore/data/dynamodb/2012-08-10/waiters-2.json,sha256=hB-c-jpTdNTYmewDwyB-I07kLGoXNTuJMBgtJ6Ru99Q,3061 +botocore/data/dynamodbstreams/2012-08-10/endpoint-rule-set-1.json.gz,sha256=2x3hBh73R0UJcOi5ORhKnvCsDiqqYHiYS0qTV0cWfzQ,1602 +botocore/data/dynamodbstreams/2012-08-10/examples-1.json,sha256=LF2m4pmyTs0G8NR6AhmybL0E2F9WHfnbxz5q31DtjAg,7693 +botocore/data/dynamodbstreams/2012-08-10/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/dynamodbstreams/2012-08-10/service-2.json.gz,sha256=Xe4wnRSeKsJQbuY9wcu-taHCAY0LWMeSyDbC7cYNl2o,7008 +botocore/data/ebs/2019-11-02/endpoint-rule-set-1.json.gz,sha256=v_6QNY9jng7bhL1VD5MItAkm7cdCihcIlaCIMDnS3b8,1145 +botocore/data/ebs/2019-11-02/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/ebs/2019-11-02/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/ebs/2019-11-02/service-2.json.gz,sha256=6HUqAyX8punTepOgcfzkcIm0IZpKKa9pgB6lcpDboCk,6402 +botocore/data/ec2-instance-connect/2018-04-02/endpoint-rule-set-1.json.gz,sha256=N2ZXT83lhMH0lJ0Lf_EJ75uEbvDSjRpWVH-eBmSLEXc,1161 +botocore/data/ec2-instance-connect/2018-04-02/examples-1.json,sha256=Qnm4-ldcu-2O38JTe_w17UJWdblMaRBfIc8HyJ62DYU,1712 +botocore/data/ec2-instance-connect/2018-04-02/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/ec2-instance-connect/2018-04-02/service-2.json.gz,sha256=hrDsz9198RQgULdz4mvxKLNx2jp7lxKjn-HDePaXGuQ,2303 +botocore/data/ec2/2014-09-01/endpoint-rule-set-1.json.gz,sha256=K9D6AE-a9VFK3OoorpFvidXIcqaZI7YqMYkkS9kWrd8,1237 +botocore/data/ec2/2014-09-01/paginators-1.json,sha256=XpA8TZvmBGGraKlRGE-U-YeLIBN1ZvbcyE8Wh8uuIDM,1271 +botocore/data/ec2/2014-09-01/service-2.json.gz,sha256=pHyO-s_Zgg4gy2M7JYFh1p6wIMDM5OIFGyR21N1scbk,71841 +botocore/data/ec2/2014-09-01/waiters-2.json,sha256=HG1xDu-8ICfvY1n_YV9i0ylufepFUYmDd0dLkQxwKuY,8548 +botocore/data/ec2/2014-10-01/endpoint-rule-set-1.json.gz,sha256=K9D6AE-a9VFK3OoorpFvidXIcqaZI7YqMYkkS9kWrd8,1237 +botocore/data/ec2/2014-10-01/paginators-1.json,sha256=Uns0O6V6ZIXI09iZdCY77w-CBHbes_siW5vFU-bpE1w,1439 +botocore/data/ec2/2014-10-01/service-2.json.gz,sha256=urz6ultnP1u3wmQNv_U9wSFqHTtC2HM_fcSCvD9LmAA,75362 +botocore/data/ec2/2014-10-01/waiters-2.json,sha256=UDhKYGIrItEq2e56vKMh6yLdn_YfsfTYsmankCjsR3k,11040 +botocore/data/ec2/2015-03-01/endpoint-rule-set-1.json.gz,sha256=K9D6AE-a9VFK3OoorpFvidXIcqaZI7YqMYkkS9kWrd8,1237 +botocore/data/ec2/2015-03-01/paginators-1.json,sha256=Uns0O6V6ZIXI09iZdCY77w-CBHbes_siW5vFU-bpE1w,1439 +botocore/data/ec2/2015-03-01/service-2.json.gz,sha256=Uudd-KzERibtl2xDoPk7Lr2LGy8Ad6RGYR01rZwmBlE,77885 +botocore/data/ec2/2015-03-01/waiters-2.json,sha256=UDhKYGIrItEq2e56vKMh6yLdn_YfsfTYsmankCjsR3k,11040 +botocore/data/ec2/2015-04-15/endpoint-rule-set-1.json.gz,sha256=K9D6AE-a9VFK3OoorpFvidXIcqaZI7YqMYkkS9kWrd8,1237 +botocore/data/ec2/2015-04-15/paginators-1.json,sha256=Uns0O6V6ZIXI09iZdCY77w-CBHbes_siW5vFU-bpE1w,1439 +botocore/data/ec2/2015-04-15/service-2.json.gz,sha256=rv4v0xks512TXE6bszJOY1D5KKDKecdT5yeGNUZkjLQ,90171 +botocore/data/ec2/2015-04-15/waiters-2.json,sha256=1iUHJTDrTvb5_HbDMbVVzC4Ex1S97GZl-tnP70MaDEY,11546 +botocore/data/ec2/2015-10-01/endpoint-rule-set-1.json.gz,sha256=KASLug3JYd9fS8hNWxUIJta0OzsJM2ZoSTrs4RoX7gA,1391 +botocore/data/ec2/2015-10-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/ec2/2015-10-01/paginators-1.json,sha256=Vom5HeCc0UgDyEyYKw3piztolJ3IIxz_tIhEX61TvM8,1793 +botocore/data/ec2/2015-10-01/service-2.json.gz,sha256=NiDEPnCQtc_IHz85Gq033Zm5ABE6p6Wl-O1VSMVDf_o,107913 +botocore/data/ec2/2015-10-01/waiters-2.json,sha256=8sXo9xWtm1IZMKcm9Ne42ha-9XDTVP_fZUejgA1tw3E,14823 +botocore/data/ec2/2016-04-01/endpoint-rule-set-1.json.gz,sha256=KASLug3JYd9fS8hNWxUIJta0OzsJM2ZoSTrs4RoX7gA,1391 +botocore/data/ec2/2016-04-01/examples-1.json,sha256=0xdUoNVzXNn5ZMmA_aiPwiQC68adrXjBJPhw3AzQC8M,109914 +botocore/data/ec2/2016-04-01/paginators-1.json,sha256=Vom5HeCc0UgDyEyYKw3piztolJ3IIxz_tIhEX61TvM8,1793 +botocore/data/ec2/2016-04-01/service-2.json.gz,sha256=lx4XKwfqqTSrG-LNtUb4Owdx8-U4KKfyYqzNVryKWNY,112481 +botocore/data/ec2/2016-04-01/waiters-2.json,sha256=ZjSjdDS-pisO_MoRjsulXMshrcU5qNJd4m1bOBQ9mKQ,15259 +botocore/data/ec2/2016-09-15/endpoint-rule-set-1.json.gz,sha256=KASLug3JYd9fS8hNWxUIJta0OzsJM2ZoSTrs4RoX7gA,1391 +botocore/data/ec2/2016-09-15/examples-1.json,sha256=Dv18Ql8faOeBMQlenC7HBzlgrNQXNeokvLsyFf6Q_yY,110174 +botocore/data/ec2/2016-09-15/paginators-1.json,sha256=Vom5HeCc0UgDyEyYKw3piztolJ3IIxz_tIhEX61TvM8,1793 +botocore/data/ec2/2016-09-15/service-2.json.gz,sha256=a9ynqYTvqVzIFN2vdNlkL0SK2aBjWXdQ_5rK2Teg6jg,114400 +botocore/data/ec2/2016-09-15/waiters-2.json,sha256=1ZtptOEInU4p-4ZQFXbC5lxZ8XNsseki72qxLO2dX4M,14875 +botocore/data/ec2/2016-11-15/endpoint-rule-set-1.json.gz,sha256=0GoLM3eS0aqb49w2x4mLsh27E7pl7K1f2ld0GERi4LQ,1231 +botocore/data/ec2/2016-11-15/examples-1.json,sha256=gB8-MuMSl9N4ic1oBYCv02B_YplxOdnKsfS7g5pY7hk,147949 +botocore/data/ec2/2016-11-15/paginators-1.json,sha256=PI1wQHvIaaj2WmsiivQzl_-9FghLRP6HcICYipytcC8,32855 +botocore/data/ec2/2016-11-15/paginators-1.sdk-extras.json,sha256=s-xAN9v51q2N4UE-PQ_I-wK9PDbrSnwQlKx0yA_rmSk,249 +botocore/data/ec2/2016-11-15/service-2.json.gz,sha256=ZGPniL4gczArgeAOgEQWUxQxez9RelB4xLe7OUwM388,526412 +botocore/data/ec2/2016-11-15/waiters-2.json,sha256=YmQCQfMwzOFvJoNnRrPKzB-J9ZSK6nr49P7RXP2eXIk,22909 +botocore/data/ecr-public/2020-10-30/endpoint-rule-set-1.json.gz,sha256=G7Wvhy-ZExPPFW6v4nfPUi_31ZLjFD-k9sCACb76n98,1245 +botocore/data/ecr-public/2020-10-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/ecr-public/2020-10-30/paginators-1.json,sha256=EEmON1DSCdAARd-o_S_RiZ6rXcWO8AZbYlx4UMyZEGE,711 +botocore/data/ecr-public/2020-10-30/service-2.json.gz,sha256=87f2UNenfGejpvLO8o6kOwH2ApWyTy21BXTLjMYP2D4,10719 +botocore/data/ecr/2015-09-21/endpoint-rule-set-1.json.gz,sha256=_Ht-Q1Buxhz_DJrswIsw0-m7YrQMucugthyMTXNF3B0,2387 +botocore/data/ecr/2015-09-21/examples-1.json,sha256=cFx-qAY3SfNXEHCMe7I9RTWxV-Jtlo8moRHGDZ5UCAM,6603 +botocore/data/ecr/2015-09-21/paginators-1.json,sha256=jTAyTM5a36H94lthRLaf_MOoFKa0_9YW0wqzaMoyiNM,1736 +botocore/data/ecr/2015-09-21/service-2.json.gz,sha256=o8U5UcZZaK59bDB71jcE0Pzds3e5tyCWvd47ZfvnkRg,30098 +botocore/data/ecr/2015-09-21/waiters-2.json,sha256=j4QQUhn_PYN87gWoaY1j1RR-lv7KjzPItwwn1WMYkB8,1482 +botocore/data/ecs/2014-11-13/endpoint-rule-set-1.json.gz,sha256=CmcggztFvARM3A8sqkDsqjAbSmUhbZsX7_zpfyE63Fo,1145 +botocore/data/ecs/2014-11-13/examples-1.json,sha256=Qp-rrnSHaDiVv4ESeJkTGfC1-guCjRc9B9LfiwjrMjg,36519 +botocore/data/ecs/2014-11-13/paginators-1.json,sha256=Y_nqEkKUMY3UhZ5D6DJ2QqxBHfnLkqM6FsOxPp5JUVE,1565 +botocore/data/ecs/2014-11-13/service-2.json.gz,sha256=CGT8Kq3LrJHik3Ns8dwZD2EL7zYHuieCt0yLXhHrFbY,144250 +botocore/data/ecs/2014-11-13/waiters-2.json,sha256=kPEIwbdVssSshiXHBcYoGET9zZ8wMFarRra8TZRcOuo,4861 +botocore/data/efs/2015-02-01/endpoint-rule-set-1.json.gz,sha256=PUIb4TYJb91CIeTwxDgPDnazu9I054YXKoD_UKaCpps,1571 +botocore/data/efs/2015-02-01/examples-1.json,sha256=0EFBCHNGLNS0ftGQqjngkhfTFYpw6E-7lnuAh-d6YKU,8825 +botocore/data/efs/2015-02-01/paginators-1.json,sha256=SKRuOWm1E5Nvvzppzjn-IeS1Lj0I3qSqvc9t9XtKpA4,878 +botocore/data/efs/2015-02-01/service-2.json.gz,sha256=e7mi7k1TEf3U2YaNytQ1Ba4qP-FsVZLk9kJYbtScS_Y,23873 +botocore/data/eks-auth/2023-11-26/endpoint-rule-set-1.json.gz,sha256=jWVTQdaXyInDXo2EU-YLsjC38U8z-gd_JXcvzWfNLfg,1126 +botocore/data/eks-auth/2023-11-26/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/eks-auth/2023-11-26/service-2.json.gz,sha256=GaOByrr2K-0JDoIcymmJLhr_WU8TbcPYEGPacM8Nnlw,2429 +botocore/data/eks-auth/2023-11-26/waiters-2.json,sha256=tj1ZnaqhwmJkUEQlwH7wm1SqY3lg1BvZDfzfPaIgNrY,38 +botocore/data/eks/2017-11-01/endpoint-rule-set-1.json.gz,sha256=J2wcxqerUD-_rz1t-eU2dGctEhlguV7jfEsD34Av8PI,1265 +botocore/data/eks/2017-11-01/examples-1.json,sha256=vCT3MFB7D3tNzqaIdxd8nyDbt7hevsAvDE4RQTQcEKg,5021 +botocore/data/eks/2017-11-01/paginators-1.json,sha256=AfkBScAIdjZcQAVSOum05Cst_CG3W1iMD1Dq0oqIioE,2905 +botocore/data/eks/2017-11-01/service-2.json.gz,sha256=EI1bO0lKppoRzvTgLKFmre56oA9uIRWf0RHyzyFgQo0,65975 +botocore/data/eks/2017-11-01/service-2.sdk-extras.json,sha256=pmn0V8Su5NiqW8Y3X-IBtzD1Bz_JANtKgU4fsr-i_bM,107 +botocore/data/eks/2017-11-01/waiters-2.json,sha256=-xgYqF67mx9fRLPaW0Rr0of9jfMxns0bIf2dss4qSlw,4815 +botocore/data/elasticache/2014-09-30/endpoint-rule-set-1.json.gz,sha256=Ho5Q5fpEJLy4HiXI-e8vlcxzf-BNr5gnhK6JotiYBD4,1241 +botocore/data/elasticache/2014-09-30/paginators-1.json,sha256=YkZxwpICpidoDrIimyr0yFGYg_T0emkSfhlNfPOfVMA,2171 +botocore/data/elasticache/2014-09-30/service-2.json.gz,sha256=IBaKUxJbVWg1DdD3MwrdmFwHxX98NGi-lBQP5Skhqdg,22920 +botocore/data/elasticache/2014-09-30/waiters-2.json,sha256=mIVMN9SNrvDJ2iW_uXAA-N5ptxGmDw964Sv89zKAs-g,3719 +botocore/data/elasticache/2015-02-02/endpoint-rule-set-1.json.gz,sha256=EAl_qIvFNuYTyCooYvFpz0j4u8c0UcZ8SRwDv36lSKA,1236 +botocore/data/elasticache/2015-02-02/examples-1.json,sha256=iWpOlje8s2EFHlnYNgjHX2DpC7teIKmeA7f6e51u00I,111590 +botocore/data/elasticache/2015-02-02/paginators-1.json,sha256=XrsOWe2fflZLszEuZYsZjeXPNAAj5IjpOdfsse_Peg8,3401 +botocore/data/elasticache/2015-02-02/service-2.json.gz,sha256=o7PGQ2sXB2GC4am6xaVr6hGzTDGAgqUHfg7V1AOQ1l0,57752 +botocore/data/elasticache/2015-02-02/waiters-2.json,sha256=N6NTYHqUoktWaIjapl3RDepPknxNlIbb8a0wnS0HB_E,5118 +botocore/data/elasticbeanstalk/2010-12-01/endpoint-rule-set-1.json.gz,sha256=LktyoMkyGaCHjfG7tbSLeU3q38Fnr5zM5opXlbzvRxI,1240 +botocore/data/elasticbeanstalk/2010-12-01/examples-1.json,sha256=EuEpZEobhGxWPfRosGTFNWYs8zRFVtkQtLXD8M_5fm0,37449 +botocore/data/elasticbeanstalk/2010-12-01/paginators-1.json,sha256=qM8N07fmdTtnZBXFiyFeW31EjqjmDWb-viwc19UyF5o,934 +botocore/data/elasticbeanstalk/2010-12-01/service-2.json.gz,sha256=HMHz2ruHrauAcDALa47EJQwBWl14ssSTwwRLbq88hR0,30625 +botocore/data/elasticbeanstalk/2010-12-01/waiters-2.json,sha256=nS1qW0cVQpjnVhpONryvuFWWW4JwJYSW82ooLigmCu0,1463 +botocore/data/elb/2012-06-01/endpoint-rule-set-1.json.gz,sha256=BmWb3nNfU31WAiaVj_8aCtxq4Rb6tIRAyu7UlrKF_Mg,1242 +botocore/data/elb/2012-06-01/examples-1.json,sha256=NE6HcGypE87pOfvGkxKi_QD-UJ_qWHG2_Q9ynk6V9xA,30446 +botocore/data/elb/2012-06-01/paginators-1.json,sha256=udADJnjh3b-REUTKNlC9yYaRI6aOiXfx3demJA1Msxg,373 +botocore/data/elb/2012-06-01/service-2.json.gz,sha256=sJNBdx5VkNWGM9DUVY4Zl-L0F_kRCI6VwVjwHExH6Ls,13212 +botocore/data/elb/2012-06-01/waiters-2.json,sha256=9NjB-6qbZ5pHxElH90T-4YPEBdXHCA9QHdcF96gTbP0,1527 +botocore/data/elbv2/2015-12-01/endpoint-rule-set-1.json.gz,sha256=BmWb3nNfU31WAiaVj_8aCtxq4Rb6tIRAyu7UlrKF_Mg,1242 +botocore/data/elbv2/2015-12-01/examples-1.json,sha256=4Qxoz28hEDW8u1O7iGLKnH9NNb7Po5qybLFQtvtR7ss,44281 +botocore/data/elbv2/2015-12-01/paginators-1.json,sha256=HdpSUaB3jTHWaSt0O3wUi_qAjuGs7sz-vbUPV64kgWc,1744 +botocore/data/elbv2/2015-12-01/service-2.json.gz,sha256=QLr8zw9hNu8fMZvV4ptemW03lggd1QEieRpfG5apaD4,33494 +botocore/data/elbv2/2015-12-01/waiters-2.json,sha256=k-g2ypXqfbW4ktwuK1iVKpApIncFhOPemhbs7pf7cW8,2371 +botocore/data/elementalinference/2018-11-14/endpoint-rule-set-1.json.gz,sha256=5skZSDTr-Q6Aeyt7W-WujlZz4LLTXJP_W2I1-HSscFg,1306 +botocore/data/elementalinference/2018-11-14/paginators-1.json,sha256=hVejHdIT2U8VRGjBHTvLlMjMUI6bLbH4oYdK-YTgiN8,515 +botocore/data/elementalinference/2018-11-14/service-2.json.gz,sha256=dnyTPShaJBJFoSBvIhVAk3gqAanjIAzUj7-3Whj9BsM,10921 +botocore/data/elementalinference/2018-11-14/waiters-2.json,sha256=thw5wpHZoM5uhG6gzxFZBxEAXUDOQmTEelao89Qsg5Q,831 +botocore/data/emr-containers/2020-10-01/endpoint-rule-set-1.json.gz,sha256=GVxHBCrCluBdGa4A4w6Cp6uIdmI0uZ58R1nes7wNxeU,1235 +botocore/data/emr-containers/2020-10-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/emr-containers/2020-10-01/paginators-1.json,sha256=H-qB-RVfZ-v6uivpkok6jdL9RsL9yHZmDhdG4hdiPtU,889 +botocore/data/emr-containers/2020-10-01/service-2.json.gz,sha256=u27zMOI_wqa1rgRxqaIxaXcxqgDhP357Kw5seD-5D1U,12814 +botocore/data/emr-serverless/2021-07-13/endpoint-rule-set-1.json.gz,sha256=f5697P-t5HG6VO6p_8vl_DgROKCAQF_bXtVqCPXdOXM,1152 +botocore/data/emr-serverless/2021-07-13/paginators-1.json,sha256=Na-h3l_QWsEXfi-8B3gNWW4bKwCWV9yheUqB27bSeao,691 +botocore/data/emr-serverless/2021-07-13/service-2.json.gz,sha256=IElhvxrDUT8rQch-Vj4--Pp33dMiJfyz_AtnY1gOMDw,15063 +botocore/data/emr/2009-03-31/endpoint-rule-set-1.json.gz,sha256=YDKsoqN5X9KG3qm7zD24blJjTBxpCkZ1NSr4sWInkW0,1239 +botocore/data/emr/2009-03-31/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/emr/2009-03-31/paginators-1.json,sha256=r-1lOdPCHq4StMtOycMBbIg1ApZvB8TZBxNcRTcxW4Q,1486 +botocore/data/emr/2009-03-31/service-2.json.gz,sha256=DBMFGdABPZgrfYovXQE0VfAQG0k7L11Nll7l3te_OTo,51050 +botocore/data/emr/2009-03-31/waiters-2.json,sha256=pMh5RSVHgFU-DlrH0dSf4IibHo9Hddmg9DvaR4a0Z90,2073 +botocore/data/endpoints.json,sha256=6sKhXF-Z2RkY5iQhSUOGt7HTsf0qf34DRM-cP3Lg3l4,1253629 +botocore/data/entityresolution/2018-05-10/endpoint-rule-set-1.json.gz,sha256=b8UKpIsb_VP6n4zGDBOGQocdkK1ZcpLsJCzlU5-g2pU,1304 +botocore/data/entityresolution/2018-05-10/paginators-1.json,sha256=SRcdwInaqBXq7gpYBftOPb7OMmwgOTUQUTrGaY4594g,1245 +botocore/data/entityresolution/2018-05-10/service-2.json.gz,sha256=h7FBZNYyDJUBwSbY1KpxWKHkQVu_a8DxBWiB_gMaEd8,19645 +botocore/data/es/2015-01-01/endpoint-rule-set-1.json.gz,sha256=nJK8otpPVXWguBKs7I8XFEqHOy3AKzB5-zM9gXTCipo,1311 +botocore/data/es/2015-01-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/es/2015-01-01/paginators-1.json,sha256=sbfve7QYejJgHClHTY4PgdwH4A-PJlY2y0XZ0qRCq9Q,1022 +botocore/data/es/2015-01-01/service-2.json.gz,sha256=lQ7uSBxTJj47f8W0ECILNQVRrpZab5SYSLFVUylWexc,31465 +botocore/data/eventbridgev2/2025-05-15/endpoint-rule-set-1.json.gz,sha256=CH9m_6a0a7sMVE1xP5vUZB3I4xBhJDwuKSFkNIReaxQ,2362 +botocore/data/eventbridgev2/2025-05-15/paginators-1.json,sha256=LO2U3S_O5V7dw9lwPkRH6E0kx1tDJhRj-6_y3ZGqf_M,706 +botocore/data/eventbridgev2/2025-05-15/service-2.json.gz,sha256=urK8iEIC7P8u6WvJtXYvn3T7zqMNUACMkeILkjt7fxo,17759 +botocore/data/eventbridgev2/2025-05-15/waiters-2.json,sha256=o9-vODiuPuwx3YDTaAPSRkMJ_Z5QiCE-od2R3PNoTfs,1837 +botocore/data/events/2014-02-03/endpoint-rule-set-1.json.gz,sha256=s9prG-GiwjdmHX2XkOibq70jaN9nOYgpdcmw6khwJgg,1856 +botocore/data/events/2014-02-03/service-2.json.gz,sha256=0Q8s0z7R3Aii5oFu8te32mU-aS5EK9L6iIjNXFgwd3Y,5254 +botocore/data/events/2015-10-07/endpoint-rule-set-1.json.gz,sha256=gZFQb1aJ11x3G9sEtpKWXqdoGTlRvw-Il4i5y27XZAw,1843 +botocore/data/events/2015-10-07/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/events/2015-10-07/paginators-1.json,sha256=A4gA5VY4LAnP_3iCOI-P0-c5nVH5ntM9hOh3gytyGco,504 +botocore/data/events/2015-10-07/service-2.json.gz,sha256=8xnKrvdQkbEtILwcxRNmhfqSJyWsLjZnF24efL7wC5g,36819 +botocore/data/evs/2023-07-27/endpoint-rule-set-1.json.gz,sha256=55aI16YtSnSipgTS6mi4TiJOc7G8m5WSsLAwBOsn7MA,1295 +botocore/data/evs/2023-07-27/paginators-1.json,sha256=qDEzzYcvatACJgJ9x-8pDKEkdrN6OpIN_mhvU-PlNCQ,908 +botocore/data/evs/2023-07-27/service-2.json.gz,sha256=c1WEq6wSlLK_8A11gOfya-ZXLdTe9qSd0c-NN_Sqe9E,15887 +botocore/data/evs/2023-07-27/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/finspace-data/2020-07-13/endpoint-rule-set-1.json.gz,sha256=g6PLgKTvUvCtPkYrrVN83bdUxSzinLJ8KLvYEJ9jH1I,1152 +botocore/data/finspace-data/2020-07-13/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/finspace-data/2020-07-13/paginators-1.json,sha256=2RzSHda8vNoQX1L1pkYSwHfCF6Us0IKOrXdsXe-ZHkU,851 +botocore/data/finspace-data/2020-07-13/service-2.json.gz,sha256=YRtlf4DxeseTX_StKu0oocP-_7mw2sOl37k1otN9k74,14501 +botocore/data/finspace/2021-03-12/endpoint-rule-set-1.json.gz,sha256=dc5CDZ36fe8gpypFK9qgrNjGVvdE4Oo2aLmX2XNuJeM,1149 +botocore/data/finspace/2021-03-12/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/finspace/2021-03-12/paginators-1.json,sha256=S_FGEtC07GgFCRSKmv_l4RhRBCFmOEmIsQl7QfDI678,197 +botocore/data/finspace/2021-03-12/service-2.json.gz,sha256=UK6-11mgUQKhKxGDHCogMF7VJJ-8RRwtUCMNiF7nFqQ,30464 +botocore/data/firehose/2015-08-04/endpoint-rule-set-1.json.gz,sha256=fazDKCL4hQgtyfQ12lna5ZaHcYsVJCL2eGA6CNHhos0,1148 +botocore/data/firehose/2015-08-04/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/firehose/2015-08-04/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/firehose/2015-08-04/service-2.json.gz,sha256=gjvI2DfXMqNrG0bHorIRW8C_yrZLm5hkjQk9if-KrtY,36363 +botocore/data/fis/2020-12-01/endpoint-rule-set-1.json.gz,sha256=unxvLu0VCL-SkLnm5pKGg9BQoh5xN3iY1Zd5slOwfEA,1230 +botocore/data/fis/2020-12-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/fis/2020-12-01/paginators-1.json,sha256=FZ37WL0c12pVwCrWPZghttxIuVBtwDYe0h208m44zeQ,1107 +botocore/data/fis/2020-12-01/service-2.json.gz,sha256=yuQnqUYcqRHesboTrSZc4vJYN9dV6ihAE3lZ6pdh-XY,10124 +botocore/data/fms/2018-01-01/endpoint-rule-set-1.json.gz,sha256=kMrF-m7kmJO5GgvNXmFEiqRQ4My0iMkzzkj2xBII-KM,1145 +botocore/data/fms/2018-01-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/fms/2018-01-01/paginators-1.json,sha256=Nv9OHpCiWQyuj5sj_Pz-0TjbnmtiMCR0tuySMApzYjM,1470 +botocore/data/fms/2018-01-01/service-2.json.gz,sha256=Sajl6EIfQr55VpNDcYkZd6xJVUnBQbFta120VZoQ-sY,34811 +botocore/data/forecast/2018-06-26/endpoint-rule-set-1.json.gz,sha256=_22pLtPsFbwyUl4zMNRldiFxMsZ0Z0Fc-ObibKSwBaE,1148 +botocore/data/forecast/2018-06-26/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/forecast/2018-06-26/paginators-1.json,sha256=uwjfu4LU_nDuv9woqU_mcL_58oVcFi8QfUSAtQycpA8,2508 +botocore/data/forecast/2018-06-26/service-2.json.gz,sha256=OEgRFHJEwi4Uq18VbJGDe1T8tNWg61YB8q7ErafylSI,40082 +botocore/data/forecastquery/2018-06-26/endpoint-rule-set-1.json.gz,sha256=-DFMmjY5MqYrqm1jqi_aZGuBCk_NtFICaW4DwXFO2OI,1152 +botocore/data/forecastquery/2018-06-26/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/forecastquery/2018-06-26/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/forecastquery/2018-06-26/service-2.json.gz,sha256=zIVAb4p_RsvsYAvgO0m8Wb5-0zfm_GRiIErSjwZ-xo8,2184 +botocore/data/frauddetector/2019-11-15/endpoint-rule-set-1.json.gz,sha256=ws-GJWePkAoo7xLOLP_fs6syzZCMiGN9_hf5REArNUw,1152 +botocore/data/frauddetector/2019-11-15/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/frauddetector/2019-11-15/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/frauddetector/2019-11-15/service-2.json.gz,sha256=JjP2BR0XS1U6RFJP8pCjAE4Vtgo9oGNTvDbx4nh4ASk,24352 +botocore/data/freetier/2023-09-07/endpoint-rule-set-1.json.gz,sha256=9_pOO61bDdOu08TPdih6sKtDCvqrHWf69zc-Xkhale4,1420 +botocore/data/freetier/2023-09-07/paginators-1.json,sha256=7D1EodEvT5WrTkrtPkS0gfa4nMHocl8ljxcUoMJ1_8k,370 +botocore/data/freetier/2023-09-07/service-2.json.gz,sha256=sC9pqOgLWu756WUzycWGEWmfzi9Q0WTr30qHfwpyu5o,4640 +botocore/data/freetier/2023-09-07/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/fsx/2018-03-01/endpoint-rule-set-1.json.gz,sha256=yW0oYsli94ORrKOn8IsPFJg8uCjSvdW1qQowl12F9_Y,1145 +botocore/data/fsx/2018-03-01/examples-1.json,sha256=Ys4PS4GcrfV3F5Lg4hkaZgyemGgNKNLYSm-uepLDkR4,14242 +botocore/data/fsx/2018-03-01/paginators-1.json,sha256=s2sVh0nBC-oAhWvml5z1jHydcZmhTD8r5N-d5V12VjU,1250 +botocore/data/fsx/2018-03-01/service-2.json.gz,sha256=V5VkmrNRekC8lQDBFTpm9rYPHKUwUOiyWlOUPAgrIu0,80252 +botocore/data/gamelift/2015-10-01/endpoint-rule-set-1.json.gz,sha256=Gp8iK7GtoVt_n8xetWmVeZnAEOao0C8pqhKwkeaXEeY,1149 +botocore/data/gamelift/2015-10-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/gamelift/2015-10-01/paginators-1.json,sha256=hPYwxPqikAsx9c8Py0ZJ_Yie8F5B8RnfEVJkend1l-M,4461 +botocore/data/gamelift/2015-10-01/service-2.json.gz,sha256=r57RnYWqsTzA5VYsjKg_PaXEcLUT932C5p7dr4vViJw,127095 +botocore/data/gameliftstreams/2018-05-10/endpoint-rule-set-1.json.gz,sha256=CfBTXac9wd5_JRMwRneUF0tbmEf6LuvrwXDlpUICgb8,839 +botocore/data/gameliftstreams/2018-05-10/paginators-1.json,sha256=FgpHs1dgtbzHUeqjX4F9I_ZqplXQCJJI9R3k409Jj5M,851 +botocore/data/gameliftstreams/2018-05-10/service-2.json.gz,sha256=lpUGnLzEomm1bj5Q3rRra9RBKxrXu593-rj2tZc__t8,38261 +botocore/data/gameliftstreams/2018-05-10/waiters-2.json,sha256=1bi9fg1J2pQ1930lM_N2eBvnZBKDCjdy7s5sFugPVA4,2343 +botocore/data/geo-maps/2020-11-19/endpoint-rule-set-1.json.gz,sha256=OcvTUzE-EDtGgZHt7waf2KnGVnYcj1Z8YKr7KyutT40,1632 +botocore/data/geo-maps/2020-11-19/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/geo-maps/2020-11-19/service-2.json.gz,sha256=eSra4eMm2xzgJyC8wT4QEHjmIPTtZdzmNMVKYI1pVs4,8502 +botocore/data/geo-maps/2020-11-19/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/geo-places/2020-11-19/endpoint-rule-set-1.json.gz,sha256=iuUqLFpn-bh0GNooEtwPaZbHNN2H6KggwBZMPiJjwp4,1634 +botocore/data/geo-places/2020-11-19/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/geo-places/2020-11-19/service-2.json.gz,sha256=6W3Du1Df5bspYGnDW3QhzL_b2Ks8rEZ32aCtNC47Gfk,20143 +botocore/data/geo-places/2020-11-19/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/geo-routes/2020-11-19/endpoint-rule-set-1.json.gz,sha256=63o9zYTbIMc4ijORxnkplqrTJZmOHT4_7Lyatr-I5HU,1636 +botocore/data/geo-routes/2020-11-19/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/geo-routes/2020-11-19/service-2.json.gz,sha256=3Njd_U0cjqz8sZB3xhgCBuDQi8hMAt-6mw3Qt0v0G9s,49948 +botocore/data/geo-routes/2020-11-19/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/glacier/2012-06-01/endpoint-rule-set-1.json.gz,sha256=WSuD0aGZHFJXWibuUJ3NlxA4K2CSS6sTYwuubJPVN_8,1232 +botocore/data/glacier/2012-06-01/examples-1.json,sha256=hR-1NmWo9lL0Cdqnr6x95Ywu_VfJucv0T4OveUp-S4o,27536 +botocore/data/glacier/2012-06-01/paginators-1.json,sha256=RAeqGFOs4GRiC-DuphMOBHWljwDfqBQINYf1qA2LbNA,628 +botocore/data/glacier/2012-06-01/service-2.json.gz,sha256=fhj7UqVG209fZMF1lFLF6OXnJsZAcbr-1ys7dMgEFps,21085 +botocore/data/glacier/2012-06-01/waiters-2.json,sha256=hzoyJJT1wJh9lq1_z4MK2ZBj98TGRhroii0kbeFXnJw,785 +botocore/data/globalaccelerator/2018-08-08/endpoint-rule-set-1.json.gz,sha256=59aZn14vfImfD2RWmv3vK5OdAs44Q_E5fElFt9WGt6M,1155 +botocore/data/globalaccelerator/2018-08-08/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/globalaccelerator/2018-08-08/paginators-1.json,sha256=Exal9Oqocr6pKQ_T5yEFYLXwm0BSxPYcuZTjZL2_8x8,2016 +botocore/data/globalaccelerator/2018-08-08/service-2.json.gz,sha256=HE3rvMrUWk0J10LVAOzY_EtQyWzyH3ECxUyDeBI57Ws,21919 +botocore/data/glue/2017-03-31/endpoint-rule-set-1.json.gz,sha256=9HXJywiGmyy1pqfs53zN_daEmJmIlwm9VdnSnbaFh30,1146 +botocore/data/glue/2017-03-31/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/glue/2017-03-31/paginators-1.json,sha256=yZlRez3Q-RMM1K8eqnKU_yBSIhA7VoUWwht4jZPnWZA,5994 +botocore/data/glue/2017-03-31/paginators-1.sdk-extras.json,sha256=05DstPbLjNqM3IFboIp0gB9jwSQ1X_PuyxkLA1ecfL4,218 +botocore/data/glue/2017-03-31/service-2.json.gz,sha256=-H2mTtaAZ9WYGV5bG7HCN0b3n5rqjitCKlhDG4SKW8c,190870 +botocore/data/grafana/2020-08-18/endpoint-rule-set-1.json.gz,sha256=3wugDM73cBo3qHmdqAJzDnzs_x3YU8Zqq9Kq6vE3aF0,1148 +botocore/data/grafana/2020-08-18/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/grafana/2020-08-18/paginators-1.json,sha256=1w34xYXn5nUies34W7BC_lzVPnbnhggKcWKUug4ckRc,908 +botocore/data/grafana/2020-08-18/paginators-1.sdk-extras.json,sha256=_g8panv1mpml0x69Y013wHvb22Sy63dKgVhK5oRqbwE,329 +botocore/data/grafana/2020-08-18/service-2.json.gz,sha256=nL_G4I-BuDm3STIoDRhAY1MVJyU_J6U3TLZ1ogS-NME,15445 +botocore/data/grafana/2020-08-18/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/greengrass/2017-06-07/endpoint-rule-set-1.json.gz,sha256=jgbEL8SixIqkh8QC2Vm12m1U4eqtFnbU1H1aqaT6A3M,1361 +botocore/data/greengrass/2017-06-07/paginators-1.json,sha256=LphzapxioJkdlNs-zU4IVmg_pjswwy8RuDPq79sbW64,3366 +botocore/data/greengrass/2017-06-07/service-2.json.gz,sha256=gAUZpzFez0oiGenD8EQ2cJ69J83oPzStBNpRAIz0Y6o,17132 +botocore/data/greengrassv2/2020-11-30/endpoint-rule-set-1.json.gz,sha256=jgbEL8SixIqkh8QC2Vm12m1U4eqtFnbU1H1aqaT6A3M,1361 +botocore/data/greengrassv2/2020-11-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/greengrassv2/2020-11-30/paginators-1.json,sha256=geNY9pksg1eDuJ9mpqk1iee_t8zQuFBrBG_O6eaZ7GU,1283 +botocore/data/greengrassv2/2020-11-30/service-2.json.gz,sha256=LxbALx3pQ0sSH1ePPGuz3G-nIrRgSmWdkQ0rW1-qcPE,20272 +botocore/data/groundstation/2019-05-23/endpoint-rule-set-1.json.gz,sha256=S2yDpoLkYT5sxDJlHsKshau740Uih6oCYIedLivtCdg,1151 +botocore/data/groundstation/2019-05-23/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/groundstation/2019-05-23/paginators-1.json,sha256=vinOjg2xaBYlxxSX7iaPe7uC5nFyaQmdrSytI_eAvpo,1767 +botocore/data/groundstation/2019-05-23/service-2.json.gz,sha256=kKhZA4X30GA2DP8xkH65EhwFfAOUFmQRhjuxqthVOLU,22102 +botocore/data/groundstation/2019-05-23/waiters-2.json,sha256=QbUp1yk51fdhICpoReJ2FSBa0hJV2lwqMxwRtzxnNKg,1029 +botocore/data/guardduty/2017-11-28/endpoint-rule-set-1.json.gz,sha256=WOs3NNdGX9k3hOPwtmgpdAlhGv6ZeVia8NOftSO8WLA,1235 +botocore/data/guardduty/2017-11-28/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/guardduty/2017-11-28/paginators-1.json,sha256=BxVJLNClZ8uPQH5ySneVMfHZUxeokRQwPhHth_TZWBg,2958 +botocore/data/guardduty/2017-11-28/service-2.json.gz,sha256=3G3OmbUWOu1NZkXELktoAz-n33lMbdYHOYnKd62Yuyo,82118 +botocore/data/health/2016-08-04/endpoint-rule-set-1.json.gz,sha256=3ib9Z5rPNv9vd92w0Avlu9b_HAasSu9-6ashdO3Z2gE,1524 +botocore/data/health/2016-08-04/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/health/2016-08-04/paginators-1.json,sha256=yiHNcdPNOcqngUnAvp1BUD8e9oWSgqGS-T0Esl6r8vI,1397 +botocore/data/health/2016-08-04/service-2.json.gz,sha256=ZZw3uLHcppFf_HIPHkfli8z_qLsb7mi14SiCMGUDJX8,10840 +botocore/data/healthlake/2017-07-01/endpoint-rule-set-1.json.gz,sha256=x6xuWZRPogxcPYMkcDaTzEgAsxtf79WxdraPGGrhdM8,1151 +botocore/data/healthlake/2017-07-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/healthlake/2017-07-01/paginators-1.json,sha256=yZ8k8cs06jDQK0Ka-QEwIBWIIECYjc7rEURNhMHKKQg,559 +botocore/data/healthlake/2017-07-01/service-2.json.gz,sha256=XhLidTXd9GtTWl7zsIsx4aq3k32J21YBtCAbIfmGI3s,14842 +botocore/data/healthlake/2017-07-01/waiters-2.json,sha256=VVZ7QUK6A6chi59DOxhPPug3brm7n7mNQFcTCqRdXfg,3344 +botocore/data/iam-toolbox/2018-05-10/endpoint-rule-set-1.json.gz,sha256=5MNyoewYH8LF4YL50jFrNC3tYZPO464LSYwOIbMfTrs,1302 +botocore/data/iam-toolbox/2018-05-10/paginators-1.json,sha256=kUioOR8SS4lGQrpn-BR1SPB8hLKvj9ZBDgFhvgUwzyo,175 +botocore/data/iam-toolbox/2018-05-10/service-2.json.gz,sha256=DU2eOWsXENkXIaIC6534UR91fNXuJobpz3GbAH1MzQI,3221 +botocore/data/iam-toolbox/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/iam/2010-05-08/endpoint-rule-set-1.json.gz,sha256=EwiShcpUXVN9wWGHG-k7ka_atTqcqL_L_hkfsIEiIIo,2918 +botocore/data/iam/2010-05-08/examples-1.json,sha256=T5EqrFFZBiVlL9dsN-T5DnigU1UnMSXfVVwBK00AWrU,48537 +botocore/data/iam/2010-05-08/paginators-1.json,sha256=Mrjh9WIhO3YlPK04LELNlBGOWlr4EOWDPV22S4XlPM4,7036 +botocore/data/iam/2010-05-08/service-2.json.gz,sha256=RFFfx1B4qyic9zUkdH5LpQL-hGHXieYUDkQ9F9ihTtw,88526 +botocore/data/iam/2010-05-08/waiters-2.json,sha256=sC6nS5oxMDEinb4z8GAMfZvFfPVWBzL_j1chnAT_z4k,1462 +botocore/data/identitystore/2020-06-15/endpoint-rule-set-1.json.gz,sha256=v0U1QdGr5VOlt_jvtnl7IR628eIWajReJ0cpLv8K-pY,1235 +botocore/data/identitystore/2020-06-15/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/identitystore/2020-06-15/paginators-1.json,sha256=lpGJQxUC8FqJ_JuWaDSHw3cMW677pwZDQpoWRcBvA0M,704 +botocore/data/identitystore/2020-06-15/service-2.json.gz,sha256=KzcQpz6J-IhtG8MBCvI3pDhJfxxIR9bJ1Elx5gdni20,9460 +botocore/data/imagebuilder/2019-12-02/endpoint-rule-set-1.json.gz,sha256=Wae4kGFysaahfREnZQrcaoY_-wL38--8rby5zDUgorY,1237 +botocore/data/imagebuilder/2019-12-02/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/imagebuilder/2019-12-02/paginators-1.json,sha256=I6-12lIoWuh4TkN0X8GiCzc-8ilu4xD69Zuj_ckMoUQ,3820 +botocore/data/imagebuilder/2019-12-02/paginators-1.sdk-extras.json,sha256=bGrcj8XUhY0YmfiAvQv2t8JVDaOCrqss44DaLHra1HA,2040 +botocore/data/imagebuilder/2019-12-02/service-2.json.gz,sha256=kJIT_40-wH-M6UsPJoNsqxvck_FJq_zGZbkr-777XNQ,59286 +botocore/data/importexport/2010-06-01/endpoint-rule-set-1.json.gz,sha256=aiR3woQm_NkPF1T_Izlb56vy1-V_Tn6zHrqF6Wiep0E,1599 +botocore/data/importexport/2010-06-01/paginators-1.json,sha256=Etmobek-KI_4Gx8vLRBQsy6nYiRvog88hJCCXuRESZQ,215 +botocore/data/importexport/2010-06-01/service-2.json.gz,sha256=PGrlw5nVoSWfd2Oq3lmmyzWgwpljobuv32KnvUNQ00o,4733 +botocore/data/inspector-scan/2023-08-08/endpoint-rule-set-1.json.gz,sha256=FcV4MwWb72ZjYIZntK52HLc7GxFpKy0RL-yheDIqvbY,1303 +botocore/data/inspector-scan/2023-08-08/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/inspector-scan/2023-08-08/service-2.json.gz,sha256=XV-gVf44wVZdIMTML3XhTxR-fE2RPgcfBv7GIu_lHpI,1715 +botocore/data/inspector/2015-08-18/endpoint-rule-set-1.json.gz,sha256=O8dCAxTN_Z3k-lji0R72_qnDl-dt_RNLSxZJelcTCTc,1147 +botocore/data/inspector/2015-08-18/service-2.json.gz,sha256=2iK9_y1M1GxLYyTnQx_ruUGsiUKigiuiex1i2F6yhw4,8021 +botocore/data/inspector/2016-02-16/endpoint-rule-set-1.json.gz,sha256=eW8WyHuoMYu4SDiOp2RUk4o5rixJ92VWt3ygrlglJiE,1149 +botocore/data/inspector/2016-02-16/examples-1.json,sha256=EoIoRt_vSBIFaQ8UnXLRGL2W5H50CW9rscWvZ012w-g,36903 +botocore/data/inspector/2016-02-16/paginators-1.json,sha256=weo6-A-gbXJmE6B8bFERy0jQdJHvIDANiZLITbP_9ZQ,1610 +botocore/data/inspector/2016-02-16/service-2.json.gz,sha256=PaZMcwdtc0ivI-8lMn7YOw9WTh3lcu6BunlUWj0RVYU,14162 +botocore/data/inspector2/2020-06-08/endpoint-rule-set-1.json.gz,sha256=mogyJA8V5XPpojlmYraI5qxOuB15zcv5Wgl8JCI1dX8,1151 +botocore/data/inspector2/2020-06-08/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/inspector2/2020-06-08/paginators-1.json,sha256=B_VRHM-B0wguM7ftPc-fdsJbEsUz1SHQxCeaj3_Rnbk,3121 +botocore/data/inspector2/2020-06-08/paginators-1.sdk-extras.json,sha256=WXkFBTPQczZBVGrBAb2IoUJRliU1uNg-m8znDFawOOA,287 +botocore/data/inspector2/2020-06-08/service-2.json.gz,sha256=SopHhByHYJnMk0gv6-zAZZof_pnf-FD7zY7Nvs4LbAQ,56872 +botocore/data/inspector2/2020-06-08/waiters-2.json,sha256=poLs59XOoMwx6BG8hTioNYZHi-tGTIcI1SaI97kLIqs,2830 +botocore/data/interconnect/2022-07-26/endpoint-rule-set-1.json.gz,sha256=WfUuaoONdAJWDI2JW8mi19xvDi-FoVZpXgetSVNQe98,835 +botocore/data/interconnect/2022-07-26/paginators-1.json,sha256=6s5rdvE8zT8ZpeV6-Og8AJjthRVw0naCwc7Qfm0eDic,533 +botocore/data/interconnect/2022-07-26/service-2.json.gz,sha256=oFZhI4-d0NFcMtqXodTwxIzvQRkI5Nub8EOnqCadhJ0,5776 +botocore/data/interconnect/2022-07-26/waiters-2.json,sha256=lC2TdMJ5V58Z-TnX7GCC9Dwo-a74IazkFbHHst-6X-U,880 +botocore/data/internetmonitor/2021-06-03/endpoint-rule-set-1.json.gz,sha256=xVQ7ZLBSSBYuqkUZRZ4bDvRJNcgdf8OyolQLoUI-QYI,1155 +botocore/data/internetmonitor/2021-06-03/paginators-1.json,sha256=5eozwrH81SIJWEZD5zsaAs9rr8CvRMMoXvYnOj1IxTs,531 +botocore/data/internetmonitor/2021-06-03/service-2.json.gz,sha256=uoSKvpCMXLpVAXRzWaMqHdTfbzB7UUfXhxmcUcdhOzI,13894 +botocore/data/internetmonitor/2021-06-03/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/invoicing/2024-12-01/endpoint-rule-set-1.json.gz,sha256=qw0-RE2V7A7SoaGrCm9Itfi04p8ec_2jcEVIWtLkmxg,899 +botocore/data/invoicing/2024-12-01/paginators-1.json,sha256=UKYJyhpXvK9TPnDYfh-rcg8FcQQBUDscovEStcd5w08,955 +botocore/data/invoicing/2024-12-01/service-2.json.gz,sha256=iCqxyjLcWOVA4bth4KfFVusEdU3KpllnSlB2tfuKtOs,13627 +botocore/data/invoicing/2024-12-01/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/iot-data/2015-05-28/endpoint-rule-set-1.json.gz,sha256=1c6dk20i_irSZvbxhH4gyQ_OP6yBF303RjwAU9kCEmU,1482 +botocore/data/iot-data/2015-05-28/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/iot-data/2015-05-28/paginators-1.json,sha256=mpB7lhocS5YCbuMn4MSj96yNruXluJ5g1IwDadSt1q8,373 +botocore/data/iot-data/2015-05-28/service-2.json.gz,sha256=4bNFPuk9b_PjGNj24Qgfqk23NPnWpJ46kbpAnPPAiKA,7049 +botocore/data/iot-jobs-data/2017-09-29/endpoint-rule-set-1.json.gz,sha256=l34hVHxwo9ZzpQfOR4F0QGpT0cvgOTEWRPY5H5x4wI4,1153 +botocore/data/iot-jobs-data/2017-09-29/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/iot-jobs-data/2017-09-29/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/iot-jobs-data/2017-09-29/service-2.json.gz,sha256=edG2jMsoRc2UMb_Ur_Wj3BV2wUZKoCuSXjRcyS5qiBo,5041 +botocore/data/iot-managed-integrations/2025-03-03/endpoint-rule-set-1.json.gz,sha256=O3XNi8-Yy0EehAxpd29dGZ455XlzF1SNOkFOkglFQY8,843 +botocore/data/iot-managed-integrations/2025-03-03/paginators-1.json,sha256=leQj5NI9WDhpU0Nx_QY00lDJKI4MdDEV3xRl-1CP-0c,2987 +botocore/data/iot-managed-integrations/2025-03-03/service-2.json.gz,sha256=EIWIsZH5p2-qvWPL3noJWqAOQc8NHN9d9vWFWPbyecg,30256 +botocore/data/iot/2015-05-28/endpoint-rule-set-1.json.gz,sha256=rrqpDi9FGYojxJxeLMhP1oGzLVabRBDj65Z16EQY7is,1266 +botocore/data/iot/2015-05-28/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/iot/2015-05-28/paginators-1.json,sha256=cwRCZcmss_KOdZo4DoDmqXspGgJAJMjsUeZd9h_QzMk,11232 +botocore/data/iot/2015-05-28/service-2.json.gz,sha256=uxMckc0gAZdTR6vltWDOfaRseWE-FIyVWKkAHpjgodA,129358 +botocore/data/iotdeviceadvisor/2020-09-18/endpoint-rule-set-1.json.gz,sha256=ugOpRiFsPz7mp648UCGht4j5fZeaP-TOCxbBdXbhdCI,1158 +botocore/data/iotdeviceadvisor/2020-09-18/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/iotdeviceadvisor/2020-09-18/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/iotdeviceadvisor/2020-09-18/service-2.json.gz,sha256=92qAYWk85axt48e8j2aolX6eKmYHNTL87zGYYwGg6C8,5326 +botocore/data/iotfleetwise/2021-06-17/endpoint-rule-set-1.json.gz,sha256=hvPgm2qSQXZ-BDeQKmQ22ciO8pyDs6dkm3a73LcoSZ0,1151 +botocore/data/iotfleetwise/2021-06-17/paginators-1.json,sha256=Lpq4Xh8dmuXKeTACXakpt6YLxU7zzWOvkBqWbj9TuNI,2430 +botocore/data/iotfleetwise/2021-06-17/service-2.json.gz,sha256=bDju8tfW96Rcg8FXdiazFoGL4U2Q-f2phUwi7GRIf-U,30396 +botocore/data/iotfleetwise/2021-06-17/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/iotsecuretunneling/2018-10-05/endpoint-rule-set-1.json.gz,sha256=mgNNwiNxw0o_lxonGjjF06wlXrOn_b_OMqGQm2uB2MQ,1401 +botocore/data/iotsecuretunneling/2018-10-05/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/iotsecuretunneling/2018-10-05/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/iotsecuretunneling/2018-10-05/service-2.json.gz,sha256=EjLi1k6OuKQXDioAc4tZ66XpCwPGgMUuNPkq5qgQOcY,3432 +botocore/data/iotsitewise/2019-12-02/endpoint-rule-set-1.json.gz,sha256=q5mwx1Y3eqqxKFO0K7ZdoEpHSNe-bN0r6xkJGjQMVEU,1150 +botocore/data/iotsitewise/2019-12-02/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/iotsitewise/2019-12-02/paginators-1.json,sha256=udjD2V75oZfEiVr9whBLvNeCBVVAJyzi8M4igelvW8Y,7402 +botocore/data/iotsitewise/2019-12-02/paginators-1.sdk-extras.json,sha256=i7MBOY3Zn6_JIJAwjESLEaasNO0jPVQ-qfrRjBxQA8c,802 +botocore/data/iotsitewise/2019-12-02/service-2.json.gz,sha256=jKUBBNQx-zi9TMyCDcVYeg7mbrCXNT8ci7Od7QNQps8,94315 +botocore/data/iotsitewise/2019-12-02/waiters-2.json,sha256=qVN5Ie90YeUrNZqZKgckPkyTBYdKjgEbbrlsx-3RXUw,2237 +botocore/data/iotthingsgraph/2018-09-06/endpoint-rule-set-1.json.gz,sha256=y13XyAUHzWvjtI7X5VuCaBAI3uizFmRJAYGBwwqP5Yk,1217 +botocore/data/iotthingsgraph/2018-09-06/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/iotthingsgraph/2018-09-06/paginators-1.json,sha256=3329WY0CXoFVg2osoDFw4kPWYxWK559asARwgffXvbw,1730 +botocore/data/iotthingsgraph/2018-09-06/service-2.json.gz,sha256=EsGYhxuHJytM8YvxDfT_zSHLY8dlbNN7BUu_D2S0Ulw,10367 +botocore/data/iottwinmaker/2021-11-29/endpoint-rule-set-1.json.gz,sha256=y58mUQjFBqEg0yB0dcJc0jeul2q5bqpbs9PJg8PS-kw,1152 +botocore/data/iottwinmaker/2021-11-29/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/iottwinmaker/2021-11-29/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/iottwinmaker/2021-11-29/service-2.json.gz,sha256=1YA8AsoY29myyMnAI0c0L8EtZJaZFOV4e4XJZ63hGes,16782 +botocore/data/iottwinmaker/2021-11-29/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/iotwireless/2020-11-22/endpoint-rule-set-1.json.gz,sha256=llApZKudB2WYqQIqvIbDgBo_bO5qy41PThaY8itdVSA,1153 +botocore/data/iotwireless/2020-11-22/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/iotwireless/2020-11-22/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/iotwireless/2020-11-22/service-2.json.gz,sha256=_no_6TJ2g8GVc_5oOy5guMHHBTCFmIVX_lSrwTCQUmM,38162 +botocore/data/ivs-realtime/2020-07-14/endpoint-rule-set-1.json.gz,sha256=7UgjijB6egFC3sRZrHRDA0pNl0DypYkUtltzX49q3RU,1300 +botocore/data/ivs-realtime/2020-07-14/paginators-1.json,sha256=HgDr8EFYuc0XAzussWpMVIYF65h571wHeqsQEV4DNTI,550 +botocore/data/ivs-realtime/2020-07-14/service-2.json.gz,sha256=S1naSFnYfKAu9EznAVTRgWyzROMcm_Gr00RM_cZPQhs,19806 +botocore/data/ivs-realtime/2020-07-14/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/ivs/2020-07-14/endpoint-rule-set-1.json.gz,sha256=v5PsUto0f2Fq38BXJhliIk6wbgsG9pTV-SMuKGJuQU0,1145 +botocore/data/ivs/2020-07-14/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/ivs/2020-07-14/paginators-1.json,sha256=IRY51FDnjH2I5EUsX_Ia0ektofgr9h4xXibyEj_a1is,1053 +botocore/data/ivs/2020-07-14/service-2.json.gz,sha256=GL55aQAJDdiJBJvFB1KATGnYqhH42z4f41IA69t5NOA,20103 +botocore/data/ivschat/2020-07-14/endpoint-rule-set-1.json.gz,sha256=s4GzLMeWdZGbW_n4dn9NfP4ETzulV8gfhaZ5C6ZnNKU,1149 +botocore/data/ivschat/2020-07-14/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/ivschat/2020-07-14/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/ivschat/2020-07-14/service-2.json.gz,sha256=FNbzJsTHyzLPjThZ69C0vG1W_0fhDuztSkpCB6z-pV0,8309 +botocore/data/ivschat/2020-07-14/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/kafka/2018-11-14/endpoint-rule-set-1.json.gz,sha256=nUgV-jhdGdRKQY6APATscQs7KsfeMMZR7Z-QPmhaMIQ,1597 +botocore/data/kafka/2018-11-14/paginators-1.json,sha256=Lmq3WylSqus0mXFPis3ZiX_DYrpa08vSkyiIpYcpqjs,2459 +botocore/data/kafka/2018-11-14/service-2.json.gz,sha256=6wqD2h7t6MQzBEM_DbKIL6kcjfeWl32HZ_ijjKh7Sa4,31800 +botocore/data/kafkaconnect/2021-09-14/endpoint-rule-set-1.json.gz,sha256=5S7IHe86JTN7Z2SFuNDJIdqzsyd7hndUkAMrMkXKrk4,1417 +botocore/data/kafkaconnect/2021-09-14/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/kafkaconnect/2021-09-14/paginators-1.json,sha256=Eq3TTanPhDwtKpF3EUFUazS6C8Dkwb7TENJPyonaUkc,733 +botocore/data/kafkaconnect/2021-09-14/service-2.json.gz,sha256=g_Ak1WQ9Az6DZZpHl60f5igzzMyfrNSy56FpP5c5AcA,8411 +botocore/data/kafkaconnect/2021-09-14/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/kendra-ranking/2022-10-19/endpoint-rule-set-1.json.gz,sha256=kC2YiEcSaDRbuX9pZO6pIEN1-exSrnlp6cPorO5_huo,1129 +botocore/data/kendra-ranking/2022-10-19/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/kendra-ranking/2022-10-19/service-2.json.gz,sha256=8BllnCwFw4sdHsiQz3oSWvX7gwSLVW685hHw75bYQ5M,4449 +botocore/data/kendra/2019-02-03/endpoint-rule-set-1.json.gz,sha256=OqkHEaNsflDt80kO_wMOO65VOu4USbq6wyVe_tzKNLM,1147 +botocore/data/kendra/2019-02-03/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/kendra/2019-02-03/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/kendra/2019-02-03/service-2.json.gz,sha256=i8diz8PhmDPW79ObfEfrg3ZrrQuLphn1ux4fRRS13uI,71281 +botocore/data/keyspaces/2022-02-10/endpoint-rule-set-1.json.gz,sha256=lt1P21s5OgwhzbPOS6WSPzo9gUKlWfzKPrF14WEx9ps,1235 +botocore/data/keyspaces/2022-02-10/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/keyspaces/2022-02-10/paginators-1.json,sha256=Wa_EIMfB_oAzXrO5w1GuutNoupwn1mvgPSdNbD3gySk,668 +botocore/data/keyspaces/2022-02-10/service-2.json.gz,sha256=EjkB9p-tHjDZX5r__4gQchuPGnZnTs1V3GQ8An5MaKI,15448 +botocore/data/keyspaces/2022-02-10/waiters-2.json,sha256=tj1ZnaqhwmJkUEQlwH7wm1SqY3lg1BvZDfzfPaIgNrY,38 +botocore/data/keyspacesstreams/2024-09-09/endpoint-rule-set-1.json.gz,sha256=69r4HLLRhY-kXi2sJ085jzA6h4K7mNTONldPKkDr-Cc,840 +botocore/data/keyspacesstreams/2024-09-09/paginators-1.json,sha256=ec8rJotxhYa6UUQ81C-pRcMIA-QlKfB_AiaFoHv7Fbc,342 +botocore/data/keyspacesstreams/2024-09-09/paginators-1.sdk-extras.json,sha256=feP3TZjM_YFuizYme5vJUtGlqVgcpqFZXswcFM3kUwg,378 +botocore/data/keyspacesstreams/2024-09-09/service-2.json.gz,sha256=t2MeacRATfp0IzX4i0RK3pbtftB5woE0xz53_BmyMEA,7354 +botocore/data/kinesis-video-archived-media/2017-09-30/endpoint-rule-set-1.json.gz,sha256=Bd11NZwvrCIpnupmmzkiFZDWdG3tOTazWVuCs_1-yMw,1151 +botocore/data/kinesis-video-archived-media/2017-09-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/kinesis-video-archived-media/2017-09-30/paginators-1.json,sha256=2QyELet6SZ2S2nDPmoKrNlJ9kQyJyMlMTkrUh1FHeh0,346 +botocore/data/kinesis-video-archived-media/2017-09-30/service-2.json.gz,sha256=DmmL9r_e9jW2-2fFmbwdFYmEUVEGc6_D43IiGMIS-uw,13543 +botocore/data/kinesis-video-media/2017-09-30/endpoint-rule-set-1.json.gz,sha256=Bd11NZwvrCIpnupmmzkiFZDWdG3tOTazWVuCs_1-yMw,1151 +botocore/data/kinesis-video-media/2017-09-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/kinesis-video-media/2017-09-30/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/kinesis-video-media/2017-09-30/service-2.json.gz,sha256=bhoYx3yDcTds9lLKCw79urigPJ_8PBW_hOUfMTzZdtw,3456 +botocore/data/kinesis-video-signaling/2019-12-04/endpoint-rule-set-1.json.gz,sha256=Bd11NZwvrCIpnupmmzkiFZDWdG3tOTazWVuCs_1-yMw,1151 +botocore/data/kinesis-video-signaling/2019-12-04/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/kinesis-video-signaling/2019-12-04/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/kinesis-video-signaling/2019-12-04/service-2.json.gz,sha256=hgfQDdpe7COrjBm_iqXtKfAswZ1hJrNLmEOlewqpPso,2462 +botocore/data/kinesis-video-webrtc-storage/2018-05-10/endpoint-rule-set-1.json.gz,sha256=KQIR1BxWnE4yWBdwTx89MTfcx-cX21A-dj6cfJlI1AI,1302 +botocore/data/kinesis-video-webrtc-storage/2018-05-10/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/kinesis-video-webrtc-storage/2018-05-10/service-2.json.gz,sha256=4qGaaQcIK_tPNt_IRrq7Wgqrchd4DkVbQH9dLAiI3Dk,2094 +botocore/data/kinesis/2013-12-02/endpoint-rule-set-1.json.gz,sha256=VxRdlQPwMOGHcpY3FWJiDlcsyD-VymcVu-8G4GygWpE,10134 +botocore/data/kinesis/2013-12-02/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/kinesis/2013-12-02/paginators-1.json,sha256=JRCu-NS9AeVbLtc3txsFtFUMdD3hDJ9VXYg_9NWf5ig,1427 +botocore/data/kinesis/2013-12-02/service-2.json.gz,sha256=cIVWfE-xU3UhrWJMPYLWQPSu83TRxshDch-SguJdB-A,34410 +botocore/data/kinesis/2013-12-02/waiters-2.json,sha256=jC6kxWvPj-fOhfrks0_Hg7AmPa4vVgElCgA9_wM8-X0,1602 +botocore/data/kinesisanalytics/2015-08-14/endpoint-rule-set-1.json.gz,sha256=z_S6hBn0gagQnyQjpMi0FvwhbrQCk8umWgjASj9MQTk,1156 +botocore/data/kinesisanalytics/2015-08-14/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/kinesisanalytics/2015-08-14/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/kinesisanalytics/2015-08-14/service-2.json.gz,sha256=dK4IxFHO3Q9WJsmE0MvnqwRylf-fJ-h8Dz7yEtKC00w,14058 +botocore/data/kinesisanalyticsv2/2018-05-23/endpoint-rule-set-1.json.gz,sha256=z_S6hBn0gagQnyQjpMi0FvwhbrQCk8umWgjASj9MQTk,1156 +botocore/data/kinesisanalyticsv2/2018-05-23/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/kinesisanalyticsv2/2018-05-23/paginators-1.json,sha256=4ttV2ZxNQIdY8Gfsw2atQYWigRj6V6b8bLI70CA4vKs,753 +botocore/data/kinesisanalyticsv2/2018-05-23/service-2.json.gz,sha256=2it1AyVAAeIkI3vAx_DkpCf1PXxyvMBUVgzpcy5K5IA,25514 +botocore/data/kinesisvideo/2017-09-30/endpoint-rule-set-1.json.gz,sha256=Bd11NZwvrCIpnupmmzkiFZDWdG3tOTazWVuCs_1-yMw,1151 +botocore/data/kinesisvideo/2017-09-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/kinesisvideo/2017-09-30/paginators-1.json,sha256=u4Avq0nOOLDcxZR6MF_lKdBBqtPAxO96JsSaq9zIdqQ,758 +botocore/data/kinesisvideo/2017-09-30/service-2.json.gz,sha256=dEOrAz7mN7soWMDLzqBT0kR9ZwdlVrdurlz24lzycqI,15837 +botocore/data/kms/2014-11-01/endpoint-rule-set-1.json.gz,sha256=JW4VJs9KowI0WBtcCeOAjdZFVGi1ByF1lPmzDe4N2K0,1145 +botocore/data/kms/2014-11-01/examples-1.json,sha256=TgahTl1uBYiHIxv63mxyaCc-5c9xQKobWHPhDio4x3c,77655 +botocore/data/kms/2014-11-01/paginators-1.json,sha256=pUo1LF_2xGXf1-sM8xJiafSISwL8m8ygUKravuO-Iv0,1565 +botocore/data/kms/2014-11-01/service-2.json.gz,sha256=TAj3-sqgRPDnYgmzo4xKxH73CPNWLI_Tb0A-USJ308Y,76726 +botocore/data/lakeformation/2017-03-31/endpoint-rule-set-1.json.gz,sha256=HW7q5PpgZ-J6XPPhGWXjUFdBoRcGdS46Yo2YkpMdHL8,1152 +botocore/data/lakeformation/2017-03-31/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/lakeformation/2017-03-31/paginators-1.json,sha256=Igy5JjRrtDgCMOgHT__Et_A6258hmdm2ohrVs2ZasJc,1052 +botocore/data/lakeformation/2017-03-31/paginators-1.sdk-extras.json,sha256=C6kS_EfPl5yTSl_zGXUU30Tp5Z82gPH2KKPi-u1IbOo,159 +botocore/data/lakeformation/2017-03-31/service-2.json.gz,sha256=dMdq0vMUlYeWhsAKLan56A1YTt_4NO0erRa9UF6lDgs,26304 +botocore/data/lambda-core/2026-04-30/endpoint-rule-set-1.json.gz,sha256=syXOZLJSkrCDstDYi9UWdnDbB_EixTsKCH-jPvNpT9c,1297 +botocore/data/lambda-core/2026-04-30/paginators-1.json,sha256=15pglxdKH9fj7qDUiuL0LUe8OW8rdxFOjX-bz6zrb2g,201 +botocore/data/lambda-core/2026-04-30/service-2.json.gz,sha256=dx-qFv8zXk3KuzFwUL-n79zR-ZzU2IblfV8kUvrXEMY,5331 +botocore/data/lambda-microvms/2025-09-09/endpoint-rule-set-1.json.gz,sha256=syXOZLJSkrCDstDYi9UWdnDbB_EixTsKCH-jPvNpT9c,1297 +botocore/data/lambda-microvms/2025-09-09/paginators-1.json,sha256=ZMoqkq4r0GJ3EasDInczYmia7S3OZfkX1VN1Qr3j00o,1037 +botocore/data/lambda-microvms/2025-09-09/service-2.json.gz,sha256=twL2Mv0spxCNoOV5lS45RWyLZM3Un1ahAoI0eY72Xa4,11044 +botocore/data/lambda/2014-11-11/endpoint-rule-set-1.json.gz,sha256=NIh4li-AUPf5dEgVARw8pzeFueC-ybbzy8_pypFNcLM,1288 +botocore/data/lambda/2014-11-11/service-2.json.gz,sha256=0sdTuCv4PmP23WqX81I11fenY3-Y9__5dV_-WzVp3Cs,5528 +botocore/data/lambda/2015-03-31/endpoint-rule-set-1.json.gz,sha256=lPaK1rlVSShkJpbOyJ1qLxlq3cGqSFte40XIK1f_PuE,1148 +botocore/data/lambda/2015-03-31/examples-1.json,sha256=_TOXptTVZUFkSxrkaq_JpIKLxUYjRcK_TpC_0itGHLg,52811 +botocore/data/lambda/2015-03-31/paginators-1.json,sha256=i67K4A0RFZT91hXah41P-AlfAvxXy1TrUAcmy7foZ4o,2839 +botocore/data/lambda/2015-03-31/paginators-1.sdk-extras.json,sha256=evspsJGQ9hFD7SsREZ6pj-ooY7RdvPPHneM5PA4AKaM,196 +botocore/data/lambda/2015-03-31/service-2.json.gz,sha256=STMgGWCPoYCm9JwJPltZM_BeheGwwOTYDuREfSbHlPA,71638 +botocore/data/lambda/2015-03-31/waiters-2.json,sha256=imWEXGOjCilT015RuJNYSaoSXEuafoWZQgL_4KttBqU,4032 +botocore/data/launch-wizard/2018-05-10/endpoint-rule-set-1.json.gz,sha256=uGi9uEnjoUicFZM-_BxTw1x1Gc9xAi4ND5RwkSP5qa0,1302 +botocore/data/launch-wizard/2018-05-10/paginators-1.json,sha256=jEbHb-pm__Gz3-ou4XdgZ2_6sjjtpaq_PSOortlk028,929 +botocore/data/launch-wizard/2018-05-10/service-2.json.gz,sha256=UBxiXR7SmJv-yPUC5vydqec1h57CfxfI9JHczElJJx4,5842 +botocore/data/launch-wizard/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/lex-models/2017-04-19/endpoint-rule-set-1.json.gz,sha256=FRgdPjgqvJsPOkRJ2C_nVO_6jtrKEuRdtIdwzfg0gJk,1331 +botocore/data/lex-models/2017-04-19/examples-1.json,sha256=bOPm5nP9H4YSzKIpuI2sCPe4agTMgdenNLtxDAWIat4,23898 +botocore/data/lex-models/2017-04-19/paginators-1.json,sha256=NmghgFUthvQgC3SqXuZBn-6vnUJ5ey3MZYBpRF7YMqI,1686 +botocore/data/lex-models/2017-04-19/service-2.json.gz,sha256=3MV8f-eev834uKm7MptcCYQWVo_EikO68RSiJQOEnVw,30189 +botocore/data/lex-runtime/2016-11-28/endpoint-rule-set-1.json.gz,sha256=U8cpvPXB1tQMbrhlEFJaUB8azOFC8yrq8vk1bMK7JvE,1331 +botocore/data/lex-runtime/2016-11-28/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/lex-runtime/2016-11-28/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/lex-runtime/2016-11-28/service-2.json.gz,sha256=GQGpRqe6qIuWmkE7xsBuMRA-3WffMJasJwujMZHV_q4,11824 +botocore/data/lexv2-models/2020-08-07/endpoint-rule-set-1.json.gz,sha256=0l_5xfhsVDcsMlVMirYo-dG0phla6h8YXJ-Y5tWUHHE,1154 +botocore/data/lexv2-models/2020-08-07/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/lexv2-models/2020-08-07/paginators-1.json,sha256=xfjYty1IoohGf3f4kAT1vsdeueoZSLAOzlzi2CEdajg,666 +botocore/data/lexv2-models/2020-08-07/service-2.json.gz,sha256=4-rp8oZgsh7AJq5lnnYkfMGmtfhazby3JT5fufQfUw0,83552 +botocore/data/lexv2-models/2020-08-07/waiters-2.json,sha256=Kj-OzJdHpbEuK2Og-0ok3E17irFQKjDwk2KfOj_xKcQ,7231 +botocore/data/lexv2-runtime/2020-08-07/endpoint-rule-set-1.json.gz,sha256=lfM6TghMEqqUcd2YBDUjqc_7XiLBWQNjQ3Sv0-E6r20,1154 +botocore/data/lexv2-runtime/2020-08-07/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/lexv2-runtime/2020-08-07/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/lexv2-runtime/2020-08-07/service-2.json.gz,sha256=4u5gfT264SflIyrzC6Boo75bSE665IxELWLWNjBearw,12913 +botocore/data/license-manager-linux-subscriptions/2018-05-10/endpoint-rule-set-1.json.gz,sha256=7ZQyvgtvNnq33At3NoPuwhQ5fgnw6o41deeK5h0VZ2Y,1318 +botocore/data/license-manager-linux-subscriptions/2018-05-10/paginators-1.json,sha256=9hH87MXwn0OiJQlRwCyyof-Pe9Esid1WmRA32IvfLKU,591 +botocore/data/license-manager-linux-subscriptions/2018-05-10/service-2.json.gz,sha256=qiXhj1p_1igB8SgYqCFHgZ6XDthTwZ3ybzjEPHunstw,4790 +botocore/data/license-manager-user-subscriptions/2018-05-10/endpoint-rule-set-1.json.gz,sha256=CTxScM0bnYDQA0yeVy-6kT7s7yEuxhIoRD9YyJbNiT0,1171 +botocore/data/license-manager-user-subscriptions/2018-05-10/paginators-1.json,sha256=LNXlRPzi78dh0YocO4Tld8ErItNbVafz-InxaJIVd-s,944 +botocore/data/license-manager-user-subscriptions/2018-05-10/service-2.json.gz,sha256=p48rouGBv9rzy081B6Yj70e-kjHMr1hEBiGkijfQi_U,6859 +botocore/data/license-manager-user-subscriptions/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/license-manager/2018-08-01/endpoint-rule-set-1.json.gz,sha256=lEsZ4VBPYoy567jOCfAFTOWvSkb-Al-fz6i8FE61pS4,1156 +botocore/data/license-manager/2018-08-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/license-manager/2018-08-01/paginators-1.json,sha256=u83kulrKizQ1RsV1wfSx_UjFbm72dCbztJd3m2qKZwc,1012 +botocore/data/license-manager/2018-08-01/service-2.json.gz,sha256=GnpllxguLYurUTXWtkp89yTZI8ZDXnbEQCZF6aatxPQ,20604 +botocore/data/lightsail/2016-11-28/endpoint-rule-set-1.json.gz,sha256=NeshX3MeqLGDM6nFh3obTM_pgT19Kg5khLhrs_EEXbQ,1150 +botocore/data/lightsail/2016-11-28/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/lightsail/2016-11-28/paginators-1.json,sha256=9EaLlqeMLm1cO4A5z-uPznc4OgcKMLV3tbvMLdSjZF4,2925 +botocore/data/lightsail/2016-11-28/service-2.json.gz,sha256=8JyLnlOSlaWeBfkn5UMPBJv0_FGcnfFF9LqNbo54F2M,90416 +botocore/data/location/2020-11-19/endpoint-rule-set-1.json.gz,sha256=MQALw4h0iv21OzB19WUgJqtH6L48YrNIQpdZ7CE12-8,1145 +botocore/data/location/2020-11-19/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/location/2020-11-19/paginators-1.json,sha256=0DPFZaN6UaGhmVtzMy3nvIFho67OtGlo8yAXr30dalY,2028 +botocore/data/location/2020-11-19/paginators-1.sdk-extras.json,sha256=RkjEzGF7VMsfK3VpqDSV5a3Ol5XSjUAn_udGxYz4uyA,197 +botocore/data/location/2020-11-19/service-2.json.gz,sha256=ty7RC7Wzf1rg7HMauXZmsxr1MpzfCBsMxxHBJzUJfPE,48019 +botocore/data/location/2020-11-19/waiters-2.json,sha256=pvgUHq4gn2CM71EbuzvVRjxYRbrY0o3YZ3UueF0aib0,560 +botocore/data/logs/2014-03-28/endpoint-rule-set-1.json.gz,sha256=xeV2HXORZOPUVJ34v21DEHzxfb_xnTUCGoTtZS8YgFw,1230 +botocore/data/logs/2014-03-28/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/logs/2014-03-28/paginators-1.json,sha256=W91hHLGNPpWw3v8TNgDZcoNQYH16WKc2tDaMivPQgQI,3553 +botocore/data/logs/2014-03-28/paginators-1.sdk-extras.json,sha256=NEtgDwhuhzOQCycfuhXi4tSe1_y5ekrExDmxJdQ9M1Q,198 +botocore/data/logs/2014-03-28/service-2.json.gz,sha256=raCPxlBdtM-YKBNK0OxnnhRXaI8NuG_P6d4o6xb_dJc,88551 +botocore/data/lookoutequipment/2020-12-15/endpoint-rule-set-1.json.gz,sha256=RkZpBZGytUlbISZS8GaX2jlzfIGWjM_P-TXPe7RMOHI,1156 +botocore/data/lookoutequipment/2020-12-15/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/lookoutequipment/2020-12-15/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/lookoutequipment/2020-12-15/service-2.json.gz,sha256=8mf6FCUT588igoftNNB1Jigeh3EvDq2Hnv3sZHWeEA0,23238 +botocore/data/m2/2021-04-28/endpoint-rule-set-1.json.gz,sha256=OtKIJUuT9y_gwF33mzlCYCgNvvY33idUwheeospuP8M,1145 +botocore/data/m2/2021-04-28/paginators-1.json,sha256=-WFr8vEIZ868a3Kwj0mLLgJuYR_MI2osQq2gIZMWL4g,1787 +botocore/data/m2/2021-04-28/service-2.json.gz,sha256=J1_wbtDE4BV_HAyx_F7jk1nBLDakSDkqg1BsZm95Mww,16743 +botocore/data/machinelearning/2014-12-12/endpoint-rule-set-1.json.gz,sha256=V7TrtI7VkKz8Dp-XfJQgG31Fo7xViP5m9xcSRoNmvMY,1156 +botocore/data/machinelearning/2014-12-12/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/machinelearning/2014-12-12/paginators-1.json,sha256=80ddAOlwUPt-mXpDtk3eJqnm7lB95-DjTW6-G6eqmJc,679 +botocore/data/machinelearning/2014-12-12/service-2.json.gz,sha256=WZcZI_YfgCVvSlwTYh9IAYXRIIYReR-9xqMuE8gjEZg,21306 +botocore/data/machinelearning/2014-12-12/waiters-2.json,sha256=_tyML4Sw4VQBk8fUWh1bUQjlcooL1hgRpvkqxKxEeCY,1902 +botocore/data/macie2/2020-01-01/endpoint-rule-set-1.json.gz,sha256=MTSnxVKHA4rWtvc9Y_FYzrWADZsmTz6r5MpRKnirLj8,1148 +botocore/data/macie2/2020-01-01/paginators-1.json,sha256=QNpyggmzK1vrlEP4LHAy0qKzLTJNNoV9x3y8nqJkj3o,2959 +botocore/data/macie2/2020-01-01/service-2.json.gz,sha256=kC6l_iVjAK5J8sZYwFraF5Vk7-ZxWYfqGNtdwi-Lbfk,59386 +botocore/data/macie2/2020-01-01/waiters-2.json,sha256=YjTydOnsawe754SLZZxzxMgFaq0M88fq5jOu-UQvAWE,553 +botocore/data/mailmanager/2023-10-17/endpoint-rule-set-1.json.gz,sha256=vxSn43Xuja_C7HayhgGoqtvClwGDv4hM5gVJ5_LNLSE,1302 +botocore/data/mailmanager/2023-10-17/paginators-1.json,sha256=6E7z80hMmR8EXzJv7fi20eCjKOVott4o8agYmmVvlU4,2045 +botocore/data/mailmanager/2023-10-17/service-2.json.gz,sha256=eGXdQCzB34FKm8qBZZSgbXd_UKhbQKNXCL9bZliOQxI,23801 +botocore/data/mailmanager/2023-10-17/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/managedblockchain-query/2023-05-04/endpoint-rule-set-1.json.gz,sha256=XZBjeRPdL2het1Zzd-VCR-5Loe2BkwclbIJpJMi2uhM,1312 +botocore/data/managedblockchain-query/2023-05-04/paginators-1.json,sha256=aLhFDqzj7KQVTC4MVIRoQAo6tDKInqtlsOkFmvln-7o,882 +botocore/data/managedblockchain-query/2023-05-04/service-2.json.gz,sha256=-oonnROdu6nGJEk1n9KbSnp9fCis5T1EYLsenTG5RFo,7186 +botocore/data/managedblockchain-query/2023-05-04/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/managedblockchain/2018-09-24/endpoint-rule-set-1.json.gz,sha256=CDJ4co8_qmOIvYiWY22KMzT2WPR8hNTTD7pPUS2czD8,1158 +botocore/data/managedblockchain/2018-09-24/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/managedblockchain/2018-09-24/paginators-1.json,sha256=zAjmRcrAx6dDwoJVM-7ceZ1U04fGfxMgQsREvvVcIeI,189 +botocore/data/managedblockchain/2018-09-24/service-2.json.gz,sha256=S_Y6inYSZ6iy3XvvQRQvcCzSxj00FJE9EsJFaduOD84,13879 +botocore/data/marketplace-agreement/2020-03-01/endpoint-rule-set-1.json.gz,sha256=FmoBERPIcUd32WTKLpXVmizeqz3lXs8ZD8PO_f90tn4,1309 +botocore/data/marketplace-agreement/2020-03-01/paginators-1.json,sha256=7tsoKjwv1l8-dbG5OaVAmryM5_pMcJpiy8Xoj0ktNAo,1471 +botocore/data/marketplace-agreement/2020-03-01/service-2.json.gz,sha256=E1xbCjBmrH2Lf4fLQWH3UnSZ3VlhHSN1PgNj7IqhSGY,25085 +botocore/data/marketplace-agreement/2020-03-01/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/marketplace-catalog/2018-09-17/endpoint-rule-set-1.json.gz,sha256=weGBo0wB8P4kJSSfUVGCjXQ3YbX57pttVwBMLPl3qOc,1368 +botocore/data/marketplace-catalog/2018-09-17/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/marketplace-catalog/2018-09-17/paginators-1.json,sha256=uTIWnSyRh-YAXtDNx1rFRHA0zgLzw8iyX86RMFZXXOk,728 +botocore/data/marketplace-catalog/2018-09-17/paginators-1.sdk-extras.json,sha256=FDYPG1rlyExdaIey8YBig2469UOqtEAY4j1UcimLZSI,364 +botocore/data/marketplace-catalog/2018-09-17/service-2.json.gz,sha256=2TjHV6npQKVP1oqu9l8-bvO3GwukC5U_VvMitaFmfGA,16908 +botocore/data/marketplace-deployment/2023-01-25/endpoint-rule-set-1.json.gz,sha256=4E_M7LOIDvV4Tna5mGCXaQZLvKmXbE2TQ3t745LUWGE,1310 +botocore/data/marketplace-deployment/2023-01-25/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/marketplace-deployment/2023-01-25/service-2.json.gz,sha256=x3OtIF6V9Y6GPBiPJNH57e_z9SvWHkG7b7HqlJup14M,2721 +botocore/data/marketplace-discovery/2026-02-05/endpoint-rule-set-1.json.gz,sha256=S4XVbamEHfdiqxObesnLhXsVfbxkHAkJML6T2hUp_rg,844 +botocore/data/marketplace-discovery/2026-02-05/paginators-1.json,sha256=mo2bd-sWKJH5xPZoh6sSR0Ojyf5ZAECy9gkJYyRnxxI,854 +botocore/data/marketplace-discovery/2026-02-05/paginators-1.sdk-extras.json,sha256=3kIR5OlvKVPDXoR_fuD1czklo_lLe7bxdPrfNDKUzZI,266 +botocore/data/marketplace-discovery/2026-02-05/service-2.json.gz,sha256=jo8jouLQWrDXUEqiCPnYjLwV6XAkU_X13Sj8JFsVL0E,17053 +botocore/data/marketplace-discovery/2026-02-05/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/marketplace-entitlement/2017-01-11/endpoint-rule-set-1.json.gz,sha256=OlfAuKgV0VZ16B-VtObShwJFKW_9eg2bEpUnXfec2jk,1530 +botocore/data/marketplace-entitlement/2017-01-11/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/marketplace-entitlement/2017-01-11/paginators-1.json,sha256=xFY_-BU5Ho7OPWDGn_aX-WwguHOeDyE1N4F-7nlw2KA,194 +botocore/data/marketplace-entitlement/2017-01-11/service-2.json.gz,sha256=TPifrtY3p_i6NUaloR9EVKQa8vthnY7v5EODFeNZh7E,2500 +botocore/data/marketplace-reporting/2018-05-10/endpoint-rule-set-1.json.gz,sha256=76RQ2UDBlDPFYjT8ziVHXybBZbvSiHObpyeTvCjafos,1308 +botocore/data/marketplace-reporting/2018-05-10/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/marketplace-reporting/2018-05-10/service-2.json.gz,sha256=cgT9tyDclFPwKZacRi_SWdcX8Kg-X8HwuwMBPXKXqBk,2451 +botocore/data/marketplace-reporting/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/marketplacecommerceanalytics/2015-07-01/endpoint-rule-set-1.json.gz,sha256=nDbXcFo3pP_2DIJeoD27QAJYoIlpadBzGZU8eNsP0vk,1166 +botocore/data/marketplacecommerceanalytics/2015-07-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/marketplacecommerceanalytics/2015-07-01/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/marketplacecommerceanalytics/2015-07-01/service-2.json.gz,sha256=qX4W8wJaqtTx3Pae5zjt4-WYrl_LGsiTuZUTCC6ji4A,3272 +botocore/data/mediaconnect/2018-11-14/endpoint-rule-set-1.json.gz,sha256=-stHc3a2cqSjKlWX2Nj5UCn-PfpOH3pPUAaAZ-RIabg,1152 +botocore/data/mediaconnect/2018-11-14/paginators-1.json,sha256=KOhLwBbi4k8mumx_ac5Y-x7OkCFL705ZJOer8NxET9g,1712 +botocore/data/mediaconnect/2018-11-14/service-2.json.gz,sha256=5gtQSe_yWatwEOgobwnl1mtSnfNWES3g4q7bK1UtY2Q,51131 +botocore/data/mediaconnect/2018-11-14/waiters-2.json,sha256=EEHy5rGv9mAJgKOlh4YC4FxeIJqgtf8PP6Jm7oDPGFw,9833 +botocore/data/mediaconvert/2017-08-29/endpoint-rule-set-1.json.gz,sha256=nom2Y56sIrQp9874zsEufFJOskoetoMfDU_TBuZt7oY,1299 +botocore/data/mediaconvert/2017-08-29/paginators-1.json,sha256=oX8chsZnZYHV50i1ILrfgyIYwOI82aSlb46knSQk0qw,1153 +botocore/data/mediaconvert/2017-08-29/paginators-1.sdk-extras.json,sha256=tG933F4yMTEHzj_2Y6YhkDuomhFhDjRDmF0k1I7n8II,208 +botocore/data/mediaconvert/2017-08-29/service-2.json.gz,sha256=9azLGaKbyJdPBxWpDXKmaXwM45J-yRNMu8fLCBP3Phg,183826 +botocore/data/medialive/2017-10-14/endpoint-rule-set-1.json.gz,sha256=jvorw5cPs3MfSEi-P8Mg8I02BBStMwDOCBChl6Bfsfk,1149 +botocore/data/medialive/2017-10-14/paginators-1.json,sha256=WjRxjvuXG4nsTz1IQogEjLBHw0HlX28zgkSS-J5nc5U,4028 +botocore/data/medialive/2017-10-14/service-2.json.gz,sha256=74xCU8w0Q7gY-30_PrzWomspEvkYtDyulAZz7RabybY,137320 +botocore/data/medialive/2017-10-14/waiters-2.json,sha256=mD5-Z6nCCRgSwY38HNkhQEv4rYoqpNTMefBgRtDQto8,15752 +botocore/data/mediapackage-vod/2018-11-07/endpoint-rule-set-1.json.gz,sha256=nuLHC6asp8cYVw214f8x_F2fUcuLldIp_RaBxlpJOwo,1155 +botocore/data/mediapackage-vod/2018-11-07/paginators-1.json,sha256=uyOY7MfVXvY7qil_RhqS9KThRg9A3_8LB6C8en49Z3k,551 +botocore/data/mediapackage-vod/2018-11-07/service-2.json.gz,sha256=-SUuT9gC0l-ZIZtvg8ShrtP9wfuC0-JcXjlYt3KQ45o,7204 +botocore/data/mediapackage/2017-10-12/endpoint-rule-set-1.json.gz,sha256=48wgq6WaHfZLQL0l0KwSpQROIgOOMNaniAFkPInTfwU,1152 +botocore/data/mediapackage/2017-10-12/paginators-1.json,sha256=Hkze_cyn0q7t1o4PHpf079W6jE_g7l8tGQf7x-t3ocs,531 +botocore/data/mediapackage/2017-10-12/service-2.json.gz,sha256=GUH52fiHx8FNsyS5I77DKuDTtYHLqZjYC19id1_JTW4,9876 +botocore/data/mediapackagev2/2022-12-25/endpoint-rule-set-1.json.gz,sha256=ObsmW-kr8Z_8-3DX6Y2rnnbu9RnGkZei9sEJZbkd05w,1304 +botocore/data/mediapackagev2/2022-12-25/paginators-1.json,sha256=TvuW6sRcN2gAFECRS2bfyHrPTUcTAZR4uEjHacwMgyU,676 +botocore/data/mediapackagev2/2022-12-25/service-2.json.gz,sha256=4RFAdpc0OAUOTvm2E4Ezh3831PDoxu4WBPRDnhQjW7c,30387 +botocore/data/mediapackagev2/2022-12-25/waiters-2.json,sha256=lXTTb_E9woEww_3b3x_f7fITdrFO96-eKWcC7F6VXGU,832 +botocore/data/mediastore-data/2017-09-01/endpoint-rule-set-1.json.gz,sha256=IBiL_PGuMxFwqz7qLsze0vIBT7AURUBe3BNMqRpWZ3Q,1153 +botocore/data/mediastore-data/2017-09-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/mediastore-data/2017-09-01/paginators-1.json,sha256=iGhEIo_9ydhnm5jAD4K6mIgNGZ51FKUA4AlfMlG0sao,181 +botocore/data/mediastore-data/2017-09-01/service-2.json.gz,sha256=aUaYVamMSZsPLL6CchZQMnFCpdM9QK4KflBZs06C44s,3785 +botocore/data/mediastore/2017-09-01/endpoint-rule-set-1.json.gz,sha256=2UzKfqr1ZDlYX-kUmbR5ZnZz1kGNVfSyebvSa1PERYo,1150 +botocore/data/mediastore/2017-09-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/mediastore/2017-09-01/paginators-1.json,sha256=0XO8tEPJl9J7qprTHPQQt6dC7GrjIoqoCn4AcAbjiyM,191 +botocore/data/mediastore/2017-09-01/service-2.json.gz,sha256=DOi1r5KYvDYhiIWa927vts9G23pIkTqmW317bVlbMus,7064 +botocore/data/mediatailor/2018-04-23/endpoint-rule-set-1.json.gz,sha256=f-KvqPXOuTWIz2kRdB3fUw9VUnQmvwOt2sNLFNpnWYU,1383 +botocore/data/mediatailor/2018-04-23/paginators-1.json,sha256=s4Z2Pv0D-top_iiqYX0QPx-iRfOYlPNNRpSnTQbQlXQ,1496 +botocore/data/mediatailor/2018-04-23/service-2.json.gz,sha256=kOSiQMCm2PdzvtChPKJdd-fKwyTAeIk5Jp3swhMacZY,36038 +botocore/data/medical-imaging/2023-07-19/endpoint-rule-set-1.json.gz,sha256=XeBhdJI9oB0b7UxfD45mDWz_NR5hSmpQIYn4CxQ2lIw,1304 +botocore/data/medical-imaging/2023-07-19/paginators-1.json,sha256=Zdv-t-Mpi7RENFkReFlaQ40h5arjqt4t0EDliR_8VOs,739 +botocore/data/medical-imaging/2023-07-19/paginators-1.sdk-extras.json,sha256=SH5DkCGoc9NqpV_7FKFpREPdZP8dt8uz6TDVCQZmeCI,159 +botocore/data/medical-imaging/2023-07-19/service-2.json.gz,sha256=g2Dy8wxa31w-UgFeB-iMYCjQmH04fSQAPlej3ZD2X74,10044 +botocore/data/medical-imaging/2023-07-19/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/memorydb/2021-01-01/endpoint-rule-set-1.json.gz,sha256=YTS8OrBYVkrBqJZRGdS03q4kUIJ_yHWVTota6KUXpP8,1264 +botocore/data/memorydb/2021-01-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/memorydb/2021-01-01/paginators-1.json,sha256=rPx4219WMZpwPaLBF1L70DN_b4x5cChfNl3u_g65bj0,2277 +botocore/data/memorydb/2021-01-01/service-2.json.gz,sha256=Z7W0tiwEaQcZti7w8BlDc6aHUjuohrBbzCi7nBGr6So,19249 +botocore/data/meteringmarketplace/2016-01-14/endpoint-rule-set-1.json.gz,sha256=0-qgEW4jvf011nIkUYSRQLHy44kkKzyh_4QmkwgT4bg,1518 +botocore/data/meteringmarketplace/2016-01-14/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/meteringmarketplace/2016-01-14/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/meteringmarketplace/2016-01-14/service-2.json.gz,sha256=COqKDn8pnzVO4M4fDge2W-j2g-mQ8VoGGQCfVvUr1Lo,9568 +botocore/data/mgh/2017-05-31/endpoint-rule-set-1.json.gz,sha256=7q6K0Wd8BBR3TksQAU_4rNhnCE6AK-aJxGHdjemMe-M,1145 +botocore/data/mgh/2017-05-31/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/mgh/2017-05-31/paginators-1.json,sha256=E2Ik6-I1lm3WF_e7avtK8OpnpzPT0CQg3im2ILInNK0,1326 +botocore/data/mgh/2017-05-31/service-2.json.gz,sha256=eSANMn9SLfLF4mfK3yksv46tLtlb02MlMV_XT-SAh54,8366 +botocore/data/mgn/2020-02-26/endpoint-rule-set-1.json.gz,sha256=OY3TIbNnOtPDLfC_JSFKLaOnNnPAP8mSQ6VB1CSF75c,1145 +botocore/data/mgn/2020-02-26/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/mgn/2020-02-26/paginators-1.json,sha256=VaiXv2ZxpQyrnwC5WlNx8QLAMXWS5KDVO4POmBHXDgk,5023 +botocore/data/mgn/2020-02-26/service-2.json.gz,sha256=vxoDKFdDP9G4c91DO45_k3Zr5NCFxJrL_WpLCoep7ZY,32217 +botocore/data/mgn/2020-02-26/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/migration-hub-refactor-spaces/2021-10-26/endpoint-rule-set-1.json.gz,sha256=vvAmoLmg-nzfCAlSzORo9pUih5h3MvNBR4Mn1V3KpSY,1154 +botocore/data/migration-hub-refactor-spaces/2021-10-26/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/migration-hub-refactor-spaces/2021-10-26/paginators-1.json,sha256=OZ6GIc0aC4ikn9W96M2UbxWeBdIy3QA7ksZ2Ec7t1e8,904 +botocore/data/migration-hub-refactor-spaces/2021-10-26/service-2.json.gz,sha256=uGMu_UTnbSfGDcw9-CaPKS_V9yJcxUf48iOawBWafZk,12478 +botocore/data/migrationhub-config/2019-06-30/endpoint-rule-set-1.json.gz,sha256=9pRk4DRoAxIRzou6WlDG8CJJRwPpYBasSKfbJoLE-sA,1156 +botocore/data/migrationhub-config/2019-06-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/migrationhub-config/2019-06-30/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/migrationhub-config/2019-06-30/service-2.json.gz,sha256=1NegEmrvjmy539x3nAf6pzzTVOQmgsMUerNmtUSA9XI,2737 +botocore/data/migrationhuborchestrator/2021-08-28/endpoint-rule-set-1.json.gz,sha256=GmoNHyGijLfa28jy20d_sV7psMAAbvnYmiu0aABEauE,1308 +botocore/data/migrationhuborchestrator/2021-08-28/paginators-1.json,sha256=K3BSaAaX302rt-fuD-8ewfuAaO1cXLwfwPxQmgs4gLw,1272 +botocore/data/migrationhuborchestrator/2021-08-28/service-2.json.gz,sha256=E444njR31eFC0DLb9kVpQZlwVT1J8N2CTaXtC33j69k,8432 +botocore/data/migrationhuborchestrator/2021-08-28/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/migrationhubstrategy/2020-02-19/endpoint-rule-set-1.json.gz,sha256=jQvxPhxS5tP07VHh0xwaCiEtWwGjZhlF0LeJJ1p8acI,1157 +botocore/data/migrationhubstrategy/2020-02-19/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/migrationhubstrategy/2020-02-19/paginators-1.json,sha256=1kU7uoqpjQDozh9dBNVWf7QyZDxK2PBkajg_gfz7dxY,1076 +botocore/data/migrationhubstrategy/2020-02-19/paginators-1.sdk-extras.json,sha256=x686VmA6fsdUSIKSMZbp5ZF280pREQ7HpnPkgQTZ730,220 +botocore/data/migrationhubstrategy/2020-02-19/service-2.json.gz,sha256=glSMF5eqkFmM_QL5JgDh_HyyvJtWnS5mqSq-p_xP88Q,13363 +botocore/data/mpa/2022-07-26/endpoint-rule-set-1.json.gz,sha256=CTHvIr_zhyaywtc6lX0whQWjHlkgnUZfPfS2gO9wZ5c,830 +botocore/data/mpa/2022-07-26/paginators-1.json,sha256=KEAQbo0paz_QEgpKVo8h7BdV99138Ck3ni2bl7msPKs,1049 +botocore/data/mpa/2022-07-26/service-2.json.gz,sha256=xmYiC_x0_9c0yyi7o6ITWOShCgsW1FjTN6x1Zd54NBM,10061 +botocore/data/mpa/2022-07-26/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/mq/2017-11-27/endpoint-rule-set-1.json.gz,sha256=hZTmwLWxb2W6371NbkPFBn5KtPyLpp_0xLbg70DFHhI,1145 +botocore/data/mq/2017-11-27/paginators-1.json,sha256=-DY8F2uWKX2j7xPUgs4NZef23MDPRn7XVBEkrpl3SGM,373 +botocore/data/mq/2017-11-27/service-2.json.gz,sha256=uhpynvyw57bg0E8L_N67j5ugxoP0S5Dr7Ro74eRLNrc,15593 +botocore/data/mturk/2017-01-17/endpoint-rule-set-1.json.gz,sha256=78vKZoEcUraBelN-FOGPDslOz4tddjhFM86sFInY0po,1217 +botocore/data/mturk/2017-01-17/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/mturk/2017-01-17/paginators-1.json,sha256=NFfGwUHHAX0lwKOB92RJHnfVkFP5IvDCtM1FnTJ-A0g,1591 +botocore/data/mturk/2017-01-17/service-2.json.gz,sha256=kP4tODqs4phyX-i1tNjXlJPj8X5I70qawpgJlr7kPiA,19770 +botocore/data/mwaa-serverless/2024-07-26/endpoint-rule-set-1.json.gz,sha256=3L2sSXd5tBVJKcKRew4SW2K3p6ZJsqqIkPbfdnnGHdI,841 +botocore/data/mwaa-serverless/2024-07-26/paginators-1.json,sha256=2DDXXuSHlzvaAEATWiKeZEofpzHNoZ6pZrqYA6mxWxw,709 +botocore/data/mwaa-serverless/2024-07-26/service-2.json.gz,sha256=QPQwfDOGbE7_vEOODWAbNSUkw-R8yCOumJJMbBFb8pE,10499 +botocore/data/mwaa-serverless/2024-07-26/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/mwaa/2020-07-01/endpoint-rule-set-1.json.gz,sha256=lazpP2y5AwzNYwyec3NyGc9SQaeo7vja5Lw6jYMg3x0,1149 +botocore/data/mwaa/2020-07-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/mwaa/2020-07-01/paginators-1.json,sha256=ggep_PmvO9S8tClL3v6oAmOMYV4qZcZt06URX5M9658,195 +botocore/data/mwaa/2020-07-01/service-2.json.gz,sha256=Y99yFkzlpZShTn84wg_9Sh0Cyqpb6paKO06gkeUjhJI,11878 +botocore/data/neptune-graph/2023-11-29/endpoint-rule-set-1.json.gz,sha256=xfFIxYEtXP3ool-xBcNi139TAHnAI5NL-3qAE0oXZyo,1408 +botocore/data/neptune-graph/2023-11-29/paginators-1.json,sha256=BahW2a3tKEIHiNWsH6L6euKU2GNi128H27Z9QERjxwE,869 +botocore/data/neptune-graph/2023-11-29/service-2.json.gz,sha256=lKzndoDPYaSZo7y1nOabEdJtGPfL1VxQWq-uwuB2iVw,15164 +botocore/data/neptune-graph/2023-11-29/waiters-2.json,sha256=KT4CSJIIJS_NGt8Lr21pPZdE6vPj-STKCnh9Z8opMQs,6302 +botocore/data/neptune/2014-10-31/endpoint-rule-set-1.json.gz,sha256=Pkub-pR1A2BTKs3Cmqou5hsB_djGU51vGJG9B92njME,1230 +botocore/data/neptune/2014-10-31/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/neptune/2014-10-31/paginators-1.json,sha256=66ojR4_WsS4k5APEI8fjU0mCJTn7B15KuG8mPLcqFk8,2881 +botocore/data/neptune/2014-10-31/service-2.json.gz,sha256=en2gW8703Gpzoal2rTZ3smTATZxgwavqnu0lbX7rrsw,46650 +botocore/data/neptune/2014-10-31/service-2.sdk-extras.json,sha256=U_PgxwtPhWl8ZwLlxYiXD4ZQ4iy605x4miYT38nMvnM,561 +botocore/data/neptune/2014-10-31/waiters-2.json,sha256=8bYoMOMz2Tb0aGdtlPhvlMel075q1n7BRnCpQ-Bcc1c,2398 +botocore/data/neptunedata/2023-08-01/endpoint-rule-set-1.json.gz,sha256=bdw6MjQ2Wn94dU5hJ4PvaSl1mqXq6MpgjEpn-g6og3w,1297 +botocore/data/neptunedata/2023-08-01/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/neptunedata/2023-08-01/service-2.json.gz,sha256=AmfeXeNJpnXrZcyvq1-jxviDzNyyhRNbYTE9jTN8Mto,23810 +botocore/data/network-firewall/2020-11-12/endpoint-rule-set-1.json.gz,sha256=wSnj7Dm1nB3R3MbgWOWQm8lwBiLOMcvOsK1zmk58LeE,1155 +botocore/data/network-firewall/2020-11-12/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/network-firewall/2020-11-12/paginators-1.json,sha256=pHtDypwz0yjf4nE6qiqymJOWgCbEReUbbZLJQML1ccg,2506 +botocore/data/network-firewall/2020-11-12/paginators-1.sdk-extras.json,sha256=IXOL2iFcreaZkcp6DCHiCPgS0wjUqDn0ZJCbvuOIIS4,594 +botocore/data/network-firewall/2020-11-12/service-2.json.gz,sha256=aQjg8dGCrBndpZ4VhbQsWcfg0tIF8C1K1qe3QgdC9is,59709 +botocore/data/network-security-manager/2025-10-30/endpoint-rule-set-1.json.gz,sha256=vXDAQ8g3JCIlzY0lOpxOd0c8gDxDN3PRD4Y5Q_3Us2Y,845 +botocore/data/network-security-manager/2025-10-30/paginators-1.json,sha256=iznscTfGlNeOa8-f9dAIY4L6ZKd_DXHQB9XW_z9Fcyg,2469 +botocore/data/network-security-manager/2025-10-30/service-2.json.gz,sha256=ZjM_KkFA1obdxtrUrRXlHhIO-kZmGsDr-laSNDNhn20,18533 +botocore/data/network-security-manager/2025-10-30/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/networkflowmonitor/2023-04-19/endpoint-rule-set-1.json.gz,sha256=pdQ247KCX8HW_WamCAii5HhimH6bswgyok-PsQrqvFA,840 +botocore/data/networkflowmonitor/2023-04-19/paginators-1.json,sha256=wOkVGLLik9ddJ4lYIRfRNlO9nDh6X68BQwpo38vvMM4,944 +botocore/data/networkflowmonitor/2023-04-19/paginators-1.sdk-extras.json,sha256=-Yf09BFjNn75UmPPQdc2fhWCP04zh0pZHgwGD41NTeE,411 +botocore/data/networkflowmonitor/2023-04-19/service-2.json.gz,sha256=DNSQzUXL6w8E9_GVa3EugJE05fUJ10YIh69-rIoug0o,10353 +botocore/data/networkflowmonitor/2023-04-19/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/networkmanager/2019-07-05/endpoint-rule-set-1.json.gz,sha256=_paPWABuPfq5Z6sy0mJNfITMW8LpCp1nEltyNu_5R4U,1732 +botocore/data/networkmanager/2019-07-05/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/networkmanager/2019-07-05/paginators-1.json,sha256=754b8htCAy6FPKWQ_iWwTXNPcCZ262kCjNyOe3pwHeA,4412 +botocore/data/networkmanager/2019-07-05/service-2.json.gz,sha256=X1pRVKatw8ZlLOPi0NjozOQcRl7GcdiD0hCnidzS7P0,26467 +botocore/data/networkmonitor/2023-08-01/endpoint-rule-set-1.json.gz,sha256=lV3QBqP0PV3I0yJurZW7X97Q8NHQwUGGs_37H2lTgYo,1303 +botocore/data/networkmonitor/2023-08-01/paginators-1.json,sha256=nHQ47DVYXQU7zjhe4CUO3-J0OdqR2OjaTaQ4c8vcMW0,187 +botocore/data/networkmonitor/2023-08-01/service-2.json.gz,sha256=LZAkylPGer_5iHbmOcuUXw51QcEjo0mioQp8OZ89wis,5147 +botocore/data/networkmonitor/2023-08-01/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/notifications/2018-05-10/endpoint-rule-set-1.json.gz,sha256=Px76d4AkbLXraK5H--4NPPd_qZx4L-Y2FcWVxVaOoIQ,836 +botocore/data/notifications/2018-05-10/paginators-1.json,sha256=_5tXGmluTxccm4q-eU4QTXfA4R5Hxz9gDkSXXrlvX0U,2086 +botocore/data/notifications/2018-05-10/service-2.json.gz,sha256=xLIUcl_lPs6BI8i3kNWN9lOVpKeObuNLz1Pj--JFUyc,17128 +botocore/data/notifications/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/notificationscontacts/2018-05-10/endpoint-rule-set-1.json.gz,sha256=Tx_lRcNQtaY_zvEA0xgLa5cW9vGGg4TmFe8bDGTXxBo,907 +botocore/data/notificationscontacts/2018-05-10/paginators-1.json,sha256=iGpOqu4PGgEba54bj8oQAK9ZfNQBKCi6VlSw5JqLScQ,197 +botocore/data/notificationscontacts/2018-05-10/service-2.json.gz,sha256=JVYAbMp7dI1GyK23bV_MkaKjBDd1rw-OpbfkEFPUQcQ,3777 +botocore/data/notificationscontacts/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/nova-act/2025-08-22/endpoint-rule-set-1.json.gz,sha256=mBROkbGCb9BVVeM4sozRTMqvtQSCUZY8u9pQnD-cwHU,1278 +botocore/data/nova-act/2025-08-22/paginators-1.json,sha256=2GRK4t9Cd6oYnF3H6gUz_6Lbv0c18GOjS43yorxtv40,727 +botocore/data/nova-act/2025-08-22/service-2.json.gz,sha256=ibLUmZRZlxlDxtrTTGkxlYpnJRhLeK3VJTnlw5mbWgE,7110 +botocore/data/nova-act/2025-08-22/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/oam/2022-06-10/endpoint-rule-set-1.json.gz,sha256=4T9MsurrgHsLj9j91E8e3kzUsLM9VNHDRkLFf-ajJ0M,1295 +botocore/data/oam/2022-06-10/paginators-1.json,sha256=O-yiC1jmUubOdoY_nq_BvS2UBfskjOM7cgJ547VWO3U,501 +botocore/data/oam/2022-06-10/service-2.json.gz,sha256=sP_j-R78VzgMB4fcIcde9g3epyh6yz0gBFnqomDxryY,7039 +botocore/data/observabilityadmin/2018-05-10/endpoint-rule-set-1.json.gz,sha256=7KbEiJPwGSn88T6qwxjdZygekzx6VUI1A6sJ4XrzbAE,1307 +botocore/data/observabilityadmin/2018-05-10/paginators-1.json,sha256=_oggJRtD16LZ72EsDYTLUP1fHHnlD5Cr6TbvLRzO1OI,1556 +botocore/data/observabilityadmin/2018-05-10/service-2.json.gz,sha256=wYXfa40fDiFnYqfDINUCYnbTI5NiOQU4Km7nbSjw7OU,21764 +botocore/data/observabilityadmin/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/odb/2024-08-20/endpoint-rule-set-1.json.gz,sha256=CDWKPbUi8tsDi7bloX-MXGHUtoez4sY-c_G6bTTGgos,1295 +botocore/data/odb/2024-08-20/paginators-1.json,sha256=8UCdxkDbx9ZsqFn1xmq6ojRS5PNl0Lyu36JD-NcVmYc,3877 +botocore/data/odb/2024-08-20/service-2.json.gz,sha256=S8VDg7ohyFjDwnQlZWXvO-Ww6dT6iVe9oQSxk53zRgo,42171 +botocore/data/odb/2024-08-20/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/omics/2022-11-28/endpoint-rule-set-1.json.gz,sha256=JWa6vCksj2DbYw1j7SrHbnvsKB8-84k9Z7D7-2Cxat0,1297 +botocore/data/omics/2022-11-28/paginators-1.json,sha256=ck_nyL1vMK-zBOfR2xqX_GnqX08ERkbHRBUaSJs30to,4291 +botocore/data/omics/2022-11-28/service-2.json.gz,sha256=nHizPGq0_yfzBjosl2TEAdbV5Mo5TjnwWoCO8bAg6zo,50695 +botocore/data/omics/2022-11-28/waiters-2.json,sha256=ilyIBGDpQrZwAA4HzC3dsLnCTYa65vhX32YyypUzGwg,15423 +botocore/data/opensearch/2021-01-01/endpoint-rule-set-1.json.gz,sha256=v35vV-N0c1oiRXF_32CoqAWRTUGdS1ECQnK5WaqsjAQ,1311 +botocore/data/opensearch/2021-01-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/opensearch/2021-01-01/paginators-1.json,sha256=8SvuZgZ0Y0xqPmOvNUfP4ApehWFu0vVQzqEDf2RJcc4,203 +botocore/data/opensearch/2021-01-01/service-2.json.gz,sha256=icyvBIEerjyqhAZIlkseGpzLQX9ker8z8nVxyCpNlxE,60117 +botocore/data/opensearchserverless/2021-11-01/endpoint-rule-set-1.json.gz,sha256=mhDrCmMtdy17erAbh-UPFYff3IFUgVzLlD4XC8k570Q,1296 +botocore/data/opensearchserverless/2021-11-01/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/opensearchserverless/2021-11-01/service-2.json.gz,sha256=4YrbdLK91U2Op2D3_onNaSc3dbVsUNJAoAJ8JmRVOak,14555 +botocore/data/opensearchserverless/2021-11-01/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/organizations/2016-11-28/endpoint-rule-set-1.json.gz,sha256=8wMQuR7fcUm5AqmudFSCJE2ocMNVWAIlCMH0Egujhks,1651 +botocore/data/organizations/2016-11-28/examples-1.json,sha256=H-s8eMAzogFkvDj193d_NweczAUFsyrDfjFEE_77BFQ,50009 +botocore/data/organizations/2016-11-28/paginators-1.json,sha256=gN3_2FHJt6Xyap3z3IqVjvbbPcC4jGIddUvlLZOsqA8,3185 +botocore/data/organizations/2016-11-28/paginators-1.sdk-extras.json,sha256=2OOgdafaSQgkls_L9T5FWZ0oZuzRK3NY3Dw4ogTQjDI,382 +botocore/data/organizations/2016-11-28/service-2.json.gz,sha256=UMxJneUz82iseaCv3YGD1j0PI7Myu6TjL6RBwhUwtpk,42058 +botocore/data/osis/2022-01-01/endpoint-rule-set-1.json.gz,sha256=3BaH0FAcnk9hERZgsTDsJ3tJkwcRdNzDzoLX2SZkHvA,1296 +botocore/data/osis/2022-01-01/paginators-1.json,sha256=VWwFT-KdB44DIlPwqvTWail3glgRum4sRxVlzVEE5ek,405 +botocore/data/osis/2022-01-01/service-2.json.gz,sha256=PnAUpo4RIv3pIL-OffwARe--Cscalkgi7k9Tqaqkhfo,7785 +botocore/data/outposts/2019-12-03/endpoint-rule-set-1.json.gz,sha256=wBd3lKSbFUFCkDmsH4Up3ScbueWSEObyms3MskTR6qA,1233 +botocore/data/outposts/2019-12-03/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/outposts/2019-12-03/paginators-1.json,sha256=PC6pHxic-BysrC62ldE9aDBo4qp5rFHqkWBk01jTu28,2257 +botocore/data/outposts/2019-12-03/paginators-1.sdk-extras.json,sha256=jRZD-sEysIXUGrm--kmlGyyIEUqwxEYvMDB2tB-dsYA,367 +botocore/data/outposts/2019-12-03/service-2.json.gz,sha256=9MOFsULSQAmxdrXmrHOIgDwGxX8OxmbO3mArFdXupVo,21716 +botocore/data/partitions.json,sha256=DK-d_hOTOcR6ZOEOYfeuQ25_sclDU6T4YrFAnRezVFw,7115 +botocore/data/partnercentral-account/2025-04-04/endpoint-rule-set-1.json.gz,sha256=taRQ9xycQqwB8PUIbgJaBYd-rDEPID0VEQvkyEBIPb8,843 +botocore/data/partnercentral-account/2025-04-04/paginators-1.json,sha256=rKgRX7jPlJ6Pexb0EVPaPAZJh-b3O0NbkhQQd6EM0Is,536 +botocore/data/partnercentral-account/2025-04-04/service-2.json.gz,sha256=f58_mhUioyknnluYPmhApzgDbIleacwKYy6Vp-1eyKA,16010 +botocore/data/partnercentral-account/2025-04-04/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/partnercentral-benefits/2018-05-10/endpoint-rule-set-1.json.gz,sha256=Cbn4Vur6GP1zrbofZsVHMaezmRvwV9K8PfsAkviH4rc,844 +botocore/data/partnercentral-benefits/2018-05-10/paginators-1.json,sha256=Spea3UM7gtCjad2_yxNdgtZ5FGKHphff2--T_uUEOpg,577 +botocore/data/partnercentral-benefits/2018-05-10/service-2.json.gz,sha256=_MAB9-xznQJ22VajSO0BkjdJfTHsEqNfqY-k_fjSNX0,9064 +botocore/data/partnercentral-benefits/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/partnercentral-channel/2024-03-18/endpoint-rule-set-1.json.gz,sha256=9N5LLgLZmr2sl7bmwUm0hTj3IFNZZL05XkpmD7-9sgQ,1121 +botocore/data/partnercentral-channel/2024-03-18/paginators-1.json,sha256=J7M-NM9oSS1RXWYRJ9dLiXrIs9T2TZZWeMNvOEqIU80,533 +botocore/data/partnercentral-channel/2024-03-18/service-2.json.gz,sha256=86SrUf9T6-bD_55EzuVATIVZXZ8XOuKjF_wYRq-bLeQ,6739 +botocore/data/partnercentral-channel/2024-03-18/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/partnercentral-revenue-measurement/2022-07-26/endpoint-rule-set-1.json.gz,sha256=da5IUOQRtm75u-7wqaUumEEzgV-UoNCUQMN5g-rBVMg,1118 +botocore/data/partnercentral-revenue-measurement/2022-07-26/paginators-1.json,sha256=G2ZNtQI64piLkSrbtH3I9CW1pgY1ETXo7YWbSERKhrw,853 +botocore/data/partnercentral-revenue-measurement/2022-07-26/service-2.json.gz,sha256=tc_7dIte8p2bZIeaxhrCKXfX0jA3ttn6s2xpk4ibFHE,8435 +botocore/data/partnercentral-revenue-measurement/2022-07-26/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/partnercentral-selling/2022-07-26/endpoint-rule-set-1.json.gz,sha256=EIKgPInbei-nejZ_J44QzjMzSX47Xms4xLFXnT-DeWA,843 +botocore/data/partnercentral-selling/2022-07-26/paginators-1.json,sha256=qB4V0iKeUzrc2cnRhYf_-JHLkP-fFO59Wr7LffmDQo8,2291 +botocore/data/partnercentral-selling/2022-07-26/service-2.json.gz,sha256=9kwUN73xkZujMc8XpEC63DVuakA1KE2g8YFS0swKaK0,61931 +botocore/data/partnercentral-selling/2022-07-26/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/payment-cryptography-data/2022-02-03/endpoint-rule-set-1.json.gz,sha256=JA2ZAdjBigmFZJq4ky6R8MjI9Zs23mtNQPw09r5PeI4,1318 +botocore/data/payment-cryptography-data/2022-02-03/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/payment-cryptography-data/2022-02-03/service-2.json.gz,sha256=j848ZRUAi5Mb1qBVfC54JP7ZwW_McmO9TjPrbUZPHdE,19963 +botocore/data/payment-cryptography-data/2022-02-03/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/payment-cryptography/2021-09-14/endpoint-rule-set-1.json.gz,sha256=9xHkvCZyd304x8AKZCzLYLz9p9relGSRxvjECO_y1N4,1318 +botocore/data/payment-cryptography/2021-09-14/paginators-1.json,sha256=Q3nZHuUZ53pNZpShnEVxB2Z6ec8thvlIx-hPXFVBNM8,504 +botocore/data/payment-cryptography/2021-09-14/service-2.json.gz,sha256=25twxVPuFsOjOAcZvo6BIP8MH309FotvHLfxceqXxWY,24499 +botocore/data/payment-cryptography/2021-09-14/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/pca-connector-ad/2018-05-10/endpoint-rule-set-1.json.gz,sha256=ebKAwHOXGsQsmzHZVvd5lNJrtRB7b2wTR0uT9wBvki0,1305 +botocore/data/pca-connector-ad/2018-05-10/paginators-1.json,sha256=AS3R0cOqXrf6ALY1Ar4Z_HdXbvrA4SwPve_YSeqtIFc,932 +botocore/data/pca-connector-ad/2018-05-10/service-2.json.gz,sha256=GYKal384utgFQcracLAzI2jaMz5po3BoqrMv9cDuLIU,13292 +botocore/data/pca-connector-scep/2018-05-10/endpoint-rule-set-1.json.gz,sha256=furW1J_DuaRMPAszr2eoOezdbLuCmQtOclY1eKd2EZg,1307 +botocore/data/pca-connector-scep/2018-05-10/paginators-1.json,sha256=-TAE2EG4hET8i1kSBmb5SkQbT8NEQ_peQNskuUSs0Ug,364 +botocore/data/pca-connector-scep/2018-05-10/service-2.json.gz,sha256=wAibBXMLb4hWbXdlDam5_R8ST0KL8zo7QNQq6pQsgkg,5932 +botocore/data/pca-connector-scep/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/pcs/2023-02-10/endpoint-rule-set-1.json.gz,sha256=oJpUtwKxBlEenxxMtyRRRfGh-eotdVBDvcbBWYGPMD0,1295 +botocore/data/pcs/2023-02-10/paginators-1.json,sha256=rm1F2IEEf8TDUFnjJYcvrgQaCiVryXOmbjqdg-aescw,525 +botocore/data/pcs/2023-02-10/service-2.json.gz,sha256=6OUDQC5adBEvyxeVQM_XSc4EU5JuuqnFnQY4ozqyF10,16135 +botocore/data/pcs/2023-02-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/personalize-events/2018-03-22/endpoint-rule-set-1.json.gz,sha256=tMhkPqUh14DAW3WLwzJPBX50a7q0Q52I9mgxuGZ7ou4,1158 +botocore/data/personalize-events/2018-03-22/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/personalize-events/2018-03-22/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/personalize-events/2018-03-22/service-2.json.gz,sha256=qIpTXcar6LVwRFNpyMSVDafZnY-GZ1omovR8Z7mDRW8,3891 +botocore/data/personalize-runtime/2018-05-22/endpoint-rule-set-1.json.gz,sha256=pxXw15NHwFFLURZbsaS-h3VbQ0Xi8PxBP3SeWEhIZxg,1159 +botocore/data/personalize-runtime/2018-05-22/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/personalize-runtime/2018-05-22/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/personalize-runtime/2018-05-22/service-2.json.gz,sha256=MeSQXlIh2LuQw44ns3hQ95ANqsh3BtUXgRran7888w4,3843 +botocore/data/personalize/2018-05-22/endpoint-rule-set-1.json.gz,sha256=eTQnLK4i3ZsvOQ-eoj0w_6m5BkoWQWB2t9nGwKTe4Ow,1153 +botocore/data/personalize/2018-05-22/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/personalize/2018-05-22/paginators-1.json,sha256=PfTPE03jTLANh2F51b68_GALtAUqFWJp2R0o2Xl5u0A,2766 +botocore/data/personalize/2018-05-22/service-2.json.gz,sha256=5gABkz_rMkHII0lAkevJN9PtdcsQGDcrj-y2xoxkn0w,31275 +botocore/data/pi/2018-02-27/endpoint-rule-set-1.json.gz,sha256=OSepfzzzvbhzjeVtWrNlCOfyq_GxLVDHh0d-kUT9F1A,1144 +botocore/data/pi/2018-02-27/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/pi/2018-02-27/paginators-1.json,sha256=D4MfyXNwntTAVHzw628L7DpyMfLH9jWLOW9579VPNsE,226 +botocore/data/pi/2018-02-27/service-2.json.gz,sha256=0MBmUjLbmLvN51A4O7jWrjqzL85Buw_O_37LcHI4XEc,12200 +botocore/data/pinpoint-email/2018-07-26/endpoint-rule-set-1.json.gz,sha256=HbfARjrT4LonP8LxCb2iXEWch0pT0MJ0bJPUejQQVKs,1146 +botocore/data/pinpoint-email/2018-07-26/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/pinpoint-email/2018-07-26/paginators-1.json,sha256=G74a7tI3gD77zuNQfj6bfDHtriSA2qhAWh6Su9Tw6Bc,914 +botocore/data/pinpoint-email/2018-07-26/service-2.json.gz,sha256=4KzRAiOZNA57P5Xe2l85afl15yXf_YiK1Q9FLRjXG6k,23622 +botocore/data/pinpoint-sms-voice-v2/2022-03-31/endpoint-rule-set-1.json.gz,sha256=NhXX7kjbfKP-YePWBIsBgzMPG-2slEl-Uk5sVcXL7P8,1150 +botocore/data/pinpoint-sms-voice-v2/2022-03-31/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/pinpoint-sms-voice-v2/2022-03-31/paginators-1.json,sha256=voQ3N7tafnPajeKx7xDbY6WuongXbUlhXKMga7-qPXk,5227 +botocore/data/pinpoint-sms-voice-v2/2022-03-31/paginators-1.sdk-extras.json,sha256=TJpujYCcW_A5_4aTAOiFRkEbW5NZ2lT5yKFLQUkaQTQ,1577 +botocore/data/pinpoint-sms-voice-v2/2022-03-31/service-2.json.gz,sha256=OtjjMHyV57nHiAhR2xgoJ88lM8lhzV4NUvVjV_ch3k8,56292 +botocore/data/pinpoint-sms-voice-v2/2022-03-31/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/pinpoint-sms-voice/2018-09-05/endpoint-rule-set-1.json.gz,sha256=EgAguZwp7L9N8ylpEEMe0W-Uo9N-ivxamLCky5kyKOQ,1305 +botocore/data/pinpoint-sms-voice/2018-09-05/service-2.json.gz,sha256=b3lpX79IWC_Ah8nz3HonODz1IU3ZbcTX1sxAOn0GqoU,3344 +botocore/data/pinpoint/2016-12-01/endpoint-rule-set-1.json.gz,sha256=eC9pboIsgTcGSrpMzpZW3YmXeHGmN42pAWSrGxPq0jQ,1313 +botocore/data/pinpoint/2016-12-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/pinpoint/2016-12-01/service-2.json.gz,sha256=FK7ENPlOnS1J0er1EibekDCGPBRRKb-mbS5J7FiRIe8,70366 +botocore/data/pipes/2015-10-07/endpoint-rule-set-1.json.gz,sha256=qK_0_uE_B0H2MCIEQWveKcGapP774bxD_PN8nYnjvPk,1293 +botocore/data/pipes/2015-10-07/paginators-1.json,sha256=a_b-W2Fj-9dt3XIXqHzXHKGRz8elOX8p9h2pI3wg5ls,176 +botocore/data/pipes/2015-10-07/service-2.json.gz,sha256=6tjRJ_773iASXi-wZjuHzezPF1_4soJmd9U2XcWpD7U,23040 +botocore/data/pipes/2015-10-07/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/polly/2016-06-10/endpoint-rule-set-1.json.gz,sha256=PgjMwuOarrHvSXv7X-hsH2NpB94WRwcyF2VYr-HOJ28,1147 +botocore/data/polly/2016-06-10/examples-1.json,sha256=4KBzptmKd-ySr0PXR8a5UOE6w8nw-mm0Iq-LRhrtcNM,5101 +botocore/data/polly/2016-06-10/paginators-1.json,sha256=IJnO61fPCtuJPYshmxGjm9ZzkXfOxEvsL0acyUPG55E,463 +botocore/data/polly/2016-06-10/service-2.json.gz,sha256=clMy93nmfSklgBUqowZAg-5R83cSHmYIUPN1g9ywAIU,10061 +botocore/data/pricing-plan-manager/2025-08-05/endpoint-rule-set-1.json.gz,sha256=fA3UYQ9kBNowj0xSUDlwr-uScxT60bbX5hQaPGCUQ1U,471 +botocore/data/pricing-plan-manager/2025-08-05/paginators-1.json,sha256=X8LJIe2gjYmyvBdYlCGrtVDLXtrS_3NCN8cEA75xdCM,172 +botocore/data/pricing-plan-manager/2025-08-05/service-2.json.gz,sha256=TxCTgBm6Sgm4TjSoWV20JuaOMSMnLJQRUQuukkzQBe4,4981 +botocore/data/pricing/2017-10-15/endpoint-rule-set-1.json.gz,sha256=n8SDvVvFOcXVz2fyUrITp2vjSw4eoY08LznUPuZvCZ0,1217 +botocore/data/pricing/2017-10-15/examples-1.json,sha256=LX0A-kHCd3N64FsP7EdT6IV-Sej2qNX9ygW6n6jBucs,4263 +botocore/data/pricing/2017-10-15/paginators-1.json,sha256=rizUQ-J932MNyVUTMjrRSVOm-tmzWnvnYhWoIMGxuuM,820 +botocore/data/pricing/2017-10-15/service-2.json.gz,sha256=cD9sUl9-yFJxvBP9EGpcxk_oPrNHJMUPbVjiSutCaDc,4485 +botocore/data/pricing/2017-10-15/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/proton/2020-07-20/endpoint-rule-set-1.json.gz,sha256=rAQc4WNqZgoS9jbQ_zab9wECZZiROpy-o94-4V2Vu14,1148 +botocore/data/proton/2020-07-20/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/proton/2020-07-20/paginators-1.json,sha256=oioU0xuoNT12gWLZPvkd8rPQIM9gk8AOnNiZxDJybLs,3501 +botocore/data/proton/2020-07-20/service-2.json.gz,sha256=J7rkk4s_2lK9NPoJI5rKRAzA_Ic9YNoLODj4QDcSwtA,28216 +botocore/data/proton/2020-07-20/waiters-2.json,sha256=sGpaiRnx46CfHQh_T__IIByVlrchRRjseWa3NCdIqdI,6872 +botocore/data/qapps/2023-11-27/endpoint-rule-set-1.json.gz,sha256=zBWMjBr5VcCAvu36b0NibJWpTQS-3S0HromTYIQ2-Rs,1300 +botocore/data/qapps/2023-11-27/paginators-1.json,sha256=McjYxb7TrO3PLEXrYryPjrdMEbmbSYts2zAu2s0zbnY,340 +botocore/data/qapps/2023-11-27/service-2.json.gz,sha256=Y1rmLFJ3s0DblxgajDU4Igi78vCUQ-DpiEd4UctPYNY,13950 +botocore/data/qapps/2023-11-27/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/qbusiness/2023-11-27/endpoint-rule-set-1.json.gz,sha256=Sd0RLQopAaSFMlFLtdHl3qjQMDw4hnDLF1KLAv7heFA,1126 +botocore/data/qbusiness/2023-11-27/paginators-1.json,sha256=gZen1QNNvkJOLGMSxX-PievGiDr5-MEjDOr73fyIbvY,3436 +botocore/data/qbusiness/2023-11-27/paginators-1.sdk-extras.json,sha256=q8kHxHtnclzAwbKItnYG8gmYQx4NgxA2wfYQVA3yew8,428 +botocore/data/qbusiness/2023-11-27/service-2.json.gz,sha256=iwACQFOgI328XZVKh1_PFCjnMbvzORKj9eONeKIF_bE,55359 +botocore/data/qbusiness/2023-11-27/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/qconnect/2020-10-19/endpoint-rule-set-1.json.gz,sha256=qE7A7KgG_SrT-F4HRIy-hlPxR70mWJd0rJSpiLTg9Q8,1298 +botocore/data/qconnect/2020-10-19/paginators-1.json,sha256=GYd2gjUE20taDe6ajFlJocZuudyjyS6HBP11PTmuE_4,4093 +botocore/data/qconnect/2020-10-19/service-2.json.gz,sha256=-rKqNfY9cf-dUHxaDR1RaMl4FpqGhTQMh3MHV7uplVc,62356 +botocore/data/qconnect/2020-10-19/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/quicksight/2018-04-01/endpoint-rule-set-1.json.gz,sha256=v8zdjNt1hB70o584WlZaHPBmsjgt0oZCrM-7_28v0ag,1152 +botocore/data/quicksight/2018-04-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/quicksight/2018-04-01/paginators-1.json,sha256=16etuGEJhevO-PqxvfNfgwwAU1ict4998Ky34-hgy4w,8612 +botocore/data/quicksight/2018-04-01/paginators-1.sdk-extras.json,sha256=ZUn62ax5ItlivzRGQuaFV_zpO2b_8P8snqcbH4S7h0w,5395 +botocore/data/quicksight/2018-04-01/service-2.json.gz,sha256=xkHHZNJMl4NNQi7BjMNwkiw9IqbDOyEqcR5tHw0ld84,253158 +botocore/data/ram/2018-01-04/endpoint-rule-set-1.json.gz,sha256=346kH9aS9I7VZ3R1UO0oxaC9DJpM7yToU9Bax_qpEMY,1230 +botocore/data/ram/2018-01-04/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/ram/2018-01-04/paginators-1.json,sha256=sVQ8Gi3Ud7-EOQivarb5SNo1spDuC88N7qI2Jg5JJ1I,1267 +botocore/data/ram/2018-01-04/service-2.json.gz,sha256=HWN3i2kOVPjAvw1BG8_hW713VJRiRiKuxpy2U0_Kpr4,19480 +botocore/data/rbin/2021-06-15/endpoint-rule-set-1.json.gz,sha256=jlBawwgNtlYliU_MvtKEG6cN5KUFlB-EQ1wklAH2cqc,1146 +botocore/data/rbin/2021-06-15/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/rbin/2021-06-15/paginators-1.json,sha256=LB-X6UiLpJdFPrOCSc0raKGabdXiY9PhtS7nzQJbMts,181 +botocore/data/rbin/2021-06-15/service-2.json.gz,sha256=891XSkww2_iS2k1SbvIc84fxdytAMzk5L3bXxaQaqFI,4962 +botocore/data/rds-data/2018-08-01/endpoint-rule-set-1.json.gz,sha256=AvsGtcrpHMiYU7aZkV8Oe0v8VOOTka_tEyqZ7HzlcFg,1149 +botocore/data/rds-data/2018-08-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/rds-data/2018-08-01/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/rds-data/2018-08-01/service-2.json.gz,sha256=NUwBwgz9UIiWLN0V9ack_UVYqcTiEhAFwH11vSRHxU8,6773 +botocore/data/rds/2014-09-01/endpoint-rule-set-1.json.gz,sha256=vhXb58twMj94xPoydYGJ4v0R5FF2m7KLUIlneR-hWvE,1234 +botocore/data/rds/2014-09-01/paginators-1.json,sha256=CKMhQjYqNQB1hiHNi4vCNIVtQvu29SM_ySRhqxTKfOQ,3095 +botocore/data/rds/2014-09-01/service-2.json.gz,sha256=Fo5EGTp9MiCQHk_Cpsb7i4kG5GE6VE4UjJZ4mbfV-9M,37839 +botocore/data/rds/2014-09-01/waiters-2.json,sha256=9BpCCotIHKKeyJHD5Bo1fdRi6EnHK6jyJJx_9wswzCQ,2645 +botocore/data/rds/2014-10-31/endpoint-rule-set-1.json.gz,sha256=Pkub-pR1A2BTKs3Cmqou5hsB_djGU51vGJG9B92njME,1230 +botocore/data/rds/2014-10-31/examples-1.json,sha256=Pa_Dpbo8pg0O9rZRPEuFXsgnzT6XUqIfwHpXauQnc0M,57903 +botocore/data/rds/2014-10-31/paginators-1.json,sha256=tEQ2vTQBWe75XcfKBUsDdc3jb82g62kS0IbHzJ9GrNg,7602 +botocore/data/rds/2014-10-31/paginators-1.sdk-extras.json,sha256=S21buVoyp0LlykSD0lYWlVIRbOqJB4qpVw7mt2GFprQ,192 +botocore/data/rds/2014-10-31/service-2.json.gz,sha256=lu0iCW8WiJ0UfV7IDPW5VcbhyAW-ZyGGPAtNIpmnUrY,173849 +botocore/data/rds/2014-10-31/service-2.sdk-extras.json,sha256=NWqAyPauBSLTPFOO_wMu4XZ7VTkw7nY8QjCorphUpTM,1345 +botocore/data/rds/2014-10-31/waiters-2.json,sha256=Cie-0ucJVp-eOgbIO-Q7x05yv0nGajshOhmHvB_K6wo,10970 +botocore/data/redshift-data/2019-12-20/endpoint-rule-set-1.json.gz,sha256=XcpKPS3zPulZORWChhL7wF_-QIWkvtUKRvTgUVSby9A,1152 +botocore/data/redshift-data/2019-12-20/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/redshift-data/2019-12-20/paginators-1.json,sha256=_enxtlCQYXMee7Z0xVLUczdQOr6CNgyI0NFtikx5Dz8,1270 +botocore/data/redshift-data/2019-12-20/paginators-1.sdk-extras.json,sha256=W5XyQjgYJkSJeO772ifvKMUzs8b5j4AvLb8YQwWnm_w,458 +botocore/data/redshift-data/2019-12-20/service-2.json.gz,sha256=hMH7v3J0jTg97s5JvdlKBXYCbpnbWX-rD0nFpbbJ5Go,9503 +botocore/data/redshift-serverless/2021-04-21/endpoint-rule-set-1.json.gz,sha256=_Xi7PSV0cX87vJR9cJVHIdpIWCfOsulqO0HV64N8e7Q,1156 +botocore/data/redshift-serverless/2021-04-21/paginators-1.json,sha256=1vYwDzBLSRf-kJMkph5FppY0Ud0HNqTgr3GRJoiOKh4,2476 +botocore/data/redshift-serverless/2021-04-21/service-2.json.gz,sha256=HIsFjZ4gjrngH1ZRFe_V_QjfsFANusM_hbT99hZXzBQ,24440 +botocore/data/redshift/2012-12-01/endpoint-rule-set-1.json.gz,sha256=vCidxBrHAF-SYVyleMQf15pOdH8pJdkj8Kja6F6Hyyw,1234 +botocore/data/redshift/2012-12-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/redshift/2012-12-01/paginators-1.json,sha256=xJDyKYCexBfK1p5WxFrVdpZdylR_0FJ0O-74NRnjIKs,6883 +botocore/data/redshift/2012-12-01/service-2.json.gz,sha256=FDRB60cSCB22ES5FzSRYzqq9xiofFfAjLQXP5zAGrH4,80468 +botocore/data/redshift/2012-12-01/waiters-2.json,sha256=mvax_COD6X10xa7Toxa2DsrarFdKFg9kOWbIKRLahS4,2344 +botocore/data/rekognition/2016-06-27/endpoint-rule-set-1.json.gz,sha256=3l6ka-StzJosu5k-Sa12TgUak_-AOHTzYr_4CCbNBsM,1150 +botocore/data/rekognition/2016-06-27/examples-1.json,sha256=pEUj6cF9yKB10eaE3lAAObBMc4nV3Jak105Ro2A3ZMc,20327 +botocore/data/rekognition/2016-06-27/paginators-1.json,sha256=mDoU6wXUCCgHeOrcvnEqTpQ18yV5otpEqZt5TsFarFA,1699 +botocore/data/rekognition/2016-06-27/service-2.json.gz,sha256=-cXUKIA9HifOEWZxpnnLpXquse2sB15i6L5VrbPM_PU,72839 +botocore/data/rekognition/2016-06-27/waiters-2.json,sha256=KRKVzu37WzZwVdazhDURGYo_qTbgIDDIhBTPyvTt1lg,1542 +botocore/data/repostspace/2022-05-13/endpoint-rule-set-1.json.gz,sha256=gQIigQqZejzy_Epavin-KR3AcpawWzq6lXtI-5dlmdw,1300 +botocore/data/repostspace/2022-05-13/paginators-1.json,sha256=YMwMMFg603UCo-j7eNe9slLy3jUAAYa_ODX-Fh8ffd8,345 +botocore/data/repostspace/2022-05-13/service-2.json.gz,sha256=v-8_wr_5xI3Y9Vn08oDSW_DcSbMO-xJWAKsf2Qj_B1M,5946 +botocore/data/repostspace/2022-05-13/waiters-2.json,sha256=IFWB48e2MaBFCt9EUH0lvhocQtIiyM5RKbLNpjKLwOY,2429 +botocore/data/resiliencehub/2020-04-30/endpoint-rule-set-1.json.gz,sha256=x3OwsGUvTDtYfRKkmwPVB9P4V7MDF_LNCAtFB77ZdlM,1152 +botocore/data/resiliencehub/2020-04-30/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/resiliencehub/2020-04-30/paginators-1.json,sha256=i9EQooI4rve2dtahGRtoAc2pv-KEBS8cGy_L4ND_khM,569 +botocore/data/resiliencehub/2020-04-30/service-2.json.gz,sha256=csZ4ck4CCPKUGz9Ng-KeZSUbRBOjK82BqBeAeXbe8iQ,29858 +botocore/data/resiliencehubv2/2026-02-17/endpoint-rule-set-1.json.gz,sha256=_VStASpWu2lDGug1IzgpYD-M4JiTi9JQ3dCJqc5nIVc,1299 +botocore/data/resiliencehubv2/2026-02-17/paginators-1.json,sha256=hUWURNtL3dSLD4RkuDOPUmxDily3MLpbZyMfmXTQBww,4195 +botocore/data/resiliencehubv2/2026-02-17/paginators-1.sdk-extras.json,sha256=vNnpjloHYq0yGJl8JfYxzIA1O6L3tXVU0WEuK_GaaDI,170 +botocore/data/resiliencehubv2/2026-02-17/service-2.json.gz,sha256=_Z_mbSESnMsZ86j_Qpk9JU8CmeYf_6zFLRLTe3Jucfk,28572 +botocore/data/resiliencehubv2/2026-02-17/waiters-2.json,sha256=whQUgXAKKLUwqIqGJNPNW9lUAL-BZ7oXlWgW5e0sYsg,2677 +botocore/data/resource-explorer-2/2022-07-28/endpoint-rule-set-1.json.gz,sha256=F97-QNjCg-RZENl0Q_16onCzVUGuwpI8R4gvxA_MDy0,1158 +botocore/data/resource-explorer-2/2022-07-28/paginators-1.json,sha256=PuLP47m2fS4G52Ue_2zXbnv1mbBD9Nd9Rjb9ZH4H6pE,1891 +botocore/data/resource-explorer-2/2022-07-28/paginators-1.sdk-extras.json,sha256=1BdFcjO9uS6r4XTazfeCLVaX7KPO_oELRwSUy9VBt7M,268 +botocore/data/resource-explorer-2/2022-07-28/service-2.json.gz,sha256=H0xHFZikxVQjzqxfYabQwx94VBSubH0CgzbSqgy404A,16052 +botocore/data/resource-explorer-2/2022-07-28/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/resource-groups/2017-11-27/endpoint-rule-set-1.json.gz,sha256=kG7OPT4iX12YehvWdSObqyr9N1HYVboj-GcBWTzsMpI,1239 +botocore/data/resource-groups/2017-11-27/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/resource-groups/2017-11-27/paginators-1.json,sha256=UDYlyYXlpEjfEq1H2dATIOO_M33BElQGPX3C7qMybI8,971 +botocore/data/resource-groups/2017-11-27/paginators-1.sdk-extras.json,sha256=sLaKgsyulktCelU4GGH6YRaRLWwmRiSmoKar3VfbunY,165 +botocore/data/resource-groups/2017-11-27/service-2.json.gz,sha256=4rodiBYH4yeram7cmWprBAzADHPw1Ta7Gs9pqxGrcXI,14326 +botocore/data/resourcegroupstaggingapi/2017-01-26/endpoint-rule-set-1.json.gz,sha256=9KazffOGEm5UE1pi2GXGseNw9ZkXMc2jIqhL0-OPWBU,1149 +botocore/data/resourcegroupstaggingapi/2017-01-26/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/resourcegroupstaggingapi/2017-01-26/paginators-1.json,sha256=foWHoPRSV6VjAwni6ujDQPi6y99hZYvvnaMzbzSAqFY,854 +botocore/data/resourcegroupstaggingapi/2017-01-26/service-2.json.gz,sha256=6tvZRYAtY3cpc_e6LH1Vm45YYsv-91ml3ZiR5xUB5kY,8957 +botocore/data/rolesanywhere/2018-05-10/endpoint-rule-set-1.json.gz,sha256=TrNjky0FuZ42jKRdO2Hltn2txQBiPzD7b5RncaGtxDs,1153 +botocore/data/rolesanywhere/2018-05-10/paginators-1.json,sha256=IaF8k8b_3R6qbXcxbFkIQqN0DTaCim4eQhIiEanVZkc,541 +botocore/data/rolesanywhere/2018-05-10/service-2.json.gz,sha256=F6VjqEJzGcxz-XKmNCadfPRsQd4nwP1u9fxgHLyPKGk,7229 +botocore/data/rolesanywhere/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/route53-recovery-cluster/2019-12-02/endpoint-rule-set-1.json.gz,sha256=WaX8PzRDFvIYmbK8VF8KsqK15Hc1JzdpHBPh1swb1Z8,1168 +botocore/data/route53-recovery-cluster/2019-12-02/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/route53-recovery-cluster/2019-12-02/paginators-1.json,sha256=UhH6MsunbcB3w057DvJoHxEWGweOpch31kCr68-51eQ,201 +botocore/data/route53-recovery-cluster/2019-12-02/service-2.json.gz,sha256=odx0i9ORvZ1JyjiBzx_krPbL3VZyqXn1VezN4lNn-6o,4044 +botocore/data/route53-recovery-control-config/2020-11-02/endpoint-rule-set-1.json.gz,sha256=BX6k80kespNoM3s4ooM7L0FaJuevD5SUzlFpx6SLk8c,1563 +botocore/data/route53-recovery-control-config/2020-11-02/paginators-1.json,sha256=eDByeUTgAtdsrqJD0NiWUp5AfuXhqM2q0oa-5MCgt38,892 +botocore/data/route53-recovery-control-config/2020-11-02/service-2.json.gz,sha256=-acu6Hv4XZbLbLPwVMq32Mo9bAxHMuXu6B_NdKUKNEg,8486 +botocore/data/route53-recovery-control-config/2020-11-02/waiters-2.json,sha256=iw6vHr5XZ7c87aPCP4ejk0EHpOVt-ZT2ioC0asbgGJA,3674 +botocore/data/route53-recovery-readiness/2019-12-02/endpoint-rule-set-1.json.gz,sha256=9PcM3vW2zFPOxPHBUmBP0n96sdMIZzyumswBLHci7JM,1166 +botocore/data/route53-recovery-readiness/2019-12-02/paginators-1.json,sha256=bkbDR1VU1mtDe84KapiLM8rWUPHKj-aEpn7TLzqFeW0,2032 +botocore/data/route53-recovery-readiness/2019-12-02/service-2.json.gz,sha256=8e6drjw7rNcWzDlcpKHz0_3JuPQ7JjbI8I2rXUl9vMY,7335 +botocore/data/route53/2013-04-01/endpoint-rule-set-1.json.gz,sha256=rhzRNumuzAGmNq6AYReFpUTSL3tQKS1yeiwa3s4pyQY,2244 +botocore/data/route53/2013-04-01/examples-1.json,sha256=C3c7hhO4Y2jbpqrTEGNc7x007deldIJsNVDxdhaH_T8,29631 +botocore/data/route53/2013-04-01/paginators-1.json,sha256=-nS2WnQKiOUbqyQRXiMxCbqHwZ7xJQXVS98-vYEjiuI,1734 +botocore/data/route53/2013-04-01/service-2.json.gz,sha256=q-SmgEY4R04POfTnZROII1QTDOPduJu7KKOhuMJZ9Iw,64381 +botocore/data/route53/2013-04-01/waiters-2.json,sha256=s6BzW8AQ9pEM5yCsRa64E7lfUvhX5vxNARuiAtZwjsU,338 +botocore/data/route53domains/2014-05-15/endpoint-rule-set-1.json.gz,sha256=too9mxawEpfhXMCU6IaosINHXflQcA4slJCpgHhYxGg,1155 +botocore/data/route53domains/2014-05-15/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/route53domains/2014-05-15/paginators-1.json,sha256=VN49BhgZ_VxpcqSi9W0aIr8bv4iFv9QnrVoUerrFwoI,696 +botocore/data/route53domains/2014-05-15/service-2.json.gz,sha256=lwiV15eB5A3y0JWFl-slTQP2ecGPGPwRADRYxKSdv3U,22427 +botocore/data/route53globalresolver/2022-09-27/endpoint-rule-set-1.json.gz,sha256=SlsJNctMrFaw6mjdU1hPGn4CqqGk45TDPW5XuGOSCMw,845 +botocore/data/route53globalresolver/2022-09-27/paginators-1.json,sha256=aGbGK6uzGJYeM9kVSLLwGml6g13SFOCe9COvovr1g7g,1785 +botocore/data/route53globalresolver/2022-09-27/service-2.json.gz,sha256=DOX8Xja79nwIBuDZ6WONC7pGjtzOtxMkiDKXFkd9PQg,18078 +botocore/data/route53globalresolver/2022-09-27/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/route53profiles/2018-05-10/endpoint-rule-set-1.json.gz,sha256=pBviN_GLGT89uapri-iKU3JusUoKmblDVIE7jwgRCXI,1306 +botocore/data/route53profiles/2018-05-10/paginators-1.json,sha256=-QdEoZNxlj37dlhmE8U5hCOev_UGR-5Nv-_bCti2HtE,579 +botocore/data/route53profiles/2018-05-10/service-2.json.gz,sha256=dwAIiAmar1fkJ8ypuhFCBlXxzmSTio5rQyZohnRr4Q8,4822 +botocore/data/route53resolver/2018-04-01/endpoint-rule-set-1.json.gz,sha256=QohN4qNdMbTBSnJdRfG3tJJjZ7uOR2Fj_RbBA9ZqvR0,1240 +botocore/data/route53resolver/2018-04-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/route53resolver/2018-04-01/paginators-1.json,sha256=-DmN0JhkYyfaTvKvexVGNxJ5xQoTnQVyF-z61_kpFTU,3134 +botocore/data/route53resolver/2018-04-01/paginators-1.sdk-extras.json,sha256=3XJ5UEbB_NT-xjx41jRgxgoNKMWuUL-bcLPzf9n1o9I,806 +botocore/data/route53resolver/2018-04-01/service-2.json.gz,sha256=-ljg590-wHXZCGx6fQpzm3ufyTUkm63pgkfFbmIMMx0,41947 +botocore/data/rtbfabric/2023-05-15/endpoint-rule-set-1.json.gz,sha256=39ThM4B7F5UaHNy5BwpAlGC9bH_SMkg_oUxxCV34E1U,1279 +botocore/data/rtbfabric/2023-05-15/paginators-1.json,sha256=lcwlu6jIwitInYxKFNw1galcLL8L_mxSUqEuoayAVTQ,886 +botocore/data/rtbfabric/2023-05-15/service-2.json.gz,sha256=OTy27J_eD0AjsCG9kaCi22aEt60TshTlG074M77Unc0,14000 +botocore/data/rtbfabric/2023-05-15/waiters-2.json,sha256=0zAqHl3JeT6M_rjSxSbjFuRIg1yoebQWE20lUmyGLg8,9264 +botocore/data/rum/2018-05-10/endpoint-rule-set-1.json.gz,sha256=mlnNkP9yblTzyPophBDcibpsx-TmmCxH-j6FI4CJ7FQ,1145 +botocore/data/rum/2018-05-10/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/rum/2018-05-10/paginators-1.json,sha256=eiv4iOnLCb9wVy6VijmIS8FeKbt7SfSmIY3M4qv3wIs,733 +botocore/data/rum/2018-05-10/service-2.json.gz,sha256=jneMUUKmIGkbacMsaN_GL_OHb5uukVn98HWOfI5RwQg,15171 +botocore/data/rum/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/s3/2006-03-01/endpoint-rule-set-1.json.gz,sha256=6vvxtEvSqd5tPabbInvg7kaCvEYkb1-uaDbW83V-Cyw,23219 +botocore/data/s3/2006-03-01/examples-1.json,sha256=bGw9MrbmwHRES_w7kwW-Hr31-Js7JGP_oxoE4Tw21b4,57596 +botocore/data/s3/2006-03-01/paginators-1.json,sha256=2mUIB46tWmydX57d3LQ25ZlFhEhglsWib-HwLCnEsMg,2041 +botocore/data/s3/2006-03-01/paginators-1.sdk-extras.json,sha256=IgryxNZnV4NbLzxUSFairUWcqfokTwRjuQo-9ns2Yww,1118 +botocore/data/s3/2006-03-01/service-2.json.gz,sha256=yrqs-WY0Xaniv0u-u2mIWKVWZKe9LP6-f9wC2Wi1zLM,184637 +botocore/data/s3/2006-03-01/service-2.sdk-extras.json,sha256=Fhejim14rytpb7Tha_0Y63Ktc6qdJHvAeGQ8-d_WZAg,98 +botocore/data/s3/2006-03-01/waiters-2.json,sha256=m0RJIxnJW7u6emLjY1201rmfeKxgz1f7VDU7qKJOI4c,1436 +botocore/data/s3control/2018-08-20/endpoint-rule-set-1.json.gz,sha256=svdECBD8HgcWtWyaR6U7p3leR6938L3GWvfpsboyebQ,9127 +botocore/data/s3control/2018-08-20/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/s3control/2018-08-20/paginators-1.json,sha256=XOG3QAgT_Ohb5f0Z6B-TyJTX1M_rM05_HryKrEyxYiM,603 +botocore/data/s3control/2018-08-20/service-2.json.gz,sha256=1KffrrzsFYqPey8MdCU-umdaL2rQRUNCFgtG098Zwi4,67598 +botocore/data/s3files/2025-05-05/endpoint-rule-set-1.json.gz,sha256=_sh1LoX_HW_sNksfw805Z_Xfbom3QkF9mLkMH4ngPGo,834 +botocore/data/s3files/2025-05-05/paginators-1.json,sha256=f7jI8jM1wNmzxbBRh_dNnA8JAyxfbzhxF11r7UznRm8,698 +botocore/data/s3files/2025-05-05/service-2.json.gz,sha256=3uPbbEqDnZcV8SWqbLl5VFjtDRjVXR_rUMf47I_R71I,8629 +botocore/data/s3files/2025-05-05/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/s3outposts/2017-07-25/endpoint-rule-set-1.json.gz,sha256=HlweDNuvqo1LljzdshRtwdYqVGTl8M6ACQ6Z7o3i2Uc,1152 +botocore/data/s3outposts/2017-07-25/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/s3outposts/2017-07-25/paginators-1.json,sha256=MNhAyDW1gAXK_msh5EL1QpaFjXE7KCnk2xzMuUZUiT4,527 +botocore/data/s3outposts/2017-07-25/service-2.json.gz,sha256=0JPfe6Z-l7keov5jXc7XMYrwGzvc2BpNp8mVcJ-rIDs,3475 +botocore/data/s3tables/2018-05-10/endpoint-rule-set-1.json.gz,sha256=THUtywM-OvNUPuS8XqjoMGPpfGw6xbtqR5izU40DuBk,1298 +botocore/data/s3tables/2018-05-10/paginators-1.json,sha256=V7AYoq3xxG0TSO6MYFTjrAVhuUQ2fdLSCfrIS-LheUk,569 +botocore/data/s3tables/2018-05-10/service-2.json.gz,sha256=2xTl9eoH7ywh06CvSgX4cj5nHR-L2MJsyf8XvvYjBcY,15153 +botocore/data/s3tables/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/s3vectors/2025-07-15/endpoint-rule-set-1.json.gz,sha256=LIWVCbT2L22M8qwCCnWxyQUcXvnI7ZMbIaVPkz6fN_M,835 +botocore/data/s3vectors/2025-07-15/paginators-1.json,sha256=R2YydGBN5C5LKwTrBLOaEmGbblN6XKF1T4LsLvucH1I,645 +botocore/data/s3vectors/2025-07-15/paginators-1.sdk-extras.json,sha256=6yIRHpIhFIaZyEuV-i29l8i7LGwVvZ7cumQBsieFqEI,166 +botocore/data/s3vectors/2025-07-15/service-2.json.gz,sha256=wIXoiv7uP18TgQ50UjaXBrgVroeVptmeKcnSc9PGHyU,8825 +botocore/data/s3vectors/2025-07-15/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/sagemaker-a2i-runtime/2019-11-07/endpoint-rule-set-1.json.gz,sha256=AbxRb1I553vAO1I3YjU3x3-vM1sf-RCTGi12zThXeK4,1159 +botocore/data/sagemaker-a2i-runtime/2019-11-07/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/sagemaker-a2i-runtime/2019-11-07/paginators-1.json,sha256=X0gq-uz_QUVGPACQxWwKf6n-ZZ-MsaXi3huDYMOu10o,199 +botocore/data/sagemaker-a2i-runtime/2019-11-07/service-2.json.gz,sha256=fih3ll6jkzauG7O6MAjs-SK9yI0wdJFss3F97Pk8SB8,3798 +botocore/data/sagemaker-edge/2020-09-23/endpoint-rule-set-1.json.gz,sha256=zG2DLeqmOFXiOxFBx2dwK5fwsxatTArd4wLUGz_2ZFE,1153 +botocore/data/sagemaker-edge/2020-09-23/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/sagemaker-edge/2020-09-23/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/sagemaker-edge/2020-09-23/service-2.json.gz,sha256=9_Qfm3ltVwI6vDgMwJ8uJuNaw9e2--aZ-IEcoK8KDsw,2248 +botocore/data/sagemaker-featurestore-runtime/2020-07-01/endpoint-rule-set-1.json.gz,sha256=cncgyAEppdXjiK_vkm1srra5NdqEnO6dgKteWXJW9bU,1168 +botocore/data/sagemaker-featurestore-runtime/2020-07-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/sagemaker-featurestore-runtime/2020-07-01/paginators-1.json,sha256=Tg9HiC0dBjl4hw-YOH4z098KPvy2r_GNtgN0qnzn-Qk,195 +botocore/data/sagemaker-featurestore-runtime/2020-07-01/service-2.json.gz,sha256=_9sKAIaznOAec-E3YzdmOFpLDCc76APoKxmLl5hnLa8,5713 +botocore/data/sagemaker-geospatial/2020-05-27/endpoint-rule-set-1.json.gz,sha256=tHh6hqVw4GBtAt4S0FUVT0d99gQBa56atdt2FxjMl_o,1308 +botocore/data/sagemaker-geospatial/2020-05-27/paginators-1.json,sha256=F6o4MlbqixSACzxItwWHBiMmvvc3VqdxdWlY9NRKy6E,609 +botocore/data/sagemaker-geospatial/2020-05-27/service-2.json.gz,sha256=JP9hV9tBggJ0bVsLV6AdG9ANEjYrhbezYIXySt5olhw,11920 +botocore/data/sagemaker-metrics/2022-09-30/endpoint-rule-set-1.json.gz,sha256=Y7UDqmVenPgBAQiOzh-rkLAOq-Ana51Rt7SMcuAnVp8,1240 +botocore/data/sagemaker-metrics/2022-09-30/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/sagemaker-metrics/2022-09-30/service-2.json.gz,sha256=coUwobjX9v8BlS5eL4s-o_zGsKme1YcQqGQzVokPdTc,2024 +botocore/data/sagemaker-runtime/2017-05-13/endpoint-rule-set-1.json.gz,sha256=qQDkvXuWspGLkZ8x9gWDkO9wo4Dzx4Cn26aXEdGsDmQ,1271 +botocore/data/sagemaker-runtime/2017-05-13/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/sagemaker-runtime/2017-05-13/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/sagemaker-runtime/2017-05-13/service-2.json.gz,sha256=ZFcvK1h8xu14K3r7PIEBQXpNMyd6eqIX2tJsKWvxzks,6257 +botocore/data/sagemaker/2017-07-24/endpoint-rule-set-1.json.gz,sha256=50mCnJ4y1DVINDmI-TzGqdFp4sXoP07imrylFwpw3yg,1268 +botocore/data/sagemaker/2017-07-24/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/sagemaker/2017-07-24/paginators-1.json,sha256=DkaFSBKQCKQIesCPQuwlOtrZnka1Kv_F-0mK4a8iBWc,16075 +botocore/data/sagemaker/2017-07-24/paginators-1.sdk-extras.json,sha256=ibwgf2aj5blabLx_CmGc9HT5PfqVfBU1UhdXmAVxmi0,154 +botocore/data/sagemaker/2017-07-24/service-2.json.gz,sha256=JbKf-50CzajWWsI8EFibUUohZzZmUTv_eOojMuly3CE,371738 +botocore/data/sagemaker/2017-07-24/waiters-2.json,sha256=dwquOoDq4TRr9dNbKme2UILOi8MJfe5ADTYkP4bfABA,7018 +botocore/data/sagemakerjobruntime/2026-02-01/endpoint-rule-set-1.json.gz,sha256=OKezSLoDJ8Plk_8_jlZ1WKPIqp3-sRArVNNJnuIatqU,1001 +botocore/data/sagemakerjobruntime/2026-02-01/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/sagemakerjobruntime/2026-02-01/service-2.json.gz,sha256=PMzYPWC8NgDTi_yEbr_obuKGpucHfBSM2OTsGAK4mWo,2312 +botocore/data/savingsplans/2019-06-28/endpoint-rule-set-1.json.gz,sha256=fFUqg-BmmtkIImoXnCxem7DmTGUjIbrH7KRT7FXyhNA,1543 +botocore/data/savingsplans/2019-06-28/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/savingsplans/2019-06-28/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/savingsplans/2019-06-28/service-2.json.gz,sha256=2l5XXKEEDWfB_JhNvV3oKcascRY2SZqypXEuT7LNOgQ,4572 +botocore/data/scheduler/2021-06-30/endpoint-rule-set-1.json.gz,sha256=xW-YDNNrBTiW9IGyiizazs4wG0YBgS4V-zVLTxNlYkc,1296 +botocore/data/scheduler/2021-06-30/paginators-1.json,sha256=VH5c3yVo1Un4lL_GVN-D3A5GNOTWMmnqAQ0QZAOMJOo,363 +botocore/data/scheduler/2021-06-30/service-2.json.gz,sha256=nC_EfFTWb8HWRsVIdAfruhlSIfuSvelRVfmuTVooheA,9349 +botocore/data/schemas/2019-12-02/endpoint-rule-set-1.json.gz,sha256=OZiKt-qBQYBEPSY-UwVTgVuwXJ5tnIP4vVHdjbIV81Y,1149 +botocore/data/schemas/2019-12-02/paginators-1.json,sha256=JG7VhSHU5MW5ZSEzWuvc0fcOMdYngtguHEeVk1fPoro,830 +botocore/data/schemas/2019-12-02/service-2.json.gz,sha256=wk0kbkfeAO5-linwVMFkEuA8cbfDIgY8Sbv_t3Ztx3s,5722 +botocore/data/schemas/2019-12-02/waiters-2.json,sha256=t1IowU2djOrDdhK7r7dmmVfVARz1Zp31Dl3MPtnqy5I,824 +botocore/data/sdb/2009-04-15/endpoint-rule-set-1.json.gz,sha256=47nop1bB_CJD475lT6FbViPhA-93RW_Bwd8-TOqOcWc,1198 +botocore/data/sdb/2009-04-15/paginators-1.json,sha256=3KF7ZF879CPbTIZ8drlqnq5S3aFHdubXunwekE3ARG4,317 +botocore/data/sdb/2009-04-15/service-2.json.gz,sha256=MpcRuu3ML15QaKtqwehwKHTxUHTZZ6ykMuaINsLrrLw,6036 +botocore/data/sdk-default-configuration.json,sha256=LlmdeqSk0HQAKMCGNgPsFO1K6dJXQdjzq8Ad3wRs7g8,4135 +botocore/data/secretsmanager/2017-10-17/endpoint-rule-set-1.json.gz,sha256=2ClheJbZyNLUKTSmLkfROmG8ptQvKHSiYizEJ0Xmqcs,1351 +botocore/data/secretsmanager/2017-10-17/examples-1.json,sha256=3LKYx_uc48qXDFx7m8cU2l8XByq1wu28h5fOggrmDCI,22410 +botocore/data/secretsmanager/2017-10-17/paginators-1.json,sha256=wFoEW6m_jRSAAt8D1r54a9XXWnZerkFn83sHj413-ww,188 +botocore/data/secretsmanager/2017-10-17/service-2.json.gz,sha256=Sw9SNoj-ioyfjUTUqNP-Ugybddm3ekLClUkAzBhfSmo,22383 +botocore/data/secretsmanager/2017-10-17/service-2.sdk-extras.json,sha256=IEA3uxtjPY8I1on-q2W9-tozHHIVmneQyB6gCTcYTro,120 +botocore/data/security-ir/2018-05-10/endpoint-rule-set-1.json.gz,sha256=FQn6ytfDWwHInPQF5iFJbQoBL33vxafzzoYSXrP6A-g,835 +botocore/data/security-ir/2018-05-10/paginators-1.json,sha256=0Rs0rrkL3TlunqQTlmqG3Exz6DaEY1o4HU93oVh66QA,842 +botocore/data/security-ir/2018-05-10/paginators-1.sdk-extras.json,sha256=fyqNotjQNWEgg7fBBmltwWw7IoVr8M4vv9081d1COKk,339 +botocore/data/security-ir/2018-05-10/service-2.json.gz,sha256=y87HsxP17wc-xYwa1cVjyRf4WMSh095FxPVM5DMMNl4,12359 +botocore/data/security-ir/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/securityagent/2025-09-06/endpoint-rule-set-1.json.gz,sha256=yj82kqRFUkMv7K8T7V5nYan8z4ocYmsuLMQ8_jQ__es,837 +botocore/data/securityagent/2025-09-06/paginators-1.json,sha256=XcbxuK_O9Le_HBg4G-lLLiATzddDEjtRlx8HPntAnv0,4182 +botocore/data/securityagent/2025-09-06/service-2.json.gz,sha256=_qbhexVSPzxVE6YtVWFusueOhiXHxmYG4aM6AyaoRYA,37578 +botocore/data/securityagent/2025-09-06/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/securityhub/2018-10-26/endpoint-rule-set-1.json.gz,sha256=g1nvbSV0xbzM0EH2DefaRsmz2PsYYoWCoaDku3FXTU8,1151 +botocore/data/securityhub/2018-10-26/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/securityhub/2018-10-26/paginators-1.json,sha256=FyVRzZ7uVAyOMm_bhT1m4AGS1XV6AF0EJ6aDhI0o70g,4475 +botocore/data/securityhub/2018-10-26/paginators-1.sdk-extras.json,sha256=rLnwrpPspDeMERSjLYUetXaDXhYmf8ThnPy59C1qFx8,659 +botocore/data/securityhub/2018-10-26/service-2.json.gz,sha256=A7IhL_FndEZq_jN2-UKfBMm-4Aw7TrzwNoQfJA2rgr8,177405 +botocore/data/securitylake/2018-05-10/endpoint-rule-set-1.json.gz,sha256=lhGhCXRQARVu-80lsHIo6xyKHButAmtV0TXOygI8BbA,1299 +botocore/data/securitylake/2018-05-10/paginators-1.json,sha256=aw_RlW6BEfqxgzWUOJF6ZrCTf49mvjJ9uAmhefV_2kg,705 +botocore/data/securitylake/2018-05-10/paginators-1.sdk-extras.json,sha256=v0jKSsBUrC-WdKoMQzNm6hfXLmDajUWqKZtLDn1TA9k,169 +botocore/data/securitylake/2018-05-10/service-2.json.gz,sha256=eKSL3t5KnKMo80CF6YyyN06_WpnmV8fQZ0WnKp69MLE,14449 +botocore/data/serverlessrepo/2017-09-08/endpoint-rule-set-1.json.gz,sha256=12YrM8jdl1iE7bU5o14VNR-q5zkhhMGq4ZgL5IGOMWo,1237 +botocore/data/serverlessrepo/2017-09-08/paginators-1.json,sha256=6mp7kgpraGJSmfK8vEcMsz_LdDUfQN9dI4kjn83wRhY,543 +botocore/data/serverlessrepo/2017-09-08/service-2.json.gz,sha256=1lpTNtrF9DrLG7SNy8AY8Lm7d_J8zpnwxtNSghWLU9Y,9570 +botocore/data/service-quotas/2019-06-24/endpoint-rule-set-1.json.gz,sha256=gdOyS10VghkFOtWORBi2mmzrTt1hwMRqYHtDRWJPizM,1237 +botocore/data/service-quotas/2019-06-24/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/service-quotas/2019-06-24/paginators-1.json,sha256=e9hZphztzUJpLy1e7mpXUhwobjRsYyLMWkY1DYQfRpw,1149 +botocore/data/service-quotas/2019-06-24/service-2.json.gz,sha256=B2IfFchqITC0QMcD4HXv34vfbnjVeslpB1X7xb_Xofc,10631 +botocore/data/servicecatalog-appregistry/2020-06-24/endpoint-rule-set-1.json.gz,sha256=kkpbKqI6ovGda_xwVKClPDFryB2pgn7NegtCqvsFI_E,1247 +botocore/data/servicecatalog-appregistry/2020-06-24/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/servicecatalog-appregistry/2020-06-24/paginators-1.json,sha256=2lclqrEMyRSrGV8L1DZoppkiLhUGI1VcinKImELBFi0,928 +botocore/data/servicecatalog-appregistry/2020-06-24/service-2.json.gz,sha256=pH5rKi1xDH3WttA8mWrEGpV6N2pokPYY-sLgKuiZ9_Q,7868 +botocore/data/servicecatalog/2015-12-10/endpoint-rule-set-1.json.gz,sha256=PSuoJo41DrpbIpVq1bx7NG0RjNC1yCDPh9K-3iBGgeE,1153 +botocore/data/servicecatalog/2015-12-10/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/servicecatalog/2015-12-10/paginators-1.json,sha256=ghdoop27a-JBxcfHGVvA0vMp9y_Z-MY1R7TuRQCrmP4,2996 +botocore/data/servicecatalog/2015-12-10/service-2.json.gz,sha256=sEm8koX2LfuvMK1ynJMnACSE8B_ErBlSrecd5n-0CWE,37405 +botocore/data/servicediscovery/2017-03-14/endpoint-rule-set-1.json.gz,sha256=_6-ns9iEteCIOPnnDfXHbStJB08L3jPraiDY7hlK23o,1285 +botocore/data/servicediscovery/2017-03-14/examples-1.json,sha256=iJqJB_1uy_oppRbcXbl5SmCA2yLLVdSdj674nZ7dSQQ,18861 +botocore/data/servicediscovery/2017-03-14/paginators-1.json,sha256=sKu-j-WBHT8KpiemY4vgLiQkV1Ub2GtqLbYiUxdkjjE,683 +botocore/data/servicediscovery/2017-03-14/paginators-1.sdk-extras.json,sha256=a89DrBwGFK_Oa_9ICtG1QFEBjaavhUhMm-2DyI02_Nw,166 +botocore/data/servicediscovery/2017-03-14/service-2.json.gz,sha256=R6FeVhttkIHTx_QS4QJ___hZb40oOs5VPBtqcruJJ_M,20605 +botocore/data/ses/2010-12-01/endpoint-rule-set-1.json.gz,sha256=HbfARjrT4LonP8LxCb2iXEWch0pT0MJ0bJPUejQQVKs,1146 +botocore/data/ses/2010-12-01/examples-1.json,sha256=LdOG9qOcWahQ6xYBc3_UEV-teA96yJJSesbf0fNI8Bw,28834 +botocore/data/ses/2010-12-01/paginators-1.json,sha256=G_7q2KFDP0LwwEUoCgd9qikwYlHoaFwDjQ_3CtWBVPw,883 +botocore/data/ses/2010-12-01/service-2.json.gz,sha256=xO1bmMDLBvHYmGnpCT9E1oE3V5evkQyFFk65jHp2Y4M,36439 +botocore/data/ses/2010-12-01/waiters-2.json,sha256=4GF4zY3Tg43WiGAVWSJeabII8bSEU7_ElsMj_G3Bt68,380 +botocore/data/sesv2/2019-09-27/endpoint-rule-set-1.json.gz,sha256=HN73tFjBMCKL3gGZzBH_vOES_GnvWpiBsrHLh_ZKm8w,1844 +botocore/data/sesv2/2019-09-27/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/sesv2/2019-09-27/paginators-1.json,sha256=Ga1EbNqLYpgIur4tDR4rKS8VZhw_GIxdsoYGnbfgTuk,1076 +botocore/data/sesv2/2019-09-27/service-2.json.gz,sha256=6rJaMAYdizTTn2BLIDpWn-3p8TKlFyEK6JBTgVp7_nE,71176 +botocore/data/shield/2016-06-02/endpoint-rule-set-1.json.gz,sha256=j-tvK99YlMng2rdLpippMs9EI0Byhp32_HOzlax5X_k,1345 +botocore/data/shield/2016-06-02/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/shield/2016-06-02/paginators-1.json,sha256=MRQd38Sw6vMYcdoF_zRIXAdMaDQHs_indt6OtJxi0BE,361 +botocore/data/shield/2016-06-02/service-2.json.gz,sha256=Sn7evOrxXeKnnmbWb77iV50BL--3TMX4riV1tchyqis,15250 +botocore/data/signer-data/2017-08-25/endpoint-rule-set-1.json.gz,sha256=xqGtMypY0abdqtPXsO3sTLPjRwemaIs5ALcjY990hCs,619 +botocore/data/signer-data/2017-08-25/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/signer-data/2017-08-25/service-2.json.gz,sha256=Q2XISuC_bzXbxdxe8KIR1L5ThOrDTQHTuFxQwZ7XSy4,1270 +botocore/data/signer-data/2017-08-25/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/signer/2017-08-25/endpoint-rule-set-1.json.gz,sha256=SuukX-9h3LYHmaxBLLxwB7e4rki5QEfCjVqeNVxcYcc,1146 +botocore/data/signer/2017-08-25/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/signer/2017-08-25/paginators-1.json,sha256=vjItW2pdi1KsZB_HwJEZqWIDJNHlrKbyxSuN6x8LHkU,526 +botocore/data/signer/2017-08-25/service-2.json.gz,sha256=rsa59RqFE_xU2MjJO6a8N40om0594bq7YPffX5vFYiI,10333 +botocore/data/signer/2017-08-25/waiters-2.json,sha256=ZvZgSYJd2QhWkeR1jaM1ECQ8295slZ6oDEFLtA2tYRE,607 +botocore/data/signin/2023-01-01/endpoint-rule-set-1.json.gz,sha256=JrA_X97AUDGs5nlgTt03WonDgG6Bm3WgJi9NO2WC4bQ,2429 +botocore/data/signin/2023-01-01/paginators-1.json,sha256=HiJhQS-GUoHgefdMHyl_sw040VMcNImQdeCv-ftBiFc,219 +botocore/data/signin/2023-01-01/service-2.json.gz,sha256=F8coJhsy9ZdlGB8IdvpnmEltmbdwCuiyW2RzV6-molA,8716 +botocore/data/simpledbv2/2025-09-26/endpoint-rule-set-1.json.gz,sha256=h6pnXagInX3ahEaOxQ0TIU52foVHSX4Ofp8AE5UFaBk,1364 +botocore/data/simpledbv2/2025-09-26/paginators-1.json,sha256=pIvItLgKwEESQUjTyoyt_ZOPnog_n-oQrmEob_fty3k,193 +botocore/data/simpledbv2/2025-09-26/service-2.json.gz,sha256=qdfkaBJaeeWL7T1DGC_rKlKhF80ZG4CLpQjIPJLQ-50,3480 +botocore/data/simpledbv2/2025-09-26/waiters-2.json,sha256=9KOY1e7IetG2z-8rUOAn5Ijy4Y1dj426X9-XQDsn2vI,446 +botocore/data/sms-voice/2018-09-05/endpoint-rule-set-1.json.gz,sha256=15B3uqRHEd3sMavZe1PxgpGR3g3P-Pp6F_2L8t1otJg,1307 +botocore/data/sms-voice/2018-09-05/service-2.json.gz,sha256=b3lpX79IWC_Ah8nz3HonODz1IU3ZbcTX1sxAOn0GqoU,3344 +botocore/data/snow-device-management/2021-08-04/endpoint-rule-set-1.json.gz,sha256=1xfFX7cdSWwJQsHztISXhpbCq5XSg1t7LNnH3IL_4M8,1162 +botocore/data/snow-device-management/2021-08-04/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/snow-device-management/2021-08-04/paginators-1.json,sha256=rNmRDBuxcetGirFRJQJA1vFXHeMY-sFLZ8BSld7BkFw,677 +botocore/data/snow-device-management/2021-08-04/service-2.json.gz,sha256=YYwTtvu04H84c-nsUp0RXI96j7HssPEukw5cByXEPLM,5955 +botocore/data/snowball/2016-06-30/endpoint-rule-set-1.json.gz,sha256=Q653tU_8122HYSXJOC-wgxwyHvjDxE70SbWtmSvhZeo,1150 +botocore/data/snowball/2016-06-30/examples-1.json,sha256=c2uRhH8SNSzMSPVVlezBwPPoxWxhOl2QxkdNc0A37q4,18099 +botocore/data/snowball/2016-06-30/paginators-1.json,sha256=vMdXg3dD9a7r3ifpM8lAmkBfXJBVz66l-6uUq_4OJjo,1061 +botocore/data/snowball/2016-06-30/service-2.json.gz,sha256=dH2yK6ruCE1P3kLbdlrBC7lS3L85Qd7Njc-x-kGHFQI,17037 +botocore/data/sns/2010-03-31/endpoint-rule-set-1.json.gz,sha256=Vq7AeEhwMSxyMY9zNIpXzv-zJxH-aCnyjI587irNX5M,1228 +botocore/data/sns/2010-03-31/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/sns/2010-03-31/paginators-1.json,sha256=a5cU7i3ZYF5D-u8S4oYs5kDUAAeav2kcWeG21u8RjPg,1241 +botocore/data/sns/2010-03-31/service-2.json.gz,sha256=rETP2i6It17PoMAXCXvE6aNWFBvrXJ8zgJtaQaFf5QE,27102 +botocore/data/socialmessaging/2024-01-01/endpoint-rule-set-1.json.gz,sha256=K23WJBmwF2_ojjlguTYP2rGnRSyZDv7-ufNH0nz6sIQ,1305 +botocore/data/socialmessaging/2024-01-01/paginators-1.json,sha256=E36wy3kUfQpsU4jTVhDhwjyNya-f5HYOb0boQY0_k94,921 +botocore/data/socialmessaging/2024-01-01/service-2.json.gz,sha256=7eCMIRHgtVgS_NHtH3S1Cz2IpZ6JacUAedwrp8QSgps,17770 +botocore/data/sqs/2012-11-05/endpoint-rule-set-1.json.gz,sha256=mGqH8Os17JcB404-e-X7eg5je0Z-Q-qWP1c9sMyMpso,1230 +botocore/data/sqs/2012-11-05/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/sqs/2012-11-05/paginators-1.json,sha256=fwyodl-UMt13laxQWAtCY9DEgncIy3mWPV-tS9M5m50,363 +botocore/data/sqs/2012-11-05/service-2.json.gz,sha256=meEIlFLqUtjErH92P213TJmyNrjIUcrxk9p5ILBR2N0,24462 +botocore/data/ssm-contacts/2021-05-03/endpoint-rule-set-1.json.gz,sha256=rH-RuAKS29IuDnW8RTbOisaQpajgZJwy3MeAmkjQJh0,1152 +botocore/data/ssm-contacts/2021-05-03/examples-1.json,sha256=DgD8jM1qr-3c2rDYBCXlsWUyaA_3S4VTwUogOr5KX0s,28860 +botocore/data/ssm-contacts/2021-05-03/paginators-1.json,sha256=Zvq8EuioTe0ZGvZrNX07bNJzAplhIUTDre4-HOhKrsc,1872 +botocore/data/ssm-contacts/2021-05-03/service-2.json.gz,sha256=We8mmTe62t9d9msZKp_e8ZhlNwcaIhPkqGOjQCkx970,12928 +botocore/data/ssm-guiconnect/2021-05-01/endpoint-rule-set-1.json.gz,sha256=5J9fJxkH4Ggy_6baZ4iVdxXcop_VnR2dGwEZzfLMbMo,1303 +botocore/data/ssm-guiconnect/2021-05-01/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/ssm-guiconnect/2021-05-01/service-2.json.gz,sha256=QrLAZqOsdx97w29_X0IPukqp346CipX62JvWskA0Xkg,2137 +botocore/data/ssm-incidents/2018-05-10/endpoint-rule-set-1.json.gz,sha256=e8Piud6q9y75f8DWdy2HzU5vYem6V_M30FUg9fOCzYk,1154 +botocore/data/ssm-incidents/2018-05-10/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/ssm-incidents/2018-05-10/paginators-1.json,sha256=4qlmECBX9jmRprL7ROo4h4MHrfWWjH2gGPLr9sjuV3o,1259 +botocore/data/ssm-incidents/2018-05-10/service-2.json.gz,sha256=OJFurd5bGeIOovoGDmN2ERNhWN3SkzuHNNJl9U-xkc0,14417 +botocore/data/ssm-incidents/2018-05-10/waiters-2.json,sha256=1xhj2BSaBj_CCZlCG7wTLL4ZB0e8_Uuq97DXjf7rADI,1465 +botocore/data/ssm-quicksetup/2018-05-10/endpoint-rule-set-1.json.gz,sha256=DRlMSleHI-7ZAVxyKohApde46T6TkpUxUZhbI15sKbQ,1304 +botocore/data/ssm-quicksetup/2018-05-10/paginators-1.json,sha256=ytpk-IGuYLt-RpYS7gbZI2X5ie6eJJCCLWzTWUDNOK4,399 +botocore/data/ssm-quicksetup/2018-05-10/service-2.json.gz,sha256=azQxAFLeF9c43ab4d7hMT--hao5CJAwPKSvzZqX0p5c,7343 +botocore/data/ssm-sap/2018-05-10/endpoint-rule-set-1.json.gz,sha256=g9sDb5NM_fr9I4bOY1x0PQNpCAJxMvJER_3SnBivfm4,1298 +botocore/data/ssm-sap/2018-05-10/paginators-1.json,sha256=i0Tvuamq_Ap6wKbKpSApgTZGJLyfNvFSUViSk9VAMfc,1615 +botocore/data/ssm-sap/2018-05-10/service-2.json.gz,sha256=keJJVV_mkjguAD6MVjPCj7EpUxcYQEJqi-6bwIUVEg4,9460 +botocore/data/ssm/2014-11-06/endpoint-rule-set-1.json.gz,sha256=ebOtKnlRXrfiX6MQ9_238VrzSocE5OpOk3VXebggWf4,1230 +botocore/data/ssm/2014-11-06/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/ssm/2014-11-06/paginators-1.json,sha256=P2fj63MB6SELvyKruwrW5L7jWmWPMmnO-PVugFRurps,9477 +botocore/data/ssm/2014-11-06/service-2.json.gz,sha256=IEia63sj17K9fuQ3UfmMlf4_vmfo5msfsI2RZRyUW8I,139638 +botocore/data/ssm/2014-11-06/waiters-2.json,sha256=eTUBQgvIuYcA9hhUZZ3mY4KqLap6FbcReyPUqdPYduc,1457 +botocore/data/sso-admin/2020-07-20/endpoint-rule-set-1.json.gz,sha256=yvyQ3iY3jwOgBQPDFA9If2s_qLZCTfPdL54bSeccdsQ,1230 +botocore/data/sso-admin/2020-07-20/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/sso-admin/2020-07-20/paginators-1.json,sha256=qZAezchNLenRfONwBw23xbxnrzegsABWy2sD0Kfy8lw,3874 +botocore/data/sso-admin/2020-07-20/service-2.json.gz,sha256=VBAIqDWLwxGiWnET-J20YQ9KxRHHJgZeI2vQV2NoeT0,25019 +botocore/data/sso-admin/2020-07-20/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/sso-oidc/2019-06-10/endpoint-rule-set-1.json.gz,sha256=2McbEqEaNT_r2bF3V-GQWw_jQq8Necx0I8CLYXBbFaI,1231 +botocore/data/sso-oidc/2019-06-10/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/sso-oidc/2019-06-10/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/sso-oidc/2019-06-10/service-2.json.gz,sha256=r-R9rLZbBsUT8N1yNRmdqr0H0kkWBK7Hf3cbX4SQ0hM,6085 +botocore/data/sso/2019-06-10/endpoint-rule-set-1.json.gz,sha256=A-v4zukgB1rXx7KAhB88GmYLKil4rE24JisHwKGH5K0,1233 +botocore/data/sso/2019-06-10/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/sso/2019-06-10/paginators-1.json,sha256=IScw_JafDnQ5pGRs-y61MtR0d4glhFcZR5D-8KLn2-Y,356 +botocore/data/sso/2019-06-10/service-2.json.gz,sha256=tOsEYCWKYofl-Ei0GxBK0yhICXP37JG6HqosSrQ_lTY,2954 +botocore/data/stepfunctions/2016-11-23/endpoint-rule-set-1.json.gz,sha256=Pde3QNT3Xr0D8GWbIW5EZh9PqU0ES_rPChlDSWuIjUU,1421 +botocore/data/stepfunctions/2016-11-23/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/stepfunctions/2016-11-23/paginators-1.json,sha256=2p0xo5UgVh-6RA8-svDfT8HBM_Cf2d05upRi3VIOnuY,856 +botocore/data/stepfunctions/2016-11-23/service-2.json.gz,sha256=EZMrVN6Df7NlUxpLbm239SO7e3w4TMEYmW0m9WO1fOg,32672 +botocore/data/storagegateway/2013-06-30/endpoint-rule-set-1.json.gz,sha256=GD49FIfyCpzKe0ei5TnjwzsKqUqjv59Lpqv_OhcefXc,1153 +botocore/data/storagegateway/2013-06-30/examples-1.json,sha256=2-mBPJqbSFv2f3t6KqdtrU5dW0Z49zylBvFGmoQEAk8,49947 +botocore/data/storagegateway/2013-06-30/paginators-1.json,sha256=eu2RmTSbh-kl0okK4tdVRDTS3tlqOAoyoeuPW4f_GJ4,2101 +botocore/data/storagegateway/2013-06-30/service-2.json.gz,sha256=dGP6jJgblpHY4KzpO6Ii_-nZH71lpB0FwMMvirdgyvY,57374 +botocore/data/sts/2011-06-15/endpoint-rule-set-1.json.gz,sha256=Mtap8SGThTUJ-plxhkbwxIUUaIFZVzkeGQA9XQbbxWY,1775 +botocore/data/sts/2011-06-15/examples-1.json,sha256=yD_CcHN2f9t9PlGQ5NzOJaCYccexGPoonbBW2T4OMck,11885 +botocore/data/sts/2011-06-15/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/sts/2011-06-15/service-2.json.gz,sha256=j_K9r8Qx5TPqdYGNQTqec6FpkewqcVd3BSmv_dfTNQ4,20858 +botocore/data/supplychain/2024-01-01/endpoint-rule-set-1.json.gz,sha256=C_uBkqehBfCVEL_0EdmV1Jo7YVty3m7bgLKqqn3YKcA,1295 +botocore/data/supplychain/2024-01-01/paginators-1.json,sha256=LBjGaMynSYN1nFn9bxNRdFiHV-bdpQsauaC28a-tWpI,1066 +botocore/data/supplychain/2024-01-01/service-2.json.gz,sha256=JEO1kEpEBJUZlDY_ZB9BwxeajN1e2YpJLzBv7k-waqE,14123 +botocore/data/support-app/2021-08-20/endpoint-rule-set-1.json.gz,sha256=K964jY9o5E2iej1WLT8Wu-oH9TQWyOxRyWNPcdahj1Q,1148 +botocore/data/support-app/2021-08-20/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/support-app/2021-08-20/service-2.json.gz,sha256=5FqiUu3IHyg8MD-A_4ny9IONiFPI9jR9gliiOPfjHjw,4271 +botocore/data/support/2013-04-15/endpoint-rule-set-1.json.gz,sha256=TUSR1o9uit1W7fESrmKWdDld1BbtkW624OSQ1kpKpd0,1554 +botocore/data/support/2013-04-15/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/support/2013-04-15/paginators-1.json,sha256=b74jGAMdiNu8tKXAfyVILd2DpHqQx91qieo1BlSXpK8,363 +botocore/data/support/2013-04-15/service-2.json.gz,sha256=IxOJP5Qe0L4X62tq2ovfXtCKl1VJ5Mnifhxho6lCZhU,17681 +botocore/data/supportauthz/2026-06-30/endpoint-rule-set-1.json.gz,sha256=P8qUAWHahl89TIxbw8bFnmNL8JmV5ua6bP2J4d4q6tw,834 +botocore/data/supportauthz/2026-06-30/paginators-1.json,sha256=x1qYbGmD7TUytJ67yNZfiEzxAjSZowM5YCdXtZDfrAg,555 +botocore/data/supportauthz/2026-06-30/service-2.json.gz,sha256=NJIDVD1OTfKV1YjIXwhlkEXKU1pzXQFk96Pr6g9s7wk,4796 +botocore/data/supportauthz/2026-06-30/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/sustainability/2018-05-10/endpoint-rule-set-1.json.gz,sha256=CnHiVMyQJmtTFls-n4pOoqCUm4hPnFPaUycq4kEiBmU,1170 +botocore/data/sustainability/2018-05-10/paginators-1.json,sha256=9sz5bPDC4PiP9kN0N_OnB07bDZAbOA-z7YVXxDOOZ9A,759 +botocore/data/sustainability/2018-05-10/service-2.json.gz,sha256=qQ39yZDyBu3WxjUU40KwMJT-TLYrkqrNyZ87r5kOVnQ,4035 +botocore/data/swf/2012-01-25/endpoint-rule-set-1.json.gz,sha256=7o0YCiXEFk7Lr_Uq4Ke9eiBvDJ8_5vCEeVyJfsTikIk,1406 +botocore/data/swf/2012-01-25/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/swf/2012-01-25/paginators-1.json,sha256=tOiP-8y-iuvOSJM35cQg6qCE0ai5dd5IWenCE1BH_yk,1496 +botocore/data/swf/2012-01-25/service-2.json.gz,sha256=YVqScr9TByhe5vQeTghvGQBKvTcvBIK5Zmn4gVFDCd0,34463 +botocore/data/synthetics/2017-10-11/endpoint-rule-set-1.json.gz,sha256=kaMaBfyFHyTBAR6LMOOGeDs0G85KGTGZBCG16gBYuzc,1150 +botocore/data/synthetics/2017-10-11/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/synthetics/2017-10-11/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/synthetics/2017-10-11/service-2.json.gz,sha256=YVlzL-j-u_Dvc7RZK91JT9Gs9HgRfCXPXs4pyxaXREw,19783 +botocore/data/taxsettings/2018-05-10/endpoint-rule-set-1.json.gz,sha256=P33jTeuqsJlLqyUCwqPMireU1ofJfNPmU63Y8wZQjJg,1377 +botocore/data/taxsettings/2018-05-10/paginators-1.json,sha256=13Jr861DGqLOCbfOeKUIwyYioTGmqiwvg_Cps7HFSx0,572 +botocore/data/taxsettings/2018-05-10/service-2.json.gz,sha256=wtQ9JTwngkDE4-1T4m7qg-R7h1X33aZFENIbPHoynM8,15354 +botocore/data/taxsettings/2018-05-10/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/textract/2018-06-27/endpoint-rule-set-1.json.gz,sha256=77TGGCUIlK-73iVKQ9YQivccuawptDliXITyvLbwifU,1149 +botocore/data/textract/2018-06-27/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/textract/2018-06-27/paginators-1.json,sha256=IQfBMdVD87vGqQnApoCTQrnbboZ3niS3DPFSlfrqh80,363 +botocore/data/textract/2018-06-27/service-2.json.gz,sha256=r-Zf7ZbZkdCi9eVm25iII_ssXFN60qtpbKp7m4gUh1c,22598 +botocore/data/timestream-influxdb/2023-01-27/endpoint-rule-set-1.json.gz,sha256=wKIjBph7_W5CSp5ZOTnKf1XY4sF7vLfAxokemcYV--U,1307 +botocore/data/timestream-influxdb/2023-01-27/paginators-1.json,sha256=cDLaSf0_Z8rPQ7KYD8cKQiTiiLetZFmrHGtTYjGEdNc,848 +botocore/data/timestream-influxdb/2023-01-27/service-2.json.gz,sha256=PLO_zxCitx4vym9w8Co1FgmmLftic3hDAY1hDSYRkvI,18617 +botocore/data/timestream-query/2018-11-01/endpoint-rule-set-1.json.gz,sha256=mDgvRH_I4wv6AD-V5ZNo5Yu67E1nPaJPoZI3Pyrxw5s,1375 +botocore/data/timestream-query/2018-11-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/timestream-query/2018-11-01/paginators-1.json,sha256=0Ppw_OmGYMcK7_ULTp_ACg7XEYwjNmlL1Uu5rXuTMRU,651 +botocore/data/timestream-query/2018-11-01/service-2.json.gz,sha256=EuaUINykVmLEspeckMbzmJoJCfH9SAxmUvoYW26Jl6U,14832 +botocore/data/timestream-write/2018-11-01/endpoint-rule-set-1.json.gz,sha256=C7ztBnfrGgQcp-8I_xNmGUJwvJFXbj9jqmaVgxD1OTU,1374 +botocore/data/timestream-write/2018-11-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/timestream-write/2018-11-01/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/timestream-write/2018-11-01/service-2.json.gz,sha256=W0hOETW7-87ZUSYS9Z05UbfU2hN-Udq5eANRsSYYZCE,12148 +botocore/data/tnb/2008-10-21/endpoint-rule-set-1.json.gz,sha256=wuxIqNsQyC8Gi3Nx3pExFGs0rTv1U5N6hPTmHHkhiuY,1295 +botocore/data/tnb/2008-10-21/paginators-1.json,sha256=oz2uxUX8r9w5q6IjSx3zIxuNl3_jtJnCGLbFF1j0okw,932 +botocore/data/tnb/2008-10-21/service-2.json.gz,sha256=9kssHHVi0wxjFbt4KWXJh51J9moNqoAG0BPtHsy5y6g,10024 +botocore/data/transcribe/2017-10-26/endpoint-rule-set-1.json.gz,sha256=pbP6YdROncvGql3hG2JaPa1bWZ-nMAbJcQsl-iAfdjA,1338 +botocore/data/transcribe/2017-10-26/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/transcribe/2017-10-26/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/transcribe/2017-10-26/service-2.json.gz,sha256=cS5XiwmuObw10dilG6hsSblLGc5VJhpmoGXr3cWV3NA,35994 +botocore/data/transcribe/2017-10-26/waiters-2.json,sha256=A7s_Vv8U1ZbFcz9_ftc9qvvJsEi8j6nwiwPVBMiDsoI,3500 +botocore/data/transfer/2018-11-05/endpoint-rule-set-1.json.gz,sha256=tEY-rMKIgRhc_FCOGMfKeOBO5SorIeLonKR-MRkb59s,1148 +botocore/data/transfer/2018-11-05/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/transfer/2018-11-05/paginators-1.json,sha256=q8S788HxDFyo8rjz_-33w3gJTVEEJ3VDafrzWIwkJSQ,2416 +botocore/data/transfer/2018-11-05/service-2.json.gz,sha256=oXeKZzmIJTKbBgvOFuuODstn3ECntu8pp9oPZc7lsvM,68158 +botocore/data/transfer/2018-11-05/waiters-2.json,sha256=hVdSZ-CDADnA9zRgSm0tK-qrrIGLUKXug5j6Ave1F-Q,868 +botocore/data/translate/2017-07-01/endpoint-rule-set-1.json.gz,sha256=gSBmRLgDBRi2D-c-frpECfvbBRi3fyGKs1MU78KKKx0,1149 +botocore/data/translate/2017-07-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/translate/2017-07-01/paginators-1.json,sha256=eE-1ycW-V5DQ_8t4NsRrfJYKhUnAaS7d5OyOimdaOaA,209 +botocore/data/translate/2017-07-01/service-2.json.gz,sha256=Tcc45xtFLgwy7dm-UIpvM45dXClwPUutgdPQKUvH5qU,13025 +botocore/data/trustedadvisor/2022-09-15/endpoint-rule-set-1.json.gz,sha256=aihwDxqFLTMyocsQk4p5QX1jqKIuN6XeSRLy6otWA2E,1303 +botocore/data/trustedadvisor/2022-09-15/paginators-1.json,sha256=pN703r_F5xv9_R3zMNwo0F5dGN0eZNYjF20L5k8kd6w,1432 +botocore/data/trustedadvisor/2022-09-15/service-2.json.gz,sha256=26Ibqx11FElqtVP9izeWmtgavuBLzw1TCPtjjLImK9c,6248 +botocore/data/trustedadvisor/2022-09-15/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/uxc/2024-07-01/endpoint-rule-set-1.json.gz,sha256=nDd7WPXRaolRpBgA-wvNbeMCZfp-BmjLioV2m7XXMLA,830 +botocore/data/uxc/2024-07-01/paginators-1.json,sha256=pOYgNv4X7HHDlu-4YPATH3M-sbOwNECqD_gWNzFKN3M,187 +botocore/data/uxc/2024-07-01/service-2.json.gz,sha256=7_2TBvQl2R32bLJqSpMj19FzUhDzObNFdE023ZlUlIw,2606 +botocore/data/uxc/2024-07-01/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/verifiedpermissions/2021-12-01/endpoint-rule-set-1.json.gz,sha256=DzCbxO3yWsd2IlMFSWn7G4BSmx-8ZtuFas1O6enl0xE,1306 +botocore/data/verifiedpermissions/2021-12-01/paginators-1.json,sha256=hZnq5ZS5MOwy43RAyNRq9IE9FlrTPHGx_6anogs9kDI,891 +botocore/data/verifiedpermissions/2021-12-01/service-2.json.gz,sha256=TGHEn_X_EDzSwAydHb0Gkdbmj29LfcPsbyOUo54AoB4,29581 +botocore/data/verifiedpermissions/2021-12-01/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/voice-id/2021-09-27/endpoint-rule-set-1.json.gz,sha256=o_swL5CVCNNKUmHGWyrEkzAxl5otzwADj5rveAeDtfk,1148 +botocore/data/voice-id/2021-09-27/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/voice-id/2021-09-27/paginators-1.json,sha256=mgfNZB61NZhxJAtDiZ1WOqHTvwaWmArbDCHTAkdf520,1073 +botocore/data/voice-id/2021-09-27/service-2.json.gz,sha256=9IF9ooAJ20B-hGZsrsuPPCyszy6lor1NuNZ2U4lJWgQ,11875 +botocore/data/vpc-lattice/2022-11-30/endpoint-rule-set-1.json.gz,sha256=fckkJEL4OZu9__qGqK28Zn6TSLHtTG-zJ3dJVROL704,1301 +botocore/data/vpc-lattice/2022-11-30/paginators-1.json,sha256=SvSN7CWErphlp2WSxvI_k2ML9sIm4oqTKNRBzZrWBKA,2586 +botocore/data/vpc-lattice/2022-11-30/service-2.json.gz,sha256=9liSTnG7ZR3MvFPADR5Ccn98M-_jInC49OXl4s4NTEI,28528 +botocore/data/vpc-lattice/2022-11-30/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/waf-regional/2016-11-28/endpoint-rule-set-1.json.gz,sha256=Jyj2XXp7jS22urWEsk1GGbFeRcCy68VrcYDZxtnt6m0,1149 +botocore/data/waf-regional/2016-11-28/examples-1.json,sha256=6OPuCnLynJIfGO-Vxhb9ZZV9ktEKhpByvf2jSwAg-DY,29749 +botocore/data/waf-regional/2016-11-28/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/waf-regional/2016-11-28/service-2.json.gz,sha256=_IXlaoPHzc560oXkIFJeGZRqhJqbLSK7q8K-AggvXw8,42885 +botocore/data/waf/2015-08-24/endpoint-rule-set-1.json.gz,sha256=2l7LwFczG_3UBS7q4PS5CfGuUbI_rqiaoVm3qRajeGQ,1340 +botocore/data/waf/2015-08-24/examples-1.json,sha256=6OPuCnLynJIfGO-Vxhb9ZZV9ktEKhpByvf2jSwAg-DY,29749 +botocore/data/waf/2015-08-24/paginators-1.json,sha256=ulE-ztimMiePJZAVUJkWb57N9b2OKV7xz_GIOHCw7PM,2717 +botocore/data/waf/2015-08-24/service-2.json.gz,sha256=mZNgqhNMfqnxzZ0mdjWue54zwkRMo6DJAscs-QPMo3M,41724 +botocore/data/wafv2/2019-07-29/endpoint-rule-set-1.json.gz,sha256=tMJxvKIrmRF2mu4-7hF2ihteMLZZHAPrxnbQ1MLngwU,1148 +botocore/data/wafv2/2019-07-29/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/wafv2/2019-07-29/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/wafv2/2019-07-29/service-2.json.gz,sha256=Q38Uf21EoOCJTRhZzuAC1co4CGSBJSrLVJKOl4lfZ7Q,89691 +botocore/data/wellarchitected/2020-03-31/endpoint-rule-set-1.json.gz,sha256=50psPFX-R0Y8gmOI2C6uQ77SAtfME2XbQcluDMgSRCI,1803 +botocore/data/wellarchitected/2020-03-31/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/wellarchitected/2020-03-31/paginators-1.json,sha256=k9gSfbaKl2l7r0ROtQRvfuU-7s30bWNfozGE7PtAAoc,1041 +botocore/data/wellarchitected/2020-03-31/service-2.json.gz,sha256=M50KMa24isdhSkeB8cbsZQgpNIN8L2IvD38U0lOPrIY,35290 +botocore/data/wickr/2024-02-01/endpoint-rule-set-1.json.gz,sha256=x518AREe5RVdlz9d43F8eyEgejVMdSkVweEHg9OhfsM,1302 +botocore/data/wickr/2024-02-01/paginators-1.json,sha256=z_vaZpEWOTnwkP_Wdm5bru-1LQERs8qd2ufKsXmlvAU,1344 +botocore/data/wickr/2024-02-01/service-2.json.gz,sha256=AcRXhJwt5YyOiT3DHexi_CTo6BU4S9z_2yNMuhxByKA,21712 +botocore/data/wickr/2024-02-01/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/wisdom/2020-10-19/endpoint-rule-set-1.json.gz,sha256=KpgBx9dFG1MevLSKlA0zKwhondGDX5RMAhDnTaVT_gY,1148 +botocore/data/wisdom/2020-10-19/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/wisdom/2020-10-19/paginators-1.json,sha256=Mrm9rH5_xIiJTb4YXQUznBuP7k3tAPM5lVtE0HpFnow,1778 +botocore/data/wisdom/2020-10-19/service-2.json.gz,sha256=imdNsOVBdkfS9D0SkZHgBAjlCDKhWGJsd2AFW1oONCI,17064 +botocore/data/workdocs/2016-05-01/endpoint-rule-set-1.json.gz,sha256=qihkyIIP7NoHYoxAdoLTxdv5QmKynXBotdJ-aG-brak,1150 +botocore/data/workdocs/2016-05-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/workdocs/2016-05-01/paginators-1.json,sha256=PERmz7nK6Ur9t877K2ivamloNl6knySKpwgvjbOcUe0,1666 +botocore/data/workdocs/2016-05-01/service-2.json.gz,sha256=ShNWaWzUxoqp9YtA-En6ClYL66VOJMcahx_nS6AiOBQ,16249 +botocore/data/workmail/2017-10-01/endpoint-rule-set-1.json.gz,sha256=u5p-7XkdNLpvrm7lkCYqcqO9XGzpB6orjsuVfYbWeD4,1150 +botocore/data/workmail/2017-10-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/workmail/2017-10-01/paginators-1.json,sha256=9nz-4DRPw6f_GmwUgKagpcRqgRjcQXmRkBBpC8Yk35E,1747 +botocore/data/workmail/2017-10-01/service-2.json.gz,sha256=IW4OKZij6NFE3aUPo_HtfFxqKPcEr2tjfVj0xRXnh9c,27947 +botocore/data/workmailmessageflow/2019-05-01/endpoint-rule-set-1.json.gz,sha256=VfANV-1pZrL7RCiygscSLDNUvWoFxxc5d8ylqELrYfI,1158 +botocore/data/workmailmessageflow/2019-05-01/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/workmailmessageflow/2019-05-01/paginators-1.json,sha256=hIQ7AbLBsY4fPSNLVMg0dS45m6cjZKFTjbp3ZLh4zj8,23 +botocore/data/workmailmessageflow/2019-05-01/service-2.json.gz,sha256=3Z8QpcJix74v_ob1_BQVkeT3uKzn4vHRQOqlHx8_3WM,2293 +botocore/data/workspaces-instances/2022-07-26/endpoint-rule-set-1.json.gz,sha256=kFO76cnplbg1N52CgjtXNbFEVYMkTGAYyZmagFsnc_M,841 +botocore/data/workspaces-instances/2022-07-26/paginators-1.json,sha256=sQfPhLdTn-F-MxfRXo7SLcJaHDvQ_P5RKI12w1QlaFo,539 +botocore/data/workspaces-instances/2022-07-26/service-2.json.gz,sha256=BWpTaoRn4br0MF2u7TySt6-MXyt9dM8yjI1c5nu0Upw,9797 +botocore/data/workspaces-instances/2022-07-26/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/workspaces-thin-client/2023-08-22/endpoint-rule-set-1.json.gz,sha256=DGts8AniDmv2GGI8Ui18L-LNV-KNa-q5bADLssixvJw,1297 +botocore/data/workspaces-thin-client/2023-08-22/paginators-1.json,sha256=eoHZHYlG1VP49fqQ29q3I58cojJxkZ8AQQg_xOyd10Y,525 +botocore/data/workspaces-thin-client/2023-08-22/service-2.json.gz,sha256=YCp8sJETZlDOtqZi6FT0drOnua9FVVuoMiKijQXbyVg,6460 +botocore/data/workspaces-web/2020-07-08/endpoint-rule-set-1.json.gz,sha256=H69AHcca8SkAp0uzZFVMOxt-L-JcIKd9JWD7V34i4iw,1154 +botocore/data/workspaces-web/2020-07-08/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/workspaces-web/2020-07-08/paginators-1.json,sha256=sN0zyznWr8VY3qKPlet-yVbvoL5SfLBtaJf0jYyes08,551 +botocore/data/workspaces-web/2020-07-08/service-2.json.gz,sha256=0o_vBoW2Qc2EVTk0D2Ht0NdbyY8cFBthRoJDzUJR578,21865 +botocore/data/workspaces-web/2020-07-08/waiters-2.json,sha256=fsA0_mwCl57UFPiqxJUWLb9AE7gd9kpBT4x0_6Q7dww,39 +botocore/data/workspaces/2015-04-08/endpoint-rule-set-1.json.gz,sha256=mvL0Z2TUcwZD8Rw002XA2S3jAweIL5b-rTOXvQdn6NA,1151 +botocore/data/workspaces/2015-04-08/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/workspaces/2015-04-08/paginators-1.json,sha256=RiG7XOsbS-HVzRL_pYhhradEGVQnhRgv41nSgAAzABI,1504 +botocore/data/workspaces/2015-04-08/service-2.json.gz,sha256=lx96_kA-R-DozHAbR_TRq1wJhAlJFOxD-Mhq95bCm-o,41430 +botocore/data/xray/2016-04-12/endpoint-rule-set-1.json.gz,sha256=RDpm0H-9JXzBKyvz3Mz_X0WsiItxXpfxAI4Do0bgfuM,1146 +botocore/data/xray/2016-04-12/examples-1.json,sha256=K3b6mgYkitvcecSlJT-iV_EQATmvOySs66iKJI5qx0g,44 +botocore/data/xray/2016-04-12/paginators-1.json,sha256=2BXVUlpR51GRav7g4-ML3Fr7U9pBDqXax4lZYeJnwZU,1785 +botocore/data/xray/2016-04-12/service-2.json.gz,sha256=HL-vXhVleDG3uJg_ncdsqPLujj3K9kKCpLo0Wdrg7-E,21603 +botocore/discovery.py,sha256=n1lxC_BzuKlwkj2fi-KtVTK-oku8RL9vWrdb7r4oaBc,11069 +botocore/docs/__init__.py,sha256=Mxx6eiy76-SxPpUsPMUPoHS-Wjy8Mj1gDfrowCu0S4U,2027 +botocore/docs/__pycache__/__init__.cpython-312.pyc,, +botocore/docs/__pycache__/client.cpython-312.pyc,, +botocore/docs/__pycache__/docstring.cpython-312.pyc,, +botocore/docs/__pycache__/example.cpython-312.pyc,, +botocore/docs/__pycache__/method.cpython-312.pyc,, +botocore/docs/__pycache__/paginator.cpython-312.pyc,, +botocore/docs/__pycache__/params.cpython-312.pyc,, +botocore/docs/__pycache__/service.cpython-312.pyc,, +botocore/docs/__pycache__/shape.cpython-312.pyc,, +botocore/docs/__pycache__/sharedexample.cpython-312.pyc,, +botocore/docs/__pycache__/translator.cpython-312.pyc,, +botocore/docs/__pycache__/utils.cpython-312.pyc,, +botocore/docs/__pycache__/waiter.cpython-312.pyc,, +botocore/docs/bcdoc/__init__.py,sha256=V2g87AefB2DOD9_3xIF5k9Nv5ttb4_gNJOVvSF0Mp3s,588 +botocore/docs/bcdoc/__pycache__/__init__.cpython-312.pyc,, +botocore/docs/bcdoc/__pycache__/docstringparser.cpython-312.pyc,, +botocore/docs/bcdoc/__pycache__/restdoc.cpython-312.pyc,, +botocore/docs/bcdoc/__pycache__/style.cpython-312.pyc,, +botocore/docs/bcdoc/docstringparser.py,sha256=a-TefXFJysEog9AdTgTwa8Pzik9sxcFlbhQ83jGwFR0,10197 +botocore/docs/bcdoc/restdoc.py,sha256=FLK19ZxPqz-kHJ7SF97f_dhqhwv_wggDiANZ1rHNwrQ,9771 +botocore/docs/bcdoc/style.py,sha256=bNfbjquLLfEt9F6HH_g7FwMeCU9ZZJLNXN-spELATvY,13120 +botocore/docs/client.py,sha256=qpjAo2fOWZ0QgeX9GWkNX9q80R0DFMdQnTIORpc3Hxk,17269 +botocore/docs/docstring.py,sha256=Jo9lA4ZFPq75cNCUfpz7zWiXlDB-Cn3bP62cZvBntfA,3648 +botocore/docs/example.py,sha256=ZOCQpJ8irhZ-0Urf7VJkd0SwDeJ1WLvyJc-TMBCZ4Ho,8945 +botocore/docs/method.py,sha256=_GiyG_2GV1uNOCq2e5MEEc300Wk3rACROLUAAxxay7s,12058 +botocore/docs/paginator.py,sha256=9NvyH4-Xw5HLhKjJAUGC04u7jmjKOjM2nVqyr30F0DQ,8958 +botocore/docs/params.py,sha256=k8GP7bxv1lWMnxKb84QKjEo182pOwDUt7q8VV7vcmB8,11727 +botocore/docs/service.py,sha256=oSPLoXn08cSsFDToEuFp4DDOl_COuSYQJ7q7OEf2lfg,4990 +botocore/docs/shape.py,sha256=EZze3L3AhPNnx_iHvRtn2Z-04TbMHTZ2_okdpAmwPOc,5198 +botocore/docs/sharedexample.py,sha256=WrAklim6mYWOgqcm9qmm5ajXpr2FqIGNuXnmlQrmNiU,9202 +botocore/docs/translator.py,sha256=v9ZTifRrwmfxBHCBaRPoZqufvpHI31pdVMny1wcVi-4,2331 +botocore/docs/utils.py,sha256=Pl51LG2ye7GNpGRzLvmp-hbCLiLgb_uBAUdZFggNLYY,10381 +botocore/docs/waiter.py,sha256=5FeM3u1s8a1EK9sjNXDTLt6-tVlSMnJaA3gVP6pNEX4,6531 +botocore/endpoint.py,sha256=DOkkoRjjrZiixxKTVBUI78Gz4PhPsbgCcjlIKbplYmI,17469 +botocore/endpoint_provider.py,sha256=Et_BIjV54YXBmaDRJfVDlCowHydF5-yWlh488slXXRw,23547 +botocore/errorfactory.py,sha256=hdrxsOw0ihpT540ukWPbWqc7D-Dxe_la5H-ZcXgdLJ4,3722 +botocore/eventstream.py,sha256=3giwq42Lq1bg1j28-W2EfqQgbaS4kZKIyhn59pkR9s0,20279 +botocore/exceptions.py,sha256=QySwXqxIAg3tW3fZSdV7Hh3m3Uf1f-J1eIKARpEIi8w,24299 +botocore/handlers.py,sha256=UE-f0xpWoZfdY-5GeW-G3kccAuVQsoE6X7z6zzvNaBk,68835 +botocore/history.py,sha256=QR1WnpJYTo02Rz3GqWt45sF6wzu6EQrM_kS3FPH58t4,1744 +botocore/hooks.py,sha256=pITClZg88eiQX3PsQ1_32wydi52LJDIanF0Yvf2B4xI,25053 +botocore/httpchecksum.py,sha256=pc7osU0bq02-OY8n9FF74yuzUCimNtWAKYkKvx3WAK8,22490 +botocore/httpsession.py,sha256=FLKWExTHEUmdp4XsMPANmwTq1VpEA7KrpcXoIv_ZO3s,20785 +botocore/loaders.py,sha256=qivgjD3ThvUmgbJ-Ml0oZkzZxrnNvWz47_fS7aZq6kI,18826 +botocore/model.py,sha256=H1Asfd8coym2leMNNu_3c_0sSDQ07x8dm8J1gtHDi4o,32334 +botocore/monitoring.py,sha256=RDflkGbBrwnsH4pSENOkWikk1UuqkJVqHSYpHQtvK5M,20594 +botocore/paginate.py,sha256=SYqY0TZ4OJIkp5ZvqMUKkn8nJHW8VS7lS_4hm1npGOI,27909 +botocore/parsers.py,sha256=I2fcS58H5JUYXGXqbp03AUEzlo3hn4YIIvSLGn4fJuo,60629 +botocore/plugin.py,sha256=KXPHXEc1Ze272-Wzo5k-FEbNs-a2H_rNClPaKz2F2FE,2503 +botocore/regions.py,sha256=Ymf_EIS-CZgKBLmirZOieFg7SdmCvrrJxOtFa4v-gYM,35041 +botocore/response.py,sha256=4u5vgew19drph59fXGANyiK3iixDzvsNrDuLiUzp0R0,7865 +botocore/retries/__init__.py,sha256=YaZ6AwMRyuDBs5fOvl-PAvxQxZE2RBlcad2JmLOMo8k,121 +botocore/retries/__pycache__/__init__.cpython-312.pyc,, +botocore/retries/__pycache__/adaptive.cpython-312.pyc,, +botocore/retries/__pycache__/base.cpython-312.pyc,, +botocore/retries/__pycache__/bucket.cpython-312.pyc,, +botocore/retries/__pycache__/quota.cpython-312.pyc,, +botocore/retries/__pycache__/special.cpython-312.pyc,, +botocore/retries/__pycache__/standard.cpython-312.pyc,, +botocore/retries/__pycache__/throttling.cpython-312.pyc,, +botocore/retries/adaptive.py,sha256=0Y0QjSgK0sGS1nbWZV7wiBZgR82a-nA-vL5HjQadLOs,4207 +botocore/retries/base.py,sha256=rGJYVZEXLGSQ2BnaIT-W9ccGtSbIMvU-wzmV78d-Ccg,797 +botocore/retries/bucket.py,sha256=sZ5TGGMCyIh3qcRI5p5csNXPvtXZ2nC-sH6u5wJiIac,3994 +botocore/retries/quota.py,sha256=bijUNfy2fejuma0OB43sIn57OdRDhLSdFV_xCmcuTv4,1937 +botocore/retries/special.py,sha256=hePuqKytb0BmjKmfXbMf4fGrwiArbqu_HBepROpmi7U,1664 +botocore/retries/standard.py,sha256=ul7eBRx0x4nOWbA-nf8yxNolIZADL_f1EkebjOHMWYs,27817 +botocore/retries/throttling.py,sha256=x8pU_jMyapr0YODg8mtyYoXa8MzDAf0e-bWg9EfkFos,1779 +botocore/retryhandler.py,sha256=0cmoBCE3aIo60-co7idqO9FA2pIcyoI3NcDD_EHv0Yw,14702 +botocore/serialize.py,sha256=_jonbqgft101ucV5xExOql1oT2KdD3_tBX7Kt_Kh8_8,52015 +botocore/session.py,sha256=Hak58qxe5wV3XF5WARd9lxdPXOB1XtaVAVEYy2TGHWw,51661 +botocore/signers.py,sha256=M0Df1BohfuqKhBjqvqsaAMaKIQV7dcOwlMD8M9Lh8X4,34984 +botocore/stub.py,sha256=lsTeEZbwA4tZV1IRE3p-7hsmSHY7KW0dYF5sM6GuUfQ,16193 +botocore/tokens.py,sha256=2DLXjcQue0gtcNYQ56GKl2kRZ2mGzxV_0mREVIyjg1M,14092 +botocore/translate.py,sha256=UfKIIWr_BAcwvMScHuqrLtSD5yuXecl7Rs0Et3jfREc,3406 +botocore/useragent.py,sha256=lcMxuRjFQZG_ZxQFoOelRF8dmeEIfgWqh0sO1dMeOuQ,24585 +botocore/utils.py,sha256=6dkYLqz2iBSZKEo1f6F1l5bX9XvDKTC2kakq2ZFfJTA,138508 +botocore/validate.py,sha256=jy2cnawJbL1XM2aBiJyup1umIj7cQjHNrolomvsP3zo,15383 +botocore/vendored/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +botocore/vendored/__pycache__/__init__.cpython-312.pyc,, +botocore/vendored/__pycache__/six.cpython-312.pyc,, +botocore/vendored/requests/__init__.py,sha256=Pu8JNWAMzj9l8E0Qs4rU7clTOfxVIA6OuUKJkJDmvvc,227 +botocore/vendored/requests/__pycache__/__init__.cpython-312.pyc,, +botocore/vendored/requests/__pycache__/exceptions.cpython-312.pyc,, +botocore/vendored/requests/exceptions.py,sha256=zZhHieXgR1teqbvuo_9OrwDMHnrvRtulW97VfzumQv4,2517 +botocore/vendored/requests/packages/__init__.py,sha256=aXkbNCjM_WhryRBocE4AaA_p7-CTxL5LOutY7XzKm4s,62 +botocore/vendored/requests/packages/__pycache__/__init__.cpython-312.pyc,, +botocore/vendored/requests/packages/urllib3/__init__.py,sha256=Nrq2HJOk0McF4saJ5zySsjVKGPV6j05iAFTJwkKEzOI,184 +botocore/vendored/requests/packages/urllib3/__pycache__/__init__.cpython-312.pyc,, +botocore/vendored/requests/packages/urllib3/__pycache__/exceptions.cpython-312.pyc,, +botocore/vendored/requests/packages/urllib3/exceptions.py,sha256=za-cEwBqxBKOqqKTaIVAMdH3j1nDRqi-MtdojdpU4Wc,4374 +botocore/vendored/six.py,sha256=TOOfQi7nFGfMrIvtdr6wX4wyHH8M7aknmuLfo2cBBrM,34549 +botocore/waiter.py,sha256=n4_6HcqEc-cXDMPsECdwKfhNSowmfzw490-c1BIZYn0,14711 diff --git a/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/WHEEL b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/WHEEL new file mode 100644 index 0000000..dcfdc6e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.1.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/top_level.txt new file mode 100644 index 0000000..c5b9e12 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore-1.43.104.dist-info/top_level.txt @@ -0,0 +1 @@ +botocore diff --git a/venv/lib/python3.12/site-packages/botocore/__init__.py b/venv/lib/python3.12/site-packages/botocore/__init__.py new file mode 100644 index 0000000..3ac3873 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/__init__.py @@ -0,0 +1,277 @@ +# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import logging +import os +import re +from logging import NullHandler + +__version__ = '1.43.104' + + +# Configure default logger to do nothing +log = logging.getLogger('botocore') +log.addHandler(NullHandler()) + +_INITIALIZERS = [] + +_first_cap_regex = re.compile('(.)([A-Z][a-z]+)') +_end_cap_regex = re.compile('([a-z0-9])([A-Z])') +# The regex below handles the special case where some acronym +# name is pluralized, e.g GatewayARNs, ListWebACLs, SomeCNAMEs. +_special_case_transform = re.compile('[A-Z]{2,}s$') +# Prepopulate the cache with special cases that don't match +# our regular transformation. +_xform_cache = { + ('CreateCachediSCSIVolume', '_'): 'create_cached_iscsi_volume', + ('CreateCachediSCSIVolume', '-'): 'create-cached-iscsi-volume', + ('DescribeCachediSCSIVolumes', '_'): 'describe_cached_iscsi_volumes', + ('DescribeCachediSCSIVolumes', '-'): 'describe-cached-iscsi-volumes', + ('DescribeStorediSCSIVolumes', '_'): 'describe_stored_iscsi_volumes', + ('DescribeStorediSCSIVolumes', '-'): 'describe-stored-iscsi-volumes', + ('CreateStorediSCSIVolume', '_'): 'create_stored_iscsi_volume', + ('CreateStorediSCSIVolume', '-'): 'create-stored-iscsi-volume', + ('ListHITsForQualificationType', '_'): 'list_hits_for_qualification_type', + ('ListHITsForQualificationType', '-'): 'list-hits-for-qualification-type', + ('ExecutePartiQLStatement', '_'): 'execute_partiql_statement', + ('ExecutePartiQLStatement', '-'): 'execute-partiql-statement', + ('ExecutePartiQLTransaction', '_'): 'execute_partiql_transaction', + ('ExecutePartiQLTransaction', '-'): 'execute-partiql-transaction', + ('ExecutePartiQLBatch', '_'): 'execute_partiql_batch', + ('ExecutePartiQLBatch', '-'): 'execute-partiql-batch', + ('CreateOAuth2Token', '_'): 'create_oauth2_token', + ('CreateOAuth2Token', '-'): 'create-oauth2-token', + ('CreateOAuth2TokenWithIAM', '_'): 'create_oauth2_token_with_iam', + ('CreateOAuth2TokenWithIAM', '-'): 'create-oauth2-token-with-iam', + ( + 'IntrospectOAuth2TokenWithIAM', + '_', + ): 'introspect_oauth2_token_with_iam', + ( + 'IntrospectOAuth2TokenWithIAM', + '-', + ): 'introspect-oauth2-token-with-iam', + ('RevokeOAuth2TokenWithIAM', '_'): 'revoke_oauth2_token_with_iam', + ('RevokeOAuth2TokenWithIAM', '-'): 'revoke-oauth2-token-with-iam', + ( + 'AssociateWhatsAppBusinessAccount', + '_', + ): 'associate_whatsapp_business_account', + ( + 'AssociateWhatsAppBusinessAccount', + '-', + ): 'associate-whatsapp-business-account', + ('CreateWhatsAppDataset', '_'): 'create_whatsapp_dataset', + ('CreateWhatsAppDataset', '-'): 'create-whatsapp-dataset', + ('CreateWhatsAppFlow', '_'): 'create_whatsapp_flow', + ('CreateWhatsAppFlow', '-'): 'create-whatsapp-flow', + ('CreateWhatsAppMessageTemplate', '_'): 'create_whatsapp_message_template', + ('CreateWhatsAppMessageTemplate', '-'): 'create-whatsapp-message-template', + ( + 'CreateWhatsAppMessageTemplateFromLibrary', + '_', + ): 'create_whatsapp_message_template_from_library', + ( + 'CreateWhatsAppMessageTemplateFromLibrary', + '-', + ): 'create-whatsapp-message-template-from-library', + ( + 'CreateWhatsAppMessageTemplateMedia', + '_', + ): 'create_whatsapp_message_template_media', + ( + 'CreateWhatsAppMessageTemplateMedia', + '-', + ): 'create-whatsapp-message-template-media', + ('DeleteWhatsAppFlow', '_'): 'delete_whatsapp_flow', + ('DeleteWhatsAppFlow', '-'): 'delete-whatsapp-flow', + ('DeleteWhatsAppMessageMedia', '_'): 'delete_whatsapp_message_media', + ('DeleteWhatsAppMessageMedia', '-'): 'delete-whatsapp-message-media', + ('DeleteWhatsAppMessageTemplate', '_'): 'delete_whatsapp_message_template', + ('DeleteWhatsAppMessageTemplate', '-'): 'delete-whatsapp-message-template', + ('DeprecateWhatsAppFlow', '_'): 'deprecate_whatsapp_flow', + ('DeprecateWhatsAppFlow', '-'): 'deprecate-whatsapp-flow', + ( + 'DisassociateWhatsAppBusinessAccount', + '_', + ): 'disassociate_whatsapp_business_account', + ( + 'DisassociateWhatsAppBusinessAccount', + '-', + ): 'disassociate-whatsapp-business-account', + ( + 'GetLinkedWhatsAppBusinessAccount', + '_', + ): 'get_linked_whatsapp_business_account', + ( + 'GetLinkedWhatsAppBusinessAccount', + '-', + ): 'get-linked-whatsapp-business-account', + ( + 'GetLinkedWhatsAppBusinessAccountPhoneNumber', + '_', + ): 'get_linked_whatsapp_business_account_phone_number', + ( + 'GetLinkedWhatsAppBusinessAccountPhoneNumber', + '-', + ): 'get-linked-whatsapp-business-account-phone-number', + ('GetOTelEnrichment', '_'): 'get_otel_enrichment', + ('GetOTelEnrichment', '-'): 'get-otel-enrichment', + ('GetWhatsAppBusinessPublicKey', '_'): 'get_whatsapp_business_public_key', + ('GetWhatsAppBusinessPublicKey', '-'): 'get-whatsapp-business-public-key', + ('GetWhatsAppCallPermission', '_'): 'get_whatsapp_call_permission', + ('GetWhatsAppCallPermission', '-'): 'get-whatsapp-call-permission', + ('GetWhatsAppFlow', '_'): 'get_whatsapp_flow', + ('GetWhatsAppFlow', '-'): 'get-whatsapp-flow', + ('GetWhatsAppFlowPreview', '_'): 'get_whatsapp_flow_preview', + ('GetWhatsAppFlowPreview', '-'): 'get-whatsapp-flow-preview', + ('GetWhatsAppMessageMedia', '_'): 'get_whatsapp_message_media', + ('GetWhatsAppMessageMedia', '-'): 'get-whatsapp-message-media', + ('GetWhatsAppMessageTemplate', '_'): 'get_whatsapp_message_template', + ('GetWhatsAppMessageTemplate', '-'): 'get-whatsapp-message-template', + ( + 'ListLinkedWhatsAppBusinessAccounts', + '_', + ): 'list_linked_whatsapp_business_accounts', + ( + 'ListLinkedWhatsAppBusinessAccounts', + '-', + ): 'list-linked-whatsapp-business-accounts', + ('ListWhatsAppFlowAssets', '_'): 'list_whatsapp_flow_assets', + ('ListWhatsAppFlowAssets', '-'): 'list-whatsapp-flow-assets', + ('ListWhatsAppFlows', '_'): 'list_whatsapp_flows', + ('ListWhatsAppFlows', '-'): 'list-whatsapp-flows', + ('ListWhatsAppMessageTemplates', '_'): 'list_whatsapp_message_templates', + ('ListWhatsAppMessageTemplates', '-'): 'list-whatsapp-message-templates', + ('ListWhatsAppTemplateLibrary', '_'): 'list_whatsapp_template_library', + ('ListWhatsAppTemplateLibrary', '-'): 'list-whatsapp-template-library', + ('PostWhatsAppMessageMedia', '_'): 'post_whatsapp_message_media', + ('PostWhatsAppMessageMedia', '-'): 'post-whatsapp-message-media', + ('PublishWhatsAppFlow', '_'): 'publish_whatsapp_flow', + ('PublishWhatsAppFlow', '-'): 'publish-whatsapp-flow', + ('PutWhatsAppBusinessPublicKey', '_'): 'put_whatsapp_business_public_key', + ('PutWhatsAppBusinessPublicKey', '-'): 'put-whatsapp-business-public-key', + ( + 'PutWhatsAppBusinessAccountEventDestinations', + '_', + ): 'put_whatsapp_business_account_event_destinations', + ( + 'PutWhatsAppBusinessAccountEventDestinations', + '-', + ): 'put-whatsapp-business-account-event-destinations', + ('SendWhatsAppConversionEvent', '_'): 'send_whatsapp_conversion_event', + ('SendWhatsAppConversionEvent', '-'): 'send-whatsapp-conversion-event', + ('SendWhatsAppMessage', '_'): 'send_whatsapp_message', + ('SendWhatsAppMessage', '-'): 'send-whatsapp-message', + ('SendWhatsAppCallEvent', '_'): 'send_whatsapp_call_event', + ('SendWhatsAppCallEvent', '-'): 'send-whatsapp-call-event', + ('StartOTelEnrichment', '_'): 'start_otel_enrichment', + ('StartOTelEnrichment', '-'): 'start-otel-enrichment', + ('StopOTelEnrichment', '_'): 'stop_otel_enrichment', + ('StopOTelEnrichment', '-'): 'stop-otel-enrichment', + ('UpdateOTelEnrichment', '_'): 'update_otel_enrichment', + ('UpdateOTelEnrichment', '-'): 'update-otel-enrichment', + ('UpdateWhatsAppFlow', '_'): 'update_whatsapp_flow', + ('UpdateWhatsAppFlow', '-'): 'update-whatsapp-flow', + ('UpdateWhatsAppFlowAssets', '_'): 'update_whatsapp_flow_assets', + ('UpdateWhatsAppFlowAssets', '-'): 'update-whatsapp-flow-assets', + ( + 'UpdateLinkedWhatsAppBusinessAccountPhoneNumber', + '_', + ): 'update_linked_whatsapp_business_account_phone_number', + ( + 'UpdateLinkedWhatsAppBusinessAccountPhoneNumber', + '-', + ): 'update-linked-whatsapp-business-account-phone-number', + ('UpdateWhatsAppMessageTemplate', '_'): 'update_whatsapp_message_template', + ('UpdateWhatsAppMessageTemplate', '-'): 'update-whatsapp-message-template', +} +ScalarTypes = ('string', 'integer', 'boolean', 'timestamp', 'float', 'double') + +BOTOCORE_ROOT = os.path.dirname(os.path.abspath(__file__)) + + +# Used to specify anonymous (unsigned) request signature +class UNSIGNED: + def __copy__(self): + return self + + def __deepcopy__(self, memodict): + return self + + +UNSIGNED = UNSIGNED() + + +def xform_name(name, sep='_', _xform_cache=_xform_cache): + """Convert camel case to a "pythonic" name. + + If the name contains the ``sep`` character, then it is + returned unchanged. + + """ + if sep in name: + # If the sep is in the name, assume that it's already + # transformed and return the string unchanged. + return name + key = (name, sep) + if key not in _xform_cache: + if _special_case_transform.search(name) is not None: + is_special = _special_case_transform.search(name) + matched = is_special.group() + # Replace something like ARNs, ACLs with _arns, _acls. + name = f"{name[: -len(matched)]}{sep}{matched.lower()}" + s1 = _first_cap_regex.sub(r'\1' + sep + r'\2', name) + transformed = _end_cap_regex.sub(r'\1' + sep + r'\2', s1).lower() + _xform_cache[key] = transformed + return _xform_cache[key] + + +def register_initializer(callback): + """Register an initializer function for session creation. + + This initializer function will be invoked whenever a new + `botocore.session.Session` is instantiated. + + :type callback: callable + :param callback: A callable that accepts a single argument + of type `botocore.session.Session`. + + """ + _INITIALIZERS.append(callback) + + +def unregister_initializer(callback): + """Unregister an initializer function. + + :type callback: callable + :param callback: A callable that was previously registered + with `botocore.register_initializer`. + + :raises ValueError: If a callback is provided that is not currently + registered as an initializer. + + """ + _INITIALIZERS.remove(callback) + + +def invoke_initializers(session): + """Invoke all initializers for a session. + + :type session: botocore.session.Session + :param session: The session to initialize. + + """ + for initializer in _INITIALIZERS: + initializer(session) diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..51d7fa1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/args.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/args.cpython-312.pyc new file mode 100644 index 0000000..f93bc1a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/args.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/auth.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/auth.cpython-312.pyc new file mode 100644 index 0000000..dcb1840 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/auth.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/awsrequest.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/awsrequest.cpython-312.pyc new file mode 100644 index 0000000..d2337b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/awsrequest.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/client.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/client.cpython-312.pyc new file mode 100644 index 0000000..79dc04b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/client.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/compat.cpython-312.pyc new file mode 100644 index 0000000..32557bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/compat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/compress.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/compress.cpython-312.pyc new file mode 100644 index 0000000..a1fbbf6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/compress.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/config.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..7e42644 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/configloader.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/configloader.cpython-312.pyc new file mode 100644 index 0000000..4015dba Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/configloader.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/configprovider.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/configprovider.cpython-312.pyc new file mode 100644 index 0000000..696069f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/configprovider.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/context.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/context.cpython-312.pyc new file mode 100644 index 0000000..85181c6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/context.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/credentials.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/credentials.cpython-312.pyc new file mode 100644 index 0000000..6b269ef Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/credentials.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/discovery.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/discovery.cpython-312.pyc new file mode 100644 index 0000000..2d8431f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/discovery.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/endpoint.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/endpoint.cpython-312.pyc new file mode 100644 index 0000000..b1c0949 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/endpoint.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/endpoint_provider.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/endpoint_provider.cpython-312.pyc new file mode 100644 index 0000000..242d082 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/endpoint_provider.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/errorfactory.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/errorfactory.cpython-312.pyc new file mode 100644 index 0000000..23882ff Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/errorfactory.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/eventstream.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/eventstream.cpython-312.pyc new file mode 100644 index 0000000..b6c953f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/eventstream.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..6aa8405 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/handlers.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/handlers.cpython-312.pyc new file mode 100644 index 0000000..b4a1c25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/handlers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/history.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/history.cpython-312.pyc new file mode 100644 index 0000000..9825de7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/history.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/hooks.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/hooks.cpython-312.pyc new file mode 100644 index 0000000..014206b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/hooks.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/httpchecksum.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/httpchecksum.cpython-312.pyc new file mode 100644 index 0000000..b783136 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/httpchecksum.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/httpsession.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/httpsession.cpython-312.pyc new file mode 100644 index 0000000..51c7d97 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/httpsession.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/loaders.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/loaders.cpython-312.pyc new file mode 100644 index 0000000..a41f5ee Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/loaders.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/model.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/model.cpython-312.pyc new file mode 100644 index 0000000..11ae3e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/monitoring.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/monitoring.cpython-312.pyc new file mode 100644 index 0000000..ca06704 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/monitoring.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/paginate.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/paginate.cpython-312.pyc new file mode 100644 index 0000000..37ef291 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/paginate.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/parsers.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/parsers.cpython-312.pyc new file mode 100644 index 0000000..a60980a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/parsers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/plugin.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/plugin.cpython-312.pyc new file mode 100644 index 0000000..17acf19 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/plugin.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/regions.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/regions.cpython-312.pyc new file mode 100644 index 0000000..67e0f06 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/regions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/response.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/response.cpython-312.pyc new file mode 100644 index 0000000..950f87f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/retryhandler.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/retryhandler.cpython-312.pyc new file mode 100644 index 0000000..6f7c09f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/retryhandler.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/serialize.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/serialize.cpython-312.pyc new file mode 100644 index 0000000..c1e4f37 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/serialize.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/session.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/session.cpython-312.pyc new file mode 100644 index 0000000..1191d47 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/session.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/signers.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/signers.cpython-312.pyc new file mode 100644 index 0000000..bbb8aa3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/signers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/stub.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/stub.cpython-312.pyc new file mode 100644 index 0000000..6cbb4b9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/stub.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/tokens.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/tokens.cpython-312.pyc new file mode 100644 index 0000000..a2ad787 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/tokens.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/translate.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/translate.cpython-312.pyc new file mode 100644 index 0000000..4b029df Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/translate.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/useragent.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/useragent.cpython-312.pyc new file mode 100644 index 0000000..3c69253 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/useragent.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..ff3225c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/validate.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/validate.cpython-312.pyc new file mode 100644 index 0000000..beb9769 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/validate.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/__pycache__/waiter.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/__pycache__/waiter.cpython-312.pyc new file mode 100644 index 0000000..3a7d94a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/__pycache__/waiter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/args.py b/venv/lib/python3.12/site-packages/botocore/args.py new file mode 100644 index 0000000..d4ee57c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/args.py @@ -0,0 +1,1051 @@ +# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Internal module to help with normalizing botocore client args. + +This module (and all function/classes within this module) should be +considered internal, and *not* a public API. + +""" + +import copy +import logging +import socket + +import botocore.exceptions +import botocore.parsers +import botocore.serialize +from botocore.config import Config +from botocore.endpoint import EndpointCreator +from botocore.regions import EndpointResolverBuiltins as EPRBuiltins +from botocore.regions import EndpointRulesetResolver +from botocore.signers import RequestSigner +from botocore.useragent import UserAgentString, register_feature_id +from botocore.utils import ( + PRIORITY_ORDERED_SUPPORTED_PROTOCOLS, # noqa: F401 + ensure_boolean, + is_s3_accelerate_url, +) + +logger = logging.getLogger(__name__) + + +VALID_REGIONAL_ENDPOINTS_CONFIG = [ + 'legacy', + 'regional', +] +LEGACY_GLOBAL_STS_REGIONS = [ + 'ap-northeast-1', + 'ap-south-1', + 'ap-southeast-1', + 'ap-southeast-2', + 'aws-global', + 'ca-central-1', + 'eu-central-1', + 'eu-north-1', + 'eu-west-1', + 'eu-west-2', + 'eu-west-3', + 'sa-east-1', + 'us-east-1', + 'us-east-2', + 'us-west-1', + 'us-west-2', +] +# Maximum allowed length of the ``user_agent_appid`` config field. Longer +# values result in a warning-level log message. +USERAGENT_APPID_MAXLEN = 50 + +VALID_REQUEST_CHECKSUM_CALCULATION_CONFIG = ( + "when_supported", + "when_required", +) +VALID_RESPONSE_CHECKSUM_VALIDATION_CONFIG = ( + "when_supported", + "when_required", +) + + +VALID_ACCOUNT_ID_ENDPOINT_MODE_CONFIG = ( + 'preferred', + 'disabled', + 'required', +) + + +class ClientArgsCreator: + def __init__( + self, + event_emitter, + user_agent, + response_parser_factory, + loader, + exceptions_factory, + config_store, + user_agent_creator=None, + ): + self._event_emitter = event_emitter + self._response_parser_factory = response_parser_factory + self._loader = loader + self._exceptions_factory = exceptions_factory + self._config_store = config_store + if user_agent_creator is None: + self._session_ua_creator = UserAgentString.from_environment() + else: + self._session_ua_creator = user_agent_creator + + def get_client_args( + self, + service_model, + region_name, + is_secure, + endpoint_url, + verify, + credentials, + scoped_config, + client_config, + endpoint_bridge, + auth_token=None, + endpoints_ruleset_data=None, + partition_data=None, + ): + final_args = self.compute_client_args( + service_model, + client_config, + endpoint_bridge, + region_name, + endpoint_url, + is_secure, + scoped_config, + ) + + service_name = final_args['service_name'] # noqa + parameter_validation = final_args['parameter_validation'] + endpoint_config = final_args['endpoint_config'] + protocol = final_args['protocol'] + config_kwargs = final_args['config_kwargs'] + s3_config = final_args['s3_config'] + partition = endpoint_config['metadata'].get('partition', None) + socket_options = final_args['socket_options'] + configured_endpoint_url = final_args['configured_endpoint_url'] + signing_region = endpoint_config['signing_region'] + endpoint_region_name = endpoint_config['region_name'] + account_id_endpoint_mode = config_kwargs['account_id_endpoint_mode'] + s3_disable_express_session_auth = config_kwargs[ + 's3_disable_express_session_auth' + ] + auth_scheme_preference = config_kwargs['auth_scheme_preference'] + + event_emitter = copy.copy(self._event_emitter) + signer = RequestSigner( + service_model.service_id, + signing_region, + endpoint_config['signing_name'], + endpoint_config['signature_version'], + credentials, + event_emitter, + auth_token, + ) + + config_kwargs['s3'] = s3_config + new_config = Config(**config_kwargs) + endpoint_creator = EndpointCreator(event_emitter) + + endpoint = endpoint_creator.create_endpoint( + service_model, + region_name=endpoint_region_name, + endpoint_url=endpoint_config['endpoint_url'], + verify=verify, + response_parser_factory=self._response_parser_factory, + max_pool_connections=new_config.max_pool_connections, + proxies=new_config.proxies, + timeout=(new_config.connect_timeout, new_config.read_timeout), + socket_options=socket_options, + client_cert=new_config.client_cert, + proxies_config=new_config.proxies_config, + ) + + # Emit event to allow service-specific or customer customization of serializer kwargs + event_name = f'creating-serializer.{service_name}' + serializer_kwargs = { + 'timestamp_precision': botocore.serialize.TIMESTAMP_PRECISION_DEFAULT + } + event_emitter.emit( + event_name, + protocol_name=protocol, + service_model=service_model, + serializer_kwargs=serializer_kwargs, + ) + + serializer = botocore.serialize.create_serializer( + protocol, + parameter_validation, + timestamp_precision=serializer_kwargs['timestamp_precision'], + ) + response_parser = botocore.parsers.create_parser(protocol) + + ruleset_resolver = self._build_endpoint_resolver( + endpoints_ruleset_data, + partition_data, + client_config, + service_model, + endpoint_region_name, + region_name, + configured_endpoint_url, + endpoint, + is_secure, + endpoint_bridge, + event_emitter, + credentials, + account_id_endpoint_mode, + s3_disable_express_session_auth, + auth_scheme_preference, + ) + + # Copy the session's user agent factory and adds client configuration. + client_ua_creator = self._session_ua_creator.with_client_config( + new_config + ) + supplied_ua = client_config.user_agent if client_config else None + new_config._supplied_user_agent = supplied_ua + + return { + 'serializer': serializer, + 'endpoint': endpoint, + 'response_parser': response_parser, + 'event_emitter': event_emitter, + 'request_signer': signer, + 'service_model': service_model, + 'loader': self._loader, + 'client_config': new_config, + 'partition': partition, + 'exceptions_factory': self._exceptions_factory, + 'endpoint_ruleset_resolver': ruleset_resolver, + 'user_agent_creator': client_ua_creator, + } + + def compute_client_args( + self, + service_model, + client_config, + endpoint_bridge, + region_name, + endpoint_url, + is_secure, + scoped_config, + ): + service_name = service_model.endpoint_prefix + protocol = service_model.resolved_protocol + parameter_validation = True + if client_config and not client_config.parameter_validation: + parameter_validation = False + elif scoped_config: + raw_value = scoped_config.get('parameter_validation') + if raw_value is not None: + parameter_validation = ensure_boolean(raw_value) + + s3_config = self.compute_s3_config(client_config) + + configured_endpoint_url = self._compute_configured_endpoint_url( + client_config=client_config, + endpoint_url=endpoint_url, + ) + if configured_endpoint_url is not None: + register_feature_id('ENDPOINT_OVERRIDE') + + endpoint_config = self._compute_endpoint_config( + service_name=service_name, + region_name=region_name, + endpoint_url=configured_endpoint_url, + is_secure=is_secure, + endpoint_bridge=endpoint_bridge, + s3_config=s3_config, + ) + endpoint_variant_tags = endpoint_config['metadata'].get('tags', []) + + # Some third-party libraries expect the final user-agent string in + # ``client.meta.config.user_agent``. To maintain backwards + # compatibility, the preliminary user-agent string (before any Config + # object modifications and without request-specific user-agent + # components) is stored in the new Config object's ``user_agent`` + # property but not used by Botocore itself. + preliminary_ua_string = self._session_ua_creator.with_client_config( + client_config + ).to_string() + # Create a new client config to be passed to the client based + # on the final values. We do not want the user to be able + # to try to modify an existing client with a client config. + config_kwargs = dict( + region_name=endpoint_config['region_name'], + signature_version=endpoint_config['signature_version'], + user_agent=preliminary_ua_string, + ) + if 'dualstack' in endpoint_variant_tags: + config_kwargs.update(use_dualstack_endpoint=True) + if 'fips' in endpoint_variant_tags: + config_kwargs.update(use_fips_endpoint=True) + if client_config is not None: + config_kwargs.update( + connect_timeout=client_config.connect_timeout, + read_timeout=client_config.read_timeout, + max_pool_connections=client_config.max_pool_connections, + proxies=client_config.proxies, + proxies_config=client_config.proxies_config, + retries=client_config.retries, + client_cert=client_config.client_cert, + inject_host_prefix=client_config.inject_host_prefix, + tcp_keepalive=client_config.tcp_keepalive, + user_agent_extra=client_config.user_agent_extra, + user_agent_appid=client_config.user_agent_appid, + request_min_compression_size_bytes=( + client_config.request_min_compression_size_bytes + ), + disable_request_compression=( + client_config.disable_request_compression + ), + client_context_params=client_config.client_context_params, + sigv4a_signing_region_set=( + client_config.sigv4a_signing_region_set + ), + request_checksum_calculation=( + client_config.request_checksum_calculation + ), + response_checksum_validation=( + client_config.response_checksum_validation + ), + account_id_endpoint_mode=client_config.account_id_endpoint_mode, + auth_scheme_preference=client_config.auth_scheme_preference, + s3_disable_express_session_auth=( + client_config.s3.get('disable_s3_express_session_auth') + if client_config.s3 is not None + else None + ), + ) + self._compute_retry_config(config_kwargs) + self._compute_connect_timeout(config_kwargs) + self._compute_user_agent_appid_config(config_kwargs) + self._compute_request_compression_config(config_kwargs) + self._compute_sigv4a_signing_region_set_config(config_kwargs) + self._compute_checksum_config(config_kwargs) + self._compute_account_id_endpoint_mode_config(config_kwargs) + self._compute_inject_host_prefix(client_config, config_kwargs) + self._compute_auth_scheme_preference_config( + client_config, config_kwargs + ) + self._compute_signature_version_config(client_config, config_kwargs) + self._compute_s3_disable_express_session_auth(config_kwargs) + s3_config = self.compute_s3_config(client_config) + + is_s3_service = self._is_s3_service(service_name) + + if is_s3_service and 'dualstack' in endpoint_variant_tags: + if s3_config is None: + s3_config = {} + s3_config['use_dualstack_endpoint'] = True + + return { + 'service_name': service_name, + 'parameter_validation': parameter_validation, + 'configured_endpoint_url': configured_endpoint_url, + 'endpoint_config': endpoint_config, + 'protocol': protocol, + 'config_kwargs': config_kwargs, + 's3_config': s3_config, + 'socket_options': self._compute_socket_options( + scoped_config, client_config + ), + } + + def _compute_inject_host_prefix(self, client_config, config_kwargs): + # In the cases that a Config object was not provided, or the private value + # remained UNSET, we should resolve the value from the config store. + if ( + client_config is None + or client_config._inject_host_prefix == 'UNSET' + ): + configured_disable_host_prefix_injection = ( + self._config_store.get_config_variable( + 'disable_host_prefix_injection' + ) + ) + if configured_disable_host_prefix_injection is not None: + config_kwargs[ + 'inject_host_prefix' + ] = not configured_disable_host_prefix_injection + else: + config_kwargs['inject_host_prefix'] = True + + def _compute_configured_endpoint_url(self, client_config, endpoint_url): + if endpoint_url is not None: + return endpoint_url + + if self._ignore_configured_endpoint_urls(client_config): + logger.debug("Ignoring configured endpoint URLs.") + return endpoint_url + + return self._config_store.get_config_variable('endpoint_url') + + def _ignore_configured_endpoint_urls(self, client_config): + if ( + client_config + and client_config.ignore_configured_endpoint_urls is not None + ): + return client_config.ignore_configured_endpoint_urls + + return self._config_store.get_config_variable( + 'ignore_configured_endpoint_urls' + ) + + def compute_s3_config(self, client_config): + s3_configuration = self._config_store.get_config_variable('s3') + + # Next specific client config values takes precedence over + # specific values in the scoped config. + if client_config is not None: + if client_config.s3 is not None: + if s3_configuration is None: + s3_configuration = client_config.s3 + else: + # The current s3_configuration dictionary may be + # from a source that only should be read from so + # we want to be safe and just make a copy of it to modify + # before it actually gets updated. + s3_configuration = s3_configuration.copy() + s3_configuration.update(client_config.s3) + + return s3_configuration + + def _is_s3_service(self, service_name): + """Whether the service is S3 or S3 Control. + + Note that throughout this class, service_name refers to the endpoint + prefix, not the folder name of the service in botocore/data. For + S3 Control, the folder name is 's3control' but the endpoint prefix is + 's3-control'. + """ + return service_name in ['s3', 's3-control'] + + def _compute_endpoint_config( + self, + service_name, + region_name, + endpoint_url, + is_secure, + endpoint_bridge, + s3_config, + ): + resolve_endpoint_kwargs = { + 'service_name': service_name, + 'region_name': region_name, + 'endpoint_url': endpoint_url, + 'is_secure': is_secure, + 'endpoint_bridge': endpoint_bridge, + } + if service_name == 's3': + return self._compute_s3_endpoint_config( + s3_config=s3_config, **resolve_endpoint_kwargs + ) + if service_name == 'sts': + return self._compute_sts_endpoint_config(**resolve_endpoint_kwargs) + return self._resolve_endpoint(**resolve_endpoint_kwargs) + + def _compute_s3_endpoint_config( + self, s3_config, **resolve_endpoint_kwargs + ): + force_s3_global = self._should_force_s3_global( + resolve_endpoint_kwargs['region_name'], s3_config + ) + if force_s3_global: + resolve_endpoint_kwargs['region_name'] = None + endpoint_config = self._resolve_endpoint(**resolve_endpoint_kwargs) + self._set_region_if_custom_s3_endpoint( + endpoint_config, resolve_endpoint_kwargs['endpoint_bridge'] + ) + # For backwards compatibility reasons, we want to make sure the + # client.meta.region_name will remain us-east-1 if we forced the + # endpoint to be the global region. Specifically, if this value + # changes to aws-global, it breaks logic where a user is checking + # for us-east-1 as the global endpoint such as in creating buckets. + if force_s3_global and endpoint_config['region_name'] == 'aws-global': + endpoint_config['region_name'] = 'us-east-1' + return endpoint_config + + def _should_force_s3_global(self, region_name, s3_config): + s3_regional_config = 'legacy' + if s3_config and 'us_east_1_regional_endpoint' in s3_config: + s3_regional_config = s3_config['us_east_1_regional_endpoint'] + self._validate_s3_regional_config(s3_regional_config) + + is_global_region = region_name in ('us-east-1', None) + return s3_regional_config == 'legacy' and is_global_region + + def _validate_s3_regional_config(self, config_val): + if config_val not in VALID_REGIONAL_ENDPOINTS_CONFIG: + raise botocore.exceptions.InvalidS3UsEast1RegionalEndpointConfigError( + s3_us_east_1_regional_endpoint_config=config_val + ) + + def _validate_s3_disable_express_session_auth(self, config_val): + string_bool = isinstance(config_val, str) and config_val.lower() in [ + 'true', + 'false', + ] + if not isinstance(config_val, bool) and not string_bool: + raise botocore.exceptions.InvalidConfigError( + error_msg=( + f'Invalid value "{config_val}" for ' + 's3_disable_express_session_auth. Value must be a boolean' + ) + ) + + def _set_region_if_custom_s3_endpoint( + self, endpoint_config, endpoint_bridge + ): + # If a user is providing a custom URL, the endpoint resolver will + # refuse to infer a signing region. If we want to default to s3v4, + # we have to account for this. + if ( + endpoint_config['signing_region'] is None + and endpoint_config['region_name'] is None + ): + endpoint = endpoint_bridge.resolve('s3') + endpoint_config['signing_region'] = endpoint['signing_region'] + endpoint_config['region_name'] = endpoint['region_name'] + + def _compute_sts_endpoint_config(self, **resolve_endpoint_kwargs): + endpoint_config = self._resolve_endpoint(**resolve_endpoint_kwargs) + if self._should_set_global_sts_endpoint( + resolve_endpoint_kwargs['region_name'], + resolve_endpoint_kwargs['endpoint_url'], + endpoint_config, + ): + self._set_global_sts_endpoint( + endpoint_config, resolve_endpoint_kwargs['is_secure'] + ) + return endpoint_config + + def _should_set_global_sts_endpoint( + self, region_name, endpoint_url, endpoint_config + ): + has_variant_tags = endpoint_config and endpoint_config.get( + 'metadata', {} + ).get('tags') + if endpoint_url or has_variant_tags: + return False + return ( + self._get_sts_regional_endpoints_config() == 'legacy' + and region_name in LEGACY_GLOBAL_STS_REGIONS + ) + + def _get_sts_regional_endpoints_config(self): + sts_regional_endpoints_config = self._config_store.get_config_variable( + 'sts_regional_endpoints' + ) + if not sts_regional_endpoints_config: + sts_regional_endpoints_config = 'regional' + if ( + sts_regional_endpoints_config + not in VALID_REGIONAL_ENDPOINTS_CONFIG + ): + raise botocore.exceptions.InvalidSTSRegionalEndpointsConfigError( + sts_regional_endpoints_config=sts_regional_endpoints_config + ) + return sts_regional_endpoints_config + + def _set_global_sts_endpoint(self, endpoint_config, is_secure): + scheme = 'https' if is_secure else 'http' + endpoint_config['endpoint_url'] = f'{scheme}://sts.amazonaws.com' + endpoint_config['signing_region'] = 'us-east-1' + + def _resolve_endpoint( + self, + service_name, + region_name, + endpoint_url, + is_secure, + endpoint_bridge, + ): + return endpoint_bridge.resolve( + service_name, region_name, endpoint_url, is_secure + ) + + def _compute_socket_options(self, scoped_config, client_config=None): + # This disables Nagle's algorithm and is the default socket options + # in urllib3. + + socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)] + client_keepalive = client_config and client_config.tcp_keepalive + if client_keepalive is None: + client_keepalive = self._config_store.get_config_variable( + 'tcp_keepalive' + ) + + if client_keepalive: + socket_options.append((socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)) + return socket_options + + def _compute_retry_config(self, config_kwargs): + self._compute_retry_max_attempts(config_kwargs) + self._compute_retry_mode(config_kwargs) + + def _compute_retry_max_attempts(self, config_kwargs): + # There's a pre-existing max_attempts client config value that actually + # means max *retry* attempts. There's also a `max_attempts` we pull + # from the config store that means *total attempts*, which includes the + # intitial request. We can't change what `max_attempts` means in + # client config so we try to normalize everything to a new + # "total_max_attempts" variable. We ensure that after this, the only + # configuration for "max attempts" is the 'total_max_attempts' key. + # An explicitly provided max_attempts in the client config + # overrides everything. + retries = config_kwargs.get('retries') + if retries is not None: + if 'total_max_attempts' in retries: + retries.pop('max_attempts', None) + return + if 'max_attempts' in retries: + value = retries.pop('max_attempts') + # client config max_attempts means total retries so we + # have to add one for 'total_max_attempts' to account + # for the initial request. + retries['total_max_attempts'] = value + 1 + return + # Otherwise we'll check the config store which checks env vars, + # config files, etc. There is no default value for max_attempts + # so if this returns None and we don't set a default value here. + max_attempts = self._config_store.get_config_variable('max_attempts') + if max_attempts is not None: + if retries is None: + retries = {} + config_kwargs['retries'] = retries + retries['total_max_attempts'] = max_attempts + + def _compute_retry_mode(self, config_kwargs): + retries = config_kwargs.get('retries') + if retries is None: + retries = {} + config_kwargs['retries'] = retries + elif 'mode' in retries: + # If there's a retry mode explicitly set in the client config + # that overrides everything. + return + retry_mode = self._config_store.get_config_variable('retry_mode') + if retry_mode is None: + retry_mode = 'legacy' + retries['mode'] = retry_mode + + def _compute_connect_timeout(self, config_kwargs): + # Checking if connect_timeout is set on the client config. + # If it is not, we check the config_store in case a + # non legacy default mode has been configured. + connect_timeout = config_kwargs.get('connect_timeout') + if connect_timeout is not None: + return + connect_timeout = self._config_store.get_config_variable( + 'connect_timeout' + ) + if connect_timeout: + config_kwargs['connect_timeout'] = connect_timeout + + def _compute_request_compression_config(self, config_kwargs): + min_size = config_kwargs.get('request_min_compression_size_bytes') + disabled = config_kwargs.get('disable_request_compression') + if min_size is None: + min_size = self._config_store.get_config_variable( + 'request_min_compression_size_bytes' + ) + # conversion func is skipped so input validation must be done here + # regardless if the value is coming from the config store or the + # config object + min_size = self._validate_min_compression_size(min_size) + config_kwargs['request_min_compression_size_bytes'] = min_size + + if disabled is None: + disabled = self._config_store.get_config_variable( + 'disable_request_compression' + ) + else: + # if the user provided a value we must check if it's a boolean + disabled = ensure_boolean(disabled) + config_kwargs['disable_request_compression'] = disabled + + def _compute_s3_disable_express_session_auth(self, config_kwargs): + disable_express = config_kwargs.get('s3_disable_express_session_auth') + if disable_express is None: + disable_express = self._config_store.get_config_variable( + 's3_disable_express_session_auth' + ) + + # Raise an error if the value does not represent a boolean. + if disable_express is not None: + self._validate_s3_disable_express_session_auth(disable_express) + config_kwargs['s3_disable_express_session_auth'] = ensure_boolean( + disable_express + ) + + def _validate_min_compression_size(self, min_size): + min_allowed_min_size = 1 + max_allowed_min_size = 1048576 + error_msg_base = ( + f'Invalid value "{min_size}" for ' + 'request_min_compression_size_bytes.' + ) + try: + min_size = int(min_size) + except (ValueError, TypeError): + msg = ( + f'{error_msg_base} Value must be an integer. ' + f'Received {type(min_size)} instead.' + ) + raise botocore.exceptions.InvalidConfigError(error_msg=msg) + if not min_allowed_min_size <= min_size <= max_allowed_min_size: + msg = ( + f'{error_msg_base} Value must be between ' + f'{min_allowed_min_size} and {max_allowed_min_size}.' + ) + raise botocore.exceptions.InvalidConfigError(error_msg=msg) + + return min_size + + def _ensure_boolean(self, val): + if isinstance(val, bool): + return val + else: + return val.lower() == 'true' + + def _build_endpoint_resolver( + self, + endpoints_ruleset_data, + partition_data, + client_config, + service_model, + endpoint_region_name, + region_name, + endpoint_url, + endpoint, + is_secure, + endpoint_bridge, + event_emitter, + credentials, + account_id_endpoint_mode, + s3_disable_express_session_auth, + auth_scheme_preference, + ): + if endpoints_ruleset_data is None: + return None + + # The legacy EndpointResolver is global to the session, but + # EndpointRulesetResolver is service-specific. Builtins for + # EndpointRulesetResolver must not be derived from the legacy + # endpoint resolver's output, including final_args, s3_config, + # etc. + s3_config_raw = self.compute_s3_config(client_config) or {} + service_name_raw = service_model.endpoint_prefix + # Maintain complex logic for s3 and sts endpoints for backwards + # compatibility. + if service_name_raw in ['s3', 'sts'] or region_name is None: + eprv2_region_name = endpoint_region_name + else: + eprv2_region_name = region_name + resolver_builtins = self.compute_endpoint_resolver_builtin_defaults( + region_name=eprv2_region_name, + service_name=service_name_raw, + s3_config=s3_config_raw, + endpoint_bridge=endpoint_bridge, + client_endpoint_url=endpoint_url, + legacy_endpoint_url=endpoint.host, + credentials=credentials, + account_id_endpoint_mode=account_id_endpoint_mode, + s3_disable_express_session_auth=s3_disable_express_session_auth, + ) + # Client context params for s3 conflict with the available settings + # in the `s3` parameter on the `Config` object. If the same parameter + # is set in both places, the value in the `s3` parameter takes priority. + if client_config is not None: + client_context = client_config.client_context_params or {} + else: + client_context = {} + if self._is_s3_service(service_name_raw): + client_context.update(s3_config_raw) + if s3_disable_express_session_auth is not None: + client_context['disable_s3_express_session_auth'] = ( + s3_disable_express_session_auth + ) + + sig_version = ( + client_config.signature_version + if client_config is not None + else None + ) + return EndpointRulesetResolver( + endpoint_ruleset_data=endpoints_ruleset_data, + partition_data=partition_data, + service_model=service_model, + builtins=resolver_builtins, + client_context=client_context, + event_emitter=event_emitter, + use_ssl=is_secure, + requested_auth_scheme=sig_version, + auth_scheme_preference=auth_scheme_preference, + ) + + def compute_endpoint_resolver_builtin_defaults( + self, + region_name, + service_name, + s3_config, + endpoint_bridge, + client_endpoint_url, + legacy_endpoint_url, + credentials, + account_id_endpoint_mode, + s3_disable_express_session_auth, + ): + # EndpointRulesetResolver rulesets may accept an "SDK::Endpoint" as + # input. If the endpoint_url argument of create_client() is set, it + # always takes priority. + if client_endpoint_url: + given_endpoint = client_endpoint_url + # If an endpoints.json data file other than the one bundled within + # the botocore/data directory is used, the output of legacy + # endpoint resolution is provided to EndpointRulesetResolver. + elif not endpoint_bridge.resolver_uses_builtin_data(): + given_endpoint = legacy_endpoint_url + else: + given_endpoint = None + + # The endpoint rulesets differ from legacy botocore behavior in whether + # forcing path style addressing in incompatible situations raises an + # exception or silently ignores the config setting. The + # AWS_S3_FORCE_PATH_STYLE parameter is adjusted both here and for each + # operation so that the ruleset behavior is backwards compatible. + if s3_config.get('use_accelerate_endpoint', False): + force_path_style = False + elif client_endpoint_url is not None and not is_s3_accelerate_url( + client_endpoint_url + ): + force_path_style = s3_config.get('addressing_style') != 'virtual' + else: + force_path_style = s3_config.get('addressing_style') == 'path' + + return { + EPRBuiltins.AWS_REGION: region_name, + EPRBuiltins.AWS_USE_FIPS: ( + # SDK_ENDPOINT cannot be combined with AWS_USE_FIPS + given_endpoint is None + # use legacy resolver's _resolve_endpoint_variant_config_var() + # or default to False if it returns None + and endpoint_bridge._resolve_endpoint_variant_config_var( + 'use_fips_endpoint' + ) + or False + ), + EPRBuiltins.AWS_USE_DUALSTACK: ( + # SDK_ENDPOINT cannot be combined with AWS_USE_DUALSTACK + given_endpoint is None + # use legacy resolver's _resolve_use_dualstack_endpoint() and + # or default to False if it returns None + and endpoint_bridge._resolve_use_dualstack_endpoint( + service_name + ) + or False + ), + EPRBuiltins.AWS_STS_USE_GLOBAL_ENDPOINT: ( + self._should_set_global_sts_endpoint( + region_name=region_name, + endpoint_url=None, + endpoint_config=None, + ) + ), + EPRBuiltins.AWS_S3_USE_GLOBAL_ENDPOINT: ( + self._should_force_s3_global(region_name, s3_config) + ), + EPRBuiltins.AWS_S3_ACCELERATE: s3_config.get( + 'use_accelerate_endpoint', False + ), + EPRBuiltins.AWS_S3_FORCE_PATH_STYLE: force_path_style, + EPRBuiltins.AWS_S3_USE_ARN_REGION: s3_config.get( + 'use_arn_region', True + ), + EPRBuiltins.AWS_S3CONTROL_USE_ARN_REGION: s3_config.get( + 'use_arn_region', False + ), + EPRBuiltins.AWS_S3_DISABLE_MRAP: s3_config.get( + 's3_disable_multiregion_access_points', False + ), + EPRBuiltins.AWS_S3_DISABLE_EXPRESS_SESSION_AUTH: ( + s3_disable_express_session_auth + ), + EPRBuiltins.SDK_ENDPOINT: given_endpoint, + EPRBuiltins.ACCOUNT_ID: credentials.get_deferred_property( + 'account_id' + ) + if credentials + else None, + EPRBuiltins.ACCOUNT_ID_ENDPOINT_MODE: account_id_endpoint_mode, + } + + def _compute_user_agent_appid_config(self, config_kwargs): + user_agent_appid = config_kwargs.get('user_agent_appid') + if user_agent_appid is None: + user_agent_appid = self._config_store.get_config_variable( + 'user_agent_appid' + ) + if ( + user_agent_appid is not None + and len(user_agent_appid) > USERAGENT_APPID_MAXLEN + ): + logger.warning( + 'The configured value for user_agent_appid exceeds the ' + 'maximum length of %d characters.', + USERAGENT_APPID_MAXLEN, + ) + config_kwargs['user_agent_appid'] = user_agent_appid + + def _compute_sigv4a_signing_region_set_config(self, config_kwargs): + sigv4a_signing_region_set = config_kwargs.get( + 'sigv4a_signing_region_set' + ) + if sigv4a_signing_region_set is None: + sigv4a_signing_region_set = self._config_store.get_config_variable( + 'sigv4a_signing_region_set' + ) + config_kwargs['sigv4a_signing_region_set'] = sigv4a_signing_region_set + + def _compute_checksum_config(self, config_kwargs): + self._handle_checksum_config( + config_kwargs, + config_key="request_checksum_calculation", + valid_options=VALID_REQUEST_CHECKSUM_CALCULATION_CONFIG, + ) + self._handle_checksum_config( + config_kwargs, + config_key="response_checksum_validation", + valid_options=VALID_RESPONSE_CHECKSUM_VALIDATION_CONFIG, + ) + + def _handle_checksum_config( + self, + config_kwargs, + config_key, + valid_options, + ): + value = config_kwargs.get(config_key) + if value is None: + value = self._config_store.get_config_variable(config_key) + + if isinstance(value, str): + value = value.lower() + + if value not in valid_options: + raise botocore.exceptions.InvalidChecksumConfigError( + config_key=config_key, + config_value=value, + valid_options=valid_options, + ) + self._register_checksum_config_feature_ids(value, config_key) + config_kwargs[config_key] = value + + def _register_checksum_config_feature_ids(self, value, config_key): + checksum_config_feature_id = None + if config_key == "request_checksum_calculation": + checksum_config_feature_id = ( + f"FLEXIBLE_CHECKSUMS_REQ_{value.upper()}" + ) + elif config_key == "response_checksum_validation": + checksum_config_feature_id = ( + f"FLEXIBLE_CHECKSUMS_RES_{value.upper()}" + ) + if checksum_config_feature_id is not None: + register_feature_id(checksum_config_feature_id) + + def _compute_account_id_endpoint_mode_config(self, config_kwargs): + config_key = 'account_id_endpoint_mode' + + # Disable account id based endpoint routing for unsigned requests + # since there are no credentials to resolve. + signature_version = config_kwargs.get('signature_version') + if signature_version is botocore.UNSIGNED: + config_kwargs[config_key] = 'disabled' + return + + account_id_endpoint_mode = config_kwargs.get(config_key) + if account_id_endpoint_mode is None: + account_id_endpoint_mode = self._config_store.get_config_variable( + config_key + ) + + if isinstance(account_id_endpoint_mode, str): + account_id_endpoint_mode = account_id_endpoint_mode.lower() + + if ( + account_id_endpoint_mode + not in VALID_ACCOUNT_ID_ENDPOINT_MODE_CONFIG + ): + raise botocore.exceptions.InvalidConfigError( + error_msg=f"The configured value '{account_id_endpoint_mode}' for '{config_key}' is " + f"invalid. Valid values are: {VALID_ACCOUNT_ID_ENDPOINT_MODE_CONFIG}." + ) + + config_kwargs[config_key] = account_id_endpoint_mode + + def _compute_auth_scheme_preference_config( + self, client_config, config_kwargs + ): + config_key = 'auth_scheme_preference' + set_in_config_object = False + + if client_config and client_config.auth_scheme_preference: + value = client_config.auth_scheme_preference + set_in_config_object = True + else: + value = self._config_store.get_config_variable(config_key) + + if value is None: + config_kwargs[config_key] = None + return + + if not isinstance(value, str): + raise botocore.exceptions.InvalidConfigError( + error_msg=( + f"{config_key} must be a comma-delimited string. " + f"Received {type(value)} instead: {value}." + ) + ) + + value = ','.join( + item.replace(' ', '').replace('\t', '') + for item in value.split(',') + if item.strip() + ) + + if set_in_config_object: + value = ClientConfigString(value) + + config_kwargs[config_key] = value + + def _compute_signature_version_config(self, client_config, config_kwargs): + if client_config and client_config.signature_version: + value = client_config.signature_version + if isinstance(value, str): + config_kwargs['signature_version'] = ClientConfigString(value) + + +class ConfigObjectWrapper: + """Base class to mark values set via in-code Config object.""" + + pass + + +class ClientConfigString(str, ConfigObjectWrapper): + def __new__(cls, value=None): + return super().__new__(cls, value) diff --git a/venv/lib/python3.12/site-packages/botocore/auth.py b/venv/lib/python3.12/site-packages/botocore/auth.py new file mode 100644 index 0000000..82408b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/auth.py @@ -0,0 +1,1234 @@ +# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import base64 +import calendar +import datetime +import functools +import hmac +import json +import logging +import time +from collections.abc import Mapping +from email.utils import formatdate +from hashlib import sha1, sha256 +from operator import itemgetter + +from botocore.compat import ( + HAS_CRT, + MD5_AVAILABLE, # noqa: F401 + HTTPHeaders, + encodebytes, + ensure_unicode, + get_current_datetime, + parse_qs, + quote, + unquote, + urlsplit, + urlunsplit, +) +from botocore.exceptions import ( + NoAuthTokenError, + NoCredentialsError, + UnknownSignatureVersionError, + UnsupportedSignatureVersionError, +) +from botocore.utils import ( + is_valid_ipv6_endpoint_url, + normalize_url_path, + percent_encode_sequence, +) + +logger = logging.getLogger(__name__) + + +EMPTY_SHA256_HASH = ( + 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' +) +# This is the buffer size used when calculating sha256 checksums. +# Experimenting with various buffer sizes showed that this value generally +# gave the best result (in terms of performance). +PAYLOAD_BUFFER = 1024 * 1024 +ISO8601 = '%Y-%m-%dT%H:%M:%SZ' +SIGV4_TIMESTAMP = '%Y%m%dT%H%M%SZ' +SIGNED_HEADERS_BLACKLIST = [ + 'connection', + 'expect', + 'keep-alive', + 'proxy-authenticate', + 'proxy-authorization', + 'te', + 'trailer', + 'transfer-encoding', + 'upgrade', + 'user-agent', + 'x-amzn-trace-id', +] +UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD' +STREAMING_UNSIGNED_PAYLOAD_TRAILER = 'STREAMING-UNSIGNED-PAYLOAD-TRAILER' + + +def _host_from_url(url): + # Given URL, derive value for host header. Ensure that value: + # 1) is lowercase + # 2) excludes port, if it was the default port + # 3) excludes userinfo + url_parts = urlsplit(url) + host = url_parts.hostname # urlsplit's hostname is always lowercase + if is_valid_ipv6_endpoint_url(url): + host = f'[{host}]' + default_ports = { + 'http': 80, + 'https': 443, + } + if url_parts.port is not None: + if url_parts.port != default_ports.get(url_parts.scheme): + host = f'{host}:{url_parts.port}' + return host + + +def _get_body_as_dict(request): + # For query services, request.data is form-encoded and is already a + # dict, but for other services such as rest-json it could be a json + # string or bytes. In those cases we attempt to load the data as a + # dict. + data = request.data + if isinstance(data, bytes): + data = json.loads(data.decode('utf-8')) + elif isinstance(data, str): + data = json.loads(data) + return data + + +class BaseSigner: + REQUIRES_REGION = False + REQUIRES_TOKEN = False + + def add_auth(self, request): + raise NotImplementedError("add_auth") + + +class TokenSigner(BaseSigner): + REQUIRES_TOKEN = True + """ + Signers that expect an authorization token to perform the authorization + """ + + def __init__(self, auth_token): + self.auth_token = auth_token + + +class SigV2Auth(BaseSigner): + """ + Sign a request with Signature V2. + """ + + def __init__(self, credentials): + self.credentials = credentials + + def calc_signature(self, request, params): + logger.debug("Calculating signature using v2 auth.") + split = urlsplit(request.url) + path = split.path + if len(path) == 0: + path = '/' + string_to_sign = f"{request.method}\n{split.netloc}\n{path}\n" + lhmac = hmac.new( + self.credentials.secret_key.encode("utf-8"), digestmod=sha256 + ) + pairs = [] + for key in sorted(params): + # Any previous signature should not be a part of this + # one, so we skip that particular key. This prevents + # issues during retries. + if key == 'Signature': + continue + value = str(params[key]) + quoted_key = quote(key.encode('utf-8'), safe='') + quoted_value = quote(value.encode('utf-8'), safe='-_~') + pairs.append(f'{quoted_key}={quoted_value}') + qs = '&'.join(pairs) + string_to_sign += qs + logger.debug('String to sign: %s', string_to_sign) + lhmac.update(string_to_sign.encode('utf-8')) + b64 = base64.b64encode(lhmac.digest()).strip().decode('utf-8') + return (qs, b64) + + def add_auth(self, request): + # The auth handler is the last thing called in the + # preparation phase of a prepared request. + # Because of this we have to parse the query params + # from the request body so we can update them with + # the sigv2 auth params. + if self.credentials is None: + raise NoCredentialsError() + if request.data: + # POST + params = request.data + else: + # GET + params = request.params + params['AWSAccessKeyId'] = self.credentials.access_key + params['SignatureVersion'] = '2' + params['SignatureMethod'] = 'HmacSHA256' + params['Timestamp'] = time.strftime(ISO8601, time.gmtime()) + if self.credentials.token: + params['SecurityToken'] = self.credentials.token + qs, signature = self.calc_signature(request, params) + params['Signature'] = signature + return request + + +class SigV3Auth(BaseSigner): + def __init__(self, credentials): + self.credentials = credentials + + def add_auth(self, request): + if self.credentials is None: + raise NoCredentialsError() + if 'Date' in request.headers: + del request.headers['Date'] + request.headers['Date'] = formatdate(usegmt=True) + if self.credentials.token: + if 'X-Amz-Security-Token' in request.headers: + del request.headers['X-Amz-Security-Token'] + request.headers['X-Amz-Security-Token'] = self.credentials.token + new_hmac = hmac.new( + self.credentials.secret_key.encode('utf-8'), digestmod=sha256 + ) + new_hmac.update(request.headers['Date'].encode('utf-8')) + encoded_signature = encodebytes(new_hmac.digest()).strip() + signature = ( + f"AWS3-HTTPS AWSAccessKeyId={self.credentials.access_key}," + f"Algorithm=HmacSHA256,Signature={encoded_signature.decode('utf-8')}" + ) + if 'X-Amzn-Authorization' in request.headers: + del request.headers['X-Amzn-Authorization'] + request.headers['X-Amzn-Authorization'] = signature + + +class SigV4Auth(BaseSigner): + """ + Sign a request with Signature V4. + """ + + REQUIRES_REGION = True + + def __init__(self, credentials, service_name, region_name): + self.credentials = credentials + # We initialize these value here so the unit tests can have + # valid values. But these will get overriden in ``add_auth`` + # later for real requests. + self._region_name = region_name + self._service_name = service_name + + def _sign(self, key, msg, hex=False): + if hex: + sig = hmac.new(key, msg.encode('utf-8'), sha256).hexdigest() + else: + sig = hmac.new(key, msg.encode('utf-8'), sha256).digest() + return sig + + def headers_to_sign(self, request): + """ + Select the headers from the request that need to be included + in the StringToSign. + """ + header_map = HTTPHeaders() + for name, value in request.headers.items(): + lname = name.lower() + if lname not in SIGNED_HEADERS_BLACKLIST: + header_map[lname] = value + if 'host' not in header_map: + # TODO: We should set the host ourselves, instead of relying on our + # HTTP client to set it for us. + header_map['host'] = _host_from_url(request.url) + return header_map + + def canonical_query_string(self, request): + # The query string can come from two parts. One is the + # params attribute of the request. The other is from the request + # url (in which case we have to re-split the url into its components + # and parse out the query string component). + if request.params: + return self._canonical_query_string_params(request.params) + else: + return self._canonical_query_string_url(urlsplit(request.url)) + + def _canonical_query_string_params(self, params): + # [(key, value), (key2, value2)] + key_val_pairs = [] + if isinstance(params, Mapping): + params = params.items() + for key, value in params: + key_val_pairs.append( + (quote(key, safe='-_.~'), quote(str(value), safe='-_.~')) + ) + sorted_key_vals = [] + # Sort by the URI-encoded key names, and in the case of + # repeated keys, then sort by the value. + for key, value in sorted(key_val_pairs): + sorted_key_vals.append(f'{key}={value}') + canonical_query_string = '&'.join(sorted_key_vals) + return canonical_query_string + + def _canonical_query_string_url(self, parts): + canonical_query_string = '' + if parts.query: + # [(key, value), (key2, value2)] + key_val_pairs = [] + for pair in parts.query.split('&'): + key, _, value = pair.partition('=') + key_val_pairs.append((key, value)) + sorted_key_vals = [] + # Sort by the URI-encoded key names, and in the case of + # repeated keys, then sort by the value. + for key, value in sorted(key_val_pairs): + sorted_key_vals.append(f'{key}={value}') + canonical_query_string = '&'.join(sorted_key_vals) + return canonical_query_string + + def canonical_headers(self, headers_to_sign): + """ + Return the headers that need to be included in the StringToSign + in their canonical form by converting all header keys to lower + case, sorting them in alphabetical order and then joining + them into a string, separated by newlines. + """ + headers = [] + sorted_header_names = sorted(set(headers_to_sign)) + for key in sorted_header_names: + value = ','.join( + self._header_value(v) for v in headers_to_sign.get_all(key) + ) + headers.append(f'{key}:{ensure_unicode(value)}') + return '\n'.join(headers) + + def _header_value(self, value): + # From the sigv4 docs: + # Lowercase(HeaderName) + ':' + Trimall(HeaderValue) + # + # The Trimall function removes excess white space before and after + # values, and converts sequential spaces to a single space. + return ' '.join(value.split()) + + def signed_headers(self, headers_to_sign): + headers = sorted(n.lower().strip() for n in set(headers_to_sign)) + return ';'.join(headers) + + def _is_streaming_checksum_payload(self, request): + checksum_context = request.context.get('checksum', {}) + algorithm = checksum_context.get('request_algorithm') + return isinstance(algorithm, dict) and algorithm.get('in') == 'trailer' + + def payload(self, request): + if self._is_streaming_checksum_payload(request): + return STREAMING_UNSIGNED_PAYLOAD_TRAILER + elif not self._should_sha256_sign_payload(request): + # When payload signing is disabled, we use this static string in + # place of the payload checksum. + return UNSIGNED_PAYLOAD + request_body = request.body + if request_body and hasattr(request_body, 'seek'): + position = request_body.tell() + read_chunksize = functools.partial( + request_body.read, PAYLOAD_BUFFER + ) + checksum = sha256() + for chunk in iter(read_chunksize, b''): + checksum.update(chunk) + hex_checksum = checksum.hexdigest() + request_body.seek(position) + return hex_checksum + elif request_body: + # The request serialization has ensured that + # request.body is a bytes() type. + return sha256(request_body).hexdigest() + else: + return EMPTY_SHA256_HASH + + def _should_sha256_sign_payload(self, request): + # Payloads will always be signed over insecure connections. + if not request.url.startswith('https'): + return True + + # Certain operations may have payload signing disabled by default. + # Since we don't have access to the operation model, we pass in this + # bit of metadata through the request context. + return request.context.get('payload_signing_enabled', True) + + def canonical_request(self, request): + cr = [request.method.upper()] + path = self._normalize_url_path(urlsplit(request.url).path) + cr.append(path) + cr.append(self.canonical_query_string(request)) + headers_to_sign = self.headers_to_sign(request) + cr.append(self.canonical_headers(headers_to_sign) + '\n') + cr.append(self.signed_headers(headers_to_sign)) + if 'X-Amz-Content-SHA256' in request.headers: + body_checksum = request.headers['X-Amz-Content-SHA256'] + else: + body_checksum = self.payload(request) + cr.append(body_checksum) + return '\n'.join(cr) + + def _normalize_url_path(self, path): + normalized_path = quote(normalize_url_path(path), safe='/~') + return normalized_path + + def scope(self, request): + scope = [self.credentials.access_key] + scope.append(request.context['timestamp'][0:8]) + scope.append(self._region_name) + scope.append(self._service_name) + scope.append('aws4_request') + return '/'.join(scope) + + def credential_scope(self, request): + scope = [] + scope.append(request.context['timestamp'][0:8]) + scope.append(self._region_name) + scope.append(self._service_name) + scope.append('aws4_request') + return '/'.join(scope) + + def string_to_sign(self, request, canonical_request): + """ + Return the canonical StringToSign as well as a dict + containing the original version of all headers that + were included in the StringToSign. + """ + sts = ['AWS4-HMAC-SHA256'] + sts.append(request.context['timestamp']) + sts.append(self.credential_scope(request)) + sts.append(sha256(canonical_request.encode('utf-8')).hexdigest()) + return '\n'.join(sts) + + def signature(self, string_to_sign, request): + key = self.credentials.secret_key + k_date = self._sign( + (f"AWS4{key}").encode(), request.context["timestamp"][0:8] + ) + k_region = self._sign(k_date, self._region_name) + k_service = self._sign(k_region, self._service_name) + k_signing = self._sign(k_service, 'aws4_request') + return self._sign(k_signing, string_to_sign, hex=True) + + def add_auth(self, request): + if self.credentials is None: + raise NoCredentialsError() + datetime_now = get_current_datetime() + request.context['timestamp'] = datetime_now.strftime(SIGV4_TIMESTAMP) + # This could be a retry. Make sure the previous + # authorization header is removed first. + self._modify_request_before_signing(request) + canonical_request = self.canonical_request(request) + logger.debug("Calculating signature using v4 auth.") + logger.debug('CanonicalRequest:\n%s', canonical_request) + string_to_sign = self.string_to_sign(request, canonical_request) + logger.debug('StringToSign:\n%s', string_to_sign) + signature = self.signature(string_to_sign, request) + logger.debug('Signature:\n%s', signature) + + self._inject_signature_to_request(request, signature) + + def _inject_signature_to_request(self, request, signature): + auth_str = [f'AWS4-HMAC-SHA256 Credential={self.scope(request)}'] + headers_to_sign = self.headers_to_sign(request) + auth_str.append( + f"SignedHeaders={self.signed_headers(headers_to_sign)}" + ) + auth_str.append(f'Signature={signature}') + request.headers['Authorization'] = ', '.join(auth_str) + return request + + def _modify_request_before_signing(self, request): + if 'Authorization' in request.headers: + del request.headers['Authorization'] + self._set_necessary_date_headers(request) + if self.credentials.token: + if 'X-Amz-Security-Token' in request.headers: + del request.headers['X-Amz-Security-Token'] + request.headers['X-Amz-Security-Token'] = self.credentials.token + + if not request.context.get('payload_signing_enabled', True): + if 'X-Amz-Content-SHA256' in request.headers: + del request.headers['X-Amz-Content-SHA256'] + request.headers['X-Amz-Content-SHA256'] = UNSIGNED_PAYLOAD + + def _set_necessary_date_headers(self, request): + # The spec allows for either the Date _or_ the X-Amz-Date value to be + # used so we check both. If there's a Date header, we use the date + # header. Otherwise we use the X-Amz-Date header. + if 'Date' in request.headers: + del request.headers['Date'] + datetime_timestamp = datetime.datetime.strptime( + request.context['timestamp'], SIGV4_TIMESTAMP + ) + request.headers['Date'] = formatdate( + int(calendar.timegm(datetime_timestamp.timetuple())) + ) + if 'X-Amz-Date' in request.headers: + del request.headers['X-Amz-Date'] + else: + if 'X-Amz-Date' in request.headers: + del request.headers['X-Amz-Date'] + request.headers['X-Amz-Date'] = request.context['timestamp'] + + +class S3SigV4Auth(SigV4Auth): + def _modify_request_before_signing(self, request): + super()._modify_request_before_signing(request) + if 'X-Amz-Content-SHA256' in request.headers: + del request.headers['X-Amz-Content-SHA256'] + + request.headers['X-Amz-Content-SHA256'] = self.payload(request) + + def _should_sha256_sign_payload(self, request): + # S3 allows optional body signing, so to minimize the performance + # impact, we opt to not SHA256 sign the body on streaming uploads, + # provided that we're on https. + client_config = request.context.get('client_config') + s3_config = getattr(client_config, 's3', None) + + # The config could be None if it isn't set, or if the customer sets it + # to None. + if s3_config is None: + s3_config = {} + + # The explicit configuration takes precedence over any implicit + # configuration. + sign_payload = s3_config.get('payload_signing_enabled', None) + if sign_payload is not None: + return sign_payload + + # We require that both a checksum be present and https be enabled + # to implicitly disable body signing. The combination of TLS and + # a checksum is sufficiently secure and durable for us to be + # confident in the request without body signing. + checksum_header = 'Content-MD5' + checksum_context = request.context.get('checksum', {}) + algorithm = checksum_context.get('request_algorithm') + if isinstance(algorithm, dict) and algorithm.get('in') == 'header': + checksum_header = algorithm['name'] + if ( + not request.url.startswith("https") + or checksum_header not in request.headers + ): + return True + + # If the input is streaming we disable body signing by default. + if request.context.get('has_streaming_input', False): + return False + + # If the S3-specific checks had no results, delegate to the generic + # checks. + return super()._should_sha256_sign_payload(request) + + def _normalize_url_path(self, path): + # For S3, we do not normalize the path. + return path + + +class S3ExpressAuth(S3SigV4Auth): + REQUIRES_IDENTITY_CACHE = True + + def __init__( + self, credentials, service_name, region_name, *, identity_cache + ): + super().__init__(credentials, service_name, region_name) + self._identity_cache = identity_cache + + def add_auth(self, request): + super().add_auth(request) + + def _modify_request_before_signing(self, request): + super()._modify_request_before_signing(request) + if 'x-amz-s3session-token' not in request.headers: + request.headers['x-amz-s3session-token'] = self.credentials.token + # S3Express does not support STS' X-Amz-Security-Token + if 'X-Amz-Security-Token' in request.headers: + del request.headers['X-Amz-Security-Token'] + + +class S3ExpressPostAuth(S3ExpressAuth): + REQUIRES_IDENTITY_CACHE = True + + def add_auth(self, request): + datetime_now = get_current_datetime() + request.context['timestamp'] = datetime_now.strftime(SIGV4_TIMESTAMP) + + fields = {} + if request.context.get('s3-presign-post-fields', None) is not None: + fields = request.context['s3-presign-post-fields'] + + policy = {} + conditions = [] + if request.context.get('s3-presign-post-policy', None) is not None: + policy = request.context['s3-presign-post-policy'] + if policy.get('conditions', None) is not None: + conditions = policy['conditions'] + + policy['conditions'] = conditions + + fields['x-amz-algorithm'] = 'AWS4-HMAC-SHA256' + fields['x-amz-credential'] = self.scope(request) + fields['x-amz-date'] = request.context['timestamp'] + + conditions.append({'x-amz-algorithm': 'AWS4-HMAC-SHA256'}) + conditions.append({'x-amz-credential': self.scope(request)}) + conditions.append({'x-amz-date': request.context['timestamp']}) + + if self.credentials.token is not None: + fields['X-Amz-S3session-Token'] = self.credentials.token + conditions.append( + {'X-Amz-S3session-Token': self.credentials.token} + ) + + # Dump the base64 encoded policy into the fields dictionary. + fields['policy'] = base64.b64encode( + json.dumps(policy).encode('utf-8') + ).decode('utf-8') + + fields['x-amz-signature'] = self.signature(fields['policy'], request) + + request.context['s3-presign-post-fields'] = fields + request.context['s3-presign-post-policy'] = policy + + +class S3ExpressQueryAuth(S3ExpressAuth): + DEFAULT_EXPIRES = 300 + REQUIRES_IDENTITY_CACHE = True + + def __init__( + self, + credentials, + service_name, + region_name, + *, + identity_cache, + expires=DEFAULT_EXPIRES, + ): + super().__init__( + credentials, + service_name, + region_name, + identity_cache=identity_cache, + ) + self._expires = expires + + def _modify_request_before_signing(self, request): + # We automatically set this header, so if it's the auto-set value we + # want to get rid of it since it doesn't make sense for presigned urls. + content_type = request.headers.get('content-type') + blocklisted_content_type = ( + 'application/x-www-form-urlencoded; charset=utf-8' + ) + if content_type == blocklisted_content_type: + del request.headers['content-type'] + + # Note that we're not including X-Amz-Signature. + # From the docs: "The Canonical Query String must include all the query + # parameters from the preceding table except for X-Amz-Signature. + signed_headers = self.signed_headers(self.headers_to_sign(request)) + + auth_params = { + 'X-Amz-Algorithm': 'AWS4-HMAC-SHA256', + 'X-Amz-Credential': self.scope(request), + 'X-Amz-Date': request.context['timestamp'], + 'X-Amz-Expires': self._expires, + 'X-Amz-SignedHeaders': signed_headers, + } + if self.credentials.token is not None: + auth_params['X-Amz-S3session-Token'] = self.credentials.token + # Now parse the original query string to a dict, inject our new query + # params, and serialize back to a query string. + url_parts = urlsplit(request.url) + # parse_qs makes each value a list, but in our case we know we won't + # have repeated keys so we know we have single element lists which we + # can convert back to scalar values. + query_string_parts = parse_qs(url_parts.query, keep_blank_values=True) + query_dict = {k: v[0] for k, v in query_string_parts.items()} + + if request.params: + query_dict.update(request.params) + request.params = {} + # The spec is particular about this. It *has* to be: + # https://?& + # You can't mix the two types of params together, i.e just keep doing + # new_query_params.update(op_params) + # new_query_params.update(auth_params) + # percent_encode_sequence(new_query_params) + operation_params = '' + if request.data: + # We also need to move the body params into the query string. To + # do this, we first have to convert it to a dict. + query_dict.update(_get_body_as_dict(request)) + request.data = '' + if query_dict: + operation_params = percent_encode_sequence(query_dict) + '&' + new_query_string = ( + f"{operation_params}{percent_encode_sequence(auth_params)}" + ) + # url_parts is a tuple (and therefore immutable) so we need to create + # a new url_parts with the new query string. + # - + # scheme - 0 + # netloc - 1 + # path - 2 + # query - 3 <-- we're replacing this. + # fragment - 4 + p = url_parts + new_url_parts = (p[0], p[1], p[2], new_query_string, p[4]) + request.url = urlunsplit(new_url_parts) + + def _inject_signature_to_request(self, request, signature): + # Rather than calculating an "Authorization" header, for the query + # param quth, we just append an 'X-Amz-Signature' param to the end + # of the query string. + request.url += f'&X-Amz-Signature={signature}' + + def _normalize_url_path(self, path): + # For S3, we do not normalize the path. + return path + + def payload(self, request): + # From the doc link above: + # "You don't include a payload hash in the Canonical Request, because + # when you create a presigned URL, you don't know anything about the + # payload. Instead, you use a constant string "UNSIGNED-PAYLOAD". + return UNSIGNED_PAYLOAD + + +class SigV4QueryAuth(SigV4Auth): + DEFAULT_EXPIRES = 3600 + + def __init__( + self, credentials, service_name, region_name, expires=DEFAULT_EXPIRES + ): + super().__init__(credentials, service_name, region_name) + self._expires = expires + + def _modify_request_before_signing(self, request): + # We automatically set this header, so if it's the auto-set value we + # want to get rid of it since it doesn't make sense for presigned urls. + content_type = request.headers.get('content-type') + blacklisted_content_type = ( + 'application/x-www-form-urlencoded; charset=utf-8' + ) + if content_type == blacklisted_content_type: + del request.headers['content-type'] + + # Note that we're not including X-Amz-Signature. + # From the docs: "The Canonical Query String must include all the query + # parameters from the preceding table except for X-Amz-Signature. + signed_headers = self.signed_headers(self.headers_to_sign(request)) + + auth_params = { + 'X-Amz-Algorithm': 'AWS4-HMAC-SHA256', + 'X-Amz-Credential': self.scope(request), + 'X-Amz-Date': request.context['timestamp'], + 'X-Amz-Expires': self._expires, + 'X-Amz-SignedHeaders': signed_headers, + } + if self.credentials.token is not None: + auth_params['X-Amz-Security-Token'] = self.credentials.token + # Now parse the original query string to a dict, inject our new query + # params, and serialize back to a query string. + url_parts = urlsplit(request.url) + # parse_qs makes each value a list, but in our case we know we won't + # have repeated keys so we know we have single element lists which we + # can convert back to scalar values. + query_string_parts = parse_qs(url_parts.query, keep_blank_values=True) + query_dict = {k: v[0] for k, v in query_string_parts.items()} + + if request.params: + query_dict.update(request.params) + request.params = {} + # The spec is particular about this. It *has* to be: + # https://?& + # You can't mix the two types of params together, i.e just keep doing + # new_query_params.update(op_params) + # new_query_params.update(auth_params) + # percent_encode_sequence(new_query_params) + operation_params = '' + if request.data: + # We also need to move the body params into the query string. To + # do this, we first have to convert it to a dict. + query_dict.update(_get_body_as_dict(request)) + request.data = '' + if query_dict: + operation_params = percent_encode_sequence(query_dict) + '&' + new_query_string = ( + f"{operation_params}{percent_encode_sequence(auth_params)}" + ) + # url_parts is a tuple (and therefore immutable) so we need to create + # a new url_parts with the new query string. + # - + # scheme - 0 + # netloc - 1 + # path - 2 + # query - 3 <-- we're replacing this. + # fragment - 4 + p = url_parts + new_url_parts = (p[0], p[1], p[2], new_query_string, p[4]) + request.url = urlunsplit(new_url_parts) + + def _inject_signature_to_request(self, request, signature): + # Rather than calculating an "Authorization" header, for the query + # param quth, we just append an 'X-Amz-Signature' param to the end + # of the query string. + request.url += f'&X-Amz-Signature={signature}' + + +class S3SigV4QueryAuth(SigV4QueryAuth): + """S3 SigV4 auth using query parameters. + + This signer will sign a request using query parameters and signature + version 4, i.e a "presigned url" signer. + + Based off of: + + http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html + + """ + + def _normalize_url_path(self, path): + # For S3, we do not normalize the path. + return path + + def payload(self, request): + # From the doc link above: + # "You don't include a payload hash in the Canonical Request, because + # when you create a presigned URL, you don't know anything about the + # payload. Instead, you use a constant string "UNSIGNED-PAYLOAD". + return UNSIGNED_PAYLOAD + + +class S3SigV4PostAuth(SigV4Auth): + """ + Presigns a s3 post + + Implementation doc here: + http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html + """ + + def add_auth(self, request): + datetime_now = get_current_datetime() + request.context['timestamp'] = datetime_now.strftime(SIGV4_TIMESTAMP) + + fields = {} + if request.context.get('s3-presign-post-fields', None) is not None: + fields = request.context['s3-presign-post-fields'] + + policy = {} + conditions = [] + if request.context.get('s3-presign-post-policy', None) is not None: + policy = request.context['s3-presign-post-policy'] + if policy.get('conditions', None) is not None: + conditions = policy['conditions'] + + policy['conditions'] = conditions + + fields['x-amz-algorithm'] = 'AWS4-HMAC-SHA256' + fields['x-amz-credential'] = self.scope(request) + fields['x-amz-date'] = request.context['timestamp'] + + conditions.append({'x-amz-algorithm': 'AWS4-HMAC-SHA256'}) + conditions.append({'x-amz-credential': self.scope(request)}) + conditions.append({'x-amz-date': request.context['timestamp']}) + + if self.credentials.token is not None: + fields['x-amz-security-token'] = self.credentials.token + conditions.append({'x-amz-security-token': self.credentials.token}) + + # Dump the base64 encoded policy into the fields dictionary. + fields['policy'] = base64.b64encode( + json.dumps(policy).encode('utf-8') + ).decode('utf-8') + + fields['x-amz-signature'] = self.signature(fields['policy'], request) + + request.context['s3-presign-post-fields'] = fields + request.context['s3-presign-post-policy'] = policy + + +class HmacV1Auth(BaseSigner): + # List of Query String Arguments of Interest + QSAOfInterest = [ + 'accelerate', + 'acl', + 'cors', + 'defaultObjectAcl', + 'location', + 'logging', + 'partNumber', + 'policy', + 'requestPayment', + 'torrent', + 'versioning', + 'versionId', + 'versions', + 'website', + 'uploads', + 'uploadId', + 'response-content-type', + 'response-content-language', + 'response-expires', + 'response-cache-control', + 'response-content-disposition', + 'response-content-encoding', + 'delete', + 'lifecycle', + 'tagging', + 'restore', + 'storageClass', + 'notification', + 'replication', + 'requestPayment', + 'analytics', + 'metrics', + 'inventory', + 'select', + 'select-type', + 'object-lock', + ] + + def __init__(self, credentials, service_name=None, region_name=None): + self.credentials = credentials + + def sign_string(self, string_to_sign): + new_hmac = hmac.new( + self.credentials.secret_key.encode('utf-8'), digestmod=sha1 + ) + new_hmac.update(string_to_sign.encode('utf-8')) + return encodebytes(new_hmac.digest()).strip().decode('utf-8') + + def canonical_standard_headers(self, headers): + interesting_headers = ['content-md5', 'content-type', 'date'] + hoi = [] + if 'Date' in headers: + del headers['Date'] + headers['Date'] = self._get_date() + for ih in interesting_headers: + found = False + for key in headers: + lk = key.lower() + if headers[key] is not None and lk == ih: + hoi.append(headers[key].strip()) + found = True + if not found: + hoi.append('') + return '\n'.join(hoi) + + def canonical_custom_headers(self, headers): + hoi = [] + custom_headers = {} + for key in headers: + lk = key.lower() + if headers[key] is not None: + if lk.startswith('x-amz-'): + custom_headers[lk] = ','.join( + v.strip() for v in headers.get_all(key) + ) + sorted_header_keys = sorted(custom_headers.keys()) + for key in sorted_header_keys: + hoi.append(f"{key}:{custom_headers[key]}") + return '\n'.join(hoi) + + def unquote_v(self, nv): + """ + TODO: Do we need this? + """ + if len(nv) == 1: + return nv + else: + return (nv[0], unquote(nv[1])) + + def canonical_resource(self, split, auth_path=None): + # don't include anything after the first ? in the resource... + # unless it is one of the QSA of interest, defined above + # NOTE: + # The path in the canonical resource should always be the + # full path including the bucket name, even for virtual-hosting + # style addressing. The ``auth_path`` keeps track of the full + # path for the canonical resource and would be passed in if + # the client was using virtual-hosting style. + if auth_path is not None: + buf = auth_path + else: + buf = split.path + if split.query: + qsa = split.query.split('&') + qsa = [a.split('=', 1) for a in qsa] + qsa = [ + self.unquote_v(a) for a in qsa if a[0] in self.QSAOfInterest + ] + if len(qsa) > 0: + qsa.sort(key=itemgetter(0)) + qsa = ['='.join(a) for a in qsa] + buf += '?' + buf += '&'.join(qsa) + return buf + + def canonical_string( + self, method, split, headers, expires=None, auth_path=None + ): + cs = method.upper() + '\n' + cs += self.canonical_standard_headers(headers) + '\n' + custom_headers = self.canonical_custom_headers(headers) + if custom_headers: + cs += custom_headers + '\n' + cs += self.canonical_resource(split, auth_path=auth_path) + return cs + + def get_signature( + self, method, split, headers, expires=None, auth_path=None + ): + if self.credentials.token: + del headers['x-amz-security-token'] + headers['x-amz-security-token'] = self.credentials.token + string_to_sign = self.canonical_string( + method, split, headers, auth_path=auth_path + ) + logger.debug('StringToSign:\n%s', string_to_sign) + return self.sign_string(string_to_sign) + + def add_auth(self, request): + if self.credentials is None: + raise NoCredentialsError + logger.debug("Calculating signature using hmacv1 auth.") + split = urlsplit(request.url) + logger.debug("HTTP request method: %s", request.method) + signature = self.get_signature( + request.method, split, request.headers, auth_path=request.auth_path + ) + self._inject_signature(request, signature) + + def _get_date(self): + return formatdate(usegmt=True) + + def _inject_signature(self, request, signature): + if 'Authorization' in request.headers: + # We have to do this because request.headers is not + # normal dictionary. It has the (unintuitive) behavior + # of aggregating repeated setattr calls for the same + # key value. For example: + # headers['foo'] = 'a'; headers['foo'] = 'b' + # list(headers) will print ['foo', 'foo']. + del request.headers['Authorization'] + + auth_header = f"AWS {self.credentials.access_key}:{signature}" + request.headers['Authorization'] = auth_header + + +class HmacV1QueryAuth(HmacV1Auth): + """ + Generates a presigned request for s3. + + Spec from this document: + + http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html + #RESTAuthenticationQueryStringAuth + + """ + + DEFAULT_EXPIRES = 3600 + + def __init__(self, credentials, expires=DEFAULT_EXPIRES): + self.credentials = credentials + self._expires = expires + + def _get_date(self): + return str(int(time.time() + int(self._expires))) + + def _inject_signature(self, request, signature): + query_dict = {} + query_dict['AWSAccessKeyId'] = self.credentials.access_key + query_dict['Signature'] = signature + + for header_key in request.headers: + lk = header_key.lower() + # For query string requests, Expires is used instead of the + # Date header. + if header_key == 'Date': + query_dict['Expires'] = request.headers['Date'] + # We only want to include relevant headers in the query string. + # These can be anything that starts with x-amz, is Content-MD5, + # or is Content-Type. + elif lk.startswith('x-amz-') or lk in ( + 'content-md5', + 'content-type', + ): + query_dict[lk] = request.headers[lk] + # Combine all of the identified headers into an encoded + # query string + new_query_string = percent_encode_sequence(query_dict) + + # Create a new url with the presigned url. + p = urlsplit(request.url) + if p[3]: + # If there was a pre-existing query string, we should + # add that back before injecting the new query string. + new_query_string = f'{p[3]}&{new_query_string}' + new_url_parts = (p[0], p[1], p[2], new_query_string, p[4]) + request.url = urlunsplit(new_url_parts) + + +class HmacV1PostAuth(HmacV1Auth): + """ + Generates a presigned post for s3. + + Spec from this document: + + http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingHTTPPOST.html + """ + + def add_auth(self, request): + fields = {} + if request.context.get('s3-presign-post-fields', None) is not None: + fields = request.context['s3-presign-post-fields'] + + policy = {} + conditions = [] + if request.context.get('s3-presign-post-policy', None) is not None: + policy = request.context['s3-presign-post-policy'] + if policy.get('conditions', None) is not None: + conditions = policy['conditions'] + + policy['conditions'] = conditions + + fields['AWSAccessKeyId'] = self.credentials.access_key + + if self.credentials.token is not None: + fields['x-amz-security-token'] = self.credentials.token + conditions.append({'x-amz-security-token': self.credentials.token}) + + # Dump the base64 encoded policy into the fields dictionary. + fields['policy'] = base64.b64encode( + json.dumps(policy).encode('utf-8') + ).decode('utf-8') + + fields['signature'] = self.sign_string(fields['policy']) + + request.context['s3-presign-post-fields'] = fields + request.context['s3-presign-post-policy'] = policy + + +class BearerAuth(TokenSigner): + """ + Performs bearer token authorization by placing the bearer token in the + Authorization header as specified by Section 2.1 of RFC 6750. + + https://datatracker.ietf.org/doc/html/rfc6750#section-2.1 + """ + + def add_auth(self, request): + if self.auth_token is None: + raise NoAuthTokenError() + + auth_header = f'Bearer {self.auth_token.token}' + if 'Authorization' in request.headers: + del request.headers['Authorization'] + request.headers['Authorization'] = auth_header + + +def resolve_auth_type(auth_trait): + for auth_type in auth_trait: + if auth_type == 'smithy.api#noAuth': + return AUTH_TYPE_TO_SIGNATURE_VERSION[auth_type] + elif auth_type in AUTH_TYPE_TO_SIGNATURE_VERSION: + signature_version = AUTH_TYPE_TO_SIGNATURE_VERSION[auth_type] + if signature_version in AUTH_TYPE_MAPS: + return signature_version + else: + raise UnknownSignatureVersionError(signature_version=auth_type) + raise UnsupportedSignatureVersionError(signature_version=auth_trait) + + +def resolve_auth_scheme_preference(preference_list, auth_options): + service_supported = [scheme.split('#')[-1] for scheme in auth_options] + + unsupported = [ + scheme + for scheme in preference_list + if scheme not in AUTH_PREF_TO_SIGNATURE_VERSION + ] + if unsupported: + logger.debug( + "Unsupported auth schemes in preference list: %r", unsupported + ) + + combined = preference_list + service_supported + prioritized_schemes = [ + scheme + for scheme in dict.fromkeys(combined) + if scheme in service_supported + ] + + for scheme in prioritized_schemes: + if scheme == 'noAuth': + return AUTH_PREF_TO_SIGNATURE_VERSION[scheme] + sig_version = AUTH_PREF_TO_SIGNATURE_VERSION.get(scheme) + if sig_version in AUTH_TYPE_MAPS: + return sig_version + + raise UnsupportedSignatureVersionError( + signature_version=', '.join(sorted(service_supported)) + ) + + +AUTH_TYPE_MAPS = { + 'v2': SigV2Auth, + 'v3': SigV3Auth, + 'v3https': SigV3Auth, + 's3': HmacV1Auth, + 's3-query': HmacV1QueryAuth, + 's3-presign-post': HmacV1PostAuth, + 's3v4-presign-post': S3SigV4PostAuth, + 'v4-s3express': S3ExpressAuth, + 'v4-s3express-query': S3ExpressQueryAuth, + 'v4-s3express-presign-post': S3ExpressPostAuth, + 'bearer': BearerAuth, +} + +# Define v4 signers depending on if CRT is present +if HAS_CRT: + from botocore.crt.auth import CRT_AUTH_TYPE_MAPS + + AUTH_TYPE_MAPS.update(CRT_AUTH_TYPE_MAPS) +else: + AUTH_TYPE_MAPS.update( + { + 'v4': SigV4Auth, + 'v4-query': SigV4QueryAuth, + 's3v4': S3SigV4Auth, + 's3v4-query': S3SigV4QueryAuth, + } + ) + +AUTH_TYPE_TO_SIGNATURE_VERSION = { + 'aws.auth#sigv4': 'v4', + 'aws.auth#sigv4a': 'v4a', + 'smithy.api#httpBearerAuth': 'bearer', + 'smithy.api#noAuth': 'none', +} + +# Mapping used specifically for resolving user-configured auth scheme preferences. +# This is similar to AUTH_TYPE_TO_SIGNATURE_VERSION, but uses simplified keys by +# stripping the auth trait prefixes ('smithy.api#httpBearerAuth' → 'httpBearerAuth'). +# These simplified keys match what customers are expected to provide in configuration. +AUTH_PREF_TO_SIGNATURE_VERSION = { + auth_scheme.split('#')[-1]: sig_version + for auth_scheme, sig_version in AUTH_TYPE_TO_SIGNATURE_VERSION.items() +} diff --git a/venv/lib/python3.12/site-packages/botocore/awsrequest.py b/venv/lib/python3.12/site-packages/botocore/awsrequest.py new file mode 100644 index 0000000..ed17b4e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/awsrequest.py @@ -0,0 +1,642 @@ +# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import functools +import logging +from collections.abc import Mapping + +import urllib3.util +from urllib3.connection import HTTPConnection, VerifiedHTTPSConnection +from urllib3.connectionpool import HTTPConnectionPool, HTTPSConnectionPool + +import botocore.utils +from botocore.compat import ( + HTTPHeaders, + HTTPResponse, + MutableMapping, + urlencode, + urlparse, + urlsplit, + urlunsplit, +) +from botocore.exceptions import UnseekableStreamError + +logger = logging.getLogger(__name__) + + +class AWSHTTPResponse(HTTPResponse): + # The *args, **kwargs is used because the args are slightly + # different in py2.6 than in py2.7/py3. + def __init__(self, *args, **kwargs): + self._status_tuple = kwargs.pop('status_tuple') + HTTPResponse.__init__(self, *args, **kwargs) + + def _read_status(self): + if self._status_tuple is not None: + status_tuple = self._status_tuple + self._status_tuple = None + return status_tuple + else: + return HTTPResponse._read_status(self) + + +class AWSConnection: + """Mixin for HTTPConnection that supports Expect 100-continue. + + This when mixed with a subclass of httplib.HTTPConnection (though + technically we subclass from urllib3, which subclasses + httplib.HTTPConnection) and we only override this class to support Expect + 100-continue, which we need for S3. As far as I can tell, this is + general purpose enough to not be specific to S3, but I'm being + tentative and keeping it in botocore because I've only tested + this against AWS services. + + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._original_response_cls = self.response_class + # This variable is set when we receive an early response from the + # server. If this value is set to True, any calls to send() are noops. + # This value is reset to false every time _send_request is called. + # This is to workaround changes in urllib3 2.0 which uses separate + # send() calls in request() instead of delegating to endheaders(), + # which is where the body is sent in CPython's HTTPConnection. + self._response_received = False + self._expect_header_set = False + self._send_called = False + + def close(self): + super().close() + # Reset all of our instance state we were tracking. + self._response_received = False + self._expect_header_set = False + self._send_called = False + self.response_class = self._original_response_cls + + def request(self, method, url, body=None, headers=None, *args, **kwargs): + if headers is None: + headers = {} + self._response_received = False + self.response_class = self._original_response_cls + if headers.get('Expect', b'') == b'100-continue': + self._expect_header_set = True + else: + self._expect_header_set = False + rval = super().request(method, url, body, headers, *args, **kwargs) + self._expect_header_set = False + return rval + + def _convert_to_bytes(self, mixed_buffer): + # Take a list of mixed str/bytes and convert it + # all into a single bytestring. + # Any str will be encoded as utf-8. + bytes_buffer = [] + for chunk in mixed_buffer: + if isinstance(chunk, str): + bytes_buffer.append(chunk.encode('utf-8')) + else: + bytes_buffer.append(chunk) + msg = b"\r\n".join(bytes_buffer) + return msg + + def _send_output(self, message_body=None, *args, **kwargs): + self._buffer.extend((b"", b"")) + msg = self._convert_to_bytes(self._buffer) + del self._buffer[:] + # If msg and message_body are sent in a single send() call, + # it will avoid performance problems caused by the interaction + # between delayed ack and the Nagle algorithm. + if isinstance(message_body, bytes): + msg += message_body + message_body = None + self.send(msg) + if self._expect_header_set: + # This is our custom behavior. If the Expect header was + # set, it will trigger this custom behavior. + logger.debug("Waiting for 100 Continue response.") + # Wait for 1 second for the server to send a response. + if urllib3.util.wait_for_read(self.sock, 1): + self._handle_expect_response(message_body) + return + else: + # From the RFC: + # Because of the presence of older implementations, the + # protocol allows ambiguous situations in which a client may + # send "Expect: 100-continue" without receiving either a 417 + # (Expectation Failed) status or a 100 (Continue) status. + # Therefore, when a client sends this header field to an origin + # server (possibly via a proxy) from which it has never seen a + # 100 (Continue) status, the client SHOULD NOT wait for an + # indefinite period before sending the request body. + logger.debug( + "No response seen from server, continuing to " + "send the response body." + ) + if message_body is not None: + # message_body was not a string (i.e. it is a file), and + # we must run the risk of Nagle. + self.send(message_body) + + def _consume_headers(self, fp): + # Most servers (including S3) will just return + # the CLRF after the 100 continue response. However, + # some servers (I've specifically seen this for squid when + # used as a straight HTTP proxy) will also inject a + # Connection: keep-alive header. To account for this + # we'll read until we read '\r\n', and ignore any headers + # that come immediately after the 100 continue response. + current = None + while current != b'\r\n': + current = fp.readline() + + def _handle_expect_response(self, message_body): + # This is called when we sent the request headers containing + # an Expect: 100-continue header and received a response. + # We now need to figure out what to do. + fp = self.sock.makefile('rb', 0) + try: + maybe_status_line = fp.readline() + parts = maybe_status_line.split(None, 2) + if self._is_100_continue_status(maybe_status_line): + self._consume_headers(fp) + logger.debug( + "100 Continue response seen, now sending request body." + ) + self._send_message_body(message_body) + elif len(parts) == 3 and parts[0].startswith(b'HTTP/'): + # From the RFC: + # Requirements for HTTP/1.1 origin servers: + # + # - Upon receiving a request which includes an Expect + # request-header field with the "100-continue" + # expectation, an origin server MUST either respond with + # 100 (Continue) status and continue to read from the + # input stream, or respond with a final status code. + # + # So if we don't get a 100 Continue response, then + # whatever the server has sent back is the final response + # and don't send the message_body. + logger.debug( + "Received a non 100 Continue response " + "from the server, NOT sending request body." + ) + status_tuple = ( + parts[0].decode('ascii'), + int(parts[1]), + parts[2].decode('ascii'), + ) + response_class = functools.partial( + AWSHTTPResponse, status_tuple=status_tuple + ) + self.response_class = response_class + self._response_received = True + finally: + fp.close() + + def _send_message_body(self, message_body): + if message_body is not None: + self.send(message_body) + + def send(self, str): + if self._response_received: + if not self._send_called: + # urllib3 2.0 chunks and calls send potentially + # thousands of times inside `request` unlike the + # standard library. Only log this once for sanity. + logger.debug( + "send() called, but response already received. " + "Not sending data." + ) + self._send_called = True + return + return super().send(str) + + def _is_100_continue_status(self, maybe_status_line): + parts = maybe_status_line.split(None, 2) + # Check for HTTP/ 100 Continue\r\n or HTTP/ 100\r\n + return ( + len(parts) >= 2 + and parts[0].startswith(b'HTTP/') + and parts[1] == b'100' + ) + + +class AWSHTTPConnection(AWSConnection, HTTPConnection): + """An HTTPConnection that supports 100 Continue behavior.""" + + +class AWSHTTPSConnection(AWSConnection, VerifiedHTTPSConnection): + """An HTTPSConnection that supports 100 Continue behavior.""" + + +class AWSHTTPConnectionPool(HTTPConnectionPool): + ConnectionCls = AWSHTTPConnection + + +class AWSHTTPSConnectionPool(HTTPSConnectionPool): + ConnectionCls = AWSHTTPSConnection + + +def prepare_request_dict( + request_dict, endpoint_url, context=None, user_agent=None +): + """ + This method prepares a request dict to be created into an + AWSRequestObject. This prepares the request dict by adding the + url and the user agent to the request dict. + + :type request_dict: dict + :param request_dict: The request dict (created from the + ``serialize`` module). + + :type user_agent: string + :param user_agent: The user agent to use for this request. + + :type endpoint_url: string + :param endpoint_url: The full endpoint url, which contains at least + the scheme, the hostname, and optionally any path components. + """ + r = request_dict + if user_agent is not None: + headers = r['headers'] + headers['User-Agent'] = user_agent + host_prefix = r.get('host_prefix') + url = _urljoin(endpoint_url, r['url_path'], host_prefix) + if r['query_string']: + # NOTE: This is to avoid circular import with utils. This is being + # done to avoid moving classes to different modules as to not cause + # breaking chainges. + percent_encode_sequence = botocore.utils.percent_encode_sequence + encoded_query_string = percent_encode_sequence(r['query_string']) + if '?' not in url: + url += f'?{encoded_query_string}' + else: + url += f'&{encoded_query_string}' + r['url'] = url + r['context'] = context + if context is None: + r['context'] = {} + + +def create_request_object(request_dict): + """ + This method takes a request dict and creates an AWSRequest object + from it. + + :type request_dict: dict + :param request_dict: The request dict (created from the + ``prepare_request_dict`` method). + + :rtype: ``botocore.awsrequest.AWSRequest`` + :return: An AWSRequest object based on the request_dict. + + """ + r = request_dict + request_object = AWSRequest( + method=r['method'], + url=r['url'], + data=r['body'], + headers=r['headers'], + auth_path=r.get('auth_path'), + ) + request_object.context = r['context'] + return request_object + + +def _urljoin(endpoint_url, url_path, host_prefix): + p = urlsplit(endpoint_url) + # - + # scheme - p[0] + # netloc - p[1] + # path - p[2] + # query - p[3] + # fragment - p[4] + if not url_path or url_path == '/': + # If there's no path component, ensure the URL ends with + # a '/' for backwards compatibility. + if not p[2]: + new_path = '/' + else: + new_path = p[2] + elif p[2].endswith('/') and url_path.startswith('/'): + new_path = p[2][:-1] + url_path + else: + new_path = p[2] + url_path + + new_netloc = p[1] + if host_prefix is not None: + new_netloc = host_prefix + new_netloc + + reconstructed = urlunsplit((p[0], new_netloc, new_path, p[3], p[4])) + return reconstructed + + +class AWSRequestPreparer: + """ + This class performs preparation on AWSRequest objects similar to that of + the PreparedRequest class does in the requests library. However, the logic + has been boiled down to meet the specific use cases in botocore. Of note + there are the following differences: + This class does not heavily prepare the URL. Requests performed many + validations and corrections to ensure the URL is properly formatted. + Botocore either performs these validations elsewhere or otherwise + consistently provides well formatted URLs. + + This class does not heavily prepare the body. Body preperation is + simple and supports only the cases that we document: bytes and + file-like objects to determine the content-length. This will also + additionally prepare a body that is a dict to be url encoded params + string as some signers rely on this. Finally, this class does not + support multipart file uploads. + + This class does not prepare the method, auth or cookies. + """ + + def prepare(self, original): + method = original.method + url = self._prepare_url(original) + body = self._prepare_body(original) + headers = self._prepare_headers(original, body) + stream_output = original.stream_output + context = original.context + + return AWSPreparedRequest( + method, url, headers, body, stream_output, context + ) + + def _prepare_url(self, original): + url = original.url + if original.params: + url_parts = urlparse(url) + delim = '&' if url_parts.query else '?' + if isinstance(original.params, Mapping): + params_to_encode = list(original.params.items()) + else: + params_to_encode = original.params + params = urlencode(params_to_encode, doseq=True) + url = delim.join((url, params)) + return url + + def _prepare_headers(self, original, prepared_body=None): + headers = HeadersDict(original.headers.items()) + + # If the transfer encoding or content length is already set, use that + if 'Transfer-Encoding' in headers or 'Content-Length' in headers: + return headers + + # Ensure we set the content length when it is expected + if original.method not in ('GET', 'HEAD', 'OPTIONS'): + length = self._determine_content_length(prepared_body) + if length is not None: + headers['Content-Length'] = str(length) + else: + # Failed to determine content length, using chunked + # NOTE: This shouldn't ever happen in practice + body_type = type(prepared_body) + logger.debug('Failed to determine length of %s', body_type) + headers['Transfer-Encoding'] = 'chunked' + + return headers + + def _to_utf8(self, item): + key, value = item + if isinstance(key, str): + key = key.encode('utf-8') + if isinstance(value, str): + value = value.encode('utf-8') + return key, value + + def _prepare_body(self, original): + """Prepares the given HTTP body data.""" + body = original.data + if body == b'': + body = None + + if isinstance(body, dict): + params = [self._to_utf8(item) for item in body.items()] + body = urlencode(params, doseq=True) + + return body + + def _determine_content_length(self, body): + return botocore.utils.determine_content_length(body) + + +class AWSRequest: + """Represents the elements of an HTTP request. + + This class was originally inspired by requests.models.Request, but has been + boiled down to meet the specific use cases in botocore. That being said this + class (even in requests) is effectively a named-tuple. + """ + + _REQUEST_PREPARER_CLS = AWSRequestPreparer + + def __init__( + self, + method=None, + url=None, + headers=None, + data=None, + params=None, + auth_path=None, + stream_output=False, + ): + self._request_preparer = self._REQUEST_PREPARER_CLS() + + # Default empty dicts for dict params. + params = {} if params is None else params + + self.method = method + self.url = url + self.headers = HTTPHeaders() + self.data = data + self.params = params + self.auth_path = auth_path + self.stream_output = stream_output + + if headers is not None: + for key, value in headers.items(): + self.headers[key] = value + + # This is a dictionary to hold information that is used when + # processing the request. What is inside of ``context`` is open-ended. + # For example, it may have a timestamp key that is used for holding + # what the timestamp is when signing the request. Note that none + # of the information that is inside of ``context`` is directly + # sent over the wire; the information is only used to assist in + # creating what is sent over the wire. + self.context = {} + + def prepare(self): + """Constructs a :class:`AWSPreparedRequest `.""" + return self._request_preparer.prepare(self) + + @property + def body(self): + body = self.prepare().body + if isinstance(body, str): + body = body.encode('utf-8') + return body + + +class AWSPreparedRequest: + """A data class representing a finalized request to be sent over the wire. + + Requests at this stage should be treated as final, and the properties of + the request should not be modified. + + :ivar method: The HTTP Method + :ivar url: The full url + :ivar headers: The HTTP headers to send. + :ivar body: The HTTP body. + :ivar stream_output: If the response for this request should be streamed. + :ivar context: The request context from the originating ``AWSRequest``. + """ + + def __init__( + self, method, url, headers, body, stream_output, context=None + ): + self.method = method + self.url = url + self.headers = headers + self.body = body + self.stream_output = stream_output + self.context = context + + def __repr__(self): + fmt = ( + '' + ) + return fmt % (self.stream_output, self.method, self.url, self.headers) + + def reset_stream(self): + """Resets the streaming body to it's initial position. + + If the request contains a streaming body (a streamable file-like object) + seek to the object's initial position to ensure the entire contents of + the object is sent. This is a no-op for static bytes-like body types. + """ + # Trying to reset a stream when there is a no stream will + # just immediately return. It's not an error, it will produce + # the same result as if we had actually reset the stream (we'll send + # the entire body contents again if we need to). + # Same case if the body is a string/bytes/bytearray type. + + non_seekable_types = (bytes, str, bytearray) + if self.body is None or isinstance(self.body, non_seekable_types): + return + try: + logger.debug("Rewinding stream: %s", self.body) + self.body.seek(0) + except Exception as e: + logger.debug("Unable to rewind stream: %s", e) + raise UnseekableStreamError(stream_object=self.body) + + +class AWSResponse: + """A data class representing an HTTP response. + + This class was originally inspired by requests.models.Response, but has + been boiled down to meet the specific use cases in botocore. This has + effectively been reduced to a named tuple. + + :ivar url: The full url. + :ivar status_code: The status code of the HTTP response. + :ivar headers: The HTTP headers received. + :ivar body: The HTTP response body. + """ + + def __init__(self, url, status_code, headers, raw): + self.url = url + self.status_code = status_code + self.headers = HeadersDict(headers) + self.raw = raw + + self._content = None + + @property + def content(self): + """Content of the response as bytes.""" + + if self._content is None: + # Read the contents. + # NOTE: requests would attempt to call stream and fall back + # to a custom generator that would call read in a loop, but + # we don't rely on this behavior + self._content = b''.join(self.raw.stream()) or b'' + + return self._content + + @property + def text(self): + """Content of the response as a proper text type. + + Uses the encoding type provided in the reponse headers to decode the + response content into a proper text type. If the encoding is not + present in the headers, UTF-8 is used as a default. + """ + encoding = botocore.utils.get_encoding_from_headers(self.headers) + if encoding: + return self.content.decode(encoding) + else: + return self.content.decode('utf-8') + + +class _HeaderKey: + def __init__(self, key): + self._key = key + self._lower = key.lower() + + def __hash__(self): + return hash(self._lower) + + def __eq__(self, other): + return isinstance(other, _HeaderKey) and self._lower == other._lower + + def __str__(self): + return self._key + + def __repr__(self): + return repr(self._key) + + +class HeadersDict(MutableMapping): + """A case-insenseitive dictionary to represent HTTP headers.""" + + def __init__(self, *args, **kwargs): + self._dict = {} + self.update(*args, **kwargs) + + def __setitem__(self, key, value): + self._dict[_HeaderKey(key)] = value + + def __getitem__(self, key): + return self._dict[_HeaderKey(key)] + + def __delitem__(self, key): + del self._dict[_HeaderKey(key)] + + def __iter__(self): + return (str(key) for key in self._dict) + + def __len__(self): + return len(self._dict) + + def __repr__(self): + return repr(self._dict) + + def copy(self): + return HeadersDict(self.items()) diff --git a/venv/lib/python3.12/site-packages/botocore/cacert.pem b/venv/lib/python3.12/site-packages/botocore/cacert.pem new file mode 100644 index 0000000..68d8993 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/cacert.pem @@ -0,0 +1,4251 @@ + +# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Label: "GlobalSign Root CA" +# Serial: 4835703278459707669005204 +# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a +# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c +# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99 +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Label: "GlobalSign Root CA - R2" +# Serial: 4835703278459682885658125 +# MD5 Fingerprint: 94:14:77:7e:3e:5e:fd:8f:30:bd:41:b0:cf:e7:d0:30 +# SHA1 Fingerprint: 75:e0:ab:b6:13:85:12:27:1c:04:f8:5f:dd:de:38:e4:b7:24:2e:fe +# SHA256 Fingerprint: ca:42:dd:41:74:5f:d0:b8:1e:b9:02:36:2c:f9:d8:bf:71:9d:a1:bd:1b:1e:fc:94:6f:5b:4c:99:f4:2c:1b:9e +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Label: "Verisign Class 3 Public Primary Certification Authority - G3" +# Serial: 206684696279472310254277870180966723415 +# MD5 Fingerprint: cd:68:b6:a7:c7:c4:ce:75:e0:1d:4f:57:44:61:92:09 +# SHA1 Fingerprint: 13:2d:0d:45:53:4b:69:97:cd:b2:d5:c3:39:e2:55:76:60:9b:5c:c6 +# SHA256 Fingerprint: eb:04:cf:5e:b1:f3:9a:fa:76:2f:2b:b1:20:f2:96:cb:a5:20:c1:b9:7d:b1:58:95:65:b8:1c:b9:a1:7b:72:44 +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b +N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t +KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu +kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm +CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ +Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu +imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te +2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe +DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p +F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt +TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Premium 2048 Secure Server CA" +# Serial: 946069240 +# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90 +# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31 +# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77 +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Label: "Baltimore CyberTrust Root" +# Serial: 33554617 +# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4 +# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74 +# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Label: "AddTrust External Root" +# Serial: 1 +# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f +# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68 +# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2 +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs +IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 +MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h +bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt +H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 +uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX +mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX +a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN +E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 +WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD +VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 +Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU +cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx +IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN +AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH +YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC +Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX +c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a +mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Label: "Entrust Root Certification Authority" +# Serial: 1164660820 +# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 +# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 +# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Global CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Global CA O=GeoTrust Inc. +# Label: "GeoTrust Global CA" +# Serial: 144470 +# MD5 Fingerprint: f7:75:ab:29:fb:51:4e:b7:77:5e:ff:05:3c:99:8e:f5 +# SHA1 Fingerprint: de:28:f4:a4:ff:e5:b9:2f:a3:c5:03:d1:a3:49:a7:f9:96:2a:82:12 +# SHA256 Fingerprint: ff:85:6a:2d:25:1d:cd:88:d3:66:56:f4:50:12:67:98:cf:ab:aa:de:40:79:9c:72:2d:e4:d2:b5:db:36:a7:3a +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Label: "GeoTrust Universal CA" +# Serial: 1 +# MD5 Fingerprint: 92:65:58:8b:a2:1a:31:72:73:68:5c:b4:a5:7a:07:48 +# SHA1 Fingerprint: e6:21:f3:35:43:79:05:9a:4b:68:30:9d:8a:2f:74:22:15:87:ec:79 +# SHA256 Fingerprint: a0:45:9b:9f:63:b2:25:59:f5:fa:5d:4c:6d:b3:f9:f7:2f:f1:93:42:03:35:78:f0:73:bf:1d:1b:46:cb:b9:12 +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy +c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 +IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV +VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 +cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT +QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh +F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v +c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w +mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd +VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX +teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ +f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe +Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ +nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY +MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX +IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn +ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z +uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN +Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja +QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW +koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 +ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt +DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm +bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Label: "GeoTrust Universal CA 2" +# Serial: 1 +# MD5 Fingerprint: 34:fc:b8:d0:36:db:9e:14:b3:c2:f2:db:8f:e4:94:c7 +# SHA1 Fingerprint: 37:9a:19:7b:41:85:45:35:0c:a6:03:69:f3:3c:2e:af:47:4f:20:79 +# SHA256 Fingerprint: a0:23:4f:3b:c8:52:7c:a5:62:8e:ec:81:ad:5d:69:89:5d:a5:68:0d:c9:1d:1c:b8:47:7f:33:f8:78:b9:5b:0b +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy +c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD +VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1 +c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81 +WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG +FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq +XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL +se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb +KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd +IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73 +y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt +hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc +QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4 +Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV +HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ +KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ +L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr +Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo +ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY +T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz +GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m +1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV +OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH +6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX +QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +# Issuer: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association +# Subject: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association +# Label: "Visa eCommerce Root" +# Serial: 25952180776285836048024890241505565794 +# MD5 Fingerprint: fc:11:b8:d8:08:93:30:00:6d:23:f9:7e:eb:52:1e:02 +# SHA1 Fingerprint: 70:17:9b:86:8c:00:a4:fa:60:91:52:22:3f:9f:3e:32:bd:e0:05:62 +# SHA256 Fingerprint: 69:fa:c9:bd:55:fb:0a:c7:8d:53:bb:ee:5c:f1:d5:97:98:9f:d0:aa:ab:20:a2:51:51:bd:f1:73:3e:e7:d1:22 +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr +MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl +cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw +CQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h +dGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l +cmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h +2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E +lpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV +ZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq +299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t +vz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL +dXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF +AAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR +zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3 +LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd +7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw +++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +# Issuer: CN=AAA Certificate Services O=Comodo CA Limited +# Subject: CN=AAA Certificate Services O=Comodo CA Limited +# Label: "Comodo AAA Services root" +# Serial: 1 +# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0 +# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49 +# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4 +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority +# Subject: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority +# Label: "QuoVadis Root CA" +# Serial: 985026699 +# MD5 Fingerprint: 27:de:36:fe:72:b7:00:03:00:9d:f4:f0:1e:6c:04:24 +# SHA1 Fingerprint: de:3f:40:bd:50:93:d3:9b:6c:60:f6:da:bc:07:62:01:00:89:76:c9 +# SHA256 Fingerprint: a4:5e:de:3b:bb:f0:9c:8a:e1:5c:72:ef:c0:72:68:d6:93:a2:1c:99:6f:d5:1e:67:ca:07:94:60:fd:6d:88:73 +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz +MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw +IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR +dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp +li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D +rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ +WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug +F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU +xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC +Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv +dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw +ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl +IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh +c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy +ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI +KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T +KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq +y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p +dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD +VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk +fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 +7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R +cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y +mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW +xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK +SnQ2+Q== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2" +# Serial: 1289 +# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b +# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7 +# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86 +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3" +# Serial: 1478 +# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf +# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85 +# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35 +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1 +# Subject: O=SECOM Trust.net OU=Security Communication RootCA1 +# Label: "Security Communication Root CA" +# Serial: 0 +# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a +# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7 +# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY +MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t +dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 +WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD +VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 +9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ +DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 +Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N +QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ +xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G +A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG +kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr +Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 +Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU +JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot +RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== +-----END CERTIFICATE----- + +# Issuer: CN=Sonera Class2 CA O=Sonera +# Subject: CN=Sonera Class2 CA O=Sonera +# Label: "Sonera Class 2 Root CA" +# Serial: 29 +# MD5 Fingerprint: a3:ec:75:0f:2e:88:df:fa:48:01:4e:0b:5c:48:6f:fb +# SHA1 Fingerprint: 37:f7:6d:e6:07:7c:90:c5:b1:3e:93:1a:b7:41:10:b4:f2:e4:9a:27 +# SHA256 Fingerprint: 79:08:b4:03:14:c1:38:10:0b:51:8d:07:35:80:7f:fb:fc:f8:51:8a:00:95:33:71:05:ba:38:6b:15:3d:d9:27 +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx +MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o +Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt +5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s +3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej +vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu +8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil +zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ +3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD +FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 +Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 +ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M +-----END CERTIFICATE----- + +# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Label: "XRamp Global CA Root" +# Serial: 107108908803651509692980124233745014957 +# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1 +# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6 +# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2 +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Label: "Go Daddy Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67 +# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4 +# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4 +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Label: "Starfield Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24 +# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a +# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58 +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +# Issuer: O=Government Root Certification Authority +# Subject: O=Government Root Certification Authority +# Label: "Taiwan GRCA" +# Serial: 42023070807708724159991140556527066870 +# MD5 Fingerprint: 37:85:44:53:32:45:1f:20:f0:f3:95:e1:25:c4:43:4e +# SHA1 Fingerprint: f4:8b:11:bf:de:ab:be:94:54:20:71:e6:41:de:6b:be:88:2b:40:b9 +# SHA256 Fingerprint: 76:00:29:5e:ef:e8:5b:9e:1f:d6:24:db:76:06:2a:aa:ae:59:81:8a:54:d2:77:4c:d4:c0:b2:c0:11:31:e1:b3 +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/ +MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow +PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR +IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q +gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy +yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts +F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2 +jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx +ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC +VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK +YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH +EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN +Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud +DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE +MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK +UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf +qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK +ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE +JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7 +hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1 +EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm +nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX +udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz +ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe +LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl +pYYsfPQS +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root CA" +# Serial: 17154717934120587862167794914071425081 +# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 +# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 +# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root CA" +# Serial: 10944719598952040374951832963794454346 +# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e +# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 +# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert High Assurance EV Root CA" +# Serial: 3553400076410547919724730734378100087 +# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a +# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 +# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +# Issuer: CN=Class 2 Primary CA O=Certplus +# Subject: CN=Class 2 Primary CA O=Certplus +# Label: "Certplus Class 2 Primary CA" +# Serial: 177770208045934040241468760488327595043 +# MD5 Fingerprint: 88:2c:8c:52:b8:a2:3c:f3:f7:bb:03:ea:ae:ac:42:0b +# SHA1 Fingerprint: 74:20:74:41:72:9c:dd:92:ec:79:31:d8:23:10:8d:c2:81:92:e2:bb +# SHA256 Fingerprint: 0f:99:3c:8a:ef:97:ba:af:56:87:14:0e:d5:9a:d1:82:1b:b4:af:ac:f0:aa:9a:58:b5:d5:7a:33:8a:3a:fb:cb +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw +PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz +cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9 +MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz +IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ +ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR +VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL +kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd +EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas +H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0 +HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud +DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4 +QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu +Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/ +AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8 +yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR +FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA +ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB +kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +# Issuer: CN=DST Root CA X3 O=Digital Signature Trust Co. +# Subject: CN=DST Root CA X3 O=Digital Signature Trust Co. +# Label: "DST Root CA X3" +# Serial: 91299735575339953335919266965803778155 +# MD5 Fingerprint: 41:03:52:dc:0f:f7:50:1b:16:f0:02:8e:ba:6f:45:c5 +# SHA1 Fingerprint: da:c9:02:4f:54:d8:f6:df:94:93:5f:b1:73:26:38:ca:6a:d7:7c:13 +# SHA256 Fingerprint: 06:87:26:03:31:a7:24:03:d9:09:f1:05:e6:9b:cf:0d:32:e1:bd:24:93:ff:c6:d9:20:6d:11:bc:d6:77:07:39 +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow +PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD +Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O +rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq +OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b +xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw +7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD +aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG +SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 +ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr +AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz +R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 +JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo +Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Label: "SwissSign Gold CA - G2" +# Serial: 13492815561806991280 +# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93 +# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61 +# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95 +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln +biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF +MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT +d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 +76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ +bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c +6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE +emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd +MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt +MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y +MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y +FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi +aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM +gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB +qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 +lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn +8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 +45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO +UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 +O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC +bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv +GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a +77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC +hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 +92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp +Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w +ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt +Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Label: "SwissSign Silver CA - G2" +# Serial: 5700383053117599563 +# MD5 Fingerprint: e0:06:a1:c9:7d:cf:c9:fc:0d:c0:56:75:96:d8:62:13 +# SHA1 Fingerprint: 9b:aa:e5:9f:56:ee:21:cb:43:5a:be:25:93:df:a7:f0:40:d1:1d:cb +# SHA256 Fingerprint: be:6c:4d:a2:bb:b9:ba:59:b6:f3:93:97:68:37:42:46:c3:c0:05:99:3f:a9:8f:02:0d:1d:ed:be:d4:8a:81:d5 +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE +BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu +IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow +RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY +U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv +Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br +YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF +nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH +6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt +eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ +c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ +MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH +HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf +jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 +5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB +rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c +wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB +AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp +WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 +xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ +2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ +IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 +aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X +em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR +dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ +OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ +hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy +tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Subject: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Label: "GeoTrust Primary Certification Authority" +# Serial: 32798226551256963324313806436981982369 +# MD5 Fingerprint: 02:26:c3:01:5e:08:30:37:43:a9:d0:7d:cf:37:e6:bf +# SHA1 Fingerprint: 32:3c:11:8e:1b:f7:b8:b6:52:54:e2:e2:10:0d:d6:02:90:37:f0:96 +# SHA256 Fingerprint: 37:d5:10:06:c5:12:ea:ab:62:64:21:f1:ec:8c:92:01:3f:c5:f8:2a:e9:8e:e5:33:eb:46:19:b8:de:b4:d0:6c +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo +R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx +MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9 +AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA +ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0 +7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W +kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI +mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ +KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1 +6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl +4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K +oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj +UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU +AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA" +# Serial: 69529181992039203566298953787712940909 +# MD5 Fingerprint: 8c:ca:dc:0b:22:ce:f5:be:72:ac:41:1a:11:a8:d8:12 +# SHA1 Fingerprint: 91:c6:d6:ee:3e:8a:c8:63:84:e5:48:c2:99:29:5c:75:6c:81:7b:81 +# SHA256 Fingerprint: 8d:72:2f:81:a9:c1:13:c0:79:1d:f1:36:a2:96:6d:b2:6c:95:0a:97:1d:b4:6b:41:99:f4:ea:54:b7:8b:fb:9f +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw +NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j +LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG +A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs +W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta +3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk +6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 +Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J +NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP +r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU +DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz +YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 +/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ +LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 +jVaMaA== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G5" +# Serial: 33037644167568058970164719475676101450 +# MD5 Fingerprint: cb:17:e4:31:67:3e:e2:09:fe:45:57:93:f3:0a:fa:1c +# SHA1 Fingerprint: 4e:b6:d5:78:49:9b:1c:cf:5f:58:1e:ad:56:be:3d:9b:67:44:a5:e5 +# SHA256 Fingerprint: 9a:cf:ab:7e:43:c8:d8:80:d0:6b:26:2a:94:de:ee:e4:b4:65:99:89:c3:d0:ca:f1:9b:af:64:05:e4:1a:b7:df +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +# Issuer: CN=SecureTrust CA O=SecureTrust Corporation +# Subject: CN=SecureTrust CA O=SecureTrust Corporation +# Label: "SecureTrust CA" +# Serial: 17199774589125277788362757014266862032 +# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1 +# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11 +# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73 +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +# Issuer: CN=Secure Global CA O=SecureTrust Corporation +# Subject: CN=Secure Global CA O=SecureTrust Corporation +# Label: "Secure Global CA" +# Serial: 9751836167731051554232119481456978597 +# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de +# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b +# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69 +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO Certification Authority O=COMODO CA Limited +# Label: "COMODO Certification Authority" +# Serial: 104350513648249232941998508985834464573 +# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 +# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b +# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- + +# Issuer: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. +# Subject: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. +# Label: "Network Solutions Certificate Authority" +# Serial: 116697915152937497490437556386812487904 +# MD5 Fingerprint: d3:f3:a6:16:c0:fa:6b:1d:59:b1:2d:96:4d:0e:11:2e +# SHA1 Fingerprint: 74:f8:a3:c3:ef:e7:b3:90:06:4b:83:90:3c:21:64:60:20:e5:df:ce +# SHA256 Fingerprint: 15:f0:ba:00:a3:ac:7a:f3:ac:88:4c:07:2b:10:11:a0:77:bd:77:c0:97:f4:01:64:b2:f8:59:8a:bd:83:86:0c +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi +MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV +UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO +ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz +c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP +OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl +mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF +BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4 +qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw +gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu +bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp +dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8 +6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/ +h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH +/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN +pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Label: "COMODO ECC Certification Authority" +# Serial: 41578283867086692638256921589707938090 +# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 +# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 +# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GA CA" +# Serial: 86718877871133159090080555911823548314 +# MD5 Fingerprint: bc:6c:51:33:a7:e9:d3:66:63:54:15:72:1b:21:92:93 +# SHA1 Fingerprint: 59:22:a1:e1:5a:ea:16:35:21:f8:98:39:6a:46:46:b0:44:1b:0f:a9 +# SHA256 Fingerprint: 41:c9:23:86:6a:b4:ca:d6:b7:ad:57:80:81:58:2e:02:07:97:a6:cb:df:4f:ff:78:ce:83:96:b3:89:37:d7:f5 +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB +ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly +aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w +NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G +A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX +SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR +VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2 +w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF +mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg +4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9 +4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw +EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx +SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2 +ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8 +vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi +Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ +/L7fCg0= +-----END CERTIFICATE----- + +# Issuer: CN=Certigna O=Dhimyotis +# Subject: CN=Certigna O=Dhimyotis +# Label: "Certigna" +# Serial: 18364802974209362175 +# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff +# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97 +# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +# Issuer: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center +# Subject: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center +# Label: "Deutsche Telekom Root CA 2" +# Serial: 38 +# MD5 Fingerprint: 74:01:4a:91:b1:08:c4:58:ce:47:cd:f0:dd:11:53:08 +# SHA1 Fingerprint: 85:a4:08:c0:9c:19:3e:5d:51:58:7d:cd:d6:13:30:fd:8c:de:37:bf +# SHA256 Fingerprint: b6:19:1a:50:d0:c3:97:7f:7d:a9:9b:cd:aa:c8:6a:22:7d:ae:b9:67:9e:c7:0b:a3:b0:c9:d9:22:71:c1:70:d3 +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc +MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj +IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB +IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE +RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl +U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290 +IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU +ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC +QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr +rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S +NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc +QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH +txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP +BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC +AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp +tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa +IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl +6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+ +xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc +# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc +# Label: "Cybertrust Global Root" +# Serial: 4835703278459682877484360 +# MD5 Fingerprint: 72:e4:4a:87:e3:69:40:80:77:ea:bc:e3:f4:ff:f0:e1 +# SHA1 Fingerprint: 5f:43:e5:b1:bf:f8:78:8c:ac:1c:c7:ca:4a:9a:c6:22:2b:cc:34:c6 +# SHA256 Fingerprint: 96:0a:df:00:63:e9:63:56:75:0c:29:65:dd:0a:08:67:da:0b:9c:bd:6e:77:71:4a:ea:fb:23:49:ab:39:3d:a3 +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG +A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh +bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE +ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS +b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5 +7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS +J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y +HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP +t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz +FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY +XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ +MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw +hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js +MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA +A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj +Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx +XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o +omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc +A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Label: "ePKI Root Certification Authority" +# Serial: 28956088682735189655030529057352760477 +# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3 +# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0 +# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5 +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +# Issuer: O=certSIGN OU=certSIGN ROOT CA +# Subject: O=certSIGN OU=certSIGN ROOT CA +# Label: "certSIGN ROOT CA" +# Serial: 35210227249154 +# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17 +# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b +# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G3" +# Serial: 28809105769928564313984085209975885599 +# MD5 Fingerprint: b5:e8:34:36:c9:10:44:58:48:70:6d:2e:83:d4:b8:05 +# SHA1 Fingerprint: 03:9e:ed:b8:0b:e7:a0:3c:69:53:89:3b:20:d2:d9:32:3a:4c:2a:fd +# SHA256 Fingerprint: b4:78:b8:12:25:0d:f8:78:63:5c:2a:a7:ec:7d:15:5e:aa:62:5e:e8:29:16:e2:cd:29:43:61:88:6c:d1:fb:d4 +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ +BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0 +BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz ++uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm +hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn +5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W +JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL +DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC +huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB +AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB +zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN +kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH +SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G +spki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G2" +# Serial: 71758320672825410020661621085256472406 +# MD5 Fingerprint: 74:9d:ea:60:24:c4:fd:22:53:3e:cc:3a:72:d9:29:4f +# SHA1 Fingerprint: aa:db:bc:22:23:8f:c4:01:a1:27:bb:38:dd:f4:1d:db:08:9e:f0:12 +# SHA256 Fingerprint: a4:31:0d:50:af:18:a6:44:71:90:37:2a:86:af:af:8b:95:1f:fb:43:1d:83:7f:1e:56:88:b4:59:71:ed:15:57 +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp +IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi +BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw +MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig +YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v +dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/ +BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6 +papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K +DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3 +KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox +XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G3" +# Serial: 127614157056681299805556476275995414779 +# MD5 Fingerprint: fb:1b:5d:43:8a:94:cd:44:c6:76:f2:43:4b:47:e7:31 +# SHA1 Fingerprint: f1:8b:53:8d:1b:e9:03:b6:a6:f0:56:43:5b:17:15:89:ca:f3:6b:f2 +# SHA256 Fingerprint: 4b:03:f4:58:07:ad:70:f2:1b:fc:2c:ae:71:c9:fd:e4:60:4c:06:4c:f5:ff:b6:86:ba:e5:db:aa:d7:fd:d3:4c +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB +rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV +BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa +Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl +LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u +MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm +gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8 +YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf +b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9 +9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S +zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk +OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA +2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW +oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c +KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM +m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu +MdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G2" +# Serial: 80682863203381065782177908751794619243 +# MD5 Fingerprint: 01:5e:d8:6b:bd:6f:3d:8e:a1:31:f8:12:e0:98:73:6a +# SHA1 Fingerprint: 8d:17:84:d5:37:f3:03:7d:ec:70:fe:57:8b:51:9a:99:e6:10:d7:b0 +# SHA256 Fingerprint: 5e:db:7a:c4:3b:82:a0:6a:87:61:e8:d7:be:49:79:eb:f2:61:1f:7d:d7:9b:f9:1c:1c:6b:56:6a:21:9e:d7:66 +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL +MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj +KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2 +MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw +NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV +BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH +MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL +So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal +tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG +CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT +qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz +rD6ogRLQy7rQkgu2npaqBA+K +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Universal Root Certification Authority" +# Serial: 85209574734084581917763752644031726877 +# MD5 Fingerprint: 8e:ad:b5:01:aa:4d:81:e4:8c:1d:d1:e1:14:00:95:19 +# SHA1 Fingerprint: 36:79:ca:35:66:87:72:30:4d:30:a5:fb:87:3b:0f:a7:7b:b7:0d:54 +# SHA256 Fingerprint: 23:99:56:11:27:a5:71:25:de:8c:ef:ea:61:0d:df:2f:a0:78:b5:c8:06:7f:4e:82:82:90:bf:b8:60:e8:4b:3c +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB +vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W +ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 +IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y +IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh +bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF +9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH +H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H +LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN +/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT +rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw +WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs +exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4 +sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+ +seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz +4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+ +BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR +lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 +7M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G4" +# Serial: 63143484348153506665311985501458640051 +# MD5 Fingerprint: 3a:52:e1:e7:fd:6f:3a:e3:6f:f3:6f:99:1b:f9:22:41 +# SHA1 Fingerprint: 22:d5:d8:df:8f:02:31:d1:8d:f7:9d:b7:cf:8a:2d:64:c9:3f:6c:3a +# SHA256 Fingerprint: 69:dd:d7:ea:90:bb:57:c9:3e:13:5d:c8:5e:a6:fc:d5:48:0b:60:32:39:bd:c4:54:fc:75:8b:2a:26:cf:7f:79 +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp +U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg +SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln +biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm +GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve +fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ +aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj +aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW +kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC +4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga +FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Subject: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Label: "NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny" +# Serial: 80544274841616 +# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 +# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 +# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA - G2" +# Serial: 10000012 +# MD5 Fingerprint: 7c:a5:0f:f8:5b:9a:7d:6d:30:ae:54:5a:e3:42:a2:8a +# SHA1 Fingerprint: 59:af:82:79:91:86:c7:b4:75:07:cb:cf:03:57:46:eb:04:dd:b7:16 +# SHA256 Fingerprint: 66:8c:83:94:7d:a6:3b:72:4b:ec:e1:74:3c:31:a0:e6:ae:d0:db:8e:c5:b3:1b:e3:77:bb:78:4f:91:b6:71:6f +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX +DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291 +qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp +uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU +Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE +pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp +5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M +UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN +GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy +5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv +6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK +eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6 +B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/ +BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov +L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG +SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS +CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen +5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897 +IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK +gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL ++63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL +vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm +bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk +N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC +Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z +ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Label: "SecureSign RootCA11" +# Serial: 1 +# MD5 Fingerprint: b7:52:74:e2:92:b4:80:93:f2:75:e4:cc:d7:f2:ea:26 +# SHA1 Fingerprint: 3b:c4:9f:48:f8:f3:73:a0:9c:1e:bd:f8:5b:b1:c3:65:c7:d8:11:b3 +# SHA256 Fingerprint: bf:0f:ee:fb:9e:3a:58:1a:d5:f9:e9:db:75:89:98:57:43:d2:61:08:5c:4d:31:4f:6f:5d:72:59:aa:42:16:12 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr +MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG +A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 +MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp +Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD +QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz +i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 +h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV +MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 +UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni +8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC +h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm +KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ +X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr +QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 +pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN +QSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Label: "Microsec e-Szigno Root CA 2009" +# Serial: 14014712776195784473 +# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 +# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e +# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Label: "GlobalSign Root CA - R3" +# Serial: 4835703278459759426209954 +# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 +# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad +# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 6047274297262753887 +# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3 +# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa +# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy +MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD +VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv +ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl +AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF +661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 +am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 +ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 +PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS +3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k +SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF +3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM +ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g +StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz +Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB +jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +# Issuer: CN=Izenpe.com O=IZENPE S.A. +# Subject: CN=Izenpe.com O=IZENPE S.A. +# Label: "Izenpe.com" +# Serial: 917563065490389241595536686991402621 +# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 +# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 +# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +# Issuer: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. +# Subject: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. +# Label: "Chambers of Commerce Root - 2008" +# Serial: 11806822484801597146 +# MD5 Fingerprint: 5e:80:9e:84:5a:0e:65:0b:17:02:f3:55:18:2a:3e:d7 +# SHA1 Fingerprint: 78:6a:74:ac:76:ab:14:7f:9c:6a:30:50:ba:9e:a8:7e:fe:9a:ce:3c +# SHA256 Fingerprint: 06:3e:4a:fa:c4:91:df:d3:32:f3:08:9b:85:42:e9:46:17:d8:93:d7:fe:94:4e:10:a7:93:7e:e2:9d:96:93:c0 +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz +IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz +MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj +dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw +EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp +MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9 +28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq +VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q +DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR +5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL +ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a +Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl +UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s ++12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5 +Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx +hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV +HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1 ++HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN +YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t +L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy +ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt +IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV +HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w +DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW +PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF +5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1 +glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH +FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2 +pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD +xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG +tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq +jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De +fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ +d0jQ +-----END CERTIFICATE----- + +# Issuer: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. +# Subject: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. +# Label: "Global Chambersign Root - 2008" +# Serial: 14541511773111788494 +# MD5 Fingerprint: 9e:80:ff:78:01:0c:2e:c1:36:bd:fe:96:90:6e:08:f3 +# SHA1 Fingerprint: 4a:bd:ee:ec:95:0d:35:9c:89:ae:c7:52:a1:2c:5b:29:f6:d6:aa:0c +# SHA256 Fingerprint: 13:63:35:43:93:34:a7:69:80:16:a0:d3:24:de:72:28:4e:07:9d:7b:52:20:bb:8f:bd:74:78:16:ee:be:ba:ca +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx +MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy +cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG +A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl +BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed +KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 +G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 +zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 +ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG +HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 +Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V +yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e +beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r +6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog +zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW +BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr +ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp +ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk +cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt +YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC +CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow +KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI +hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ +UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz +X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x +fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz +a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd +Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd +SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O +AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso +M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge +v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Services Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 +# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f +# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Commercial O=AffirmTrust +# Subject: CN=AffirmTrust Commercial O=AffirmTrust +# Label: "AffirmTrust Commercial" +# Serial: 8608355977964138876 +# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 +# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 +# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Networking O=AffirmTrust +# Subject: CN=AffirmTrust Networking O=AffirmTrust +# Label: "AffirmTrust Networking" +# Serial: 8957382827206547757 +# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f +# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f +# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium O=AffirmTrust +# Subject: CN=AffirmTrust Premium O=AffirmTrust +# Label: "AffirmTrust Premium" +# Serial: 7893706540734352110 +# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 +# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 +# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust +# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust +# Label: "AffirmTrust Premium ECC" +# Serial: 8401224907861490260 +# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d +# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb +# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA" +# Serial: 279744 +# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 +# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e +# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Label: "TWCA Root Certification Authority" +# Serial: 1 +# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 +# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 +# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Label: "Security Communication RootCA2" +# Serial: 0 +# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 +# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 +# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2011" +# Serial: 0 +# MD5 Fingerprint: 73:9f:4c:4b:73:5b:79:e9:fa:ba:1c:ef:6e:cb:d5:c9 +# SHA1 Fingerprint: fe:45:65:9b:79:03:5b:98:a1:61:b5:51:2e:ac:da:58:09:48:22:4d +# SHA256 Fingerprint: bc:10:4f:15:a4:8b:e7:09:dc:a5:42:a7:e1:d4:b9:df:6f:05:45:27:e8:02:ea:a9:2d:59:54:44:25:8a:fe:71 +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix +RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p +YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw +NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK +EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl +cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz +dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ +fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns +bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD +75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP +FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV +HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp +5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu +b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA +A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p +6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7 +dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys +Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI +l7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Label: "Actalis Authentication Root CA" +# Serial: 6271844772424770508 +# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 +# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac +# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +# Issuer: O=Trustis Limited OU=Trustis FPS Root CA +# Subject: O=Trustis Limited OU=Trustis FPS Root CA +# Label: "Trustis FPS Root CA" +# Serial: 36053640375399034304724988975563710553 +# MD5 Fingerprint: 30:c9:e7:1e:6b:e6:14:eb:65:b2:16:69:20:31:67:4d +# SHA1 Fingerprint: 3b:c0:38:0b:33:c3:f6:a6:0c:86:15:22:93:d9:df:f5:4b:81:c0:04 +# SHA256 Fingerprint: c1:b4:82:99:ab:a5:20:8f:e9:63:0a:ce:55:ca:68:a0:3e:da:5a:51:9c:88:02:a0:d3:a6:73:be:8f:8e:55:7d +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL +ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx +MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc +MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+ +AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH +iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj +vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA +0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB +OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/ +BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E +FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01 +GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW +zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4 +1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE +f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F +jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN +ZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 2 Root CA" +# Serial: 2 +# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 +# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 +# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 3 Root CA" +# Serial: 2 +# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec +# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 +# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 3" +# Serial: 1 +# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef +# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 +# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +# Issuer: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus +# Subject: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus +# Label: "EE Certification Centre Root CA" +# Serial: 112324828676200291871926431888494945866 +# MD5 Fingerprint: 43:5e:88:d4:7d:1a:4a:7e:fd:84:2e:52:eb:01:d4:6f +# SHA1 Fingerprint: c9:a8:b9:e7:55:80:5e:58:e3:53:77:a7:25:eb:af:c3:7b:27:cc:d7 +# SHA256 Fingerprint: 3e:84:ba:43:42:90:85:16:e7:75:73:c0:99:2f:09:79:ca:08:4e:46:85:68:1f:f1:95:cc:ba:8a:22:9b:8a:76 +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1 +MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1 +czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG +CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy +MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl +ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS +b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy +euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO +bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw +WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d +MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE +1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/ +zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB +BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF +BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV +v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG +E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW +iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v +GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 2009" +# Serial: 623603 +# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f +# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 +# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 EV 2009" +# Serial: 623604 +# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 +# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 +# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R2 O=Disig a.s. +# Subject: CN=CA Disig Root R2 O=Disig a.s. +# Label: "CA Disig Root R2" +# Serial: 10572350602393338211 +# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 +# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 +# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Label: "ACCVRAIZ1" +# Serial: 6828503384748696800 +# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 +# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 +# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA Global Root CA" +# Serial: 3262 +# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 +# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 +# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Label: "TeliaSonera Root CA v1" +# Serial: 199041966741090107964904287217786801558 +# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c +# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37 +# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89 +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 2" +# Serial: 1 +# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a +# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 +# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot 2011 O=Atos +# Subject: CN=Atos TrustedRoot 2011 O=Atos +# Label: "Atos TrustedRoot 2011" +# Serial: 6643877497813316402 +# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 +# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 +# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 1 G3" +# Serial: 687049649626669250736271037606554624078720034195 +# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab +# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 +# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2 G3" +# Serial: 390156079458959257446133169266079962026824725800 +# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 +# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 +# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3 G3" +# Serial: 268090761170461462463995952157327242137089239581 +# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 +# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d +# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 14367148294922964480859022125800977897474 +# MD5 Fingerprint: 20:f0:27:68:d1:7e:a0:9d:0e:e6:2a:ca:df:5c:89:8e +# SHA1 Fingerprint: 69:69:56:2e:40:80:f4:24:a1:e7:19:9f:14:ba:f3:ee:58:ab:6a:bb +# SHA256 Fingerprint: be:c9:49:11:c2:95:56:76:db:6c:0a:55:09:86:d7:6e:3b:a0:05:66:7c:44:2c:97:62:b4:fb:b7:73:de:22:8c +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ +FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F +uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX +kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs +ewv4n4Q= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA - G3" +# Serial: 10003001 +# MD5 Fingerprint: 0b:46:67:07:db:10:2f:19:8c:35:50:60:d1:0b:f4:37 +# SHA1 Fingerprint: d8:eb:6b:41:51:92:59:e0:f3:e7:85:00:c0:3d:b6:88:97:c9:ee:fc +# SHA256 Fingerprint: 3c:4f:b0:b9:5a:b8:b3:00:32:f4:32:b8:6f:53:5f:e1:72:c1:85:d0:fd:39:86:58:37:cf:36:18:7f:a6:f4:28 +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX +DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP +cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW +IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX +xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy +KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR +9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az +5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8 +6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7 +Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP +bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt +BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt +XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd +INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp +LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8 +Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp +gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh +/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw +0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A +fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq +4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR +1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ +QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM +94B7IWcnMFk= +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden +# Label: "Staat der Nederlanden EV Root CA" +# Serial: 10000013 +# MD5 Fingerprint: fc:06:af:7b:e8:1a:f1:9a:b4:e8:d2:70:1f:c0:f5:ba +# SHA1 Fingerprint: 76:e2:7e:c1:4f:db:82:c1:c0:a6:75:b5:05:be:3d:29:b4:ed:db:bb +# SHA256 Fingerprint: 4d:24:91:41:4c:fe:95:67:46:ec:4c:ef:a6:cf:6f:72:e2:8a:13:29:43:2f:9d:8a:90:7a:c4:cb:5d:ad:c1:5a +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y +MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg +TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS +b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS +M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC +UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d +Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p +rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l +pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb +j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC +KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS +/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X +cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH +1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP +px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7 +MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u +2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS +v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC +wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy +CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e +vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6 +Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa +Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL +eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8 +FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc +7uzXLg== +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Label: "IdenTrust Commercial Root CA 1" +# Serial: 13298821034946342390520003877796839426 +# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 +# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 +# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Label: "IdenTrust Public Sector Root CA 1" +# Serial: 13298821034946342390521976156843933698 +# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba +# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd +# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G2" +# Serial: 1246989352 +# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 +# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 +# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - EC1" +# Serial: 51543124481930649114116133369 +# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc +# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 +# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority +# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority +# Label: "CFCA EV ROOT" +# Serial: 407555286 +# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 +# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 +# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +# Issuer: CN=T\xdcRKTRUST Elektronik Sertifika Hizmet Sa\u011flay\u0131c\u0131s\u0131 H5 O=T\xdcRKTRUST Bilgi \u0130leti\u015fim ve Bili\u015fim G\xfcvenli\u011fi Hizmetleri A.\u015e. +# Subject: CN=T\xdcRKTRUST Elektronik Sertifika Hizmet Sa\u011flay\u0131c\u0131s\u0131 H5 O=T\xdcRKTRUST Bilgi \u0130leti\u015fim ve Bili\u015fim G\xfcvenli\u011fi Hizmetleri A.\u015e. +# Label: "T\xdcRKTRUST Elektronik Sertifika Hizmet Sa\u011flay\u0131c\u0131s\u0131 H5" +# Serial: 156233699172481 +# MD5 Fingerprint: da:70:8e:f0:22:df:93:26:f6:5f:9f:d3:15:06:52:4e +# SHA1 Fingerprint: c4:18:f6:4d:46:d1:df:00:3d:27:30:13:72:43:a9:12:11:c6:75:fb +# SHA256 Fingerprint: 49:35:1b:90:34:44:c1:85:cc:dc:5c:69:3d:24:d8:55:5c:b2:08:d6:a8:14:13:07:69:9f:4a:f0:63:19:9d:78 +-----BEGIN CERTIFICATE----- +MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UE +BhMCVFIxDzANBgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxn +aSDEsGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkg +QS7Fni4xQjBABgNVBAMMOVTDnFJLVFJVU1QgRWxla3Ryb25payBTZXJ0aWZpa2Eg +SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSBINTAeFw0xMzA0MzAwODA3MDFaFw0yMzA0 +MjgwODA3MDFaMIGxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0wSwYD +VQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8 +dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg1MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApCUZ4WWe60ghUEoI5RHwWrom +/4NZzkQqL/7hzmAD/I0Dpe3/a6i6zDQGn1k19uwsu537jVJp45wnEFPzpALFp/kR +Gml1bsMdi9GYjZOHp3GXDSHHmflS0yxjXVW86B8BSLlg/kJK9siArs1mep5Fimh3 +4khon6La8eHBEJ/rPCmBp+EyCNSgBbGM+42WAA4+Jd9ThiI7/PS98wl+d+yG6w8z +5UNP9FR1bSmZLmZaQ9/LXMrI5Tjxfjs1nQ/0xVqhzPMggCTTV+wVunUlm+hkS7M0 +hO8EuPbJbKoCPrZV4jI3X/xml1/N1p7HIL9Nxqw/dV8c7TKcfGkAaZHjIxhT6QID +AQABo0IwQDAdBgNVHQ4EFgQUVpkHHtOsDGlktAxQR95DLL4gwPswDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJ5FdnsX +SDLyOIspve6WSk6BGLFRRyDN0GSxDsnZAdkJzsiZ3GglE9Rc8qPoBP5yCccLqh0l +VX6Wmle3usURehnmp349hQ71+S4pL+f5bFgWV1Al9j4uPqrtd3GqqpmWRgqujuwq +URawXs3qZwQcWDD1YIq9pr1N5Za0/EKJAWv2cMhQOQwt1WbZyNKzMrcbGW3LM/nf +peYVhDfwwvJllpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CF +Yv4HAqGEVka+lgqaE9chTLd8B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW ++qtB4Uu2NQvAmxU= +-----END CERTIFICATE----- + +# Issuer: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903 +# Subject: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903 +# Label: "Certinomis - Root CA" +# Serial: 1 +# MD5 Fingerprint: 14:0a:fd:8d:a8:28:b5:38:69:db:56:7e:61:22:03:3f +# SHA1 Fingerprint: 9d:70:bb:01:a5:a4:a0:18:11:2e:f7:1c:01:b9:32:c5:34:e7:88:a8 +# SHA256 Fingerprint: 2a:99:f5:bc:11:74:b7:3c:bb:1d:62:08:84:e0:1c:34:e5:1c:cb:39:78:da:12:5f:0e:33:26:88:83:bf:41:58 +-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET +MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb +BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz +MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx +FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g +Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2 +fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl +LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV +WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF +TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb +5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc +CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri +wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ +wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG +m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4 +F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng +WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0 +2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF +AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/ +0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw +F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS +g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj +qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN +h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/ +ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V +btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj +Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ +8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW +gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GB CA" +# Serial: 157768595616588414422159278966750757568 +# MD5 Fingerprint: a4:eb:b9:61:28:2e:b7:2f:98:b0:35:26:90:99:51:1d +# SHA1 Fingerprint: 0f:f9:40:76:18:d3:d7:6a:4b:98:f0:a8:35:9e:0c:fd:27:ac:cc:ed +# SHA256 Fingerprint: 6b:9c:08:e8:6e:b0:f7:67:cf:ad:65:cd:98:b6:21:49:e5:49:4a:67:f5:84:5e:7b:d1:ed:01:9f:27:b8:6b:d6 +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +# Issuer: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Subject: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Label: "SZAFIR ROOT CA2" +# Serial: 357043034767186914217277344587386743377558296292 +# MD5 Fingerprint: 11:64:c1:89:b0:24:b1:8c:b1:07:7e:89:9e:51:9e:99 +# SHA1 Fingerprint: e2:52:fa:95:3f:ed:db:24:60:bd:6e:28:f3:9c:cc:cf:5e:b3:3f:de +# SHA256 Fingerprint: a1:33:9d:33:28:1a:0b:56:e5:57:d3:d3:2b:1c:e7:f9:36:7e:b0:94:bd:5f:a7:2a:7e:50:04:c8:de:d7:ca:fe +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 +ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw +NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg +Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN +QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT +3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw +3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 +3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 +BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN +XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF +AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw +8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG +nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP +oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy +d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg +LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA 2" +# Serial: 44979900017204383099463764357512596969 +# MD5 Fingerprint: 6d:46:9e:d9:25:6d:08:23:5b:5e:74:7d:1e:27:db:f2 +# SHA1 Fingerprint: d3:dd:48:3e:2b:bf:4c:05:e8:af:10:f5:fa:76:26:cf:d3:dc:30:92 +# SHA256 Fingerprint: b6:76:f2:ed:da:e8:77:5c:d3:6c:b0:f6:3c:d1:d4:60:39:61:f4:9e:62:65:ba:01:3a:2f:03:07:b6:d0:b8:04 +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: ca:ff:e2:db:03:d9:cb:4b:e9:0f:ad:84:fd:7b:18:ce +# SHA1 Fingerprint: 01:0c:06:95:a6:98:19:14:ff:bf:5f:c6:b0:b6:95:ea:29:e9:12:a6 +# SHA256 Fingerprint: a0:40:92:9a:02:ce:53:b4:ac:f4:f2:ff:c6:98:1c:e4:49:6f:75:5e:6d:45:fe:0b:2a:69:2b:cd:52:52:3f:36 +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix +DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k +IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT +N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v +dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG +A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh +ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx +QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA +4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 +AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 +4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C +ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV +9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD +gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 +Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq +NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko +LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd +ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I +XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI +M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot +9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V +Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea +j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh +X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ +l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf +bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 +pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK +e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 +vm9qp/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions ECC RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: 81:e5:b4:17:eb:c2:f5:e1:4b:0d:41:7b:49:92:fe:ef +# SHA1 Fingerprint: 9f:f1:71:8d:92:d5:9a:f3:7d:74:97:b4:bc:6f:84:68:0b:ba:b6:66 +# SHA256 Fingerprint: 44:b5:45:aa:8a:25:e6:5a:73:ca:15:dc:27:fc:36:d2:4c:1c:b9:95:3a:06:65:39:b1:15:82:dc:48:7b:48:33 +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN +BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl +bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv +b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ +BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj +YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 +MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 +dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg +QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa +jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi +C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep +lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof +TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +# Issuer: CN=Certplus Root CA G1 O=Certplus +# Subject: CN=Certplus Root CA G1 O=Certplus +# Label: "Certplus Root CA G1" +# Serial: 1491911565779898356709731176965615564637713 +# MD5 Fingerprint: 7f:09:9c:f7:d9:b9:5c:69:69:56:d5:37:3e:14:0d:42 +# SHA1 Fingerprint: 22:fd:d0:b7:fd:a2:4e:0d:ac:49:2c:a0:ac:a6:7b:6a:1f:e3:f7:66 +# SHA256 Fingerprint: 15:2a:40:2b:fc:df:2c:d5:48:05:4d:22:75:b3:9c:7f:ca:3e:c0:97:80:78:b0:f0:ea:76:e5:61:a6:c7:43:3e +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUA +MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy +dHBsdXMgUm9vdCBDQSBHMTAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBa +MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy +dHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +ANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHNr49a +iZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt +6kuJPKNxQv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP +0FG7Yn2ksYyy/yARujVjBYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f +6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTvLRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDE +EW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2z4QTd28n6v+WZxcIbekN +1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc4nBvCGrc +h2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCT +mehd4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV +4EJQeIQEQWGw9CEjjy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPO +WftwenMGE9nTdDckQQoRb5fc5+R+ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1Ud +DwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSowcCbkahDFXxd +Bie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHYlwuBsTANBgkq +hkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh +66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7 +/SMNkPX0XtPGYX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BS +S7CTKtQ+FjPlnsZlFT5kOwQ/2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j +2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F6ALEUz65noe8zDUa3qHpimOHZR4R +Kttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilXCNQ314cnrUlZp5Gr +RHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWetUNy +6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEV +V/xuZDDCVRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5 +g4VCXA9DO2pJNdWY9BW/+mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl +++O/QmueD6i9a5jc2NvLi6Td11n0bt3+qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo= +-----END CERTIFICATE----- + +# Issuer: CN=Certplus Root CA G2 O=Certplus +# Subject: CN=Certplus Root CA G2 O=Certplus +# Label: "Certplus Root CA G2" +# Serial: 1492087096131536844209563509228951875861589 +# MD5 Fingerprint: a7:ee:c4:78:2d:1b:ee:2d:b9:29:ce:d6:a7:96:32:31 +# SHA1 Fingerprint: 4f:65:8e:1f:e9:06:d8:28:02:e9:54:47:41:c9:54:25:5d:69:cc:1a +# SHA256 Fingerprint: 6c:c0:50:41:e6:44:5e:74:69:6c:4c:fb:c9:f8:0f:54:3b:7e:ab:bb:44:b4:ce:6f:78:7c:6a:99:71:c4:2f:17 +-----BEGIN CERTIFICATE----- +MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4x +CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs +dXMgUm9vdCBDQSBHMjAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4x +CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs +dXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABM0PW1aC3/BFGtat +93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uNAm8x +Ik0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0P +AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwj +FNiPwyCrKGBZMB8GA1UdIwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqG +SM49BAMDA2gAMGUCMHD+sAvZ94OX7PNVHdTcswYO/jOYnYs5kGuUIe22113WTNch +p+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjlvPl5adytRSv3tjFzzAal +U5ORGpOucGpnutee5WEaXw== +-----END CERTIFICATE----- + +# Issuer: CN=OpenTrust Root CA G1 O=OpenTrust +# Subject: CN=OpenTrust Root CA G1 O=OpenTrust +# Label: "OpenTrust Root CA G1" +# Serial: 1492036577811947013770400127034825178844775 +# MD5 Fingerprint: 76:00:cc:81:29:cd:55:5e:88:6a:7a:2e:f7:4d:39:da +# SHA1 Fingerprint: 79:91:e8:34:f7:e2:ee:dd:08:95:01:52:e9:55:2d:14:e9:58:d5:7e +# SHA256 Fingerprint: 56:c7:71:28:d9:8c:18:d9:1b:4c:fd:ff:bc:25:ee:91:03:d4:75:8e:a2:ab:ad:82:6a:90:f3:45:7d:46:0e:b4 +-----BEGIN CERTIFICATE----- +MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUA +MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w +ZW5UcnVzdCBSb290IENBIEcxMB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAw +MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU +T3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7faYp6b +wiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX +/uMftk87ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR0 +77F9jAHiOH3BX2pfJLKOYheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGP +uY4zbGneWK2gDqdkVBFpRGZPTBKnjix9xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLx +p2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO9z0M+Yo0FMT7MzUj8czx +Kselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq3ywgsNw2 +TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+W +G+Oin6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPw +vFEVVJSmdz7QdFG9URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYY +EQRVzXR7z2FwefR7LFxckvzluFqrTJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUl0YhVyE1 +2jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/PxN3DlCPaTKbYw +DQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E +PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kf +gLMtMrpkZ2CvuVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbS +FXJfLkur1J1juONI5f6ELlgKn0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0 +V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLhX4SPgPL0DTatdrOjteFkdjpY3H1P +XlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80nR14SohWZ25g/4/I +i+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcmGS3t +TAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L91 +09S5zvE/bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/Ky +Pu1svf0OnWZzsD2097+o4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJ +AwSQiumPv+i2tCqjI40cHLI5kqiPAlxAOXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj +1oxx +-----END CERTIFICATE----- + +# Issuer: CN=OpenTrust Root CA G2 O=OpenTrust +# Subject: CN=OpenTrust Root CA G2 O=OpenTrust +# Label: "OpenTrust Root CA G2" +# Serial: 1492012448042702096986875987676935573415441 +# MD5 Fingerprint: 57:24:b6:59:24:6b:ae:c8:fe:1c:0c:20:f2:c0:4e:eb +# SHA1 Fingerprint: 79:5f:88:60:c5:ab:7c:3d:92:e6:cb:f4:8d:e1:45:cd:11:ef:60:0b +# SHA256 Fingerprint: 27:99:58:29:fe:6a:75:15:c1:bf:e8:48:f9:c4:76:1d:b1:6c:22:59:29:25:7b:f4:0d:08:94:f2:9e:a8:ba:f2 +-----BEGIN CERTIFICATE----- +MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUA +MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w +ZW5UcnVzdCBSb290IENBIEcyMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAw +MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU +T3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+Ntmh +/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78e +CbY2albz4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/6 +1UWY0jUJ9gNDlP7ZvyCVeYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fE +FY8ElggGQgT4hNYdvJGmQr5J1WqIP7wtUdGejeBSzFfdNTVY27SPJIjki9/ca1TS +gSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz3GIZ38i1MH/1PCZ1Eb3X +G7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj3CzMpSZy +YhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaH +vGOz9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4 +t/bQWVyJ98LVtZR00dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/ +gh7PU3+06yzbXfZqfUAkBXKJOAGTy3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUajn6QiL3 +5okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59M4PLuG53hq8w +DQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz +Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0 +nXGEL8pZ0keImUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qT +RmTFAHneIWv2V6CG1wZy7HBGS4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpT +wm+bREx50B1ws9efAvSyB7DH5fitIw6mVskpEndI2S9G/Tvw/HRwkqWOOAgfZDC2 +t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ6e18CL13zSdkzJTa +TkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97krgCf2 +o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU +3jg9CcCoSmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eA +iN1nE28daCSLT7d0geX0YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14f +WKGVyasvc0rQLW6aWQ9VGHgtPFGml4vmu7JwqkwR3v98KzfUetF3NI/n+UL3PIEM +S1IK +-----END CERTIFICATE----- + +# Issuer: CN=OpenTrust Root CA G3 O=OpenTrust +# Subject: CN=OpenTrust Root CA G3 O=OpenTrust +# Label: "OpenTrust Root CA G3" +# Serial: 1492104908271485653071219941864171170455615 +# MD5 Fingerprint: 21:37:b4:17:16:92:7b:67:46:70:a9:96:d7:a8:13:24 +# SHA1 Fingerprint: 6e:26:64:f3:56:bf:34:55:bf:d1:93:3f:7c:01:de:d8:13:da:8a:a6 +# SHA256 Fingerprint: b7:c3:62:31:70:6e:81:07:8c:36:7c:b8:96:19:8f:1e:32:08:dd:92:69:49:dd:8f:57:09:a4:10:f7:5b:62:92 +-----BEGIN CERTIFICATE----- +MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAx +CzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5U +cnVzdCBSb290IENBIEczMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFow +QDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwUT3Bl +blRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARK7liuTcpm +3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5Bta1d +oYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4G +A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5 +DMlv4VBN0BBY3JWIbTAfBgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAK +BggqhkjOPQQDAwNpADBmAjEAj6jcnboMBBf6Fek9LykBl7+BFjNAk2z8+e2AcG+q +j9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta3U1fJAuwACEl74+nBCZx +4nxp5V2a+EEfOzmTk51V6s2N8fvB +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X1 O=Internet Security Research Group +# Subject: CN=ISRG Root X1 O=Internet Security Research Group +# Label: "ISRG Root X1" +# Serial: 172886928669790476064670243504169061120 +# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e +# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8 +# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6 +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +# Issuer: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Subject: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Label: "AC RAIZ FNMT-RCM" +# Serial: 485876308206448804701554682760554759 +# MD5 Fingerprint: e2:09:04:b4:d3:bd:d1:a0:14:fd:1a:d2:47:c4:57:1d +# SHA1 Fingerprint: ec:50:35:07:b2:15:c4:95:62:19:e2:a8:9a:5b:42:99:2c:4c:2c:20 +# SHA256 Fingerprint: eb:c5:57:0c:29:01:8c:4d:67:b1:aa:12:7b:af:12:f7:03:b4:61:1e:bc:17:b7:da:b5:57:38:94:17:9b:93:fa +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx +CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ +WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ +BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG +Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ +yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf +BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz +WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF +tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z +374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC +IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL +mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 +wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS +MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 +ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet +UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H +YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 +LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 +RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM +LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf +77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N +JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm +fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp +6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp +1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B +9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok +RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv +uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 1 O=Amazon +# Subject: CN=Amazon Root CA 1 O=Amazon +# Label: "Amazon Root CA 1" +# Serial: 143266978916655856878034712317230054538369994 +# MD5 Fingerprint: 43:c6:bf:ae:ec:fe:ad:2f:18:c6:88:68:30:fc:c8:e6 +# SHA1 Fingerprint: 8d:a7:f9:65:ec:5e:fc:37:91:0f:1c:6e:59:fd:c1:cc:6a:6e:de:16 +# SHA256 Fingerprint: 8e:cd:e6:88:4f:3d:87:b1:12:5b:a3:1a:c3:fc:b1:3d:70:16:de:7f:57:cc:90:4f:e1:cb:97:c6:ae:98:19:6e +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 2 O=Amazon +# Subject: CN=Amazon Root CA 2 O=Amazon +# Label: "Amazon Root CA 2" +# Serial: 143266982885963551818349160658925006970653239 +# MD5 Fingerprint: c8:e5:8d:ce:a8:42:e2:7a:c0:2a:5c:7c:9e:26:bf:66 +# SHA1 Fingerprint: 5a:8c:ef:45:d7:a6:98:59:76:7a:8c:8b:44:96:b5:78:cf:47:4b:1a +# SHA256 Fingerprint: 1b:a5:b2:aa:8c:65:40:1a:82:96:01:18:f8:0b:ec:4f:62:30:4d:83:ce:c4:71:3a:19:c3:9c:01:1e:a4:6d:b4 +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 3 O=Amazon +# Subject: CN=Amazon Root CA 3 O=Amazon +# Label: "Amazon Root CA 3" +# Serial: 143266986699090766294700635381230934788665930 +# MD5 Fingerprint: a0:d4:ef:0b:f7:b5:d8:49:95:2a:ec:f5:c4:fc:81:87 +# SHA1 Fingerprint: 0d:44:dd:8c:3c:8c:1a:1a:58:75:64:81:e9:0f:2e:2a:ff:b3:d2:6e +# SHA256 Fingerprint: 18:ce:6c:fe:7b:f1:4e:60:b2:e3:47:b8:df:e8:68:cb:31:d0:2e:bb:3a:da:27:15:69:f5:03:43:b4:6d:b3:a4 +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 4 O=Amazon +# Subject: CN=Amazon Root CA 4 O=Amazon +# Label: "Amazon Root CA 4" +# Serial: 143266989758080763974105200630763877849284878 +# MD5 Fingerprint: 89:bc:27:d5:eb:17:8d:06:6a:69:d5:fd:89:47:b4:cd +# SHA1 Fingerprint: f6:10:84:07:d6:f8:bb:67:98:0c:c2:e2:44:c2:eb:ae:1c:ef:63:be +# SHA256 Fingerprint: e3:5d:28:41:9e:d0:20:25:cf:a6:90:38:cd:62:39:62:45:8d:a5:c6:95:fb:de:a3:c2:2b:0b:fb:25:89:70:92 +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +# Issuer: CN=LuxTrust Global Root 2 O=LuxTrust S.A. +# Subject: CN=LuxTrust Global Root 2 O=LuxTrust S.A. +# Label: "LuxTrust Global Root 2" +# Serial: 59914338225734147123941058376788110305822489521 +# MD5 Fingerprint: b2:e1:09:00:61:af:f7:f1:91:6f:c4:ad:8d:5e:3b:7c +# SHA1 Fingerprint: 1e:0e:56:19:0a:d1:8b:25:98:b2:04:44:ff:66:8a:04:17:99:5f:3f +# SHA256 Fingerprint: 54:45:5f:71:29:c2:0b:14:47:c4:18:f9:97:16:8f:24:c5:8f:c5:02:3b:f5:da:5b:e2:eb:6e:1d:d8:90:2e:d5 +-----BEGIN CERTIFICATE----- +MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQEL +BQAwRjELMAkGA1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNV +BAMMFkx1eFRydXN0IEdsb2JhbCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUw +MzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEWMBQGA1UECgwNTHV4VHJ1c3QgUy5B +LjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wmKb3F +ibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTem +hfY7RBi2xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1 +EMShduxq3sVs35a0VkBCwGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsn +Xpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4 +zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkmFRseTJIpgp7VkoGSQXAZ +96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niFwpN6cj5m +j5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4g +DEa/a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+ +8kPREd8vZS9kzl8UubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2j +X5t/Lax5Gw5CMZdjpPuKadUiDTSQMC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmH +hFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB/zBCBgNVHSAEOzA5MDcGByuB +KwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5Lmx1eHRydXN0 +Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT ++Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQEL +BQADggIBAGoZFO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9 +BzZAcg4atmpZ1gDlaCDdLnINH2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTO +jFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW7MM3LGVYvlcAGvI1+ut7MV3CwRI9 +loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIuZY+kt9J/Z93I055c +qqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWAVWe+ +2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/ +JEAdemrRTxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKre +zrnK+T+Tb/mjuuqlPpmt/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQf +LSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+ +x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31IiyBMz2TWuJdGsE7RKlY6 +oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr +-----END CERTIFICATE----- + +# Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" +# Serial: 1 +# MD5 Fingerprint: dc:00:81:dc:69:2f:3e:2f:b0:3b:f6:3d:5a:91:8e:49 +# SHA1 Fingerprint: 31:43:64:9b:ec:ce:27:ec:ed:3a:3f:0b:8f:0d:e4:e8:91:dd:ee:ca +# SHA256 Fingerprint: 46:ed:c3:68:90:46:d5:3a:45:3f:b3:10:4a:b8:0d:ca:ec:65:8b:26:60:ea:16:29:dd:7e:86:79:90:64:87:16 +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx +GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp +bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w +KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 +BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy +dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG +EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll +IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU +QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT +TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg +LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 +a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr +LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr +N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X +YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ +iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f +AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH +V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf +IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 +lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c +8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf +lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +# Issuer: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Subject: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Label: "GDCA TrustAUTH R5 ROOT" +# Serial: 9009899650740120186 +# MD5 Fingerprint: 63:cc:d9:3d:34:35:5c:6f:53:a3:e2:08:70:48:1f:b4 +# SHA1 Fingerprint: 0f:36:38:5b:81:1a:25:c3:9b:31:4e:83:ca:e9:34:66:70:cc:74:b4 +# SHA256 Fingerprint: bf:ff:8f:d0:44:33:48:7d:6a:8a:a6:0c:1a:29:76:7a:9f:c2:bb:b0:5e:42:0f:71:3a:13:b9:92:89:1d:38:93 +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE +BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 +MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w +HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj +Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj +TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u +KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj +qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm +MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 +ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP +zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk +L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC +jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA +HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC +AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm +DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 +COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry +L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf +JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg +IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io +2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV +09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ +XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq +T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe +MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Label: "SSL.com Root Certification Authority RSA" +# Serial: 8875640296558310041 +# MD5 Fingerprint: 86:69:12:c0:70:f1:ec:ac:ac:c2:d5:bc:a5:5b:a1:29 +# SHA1 Fingerprint: b7:ab:33:08:d1:ea:44:77:ba:14:80:12:5a:6f:bd:a9:36:49:0c:bb +# SHA256 Fingerprint: 85:66:6a:56:2e:e0:be:5c:e9:25:c1:d8:89:0a:6f:76:a8:7e:c1:6d:4d:7d:5f:29:ea:74:19:cf:20:12:3b:69 +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK +DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz +OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R +xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX +qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC +C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 +6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh +/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF +YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E +JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc +US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 +ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm ++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi +M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G +A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV +cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc +Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs +PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ +q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 +cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr +a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I +H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y +K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu +nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf +oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY +Ic2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com Root Certification Authority ECC" +# Serial: 8495723813297216424 +# MD5 Fingerprint: 2e:da:e4:39:7f:9c:8f:37:d1:70:9f:26:17:51:3a:8e +# SHA1 Fingerprint: c3:19:7c:39:24:e6:54:af:1b:c4:ab:20:95:7a:e2:c3:0e:13:02:6a +# SHA256 Fingerprint: 34:17:bb:06:cc:60:07:da:1b:96:1c:92:0b:8a:b4:ce:3f:ad:82:0e:4a:a3:0b:9a:cb:c4:a7:4e:bd:ce:bc:65 +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz +WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 +b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS +b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI +7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg +CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD +VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T +kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ +gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority RSA R2" +# Serial: 6248227494352943350 +# MD5 Fingerprint: e1:1e:31:58:1a:ae:54:53:02:f6:17:6a:11:7b:4d:95 +# SHA1 Fingerprint: 74:3a:f0:52:9b:d0:32:a0:f4:4a:83:cd:d4:ba:a9:7b:7c:2e:c4:9a +# SHA256 Fingerprint: 2e:7b:f1:6c:c2:24:85:a7:bb:e2:aa:86:96:75:07:61:b0:ae:39:be:3b:2f:e9:d0:cc:6d:4e:f7:34:91:42:5c +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE +CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy +MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G +A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD +DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq +M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf +OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa +4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 +HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR +aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA +b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ +Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV +PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO +pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu +UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY +MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 +9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW +s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 +Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg +cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM +79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz +/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt +ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm +Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK +QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ +w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi +S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 +mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority ECC" +# Serial: 3182246526754555285 +# MD5 Fingerprint: 59:53:22:65:83:42:01:54:c0:ce:42:b9:5a:7c:f2:90 +# SHA1 Fingerprint: 4c:dd:51:a3:d1:f5:20:32:14:b0:c6:c5:32:23:03:91:c7:46:42:6d +# SHA256 Fingerprint: 22:a2:c1:f7:bd:ed:70:4c:c1:e7:01:b5:f4:08:c3:10:88:0f:e9:56:b5:de:2a:4a:44:f9:9c:87:3a:25:a7:c8 +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx +NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv +bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA +VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku +WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX +5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ +ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg +h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- diff --git a/venv/lib/python3.12/site-packages/botocore/client.py b/venv/lib/python3.12/site-packages/botocore/client.py new file mode 100644 index 0000000..8ffd4c1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/client.py @@ -0,0 +1,1456 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import logging + +from botocore import ( + UNSIGNED, # noqa: F401 + waiter, + xform_name, +) +from botocore.args import ClientArgsCreator +from botocore.auth import ( + AUTH_TYPE_MAPS, + resolve_auth_scheme_preference, + resolve_auth_type, +) +from botocore.awsrequest import prepare_request_dict +from botocore.compress import maybe_compress_request +from botocore.config import Config +from botocore.context import with_current_context +from botocore.credentials import RefreshableCredentials +from botocore.discovery import ( + EndpointDiscoveryHandler, + EndpointDiscoveryManager, + block_endpoint_discovery_required_operations, +) +from botocore.docs.docstring import ClientMethodDocstring, PaginatorDocstring +from botocore.exceptions import ( + ClientError, # noqa: F401 + DataNotFoundError, + InvalidEndpointDiscoveryConfigurationError, + OperationNotPageableError, + UnknownServiceError, + UnknownSignatureVersionError, +) +from botocore.history import get_global_history_recorder +from botocore.hooks import first_non_none_response +from botocore.httpchecksum import ( + apply_request_checksum, + resolve_checksum_context, +) +from botocore.model import ServiceModel +from botocore.paginate import Paginator +from botocore.retries import adaptive, standard +from botocore.useragent import UserAgentString, register_feature_id +from botocore.utils import ( + CachedProperty, + EventbridgeSignerSetter, + S3ArnParamHandler, # noqa: F401 + S3ControlArnParamHandler, # noqa: F401 + S3ControlArnParamHandlerv2, + S3ControlEndpointSetter, # noqa: F401 + S3EndpointSetter, # noqa: F401 + S3ExpressIdentityResolver, + S3RegionRedirector, # noqa: F401 + S3RegionRedirectorv2, + ensure_boolean, + get_service_module_name, +) + +logger = logging.getLogger(__name__) +history_recorder = get_global_history_recorder() + + +class ClientCreator: + """Creates client objects for a service.""" + + def __init__( + self, + loader, + endpoint_resolver, + user_agent, + event_emitter, + retry_handler_factory, + retry_config_translator, + response_parser_factory=None, + exceptions_factory=None, + config_store=None, + user_agent_creator=None, + auth_token_resolver=None, + ): + self._loader = loader + self._endpoint_resolver = endpoint_resolver + self._user_agent = user_agent + self._event_emitter = event_emitter + self._retry_handler_factory = retry_handler_factory + self._retry_config_translator = retry_config_translator + self._response_parser_factory = response_parser_factory + self._exceptions_factory = exceptions_factory + # TODO: Migrate things away from scoped_config in favor of the + # config_store. The config store can pull things from both the scoped + # config and environment variables (and potentially more in the + # future). + self._config_store = config_store + self._user_agent_creator = user_agent_creator + self._auth_token_resolver = auth_token_resolver + + def create_client( + self, + service_name, + region_name, + is_secure=True, + endpoint_url=None, + verify=None, + credentials=None, + scoped_config=None, + api_version=None, + client_config=None, + auth_token=None, + ): + responses = self._event_emitter.emit( + 'choose-service-name', service_name=service_name + ) + service_name = first_non_none_response(responses, default=service_name) + service_model = self._load_service_model(service_name, api_version) + try: + endpoints_ruleset_data = self._load_service_endpoints_ruleset( + service_name, api_version + ) + partition_data = self._loader.load_data('partitions') + except UnknownServiceError: + endpoints_ruleset_data = None + partition_data = None + logger.info( + 'No endpoints ruleset found for service %s, falling back to ' + 'legacy endpoint routing.', + service_name, + ) + + cls = self._create_client_class(service_name, service_model) + region_name, client_config = self._normalize_fips_region( + region_name, client_config + ) + if auth := service_model.metadata.get('auth'): + service_signature_version = resolve_auth_type(auth) + else: + service_signature_version = service_model.metadata.get( + 'signatureVersion' + ) + endpoint_bridge = ClientEndpointBridge( + self._endpoint_resolver, + scoped_config, + client_config, + service_signing_name=service_model.metadata.get('signingName'), + config_store=self._config_store, + service_signature_version=service_signature_version, + ) + if token := self._evaluate_client_specific_token( + service_model.signing_name + ): + auth_token = token + client_args = self._get_client_args( + service_model, + region_name, + is_secure, + endpoint_url, + verify, + credentials, + scoped_config, + client_config, + endpoint_bridge, + auth_token, + endpoints_ruleset_data, + partition_data, + ) + service_client = cls(**client_args) + self._register_retries(service_client) + self._register_s3_events( + client=service_client, + endpoint_bridge=None, + endpoint_url=None, + client_config=client_config, + scoped_config=scoped_config, + ) + self._register_s3express_events(client=service_client) + self._register_s3_control_events(client=service_client) + self._register_importexport_events(client=service_client) + self._register_endpoint_discovery( + service_client, endpoint_url, client_config + ) + return service_client + + def create_client_class(self, service_name, api_version=None): + service_model = self._load_service_model(service_name, api_version) + return self._create_client_class(service_name, service_model) + + def _create_client_class(self, service_name, service_model): + class_attributes = self._create_methods(service_model) + py_name_to_operation_name = self._create_name_mapping(service_model) + class_attributes['_PY_TO_OP_NAME'] = py_name_to_operation_name + bases = [BaseClient] + service_id = service_model.service_id.hyphenize() + self._event_emitter.emit( + f'creating-client-class.{service_id}', + class_attributes=class_attributes, + base_classes=bases, + ) + class_name = get_service_module_name(service_model) + cls = type(str(class_name), tuple(bases), class_attributes) + return cls + + def _normalize_fips_region(self, region_name, client_config): + if region_name is not None: + normalized_region_name = region_name.replace('fips-', '').replace( + '-fips', '' + ) + # If region has been transformed then set flag + if normalized_region_name != region_name: + config_use_fips_endpoint = Config(use_fips_endpoint=True) + if client_config: + # Keeping endpoint setting client specific + client_config = client_config.merge( + config_use_fips_endpoint + ) + else: + client_config = config_use_fips_endpoint + logger.warning( + 'transforming region from %s to %s and setting ' + 'use_fips_endpoint to true. client should not ' + 'be configured with a fips psuedo region.', + region_name, + normalized_region_name, + ) + region_name = normalized_region_name + return region_name, client_config + + def _load_service_model(self, service_name, api_version=None): + json_model = self._loader.load_service_model( + service_name, 'service-2', api_version=api_version + ) + service_model = ServiceModel(json_model, service_name=service_name) + return service_model + + def _load_service_endpoints_ruleset(self, service_name, api_version=None): + return self._loader.load_service_model( + service_name, 'endpoint-rule-set-1', api_version=api_version + ) + + def _register_retries(self, client): + retry_mode = client.meta.config.retries['mode'] + if retry_mode == 'standard': + self._register_v2_standard_retries(client) + elif retry_mode == 'adaptive': + self._register_v2_standard_retries(client) + self._register_v2_adaptive_retries(client) + elif retry_mode == 'legacy': + self._register_legacy_retries(client) + else: + return + register_feature_id(f'RETRY_MODE_{retry_mode.upper()}') + + def _register_v2_standard_retries(self, client): + max_attempts = client.meta.config.retries.get('total_max_attempts') + kwargs = {'client': client} + if max_attempts is not None: + kwargs['max_attempts'] = max_attempts + standard.register_retry_handler(**kwargs) + + def _register_v2_adaptive_retries(self, client): + adaptive.register_retry_handler(client) + + def _register_legacy_retries(self, client): + endpoint_prefix = client.meta.service_model.endpoint_prefix + service_id = client.meta.service_model.service_id + service_event_name = service_id.hyphenize() + + # First, we load the entire retry config for all services, + # then pull out just the information we need. + original_config = self._loader.load_data('_retry') + if not original_config: + return + + retries = self._transform_legacy_retries(client.meta.config.retries) + retry_config = self._retry_config_translator.build_retry_config( + endpoint_prefix, + original_config.get('retry', {}), + original_config.get('definitions', {}), + retries, + ) + + logger.debug( + "Registering retry handlers for service: %s", + client.meta.service_model.service_name, + ) + handler = self._retry_handler_factory.create_retry_handler( + retry_config, endpoint_prefix + ) + unique_id = f'retry-config-{service_event_name}' + client.meta.events.register( + f"needs-retry.{service_event_name}", handler, unique_id=unique_id + ) + + def _transform_legacy_retries(self, retries): + if retries is None: + return + copied_args = retries.copy() + if 'total_max_attempts' in retries: + copied_args = retries.copy() + copied_args['max_attempts'] = ( + copied_args.pop('total_max_attempts') - 1 + ) + return copied_args + + def _get_retry_mode(self, client, config_store): + client_retries = client.meta.config.retries + if ( + client_retries is not None + and client_retries.get('mode') is not None + ): + return client_retries['mode'] + return config_store.get_config_variable('retry_mode') or 'legacy' + + def _register_endpoint_discovery(self, client, endpoint_url, config): + if endpoint_url is not None: + # Don't register any handlers in the case of a custom endpoint url + return + # Only attach handlers if the service supports discovery + if client.meta.service_model.endpoint_discovery_operation is None: + return + events = client.meta.events + service_id = client.meta.service_model.service_id.hyphenize() + enabled = False + if config and config.endpoint_discovery_enabled is not None: + enabled = config.endpoint_discovery_enabled + elif self._config_store: + enabled = self._config_store.get_config_variable( + 'endpoint_discovery_enabled' + ) + + enabled = self._normalize_endpoint_discovery_config(enabled) + if enabled and self._requires_endpoint_discovery(client, enabled): + discover = enabled is True + manager = EndpointDiscoveryManager( + client, always_discover=discover + ) + handler = EndpointDiscoveryHandler(manager) + handler.register(events, service_id) + else: + events.register( + 'before-parameter-build', + block_endpoint_discovery_required_operations, + ) + + def _normalize_endpoint_discovery_config(self, enabled): + """Config must either be a boolean-string or string-literal 'auto'""" + if isinstance(enabled, str): + enabled = enabled.lower().strip() + if enabled == 'auto': + return enabled + elif enabled in ('true', 'false'): + return ensure_boolean(enabled) + elif isinstance(enabled, bool): + return enabled + + raise InvalidEndpointDiscoveryConfigurationError(config_value=enabled) + + def _requires_endpoint_discovery(self, client, enabled): + if enabled == "auto": + return client.meta.service_model.endpoint_discovery_required + return enabled + + def _register_eventbridge_events( + self, client, endpoint_bridge, endpoint_url + ): + if client.meta.service_model.service_name != 'events': + return + EventbridgeSignerSetter( + endpoint_resolver=self._endpoint_resolver, + region=client.meta.region_name, + endpoint_url=endpoint_url, + ).register(client.meta.events) + + def _register_s3express_events( + self, + client, + endpoint_bridge=None, + endpoint_url=None, + client_config=None, + scoped_config=None, + ): + if client.meta.service_model.service_name != 's3': + return + S3ExpressIdentityResolver(client, RefreshableCredentials).register() + + def _register_s3_events( + self, + client, + endpoint_bridge, + endpoint_url, + client_config, + scoped_config, + ): + if client.meta.service_model.service_name != 's3': + return + S3RegionRedirectorv2(None, client).register() + self._set_s3_presign_signature_version( + client.meta, client_config, scoped_config + ) + client.meta.events.register( + 'before-parameter-build.s3', self._inject_s3_input_parameters + ) + + def _register_s3_control_events( + self, + client, + endpoint_bridge=None, + endpoint_url=None, + client_config=None, + scoped_config=None, + ): + if client.meta.service_model.service_name != 's3control': + return + S3ControlArnParamHandlerv2().register(client.meta.events) + + def _set_s3_presign_signature_version( + self, client_meta, client_config, scoped_config + ): + # This will return the manually configured signature version, or None + # if none was manually set. If a customer manually sets the signature + # version, we always want to use what they set. + provided_signature_version = _get_configured_signature_version( + 's3', client_config, scoped_config + ) + if provided_signature_version is not None: + return + + # Check to see if the region is a region that we know about. If we + # don't know about a region, then we can safely assume it's a new + # region that is sigv4 only, since all new S3 regions only allow sigv4. + # The only exception is aws-global. This is a pseudo-region for the + # global endpoint, we should respect the signature versions it + # supports, which includes v2. + regions = self._endpoint_resolver.get_available_endpoints( + 's3', client_meta.partition + ) + if ( + client_meta.region_name != 'aws-global' + and client_meta.region_name not in regions + ): + return + + # If it is a region we know about, we want to default to sigv2, so here + # we check to see if it is available. + endpoint = self._endpoint_resolver.construct_endpoint( + 's3', client_meta.region_name + ) + signature_versions = endpoint['signatureVersions'] + if 's3' not in signature_versions: + return + + # We now know that we're in a known region that supports sigv2 and + # the customer hasn't set a signature version so we default the + # signature version to sigv2. + client_meta.events.register( + 'choose-signer.s3', self._default_s3_presign_to_sigv2 + ) + + def _inject_s3_input_parameters(self, params, context, **kwargs): + context['input_params'] = {} + inject_parameters = ('Bucket', 'Delete', 'Key', 'Prefix') + for inject_parameter in inject_parameters: + if inject_parameter in params: + context['input_params'][inject_parameter] = params[ + inject_parameter + ] + + def _default_s3_presign_to_sigv2(self, signature_version, **kwargs): + """ + Returns the 's3' (sigv2) signer if presigning an s3 request. This is + intended to be used to set the default signature version for the signer + to sigv2. Situations where an asymmetric signature is required are the + exception, for example MRAP needs v4a. + + :type signature_version: str + :param signature_version: The current client signature version. + + :type signing_name: str + :param signing_name: The signing name of the service. + + :return: 's3' if the request is an s3 presign request, None otherwise + """ + if signature_version.startswith('v4a'): + return + + if signature_version.startswith('v4-s3express'): + return signature_version + + for suffix in ['-query', '-presign-post']: + if signature_version.endswith(suffix): + return f's3{suffix}' + + def _register_importexport_events( + self, + client, + endpoint_bridge=None, + endpoint_url=None, + client_config=None, + scoped_config=None, + ): + if client.meta.service_model.service_name != 'importexport': + return + self._set_importexport_signature_version( + client.meta, client_config, scoped_config + ) + + def _set_importexport_signature_version( + self, client_meta, client_config, scoped_config + ): + # This will return the manually configured signature version, or None + # if none was manually set. If a customer manually sets the signature + # version, we always want to use what they set. + configured_signature_version = _get_configured_signature_version( + 'importexport', client_config, scoped_config + ) + if configured_signature_version is not None: + return + + # importexport has a modeled signatureVersion of v2, but we + # previously switched to v4 via endpoint.json before endpoint rulesets. + # Override the model's signatureVersion for backwards compatability. + client_meta.events.register( + 'choose-signer.importexport', self._default_signer_to_sigv4 + ) + + def _default_signer_to_sigv4(self, signature_version, **kwargs): + return 'v4' + + def _get_client_args( + self, + service_model, + region_name, + is_secure, + endpoint_url, + verify, + credentials, + scoped_config, + client_config, + endpoint_bridge, + auth_token, + endpoints_ruleset_data, + partition_data, + ): + args_creator = ClientArgsCreator( + self._event_emitter, + self._user_agent, + self._response_parser_factory, + self._loader, + self._exceptions_factory, + config_store=self._config_store, + user_agent_creator=self._user_agent_creator, + ) + return args_creator.get_client_args( + service_model, + region_name, + is_secure, + endpoint_url, + verify, + credentials, + scoped_config, + client_config, + endpoint_bridge, + auth_token, + endpoints_ruleset_data, + partition_data, + ) + + def _create_methods(self, service_model): + op_dict = {} + for operation_name in service_model.operation_names: + py_operation_name = xform_name(operation_name) + op_dict[py_operation_name] = self._create_api_method( + py_operation_name, operation_name, service_model + ) + return op_dict + + def _create_name_mapping(self, service_model): + # py_name -> OperationName, for every operation available + # for a service. + mapping = {} + for operation_name in service_model.operation_names: + py_operation_name = xform_name(operation_name) + mapping[py_operation_name] = operation_name + return mapping + + def _create_api_method( + self, py_operation_name, operation_name, service_model + ): + def _api_call(self, *args, **kwargs): + # We're accepting *args so that we can give a more helpful + # error message than TypeError: _api_call takes exactly + # 1 argument. + if args: + raise TypeError( + f"{py_operation_name}() only accepts keyword arguments." + ) + # The "self" in this scope is referring to the BaseClient. + return self._make_api_call(operation_name, kwargs) + + _api_call.__name__ = str(py_operation_name) + + # Add the docstring to the client method + operation_model = service_model.operation_model(operation_name) + docstring = ClientMethodDocstring( + operation_model=operation_model, + method_name=operation_name, + event_emitter=self._event_emitter, + method_description=operation_model.documentation, + example_prefix=f'response = client.{py_operation_name}', + include_signature=False, + ) + _api_call.__doc__ = docstring + return _api_call + + def _evaluate_client_specific_token(self, signing_name): + # Resolves an auth_token for the given signing_name. + # Returns None if no resolver is set or if resolution fails. + resolver = self._auth_token_resolver + if not resolver or not signing_name: + return None + + return resolver(signing_name=signing_name) + + +class ClientEndpointBridge: + """Bridges endpoint data and client creation + + This class handles taking out the relevant arguments from the endpoint + resolver and determining which values to use, taking into account any + client configuration options and scope configuration options. + + This class also handles determining what, if any, region to use if no + explicit region setting is provided. For example, Amazon S3 client will + utilize "us-east-1" by default if no region can be resolved.""" + + DEFAULT_ENDPOINT = '{service}.{region}.amazonaws.com' + _DUALSTACK_CUSTOMIZED_SERVICES = ['s3', 's3-control'] + + def __init__( + self, + endpoint_resolver, + scoped_config=None, + client_config=None, + default_endpoint=None, + service_signing_name=None, + config_store=None, + service_signature_version=None, + ): + self.service_signing_name = service_signing_name + self.endpoint_resolver = endpoint_resolver + self.scoped_config = scoped_config + self.client_config = client_config + self.default_endpoint = default_endpoint or self.DEFAULT_ENDPOINT + self.config_store = config_store + self.service_signature_version = service_signature_version + + def resolve( + self, service_name, region_name=None, endpoint_url=None, is_secure=True + ): + region_name = self._check_default_region(service_name, region_name) + use_dualstack_endpoint = self._resolve_use_dualstack_endpoint( + service_name + ) + use_fips_endpoint = self._resolve_endpoint_variant_config_var( + 'use_fips_endpoint' + ) + resolved = self.endpoint_resolver.construct_endpoint( + service_name, + region_name, + use_dualstack_endpoint=use_dualstack_endpoint, + use_fips_endpoint=use_fips_endpoint, + ) + + # If we can't resolve the region, we'll attempt to get a global + # endpoint for non-regionalized services (iam, route53, etc) + if not resolved: + # TODO: fallback partition_name should be configurable in the + # future for users to define as needed. + resolved = self.endpoint_resolver.construct_endpoint( + service_name, + region_name, + partition_name='aws', + use_dualstack_endpoint=use_dualstack_endpoint, + use_fips_endpoint=use_fips_endpoint, + ) + + if resolved: + return self._create_endpoint( + resolved, service_name, region_name, endpoint_url, is_secure + ) + else: + return self._assume_endpoint( + service_name, region_name, endpoint_url, is_secure + ) + + def resolver_uses_builtin_data(self): + return self.endpoint_resolver.uses_builtin_data + + def _check_default_region(self, service_name, region_name): + if region_name is not None: + return region_name + # Use the client_config region if no explicit region was provided. + if self.client_config and self.client_config.region_name is not None: + return self.client_config.region_name + + def _create_endpoint( + self, resolved, service_name, region_name, endpoint_url, is_secure + ): + region_name, signing_region = self._pick_region_values( + resolved, region_name, endpoint_url + ) + if endpoint_url is None: + endpoint_url = self._make_url( + resolved.get('hostname'), + is_secure, + resolved.get('protocols', []), + ) + signature_version = self._resolve_signature_version( + service_name, resolved + ) + signing_name = self._resolve_signing_name(service_name, resolved) + return self._create_result( + service_name=service_name, + region_name=region_name, + signing_region=signing_region, + signing_name=signing_name, + endpoint_url=endpoint_url, + metadata=resolved, + signature_version=signature_version, + ) + + def _resolve_endpoint_variant_config_var(self, config_var): + client_config = self.client_config + config_val = False + + # Client configuration arg has precedence + if client_config and getattr(client_config, config_var) is not None: + return getattr(client_config, config_var) + elif self.config_store is not None: + # Check config store + config_val = self.config_store.get_config_variable(config_var) + return config_val + + def _resolve_use_dualstack_endpoint(self, service_name): + s3_dualstack_mode = self._is_s3_dualstack_mode(service_name) + if s3_dualstack_mode is not None: + return s3_dualstack_mode + return self._resolve_endpoint_variant_config_var( + 'use_dualstack_endpoint' + ) + + def _is_s3_dualstack_mode(self, service_name): + if service_name not in self._DUALSTACK_CUSTOMIZED_SERVICES: + return None + # TODO: This normalization logic is duplicated from the + # ClientArgsCreator class. Consolidate everything to + # ClientArgsCreator. _resolve_signature_version also has similarly + # duplicated logic. + client_config = self.client_config + if ( + client_config is not None + and client_config.s3 is not None + and 'use_dualstack_endpoint' in client_config.s3 + ): + # Client config trumps scoped config. + return client_config.s3['use_dualstack_endpoint'] + if self.scoped_config is not None: + enabled = self.scoped_config.get('s3', {}).get( + 'use_dualstack_endpoint' + ) + if enabled in [True, 'True', 'true']: + return True + + def _assume_endpoint( + self, service_name, region_name, endpoint_url, is_secure + ): + if endpoint_url is None: + # Expand the default hostname URI template. + hostname = self.default_endpoint.format( + service=service_name, region=region_name + ) + endpoint_url = self._make_url( + hostname, is_secure, ['http', 'https'] + ) + logger.debug( + 'Assuming an endpoint for %s, %s: %s', + service_name, + region_name, + endpoint_url, + ) + # We still want to allow the user to provide an explicit version. + signature_version = self._resolve_signature_version( + service_name, {'signatureVersions': ['v4']} + ) + signing_name = self._resolve_signing_name(service_name, resolved={}) + return self._create_result( + service_name=service_name, + region_name=region_name, + signing_region=region_name, + signing_name=signing_name, + signature_version=signature_version, + endpoint_url=endpoint_url, + metadata={}, + ) + + def _create_result( + self, + service_name, + region_name, + signing_region, + signing_name, + endpoint_url, + signature_version, + metadata, + ): + return { + 'service_name': service_name, + 'region_name': region_name, + 'signing_region': signing_region, + 'signing_name': signing_name, + 'endpoint_url': endpoint_url, + 'signature_version': signature_version, + 'metadata': metadata, + } + + def _make_url(self, hostname, is_secure, supported_protocols): + if is_secure and 'https' in supported_protocols: + scheme = 'https' + else: + scheme = 'http' + return f'{scheme}://{hostname}' + + def _resolve_signing_name(self, service_name, resolved): + # CredentialScope overrides everything else. + if ( + 'credentialScope' in resolved + and 'service' in resolved['credentialScope'] + ): + return resolved['credentialScope']['service'] + # Use the signingName from the model if present. + if self.service_signing_name: + return self.service_signing_name + # Just assume is the same as the service name. + return service_name + + def _pick_region_values(self, resolved, region_name, endpoint_url): + signing_region = region_name + if endpoint_url is None: + # Do not use the region name or signing name from the resolved + # endpoint if the user explicitly provides an endpoint_url. This + # would happen if we resolve to an endpoint where the service has + # a "defaults" section that overrides all endpoint with a single + # hostname and credentialScope. This has been the case historically + # for how STS has worked. The only way to resolve an STS endpoint + # was to provide a region_name and an endpoint_url. In that case, + # we would still resolve an endpoint, but we would not use the + # resolved endpointName or signingRegion because we want to allow + # custom endpoints. + region_name = resolved['endpointName'] + signing_region = region_name + if ( + 'credentialScope' in resolved + and 'region' in resolved['credentialScope'] + ): + signing_region = resolved['credentialScope']['region'] + return region_name, signing_region + + def _resolve_signature_version(self, service_name, resolved): + configured_version = _get_configured_signature_version( + service_name, self.client_config, self.scoped_config + ) + if configured_version is not None: + return configured_version + + # These have since added the "auth" key to the service model + # with "aws.auth#sigv4", but preserve existing behavior from + # when we preferred endpoints.json over the service models + if service_name in ('s3', 's3-control'): + return 's3v4' + + if self.service_signature_version is not None: + # Prefer the service model + potential_versions = [self.service_signature_version] + else: + # Fall back to endpoints.json to preserve existing behavior, which + # may be useful for users who have custom service models + potential_versions = resolved.get('signatureVersions', []) + # This was added for the V2 -> V4 transition, + # for services that added V4 after V2 in endpoints.json + if 'v4' in potential_versions: + return 'v4' + # Now just iterate over the signature versions in order until we + # find the first one that is known to Botocore. + for known in potential_versions: + if known in AUTH_TYPE_MAPS: + return known + + raise UnknownSignatureVersionError( + signature_version=potential_versions + ) + + +class BaseClient: + # This is actually reassigned with the py->op_name mapping + # when the client creator creates the subclass. This value is used + # because calls such as client.get_paginator('list_objects') use the + # snake_case name, but we need to know the ListObjects form. + # xform_name() does the ListObjects->list_objects conversion, but + # we need the reverse mapping here. + _PY_TO_OP_NAME = {} + + def __init__( + self, + serializer, + endpoint, + response_parser, + event_emitter, + request_signer, + service_model, + loader, + client_config, + partition, + exceptions_factory, + endpoint_ruleset_resolver=None, + user_agent_creator=None, + ): + self._serializer = serializer + self._endpoint = endpoint + self._ruleset_resolver = endpoint_ruleset_resolver + self._response_parser = response_parser + self._request_signer = request_signer + self._cache = {} + self._loader = loader + self._client_config = client_config + self.meta = ClientMeta( + event_emitter, + self._client_config, + endpoint.host, + service_model, + self._PY_TO_OP_NAME, + partition, + ) + self._exceptions_factory = exceptions_factory + self._exceptions = None + self._user_agent_creator = user_agent_creator + if self._user_agent_creator is None: + self._user_agent_creator = ( + UserAgentString.from_environment().with_client_config( + self._client_config + ) + ) + self._register_handlers() + + def __getattr__(self, item): + service_id = self._service_model.service_id.hyphenize() + event_name = f'getattr.{service_id}.{item}' + + handler, event_response = self.meta.events.emit_until_response( + event_name, client=self + ) + + if event_response is not None: + return event_response + + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + + def close(self): + """Closes underlying endpoint connections.""" + self._endpoint.close() + + def _register_handlers(self): + # Register the handler required to sign requests. + service_id = self.meta.service_model.service_id.hyphenize() + self.meta.events.register( + f"request-created.{service_id}", self._request_signer.handler + ) + # Rebuild user agent string right before request is sent + # to ensure all registered features are included. + self.meta.events.register_last( + f"request-created.{service_id}", + self._user_agent_creator.rebuild_and_replace_user_agent_handler, + ) + + @property + def _service_model(self): + return self.meta.service_model + + @with_current_context() + def _make_api_call(self, operation_name, api_params): + operation_model = self._service_model.operation_model(operation_name) + service_name = self._service_model.service_name + history_recorder.record( + 'API_CALL', + { + 'service': service_name, + 'operation': operation_name, + 'params': api_params, + }, + ) + if operation_model.deprecated: + logger.debug( + 'Warning: %s.%s() is deprecated', service_name, operation_name + ) + # If the operation has the `auth` property and the client has a + # configured auth scheme preference, use both to compute the + # auth type. Otherwise, fallback to auth/auth_type resolution. + if operation_model.auth and self.meta.config.auth_scheme_preference: + preferred_schemes = self.meta.config.auth_scheme_preference.split( + ',' + ) + auth_type = resolve_auth_scheme_preference( + preferred_schemes, operation_model.auth + ) + else: + auth_type = operation_model.resolved_auth_type + request_context = { + 'client_region': self.meta.region_name, + 'client_config': self.meta.config, + 'has_streaming_input': operation_model.has_streaming_input, + 'auth_type': auth_type, + 'unsigned_payload': operation_model.unsigned_payload, + 'auth_options': self._service_model.metadata.get('auth'), + } + + api_params = self._emit_api_params( + api_params=api_params, + operation_model=operation_model, + context=request_context, + ) + ( + endpoint_url, + additional_headers, + properties, + ) = self._resolve_endpoint_ruleset( + operation_model, api_params, request_context + ) + if properties: + # Pass arbitrary endpoint info with the Request + # for use during construction. + request_context['endpoint_properties'] = properties + request_dict = self._convert_to_request_dict( + api_params=api_params, + operation_model=operation_model, + endpoint_url=endpoint_url, + context=request_context, + headers=additional_headers, + ) + resolve_checksum_context(request_dict, operation_model, api_params) + + service_id = self._service_model.service_id.hyphenize() + handler, event_response = self.meta.events.emit_until_response( + f'before-call.{service_id}.{operation_name}', + model=operation_model, + params=request_dict, + request_signer=self._request_signer, + context=request_context, + ) + + if event_response is not None: + http, parsed_response = event_response + else: + maybe_compress_request( + self.meta.config, request_dict, operation_model + ) + apply_request_checksum(request_dict) + http, parsed_response = self._make_request( + operation_model, request_dict, request_context + ) + + self.meta.events.emit( + f'after-call.{service_id}.{operation_name}', + http_response=http, + parsed=parsed_response, + model=operation_model, + context=request_context, + ) + + if http.status_code >= 300: + error_info = parsed_response.get("Error", {}) + error_code = request_context.get( + 'error_code_override' + ) or error_info.get("Code") + error_class = self.exceptions.from_code(error_code) + raise error_class(parsed_response, operation_name) + else: + return parsed_response + + def _make_request(self, operation_model, request_dict, request_context): + try: + return self._endpoint.make_request(operation_model, request_dict) + except Exception as e: + self.meta.events.emit( + f'after-call-error.{self._service_model.service_id.hyphenize()}.{operation_model.name}', + exception=e, + context=request_context, + ) + raise + + def _convert_to_request_dict( + self, + api_params, + operation_model, + endpoint_url, + context=None, + headers=None, + set_user_agent_header=True, + ): + request_dict = self._serializer.serialize_to_request( + api_params, operation_model + ) + if not self._client_config.inject_host_prefix: + request_dict.pop('host_prefix', None) + if headers is not None: + request_dict['headers'].update(headers) + if set_user_agent_header: + user_agent = self._user_agent_creator.to_string() + else: + user_agent = None + prepare_request_dict( + request_dict, + endpoint_url=endpoint_url, + user_agent=user_agent, + context=context, + ) + return request_dict + + def _emit_api_params(self, api_params, operation_model, context): + # Given the API params provided by the user and the operation_model + # we can serialize the request to a request_dict. + operation_name = operation_model.name + + # Emit an event that allows users to modify the parameters at the + # beginning of the method. It allows handlers to modify existing + # parameters or return a new set of parameters to use. + service_id = self._service_model.service_id.hyphenize() + responses = self.meta.events.emit( + f'provide-client-params.{service_id}.{operation_name}', + params=api_params, + model=operation_model, + context=context, + ) + api_params = first_non_none_response(responses, default=api_params) + + self.meta.events.emit( + f'before-parameter-build.{service_id}.{operation_name}', + params=api_params, + model=operation_model, + context=context, + ) + return api_params + + def _resolve_endpoint_ruleset( + self, + operation_model, + params, + request_context, + ignore_signing_region=False, + ): + """Returns endpoint URL and list of additional headers returned from + EndpointRulesetResolver for the given operation and params. If the + ruleset resolver is not available, for example because the service has + no endpoints ruleset file, the legacy endpoint resolver's value is + returned. + + Use ignore_signing_region for generating presigned URLs or any other + situation where the signing region information from the ruleset + resolver should be ignored. + + Returns tuple of URL and headers dictionary. Additionally, the + request_context dict is modified in place with any signing information + returned from the ruleset resolver. + """ + if self._ruleset_resolver is None: + endpoint_url = self.meta.endpoint_url + additional_headers = {} + endpoint_properties = {} + else: + endpoint_info = self._ruleset_resolver.construct_endpoint( + operation_model=operation_model, + call_args=params, + request_context=request_context, + ) + endpoint_url = endpoint_info.url + additional_headers = endpoint_info.headers + endpoint_properties = endpoint_info.properties + # If authSchemes is present, overwrite default auth type and + # signing context derived from service model. + auth_schemes = endpoint_info.properties.get('authSchemes') + if auth_schemes is not None: + auth_info = self._ruleset_resolver.auth_schemes_to_signing_ctx( + auth_schemes + ) + auth_type, signing_context = auth_info + request_context['auth_type'] = auth_type + if 'region' in signing_context and ignore_signing_region: + del signing_context['region'] + if 'signing' in request_context: + request_context['signing'].update(signing_context) + else: + request_context['signing'] = signing_context + + return endpoint_url, additional_headers, endpoint_properties + + def get_paginator(self, operation_name): + """Create a paginator for an operation. + + :type operation_name: string + :param operation_name: The operation name. This is the same name + as the method name on the client. For example, if the + method name is ``create_foo``, and you'd normally invoke the + operation as ``client.create_foo(**kwargs)``, if the + ``create_foo`` operation can be paginated, you can use the + call ``client.get_paginator("create_foo")``. + + :raise OperationNotPageableError: Raised if the operation is not + pageable. You can use the ``client.can_paginate`` method to + check if an operation is pageable. + + :rtype: ``botocore.paginate.Paginator`` + :return: A paginator object. + + """ + if not self.can_paginate(operation_name): + raise OperationNotPageableError(operation_name=operation_name) + else: + actual_operation_name = self._PY_TO_OP_NAME[operation_name] + + # Create a new paginate method that will serve as a proxy to + # the underlying Paginator.paginate method. This is needed to + # attach a docstring to the method. + def paginate(self, **kwargs): + return Paginator.paginate(self, **kwargs) + + paginator_config = self._cache['page_config'][ + actual_operation_name + ] + # Add the docstring for the paginate method. + paginate.__doc__ = PaginatorDocstring( + paginator_name=actual_operation_name, + event_emitter=self.meta.events, + service_model=self.meta.service_model, + paginator_config=paginator_config, + include_signature=False, + ) + + # Rename the paginator class based on the type of paginator. + service_module_name = get_service_module_name( + self.meta.service_model + ) + paginator_class_name = ( + f"{service_module_name}.Paginator.{actual_operation_name}" + ) + + # Create the new paginator class + documented_paginator_cls = type( + paginator_class_name, (Paginator,), {'paginate': paginate} + ) + + operation_model = self._service_model.operation_model( + actual_operation_name + ) + paginator = documented_paginator_cls( + getattr(self, operation_name), + paginator_config, + operation_model, + ) + return paginator + + def can_paginate(self, operation_name): + """Check if an operation can be paginated. + + :type operation_name: string + :param operation_name: The operation name. This is the same name + as the method name on the client. For example, if the + method name is ``create_foo``, and you'd normally invoke the + operation as ``client.create_foo(**kwargs)``, if the + ``create_foo`` operation can be paginated, you can use the + call ``client.get_paginator("create_foo")``. + + :return: ``True`` if the operation can be paginated, + ``False`` otherwise. + + """ + if 'page_config' not in self._cache: + try: + page_config = self._loader.load_service_model( + self._service_model.service_name, + 'paginators-1', + self._service_model.api_version, + )['pagination'] + self._cache['page_config'] = page_config + except DataNotFoundError: + self._cache['page_config'] = {} + actual_operation_name = self._PY_TO_OP_NAME[operation_name] + return actual_operation_name in self._cache['page_config'] + + def _get_waiter_config(self): + if 'waiter_config' not in self._cache: + try: + waiter_config = self._loader.load_service_model( + self._service_model.service_name, + 'waiters-2', + self._service_model.api_version, + ) + self._cache['waiter_config'] = waiter_config + except DataNotFoundError: + self._cache['waiter_config'] = {} + return self._cache['waiter_config'] + + def get_waiter(self, waiter_name): + """Returns an object that can wait for some condition. + + :type waiter_name: str + :param waiter_name: The name of the waiter to get. See the waiters + section of the service docs for a list of available waiters. + + :returns: The specified waiter object. + :rtype: ``botocore.waiter.Waiter`` + """ + config = self._get_waiter_config() + if not config: + raise ValueError(f"Waiter does not exist: {waiter_name}") + model = waiter.WaiterModel(config) + mapping = {} + for name in model.waiter_names: + mapping[xform_name(name)] = name + if waiter_name not in mapping: + raise ValueError(f"Waiter does not exist: {waiter_name}") + + return waiter.create_waiter_with_client( + mapping[waiter_name], model, self + ) + + @CachedProperty + def waiter_names(self): + """Returns a list of all available waiters.""" + config = self._get_waiter_config() + if not config: + return [] + model = waiter.WaiterModel(config) + # Waiter configs is a dict, we just want the waiter names + # which are the keys in the dict. + return [xform_name(name) for name in model.waiter_names] + + @property + def exceptions(self): + if self._exceptions is None: + self._exceptions = self._load_exceptions() + return self._exceptions + + def _load_exceptions(self): + return self._exceptions_factory.create_client_exceptions( + self._service_model + ) + + def _get_credentials(self): + """ + This private interface is subject to abrupt breaking changes, including + removal, in any botocore release. + """ + return self._request_signer._credentials + + +class ClientMeta: + """Holds additional client methods. + + This class holds additional information for clients. It exists for + two reasons: + + * To give advanced functionality to clients + * To namespace additional client attributes from the operation + names which are mapped to methods at runtime. This avoids + ever running into collisions with operation names. + + """ + + def __init__( + self, + events, + client_config, + endpoint_url, + service_model, + method_to_api_mapping, + partition, + ): + self.events = events + self._client_config = client_config + self._endpoint_url = endpoint_url + self._service_model = service_model + self._method_to_api_mapping = method_to_api_mapping + self._partition = partition + + @property + def service_model(self): + return self._service_model + + @property + def region_name(self): + return self._client_config.region_name + + @property + def endpoint_url(self): + return self._endpoint_url + + @property + def config(self): + return self._client_config + + @property + def method_to_api_mapping(self): + return self._method_to_api_mapping + + @property + def partition(self): + return self._partition + + +def _get_configured_signature_version( + service_name, client_config, scoped_config +): + """ + Gets the manually configured signature version. + + :returns: the customer configured signature version, or None if no + signature version was configured. + """ + # Client config overrides everything. + if client_config and client_config.signature_version is not None: + return client_config.signature_version + + # Scoped config overrides picking from the endpoint metadata. + if scoped_config is not None: + # A given service may have service specific configuration in the + # config file, so we need to check there as well. + service_config = scoped_config.get(service_name) + if service_config is not None and isinstance(service_config, dict): + version = service_config.get('signature_version') + if version: + logger.debug( + "Switching signature version for service %s " + "to version %s based on config file override.", + service_name, + version, + ) + return version + return None diff --git a/venv/lib/python3.12/site-packages/botocore/compat.py b/venv/lib/python3.12/site-packages/botocore/compat.py new file mode 100644 index 0000000..62a265c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/compat.py @@ -0,0 +1,371 @@ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import copy +import datetime +import sys +import inspect +import warnings +import hashlib +from http.client import HTTPMessage +import logging +import shlex +import re +import os +from collections import OrderedDict +from collections.abc import MutableMapping +from math import floor + +from botocore.vendored import six +from botocore.exceptions import MD5UnavailableError +from dateutil.tz import tzlocal +from urllib3 import exceptions + +logger = logging.getLogger(__name__) + + +class HTTPHeaders(HTTPMessage): + pass + +from urllib.parse import ( + quote, + urlencode, + unquote, + unquote_plus, + urlparse, + urlsplit, + urlunsplit, + urljoin, + parse_qsl, + parse_qs, +) +from http.client import HTTPResponse +from io import IOBase as _IOBase +from base64 import encodebytes +from email.utils import formatdate +from itertools import zip_longest +file_type = _IOBase +zip = zip + +# In python3, unquote takes a str() object, url decodes it, +# then takes the bytestring and decodes it to utf-8. +unquote_str = unquote_plus + +def set_socket_timeout(http_response, timeout): + """Set the timeout of the socket from an HTTPResponse. + + :param http_response: An instance of ``httplib.HTTPResponse`` + + """ + http_response._fp.fp.raw._sock.settimeout(timeout) + +def accepts_kwargs(func): + return inspect.getfullargspec(func)[2] + +def ensure_unicode(s, encoding=None, errors=None): + # NOOP in Python 3, because every string is already unicode + return s + +def ensure_bytes(s, encoding='utf-8', errors='strict'): + if isinstance(s, str): + return s.encode(encoding, errors) + if isinstance(s, bytes): + return s + raise ValueError(f"Expected str or bytes, received {type(s)}.") + + +import xml.etree.ElementTree as ETree +XMLParseError = ETree.ParseError + +import json + + +def filter_ssl_warnings(): + # Ignore warnings related to SNI as it is not being used in validations. + warnings.filterwarnings( + 'ignore', + message="A true SSLContext object is not available.*", + category=exceptions.InsecurePlatformWarning, + module=r".*urllib3\.util\.ssl_", + ) + + +@classmethod +def from_dict(cls, d): + new_instance = cls() + for key, value in d.items(): + new_instance[key] = value + return new_instance + + +@classmethod +def from_pairs(cls, pairs): + new_instance = cls() + for key, value in pairs: + new_instance[key] = value + return new_instance + + +HTTPHeaders.from_dict = from_dict +HTTPHeaders.from_pairs = from_pairs + + +def copy_kwargs(kwargs): + """ + This used to be a compat shim for 2.6 but is now just an alias. + """ + copy_kwargs = copy.copy(kwargs) + return copy_kwargs + + +def total_seconds(delta): + """ + Returns the total seconds in a ``datetime.timedelta``. + + This used to be a compat shim for 2.6 but is now just an alias. + + :param delta: The timedelta object + :type delta: ``datetime.timedelta`` + """ + return delta.total_seconds() + + +# Checks to see if md5 is available on this system. A given system might not +# have access to it for various reasons, such as FIPS mode being enabled. +try: + hashlib.md5(usedforsecurity=False) + MD5_AVAILABLE = True +except (AttributeError, ValueError): + MD5_AVAILABLE = False + + +def get_md5(*args, **kwargs): + """ + Attempts to get an md5 hashing object. + + :param args: Args to pass to the MD5 constructor + :param kwargs: Key word arguments to pass to the MD5 constructor + :return: An MD5 hashing object if available. If it is unavailable, None + is returned if raise_error_if_unavailable is set to False. + """ + if MD5_AVAILABLE: + return hashlib.md5(*args, **kwargs) + else: + raise MD5UnavailableError() + + +def compat_shell_split(s, platform=None): + if platform is None: + platform = sys.platform + + if platform == "win32": + return _windows_shell_split(s) + else: + return shlex.split(s) + + +def _windows_shell_split(s): + """Splits up a windows command as the built-in command parser would. + + Windows has potentially bizarre rules depending on where you look. When + spawning a process via the Windows C runtime (which is what python does + when you call popen) the rules are as follows: + + https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments + + To summarize: + + * Only space and tab are valid delimiters + * Double quotes are the only valid quotes + * Backslash is interpreted literally unless it is part of a chain that + leads up to a double quote. Then the backslashes escape the backslashes, + and if there is an odd number the final backslash escapes the quote. + + :param s: The command string to split up into parts. + :return: A list of command components. + """ + if not s: + return [] + + components = [] + buff = [] + is_quoted = False + num_backslashes = 0 + for character in s: + if character == '\\': + # We can't simply append backslashes because we don't know if + # they are being used as escape characters or not. Instead we + # keep track of how many we've encountered and handle them when + # we encounter a different character. + num_backslashes += 1 + elif character == '"': + if num_backslashes > 0: + # The backslashes are in a chain leading up to a double + # quote, so they are escaping each other. + buff.append('\\' * int(floor(num_backslashes / 2))) + remainder = num_backslashes % 2 + num_backslashes = 0 + if remainder == 1: + # The number of backslashes is uneven, so they are also + # escaping the double quote, so it needs to be added to + # the current component buffer. + buff.append('"') + continue + + # We've encountered a double quote that is not escaped, + # so we toggle is_quoted. + is_quoted = not is_quoted + + # If there are quotes, then we may want an empty string. To be + # safe, we add an empty string to the buffer so that we make + # sure it sticks around if there's nothing else between quotes. + # If there is other stuff between quotes, the empty string will + # disappear during the joining process. + buff.append('') + elif character in [' ', '\t'] and not is_quoted: + # Since the backslashes aren't leading up to a quote, we put in + # the exact number of backslashes. + if num_backslashes > 0: + buff.append('\\' * num_backslashes) + num_backslashes = 0 + + # Excess whitespace is ignored, so only add the components list + # if there is anything in the buffer. + if buff: + components.append(''.join(buff)) + buff = [] + else: + # Since the backslashes aren't leading up to a quote, we put in + # the exact number of backslashes. + if num_backslashes > 0: + buff.append('\\' * num_backslashes) + num_backslashes = 0 + buff.append(character) + + # Quotes must be terminated. + if is_quoted: + raise ValueError(f"No closing quotation in string: {s}") + + # There may be some leftover backslashes, so we need to add them in. + # There's no quote so we add the exact number. + if num_backslashes > 0: + buff.append('\\' * num_backslashes) + + # Add the final component in if there is anything in the buffer. + if buff: + components.append(''.join(buff)) + + return components + + +def get_tzinfo_options(): + # Due to dateutil/dateutil#197, Windows may fail to parse times in the past + # with the system clock. We can alternatively fallback to tzwininfo when + # this happens, which will get time info from the Windows registry. + if sys.platform == 'win32': + from dateutil.tz import tzwinlocal + + return (tzlocal, tzwinlocal) + else: + return (tzlocal,) + + +# Detect if CRT is available for use +try: + import awscrt.auth + + # Allow user opt-out if needed + disabled = os.environ.get('BOTO_DISABLE_CRT', "false") + HAS_CRT = not disabled.lower() == 'true' +except ImportError: + HAS_CRT = False + + +def has_minimum_crt_version(minimum_version): + """Not intended for use outside botocore.""" + if not HAS_CRT: + return False + + crt_version_str = awscrt.__version__ + try: + crt_version_ints = map(int, crt_version_str.split(".")) + crt_version_tuple = tuple(crt_version_ints) + except (TypeError, ValueError): + return False + + return crt_version_tuple >= minimum_version + + +def get_current_datetime(remove_tzinfo=True): + """Retrieve the current timezone in UTC, with or without an explicit timezone.""" + datetime_now = datetime.datetime.now(datetime.timezone.utc) + if remove_tzinfo: + datetime_now = datetime_now.replace(tzinfo=None) + return datetime_now + + +######################################################## +# urllib3 compat backports # +######################################################## + +# Vendoring IPv6 validation regex patterns from urllib3 +# https://github.com/urllib3/urllib3/blob/7e856c0/src/urllib3/util/url.py +IPV4_PAT = r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}" +IPV4_RE = re.compile("^" + IPV4_PAT + "$") +HEX_PAT = "[0-9A-Fa-f]{1,4}" +LS32_PAT = "(?:{hex}:{hex}|{ipv4})".format(hex=HEX_PAT, ipv4=IPV4_PAT) +_subs = {"hex": HEX_PAT, "ls32": LS32_PAT} +_variations = [ + # 6( h16 ":" ) ls32 + "(?:%(hex)s:){6}%(ls32)s", + # "::" 5( h16 ":" ) ls32 + "::(?:%(hex)s:){5}%(ls32)s", + # [ h16 ] "::" 4( h16 ":" ) ls32 + "(?:%(hex)s)?::(?:%(hex)s:){4}%(ls32)s", + # [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + "(?:(?:%(hex)s:)?%(hex)s)?::(?:%(hex)s:){3}%(ls32)s", + # [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + "(?:(?:%(hex)s:){0,2}%(hex)s)?::(?:%(hex)s:){2}%(ls32)s", + # [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + "(?:(?:%(hex)s:){0,3}%(hex)s)?::%(hex)s:%(ls32)s", + # [ *4( h16 ":" ) h16 ] "::" ls32 + "(?:(?:%(hex)s:){0,4}%(hex)s)?::%(ls32)s", + # [ *5( h16 ":" ) h16 ] "::" h16 + "(?:(?:%(hex)s:){0,5}%(hex)s)?::%(hex)s", + # [ *6( h16 ":" ) h16 ] "::" + "(?:(?:%(hex)s:){0,6}%(hex)s)?::", +] + +UNRESERVED_PAT = ( + r"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._!\-~" +) +IPV6_PAT = "(?:" + "|".join([x % _subs for x in _variations]) + ")" +ZONE_ID_PAT = "(?:%25|%)(?:[" + UNRESERVED_PAT + "]|%[a-fA-F0-9]{2})+" +IPV6_ADDRZ_PAT = r"\[" + IPV6_PAT + r"(?:" + ZONE_ID_PAT + r")?\]" +IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT + "$") + +# These are the characters that are stripped by post-bpo-43882 urlparse(). +UNSAFE_URL_CHARS = frozenset('\t\r\n') + +# Detect if gzip is available for use +try: + import gzip + HAS_GZIP = True +except ImportError: + HAS_GZIP = False + +# Conditional import for awscrt EC crypto functionality +if HAS_CRT and has_minimum_crt_version((0, 28, 4)): + from awscrt.crypto import EC +else: + EC = None diff --git a/venv/lib/python3.12/site-packages/botocore/compress.py b/venv/lib/python3.12/site-packages/botocore/compress.py new file mode 100644 index 0000000..d3dac6f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/compress.py @@ -0,0 +1,128 @@ +# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +""" +NOTE: All functions in this module are considered private and are +subject to abrupt breaking changes. Please do not use them directly. + +""" + +import io +import logging +from gzip import GzipFile +from gzip import compress as gzip_compress + +from botocore.compat import urlencode +from botocore.useragent import register_feature_id +from botocore.utils import determine_content_length + +logger = logging.getLogger(__name__) + + +def maybe_compress_request(config, request_dict, operation_model): + """Attempt to compress the request body using the modeled encodings.""" + if _should_compress_request(config, request_dict, operation_model): + for encoding in operation_model.request_compression['encodings']: + encoder = COMPRESSION_MAPPING.get(encoding) + if encoder is not None: + logger.debug('Compressing request with %s encoding.', encoding) + request_dict['body'] = encoder(request_dict['body']) + _set_compression_header(request_dict['headers'], encoding) + return + else: + logger.debug('Unsupported compression encoding: %s', encoding) + + +def _should_compress_request(config, request_dict, operation_model): + if ( + config.disable_request_compression is not True + and config.signature_version != 'v2' + and operation_model.request_compression is not None + ): + if not _is_compressible_type(request_dict): + body_type = type(request_dict['body']) + log_msg = 'Body type %s does not support compression.' + logger.debug(log_msg, body_type) + return False + + if operation_model.has_streaming_input: + streaming_input = operation_model.get_streaming_input() + streaming_metadata = streaming_input.metadata + return 'requiresLength' not in streaming_metadata + + body_size = _get_body_size(request_dict['body']) + min_size = config.request_min_compression_size_bytes + return min_size <= body_size + + return False + + +def _is_compressible_type(request_dict): + body = request_dict['body'] + # Coerce dict to a format compatible with compression. + if isinstance(body, dict): + body = urlencode(body, doseq=True, encoding='utf-8').encode('utf-8') + request_dict['body'] = body + is_supported_type = isinstance(body, (str, bytes, bytearray)) + return is_supported_type or hasattr(body, 'read') + + +def _get_body_size(body): + size = determine_content_length(body) + if size is None: + logger.debug( + 'Unable to get length of the request body: %s. ' + 'Skipping compression.', + body, + ) + size = 0 + return size + + +def _gzip_compress_body(body): + register_feature_id('GZIP_REQUEST_COMPRESSION') + if isinstance(body, str): + return gzip_compress(body.encode('utf-8')) + elif isinstance(body, (bytes, bytearray)): + return gzip_compress(body) + elif hasattr(body, 'read'): + if hasattr(body, 'seek') and hasattr(body, 'tell'): + current_position = body.tell() + compressed_obj = _gzip_compress_fileobj(body) + body.seek(current_position) + return compressed_obj + return _gzip_compress_fileobj(body) + + +def _gzip_compress_fileobj(body): + compressed_obj = io.BytesIO() + with GzipFile(fileobj=compressed_obj, mode='wb') as gz: + while True: + chunk = body.read(8192) + if not chunk: + break + if isinstance(chunk, str): + chunk = chunk.encode('utf-8') + gz.write(chunk) + compressed_obj.seek(0) + return compressed_obj + + +def _set_compression_header(headers, encoding): + ce_header = headers.get('Content-Encoding') + if ce_header is None: + headers['Content-Encoding'] = encoding + else: + headers['Content-Encoding'] = f'{ce_header},{encoding}' + + +COMPRESSION_MAPPING = {'gzip': _gzip_compress_body} diff --git a/venv/lib/python3.12/site-packages/botocore/config.py b/venv/lib/python3.12/site-packages/botocore/config.py new file mode 100644 index 0000000..d85a082 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/config.py @@ -0,0 +1,484 @@ +# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import copy + +from botocore.compat import OrderedDict +from botocore.endpoint import DEFAULT_TIMEOUT, MAX_POOL_CONNECTIONS +from botocore.exceptions import ( + InvalidMaxRetryAttemptsError, + InvalidRetryConfigurationError, + InvalidRetryModeError, + InvalidS3AddressingStyleError, +) + + +class Config: + """Advanced configuration for Botocore clients. + + :type region_name: str + :param region_name: The region to use in instantiating the client + + :type signature_version: str + :param signature_version: The signature version when signing requests. + + :type user_agent: str + :param user_agent: The value to use in the User-Agent header. + + :type user_agent_extra: str + :param user_agent_extra: The value to append to the current User-Agent + header value. + + :type user_agent_appid: str + :param user_agent_appid: A value that gets included in the User-Agent + string in the format "app/". Allowed characters are + ASCII alphanumerics and ``!#$%&'*+-.^_`|~``. All other characters will + be replaced by a ``-``. + + :type connect_timeout: float or int + :param connect_timeout: The time in seconds till a timeout exception is + thrown when attempting to make a connection. The default is 60 + seconds. + + :type read_timeout: float or int + :param read_timeout: The time in seconds till a timeout exception is + thrown when attempting to read from a connection. The default is + 60 seconds. + + :type parameter_validation: bool + :param parameter_validation: Whether parameter validation should occur + when serializing requests. The default is True. You can disable + parameter validation for performance reasons. Otherwise, it's + recommended to leave parameter validation enabled. + + :type max_pool_connections: int + :param max_pool_connections: The maximum number of connections to + keep in a connection pool. If this value is not set, the default + value of 10 is used. + + :type proxies: dict + :param proxies: A dictionary of proxy servers to use by protocol or + endpoint, e.g.: + ``{'http': 'foo.bar:3128', 'http://hostname': 'foo.bar:4012'}``. + The proxies are used on each request. + + :type proxies_config: dict + :param proxies_config: A dictionary of additional proxy configurations. + Valid keys are: + + * ``proxy_ca_bundle`` -- The path to a custom certificate bundle to use + when establishing SSL/TLS connections with proxy. + + * ``proxy_client_cert`` -- The path to a certificate for proxy + TLS client authentication. + + When a string is provided it is treated as a path to a proxy client + certificate. When a two element tuple is provided, it will be + interpreted as the path to the client certificate, and the path + to the certificate key. + + * ``proxy_use_forwarding_for_https`` -- For HTTPS proxies, + forward your requests to HTTPS destinations with an absolute + URI. We strongly recommend you only use this option with + trusted or corporate proxies. Value must be boolean. + + :type s3: dict + :param s3: A dictionary of S3 specific configurations. + Valid keys are: + + * ``use_accelerate_endpoint`` -- Refers to whether to use the S3 + Accelerate endpoint. The value must be a boolean. If True, the + client will use the S3 Accelerate endpoint. If the S3 Accelerate + endpoint is being used then the addressing style will always + be virtual. + + * ``payload_signing_enabled`` -- Refers to whether or not to SHA256 + sign SigV4 payloads. For operations that support request checksums, + this only applies when ``request_checksum_calculation`` is set to + ``when_required``. Otherwise, this is disabled for + streaming uploads (UploadPart and PutObject) by default. + + * ``addressing_style`` -- Refers to the style in which to address + s3 endpoints. Values must be a string that equals one of: + + * ``auto`` -- Addressing style is chosen for user. Depending + on the configuration of client, the endpoint may be addressed in + the virtual or the path style. Note that this is the default + behavior if no style is specified. + + * ``virtual`` -- Addressing style is always virtual. The name of the + bucket must be DNS compatible or an exception will be thrown. + Endpoints will be addressed as such: ``amzn-s3-demo-bucket.s3.amazonaws.com`` + + * ``path`` -- Addressing style is always by path. Endpoints will be + addressed as such: ``s3.amazonaws.com/amzn-s3-demo-bucket`` + + * ``us_east_1_regional_endpoint`` -- Refers to what S3 endpoint to use + when the region is configured to be us-east-1. Values must be a + string that equals: + + * ``regional`` -- Use the us-east-1.amazonaws.com endpoint if the + client is configured to use the us-east-1 region. + + * ``legacy`` -- Use the s3.amazonaws.com endpoint if the client is + configured to use the us-east-1 region. This is the default if + the configuration option is not specified. + + * ``s3_disable_express_session_auth`` -- Refers to whether to use S3 + Express session authentication. The value must be a boolean. If True, the + client will NOT use S3 Express session authentication. + + Defaults to None. + + + :type retries: dict + :param retries: A dictionary for configuration related to retry behavior. + Valid keys are: + + * ``total_max_attempts`` -- An integer representing the maximum number of + total attempts that will be made on a single request. This includes + the initial request, so a value of 1 indicates that no requests + will be retried. If ``total_max_attempts`` and ``max_attempts`` + are both provided, ``total_max_attempts`` takes precedence. + ``total_max_attempts`` is preferred over ``max_attempts`` because + it maps to the ``AWS_MAX_ATTEMPTS`` environment variable and + the ``max_attempts`` config file value. + * ``max_attempts`` -- An integer representing the maximum number of + retry attempts that will be made on a single request. For + example, setting this value to 2 will result in the request + being retried at most two times after the initial request. Setting + this value to 0 will result in no retries ever being attempted after + the initial request. If not provided, the number of retries will + default to the value specified in the service model, which is + typically four retries. + * ``mode`` -- A string representing the type of retry mode botocore + should use. Valid values are: + + * ``legacy`` - The pre-existing retry behavior. + + * ``standard`` - The standardized set of retry rules. This will also + default to 3 max attempts unless overridden. + + * ``adaptive`` - Retries with additional client side throttling. + + :type client_cert: str, (str, str) + :param client_cert: The path to a certificate for TLS client authentication. + + When a string is provided it is treated as a path to a client + certificate to be used when creating a TLS connection. + + If a client key is to be provided alongside the client certificate the + client_cert should be set to a tuple of length two where the first + element is the path to the client certificate and the second element is + the path to the certificate key. + + :type inject_host_prefix: bool + :param inject_host_prefix: Whether host prefix injection should occur. + + Defaults to None. + + The default of None is equivalent to setting to True, which enables + the injection of operation parameters into the prefix of the hostname. + Setting this to False disables the injection of operation parameters + into the prefix of the hostname. Setting this to False is useful for + clients providing custom endpoints that should not have their host + prefix modified. + + :type use_dualstack_endpoint: bool + :param use_dualstack_endpoint: Setting to True enables dualstack + endpoint resolution. + + Defaults to None. + + :type use_fips_endpoint: bool + :param use_fips_endpoint: Setting to True enables fips + endpoint resolution. + + Defaults to None. + + :type ignore_configured_endpoint_urls: bool + :param ignore_configured_endpoint_urls: Setting to True disables use + of endpoint URLs provided via environment variables and + the shared configuration file. + + Defaults to None. + + :type tcp_keepalive: bool + :param tcp_keepalive: Enables the TCP Keep-Alive socket option used when + creating new connections if set to True. + + Defaults to False. + + :type request_min_compression_size_bytes: int + :param request_min_compression_size_bytes: The minimum size in bytes that a + request body should be to trigger compression. All requests with + streaming input that don't contain the ``requiresLength`` trait will be + compressed regardless of this setting. + + Defaults to None. + + :type disable_request_compression: bool + :param disable_request_compression: Disables request body compression if + set to True. + + Defaults to None. + + :type sigv4a_signing_region_set: string + :param sigv4a_signing_region_set: A set of AWS regions to apply the signature for + when using SigV4a for signing. Set to ``*`` to represent all regions. + + Defaults to None. + + :type client_context_params: dict + :param client_context_params: A dictionary of parameters specific to + individual services. If available, valid parameters can be found in + the ``Client Context Parameters`` section of the service client's + documentation. Invalid parameters or ones that are not used by the + specified service will be ignored. + + Defaults to None. + + :type request_checksum_calculation: str + :param request_checksum_calculation: Determines when a checksum will be + calculated for request payloads. Valid values are: + + * ``when_supported`` -- When set, a checksum will be calculated for + all request payloads of operations modeled with the ``httpChecksum`` + trait where ``requestChecksumRequired`` is ``true`` or a + ``requestAlgorithmMember`` is modeled. + + * ``when_required`` -- When set, a checksum will only be calculated + for request payloads of operations modeled with the ``httpChecksum`` + trait where ``requestChecksumRequired`` is ``true`` or where a + ``requestAlgorithmMember`` is modeled and supplied. + + Defaults to None. + + :type response_checksum_validation: str + :param response_checksum_validation: Determines when checksum validation + will be performed on response payloads. Valid values are: + + * ``when_supported`` -- When set, checksum validation is performed on + all response payloads of operations modeled with the ``httpChecksum`` + trait where ``responseAlgorithms`` is modeled, except when no modeled + checksum algorithms are supported. + + * ``when_required`` -- When set, checksum validation is not performed + on response payloads of operations unless the checksum algorithm is + supported and the ``requestValidationModeMember`` member is set to ``ENABLED``. + + Defaults to None. + + :type account_id_endpoint_mode: str + :param account_id_endpoint_mode: The value used to determine the client's + behavior for account ID based endpoint routing. Valid values are: + + * ``preferred`` - The endpoint should include account ID if available. + * ``disabled`` - A resolved endpoint does not include account ID. + * ``required`` - The endpoint must include account ID. If the account ID + isn't available, an exception will be raised. + + If a value is not provided, the client will default to ``preferred``. + + Defaults to None. + + :type auth_scheme_preference: str + :param auth_scheme_preference: A comma-delimited string of case-sensitive + auth scheme names used to determine the client's auth scheme preference. + + Defaults to None. + """ + + OPTION_DEFAULTS = OrderedDict( + [ + ('region_name', None), + ('signature_version', None), + ('user_agent', None), + ('user_agent_extra', None), + ('user_agent_appid', None), + ('connect_timeout', DEFAULT_TIMEOUT), + ('read_timeout', DEFAULT_TIMEOUT), + ('parameter_validation', True), + ('max_pool_connections', MAX_POOL_CONNECTIONS), + ('proxies', None), + ('proxies_config', None), + ('s3', None), + ('s3_disable_express_session_auth', None), + ('retries', None), + ('client_cert', None), + ('inject_host_prefix', None), + ('endpoint_discovery_enabled', None), + ('use_dualstack_endpoint', None), + ('use_fips_endpoint', None), + ('ignore_configured_endpoint_urls', None), + ('defaults_mode', None), + ('tcp_keepalive', None), + ('request_min_compression_size_bytes', None), + ('disable_request_compression', None), + ('client_context_params', None), + ('sigv4a_signing_region_set', None), + ('request_checksum_calculation', None), + ('response_checksum_validation', None), + ('account_id_endpoint_mode', None), + ('auth_scheme_preference', None), + ] + ) + + NON_LEGACY_OPTION_DEFAULTS = { + 'connect_timeout': None, + } + + # The original default value of the inject_host_prefix parameter was True. + # This prevented the ability to override the value from other locations in + # the parameter provider chain, like env vars or the shared configuration + # file. TO accomplish this, we need to disambiguate when the value was set + # by the user or not. This overrides the parameter with a property so the + # default value of inject_host_prefix is still True if it is not set by the + # user. + @property + def inject_host_prefix(self): + if self._inject_host_prefix == "UNSET": + return True + + return self._inject_host_prefix + + # Override the setter for the case where the user does supply a value; + # _inject_host_prefix will no longer be "UNSET". + @inject_host_prefix.setter + def inject_host_prefix(self, value): + self._inject_host_prefix = value + + def __init__(self, *args, **kwargs): + self._user_provided_options = self._record_user_provided_options( + args, kwargs + ) + + # By default, we use a value that indicates the user did not + # set it. This value MUST persist on the Config object to be used + # elsewhere. + self._inject_host_prefix = 'UNSET' + + # Merge the user_provided options onto the default options + config_vars = copy.copy(self.OPTION_DEFAULTS) + defaults_mode = self._user_provided_options.get( + 'defaults_mode', 'legacy' + ) + if defaults_mode != 'legacy': + config_vars.update(self.NON_LEGACY_OPTION_DEFAULTS) + + config_vars.update(self._user_provided_options) + + # Set the attributes based on the config_vars + for key, value in config_vars.items(): + # Default values for the Config object are set here. We don't want + # to use `setattr` in the case where the user already supplied a + # value. + if ( + key == 'inject_host_prefix' + and 'inject_host_prefix' + not in self._user_provided_options.keys() + ): + continue + setattr(self, key, value) + + # Validate the s3 options + self._validate_s3_configuration(self.s3) + + self._validate_retry_configuration(self.retries) + + def _record_user_provided_options(self, args, kwargs): + option_order = list(self.OPTION_DEFAULTS) + user_provided_options = {} + + # Iterate through the kwargs passed through to the constructor and + # map valid keys to the dictionary + for key, value in kwargs.items(): + if key in self.OPTION_DEFAULTS: + user_provided_options[key] = value + # The key must exist in the available options + else: + raise TypeError(f"Got unexpected keyword argument '{key}'") + + # The number of args should not be longer than the allowed + # options + if len(args) > len(option_order): + raise TypeError( + f"Takes at most {len(option_order)} arguments ({len(args)} given)" + ) + + # Iterate through the args passed through to the constructor and map + # them to appropriate keys. + for i, arg in enumerate(args): + # If a kwarg was specified for the arg, then error out + if option_order[i] in user_provided_options: + raise TypeError( + f"Got multiple values for keyword argument '{option_order[i]}'" + ) + user_provided_options[option_order[i]] = arg + + return user_provided_options + + def _validate_s3_configuration(self, s3): + if s3 is not None: + addressing_style = s3.get('addressing_style') + if addressing_style not in ['virtual', 'auto', 'path', None]: + raise InvalidS3AddressingStyleError( + s3_addressing_style=addressing_style + ) + + def _validate_retry_configuration(self, retries): + valid_options = ('max_attempts', 'mode', 'total_max_attempts') + valid_modes = ('legacy', 'standard', 'adaptive') + if retries is not None: + for key, value in retries.items(): + if key not in valid_options: + raise InvalidRetryConfigurationError( + retry_config_option=key, + valid_options=valid_options, + ) + if key == 'max_attempts' and value < 0: + raise InvalidMaxRetryAttemptsError( + provided_max_attempts=value, + min_value=0, + ) + if key == 'total_max_attempts' and value < 1: + raise InvalidMaxRetryAttemptsError( + provided_max_attempts=value, + min_value=1, + ) + if key == 'mode' and value not in valid_modes: + raise InvalidRetryModeError( + provided_retry_mode=value, + valid_modes=valid_modes, + ) + + def merge(self, other_config): + """Merges the config object with another config object + + This will merge in all non-default values from the provided config + and return a new config object + + :type other_config: botocore.config.Config + :param other config: Another config object to merge with. The values + in the provided config object will take precedence in the merging + + :returns: A config object built from the merged values of both + config objects. + """ + # Make a copy of the current attributes in the config object. + config_options = copy.copy(self._user_provided_options) + + # Merge in the user provided options from the other config + config_options.update(other_config._user_provided_options) + + # Return a new config object with the merged properties. + return Config(**config_options) diff --git a/venv/lib/python3.12/site-packages/botocore/configloader.py b/venv/lib/python3.12/site-packages/botocore/configloader.py new file mode 100644 index 0000000..0b6c82b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/configloader.py @@ -0,0 +1,287 @@ +# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/ +# Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import configparser +import copy +import os +import shlex +import sys + +import botocore.exceptions + + +def multi_file_load_config(*filenames): + """Load and combine multiple INI configs with profiles. + + This function will take a list of filesnames and return + a single dictionary that represents the merging of the loaded + config files. + + If any of the provided filenames does not exist, then that file + is ignored. It is therefore ok to provide a list of filenames, + some of which may not exist. + + Configuration files are **not** deep merged, only the top level + keys are merged. The filenames should be passed in order of + precedence. The first config file has precedence over the + second config file, which has precedence over the third config file, + etc. The only exception to this is that the "profiles" key is + merged to combine profiles from multiple config files into a + single profiles mapping. However, if a profile is defined in + multiple config files, then the config file with the highest + precedence is used. Profile values themselves are not merged. + For example:: + + FileA FileB FileC + [foo] [foo] [bar] + a=1 a=2 a=3 + b=2 + + [bar] [baz] [profile a] + a=2 a=3 region=e + + [profile a] [profile b] [profile c] + region=c region=d region=f + + The final result of ``multi_file_load_config(FileA, FileB, FileC)`` + would be:: + + {"foo": {"a": 1}, "bar": {"a": 2}, "baz": {"a": 3}, + "profiles": {"a": {"region": "c"}}, {"b": {"region": d"}}, + {"c": {"region": "f"}}} + + Note that the "foo" key comes from A, even though it's defined in both + FileA and FileB. Because "foo" was defined in FileA first, then the values + for "foo" from FileA are used and the values for "foo" from FileB are + ignored. Also note where the profiles originate from. Profile "a" + comes FileA, profile "b" comes from FileB, and profile "c" comes + from FileC. + + """ + configs = [] + profiles = [] + for filename in filenames: + try: + loaded = load_config(filename) + except botocore.exceptions.ConfigNotFound: + continue + profiles.append(loaded.pop('profiles')) + configs.append(loaded) + merged_config = _merge_list_of_dicts(configs) + merged_profiles = _merge_list_of_dicts(profiles) + merged_config['profiles'] = merged_profiles + return merged_config + + +def _merge_list_of_dicts(list_of_dicts): + merged_dicts = {} + for single_dict in list_of_dicts: + for key, value in single_dict.items(): + if key not in merged_dicts: + merged_dicts[key] = value + return merged_dicts + + +def load_config(config_filename): + """Parse a INI config with profiles. + + This will parse an INI config file and map top level profiles + into a top level "profile" key. + + If you want to parse an INI file and map all section names to + top level keys, use ``raw_config_parse`` instead. + + """ + parsed = raw_config_parse(config_filename) + return build_profile_map(parsed) + + +def raw_config_parse(config_filename, parse_subsections=True): + """Returns the parsed INI config contents. + + Each section name is a top level key. + + :param config_filename: The name of the INI file to parse + + :param parse_subsections: If True, parse indented blocks as + subsections that represent their own configuration dictionary. + For example, if the config file had the contents:: + + s3 = + signature_version = s3v4 + addressing_style = path + + The resulting ``raw_config_parse`` would be:: + + {'s3': {'signature_version': 's3v4', 'addressing_style': 'path'}} + + If False, do not try to parse subsections and return the indented + block as its literal value:: + + {'s3': '\nsignature_version = s3v4\naddressing_style = path'} + + :returns: A dict with keys for each profile found in the config + file and the value of each key being a dict containing name + value pairs found in that profile. + + :raises: ConfigNotFound, ConfigParseError + """ + config = {} + path = config_filename + if path is not None: + path = os.path.expandvars(path) + path = os.path.expanduser(path) + if not os.path.isfile(path): + raise botocore.exceptions.ConfigNotFound(path=_unicode_path(path)) + cp = configparser.RawConfigParser() + try: + cp.read([path]) + except (configparser.Error, UnicodeDecodeError) as e: + raise botocore.exceptions.ConfigParseError( + path=_unicode_path(path), error=e + ) from None + else: + for section in cp.sections(): + config[section] = {} + for option in cp.options(section): + config_value = cp.get(section, option) + if parse_subsections and config_value.startswith('\n'): + # Then we need to parse the inner contents as + # hierarchical. We support a single level + # of nesting for now. + try: + config_value = _parse_nested(config_value) + except ValueError as e: + raise botocore.exceptions.ConfigParseError( + path=_unicode_path(path), error=e + ) from None + config[section][option] = config_value + return config + + +def _unicode_path(path): + if isinstance(path, str): + return path + # According to the documentation getfilesystemencoding can return None + # on unix in which case the default encoding is used instead. + filesystem_encoding = sys.getfilesystemencoding() + if filesystem_encoding is None: + filesystem_encoding = sys.getdefaultencoding() + return path.decode(filesystem_encoding, 'replace') + + +def _parse_nested(config_value): + # Given a value like this: + # \n + # foo = bar + # bar = baz + # We need to parse this into + # {'foo': 'bar', 'bar': 'baz} + parsed = {} + for line in config_value.splitlines(): + line = line.strip() + if not line: + continue + # The caller will catch ValueError + # and raise an appropriate error + # if this fails. + key, value = line.split('=', 1) + parsed[key.strip()] = value.strip() + return parsed + + +def _parse_section(key, values): + result = {} + try: + parts = shlex.split(key) + except ValueError: + return result + if len(parts) == 2: + result[parts[1]] = values + return result + + +def build_profile_map(parsed_ini_config): + """Convert the parsed INI config into a profile map. + + The config file format requires that every profile except the + default to be prepended with "profile", e.g.:: + + [profile test] + aws_... = foo + aws_... = bar + + [profile bar] + aws_... = foo + aws_... = bar + + # This is *not* a profile + [preview] + otherstuff = 1 + + # Neither is this + [foobar] + morestuff = 2 + + The build_profile_map will take a parsed INI config file where each top + level key represents a section name, and convert into a format where all + the profiles are under a single top level "profiles" key, and each key in + the sub dictionary is a profile name. For example, the above config file + would be converted from:: + + {"profile test": {"aws_...": "foo", "aws...": "bar"}, + "profile bar": {"aws...": "foo", "aws...": "bar"}, + "preview": {"otherstuff": ...}, + "foobar": {"morestuff": ...}, + } + + into:: + + {"profiles": {"test": {"aws_...": "foo", "aws...": "bar"}, + "bar": {"aws...": "foo", "aws...": "bar"}, + "preview": {"otherstuff": ...}, + "foobar": {"morestuff": ...}, + } + + If there are no profiles in the provided parsed INI contents, then + an empty dict will be the value associated with the ``profiles`` key. + + .. note:: + + This will not mutate the passed in parsed_ini_config. Instead it will + make a deepcopy and return that value. + + """ + parsed_config = copy.deepcopy(parsed_ini_config) + profiles = {} + sso_sessions = {} + services = {} + final_config = {} + for key, values in parsed_config.items(): + if key.startswith("profile"): + profiles.update(_parse_section(key, values)) + elif key.startswith("sso-session"): + sso_sessions.update(_parse_section(key, values)) + elif key.startswith("services"): + services.update(_parse_section(key, values)) + elif key == 'default': + # default section is special and is considered a profile + # name but we don't require you use 'profile "default"' + # as a section. + profiles[key] = values + else: + final_config[key] = values + final_config['profiles'] = profiles + final_config['sso_sessions'] = sso_sessions + final_config['services'] = services + return final_config diff --git a/venv/lib/python3.12/site-packages/botocore/configprovider.py b/venv/lib/python3.12/site-packages/botocore/configprovider.py new file mode 100644 index 0000000..3a7be3a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/configprovider.py @@ -0,0 +1,1081 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""This module contains the interface for controlling how configuration +is loaded. +""" + +import copy +import logging +import os + +from botocore import utils +from botocore.exceptions import InvalidConfigError + +try: + # This is not a public interface and is subject to abrupt breaking changes. + # Currently it's only available to internal users for testing and validation. + # Any usage is not advised or supported in external code bases. + from botocore.customizations.retries import DEFAULT_NEW_RETRIES +except ImportError: + DEFAULT_NEW_RETRIES = False + + +def _resolve_new_retries(): + _env_new_retries = os.environ.get('AWS_NEW_RETRIES_2026') + if _env_new_retries is not None: + return _env_new_retries.lower() == 'true' + return DEFAULT_NEW_RETRIES + + +NEW_RETRIES_ENABLED = _resolve_new_retries() +_DEFAULT_RETRY_MODE = 'standard' if NEW_RETRIES_ENABLED else 'legacy' + +logger = logging.getLogger(__name__) + +#: A default dictionary that maps the logical names for session variables +#: to the specific environment variables and configuration file names +#: that contain the values for these variables. +#: When creating a new Session object, you can pass in your own dictionary +#: to remap the logical names or to add new logical names. You can then +#: get the current value for these variables by using the +#: ``get_config_variable`` method of the :class:`botocore.session.Session` +#: class. +#: These form the keys of the dictionary. The values in the dictionary +#: are tuples of (, , , +#: ). +#: The conversion func is a function that takes the configuration value +#: as an argument and returns the converted value. If this value is +#: None, then the configuration value is returned unmodified. This +#: conversion function can be used to type convert config values to +#: values other than the default values of strings. +#: The ``profile`` and ``config_file`` variables should always have a +#: None value for the first entry in the tuple because it doesn't make +#: sense to look inside the config file for the location of the config +#: file or for the default profile to use. +#: The ``config_name`` is the name to look for in the configuration file, +#: the ``env var`` is the OS environment variable (``os.environ``) to +#: use, and ``default_value`` is the value to use if no value is otherwise +#: found. +#: NOTE: Fixing the spelling of this variable would be a breaking change. +#: Please leave as is. +BOTOCORE_DEFAUT_SESSION_VARIABLES = { + # logical: config_file, env_var, default_value, conversion_func + 'profile': (None, ['AWS_DEFAULT_PROFILE', 'AWS_PROFILE'], None, None), + 'region': ('region', 'AWS_DEFAULT_REGION', None, None), + 'data_path': ('data_path', 'AWS_DATA_PATH', None, None), + 'config_file': (None, 'AWS_CONFIG_FILE', '~/.aws/config', None), + 'ca_bundle': ('ca_bundle', 'AWS_CA_BUNDLE', None, None), + 'api_versions': ('api_versions', None, {}, None), + # This is the shared credentials file amongst sdks. + 'credentials_file': ( + None, + 'AWS_SHARED_CREDENTIALS_FILE', + '~/.aws/credentials', + None, + ), + # These variables only exist in the config file. + # This is the number of seconds until we time out a request to + # the instance metadata service. + 'metadata_service_timeout': ( + 'metadata_service_timeout', + 'AWS_METADATA_SERVICE_TIMEOUT', + 1, + int, + ), + # This is the number of request attempts we make until we give + # up trying to retrieve data from the instance metadata service. + 'metadata_service_num_attempts': ( + 'metadata_service_num_attempts', + 'AWS_METADATA_SERVICE_NUM_ATTEMPTS', + 1, + int, + ), + 'ec2_metadata_service_endpoint': ( + 'ec2_metadata_service_endpoint', + 'AWS_EC2_METADATA_SERVICE_ENDPOINT', + None, + None, + ), + 'ec2_metadata_service_endpoint_mode': ( + 'ec2_metadata_service_endpoint_mode', + 'AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE', + None, + None, + ), + 'ec2_metadata_v1_disabled': ( + 'ec2_metadata_v1_disabled', + 'AWS_EC2_METADATA_V1_DISABLED', + False, + utils.ensure_boolean, + ), + 'imds_use_ipv6': ( + 'imds_use_ipv6', + 'AWS_IMDS_USE_IPV6', + False, + utils.ensure_boolean, + ), + 'use_dualstack_endpoint': ( + 'use_dualstack_endpoint', + 'AWS_USE_DUALSTACK_ENDPOINT', + None, + utils.ensure_boolean, + ), + 'use_fips_endpoint': ( + 'use_fips_endpoint', + 'AWS_USE_FIPS_ENDPOINT', + None, + utils.ensure_boolean, + ), + 'ignore_configured_endpoint_urls': ( + 'ignore_configured_endpoint_urls', + 'AWS_IGNORE_CONFIGURED_ENDPOINT_URLS', + None, + utils.ensure_boolean, + ), + 'parameter_validation': ('parameter_validation', None, True, None), + # Client side monitoring configurations. + # Note: These configurations are considered internal to botocore. + # Do not use them until publicly documented. + 'csm_enabled': ( + 'csm_enabled', + 'AWS_CSM_ENABLED', + False, + utils.ensure_boolean, + ), + 'csm_host': ('csm_host', 'AWS_CSM_HOST', '127.0.0.1', None), + 'csm_port': ('csm_port', 'AWS_CSM_PORT', 31000, int), + 'csm_client_id': ('csm_client_id', 'AWS_CSM_CLIENT_ID', '', None), + # Endpoint discovery configuration + 'endpoint_discovery_enabled': ( + 'endpoint_discovery_enabled', + 'AWS_ENDPOINT_DISCOVERY_ENABLED', + 'auto', + None, + ), + 'sts_regional_endpoints': ( + 'sts_regional_endpoints', + 'AWS_STS_REGIONAL_ENDPOINTS', + 'regional', + None, + ), + 'retry_mode': ('retry_mode', 'AWS_RETRY_MODE', _DEFAULT_RETRY_MODE, None), + 'defaults_mode': ('defaults_mode', 'AWS_DEFAULTS_MODE', 'legacy', None), + # We can't have a default here for v1 because we need to defer to + # whatever the defaults are in _retry.json. + 'max_attempts': ('max_attempts', 'AWS_MAX_ATTEMPTS', None, int), + 'user_agent_appid': ('sdk_ua_app_id', 'AWS_SDK_UA_APP_ID', None, None), + 'request_min_compression_size_bytes': ( + 'request_min_compression_size_bytes', + 'AWS_REQUEST_MIN_COMPRESSION_SIZE_BYTES', + 10240, + None, + ), + 'disable_request_compression': ( + 'disable_request_compression', + 'AWS_DISABLE_REQUEST_COMPRESSION', + False, + utils.ensure_boolean, + ), + 'sigv4a_signing_region_set': ( + 'sigv4a_signing_region_set', + 'AWS_SIGV4A_SIGNING_REGION_SET', + None, + None, + ), + 'request_checksum_calculation': ( + 'request_checksum_calculation', + 'AWS_REQUEST_CHECKSUM_CALCULATION', + "when_supported", + None, + ), + 'response_checksum_validation': ( + 'response_checksum_validation', + 'AWS_RESPONSE_CHECKSUM_VALIDATION', + "when_supported", + None, + ), + 'account_id_endpoint_mode': ( + 'account_id_endpoint_mode', + 'AWS_ACCOUNT_ID_ENDPOINT_MODE', + 'preferred', + None, + ), + 'disable_host_prefix_injection': ( + 'disable_host_prefix_injection', + 'AWS_DISABLE_HOST_PREFIX_INJECTION', + None, + utils.ensure_boolean, + ), + 'auth_scheme_preference': ( + 'auth_scheme_preference', + 'AWS_AUTH_SCHEME_PREFERENCE', + None, + None, + ), + 'tcp_keepalive': ( + 'tcp_keepalive', + 'BOTOCORE_TCP_KEEPALIVE', + None, + utils.ensure_boolean, + ), + 's3_disable_express_session_auth': ( + 's3_disable_express_session_auth', + 'AWS_S3_DISABLE_EXPRESS_SESSION_AUTH', + None, + None, + ), +} +# A mapping for the s3 specific configuration vars. These are the configuration +# vars that typically go in the s3 section of the config file. This mapping +# follows the same schema as the previous session variable mapping. +DEFAULT_S3_CONFIG_VARS = { + 'addressing_style': (('s3', 'addressing_style'), None, None, None), + 'use_accelerate_endpoint': ( + ('s3', 'use_accelerate_endpoint'), + None, + None, + utils.ensure_boolean, + ), + 'use_dualstack_endpoint': ( + ('s3', 'use_dualstack_endpoint'), + None, + None, + utils.ensure_boolean, + ), + 'payload_signing_enabled': ( + ('s3', 'payload_signing_enabled'), + None, + None, + utils.ensure_boolean, + ), + 'use_arn_region': ( + ['s3_use_arn_region', ('s3', 'use_arn_region')], + 'AWS_S3_USE_ARN_REGION', + None, + utils.ensure_boolean, + ), + 'us_east_1_regional_endpoint': ( + [ + 's3_us_east_1_regional_endpoint', + ('s3', 'us_east_1_regional_endpoint'), + ], + 'AWS_S3_US_EAST_1_REGIONAL_ENDPOINT', + None, + None, + ), + 's3_disable_multiregion_access_points': ( + ('s3', 's3_disable_multiregion_access_points'), + 'AWS_S3_DISABLE_MULTIREGION_ACCESS_POINTS', + None, + utils.ensure_boolean, + ), +} +# A mapping for the proxy specific configuration vars. These are +# used to configure how botocore interacts with proxy setups while +# sending requests. +DEFAULT_PROXIES_CONFIG_VARS = { + 'proxy_ca_bundle': ('proxy_ca_bundle', None, None, None), + 'proxy_client_cert': ('proxy_client_cert', None, None, None), + 'proxy_use_forwarding_for_https': ( + 'proxy_use_forwarding_for_https', + None, + None, + utils.normalize_boolean, + ), +} + + +def create_botocore_default_config_mapping(session): + chain_builder = ConfigChainFactory(session=session) + config_mapping = _create_config_chain_mapping( + chain_builder, BOTOCORE_DEFAUT_SESSION_VARIABLES + ) + config_mapping['s3'] = SectionConfigProvider( + 's3', + session, + _create_config_chain_mapping(chain_builder, DEFAULT_S3_CONFIG_VARS), + ) + config_mapping['proxies_config'] = SectionConfigProvider( + 'proxies_config', + session, + _create_config_chain_mapping( + chain_builder, DEFAULT_PROXIES_CONFIG_VARS + ), + ) + return config_mapping + + +def _create_config_chain_mapping(chain_builder, config_variables): + mapping = {} + for logical_name, config in config_variables.items(): + mapping[logical_name] = chain_builder.create_config_chain( + instance_name=logical_name, + env_var_names=config[1], + config_property_names=config[0], + default=config[2], + conversion_func=config[3], + ) + return mapping + + +class DefaultConfigResolver: + def __init__(self, default_config_data): + self._base_default_config = default_config_data['base'] + self._modes = default_config_data['modes'] + self._resolved_default_configurations = {} + + def _resolve_default_values_by_mode(self, mode): + default_config = self._base_default_config.copy() + modifications = self._modes.get(mode) + + for config_var in modifications: + default_value = default_config[config_var] + modification_dict = modifications[config_var] + modification = list(modification_dict.keys())[0] + modification_value = modification_dict[modification] + if modification == 'multiply': + default_value *= modification_value + elif modification == 'add': + default_value += modification_value + elif modification == 'override': + default_value = modification_value + default_config[config_var] = default_value + return default_config + + def get_default_modes(self): + default_modes = ['legacy', 'auto'] + default_modes.extend(self._modes.keys()) + return default_modes + + def get_default_config_values(self, mode): + if mode not in self._resolved_default_configurations: + defaults = self._resolve_default_values_by_mode(mode) + self._resolved_default_configurations[mode] = defaults + return self._resolved_default_configurations[mode] + + +class ConfigChainFactory: + """Factory class to create our most common configuration chain case. + + This is a convenience class to construct configuration chains that follow + our most common pattern. This is to prevent ordering them incorrectly, + and to make the config chain construction more readable. + """ + + def __init__(self, session, environ=None): + """Initialize a ConfigChainFactory. + + :type session: :class:`botocore.session.Session` + :param session: This is the session that should be used to look up + values from the config file. + + :type environ: dict + :param environ: A mapping to use for environment variables. If this + is not provided it will default to use os.environ. + """ + self._session = session + if environ is None: + environ = os.environ + self._environ = environ + + def create_config_chain( + self, + instance_name=None, + env_var_names=None, + config_property_names=None, + default=None, + conversion_func=None, + ): + """Build a config chain following the standard botocore pattern. + + In botocore most of our config chains follow the the precendence: + session_instance_variables, environment, config_file, default_value. + + This is a convenience function for creating a chain that follow + that precendence. + + :type instance_name: str + :param instance_name: This indicates what session instance variable + corresponds to this config value. If it is None it will not be + added to the chain. + + :type env_var_names: str or list of str or None + :param env_var_names: One or more environment variable names to + search for this value. They are searched in order. If it is None + it will not be added to the chain. + + :type config_property_names: str/tuple or list of str/tuple or None + :param config_property_names: One of more strings or tuples + representing the name of the key in the config file for this + config option. They are searched in order. If it is None it will + not be added to the chain. + + :type default: Any + :param default: Any constant value to be returned. + + :type conversion_func: None or callable + :param conversion_func: If this value is None then it has no effect on + the return type. Otherwise, it is treated as a function that will + conversion_func our provided type. + + :rvalue: ConfigChain + :returns: A ConfigChain that resolves in the order env_var_names -> + config_property_name -> default. Any values that were none are + omitted form the chain. + """ + providers = [] + if instance_name is not None: + providers.append( + InstanceVarProvider( + instance_var=instance_name, session=self._session + ) + ) + if env_var_names is not None: + providers.extend(self._get_env_providers(env_var_names)) + if config_property_names is not None: + providers.extend( + self._get_scoped_config_providers(config_property_names) + ) + if default is not None: + providers.append(ConstantProvider(value=default)) + + return ChainProvider( + providers=providers, + conversion_func=conversion_func, + ) + + def _get_env_providers(self, env_var_names): + env_var_providers = [] + if not isinstance(env_var_names, list): + env_var_names = [env_var_names] + for env_var_name in env_var_names: + env_var_providers.append( + EnvironmentProvider(name=env_var_name, env=self._environ) + ) + return env_var_providers + + def _get_scoped_config_providers(self, config_property_names): + scoped_config_providers = [] + if not isinstance(config_property_names, list): + config_property_names = [config_property_names] + for config_property_name in config_property_names: + scoped_config_providers.append( + ScopedConfigProvider( + config_var_name=config_property_name, + session=self._session, + ) + ) + return scoped_config_providers + + +class ConfigValueStore: + """The ConfigValueStore object stores configuration values.""" + + def __init__(self, mapping=None): + """Initialize a ConfigValueStore. + + :type mapping: dict + :param mapping: The mapping parameter is a map of string to a subclass + of BaseProvider. When a config variable is asked for via the + get_config_variable method, the corresponding provider will be + invoked to load the value. + """ + self._overrides = {} + self._mapping = {} + if mapping is not None: + for logical_name, provider in mapping.items(): + self.set_config_provider(logical_name, provider) + + def __deepcopy__(self, memo): + config_store = ConfigValueStore(copy.deepcopy(self._mapping, memo)) + for logical_name, override_value in self._overrides.items(): + config_store.set_config_variable(logical_name, override_value) + + return config_store + + def __copy__(self): + config_store = ConfigValueStore(copy.copy(self._mapping)) + for logical_name, override_value in self._overrides.items(): + config_store.set_config_variable(logical_name, override_value) + + return config_store + + def get_config_variable(self, logical_name): + """ + Retrieve the value associated with the specified logical_name + from the corresponding provider. If no value is found None will + be returned. + + :type logical_name: str + :param logical_name: The logical name of the session variable + you want to retrieve. This name will be mapped to the + appropriate environment variable name for this session as + well as the appropriate config file entry. + + :returns: value of variable or None if not defined. + """ + if logical_name in self._overrides: + return self._overrides[logical_name] + if logical_name not in self._mapping: + return None + provider = self._mapping[logical_name] + return provider.provide() + + def get_config_provider(self, logical_name): + """ + Retrieve the provider associated with the specified logical_name. + If no provider is found None will be returned. + + :type logical_name: str + :param logical_name: The logical name of the session variable + you want to retrieve. This name will be mapped to the + appropriate environment variable name for this session as + well as the appropriate config file entry. + + :returns: configuration provider or None if not defined. + """ + if ( + logical_name in self._overrides + or logical_name not in self._mapping + ): + return None + provider = self._mapping[logical_name] + return provider + + def set_config_variable(self, logical_name, value): + """Set a configuration variable to a specific value. + + By using this method, you can override the normal lookup + process used in ``get_config_variable`` by explicitly setting + a value. Subsequent calls to ``get_config_variable`` will + use the ``value``. This gives you per-session specific + configuration values. + + :: + >>> # Assume logical name 'foo' maps to env var 'FOO' + >>> os.environ['FOO'] = 'myvalue' + >>> s.get_config_variable('foo') + 'myvalue' + >>> s.set_config_variable('foo', 'othervalue') + >>> s.get_config_variable('foo') + 'othervalue' + + :type logical_name: str + :param logical_name: The logical name of the session variable + you want to set. These are the keys in ``SESSION_VARIABLES``. + + :param value: The value to associate with the config variable. + """ + self._overrides[logical_name] = value + + def clear_config_variable(self, logical_name): + """Remove an override config variable from the session. + + :type logical_name: str + :param logical_name: The name of the parameter to clear the override + value from. + """ + self._overrides.pop(logical_name, None) + + def set_config_provider(self, logical_name, provider): + """Set the provider for a config value. + + This provides control over how a particular configuration value is + loaded. This replaces the provider for ``logical_name`` with the new + ``provider``. + + :type logical_name: str + :param logical_name: The name of the config value to change the config + provider for. + + :type provider: :class:`botocore.configprovider.BaseProvider` + :param provider: The new provider that should be responsible for + providing a value for the config named ``logical_name``. + """ + self._mapping[logical_name] = provider + + +class SmartDefaultsConfigStoreFactory: + def __init__(self, default_config_resolver, imds_region_provider): + self._default_config_resolver = default_config_resolver + self._imds_region_provider = imds_region_provider + # Initializing _instance_metadata_region as None so we + # can fetch region in a lazy fashion only when needed. + self._instance_metadata_region = None + + def merge_smart_defaults(self, config_store, mode, region_name): + if mode == 'auto': + mode = self.resolve_auto_mode(region_name) + default_configs = ( + self._default_config_resolver.get_default_config_values(mode) + ) + for config_var in default_configs: + config_value = default_configs[config_var] + method = getattr(self, f'_set_{config_var}', None) + if method: + method(config_store, config_value) + + def resolve_auto_mode(self, region_name): + current_region = None + if os.environ.get('AWS_EXECUTION_ENV'): + default_region = os.environ.get('AWS_DEFAULT_REGION') + current_region = os.environ.get('AWS_REGION', default_region) + if not current_region: + if self._instance_metadata_region: + current_region = self._instance_metadata_region + else: + try: + current_region = self._imds_region_provider.provide() + self._instance_metadata_region = current_region + except Exception: + pass + + if current_region: + if region_name == current_region: + return 'in-region' + else: + return 'cross-region' + return 'standard' + + def _update_provider(self, config_store, variable, value): + original_provider = config_store.get_config_provider(variable) + default_provider = ConstantProvider(value) + if isinstance(original_provider, ChainProvider): + chain_provider_copy = copy.deepcopy(original_provider) + chain_provider_copy.set_default_provider(default_provider) + default_provider = chain_provider_copy + elif isinstance(original_provider, BaseProvider): + default_provider = ChainProvider( + providers=[original_provider, default_provider] + ) + config_store.set_config_provider(variable, default_provider) + + def _update_section_provider( + self, config_store, section_name, variable, value + ): + section_provider_copy = copy.deepcopy( + config_store.get_config_provider(section_name) + ) + section_provider_copy.set_default_provider( + variable, ConstantProvider(value) + ) + config_store.set_config_provider(section_name, section_provider_copy) + + def _set_retryMode(self, config_store, value): + self._update_provider(config_store, 'retry_mode', value) + + def _set_stsRegionalEndpoints(self, config_store, value): + self._update_provider(config_store, 'sts_regional_endpoints', value) + + def _set_s3UsEast1RegionalEndpoints(self, config_store, value): + self._update_section_provider( + config_store, 's3', 'us_east_1_regional_endpoint', value + ) + + def _set_connectTimeoutInMillis(self, config_store, value): + self._update_provider(config_store, 'connect_timeout', value / 1000) + + +class BaseProvider: + """Base class for configuration value providers. + + A configuration provider has some method of providing a configuration + value. + """ + + def provide(self): + """Provide a config value.""" + raise NotImplementedError('provide') + + +class ChainProvider(BaseProvider): + """This provider wraps one or more other providers. + + Each provider in the chain is called, the first one returning a non-None + value is then returned. + """ + + def __init__(self, providers=None, conversion_func=None): + """Initalize a ChainProvider. + + :type providers: list + :param providers: The initial list of providers to check for values + when invoked. + + :type conversion_func: None or callable + :param conversion_func: If this value is None then it has no affect on + the return type. Otherwise, it is treated as a function that will + transform provided value. + """ + if providers is None: + providers = [] + self._providers = providers + self._conversion_func = conversion_func + + def __deepcopy__(self, memo): + return ChainProvider( + copy.deepcopy(self._providers, memo), self._conversion_func + ) + + def provide(self): + """Provide the value from the first provider to return non-None. + + Each provider in the chain has its provide method called. The first + one in the chain to return a non-None value is the returned from the + ChainProvider. When no non-None value is found, None is returned. + """ + for provider in self._providers: + value = provider.provide() + if value is not None: + return self._convert_type(value) + return None + + def set_default_provider(self, default_provider): + if self._providers and isinstance( + self._providers[-1], ConstantProvider + ): + self._providers[-1] = default_provider + else: + self._providers.append(default_provider) + + num_of_constants = sum( + isinstance(provider, ConstantProvider) + for provider in self._providers + ) + if num_of_constants > 1: + logger.info( + 'ChainProvider object contains multiple ' + 'instances of ConstantProvider objects' + ) + + def _convert_type(self, value): + if self._conversion_func is not None: + return self._conversion_func(value) + return value + + def __repr__(self): + return '[{}]'.format(', '.join([str(p) for p in self._providers])) + + +class InstanceVarProvider(BaseProvider): + """This class loads config values from the session instance vars.""" + + def __init__(self, instance_var, session): + """Initialize InstanceVarProvider. + + :type instance_var: str + :param instance_var: The instance variable to load from the session. + + :type session: :class:`botocore.session.Session` + :param session: The botocore session to get the loaded configuration + file variables from. + """ + self._instance_var = instance_var + self._session = session + + def __deepcopy__(self, memo): + return InstanceVarProvider( + copy.deepcopy(self._instance_var, memo), self._session + ) + + def provide(self): + """Provide a config value from the session instance vars.""" + instance_vars = self._session.instance_variables() + value = instance_vars.get(self._instance_var) + return value + + def __repr__(self): + return f'InstanceVarProvider(instance_var={self._instance_var}, session={self._session})' + + +class ScopedConfigProvider(BaseProvider): + def __init__(self, config_var_name, session): + """Initialize ScopedConfigProvider. + + :type config_var_name: str or tuple + :param config_var_name: The name of the config variable to load from + the configuration file. If the value is a tuple, it must only + consist of two items, where the first item represents the section + and the second item represents the config var name in the section. + + :type session: :class:`botocore.session.Session` + :param session: The botocore session to get the loaded configuration + file variables from. + """ + self._config_var_name = config_var_name + self._session = session + + def __deepcopy__(self, memo): + return ScopedConfigProvider( + copy.deepcopy(self._config_var_name, memo), self._session + ) + + def provide(self): + """Provide a value from a config file property.""" + scoped_config = self._session.get_scoped_config() + if isinstance(self._config_var_name, tuple): + section_config = scoped_config.get(self._config_var_name[0]) + if not isinstance(section_config, dict): + return None + return section_config.get(self._config_var_name[1]) + return scoped_config.get(self._config_var_name) + + def __repr__(self): + return f'ScopedConfigProvider(config_var_name={self._config_var_name}, session={self._session})' + + +class EnvironmentProvider(BaseProvider): + """This class loads config values from environment variables.""" + + def __init__(self, name, env): + """Initialize with the keys in the dictionary to check. + + :type name: str + :param name: The key with that name will be loaded and returned. + + :type env: dict + :param env: Environment variables dictionary to get variables from. + """ + self._name = name + self._env = env + + def __deepcopy__(self, memo): + return EnvironmentProvider( + copy.deepcopy(self._name, memo), copy.deepcopy(self._env, memo) + ) + + def provide(self): + """Provide a config value from a source dictionary.""" + if self._name in self._env: + return self._env[self._name] + return None + + def __repr__(self): + return f'EnvironmentProvider(name={self._name}, env={self._env})' + + +class SectionConfigProvider(BaseProvider): + """Provides a dictionary from a section in the scoped config + + This is useful for retrieving scoped config variables (i.e. s3) that have + their own set of config variables and resolving logic. + """ + + def __init__(self, section_name, session, override_providers=None): + self._section_name = section_name + self._session = session + self._scoped_config_provider = ScopedConfigProvider( + self._section_name, self._session + ) + self._override_providers = override_providers + if self._override_providers is None: + self._override_providers = {} + + def __deepcopy__(self, memo): + return SectionConfigProvider( + copy.deepcopy(self._section_name, memo), + self._session, + copy.deepcopy(self._override_providers, memo), + ) + + def provide(self): + section_config = self._scoped_config_provider.provide() + if section_config and not isinstance(section_config, dict): + logger.debug( + "The %s config key is not a dictionary type, " + "ignoring its value of: %s", + self._section_name, + section_config, + ) + return None + for section_config_var, provider in self._override_providers.items(): + provider_val = provider.provide() + if provider_val is not None: + if section_config is None: + section_config = {} + section_config[section_config_var] = provider_val + return section_config + + def set_default_provider(self, key, default_provider): + provider = self._override_providers.get(key) + if isinstance(provider, ChainProvider): + provider.set_default_provider(default_provider) + return + elif isinstance(provider, BaseProvider): + default_provider = ChainProvider( + providers=[provider, default_provider] + ) + self._override_providers[key] = default_provider + + def __repr__(self): + return ( + f'SectionConfigProvider(section_name={self._section_name}, ' + f'session={self._session}, ' + f'override_providers={self._override_providers})' + ) + + +class ConstantProvider(BaseProvider): + """This provider provides a constant value.""" + + def __init__(self, value): + self._value = value + + def __deepcopy__(self, memo): + return ConstantProvider(copy.deepcopy(self._value, memo)) + + def provide(self): + """Provide the constant value given during initialization.""" + return self._value + + def __repr__(self): + return f'ConstantProvider(value={self._value})' + + +class ConfiguredEndpointProvider(BaseProvider): + """Lookup an endpoint URL from environment variable or shared config file. + + NOTE: This class is considered private and is subject to abrupt breaking + changes or removal without prior announcement. Please do not use it + directly. + """ + + _ENDPOINT_URL_LOOKUP_ORDER = [ + 'environment_service', + 'environment_global', + 'config_service', + 'config_global', + ] + + def __init__( + self, + full_config, + scoped_config, + client_name, + environ=None, + ): + """Initialize a ConfiguredEndpointProviderChain. + + :type full_config: dict + :param full_config: This is the dict representing the full + configuration file. + + :type scoped_config: dict + :param scoped_config: This is the dict representing the configuration + for the current profile for the session. + + :type client_name: str + :param client_name: The name used to instantiate a client using + botocore.session.Session.create_client. + + :type environ: dict + :param environ: A mapping to use for environment variables. If this + is not provided it will default to use os.environ. + """ + self._full_config = full_config + self._scoped_config = scoped_config + self._client_name = client_name + self._transformed_service_id = self._get_snake_case_service_id( + self._client_name + ) + if environ is None: + environ = os.environ + self._environ = environ + + def provide(self): + """Lookup the configured endpoint URL. + + The order is: + + 1. The value provided by a service-specific environment variable. + 2. The value provided by the global endpoint environment variable + (AWS_ENDPOINT_URL). + 3. The value provided by a service-specific parameter from a services + definition section in the shared configuration file. + 4. The value provided by the global parameter from a services + definition section in the shared configuration file. + """ + for location in self._ENDPOINT_URL_LOOKUP_ORDER: + logger.debug( + 'Looking for endpoint for %s via: %s', + self._client_name, + location, + ) + + endpoint_url = getattr(self, f'_get_endpoint_url_{location}')() + + if endpoint_url: + logger.info( + 'Found endpoint for %s via: %s.', + self._client_name, + location, + ) + return endpoint_url + + logger.debug('No configured endpoint found.') + return None + + def _get_snake_case_service_id(self, client_name): + # Get the service ID without loading the service data file, accounting + # for any aliases and standardizing the names with hyphens. + client_name = utils.SERVICE_NAME_ALIASES.get(client_name, client_name) + hyphenized_service_id = ( + utils.CLIENT_NAME_TO_HYPHENIZED_SERVICE_ID_OVERRIDES.get( + client_name, client_name + ) + ) + return hyphenized_service_id.replace('-', '_') + + def _get_service_env_var_name(self): + transformed_service_id_env = self._transformed_service_id.upper() + return f'AWS_ENDPOINT_URL_{transformed_service_id_env}' + + def _get_services_config(self): + if 'services' not in self._scoped_config: + return {} + + section_name = self._scoped_config['services'] + services_section = self._full_config.get('services', {}).get( + section_name + ) + + if not services_section: + error_msg = ( + f'The profile is configured to use the services ' + f'section but the "{section_name}" services ' + f'configuration does not exist.' + ) + raise InvalidConfigError(error_msg=error_msg) + + return services_section + + def _get_endpoint_url_config_service(self): + snakecase_service_id = self._transformed_service_id.lower() + return ( + self._get_services_config() + .get(snakecase_service_id, {}) + .get('endpoint_url') + ) + + def _get_endpoint_url_config_global(self): + return self._scoped_config.get('endpoint_url') + + def _get_endpoint_url_environment_service(self): + return EnvironmentProvider( + name=self._get_service_env_var_name(), env=self._environ + ).provide() + + def _get_endpoint_url_environment_global(self): + return EnvironmentProvider( + name='AWS_ENDPOINT_URL', env=self._environ + ).provide() diff --git a/venv/lib/python3.12/site-packages/botocore/context.py b/venv/lib/python3.12/site-packages/botocore/context.py new file mode 100644 index 0000000..8034747 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/context.py @@ -0,0 +1,127 @@ +# Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +""" +NOTE: All classes and functions in this module are considered private and are +subject to abrupt breaking changes. Please do not use them directly. +""" + +from contextlib import contextmanager +from contextvars import ContextVar +from copy import deepcopy +from dataclasses import dataclass, field +from functools import wraps + + +@dataclass +class ClientContext: + """ + Encapsulation of objects tracked within the ``_context`` context variable. + + ``features`` is a set responsible for storing features used during + preparation of an AWS request. ``botocore.useragent.register_feature_id`` + is used to add to this set. + """ + + features: set[str] = field(default_factory=set) + + +_context = ContextVar("_context") + + +def get_context(): + """Get the current ``_context`` context variable if set, else None.""" + return _context.get(None) + + +def set_context(ctx): + """Set the current ``_context`` context variable. + + :type ctx: ClientContext + :param ctx: Client context object to set as the current context variable. + + :rtype: contextvars.Token + :returns: Token object used to revert the context variable to what it was + before the corresponding set. + """ + token = _context.set(ctx) + return token + + +def reset_context(token): + """Reset the current ``_context`` context variable. + + :type token: contextvars.Token + :param token: Token object to reset the context variable. + """ + _context.reset(token) + + +@contextmanager +def start_as_current_context(ctx=None): + """ + Context manager that copies the passed or current context object and sets + it as the current context variable. If no context is found, a new + ``ClientContext`` object is created. It mainly ensures the context variable + is reset to the previous value once the executed code returns. + + Example usage: + + def my_feature(): + with start_as_current_context(): + register_feature_id('MY_FEATURE') + pass + + :type ctx: ClientContext + :param ctx: The client context object to set as the new context variable. + If not provided, the current or a new context variable is used. + """ + current = ctx or get_context() + if current is None: + new = ClientContext() + else: + new = deepcopy(current) + token = set_context(new) + try: + yield + finally: + reset_context(token) + + +def with_current_context(hook=None): + """ + Decorator that wraps ``start_as_current_context`` and optionally invokes a + hook within the newly-set context. This is just syntactic sugar to avoid + indenting existing code under the context manager. + + Example usage: + + @with_current_context(partial(register_feature_id, 'MY_FEATURE')) + def my_feature(): + pass + + :type hook: callable + :param hook: A callable that will be invoked within the scope of the + ``start_as_current_context`` context manager. + """ + + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + with start_as_current_context(): + if hook: + hook() + return func(*args, **kwargs) + + return wrapper + + return decorator diff --git a/venv/lib/python3.12/site-packages/botocore/credentials.py b/venv/lib/python3.12/site-packages/botocore/credentials.py new file mode 100644 index 0000000..571dfea --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/credentials.py @@ -0,0 +1,2781 @@ +# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import base64 +import datetime +import getpass +import json +import logging +import os +import subprocess +import threading +import time +import uuid +from collections import namedtuple +from copy import deepcopy +from hashlib import sha1, sha256 + +import dateutil.parser +from dateutil.parser import parse +from dateutil.tz import tzlocal, tzutc + +import botocore.compat +import botocore.configloader +from botocore import UNSIGNED +from botocore.compat import ( + EC, + compat_shell_split, + total_seconds, +) +from botocore.config import Config +from botocore.exceptions import ( + ConfigNotFound, + CredentialRetrievalError, + InfiniteLoopConfigError, + InvalidConfigError, + LoginError, + LoginInsufficientPermissions, + LoginRefreshRequired, + LoginTokenLoadError, + MetadataRetrievalError, + MissingDependencyException, + PartialCredentialsError, + RefreshWithMFAUnsupportedError, + UnauthorizedSSOTokenError, + UnknownCredentialError, +) +from botocore.tokens import SSOTokenProvider +from botocore.useragent import register_feature_id, register_feature_ids +from botocore.utils import ( + ArnParser, + ContainerMetadataFetcher, + FileWebIdentityTokenLoader, + InstanceMetadataFetcher, + JSONFileCache, + LoginTokenLoader, + SSOTokenLoader, + create_nested_client, + get_login_token_cache_directory, + parse_key_val_file, + resolve_imds_endpoint_mode, +) + +logger = logging.getLogger(__name__) +ReadOnlyCredentials = namedtuple( + 'ReadOnlyCredentials', + ['access_key', 'secret_key', 'token', 'account_id'], + defaults=(None,), +) + +_DEFAULT_MANDATORY_REFRESH_TIMEOUT = 10 * 60 # 10 min +_DEFAULT_ADVISORY_REFRESH_TIMEOUT = 15 * 60 # 15 min + + +def create_credential_resolver(session, cache=None, region_name=None): + """Create a default credential resolver. + + This creates a pre-configured credential resolver + that includes the default lookup chain for + credentials. + + """ + profile_name = session.get_config_variable('profile') or 'default' + metadata_timeout = session.get_config_variable('metadata_service_timeout') + num_attempts = session.get_config_variable('metadata_service_num_attempts') + disable_env_vars = session.instance_variables().get('profile') is not None + + imds_config = { + 'ec2_metadata_service_endpoint': session.get_config_variable( + 'ec2_metadata_service_endpoint' + ), + 'ec2_metadata_service_endpoint_mode': resolve_imds_endpoint_mode( + session + ), + 'ec2_credential_refresh_window': _DEFAULT_ADVISORY_REFRESH_TIMEOUT, + 'ec2_metadata_v1_disabled': session.get_config_variable( + 'ec2_metadata_v1_disabled' + ), + } + + if cache is None: + cache = {} + + env_provider = EnvProvider() + container_provider = ContainerProvider() + instance_metadata_provider = InstanceMetadataProvider( + iam_role_fetcher=InstanceMetadataFetcher( + timeout=metadata_timeout, + num_attempts=num_attempts, + user_agent=session.user_agent(), + config=imds_config, + ) + ) + + profile_provider_builder = ProfileProviderBuilder( + session, cache=cache, region_name=region_name + ) + assume_role_provider = AssumeRoleProvider( + load_config=lambda: session.full_config, + client_creator=_get_client_creator(session, region_name), + cache=cache, + profile_name=profile_name, + credential_sourcer=CanonicalNameCredentialSourcer( + [env_provider, container_provider, instance_metadata_provider] + ), + profile_provider_builder=profile_provider_builder, + ) + + pre_profile = [ + env_provider, + assume_role_provider, + ] + profile_providers = profile_provider_builder.providers( + profile_name=profile_name, + disable_env_vars=disable_env_vars, + ) + post_profile = [ + OriginalEC2Provider(), + BotoProvider(), + container_provider, + instance_metadata_provider, + ] + providers = pre_profile + profile_providers + post_profile + + if disable_env_vars: + # An explicitly provided profile will negate an EnvProvider. + # We will defer to providers that understand the "profile" + # concept to retrieve credentials. + # The one edge case if is all three values are provided via + # env vars: + # export AWS_ACCESS_KEY_ID=foo + # export AWS_SECRET_ACCESS_KEY=bar + # export AWS_PROFILE=baz + # Then, just like our client() calls, the explicit credentials + # will take precedence. + # + # This precedence is enforced by leaving the EnvProvider in the chain. + # This means that the only way a "profile" would win is if the + # EnvProvider does not return credentials, which is what we want + # in this scenario. + providers.remove(env_provider) + logger.debug( + 'Skipping environment variable credential check' + ' because profile name was explicitly set.' + ) + + resolver = CredentialResolver(providers=providers) + return resolver + + +class ProfileProviderBuilder: + """This class handles the creation of profile based providers. + + NOTE: This class is only intended for internal use. + + This class handles the creation and ordering of the various credential + providers that primarly source their configuration from the shared config. + This is needed to enable sharing between the default credential chain and + the source profile chain created by the assume role provider. + """ + + def __init__( + self, + session, + cache=None, + region_name=None, + sso_token_cache=None, + login_token_cache=None, + ): + self._session = session + self._cache = cache + self._region_name = region_name + self._sso_token_cache = sso_token_cache + self._login_token_cache = login_token_cache + + def providers(self, profile_name, disable_env_vars=False): + return [ + self._create_web_identity_provider( + profile_name, + disable_env_vars, + ), + self._create_sso_provider(profile_name), + self._create_shared_credential_provider(profile_name), + self._create_login_provider(profile_name), + self._create_process_provider(profile_name), + self._create_config_provider(profile_name), + ] + + def _create_process_provider(self, profile_name): + return ProcessProvider( + profile_name=profile_name, + load_config=lambda: self._session.full_config, + ) + + def _create_shared_credential_provider(self, profile_name): + credential_file = self._session.get_config_variable('credentials_file') + return SharedCredentialProvider( + profile_name=profile_name, + creds_filename=credential_file, + ) + + def _create_config_provider(self, profile_name): + config_file = self._session.get_config_variable('config_file') + return ConfigProvider( + profile_name=profile_name, + config_filename=config_file, + ) + + def _create_web_identity_provider(self, profile_name, disable_env_vars): + return AssumeRoleWithWebIdentityProvider( + load_config=lambda: self._session.full_config, + client_creator=_get_client_creator( + self._session, self._region_name + ), + cache=self._cache, + profile_name=profile_name, + disable_env_vars=disable_env_vars, + ) + + def _create_sso_provider(self, profile_name): + return SSOProvider( + load_config=lambda: self._session.full_config, + client_creator=self._session.create_client, + profile_name=profile_name, + cache=self._cache, + token_cache=self._sso_token_cache, + token_provider=SSOTokenProvider( + self._session, + cache=self._sso_token_cache, + profile_name=profile_name, + ), + ) + + def _create_login_provider(self, profile_name): + return LoginProvider( + load_config=lambda: self._session.full_config, + client_creator=self._session.create_client, + profile_name=profile_name, + token_cache=self._login_token_cache, + ) + + +def get_credentials(session): + resolver = create_credential_resolver(session) + return resolver.load_credentials() + + +def _local_now(): + return datetime.datetime.now(tzlocal()) + + +def _parse_if_needed(value): + if isinstance(value, datetime.datetime): + return value + return parse(value) + + +def _serialize_if_needed(value, iso=False): + if isinstance(value, datetime.datetime): + if iso: + return value.isoformat() + return value.strftime('%Y-%m-%dT%H:%M:%S%Z') + return value + + +def _get_client_creator(session, region_name): + def client_creator(service_name, **kwargs): + create_client_kwargs = {'region_name': region_name} + create_client_kwargs.update(**kwargs) + return create_nested_client( + session, service_name, **create_client_kwargs + ) + + return client_creator + + +def create_assume_role_refresher(client, params): + def refresh(): + response = client.assume_role(**params) + credentials = response['Credentials'] + # We need to normalize the credential names to + # the values expected by the refresh creds. + return { + 'access_key': credentials['AccessKeyId'], + 'secret_key': credentials['SecretAccessKey'], + 'token': credentials['SessionToken'], + 'expiry_time': _serialize_if_needed(credentials['Expiration']), + } + + return refresh + + +def create_mfa_serial_refresher(actual_refresh): + class _Refresher: + def __init__(self, refresh): + self._refresh = refresh + self._has_been_called = False + + def __call__(self): + if self._has_been_called: + # We can explore an option in the future to support + # reprompting for MFA, but for now we just error out + # when the temp creds expire. + raise RefreshWithMFAUnsupportedError() + self._has_been_called = True + return self._refresh() + + return _Refresher(actual_refresh) + + +class Credentials: + """ + Holds the credentials needed to authenticate requests. + + :param str access_key: The access key part of the credentials. + :param str secret_key: The secret key part of the credentials. + :param str token: The security token, valid only for session credentials. + :param str method: A string which identifies where the credentials + were found. + :param str account_id: (optional) An account ID associated with the credentials. + """ + + def __init__( + self, access_key, secret_key, token=None, method=None, account_id=None + ): + self.access_key = access_key + self.secret_key = secret_key + self.token = token + + if method is None: + method = 'explicit' + self.method = method + self.account_id = account_id + + self._normalize() + + def _normalize(self): + # Keys would sometimes (accidentally) contain non-ascii characters. + # It would cause a confusing UnicodeDecodeError in Python 2. + # We explicitly convert them into unicode to avoid such error. + # + # Eventually the service will decide whether to accept the credential. + # This also complies with the behavior in Python 3. + self.access_key = botocore.compat.ensure_unicode(self.access_key) + self.secret_key = botocore.compat.ensure_unicode(self.secret_key) + + def get_frozen_credentials(self): + return ReadOnlyCredentials( + self.access_key, self.secret_key, self.token, self.account_id + ) + + def get_deferred_property(self, property_name): + def get_property(): + return getattr(self, property_name, None) + + return get_property + + +class RefreshableCredentials(Credentials): + """ + Holds the credentials needed to authenticate requests. In addition, it + knows how to refresh itself. + + :param str access_key: The access key part of the credentials. + :param str secret_key: The secret key part of the credentials. + :param str token: The security token, valid only for session credentials. + :param datetime expiry_time: The expiration time of the credentials. + :param function refresh_using: Callback function to refresh the credentials. + :param str method: A string which identifies where the credentials + were found. + :param function time_fetcher: Callback function to retrieve current time. + """ + + # The time at which we'll attempt to refresh, but not + # block if someone else is refreshing. + _advisory_refresh_timeout = _DEFAULT_ADVISORY_REFRESH_TIMEOUT + # The time at which all threads will block waiting for + # refreshed credentials. + _mandatory_refresh_timeout = _DEFAULT_MANDATORY_REFRESH_TIMEOUT + + def __init__( + self, + access_key, + secret_key, + token, + expiry_time, + refresh_using, + method, + time_fetcher=_local_now, + advisory_timeout=None, + mandatory_timeout=None, + account_id=None, + ): + self._refresh_using = refresh_using + self._access_key = access_key + self._secret_key = secret_key + self._token = token + self._account_id = account_id + self._expiry_time = expiry_time + self._time_fetcher = time_fetcher + self._refresh_lock = threading.Lock() + self.method = method + self._frozen_credentials = ReadOnlyCredentials( + access_key, secret_key, token, account_id + ) + self._normalize() + if advisory_timeout is not None: + self._advisory_refresh_timeout = advisory_timeout + if mandatory_timeout is not None: + self._mandatory_refresh_timeout = mandatory_timeout + + def _normalize(self): + self._access_key = botocore.compat.ensure_unicode(self._access_key) + self._secret_key = botocore.compat.ensure_unicode(self._secret_key) + + @classmethod + def create_from_metadata( + cls, + metadata, + refresh_using, + method, + advisory_timeout=None, + mandatory_timeout=None, + ): + kwargs = {} + if advisory_timeout is not None: + kwargs['advisory_timeout'] = advisory_timeout + if mandatory_timeout is not None: + kwargs['mandatory_timeout'] = mandatory_timeout + + instance = cls( + access_key=metadata['access_key'], + secret_key=metadata['secret_key'], + token=metadata['token'], + expiry_time=cls._expiry_datetime(metadata['expiry_time']), + method=method, + refresh_using=refresh_using, + account_id=metadata.get('account_id'), + **kwargs, + ) + return instance + + @property + def access_key(self): + """Warning: Using this property can lead to race conditions if you + access another property subsequently along the refresh boundary. + Please use get_frozen_credentials instead. + """ + self._refresh() + return self._access_key + + @access_key.setter + def access_key(self, value): + self._access_key = value + + @property + def secret_key(self): + """Warning: Using this property can lead to race conditions if you + access another property subsequently along the refresh boundary. + Please use get_frozen_credentials instead. + """ + self._refresh() + return self._secret_key + + @secret_key.setter + def secret_key(self, value): + self._secret_key = value + + @property + def token(self): + """Warning: Using this property can lead to race conditions if you + access another property subsequently along the refresh boundary. + Please use get_frozen_credentials instead. + """ + self._refresh() + return self._token + + @token.setter + def token(self, value): + self._token = value + + @property + def account_id(self): + """Warning: Using this property can lead to race conditions if you + access another property subsequently along the refresh boundary. + Please use get_frozen_credentials instead. + """ + self._refresh() + return self._account_id + + @account_id.setter + def account_id(self, value): + self._account_id = value + + def _seconds_remaining(self): + delta = self._expiry_time - self._time_fetcher() + return total_seconds(delta) + + def refresh_needed(self, refresh_in=None): + """Check if a refresh is needed. + + A refresh is needed if the expiry time associated + with the temporary credentials is less than the + provided ``refresh_in``. If ``time_delta`` is not + provided, ``self.advisory_refresh_needed`` will be used. + + For example, if your temporary credentials expire + in 10 minutes and the provided ``refresh_in`` is + ``15 * 60``, then this function will return ``True``. + + :type refresh_in: int + :param refresh_in: The number of seconds before the + credentials expire in which refresh attempts should + be made. + + :return: True if refresh needed, False otherwise. + + """ + if self._expiry_time is None: + # No expiration, so assume we don't need to refresh. + return False + + if refresh_in is None: + refresh_in = self._advisory_refresh_timeout + # The credentials should be refreshed if they're going to expire + # in less than 5 minutes. + if self._seconds_remaining() >= refresh_in: + # There's enough time left. Don't refresh. + return False + logger.debug("Credentials need to be refreshed.") + return True + + def _is_expired(self): + # Checks if the current credentials are expired. + return self.refresh_needed(refresh_in=0) + + def _refresh(self): + # In the common case where we don't need a refresh, we + # can immediately exit and not require acquiring the + # refresh lock. + if not self.refresh_needed(self._advisory_refresh_timeout): + return + + # acquire() doesn't accept kwargs, but False is indicating + # that we should not block if we can't acquire the lock. + # If we aren't able to acquire the lock, we'll trigger + # the else clause. + if self._refresh_lock.acquire(False): + try: + if not self.refresh_needed(self._advisory_refresh_timeout): + return + is_mandatory_refresh = self.refresh_needed( + self._mandatory_refresh_timeout + ) + self._protected_refresh(is_mandatory=is_mandatory_refresh) + return + finally: + self._refresh_lock.release() + elif self.refresh_needed(self._mandatory_refresh_timeout): + # If we're within the mandatory refresh window, + # we must block until we get refreshed credentials. + with self._refresh_lock: + if not self.refresh_needed(self._mandatory_refresh_timeout): + return + self._protected_refresh(is_mandatory=True) + + def _protected_refresh(self, is_mandatory): + # precondition: this method should only be called if you've acquired + # the self._refresh_lock. + try: + metadata = self._refresh_using() + except Exception: + period_name = 'mandatory' if is_mandatory else 'advisory' + logger.warning( + "Refreshing temporary credentials failed " + "during %s refresh period.", + period_name, + exc_info=True, + ) + if is_mandatory: + # If this is a mandatory refresh, then + # all errors that occur when we attempt to refresh + # credentials are propagated back to the user. + raise + # Otherwise we'll just return. + # The end result will be that we'll use the current + # set of temporary credentials we have. + return + self._set_from_data(metadata) + self._frozen_credentials = ReadOnlyCredentials( + self._access_key, self._secret_key, self._token, self._account_id + ) + if self._is_expired(): + # We successfully refreshed credentials but for whatever + # reason, our refreshing function returned credentials + # that are still expired. In this scenario, the only + # thing we can do is let the user know and raise + # an exception. + msg = ( + "Credentials were refreshed, but the " + "refreshed credentials are still expired." + ) + logger.warning(msg) + raise RuntimeError(msg) + + @staticmethod + def _expiry_datetime(time_str): + return parse(time_str) + + def _set_from_data(self, data): + expected_keys = ['access_key', 'secret_key', 'token', 'expiry_time'] + if not data: + missing_keys = expected_keys + else: + missing_keys = [k for k in expected_keys if k not in data] + + if missing_keys: + message = "Credential refresh failed, response did not contain: %s" + raise CredentialRetrievalError( + provider=self.method, + error_msg=message % ', '.join(missing_keys), + ) + + self.access_key = data['access_key'] + self.secret_key = data['secret_key'] + self.token = data['token'] + self._expiry_time = parse(data['expiry_time']) + self.account_id = data.get('account_id') + logger.debug( + "Retrieved credentials will expire at: %s", self._expiry_time + ) + self._normalize() + + def get_frozen_credentials(self): + """Return immutable credentials. + + The ``access_key``, ``secret_key``, and ``token`` properties + on this class will always check and refresh credentials if + needed before returning the particular credentials. + + This has an edge case where you can get inconsistent + credentials. Imagine this: + + # Current creds are "t1" + tmp.access_key ---> expired? no, so return t1.access_key + # ---- time is now expired, creds need refreshing to "t2" ---- + tmp.secret_key ---> expired? yes, refresh and return t2.secret_key + + This means we're using the access key from t1 with the secret key + from t2. To fix this issue, you can request a frozen credential object + which is guaranteed not to change. + + The frozen credentials returned from this method should be used + immediately and then discarded. The typical usage pattern would + be:: + + creds = RefreshableCredentials(...) + some_code = SomeSignerObject() + # I'm about to sign the request. + # The frozen credentials are only used for the + # duration of generate_presigned_url and will be + # immediately thrown away. + request = some_code.sign_some_request( + with_credentials=creds.get_frozen_credentials()) + print("Signed request:", request) + + """ + self._refresh() + return self._frozen_credentials + + +class DeferredRefreshableCredentials(RefreshableCredentials): + """Refreshable credentials that don't require initial credentials. + + refresh_using will be called upon first access. + """ + + def __init__(self, refresh_using, method, time_fetcher=_local_now): + self._refresh_using = refresh_using + self._access_key = None + self._secret_key = None + self._token = None + self._account_id = None + self._expiry_time = None + self._time_fetcher = time_fetcher + self._refresh_lock = threading.Lock() + self.method = method + self._frozen_credentials = None + + def refresh_needed(self, refresh_in=None): + if self._frozen_credentials is None: + return True + return super().refresh_needed(refresh_in) + + +class CachedCredentialFetcher: + DEFAULT_EXPIRY_WINDOW_SECONDS = 60 * 15 + + def __init__(self, cache=None, expiry_window_seconds=None): + if cache is None: + cache = {} + self._cache = cache + self._cache_key = self._create_cache_key() + if expiry_window_seconds is None: + expiry_window_seconds = self.DEFAULT_EXPIRY_WINDOW_SECONDS + self._expiry_window_seconds = expiry_window_seconds + self.feature_ids = set() + + def _create_cache_key(self): + raise NotImplementedError('_create_cache_key()') + + def _make_file_safe(self, filename): + # Replace :, path sep, and / to make it the string filename safe. + filename = filename.replace(':', '_').replace(os.sep, '_') + return filename.replace('/', '_') + + def _get_credentials(self): + raise NotImplementedError('_get_credentials()') + + def fetch_credentials(self): + return self._get_cached_credentials() + + def _get_cached_credentials(self): + """Get up-to-date credentials. + + This will check the cache for up-to-date credentials, calling assume + role if none are available. + """ + response = self._load_from_cache() + if response is None: + response = self._get_credentials() + self._write_to_cache(response) + else: + logger.debug("Credentials for role retrieved from cache.") + + creds = response['Credentials'] + expiration = _serialize_if_needed(creds['Expiration'], iso=True) + credentials = { + 'access_key': creds['AccessKeyId'], + 'secret_key': creds['SecretAccessKey'], + 'token': creds['SessionToken'], + 'expiry_time': expiration, + 'account_id': creds.get('AccountId'), + } + + return credentials + + def _load_from_cache(self): + if self._cache_key in self._cache: + creds = deepcopy(self._cache[self._cache_key]) + if not self._is_expired(creds): + return creds + else: + logger.debug( + "Credentials were found in cache, but they are expired." + ) + return None + + def _write_to_cache(self, response): + self._cache[self._cache_key] = deepcopy(response) + + def _is_expired(self, credentials): + """Check if credentials are expired.""" + end_time = _parse_if_needed(credentials['Credentials']['Expiration']) + seconds = total_seconds(end_time - _local_now()) + return seconds < self._expiry_window_seconds + + +class BaseAssumeRoleCredentialFetcher(CachedCredentialFetcher): + def __init__( + self, + client_creator, + role_arn, + extra_args=None, + cache=None, + expiry_window_seconds=None, + ): + self._client_creator = client_creator + self._role_arn = role_arn + + if extra_args is None: + self._assume_kwargs = {} + else: + self._assume_kwargs = deepcopy(extra_args) + self._assume_kwargs['RoleArn'] = self._role_arn + + self._role_session_name = self._assume_kwargs.get('RoleSessionName') + self._using_default_session_name = False + if not self._role_session_name: + self._generate_assume_role_name() + + super().__init__(cache, expiry_window_seconds) + + def _generate_assume_role_name(self): + self._role_session_name = f'botocore-session-{int(time.time())}' + self._assume_kwargs['RoleSessionName'] = self._role_session_name + self._using_default_session_name = True + + def _create_cache_key(self): + """Create a predictable cache key for the current configuration. + + The cache key is intended to be compatible with file names. + """ + args = deepcopy(self._assume_kwargs) + + # The role session name gets randomly generated, so we don't want it + # in the hash. + if self._using_default_session_name: + del args['RoleSessionName'] + + if 'Policy' in args: + # To have a predictable hash, the keys of the policy must be + # sorted, so we have to load it here to make sure it gets sorted + # later on. + args['Policy'] = json.loads(args['Policy']) + + args = json.dumps(args, sort_keys=True) + argument_hash = sha1(args.encode('utf-8')).hexdigest() + return self._make_file_safe(argument_hash) + + def _add_account_id_to_response(self, response): + role_arn = response.get('AssumedRoleUser', {}).get('Arn') + if ArnParser.is_arn(role_arn): + arn_parser = ArnParser() + account_id = arn_parser.parse_arn(role_arn)['account'] + response['Credentials']['AccountId'] = account_id + else: + logger.debug("Unable to extract account ID from Arn: %s", role_arn) + + +class AssumeRoleCredentialFetcher(BaseAssumeRoleCredentialFetcher): + def __init__( + self, + client_creator, + source_credentials, + role_arn, + extra_args=None, + mfa_prompter=None, + cache=None, + expiry_window_seconds=None, + ): + """ + :type client_creator: callable + :param client_creator: A callable that creates a client taking + arguments like ``Session.create_client``. + + :type source_credentials: Credentials + :param source_credentials: The credentials to use to create the + client for the call to AssumeRole. + + :type role_arn: str + :param role_arn: The ARN of the role to be assumed. + + :type extra_args: dict + :param extra_args: Any additional arguments to add to the assume + role request using the format of the botocore operation. + Possible keys include, but may not be limited to, + DurationSeconds, Policy, SerialNumber, ExternalId and + RoleSessionName. + + :type mfa_prompter: callable + :param mfa_prompter: A callable that returns input provided by the + user (i.e raw_input, getpass.getpass, etc.). + + :type cache: dict + :param cache: An object that supports ``__getitem__``, + ``__setitem__``, and ``__contains__``. An example of this is + the ``JSONFileCache`` class in aws-cli. + + :type expiry_window_seconds: int + :param expiry_window_seconds: The amount of time, in seconds, + """ + self._source_credentials = source_credentials + self._mfa_prompter = mfa_prompter + if self._mfa_prompter is None: + self._mfa_prompter = getpass.getpass + + super().__init__( + client_creator, + role_arn, + extra_args=extra_args, + cache=cache, + expiry_window_seconds=expiry_window_seconds, + ) + + def _get_credentials(self): + """Get credentials by calling assume role.""" + register_feature_ids(self.feature_ids) + kwargs = self._assume_role_kwargs() + client = self._create_client() + response = client.assume_role(**kwargs) + self._add_account_id_to_response(response) + return response + + def _assume_role_kwargs(self): + """Get the arguments for assume role based on current configuration.""" + assume_role_kwargs = deepcopy(self._assume_kwargs) + + mfa_serial = assume_role_kwargs.get('SerialNumber') + + if mfa_serial is not None: + prompt = f'Enter MFA code for {mfa_serial}: ' + token_code = self._mfa_prompter(prompt) + assume_role_kwargs['TokenCode'] = token_code + + duration_seconds = assume_role_kwargs.get('DurationSeconds') + + if duration_seconds is not None: + assume_role_kwargs['DurationSeconds'] = duration_seconds + + return assume_role_kwargs + + def _create_client(self): + """Create an STS client using the source credentials.""" + frozen_credentials = self._source_credentials.get_frozen_credentials() + return self._client_creator( + 'sts', + aws_access_key_id=frozen_credentials.access_key, + aws_secret_access_key=frozen_credentials.secret_key, + aws_session_token=frozen_credentials.token, + ) + + +class AssumeRoleWithWebIdentityCredentialFetcher( + BaseAssumeRoleCredentialFetcher +): + def __init__( + self, + client_creator, + web_identity_token_loader, + role_arn, + extra_args=None, + cache=None, + expiry_window_seconds=None, + ): + """ + :type client_creator: callable + :param client_creator: A callable that creates a client taking + arguments like ``Session.create_client``. + + :type web_identity_token_loader: callable + :param web_identity_token_loader: A callable that takes no arguments + and returns a web identity token str. + + :type role_arn: str + :param role_arn: The ARN of the role to be assumed. + + :type extra_args: dict + :param extra_args: Any additional arguments to add to the assume + role request using the format of the botocore operation. + Possible keys include, but may not be limited to, + DurationSeconds, Policy, SerialNumber, ExternalId and + RoleSessionName. + + :type cache: dict + :param cache: An object that supports ``__getitem__``, + ``__setitem__``, and ``__contains__``. An example of this is + the ``JSONFileCache`` class in aws-cli. + + :type expiry_window_seconds: int + :param expiry_window_seconds: The amount of time, in seconds, + """ + self._web_identity_token_loader = web_identity_token_loader + + super().__init__( + client_creator, + role_arn, + extra_args=extra_args, + cache=cache, + expiry_window_seconds=expiry_window_seconds, + ) + + def _get_credentials(self): + """Get credentials by calling assume role.""" + register_feature_ids(self.feature_ids) + kwargs = self._assume_role_kwargs() + # Assume role with web identity does not require credentials other than + # the token, explicitly configure the client to not sign requests. + config = Config(signature_version=UNSIGNED) + client = self._client_creator('sts', config=config) + response = client.assume_role_with_web_identity(**kwargs) + self._add_account_id_to_response(response) + return response + + def _assume_role_kwargs(self): + """Get the arguments for assume role based on current configuration.""" + assume_role_kwargs = deepcopy(self._assume_kwargs) + identity_token = self._web_identity_token_loader() + assume_role_kwargs['WebIdentityToken'] = identity_token + + return assume_role_kwargs + + +class CredentialProvider: + # A short name to identify the provider within botocore. + METHOD = None + + # A name to identify the provider for use in cross-sdk features like + # assume role's `credential_source` configuration option. These names + # are to be treated in a case-insensitive way. NOTE: any providers not + # implemented in botocore MUST prefix their canonical names with + # 'custom' or we DO NOT guarantee that it will work with any features + # that this provides. + CANONICAL_NAME = None + + def __init__(self, session=None): + self.session = session + + def load(self): + """ + Loads the credentials from their source & sets them on the object. + + Subclasses should implement this method (by reading from disk, the + environment, the network or wherever), returning ``True`` if they were + found & loaded. + + If not found, this method should return ``False``, indicating that the + ``CredentialResolver`` should fall back to the next available method. + + The default implementation does nothing, assuming the user has set the + ``access_key/secret_key/token`` themselves. + + :returns: Whether credentials were found & set + :rtype: Credentials + """ + return True + + def _extract_creds_from_mapping(self, mapping, *key_names): + found = [] + for key_name in key_names: + try: + found.append(mapping[key_name]) + except KeyError: + raise PartialCredentialsError( + provider=self.METHOD, cred_var=key_name + ) + return found + + +class ProcessProvider(CredentialProvider): + METHOD = 'custom-process' + + def __init__(self, profile_name, load_config, popen=subprocess.Popen): + self._profile_name = profile_name + self._load_config = load_config + self._loaded_config = None + self._popen = popen + + def load(self): + credential_process = self._credential_process + if credential_process is None: + return + + register_feature_id('CREDENTIALS_PROFILE_PROCESS') + creds_dict = self._retrieve_credentials_using(credential_process) + register_feature_id('CREDENTIALS_PROCESS') + if creds_dict.get('expiry_time') is not None: + return RefreshableCredentials.create_from_metadata( + creds_dict, + lambda: self._retrieve_credentials_using(credential_process), + self.METHOD, + ) + + return Credentials( + access_key=creds_dict['access_key'], + secret_key=creds_dict['secret_key'], + token=creds_dict.get('token'), + method=self.METHOD, + account_id=creds_dict.get('account_id'), + ) + + def _retrieve_credentials_using(self, credential_process): + # We're not using shell=True, so we need to pass the + # command and all arguments as a list. + process_list = compat_shell_split(credential_process) + p = self._popen( + process_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + stdout, stderr = p.communicate() + if p.returncode != 0: + raise CredentialRetrievalError( + provider=self.METHOD, error_msg=stderr.decode('utf-8') + ) + parsed = botocore.compat.json.loads(stdout.decode('utf-8')) + version = parsed.get('Version', '') + if version != 1: + raise CredentialRetrievalError( + provider=self.METHOD, + error_msg=( + f"Unsupported version '{version}' for credential process " + f"provider, supported versions: 1" + ), + ) + try: + return { + 'access_key': parsed['AccessKeyId'], + 'secret_key': parsed['SecretAccessKey'], + 'token': parsed.get('SessionToken'), + 'expiry_time': parsed.get('Expiration'), + 'account_id': self._get_account_id(parsed), + } + except KeyError as e: + raise CredentialRetrievalError( + provider=self.METHOD, + error_msg=f"Missing required key in response: {e}", + ) + + @property + def _credential_process(self): + return self.profile_config.get('credential_process') + + @property + def profile_config(self): + if self._loaded_config is None: + self._loaded_config = self._load_config() + profile_config = self._loaded_config.get('profiles', {}).get( + self._profile_name, {} + ) + return profile_config + + def _get_account_id(self, parsed): + account_id = parsed.get('AccountId') + return account_id or self.profile_config.get('aws_account_id') + + +class InstanceMetadataProvider(CredentialProvider): + METHOD = 'iam-role' + CANONICAL_NAME = 'Ec2InstanceMetadata' + + def __init__(self, iam_role_fetcher): + self._role_fetcher = iam_role_fetcher + + def load(self): + fetcher = self._role_fetcher + # We do the first request, to see if we get useful data back. + # If not, we'll pass & move on to whatever's next in the credential + # chain. + metadata = fetcher.retrieve_iam_role_credentials() + if not metadata: + return None + register_feature_id('CREDENTIALS_IMDS') + logger.info( + 'Found credentials from IAM Role: %s', metadata['role_name'] + ) + # We manually set the data here, since we already made the request & + # have it. When the expiry is hit, the credentials will auto-refresh + # themselves. + creds = RefreshableCredentials.create_from_metadata( + metadata, + method=self.METHOD, + refresh_using=fetcher.retrieve_iam_role_credentials, + ) + return creds + + +class EnvProvider(CredentialProvider): + METHOD = 'env' + CANONICAL_NAME = 'Environment' + ACCESS_KEY = 'AWS_ACCESS_KEY_ID' + SECRET_KEY = 'AWS_SECRET_ACCESS_KEY' + # The token can come from either of these env var. + # AWS_SESSION_TOKEN is what other AWS SDKs have standardized on. + TOKENS = ['AWS_SECURITY_TOKEN', 'AWS_SESSION_TOKEN'] + EXPIRY_TIME = 'AWS_CREDENTIAL_EXPIRATION' + ACCOUNT_ID = 'AWS_ACCOUNT_ID' + + def __init__(self, environ=None, mapping=None): + """ + + :param environ: The environment variables (defaults to + ``os.environ`` if no value is provided). + :param mapping: An optional mapping of variable names to + environment variable names. Use this if you want to + change the mapping of access_key->AWS_ACCESS_KEY_ID, etc. + The dict can have up to 5 keys: + * ``access_key`` + * ``secret_key`` + * ``token`` + * ``expiry_time`` + * ``account_id`` + """ + if environ is None: + environ = os.environ + self.environ = environ + self._mapping = self._build_mapping(mapping) + + def _build_mapping(self, mapping): + # Mapping of variable name to env var name. + var_mapping = {} + if mapping is None: + # Use the class var default. + var_mapping['access_key'] = self.ACCESS_KEY + var_mapping['secret_key'] = self.SECRET_KEY + var_mapping['token'] = self.TOKENS + var_mapping['expiry_time'] = self.EXPIRY_TIME + var_mapping['account_id'] = self.ACCOUNT_ID + else: + var_mapping['access_key'] = mapping.get( + 'access_key', self.ACCESS_KEY + ) + var_mapping['secret_key'] = mapping.get( + 'secret_key', self.SECRET_KEY + ) + var_mapping['token'] = mapping.get('token', self.TOKENS) + if not isinstance(var_mapping['token'], list): + var_mapping['token'] = [var_mapping['token']] + var_mapping['expiry_time'] = mapping.get( + 'expiry_time', self.EXPIRY_TIME + ) + var_mapping['account_id'] = mapping.get( + 'account_id', self.ACCOUNT_ID + ) + return var_mapping + + def load(self): + """ + Search for credentials in explicit environment variables. + """ + + access_key = self.environ.get(self._mapping['access_key'], '') + + if access_key: + logger.info('Found credentials in environment variables.') + fetcher = self._create_credentials_fetcher() + credentials = fetcher(require_expiry=False) + register_feature_id('CREDENTIALS_ENV_VARS') + + expiry_time = credentials['expiry_time'] + if expiry_time is not None: + expiry_time = parse(expiry_time) + return RefreshableCredentials( + credentials['access_key'], + credentials['secret_key'], + credentials['token'], + expiry_time, + refresh_using=fetcher, + method=self.METHOD, + account_id=credentials['account_id'], + ) + + return Credentials( + credentials['access_key'], + credentials['secret_key'], + credentials['token'], + method=self.METHOD, + account_id=credentials['account_id'], + ) + else: + return None + + def _create_credentials_fetcher(self): + mapping = self._mapping + method = self.METHOD + environ = self.environ + + def fetch_credentials(require_expiry=True): + credentials = {} + + access_key = environ.get(mapping['access_key'], '') + if not access_key: + raise PartialCredentialsError( + provider=method, cred_var=mapping['access_key'] + ) + credentials['access_key'] = access_key + + secret_key = environ.get(mapping['secret_key'], '') + if not secret_key: + raise PartialCredentialsError( + provider=method, cred_var=mapping['secret_key'] + ) + credentials['secret_key'] = secret_key + + credentials['token'] = None + for token_env_var in mapping['token']: + token = environ.get(token_env_var, '') + if token: + credentials['token'] = token + break + + credentials['expiry_time'] = None + expiry_time = environ.get(mapping['expiry_time'], '') + if expiry_time: + credentials['expiry_time'] = expiry_time + if require_expiry and not expiry_time: + raise PartialCredentialsError( + provider=method, cred_var=mapping['expiry_time'] + ) + + credentials['account_id'] = None + account_id = environ.get(mapping['account_id'], '') + if account_id: + credentials['account_id'] = account_id + + return credentials + + return fetch_credentials + + +class OriginalEC2Provider(CredentialProvider): + METHOD = 'ec2-credentials-file' + CANONICAL_NAME = 'Ec2Config' + + CRED_FILE_ENV = 'AWS_CREDENTIAL_FILE' + ACCESS_KEY = 'AWSAccessKeyId' + SECRET_KEY = 'AWSSecretKey' + + def __init__(self, environ=None, parser=None): + if environ is None: + environ = os.environ + if parser is None: + parser = parse_key_val_file + self._environ = environ + self._parser = parser + + def load(self): + """ + Search for a credential file used by original EC2 CLI tools. + """ + if 'AWS_CREDENTIAL_FILE' in self._environ: + full_path = os.path.expanduser( + self._environ['AWS_CREDENTIAL_FILE'] + ) + creds = self._parser(full_path) + if self.ACCESS_KEY in creds: + logger.info('Found credentials in AWS_CREDENTIAL_FILE.') + access_key = creds[self.ACCESS_KEY] + secret_key = creds[self.SECRET_KEY] + # EC2 creds file doesn't support session tokens. + return Credentials(access_key, secret_key, method=self.METHOD) + else: + return None + + +class SharedCredentialProvider(CredentialProvider): + METHOD = 'shared-credentials-file' + CANONICAL_NAME = 'SharedCredentials' + + ACCESS_KEY = 'aws_access_key_id' + SECRET_KEY = 'aws_secret_access_key' + # Same deal as the EnvProvider above. Botocore originally supported + # aws_security_token, but the SDKs are standardizing on aws_session_token + # so we support both. + TOKENS = ['aws_security_token', 'aws_session_token'] + ACCOUNT_ID = 'aws_account_id' + + def __init__(self, creds_filename, profile_name=None, ini_parser=None): + self._creds_filename = creds_filename + if profile_name is None: + profile_name = 'default' + self._profile_name = profile_name + if ini_parser is None: + ini_parser = botocore.configloader.raw_config_parse + self._ini_parser = ini_parser + + def load(self): + try: + available_creds = self._ini_parser(self._creds_filename) + except ConfigNotFound: + return None + if self._profile_name in available_creds: + config = available_creds[self._profile_name] + if self.ACCESS_KEY in config: + logger.info( + "Found credentials in shared credentials file: %s", + self._creds_filename, + ) + access_key, secret_key = self._extract_creds_from_mapping( + config, self.ACCESS_KEY, self.SECRET_KEY + ) + token = self._get_session_token(config) + account_id = self._get_account_id(config) + register_feature_id('CREDENTIALS_PROFILE') + return Credentials( + access_key, + secret_key, + token, + method=self.METHOD, + account_id=account_id, + ) + + def _get_session_token(self, config): + for token_envvar in self.TOKENS: + if token_envvar in config: + return config[token_envvar] + + def _get_account_id(self, config): + return config.get(self.ACCOUNT_ID) + + +class ConfigProvider(CredentialProvider): + """INI based config provider with profile sections.""" + + METHOD = 'config-file' + CANONICAL_NAME = 'SharedConfig' + + ACCESS_KEY = 'aws_access_key_id' + SECRET_KEY = 'aws_secret_access_key' + # Same deal as the EnvProvider above. Botocore originally supported + # aws_security_token, but the SDKs are standardizing on aws_session_token + # so we support both. + TOKENS = ['aws_security_token', 'aws_session_token'] + ACCOUNT_ID = 'aws_account_id' + + def __init__(self, config_filename, profile_name, config_parser=None): + """ + + :param config_filename: The session configuration scoped to the current + profile. This is available via ``session.config``. + :param profile_name: The name of the current profile. + :param config_parser: A config parser callable. + + """ + self._config_filename = config_filename + self._profile_name = profile_name + if config_parser is None: + config_parser = botocore.configloader.load_config + self._config_parser = config_parser + + def load(self): + """ + If there is are credentials in the configuration associated with + the session, use those. + """ + try: + full_config = self._config_parser(self._config_filename) + except ConfigNotFound: + return None + if self._profile_name in full_config['profiles']: + profile_config = full_config['profiles'][self._profile_name] + if self.ACCESS_KEY in profile_config: + logger.info( + "Credentials found in config file: %s", + self._config_filename, + ) + access_key, secret_key = self._extract_creds_from_mapping( + profile_config, self.ACCESS_KEY, self.SECRET_KEY + ) + token = self._get_session_token(profile_config) + account_id = self._get_account_id(profile_config) + register_feature_id('CREDENTIALS_PROFILE') + return Credentials( + access_key, + secret_key, + token, + method=self.METHOD, + account_id=account_id, + ) + else: + return None + + def _get_session_token(self, profile_config): + for token_name in self.TOKENS: + if token_name in profile_config: + return profile_config[token_name] + + def _get_account_id(self, config): + return config.get(self.ACCOUNT_ID) + + +class BotoProvider(CredentialProvider): + METHOD = 'boto-config' + CANONICAL_NAME = 'Boto2Config' + + BOTO_CONFIG_ENV = 'BOTO_CONFIG' + DEFAULT_CONFIG_FILENAMES = ['/etc/boto.cfg', '~/.boto'] + ACCESS_KEY = 'aws_access_key_id' + SECRET_KEY = 'aws_secret_access_key' + + def __init__(self, environ=None, ini_parser=None): + if environ is None: + environ = os.environ + if ini_parser is None: + ini_parser = botocore.configloader.raw_config_parse + self._environ = environ + self._ini_parser = ini_parser + + def load(self): + """ + Look for credentials in boto config file. + """ + if self.BOTO_CONFIG_ENV in self._environ: + potential_locations = [self._environ[self.BOTO_CONFIG_ENV]] + else: + potential_locations = self.DEFAULT_CONFIG_FILENAMES + for filename in potential_locations: + try: + config = self._ini_parser(filename) + except ConfigNotFound: + # Move on to the next potential config file name. + continue + if 'Credentials' in config: + credentials = config['Credentials'] + if self.ACCESS_KEY in credentials: + logger.info( + "Found credentials in boto config file: %s", filename + ) + access_key, secret_key = self._extract_creds_from_mapping( + credentials, self.ACCESS_KEY, self.SECRET_KEY + ) + register_feature_id('CREDENTIALS_BOTO2_CONFIG_FILE') + return Credentials( + access_key, secret_key, method=self.METHOD + ) + + +class AssumeRoleProvider(CredentialProvider): + METHOD = 'assume-role' + # The AssumeRole provider is logically part of the SharedConfig and + # SharedCredentials providers. Since the purpose of the canonical name + # is to provide cross-sdk compatibility, calling code will need to be + # aware that either of those providers should be tied to the AssumeRole + # provider as much as possible. + CANONICAL_NAME = None + ROLE_CONFIG_VAR = 'role_arn' + WEB_IDENTITY_TOKE_FILE_VAR = 'web_identity_token_file' + # Credentials are considered expired (and will be refreshed) once the total + # remaining time left until the credentials expires is less than the + # EXPIRY_WINDOW. + EXPIRY_WINDOW_SECONDS = 60 * 15 + NAMED_PROVIDER_FEATURE_MAP = { + 'Ec2InstanceMetadata': 'CREDENTIALS_IMDS', + 'Environment': 'CREDENTIALS_ENV_VARS', + 'EcsContainer': 'CREDENTIALS_HTTP', + } + + def __init__( + self, + load_config, + client_creator, + cache, + profile_name, + prompter=getpass.getpass, + credential_sourcer=None, + profile_provider_builder=None, + ): + """ + :type load_config: callable + :param load_config: A function that accepts no arguments, and + when called, will return the full configuration dictionary + for the session (``session.full_config``). + + :type client_creator: callable + :param client_creator: A factory function that will create + a client when called. Has the same interface as + ``botocore.session.Session.create_client``. + + :type cache: dict + :param cache: An object that supports ``__getitem__``, + ``__setitem__``, and ``__contains__``. An example + of this is the ``JSONFileCache`` class in the CLI. + + :type profile_name: str + :param profile_name: The name of the profile. + + :type prompter: callable + :param prompter: A callable that returns input provided + by the user (i.e raw_input, getpass.getpass, etc.). + + :type credential_sourcer: CanonicalNameCredentialSourcer + :param credential_sourcer: A credential provider that takes a + configuration, which is used to provide the source credentials + for the STS call. + """ + #: The cache used to first check for assumed credentials. + #: This is checked before making the AssumeRole API + #: calls and can be useful if you have short lived + #: scripts and you'd like to avoid calling AssumeRole + #: until the credentials are expired. + self.cache = cache + self._load_config = load_config + # client_creator is a callable that creates function. + # It's basically session.create_client + self._client_creator = client_creator + self._profile_name = profile_name + self._prompter = prompter + # The _loaded_config attribute will be populated from the + # load_config() function once the configuration is actually + # loaded. The reason we go through all this instead of just + # requiring that the loaded_config be passed to us is to that + # we can defer configuration loaded until we actually try + # to load credentials (as opposed to when the object is + # instantiated). + self._loaded_config = {} + self._credential_sourcer = credential_sourcer + self._profile_provider_builder = profile_provider_builder + self._visited_profiles = [self._profile_name] + self._feature_ids = set() + + def load(self): + self._loaded_config = self._load_config() + profiles = self._loaded_config.get('profiles', {}) + profile = profiles.get(self._profile_name, {}) + if self._has_assume_role_config_vars(profile): + return self._load_creds_via_assume_role(self._profile_name) + + def _has_assume_role_config_vars(self, profile): + return ( + self.ROLE_CONFIG_VAR in profile + and + # We need to ensure this provider doesn't look at a profile when + # the profile has configuration for web identity. Simply relying on + # the order in the credential chain is insufficient as it doesn't + # prevent the case when we're doing an assume role chain. + self.WEB_IDENTITY_TOKE_FILE_VAR not in profile + ) + + def _load_creds_via_assume_role(self, profile_name): + role_config = self._get_role_config(profile_name) + source_credentials = self._resolve_source_credentials( + role_config, profile_name + ) + + extra_args = {} + role_session_name = role_config.get('role_session_name') + if role_session_name is not None: + extra_args['RoleSessionName'] = role_session_name + + external_id = role_config.get('external_id') + if external_id is not None: + extra_args['ExternalId'] = external_id + + mfa_serial = role_config.get('mfa_serial') + if mfa_serial is not None: + extra_args['SerialNumber'] = mfa_serial + + duration_seconds = role_config.get('duration_seconds') + if duration_seconds is not None: + extra_args['DurationSeconds'] = duration_seconds + + fetcher = AssumeRoleCredentialFetcher( + client_creator=self._client_creator, + source_credentials=source_credentials, + role_arn=role_config['role_arn'], + extra_args=extra_args, + mfa_prompter=self._prompter, + cache=self.cache, + ) + fetcher.feature_ids = self._feature_ids.copy() + refresher = fetcher.fetch_credentials + if mfa_serial is not None: + refresher = create_mfa_serial_refresher(refresher) + + self._feature_ids.add('CREDENTIALS_STS_ASSUME_ROLE') + register_feature_ids(self._feature_ids) + # The initial credentials are empty and the expiration time is set + # to now so that we can delay the call to assume role until it is + # strictly needed. + return DeferredRefreshableCredentials( + method=self.METHOD, + refresh_using=refresher, + time_fetcher=_local_now, + ) + + def _get_role_config(self, profile_name): + """Retrieves and validates the role configuration for the profile.""" + profiles = self._loaded_config.get('profiles', {}) + + profile = profiles[profile_name] + source_profile = profile.get('source_profile') + role_arn = profile['role_arn'] + credential_source = profile.get('credential_source') + mfa_serial = profile.get('mfa_serial') + external_id = profile.get('external_id') + role_session_name = profile.get('role_session_name') + duration_seconds = profile.get('duration_seconds') + + role_config = { + 'role_arn': role_arn, + 'external_id': external_id, + 'mfa_serial': mfa_serial, + 'role_session_name': role_session_name, + 'source_profile': source_profile, + 'credential_source': credential_source, + } + + if duration_seconds is not None: + try: + role_config['duration_seconds'] = int(duration_seconds) + except ValueError: + pass + + # Either the credential source or the source profile must be + # specified, but not both. + if credential_source is not None and source_profile is not None: + raise InvalidConfigError( + error_msg=( + f'The profile "{profile_name}" contains both ' + 'source_profile and credential_source.' + ) + ) + elif credential_source is None and source_profile is None: + raise PartialCredentialsError( + provider=self.METHOD, + cred_var='source_profile or credential_source', + ) + elif credential_source is not None: + self._validate_credential_source(profile_name, credential_source) + else: + self._validate_source_profile(profile_name, source_profile) + + return role_config + + def _validate_credential_source(self, parent_profile, credential_source): + if self._credential_sourcer is None: + raise InvalidConfigError( + error_msg=( + f"The credential_source \"{credential_source}\" is specified " + f"in profile \"{parent_profile}\", " + f"but no source provider was configured." + ) + ) + if not self._credential_sourcer.is_supported(credential_source): + raise InvalidConfigError( + error_msg=( + f"The credential source \"{credential_source}\" referenced " + f"in profile \"{parent_profile}\" is not valid." + ) + ) + + def _source_profile_has_credentials(self, profile): + return any( + [ + self._has_static_credentials(profile), + self._has_assume_role_config_vars(profile), + ] + ) + + def _validate_source_profile( + self, parent_profile_name, source_profile_name + ): + profiles = self._loaded_config.get('profiles', {}) + if source_profile_name not in profiles: + raise InvalidConfigError( + error_msg=( + f"The source_profile \"{source_profile_name}\" referenced in " + f"the profile \"{parent_profile_name}\" does not exist." + ) + ) + + source_profile = profiles[source_profile_name] + + # Make sure we aren't going into an infinite loop. If we haven't + # visited the profile yet, we're good. + if source_profile_name not in self._visited_profiles: + return + + # If we have visited the profile and the profile isn't simply + # referencing itself, that's an infinite loop. + if source_profile_name != parent_profile_name: + raise InfiniteLoopConfigError( + source_profile=source_profile_name, + visited_profiles=self._visited_profiles, + ) + + # A profile is allowed to reference itself so that it can source + # static credentials and have configuration all in the same + # profile. This will only ever work for the top level assume + # role because the static credentials will otherwise take + # precedence. + if not self._has_static_credentials(source_profile): + raise InfiniteLoopConfigError( + source_profile=source_profile_name, + visited_profiles=self._visited_profiles, + ) + + def _has_static_credentials(self, profile): + static_keys = ['aws_secret_access_key', 'aws_access_key_id'] + return any(static_key in profile for static_key in static_keys) + + def _resolve_source_credentials(self, role_config, profile_name): + credential_source = role_config.get('credential_source') + if credential_source is not None: + self._feature_ids.add('CREDENTIALS_PROFILE_NAMED_PROVIDER') + return self._resolve_credentials_from_source( + credential_source, profile_name + ) + + source_profile = role_config['source_profile'] + self._visited_profiles.append(source_profile) + self._feature_ids.add('CREDENTIALS_PROFILE_SOURCE_PROFILE') + return self._resolve_credentials_from_profile(source_profile) + + def _resolve_credentials_from_profile(self, profile_name): + profiles = self._loaded_config.get('profiles', {}) + profile = profiles[profile_name] + self._feature_ids.add('CREDENTIALS_PROFILE') + if ( + self._has_static_credentials(profile) + and not self._profile_provider_builder + ): + # This is only here for backwards compatibility. If this provider + # isn't given a profile provider builder we still want to be able + # to handle the basic static credential case as we would before the + # profile provider builder parameter was added. + return self._resolve_static_credentials_from_profile(profile) + elif self._has_static_credentials( + profile + ) or not self._has_assume_role_config_vars(profile): + profile_providers = self._profile_provider_builder.providers( + profile_name=profile_name, + disable_env_vars=True, + ) + profile_chain = CredentialResolver(profile_providers) + credentials = profile_chain.load_credentials() + if credentials is None: + error_message = ( + 'The source profile "%s" must have credentials.' + ) + raise InvalidConfigError( + error_msg=error_message % profile_name, + ) + return credentials + + return self._load_creds_via_assume_role(profile_name) + + def _resolve_static_credentials_from_profile(self, profile): + try: + return Credentials( + access_key=profile['aws_access_key_id'], + secret_key=profile['aws_secret_access_key'], + token=profile.get('aws_session_token'), + ) + except KeyError as e: + raise PartialCredentialsError( + provider=self.METHOD, cred_var=str(e) + ) + + def _resolve_credentials_from_source( + self, credential_source, profile_name + ): + credentials = self._credential_sourcer.source_credentials( + credential_source + ) + if credentials is None: + raise CredentialRetrievalError( + provider=credential_source, + error_msg=( + 'No credentials found in credential_source referenced ' + f'in profile {profile_name}' + ), + ) + named_provider_feature_id = self.NAMED_PROVIDER_FEATURE_MAP.get( + credential_source + ) + if named_provider_feature_id: + self._feature_ids.add(named_provider_feature_id) + return credentials + + +class AssumeRoleWithWebIdentityProvider(CredentialProvider): + METHOD = 'assume-role-with-web-identity' + CANONICAL_NAME = None + _CONFIG_TO_ENV_VAR = { + 'web_identity_token_file': 'AWS_WEB_IDENTITY_TOKEN_FILE', + 'role_session_name': 'AWS_ROLE_SESSION_NAME', + 'role_arn': 'AWS_ROLE_ARN', + } + + def __init__( + self, + load_config, + client_creator, + profile_name, + cache=None, + disable_env_vars=False, + token_loader_cls=None, + ): + self.cache = cache + self._load_config = load_config + self._client_creator = client_creator + self._profile_name = profile_name + self._profile_config = None + self._disable_env_vars = disable_env_vars + if token_loader_cls is None: + token_loader_cls = FileWebIdentityTokenLoader + self._token_loader_cls = token_loader_cls + self._feature_ids = set() + + def load(self): + return self._assume_role_with_web_identity() + + def _get_profile_config(self, key): + if self._profile_config is None: + loaded_config = self._load_config() + profiles = loaded_config.get('profiles', {}) + self._profile_config = profiles.get(self._profile_name, {}) + return self._profile_config.get(key) + + def _get_env_config(self, key): + if self._disable_env_vars: + return None + env_key = self._CONFIG_TO_ENV_VAR.get(key) + if env_key and env_key in os.environ: + return os.environ[env_key] + return None + + def _get_config(self, key): + env_value = self._get_env_config(key) + if env_value is not None: + self._feature_ids.add('CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN') + return env_value + + config_value = self._get_profile_config(key) + if config_value is not None: + self._feature_ids.add('CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN') + return config_value + + return None + + def _assume_role_with_web_identity(self): + token_path = self._get_config('web_identity_token_file') + if not token_path: + return None + token_loader = self._token_loader_cls(token_path) + + role_arn = self._get_config('role_arn') + if not role_arn: + error_msg = ( + 'The provided profile or the current environment is ' + 'configured to assume role with web identity but has no ' + 'role ARN configured. Ensure that the profile has the role_arn' + 'configuration set or the AWS_ROLE_ARN env var is set.' + ) + raise InvalidConfigError(error_msg=error_msg) + + extra_args = {} + role_session_name = self._get_config('role_session_name') + if role_session_name is not None: + extra_args['RoleSessionName'] = role_session_name + + fetcher = AssumeRoleWithWebIdentityCredentialFetcher( + client_creator=self._client_creator, + web_identity_token_loader=token_loader, + role_arn=role_arn, + extra_args=extra_args, + cache=self.cache, + ) + fetcher.feature_ids = self._feature_ids.copy() + + self._feature_ids.add('CREDENTIALS_STS_ASSUME_ROLE_WEB_ID') + register_feature_ids(self._feature_ids) + # The initial credentials are empty and the expiration time is set + # to now so that we can delay the call to assume role until it is + # strictly needed. + return DeferredRefreshableCredentials( + method=self.METHOD, + refresh_using=fetcher.fetch_credentials, + ) + + +class CanonicalNameCredentialSourcer: + def __init__(self, providers): + self._providers = providers + + def is_supported(self, source_name): + """Validates a given source name. + + :type source_name: str + :param source_name: The value of credential_source in the config + file. This is the canonical name of the credential provider. + + :rtype: bool + :returns: True if the credential provider is supported, + False otherwise. + """ + return source_name in [p.CANONICAL_NAME for p in self._providers] + + def source_credentials(self, source_name): + """Loads source credentials based on the provided configuration. + + :type source_name: str + :param source_name: The value of credential_source in the config + file. This is the canonical name of the credential provider. + + :rtype: Credentials + """ + source = self._get_provider(source_name) + if isinstance(source, CredentialResolver): + return source.load_credentials() + return source.load() + + def _get_provider(self, canonical_name): + """Return a credential provider by its canonical name. + + :type canonical_name: str + :param canonical_name: The canonical name of the provider. + + :raises UnknownCredentialError: Raised if no + credential provider by the provided name + is found. + """ + provider = self._get_provider_by_canonical_name(canonical_name) + + # The AssumeRole provider should really be part of the SharedConfig + # provider rather than being its own thing, but it is not. It is + # effectively part of both the SharedConfig provider and the + # SharedCredentials provider now due to the way it behaves. + # Therefore if we want either of those providers we should return + # the AssumeRole provider with it. + if canonical_name.lower() in ['sharedconfig', 'sharedcredentials']: + assume_role_provider = self._get_provider_by_method('assume-role') + if assume_role_provider is not None: + # The SharedConfig or SharedCredentials provider may not be + # present if it was removed for some reason, but the + # AssumeRole provider could still be present. In that case, + # return the assume role provider by itself. + if provider is None: + return assume_role_provider + + # If both are present, return them both as a + # CredentialResolver so that calling code can treat them as + # a single entity. + return CredentialResolver([assume_role_provider, provider]) + + if provider is None: + raise UnknownCredentialError(name=canonical_name) + + return provider + + def _get_provider_by_canonical_name(self, canonical_name): + """Return a credential provider by its canonical name. + + This function is strict, it does not attempt to address + compatibility issues. + """ + for provider in self._providers: + name = provider.CANONICAL_NAME + # Canonical names are case-insensitive + if name and name.lower() == canonical_name.lower(): + return provider + + def _get_provider_by_method(self, method): + """Return a credential provider by its METHOD name.""" + for provider in self._providers: + if provider.METHOD == method: + return provider + + +class ContainerProvider(CredentialProvider): + METHOD = 'container-role' + CANONICAL_NAME = 'EcsContainer' + ENV_VAR = 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' + ENV_VAR_FULL = 'AWS_CONTAINER_CREDENTIALS_FULL_URI' + ENV_VAR_AUTH_TOKEN = 'AWS_CONTAINER_AUTHORIZATION_TOKEN' + ENV_VAR_AUTH_TOKEN_FILE = 'AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE' + + def __init__(self, environ=None, fetcher=None): + if environ is None: + environ = os.environ + if fetcher is None: + fetcher = ContainerMetadataFetcher() + self._environ = environ + self._fetcher = fetcher + + def load(self): + # This cred provider is only triggered if the self.ENV_VAR is set, + # which only happens if you opt into this feature. + if self.ENV_VAR in self._environ or self.ENV_VAR_FULL in self._environ: + return self._retrieve_or_fail() + + def _retrieve_or_fail(self): + if self._provided_relative_uri(): + full_uri = self._fetcher.full_url(self._environ[self.ENV_VAR]) + else: + full_uri = self._environ[self.ENV_VAR_FULL] + fetcher = self._create_fetcher(full_uri) + creds = fetcher() + return RefreshableCredentials( + access_key=creds['access_key'], + secret_key=creds['secret_key'], + token=creds['token'], + method=self.METHOD, + expiry_time=_parse_if_needed(creds['expiry_time']), + refresh_using=fetcher, + account_id=creds.get('account_id'), + ) + + def _build_headers(self): + auth_token = None + if self.ENV_VAR_AUTH_TOKEN_FILE in self._environ: + auth_token_file_path = self._environ[self.ENV_VAR_AUTH_TOKEN_FILE] + with open(auth_token_file_path) as token_file: + auth_token = token_file.read() + elif self.ENV_VAR_AUTH_TOKEN in self._environ: + auth_token = self._environ[self.ENV_VAR_AUTH_TOKEN] + if auth_token is not None: + self._validate_auth_token(auth_token) + return {'Authorization': auth_token} + + def _validate_auth_token(self, auth_token): + if "\r" in auth_token or "\n" in auth_token: + raise ValueError("Auth token value is not a legal header value") + + def _create_fetcher(self, full_uri, *args, **kwargs): + def fetch_creds(): + try: + headers = self._build_headers() + response = self._fetcher.retrieve_full_uri( + full_uri, headers=headers + ) + register_feature_id('CREDENTIALS_HTTP') + except MetadataRetrievalError as e: + logger.debug( + "Error retrieving container metadata: %s", e, exc_info=True + ) + raise CredentialRetrievalError( + provider=self.METHOD, error_msg=str(e) + ) + return { + 'access_key': response['AccessKeyId'], + 'secret_key': response['SecretAccessKey'], + 'token': response['Token'], + 'expiry_time': response['Expiration'], + 'account_id': response.get('AccountId'), + } + + return fetch_creds + + def _provided_relative_uri(self): + return self.ENV_VAR in self._environ + + +class CredentialResolver: + def __init__(self, providers): + """ + + :param providers: A list of ``CredentialProvider`` instances. + + """ + self.providers = providers + + def insert_before(self, name, credential_provider): + """ + Inserts a new instance of ``CredentialProvider`` into the chain that + will be tried before an existing one. + + :param name: The short name of the credentials you'd like to insert the + new credentials before. (ex. ``env`` or ``config``). Existing names + & ordering can be discovered via ``self.available_methods``. + :type name: string + + :param cred_instance: An instance of the new ``Credentials`` object + you'd like to add to the chain. + :type cred_instance: A subclass of ``Credentials`` + """ + try: + offset = [p.METHOD for p in self.providers].index(name) + except ValueError: + raise UnknownCredentialError(name=name) + self.providers.insert(offset, credential_provider) + + def insert_after(self, name, credential_provider): + """ + Inserts a new type of ``Credentials`` instance into the chain that will + be tried after an existing one. + + :param name: The short name of the credentials you'd like to insert the + new credentials after. (ex. ``env`` or ``config``). Existing names + & ordering can be discovered via ``self.available_methods``. + :type name: string + + :param cred_instance: An instance of the new ``Credentials`` object + you'd like to add to the chain. + :type cred_instance: A subclass of ``Credentials`` + """ + offset = self._get_provider_offset(name) + self.providers.insert(offset + 1, credential_provider) + + def remove(self, name): + """ + Removes a given ``Credentials`` instance from the chain. + + :param name: The short name of the credentials instance to remove. + :type name: string + """ + available_methods = [p.METHOD for p in self.providers] + if name not in available_methods: + # It's not present. Fail silently. + return + + offset = available_methods.index(name) + self.providers.pop(offset) + + def get_provider(self, name): + """Return a credential provider by name. + + :type name: str + :param name: The name of the provider. + + :raises UnknownCredentialError: Raised if no + credential provider by the provided name + is found. + """ + return self.providers[self._get_provider_offset(name)] + + def _get_provider_offset(self, name): + try: + return [p.METHOD for p in self.providers].index(name) + except ValueError: + raise UnknownCredentialError(name=name) + + def load_credentials(self): + """ + Goes through the credentials chain, returning the first ``Credentials`` + that could be loaded. + """ + # First provider to return a non-None response wins. + for provider in self.providers: + logger.debug("Looking for credentials via: %s", provider.METHOD) + creds = provider.load() + if creds is not None: + return creds + + # If we got here, no credentials could be found. + # This feels like it should be an exception, but historically, ``None`` + # is returned. + # + # +1 + # -js + return None + + +class SSOCredentialFetcher(CachedCredentialFetcher): + _UTC_DATE_FORMAT = '%Y-%m-%dT%H:%M:%SZ' + + def __init__( + self, + start_url, + sso_region, + role_name, + account_id, + client_creator, + token_loader=None, + cache=None, + expiry_window_seconds=None, + token_provider=None, + sso_session_name=None, + time_fetcher=_local_now, + ): + self._client_creator = client_creator + self._sso_region = sso_region + self._role_name = role_name + self._account_id = account_id + self._start_url = start_url + self._token_loader = token_loader + self._token_provider = token_provider + self._sso_session_name = sso_session_name + self._time_fetcher = time_fetcher + super().__init__(cache, expiry_window_seconds) + + def _create_cache_key(self): + """Create a predictable cache key for the current configuration. + + The cache key is intended to be compatible with file names. + """ + args = { + 'roleName': self._role_name, + 'accountId': self._account_id, + } + if self._sso_session_name: + args['sessionName'] = self._sso_session_name + else: + args['startUrl'] = self._start_url + # NOTE: It would be good to hoist this cache key construction logic + # into the CachedCredentialFetcher class as we should be consistent. + # Unfortunately, the current assume role fetchers that sub class don't + # pass separators resulting in non-minified JSON. In the long term, + # all fetchers should use the below caching scheme. + args = json.dumps(args, sort_keys=True, separators=(',', ':')) + argument_hash = sha1(args.encode('utf-8')).hexdigest() + return self._make_file_safe(argument_hash) + + def _parse_timestamp(self, timestamp_ms): + # fromtimestamp expects seconds so: milliseconds / 1000 = seconds + timestamp_seconds = timestamp_ms / 1000.0 + timestamp = datetime.datetime.fromtimestamp(timestamp_seconds, tzutc()) + return timestamp.strftime(self._UTC_DATE_FORMAT) + + def _get_credentials(self): + """Get credentials by calling SSO get role credentials.""" + config = Config( + signature_version=UNSIGNED, + region_name=self._sso_region, + ) + client = self._client_creator('sso', config=config) + if self._token_provider: + initial_token_data = self._token_provider.load_token() + token = initial_token_data.get_frozen_token().token + else: + token_dict = self._token_loader(self._start_url) + token = token_dict['accessToken'] + + # raise an UnauthorizedSSOTokenError if the loaded legacy token + # is expired to save a call to GetRoleCredentials with an + # expired token. + expiration = dateutil.parser.parse(token_dict['expiresAt']) + remaining = total_seconds(expiration - self._time_fetcher()) + if remaining <= 0: + raise UnauthorizedSSOTokenError() + + kwargs = { + 'roleName': self._role_name, + 'accountId': self._account_id, + 'accessToken': token, + } + try: + register_feature_ids(self.feature_ids) + response = client.get_role_credentials(**kwargs) + except client.exceptions.UnauthorizedException: + raise UnauthorizedSSOTokenError() + credentials = response['roleCredentials'] + + credentials = { + 'ProviderType': 'sso', + 'Credentials': { + 'AccessKeyId': credentials['accessKeyId'], + 'SecretAccessKey': credentials['secretAccessKey'], + 'SessionToken': credentials['sessionToken'], + 'Expiration': self._parse_timestamp(credentials['expiration']), + 'AccountId': self._account_id, + }, + } + return credentials + + +class SSOProvider(CredentialProvider): + METHOD = 'sso' + + _SSO_TOKEN_CACHE_DIR = os.path.expanduser( + os.path.join('~', '.aws', 'sso', 'cache') + ) + _PROFILE_REQUIRED_CONFIG_VARS = ( + 'sso_role_name', + 'sso_account_id', + ) + _SSO_REQUIRED_CONFIG_VARS = ( + 'sso_start_url', + 'sso_region', + ) + _ALL_REQUIRED_CONFIG_VARS = ( + _PROFILE_REQUIRED_CONFIG_VARS + _SSO_REQUIRED_CONFIG_VARS + ) + + def __init__( + self, + load_config, + client_creator, + profile_name, + cache=None, + token_cache=None, + token_provider=None, + ): + if token_cache is None: + token_cache = JSONFileCache(self._SSO_TOKEN_CACHE_DIR) + self._token_cache = token_cache + self._token_provider = token_provider + if cache is None: + cache = {} + self.cache = cache + self._load_config = load_config + self._client_creator = client_creator + self._profile_name = profile_name + self._feature_ids = set() + + def _load_sso_config(self): + loaded_config = self._load_config() + profiles = loaded_config.get('profiles', {}) + profile_name = self._profile_name + profile_config = profiles.get(self._profile_name, {}) + sso_sessions = loaded_config.get('sso_sessions', {}) + + # Role name & Account ID indicate the cred provider should be used + if all( + c not in profile_config for c in self._PROFILE_REQUIRED_CONFIG_VARS + ): + return None + + resolved_config, extra_reqs = self._resolve_sso_session_reference( + profile_config, sso_sessions + ) + + config = {} + missing_config_vars = [] + all_required_configs = self._ALL_REQUIRED_CONFIG_VARS + extra_reqs + for config_var in all_required_configs: + if config_var in resolved_config: + config[config_var] = resolved_config[config_var] + else: + missing_config_vars.append(config_var) + + if missing_config_vars: + missing = ', '.join(missing_config_vars) + raise InvalidConfigError( + error_msg=( + f'The profile "{profile_name}" is configured to use SSO ' + f'but is missing required configuration: {missing}' + ) + ) + return config + + def _resolve_sso_session_reference(self, profile_config, sso_sessions): + sso_session_name = profile_config.get('sso_session') + if sso_session_name is None: + # No reference to resolve, proceed with legacy flow + return profile_config, () + + if sso_session_name not in sso_sessions: + error_msg = f'The specified sso-session does not exist: "{sso_session_name}"' + raise InvalidConfigError(error_msg=error_msg) + + config = profile_config.copy() + session = sso_sessions[sso_session_name] + for config_var, val in session.items(): + # Validate any keys referenced in both profile and sso_session match + if config.get(config_var, val) != val: + error_msg = ( + f"The value for {config_var} is inconsistent between " + f"profile ({config[config_var]}) and sso-session ({val})." + ) + raise InvalidConfigError(error_msg=error_msg) + config[config_var] = val + return config, ('sso_session',) + + def load(self): + sso_config = self._load_sso_config() + if not sso_config: + return None + + fetcher_kwargs = { + 'start_url': sso_config['sso_start_url'], + 'sso_region': sso_config['sso_region'], + 'role_name': sso_config['sso_role_name'], + 'account_id': sso_config['sso_account_id'], + 'client_creator': self._client_creator, + 'token_loader': SSOTokenLoader(cache=self._token_cache), + 'cache': self.cache, + } + sso_session_in_config = 'sso_session' in sso_config + if sso_session_in_config: + fetcher_kwargs['sso_session_name'] = sso_config['sso_session'] + fetcher_kwargs['token_provider'] = self._token_provider + self._feature_ids.add('CREDENTIALS_PROFILE_SSO') + else: + self._feature_ids.add('CREDENTIALS_PROFILE_SSO_LEGACY') + + sso_fetcher = SSOCredentialFetcher(**fetcher_kwargs) + sso_fetcher.feature_ids = self._feature_ids.copy() + + if sso_session_in_config: + self._feature_ids.add('CREDENTIALS_SSO') + else: + self._feature_ids.add('CREDENTIALS_SSO_LEGACY') + + register_feature_ids(self._feature_ids) + return DeferredRefreshableCredentials( + method=self.METHOD, + refresh_using=sso_fetcher.fetch_credentials, + ) + + +def _base64_url_encode_no_padding(data): + return base64.urlsafe_b64encode(data).rstrip(b'=').decode('ascii') + + +def _build_dpop_header(private_key, uri, uid=None, ts=None): + if EC is None: + raise MissingDependencyException( + msg=( + "This operation requires an additional dependency. You" + " will need to pip install \"botocore[crt]\" before proceeding." + ) + ) + x, y = private_key.get_public_coords() + jwk = { + "kty": "EC", + "x": _base64_url_encode_no_padding(x), + "y": _base64_url_encode_no_padding(y), + "crv": "P-256", + } + + header = { + "typ": "dpop+jwt", + "alg": "ES256", + "jwk": jwk, + } + + payload = { + "htm": "POST", + "htu": uri, + "iat": ts or int(time.time()), + "jti": uid or str(uuid.uuid4()), + } + header_b64 = _base64_url_encode_no_padding( + json.dumps(header, separators=(',', ':')).encode() + ) + payload_b64 = _base64_url_encode_no_padding( + json.dumps(payload, separators=(',', ':')).encode() + ) + signing_input = f"{header_b64}.{payload_b64}".encode() + signature = private_key.sign(sha256(signing_input).digest()) + signature_bytes = EC.decode_der_signature_to_padded_pair( + signature, pad_to=32 + ) + signature_b64 = _base64_url_encode_no_padding(signature_bytes) + + return f"{header_b64}.{payload_b64}.{signature_b64}" + + +def _build_add_dpop_header_handler(private_key): + """Builds a before-call handler for calculating and setting the DPoP header""" + + def _add_dpop_header_handler(**kwargs): + kwargs['params']['headers']['DPoP'] = _build_dpop_header( + private_key, kwargs['params']['url'] + ) + + return _add_dpop_header_handler + + +class LoginCredentialFetcher: + """ + Converts login access tokens from the cached token to + credentials, and supports refreshing them. + """ + + _REFRESH_THRESHOLD = 5 * 60 + _REQUIRED_TOKEN_FIELDS = ( + 'accessToken', + 'refreshToken', + 'dpopKey', + 'clientId', + ) + + def __init__( + self, + session_name, + token_loader, + client_creator, + time_fetcher=_local_now, + feature_ids=None, + ): + self._session_name = session_name + self._token_loader = token_loader + self._client_creator = client_creator + self._time_fetcher = time_fetcher + if feature_ids is None: + feature_ids = set() + self.feature_ids = feature_ids + + def load_cached_credentials(self): + """Loads cached credentials without checking their expiry.""" + token = self._token_loader.load_token(self._session_name) + + if token is None: + raise LoginTokenLoadError( + error_msg='Unable to load a existing login session for session ' + f'{self._session_name}. Please reauthenticate with ' + "'aws login'.", + ) + + missing_fields = [ + key for key in self._REQUIRED_TOKEN_FIELDS if key not in token + ] + if missing_fields: + raise LoginTokenLoadError( + error_msg=f'Failed to load access token from token cache, missing required fields: {", ".join(missing_fields)}.' + ) + + return self._token_to_credentials(token) + + def refresh_credentials(self): + """Refreshes login credentials, including saving them to the cache.""" + if self.feature_ids: + register_feature_ids(self.feature_ids) + # Reload the token from disk, we need the refresh info + token = self._token_loader.load_token(self._session_name) + private_key = self._load_private_key(token) + + # Check if token has already been refreshed and is still valid + if ( + token + and 'accessToken' in token + and 'expiresAt' in token['accessToken'] + ): + expiry_time = _parse_if_needed(token['accessToken']['expiresAt']) + remaining_time = total_seconds(expiry_time - self._time_fetcher()) + if remaining_time > self._REFRESH_THRESHOLD: + return self._token_to_credentials(token) + + config = botocore.config.Config( + signature_version=botocore.UNSIGNED, + ) + client = self._client_creator( + 'signin', + config=config, + ) + + client.meta.events.register( + 'before-call.signin.CreateOAuth2Token', + _build_add_dpop_header_handler(private_key), + ) + + try: + response = client.create_o_auth2_token( + tokenInput={ + 'clientId': token['clientId'], + 'refreshToken': token['refreshToken'], + 'grantType': 'refresh_token', + }, + ) + except client.exceptions.AccessDeniedException as e: + error_type = e.response.get('error', '') + if error_type in ('TOKEN_EXPIRED', 'USER_CREDENTIALS_CHANGED'): + raise LoginRefreshRequired() from e + elif error_type == 'INSUFFICIENT_PERMISSIONS': + raise LoginInsufficientPermissions() from e + raise LoginError() from e + + if response is None or 'tokenOutput' not in response: + raise LoginTokenLoadError( + error_msg=( + "Unable to refresh access token due to an invalid service response. " + "Please try running 'aws login' again. If the issue persists, there " + "may be a temporary signin service problem." + ) + ) + + output = response.get('tokenOutput') + + expires_timestamp = self._time_fetcher().astimezone( + tzutc() + ) + datetime.timedelta(seconds=output['expiresIn']) + + # Overwrite token with refreshed fields + token.update( + { + 'accessToken': { + 'accessKeyId': output['accessToken']['accessKeyId'], + 'secretAccessKey': output['accessToken'][ + 'secretAccessKey' + ], + 'sessionToken': output['accessToken']['sessionToken'], + 'accountId': token['accessToken']['accountId'], + 'expiresAt': expires_timestamp.strftime( + '%Y-%m-%dT%H:%M:%SZ' + ), + }, + 'refreshToken': output['refreshToken'], + } + ) + self._token_loader.save_token(self._session_name, token) + + return self._token_to_credentials(token) + + @staticmethod + def _token_to_credentials(token): + return { + 'access_key': token['accessToken']['accessKeyId'], + 'secret_key': token['accessToken']['secretAccessKey'], + 'token': token['accessToken']['sessionToken'], + 'expiry_time': token['accessToken']['expiresAt'], + 'account_id': token['accessToken']['accountId'], + } + + @staticmethod + def _load_private_key(token): + if 'dpopKey' not in token: + raise LoginTokenLoadError( + error_msg='Private key not found in cached token.' + ) + + # Remove the PEM header and footer lines + lines = token['dpopKey'].splitlines() + content_lines = [ + line + for line in lines + if not line.startswith('-----BEGIN') + and not line.startswith('-----END') + ] + + # strip should handle the optional newline at the end as well + contents = ''.join(content_lines).strip() + + try: + return EC.new_key_from_der_data(base64.b64decode(contents)) + except ValueError as e: + raise LoginTokenLoadError( + error_msg='Unable to load private key from cached token.' + ) from e + + +class LoginProvider(CredentialProvider): + METHOD = 'login' + + def __init__( + self, + load_config, + client_creator, + profile_name, + token_cache=None, + ): + super().__init__() + if token_cache is None: + token_cache = JSONFileCache(get_login_token_cache_directory()) + self._token_cache = token_cache + + self._load_config = load_config + self._client_creator = client_creator + self._profile_name = profile_name + self._feature_ids = {'CREDENTIALS_PROFILE_LOGIN', 'CREDENTIALS_LOGIN'} + + def load(self): + loaded_config = self._load_config() + profiles = loaded_config.get('profiles', {}) + profile_config = profiles.get(self._profile_name, {}) + + if 'login_session' not in profile_config: + return None + + if EC is None: + raise MissingDependencyException( + msg=( + "Using the login credential provider requires an " + "additional dependency. You will need to pip install " + "\"botocore[crt]\" before proceeding." + ) + ) + + fetcher = LoginCredentialFetcher( + session_name=profile_config['login_session'], + token_loader=LoginTokenLoader(self._token_cache), + client_creator=self._client_creator, + time_fetcher=_local_now, + feature_ids=self._feature_ids, + ) + + register_feature_ids(self._feature_ids) + + # Return the current cached credentials initially, + # regardless if they're expired + cached_credentials = fetcher.load_cached_credentials() + + return RefreshableCredentials( + access_key=cached_credentials['access_key'], + secret_key=cached_credentials['secret_key'], + token=cached_credentials['token'], + expiry_time=_parse_if_needed(cached_credentials['expiry_time']), + account_id=cached_credentials['account_id'], + method=self.METHOD, + refresh_using=fetcher.refresh_credentials, + time_fetcher=_local_now, + ) diff --git a/venv/lib/python3.12/site-packages/botocore/crt/__init__.py b/venv/lib/python3.12/site-packages/botocore/crt/__init__.py new file mode 100644 index 0000000..952ebf3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/crt/__init__.py @@ -0,0 +1,27 @@ +# Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +# A list of auth types supported by the signers in botocore/crt/auth.py. This +# should always match the keys of botocore.crt.auth.CRT_AUTH_TYPE_MAPS. The +# information is duplicated here so that it can be accessed in environments +# where `awscrt` is not present and any import from botocore.crt.auth would +# fail. +CRT_SUPPORTED_AUTH_TYPES = ( + 'v4', + 'v4-query', + 'v4a', + 's3v4', + 's3v4-query', + 's3v4a', + 's3v4a-query', +) diff --git a/venv/lib/python3.12/site-packages/botocore/crt/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/crt/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..97e2ca2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/crt/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/crt/__pycache__/auth.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/crt/__pycache__/auth.cpython-312.pyc new file mode 100644 index 0000000..f9568a4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/crt/__pycache__/auth.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/crt/auth.py b/venv/lib/python3.12/site-packages/botocore/crt/auth.py new file mode 100644 index 0000000..e36730e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/crt/auth.py @@ -0,0 +1,629 @@ +# Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +from io import BytesIO + +from botocore.auth import ( + SIGNED_HEADERS_BLACKLIST, + STREAMING_UNSIGNED_PAYLOAD_TRAILER, + UNSIGNED_PAYLOAD, + BaseSigner, + _get_body_as_dict, + _host_from_url, +) +from botocore.compat import ( + HTTPHeaders, + awscrt, + get_current_datetime, + parse_qs, + urlsplit, + urlunsplit, +) +from botocore.exceptions import NoCredentialsError +from botocore.useragent import register_feature_id +from botocore.utils import percent_encode_sequence + + +class CrtSigV4Auth(BaseSigner): + REQUIRES_REGION = True + _PRESIGNED_HEADERS_BLOCKLIST = [ + 'Authorization', + 'X-Amz-Date', + 'X-Amz-Content-SHA256', + 'X-Amz-Security-Token', + ] + _SIGNATURE_TYPE = awscrt.auth.AwsSignatureType.HTTP_REQUEST_HEADERS + _USE_DOUBLE_URI_ENCODE = True + _SHOULD_NORMALIZE_URI_PATH = True + + def __init__(self, credentials, service_name, region_name): + self.credentials = credentials + self._service_name = service_name + self._region_name = region_name + self._expiration_in_seconds = None + + def _is_streaming_checksum_payload(self, request): + checksum_context = request.context.get('checksum', {}) + algorithm = checksum_context.get('request_algorithm') + return isinstance(algorithm, dict) and algorithm.get('in') == 'trailer' + + def add_auth(self, request): + if self.credentials is None: + raise NoCredentialsError() + + datetime_now = get_current_datetime(remove_tzinfo=False) + + # Use existing 'X-Amz-Content-SHA256' header if able + existing_sha256 = self._get_existing_sha256(request) + + self._modify_request_before_signing(request) + + credentials_provider = awscrt.auth.AwsCredentialsProvider.new_static( + access_key_id=self.credentials.access_key, + secret_access_key=self.credentials.secret_key, + session_token=self.credentials.token, + ) + + if self._is_streaming_checksum_payload(request): + explicit_payload = STREAMING_UNSIGNED_PAYLOAD_TRAILER + elif self._should_sha256_sign_payload(request): + if existing_sha256: + explicit_payload = existing_sha256 + else: + explicit_payload = None # to be calculated during signing + else: + explicit_payload = UNSIGNED_PAYLOAD + + if self._should_add_content_sha256_header(explicit_payload): + body_header = ( + awscrt.auth.AwsSignedBodyHeaderType.X_AMZ_CONTENT_SHA_256 + ) + else: + body_header = awscrt.auth.AwsSignedBodyHeaderType.NONE + + signing_config = awscrt.auth.AwsSigningConfig( + algorithm=awscrt.auth.AwsSigningAlgorithm.V4, + signature_type=self._SIGNATURE_TYPE, + credentials_provider=credentials_provider, + region=self._region_name, + service=self._service_name, + date=datetime_now, + should_sign_header=self._should_sign_header, + use_double_uri_encode=self._USE_DOUBLE_URI_ENCODE, + should_normalize_uri_path=self._SHOULD_NORMALIZE_URI_PATH, + signed_body_value=explicit_payload, + signed_body_header_type=body_header, + expiration_in_seconds=self._expiration_in_seconds, + ) + crt_request = self._crt_request_from_aws_request(request) + future = awscrt.auth.aws_sign_request(crt_request, signing_config) + future.result() + self._apply_signing_changes(request, crt_request) + + def _crt_request_from_aws_request(self, aws_request): + url_parts = urlsplit(aws_request.url) + crt_path = url_parts.path if url_parts.path else '/' + if aws_request.params: + array = [] + for param, value in aws_request.params.items(): + value = str(value) + array.append(f'{param}={value}') + crt_path = crt_path + '?' + '&'.join(array) + elif url_parts.query: + crt_path = f'{crt_path}?{url_parts.query}' + + crt_headers = awscrt.http.HttpHeaders(aws_request.headers.items()) + + # CRT requires body (if it exists) to be an I/O stream. + crt_body_stream = None + if aws_request.body: + if hasattr(aws_request.body, 'seek'): + crt_body_stream = aws_request.body + else: + crt_body_stream = BytesIO(aws_request.body) + + crt_request = awscrt.http.HttpRequest( + method=aws_request.method, + path=crt_path, + headers=crt_headers, + body_stream=crt_body_stream, + ) + return crt_request + + def _apply_signing_changes(self, aws_request, signed_crt_request): + # Apply changes from signed CRT request to the AWSRequest + aws_request.headers = HTTPHeaders.from_pairs( + list(signed_crt_request.headers) + ) + + def _should_sign_header(self, name, **kwargs): + return name.lower() not in SIGNED_HEADERS_BLACKLIST + + def _modify_request_before_signing(self, request): + # This could be a retry. Make sure the previous + # authorization headers are removed first. + for h in self._PRESIGNED_HEADERS_BLOCKLIST: + if h in request.headers: + del request.headers[h] + # If necessary, add the host header + if 'host' not in request.headers: + request.headers['host'] = _host_from_url(request.url) + + def _get_existing_sha256(self, request): + return request.headers.get('X-Amz-Content-SHA256') + + def _should_sha256_sign_payload(self, request): + # Payloads will always be signed over insecure connections. + if not request.url.startswith('https'): + return True + + # Certain operations may have payload signing disabled by default. + # Since we don't have access to the operation model, we pass in this + # bit of metadata through the request context. + return request.context.get('payload_signing_enabled', True) + + def _should_add_content_sha256_header(self, explicit_payload): + # only add X-Amz-Content-SHA256 header if payload is explicitly set + return explicit_payload is not None + + +class CrtS3SigV4Auth(CrtSigV4Auth): + # For S3, we do not normalize the path. + _USE_DOUBLE_URI_ENCODE = False + _SHOULD_NORMALIZE_URI_PATH = False + + def _get_existing_sha256(self, request): + # always recalculate + return None + + def _should_sha256_sign_payload(self, request): + # S3 allows optional body signing, so to minimize the performance + # impact, we opt to not SHA256 sign the body on streaming uploads, + # provided that we're on https. + client_config = request.context.get('client_config') + s3_config = getattr(client_config, 's3', None) + + # The config could be None if it isn't set, or if the customer sets it + # to None. + if s3_config is None: + s3_config = {} + + # The explicit configuration takes precedence over any implicit + # configuration. + sign_payload = s3_config.get('payload_signing_enabled', None) + if sign_payload is not None: + return sign_payload + + # We require that both a checksum be present and https be enabled + # to implicitly disable body signing. The combination of TLS and + # a checksum is sufficiently secure and durable for us to be + # confident in the request without body signing. + checksum_header = 'Content-MD5' + checksum_context = request.context.get('checksum', {}) + algorithm = checksum_context.get('request_algorithm') + if isinstance(algorithm, dict) and algorithm.get('in') == 'header': + checksum_header = algorithm['name'] + if ( + not request.url.startswith('https') + or checksum_header not in request.headers + ): + return True + + # If the input is streaming we disable body signing by default. + if request.context.get('has_streaming_input', False): + return False + + # If the S3-specific checks had no results, delegate to the generic + # checks. + return super()._should_sha256_sign_payload(request) + + def _should_add_content_sha256_header(self, explicit_payload): + # Always add X-Amz-Content-SHA256 header + return True + + +class CrtSigV4AsymAuth(BaseSigner): + REQUIRES_REGION = True + _PRESIGNED_HEADERS_BLOCKLIST = [ + 'Authorization', + 'X-Amz-Date', + 'X-Amz-Content-SHA256', + 'X-Amz-Security-Token', + ] + _SIGNATURE_TYPE = awscrt.auth.AwsSignatureType.HTTP_REQUEST_HEADERS + _USE_DOUBLE_URI_ENCODE = True + _SHOULD_NORMALIZE_URI_PATH = True + + def __init__(self, credentials, service_name, region_name): + self.credentials = credentials + self._service_name = service_name + self._region_name = region_name + self._expiration_in_seconds = None + + def add_auth(self, request): + register_feature_id("SIGV4A_SIGNING") + if self.credentials is None: + raise NoCredentialsError() + + datetime_now = get_current_datetime(remove_tzinfo=False) + + # Use existing 'X-Amz-Content-SHA256' header if able + existing_sha256 = self._get_existing_sha256(request) + + self._modify_request_before_signing(request) + + credentials_provider = awscrt.auth.AwsCredentialsProvider.new_static( + access_key_id=self.credentials.access_key, + secret_access_key=self.credentials.secret_key, + session_token=self.credentials.token, + ) + + if self._is_streaming_checksum_payload(request): + explicit_payload = STREAMING_UNSIGNED_PAYLOAD_TRAILER + elif self._should_sha256_sign_payload(request): + if existing_sha256: + explicit_payload = existing_sha256 + else: + explicit_payload = None # to be calculated during signing + else: + explicit_payload = UNSIGNED_PAYLOAD + + if self._should_add_content_sha256_header(explicit_payload): + body_header = ( + awscrt.auth.AwsSignedBodyHeaderType.X_AMZ_CONTENT_SHA_256 + ) + else: + body_header = awscrt.auth.AwsSignedBodyHeaderType.NONE + + signing_config = awscrt.auth.AwsSigningConfig( + algorithm=awscrt.auth.AwsSigningAlgorithm.V4_ASYMMETRIC, + signature_type=self._SIGNATURE_TYPE, + credentials_provider=credentials_provider, + region=self._region_name, + service=self._service_name, + date=datetime_now, + should_sign_header=self._should_sign_header, + use_double_uri_encode=self._USE_DOUBLE_URI_ENCODE, + should_normalize_uri_path=self._SHOULD_NORMALIZE_URI_PATH, + signed_body_value=explicit_payload, + signed_body_header_type=body_header, + expiration_in_seconds=self._expiration_in_seconds, + ) + crt_request = self._crt_request_from_aws_request(request) + future = awscrt.auth.aws_sign_request(crt_request, signing_config) + future.result() + self._apply_signing_changes(request, crt_request) + + def _crt_request_from_aws_request(self, aws_request): + url_parts = urlsplit(aws_request.url) + crt_path = url_parts.path if url_parts.path else '/' + if aws_request.params: + array = [] + for param, value in aws_request.params.items(): + value = str(value) + array.append(f'{param}={value}') + crt_path = crt_path + '?' + '&'.join(array) + elif url_parts.query: + crt_path = f'{crt_path}?{url_parts.query}' + + crt_headers = awscrt.http.HttpHeaders(aws_request.headers.items()) + + # CRT requires body (if it exists) to be an I/O stream. + crt_body_stream = None + if aws_request.body: + if hasattr(aws_request.body, 'seek'): + crt_body_stream = aws_request.body + else: + crt_body_stream = BytesIO(aws_request.body) + + crt_request = awscrt.http.HttpRequest( + method=aws_request.method, + path=crt_path, + headers=crt_headers, + body_stream=crt_body_stream, + ) + return crt_request + + def _apply_signing_changes(self, aws_request, signed_crt_request): + # Apply changes from signed CRT request to the AWSRequest + aws_request.headers = HTTPHeaders.from_pairs( + list(signed_crt_request.headers) + ) + + def _should_sign_header(self, name, **kwargs): + return name.lower() not in SIGNED_HEADERS_BLACKLIST + + def _modify_request_before_signing(self, request): + # This could be a retry. Make sure the previous + # authorization headers are removed first. + for h in self._PRESIGNED_HEADERS_BLOCKLIST: + if h in request.headers: + del request.headers[h] + # If necessary, add the host header + if 'host' not in request.headers: + request.headers['host'] = _host_from_url(request.url) + + def _get_existing_sha256(self, request): + return request.headers.get('X-Amz-Content-SHA256') + + def _is_streaming_checksum_payload(self, request): + checksum_context = request.context.get('checksum', {}) + algorithm = checksum_context.get('request_algorithm') + return isinstance(algorithm, dict) and algorithm.get('in') == 'trailer' + + def _should_sha256_sign_payload(self, request): + # Payloads will always be signed over insecure connections. + if not request.url.startswith('https'): + return True + + # Certain operations may have payload signing disabled by default. + # Since we don't have access to the operation model, we pass in this + # bit of metadata through the request context. + return request.context.get('payload_signing_enabled', True) + + def _should_add_content_sha256_header(self, explicit_payload): + # only add X-Amz-Content-SHA256 header if payload is explicitly set + return explicit_payload is not None + + +class CrtS3SigV4AsymAuth(CrtSigV4AsymAuth): + # For S3, we do not normalize the path. + _USE_DOUBLE_URI_ENCODE = False + _SHOULD_NORMALIZE_URI_PATH = False + + def _get_existing_sha256(self, request): + # always recalculate + return None + + def _should_sha256_sign_payload(self, request): + # S3 allows optional body signing, so to minimize the performance + # impact, we opt to not SHA256 sign the body on streaming uploads, + # provided that we're on https. + client_config = request.context.get('client_config') + s3_config = getattr(client_config, 's3', None) + + # The config could be None if it isn't set, or if the customer sets it + # to None. + if s3_config is None: + s3_config = {} + + # The explicit configuration takes precedence over any implicit + # configuration. + sign_payload = s3_config.get('payload_signing_enabled', None) + if sign_payload is not None: + return sign_payload + + # We require that both content-md5 be present and https be enabled + # to implicitly disable body signing. The combination of TLS and + # content-md5 is sufficiently secure and durable for us to be + # confident in the request without body signing. + if ( + not request.url.startswith('https') + or 'Content-MD5' not in request.headers + ): + return True + + # If the input is streaming we disable body signing by default. + if request.context.get('has_streaming_input', False): + return False + + # If the S3-specific checks had no results, delegate to the generic + # checks. + return super()._should_sha256_sign_payload(request) + + def _should_add_content_sha256_header(self, explicit_payload): + # Always add X-Amz-Content-SHA256 header + return True + + +class CrtSigV4AsymQueryAuth(CrtSigV4AsymAuth): + DEFAULT_EXPIRES = 3600 + _SIGNATURE_TYPE = awscrt.auth.AwsSignatureType.HTTP_REQUEST_QUERY_PARAMS + + def __init__( + self, credentials, service_name, region_name, expires=DEFAULT_EXPIRES + ): + super().__init__(credentials, service_name, region_name) + self._expiration_in_seconds = expires + + def _modify_request_before_signing(self, request): + super()._modify_request_before_signing(request) + + # We automatically set this header, so if it's the auto-set value we + # want to get rid of it since it doesn't make sense for presigned urls. + content_type = request.headers.get('content-type') + if content_type == 'application/x-www-form-urlencoded; charset=utf-8': + del request.headers['content-type'] + + # Now parse the original query string to a dict, inject our new query + # params, and serialize back to a query string. + url_parts = urlsplit(request.url) + # parse_qs makes each value a list, but in our case we know we won't + # have repeated keys so we know we have single element lists which we + # can convert back to scalar values. + query_string_parts = parse_qs(url_parts.query, keep_blank_values=True) + query_dict = {k: v[0] for k, v in query_string_parts.items()} + + # The spec is particular about this. It *has* to be: + # https://?& + # You can't mix the two types of params together, i.e just keep doing + # new_query_params.update(op_params) + # new_query_params.update(auth_params) + # percent_encode_sequence(new_query_params) + if request.data: + # We also need to move the body params into the query string. To + # do this, we first have to convert it to a dict. + query_dict.update(_get_body_as_dict(request)) + request.data = '' + new_query_string = percent_encode_sequence(query_dict) + # url_parts is a tuple (and therefore immutable) so we need to create + # a new url_parts with the new query string. + # - + # scheme - 0 + # netloc - 1 + # path - 2 + # query - 3 <-- we're replacing this. + # fragment - 4 + p = url_parts + new_url_parts = (p[0], p[1], p[2], new_query_string, p[4]) + request.url = urlunsplit(new_url_parts) + + def _apply_signing_changes(self, aws_request, signed_crt_request): + # Apply changes from signed CRT request to the AWSRequest + super()._apply_signing_changes(aws_request, signed_crt_request) + + signed_query = urlsplit(signed_crt_request.path).query + p = urlsplit(aws_request.url) + # urlsplit() returns a tuple (and therefore immutable) so we + # need to create new url with the new query string. + # - + # scheme - 0 + # netloc - 1 + # path - 2 + # query - 3 <-- we're replacing this. + # fragment - 4 + aws_request.url = urlunsplit((p[0], p[1], p[2], signed_query, p[4])) + + +class CrtS3SigV4AsymQueryAuth(CrtSigV4AsymQueryAuth): + """S3 SigV4A auth using query parameters. + This signer will sign a request using query parameters and signature + version 4A, i.e a "presigned url" signer. + """ + + # For S3, we do not normalize the path. + _USE_DOUBLE_URI_ENCODE = False + _SHOULD_NORMALIZE_URI_PATH = False + + def _should_sha256_sign_payload(self, request): + # From the doc link above: + # "You don't include a payload hash in the Canonical Request, because + # when you create a presigned URL, you don't know anything about the + # payload. Instead, you use a constant string "UNSIGNED-PAYLOAD". + return False + + def _should_add_content_sha256_header(self, explicit_payload): + # Never add X-Amz-Content-SHA256 header + return False + + +class CrtSigV4QueryAuth(CrtSigV4Auth): + DEFAULT_EXPIRES = 3600 + _SIGNATURE_TYPE = awscrt.auth.AwsSignatureType.HTTP_REQUEST_QUERY_PARAMS + + def __init__( + self, credentials, service_name, region_name, expires=DEFAULT_EXPIRES + ): + super().__init__(credentials, service_name, region_name) + self._expiration_in_seconds = expires + + def _modify_request_before_signing(self, request): + super()._modify_request_before_signing(request) + + # We automatically set this header, so if it's the auto-set value we + # want to get rid of it since it doesn't make sense for presigned urls. + content_type = request.headers.get('content-type') + if content_type == 'application/x-www-form-urlencoded; charset=utf-8': + del request.headers['content-type'] + + # Now parse the original query string to a dict, inject our new query + # params, and serialize back to a query string. + url_parts = urlsplit(request.url) + # parse_qs makes each value a list, but in our case we know we won't + # have repeated keys so we know we have single element lists which we + # can convert back to scalar values. + query_dict = { + k: v[0] + for k, v in parse_qs( + url_parts.query, keep_blank_values=True + ).items() + } + if request.params: + query_dict.update(request.params) + request.params = {} + # The spec is particular about this. It *has* to be: + # https://?& + # You can't mix the two types of params together, i.e just keep doing + # new_query_params.update(op_params) + # new_query_params.update(auth_params) + # percent_encode_sequence(new_query_params) + if request.data: + # We also need to move the body params into the query string. To + # do this, we first have to convert it to a dict. + query_dict.update(_get_body_as_dict(request)) + request.data = '' + new_query_string = percent_encode_sequence(query_dict) + # url_parts is a tuple (and therefore immutable) so we need to create + # a new url_parts with the new query string. + # - + # scheme - 0 + # netloc - 1 + # path - 2 + # query - 3 <-- we're replacing this. + # fragment - 4 + p = url_parts + new_url_parts = (p[0], p[1], p[2], new_query_string, p[4]) + request.url = urlunsplit(new_url_parts) + + def _apply_signing_changes(self, aws_request, signed_crt_request): + # Apply changes from signed CRT request to the AWSRequest + super()._apply_signing_changes(aws_request, signed_crt_request) + + signed_query = urlsplit(signed_crt_request.path).query + p = urlsplit(aws_request.url) + # urlsplit() returns a tuple (and therefore immutable) so we + # need to create new url with the new query string. + # - + # scheme - 0 + # netloc - 1 + # path - 2 + # query - 3 <-- we're replacing this. + # fragment - 4 + aws_request.url = urlunsplit((p[0], p[1], p[2], signed_query, p[4])) + + +class CrtS3SigV4QueryAuth(CrtSigV4QueryAuth): + """S3 SigV4 auth using query parameters. + This signer will sign a request using query parameters and signature + version 4, i.e a "presigned url" signer. + Based off of: + http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html + """ + + # For S3, we do not normalize the path. + _USE_DOUBLE_URI_ENCODE = False + _SHOULD_NORMALIZE_URI_PATH = False + + def _should_sha256_sign_payload(self, request): + # From the doc link above: + # "You don't include a payload hash in the Canonical Request, because + # when you create a presigned URL, you don't know anything about the + # payload. Instead, you use a constant string "UNSIGNED-PAYLOAD". + return False + + def _should_add_content_sha256_header(self, explicit_payload): + # Never add X-Amz-Content-SHA256 header + return False + + +# Defined at the bottom of module to ensure all Auth +# classes are defined. +CRT_AUTH_TYPE_MAPS = { + 'v4': CrtSigV4Auth, + 'v4-query': CrtSigV4QueryAuth, + 'v4a': CrtSigV4AsymAuth, + 's3v4': CrtS3SigV4Auth, + 's3v4-query': CrtS3SigV4QueryAuth, + 's3v4a': CrtS3SigV4AsymAuth, + 's3v4a-query': CrtS3SigV4AsymQueryAuth, +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/_retry.json b/venv/lib/python3.12/site-packages/botocore/data/_retry.json new file mode 100644 index 0000000..31e486e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/_retry.json @@ -0,0 +1,300 @@ +{ + "definitions": { + "throttling": { + "applies_when": { + "response": { + "service_error_code": "Throttling", + "http_status_code": 400 + } + } + }, + "throttling_exception": { + "applies_when": { + "response": { + "service_error_code": "ThrottlingException", + "http_status_code": 400 + } + } + }, + "throttled_exception": { + "applies_when": { + "response": { + "service_error_code": "ThrottledException", + "http_status_code": 400 + } + } + }, + "request_throttled_exception": { + "applies_when": { + "response": { + "service_error_code": "RequestThrottledException", + "http_status_code": 400 + } + } + }, + "too_many_requests": { + "applies_when": { + "response": { + "http_status_code": 429 + } + } + }, + "general_socket_errors": { + "applies_when": { + "socket_errors": ["GENERAL_CONNECTION_ERROR"] + } + }, + "general_server_error": { + "applies_when": { + "response": { + "http_status_code": 500 + } + } + }, + "bad_gateway": { + "applies_when": { + "response": { + "http_status_code": 502 + } + } + }, + "service_unavailable": { + "applies_when": { + "response": { + "http_status_code": 503 + } + } + }, + "gateway_timeout": { + "applies_when": { + "response": { + "http_status_code": 504 + } + } + }, + "limit_exceeded": { + "applies_when": { + "response": { + "http_status_code": 509 + } + } + }, + "throughput_exceeded": { + "applies_when": { + "response": { + "service_error_code": "ProvisionedThroughputExceededException", + "http_status_code": 400 + } + } + } + }, + "retry": { + "__default__": { + "max_attempts": 5, + "delay": { + "type": "exponential", + "base": "rand", + "growth_factor": 2 + }, + "policies": { + "general_socket_errors": {"$ref": "general_socket_errors"}, + "general_server_error": {"$ref": "general_server_error"}, + "bad_gateway": {"$ref": "bad_gateway"}, + "service_unavailable": {"$ref": "service_unavailable"}, + "gateway_timeout": {"$ref": "gateway_timeout"}, + "limit_exceeded": {"$ref": "limit_exceeded"}, + "throttling_exception": {"$ref": "throttling_exception"}, + "throttled_exception": {"$ref": "throttled_exception"}, + "request_throttled_exception": {"$ref": "request_throttled_exception"}, + "throttling": {"$ref": "throttling"}, + "too_many_requests": {"$ref": "too_many_requests"}, + "throughput_exceeded": {"$ref": "throughput_exceeded"} + } + }, + "organizations": { + "__default__": { + "policies": { + "too_many_requests": { + "applies_when": { + "response": { + "service_error_code": "TooManyRequestsException", + "http_status_code": 400 + } + } + } + } + } + }, + "dynamodb": { + "__default__": { + "max_attempts": 10, + "delay": { + "type": "exponential", + "base": 0.05, + "growth_factor": 2 + }, + "policies": { + "write_conflict": { + "applies_when": { + "response": { + "service_error_code": "ReplicatedWriteConflictException", + "http_status_code": 409 + } + } + }, + "still_processing": { + "applies_when": { + "response": { + "service_error_code": "TransactionInProgressException", + "http_status_code": 400 + } + } + }, + "crc32": { + "applies_when": { + "response": { + "crc32body": "x-amz-crc32" + } + } + } + } + } + }, + "ec2": { + "__default__": { + "policies": { + "request_limit_exceeded": { + "applies_when": { + "response": { + "service_error_code": "RequestLimitExceeded", + "http_status_code": 503 + } + } + }, + "ec2_throttled_exception": { + "applies_when": { + "response": { + "service_error_code": "EC2ThrottledException", + "http_status_code": 503 + } + } + } + } + } + }, + "cloudsearch": { + "__default__": { + "policies": { + "request_limit_exceeded": { + "applies_when": { + "response": { + "service_error_code": "BandwidthLimitExceeded", + "http_status_code": 509 + } + } + } + } + } + }, + "kinesis": { + "__default__": { + "policies": { + "request_limit_exceeded": { + "applies_when": { + "response": { + "service_error_code": "LimitExceededException", + "http_status_code": 400 + } + } + } + } + } + }, + "sqs": { + "__default__": { + "policies": { + "request_limit_exceeded": { + "applies_when": { + "response": { + "service_error_code": "RequestThrottled", + "http_status_code": 403 + } + } + } + } + } + }, + "s3": { + "__default__": { + "policies": { + "timeouts": { + "applies_when": { + "response": { + "http_status_code": 400, + "service_error_code": "RequestTimeout" + } + } + }, + "contentmd5": { + "applies_when": { + "response": { + "http_status_code": 400, + "service_error_code": "BadDigest" + } + } + } + } + } + }, + "glacier": { + "__default__": { + "policies": { + "timeouts": { + "applies_when": { + "response": { + "http_status_code": 408, + "service_error_code": "RequestTimeoutException" + } + } + } + } + } + }, + "route53": { + "__default__": { + "policies": { + "request_limit_exceeded": { + "applies_when": { + "response": { + "service_error_code": "Throttling", + "http_status_code": 400 + } + } + }, + "still_processing": { + "applies_when": { + "response": { + "service_error_code": "PriorRequestNotComplete", + "http_status_code": 400 + } + } + } + } + } + }, + "sts": { + "__default__": { + "policies": { + "idp_unreachable_error": { + "applies_when": { + "response": { + "service_error_code": "IDPCommunicationError", + "http_status_code": 400 + } + } + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..5ee6eee Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/paginators-1.json new file mode 100644 index 0000000..de88b6d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/paginators-1.json @@ -0,0 +1,70 @@ +{ + "pagination": { + "ListAnalyzedResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "analyzedResources" + }, + "ListAnalyzers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "analyzers" + }, + "ListArchiveRules": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "archiveRules" + }, + "ListFindings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findings" + }, + "ListAccessPreviewFindings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findings" + }, + "ListAccessPreviews": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "accessPreviews" + }, + "ValidatePolicy": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findings" + }, + "ListPolicyGenerations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policyGenerations" + }, + "GetFindingV2": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findingDetails" + }, + "ListFindingsV2": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findings" + }, + "GetFindingRecommendation": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recommendedSteps" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/paginators-1.sdk-extras.json new file mode 100644 index 0000000..2fe19c0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/paginators-1.sdk-extras.json @@ -0,0 +1,31 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetFindingV2": { + "non_aggregate_keys": [ + "resource", + "status", + "error", + "createdAt", + "resourceType", + "findingType", + "resourceOwnerAccount", + "analyzedAt", + "id", + "updatedAt" + ] + }, + "GetFindingRecommendation": { + "non_aggregate_keys": [ + "status", + "error", + "completedAt", + "recommendationType", + "resourceArn", + "startedAt" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/service-2.json.gz new file mode 100644 index 0000000..fd77670 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/accessanalyzer/2019-11-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/account-access/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/account-access/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..9c73975 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/account-access/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/account-access/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/account-access/2018-05-10/paginators-1.json new file mode 100644 index 0000000..f2c21e0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/account-access/2018-05-10/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListApplications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "applications" + }, + "ListEntitlements": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "entitlements" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/account-access/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/account-access/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..b781873 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/account-access/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/account-access/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/account-access/2018-05-10/waiters-2.json new file mode 100644 index 0000000..28fe0b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/account-access/2018-05-10/waiters-2.json @@ -0,0 +1,26 @@ +{ + "version" : 2, + "waiters" : { + "ApplicationActive" : { + "delay" : 5, + "maxAttempts" : 24, + "operation" : "GetApplication", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CREATE_FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETE_FAILED" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ad818ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/paginators-1.json new file mode 100644 index 0000000..5e75ec8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListRegions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Regions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/service-2.json.gz new file mode 100644 index 0000000..467dd20 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/account/2021-02-01/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..63ce884 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/paginators-1.json new file mode 100644 index 0000000..c1f4e23 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListCertificateAuthorities": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CertificateAuthorities" + }, + "ListTags": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Tags" + }, + "ListPermissions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Permissions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/service-2.json.gz new file mode 100644 index 0000000..da4b9f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/waiters-2.json new file mode 100644 index 0000000..1d48140 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/acm-pca/2017-08-22/waiters-2.json @@ -0,0 +1,64 @@ +{ + "version" : 2, + "waiters" : { + "AuditReportCreated" : { + "description" : "Wait until a Audit Report is created", + "delay" : 3, + "maxAttempts" : 60, + "operation" : "DescribeCertificateAuthorityAuditReport", + "acceptors" : [ { + "matcher" : "path", + "argument" : "AuditReportStatus", + "state" : "success", + "expected" : "SUCCESS" + }, { + "matcher" : "path", + "argument" : "AuditReportStatus", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "AccessDeniedException" + } ] + }, + "CertificateAuthorityCSRCreated" : { + "description" : "Wait until a Certificate Authority CSR is created", + "delay" : 3, + "maxAttempts" : 60, + "operation" : "GetCertificateAuthorityCsr", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : false + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "RequestInProgressException" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "AccessDeniedException" + } ] + }, + "CertificateIssued" : { + "description" : "Wait until a certificate is issued", + "delay" : 1, + "maxAttempts" : 60, + "operation" : "GetCertificate", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : false + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "RequestInProgressException" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "AccessDeniedException" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d5f8e5f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/paginators-1.json new file mode 100644 index 0000000..7ba28ca --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "ListCertificates": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxItems", + "result_key": "CertificateSummaryList" + }, + "SearchCertificates": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Results" + }, + "ListAcmeAccounts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AcmeAccounts" + }, + "ListAcmeDomainValidations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AcmeDomainValidations" + }, + "ListAcmeEndpoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AcmeEndpoints" + }, + "ListAcmeExternalAccountBindings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ExternalAccountBindings" + }, + "ListCertificateDomainValidations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxItems", + "result_key": "DomainValidationSummaryList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/service-2.json.gz new file mode 100644 index 0000000..04d000f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/waiters-2.json new file mode 100644 index 0000000..d7877d7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/acm/2015-12-08/waiters-2.json @@ -0,0 +1,106 @@ +{ + "version" : 2, + "waiters" : { + "AcmeDomainValidationDeleted" : { + "description" : "Wait until an ACME domain validation has been deleted.", + "delay" : 5, + "maxAttempts" : 60, + "operation" : "DescribeAcmeDomainValidation", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "AcmeDomainValidation.Status", + "state" : "retry", + "expected" : "DELETING" + } ] + }, + "AcmeDomainValidationValidated" : { + "description" : "Wait until an ACME domain validation reaches a terminal validation state.", + "delay" : 5, + "maxAttempts" : 60, + "operation" : "DescribeAcmeDomainValidation", + "acceptors" : [ { + "matcher" : "path", + "argument" : "AcmeDomainValidation.Status", + "state" : "success", + "expected" : "VALID" + }, { + "matcher" : "path", + "argument" : "AcmeDomainValidation.Status", + "state" : "failure", + "expected" : "INVALID" + }, { + "matcher" : "path", + "argument" : "AcmeDomainValidation.Status", + "state" : "retry", + "expected" : "VALIDATING" + } ] + }, + "AcmeEndpointActive" : { + "description" : "Wait until an ACME endpoint has finished provisioning and is ACTIVE.", + "delay" : 5, + "maxAttempts" : 60, + "operation" : "DescribeAcmeEndpoint", + "acceptors" : [ { + "matcher" : "path", + "argument" : "AcmeEndpoint.Status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "AcmeEndpoint.Status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "AcmeEndpoint.Status", + "state" : "retry", + "expected" : "CREATING" + } ] + }, + "AcmeEndpointDeleted" : { + "description" : "Wait until an ACME endpoint has been deleted.", + "delay" : 5, + "maxAttempts" : 60, + "operation" : "DescribeAcmeEndpoint", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "AcmeEndpoint.Status", + "state" : "retry", + "expected" : "DELETING" + } ] + }, + "CertificateValidated" : { + "delay" : 60, + "maxAttempts" : 5, + "operation" : "DescribeCertificate", + "acceptors" : [ { + "matcher" : "pathAll", + "argument" : "Certificate.DomainValidationOptions[].ValidationStatus", + "state" : "success", + "expected" : "SUCCESS" + }, { + "matcher" : "pathAny", + "argument" : "Certificate.DomainValidationOptions[].ValidationStatus", + "state" : "retry", + "expected" : "PENDING_VALIDATION" + }, { + "matcher" : "path", + "argument" : "Certificate.Status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "ResourceNotFoundException" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/agent-registry-control/2025-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/agent-registry-control/2025-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3a58d02 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/agent-registry-control/2025-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/agent-registry-control/2025-12-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/agent-registry-control/2025-12-01/paginators-1.json new file mode 100644 index 0000000..768d128 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/agent-registry-control/2025-12-01/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListRegistries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "registries" + }, + "ListRegistryRecords": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "registryRecords" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/agent-registry-control/2025-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/agent-registry-control/2025-12-01/service-2.json.gz new file mode 100644 index 0000000..f31d58a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/agent-registry-control/2025-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/agent-registry-control/2025-12-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/agent-registry-control/2025-12-01/waiters-2.json new file mode 100644 index 0000000..bbce1e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/agent-registry-control/2025-12-01/waiters-2.json @@ -0,0 +1,57 @@ +{ + "version" : 2, + "waiters" : { + "RegistryReady" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetRegistry", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "READY" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CREATE_FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "UPDATE_FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETE_FAILED" + } ] + }, + "RegistryRecordApproved" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetRegistryRecord", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "APPROVED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "REJECTED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CREATE_FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "UPDATE_FAILED" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/agent-registry/2025-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/agent-registry/2025-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8b7e98d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/agent-registry/2025-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/agent-registry/2025-12-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/agent-registry/2025-12-01/paginators-1.json new file mode 100644 index 0000000..52b793e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/agent-registry/2025-12-01/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListDiscoverableRegistryRecords": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "registryRecords" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/agent-registry/2025-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/agent-registry/2025-12-01/service-2.json.gz new file mode 100644 index 0000000..ae88165 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/agent-registry/2025-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/aiops/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/aiops/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..e091ef7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/aiops/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/aiops/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/aiops/2018-05-10/paginators-1.json new file mode 100644 index 0000000..7377349 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/aiops/2018-05-10/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListInvestigationGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "investigationGroups" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/aiops/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/aiops/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..e53ccfe Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/aiops/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/aiops/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/aiops/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/aiops/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8fe4aff Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/paginators-1.json new file mode 100644 index 0000000..656dd3a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListWorkspaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workspaces" + }, + "ListRuleGroupsNamespaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "ruleGroupsNamespaces" + }, + "ListScrapers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "scrapers" + }, + "ListAnomalyDetectors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "anomalyDetectors" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/service-2.json.gz new file mode 100644 index 0000000..a7f1f0f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/waiters-2.json new file mode 100644 index 0000000..0222806 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/amp/2020-08-01/waiters-2.json @@ -0,0 +1,114 @@ +{ + "version" : 2, + "waiters" : { + "AnomalyDetectorActive" : { + "description" : "Wait until the anomaly detector reaches ACTIVE status", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "DescribeAnomalyDetector", + "acceptors" : [ { + "matcher" : "path", + "argument" : "anomalyDetector.status.statusCode", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "anomalyDetector.status.statusCode", + "state" : "retry", + "expected" : "CREATING" + }, { + "matcher" : "path", + "argument" : "anomalyDetector.status.statusCode", + "state" : "retry", + "expected" : "UPDATING" + } ] + }, + "AnomalyDetectorDeleted" : { + "description" : "Wait until the anomaly detector reaches DELETED status", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "DescribeAnomalyDetector", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "anomalyDetector.status.statusCode", + "state" : "retry", + "expected" : "DELETING" + } ] + }, + "ScraperActive" : { + "description" : "Wait until a scraper reaches ACTIVE status", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "DescribeScraper", + "acceptors" : [ { + "matcher" : "path", + "argument" : "scraper.status.statusCode", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "scraper.status.statusCode", + "state" : "failure", + "expected" : "CREATION_FAILED" + } ] + }, + "ScraperDeleted" : { + "description" : "Wait until a scraper reaches DELETED status", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "DescribeScraper", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "scraper.status.statusCode", + "state" : "failure", + "expected" : "DELETION_FAILED" + } ] + }, + "WorkspaceActive" : { + "description" : "Wait until a workspace reaches ACTIVE status", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "DescribeWorkspace", + "acceptors" : [ { + "matcher" : "path", + "argument" : "workspace.status.statusCode", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "workspace.status.statusCode", + "state" : "retry", + "expected" : "UPDATING" + }, { + "matcher" : "path", + "argument" : "workspace.status.statusCode", + "state" : "retry", + "expected" : "CREATING" + } ] + }, + "WorkspaceDeleted" : { + "description" : "Wait until a workspace reaches DELETED status", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "DescribeWorkspace", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "workspace.status.statusCode", + "state" : "retry", + "expected" : "DELETING" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/amplify/2017-07-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/amplify/2017-07-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..efadef5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/amplify/2017-07-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/amplify/2017-07-25/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/amplify/2017-07-25/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/amplify/2017-07-25/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/amplify/2017-07-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/amplify/2017-07-25/paginators-1.json new file mode 100644 index 0000000..f84208e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/amplify/2017-07-25/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListApps": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "apps" + }, + "ListBranches": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "branches" + }, + "ListDomainAssociations": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "domainAssociations" + }, + "ListJobs": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "jobSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/amplify/2017-07-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/amplify/2017-07-25/service-2.json.gz new file mode 100644 index 0000000..062b143 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/amplify/2017-07-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/amplifybackend/2020-08-11/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/amplifybackend/2020-08-11/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0e5cb12 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/amplifybackend/2020-08-11/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/amplifybackend/2020-08-11/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/amplifybackend/2020-08-11/paginators-1.json new file mode 100644 index 0000000..40304c7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/amplifybackend/2020-08-11/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListBackendJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Jobs" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/amplifybackend/2020-08-11/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/amplifybackend/2020-08-11/service-2.json.gz new file mode 100644 index 0000000..3a73a8d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/amplifybackend/2020-08-11/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..166ed58 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/paginators-1.json new file mode 100644 index 0000000..d0a0242 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/paginators-1.json @@ -0,0 +1,43 @@ +{ + "pagination": { + "ListComponents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "entities" + }, + "ListThemes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "entities" + }, + "ExportComponents": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "entities" + }, + "ExportThemes": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "entities" + }, + "ExportForms": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "entities" + }, + "ListForms": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "entities" + }, + "ListCodegenJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "entities" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/service-2.json.gz new file mode 100644 index 0000000..5c278e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/amplifyuibuilder/2021-08-11/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/apigateway/2015-07-09/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/apigateway/2015-07-09/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0ffa1f5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/apigateway/2015-07-09/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/apigateway/2015-07-09/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/apigateway/2015-07-09/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/apigateway/2015-07-09/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/apigateway/2015-07-09/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/apigateway/2015-07-09/paginators-1.json new file mode 100644 index 0000000..2a875c5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/apigateway/2015-07-09/paginators-1.json @@ -0,0 +1,117 @@ +{ + "pagination": { + "GetApiKeys": { + "input_token": "position", + "output_token": "position", + "limit_key": "limit", + "result_key": "items" + }, + "GetBasePathMappings": { + "input_token": "position", + "output_token": "position", + "limit_key": "limit", + "result_key": "items" + }, + "GetClientCertificates": { + "input_token": "position", + "output_token": "position", + "limit_key": "limit", + "result_key": "items" + }, + "GetDeployments": { + "input_token": "position", + "output_token": "position", + "limit_key": "limit", + "result_key": "items" + }, + "GetDomainNames": { + "input_token": "position", + "output_token": "position", + "limit_key": "limit", + "result_key": "items" + }, + "GetModels": { + "input_token": "position", + "output_token": "position", + "limit_key": "limit", + "result_key": "items" + }, + "GetResources": { + "input_token": "position", + "output_token": "position", + "limit_key": "limit", + "result_key": "items" + }, + "GetRestApis": { + "input_token": "position", + "output_token": "position", + "limit_key": "limit", + "result_key": "items" + }, + "GetUsage": { + "input_token": "position", + "output_token": "position", + "limit_key": "limit", + "result_key": "items", + "non_aggregate_keys": [ + "usagePlanId", + "startDate", + "endDate" + ] + }, + "GetUsagePlans": { + "input_token": "position", + "output_token": "position", + "limit_key": "limit", + "result_key": "items" + }, + "GetUsagePlanKeys": { + "input_token": "position", + "output_token": "position", + "limit_key": "limit", + "result_key": "items" + }, + "GetVpcLinks": { + "input_token": "position", + "limit_key": "limit", + "output_token": "position", + "result_key": "items" + }, + "GetAuthorizers": { + "input_token": "position", + "limit_key": "limit", + "output_token": "position", + "result_key": "items" + }, + "GetDocumentationParts": { + "input_token": "position", + "limit_key": "limit", + "output_token": "position", + "result_key": "items" + }, + "GetDocumentationVersions": { + "input_token": "position", + "limit_key": "limit", + "output_token": "position", + "result_key": "items" + }, + "GetGatewayResponses": { + "input_token": "position", + "limit_key": "limit", + "output_token": "position", + "result_key": "items" + }, + "GetRequestValidators": { + "input_token": "position", + "limit_key": "limit", + "output_token": "position", + "result_key": "items" + }, + "GetSdkTypes": { + "input_token": "position", + "limit_key": "limit", + "output_token": "position", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/apigateway/2015-07-09/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/apigateway/2015-07-09/service-2.json.gz new file mode 100644 index 0000000..4df44c8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/apigateway/2015-07-09/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/apigatewaymanagementapi/2018-11-29/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/apigatewaymanagementapi/2018-11-29/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..e0bc767 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/apigatewaymanagementapi/2018-11-29/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/apigatewaymanagementapi/2018-11-29/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/apigatewaymanagementapi/2018-11-29/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/apigatewaymanagementapi/2018-11-29/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/apigatewaymanagementapi/2018-11-29/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/apigatewaymanagementapi/2018-11-29/service-2.json.gz new file mode 100644 index 0000000..9318d7a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/apigatewaymanagementapi/2018-11-29/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/apigatewayv2/2018-11-29/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/apigatewayv2/2018-11-29/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0ffa1f5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/apigatewayv2/2018-11-29/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/apigatewayv2/2018-11-29/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/apigatewayv2/2018-11-29/paginators-1.json new file mode 100644 index 0000000..0c9022f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/apigatewayv2/2018-11-29/paginators-1.json @@ -0,0 +1,94 @@ +{ + "pagination": { + "GetApis": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "GetAuthorizers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "GetDeployments": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "GetDomainNames": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "GetIntegrationResponses": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "GetIntegrations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "GetModels": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "GetRouteResponses": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "GetRoutes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "GetStages": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "ListRoutingRules": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RoutingRules" + }, + "ListPortalProducts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListPortals": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListProductPages": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListProductRestEndpointPages": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/apigatewayv2/2018-11-29/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/apigatewayv2/2018-11-29/service-2.json.gz new file mode 100644 index 0000000..8825fa9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/apigatewayv2/2018-11-29/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f8c0d32 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/examples-1.json new file mode 100644 index 0000000..664e05e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/examples-1.json @@ -0,0 +1,720 @@ +{ + "version": "1.0", + "examples": { + "CreateApplication": [ + { + "input": { + "Description": "An application used for creating an example.", + "Name": "example-application" + }, + "output": { + "Description": "An application used for creating an example.", + "Id": "339ohji", + "Name": "example-application" + }, + "comments": { + }, + "description": "The following create-application example creates an application in AWS AppConfig.", + "id": "to-create-an-application-1632264511615", + "title": "To create an application" + } + ], + "CreateConfigurationProfile": [ + { + "input": { + "ApplicationId": "339ohji", + "LocationUri": "ssm-parameter://Example-Parameter", + "Name": "Example-Configuration-Profile", + "RetrievalRoleArn": "arn:aws:iam::111122223333:role/Example-App-Config-Role" + }, + "output": { + "ApplicationId": "339ohji", + "Id": "ur8hx2f", + "LocationUri": "ssm-parameter://Example-Parameter", + "Name": "Example-Configuration-Profile", + "RetrievalRoleArn": "arn:aws:iam::111122223333:role/Example-App-Config-Role" + }, + "comments": { + }, + "description": "The following create-configuration-profile example creates a configuration profile using a configuration stored in Parameter Store, a capability of Systems Manager.", + "id": "to-create-a-configuration-profile-1632264580336", + "title": "To create a configuration profile" + } + ], + "CreateDeploymentStrategy": [ + { + "input": { + "DeploymentDurationInMinutes": 15, + "GrowthFactor": 25, + "Name": "Example-Deployment", + "ReplicateTo": "SSM_DOCUMENT" + }, + "output": { + "DeploymentDurationInMinutes": 15, + "FinalBakeTimeInMinutes": 0, + "GrowthFactor": 25, + "GrowthType": "LINEAR", + "Id": "1225qzk", + "Name": "Example-Deployment", + "ReplicateTo": "SSM_DOCUMENT" + }, + "comments": { + }, + "description": "The following create-deployment-strategy example creates a deployment strategy called Example-Deployment that takes 15 minutes and deploys the configuration to 25% of the application at a time. The strategy is also copied to an SSM Document.", + "id": "to-create-a-deployment-strategy-1632264783812", + "title": "To create a deployment strategy" + } + ], + "CreateEnvironment": [ + { + "input": { + "ApplicationId": "339ohji", + "Name": "Example-Environment" + }, + "output": { + "ApplicationId": "339ohji", + "Id": "54j1r29", + "Name": "Example-Environment", + "State": "READY_FOR_DEPLOYMENT" + }, + "comments": { + }, + "description": "The following create-environment example creates an AWS AppConfig environment named Example-Environment using the application you created using create-application", + "id": "to-create-an-environment-1632265124975", + "title": "To create an environment" + } + ], + "CreateHostedConfigurationVersion": [ + { + "input": { + "ApplicationId": "339ohji", + "ConfigurationProfileId": "ur8hx2f", + "Content": "eyAiTmFtZSI6ICJFeGFtcGxlQXBwbGljYXRpb24iLCAiSWQiOiBFeGFtcGxlSUQsICJSYW5rIjogNyB9", + "ContentType": "text", + "LatestVersionNumber": 1 + }, + "output": { + "ApplicationId": "339ohji", + "ConfigurationProfileId": "ur8hx2f", + "ContentType": "text", + "VersionNumber": 1 + }, + "comments": { + }, + "description": "The following create-hosted-configuration-version example creates a new configuration in the AWS AppConfig configuration store.", + "id": "to-create-a-hosted-configuration-version-1632265196980", + "title": "To create a hosted configuration version" + } + ], + "DeleteApplication": [ + { + "input": { + "ApplicationId": "339ohji" + }, + "comments": { + }, + "description": "The following delete-application example deletes the specified application. \n", + "id": "to-delete-an-application-1632265343951", + "title": "To delete an application" + } + ], + "DeleteConfigurationProfile": [ + { + "input": { + "ApplicationId": "339ohji", + "ConfigurationProfileId": "ur8hx2f" + }, + "comments": { + }, + "description": "The following delete-configuration-profile example deletes the specified configuration profile.", + "id": "to-delete-a-configuration-profile-1632265401308", + "title": "To delete a configuration profile" + } + ], + "DeleteDeploymentStrategy": [ + { + "input": { + "DeploymentStrategyId": "1225qzk" + }, + "comments": { + }, + "description": "The following delete-deployment-strategy example deletes the specified deployment strategy.", + "id": "to-delete-a-deployment-strategy-1632265473708", + "title": "To delete a deployment strategy" + } + ], + "DeleteEnvironment": [ + { + "input": { + "ApplicationId": "339ohji", + "EnvironmentId": "54j1r29" + }, + "comments": { + }, + "description": "The following delete-environment example deletes the specified application environment.", + "id": "to-delete-an-environment-1632265641044", + "title": "To delete an environment" + } + ], + "DeleteHostedConfigurationVersion": [ + { + "input": { + "ApplicationId": "339ohji", + "ConfigurationProfileId": "ur8hx2f", + "VersionNumber": 1 + }, + "comments": { + }, + "description": "The following delete-hosted-configuration-version example deletes a configuration version hosted in the AWS AppConfig configuration store.", + "id": "to-delete-a-hosted-configuration-version-1632265720740", + "title": "To delete a hosted configuration version" + } + ], + "GetApplication": [ + { + "input": { + "ApplicationId": "339ohji" + }, + "output": { + "Id": "339ohji", + "Name": "example-application" + }, + "comments": { + }, + "description": "The following get-application example lists the details of the specified application.", + "id": "to-list-details-of-an-application-1632265864702", + "title": "To list details of an application" + } + ], + "GetConfiguration": [ + { + "input": { + "Application": "example-application", + "ClientId": "example-id", + "Configuration": "Example-Configuration-Profile", + "Environment": "Example-Environment" + }, + "output": { + "ConfigurationVersion": "1", + "ContentType": "application/octet-stream" + }, + "comments": { + }, + "description": "The following get-configuration example returns the configuration details of the example application. On subsequent calls to get-configuration, use the client-configuration-version parameter to only update the configuration of your application if the version has changed. Only updating the configuration when the version has changed avoids excess charges incurred by calling get-configuration.", + "id": "to-retrieve-configuration-details-1632265954314", + "title": "To retrieve configuration details" + } + ], + "GetConfigurationProfile": [ + { + "input": { + "ApplicationId": "339ohji", + "ConfigurationProfileId": "ur8hx2f" + }, + "output": { + "ApplicationId": "339ohji", + "Id": "ur8hx2f", + "LocationUri": "ssm-parameter://Example-Parameter", + "Name": "Example-Configuration-Profile", + "RetrievalRoleArn": "arn:aws:iam::111122223333:role/Example-App-Config-Role" + }, + "comments": { + }, + "description": "The following get-configuration-profile example returns the details of the specified configuration profile.", + "id": "to-retrieve-configuration-profile-details-1632266081013", + "title": "To retrieve configuration profile details" + } + ], + "GetDeployment": [ + { + "input": { + "ApplicationId": "339ohji", + "DeploymentNumber": 1, + "EnvironmentId": "54j1r29" + }, + "output": { + "ApplicationId": "339ohji", + "CompletedAt": "2021-09-17T21:59:03.888000+00:00", + "ConfigurationLocationUri": "ssm-parameter://Example-Parameter", + "ConfigurationName": "Example-Configuration-Profile", + "ConfigurationProfileId": "ur8hx2f", + "ConfigurationVersion": "1", + "DeploymentDurationInMinutes": 15, + "DeploymentNumber": 1, + "DeploymentStrategyId": "1225qzk", + "EnvironmentId": "54j1r29", + "EventLog": [ + { + "Description": "Deployment completed", + "EventType": "DEPLOYMENT_COMPLETED", + "OccurredAt": "2021-09-17T21:59:03.888000+00:00", + "TriggeredBy": "APPCONFIG" + }, + { + "Description": "Deployment bake time started", + "EventType": "BAKE_TIME_STARTED", + "OccurredAt": "2021-09-17T21:58:57.722000+00:00", + "TriggeredBy": "APPCONFIG" + }, + { + "Description": "Configuration available to 100.00% of clients", + "EventType": "PERCENTAGE_UPDATED", + "OccurredAt": "2021-09-17T21:55:56.816000+00:00", + "TriggeredBy": "APPCONFIG" + }, + { + "Description": "Configuration available to 75.00% of clients", + "EventType": "PERCENTAGE_UPDATED", + "OccurredAt": "2021-09-17T21:52:56.567000+00:00", + "TriggeredBy": "APPCONFIG" + }, + { + "Description": "Configuration available to 50.00% of clients", + "EventType": "PERCENTAGE_UPDATED", + "OccurredAt": "2021-09-17T21:49:55.737000+00:00", + "TriggeredBy": "APPCONFIG" + }, + { + "Description": "Configuration available to 25.00% of clients", + "EventType": "PERCENTAGE_UPDATED", + "OccurredAt": "2021-09-17T21:46:55.187000+00:00", + "TriggeredBy": "APPCONFIG" + }, + { + "Description": "Deployment started", + "EventType": "DEPLOYMENT_STARTED", + "OccurredAt": "2021-09-17T21:43:54.205000+00:00", + "TriggeredBy": "USER" + } + ], + "FinalBakeTimeInMinutes": 0, + "GrowthFactor": 25, + "GrowthType": "LINEAR", + "PercentageComplete": 100, + "StartedAt": "2021-09-17T21:43:54.205000+00:00", + "State": "COMPLETE" + }, + "comments": { + }, + "description": "The following get-deployment example lists details of the deployment to the application in the specified environment and deployment.", + "id": "to-retrieve-deployment-details-1633976766883", + "title": "To retrieve deployment details" + } + ], + "GetDeploymentStrategy": [ + { + "input": { + "DeploymentStrategyId": "1225qzk" + }, + "output": { + "DeploymentDurationInMinutes": 15, + "FinalBakeTimeInMinutes": 0, + "GrowthFactor": 25, + "GrowthType": "LINEAR", + "Id": "1225qzk", + "Name": "Example-Deployment", + "ReplicateTo": "SSM_DOCUMENT" + }, + "comments": { + }, + "description": "The following get-deployment-strategy example lists the details of the specified deployment strategy.", + "id": "to-retrieve-details-of-a-deployment-strategy-1632266385805", + "title": "To retrieve details of a deployment strategy" + } + ], + "GetEnvironment": [ + { + "input": { + "ApplicationId": "339ohji", + "EnvironmentId": "54j1r29" + }, + "output": { + "ApplicationId": "339ohji", + "Id": "54j1r29", + "Name": "Example-Environment", + "State": "READY_FOR_DEPLOYMENT" + }, + "comments": { + }, + "description": "The following get-environment example returns the details and state of the specified environment.", + "id": "to-retrieve-environment-details-1632266924806", + "title": "To retrieve environment details" + } + ], + "GetHostedConfigurationVersion": [ + { + "input": { + "ApplicationId": "339ohji", + "ConfigurationProfileId": "ur8hx2f", + "VersionNumber": 1 + }, + "output": { + "ApplicationId": "339ohji", + "ConfigurationProfileId": "ur8hx2f", + "ContentType": "application/json", + "VersionNumber": 1 + }, + "comments": { + }, + "description": "The following get-hosted-configuration-version example retrieves the configuration details of the AWS AppConfig hosted configuration.", + "id": "to-retrieve-hosted-configuration-details-1632267003527", + "title": "To retrieve hosted configuration details" + } + ], + "ListApplications": [ + { + "input": { + }, + "output": { + "Items": [ + { + "Description": "An application used for creating an example.", + "Id": "339ohji", + "Name": "test-application" + }, + { + "Id": "rwalwu7", + "Name": "Test-Application" + } + ] + }, + "comments": { + }, + "description": "The following list-applications example lists the available applications in your AWS account.", + "id": "to-list-the-available-applications-1632267111131", + "title": "To list the available applications" + } + ], + "ListConfigurationProfiles": [ + { + "input": { + "ApplicationId": "339ohji" + }, + "output": { + "Items": [ + { + "ApplicationId": "339ohji", + "Id": "ur8hx2f", + "LocationUri": "ssm-parameter://Example-Parameter", + "Name": "Example-Configuration-Profile" + } + ] + }, + "comments": { + }, + "description": "The following list-configuration-profiles example lists the available configuration profiles for the specified application.", + "id": "to-list-the-available-configuration-profiles-1632267193265", + "title": "To list the available configuration profiles" + } + ], + "ListDeploymentStrategies": [ + { + "input": { + }, + "output": { + "Items": [ + { + "DeploymentDurationInMinutes": 15, + "FinalBakeTimeInMinutes": 0, + "GrowthFactor": 25, + "GrowthType": "LINEAR", + "Id": "1225qzk", + "Name": "Example-Deployment", + "ReplicateTo": "SSM_DOCUMENT" + } + ] + }, + "comments": { + }, + "description": "The following list-deployment-strategies example lists the available deployment strategies in your AWS account.", + "id": "to-list-the-available-deployment-strategies-1632267364180", + "title": "To list the available deployment strategies" + } + ], + "ListDeployments": [ + { + "input": { + "ApplicationId": "339ohji", + "EnvironmentId": "54j1r29" + }, + "output": { + "Items": [ + { + "CompletedAt": "2021-09-17T21:59:03.888000+00:00", + "ConfigurationName": "Example-Configuration-Profile", + "ConfigurationVersion": "1", + "DeploymentDurationInMinutes": 15, + "DeploymentNumber": 1, + "FinalBakeTimeInMinutes": 0, + "GrowthFactor": 25, + "GrowthType": "LINEAR", + "PercentageComplete": 100, + "StartedAt": "2021-09-17T21:43:54.205000+00:00", + "State": "COMPLETE" + } + ] + }, + "comments": { + }, + "description": "The following list-deployments example lists the available deployments in your AWS account for the specified application and environment.", + "id": "to-list-the-available-deployments-1632267282025", + "title": "To list the available deployments" + } + ], + "ListEnvironments": [ + { + "input": { + "ApplicationId": "339ohji" + }, + "output": { + "Items": [ + { + "ApplicationId": "339ohji", + "Id": "54j1r29", + "Name": "Example-Environment", + "State": "READY_FOR_DEPLOYMENT" + } + ] + }, + "comments": { + }, + "description": "The following list-environments example lists the available environments in your AWS account for the specified application.", + "id": "to-list-the-available-environments-1632267474389", + "title": "To list the available environments" + } + ], + "ListHostedConfigurationVersions": [ + { + "input": { + "ApplicationId": "339ohji", + "ConfigurationProfileId": "ur8hx2f" + }, + "output": { + "Items": [ + { + "ApplicationId": "339ohji", + "ConfigurationProfileId": "ur8hx2f", + "ContentType": "application/json", + "VersionNumber": 1 + } + ] + }, + "comments": { + }, + "description": "The following list-hosted-configuration-versions example lists the configurations versions hosted in the AWS AppConfig hosted configuration store for the specified application and configuration profile.", + "id": "to-list-the-available-hosted-configuration-versions-1632267647667", + "title": "To list the available hosted configuration versions" + } + ], + "ListTagsForResource": [ + { + "input": { + "ResourceArn": "arn:aws:appconfig:us-east-1:111122223333:application/339ohji" + }, + "output": { + "Tags": { + "group1": "1" + } + }, + "comments": { + }, + "description": "The following list-tags-for-resource example lists the tags of a specified application.", + "id": "to-list-the-tags-of-an-application-1632328796560", + "title": "To list the tags of an application" + } + ], + "StartDeployment": [ + { + "input": { + "ApplicationId": "339ohji", + "ConfigurationProfileId": "ur8hx2f", + "ConfigurationVersion": "1", + "DeploymentStrategyId": "1225qzk", + "Description": "", + "EnvironmentId": "54j1r29", + "Tags": { + } + }, + "output": { + "ApplicationId": "339ohji", + "ConfigurationLocationUri": "ssm-parameter://Example-Parameter", + "ConfigurationName": "Example-Configuration-Profile", + "ConfigurationProfileId": "ur8hx2f", + "ConfigurationVersion": "1", + "DeploymentDurationInMinutes": 15, + "DeploymentNumber": 1, + "DeploymentStrategyId": "1225qzk", + "EnvironmentId": "54j1r29", + "EventLog": [ + { + "Description": "Deployment started", + "EventType": "DEPLOYMENT_STARTED", + "OccurredAt": "2021-09-17T21:43:54.205000+00:00", + "TriggeredBy": "USER" + } + ], + "FinalBakeTimeInMinutes": 0, + "GrowthFactor": 25, + "GrowthType": "LINEAR", + "PercentageComplete": 1.0, + "StartedAt": "2021-09-17T21:43:54.205000+00:00", + "State": "DEPLOYING" + }, + "comments": { + }, + "description": "The following start-deployment example starts a deployment to the application using the specified environment, deployment strategy, and configuration profile.", + "id": "to-start-a-configuration-deployment-1632328956790", + "title": "To start a configuration deployment" + } + ], + "StopDeployment": [ + { + "input": { + "ApplicationId": "339ohji", + "DeploymentNumber": 2, + "EnvironmentId": "54j1r29" + }, + "output": { + "DeploymentDurationInMinutes": 15, + "DeploymentNumber": 2, + "FinalBakeTimeInMinutes": 0, + "GrowthFactor": 25.0, + "PercentageComplete": 1.0 + }, + "comments": { + }, + "description": "The following stop-deployment example stops the deployment of an application configuration to the specified environment.", + "id": "to-stop-configuration-deployment-1632329139126", + "title": "To stop configuration deployment" + } + ], + "TagResource": [ + { + "input": { + "ResourceArn": "arn:aws:appconfig:us-east-1:111122223333:application/339ohji", + "Tags": { + "group1": "1" + } + }, + "comments": { + }, + "description": "The following tag-resource example tags an application resource.", + "id": "to-tag-an-application-1632330350645", + "title": "To tag an application" + } + ], + "UntagResource": [ + { + "input": { + "ResourceArn": "arn:aws:appconfig:us-east-1:111122223333:application/339ohji", + "TagKeys": [ + "group1" + ] + }, + "comments": { + }, + "description": "The following untag-resource example removes the group1 tag from the specified application.", + "id": "to-remove-a-tag-from-an-application-1632330429881", + "title": "To remove a tag from an application" + } + ], + "UpdateApplication": [ + { + "input": { + "ApplicationId": "339ohji", + "Description": "", + "Name": "Example-Application" + }, + "output": { + "Description": "An application used for creating an example.", + "Id": "339ohji", + "Name": "Example-Application" + }, + "comments": { + }, + "description": "The following update-application example updates the name of the specified application.", + "id": "to-update-an-application-1632330585893", + "title": "To update an application" + } + ], + "UpdateConfigurationProfile": [ + { + "input": { + "ApplicationId": "339ohji", + "ConfigurationProfileId": "ur8hx2f", + "Description": "Configuration profile used for examples." + }, + "output": { + "ApplicationId": "339ohji", + "Description": "Configuration profile used for examples.", + "Id": "ur8hx2f", + "LocationUri": "ssm-parameter://Example-Parameter", + "Name": "Example-Configuration-Profile", + "RetrievalRoleArn": "arn:aws:iam::111122223333:role/Example-App-Config-Role" + }, + "comments": { + }, + "description": "The following update-configuration-profile example updates the description of the specified configuration profile.", + "id": "to-update-a-configuration-profile-1632330721974", + "title": "To update a configuration profile" + } + ], + "UpdateDeploymentStrategy": [ + { + "input": { + "DeploymentStrategyId": "1225qzk", + "FinalBakeTimeInMinutes": 20 + }, + "output": { + "DeploymentDurationInMinutes": 15, + "FinalBakeTimeInMinutes": 20, + "GrowthFactor": 25, + "GrowthType": "LINEAR", + "Id": "1225qzk", + "Name": "Example-Deployment", + "ReplicateTo": "SSM_DOCUMENT" + }, + "comments": { + }, + "description": "The following update-deployment-strategy example updates final bake time to 20 minutes in the specified deployment strategy. ::\n", + "id": "to-update-a-deployment-strategy-1632330896602", + "title": "To update a deployment strategy" + } + ], + "UpdateEnvironment": [ + { + "input": { + "ApplicationId": "339ohji", + "Description": "An environment for examples.", + "EnvironmentId": "54j1r29" + }, + "output": { + "ApplicationId": "339ohji", + "Description": "An environment for examples.", + "Id": "54j1r29", + "Name": "Example-Environment", + "State": "ROLLED_BACK" + }, + "comments": { + }, + "description": "The following update-environment example updates an environment's description.", + "id": "to-update-an-environment-1632331382428", + "title": "To update an environment" + } + ], + "ValidateConfiguration": [ + { + "input": { + "ApplicationId": "abc1234", + "ConfigurationProfileId": "ur8hx2f", + "ConfigurationVersion": "1" + }, + "comments": { + }, + "description": "The following validate-configuration example uses the validators in a configuration profile to validate a configuration.", + "id": "to-validate-a-configuration-1632331491365", + "title": "To validate a configuration" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/paginators-1.json new file mode 100644 index 0000000..43976e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/paginators-1.json @@ -0,0 +1,70 @@ +{ + "pagination": { + "ListApplications": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListConfigurationProfiles": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListDeploymentStrategies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListDeployments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListEnvironments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListExtensionAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListExtensions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListHostedConfigurationVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListExperimentDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListExperimentRunEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListExperimentRuns": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/service-2.json.gz new file mode 100644 index 0000000..5478471 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/waiters-2.json new file mode 100644 index 0000000..faaf59d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appconfig/2019-10-09/waiters-2.json @@ -0,0 +1,55 @@ +{ + "version": 2, + "waiters": { + "EnvironmentReadyForDeployment": { + "operation": "GetEnvironment", + "delay": 30, + "maxAttempts": 999, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "ReadyForDeployment" + }, + { + "state": "failure", + "matcher": "path", + "argument": "State", + "expected": "RolledBack" + }, + { + "state": "failure", + "matcher": "path", + "argument": "State", + "expected": "Reverted" + } + ] + }, + "DeploymentComplete": { + "operation": "GetDeployment", + "delay": 30, + "maxAttempts": 999, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "COMPLETE" + }, + { + "state": "failure", + "matcher": "path", + "argument": "State", + "expected": "ROLLED_BACK" + }, + { + "state": "failure", + "matcher": "path", + "argument": "State", + "expected": "REVERTED" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appconfigdata/2021-11-11/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appconfigdata/2021-11-11/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..653da79 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appconfigdata/2021-11-11/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appconfigdata/2021-11-11/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/appconfigdata/2021-11-11/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appconfigdata/2021-11-11/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appconfigdata/2021-11-11/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/appconfigdata/2021-11-11/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appconfigdata/2021-11-11/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appconfigdata/2021-11-11/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appconfigdata/2021-11-11/service-2.json.gz new file mode 100644 index 0000000..66f5667 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appconfigdata/2021-11-11/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appfabric/2023-05-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appfabric/2023-05-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..fcb9e49 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appfabric/2023-05-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appfabric/2023-05-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/appfabric/2023-05-19/paginators-1.json new file mode 100644 index 0000000..8138e8a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appfabric/2023-05-19/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListAppAuthorizations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "appAuthorizationSummaryList" + }, + "ListAppBundles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "appBundleSummaryList" + }, + "ListIngestionDestinations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "ingestionDestinations" + }, + "ListIngestions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "ingestions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appfabric/2023-05-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appfabric/2023-05-19/service-2.json.gz new file mode 100644 index 0000000..3acd1ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appfabric/2023-05-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appfabric/2023-05-19/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/appfabric/2023-05-19/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appfabric/2023-05-19/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appflow/2020-08-23/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appflow/2020-08-23/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..41da78a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appflow/2020-08-23/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appflow/2020-08-23/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/appflow/2020-08-23/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appflow/2020-08-23/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appflow/2020-08-23/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/appflow/2020-08-23/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appflow/2020-08-23/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appflow/2020-08-23/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appflow/2020-08-23/service-2.json.gz new file mode 100644 index 0000000..05e514c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appflow/2020-08-23/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appintegrations/2020-07-29/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appintegrations/2020-07-29/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4873d96 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appintegrations/2020-07-29/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appintegrations/2020-07-29/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/appintegrations/2020-07-29/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appintegrations/2020-07-29/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appintegrations/2020-07-29/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/appintegrations/2020-07-29/paginators-1.json new file mode 100644 index 0000000..64b4b5c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appintegrations/2020-07-29/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListApplications": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Applications" + }, + "ListDataIntegrationAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DataIntegrationAssociations" + }, + "ListDataIntegrations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DataIntegrations" + }, + "ListEventIntegrationAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EventIntegrationAssociations" + }, + "ListEventIntegrations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EventIntegrations" + }, + "ListApplicationAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ApplicationAssociations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appintegrations/2020-07-29/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appintegrations/2020-07-29/service-2.json.gz new file mode 100644 index 0000000..cdafaa9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appintegrations/2020-07-29/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/application-autoscaling/2016-02-06/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/application-autoscaling/2016-02-06/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0f759ff Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/application-autoscaling/2016-02-06/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/application-autoscaling/2016-02-06/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/application-autoscaling/2016-02-06/examples-1.json new file mode 100644 index 0000000..5abcd55 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/application-autoscaling/2016-02-06/examples-1.json @@ -0,0 +1,221 @@ +{ + "version": "1.0", + "examples": { + "DeleteScalingPolicy": [ + { + "input": { + "PolicyName": "web-app-cpu-lt-25", + "ResourceId": "service/default/web-app", + "ScalableDimension": "ecs:service:DesiredCount", + "ServiceNamespace": "ecs" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes a scaling policy for the Amazon ECS service called web-app, which is running in the default cluster.", + "id": "to-delete-a-scaling-policy-1470863892689", + "title": "To delete a scaling policy" + } + ], + "DeregisterScalableTarget": [ + { + "input": { + "ResourceId": "service/default/web-app", + "ScalableDimension": "ecs:service:DesiredCount", + "ServiceNamespace": "ecs" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deregisters a scalable target for an Amazon ECS service called web-app that is running in the default cluster.", + "id": "to-deregister-a-scalable-target-1470864164895", + "title": "To deregister a scalable target" + } + ], + "DescribeScalableTargets": [ + { + "input": { + "ServiceNamespace": "ecs" + }, + "output": { + "ScalableTargets": [ + { + "CreationTime": "2019-05-06T11:21:46.199Z", + "MaxCapacity": 10, + "MinCapacity": 1, + "ResourceId": "service/default/web-app", + "RoleARN": "arn:aws:iam::012345678910:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService", + "ScalableDimension": "ecs:service:DesiredCount", + "ServiceNamespace": "ecs", + "SuspendedState": { + "DynamicScalingInSuspended": false, + "DynamicScalingOutSuspended": false, + "ScheduledScalingSuspended": false + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the scalable targets for the ECS service namespace.", + "id": "to-describe-scalable-targets-1470864286961", + "title": "To describe scalable targets" + } + ], + "DescribeScalingActivities": [ + { + "input": { + "ResourceId": "service/default/web-app", + "ScalableDimension": "ecs:service:DesiredCount", + "ServiceNamespace": "ecs" + }, + "output": { + "ScalingActivities": [ + { + "ActivityId": "e6c5f7d1-dbbb-4a3f-89b2-51f33e766399", + "Cause": "monitor alarm web-app-cpu-lt-25 in state ALARM triggered policy web-app-cpu-lt-25", + "Description": "Setting desired count to 1.", + "EndTime": "2019-05-06T16:04:32.111Z", + "ResourceId": "service/default/web-app", + "ScalableDimension": "ecs:service:DesiredCount", + "ServiceNamespace": "ecs", + "StartTime": "2019-05-06T16:03:58.171Z", + "StatusCode": "Successful", + "StatusMessage": "Successfully set desired count to 1. Change successfully fulfilled by ecs." + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the scaling activities for an Amazon ECS service called web-app that is running in the default cluster.", + "id": "to-describe-scaling-activities-for-a-scalable-target-1470864398629", + "title": "To describe scaling activities for a scalable target" + } + ], + "DescribeScalingPolicies": [ + { + "input": { + "ServiceNamespace": "ecs" + }, + "output": { + "NextToken": "", + "ScalingPolicies": [ + { + "Alarms": [ + { + "AlarmARN": "arn:aws:cloudwatch:us-west-2:012345678910:alarm:web-app-cpu-gt-75", + "AlarmName": "web-app-cpu-gt-75" + } + ], + "CreationTime": "2019-05-06T12:11:39.230Z", + "PolicyARN": "arn:aws:autoscaling:us-west-2:012345678910:scalingPolicy:6d8972f3-efc8-437c-92d1-6270f29a66e7:resource/ecs/service/default/web-app:policyName/web-app-cpu-gt-75", + "PolicyName": "web-app-cpu-gt-75", + "PolicyType": "StepScaling", + "ResourceId": "service/default/web-app", + "ScalableDimension": "ecs:service:DesiredCount", + "ServiceNamespace": "ecs", + "StepScalingPolicyConfiguration": { + "AdjustmentType": "PercentChangeInCapacity", + "Cooldown": 60, + "StepAdjustments": [ + { + "MetricIntervalLowerBound": 0, + "ScalingAdjustment": 200 + } + ] + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the scaling policies for the ECS service namespace.", + "id": "to-describe-scaling-policies-1470864609734", + "title": "To describe scaling policies" + } + ], + "PutScalingPolicy": [ + { + "input": { + "PolicyName": "cpu75-target-tracking-scaling-policy", + "PolicyType": "TargetTrackingScaling", + "ResourceId": "service/default/web-app", + "ScalableDimension": "ecs:service:DesiredCount", + "ServiceNamespace": "ecs", + "TargetTrackingScalingPolicyConfiguration": { + "PredefinedMetricSpecification": { + "PredefinedMetricType": "ECSServiceAverageCPUUtilization" + }, + "ScaleInCooldown": 60, + "ScaleOutCooldown": 60, + "TargetValue": 75 + } + }, + "output": { + "Alarms": [ + { + "AlarmARN": "arn:aws:cloudwatch:us-west-2:012345678910:alarm:TargetTracking-service/default/web-app-AlarmHigh-d4f0770c-b46e-434a-a60f-3b36d653feca", + "AlarmName": "TargetTracking-service/default/web-app-AlarmHigh-d4f0770c-b46e-434a-a60f-3b36d653feca" + }, + { + "AlarmARN": "arn:aws:cloudwatch:us-west-2:012345678910:alarm:TargetTracking-service/default/web-app-AlarmLow-1b437334-d19b-4a63-a812-6c67aaf2910d", + "AlarmName": "TargetTracking-service/default/web-app-AlarmLow-1b437334-d19b-4a63-a812-6c67aaf2910d" + } + ], + "PolicyARN": "arn:aws:autoscaling:us-west-2:012345678910:scalingPolicy:6d8972f3-efc8-437c-92d1-6270f29a66e7:resource/ecs/service/default/web-app:policyName/cpu75-target-tracking-scaling-policy" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example applies a target tracking scaling policy with a predefined metric specification to an Amazon ECS service called web-app in the default cluster. The policy keeps the average CPU utilization of the service at 75 percent, with scale-out and scale-in cooldown periods of 60 seconds.", + "id": "to-apply-a-target-tracking-scaling-policy-with-a-predefined-metric-specification-1569364247984", + "title": "To apply a target tracking scaling policy with a predefined metric specification" + } + ], + "RegisterScalableTarget": [ + { + "input": { + "MaxCapacity": 10, + "MinCapacity": 1, + "ResourceId": "service/default/web-app", + "ScalableDimension": "ecs:service:DesiredCount", + "ServiceNamespace": "ecs" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example registers a scalable target from an Amazon ECS service called web-app that is running on the default cluster, with a minimum desired count of 1 task and a maximum desired count of 10 tasks.", + "id": "to-register-a-new-scalable-target-1470864910380", + "title": "To register an ECS service as a scalable target" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/application-autoscaling/2016-02-06/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/application-autoscaling/2016-02-06/paginators-1.json new file mode 100644 index 0000000..7ec8f3a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/application-autoscaling/2016-02-06/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "DescribeScalableTargets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ScalableTargets" + }, + "DescribeScalingActivities": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ScalingActivities" + }, + "DescribeScalingPolicies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ScalingPolicies" + }, + "DescribeScheduledActions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ScheduledActions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/application-autoscaling/2016-02-06/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/application-autoscaling/2016-02-06/service-2.json.gz new file mode 100644 index 0000000..d55afa0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/application-autoscaling/2016-02-06/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/application-insights/2018-11-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/application-insights/2018-11-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..63bdcd5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/application-insights/2018-11-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/application-insights/2018-11-25/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/application-insights/2018-11-25/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/application-insights/2018-11-25/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/application-insights/2018-11-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/application-insights/2018-11-25/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/application-insights/2018-11-25/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/application-insights/2018-11-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/application-insights/2018-11-25/service-2.json.gz new file mode 100644 index 0000000..d55bd11 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/application-insights/2018-11-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..317ed0f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/paginators-1.json new file mode 100644 index 0000000..6c2ab25 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "ListServiceDependencies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ServiceDependencies" + }, + "ListServiceDependents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ServiceDependents" + }, + "ListServiceLevelObjectives": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SloSummaries" + }, + "ListServiceOperations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ServiceOperations" + }, + "ListServices": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ServiceSummaries" + }, + "ListServiceLevelObjectiveExclusionWindows": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ExclusionWindows" + }, + "ListServiceStates": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ServiceStates" + }, + "ListEntityEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ChangeEvents" + }, + "GetInstrumentationConfigurationStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Events" + }, + "ListInstrumentationConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "LatestConfigurations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/paginators-1.sdk-extras.json new file mode 100644 index 0000000..448bf1d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/paginators-1.sdk-extras.json @@ -0,0 +1,61 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListServiceDependencies": { + "non_aggregate_keys": [ + "StartTime", + "EndTime" + ] + }, + "ListServiceDependents": { + "non_aggregate_keys": [ + "StartTime", + "EndTime" + ] + }, + "ListServiceOperations": { + "non_aggregate_keys": [ + "StartTime", + "EndTime" + ] + }, + "ListServices": { + "non_aggregate_keys": [ + "StartTime", + "EndTime" + ] + }, + "ListServiceStates": { + "non_aggregate_keys": [ + "StartTime", + "EndTime" + ] + }, + "ListEntityEvents": { + "non_aggregate_keys": [ + "StartTime", + "EndTime" + ] + }, + "GetInstrumentationConfigurationStatus": { + "non_aggregate_keys": [ + "Service", + "SignalType", + "Environment", + "Location", + "Status" + ] + }, + "ListInstrumentationConfigurations": { + "non_aggregate_keys": [ + "Service", + "Environment", + "SyncedAt", + "SyncInterval", + "Changed" + ] + } + } + } + } diff --git a/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/service-2.json.gz new file mode 100644 index 0000000..55995a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/application-signals/2024-04-15/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/applicationcostprofiler/2020-09-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/applicationcostprofiler/2020-09-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1271819 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/applicationcostprofiler/2020-09-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/applicationcostprofiler/2020-09-10/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/applicationcostprofiler/2020-09-10/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/applicationcostprofiler/2020-09-10/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/applicationcostprofiler/2020-09-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/applicationcostprofiler/2020-09-10/paginators-1.json new file mode 100644 index 0000000..adffd06 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/applicationcostprofiler/2020-09-10/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListReportDefinitions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "reportDefinitions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/applicationcostprofiler/2020-09-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/applicationcostprofiler/2020-09-10/service-2.json.gz new file mode 100644 index 0000000..1dc3807 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/applicationcostprofiler/2020-09-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appmesh/2018-10-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2018-10-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1b22833 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2018-10-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appmesh/2018-10-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2018-10-01/examples-1.json new file mode 100644 index 0000000..752e89e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2018-10-01/examples-1.json @@ -0,0 +1,4 @@ +{ + "version": "1.0", + "examples": { } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/appmesh/2018-10-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2018-10-01/paginators-1.json new file mode 100644 index 0000000..162b8b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2018-10-01/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListMeshes": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "meshes" + }, + "ListRoutes": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "routes" + }, + "ListVirtualNodes": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "virtualNodes" + }, + "ListVirtualRouters": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "virtualRouters" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appmesh/2018-10-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2018-10-01/service-2.json.gz new file mode 100644 index 0000000..8de1e85 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2018-10-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appmesh/2019-01-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2019-01-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c29a3c3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2019-01-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appmesh/2019-01-25/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2019-01-25/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2019-01-25/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appmesh/2019-01-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2019-01-25/paginators-1.json new file mode 100644 index 0000000..5a79b5b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2019-01-25/paginators-1.json @@ -0,0 +1,52 @@ +{ + "pagination": { + "ListMeshes": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "meshes" + }, + "ListRoutes": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "routes" + }, + "ListVirtualNodes": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "virtualNodes" + }, + "ListVirtualRouters": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "virtualRouters" + }, + "ListVirtualServices": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "virtualServices" + }, + "ListTagsForResource": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "tags" + }, + "ListGatewayRoutes": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "gatewayRoutes" + }, + "ListVirtualGateways": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "virtualGateways" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appmesh/2019-01-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2019-01-25/service-2.json.gz new file mode 100644 index 0000000..530ef18 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appmesh/2019-01-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/apprunner/2020-05-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/apprunner/2020-05-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..aa32ecd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/apprunner/2020-05-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/apprunner/2020-05-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/apprunner/2020-05-15/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/apprunner/2020-05-15/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/apprunner/2020-05-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/apprunner/2020-05-15/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/apprunner/2020-05-15/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/apprunner/2020-05-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/apprunner/2020-05-15/service-2.json.gz new file mode 100644 index 0000000..4ebf9d6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/apprunner/2020-05-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7d68f1a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/paginators-1.json new file mode 100644 index 0000000..40cbf4b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/paginators-1.json @@ -0,0 +1,60 @@ +{ + "pagination": { + "DescribeDirectoryConfigs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DirectoryConfigs" + }, + "DescribeFleets": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Fleets" + }, + "DescribeImageBuilders": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ImageBuilders" + }, + "DescribeImages": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Images" + }, + "DescribeSessions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Sessions" + }, + "DescribeStacks": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Stacks" + }, + "DescribeUserStackAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "UserStackAssociations" + }, + "DescribeUsers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Users" + }, + "ListAssociatedFleets": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Names" + }, + "ListAssociatedStacks": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Names" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/service-2.json.gz new file mode 100644 index 0000000..3414f05 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/waiters-2.json new file mode 100644 index 0000000..1c8dea0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appstream/2016-12-01/waiters-2.json @@ -0,0 +1,55 @@ +{ + "version": 2, + "waiters": { + "FleetStarted": { + "delay": 30, + "maxAttempts": 40, + "operation": "DescribeFleets", + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Fleets[].State", + "expected": "RUNNING" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "Fleets[].State", + "expected": "STOPPING" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "Fleets[].State", + "expected": "STOPPED" + } + ] + }, + "FleetStopped": { + "delay": 30, + "maxAttempts": 40, + "operation": "DescribeFleets", + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Fleets[].State", + "expected": "STOPPED" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "Fleets[].State", + "expected": "STARTING" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "Fleets[].State", + "expected": "RUNNING" + } + ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/appsync/2017-07-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appsync/2017-07-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4dedea1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appsync/2017-07-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/appsync/2017-07-25/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/appsync/2017-07-25/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appsync/2017-07-25/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appsync/2017-07-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/appsync/2017-07-25/paginators-1.json new file mode 100644 index 0000000..fbb7de0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/appsync/2017-07-25/paginators-1.json @@ -0,0 +1,76 @@ +{ + "pagination": { + "ListApiKeys": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "apiKeys" + }, + "ListDataSources": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "dataSources" + }, + "ListFunctions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "functions" + }, + "ListGraphqlApis": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "graphqlApis" + }, + "ListResolvers": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "resolvers" + }, + "ListResolversByFunction": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "resolvers" + }, + "ListTypes": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "types" + }, + "ListDomainNames": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "domainNameConfigs" + }, + "ListSourceApiAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sourceApiAssociationSummaries" + }, + "ListTypesByAssociation": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "types" + }, + "ListApis": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "apis" + }, + "ListChannelNamespaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "channelNamespaces" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/appsync/2017-07-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/appsync/2017-07-25/service-2.json.gz new file mode 100644 index 0000000..5ee04f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/appsync/2017-07-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..37fbe2d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/paginators-1.json new file mode 100644 index 0000000..1f7f1ce --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/paginators-1.json @@ -0,0 +1,58 @@ +{ + "pagination": { + "GetPlanEvaluationStatus": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "warnings" + }, + "GetPlanExecution": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "stepStates" + }, + "ListPlanExecutionEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListPlanExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListPlans": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "plans" + }, + "ListPlansInRegion": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "plans" + }, + "ListRoute53HealthChecks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "healthChecks" + }, + "ListRoute53HealthChecksInRegion": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "healthChecks" + }, + "ListServiceQuotaWarnings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "serviceQuotaWarningSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/paginators-1.sdk-extras.json new file mode 100644 index 0000000..824c899 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/paginators-1.sdk-extras.json @@ -0,0 +1,35 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetPlanEvaluationStatus": { + "non_aggregate_keys": [ + "lastEvaluatedVersion", + "planArn", + "lastEvaluationTime", + "evaluationState", + "region" + ] + }, + "GetPlanExecution": { + "non_aggregate_keys": [ + "comment", + "executionRegion", + "endTime", + "startTime", + "executionState", + "executionAction", + "plan", + "executionId", + "updatedAt", + "planArn", + "actualRecoveryTime", + "version", + "mode", + "generatedReportDetails", + "recoveryExecutionId" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/service-2.json.gz new file mode 100644 index 0000000..096a2e5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/waiters-2.json new file mode 100644 index 0000000..c9624e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/arc-region-switch/2022-07-26/waiters-2.json @@ -0,0 +1,57 @@ +{ + "version" : 2, + "waiters" : { + "PlanEvaluationStatusPassed" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetPlanEvaluationStatus", + "acceptors" : [ { + "matcher" : "path", + "argument" : "evaluationState", + "state" : "success", + "expected" : "passed" + }, { + "matcher" : "path", + "argument" : "evaluationState", + "state" : "failure", + "expected" : "actionRequired" + }, { + "matcher" : "path", + "argument" : "evaluationState", + "state" : "retry", + "expected" : "pendingEvaluation" + } ] + }, + "PlanExecutionCompleted" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetPlanExecution", + "acceptors" : [ { + "matcher" : "path", + "argument" : "executionState", + "state" : "success", + "expected" : "completed" + }, { + "matcher" : "path", + "argument" : "executionState", + "state" : "success", + "expected" : "completedWithExceptions" + }, { + "matcher" : "path", + "argument" : "executionState", + "state" : "failure", + "expected" : "failed" + }, { + "matcher" : "path", + "argument" : "executionState", + "state" : "failure", + "expected" : "canceled" + }, { + "matcher" : "path", + "argument" : "executionState", + "state" : "failure", + "expected" : "planExecutionTimedOut" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/arc-zonal-shift/2022-10-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/arc-zonal-shift/2022-10-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1de25e2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/arc-zonal-shift/2022-10-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/arc-zonal-shift/2022-10-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/arc-zonal-shift/2022-10-30/paginators-1.json new file mode 100644 index 0000000..9fcfc56 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/arc-zonal-shift/2022-10-30/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListManagedResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListZonalShifts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAutoshifts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/arc-zonal-shift/2022-10-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/arc-zonal-shift/2022-10-30/service-2.json.gz new file mode 100644 index 0000000..fd10ffc Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/arc-zonal-shift/2022-10-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/arc-zonal-shift/2022-10-30/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/arc-zonal-shift/2022-10-30/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/arc-zonal-shift/2022-10-30/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/artifact/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/artifact/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..85dae67 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/artifact/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/artifact/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/artifact/2018-05-10/paginators-1.json new file mode 100644 index 0000000..f2aca53 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/artifact/2018-05-10/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListReports": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "reports" + }, + "ListCustomerAgreements": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "customerAgreements" + }, + "ListReportVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "reports" + }, + "ListComplianceInquiries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "complianceInquiries" + }, + "ListComplianceInquiryQueries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "queries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/artifact/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/artifact/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..3411b9a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/artifact/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/artifact/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/artifact/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/artifact/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/athena/2017-05-18/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/athena/2017-05-18/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ce25eb9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/athena/2017-05-18/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/athena/2017-05-18/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/athena/2017-05-18/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/athena/2017-05-18/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/athena/2017-05-18/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/athena/2017-05-18/paginators-1.json new file mode 100644 index 0000000..3b126ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/athena/2017-05-18/paginators-1.json @@ -0,0 +1,50 @@ +{ + "pagination": { + "ListNamedQueries": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "NamedQueryIds" + }, + "ListQueryExecutions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "QueryExecutionIds" + }, + "GetQueryResults": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResultSet.Rows", + "non_aggregate_keys": [ + "ResultSet.ResultSetMetadata", + "UpdateCount" + ] + }, + "ListDataCatalogs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DataCatalogsSummary" + }, + "ListDatabases": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DatabaseList" + }, + "ListTableMetadata": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TableMetadataList" + }, + "ListTagsForResource": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Tags" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/athena/2017-05-18/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/athena/2017-05-18/service-2.json.gz new file mode 100644 index 0000000..e60942a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/athena/2017-05-18/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/auditmanager/2017-07-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/auditmanager/2017-07-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f3accf3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/auditmanager/2017-07-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/auditmanager/2017-07-25/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/auditmanager/2017-07-25/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/auditmanager/2017-07-25/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/auditmanager/2017-07-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/auditmanager/2017-07-25/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/auditmanager/2017-07-25/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/auditmanager/2017-07-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/auditmanager/2017-07-25/service-2.json.gz new file mode 100644 index 0000000..2bcc7f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/auditmanager/2017-07-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/autoscaling-plans/2018-01-06/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/autoscaling-plans/2018-01-06/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..082179f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/autoscaling-plans/2018-01-06/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/autoscaling-plans/2018-01-06/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/autoscaling-plans/2018-01-06/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/autoscaling-plans/2018-01-06/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/autoscaling-plans/2018-01-06/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/autoscaling-plans/2018-01-06/paginators-1.json new file mode 100644 index 0000000..e3f812a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/autoscaling-plans/2018-01-06/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "DescribeScalingPlanResources": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ScalingPlanResources" + }, + "DescribeScalingPlans": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ScalingPlans" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/autoscaling-plans/2018-01-06/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/autoscaling-plans/2018-01-06/service-2.json.gz new file mode 100644 index 0000000..c8ee3b1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/autoscaling-plans/2018-01-06/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0c0dac3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/examples-1.json new file mode 100644 index 0000000..af6929b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/examples-1.json @@ -0,0 +1,1696 @@ +{ + "version": "1.0", + "examples": { + "AttachInstances": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "InstanceIds": [ + "i-93633f9b" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example attaches the specified instance to the specified Auto Scaling group.", + "id": "autoscaling-attach-instances-1", + "title": "To attach an instance to an Auto Scaling group" + } + ], + "AttachLoadBalancerTargetGroups": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "TargetGroupARNs": [ + "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example attaches the specified target group to the specified Auto Scaling group.", + "id": "autoscaling-attach-load-balancer-target-groups-1", + "title": "To attach a target group to an Auto Scaling group" + } + ], + "AttachLoadBalancers": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "LoadBalancerNames": [ + "my-load-balancer" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example attaches the specified load balancer to the specified Auto Scaling group.", + "id": "autoscaling-attach-load-balancers-1", + "title": "To attach a load balancer to an Auto Scaling group" + } + ], + "AttachTrafficSources": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "TrafficSources": [ + { + "Identifier": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067" + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example attaches the specified target group to the specified Auto Scaling group.", + "id": "to-attach-a-target-group-to-an-auto-scaling-group-1680036570089", + "title": "To attach a target group to an Auto Scaling group" + } + ], + "CancelInstanceRefresh": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group" + }, + "output": { + "InstanceRefreshId": "08b91cf7-8fa6-48af-b6a6-d227f40f1b9b" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example cancels an instance refresh operation in progress.", + "id": "to-cancel-an-instance-refresh-1592960979817", + "title": "To cancel an instance refresh" + } + ], + "CompleteLifecycleAction": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "LifecycleActionResult": "CONTINUE", + "LifecycleActionToken": "bcd2f1b8-9a78-44d3-8a7a-4dd07d7cf635", + "LifecycleHookName": "my-lifecycle-hook" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example notifies Auto Scaling that the specified lifecycle action is complete so that it can finish launching or terminating the instance.", + "id": "autoscaling-complete-lifecycle-action-1", + "title": "To complete the lifecycle action" + } + ], + "CreateAutoScalingGroup": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "DefaultInstanceWarmup": 120, + "LaunchTemplate": { + "LaunchTemplateName": "my-template-for-auto-scaling", + "Version": "$Default" + }, + "MaxInstanceLifetime": 2592000, + "MaxSize": 3, + "MinSize": 1, + "VPCZoneIdentifier": "subnet-057fa0918fEXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an Auto Scaling group.", + "id": "autoscaling-create-auto-scaling-group-1", + "title": "To create an Auto Scaling group" + }, + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "HealthCheckGracePeriod": 300, + "HealthCheckType": "ELB", + "LaunchTemplate": { + "LaunchTemplateName": "my-template-for-auto-scaling", + "Version": "$Default" + }, + "MaxSize": 3, + "MinSize": 1, + "TargetGroupARNs": [ + "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067" + ], + "VPCZoneIdentifier": "subnet-057fa0918fEXAMPLE, subnet-610acd08EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an Auto Scaling group and attaches the specified target group.", + "id": "autoscaling-create-auto-scaling-group-2", + "title": "To create an Auto Scaling group with an attached target group" + }, + { + "input": { + "AutoScalingGroupName": "my-asg", + "DesiredCapacity": 3, + "MaxSize": 5, + "MinSize": 1, + "MixedInstancesPolicy": { + "InstancesDistribution": { + "OnDemandBaseCapacity": 1, + "OnDemandPercentageAboveBaseCapacity": 50, + "SpotAllocationStrategy": "price-capacity-optimized" + }, + "LaunchTemplate": { + "LaunchTemplateSpecification": { + "LaunchTemplateName": "my-launch-template-for-x86", + "Version": "$Default" + }, + "Overrides": [ + { + "InstanceType": "c6g.large", + "LaunchTemplateSpecification": { + "LaunchTemplateName": "my-launch-template-for-arm", + "Version": "$Default" + } + }, + { + "InstanceType": "c5.large" + }, + { + "InstanceType": "c5a.large" + } + ] + } + }, + "VPCZoneIdentifier": "subnet-057fa0918fEXAMPLE, subnet-610acd08EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an Auto Scaling group with a mixed instances policy. It specifies the c5.large, c5a.large, and c6g.large instance types and defines a different launch template for the c6g.large instance type.", + "id": "autoscaling-create-auto-scaling-group-3", + "title": "To create an Auto Scaling group with a mixed instances policy" + }, + { + "input": { + "AutoScalingGroupName": "my-asg", + "DesiredCapacity": 4, + "DesiredCapacityType": "units", + "MaxSize": 100, + "MinSize": 0, + "MixedInstancesPolicy": { + "InstancesDistribution": { + "OnDemandPercentageAboveBaseCapacity": 50, + "SpotAllocationStrategy": "price-capacity-optimized" + }, + "LaunchTemplate": { + "LaunchTemplateSpecification": { + "LaunchTemplateName": "my-template-for-auto-scaling", + "Version": "$Default" + }, + "Overrides": [ + { + "InstanceRequirements": { + "CpuManufacturers": [ + "intel" + ], + "MemoryMiB": { + "Min": 16384 + }, + "VCpuCount": { + "Max": 8, + "Min": 4 + } + } + } + ] + } + }, + "VPCZoneIdentifier": "subnet-057fa0918fEXAMPLE, subnet-610acd08EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an Auto Scaling group using attribute-based instance type selection. It requires the instance types to have a minimum of four vCPUs and a maximum of eight vCPUs, a minimum of 16,384 MiB of memory, and an Intel manufactured CPU.", + "id": "autoscaling-create-auto-scaling-group-4", + "title": "To create an Auto Scaling group using attribute-based instance type selection" + } + ], + "CreateLaunchConfiguration": [ + { + "input": { + "IamInstanceProfile": "my-iam-role", + "ImageId": "ami-12345678", + "InstanceType": "m3.medium", + "LaunchConfigurationName": "my-launch-config", + "SecurityGroups": [ + "sg-eb2af88e" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a launch configuration.", + "id": "autoscaling-create-launch-configuration-1", + "title": "To create a launch configuration" + } + ], + "CreateOrUpdateTags": [ + { + "input": { + "Tags": [ + { + "Key": "Role", + "PropagateAtLaunch": true, + "ResourceId": "my-auto-scaling-group", + "ResourceType": "auto-scaling-group", + "Value": "WebServer" + }, + { + "Key": "Dept", + "PropagateAtLaunch": true, + "ResourceId": "my-auto-scaling-group", + "ResourceType": "auto-scaling-group", + "Value": "Research" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds two tags to the specified Auto Scaling group.", + "id": "autoscaling-create-or-update-tags-1", + "title": "To create or update tags for an Auto Scaling group" + } + ], + "DeleteAutoScalingGroup": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified Auto Scaling group.", + "id": "autoscaling-delete-auto-scaling-group-1", + "title": "To delete an Auto Scaling group" + }, + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "ForceDelete": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified Auto Scaling group and all its instances.", + "id": "autoscaling-delete-auto-scaling-group-2", + "title": "To delete an Auto Scaling group and all its instances" + } + ], + "DeleteLaunchConfiguration": [ + { + "input": { + "LaunchConfigurationName": "my-launch-config" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified launch configuration.", + "id": "autoscaling-delete-launch-configuration-1", + "title": "To delete a launch configuration" + } + ], + "DeleteLifecycleHook": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "LifecycleHookName": "my-lifecycle-hook" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified lifecycle hook.", + "id": "autoscaling-delete-lifecycle-hook-1", + "title": "To delete a lifecycle hook" + } + ], + "DeleteNotificationConfiguration": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "TopicARN": "arn:aws:sns:us-west-2:123456789012:my-sns-topic" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified notification from the specified Auto Scaling group.", + "id": "autoscaling-delete-notification-configuration-1", + "title": "To delete an Auto Scaling notification" + } + ], + "DeletePolicy": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "PolicyName": "my-step-scale-out-policy" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified Auto Scaling policy.", + "id": "autoscaling-delete-policy-1", + "title": "To delete an Auto Scaling policy" + } + ], + "DeleteScheduledAction": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "ScheduledActionName": "my-scheduled-action" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified scheduled action from the specified Auto Scaling group.", + "id": "autoscaling-delete-scheduled-action-1", + "title": "To delete a scheduled action from an Auto Scaling group" + } + ], + "DeleteTags": [ + { + "input": { + "Tags": [ + { + "Key": "Dept", + "ResourceId": "my-auto-scaling-group", + "ResourceType": "auto-scaling-group", + "Value": "Research" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified tag from the specified Auto Scaling group.", + "id": "autoscaling-delete-tags-1", + "title": "To delete a tag from an Auto Scaling group" + } + ], + "DescribeAccountLimits": [ + { + "output": { + "MaxNumberOfAutoScalingGroups": 20, + "MaxNumberOfLaunchConfigurations": 100, + "NumberOfAutoScalingGroups": 3, + "NumberOfLaunchConfigurations": 5 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the Amazon EC2 Auto Scaling service quotas for your account.", + "id": "autoscaling-describe-account-limits-1", + "title": "To describe your Auto Scaling account limits" + } + ], + "DescribeAdjustmentTypes": [ + { + "output": { + "AdjustmentTypes": [ + { + "AdjustmentType": "ChangeInCapacity" + }, + { + "AdjustmentType": "ExactCapcity" + }, + { + "AdjustmentType": "PercentChangeInCapacity" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the available adjustment types.", + "id": "autoscaling-describe-adjustment-types-1", + "title": "To describe the Amazon EC2 Auto Scaling adjustment types" + } + ], + "DescribeAutoScalingGroups": [ + { + "input": { + "AutoScalingGroupNames": [ + "my-auto-scaling-group" + ] + }, + "output": { + "AutoScalingGroups": [ + { + "AutoScalingGroupARN": "arn:aws:autoscaling:us-west-1:123456789012:autoScalingGroup:12345678-1234-1234-1234-123456789012:autoScalingGroupName/my-auto-scaling-group", + "AutoScalingGroupName": "my-auto-scaling-group", + "AvailabilityZones": [ + "us-west-2a", + "us-west-2b", + "us-west-2c" + ], + "CreatedTime": "2023-03-09T22:15:11.611Z", + "DefaultCooldown": 300, + "DesiredCapacity": 2, + "EnabledMetrics": [ + + ], + "HealthCheckGracePeriod": 300, + "HealthCheckType": "EC2", + "Instances": [ + { + "AvailabilityZone": "us-west-2c", + "HealthStatus": "Healthy", + "InstanceId": "i-05b4f7d5be44822a6", + "InstanceType": "t3.micro", + "LaunchConfigurationName": "my-launch-config", + "LifecycleState": "InService", + "ProtectedFromScaleIn": false + }, + { + "AvailabilityZone": "us-west-2b", + "HealthStatus": "Healthy", + "InstanceId": "i-0c20ac468fa3049e8", + "InstanceType": "t3.micro", + "LaunchConfigurationName": "my-launch-config", + "LifecycleState": "InService", + "ProtectedFromScaleIn": false + } + ], + "LaunchConfigurationName": "my-launch-config", + "LoadBalancerNames": [ + + ], + "MaxSize": 5, + "MinSize": 1, + "NewInstancesProtectedFromScaleIn": false, + "ServiceLinkedRoleARN": "arn:aws:iam::123456789012:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling", + "SuspendedProcesses": [ + + ], + "Tags": [ + + ], + "TargetGroupARNs": [ + + ], + "TerminationPolicies": [ + "Default" + ], + "TrafficSources": [ + + ], + "VPCZoneIdentifier": "subnet-5ea0c127,subnet-6194ea3b,subnet-c934b782" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified Auto Scaling group.", + "id": "autoscaling-describe-auto-scaling-groups-1", + "title": "To describe an Auto Scaling group" + } + ], + "DescribeAutoScalingInstances": [ + { + "input": { + "InstanceIds": [ + "i-05b4f7d5be44822a6" + ] + }, + "output": { + "AutoScalingInstances": [ + { + "AutoScalingGroupName": "my-auto-scaling-group", + "AvailabilityZone": "us-west-2c", + "HealthStatus": "HEALTHY", + "InstanceId": "i-05b4f7d5be44822a6", + "InstanceType": "t3.micro", + "LaunchConfigurationName": "my-launch-config", + "LifecycleState": "InService", + "ProtectedFromScaleIn": false + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified Auto Scaling instance.", + "id": "autoscaling-describe-auto-scaling-instances-1", + "title": "To describe one or more Auto Scaling instances" + } + ], + "DescribeAutoScalingNotificationTypes": [ + { + "output": { + "AutoScalingNotificationTypes": [ + "autoscaling:EC2_INSTANCE_LAUNCH", + "autoscaling:EC2_INSTANCE_LAUNCH_ERROR", + "autoscaling:EC2_INSTANCE_TERMINATE", + "autoscaling:EC2_INSTANCE_TERMINATE_ERROR", + "autoscaling:TEST_NOTIFICATION" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the available notification types.", + "id": "autoscaling-describe-auto-scaling-notification-types-1", + "title": "To describe the Auto Scaling notification types" + } + ], + "DescribeInstanceRefreshes": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group" + }, + "output": { + "InstanceRefreshes": [ + { + "AutoScalingGroupName": "my-auto-scaling-group", + "InstanceRefreshId": "08b91cf7-8fa6-48af-b6a6-d227f40f1b9b", + "InstancesToUpdate": 0, + "PercentageComplete": 50, + "Preferences": { + "AlarmSpecification": { + "Alarms": [ + "my-alarm" + ] + }, + "AutoRollback": true, + "InstanceWarmup": 200, + "MinHealthyPercentage": 90, + "ScaleInProtectedInstances": "Ignore", + "SkipMatching": false, + "StandbyInstances": "Ignore" + }, + "StartTime": "2023-06-13T16:46:52+00:00", + "Status": "InProgress", + "StatusReason": "Waiting for instances to warm up before continuing. For example: i-0645704820a8e83ff is warming up." + }, + { + "AutoScalingGroupName": "my-auto-scaling-group", + "EndTime": "2023-06-02T13:59:45+00:00", + "InstanceRefreshId": "0e151305-1e57-4a32-a256-1fd14157c5ec", + "InstancesToUpdate": 0, + "PercentageComplete": 100, + "Preferences": { + "AlarmSpecification": { + "Alarms": [ + "my-alarm" + ] + }, + "AutoRollback": true, + "InstanceWarmup": 200, + "MinHealthyPercentage": 90, + "ScaleInProtectedInstances": "Ignore", + "SkipMatching": false, + "StandbyInstances": "Ignore" + }, + "StartTime": "2023-06-02T13:53:37+00:00", + "Status": "Successful" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the instance refreshes for the specified Auto Scaling group.", + "id": "to-list-instance-refreshes-1592959593746", + "title": "To list instance refreshes" + } + ], + "DescribeLaunchConfigurations": [ + { + "input": { + "LaunchConfigurationNames": [ + "my-launch-config" + ] + }, + "output": { + "LaunchConfigurations": [ + { + "AssociatePublicIpAddress": true, + "BlockDeviceMappings": [ + + ], + "CreatedTime": "2014-05-07T17:39:28.599Z", + "EbsOptimized": false, + "ImageId": "ami-043a5034", + "InstanceMonitoring": { + "Enabled": true + }, + "InstanceType": "t1.micro", + "LaunchConfigurationARN": "arn:aws:autoscaling:us-west-2:123456789012:launchConfiguration:98d3b196-4cf9-4e88-8ca1-8547c24ced8b:launchConfigurationName/my-launch-config", + "LaunchConfigurationName": "my-launch-config", + "SecurityGroups": [ + "sg-67ef0308" + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified launch configuration.", + "id": "autoscaling-describe-launch-configurations-1", + "title": "To describe Auto Scaling launch configurations" + } + ], + "DescribeLifecycleHookTypes": [ + { + "output": { + "LifecycleHookTypes": [ + "autoscaling:EC2_INSTANCE_LAUNCHING", + "autoscaling:EC2_INSTANCE_TERMINATING" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the available lifecycle hook types.", + "id": "autoscaling-describe-lifecycle-hook-types-1", + "title": "To describe the available types of lifecycle hooks" + } + ], + "DescribeLifecycleHooks": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group" + }, + "output": { + "LifecycleHooks": [ + { + "AutoScalingGroupName": "my-auto-scaling-group", + "DefaultResult": "ABANDON", + "GlobalTimeout": 172800, + "HeartbeatTimeout": 3600, + "LifecycleHookName": "my-lifecycle-hook", + "LifecycleTransition": "autoscaling:EC2_INSTANCE_LAUNCHING", + "NotificationTargetARN": "arn:aws:sns:us-west-2:123456789012:my-sns-topic", + "RoleARN": "arn:aws:iam::123456789012:role/my-auto-scaling-role" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the lifecycle hooks for the specified Auto Scaling group.", + "id": "autoscaling-describe-lifecycle-hooks-1", + "title": "To describe your lifecycle hooks" + } + ], + "DescribeLoadBalancerTargetGroups": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group" + }, + "output": { + "LoadBalancerTargetGroups": [ + { + "LoadBalancerTargetGroupARN": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "State": "Added" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the target groups attached to the specified Auto Scaling group.", + "id": "autoscaling-describe-load-balancer-target-groups-1", + "title": "To describe the target groups for an Auto Scaling group" + } + ], + "DescribeLoadBalancers": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group" + }, + "output": { + "LoadBalancers": [ + { + "LoadBalancerName": "my-load-balancer", + "State": "Added" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the load balancers attached to the specified Auto Scaling group.", + "id": "autoscaling-describe-load-balancers-1", + "title": "To describe the load balancers for an Auto Scaling group" + } + ], + "DescribeMetricCollectionTypes": [ + { + "output": { + "Granularities": [ + { + "Granularity": "1Minute" + } + ], + "Metrics": [ + { + "Metric": "GroupMinSize" + }, + { + "Metric": "GroupMaxSize" + }, + { + "Metric": "GroupDesiredCapacity" + }, + { + "Metric": "GroupInServiceInstances" + }, + { + "Metric": "GroupPendingInstances" + }, + { + "Metric": "GroupTerminatingInstances" + }, + { + "Metric": "GroupStandbyInstances" + }, + { + "Metric": "GroupTotalInstances" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the available metric collection types.", + "id": "autoscaling-describe-metric-collection-types-1", + "title": "To describe the Auto Scaling metric collection types" + } + ], + "DescribeNotificationConfigurations": [ + { + "input": { + "AutoScalingGroupNames": [ + "my-auto-scaling-group" + ] + }, + "output": { + "NotificationConfigurations": [ + { + "AutoScalingGroupName": "my-auto-scaling-group", + "NotificationType": "autoscaling:TEST_NOTIFICATION", + "TopicARN": "arn:aws:sns:us-west-2:123456789012:my-sns-topic-2" + }, + { + "AutoScalingGroupName": "my-auto-scaling-group", + "NotificationType": "autoscaling:TEST_NOTIFICATION", + "TopicARN": "arn:aws:sns:us-west-2:123456789012:my-sns-topic" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the notification configurations for the specified Auto Scaling group.", + "id": "autoscaling-describe-notification-configurations-1", + "title": "To describe Auto Scaling notification configurations" + } + ], + "DescribePolicies": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group" + }, + "output": { + "ScalingPolicies": [ + { + "AdjustmentType": "ChangeInCapacity", + "Alarms": [ + + ], + "AutoScalingGroupName": "my-auto-scaling-group", + "PolicyARN": "arn:aws:autoscaling:us-west-2:123456789012:scalingPolicy:2233f3d7-6290-403b-b632-93c553560106:autoScalingGroupName/my-auto-scaling-group:policyName/ScaleIn", + "PolicyName": "ScaleIn", + "ScalingAdjustment": -1 + }, + { + "AdjustmentType": "PercentChangeInCapacity", + "Alarms": [ + + ], + "AutoScalingGroupName": "my-auto-scaling-group", + "Cooldown": 60, + "MinAdjustmentStep": 2, + "PolicyARN": "arn:aws:autoscaling:us-west-2:123456789012:scalingPolicy:2b435159-cf77-4e89-8c0e-d63b497baad7:autoScalingGroupName/my-auto-scaling-group:policyName/ScalePercentChange", + "PolicyName": "ScalePercentChange", + "ScalingAdjustment": 25 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the policies for the specified Auto Scaling group.", + "id": "autoscaling-describe-policies-1", + "title": "To describe scaling policies" + } + ], + "DescribeScalingActivities": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group" + }, + "output": { + "Activities": [ + { + "ActivityId": "f9f2d65b-f1f2-43e7-b46d-d86756459699", + "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:12345678-1234-1234-1234-123456789012:autoScalingGroupName/my-auto-scaling-group", + "AutoScalingGroupName": "my-auto-scaling-group", + "Cause": "At 2013-08-19T20:53:25Z a user request created an AutoScalingGroup changing the desired capacity from 0 to 1. At 2013-08-19T20:53:29Z an instance was started in response to a difference between desired and actual capacity, increasing the capacity from 0 to 1.", + "Description": "Launching a new EC2 instance: i-4ba0837f", + "Details": "details", + "EndTime": "2013-08-19T20:54:02Z", + "Progress": 100, + "StartTime": "2013-08-19T20:53:29.930Z", + "StatusCode": "Successful" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the scaling activities for the specified Auto Scaling group.", + "id": "autoscaling-describe-scaling-activities-1", + "title": "To describe the scaling activities for an Auto Scaling group" + } + ], + "DescribeScalingProcessTypes": [ + { + "output": { + "Processes": [ + { + "ProcessName": "AZRebalance" + }, + { + "ProcessName": "AddToLoadBalancer" + }, + { + "ProcessName": "AlarmNotification" + }, + { + "ProcessName": "HealthCheck" + }, + { + "ProcessName": "Launch" + }, + { + "ProcessName": "ReplaceUnhealthy" + }, + { + "ProcessName": "ScheduledActions" + }, + { + "ProcessName": "Terminate" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the Auto Scaling process types.", + "id": "autoscaling-describe-scaling-process-types-1", + "title": "To describe the Auto Scaling process types" + } + ], + "DescribeScheduledActions": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group" + }, + "output": { + "ScheduledUpdateGroupActions": [ + { + "AutoScalingGroupName": "my-auto-scaling-group", + "DesiredCapacity": 4, + "MaxSize": 6, + "MinSize": 2, + "Recurrence": "30 0 1 12 0", + "ScheduledActionARN": "arn:aws:autoscaling:us-west-2:123456789012:scheduledUpdateGroupAction:8e86b655-b2e6-4410-8f29-b4f094d6871c:autoScalingGroupName/my-auto-scaling-group:scheduledActionName/my-scheduled-action", + "ScheduledActionName": "my-scheduled-action", + "StartTime": "2016-12-01T00:30:00Z", + "Time": "2016-12-01T00:30:00Z" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the scheduled actions for the specified Auto Scaling group.", + "id": "autoscaling-describe-scheduled-actions-1", + "title": "To describe scheduled actions" + } + ], + "DescribeTags": [ + { + "input": { + "Filters": [ + { + "Name": "auto-scaling-group", + "Values": [ + "my-auto-scaling-group" + ] + } + ] + }, + "output": { + "Tags": [ + { + "Key": "Dept", + "PropagateAtLaunch": true, + "ResourceId": "my-auto-scaling-group", + "ResourceType": "auto-scaling-group", + "Value": "Research" + }, + { + "Key": "Role", + "PropagateAtLaunch": true, + "ResourceId": "my-auto-scaling-group", + "ResourceType": "auto-scaling-group", + "Value": "WebServer" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the tags for the specified Auto Scaling group.", + "id": "autoscaling-describe-tags-1", + "title": "To describe tags" + } + ], + "DescribeTerminationPolicyTypes": [ + { + "output": { + "TerminationPolicyTypes": [ + "ClosestToNextInstanceHour", + "Default", + "NewestInstance", + "OldestInstance", + "OldestLaunchConfiguration" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the available termination policy types.", + "id": "autoscaling-describe-termination-policy-types-1", + "title": "To describe termination policy types" + } + ], + "DescribeTrafficSources": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group" + }, + "output": { + "NextToken": "", + "TrafficSources": [ + { + "Identifier": "arn:aws:vpc-lattice:us-west-2:123456789012:targetgroup/tg-0e2f2665eEXAMPLE", + "State": "InService", + "Type": "vpc-lattice" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the target groups attached to the specified Auto Scaling group.", + "id": "to-describe-the-target-groups-for-an-auto-scaling-group-1680040714521", + "title": "To describe the target groups for an Auto Scaling group" + } + ], + "DetachInstances": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "InstanceIds": [ + "i-93633f9b" + ], + "ShouldDecrementDesiredCapacity": true + }, + "output": { + "Activities": [ + { + "ActivityId": "5091cb52-547a-47ce-a236-c9ccbc2cb2c9", + "AutoScalingGroupName": "my-auto-scaling-group", + "Cause": "At 2015-04-12T15:02:16Z instance i-93633f9b was detached in response to a user request, shrinking the capacity from 2 to 1.", + "Description": "Detaching EC2 instance: i-93633f9b", + "Details": "details", + "Progress": 50, + "StartTime": "2015-04-12T15:02:16.179Z", + "StatusCode": "InProgress" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the specified instance from the specified Auto Scaling group.", + "id": "autoscaling-detach-instances-1", + "title": "To detach an instance from an Auto Scaling group" + } + ], + "DetachLoadBalancerTargetGroups": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "TargetGroupARNs": [ + "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the specified target group from the specified Auto Scaling group", + "id": "autoscaling-detach-load-balancer-target-groups-1", + "title": "To detach a target group from an Auto Scaling group" + } + ], + "DetachLoadBalancers": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "LoadBalancerNames": [ + "my-load-balancer" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the specified load balancer from the specified Auto Scaling group.", + "id": "autoscaling-detach-load-balancers-1", + "title": "To detach a load balancer from an Auto Scaling group" + } + ], + "DetachTrafficSources": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "TrafficSources": [ + { + "Identifier": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067" + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the specified target group from the specified Auto Scaling group.", + "id": "to-detach-a-target-group-from-an-auto-scaling-group-1680040404169", + "title": "To detach a target group from an Auto Scaling group" + } + ], + "DisableMetricsCollection": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "Metrics": [ + "GroupDesiredCapacity" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disables collecting data for the GroupDesiredCapacity metric for the specified Auto Scaling group.", + "id": "autoscaling-disable-metrics-collection-1", + "title": "To disable metrics collection for an Auto Scaling group" + } + ], + "EnableMetricsCollection": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "Granularity": "1Minute" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables data collection for the specified Auto Scaling group.", + "id": "autoscaling-enable-metrics-collection-1", + "title": "To enable metrics collection for an Auto Scaling group" + } + ], + "EnterStandby": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "InstanceIds": [ + "i-93633f9b" + ], + "ShouldDecrementDesiredCapacity": true + }, + "output": { + "Activities": [ + { + "ActivityId": "ffa056b4-6ed3-41ba-ae7c-249dfae6eba1", + "AutoScalingGroupName": "my-auto-scaling-group", + "Cause": "At 2015-04-12T15:10:23Z instance i-93633f9b was moved to standby in response to a user request, shrinking the capacity from 2 to 1.", + "Description": "Moving EC2 instance to Standby: i-93633f9b", + "Details": "details", + "Progress": 50, + "StartTime": "2015-04-12T15:10:23.640Z", + "StatusCode": "InProgress" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example puts the specified instance into standby mode.", + "id": "autoscaling-enter-standby-1", + "title": "To move instances into standby mode" + } + ], + "ExecutePolicy": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "BreachThreshold": 50.0, + "MetricValue": 59.0, + "PolicyName": "my-step-scale-out-policy" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example executes the specified policy.", + "id": "autoscaling-execute-policy-1", + "title": "To execute a scaling policy" + } + ], + "ExitStandby": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "InstanceIds": [ + "i-93633f9b" + ] + }, + "output": { + "Activities": [ + { + "ActivityId": "142928e1-a2dc-453a-9b24-b85ad6735928", + "AutoScalingGroupName": "my-auto-scaling-group", + "Cause": "At 2015-04-12T15:14:29Z instance i-93633f9b was moved out of standby in response to a user request, increasing the capacity from 1 to 2.", + "Description": "Moving EC2 instance out of Standby: i-93633f9b", + "Details": "details", + "Progress": 30, + "StartTime": "2015-04-12T15:14:29.886Z", + "StatusCode": "PreInService" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example moves the specified instance out of standby mode.", + "id": "autoscaling-exit-standby-1", + "title": "To move instances out of standby mode" + } + ], + "PutLifecycleHook": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "DefaultResult": "CONTINUE", + "HeartbeatTimeout": 300, + "LifecycleHookName": "my-launch-lifecycle-hook", + "LifecycleTransition": "autoscaling:EC2_INSTANCE_LAUNCHING" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a lifecycle hook for instance launch.", + "id": "autoscaling-put-lifecycle-hook-1", + "title": "To create a launch lifecycle hook" + } + ], + "PutNotificationConfiguration": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "NotificationTypes": [ + "autoscaling:TEST_NOTIFICATION" + ], + "TopicARN": "arn:aws:sns:us-west-2:123456789012:my-sns-topic" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds the specified notification to the specified Auto Scaling group.", + "id": "autoscaling-put-notification-configuration-1", + "title": "To add an Auto Scaling notification" + } + ], + "PutScalingPolicy": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "PolicyName": "alb1000-target-tracking-scaling-policy", + "PolicyType": "TargetTrackingScaling", + "TargetTrackingConfiguration": { + "PredefinedMetricSpecification": { + "PredefinedMetricType": "ALBRequestCountPerTarget", + "ResourceLabel": "app/my-alb/778d41231b141a0f/targetgroup/my-alb-target-group/943f017f100becff" + }, + "TargetValue": 1000.0 + } + }, + "output": { + "Alarms": [ + { + "AlarmARN": "arn:aws:cloudwatch:us-west-2:123456789012:alarm:TargetTracking-my-asg-AlarmHigh-fc0e4183-23ac-497e-9992-691c9980c38e", + "AlarmName": "TargetTracking-my-asg-AlarmHigh-fc0e4183-23ac-497e-9992-691c9980c38e" + }, + { + "AlarmARN": "arn:aws:cloudwatch:us-west-2:123456789012:alarm:TargetTracking-my-asg-AlarmLow-61a39305-ed0c-47af-bd9e-471a352ee1a2", + "AlarmName": "TargetTracking-my-asg-AlarmLow-61a39305-ed0c-47af-bd9e-471a352ee1a2" + } + ], + "PolicyARN": "arn:aws:autoscaling:us-west-2:123456789012:scalingPolicy:228f02c2-c665-4bfd-aaac-8b04080bea3c:autoScalingGroupName/my-auto-scaling-group:policyName/alb1000-target-tracking-scaling-policy" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds the specified policy to the specified Auto Scaling group.", + "id": "autoscaling-put-scaling-policy-1", + "title": "To add a scaling policy to an Auto Scaling group" + } + ], + "PutScheduledUpdateGroupAction": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "DesiredCapacity": 4, + "EndTime": "2014-05-12T08:00:00Z", + "MaxSize": 6, + "MinSize": 2, + "ScheduledActionName": "my-scheduled-action", + "StartTime": "2014-05-12T08:00:00Z" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds the specified scheduled action to the specified Auto Scaling group.", + "id": "autoscaling-put-scheduled-update-group-action-1", + "title": "To add a scheduled action to an Auto Scaling group" + } + ], + "PutWarmPool": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "InstanceReusePolicy": { + "ReuseOnScaleIn": true + }, + "MinSize": 30, + "PoolState": "Hibernated" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a warm pool for the specified Auto Scaling group.", + "id": "to-add-a-warm-pool-to-an-auto-scaling-group-1617818810383", + "title": "To create a warm pool for an Auto Scaling group" + } + ], + "RecordLifecycleActionHeartbeat": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "LifecycleActionToken": "bcd2f1b8-9a78-44d3-8a7a-4dd07d7cf635", + "LifecycleHookName": "my-lifecycle-hook" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example records a lifecycle action heartbeat to keep the instance in a pending state.", + "id": "autoscaling-record-lifecycle-action-heartbeat-1", + "title": "To record a lifecycle action heartbeat" + } + ], + "ResumeProcesses": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "ScalingProcesses": [ + "AlarmNotification" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example resumes the specified suspended scaling process for the specified Auto Scaling group.", + "id": "autoscaling-resume-processes-1", + "title": "To resume Auto Scaling processes" + } + ], + "SetDesiredCapacity": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "DesiredCapacity": 2, + "HonorCooldown": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example sets the desired capacity for the specified Auto Scaling group.", + "id": "autoscaling-set-desired-capacity-1", + "title": "To set the desired capacity for an Auto Scaling group" + } + ], + "SetInstanceHealth": [ + { + "input": { + "HealthStatus": "Unhealthy", + "InstanceId": "i-93633f9b" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example sets the health status of the specified instance to Unhealthy.", + "id": "autoscaling-set-instance-health-1", + "title": "To set the health status of an instance" + } + ], + "SetInstanceProtection": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "InstanceIds": [ + "i-93633f9b" + ], + "ProtectedFromScaleIn": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables instance protection for the specified instance.", + "id": "autoscaling-set-instance-protection-1", + "title": "To enable instance protection for an instance" + }, + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "InstanceIds": [ + "i-93633f9b" + ], + "ProtectedFromScaleIn": false + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disables instance protection for the specified instance.", + "id": "autoscaling-set-instance-protection-2", + "title": "To disable instance protection for an instance" + } + ], + "StartInstanceRefresh": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "DesiredConfiguration": { + "LaunchTemplate": { + "LaunchTemplateName": "my-template-for-auto-scaling", + "Version": "$Latest" + } + }, + "Preferences": { + "AlarmSpecification": { + "Alarms": [ + "my-alarm" + ] + }, + "AutoRollback": true, + "InstanceWarmup": 200, + "MinHealthyPercentage": 90 + } + }, + "output": { + "InstanceRefreshId": "08b91cf7-8fa6-48af-b6a6-d227f40f1b9b" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example starts an instance refresh for the specified Auto Scaling group.", + "id": "to-start-an-instance-refresh-1592957271522", + "title": "To start an instance refresh" + } + ], + "SuspendProcesses": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "ScalingProcesses": [ + "AlarmNotification" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example suspends the specified scaling process for the specified Auto Scaling group.", + "id": "autoscaling-suspend-processes-1", + "title": "To suspend Auto Scaling processes" + } + ], + "TerminateInstanceInAutoScalingGroup": [ + { + "input": { + "InstanceId": "i-93633f9b", + "ShouldDecrementDesiredCapacity": false + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example terminates the specified instance from the specified Auto Scaling group without updating the size of the group. Auto Scaling launches a replacement instance after the specified instance terminates.", + "id": "autoscaling-terminate-instance-in-auto-scaling-group-1", + "title": "To terminate an instance in an Auto Scaling group" + } + ], + "UpdateAutoScalingGroup": [ + { + "input": { + "AutoScalingGroupName": "my-auto-scaling-group", + "LaunchTemplate": { + "LaunchTemplateName": "my-template-for-auto-scaling", + "Version": "2" + }, + "MaxSize": 5, + "MinSize": 1, + "NewInstancesProtectedFromScaleIn": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example updates multiple properties at the same time.", + "id": "autoscaling-update-auto-scaling-group-1", + "title": "To update an Auto Scaling group" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/paginators-1.json new file mode 100644 index 0000000..ac5939d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/paginators-1.json @@ -0,0 +1,70 @@ +{ + "pagination": { + "DescribeAutoScalingGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxRecords", + "result_key": "AutoScalingGroups" + }, + "DescribeAutoScalingInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxRecords", + "result_key": "AutoScalingInstances" + }, + "DescribeLaunchConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxRecords", + "result_key": "LaunchConfigurations" + }, + "DescribeNotificationConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxRecords", + "result_key": "NotificationConfigurations" + }, + "DescribePolicies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxRecords", + "result_key": "ScalingPolicies" + }, + "DescribeScalingActivities": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxRecords", + "result_key": "Activities" + }, + "DescribeScheduledActions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxRecords", + "result_key": "ScheduledUpdateGroupActions" + }, + "DescribeTags": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxRecords", + "result_key": "Tags" + }, + "DescribeLoadBalancerTargetGroups": { + "input_token": "NextToken", + "limit_key": "MaxRecords", + "output_token": "NextToken", + "result_key": "LoadBalancerTargetGroups" + }, + "DescribeLoadBalancers": { + "input_token": "NextToken", + "limit_key": "MaxRecords", + "output_token": "NextToken", + "result_key": "LoadBalancers" + }, + "DescribeWarmPool": { + "input_token": "NextToken", + "limit_key": "MaxRecords", + "output_token": "NextToken", + "result_key": "Instances" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/paginators-1.sdk-extras.json new file mode 100644 index 0000000..1c63499 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "DescribeWarmPool": { + "non_aggregate_keys": [ + "WarmPoolConfiguration" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/service-2.json.gz new file mode 100644 index 0000000..59e6597 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/autoscaling/2011-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/b2bi/2022-06-23/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/b2bi/2022-06-23/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..280b8cc Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/b2bi/2022-06-23/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/b2bi/2022-06-23/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/b2bi/2022-06-23/paginators-1.json new file mode 100644 index 0000000..5f3b0d2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/b2bi/2022-06-23/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListCapabilities": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "capabilities" + }, + "ListPartnerships": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "partnerships" + }, + "ListProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "profiles" + }, + "ListTransformers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "transformers" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/b2bi/2022-06-23/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/b2bi/2022-06-23/service-2.json.gz new file mode 100644 index 0000000..e0bcc62 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/b2bi/2022-06-23/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/b2bi/2022-06-23/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/b2bi/2022-06-23/waiters-2.json new file mode 100644 index 0000000..35d8698 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/b2bi/2022-06-23/waiters-2.json @@ -0,0 +1,21 @@ +{ + "version" : 2, + "waiters" : { + "TransformerJobSucceeded" : { + "delay" : 10, + "maxAttempts" : 12, + "operation" : "GetTransformerJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "succeeded" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "failed" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/backup-gateway/2021-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/backup-gateway/2021-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f808605 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/backup-gateway/2021-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/backup-gateway/2021-01-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/backup-gateway/2021-01-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/backup-gateway/2021-01-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/backup-gateway/2021-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/backup-gateway/2021-01-01/paginators-1.json new file mode 100644 index 0000000..462aacd --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/backup-gateway/2021-01-01/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListGateways": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Gateways" + }, + "ListHypervisors": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Hypervisors" + }, + "ListVirtualMachines": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VirtualMachines" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/backup-gateway/2021-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/backup-gateway/2021-01-01/service-2.json.gz new file mode 100644 index 0000000..ad13022 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/backup-gateway/2021-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..48d707b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/paginators-1.json new file mode 100644 index 0000000..615cc16 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/paginators-1.json @@ -0,0 +1,154 @@ +{ + "pagination": { + "ListBackupJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BackupJobs" + }, + "ListBackupPlanTemplates": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BackupPlanTemplatesList" + }, + "ListBackupPlanVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BackupPlanVersionsList" + }, + "ListBackupPlans": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BackupPlansList" + }, + "ListBackupSelections": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BackupSelectionsList" + }, + "ListBackupVaults": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BackupVaultList" + }, + "ListCopyJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CopyJobs" + }, + "ListProtectedResources": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Results" + }, + "ListRecoveryPointsByBackupVault": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RecoveryPoints" + }, + "ListRecoveryPointsByResource": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RecoveryPoints" + }, + "ListRestoreJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RestoreJobs" + }, + "ListLegalHolds": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "LegalHolds" + }, + "ListRecoveryPointsByLegalHold": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RecoveryPoints" + }, + "ListProtectedResourcesByBackupVault": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Results" + }, + "ListRestoreJobsByProtectedResource": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RestoreJobs" + }, + "ListRestoreTestingPlans": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RestoreTestingPlans" + }, + "ListRestoreTestingSelections": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RestoreTestingSelections" + }, + "ListIndexedRecoveryPoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "IndexedRecoveryPoints" + }, + "ListRestoreAccessBackupVaults": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RestoreAccessBackupVaults" + }, + "ListTieringConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TieringConfigurations" + }, + "ListScanJobSummaries": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ScanJobSummaries" + }, + "ListScanJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ScanJobs" + }, + "ListBackupAccessPoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BackupAccessPoints" + }, + "ListBackupAccessPointsByRecoveryPoint": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BackupAccessPoints" + }, + "ListBackupAccessPointsByResource": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BackupAccessPoints" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/paginators-1.sdk-extras.json new file mode 100644 index 0000000..9a5d767 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListScanJobSummaries": { + "non_aggregate_keys": [ + "AggregationPeriod" + ] + } + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/service-2.json.gz new file mode 100644 index 0000000..93d1f1d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/backup/2018-11-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/backupsearch/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/backupsearch/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6cd3511 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/backupsearch/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/backupsearch/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/backupsearch/2018-05-10/paginators-1.json new file mode 100644 index 0000000..bc482fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/backupsearch/2018-05-10/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListSearchJobBackups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Results" + }, + "ListSearchJobResults": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Results" + }, + "ListSearchJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SearchJobs" + }, + "ListSearchResultExportJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ExportJobs" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/backupsearch/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/backupsearch/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..6ba4c65 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/backupsearch/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/backupsearch/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/backupsearch/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/backupsearch/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/batch/2016-08-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/batch/2016-08-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c515888 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/batch/2016-08-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/batch/2016-08-10/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/batch/2016-08-10/examples-1.json new file mode 100644 index 0000000..18203dc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/batch/2016-08-10/examples-1.json @@ -0,0 +1,711 @@ +{ + "version": "1.0", + "examples": { + "CancelJob": [ + { + "input": { + "jobId": "1d828f65-7a4d-42e8-996d-3b900ed59dc4", + "reason": "Cancelling job." + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example cancels a job with the specified job ID.", + "id": "to-cancel-a-job-1481152314733", + "title": "To cancel a job" + } + ], + "CreateComputeEnvironment": [ + { + "input": { + "type": "MANAGED", + "computeEnvironmentName": "C4OnDemand", + "computeResources": { + "type": "EC2", + "desiredvCpus": 48, + "ec2KeyPair": "id_rsa", + "instanceRole": "ecsInstanceRole", + "instanceTypes": [ + "c4.large", + "c4.xlarge", + "c4.2xlarge", + "c4.4xlarge", + "c4.8xlarge" + ], + "maxvCpus": 128, + "minvCpus": 0, + "securityGroupIds": [ + "sg-cf5093b2" + ], + "subnets": [ + "subnet-220c0e0a", + "subnet-1a95556d", + "subnet-978f6dce" + ], + "tags": { + "Name": "Batch Instance - C4OnDemand" + } + }, + "serviceRole": "arn:aws:iam::012345678910:role/AWSBatchServiceRole", + "state": "ENABLED" + }, + "output": { + "computeEnvironmentArn": "arn:aws:batch:us-east-1:012345678910:compute-environment/C4OnDemand", + "computeEnvironmentName": "C4OnDemand" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a managed compute environment with specific C4 instance types that are launched on demand. The compute environment is called C4OnDemand.", + "id": "to-create-a-managed-ec2-compute-environment-1481152600017", + "title": "To create a managed EC2 compute environment" + }, + { + "input": { + "type": "MANAGED", + "computeEnvironmentName": "M4Spot", + "computeResources": { + "type": "SPOT", + "bidPercentage": 20, + "desiredvCpus": 4, + "ec2KeyPair": "id_rsa", + "instanceRole": "ecsInstanceRole", + "instanceTypes": [ + "m4" + ], + "maxvCpus": 128, + "minvCpus": 0, + "securityGroupIds": [ + "sg-cf5093b2" + ], + "spotIamFleetRole": "arn:aws:iam::012345678910:role/aws-ec2-spot-fleet-role", + "subnets": [ + "subnet-220c0e0a", + "subnet-1a95556d", + "subnet-978f6dce" + ], + "tags": { + "Name": "Batch Instance - M4Spot" + } + }, + "serviceRole": "arn:aws:iam::012345678910:role/AWSBatchServiceRole", + "state": "ENABLED" + }, + "output": { + "computeEnvironmentArn": "arn:aws:batch:us-east-1:012345678910:compute-environment/M4Spot", + "computeEnvironmentName": "M4Spot" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a managed compute environment with the M4 instance type that is launched when the Spot bid price is at or below 20% of the On-Demand price for the instance type. The compute environment is called M4Spot.", + "id": "to-create-a-managed-ec2-spot-compute-environment-1481152844190", + "title": "To create a managed EC2 Spot compute environment" + } + ], + "CreateJobQueue": [ + { + "input": { + "computeEnvironmentOrder": [ + { + "computeEnvironment": "M4Spot", + "order": 1 + } + ], + "jobQueueName": "LowPriority", + "priority": 1, + "state": "ENABLED" + }, + "output": { + "jobQueueArn": "arn:aws:batch:us-east-1:012345678910:job-queue/LowPriority", + "jobQueueName": "LowPriority" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a job queue called LowPriority that uses the M4Spot compute environment.", + "id": "to-create-a-job-queue-with-a-single-compute-environment-1481152967946", + "title": "To create a job queue with a single compute environment" + }, + { + "input": { + "computeEnvironmentOrder": [ + { + "computeEnvironment": "C4OnDemand", + "order": 1 + }, + { + "computeEnvironment": "M4Spot", + "order": 2 + } + ], + "jobQueueName": "HighPriority", + "priority": 10, + "state": "ENABLED" + }, + "output": { + "jobQueueArn": "arn:aws:batch:us-east-1:012345678910:job-queue/HighPriority", + "jobQueueName": "HighPriority" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a job queue called HighPriority that uses the C4OnDemand compute environment with an order of 1 and the M4Spot compute environment with an order of 2.", + "id": "to-create-a-job-queue-with-multiple-compute-environments-1481153027051", + "title": "To create a job queue with multiple compute environments" + } + ], + "DeleteComputeEnvironment": [ + { + "input": { + "computeEnvironment": "P2OnDemand" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the P2OnDemand compute environment.", + "id": "to-delete-a-compute-environment-1481153105644", + "title": "To delete a compute environment" + } + ], + "DeleteJobQueue": [ + { + "input": { + "jobQueue": "GPGPU" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the GPGPU job queue.", + "id": "to-delete-a-job-queue-1481153508134", + "title": "To delete a job queue" + } + ], + "DeregisterJobDefinition": [ + { + "input": { + "jobDefinition": "sleep10" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deregisters a job definition called sleep10.", + "id": "to-deregister-a-job-definition-1481153579565", + "title": "To deregister a job definition" + } + ], + "DescribeComputeEnvironments": [ + { + "input": { + "computeEnvironments": [ + "P2OnDemand" + ] + }, + "output": { + "computeEnvironments": [ + { + "type": "MANAGED", + "computeEnvironmentArn": "arn:aws:batch:us-east-1:012345678910:compute-environment/P2OnDemand", + "computeEnvironmentName": "P2OnDemand", + "computeResources": { + "type": "EC2", + "desiredvCpus": 48, + "ec2KeyPair": "id_rsa", + "instanceRole": "ecsInstanceRole", + "instanceTypes": [ + "p2" + ], + "maxvCpus": 128, + "minvCpus": 0, + "securityGroupIds": [ + "sg-cf5093b2" + ], + "subnets": [ + "subnet-220c0e0a", + "subnet-1a95556d", + "subnet-978f6dce" + ], + "tags": { + "Name": "Batch Instance - P2OnDemand" + } + }, + "ecsClusterArn": "arn:aws:ecs:us-east-1:012345678910:cluster/P2OnDemand_Batch_2c06f29d-d1fe-3a49-879d-42394c86effc", + "serviceRole": "arn:aws:iam::012345678910:role/AWSBatchServiceRole", + "state": "ENABLED", + "status": "VALID", + "statusReason": "ComputeEnvironment Healthy" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the P2OnDemand compute environment.", + "id": "to-describe-a-compute-environment-1481153713334", + "title": "To describe a compute environment" + } + ], + "DescribeJobDefinitions": [ + { + "input": { + "status": "ACTIVE" + }, + "output": { + "jobDefinitions": [ + { + "type": "container", + "containerProperties": { + "command": [ + "sleep", + "60" + ], + "environment": [ + + ], + "image": "busybox", + "mountPoints": [ + + ], + "resourceRequirements": [ + { + "type": "MEMORY", + "value": "128" + }, + { + "type": "VCPU", + "value": "1" + } + ], + "ulimits": [ + + ], + "volumes": [ + + ] + }, + "jobDefinitionArn": "arn:aws:batch:us-east-1:012345678910:job-definition/sleep60:1", + "jobDefinitionName": "sleep60", + "revision": 1, + "status": "ACTIVE" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all of your active job definitions.", + "id": "to-describe-active-job-definitions-1481153895831", + "title": "To describe active job definitions" + } + ], + "DescribeJobQueues": [ + { + "input": { + "jobQueues": [ + "HighPriority" + ] + }, + "output": { + "jobQueues": [ + { + "computeEnvironmentOrder": [ + { + "computeEnvironment": "arn:aws:batch:us-east-1:012345678910:compute-environment/C4OnDemand", + "order": 1 + } + ], + "jobQueueArn": "arn:aws:batch:us-east-1:012345678910:job-queue/HighPriority", + "jobQueueName": "HighPriority", + "priority": 1, + "state": "ENABLED", + "status": "VALID", + "statusReason": "JobQueue Healthy" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the HighPriority job queue.", + "id": "to-describe-a-job-queue-1481153995804", + "title": "To describe a job queue" + } + ], + "DescribeJobs": [ + { + "input": { + "jobs": [ + "24fa2d7a-64c4-49d2-8b47-f8da4fbde8e9" + ] + }, + "output": { + "jobs": [ + { + "container": { + "command": [ + "sleep", + "60" + ], + "containerInstanceArn": "arn:aws:ecs:us-east-1:012345678910:container-instance/5406d7cd-58bd-4b8f-9936-48d7c6b1526c", + "environment": [ + + ], + "exitCode": 0, + "image": "busybox", + "memory": 128, + "mountPoints": [ + + ], + "ulimits": [ + + ], + "vcpus": 1, + "volumes": [ + + ] + }, + "createdAt": 1480460782010, + "dependsOn": [ + + ], + "jobDefinition": "sleep60", + "jobId": "24fa2d7a-64c4-49d2-8b47-f8da4fbde8e9", + "jobName": "example", + "jobQueue": "arn:aws:batch:us-east-1:012345678910:job-queue/HighPriority", + "parameters": { + }, + "startedAt": 1480460816500, + "status": "SUCCEEDED", + "stoppedAt": 1480460880699 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes a job with the specified job ID.", + "id": "to-describe-a-specific-job-1481154090490", + "title": "To describe a specific job" + } + ], + "ListJobs": [ + { + "input": { + "jobQueue": "HighPriority" + }, + "output": { + "jobSummaryList": [ + { + "jobId": "e66ff5fd-a1ff-4640-b1a2-0b0a142f49bb", + "jobName": "example" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists the running jobs in the HighPriority job queue.", + "id": "to-list-running-jobs-1481154202164", + "title": "To list running jobs" + }, + { + "input": { + "jobQueue": "HighPriority", + "jobStatus": "SUBMITTED" + }, + "output": { + "jobSummaryList": [ + { + "jobId": "68f0c163-fbd4-44e6-9fd1-25b14a434786", + "jobName": "example" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists jobs in the HighPriority job queue that are in the SUBMITTED job status.", + "id": "to-list-submitted-jobs-1481154251623", + "title": "To list submitted jobs" + } + ], + "ListTagsForResource": [ + { + "input": { + "resourceArn": "arn:aws:batch:us-east-1:123456789012:job-definition/sleep30:1" + }, + "output": { + "tags": { + "Department": "Engineering", + "Stage": "Alpha", + "User": "JaneDoe" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This demonstrates calling the ListTagsForResource action.", + "id": "listtagsforresource-example-1591293003710", + "title": "ListTagsForResource Example" + } + ], + "RegisterJobDefinition": [ + { + "input": { + "type": "container", + "containerProperties": { + "command": [ + "sleep", + "10" + ], + "image": "busybox", + "resourceRequirements": [ + { + "type": "MEMORY", + "value": "128" + }, + { + "type": "VCPU", + "value": "1" + } + ] + }, + "jobDefinitionName": "sleep10" + }, + "output": { + "jobDefinitionArn": "arn:aws:batch:us-east-1:012345678910:job-definition/sleep10:1", + "jobDefinitionName": "sleep10", + "revision": 1 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example registers a job definition for a simple container job.", + "id": "to-register-a-job-definition-1481154325325", + "title": "To register a job definition" + }, + { + "input": { + "type": "container", + "containerProperties": { + "command": [ + "sleep", + "30" + ], + "image": "busybox", + "resourceRequirements": [ + { + "type": "MEMORY", + "value": "128" + }, + { + "type": "VCPU", + "value": "1" + } + ] + }, + "jobDefinitionName": "sleep30", + "tags": { + "Department": "Engineering", + "User": "JaneDoe" + } + }, + "output": { + "jobDefinitionArn": "arn:aws:batch:us-east-1:012345678910:job-definition/sleep30:1", + "jobDefinitionName": "sleep30", + "revision": 1 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This demonstrates calling the RegisterJobDefinition action, including tags.", + "id": "registerjobdefinition-with-tags-1591290509028", + "title": "RegisterJobDefinition with tags" + } + ], + "SubmitJob": [ + { + "input": { + "jobDefinition": "sleep60", + "jobName": "example", + "jobQueue": "HighPriority" + }, + "output": { + "jobId": "876da822-4198-45f2-a252-6cea32512ea8", + "jobName": "example" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example submits a simple container job called example to the HighPriority job queue.", + "id": "to-submit-a-job-to-a-queue-1481154481673", + "title": "To submit a job to a queue" + } + ], + "TagResource": [ + { + "input": { + "resourceArn": "arn:aws:batch:us-east-1:123456789012:job-definition/sleep30:1", + "tags": { + "Stage": "Alpha" + } + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This demonstrates calling the TagResource action.", + "id": "tagresource-example-1591291959952", + "title": "TagResource Example" + } + ], + "TerminateJob": [ + { + "input": { + "jobId": "61e743ed-35e4-48da-b2de-5c8333821c84", + "reason": "Terminating job." + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example terminates a job with the specified job ID.", + "id": "to-terminate-a-job-1481154558276", + "title": "To terminate a job" + } + ], + "UntagResource": [ + { + "input": { + "resourceArn": "arn:aws:batch:us-east-1:123456789012:job-definition/sleep30:1", + "tagKeys": [ + "Stage" + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This demonstrates calling the UntagResource action.", + "id": "untagresource-example-1591292811042", + "title": "UntagResource Example" + } + ], + "UpdateComputeEnvironment": [ + { + "input": { + "computeEnvironment": "P2OnDemand", + "state": "DISABLED" + }, + "output": { + "computeEnvironmentArn": "arn:aws:batch:us-east-1:012345678910:compute-environment/P2OnDemand", + "computeEnvironmentName": "P2OnDemand" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disables the P2OnDemand compute environment so it can be deleted.", + "id": "to-update-a-compute-environment-1481154702731", + "title": "To update a compute environment" + } + ], + "UpdateJobQueue": [ + { + "input": { + "jobQueue": "GPGPU", + "state": "DISABLED" + }, + "output": { + "jobQueueArn": "arn:aws:batch:us-east-1:012345678910:job-queue/GPGPU", + "jobQueueName": "GPGPU" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disables a job queue so that it can be deleted.", + "id": "to-update-a-job-queue-1481154806981", + "title": "To update a job queue" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/batch/2016-08-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/batch/2016-08-10/paginators-1.json new file mode 100644 index 0000000..e8f4d9e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/batch/2016-08-10/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "DescribeComputeEnvironments": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "computeEnvironments" + }, + "DescribeJobDefinitions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "jobDefinitions" + }, + "DescribeJobQueues": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "jobQueues" + }, + "ListJobs": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "jobSummaryList" + }, + "ListSchedulingPolicies": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "schedulingPolicies" + }, + "ListConsumableResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "consumableResources" + }, + "ListJobsByConsumableResource": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobs" + }, + "DescribeServiceEnvironments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "serviceEnvironments" + }, + "ListServiceJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobSummaryList" + }, + "ListQuotaShares": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "quotaShares" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/batch/2016-08-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/batch/2016-08-10/service-2.json.gz new file mode 100644 index 0000000..11028b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/batch/2016-08-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-dashboards/2025-08-18/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bcm-dashboards/2025-08-18/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1cb565e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bcm-dashboards/2025-08-18/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-dashboards/2025-08-18/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/bcm-dashboards/2025-08-18/paginators-1.json new file mode 100644 index 0000000..c250958 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bcm-dashboards/2025-08-18/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListDashboards": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dashboards" + }, + "ListScheduledReports": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "scheduledReports" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-dashboards/2025-08-18/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bcm-dashboards/2025-08-18/service-2.json.gz new file mode 100644 index 0000000..843024b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bcm-dashboards/2025-08-18/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-dashboards/2025-08-18/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/bcm-dashboards/2025-08-18/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bcm-dashboards/2025-08-18/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-data-exports/2023-11-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bcm-data-exports/2023-11-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d400c10 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bcm-data-exports/2023-11-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-data-exports/2023-11-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/bcm-data-exports/2023-11-26/paginators-1.json new file mode 100644 index 0000000..3d03805 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bcm-data-exports/2023-11-26/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListExecutions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Executions" + }, + "ListExports": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Exports" + }, + "ListTables": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Tables" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-data-exports/2023-11-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bcm-data-exports/2023-11-26/service-2.json.gz new file mode 100644 index 0000000..72712ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bcm-data-exports/2023-11-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-pricing-calculator/2024-06-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bcm-pricing-calculator/2024-06-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ea6eeb3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bcm-pricing-calculator/2024-06-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-pricing-calculator/2024-06-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/bcm-pricing-calculator/2024-06-19/paginators-1.json new file mode 100644 index 0000000..678b04a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bcm-pricing-calculator/2024-06-19/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "ListBillEstimateCommitments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListBillEstimateInputCommitmentModifications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListBillEstimateInputUsageModifications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListBillEstimateLineItems": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListBillEstimates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListBillScenarioCommitmentModifications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListBillScenarioUsageModifications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListBillScenarios": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListWorkloadEstimateUsage": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListWorkloadEstimates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-pricing-calculator/2024-06-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bcm-pricing-calculator/2024-06-19/service-2.json.gz new file mode 100644 index 0000000..8386cd7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bcm-pricing-calculator/2024-06-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-pricing-calculator/2024-06-19/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/bcm-pricing-calculator/2024-06-19/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bcm-pricing-calculator/2024-06-19/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-recommended-actions/2024-11-14/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bcm-recommended-actions/2024-11-14/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..287bf30 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bcm-recommended-actions/2024-11-14/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-recommended-actions/2024-11-14/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/bcm-recommended-actions/2024-11-14/paginators-1.json new file mode 100644 index 0000000..11c6a2d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bcm-recommended-actions/2024-11-14/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListRecommendedActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recommendedActions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-recommended-actions/2024-11-14/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bcm-recommended-actions/2024-11-14/service-2.json.gz new file mode 100644 index 0000000..a4ccbea Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bcm-recommended-actions/2024-11-14/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bcm-recommended-actions/2024-11-14/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/bcm-recommended-actions/2024-11-14/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bcm-recommended-actions/2024-11-14/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6925bca Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/paginators-1.json new file mode 100644 index 0000000..d3b7246 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/paginators-1.json @@ -0,0 +1,50 @@ +{ + "pagination": { + "Retrieve": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "retrievalResults" + }, + "GetAgentMemory": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxItems", + "result_key": "memoryContents" + }, + "Rerank": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "results" + }, + "ListInvocationSteps": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "invocationStepSummaries" + }, + "ListInvocations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "invocationSummaries" + }, + "ListSessions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sessionSummaries" + }, + "ListFlowExecutionEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "flowExecutionEvents" + }, + "ListFlowExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "flowExecutionSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/paginators-1.sdk-extras.json new file mode 100644 index 0000000..30da00d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "Retrieve": { + "non_aggregate_keys": [ + "guardrailAction" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/service-2.json.gz new file mode 100644 index 0000000..36ab056 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent-runtime/2023-07-26/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent/2023-06-05/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent/2023-06-05/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a9bf0e7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent/2023-06-05/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent/2023-06-05/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent/2023-06-05/paginators-1.json new file mode 100644 index 0000000..3df76af --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent/2023-06-05/paginators-1.json @@ -0,0 +1,94 @@ +{ + "pagination": { + "ListAgentActionGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "actionGroupSummaries" + }, + "ListAgentAliases": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agentAliasSummaries" + }, + "ListAgentKnowledgeBases": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agentKnowledgeBaseSummaries" + }, + "ListAgentVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agentVersionSummaries" + }, + "ListAgents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agentSummaries" + }, + "ListDataSources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dataSourceSummaries" + }, + "ListIngestionJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "ingestionJobSummaries" + }, + "ListKnowledgeBases": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "knowledgeBaseSummaries" + }, + "ListFlowAliases": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "flowAliasSummaries" + }, + "ListFlowVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "flowVersionSummaries" + }, + "ListFlows": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "flowSummaries" + }, + "ListPrompts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "promptSummaries" + }, + "ListKnowledgeBaseDocuments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "documentDetails" + }, + "ListAgentCollaborators": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agentCollaboratorSummaries" + }, + "ListVpcConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent/2023-06-05/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent/2023-06-05/service-2.json.gz new file mode 100644 index 0000000..3c4812e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent/2023-06-05/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent/2023-06-05/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent/2023-06-05/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agent/2023-06-05/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..fd0eb9f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/paginators-1.json new file mode 100644 index 0000000..f333915 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/paginators-1.json @@ -0,0 +1,238 @@ +{ + "pagination": { + "ListAgentRuntimeEndpoints": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "runtimeEndpoints" + }, + "ListAgentRuntimeVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agentRuntimes" + }, + "ListAgentRuntimes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agentRuntimes" + }, + "ListApiKeyCredentialProviders": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "credentialProviders" + }, + "ListBrowsers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "browserSummaries" + }, + "ListCodeInterpreters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "codeInterpreterSummaries" + }, + "ListGatewayTargets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListGateways": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListMemories": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "memories" + }, + "ListOauth2CredentialProviders": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "credentialProviders" + }, + "ListWorkloadIdentities": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workloadIdentities" + }, + "ListEvaluators": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "evaluators" + }, + "ListOnlineEvaluationConfigs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "onlineEvaluationConfigs" + }, + "ListPolicies": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policies" + }, + "ListPolicyEngines": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policyEngines" + }, + "ListPolicyGenerationAssets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policyGenerationAssets" + }, + "ListPolicyGenerations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policyGenerations" + }, + "ListBrowserProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "profileSummaries" + }, + "ListRegistries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "registries" + }, + "ListRegistryRecords": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "registryRecords" + }, + "ListHarnesses": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "harnesses" + }, + "ListConfigurationBundleVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "versions" + }, + "ListConfigurationBundles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "bundles" + }, + "ListGatewayRules": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "gatewayRules" + }, + "ListPaymentConnectors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "paymentConnectors" + }, + "ListPaymentCredentialProviders": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "credentialProviders" + }, + "ListPaymentManagers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "paymentManagers" + }, + "ListPolicyEngineSummaries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policyEngines" + }, + "ListPolicyGenerationSummaries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policyGenerations" + }, + "ListPolicySummaries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policies" + }, + "ListDatasetExamples": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "examples" + }, + "ListDatasetVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "versions" + }, + "ListDatasets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "datasets" + }, + "ListHarnessEndpoints": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "endpoints" + }, + "ListHarnessVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "harnessVersions" + }, + "ListAgentRuntimeVersionsByCapacityProvider": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agentRuntimes" + }, + "ListCapacityProviders": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "capacityProviders" + }, + "ListGatewayRateLimits": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "rateLimits" + }, + "ListConsentPortals": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "consentPortals" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/paginators-1.sdk-extras.json new file mode 100644 index 0000000..bdaea5e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/paginators-1.sdk-extras.json @@ -0,0 +1,14 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListDatasetExamples": { + "non_aggregate_keys": [ + "datasetArn", + "datasetId", + "datasetVersion" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/service-2.json.gz new file mode 100644 index 0000000..8a11eaa Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/waiters-2.json new file mode 100644 index 0000000..605a9f9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore-control/2023-06-05/waiters-2.json @@ -0,0 +1,149 @@ +{ + "version" : 2, + "waiters" : { + "MemoryCreated" : { + "delay" : 2, + "maxAttempts" : 60, + "operation" : "GetMemory", + "acceptors" : [ { + "matcher" : "path", + "argument" : "memory.status", + "state" : "retry", + "expected" : "CREATING" + }, { + "matcher" : "path", + "argument" : "memory.status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "memory.status", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "PolicyActive" : { + "description" : "Wait until a Policy is active", + "delay" : 5, + "maxAttempts" : 24, + "operation" : "GetPolicy", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CREATE_FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "UPDATE_FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETE_FAILED" + } ] + }, + "PolicyDeleted" : { + "description" : "Wait until a Policy is deleted", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "GetPolicy", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "DELETING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETE_FAILED" + } ] + }, + "PolicyEngineActive" : { + "description" : "Wait until a PolicyEngine is active", + "delay" : 5, + "maxAttempts" : 24, + "operation" : "GetPolicyEngine", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CREATE_FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "UPDATE_FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETE_FAILED" + } ] + }, + "PolicyEngineDeleted" : { + "description" : "Wait until a PolicyEngine is deleted", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "GetPolicyEngine", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "DELETING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETE_FAILED" + } ] + }, + "PolicyGenerationCompleted" : { + "description" : "Wait until policy generation is completed", + "delay" : 5, + "maxAttempts" : 24, + "operation" : "GetPolicyGeneration", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "GENERATED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "GENERATING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "GENERATE_FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETE_FAILED" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore/2024-02-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore/2024-02-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dfa08f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore/2024-02-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore/2024-02-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore/2024-02-28/paginators-1.json new file mode 100644 index 0000000..7dfdb01 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore/2024-02-28/paginators-1.json @@ -0,0 +1,70 @@ +{ + "pagination": { + "ListActors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "actorSummaries" + }, + "ListEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "events" + }, + "ListMemoryRecords": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "memoryRecordSummaries" + }, + "ListSessions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sessionSummaries" + }, + "RetrieveMemoryRecords": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "memoryRecordSummaries" + }, + "ListMemoryExtractionJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobs" + }, + "ListABTests": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "abTests" + }, + "ListBatchEvaluations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "batchEvaluations" + }, + "ListRecommendations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recommendationSummaries" + }, + "ListPaymentInstruments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "paymentInstruments" + }, + "ListPaymentSessions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "paymentSessions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore/2024-02-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore/2024-02-28/service-2.json.gz new file mode 100644 index 0000000..49575be Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore/2024-02-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore/2024-02-28/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore/2024-02-28/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-agentcore/2024-02-28/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation-runtime/2024-06-13/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation-runtime/2024-06-13/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..402f07d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation-runtime/2024-06-13/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation-runtime/2024-06-13/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation-runtime/2024-06-13/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation-runtime/2024-06-13/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation-runtime/2024-06-13/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation-runtime/2024-06-13/service-2.json.gz new file mode 100644 index 0000000..a208505 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation-runtime/2024-06-13/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation/2023-07-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation/2023-07-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..2c3260d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation/2023-07-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation/2023-07-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation/2023-07-26/paginators-1.json new file mode 100644 index 0000000..30ed5a0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation/2023-07-26/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListBlueprints": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "blueprints" + }, + "ListDataAutomationProjects": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "projects" + }, + "ListDataAutomationLibraries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "libraries" + }, + "ListDataAutomationLibraryEntities": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "entities" + }, + "ListDataAutomationLibraryIngestionJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobs" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation/2023-07-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation/2023-07-26/service-2.json.gz new file mode 100644 index 0000000..2a86b5b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-data-automation/2023-07-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-runtime/2023-09-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-runtime/2023-09-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..642d520 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-runtime/2023-09-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-runtime/2023-09-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-runtime/2023-09-30/paginators-1.json new file mode 100644 index 0000000..b6c3d8b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-runtime/2023-09-30/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListAsyncInvokes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "asyncInvokeSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-runtime/2023-09-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock-runtime/2023-09-30/service-2.json.gz new file mode 100644 index 0000000..69e01c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock-runtime/2023-09-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock-runtime/2023-09-30/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock-runtime/2023-09-30/waiters-2.json new file mode 100644 index 0000000..4b20636 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock-runtime/2023-09-30/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock/2023-04-20/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock/2023-04-20/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0079a2a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock/2023-04-20/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock/2023-04-20/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock/2023-04-20/paginators-1.json new file mode 100644 index 0000000..ca17a87 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock/2023-04-20/paginators-1.json @@ -0,0 +1,117 @@ +{ + "pagination": { + "ListCustomModels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "modelSummaries" + }, + "ListModelCustomizationJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "modelCustomizationJobSummaries" + }, + "ListProvisionedModelThroughputs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "provisionedModelSummaries" + }, + "ListEvaluationJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobSummaries" + }, + "ListGuardrails": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "guardrails" + }, + "ListModelCopyJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "modelCopyJobSummaries" + }, + "ListModelInvocationJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "invocationJobSummaries" + }, + "ListImportedModels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "modelSummaries" + }, + "ListModelImportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "modelImportJobSummaries" + }, + "ListInferenceProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "inferenceProfileSummaries" + }, + "ListMarketplaceModelEndpoints": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "marketplaceModelEndpoints" + }, + "ListPromptRouters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "promptRouterSummaries" + }, + "ListCustomModelDeployments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "modelDeploymentSummaries" + }, + "ListAutomatedReasoningPolicies": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "automatedReasoningPolicySummaries" + }, + "ListAutomatedReasoningPolicyBuildWorkflows": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "automatedReasoningPolicyBuildWorkflowSummaries" + }, + "ListAutomatedReasoningPolicyTestCases": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "testCases" + }, + "ListAutomatedReasoningPolicyTestResults": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "testResults" + }, + "ListEnforcedGuardrailsConfiguration": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "guardrailsConfig" + }, + "ListAdvancedPromptOptimizationJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock/2023-04-20/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/bedrock/2023-04-20/service-2.json.gz new file mode 100644 index 0000000..af16094 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/bedrock/2023-04-20/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/bedrock/2023-04-20/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/bedrock/2023-04-20/waiters-2.json new file mode 100644 index 0000000..4b20636 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/bedrock/2023-04-20/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..583770f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/paginators-1.json new file mode 100644 index 0000000..8b320eb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "ListBillingViews": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "billingViews" + }, + "ListSourceViewsForBillingView": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sourceViews" + }, + "GetCreditAllocationHistory": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "creditAllocationHistoryList" + }, + "ListEnterpriseSupportLinkedAccountCharges": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "linkedAccount" + }, + "ListBillingViewSegments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListBusinessSupportAccountCharges": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "accountCharges" + }, + "ListBusinessSupportSubscriptionHistory": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "subscriptionContracts" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/paginators-1.sdk-extras.json new file mode 100644 index 0000000..942c979 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/paginators-1.sdk-extras.json @@ -0,0 +1,13 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetCreditAllocationHistory": { + "non_aggregate_keys": [ + "partialResults", + "failedMonths" + ] + } + } + } + } diff --git a/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/service-2.json.gz new file mode 100644 index 0000000..623f836 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/billing/2023-09-07/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d997842 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/paginators-1.json new file mode 100644 index 0000000..2ca4d75 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/paginators-1.json @@ -0,0 +1,80 @@ +{ + "pagination": { + "ListAccountAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "LinkedAccounts" + }, + "ListBillingGroupCostReports": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "BillingGroupCostReports" + }, + "ListBillingGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "BillingGroups" + }, + "ListCustomLineItems": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CustomLineItems" + }, + "ListPricingPlans": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "BillingPeriod" + ], + "output_token": "NextToken", + "result_key": "PricingPlans" + }, + "ListPricingPlansAssociatedWithPricingRule": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "BillingPeriod", + "PricingRuleArn" + ], + "output_token": "NextToken", + "result_key": "PricingPlanArns" + }, + "ListPricingRules": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "BillingPeriod" + ], + "output_token": "NextToken", + "result_key": "PricingRules" + }, + "ListPricingRulesAssociatedToPricingPlan": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "BillingPeriod", + "PricingPlanArn" + ], + "output_token": "NextToken", + "result_key": "PricingRuleArns" + }, + "ListResourcesAssociatedToCustomLineItem": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "Arn" + ], + "output_token": "NextToken", + "result_key": "AssociatedResources" + }, + "ListCustomLineItemVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CustomLineItemVersions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/service-2.json.gz new file mode 100644 index 0000000..fc526da Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/waiters-2.json new file mode 100644 index 0000000..ee5023d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/billingconductor/2021-07-30/waiters-2.json @@ -0,0 +1,4 @@ +{ + "version": 2, + "waiters": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/braket/2019-09-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/braket/2019-09-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..2e5e1f9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/braket/2019-09-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/braket/2019-09-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/braket/2019-09-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/braket/2019-09-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/braket/2019-09-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/braket/2019-09-01/paginators-1.json new file mode 100644 index 0000000..2f0f69e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/braket/2019-09-01/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "SearchDevices": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "devices" + }, + "SearchQuantumTasks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "quantumTasks" + }, + "SearchJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobs" + }, + "SearchSpendingLimits": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "spendingLimits" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/braket/2019-09-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/braket/2019-09-01/service-2.json.gz new file mode 100644 index 0000000..5ee688e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/braket/2019-09-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/budgets/2016-10-20/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/budgets/2016-10-20/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..49d01ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/budgets/2016-10-20/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/budgets/2016-10-20/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/budgets/2016-10-20/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/budgets/2016-10-20/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/budgets/2016-10-20/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/budgets/2016-10-20/paginators-1.json new file mode 100644 index 0000000..15f7a63 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/budgets/2016-10-20/paginators-1.json @@ -0,0 +1,52 @@ +{ + "pagination": { + "DescribeBudgets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Budgets" + }, + "DescribeNotificationsForBudget": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Notifications" + }, + "DescribeSubscribersForNotification": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Subscribers" + }, + "DescribeBudgetPerformanceHistory": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "BudgetPerformanceHistory" + }, + "DescribeBudgetActionHistories": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ActionHistories" + }, + "DescribeBudgetActionsForAccount": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Actions" + }, + "DescribeBudgetActionsForBudget": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Actions" + }, + "DescribeBudgetNotificationsForAccount": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "BudgetNotificationsForAccount" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/budgets/2016-10-20/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/budgets/2016-10-20/service-2.json.gz new file mode 100644 index 0000000..ddcd44d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/budgets/2016-10-20/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..efdc4f8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/paginators-1.json new file mode 100644 index 0000000..764af9f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/paginators-1.json @@ -0,0 +1,82 @@ +{ + "pagination": { + "GetAnomalies": { + "input_token": "NextPageToken", + "output_token": "NextPageToken", + "limit_key": "MaxResults", + "result_key": "Anomalies" + }, + "GetAnomalyMonitors": { + "input_token": "NextPageToken", + "output_token": "NextPageToken", + "limit_key": "MaxResults", + "result_key": "AnomalyMonitors" + }, + "GetAnomalySubscriptions": { + "input_token": "NextPageToken", + "output_token": "NextPageToken", + "limit_key": "MaxResults", + "result_key": "AnomalySubscriptions" + }, + "GetCostAndUsageComparisons": { + "input_token": "NextPageToken", + "output_token": "NextPageToken", + "limit_key": "MaxResults", + "result_key": "CostAndUsageComparisons" + }, + "GetCostComparisonDrivers": { + "input_token": "NextPageToken", + "output_token": "NextPageToken", + "limit_key": "MaxResults", + "result_key": "CostComparisonDrivers" + }, + "ListCostAllocationTagBackfillHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BackfillRequests" + }, + "ListCostAllocationTags": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CostAllocationTags" + }, + "ListCostCategoryDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CostCategoryReferences" + }, + "ListCostCategoryResourceAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CostCategoryResourceAssociations" + }, + "GetReservationPurchaseRecommendation": { + "input_token": "NextPageToken", + "output_token": "NextPageToken", + "limit_key": "PageSize", + "result_key": "Recommendations" + }, + "GetRightsizingRecommendation": { + "input_token": "NextPageToken", + "output_token": "NextPageToken", + "limit_key": "PageSize", + "result_key": "RightsizingRecommendations" + }, + "ListCommitmentPurchaseAnalyses": { + "input_token": "NextPageToken", + "output_token": "NextPageToken", + "limit_key": "PageSize", + "result_key": "AnalysisSummaryList" + }, + "ListSavingsPlansPurchaseRecommendationGeneration": { + "input_token": "NextPageToken", + "output_token": "NextPageToken", + "limit_key": "PageSize", + "result_key": "GenerationSummaryList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/paginators-1.sdk-extras.json new file mode 100644 index 0000000..83b187a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/paginators-1.sdk-extras.json @@ -0,0 +1,24 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetCostAndUsageComparisons": { + "non_aggregate_keys": [ + "TotalCostAndUsage" + ] + }, + "GetReservationPurchaseRecommendation": { + "non_aggregate_keys": [ + "Metadata" + ] + }, + "GetRightsizingRecommendation": { + "non_aggregate_keys": [ + "Configuration", + "Metadata", + "Summary" + ] + } + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/service-2.json.gz new file mode 100644 index 0000000..2e3f0e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ce/2017-10-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chatbot/2017-10-11/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chatbot/2017-10-11/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8972f00 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chatbot/2017-10-11/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chatbot/2017-10-11/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/chatbot/2017-10-11/paginators-1.json new file mode 100644 index 0000000..5e4b764 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/chatbot/2017-10-11/paginators-1.json @@ -0,0 +1,58 @@ +{ + "pagination": { + "DescribeChimeWebhookConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "WebhookConfigurations" + }, + "DescribeSlackChannelConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SlackChannelConfigurations" + }, + "DescribeSlackUserIdentities": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SlackUserIdentities" + }, + "DescribeSlackWorkspaces": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SlackWorkspaces" + }, + "ListMicrosoftTeamsChannelConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TeamChannelConfigurations" + }, + "ListMicrosoftTeamsConfiguredTeams": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ConfiguredTeams" + }, + "ListMicrosoftTeamsUserIdentities": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TeamsUserIdentities" + }, + "ListAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Associations" + }, + "ListCustomActions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CustomActions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/chatbot/2017-10-11/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chatbot/2017-10-11/service-2.json.gz new file mode 100644 index 0000000..1fb525b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chatbot/2017-10-11/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-identity/2021-04-20/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-identity/2021-04-20/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..fa74448 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-identity/2021-04-20/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-identity/2021-04-20/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-identity/2021-04-20/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-identity/2021-04-20/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-identity/2021-04-20/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-identity/2021-04-20/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-identity/2021-04-20/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-identity/2021-04-20/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-identity/2021-04-20/service-2.json.gz new file mode 100644 index 0000000..891abc4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-identity/2021-04-20/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-media-pipelines/2021-07-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-media-pipelines/2021-07-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..737882d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-media-pipelines/2021-07-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-media-pipelines/2021-07-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-media-pipelines/2021-07-15/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-media-pipelines/2021-07-15/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-media-pipelines/2021-07-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-media-pipelines/2021-07-15/service-2.json.gz new file mode 100644 index 0000000..2382079 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-media-pipelines/2021-07-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-meetings/2021-07-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-meetings/2021-07-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..451c314 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-meetings/2021-07-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-meetings/2021-07-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-meetings/2021-07-15/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-meetings/2021-07-15/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-meetings/2021-07-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-meetings/2021-07-15/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-meetings/2021-07-15/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-meetings/2021-07-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-meetings/2021-07-15/service-2.json.gz new file mode 100644 index 0000000..2bdf7b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-meetings/2021-07-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-messaging/2021-05-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-messaging/2021-05-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dbb4708 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-messaging/2021-05-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-messaging/2021-05-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-messaging/2021-05-15/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-messaging/2021-05-15/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-messaging/2021-05-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-messaging/2021-05-15/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-messaging/2021-05-15/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-messaging/2021-05-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-messaging/2021-05-15/service-2.json.gz new file mode 100644 index 0000000..92602a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-messaging/2021-05-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-voice/2022-08-03/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-voice/2022-08-03/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ae43512 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-voice/2022-08-03/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-voice/2022-08-03/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-voice/2022-08-03/paginators-1.json new file mode 100644 index 0000000..648f71e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-voice/2022-08-03/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListSipMediaApplications": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SipMediaApplications" + }, + "ListSipRules": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SipRules" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-voice/2022-08-03/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-voice/2022-08-03/service-2.json.gz new file mode 100644 index 0000000..2e8ac2e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chime-sdk-voice/2022-08-03/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime/2018-05-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chime/2018-05-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d1e210f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chime/2018-05-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime/2018-05-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/chime/2018-05-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/chime/2018-05-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime/2018-05-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/chime/2018-05-01/paginators-1.json new file mode 100644 index 0000000..617b114 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/chime/2018-05-01/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListAccounts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Accounts" + }, + "ListUsers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Users" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/chime/2018-05-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/chime/2018-05-01/service-2.json.gz new file mode 100644 index 0000000..719b0a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/chime/2018-05-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cleanrooms/2022-02-17/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cleanrooms/2022-02-17/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ee4161d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cleanrooms/2022-02-17/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cleanrooms/2022-02-17/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cleanrooms/2022-02-17/paginators-1.json new file mode 100644 index 0000000..707e837 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cleanrooms/2022-02-17/paginators-1.json @@ -0,0 +1,142 @@ +{ + "pagination": { + "ListCollaborations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "collaborationList" + }, + "ListConfiguredTableAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "configuredTableAssociationSummaries" + }, + "ListConfiguredTables": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "configuredTableSummaries" + }, + "ListMembers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "memberSummaries" + }, + "ListMemberships": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "membershipSummaries" + }, + "ListProtectedQueries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "protectedQueries" + }, + "ListSchemas": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "schemaSummaries" + }, + "ListAnalysisTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "analysisTemplateSummaries" + }, + "ListCollaborationAnalysisTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "collaborationAnalysisTemplateSummaries" + }, + "ListCollaborationConfiguredAudienceModelAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "collaborationConfiguredAudienceModelAssociationSummaries" + }, + "ListCollaborationPrivacyBudgetTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "collaborationPrivacyBudgetTemplateSummaries" + }, + "ListCollaborationPrivacyBudgets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "collaborationPrivacyBudgetSummaries" + }, + "ListConfiguredAudienceModelAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "configuredAudienceModelAssociationSummaries" + }, + "ListPrivacyBudgetTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "privacyBudgetTemplateSummaries" + }, + "ListPrivacyBudgets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "privacyBudgetSummaries" + }, + "ListCollaborationIdNamespaceAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "collaborationIdNamespaceAssociationSummaries" + }, + "ListIdMappingTables": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "idMappingTableSummaries" + }, + "ListIdNamespaceAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "idNamespaceAssociationSummaries" + }, + "ListProtectedJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "protectedJobs" + }, + "ListCollaborationChangeRequests": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "collaborationChangeRequestSummaries" + }, + "ListIntermediateTableVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "intermediateTableVersionSummaries" + }, + "ListIntermediateTables": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "intermediateTableSummaries" + }, + "ListAnalysisLogExports": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "analysisLogExports" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cleanrooms/2022-02-17/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cleanrooms/2022-02-17/service-2.json.gz new file mode 100644 index 0000000..c14e5e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cleanrooms/2022-02-17/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cleanrooms/2022-02-17/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cleanrooms/2022-02-17/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cleanrooms/2022-02-17/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cleanroomsml/2023-09-06/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cleanroomsml/2023-09-06/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..18ea8c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cleanroomsml/2023-09-06/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cleanroomsml/2023-09-06/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cleanroomsml/2023-09-06/paginators-1.json new file mode 100644 index 0000000..570b41c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cleanroomsml/2023-09-06/paginators-1.json @@ -0,0 +1,100 @@ +{ + "pagination": { + "ListAudienceExportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "audienceExportJobs" + }, + "ListAudienceGenerationJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "audienceGenerationJobs" + }, + "ListAudienceModels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "audienceModels" + }, + "ListConfiguredAudienceModels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "configuredAudienceModels" + }, + "ListTrainingDatasets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "trainingDatasets" + }, + "ListCollaborationConfiguredModelAlgorithmAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "collaborationConfiguredModelAlgorithmAssociations" + }, + "ListCollaborationMLInputChannels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "collaborationMLInputChannelsList" + }, + "ListCollaborationTrainedModelExportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "collaborationTrainedModelExportJobs" + }, + "ListCollaborationTrainedModelInferenceJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "collaborationTrainedModelInferenceJobs" + }, + "ListCollaborationTrainedModels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "collaborationTrainedModels" + }, + "ListConfiguredModelAlgorithmAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "configuredModelAlgorithmAssociations" + }, + "ListConfiguredModelAlgorithms": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "configuredModelAlgorithms" + }, + "ListMLInputChannels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "mlInputChannelsList" + }, + "ListTrainedModelInferenceJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "trainedModelInferenceJobs" + }, + "ListTrainedModels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "trainedModels" + }, + "ListTrainedModelVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "trainedModels" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cleanroomsml/2023-09-06/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cleanroomsml/2023-09-06/service-2.json.gz new file mode 100644 index 0000000..517e2f9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cleanroomsml/2023-09-06/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cleanroomsml/2023-09-06/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cleanroomsml/2023-09-06/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cleanroomsml/2023-09-06/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloud9/2017-09-23/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloud9/2017-09-23/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..45a1d37 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloud9/2017-09-23/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloud9/2017-09-23/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloud9/2017-09-23/examples-1.json new file mode 100644 index 0000000..fdef270 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloud9/2017-09-23/examples-1.json @@ -0,0 +1,315 @@ +{ + "version": "1.0", + "examples": { + "CreateEnvironmentEC2": [ + { + "input": { + "name": "my-demo-environment", + "automaticStopTimeMinutes": 60, + "description": "This is my demonstration environment.", + "instanceType": "t2.micro", + "ownerArn": "arn:aws:iam::123456789012:user/MyDemoUser", + "subnetId": "subnet-6300cd1b" + }, + "output": { + "environmentId": "8d9967e2f0624182b74e7690ad69ebEX" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "createenvironmentec2-1516821730547", + "title": "CreateEnvironmentEC2" + } + ], + "CreateEnvironmentMembership": [ + { + "input": { + "environmentId": "8d9967e2f0624182b74e7690ad69ebEX", + "permissions": "read-write", + "userArn": "arn:aws:iam::123456789012:user/AnotherDemoUser" + }, + "output": { + "membership": { + "environmentId": "8d9967e2f0624182b74e7690ad69ebEX", + "permissions": "read-write", + "userArn": "arn:aws:iam::123456789012:user/AnotherDemoUser", + "userId": "AIDAJ3BA6O2FMJWCWXHEX" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "createenvironmentmembership-1516822583452", + "title": "CreateEnvironmentMembership" + } + ], + "DeleteEnvironment": [ + { + "input": { + "environmentId": "8d9967e2f0624182b74e7690ad69ebEX" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "deleteenvironment-1516822903149", + "title": "DeleteEnvironment" + } + ], + "DeleteEnvironmentMembership": [ + { + "input": { + "environmentId": "8d9967e2f0624182b74e7690ad69ebEX", + "userArn": "arn:aws:iam::123456789012:user/AnotherDemoUser" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "deleteenvironmentmembership-1516822975655", + "title": "DeleteEnvironmentMembership" + } + ], + "DescribeEnvironmentMemberships": [ + { + "input": { + "environmentId": "8d9967e2f0624182b74e7690ad69ebEX" + }, + "output": { + "memberships": [ + { + "environmentId": "8d9967e2f0624182b74e7690ad69ebEX", + "permissions": "read-write", + "userArn": "arn:aws:iam::123456789012:user/AnotherDemoUser", + "userId": "AIDAJ3BA6O2FMJWCWXHEX" + }, + { + "environmentId": "8d9967e2f0624182b74e7690ad69ebEX", + "permissions": "owner", + "userArn": "arn:aws:iam::123456789012:user/MyDemoUser", + "userId": "AIDAJNUEDQAQWFELJDLEX" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example gets information about all of the environment members for the specified development environment.", + "id": "describeenvironmentmemberships1-1516823070453", + "title": "DescribeEnvironmentMemberships1" + }, + { + "input": { + "environmentId": "8d9967e2f0624182b74e7690ad69ebEX", + "permissions": [ + "owner" + ] + }, + "output": { + "memberships": [ + { + "environmentId": "8d9967e2f0624182b74e7690ad69ebEX", + "permissions": "owner", + "userArn": "arn:aws:iam::123456789012:user/MyDemoUser", + "userId": "AIDAJNUEDQAQWFELJDLEX" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example gets information about the owner of the specified development environment.", + "id": "describeenvironmentmemberships2-1516823191355", + "title": "DescribeEnvironmentMemberships2" + }, + { + "input": { + "userArn": "arn:aws:iam::123456789012:user/MyDemoUser" + }, + "output": { + "memberships": [ + { + "environmentId": "10a75714bd494714929e7f5ec4125aEX", + "lastAccess": "2018-01-19T11:06:13Z", + "permissions": "owner", + "userArn": "arn:aws:iam::123456789012:user/MyDemoUser", + "userId": "AIDAJNUEDQAQWFELJDLEX" + }, + { + "environmentId": "12bfc3cd537f41cb9776f8af5525c9EX", + "lastAccess": "2018-01-19T11:39:19Z", + "permissions": "owner", + "userArn": "arn:aws:iam::123456789012:user/MyDemoUser", + "userId": "AIDAJNUEDQAQWFELJDLEX" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example gets development environment membership information for the specified user.", + "id": "describeenvironmentmemberships3-1516823268793", + "title": "DescribeEnvironmentMemberships3" + } + ], + "DescribeEnvironmentStatus": [ + { + "input": { + "environmentId": "8d9967e2f0624182b74e7690ad69ebEX" + }, + "output": { + "message": "Environment is ready to use", + "status": "ready" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "describeenvironmentstatus-1516823462133", + "title": "DescribeEnvironmentStatus" + } + ], + "DescribeEnvironments": [ + { + "input": { + "environmentIds": [ + "8d9967e2f0624182b74e7690ad69ebEX", + "349c86d4579e4e7298d500ff57a6b2EX" + ] + }, + "output": { + "environments": [ + { + "name": "my-demo-environment", + "type": "ec2", + "arn": "arn:aws:cloud9:us-east-2:123456789012:environment:8d9967e2f0624182b74e7690ad69ebEX", + "description": "This is my demonstration environment.", + "id": "8d9967e2f0624182b74e7690ad69ebEX", + "lifecycle": { + "status": "CREATED" + }, + "ownerArn": "arn:aws:iam::123456789012:user/MyDemoUser" + }, + { + "name": "another-demo-environment", + "type": "ssh", + "arn": "arn:aws:cloud9:us-east-2:123456789012:environment:349c86d4579e4e7298d500ff57a6b2EX", + "description": "", + "id": "349c86d4579e4e7298d500ff57a6b2EX", + "lifecycle": { + "status": "CREATED" + }, + "ownerArn": "arn:aws:sts::123456789012:assumed-role/AnotherDemoUser/AnotherDemoUser" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "describeenvironments-1516823568291", + "title": "DescribeEnvironments" + } + ], + "ListEnvironments": [ + { + "input": { + }, + "output": { + "environmentIds": [ + "349c86d4579e4e7298d500ff57a6b2EX", + "45a3da47af0840f2b0c0824f5ee232EX" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "listenvironments-1516823687205", + "title": "ListEnvironments" + } + ], + "UpdateEnvironment": [ + { + "input": { + "name": "my-changed-demo-environment", + "description": "This is my changed demonstration environment.", + "environmentId": "8d9967e2f0624182b74e7690ad69ebEX" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "updateenvironment-1516823781910", + "title": "UpdateEnvironment" + } + ], + "UpdateEnvironmentMembership": [ + { + "input": { + "environmentId": "8d9967e2f0624182b74e7690ad69ebEX", + "permissions": "read-only", + "userArn": "arn:aws:iam::123456789012:user/AnotherDemoUser" + }, + "output": { + "membership": { + "environmentId": "8d9967e2f0624182b74e7690ad69eb31", + "permissions": "read-only", + "userArn": "arn:aws:iam::123456789012:user/AnotherDemoUser", + "userId": "AIDAJ3BA6O2FMJWCWXHEX" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "updateenvironmentmembership-1516823876645", + "title": "UpdateEnvironmentMembership" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloud9/2017-09-23/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloud9/2017-09-23/paginators-1.json new file mode 100644 index 0000000..1c4c2ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloud9/2017-09-23/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "DescribeEnvironmentMemberships": { + "result_key": "memberships", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "ListEnvironments": { + "result_key": "environmentIds", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloud9/2017-09-23/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloud9/2017-09-23/service-2.json.gz new file mode 100644 index 0000000..415a063 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloud9/2017-09-23/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..9ad51d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/paginators-1.json new file mode 100644 index 0000000..14380b0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListResourceRequests": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResourceRequestStatusSummaries" + }, + "ListResources": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResourceDescriptions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/paginators-1.sdk-extras.json new file mode 100644 index 0000000..d0d47fb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListResources": { + "non_aggregate_keys": [ + "TypeName" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/service-2.json.gz new file mode 100644 index 0000000..076bc8d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/waiters-2.json new file mode 100644 index 0000000..e5f82ac --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudcontrol/2021-09-30/waiters-2.json @@ -0,0 +1,27 @@ +{ + "version" : 2, + "waiters" : { + "ResourceRequestSuccess" : { + "description" : "Wait until resource operation request is successful", + "delay" : 5, + "maxAttempts" : 24, + "operation" : "GetResourceRequestStatus", + "acceptors" : [ { + "matcher" : "path", + "argument" : "ProgressEvent.OperationStatus", + "state" : "success", + "expected" : "SUCCESS" + }, { + "matcher" : "path", + "argument" : "ProgressEvent.OperationStatus", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "ProgressEvent.OperationStatus", + "state" : "failure", + "expected" : "CANCEL_COMPLETE" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2016-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2016-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3583de3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2016-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2016-05-10/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2016-05-10/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2016-05-10/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2016-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2016-05-10/paginators-1.json new file mode 100644 index 0000000..22cc439 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2016-05-10/paginators-1.json @@ -0,0 +1,100 @@ +{ + "pagination": { + "ListObjectParentPaths": { + "result_key": "PathToObjectIdentifiersList", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListFacetNames": { + "result_key": "FacetNames", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListPublishedSchemaArns": { + "result_key": "SchemaArns", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListDirectories": { + "result_key": "Directories", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListDevelopmentSchemaArns": { + "result_key": "SchemaArns", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListTypedLinkFacetNames": { + "result_key": "FacetNames", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListIndex": { + "result_key": "IndexAttachments", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListFacetAttributes": { + "result_key": "Attributes", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListObjectPolicies": { + "result_key": "AttachedPolicyIds", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListTagsForResource": { + "result_key": "Tags", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListAttachedIndices": { + "result_key": "IndexAttachments", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "LookupPolicy": { + "result_key": "PolicyToPathList", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListPolicyAttachments": { + "result_key": "ObjectIdentifiers", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListObjectAttributes": { + "result_key": "Attributes", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListAppliedSchemaArns": { + "result_key": "SchemaArns", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListTypedLinkFacetAttributes": { + "result_key": "Attributes", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2016-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2016-05-10/service-2.json.gz new file mode 100644 index 0000000..dd74594 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2016-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2017-01-11/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2017-01-11/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a6e8da4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2017-01-11/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2017-01-11/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2017-01-11/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2017-01-11/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2017-01-11/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2017-01-11/paginators-1.json new file mode 100644 index 0000000..5a06fb0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2017-01-11/paginators-1.json @@ -0,0 +1,118 @@ +{ + "pagination": { + "ListObjectParentPaths": { + "result_key": "PathToObjectIdentifiersList", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListFacetNames": { + "result_key": "FacetNames", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListPublishedSchemaArns": { + "result_key": "SchemaArns", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListDirectories": { + "result_key": "Directories", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListDevelopmentSchemaArns": { + "result_key": "SchemaArns", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListTypedLinkFacetNames": { + "result_key": "FacetNames", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListIndex": { + "result_key": "IndexAttachments", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListFacetAttributes": { + "result_key": "Attributes", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListObjectPolicies": { + "result_key": "AttachedPolicyIds", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListTagsForResource": { + "result_key": "Tags", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListAttachedIndices": { + "result_key": "IndexAttachments", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "LookupPolicy": { + "result_key": "PolicyToPathList", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListPolicyAttachments": { + "result_key": "ObjectIdentifiers", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListObjectAttributes": { + "result_key": "Attributes", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListAppliedSchemaArns": { + "result_key": "SchemaArns", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListTypedLinkFacetAttributes": { + "result_key": "Attributes", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListIncomingTypedLinks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LinkSpecifiers" + }, + "ListManagedSchemaArns": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SchemaArns" + }, + "ListOutgoingTypedLinks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TypedLinkSpecifiers" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2017-01-11/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2017-01-11/service-2.json.gz new file mode 100644 index 0000000..d07e16f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/clouddirectory/2017-01-11/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..47fa43a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/paginators-1.json new file mode 100644 index 0000000..6e766b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/paginators-1.json @@ -0,0 +1,144 @@ +{ + "pagination": { + "DescribeAccountLimits": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "AccountLimits" + }, + "DescribeChangeSet": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Changes", + "non_aggregate_keys": [ + "ChangeSetName", + "ChangeSetId", + "StackId", + "StackName", + "Description", + "Parameters", + "CreationTime", + "ExecutionStatus", + "Status", + "StatusReason", + "NotificationARNs", + "RollbackConfiguration", + "Capabilities", + "Tags", + "ParentChangeSetId", + "IncludeNestedStacks", + "RootChangeSetId", + "OnStackFailure", + "ImportExistingResources", + "StackDriftStatus", + "DeploymentMode", + "DeploymentConfig" + ] + }, + "DescribeStackEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "StackEvents" + }, + "DescribeStacks": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Stacks" + }, + "ListChangeSets": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Summaries" + }, + "ListStackInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Summaries" + }, + "ListStackResources": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "StackResourceSummaries" + }, + "ListStacks": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "StackSummaries" + }, + "ListStackSetOperationResults": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Summaries" + }, + "ListStackSetOperations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Summaries" + }, + "ListStackSets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Summaries" + }, + "ListExports": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Exports" + }, + "ListImports": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Imports" + }, + "ListTypes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TypeSummaries" + }, + "ListGeneratedTemplates": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Summaries" + }, + "ListResourceScanRelatedResources": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "RelatedResources" + }, + "ListResourceScanResources": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Resources" + }, + "ListResourceScans": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ResourceScanSummaries" + }, + "ListStackRefactorActions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "StackRefactorActions" + }, + "ListStackRefactors": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "StackRefactorSummaries" + }, + "DescribeEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "OperationEvents" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/service-2.json.gz new file mode 100644 index 0000000..bbee3f3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/waiters-2.json new file mode 100644 index 0000000..0bfa9b0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudformation/2010-05-15/waiters-2.json @@ -0,0 +1,404 @@ +{ + "version": 2, + "waiters": { + "StackExists": { + "delay": 5, + "operation": "DescribeStacks", + "maxAttempts": 20, + "acceptors": [ + { + "matcher": "status", + "expected": 200, + "state": "success" + }, + { + "matcher": "error", + "expected": "ValidationError", + "state": "retry" + } + ] + }, + "StackCreateComplete": { + "delay": 30, + "operation": "DescribeStacks", + "maxAttempts": 120, + "description": "Wait until stack status is CREATE_COMPLETE.", + "acceptors": [ + { + "argument": "Stacks[].StackStatus", + "expected": "CREATE_COMPLETE", + "matcher": "pathAll", + "state": "success" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_COMPLETE", + "matcher": "pathAll", + "state": "success" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_IN_PROGRESS", + "matcher": "pathAll", + "state": "success" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS", + "matcher": "pathAll", + "state": "success" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_FAILED", + "matcher": "pathAll", + "state": "success" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_ROLLBACK_IN_PROGRESS", + "matcher": "pathAll", + "state": "success" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_ROLLBACK_FAILED", + "matcher": "pathAll", + "state": "success" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS", + "matcher": "pathAll", + "state": "success" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_ROLLBACK_COMPLETE", + "matcher": "pathAll", + "state": "success" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "CREATE_FAILED", + "matcher": "pathAny", + "state": "failure" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "DELETE_COMPLETE", + "matcher": "pathAny", + "state": "failure" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "DELETE_FAILED", + "matcher": "pathAny", + "state": "failure" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "ROLLBACK_FAILED", + "matcher": "pathAny", + "state": "failure" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "ROLLBACK_COMPLETE", + "matcher": "pathAny", + "state": "failure" + }, + { + "expected": "ValidationError", + "matcher": "error", + "state": "failure" + } + ] + }, + "StackDeleteComplete": { + "delay": 30, + "operation": "DescribeStacks", + "maxAttempts": 120, + "description": "Wait until stack status is DELETE_COMPLETE.", + "acceptors": [ + { + "argument": "Stacks[].StackStatus", + "expected": "DELETE_COMPLETE", + "matcher": "pathAll", + "state": "success" + }, + { + "expected": "ValidationError", + "matcher": "error", + "state": "success" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "DELETE_FAILED", + "matcher": "pathAny", + "state": "failure" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "CREATE_FAILED", + "matcher": "pathAny", + "state": "failure" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "ROLLBACK_FAILED", + "matcher": "pathAny", + "state": "failure" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_ROLLBACK_IN_PROGRESS", + "matcher": "pathAny", + "state": "failure" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_ROLLBACK_FAILED", + "matcher": "pathAny", + "state": "failure" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_ROLLBACK_COMPLETE", + "matcher": "pathAny", + "state": "failure" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_COMPLETE", + "matcher": "pathAny", + "state": "failure" + } + ] + }, + "StackUpdateComplete": { + "delay": 30, + "maxAttempts": 120, + "operation": "DescribeStacks", + "description": "Wait until stack status is UPDATE_COMPLETE.", + "acceptors": [ + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_COMPLETE", + "matcher": "pathAll", + "state": "success" + }, + { + "expected": "UPDATE_FAILED", + "matcher": "pathAny", + "state": "failure", + "argument": "Stacks[].StackStatus" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_ROLLBACK_FAILED", + "matcher": "pathAny", + "state": "failure" + }, + { + "expected": "UPDATE_ROLLBACK_COMPLETE", + "matcher": "pathAny", + "state": "failure", + "argument": "Stacks[].StackStatus" + }, + { + "expected": "ValidationError", + "matcher": "error", + "state": "failure" + } + ] + }, + "StackImportComplete": { + "delay": 30, + "maxAttempts": 120, + "operation": "DescribeStacks", + "description": "Wait until stack status is IMPORT_COMPLETE.", + "acceptors": [ + { + "argument": "Stacks[].StackStatus", + "expected": "IMPORT_COMPLETE", + "matcher": "pathAll", + "state": "success" + }, + { + "expected": "ROLLBACK_COMPLETE", + "matcher": "pathAny", + "state": "failure", + "argument": "Stacks[].StackStatus" + }, + { + "expected": "ROLLBACK_FAILED", + "matcher": "pathAny", + "state": "failure", + "argument": "Stacks[].StackStatus" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "IMPORT_ROLLBACK_IN_PROGRESS", + "matcher": "pathAny", + "state": "failure" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "IMPORT_ROLLBACK_FAILED", + "matcher": "pathAny", + "state": "failure" + }, + { + "expected": "IMPORT_ROLLBACK_COMPLETE", + "matcher": "pathAny", + "state": "failure", + "argument": "Stacks[].StackStatus" + }, + { + "expected": "ValidationError", + "matcher": "error", + "state": "failure" + } + ] + }, + "StackRollbackComplete": { + "delay": 30, + "operation": "DescribeStacks", + "maxAttempts": 120, + "description": "Wait until stack status is UPDATE_ROLLBACK_COMPLETE.", + "acceptors": [ + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_ROLLBACK_COMPLETE", + "matcher": "pathAll", + "state": "success" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_FAILED", + "matcher": "pathAny", + "state": "failure" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "UPDATE_ROLLBACK_FAILED", + "matcher": "pathAny", + "state": "failure" + }, + { + "argument": "Stacks[].StackStatus", + "expected": "DELETE_FAILED", + "matcher": "pathAny", + "state": "failure" + }, + { + "expected": "ValidationError", + "matcher": "error", + "state": "failure" + } + ] + }, + "ChangeSetCreateComplete": { + "delay": 30, + "operation": "DescribeChangeSet", + "maxAttempts": 120, + "description": "Wait until change set status is CREATE_COMPLETE.", + "acceptors": [ + { + "argument": "Status", + "expected": "CREATE_COMPLETE", + "matcher": "path", + "state": "success" + }, + { + "argument": "Status", + "expected": "FAILED", + "matcher": "path", + "state": "failure" + }, + { + "expected": "ValidationError", + "matcher": "error", + "state": "failure" + } + ] + }, + "StackRefactorCreateComplete": { + "delay": 5, + "operation": "DescribeStackRefactor", + "maxAttempts": 120, + "description": "Wait until the stack refactor status is CREATE_COMPLETE.", + "acceptors": [ + { + "argument": "Status", + "expected": "CREATE_COMPLETE", + "matcher": "path", + "state": "success" + }, + { + "argument": "Status", + "expected": "CREATE_FAILED", + "matcher": "path", + "state": "failure" + }, + { + "expected": "ValidationError", + "matcher": "error", + "state": "failure" + } + ] + }, + "StackRefactorExecuteComplete": { + "delay": 15, + "operation": "DescribeStackRefactor", + "maxAttempts": 120, + "description": "Wait until the stack refactor status is EXECUTE_COMPLETE.", + "acceptors": [ + { + "argument": "ExecutionStatus", + "expected": "EXECUTE_COMPLETE", + "matcher": "path", + "state": "success" + }, + { + "argument": "ExecutionStatus", + "expected": "EXECUTE_FAILED", + "matcher": "path", + "state": "failure" + }, + { + "argument": "ExecutionStatus", + "expected": "ROLLBACK_COMPLETE", + "matcher": "path", + "state": "failure" + }, + { + "expected": "ValidationError", + "matcher": "error", + "state": "failure" + } + ] + }, + "TypeRegistrationComplete": { + "delay": 30, + "operation": "DescribeTypeRegistration", + "maxAttempts": 120, + "description": "Wait until type registration is COMPLETE.", + "acceptors": [ + { + "argument": "ProgressStatus", + "expected": "COMPLETE", + "matcher": "path", + "state": "success" + }, + { + "argument": "ProgressStatus", + "expected": "FAILED", + "matcher": "path", + "state": "failure" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront-keyvaluestore/2022-07-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront-keyvaluestore/2022-07-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7083435 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront-keyvaluestore/2022-07-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront-keyvaluestore/2022-07-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront-keyvaluestore/2022-07-26/paginators-1.json new file mode 100644 index 0000000..8fda57a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront-keyvaluestore/2022-07-26/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListKeys": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront-keyvaluestore/2022-07-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront-keyvaluestore/2022-07-26/service-2.json.gz new file mode 100644 index 0000000..23d6bb1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront-keyvaluestore/2022-07-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-05-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-05-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cebb602 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-05-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-05-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-05-31/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-05-31/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-05-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-05-31/service-2.json.gz new file mode 100644 index 0000000..95280ff Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-05-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-05-31/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-05-31/waiters-2.json new file mode 100644 index 0000000..6e044bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-05-31/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 60, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-10-21/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-10-21/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cebb602 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-10-21/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-10-21/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-10-21/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-10-21/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-10-21/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-10-21/service-2.json.gz new file mode 100644 index 0000000..1712142 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-10-21/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-10-21/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-10-21/waiters-2.json new file mode 100644 index 0000000..6e044bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-10-21/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 60, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-11-06/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-11-06/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cebb602 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-11-06/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-11-06/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-11-06/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-11-06/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-11-06/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-11-06/service-2.json.gz new file mode 100644 index 0000000..97d2efb Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-11-06/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-11-06/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-11-06/waiters-2.json new file mode 100644 index 0000000..6e044bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2014-11-06/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 60, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-04-17/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-04-17/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cebb602 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-04-17/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-04-17/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-04-17/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-04-17/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-04-17/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-04-17/service-2.json.gz new file mode 100644 index 0000000..b67a367 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-04-17/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-04-17/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-04-17/waiters-2.json new file mode 100644 index 0000000..6e044bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-04-17/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 60, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-07-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-07-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cebb602 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-07-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-07-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-07-27/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-07-27/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-07-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-07-27/service-2.json.gz new file mode 100644 index 0000000..cf3a215 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-07-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-07-27/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-07-27/waiters-2.json new file mode 100644 index 0000000..6e044bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-07-27/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 60, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-09-17/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-09-17/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cebb602 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-09-17/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-09-17/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-09-17/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-09-17/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-09-17/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-09-17/service-2.json.gz new file mode 100644 index 0000000..8efa51f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-09-17/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-09-17/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-09-17/waiters-2.json new file mode 100644 index 0000000..6e044bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2015-09-17/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 60, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-13/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-13/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cebb602 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-13/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-13/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-13/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-13/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-13/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-13/service-2.json.gz new file mode 100644 index 0000000..e28a457 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-13/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-13/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-13/waiters-2.json new file mode 100644 index 0000000..6e044bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-13/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 60, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dd368ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/service-2.json.gz new file mode 100644 index 0000000..8606a9f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/waiters-2.json new file mode 100644 index 0000000..6e044bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-01-28/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 60, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dd368ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/service-2.json.gz new file mode 100644 index 0000000..a949c14 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/waiters-2.json new file mode 100644 index 0000000..6e044bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-01/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 60, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-20/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-20/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dd368ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-20/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-20/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-20/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-20/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-20/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-20/service-2.json.gz new file mode 100644 index 0000000..2f1703b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-20/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-20/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-20/waiters-2.json new file mode 100644 index 0000000..6e044bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-08-20/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 60, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dd368ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/service-2.json.gz new file mode 100644 index 0000000..78db587 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/waiters-2.json new file mode 100644 index 0000000..6e044bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-07/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 60, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dd368ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/service-2.json.gz new file mode 100644 index 0000000..e1a68a9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/waiters-2.json new file mode 100644 index 0000000..6e044bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-09-29/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 60, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dd368ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/service-2.json.gz new file mode 100644 index 0000000..6a3e3bf Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/waiters-2.json new file mode 100644 index 0000000..6e044bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2016-11-25/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 60, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-03-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-03-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dd368ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-03-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-03-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-03-25/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-03-25/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-03-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-03-25/service-2.json.gz new file mode 100644 index 0000000..5b0f065 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-03-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-03-25/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-03-25/waiters-2.json new file mode 100644 index 0000000..edd74b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-03-25/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 30, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dd368ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/service-2.json.gz new file mode 100644 index 0000000..2b3e186 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/waiters-2.json new file mode 100644 index 0000000..edd74b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2017-10-30/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 30, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dd368ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/service-2.json.gz new file mode 100644 index 0000000..6c4c078 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/waiters-2.json new file mode 100644 index 0000000..edd74b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-06-18/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 30, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dd368ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/service-2.json.gz new file mode 100644 index 0000000..e782ff1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/waiters-2.json new file mode 100644 index 0000000..edd74b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2018-11-05/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 25, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 30, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dd368ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/paginators-1.json new file mode 100644 index 0000000..51fbb90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/service-2.json.gz new file mode 100644 index 0000000..ba15e3e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/waiters-2.json new file mode 100644 index 0000000..95f0a2d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2019-03-26/waiters-2.json @@ -0,0 +1,47 @@ +{ + "version": 2, + "waiters": { + "DistributionDeployed": { + "delay": 60, + "operation": "GetDistribution", + "maxAttempts": 35, + "description": "Wait until a distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "Distribution.Status" + } + ] + }, + "InvalidationCompleted": { + "delay": 20, + "operation": "GetInvalidation", + "maxAttempts": 30, + "description": "Wait until an invalidation has completed.", + "acceptors": [ + { + "expected": "Completed", + "matcher": "path", + "state": "success", + "argument": "Invalidation.Status" + } + ] + }, + "StreamingDistributionDeployed": { + "delay": 60, + "operation": "GetStreamingDistribution", + "maxAttempts": 25, + "description": "Wait until a streaming distribution is deployed.", + "acceptors": [ + { + "expected": "Deployed", + "matcher": "path", + "state": "success", + "argument": "StreamingDistribution.Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..35efa1f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/paginators-1.json new file mode 100644 index 0000000..f956149 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/paginators-1.json @@ -0,0 +1,110 @@ +{ + "pagination": { + "ListCloudFrontOriginAccessIdentities": { + "input_token": "Marker", + "output_token": "CloudFrontOriginAccessIdentityList.NextMarker", + "limit_key": "MaxItems", + "more_results": "CloudFrontOriginAccessIdentityList.IsTruncated", + "result_key": "CloudFrontOriginAccessIdentityList.Items" + }, + "ListDistributions": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "DistributionList.IsTruncated", + "result_key": "DistributionList.Items" + }, + "ListInvalidations": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "more_results": "InvalidationList.IsTruncated", + "result_key": "InvalidationList.Items" + }, + "ListStreamingDistributions": { + "input_token": "Marker", + "output_token": "StreamingDistributionList.NextMarker", + "limit_key": "MaxItems", + "more_results": "StreamingDistributionList.IsTruncated", + "result_key": "StreamingDistributionList.Items" + }, + "ListKeyValueStores": { + "input_token": "Marker", + "limit_key": "MaxItems", + "output_token": "KeyValueStoreList.NextMarker", + "result_key": "KeyValueStoreList.Items" + }, + "ListPublicKeys": { + "input_token": "Marker", + "output_token": "PublicKeyList.NextMarker", + "limit_key": "MaxItems", + "result_key": "PublicKeyList.Items" + }, + "ListConnectionGroups": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "ConnectionGroups" + }, + "ListDistributionTenants": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "DistributionTenantList" + }, + "ListDistributionTenantsByCustomization": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "DistributionTenantList" + }, + "ListDistributionsByConnectionMode": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "result_key": "DistributionList.Items" + }, + "ListDomainConflicts": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "DomainConflicts" + }, + "ListInvalidationsForDistributionTenant": { + "input_token": "Marker", + "output_token": "InvalidationList.NextMarker", + "limit_key": "MaxItems", + "result_key": "InvalidationList.Items" + }, + "ListOriginAccessControls": { + "input_token": "Marker", + "output_token": "OriginAccessControlList.NextMarker", + "limit_key": "MaxItems", + "result_key": "OriginAccessControlList.Items" + }, + "ListConnectionFunctions": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "ConnectionFunctions" + }, + "ListDistributionsByConnectionFunction": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "result_key": "DistributionList.Items" + }, + "ListDistributionsByTrustStore": { + "input_token": "Marker", + "output_token": "DistributionList.NextMarker", + "limit_key": "MaxItems", + "result_key": "DistributionList.Items" + }, + "ListTrustStores": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "TrustStoreList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/service-2.json.gz new file mode 100644 index 0000000..5b04a94 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/waiters-2.json new file mode 100644 index 0000000..1e2c625 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudfront/2020-05-31/waiters-2.json @@ -0,0 +1,53 @@ +{ + "version" : 2, + "waiters" : { + "DistributionDeployed" : { + "description" : "Wait until a distribution is deployed.", + "delay" : 60, + "maxAttempts" : 35, + "operation" : "GetDistribution", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Distribution.Status", + "state" : "success", + "expected" : "Deployed" + } ] + }, + "InvalidationCompleted" : { + "description" : "Wait until an invalidation has completed.", + "delay" : 20, + "maxAttempts" : 30, + "operation" : "GetInvalidation", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Invalidation.Status", + "state" : "success", + "expected" : "Completed" + } ] + }, + "InvalidationForDistributionTenantCompleted" : { + "description" : "Wait until an invalidation for distribution tenant has completed.", + "delay" : 20, + "maxAttempts" : 30, + "operation" : "GetInvalidationForDistributionTenant", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Invalidation.Status", + "state" : "success", + "expected" : "Completed" + } ] + }, + "StreamingDistributionDeployed" : { + "description" : "Wait until a streaming distribution is deployed.", + "delay" : 60, + "maxAttempts" : 25, + "operation" : "GetStreamingDistribution", + "acceptors" : [ { + "matcher" : "path", + "argument" : "StreamingDistribution.Status", + "state" : "success", + "expected" : "Deployed" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudhsm/2014-05-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudhsm/2014-05-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1c4838b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudhsm/2014-05-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudhsm/2014-05-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudhsm/2014-05-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudhsm/2014-05-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudhsm/2014-05-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudhsm/2014-05-30/paginators-1.json new file mode 100644 index 0000000..3dedddf --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudhsm/2014-05-30/paginators-1.json @@ -0,0 +1,19 @@ +{ + "pagination": { + "ListHapgs": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "HapgList" + }, + "ListHsms": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "HsmList" + }, + "ListLunaClients": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ClientList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudhsm/2014-05-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudhsm/2014-05-30/service-2.json.gz new file mode 100644 index 0000000..d704fe2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudhsm/2014-05-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudhsmv2/2017-04-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudhsmv2/2017-04-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3a1f90a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudhsmv2/2017-04-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudhsmv2/2017-04-28/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudhsmv2/2017-04-28/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudhsmv2/2017-04-28/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudhsmv2/2017-04-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudhsmv2/2017-04-28/paginators-1.json new file mode 100644 index 0000000..19c403f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudhsmv2/2017-04-28/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "DescribeBackups": { + "result_key": "Backups", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "DescribeClusters": { + "result_key": "Clusters", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListTags": { + "result_key": "TagList", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudhsmv2/2017-04-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudhsmv2/2017-04-28/service-2.json.gz new file mode 100644 index 0000000..97acc59 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudhsmv2/2017-04-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2011-02-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2011-02-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..309cfb1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2011-02-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2011-02-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2011-02-01/service-2.json.gz new file mode 100644 index 0000000..8d13311 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2011-02-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2013-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2013-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ebf2ebe Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2013-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2013-01-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2013-01-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2013-01-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2013-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2013-01-01/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2013-01-01/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2013-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2013-01-01/service-2.json.gz new file mode 100644 index 0000000..f259080 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudsearch/2013-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudsearchdomain/2013-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudsearchdomain/2013-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..285358e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudsearchdomain/2013-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudsearchdomain/2013-01-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudsearchdomain/2013-01-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudsearchdomain/2013-01-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudsearchdomain/2013-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudsearchdomain/2013-01-01/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudsearchdomain/2013-01-01/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudsearchdomain/2013-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudsearchdomain/2013-01-01/service-2.json.gz new file mode 100644 index 0000000..6cadff4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudsearchdomain/2013-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudtrail-data/2021-08-11/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail-data/2021-08-11/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d175095 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail-data/2021-08-11/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudtrail-data/2021-08-11/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail-data/2021-08-11/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail-data/2021-08-11/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudtrail-data/2021-08-11/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail-data/2021-08-11/service-2.json.gz new file mode 100644 index 0000000..2e42798 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail-data/2021-08-11/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudtrail/2013-11-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail/2013-11-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..acb1ad4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail/2013-11-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudtrail/2013-11-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail/2013-11-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail/2013-11-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudtrail/2013-11-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail/2013-11-01/paginators-1.json new file mode 100644 index 0000000..1e17b41 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail/2013-11-01/paginators-1.json @@ -0,0 +1,43 @@ +{ + "pagination": { + "LookupEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Events" + }, + "ListPublicKeys": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "PublicKeyList" + }, + "ListTags": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ResourceTagList" + }, + "ListTrails": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Trails" + }, + "ListImportFailures": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Failures" + }, + "ListImports": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Imports" + }, + "ListInsightsData": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Events" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudtrail/2013-11-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail/2013-11-01/service-2.json.gz new file mode 100644 index 0000000..6b6c625 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudtrail/2013-11-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..2dbcd8d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/paginators-1.json new file mode 100644 index 0000000..1725d52 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/paginators-1.json @@ -0,0 +1,54 @@ +{ + "pagination": { + "DescribeAlarmHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxRecords", + "result_key": "AlarmHistoryItems" + }, + "DescribeAlarms": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxRecords", + "result_key": [ + "MetricAlarms", + "CompositeAlarms", + "LogAlarms" + ] + }, + "ListDashboards": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "DashboardEntries" + }, + "ListMetrics": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": [ + "Metrics", + "OwningAccounts" + ] + }, + "GetMetricData": { + "input_token": "NextToken", + "limit_key": "MaxDatapoints", + "output_token": "NextToken", + "result_key": [ + "MetricDataResults", + "Messages" + ] + }, + "DescribeAnomalyDetectors": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AnomalyDetectors" + }, + "ListAlarmMuteRules": { + "input_token": "NextToken", + "limit_key": "MaxRecords", + "output_token": "NextToken", + "result_key": "AlarmMuteRuleSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/service-2.json.gz new file mode 100644 index 0000000..e6ed1ea Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/waiters-2.json new file mode 100644 index 0000000..32ceb8c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudwatch/2010-08-01/waiters-2.json @@ -0,0 +1,61 @@ +{ + "version": 2, + "waiters": { + "AlarmExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeAlarms", + "acceptors": [ + { + "matcher": "path", + "expected": true, + "argument": "length(MetricAlarms[]) > `0`", + "state": "success" + } + ] + }, + "AlarmMuteRuleExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "GetAlarmMuteRule", + "acceptors": [ + { + "matcher": "status", + "expected": 200, + "state": "success" + }, + { + "matcher": "status", + "expected": 404, + "state": "retry" + } + ] + }, + "CompositeAlarmExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeAlarms", + "acceptors": [ + { + "matcher": "path", + "expected": true, + "argument": "length(CompositeAlarms[]) > `0`", + "state": "success" + } + ] + }, + "LogAlarmExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeAlarms", + "acceptors": [ + { + "matcher": "path", + "expected": true, + "argument": "length(LogAlarms[]) > `0`", + "state": "success" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudwatchomni/2025-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudwatchomni/2025-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..35630af Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudwatchomni/2025-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudwatchomni/2025-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cloudwatchomni/2025-01-01/paginators-1.json new file mode 100644 index 0000000..d676489 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cloudwatchomni/2025-01-01/paginators-1.json @@ -0,0 +1,97 @@ +{ + "pagination": { + "GetContextGraph": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "nodes" + }, + "GetTelemetryQueryResults": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "rows", + "non_aggregate_keys": [ + "statistics", + "status" + ] + }, + "ListAccessGrants": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAccessProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAlerts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDomainAccessGrantsForOrganization": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDomains": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListIntegrations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListOmniDashboards": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListSpaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListSpacesForOrganization": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListTelemetryFields": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "fields" + }, + "ListTelemetryQuerySessions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sessions" + }, + "ListViews": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "SearchPrincipals": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "results" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cloudwatchomni/2025-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cloudwatchomni/2025-01-01/service-2.json.gz new file mode 100644 index 0000000..69f0953 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cloudwatchomni/2025-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..68bfc35 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/paginators-1.json new file mode 100644 index 0000000..ad47997 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "ListDomains": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "domains" + }, + "ListPackageVersionAssets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assets" + }, + "ListPackageVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "versions" + }, + "ListPackages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "packages" + }, + "ListRepositories": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "repositories" + }, + "ListRepositoriesInDomain": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "repositories" + }, + "ListAllowedRepositoriesForGroup": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "allowedRepositories" + }, + "ListAssociatedPackages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "packages" + }, + "ListPackageGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "packageGroups" + }, + "ListSubPackageGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "packageGroups" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/paginators-1.sdk-extras.json new file mode 100644 index 0000000..d58fb3f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/paginators-1.sdk-extras.json @@ -0,0 +1,24 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListPackageVersionAssets": { + "non_aggregate_keys": [ + "package", + "format", + "namespace", + "version", + "versionRevision" + ] + }, + "ListPackageVersions": { + "non_aggregate_keys": [ + "defaultDisplayVersion", + "format", + "package", + "namespace" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/service-2.json.gz new file mode 100644 index 0000000..26fa0f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codeartifact/2018-09-22/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codebuild/2016-10-06/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codebuild/2016-10-06/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..e021ce6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codebuild/2016-10-06/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codebuild/2016-10-06/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/codebuild/2016-10-06/examples-1.json new file mode 100644 index 0000000..a5fb660 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codebuild/2016-10-06/examples-1.json @@ -0,0 +1,281 @@ +{ + "version": "1.0", + "examples": { + "BatchGetBuilds": [ + { + "input": { + "ids": [ + "codebuild-demo-project:9b0ac37f-d19e-4254-9079-f47e9a389eEX", + "codebuild-demo-project:b79a46f7-1473-4636-a23f-da9c45c208EX" + ] + }, + "output": { + "builds": [ + { + "arn": "arn:aws:codebuild:us-east-1:123456789012:build/codebuild-demo-project:9b0ac37f-d19e-4254-9079-f47e9a389eEX", + "artifacts": { + "location": "arn:aws:s3:::codebuild-123456789012-output-bucket/codebuild-demo-project" + }, + "buildComplete": true, + "buildStatus": "SUCCEEDED", + "currentPhase": "COMPLETED", + "endTime": 1479832474.764, + "environment": { + "type": "LINUX_CONTAINER", + "computeType": "BUILD_GENERAL1_SMALL", + "environmentVariables": [ + + ], + "image": "aws/codebuild/java:openjdk-8", + "privilegedMode": false + }, + "id": "codebuild-demo-project:9b0ac37f-d19e-4254-9079-f47e9a389eEX", + "initiator": "MyDemoUser", + "logs": { + "deepLink": "https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logEvent:group=/aws/codebuild/codebuild-demo-project;stream=9b0ac37f-d19e-4254-9079-f47e9a389eEX", + "groupName": "/aws/codebuild/codebuild-demo-project", + "streamName": "9b0ac37f-d19e-4254-9079-f47e9a389eEX" + }, + "phases": [ + { + "durationInSeconds": 0, + "endTime": 1479832342.23, + "phaseStatus": "SUCCEEDED", + "phaseType": "SUBMITTED", + "startTime": 1479832341.854 + }, + { + "contexts": [ + + ], + "durationInSeconds": 72, + "endTime": 1479832415.064, + "phaseStatus": "SUCCEEDED", + "phaseType": "PROVISIONING", + "startTime": 1479832342.23 + }, + { + "contexts": [ + + ], + "durationInSeconds": 46, + "endTime": 1479832461.261, + "phaseStatus": "SUCCEEDED", + "phaseType": "DOWNLOAD_SOURCE", + "startTime": 1479832415.064 + }, + { + "contexts": [ + + ], + "durationInSeconds": 0, + "endTime": 1479832461.354, + "phaseStatus": "SUCCEEDED", + "phaseType": "INSTALL", + "startTime": 1479832461.261 + }, + { + "contexts": [ + + ], + "durationInSeconds": 0, + "endTime": 1479832461.448, + "phaseStatus": "SUCCEEDED", + "phaseType": "PRE_BUILD", + "startTime": 1479832461.354 + }, + { + "contexts": [ + + ], + "durationInSeconds": 9, + "endTime": 1479832471.115, + "phaseStatus": "SUCCEEDED", + "phaseType": "BUILD", + "startTime": 1479832461.448 + }, + { + "contexts": [ + + ], + "durationInSeconds": 0, + "endTime": 1479832471.224, + "phaseStatus": "SUCCEEDED", + "phaseType": "POST_BUILD", + "startTime": 1479832471.115 + }, + { + "contexts": [ + + ], + "durationInSeconds": 0, + "endTime": 1479832471.791, + "phaseStatus": "SUCCEEDED", + "phaseType": "UPLOAD_ARTIFACTS", + "startTime": 1479832471.224 + }, + { + "contexts": [ + + ], + "durationInSeconds": 2, + "endTime": 1479832474.764, + "phaseStatus": "SUCCEEDED", + "phaseType": "FINALIZING", + "startTime": 1479832471.791 + }, + { + "phaseType": "COMPLETED", + "startTime": 1479832474.764 + } + ], + "projectName": "codebuild-demo-project", + "source": { + "type": "S3", + "buildspec": "", + "location": "arn:aws:s3:::codebuild-123456789012-input-bucket/MessageUtil.zip" + }, + "startTime": 1479832341.854, + "timeoutInMinutes": 60 + }, + { + "arn": "arn:aws:codebuild:us-east-1:123456789012:build/codebuild-demo-project:b79a46f7-1473-4636-a23f-da9c45c208EX", + "artifacts": { + "location": "arn:aws:s3:::codebuild-123456789012-output-bucket/codebuild-demo-project" + }, + "buildComplete": true, + "buildStatus": "SUCCEEDED", + "currentPhase": "COMPLETED", + "endTime": 1479401214.239, + "environment": { + "type": "LINUX_CONTAINER", + "computeType": "BUILD_GENERAL1_SMALL", + "environmentVariables": [ + + ], + "image": "aws/codebuild/java:openjdk-8", + "privilegedMode": false + }, + "id": "codebuild-demo-project:b79a46f7-1473-4636-a23f-da9c45c208EX", + "initiator": "MyDemoUser", + "logs": { + "deepLink": "https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logEvent:group=/aws/codebuild/codebuild-demo-project;stream=b79a46f7-1473-4636-a23f-da9c45c208EX", + "groupName": "/aws/codebuild/codebuild-demo-project", + "streamName": "b79a46f7-1473-4636-a23f-da9c45c208EX" + }, + "phases": [ + { + "durationInSeconds": 0, + "endTime": 1479401082.342, + "phaseStatus": "SUCCEEDED", + "phaseType": "SUBMITTED", + "startTime": 1479401081.869 + }, + { + "contexts": [ + + ], + "durationInSeconds": 71, + "endTime": 1479401154.129, + "phaseStatus": "SUCCEEDED", + "phaseType": "PROVISIONING", + "startTime": 1479401082.342 + }, + { + "contexts": [ + + ], + "durationInSeconds": 45, + "endTime": 1479401199.136, + "phaseStatus": "SUCCEEDED", + "phaseType": "DOWNLOAD_SOURCE", + "startTime": 1479401154.129 + }, + { + "contexts": [ + + ], + "durationInSeconds": 0, + "endTime": 1479401199.236, + "phaseStatus": "SUCCEEDED", + "phaseType": "INSTALL", + "startTime": 1479401199.136 + }, + { + "contexts": [ + + ], + "durationInSeconds": 0, + "endTime": 1479401199.345, + "phaseStatus": "SUCCEEDED", + "phaseType": "PRE_BUILD", + "startTime": 1479401199.236 + }, + { + "contexts": [ + + ], + "durationInSeconds": 9, + "endTime": 1479401208.68, + "phaseStatus": "SUCCEEDED", + "phaseType": "BUILD", + "startTime": 1479401199.345 + }, + { + "contexts": [ + + ], + "durationInSeconds": 0, + "endTime": 1479401208.783, + "phaseStatus": "SUCCEEDED", + "phaseType": "POST_BUILD", + "startTime": 1479401208.68 + }, + { + "contexts": [ + + ], + "durationInSeconds": 0, + "endTime": 1479401209.463, + "phaseStatus": "SUCCEEDED", + "phaseType": "UPLOAD_ARTIFACTS", + "startTime": 1479401208.783 + }, + { + "contexts": [ + + ], + "durationInSeconds": 4, + "endTime": 1479401214.239, + "phaseStatus": "SUCCEEDED", + "phaseType": "FINALIZING", + "startTime": 1479401209.463 + }, + { + "phaseType": "COMPLETED", + "startTime": 1479401214.239 + } + ], + "projectName": "codebuild-demo-project", + "source": { + "type": "S3", + "location": "arn:aws:s3:::codebuild-123456789012-input-bucket/MessageUtil.zip" + }, + "startTime": 1479401081.869, + "timeoutInMinutes": 60 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example gets information about builds with the specified build IDs.", + "id": "to-get-information-about-builds-1501187184588", + "title": "To get information about builds" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codebuild/2016-10-06/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/codebuild/2016-10-06/paginators-1.json new file mode 100644 index 0000000..b482a96 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codebuild/2016-10-06/paginators-1.json @@ -0,0 +1,91 @@ +{ + "pagination": { + "ListBuilds": { + "output_token": "nextToken", + "input_token": "nextToken", + "result_key": "ids" + }, + "ListProjects": { + "output_token": "nextToken", + "input_token": "nextToken", + "result_key": "projects" + }, + "ListBuildsForProject": { + "output_token": "nextToken", + "input_token": "nextToken", + "result_key": "ids" + }, + "DescribeTestCases": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "testCases" + }, + "ListReportGroups": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "reportGroups" + }, + "ListReports": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "reports" + }, + "ListReportsForReportGroup": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "reports" + }, + "ListSharedProjects": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "projects" + }, + "ListSharedReportGroups": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "reportGroups" + }, + "DescribeCodeCoverages": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "codeCoverages" + }, + "ListBuildBatches": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "ids" + }, + "ListBuildBatchesForProject": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "ids" + }, + "ListCommandExecutionsForSandbox": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "commandExecutions" + }, + "ListSandboxes": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "ids" + }, + "ListSandboxesForProject": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "ids" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codebuild/2016-10-06/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codebuild/2016-10-06/service-2.json.gz new file mode 100644 index 0000000..90be077 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codebuild/2016-10-06/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codecatalyst/2022-09-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codecatalyst/2022-09-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8138554 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codecatalyst/2022-09-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codecatalyst/2022-09-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/codecatalyst/2022-09-28/paginators-1.json new file mode 100644 index 0000000..15ed87f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codecatalyst/2022-09-28/paginators-1.json @@ -0,0 +1,63 @@ +{ + "pagination": { + "ListAccessTokens": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDevEnvironments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListEventLogs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListProjects": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListSourceRepositories": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListSourceRepositoryBranches": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListSpaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "items" + }, + "ListDevEnvironmentSessions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListWorkflowRuns": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListWorkflows": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codecatalyst/2022-09-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codecatalyst/2022-09-28/service-2.json.gz new file mode 100644 index 0000000..4085444 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codecatalyst/2022-09-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codecatalyst/2022-09-28/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/codecatalyst/2022-09-28/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codecatalyst/2022-09-28/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codecommit/2015-04-13/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codecommit/2015-04-13/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..760e1ac Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codecommit/2015-04-13/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codecommit/2015-04-13/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/codecommit/2015-04-13/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codecommit/2015-04-13/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codecommit/2015-04-13/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/codecommit/2015-04-13/paginators-1.json new file mode 100644 index 0000000..942c128 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codecommit/2015-04-13/paginators-1.json @@ -0,0 +1,55 @@ +{ + "pagination": { + "ListBranches": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "branches" + }, + "ListRepositories": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "repositories" + }, + "GetCommentsForComparedCommit": { + "result_key": "commentsForComparedCommitData", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "DescribePullRequestEvents": { + "result_key": "pullRequestEvents", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "GetCommentsForPullRequest": { + "result_key": "commentsForPullRequestData", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "ListPullRequests": { + "result_key": "pullRequestIds", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "GetDifferences": { + "result_key": "differences", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetBlobDifferences": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "isBinary", + "beforeBlobSize", + "afterBlobSize" + ], + "output_token": "NextToken", + "result_key": "hunks" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codecommit/2015-04-13/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codecommit/2015-04-13/service-2.json.gz new file mode 100644 index 0000000..bafda10 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codecommit/2015-04-13/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeconnections/2023-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codeconnections/2023-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..34d67d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codeconnections/2023-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeconnections/2023-12-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/codeconnections/2023-12-01/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codeconnections/2023-12-01/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeconnections/2023-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codeconnections/2023-12-01/service-2.json.gz new file mode 100644 index 0000000..07b34fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codeconnections/2023-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cc872ac Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/paginators-1.json new file mode 100644 index 0000000..aae3fad --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/paginators-1.json @@ -0,0 +1,49 @@ +{ + "pagination": { + "ListApplicationRevisions": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "revisions" + }, + "ListApplications": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "applications" + }, + "ListDeploymentConfigs": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "deploymentConfigsList" + }, + "ListDeploymentGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "deploymentGroups" + }, + "ListDeploymentInstances": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "instancesList" + }, + "ListDeployments": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "deployments" + }, + "ListDeploymentTargets": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "targetIds" + }, + "ListGitHubAccountTokenNames": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "tokenNameList" + }, + "ListOnPremisesInstances": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "instanceNames" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/service-2.json.gz new file mode 100644 index 0000000..2afe3f9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/waiters-2.json new file mode 100644 index 0000000..0fea4fa --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codedeploy/2014-10-06/waiters-2.json @@ -0,0 +1,30 @@ +{ + "version": 2, + "waiters": { + "DeploymentSuccessful": { + "delay": 15, + "operation": "GetDeployment", + "maxAttempts": 120, + "acceptors": [ + { + "expected": "Succeeded", + "matcher": "path", + "state": "success", + "argument": "deploymentInfo.status" + }, + { + "expected": "Failed", + "matcher": "path", + "state": "failure", + "argument": "deploymentInfo.status" + }, + { + "expected": "Stopped", + "matcher": "path", + "state": "failure", + "argument": "deploymentInfo.status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a2885c5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/paginators-1.json new file mode 100644 index 0000000..bbc1f58 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListRepositoryAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RepositoryAssociationSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/service-2.json.gz new file mode 100644 index 0000000..6240446 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/waiters-2.json new file mode 100644 index 0000000..cd1730f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codeguru-reviewer/2019-09-19/waiters-2.json @@ -0,0 +1,58 @@ +{ + "version": 2, + "waiters": + { + "RepositoryAssociationSucceeded": + { + "description": "Wait until a repository association is complete.", + "operation": "DescribeRepositoryAssociation", + "delay": 10, + "maxAttempts": 30, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "RepositoryAssociation.State", + "expected": "Associated" + }, + { + "state": "failure", + "matcher": "path", + "argument": "RepositoryAssociation.State", + "expected": "Failed" + }, + { + "state": "retry", + "matcher": "path", + "argument": "RepositoryAssociation.State", + "expected": "Associating" + }] + }, + "CodeReviewCompleted": + { + "description": "Wait until a code review is complete.", + "operation": "DescribeCodeReview", + "delay": 10, + "maxAttempts": 180, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "CodeReview.State", + "expected": "Completed" + }, + { + "state": "failure", + "matcher": "path", + "argument": "CodeReview.State", + "expected": "Failed" + }, + { + "state": "retry", + "matcher": "path", + "argument": "CodeReview.State", + "expected": "Pending" + }] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeguru-security/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codeguru-security/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3e31d1f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codeguru-security/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeguru-security/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/codeguru-security/2018-05-10/paginators-1.json new file mode 100644 index 0000000..03e1cbf --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codeguru-security/2018-05-10/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "GetFindings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findings" + }, + "ListFindingsMetrics": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findingsMetrics" + }, + "ListScans": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "summaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeguru-security/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codeguru-security/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..5612815 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codeguru-security/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeguru-security/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/codeguru-security/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codeguru-security/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeguruprofiler/2019-07-18/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codeguruprofiler/2019-07-18/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f054a7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codeguruprofiler/2019-07-18/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeguruprofiler/2019-07-18/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/codeguruprofiler/2019-07-18/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codeguruprofiler/2019-07-18/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeguruprofiler/2019-07-18/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/codeguruprofiler/2019-07-18/paginators-1.json new file mode 100644 index 0000000..c787d76 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codeguruprofiler/2019-07-18/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListProfileTimes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "profileTimes" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codeguruprofiler/2019-07-18/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codeguruprofiler/2019-07-18/service-2.json.gz new file mode 100644 index 0000000..4fdd275 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codeguruprofiler/2019-07-18/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codepipeline/2015-07-09/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codepipeline/2015-07-09/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..86a9af7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codepipeline/2015-07-09/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codepipeline/2015-07-09/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/codepipeline/2015-07-09/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codepipeline/2015-07-09/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codepipeline/2015-07-09/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/codepipeline/2015-07-09/paginators-1.json new file mode 100644 index 0000000..32217ac --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codepipeline/2015-07-09/paginators-1.json @@ -0,0 +1,51 @@ +{ + "pagination": { + "ListActionTypes": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "actionTypes" + }, + "ListPipelineExecutions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "pipelineExecutionSummaries" + }, + "ListPipelines": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "pipelines", + "limit_key": "maxResults" + }, + "ListWebhooks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "webhooks" + }, + "ListActionExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "actionExecutionDetails" + }, + "ListTagsForResource": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tags" + }, + "ListRuleExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "ruleExecutionDetails" + }, + "ListDeployActionExecutionTargets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "targets" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codepipeline/2015-07-09/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codepipeline/2015-07-09/service-2.json.gz new file mode 100644 index 0000000..3decc31 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codepipeline/2015-07-09/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codestar-connections/2019-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codestar-connections/2019-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f3bcb73 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codestar-connections/2019-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codestar-connections/2019-12-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/codestar-connections/2019-12-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codestar-connections/2019-12-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codestar-connections/2019-12-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/codestar-connections/2019-12-01/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codestar-connections/2019-12-01/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codestar-connections/2019-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codestar-connections/2019-12-01/service-2.json.gz new file mode 100644 index 0000000..fadd688 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codestar-connections/2019-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codestar-notifications/2019-10-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codestar-notifications/2019-10-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c0ac99d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codestar-notifications/2019-10-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/codestar-notifications/2019-10-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/codestar-notifications/2019-10-15/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codestar-notifications/2019-10-15/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codestar-notifications/2019-10-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/codestar-notifications/2019-10-15/paginators-1.json new file mode 100644 index 0000000..9501814 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/codestar-notifications/2019-10-15/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListEventTypes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EventTypes" + }, + "ListNotificationRules": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "NotificationRules" + }, + "ListTargets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Targets" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/codestar-notifications/2019-10-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/codestar-notifications/2019-10-15/service-2.json.gz new file mode 100644 index 0000000..2c47126 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/codestar-notifications/2019-10-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cognito-identity/2014-06-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cognito-identity/2014-06-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f684139 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cognito-identity/2014-06-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cognito-identity/2014-06-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cognito-identity/2014-06-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cognito-identity/2014-06-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cognito-identity/2014-06-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cognito-identity/2014-06-30/paginators-1.json new file mode 100644 index 0000000..2af6e40 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cognito-identity/2014-06-30/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListIdentityPools": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IdentityPools" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cognito-identity/2014-06-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cognito-identity/2014-06-30/service-2.json.gz new file mode 100644 index 0000000..5bc31ac Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cognito-identity/2014-06-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cognito-idp/2016-04-18/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cognito-idp/2016-04-18/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..e07d862 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cognito-idp/2016-04-18/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cognito-idp/2016-04-18/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cognito-idp/2016-04-18/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cognito-idp/2016-04-18/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cognito-idp/2016-04-18/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cognito-idp/2016-04-18/paginators-1.json new file mode 100644 index 0000000..51b7c94 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cognito-idp/2016-04-18/paginators-1.json @@ -0,0 +1,58 @@ +{ + "pagination": { + "AdminListGroupsForUser": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "Groups" + }, + "AdminListUserAuthEvents": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AuthEvents" + }, + "ListGroups": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "Groups" + }, + "ListIdentityProviders": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Providers" + }, + "ListResourceServers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ResourceServers" + }, + "ListUserPoolClients": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "UserPoolClients" + }, + "ListUserPools": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "UserPools" + }, + "ListUsersInGroup": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "Users" + }, + "ListUsers": { + "input_token": "PaginationToken", + "limit_key": "Limit", + "output_token": "PaginationToken", + "result_key": "Users" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cognito-idp/2016-04-18/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cognito-idp/2016-04-18/service-2.json.gz new file mode 100644 index 0000000..1484b1a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cognito-idp/2016-04-18/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cognito-sync/2014-06-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cognito-sync/2014-06-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0c7b70c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cognito-sync/2014-06-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cognito-sync/2014-06-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cognito-sync/2014-06-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cognito-sync/2014-06-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cognito-sync/2014-06-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cognito-sync/2014-06-30/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cognito-sync/2014-06-30/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cognito-sync/2014-06-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cognito-sync/2014-06-30/service-2.json.gz new file mode 100644 index 0000000..eb8bd6a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cognito-sync/2014-06-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/comprehend/2017-11-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/comprehend/2017-11-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..522af1c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/comprehend/2017-11-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/comprehend/2017-11-27/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/comprehend/2017-11-27/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/comprehend/2017-11-27/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/comprehend/2017-11-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/comprehend/2017-11-27/paginators-1.json new file mode 100644 index 0000000..3a45818 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/comprehend/2017-11-27/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "ListTopicsDetectionJobs": { + "result_key": "TopicsDetectionJobPropertiesList", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListDocumentClassificationJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DocumentClassificationJobPropertiesList" + }, + "ListDocumentClassifiers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DocumentClassifierPropertiesList" + }, + "ListDominantLanguageDetectionJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DominantLanguageDetectionJobPropertiesList" + }, + "ListEntitiesDetectionJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EntitiesDetectionJobPropertiesList" + }, + "ListEntityRecognizers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EntityRecognizerPropertiesList" + }, + "ListKeyPhrasesDetectionJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "KeyPhrasesDetectionJobPropertiesList" + }, + "ListSentimentDetectionJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SentimentDetectionJobPropertiesList" + }, + "ListEndpoints": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EndpointPropertiesList" + }, + "ListPiiEntitiesDetectionJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PiiEntitiesDetectionJobPropertiesList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/comprehend/2017-11-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/comprehend/2017-11-27/service-2.json.gz new file mode 100644 index 0000000..9c08237 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/comprehend/2017-11-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/comprehendmedical/2018-10-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/comprehendmedical/2018-10-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0b5400c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/comprehendmedical/2018-10-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/comprehendmedical/2018-10-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/comprehendmedical/2018-10-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/comprehendmedical/2018-10-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/comprehendmedical/2018-10-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/comprehendmedical/2018-10-30/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/comprehendmedical/2018-10-30/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/comprehendmedical/2018-10-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/comprehendmedical/2018-10-30/service-2.json.gz new file mode 100644 index 0000000..899af25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/comprehendmedical/2018-10-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer-automation/2025-09-22/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer-automation/2025-09-22/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c947daf Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer-automation/2025-09-22/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer-automation/2025-09-22/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer-automation/2025-09-22/paginators-1.json new file mode 100644 index 0000000..93d1188 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer-automation/2025-09-22/paginators-1.json @@ -0,0 +1,58 @@ +{ + "pagination": { + "ListAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "accounts" + }, + "ListAutomationEventSteps": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "automationEventSteps" + }, + "ListAutomationEventSummaries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "automationEventSummaries" + }, + "ListAutomationEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "automationEvents" + }, + "ListAutomationRulePreview": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "previewResults" + }, + "ListAutomationRulePreviewSummaries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "previewResultSummaries" + }, + "ListAutomationRules": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "automationRules" + }, + "ListRecommendedActionSummaries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recommendedActionSummaries" + }, + "ListRecommendedActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recommendedActions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer-automation/2025-09-22/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer-automation/2025-09-22/service-2.json.gz new file mode 100644 index 0000000..c9f3d20 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer-automation/2025-09-22/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer-automation/2025-09-22/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer-automation/2025-09-22/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer-automation/2025-09-22/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer/2019-11-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer/2019-11-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a278b20 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer/2019-11-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer/2019-11-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer/2019-11-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer/2019-11-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer/2019-11-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer/2019-11-01/paginators-1.json new file mode 100644 index 0000000..1d115fc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer/2019-11-01/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "DescribeRecommendationExportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recommendationExportJobs" + }, + "GetEnrollmentStatusesForOrganization": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "accountEnrollmentStatuses" + }, + "GetLambdaFunctionRecommendations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "lambdaFunctionRecommendations" + }, + "GetRecommendationPreferences": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recommendationPreferencesDetails" + }, + "GetRecommendationSummaries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recommendationSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer/2019-11-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer/2019-11-01/service-2.json.gz new file mode 100644 index 0000000..f70090a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/compute-optimizer/2019-11-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/config/2014-11-12/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/config/2014-11-12/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f8d1746 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/config/2014-11-12/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/config/2014-11-12/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/config/2014-11-12/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/config/2014-11-12/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/config/2014-11-12/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/config/2014-11-12/paginators-1.json new file mode 100644 index 0000000..c44bb99 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/config/2014-11-12/paginators-1.json @@ -0,0 +1,213 @@ +{ + "pagination": { + "DescribeComplianceByConfigRule": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ComplianceByConfigRules" + }, + "DescribeComplianceByResource": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ComplianceByResources", + "limit_key": "Limit" + }, + "DescribeConfigRules": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ConfigRules" + }, + "GetComplianceDetailsByConfigRule": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "EvaluationResults", + "limit_key": "Limit" + }, + "GetComplianceDetailsByResource": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "EvaluationResults" + }, + "GetResourceConfigHistory": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "configurationItems", + "limit_key": "limit" + }, + "ListDiscoveredResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "resourceIdentifiers", + "limit_key": "limit" + }, + "DescribeAggregateComplianceByConfigRules": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "AggregateComplianceByConfigRules" + }, + "DescribeAggregationAuthorizations": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "AggregationAuthorizations" + }, + "DescribeConfigRuleEvaluationStatus": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "ConfigRulesEvaluationStatus" + }, + "DescribeConfigurationAggregatorSourcesStatus": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "AggregatedSourceStatusList" + }, + "DescribeConfigurationAggregators": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "ConfigurationAggregators" + }, + "DescribePendingAggregationRequests": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "PendingAggregationRequests" + }, + "DescribeRetentionConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "RetentionConfigurations" + }, + "GetAggregateComplianceDetailsByConfigRule": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "AggregateEvaluationResults" + }, + "ListAggregateDiscoveredResources": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "ResourceIdentifiers" + }, + "DescribeRemediationExecutionStatus": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "RemediationExecutionStatuses" + }, + "DescribeAggregateComplianceByConformancePacks": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "AggregateComplianceByConformancePacks" + }, + "DescribeConformancePackStatus": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "ConformancePackStatusDetails" + }, + "DescribeConformancePacks": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "ConformancePackDetails" + }, + "DescribeOrganizationConfigRuleStatuses": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "OrganizationConfigRuleStatuses" + }, + "DescribeOrganizationConfigRules": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "OrganizationConfigRules" + }, + "DescribeOrganizationConformancePackStatuses": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "OrganizationConformancePackStatuses" + }, + "DescribeOrganizationConformancePacks": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "OrganizationConformancePacks" + }, + "GetConformancePackComplianceSummary": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "ConformancePackComplianceSummaryList" + }, + "GetOrganizationConfigRuleDetailedStatus": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "OrganizationConfigRuleDetailedStatus" + }, + "GetOrganizationConformancePackDetailedStatus": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "OrganizationConformancePackDetailedStatuses" + }, + "ListTagsForResource": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "Tags" + }, + "SelectAggregateResourceConfig": { + "input_token": "NextToken", + "limit_key": "Limit", + "non_aggregate_keys": [ + "QueryInfo" + ], + "output_token": "NextToken", + "result_key": "Results" + }, + "SelectResourceConfig": { + "input_token": "NextToken", + "limit_key": "Limit", + "non_aggregate_keys": [ + "QueryInfo" + ], + "output_token": "NextToken", + "result_key": "Results" + }, + "ListResourceEvaluations": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "ResourceEvaluations" + }, + "ListConfigurationRecorders": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ConfigurationRecorderSummaries" + }, + "DescribeConformancePackCompliance": { + "input_token": "NextToken", + "limit_key": "Limit", + "non_aggregate_keys": [ + "ConformancePackName" + ], + "output_token": "NextToken", + "result_key": "ConformancePackRuleComplianceList" + }, + "ListConnectors": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ConnectorSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/config/2014-11-12/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/config/2014-11-12/service-2.json.gz new file mode 100644 index 0000000..54e70e5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/config/2014-11-12/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connect-contact-lens/2020-08-21/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connect-contact-lens/2020-08-21/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..249d6a4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connect-contact-lens/2020-08-21/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connect-contact-lens/2020-08-21/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/connect-contact-lens/2020-08-21/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/connect-contact-lens/2020-08-21/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/connect-contact-lens/2020-08-21/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/connect-contact-lens/2020-08-21/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/connect-contact-lens/2020-08-21/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/connect-contact-lens/2020-08-21/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connect-contact-lens/2020-08-21/service-2.json.gz new file mode 100644 index 0000000..3dcef62 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connect-contact-lens/2020-08-21/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connect/2017-08-08/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connect/2017-08-08/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c6919f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connect/2017-08-08/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connect/2017-08-08/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/connect/2017-08-08/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/connect/2017-08-08/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/connect/2017-08-08/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/connect/2017-08-08/paginators-1.json new file mode 100644 index 0000000..41da626 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/connect/2017-08-08/paginators-1.json @@ -0,0 +1,627 @@ +{ + "pagination": { + "GetMetricData": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MetricResults" + }, + "ListRoutingProfiles": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "RoutingProfileSummaryList" + }, + "ListSecurityProfiles": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SecurityProfileSummaryList" + }, + "ListUserHierarchyGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "UserHierarchyGroupSummaryList" + }, + "ListUsers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "UserSummaryList" + }, + "ListContactFlows": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ContactFlowSummaryList" + }, + "ListHoursOfOperations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "HoursOfOperationSummaryList" + }, + "ListPhoneNumbers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PhoneNumberSummaryList" + }, + "ListQueues": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "QueueSummaryList" + }, + "ListPrompts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PromptSummaryList" + }, + "ListRoutingProfileQueues": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "RoutingProfileQueueConfigSummaryList", + "non_aggregate_keys": [ + "LastModifiedRegion", + "LastModifiedTime" + ] + }, + "ListApprovedOrigins": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Origins" + }, + "ListInstanceAttributes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Attributes" + }, + "ListInstanceStorageConfigs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "StorageConfigs" + }, + "ListInstances": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InstanceSummaryList" + }, + "ListLambdaFunctions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LambdaFunctions" + }, + "ListLexBots": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LexBots" + }, + "ListSecurityKeys": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SecurityKeys" + }, + "ListIntegrationAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IntegrationAssociationSummaryList" + }, + "ListUseCases": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "UseCaseSummaryList" + }, + "ListQuickConnects": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "QuickConnectSummaryList" + }, + "ListQueueQuickConnects": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "QuickConnectSummaryList", + "non_aggregate_keys": [ + "LastModifiedRegion", + "LastModifiedTime" + ] + }, + "ListBots": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LexBots" + }, + "ListAgentStatuses": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AgentStatusSummaryList" + }, + "ListSecurityProfilePermissions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Permissions", + "non_aggregate_keys": [ + "LastModifiedRegion", + "LastModifiedTime" + ] + }, + "ListContactReferences": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ReferenceSummaryList" + }, + "ListContactFlowModules": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ContactFlowModulesSummaryList" + }, + "ListDefaultVocabularies": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DefaultVocabularyList" + }, + "SearchVocabularies": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "VocabularySummaryList" + }, + "ListPhoneNumbersV2": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ListPhoneNumbersSummaryList" + }, + "SearchAvailablePhoneNumbers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AvailableNumbersList" + }, + "SearchUsers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "Users" + }, + "ListTaskTemplates": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TaskTemplates" + }, + "SearchSecurityProfiles": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "SecurityProfiles" + }, + "SearchQueues": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "Queues" + }, + "SearchRoutingProfiles": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "RoutingProfiles" + }, + "ListTrafficDistributionGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TrafficDistributionGroupSummaryList" + }, + "ListRules": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "RuleSummaryList" + }, + "ListContactEvaluations": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "EvaluationSummaryList" + }, + "ListEvaluationFormVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EvaluationFormVersionSummaryList" + }, + "ListEvaluationForms": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EvaluationFormSummaryList" + }, + "SearchHoursOfOperations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "HoursOfOperations" + }, + "SearchPrompts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "Prompts" + }, + "SearchQuickConnects": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "QuickConnects" + }, + "SearchResourceTags": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Tags" + }, + "ListTrafficDistributionGroupUsers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TrafficDistributionGroupUserSummaryList" + }, + "ListViewVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ViewVersionSummaryList" + }, + "ListViews": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ViewsSummaryList" + }, + "ListSecurityProfileApplications": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Applications", + "non_aggregate_keys": [ + "LastModifiedRegion", + "LastModifiedTime" + ] + }, + "ListFlowAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "FlowAssociationSummaryList" + }, + "ListPredefinedAttributes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PredefinedAttributeSummaryList" + }, + "ListUserProficiencies": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "LastModifiedTime", + "LastModifiedRegion" + ], + "output_token": "NextToken", + "result_key": "UserProficiencyList" + }, + "SearchContacts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "TotalCount" + ], + "output_token": "NextToken", + "result_key": "Contacts" + }, + "SearchPredefinedAttributes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "PredefinedAttributes" + }, + "SearchContactFlowModules": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "ContactFlowModules" + }, + "SearchContactFlows": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "ContactFlows" + }, + "ListAuthenticationProfiles": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AuthenticationProfileSummaryList" + }, + "SearchAgentStatuses": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "AgentStatuses" + }, + "SearchUserHierarchyGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "UserHierarchyGroups" + }, + "ListContactFlowVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ContactFlowVersionSummaryList" + }, + "ListHoursOfOperationOverrides": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "LastModifiedRegion", + "LastModifiedTime" + ], + "output_token": "NextToken", + "result_key": "HoursOfOperationOverrideList" + }, + "SearchHoursOfOperationOverrides": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "HoursOfOperationOverrides" + }, + "ListRoutingProfileManualAssignmentQueues": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "LastModifiedRegion", + "LastModifiedTime" + ], + "output_token": "NextToken", + "result_key": "RoutingProfileManualAssignmentQueueConfigSummaryList" + }, + "ListContactFlowModuleAliases": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ContactFlowModuleAliasSummaryList" + }, + "ListContactFlowModuleVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ContactFlowModuleVersionSummaryList" + }, + "ListDataTableAttributes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Attributes" + }, + "ListDataTablePrimaryValues": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PrimaryValuesList" + }, + "ListDataTableValues": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Values" + }, + "ListDataTables": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DataTableSummaryList" + }, + "ListWorkspacePages": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "WorkspacePageList" + }, + "ListWorkspaces": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "WorkspaceSummaryList" + }, + "SearchDataTables": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "DataTables" + }, + "SearchViews": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "Views" + }, + "SearchWorkspaceAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "WorkspaceAssociations" + }, + "SearchWorkspaces": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "Workspaces" + }, + "ListEntitySecurityProfiles": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SecurityProfiles" + }, + "ListSecurityProfileFlowModules": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "LastModifiedRegion", + "LastModifiedTime" + ], + "output_token": "NextToken", + "result_key": "AllowedFlowModules" + }, + "ListChildHoursOfOperations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "LastModifiedRegion", + "LastModifiedTime" + ], + "output_token": "NextToken", + "result_key": "ChildHoursOfOperationsSummaryList" + }, + "ListTestCases": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TestCaseSummaryList" + }, + "SearchTestCases": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "TestCases" + }, + "ListAttachedFilesConfigurations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AttachedFilesConfigurations" + }, + "SearchRules": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "Rules" + }, + "ListMetrics": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MetricSummaryList" + }, + "SearchMetrics": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ApproximateTotalCount" + ], + "output_token": "NextToken", + "result_key": "Metrics" + }, + "ListExtractionDefinitions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ExtractionDefinitionSummaryList" + }, + "ListSecurityProfileAIAgents": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "LastModifiedRegion", + "LastModifiedTime" + ], + "output_token": "NextToken", + "result_key": "AllowedAIAgents" + }, + "ListEvaluationFormAIVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AIVersionSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/connect/2017-08-08/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connect/2017-08-08/service-2.json.gz new file mode 100644 index 0000000..6d0b744 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connect/2017-08-08/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectcampaigns/2021-01-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connectcampaigns/2021-01-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7893848 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connectcampaigns/2021-01-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectcampaigns/2021-01-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/connectcampaigns/2021-01-30/paginators-1.json new file mode 100644 index 0000000..6ab0451 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/connectcampaigns/2021-01-30/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListCampaigns": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "campaignSummaryList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectcampaigns/2021-01-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connectcampaigns/2021-01-30/service-2.json.gz new file mode 100644 index 0000000..d79e552 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connectcampaigns/2021-01-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectcampaignsv2/2024-04-23/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connectcampaignsv2/2024-04-23/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c70a2dc Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connectcampaignsv2/2024-04-23/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectcampaignsv2/2024-04-23/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/connectcampaignsv2/2024-04-23/paginators-1.json new file mode 100644 index 0000000..c839607 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/connectcampaignsv2/2024-04-23/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListCampaigns": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "campaignSummaryList" + }, + "ListConnectInstanceIntegrations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "integrationSummaryList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectcampaignsv2/2024-04-23/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connectcampaignsv2/2024-04-23/service-2.json.gz new file mode 100644 index 0000000..5abb02b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connectcampaignsv2/2024-04-23/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cf5de24 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/paginators-1.json new file mode 100644 index 0000000..502a550 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "SearchCases": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "cases" + }, + "SearchRelatedItems": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "relatedItems" + }, + "ListCaseRules": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "caseRules" + }, + "SearchAllRelatedItems": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "relatedItems" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/paginators-1.sdk-extras.json new file mode 100644 index 0000000..b4022bb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "SearchCases": { + "non_aggregate_keys": [ + "totalCount" + ] + } + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/service-2.json.gz new file mode 100644 index 0000000..459ace1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/connectcases/2022-10-03/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/connecthealth/2025-01-29/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connecthealth/2025-01-29/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f6797b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connecthealth/2025-01-29/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connecthealth/2025-01-29/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/connecthealth/2025-01-29/paginators-1.json new file mode 100644 index 0000000..101c8d8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/connecthealth/2025-01-29/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListDomains": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "domains" + }, + "ListSubscriptions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "subscriptions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/connecthealth/2025-01-29/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connecthealth/2025-01-29/service-2.json.gz new file mode 100644 index 0000000..9f8a8e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connecthealth/2025-01-29/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connecthealth/2025-01-29/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/connecthealth/2025-01-29/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/connecthealth/2025-01-29/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectparticipant/2018-09-07/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connectparticipant/2018-09-07/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f03182f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connectparticipant/2018-09-07/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectparticipant/2018-09-07/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/connectparticipant/2018-09-07/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/connectparticipant/2018-09-07/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectparticipant/2018-09-07/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/connectparticipant/2018-09-07/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/connectparticipant/2018-09-07/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/connectparticipant/2018-09-07/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/connectparticipant/2018-09-07/service-2.json.gz new file mode 100644 index 0000000..e72b502 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/connectparticipant/2018-09-07/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/controlcatalog/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/controlcatalog/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..5fe705f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/controlcatalog/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/controlcatalog/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/controlcatalog/2018-05-10/paginators-1.json new file mode 100644 index 0000000..2e3358d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/controlcatalog/2018-05-10/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListCommonControls": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CommonControls" + }, + "ListDomains": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Domains" + }, + "ListObjectives": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Objectives" + }, + "ListControls": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Controls" + }, + "ListControlMappings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ControlMappings" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/controlcatalog/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/controlcatalog/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..e6be32c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/controlcatalog/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/controlcatalog/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/controlcatalog/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/controlcatalog/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/controltower/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/controltower/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ca2cf53 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/controltower/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/controltower/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/controltower/2018-05-10/paginators-1.json new file mode 100644 index 0000000..e34843b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/controltower/2018-05-10/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListEnabledControls": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "enabledControls" + }, + "ListLandingZones": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "landingZones" + }, + "ListBaselines": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "baselines" + }, + "ListEnabledBaselines": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "enabledBaselines" + }, + "ListControlOperations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "controlOperations" + }, + "ListLandingZoneOperations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "landingZoneOperations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/controltower/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/controltower/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..ea2bee2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/controltower/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/controltower/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/controltower/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/controltower/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c925b49 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/paginators-1.json new file mode 100644 index 0000000..79511ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListEnrollmentStatuses": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListRecommendationSummaries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListRecommendations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListEfficiencyMetrics": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "efficiencyMetricsByGroup" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/paginators-1.sdk-extras.json new file mode 100644 index 0000000..f9c62d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/paginators-1.sdk-extras.json @@ -0,0 +1,20 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListEnrollmentStatuses": { + "non_aggregate_keys": [ + "includeMemberAccounts" + ] + }, + "ListRecommendationSummaries": { + "non_aggregate_keys": [ + "groupBy", + "currencyCode", + "estimatedTotalDedupedSavings", + "metrics" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/service-2.json.gz new file mode 100644 index 0000000..cf68406 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cost-optimization-hub/2022-07-26/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cur/2017-01-06/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cur/2017-01-06/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..2e350e7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cur/2017-01-06/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/cur/2017-01-06/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/cur/2017-01-06/examples-1.json new file mode 100644 index 0000000..d647e38 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cur/2017-01-06/examples-1.json @@ -0,0 +1,102 @@ +{ + "version": "1.0", + "examples": { + "DeleteReportDefinition": [ + { + "input": { + "ReportName": "ExampleReport" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes the AWS Cost and Usage report named ExampleReport.", + "id": "to-delete-a-report", + "title": "To delete the AWS Cost and Usage report named ExampleReport." + } + ], + "DescribeReportDefinitions": [ + { + "input": { + "MaxResults": 5 + }, + "output": { + "ReportDefinitions": [ + { + "AdditionalArtifacts": [ + "QUICKSIGHT" + ], + "AdditionalSchemaElements": [ + "RESOURCES" + ], + "Compression": "GZIP", + "Format": "textORcsv", + "ReportName": "ExampleReport", + "S3Bucket": "example-s3-bucket", + "S3Prefix": "exampleprefix", + "S3Region": "us-east-1", + "TimeUnit": "HOURLY" + }, + { + "AdditionalArtifacts": [ + "QUICKSIGHT" + ], + "AdditionalSchemaElements": [ + "RESOURCES" + ], + "Compression": "GZIP", + "Format": "textORcsv", + "ReportName": "ExampleReport2", + "S3Bucket": "example-s3-bucket", + "S3Prefix": "exampleprefix", + "S3Region": "us-east-1", + "TimeUnit": "HOURLY" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example lists the AWS Cost and Usage reports for the account.", + "id": "to-retrieve-report-definitions", + "title": "To list the AWS Cost and Usage reports for the account." + } + ], + "PutReportDefinition": [ + { + "input": { + "ReportDefinition": { + "AdditionalArtifacts": [ + "REDSHIFT", + "QUICKSIGHT" + ], + "AdditionalSchemaElements": [ + "RESOURCES" + ], + "Compression": "ZIP", + "Format": "textORcsv", + "ReportName": "ExampleReport", + "S3Bucket": "example-s3-bucket", + "S3Prefix": "exampleprefix", + "S3Region": "us-east-1", + "TimeUnit": "DAILY" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a AWS Cost and Usage report named ExampleReport.", + "id": "to-create-a-report-definitions", + "title": "To create a report named ExampleReport." + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cur/2017-01-06/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/cur/2017-01-06/paginators-1.json new file mode 100644 index 0000000..7db4dfe --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/cur/2017-01-06/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "DescribeReportDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ReportDefinitions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/cur/2017-01-06/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/cur/2017-01-06/service-2.json.gz new file mode 100644 index 0000000..795ab44 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/cur/2017-01-06/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7f960da Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/paginators-1.json new file mode 100644 index 0000000..5fbaf04 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/paginators-1.json @@ -0,0 +1,88 @@ +{ + "pagination": { + "ListEventStreams": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "GetSimilarProfiles": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ProfileIds" + }, + "ListObjectTypeAttributes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListRuleBasedMatches": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "MatchIds" + }, + "ListSegmentDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListEventTriggers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListDomainLayouts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListUploadJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListDomainObjectTypes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListRecommenderRecipes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RecommenderRecipes" + }, + "ListRecommenders": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Recommenders" + }, + "ListRecommenderFilters": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RecommenderFilters" + }, + "ListRecommenderSchemas": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RecommenderSchemas" + }, + "ListSegmentSubscriptionEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Events" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/paginators-1.sdk-extras.json new file mode 100644 index 0000000..15e5e31 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/paginators-1.sdk-extras.json @@ -0,0 +1,15 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetSimilarProfiles": { + "non_aggregate_keys": [ + "MatchType", + "MatchId", + "RuleLevel", + "ConfidenceScore" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/service-2.json.gz new file mode 100644 index 0000000..e18e403 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/customer-profiles/2020-08-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/databrew/2017-07-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/databrew/2017-07-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4a0e36b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/databrew/2017-07-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/databrew/2017-07-25/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/databrew/2017-07-25/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/databrew/2017-07-25/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/databrew/2017-07-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/databrew/2017-07-25/paginators-1.json new file mode 100644 index 0000000..d18a749 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/databrew/2017-07-25/paginators-1.json @@ -0,0 +1,52 @@ +{ + "pagination": { + "ListDatasets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Datasets" + }, + "ListJobRuns": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "JobRuns" + }, + "ListJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Jobs" + }, + "ListProjects": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Projects" + }, + "ListRecipeVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Recipes" + }, + "ListRecipes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Recipes" + }, + "ListSchedules": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Schedules" + }, + "ListRulesets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Rulesets" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/databrew/2017-07-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/databrew/2017-07-25/service-2.json.gz new file mode 100644 index 0000000..9825ca6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/databrew/2017-07-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dataexchange/2017-07-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dataexchange/2017-07-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1a9926b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dataexchange/2017-07-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dataexchange/2017-07-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/dataexchange/2017-07-25/paginators-1.json new file mode 100644 index 0000000..cf704b0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dataexchange/2017-07-25/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "ListDataSetRevisions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Revisions" + }, + "ListDataSets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DataSets" + }, + "ListJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Jobs" + }, + "ListRevisionAssets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Assets" + }, + "ListEventActions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EventActions" + }, + "ListDataGrants": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DataGrantSummaries" + }, + "ListReceivedDataGrants": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DataGrantSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dataexchange/2017-07-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dataexchange/2017-07-25/service-2.json.gz new file mode 100644 index 0000000..8c66ead Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dataexchange/2017-07-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dataexchange/2017-07-25/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/dataexchange/2017-07-25/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dataexchange/2017-07-25/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/datapipeline/2012-10-29/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/datapipeline/2012-10-29/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a6159f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/datapipeline/2012-10-29/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/datapipeline/2012-10-29/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/datapipeline/2012-10-29/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/datapipeline/2012-10-29/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/datapipeline/2012-10-29/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/datapipeline/2012-10-29/paginators-1.json new file mode 100644 index 0000000..c859c9f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/datapipeline/2012-10-29/paginators-1.json @@ -0,0 +1,23 @@ +{ + "pagination": { + "ListPipelines": { + "input_token": "marker", + "output_token": "marker", + "more_results": "hasMoreResults", + "result_key": "pipelineIdList" + }, + "DescribeObjects": { + "input_token": "marker", + "output_token": "marker", + "more_results": "hasMoreResults", + "result_key": "pipelineObjects" + }, + "QueryObjects": { + "input_token": "marker", + "output_token": "marker", + "more_results": "hasMoreResults", + "limit_key": "limit", + "result_key": "ids" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/datapipeline/2012-10-29/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/datapipeline/2012-10-29/service-2.json.gz new file mode 100644 index 0000000..84d9bca Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/datapipeline/2012-10-29/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/datasync/2018-11-09/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/datasync/2018-11-09/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4ae01c3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/datasync/2018-11-09/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/datasync/2018-11-09/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/datasync/2018-11-09/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/datasync/2018-11-09/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/datasync/2018-11-09/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/datasync/2018-11-09/paginators-1.json new file mode 100644 index 0000000..c6892f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/datasync/2018-11-09/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListAgents": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Agents" + }, + "ListLocations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Locations" + }, + "ListTagsForResource": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Tags" + }, + "ListTaskExecutions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TaskExecutions" + }, + "ListTasks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Tasks" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/datasync/2018-11-09/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/datasync/2018-11-09/service-2.json.gz new file mode 100644 index 0000000..075c242 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/datasync/2018-11-09/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7c30e22 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/paginators-1.json new file mode 100644 index 0000000..462d94b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/paginators-1.json @@ -0,0 +1,244 @@ +{ + "pagination": { + "ListAssetRevisions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDataSourceRunActivities": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDataSourceRuns": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDataSources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDomains": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListEnvironmentBlueprintConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListEnvironmentBlueprints": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListEnvironmentProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListEnvironments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNotifications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "notifications" + }, + "ListProjectMemberships": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "members" + }, + "ListProjects": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListSubscriptionGrants": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListSubscriptionRequests": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListSubscriptionTargets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListSubscriptions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "Search": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "SearchGroupProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "SearchListings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "SearchTypes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "SearchUserProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListMetadataGenerationRuns": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListTimeSeriesDataPoints": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListEnvironmentActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListLineageNodeHistory": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "nodes" + }, + "ListAssetFilters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDataProductRevisions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDomainUnitsForParent": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListEntityOwners": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "owners" + }, + "ListPolicyGrants": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "grantList" + }, + "ListRules": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListConnections": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListJobRuns": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListLineageEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListProjectProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAccountPools": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAccountsInAccountPool": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "QueryGraph": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNotebookRuns": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNotebooks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/paginators-1.sdk-extras.json new file mode 100644 index 0000000..10bdbf1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/paginators-1.sdk-extras.json @@ -0,0 +1,23 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "Search": { + "non_aggregate_keys": [ + "totalMatchCount" + ] + }, + "SearchListings": { + "non_aggregate_keys": [ + "totalMatchCount", + "aggregates" + ] + }, + "SearchTypes": { + "non_aggregate_keys": [ + "totalMatchCount" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..56fcc3e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/datazone/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dax/2017-04-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dax/2017-04-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..9e9254d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dax/2017-04-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dax/2017-04-19/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/dax/2017-04-19/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dax/2017-04-19/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dax/2017-04-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/dax/2017-04-19/paginators-1.json new file mode 100644 index 0000000..c13b2df --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dax/2017-04-19/paginators-1.json @@ -0,0 +1,45 @@ +{ + "pagination": { + "DescribeClusters": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Clusters" + }, + "DescribeDefaultParameters": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Parameters" + }, + "DescribeEvents": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Events" + }, + "DescribeParameterGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ParameterGroups" + }, + "DescribeParameters": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Parameters" + }, + "DescribeSubnetGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SubnetGroups" + }, + "ListTags": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Tags" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dax/2017-04-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dax/2017-04-19/service-2.json.gz new file mode 100644 index 0000000..7e7ce6d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dax/2017-04-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..bcef0dd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/paginators-1.json new file mode 100644 index 0000000..5905a3f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/paginators-1.json @@ -0,0 +1,184 @@ +{ + "pagination": { + "GetSessionsStatisticsAggregation": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "statistics" + }, + "ListAvailableMeteredProducts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "meteredProducts" + }, + "ListBudgets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "budgets" + }, + "ListFarmMembers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "members" + }, + "ListFarms": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "farms" + }, + "ListFleetMembers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "members" + }, + "ListFleets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "fleets" + }, + "ListJobMembers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "members" + }, + "ListJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobs" + }, + "ListLicenseEndpoints": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "licenseEndpoints" + }, + "ListMeteredProducts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "meteredProducts" + }, + "ListMonitors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "monitors" + }, + "ListQueueEnvironments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "environments" + }, + "ListQueueFleetAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "queueFleetAssociations" + }, + "ListQueueMembers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "members" + }, + "ListQueues": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "queues" + }, + "ListSessionActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sessionActions" + }, + "ListSessions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sessions" + }, + "ListSessionsForWorker": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sessions" + }, + "ListStepConsumers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "consumers" + }, + "ListStepDependencies": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dependencies" + }, + "ListSteps": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "steps" + }, + "ListStorageProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "storageProfiles" + }, + "ListStorageProfilesForQueue": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "storageProfiles" + }, + "ListTasks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tasks" + }, + "ListWorkers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workers" + }, + "ListJobParameterDefinitions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobParameterDefinitions" + }, + "ListLimits": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "limits" + }, + "ListQueueLimitAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "queueLimitAssociations" + }, + "ListVolumes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "volumes" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/paginators-1.sdk-extras.json new file mode 100644 index 0000000..27c22e7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/paginators-1.sdk-extras.json @@ -0,0 +1,13 @@ + { + "version": 1.0, + "merge": { + "pagination": { + "GetSessionsStatisticsAggregation": { + "non_aggregate_keys": [ + "status", + "statusMessage" + ] + } + } + } + } diff --git a/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/service-2.json.gz new file mode 100644 index 0000000..9fe79da Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/waiters-2.json new file mode 100644 index 0000000..a494c63 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/deadline/2023-10-12/waiters-2.json @@ -0,0 +1,217 @@ +{ + "version" : 2, + "waiters" : { + "FleetActive" : { + "description" : "Wait until a Fleet is activated. Use this after invoking CreateFleet or UpdateFleet.", + "delay" : 5, + "maxAttempts" : 180, + "operation" : "GetFleet", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CREATE_FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "UPDATE_FAILED" + } ] + }, + "JobComplete" : { + "description" : "Wait until a job reaches any terminal status. Waits up to 1 hour by default.", + "delay" : 15, + "maxAttempts" : 240, + "operation" : "GetJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "taskRunStatus", + "state" : "success", + "expected" : "SUCCEEDED" + }, { + "matcher" : "path", + "argument" : "taskRunStatus", + "state" : "success", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "taskRunStatus", + "state" : "success", + "expected" : "CANCELED" + }, { + "matcher" : "path", + "argument" : "taskRunStatus", + "state" : "success", + "expected" : "SUSPENDED" + }, { + "matcher" : "path", + "argument" : "taskRunStatus", + "state" : "success", + "expected" : "NOT_COMPATIBLE" + }, { + "matcher" : "path", + "argument" : "lifecycleStatus", + "state" : "success", + "expected" : "ARCHIVED" + } ] + }, + "JobCreateComplete" : { + "description" : "Wait until a job is created. Use this after invoking CreateJob.", + "delay" : 1, + "maxAttempts" : 120, + "operation" : "GetJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "lifecycleStatus", + "state" : "success", + "expected" : "CREATE_COMPLETE" + }, { + "matcher" : "path", + "argument" : "lifecycleStatus", + "state" : "success", + "expected" : "UPDATE_IN_PROGRESS" + }, { + "matcher" : "path", + "argument" : "lifecycleStatus", + "state" : "success", + "expected" : "UPDATE_FAILED" + }, { + "matcher" : "path", + "argument" : "lifecycleStatus", + "state" : "success", + "expected" : "UPDATE_SUCCEEDED" + }, { + "matcher" : "path", + "argument" : "lifecycleStatus", + "state" : "failure", + "expected" : "UPLOAD_FAILED" + }, { + "matcher" : "path", + "argument" : "lifecycleStatus", + "state" : "failure", + "expected" : "CREATE_FAILED" + } ] + }, + "JobSucceeded" : { + "description" : "Wait until a job has succeeded. Fails if the job reaches a non-successful terminal status. Waits up to 1 hour by default.", + "delay" : 15, + "maxAttempts" : 240, + "operation" : "GetJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "taskRunStatus", + "state" : "success", + "expected" : "SUCCEEDED" + }, { + "matcher" : "path", + "argument" : "taskRunStatus", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "taskRunStatus", + "state" : "failure", + "expected" : "CANCELED" + }, { + "matcher" : "path", + "argument" : "taskRunStatus", + "state" : "failure", + "expected" : "SUSPENDED" + }, { + "matcher" : "path", + "argument" : "taskRunStatus", + "state" : "failure", + "expected" : "NOT_COMPATIBLE" + }, { + "matcher" : "path", + "argument" : "lifecycleStatus", + "state" : "failure", + "expected" : "ARCHIVED" + } ] + }, + "LicenseEndpointDeleted" : { + "description" : "Wait until a LicenseEndpoint is Deleted. Use this after invoking DeleteLicenseEndpoint.", + "delay" : 10, + "maxAttempts" : 234, + "operation" : "GetLicenseEndpoint", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "NOT_READY" + } ] + }, + "LicenseEndpointValid" : { + "description" : "Wait until a LicenseEndpoint is Ready. Use this after invoking CreateLicenseEndpoint.", + "delay" : 10, + "maxAttempts" : 114, + "operation" : "GetLicenseEndpoint", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "READY" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "NOT_READY" + } ] + }, + "QueueFleetAssociationStopped" : { + "description" : "Wait until a QueueFleetAssociation is stopped. Use this after setting the status to STOP_SCHEDULING_AND_COMPLETE_TASKS or STOP_SCHEDULING_AND_CANCEL_TASKS to wait for a QueueFleetAssociation to reach STOPPED", + "delay" : 10, + "maxAttempts" : 60, + "operation" : "GetQueueFleetAssociation", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "STOPPED" + } ] + }, + "QueueLimitAssociationStopped" : { + "description" : "Wait until a QueueLimitAssociation is stopped. Use this after setting the status to STOP_LIMIT_USAGE_AND_COMPLETE_TASKS or STOP_LIMIT_USAGE_AND_CANCEL_TASKS to wait for a QueueLimitAssociation to reach STOPPED", + "delay" : 10, + "maxAttempts" : 60, + "operation" : "GetQueueLimitAssociation", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "STOPPED" + } ] + }, + "QueueScheduling" : { + "delay" : 10, + "maxAttempts" : 70, + "operation" : "GetQueue", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "SCHEDULING" + } ] + }, + "QueueSchedulingBlocked" : { + "delay" : 10, + "maxAttempts" : 30, + "operation" : "GetQueue", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "SCHEDULING_BLOCKED" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/detective/2018-10-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/detective/2018-10-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..12d977d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/detective/2018-10-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/detective/2018-10-26/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/detective/2018-10-26/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/detective/2018-10-26/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/detective/2018-10-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/detective/2018-10-26/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/detective/2018-10-26/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/detective/2018-10-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/detective/2018-10-26/service-2.json.gz new file mode 100644 index 0000000..8dbadd9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/detective/2018-10-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/devicefarm/2015-06-23/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/devicefarm/2015-06-23/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b536615 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/devicefarm/2015-06-23/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/devicefarm/2015-06-23/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/devicefarm/2015-06-23/examples-1.json new file mode 100644 index 0000000..9db4e46 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/devicefarm/2015-06-23/examples-1.json @@ -0,0 +1,1242 @@ +{ + "version": "1.0", + "examples": { + "CreateDevicePool": [ + { + "input": { + "name": "MyDevicePool", + "description": "My Android devices", + "projectArn": "arn:aws:devicefarm:us-west-2:123456789101:project:EXAMPLE-GUID-123-456", + "rules": [ + + ] + }, + "output": { + "devicePool": { + } + }, + "comments": { + "input": { + "name": "A device pool contains related devices, such as devices that run only on Android or that run only on iOS.", + "projectArn": "You can get the project ARN by using the list-projects CLI command." + }, + "output": { + } + }, + "description": "The following example creates a new device pool named MyDevicePool inside an existing project.", + "id": "createdevicepool-example-1470862210860", + "title": "To create a new device pool" + } + ], + "CreateProject": [ + { + "input": { + "name": "MyProject" + }, + "output": { + "project": { + "name": "MyProject", + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:5e01a8c7-c861-4c0a-b1d5-12345EXAMPLE", + "created": "1472660939.152" + } + }, + "comments": { + "input": { + "name": "A project in Device Farm is a workspace that contains test runs. A run is a test of a single app against one or more devices." + }, + "output": { + } + }, + "description": "The following example creates a new project named MyProject.", + "id": "createproject-example-1470862210860", + "title": "To create a new project" + } + ], + "CreateRemoteAccessSession": [ + { + "input": { + "name": "MySession", + "configuration": { + "billingMethod": "METERED" + }, + "deviceArn": "arn:aws:devicefarm:us-west-2::device:123EXAMPLE", + "projectArn": "arn:aws:devicefarm:us-west-2:123456789101:project:EXAMPLE-GUID-123-456" + }, + "output": { + "remoteAccessSession": { + } + }, + "comments": { + "input": { + "deviceArn": "You can get the device ARN by using the list-devices CLI command.", + "projectArn": "You can get the project ARN by using the list-projects CLI command." + }, + "output": { + } + }, + "description": "The following example creates a remote access session named MySession.", + "id": "to-create-a-remote-access-session-1470970668274", + "title": "To create a remote access session" + } + ], + "CreateUpload": [ + { + "input": { + "name": "MyAppiumPythonUpload", + "type": "APPIUM_PYTHON_TEST_PACKAGE", + "projectArn": "arn:aws:devicefarm:us-west-2:123456789101:project:EXAMPLE-GUID-123-456" + }, + "output": { + "upload": { + "name": "MyAppiumPythonUpload", + "type": "APPIUM_PYTHON_TEST_PACKAGE", + "arn": "arn:aws:devicefarm:us-west-2:123456789101:upload:5e01a8c7-c861-4c0a-b1d5-5ec6e6c6dd23/b5340a65-3da7-4da6-a26e-12345EXAMPLE", + "created": "1472661404.186", + "status": "INITIALIZED", + "url": "https://prod-us-west-2-uploads.s3-us-west-2.amazonaws.com/arn%3Aaws%3Adevicefarm%3Aus-west-2%3A123456789101%3Aproject%3A5e01a8c7-c861-4c0a-b1d5-12345EXAMPLE/uploads/arn%3Aaws%3Adevicefarm%3Aus-west-2%3A123456789101%3Aupload%3A5e01a8c7-c861-4c0a-b1d5-5ec6e6c6dd23/b5340a65-3da7-4da6-a26e-12345EXAMPLE/MyAppiumPythonUpload?AWSAccessKeyId=1234567891011EXAMPLE&Expires=1472747804&Signature=1234567891011EXAMPLE" + } + }, + "comments": { + "input": { + "projectArn": "You can get the project ARN by using the list-projects CLI command." + }, + "output": { + } + }, + "description": "The following example creates a new Appium Python test package upload inside an existing project.", + "id": "createupload-example-1470864711775", + "title": "To create a new test package upload" + } + ], + "DeleteDevicePool": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2::devicepool:123-456-EXAMPLE-GUID" + }, + "output": { + }, + "comments": { + "input": { + "arn": "You can get the device pool ARN by using the list-device-pools CLI command." + }, + "output": { + } + }, + "description": "The following example deletes a specific device pool.", + "id": "deletedevicepool-example-1470866975494", + "title": "To delete a device pool" + } + ], + "DeleteProject": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:EXAMPLE-GUID-123-456" + }, + "output": { + }, + "comments": { + "input": { + "arn": "You can get the project ARN by using the list-projects CLI command." + }, + "output": { + } + }, + "description": "The following example deletes a specific project.", + "id": "deleteproject-example-1470867374212", + "title": "To delete a project" + } + ], + "DeleteRemoteAccessSession": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:session:EXAMPLE-GUID-123-456" + }, + "output": { + }, + "comments": { + "input": { + "arn": "You can get the remote access session ARN by using the list-remote-access-sessions CLI command." + }, + "output": { + } + }, + "description": "The following example deletes a specific remote access session.", + "id": "to-delete-a-specific-remote-access-session-1470971431677", + "title": "To delete a specific remote access session" + } + ], + "DeleteRun": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:run:EXAMPLE-GUID-123-456" + }, + "output": { + }, + "comments": { + "input": { + "arn": "You can get the run ARN by using the list-runs CLI command." + }, + "output": { + } + }, + "description": "The following example deletes a specific test run.", + "id": "deleterun-example-1470867905129", + "title": "To delete a run" + } + ], + "DeleteUpload": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:upload:EXAMPLE-GUID-123-456" + }, + "output": { + }, + "comments": { + "input": { + "arn": "You can get the upload ARN by using the list-uploads CLI command." + }, + "output": { + } + }, + "description": "The following example deletes a specific upload.", + "id": "deleteupload-example-1470868363942", + "title": "To delete a specific upload" + } + ], + "GetAccountSettings": [ + { + "input": { + }, + "output": { + "accountSettings": { + "awsAccountNumber": "123456789101", + "unmeteredDevices": { + "ANDROID": 1, + "IOS": 2 + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns information about your Device Farm account settings.", + "id": "to-get-information-about-account-settings-1472567568189", + "title": "To get information about account settings" + } + ], + "GetDevice": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2::device:123EXAMPLE" + }, + "output": { + "device": { + "name": "LG G2 (Sprint)", + "arn": "arn:aws:devicefarm:us-west-2::device:A0E6E6E1059E45918208DF75B2B7EF6C", + "cpu": { + "architecture": "armeabi-v7a", + "clock": 2265.6, + "frequency": "MHz" + }, + "formFactor": "PHONE", + "heapSize": 256000000, + "image": "75B2B7EF6C12345EXAMPLE", + "manufacturer": "LG", + "memory": 16000000000, + "model": "G2 (Sprint)", + "os": "4.2.2", + "platform": "ANDROID", + "resolution": { + "height": 1920, + "width": 1080 + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns information about a specific device.", + "id": "getdevice-example-1470870602173", + "title": "To get information about a device" + } + ], + "GetDevicePool": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:EXAMPLE-GUID-123-456" + }, + "output": { + "devicePool": { + } + }, + "comments": { + "input": { + "arn": "You can obtain the project ARN by using the list-projects CLI command." + }, + "output": { + } + }, + "description": "The following example returns information about a specific device pool, given a project ARN.", + "id": "getdevicepool-example-1470870873136", + "title": "To get information about a device pool" + } + ], + "GetDevicePoolCompatibility": [ + { + "input": { + "appArn": "arn:aws:devicefarm:us-west-2::app:123-456-EXAMPLE-GUID", + "devicePoolArn": "arn:aws:devicefarm:us-west-2::devicepool:123-456-EXAMPLE-GUID", + "testType": "APPIUM_PYTHON" + }, + "output": { + "compatibleDevices": [ + + ], + "incompatibleDevices": [ + + ] + }, + "comments": { + "input": { + "devicePoolArn": "You can get the device pool ARN by using the list-device-pools CLI command." + }, + "output": { + } + }, + "description": "The following example returns information about the compatibility of a specific device pool, given its ARN.", + "id": "getdevicepoolcompatibility-example-1470925003466", + "title": "To get information about the compatibility of a device pool" + } + ], + "GetJob": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2::job:123-456-EXAMPLE-GUID" + }, + "output": { + "job": { + } + }, + "comments": { + "input": { + "arn": "You can get the job ARN by using the list-jobs CLI command." + }, + "output": { + } + }, + "description": "The following example returns information about a specific job.", + "id": "getjob-example-1470928294268", + "title": "To get information about a job" + } + ], + "GetOfferingStatus": [ + { + "input": { + "nextToken": "RW5DdDJkMWYwZjM2MzM2VHVpOHJIUXlDUXlhc2QzRGViYnc9SEXAMPLE=" + }, + "output": { + "current": { + "D68B3C05-1BA6-4360-BC69-12345EXAMPLE": { + "offering": { + "type": "RECURRING", + "description": "Android Remote Access Unmetered Device Slot", + "id": "D68B3C05-1BA6-4360-BC69-12345EXAMPLE", + "platform": "ANDROID" + }, + "quantity": 1 + } + }, + "nextPeriod": { + "D68B3C05-1BA6-4360-BC69-12345EXAMPLE": { + "effectiveOn": "1472688000", + "offering": { + "type": "RECURRING", + "description": "Android Remote Access Unmetered Device Slot", + "id": "D68B3C05-1BA6-4360-BC69-12345EXAMPLE", + "platform": "ANDROID" + }, + "quantity": 1 + } + } + }, + "comments": { + "input": { + "nextToken": "A dynamically generated value, used for paginating results." + }, + "output": { + } + }, + "description": "The following example returns information about Device Farm offerings available to your account.", + "id": "to-get-status-information-about-device-offerings-1472568124402", + "title": "To get status information about device offerings" + } + ], + "GetProject": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:5e01a8c7-c861-4c0a-b1d5-12345EXAMPLE" + }, + "output": { + "project": { + "name": "My Project", + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:5e01a8c7-c861-4c0a-b1d5-12345EXAMPLE", + "created": "1472660939.152" + } + }, + "comments": { + "input": { + "arn": "You can get the project ARN by using the list-projects CLI command." + }, + "output": { + } + }, + "description": "The following example gets information about a specific project.", + "id": "to-get-a-project-1470975038449", + "title": "To get information about a project" + } + ], + "GetRemoteAccessSession": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:session:EXAMPLE-GUID-123-456" + }, + "output": { + "remoteAccessSession": { + } + }, + "comments": { + "input": { + "arn": "You can get the remote access session ARN by using the list-remote-access-sessions CLI command." + }, + "output": { + } + }, + "description": "The following example gets a specific remote access session.", + "id": "to-get-a-remote-access-session-1471014119414", + "title": "To get a remote access session" + } + ], + "GetRun": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:run:5e01a8c7-c861-4c0a-b1d5-5ec6e6c6dd23/0fcac17b-6122-44d7-ae5a-12345EXAMPLE" + }, + "output": { + "run": { + "name": "My Test Run", + "type": "BUILTIN_EXPLORER", + "arn": "arn:aws:devicefarm:us-west-2:123456789101:run:5e01a8c7-c861-4c0a-b1d5-5ec6e6c6dd23/0fcac17b-6122-44d7-ae5a-12345EXAMPLE", + "billingMethod": "METERED", + "completedJobs": 0, + "counters": { + "errored": 0, + "failed": 0, + "passed": 0, + "skipped": 0, + "stopped": 0, + "total": 0, + "warned": 0 + }, + "created": "1472667509.852", + "deviceMinutes": { + "metered": 0.0, + "total": 0.0, + "unmetered": 0.0 + }, + "platform": "ANDROID", + "result": "PENDING", + "status": "RUNNING", + "totalJobs": 3 + } + }, + "comments": { + "input": { + "arn": "You can get the run ARN by using the list-runs CLI command." + }, + "output": { + } + }, + "description": "The following example gets information about a specific test run.", + "id": "to-get-a-test-run-1471015895657", + "title": "To get information about a test run" + } + ], + "GetSuite": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:suite:EXAMPLE-GUID-123-456" + }, + "output": { + "suite": { + } + }, + "comments": { + "input": { + "arn": "You can get the suite ARN by using the list-suites CLI command." + }, + "output": { + } + }, + "description": "The following example gets information about a specific test suite.", + "id": "to-get-information-about-a-test-suite-1471016525008", + "title": "To get information about a test suite" + } + ], + "GetTest": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:test:EXAMPLE-GUID-123-456" + }, + "output": { + "test": { + } + }, + "comments": { + "input": { + "arn": "You can get the test ARN by using the list-tests CLI command." + }, + "output": { + } + }, + "description": "The following example gets information about a specific test.", + "id": "to-get-information-about-a-specific-test-1471025744238", + "title": "To get information about a specific test" + } + ], + "GetUpload": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:upload:EXAMPLE-GUID-123-456" + }, + "output": { + "upload": { + } + }, + "comments": { + "input": { + "arn": "You can get the test ARN by using the list-uploads CLI command." + }, + "output": { + } + }, + "description": "The following example gets information about a specific upload.", + "id": "to-get-information-about-a-specific-upload-1471025996221", + "title": "To get information about a specific upload" + } + ], + "InstallToRemoteAccessSession": [ + { + "input": { + "appArn": "arn:aws:devicefarm:us-west-2:123456789101:app:EXAMPLE-GUID-123-456", + "remoteAccessSessionArn": "arn:aws:devicefarm:us-west-2:123456789101:session:EXAMPLE-GUID-123-456" + }, + "output": { + "appUpload": { + } + }, + "comments": { + "input": { + "remoteAccessSessionArn": "You can get the remote access session ARN by using the list-remote-access-sessions CLI command." + }, + "output": { + } + }, + "description": "The following example installs a specific app to a device in a specific remote access session.", + "id": "to-install-to-a-remote-access-session-1471634453818", + "title": "To install to a remote access session" + } + ], + "ListArtifacts": [ + { + "input": { + "type": "SCREENSHOT", + "arn": "arn:aws:devicefarm:us-west-2:123456789101:run:EXAMPLE-GUID-123-456" + }, + "comments": { + "input": { + "arn": "Can also be used to list artifacts for a Job, Suite, or Test ARN." + }, + "output": { + } + }, + "description": "The following example lists screenshot artifacts for a specific run.", + "id": "to-list-artifacts-for-a-resource-1471635409527", + "title": "To list artifacts for a resource" + } + ], + "ListDevicePools": [ + { + "input": { + "type": "PRIVATE", + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:EXAMPLE-GUID-123-456" + }, + "output": { + "devicePools": [ + { + "name": "Top Devices", + "arn": "arn:aws:devicefarm:us-west-2::devicepool:082d10e5-d7d7-48a5-ba5c-12345EXAMPLE", + "description": "Top devices", + "rules": [ + { + "value": "[\"arn:aws:devicefarm:us-west-2::device:123456789EXAMPLE\",\"arn:aws:devicefarm:us-west-2::device:123456789EXAMPLE\",\"arn:aws:devicefarm:us-west-2::device:123456789EXAMPLE\",\"arn:aws:devicefarm:us-west-2::device:123456789EXAMPLE\",\"arn:aws:devicefarm:us-west-2::device:123456789EXAMPLE\",\"arn:aws:devicefarm:us-west-2::device:123456789EXAMPLE\",\"arn:aws:devicefarm:us-west-2::device:123456789EXAMPLE\",\"arn:aws:devicefarm:us-west-2::device:123456789EXAMPLE\",\"arn:aws:devicefarm:us-west-2::device:123456789EXAMPLE\",\"arn:aws:devicefarm:us-west-2::device:123456789EXAMPLE\"]", + "attribute": "ARN", + "operator": "IN" + } + ] + }, + { + "name": "My Android Device Pool", + "arn": "arn:aws:devicefarm:us-west-2:123456789101:devicepool:5e01a8c7-c861-4c0a-b1d5-5ec6e6c6dd23/bf96e75a-28f6-4e61-b6a7-12345EXAMPLE", + "description": "Samsung Galaxy Android devices", + "rules": [ + { + "value": "[\"arn:aws:devicefarm:us-west-2::device:123456789EXAMPLE\",\"arn:aws:devicefarm:us-west-2::device:123456789EXAMPLE\",\"arn:aws:devicefarm:us-west-2::device:123456789EXAMPLE\"]", + "attribute": "ARN", + "operator": "IN" + } + ] + } + ] + }, + "comments": { + "input": { + "arn": "You can get the project ARN by using the list-projects CLI command." + }, + "output": { + } + }, + "description": "The following example returns information about the private device pools in a specific project.", + "id": "to-get-information-about-device-pools-1471635745170", + "title": "To get information about device pools" + } + ], + "ListDevices": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:EXAMPLE-GUID-123-456" + }, + "output": { + }, + "comments": { + "input": { + "arn": "You can get the project ARN by using the list-projects CLI command." + }, + "output": { + } + }, + "description": "The following example returns information about the available devices in a specific project.", + "id": "to-get-information-about-devices-1471641699344", + "title": "To get information about devices" + } + ], + "ListJobs": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:EXAMPLE-GUID-123-456" + }, + "comments": { + "input": { + "arn": "You can get the project ARN by using the list-jobs CLI command." + }, + "output": { + } + }, + "description": "The following example returns information about jobs in a specific project.", + "id": "to-get-information-about-jobs-1471642228071", + "title": "To get information about jobs" + } + ], + "ListOfferingTransactions": [ + { + "input": { + "nextToken": "RW5DdDJkMWYwZjM2MzM2VHVpOHJIUXlDUXlhc2QzRGViYnc9SEXAMPLE=" + }, + "output": { + "offeringTransactions": [ + { + "cost": { + "amount": 0, + "currencyCode": "USD" + }, + "createdOn": "1470021420", + "offeringStatus": { + "type": "RENEW", + "effectiveOn": "1472688000", + "offering": { + "type": "RECURRING", + "description": "Android Remote Access Unmetered Device Slot", + "id": "D68B3C05-1BA6-4360-BC69-12345EXAMPLE", + "platform": "ANDROID" + }, + "quantity": 0 + }, + "transactionId": "03728003-d1ea-4851-abd6-12345EXAMPLE" + }, + { + "cost": { + "amount": 250, + "currencyCode": "USD" + }, + "createdOn": "1470021420", + "offeringStatus": { + "type": "PURCHASE", + "effectiveOn": "1470021420", + "offering": { + "type": "RECURRING", + "description": "Android Remote Access Unmetered Device Slot", + "id": "D68B3C05-1BA6-4360-BC69-12345EXAMPLE", + "platform": "ANDROID" + }, + "quantity": 1 + }, + "transactionId": "56820b6e-06bd-473a-8ff8-12345EXAMPLE" + }, + { + "cost": { + "amount": 175, + "currencyCode": "USD" + }, + "createdOn": "1465538520", + "offeringStatus": { + "type": "PURCHASE", + "effectiveOn": "1465538520", + "offering": { + "type": "RECURRING", + "description": "Android Unmetered Device Slot", + "id": "8980F81C-00D7-469D-8EC6-12345EXAMPLE", + "platform": "ANDROID" + }, + "quantity": 1 + }, + "transactionId": "953ae2c6-d760-4a04-9597-12345EXAMPLE" + }, + { + "cost": { + "amount": 8.07, + "currencyCode": "USD" + }, + "createdOn": "1459344300", + "offeringStatus": { + "type": "PURCHASE", + "effectiveOn": "1459344300", + "offering": { + "type": "RECURRING", + "description": "iOS Unmetered Device Slot", + "id": "A53D4D73-A6F6-4B82-A0B0-12345EXAMPLE", + "platform": "IOS" + }, + "quantity": 1 + }, + "transactionId": "2baf9021-ae3e-47f5-ab52-12345EXAMPLE" + } + ] + }, + "comments": { + "input": { + "nextToken": "A dynamically generated value, used for paginating results." + }, + "output": { + } + }, + "description": "The following example returns information about Device Farm offering transactions.", + "id": "to-get-information-about-device-offering-transactions-1472561712315", + "title": "To get information about device offering transactions" + } + ], + "ListOfferings": [ + { + "input": { + "nextToken": "RW5DdDJkMWYwZjM2MzM2VHVpOHJIUXlDUXlhc2QzRGViYnc9SEXAMPLE=" + }, + "output": { + "offerings": [ + { + "type": "RECURRING", + "description": "iOS Unmetered Device Slot", + "id": "A53D4D73-A6F6-4B82-A0B0-12345EXAMPLE", + "platform": "IOS", + "recurringCharges": [ + { + "cost": { + "amount": 250, + "currencyCode": "USD" + }, + "frequency": "MONTHLY" + } + ] + }, + { + "type": "RECURRING", + "description": "Android Unmetered Device Slot", + "id": "8980F81C-00D7-469D-8EC6-12345EXAMPLE", + "platform": "ANDROID", + "recurringCharges": [ + { + "cost": { + "amount": 250, + "currencyCode": "USD" + }, + "frequency": "MONTHLY" + } + ] + }, + { + "type": "RECURRING", + "description": "Android Remote Access Unmetered Device Slot", + "id": "D68B3C05-1BA6-4360-BC69-12345EXAMPLE", + "platform": "ANDROID", + "recurringCharges": [ + { + "cost": { + "amount": 250, + "currencyCode": "USD" + }, + "frequency": "MONTHLY" + } + ] + }, + { + "type": "RECURRING", + "description": "iOS Remote Access Unmetered Device Slot", + "id": "552B4DAD-A6C9-45C4-94FB-12345EXAMPLE", + "platform": "IOS", + "recurringCharges": [ + { + "cost": { + "amount": 250, + "currencyCode": "USD" + }, + "frequency": "MONTHLY" + } + ] + } + ] + }, + "comments": { + "input": { + "nextToken": "A dynamically generated value, used for paginating results." + }, + "output": { + } + }, + "description": "The following example returns information about available device offerings.", + "id": "to-get-information-about-device-offerings-1472562810999", + "title": "To get information about device offerings" + } + ], + "ListProjects": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:7ad300ed-8183-41a7-bf94-12345EXAMPLE", + "nextToken": "RW5DdDJkMWYwZjM2MzM2VHVpOHJIUXlDUXlhc2QzRGViYnc9SEXAMPLE" + }, + "output": { + "projects": [ + { + "name": "My Test Project", + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:7ad300ed-8183-41a7-bf94-12345EXAMPLE", + "created": "1453163262.105" + }, + { + "name": "Hello World", + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:d6b087d9-56db-4e44-b9ec-12345EXAMPLE", + "created": "1470350112.439" + } + ] + }, + "comments": { + "input": { + "nextToken": "A dynamically generated value, used for paginating results." + }, + "output": { + } + }, + "description": "The following example returns information about the specified project in Device Farm.", + "id": "to-get-information-about-a-device-farm-project-1472564014388", + "title": "To get information about a Device Farm project" + } + ], + "ListRemoteAccessSessions": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:session:EXAMPLE-GUID-123-456", + "nextToken": "RW5DdDJkMWYwZjM2MzM2VHVpOHJIUXlDUXlhc2QzRGViYnc9SEXAMPLE=" + }, + "output": { + "remoteAccessSessions": [ + + ] + }, + "comments": { + "input": { + "arn": "You can get the Amazon Resource Name (ARN) of the session by using the list-sessions CLI command.", + "nextToken": "A dynamically generated value, used for paginating results." + }, + "output": { + } + }, + "description": "The following example returns information about a specific Device Farm remote access session.", + "id": "to-get-information-about-a-remote-access-session-1472581144803", + "title": "To get information about a remote access session" + } + ], + "ListRuns": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:run:5e01a8c7-c861-4c0a-b1d5-5ec6e6c6dd23/0fcac17b-6122-44d7-ae5a-12345EXAMPLE", + "nextToken": "RW5DdDJkMWYwZjM2MzM2VHVpOHJIUXlDUXlhc2QzRGViYnc9SEXAMPLE" + }, + "output": { + "runs": [ + { + "name": "My Test Run", + "type": "BUILTIN_EXPLORER", + "arn": "arn:aws:devicefarm:us-west-2:123456789101:run:5e01a8c7-c861-4c0a-b1d5-5ec6e6c6dd23/0fcac17b-6122-44d7-ae5a-12345EXAMPLE", + "billingMethod": "METERED", + "completedJobs": 0, + "counters": { + "errored": 0, + "failed": 0, + "passed": 0, + "skipped": 0, + "stopped": 0, + "total": 0, + "warned": 0 + }, + "created": "1472667509.852", + "deviceMinutes": { + "metered": 0.0, + "total": 0.0, + "unmetered": 0.0 + }, + "platform": "ANDROID", + "result": "PENDING", + "status": "RUNNING", + "totalJobs": 3 + } + ] + }, + "comments": { + "input": { + "arn": "You can get the Amazon Resource Name (ARN) of the run by using the list-runs CLI command.", + "nextToken": "A dynamically generated value, used for paginating results." + }, + "output": { + } + }, + "description": "The following example returns information about a specific test run.", + "id": "to-get-information-about-test-runs-1472582711069", + "title": "To get information about a test run" + } + ], + "ListSamples": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:EXAMPLE-GUID-123-456", + "nextToken": "RW5DdDJkMWYwZjM2MzM2VHVpOHJIUXlDUXlhc2QzRGViYnc9SEXAMPLE" + }, + "output": { + "samples": [ + + ] + }, + "comments": { + "input": { + "arn": "You can get the Amazon Resource Name (ARN) of the project by using the list-projects CLI command.", + "nextToken": "A dynamically generated value, used for paginating results." + }, + "output": { + } + }, + "description": "The following example returns information about samples, given a specific Device Farm project.", + "id": "to-get-information-about-samples-1472582847534", + "title": "To get information about samples" + } + ], + "ListSuites": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:job:EXAMPLE-GUID-123-456", + "nextToken": "RW5DdDJkMWYwZjM2MzM2VHVpOHJIUXlDUXlhc2QzRGViYnc9SEXAMPLE" + }, + "output": { + "suites": [ + + ] + }, + "comments": { + "input": { + "arn": "You can get the Amazon Resource Name (ARN) of the job by using the list-jobs CLI command.", + "nextToken": "A dynamically generated value, used for paginating results." + }, + "output": { + } + }, + "description": "The following example returns information about suites, given a specific Device Farm job.", + "id": "to-get-information-about-suites-1472583038218", + "title": "To get information about suites" + } + ], + "ListTests": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:EXAMPLE-GUID-123-456", + "nextToken": "RW5DdDJkMWYwZjM2MzM2VHVpOHJIUXlDUXlhc2QzRGViYnc9SEXAMPLE" + }, + "output": { + "tests": [ + + ] + }, + "comments": { + "input": { + "arn": "You can get the Amazon Resource Name (ARN) of the project by using the list-projects CLI command.", + "nextToken": "A dynamically generated value, used for paginating results." + }, + "output": { + } + }, + "description": "The following example returns information about tests, given a specific Device Farm project.", + "id": "to-get-information-about-tests-1472617372212", + "title": "To get information about tests" + } + ], + "ListUniqueProblems": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:EXAMPLE-GUID-123-456", + "nextToken": "RW5DdDJkMWYwZjM2MzM2VHVpOHJIUXlDUXlhc2QzRGViYnc9SEXAMPLE" + }, + "output": { + "uniqueProblems": { + } + }, + "comments": { + "input": { + "arn": "You can get the Amazon Resource Name (ARN) of the project by using the list-projects CLI command.", + "nextToken": "A dynamically generated value, used for paginating results." + }, + "output": { + } + }, + "description": "The following example returns information about unique problems, given a specific Device Farm project.", + "id": "to-get-information-about-unique-problems-1472617781008", + "title": "To get information about unique problems" + } + ], + "ListUploads": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:EXAMPLE-GUID-123-456", + "nextToken": "RW5DdDJkMWYwZjM2MzM2VHVpOHJIUXlDUXlhc2QzRGViYnc9SEXAMPLE" + }, + "output": { + "uploads": [ + + ] + }, + "comments": { + "input": { + "arn": "You can get the Amazon Resource Name (ARN) of the project by using the list-projects CLI command.", + "nextToken": "A dynamically generated value, used for paginating results." + }, + "output": { + } + }, + "description": "The following example returns information about uploads, given a specific Device Farm project.", + "id": "to-get-information-about-uploads-1472617943090", + "title": "To get information about uploads" + } + ], + "PurchaseOffering": [ + { + "input": { + "offeringId": "D68B3C05-1BA6-4360-BC69-12345EXAMPLE", + "quantity": 1 + }, + "output": { + "offeringTransaction": { + "cost": { + "amount": 8.07, + "currencyCode": "USD" + }, + "createdOn": "1472648340", + "offeringStatus": { + "type": "PURCHASE", + "effectiveOn": "1472648340", + "offering": { + "type": "RECURRING", + "description": "Android Remote Access Unmetered Device Slot", + "id": "D68B3C05-1BA6-4360-BC69-12345EXAMPLE", + "platform": "ANDROID" + }, + "quantity": 1 + }, + "transactionId": "d30614ed-1b03-404c-9893-12345EXAMPLE" + } + }, + "comments": { + "input": { + "offeringId": "You can get the offering ID by using the list-offerings CLI command." + }, + "output": { + } + }, + "description": "The following example purchases a specific device slot offering.", + "id": "to-purchase-a-device-slot-offering-1472648146343", + "title": "To purchase a device slot offering" + } + ], + "RenewOffering": [ + { + "input": { + "offeringId": "D68B3C05-1BA6-4360-BC69-12345EXAMPLE", + "quantity": 1 + }, + "output": { + "offeringTransaction": { + "cost": { + "amount": 250, + "currencyCode": "USD" + }, + "createdOn": "1472648880", + "offeringStatus": { + "type": "RENEW", + "effectiveOn": "1472688000", + "offering": { + "type": "RECURRING", + "description": "Android Remote Access Unmetered Device Slot", + "id": "D68B3C05-1BA6-4360-BC69-12345EXAMPLE", + "platform": "ANDROID" + }, + "quantity": 1 + }, + "transactionId": "e90f1405-8c35-4561-be43-12345EXAMPLE" + } + }, + "comments": { + "input": { + "offeringId": "You can get the offering ID by using the list-offerings CLI command." + }, + "output": { + } + }, + "description": "The following example renews a specific device slot offering.", + "id": "to-renew-a-device-slot-offering-1472648899785", + "title": "To renew a device slot offering" + } + ], + "ScheduleRun": [ + { + "input": { + "name": "MyRun", + "devicePoolArn": "arn:aws:devicefarm:us-west-2:123456789101:pool:EXAMPLE-GUID-123-456", + "projectArn": "arn:aws:devicefarm:us-west-2:123456789101:project:EXAMPLE-GUID-123-456", + "test": { + "type": "APPIUM_JAVA_JUNIT", + "testPackageArn": "arn:aws:devicefarm:us-west-2:123456789101:test:EXAMPLE-GUID-123-456" + } + }, + "output": { + "run": { + } + }, + "comments": { + "input": { + "devicePoolArn": "You can get the Amazon Resource Name (ARN) of the device pool by using the list-pools CLI command.", + "projectArn": "You can get the Amazon Resource Name (ARN) of the project by using the list-projects CLI command.", + "testPackageArn": "You can get the Amazon Resource Name (ARN) of the test package by using the list-tests CLI command." + }, + "output": { + } + }, + "description": "The following example schedules a test run named MyRun.", + "id": "to-schedule-a-test-run-1472652429636", + "title": "To schedule a test run" + } + ], + "StopRun": [ + { + "input": { + "arn": "arn:aws:devicefarm:us-west-2:123456789101:run:EXAMPLE-GUID-123-456" + }, + "output": { + "run": { + } + }, + "comments": { + "input": { + "arn": "You can get the Amazon Resource Name (ARN) of the test run by using the list-runs CLI command." + }, + "output": { + } + }, + "description": "The following example stops a specific test run.", + "id": "to-stop-a-test-run-1472653770340", + "title": "To stop a test run" + } + ], + "UpdateDevicePool": [ + { + "input": { + "name": "NewName", + "arn": "arn:aws:devicefarm:us-west-2::devicepool:082d10e5-d7d7-48a5-ba5c-12345EXAMPLE", + "description": "NewDescription", + "rules": [ + { + "value": "True", + "attribute": "REMOTE_ACCESS_ENABLED", + "operator": "EQUALS" + } + ] + }, + "output": { + "devicePool": { + } + }, + "comments": { + "input": { + "arn": "You can get the Amazon Resource Name (ARN) of the device pool by using the list-pools CLI command." + }, + "output": { + "devicePool": "Note: you cannot update curated device pools." + } + }, + "description": "The following example updates the specified device pool with a new name and description. It also enables remote access of devices in the device pool.", + "id": "to-update-a-device-pool-1472653887677", + "title": "To update a device pool" + } + ], + "UpdateProject": [ + { + "input": { + "name": "NewName", + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:8f75187d-101e-4625-accc-12345EXAMPLE" + }, + "output": { + "project": { + "name": "NewName", + "arn": "arn:aws:devicefarm:us-west-2:123456789101:project:8f75187d-101e-4625-accc-12345EXAMPLE", + "created": "1448400709.927" + } + }, + "comments": { + "input": { + "arn": "You can get the Amazon Resource Name (ARN) of the project by using the list-projects CLI command." + }, + "output": { + } + }, + "description": "The following example updates the specified project with a new name.", + "id": "to-update-a-device-pool-1472653887677", + "title": "To update a device pool" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/devicefarm/2015-06-23/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/devicefarm/2015-06-23/paginators-1.json new file mode 100644 index 0000000..982e07f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/devicefarm/2015-06-23/paginators-1.json @@ -0,0 +1,110 @@ +{ + "pagination": { + "ListArtifacts": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "artifacts" + }, + "ListDevicePools": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "devicePools" + }, + "ListDevices": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "devices" + }, + "ListJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "jobs" + }, + "ListProjects": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "projects" + }, + "ListRuns": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "runs" + }, + "ListSamples": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "samples" + }, + "ListSuites": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "suites" + }, + "ListTests": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "tests" + }, + "ListUniqueProblems": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "uniqueProblems" + }, + "ListUploads": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "uploads" + }, + "GetOfferingStatus": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": [ + "current", + "nextPeriod" + ] + }, + "ListOfferingTransactions": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "offeringTransactions" + }, + "ListOfferings": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "offerings" + }, + "ListDeviceInstances": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "deviceInstances" + }, + "ListInstanceProfiles": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "instanceProfiles" + }, + "ListNetworkProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "networkProfiles" + }, + "ListOfferingPromotions": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "offeringPromotions" + }, + "ListRemoteAccessSessions": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "remoteAccessSessions" + }, + "ListVPCEConfigurations": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "vpceConfigurations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/devicefarm/2015-06-23/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/devicefarm/2015-06-23/service-2.json.gz new file mode 100644 index 0000000..909c010 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/devicefarm/2015-06-23/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/devops-agent/2026-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/devops-agent/2026-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..eaf958e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/devops-agent/2026-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/devops-agent/2026-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/devops-agent/2026-01-01/paginators-1.json new file mode 100644 index 0000000..a193512 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/devops-agent/2026-01-01/paginators-1.json @@ -0,0 +1,76 @@ +{ + "pagination": { + "ListAgentSpaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agentSpaces" + }, + "ListAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "associations" + }, + "ListBacklogTasks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "limit", + "result_key": "tasks" + }, + "ListExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "limit", + "result_key": "executions" + }, + "ListGoals": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "limit", + "result_key": "goals" + }, + "ListJournalRecords": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "limit", + "result_key": "records" + }, + "ListServices": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "services" + }, + "ListAssetFiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAssetTypes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAssetVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAssets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListTriggers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/devops-agent/2026-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/devops-agent/2026-01-01/service-2.json.gz new file mode 100644 index 0000000..f303b92 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/devops-agent/2026-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/devops-guru/2020-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/devops-guru/2020-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7a9ba39 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/devops-guru/2020-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/devops-guru/2020-12-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/devops-guru/2020-12-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/devops-guru/2020-12-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/devops-guru/2020-12-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/devops-guru/2020-12-01/paginators-1.json new file mode 100644 index 0000000..d0d5871 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/devops-guru/2020-12-01/paginators-1.json @@ -0,0 +1,125 @@ +{ + "pagination": { + "DescribeResourceCollectionHealth": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": [ + "CloudFormation", + "Service", + "Tags" + ] + }, + "GetResourceCollection": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": [ + "ResourceCollection.CloudFormation.StackNames", + "ResourceCollection.Tags" + ], + "non_aggregate_keys": [ + "ResourceCollection" + ] + }, + "ListAnomaliesForInsight": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": [ + "ReactiveAnomalies", + "ProactiveAnomalies" + ] + }, + "ListEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Events" + }, + "ListInsights": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": [ + "ProactiveInsights", + "ReactiveInsights" + ] + }, + "ListNotificationChannels": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Channels" + }, + "ListRecommendations": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Recommendations" + }, + "SearchInsights": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": [ + "ProactiveInsights", + "ReactiveInsights" + ] + }, + "GetCostEstimation": { + "input_token": "NextToken", + "non_aggregate_keys": [ + "Status", + "TotalCost", + "TimeRange", + "ResourceCollection" + ], + "output_token": "NextToken", + "result_key": [ + "Costs" + ] + }, + "DescribeOrganizationResourceCollectionHealth": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": [ + "CloudFormation", + "Account", + "Service", + "Tags" + ] + }, + "ListOrganizationInsights": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": [ + "ProactiveInsights", + "ReactiveInsights" + ] + }, + "SearchOrganizationInsights": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": [ + "ProactiveInsights", + "ReactiveInsights" + ] + }, + "ListAnomalousLogGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": [ + "InsightId", + "AnomalousLogGroups" + ] + }, + "ListMonitoredResources": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": [ + "MonitoredResourceIdentifiers" + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/devops-guru/2020-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/devops-guru/2020-12-01/service-2.json.gz new file mode 100644 index 0000000..04cc6ce Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/devops-guru/2020-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/directconnect/2012-10-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/directconnect/2012-10-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..99ae0c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/directconnect/2012-10-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/directconnect/2012-10-25/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/directconnect/2012-10-25/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/directconnect/2012-10-25/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/directconnect/2012-10-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/directconnect/2012-10-25/paginators-1.json new file mode 100644 index 0000000..dbca668 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/directconnect/2012-10-25/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "DescribeDirectConnectGatewayAssociations": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "directConnectGatewayAssociations" + }, + "DescribeDirectConnectGatewayAttachments": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "directConnectGatewayAttachments" + }, + "DescribeDirectConnectGateways": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "directConnectGateways" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/directconnect/2012-10-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/directconnect/2012-10-25/service-2.json.gz new file mode 100644 index 0000000..75ebb67 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/directconnect/2012-10-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/discovery/2015-11-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/discovery/2015-11-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..e9ae4a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/discovery/2015-11-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/discovery/2015-11-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/discovery/2015-11-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/discovery/2015-11-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/discovery/2015-11-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/discovery/2015-11-01/paginators-1.json new file mode 100644 index 0000000..5567a77 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/discovery/2015-11-01/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "DescribeAgents": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "agentsInfo" + }, + "DescribeContinuousExports": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "descriptions" + }, + "DescribeExportConfigurations": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "exportsInfo" + }, + "DescribeExportTasks": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "exportsInfo" + }, + "DescribeTags": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "tags" + }, + "ListConfigurations": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "configurations" + }, + "DescribeImportTasks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tasks" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/discovery/2015-11-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/discovery/2015-11-01/service-2.json.gz new file mode 100644 index 0000000..d01470d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/discovery/2015-11-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dlm/2018-01-12/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dlm/2018-01-12/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8dab4c3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dlm/2018-01-12/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dlm/2018-01-12/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/dlm/2018-01-12/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dlm/2018-01-12/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dlm/2018-01-12/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/dlm/2018-01-12/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dlm/2018-01-12/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dlm/2018-01-12/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dlm/2018-01-12/service-2.json.gz new file mode 100644 index 0000000..e8400e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dlm/2018-01-12/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1ea40de Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/examples-1.json new file mode 100644 index 0000000..f9e8c4e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/examples-1.json @@ -0,0 +1,1074 @@ +{ + "version": "1.0", + "examples": { + "AddTagsToResource": [ + { + "input": { + "ResourceArn": "arn:aws:dms:us-east-1:123456789012:endpoint:ASXWXJZLNWNT5HTWCGV2BUJQ7E", + "Tags": [ + { + "Key": "Acount", + "Value": "1633456" + } + ] + }, + "output": { + }, + "comments": { + "input": { + "ResourceArn": "Required. Use the ARN of the resource you want to tag.", + "Tags": "Required. Use the Key/Value pair format." + }, + "output": { + } + }, + "description": "Adds metadata tags to an AWS DMS resource, including replication instance, endpoint, security group, and migration task. These tags can also be used with cost allocation reporting to track cost associated with AWS DMS resources, or used in a Condition statement in an IAM policy for AWS DMS.", + "id": "add-tags-to-resource-1481744141435", + "title": "Add tags to resource" + } + ], + "CreateEndpoint": [ + { + "input": { + "CertificateArn": "", + "DatabaseName": "testdb", + "EndpointIdentifier": "test-endpoint-1", + "EndpointType": "source", + "EngineName": "mysql", + "ExtraConnectionAttributes": "", + "KmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/4c1731d6-5435-ed4d-be13-d53411a7cfbd", + "Password": "pasword", + "Port": 3306, + "ServerName": "mydb.cx1llnox7iyx.us-west-2.rds.amazonaws.com", + "SslMode": "require", + "Tags": [ + { + "Key": "Acount", + "Value": "143327655" + } + ], + "Username": "username" + }, + "output": { + "Endpoint": { + "EndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:RAAR3R22XSH46S3PWLC3NJAWKM", + "EndpointIdentifier": "test-endpoint-1", + "EndpointType": "source", + "EngineName": "mysql", + "KmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/4c1731d6-5435-ed4d-be13-d53411a7cfbd", + "Port": 3306, + "ServerName": "mydb.cx1llnox7iyx.us-west-2.rds.amazonaws.com", + "Status": "active", + "Username": "username" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates an endpoint using the provided settings.", + "id": "create-endpoint-1481746254348", + "title": "Create endpoint" + } + ], + "CreateReplicationInstance": [ + { + "input": { + "AllocatedStorage": 123, + "AutoMinorVersionUpgrade": true, + "AvailabilityZone": "", + "EngineVersion": "", + "KmsKeyId": "", + "MultiAZ": true, + "PreferredMaintenanceWindow": "", + "PubliclyAccessible": true, + "ReplicationInstanceClass": "", + "ReplicationInstanceIdentifier": "", + "ReplicationSubnetGroupIdentifier": "", + "Tags": [ + { + "Key": "string", + "Value": "string" + } + ], + "VpcSecurityGroupIds": [ + + ] + }, + "output": { + "ReplicationInstance": { + "AllocatedStorage": 5, + "AutoMinorVersionUpgrade": true, + "EngineVersion": "1.5.0", + "KmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/4c1731d6-5435-ed4d-be13-d53411a7cfbd", + "PendingModifiedValues": { + }, + "PreferredMaintenanceWindow": "sun:06:00-sun:14:00", + "PubliclyAccessible": true, + "ReplicationInstanceArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ", + "ReplicationInstanceClass": "dms.t2.micro", + "ReplicationInstanceIdentifier": "test-rep-1", + "ReplicationInstanceStatus": "creating", + "ReplicationSubnetGroup": { + "ReplicationSubnetGroupDescription": "default", + "ReplicationSubnetGroupIdentifier": "default", + "SubnetGroupStatus": "Complete", + "Subnets": [ + { + "SubnetAvailabilityZone": { + "Name": "us-east-1d" + }, + "SubnetIdentifier": "subnet-f6dd91af", + "SubnetStatus": "Active" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1b" + }, + "SubnetIdentifier": "subnet-3605751d", + "SubnetStatus": "Active" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1c" + }, + "SubnetIdentifier": "subnet-c2daefb5", + "SubnetStatus": "Active" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1e" + }, + "SubnetIdentifier": "subnet-85e90cb8", + "SubnetStatus": "Active" + } + ], + "VpcId": "vpc-6741a603" + } + } + }, + "comments": { + "output": { + } + }, + "description": "Creates the replication instance using the specified parameters.", + "id": "create-replication-instance-1481746705295", + "title": "Create replication instance" + } + ], + "CreateReplicationSubnetGroup": [ + { + "input": { + "ReplicationSubnetGroupDescription": "US West subnet group", + "ReplicationSubnetGroupIdentifier": "us-west-2ab-vpc-215ds366", + "SubnetIds": [ + "subnet-e145356n", + "subnet-58f79200" + ], + "Tags": [ + { + "Key": "Acount", + "Value": "145235" + } + ] + }, + "output": { + "ReplicationSubnetGroup": { + } + }, + "comments": { + "output": { + } + }, + "description": "Creates a replication subnet group given a list of the subnet IDs in a VPC.", + "id": "create-replication-subnet-group-1481747297930", + "title": "Create replication subnet group" + } + ], + "CreateReplicationTask": [ + { + "input": { + "CdcStartTime": "2016-12-14T18:25:43Z", + "MigrationType": "full-load", + "ReplicationInstanceArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ", + "ReplicationTaskIdentifier": "task1", + "ReplicationTaskSettings": "", + "SourceEndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:ZW5UAN6P4E77EC7YWHK4RZZ3BE", + "TableMappings": "file://mappingfile.json", + "Tags": [ + { + "Key": "Acount", + "Value": "24352226" + } + ], + "TargetEndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:ASXWXJZLNWNT5HTWCGV2BUJQ7E" + }, + "output": { + "ReplicationTask": { + "MigrationType": "full-load", + "ReplicationInstanceArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ", + "ReplicationTaskArn": "arn:aws:dms:us-east-1:123456789012:task:OEAMB3NXSTZ6LFYZFEPPBBXPYM", + "ReplicationTaskCreationDate": "2016-12-14T18:25:43Z", + "ReplicationTaskIdentifier": "task1", + "ReplicationTaskSettings": "{\"TargetMetadata\":{\"TargetSchema\":\"\",\"SupportLobs\":true,\"FullLobMode\":true,\"LobChunkSize\":64,\"LimitedSizeLobMode\":false,\"LobMaxSize\":0},\"FullLoadSettings\":{\"FullLoadEnabled\":true,\"ApplyChangesEnabled\":false,\"TargetTablePrepMode\":\"DROP_AND_CREATE\",\"CreatePkAfterFullLoad\":false,\"StopTaskCachedChangesApplied\":false,\"StopTaskCachedChangesNotApplied\":false,\"ResumeEnabled\":false,\"ResumeMinTableSize\":100000,\"ResumeOnlyClusteredPKTables\":true,\"MaxFullLoadSubTasks\":8,\"TransactionConsistencyTimeout\":600,\"CommitRate\":10000},\"Logging\":{\"EnableLogging\":false}}", + "SourceEndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:ZW5UAN6P4E77EC7YWHK4RZZ3BE", + "Status": "creating", + "TableMappings": "file://mappingfile.json", + "TargetEndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:ASXWXJZLNWNT5HTWCGV2BUJQ7E" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a replication task using the specified parameters.", + "id": "create-replication-task-1481747646288", + "title": "Create replication task" + } + ], + "DeleteCertificate": [ + { + "input": { + "CertificateArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUSM457DE6XFJCJQ" + }, + "output": { + "Certificate": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the specified certificate.", + "id": "delete-certificate-1481751957981", + "title": "Delete Certificate" + } + ], + "DeleteConnection": [ + { + "input": { + "EndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:RAAR3R22XSH46S3PWLC3NJAWKM", + "ReplicationInstanceArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ" + }, + "output": { + "Connection": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the connection between the replication instance and the endpoint.", + "id": "delete-connection-1481751957981", + "title": "Delete Connection" + } + ], + "DeleteEndpoint": [ + { + "input": { + "EndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:RAAR3R22XSH46S3PWLC3NJAWKM" + }, + "output": { + "Endpoint": { + "EndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:RAAR3R22XSH46S3PWLC3NJAWKM", + "EndpointIdentifier": "test-endpoint-1", + "EndpointType": "source", + "EngineName": "mysql", + "KmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/4c1731d6-5435-ed4d-be13-d53411a7cfbd", + "Port": 3306, + "ServerName": "mydb.cx1llnox7iyx.us-west-2.rds.amazonaws.com", + "Status": "active", + "Username": "username" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the specified endpoint. All tasks associated with the endpoint must be deleted before you can delete the endpoint.\n", + "id": "delete-endpoint-1481752425530", + "title": "Delete Endpoint" + } + ], + "DeleteReplicationInstance": [ + { + "input": { + "ReplicationInstanceArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ" + }, + "output": { + "ReplicationInstance": { + "AllocatedStorage": 5, + "AutoMinorVersionUpgrade": true, + "EngineVersion": "1.5.0", + "KmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/4c1731d6-5435-ed4d-be13-d53411a7cfbd", + "PendingModifiedValues": { + }, + "PreferredMaintenanceWindow": "sun:06:00-sun:14:00", + "PubliclyAccessible": true, + "ReplicationInstanceArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ", + "ReplicationInstanceClass": "dms.t2.micro", + "ReplicationInstanceIdentifier": "test-rep-1", + "ReplicationInstanceStatus": "creating", + "ReplicationSubnetGroup": { + "ReplicationSubnetGroupDescription": "default", + "ReplicationSubnetGroupIdentifier": "default", + "SubnetGroupStatus": "Complete", + "Subnets": [ + { + "SubnetAvailabilityZone": { + "Name": "us-east-1d" + }, + "SubnetIdentifier": "subnet-f6dd91af", + "SubnetStatus": "Active" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1b" + }, + "SubnetIdentifier": "subnet-3605751d", + "SubnetStatus": "Active" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1c" + }, + "SubnetIdentifier": "subnet-c2daefb5", + "SubnetStatus": "Active" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1e" + }, + "SubnetIdentifier": "subnet-85e90cb8", + "SubnetStatus": "Active" + } + ], + "VpcId": "vpc-6741a603" + } + } + }, + "comments": { + "output": { + } + }, + "description": "Deletes the specified replication instance. You must delete any migration tasks that are associated with the replication instance before you can delete it.\n\n", + "id": "delete-replication-instance-1481752552839", + "title": "Delete Replication Instance" + } + ], + "DeleteReplicationSubnetGroup": [ + { + "input": { + "ReplicationSubnetGroupIdentifier": "us-west-2ab-vpc-215ds366" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes a replication subnet group.", + "id": "delete-replication-subnet-group-1481752728597", + "title": "Delete Replication Subnet Group" + } + ], + "DeleteReplicationTask": [ + { + "input": { + "ReplicationTaskArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ" + }, + "output": { + "ReplicationTask": { + "MigrationType": "full-load", + "ReplicationInstanceArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ", + "ReplicationTaskArn": "arn:aws:dms:us-east-1:123456789012:task:OEAMB3NXSTZ6LFYZFEPPBBXPYM", + "ReplicationTaskCreationDate": "2016-12-14T18:25:43Z", + "ReplicationTaskIdentifier": "task1", + "ReplicationTaskSettings": "{\"TargetMetadata\":{\"TargetSchema\":\"\",\"SupportLobs\":true,\"FullLobMode\":true,\"LobChunkSize\":64,\"LimitedSizeLobMode\":false,\"LobMaxSize\":0},\"FullLoadSettings\":{\"FullLoadEnabled\":true,\"ApplyChangesEnabled\":false,\"TargetTablePrepMode\":\"DROP_AND_CREATE\",\"CreatePkAfterFullLoad\":false,\"StopTaskCachedChangesApplied\":false,\"StopTaskCachedChangesNotApplied\":false,\"ResumeEnabled\":false,\"ResumeMinTableSize\":100000,\"ResumeOnlyClusteredPKTables\":true,\"MaxFullLoadSubTasks\":8,\"TransactionConsistencyTimeout\":600,\"CommitRate\":10000},\"Logging\":{\"EnableLogging\":false}}", + "SourceEndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:ZW5UAN6P4E77EC7YWHK4RZZ3BE", + "Status": "creating", + "TableMappings": "file://mappingfile.json", + "TargetEndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:ASXWXJZLNWNT5HTWCGV2BUJQ7E" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the specified replication task.", + "id": "delete-replication-task-1481752903506", + "title": "Delete Replication Task" + } + ], + "DescribeAccountAttributes": [ + { + "input": { + }, + "output": { + "AccountQuotas": [ + { + "AccountQuotaName": "ReplicationInstances", + "Max": 20, + "Used": 0 + }, + { + "AccountQuotaName": "AllocatedStorage", + "Max": 20, + "Used": 0 + }, + { + "AccountQuotaName": "Endpoints", + "Max": 20, + "Used": 0 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists all of the AWS DMS attributes for a customer account. The attributes include AWS DMS quotas for the account, such as the number of replication instances allowed. The description for a quota includes the quota name, current usage toward that quota, and the quota's maximum value. This operation does not take any parameters.", + "id": "describe-acount-attributes-1481753085663", + "title": "Describe acount attributes" + } + ], + "DescribeCertificates": [ + { + "input": { + "Filters": [ + { + "Name": "string", + "Values": [ + "string", + "string" + ] + } + ], + "Marker": "", + "MaxRecords": 123 + }, + "output": { + "Certificates": [ + + ], + "Marker": "" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Provides a description of the certificate.", + "id": "describe-certificates-1481753186244", + "title": "Describe certificates" + } + ], + "DescribeConnections": [ + { + "input": { + "Filters": [ + { + "Name": "string", + "Values": [ + "string", + "string" + ] + } + ], + "Marker": "", + "MaxRecords": 123 + }, + "output": { + "Connections": [ + { + "EndpointArn": "arn:aws:dms:us-east-arn:aws:dms:us-east-1:123456789012:endpoint:ZW5UAN6P4E77EC7YWHK4RZZ3BE", + "EndpointIdentifier": "testsrc1", + "ReplicationInstanceArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ", + "ReplicationInstanceIdentifier": "test", + "Status": "successful" + } + ], + "Marker": "" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Describes the status of the connections that have been made between the replication instance and an endpoint. Connections are created when you test an endpoint.", + "id": "describe-connections-1481754477953", + "title": "Describe connections" + } + ], + "DescribeEndpointTypes": [ + { + "input": { + "Filters": [ + { + "Name": "string", + "Values": [ + "string", + "string" + ] + } + ], + "Marker": "", + "MaxRecords": 123 + }, + "output": { + "Marker": "", + "SupportedEndpointTypes": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns information about the type of endpoints available.", + "id": "describe-endpoint-types-1481754742591", + "title": "Describe endpoint types" + } + ], + "DescribeEndpoints": [ + { + "input": { + "Filters": [ + { + "Name": "string", + "Values": [ + "string", + "string" + ] + } + ], + "Marker": "", + "MaxRecords": 123 + }, + "output": { + "Endpoints": [ + + ], + "Marker": "" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns information about the endpoints for your account in the current region.", + "id": "describe-endpoints-1481754926060", + "title": "Describe endpoints" + } + ], + "DescribeOrderableReplicationInstances": [ + { + "input": { + "Marker": "", + "MaxRecords": 123 + }, + "output": { + "Marker": "", + "OrderableReplicationInstances": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns information about the replication instance types that can be created in the specified region.", + "id": "describe-orderable-replication-instances-1481755123669", + "title": "Describe orderable replication instances" + } + ], + "DescribeRefreshSchemasStatus": [ + { + "input": { + "EndpointArn": "" + }, + "output": { + "RefreshSchemasStatus": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns the status of the refresh-schemas operation.", + "id": "describe-refresh-schema-status-1481755303497", + "title": "Describe refresh schema status" + } + ], + "DescribeReplicationInstances": [ + { + "input": { + "Filters": [ + { + "Name": "string", + "Values": [ + "string", + "string" + ] + } + ], + "Marker": "", + "MaxRecords": 123 + }, + "output": { + "Marker": "", + "ReplicationInstances": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns the status of the refresh-schemas operation.", + "id": "describe-replication-instances-1481755443952", + "title": "Describe replication instances" + } + ], + "DescribeReplicationSubnetGroups": [ + { + "input": { + "Filters": [ + { + "Name": "string", + "Values": [ + "string", + "string" + ] + } + ], + "Marker": "", + "MaxRecords": 123 + }, + "output": { + "Marker": "", + "ReplicationSubnetGroups": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns information about the replication subnet groups.", + "id": "describe-replication-subnet-groups-1481755621284", + "title": "Describe replication subnet groups" + } + ], + "DescribeReplicationTasks": [ + { + "input": { + "Filters": [ + { + "Name": "string", + "Values": [ + "string", + "string" + ] + } + ], + "Marker": "", + "MaxRecords": 123 + }, + "output": { + "Marker": "", + "ReplicationTasks": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns information about replication tasks for your account in the current region.", + "id": "describe-replication-tasks-1481755777563", + "title": "Describe replication tasks" + } + ], + "DescribeSchemas": [ + { + "input": { + "EndpointArn": "", + "Marker": "", + "MaxRecords": 123 + }, + "output": { + "Marker": "", + "Schemas": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns information about the schema for the specified endpoint.", + "id": "describe-schemas-1481755933924", + "title": "Describe schemas" + } + ], + "DescribeTableStatistics": [ + { + "input": { + "Marker": "", + "MaxRecords": 123, + "ReplicationTaskArn": "" + }, + "output": { + "Marker": "", + "ReplicationTaskArn": "", + "TableStatistics": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns table statistics on the database migration task, including table name, rows inserted, rows updated, and rows deleted.", + "id": "describe-table-statistics-1481756071890", + "title": "Describe table statistics" + } + ], + "ImportCertificate": [ + { + "input": { + "CertificateIdentifier": "", + "CertificatePem": "" + }, + "output": { + "Certificate": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Uploads the specified certificate.", + "id": "import-certificate-1481756197206", + "title": "Import certificate" + } + ], + "ListTagsForResource": [ + { + "input": { + "ResourceArn": "" + }, + "output": { + "TagList": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists all tags for an AWS DMS resource.", + "id": "list-tags-for-resource-1481761095501", + "title": "List tags for resource" + } + ], + "ModifyEndpoint": [ + { + "input": { + "CertificateArn": "", + "DatabaseName": "", + "EndpointArn": "", + "EndpointIdentifier": "", + "EndpointType": "source", + "EngineName": "", + "ExtraConnectionAttributes": "", + "Password": "", + "Port": 123, + "ServerName": "", + "SslMode": "require", + "Username": "" + }, + "output": { + "Endpoint": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Modifies the specified endpoint.", + "id": "modify-endpoint-1481761649937", + "title": "Modify endpoint" + } + ], + "ModifyReplicationInstance": [ + { + "input": { + "AllocatedStorage": 123, + "AllowMajorVersionUpgrade": true, + "ApplyImmediately": true, + "AutoMinorVersionUpgrade": true, + "EngineVersion": "1.5.0", + "MultiAZ": true, + "PreferredMaintenanceWindow": "sun:06:00-sun:14:00", + "ReplicationInstanceArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ", + "ReplicationInstanceClass": "dms.t2.micro", + "ReplicationInstanceIdentifier": "test-rep-1", + "VpcSecurityGroupIds": [ + + ] + }, + "output": { + "ReplicationInstance": { + "AllocatedStorage": 5, + "AutoMinorVersionUpgrade": true, + "EngineVersion": "1.5.0", + "KmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/4c1731d6-5435-ed4d-be13-d53411a7cfbd", + "PendingModifiedValues": { + }, + "PreferredMaintenanceWindow": "sun:06:00-sun:14:00", + "PubliclyAccessible": true, + "ReplicationInstanceArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ", + "ReplicationInstanceClass": "dms.t2.micro", + "ReplicationInstanceIdentifier": "test-rep-1", + "ReplicationInstanceStatus": "available", + "ReplicationSubnetGroup": { + "ReplicationSubnetGroupDescription": "default", + "ReplicationSubnetGroupIdentifier": "default", + "SubnetGroupStatus": "Complete", + "Subnets": [ + { + "SubnetAvailabilityZone": { + "Name": "us-east-1d" + }, + "SubnetIdentifier": "subnet-f6dd91af", + "SubnetStatus": "Active" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1b" + }, + "SubnetIdentifier": "subnet-3605751d", + "SubnetStatus": "Active" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1c" + }, + "SubnetIdentifier": "subnet-c2daefb5", + "SubnetStatus": "Active" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1e" + }, + "SubnetIdentifier": "subnet-85e90cb8", + "SubnetStatus": "Active" + } + ], + "VpcId": "vpc-6741a603" + } + } + }, + "comments": { + "output": { + } + }, + "description": "Modifies the replication instance to apply new settings. You can change one or more parameters by specifying these parameters and the new values in the request. Some settings are applied during the maintenance window.", + "id": "modify-replication-instance-1481761784746", + "title": "Modify replication instance" + } + ], + "ModifyReplicationSubnetGroup": [ + { + "input": { + "ReplicationSubnetGroupDescription": "", + "ReplicationSubnetGroupIdentifier": "", + "SubnetIds": [ + + ] + }, + "output": { + "ReplicationSubnetGroup": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Modifies the settings for the specified replication subnet group.", + "id": "modify-replication-subnet-group-1481762275392", + "title": "Modify replication subnet group" + } + ], + "RefreshSchemas": [ + { + "input": { + "EndpointArn": "", + "ReplicationInstanceArn": "" + }, + "output": { + "RefreshSchemasStatus": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Populates the schema for the specified endpoint. This is an asynchronous operation and can take several minutes. You can check the status of this operation by calling the describe-refresh-schemas-status operation.", + "id": "refresh-schema-1481762399111", + "title": "Refresh schema" + } + ], + "RemoveTagsFromResource": [ + { + "input": { + "ResourceArn": "arn:aws:dms:us-east-1:123456789012:endpoint:ASXWXJZLNWNT5HTWCGV2BUJQ7E", + "TagKeys": [ + + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Removes metadata tags from an AWS DMS resource.", + "id": "remove-tags-from-resource-1481762571330", + "title": "Remove tags from resource" + } + ], + "StartReplicationTask": [ + { + "input": { + "CdcStartTime": "2016-12-14T13:33:20Z", + "ReplicationTaskArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ", + "StartReplicationTaskType": "start-replication" + }, + "output": { + "ReplicationTask": { + "MigrationType": "full-load", + "ReplicationInstanceArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ", + "ReplicationTaskArn": "arn:aws:dms:us-east-1:123456789012:task:OEAMB3NXSTZ6LFYZFEPPBBXPYM", + "ReplicationTaskCreationDate": "2016-12-14T18:25:43Z", + "ReplicationTaskIdentifier": "task1", + "ReplicationTaskSettings": "{\"TargetMetadata\":{\"TargetSchema\":\"\",\"SupportLobs\":true,\"FullLobMode\":true,\"LobChunkSize\":64,\"LimitedSizeLobMode\":false,\"LobMaxSize\":0},\"FullLoadSettings\":{\"FullLoadEnabled\":true,\"ApplyChangesEnabled\":false,\"TargetTablePrepMode\":\"DROP_AND_CREATE\",\"CreatePkAfterFullLoad\":false,\"StopTaskCachedChangesApplied\":false,\"StopTaskCachedChangesNotApplied\":false,\"ResumeEnabled\":false,\"ResumeMinTableSize\":100000,\"ResumeOnlyClusteredPKTables\":true,\"MaxFullLoadSubTasks\":8,\"TransactionConsistencyTimeout\":600,\"CommitRate\":10000},\"Logging\":{\"EnableLogging\":false}}", + "SourceEndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:ZW5UAN6P4E77EC7YWHK4RZZ3BE", + "Status": "creating", + "TableMappings": "file://mappingfile.json", + "TargetEndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:ASXWXJZLNWNT5HTWCGV2BUJQ7E" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Starts the replication task.", + "id": "start-replication-task-1481762706778", + "title": "Start replication task" + } + ], + "StopReplicationTask": [ + { + "input": { + "ReplicationTaskArn": "arn:aws:dms:us-east-1:123456789012:endpoint:ASXWXJZLNWNT5HTWCGV2BUJQ7E" + }, + "output": { + "ReplicationTask": { + "MigrationType": "full-load", + "ReplicationInstanceArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ", + "ReplicationTaskArn": "arn:aws:dms:us-east-1:123456789012:task:OEAMB3NXSTZ6LFYZFEPPBBXPYM", + "ReplicationTaskCreationDate": "2016-12-14T18:25:43Z", + "ReplicationTaskIdentifier": "task1", + "ReplicationTaskSettings": "{\"TargetMetadata\":{\"TargetSchema\":\"\",\"SupportLobs\":true,\"FullLobMode\":true,\"LobChunkSize\":64,\"LimitedSizeLobMode\":false,\"LobMaxSize\":0},\"FullLoadSettings\":{\"FullLoadEnabled\":true,\"ApplyChangesEnabled\":false,\"TargetTablePrepMode\":\"DROP_AND_CREATE\",\"CreatePkAfterFullLoad\":false,\"StopTaskCachedChangesApplied\":false,\"StopTaskCachedChangesNotApplied\":false,\"ResumeEnabled\":false,\"ResumeMinTableSize\":100000,\"ResumeOnlyClusteredPKTables\":true,\"MaxFullLoadSubTasks\":8,\"TransactionConsistencyTimeout\":600,\"CommitRate\":10000},\"Logging\":{\"EnableLogging\":false}}", + "SourceEndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:ZW5UAN6P4E77EC7YWHK4RZZ3BE", + "Status": "creating", + "TableMappings": "file://mappingfile.json", + "TargetEndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:ASXWXJZLNWNT5HTWCGV2BUJQ7E" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Stops the replication task.", + "id": "stop-replication-task-1481762924947", + "title": "Stop replication task" + } + ], + "TestConnection": [ + { + "input": { + "EndpointArn": "arn:aws:dms:us-east-1:123456789012:endpoint:RAAR3R22XSH46S3PWLC3NJAWKM", + "ReplicationInstanceArn": "arn:aws:dms:us-east-1:123456789012:rep:6UTDJGBOUS3VI3SUWA66XFJCJQ" + }, + "output": { + "Connection": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Tests the connection between the replication instance and the endpoint.", + "id": "test-conection-1481763017636", + "title": "Test conection" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/paginators-1.json new file mode 100644 index 0000000..ae92c82 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/paginators-1.json @@ -0,0 +1,100 @@ +{ + "pagination": { + "DescribeSchemas": { + "result_key": "Schemas", + "output_token": "Marker", + "input_token": "Marker", + "limit_key": "MaxRecords" + }, + "DescribeCertificates": { + "result_key": "Certificates", + "output_token": "Marker", + "input_token": "Marker", + "limit_key": "MaxRecords" + }, + "DescribeEndpoints": { + "result_key": "Endpoints", + "output_token": "Marker", + "input_token": "Marker", + "limit_key": "MaxRecords" + }, + "DescribeEventSubscriptions": { + "result_key": "EventSubscriptionsList", + "output_token": "Marker", + "input_token": "Marker", + "limit_key": "MaxRecords" + }, + "DescribeEndpointTypes": { + "result_key": "SupportedEndpointTypes", + "output_token": "Marker", + "input_token": "Marker", + "limit_key": "MaxRecords" + }, + "DescribeReplicationInstances": { + "result_key": "ReplicationInstances", + "output_token": "Marker", + "input_token": "Marker", + "limit_key": "MaxRecords" + }, + "DescribeTableStatistics": { + "result_key": "TableStatistics", + "output_token": "Marker", + "input_token": "Marker", + "limit_key": "MaxRecords" + }, + "DescribeConnections": { + "result_key": "Connections", + "output_token": "Marker", + "input_token": "Marker", + "limit_key": "MaxRecords" + }, + "DescribeReplicationTaskAssessmentResults": { + "result_key": "ReplicationTaskAssessmentResults", + "output_token": "Marker", + "input_token": "Marker", + "limit_key": "MaxRecords" + }, + "DescribeEvents": { + "result_key": "Events", + "output_token": "Marker", + "input_token": "Marker", + "limit_key": "MaxRecords" + }, + "DescribeOrderableReplicationInstances": { + "result_key": "OrderableReplicationInstances", + "output_token": "Marker", + "input_token": "Marker", + "limit_key": "MaxRecords" + }, + "DescribeReplicationSubnetGroups": { + "result_key": "ReplicationSubnetGroups", + "output_token": "Marker", + "input_token": "Marker", + "limit_key": "MaxRecords" + }, + "DescribeReplicationTasks": { + "result_key": "ReplicationTasks", + "output_token": "Marker", + "input_token": "Marker", + "limit_key": "MaxRecords" + }, + "DescribeDataMigrations": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DataMigrations" + }, + "DescribeMetadataModelChildren": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "MetadataModelChildren" + }, + "DescribeMetadataModelCreations": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Requests" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/service-2.json.gz new file mode 100644 index 0000000..51df24b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/waiters-2.json new file mode 100644 index 0000000..74c99e5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dms/2016-01-01/waiters-2.json @@ -0,0 +1,510 @@ +{ + "version":2, + "waiters":{ + "TestConnectionSucceeds":{ + "acceptors":[ + { + "argument":"Connections[].Status", + "expected":"successful", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"Connections[].Status", + "expected":"failed", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":5, + "description":"Wait until testing connection succeeds.", + "maxAttempts":60, + "operation":"DescribeConnections" + }, + "EndpointDeleted":{ + "acceptors":[ + { + "expected":"ResourceNotFoundFault", + "matcher":"error", + "state":"success" + }, + { + "argument":"Endpoints[].Status", + "expected":"active", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"Endpoints[].Status", + "expected":"creating", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":5, + "description":"Wait until testing endpoint is deleted.", + "maxAttempts":60, + "operation":"DescribeEndpoints" + }, + "ReplicationInstanceAvailable":{ + "acceptors":[ + { + "argument":"ReplicationInstances[].ReplicationInstanceStatus", + "expected":"available", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"ReplicationInstances[].ReplicationInstanceStatus", + "expected":"deleting", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationInstances[].ReplicationInstanceStatus", + "expected":"incompatible-credentials", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationInstances[].ReplicationInstanceStatus", + "expected":"incompatible-network", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationInstances[].ReplicationInstanceStatus", + "expected":"inaccessible-encryption-credentials", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":60, + "description":"Wait until DMS replication instance is available.", + "maxAttempts":60, + "operation":"DescribeReplicationInstances" + }, + "ReplicationInstanceDeleted":{ + "acceptors":[ + { + "argument":"ReplicationInstances[].ReplicationInstanceStatus", + "expected":"available", + "matcher":"pathAny", + "state":"failure" + }, + { + "expected":"ResourceNotFoundFault", + "matcher":"error", + "state":"success" + } + ], + "delay":15, + "description":"Wait until DMS replication instance is deleted.", + "maxAttempts":60, + "operation":"DescribeReplicationInstances" + }, + "ReplicationTaskReady":{ + "acceptors":[ + { + "argument":"ReplicationTasks[].Status", + "expected":"ready", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"starting", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"running", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"stopping", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"stopped", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"failed", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"modifying", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"testing", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"deleting", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":15, + "description":"Wait until DMS replication task is ready.", + "maxAttempts":60, + "operation":"DescribeReplicationTasks" + }, + "ReplicationTaskStopped":{ + "acceptors":[ + { + "argument":"ReplicationTasks[].Status", + "expected":"stopped", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"ready", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"creating", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"starting", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"failed", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"modifying", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"testing", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"deleting", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":15, + "description":"Wait until DMS replication task is stopped.", + "maxAttempts":60, + "operation":"DescribeReplicationTasks" + }, + "ReplicationTaskRunning":{ + "acceptors":[ + { + "argument":"ReplicationTasks[].Status", + "expected":"running", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"ready", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"creating", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"stopping", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"stopped", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"failed", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"modifying", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"testing", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"deleting", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":15, + "description":"Wait until DMS replication task is running.", + "maxAttempts":60, + "operation":"DescribeReplicationTasks" + }, + "ReplicationTaskDeleted":{ + "acceptors":[ + { + "argument":"ReplicationTasks[].Status", + "expected":"ready", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"creating", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"stopped", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"running", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"ReplicationTasks[].Status", + "expected":"failed", + "matcher":"pathAny", + "state":"failure" + }, + { + "expected":"ResourceNotFoundFault", + "matcher":"error", + "state":"success" + } + ], + "delay":15, + "description":"Wait until DMS replication task is deleted.", + "maxAttempts":60, + "operation":"DescribeReplicationTasks" + }, + "MetadataModelImported":{ + "acceptors":[ + { + "argument":"Requests[].Status", + "expected":"SUCCESS", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"Requests[].Status", + "expected":"FAILED", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":60, + "description":"Wait until DMS metadata model is imported.", + "maxAttempts":30, + "operation":"DescribeMetadataModelImports" + }, + "MetadataModelAssessed":{ + "acceptors":[ + { + "argument":"Requests[].Status", + "expected":"SUCCESS", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"Requests[].Status", + "expected":"FAILED", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":30, + "description":"Wait until DMS metadata model is assessed.", + "maxAttempts":360, + "operation":"DescribeMetadataModelAssessments" + }, + "MetadataModelConverted":{ + "acceptors":[ + { + "argument":"Requests[].Status", + "expected":"SUCCESS", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"Requests[].Status", + "expected":"FAILED", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":30, + "description":"Wait until DMS metadata model is converted.", + "maxAttempts":240, + "operation":"DescribeMetadataModelConversions" + }, + "MetadataModelExportedAsScript":{ + "acceptors":[ + { + "argument":"Requests[].Status", + "expected":"SUCCESS", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"Requests[].Status", + "expected":"FAILED", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":20, + "description":"Wait until DMS metadata model is exported as script.", + "maxAttempts":90, + "operation":"DescribeMetadataModelExportsAsScript" + }, + "MetadataModelExportedToTarget":{ + "acceptors":[ + { + "argument":"Requests[].Status", + "expected":"SUCCESS", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"Requests[].Status", + "expected":"FAILED", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":20, + "description":"Wait until DMS metadata model is exported to target.", + "maxAttempts":90, + "operation":"DescribeMetadataModelExportsToTarget" + }, + "MetadataModelCreated":{ + "acceptors":[ + { + "argument":"Requests[].Status", + "expected":"SUCCESS", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"Requests[].Status", + "expected":"FAILED", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":15, + "description":"Wait until DMS metadata model is created.", + "maxAttempts":40, + "operation":"DescribeMetadataModelCreations" + }, + "ExtensionPackAssociated":{ + "acceptors":[ + { + "argument":"Requests[].Status", + "expected":"SUCCESS", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"Requests[].Status", + "expected":"FAILED", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":10, + "description":"Wait until DMS extension pack is associated.", + "maxAttempts":60, + "operation":"DescribeExtensionPackAssociations" + }, + "MetadataModelConversionCancelled":{ + "acceptors":[ + { + "argument":"Requests[].Status", + "expected":"CANCELED", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"Requests[].Status", + "expected":"FAILED", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":10, + "description":"Wait until DMS metadata model conversion is cancelled.", + "maxAttempts":180, + "operation":"DescribeMetadataModelConversions" + }, + "MetadataModelCreationCancelled":{ + "acceptors":[ + { + "argument":"Requests[].Status", + "expected":"CANCELED", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"Requests[].Status", + "expected":"FAILED", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":10, + "description":"Wait until DMS metadata model creation is cancelled.", + "maxAttempts":180, + "operation":"DescribeMetadataModelCreations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/docdb-elastic/2022-11-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/docdb-elastic/2022-11-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ba4b3b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/docdb-elastic/2022-11-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/docdb-elastic/2022-11-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/docdb-elastic/2022-11-28/paginators-1.json new file mode 100644 index 0000000..bed516d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/docdb-elastic/2022-11-28/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListClusterSnapshots": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "snapshots" + }, + "ListClusters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "clusters" + }, + "ListPendingMaintenanceActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "resourcePendingMaintenanceActions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/docdb-elastic/2022-11-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/docdb-elastic/2022-11-28/service-2.json.gz new file mode 100644 index 0000000..b1e6683 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/docdb-elastic/2022-11-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0444dfe Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/paginators-1.json new file mode 100644 index 0000000..cc1a2f1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/paginators-1.json @@ -0,0 +1,82 @@ +{ + "pagination": { + "DescribeCertificates": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "Certificates" + }, + "DescribeDBClusterParameterGroups": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBClusterParameterGroups" + }, + "DescribeDBClusterParameters": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "Parameters" + }, + "DescribeDBClusterSnapshots": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBClusterSnapshots" + }, + "DescribeDBClusters": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBClusters" + }, + "DescribeDBEngineVersions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBEngineVersions" + }, + "DescribeDBInstances": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBInstances" + }, + "DescribeDBSubnetGroups": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBSubnetGroups" + }, + "DescribeEvents": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "Events" + }, + "DescribeOrderableDBInstanceOptions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "OrderableDBInstanceOptions" + }, + "DescribePendingMaintenanceActions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "PendingMaintenanceActions" + }, + "DescribeEventSubscriptions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "EventSubscriptionsList" + }, + "DescribeGlobalClusters": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "GlobalClusters" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/service-2.json.gz new file mode 100644 index 0000000..b79529c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/service-2.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/service-2.sdk-extras.json new file mode 100644 index 0000000..85e8a10 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/service-2.sdk-extras.json @@ -0,0 +1,23 @@ + { + "version": 1.0, + "merge": { + "shapes": { + "CopyDBClusterSnapshotMessage": { + "members": { + "SourceRegion": { + "shape": "String", + "documentation": "

The ID of the region that contains the snapshot to be copied.

" + } + } + }, + "CreateDBClusterMessage": { + "members": { + "SourceRegion": { + "shape": "String", + "documentation": "

The ID of the region that contains the source for the db cluster.

" + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/waiters-2.json new file mode 100644 index 0000000..e75f03b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/docdb/2014-10-31/waiters-2.json @@ -0,0 +1,90 @@ +{ + "version": 2, + "waiters": { + "DBInstanceAvailable": { + "delay": 30, + "operation": "DescribeDBInstances", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "failed", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "incompatible-restore", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "incompatible-parameters", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + } + ] + }, + "DBInstanceDeleted": { + "delay": 30, + "operation": "DescribeDBInstances", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "DBInstanceNotFound", + "matcher": "error", + "state": "success" + }, + { + "expected": "creating", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "modifying", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "rebooting", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "resetting-master-credentials", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/drs/2020-02-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/drs/2020-02-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..57ee816 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/drs/2020-02-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/drs/2020-02-26/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/drs/2020-02-26/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/drs/2020-02-26/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/drs/2020-02-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/drs/2020-02-26/paginators-1.json new file mode 100644 index 0000000..9907b8f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/drs/2020-02-26/paginators-1.json @@ -0,0 +1,94 @@ +{ + "pagination": { + "DescribeJobLogItems": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "DescribeJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "DescribeRecoveryInstances": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "DescribeRecoverySnapshots": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "DescribeReplicationConfigurationTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "DescribeSourceServers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListExtensibleSourceServers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListStagingAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "accounts" + }, + "DescribeLaunchConfigurationTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "DescribeSourceNetworks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListLaunchActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListRecoveryPlanExecutionSteps": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recoveryPlanExecutionSteps" + }, + "ListRecoveryPlanExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recoveryPlanExecutions" + }, + "ListRecoveryPlanSteps": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recoveryPlanSteps" + }, + "ListRecoveryPlans": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recoveryPlans" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/drs/2020-02-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/drs/2020-02-26/service-2.json.gz new file mode 100644 index 0000000..b6a33e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/drs/2020-02-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ds-data/2023-05-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ds-data/2023-05-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..95653ef Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ds-data/2023-05-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ds-data/2023-05-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ds-data/2023-05-31/paginators-1.json new file mode 100644 index 0000000..5038135 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ds-data/2023-05-31/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListGroupMembers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Members" + }, + "ListGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Groups" + }, + "ListGroupsForMember": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Groups" + }, + "ListUsers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Users" + }, + "SearchGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Groups" + }, + "SearchUsers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Users" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ds-data/2023-05-31/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/ds-data/2023-05-31/paginators-1.sdk-extras.json new file mode 100644 index 0000000..35f9e23 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ds-data/2023-05-31/paginators-1.sdk-extras.json @@ -0,0 +1,46 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListGroupMembers": { + "non_aggregate_keys": [ + "DirectoryId", + "MemberRealm", + "Realm" + ] + }, + "ListGroups": { + "non_aggregate_keys": [ + "DirectoryId", + "Realm" + ] + }, + "ListGroupsForMember": { + "non_aggregate_keys": [ + "DirectoryId", + "MemberRealm", + "Realm" + ] + }, + "ListUsers": { + "non_aggregate_keys": [ + "DirectoryId", + "Realm" + ] + }, + "SearchGroups": { + "non_aggregate_keys": [ + "DirectoryId", + "Realm" + ] + }, + "SearchUsers": { + "non_aggregate_keys": [ + "DirectoryId", + "Realm" + ] + } + } + } + } + \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/ds-data/2023-05-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ds-data/2023-05-31/service-2.json.gz new file mode 100644 index 0000000..000161d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ds-data/2023-05-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ac9a7cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/paginators-1.json new file mode 100644 index 0000000..71bc41d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/paginators-1.json @@ -0,0 +1,92 @@ +{ + "pagination": { + "DescribeDomainControllers": { + "result_key": "DomainControllers", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "Limit" + }, + "DescribeDirectories": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "DirectoryDescriptions" + }, + "DescribeSharedDirectories": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "SharedDirectories" + }, + "DescribeSnapshots": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "Snapshots" + }, + "DescribeTrusts": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "Trusts" + }, + "ListIpRoutes": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "IpRoutesInfo" + }, + "ListLogSubscriptions": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "LogSubscriptions" + }, + "ListSchemaExtensions": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "SchemaExtensionsInfo" + }, + "ListTagsForResource": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "Tags" + }, + "DescribeClientAuthenticationSettings": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "ClientAuthenticationSettingsInfo" + }, + "DescribeLDAPSSettings": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "LDAPSSettingsInfo" + }, + "DescribeRegions": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "RegionsDescription" + }, + "DescribeUpdateDirectory": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "UpdateActivities" + }, + "ListCertificates": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "CertificatesInfo" + }, + "ListADAssessments": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "Assessments" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/service-2.json.gz new file mode 100644 index 0000000..799a6c6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/waiters-2.json new file mode 100644 index 0000000..962c7b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ds/2015-04-16/waiters-2.json @@ -0,0 +1,24 @@ +{ + "version": 2, + "waiters": { + "HybridADUpdated": { + "operation": "DescribeHybridADUpdate", + "delay": 120, + "maxAttempts": 60, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "UpdateActivities.SelfManagedInstances[].Status", + "expected": "Updated" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "UpdateActivities.SelfManagedInstances[].Status", + "expected": "UpdateFailed" + } + ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/dsql/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dsql/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..599f969 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dsql/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dsql/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/dsql/2018-05-10/paginators-1.json new file mode 100644 index 0000000..7249aa7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dsql/2018-05-10/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListClusters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "clusters" + }, + "ListStreams": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "streams" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dsql/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dsql/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..1213b32 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dsql/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dsql/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/dsql/2018-05-10/waiters-2.json new file mode 100644 index 0000000..73cae97 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dsql/2018-05-10/waiters-2.json @@ -0,0 +1,51 @@ +{ + "version" : 2, + "waiters" : { + "ClusterActive" : { + "description" : "Wait until a Cluster is ACTIVE", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "GetCluster", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + } ] + }, + "ClusterNotExists" : { + "description" : "Wait until a Cluster is gone", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "GetCluster", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + } ] + }, + "StreamActive" : { + "description" : "Wait until a Stream is ACTIVE", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "GetStream", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + } ] + }, + "StreamNotExists" : { + "description" : "Wait until a Stream is gone", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "GetStream", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2011-12-05/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2011-12-05/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..53e269e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2011-12-05/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2011-12-05/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2011-12-05/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2011-12-05/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c79dec2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/examples-1.json new file mode 100644 index 0000000..bbc763c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/examples-1.json @@ -0,0 +1,631 @@ +{ + "version": "1.0", + "examples": { + "BatchGetItem": [ + { + "input": { + "RequestItems": { + "Music": { + "Keys": [ + { + "Artist": { + "S": "No One You Know" + }, + "SongTitle": { + "S": "Call Me Today" + } + }, + { + "Artist": { + "S": "Acme Band" + }, + "SongTitle": { + "S": "Happy Day" + } + }, + { + "Artist": { + "S": "No One You Know" + }, + "SongTitle": { + "S": "Scared of My Shadow" + } + } + ], + "ProjectionExpression": "AlbumTitle" + } + } + }, + "output": { + "Responses": { + "Music": [ + { + "AlbumTitle": { + "S": "Somewhat Famous" + } + }, + { + "AlbumTitle": { + "S": "Blue Sky Blues" + } + }, + { + "AlbumTitle": { + "S": "Louder Than Ever" + } + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example reads multiple items from the Music table using a batch of three GetItem requests. Only the AlbumTitle attribute is returned.", + "id": "to-retrieve-multiple-items-from-a-table-1476118438992", + "title": "To retrieve multiple items from a table" + } + ], + "BatchWriteItem": [ + { + "input": { + "RequestItems": { + "Music": [ + { + "PutRequest": { + "Item": { + "AlbumTitle": { + "S": "Somewhat Famous" + }, + "Artist": { + "S": "No One You Know" + }, + "SongTitle": { + "S": "Call Me Today" + } + } + } + }, + { + "PutRequest": { + "Item": { + "AlbumTitle": { + "S": "Songs About Life" + }, + "Artist": { + "S": "Acme Band" + }, + "SongTitle": { + "S": "Happy Day" + } + } + } + }, + { + "PutRequest": { + "Item": { + "AlbumTitle": { + "S": "Blue Sky Blues" + }, + "Artist": { + "S": "No One You Know" + }, + "SongTitle": { + "S": "Scared of My Shadow" + } + } + } + } + ] + } + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds three new items to the Music table using a batch of three PutItem requests.", + "id": "to-add-multiple-items-to-a-table-1476118519747", + "title": "To add multiple items to a table" + } + ], + "CreateTable": [ + { + "input": { + "AttributeDefinitions": [ + { + "AttributeName": "Artist", + "AttributeType": "S" + }, + { + "AttributeName": "SongTitle", + "AttributeType": "S" + } + ], + "KeySchema": [ + { + "AttributeName": "Artist", + "KeyType": "HASH" + }, + { + "AttributeName": "SongTitle", + "KeyType": "RANGE" + } + ], + "ProvisionedThroughput": { + "ReadCapacityUnits": 5, + "WriteCapacityUnits": 5 + }, + "TableName": "Music" + }, + "output": { + "TableDescription": { + "AttributeDefinitions": [ + { + "AttributeName": "Artist", + "AttributeType": "S" + }, + { + "AttributeName": "SongTitle", + "AttributeType": "S" + } + ], + "CreationDateTime": "1421866952.062", + "ItemCount": 0, + "KeySchema": [ + { + "AttributeName": "Artist", + "KeyType": "HASH" + }, + { + "AttributeName": "SongTitle", + "KeyType": "RANGE" + } + ], + "ProvisionedThroughput": { + "ReadCapacityUnits": 5, + "WriteCapacityUnits": 5 + }, + "TableName": "Music", + "TableSizeBytes": 0, + "TableStatus": "CREATING" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a table named Music.", + "id": "to-create-a-table-1476116291743", + "title": "To create a table" + } + ], + "DeleteItem": [ + { + "input": { + "Key": { + "Artist": { + "S": "No One You Know" + }, + "SongTitle": { + "S": "Scared of My Shadow" + } + }, + "TableName": "Music" + }, + "output": { + "ConsumedCapacity": { + "CapacityUnits": 1, + "TableName": "Music" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes an item from the Music table.", + "id": "to-delete-an-item-1475884573758", + "title": "To delete an item" + } + ], + "DeleteTable": [ + { + "input": { + "TableName": "Music" + }, + "output": { + "TableDescription": { + "ItemCount": 0, + "ProvisionedThroughput": { + "NumberOfDecreasesToday": 1, + "ReadCapacityUnits": 5, + "WriteCapacityUnits": 5 + }, + "TableName": "Music", + "TableSizeBytes": 0, + "TableStatus": "DELETING" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the Music table.", + "id": "to-delete-a-table-1475884368755", + "title": "To delete a table" + } + ], + "DescribeLimits": [ + { + "input": { + }, + "output": { + "AccountMaxReadCapacityUnits": 20000, + "AccountMaxWriteCapacityUnits": 20000, + "TableMaxReadCapacityUnits": 10000, + "TableMaxWriteCapacityUnits": 10000 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the maximum read and write capacity units per table, and for the AWS account, in the current AWS region.", + "id": "to-determine-capacity-limits-per-table-and-account-in-the-current-aws-region-1475884162064", + "title": "To determine capacity limits per table and account, in the current AWS region" + } + ], + "DescribeTable": [ + { + "input": { + "TableName": "Music" + }, + "output": { + "Table": { + "AttributeDefinitions": [ + { + "AttributeName": "Artist", + "AttributeType": "S" + }, + { + "AttributeName": "SongTitle", + "AttributeType": "S" + } + ], + "CreationDateTime": "1421866952.062", + "ItemCount": 0, + "KeySchema": [ + { + "AttributeName": "Artist", + "KeyType": "HASH" + }, + { + "AttributeName": "SongTitle", + "KeyType": "RANGE" + } + ], + "ProvisionedThroughput": { + "NumberOfDecreasesToday": 1, + "ReadCapacityUnits": 5, + "WriteCapacityUnits": 5 + }, + "TableName": "Music", + "TableSizeBytes": 0, + "TableStatus": "ACTIVE" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the Music table.", + "id": "to-describe-a-table-1475884440502", + "title": "To describe a table" + } + ], + "GetItem": [ + { + "input": { + "Key": { + "Artist": { + "S": "Acme Band" + }, + "SongTitle": { + "S": "Happy Day" + } + }, + "TableName": "Music" + }, + "output": { + "Item": { + "AlbumTitle": { + "S": "Songs About Life" + }, + "Artist": { + "S": "Acme Band" + }, + "SongTitle": { + "S": "Happy Day" + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example retrieves an item from the Music table. The table has a partition key and a sort key (Artist and SongTitle), so you must specify both of these attributes.", + "id": "to-read-an-item-from-a-table-1475884258350", + "title": "To read an item from a table" + } + ], + "ListTables": [ + { + "input": { + }, + "output": { + "TableNames": [ + "Forum", + "ProductCatalog", + "Reply", + "Thread" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists all of the tables associated with the current AWS account and endpoint.", + "id": "to-list-tables-1475884741238", + "title": "To list tables" + } + ], + "PutItem": [ + { + "input": { + "Item": { + "AlbumTitle": { + "S": "Somewhat Famous" + }, + "Artist": { + "S": "No One You Know" + }, + "SongTitle": { + "S": "Call Me Today" + } + }, + "ReturnConsumedCapacity": "TOTAL", + "TableName": "Music" + }, + "output": { + "ConsumedCapacity": { + "CapacityUnits": 1, + "TableName": "Music" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds a new item to the Music table.", + "id": "to-add-an-item-to-a-table-1476116191110", + "title": "To add an item to a table" + } + ], + "Query": [ + { + "input": { + "ExpressionAttributeValues": { + ":v1": { + "S": "No One You Know" + } + }, + "KeyConditionExpression": "Artist = :v1", + "ProjectionExpression": "SongTitle", + "TableName": "Music" + }, + "output": { + "ConsumedCapacity": { + }, + "Count": 2, + "Items": [ + { + "SongTitle": { + "S": "Call Me Today" + } + } + ], + "ScannedCount": 2 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example queries items in the Music table. The table has a partition key and sort key (Artist and SongTitle), but this query only specifies the partition key value. It returns song titles by the artist named \"No One You Know\".", + "id": "to-query-an-item-1475883874631", + "title": "To query an item" + } + ], + "Scan": [ + { + "input": { + "ExpressionAttributeNames": { + "#AT": "AlbumTitle", + "#ST": "SongTitle" + }, + "ExpressionAttributeValues": { + ":a": { + "S": "No One You Know" + } + }, + "FilterExpression": "Artist = :a", + "ProjectionExpression": "#ST, #AT", + "TableName": "Music" + }, + "output": { + "ConsumedCapacity": { + }, + "Count": 2, + "Items": [ + { + "AlbumTitle": { + "S": "Somewhat Famous" + }, + "SongTitle": { + "S": "Call Me Today" + } + }, + { + "AlbumTitle": { + "S": "Blue Sky Blues" + }, + "SongTitle": { + "S": "Scared of My Shadow" + } + } + ], + "ScannedCount": 3 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example scans the entire Music table, and then narrows the results to songs by the artist \"No One You Know\". For each item, only the album title and song title are returned.", + "id": "to-scan-a-table-1475883652470", + "title": "To scan a table" + } + ], + "UpdateItem": [ + { + "input": { + "ExpressionAttributeNames": { + "#AT": "AlbumTitle", + "#Y": "Year" + }, + "ExpressionAttributeValues": { + ":t": { + "S": "Louder Than Ever" + }, + ":y": { + "N": "2015" + } + }, + "Key": { + "Artist": { + "S": "Acme Band" + }, + "SongTitle": { + "S": "Happy Day" + } + }, + "ReturnValues": "ALL_NEW", + "TableName": "Music", + "UpdateExpression": "SET #Y = :y, #AT = :t" + }, + "output": { + "Attributes": { + "AlbumTitle": { + "S": "Louder Than Ever" + }, + "Artist": { + "S": "Acme Band" + }, + "SongTitle": { + "S": "Happy Day" + }, + "Year": { + "N": "2015" + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example updates an item in the Music table. It adds a new attribute (Year) and modifies the AlbumTitle attribute. All of the attributes in the item, as they appear after the update, are returned in the response.", + "id": "to-update-an-item-in-a-table-1476118250055", + "title": "To update an item in a table" + } + ], + "UpdateTable": [ + { + "input": { + "ProvisionedThroughput": { + "ReadCapacityUnits": 10, + "WriteCapacityUnits": 10 + }, + "TableName": "MusicCollection" + }, + "output": { + "TableDescription": { + "AttributeDefinitions": [ + { + "AttributeName": "Artist", + "AttributeType": "S" + }, + { + "AttributeName": "SongTitle", + "AttributeType": "S" + } + ], + "CreationDateTime": "1421866952.062", + "ItemCount": 0, + "KeySchema": [ + { + "AttributeName": "Artist", + "KeyType": "HASH" + }, + { + "AttributeName": "SongTitle", + "KeyType": "RANGE" + } + ], + "ProvisionedThroughput": { + "LastIncreaseDateTime": "1421874759.194", + "NumberOfDecreasesToday": 1, + "ReadCapacityUnits": 1, + "WriteCapacityUnits": 1 + }, + "TableName": "MusicCollection", + "TableSizeBytes": 0, + "TableStatus": "UPDATING" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example increases the provisioned read and write capacity on the Music table.", + "id": "to-modify-a-tables-provisioned-throughput-1476118076147", + "title": "To modify a table's provisioned throughput" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/paginators-1.json new file mode 100644 index 0000000..8e10a0c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/paginators-1.json @@ -0,0 +1,47 @@ +{ + "pagination": { + "ListBackups": { + "input_token": "ExclusiveStartBackupArn", + "output_token": "LastEvaluatedBackupArn", + "limit_key": "Limit", + "result_key": "BackupSummaries" + }, + "ListTables": { + "input_token": "ExclusiveStartTableName", + "output_token": "LastEvaluatedTableName", + "limit_key": "Limit", + "result_key": "TableNames" + }, + "Query": { + "input_token": "ExclusiveStartKey", + "output_token": "LastEvaluatedKey", + "limit_key": "Limit", + "result_key": [ + "Items", + "Count", + "ScannedCount" + ], + "non_aggregate_keys": [ + "ConsumedCapacity" + ] + }, + "Scan": { + "input_token": "ExclusiveStartKey", + "output_token": "LastEvaluatedKey", + "limit_key": "Limit", + "result_key": [ + "Items", + "Count", + "ScannedCount" + ], + "non_aggregate_keys": [ + "ConsumedCapacity" + ] + }, + "ListTagsOfResource": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Tags" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/service-2.json.gz new file mode 100644 index 0000000..d2e627e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/waiters-2.json new file mode 100644 index 0000000..5635258 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dynamodb/2012-08-10/waiters-2.json @@ -0,0 +1,117 @@ +{ + "version": 2, + "waiters": { + "ContributorInsightsEnabled": { + "operation": "DescribeContributorInsights", + "delay": 20, + "maxAttempts": 30, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "ContributorInsightsStatus", + "expected": "ENABLED" + }, + { + "state": "failure", + "matcher": "path", + "argument": "ContributorInsightsStatus", + "expected": "FAILED" + } + ] + }, + "ExportCompleted": { + "operation": "DescribeExport", + "delay": 20, + "maxAttempts": 60, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "ExportDescription.ExportStatus", + "expected": "COMPLETED" + }, + { + "state": "failure", + "matcher": "path", + "argument": "ExportDescription.ExportStatus", + "expected": "FAILED" + } + ] + }, + "ImportCompleted": { + "operation": "DescribeImport", + "delay": 20, + "maxAttempts": 60, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "ImportTableDescription.ImportStatus", + "expected": "COMPLETED" + }, + { + "state": "failure", + "matcher": "path", + "argument": "ImportTableDescription.ImportStatus", + "expected": "FAILED" + }, + { + "state": "failure", + "matcher": "path", + "argument": "ImportTableDescription.ImportStatus", + "expected": "CANCELLED" + } + ] + }, + "KinesisStreamingDestinationActive": { + "operation": "DescribeKinesisStreamingDestination", + "delay": 20, + "maxAttempts": 30, + "acceptors": [ + { + "state": "success", + "matcher": "pathAny", + "argument": "KinesisDataStreamDestinations[].DestinationStatus", + "expected": "ACTIVE" + }, + { + "state": "failure", + "matcher": "path", + "argument": "length(KinesisDataStreamDestinations) > `0` && length(KinesisDataStreamDestinations[?DestinationStatus == 'DISABLED' || DestinationStatus == 'ENABLE_FAILED']) == length(KinesisDataStreamDestinations)", + "expected": true + } + ] + }, + "TableExists": { + "delay": 20, + "operation": "DescribeTable", + "maxAttempts": 25, + "acceptors": [ + { + "expected": "ACTIVE", + "matcher": "path", + "state": "success", + "argument": "Table.TableStatus" + }, + { + "expected": "ResourceNotFoundException", + "matcher": "error", + "state": "retry" + } + ] + }, + "TableNotExists": { + "delay": 20, + "operation": "DescribeTable", + "maxAttempts": 25, + "acceptors": [ + { + "expected": "ResourceNotFoundException", + "matcher": "error", + "state": "success" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dynamodbstreams/2012-08-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dynamodbstreams/2012-08-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..2ad465c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dynamodbstreams/2012-08-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/dynamodbstreams/2012-08-10/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/dynamodbstreams/2012-08-10/examples-1.json new file mode 100644 index 0000000..8287e2c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dynamodbstreams/2012-08-10/examples-1.json @@ -0,0 +1,212 @@ +{ + "version": "1.0", + "examples": { + "DescribeStream": [ + { + "input": { + "StreamArn": "arn:aws:dynamodb:us-west-2:111122223333:table/Forum/stream/2015-05-20T20:51:10.252" + }, + "output": { + "StreamDescription": { + "CreationRequestDateTime": "Wed May 20 13:51:10 PDT 2015", + "KeySchema": [ + { + "AttributeName": "ForumName", + "KeyType": "HASH" + }, + { + "AttributeName": "Subject", + "KeyType": "RANGE" + } + ], + "Shards": [ + { + "SequenceNumberRange": { + "EndingSequenceNumber": "20500000000000000910398", + "StartingSequenceNumber": "20500000000000000910398" + }, + "ShardId": "shardId-00000001414562045508-2bac9cd2" + }, + { + "ParentShardId": "shardId-00000001414562045508-2bac9cd2", + "SequenceNumberRange": { + "EndingSequenceNumber": "820400000000000001192334", + "StartingSequenceNumber": "820400000000000001192334" + }, + "ShardId": "shardId-00000001414576573621-f55eea83" + }, + { + "ParentShardId": "shardId-00000001414576573621-f55eea83", + "SequenceNumberRange": { + "EndingSequenceNumber": "1683700000000000001135967", + "StartingSequenceNumber": "1683700000000000001135967" + }, + "ShardId": "shardId-00000001414592258131-674fd923" + }, + { + "ParentShardId": "shardId-00000001414592258131-674fd923", + "SequenceNumberRange": { + "StartingSequenceNumber": "2574600000000000000935255" + }, + "ShardId": "shardId-00000001414608446368-3a1afbaf" + } + ], + "StreamArn": "arn:aws:dynamodb:us-west-2:111122223333:table/Forum/stream/2015-05-20T20:51:10.252", + "StreamLabel": "2015-05-20T20:51:10.252", + "StreamStatus": "ENABLED", + "StreamViewType": "NEW_AND_OLD_IMAGES", + "TableName": "Forum" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example describes a stream with a given stream ARN.", + "id": "to-describe-a-stream-with-a-given-stream-arn-1473457835200", + "title": "To describe a stream with a given stream ARN" + } + ], + "GetRecords": [ + { + "input": { + "ShardIterator": "arn:aws:dynamodb:us-west-2:111122223333:table/Forum/stream/2015-05-20T20:51:10.252|1|AAAAAAAAAAEvJp6D+zaQ... ..." + }, + "output": { + "NextShardIterator": "arn:aws:dynamodb:us-west-2:111122223333:table/Forum/stream/2015-05-20T20:51:10.252|1|AAAAAAAAAAGQBYshYDEe ... ...", + "Records": [ + { + "awsRegion": "us-west-2", + "dynamodb": { + "ApproximateCreationDateTime": "1.46480646E9", + "Keys": { + "ForumName": { + "S": "DynamoDB" + }, + "Subject": { + "S": "DynamoDB Thread 3" + } + }, + "SequenceNumber": "300000000000000499659", + "SizeBytes": 41, + "StreamViewType": "KEYS_ONLY" + }, + "eventID": "e2fd9c34eff2d779b297b26f5fef4206", + "eventName": "INSERT", + "eventSource": "aws:dynamodb", + "eventVersion": "1.0" + }, + { + "awsRegion": "us-west-2", + "dynamodb": { + "ApproximateCreationDateTime": "1.46480527E9", + "Keys": { + "ForumName": { + "S": "DynamoDB" + }, + "Subject": { + "S": "DynamoDB Thread 1" + } + }, + "SequenceNumber": "400000000000000499660", + "SizeBytes": 41, + "StreamViewType": "KEYS_ONLY" + }, + "eventID": "4b25bd0da9a181a155114127e4837252", + "eventName": "MODIFY", + "eventSource": "aws:dynamodb", + "eventVersion": "1.0" + }, + { + "awsRegion": "us-west-2", + "dynamodb": { + "ApproximateCreationDateTime": "1.46480646E9", + "Keys": { + "ForumName": { + "S": "DynamoDB" + }, + "Subject": { + "S": "DynamoDB Thread 2" + } + }, + "SequenceNumber": "500000000000000499661", + "SizeBytes": 41, + "StreamViewType": "KEYS_ONLY" + }, + "eventID": "740280c73a3df7842edab3548a1b08ad", + "eventName": "REMOVE", + "eventSource": "aws:dynamodb", + "eventVersion": "1.0" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves all the stream records from a shard.", + "id": "to-retrieve-all-the-stream-records-from-a-shard-1473707781419", + "title": "To retrieve all the stream records from a shard" + } + ], + "GetShardIterator": [ + { + "input": { + "ShardId": "00000001414576573621-f55eea83", + "ShardIteratorType": "TRIM_HORIZON", + "StreamArn": "arn:aws:dynamodb:us-west-2:111122223333:table/Forum/stream/2015-05-20T20:51:10.252" + }, + "output": { + "ShardIterator": "arn:aws:dynamodb:us-west-2:111122223333:table/Forum/stream/2015-05-20T20:51:10.252|1|AAAAAAAAAAEvJp6D+zaQ... ..." + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns a shard iterator for the provided stream ARN and shard ID.", + "id": "to-obtain-a-shard-iterator-for-the-provided-stream-arn-and-shard-id-1473459941476", + "title": "To obtain a shard iterator for the provided stream ARN and shard ID" + } + ], + "ListStreams": [ + { + "input": { + }, + "output": { + "Streams": [ + { + "StreamArn": "arn:aws:dynamodb:us-wesst-2:111122223333:table/Forum/stream/2015-05-20T20:51:10.252", + "StreamLabel": "2015-05-20T20:51:10.252", + "TableName": "Forum" + }, + { + "StreamArn": "arn:aws:dynamodb:us-west-2:111122223333:table/Forum/stream/2015-05-20T20:50:02.714", + "StreamLabel": "2015-05-20T20:50:02.714", + "TableName": "Forum" + }, + { + "StreamArn": "arn:aws:dynamodb:us-west-2:111122223333:table/Forum/stream/2015-05-19T23:03:50.641", + "StreamLabel": "2015-05-19T23:03:50.641", + "TableName": "Forum" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example lists all of the stream ARNs.", + "id": "to-list-all-of-the-stream-arns--1473459534285", + "title": "To list all of the stream ARNs " + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dynamodbstreams/2012-08-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/dynamodbstreams/2012-08-10/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/dynamodbstreams/2012-08-10/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/dynamodbstreams/2012-08-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/dynamodbstreams/2012-08-10/service-2.json.gz new file mode 100644 index 0000000..aeceed2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/dynamodbstreams/2012-08-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ebs/2019-11-02/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ebs/2019-11-02/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..33258fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ebs/2019-11-02/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ebs/2019-11-02/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ebs/2019-11-02/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ebs/2019-11-02/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ebs/2019-11-02/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ebs/2019-11-02/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ebs/2019-11-02/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ebs/2019-11-02/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ebs/2019-11-02/service-2.json.gz new file mode 100644 index 0000000..a1c7387 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ebs/2019-11-02/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2-instance-connect/2018-04-02/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2-instance-connect/2018-04-02/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b9bba5b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2-instance-connect/2018-04-02/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2-instance-connect/2018-04-02/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2-instance-connect/2018-04-02/examples-1.json new file mode 100644 index 0000000..c5c6001 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2-instance-connect/2018-04-02/examples-1.json @@ -0,0 +1,34 @@ +{ + "version": "1.0", + "examples": { + "SendSSHPublicKey": [ + { + "input": { + "AvailabilityZone": "us-west-2a", + "InstanceId": "i-abcd1234", + "InstanceOSUser": "ec2-user", + "SSHPublicKey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3FlHqj2eqCdrGHuA6dRjfZXQ4HX5lXEIRHaNbxEwE5Te7xNF7StwhrDtiV7IdT5fDqbRyGw/szPj3xGkNTVoElCZ2dDFb2qYZ1WLIpZwj/UhO9l2mgfjR56UojjQut5Jvn2KZ1OcyrNO0J83kCaJCV7JoVbXY79FBMUccYNY45zmv9+1FMCfY6i2jdIhwR6+yLk8oubL8lIPyq7X+6b9S0yKCkB7Peml1DvghlybpAIUrC9vofHt6XP4V1i0bImw1IlljQS+DUmULRFSccATDscCX9ajnj7Crhm0HAZC0tBPXpFdHkPwL3yzYo546SCS9LKEwz62ymxxbL9k7h09t" + }, + "output": { + "RequestId": "abcd1234-abcd-1234-abcd-1234abcd1234", + "Success": true + }, + "comments": { + "input": { + "AvailabilityZone": "The zone where the instance was launched", + "InstanceId": "The instance ID to publish the key to.", + "InstanceOSUser": "This should be the user you wish to be when ssh-ing to the instance (eg, ec2-user@[instance IP])", + "SSHPublicKey": "This should be in standard OpenSSH format (ssh-rsa [key body])" + }, + "output": { + "RequestId": "This request ID should be provided when contacting AWS Support.", + "Success": "Should be true if the service does not return an error response." + } + }, + "description": "The following example pushes a sample SSH public key to the EC2 instance i-abcd1234 in AZ us-west-2b for use by the instance OS user ec2-user.", + "id": "send-ssh-key-to-an-ec2-instance-1518124883100", + "title": "To push an SSH key to an EC2 instance" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2-instance-connect/2018-04-02/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2-instance-connect/2018-04-02/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2-instance-connect/2018-04-02/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2-instance-connect/2018-04-02/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2-instance-connect/2018-04-02/service-2.json.gz new file mode 100644 index 0000000..a1c22a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2-instance-connect/2018-04-02/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-09-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-09-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6e571c8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-09-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-09-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-09-01/paginators-1.json new file mode 100644 index 0000000..b643e69 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-09-01/paginators-1.json @@ -0,0 +1,45 @@ +{ + "pagination": { + "DescribeInstanceStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceStatuses" + }, + "DescribeInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Reservations" + }, + "DescribeReservedInstancesModifications": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ReservedInstancesModifications" + }, + "DescribeReservedInstancesOfferings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ReservedInstancesOfferings" + }, + "DescribeSpotPriceHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpotPriceHistory" + }, + "DescribeTags": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Tags" + }, + "DescribeVolumeStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VolumeStatuses" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-09-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-09-01/service-2.json.gz new file mode 100644 index 0000000..23b04ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-09-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-09-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-09-01/waiters-2.json new file mode 100644 index 0000000..fb8c16b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-09-01/waiters-2.json @@ -0,0 +1,341 @@ +{ + "version": 2, + "waiters": { + "BundleTaskComplete": { + "delay": 15, + "operation": "DescribeBundleTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "complete", + "matcher": "pathAll", + "state": "success", + "argument": "BundleTasks[].State" + }, + { + "expected": "failed", + "matcher": "pathAny", + "state": "failure", + "argument": "BundleTasks[].State" + } + ] + }, + "ConversionTaskCancelled": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskCompleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelled", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelling", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskDeleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "CustomerGatewayAvailable": { + "delay": 15, + "operation": "DescribeCustomerGateways", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + } + ] + }, + "ExportTaskCancelled": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ExportTaskCompleted": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "InstanceRunning": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "running", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "shutting-down", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "InstanceStopped": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "stopped", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "InstanceTerminated": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "terminated", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "SnapshotCompleted": { + "delay": 15, + "operation": "DescribeSnapshots", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "Snapshots[].State" + } + ] + }, + "SubnetAvailable": { + "delay": 15, + "operation": "DescribeSubnets", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Subnets[].State" + } + ] + }, + "VolumeAvailable": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VolumeDeleted": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + } + ] + }, + "VolumeInUse": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "in-use", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VpcAvailable": { + "delay": 15, + "operation": "DescribeVpcs", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Vpcs[].State" + } + ] + }, + "VpnConnectionAvailable": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + }, + "VpnConnectionDeleted": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-10-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-10-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6e571c8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-10-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-10-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-10-01/paginators-1.json new file mode 100644 index 0000000..ca7a876 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-10-01/paginators-1.json @@ -0,0 +1,51 @@ +{ + "pagination": { + "DescribeInstanceStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceStatuses" + }, + "DescribeInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Reservations" + }, + "DescribeReservedInstancesModifications": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ReservedInstancesModifications" + }, + "DescribeReservedInstancesOfferings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ReservedInstancesOfferings" + }, + "DescribeSpotPriceHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpotPriceHistory" + }, + "DescribeTags": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Tags" + }, + "DescribeVolumeStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VolumeStatuses" + }, + "DescribeSnapshots": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Snapshots" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-10-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-10-01/service-2.json.gz new file mode 100644 index 0000000..e438f20 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-10-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-10-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-10-01/waiters-2.json new file mode 100644 index 0000000..17f0870 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2014-10-01/waiters-2.json @@ -0,0 +1,436 @@ +{ + "version": 2, + "waiters": { + "BundleTaskComplete": { + "delay": 15, + "operation": "DescribeBundleTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "complete", + "matcher": "pathAll", + "state": "success", + "argument": "BundleTasks[].State" + }, + { + "expected": "failed", + "matcher": "pathAny", + "state": "failure", + "argument": "BundleTasks[].State" + } + ] + }, + "ConversionTaskCancelled": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskCompleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelled", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelling", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskDeleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "CustomerGatewayAvailable": { + "delay": 15, + "operation": "DescribeCustomerGateways", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + } + ] + }, + "ExportTaskCancelled": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ExportTaskCompleted": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ImageAvailable": { + "operation": "DescribeImages", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Images[].State", + "expected": "available" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "Images[].State", + "expected": "failed" + } + ] + }, + "InstanceRunning": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "running", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "shutting-down", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "InstanceStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].InstanceStatus.Status", + "expected": "ok" + } + ] + }, + "InstanceStopped": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "stopped", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "InstanceTerminated": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "terminated", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "PasswordDataAvailable": { + "operation": "GetPasswordData", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "length(PasswordData) > `0`", + "expected": true + } + ] + }, + "SnapshotCompleted": { + "delay": 15, + "operation": "DescribeSnapshots", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "Snapshots[].State" + } + ] + }, + "SpotInstanceRequestFulfilled": { + "operation": "DescribeSpotInstanceRequests", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "fulfilled" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "schedule-expired" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "canceled-before-fulfillment" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "bad-parameters" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "system-error" + } + ] + }, + "SubnetAvailable": { + "delay": 15, + "operation": "DescribeSubnets", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Subnets[].State" + } + ] + }, + "SystemStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].SystemStatus.Status", + "expected": "ok" + } + ] + }, + "VolumeAvailable": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VolumeDeleted": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + } + ] + }, + "VolumeInUse": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "in-use", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VpcAvailable": { + "delay": 15, + "operation": "DescribeVpcs", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Vpcs[].State" + } + ] + }, + "VpnConnectionAvailable": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + }, + "VpnConnectionDeleted": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-03-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-03-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6e571c8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-03-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-03-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-03-01/paginators-1.json new file mode 100644 index 0000000..ca7a876 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-03-01/paginators-1.json @@ -0,0 +1,51 @@ +{ + "pagination": { + "DescribeInstanceStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceStatuses" + }, + "DescribeInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Reservations" + }, + "DescribeReservedInstancesModifications": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ReservedInstancesModifications" + }, + "DescribeReservedInstancesOfferings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ReservedInstancesOfferings" + }, + "DescribeSpotPriceHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpotPriceHistory" + }, + "DescribeTags": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Tags" + }, + "DescribeVolumeStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VolumeStatuses" + }, + "DescribeSnapshots": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Snapshots" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-03-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-03-01/service-2.json.gz new file mode 100644 index 0000000..4aa98c2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-03-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-03-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-03-01/waiters-2.json new file mode 100644 index 0000000..17f0870 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-03-01/waiters-2.json @@ -0,0 +1,436 @@ +{ + "version": 2, + "waiters": { + "BundleTaskComplete": { + "delay": 15, + "operation": "DescribeBundleTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "complete", + "matcher": "pathAll", + "state": "success", + "argument": "BundleTasks[].State" + }, + { + "expected": "failed", + "matcher": "pathAny", + "state": "failure", + "argument": "BundleTasks[].State" + } + ] + }, + "ConversionTaskCancelled": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskCompleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelled", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelling", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskDeleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "CustomerGatewayAvailable": { + "delay": 15, + "operation": "DescribeCustomerGateways", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + } + ] + }, + "ExportTaskCancelled": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ExportTaskCompleted": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ImageAvailable": { + "operation": "DescribeImages", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Images[].State", + "expected": "available" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "Images[].State", + "expected": "failed" + } + ] + }, + "InstanceRunning": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "running", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "shutting-down", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "InstanceStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].InstanceStatus.Status", + "expected": "ok" + } + ] + }, + "InstanceStopped": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "stopped", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "InstanceTerminated": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "terminated", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "PasswordDataAvailable": { + "operation": "GetPasswordData", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "length(PasswordData) > `0`", + "expected": true + } + ] + }, + "SnapshotCompleted": { + "delay": 15, + "operation": "DescribeSnapshots", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "Snapshots[].State" + } + ] + }, + "SpotInstanceRequestFulfilled": { + "operation": "DescribeSpotInstanceRequests", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "fulfilled" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "schedule-expired" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "canceled-before-fulfillment" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "bad-parameters" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "system-error" + } + ] + }, + "SubnetAvailable": { + "delay": 15, + "operation": "DescribeSubnets", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Subnets[].State" + } + ] + }, + "SystemStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].SystemStatus.Status", + "expected": "ok" + } + ] + }, + "VolumeAvailable": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VolumeDeleted": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + } + ] + }, + "VolumeInUse": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "in-use", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VpcAvailable": { + "delay": 15, + "operation": "DescribeVpcs", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Vpcs[].State" + } + ] + }, + "VpnConnectionAvailable": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + }, + "VpnConnectionDeleted": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-04-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-04-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6e571c8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-04-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-04-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-04-15/paginators-1.json new file mode 100644 index 0000000..ca7a876 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-04-15/paginators-1.json @@ -0,0 +1,51 @@ +{ + "pagination": { + "DescribeInstanceStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceStatuses" + }, + "DescribeInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Reservations" + }, + "DescribeReservedInstancesModifications": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ReservedInstancesModifications" + }, + "DescribeReservedInstancesOfferings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ReservedInstancesOfferings" + }, + "DescribeSpotPriceHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpotPriceHistory" + }, + "DescribeTags": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Tags" + }, + "DescribeVolumeStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VolumeStatuses" + }, + "DescribeSnapshots": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Snapshots" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-04-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-04-15/service-2.json.gz new file mode 100644 index 0000000..53cecd4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-04-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-04-15/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-04-15/waiters-2.json new file mode 100644 index 0000000..5a6dbbc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-04-15/waiters-2.json @@ -0,0 +1,458 @@ +{ + "version": 2, + "waiters": { + "InstanceExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeInstances", + "acceptors": [ + { + "matcher": "status", + "expected": 200, + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidInstanceIDNotFound", + "state": "retry" + } + ] + }, + "BundleTaskComplete": { + "delay": 15, + "operation": "DescribeBundleTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "complete", + "matcher": "pathAll", + "state": "success", + "argument": "BundleTasks[].State" + }, + { + "expected": "failed", + "matcher": "pathAny", + "state": "failure", + "argument": "BundleTasks[].State" + } + ] + }, + "ConversionTaskCancelled": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskCompleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelled", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelling", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskDeleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "CustomerGatewayAvailable": { + "delay": 15, + "operation": "DescribeCustomerGateways", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + } + ] + }, + "ExportTaskCancelled": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ExportTaskCompleted": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ImageAvailable": { + "operation": "DescribeImages", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Images[].State", + "expected": "available" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "Images[].State", + "expected": "failed" + } + ] + }, + "InstanceRunning": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "running", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "shutting-down", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "InstanceStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].InstanceStatus.Status", + "expected": "ok" + } + ] + }, + "InstanceStopped": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "stopped", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "InstanceTerminated": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "terminated", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "PasswordDataAvailable": { + "operation": "GetPasswordData", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "length(PasswordData) > `0`", + "expected": true + } + ] + }, + "SnapshotCompleted": { + "delay": 15, + "operation": "DescribeSnapshots", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "Snapshots[].State" + } + ] + }, + "SpotInstanceRequestFulfilled": { + "operation": "DescribeSpotInstanceRequests", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "fulfilled" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "schedule-expired" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "canceled-before-fulfillment" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "bad-parameters" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "system-error" + } + ] + }, + "SubnetAvailable": { + "delay": 15, + "operation": "DescribeSubnets", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Subnets[].State" + } + ] + }, + "SystemStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].SystemStatus.Status", + "expected": "ok" + } + ] + }, + "VolumeAvailable": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VolumeDeleted": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "matcher": "error", + "expected": "InvalidVolumeNotFound", + "state": "success" + } + ] + }, + "VolumeInUse": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "in-use", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VpcAvailable": { + "delay": 15, + "operation": "DescribeVpcs", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Vpcs[].State" + } + ] + }, + "VpnConnectionAvailable": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + }, + "VpnConnectionDeleted": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a830d3d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/paginators-1.json new file mode 100644 index 0000000..2bd01ad --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/paginators-1.json @@ -0,0 +1,63 @@ +{ + "pagination": { + "DescribeInstanceStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceStatuses" + }, + "DescribeInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Reservations" + }, + "DescribeReservedInstancesOfferings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ReservedInstancesOfferings" + }, + "DescribeReservedInstancesModifications": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ReservedInstancesModifications" + }, + "DescribeSnapshots": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Snapshots" + }, + "DescribeSpotFleetRequests": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpotFleetRequestConfigs" + }, + "DescribeSpotPriceHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpotPriceHistory" + }, + "DescribeTags": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Tags" + }, + "DescribeVolumeStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VolumeStatuses" + }, + "DescribeVolumes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Volumes" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/service-2.json.gz new file mode 100644 index 0000000..a6a6b66 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/waiters-2.json new file mode 100644 index 0000000..652a8ca --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2015-10-01/waiters-2.json @@ -0,0 +1,589 @@ +{ + "version": 2, + "waiters": { + "InstanceExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeInstances", + "acceptors": [ + { + "matcher": "path", + "expected": true, + "argument": "length(Reservations[]) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidInstanceID.NotFound", + "state": "retry" + } + ] + }, + "BundleTaskComplete": { + "delay": 15, + "operation": "DescribeBundleTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "complete", + "matcher": "pathAll", + "state": "success", + "argument": "BundleTasks[].State" + }, + { + "expected": "failed", + "matcher": "pathAny", + "state": "failure", + "argument": "BundleTasks[].State" + } + ] + }, + "ConsoleOutputAvailable": { + "operation": "GetConsoleOutput", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "length(Output || '') > `0`", + "expected": true + } + ] + }, + "ConversionTaskCancelled": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskCompleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelled", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelling", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskDeleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "CustomerGatewayAvailable": { + "delay": 15, + "operation": "DescribeCustomerGateways", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + } + ] + }, + "ExportTaskCancelled": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ExportTaskCompleted": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ImageExists": { + "operation": "DescribeImages", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "matcher": "path", + "expected": true, + "argument": "length(Images[]) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidAMIID.NotFound", + "state": "retry" + } + ] + }, + "ImageAvailable": { + "operation": "DescribeImages", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Images[].State", + "expected": "available" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "Images[].State", + "expected": "failed" + } + ] + }, + "InstanceRunning": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "running", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "shutting-down", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "matcher": "error", + "expected": "InvalidInstanceID.NotFound", + "state": "retry" + } + ] + }, + "InstanceStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].InstanceStatus.Status", + "expected": "ok" + }, + { + "matcher": "error", + "expected": "InvalidInstanceID.NotFound", + "state": "retry" + } + ] + }, + "InstanceStopped": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "stopped", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "InstanceTerminated": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "terminated", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "KeyPairExists": { + "operation": "DescribeKeyPairs", + "delay": 5, + "maxAttempts": 6, + "acceptors": [ + { + "expected": true, + "matcher": "pathAll", + "state": "success", + "argument": "length(KeyPairs[].KeyName) > `0`" + }, + { + "expected": "InvalidKeyPair.NotFound", + "matcher": "error", + "state": "retry" + } + ] + }, + "NatGatewayAvailable": { + "operation": "DescribeNatGateways", + "delay": 15, + "maxAttempts": 40, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "NatGateways[].State", + "expected": "available" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "NatGateways[].State", + "expected": "failed" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "NatGateways[].State", + "expected": "deleting" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "NatGateways[].State", + "expected": "deleted" + }, + { + "state": "retry", + "matcher": "error", + "expected": "NatGatewayNotFound" + } + ] + }, + "NetworkInterfaceAvailable": { + "operation": "DescribeNetworkInterfaces", + "delay": 20, + "maxAttempts": 10, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "NetworkInterfaces[].Status" + }, + { + "expected": "InvalidNetworkInterfaceID.NotFound", + "matcher": "error", + "state": "failure" + } + ] + }, + "PasswordDataAvailable": { + "operation": "GetPasswordData", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "length(PasswordData) > `0`", + "expected": true + } + ] + }, + "SnapshotCompleted": { + "delay": 15, + "operation": "DescribeSnapshots", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "Snapshots[].State" + } + ] + }, + "SpotInstanceRequestFulfilled": { + "operation": "DescribeSpotInstanceRequests", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "fulfilled" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "schedule-expired" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "canceled-before-fulfillment" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "bad-parameters" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "system-error" + } + ] + }, + "SubnetAvailable": { + "delay": 15, + "operation": "DescribeSubnets", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Subnets[].State" + } + ] + }, + "SystemStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].SystemStatus.Status", + "expected": "ok" + } + ] + }, + "VolumeAvailable": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VolumeDeleted": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "matcher": "error", + "expected": "InvalidVolume.NotFound", + "state": "success" + } + ] + }, + "VolumeInUse": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "in-use", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VpcAvailable": { + "delay": 15, + "operation": "DescribeVpcs", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Vpcs[].State" + } + ] + }, + "VpnConnectionAvailable": { + "delay": 60, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + }, + "VpnConnectionDeleted": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + }, + "VpcPeeringConnectionExists": { + "delay": 15, + "operation": "DescribeVpcPeeringConnections", + "maxAttempts": 40, + "acceptors": [ + { + "matcher": "status", + "expected": 200, + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidVpcPeeringConnectionID.NotFound", + "state": "retry" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a830d3d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/examples-1.json new file mode 100644 index 0000000..3f584e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/examples-1.json @@ -0,0 +1,3729 @@ +{ + "version": "1.0", + "examples": { + "AllocateAddress": [ + { + "input": { + "Domain": "vpc" + }, + "output": { + "AllocationId": "eipalloc-64d5890a", + "Domain": "vpc", + "PublicIp": "203.0.113.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example allocates an Elastic IP address to use with an instance in a VPC.", + "id": "ec2-allocate-address-1", + "title": "To allocate an Elastic IP address for EC2-VPC" + }, + { + "output": { + "Domain": "standard", + "PublicIp": "198.51.100.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example allocates an Elastic IP address to use with an instance in EC2-Classic.", + "id": "ec2-allocate-address-2", + "title": "To allocate an Elastic IP address for EC2-Classic" + } + ], + "AssignPrivateIpAddresses": [ + { + "input": { + "NetworkInterfaceId": "eni-e5aa89a3", + "PrivateIpAddresses": [ + "10.0.0.82" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example assigns the specified secondary private IP address to the specified network interface.", + "id": "ec2-assign-private-ip-addresses-1", + "title": "To assign a specific secondary private IP address to an interface" + }, + { + "input": { + "NetworkInterfaceId": "eni-e5aa89a3", + "SecondaryPrivateIpAddressCount": 2 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example assigns two secondary private IP addresses to the specified network interface. Amazon EC2 automatically assigns these IP addresses from the available IP addresses in the CIDR block range of the subnet the network interface is associated with.", + "id": "ec2-assign-private-ip-addresses-2", + "title": "To assign secondary private IP addresses that Amazon EC2 selects to an interface" + } + ], + "AssociateAddress": [ + { + "input": { + "AllocationId": "eipalloc-64d5890a", + "InstanceId": "i-0b263919b6498b123" + }, + "output": { + "AssociationId": "eipassoc-2bebb745" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified Elastic IP address with the specified instance in a VPC.", + "id": "ec2-associate-address-1", + "title": "To associate an Elastic IP address in EC2-VPC" + }, + { + "input": { + "AllocationId": "eipalloc-64d5890a", + "NetworkInterfaceId": "eni-1a2b3c4d" + }, + "output": { + "AssociationId": "eipassoc-2bebb745" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified Elastic IP address with the specified network interface.", + "id": "ec2-associate-address-2", + "title": "To associate an Elastic IP address with a network interface" + }, + { + "input": { + "InstanceId": "i-07ffe74c7330ebf53", + "PublicIp": "198.51.100.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates an Elastic IP address with an instance in EC2-Classic.", + "id": "ec2-associate-address-3", + "title": "To associate an Elastic IP address in EC2-Classic" + } + ], + "AssociateDhcpOptions": [ + { + "input": { + "DhcpOptionsId": "dopt-d9070ebb", + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified DHCP options set with the specified VPC.", + "id": "ec2-associate-dhcp-options-1", + "title": "To associate a DHCP options set with a VPC" + }, + { + "input": { + "DhcpOptionsId": "default", + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the default DHCP options set with the specified VPC.", + "id": "ec2-associate-dhcp-options-2", + "title": "To associate the default DHCP options set with a VPC" + } + ], + "AssociateRouteTable": [ + { + "input": { + "RouteTableId": "rtb-22574640", + "SubnetId": "subnet-9d4a7b6" + }, + "output": { + "AssociationId": "rtbassoc-781d0d1a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified route table with the specified subnet.", + "id": "ec2-associate-route-table-1", + "title": "To associate a route table with a subnet" + } + ], + "AttachInternetGateway": [ + { + "input": { + "InternetGatewayId": "igw-c0a643a9", + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example attaches the specified Internet gateway to the specified VPC.", + "id": "ec2-attach-internet-gateway-1", + "title": "To attach an Internet gateway to a VPC" + } + ], + "AttachNetworkInterface": [ + { + "input": { + "DeviceIndex": 1, + "InstanceId": "i-1234567890abcdef0", + "NetworkInterfaceId": "eni-e5aa89a3" + }, + "output": { + "AttachmentId": "eni-attach-66c4350a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example attaches the specified network interface to the specified instance.", + "id": "ec2-attach-network-interface-1", + "title": "To attach a network interface to an instance" + } + ], + "AttachVolume": [ + { + "input": { + "Device": "/dev/sdf", + "InstanceId": "i-01474ef662b89480", + "VolumeId": "vol-1234567890abcdef0" + }, + "output": { + "AttachTime": "2016-08-29T18:52:32.724Z", + "Device": "/dev/sdf", + "InstanceId": "i-01474ef662b89480", + "State": "attaching", + "VolumeId": "vol-1234567890abcdef0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example attaches a volume (``vol-1234567890abcdef0``) to an instance (``i-01474ef662b89480``) as ``/dev/sdf``.", + "id": "to-attach-a-volume-to-an-instance-1472499213109", + "title": "To attach a volume to an instance" + } + ], + "CancelSpotFleetRequests": [ + { + "input": { + "SpotFleetRequestIds": [ + "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + ], + "TerminateInstances": true + }, + "output": { + "SuccessfulFleetRequests": [ + { + "CurrentSpotFleetRequestState": "cancelled_running", + "PreviousSpotFleetRequestState": "active", + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example cancels the specified Spot fleet request and terminates its associated Spot Instances.", + "id": "ec2-cancel-spot-fleet-requests-1", + "title": "To cancel a Spot fleet request" + }, + { + "input": { + "SpotFleetRequestIds": [ + "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + ], + "TerminateInstances": false + }, + "output": { + "SuccessfulFleetRequests": [ + { + "CurrentSpotFleetRequestState": "cancelled_terminating", + "PreviousSpotFleetRequestState": "active", + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example cancels the specified Spot fleet request without terminating its associated Spot Instances.", + "id": "ec2-cancel-spot-fleet-requests-2", + "title": "To cancel a Spot fleet request without terminating its Spot Instances" + } + ], + "CancelSpotInstanceRequests": [ + { + "input": { + "SpotInstanceRequestIds": [ + "sir-08b93456" + ] + }, + "output": { + "CancelledSpotInstanceRequests": [ + { + "SpotInstanceRequestId": "sir-08b93456", + "State": "cancelled" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example cancels a Spot Instance request.", + "id": "ec2-cancel-spot-instance-requests-1", + "title": "To cancel Spot Instance requests" + } + ], + "ConfirmProductInstance": [ + { + "input": { + "InstanceId": "i-1234567890abcdef0", + "ProductCode": "774F4FF8" + }, + "output": { + "OwnerId": "123456789012" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example determines whether the specified product code is associated with the specified instance.", + "id": "to-confirm-the-product-instance-1472712108494", + "title": "To confirm the product instance" + } + ], + "CopySnapshot": [ + { + "input": { + "Description": "This is my copied snapshot.", + "DestinationRegion": "us-east-1", + "SourceRegion": "us-west-2", + "SourceSnapshotId": "snap-066877671789bd71b" + }, + "output": { + "SnapshotId": "snap-066877671789bd71b" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example copies a snapshot with the snapshot ID of ``snap-066877671789bd71b`` from the ``us-west-2`` region to the ``us-east-1`` region and adds a short description to identify the snapshot.", + "id": "to-copy-a-snapshot-1472502259774", + "title": "To copy a snapshot" + } + ], + "CreateCustomerGateway": [ + { + "input": { + "BgpAsn": 65534, + "PublicIp": "12.1.2.3", + "Type": "ipsec.1" + }, + "output": { + "CustomerGateway": { + "BgpAsn": "65534", + "CustomerGatewayId": "cgw-0e11f167", + "IpAddress": "12.1.2.3", + "State": "available", + "Type": "ipsec.1" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a customer gateway with the specified IP address for its outside interface.", + "id": "ec2-create-customer-gateway-1", + "title": "To create a customer gateway" + } + ], + "CreateDhcpOptions": [ + { + "input": { + "DhcpConfigurations": [ + { + "Key": "domain-name-servers", + "Values": [ + "10.2.5.1", + "10.2.5.2" + ] + } + ] + }, + "output": { + "DhcpOptions": { + "DhcpConfigurations": [ + { + "Key": "domain-name-servers", + "Values": [ + "10.2.5.2", + "10.2.5.1" + ] + } + ], + "DhcpOptionsId": "dopt-d9070ebb" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a DHCP options set.", + "id": "ec2-create-dhcp-options-1", + "title": "To create a DHCP options set" + } + ], + "CreateInternetGateway": [ + { + "output": { + "InternetGateway": { + "Attachments": [ + + ], + "InternetGatewayId": "igw-c0a643a9", + "Tags": [ + + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an Internet gateway.", + "id": "ec2-create-internet-gateway-1", + "title": "To create an Internet gateway" + } + ], + "CreateKeyPair": [ + { + "input": { + "KeyName": "my-key-pair" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a key pair named my-key-pair.", + "id": "ec2-create-key-pair-1", + "title": "To create a key pair" + } + ], + "CreateNatGateway": [ + { + "input": { + "AllocationId": "eipalloc-37fc1a52", + "SubnetId": "subnet-1a2b3c4d" + }, + "output": { + "NatGateway": { + "CreateTime": "2015-12-17T12:45:26.732Z", + "NatGatewayAddresses": [ + { + "AllocationId": "eipalloc-37fc1a52" + } + ], + "NatGatewayId": "nat-08d48af2a8e83edfd", + "State": "pending", + "SubnetId": "subnet-1a2b3c4d", + "VpcId": "vpc-1122aabb" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a NAT gateway in subnet subnet-1a2b3c4d and associates an Elastic IP address with the allocation ID eipalloc-37fc1a52 with the NAT gateway.", + "id": "ec2-create-nat-gateway-1", + "title": "To create a NAT gateway" + } + ], + "CreateNetworkAcl": [ + { + "input": { + "VpcId": "vpc-a01106c2" + }, + "output": { + "NetworkAcl": { + "Associations": [ + + ], + "Entries": [ + { + "CidrBlock": "0.0.0.0/0", + "Egress": true, + "Protocol": "-1", + "RuleAction": "deny", + "RuleNumber": 32767 + }, + { + "CidrBlock": "0.0.0.0/0", + "Egress": false, + "Protocol": "-1", + "RuleAction": "deny", + "RuleNumber": 32767 + } + ], + "IsDefault": false, + "NetworkAclId": "acl-5fb85d36", + "Tags": [ + + ], + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a network ACL for the specified VPC.", + "id": "ec2-create-network-acl-1", + "title": "To create a network ACL" + } + ], + "CreateNetworkAclEntry": [ + { + "input": { + "CidrBlock": "0.0.0.0/0", + "Egress": false, + "NetworkAclId": "acl-5fb85d36", + "PortRange": { + "From": 53, + "To": 53 + }, + "Protocol": "udp", + "RuleAction": "allow", + "RuleNumber": 100 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an entry for the specified network ACL. The rule allows ingress traffic from anywhere (0.0.0.0/0) on UDP port 53 (DNS) into any associated subnet.", + "id": "ec2-create-network-acl-entry-1", + "title": "To create a network ACL entry" + } + ], + "CreateNetworkInterface": [ + { + "input": { + "Description": "my network interface", + "Groups": [ + "sg-903004f8" + ], + "PrivateIpAddress": "10.0.2.17", + "SubnetId": "subnet-9d4a7b6c" + }, + "output": { + "NetworkInterface": { + "AvailabilityZone": "us-east-1d", + "Description": "my network interface", + "Groups": [ + { + "GroupId": "sg-903004f8", + "GroupName": "default" + } + ], + "MacAddress": "02:1a:80:41:52:9c", + "NetworkInterfaceId": "eni-e5aa89a3", + "OwnerId": "123456789012", + "PrivateIpAddress": "10.0.2.17", + "PrivateIpAddresses": [ + { + "Primary": true, + "PrivateIpAddress": "10.0.2.17" + } + ], + "RequesterManaged": false, + "SourceDestCheck": true, + "Status": "pending", + "SubnetId": "subnet-9d4a7b6c", + "TagSet": [ + + ], + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a network interface for the specified subnet.", + "id": "ec2-create-network-interface-1", + "title": "To create a network interface" + } + ], + "CreatePlacementGroup": [ + { + "input": { + "GroupName": "my-cluster", + "Strategy": "cluster" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a placement group with the specified name.", + "id": "to-create-a-placement-group-1472712245768", + "title": "To create a placement group" + } + ], + "CreateRoute": [ + { + "input": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": "igw-c0a643a9", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a route for the specified route table. The route matches all traffic (0.0.0.0/0) and routes it to the specified Internet gateway.", + "id": "ec2-create-route-1", + "title": "To create a route" + } + ], + "CreateRouteTable": [ + { + "input": { + "VpcId": "vpc-a01106c2" + }, + "output": { + "RouteTable": { + "Associations": [ + + ], + "PropagatingVgws": [ + + ], + "RouteTableId": "rtb-22574640", + "Routes": [ + { + "DestinationCidrBlock": "10.0.0.0/16", + "GatewayId": "local", + "State": "active" + } + ], + "Tags": [ + + ], + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a route table for the specified VPC.", + "id": "ec2-create-route-table-1", + "title": "To create a route table" + } + ], + "CreateSnapshot": [ + { + "input": { + "Description": "This is my root volume snapshot.", + "VolumeId": "vol-1234567890abcdef0" + }, + "output": { + "Description": "This is my root volume snapshot.", + "OwnerId": "012345678910", + "SnapshotId": "snap-066877671789bd71b", + "StartTime": "2014-02-28T21:06:01.000Z", + "State": "pending", + "Tags": [ + + ], + "VolumeId": "vol-1234567890abcdef0", + "VolumeSize": 8 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a snapshot of the volume with a volume ID of ``vol-1234567890abcdef0`` and a short description to identify the snapshot.", + "id": "to-create-a-snapshot-1472502529790", + "title": "To create a snapshot" + } + ], + "CreateSpotDatafeedSubscription": [ + { + "input": { + "Bucket": "my-s3-bucket", + "Prefix": "spotdata" + }, + "output": { + "SpotDatafeedSubscription": { + "Bucket": "my-s3-bucket", + "OwnerId": "123456789012", + "Prefix": "spotdata", + "State": "Active" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a Spot Instance data feed for your AWS account.", + "id": "ec2-create-spot-datafeed-subscription-1", + "title": "To create a Spot Instance datafeed" + } + ], + "CreateSubnet": [ + { + "input": { + "CidrBlock": "10.0.1.0/24", + "VpcId": "vpc-a01106c2" + }, + "output": { + "Subnet": { + "AvailabilityZone": "us-west-2c", + "AvailableIpAddressCount": 251, + "CidrBlock": "10.0.1.0/24", + "State": "pending", + "SubnetId": "subnet-9d4a7b6c", + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a subnet in the specified VPC with the specified CIDR block. We recommend that you let us select an Availability Zone for you.", + "id": "ec2-create-subnet-1", + "title": "To create a subnet" + } + ], + "CreateTags": [ + { + "input": { + "Resources": [ + "ami-78a54011" + ], + "Tags": [ + { + "Key": "Stack", + "Value": "production" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds the tag Stack=production to the specified image, or overwrites an existing tag for the AMI where the tag key is Stack.", + "id": "ec2-create-tags-1", + "title": "To add a tag to a resource" + } + ], + "CreateVolume": [ + { + "input": { + "AvailabilityZone": "us-east-1a", + "Size": 80, + "VolumeType": "gp2" + }, + "output": { + "AvailabilityZone": "us-east-1a", + "CreateTime": "2016-08-29T18:52:32.724Z", + "Encrypted": false, + "Iops": 240, + "Size": 80, + "SnapshotId": "", + "State": "creating", + "VolumeId": "vol-6b60b7c7", + "VolumeType": "gp2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an 80 GiB General Purpose (SSD) volume in the Availability Zone ``us-east-1a``.", + "id": "to-create-a-new-volume-1472496724296", + "title": "To create a new volume" + }, + { + "input": { + "AvailabilityZone": "us-east-1a", + "Iops": 1000, + "SnapshotId": "snap-066877671789bd71b", + "VolumeType": "io1" + }, + "output": { + "Attachments": [ + + ], + "AvailabilityZone": "us-east-1a", + "CreateTime": "2016-08-29T18:52:32.724Z", + "Iops": 1000, + "Size": 500, + "SnapshotId": "snap-066877671789bd71b", + "State": "creating", + "Tags": [ + + ], + "VolumeId": "vol-1234567890abcdef0", + "VolumeType": "io1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a new Provisioned IOPS (SSD) volume with 1000 provisioned IOPS from a snapshot in the Availability Zone ``us-east-1a``.", + "id": "to-create-a-new-provisioned-iops-ssd-volume-from-a-snapshot-1472498975176", + "title": "To create a new Provisioned IOPS (SSD) volume from a snapshot" + } + ], + "CreateVpc": [ + { + "input": { + "CidrBlock": "10.0.0.0/16" + }, + "output": { + "Vpc": { + "CidrBlock": "10.0.0.0/16", + "DhcpOptionsId": "dopt-7a8b9c2d", + "InstanceTenancy": "default", + "State": "pending", + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a VPC with the specified CIDR block.", + "id": "ec2-create-vpc-1", + "title": "To create a VPC" + } + ], + "DeleteCustomerGateway": [ + { + "input": { + "CustomerGatewayId": "cgw-0e11f167" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified customer gateway.", + "id": "ec2-delete-customer-gateway-1", + "title": "To delete a customer gateway" + } + ], + "DeleteDhcpOptions": [ + { + "input": { + "DhcpOptionsId": "dopt-d9070ebb" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified DHCP options set.", + "id": "ec2-delete-dhcp-options-1", + "title": "To delete a DHCP options set" + } + ], + "DeleteInternetGateway": [ + { + "input": { + "InternetGatewayId": "igw-c0a643a9" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified Internet gateway.", + "id": "ec2-delete-internet-gateway-1", + "title": "To delete an Internet gateway" + } + ], + "DeleteKeyPair": [ + { + "input": { + "KeyName": "my-key-pair" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified key pair.", + "id": "ec2-delete-key-pair-1", + "title": "To delete a key pair" + } + ], + "DeleteNatGateway": [ + { + "input": { + "NatGatewayId": "nat-04ae55e711cec5680" + }, + "output": { + "NatGatewayId": "nat-04ae55e711cec5680" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified NAT gateway.", + "id": "ec2-delete-nat-gateway-1", + "title": "To delete a NAT gateway" + } + ], + "DeleteNetworkAcl": [ + { + "input": { + "NetworkAclId": "acl-5fb85d36" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified network ACL.", + "id": "ec2-delete-network-acl-1", + "title": "To delete a network ACL" + } + ], + "DeleteNetworkAclEntry": [ + { + "input": { + "Egress": true, + "NetworkAclId": "acl-5fb85d36", + "RuleNumber": 100 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes ingress rule number 100 from the specified network ACL.", + "id": "ec2-delete-network-acl-entry-1", + "title": "To delete a network ACL entry" + } + ], + "DeleteNetworkInterface": [ + { + "input": { + "NetworkInterfaceId": "eni-e5aa89a3" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified network interface.", + "id": "ec2-delete-network-interface-1", + "title": "To delete a network interface" + } + ], + "DeletePlacementGroup": [ + { + "input": { + "GroupName": "my-cluster" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified placement group.\n", + "id": "to-delete-a-placement-group-1472712349959", + "title": "To delete a placement group" + } + ], + "DeleteRoute": [ + { + "input": { + "DestinationCidrBlock": "0.0.0.0/0", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified route from the specified route table.", + "id": "ec2-delete-route-1", + "title": "To delete a route" + } + ], + "DeleteRouteTable": [ + { + "input": { + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified route table.", + "id": "ec2-delete-route-table-1", + "title": "To delete a route table" + } + ], + "DeleteSnapshot": [ + { + "input": { + "SnapshotId": "snap-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes a snapshot with the snapshot ID of ``snap-1234567890abcdef0``. If the command succeeds, no output is returned.", + "id": "to-delete-a-snapshot-1472503042567", + "title": "To delete a snapshot" + } + ], + "DeleteSpotDatafeedSubscription": [ + { + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes a Spot data feed subscription for the account.", + "id": "ec2-delete-spot-datafeed-subscription-1", + "title": "To cancel a Spot Instance data feed subscription" + } + ], + "DeleteSubnet": [ + { + "input": { + "SubnetId": "subnet-9d4a7b6c" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified subnet.", + "id": "ec2-delete-subnet-1", + "title": "To delete a subnet" + } + ], + "DeleteTags": [ + { + "input": { + "Resources": [ + "ami-78a54011" + ], + "Tags": [ + { + "Key": "Stack", + "Value": "test" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the tag Stack=test from the specified image.", + "id": "ec2-delete-tags-1", + "title": "To delete a tag from a resource" + } + ], + "DeleteVolume": [ + { + "input": { + "VolumeId": "vol-049df61146c4d7901" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes an available volume with the volume ID of ``vol-049df61146c4d7901``. If the command succeeds, no output is returned.", + "id": "to-delete-a-volume-1472503111160", + "title": "To delete a volume" + } + ], + "DeleteVpc": [ + { + "input": { + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified VPC.", + "id": "ec2-delete-vpc-1", + "title": "To delete a VPC" + } + ], + "DescribeAccountAttributes": [ + { + "input": { + "AttributeNames": [ + "supported-platforms" + ] + }, + "output": { + "AccountAttributes": [ + { + "AttributeName": "supported-platforms", + "AttributeValues": [ + { + "AttributeValue": "EC2" + }, + { + "AttributeValue": "VPC" + } + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the supported-platforms attribute for your AWS account.", + "id": "ec2-describe-account-attributes-1", + "title": "To describe a single attribute for your AWS account" + }, + { + "output": { + "AccountAttributes": [ + { + "AttributeName": "supported-platforms", + "AttributeValues": [ + { + "AttributeValue": "EC2" + }, + { + "AttributeValue": "VPC" + } + ] + }, + { + "AttributeName": "vpc-max-security-groups-per-interface", + "AttributeValues": [ + { + "AttributeValue": "5" + } + ] + }, + { + "AttributeName": "max-elastic-ips", + "AttributeValues": [ + { + "AttributeValue": "5" + } + ] + }, + { + "AttributeName": "max-instances", + "AttributeValues": [ + { + "AttributeValue": "20" + } + ] + }, + { + "AttributeName": "vpc-max-elastic-ips", + "AttributeValues": [ + { + "AttributeValue": "5" + } + ] + }, + { + "AttributeName": "default-vpc", + "AttributeValues": [ + { + "AttributeValue": "none" + } + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the attributes for your AWS account.", + "id": "ec2-describe-account-attributes-2", + "title": "To describe all attributes for your AWS account" + } + ], + "DescribeAddresses": [ + { + "output": { + "Addresses": [ + { + "Domain": "standard", + "InstanceId": "i-1234567890abcdef0", + "PublicIp": "198.51.100.0" + }, + { + "AllocationId": "eipalloc-12345678", + "AssociationId": "eipassoc-12345678", + "Domain": "vpc", + "InstanceId": "i-1234567890abcdef0", + "NetworkInterfaceId": "eni-12345678", + "NetworkInterfaceOwnerId": "123456789012", + "PrivateIpAddress": "10.0.1.241", + "PublicIp": "203.0.113.0" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes your Elastic IP addresses.", + "id": "ec2-describe-addresses-1", + "title": "To describe your Elastic IP addresses" + }, + { + "input": { + "Filters": [ + { + "Name": "domain", + "Values": [ + "vpc" + ] + } + ] + }, + "output": { + "Addresses": [ + { + "AllocationId": "eipalloc-12345678", + "AssociationId": "eipassoc-12345678", + "Domain": "vpc", + "InstanceId": "i-1234567890abcdef0", + "NetworkInterfaceId": "eni-12345678", + "NetworkInterfaceOwnerId": "123456789012", + "PrivateIpAddress": "10.0.1.241", + "PublicIp": "203.0.113.0" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes your Elastic IP addresses for use with instances in a VPC.", + "id": "ec2-describe-addresses-2", + "title": "To describe your Elastic IP addresses for EC2-VPC" + }, + { + "input": { + "Filters": [ + { + "Name": "domain", + "Values": [ + "standard" + ] + } + ] + }, + "output": { + "Addresses": [ + { + "Domain": "standard", + "InstanceId": "i-1234567890abcdef0", + "PublicIp": "198.51.100.0" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes your Elastic IP addresses for use with instances in EC2-Classic.", + "id": "ec2-describe-addresses-3", + "title": "To describe your Elastic IP addresses for EC2-Classic" + } + ], + "DescribeAvailabilityZones": [ + { + "output": { + "AvailabilityZones": [ + { + "Messages": [ + + ], + "RegionName": "us-east-1", + "State": "available", + "ZoneName": "us-east-1b" + }, + { + "Messages": [ + + ], + "RegionName": "us-east-1", + "State": "available", + "ZoneName": "us-east-1c" + }, + { + "Messages": [ + + ], + "RegionName": "us-east-1", + "State": "available", + "ZoneName": "us-east-1d" + }, + { + "Messages": [ + + ], + "RegionName": "us-east-1", + "State": "available", + "ZoneName": "us-east-1e" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the Availability Zones that are available to you. The response includes Availability Zones only for the current region.", + "id": "ec2-describe-availability-zones-1", + "title": "To describe your Availability Zones" + } + ], + "DescribeCustomerGateways": [ + { + "input": { + "CustomerGatewayIds": [ + "cgw-0e11f167" + ] + }, + "output": { + "CustomerGateways": [ + { + "BgpAsn": "65534", + "CustomerGatewayId": "cgw-0e11f167", + "IpAddress": "12.1.2.3", + "State": "available", + "Type": "ipsec.1" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified customer gateway.", + "id": "ec2-describe-customer-gateways-1", + "title": "To describe a customer gateway" + } + ], + "DescribeDhcpOptions": [ + { + "input": { + "DhcpOptionsIds": [ + "dopt-d9070ebb" + ] + }, + "output": { + "DhcpOptions": [ + { + "DhcpConfigurations": [ + { + "Key": "domain-name-servers", + "Values": [ + "10.2.5.2", + "10.2.5.1" + ] + } + ], + "DhcpOptionsId": "dopt-d9070ebb" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified DHCP options set.", + "id": "ec2-describe-dhcp-options-1", + "title": "To describe a DHCP options set" + } + ], + "DescribeInstanceAttribute": [ + { + "input": { + "Attribute": "instanceType", + "InstanceId": "i-1234567890abcdef0" + }, + "output": { + "InstanceId": "i-1234567890abcdef0", + "InstanceType": { + "Value": "t1.micro" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the instance type of the specified instance.\n", + "id": "to-describe-the-instance-type-1472712432132", + "title": "To describe the instance type" + }, + { + "input": { + "Attribute": "disableApiTermination", + "InstanceId": "i-1234567890abcdef0" + }, + "output": { + "DisableApiTermination": { + "Value": "false" + }, + "InstanceId": "i-1234567890abcdef0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the ``disableApiTermination`` attribute of the specified instance.\n", + "id": "to-describe-the-disableapitermination-attribute-1472712533466", + "title": "To describe the disableApiTermination attribute" + }, + { + "input": { + "Attribute": "blockDeviceMapping", + "InstanceId": "i-1234567890abcdef0" + }, + "output": { + "BlockDeviceMappings": [ + { + "DeviceName": "/dev/sda1", + "Ebs": { + "AttachTime": "2013-05-17T22:42:34.000Z", + "DeleteOnTermination": true, + "Status": "attached", + "VolumeId": "vol-049df61146c4d7901" + } + }, + { + "DeviceName": "/dev/sdf", + "Ebs": { + "AttachTime": "2013-09-10T23:07:00.000Z", + "DeleteOnTermination": false, + "Status": "attached", + "VolumeId": "vol-049df61146c4d7901" + } + } + ], + "InstanceId": "i-1234567890abcdef0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the ``blockDeviceMapping`` attribute of the specified instance.\n", + "id": "to-describe-the-block-device-mapping-for-an-instance-1472712645423", + "title": "To describe the block device mapping for an instance" + } + ], + "DescribeInternetGateways": [ + { + "input": { + "Filters": [ + { + "Name": "attachment.vpc-id", + "Values": [ + "vpc-a01106c2" + ] + } + ] + }, + "output": { + "InternetGateways": [ + { + "Attachments": [ + { + "State": "available", + "VpcId": "vpc-a01106c2" + } + ], + "InternetGatewayId": "igw-c0a643a9", + "Tags": [ + + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the Internet gateway for the specified VPC.", + "id": "ec2-describe-internet-gateways-1", + "title": "To describe the Internet gateway for a VPC" + } + ], + "DescribeKeyPairs": [ + { + "input": { + "KeyNames": [ + "my-key-pair" + ] + }, + "output": { + "KeyPairs": [ + { + "KeyFingerprint": "1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f", + "KeyName": "my-key-pair" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example displays the fingerprint for the specified key.", + "id": "ec2-describe-key-pairs-1", + "title": "To display a key pair" + } + ], + "DescribeMovingAddresses": [ + { + "output": { + "MovingAddressStatuses": [ + { + "MoveStatus": "MovingToVpc", + "PublicIp": "198.51.100.0" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all of your moving Elastic IP addresses.", + "id": "ec2-describe-moving-addresses-1", + "title": "To describe your moving addresses" + } + ], + "DescribeNatGateways": [ + { + "input": { + "Filters": [ + { + "Name": "vpc-id", + "Values": [ + "vpc-1a2b3c4d" + ] + } + ] + }, + "output": { + "NatGateways": [ + { + "CreateTime": "2015-12-01T12:26:55.983Z", + "NatGatewayAddresses": [ + { + "AllocationId": "eipalloc-89c620ec", + "NetworkInterfaceId": "eni-9dec76cd", + "PrivateIp": "10.0.0.149", + "PublicIp": "198.11.222.333" + } + ], + "NatGatewayId": "nat-05dba92075d71c408", + "State": "available", + "SubnetId": "subnet-847e4dc2", + "VpcId": "vpc-1a2b3c4d" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the NAT gateway for the specified VPC.", + "id": "ec2-describe-nat-gateways-1", + "title": "To describe a NAT gateway" + } + ], + "DescribeNetworkAcls": [ + { + "input": { + "NetworkAclIds": [ + "acl-5fb85d36" + ] + }, + "output": { + "NetworkAcls": [ + { + "Associations": [ + { + "NetworkAclAssociationId": "aclassoc-66ea5f0b", + "NetworkAclId": "acl-9aeb5ef7", + "SubnetId": "subnet-65ea5f08" + } + ], + "Entries": [ + { + "CidrBlock": "0.0.0.0/0", + "Egress": true, + "Protocol": "-1", + "RuleAction": "deny", + "RuleNumber": 32767 + }, + { + "CidrBlock": "0.0.0.0/0", + "Egress": false, + "Protocol": "-1", + "RuleAction": "deny", + "RuleNumber": 32767 + } + ], + "IsDefault": false, + "NetworkAclId": "acl-5fb85d36", + "Tags": [ + + ], + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified network ACL.", + "id": "ec2-", + "title": "To describe a network ACL" + } + ], + "DescribeNetworkInterfaceAttribute": [ + { + "input": { + "Attribute": "attachment", + "NetworkInterfaceId": "eni-686ea200" + }, + "output": { + "Attachment": { + "AttachTime": "2015-05-21T20:02:20.000Z", + "AttachmentId": "eni-attach-43348162", + "DeleteOnTermination": true, + "DeviceIndex": 0, + "InstanceId": "i-1234567890abcdef0", + "InstanceOwnerId": "123456789012", + "Status": "attached" + }, + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the attachment attribute of the specified network interface.", + "id": "ec2-describe-network-interface-attribute-1", + "title": "To describe the attachment attribute of a network interface" + }, + { + "input": { + "Attribute": "description", + "NetworkInterfaceId": "eni-686ea200" + }, + "output": { + "Description": { + "Value": "My description" + }, + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the description attribute of the specified network interface.", + "id": "ec2-describe-network-interface-attribute-2", + "title": "To describe the description attribute of a network interface" + }, + { + "input": { + "Attribute": "groupSet", + "NetworkInterfaceId": "eni-686ea200" + }, + "output": { + "Groups": [ + { + "GroupId": "sg-903004f8", + "GroupName": "my-security-group" + } + ], + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the groupSet attribute of the specified network interface.", + "id": "ec2-describe-network-interface-attribute-3", + "title": "To describe the groupSet attribute of a network interface" + }, + { + "input": { + "Attribute": "sourceDestCheck", + "NetworkInterfaceId": "eni-686ea200" + }, + "output": { + "NetworkInterfaceId": "eni-686ea200", + "SourceDestCheck": { + "Value": true + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the sourceDestCheck attribute of the specified network interface.", + "id": "ec2-describe-network-interface-attribute-4", + "title": "To describe the sourceDestCheck attribute of a network interface" + } + ], + "DescribeNetworkInterfaces": [ + { + "input": { + "NetworkInterfaceIds": [ + "eni-e5aa89a3" + ] + }, + "output": { + "NetworkInterfaces": [ + { + "Association": { + "AssociationId": "eipassoc-0fbb766a", + "IpOwnerId": "123456789012", + "PublicDnsName": "ec2-203-0-113-12.compute-1.amazonaws.com", + "PublicIp": "203.0.113.12" + }, + "Attachment": { + "AttachTime": "2013-11-30T23:36:42.000Z", + "AttachmentId": "eni-attach-66c4350a", + "DeleteOnTermination": false, + "DeviceIndex": 1, + "InstanceId": "i-1234567890abcdef0", + "InstanceOwnerId": "123456789012", + "Status": "attached" + }, + "AvailabilityZone": "us-east-1d", + "Description": "my network interface", + "Groups": [ + { + "GroupId": "sg-8637d3e3", + "GroupName": "default" + } + ], + "MacAddress": "02:2f:8f:b0:cf:75", + "NetworkInterfaceId": "eni-e5aa89a3", + "OwnerId": "123456789012", + "PrivateDnsName": "ip-10-0-1-17.ec2.internal", + "PrivateIpAddress": "10.0.1.17", + "PrivateIpAddresses": [ + { + "Association": { + "AssociationId": "eipassoc-0fbb766a", + "IpOwnerId": "123456789012", + "PublicDnsName": "ec2-203-0-113-12.compute-1.amazonaws.com", + "PublicIp": "203.0.113.12" + }, + "Primary": true, + "PrivateDnsName": "ip-10-0-1-17.ec2.internal", + "PrivateIpAddress": "10.0.1.17" + } + ], + "RequesterManaged": false, + "SourceDestCheck": true, + "Status": "in-use", + "SubnetId": "subnet-b61f49f0", + "TagSet": [ + + ], + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "ec2-describe-network-interfaces-1", + "title": "To describe a network interface" + } + ], + "DescribeRegions": [ + { + "output": { + "Regions": [ + { + "Endpoint": "ec2.ap-south-1.amazonaws.com", + "RegionName": "ap-south-1" + }, + { + "Endpoint": "ec2.eu-west-1.amazonaws.com", + "RegionName": "eu-west-1" + }, + { + "Endpoint": "ec2.ap-southeast-1.amazonaws.com", + "RegionName": "ap-southeast-1" + }, + { + "Endpoint": "ec2.ap-southeast-2.amazonaws.com", + "RegionName": "ap-southeast-2" + }, + { + "Endpoint": "ec2.eu-central-1.amazonaws.com", + "RegionName": "eu-central-1" + }, + { + "Endpoint": "ec2.ap-northeast-2.amazonaws.com", + "RegionName": "ap-northeast-2" + }, + { + "Endpoint": "ec2.ap-northeast-1.amazonaws.com", + "RegionName": "ap-northeast-1" + }, + { + "Endpoint": "ec2.us-east-1.amazonaws.com", + "RegionName": "us-east-1" + }, + { + "Endpoint": "ec2.sa-east-1.amazonaws.com", + "RegionName": "sa-east-1" + }, + { + "Endpoint": "ec2.us-west-1.amazonaws.com", + "RegionName": "us-west-1" + }, + { + "Endpoint": "ec2.us-west-2.amazonaws.com", + "RegionName": "us-west-2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all the regions that are available to you.", + "id": "ec2-describe-regions-1", + "title": "To describe your regions" + } + ], + "DescribeRouteTables": [ + { + "input": { + "RouteTableIds": [ + "rtb-1f382e7d" + ] + }, + "output": { + "RouteTables": [ + { + "Associations": [ + { + "Main": true, + "RouteTableAssociationId": "rtbassoc-d8ccddba", + "RouteTableId": "rtb-1f382e7d" + } + ], + "PropagatingVgws": [ + + ], + "RouteTableId": "rtb-1f382e7d", + "Routes": [ + { + "DestinationCidrBlock": "10.0.0.0/16", + "GatewayId": "local", + "State": "active" + } + ], + "Tags": [ + + ], + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified route table.", + "id": "ec2-describe-route-tables-1", + "title": "To describe a route table" + } + ], + "DescribeScheduledInstanceAvailability": [ + { + "input": { + "FirstSlotStartTimeRange": { + "EarliestTime": "2016-01-31T00:00:00Z", + "LatestTime": "2016-01-31T04:00:00Z" + }, + "Recurrence": { + "Frequency": "Weekly", + "Interval": 1, + "OccurrenceDays": [ + 1 + ] + } + }, + "output": { + "ScheduledInstanceAvailabilitySet": [ + { + "AvailabilityZone": "us-west-2b", + "AvailableInstanceCount": 20, + "FirstSlotStartTime": "2016-01-31T00:00:00Z", + "HourlyPrice": "0.095", + "InstanceType": "c4.large", + "MaxTermDurationInDays": 366, + "MinTermDurationInDays": 366, + "NetworkPlatform": "EC2-VPC", + "Platform": "Linux/UNIX", + "PurchaseToken": "eyJ2IjoiMSIsInMiOjEsImMiOi...", + "Recurrence": { + "Frequency": "Weekly", + "Interval": 1, + "OccurrenceDaySet": [ + 1 + ], + "OccurrenceRelativeToEnd": false + }, + "SlotDurationInHours": 23, + "TotalScheduledInstanceHours": 1219 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes a schedule that occurs every week on Sunday, starting on the specified date. Note that the output contains a single schedule as an example.", + "id": "ec2-describe-scheduled-instance-availability-1", + "title": "To describe an available schedule" + } + ], + "DescribeScheduledInstances": [ + { + "input": { + "ScheduledInstanceIds": [ + "sci-1234-1234-1234-1234-123456789012" + ] + }, + "output": { + "ScheduledInstanceSet": [ + { + "AvailabilityZone": "us-west-2b", + "CreateDate": "2016-01-25T21:43:38.612Z", + "HourlyPrice": "0.095", + "InstanceCount": 1, + "InstanceType": "c4.large", + "NetworkPlatform": "EC2-VPC", + "NextSlotStartTime": "2016-01-31T09:00:00Z", + "Platform": "Linux/UNIX", + "Recurrence": { + "Frequency": "Weekly", + "Interval": 1, + "OccurrenceDaySet": [ + 1 + ], + "OccurrenceRelativeToEnd": false, + "OccurrenceUnit": "" + }, + "ScheduledInstanceId": "sci-1234-1234-1234-1234-123456789012", + "SlotDurationInHours": 32, + "TermEndDate": "2017-01-31T09:00:00Z", + "TermStartDate": "2016-01-31T09:00:00Z", + "TotalScheduledInstanceHours": 1696 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified Scheduled Instance.", + "id": "ec2-describe-scheduled-instances-1", + "title": "To describe your Scheduled Instances" + } + ], + "DescribeSnapshotAttribute": [ + { + "input": { + "Attribute": "createVolumePermission", + "SnapshotId": "snap-066877671789bd71b" + }, + "output": { + "CreateVolumePermissions": [ + + ], + "SnapshotId": "snap-066877671789bd71b" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the ``createVolumePermission`` attribute on a snapshot with the snapshot ID of ``snap-066877671789bd71b``.", + "id": "to-describe-snapshot-attributes-1472503199736", + "title": "To describe snapshot attributes" + } + ], + "DescribeSnapshots": [ + { + "input": { + "SnapshotIds": [ + "snap-1234567890abcdef0" + ] + }, + "output": { + "NextToken": "", + "Snapshots": [ + { + "Description": "This is my snapshot.", + "OwnerId": "012345678910", + "Progress": "100%", + "SnapshotId": "snap-1234567890abcdef0", + "StartTime": "2014-02-28T21:28:32.000Z", + "State": "completed", + "VolumeId": "vol-049df61146c4d7901", + "VolumeSize": 8 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes a snapshot with the snapshot ID of ``snap-1234567890abcdef0``.", + "id": "to-describe-a-snapshot-1472503807850", + "title": "To describe a snapshot" + }, + { + "input": { + "Filters": [ + { + "Name": "status", + "Values": [ + "pending" + ] + } + ], + "OwnerIds": [ + "012345678910" + ] + }, + "output": { + "NextToken": "", + "Snapshots": [ + { + "Description": "This is my copied snapshot.", + "OwnerId": "012345678910", + "Progress": "87%", + "SnapshotId": "snap-066877671789bd71b", + "StartTime": "2014-02-28T21:37:27.000Z", + "State": "pending", + "VolumeId": "vol-1234567890abcdef0", + "VolumeSize": 8 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all snapshots owned by the ID 012345678910 that are in the ``pending`` status.", + "id": "to-describe-snapshots-using-filters-1472503929793", + "title": "To describe snapshots using filters" + } + ], + "DescribeSpotDatafeedSubscription": [ + { + "output": { + "SpotDatafeedSubscription": { + "Bucket": "my-s3-bucket", + "OwnerId": "123456789012", + "Prefix": "spotdata", + "State": "Active" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the Spot Instance datafeed subscription for your AWS account.", + "id": "ec2-describe-spot-datafeed-subscription-1", + "title": "To describe the datafeed for your AWS account" + } + ], + "DescribeSpotFleetInstances": [ + { + "input": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "output": { + "ActiveInstances": [ + { + "InstanceId": "i-1234567890abcdef0", + "InstanceType": "m3.medium", + "SpotInstanceRequestId": "sir-08b93456" + } + ], + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists the Spot Instances associated with the specified Spot fleet.", + "id": "ec2-describe-spot-fleet-instances-1", + "title": "To describe the Spot Instances associated with a Spot fleet" + } + ], + "DescribeSpotFleetRequestHistory": [ + { + "input": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "StartTime": "2015-05-26T00:00:00Z" + }, + "output": { + "HistoryRecords": [ + { + "EventInformation": { + "EventSubType": "submitted" + }, + "EventType": "fleetRequestChange", + "Timestamp": "2015-05-26T23:17:20.697Z" + }, + { + "EventInformation": { + "EventSubType": "active" + }, + "EventType": "fleetRequestChange", + "Timestamp": "2015-05-26T23:17:20.873Z" + }, + { + "EventInformation": { + "EventSubType": "launched", + "InstanceId": "i-1234567890abcdef0" + }, + "EventType": "instanceChange", + "Timestamp": "2015-05-26T23:21:21.712Z" + }, + { + "EventInformation": { + "EventSubType": "launched", + "InstanceId": "i-1234567890abcdef1" + }, + "EventType": "instanceChange", + "Timestamp": "2015-05-26T23:21:21.816Z" + } + ], + "NextToken": "CpHNsscimcV5oH7bSbub03CI2Qms5+ypNpNm+53MNlR0YcXAkp0xFlfKf91yVxSExmbtma3awYxMFzNA663ZskT0AHtJ6TCb2Z8bQC2EnZgyELbymtWPfpZ1ZbauVg+P+TfGlWxWWB/Vr5dk5d4LfdgA/DRAHUrYgxzrEXAMPLE=", + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "StartTime": "2015-05-26T00:00:00Z" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example returns the history for the specified Spot fleet starting at the specified time.", + "id": "ec2-describe-spot-fleet-request-history-1", + "title": "To describe Spot fleet history" + } + ], + "DescribeSpotFleetRequests": [ + { + "input": { + "SpotFleetRequestIds": [ + "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + ] + }, + "output": { + "SpotFleetRequestConfigs": [ + { + "SpotFleetRequestConfig": { + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "EbsOptimized": false, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "cc2.8xlarge", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeleteOnTermination": false, + "DeviceIndex": 0, + "SecondaryPrivateIpAddressCount": 0, + "SubnetId": "subnet-a61dafcf" + } + ] + }, + { + "EbsOptimized": false, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "r3.8xlarge", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeleteOnTermination": false, + "DeviceIndex": 0, + "SecondaryPrivateIpAddressCount": 0, + "SubnetId": "subnet-a61dafcf" + } + ] + } + ], + "SpotPrice": "0.05", + "TargetCapacity": 20 + }, + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "SpotFleetRequestState": "active" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified Spot fleet request.", + "id": "ec2-describe-spot-fleet-requests-1", + "title": "To describe a Spot fleet request" + } + ], + "DescribeSpotInstanceRequests": [ + { + "input": { + "SpotInstanceRequestIds": [ + "sir-08b93456" + ] + }, + "output": { + "SpotInstanceRequests": [ + { + "CreateTime": "2014-04-30T18:14:55.000Z", + "InstanceId": "i-1234567890abcdef0", + "LaunchSpecification": { + "BlockDeviceMappings": [ + { + "DeviceName": "/dev/sda1", + "Ebs": { + "DeleteOnTermination": true, + "VolumeSize": 8, + "VolumeType": "standard" + } + } + ], + "EbsOptimized": false, + "ImageId": "ami-7aba833f", + "InstanceType": "m1.small", + "KeyName": "my-key-pair", + "SecurityGroups": [ + { + "GroupId": "sg-e38f24a7", + "GroupName": "my-security-group" + } + ] + }, + "LaunchedAvailabilityZone": "us-west-1b", + "ProductDescription": "Linux/UNIX", + "SpotInstanceRequestId": "sir-08b93456", + "SpotPrice": "0.010000", + "State": "active", + "Status": { + "Code": "fulfilled", + "Message": "Your Spot request is fulfilled.", + "UpdateTime": "2014-04-30T18:16:21.000Z" + }, + "Type": "one-time" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified Spot Instance request.", + "id": "ec2-describe-spot-instance-requests-1", + "title": "To describe a Spot Instance request" + } + ], + "DescribeSpotPriceHistory": [ + { + "input": { + "EndTime": "2014-01-06T08:09:10", + "InstanceTypes": [ + "m1.xlarge" + ], + "ProductDescriptions": [ + "Linux/UNIX (Amazon VPC)" + ], + "StartTime": "2014-01-06T07:08:09" + }, + "output": { + "SpotPriceHistory": [ + { + "AvailabilityZone": "us-west-1a", + "InstanceType": "m1.xlarge", + "ProductDescription": "Linux/UNIX (Amazon VPC)", + "SpotPrice": "0.080000", + "Timestamp": "2014-01-06T04:32:53.000Z" + }, + { + "AvailabilityZone": "us-west-1c", + "InstanceType": "m1.xlarge", + "ProductDescription": "Linux/UNIX (Amazon VPC)", + "SpotPrice": "0.080000", + "Timestamp": "2014-01-05T11:28:26.000Z" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example returns the Spot Price history for m1.xlarge, Linux/UNIX (Amazon VPC) instances for a particular day in January.", + "id": "ec2-describe-spot-price-history-1", + "title": "To describe Spot price history for Linux/UNIX (Amazon VPC)" + } + ], + "DescribeSubnets": [ + { + "input": { + "Filters": [ + { + "Name": "vpc-id", + "Values": [ + "vpc-a01106c2" + ] + } + ] + }, + "output": { + "Subnets": [ + { + "AvailabilityZone": "us-east-1c", + "AvailableIpAddressCount": 251, + "CidrBlock": "10.0.1.0/24", + "DefaultForAz": false, + "MapPublicIpOnLaunch": false, + "State": "available", + "SubnetId": "subnet-9d4a7b6c", + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the subnets for the specified VPC.", + "id": "ec2-describe-subnets-1", + "title": "To describe the subnets for a VPC" + } + ], + "DescribeTags": [ + { + "input": { + "Filters": [ + { + "Name": "resource-id", + "Values": [ + "i-1234567890abcdef8" + ] + } + ] + }, + "output": { + "Tags": [ + { + "Key": "Stack", + "ResourceId": "i-1234567890abcdef8", + "ResourceType": "instance", + "Value": "test" + }, + { + "Key": "Name", + "ResourceId": "i-1234567890abcdef8", + "ResourceType": "instance", + "Value": "Beta Server" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the tags for the specified instance.", + "id": "ec2-describe-tags-1", + "title": "To describe the tags for a single resource" + } + ], + "DescribeVolumeAttribute": [ + { + "input": { + "Attribute": "autoEnableIO", + "VolumeId": "vol-049df61146c4d7901" + }, + "output": { + "AutoEnableIO": { + "Value": false + }, + "VolumeId": "vol-049df61146c4d7901" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the ``autoEnableIo`` attribute of the volume with the ID ``vol-049df61146c4d7901``.", + "id": "to-describe-a-volume-attribute-1472505773492", + "title": "To describe a volume attribute" + } + ], + "DescribeVolumeStatus": [ + { + "input": { + "VolumeIds": [ + "vol-1234567890abcdef0" + ] + }, + "output": { + "VolumeStatuses": [ + { + "Actions": [ + + ], + "AvailabilityZone": "us-east-1a", + "Events": [ + + ], + "VolumeId": "vol-1234567890abcdef0", + "VolumeStatus": { + "Details": [ + { + "Name": "io-enabled", + "Status": "passed" + }, + { + "Name": "io-performance", + "Status": "not-applicable" + } + ], + "Status": "ok" + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the status for the volume ``vol-1234567890abcdef0``.", + "id": "to-describe-the-status-of-a-single-volume-1472507016193", + "title": "To describe the status of a single volume" + }, + { + "input": { + "Filters": [ + { + "Name": "volume-status.status", + "Values": [ + "impaired" + ] + } + ] + }, + "output": { + "VolumeStatuses": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the status for all volumes that are impaired. In this example output, there are no impaired volumes.", + "id": "to-describe-the-status-of-impaired-volumes-1472507239821", + "title": "To describe the status of impaired volumes" + } + ], + "DescribeVolumes": [ + { + "input": { + }, + "output": { + "NextToken": "", + "Volumes": [ + { + "Attachments": [ + { + "AttachTime": "2013-12-18T22:35:00.000Z", + "DeleteOnTermination": true, + "Device": "/dev/sda1", + "InstanceId": "i-1234567890abcdef0", + "State": "attached", + "VolumeId": "vol-049df61146c4d7901" + } + ], + "AvailabilityZone": "us-east-1a", + "CreateTime": "2013-12-18T22:35:00.084Z", + "Size": 8, + "SnapshotId": "snap-1234567890abcdef0", + "State": "in-use", + "VolumeId": "vol-049df61146c4d7901", + "VolumeType": "standard" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all of your volumes in the default region.", + "id": "to-describe-all-volumes-1472506358883", + "title": "To describe all volumes" + }, + { + "input": { + "Filters": [ + { + "Name": "attachment.instance-id", + "Values": [ + "i-1234567890abcdef0" + ] + }, + { + "Name": "attachment.delete-on-termination", + "Values": [ + "true" + ] + } + ] + }, + "output": { + "Volumes": [ + { + "Attachments": [ + { + "AttachTime": "2013-12-18T22:35:00.000Z", + "DeleteOnTermination": true, + "Device": "/dev/sda1", + "InstanceId": "i-1234567890abcdef0", + "State": "attached", + "VolumeId": "vol-049df61146c4d7901" + } + ], + "AvailabilityZone": "us-east-1a", + "CreateTime": "2013-12-18T22:35:00.084Z", + "Size": 8, + "SnapshotId": "snap-1234567890abcdef0", + "State": "in-use", + "VolumeId": "vol-049df61146c4d7901", + "VolumeType": "standard" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all volumes that are both attached to the instance with the ID i-1234567890abcdef0 and set to delete when the instance terminates.", + "id": "to-describe-volumes-that-are-attached-to-a-specific-instance-1472506613578", + "title": "To describe volumes that are attached to a specific instance" + } + ], + "DescribeVpcAttribute": [ + { + "input": { + "Attribute": "enableDnsSupport", + "VpcId": "vpc-a01106c2" + }, + "output": { + "EnableDnsSupport": { + "Value": true + }, + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the enableDnsSupport attribute. This attribute indicates whether DNS resolution is enabled for the VPC. If this attribute is true, the Amazon DNS server resolves DNS hostnames for your instances to their corresponding IP addresses; otherwise, it does not.", + "id": "ec2-describe-vpc-attribute-1", + "title": "To describe the enableDnsSupport attribute" + }, + { + "input": { + "Attribute": "enableDnsHostnames", + "VpcId": "vpc-a01106c2" + }, + "output": { + "EnableDnsHostnames": { + "Value": true + }, + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the enableDnsHostnames attribute. This attribute indicates whether the instances launched in the VPC get DNS hostnames. If this attribute is true, instances in the VPC get DNS hostnames; otherwise, they do not.", + "id": "ec2-describe-vpc-attribute-2", + "title": "To describe the enableDnsHostnames attribute" + } + ], + "DescribeVpcs": [ + { + "input": { + "VpcIds": [ + "vpc-a01106c2" + ] + }, + "output": { + "Vpcs": [ + { + "CidrBlock": "10.0.0.0/16", + "DhcpOptionsId": "dopt-7a8b9c2d", + "InstanceTenancy": "default", + "IsDefault": false, + "State": "available", + "Tags": [ + { + "Key": "Name", + "Value": "MyVPC" + } + ], + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified VPC.", + "id": "ec2-describe-vpcs-1", + "title": "To describe a VPC" + } + ], + "DetachInternetGateway": [ + { + "input": { + "InternetGatewayId": "igw-c0a643a9", + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the specified Internet gateway from the specified VPC.", + "id": "ec2-detach-internet-gateway-1", + "title": "To detach an Internet gateway from a VPC" + } + ], + "DetachNetworkInterface": [ + { + "input": { + "AttachmentId": "eni-attach-66c4350a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the specified network interface from its attached instance.", + "id": "ec2-detach-network-interface-1", + "title": "To detach a network interface from an instance" + } + ], + "DetachVolume": [ + { + "input": { + "VolumeId": "vol-1234567890abcdef0" + }, + "output": { + "AttachTime": "2014-02-27T19:23:06.000Z", + "Device": "/dev/sdb", + "InstanceId": "i-1234567890abcdef0", + "State": "detaching", + "VolumeId": "vol-049df61146c4d7901" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the volume (``vol-049df61146c4d7901``) from the instance it is attached to.", + "id": "to-detach-a-volume-from-an-instance-1472507977694", + "title": "To detach a volume from an instance" + } + ], + "DisableVgwRoutePropagation": [ + { + "input": { + "GatewayId": "vgw-9a4cacf3", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disables the specified virtual private gateway from propagating static routes to the specified route table.", + "id": "ec2-disable-vgw-route-propagation-1", + "title": "To disable route propagation" + } + ], + "DisassociateAddress": [ + { + "input": { + "AssociationId": "eipassoc-2bebb745" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disassociates an Elastic IP address from an instance in a VPC.", + "id": "ec2-disassociate-address-1", + "title": "To disassociate an Elastic IP address in EC2-VPC" + }, + { + "input": { + "PublicIp": "198.51.100.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disassociates an Elastic IP address from an instance in EC2-Classic.", + "id": "ec2-disassociate-address-2", + "title": "To disassociate an Elastic IP addresses in EC2-Classic" + } + ], + "DisassociateRouteTable": [ + { + "input": { + "AssociationId": "rtbassoc-781d0d1a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disassociates the specified route table from its associated subnet.", + "id": "ec2-disassociate-route-table-1", + "title": "To disassociate a route table" + } + ], + "EnableVgwRoutePropagation": [ + { + "input": { + "GatewayId": "vgw-9a4cacf3", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables the specified virtual private gateway to propagate static routes to the specified route table.", + "id": "ec2-enable-vgw-route-propagation-1", + "title": "To enable route propagation" + } + ], + "EnableVolumeIO": [ + { + "input": { + "VolumeId": "vol-1234567890abcdef0" + }, + "output": { + "Return": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables I/O on volume ``vol-1234567890abcdef0``.", + "id": "to-enable-io-for-a-volume-1472508114867", + "title": "To enable I/O for a volume" + } + ], + "ModifyNetworkInterfaceAttribute": [ + { + "input": { + "Attachment": { + "AttachmentId": "eni-attach-43348162", + "DeleteOnTermination": false + }, + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the attachment attribute of the specified network interface.", + "id": "ec2-modify-network-interface-attribute-1", + "title": "To modify the attachment attribute of a network interface" + }, + { + "input": { + "Description": "My description", + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the description attribute of the specified network interface.", + "id": "ec2-modify-network-interface-attribute-2", + "title": "To modify the description attribute of a network interface" + }, + { + "input": { + "Groups": [ + "sg-903004f8", + "sg-1a2b3c4d" + ], + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example command modifies the groupSet attribute of the specified network interface.", + "id": "ec2-modify-network-interface-attribute-3", + "title": "To modify the groupSet attribute of a network interface" + }, + { + "input": { + "NetworkInterfaceId": "eni-686ea200", + "SourceDestCheck": false + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example command modifies the sourceDestCheck attribute of the specified network interface.", + "id": "ec2-modify-network-interface-attribute-4", + "title": "To modify the sourceDestCheck attribute of a network interface" + } + ], + "ModifySnapshotAttribute": [ + { + "input": { + "Attribute": "createVolumePermission", + "OperationType": "remove", + "SnapshotId": "snap-1234567890abcdef0", + "UserIds": [ + "123456789012" + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies snapshot ``snap-1234567890abcdef0`` to remove the create volume permission for a user with the account ID ``123456789012``. If the command succeeds, no output is returned.", + "id": "to-modify-a-snapshot-attribute-1472508385907", + "title": "To modify a snapshot attribute" + }, + { + "input": { + "Attribute": "createVolumePermission", + "GroupNames": [ + "all" + ], + "OperationType": "add", + "SnapshotId": "snap-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example makes the snapshot ``snap-1234567890abcdef0`` public.", + "id": "to-make-a-snapshot-public-1472508470529", + "title": "To make a snapshot public" + } + ], + "ModifySpotFleetRequest": [ + { + "input": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "TargetCapacity": 20 + }, + "output": { + "Return": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example increases the target capacity of the specified Spot fleet request.", + "id": "ec2-modify-spot-fleet-request-1", + "title": "To increase the target capacity of a Spot fleet request" + }, + { + "input": { + "ExcessCapacityTerminationPolicy": "NoTermination ", + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "TargetCapacity": 10 + }, + "output": { + "Return": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example decreases the target capacity of the specified Spot fleet request without terminating any Spot Instances as a result.", + "id": "ec2-modify-spot-fleet-request-2", + "title": "To decrease the target capacity of a Spot fleet request" + } + ], + "ModifySubnetAttribute": [ + { + "input": { + "MapPublicIpOnLaunch": true, + "SubnetId": "subnet-1a2b3c4d" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the specified subnet so that all instances launched into this subnet are assigned a public IP address.", + "id": "ec2-modify-subnet-attribute-1", + "title": "To change a subnet's public IP addressing behavior" + } + ], + "ModifyVolumeAttribute": [ + { + "input": { + "AutoEnableIO": { + "Value": true + }, + "DryRun": true, + "VolumeId": "vol-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example sets the ``autoEnableIo`` attribute of the volume with the ID ``vol-1234567890abcdef0`` to ``true``. If the command succeeds, no output is returned.", + "id": "to-modify-a-volume-attribute-1472508596749", + "title": "To modify a volume attribute" + } + ], + "ModifyVpcAttribute": [ + { + "input": { + "EnableDnsSupport": { + "Value": false + }, + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the enableDnsSupport attribute. This attribute indicates whether DNS resolution is enabled for the VPC. If this attribute is true, the Amazon DNS server resolves DNS hostnames for instances in the VPC to their corresponding IP addresses; otherwise, it does not.", + "id": "ec2-modify-vpc-attribute-1", + "title": "To modify the enableDnsSupport attribute" + }, + { + "input": { + "EnableDnsHostnames": { + "Value": false + }, + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the enableDnsHostnames attribute. This attribute indicates whether instances launched in the VPC get DNS hostnames. If this attribute is true, instances in the VPC get DNS hostnames; otherwise, they do not.", + "id": "ec2-modify-vpc-attribute-2", + "title": "To modify the enableDnsHostnames attribute" + } + ], + "MoveAddressToVpc": [ + { + "input": { + "PublicIp": "54.123.4.56" + }, + "output": { + "Status": "MoveInProgress" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example moves the specified Elastic IP address to the EC2-VPC platform.", + "id": "ec2-move-address-to-vpc-1", + "title": "To move an address to EC2-VPC" + } + ], + "PurchaseScheduledInstances": [ + { + "input": { + "PurchaseRequests": [ + { + "InstanceCount": 1, + "PurchaseToken": "eyJ2IjoiMSIsInMiOjEsImMiOi..." + } + ] + }, + "output": { + "ScheduledInstanceSet": [ + { + "AvailabilityZone": "us-west-2b", + "CreateDate": "2016-01-25T21:43:38.612Z", + "HourlyPrice": "0.095", + "InstanceCount": 1, + "InstanceType": "c4.large", + "NetworkPlatform": "EC2-VPC", + "NextSlotStartTime": "2016-01-31T09:00:00Z", + "Platform": "Linux/UNIX", + "Recurrence": { + "Frequency": "Weekly", + "Interval": 1, + "OccurrenceDaySet": [ + 1 + ], + "OccurrenceRelativeToEnd": false, + "OccurrenceUnit": "" + }, + "ScheduledInstanceId": "sci-1234-1234-1234-1234-123456789012", + "SlotDurationInHours": 32, + "TermEndDate": "2017-01-31T09:00:00Z", + "TermStartDate": "2016-01-31T09:00:00Z", + "TotalScheduledInstanceHours": 1696 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example purchases a Scheduled Instance.", + "id": "ec2-purchase-scheduled-instances-1", + "title": "To purchase a Scheduled Instance" + } + ], + "ReleaseAddress": [ + { + "input": { + "AllocationId": "eipalloc-64d5890a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example releases an Elastic IP address for use with instances in a VPC.", + "id": "ec2-release-address-1", + "title": "To release an Elastic IP address for EC2-VPC" + }, + { + "input": { + "PublicIp": "198.51.100.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example releases an Elastic IP address for use with instances in EC2-Classic.", + "id": "ec2-release-address-2", + "title": "To release an Elastic IP addresses for EC2-Classic" + } + ], + "ReplaceNetworkAclAssociation": [ + { + "input": { + "AssociationId": "aclassoc-e5b95c8c", + "NetworkAclId": "acl-5fb85d36" + }, + "output": { + "NewAssociationId": "aclassoc-3999875b" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified network ACL with the subnet for the specified network ACL association.", + "id": "ec2-replace-network-acl-association-1", + "title": "To replace the network ACL associated with a subnet" + } + ], + "ReplaceNetworkAclEntry": [ + { + "input": { + "CidrBlock": "203.0.113.12/24", + "Egress": false, + "NetworkAclId": "acl-5fb85d36", + "PortRange": { + "From": 53, + "To": 53 + }, + "Protocol": "udp", + "RuleAction": "allow", + "RuleNumber": 100 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example replaces an entry for the specified network ACL. The new rule 100 allows ingress traffic from 203.0.113.12/24 on UDP port 53 (DNS) into any associated subnet.", + "id": "ec2-replace-network-acl-entry-1", + "title": "To replace a network ACL entry" + } + ], + "ReplaceRoute": [ + { + "input": { + "DestinationCidrBlock": "10.0.0.0/16", + "GatewayId": "vgw-9a4cacf3", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example replaces the specified route in the specified table table. The new route matches the specified CIDR and sends the traffic to the specified virtual private gateway.", + "id": "ec2-replace-route-1", + "title": "To replace a route" + } + ], + "ReplaceRouteTableAssociation": [ + { + "input": { + "AssociationId": "rtbassoc-781d0d1a", + "RouteTableId": "rtb-22574640" + }, + "output": { + "NewAssociationId": "rtbassoc-3a1f0f58" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified route table with the subnet for the specified route table association.", + "id": "ec2-replace-route-table-association-1", + "title": "To replace the route table associated with a subnet" + } + ], + "RequestSpotFleet": [ + { + "input": { + "SpotFleetRequestConfig": { + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "KeyName": "my-key-pair", + "SecurityGroups": [ + { + "GroupId": "sg-1a2b3c4d" + } + ], + "SubnetId": "subnet-1a2b3c4d, subnet-3c4d5e6f" + } + ], + "SpotPrice": "0.04", + "TargetCapacity": 2 + } + }, + "output": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a Spot fleet request with two launch specifications that differ only by subnet. The Spot fleet launches the instances in the specified subnet with the lowest price. If the instances are launched in a default VPC, they receive a public IP address by default. If the instances are launched in a nondefault VPC, they do not receive a public IP address by default. Note that you can't specify different subnets from the same Availability Zone in a Spot fleet request.", + "id": "ec2-request-spot-fleet-1", + "title": "To request a Spot fleet in the subnet with the lowest price" + }, + { + "input": { + "SpotFleetRequestConfig": { + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "KeyName": "my-key-pair", + "Placement": { + "AvailabilityZone": "us-west-2a, us-west-2b" + }, + "SecurityGroups": [ + { + "GroupId": "sg-1a2b3c4d" + } + ] + } + ], + "SpotPrice": "0.04", + "TargetCapacity": 2 + } + }, + "output": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a Spot fleet request with two launch specifications that differ only by Availability Zone. The Spot fleet launches the instances in the specified Availability Zone with the lowest price. If your account supports EC2-VPC only, Amazon EC2 launches the Spot instances in the default subnet of the Availability Zone. If your account supports EC2-Classic, Amazon EC2 launches the instances in EC2-Classic in the Availability Zone.", + "id": "ec2-request-spot-fleet-2", + "title": "To request a Spot fleet in the Availability Zone with the lowest price" + }, + { + "input": { + "SpotFleetRequestConfig": { + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::880185128111:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "KeyName": "my-key-pair", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeviceIndex": 0, + "Groups": [ + "sg-1a2b3c4d" + ], + "SubnetId": "subnet-1a2b3c4d" + } + ] + } + ], + "SpotPrice": "0.04", + "TargetCapacity": 2 + } + }, + "output": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example assigns public addresses to instances launched in a nondefault VPC. Note that when you specify a network interface, you must include the subnet ID and security group ID using the network interface.", + "id": "ec2-request-spot-fleet-3", + "title": "To launch Spot instances in a subnet and assign them public IP addresses" + }, + { + "input": { + "SpotFleetRequestConfig": { + "AllocationStrategy": "diversified", + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "ImageId": "ami-1a2b3c4d", + "InstanceType": "c4.2xlarge", + "SubnetId": "subnet-1a2b3c4d" + }, + { + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.2xlarge", + "SubnetId": "subnet-1a2b3c4d" + }, + { + "ImageId": "ami-1a2b3c4d", + "InstanceType": "r3.2xlarge", + "SubnetId": "subnet-1a2b3c4d" + } + ], + "SpotPrice": "0.70", + "TargetCapacity": 30 + } + }, + "output": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a Spot fleet request that launches 30 instances using the diversified allocation strategy. The launch specifications differ by instance type. The Spot fleet distributes the instances across the launch specifications such that there are 10 instances of each type.", + "id": "ec2-request-spot-fleet-4", + "title": "To request a Spot fleet using the diversified allocation strategy" + } + ], + "RequestSpotInstances": [ + { + "input": { + "InstanceCount": 5, + "LaunchSpecification": { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "KeyName": "my-key-pair", + "Placement": { + "AvailabilityZone": "us-west-2a" + }, + "SecurityGroupIds": [ + "sg-1a2b3c4d" + ] + }, + "SpotPrice": "0.03", + "Type": "one-time" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a one-time Spot Instance request for five instances in the specified Availability Zone. If your account supports EC2-VPC only, Amazon EC2 launches the instances in the default subnet of the specified Availability Zone. If your account supports EC2-Classic, Amazon EC2 launches the instances in EC2-Classic in the specified Availability Zone.", + "id": "ec2-request-spot-instances-1", + "title": "To create a one-time Spot Instance request" + }, + { + "input": { + "InstanceCount": 5, + "LaunchSpecification": { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "SecurityGroupIds": [ + "sg-1a2b3c4d" + ], + "SubnetId": "subnet-1a2b3c4d" + }, + "SpotPrice": "0.050", + "Type": "one-time" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example command creates a one-time Spot Instance request for five instances in the specified subnet. Amazon EC2 launches the instances in the specified subnet. If the VPC is a nondefault VPC, the instances do not receive a public IP address by default.", + "id": "ec2-request-spot-instances-2", + "title": "To create a one-time Spot Instance request" + } + ], + "ResetSnapshotAttribute": [ + { + "input": { + "Attribute": "createVolumePermission", + "SnapshotId": "snap-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example resets the create volume permissions for snapshot ``snap-1234567890abcdef0``. If the command succeeds, no output is returned.", + "id": "to-reset-a-snapshot-attribute-1472508825735", + "title": "To reset a snapshot attribute" + } + ], + "RestoreAddressToClassic": [ + { + "input": { + "PublicIp": "198.51.100.0" + }, + "output": { + "PublicIp": "198.51.100.0", + "Status": "MoveInProgress" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example restores the specified Elastic IP address to the EC2-Classic platform.", + "id": "ec2-restore-address-to-classic-1", + "title": "To restore an address to EC2-Classic" + } + ], + "RunScheduledInstances": [ + { + "input": { + "InstanceCount": 1, + "LaunchSpecification": { + "IamInstanceProfile": { + "Name": "my-iam-role" + }, + "ImageId": "ami-12345678", + "InstanceType": "c4.large", + "KeyName": "my-key-pair", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeviceIndex": 0, + "Groups": [ + "sg-12345678" + ], + "SubnetId": "subnet-12345678" + } + ] + }, + "ScheduledInstanceId": "sci-1234-1234-1234-1234-123456789012" + }, + "output": { + "InstanceIdSet": [ + "i-1234567890abcdef0" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example launches the specified Scheduled Instance in a VPC.", + "id": "ec2-run-scheduled-instances-1", + "title": "To launch a Scheduled Instance in a VPC" + }, + { + "input": { + "InstanceCount": 1, + "LaunchSpecification": { + "IamInstanceProfile": { + "Name": "my-iam-role" + }, + "ImageId": "ami-12345678", + "InstanceType": "c4.large", + "KeyName": "my-key-pair", + "Placement": { + "AvailabilityZone": "us-west-2b" + }, + "SecurityGroupIds": [ + "sg-12345678" + ] + }, + "ScheduledInstanceId": "sci-1234-1234-1234-1234-123456789012" + }, + "output": { + "InstanceIdSet": [ + "i-1234567890abcdef0" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example launches the specified Scheduled Instance in EC2-Classic.", + "id": "ec2-run-scheduled-instances-2", + "title": "To launch a Scheduled Instance in EC2-Classic" + } + ], + "UnassignPrivateIpAddresses": [ + { + "input": { + "NetworkInterfaceId": "eni-e5aa89a3", + "PrivateIpAddresses": [ + "10.0.0.82" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example unassigns the specified private IP address from the specified network interface.", + "id": "ec2-unassign-private-ip-addresses-1", + "title": "To unassign a secondary private IP address from a network interface" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/paginators-1.json new file mode 100644 index 0000000..2bd01ad --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/paginators-1.json @@ -0,0 +1,63 @@ +{ + "pagination": { + "DescribeInstanceStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceStatuses" + }, + "DescribeInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Reservations" + }, + "DescribeReservedInstancesOfferings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ReservedInstancesOfferings" + }, + "DescribeReservedInstancesModifications": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ReservedInstancesModifications" + }, + "DescribeSnapshots": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Snapshots" + }, + "DescribeSpotFleetRequests": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpotFleetRequestConfigs" + }, + "DescribeSpotPriceHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpotPriceHistory" + }, + "DescribeTags": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Tags" + }, + "DescribeVolumeStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VolumeStatuses" + }, + "DescribeVolumes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Volumes" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/service-2.json.gz new file mode 100644 index 0000000..d121d33 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/waiters-2.json new file mode 100644 index 0000000..aa36a04 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-04-01/waiters-2.json @@ -0,0 +1,607 @@ +{ + "version": 2, + "waiters": { + "InstanceExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeInstances", + "acceptors": [ + { + "matcher": "path", + "expected": true, + "argument": "length(Reservations[]) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidInstanceID.NotFound", + "state": "retry" + } + ] + }, + "BundleTaskComplete": { + "delay": 15, + "operation": "DescribeBundleTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "complete", + "matcher": "pathAll", + "state": "success", + "argument": "BundleTasks[].State" + }, + { + "expected": "failed", + "matcher": "pathAny", + "state": "failure", + "argument": "BundleTasks[].State" + } + ] + }, + "ConsoleOutputAvailable": { + "operation": "GetConsoleOutput", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "length(Output || '') > `0`", + "expected": true + } + ] + }, + "ConversionTaskCancelled": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskCompleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelled", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelling", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskDeleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "CustomerGatewayAvailable": { + "delay": 15, + "operation": "DescribeCustomerGateways", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + } + ] + }, + "ExportTaskCancelled": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ExportTaskCompleted": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ImageExists": { + "operation": "DescribeImages", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "matcher": "path", + "expected": true, + "argument": "length(Images[]) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidAMIID.NotFound", + "state": "retry" + } + ] + }, + "ImageAvailable": { + "operation": "DescribeImages", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Images[].State", + "expected": "available" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "Images[].State", + "expected": "failed" + } + ] + }, + "InstanceRunning": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "running", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "shutting-down", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "matcher": "error", + "expected": "InvalidInstanceID.NotFound", + "state": "retry" + } + ] + }, + "InstanceStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].InstanceStatus.Status", + "expected": "ok" + }, + { + "matcher": "error", + "expected": "InvalidInstanceID.NotFound", + "state": "retry" + } + ] + }, + "InstanceStopped": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "stopped", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "InstanceTerminated": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "terminated", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "KeyPairExists": { + "operation": "DescribeKeyPairs", + "delay": 5, + "maxAttempts": 6, + "acceptors": [ + { + "expected": true, + "matcher": "path", + "state": "success", + "argument": "length(KeyPairs[].KeyName) > `0`" + }, + { + "expected": "InvalidKeyPair.NotFound", + "matcher": "error", + "state": "retry" + } + ] + }, + "NatGatewayAvailable": { + "operation": "DescribeNatGateways", + "delay": 15, + "maxAttempts": 40, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "NatGateways[].State", + "expected": "available" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "NatGateways[].State", + "expected": "failed" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "NatGateways[].State", + "expected": "deleting" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "NatGateways[].State", + "expected": "deleted" + }, + { + "state": "retry", + "matcher": "error", + "expected": "NatGatewayNotFound" + } + ] + }, + "NetworkAclExists": { + "operation": "DescribeNetworkAcls", + "delay": 20, + "maxAttempts": 10, + "acceptors": [ + { + "matcher": "path", + "expected": true, + "argument": "length(NetworkAcls[]) > `0`", + "state": "success" + }, + { + "expected": "InvalidNetworkAclID.NotFound", + "matcher": "error", + "state": "retry" + } + ] + }, + "NetworkInterfaceAvailable": { + "operation": "DescribeNetworkInterfaces", + "delay": 20, + "maxAttempts": 10, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "NetworkInterfaces[].Status" + }, + { + "expected": "InvalidNetworkInterfaceID.NotFound", + "matcher": "error", + "state": "failure" + } + ] + }, + "PasswordDataAvailable": { + "operation": "GetPasswordData", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "length(PasswordData) > `0`", + "expected": true + } + ] + }, + "SnapshotCompleted": { + "delay": 15, + "operation": "DescribeSnapshots", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "Snapshots[].State" + } + ] + }, + "SpotInstanceRequestFulfilled": { + "operation": "DescribeSpotInstanceRequests", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "fulfilled" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "schedule-expired" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "canceled-before-fulfillment" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "bad-parameters" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "system-error" + } + ] + }, + "SubnetAvailable": { + "delay": 15, + "operation": "DescribeSubnets", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Subnets[].State" + } + ] + }, + "SystemStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].SystemStatus.Status", + "expected": "ok" + } + ] + }, + "VolumeAvailable": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VolumeDeleted": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "matcher": "error", + "expected": "InvalidVolume.NotFound", + "state": "success" + } + ] + }, + "VolumeInUse": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "in-use", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VpcAvailable": { + "delay": 15, + "operation": "DescribeVpcs", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Vpcs[].State" + } + ] + }, + "VpnConnectionAvailable": { + "delay": 60, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + }, + "VpnConnectionDeleted": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + }, + "VpcPeeringConnectionExists": { + "delay": 15, + "operation": "DescribeVpcPeeringConnections", + "maxAttempts": 40, + "acceptors": [ + { + "matcher": "status", + "expected": 200, + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidVpcPeeringConnectionID.NotFound", + "state": "retry" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a830d3d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/examples-1.json new file mode 100644 index 0000000..f6a8719 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/examples-1.json @@ -0,0 +1,3740 @@ +{ + "version": "1.0", + "examples": { + "AllocateAddress": [ + { + "input": { + "Domain": "vpc" + }, + "output": { + "AllocationId": "eipalloc-64d5890a", + "Domain": "vpc", + "PublicIp": "203.0.113.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example allocates an Elastic IP address to use with an instance in a VPC.", + "id": "ec2-allocate-address-1", + "title": "To allocate an Elastic IP address for EC2-VPC" + }, + { + "output": { + "Domain": "standard", + "PublicIp": "198.51.100.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example allocates an Elastic IP address to use with an instance in EC2-Classic.", + "id": "ec2-allocate-address-2", + "title": "To allocate an Elastic IP address for EC2-Classic" + } + ], + "AssignPrivateIpAddresses": [ + { + "input": { + "NetworkInterfaceId": "eni-e5aa89a3", + "PrivateIpAddresses": [ + "10.0.0.82" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example assigns the specified secondary private IP address to the specified network interface.", + "id": "ec2-assign-private-ip-addresses-1", + "title": "To assign a specific secondary private IP address to an interface" + }, + { + "input": { + "NetworkInterfaceId": "eni-e5aa89a3", + "SecondaryPrivateIpAddressCount": 2 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example assigns two secondary private IP addresses to the specified network interface. Amazon EC2 automatically assigns these IP addresses from the available IP addresses in the CIDR block range of the subnet the network interface is associated with.", + "id": "ec2-assign-private-ip-addresses-2", + "title": "To assign secondary private IP addresses that Amazon EC2 selects to an interface" + } + ], + "AssociateAddress": [ + { + "input": { + "AllocationId": "eipalloc-64d5890a", + "InstanceId": "i-0b263919b6498b123" + }, + "output": { + "AssociationId": "eipassoc-2bebb745" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified Elastic IP address with the specified instance in a VPC.", + "id": "ec2-associate-address-1", + "title": "To associate an Elastic IP address in EC2-VPC" + }, + { + "input": { + "AllocationId": "eipalloc-64d5890a", + "NetworkInterfaceId": "eni-1a2b3c4d" + }, + "output": { + "AssociationId": "eipassoc-2bebb745" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified Elastic IP address with the specified network interface.", + "id": "ec2-associate-address-2", + "title": "To associate an Elastic IP address with a network interface" + }, + { + "input": { + "InstanceId": "i-07ffe74c7330ebf53", + "PublicIp": "198.51.100.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates an Elastic IP address with an instance in EC2-Classic.", + "id": "ec2-associate-address-3", + "title": "To associate an Elastic IP address in EC2-Classic" + } + ], + "AssociateDhcpOptions": [ + { + "input": { + "DhcpOptionsId": "dopt-d9070ebb", + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified DHCP options set with the specified VPC.", + "id": "ec2-associate-dhcp-options-1", + "title": "To associate a DHCP options set with a VPC" + }, + { + "input": { + "DhcpOptionsId": "default", + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the default DHCP options set with the specified VPC.", + "id": "ec2-associate-dhcp-options-2", + "title": "To associate the default DHCP options set with a VPC" + } + ], + "AssociateRouteTable": [ + { + "input": { + "RouteTableId": "rtb-22574640", + "SubnetId": "subnet-9d4a7b6" + }, + "output": { + "AssociationId": "rtbassoc-781d0d1a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified route table with the specified subnet.", + "id": "ec2-associate-route-table-1", + "title": "To associate a route table with a subnet" + } + ], + "AttachInternetGateway": [ + { + "input": { + "InternetGatewayId": "igw-c0a643a9", + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example attaches the specified Internet gateway to the specified VPC.", + "id": "ec2-attach-internet-gateway-1", + "title": "To attach an Internet gateway to a VPC" + } + ], + "AttachNetworkInterface": [ + { + "input": { + "DeviceIndex": 1, + "InstanceId": "i-1234567890abcdef0", + "NetworkInterfaceId": "eni-e5aa89a3" + }, + "output": { + "AttachmentId": "eni-attach-66c4350a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example attaches the specified network interface to the specified instance.", + "id": "ec2-attach-network-interface-1", + "title": "To attach a network interface to an instance" + } + ], + "AttachVolume": [ + { + "input": { + "Device": "/dev/sdf", + "InstanceId": "i-01474ef662b89480", + "VolumeId": "vol-1234567890abcdef0" + }, + "output": { + "AttachTime": "2016-08-29T18:52:32.724Z", + "Device": "/dev/sdf", + "InstanceId": "i-01474ef662b89480", + "State": "attaching", + "VolumeId": "vol-1234567890abcdef0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example attaches a volume (``vol-1234567890abcdef0``) to an instance (``i-01474ef662b89480``) as ``/dev/sdf``.", + "id": "to-attach-a-volume-to-an-instance-1472499213109", + "title": "To attach a volume to an instance" + } + ], + "CancelSpotFleetRequests": [ + { + "input": { + "SpotFleetRequestIds": [ + "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + ], + "TerminateInstances": true + }, + "output": { + "SuccessfulFleetRequests": [ + { + "CurrentSpotFleetRequestState": "cancelled_running", + "PreviousSpotFleetRequestState": "active", + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example cancels the specified Spot fleet request and terminates its associated Spot Instances.", + "id": "ec2-cancel-spot-fleet-requests-1", + "title": "To cancel a Spot fleet request" + }, + { + "input": { + "SpotFleetRequestIds": [ + "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + ], + "TerminateInstances": false + }, + "output": { + "SuccessfulFleetRequests": [ + { + "CurrentSpotFleetRequestState": "cancelled_terminating", + "PreviousSpotFleetRequestState": "active", + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example cancels the specified Spot fleet request without terminating its associated Spot Instances.", + "id": "ec2-cancel-spot-fleet-requests-2", + "title": "To cancel a Spot fleet request without terminating its Spot Instances" + } + ], + "CancelSpotInstanceRequests": [ + { + "input": { + "SpotInstanceRequestIds": [ + "sir-08b93456" + ] + }, + "output": { + "CancelledSpotInstanceRequests": [ + { + "SpotInstanceRequestId": "sir-08b93456", + "State": "cancelled" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example cancels a Spot Instance request.", + "id": "ec2-cancel-spot-instance-requests-1", + "title": "To cancel Spot Instance requests" + } + ], + "ConfirmProductInstance": [ + { + "input": { + "InstanceId": "i-1234567890abcdef0", + "ProductCode": "774F4FF8" + }, + "output": { + "OwnerId": "123456789012" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example determines whether the specified product code is associated with the specified instance.", + "id": "to-confirm-the-product-instance-1472712108494", + "title": "To confirm the product instance" + } + ], + "CopySnapshot": [ + { + "input": { + "Description": "This is my copied snapshot.", + "DestinationRegion": "us-east-1", + "SourceRegion": "us-west-2", + "SourceSnapshotId": "snap-066877671789bd71b" + }, + "output": { + "SnapshotId": "snap-066877671789bd71b" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example copies a snapshot with the snapshot ID of ``snap-066877671789bd71b`` from the ``us-west-2`` region to the ``us-east-1`` region and adds a short description to identify the snapshot.", + "id": "to-copy-a-snapshot-1472502259774", + "title": "To copy a snapshot" + } + ], + "CreateCustomerGateway": [ + { + "input": { + "BgpAsn": 65534, + "PublicIp": "12.1.2.3", + "Type": "ipsec.1" + }, + "output": { + "CustomerGateway": { + "BgpAsn": "65534", + "CustomerGatewayId": "cgw-0e11f167", + "IpAddress": "12.1.2.3", + "State": "available", + "Type": "ipsec.1" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a customer gateway with the specified IP address for its outside interface.", + "id": "ec2-create-customer-gateway-1", + "title": "To create a customer gateway" + } + ], + "CreateDhcpOptions": [ + { + "input": { + "DhcpConfigurations": [ + { + "Key": "domain-name-servers", + "Values": [ + "10.2.5.1", + "10.2.5.2" + ] + } + ] + }, + "output": { + "DhcpOptions": { + "DhcpConfigurations": [ + { + "Key": "domain-name-servers", + "Values": [ + { + "Value": "10.2.5.2" + }, + { + "Value": "10.2.5.1" + } + ] + } + ], + "DhcpOptionsId": "dopt-d9070ebb" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a DHCP options set.", + "id": "ec2-create-dhcp-options-1", + "title": "To create a DHCP options set" + } + ], + "CreateInternetGateway": [ + { + "output": { + "InternetGateway": { + "Attachments": [ + + ], + "InternetGatewayId": "igw-c0a643a9", + "Tags": [ + + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an Internet gateway.", + "id": "ec2-create-internet-gateway-1", + "title": "To create an Internet gateway" + } + ], + "CreateKeyPair": [ + { + "input": { + "KeyName": "my-key-pair" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a key pair named my-key-pair.", + "id": "ec2-create-key-pair-1", + "title": "To create a key pair" + } + ], + "CreateNatGateway": [ + { + "input": { + "AllocationId": "eipalloc-37fc1a52", + "SubnetId": "subnet-1a2b3c4d" + }, + "output": { + "NatGateway": { + "CreateTime": "2015-12-17T12:45:26.732Z", + "NatGatewayAddresses": [ + { + "AllocationId": "eipalloc-37fc1a52" + } + ], + "NatGatewayId": "nat-08d48af2a8e83edfd", + "State": "pending", + "SubnetId": "subnet-1a2b3c4d", + "VpcId": "vpc-1122aabb" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a NAT gateway in subnet subnet-1a2b3c4d and associates an Elastic IP address with the allocation ID eipalloc-37fc1a52 with the NAT gateway.", + "id": "ec2-create-nat-gateway-1", + "title": "To create a NAT gateway" + } + ], + "CreateNetworkAcl": [ + { + "input": { + "VpcId": "vpc-a01106c2" + }, + "output": { + "NetworkAcl": { + "Associations": [ + + ], + "Entries": [ + { + "CidrBlock": "0.0.0.0/0", + "Egress": true, + "Protocol": "-1", + "RuleAction": "deny", + "RuleNumber": 32767 + }, + { + "CidrBlock": "0.0.0.0/0", + "Egress": false, + "Protocol": "-1", + "RuleAction": "deny", + "RuleNumber": 32767 + } + ], + "IsDefault": false, + "NetworkAclId": "acl-5fb85d36", + "Tags": [ + + ], + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a network ACL for the specified VPC.", + "id": "ec2-create-network-acl-1", + "title": "To create a network ACL" + } + ], + "CreateNetworkAclEntry": [ + { + "input": { + "CidrBlock": "0.0.0.0/0", + "Egress": false, + "NetworkAclId": "acl-5fb85d36", + "PortRange": { + "From": 53, + "To": 53 + }, + "Protocol": "udp", + "RuleAction": "allow", + "RuleNumber": 100 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an entry for the specified network ACL. The rule allows ingress traffic from anywhere (0.0.0.0/0) on UDP port 53 (DNS) into any associated subnet.", + "id": "ec2-create-network-acl-entry-1", + "title": "To create a network ACL entry" + } + ], + "CreateNetworkInterface": [ + { + "input": { + "Description": "my network interface", + "Groups": [ + "sg-903004f8" + ], + "PrivateIpAddress": "10.0.2.17", + "SubnetId": "subnet-9d4a7b6c" + }, + "output": { + "NetworkInterface": { + "AvailabilityZone": "us-east-1d", + "Description": "my network interface", + "Groups": [ + { + "GroupId": "sg-903004f8", + "GroupName": "default" + } + ], + "MacAddress": "02:1a:80:41:52:9c", + "NetworkInterfaceId": "eni-e5aa89a3", + "OwnerId": "123456789012", + "PrivateIpAddress": "10.0.2.17", + "PrivateIpAddresses": [ + { + "Primary": true, + "PrivateIpAddress": "10.0.2.17" + } + ], + "RequesterManaged": false, + "SourceDestCheck": true, + "Status": "pending", + "SubnetId": "subnet-9d4a7b6c", + "TagSet": [ + + ], + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a network interface for the specified subnet.", + "id": "ec2-create-network-interface-1", + "title": "To create a network interface" + } + ], + "CreatePlacementGroup": [ + { + "input": { + "GroupName": "my-cluster", + "Strategy": "cluster" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a placement group with the specified name.", + "id": "to-create-a-placement-group-1472712245768", + "title": "To create a placement group" + } + ], + "CreateRoute": [ + { + "input": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": "igw-c0a643a9", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a route for the specified route table. The route matches all traffic (0.0.0.0/0) and routes it to the specified Internet gateway.", + "id": "ec2-create-route-1", + "title": "To create a route" + } + ], + "CreateRouteTable": [ + { + "input": { + "VpcId": "vpc-a01106c2" + }, + "output": { + "RouteTable": { + "Associations": [ + + ], + "PropagatingVgws": [ + + ], + "RouteTableId": "rtb-22574640", + "Routes": [ + { + "DestinationCidrBlock": "10.0.0.0/16", + "GatewayId": "local", + "State": "active" + } + ], + "Tags": [ + + ], + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a route table for the specified VPC.", + "id": "ec2-create-route-table-1", + "title": "To create a route table" + } + ], + "CreateSnapshot": [ + { + "input": { + "Description": "This is my root volume snapshot.", + "VolumeId": "vol-1234567890abcdef0" + }, + "output": { + "Description": "This is my root volume snapshot.", + "OwnerId": "012345678910", + "SnapshotId": "snap-066877671789bd71b", + "StartTime": "2014-02-28T21:06:01.000Z", + "State": "pending", + "Tags": [ + + ], + "VolumeId": "vol-1234567890abcdef0", + "VolumeSize": 8 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a snapshot of the volume with a volume ID of ``vol-1234567890abcdef0`` and a short description to identify the snapshot.", + "id": "to-create-a-snapshot-1472502529790", + "title": "To create a snapshot" + } + ], + "CreateSpotDatafeedSubscription": [ + { + "input": { + "Bucket": "my-s3-bucket", + "Prefix": "spotdata" + }, + "output": { + "SpotDatafeedSubscription": { + "Bucket": "my-s3-bucket", + "OwnerId": "123456789012", + "Prefix": "spotdata", + "State": "Active" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a Spot Instance data feed for your AWS account.", + "id": "ec2-create-spot-datafeed-subscription-1", + "title": "To create a Spot Instance datafeed" + } + ], + "CreateSubnet": [ + { + "input": { + "CidrBlock": "10.0.1.0/24", + "VpcId": "vpc-a01106c2" + }, + "output": { + "Subnet": { + "AvailabilityZone": "us-west-2c", + "AvailableIpAddressCount": 251, + "CidrBlock": "10.0.1.0/24", + "State": "pending", + "SubnetId": "subnet-9d4a7b6c", + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a subnet in the specified VPC with the specified CIDR block. We recommend that you let us select an Availability Zone for you.", + "id": "ec2-create-subnet-1", + "title": "To create a subnet" + } + ], + "CreateTags": [ + { + "input": { + "Resources": [ + "ami-78a54011" + ], + "Tags": [ + { + "Key": "Stack", + "Value": "production" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds the tag Stack=production to the specified image, or overwrites an existing tag for the AMI where the tag key is Stack.", + "id": "ec2-create-tags-1", + "title": "To add a tag to a resource" + } + ], + "CreateVolume": [ + { + "input": { + "AvailabilityZone": "us-east-1a", + "Size": 80, + "VolumeType": "gp2" + }, + "output": { + "AvailabilityZone": "us-east-1a", + "CreateTime": "2016-08-29T18:52:32.724Z", + "Encrypted": false, + "Iops": 240, + "Size": 80, + "SnapshotId": "", + "State": "creating", + "VolumeId": "vol-6b60b7c7", + "VolumeType": "gp2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an 80 GiB General Purpose (SSD) volume in the Availability Zone ``us-east-1a``.", + "id": "to-create-a-new-volume-1472496724296", + "title": "To create a new volume" + }, + { + "input": { + "AvailabilityZone": "us-east-1a", + "Iops": 1000, + "SnapshotId": "snap-066877671789bd71b", + "VolumeType": "io1" + }, + "output": { + "Attachments": [ + + ], + "AvailabilityZone": "us-east-1a", + "CreateTime": "2016-08-29T18:52:32.724Z", + "Iops": 1000, + "Size": 500, + "SnapshotId": "snap-066877671789bd71b", + "State": "creating", + "Tags": [ + + ], + "VolumeId": "vol-1234567890abcdef0", + "VolumeType": "io1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a new Provisioned IOPS (SSD) volume with 1000 provisioned IOPS from a snapshot in the Availability Zone ``us-east-1a``.", + "id": "to-create-a-new-provisioned-iops-ssd-volume-from-a-snapshot-1472498975176", + "title": "To create a new Provisioned IOPS (SSD) volume from a snapshot" + } + ], + "CreateVpc": [ + { + "input": { + "CidrBlock": "10.0.0.0/16" + }, + "output": { + "Vpc": { + "CidrBlock": "10.0.0.0/16", + "DhcpOptionsId": "dopt-7a8b9c2d", + "InstanceTenancy": "default", + "State": "pending", + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a VPC with the specified CIDR block.", + "id": "ec2-create-vpc-1", + "title": "To create a VPC" + } + ], + "DeleteCustomerGateway": [ + { + "input": { + "CustomerGatewayId": "cgw-0e11f167" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified customer gateway.", + "id": "ec2-delete-customer-gateway-1", + "title": "To delete a customer gateway" + } + ], + "DeleteDhcpOptions": [ + { + "input": { + "DhcpOptionsId": "dopt-d9070ebb" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified DHCP options set.", + "id": "ec2-delete-dhcp-options-1", + "title": "To delete a DHCP options set" + } + ], + "DeleteInternetGateway": [ + { + "input": { + "InternetGatewayId": "igw-c0a643a9" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified Internet gateway.", + "id": "ec2-delete-internet-gateway-1", + "title": "To delete an Internet gateway" + } + ], + "DeleteKeyPair": [ + { + "input": { + "KeyName": "my-key-pair" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified key pair.", + "id": "ec2-delete-key-pair-1", + "title": "To delete a key pair" + } + ], + "DeleteNatGateway": [ + { + "input": { + "NatGatewayId": "nat-04ae55e711cec5680" + }, + "output": { + "NatGatewayId": "nat-04ae55e711cec5680" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified NAT gateway.", + "id": "ec2-delete-nat-gateway-1", + "title": "To delete a NAT gateway" + } + ], + "DeleteNetworkAcl": [ + { + "input": { + "NetworkAclId": "acl-5fb85d36" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified network ACL.", + "id": "ec2-delete-network-acl-1", + "title": "To delete a network ACL" + } + ], + "DeleteNetworkAclEntry": [ + { + "input": { + "Egress": true, + "NetworkAclId": "acl-5fb85d36", + "RuleNumber": 100 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes ingress rule number 100 from the specified network ACL.", + "id": "ec2-delete-network-acl-entry-1", + "title": "To delete a network ACL entry" + } + ], + "DeleteNetworkInterface": [ + { + "input": { + "NetworkInterfaceId": "eni-e5aa89a3" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified network interface.", + "id": "ec2-delete-network-interface-1", + "title": "To delete a network interface" + } + ], + "DeletePlacementGroup": [ + { + "input": { + "GroupName": "my-cluster" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified placement group.\n", + "id": "to-delete-a-placement-group-1472712349959", + "title": "To delete a placement group" + } + ], + "DeleteRoute": [ + { + "input": { + "DestinationCidrBlock": "0.0.0.0/0", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified route from the specified route table.", + "id": "ec2-delete-route-1", + "title": "To delete a route" + } + ], + "DeleteRouteTable": [ + { + "input": { + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified route table.", + "id": "ec2-delete-route-table-1", + "title": "To delete a route table" + } + ], + "DeleteSnapshot": [ + { + "input": { + "SnapshotId": "snap-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes a snapshot with the snapshot ID of ``snap-1234567890abcdef0``. If the command succeeds, no output is returned.", + "id": "to-delete-a-snapshot-1472503042567", + "title": "To delete a snapshot" + } + ], + "DeleteSpotDatafeedSubscription": [ + { + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes a Spot data feed subscription for the account.", + "id": "ec2-delete-spot-datafeed-subscription-1", + "title": "To cancel a Spot Instance data feed subscription" + } + ], + "DeleteSubnet": [ + { + "input": { + "SubnetId": "subnet-9d4a7b6c" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified subnet.", + "id": "ec2-delete-subnet-1", + "title": "To delete a subnet" + } + ], + "DeleteTags": [ + { + "input": { + "Resources": [ + "ami-78a54011" + ], + "Tags": [ + { + "Key": "Stack", + "Value": "test" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the tag Stack=test from the specified image.", + "id": "ec2-delete-tags-1", + "title": "To delete a tag from a resource" + } + ], + "DeleteVolume": [ + { + "input": { + "VolumeId": "vol-049df61146c4d7901" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes an available volume with the volume ID of ``vol-049df61146c4d7901``. If the command succeeds, no output is returned.", + "id": "to-delete-a-volume-1472503111160", + "title": "To delete a volume" + } + ], + "DeleteVpc": [ + { + "input": { + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified VPC.", + "id": "ec2-delete-vpc-1", + "title": "To delete a VPC" + } + ], + "DescribeAccountAttributes": [ + { + "input": { + "AttributeNames": [ + "supported-platforms" + ] + }, + "output": { + "AccountAttributes": [ + { + "AttributeName": "supported-platforms", + "AttributeValues": [ + { + "AttributeValue": "EC2" + }, + { + "AttributeValue": "VPC" + } + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the supported-platforms attribute for your AWS account.", + "id": "ec2-describe-account-attributes-1", + "title": "To describe a single attribute for your AWS account" + }, + { + "output": { + "AccountAttributes": [ + { + "AttributeName": "supported-platforms", + "AttributeValues": [ + { + "AttributeValue": "EC2" + }, + { + "AttributeValue": "VPC" + } + ] + }, + { + "AttributeName": "vpc-max-security-groups-per-interface", + "AttributeValues": [ + { + "AttributeValue": "5" + } + ] + }, + { + "AttributeName": "max-elastic-ips", + "AttributeValues": [ + { + "AttributeValue": "5" + } + ] + }, + { + "AttributeName": "max-instances", + "AttributeValues": [ + { + "AttributeValue": "20" + } + ] + }, + { + "AttributeName": "vpc-max-elastic-ips", + "AttributeValues": [ + { + "AttributeValue": "5" + } + ] + }, + { + "AttributeName": "default-vpc", + "AttributeValues": [ + { + "AttributeValue": "none" + } + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the attributes for your AWS account.", + "id": "ec2-describe-account-attributes-2", + "title": "To describe all attributes for your AWS account" + } + ], + "DescribeAddresses": [ + { + "output": { + "Addresses": [ + { + "Domain": "standard", + "InstanceId": "i-1234567890abcdef0", + "PublicIp": "198.51.100.0" + }, + { + "AllocationId": "eipalloc-12345678", + "AssociationId": "eipassoc-12345678", + "Domain": "vpc", + "InstanceId": "i-1234567890abcdef0", + "NetworkInterfaceId": "eni-12345678", + "NetworkInterfaceOwnerId": "123456789012", + "PrivateIpAddress": "10.0.1.241", + "PublicIp": "203.0.113.0" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes your Elastic IP addresses.", + "id": "ec2-describe-addresses-1", + "title": "To describe your Elastic IP addresses" + }, + { + "input": { + "Filters": [ + { + "Name": "domain", + "Values": [ + "vpc" + ] + } + ] + }, + "output": { + "Addresses": [ + { + "AllocationId": "eipalloc-12345678", + "AssociationId": "eipassoc-12345678", + "Domain": "vpc", + "InstanceId": "i-1234567890abcdef0", + "NetworkInterfaceId": "eni-12345678", + "NetworkInterfaceOwnerId": "123456789012", + "PrivateIpAddress": "10.0.1.241", + "PublicIp": "203.0.113.0" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes your Elastic IP addresses for use with instances in a VPC.", + "id": "ec2-describe-addresses-2", + "title": "To describe your Elastic IP addresses for EC2-VPC" + }, + { + "input": { + "Filters": [ + { + "Name": "domain", + "Values": [ + "standard" + ] + } + ] + }, + "output": { + "Addresses": [ + { + "Domain": "standard", + "InstanceId": "i-1234567890abcdef0", + "PublicIp": "198.51.100.0" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes your Elastic IP addresses for use with instances in EC2-Classic.", + "id": "ec2-describe-addresses-3", + "title": "To describe your Elastic IP addresses for EC2-Classic" + } + ], + "DescribeAvailabilityZones": [ + { + "output": { + "AvailabilityZones": [ + { + "Messages": [ + + ], + "RegionName": "us-east-1", + "State": "available", + "ZoneName": "us-east-1b" + }, + { + "Messages": [ + + ], + "RegionName": "us-east-1", + "State": "available", + "ZoneName": "us-east-1c" + }, + { + "Messages": [ + + ], + "RegionName": "us-east-1", + "State": "available", + "ZoneName": "us-east-1d" + }, + { + "Messages": [ + + ], + "RegionName": "us-east-1", + "State": "available", + "ZoneName": "us-east-1e" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the Availability Zones that are available to you. The response includes Availability Zones only for the current region.", + "id": "ec2-describe-availability-zones-1", + "title": "To describe your Availability Zones" + } + ], + "DescribeCustomerGateways": [ + { + "input": { + "CustomerGatewayIds": [ + "cgw-0e11f167" + ] + }, + "output": { + "CustomerGateways": [ + { + "BgpAsn": "65534", + "CustomerGatewayId": "cgw-0e11f167", + "IpAddress": "12.1.2.3", + "State": "available", + "Type": "ipsec.1" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified customer gateway.", + "id": "ec2-describe-customer-gateways-1", + "title": "To describe a customer gateway" + } + ], + "DescribeDhcpOptions": [ + { + "input": { + "DhcpOptionsIds": [ + "dopt-d9070ebb" + ] + }, + "output": { + "DhcpOptions": [ + { + "DhcpConfigurations": [ + { + "Key": "domain-name-servers", + "Values": [ + { + "Value": "10.2.5.2" + }, + { + "Value": "10.2.5.1" + } + ] + } + ], + "DhcpOptionsId": "dopt-d9070ebb" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified DHCP options set.", + "id": "ec2-describe-dhcp-options-1", + "title": "To describe a DHCP options set" + } + ], + "DescribeInstanceAttribute": [ + { + "input": { + "Attribute": "instanceType", + "InstanceId": "i-1234567890abcdef0" + }, + "output": { + "InstanceId": "i-1234567890abcdef0", + "InstanceType": { + "Value": "t1.micro" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the instance type of the specified instance.\n", + "id": "to-describe-the-instance-type-1472712432132", + "title": "To describe the instance type" + }, + { + "input": { + "Attribute": "disableApiTermination", + "InstanceId": "i-1234567890abcdef0" + }, + "output": { + "DisableApiTermination": { + "Value": "false" + }, + "InstanceId": "i-1234567890abcdef0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the ``disableApiTermination`` attribute of the specified instance.\n", + "id": "to-describe-the-disableapitermination-attribute-1472712533466", + "title": "To describe the disableApiTermination attribute" + }, + { + "input": { + "Attribute": "blockDeviceMapping", + "InstanceId": "i-1234567890abcdef0" + }, + "output": { + "BlockDeviceMappings": [ + { + "DeviceName": "/dev/sda1", + "Ebs": { + "AttachTime": "2013-05-17T22:42:34.000Z", + "DeleteOnTermination": true, + "Status": "attached", + "VolumeId": "vol-049df61146c4d7901" + } + }, + { + "DeviceName": "/dev/sdf", + "Ebs": { + "AttachTime": "2013-09-10T23:07:00.000Z", + "DeleteOnTermination": false, + "Status": "attached", + "VolumeId": "vol-049df61146c4d7901" + } + } + ], + "InstanceId": "i-1234567890abcdef0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the ``blockDeviceMapping`` attribute of the specified instance.\n", + "id": "to-describe-the-block-device-mapping-for-an-instance-1472712645423", + "title": "To describe the block device mapping for an instance" + } + ], + "DescribeInternetGateways": [ + { + "input": { + "Filters": [ + { + "Name": "attachment.vpc-id", + "Values": [ + "vpc-a01106c2" + ] + } + ] + }, + "output": { + "InternetGateways": [ + { + "Attachments": [ + { + "State": "available", + "VpcId": "vpc-a01106c2" + } + ], + "InternetGatewayId": "igw-c0a643a9", + "Tags": [ + + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the Internet gateway for the specified VPC.", + "id": "ec2-describe-internet-gateways-1", + "title": "To describe the Internet gateway for a VPC" + } + ], + "DescribeKeyPairs": [ + { + "input": { + "KeyNames": [ + "my-key-pair" + ] + }, + "output": { + "KeyPairs": [ + { + "KeyFingerprint": "1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f", + "KeyName": "my-key-pair" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example displays the fingerprint for the specified key.", + "id": "ec2-describe-key-pairs-1", + "title": "To display a key pair" + } + ], + "DescribeMovingAddresses": [ + { + "output": { + "MovingAddressStatuses": [ + { + "MoveStatus": "MovingToVpc", + "PublicIp": "198.51.100.0" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all of your moving Elastic IP addresses.", + "id": "ec2-describe-moving-addresses-1", + "title": "To describe your moving addresses" + } + ], + "DescribeNatGateways": [ + { + "input": { + "Filter": [ + { + "Name": "vpc-id", + "Values": [ + "vpc-1a2b3c4d" + ] + } + ] + }, + "output": { + "NatGateways": [ + { + "CreateTime": "2015-12-01T12:26:55.983Z", + "NatGatewayAddresses": [ + { + "AllocationId": "eipalloc-89c620ec", + "NetworkInterfaceId": "eni-9dec76cd", + "PrivateIp": "10.0.0.149", + "PublicIp": "198.11.222.333" + } + ], + "NatGatewayId": "nat-05dba92075d71c408", + "State": "available", + "SubnetId": "subnet-847e4dc2", + "VpcId": "vpc-1a2b3c4d" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the NAT gateway for the specified VPC.", + "id": "ec2-describe-nat-gateways-1", + "title": "To describe a NAT gateway" + } + ], + "DescribeNetworkAcls": [ + { + "input": { + "NetworkAclIds": [ + "acl-5fb85d36" + ] + }, + "output": { + "NetworkAcls": [ + { + "Associations": [ + { + "NetworkAclAssociationId": "aclassoc-66ea5f0b", + "NetworkAclId": "acl-9aeb5ef7", + "SubnetId": "subnet-65ea5f08" + } + ], + "Entries": [ + { + "CidrBlock": "0.0.0.0/0", + "Egress": true, + "Protocol": "-1", + "RuleAction": "deny", + "RuleNumber": 32767 + }, + { + "CidrBlock": "0.0.0.0/0", + "Egress": false, + "Protocol": "-1", + "RuleAction": "deny", + "RuleNumber": 32767 + } + ], + "IsDefault": false, + "NetworkAclId": "acl-5fb85d36", + "Tags": [ + + ], + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified network ACL.", + "id": "ec2-", + "title": "To describe a network ACL" + } + ], + "DescribeNetworkInterfaceAttribute": [ + { + "input": { + "Attribute": "attachment", + "NetworkInterfaceId": "eni-686ea200" + }, + "output": { + "Attachment": { + "AttachTime": "2015-05-21T20:02:20.000Z", + "AttachmentId": "eni-attach-43348162", + "DeleteOnTermination": true, + "DeviceIndex": 0, + "InstanceId": "i-1234567890abcdef0", + "InstanceOwnerId": "123456789012", + "Status": "attached" + }, + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the attachment attribute of the specified network interface.", + "id": "ec2-describe-network-interface-attribute-1", + "title": "To describe the attachment attribute of a network interface" + }, + { + "input": { + "Attribute": "description", + "NetworkInterfaceId": "eni-686ea200" + }, + "output": { + "Description": { + "Value": "My description" + }, + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the description attribute of the specified network interface.", + "id": "ec2-describe-network-interface-attribute-2", + "title": "To describe the description attribute of a network interface" + }, + { + "input": { + "Attribute": "groupSet", + "NetworkInterfaceId": "eni-686ea200" + }, + "output": { + "Groups": [ + { + "GroupId": "sg-903004f8", + "GroupName": "my-security-group" + } + ], + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the groupSet attribute of the specified network interface.", + "id": "ec2-describe-network-interface-attribute-3", + "title": "To describe the groupSet attribute of a network interface" + }, + { + "input": { + "Attribute": "sourceDestCheck", + "NetworkInterfaceId": "eni-686ea200" + }, + "output": { + "NetworkInterfaceId": "eni-686ea200", + "SourceDestCheck": { + "Value": true + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the sourceDestCheck attribute of the specified network interface.", + "id": "ec2-describe-network-interface-attribute-4", + "title": "To describe the sourceDestCheck attribute of a network interface" + } + ], + "DescribeNetworkInterfaces": [ + { + "input": { + "NetworkInterfaceIds": [ + "eni-e5aa89a3" + ] + }, + "output": { + "NetworkInterfaces": [ + { + "Association": { + "AssociationId": "eipassoc-0fbb766a", + "IpOwnerId": "123456789012", + "PublicDnsName": "ec2-203-0-113-12.compute-1.amazonaws.com", + "PublicIp": "203.0.113.12" + }, + "Attachment": { + "AttachTime": "2013-11-30T23:36:42.000Z", + "AttachmentId": "eni-attach-66c4350a", + "DeleteOnTermination": false, + "DeviceIndex": 1, + "InstanceId": "i-1234567890abcdef0", + "InstanceOwnerId": "123456789012", + "Status": "attached" + }, + "AvailabilityZone": "us-east-1d", + "Description": "my network interface", + "Groups": [ + { + "GroupId": "sg-8637d3e3", + "GroupName": "default" + } + ], + "MacAddress": "02:2f:8f:b0:cf:75", + "NetworkInterfaceId": "eni-e5aa89a3", + "OwnerId": "123456789012", + "PrivateDnsName": "ip-10-0-1-17.ec2.internal", + "PrivateIpAddress": "10.0.1.17", + "PrivateIpAddresses": [ + { + "Association": { + "AssociationId": "eipassoc-0fbb766a", + "IpOwnerId": "123456789012", + "PublicDnsName": "ec2-203-0-113-12.compute-1.amazonaws.com", + "PublicIp": "203.0.113.12" + }, + "Primary": true, + "PrivateDnsName": "ip-10-0-1-17.ec2.internal", + "PrivateIpAddress": "10.0.1.17" + } + ], + "RequesterManaged": false, + "SourceDestCheck": true, + "Status": "in-use", + "SubnetId": "subnet-b61f49f0", + "TagSet": [ + + ], + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "ec2-describe-network-interfaces-1", + "title": "To describe a network interface" + } + ], + "DescribeRegions": [ + { + "output": { + "Regions": [ + { + "Endpoint": "ec2.ap-south-1.amazonaws.com", + "RegionName": "ap-south-1" + }, + { + "Endpoint": "ec2.eu-west-1.amazonaws.com", + "RegionName": "eu-west-1" + }, + { + "Endpoint": "ec2.ap-southeast-1.amazonaws.com", + "RegionName": "ap-southeast-1" + }, + { + "Endpoint": "ec2.ap-southeast-2.amazonaws.com", + "RegionName": "ap-southeast-2" + }, + { + "Endpoint": "ec2.eu-central-1.amazonaws.com", + "RegionName": "eu-central-1" + }, + { + "Endpoint": "ec2.ap-northeast-2.amazonaws.com", + "RegionName": "ap-northeast-2" + }, + { + "Endpoint": "ec2.ap-northeast-1.amazonaws.com", + "RegionName": "ap-northeast-1" + }, + { + "Endpoint": "ec2.us-east-1.amazonaws.com", + "RegionName": "us-east-1" + }, + { + "Endpoint": "ec2.sa-east-1.amazonaws.com", + "RegionName": "sa-east-1" + }, + { + "Endpoint": "ec2.us-west-1.amazonaws.com", + "RegionName": "us-west-1" + }, + { + "Endpoint": "ec2.us-west-2.amazonaws.com", + "RegionName": "us-west-2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all the regions that are available to you.", + "id": "ec2-describe-regions-1", + "title": "To describe your regions" + } + ], + "DescribeRouteTables": [ + { + "input": { + "RouteTableIds": [ + "rtb-1f382e7d" + ] + }, + "output": { + "RouteTables": [ + { + "Associations": [ + { + "Main": true, + "RouteTableAssociationId": "rtbassoc-d8ccddba", + "RouteTableId": "rtb-1f382e7d" + } + ], + "PropagatingVgws": [ + + ], + "RouteTableId": "rtb-1f382e7d", + "Routes": [ + { + "DestinationCidrBlock": "10.0.0.0/16", + "GatewayId": "local", + "State": "active" + } + ], + "Tags": [ + + ], + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified route table.", + "id": "ec2-describe-route-tables-1", + "title": "To describe a route table" + } + ], + "DescribeScheduledInstanceAvailability": [ + { + "input": { + "FirstSlotStartTimeRange": { + "EarliestTime": "2016-01-31T00:00:00Z", + "LatestTime": "2016-01-31T04:00:00Z" + }, + "Recurrence": { + "Frequency": "Weekly", + "Interval": 1, + "OccurrenceDays": [ + 1 + ] + } + }, + "output": { + "ScheduledInstanceAvailabilitySet": [ + { + "AvailabilityZone": "us-west-2b", + "AvailableInstanceCount": 20, + "FirstSlotStartTime": "2016-01-31T00:00:00Z", + "HourlyPrice": "0.095", + "InstanceType": "c4.large", + "MaxTermDurationInDays": 366, + "MinTermDurationInDays": 366, + "NetworkPlatform": "EC2-VPC", + "Platform": "Linux/UNIX", + "PurchaseToken": "eyJ2IjoiMSIsInMiOjEsImMiOi...", + "Recurrence": { + "Frequency": "Weekly", + "Interval": 1, + "OccurrenceDaySet": [ + 1 + ], + "OccurrenceRelativeToEnd": false + }, + "SlotDurationInHours": 23, + "TotalScheduledInstanceHours": 1219 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes a schedule that occurs every week on Sunday, starting on the specified date. Note that the output contains a single schedule as an example.", + "id": "ec2-describe-scheduled-instance-availability-1", + "title": "To describe an available schedule" + } + ], + "DescribeScheduledInstances": [ + { + "input": { + "ScheduledInstanceIds": [ + "sci-1234-1234-1234-1234-123456789012" + ] + }, + "output": { + "ScheduledInstanceSet": [ + { + "AvailabilityZone": "us-west-2b", + "CreateDate": "2016-01-25T21:43:38.612Z", + "HourlyPrice": "0.095", + "InstanceCount": 1, + "InstanceType": "c4.large", + "NetworkPlatform": "EC2-VPC", + "NextSlotStartTime": "2016-01-31T09:00:00Z", + "Platform": "Linux/UNIX", + "Recurrence": { + "Frequency": "Weekly", + "Interval": 1, + "OccurrenceDaySet": [ + 1 + ], + "OccurrenceRelativeToEnd": false, + "OccurrenceUnit": "" + }, + "ScheduledInstanceId": "sci-1234-1234-1234-1234-123456789012", + "SlotDurationInHours": 32, + "TermEndDate": "2017-01-31T09:00:00Z", + "TermStartDate": "2016-01-31T09:00:00Z", + "TotalScheduledInstanceHours": 1696 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified Scheduled Instance.", + "id": "ec2-describe-scheduled-instances-1", + "title": "To describe your Scheduled Instances" + } + ], + "DescribeSnapshotAttribute": [ + { + "input": { + "Attribute": "createVolumePermission", + "SnapshotId": "snap-066877671789bd71b" + }, + "output": { + "CreateVolumePermissions": [ + + ], + "SnapshotId": "snap-066877671789bd71b" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the ``createVolumePermission`` attribute on a snapshot with the snapshot ID of ``snap-066877671789bd71b``.", + "id": "to-describe-snapshot-attributes-1472503199736", + "title": "To describe snapshot attributes" + } + ], + "DescribeSnapshots": [ + { + "input": { + "SnapshotIds": [ + "snap-1234567890abcdef0" + ] + }, + "output": { + "NextToken": "", + "Snapshots": [ + { + "Description": "This is my snapshot.", + "OwnerId": "012345678910", + "Progress": "100%", + "SnapshotId": "snap-1234567890abcdef0", + "StartTime": "2014-02-28T21:28:32.000Z", + "State": "completed", + "VolumeId": "vol-049df61146c4d7901", + "VolumeSize": 8 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes a snapshot with the snapshot ID of ``snap-1234567890abcdef0``.", + "id": "to-describe-a-snapshot-1472503807850", + "title": "To describe a snapshot" + }, + { + "input": { + "Filters": [ + { + "Name": "status", + "Values": [ + "pending" + ] + } + ], + "OwnerIds": [ + "012345678910" + ] + }, + "output": { + "NextToken": "", + "Snapshots": [ + { + "Description": "This is my copied snapshot.", + "OwnerId": "012345678910", + "Progress": "87%", + "SnapshotId": "snap-066877671789bd71b", + "StartTime": "2014-02-28T21:37:27.000Z", + "State": "pending", + "VolumeId": "vol-1234567890abcdef0", + "VolumeSize": 8 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all snapshots owned by the ID 012345678910 that are in the ``pending`` status.", + "id": "to-describe-snapshots-using-filters-1472503929793", + "title": "To describe snapshots using filters" + } + ], + "DescribeSpotDatafeedSubscription": [ + { + "output": { + "SpotDatafeedSubscription": { + "Bucket": "my-s3-bucket", + "OwnerId": "123456789012", + "Prefix": "spotdata", + "State": "Active" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the Spot Instance datafeed subscription for your AWS account.", + "id": "ec2-describe-spot-datafeed-subscription-1", + "title": "To describe the datafeed for your AWS account" + } + ], + "DescribeSpotFleetInstances": [ + { + "input": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "output": { + "ActiveInstances": [ + { + "InstanceId": "i-1234567890abcdef0", + "InstanceType": "m3.medium", + "SpotInstanceRequestId": "sir-08b93456" + } + ], + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists the Spot Instances associated with the specified Spot fleet.", + "id": "ec2-describe-spot-fleet-instances-1", + "title": "To describe the Spot Instances associated with a Spot fleet" + } + ], + "DescribeSpotFleetRequestHistory": [ + { + "input": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "StartTime": "2015-05-26T00:00:00Z" + }, + "output": { + "HistoryRecords": [ + { + "EventInformation": { + "EventSubType": "submitted" + }, + "EventType": "fleetRequestChange", + "Timestamp": "2015-05-26T23:17:20.697Z" + }, + { + "EventInformation": { + "EventSubType": "active" + }, + "EventType": "fleetRequestChange", + "Timestamp": "2015-05-26T23:17:20.873Z" + }, + { + "EventInformation": { + "EventSubType": "launched", + "InstanceId": "i-1234567890abcdef0" + }, + "EventType": "instanceChange", + "Timestamp": "2015-05-26T23:21:21.712Z" + }, + { + "EventInformation": { + "EventSubType": "launched", + "InstanceId": "i-1234567890abcdef1" + }, + "EventType": "instanceChange", + "Timestamp": "2015-05-26T23:21:21.816Z" + } + ], + "NextToken": "CpHNsscimcV5oH7bSbub03CI2Qms5+ypNpNm+53MNlR0YcXAkp0xFlfKf91yVxSExmbtma3awYxMFzNA663ZskT0AHtJ6TCb2Z8bQC2EnZgyELbymtWPfpZ1ZbauVg+P+TfGlWxWWB/Vr5dk5d4LfdgA/DRAHUrYgxzrEXAMPLE=", + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "StartTime": "2015-05-26T00:00:00Z" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example returns the history for the specified Spot fleet starting at the specified time.", + "id": "ec2-describe-spot-fleet-request-history-1", + "title": "To describe Spot fleet history" + } + ], + "DescribeSpotFleetRequests": [ + { + "input": { + "SpotFleetRequestIds": [ + "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + ] + }, + "output": { + "SpotFleetRequestConfigs": [ + { + "SpotFleetRequestConfig": { + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "EbsOptimized": false, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "cc2.8xlarge", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeleteOnTermination": false, + "DeviceIndex": 0, + "SecondaryPrivateIpAddressCount": 0, + "SubnetId": "subnet-a61dafcf" + } + ] + }, + { + "EbsOptimized": false, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "r3.8xlarge", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeleteOnTermination": false, + "DeviceIndex": 0, + "SecondaryPrivateIpAddressCount": 0, + "SubnetId": "subnet-a61dafcf" + } + ] + } + ], + "SpotPrice": "0.05", + "TargetCapacity": 20 + }, + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "SpotFleetRequestState": "active" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified Spot fleet request.", + "id": "ec2-describe-spot-fleet-requests-1", + "title": "To describe a Spot fleet request" + } + ], + "DescribeSpotInstanceRequests": [ + { + "input": { + "SpotInstanceRequestIds": [ + "sir-08b93456" + ] + }, + "output": { + "SpotInstanceRequests": [ + { + "CreateTime": "2014-04-30T18:14:55.000Z", + "InstanceId": "i-1234567890abcdef0", + "LaunchSpecification": { + "BlockDeviceMappings": [ + { + "DeviceName": "/dev/sda1", + "Ebs": { + "DeleteOnTermination": true, + "VolumeSize": 8, + "VolumeType": "standard" + } + } + ], + "EbsOptimized": false, + "ImageId": "ami-7aba833f", + "InstanceType": "m1.small", + "KeyName": "my-key-pair", + "SecurityGroups": [ + { + "GroupId": "sg-e38f24a7", + "GroupName": "my-security-group" + } + ] + }, + "LaunchedAvailabilityZone": "us-west-1b", + "ProductDescription": "Linux/UNIX", + "SpotInstanceRequestId": "sir-08b93456", + "SpotPrice": "0.010000", + "State": "active", + "Status": { + "Code": "fulfilled", + "Message": "Your Spot request is fulfilled.", + "UpdateTime": "2014-04-30T18:16:21.000Z" + }, + "Type": "one-time" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified Spot Instance request.", + "id": "ec2-describe-spot-instance-requests-1", + "title": "To describe a Spot Instance request" + } + ], + "DescribeSpotPriceHistory": [ + { + "input": { + "EndTime": "2014-01-06T08:09:10", + "InstanceTypes": [ + "m1.xlarge" + ], + "ProductDescriptions": [ + "Linux/UNIX (Amazon VPC)" + ], + "StartTime": "2014-01-06T07:08:09" + }, + "output": { + "SpotPriceHistory": [ + { + "AvailabilityZone": "us-west-1a", + "InstanceType": "m1.xlarge", + "ProductDescription": "Linux/UNIX (Amazon VPC)", + "SpotPrice": "0.080000", + "Timestamp": "2014-01-06T04:32:53.000Z" + }, + { + "AvailabilityZone": "us-west-1c", + "InstanceType": "m1.xlarge", + "ProductDescription": "Linux/UNIX (Amazon VPC)", + "SpotPrice": "0.080000", + "Timestamp": "2014-01-05T11:28:26.000Z" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example returns the Spot Price history for m1.xlarge, Linux/UNIX (Amazon VPC) instances for a particular day in January.", + "id": "ec2-describe-spot-price-history-1", + "title": "To describe Spot price history for Linux/UNIX (Amazon VPC)" + } + ], + "DescribeSubnets": [ + { + "input": { + "Filters": [ + { + "Name": "vpc-id", + "Values": [ + "vpc-a01106c2" + ] + } + ] + }, + "output": { + "Subnets": [ + { + "AvailabilityZone": "us-east-1c", + "AvailableIpAddressCount": 251, + "CidrBlock": "10.0.1.0/24", + "DefaultForAz": false, + "MapPublicIpOnLaunch": false, + "State": "available", + "SubnetId": "subnet-9d4a7b6c", + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the subnets for the specified VPC.", + "id": "ec2-describe-subnets-1", + "title": "To describe the subnets for a VPC" + } + ], + "DescribeTags": [ + { + "input": { + "Filters": [ + { + "Name": "resource-id", + "Values": [ + "i-1234567890abcdef8" + ] + } + ] + }, + "output": { + "Tags": [ + { + "Key": "Stack", + "ResourceId": "i-1234567890abcdef8", + "ResourceType": "instance", + "Value": "test" + }, + { + "Key": "Name", + "ResourceId": "i-1234567890abcdef8", + "ResourceType": "instance", + "Value": "Beta Server" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the tags for the specified instance.", + "id": "ec2-describe-tags-1", + "title": "To describe the tags for a single resource" + } + ], + "DescribeVolumeAttribute": [ + { + "input": { + "Attribute": "autoEnableIO", + "VolumeId": "vol-049df61146c4d7901" + }, + "output": { + "AutoEnableIO": { + "Value": false + }, + "VolumeId": "vol-049df61146c4d7901" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the ``autoEnableIo`` attribute of the volume with the ID ``vol-049df61146c4d7901``.", + "id": "to-describe-a-volume-attribute-1472505773492", + "title": "To describe a volume attribute" + } + ], + "DescribeVolumeStatus": [ + { + "input": { + "VolumeIds": [ + "vol-1234567890abcdef0" + ] + }, + "output": { + "VolumeStatuses": [ + { + "Actions": [ + + ], + "AvailabilityZone": "us-east-1a", + "Events": [ + + ], + "VolumeId": "vol-1234567890abcdef0", + "VolumeStatus": { + "Details": [ + { + "Name": "io-enabled", + "Status": "passed" + }, + { + "Name": "io-performance", + "Status": "not-applicable" + } + ], + "Status": "ok" + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the status for the volume ``vol-1234567890abcdef0``.", + "id": "to-describe-the-status-of-a-single-volume-1472507016193", + "title": "To describe the status of a single volume" + }, + { + "input": { + "Filters": [ + { + "Name": "volume-status.status", + "Values": [ + "impaired" + ] + } + ] + }, + "output": { + "VolumeStatuses": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the status for all volumes that are impaired. In this example output, there are no impaired volumes.", + "id": "to-describe-the-status-of-impaired-volumes-1472507239821", + "title": "To describe the status of impaired volumes" + } + ], + "DescribeVolumes": [ + { + "input": { + }, + "output": { + "NextToken": "", + "Volumes": [ + { + "Attachments": [ + { + "AttachTime": "2013-12-18T22:35:00.000Z", + "DeleteOnTermination": true, + "Device": "/dev/sda1", + "InstanceId": "i-1234567890abcdef0", + "State": "attached", + "VolumeId": "vol-049df61146c4d7901" + } + ], + "AvailabilityZone": "us-east-1a", + "CreateTime": "2013-12-18T22:35:00.084Z", + "Size": 8, + "SnapshotId": "snap-1234567890abcdef0", + "State": "in-use", + "VolumeId": "vol-049df61146c4d7901", + "VolumeType": "standard" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all of your volumes in the default region.", + "id": "to-describe-all-volumes-1472506358883", + "title": "To describe all volumes" + }, + { + "input": { + "Filters": [ + { + "Name": "attachment.instance-id", + "Values": [ + "i-1234567890abcdef0" + ] + }, + { + "Name": "attachment.delete-on-termination", + "Values": [ + "true" + ] + } + ] + }, + "output": { + "Volumes": [ + { + "Attachments": [ + { + "AttachTime": "2013-12-18T22:35:00.000Z", + "DeleteOnTermination": true, + "Device": "/dev/sda1", + "InstanceId": "i-1234567890abcdef0", + "State": "attached", + "VolumeId": "vol-049df61146c4d7901" + } + ], + "AvailabilityZone": "us-east-1a", + "CreateTime": "2013-12-18T22:35:00.084Z", + "Size": 8, + "SnapshotId": "snap-1234567890abcdef0", + "State": "in-use", + "VolumeId": "vol-049df61146c4d7901", + "VolumeType": "standard" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all volumes that are both attached to the instance with the ID i-1234567890abcdef0 and set to delete when the instance terminates.", + "id": "to-describe-volumes-that-are-attached-to-a-specific-instance-1472506613578", + "title": "To describe volumes that are attached to a specific instance" + } + ], + "DescribeVpcAttribute": [ + { + "input": { + "Attribute": "enableDnsSupport", + "VpcId": "vpc-a01106c2" + }, + "output": { + "EnableDnsSupport": { + "Value": true + }, + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the enableDnsSupport attribute. This attribute indicates whether DNS resolution is enabled for the VPC. If this attribute is true, the Amazon DNS server resolves DNS hostnames for your instances to their corresponding IP addresses; otherwise, it does not.", + "id": "ec2-describe-vpc-attribute-1", + "title": "To describe the enableDnsSupport attribute" + }, + { + "input": { + "Attribute": "enableDnsHostnames", + "VpcId": "vpc-a01106c2" + }, + "output": { + "EnableDnsHostnames": { + "Value": true + }, + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the enableDnsHostnames attribute. This attribute indicates whether the instances launched in the VPC get DNS hostnames. If this attribute is true, instances in the VPC get DNS hostnames; otherwise, they do not.", + "id": "ec2-describe-vpc-attribute-2", + "title": "To describe the enableDnsHostnames attribute" + } + ], + "DescribeVpcs": [ + { + "input": { + "VpcIds": [ + "vpc-a01106c2" + ] + }, + "output": { + "Vpcs": [ + { + "CidrBlock": "10.0.0.0/16", + "DhcpOptionsId": "dopt-7a8b9c2d", + "InstanceTenancy": "default", + "IsDefault": false, + "State": "available", + "Tags": [ + { + "Key": "Name", + "Value": "MyVPC" + } + ], + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified VPC.", + "id": "ec2-describe-vpcs-1", + "title": "To describe a VPC" + } + ], + "DetachInternetGateway": [ + { + "input": { + "InternetGatewayId": "igw-c0a643a9", + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the specified Internet gateway from the specified VPC.", + "id": "ec2-detach-internet-gateway-1", + "title": "To detach an Internet gateway from a VPC" + } + ], + "DetachNetworkInterface": [ + { + "input": { + "AttachmentId": "eni-attach-66c4350a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the specified network interface from its attached instance.", + "id": "ec2-detach-network-interface-1", + "title": "To detach a network interface from an instance" + } + ], + "DetachVolume": [ + { + "input": { + "VolumeId": "vol-1234567890abcdef0" + }, + "output": { + "AttachTime": "2014-02-27T19:23:06.000Z", + "Device": "/dev/sdb", + "InstanceId": "i-1234567890abcdef0", + "State": "detaching", + "VolumeId": "vol-049df61146c4d7901" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the volume (``vol-049df61146c4d7901``) from the instance it is attached to.", + "id": "to-detach-a-volume-from-an-instance-1472507977694", + "title": "To detach a volume from an instance" + } + ], + "DisableVgwRoutePropagation": [ + { + "input": { + "GatewayId": "vgw-9a4cacf3", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disables the specified virtual private gateway from propagating static routes to the specified route table.", + "id": "ec2-disable-vgw-route-propagation-1", + "title": "To disable route propagation" + } + ], + "DisassociateAddress": [ + { + "input": { + "AssociationId": "eipassoc-2bebb745" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disassociates an Elastic IP address from an instance in a VPC.", + "id": "ec2-disassociate-address-1", + "title": "To disassociate an Elastic IP address in EC2-VPC" + }, + { + "input": { + "PublicIp": "198.51.100.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disassociates an Elastic IP address from an instance in EC2-Classic.", + "id": "ec2-disassociate-address-2", + "title": "To disassociate an Elastic IP addresses in EC2-Classic" + } + ], + "DisassociateRouteTable": [ + { + "input": { + "AssociationId": "rtbassoc-781d0d1a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disassociates the specified route table from its associated subnet.", + "id": "ec2-disassociate-route-table-1", + "title": "To disassociate a route table" + } + ], + "EnableVgwRoutePropagation": [ + { + "input": { + "GatewayId": "vgw-9a4cacf3", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables the specified virtual private gateway to propagate static routes to the specified route table.", + "id": "ec2-enable-vgw-route-propagation-1", + "title": "To enable route propagation" + } + ], + "EnableVolumeIO": [ + { + "input": { + "VolumeId": "vol-1234567890abcdef0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables I/O on volume ``vol-1234567890abcdef0``.", + "id": "to-enable-io-for-a-volume-1472508114867", + "title": "To enable I/O for a volume" + } + ], + "ModifyNetworkInterfaceAttribute": [ + { + "input": { + "Attachment": { + "AttachmentId": "eni-attach-43348162", + "DeleteOnTermination": false + }, + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the attachment attribute of the specified network interface.", + "id": "ec2-modify-network-interface-attribute-1", + "title": "To modify the attachment attribute of a network interface" + }, + { + "input": { + "Description": { + "Value": "My description" + }, + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the description attribute of the specified network interface.", + "id": "ec2-modify-network-interface-attribute-2", + "title": "To modify the description attribute of a network interface" + }, + { + "input": { + "Groups": [ + "sg-903004f8", + "sg-1a2b3c4d" + ], + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example command modifies the groupSet attribute of the specified network interface.", + "id": "ec2-modify-network-interface-attribute-3", + "title": "To modify the groupSet attribute of a network interface" + }, + { + "input": { + "NetworkInterfaceId": "eni-686ea200", + "SourceDestCheck": { + "Value": false + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example command modifies the sourceDestCheck attribute of the specified network interface.", + "id": "ec2-modify-network-interface-attribute-4", + "title": "To modify the sourceDestCheck attribute of a network interface" + } + ], + "ModifySnapshotAttribute": [ + { + "input": { + "Attribute": "createVolumePermission", + "OperationType": "remove", + "SnapshotId": "snap-1234567890abcdef0", + "UserIds": [ + "123456789012" + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies snapshot ``snap-1234567890abcdef0`` to remove the create volume permission for a user with the account ID ``123456789012``. If the command succeeds, no output is returned.", + "id": "to-modify-a-snapshot-attribute-1472508385907", + "title": "To modify a snapshot attribute" + }, + { + "input": { + "Attribute": "createVolumePermission", + "GroupNames": [ + "all" + ], + "OperationType": "add", + "SnapshotId": "snap-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example makes the snapshot ``snap-1234567890abcdef0`` public.", + "id": "to-make-a-snapshot-public-1472508470529", + "title": "To make a snapshot public" + } + ], + "ModifySpotFleetRequest": [ + { + "input": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "TargetCapacity": 20 + }, + "output": { + "Return": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example increases the target capacity of the specified Spot fleet request.", + "id": "ec2-modify-spot-fleet-request-1", + "title": "To increase the target capacity of a Spot fleet request" + }, + { + "input": { + "ExcessCapacityTerminationPolicy": "NoTermination ", + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "TargetCapacity": 10 + }, + "output": { + "Return": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example decreases the target capacity of the specified Spot fleet request without terminating any Spot Instances as a result.", + "id": "ec2-modify-spot-fleet-request-2", + "title": "To decrease the target capacity of a Spot fleet request" + } + ], + "ModifySubnetAttribute": [ + { + "input": { + "MapPublicIpOnLaunch": { + "Value": true + }, + "SubnetId": "subnet-1a2b3c4d" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the specified subnet so that all instances launched into this subnet are assigned a public IP address.", + "id": "ec2-modify-subnet-attribute-1", + "title": "To change a subnet's public IP addressing behavior" + } + ], + "ModifyVolumeAttribute": [ + { + "input": { + "AutoEnableIO": { + "Value": true + }, + "DryRun": true, + "VolumeId": "vol-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example sets the ``autoEnableIo`` attribute of the volume with the ID ``vol-1234567890abcdef0`` to ``true``. If the command succeeds, no output is returned.", + "id": "to-modify-a-volume-attribute-1472508596749", + "title": "To modify a volume attribute" + } + ], + "ModifyVpcAttribute": [ + { + "input": { + "EnableDnsSupport": { + "Value": false + }, + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the enableDnsSupport attribute. This attribute indicates whether DNS resolution is enabled for the VPC. If this attribute is true, the Amazon DNS server resolves DNS hostnames for instances in the VPC to their corresponding IP addresses; otherwise, it does not.", + "id": "ec2-modify-vpc-attribute-1", + "title": "To modify the enableDnsSupport attribute" + }, + { + "input": { + "EnableDnsHostnames": { + "Value": false + }, + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the enableDnsHostnames attribute. This attribute indicates whether instances launched in the VPC get DNS hostnames. If this attribute is true, instances in the VPC get DNS hostnames; otherwise, they do not.", + "id": "ec2-modify-vpc-attribute-2", + "title": "To modify the enableDnsHostnames attribute" + } + ], + "MoveAddressToVpc": [ + { + "input": { + "PublicIp": "54.123.4.56" + }, + "output": { + "Status": "MoveInProgress" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example moves the specified Elastic IP address to the EC2-VPC platform.", + "id": "ec2-move-address-to-vpc-1", + "title": "To move an address to EC2-VPC" + } + ], + "PurchaseScheduledInstances": [ + { + "input": { + "PurchaseRequests": [ + { + "InstanceCount": 1, + "PurchaseToken": "eyJ2IjoiMSIsInMiOjEsImMiOi..." + } + ] + }, + "output": { + "ScheduledInstanceSet": [ + { + "AvailabilityZone": "us-west-2b", + "CreateDate": "2016-01-25T21:43:38.612Z", + "HourlyPrice": "0.095", + "InstanceCount": 1, + "InstanceType": "c4.large", + "NetworkPlatform": "EC2-VPC", + "NextSlotStartTime": "2016-01-31T09:00:00Z", + "Platform": "Linux/UNIX", + "Recurrence": { + "Frequency": "Weekly", + "Interval": 1, + "OccurrenceDaySet": [ + 1 + ], + "OccurrenceRelativeToEnd": false, + "OccurrenceUnit": "" + }, + "ScheduledInstanceId": "sci-1234-1234-1234-1234-123456789012", + "SlotDurationInHours": 32, + "TermEndDate": "2017-01-31T09:00:00Z", + "TermStartDate": "2016-01-31T09:00:00Z", + "TotalScheduledInstanceHours": 1696 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example purchases a Scheduled Instance.", + "id": "ec2-purchase-scheduled-instances-1", + "title": "To purchase a Scheduled Instance" + } + ], + "ReleaseAddress": [ + { + "input": { + "AllocationId": "eipalloc-64d5890a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example releases an Elastic IP address for use with instances in a VPC.", + "id": "ec2-release-address-1", + "title": "To release an Elastic IP address for EC2-VPC" + }, + { + "input": { + "PublicIp": "198.51.100.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example releases an Elastic IP address for use with instances in EC2-Classic.", + "id": "ec2-release-address-2", + "title": "To release an Elastic IP addresses for EC2-Classic" + } + ], + "ReplaceNetworkAclAssociation": [ + { + "input": { + "AssociationId": "aclassoc-e5b95c8c", + "NetworkAclId": "acl-5fb85d36" + }, + "output": { + "NewAssociationId": "aclassoc-3999875b" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified network ACL with the subnet for the specified network ACL association.", + "id": "ec2-replace-network-acl-association-1", + "title": "To replace the network ACL associated with a subnet" + } + ], + "ReplaceNetworkAclEntry": [ + { + "input": { + "CidrBlock": "203.0.113.12/24", + "Egress": false, + "NetworkAclId": "acl-5fb85d36", + "PortRange": { + "From": 53, + "To": 53 + }, + "Protocol": "udp", + "RuleAction": "allow", + "RuleNumber": 100 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example replaces an entry for the specified network ACL. The new rule 100 allows ingress traffic from 203.0.113.12/24 on UDP port 53 (DNS) into any associated subnet.", + "id": "ec2-replace-network-acl-entry-1", + "title": "To replace a network ACL entry" + } + ], + "ReplaceRoute": [ + { + "input": { + "DestinationCidrBlock": "10.0.0.0/16", + "GatewayId": "vgw-9a4cacf3", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example replaces the specified route in the specified table table. The new route matches the specified CIDR and sends the traffic to the specified virtual private gateway.", + "id": "ec2-replace-route-1", + "title": "To replace a route" + } + ], + "ReplaceRouteTableAssociation": [ + { + "input": { + "AssociationId": "rtbassoc-781d0d1a", + "RouteTableId": "rtb-22574640" + }, + "output": { + "NewAssociationId": "rtbassoc-3a1f0f58" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified route table with the subnet for the specified route table association.", + "id": "ec2-replace-route-table-association-1", + "title": "To replace the route table associated with a subnet" + } + ], + "RequestSpotFleet": [ + { + "input": { + "SpotFleetRequestConfig": { + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "KeyName": "my-key-pair", + "SecurityGroups": [ + { + "GroupId": "sg-1a2b3c4d" + } + ], + "SubnetId": "subnet-1a2b3c4d, subnet-3c4d5e6f" + } + ], + "SpotPrice": "0.04", + "TargetCapacity": 2 + } + }, + "output": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a Spot fleet request with two launch specifications that differ only by subnet. The Spot fleet launches the instances in the specified subnet with the lowest price. If the instances are launched in a default VPC, they receive a public IP address by default. If the instances are launched in a nondefault VPC, they do not receive a public IP address by default. Note that you can't specify different subnets from the same Availability Zone in a Spot fleet request.", + "id": "ec2-request-spot-fleet-1", + "title": "To request a Spot fleet in the subnet with the lowest price" + }, + { + "input": { + "SpotFleetRequestConfig": { + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "KeyName": "my-key-pair", + "Placement": { + "AvailabilityZone": "us-west-2a, us-west-2b" + }, + "SecurityGroups": [ + { + "GroupId": "sg-1a2b3c4d" + } + ] + } + ], + "SpotPrice": "0.04", + "TargetCapacity": 2 + } + }, + "output": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a Spot fleet request with two launch specifications that differ only by Availability Zone. The Spot fleet launches the instances in the specified Availability Zone with the lowest price. If your account supports EC2-VPC only, Amazon EC2 launches the Spot instances in the default subnet of the Availability Zone. If your account supports EC2-Classic, Amazon EC2 launches the instances in EC2-Classic in the Availability Zone.", + "id": "ec2-request-spot-fleet-2", + "title": "To request a Spot fleet in the Availability Zone with the lowest price" + }, + { + "input": { + "SpotFleetRequestConfig": { + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::880185128111:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "KeyName": "my-key-pair", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeviceIndex": 0, + "Groups": [ + "sg-1a2b3c4d" + ], + "SubnetId": "subnet-1a2b3c4d" + } + ] + } + ], + "SpotPrice": "0.04", + "TargetCapacity": 2 + } + }, + "output": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example assigns public addresses to instances launched in a nondefault VPC. Note that when you specify a network interface, you must include the subnet ID and security group ID using the network interface.", + "id": "ec2-request-spot-fleet-3", + "title": "To launch Spot instances in a subnet and assign them public IP addresses" + }, + { + "input": { + "SpotFleetRequestConfig": { + "AllocationStrategy": "diversified", + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "ImageId": "ami-1a2b3c4d", + "InstanceType": "c4.2xlarge", + "SubnetId": "subnet-1a2b3c4d" + }, + { + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.2xlarge", + "SubnetId": "subnet-1a2b3c4d" + }, + { + "ImageId": "ami-1a2b3c4d", + "InstanceType": "r3.2xlarge", + "SubnetId": "subnet-1a2b3c4d" + } + ], + "SpotPrice": "0.70", + "TargetCapacity": 30 + } + }, + "output": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a Spot fleet request that launches 30 instances using the diversified allocation strategy. The launch specifications differ by instance type. The Spot fleet distributes the instances across the launch specifications such that there are 10 instances of each type.", + "id": "ec2-request-spot-fleet-4", + "title": "To request a Spot fleet using the diversified allocation strategy" + } + ], + "RequestSpotInstances": [ + { + "input": { + "InstanceCount": 5, + "LaunchSpecification": { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "KeyName": "my-key-pair", + "Placement": { + "AvailabilityZone": "us-west-2a" + }, + "SecurityGroupIds": [ + "sg-1a2b3c4d" + ] + }, + "SpotPrice": "0.03", + "Type": "one-time" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a one-time Spot Instance request for five instances in the specified Availability Zone. If your account supports EC2-VPC only, Amazon EC2 launches the instances in the default subnet of the specified Availability Zone. If your account supports EC2-Classic, Amazon EC2 launches the instances in EC2-Classic in the specified Availability Zone.", + "id": "ec2-request-spot-instances-1", + "title": "To create a one-time Spot Instance request" + }, + { + "input": { + "InstanceCount": 5, + "LaunchSpecification": { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "SecurityGroupIds": [ + "sg-1a2b3c4d" + ], + "SubnetId": "subnet-1a2b3c4d" + }, + "SpotPrice": "0.050", + "Type": "one-time" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example command creates a one-time Spot Instance request for five instances in the specified subnet. Amazon EC2 launches the instances in the specified subnet. If the VPC is a nondefault VPC, the instances do not receive a public IP address by default.", + "id": "ec2-request-spot-instances-2", + "title": "To create a one-time Spot Instance request" + } + ], + "ResetSnapshotAttribute": [ + { + "input": { + "Attribute": "createVolumePermission", + "SnapshotId": "snap-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example resets the create volume permissions for snapshot ``snap-1234567890abcdef0``. If the command succeeds, no output is returned.", + "id": "to-reset-a-snapshot-attribute-1472508825735", + "title": "To reset a snapshot attribute" + } + ], + "RestoreAddressToClassic": [ + { + "input": { + "PublicIp": "198.51.100.0" + }, + "output": { + "PublicIp": "198.51.100.0", + "Status": "MoveInProgress" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example restores the specified Elastic IP address to the EC2-Classic platform.", + "id": "ec2-restore-address-to-classic-1", + "title": "To restore an address to EC2-Classic" + } + ], + "RunScheduledInstances": [ + { + "input": { + "InstanceCount": 1, + "LaunchSpecification": { + "IamInstanceProfile": { + "Name": "my-iam-role" + }, + "ImageId": "ami-12345678", + "InstanceType": "c4.large", + "KeyName": "my-key-pair", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeviceIndex": 0, + "Groups": [ + "sg-12345678" + ], + "SubnetId": "subnet-12345678" + } + ] + }, + "ScheduledInstanceId": "sci-1234-1234-1234-1234-123456789012" + }, + "output": { + "InstanceIdSet": [ + "i-1234567890abcdef0" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example launches the specified Scheduled Instance in a VPC.", + "id": "ec2-run-scheduled-instances-1", + "title": "To launch a Scheduled Instance in a VPC" + }, + { + "input": { + "InstanceCount": 1, + "LaunchSpecification": { + "IamInstanceProfile": { + "Name": "my-iam-role" + }, + "ImageId": "ami-12345678", + "InstanceType": "c4.large", + "KeyName": "my-key-pair", + "Placement": { + "AvailabilityZone": "us-west-2b" + }, + "SecurityGroupIds": [ + "sg-12345678" + ] + }, + "ScheduledInstanceId": "sci-1234-1234-1234-1234-123456789012" + }, + "output": { + "InstanceIdSet": [ + "i-1234567890abcdef0" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example launches the specified Scheduled Instance in EC2-Classic.", + "id": "ec2-run-scheduled-instances-2", + "title": "To launch a Scheduled Instance in EC2-Classic" + } + ], + "UnassignPrivateIpAddresses": [ + { + "input": { + "NetworkInterfaceId": "eni-e5aa89a3", + "PrivateIpAddresses": [ + "10.0.0.82" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example unassigns the specified private IP address from the specified network interface.", + "id": "ec2-unassign-private-ip-addresses-1", + "title": "To unassign a secondary private IP address from a network interface" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/paginators-1.json new file mode 100644 index 0000000..2bd01ad --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/paginators-1.json @@ -0,0 +1,63 @@ +{ + "pagination": { + "DescribeInstanceStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceStatuses" + }, + "DescribeInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Reservations" + }, + "DescribeReservedInstancesOfferings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ReservedInstancesOfferings" + }, + "DescribeReservedInstancesModifications": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ReservedInstancesModifications" + }, + "DescribeSnapshots": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Snapshots" + }, + "DescribeSpotFleetRequests": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpotFleetRequestConfigs" + }, + "DescribeSpotPriceHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpotPriceHistory" + }, + "DescribeTags": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Tags" + }, + "DescribeVolumeStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VolumeStatuses" + }, + "DescribeVolumes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Volumes" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/service-2.json.gz new file mode 100644 index 0000000..55fa359 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/waiters-2.json new file mode 100644 index 0000000..7105194 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-09-15/waiters-2.json @@ -0,0 +1,593 @@ +{ + "version": 2, + "waiters": { + "InstanceExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeInstances", + "acceptors": [ + { + "matcher": "path", + "expected": true, + "argument": "length(Reservations[]) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidInstanceID.NotFound", + "state": "retry" + } + ] + }, + "BundleTaskComplete": { + "delay": 15, + "operation": "DescribeBundleTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "complete", + "matcher": "pathAll", + "state": "success", + "argument": "BundleTasks[].State" + }, + { + "expected": "failed", + "matcher": "pathAny", + "state": "failure", + "argument": "BundleTasks[].State" + } + ] + }, + "ConversionTaskCancelled": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskCompleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelled", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelling", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskDeleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "CustomerGatewayAvailable": { + "delay": 15, + "operation": "DescribeCustomerGateways", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + } + ] + }, + "ExportTaskCancelled": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ExportTaskCompleted": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ImageExists": { + "operation": "DescribeImages", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "matcher": "path", + "expected": true, + "argument": "length(Images[]) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidAMIID.NotFound", + "state": "retry" + } + ] + }, + "ImageAvailable": { + "operation": "DescribeImages", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Images[].State", + "expected": "available" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "Images[].State", + "expected": "failed" + } + ] + }, + "InstanceRunning": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "running", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "shutting-down", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "matcher": "error", + "expected": "InvalidInstanceID.NotFound", + "state": "retry" + } + ] + }, + "InstanceStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].InstanceStatus.Status", + "expected": "ok" + }, + { + "matcher": "error", + "expected": "InvalidInstanceID.NotFound", + "state": "retry" + } + ] + }, + "InstanceStopped": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "stopped", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "InstanceTerminated": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "terminated", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "KeyPairExists": { + "operation": "DescribeKeyPairs", + "delay": 5, + "maxAttempts": 6, + "acceptors": [ + { + "expected": true, + "matcher": "path", + "state": "success", + "argument": "length(KeyPairs[].KeyName) > `0`" + }, + { + "expected": "InvalidKeyPair.NotFound", + "matcher": "error", + "state": "retry" + } + ] + }, + "NatGatewayAvailable": { + "operation": "DescribeNatGateways", + "delay": 15, + "maxAttempts": 40, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "NatGateways[].State", + "expected": "available" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "NatGateways[].State", + "expected": "failed" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "NatGateways[].State", + "expected": "deleting" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "NatGateways[].State", + "expected": "deleted" + }, + { + "state": "retry", + "matcher": "error", + "expected": "NatGatewayNotFound" + } + ] + }, + "NetworkInterfaceAvailable": { + "operation": "DescribeNetworkInterfaces", + "delay": 20, + "maxAttempts": 10, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "NetworkInterfaces[].Status" + }, + { + "expected": "InvalidNetworkInterfaceID.NotFound", + "matcher": "error", + "state": "failure" + } + ] + }, + "PasswordDataAvailable": { + "operation": "GetPasswordData", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "length(PasswordData) > `0`", + "expected": true + } + ] + }, + "SnapshotCompleted": { + "delay": 15, + "operation": "DescribeSnapshots", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "Snapshots[].State" + } + ] + }, + "SpotInstanceRequestFulfilled": { + "operation": "DescribeSpotInstanceRequests", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "fulfilled" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "schedule-expired" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "canceled-before-fulfillment" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "bad-parameters" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "system-error" + } + ] + }, + "SubnetAvailable": { + "delay": 15, + "operation": "DescribeSubnets", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Subnets[].State" + } + ] + }, + "SystemStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].SystemStatus.Status", + "expected": "ok" + } + ] + }, + "VolumeAvailable": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VolumeDeleted": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "matcher": "error", + "expected": "InvalidVolume.NotFound", + "state": "success" + } + ] + }, + "VolumeInUse": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "in-use", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VpcAvailable": { + "delay": 15, + "operation": "DescribeVpcs", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Vpcs[].State" + } + ] + }, + "VpcExists": { + "operation": "DescribeVpcs", + "delay": 1, + "maxAttempts": 5, + "acceptors": [ + { + "matcher": "status", + "expected": 200, + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidVpcID.NotFound", + "state": "retry" + } + ] + }, + "VpnConnectionAvailable": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + }, + "VpnConnectionDeleted": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + }, + "VpcPeeringConnectionExists": { + "delay": 15, + "operation": "DescribeVpcPeeringConnections", + "maxAttempts": 40, + "acceptors": [ + { + "matcher": "status", + "expected": 200, + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidVpcPeeringConnectionID.NotFound", + "state": "retry" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..849784e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/examples-1.json new file mode 100644 index 0000000..93b4bf8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/examples-1.json @@ -0,0 +1,5048 @@ +{ + "version": "1.0", + "examples": { + "AllocateAddress": [ + { + "input": { + "Domain": "vpc" + }, + "output": { + "AllocationId": "eipalloc-64d5890a", + "Domain": "vpc", + "PublicIp": "203.0.113.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example allocates an Elastic IP address to use with an instance in a VPC.", + "id": "ec2-allocate-address-1", + "title": "To allocate an Elastic IP address for EC2-VPC" + }, + { + "output": { + "Domain": "standard", + "PublicIp": "198.51.100.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example allocates an Elastic IP address to use with an instance in EC2-Classic.", + "id": "ec2-allocate-address-2", + "title": "To allocate an Elastic IP address for EC2-Classic" + } + ], + "AssignPrivateIpAddresses": [ + { + "input": { + "NetworkInterfaceId": "eni-e5aa89a3", + "PrivateIpAddresses": [ + "10.0.0.82" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example assigns the specified secondary private IP address to the specified network interface.", + "id": "ec2-assign-private-ip-addresses-1", + "title": "To assign a specific secondary private IP address to an interface" + }, + { + "input": { + "NetworkInterfaceId": "eni-e5aa89a3", + "SecondaryPrivateIpAddressCount": 2 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example assigns two secondary private IP addresses to the specified network interface. Amazon EC2 automatically assigns these IP addresses from the available IP addresses in the CIDR block range of the subnet the network interface is associated with.", + "id": "ec2-assign-private-ip-addresses-2", + "title": "To assign secondary private IP addresses that Amazon EC2 selects to an interface" + } + ], + "AssociateAddress": [ + { + "input": { + "AllocationId": "eipalloc-64d5890a", + "InstanceId": "i-0b263919b6498b123" + }, + "output": { + "AssociationId": "eipassoc-2bebb745" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified Elastic IP address with the specified instance in a VPC.", + "id": "ec2-associate-address-1", + "title": "To associate an Elastic IP address in EC2-VPC" + }, + { + "input": { + "AllocationId": "eipalloc-64d5890a", + "NetworkInterfaceId": "eni-1a2b3c4d" + }, + "output": { + "AssociationId": "eipassoc-2bebb745" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified Elastic IP address with the specified network interface.", + "id": "ec2-associate-address-2", + "title": "To associate an Elastic IP address with a network interface" + }, + { + "input": { + "InstanceId": "i-07ffe74c7330ebf53", + "PublicIp": "198.51.100.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates an Elastic IP address with an instance in EC2-Classic.", + "id": "ec2-associate-address-3", + "title": "To associate an Elastic IP address in EC2-Classic" + } + ], + "AssociateDhcpOptions": [ + { + "input": { + "DhcpOptionsId": "dopt-d9070ebb", + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified DHCP options set with the specified VPC.", + "id": "ec2-associate-dhcp-options-1", + "title": "To associate a DHCP options set with a VPC" + }, + { + "input": { + "DhcpOptionsId": "default", + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the default DHCP options set with the specified VPC.", + "id": "ec2-associate-dhcp-options-2", + "title": "To associate the default DHCP options set with a VPC" + } + ], + "AssociateIamInstanceProfile": [ + { + "input": { + "IamInstanceProfile": { + "Name": "admin-role" + }, + "InstanceId": "i-123456789abcde123" + }, + "output": { + "IamInstanceProfileAssociation": { + "AssociationId": "iip-assoc-0e7736511a163c209", + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/admin-role", + "Id": "AIPAJBLK7RKJKWDXVHIEC" + }, + "InstanceId": "i-123456789abcde123", + "State": "associating" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates an IAM instance profile named admin-role with the specified instance.", + "id": "to-associate-an-iam-instance-profile-with-an-instance-1528928429850", + "title": "To associate an IAM instance profile with an instance" + } + ], + "AssociateRouteTable": [ + { + "input": { + "RouteTableId": "rtb-22574640", + "SubnetId": "subnet-9d4a7b6" + }, + "output": { + "AssociationId": "rtbassoc-781d0d1a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified route table with the specified subnet.", + "id": "ec2-associate-route-table-1", + "title": "To associate a route table with a subnet" + } + ], + "AttachInternetGateway": [ + { + "input": { + "InternetGatewayId": "igw-c0a643a9", + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example attaches the specified Internet gateway to the specified VPC.", + "id": "ec2-attach-internet-gateway-1", + "title": "To attach an Internet gateway to a VPC" + } + ], + "AttachNetworkInterface": [ + { + "input": { + "DeviceIndex": 1, + "InstanceId": "i-1234567890abcdef0", + "NetworkInterfaceId": "eni-e5aa89a3" + }, + "output": { + "AttachmentId": "eni-attach-66c4350a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example attaches the specified network interface to the specified instance.", + "id": "ec2-attach-network-interface-1", + "title": "To attach a network interface to an instance" + } + ], + "AttachVolume": [ + { + "input": { + "Device": "/dev/sdf", + "InstanceId": "i-01474ef662b89480", + "VolumeId": "vol-1234567890abcdef0" + }, + "output": { + "AttachTime": "2016-08-29T18:52:32.724Z", + "Device": "/dev/sdf", + "InstanceId": "i-01474ef662b89480", + "State": "attaching", + "VolumeId": "vol-1234567890abcdef0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example attaches a volume (``vol-1234567890abcdef0``) to an instance (``i-01474ef662b89480``) as ``/dev/sdf``.", + "id": "to-attach-a-volume-to-an-instance-1472499213109", + "title": "To attach a volume to an instance" + } + ], + "AuthorizeSecurityGroupEgress": [ + { + "input": { + "GroupId": "sg-1a2b3c4d", + "IpPermissions": [ + { + "FromPort": 80, + "IpProtocol": "tcp", + "IpRanges": [ + { + "CidrIp": "10.0.0.0/16" + } + ], + "ToPort": 80 + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds a rule that grants access to the specified address ranges on TCP port 80.", + "id": "to-add-a-rule-that-allows-outbound-traffic-to-a-specific-address-range-1528929309636", + "title": "To add a rule that allows outbound traffic to a specific address range" + }, + { + "input": { + "GroupId": "sg-1a2b3c4d", + "IpPermissions": [ + { + "FromPort": 80, + "IpProtocol": "tcp", + "ToPort": 80, + "UserIdGroupPairs": [ + { + "GroupId": "sg-4b51a32f" + } + ] + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds a rule that grants access to the specified security group on TCP port 80.", + "id": "to-add-a-rule-that-allows-outbound-traffic-to-a-specific-security-group-1528929760260", + "title": "To add a rule that allows outbound traffic to a specific security group" + } + ], + "AuthorizeSecurityGroupIngress": [ + { + "input": { + "GroupId": "sg-903004f8", + "IpPermissions": [ + { + "FromPort": 22, + "IpProtocol": "tcp", + "IpRanges": [ + { + "CidrIp": "203.0.113.0/24", + "Description": "SSH access from the LA office" + } + ], + "ToPort": 22 + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables inbound traffic on TCP port 22 (SSH). The rule includes a description to help you identify it later.", + "id": "to-add-a-rule-that-allows-inbound-ssh-traffic-1529011610328", + "title": "To add a rule that allows inbound SSH traffic from an IPv4 address range" + }, + { + "input": { + "GroupId": "sg-111aaa22", + "IpPermissions": [ + { + "FromPort": 80, + "IpProtocol": "tcp", + "ToPort": 80, + "UserIdGroupPairs": [ + { + "Description": "HTTP access from other instances", + "GroupId": "sg-1a2b3c4d" + } + ] + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables inbound traffic on TCP port 80 from the specified security group. The group must be in the same VPC or a peer VPC. Incoming traffic is allowed based on the private IP addresses of instances that are associated with the specified security group.", + "id": "to-add-a-rule-that-allows-inbound-http-traffic-from-another-security-group-1529012163168", + "title": "To add a rule that allows inbound HTTP traffic from another security group" + }, + { + "input": { + "GroupId": "sg-123abc12 ", + "IpPermissions": [ + { + "FromPort": 3389, + "IpProtocol": "tcp", + "Ipv6Ranges": [ + { + "CidrIpv6": "2001:db8:1234:1a00::/64", + "Description": "RDP access from the NY office" + } + ], + "ToPort": 3389 + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds an inbound rule that allows RDP traffic from the specified IPv6 address range. The rule includes a description to help you identify it later.", + "id": "to-add-a-rule-with-a-description-1529012418116", + "title": "To add a rule that allows inbound RDP traffic from an IPv6 address range" + } + ], + "CancelSpotFleetRequests": [ + { + "input": { + "SpotFleetRequestIds": [ + "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + ], + "TerminateInstances": true + }, + "output": { + "SuccessfulFleetRequests": [ + { + "CurrentSpotFleetRequestState": "cancelled_running", + "PreviousSpotFleetRequestState": "active", + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example cancels the specified Spot fleet request and terminates its associated Spot Instances.", + "id": "ec2-cancel-spot-fleet-requests-1", + "title": "To cancel a Spot fleet request" + }, + { + "input": { + "SpotFleetRequestIds": [ + "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + ], + "TerminateInstances": false + }, + "output": { + "SuccessfulFleetRequests": [ + { + "CurrentSpotFleetRequestState": "cancelled_terminating", + "PreviousSpotFleetRequestState": "active", + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example cancels the specified Spot fleet request without terminating its associated Spot Instances.", + "id": "ec2-cancel-spot-fleet-requests-2", + "title": "To cancel a Spot fleet request without terminating its Spot Instances" + } + ], + "CancelSpotInstanceRequests": [ + { + "input": { + "SpotInstanceRequestIds": [ + "sir-08b93456" + ] + }, + "output": { + "CancelledSpotInstanceRequests": [ + { + "SpotInstanceRequestId": "sir-08b93456", + "State": "cancelled" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example cancels a Spot Instance request.", + "id": "ec2-cancel-spot-instance-requests-1", + "title": "To cancel Spot Instance requests" + } + ], + "ConfirmProductInstance": [ + { + "input": { + "InstanceId": "i-1234567890abcdef0", + "ProductCode": "774F4FF8" + }, + "output": { + "OwnerId": "123456789012" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example determines whether the specified product code is associated with the specified instance.", + "id": "to-confirm-the-product-instance-1472712108494", + "title": "To confirm the product instance" + } + ], + "CopyImage": [ + { + "input": { + "Description": "", + "Name": "My server", + "SourceImageId": "ami-5731123e", + "SourceRegion": "us-east-1" + }, + "output": { + "ImageId": "ami-438bea42" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example copies the specified AMI from the us-east-1 region to the current region.", + "id": "to-copy-an-ami-to-another-region-1529022820832", + "title": "To copy an AMI to another region" + } + ], + "CopySnapshot": [ + { + "input": { + "Description": "This is my copied snapshot.", + "DestinationRegion": "us-east-1", + "SourceRegion": "us-west-2", + "SourceSnapshotId": "snap-066877671789bd71b" + }, + "output": { + "SnapshotId": "snap-066877671789bd71b" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example copies a snapshot with the snapshot ID of ``snap-066877671789bd71b`` from the ``us-west-2`` region to the ``us-east-1`` region and adds a short description to identify the snapshot.", + "id": "to-copy-a-snapshot-1472502259774", + "title": "To copy a snapshot" + } + ], + "CreateCustomerGateway": [ + { + "input": { + "BgpAsn": 65534, + "PublicIp": "12.1.2.3", + "Type": "ipsec.1" + }, + "output": { + "CustomerGateway": { + "BgpAsn": "65534", + "CustomerGatewayId": "cgw-0e11f167", + "IpAddress": "12.1.2.3", + "State": "available", + "Type": "ipsec.1" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a customer gateway with the specified IP address for its outside interface.", + "id": "ec2-create-customer-gateway-1", + "title": "To create a customer gateway" + } + ], + "CreateDhcpOptions": [ + { + "input": { + "DhcpConfigurations": [ + { + "Key": "domain-name-servers", + "Values": [ + "10.2.5.1", + "10.2.5.2" + ] + } + ] + }, + "output": { + "DhcpOptions": { + "DhcpConfigurations": [ + { + "Key": "domain-name-servers", + "Values": [ + { + "Value": "10.2.5.2" + }, + { + "Value": "10.2.5.1" + } + ] + } + ], + "DhcpOptionsId": "dopt-d9070ebb" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a DHCP options set.", + "id": "ec2-create-dhcp-options-1", + "title": "To create a DHCP options set" + } + ], + "CreateImage": [ + { + "input": { + "BlockDeviceMappings": [ + { + "DeviceName": "/dev/sdh", + "Ebs": { + "VolumeSize": "100" + } + }, + { + "DeviceName": "/dev/sdc", + "VirtualName": "ephemeral1" + } + ], + "Description": "An AMI for my server", + "InstanceId": "i-1234567890abcdef0", + "Name": "My server", + "NoReboot": true + }, + "output": { + "ImageId": "ami-1a2b3c4d" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an AMI from the specified instance and adds an EBS volume with the device name /dev/sdh and an instance store volume with the device name /dev/sdc.", + "id": "to-create-an-ami-from-an-amazon-ebs-backed-instance-1529023150636", + "title": "To create an AMI from an Amazon EBS-backed instance" + } + ], + "CreateInternetGateway": [ + { + "output": { + "InternetGateway": { + "Attachments": [ + + ], + "InternetGatewayId": "igw-c0a643a9", + "Tags": [ + + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an Internet gateway.", + "id": "ec2-create-internet-gateway-1", + "title": "To create an Internet gateway" + } + ], + "CreateKeyPair": [ + { + "input": { + "KeyName": "my-key-pair" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a key pair named my-key-pair.", + "id": "ec2-create-key-pair-1", + "title": "To create a key pair" + } + ], + "CreateLaunchTemplate": [ + { + "input": { + "LaunchTemplateData": { + "ImageId": "ami-8c1be5f6", + "InstanceType": "t2.small", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeviceIndex": 0, + "Ipv6AddressCount": 1, + "SubnetId": "subnet-7b16de0c" + } + ], + "TagSpecifications": [ + { + "ResourceType": "instance", + "Tags": [ + { + "Key": "Name", + "Value": "webserver" + } + ] + } + ] + }, + "LaunchTemplateName": "my-template", + "VersionDescription": "WebVersion1" + }, + "output": { + "LaunchTemplate": { + "CreateTime": "2017-11-27T09:13:24.000Z", + "CreatedBy": "arn:aws:iam::123456789012:root", + "DefaultVersionNumber": 1, + "LatestVersionNumber": 1, + "LaunchTemplateId": "lt-01238c059e3466abc", + "LaunchTemplateName": "my-template" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a launch template that specifies the subnet in which to launch the instance, assigns a public IP address and an IPv6 address to the instance, and creates a tag for the instance.", + "id": "to-create-a-launch-template-1529023655488", + "title": "To create a launch template" + } + ], + "CreateLaunchTemplateVersion": [ + { + "input": { + "LaunchTemplateData": { + "ImageId": "ami-c998b6b2" + }, + "LaunchTemplateId": "lt-0abcd290751193123", + "SourceVersion": "1", + "VersionDescription": "WebVersion2" + }, + "output": { + "LaunchTemplateVersion": { + "CreateTime": "2017-12-01T13:35:46.000Z", + "CreatedBy": "arn:aws:iam::123456789012:root", + "DefaultVersion": false, + "LaunchTemplateData": { + "ImageId": "ami-c998b6b2", + "InstanceType": "t2.micro", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeviceIndex": 0, + "Ipv6Addresses": [ + { + "Ipv6Address": "2001:db8:1234:1a00::123" + } + ], + "SubnetId": "subnet-7b16de0c" + } + ] + }, + "LaunchTemplateId": "lt-0abcd290751193123", + "LaunchTemplateName": "my-template", + "VersionDescription": "WebVersion2", + "VersionNumber": 2 + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a new launch template version based on version 1 of the specified launch template and specifies a different AMI ID.", + "id": "to-create-a-launch-template-version-1529024195702", + "title": "To create a launch template version" + } + ], + "CreateNatGateway": [ + { + "input": { + "AllocationId": "eipalloc-37fc1a52", + "SubnetId": "subnet-1a2b3c4d" + }, + "output": { + "NatGateway": { + "CreateTime": "2015-12-17T12:45:26.732Z", + "NatGatewayAddresses": [ + { + "AllocationId": "eipalloc-37fc1a52" + } + ], + "NatGatewayId": "nat-08d48af2a8e83edfd", + "State": "pending", + "SubnetId": "subnet-1a2b3c4d", + "VpcId": "vpc-1122aabb" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a NAT gateway in subnet subnet-1a2b3c4d and associates an Elastic IP address with the allocation ID eipalloc-37fc1a52 with the NAT gateway.", + "id": "ec2-create-nat-gateway-1", + "title": "To create a NAT gateway" + } + ], + "CreateNetworkAcl": [ + { + "input": { + "VpcId": "vpc-a01106c2" + }, + "output": { + "NetworkAcl": { + "Associations": [ + + ], + "Entries": [ + { + "CidrBlock": "0.0.0.0/0", + "Egress": true, + "Protocol": "-1", + "RuleAction": "deny", + "RuleNumber": 32767 + }, + { + "CidrBlock": "0.0.0.0/0", + "Egress": false, + "Protocol": "-1", + "RuleAction": "deny", + "RuleNumber": 32767 + } + ], + "IsDefault": false, + "NetworkAclId": "acl-5fb85d36", + "Tags": [ + + ], + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a network ACL for the specified VPC.", + "id": "ec2-create-network-acl-1", + "title": "To create a network ACL" + } + ], + "CreateNetworkAclEntry": [ + { + "input": { + "CidrBlock": "0.0.0.0/0", + "Egress": false, + "NetworkAclId": "acl-5fb85d36", + "PortRange": { + "From": 53, + "To": 53 + }, + "Protocol": "17", + "RuleAction": "allow", + "RuleNumber": 100 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an entry for the specified network ACL. The rule allows ingress traffic from anywhere (0.0.0.0/0) on UDP port 53 (DNS) into any associated subnet.", + "id": "ec2-create-network-acl-entry-1", + "title": "To create a network ACL entry" + } + ], + "CreateNetworkInterface": [ + { + "input": { + "Description": "my network interface", + "Groups": [ + "sg-903004f8" + ], + "PrivateIpAddress": "10.0.2.17", + "SubnetId": "subnet-9d4a7b6c" + }, + "output": { + "NetworkInterface": { + "AvailabilityZone": "us-east-1d", + "Description": "my network interface", + "Groups": [ + { + "GroupId": "sg-903004f8", + "GroupName": "default" + } + ], + "MacAddress": "02:1a:80:41:52:9c", + "NetworkInterfaceId": "eni-e5aa89a3", + "OwnerId": "123456789012", + "PrivateIpAddress": "10.0.2.17", + "PrivateIpAddresses": [ + { + "Primary": true, + "PrivateIpAddress": "10.0.2.17" + } + ], + "RequesterManaged": false, + "SourceDestCheck": true, + "Status": "pending", + "SubnetId": "subnet-9d4a7b6c", + "TagSet": [ + + ], + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a network interface for the specified subnet.", + "id": "ec2-create-network-interface-1", + "title": "To create a network interface" + } + ], + "CreatePlacementGroup": [ + { + "input": { + "GroupName": "my-cluster", + "Strategy": "cluster" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a placement group with the specified name.", + "id": "to-create-a-placement-group-1472712245768", + "title": "To create a placement group" + } + ], + "CreateRoute": [ + { + "input": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": "igw-c0a643a9", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a route for the specified route table. The route matches all traffic (0.0.0.0/0) and routes it to the specified Internet gateway.", + "id": "ec2-create-route-1", + "title": "To create a route" + } + ], + "CreateRouteTable": [ + { + "input": { + "VpcId": "vpc-a01106c2" + }, + "output": { + "RouteTable": { + "Associations": [ + + ], + "PropagatingVgws": [ + + ], + "RouteTableId": "rtb-22574640", + "Routes": [ + { + "DestinationCidrBlock": "10.0.0.0/16", + "GatewayId": "local", + "State": "active" + } + ], + "Tags": [ + + ], + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a route table for the specified VPC.", + "id": "ec2-create-route-table-1", + "title": "To create a route table" + } + ], + "CreateSecurityGroup": [ + { + "input": { + "Description": "My security group", + "GroupName": "my-security-group", + "VpcId": "vpc-1a2b3c4d" + }, + "output": { + "GroupId": "sg-903004f8" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a security group for the specified VPC.", + "id": "to-create-a-security-group-for-a-vpc-1529024532716", + "title": "To create a security group for a VPC" + } + ], + "CreateSnapshot": [ + { + "input": { + "Description": "This is my root volume snapshot.", + "VolumeId": "vol-1234567890abcdef0" + }, + "output": { + "Description": "This is my root volume snapshot.", + "OwnerId": "012345678910", + "SnapshotId": "snap-066877671789bd71b", + "StartTime": "2014-02-28T21:06:01.000Z", + "State": "pending", + "Tags": [ + + ], + "VolumeId": "vol-1234567890abcdef0", + "VolumeSize": 8 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a snapshot of the volume with a volume ID of ``vol-1234567890abcdef0`` and a short description to identify the snapshot.", + "id": "to-create-a-snapshot-1472502529790", + "title": "To create a snapshot" + } + ], + "CreateSpotDatafeedSubscription": [ + { + "input": { + "Bucket": "my-s3-bucket", + "Prefix": "spotdata" + }, + "output": { + "SpotDatafeedSubscription": { + "Bucket": "my-s3-bucket", + "OwnerId": "123456789012", + "Prefix": "spotdata", + "State": "Active" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a Spot Instance data feed for your AWS account.", + "id": "ec2-create-spot-datafeed-subscription-1", + "title": "To create a Spot Instance datafeed" + } + ], + "CreateSubnet": [ + { + "input": { + "CidrBlock": "10.0.1.0/24", + "VpcId": "vpc-a01106c2" + }, + "output": { + "Subnet": { + "AvailabilityZone": "us-west-2c", + "AvailableIpAddressCount": 251, + "CidrBlock": "10.0.1.0/24", + "State": "pending", + "SubnetId": "subnet-9d4a7b6c", + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a subnet in the specified VPC with the specified CIDR block. We recommend that you let us select an Availability Zone for you.", + "id": "ec2-create-subnet-1", + "title": "To create a subnet" + } + ], + "CreateTags": [ + { + "input": { + "Resources": [ + "ami-78a54011" + ], + "Tags": [ + { + "Key": "Stack", + "Value": "production" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds the tag Stack=production to the specified image, or overwrites an existing tag for the AMI where the tag key is Stack.", + "id": "ec2-create-tags-1", + "title": "To add a tag to a resource" + } + ], + "CreateVolume": [ + { + "input": { + "AvailabilityZone": "us-east-1a", + "Size": 80, + "VolumeType": "gp2" + }, + "output": { + "AvailabilityZone": "us-east-1a", + "CreateTime": "2016-08-29T18:52:32.724Z", + "Encrypted": false, + "Iops": 240, + "Size": 80, + "SnapshotId": "", + "State": "creating", + "VolumeId": "vol-6b60b7c7", + "VolumeType": "gp2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an 80 GiB General Purpose (SSD) volume in the Availability Zone ``us-east-1a``.", + "id": "to-create-a-new-volume-1472496724296", + "title": "To create a new volume" + }, + { + "input": { + "AvailabilityZone": "us-east-1a", + "Iops": 1000, + "SnapshotId": "snap-066877671789bd71b", + "VolumeType": "io1" + }, + "output": { + "Attachments": [ + + ], + "AvailabilityZone": "us-east-1a", + "CreateTime": "2016-08-29T18:52:32.724Z", + "Iops": 1000, + "Size": 500, + "SnapshotId": "snap-066877671789bd71b", + "State": "creating", + "Tags": [ + + ], + "VolumeId": "vol-1234567890abcdef0", + "VolumeType": "io1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a new Provisioned IOPS (SSD) volume with 1000 provisioned IOPS from a snapshot in the Availability Zone ``us-east-1a``.", + "id": "to-create-a-new-provisioned-iops-ssd-volume-from-a-snapshot-1472498975176", + "title": "To create a new Provisioned IOPS (SSD) volume from a snapshot" + } + ], + "CreateVpc": [ + { + "input": { + "CidrBlock": "10.0.0.0/16" + }, + "output": { + "Vpc": { + "CidrBlock": "10.0.0.0/16", + "DhcpOptionsId": "dopt-7a8b9c2d", + "InstanceTenancy": "default", + "State": "pending", + "VpcId": "vpc-a01106c2" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a VPC with the specified CIDR block.", + "id": "ec2-create-vpc-1", + "title": "To create a VPC" + } + ], + "DeleteCustomerGateway": [ + { + "input": { + "CustomerGatewayId": "cgw-0e11f167" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified customer gateway.", + "id": "ec2-delete-customer-gateway-1", + "title": "To delete a customer gateway" + } + ], + "DeleteDhcpOptions": [ + { + "input": { + "DhcpOptionsId": "dopt-d9070ebb" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified DHCP options set.", + "id": "ec2-delete-dhcp-options-1", + "title": "To delete a DHCP options set" + } + ], + "DeleteInternetGateway": [ + { + "input": { + "InternetGatewayId": "igw-c0a643a9" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified Internet gateway.", + "id": "ec2-delete-internet-gateway-1", + "title": "To delete an Internet gateway" + } + ], + "DeleteKeyPair": [ + { + "input": { + "KeyName": "my-key-pair" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified key pair.", + "id": "ec2-delete-key-pair-1", + "title": "To delete a key pair" + } + ], + "DeleteLaunchTemplate": [ + { + "input": { + "LaunchTemplateId": "lt-0abcd290751193123" + }, + "output": { + "LaunchTemplate": { + "CreateTime": "2017-11-23T16:46:25.000Z", + "CreatedBy": "arn:aws:iam::123456789012:root", + "DefaultVersionNumber": 2, + "LatestVersionNumber": 2, + "LaunchTemplateId": "lt-0abcd290751193123", + "LaunchTemplateName": "my-template" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified launch template.", + "id": "to-delete-a-launch-template-1529024658216", + "title": "To delete a launch template" + } + ], + "DeleteLaunchTemplateVersions": [ + { + "input": { + "LaunchTemplateId": "lt-0abcd290751193123", + "Versions": [ + "1" + ] + }, + "output": { + "SuccessfullyDeletedLaunchTemplateVersions": [ + { + "LaunchTemplateId": "lt-0abcd290751193123", + "LaunchTemplateName": "my-template", + "VersionNumber": 1 + } + ], + "UnsuccessfullyDeletedLaunchTemplateVersions": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified launch template version.", + "id": "to-delete-a-launch-template-version-1529024790864", + "title": "To delete a launch template version" + } + ], + "DeleteNatGateway": [ + { + "input": { + "NatGatewayId": "nat-04ae55e711cec5680" + }, + "output": { + "NatGatewayId": "nat-04ae55e711cec5680" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified NAT gateway.", + "id": "ec2-delete-nat-gateway-1", + "title": "To delete a NAT gateway" + } + ], + "DeleteNetworkAcl": [ + { + "input": { + "NetworkAclId": "acl-5fb85d36" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified network ACL.", + "id": "ec2-delete-network-acl-1", + "title": "To delete a network ACL" + } + ], + "DeleteNetworkAclEntry": [ + { + "input": { + "Egress": true, + "NetworkAclId": "acl-5fb85d36", + "RuleNumber": 100 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes ingress rule number 100 from the specified network ACL.", + "id": "ec2-delete-network-acl-entry-1", + "title": "To delete a network ACL entry" + } + ], + "DeleteNetworkInterface": [ + { + "input": { + "NetworkInterfaceId": "eni-e5aa89a3" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified network interface.", + "id": "ec2-delete-network-interface-1", + "title": "To delete a network interface" + } + ], + "DeletePlacementGroup": [ + { + "input": { + "GroupName": "my-cluster" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified placement group.\n", + "id": "to-delete-a-placement-group-1472712349959", + "title": "To delete a placement group" + } + ], + "DeleteRoute": [ + { + "input": { + "DestinationCidrBlock": "0.0.0.0/0", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified route from the specified route table.", + "id": "ec2-delete-route-1", + "title": "To delete a route" + } + ], + "DeleteRouteTable": [ + { + "input": { + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified route table.", + "id": "ec2-delete-route-table-1", + "title": "To delete a route table" + } + ], + "DeleteSecurityGroup": [ + { + "input": { + "GroupId": "sg-903004f8" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified security group.", + "id": "to-delete-a-security-group-1529024952972", + "title": "To delete a security group" + } + ], + "DeleteSnapshot": [ + { + "input": { + "SnapshotId": "snap-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes a snapshot with the snapshot ID of ``snap-1234567890abcdef0``. If the command succeeds, no output is returned.", + "id": "to-delete-a-snapshot-1472503042567", + "title": "To delete a snapshot" + } + ], + "DeleteSpotDatafeedSubscription": [ + { + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes a Spot data feed subscription for the account.", + "id": "ec2-delete-spot-datafeed-subscription-1", + "title": "To cancel a Spot Instance data feed subscription" + } + ], + "DeleteSubnet": [ + { + "input": { + "SubnetId": "subnet-9d4a7b6c" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified subnet.", + "id": "ec2-delete-subnet-1", + "title": "To delete a subnet" + } + ], + "DeleteTags": [ + { + "input": { + "Resources": [ + "ami-78a54011" + ], + "Tags": [ + { + "Key": "Stack", + "Value": "test" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the tag Stack=test from the specified image.", + "id": "ec2-delete-tags-1", + "title": "To delete a tag from a resource" + } + ], + "DeleteVolume": [ + { + "input": { + "VolumeId": "vol-049df61146c4d7901" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes an available volume with the volume ID of ``vol-049df61146c4d7901``. If the command succeeds, no output is returned.", + "id": "to-delete-a-volume-1472503111160", + "title": "To delete a volume" + } + ], + "DeleteVpc": [ + { + "input": { + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified VPC.", + "id": "ec2-delete-vpc-1", + "title": "To delete a VPC" + } + ], + "DescribeAccountAttributes": [ + { + "input": { + "AttributeNames": [ + "supported-platforms" + ] + }, + "output": { + "AccountAttributes": [ + { + "AttributeName": "supported-platforms", + "AttributeValues": [ + { + "AttributeValue": "EC2" + }, + { + "AttributeValue": "VPC" + } + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the supported-platforms attribute for your AWS account.", + "id": "ec2-describe-account-attributes-1", + "title": "To describe a single attribute for your AWS account" + }, + { + "output": { + "AccountAttributes": [ + { + "AttributeName": "supported-platforms", + "AttributeValues": [ + { + "AttributeValue": "EC2" + }, + { + "AttributeValue": "VPC" + } + ] + }, + { + "AttributeName": "vpc-max-security-groups-per-interface", + "AttributeValues": [ + { + "AttributeValue": "5" + } + ] + }, + { + "AttributeName": "max-elastic-ips", + "AttributeValues": [ + { + "AttributeValue": "5" + } + ] + }, + { + "AttributeName": "max-instances", + "AttributeValues": [ + { + "AttributeValue": "20" + } + ] + }, + { + "AttributeName": "vpc-max-elastic-ips", + "AttributeValues": [ + { + "AttributeValue": "5" + } + ] + }, + { + "AttributeName": "default-vpc", + "AttributeValues": [ + { + "AttributeValue": "none" + } + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the attributes for your AWS account.", + "id": "ec2-describe-account-attributes-2", + "title": "To describe all attributes for your AWS account" + } + ], + "DescribeAddresses": [ + { + "output": { + "Addresses": [ + { + "Domain": "standard", + "InstanceId": "i-1234567890abcdef0", + "PublicIp": "198.51.100.0" + }, + { + "AllocationId": "eipalloc-12345678", + "AssociationId": "eipassoc-12345678", + "Domain": "vpc", + "InstanceId": "i-1234567890abcdef0", + "NetworkInterfaceId": "eni-12345678", + "NetworkInterfaceOwnerId": "123456789012", + "PrivateIpAddress": "10.0.1.241", + "PublicIp": "203.0.113.0" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes your Elastic IP addresses.", + "id": "ec2-describe-addresses-1", + "title": "To describe your Elastic IP addresses" + }, + { + "input": { + "Filters": [ + { + "Name": "domain", + "Values": [ + "vpc" + ] + } + ] + }, + "output": { + "Addresses": [ + { + "AllocationId": "eipalloc-12345678", + "AssociationId": "eipassoc-12345678", + "Domain": "vpc", + "InstanceId": "i-1234567890abcdef0", + "NetworkInterfaceId": "eni-12345678", + "NetworkInterfaceOwnerId": "123456789012", + "PrivateIpAddress": "10.0.1.241", + "PublicIp": "203.0.113.0" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes your Elastic IP addresses for use with instances in a VPC.", + "id": "ec2-describe-addresses-2", + "title": "To describe your Elastic IP addresses for EC2-VPC" + }, + { + "input": { + "Filters": [ + { + "Name": "domain", + "Values": [ + "standard" + ] + } + ] + }, + "output": { + "Addresses": [ + { + "Domain": "standard", + "InstanceId": "i-1234567890abcdef0", + "PublicIp": "198.51.100.0" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes your Elastic IP addresses for use with instances in EC2-Classic.", + "id": "ec2-describe-addresses-3", + "title": "To describe your Elastic IP addresses for EC2-Classic" + } + ], + "DescribeAvailabilityZones": [ + { + "output": { + "AvailabilityZones": [ + { + "Messages": [ + + ], + "RegionName": "us-east-1", + "State": "available", + "ZoneName": "us-east-1b" + }, + { + "Messages": [ + + ], + "RegionName": "us-east-1", + "State": "available", + "ZoneName": "us-east-1c" + }, + { + "Messages": [ + + ], + "RegionName": "us-east-1", + "State": "available", + "ZoneName": "us-east-1d" + }, + { + "Messages": [ + + ], + "RegionName": "us-east-1", + "State": "available", + "ZoneName": "us-east-1e" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the Availability Zones that are available to you. The response includes Availability Zones only for the current region.", + "id": "ec2-describe-availability-zones-1", + "title": "To describe your Availability Zones" + } + ], + "DescribeCustomerGateways": [ + { + "input": { + "CustomerGatewayIds": [ + "cgw-0e11f167" + ] + }, + "output": { + "CustomerGateways": [ + { + "BgpAsn": "65534", + "CustomerGatewayId": "cgw-0e11f167", + "IpAddress": "12.1.2.3", + "State": "available", + "Type": "ipsec.1" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified customer gateway.", + "id": "ec2-describe-customer-gateways-1", + "title": "To describe a customer gateway" + } + ], + "DescribeDhcpOptions": [ + { + "input": { + "DhcpOptionsIds": [ + "dopt-d9070ebb" + ] + }, + "output": { + "DhcpOptions": [ + { + "DhcpConfigurations": [ + { + "Key": "domain-name-servers", + "Values": [ + { + "Value": "10.2.5.2" + }, + { + "Value": "10.2.5.1" + } + ] + } + ], + "DhcpOptionsId": "dopt-d9070ebb" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified DHCP options set.", + "id": "ec2-describe-dhcp-options-1", + "title": "To describe a DHCP options set" + } + ], + "DescribeIamInstanceProfileAssociations": [ + { + "input": { + "AssociationIds": [ + "iip-assoc-0db249b1f25fa24b8" + ] + }, + "output": { + "IamInstanceProfileAssociations": [ + { + "AssociationId": "iip-assoc-0db249b1f25fa24b8", + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/admin-role", + "Id": "AIPAJVQN4F5WVLGCJDRGM" + }, + "InstanceId": "i-09eb09efa73ec1dee", + "State": "associated" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified IAM instance profile association.", + "id": "to-describe-an-iam-instance-profile-association-1529025123918", + "title": "To describe an IAM instance profile association" + } + ], + "DescribeImageAttribute": [ + { + "input": { + "Attribute": "launchPermission", + "ImageId": "ami-5731123e" + }, + "output": { + "ImageId": "ami-5731123e", + "LaunchPermissions": [ + { + "UserId": "123456789012" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the launch permissions for the specified AMI.", + "id": "to-describe-the-launch-permissions-for-an-ami-1529025296264", + "title": "To describe the launch permissions for an AMI" + } + ], + "DescribeImages": [ + { + "input": { + "ImageIds": [ + "ami-5731123e" + ] + }, + "output": { + "Images": [ + { + "Architecture": "x86_64", + "BlockDeviceMappings": [ + { + "DeviceName": "/dev/sda1", + "Ebs": { + "DeleteOnTermination": true, + "SnapshotId": "snap-1234567890abcdef0", + "VolumeSize": 8, + "VolumeType": "standard" + } + } + ], + "Description": "An AMI for my server", + "Hypervisor": "xen", + "ImageId": "ami-5731123e", + "ImageLocation": "123456789012/My server", + "ImageType": "machine", + "KernelId": "aki-88aa75e1", + "Name": "My server", + "OwnerId": "123456789012", + "Public": false, + "RootDeviceName": "/dev/sda1", + "RootDeviceType": "ebs", + "State": "available", + "VirtualizationType": "paravirtual" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified AMI.", + "id": "to-describe-an-ami-1529025482866", + "title": "To describe an AMI" + } + ], + "DescribeInstanceAttribute": [ + { + "input": { + "Attribute": "instanceType", + "InstanceId": "i-1234567890abcdef0" + }, + "output": { + "InstanceId": "i-1234567890abcdef0", + "InstanceType": { + "Value": "t1.micro" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the instance type of the specified instance.\n", + "id": "to-describe-the-instance-type-1472712432132", + "title": "To describe the instance type" + }, + { + "input": { + "Attribute": "disableApiTermination", + "InstanceId": "i-1234567890abcdef0" + }, + "output": { + "DisableApiTermination": { + "Value": "false" + }, + "InstanceId": "i-1234567890abcdef0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the ``disableApiTermination`` attribute of the specified instance.\n", + "id": "to-describe-the-disableapitermination-attribute-1472712533466", + "title": "To describe the disableApiTermination attribute" + }, + { + "input": { + "Attribute": "blockDeviceMapping", + "InstanceId": "i-1234567890abcdef0" + }, + "output": { + "BlockDeviceMappings": [ + { + "DeviceName": "/dev/sda1", + "Ebs": { + "AttachTime": "2013-05-17T22:42:34.000Z", + "DeleteOnTermination": true, + "Status": "attached", + "VolumeId": "vol-049df61146c4d7901" + } + }, + { + "DeviceName": "/dev/sdf", + "Ebs": { + "AttachTime": "2013-09-10T23:07:00.000Z", + "DeleteOnTermination": false, + "Status": "attached", + "VolumeId": "vol-049df61146c4d7901" + } + } + ], + "InstanceId": "i-1234567890abcdef0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the ``blockDeviceMapping`` attribute of the specified instance.\n", + "id": "to-describe-the-block-device-mapping-for-an-instance-1472712645423", + "title": "To describe the block device mapping for an instance" + } + ], + "DescribeInstanceStatus": [ + { + "input": { + "InstanceIds": [ + "i-1234567890abcdef0" + ] + }, + "output": { + "InstanceStatuses": [ + { + "AvailabilityZone": "us-east-1d", + "InstanceId": "i-1234567890abcdef0", + "InstanceState": { + "Code": 16, + "Name": "running" + }, + "InstanceStatus": { + "Details": [ + { + "Name": "reachability", + "Status": "passed" + } + ], + "Status": "ok" + }, + "SystemStatus": { + "Details": [ + { + "Name": "reachability", + "Status": "passed" + } + ], + "Status": "ok" + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the current status of the specified instance.", + "id": "to-describe-the-status-of-an-instance-1529025696830", + "title": "To describe the status of an instance" + } + ], + "DescribeInstances": [ + { + "input": { + "InstanceIds": [ + "i-1234567890abcdef0" + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified instance.", + "id": "to-describe-an-amazon-ec2-instance-1529025982172", + "title": "To describe an Amazon EC2 instance" + }, + { + "input": { + "Filters": [ + { + "Name": "instance-type", + "Values": [ + "t2.micro" + ] + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the instances with the t2.micro instance type.", + "id": "to-describe-the-instances-with-the-instance-type-t2micro-1529026147602", + "title": "To describe the instances with a specific instance type" + }, + { + "input": { + "Filters": [ + { + "Name": "tag:Purpose", + "Values": [ + "test" + ] + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the instances with the Purpose=test tag.", + "id": "to-describe-the-instances-with-a-specific-tag-1529026251928", + "title": "To describe the instances with a specific tag" + } + ], + "DescribeInternetGateways": [ + { + "input": { + "Filters": [ + { + "Name": "attachment.vpc-id", + "Values": [ + "vpc-a01106c2" + ] + } + ] + }, + "output": { + "InternetGateways": [ + { + "Attachments": [ + { + "State": "available", + "VpcId": "vpc-a01106c2" + } + ], + "InternetGatewayId": "igw-c0a643a9", + "Tags": [ + + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the Internet gateway for the specified VPC.", + "id": "ec2-describe-internet-gateways-1", + "title": "To describe the Internet gateway for a VPC" + } + ], + "DescribeKeyPairs": [ + { + "input": { + "KeyNames": [ + "my-key-pair" + ] + }, + "output": { + "KeyPairs": [ + { + "KeyFingerprint": "1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f", + "KeyName": "my-key-pair" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example displays the fingerprint for the specified key.", + "id": "ec2-describe-key-pairs-1", + "title": "To display a key pair" + } + ], + "DescribeLaunchTemplateVersions": [ + { + "input": { + "LaunchTemplateId": "068f72b72934aff71" + }, + "output": { + "LaunchTemplateVersions": [ + { + "CreateTime": "2017-11-20T13:12:32.000Z", + "CreatedBy": "arn:aws:iam::123456789102:root", + "DefaultVersion": false, + "LaunchTemplateData": { + "ImageId": "ami-6057e21a", + "InstanceType": "t2.medium", + "KeyName": "kp-us-east", + "NetworkInterfaces": [ + { + "DeviceIndex": 0, + "Groups": [ + "sg-7c227019" + ], + "SubnetId": "subnet-1a2b3c4d" + } + ] + }, + "LaunchTemplateId": "lt-068f72b72934aff71", + "LaunchTemplateName": "Webservers", + "VersionNumber": 2 + }, + { + "CreateTime": "2017-11-20T12:52:33.000Z", + "CreatedBy": "arn:aws:iam::123456789102:root", + "DefaultVersion": true, + "LaunchTemplateData": { + "ImageId": "ami-aabbcc11", + "InstanceType": "t2.medium", + "KeyName": "kp-us-east", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeleteOnTermination": false, + "DeviceIndex": 0, + "Groups": [ + "sg-7c227019" + ], + "SubnetId": "subnet-7b16de0c" + } + ], + "UserData": "" + }, + "LaunchTemplateId": "lt-068f72b72934aff71", + "LaunchTemplateName": "Webservers", + "VersionNumber": 1 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the versions for the specified launch template.", + "id": "to-describe-the-versions-for-a-launch-template-1529344425048", + "title": "To describe the versions for a launch template" + } + ], + "DescribeLaunchTemplates": [ + { + "input": { + "LaunchTemplateIds": [ + "lt-01238c059e3466abc" + ] + }, + "output": { + "LaunchTemplates": [ + { + "CreateTime": "2018-01-16T04:32:57.000Z", + "CreatedBy": "arn:aws:iam::123456789012:root", + "DefaultVersionNumber": 1, + "LatestVersionNumber": 1, + "LaunchTemplateId": "lt-01238c059e3466abc", + "LaunchTemplateName": "my-template" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified launch template.", + "id": "to-describe-a-launch-template-1529344182862", + "title": "To describe a launch template" + } + ], + "DescribeMovingAddresses": [ + { + "output": { + "MovingAddressStatuses": [ + { + "MoveStatus": "MovingToVpc", + "PublicIp": "198.51.100.0" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all of your moving Elastic IP addresses.", + "id": "ec2-describe-moving-addresses-1", + "title": "To describe your moving addresses" + } + ], + "DescribeNatGateways": [ + { + "input": { + "Filter": [ + { + "Name": "vpc-id", + "Values": [ + "vpc-1a2b3c4d" + ] + } + ] + }, + "output": { + "NatGateways": [ + { + "CreateTime": "2015-12-01T12:26:55.983Z", + "NatGatewayAddresses": [ + { + "AllocationId": "eipalloc-89c620ec", + "NetworkInterfaceId": "eni-9dec76cd", + "PrivateIp": "10.0.0.149", + "PublicIp": "198.11.222.333" + } + ], + "NatGatewayId": "nat-05dba92075d71c408", + "State": "available", + "SubnetId": "subnet-847e4dc2", + "VpcId": "vpc-1a2b3c4d" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the NAT gateway for the specified VPC.", + "id": "ec2-describe-nat-gateways-1", + "title": "To describe a NAT gateway" + } + ], + "DescribeNetworkAcls": [ + { + "input": { + "NetworkAclIds": [ + "acl-5fb85d36" + ] + }, + "output": { + "NetworkAcls": [ + { + "Associations": [ + { + "NetworkAclAssociationId": "aclassoc-66ea5f0b", + "NetworkAclId": "acl-9aeb5ef7", + "SubnetId": "subnet-65ea5f08" + } + ], + "Entries": [ + { + "CidrBlock": "0.0.0.0/0", + "Egress": true, + "Protocol": "-1", + "RuleAction": "deny", + "RuleNumber": 32767 + }, + { + "CidrBlock": "0.0.0.0/0", + "Egress": false, + "Protocol": "-1", + "RuleAction": "deny", + "RuleNumber": 32767 + } + ], + "IsDefault": false, + "NetworkAclId": "acl-5fb85d36", + "Tags": [ + + ], + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified network ACL.", + "id": "ec2-", + "title": "To describe a network ACL" + } + ], + "DescribeNetworkInterfaceAttribute": [ + { + "input": { + "Attribute": "attachment", + "NetworkInterfaceId": "eni-686ea200" + }, + "output": { + "Attachment": { + "AttachTime": "2015-05-21T20:02:20.000Z", + "AttachmentId": "eni-attach-43348162", + "DeleteOnTermination": true, + "DeviceIndex": 0, + "InstanceId": "i-1234567890abcdef0", + "InstanceOwnerId": "123456789012", + "Status": "attached" + }, + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the attachment attribute of the specified network interface.", + "id": "ec2-describe-network-interface-attribute-1", + "title": "To describe the attachment attribute of a network interface" + }, + { + "input": { + "Attribute": "description", + "NetworkInterfaceId": "eni-686ea200" + }, + "output": { + "Description": { + "Value": "My description" + }, + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the description attribute of the specified network interface.", + "id": "ec2-describe-network-interface-attribute-2", + "title": "To describe the description attribute of a network interface" + }, + { + "input": { + "Attribute": "groupSet", + "NetworkInterfaceId": "eni-686ea200" + }, + "output": { + "Groups": [ + { + "GroupId": "sg-903004f8", + "GroupName": "my-security-group" + } + ], + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the groupSet attribute of the specified network interface.", + "id": "ec2-describe-network-interface-attribute-3", + "title": "To describe the groupSet attribute of a network interface" + }, + { + "input": { + "Attribute": "sourceDestCheck", + "NetworkInterfaceId": "eni-686ea200" + }, + "output": { + "NetworkInterfaceId": "eni-686ea200", + "SourceDestCheck": { + "Value": true + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the sourceDestCheck attribute of the specified network interface.", + "id": "ec2-describe-network-interface-attribute-4", + "title": "To describe the sourceDestCheck attribute of a network interface" + } + ], + "DescribeNetworkInterfaces": [ + { + "input": { + "NetworkInterfaceIds": [ + "eni-e5aa89a3" + ] + }, + "output": { + "NetworkInterfaces": [ + { + "Association": { + "AssociationId": "eipassoc-0fbb766a", + "IpOwnerId": "123456789012", + "PublicDnsName": "ec2-203-0-113-12.compute-1.amazonaws.com", + "PublicIp": "203.0.113.12" + }, + "Attachment": { + "AttachTime": "2013-11-30T23:36:42.000Z", + "AttachmentId": "eni-attach-66c4350a", + "DeleteOnTermination": false, + "DeviceIndex": 1, + "InstanceId": "i-1234567890abcdef0", + "InstanceOwnerId": "123456789012", + "Status": "attached" + }, + "AvailabilityZone": "us-east-1d", + "Description": "my network interface", + "Groups": [ + { + "GroupId": "sg-8637d3e3", + "GroupName": "default" + } + ], + "MacAddress": "02:2f:8f:b0:cf:75", + "NetworkInterfaceId": "eni-e5aa89a3", + "OwnerId": "123456789012", + "PrivateDnsName": "ip-10-0-1-17.ec2.internal", + "PrivateIpAddress": "10.0.1.17", + "PrivateIpAddresses": [ + { + "Association": { + "AssociationId": "eipassoc-0fbb766a", + "IpOwnerId": "123456789012", + "PublicDnsName": "ec2-203-0-113-12.compute-1.amazonaws.com", + "PublicIp": "203.0.113.12" + }, + "Primary": true, + "PrivateDnsName": "ip-10-0-1-17.ec2.internal", + "PrivateIpAddress": "10.0.1.17" + } + ], + "RequesterManaged": false, + "SourceDestCheck": true, + "Status": "in-use", + "SubnetId": "subnet-b61f49f0", + "TagSet": [ + + ], + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "ec2-describe-network-interfaces-1", + "title": "To describe a network interface" + } + ], + "DescribeRegions": [ + { + "output": { + "Regions": [ + { + "Endpoint": "ec2.ap-south-1.amazonaws.com", + "RegionName": "ap-south-1" + }, + { + "Endpoint": "ec2.eu-west-1.amazonaws.com", + "RegionName": "eu-west-1" + }, + { + "Endpoint": "ec2.ap-southeast-1.amazonaws.com", + "RegionName": "ap-southeast-1" + }, + { + "Endpoint": "ec2.ap-southeast-2.amazonaws.com", + "RegionName": "ap-southeast-2" + }, + { + "Endpoint": "ec2.eu-central-1.amazonaws.com", + "RegionName": "eu-central-1" + }, + { + "Endpoint": "ec2.ap-northeast-2.amazonaws.com", + "RegionName": "ap-northeast-2" + }, + { + "Endpoint": "ec2.ap-northeast-1.amazonaws.com", + "RegionName": "ap-northeast-1" + }, + { + "Endpoint": "ec2.us-east-1.amazonaws.com", + "RegionName": "us-east-1" + }, + { + "Endpoint": "ec2.sa-east-1.amazonaws.com", + "RegionName": "sa-east-1" + }, + { + "Endpoint": "ec2.us-west-1.amazonaws.com", + "RegionName": "us-west-1" + }, + { + "Endpoint": "ec2.us-west-2.amazonaws.com", + "RegionName": "us-west-2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all the regions that are available to you.", + "id": "ec2-describe-regions-1", + "title": "To describe your regions" + } + ], + "DescribeRouteTables": [ + { + "input": { + "RouteTableIds": [ + "rtb-1f382e7d" + ] + }, + "output": { + "RouteTables": [ + { + "Associations": [ + { + "Main": true, + "RouteTableAssociationId": "rtbassoc-d8ccddba", + "RouteTableId": "rtb-1f382e7d" + } + ], + "PropagatingVgws": [ + + ], + "RouteTableId": "rtb-1f382e7d", + "Routes": [ + { + "DestinationCidrBlock": "10.0.0.0/16", + "GatewayId": "local", + "State": "active" + } + ], + "Tags": [ + + ], + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified route table.", + "id": "ec2-describe-route-tables-1", + "title": "To describe a route table" + } + ], + "DescribeScheduledInstanceAvailability": [ + { + "input": { + "FirstSlotStartTimeRange": { + "EarliestTime": "2016-01-31T00:00:00Z", + "LatestTime": "2016-01-31T04:00:00Z" + }, + "Recurrence": { + "Frequency": "Weekly", + "Interval": 1, + "OccurrenceDays": [ + 1 + ] + } + }, + "output": { + "ScheduledInstanceAvailabilitySet": [ + { + "AvailabilityZone": "us-west-2b", + "AvailableInstanceCount": 20, + "FirstSlotStartTime": "2016-01-31T00:00:00Z", + "HourlyPrice": "0.095", + "InstanceType": "c4.large", + "MaxTermDurationInDays": 366, + "MinTermDurationInDays": 366, + "NetworkPlatform": "EC2-VPC", + "Platform": "Linux/UNIX", + "PurchaseToken": "eyJ2IjoiMSIsInMiOjEsImMiOi...", + "Recurrence": { + "Frequency": "Weekly", + "Interval": 1, + "OccurrenceDaySet": [ + 1 + ], + "OccurrenceRelativeToEnd": false + }, + "SlotDurationInHours": 23, + "TotalScheduledInstanceHours": 1219 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes a schedule that occurs every week on Sunday, starting on the specified date. Note that the output contains a single schedule as an example.", + "id": "ec2-describe-scheduled-instance-availability-1", + "title": "To describe an available schedule" + } + ], + "DescribeScheduledInstances": [ + { + "input": { + "ScheduledInstanceIds": [ + "sci-1234-1234-1234-1234-123456789012" + ] + }, + "output": { + "ScheduledInstanceSet": [ + { + "AvailabilityZone": "us-west-2b", + "CreateDate": "2016-01-25T21:43:38.612Z", + "HourlyPrice": "0.095", + "InstanceCount": 1, + "InstanceType": "c4.large", + "NetworkPlatform": "EC2-VPC", + "NextSlotStartTime": "2016-01-31T09:00:00Z", + "Platform": "Linux/UNIX", + "Recurrence": { + "Frequency": "Weekly", + "Interval": 1, + "OccurrenceDaySet": [ + 1 + ], + "OccurrenceRelativeToEnd": false, + "OccurrenceUnit": "" + }, + "ScheduledInstanceId": "sci-1234-1234-1234-1234-123456789012", + "SlotDurationInHours": 32, + "TermEndDate": "2017-01-31T09:00:00Z", + "TermStartDate": "2016-01-31T09:00:00Z", + "TotalScheduledInstanceHours": 1696 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified Scheduled Instance.", + "id": "ec2-describe-scheduled-instances-1", + "title": "To describe your Scheduled Instances" + } + ], + "DescribeSecurityGroupReferences": [ + { + "input": { + "GroupId": [ + "sg-903004f8" + ] + }, + "output": { + "SecurityGroupReferenceSet": [ + { + "GroupId": "sg-903004f8", + "ReferencingVpcId": "vpc-1a2b3c4d", + "VpcPeeringConnectionId": "pcx-b04deed9" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the security group references for the specified security group.", + "id": "to-describe-security-group-references-1529354312088", + "title": "To describe security group references" + } + ], + "DescribeSecurityGroups": [ + { + "input": { + "GroupIds": [ + "sg-903004f8" + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified security group.", + "id": "to-describe-a-security-group-1529354426314", + "title": "To describe a security group" + }, + { + "input": { + "Filters": [ + { + "Name": "tag:Purpose", + "Values": [ + "test" + ] + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the security groups that include the specified tag (Purpose=test).", + "id": "to-describe-a-tagged-security-group-1529354553880", + "title": "To describe a tagged security group" + } + ], + "DescribeSnapshotAttribute": [ + { + "input": { + "Attribute": "createVolumePermission", + "SnapshotId": "snap-066877671789bd71b" + }, + "output": { + "CreateVolumePermissions": [ + + ], + "SnapshotId": "snap-066877671789bd71b" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the ``createVolumePermission`` attribute on a snapshot with the snapshot ID of ``snap-066877671789bd71b``.", + "id": "to-describe-snapshot-attributes-1472503199736", + "title": "To describe snapshot attributes" + } + ], + "DescribeSnapshots": [ + { + "input": { + "SnapshotIds": [ + "snap-1234567890abcdef0" + ] + }, + "output": { + "NextToken": "", + "Snapshots": [ + { + "Description": "This is my snapshot.", + "OwnerId": "012345678910", + "Progress": "100%", + "SnapshotId": "snap-1234567890abcdef0", + "StartTime": "2014-02-28T21:28:32.000Z", + "State": "completed", + "VolumeId": "vol-049df61146c4d7901", + "VolumeSize": 8 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes a snapshot with the snapshot ID of ``snap-1234567890abcdef0``.", + "id": "to-describe-a-snapshot-1472503807850", + "title": "To describe a snapshot" + }, + { + "input": { + "Filters": [ + { + "Name": "status", + "Values": [ + "pending" + ] + } + ], + "OwnerIds": [ + "012345678910" + ] + }, + "output": { + "NextToken": "", + "Snapshots": [ + { + "Description": "This is my copied snapshot.", + "OwnerId": "012345678910", + "Progress": "87%", + "SnapshotId": "snap-066877671789bd71b", + "StartTime": "2014-02-28T21:37:27.000Z", + "State": "pending", + "VolumeId": "vol-1234567890abcdef0", + "VolumeSize": 8 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all snapshots owned by the ID 012345678910 that are in the ``pending`` status.", + "id": "to-describe-snapshots-using-filters-1472503929793", + "title": "To describe snapshots using filters" + } + ], + "DescribeSpotDatafeedSubscription": [ + { + "output": { + "SpotDatafeedSubscription": { + "Bucket": "my-s3-bucket", + "OwnerId": "123456789012", + "Prefix": "spotdata", + "State": "Active" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the Spot Instance datafeed subscription for your AWS account.", + "id": "ec2-describe-spot-datafeed-subscription-1", + "title": "To describe the datafeed for your AWS account" + } + ], + "DescribeSpotFleetInstances": [ + { + "input": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "output": { + "ActiveInstances": [ + { + "InstanceId": "i-1234567890abcdef0", + "InstanceType": "m3.medium", + "SpotInstanceRequestId": "sir-08b93456" + } + ], + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists the Spot Instances associated with the specified Spot fleet.", + "id": "ec2-describe-spot-fleet-instances-1", + "title": "To describe the Spot Instances associated with a Spot fleet" + } + ], + "DescribeSpotFleetRequestHistory": [ + { + "input": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "StartTime": "2015-05-26T00:00:00Z" + }, + "output": { + "HistoryRecords": [ + { + "EventInformation": { + "EventSubType": "submitted" + }, + "EventType": "fleetRequestChange", + "Timestamp": "2015-05-26T23:17:20.697Z" + }, + { + "EventInformation": { + "EventSubType": "active" + }, + "EventType": "fleetRequestChange", + "Timestamp": "2015-05-26T23:17:20.873Z" + }, + { + "EventInformation": { + "EventSubType": "launched", + "InstanceId": "i-1234567890abcdef0" + }, + "EventType": "instanceChange", + "Timestamp": "2015-05-26T23:21:21.712Z" + }, + { + "EventInformation": { + "EventSubType": "launched", + "InstanceId": "i-1234567890abcdef1" + }, + "EventType": "instanceChange", + "Timestamp": "2015-05-26T23:21:21.816Z" + } + ], + "NextToken": "CpHNsscimcV5oH7bSbub03CI2Qms5+ypNpNm+53MNlR0YcXAkp0xFlfKf91yVxSExmbtma3awYxMFzNA663ZskT0AHtJ6TCb2Z8bQC2EnZgyELbymtWPfpZ1ZbauVg+P+TfGlWxWWB/Vr5dk5d4LfdgA/DRAHUrYgxzrEXAMPLE=", + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "StartTime": "2015-05-26T00:00:00Z" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example returns the history for the specified Spot fleet starting at the specified time.", + "id": "ec2-describe-spot-fleet-request-history-1", + "title": "To describe Spot fleet history" + } + ], + "DescribeSpotFleetRequests": [ + { + "input": { + "SpotFleetRequestIds": [ + "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + ] + }, + "output": { + "SpotFleetRequestConfigs": [ + { + "SpotFleetRequestConfig": { + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "EbsOptimized": false, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "cc2.8xlarge", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeleteOnTermination": false, + "DeviceIndex": 0, + "SecondaryPrivateIpAddressCount": 0, + "SubnetId": "subnet-a61dafcf" + } + ] + }, + { + "EbsOptimized": false, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "r3.8xlarge", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeleteOnTermination": false, + "DeviceIndex": 0, + "SecondaryPrivateIpAddressCount": 0, + "SubnetId": "subnet-a61dafcf" + } + ] + } + ], + "SpotPrice": "0.05", + "TargetCapacity": 20 + }, + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "SpotFleetRequestState": "active" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified Spot fleet request.", + "id": "ec2-describe-spot-fleet-requests-1", + "title": "To describe a Spot fleet request" + } + ], + "DescribeSpotInstanceRequests": [ + { + "input": { + "SpotInstanceRequestIds": [ + "sir-08b93456" + ] + }, + "output": { + "SpotInstanceRequests": [ + { + "CreateTime": "2014-04-30T18:14:55.000Z", + "InstanceId": "i-1234567890abcdef0", + "LaunchSpecification": { + "BlockDeviceMappings": [ + { + "DeviceName": "/dev/sda1", + "Ebs": { + "DeleteOnTermination": true, + "VolumeSize": 8, + "VolumeType": "standard" + } + } + ], + "EbsOptimized": false, + "ImageId": "ami-7aba833f", + "InstanceType": "m1.small", + "KeyName": "my-key-pair", + "SecurityGroups": [ + { + "GroupId": "sg-e38f24a7", + "GroupName": "my-security-group" + } + ] + }, + "LaunchedAvailabilityZone": "us-west-1b", + "ProductDescription": "Linux/UNIX", + "SpotInstanceRequestId": "sir-08b93456", + "SpotPrice": "0.010000", + "State": "active", + "Status": { + "Code": "fulfilled", + "Message": "Your Spot request is fulfilled.", + "UpdateTime": "2014-04-30T18:16:21.000Z" + }, + "Type": "one-time" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified Spot Instance request.", + "id": "ec2-describe-spot-instance-requests-1", + "title": "To describe a Spot Instance request" + } + ], + "DescribeSpotPriceHistory": [ + { + "input": { + "EndTime": "2014-01-06T08:09:10", + "InstanceTypes": [ + "m1.xlarge" + ], + "ProductDescriptions": [ + "Linux/UNIX (Amazon VPC)" + ], + "StartTime": "2014-01-06T07:08:09" + }, + "output": { + "SpotPriceHistory": [ + { + "AvailabilityZone": "us-west-1a", + "InstanceType": "m1.xlarge", + "ProductDescription": "Linux/UNIX (Amazon VPC)", + "SpotPrice": "0.080000", + "Timestamp": "2014-01-06T04:32:53.000Z" + }, + { + "AvailabilityZone": "us-west-1c", + "InstanceType": "m1.xlarge", + "ProductDescription": "Linux/UNIX (Amazon VPC)", + "SpotPrice": "0.080000", + "Timestamp": "2014-01-05T11:28:26.000Z" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example returns the Spot Price history for m1.xlarge, Linux/UNIX (Amazon VPC) instances for a particular day in January.", + "id": "ec2-describe-spot-price-history-1", + "title": "To describe Spot price history for Linux/UNIX (Amazon VPC)" + } + ], + "DescribeSubnets": [ + { + "input": { + "Filters": [ + { + "Name": "vpc-id", + "Values": [ + "vpc-a01106c2" + ] + } + ] + }, + "output": { + "Subnets": [ + { + "AvailabilityZone": "us-east-1c", + "AvailableIpAddressCount": 251, + "CidrBlock": "10.0.1.0/24", + "DefaultForAz": false, + "MapPublicIpOnLaunch": false, + "State": "available", + "SubnetId": "subnet-9d4a7b6c", + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the subnets for the specified VPC.", + "id": "ec2-describe-subnets-1", + "title": "To describe the subnets for a VPC" + } + ], + "DescribeTags": [ + { + "input": { + "Filters": [ + { + "Name": "resource-id", + "Values": [ + "i-1234567890abcdef8" + ] + } + ] + }, + "output": { + "Tags": [ + { + "Key": "Stack", + "ResourceId": "i-1234567890abcdef8", + "ResourceType": "instance", + "Value": "test" + }, + { + "Key": "Name", + "ResourceId": "i-1234567890abcdef8", + "ResourceType": "instance", + "Value": "Beta Server" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the tags for the specified instance.", + "id": "ec2-describe-tags-1", + "title": "To describe the tags for a single resource" + } + ], + "DescribeVolumeAttribute": [ + { + "input": { + "Attribute": "autoEnableIO", + "VolumeId": "vol-049df61146c4d7901" + }, + "output": { + "AutoEnableIO": { + "Value": false + }, + "VolumeId": "vol-049df61146c4d7901" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the ``autoEnableIo`` attribute of the volume with the ID ``vol-049df61146c4d7901``.", + "id": "to-describe-a-volume-attribute-1472505773492", + "title": "To describe a volume attribute" + } + ], + "DescribeVolumeStatus": [ + { + "input": { + "VolumeIds": [ + "vol-1234567890abcdef0" + ] + }, + "output": { + "VolumeStatuses": [ + { + "Actions": [ + + ], + "AvailabilityZone": "us-east-1a", + "Events": [ + + ], + "VolumeId": "vol-1234567890abcdef0", + "VolumeStatus": { + "Details": [ + { + "Name": "io-enabled", + "Status": "passed" + }, + { + "Name": "io-performance", + "Status": "not-applicable" + } + ], + "Status": "ok" + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the status for the volume ``vol-1234567890abcdef0``.", + "id": "to-describe-the-status-of-a-single-volume-1472507016193", + "title": "To describe the status of a single volume" + }, + { + "input": { + "Filters": [ + { + "Name": "volume-status.status", + "Values": [ + "impaired" + ] + } + ] + }, + "output": { + "VolumeStatuses": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the status for all volumes that are impaired. In this example output, there are no impaired volumes.", + "id": "to-describe-the-status-of-impaired-volumes-1472507239821", + "title": "To describe the status of impaired volumes" + } + ], + "DescribeVolumes": [ + { + "input": { + }, + "output": { + "NextToken": "", + "Volumes": [ + { + "Attachments": [ + { + "AttachTime": "2013-12-18T22:35:00.000Z", + "DeleteOnTermination": true, + "Device": "/dev/sda1", + "InstanceId": "i-1234567890abcdef0", + "State": "attached", + "VolumeId": "vol-049df61146c4d7901" + } + ], + "AvailabilityZone": "us-east-1a", + "CreateTime": "2013-12-18T22:35:00.084Z", + "Size": 8, + "SnapshotId": "snap-1234567890abcdef0", + "State": "in-use", + "VolumeId": "vol-049df61146c4d7901", + "VolumeType": "standard" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all of your volumes in the default region.", + "id": "to-describe-all-volumes-1472506358883", + "title": "To describe all volumes" + }, + { + "input": { + "Filters": [ + { + "Name": "attachment.instance-id", + "Values": [ + "i-1234567890abcdef0" + ] + }, + { + "Name": "attachment.delete-on-termination", + "Values": [ + "true" + ] + } + ] + }, + "output": { + "Volumes": [ + { + "Attachments": [ + { + "AttachTime": "2013-12-18T22:35:00.000Z", + "DeleteOnTermination": true, + "Device": "/dev/sda1", + "InstanceId": "i-1234567890abcdef0", + "State": "attached", + "VolumeId": "vol-049df61146c4d7901" + } + ], + "AvailabilityZone": "us-east-1a", + "CreateTime": "2013-12-18T22:35:00.084Z", + "Size": 8, + "SnapshotId": "snap-1234567890abcdef0", + "State": "in-use", + "VolumeId": "vol-049df61146c4d7901", + "VolumeType": "standard" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes all volumes that are both attached to the instance with the ID i-1234567890abcdef0 and set to delete when the instance terminates.", + "id": "to-describe-volumes-that-are-attached-to-a-specific-instance-1472506613578", + "title": "To describe volumes that are attached to a specific instance" + } + ], + "DescribeVpcAttribute": [ + { + "input": { + "Attribute": "enableDnsSupport", + "VpcId": "vpc-a01106c2" + }, + "output": { + "EnableDnsSupport": { + "Value": true + }, + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the enableDnsSupport attribute. This attribute indicates whether DNS resolution is enabled for the VPC. If this attribute is true, the Amazon DNS server resolves DNS hostnames for your instances to their corresponding IP addresses; otherwise, it does not.", + "id": "ec2-describe-vpc-attribute-1", + "title": "To describe the enableDnsSupport attribute" + }, + { + "input": { + "Attribute": "enableDnsHostnames", + "VpcId": "vpc-a01106c2" + }, + "output": { + "EnableDnsHostnames": { + "Value": true + }, + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the enableDnsHostnames attribute. This attribute indicates whether the instances launched in the VPC get DNS hostnames. If this attribute is true, instances in the VPC get DNS hostnames; otherwise, they do not.", + "id": "ec2-describe-vpc-attribute-2", + "title": "To describe the enableDnsHostnames attribute" + } + ], + "DescribeVpcs": [ + { + "input": { + "VpcIds": [ + "vpc-a01106c2" + ] + }, + "output": { + "Vpcs": [ + { + "CidrBlock": "10.0.0.0/16", + "DhcpOptionsId": "dopt-7a8b9c2d", + "InstanceTenancy": "default", + "IsDefault": false, + "State": "available", + "Tags": [ + { + "Key": "Name", + "Value": "MyVPC" + } + ], + "VpcId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified VPC.", + "id": "ec2-describe-vpcs-1", + "title": "To describe a VPC" + } + ], + "DetachInternetGateway": [ + { + "input": { + "InternetGatewayId": "igw-c0a643a9", + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the specified Internet gateway from the specified VPC.", + "id": "ec2-detach-internet-gateway-1", + "title": "To detach an Internet gateway from a VPC" + } + ], + "DetachNetworkInterface": [ + { + "input": { + "AttachmentId": "eni-attach-66c4350a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the specified network interface from its attached instance.", + "id": "ec2-detach-network-interface-1", + "title": "To detach a network interface from an instance" + } + ], + "DetachVolume": [ + { + "input": { + "VolumeId": "vol-1234567890abcdef0" + }, + "output": { + "AttachTime": "2014-02-27T19:23:06.000Z", + "Device": "/dev/sdb", + "InstanceId": "i-1234567890abcdef0", + "State": "detaching", + "VolumeId": "vol-049df61146c4d7901" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the volume (``vol-049df61146c4d7901``) from the instance it is attached to.", + "id": "to-detach-a-volume-from-an-instance-1472507977694", + "title": "To detach a volume from an instance" + } + ], + "DisableVgwRoutePropagation": [ + { + "input": { + "GatewayId": "vgw-9a4cacf3", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disables the specified virtual private gateway from propagating static routes to the specified route table.", + "id": "ec2-disable-vgw-route-propagation-1", + "title": "To disable route propagation" + } + ], + "DisassociateAddress": [ + { + "input": { + "AssociationId": "eipassoc-2bebb745" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disassociates an Elastic IP address from an instance in a VPC.", + "id": "ec2-disassociate-address-1", + "title": "To disassociate an Elastic IP address in EC2-VPC" + }, + { + "input": { + "PublicIp": "198.51.100.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disassociates an Elastic IP address from an instance in EC2-Classic.", + "id": "ec2-disassociate-address-2", + "title": "To disassociate an Elastic IP addresses in EC2-Classic" + } + ], + "DisassociateIamInstanceProfile": [ + { + "input": { + "AssociationId": "iip-assoc-05020b59952902f5f" + }, + "output": { + "IamInstanceProfileAssociation": { + "AssociationId": "iip-assoc-05020b59952902f5f", + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/admin-role", + "Id": "AIPAI5IVIHMFFYY2DKV5Y" + }, + "InstanceId": "i-123456789abcde123", + "State": "disassociating" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disassociates the specified IAM instance profile from an instance.", + "id": "to-disassociate-an-iam-instance-profile-1529355364478", + "title": "To disassociate an IAM instance profile" + } + ], + "DisassociateRouteTable": [ + { + "input": { + "AssociationId": "rtbassoc-781d0d1a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example disassociates the specified route table from its associated subnet.", + "id": "ec2-disassociate-route-table-1", + "title": "To disassociate a route table" + } + ], + "EnableVgwRoutePropagation": [ + { + "input": { + "GatewayId": "vgw-9a4cacf3", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables the specified virtual private gateway to propagate static routes to the specified route table.", + "id": "ec2-enable-vgw-route-propagation-1", + "title": "To enable route propagation" + } + ], + "EnableVolumeIO": [ + { + "input": { + "VolumeId": "vol-1234567890abcdef0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables I/O on volume ``vol-1234567890abcdef0``.", + "id": "to-enable-io-for-a-volume-1472508114867", + "title": "To enable I/O for a volume" + } + ], + "GetConsoleOutput": [ + { + "input": { + "InstanceId": "i-1234567890abcdef0" + }, + "output": { + "InstanceId": "i-1234567890abcdef0", + "Output": "...", + "Timestamp": "2018-05-25T21:23:53.000Z" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example gets the console output for the specified instance.", + "id": "to-get-the-console-output-1529355683194", + "title": "To get the console output" + } + ], + "GetLaunchTemplateData": [ + { + "input": { + "InstanceId": "0123d646e8048babc" + }, + "output": { + "LaunchTemplateData": { + "BlockDeviceMappings": [ + { + "DeviceName": "/dev/xvda", + "Ebs": { + "DeleteOnTermination": true, + "Encrypted": false, + "Iops": 100, + "SnapshotId": "snap-02594938353ef77d3", + "VolumeSize": 8, + "VolumeType": "gp2" + } + } + ], + "EbsOptimized": false, + "ImageId": "ami-32cf7b4a", + "InstanceType": "t2.medium", + "KeyName": "my-key-pair", + "Monitoring": { + "Enabled": false + }, + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": false, + "DeleteOnTermination": true, + "Description": "", + "DeviceIndex": 0, + "Groups": [ + "sg-d14e1bb4" + ], + "Ipv6Addresses": [ + + ], + "NetworkInterfaceId": "eni-4338b5a9", + "PrivateIpAddress": "10.0.3.233", + "PrivateIpAddresses": [ + { + "Primary": true, + "PrivateIpAddress": "10.0.3.233" + } + ], + "SubnetId": "subnet-5264e837" + } + ], + "Placement": { + "AvailabilityZone": "us-east-2b", + "GroupName": "", + "Tenancy": "default" + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example gets the launch template data for the specified instance.", + "id": "to-get-the-launch-template-data-for-an-instance--1529356515702", + "title": "To get the launch template data for an instance " + } + ], + "ModifyImageAttribute": [ + { + "input": { + "ImageId": "ami-5731123e", + "LaunchPermission": { + "Add": [ + { + "Group": "all" + } + ] + } + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example makes the specified AMI public.", + "id": "to-make-an-ami-public-1529357395278", + "title": "To make an AMI public" + }, + { + "input": { + "ImageId": "ami-5731123e", + "LaunchPermission": { + "Add": [ + { + "UserId": "123456789012" + } + ] + } + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example grants launch permissions for the specified AMI to the specified AWS account.", + "id": "to-grant-launch-permissions-1529357727906", + "title": "To grant launch permissions" + } + ], + "ModifyInstanceAttribute": [ + { + "input": { + "InstanceId": "i-1234567890abcdef0", + "InstanceType": { + "Value": "m5.large" + } + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the instance type of the specified stopped instance.", + "id": "to-modify-the-instance-type-1529357844378", + "title": "To modify the instance type" + }, + { + "input": { + "EnaSupport": { + "Value": true + }, + "InstanceId": "i-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables enhanced networking for the specified stopped instance.", + "id": "to-enable-enhanced-networking-1529358279870", + "title": "To enable enhanced networking" + } + ], + "ModifyLaunchTemplate": [ + { + "input": { + "DefaultVersion": "2", + "LaunchTemplateId": "lt-0abcd290751193123" + }, + "output": { + "LaunchTemplate": { + "CreateTime": "2017-12-01T13:35:46.000Z", + "CreatedBy": "arn:aws:iam::123456789012:root", + "DefaultVersionNumber": 2, + "LatestVersionNumber": 2, + "LaunchTemplateId": "lt-0abcd290751193123", + "LaunchTemplateName": "WebServers" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example specifies version 2 as the default version of the specified launch template.", + "id": "to-change-the-default-version-of-a-launch-template-1529358440364", + "title": "To change the default version of a launch template" + } + ], + "ModifyNetworkInterfaceAttribute": [ + { + "input": { + "Attachment": { + "AttachmentId": "eni-attach-43348162", + "DeleteOnTermination": false + }, + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the attachment attribute of the specified network interface.", + "id": "ec2-modify-network-interface-attribute-1", + "title": "To modify the attachment attribute of a network interface" + }, + { + "input": { + "Description": { + "Value": "My description" + }, + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the description attribute of the specified network interface.", + "id": "ec2-modify-network-interface-attribute-2", + "title": "To modify the description attribute of a network interface" + }, + { + "input": { + "Groups": [ + "sg-903004f8", + "sg-1a2b3c4d" + ], + "NetworkInterfaceId": "eni-686ea200" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example command modifies the groupSet attribute of the specified network interface.", + "id": "ec2-modify-network-interface-attribute-3", + "title": "To modify the groupSet attribute of a network interface" + }, + { + "input": { + "NetworkInterfaceId": "eni-686ea200", + "SourceDestCheck": { + "Value": false + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example command modifies the sourceDestCheck attribute of the specified network interface.", + "id": "ec2-modify-network-interface-attribute-4", + "title": "To modify the sourceDestCheck attribute of a network interface" + } + ], + "ModifySnapshotAttribute": [ + { + "input": { + "Attribute": "createVolumePermission", + "OperationType": "remove", + "SnapshotId": "snap-1234567890abcdef0", + "UserIds": [ + "123456789012" + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies snapshot ``snap-1234567890abcdef0`` to remove the create volume permission for a user with the account ID ``123456789012``. If the command succeeds, no output is returned.", + "id": "to-modify-a-snapshot-attribute-1472508385907", + "title": "To modify a snapshot attribute" + }, + { + "input": { + "Attribute": "createVolumePermission", + "GroupNames": [ + "all" + ], + "OperationType": "add", + "SnapshotId": "snap-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example makes the snapshot ``snap-1234567890abcdef0`` public.", + "id": "to-make-a-snapshot-public-1472508470529", + "title": "To make a snapshot public" + } + ], + "ModifySpotFleetRequest": [ + { + "input": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "TargetCapacity": 20 + }, + "output": { + "Return": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example increases the target capacity of the specified Spot fleet request.", + "id": "ec2-modify-spot-fleet-request-1", + "title": "To increase the target capacity of a Spot fleet request" + }, + { + "input": { + "ExcessCapacityTerminationPolicy": "NoTermination ", + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE", + "TargetCapacity": 10 + }, + "output": { + "Return": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example decreases the target capacity of the specified Spot fleet request without terminating any Spot Instances as a result.", + "id": "ec2-modify-spot-fleet-request-2", + "title": "To decrease the target capacity of a Spot fleet request" + } + ], + "ModifySubnetAttribute": [ + { + "input": { + "MapPublicIpOnLaunch": { + "Value": true + }, + "SubnetId": "subnet-1a2b3c4d" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the specified subnet so that all instances launched into this subnet are assigned a public IP address.", + "id": "ec2-modify-subnet-attribute-1", + "title": "To change a subnet's public IP addressing behavior" + } + ], + "ModifyVolumeAttribute": [ + { + "input": { + "AutoEnableIO": { + "Value": true + }, + "DryRun": true, + "VolumeId": "vol-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example sets the ``autoEnableIo`` attribute of the volume with the ID ``vol-1234567890abcdef0`` to ``true``. If the command succeeds, no output is returned.", + "id": "to-modify-a-volume-attribute-1472508596749", + "title": "To modify a volume attribute" + } + ], + "ModifyVpcAttribute": [ + { + "input": { + "EnableDnsSupport": { + "Value": false + }, + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the enableDnsSupport attribute. This attribute indicates whether DNS resolution is enabled for the VPC. If this attribute is true, the Amazon DNS server resolves DNS hostnames for instances in the VPC to their corresponding IP addresses; otherwise, it does not.", + "id": "ec2-modify-vpc-attribute-1", + "title": "To modify the enableDnsSupport attribute" + }, + { + "input": { + "EnableDnsHostnames": { + "Value": false + }, + "VpcId": "vpc-a01106c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the enableDnsHostnames attribute. This attribute indicates whether instances launched in the VPC get DNS hostnames. If this attribute is true, instances in the VPC get DNS hostnames; otherwise, they do not.", + "id": "ec2-modify-vpc-attribute-2", + "title": "To modify the enableDnsHostnames attribute" + } + ], + "MoveAddressToVpc": [ + { + "input": { + "PublicIp": "54.123.4.56" + }, + "output": { + "Status": "MoveInProgress" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example moves the specified Elastic IP address to the EC2-VPC platform.", + "id": "ec2-move-address-to-vpc-1", + "title": "To move an address to EC2-VPC" + } + ], + "PurchaseScheduledInstances": [ + { + "input": { + "PurchaseRequests": [ + { + "InstanceCount": 1, + "PurchaseToken": "eyJ2IjoiMSIsInMiOjEsImMiOi..." + } + ] + }, + "output": { + "ScheduledInstanceSet": [ + { + "AvailabilityZone": "us-west-2b", + "CreateDate": "2016-01-25T21:43:38.612Z", + "HourlyPrice": "0.095", + "InstanceCount": 1, + "InstanceType": "c4.large", + "NetworkPlatform": "EC2-VPC", + "NextSlotStartTime": "2016-01-31T09:00:00Z", + "Platform": "Linux/UNIX", + "Recurrence": { + "Frequency": "Weekly", + "Interval": 1, + "OccurrenceDaySet": [ + 1 + ], + "OccurrenceRelativeToEnd": false, + "OccurrenceUnit": "" + }, + "ScheduledInstanceId": "sci-1234-1234-1234-1234-123456789012", + "SlotDurationInHours": 32, + "TermEndDate": "2017-01-31T09:00:00Z", + "TermStartDate": "2016-01-31T09:00:00Z", + "TotalScheduledInstanceHours": 1696 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example purchases a Scheduled Instance.", + "id": "ec2-purchase-scheduled-instances-1", + "title": "To purchase a Scheduled Instance" + } + ], + "RebootInstances": [ + { + "input": { + "InstanceIds": [ + "i-1234567890abcdef5" + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example reboots the specified EC2 instance.", + "id": "to-reboot-an-ec2-instance-1529358566382", + "title": "To reboot an EC2 instance" + } + ], + "ReleaseAddress": [ + { + "input": { + "AllocationId": "eipalloc-64d5890a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example releases an Elastic IP address for use with instances in a VPC.", + "id": "ec2-release-address-1", + "title": "To release an Elastic IP address for EC2-VPC" + }, + { + "input": { + "PublicIp": "198.51.100.0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example releases an Elastic IP address for use with instances in EC2-Classic.", + "id": "ec2-release-address-2", + "title": "To release an Elastic IP addresses for EC2-Classic" + } + ], + "ReplaceNetworkAclAssociation": [ + { + "input": { + "AssociationId": "aclassoc-e5b95c8c", + "NetworkAclId": "acl-5fb85d36" + }, + "output": { + "NewAssociationId": "aclassoc-3999875b" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified network ACL with the subnet for the specified network ACL association.", + "id": "ec2-replace-network-acl-association-1", + "title": "To replace the network ACL associated with a subnet" + } + ], + "ReplaceNetworkAclEntry": [ + { + "input": { + "CidrBlock": "203.0.113.12/24", + "Egress": false, + "NetworkAclId": "acl-5fb85d36", + "PortRange": { + "From": 53, + "To": 53 + }, + "Protocol": "17", + "RuleAction": "allow", + "RuleNumber": 100 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example replaces an entry for the specified network ACL. The new rule 100 allows ingress traffic from 203.0.113.12/24 on UDP port 53 (DNS) into any associated subnet.", + "id": "ec2-replace-network-acl-entry-1", + "title": "To replace a network ACL entry" + } + ], + "ReplaceRoute": [ + { + "input": { + "DestinationCidrBlock": "10.0.0.0/16", + "GatewayId": "vgw-9a4cacf3", + "RouteTableId": "rtb-22574640" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example replaces the specified route in the specified table table. The new route matches the specified CIDR and sends the traffic to the specified virtual private gateway.", + "id": "ec2-replace-route-1", + "title": "To replace a route" + } + ], + "ReplaceRouteTableAssociation": [ + { + "input": { + "AssociationId": "rtbassoc-781d0d1a", + "RouteTableId": "rtb-22574640" + }, + "output": { + "NewAssociationId": "rtbassoc-3a1f0f58" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified route table with the subnet for the specified route table association.", + "id": "ec2-replace-route-table-association-1", + "title": "To replace the route table associated with a subnet" + } + ], + "RequestSpotFleet": [ + { + "input": { + "SpotFleetRequestConfig": { + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "KeyName": "my-key-pair", + "SecurityGroups": [ + { + "GroupId": "sg-1a2b3c4d" + } + ], + "SubnetId": "subnet-1a2b3c4d, subnet-3c4d5e6f" + } + ], + "SpotPrice": "0.04", + "TargetCapacity": 2 + } + }, + "output": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a Spot fleet request with two launch specifications that differ only by subnet. The Spot fleet launches the instances in the specified subnet with the lowest price. If the instances are launched in a default VPC, they receive a public IP address by default. If the instances are launched in a nondefault VPC, they do not receive a public IP address by default. Note that you can't specify different subnets from the same Availability Zone in a Spot fleet request.", + "id": "ec2-request-spot-fleet-1", + "title": "To request a Spot fleet in the subnet with the lowest price" + }, + { + "input": { + "SpotFleetRequestConfig": { + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "KeyName": "my-key-pair", + "Placement": { + "AvailabilityZone": "us-west-2a, us-west-2b" + }, + "SecurityGroups": [ + { + "GroupId": "sg-1a2b3c4d" + } + ] + } + ], + "SpotPrice": "0.04", + "TargetCapacity": 2 + } + }, + "output": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a Spot fleet request with two launch specifications that differ only by Availability Zone. The Spot fleet launches the instances in the specified Availability Zone with the lowest price. If your account supports EC2-VPC only, Amazon EC2 launches the Spot instances in the default subnet of the Availability Zone. If your account supports EC2-Classic, Amazon EC2 launches the instances in EC2-Classic in the Availability Zone.", + "id": "ec2-request-spot-fleet-2", + "title": "To request a Spot fleet in the Availability Zone with the lowest price" + }, + { + "input": { + "SpotFleetRequestConfig": { + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::880185128111:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "KeyName": "my-key-pair", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeviceIndex": 0, + "Groups": [ + "sg-1a2b3c4d" + ], + "SubnetId": "subnet-1a2b3c4d" + } + ] + } + ], + "SpotPrice": "0.04", + "TargetCapacity": 2 + } + }, + "output": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example assigns public addresses to instances launched in a nondefault VPC. Note that when you specify a network interface, you must include the subnet ID and security group ID using the network interface.", + "id": "ec2-request-spot-fleet-3", + "title": "To launch Spot instances in a subnet and assign them public IP addresses" + }, + { + "input": { + "SpotFleetRequestConfig": { + "AllocationStrategy": "diversified", + "IamFleetRole": "arn:aws:iam::123456789012:role/my-spot-fleet-role", + "LaunchSpecifications": [ + { + "ImageId": "ami-1a2b3c4d", + "InstanceType": "c4.2xlarge", + "SubnetId": "subnet-1a2b3c4d" + }, + { + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.2xlarge", + "SubnetId": "subnet-1a2b3c4d" + }, + { + "ImageId": "ami-1a2b3c4d", + "InstanceType": "r3.2xlarge", + "SubnetId": "subnet-1a2b3c4d" + } + ], + "SpotPrice": "0.70", + "TargetCapacity": 30 + } + }, + "output": { + "SpotFleetRequestId": "sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a Spot fleet request that launches 30 instances using the diversified allocation strategy. The launch specifications differ by instance type. The Spot fleet distributes the instances across the launch specifications such that there are 10 instances of each type.", + "id": "ec2-request-spot-fleet-4", + "title": "To request a Spot fleet using the diversified allocation strategy" + } + ], + "RequestSpotInstances": [ + { + "input": { + "InstanceCount": 5, + "LaunchSpecification": { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "KeyName": "my-key-pair", + "Placement": { + "AvailabilityZone": "us-west-2a" + }, + "SecurityGroupIds": [ + "sg-1a2b3c4d" + ] + }, + "SpotPrice": "0.03", + "Type": "one-time" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a one-time Spot Instance request for five instances in the specified Availability Zone. If your account supports EC2-VPC only, Amazon EC2 launches the instances in the default subnet of the specified Availability Zone. If your account supports EC2-Classic, Amazon EC2 launches the instances in EC2-Classic in the specified Availability Zone.", + "id": "ec2-request-spot-instances-1", + "title": "To create a one-time Spot Instance request" + }, + { + "input": { + "InstanceCount": 5, + "LaunchSpecification": { + "IamInstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/my-iam-role" + }, + "ImageId": "ami-1a2b3c4d", + "InstanceType": "m3.medium", + "SecurityGroupIds": [ + "sg-1a2b3c4d" + ], + "SubnetId": "subnet-1a2b3c4d" + }, + "SpotPrice": "0.050", + "Type": "one-time" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example command creates a one-time Spot Instance request for five instances in the specified subnet. Amazon EC2 launches the instances in the specified subnet. If the VPC is a nondefault VPC, the instances do not receive a public IP address by default.", + "id": "ec2-request-spot-instances-2", + "title": "To create a one-time Spot Instance request" + } + ], + "ResetImageAttribute": [ + { + "input": { + "Attribute": "launchPermission", + "ImageId": "ami-5731123e" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example resets the launchPermission attribute for the specified AMI. By default, AMIs are private.", + "id": "to-reset-the-launchpermission-attribute-1529359519534", + "title": "To reset the launchPermission attribute" + } + ], + "ResetInstanceAttribute": [ + { + "input": { + "Attribute": "sourceDestCheck", + "InstanceId": "i-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example resets the sourceDestCheck attribute for the specified instance.", + "id": "to-reset-the-sourcedestcheck-attribute-1529359630708", + "title": "To reset the sourceDestCheck attribute" + } + ], + "ResetSnapshotAttribute": [ + { + "input": { + "Attribute": "createVolumePermission", + "SnapshotId": "snap-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example resets the create volume permissions for snapshot ``snap-1234567890abcdef0``. If the command succeeds, no output is returned.", + "id": "to-reset-a-snapshot-attribute-1472508825735", + "title": "To reset a snapshot attribute" + } + ], + "RestoreAddressToClassic": [ + { + "input": { + "PublicIp": "198.51.100.0" + }, + "output": { + "PublicIp": "198.51.100.0", + "Status": "MoveInProgress" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example restores the specified Elastic IP address to the EC2-Classic platform.", + "id": "ec2-restore-address-to-classic-1", + "title": "To restore an address to EC2-Classic" + } + ], + "RunInstances": [ + { + "input": { + "BlockDeviceMappings": [ + { + "DeviceName": "/dev/sdh", + "Ebs": { + "VolumeSize": 100 + } + } + ], + "ImageId": "ami-abc12345", + "InstanceType": "t2.micro", + "KeyName": "my-key-pair", + "MaxCount": 1, + "MinCount": 1, + "SecurityGroupIds": [ + "sg-1a2b3c4d" + ], + "SubnetId": "subnet-6e7f829e", + "TagSpecifications": [ + { + "ResourceType": "instance", + "Tags": [ + { + "Key": "Purpose", + "Value": "test" + } + ] + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example launches an instance using the specified AMI, instance type, security group, subnet, block device mapping, and tags.", + "id": "to-launch-an-instance-1529360150806", + "title": "To launch an instance" + } + ], + "RunScheduledInstances": [ + { + "input": { + "InstanceCount": 1, + "LaunchSpecification": { + "IamInstanceProfile": { + "Name": "my-iam-role" + }, + "ImageId": "ami-12345678", + "InstanceType": "c4.large", + "KeyName": "my-key-pair", + "NetworkInterfaces": [ + { + "AssociatePublicIpAddress": true, + "DeviceIndex": 0, + "Groups": [ + "sg-12345678" + ], + "SubnetId": "subnet-12345678" + } + ] + }, + "ScheduledInstanceId": "sci-1234-1234-1234-1234-123456789012" + }, + "output": { + "InstanceIdSet": [ + "i-1234567890abcdef0" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example launches the specified Scheduled Instance in a VPC.", + "id": "ec2-run-scheduled-instances-1", + "title": "To launch a Scheduled Instance in a VPC" + }, + { + "input": { + "InstanceCount": 1, + "LaunchSpecification": { + "IamInstanceProfile": { + "Name": "my-iam-role" + }, + "ImageId": "ami-12345678", + "InstanceType": "c4.large", + "KeyName": "my-key-pair", + "Placement": { + "AvailabilityZone": "us-west-2b" + }, + "SecurityGroupIds": [ + "sg-12345678" + ] + }, + "ScheduledInstanceId": "sci-1234-1234-1234-1234-123456789012" + }, + "output": { + "InstanceIdSet": [ + "i-1234567890abcdef0" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example launches the specified Scheduled Instance in EC2-Classic.", + "id": "ec2-run-scheduled-instances-2", + "title": "To launch a Scheduled Instance in EC2-Classic" + } + ], + "StartInstances": [ + { + "input": { + "InstanceIds": [ + "i-1234567890abcdef0" + ] + }, + "output": { + "StartingInstances": [ + { + "CurrentState": { + "Code": 0, + "Name": "pending" + }, + "InstanceId": "i-1234567890abcdef0", + "PreviousState": { + "Code": 80, + "Name": "stopped" + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example starts the specified EC2 instance.", + "id": "to-start-a-stopped-ec2-instance-1529358792730", + "title": "To start a stopped EC2 instance" + } + ], + "StopInstances": [ + { + "input": { + "InstanceIds": [ + "i-1234567890abcdef0" + ] + }, + "output": { + "StoppingInstances": [ + { + "CurrentState": { + "Code": 64, + "Name": "stopping" + }, + "InstanceId": "i-1234567890abcdef0", + "PreviousState": { + "Code": 16, + "Name": "running" + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example stops the specified EC2 instance.", + "id": "to-stop-a-running-ec2-instance-1529358905540", + "title": "To stop a running EC2 instance" + } + ], + "TerminateInstances": [ + { + "input": { + "InstanceIds": [ + "i-1234567890abcdef0" + ] + }, + "output": { + "TerminatingInstances": [ + { + "CurrentState": { + "Code": 32, + "Name": "shutting-down" + }, + "InstanceId": "i-1234567890abcdef0", + "PreviousState": { + "Code": 16, + "Name": "running" + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example terminates the specified EC2 instance.", + "id": "to-terminate-an-ec2-instance-1529359350660", + "title": "To terminate an EC2 instance" + } + ], + "UnassignPrivateIpAddresses": [ + { + "input": { + "NetworkInterfaceId": "eni-e5aa89a3", + "PrivateIpAddresses": [ + "10.0.0.82" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example unassigns the specified private IP address from the specified network interface.", + "id": "ec2-unassign-private-ip-addresses-1", + "title": "To unassign a secondary private IP address from a network interface" + } + ], + "UpdateSecurityGroupRuleDescriptionsEgress": [ + { + "input": { + "GroupId": "sg-123abc12", + "IpPermissions": [ + { + "FromPort": 80, + "IpProtocol": "tcp", + "IpRanges": [ + { + "CidrIp": "203.0.113.0/24", + "Description": "Outbound HTTP access to server 2" + } + ], + "ToPort": 80 + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example updates the description for the specified security group rule.", + "id": "to-update-an-outbound-security-group-rule-description-1529360481544", + "title": "To update an outbound security group rule description" + } + ], + "UpdateSecurityGroupRuleDescriptionsIngress": [ + { + "input": { + "GroupId": "sg-123abc12", + "IpPermissions": [ + { + "FromPort": 22, + "IpProtocol": "tcp", + "IpRanges": [ + { + "CidrIp": "203.0.113.0/16", + "Description": "SSH access from the LA office" + } + ], + "ToPort": 22 + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example updates the description for the specified security group rule.", + "id": "to-update-an-inbound-security-group-rule-description-1529360820372", + "title": "To update an inbound security group rule description" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/paginators-1.json new file mode 100644 index 0000000..467bbbb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/paginators-1.json @@ -0,0 +1,1045 @@ +{ + "pagination": { + "DescribeRouteTables": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RouteTables" + }, + "DescribeIamInstanceProfileAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "IamInstanceProfileAssociations" + }, + "DescribeInstanceStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceStatuses" + }, + "DescribeInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Reservations" + }, + "DescribeReservedInstancesOfferings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ReservedInstancesOfferings" + }, + "DescribeReservedInstancesModifications": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ReservedInstancesModifications" + }, + "DescribeSecurityGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SecurityGroups" + }, + "DescribeSnapshots": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Snapshots" + }, + "DescribeSpotFleetInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ActiveInstances" + }, + "DescribeSpotFleetRequests": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpotFleetRequestConfigs" + }, + "DescribeSpotPriceHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpotPriceHistory" + }, + "DescribeTags": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Tags" + }, + "DescribeVolumeStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VolumeStatuses" + }, + "DescribeVolumes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Volumes" + }, + "DescribeNatGateways": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "NatGateways" + }, + "DescribeNetworkInterfaces": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "NetworkInterfaces" + }, + "DescribeVpcEndpoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VpcEndpoints" + }, + "DescribeVpcEndpointServices": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": [ + "ServiceDetails", + "ServiceNames" + ] + }, + "DescribeVpcEndpointConnections": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VpcEndpointConnections" + }, + "DescribeByoipCidrs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ByoipCidrs" + }, + "DescribeCapacityReservations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CapacityReservations" + }, + "DescribeClassicLinkInstances": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Instances" + }, + "DescribeClientVpnAuthorizationRules": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AuthorizationRules" + }, + "DescribeClientVpnConnections": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Connections" + }, + "DescribeClientVpnEndpoints": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ClientVpnEndpoints" + }, + "DescribeClientVpnRoutes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Routes" + }, + "DescribeClientVpnTargetNetworks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ClientVpnTargetNetworks" + }, + "DescribeEgressOnlyInternetGateways": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EgressOnlyInternetGateways" + }, + "DescribeFleets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Fleets" + }, + "DescribeFlowLogs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "FlowLogs" + }, + "DescribeFpgaImages": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "FpgaImages" + }, + "DescribeHostReservationOfferings": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "OfferingSet" + }, + "DescribeHostReservations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "HostReservationSet" + }, + "DescribeHosts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Hosts" + }, + "DescribeImportImageTasks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ImportImageTasks" + }, + "DescribeImportSnapshotTasks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ImportSnapshotTasks" + }, + "DescribeInstanceCreditSpecifications": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InstanceCreditSpecifications" + }, + "DescribeLaunchTemplateVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LaunchTemplateVersions" + }, + "DescribeLaunchTemplates": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LaunchTemplates" + }, + "DescribeMovingAddresses": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MovingAddressStatuses" + }, + "DescribeNetworkInterfacePermissions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "NetworkInterfacePermissions" + }, + "DescribePrefixLists": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PrefixLists" + }, + "DescribePrincipalIdFormat": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Principals" + }, + "DescribePublicIpv4Pools": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PublicIpv4Pools" + }, + "DescribeScheduledInstanceAvailability": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ScheduledInstanceAvailabilitySet" + }, + "DescribeScheduledInstances": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ScheduledInstanceSet" + }, + "DescribeStaleSecurityGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "StaleSecurityGroupSet" + }, + "DescribeTransitGatewayAttachments": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGatewayAttachments" + }, + "DescribeTransitGatewayRouteTables": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGatewayRouteTables" + }, + "DescribeTransitGatewayVpcAttachments": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGatewayVpcAttachments" + }, + "DescribeTransitGateways": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGateways" + }, + "DescribeVolumesModifications": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "VolumesModifications" + }, + "DescribeVpcClassicLinkDnsSupport": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Vpcs" + }, + "DescribeVpcEndpointConnectionNotifications": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ConnectionNotificationSet" + }, + "DescribeVpcEndpointServiceConfigurations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ServiceConfigurations" + }, + "DescribeVpcEndpointServicePermissions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AllowedPrincipals" + }, + "DescribeVpcPeeringConnections": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "VpcPeeringConnections" + }, + "GetTransitGatewayAttachmentPropagations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGatewayAttachmentPropagations" + }, + "GetTransitGatewayRouteTableAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Associations" + }, + "GetTransitGatewayRouteTablePropagations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGatewayRouteTablePropagations" + }, + "DescribeInternetGateways": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InternetGateways" + }, + "DescribeNetworkAcls": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "NetworkAcls" + }, + "DescribeVpcs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Vpcs" + }, + "DescribeSpotInstanceRequests": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SpotInstanceRequests" + }, + "DescribeDhcpOptions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DhcpOptions" + }, + "DescribeSubnets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Subnets" + }, + "DescribeTrafficMirrorFilters": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TrafficMirrorFilters" + }, + "DescribeTrafficMirrorSessions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TrafficMirrorSessions" + }, + "DescribeTrafficMirrorTargets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TrafficMirrorTargets" + }, + "DescribeExportImageTasks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ExportImageTasks" + }, + "DescribeFastSnapshotRestores": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "FastSnapshotRestores" + }, + "DescribeIpv6Pools": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Ipv6Pools" + }, + "GetAssociatedIpv6PoolCidrs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Ipv6CidrAssociations" + }, + "DescribeCoipPools": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CoipPools" + }, + "DescribeInstanceTypeOfferings": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InstanceTypeOfferings" + }, + "DescribeInstanceTypes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InstanceTypes" + }, + "DescribeLocalGatewayRouteTableVirtualInterfaceGroupAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LocalGatewayRouteTableVirtualInterfaceGroupAssociations" + }, + "DescribeLocalGatewayRouteTableVpcAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LocalGatewayRouteTableVpcAssociations" + }, + "DescribeLocalGatewayRouteTables": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LocalGatewayRouteTables" + }, + "DescribeLocalGatewayVirtualInterfaceGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LocalGatewayVirtualInterfaceGroups" + }, + "DescribeLocalGatewayVirtualInterfaces": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LocalGatewayVirtualInterfaces" + }, + "DescribeLocalGateways": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LocalGateways" + }, + "DescribeTransitGatewayMulticastDomains": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGatewayMulticastDomains" + }, + "DescribeTransitGatewayPeeringAttachments": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGatewayPeeringAttachments" + }, + "GetTransitGatewayMulticastDomainAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MulticastDomainAssociations" + }, + "SearchLocalGatewayRoutes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Routes" + }, + "SearchTransitGatewayMulticastGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MulticastGroups" + }, + "DescribeManagedPrefixLists": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PrefixLists" + }, + "GetManagedPrefixListAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PrefixListAssociations" + }, + "GetManagedPrefixListEntries": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Entries" + }, + "GetGroupsForCapacityReservation": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CapacityReservationGroups" + }, + "DescribeCarrierGateways": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CarrierGateways" + }, + "GetTransitGatewayPrefixListReferences": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGatewayPrefixListReferences" + }, + "DescribeNetworkInsightsAnalyses": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "NetworkInsightsAnalyses" + }, + "DescribeNetworkInsightsPaths": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "NetworkInsightsPaths" + }, + "DescribeTransitGatewayConnectPeers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGatewayConnectPeers" + }, + "DescribeTransitGatewayConnects": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGatewayConnects" + }, + "DescribeAddressesAttribute": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Addresses" + }, + "DescribeReplaceRootVolumeTasks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ReplaceRootVolumeTasks" + }, + "DescribeStoreImageTasks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "StoreImageTaskResults" + }, + "DescribeSecurityGroupRules": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SecurityGroupRules" + }, + "DescribeInstanceEventWindows": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InstanceEventWindows" + }, + "DescribeTrunkInterfaceAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InterfaceAssociations" + }, + "GetVpnConnectionDeviceTypes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "VpnConnectionDeviceTypes" + }, + "DescribeCapacityReservationFleets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CapacityReservationFleets" + }, + "GetInstanceTypesFromInstanceRequirements": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InstanceTypes" + }, + "GetSpotPlacementScores": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SpotPlacementScores" + }, + "DescribeSnapshotTierStatus": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SnapshotTierStatuses" + }, + "ListSnapshotsInRecycleBin": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Snapshots" + }, + "DescribeIpamPools": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpamPools" + }, + "DescribeIpamScopes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpamScopes" + }, + "DescribeIpams": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Ipams" + }, + "DescribeNetworkInsightsAccessScopeAnalyses": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "NetworkInsightsAccessScopeAnalyses" + }, + "DescribeNetworkInsightsAccessScopes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "NetworkInsightsAccessScopes" + }, + "GetIpamAddressHistory": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "HistoryRecords" + }, + "GetIpamPoolAllocations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpamPoolAllocations" + }, + "GetIpamPoolCidrs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpamPoolCidrs" + }, + "GetIpamResourceCidrs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpamResourceCidrs" + }, + "DescribeFastLaunchImages": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "FastLaunchImages" + }, + "ListImagesInRecycleBin": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Images" + }, + "DescribeTransitGatewayPolicyTables": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGatewayPolicyTables" + }, + "DescribeTransitGatewayRouteTableAnnouncements": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGatewayRouteTableAnnouncements" + }, + "GetTransitGatewayPolicyTableAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Associations" + }, + "DescribeAddressTransfers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AddressTransfers" + }, + "DescribeAwsNetworkPerformanceMetricSubscriptions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Subscriptions" + }, + "GetAwsNetworkPerformanceData": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DataResponses" + }, + "DescribeVerifiedAccessEndpoints": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "VerifiedAccessEndpoints" + }, + "DescribeVerifiedAccessGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "VerifiedAccessGroups" + }, + "DescribeVerifiedAccessInstanceLoggingConfigurations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LoggingConfigurations" + }, + "DescribeVerifiedAccessInstances": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "VerifiedAccessInstances" + }, + "DescribeVerifiedAccessTrustProviders": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "VerifiedAccessTrustProviders" + }, + "DescribeImages": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Images" + }, + "DescribeIpamResourceDiscoveries": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpamResourceDiscoveries" + }, + "DescribeIpamResourceDiscoveryAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpamResourceDiscoveryAssociations" + }, + "GetIpamDiscoveredAccounts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpamDiscoveredAccounts" + }, + "GetIpamDiscoveredResourceCidrs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpamDiscoveredResourceCidrs" + }, + "GetNetworkInsightsAccessScopeAnalysisFindings": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AnalysisFindings" + }, + "DescribeInstanceConnectEndpoints": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InstanceConnectEndpoints" + }, + "GetSecurityGroupsForVpc": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SecurityGroupForVpcs" + }, + "DescribeCapacityBlockOfferings": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CapacityBlockOfferings" + }, + "DescribeInstanceTopology": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Instances" + }, + "DescribeMacHosts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MacHosts" + }, + "DescribeCapacityReservationBillingRequests": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CapacityReservationBillingRequests" + }, + "DescribeInstanceImageMetadata": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InstanceImageMetadata" + }, + "DescribeSecurityGroupVpcAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SecurityGroupVpcAssociations" + }, + "DescribeCapacityBlockExtensionHistory": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CapacityBlockExtensions" + }, + "DescribeCapacityBlockExtensionOfferings": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CapacityBlockExtensionOfferings" + }, + "DescribeRouteServerEndpoints": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "RouteServerEndpoints" + }, + "DescribeRouteServerPeers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "RouteServerPeers" + }, + "DescribeRouteServers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "RouteServers" + }, + "DescribeMacModificationTasks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MacModificationTasks" + }, + "DescribeCapacityBlockStatus": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CapacityBlockStatuses" + }, + "DescribeCapacityBlocks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CapacityBlocks" + }, + "DescribeImageReferences": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ImageReferences" + }, + "DescribeImageUsageReportEntries": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ImageUsageReportEntries" + }, + "DescribeImageUsageReports": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ImageUsageReports" + }, + "DescribeCapacityManagerDataExports": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CapacityManagerDataExports" + }, + "GetCapacityManagerMetricData": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MetricDataResults" + }, + "GetCapacityManagerMetricDimensions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MetricDimensionResults" + }, + "DescribeIpamPrefixListResolverTargets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpamPrefixListResolverTargets" + }, + "DescribeIpamPrefixListResolvers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpamPrefixListResolvers" + }, + "GetIpamPrefixListResolverRules": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Rules" + }, + "GetIpamPrefixListResolverVersionEntries": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Entries" + }, + "GetIpamPrefixListResolverVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpamPrefixListResolverVersions" + }, + "DescribeVpnConcentrators": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "VpnConcentrators" + }, + "SearchTransitGatewayRoutes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "more_results": "AdditionalRoutesAvailable", + "output_token": "NextToken", + "result_key": "Routes" + }, + "DescribeSecondaryInterfaces": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SecondaryInterfaces" + }, + "DescribeSecondaryNetworks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SecondaryNetworks" + }, + "DescribeSecondarySubnets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SecondarySubnets" + }, + "GetCapacityManagerMonitoredTagKeys": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CapacityManagerTagKeys" + }, + "DescribeIpamPoolAllocations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpamPoolAllocations" + }, + "GetTransitGatewayPolicyTableEntries": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransitGatewayPolicyTableEntries" + }, + "DescribeCapacityReservationDateChangeQuotes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CapacityReservationModificationQuotes" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/paginators-1.sdk-extras.json new file mode 100644 index 0000000..823bbb5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/paginators-1.sdk-extras.json @@ -0,0 +1,13 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetNetworkInsightsAccessScopeAnalysisFindings": { + "non_aggregate_keys": [ + "AnalysisStatus", + "NetworkInsightsAccessScopeAnalysisId" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/service-2.json.gz new file mode 100644 index 0000000..0640cb3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/waiters-2.json new file mode 100644 index 0000000..5a8479a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ec2/2016-11-15/waiters-2.json @@ -0,0 +1,887 @@ +{ + "version": 2, + "waiters": { + "InstanceExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeInstances", + "acceptors": [ + { + "matcher": "path", + "expected": true, + "argument": "length(Reservations[]) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidInstanceID.NotFound", + "state": "retry" + } + ] + }, + "BundleTaskComplete": { + "delay": 15, + "operation": "DescribeBundleTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "complete", + "matcher": "pathAll", + "state": "success", + "argument": "BundleTasks[].State" + }, + { + "expected": "failed", + "matcher": "pathAny", + "state": "failure", + "argument": "BundleTasks[].State" + } + ] + }, + "ConversionTaskCancelled": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskCompleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelled", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + }, + { + "expected": "cancelling", + "matcher": "pathAny", + "state": "failure", + "argument": "ConversionTasks[].State" + } + ] + }, + "ConversionTaskDeleted": { + "delay": 15, + "operation": "DescribeConversionTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "ConversionTasks[].State" + } + ] + }, + "CustomerGatewayAvailable": { + "delay": 15, + "operation": "DescribeCustomerGateways", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "CustomerGateways[].State" + } + ] + }, + "ExportTaskCancelled": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "cancelled", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ExportTaskCompleted": { + "delay": 15, + "operation": "DescribeExportTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ExportTasks[].State" + } + ] + }, + "ImageExists": { + "operation": "DescribeImages", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "matcher": "path", + "expected": true, + "argument": "length(Images[]) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidAMIID.NotFound", + "state": "retry" + } + ] + }, + "ImageAvailable": { + "operation": "DescribeImages", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Images[].State", + "expected": "available" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "Images[].State", + "expected": "failed" + } + ] + }, + "ImageUsageReportAvailable": { + "operation": "DescribeImageUsageReports", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "ImageUsageReports[].State", + "expected": "available" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "ImageUsageReports[].State", + "expected": "failed" + } + ] + }, + "InstanceRunning": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "running", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "shutting-down", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "matcher": "error", + "expected": "InvalidInstanceID.NotFound", + "state": "retry" + } + ] + }, + "InstanceStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].InstanceStatus.Status", + "expected": "ok" + }, + { + "matcher": "error", + "expected": "InvalidInstanceID.NotFound", + "state": "retry" + } + ] + }, + "InstanceStopped": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "stopped", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "terminated", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "InstanceTerminated": { + "delay": 15, + "operation": "DescribeInstances", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "terminated", + "matcher": "pathAll", + "state": "success", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + }, + { + "expected": "stopping", + "matcher": "pathAny", + "state": "failure", + "argument": "Reservations[].Instances[].State.Name" + } + ] + }, + "InternetGatewayExists": { + "operation": "DescribeInternetGateways", + "delay": 5, + "maxAttempts": 6, + "acceptors": [ + { + "expected": true, + "matcher": "path", + "state": "success", + "argument": "length(InternetGateways[].InternetGatewayId) > `0`" + }, + { + "expected": "InvalidInternetGateway.NotFound", + "matcher": "error", + "state": "retry" + } + ] + }, + "KeyPairExists": { + "operation": "DescribeKeyPairs", + "delay": 5, + "maxAttempts": 6, + "acceptors": [ + { + "expected": true, + "matcher": "path", + "state": "success", + "argument": "length(KeyPairs[].KeyName) > `0`" + }, + { + "expected": "InvalidKeyPair.NotFound", + "matcher": "error", + "state": "retry" + } + ] + }, + "NatGatewayAvailable": { + "operation": "DescribeNatGateways", + "delay": 15, + "maxAttempts": 40, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "NatGateways[].State", + "expected": "available" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "NatGateways[].State", + "expected": "failed" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "NatGateways[].State", + "expected": "deleting" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "NatGateways[].State", + "expected": "deleted" + }, + { + "state": "retry", + "matcher": "error", + "expected": "NatGatewayNotFound" + } + ] + }, + "NatGatewayDeleted": { + "operation": "DescribeNatGateways", + "delay": 15, + "maxAttempts": 40, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "NatGateways[].State", + "expected": "deleted" + }, + { + "state": "success", + "matcher": "error", + "expected": "NatGatewayNotFound" + } + ] + }, + "NetworkInterfaceAvailable": { + "operation": "DescribeNetworkInterfaces", + "delay": 20, + "maxAttempts": 10, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "NetworkInterfaces[].Status" + }, + { + "expected": "InvalidNetworkInterfaceID.NotFound", + "matcher": "error", + "state": "failure" + } + ] + }, + "PasswordDataAvailable": { + "operation": "GetPasswordData", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "length(PasswordData) > `0`", + "expected": true + } + ] + }, + "SnapshotCompleted": { + "delay": 15, + "operation": "DescribeSnapshots", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "Snapshots[].State" + }, + { + "expected": "error", + "matcher": "pathAny", + "state": "failure", + "argument": "Snapshots[].State" + } + ] + }, + "SnapshotImported": { + "delay": 15, + "operation": "DescribeImportSnapshotTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "completed", + "matcher": "pathAll", + "state": "success", + "argument": "ImportSnapshotTasks[].SnapshotTaskDetail.Status" + }, + { + "expected": "error", + "matcher": "pathAny", + "state": "failure", + "argument": "ImportSnapshotTasks[].SnapshotTaskDetail.Status" + } + ] + }, + "SecurityGroupExists": { + "operation": "DescribeSecurityGroups", + "delay": 5, + "maxAttempts": 6, + "acceptors": [ + { + "expected": true, + "matcher": "path", + "state": "success", + "argument": "length(SecurityGroups[].GroupId) > `0`" + }, + { + "expected": "InvalidGroup.NotFound", + "matcher": "error", + "state": "retry" + } + ] + }, + "SecurityGroupVpcAssociationAssociated": { + "delay": 10, + "maxAttempts": 7, + "operation": "DescribeSecurityGroupVpcAssociations", + "acceptors": [ + { + "expected": "associated", + "matcher": "pathAll", + "state": "success", + "argument": "SecurityGroupVpcAssociations[].State" + }, + { + "expected": "associating", + "matcher": "pathAny", + "state": "retry", + "argument": "SecurityGroupVpcAssociations[].State" + }, + { + "expected": "association-failed", + "matcher": "pathAny", + "state": "failure", + "argument": "SecurityGroupVpcAssociations[].State" + } + ] + }, + "SecurityGroupVpcAssociationDisassociated": { + "delay": 10, + "maxAttempts": 7, + "operation": "DescribeSecurityGroupVpcAssociations", + "acceptors": [ + { + "expected": "disassociated", + "matcher": "pathAll", + "state": "success", + "argument": "SecurityGroupVpcAssociations[].State" + }, + { + "expected": "disassociating", + "matcher": "pathAny", + "state": "retry", + "argument": "SecurityGroupVpcAssociations[].State" + }, + { + "expected": "disassociation-failed", + "matcher": "pathAny", + "state": "failure", + "argument": "SecurityGroupVpcAssociations[].State" + }, + { + "expected": true, + "matcher": "path", + "state": "success", + "argument": "length(SecurityGroupVpcAssociations[]) == `0`" + } + ] + }, + "SpotInstanceRequestFulfilled": { + "operation": "DescribeSpotInstanceRequests", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "fulfilled" + }, + { + "state": "success", + "matcher": "pathAll", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "request-canceled-and-instance-running" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "schedule-expired" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "canceled-before-fulfillment" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "bad-parameters" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "SpotInstanceRequests[].Status.Code", + "expected": "system-error" + }, + { + "state": "retry", + "matcher": "error", + "expected": "InvalidSpotInstanceRequestID.NotFound" + } + ] + }, + "StoreImageTaskComplete": { + "delay": 5, + "operation": "DescribeStoreImageTasks", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "Completed", + "matcher": "pathAll", + "state": "success", + "argument": "StoreImageTaskResults[].StoreTaskState" + }, + { + "expected": "Failed", + "matcher": "pathAny", + "state": "failure", + "argument": "StoreImageTaskResults[].StoreTaskState" + }, + { + "expected": "InProgress", + "matcher": "pathAny", + "state": "retry", + "argument": "StoreImageTaskResults[].StoreTaskState" + } + ] + }, + "SubnetAvailable": { + "delay": 15, + "operation": "DescribeSubnets", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Subnets[].State" + } + ] + }, + "SystemStatusOk": { + "operation": "DescribeInstanceStatus", + "maxAttempts": 40, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "InstanceStatuses[].SystemStatus.Status", + "expected": "ok" + } + ] + }, + "VolumeAvailable": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VolumeDeleted": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "matcher": "error", + "expected": "InvalidVolume.NotFound", + "state": "success" + } + ] + }, + "VolumeInUse": { + "delay": 15, + "operation": "DescribeVolumes", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "in-use", + "matcher": "pathAll", + "state": "success", + "argument": "Volumes[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Volumes[].State" + } + ] + }, + "VpcAvailable": { + "delay": 15, + "operation": "DescribeVpcs", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Vpcs[].State" + } + ] + }, + "VpcExists": { + "operation": "DescribeVpcs", + "delay": 1, + "maxAttempts": 5, + "acceptors": [ + { + "matcher": "status", + "expected": 200, + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidVpcID.NotFound", + "state": "retry" + } + ] + }, + "VpnConnectionAvailable": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + }, + "VpnConnectionDeleted": { + "delay": 15, + "operation": "DescribeVpnConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "VpnConnections[].State" + }, + { + "expected": "pending", + "matcher": "pathAny", + "state": "failure", + "argument": "VpnConnections[].State" + } + ] + }, + "VpcPeeringConnectionExists": { + "delay": 15, + "operation": "DescribeVpcPeeringConnections", + "maxAttempts": 40, + "acceptors": [ + { + "matcher": "status", + "expected": 200, + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidVpcPeeringConnectionID.NotFound", + "state": "retry" + } + ] + }, + "VpcPeeringConnectionDeleted": { + "delay": 15, + "operation": "DescribeVpcPeeringConnections", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "VpcPeeringConnections[].Status.Code" + }, + { + "matcher": "error", + "expected": "InvalidVpcPeeringConnectionID.NotFound", + "state": "success" + } + ] + }, + "SecondaryNetworkCreateComplete": { + "operation": "DescribeSecondaryNetworks", + "delay": 10, + "maxAttempts": 30, + "acceptors": [ + { + "expected": "create-complete", + "matcher": "pathAll", + "state": "success", + "argument": "SecondaryNetworks[].State" + }, + { + "expected": "create-failed", + "matcher": "pathAny", + "state": "failure", + "argument": "SecondaryNetworks[].State" + }, + { + "matcher": "error", + "expected": "InvalidSecondaryNetworkId.NotFound", + "state": "retry" + } + ] + }, + "SecondaryNetworkDeleteComplete": { + "operation": "DescribeSecondaryNetworks", + "delay": 10, + "maxAttempts": 30, + "acceptors": [ + { + "expected": "delete-complete", + "matcher": "pathAll", + "state": "success", + "argument": "SecondaryNetworks[].State" + }, + { + "expected": "delete-failed", + "matcher": "pathAny", + "state": "failure", + "argument": "SecondaryNetworks[].State" + } + ] + }, + "SecondarySubnetCreateComplete": { + "operation": "DescribeSecondarySubnets", + "delay": 10, + "maxAttempts": 30, + "acceptors": [ + { + "expected": "create-complete", + "matcher": "pathAll", + "state": "success", + "argument": "SecondarySubnets[].State" + }, + { + "expected": "delete-failed", + "matcher": "pathAny", + "state": "failure", + "argument": "SecondarySubnets[].State" + }, + { + "matcher": "error", + "expected": "InvalidSecondarySubnetId.NotFound", + "state": "retry" + } + ] + }, + "SecondarySubnetDeleteComplete": { + "operation": "DescribeSecondarySubnets", + "delay": 10, + "maxAttempts": 30, + "acceptors": [ + { + "expected": "delete-complete", + "matcher": "pathAll", + "state": "success", + "argument": "SecondarySubnets[].State" + }, + { + "expected": "delete-failed", + "matcher": "pathAny", + "state": "failure", + "argument": "SecondarySubnets[].State" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecr-public/2020-10-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ecr-public/2020-10-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6e4c631 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ecr-public/2020-10-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecr-public/2020-10-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ecr-public/2020-10-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ecr-public/2020-10-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecr-public/2020-10-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ecr-public/2020-10-30/paginators-1.json new file mode 100644 index 0000000..b9dbda4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ecr-public/2020-10-30/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "DescribeImageTags": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "imageTagDetails" + }, + "DescribeImages": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "imageDetails" + }, + "DescribeRegistries": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "registries" + }, + "DescribeRepositories": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "repositories" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecr-public/2020-10-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ecr-public/2020-10-30/service-2.json.gz new file mode 100644 index 0000000..ed4a6aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ecr-public/2020-10-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..30719c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/examples-1.json new file mode 100644 index 0000000..7daf57f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/examples-1.json @@ -0,0 +1,195 @@ +{ + "version": "1.0", + "examples": { + "BatchDeleteImage": [ + { + "input": { + "imageIds": [ + { + "imageTag": "precise" + } + ], + "repositoryName": "ubuntu" + }, + "output": { + "failures": [ + + ], + "imageIds": [ + { + "imageDigest": "sha256:examplee6d1e504117a17000003d3753086354a38375961f2e665416ef4b1b2f", + "imageTag": "precise" + } + ] + }, + "comments": { + }, + "description": "This example deletes images with the tags precise and trusty in a repository called ubuntu in the default registry for an account.", + "id": "batchdeleteimages-example-1470860541707", + "title": "To delete multiple images" + } + ], + "BatchGetImage": [ + { + "input": { + "imageIds": [ + { + "imageTag": "precise" + } + ], + "repositoryName": "ubuntu" + }, + "output": { + "failures": [ + + ], + "images": [ + { + "imageId": { + "imageDigest": "sha256:example76bdff6d83a09ba2a818f0d00000063724a9ac3ba5019c56f74ebf42a", + "imageTag": "precise" + }, + "imageManifest": "{\n \"schemaVersion\": 1,\n \"name\": \"ubuntu\",\n \"tag\": \"precise\",\n...", + "registryId": "244698725403", + "repositoryName": "ubuntu" + } + ] + }, + "comments": { + "output": { + "imageManifest": "In this example, the imageManifest in the output JSON has been truncated." + } + }, + "description": "This example obtains information for an image with a specified image digest ID from the repository named ubuntu in the current account.", + "id": "batchgetimage-example-1470862771437", + "title": "To obtain multiple images in a single request" + } + ], + "CreateRepository": [ + { + "input": { + "repositoryName": "project-a/nginx-web-app" + }, + "output": { + "repository": { + "registryId": "012345678901", + "repositoryArn": "arn:aws:ecr:us-west-2:012345678901:repository/project-a/nginx-web-app", + "repositoryName": "project-a/nginx-web-app" + } + }, + "comments": { + "output": { + "imageManifest": "In this example, the imageManifest in the output JSON has been truncated." + } + }, + "description": "This example creates a repository called nginx-web-app inside the project-a namespace in the default registry for an account.", + "id": "createrepository-example-1470863688724", + "title": "To create a new repository" + } + ], + "DeleteRepository": [ + { + "input": { + "force": true, + "repositoryName": "ubuntu" + }, + "output": { + "repository": { + "registryId": "012345678901", + "repositoryArn": "arn:aws:ecr:us-west-2:012345678901:repository/ubuntu", + "repositoryName": "ubuntu" + } + }, + "comments": { + "output": { + "imageManifest": "In this example, the imageManifest in the output JSON has been truncated." + } + }, + "description": "This example force deletes a repository named ubuntu in the default registry for an account. The force parameter is required if the repository contains images.", + "id": "deleterepository-example-1470863805703", + "title": "To force delete a repository" + } + ], + "DeleteRepositoryPolicy": [ + { + "input": { + "repositoryName": "ubuntu" + }, + "output": { + "policyText": "{ ... }", + "registryId": "012345678901", + "repositoryName": "ubuntu" + }, + "comments": { + }, + "description": "This example deletes the policy associated with the repository named ubuntu in the current account.", + "id": "deleterepositorypolicy-example-1470866943748", + "title": "To delete the policy associated with a repository" + } + ], + "DescribeRepositories": [ + { + "input": { + }, + "output": { + "repositories": [ + { + "registryId": "012345678910", + "repositoryArn": "arn:aws:ecr:us-west-2:012345678910:repository/ubuntu", + "repositoryName": "ubuntu" + }, + { + "registryId": "012345678910", + "repositoryArn": "arn:aws:ecr:us-west-2:012345678910:repository/test", + "repositoryName": "test" + } + ] + }, + "comments": { + "output": { + } + }, + "description": "The following example obtains a list and description of all repositories in the default registry to which the current user has access.", + "id": "describe-repositories-1470856017467", + "title": "To describe all repositories in the current account" + } + ], + "GetRepositoryPolicy": [ + { + "input": { + "repositoryName": "ubuntu" + }, + "output": { + "policyText": "{\n \"Version\" : \"2008-10-17\",\n \"Statement\" : [ {\n \"Sid\" : \"new statement\",\n \"Effect\" : \"Allow\",\n \"Principal\" : {\n \"AWS\" : \"arn:aws:iam::012345678901:role/CodeDeployDemo\"\n },\n\"Action\" : [ \"ecr:GetDownloadUrlForLayer\", \"ecr:BatchGetImage\", \"ecr:BatchCheckLayerAvailability\" ]\n } ]\n}", + "registryId": "012345678901", + "repositoryName": "ubuntu" + }, + "comments": { + }, + "description": "This example obtains the repository policy for the repository named ubuntu.", + "id": "getrepositorypolicy-example-1470867669211", + "title": "To get the current policy for a repository" + } + ], + "ListImages": [ + { + "input": { + "repositoryName": "ubuntu" + }, + "output": { + "imageIds": [ + { + "imageDigest": "sha256:764f63476bdff6d83a09ba2a818f0d35757063724a9ac3ba5019c56f74ebf42a", + "imageTag": "precise" + } + ] + }, + "comments": { + }, + "description": "This example lists all of the images in the repository named ubuntu in the default registry in the current account. ", + "id": "listimages-example-1470868161594", + "title": "To list all images in a repository" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/paginators-1.json new file mode 100644 index 0000000..a2161d3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/paginators-1.json @@ -0,0 +1,66 @@ +{ + "pagination": { + "ListImages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "imageIds" + }, + "DescribeImages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "imageDetails" + }, + "DescribeRepositories": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "repositories" + }, + "DescribeImageScanFindings": { + "input_token": "nextToken", + "limit_key": "maxResults", + "non_aggregate_keys": [ + "registryId", + "repositoryName", + "imageId", + "imageScanStatus", + "imageScanFindings" + ], + "output_token": "nextToken", + "result_key": [ + "imageScanFindings.findings", + "imageScanFindings.enhancedFindings" + ] + }, + "GetLifecyclePolicyPreview": { + "input_token": "nextToken", + "limit_key": "maxResults", + "non_aggregate_keys": [ + "registryId", + "repositoryName", + "lifecyclePolicyText", + "status", + "summary" + ], + "output_token": "nextToken", + "result_key": "previewResults" + }, + "DescribePullThroughCacheRules": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "pullThroughCacheRules" + }, + "DescribeRepositoryCreationTemplates": { + "input_token": "nextToken", + "limit_key": "maxResults", + "non_aggregate_keys": [ + "registryId" + ], + "output_token": "nextToken", + "result_key": "repositoryCreationTemplates" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/service-2.json.gz new file mode 100644 index 0000000..5dc74ea Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/waiters-2.json new file mode 100644 index 0000000..9ef9608 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ecr/2015-09-21/waiters-2.json @@ -0,0 +1,45 @@ +{ + "version": 2, + "waiters": { + "ImageScanComplete": { + "description": "Wait until an image scan is complete and findings can be accessed", + "operation": "DescribeImageScanFindings", + "delay": 5, + "maxAttempts": 60, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "imageScanStatus.status", + "expected": "COMPLETE" + }, + { + "state": "failure", + "matcher": "path", + "argument": "imageScanStatus.status", + "expected": "FAILED" + } + ] + }, + "LifecyclePolicyPreviewComplete": { + "description": "Wait until a lifecycle policy preview request is complete and results can be accessed", + "operation": "GetLifecyclePolicyPreview", + "delay": 5, + "maxAttempts": 20, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "status", + "expected": "COMPLETE" + }, + { + "state": "failure", + "matcher": "path", + "argument": "status", + "expected": "FAILED" + } + ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..11baafb Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/examples-1.json new file mode 100644 index 0000000..0fbf7b3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/examples-1.json @@ -0,0 +1,1137 @@ +{ + "version": "1.0", + "examples": { + "CreateCluster": [ + { + "input": { + "clusterName": "my_cluster" + }, + "output": { + "cluster": { + "activeServicesCount": 0, + "clusterArn": "arn:aws:ecs:us-east-1:012345678910:cluster/my_cluster", + "clusterName": "my_cluster", + "pendingTasksCount": 0, + "registeredContainerInstancesCount": 0, + "runningTasksCount": 0, + "status": "ACTIVE" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a cluster in your default region.", + "id": "to-create-a-new-cluster-1472514079365", + "title": "To create a new cluster" + } + ], + "CreateService": [ + { + "input": { + "desiredCount": 10, + "serviceName": "ecs-simple-service", + "taskDefinition": "hello_world" + }, + "output": { + "service": { + "clusterArn": "arn:aws:ecs:us-east-1:012345678910:cluster/default", + "createdAt": "2016-08-29T16:13:47.298Z", + "deploymentConfiguration": { + "maximumPercent": 200, + "minimumHealthyPercent": 100 + }, + "deployments": [ + { + "createdAt": "2016-08-29T16:13:47.298Z", + "desiredCount": 10, + "id": "ecs-svc/9223370564342348388", + "pendingCount": 0, + "runningCount": 0, + "status": "PRIMARY", + "taskDefinition": "arn:aws:ecs:us-east-1:012345678910:task-definition/hello_world:6", + "updatedAt": "2016-08-29T16:13:47.298Z" + }, + { + "createdAt": "2016-08-29T15:52:44.481Z", + "desiredCount": 0, + "id": "ecs-svc/9223370564343611322", + "pendingCount": 0, + "runningCount": 0, + "status": "ACTIVE", + "taskDefinition": "arn:aws:ecs:us-east-1:012345678910:task-definition/hello_world:6", + "updatedAt": "2016-08-29T16:11:38.941Z" + } + ], + "desiredCount": 10, + "events": [ + + ], + "loadBalancers": [ + + ], + "pendingCount": 0, + "runningCount": 0, + "serviceArn": "arn:aws:ecs:us-east-1:012345678910:service/ecs-simple-service", + "serviceName": "ecs-simple-service", + "status": "ACTIVE", + "taskDefinition": "arn:aws:ecs:us-east-1:012345678910:task-definition/hello_world:6" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a service in your default region called ``ecs-simple-service``. The service uses the ``hello_world`` task definition and it maintains 10 copies of that task.", + "id": "to-create-a-new-service-1472512584282", + "title": "To create a new service" + }, + { + "input": { + "desiredCount": 10, + "loadBalancers": [ + { + "containerName": "simple-app", + "containerPort": 80, + "loadBalancerName": "EC2Contai-EcsElast-15DCDAURT3ZO2" + } + ], + "role": "ecsServiceRole", + "serviceName": "ecs-simple-service-elb", + "taskDefinition": "console-sample-app-static" + }, + "output": { + "service": { + "clusterArn": "arn:aws:ecs:us-east-1:012345678910:cluster/default", + "createdAt": "2016-08-29T16:02:54.884Z", + "deploymentConfiguration": { + "maximumPercent": 200, + "minimumHealthyPercent": 100 + }, + "deployments": [ + { + "createdAt": "2016-08-29T16:02:54.884Z", + "desiredCount": 10, + "id": "ecs-svc/9223370564343000923", + "pendingCount": 0, + "runningCount": 0, + "status": "PRIMARY", + "taskDefinition": "arn:aws:ecs:us-east-1:012345678910:task-definition/console-sample-app-static:6", + "updatedAt": "2016-08-29T16:02:54.884Z" + } + ], + "desiredCount": 10, + "events": [ + + ], + "loadBalancers": [ + { + "containerName": "simple-app", + "containerPort": 80, + "loadBalancerName": "EC2Contai-EcsElast-15DCDAURT3ZO2" + } + ], + "pendingCount": 0, + "roleArn": "arn:aws:iam::012345678910:role/ecsServiceRole", + "runningCount": 0, + "serviceArn": "arn:aws:ecs:us-east-1:012345678910:service/ecs-simple-service-elb", + "serviceName": "ecs-simple-service-elb", + "status": "ACTIVE", + "taskDefinition": "arn:aws:ecs:us-east-1:012345678910:task-definition/console-sample-app-static:6" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a service in your default region called ``ecs-simple-service-elb``. The service uses the ``ecs-demo`` task definition and it maintains 10 copies of that task. You must reference an existing load balancer in the same region by its name.", + "id": "to-create-a-new-service-behind-a-load-balancer-1472512484823", + "title": "To create a new service behind a load balancer" + } + ], + "DeleteAccountSetting": [ + { + "input": { + "name": "serviceLongArnFormat" + }, + "output": { + "setting": { + "name": "serviceLongArnFormat", + "value": "enabled", + "principalArn": "arn:aws:iam:::user/principalName" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the account setting for your user for the specified resource type.", + "id": "to-delete-the-account-setting-for-your-user-account-1549524548115", + "title": "To delete your account setting" + }, + { + "input": { + "name": "containerInstanceLongArnFormat", + "principalArn": "arn:aws:iam:::user/principalName" + }, + "output": { + "setting": { + "name": "containerInstanceLongArnFormat", + "value": "enabled", + "principalArn": "arn:aws:iam:::user/principalName" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the account setting for a specific IAM user or IAM role for the specified resource type. Only the root user can view or modify the account settings for another user.", + "id": "to-delete-the-account-setting-for-a-specific-iam-user-or-iam-role-1549524612917", + "title": "To delete the account settings for a specific IAM user or IAM role" + } + ], + "DeleteCluster": [ + { + "input": { + "cluster": "my_cluster" + }, + "output": { + "cluster": { + "activeServicesCount": 0, + "clusterArn": "arn:aws:ecs:us-east-1:012345678910:cluster/my_cluster", + "clusterName": "my_cluster", + "pendingTasksCount": 0, + "registeredContainerInstancesCount": 0, + "runningTasksCount": 0, + "status": "INACTIVE" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes an empty cluster in your default region.", + "id": "to-delete-an-empty-cluster-1472512705352", + "title": "To delete an empty cluster" + } + ], + "DeleteService": [ + { + "input": { + "service": "my-http-service" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the my-http-service service. The service must have a desired count and running count of 0 before you can delete it.", + "id": "e8183e38-f86e-4390-b811-f74f30a6007d", + "title": "To delete a service" + } + ], + "DeregisterContainerInstance": [ + { + "input": { + "cluster": "default", + "containerInstance": "container_instance_UUID", + "force": true + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deregisters a container instance from the specified cluster in your default region. If there are still tasks running on the container instance, you must either stop those tasks before deregistering, or use the force option.", + "id": "bf624927-cf64-4f4b-8b7e-c024a4e682f6", + "title": "To deregister a container instance from a cluster" + } + ], + "DescribeClusters": [ + { + "input": { + "clusters": [ + "default" + ] + }, + "output": { + "clusters": [ + { + "clusterArn": "arn:aws:ecs:us-east-1:aws_account_id:cluster/default", + "clusterName": "default", + "status": "ACTIVE" + } + ], + "failures": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example provides a description of the specified cluster in your default region.", + "id": "ba88d100-9672-4231-80da-a4bd210bf728", + "title": "To describe a cluster" + } + ], + "DescribeContainerInstances": [ + { + "input": { + "cluster": "default", + "containerInstances": [ + "f2756532-8f13-4d53-87c9-aed50dc94cd7" + ] + }, + "output": { + "containerInstances": [ + { + "agentConnected": true, + "containerInstanceArn": "arn:aws:ecs:us-east-1:012345678910:container-instance/f2756532-8f13-4d53-87c9-aed50dc94cd7", + "ec2InstanceId": "i-807f3249", + "pendingTasksCount": 0, + "registeredResources": [ + { + "name": "CPU", + "type": "INTEGER", + "doubleValue": 0.0, + "integerValue": 2048, + "longValue": 0 + }, + { + "name": "MEMORY", + "type": "INTEGER", + "doubleValue": 0.0, + "integerValue": 3768, + "longValue": 0 + }, + { + "name": "PORTS", + "type": "STRINGSET", + "doubleValue": 0.0, + "integerValue": 0, + "longValue": 0, + "stringSetValue": [ + "2376", + "22", + "51678", + "2375" + ] + } + ], + "remainingResources": [ + { + "name": "CPU", + "type": "INTEGER", + "doubleValue": 0.0, + "integerValue": 1948, + "longValue": 0 + }, + { + "name": "MEMORY", + "type": "INTEGER", + "doubleValue": 0.0, + "integerValue": 3668, + "longValue": 0 + }, + { + "name": "PORTS", + "type": "STRINGSET", + "doubleValue": 0.0, + "integerValue": 0, + "longValue": 0, + "stringSetValue": [ + "2376", + "22", + "80", + "51678", + "2375" + ] + } + ], + "runningTasksCount": 1, + "status": "ACTIVE" + } + ], + "failures": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example provides a description of the specified container instance in your default region, using the container instance UUID as an identifier.", + "id": "c8f439de-eb27-4269-8ca7-2c0a7ba75ab0", + "title": "To describe container instance" + } + ], + "DescribeServices": [ + { + "input": { + "services": [ + "ecs-simple-service" + ] + }, + "output": { + "failures": [ + + ], + "services": [ + { + "clusterArn": "arn:aws:ecs:us-east-1:012345678910:cluster/default", + "createdAt": "2016-08-29T16:25:52.130Z", + "deploymentConfiguration": { + "maximumPercent": 200, + "minimumHealthyPercent": 100 + }, + "deployments": [ + { + "createdAt": "2016-08-29T16:25:52.130Z", + "desiredCount": 1, + "id": "ecs-svc/9223370564341623665", + "pendingCount": 0, + "runningCount": 0, + "status": "PRIMARY", + "taskDefinition": "arn:aws:ecs:us-east-1:012345678910:task-definition/hello_world:6", + "updatedAt": "2016-08-29T16:25:52.130Z" + } + ], + "desiredCount": 1, + "events": [ + { + "createdAt": "2016-08-29T16:25:58.520Z", + "id": "38c285e5-d335-4b68-8b15-e46dedc8e88d", + "message": "(service ecs-simple-service) was unable to place a task because no container instance met all of its requirements. The closest matching (container-instance 3f4de1c5-ffdd-4954-af7e-75b4be0c8841) is already using a port required by your task. For more information, see the Troubleshooting section of the Amazon ECS Developer Guide." + } + ], + "loadBalancers": [ + + ], + "pendingCount": 0, + "runningCount": 0, + "serviceArn": "arn:aws:ecs:us-east-1:012345678910:service/ecs-simple-service", + "serviceName": "ecs-simple-service", + "status": "ACTIVE", + "taskDefinition": "arn:aws:ecs:us-east-1:012345678910:task-definition/hello_world:6" + } + ] + }, + "comments": { + "input": { + }, + "output": { + "services[0].events[0].message": "In this example, there is a service event that shows unavailable cluster resources." + } + }, + "description": "This example provides descriptive information about the service named ``ecs-simple-service``.", + "id": "to-describe-a-service-1472513256350", + "title": "To describe a service" + } + ], + "DescribeTaskDefinition": [ + { + "input": { + "taskDefinition": "hello_world:8" + }, + "output": { + "taskDefinition": { + "containerDefinitions": [ + { + "name": "wordpress", + "cpu": 10, + "environment": [ + + ], + "essential": true, + "image": "wordpress", + "links": [ + "mysql" + ], + "memory": 500, + "mountPoints": [ + + ], + "portMappings": [ + { + "containerPort": 80, + "hostPort": 80 + } + ], + "volumesFrom": [ + + ] + }, + { + "name": "mysql", + "cpu": 10, + "environment": [ + { + "name": "MYSQL_ROOT_PASSWORD", + "value": "password" + } + ], + "essential": true, + "image": "mysql", + "memory": 500, + "mountPoints": [ + + ], + "portMappings": [ + + ], + "volumesFrom": [ + + ] + } + ], + "family": "hello_world", + "revision": 8, + "taskDefinitionArn": "arn:aws:ecs:us-east-1::task-definition/hello_world:8", + "volumes": [ + + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example provides a description of the specified task definition.", + "id": "4c21eeb1-f1da-4a08-8c44-297fc8d0ea88", + "title": "To describe a task definition" + } + ], + "DescribeTasks": [ + { + "input": { + "tasks": [ + "c5cba4eb-5dad-405e-96db-71ef8eefe6a8" + ] + }, + "output": { + "failures": [ + + ], + "tasks": [ + { + "clusterArn": "arn:aws:ecs:::cluster/default", + "containerInstanceArn": "arn:aws:ecs:::container-instance/18f9eda5-27d7-4c19-b133-45adc516e8fb", + "containers": [ + { + "name": "ecs-demo", + "containerArn": "arn:aws:ecs:::container/7c01765b-c588-45b3-8290-4ba38bd6c5a6", + "lastStatus": "RUNNING", + "networkBindings": [ + { + "bindIP": "0.0.0.0", + "containerPort": 80, + "hostPort": 80 + } + ], + "taskArn": "arn:aws:ecs:::task/c5cba4eb-5dad-405e-96db-71ef8eefe6a8" + } + ], + "desiredStatus": "RUNNING", + "lastStatus": "RUNNING", + "overrides": { + "containerOverrides": [ + { + "name": "ecs-demo" + } + ] + }, + "startedBy": "ecs-svc/9223370608528463088", + "taskArn": "arn:aws:ecs:::task/c5cba4eb-5dad-405e-96db-71ef8eefe6a8", + "taskDefinitionArn": "arn:aws:ecs:::task-definition/amazon-ecs-sample:1" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example provides a description of the specified task, using the task UUID as an identifier.", + "id": "a90b0cde-f965-4946-b55e-cfd8cc54e827", + "title": "To describe a task" + } + ], + "ListAccountSettings": [ + { + "input": { + "effectiveSettings": true + }, + "output": { + "settings": [ + { + "name": "containerInstanceLongArnFormat", + "value": "disabled", + "principalArn": "arn:aws:iam:::user/principalName" + }, + { + "name": "serviceLongArnFormat", + "value": "enabled", + "principalArn": "arn:aws:iam:::user/principalName" + }, + { + "name": "taskLongArnFormat", + "value": "disabled", + "principalArn": "arn:aws:iam:::user/principalName" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example displays the effective account settings for your account.", + "id": "to-view-your-account-settings-1549524118170", + "title": "To view your effective account settings" + }, + { + "input": { + "effectiveSettings": true, + "principalArn": "arn:aws:iam:::user/principalName" + }, + "output": { + "settings": [ + { + "name": "containerInstanceLongArnFormat", + "value": "disabled", + "principalArn": "arn:aws:iam:::user/principalName" + }, + { + "name": "serviceLongArnFormat", + "value": "enabled", + "principalArn": "arn:aws:iam:::user/principalName" + }, + { + "name": "taskLongArnFormat", + "value": "disabled", + "principalArn": "arn:aws:iam:::user/principalName" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example displays the effective account settings for the specified user or role.", + "id": "to-view-the-account-settings-for-a-specific-iam-user-or-iam-role-1549524237932", + "title": "To view the effective account settings for a specific IAM user or IAM role" + } + ], + "ListClusters": [ + { + "input": { + }, + "output": { + "clusterArns": [ + "arn:aws:ecs:us-east-1::cluster/test", + "arn:aws:ecs:us-east-1::cluster/default" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists all of your available clusters in your default region.", + "id": "e337d059-134f-4125-ba8e-4f499139facf", + "title": "To list your available clusters" + } + ], + "ListContainerInstances": [ + { + "input": { + "cluster": "default" + }, + "output": { + "containerInstanceArns": [ + "arn:aws:ecs:us-east-1::container-instance/f6bbb147-5370-4ace-8c73-c7181ded911f", + "arn:aws:ecs:us-east-1::container-instance/ffe3d344-77e2-476c-a4d0-bf560ad50acb" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists all of your available container instances in the specified cluster in your default region.", + "id": "62a82a94-713c-4e18-8420-1d2b2ba9d484", + "title": "To list your available container instances in a cluster" + } + ], + "ListServices": [ + { + "input": { + }, + "output": { + "serviceArns": [ + "arn:aws:ecs:us-east-1:012345678910:service/my-http-service" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists the services running in the default cluster for an account.", + "id": "1d9a8037-4e0e-4234-a528-609656809a3a", + "title": "To list the services in a cluster" + } + ], + "ListTagsForResource": [ + { + "input": { + "resourceArn": "arn:aws:ecs:region:aws_account_id:cluster/dev" + }, + "output": { + "tags": [ + { + "key": "team", + "value": "dev" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists the tags for the 'dev' cluster.", + "id": "to-list-the-tags-for-a-cluster-1540582700259", + "title": "To list the tags for a cluster." + } + ], + "ListTaskDefinitionFamilies": [ + { + "input": { + }, + "output": { + "families": [ + "node-js-app", + "web-timer", + "hpcc", + "hpcc-c4-8xlarge" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists all of your registered task definition families.", + "id": "b5c89769-1d94-4ca2-a79e-8069103c7f75", + "title": "To list your registered task definition families" + }, + { + "input": { + "familyPrefix": "hpcc" + }, + "output": { + "families": [ + "hpcc", + "hpcc-c4-8xlarge" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists the task definition revisions that start with \"hpcc\".", + "id": "8a4cf9a6-42c1-4fe3-852d-99ac8968e11b", + "title": "To filter your registered task definition families" + } + ], + "ListTaskDefinitions": [ + { + "input": { + }, + "output": { + "taskDefinitionArns": [ + "arn:aws:ecs:us-east-1::task-definition/sleep300:2", + "arn:aws:ecs:us-east-1::task-definition/sleep360:1", + "arn:aws:ecs:us-east-1::task-definition/wordpress:3", + "arn:aws:ecs:us-east-1::task-definition/wordpress:4", + "arn:aws:ecs:us-east-1::task-definition/wordpress:5", + "arn:aws:ecs:us-east-1::task-definition/wordpress:6" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists all of your registered task definitions.", + "id": "b381ebaf-7eba-4d60-b99b-7f6ae49d3d60", + "title": "To list your registered task definitions" + }, + { + "input": { + "familyPrefix": "wordpress" + }, + "output": { + "taskDefinitionArns": [ + "arn:aws:ecs:us-east-1::task-definition/wordpress:3", + "arn:aws:ecs:us-east-1::task-definition/wordpress:4", + "arn:aws:ecs:us-east-1::task-definition/wordpress:5", + "arn:aws:ecs:us-east-1::task-definition/wordpress:6" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists the task definition revisions of a specified family.", + "id": "734e7afd-753a-4bc2-85d0-badddce10910", + "title": "To list the registered task definitions in a family" + } + ], + "ListTasks": [ + { + "input": { + "cluster": "default" + }, + "output": { + "taskArns": [ + "arn:aws:ecs:us-east-1:012345678910:task/0cc43cdb-3bee-4407-9c26-c0e6ea5bee84", + "arn:aws:ecs:us-east-1:012345678910:task/6b809ef6-c67e-4467-921f-ee261c15a0a1" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists all of the tasks in a cluster.", + "id": "9a6ec707-1a77-45d0-b2eb-516b5dd9e924", + "title": "To list the tasks in a cluster" + }, + { + "input": { + "cluster": "default", + "containerInstance": "f6bbb147-5370-4ace-8c73-c7181ded911f" + }, + "output": { + "taskArns": [ + "arn:aws:ecs:us-east-1:012345678910:task/0cc43cdb-3bee-4407-9c26-c0e6ea5bee84" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists the tasks of a specified container instance. Specifying a ``containerInstance`` value limits the results to tasks that belong to that container instance.", + "id": "024bf3b7-9cbb-44e3-848f-9d074e1fecce", + "title": "To list the tasks on a particular container instance" + } + ], + "PutAccountSetting": [ + { + "input": { + "name": "serviceLongArnFormat", + "value": "enabled" + }, + "output": { + "setting": { + "name": "serviceLongArnFormat", + "value": "enabled", + "principalArn": "arn:aws:iam:::user/principalName" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies your account settings to opt in to the new ARN and resource ID format for Amazon ECS services. If you’re using this command as the root user, then changes apply to the entire AWS account, unless an IAM user or role explicitly overrides these settings for themselves.", + "id": "to-modify-the-account-settings-for-your-iam-user-account-1549523130939", + "title": "To modify your account settings" + }, + { + "input": { + "name": "containerInstanceLongArnFormat", + "value": "enabled", + "principalArn": "arn:aws:iam:::user/principalName" + }, + "output": { + "setting": { + "name": "containerInstanceLongArnFormat", + "value": "enabled", + "principalArn": "arn:aws:iam:::user/principalName" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the account setting for a specific IAM user or IAM role to opt in to the new ARN and resource ID format for Amazon ECS container instances. If you’re using this command as the root user, then changes apply to the entire AWS account, unless an IAM user or role explicitly overrides these settings for themselves.", + "id": "to-modify-the-account-settings-for-a-specific-iam-user-or-iam-role-1549523518390", + "title": "To modify the account settings for a specific IAM user or IAM role" + } + ], + "PutAccountSettingDefault": [ + { + "input": { + "name": "serviceLongArnFormat", + "value": "enabled" + }, + "output": { + "setting": { + "name": "serviceLongArnFormat", + "value": "enabled", + "principalArn": "arn:aws:iam:::root" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the default account setting for the specified resource for all IAM users or roles on an account. These changes apply to the entire AWS account, unless an IAM user or role explicitly overrides these settings for themselves.", + "id": "to-modify-the-default-account-settings-for-all-iam-users-or-roles-on-your-account-1549523794603", + "title": "To modify the default account settings for all IAM users or roles on an account" + } + ], + "RegisterTaskDefinition": [ + { + "input": { + "containerDefinitions": [ + { + "name": "sleep", + "command": [ + "sleep", + "360" + ], + "cpu": 10, + "essential": true, + "image": "busybox", + "memory": 10 + } + ], + "family": "sleep360", + "taskRoleArn": "", + "volumes": [ + + ] + }, + "output": { + "taskDefinition": { + "containerDefinitions": [ + { + "name": "sleep", + "command": [ + "sleep", + "360" + ], + "cpu": 10, + "environment": [ + + ], + "essential": true, + "image": "busybox", + "memory": 10, + "mountPoints": [ + + ], + "portMappings": [ + + ], + "volumesFrom": [ + + ] + } + ], + "family": "sleep360", + "revision": 1, + "taskDefinitionArn": "arn:aws:ecs:us-east-1::task-definition/sleep360:19", + "volumes": [ + + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example registers a task definition to the specified family.", + "id": "to-register-a-task-definition-1470764550877", + "title": "To register a task definition" + } + ], + "RunTask": [ + { + "input": { + "cluster": "default", + "taskDefinition": "sleep360:1" + }, + "output": { + "tasks": [ + { + "containerInstanceArn": "arn:aws:ecs:us-east-1::container-instance/ffe3d344-77e2-476c-a4d0-bf560ad50acb", + "containers": [ + { + "name": "sleep", + "containerArn": "arn:aws:ecs:us-east-1::container/58591c8e-be29-4ddf-95aa-ee459d4c59fd", + "lastStatus": "PENDING", + "taskArn": "arn:aws:ecs:us-east-1::task/a9f21ea7-c9f5-44b1-b8e6-b31f50ed33c0" + } + ], + "desiredStatus": "RUNNING", + "lastStatus": "PENDING", + "overrides": { + "containerOverrides": [ + { + "name": "sleep" + } + ] + }, + "taskArn": "arn:aws:ecs:us-east-1::task/a9f21ea7-c9f5-44b1-b8e6-b31f50ed33c0", + "taskDefinitionArn": "arn:aws:ecs:us-east-1::task-definition/sleep360:1" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example runs the specified task definition on your default cluster.", + "id": "6f238c83-a133-42cd-ab3d-abeca0560445", + "title": "To run a task on your default cluster" + } + ], + "TagResource": [ + { + "input": { + "resourceArn": "arn:aws:ecs:region:aws_account_id:cluster/dev", + "tags": [ + { + "key": "team", + "value": "dev" + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example tags the 'dev' cluster with key 'team' and value 'dev'.", + "id": "to-tag-a-cluster-1540581863751", + "title": "To tag a cluster." + } + ], + "UntagResource": [ + { + "input": { + "resourceArn": "arn:aws:ecs:region:aws_account_id:cluster/dev", + "tagKeys": [ + "team" + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the 'team' tag from the 'dev' cluster.", + "id": "to-untag-a-cluster-1540582546056", + "title": "To untag a cluster." + } + ], + "UpdateService": [ + { + "input": { + "service": "my-http-service", + "taskDefinition": "amazon-ecs-sample" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example updates the my-http-service service to use the amazon-ecs-sample task definition.", + "id": "cc9e8900-0cc2-44d2-8491-64d1d3d37887", + "title": "To change the task definition used in a service" + }, + { + "input": { + "desiredCount": 10, + "service": "my-http-service" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example updates the desired count of the my-http-service service to 10.", + "id": "9581d6c5-02e3-4140-8cc1-5a4301586633", + "title": "To change the number of tasks in a service" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/paginators-1.json new file mode 100644 index 0000000..cd66d4a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/paginators-1.json @@ -0,0 +1,58 @@ +{ + "pagination": { + "ListClusters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "clusterArns" + }, + "ListContainerInstances": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "containerInstanceArns" + }, + "ListTaskDefinitions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "taskDefinitionArns" + }, + "ListTaskDefinitionFamilies": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "families" + }, + "ListTasks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "taskArns" + }, + "ListServices": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "serviceArns" + }, + "ListAccountSettings": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "settings" + }, + "ListAttributes": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "attributes" + }, + "ListServicesByNamespace": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "serviceArns" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/service-2.json.gz new file mode 100644 index 0000000..1a20aa5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/waiters-2.json new file mode 100644 index 0000000..41114a3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ecs/2014-11-13/waiters-2.json @@ -0,0 +1,174 @@ +{ + "version" : 2, + "waiters" : { + "DaemonActive" : { + "delay" : 15, + "maxAttempts" : 8, + "operation" : "DescribeDaemon", + "acceptors" : [ { + "matcher" : "path", + "argument" : "daemon.status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "daemon.status", + "state" : "failure", + "expected" : "DELETE_IN_PROGRESS" + } ] + }, + "DaemonDeploymentStopped" : { + "delay" : 15, + "maxAttempts" : 8, + "operation" : "DescribeDaemonDeployments", + "acceptors" : [ { + "matcher" : "pathAll", + "argument" : "daemonDeployments[].status", + "state" : "success", + "expected" : "STOPPED" + }, { + "matcher" : "pathAny", + "argument" : "failures[].reason", + "state" : "failure", + "expected" : "MISSING" + } ] + }, + "DaemonDeploymentSuccessful" : { + "delay" : 15, + "maxAttempts" : 8, + "operation" : "DescribeDaemonDeployments", + "acceptors" : [ { + "matcher" : "pathAll", + "argument" : "daemonDeployments[].status", + "state" : "success", + "expected" : "SUCCESSFUL" + }, { + "matcher" : "pathAny", + "argument" : "daemonDeployments[].status", + "state" : "failure", + "expected" : "STOPPED" + }, { + "matcher" : "pathAny", + "argument" : "daemonDeployments[].status", + "state" : "failure", + "expected" : "ROLLBACK_FAILED" + }, { + "matcher" : "pathAny", + "argument" : "daemonDeployments[].status", + "state" : "failure", + "expected" : "ROLLBACK_SUCCESSFUL" + }, { + "matcher" : "pathAny", + "argument" : "failures[].reason", + "state" : "failure", + "expected" : "MISSING" + } ] + }, + "DaemonTaskDefinitionActive" : { + "delay" : 15, + "maxAttempts" : 8, + "operation" : "DescribeDaemonTaskDefinition", + "acceptors" : [ { + "matcher" : "path", + "argument" : "daemonTaskDefinition.status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "daemonTaskDefinition.status", + "state" : "failure", + "expected" : "DELETE_IN_PROGRESS" + }, { + "matcher" : "path", + "argument" : "daemonTaskDefinition.status", + "state" : "failure", + "expected" : "DELETED" + } ] + }, + "DaemonTaskDefinitionDeleted" : { + "delay" : 15, + "maxAttempts" : 8, + "operation" : "DescribeDaemonTaskDefinition", + "acceptors" : [ { + "matcher" : "path", + "argument" : "daemonTaskDefinition.status", + "state" : "success", + "expected" : "DELETED" + } ] + }, + "ServicesInactive" : { + "delay" : 15, + "maxAttempts" : 40, + "operation" : "DescribeServices", + "acceptors" : [ { + "matcher" : "pathAny", + "argument" : "failures[].reason", + "state" : "failure", + "expected" : "MISSING" + }, { + "matcher" : "pathAny", + "argument" : "services[].status", + "state" : "success", + "expected" : "INACTIVE" + } ] + }, + "ServicesStable" : { + "delay" : 15, + "maxAttempts" : 40, + "operation" : "DescribeServices", + "acceptors" : [ { + "matcher" : "pathAny", + "argument" : "failures[].reason", + "state" : "failure", + "expected" : "MISSING" + }, { + "matcher" : "pathAny", + "argument" : "services[].status", + "state" : "failure", + "expected" : "DRAINING" + }, { + "matcher" : "pathAny", + "argument" : "services[].status", + "state" : "failure", + "expected" : "INACTIVE" + }, { + "matcher" : "path", + "argument" : "length(services[?!(length(deployments) == `1` && runningCount == desiredCount)]) == `0`", + "state" : "success", + "expected" : true + } ] + }, + "TasksRunning" : { + "delay" : 6, + "maxAttempts" : 100, + "operation" : "DescribeTasks", + "acceptors" : [ { + "matcher" : "pathAny", + "argument" : "tasks[].lastStatus", + "state" : "failure", + "expected" : "STOPPED" + }, { + "matcher" : "pathAny", + "argument" : "failures[].reason", + "state" : "failure", + "expected" : "MISSING" + }, { + "matcher" : "pathAll", + "argument" : "tasks[].lastStatus", + "state" : "success", + "expected" : "RUNNING" + } ] + }, + "TasksStopped" : { + "delay" : 6, + "maxAttempts" : 100, + "operation" : "DescribeTasks", + "acceptors" : [ { + "matcher" : "pathAll", + "argument" : "tasks[].lastStatus", + "state" : "success", + "expected" : "STOPPED" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/efs/2015-02-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/efs/2015-02-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..57dfc61 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/efs/2015-02-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/efs/2015-02-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/efs/2015-02-01/examples-1.json new file mode 100644 index 0000000..f3c75b3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/efs/2015-02-01/examples-1.json @@ -0,0 +1,294 @@ +{ + "version": "1.0", + "examples": { + "CreateFileSystem": [ + { + "input": { + "Backup": true, + "CreationToken": "tokenstring", + "Encrypted": true, + "PerformanceMode": "generalPurpose", + "Tags": [ + { + "Key": "Name", + "Value": "MyFileSystem" + } + ] + }, + "output": { + "CreationTime": "1481841524.0", + "CreationToken": "tokenstring", + "Encrypted": true, + "FileSystemId": "fs-01234567", + "LifeCycleState": "creating", + "NumberOfMountTargets": 0, + "OwnerId": "012345678912", + "PerformanceMode": "generalPurpose", + "SizeInBytes": { + "Value": 0 + }, + "Tags": [ + { + "Key": "Name", + "Value": "MyFileSystem" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation creates a new, encrypted file system with automatic backups enabled, and the default generalpurpose performance mode.", + "id": "to-create-a-new-file-system-1481840798547", + "title": "To create a new file system" + } + ], + "CreateMountTarget": [ + { + "input": { + "FileSystemId": "fs-01234567", + "SubnetId": "subnet-1234abcd" + }, + "output": { + "FileSystemId": "fs-01234567", + "IpAddress": "192.0.0.2", + "LifeCycleState": "creating", + "MountTargetId": "fsmt-12340abc", + "NetworkInterfaceId": "eni-cedf6789", + "OwnerId": "012345678912", + "SubnetId": "subnet-1234abcd" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation creates a new mount target for an EFS file system.", + "id": "to-create-a-new-mount-target-1481842289329", + "title": "To create a new mount target" + } + ], + "CreateTags": [ + { + "input": { + "FileSystemId": "fs-01234567", + "Tags": [ + { + "Key": "Name", + "Value": "MyFileSystem" + } + ] + }, + "comments": { + }, + "description": "This operation creates a new tag for an EFS file system.", + "id": "to-create-a-new-tag-1481843409357", + "title": "To create a new tag" + } + ], + "DeleteFileSystem": [ + { + "input": { + "FileSystemId": "fs-01234567" + }, + "comments": { + }, + "description": "This operation deletes an EFS file system.", + "id": "to-delete-a-file-system-1481847318348", + "title": "To delete a file system" + } + ], + "DeleteMountTarget": [ + { + "input": { + "MountTargetId": "fsmt-12340abc" + }, + "comments": { + }, + "description": "This operation deletes a mount target.", + "id": "to-delete-a-mount-target-1481847635607", + "title": "To delete a mount target" + } + ], + "DeleteTags": [ + { + "input": { + "FileSystemId": "fs-01234567", + "TagKeys": [ + "Name" + ] + }, + "comments": { + }, + "description": "This operation deletes tags for an EFS file system.", + "id": "to-delete-tags-for-an-efs-file-system-1481848189061", + "title": "To delete tags for an EFS file system" + } + ], + "DescribeFileSystems": [ + { + "input": { + }, + "output": { + "FileSystems": [ + { + "CreationTime": "1481841524.0", + "CreationToken": "tokenstring", + "FileSystemId": "fs-01234567", + "LifeCycleState": "available", + "Name": "MyFileSystem", + "NumberOfMountTargets": 1, + "OwnerId": "012345678912", + "PerformanceMode": "generalPurpose", + "SizeInBytes": { + "Value": 6144 + }, + "Tags": [ + { + "Key": "Name", + "Value": "MyFileSystem" + } + ] + } + ] + }, + "comments": { + }, + "description": "This operation describes all of the EFS file systems in an account.", + "id": "to-describe-an-efs-file-system-1481848448460", + "title": "To describe an EFS file system" + } + ], + "DescribeLifecycleConfiguration": [ + { + "input": { + "FileSystemId": "fs-01234567" + }, + "output": { + "LifecyclePolicies": [ + { + "TransitionToIA": "AFTER_30_DAYS" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation describes a file system's LifecycleConfiguration. EFS lifecycle management uses the LifecycleConfiguration object to identify which files to move to the EFS Infrequent Access (IA) storage class. ", + "id": "to-describe-the-lifecycle-configuration-for-a-file-system-1551200664502", + "title": "To describe the lifecycle configuration for a file system" + } + ], + "DescribeMountTargetSecurityGroups": [ + { + "input": { + "MountTargetId": "fsmt-12340abc" + }, + "output": { + "SecurityGroups": [ + "sg-4567abcd" + ] + }, + "comments": { + }, + "description": "This operation describes all of the security groups for a file system's mount target.", + "id": "to-describe-the-security-groups-for-a-mount-target-1481849317823", + "title": "To describe the security groups for a mount target" + } + ], + "DescribeMountTargets": [ + { + "input": { + "FileSystemId": "fs-01234567" + }, + "output": { + "MountTargets": [ + { + "FileSystemId": "fs-01234567", + "IpAddress": "192.0.0.2", + "LifeCycleState": "available", + "MountTargetId": "fsmt-12340abc", + "NetworkInterfaceId": "eni-cedf6789", + "OwnerId": "012345678912", + "SubnetId": "subnet-1234abcd" + } + ] + }, + "comments": { + }, + "description": "This operation describes all of a file system's mount targets.", + "id": "to-describe-the-mount-targets-for-a-file-system-1481849958584", + "title": "To describe the mount targets for a file system" + } + ], + "DescribeTags": [ + { + "input": { + "FileSystemId": "fs-01234567" + }, + "output": { + "Tags": [ + { + "Key": "Name", + "Value": "MyFileSystem" + } + ] + }, + "comments": { + }, + "description": "This operation describes all of a file system's tags.", + "id": "to-describe-the-tags-for-a-file-system-1481850497090", + "title": "To describe the tags for a file system" + } + ], + "ModifyMountTargetSecurityGroups": [ + { + "input": { + "MountTargetId": "fsmt-12340abc", + "SecurityGroups": [ + "sg-abcd1234" + ] + }, + "comments": { + }, + "description": "This operation modifies the security groups associated with a mount target for a file system.", + "id": "to-modify-the-security-groups-associated-with-a-mount-target-for-a-file-system-1481850772562", + "title": "To modify the security groups associated with a mount target for a file system" + } + ], + "PutLifecycleConfiguration": [ + { + "input": { + "FileSystemId": "fs-01234567", + "LifecyclePolicies": [ + { + "TransitionToIA": "AFTER_30_DAYS" + } + ] + }, + "output": { + "LifecyclePolicies": [ + { + "TransitionToIA": "AFTER_30_DAYS" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation enables lifecycle management on a file system by creating a new LifecycleConfiguration object. A LifecycleConfiguration object defines when files in an Amazon EFS file system are automatically transitioned to the lower-cost EFS Infrequent Access (IA) storage class. A LifecycleConfiguration applies to all files in a file system.", + "id": "creates-a-new-lifecycleconfiguration-object-for-a-file-system-1551201594692", + "title": "Creates a new lifecycleconfiguration object for a file system" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/efs/2015-02-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/efs/2015-02-01/paginators-1.json new file mode 100644 index 0000000..047d3e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/efs/2015-02-01/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "DescribeFileSystems": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "FileSystems" + }, + "DescribeMountTargets": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "MountTargets" + }, + "DescribeTags": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "Tags" + }, + "DescribeAccessPoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AccessPoints" + }, + "DescribeReplicationConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Replications" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/efs/2015-02-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/efs/2015-02-01/service-2.json.gz new file mode 100644 index 0000000..44b5cae Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/efs/2015-02-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/eks-auth/2023-11-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/eks-auth/2023-11-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..9373eed Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/eks-auth/2023-11-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/eks-auth/2023-11-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/eks-auth/2023-11-26/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/eks-auth/2023-11-26/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/eks-auth/2023-11-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/eks-auth/2023-11-26/service-2.json.gz new file mode 100644 index 0000000..764ef02 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/eks-auth/2023-11-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/eks-auth/2023-11-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/eks-auth/2023-11-26/waiters-2.json new file mode 100644 index 0000000..4b20636 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/eks-auth/2023-11-26/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1a310ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/examples-1.json new file mode 100644 index 0000000..8ea2517 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/examples-1.json @@ -0,0 +1,135 @@ +{ + "version": "1.0", + "examples": { + "CreateCluster": [ + { + "input": { + "version": "1.10", + "name": "prod", + "clientRequestToken": "1d2129a1-3d38-460a-9756-e5b91fddb951", + "resourcesVpcConfig": { + "securityGroupIds": [ + "sg-6979fe18" + ], + "subnetIds": [ + "subnet-6782e71e", + "subnet-e7e761ac" + ] + }, + "roleArn": "arn:aws:iam::012345678910:role/eks-service-role-AWSServiceRoleForAmazonEKS-J7ONKE3BQ4PI" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates an Amazon EKS cluster called prod.", + "id": "to-create-a-new-cluster-1527868185648", + "title": "To create a new cluster" + } + ], + "DeleteCluster": [ + { + "input": { + "name": "devel" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example command deletes a cluster named `devel` in your default region.", + "id": "to-delete-a-cluster-1527868641252", + "title": "To delete a cluster" + } + ], + "DescribeCluster": [ + { + "input": { + "name": "devel" + }, + "output": { + "cluster": { + "version": "1.10", + "name": "devel", + "arn": "arn:aws:eks:us-west-2:012345678910:cluster/devel", + "certificateAuthority": { + "data": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNE1EVXpNVEl6TVRFek1Wb1hEVEk0TURVeU9ESXpNVEV6TVZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTTZWCjVUaG4rdFcySm9Xa2hQMzRlVUZMNitaRXJOZGIvWVdrTmtDdWNGS2RaaXl2TjlMVmdvUmV2MjlFVFZlN1ZGbSsKUTJ3ZURyRXJiQyt0dVlibkFuN1ZLYmE3ay9hb1BHekZMdmVnb0t6b0M1N2NUdGVwZzRIazRlK2tIWHNaME10MApyb3NzcjhFM1ROeExETnNJTThGL1cwdjhsTGNCbWRPcjQyV2VuTjFHZXJnaDNSZ2wzR3JIazBnNTU0SjFWenJZCm9hTi8zODFUczlOTFF2QTBXb0xIcjBFRlZpTFdSZEoyZ3lXaC9ybDVyOFNDOHZaQXg1YW1BU0hVd01aTFpWRC8KTDBpOW4wRVM0MkpVdzQyQmxHOEdpd3NhTkJWV3lUTHZKclNhRXlDSHFtVVZaUTFDZkFXUjl0L3JleVVOVXM3TApWV1FqM3BFbk9RMitMSWJrc0RzQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFNZ3RsQ1dIQ2U2YzVHMXl2YlFTS0Q4K2hUalkKSm1NSG56L2EvRGt0WG9YUjFVQzIrZUgzT1BZWmVjRVZZZHVaSlZCckNNQ2VWR0ZkeWdBYlNLc1FxWDg0S2RXbAp1MU5QaERDSmEyRHliN2pVMUV6VThTQjFGZUZ5ZFE3a0hNS1E1blpBRVFQOTY4S01hSGUrSm0yQ2x1UFJWbEJVCjF4WlhTS1gzTVZ0K1Q0SU1EV2d6c3JRSjVuQkRjdEtLcUZtM3pKdVVubHo5ZEpVckdscEltMjVJWXJDckxYUFgKWkUwRUtRNWEzMHhkVWNrTHRGQkQrOEtBdFdqSS9yZUZPNzM1YnBMdVoyOTBaNm42QlF3elRrS0p4cnhVc3QvOAppNGsxcnlsaUdWMm5SSjBUYjNORkczNHgrYWdzYTRoSTFPbU90TFM0TmgvRXJxT3lIUXNDc2hEQUtKUT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=" + }, + "createdAt": 1527807879.988, + "endpoint": "https://A0DCCD80A04F01705DD065655C30CC3D.yl4.us-west-2.eks.amazonaws.com", + "resourcesVpcConfig": { + "securityGroupIds": [ + "sg-6979fe18" + ], + "subnetIds": [ + "subnet-6782e71e", + "subnet-e7e761ac" + ], + "vpcId": "vpc-950809ec" + }, + "roleArn": "arn:aws:iam::012345678910:role/eks-service-role-AWSServiceRoleForAmazonEKS-J7ONKE3BQ4PI", + "status": "ACTIVE" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example command provides a description of the specified cluster in your default region.", + "id": "to-describe-a-cluster-1527868708512", + "title": "To describe a cluster" + } + ], + "ListClusters": [ + { + "input": { + }, + "output": { + "clusters": [ + "devel", + "prod" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example command lists all of your available clusters in your default region.", + "id": "to-list-your-available-clusters-1527868801040", + "title": "To list your available clusters" + } + ], + "ListTagsForResource": [ + { + "input": { + "resourceArn": "arn:aws:eks:us-west-2:012345678910:cluster/beta" + }, + "output": { + "tags": { + "aws:tag:domain": "beta" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists all of the tags for the `beta` cluster.", + "id": "to-list-tags-for-a-cluster-1568666903378", + "title": "To list tags for a cluster" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/paginators-1.json new file mode 100644 index 0000000..c3cb416 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/paginators-1.json @@ -0,0 +1,104 @@ +{ + "pagination": { + "ListClusters": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "clusters" + }, + "ListUpdates": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "updateIds" + }, + "ListNodegroups": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "nodegroups" + }, + "ListFargateProfiles": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "fargateProfileNames" + }, + "DescribeAddonVersions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "addons" + }, + "ListAddons": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "addons" + }, + "ListIdentityProviderConfigs": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "identityProviderConfigs" + }, + "ListEksAnywhereSubscriptions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "subscriptions" + }, + "ListPodIdentityAssociations": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "associations" + }, + "ListAccessEntries": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "accessEntries" + }, + "ListAccessPolicies": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "accessPolicies" + }, + "ListAssociatedAccessPolicies": { + "input_token": "nextToken", + "limit_key": "maxResults", + "non_aggregate_keys": [ + "clusterName", + "principalArn" + ], + "output_token": "nextToken", + "result_key": "associatedAccessPolicies" + }, + "ListInsights": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "insights" + }, + "DescribeClusterVersions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "clusterVersions" + }, + "ListCapabilities": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "capabilities" + }, + "ListCertificateAuthorities": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "certificateAuthorities" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/service-2.json.gz new file mode 100644 index 0000000..e9c204c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/service-2.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/service-2.sdk-extras.json new file mode 100644 index 0000000..b636c21 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/service-2.sdk-extras.json @@ -0,0 +1,8 @@ +{ + "version": 1.0, + "merge": { + "metadata": { + "serviceId":"EKS" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/waiters-2.json new file mode 100644 index 0000000..0db7f42 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/eks/2017-11-01/waiters-2.json @@ -0,0 +1,202 @@ +{ + "version": 2, + "waiters": { + "ClusterActive": { + "delay": 30, + "operation": "DescribeCluster", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "DELETING", + "matcher": "path", + "state": "failure", + "argument": "cluster.status" + }, + { + "expected": "FAILED", + "matcher": "path", + "state": "failure", + "argument": "cluster.status" + }, + { + "expected": "ACTIVE", + "matcher": "path", + "state": "success", + "argument": "cluster.status" + } + ] + }, + "ClusterDeleted": { + "delay": 30, + "operation": "DescribeCluster", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "ACTIVE", + "matcher": "path", + "state": "failure", + "argument": "cluster.status" + }, + { + "expected": "CREATING", + "matcher": "path", + "state": "failure", + "argument": "cluster.status" + }, + { + "expected": "PENDING", + "matcher": "path", + "state": "failure", + "argument": "cluster.status" + }, + { + "expected": "ResourceNotFoundException", + "matcher": "error", + "state": "success" + } + ] + }, + "NodegroupActive": { + "delay": 30, + "operation": "DescribeNodegroup", + "maxAttempts": 80, + "acceptors": [ + { + "expected": "CREATE_FAILED", + "matcher": "path", + "state": "failure", + "argument": "nodegroup.status" + }, + { + "expected": "ACTIVE", + "matcher": "path", + "state": "success", + "argument": "nodegroup.status" + } + ] + }, + "NodegroupDeleted": { + "delay": 30, + "operation": "DescribeNodegroup", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "DELETE_FAILED", + "matcher": "path", + "state": "failure", + "argument": "nodegroup.status" + }, + { + "expected": "ResourceNotFoundException", + "matcher": "error", + "state": "success" + } + ] + }, + "AddonActive": { + "delay": 10, + "operation": "DescribeAddon", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "CREATE_FAILED", + "matcher": "path", + "state": "failure", + "argument": "addon.status" + }, + { + "expected": "DEGRADED", + "matcher": "path", + "state": "failure", + "argument": "addon.status" + }, + { + "expected": "ACTIVE", + "matcher": "path", + "state": "success", + "argument": "addon.status" + } + ] + }, + "AddonDeleted": { + "delay": 10, + "operation": "DescribeAddon", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "DELETE_FAILED", + "matcher": "path", + "state": "failure", + "argument": "addon.status" + }, + { + "expected": "ResourceNotFoundException", + "matcher": "error", + "state": "success" + } + ] + }, + "FargateProfileActive": { + "delay": 10, + "operation": "DescribeFargateProfile", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "CREATE_FAILED", + "matcher": "path", + "state": "failure", + "argument": "fargateProfile.status" + }, + { + "expected": "ACTIVE", + "matcher": "path", + "state": "success", + "argument": "fargateProfile.status" + } + ] + }, + "FargateProfileDeleted": { + "delay": 30, + "operation": "DescribeFargateProfile", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "DELETE_FAILED", + "matcher": "path", + "state": "failure", + "argument": "fargateProfile.status" + }, + { + "expected": "ResourceNotFoundException", + "matcher": "error", + "state": "success" + } + ] + }, + "CertificateAuthorityUpdateComplete": { + "delay": 30, + "operation": "DescribeUpdate", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "Failed", + "matcher": "path", + "state": "failure", + "argument": "update.status" + }, + { + "expected": "Cancelled", + "matcher": "path", + "state": "failure", + "argument": "update.status" + }, + { + "expected": "Successful", + "matcher": "path", + "state": "success", + "argument": "update.status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticache/2014-09-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2014-09-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ce2f8b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2014-09-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticache/2014-09-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2014-09-30/paginators-1.json new file mode 100644 index 0000000..8724740 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2014-09-30/paginators-1.json @@ -0,0 +1,76 @@ +{ + "pagination": { + "DescribeCacheClusters": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "CacheClusters" + }, + "DescribeCacheEngineVersions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "CacheEngineVersions" + }, + "DescribeCacheParameterGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "CacheParameterGroups" + }, + "DescribeCacheParameters": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Parameters" + }, + "DescribeCacheSecurityGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "CacheSecurityGroups" + }, + "DescribeCacheSubnetGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "CacheSubnetGroups" + }, + "DescribeEngineDefaultParameters": { + "input_token": "Marker", + "output_token": "EngineDefaults.Marker", + "limit_key": "MaxRecords", + "result_key": "EngineDefaults.Parameters" + }, + "DescribeEvents": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Events" + }, + "DescribeReservedCacheNodes": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ReservedCacheNodes" + }, + "DescribeReservedCacheNodesOfferings": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ReservedCacheNodesOfferings" + }, + "DescribeReplicationGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ReplicationGroups" + }, + "DescribeSnapshots": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Snapshots" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticache/2014-09-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2014-09-30/service-2.json.gz new file mode 100644 index 0000000..74b1517 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2014-09-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticache/2014-09-30/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2014-09-30/waiters-2.json new file mode 100644 index 0000000..ccb904a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2014-09-30/waiters-2.json @@ -0,0 +1,139 @@ +{ + "version": 2, + "waiters": { + "CacheClusterAvailable": { + "delay": 30, + "operation": "DescribeCacheClusters", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "CacheClusters[].CacheClusterStatus" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "CacheClusters[].CacheClusterStatus" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "CacheClusters[].CacheClusterStatus" + }, + { + "expected": "incompatible-network", + "matcher": "pathAny", + "state": "failure", + "argument": "CacheClusters[].CacheClusterStatus" + }, + { + "expected": "restore-failed", + "matcher": "pathAny", + "state": "failure", + "argument": "CacheClusters[].CacheClusterStatus" + } + ] + }, + "CacheClusterDeleted": { + "delay": 30, + "operation": "DescribeCacheClusters", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "CacheClusterNotFound", + "matcher": "error", + "state": "success" + }, + { + "expected": "creating", + "matcher": "pathAny", + "state": "failure", + "argument": "CacheClusters[].CacheClusterStatus" + }, + { + "expected": "modifying", + "matcher": "pathAny", + "state": "failure", + "argument": "CacheClusters[].CacheClusterStatus" + }, + { + "expected": "rebooting", + "matcher": "pathAny", + "state": "failure", + "argument": "CacheClusters[].CacheClusterStatus" + } + ] + }, + "ReplicationGroupAvailable": { + "delay": 30, + "operation": "DescribeReplicationGroups", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "ReplicationGroups[].Status" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "ReplicationGroups[].Status" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "ReplicationGroups[].Status" + }, + { + "expected": "incompatible-network", + "matcher": "pathAny", + "state": "failure", + "argument": "ReplicationGroups[].Status" + }, + { + "expected": "restore-failed", + "matcher": "pathAny", + "state": "failure", + "argument": "ReplicationGroups[].Status" + } + ] + }, + "ReplicationGroupDeleted": { + "delay": 30, + "operation": "DescribeReplicationGroups", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "ReplicationGroupNotFoundFault", + "matcher": "error", + "state": "success" + }, + { + "expected": "creating", + "matcher": "pathAny", + "state": "failure", + "argument": "ReplicationGroups[].Status" + }, + { + "expected": "modifying", + "matcher": "pathAny", + "state": "failure", + "argument": "ReplicationGroups[].Status" + }, + { + "expected": "rebooting", + "matcher": "pathAny", + "state": "failure", + "argument": "ReplicationGroups[].Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b0ff67e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/examples-1.json new file mode 100644 index 0000000..f1d21bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/examples-1.json @@ -0,0 +1,3149 @@ +{ + "version": "1.0", + "examples": { + "AddTagsToResource": [ + { + "input": { + "ResourceName": "arn:aws:elasticache:us-east-1:1234567890:cluster:my-mem-cluster", + "Tags": [ + { + "Key": "APIVersion", + "Value": "20150202" + }, + { + "Key": "Service", + "Value": "ElastiCache" + } + ] + }, + "output": { + "TagList": [ + { + "Key": "APIVersion", + "Value": "20150202" + }, + { + "Key": "Service", + "Value": "ElastiCache" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Adds up to 10 tags, key/value pairs, to a cluster or snapshot resource.", + "id": "addtagstoresource-1482430264385", + "title": "AddTagsToResource" + } + ], + "AuthorizeCacheSecurityGroupIngress": [ + { + "input": { + "CacheSecurityGroupName": "my-sec-grp", + "EC2SecurityGroupName": "my-ec2-sec-grp", + "EC2SecurityGroupOwnerId": "1234567890" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Allows network ingress to a cache security group. Applications using ElastiCache must be running on Amazon EC2. Amazon EC2 security groups are used as the authorization mechanism.", + "id": "authorizecachecachesecuritygroupingress-1483046446206", + "title": "AuthorizeCacheCacheSecurityGroupIngress" + } + ], + "CopySnapshot": [ + { + "input": { + "SourceSnapshotName": "my-snapshot", + "TargetBucket": "", + "TargetSnapshotName": "my-snapshot-copy" + }, + "output": { + "Snapshot": { + "AutoMinorVersionUpgrade": true, + "CacheClusterCreateTime": "2016-12-21T22:24:04.955Z", + "CacheClusterId": "my-redis4", + "CacheNodeType": "cache.m3.large", + "CacheParameterGroupName": "default.redis3.2", + "CacheSubnetGroupName": "default", + "Engine": "redis", + "EngineVersion": "3.2.4", + "NodeSnapshots": [ + { + "CacheNodeCreateTime": "2016-12-21T22:24:04.955Z", + "CacheNodeId": "0001", + "CacheSize": "3 MB", + "SnapshotCreateTime": "2016-12-28T07:00:52Z" + } + ], + "NumCacheNodes": 1, + "Port": 6379, + "PreferredAvailabilityZone": "us-east-1c", + "PreferredMaintenanceWindow": "tue:09:30-tue:10:30", + "SnapshotName": "my-snapshot-copy", + "SnapshotRetentionLimit": 7, + "SnapshotSource": "manual", + "SnapshotStatus": "creating", + "SnapshotWindow": "07:00-08:00", + "VpcId": "vpc-3820329f3" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Copies a snapshot to a specified name.", + "id": "copysnapshot-1482961393820", + "title": "CopySnapshot" + } + ], + "CreateCacheCluster": [ + { + "input": { + "AZMode": "cross-az", + "CacheClusterId": "my-memcached-cluster", + "CacheNodeType": "cache.r3.large", + "CacheSubnetGroupName": "default", + "Engine": "memcached", + "EngineVersion": "1.4.24", + "NumCacheNodes": 2, + "Port": 11211 + }, + "output": { + "CacheCluster": { + "AutoMinorVersionUpgrade": true, + "CacheClusterId": "my-memcached-cluster", + "CacheClusterStatus": "creating", + "CacheNodeType": "cache.r3.large", + "CacheParameterGroup": { + "CacheNodeIdsToReboot": [ + + ], + "CacheParameterGroupName": "default.memcached1.4", + "ParameterApplyStatus": "in-sync" + }, + "CacheSecurityGroups": [ + + ], + "CacheSubnetGroupName": "default", + "ClientDownloadLandingPage": "https://console.aws.amazon.com/elasticache/home#client-download:", + "Engine": "memcached", + "EngineVersion": "1.4.24", + "NumCacheNodes": 2, + "PendingModifiedValues": { + }, + "PreferredAvailabilityZone": "Multiple", + "PreferredMaintenanceWindow": "wed:09:00-wed:10:00" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a Memcached cluster with 2 nodes. ", + "id": "createcachecluster-1474994727381", + "title": "CreateCacheCluster" + }, + { + "input": { + "AutoMinorVersionUpgrade": true, + "CacheClusterId": "my-redis", + "CacheNodeType": "cache.r3.larage", + "CacheSubnetGroupName": "default", + "Engine": "redis", + "EngineVersion": "3.2.4", + "NumCacheNodes": 1, + "Port": 6379, + "PreferredAvailabilityZone": "us-east-1c", + "SnapshotRetentionLimit": 7 + }, + "output": { + "CacheCluster": { + "AutoMinorVersionUpgrade": true, + "CacheClusterId": "my-redis", + "CacheClusterStatus": "creating", + "CacheNodeType": "cache.m3.large", + "CacheParameterGroup": { + "CacheNodeIdsToReboot": [ + + ], + "CacheParameterGroupName": "default.redis3.2", + "ParameterApplyStatus": "in-sync" + }, + "CacheSecurityGroups": [ + + ], + "CacheSubnetGroupName": "default", + "ClientDownloadLandingPage": "https: //console.aws.amazon.com/elasticache/home#client-download: ", + "Engine": "redis", + "EngineVersion": "3.2.4", + "NumCacheNodes": 1, + "PendingModifiedValues": { + }, + "PreferredAvailabilityZone": "us-east-1c", + "PreferredMaintenanceWindow": "fri: 05: 30-fri: 06: 30", + "SnapshotRetentionLimit": 7, + "SnapshotWindow": "10: 00-11: 00" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a Redis cluster with 1 node. ", + "id": "createcachecluster-1474994727381", + "title": "CreateCacheCluster" + } + ], + "CreateCacheParameterGroup": [ + { + "input": { + "CacheParameterGroupFamily": "redis2.8", + "CacheParameterGroupName": "custom-redis2-8", + "Description": "Custom Redis 2.8 parameter group." + }, + "output": { + "CacheParameterGroup": { + "CacheParameterGroupFamily": "redis2.8", + "CacheParameterGroupName": "custom-redis2-8", + "Description": "Custom Redis 2.8 parameter group." + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates the Amazon ElastiCache parameter group custom-redis2-8.", + "id": "createcacheparametergroup-1474997699362", + "title": "CreateCacheParameterGroup" + } + ], + "CreateCacheSecurityGroup": [ + { + "input": { + "CacheSecurityGroupName": "my-cache-sec-grp", + "Description": "Example ElastiCache security group." + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates an ElastiCache security group. ElastiCache security groups are only for clusters not running in an AWS VPC.", + "id": "createcachesecuritygroup-1483041506604", + "title": "CreateCacheSecurityGroup" + } + ], + "CreateCacheSubnetGroup": [ + { + "input": { + "CacheSubnetGroupDescription": "Sample subnet group", + "CacheSubnetGroupName": "my-sn-grp2", + "SubnetIds": [ + "subnet-6f28c982", + "subnet-bcd382f3", + "subnet-845b3e7c0" + ] + }, + "output": { + "CacheSubnetGroup": { + "CacheSubnetGroupDescription": "My subnet group.", + "CacheSubnetGroupName": "my-sn-grp", + "Subnets": [ + { + "SubnetAvailabilityZone": { + "Name": "us-east-1a" + }, + "SubnetIdentifier": "subnet-6f28c982" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1c" + }, + "SubnetIdentifier": "subnet-bcd382f3" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1b" + }, + "SubnetIdentifier": "subnet-845b3e7c0" + } + ], + "VpcId": "vpc-91280df6" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a new cache subnet group.", + "id": "createcachesubnet-1483042274558", + "title": "CreateCacheSubnet" + } + ], + "CreateReplicationGroup": [ + { + "input": { + "AutomaticFailoverEnabled": true, + "CacheNodeType": "cache.m3.medium", + "Engine": "redis", + "EngineVersion": "2.8.24", + "NumCacheClusters": 3, + "ReplicationGroupDescription": "A Redis replication group.", + "ReplicationGroupId": "my-redis-rg", + "SnapshotRetentionLimit": 30 + }, + "output": { + "ReplicationGroup": { + "AutomaticFailover": "enabling", + "Description": "A Redis replication group.", + "MemberClusters": [ + "my-redis-rg-001", + "my-redis-rg-002", + "my-redis-rg-003" + ], + "PendingModifiedValues": { + }, + "ReplicationGroupId": "my-redis-rg", + "SnapshottingClusterId": "my-redis-rg-002", + "Status": "creating" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a Redis replication group with 3 nodes.", + "id": "createcachereplicationgroup-1474998730655", + "title": "CreateCacheReplicationGroup" + }, + { + "input": { + "AutoMinorVersionUpgrade": true, + "CacheNodeType": "cache.m3.medium", + "CacheParameterGroupName": "default.redis3.2.cluster.on", + "Engine": "redis", + "EngineVersion": "3.2.4", + "NodeGroupConfiguration": [ + { + "PrimaryAvailabilityZone": "us-east-1c", + "ReplicaAvailabilityZones": [ + "us-east-1b" + ], + "ReplicaCount": 1, + "Slots": "0-8999" + }, + { + "PrimaryAvailabilityZone": "us-east-1a", + "ReplicaAvailabilityZones": [ + "us-east-1a", + "us-east-1c" + ], + "ReplicaCount": 2, + "Slots": "9000-16383" + } + ], + "NumNodeGroups": 2, + "ReplicationGroupDescription": "A multi-sharded replication group", + "ReplicationGroupId": "clustered-redis-rg", + "SnapshotRetentionLimit": 8 + }, + "output": { + "ReplicationGroup": { + "AutomaticFailover": "enabled", + "Description": "Sharded replication group", + "MemberClusters": [ + "rc-rg3-0001-001", + "rc-rg3-0001-002", + "rc-rg3-0002-001", + "rc-rg3-0002-002", + "rc-rg3-0002-003" + ], + "PendingModifiedValues": { + }, + "ReplicationGroupId": "clustered-redis-rg", + "SnapshotRetentionLimit": 8, + "SnapshotWindow": "05:30-06:30", + "Status": "creating" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a Redis (cluster mode enabled) replication group with two shards. One shard has one read replica node and the other shard has two read replicas.", + "id": "createreplicationgroup-1483657035585", + "title": "CreateReplicationGroup" + } + ], + "CreateSnapshot": [ + { + "input": { + "CacheClusterId": "onenoderedis", + "SnapshotName": "snapshot-1" + }, + "output": { + "Snapshot": { + "AutoMinorVersionUpgrade": true, + "CacheClusterCreateTime": "2017-02-03T15:43:36.278Z", + "CacheClusterId": "onenoderedis", + "CacheNodeType": "cache.m3.medium", + "CacheParameterGroupName": "default.redis3.2", + "CacheSubnetGroupName": "default", + "Engine": "redis", + "EngineVersion": "3.2.4", + "NodeSnapshots": [ + { + "CacheNodeCreateTime": "2017-02-03T15:43:36.278Z", + "CacheNodeId": "0001", + "CacheSize": "" + } + ], + "NumCacheNodes": 1, + "Port": 6379, + "PreferredAvailabilityZone": "us-west-2c", + "PreferredMaintenanceWindow": "sat:08:00-sat:09:00", + "SnapshotName": "snapshot-1", + "SnapshotRetentionLimit": 1, + "SnapshotSource": "manual", + "SnapshotStatus": "creating", + "SnapshotWindow": "00:00-01:00", + "VpcId": "vpc-73c3cd17" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a snapshot of a non-clustered Redis cluster that has only one node.", + "id": "createsnapshot-1474999681024", + "title": "CreateSnapshot - NonClustered Redis, no read-replicas" + }, + { + "input": { + "CacheClusterId": "threenoderedis-001", + "SnapshotName": "snapshot-2" + }, + "output": { + "Snapshot": { + "AutoMinorVersionUpgrade": true, + "CacheClusterCreateTime": "2017-02-03T15:43:36.278Z", + "CacheClusterId": "threenoderedis-001", + "CacheNodeType": "cache.m3.medium", + "CacheParameterGroupName": "default.redis3.2", + "CacheSubnetGroupName": "default", + "Engine": "redis", + "EngineVersion": "3.2.4", + "NodeSnapshots": [ + { + "CacheNodeCreateTime": "2017-02-03T15:43:36.278Z", + "CacheNodeId": "0001", + "CacheSize": "" + } + ], + "NumCacheNodes": 1, + "Port": 6379, + "PreferredAvailabilityZone": "us-west-2c", + "PreferredMaintenanceWindow": "sat:08:00-sat:09:00", + "SnapshotName": "snapshot-2", + "SnapshotRetentionLimit": 1, + "SnapshotSource": "manual", + "SnapshotStatus": "creating", + "SnapshotWindow": "00:00-01:00", + "VpcId": "vpc-73c3cd17" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a snapshot of a non-clustered Redis cluster that has only three nodes, primary and two read-replicas. CacheClusterId must be a specific node in the cluster.", + "id": "createsnapshot-1474999681024", + "title": "CreateSnapshot - NonClustered Redis, 2 read-replicas" + }, + { + "input": { + "ReplicationGroupId": "clusteredredis", + "SnapshotName": "snapshot-2x5" + }, + "output": { + "Snapshot": { + "AutoMinorVersionUpgrade": true, + "AutomaticFailover": "enabled", + "CacheNodeType": "cache.m3.medium", + "CacheParameterGroupName": "default.redis3.2.cluster.on", + "CacheSubnetGroupName": "default", + "Engine": "redis", + "EngineVersion": "3.2.4", + "NodeSnapshots": [ + { + "CacheSize": "", + "NodeGroupId": "0001" + }, + { + "CacheSize": "", + "NodeGroupId": "0002" + } + ], + "NumNodeGroups": 2, + "Port": 6379, + "PreferredMaintenanceWindow": "mon:09:30-mon:10:30", + "ReplicationGroupDescription": "Redis cluster with 2 shards.", + "ReplicationGroupId": "clusteredredis", + "SnapshotName": "snapshot-2x5", + "SnapshotRetentionLimit": 1, + "SnapshotSource": "manual", + "SnapshotStatus": "creating", + "SnapshotWindow": "12:00-13:00", + "VpcId": "vpc-73c3cd17" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a snapshot of a clustered Redis cluster that has 2 shards, each with a primary and 4 read-replicas.", + "id": "createsnapshot-clustered-redis-1486144841758", + "title": "CreateSnapshot-clustered Redis" + } + ], + "DeleteCacheCluster": [ + { + "input": { + "CacheClusterId": "my-memcached" + }, + "output": { + "CacheCluster": { + "AutoMinorVersionUpgrade": true, + "CacheClusterCreateTime": "2016-12-22T16:05:17.314Z", + "CacheClusterId": "my-memcached", + "CacheClusterStatus": "deleting", + "CacheNodeType": "cache.r3.large", + "CacheParameterGroup": { + "CacheNodeIdsToReboot": [ + + ], + "CacheParameterGroupName": "default.memcached1.4", + "ParameterApplyStatus": "in-sync" + }, + "CacheSecurityGroups": [ + + ], + "CacheSubnetGroupName": "default", + "ClientDownloadLandingPage": "https://console.aws.amazon.com/elasticache/home#client-download:", + "ConfigurationEndpoint": { + "Address": "my-memcached2.ameaqx.cfg.use1.cache.amazonaws.com", + "Port": 11211 + }, + "Engine": "memcached", + "EngineVersion": "1.4.24", + "NumCacheNodes": 2, + "PendingModifiedValues": { + }, + "PreferredAvailabilityZone": "Multiple", + "PreferredMaintenanceWindow": "tue:07:30-tue:08:30" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes an Amazon ElastiCache cluster.", + "id": "deletecachecluster-1475010605291", + "title": "DeleteCacheCluster" + } + ], + "DeleteCacheParameterGroup": [ + { + "input": { + "CacheParameterGroupName": "custom-mem1-4" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the Amazon ElastiCache parameter group custom-mem1-4.", + "id": "deletecacheparametergroup-1475010933957", + "title": "DeleteCacheParameterGroup" + } + ], + "DeleteCacheSecurityGroup": [ + { + "input": { + "CacheSecurityGroupName": "my-sec-group" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes a cache security group.", + "id": "deletecachesecuritygroup-1483046967507", + "title": "DeleteCacheSecurityGroup" + } + ], + "DeleteCacheSubnetGroup": [ + { + "input": { + "CacheSubnetGroupName": "my-subnet-group" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the Amazon ElastiCache subnet group my-subnet-group.", + "id": "deletecachesubnetgroup-1475011431325", + "title": "DeleteCacheSubnetGroup" + } + ], + "DeleteReplicationGroup": [ + { + "input": { + "ReplicationGroupId": "my-redis-rg", + "RetainPrimaryCluster": false + }, + "output": { + "ReplicationGroup": { + "AutomaticFailover": "disabled", + "Description": "simple redis cluster", + "PendingModifiedValues": { + }, + "ReplicationGroupId": "my-redis-rg", + "Status": "deleting" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the Amazon ElastiCache replication group my-redis-rg.", + "id": "deletereplicationgroup-1475011641804", + "title": "DeleteReplicationGroup" + } + ], + "DeleteSnapshot": [ + { + "input": { + "SnapshotName": "snapshot-20161212" + }, + "output": { + "Snapshot": { + "AutoMinorVersionUpgrade": true, + "CacheClusterCreateTime": "2016-12-21T22:27:12.543Z", + "CacheClusterId": "my-redis5", + "CacheNodeType": "cache.m3.large", + "CacheParameterGroupName": "default.redis3.2", + "CacheSubnetGroupName": "default", + "Engine": "redis", + "EngineVersion": "3.2.4", + "NodeSnapshots": [ + { + "CacheNodeCreateTime": "2016-12-21T22:27:12.543Z", + "CacheNodeId": "0001", + "CacheSize": "3 MB", + "SnapshotCreateTime": "2016-12-21T22:30:26Z" + } + ], + "NumCacheNodes": 1, + "Port": 6379, + "PreferredAvailabilityZone": "us-east-1c", + "PreferredMaintenanceWindow": "fri:05:30-fri:06:30", + "SnapshotName": "snapshot-20161212", + "SnapshotRetentionLimit": 7, + "SnapshotSource": "manual", + "SnapshotStatus": "deleting", + "SnapshotWindow": "10:00-11:00", + "VpcId": "vpc-91280df6" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the Redis snapshot snapshot-20160822.", + "id": "deletesnapshot-1475011945779", + "title": "DeleteSnapshot" + } + ], + "DescribeCacheClusters": [ + { + "input": { + "CacheClusterId": "my-mem-cluster" + }, + "output": { + "CacheClusters": [ + { + "AutoMinorVersionUpgrade": true, + "CacheClusterCreateTime": "2016-12-21T21:59:43.794Z", + "CacheClusterId": "my-mem-cluster", + "CacheClusterStatus": "available", + "CacheNodeType": "cache.t2.medium", + "CacheParameterGroup": { + "CacheNodeIdsToReboot": [ + + ], + "CacheParameterGroupName": "default.memcached1.4", + "ParameterApplyStatus": "in-sync" + }, + "CacheSecurityGroups": [ + + ], + "CacheSubnetGroupName": "default", + "ClientDownloadLandingPage": "https://console.aws.amazon.com/elasticache/home#client-download:", + "ConfigurationEndpoint": { + "Address": "my-mem-cluster.abcdef.cfg.use1.cache.amazonaws.com", + "Port": 11211 + }, + "Engine": "memcached", + "EngineVersion": "1.4.24", + "NumCacheNodes": 2, + "PendingModifiedValues": { + }, + "PreferredAvailabilityZone": "Multiple", + "PreferredMaintenanceWindow": "wed:06:00-wed:07:00" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists the details for up to 50 cache clusters.", + "id": "describecacheclusters-1475012269754", + "title": "DescribeCacheClusters" + }, + { + "input": { + "CacheClusterId": "my-mem-cluster", + "ShowCacheNodeInfo": true + }, + "output": { + "CacheClusters": [ + { + "AutoMinorVersionUpgrade": true, + "CacheClusterCreateTime": "2016-12-21T21:59:43.794Z", + "CacheClusterId": "my-mem-cluster", + "CacheClusterStatus": "available", + "CacheNodeType": "cache.t2.medium", + "CacheNodes": [ + { + "CacheNodeCreateTime": "2016-12-21T21:59:43.794Z", + "CacheNodeId": "0001", + "CacheNodeStatus": "available", + "CustomerAvailabilityZone": "us-east-1b", + "Endpoint": { + "Address": "my-mem-cluster.ameaqx.0001.use1.cache.amazonaws.com", + "Port": 11211 + }, + "ParameterGroupStatus": "in-sync" + }, + { + "CacheNodeCreateTime": "2016-12-21T21:59:43.794Z", + "CacheNodeId": "0002", + "CacheNodeStatus": "available", + "CustomerAvailabilityZone": "us-east-1a", + "Endpoint": { + "Address": "my-mem-cluster.ameaqx.0002.use1.cache.amazonaws.com", + "Port": 11211 + }, + "ParameterGroupStatus": "in-sync" + } + ], + "CacheParameterGroup": { + "CacheNodeIdsToReboot": [ + + ], + "CacheParameterGroupName": "default.memcached1.4", + "ParameterApplyStatus": "in-sync" + }, + "CacheSecurityGroups": [ + + ], + "CacheSubnetGroupName": "default", + "ClientDownloadLandingPage": "https://console.aws.amazon.com/elasticache/home#client-download:", + "ConfigurationEndpoint": { + "Address": "my-mem-cluster.ameaqx.cfg.use1.cache.amazonaws.com", + "Port": 11211 + }, + "Engine": "memcached", + "EngineVersion": "1.4.24", + "NumCacheNodes": 2, + "PendingModifiedValues": { + }, + "PreferredAvailabilityZone": "Multiple", + "PreferredMaintenanceWindow": "wed:06:00-wed:07:00" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists the details for the cache cluster my-mem-cluster.", + "id": "describecacheclusters-1475012269754", + "title": "DescribeCacheClusters" + } + ], + "DescribeCacheEngineVersions": [ + { + "input": { + }, + "output": { + "CacheEngineVersions": [ + { + "CacheEngineDescription": "memcached", + "CacheEngineVersionDescription": "memcached version 1.4.14", + "CacheParameterGroupFamily": "memcached1.4", + "Engine": "memcached", + "EngineVersion": "1.4.14" + }, + { + "CacheEngineDescription": "memcached", + "CacheEngineVersionDescription": "memcached version 1.4.24", + "CacheParameterGroupFamily": "memcached1.4", + "Engine": "memcached", + "EngineVersion": "1.4.24" + }, + { + "CacheEngineDescription": "memcached", + "CacheEngineVersionDescription": "memcached version 1.4.33", + "CacheParameterGroupFamily": "memcached1.4", + "Engine": "memcached", + "EngineVersion": "1.4.33" + }, + { + "CacheEngineDescription": "memcached", + "CacheEngineVersionDescription": "memcached version 1.4.5", + "CacheParameterGroupFamily": "memcached1.4", + "Engine": "memcached", + "EngineVersion": "1.4.5" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.6.13", + "CacheParameterGroupFamily": "redis2.6", + "Engine": "redis", + "EngineVersion": "2.6.13" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.8.19", + "CacheParameterGroupFamily": "redis2.8", + "Engine": "redis", + "EngineVersion": "2.8.19" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.8.21", + "CacheParameterGroupFamily": "redis2.8", + "Engine": "redis", + "EngineVersion": "2.8.21" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.8.22 R5", + "CacheParameterGroupFamily": "redis2.8", + "Engine": "redis", + "EngineVersion": "2.8.22" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.8.23 R4", + "CacheParameterGroupFamily": "redis2.8", + "Engine": "redis", + "EngineVersion": "2.8.23" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.8.24 R3", + "CacheParameterGroupFamily": "redis2.8", + "Engine": "redis", + "EngineVersion": "2.8.24" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.8.6", + "CacheParameterGroupFamily": "redis2.8", + "Engine": "redis", + "EngineVersion": "2.8.6" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 3.2.4", + "CacheParameterGroupFamily": "redis3.2", + "Engine": "redis", + "EngineVersion": "3.2.4" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists the details for up to 25 Memcached and Redis cache engine versions.", + "id": "describecacheengineversions-1475012638790", + "title": "DescribeCacheEngineVersions" + }, + { + "input": { + "DefaultOnly": false, + "Engine": "redis", + "MaxRecords": 50 + }, + "output": { + "CacheEngineVersions": [ + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.6.13", + "CacheParameterGroupFamily": "redis2.6", + "Engine": "redis", + "EngineVersion": "2.6.13" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.8.19", + "CacheParameterGroupFamily": "redis2.8", + "Engine": "redis", + "EngineVersion": "2.8.19" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.8.21", + "CacheParameterGroupFamily": "redis2.8", + "Engine": "redis", + "EngineVersion": "2.8.21" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.8.22 R5", + "CacheParameterGroupFamily": "redis2.8", + "Engine": "redis", + "EngineVersion": "2.8.22" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.8.23 R4", + "CacheParameterGroupFamily": "redis2.8", + "Engine": "redis", + "EngineVersion": "2.8.23" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.8.24 R3", + "CacheParameterGroupFamily": "redis2.8", + "Engine": "redis", + "EngineVersion": "2.8.24" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 2.8.6", + "CacheParameterGroupFamily": "redis2.8", + "Engine": "redis", + "EngineVersion": "2.8.6" + }, + { + "CacheEngineDescription": "Redis", + "CacheEngineVersionDescription": "redis version 3.2.4", + "CacheParameterGroupFamily": "redis3.2", + "Engine": "redis", + "EngineVersion": "3.2.4" + } + ], + "Marker": "" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists the details for up to 50 Redis cache engine versions.", + "id": "describecacheengineversions-1475012638790", + "title": "DescribeCacheEngineVersions" + } + ], + "DescribeCacheParameterGroups": [ + { + "input": { + "CacheParameterGroupName": "custom-mem1-4" + }, + "output": { + "CacheParameterGroups": [ + { + "CacheParameterGroupFamily": "memcached1.4", + "CacheParameterGroupName": "custom-mem1-4", + "Description": "Custom memcache param group" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns a list of cache parameter group descriptions. If a cache parameter group name is specified, the list contains only the descriptions for that group.", + "id": "describecacheparametergroups-1483045457557", + "title": "DescribeCacheParameterGroups" + } + ], + "DescribeCacheParameters": [ + { + "input": { + "CacheParameterGroupName": "custom-redis2-8", + "MaxRecords": 100, + "Source": "user" + }, + "output": { + "Marker": "", + "Parameters": [ + { + "AllowedValues": "yes,no", + "ChangeType": "requires-reboot", + "DataType": "string", + "Description": "Apply rehashing or not.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "activerehashing", + "ParameterValue": "yes", + "Source": "system" + }, + { + "AllowedValues": "always,everysec,no", + "ChangeType": "immediate", + "DataType": "string", + "Description": "fsync policy for AOF persistence", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "appendfsync", + "ParameterValue": "everysec", + "Source": "system" + }, + { + "AllowedValues": "yes,no", + "ChangeType": "immediate", + "DataType": "string", + "Description": "Enable Redis persistence.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "appendonly", + "ParameterValue": "no", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Normal client output buffer hard limit in bytes.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-normal-hard-limit", + "ParameterValue": "0", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Normal client output buffer soft limit in bytes.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-normal-soft-limit", + "ParameterValue": "0", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Normal client output buffer soft limit in seconds.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-normal-soft-seconds", + "ParameterValue": "0", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Pubsub client output buffer hard limit in bytes.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-pubsub-hard-limit", + "ParameterValue": "33554432", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Pubsub client output buffer soft limit in bytes.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-pubsub-soft-limit", + "ParameterValue": "8388608", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Pubsub client output buffer soft limit in seconds.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-pubsub-soft-seconds", + "ParameterValue": "60", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Slave client output buffer soft limit in seconds.", + "IsModifiable": false, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-slave-soft-seconds", + "ParameterValue": "60", + "Source": "system" + }, + { + "AllowedValues": "yes,no", + "ChangeType": "immediate", + "DataType": "string", + "Description": "If enabled, clients who attempt to write to a read-only slave will be disconnected. Applicable to 2.8.23 and higher.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.23", + "ParameterName": "close-on-slave-write", + "ParameterValue": "yes", + "Source": "system" + }, + { + "AllowedValues": "1-1200000", + "ChangeType": "requires-reboot", + "DataType": "integer", + "Description": "Set the number of databases.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "databases", + "ParameterValue": "16", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The maximum number of hash entries in order for the dataset to be compressed.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "hash-max-ziplist-entries", + "ParameterValue": "512", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The threshold of biggest hash entries in order for the dataset to be compressed.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "hash-max-ziplist-value", + "ParameterValue": "64", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The maximum number of list entries in order for the dataset to be compressed.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "list-max-ziplist-entries", + "ParameterValue": "512", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The threshold of biggest list entries in order for the dataset to be compressed.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "list-max-ziplist-value", + "ParameterValue": "64", + "Source": "system" + }, + { + "AllowedValues": "5000", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Max execution time of a Lua script in milliseconds. 0 for unlimited execution without warnings.", + "IsModifiable": false, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "lua-time-limit", + "ParameterValue": "5000", + "Source": "system" + }, + { + "AllowedValues": "1-65000", + "ChangeType": "requires-reboot", + "DataType": "integer", + "Description": "The maximum number of Redis clients.", + "IsModifiable": false, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "maxclients", + "ParameterValue": "65000", + "Source": "system" + }, + { + "AllowedValues": "volatile-lru,allkeys-lru,volatile-random,allkeys-random,volatile-ttl,noeviction", + "ChangeType": "immediate", + "DataType": "string", + "Description": "Max memory policy.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "maxmemory-policy", + "ParameterValue": "volatile-lru", + "Source": "system" + }, + { + "AllowedValues": "1-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Max memory samples.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "maxmemory-samples", + "ParameterValue": "3", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Maximum number of seconds within which the master must receive a ping from a slave to take writes. Use this parameter together with min-slaves-to-write to regulate when the master stops accepting writes. Setting this value to 0 means the master always takes writes.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "min-slaves-max-lag", + "ParameterValue": "10", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Number of slaves that must be connected in order for master to take writes. Use this parameter together with min-slaves-max-lag to regulate when the master stops accepting writes. Setting this to 0 means the master always takes writes.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "min-slaves-to-write", + "ParameterValue": "0", + "Source": "system" + }, + { + "ChangeType": "immediate", + "DataType": "string", + "Description": "The keyspace events for Redis to notify Pub/Sub clients about. By default all notifications are disabled", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "notify-keyspace-events", + "Source": "system" + }, + { + "AllowedValues": "16384-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The replication backlog size in bytes for PSYNC. This is the size of the buffer which accumulates slave data when slave is disconnected for some time, so that when slave reconnects again, only transfer the portion of data which the slave missed. Minimum value is 16K.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "repl-backlog-size", + "ParameterValue": "1048576", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The amount of time in seconds after the master no longer have any slaves connected for the master to free the replication backlog. A value of 0 means to never release the backlog.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "repl-backlog-ttl", + "ParameterValue": "3600", + "Source": "system" + }, + { + "AllowedValues": "11-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The timeout in seconds for bulk transfer I/O during sync and master timeout from the perspective of the slave, and slave timeout from the perspective of the master.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "repl-timeout", + "ParameterValue": "60", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The amount of memory reserved for non-cache memory usage, in bytes. You may want to increase this parameter for nodes with read replicas, AOF enabled, etc, to reduce swap usage.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "reserved-memory", + "ParameterValue": "0", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The limit in the size of the set in order for the dataset to be compressed.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "set-max-intset-entries", + "ParameterValue": "512", + "Source": "system" + }, + { + "AllowedValues": "yes,no", + "ChangeType": "immediate", + "DataType": "string", + "Description": "Configures if chaining of slaves is allowed", + "IsModifiable": false, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "slave-allow-chaining", + "ParameterValue": "no", + "Source": "system" + }, + { + "AllowedValues": "-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The execution time, in microseconds, to exceed in order for the command to get logged. Note that a negative number disables the slow log, while a value of zero forces the logging of every command.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "slowlog-log-slower-than", + "ParameterValue": "10000", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The length of the slow log. There is no limit to this length. Just be aware that it will consume memory. You can reclaim memory used by the slow log with SLOWLOG RESET.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "slowlog-max-len", + "ParameterValue": "128", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "If non-zero, send ACKs every given number of seconds.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "tcp-keepalive", + "ParameterValue": "0", + "Source": "system" + }, + { + "AllowedValues": "0,20-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Close connection if client is idle for a given number of seconds, or never if 0.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "timeout", + "ParameterValue": "0", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The maximum number of sorted set entries in order for the dataset to be compressed.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "zset-max-ziplist-entries", + "ParameterValue": "128", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The threshold of biggest sorted set entries in order for the dataset to be compressed.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "zset-max-ziplist-value", + "ParameterValue": "64", + "Source": "system" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists up to 100 user parameter values for the parameter group custom.redis2.8.", + "id": "describecacheparameters-1475013576900", + "title": "DescribeCacheParameters" + } + ], + "DescribeCacheSecurityGroups": [ + { + "input": { + "CacheSecurityGroupName": "my-sec-group" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns a list of cache security group descriptions. If a cache security group name is specified, the list contains only the description of that group.", + "id": "describecachesecuritygroups-1483047200801", + "title": "DescribeCacheSecurityGroups" + } + ], + "DescribeCacheSubnetGroups": [ + { + "input": { + "MaxRecords": 25 + }, + "output": { + "CacheSubnetGroups": [ + { + "CacheSubnetGroupDescription": "Default CacheSubnetGroup", + "CacheSubnetGroupName": "default", + "Subnets": [ + { + "SubnetAvailabilityZone": { + "Name": "us-east-1a" + }, + "SubnetIdentifier": "subnet-1a2b3c4d" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1c" + }, + "SubnetIdentifier": "subnet-a1b2c3d4" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1e" + }, + "SubnetIdentifier": "subnet-abcd1234" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1b" + }, + "SubnetIdentifier": "subnet-1234abcd" + } + ], + "VpcId": "vpc-91280df6" + } + ], + "Marker": "" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Describes up to 25 cache subnet groups.", + "id": "describecachesubnetgroups-1482439214064", + "title": "DescribeCacheSubnetGroups" + } + ], + "DescribeEngineDefaultParameters": [ + { + "input": { + "CacheParameterGroupFamily": "redis2.8", + "MaxRecords": 25 + }, + "output": { + "EngineDefaults": { + "CacheNodeTypeSpecificParameters": [ + { + "AllowedValues": "0-", + "CacheNodeTypeSpecificValues": [ + { + "CacheNodeType": "cache.c1.xlarge", + "Value": "650117120" + }, + { + "CacheNodeType": "cache.m1.large", + "Value": "702545920" + }, + { + "CacheNodeType": "cache.m1.medium", + "Value": "309329920" + }, + { + "CacheNodeType": "cache.m1.small", + "Value": "94371840" + }, + { + "CacheNodeType": "cache.m1.xlarge", + "Value": "1488977920" + }, + { + "CacheNodeType": "cache.m2.2xlarge", + "Value": "3502243840" + }, + { + "CacheNodeType": "cache.m2.4xlarge", + "Value": "7088373760" + }, + { + "CacheNodeType": "cache.m2.xlarge", + "Value": "1709178880" + }, + { + "CacheNodeType": "cache.m3.2xlarge", + "Value": "2998927360" + }, + { + "CacheNodeType": "cache.m3.large", + "Value": "650117120" + }, + { + "CacheNodeType": "cache.m3.medium", + "Value": "309329920" + }, + { + "CacheNodeType": "cache.m3.xlarge", + "Value": "1426063360" + }, + { + "CacheNodeType": "cache.m4.10xlarge", + "Value": "16604761424" + }, + { + "CacheNodeType": "cache.m4.2xlarge", + "Value": "3188912636" + }, + { + "CacheNodeType": "cache.m4.4xlarge", + "Value": "6525729063" + }, + { + "CacheNodeType": "cache.m4.large", + "Value": "689259315" + }, + { + "CacheNodeType": "cache.m4.xlarge", + "Value": "1532850176" + }, + { + "CacheNodeType": "cache.r3.2xlarge", + "Value": "6081740800" + }, + { + "CacheNodeType": "cache.r3.4xlarge", + "Value": "12268339200" + }, + { + "CacheNodeType": "cache.r3.8xlarge", + "Value": "24536678400" + }, + { + "CacheNodeType": "cache.r3.large", + "Value": "1468006400" + }, + { + "CacheNodeType": "cache.r3.xlarge", + "Value": "3040870400" + }, + { + "CacheNodeType": "cache.t1.micro", + "Value": "14260633" + }, + { + "CacheNodeType": "cache.t2.medium", + "Value": "346134937" + }, + { + "CacheNodeType": "cache.t2.micro", + "Value": "58195968" + }, + { + "CacheNodeType": "cache.t2.small", + "Value": "166513868" + } + ], + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Slave client output buffer hard limit in bytes.", + "IsModifiable": false, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-slave-hard-limit", + "Source": "system" + }, + { + "AllowedValues": "0-", + "CacheNodeTypeSpecificValues": [ + { + "CacheNodeType": "cache.c1.xlarge", + "Value": "650117120" + }, + { + "CacheNodeType": "cache.m1.large", + "Value": "702545920" + }, + { + "CacheNodeType": "cache.m1.medium", + "Value": "309329920" + }, + { + "CacheNodeType": "cache.m1.small", + "Value": "94371840" + }, + { + "CacheNodeType": "cache.m1.xlarge", + "Value": "1488977920" + }, + { + "CacheNodeType": "cache.m2.2xlarge", + "Value": "3502243840" + }, + { + "CacheNodeType": "cache.m2.4xlarge", + "Value": "7088373760" + }, + { + "CacheNodeType": "cache.m2.xlarge", + "Value": "1709178880" + }, + { + "CacheNodeType": "cache.m3.2xlarge", + "Value": "2998927360" + }, + { + "CacheNodeType": "cache.m3.large", + "Value": "650117120" + }, + { + "CacheNodeType": "cache.m3.medium", + "Value": "309329920" + }, + { + "CacheNodeType": "cache.m3.xlarge", + "Value": "1426063360" + }, + { + "CacheNodeType": "cache.m4.10xlarge", + "Value": "16604761424" + }, + { + "CacheNodeType": "cache.m4.2xlarge", + "Value": "3188912636" + }, + { + "CacheNodeType": "cache.m4.4xlarge", + "Value": "6525729063" + }, + { + "CacheNodeType": "cache.m4.large", + "Value": "689259315" + }, + { + "CacheNodeType": "cache.m4.xlarge", + "Value": "1532850176" + }, + { + "CacheNodeType": "cache.r3.2xlarge", + "Value": "6081740800" + }, + { + "CacheNodeType": "cache.r3.4xlarge", + "Value": "12268339200" + }, + { + "CacheNodeType": "cache.r3.8xlarge", + "Value": "24536678400" + }, + { + "CacheNodeType": "cache.r3.large", + "Value": "1468006400" + }, + { + "CacheNodeType": "cache.r3.xlarge", + "Value": "3040870400" + }, + { + "CacheNodeType": "cache.t1.micro", + "Value": "14260633" + }, + { + "CacheNodeType": "cache.t2.medium", + "Value": "346134937" + }, + { + "CacheNodeType": "cache.t2.micro", + "Value": "58195968" + }, + { + "CacheNodeType": "cache.t2.small", + "Value": "166513868" + } + ], + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Slave client output buffer soft limit in bytes.", + "IsModifiable": false, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-slave-soft-limit", + "Source": "system" + }, + { + "AllowedValues": "0-", + "CacheNodeTypeSpecificValues": [ + { + "CacheNodeType": "cache.c1.xlarge", + "Value": "6501171200" + }, + { + "CacheNodeType": "cache.m1.large", + "Value": "7025459200" + }, + { + "CacheNodeType": "cache.m1.medium", + "Value": "3093299200" + }, + { + "CacheNodeType": "cache.m1.small", + "Value": "943718400" + }, + { + "CacheNodeType": "cache.m1.xlarge", + "Value": "14889779200" + }, + { + "CacheNodeType": "cache.m2.2xlarge", + "Value": "35022438400" + }, + { + "CacheNodeType": "cache.m2.4xlarge", + "Value": "70883737600" + }, + { + "CacheNodeType": "cache.m2.xlarge", + "Value": "17091788800" + }, + { + "CacheNodeType": "cache.m3.2xlarge", + "Value": "29989273600" + }, + { + "CacheNodeType": "cache.m3.large", + "Value": "6501171200" + }, + { + "CacheNodeType": "cache.m3.medium", + "Value": "2988441600" + }, + { + "CacheNodeType": "cache.m3.xlarge", + "Value": "14260633600" + }, + { + "CacheNodeType": "cache.m4.10xlarge", + "Value": "166047614239" + }, + { + "CacheNodeType": "cache.m4.2xlarge", + "Value": "31889126359" + }, + { + "CacheNodeType": "cache.m4.4xlarge", + "Value": "65257290629" + }, + { + "CacheNodeType": "cache.m4.large", + "Value": "6892593152" + }, + { + "CacheNodeType": "cache.m4.xlarge", + "Value": "15328501760" + }, + { + "CacheNodeType": "cache.r3.2xlarge", + "Value": "62495129600" + }, + { + "CacheNodeType": "cache.r3.4xlarge", + "Value": "126458265600" + }, + { + "CacheNodeType": "cache.r3.8xlarge", + "Value": "254384537600" + }, + { + "CacheNodeType": "cache.r3.large", + "Value": "14470348800" + }, + { + "CacheNodeType": "cache.r3.xlarge", + "Value": "30513561600" + }, + { + "CacheNodeType": "cache.t1.micro", + "Value": "142606336" + }, + { + "CacheNodeType": "cache.t2.medium", + "Value": "3461349376" + }, + { + "CacheNodeType": "cache.t2.micro", + "Value": "581959680" + }, + { + "CacheNodeType": "cache.t2.small", + "Value": "1665138688" + } + ], + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The maximum configurable amount of memory to use to store items, in bytes.", + "IsModifiable": false, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "maxmemory", + "Source": "system" + } + ], + "CacheParameterGroupFamily": "redis2.8", + "Marker": "bWluLXNsYXZlcy10by13cml0ZQ==", + "Parameters": [ + { + "AllowedValues": "yes,no", + "ChangeType": "requires-reboot", + "DataType": "string", + "Description": "Apply rehashing or not.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "activerehashing", + "ParameterValue": "yes", + "Source": "system" + }, + { + "AllowedValues": "always,everysec,no", + "ChangeType": "immediate", + "DataType": "string", + "Description": "fsync policy for AOF persistence", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "appendfsync", + "ParameterValue": "everysec", + "Source": "system" + }, + { + "AllowedValues": "yes,no", + "ChangeType": "immediate", + "DataType": "string", + "Description": "Enable Redis persistence.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "appendonly", + "ParameterValue": "no", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Normal client output buffer hard limit in bytes.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-normal-hard-limit", + "ParameterValue": "0", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Normal client output buffer soft limit in bytes.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-normal-soft-limit", + "ParameterValue": "0", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Normal client output buffer soft limit in seconds.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-normal-soft-seconds", + "ParameterValue": "0", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Pubsub client output buffer hard limit in bytes.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-pubsub-hard-limit", + "ParameterValue": "33554432", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Pubsub client output buffer soft limit in bytes.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-pubsub-soft-limit", + "ParameterValue": "8388608", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Pubsub client output buffer soft limit in seconds.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-pubsub-soft-seconds", + "ParameterValue": "60", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Slave client output buffer soft limit in seconds.", + "IsModifiable": false, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "client-output-buffer-limit-slave-soft-seconds", + "ParameterValue": "60", + "Source": "system" + }, + { + "AllowedValues": "yes,no", + "ChangeType": "immediate", + "DataType": "string", + "Description": "If enabled, clients who attempt to write to a read-only slave will be disconnected. Applicable to 2.8.23 and higher.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.23", + "ParameterName": "close-on-slave-write", + "ParameterValue": "yes", + "Source": "system" + }, + { + "AllowedValues": "1-1200000", + "ChangeType": "requires-reboot", + "DataType": "integer", + "Description": "Set the number of databases.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "databases", + "ParameterValue": "16", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The maximum number of hash entries in order for the dataset to be compressed.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "hash-max-ziplist-entries", + "ParameterValue": "512", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The threshold of biggest hash entries in order for the dataset to be compressed.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "hash-max-ziplist-value", + "ParameterValue": "64", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The maximum number of list entries in order for the dataset to be compressed.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "list-max-ziplist-entries", + "ParameterValue": "512", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "The threshold of biggest list entries in order for the dataset to be compressed.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "list-max-ziplist-value", + "ParameterValue": "64", + "Source": "system" + }, + { + "AllowedValues": "5000", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Max execution time of a Lua script in milliseconds. 0 for unlimited execution without warnings.", + "IsModifiable": false, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "lua-time-limit", + "ParameterValue": "5000", + "Source": "system" + }, + { + "AllowedValues": "1-65000", + "ChangeType": "requires-reboot", + "DataType": "integer", + "Description": "The maximum number of Redis clients.", + "IsModifiable": false, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "maxclients", + "ParameterValue": "65000", + "Source": "system" + }, + { + "AllowedValues": "volatile-lru,allkeys-lru,volatile-random,allkeys-random,volatile-ttl,noeviction", + "ChangeType": "immediate", + "DataType": "string", + "Description": "Max memory policy.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "maxmemory-policy", + "ParameterValue": "volatile-lru", + "Source": "system" + }, + { + "AllowedValues": "1-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Max memory samples.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "maxmemory-samples", + "ParameterValue": "3", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Maximum number of seconds within which the master must receive a ping from a slave to take writes. Use this parameter together with min-slaves-to-write to regulate when the master stops accepting writes. Setting this value to 0 means the master always takes writes.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "min-slaves-max-lag", + "ParameterValue": "10", + "Source": "system" + }, + { + "AllowedValues": "0-", + "ChangeType": "immediate", + "DataType": "integer", + "Description": "Number of slaves that must be connected in order for master to take writes. Use this parameter together with min-slaves-max-lag to regulate when the master stops accepting writes. Setting this to 0 means the master always takes writes.", + "IsModifiable": true, + "MinimumEngineVersion": "2.8.6", + "ParameterName": "min-slaves-to-write", + "ParameterValue": "0", + "Source": "system" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns the default engine and system parameter information for the specified cache engine.", + "id": "describeenginedefaultparameters-1481738057686", + "title": "DescribeEngineDefaultParameters" + } + ], + "DescribeEvents": [ + { + "input": { + "Duration": 360, + "SourceType": "cache-cluster" + }, + "output": { + "Events": [ + { + "Date": "2016-12-22T16:27:56.088Z", + "Message": "Added cache node 0001 in availability zone us-east-1e", + "SourceIdentifier": "redis-cluster", + "SourceType": "cache-cluster" + }, + { + "Date": "2016-12-22T16:27:56.078Z", + "Message": "Cache cluster created", + "SourceIdentifier": "redis-cluster", + "SourceType": "cache-cluster" + }, + { + "Date": "2016-12-22T16:05:17.326Z", + "Message": "Added cache node 0002 in availability zone us-east-1c", + "SourceIdentifier": "my-memcached2", + "SourceType": "cache-cluster" + }, + { + "Date": "2016-12-22T16:05:17.323Z", + "Message": "Added cache node 0001 in availability zone us-east-1e", + "SourceIdentifier": "my-memcached2", + "SourceType": "cache-cluster" + }, + { + "Date": "2016-12-22T16:05:17.314Z", + "Message": "Cache cluster created", + "SourceIdentifier": "my-memcached2", + "SourceType": "cache-cluster" + } + ], + "Marker": "" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Describes all the cache-cluster events for the past 120 minutes.", + "id": "describeevents-1481843894757", + "title": "DescribeEvents" + }, + { + "input": { + "StartTime": "2016-12-22T15:00:00.000Z" + }, + "output": { + "Events": [ + { + "Date": "2016-12-22T21:35:46.674Z", + "Message": "Snapshot succeeded for snapshot with ID 'cr-bkup' of replication group with ID 'clustered-redis'", + "SourceIdentifier": "clustered-redis-0001-001", + "SourceType": "cache-cluster" + }, + { + "Date": "2016-12-22T16:27:56.088Z", + "Message": "Added cache node 0001 in availability zone us-east-1e", + "SourceIdentifier": "redis-cluster", + "SourceType": "cache-cluster" + }, + { + "Date": "2016-12-22T16:27:56.078Z", + "Message": "Cache cluster created", + "SourceIdentifier": "redis-cluster", + "SourceType": "cache-cluster" + }, + { + "Date": "2016-12-22T16:05:17.326Z", + "Message": "Added cache node 0002 in availability zone us-east-1c", + "SourceIdentifier": "my-memcached2", + "SourceType": "cache-cluster" + }, + { + "Date": "2016-12-22T16:05:17.323Z", + "Message": "Added cache node 0001 in availability zone us-east-1e", + "SourceIdentifier": "my-memcached2", + "SourceType": "cache-cluster" + }, + { + "Date": "2016-12-22T16:05:17.314Z", + "Message": "Cache cluster created", + "SourceIdentifier": "my-memcached2", + "SourceType": "cache-cluster" + } + ], + "Marker": "" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Describes all the replication-group events from 3:00P to 5:00P on November 11, 2016.", + "id": "describeevents-1481843894757", + "title": "DescribeEvents" + } + ], + "DescribeReplicationGroups": [ + { + "input": { + }, + "output": { + "Marker": "", + "ReplicationGroups": [ + { + "AutomaticFailover": "enabled", + "Description": "Test cluster", + "MemberClusters": [ + "clustered-redis-0001-001", + "clustered-redis-0001-002", + "clustered-redis-0002-001", + "clustered-redis-0002-002" + ], + "NodeGroups": [ + { + "NodeGroupId": "0001", + "NodeGroupMembers": [ + { + "CacheClusterId": "clustered-redis-0001-001", + "CacheNodeId": "0001", + "PreferredAvailabilityZone": "us-east-1e" + }, + { + "CacheClusterId": "clustered-redis-0001-002", + "CacheNodeId": "0001", + "PreferredAvailabilityZone": "us-east-1c" + } + ], + "Status": "available" + }, + { + "NodeGroupId": "0002", + "NodeGroupMembers": [ + { + "CacheClusterId": "clustered-redis-0002-001", + "CacheNodeId": "0001", + "PreferredAvailabilityZone": "us-east-1c" + }, + { + "CacheClusterId": "clustered-redis-0002-002", + "CacheNodeId": "0001", + "PreferredAvailabilityZone": "us-east-1b" + } + ], + "Status": "available" + } + ], + "PendingModifiedValues": { + }, + "ReplicationGroupId": "clustered-redis", + "Status": "available" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns information about the replication group myreplgroup.", + "id": "describereplicationgroups-1481742639427", + "title": "DescribeReplicationGroups" + } + ], + "DescribeReservedCacheNodes": [ + { + "input": { + "MaxRecords": 25 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns information about reserved cache nodes for this account, or about a specified reserved cache node. If the account has no reserved cache nodes, the operation returns an empty list, as shown here.", + "id": "describereservedcachenodes-1481742348045", + "title": "DescribeReservedCacheNodes" + } + ], + "DescribeReservedCacheNodesOfferings": [ + { + "input": { + "MaxRecords": 20 + }, + "output": { + "Marker": "1ef01f5b-433f-94ff-a530-61a56bfc8e7a", + "ReservedCacheNodesOfferings": [ + { + "CacheNodeType": "cache.m1.small", + "Duration": 94608000, + "FixedPrice": 157.0, + "OfferingType": "Medium Utilization", + "ProductDescription": "memcached", + "RecurringCharges": [ + + ], + "ReservedCacheNodesOfferingId": "0167633d-37f6-4222-b872-b1f22eb79ba4", + "UsagePrice": 0.017 + }, + { + "CacheNodeType": "cache.m4.xlarge", + "Duration": 94608000, + "FixedPrice": 1248.0, + "OfferingType": "Heavy Utilization", + "ProductDescription": "redis", + "RecurringCharges": [ + { + "RecurringChargeAmount": 0.077, + "RecurringChargeFrequency": "Hourly" + } + ], + "ReservedCacheNodesOfferingId": "02c04e13-baca-4e71-9ceb-620eed94827d", + "UsagePrice": 0.0 + }, + { + "CacheNodeType": "cache.m2.4xlarge", + "Duration": 94608000, + "FixedPrice": 2381.0, + "OfferingType": "Medium Utilization", + "ProductDescription": "memcached", + "RecurringCharges": [ + + ], + "ReservedCacheNodesOfferingId": "02e1755e-76e8-48e3-8d82-820a5726a458", + "UsagePrice": 0.276 + }, + { + "CacheNodeType": "cache.m1.small", + "Duration": 94608000, + "FixedPrice": 188.0, + "OfferingType": "Heavy Utilization", + "ProductDescription": "redis", + "RecurringCharges": [ + { + "RecurringChargeAmount": 0.013, + "RecurringChargeFrequency": "Hourly" + } + ], + "ReservedCacheNodesOfferingId": "03315215-7b87-421a-a3dd-785021e4113f", + "UsagePrice": 0.0 + }, + { + "CacheNodeType": "cache.m4.10xlarge", + "Duration": 31536000, + "FixedPrice": 6158.0, + "OfferingType": "Heavy Utilization", + "ProductDescription": "redis", + "RecurringCharges": [ + { + "RecurringChargeAmount": 1.125, + "RecurringChargeFrequency": "Hourly" + } + ], + "ReservedCacheNodesOfferingId": "05ffbb44-2ace-4476-a2a5-8ec99f866fb3", + "UsagePrice": 0.0 + }, + { + "CacheNodeType": "cache.m1.small", + "Duration": 31536000, + "FixedPrice": 101.0, + "OfferingType": "Medium Utilization", + "ProductDescription": "redis", + "RecurringCharges": [ + + ], + "ReservedCacheNodesOfferingId": "065c71ae-4a4e-4f1e-bebf-37525f4c6cb2", + "UsagePrice": 0.023 + }, + { + "CacheNodeType": "cache.m1.medium", + "Duration": 94608000, + "FixedPrice": 314.0, + "OfferingType": "Medium Utilization", + "ProductDescription": "memcached", + "RecurringCharges": [ + + ], + "ReservedCacheNodesOfferingId": "06774b12-7f5e-48c1-907a-f286c63f327d", + "UsagePrice": 0.034 + }, + { + "CacheNodeType": "cache.m2.xlarge", + "Duration": 31536000, + "FixedPrice": 163.0, + "OfferingType": "Light Utilization", + "ProductDescription": "memcached", + "RecurringCharges": [ + + ], + "ReservedCacheNodesOfferingId": "0924ac6b-847f-4761-ba6b-4290b2adf719", + "UsagePrice": 0.137 + }, + { + "CacheNodeType": "cache.m2.xlarge", + "Duration": 94608000, + "FixedPrice": 719.0, + "OfferingType": "Heavy Utilization", + "ProductDescription": "redis", + "RecurringCharges": [ + { + "RecurringChargeAmount": 0.049, + "RecurringChargeFrequency": "Hourly" + } + ], + "ReservedCacheNodesOfferingId": "09eeb126-69b6-4d3f-8f94-ca3510629f53", + "UsagePrice": 0.0 + }, + { + "CacheNodeType": "cache.r3.2xlarge", + "Duration": 94608000, + "FixedPrice": 4132.0, + "OfferingType": "Heavy Utilization", + "ProductDescription": "redis", + "RecurringCharges": [ + { + "RecurringChargeAmount": 0.182, + "RecurringChargeFrequency": "Hourly" + } + ], + "ReservedCacheNodesOfferingId": "0a516ad8-557f-4310-9dd0-2448c2ff4d62", + "UsagePrice": 0.0 + }, + { + "CacheNodeType": "cache.c1.xlarge", + "Duration": 94608000, + "FixedPrice": 875.0, + "OfferingType": "Light Utilization", + "ProductDescription": "memcached", + "RecurringCharges": [ + + ], + "ReservedCacheNodesOfferingId": "0b0c1cc5-2177-4150-95d7-c67ec34dcb19", + "UsagePrice": 0.363 + }, + { + "CacheNodeType": "cache.m4.10xlarge", + "Duration": 94608000, + "FixedPrice": 12483.0, + "OfferingType": "Heavy Utilization", + "ProductDescription": "memcached", + "RecurringCharges": [ + { + "RecurringChargeAmount": 0.76, + "RecurringChargeFrequency": "Hourly" + } + ], + "ReservedCacheNodesOfferingId": "0c2b139b-1cff-43d0-8fba-0c753f9b1950", + "UsagePrice": 0.0 + }, + { + "CacheNodeType": "cache.c1.xlarge", + "Duration": 31536000, + "FixedPrice": 1620.0, + "OfferingType": "Heavy Utilization", + "ProductDescription": "memcached", + "RecurringCharges": [ + { + "RecurringChargeAmount": 0.207, + "RecurringChargeFrequency": "Hourly" + } + ], + "ReservedCacheNodesOfferingId": "0c52115b-38cb-47a2-8dbc-e02e40b6a13f", + "UsagePrice": 0.0 + }, + { + "CacheNodeType": "cache.m2.4xlarge", + "Duration": 94608000, + "FixedPrice": 2381.0, + "OfferingType": "Medium Utilization", + "ProductDescription": "redis", + "RecurringCharges": [ + + ], + "ReservedCacheNodesOfferingId": "12fcb19c-5416-4e1d-934f-28f1e2cb8599", + "UsagePrice": 0.276 + }, + { + "CacheNodeType": "cache.m4.xlarge", + "Duration": 31536000, + "FixedPrice": 616.0, + "OfferingType": "Heavy Utilization", + "ProductDescription": "memcached", + "RecurringCharges": [ + { + "RecurringChargeAmount": 0.112, + "RecurringChargeFrequency": "Hourly" + } + ], + "ReservedCacheNodesOfferingId": "13af20ad-914d-4d8b-9763-fa2e565f3549", + "UsagePrice": 0.0 + }, + { + "CacheNodeType": "cache.r3.8xlarge", + "Duration": 94608000, + "FixedPrice": 16528.0, + "OfferingType": "Heavy Utilization", + "ProductDescription": "memcached", + "RecurringCharges": [ + { + "RecurringChargeAmount": 0.729, + "RecurringChargeFrequency": "Hourly" + } + ], + "ReservedCacheNodesOfferingId": "14da3d3f-b526-4dbf-b09b-355578b2a576", + "UsagePrice": 0.0 + }, + { + "CacheNodeType": "cache.m1.medium", + "Duration": 94608000, + "FixedPrice": 140.0, + "OfferingType": "Light Utilization", + "ProductDescription": "redis", + "RecurringCharges": [ + + ], + "ReservedCacheNodesOfferingId": "15d7018c-71fb-4717-8409-4bdcdca18da7", + "UsagePrice": 0.052 + }, + { + "CacheNodeType": "cache.m4.4xlarge", + "Duration": 94608000, + "FixedPrice": 4993.0, + "OfferingType": "Heavy Utilization", + "ProductDescription": "memcached", + "RecurringCharges": [ + { + "RecurringChargeAmount": 0.304, + "RecurringChargeFrequency": "Hourly" + } + ], + "ReservedCacheNodesOfferingId": "1ae7ec5f-a76e-49b6-822b-629b1768a13a", + "UsagePrice": 0.0 + }, + { + "CacheNodeType": "cache.m3.2xlarge", + "Duration": 31536000, + "FixedPrice": 1772.0, + "OfferingType": "Heavy Utilization", + "ProductDescription": "redis", + "RecurringCharges": [ + { + "RecurringChargeAmount": 0.25, + "RecurringChargeFrequency": "Hourly" + } + ], + "ReservedCacheNodesOfferingId": "1d31242b-3925-48d1-b882-ce03204e6013", + "UsagePrice": 0.0 + }, + { + "CacheNodeType": "cache.t1.micro", + "Duration": 31536000, + "FixedPrice": 54.0, + "OfferingType": "Medium Utilization", + "ProductDescription": "memcached", + "RecurringCharges": [ + + ], + "ReservedCacheNodesOfferingId": "1ef01f5b-94ff-433f-a530-61a56bfc8e7a", + "UsagePrice": 0.008 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists available reserved cache node offerings.", + "id": "describereseredcachenodeofferings-1481742869998", + "title": "DescribeReseredCacheNodeOfferings" + }, + { + "input": { + "CacheNodeType": "cache.r3.large", + "Duration": "3", + "MaxRecords": 25, + "OfferingType": "Light Utilization", + "ReservedCacheNodesOfferingId": "" + }, + "output": { + "Marker": "", + "ReservedCacheNodesOfferings": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists available reserved cache node offerings for cache.r3.large nodes with a 3 year commitment.", + "id": "describereseredcachenodeofferings-1481742869998", + "title": "DescribeReseredCacheNodeOfferings" + }, + { + "input": { + "CacheNodeType": "", + "Duration": "", + "Marker": "", + "MaxRecords": 25, + "OfferingType": "", + "ProductDescription": "", + "ReservedCacheNodesOfferingId": "438012d3-4052-4cc7-b2e3-8d3372e0e706" + }, + "output": { + "Marker": "", + "ReservedCacheNodesOfferings": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists available reserved cache node offerings.", + "id": "describereseredcachenodeofferings-1481742869998", + "title": "DescribeReseredCacheNodeOfferings" + } + ], + "DescribeSnapshots": [ + { + "input": { + "SnapshotName": "snapshot-20161212" + }, + "output": { + "Marker": "", + "Snapshots": [ + { + "AutoMinorVersionUpgrade": true, + "CacheClusterCreateTime": "2016-12-21T22:27:12.543Z", + "CacheClusterId": "my-redis5", + "CacheNodeType": "cache.m3.large", + "CacheParameterGroupName": "default.redis3.2", + "CacheSubnetGroupName": "default", + "Engine": "redis", + "EngineVersion": "3.2.4", + "NodeSnapshots": [ + { + "CacheNodeCreateTime": "2016-12-21T22:27:12.543Z", + "CacheNodeId": "0001", + "CacheSize": "3 MB", + "SnapshotCreateTime": "2016-12-21T22:30:26Z" + } + ], + "NumCacheNodes": 1, + "Port": 6379, + "PreferredAvailabilityZone": "us-east-1c", + "PreferredMaintenanceWindow": "fri:05:30-fri:06:30", + "SnapshotName": "snapshot-20161212", + "SnapshotRetentionLimit": 7, + "SnapshotSource": "manual", + "SnapshotStatus": "available", + "SnapshotWindow": "10:00-11:00", + "VpcId": "vpc-91280df6" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns information about the snapshot mysnapshot. By default.", + "id": "describesnapshots-1481743399584", + "title": "DescribeSnapshots" + } + ], + "ListAllowedNodeTypeModifications": [ + { + "input": { + "ReplicationGroupId": "myreplgroup" + }, + "output": { + "ScaleUpModifications": [ + "cache.m4.10xlarge", + "cache.m4.2xlarge", + "cache.m4.4xlarge", + "cache.m4.xlarge", + "cache.r3.2xlarge", + "cache.r3.4xlarge", + "cache.r3.8xlarge", + "cache.r3.xlarge" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists all available node types that you can scale your Redis cluster's or replication group's current node type up to.", + "id": "listallowednodetypemodifications-1481748494872", + "title": "ListAllowedNodeTypeModifications" + }, + { + "input": { + "CacheClusterId": "mycluster" + }, + "output": { + "ScaleUpModifications": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists all available node types that you can scale your Redis cluster's or replication group's current node type up to.", + "id": "listallowednodetypemodifications-1481748494872", + "title": "ListAllowedNodeTypeModifications" + } + ], + "ListTagsForResource": [ + { + "input": { + "ResourceName": "arn:aws:elasticache:us-west-2::cluster:mycluster" + }, + "output": { + "TagList": [ + { + "Key": "APIVersion", + "Value": "20150202" + }, + { + "Key": "Service", + "Value": "ElastiCache" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists all cost allocation tags currently on the named resource. A cost allocation tag is a key-value pair where the key is case-sensitive and the value is optional. You can use cost allocation tags to categorize and track your AWS costs.", + "id": "listtagsforresource-1481748784584", + "title": "ListTagsForResource" + } + ], + "ModifyCacheCluster": [ + { + "input": { + "ApplyImmediately": true, + "CacheClusterId": "redis-cluster", + "SnapshotRetentionLimit": 14 + }, + "output": { + "CacheCluster": { + "AutoMinorVersionUpgrade": true, + "CacheClusterCreateTime": "2016-12-22T16:27:56.078Z", + "CacheClusterId": "redis-cluster", + "CacheClusterStatus": "available", + "CacheNodeType": "cache.r3.large", + "CacheParameterGroup": { + "CacheNodeIdsToReboot": [ + + ], + "CacheParameterGroupName": "default.redis3.2", + "ParameterApplyStatus": "in-sync" + }, + "CacheSecurityGroups": [ + + ], + "CacheSubnetGroupName": "default", + "ClientDownloadLandingPage": "https://console.aws.amazon.com/elasticache/home#client-download:", + "Engine": "redis", + "EngineVersion": "3.2.4", + "NumCacheNodes": 1, + "PendingModifiedValues": { + }, + "PreferredAvailabilityZone": "us-east-1e", + "PreferredMaintenanceWindow": "fri:09:00-fri:10:00", + "SnapshotRetentionLimit": 14, + "SnapshotWindow": "07:00-08:00" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Copies a snapshot to a specified name.", + "id": "modifycachecluster-1482962725919", + "title": "ModifyCacheCluster" + } + ], + "ModifyCacheParameterGroup": [ + { + "input": { + "CacheParameterGroupName": "custom-mem1-4", + "ParameterNameValues": [ + { + "ParameterName": "binding_protocol", + "ParameterValue": "ascii" + }, + { + "ParameterName": "chunk_size", + "ParameterValue": "96" + } + ] + }, + "output": { + "CacheParameterGroupName": "custom-mem1-4" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Modifies one or more parameter values in the specified parameter group. You cannot modify any default parameter group.", + "id": "modifycacheparametergroup-1482966746787", + "title": "ModifyCacheParameterGroup" + } + ], + "ModifyCacheSubnetGroup": [ + { + "input": { + "CacheSubnetGroupName": "my-sn-grp", + "SubnetIds": [ + "subnet-bcde2345" + ] + }, + "output": { + "CacheSubnetGroup": { + "CacheSubnetGroupDescription": "My subnet group.", + "CacheSubnetGroupName": "my-sn-grp", + "Subnets": [ + { + "SubnetAvailabilityZone": { + "Name": "us-east-1c" + }, + "SubnetIdentifier": "subnet-a1b2c3d4" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1e" + }, + "SubnetIdentifier": "subnet-1a2b3c4d" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1e" + }, + "SubnetIdentifier": "subnet-bcde2345" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1c" + }, + "SubnetIdentifier": "subnet-1234abcd" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-east-1b" + }, + "SubnetIdentifier": "subnet-abcd1234" + } + ], + "VpcId": "vpc-91280df6" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Modifies an existing ElastiCache subnet group.", + "id": "modifycachesubnetgroup-1483043446226", + "title": "ModifyCacheSubnetGroup" + } + ], + "ModifyReplicationGroup": [ + { + "input": { + "ApplyImmediately": true, + "ReplicationGroupDescription": "Modified replication group", + "ReplicationGroupId": "my-redis-rg", + "SnapshotRetentionLimit": 30, + "SnapshottingClusterId": "my-redis-rg-001" + }, + "output": { + "ReplicationGroup": { + "AutomaticFailover": "enabled", + "Description": "Modified replication group", + "MemberClusters": [ + "my-redis-rg-001", + "my-redis-rg-002", + "my-redis-rg-003" + ], + "NodeGroups": [ + { + "NodeGroupId": "0001", + "NodeGroupMembers": [ + { + "CacheClusterId": "my-redis-rg-001", + "CacheNodeId": "0001", + "CurrentRole": "primary", + "PreferredAvailabilityZone": "us-east-1b", + "ReadEndpoint": { + "Address": "my-redis-rg-001.abcdef.0001.use1.cache.amazonaws.com", + "Port": 6379 + } + }, + { + "CacheClusterId": "my-redis-rg-002", + "CacheNodeId": "0001", + "CurrentRole": "replica", + "PreferredAvailabilityZone": "us-east-1a", + "ReadEndpoint": { + "Address": "my-redis-rg-002.abcdef.0001.use1.cache.amazonaws.com", + "Port": 6379 + } + }, + { + "CacheClusterId": "my-redis-rg-003", + "CacheNodeId": "0001", + "CurrentRole": "replica", + "PreferredAvailabilityZone": "us-east-1c", + "ReadEndpoint": { + "Address": "my-redis-rg-003.abcdef.0001.use1.cache.amazonaws.com", + "Port": 6379 + } + } + ], + "PrimaryEndpoint": { + "Address": "my-redis-rg.abcdef.ng.0001.use1.cache.amazonaws.com", + "Port": 6379 + }, + "Status": "available" + } + ], + "PendingModifiedValues": { + }, + "ReplicationGroupId": "my-redis-rg", + "SnapshottingClusterId": "my-redis-rg-002", + "Status": "available" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "modifyreplicationgroup-1483039689581", + "title": "ModifyReplicationGroup" + } + ], + "PurchaseReservedCacheNodesOffering": [ + { + "input": { + "ReservedCacheNodesOfferingId": "1ef01f5b-94ff-433f-a530-61a56bfc8e7a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Allows you to purchase a reserved cache node offering.", + "id": "purchasereservedcachenodesofferings-1483040798484", + "title": "PurchaseReservedCacheNodesOfferings" + } + ], + "RebootCacheCluster": [ + { + "input": { + "CacheClusterId": "custom-mem1-4 ", + "CacheNodeIdsToReboot": [ + "0001", + "0002" + ] + }, + "output": { + "CacheCluster": { + "AutoMinorVersionUpgrade": true, + "CacheClusterCreateTime": "2016-12-21T21:59:43.794Z", + "CacheClusterId": "my-mem-cluster", + "CacheClusterStatus": "rebooting cache cluster nodes", + "CacheNodeType": "cache.t2.medium", + "CacheParameterGroup": { + "CacheNodeIdsToReboot": [ + + ], + "CacheParameterGroupName": "default.memcached1.4", + "ParameterApplyStatus": "in-sync" + }, + "CacheSecurityGroups": [ + + ], + "CacheSubnetGroupName": "default", + "ClientDownloadLandingPage": "https://console.aws.amazon.com/elasticache/home#client-download:", + "ConfigurationEndpoint": { + "Address": "my-mem-cluster.abcdef.cfg.use1.cache.amazonaws.com", + "Port": 11211 + }, + "Engine": "memcached", + "EngineVersion": "1.4.24", + "NumCacheNodes": 2, + "PendingModifiedValues": { + }, + "PreferredAvailabilityZone": "Multiple", + "PreferredMaintenanceWindow": "wed:06:00-wed:07:00" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Reboots the specified nodes in the names cluster.", + "id": "rebootcachecluster-1482969019505", + "title": "RebootCacheCluster" + } + ], + "RemoveTagsFromResource": [ + { + "input": { + "ResourceName": "arn:aws:elasticache:us-east-1:1234567890:cluster:my-mem-cluster", + "TagKeys": [ + "A", + "C", + "E" + ] + }, + "output": { + "TagList": [ + { + "Key": "B", + "Value": "Banana" + }, + { + "Key": "D", + "Value": "Dog" + }, + { + "Key": "F", + "Value": "Fox" + }, + { + "Key": "I", + "Value": "" + }, + { + "Key": "K", + "Value": "Kite" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Removes tags identified by a list of tag keys from the list of tags on the specified resource.", + "id": "removetagsfromresource-1483037920947", + "title": "RemoveTagsFromResource" + } + ], + "ResetCacheParameterGroup": [ + { + "input": { + "CacheParameterGroupName": "custom-mem1-4", + "ResetAllParameters": true + }, + "output": { + "CacheParameterGroupName": "custom-mem1-4" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Modifies the parameters of a cache parameter group to the engine or system default value.", + "id": "resetcacheparametergroup-1483038334014", + "title": "ResetCacheParameterGroup" + } + ], + "RevokeCacheSecurityGroupIngress": [ + { + "input": { + "CacheSecurityGroupName": "my-sec-grp", + "EC2SecurityGroupName": "my-ec2-sec-grp", + "EC2SecurityGroupOwnerId": "1234567890" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns a list of cache security group descriptions. If a cache security group name is specified, the list contains only the description of that group.", + "id": "describecachesecuritygroups-1483047200801", + "title": "DescribeCacheSecurityGroups" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/paginators-1.json new file mode 100644 index 0000000..12368b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/paginators-1.json @@ -0,0 +1,118 @@ +{ + "pagination": { + "DescribeCacheClusters": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "CacheClusters" + }, + "DescribeCacheEngineVersions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "CacheEngineVersions" + }, + "DescribeCacheParameterGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "CacheParameterGroups" + }, + "DescribeCacheParameters": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Parameters" + }, + "DescribeCacheSecurityGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "CacheSecurityGroups" + }, + "DescribeCacheSubnetGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "CacheSubnetGroups" + }, + "DescribeEngineDefaultParameters": { + "input_token": "Marker", + "output_token": "EngineDefaults.Marker", + "limit_key": "MaxRecords", + "result_key": "EngineDefaults.Parameters" + }, + "DescribeEvents": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Events" + }, + "DescribeReservedCacheNodes": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ReservedCacheNodes" + }, + "DescribeReservedCacheNodesOfferings": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ReservedCacheNodesOfferings" + }, + "DescribeReplicationGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ReplicationGroups" + }, + "DescribeSnapshots": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Snapshots" + }, + "DescribeServiceUpdates": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "ServiceUpdates" + }, + "DescribeUpdateActions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "UpdateActions" + }, + "DescribeGlobalReplicationGroups": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "GlobalReplicationGroups" + }, + "DescribeUserGroups": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "UserGroups" + }, + "DescribeUsers": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "Users" + }, + "DescribeServerlessCacheSnapshots": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ServerlessCacheSnapshots" + }, + "DescribeServerlessCaches": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ServerlessCaches" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/service-2.json.gz new file mode 100644 index 0000000..5b2b7a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/waiters-2.json new file mode 100644 index 0000000..c177d7b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elasticache/2015-02-02/waiters-2.json @@ -0,0 +1,143 @@ +{ + "version":2, + "waiters":{ + "CacheClusterAvailable":{ + "acceptors":[ + { + "argument":"CacheClusters[].CacheClusterStatus", + "expected":"available", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"CacheClusters[].CacheClusterStatus", + "expected":"deleted", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"CacheClusters[].CacheClusterStatus", + "expected":"deleting", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"CacheClusters[].CacheClusterStatus", + "expected":"incompatible-network", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"CacheClusters[].CacheClusterStatus", + "expected":"restore-failed", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":15, + "description":"Wait until ElastiCache cluster is available.", + "maxAttempts":40, + "operation":"DescribeCacheClusters" + }, + "CacheClusterDeleted":{ + "acceptors":[ + { + "argument":"CacheClusters[].CacheClusterStatus", + "expected":"deleted", + "matcher":"pathAll", + "state":"success" + }, + { + "expected":"CacheClusterNotFound", + "matcher":"error", + "state":"success" + }, + { + "argument":"CacheClusters[].CacheClusterStatus", + "expected":"available", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"CacheClusters[].CacheClusterStatus", + "expected":"creating", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"CacheClusters[].CacheClusterStatus", + "expected":"incompatible-network", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"CacheClusters[].CacheClusterStatus", + "expected":"modifying", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"CacheClusters[].CacheClusterStatus", + "expected":"restore-failed", + "matcher":"pathAny", + "state":"failure" + }, + { + "argument":"CacheClusters[].CacheClusterStatus", + "expected":"snapshotting", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":15, + "description":"Wait until ElastiCache cluster is deleted.", + "maxAttempts":40, + "operation":"DescribeCacheClusters" + }, + "ReplicationGroupAvailable":{ + "acceptors":[ + { + "argument":"ReplicationGroups[].Status", + "expected":"available", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"ReplicationGroups[].Status", + "expected":"deleted", + "matcher":"pathAny", + "state":"failure" + } + ], + "delay":15, + "description":"Wait until ElastiCache replication group is available.", + "maxAttempts":40, + "operation":"DescribeReplicationGroups" + }, + "ReplicationGroupDeleted":{ + "acceptors":[ + { + "argument":"ReplicationGroups[].Status", + "expected":"deleted", + "matcher":"pathAll", + "state":"success" + }, + { + "argument":"ReplicationGroups[].Status", + "expected":"available", + "matcher":"pathAny", + "state":"failure" + }, + { + "expected":"ReplicationGroupNotFoundFault", + "matcher":"error", + "state":"success" + } + ], + "delay":15, + "description":"Wait until ElastiCache replication group is deleted.", + "maxAttempts":40, + "operation":"DescribeReplicationGroups" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..89ab5c5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/examples-1.json new file mode 100644 index 0000000..0fded62 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/examples-1.json @@ -0,0 +1,1109 @@ +{ + "version": "1.0", + "examples": { + "AbortEnvironmentUpdate": [ + { + "input": { + "EnvironmentName": "my-env" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following code aborts a running application version deployment for an environment named my-env:", + "id": "to-abort-a-deployment-1456267848227", + "title": "To abort a deployment" + } + ], + "CheckDNSAvailability": [ + { + "input": { + "CNAMEPrefix": "my-cname" + }, + "output": { + "Available": true, + "FullyQualifiedCNAME": "my-cname.us-west-2.elasticbeanstalk.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation checks the availability of the subdomain my-cname:", + "id": "to-check-the-availability-of-a-cname-1456268589537", + "title": "To check the availability of a CNAME" + } + ], + "CreateApplication": [ + { + "input": { + "ApplicationName": "my-app", + "Description": "my application" + }, + "output": { + "Application": { + "ApplicationName": "my-app", + "ConfigurationTemplates": [ + + ], + "DateCreated": "2015-02-12T18:32:21.181Z", + "DateUpdated": "2015-02-12T18:32:21.181Z", + "Description": "my application" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation creates a new application named my-app:", + "id": "to-create-a-new-application-1456268895683", + "title": "To create a new application" + } + ], + "CreateApplicationVersion": [ + { + "input": { + "ApplicationName": "my-app", + "AutoCreateApplication": true, + "Description": "my-app-v1", + "Process": true, + "SourceBundle": { + "S3Bucket": "my-bucket", + "S3Key": "sample.war" + }, + "VersionLabel": "v1" + }, + "output": { + "ApplicationVersion": { + "ApplicationName": "my-app", + "DateCreated": "2015-02-03T23:01:25.412Z", + "DateUpdated": "2015-02-03T23:01:25.412Z", + "Description": "my-app-v1", + "SourceBundle": { + "S3Bucket": "my-bucket", + "S3Key": "sample.war" + }, + "VersionLabel": "v1" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation creates a new version (v1) of an application named my-app:", + "id": "to-create-a-new-application-1456268895683", + "title": "To create a new application" + } + ], + "CreateConfigurationTemplate": [ + { + "input": { + "ApplicationName": "my-app", + "EnvironmentId": "e-rpqsewtp2j", + "TemplateName": "my-app-v1" + }, + "output": { + "ApplicationName": "my-app", + "DateCreated": "2015-08-12T18:40:39Z", + "DateUpdated": "2015-08-12T18:40:39Z", + "SolutionStackName": "64bit Amazon Linux 2015.03 v2.0.0 running Tomcat 8 Java 8", + "TemplateName": "my-app-v1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation creates a configuration template named my-app-v1 from the settings applied to an environment with the id e-rpqsewtp2j:", + "id": "to-create-a-configuration-template-1456269283586", + "title": "To create a configuration template" + } + ], + "CreateEnvironment": [ + { + "input": { + "ApplicationName": "my-app", + "CNAMEPrefix": "my-app", + "EnvironmentName": "my-env", + "SolutionStackName": "64bit Amazon Linux 2015.03 v2.0.0 running Tomcat 8 Java 8", + "VersionLabel": "v1" + }, + "output": { + "ApplicationName": "my-app", + "CNAME": "my-app.elasticbeanstalk.com", + "DateCreated": "2015-02-03T23:04:54.479Z", + "DateUpdated": "2015-02-03T23:04:54.479Z", + "EnvironmentId": "e-izqpassy4h", + "EnvironmentName": "my-env", + "Health": "Grey", + "SolutionStackName": "64bit Amazon Linux 2015.03 v2.0.0 running Tomcat 8 Java 8", + "Status": "Launching", + "Tier": { + "Name": "WebServer", + "Type": "Standard", + "Version": " " + }, + "VersionLabel": "v1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation creates a new environment for version v1 of a java application named my-app:", + "id": "to-create-a-new-environment-for-an-application-1456269380396", + "title": "To create a new environment for an application" + } + ], + "CreateStorageLocation": [ + { + "output": { + "S3Bucket": "elasticbeanstalk-us-west-2-0123456789012" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation creates a new environment for version v1 of a java application named my-app:", + "id": "to-create-a-new-environment-for-an-application-1456269380396", + "title": "To create a new environment for an application" + } + ], + "DeleteApplication": [ + { + "input": { + "ApplicationName": "my-app" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation deletes an application named my-app:", + "id": "to-delete-an-application-1456269699366", + "title": "To delete an application" + } + ], + "DeleteApplicationVersion": [ + { + "input": { + "ApplicationName": "my-app", + "DeleteSourceBundle": true, + "VersionLabel": "22a0-stage-150819_182129" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation deletes an application version named 22a0-stage-150819_182129 for an application named my-app:", + "id": "to-delete-an-application-version-1456269792956", + "title": "To delete an application version" + } + ], + "DeleteConfigurationTemplate": [ + { + "input": { + "ApplicationName": "my-app", + "TemplateName": "my-template" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation deletes a configuration template named my-template for an application named my-app:", + "id": "to-delete-a-configuration-template-1456269836701", + "title": "To delete a configuration template" + } + ], + "DeleteEnvironmentConfiguration": [ + { + "input": { + "ApplicationName": "my-app", + "EnvironmentName": "my-env" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation deletes a draft configuration for an environment named my-env:", + "id": "to-delete-a-draft-configuration-1456269886654", + "title": "To delete a draft configuration" + } + ], + "DescribeApplicationVersions": [ + { + "input": { + "ApplicationName": "my-app", + "VersionLabels": [ + "v2" + ] + }, + "output": { + "ApplicationVersions": [ + { + "ApplicationName": "my-app", + "DateCreated": "2015-07-23T01:32:26.079Z", + "DateUpdated": "2015-07-23T01:32:26.079Z", + "Description": "update cover page", + "SourceBundle": { + "S3Bucket": "elasticbeanstalk-us-west-2-015321684451", + "S3Key": "my-app/5026-stage-150723_224258.war" + }, + "VersionLabel": "v2" + }, + { + "ApplicationName": "my-app", + "DateCreated": "2015-07-23T22:26:10.816Z", + "DateUpdated": "2015-07-23T22:26:10.816Z", + "Description": "initial version", + "SourceBundle": { + "S3Bucket": "elasticbeanstalk-us-west-2-015321684451", + "S3Key": "my-app/5026-stage-150723_222618.war" + }, + "VersionLabel": "v1" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation retrieves information about an application version labeled v2:", + "id": "to-view-information-about-an-application-version-1456269947428", + "title": "To view information about an application version" + } + ], + "DescribeApplications": [ + { + "input": { + }, + "output": { + "Applications": [ + { + "ApplicationName": "ruby", + "ConfigurationTemplates": [ + + ], + "DateCreated": "2015-08-13T21:05:44.376Z", + "DateUpdated": "2015-08-13T21:05:44.376Z", + "Versions": [ + "Sample Application" + ] + }, + { + "ApplicationName": "pythonsample", + "ConfigurationTemplates": [ + + ], + "DateCreated": "2015-08-13T19:05:43.637Z", + "DateUpdated": "2015-08-13T19:05:43.637Z", + "Description": "Application created from the EB CLI using \"eb init\"", + "Versions": [ + "Sample Application" + ] + }, + { + "ApplicationName": "nodejs-example", + "ConfigurationTemplates": [ + + ], + "DateCreated": "2015-08-06T17:50:02.486Z", + "DateUpdated": "2015-08-06T17:50:02.486Z", + "Versions": [ + "add elasticache", + "First Release" + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation retrieves information about applications in the current region:", + "id": "to-view-a-list-of-applications-1456270027373", + "title": "To view a list of applications" + } + ], + "DescribeConfigurationOptions": [ + { + "input": { + "ApplicationName": "my-app", + "EnvironmentName": "my-env" + }, + "output": { + "Options": [ + { + "ChangeSeverity": "NoInterruption", + "DefaultValue": "30", + "MaxValue": 300, + "MinValue": 5, + "Name": "Interval", + "Namespace": "aws:elb:healthcheck", + "UserDefined": false, + "ValueType": "Scalar" + }, + { + "ChangeSeverity": "NoInterruption", + "DefaultValue": "2000000", + "MinValue": 0, + "Name": "LowerThreshold", + "Namespace": "aws:autoscaling:trigger", + "UserDefined": false, + "ValueType": "Scalar" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation retrieves descriptions of all available configuration options for an environment named my-env:", + "id": "to-view-configuration-options-for-an-environment-1456276763917", + "title": "To view configuration options for an environment" + } + ], + "DescribeConfigurationSettings": [ + { + "input": { + "ApplicationName": "my-app", + "EnvironmentName": "my-env" + }, + "output": { + "ConfigurationSettings": [ + { + "ApplicationName": "my-app", + "DateCreated": "2015-08-13T19:16:25Z", + "DateUpdated": "2015-08-13T23:30:07Z", + "DeploymentStatus": "deployed", + "Description": "Environment created from the EB CLI using \"eb create\"", + "EnvironmentName": "my-env", + "OptionSettings": [ + { + "Namespace": "aws:autoscaling:asg", + "OptionName": "Availability Zones", + "ResourceName": "AWSEBAutoScalingGroup", + "Value": "Any" + }, + { + "Namespace": "aws:autoscaling:asg", + "OptionName": "Cooldown", + "ResourceName": "AWSEBAutoScalingGroup", + "Value": "360" + }, + { + "Namespace": "aws:elb:policies", + "OptionName": "ConnectionDrainingTimeout", + "ResourceName": "AWSEBLoadBalancer", + "Value": "20" + }, + { + "Namespace": "aws:elb:policies", + "OptionName": "ConnectionSettingIdleTimeout", + "ResourceName": "AWSEBLoadBalancer", + "Value": "60" + } + ], + "SolutionStackName": "64bit Amazon Linux 2015.03 v2.0.0 running Tomcat 8 Java 8" + } + ] + }, + "comments": { + "input": { + }, + "output": { + "abbreviated": "Output is abbreviated" + } + }, + "description": "The following operation retrieves configuration settings for an environment named my-env:", + "id": "to-view-configurations-settings-for-an-environment-1456276924537", + "title": "To view configurations settings for an environment" + } + ], + "DescribeEnvironmentHealth": [ + { + "input": { + "AttributeNames": [ + "All" + ], + "EnvironmentName": "my-env" + }, + "output": { + "ApplicationMetrics": { + "Duration": 10, + "Latency": { + "P10": 0.001, + "P50": 0.001, + "P75": 0.002, + "P85": 0.003, + "P90": 0.003, + "P95": 0.004, + "P99": 0.004, + "P999": 0.004 + }, + "RequestCount": 45, + "StatusCodes": { + "Status2xx": 45, + "Status3xx": 0, + "Status4xx": 0, + "Status5xx": 0 + } + }, + "Causes": [ + + ], + "Color": "Green", + "EnvironmentName": "my-env", + "HealthStatus": "Ok", + "InstancesHealth": { + "Degraded": 0, + "Info": 0, + "NoData": 0, + "Ok": 1, + "Pending": 0, + "Severe": 0, + "Unknown": 0, + "Warning": 0 + }, + "RefreshedAt": "2015-08-20T21:09:18Z" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation retrieves overall health information for an environment named my-env:", + "id": "to-view-environment-health-1456277109510", + "title": "To view environment health" + } + ], + "DescribeEnvironmentResources": [ + { + "input": { + "EnvironmentName": "my-env" + }, + "output": { + "EnvironmentResources": { + "AutoScalingGroups": [ + { + "Name": "awseb-e-qu3fyyjyjs-stack-AWSEBAutoScalingGroup-QSB2ZO88SXZT" + } + ], + "EnvironmentName": "my-env", + "Instances": [ + { + "Id": "i-0c91c786" + } + ], + "LaunchConfigurations": [ + { + "Name": "awseb-e-qu3fyyjyjs-stack-AWSEBAutoScalingLaunchConfiguration-1UUVQIBC96TQ2" + } + ], + "LoadBalancers": [ + { + "Name": "awseb-e-q-AWSEBLoa-1EEPZ0K98BIF0" + } + ], + "Queues": [ + + ], + "Triggers": [ + + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation retrieves information about resources in an environment named my-env:", + "id": "to-view-information-about-the-aws-resources-in-your-environment-1456277206232", + "title": "To view information about the AWS resources in your environment" + } + ], + "DescribeEnvironments": [ + { + "input": { + "EnvironmentNames": [ + "my-env" + ] + }, + "output": { + "Environments": [ + { + "AbortableOperationInProgress": false, + "ApplicationName": "my-app", + "CNAME": "my-env.elasticbeanstalk.com", + "DateCreated": "2015-08-07T20:48:49.599Z", + "DateUpdated": "2015-08-12T18:16:55.019Z", + "EndpointURL": "awseb-e-w-AWSEBLoa-1483140XB0Q4L-109QXY8121.us-west-2.elb.amazonaws.com", + "EnvironmentId": "e-rpqsewtp2j", + "EnvironmentName": "my-env", + "Health": "Green", + "SolutionStackName": "64bit Amazon Linux 2015.03 v2.0.0 running Tomcat 8 Java 8", + "Status": "Ready", + "Tier": { + "Name": "WebServer", + "Type": "Standard", + "Version": " " + }, + "VersionLabel": "7f58-stage-150812_025409" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation retrieves information about an environment named my-env:", + "id": "to-view-information-about-an-environment-1456277288662", + "title": "To view information about an environment" + } + ], + "DescribeEvents": [ + { + "input": { + "EnvironmentName": "my-env" + }, + "output": { + "Events": [ + { + "ApplicationName": "my-app", + "EnvironmentName": "my-env", + "EventDate": "2015-08-20T07:06:53.535Z", + "Message": "Environment health has transitioned from Info to Ok.", + "Severity": "INFO" + }, + { + "ApplicationName": "my-app", + "EnvironmentName": "my-env", + "EventDate": "2015-08-20T07:06:02.049Z", + "Message": "Environment update completed successfully.", + "RequestId": "b7f3960b-4709-11e5-ba1e-07e16200da41", + "Severity": "INFO" + }, + { + "ApplicationName": "my-app", + "EnvironmentName": "my-env", + "EventDate": "2015-08-13T19:16:27.561Z", + "Message": "Using elasticbeanstalk-us-west-2-012445113685 as Amazon S3 storage bucket for environment data.", + "RequestId": "ca8dfbf6-41ef-11e5-988b-651aa638f46b", + "Severity": "INFO" + }, + { + "ApplicationName": "my-app", + "EnvironmentName": "my-env", + "EventDate": "2015-08-13T19:16:26.581Z", + "Message": "createEnvironment is starting.", + "RequestId": "cdfba8f6-41ef-11e5-988b-65638f41aa6b", + "Severity": "INFO" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation retrieves events for an environment named my-env:", + "id": "to-view-events-for-an-environment-1456277367589", + "title": "To view events for an environment" + } + ], + "DescribeInstancesHealth": [ + { + "input": { + "AttributeNames": [ + "All" + ], + "EnvironmentName": "my-env" + }, + "output": { + "InstanceHealthList": [ + { + "ApplicationMetrics": { + "Duration": 10, + "Latency": { + "P10": 0, + "P50": 0.001, + "P75": 0.002, + "P85": 0.003, + "P90": 0.004, + "P95": 0.005, + "P99": 0.006, + "P999": 0.006 + }, + "RequestCount": 48, + "StatusCodes": { + "Status2xx": 47, + "Status3xx": 0, + "Status4xx": 1, + "Status5xx": 0 + } + }, + "Causes": [ + + ], + "Color": "Green", + "HealthStatus": "Ok", + "InstanceId": "i-08691cc7", + "LaunchedAt": "2015-08-13T19:17:09Z", + "System": { + "CPUUtilization": { + "IOWait": 0.2, + "IRQ": 0, + "Idle": 97.8, + "Nice": 0.1, + "SoftIRQ": 0.1, + "System": 0.3, + "User": 1.5 + }, + "LoadAverage": [ + 0, + 0.02, + 0.05 + ] + } + } + ], + "RefreshedAt": "2015-08-20T21:09:08Z" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation retrieves health information for instances in an environment named my-env:", + "id": "to-view-environment-health-1456277424757", + "title": "To view environment health" + } + ], + "ListAvailableSolutionStacks": [ + { + "output": { + "SolutionStackDetails": [ + { + "PermittedFileTypes": [ + "zip" + ], + "SolutionStackName": "64bit Amazon Linux 2015.03 v2.0.0 running Node.js" + } + ], + "SolutionStacks": [ + "64bit Amazon Linux 2015.03 v2.0.0 running Node.js", + "64bit Amazon Linux 2015.03 v2.0.0 running PHP 5.6", + "64bit Amazon Linux 2015.03 v2.0.0 running PHP 5.5", + "64bit Amazon Linux 2015.03 v2.0.0 running PHP 5.4", + "64bit Amazon Linux 2015.03 v2.0.0 running Python 3.4", + "64bit Amazon Linux 2015.03 v2.0.0 running Python 2.7", + "64bit Amazon Linux 2015.03 v2.0.0 running Python", + "64bit Amazon Linux 2015.03 v2.0.0 running Ruby 2.2 (Puma)", + "64bit Amazon Linux 2015.03 v2.0.0 running Ruby 2.2 (Passenger Standalone)", + "64bit Amazon Linux 2015.03 v2.0.0 running Ruby 2.1 (Puma)", + "64bit Amazon Linux 2015.03 v2.0.0 running Ruby 2.1 (Passenger Standalone)", + "64bit Amazon Linux 2015.03 v2.0.0 running Ruby 2.0 (Puma)", + "64bit Amazon Linux 2015.03 v2.0.0 running Ruby 2.0 (Passenger Standalone)", + "64bit Amazon Linux 2015.03 v2.0.0 running Ruby 1.9.3", + "64bit Amazon Linux 2015.03 v2.0.0 running Tomcat 8 Java 8", + "64bit Amazon Linux 2015.03 v2.0.0 running Tomcat 7 Java 7", + "64bit Amazon Linux 2015.03 v2.0.0 running Tomcat 7 Java 6", + "64bit Windows Server Core 2012 R2 running IIS 8.5", + "64bit Windows Server 2012 R2 running IIS 8.5", + "64bit Windows Server 2012 running IIS 8", + "64bit Windows Server 2008 R2 running IIS 7.5", + "64bit Amazon Linux 2015.03 v2.0.0 running Docker 1.6.2", + "64bit Amazon Linux 2015.03 v2.0.0 running Multi-container Docker 1.6.2 (Generic)", + "64bit Debian jessie v2.0.0 running GlassFish 4.1 Java 8 (Preconfigured - Docker)", + "64bit Debian jessie v2.0.0 running GlassFish 4.0 Java 7 (Preconfigured - Docker)", + "64bit Debian jessie v2.0.0 running Go 1.4 (Preconfigured - Docker)", + "64bit Debian jessie v2.0.0 running Go 1.3 (Preconfigured - Docker)", + "64bit Debian jessie v2.0.0 running Python 3.4 (Preconfigured - Docker)" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation lists solution stacks for all currently available platform configurations and any that you have used in the past:", + "id": "to-view-solution-stacks-1456277504811", + "title": "To view solution stacks" + } + ], + "RebuildEnvironment": [ + { + "input": { + "EnvironmentName": "my-env" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation terminates and recreates the resources in an environment named my-env:", + "id": "to-rebuild-an-environment-1456277600918", + "title": "To rebuild an environment" + } + ], + "RequestEnvironmentInfo": [ + { + "input": { + "EnvironmentName": "my-env", + "InfoType": "tail" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation requests logs from an environment named my-env:", + "id": "to-request-tailed-logs-1456277657045", + "title": "To request tailed logs" + } + ], + "RestartAppServer": [ + { + "input": { + "EnvironmentName": "my-env" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation restarts application servers on all instances in an environment named my-env:", + "id": "to-restart-application-servers-1456277739302", + "title": "To restart application servers" + } + ], + "RetrieveEnvironmentInfo": [ + { + "input": { + "EnvironmentName": "my-env", + "InfoType": "tail" + }, + "output": { + "EnvironmentInfo": [ + { + "Ec2InstanceId": "i-09c1c867", + "InfoType": "tail", + "Message": "https://elasticbeanstalk-us-west-2-0123456789012.s3.amazonaws.com/resources/environments/logs/tail/e-fyqyju3yjs/i-09c1c867/TailLogs-1440109397703.out?AWSAccessKeyId=AKGPT4J56IAJ2EUBL5CQ&Expires=1440195891&Signature=n%2BEalOV6A2HIOx4Rcfb7LT16bBM%3D", + "SampleTimestamp": "2015-08-20T22:23:17.703Z" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation retrieves a link to logs from an environment named my-env:", + "id": "to-retrieve-tailed-logs-1456277792734", + "title": "To retrieve tailed logs" + } + ], + "SwapEnvironmentCNAMEs": [ + { + "input": { + "DestinationEnvironmentName": "my-env-green", + "SourceEnvironmentName": "my-env-blue" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation swaps the assigned subdomains of two environments:", + "id": "to-swap-environment-cnames-1456277839438", + "title": "To swap environment CNAMES" + } + ], + "TerminateEnvironment": [ + { + "input": { + "EnvironmentName": "my-env" + }, + "output": { + "AbortableOperationInProgress": false, + "ApplicationName": "my-app", + "CNAME": "my-env.elasticbeanstalk.com", + "DateCreated": "2015-08-12T18:52:53.622Z", + "DateUpdated": "2015-08-12T19:05:54.744Z", + "EndpointURL": "awseb-e-f-AWSEBLoa-1I9XUMP4-8492WNUP202574.us-west-2.elb.amazonaws.com", + "EnvironmentId": "e-fh2eravpns", + "EnvironmentName": "my-env", + "Health": "Grey", + "SolutionStackName": "64bit Amazon Linux 2015.03 v2.0.0 running Tomcat 8 Java 8", + "Status": "Terminating", + "Tier": { + "Name": "WebServer", + "Type": "Standard", + "Version": " " + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation terminates an Elastic Beanstalk environment named my-env:", + "id": "to-terminate-an-environment-1456277888556", + "title": "To terminate an environment" + } + ], + "UpdateApplication": [ + { + "input": { + "ApplicationName": "my-app", + "Description": "my Elastic Beanstalk application" + }, + "output": { + "Application": { + "ApplicationName": "my-app", + "ConfigurationTemplates": [ + + ], + "DateCreated": "2015-08-13T19:15:50.449Z", + "DateUpdated": "2015-08-20T22:34:56.195Z", + "Description": "my Elastic Beanstalk application", + "Versions": [ + "2fba-stage-150819_234450", + "bf07-stage-150820_214945", + "93f8", + "fd7c-stage-150820_000431", + "22a0-stage-150819_185942" + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation updates the description of an application named my-app:", + "id": "to-change-an-applications-description-1456277957075", + "title": "To change an application's description" + } + ], + "UpdateApplicationVersion": [ + { + "input": { + "ApplicationName": "my-app", + "Description": "new description", + "VersionLabel": "22a0-stage-150819_185942" + }, + "output": { + "ApplicationVersion": { + "ApplicationName": "my-app", + "DateCreated": "2015-08-19T18:59:17.646Z", + "DateUpdated": "2015-08-20T22:53:28.871Z", + "Description": "new description", + "SourceBundle": { + "S3Bucket": "elasticbeanstalk-us-west-2-0123456789012", + "S3Key": "my-app/22a0-stage-150819_185942.war" + }, + "VersionLabel": "22a0-stage-150819_185942" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation updates the description of an application version named 22a0-stage-150819_185942:", + "id": "to-change-an-application-versions-description-1456278019237", + "title": "To change an application version's description" + } + ], + "UpdateConfigurationTemplate": [ + { + "input": { + "ApplicationName": "my-app", + "OptionsToRemove": [ + { + "Namespace": "aws:elasticbeanstalk:healthreporting:system", + "OptionName": "ConfigDocument" + } + ], + "TemplateName": "my-template" + }, + "output": { + "ApplicationName": "my-app", + "DateCreated": "2015-08-20T22:39:31Z", + "DateUpdated": "2015-08-20T22:43:11Z", + "SolutionStackName": "64bit Amazon Linux 2015.03 v2.0.0 running Tomcat 8 Java 8", + "TemplateName": "my-template" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation removes the configured CloudWatch custom health metrics configuration ConfigDocument from a saved configuration template named my-template:", + "id": "to-update-a-configuration-template-1456278075300", + "title": "To update a configuration template" + } + ], + "UpdateEnvironment": [ + { + "input": { + "EnvironmentName": "my-env", + "VersionLabel": "v2" + }, + "output": { + "ApplicationName": "my-app", + "CNAME": "my-env.elasticbeanstalk.com", + "DateCreated": "2015-02-03T23:04:54.453Z", + "DateUpdated": "2015-02-03T23:12:29.119Z", + "EndpointURL": "awseb-e-i-AWSEBLoa-1RDLX6TC9VUAO-0123456789.us-west-2.elb.amazonaws.com", + "EnvironmentId": "e-szqipays4h", + "EnvironmentName": "my-env", + "Health": "Grey", + "SolutionStackName": "64bit Amazon Linux running Tomcat 7", + "Status": "Updating", + "Tier": { + "Name": "WebServer", + "Type": "Standard", + "Version": " " + }, + "VersionLabel": "v2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation updates an environment named \"my-env\" to version \"v2\" of the application to which it belongs:", + "id": "to-update-an-environment-to-a-new-version-1456278210718", + "title": "To update an environment to a new version" + }, + { + "input": { + "EnvironmentName": "my-env", + "OptionSettings": [ + { + "Namespace": "aws:elb:healthcheck", + "OptionName": "Interval", + "Value": "15" + }, + { + "Namespace": "aws:elb:healthcheck", + "OptionName": "Timeout", + "Value": "8" + }, + { + "Namespace": "aws:elb:healthcheck", + "OptionName": "HealthyThreshold", + "Value": "2" + }, + { + "Namespace": "aws:elb:healthcheck", + "OptionName": "UnhealthyThreshold", + "Value": "3" + } + ] + }, + "output": { + "AbortableOperationInProgress": true, + "ApplicationName": "my-app", + "CNAME": "my-env.elasticbeanstalk.com", + "DateCreated": "2015-08-07T20:48:49.599Z", + "DateUpdated": "2015-08-12T18:15:23.804Z", + "EndpointURL": "awseb-e-w-AWSEBLoa-14XB83101Q4L-104QXY80921.sa-east-1.elb.amazonaws.com", + "EnvironmentId": "e-wtp2rpqsej", + "EnvironmentName": "my-env", + "Health": "Grey", + "SolutionStackName": "64bit Amazon Linux 2015.03 v2.0.0 running Tomcat 8 Java 8", + "Status": "Updating", + "Tier": { + "Name": "WebServer", + "Type": "Standard", + "Version": " " + }, + "VersionLabel": "7f58-stage-150812_025409" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation configures several options in the aws:elb:loadbalancer namespace:", + "id": "to-configure-option-settings-1456278286349", + "title": "To configure option settings" + } + ], + "ValidateConfigurationSettings": [ + { + "input": { + "ApplicationName": "my-app", + "EnvironmentName": "my-env", + "OptionSettings": [ + { + "Namespace": "aws:elasticbeanstalk:healthreporting:system", + "OptionName": "ConfigDocument", + "Value": "{\"CloudWatchMetrics\": {\"Environment\": {\"ApplicationLatencyP99.9\": null,\"InstancesSevere\": 60,\"ApplicationLatencyP90\": 60,\"ApplicationLatencyP99\": null,\"ApplicationLatencyP95\": 60,\"InstancesUnknown\": 60,\"ApplicationLatencyP85\": 60,\"InstancesInfo\": null,\"ApplicationRequests2xx\": null,\"InstancesDegraded\": null,\"InstancesWarning\": 60,\"ApplicationLatencyP50\": 60,\"ApplicationRequestsTotal\": null,\"InstancesNoData\": null,\"InstancesPending\": 60,\"ApplicationLatencyP10\": null,\"ApplicationRequests5xx\": null,\"ApplicationLatencyP75\": null,\"InstancesOk\": 60,\"ApplicationRequests3xx\": null,\"ApplicationRequests4xx\": null},\"Instance\": {\"ApplicationLatencyP99.9\": null,\"ApplicationLatencyP90\": 60,\"ApplicationLatencyP99\": null,\"ApplicationLatencyP95\": null,\"ApplicationLatencyP85\": null,\"CPUUser\": 60,\"ApplicationRequests2xx\": null,\"CPUIdle\": null,\"ApplicationLatencyP50\": null,\"ApplicationRequestsTotal\": 60,\"RootFilesystemUtil\": null,\"LoadAverage1min\": null,\"CPUIrq\": null,\"CPUNice\": 60,\"CPUIowait\": 60,\"ApplicationLatencyP10\": null,\"LoadAverage5min\": null,\"ApplicationRequests5xx\": null,\"ApplicationLatencyP75\": 60,\"CPUSystem\": 60,\"ApplicationRequests3xx\": 60,\"ApplicationRequests4xx\": null,\"InstanceHealth\": null,\"CPUSoftirq\": 60}},\"Version\": 1}" + } + ] + }, + "output": { + "Messages": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation validates a CloudWatch custom metrics config document:", + "id": "to-validate-configuration-settings-1456278393654", + "title": "To validate configuration settings" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/paginators-1.json new file mode 100644 index 0000000..4f53c86 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "DescribeEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxRecords", + "result_key": "Events" + }, + "DescribeApplicationVersions": { + "input_token": "NextToken", + "limit_key": "MaxRecords", + "output_token": "NextToken", + "result_key": "ApplicationVersions" + }, + "DescribeEnvironmentManagedActionHistory": { + "input_token": "NextToken", + "limit_key": "MaxItems", + "output_token": "NextToken", + "result_key": "ManagedActionHistoryItems" + }, + "DescribeEnvironments": { + "input_token": "NextToken", + "limit_key": "MaxRecords", + "output_token": "NextToken", + "result_key": "Environments" + }, + "ListPlatformVersions": { + "input_token": "NextToken", + "limit_key": "MaxRecords", + "output_token": "NextToken", + "result_key": "PlatformSummaryList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/service-2.json.gz new file mode 100644 index 0000000..c76d447 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/waiters-2.json new file mode 100644 index 0000000..4fb906b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elasticbeanstalk/2010-12-01/waiters-2.json @@ -0,0 +1,63 @@ +{ + "version": 2, + "waiters": { + "EnvironmentExists": { + "delay": 20, + "maxAttempts": 20, + "operation": "DescribeEnvironments", + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Environments[].Status", + "expected": "Ready" + }, + { + "state": "retry", + "matcher": "pathAll", + "argument": "Environments[].Status", + "expected": "Launching" + } + ] + }, + "EnvironmentUpdated": { + "delay": 20, + "maxAttempts": 20, + "operation": "DescribeEnvironments", + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Environments[].Status", + "expected": "Ready" + }, + { + "state": "retry", + "matcher": "pathAll", + "argument": "Environments[].Status", + "expected": "Updating" + } + ] + }, + "EnvironmentTerminated": { + "delay": 20, + "maxAttempts": 20, + "operation": "DescribeEnvironments", + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Environments[].Status", + "expected": "Terminated" + }, + { + "state": "retry", + "matcher": "pathAll", + "argument": "Environments[].Status", + "expected": "Terminating" + } + ] + } + } +} + diff --git a/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..5cdb688 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/examples-1.json new file mode 100644 index 0000000..ce50fdd --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/examples-1.json @@ -0,0 +1,1036 @@ +{ + "version": "1.0", + "examples": { + "AddTags": [ + { + "input": { + "LoadBalancerNames": [ + "my-load-balancer" + ], + "Tags": [ + { + "Key": "project", + "Value": "lima" + }, + { + "Key": "department", + "Value": "digital-media" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds two tags to the specified load balancer.", + "id": "elb-add-tags-1", + "title": "To add tags to a load balancer" + } + ], + "ApplySecurityGroupsToLoadBalancer": [ + { + "input": { + "LoadBalancerName": "my-load-balancer", + "SecurityGroups": [ + "sg-fc448899" + ] + }, + "output": { + "SecurityGroups": [ + "sg-fc448899" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates a security group with the specified load balancer in a VPC.", + "id": "elb-apply-security-groups-to-load-balancer-1", + "title": "To associate a security group with a load balancer in a VPC" + } + ], + "AttachLoadBalancerToSubnets": [ + { + "input": { + "LoadBalancerName": "my-load-balancer", + "Subnets": [ + "subnet-0ecac448" + ] + }, + "output": { + "Subnets": [ + "subnet-15aaab61", + "subnet-0ecac448" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds the specified subnet to the set of configured subnets for the specified load balancer.", + "id": "elb-attach-load-balancer-to-subnets-1", + "title": "To attach subnets to a load balancer" + } + ], + "ConfigureHealthCheck": [ + { + "input": { + "HealthCheck": { + "HealthyThreshold": 2, + "Interval": 30, + "Target": "HTTP:80/png", + "Timeout": 3, + "UnhealthyThreshold": 2 + }, + "LoadBalancerName": "my-load-balancer" + }, + "output": { + "HealthCheck": { + "HealthyThreshold": 2, + "Interval": 30, + "Target": "HTTP:80/png", + "Timeout": 3, + "UnhealthyThreshold": 2 + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example specifies the health check settings used to evaluate the health of your backend EC2 instances.", + "id": "elb-configure-health-check-1", + "title": "To specify the health check settings for your backend EC2 instances" + } + ], + "CreateAppCookieStickinessPolicy": [ + { + "input": { + "CookieName": "my-app-cookie", + "LoadBalancerName": "my-load-balancer", + "PolicyName": "my-app-cookie-policy" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example generates a stickiness policy that follows the sticky session lifetimes of the application-generated cookie.", + "id": "elb-create-app-cookie-stickiness-policy-1", + "title": "To generate a stickiness policy for your load balancer" + } + ], + "CreateLBCookieStickinessPolicy": [ + { + "input": { + "CookieExpirationPeriod": 60, + "LoadBalancerName": "my-load-balancer", + "PolicyName": "my-duration-cookie-policy" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example generates a stickiness policy with sticky session lifetimes controlled by the specified expiration period.", + "id": "elb-create-lb-cookie-stickiness-policy-1", + "title": "To generate a duration-based stickiness policy for your load balancer" + } + ], + "CreateLoadBalancer": [ + { + "input": { + "Listeners": [ + { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + } + ], + "LoadBalancerName": "my-load-balancer", + "SecurityGroups": [ + "sg-a61988c3" + ], + "Subnets": [ + "subnet-15aaab61" + ] + }, + "output": { + "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a load balancer with an HTTP listener in a VPC.", + "id": "elb-create-load-balancer-1", + "title": "To create an HTTP load balancer in a VPC" + }, + { + "input": { + "AvailabilityZones": [ + "us-west-2a" + ], + "Listeners": [ + { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + } + ], + "LoadBalancerName": "my-load-balancer" + }, + "output": { + "DNSName": "my-load-balancer-123456789.us-west-2.elb.amazonaws.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a load balancer with an HTTP listener in EC2-Classic.", + "id": "elb-create-load-balancer-2", + "title": "To create an HTTP load balancer in EC2-Classic" + }, + { + "input": { + "Listeners": [ + { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + }, + { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" + } + ], + "LoadBalancerName": "my-load-balancer", + "SecurityGroups": [ + "sg-a61988c3" + ], + "Subnets": [ + "subnet-15aaab61" + ] + }, + "output": { + "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a load balancer with an HTTPS listener in a VPC.", + "id": "elb-create-load-balancer-3", + "title": "To create an HTTPS load balancer in a VPC" + }, + { + "input": { + "AvailabilityZones": [ + "us-west-2a" + ], + "Listeners": [ + { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + }, + { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" + } + ], + "LoadBalancerName": "my-load-balancer" + }, + "output": { + "DNSName": "my-load-balancer-123456789.us-west-2.elb.amazonaws.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a load balancer with an HTTPS listener in EC2-Classic.", + "id": "elb-create-load-balancer-4", + "title": "To create an HTTPS load balancer in EC2-Classic" + }, + { + "input": { + "Listeners": [ + { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + } + ], + "LoadBalancerName": "my-load-balancer", + "Scheme": "internal", + "SecurityGroups": [ + "sg-a61988c3" + ], + "Subnets": [ + "subnet-15aaab61" + ] + }, + "output": { + "DNSName": "internal-my-load-balancer-123456789.us-west-2.elb.amazonaws.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an internal load balancer with an HTTP listener in a VPC.", + "id": "elb-create-load-balancer-5", + "title": "To create an internal load balancer" + } + ], + "CreateLoadBalancerListeners": [ + { + "input": { + "Listeners": [ + { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + } + ], + "LoadBalancerName": "my-load-balancer" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a listener for your load balancer at port 80 using the HTTP protocol.", + "id": "elb-create-load-balancer-listeners-1", + "title": "To create an HTTP listener for a load balancer" + }, + { + "input": { + "Listeners": [ + { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" + } + ], + "LoadBalancerName": "my-load-balancer" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a listener for your load balancer at port 443 using the HTTPS protocol.", + "id": "elb-create-load-balancer-listeners-2", + "title": "To create an HTTPS listener for a load balancer" + } + ], + "CreateLoadBalancerPolicy": [ + { + "input": { + "LoadBalancerName": "my-load-balancer", + "PolicyAttributes": [ + { + "AttributeName": "ProxyProtocol", + "AttributeValue": "true" + } + ], + "PolicyName": "my-ProxyProtocol-policy", + "PolicyTypeName": "ProxyProtocolPolicyType" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a policy that enables Proxy Protocol on the specified load balancer.", + "id": "elb-create-load-balancer-policy-1", + "title": "To create a policy that enables Proxy Protocol on a load balancer" + }, + { + "input": { + "LoadBalancerName": "my-load-balancer", + "PolicyAttributes": [ + { + "AttributeName": "PublicKey", + "AttributeValue": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwAYUjnfyEyXr1pxjhFWBpMlggUcqoi3kl+dS74kj//c6x7ROtusUaeQCTgIUkayttRDWchuqo1pHC1u+n5xxXnBBe2ejbb2WRsKIQ5rXEeixsjFpFsojpSQKkzhVGI6mJVZBJDVKSHmswnwLBdofLhzvllpovBPTHe+o4haAWvDBALJU0pkSI1FecPHcs2hwxf14zHoXy1e2k36A64nXW43wtfx5qcVSIxtCEOjnYRg7RPvybaGfQ+v6Iaxb/+7J5kEvZhTFQId+bSiJImF1FSUT1W1xwzBZPUbcUkkXDj45vC2s3Z8E+Lk7a3uZhvsQHLZnrfuWjBWGWvZ/MhZYgEXAMPLE" + } + ], + "PolicyName": "my-PublicKey-policy", + "PolicyTypeName": "PublicKeyPolicyType" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a public key policy.", + "id": "elb-create-load-balancer-policy-2", + "title": "To create a public key policy" + }, + { + "input": { + "LoadBalancerName": "my-load-balancer", + "PolicyAttributes": [ + { + "AttributeName": "PublicKeyPolicyName", + "AttributeValue": "my-PublicKey-policy" + } + ], + "PolicyName": "my-authentication-policy", + "PolicyTypeName": "BackendServerAuthenticationPolicyType" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a backend server authentication policy that enables authentication on your backend instance using a public key policy.", + "id": "elb-create-load-balancer-policy-3", + "title": "To create a backend server authentication policy" + } + ], + "DeleteLoadBalancer": [ + { + "input": { + "LoadBalancerName": "my-load-balancer" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified load balancer.", + "id": "elb-delete-load-balancer-1", + "title": "To delete a load balancer" + } + ], + "DeleteLoadBalancerListeners": [ + { + "input": { + "LoadBalancerName": "my-load-balancer", + "LoadBalancerPorts": [ + 80 + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the listener for the specified port from the specified load balancer.", + "id": "elb-delete-load-balancer-listeners-1", + "title": "To delete a listener from your load balancer" + } + ], + "DeleteLoadBalancerPolicy": [ + { + "input": { + "LoadBalancerName": "my-load-balancer", + "PolicyName": "my-duration-cookie-policy" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified policy from the specified load balancer. The policy must not be enabled on any listener.", + "id": "elb-delete-load-balancer-policy-1", + "title": "To delete a policy from your load balancer" + } + ], + "DeregisterInstancesFromLoadBalancer": [ + { + "input": { + "Instances": [ + { + "InstanceId": "i-d6f6fae3" + } + ], + "LoadBalancerName": "my-load-balancer" + }, + "output": { + "Instances": [ + { + "InstanceId": "i-207d9717" + }, + { + "InstanceId": "i-afefb49b" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deregisters the specified instance from the specified load balancer.", + "id": "elb-deregister-instances-from-load-balancer-1", + "title": "To deregister instances from a load balancer" + } + ], + "DescribeInstanceHealth": [ + { + "input": { + "LoadBalancerName": "my-load-balancer" + }, + "output": { + "InstanceStates": [ + { + "Description": "N/A", + "InstanceId": "i-207d9717", + "ReasonCode": "N/A", + "State": "InService" + }, + { + "Description": "N/A", + "InstanceId": "i-afefb49b", + "ReasonCode": "N/A", + "State": "InService" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the health of the instances for the specified load balancer.", + "id": "elb-describe-instance-health-1", + "title": "To describe the health of the instances for a load balancer" + } + ], + "DescribeLoadBalancerAttributes": [ + { + "input": { + "LoadBalancerName": "my-load-balancer" + }, + "output": { + "LoadBalancerAttributes": { + "AccessLog": { + "Enabled": false + }, + "ConnectionDraining": { + "Enabled": false, + "Timeout": 300 + }, + "ConnectionSettings": { + "IdleTimeout": 60 + }, + "CrossZoneLoadBalancing": { + "Enabled": false + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the attributes of the specified load balancer.", + "id": "elb-describe-load-balancer-attributes-1", + "title": "To describe the attributes of a load balancer" + } + ], + "DescribeLoadBalancerPolicies": [ + { + "input": { + "LoadBalancerName": "my-load-balancer", + "PolicyNames": [ + "my-authentication-policy" + ] + }, + "output": { + "PolicyDescriptions": [ + { + "PolicyAttributeDescriptions": [ + { + "AttributeName": "PublicKeyPolicyName", + "AttributeValue": "my-PublicKey-policy" + } + ], + "PolicyName": "my-authentication-policy", + "PolicyTypeName": "BackendServerAuthenticationPolicyType" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified policy associated with the specified load balancer.", + "id": "elb-describe-load-balancer-policies-1", + "title": "To describe a policy associated with a load balancer" + } + ], + "DescribeLoadBalancerPolicyTypes": [ + { + "input": { + "PolicyTypeNames": [ + "ProxyProtocolPolicyType" + ] + }, + "output": { + "PolicyTypeDescriptions": [ + { + "Description": "Policy that controls whether to include the IP address and port of the originating request for TCP messages. This policy operates on TCP listeners only.", + "PolicyAttributeTypeDescriptions": [ + { + "AttributeName": "ProxyProtocol", + "AttributeType": "Boolean", + "Cardinality": "ONE" + } + ], + "PolicyTypeName": "ProxyProtocolPolicyType" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified load balancer policy type.", + "id": "elb-describe-load-balancer-policy-types-1", + "title": "To describe a load balancer policy type defined by Elastic Load Balancing" + } + ], + "DescribeLoadBalancers": [ + { + "input": { + "LoadBalancerNames": [ + "my-load-balancer" + ] + }, + "output": { + "LoadBalancerDescriptions": [ + { + "AvailabilityZones": [ + "us-west-2a" + ], + "BackendServerDescriptions": [ + { + "InstancePort": 80, + "PolicyNames": [ + "my-ProxyProtocol-policy" + ] + } + ], + "CanonicalHostedZoneName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "CanonicalHostedZoneNameID": "Z3DZXE0EXAMPLE", + "CreatedTime": "2015-03-19T03:24:02.650Z", + "DNSName": "my-load-balancer-1234567890.us-west-2.elb.amazonaws.com", + "HealthCheck": { + "HealthyThreshold": 2, + "Interval": 30, + "Target": "HTTP:80/png", + "Timeout": 3, + "UnhealthyThreshold": 2 + }, + "Instances": [ + { + "InstanceId": "i-207d9717" + }, + { + "InstanceId": "i-afefb49b" + } + ], + "ListenerDescriptions": [ + { + "Listener": { + "InstancePort": 80, + "InstanceProtocol": "HTTP", + "LoadBalancerPort": 80, + "Protocol": "HTTP" + }, + "PolicyNames": [ + + ] + }, + { + "Listener": { + "InstancePort": 443, + "InstanceProtocol": "HTTPS", + "LoadBalancerPort": 443, + "Protocol": "HTTPS", + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/my-server-cert" + }, + "PolicyNames": [ + "ELBSecurityPolicy-2015-03" + ] + } + ], + "LoadBalancerName": "my-load-balancer", + "Policies": { + "AppCookieStickinessPolicies": [ + + ], + "LBCookieStickinessPolicies": [ + { + "CookieExpirationPeriod": 60, + "PolicyName": "my-duration-cookie-policy" + } + ], + "OtherPolicies": [ + "my-PublicKey-policy", + "my-authentication-policy", + "my-SSLNegotiation-policy", + "my-ProxyProtocol-policy", + "ELBSecurityPolicy-2015-03" + ] + }, + "Scheme": "internet-facing", + "SecurityGroups": [ + "sg-a61988c3" + ], + "SourceSecurityGroup": { + "GroupName": "my-elb-sg", + "OwnerAlias": "123456789012" + }, + "Subnets": [ + "subnet-15aaab61" + ], + "VPCId": "vpc-a01106c2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified load balancer.", + "id": "elb-describe-load-balancers-1", + "title": "To describe one of your load balancers" + } + ], + "DescribeTags": [ + { + "input": { + "LoadBalancerNames": [ + "my-load-balancer" + ] + }, + "output": { + "TagDescriptions": [ + { + "LoadBalancerName": "my-load-balancer", + "Tags": [ + { + "Key": "project", + "Value": "lima" + }, + { + "Key": "department", + "Value": "digital-media" + } + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the tags for the specified load balancer.", + "id": "elb-describe-tags-1", + "title": "To describe the tags for a load balancer" + } + ], + "DetachLoadBalancerFromSubnets": [ + { + "input": { + "LoadBalancerName": "my-load-balancer", + "Subnets": [ + "subnet-0ecac448" + ] + }, + "output": { + "Subnets": [ + "subnet-15aaab61" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example detaches the specified load balancer from the specified subnet.", + "id": "elb-detach-load-balancer-from-subnets-1", + "title": "To detach a load balancer from a subnet" + } + ], + "DisableAvailabilityZonesForLoadBalancer": [ + { + "input": { + "AvailabilityZones": [ + "us-west-2a" + ], + "LoadBalancerName": "my-load-balancer" + }, + "output": { + "AvailabilityZones": [ + "us-west-2b" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example removes the specified Availability Zone from the set of Availability Zones for the specified load balancer.", + "id": "elb-disable-availability-zones-for-load-balancer-1", + "title": "To disable an Availability Zone for a load balancer" + } + ], + "EnableAvailabilityZonesForLoadBalancer": [ + { + "input": { + "AvailabilityZones": [ + "us-west-2b" + ], + "LoadBalancerName": "my-load-balancer" + }, + "output": { + "AvailabilityZones": [ + "us-west-2a", + "us-west-2b" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds the specified Availability Zone to the specified load balancer.", + "id": "elb-enable-availability-zones-for-load-balancer-1", + "title": "To enable an Availability Zone for a load balancer" + } + ], + "ModifyLoadBalancerAttributes": [ + { + "input": { + "LoadBalancerAttributes": { + "CrossZoneLoadBalancing": { + "Enabled": true + } + }, + "LoadBalancerName": "my-load-balancer" + }, + "output": { + "LoadBalancerAttributes": { + "CrossZoneLoadBalancing": { + "Enabled": true + } + }, + "LoadBalancerName": "my-load-balancer" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables cross-zone load balancing for the specified load balancer.", + "id": "elb-modify-load-balancer-attributes-1", + "title": "To enable cross-zone load balancing" + }, + { + "input": { + "LoadBalancerAttributes": { + "ConnectionDraining": { + "Enabled": true, + "Timeout": 300 + } + }, + "LoadBalancerName": "my-load-balancer" + }, + "output": { + "LoadBalancerAttributes": { + "ConnectionDraining": { + "Enabled": true, + "Timeout": 300 + } + }, + "LoadBalancerName": "my-load-balancer" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables connection draining for the specified load balancer.", + "id": "elb-modify-load-balancer-attributes-2", + "title": "To enable connection draining" + } + ], + "RegisterInstancesWithLoadBalancer": [ + { + "input": { + "Instances": [ + { + "InstanceId": "i-d6f6fae3" + } + ], + "LoadBalancerName": "my-load-balancer" + }, + "output": { + "Instances": [ + { + "InstanceId": "i-d6f6fae3" + }, + { + "InstanceId": "i-207d9717" + }, + { + "InstanceId": "i-afefb49b" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example registers the specified instance with the specified load balancer.", + "id": "elb-register-instances-with-load-balancer-1", + "title": "To register instances with a load balancer" + } + ], + "RemoveTags": [ + { + "input": { + "LoadBalancerNames": [ + "my-load-balancer" + ], + "Tags": [ + { + "Key": "project" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example removes the specified tag from the specified load balancer.", + "id": "elb-remove-tags-1", + "title": "To remove tags from a load balancer" + } + ], + "SetLoadBalancerListenerSSLCertificate": [ + { + "input": { + "LoadBalancerName": "my-load-balancer", + "LoadBalancerPort": 443, + "SSLCertificateId": "arn:aws:iam::123456789012:server-certificate/new-server-cert" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example replaces the existing SSL certificate for the specified HTTPS listener.", + "id": "elb-set-load-balancer-listener-ssl-certificate-1", + "title": "To update the SSL certificate for an HTTPS listener" + } + ], + "SetLoadBalancerPoliciesForBackendServer": [ + { + "input": { + "InstancePort": 80, + "LoadBalancerName": "my-load-balancer", + "PolicyNames": [ + "my-ProxyProtocol-policy" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example replaces the policies that are currently associated with the specified port.", + "id": "elb-set-load-balancer-policies-for-backend-server-1", + "title": "To replace the policies associated with a port for a backend instance" + } + ], + "SetLoadBalancerPoliciesOfListener": [ + { + "input": { + "LoadBalancerName": "my-load-balancer", + "LoadBalancerPort": 80, + "PolicyNames": [ + "my-SSLNegotiation-policy" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example replaces the policies that are currently associated with the specified listener.", + "id": "elb-set-load-balancer-policies-of-listener-1", + "title": "To replace the policies associated with a listener" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/paginators-1.json new file mode 100644 index 0000000..b3bd330 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "DescribeLoadBalancers": { + "input_token": "Marker", + "output_token": "NextMarker", + "result_key": "LoadBalancerDescriptions", + "limit_key": "PageSize" + }, + "DescribeAccountLimits": { + "input_token": "Marker", + "limit_key": "PageSize", + "output_token": "NextMarker", + "result_key": "Limits" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/service-2.json.gz new file mode 100644 index 0000000..d9189c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/waiters-2.json new file mode 100644 index 0000000..182e070 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elb/2012-06-01/waiters-2.json @@ -0,0 +1,54 @@ +{ + "version":2, + "waiters":{ + "InstanceDeregistered": { + "delay": 15, + "operation": "DescribeInstanceHealth", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "OutOfService", + "matcher": "pathAll", + "state": "success", + "argument": "InstanceStates[].State" + }, + { + "matcher": "error", + "expected": "InvalidInstance", + "state": "success" + } + ] + }, + "AnyInstanceInService":{ + "acceptors":[ + { + "argument":"InstanceStates[].State", + "expected":"InService", + "matcher":"pathAny", + "state":"success" + } + ], + "delay":15, + "maxAttempts":40, + "operation":"DescribeInstanceHealth" + }, + "InstanceInService":{ + "acceptors":[ + { + "argument":"InstanceStates[].State", + "expected":"InService", + "matcher":"pathAll", + "state":"success" + }, + { + "matcher": "error", + "expected": "InvalidInstance", + "state": "retry" + } + ], + "delay":15, + "maxAttempts":40, + "operation":"DescribeInstanceHealth" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..5cdb688 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/examples-1.json new file mode 100644 index 0000000..508b099 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/examples-1.json @@ -0,0 +1,1384 @@ +{ + "version": "1.0", + "examples": { + "AddTags": [ + { + "input": { + "ResourceArns": [ + "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188" + ], + "Tags": [ + { + "Key": "project", + "Value": "lima" + }, + { + "Key": "department", + "Value": "digital-media" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds the specified tags to the specified load balancer.", + "id": "elbv2-add-tags-1", + "title": "To add tags to a load balancer" + } + ], + "CreateListener": [ + { + "input": { + "DefaultActions": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Type": "forward" + } + ], + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188", + "Port": 80, + "Protocol": "HTTP" + }, + "output": { + "Listeners": [ + { + "DefaultActions": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Type": "forward" + } + ], + "ListenerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2", + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188", + "Port": 80, + "Protocol": "HTTP" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an HTTP listener for the specified load balancer that forwards requests to the specified target group.", + "id": "elbv2-create-listener-1", + "title": "To create an HTTP listener" + }, + { + "input": { + "Certificates": [ + { + "CertificateArn": "arn:aws:iam::123456789012:server-certificate/my-server-cert" + } + ], + "DefaultActions": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Type": "forward" + } + ], + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188", + "Port": 443, + "Protocol": "HTTPS", + "SslPolicy": "ELBSecurityPolicy-2015-05" + }, + "output": { + "Listeners": [ + { + "Certificates": [ + { + "CertificateArn": "arn:aws:iam::123456789012:server-certificate/my-server-cert" + } + ], + "DefaultActions": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Type": "forward" + } + ], + "ListenerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2", + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188", + "Port": 443, + "Protocol": "HTTPS", + "SslPolicy": "ELBSecurityPolicy-2015-05" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an HTTPS listener for the specified load balancer that forwards requests to the specified target group. Note that you must specify an SSL certificate for an HTTPS listener. You can create and manage certificates using AWS Certificate Manager (ACM). Alternatively, you can create a certificate using SSL/TLS tools, get the certificate signed by a certificate authority (CA), and upload the certificate to AWS Identity and Access Management (IAM).", + "id": "elbv2-create-listener-2", + "title": "To create an HTTPS listener" + } + ], + "CreateLoadBalancer": [ + { + "input": { + "Name": "my-load-balancer", + "Subnets": [ + "subnet-b7d581c0", + "subnet-8360a9e7" + ] + }, + "output": { + "LoadBalancers": [ + { + "AvailabilityZones": [ + { + "SubnetId": "subnet-8360a9e7", + "ZoneName": "us-west-2a" + }, + { + "SubnetId": "subnet-b7d581c0", + "ZoneName": "us-west-2b" + } + ], + "CanonicalHostedZoneId": "Z2P70J7EXAMPLE", + "CreatedTime": "2016-03-25T21:26:12.920Z", + "DNSName": "my-load-balancer-424835706.us-west-2.elb.amazonaws.com", + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188", + "LoadBalancerName": "my-load-balancer", + "Scheme": "internet-facing", + "SecurityGroups": [ + "sg-5943793c" + ], + "State": { + "Code": "provisioning" + }, + "Type": "application", + "VpcId": "vpc-3ac0fb5f" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an Internet-facing load balancer and enables the Availability Zones for the specified subnets.", + "id": "elbv2-create-load-balancer-1", + "title": "To create an Internet-facing load balancer" + }, + { + "input": { + "Name": "my-internal-load-balancer", + "Scheme": "internal", + "SecurityGroups": [ + + ], + "Subnets": [ + "subnet-b7d581c0", + "subnet-8360a9e7" + ] + }, + "output": { + "LoadBalancers": [ + { + "AvailabilityZones": [ + { + "SubnetId": "subnet-8360a9e7", + "ZoneName": "us-west-2a" + }, + { + "SubnetId": "subnet-b7d581c0", + "ZoneName": "us-west-2b" + } + ], + "CanonicalHostedZoneId": "Z2P70J7EXAMPLE", + "CreatedTime": "2016-03-25T21:29:48.850Z", + "DNSName": "internal-my-internal-load-balancer-1529930873.us-west-2.elb.amazonaws.com", + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-internal-load-balancer/5b49b8d4303115c2", + "LoadBalancerName": "my-internal-load-balancer", + "Scheme": "internal", + "SecurityGroups": [ + "sg-5943793c" + ], + "State": { + "Code": "provisioning" + }, + "Type": "application", + "VpcId": "vpc-3ac0fb5f" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an internal load balancer and enables the Availability Zones for the specified subnets.", + "id": "elbv2-create-load-balancer-2", + "title": "To create an internal load balancer" + } + ], + "CreateRule": [ + { + "input": { + "Actions": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Type": "forward" + } + ], + "Conditions": [ + { + "Field": "path-pattern", + "Values": [ + "/img/*" + ] + } + ], + "ListenerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2", + "Priority": 10 + }, + "output": { + "Rules": [ + { + "Actions": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Type": "forward" + } + ], + "Conditions": [ + { + "Field": "path-pattern", + "Values": [ + "/img/*" + ] + } + ], + "IsDefault": false, + "Priority": "10", + "RuleArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener-rule/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2/9683b2d02a6cabee" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a rule that forwards requests to the specified target group if the URL contains the specified pattern (for example, /img/*).", + "id": "elbv2-create-rule-1", + "title": "To create a rule" + } + ], + "CreateTargetGroup": [ + { + "input": { + "Name": "my-targets", + "Port": 80, + "Protocol": "HTTP", + "VpcId": "vpc-3ac0fb5f" + }, + "output": { + "TargetGroups": [ + { + "HealthCheckIntervalSeconds": 30, + "HealthCheckPath": "/", + "HealthCheckPort": "traffic-port", + "HealthCheckProtocol": "HTTP", + "HealthCheckTimeoutSeconds": 5, + "HealthyThresholdCount": 5, + "Matcher": { + "HttpCode": "200" + }, + "Port": 80, + "Protocol": "HTTP", + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "TargetGroupName": "my-targets", + "UnhealthyThresholdCount": 2, + "VpcId": "vpc-3ac0fb5f" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a target group that you can use to route traffic to targets using HTTP on port 80. This target group uses the default health check configuration.", + "id": "elbv2-create-target-group-1", + "title": "To create a target group" + } + ], + "DeleteListener": [ + { + "input": { + "ListenerArn": "arn:aws:elasticloadbalancing:ua-west-2:123456789012:listener/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified listener.", + "id": "elbv2-delete-listener-1", + "title": "To delete a listener" + } + ], + "DeleteLoadBalancer": [ + { + "input": { + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified load balancer.", + "id": "elbv2-delete-load-balancer-1", + "title": "To delete a load balancer" + } + ], + "DeleteRule": [ + { + "input": { + "RuleArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener-rule/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2/1291d13826f405c3" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified rule.", + "id": "elbv2-delete-rule-1", + "title": "To delete a rule" + } + ], + "DeleteTargetGroup": [ + { + "input": { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified target group.", + "id": "elbv2-delete-target-group-1", + "title": "To delete a target group" + } + ], + "DeregisterTargets": [ + { + "input": { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Targets": [ + { + "Id": "i-0f76fade" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deregisters the specified instance from the specified target group.", + "id": "elbv2-deregister-targets-1", + "title": "To deregister a target from a target group" + } + ], + "DescribeListeners": [ + { + "input": { + "ListenerArns": [ + "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2" + ] + }, + "output": { + "Listeners": [ + { + "DefaultActions": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Type": "forward" + } + ], + "ListenerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2", + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188", + "Port": 80, + "Protocol": "HTTP" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified listener.", + "id": "elbv2-describe-listeners-1", + "title": "To describe a listener" + } + ], + "DescribeLoadBalancerAttributes": [ + { + "input": { + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188" + }, + "output": { + "Attributes": [ + { + "Key": "access_logs.s3.enabled", + "Value": "false" + }, + { + "Key": "idle_timeout.timeout_seconds", + "Value": "60" + }, + { + "Key": "access_logs.s3.prefix", + "Value": "" + }, + { + "Key": "deletion_protection.enabled", + "Value": "false" + }, + { + "Key": "access_logs.s3.bucket", + "Value": "" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the attributes of the specified load balancer.", + "id": "elbv2-describe-load-balancer-attributes-1", + "title": "To describe load balancer attributes" + } + ], + "DescribeLoadBalancers": [ + { + "input": { + "LoadBalancerArns": [ + "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188" + ] + }, + "output": { + "LoadBalancers": [ + { + "AvailabilityZones": [ + { + "SubnetId": "subnet-8360a9e7", + "ZoneName": "us-west-2a" + }, + { + "SubnetId": "subnet-b7d581c0", + "ZoneName": "us-west-2b" + } + ], + "CanonicalHostedZoneId": "Z2P70J7EXAMPLE", + "CreatedTime": "2016-03-25T21:26:12.920Z", + "DNSName": "my-load-balancer-424835706.us-west-2.elb.amazonaws.com", + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188", + "LoadBalancerName": "my-load-balancer", + "Scheme": "internet-facing", + "SecurityGroups": [ + "sg-5943793c" + ], + "State": { + "Code": "active" + }, + "Type": "application", + "VpcId": "vpc-3ac0fb5f" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified load balancer.", + "id": "elbv2-describe-load-balancers-1", + "title": "To describe a load balancer" + } + ], + "DescribeRules": [ + { + "input": { + "RuleArns": [ + "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener-rule/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2/9683b2d02a6cabee" + ] + }, + "output": { + "Rules": [ + { + "Actions": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Type": "forward" + } + ], + "Conditions": [ + { + "Field": "path-pattern", + "Values": [ + "/img/*" + ] + } + ], + "IsDefault": false, + "Priority": "10", + "RuleArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener-rule/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2/9683b2d02a6cabee" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified rule.", + "id": "elbv2-describe-rules-1", + "title": "To describe a rule" + } + ], + "DescribeSSLPolicies": [ + { + "input": { + "Names": [ + "ELBSecurityPolicy-2015-05" + ] + }, + "output": { + "SslPolicies": [ + { + "Ciphers": [ + { + "Name": "ECDHE-ECDSA-AES128-GCM-SHA256", + "Priority": 1 + }, + { + "Name": "ECDHE-RSA-AES128-GCM-SHA256", + "Priority": 2 + }, + { + "Name": "ECDHE-ECDSA-AES128-SHA256", + "Priority": 3 + }, + { + "Name": "ECDHE-RSA-AES128-SHA256", + "Priority": 4 + }, + { + "Name": "ECDHE-ECDSA-AES128-SHA", + "Priority": 5 + }, + { + "Name": "ECDHE-RSA-AES128-SHA", + "Priority": 6 + }, + { + "Name": "DHE-RSA-AES128-SHA", + "Priority": 7 + }, + { + "Name": "ECDHE-ECDSA-AES256-GCM-SHA384", + "Priority": 8 + }, + { + "Name": "ECDHE-RSA-AES256-GCM-SHA384", + "Priority": 9 + }, + { + "Name": "ECDHE-ECDSA-AES256-SHA384", + "Priority": 10 + }, + { + "Name": "ECDHE-RSA-AES256-SHA384", + "Priority": 11 + }, + { + "Name": "ECDHE-RSA-AES256-SHA", + "Priority": 12 + }, + { + "Name": "ECDHE-ECDSA-AES256-SHA", + "Priority": 13 + }, + { + "Name": "AES128-GCM-SHA256", + "Priority": 14 + }, + { + "Name": "AES128-SHA256", + "Priority": 15 + }, + { + "Name": "AES128-SHA", + "Priority": 16 + }, + { + "Name": "AES256-GCM-SHA384", + "Priority": 17 + }, + { + "Name": "AES256-SHA256", + "Priority": 18 + }, + { + "Name": "AES256-SHA", + "Priority": 19 + } + ], + "Name": "ELBSecurityPolicy-2015-05", + "SslProtocols": [ + "TLSv1", + "TLSv1.1", + "TLSv1.2" + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified policy used for SSL negotiation.", + "id": "elbv2-describe-ssl-policies-1", + "title": "To describe a policy used for SSL negotiation" + } + ], + "DescribeTags": [ + { + "input": { + "ResourceArns": [ + "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188" + ] + }, + "output": { + "TagDescriptions": [ + { + "ResourceArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188", + "Tags": [ + { + "Key": "project", + "Value": "lima" + }, + { + "Key": "department", + "Value": "digital-media" + } + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the tags assigned to the specified load balancer.", + "id": "elbv2-describe-tags-1", + "title": "To describe the tags assigned to a load balancer" + } + ], + "DescribeTargetGroupAttributes": [ + { + "input": { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067" + }, + "output": { + "Attributes": [ + { + "Key": "stickiness.enabled", + "Value": "false" + }, + { + "Key": "deregistration_delay.timeout_seconds", + "Value": "300" + }, + { + "Key": "stickiness.type", + "Value": "lb_cookie" + }, + { + "Key": "stickiness.lb_cookie.duration_seconds", + "Value": "86400" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the attributes of the specified target group.", + "id": "elbv2-describe-target-group-attributes-1", + "title": "To describe target group attributes" + } + ], + "DescribeTargetGroups": [ + { + "input": { + "TargetGroupArns": [ + "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067" + ] + }, + "output": { + "TargetGroups": [ + { + "HealthCheckIntervalSeconds": 30, + "HealthCheckPath": "/", + "HealthCheckPort": "traffic-port", + "HealthCheckProtocol": "HTTP", + "HealthCheckTimeoutSeconds": 5, + "HealthyThresholdCount": 5, + "LoadBalancerArns": [ + "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188" + ], + "Matcher": { + "HttpCode": "200" + }, + "Port": 80, + "Protocol": "HTTP", + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "TargetGroupName": "my-targets", + "UnhealthyThresholdCount": 2, + "VpcId": "vpc-3ac0fb5f" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the specified target group.", + "id": "elbv2-describe-target-groups-1", + "title": "To describe a target group" + } + ], + "DescribeTargetHealth": [ + { + "input": { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067" + }, + "output": { + "TargetHealthDescriptions": [ + { + "Target": { + "Id": "i-0f76fade", + "Port": 80 + }, + "TargetHealth": { + "Description": "Given target group is not configured to receive traffic from ELB", + "Reason": "Target.NotInUse", + "State": "unused" + } + }, + { + "HealthCheckPort": "80", + "Target": { + "Id": "i-0f76fade", + "Port": 80 + }, + "TargetHealth": { + "State": "healthy" + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the health of the targets for the specified target group. One target is healthy but the other is not specified in an action, so it can't receive traffic from the load balancer.", + "id": "elbv2-describe-target-health-1", + "title": "To describe the health of the targets for a target group" + }, + { + "input": { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Targets": [ + { + "Id": "i-0f76fade", + "Port": 80 + } + ] + }, + "output": { + "TargetHealthDescriptions": [ + { + "HealthCheckPort": "80", + "Target": { + "Id": "i-0f76fade", + "Port": 80 + }, + "TargetHealth": { + "State": "healthy" + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example describes the health of the specified target. This target is healthy.", + "id": "elbv2-describe-target-health-2", + "title": "To describe the health of a target" + } + ], + "ModifyListener": [ + { + "input": { + "DefaultActions": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-new-targets/2453ed029918f21f", + "Type": "forward" + } + ], + "ListenerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2" + }, + "output": { + "Listeners": [ + { + "DefaultActions": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-new-targets/2453ed029918f21f", + "Type": "forward" + } + ], + "ListenerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2", + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188", + "Port": 80, + "Protocol": "HTTP" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example changes the default action for the specified listener.", + "id": "elbv2-modify-listener-1", + "title": "To change the default action for a listener" + }, + { + "input": { + "Certificates": [ + { + "CertificateArn": "arn:aws:iam::123456789012:server-certificate/my-new-server-cert" + } + ], + "ListenerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener/app/my-load-balancer/50dc6c495c0c9188/0467ef3c8400ae65" + }, + "output": { + "Listeners": [ + { + "Certificates": [ + { + "CertificateArn": "arn:aws:iam::123456789012:server-certificate/my-new-server-cert" + } + ], + "DefaultActions": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Type": "forward" + } + ], + "ListenerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener/app/my-load-balancer/50dc6c495c0c9188/0467ef3c8400ae65", + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188", + "Port": 443, + "Protocol": "HTTPS", + "SslPolicy": "ELBSecurityPolicy-2015-05" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example changes the server certificate for the specified HTTPS listener.", + "id": "elbv2-modify-listener-2", + "title": "To change the server certificate" + } + ], + "ModifyLoadBalancerAttributes": [ + { + "input": { + "Attributes": [ + { + "Key": "deletion_protection.enabled", + "Value": "true" + } + ], + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188" + }, + "output": { + "Attributes": [ + { + "Key": "deletion_protection.enabled", + "Value": "true" + }, + { + "Key": "access_logs.s3.enabled", + "Value": "false" + }, + { + "Key": "idle_timeout.timeout_seconds", + "Value": "60" + }, + { + "Key": "access_logs.s3.prefix", + "Value": "" + }, + { + "Key": "access_logs.s3.bucket", + "Value": "" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables deletion protection for the specified load balancer.", + "id": "elbv2-modify-load-balancer-attributes-1", + "title": "To enable deletion protection" + }, + { + "input": { + "Attributes": [ + { + "Key": "idle_timeout.timeout_seconds", + "Value": "30" + } + ], + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188" + }, + "output": { + "Attributes": [ + { + "Key": "idle_timeout.timeout_seconds", + "Value": "30" + }, + { + "Key": "access_logs.s3.enabled", + "Value": "false" + }, + { + "Key": "access_logs.s3.prefix", + "Value": "" + }, + { + "Key": "deletion_protection.enabled", + "Value": "true" + }, + { + "Key": "access_logs.s3.bucket", + "Value": "" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example changes the idle timeout value for the specified load balancer.", + "id": "elbv2-modify-load-balancer-attributes-2", + "title": "To change the idle timeout" + }, + { + "input": { + "Attributes": [ + { + "Key": "access_logs.s3.enabled", + "Value": "true" + }, + { + "Key": "access_logs.s3.bucket", + "Value": "my-loadbalancer-logs" + }, + { + "Key": "access_logs.s3.prefix", + "Value": "myapp" + } + ], + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188" + }, + "output": { + "Attributes": [ + { + "Key": "access_logs.s3.enabled", + "Value": "true" + }, + { + "Key": "access_logs.s3.bucket", + "Value": "my-load-balancer-logs" + }, + { + "Key": "access_logs.s3.prefix", + "Value": "myapp" + }, + { + "Key": "idle_timeout.timeout_seconds", + "Value": "60" + }, + { + "Key": "deletion_protection.enabled", + "Value": "false" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables access logs for the specified load balancer. Note that the S3 bucket must exist in the same region as the load balancer and must have a policy attached that grants access to the Elastic Load Balancing service.", + "id": "elbv2-modify-load-balancer-attributes-3", + "title": "To enable access logs" + } + ], + "ModifyRule": [ + { + "input": { + "Conditions": [ + { + "Field": "path-pattern", + "Values": [ + "/images/*" + ] + } + ], + "RuleArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener-rule/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2/9683b2d02a6cabee" + }, + "output": { + "Rules": [ + { + "Actions": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Type": "forward" + } + ], + "Conditions": [ + { + "Field": "path-pattern", + "Values": [ + "/images/*" + ] + } + ], + "IsDefault": false, + "Priority": "10", + "RuleArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener-rule/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2/9683b2d02a6cabee" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example modifies the condition for the specified rule.", + "id": "elbv2-modify-rule-1", + "title": "To modify a rule" + } + ], + "ModifyTargetGroup": [ + { + "input": { + "HealthCheckPort": "443", + "HealthCheckProtocol": "HTTPS", + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-https-targets/2453ed029918f21f" + }, + "output": { + "TargetGroups": [ + { + "HealthCheckIntervalSeconds": 30, + "HealthCheckPort": "443", + "HealthCheckProtocol": "HTTPS", + "HealthCheckTimeoutSeconds": 5, + "HealthyThresholdCount": 5, + "LoadBalancerArns": [ + "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188" + ], + "Matcher": { + "HttpCode": "200" + }, + "Port": 443, + "Protocol": "HTTPS", + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-https-targets/2453ed029918f21f", + "TargetGroupName": "my-https-targets", + "UnhealthyThresholdCount": 2, + "VpcId": "vpc-3ac0fb5f" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example changes the configuration of the health checks used to evaluate the health of the targets for the specified target group.", + "id": "elbv2-modify-target-group-1", + "title": "To modify the health check configuration for a target group" + } + ], + "ModifyTargetGroupAttributes": [ + { + "input": { + "Attributes": [ + { + "Key": "deregistration_delay.timeout_seconds", + "Value": "600" + } + ], + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067" + }, + "output": { + "Attributes": [ + { + "Key": "stickiness.enabled", + "Value": "false" + }, + { + "Key": "deregistration_delay.timeout_seconds", + "Value": "600" + }, + { + "Key": "stickiness.type", + "Value": "lb_cookie" + }, + { + "Key": "stickiness.lb_cookie.duration_seconds", + "Value": "86400" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example sets the deregistration delay timeout to the specified value for the specified target group.", + "id": "elbv2-modify-target-group-attributes-1", + "title": "To modify the deregistration delay timeout" + } + ], + "RegisterTargets": [ + { + "input": { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Targets": [ + { + "Id": "i-80c8dd94" + }, + { + "Id": "i-ceddcd4d" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example registers the specified instances with the specified target group.", + "id": "elbv2-register-targets-1", + "title": "To register targets with a target group" + }, + { + "input": { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-new-targets/3bb63f11dfb0faf9", + "Targets": [ + { + "Id": "i-80c8dd94", + "Port": 80 + }, + { + "Id": "i-80c8dd94", + "Port": 766 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example registers the specified instance with the specified target group using multiple ports. This enables you to register ECS containers on the same instance as targets in the target group.", + "id": "elbv2-register-targets-2", + "title": "To register targets with a target group using port overrides" + } + ], + "RemoveTags": [ + { + "input": { + "ResourceArns": [ + "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188" + ], + "TagKeys": [ + "project", + "department" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example removes the specified tags from the specified load balancer.", + "id": "elbv2-remove-tags-1", + "title": "To remove tags from a load balancer" + } + ], + "SetRulePriorities": [ + { + "input": { + "RulePriorities": [ + { + "Priority": 5, + "RuleArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener-rule/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2/1291d13826f405c3" + } + ] + }, + "output": { + "Rules": [ + { + "Actions": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "Type": "forward" + } + ], + "Conditions": [ + { + "Field": "path-pattern", + "Values": [ + "/img/*" + ] + } + ], + "IsDefault": false, + "Priority": "5", + "RuleArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:listener-rule/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2/1291d13826f405c3" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example sets the priority of the specified rule.", + "id": "elbv2-set-rule-priorities-1", + "title": "To set the rule priority" + } + ], + "SetSecurityGroups": [ + { + "input": { + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188", + "SecurityGroups": [ + "sg-5943793c" + ] + }, + "output": { + "SecurityGroupIds": [ + "sg-5943793c" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example associates the specified security group with the specified load balancer.", + "id": "elbv2-set-security-groups-1", + "title": "To associate a security group with a load balancer" + } + ], + "SetSubnets": [ + { + "input": { + "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188", + "Subnets": [ + "subnet-8360a9e7", + "subnet-b7d581c0" + ] + }, + "output": { + "AvailabilityZones": [ + { + "SubnetId": "subnet-8360a9e7", + "ZoneName": "us-west-2a" + }, + { + "SubnetId": "subnet-b7d581c0", + "ZoneName": "us-west-2b" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example enables the Availability Zones for the specified subnets for the specified load balancer.", + "id": "elbv2-set-subnets-1", + "title": "To enable Availability Zones for a load balancer" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/paginators-1.json new file mode 100644 index 0000000..a8e8468 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "DescribeLoadBalancers": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "PageSize", + "result_key": "LoadBalancers" + }, + "DescribeTargetGroups": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "PageSize", + "result_key": "TargetGroups" + }, + "DescribeListeners": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "PageSize", + "result_key": "Listeners" + }, + "DescribeAccountLimits": { + "input_token": "Marker", + "limit_key": "PageSize", + "output_token": "NextMarker", + "result_key": "Limits" + }, + "DescribeListenerCertificates": { + "input_token": "Marker", + "limit_key": "PageSize", + "output_token": "NextMarker", + "result_key": "Certificates" + }, + "DescribeRules": { + "input_token": "Marker", + "limit_key": "PageSize", + "output_token": "NextMarker", + "result_key": "Rules" + }, + "DescribeSSLPolicies": { + "input_token": "Marker", + "limit_key": "PageSize", + "output_token": "NextMarker", + "result_key": "SslPolicies" + }, + "DescribeTrustStoreAssociations": { + "input_token": "Marker", + "limit_key": "PageSize", + "output_token": "NextMarker", + "result_key": "TrustStoreAssociations" + }, + "DescribeTrustStoreRevocations": { + "input_token": "Marker", + "limit_key": "PageSize", + "output_token": "NextMarker", + "result_key": "TrustStoreRevocations" + }, + "DescribeTrustStores": { + "input_token": "Marker", + "limit_key": "PageSize", + "output_token": "NextMarker", + "result_key": "TrustStores" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/service-2.json.gz new file mode 100644 index 0000000..5e3acc3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/waiters-2.json new file mode 100644 index 0000000..9f3d77d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elbv2/2015-12-01/waiters-2.json @@ -0,0 +1,100 @@ +{ + "version": 2, + "waiters": { + "LoadBalancerExists": { + "delay": 15, + "operation": "DescribeLoadBalancers", + "maxAttempts": 40, + "acceptors": [ + { + "matcher": "status", + "expected": 200, + "state": "success" + }, + { + "matcher": "error", + "expected": "LoadBalancerNotFound", + "state": "retry" + } + ] + }, + "LoadBalancerAvailable": { + "delay": 15, + "operation": "DescribeLoadBalancers", + "maxAttempts": 40, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "LoadBalancers[].State.Code", + "expected": "active" + }, + { + "state": "retry", + "matcher": "pathAny", + "argument": "LoadBalancers[].State.Code", + "expected": "provisioning" + }, + { + "state": "retry", + "matcher": "error", + "expected": "LoadBalancerNotFound" + } + ] + }, + "LoadBalancersDeleted": { + "delay": 15, + "operation": "DescribeLoadBalancers", + "maxAttempts": 40, + "acceptors": [ + { + "state": "retry", + "matcher": "pathAll", + "argument": "LoadBalancers[].State.Code", + "expected": "active" + }, + { + "matcher": "error", + "expected": "LoadBalancerNotFound", + "state": "success" + } + ] + }, + "TargetInService":{ + "delay":15, + "maxAttempts":40, + "operation":"DescribeTargetHealth", + "acceptors":[ + { + "argument":"TargetHealthDescriptions[].TargetHealth.State", + "expected":"healthy", + "matcher":"pathAll", + "state":"success" + }, + { + "matcher": "error", + "expected": "InvalidInstance", + "state": "retry" + } + ] + }, + "TargetDeregistered": { + "delay": 15, + "maxAttempts": 40, + "operation": "DescribeTargetHealth", + "acceptors": [ + { + "matcher": "error", + "expected": "InvalidTarget", + "state": "success" + }, + { + "argument":"TargetHealthDescriptions[].TargetHealth.State", + "expected":"unused", + "matcher":"pathAll", + "state":"success" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elementalinference/2018-11-14/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/elementalinference/2018-11-14/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1844132 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/elementalinference/2018-11-14/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/elementalinference/2018-11-14/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/elementalinference/2018-11-14/paginators-1.json new file mode 100644 index 0000000..ceea345 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elementalinference/2018-11-14/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListFeeds": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "feeds" + }, + "ListDictionaries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dictionaries" + }, + "SearchFixtures": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "fixtures" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/elementalinference/2018-11-14/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/elementalinference/2018-11-14/service-2.json.gz new file mode 100644 index 0000000..eb03744 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/elementalinference/2018-11-14/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/elementalinference/2018-11-14/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/elementalinference/2018-11-14/waiters-2.json new file mode 100644 index 0000000..b94de7d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/elementalinference/2018-11-14/waiters-2.json @@ -0,0 +1,34 @@ +{ + "version" : 2, + "waiters" : { + "FeedDeleted" : { + "description" : "Wait until a Feed is deleted", + "delay" : 3, + "maxAttempts" : 40, + "operation" : "GetFeed", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "DELETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "DELETING" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "InternalServerErrorException" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "TooManyRequestException" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/emr-containers/2020-10-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/emr-containers/2020-10-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..718b12b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/emr-containers/2020-10-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/emr-containers/2020-10-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/emr-containers/2020-10-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/emr-containers/2020-10-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/emr-containers/2020-10-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/emr-containers/2020-10-01/paginators-1.json new file mode 100644 index 0000000..c21db2d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/emr-containers/2020-10-01/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListJobRuns": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobRuns" + }, + "ListManagedEndpoints": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "endpoints" + }, + "ListVirtualClusters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "virtualClusters" + }, + "ListJobTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "templates" + }, + "ListSecurityConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "securityConfigurations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/emr-containers/2020-10-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/emr-containers/2020-10-01/service-2.json.gz new file mode 100644 index 0000000..b6c6f93 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/emr-containers/2020-10-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/emr-serverless/2021-07-13/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/emr-serverless/2021-07-13/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f96fb9c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/emr-serverless/2021-07-13/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/emr-serverless/2021-07-13/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/emr-serverless/2021-07-13/paginators-1.json new file mode 100644 index 0000000..fd11bc7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/emr-serverless/2021-07-13/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListApplications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "applications" + }, + "ListJobRuns": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobRuns" + }, + "ListJobRunAttempts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobRunAttempts" + }, + "ListSessions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sessions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/emr-serverless/2021-07-13/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/emr-serverless/2021-07-13/service-2.json.gz new file mode 100644 index 0000000..5643cd3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/emr-serverless/2021-07-13/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4d03e8b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/paginators-1.json new file mode 100644 index 0000000..c908b4a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/paginators-1.json @@ -0,0 +1,59 @@ +{ + "pagination": { + "ListBootstrapActions": { + "input_token": "Marker", + "output_token": "Marker", + "result_key": "BootstrapActions" + }, + "ListClusters": { + "input_token": "Marker", + "output_token": "Marker", + "result_key": "Clusters" + }, + "ListInstanceGroups": { + "input_token": "Marker", + "output_token": "Marker", + "result_key": "InstanceGroups" + }, + "ListInstances": { + "input_token": "Marker", + "output_token": "Marker", + "result_key": "Instances" + }, + "ListSteps": { + "input_token": "Marker", + "output_token": "Marker", + "result_key": "Steps" + }, + "ListInstanceFleets": { + "input_token": "Marker", + "output_token": "Marker", + "result_key": "InstanceFleets" + }, + "ListSecurityConfigurations": { + "input_token": "Marker", + "output_token": "Marker", + "result_key": "SecurityConfigurations" + }, + "ListNotebookExecutions": { + "input_token": "Marker", + "output_token": "Marker", + "result_key": "NotebookExecutions" + }, + "ListStudioSessionMappings": { + "input_token": "Marker", + "output_token": "Marker", + "result_key": "SessionMappings" + }, + "ListStudios": { + "input_token": "Marker", + "output_token": "Marker", + "result_key": "Studios" + }, + "ListSessions": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Sessions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/service-2.json.gz new file mode 100644 index 0000000..018b811 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/waiters-2.json new file mode 100644 index 0000000..abba8c3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/emr/2009-03-31/waiters-2.json @@ -0,0 +1,86 @@ +{ + "version": 2, + "waiters": { + "ClusterRunning": { + "delay": 30, + "operation": "DescribeCluster", + "maxAttempts": 60, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "Cluster.Status.State", + "expected": "RUNNING" + }, + { + "state": "success", + "matcher": "path", + "argument": "Cluster.Status.State", + "expected": "WAITING" + }, + { + "state": "failure", + "matcher": "path", + "argument": "Cluster.Status.State", + "expected": "TERMINATING" + }, + { + "state": "failure", + "matcher": "path", + "argument": "Cluster.Status.State", + "expected": "TERMINATED" + }, + { + "state": "failure", + "matcher": "path", + "argument": "Cluster.Status.State", + "expected": "TERMINATED_WITH_ERRORS" + } + ] + }, + "StepComplete": { + "delay": 30, + "operation": "DescribeStep", + "maxAttempts": 60, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "Step.Status.State", + "expected": "COMPLETED" + }, + { + "state": "failure", + "matcher": "path", + "argument": "Step.Status.State", + "expected": "FAILED" + }, + { + "state": "failure", + "matcher": "path", + "argument": "Step.Status.State", + "expected": "CANCELLED" + } + ] + }, + "ClusterTerminated": { + "delay": 30, + "operation": "DescribeCluster", + "maxAttempts": 60, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "Cluster.Status.State", + "expected": "TERMINATED" + }, + { + "state": "failure", + "matcher": "path", + "argument": "Cluster.Status.State", + "expected": "TERMINATED_WITH_ERRORS" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/endpoints.json b/venv/lib/python3.12/site-packages/botocore/data/endpoints.json new file mode 100644 index 0000000..01cee86 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/endpoints.json @@ -0,0 +1,40794 @@ +{ + "partitions" : [ { + "defaults" : { + "hostname" : "{service}.{region}.{dnsSuffix}", + "protocols" : [ "https" ], + "signatureVersions" : [ "v4" ], + "variants" : [ { + "dnsSuffix" : "amazonaws.com", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + }, { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "dualstack", "fips" ] + }, { + "dnsSuffix" : "api.aws", + "hostname" : "{service}.{region}.{dnsSuffix}", + "tags" : [ "dualstack" ] + } ] + }, + "dnsSuffix" : "amazonaws.com", + "partition" : "aws", + "partitionName" : "AWS Standard", + "regionRegex" : "^(us|eu|ap|sa|ca|me|af|il|mx)\\-\\w+\\-\\d+$", + "regions" : { + "af-south-1" : { + "description" : "Africa (Cape Town)" + }, + "ap-east-1" : { + "description" : "Asia Pacific (Hong Kong)" + }, + "ap-east-2" : { + "description" : "Asia Pacific (Taipei)" + }, + "ap-northeast-1" : { + "description" : "Asia Pacific (Tokyo)" + }, + "ap-northeast-2" : { + "description" : "Asia Pacific (Seoul)" + }, + "ap-northeast-3" : { + "description" : "Asia Pacific (Osaka)" + }, + "ap-south-1" : { + "description" : "Asia Pacific (Mumbai)" + }, + "ap-south-2" : { + "description" : "Asia Pacific (Hyderabad)" + }, + "ap-southeast-1" : { + "description" : "Asia Pacific (Singapore)" + }, + "ap-southeast-2" : { + "description" : "Asia Pacific (Sydney)" + }, + "ap-southeast-3" : { + "description" : "Asia Pacific (Jakarta)" + }, + "ap-southeast-4" : { + "description" : "Asia Pacific (Melbourne)" + }, + "ap-southeast-5" : { + "description" : "Asia Pacific (Malaysia)" + }, + "ap-southeast-6" : { + "description" : "Asia Pacific (New Zealand)" + }, + "ap-southeast-7" : { + "description" : "Asia Pacific (Thailand)" + }, + "ca-central-1" : { + "description" : "Canada (Central)" + }, + "ca-west-1" : { + "description" : "Canada West (Calgary)" + }, + "eu-central-1" : { + "description" : "Europe (Frankfurt)" + }, + "eu-central-2" : { + "description" : "Europe (Zurich)" + }, + "eu-north-1" : { + "description" : "Europe (Stockholm)" + }, + "eu-south-1" : { + "description" : "Europe (Milan)" + }, + "eu-south-2" : { + "description" : "Europe (Spain)" + }, + "eu-west-1" : { + "description" : "Europe (Ireland)" + }, + "eu-west-2" : { + "description" : "Europe (London)" + }, + "eu-west-3" : { + "description" : "Europe (Paris)" + }, + "il-central-1" : { + "description" : "Israel (Tel Aviv)" + }, + "me-central-1" : { + "description" : "Middle East (UAE)" + }, + "me-south-1" : { + "description" : "Middle East (Bahrain)" + }, + "mx-central-1" : { + "description" : "Mexico (Central)" + }, + "sa-east-1" : { + "description" : "South America (Sao Paulo)" + }, + "us-east-1" : { + "description" : "US East (N. Virginia)" + }, + "us-east-2" : { + "description" : "US East (Ohio)" + }, + "us-west-1" : { + "description" : "US West (N. California)" + }, + "us-west-2" : { + "description" : "US West (Oregon)" + } + }, + "services" : { + "access-analyzer" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "access-analyzer.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "access-analyzer.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "access-analyzer.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "access-analyzer.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "access-analyzer.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "access-analyzer.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "access-analyzer.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "access-analyzer.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "access-analyzer.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "access-analyzer.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "access-analyzer.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "access-analyzer.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "access-analyzer.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "access-analyzer-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "access-analyzer-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "access-analyzer.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "access-analyzer-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "access-analyzer-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "access-analyzer.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "access-analyzer.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "access-analyzer.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "access-analyzer.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "access-analyzer.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "access-analyzer.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "access-analyzer.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "access-analyzer.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "access-analyzer.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "access-analyzer-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "access-analyzer-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "access-analyzer-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "access-analyzer-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "access-analyzer-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "access-analyzer-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "access-analyzer.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "access-analyzer.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "access-analyzer.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "access-analyzer.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "access-analyzer.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "access-analyzer-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "access-analyzer-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "access-analyzer.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "access-analyzer-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "access-analyzer-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "access-analyzer.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "access-analyzer-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "access-analyzer-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "access-analyzer.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "access-analyzer-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "access-analyzer-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "access-analyzer.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "account" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "account.us-east-1.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "acm" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "acm-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "acm-fips.ca-central-1.amazonaws.com" + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "acm-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1-fips" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "acm-fips.ca-west-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "acm-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "acm-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "acm-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "acm-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "acm-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "acm-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "acm-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "acm-fips.us-west-2.amazonaws.com" + } + } + }, + "acm-pca" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "acm-pca-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "acm-pca-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "acm-pca-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "acm-pca-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "acm-pca-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "acm-pca-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "acm-pca-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "acm-pca-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "acm-pca-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "acm-pca-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "acm-pca-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "acm-pca-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "agreement-marketplace" : { + "endpoints" : { + "us-east-1" : { + "variants" : [ { + "hostname" : "agreement-marketplace.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "airflow" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "amplify" : { + "endpoints" : { + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "amplifybackend" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "amplifyuibuilder" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "aoss" : { + "endpoints" : { + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "api.detective" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "detective.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "detective.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "detective.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "detective.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "detective.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "detective.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "detective.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "api.detective-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "detective-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "detective.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "api.detective-fips.ca-central-1.amazonaws.com" + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "detective.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "detective.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "detective.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "detective.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "detective.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "detective.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "detective.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "detective.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "detective.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "api.detective-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "detective-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "detective.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "api.detective-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "api.detective-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "detective-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "detective.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "api.detective-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "api.detective-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "detective-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "detective.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "api.detective-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "api.detective-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "detective-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "detective.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "api.detective-fips.us-west-2.amazonaws.com" + } + } + }, + "api.ecr" : { + "defaults" : { + "variants" : [ { + "hostname" : "ecr-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "hostname" : "api.ecr.af-south-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "hostname" : "api.ecr.ap-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "api.ecr.ap-northeast-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "api.ecr.ap-northeast-2.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "hostname" : "api.ecr.ap-northeast-3.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "api.ecr.ap-south-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "api.ecr.ap-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "api.ecr.ap-southeast-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "api.ecr.ap-southeast-2.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "hostname" : "api.ecr.ap-southeast-3.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "hostname" : "api.ecr.ap-southeast-4.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "credentialScope" : { + "region" : "ap-southeast-5" + }, + "hostname" : "api.ecr.ap-southeast-5.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "credentialScope" : { + "region" : "ap-southeast-7" + }, + "hostname" : "api.ecr.ap-southeast-7.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "api.ecr.ca-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "hostname" : "api.ecr.ca-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "dkr-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "ecr-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "dkr-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "ecr-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "dkr-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "ecr-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "dkr-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "ecr-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "api.ecr.eu-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "api.ecr.eu-central-2.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "hostname" : "api.ecr.eu-north-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "hostname" : "api.ecr.eu-south-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "api.ecr.eu-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "api.ecr.eu-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "api.ecr.eu-west-2.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "hostname" : "api.ecr.eu-west-3.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-dkr-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ecr-fips.us-east-1.amazonaws.com" + }, + "fips-dkr-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ecr-fips.us-east-2.amazonaws.com" + }, + "fips-dkr-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ecr-fips.us-west-1.amazonaws.com" + }, + "fips-dkr-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ecr-fips.us-west-2.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ecr-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ecr-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ecr-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ecr-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "api.ecr.il-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "hostname" : "api.ecr.me-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "hostname" : "api.ecr.me-south-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "credentialScope" : { + "region" : "mx-central-1" + }, + "hostname" : "api.ecr.mx-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "api.ecr.sa-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "api.ecr.us-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ecr-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ecr.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "api.ecr.us-east-2.amazonaws.com", + "variants" : [ { + "hostname" : "ecr-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ecr-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ecr.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "api.ecr.us-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ecr-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ecr.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "api.ecr.us-west-2.amazonaws.com", + "variants" : [ { + "hostname" : "ecr-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ecr-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ecr.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "api.ecr-public" : { + "endpoints" : { + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "api.ecr-public.us-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr-public.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "api.ecr-public.us-west-2.amazonaws.com" + } + } + }, + "api.iotdeviceadvisor" : { + "endpoints" : { + "ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "api.iotdeviceadvisor.ap-northeast-1.amazonaws.com" + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "api.iotdeviceadvisor.eu-west-1.amazonaws.com" + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "api.iotdeviceadvisor.us-east-1.amazonaws.com" + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "api.iotdeviceadvisor.us-west-2.amazonaws.com" + } + } + }, + "api.iotwireless" : { + "endpoints" : { + "ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "api.iotwireless.ap-northeast-1.amazonaws.com" + }, + "ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "api.iotwireless.ap-southeast-2.amazonaws.com" + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "api.iotwireless.eu-central-1.amazonaws.com" + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "api.iotwireless.eu-west-1.amazonaws.com" + }, + "sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "api.iotwireless.sa-east-1.amazonaws.com" + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "api.iotwireless.us-east-1.amazonaws.com" + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "api.iotwireless.us-west-2.amazonaws.com" + } + } + }, + "api.mediatailor" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "api.pricing" : { + "defaults" : { + "credentialScope" : { + "service" : "pricing" + } + }, + "endpoints" : { + "ap-south-1" : { }, + "eu-central-1" : { }, + "us-east-1" : { } + } + }, + "api.sagemaker" : { + "defaults" : { + "variants" : [ { + "hostname" : "api-fips.sagemaker.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "api-fips.sagemaker.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "api-fips.sagemaker.ca-central-1.amazonaws.com" + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "api-fips.sagemaker.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1-fips" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "api-fips.sagemaker.ca-west-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "api-fips.sagemaker.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "api-fips.sagemaker.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "api-fips.sagemaker.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "api-fips.sagemaker.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "api-fips.sagemaker.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "api-fips.sagemaker.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "api-fips.sagemaker.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "api-fips.sagemaker.us-west-2.amazonaws.com" + } + } + }, + "api.tunneling.iot" : { + "defaults" : { + "variants" : [ { + "dnsSuffix" : "amazonaws.com", + "hostname" : "api.tunneling.iot-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + }, { + "dnsSuffix" : "api.aws", + "hostname" : "api.iot-tunneling-fips.{region}.{dnsSuffix}", + "tags" : [ "dualstack", "fips" ] + }, { + "dnsSuffix" : "api.aws", + "hostname" : "api.iot-tunneling.{region}.{dnsSuffix}", + "tags" : [ "dualstack" ] + } ] + }, + "endpoints" : { + "ap-east-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "api.iot-tunneling.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "api.tunneling.iot-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "api.iot-tunneling.us-east-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "api.tunneling.iot-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "api.iot-tunneling-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "api.iot-tunneling.us-east-2.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "api.tunneling.iot-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "api.iot-tunneling.us-west-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "api.tunneling.iot-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "api.iot-tunneling-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "api.iot-tunneling.us-west-2.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "api.tunneling.iot-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "apigateway" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "apigateway-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "apigateway-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "apigateway-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "apigateway-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "apigateway-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "apigateway-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "apigateway-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "apigateway-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "apigateway-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "apigateway-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "apigateway-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "apigateway-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "app-integrations" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "appconfig" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "appconfigdata" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "appflow" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "appflow-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "appflow-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "appflow-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "appflow-fips.us-west-2.amazonaws.com" + }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "appflow-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "appflow-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "appflow-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "appflow-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "application-autoscaling" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "applicationinsights" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "applicationinsights.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "applicationinsights.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "applicationinsights.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "applicationinsights.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "applicationinsights.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "applicationinsights.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "applicationinsights.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "applicationinsights.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "applicationinsights.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "applicationinsights.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "applicationinsights.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "applicationinsights-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "applicationinsights-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "applicationinsights.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "applicationinsights-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "applicationinsights-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "applicationinsights.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "applicationinsights.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "applicationinsights.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "applicationinsights.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "applicationinsights.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "applicationinsights.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "applicationinsights.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "applicationinsights.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "applicationinsights.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "applicationinsights-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "applicationinsights-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "applicationinsights-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "applicationinsights-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "applicationinsights-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "applicationinsights-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "applicationinsights.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "applicationinsights.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "applicationinsights.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "applicationinsights.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "applicationinsights-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "applicationinsights-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "applicationinsights.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "applicationinsights-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "applicationinsights-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "applicationinsights.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "applicationinsights-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "applicationinsights-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "applicationinsights.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "applicationinsights-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "applicationinsights-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "applicationinsights.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "appmesh" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "appmesh.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "appmesh.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "appmesh.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "appmesh.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "appmesh.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "appmesh.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "appmesh.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "appmesh.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "appmesh.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "appmesh-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "appmesh-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "appmesh.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "appmesh-fips.ca-central-1.amazonaws.com" + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "appmesh.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "appmesh.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "appmesh.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "appmesh.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "appmesh.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "appmesh.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "appmesh.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "appmesh.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "appmesh.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "appmesh.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "appmesh.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "appmesh-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "appmesh-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "appmesh.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "appmesh-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "appmesh-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "appmesh-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "appmesh.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "appmesh-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "appmesh-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "appmesh-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "appmesh.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "appmesh-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "appmesh-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "appmesh-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "appmesh.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "appmesh-fips.us-west-2.amazonaws.com" + } + } + }, + "apprunner" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "apprunner-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "apprunner-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "apprunner-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "apprunner-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "apprunner-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "apprunner-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "appstream2" : { + "defaults" : { + "credentialScope" : { + "service" : "appstream" + }, + "protocols" : [ "https" ] + }, + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "appstream2-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "appstream2-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "appstream2-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { }, + "us-west-2" : { + "variants" : [ { + "hostname" : "appstream2-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "appstream2-fips.us-west-2.amazonaws.com" + } + } + }, + "appsync" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "appsync.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "appsync.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "appsync.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "appsync.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "appsync.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "appsync.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "appsync.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "appsync.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "appsync.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "appsync.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "appsync.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "appsync.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "appsync.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "appsync.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "appsync.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "appsync.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "appsync.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "appsync.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "appsync.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "appsync.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "appsync.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "appsync.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "appsync.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "appsync.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "appsync.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "appsync.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "appsync.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "appsync.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "aps" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { }, + "ap-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { }, + "ap-southeast-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { }, + "eu-north-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "deprecated" : true + }, + "us-east-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "deprecated" : true + }, + "us-west-1" : { }, + "us-west-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "deprecated" : true + } + } + }, + "arc-zonal-shift" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "athena" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "athena.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "athena.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "athena.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "athena.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "athena.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "athena.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "athena.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "athena.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "athena.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "athena.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "athena.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "athena.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "athena-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "athena.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "athena-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "athena.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "athena.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "athena.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "athena.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "athena.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "athena.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "athena.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "athena.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "athena.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "athena-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "athena-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "athena-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "athena-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "athena-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "athena-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "athena.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "athena.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "athena.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "athena.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "athena-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "athena.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "athena-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "athena.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "athena-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "athena.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "athena-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "athena.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "auditmanager" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "auditmanager-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "auditmanager-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "auditmanager-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "auditmanager-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "auditmanager-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "auditmanager-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "auditmanager-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "auditmanager-fips.us-west-2.amazonaws.com" + } + } + }, + "autoscaling" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "autoscaling-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "autoscaling-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "autoscaling-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "autoscaling-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "autoscaling-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "autoscaling-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "autoscaling-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "autoscaling-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "autoscaling-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "autoscaling-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "autoscaling-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "autoscaling-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "autoscaling-plans" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "backup" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "backup-gateway" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "batch" : { + "defaults" : { + "variants" : [ { + "hostname" : "fips.batch.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "fips.batch.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "fips.batch.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "fips.batch.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "fips.batch.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "fips.batch.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "fips.batch.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "fips.batch.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "fips.batch.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "bedrock" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "bedrock-ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "bedrock.ap-northeast-1.amazonaws.com" + }, + "bedrock-ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "bedrock.ap-northeast-2.amazonaws.com" + }, + "bedrock-ap-northeast-3" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "hostname" : "bedrock.ap-northeast-3.amazonaws.com" + }, + "bedrock-ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "bedrock.ap-south-1.amazonaws.com" + }, + "bedrock-ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "bedrock.ap-south-2.amazonaws.com" + }, + "bedrock-ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "bedrock.ap-southeast-1.amazonaws.com" + }, + "bedrock-ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "bedrock.ap-southeast-2.amazonaws.com" + }, + "bedrock-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "bedrock.ca-central-1.amazonaws.com" + }, + "bedrock-eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "bedrock.eu-central-1.amazonaws.com" + }, + "bedrock-eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "bedrock.eu-central-2.amazonaws.com" + }, + "bedrock-eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "hostname" : "bedrock.eu-north-1.amazonaws.com" + }, + "bedrock-eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "hostname" : "bedrock.eu-south-1.amazonaws.com" + }, + "bedrock-eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "bedrock.eu-south-2.amazonaws.com" + }, + "bedrock-eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "bedrock.eu-west-1.amazonaws.com" + }, + "bedrock-eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "bedrock.eu-west-2.amazonaws.com" + }, + "bedrock-eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "hostname" : "bedrock.eu-west-3.amazonaws.com" + }, + "bedrock-fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "bedrock-fips.ca-central-1.amazonaws.com" + }, + "bedrock-fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "bedrock-fips.us-east-1.amazonaws.com" + }, + "bedrock-fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "bedrock-fips.us-east-2.amazonaws.com" + }, + "bedrock-fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "bedrock-fips.us-west-2.amazonaws.com" + }, + "bedrock-runtime-ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "bedrock-runtime.ap-northeast-1.amazonaws.com" + }, + "bedrock-runtime-ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "bedrock-runtime.ap-northeast-2.amazonaws.com" + }, + "bedrock-runtime-ap-northeast-3" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "hostname" : "bedrock-runtime.ap-northeast-3.amazonaws.com" + }, + "bedrock-runtime-ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "bedrock-runtime.ap-south-1.amazonaws.com" + }, + "bedrock-runtime-ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "bedrock-runtime.ap-south-2.amazonaws.com" + }, + "bedrock-runtime-ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "bedrock-runtime.ap-southeast-1.amazonaws.com" + }, + "bedrock-runtime-ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "bedrock-runtime.ap-southeast-2.amazonaws.com" + }, + "bedrock-runtime-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "bedrock-runtime.ca-central-1.amazonaws.com" + }, + "bedrock-runtime-eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "bedrock-runtime.eu-central-1.amazonaws.com" + }, + "bedrock-runtime-eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "bedrock-runtime.eu-central-2.amazonaws.com" + }, + "bedrock-runtime-eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "hostname" : "bedrock-runtime.eu-north-1.amazonaws.com" + }, + "bedrock-runtime-eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "hostname" : "bedrock-runtime.eu-south-1.amazonaws.com" + }, + "bedrock-runtime-eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "bedrock-runtime.eu-south-2.amazonaws.com" + }, + "bedrock-runtime-eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "bedrock-runtime.eu-west-1.amazonaws.com" + }, + "bedrock-runtime-eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "bedrock-runtime.eu-west-2.amazonaws.com" + }, + "bedrock-runtime-eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "hostname" : "bedrock-runtime.eu-west-3.amazonaws.com" + }, + "bedrock-runtime-fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "bedrock-runtime-fips.ca-central-1.amazonaws.com" + }, + "bedrock-runtime-fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "bedrock-runtime-fips.us-east-1.amazonaws.com" + }, + "bedrock-runtime-fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "bedrock-runtime-fips.us-east-2.amazonaws.com" + }, + "bedrock-runtime-fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "bedrock-runtime-fips.us-west-2.amazonaws.com" + }, + "bedrock-runtime-sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "bedrock-runtime.sa-east-1.amazonaws.com" + }, + "bedrock-runtime-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "bedrock-runtime.us-east-1.amazonaws.com" + }, + "bedrock-runtime-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "bedrock-runtime.us-east-2.amazonaws.com" + }, + "bedrock-runtime-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "bedrock-runtime.us-west-2.amazonaws.com" + }, + "bedrock-sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "bedrock.sa-east-1.amazonaws.com" + }, + "bedrock-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "bedrock.us-east-1.amazonaws.com" + }, + "bedrock-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "bedrock.us-east-2.amazonaws.com" + }, + "bedrock-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "bedrock.us-west-2.amazonaws.com" + }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "billingconductor" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "billingconductor.us-east-1.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "braket" : { + "endpoints" : { + "eu-north-1" : { + "variants" : [ { + "hostname" : "braket.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "braket.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "braket.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "braket.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "braket.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "budgets" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "budgets.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "cases" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-2" : { }, + "fips-us-east-1" : { + "deprecated" : true + }, + "fips-us-west-2" : { + "deprecated" : true + }, + "us-east-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + } + } + }, + "cassandra" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "cassandra-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "cassandra-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "cassandra-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { + "variants" : [ { + "hostname" : "cassandra-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "catalog.marketplace" : { + "endpoints" : { + "us-east-1" : { } + } + }, + "ce" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "ce.us-east-1.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "chime" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "chime.us-east-1.amazonaws.com", + "protocols" : [ "https" ] + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "cleanrooms" : { + "endpoints" : { + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "cleanrooms.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "cleanrooms.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "cleanrooms.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "cleanrooms.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "cleanrooms.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "cleanrooms.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "cleanrooms.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "cleanrooms.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "cleanrooms-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "cleanrooms-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "cleanrooms-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "cleanrooms-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cleanrooms-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cleanrooms.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "cleanrooms-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cleanrooms-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cleanrooms.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "cleanrooms-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cleanrooms-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cleanrooms.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "cloud9" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "cloud9-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cloud9-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "cloud9-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "cloud9-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "cloud9-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "cloud9-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "cloud9-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "cloud9-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cloud9-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "cloud9-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cloud9-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "cloud9-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cloud9-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "cloud9-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cloud9-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + } + } + }, + "cloudcontrolapi" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cloudcontrolapi-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cloudcontrolapi.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cloudcontrolapi-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cloudcontrolapi.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "cloudcontrolapi-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "cloudcontrolapi-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "cloudcontrolapi-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "cloudcontrolapi-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "cloudcontrolapi-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "cloudcontrolapi-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cloudcontrolapi-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cloudcontrolapi.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "cloudcontrolapi-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cloudcontrolapi-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cloudcontrolapi.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cloudcontrolapi-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cloudcontrolapi.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "cloudcontrolapi-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cloudcontrolapi-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cloudcontrolapi.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "clouddirectory" : { + "endpoints" : { + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "cloudformation" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "cloudformation-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "cloudformation-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "cloudformation-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "cloudformation-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "cloudformation-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "cloudformation-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "cloudformation-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "cloudformation-fips.us-west-2.amazonaws.com" + } + } + }, + "cloudfront" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "cloudfront.amazonaws.com", + "protocols" : [ "http", "https" ] + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "cloudhsm" : { + "endpoints" : { + "us-east-1" : { } + } + }, + "cloudhsmv2" : { + "defaults" : { + "credentialScope" : { + "service" : "cloudhsm" + } + }, + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "cloudhsmv2.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "cloudhsmv2.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "cloudhsmv2.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "cloudhsmv2.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "cloudhsmv2.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "cloudhsmv2.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "cloudhsmv2.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "cloudhsmv2.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "cloudhsmv2.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "cloudhsmv2.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "cloudsearch" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "cloudtrail" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "cloudtrail-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "cloudtrail-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "cloudtrail-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "cloudtrail-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "cloudtrail-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "cloudtrail-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "cloudtrail-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "cloudtrail-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "cloudtrail-data" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "codeartifact" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "codebuild" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "codebuild-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "codebuild-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "codebuild-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "codebuild-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "codebuild-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "codebuild-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "codebuild-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "codebuild-fips.us-west-2.amazonaws.com" + } + } + }, + "codecatalyst" : { + "endpoints" : { + "aws-global" : { + "hostname" : "codecatalyst.global.api.aws" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "codecommit" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "codecommit-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "codecommit-fips.ca-central-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "codecommit-fips.ca-central-1.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "codecommit-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "codecommit-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "codecommit-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "codecommit-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "codecommit-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "codecommit-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "codecommit-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "codecommit-fips.us-west-2.amazonaws.com" + } + } + }, + "codedeploy" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "codedeploy-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "codedeploy-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "codedeploy-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "codedeploy-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "codedeploy-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "codedeploy-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "codedeploy-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "codedeploy-fips.us-west-2.amazonaws.com" + } + } + }, + "codeguru-profiler" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "codeguru-reviewer" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "codepipeline" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "codepipeline-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "codepipeline-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "codepipeline-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "codepipeline-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "codepipeline-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "codepipeline-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "codepipeline-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "codepipeline-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "codepipeline-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "codepipeline-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "codestar-connections" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "codestar-notifications" : { + "endpoints" : { + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "cognito-identity" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "cognito-identity.af-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "cognito-identity.ap-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "cognito-identity.ap-northeast-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "cognito-identity.ap-northeast-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "cognito-identity.ap-northeast-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "cognito-identity.ap-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "cognito-identity.ap-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "cognito-identity.ap-southeast-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "cognito-identity.ap-southeast-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "cognito-identity.ap-southeast-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "cognito-identity.ap-southeast-4.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "cognito-identity.ap-southeast-5.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "cognito-identity.ca-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "cognito-identity.ca-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "cognito-identity.eu-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "cognito-identity.eu-central-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "cognito-identity.eu-north-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "cognito-identity.eu-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "cognito-identity.eu-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "cognito-identity.eu-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "cognito-identity.eu-west-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "cognito-identity.eu-west-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "cognito-identity-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "cognito-identity-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "cognito-identity-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "cognito-identity-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "cognito-identity.il-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "cognito-identity.me-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "cognito-identity.me-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "cognito-identity.sa-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "cognito-identity-fips.us-east-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cognito-identity-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cognito-identity.us-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "cognito-identity-fips.us-east-2.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cognito-identity-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cognito-identity.us-east-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "cognito-identity-fips.us-west-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cognito-identity-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cognito-identity.us-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "cognito-identity-fips.us-west-2.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cognito-identity-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cognito-identity.us-west-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "cognito-idp" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "cognito-idp.af-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "cognito-idp.ap-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "cognito-idp.ap-northeast-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "cognito-idp.ap-northeast-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "cognito-idp.ap-northeast-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "cognito-idp.ap-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "cognito-idp.ap-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "cognito-idp.ap-southeast-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "cognito-idp.ap-southeast-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "cognito-idp.ap-southeast-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "cognito-idp.ap-southeast-4.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "cognito-idp.ap-southeast-5.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "cognito-idp.ca-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "cognito-idp.ca-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "cognito-idp.eu-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "cognito-idp.eu-central-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "cognito-idp.eu-north-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "cognito-idp.eu-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "cognito-idp.eu-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "cognito-idp.eu-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "cognito-idp.eu-west-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "cognito-idp.eu-west-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "cognito-idp-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "cognito-idp-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "cognito-idp-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "cognito-idp-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "cognito-idp.il-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "cognito-idp.me-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "cognito-idp.me-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "cognito-idp.sa-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "cognito-idp-fips.us-east-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cognito-idp-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cognito-idp.us-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "cognito-idp-fips.us-east-2.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cognito-idp-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cognito-idp.us-east-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "cognito-idp-fips.us-west-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cognito-idp-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cognito-idp.us-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "cognito-idp-fips.us-west-2.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cognito-idp-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cognito-idp.us-west-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "cognito-sync" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "comprehend" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "comprehend.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "comprehend.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "comprehend.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "comprehend.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "comprehend.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "comprehend-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "comprehend-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "comprehend.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "comprehend.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "comprehend.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "comprehend.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "comprehend-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "comprehend-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "comprehend-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "comprehend-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "comprehend-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "comprehend-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "comprehend.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "comprehend-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "comprehend-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "comprehend.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "comprehend-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "comprehend-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "comprehend.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "comprehendmedical" : { + "endpoints" : { + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "comprehendmedical-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "comprehendmedical-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "comprehendmedical-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "comprehendmedical-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "comprehendmedical-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "comprehendmedical-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "comprehendmedical-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "comprehendmedical-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "compute-optimizer" : { + "endpoints" : { + "af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "hostname" : "compute-optimizer.af-south-1.amazonaws.com" + }, + "ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "hostname" : "compute-optimizer.ap-east-1.amazonaws.com" + }, + "ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "compute-optimizer.ap-northeast-1.amazonaws.com" + }, + "ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "compute-optimizer.ap-northeast-2.amazonaws.com" + }, + "ap-northeast-3" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "hostname" : "compute-optimizer.ap-northeast-3.amazonaws.com" + }, + "ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "compute-optimizer.ap-south-1.amazonaws.com" + }, + "ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "compute-optimizer.ap-south-2.amazonaws.com" + }, + "ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "compute-optimizer.ap-southeast-1.amazonaws.com" + }, + "ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "compute-optimizer.ap-southeast-2.amazonaws.com" + }, + "ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "hostname" : "compute-optimizer.ap-southeast-3.amazonaws.com" + }, + "ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "hostname" : "compute-optimizer.ap-southeast-4.amazonaws.com" + }, + "ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "compute-optimizer.ca-central-1.amazonaws.com" + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "compute-optimizer.eu-central-1.amazonaws.com" + }, + "eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "compute-optimizer.eu-central-2.amazonaws.com" + }, + "eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "hostname" : "compute-optimizer.eu-north-1.amazonaws.com" + }, + "eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "hostname" : "compute-optimizer.eu-south-1.amazonaws.com" + }, + "eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "compute-optimizer.eu-south-2.amazonaws.com" + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "compute-optimizer.eu-west-1.amazonaws.com" + }, + "eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "compute-optimizer.eu-west-2.amazonaws.com" + }, + "eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "hostname" : "compute-optimizer.eu-west-3.amazonaws.com" + }, + "il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "compute-optimizer.il-central-1.amazonaws.com" + }, + "me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "hostname" : "compute-optimizer.me-central-1.amazonaws.com" + }, + "me-south-1" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "hostname" : "compute-optimizer.me-south-1.amazonaws.com" + }, + "sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "compute-optimizer.sa-east-1.amazonaws.com" + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "compute-optimizer.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "compute-optimizer.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "compute-optimizer.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "compute-optimizer.us-west-2.amazonaws.com" + } + } + }, + "config" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "config-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "config-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "config-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "config-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "config-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "config-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "config-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "config-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "connect" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "connect-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-west-2" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "connect-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "connect-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "connect-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "connect-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "connect-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "connect-campaigns" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-2" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "connect-campaigns-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "connect-campaigns-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "connect-campaigns-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "connect-campaigns-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "contact-lens" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "controltower" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "controltower-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "controltower-fips.ca-central-1.amazonaws.com" + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "controltower-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1-fips" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "controltower-fips.ca-west-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "controltower-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "controltower-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "controltower-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "controltower-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "controltower-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "controltower-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "controltower-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "controltower-fips.us-west-2.amazonaws.com" + } + } + }, + "cost-optimization-hub" : { + "endpoints" : { + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "cost-optimization-hub.us-east-1.amazonaws.com" + } + } + }, + "cur" : { + "endpoints" : { + "us-east-1" : { } + } + }, + "data-ats.iot" : { + "defaults" : { + "credentialScope" : { + "service" : "iotdata" + }, + "protocols" : [ "https" ] + }, + "endpoints" : { + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "data.iot-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "data.iot-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "data.iot-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "data.iot-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "data.iot-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "data.iot" : { + "defaults" : { + "credentialScope" : { + "service" : "iotdata" + }, + "protocols" : [ "https" ] + }, + "endpoints" : { + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "data.iot-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.us-west-2.amazonaws.com" + }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "data.iot-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "data.iot-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "data.iot-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "data.iot-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "data.jobs.iot" : { + "endpoints" : { + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "data.jobs.iot-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "data.jobs.iot-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "data.jobs.iot-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "data.jobs.iot-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "data.jobs.iot-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "data.jobs.iot-fips.us-west-2.amazonaws.com" + }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "data.jobs.iot-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "data.jobs.iot-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "data.jobs.iot-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "data.jobs.iot-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "data.mediastore" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "databrew" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "databrew-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "databrew-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "databrew-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "databrew-fips.us-west-2.amazonaws.com" + }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "databrew-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "databrew-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "databrew-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "databrew-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "dataexchange" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "datapipeline" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-2" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "datasync" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "datasync.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "datasync.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "datasync.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "datasync.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "datasync.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "datasync.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "datasync.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "datasync.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "datasync.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "datasync.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "datasync.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "datasync.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "datasync.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "datasync-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "datasync-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "datasync.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "datasync-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "datasync-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "datasync.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "datasync.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "datasync.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "datasync.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "datasync.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "datasync.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "datasync.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "datasync.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "datasync.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "datasync-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "datasync-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "datasync-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "datasync-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "datasync-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "datasync-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "datasync.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "datasync.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "datasync.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "datasync.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "datasync.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "datasync-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "datasync-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "datasync.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "datasync-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "datasync-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "datasync.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "datasync-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "datasync-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "datasync.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "datasync-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "datasync-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "datasync.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "datazone" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "hostname" : "datazone.ap-northeast-1.api.aws" + }, + "ap-northeast-2" : { + "hostname" : "datazone.ap-northeast-2.api.aws" + }, + "ap-northeast-3" : { + "hostname" : "datazone.ap-northeast-3.api.aws" + }, + "ap-south-1" : { + "hostname" : "datazone.ap-south-1.api.aws" + }, + "ap-south-2" : { + "hostname" : "datazone.ap-south-2.api.aws" + }, + "ap-southeast-1" : { + "hostname" : "datazone.ap-southeast-1.api.aws" + }, + "ap-southeast-2" : { + "hostname" : "datazone.ap-southeast-2.api.aws" + }, + "ap-southeast-3" : { + "hostname" : "datazone.ap-southeast-3.api.aws" + }, + "ap-southeast-4" : { + "hostname" : "datazone.ap-southeast-4.api.aws" + }, + "ap-southeast-5" : { + "hostname" : "datazone.ap-southeast-5.api.aws" + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "hostname" : "datazone.ap-southeast-7.api.aws" + }, + "ca-central-1" : { + "hostname" : "datazone.ca-central-1.api.aws", + "variants" : [ { + "hostname" : "datazone-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "hostname" : "datazone.ca-west-1.api.aws" + }, + "eu-central-1" : { + "hostname" : "datazone.eu-central-1.api.aws" + }, + "eu-central-2" : { }, + "eu-north-1" : { + "hostname" : "datazone.eu-north-1.api.aws" + }, + "eu-south-1" : { + "hostname" : "datazone.eu-south-1.api.aws" + }, + "eu-south-2" : { }, + "eu-west-1" : { + "hostname" : "datazone.eu-west-1.api.aws" + }, + "eu-west-2" : { + "hostname" : "datazone.eu-west-2.api.aws" + }, + "eu-west-3" : { + "hostname" : "datazone.eu-west-3.api.aws" + }, + "il-central-1" : { + "hostname" : "datazone.il-central-1.api.aws" + }, + "me-central-1" : { + "hostname" : "datazone.me-central-1.api.aws" + }, + "me-south-1" : { + "hostname" : "datazone.me-south-1.api.aws" + }, + "mx-central-1" : { + "hostname" : "datazone.mx-central-1.api.aws" + }, + "sa-east-1" : { + "hostname" : "datazone.sa-east-1.api.aws" + }, + "us-east-1" : { + "hostname" : "datazone.us-east-1.api.aws", + "variants" : [ { + "hostname" : "datazone-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "hostname" : "datazone.us-east-2.api.aws", + "variants" : [ { + "hostname" : "datazone-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "hostname" : "datazone.us-west-1.api.aws" + }, + "us-west-2" : { + "hostname" : "datazone.us-west-2.api.aws", + "variants" : [ { + "hostname" : "datazone-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "dax" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "devicefarm" : { + "endpoints" : { + "us-west-2" : { } + } + }, + "devops-guru" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "devops-guru-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "devops-guru-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "devops-guru-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "devops-guru-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "devops-guru-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "devops-guru-fips.us-west-2.amazonaws.com" + }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "devops-guru-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "devops-guru-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "devops-guru-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "devops-guru-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "directconnect" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "directconnect-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "directconnect-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "directconnect-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "directconnect-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "directconnect-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "directconnect-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "directconnect-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "directconnect-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "directconnect-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "directconnect-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "directconnect-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "directconnect-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "discovery" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "dlm" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "dlm.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "dlm.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "dlm.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "dlm.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "dlm.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "dlm.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "dlm.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "dlm.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "dlm.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "dlm.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "dlm.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "dlm.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "dlm.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "dlm-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "dlm.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "dlm-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "dlm.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "dlm.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "dlm.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "dlm.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "dlm.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "dlm.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "dlm.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "dlm.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "dlm.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "dlm.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "dlm.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "dlm.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "dlm.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "dlm.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "dlm-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "dlm.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "dlm-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "dlm.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "dlm-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "dlm.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "dlm-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "dlm.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "dms" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "dms" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "dms-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "dms-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "dms-fips.us-west-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "dms-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "dms-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "dms-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "dms-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "dms-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "dms-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "dms-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "dms-fips.us-west-2.amazonaws.com" + } + } + }, + "docdb" : { + "endpoints" : { + "ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "rds.ap-northeast-1.amazonaws.com" + }, + "ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "rds.ap-northeast-2.amazonaws.com" + }, + "ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "rds.ap-south-1.amazonaws.com" + }, + "ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "rds.ap-southeast-1.amazonaws.com" + }, + "ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "rds.ap-southeast-2.amazonaws.com" + }, + "ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "rds.ca-central-1.amazonaws.com" + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "rds.eu-central-1.amazonaws.com" + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "rds.eu-west-1.amazonaws.com" + }, + "eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "rds.eu-west-2.amazonaws.com" + }, + "eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "hostname" : "rds.eu-west-3.amazonaws.com" + }, + "sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "rds.sa-east-1.amazonaws.com" + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "rds.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "rds.us-east-2.amazonaws.com" + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "rds.us-west-2.amazonaws.com" + } + } + }, + "drs" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "drs-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "drs-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "drs-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "drs-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "drs-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "drs-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "drs-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "drs-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "ds" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "ds-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "ds-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "ds-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "ds-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ds-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ds-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ds-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ds-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "ds-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "ds-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "ds-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "ds-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "dynamodb" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "dynamodb-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "dynamodb-fips.ca-central-1.amazonaws.com" + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "dynamodb-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1-fips" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "dynamodb-fips.ca-west-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "local" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "localhost:8000", + "protocols" : [ "http" ] + }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "dynamodb-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "dynamodb-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "dynamodb-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "dynamodb-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "dynamodb-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "dynamodb-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "dynamodb-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "dynamodb-fips.us-west-2.amazonaws.com" + } + } + }, + "ebs" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "ebs-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "ebs-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "ebs-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "ebs-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ebs-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ebs-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ebs-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ebs-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "ebs-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "ebs-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "ebs-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "ebs-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "ec2" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "ec2.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "ec2.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "ec2.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "ec2.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "ec2.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "ec2.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "ec2.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "ec2-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ec2.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "ec2-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "ec2.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "ec2.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "ec2.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "ec2.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "ec2.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "ec2.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "ec2-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "ec2-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ec2-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ec2-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ec2-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ec2-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { + "variants" : [ { + "hostname" : "ec2.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "ec2.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "ec2-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ec2.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "ec2-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ec2.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "ec2-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ec2.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "ec2-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ec2.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "ecs" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ecs-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ecs-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ecs-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ecs-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "ecs-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "ecs-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "ecs-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "ecs-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "edge.sagemaker" : { + "endpoints" : { + "ap-northeast-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "eks" : { + "defaults" : { + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "fips.eks.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "fips.eks.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "fips.eks.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "fips.eks.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "fips.eks.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "fips.eks.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "fips.eks.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "fips.eks.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "fips.eks.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "eks-auth" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { + "hostname" : "eks-auth.af-south-1.api.aws" + }, + "ap-east-1" : { + "hostname" : "eks-auth.ap-east-1.api.aws" + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "hostname" : "eks-auth.ap-northeast-1.api.aws" + }, + "ap-northeast-2" : { + "hostname" : "eks-auth.ap-northeast-2.api.aws" + }, + "ap-northeast-3" : { + "hostname" : "eks-auth.ap-northeast-3.api.aws" + }, + "ap-south-1" : { + "hostname" : "eks-auth.ap-south-1.api.aws" + }, + "ap-south-2" : { + "hostname" : "eks-auth.ap-south-2.api.aws" + }, + "ap-southeast-1" : { + "hostname" : "eks-auth.ap-southeast-1.api.aws" + }, + "ap-southeast-2" : { + "hostname" : "eks-auth.ap-southeast-2.api.aws" + }, + "ap-southeast-3" : { + "hostname" : "eks-auth.ap-southeast-3.api.aws" + }, + "ap-southeast-4" : { + "hostname" : "eks-auth.ap-southeast-4.api.aws" + }, + "ap-southeast-5" : { + "hostname" : "eks-auth.ap-southeast-5.api.aws" + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "hostname" : "eks-auth.ap-southeast-7.api.aws" + }, + "ca-central-1" : { + "hostname" : "eks-auth.ca-central-1.api.aws" + }, + "ca-west-1" : { + "hostname" : "eks-auth.ca-west-1.api.aws" + }, + "eu-central-1" : { + "hostname" : "eks-auth.eu-central-1.api.aws" + }, + "eu-central-2" : { + "hostname" : "eks-auth.eu-central-2.api.aws" + }, + "eu-north-1" : { + "hostname" : "eks-auth.eu-north-1.api.aws" + }, + "eu-south-1" : { + "hostname" : "eks-auth.eu-south-1.api.aws" + }, + "eu-south-2" : { + "hostname" : "eks-auth.eu-south-2.api.aws" + }, + "eu-west-1" : { + "hostname" : "eks-auth.eu-west-1.api.aws" + }, + "eu-west-2" : { + "hostname" : "eks-auth.eu-west-2.api.aws" + }, + "eu-west-3" : { + "hostname" : "eks-auth.eu-west-3.api.aws" + }, + "il-central-1" : { + "hostname" : "eks-auth.il-central-1.api.aws" + }, + "me-central-1" : { + "hostname" : "eks-auth.me-central-1.api.aws" + }, + "me-south-1" : { + "hostname" : "eks-auth.me-south-1.api.aws" + }, + "mx-central-1" : { + "hostname" : "eks-auth.mx-central-1.api.aws" + }, + "sa-east-1" : { + "hostname" : "eks-auth.sa-east-1.api.aws" + }, + "us-east-1" : { + "hostname" : "eks-auth.us-east-1.api.aws" + }, + "us-east-2" : { + "hostname" : "eks-auth.us-east-2.api.aws" + }, + "us-west-1" : { + "hostname" : "eks-auth.us-west-1.api.aws" + }, + "us-west-2" : { + "hostname" : "eks-auth.us-west-2.api.aws" + } + } + }, + "elasticache" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "elasticache-fips.us-west-1.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "elasticache-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "elasticache-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "elasticache-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "elasticache-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "elasticache-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "elasticache-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "elasticache-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "elasticache-fips.us-west-2.amazonaws.com" + } + } + }, + "elasticbeanstalk" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "elasticbeanstalk-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "elasticbeanstalk-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "elasticbeanstalk-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "elasticbeanstalk-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { }, + "me-south-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "elasticbeanstalk-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "elasticbeanstalk.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "elasticbeanstalk-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "elasticbeanstalk-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "elasticbeanstalk.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "elasticbeanstalk-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "elasticbeanstalk.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "elasticbeanstalk-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "elasticbeanstalk-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "elasticbeanstalk.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "elasticfilesystem" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.af-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-northeast-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-northeast-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-northeast-3.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-southeast-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-southeast-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-southeast-3.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-southeast-4.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-southeast-5.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-southeast-7.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.eu-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.eu-central-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.eu-north-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.eu-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.eu-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.eu-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.eu-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.eu-west-3.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "fips-af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.af-south-1.amazonaws.com" + }, + "fips-ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-east-1.amazonaws.com" + }, + "fips-ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-northeast-1.amazonaws.com" + }, + "fips-ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-northeast-2.amazonaws.com" + }, + "fips-ap-northeast-3" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-northeast-3.amazonaws.com" + }, + "fips-ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-south-1.amazonaws.com" + }, + "fips-ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-south-2.amazonaws.com" + }, + "fips-ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-southeast-1.amazonaws.com" + }, + "fips-ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-southeast-2.amazonaws.com" + }, + "fips-ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-southeast-3.amazonaws.com" + }, + "fips-ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-southeast-4.amazonaws.com" + }, + "fips-ap-southeast-5" : { + "credentialScope" : { + "region" : "ap-southeast-5" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-southeast-5.amazonaws.com" + }, + "fips-ap-southeast-7" : { + "credentialScope" : { + "region" : "ap-southeast-7" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-southeast-7.amazonaws.com" + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ca-west-1.amazonaws.com" + }, + "fips-eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.eu-central-1.amazonaws.com" + }, + "fips-eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.eu-central-2.amazonaws.com" + }, + "fips-eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.eu-north-1.amazonaws.com" + }, + "fips-eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.eu-south-1.amazonaws.com" + }, + "fips-eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.eu-south-2.amazonaws.com" + }, + "fips-eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.eu-west-1.amazonaws.com" + }, + "fips-eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.eu-west-2.amazonaws.com" + }, + "fips-eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.eu-west-3.amazonaws.com" + }, + "fips-il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.il-central-1.amazonaws.com" + }, + "fips-me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.me-central-1.amazonaws.com" + }, + "fips-me-south-1" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.me-south-1.amazonaws.com" + }, + "fips-mx-central-1" : { + "credentialScope" : { + "region" : "mx-central-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.mx-central-1.amazonaws.com" + }, + "fips-sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.sa-east-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.il-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.me-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.me-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.mx-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.sa-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "elasticloadbalancing" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "elasticloadbalancing-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "elasticloadbalancing-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "elasticloadbalancing-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "elasticloadbalancing-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "elasticloadbalancing-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "elasticloadbalancing-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "elasticloadbalancing-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "elasticloadbalancing-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "elasticmapreduce" : { + "defaults" : { + "protocols" : [ "https" ], + "sslCommonName" : "{region}.{service}.{dnsSuffix}" + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "elasticmapreduce-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "elasticmapreduce-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { + "sslCommonName" : "{service}.{region}.{dnsSuffix}" + }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "elasticmapreduce-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "elasticmapreduce-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "elasticmapreduce-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "elasticmapreduce-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "elasticmapreduce-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "elasticmapreduce-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "sslCommonName" : "{service}.{region}.{dnsSuffix}", + "variants" : [ { + "hostname" : "elasticmapreduce-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "elasticmapreduce-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "elasticmapreduce.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "elasticmapreduce-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "elasticmapreduce-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "email" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "email-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "email-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "email-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "email-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "email-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "email-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "email-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "email-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "email-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "email-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "emr-containers" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "emr-containers-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "emr-containers-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "emr-containers-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "emr-containers-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "emr-containers-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "emr-containers-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "emr-containers-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "emr-containers-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "emr-containers-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "emr-containers-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "emr-serverless" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "emr-serverless-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "emr-serverless-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "emr-serverless-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "emr-serverless-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "emr-serverless-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "emr-serverless-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "emr-serverless-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "emr-serverless-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "emr-serverless-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "emr-serverless-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "entitlement.marketplace" : { + "defaults" : { + "credentialScope" : { + "service" : "aws-marketplace" + } + }, + "endpoints" : { + "us-east-1" : { + "variants" : [ { + "hostname" : "entitlement-marketplace.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "es" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "aos.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "aos.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "aos.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "aos.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "aos.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "aos.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "aos.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "aos.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "aos.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "aos.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "aos.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "aos.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "aos.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "aos.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "aos.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "aos.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "aos.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "aos.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "aos.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "aos.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "aos.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "aos.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "aos.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "es-fips.us-west-1.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "aos.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "aos.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "aos.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "aos.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "aos.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "aos.us-east-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "es-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "es-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "aos.us-east-2.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "es-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "es-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "aos.us-west-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "es-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "es-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "aos.us-west-2.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "es-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "es-fips.us-west-2.amazonaws.com" + } + } + }, + "events" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "events.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "events.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "events.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "events.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "events.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "events.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "events.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "events.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "events.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "events.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "events.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "events.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "events.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "events.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "events.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "events.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "events.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "events.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "events.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "events.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "events.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "events.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "events.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "events-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "events-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "events-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "events-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "events.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "events.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "events.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "events.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "events.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "events-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "events-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "events.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "events-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "events-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "events.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "events-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "events-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "events.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "events-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "events-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "events.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "eventsv2" : { + "endpoints" : { + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-5" : { }, + "ap-southeast-7" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "finspace" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "finspace-api" : { + "endpoints" : { + "ca-central-1" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "firehose" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "firehose.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "firehose.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "firehose.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "firehose.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "firehose.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "firehose.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "firehose.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "firehose.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "firehose.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "firehose.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "firehose.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "firehose.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "firehose.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "firehose.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "firehose.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "firehose.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "firehose.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "firehose.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "firehose.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "firehose.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "firehose.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "firehose.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "firehose-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "firehose-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "firehose-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "firehose-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "firehose.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "firehose.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "firehose.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "firehose.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "firehose.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "firehose-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "firehose-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "firehose.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "firehose-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "firehose-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "firehose.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "firehose-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "firehose-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "firehose.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "firehose-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "firehose-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "firehose.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "fms" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "fms-fips.af-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "fms-fips.ap-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "fms-fips.ap-northeast-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "fms-fips.ap-northeast-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-northeast-3" : { }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "fms-fips.ap-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-south-2" : { }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "fms-fips.ap-southeast-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "fms-fips.ap-southeast-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "fms-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "fms-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "fms-fips.eu-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "fms-fips.eu-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-south-2" : { }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "fms-fips.eu-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "fms-fips.eu-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "fms-fips.eu-west-3.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "fips-af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.af-south-1.amazonaws.com" + }, + "fips-ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.ap-east-1.amazonaws.com" + }, + "fips-ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.ap-northeast-1.amazonaws.com" + }, + "fips-ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "deprecated" : true, + "hostname" : "fms-fips.ap-northeast-2.amazonaws.com" + }, + "fips-ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.ap-south-1.amazonaws.com" + }, + "fips-ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.ap-southeast-1.amazonaws.com" + }, + "fips-ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "deprecated" : true, + "hostname" : "fms-fips.ap-southeast-2.amazonaws.com" + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.ca-west-1.amazonaws.com" + }, + "fips-eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.eu-central-1.amazonaws.com" + }, + "fips-eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.eu-south-1.amazonaws.com" + }, + "fips-eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.eu-west-1.amazonaws.com" + }, + "fips-eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "deprecated" : true, + "hostname" : "fms-fips.eu-west-2.amazonaws.com" + }, + "fips-eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "deprecated" : true, + "hostname" : "fms-fips.eu-west-3.amazonaws.com" + }, + "fips-me-south-1" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.me-south-1.amazonaws.com" + }, + "fips-sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.sa-east-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "fms-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "fms-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { + "variants" : [ { + "hostname" : "fms-fips.me-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "mx-central-1" : { }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "fms-fips.sa-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "fms-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "fms-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "fms-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "fms-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "forecast" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "forecast-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "forecast-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "forecast-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "forecast-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "forecast-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "forecast-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "forecastquery" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "forecastquery-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "forecastquery-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "forecastquery-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "forecastquery-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "forecastquery-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "forecastquery-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "frauddetector" : { + "endpoints" : { + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "fsx" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "fsx-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "fsx-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.ca-west-1.amazonaws.com" + }, + "fips-prod-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.ca-central-1.amazonaws.com" + }, + "fips-prod-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.ca-west-1.amazonaws.com" + }, + "fips-prod-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-east-1.amazonaws.com" + }, + "fips-prod-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-east-2.amazonaws.com" + }, + "fips-prod-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-west-1.amazonaws.com" + }, + "fips-prod-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-west-2.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "prod-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "fsx-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "prod-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "fsx-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "prod-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "fsx-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "prod-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "fsx-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "prod-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "fsx-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "prod-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "fsx-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "fsx-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "fsx-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "fsx-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "fsx-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "gamelift" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-5" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "gameliftstreams" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { + "hostname" : "gameliftstreams.af-south-1.api.aws" + }, + "ap-east-1" : { + "hostname" : "gameliftstreams.ap-east-1.api.aws" + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "hostname" : "gameliftstreams.ap-northeast-1.api.aws" + }, + "ap-northeast-2" : { + "hostname" : "gameliftstreams.ap-northeast-2.api.aws" + }, + "ap-northeast-3" : { + "hostname" : "gameliftstreams.ap-northeast-3.api.aws" + }, + "ap-south-1" : { + "hostname" : "gameliftstreams.ap-south-1.api.aws" + }, + "ap-south-2" : { + "hostname" : "gameliftstreams.ap-south-2.api.aws" + }, + "ap-southeast-1" : { + "hostname" : "gameliftstreams.ap-southeast-1.api.aws" + }, + "ap-southeast-2" : { + "hostname" : "gameliftstreams.ap-southeast-2.api.aws" + }, + "ap-southeast-3" : { + "hostname" : "gameliftstreams.ap-southeast-3.api.aws" + }, + "ap-southeast-4" : { + "hostname" : "gameliftstreams.ap-southeast-4.api.aws" + }, + "ap-southeast-5" : { + "hostname" : "gameliftstreams.ap-southeast-5.api.aws" + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "hostname" : "gameliftstreams.ap-southeast-7.api.aws" + }, + "ca-central-1" : { + "hostname" : "gameliftstreams.ca-central-1.api.aws" + }, + "ca-west-1" : { + "hostname" : "gameliftstreams.ca-west-1.api.aws" + }, + "eu-central-1" : { + "hostname" : "gameliftstreams.eu-central-1.api.aws" + }, + "eu-central-2" : { + "hostname" : "gameliftstreams.eu-central-2.api.aws" + }, + "eu-north-1" : { + "hostname" : "gameliftstreams.eu-north-1.api.aws" + }, + "eu-south-1" : { + "hostname" : "gameliftstreams.eu-south-1.api.aws" + }, + "eu-south-2" : { + "hostname" : "gameliftstreams.eu-south-2.api.aws" + }, + "eu-west-1" : { + "hostname" : "gameliftstreams.eu-west-1.api.aws" + }, + "eu-west-2" : { + "hostname" : "gameliftstreams.eu-west-2.api.aws" + }, + "eu-west-3" : { + "hostname" : "gameliftstreams.eu-west-3.api.aws" + }, + "il-central-1" : { + "hostname" : "gameliftstreams.il-central-1.api.aws" + }, + "me-central-1" : { + "hostname" : "gameliftstreams.me-central-1.api.aws" + }, + "me-south-1" : { + "hostname" : "gameliftstreams.me-south-1.api.aws" + }, + "mx-central-1" : { + "hostname" : "gameliftstreams.mx-central-1.api.aws" + }, + "sa-east-1" : { + "hostname" : "gameliftstreams.sa-east-1.api.aws" + }, + "us-east-1" : { + "hostname" : "gameliftstreams.us-east-1.api.aws" + }, + "us-east-2" : { + "hostname" : "gameliftstreams.us-east-2.api.aws" + }, + "us-west-1" : { + "hostname" : "gameliftstreams.us-west-1.api.aws" + }, + "us-west-2" : { + "hostname" : "gameliftstreams.us-west-2.api.aws" + } + } + }, + "geo" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "glacier" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "glacier-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "glacier-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "glacier-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "glacier-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "glacier-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "glacier-fips.us-west-2.amazonaws.com" + }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "glacier-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "glacier-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "glacier-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "glacier-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "globalaccelerator" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "globalaccelerator-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "glue" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "glue.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "glue.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "glue.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "glue.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "glue.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "glue.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "glue.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "glue.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "glue.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "glue.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "glue.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "glue.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "glue.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "glue.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "glue.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "glue.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "glue.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "glue.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "glue.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "glue.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "glue.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "glue.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "glue.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "glue-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "glue-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "glue-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "glue-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "glue.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "glue.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "glue.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "glue.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "glue.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "glue-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "glue-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "glue.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "glue-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "glue-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "glue.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "glue-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "glue-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "glue.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "glue-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "glue-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "glue.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "grafana" : { + "endpoints" : { + "ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "grafana.ap-northeast-1.amazonaws.com" + }, + "ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "grafana.ap-northeast-2.amazonaws.com" + }, + "ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "grafana.ap-southeast-1.amazonaws.com" + }, + "ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "grafana.ap-southeast-2.amazonaws.com" + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "grafana.eu-central-1.amazonaws.com" + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "grafana.eu-west-1.amazonaws.com" + }, + "eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "grafana.eu-west-2.amazonaws.com" + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "grafana.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "grafana.us-east-2.amazonaws.com" + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "grafana.us-west-2.amazonaws.com" + } + } + }, + "greengrass" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "greengrass-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "greengrass-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "greengrass-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "greengrass-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "greengrass-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "greengrass-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "greengrass-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "greengrass-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + }, + "isRegionalized" : true + }, + "groundstation" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "groundstation-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "groundstation-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "groundstation-fips.us-west-2.amazonaws.com" + }, + "me-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "groundstation-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "groundstation-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "groundstation-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "groundstation-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "groundstation-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "groundstation-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + } + } + }, + "guardduty" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "guardduty-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1-fips" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "guardduty-fips.ca-west-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "guardduty-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "guardduty-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "guardduty-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "guardduty-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "guardduty-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "guardduty-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "guardduty-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "guardduty-fips.us-west-2.amazonaws.com" + } + }, + "isRegionalized" : true + }, + "health" : { + "defaults" : { + "protocols" : [ "https" ], + "sslCommonName" : "health.us-east-1.amazonaws.com" + }, + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "global.health.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "health-fips.us-east-2.amazonaws.com" + }, + "us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "health-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "healthlake" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "ap-south-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "iam" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "iam.amazonaws.com", + "variants" : [ { + "hostname" : "iam-fips.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "iam.global.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "aws-global-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "iam-fips.amazonaws.com" + }, + "iam" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "iam-fips.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "iam-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "iam-fips.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "identity-chime" : { + "endpoints" : { + "eu-central-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "identity-chime-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "identity-chime-fips.us-east-1.amazonaws.com" + } + } + }, + "identitystore" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "importexport" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1", + "service" : "IngestionService" + }, + "hostname" : "importexport.amazonaws.com", + "signatureVersions" : [ "v2", "v4" ] + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "ingest.timestream" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-south-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "ingest-fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ingest.timestream-fips.us-east-1.amazonaws.com" + }, + "ingest-fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ingest.timestream-fips.us-east-2.amazonaws.com" + }, + "ingest-fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ingest.timestream-fips.us-west-2.amazonaws.com" + }, + "ingest-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "ingest.timestream-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ingest-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "ingest.timestream-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ingest-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "ingest.timestream-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "inspector" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "inspector-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "inspector-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "inspector-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "inspector-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "inspector-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "inspector-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "inspector-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "inspector-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "inspector2" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "inspector2-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "inspector2-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "inspector2-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "inspector2-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "inspector2-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "inspector2-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "inspector2-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "inspector2-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "internetmonitor" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { + "hostname" : "internetmonitor.af-south-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "hostname" : "internetmonitor.ap-east-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "hostname" : "internetmonitor.ap-northeast-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "hostname" : "internetmonitor.ap-northeast-2.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "hostname" : "internetmonitor.ap-northeast-3.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "hostname" : "internetmonitor.ap-south-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "hostname" : "internetmonitor.ap-south-2.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "hostname" : "internetmonitor.ap-southeast-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "hostname" : "internetmonitor.ap-southeast-2.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "hostname" : "internetmonitor.ap-southeast-3.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "hostname" : "internetmonitor.ap-southeast-4.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "hostname" : "internetmonitor.ap-southeast-5.api.aws" + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "hostname" : "internetmonitor.ap-southeast-7.api.aws" + }, + "ca-central-1" : { + "hostname" : "internetmonitor.ca-central-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "internetmonitor-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "internetmonitor.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "hostname" : "internetmonitor.ca-west-1.api.aws" + }, + "eu-central-1" : { + "hostname" : "internetmonitor.eu-central-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "hostname" : "internetmonitor.eu-central-2.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "hostname" : "internetmonitor.eu-north-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "hostname" : "internetmonitor.eu-south-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "hostname" : "internetmonitor.eu-south-2.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "hostname" : "internetmonitor.eu-west-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "hostname" : "internetmonitor.eu-west-2.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "hostname" : "internetmonitor.eu-west-3.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { + "hostname" : "internetmonitor.il-central-1.api.aws" + }, + "me-central-1" : { + "hostname" : "internetmonitor.me-central-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "hostname" : "internetmonitor.me-south-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "hostname" : "internetmonitor.mx-central-1.api.aws" + }, + "sa-east-1" : { + "hostname" : "internetmonitor.sa-east-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "hostname" : "internetmonitor.us-east-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "internetmonitor-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "internetmonitor.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "hostname" : "internetmonitor.us-east-2.api.aws", + "variants" : [ { + "hostname" : "internetmonitor-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "internetmonitor-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "internetmonitor.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "hostname" : "internetmonitor.us-west-1.api.aws", + "variants" : [ { + "hostname" : "internetmonitor-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "internetmonitor-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "internetmonitor.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "hostname" : "internetmonitor.us-west-2.api.aws", + "variants" : [ { + "hostname" : "internetmonitor-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "internetmonitor-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "internetmonitor.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "iot" : { + "endpoints" : { + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "iot-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "deprecated" : true, + "hostname" : "iot-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "deprecated" : true, + "hostname" : "iot-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "deprecated" : true, + "hostname" : "iot-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "deprecated" : true, + "hostname" : "iot-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "deprecated" : true, + "hostname" : "iot-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "iot-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "iot-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "iot-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "iot-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "iotfleetwise" : { + "endpoints" : { + "ap-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + } + } + }, + "iotsecuredtunneling" : { + "defaults" : { + "variants" : [ { + "hostname" : "api.tunneling.iot-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "api.tunneling.iot-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.us-west-2.amazonaws.com" + }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "api.tunneling.iot-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "api.tunneling.iot-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "api.tunneling.iot-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "api.tunneling.iot-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "iotsitewise" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "iotsitewise-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "iotsitewise-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "iotsitewise-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "iotsitewise-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "iotsitewise-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "iotsitewise-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "iotsitewise-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "iotsitewise-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "iotthingsgraph" : { + "defaults" : { + "credentialScope" : { + "service" : "iotthingsgraph" + } + }, + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-2" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "iottwinmaker" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "api-ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "api.iottwinmaker.ap-northeast-1.amazonaws.com" + }, + "api-ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "api.iottwinmaker.ap-northeast-2.amazonaws.com" + }, + "api-ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "api.iottwinmaker.ap-south-1.amazonaws.com" + }, + "api-ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "api.iottwinmaker.ap-southeast-1.amazonaws.com" + }, + "api-ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "api.iottwinmaker.ap-southeast-2.amazonaws.com" + }, + "api-eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "api.iottwinmaker.eu-central-1.amazonaws.com" + }, + "api-eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "api.iottwinmaker.eu-west-1.amazonaws.com" + }, + "api-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "api.iottwinmaker.us-east-1.amazonaws.com" + }, + "api-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "api.iottwinmaker.us-west-2.amazonaws.com" + }, + "data-ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "data.iottwinmaker.ap-northeast-1.amazonaws.com" + }, + "data-ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "data.iottwinmaker.ap-northeast-2.amazonaws.com" + }, + "data-ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "data.iottwinmaker.ap-south-1.amazonaws.com" + }, + "data-ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "data.iottwinmaker.ap-southeast-1.amazonaws.com" + }, + "data-ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "data.iottwinmaker.ap-southeast-2.amazonaws.com" + }, + "data-eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "data.iottwinmaker.eu-central-1.amazonaws.com" + }, + "data-eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "data.iottwinmaker.eu-west-1.amazonaws.com" + }, + "data-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "data.iottwinmaker.us-east-1.amazonaws.com" + }, + "data-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "data.iottwinmaker.us-west-2.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "fips-api-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "api.iottwinmaker-fips.us-east-1.amazonaws.com" + }, + "fips-api-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "api.iottwinmaker-fips.us-west-2.amazonaws.com" + }, + "fips-data-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "data.iottwinmaker-fips.us-east-1.amazonaws.com" + }, + "fips-data-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "data.iottwinmaker-fips.us-west-2.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "iottwinmaker-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "iottwinmaker-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "iottwinmaker-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "iottwinmaker-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "iotwireless" : { + "endpoints" : { + "ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "api.iotwireless.ap-northeast-1.amazonaws.com" + }, + "ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "api.iotwireless.ap-southeast-2.amazonaws.com" + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "api.iotwireless.eu-west-1.amazonaws.com" + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "api.iotwireless.us-east-1.amazonaws.com" + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "api.iotwireless.us-west-2.amazonaws.com" + } + } + }, + "ivs" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "ivschat" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "ivsrealtime" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "kafka" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "kafka-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "kafka-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "kafka-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "kafka-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "kafka-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "kafka-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "kafka-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "kafka-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "kafka-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "kafka-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "kafka-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "kafka-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "kafkaconnect" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "kendra" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "kendra-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "kendra-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "kendra-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "kendra-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "kendra-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "kendra-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "kendra-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "kendra-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "kendra-ranking" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { + "hostname" : "kendra-ranking.af-south-1.api.aws" + }, + "ap-east-1" : { + "hostname" : "kendra-ranking.ap-east-1.api.aws" + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "hostname" : "kendra-ranking.ap-northeast-1.api.aws" + }, + "ap-northeast-2" : { + "hostname" : "kendra-ranking.ap-northeast-2.api.aws" + }, + "ap-northeast-3" : { + "hostname" : "kendra-ranking.ap-northeast-3.api.aws" + }, + "ap-south-1" : { + "hostname" : "kendra-ranking.ap-south-1.api.aws" + }, + "ap-south-2" : { + "hostname" : "kendra-ranking.ap-south-2.api.aws" + }, + "ap-southeast-1" : { + "hostname" : "kendra-ranking.ap-southeast-1.api.aws" + }, + "ap-southeast-2" : { + "hostname" : "kendra-ranking.ap-southeast-2.api.aws" + }, + "ap-southeast-3" : { + "hostname" : "kendra-ranking.ap-southeast-3.api.aws" + }, + "ap-southeast-4" : { + "hostname" : "kendra-ranking.ap-southeast-4.api.aws" + }, + "ap-southeast-5" : { + "hostname" : "kendra-ranking.ap-southeast-5.api.aws" + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "hostname" : "kendra-ranking.ap-southeast-7.api.aws" + }, + "ca-central-1" : { + "hostname" : "kendra-ranking.ca-central-1.api.aws", + "variants" : [ { + "hostname" : "kendra-ranking-fips.ca-central-1.api.aws", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "hostname" : "kendra-ranking.ca-west-1.api.aws" + }, + "eu-central-2" : { + "hostname" : "kendra-ranking.eu-central-2.api.aws" + }, + "eu-north-1" : { + "hostname" : "kendra-ranking.eu-north-1.api.aws" + }, + "eu-south-1" : { + "hostname" : "kendra-ranking.eu-south-1.api.aws" + }, + "eu-south-2" : { + "hostname" : "kendra-ranking.eu-south-2.api.aws" + }, + "eu-west-1" : { + "hostname" : "kendra-ranking.eu-west-1.api.aws" + }, + "eu-west-3" : { + "hostname" : "kendra-ranking.eu-west-3.api.aws" + }, + "il-central-1" : { + "hostname" : "kendra-ranking.il-central-1.api.aws" + }, + "me-central-1" : { + "hostname" : "kendra-ranking.me-central-1.api.aws" + }, + "me-south-1" : { + "hostname" : "kendra-ranking.me-south-1.api.aws" + }, + "mx-central-1" : { + "hostname" : "kendra-ranking.mx-central-1.api.aws" + }, + "sa-east-1" : { + "hostname" : "kendra-ranking.sa-east-1.api.aws" + }, + "us-east-1" : { + "hostname" : "kendra-ranking.us-east-1.api.aws", + "variants" : [ { + "hostname" : "kendra-ranking-fips.us-east-1.api.aws", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "hostname" : "kendra-ranking.us-east-2.api.aws", + "variants" : [ { + "hostname" : "kendra-ranking-fips.us-east-2.api.aws", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "hostname" : "kendra-ranking.us-west-1.api.aws" + }, + "us-west-2" : { + "hostname" : "kendra-ranking.us-west-2.api.aws", + "variants" : [ { + "hostname" : "kendra-ranking-fips.us-west-2.api.aws", + "tags" : [ "fips" ] + } ] + } + } + }, + "kinesis" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "kinesis-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "kinesis-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "kinesis-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "kinesis-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "kinesis-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "kinesis-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "kinesis-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "kinesis-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "kinesisanalytics" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "kinesisanalytics-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "kinesisanalytics-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "kinesisanalytics-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "kinesisanalytics-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "kinesisanalytics-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "kinesisanalytics-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "kinesisanalytics-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "kinesisanalytics-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "kinesisanalytics-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "kinesisanalytics-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "kinesisanalytics-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "kinesisanalytics-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "kinesisvideo" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "kms" : { + "endpoints" : { + "ProdFips" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "deprecated" : true, + "hostname" : "kms-fips.eu-central-2.amazonaws.com" + }, + "af-south-1" : { + "variants" : [ { + "hostname" : "kms-fips.af-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "af-south-1-fips" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.af-south-1.amazonaws.com" + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "kms-fips.ap-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-east-1-fips" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-east-1.amazonaws.com" + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "kms-fips.ap-northeast-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-northeast-1-fips" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-northeast-1.amazonaws.com" + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "kms-fips.ap-northeast-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-northeast-2-fips" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-northeast-2.amazonaws.com" + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "kms-fips.ap-northeast-3.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-northeast-3-fips" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-northeast-3.amazonaws.com" + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "kms-fips.ap-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-south-1-fips" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-south-1.amazonaws.com" + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "kms-fips.ap-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-south-2-fips" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-south-2.amazonaws.com" + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "kms-fips.ap-southeast-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-1-fips" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-southeast-1.amazonaws.com" + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "kms-fips.ap-southeast-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-2-fips" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-southeast-2.amazonaws.com" + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "kms-fips.ap-southeast-3.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-3-fips" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-southeast-3.amazonaws.com" + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "kms-fips.ap-southeast-4.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-4-fips" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-southeast-4.amazonaws.com" + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "kms-fips.ap-southeast-5.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-5-fips" : { + "credentialScope" : { + "region" : "ap-southeast-5" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-southeast-5.amazonaws.com" + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "kms-fips.ap-southeast-7.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-7-fips" : { + "credentialScope" : { + "region" : "ap-southeast-7" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-southeast-7.amazonaws.com" + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "kms-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.ca-central-1.amazonaws.com" + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "kms-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1-fips" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.ca-west-1.amazonaws.com" + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "kms-fips.eu-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1-fips" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.eu-central-1.amazonaws.com" + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "kms-fips.eu-central-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-2-fips" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "deprecated" : true, + "hostname" : "kms-fips.eu-central-2.amazonaws.com" + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "kms-fips.eu-north-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-north-1-fips" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.eu-north-1.amazonaws.com" + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "kms-fips.eu-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-south-1-fips" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.eu-south-1.amazonaws.com" + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "kms-fips.eu-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-south-2-fips" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "deprecated" : true, + "hostname" : "kms-fips.eu-south-2.amazonaws.com" + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "kms-fips.eu-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-1-fips" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.eu-west-1.amazonaws.com" + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "kms-fips.eu-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-2-fips" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "deprecated" : true, + "hostname" : "kms-fips.eu-west-2.amazonaws.com" + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "kms-fips.eu-west-3.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-3-fips" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "deprecated" : true, + "hostname" : "kms-fips.eu-west-3.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "kms-fips.il-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "il-central-1-fips" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.il-central-1.amazonaws.com" + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "kms-fips.me-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "me-central-1-fips" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.me-central-1.amazonaws.com" + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "kms-fips.me-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "me-south-1-fips" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.me-south-1.amazonaws.com" + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "kms-fips.mx-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "mx-central-1-fips" : { + "credentialScope" : { + "region" : "mx-central-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.mx-central-1.amazonaws.com" + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "kms-fips.sa-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "sa-east-1-fips" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.sa-east-1.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "kms-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "kms-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "kms-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "kms-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-west-2.amazonaws.com" + } + } + }, + "lakeformation" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "lakeformation.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "lakeformation.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "lakeformation.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "lakeformation.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "lakeformation.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "lakeformation.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "lakeformation.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "lakeformation.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "lakeformation.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "lakeformation.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "lakeformation.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "lakeformation.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "lakeformation.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "lakeformation.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "lakeformation.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "lakeformation.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "lakeformation.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "lakeformation.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "lakeformation.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "lakeformation.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "lakeformation.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "lakeformation.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "lakeformation.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "lakeformation-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "lakeformation-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "lakeformation-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "lakeformation-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "lakeformation.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "lakeformation.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "lakeformation.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "lakeformation.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "lakeformation.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "lakeformation-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "lakeformation-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "lakeformation.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "lakeformation-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "lakeformation-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "lakeformation.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "lakeformation-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "lakeformation-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "lakeformation.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "lakeformation-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "lakeformation-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "lakeformation.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "lambda" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "lambda.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "lambda.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "lambda.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "lambda.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "lambda.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "lambda.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "lambda.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "lambda.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "lambda.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "lambda.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "lambda.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "lambda.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "lambda.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "lambda.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "lambda.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "lambda.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "lambda.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "lambda.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "lambda.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "lambda.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "lambda.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "lambda.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "lambda.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "lambda-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "lambda-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "lambda-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "lambda-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "lambda.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "lambda.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "lambda.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "lambda.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "lambda.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "lambda-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "lambda.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "lambda-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "lambda.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "lambda-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "lambda.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "lambda-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "lambda.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "license-manager" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "license-manager-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "license-manager-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "license-manager-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "license-manager-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "license-manager-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "license-manager-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "license-manager-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "license-manager-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "license-manager-linux-subscriptions" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "license-manager-linux-subscriptions-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "license-manager-linux-subscriptions-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "license-manager-linux-subscriptions-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "license-manager-linux-subscriptions-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "license-manager-linux-subscriptions-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "license-manager-linux-subscriptions-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "license-manager-linux-subscriptions-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "license-manager-linux-subscriptions-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "license-manager-user-subscriptions" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "license-manager-user-subscriptions-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "license-manager-user-subscriptions-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "license-manager-user-subscriptions-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "license-manager-user-subscriptions-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "license-manager-user-subscriptions-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "license-manager-user-subscriptions-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "license-manager-user-subscriptions-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "license-manager-user-subscriptions-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "lightsail" : { + "endpoints" : { + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "logs" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "logs.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "logs.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "logs.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "logs.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "logs.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "logs.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "logs.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "logs.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "logs.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "logs.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "logs.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "logs-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "logs.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "logs-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "logs.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "logs.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "logs.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "logs.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "logs.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "logs.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "logs.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "logs.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "logs.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "logs-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "logs-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "logs-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "logs-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "logs-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "logs-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "logs.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "logs.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "logs.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "logs.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "logs-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "logs.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "logs-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "logs.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "logs-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "logs.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "logs-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "logs.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "lookoutequipment" : { + "endpoints" : { + "ap-northeast-2" : { }, + "eu-west-1" : { }, + "us-east-1" : { } + } + }, + "m2" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "deprecated" : true + }, + "fips-us-east-1" : { + "deprecated" : true + }, + "fips-us-east-2" : { + "deprecated" : true + }, + "fips-us-west-1" : { + "deprecated" : true + }, + "fips-us-west-2" : { + "deprecated" : true + }, + "il-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + } + } + }, + "machinelearning" : { + "endpoints" : { + "eu-west-1" : { }, + "us-east-1" : { } + } + }, + "macie2" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "macie2.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "macie2.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "macie2.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "macie2.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "macie2.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "macie2.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "macie2.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "macie2.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "macie2.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "macie2.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "macie2.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "macie2.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "macie2.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "macie2.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "macie2.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "macie2-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "macie2-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "macie2-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "macie2-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "macie2.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "macie2.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "macie2.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "macie2-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "macie2-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "macie2.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "macie2-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "macie2-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "macie2.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "macie2-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "macie2-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "macie2.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "macie2-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "macie2-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "macie2.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "managedblockchain" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { } + } + }, + "managedblockchain-query" : { + "endpoints" : { + "us-east-1" : { } + } + }, + "marketplacecommerceanalytics" : { + "endpoints" : { + "us-east-1" : { } + } + }, + "media-pipelines-chime" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "media-pipelines-chime-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "media-pipelines-chime-fips.us-east-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "media-pipelines-chime-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "media-pipelines-chime-fips.us-west-2.amazonaws.com" + } + } + }, + "mediaconnect" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "mediaconvert" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "mediaconvert.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "mediaconvert.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "mediaconvert.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "mediaconvert.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "mediaconvert.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "mediaconvert.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "mediaconvert.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "mediaconvert.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "mediaconvert-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "mediaconvert-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "mediaconvert.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "mediaconvert.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "mediaconvert.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "mediaconvert.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "mediaconvert.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "mediaconvert.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "mediaconvert-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "mediaconvert-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "mediaconvert-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "mediaconvert-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "mediaconvert-fips.us-west-2.amazonaws.com" + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "mediaconvert.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "mediaconvert.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "mediaconvert-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "mediaconvert-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "mediaconvert.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "mediaconvert-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "mediaconvert-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "mediaconvert.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "mediaconvert-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "mediaconvert-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "mediaconvert.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "mediaconvert-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "mediaconvert-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "mediaconvert.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "medialive" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "medialive-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "medialive-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "medialive-fips.us-west-2.amazonaws.com" + }, + "me-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "medialive-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "medialive-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "medialive-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "mediapackage" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "mediapackage-vod" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "mediapackagev2" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "mediapackagev2-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "mediapackagev2-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "mediapackagev2-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "mediapackagev2-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "mediapackagev2-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "mediapackagev2-fips.us-west-2.amazonaws.com" + }, + "me-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "mediapackagev2-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "mediapackagev2-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "mediapackagev2-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "mediapackagev2-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "mediastore" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "meetings-chime" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "meetings-chime-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "meetings-chime-fips.ca-central-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-west-2" : { }, + "il-central-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "meetings-chime-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "meetings-chime-fips.us-east-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "meetings-chime-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "meetings-chime-fips.us-west-2.amazonaws.com" + } + } + }, + "memory-db" : { + "endpoints" : { + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "memory-db-fips.us-west-1.amazonaws.com" + }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "messaging-chime" : { + "endpoints" : { + "eu-central-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "messaging-chime-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "messaging-chime-fips.us-east-1.amazonaws.com" + } + } + }, + "metering.marketplace" : { + "defaults" : { + "credentialScope" : { + "service" : "aws-marketplace" + } + }, + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "metering-marketplace.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "metering-marketplace.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "metering-marketplace.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "metering-marketplace.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "metering-marketplace.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "metering-marketplace.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "metering-marketplace.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "metering-marketplace.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "metering-marketplace.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "metering-marketplace.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "metering-marketplace.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "metering-marketplace.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "metrics.sagemaker" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "metrics-fips.sagemaker.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "metrics-fips.sagemaker.ca-central-1.amazonaws.com" + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "metrics-fips.sagemaker.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1-fips" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "metrics-fips.sagemaker.ca-west-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "metrics-fips.sagemaker.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "metrics-fips.sagemaker.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "metrics-fips.sagemaker.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "metrics-fips.sagemaker.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "metrics-fips.sagemaker.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "metrics-fips.sagemaker.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "metrics-fips.sagemaker.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "metrics-fips.sagemaker.us-west-2.amazonaws.com" + } + } + }, + "mgh" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "mgn" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "mgn-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "mgn-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "mgn-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "mgn-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "migrationhub-orchestrator" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "migrationhub-strategy" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "mobileanalytics" : { + "endpoints" : { + "us-east-1" : { } + } + }, + "models-v2-lex" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "models.lex" : { + "defaults" : { + "credentialScope" : { + "service" : "lex" + }, + "variants" : [ { + "hostname" : "models-fips.lex.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "models-fips.lex.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "models-fips.lex.us-east-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "models-fips.lex.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "models-fips.lex.us-west-2.amazonaws.com" + } + } + }, + "monitoring" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "monitoring-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "monitoring-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "monitoring-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "monitoring-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "monitoring-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "monitoring-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "monitoring-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "monitoring-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "mq" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "mq-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "mq-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "mq-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "mq-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "mq-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "mq-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "mq-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "mq-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "mturk-requester" : { + "endpoints" : { + "sandbox" : { + "hostname" : "mturk-requester-sandbox.us-east-1.amazonaws.com" + }, + "us-east-1" : { } + }, + "isRegionalized" : false + }, + "neptune" : { + "endpoints" : { + "ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "hostname" : "rds.ap-east-1.amazonaws.com" + }, + "ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "rds.ap-northeast-1.amazonaws.com" + }, + "ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "rds.ap-northeast-2.amazonaws.com" + }, + "ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "rds.ap-south-1.amazonaws.com" + }, + "ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "rds.ap-southeast-1.amazonaws.com" + }, + "ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "rds.ap-southeast-2.amazonaws.com" + }, + "ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "rds.ca-central-1.amazonaws.com" + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "rds.eu-central-1.amazonaws.com" + }, + "eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "hostname" : "rds.eu-north-1.amazonaws.com" + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "rds.eu-west-1.amazonaws.com" + }, + "eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "rds.eu-west-2.amazonaws.com" + }, + "eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "hostname" : "rds.eu-west-3.amazonaws.com" + }, + "me-south-1" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "hostname" : "rds.me-south-1.amazonaws.com" + }, + "sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "rds.sa-east-1.amazonaws.com" + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "rds.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "rds.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "rds.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "rds.us-west-2.amazonaws.com" + } + } + }, + "network-firewall" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "network-firewall-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "network-firewall-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "network-firewall-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "network-firewall-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "network-firewall-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "network-firewall-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "network-firewall-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "network-firewall-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "network-firewall-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "network-firewall-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "networkmanager" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "networkmanager.us-west-2.amazonaws.com", + "variants" : [ { + "hostname" : "networkmanager-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "networkmanager-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "networkmanager.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-aws-global" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "networkmanager-fips.us-west-2.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "notifications" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { + "hostname" : "notifications.af-south-1.api.aws" + }, + "ap-east-1" : { + "hostname" : "notifications.ap-east-1.api.aws" + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "hostname" : "notifications.ap-northeast-1.api.aws" + }, + "ap-northeast-2" : { + "hostname" : "notifications.ap-northeast-2.api.aws" + }, + "ap-northeast-3" : { + "hostname" : "notifications.ap-northeast-3.api.aws" + }, + "ap-south-1" : { + "hostname" : "notifications.ap-south-1.api.aws" + }, + "ap-south-2" : { + "hostname" : "notifications.ap-south-2.api.aws" + }, + "ap-southeast-1" : { + "hostname" : "notifications.ap-southeast-1.api.aws" + }, + "ap-southeast-2" : { + "hostname" : "notifications.ap-southeast-2.api.aws" + }, + "ap-southeast-3" : { + "hostname" : "notifications.ap-southeast-3.api.aws" + }, + "ap-southeast-4" : { + "hostname" : "notifications.ap-southeast-4.api.aws" + }, + "ap-southeast-5" : { + "hostname" : "notifications.ap-southeast-5.api.aws" + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "hostname" : "notifications.ap-southeast-7.api.aws" + }, + "ca-central-1" : { + "hostname" : "notifications.ca-central-1.api.aws" + }, + "ca-west-1" : { + "hostname" : "notifications.ca-west-1.api.aws" + }, + "eu-central-1" : { + "hostname" : "notifications.eu-central-1.api.aws" + }, + "eu-central-2" : { + "hostname" : "notifications.eu-central-2.api.aws" + }, + "eu-north-1" : { + "hostname" : "notifications.eu-north-1.api.aws" + }, + "eu-south-1" : { + "hostname" : "notifications.eu-south-1.api.aws" + }, + "eu-south-2" : { + "hostname" : "notifications.eu-south-2.api.aws" + }, + "eu-west-1" : { + "hostname" : "notifications.eu-west-1.api.aws" + }, + "eu-west-2" : { + "hostname" : "notifications.eu-west-2.api.aws" + }, + "eu-west-3" : { + "hostname" : "notifications.eu-west-3.api.aws" + }, + "il-central-1" : { + "hostname" : "notifications.il-central-1.api.aws" + }, + "me-central-1" : { + "hostname" : "notifications.me-central-1.api.aws" + }, + "me-south-1" : { + "hostname" : "notifications.me-south-1.api.aws" + }, + "mx-central-1" : { + "hostname" : "notifications.mx-central-1.api.aws" + }, + "sa-east-1" : { + "hostname" : "notifications.sa-east-1.api.aws" + }, + "us-east-1" : { + "hostname" : "notifications.us-east-1.api.aws" + }, + "us-east-2" : { + "hostname" : "notifications.us-east-2.api.aws" + }, + "us-west-1" : { + "hostname" : "notifications.us-west-1.api.aws" + }, + "us-west-2" : { + "hostname" : "notifications.us-west-2.api.aws" + } + } + }, + "notifications-contacts" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "notifications-contacts.us-east-1.api.aws" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "nova-act" : { + "endpoints" : { + "us-east-1" : { } + } + }, + "oam" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "oidc" : { + "endpoints" : { + "af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "hostname" : "oidc.af-south-1.amazonaws.com" + }, + "ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "hostname" : "oidc.ap-east-1.amazonaws.com" + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "oidc.ap-northeast-1.amazonaws.com" + }, + "ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "oidc.ap-northeast-2.amazonaws.com" + }, + "ap-northeast-3" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "hostname" : "oidc.ap-northeast-3.amazonaws.com" + }, + "ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "oidc.ap-south-1.amazonaws.com" + }, + "ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "oidc.ap-south-2.amazonaws.com" + }, + "ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "oidc.ap-southeast-1.amazonaws.com" + }, + "ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "oidc.ap-southeast-2.amazonaws.com" + }, + "ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "hostname" : "oidc.ap-southeast-3.amazonaws.com" + }, + "ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "hostname" : "oidc.ap-southeast-4.amazonaws.com" + }, + "ap-southeast-5" : { + "credentialScope" : { + "region" : "ap-southeast-5" + }, + "hostname" : "oidc.ap-southeast-5.amazonaws.com" + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "oidc.ca-central-1.amazonaws.com" + }, + "ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "hostname" : "oidc.ca-west-1.amazonaws.com" + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "oidc.eu-central-1.amazonaws.com" + }, + "eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "oidc.eu-central-2.amazonaws.com" + }, + "eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "hostname" : "oidc.eu-north-1.amazonaws.com" + }, + "eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "hostname" : "oidc.eu-south-1.amazonaws.com" + }, + "eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "oidc.eu-south-2.amazonaws.com" + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "oidc.eu-west-1.amazonaws.com" + }, + "eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "oidc.eu-west-2.amazonaws.com" + }, + "eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "hostname" : "oidc.eu-west-3.amazonaws.com" + }, + "il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "oidc.il-central-1.amazonaws.com" + }, + "me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "hostname" : "oidc.me-central-1.amazonaws.com" + }, + "me-south-1" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "hostname" : "oidc.me-south-1.amazonaws.com" + }, + "mx-central-1" : { }, + "sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "oidc.sa-east-1.amazonaws.com" + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "oidc.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "oidc.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "oidc.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "oidc.us-west-2.amazonaws.com" + } + } + }, + "omics" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "omics.ap-southeast-1.amazonaws.com" + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "omics.eu-central-1.amazonaws.com" + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "omics.eu-west-1.amazonaws.com" + }, + "eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "omics.eu-west-2.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "omics-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "omics-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "omics.il-central-1.amazonaws.com" + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "omics.us-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "omics-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "omics.us-west-2.amazonaws.com", + "variants" : [ { + "hostname" : "omics-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "organizations" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "organizations.us-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "organizations-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "fips-aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "organizations-fips.us-east-1.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "osis" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "outposts" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "outposts-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "outposts-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "outposts-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "outposts-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "outposts-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "outposts-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "outposts-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "outposts-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "outposts-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "outposts-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "participant.connect" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-2" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "participant.connect-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "participant.connect-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "participant.connect-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "participant.connect-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "partnercentral-channel" : { + "endpoints" : { + "us-east-1" : { } + } + }, + "personalize" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "pi" : { + "endpoints" : { + "af-south-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "pi-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "pi.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "pi-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "pi.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "pi-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "pi-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "pi-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "pi-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "pi-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "pi-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "pi-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "pi.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "pi-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "pi.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "pi-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "pi.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "pi-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "pi.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "pinpoint" : { + "defaults" : { + "credentialScope" : { + "service" : "mobiletargeting" + } + }, + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "pinpoint.ca-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "pinpoint-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "pinpoint-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "pinpoint-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "pinpoint-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "pinpoint-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "pinpoint.us-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "pinpoint-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "pinpoint.us-east-2.amazonaws.com", + "variants" : [ { + "hostname" : "pinpoint-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "pinpoint.us-west-2.amazonaws.com", + "variants" : [ { + "hostname" : "pinpoint-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "pipes" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "polly" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "polly.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "polly.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "polly.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "polly.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "polly.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "polly.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "polly.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "polly.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "polly.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "polly-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "polly-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "polly.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "polly.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "polly.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "polly.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "polly.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "polly.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "polly.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "polly-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "polly-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "polly-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "polly-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "polly-fips.us-west-2.amazonaws.com" + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "polly.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "polly.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "polly-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "polly-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "polly.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "polly-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "polly-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "polly.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "polly-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "polly-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "polly.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "polly-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "polly-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "polly.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "portal.sso" : { + "endpoints" : { + "af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "hostname" : "portal.sso.af-south-1.amazonaws.com" + }, + "ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "hostname" : "portal.sso.ap-east-1.amazonaws.com" + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "portal.sso.ap-northeast-1.amazonaws.com" + }, + "ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "portal.sso.ap-northeast-2.amazonaws.com" + }, + "ap-northeast-3" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "hostname" : "portal.sso.ap-northeast-3.amazonaws.com" + }, + "ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "portal.sso.ap-south-1.amazonaws.com" + }, + "ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "portal.sso.ap-south-2.amazonaws.com" + }, + "ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "portal.sso.ap-southeast-1.amazonaws.com" + }, + "ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "portal.sso.ap-southeast-2.amazonaws.com" + }, + "ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "hostname" : "portal.sso.ap-southeast-3.amazonaws.com" + }, + "ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "hostname" : "portal.sso.ap-southeast-4.amazonaws.com" + }, + "ap-southeast-5" : { + "credentialScope" : { + "region" : "ap-southeast-5" + }, + "hostname" : "portal.sso.ap-southeast-5.amazonaws.com" + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "portal.sso.ca-central-1.amazonaws.com" + }, + "ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "hostname" : "portal.sso.ca-west-1.amazonaws.com" + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "portal.sso.eu-central-1.amazonaws.com" + }, + "eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "portal.sso.eu-central-2.amazonaws.com" + }, + "eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "hostname" : "portal.sso.eu-north-1.amazonaws.com" + }, + "eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "hostname" : "portal.sso.eu-south-1.amazonaws.com" + }, + "eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "portal.sso.eu-south-2.amazonaws.com" + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "portal.sso.eu-west-1.amazonaws.com" + }, + "eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "portal.sso.eu-west-2.amazonaws.com" + }, + "eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "hostname" : "portal.sso.eu-west-3.amazonaws.com" + }, + "il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "portal.sso.il-central-1.amazonaws.com" + }, + "me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "hostname" : "portal.sso.me-central-1.amazonaws.com" + }, + "me-south-1" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "hostname" : "portal.sso.me-south-1.amazonaws.com" + }, + "mx-central-1" : { }, + "sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "portal.sso.sa-east-1.amazonaws.com" + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "portal.sso.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "portal.sso.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "portal.sso.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "portal.sso.us-west-2.amazonaws.com" + } + } + }, + "profile" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "profile-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-west-2" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "profile-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "profile-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "profile-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "profile-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "profile-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "proton" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "qbusiness" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { + "hostname" : "qbusiness.af-south-1.api.aws" + }, + "ap-east-1" : { + "hostname" : "qbusiness.ap-east-1.api.aws" + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "hostname" : "qbusiness.ap-northeast-1.api.aws" + }, + "ap-northeast-2" : { + "hostname" : "qbusiness.ap-northeast-2.api.aws" + }, + "ap-northeast-3" : { + "hostname" : "qbusiness.ap-northeast-3.api.aws" + }, + "ap-south-1" : { + "hostname" : "qbusiness.ap-south-1.api.aws" + }, + "ap-south-2" : { + "hostname" : "qbusiness.ap-south-2.api.aws" + }, + "ap-southeast-1" : { + "hostname" : "qbusiness.ap-southeast-1.api.aws" + }, + "ap-southeast-2" : { + "hostname" : "qbusiness.ap-southeast-2.api.aws" + }, + "ap-southeast-3" : { + "hostname" : "qbusiness.ap-southeast-3.api.aws" + }, + "ap-southeast-4" : { + "hostname" : "qbusiness.ap-southeast-4.api.aws" + }, + "ap-southeast-5" : { + "hostname" : "qbusiness.ap-southeast-5.api.aws" + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "hostname" : "qbusiness.ap-southeast-7.api.aws" + }, + "ca-central-1" : { + "hostname" : "qbusiness.ca-central-1.api.aws" + }, + "ca-west-1" : { + "hostname" : "qbusiness.ca-west-1.api.aws" + }, + "eu-central-1" : { + "hostname" : "qbusiness.eu-central-1.api.aws" + }, + "eu-central-2" : { + "hostname" : "qbusiness.eu-central-2.api.aws" + }, + "eu-north-1" : { + "hostname" : "qbusiness.eu-north-1.api.aws" + }, + "eu-south-1" : { + "hostname" : "qbusiness.eu-south-1.api.aws" + }, + "eu-south-2" : { + "hostname" : "qbusiness.eu-south-2.api.aws" + }, + "eu-west-1" : { + "hostname" : "qbusiness.eu-west-1.api.aws" + }, + "eu-west-2" : { + "hostname" : "qbusiness.eu-west-2.api.aws" + }, + "eu-west-3" : { + "hostname" : "qbusiness.eu-west-3.api.aws" + }, + "il-central-1" : { + "hostname" : "qbusiness.il-central-1.api.aws" + }, + "me-central-1" : { + "hostname" : "qbusiness.me-central-1.api.aws" + }, + "me-south-1" : { + "hostname" : "qbusiness.me-south-1.api.aws" + }, + "mx-central-1" : { + "hostname" : "qbusiness.mx-central-1.api.aws" + }, + "sa-east-1" : { + "hostname" : "qbusiness.sa-east-1.api.aws" + }, + "us-east-1" : { + "hostname" : "qbusiness.us-east-1.api.aws" + }, + "us-east-2" : { + "hostname" : "qbusiness.us-east-2.api.aws" + }, + "us-west-1" : { + "hostname" : "qbusiness.us-west-1.api.aws" + }, + "us-west-2" : { + "hostname" : "qbusiness.us-west-2.api.aws" + } + } + }, + "query.timestream" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-south-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "quicksight" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "ram" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "ram-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ram-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "ram-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ram-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "ram-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "ram-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ram-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ram-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ram-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ram-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "ram-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ram-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "ram-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ram-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "ram-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ram-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "ram-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ram-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + } + } + }, + "rbin" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "rbin.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "rbin.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "rbin.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "rbin.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "rbin.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "rbin.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "rbin.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "rbin.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "rbin.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "rbin.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "rbin.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "rbin.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "rbin-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rbin-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rbin.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "rbin-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rbin-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rbin.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "rbin.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "rbin.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "rbin.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "rbin.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "rbin.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "rbin.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "rbin.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "rbin.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "rbin-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "rbin-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "rbin-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "rbin-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "rbin-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "rbin-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "rbin.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "rbin.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "rbin.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "rbin.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "rbin-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rbin-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rbin.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "rbin-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rbin-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rbin.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "rbin-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rbin-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rbin.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "rbin-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rbin-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rbin.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "rds" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "rds-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "rds-fips.ca-central-1.amazonaws.com" + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "rds-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1-fips" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "rds-fips.ca-west-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "rds-fips.ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "rds-fips.ca-central-1.amazonaws.com" + }, + "rds-fips.ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "rds-fips.ca-west-1.amazonaws.com" + }, + "rds-fips.us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "rds-fips.us-east-1.amazonaws.com" + }, + "rds-fips.us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "rds-fips.us-east-2.amazonaws.com" + }, + "rds-fips.us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "rds-fips.us-west-1.amazonaws.com" + }, + "rds-fips.us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "rds-fips.us-west-2.amazonaws.com" + }, + "rds.ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "rds-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "rds.ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "rds-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "rds.us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "rds-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "rds.us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "rds-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "rds.us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "rds-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "rds.us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "rds-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "sa-east-1" : { }, + "us-east-1" : { + "sslCommonName" : "{service}.{dnsSuffix}", + "variants" : [ { + "hostname" : "rds-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "rds-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "rds-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "rds-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "rds-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "rds-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "rds-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "rds-fips.us-west-2.amazonaws.com" + } + } + }, + "rds-data" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "rds-data-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "rds-data-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "rds-data-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "rds-data-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "rds-data-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "rds-data-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "rds-data-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "rds-data-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "redshift" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "redshift-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "redshift-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "redshift-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "redshift-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "redshift-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "redshift-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "redshift-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "redshift-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "redshift-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "redshift-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "redshift-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "redshift-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "redshift-serverless" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "redshift-serverless-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "redshift-serverless-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "redshift-serverless-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "redshift-serverless-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "redshift-serverless-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "redshift-serverless-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "redshift-serverless-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "redshift-serverless-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "redshift-serverless-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "redshift-serverless-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "rekognition" : { + "endpoints" : { + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "rekognition.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "rekognition.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "rekognition.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "rekognition.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "rekognition.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "rekognition-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rekognition-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rekognition.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "rekognition-fips.ca-central-1.amazonaws.com" + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "rekognition.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "rekognition.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "rekognition.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "rekognition.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "rekognition.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "rekognition-fips.ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "rekognition-fips.ca-central-1.amazonaws.com" + }, + "rekognition-fips.us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "rekognition-fips.us-east-1.amazonaws.com" + }, + "rekognition-fips.us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "rekognition-fips.us-east-2.amazonaws.com" + }, + "rekognition-fips.us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "rekognition-fips.us-west-1.amazonaws.com" + }, + "rekognition-fips.us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "rekognition-fips.us-west-2.amazonaws.com" + }, + "rekognition.ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "rekognition-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "rekognition.us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "rekognition-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "rekognition.us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "rekognition-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "rekognition.us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "rekognition-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "rekognition.us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "rekognition-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "rekognition-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rekognition-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rekognition.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "rekognition-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "rekognition-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rekognition-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rekognition.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "rekognition-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "rekognition-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rekognition-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rekognition.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "rekognition-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "rekognition-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rekognition-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rekognition.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "rekognition-fips.us-west-2.amazonaws.com" + } + } + }, + "resiliencehub" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "resiliencehub.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "resiliencehub.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "resiliencehub.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "resiliencehub.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "resiliencehub.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "resiliencehub.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "resiliencehub.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "resiliencehub.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "resiliencehub.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "resiliencehub.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "resiliencehub.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "resiliencehub.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "resiliencehub.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "resiliencehub.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "resiliencehub.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "resiliencehub.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "resiliencehub.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "resiliencehub.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "resiliencehub.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "resiliencehub.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "resource-explorer-2" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "resource-explorer-2-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "resource-explorer-2-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "resource-explorer-2-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "resource-explorer-2-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "resource-explorer-2-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "resource-explorer-2-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "resource-explorer-2-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "resource-explorer-2-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "resource-explorer-2-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "resource-explorer-2-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "resource-explorer-2-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "resource-explorer-2-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "resource-explorer-2-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "resource-explorer-2-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "resource-explorer-2-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "resource-explorer-2-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "resource-explorer-2-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "resource-explorer-2-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + } + } + }, + "resource-groups" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "resource-groups-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "resource-groups-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "resource-groups-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "resource-groups-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "resource-groups-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "resource-groups-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "resource-groups-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "resource-groups-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "rolesanywhere" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "rolesanywhere-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "rolesanywhere-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "rolesanywhere-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "rolesanywhere-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "rolesanywhere-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "rolesanywhere-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "rolesanywhere-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "rolesanywhere-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "route53" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "route53.amazonaws.com", + "variants" : [ { + "hostname" : "route53-fips.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "fips-aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "route53-fips.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "route53-recovery-control-config" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "route53-recovery-control-config.us-west-2.amazonaws.com" + } + } + }, + "route53domains" : { + "endpoints" : { + "us-east-1" : { } + } + }, + "route53profiles" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "route53profiles.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "route53profiles.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "route53profiles.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "route53profiles.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "route53profiles.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "route53profiles.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "route53profiles.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "route53profiles.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "route53profiles.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "route53profiles.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "route53profiles.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "route53profiles-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53profiles.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "route53profiles-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53profiles.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "route53profiles.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "route53profiles.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "route53profiles.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "route53profiles.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "route53profiles.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "route53profiles.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "route53profiles.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "route53profiles.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "route53profiles.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "route53profiles.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "route53profiles.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "route53profiles.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "route53profiles-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53profiles.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "route53profiles-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53profiles.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "route53profiles-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53profiles.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "route53profiles-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53profiles.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "route53resolver" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "route53resolver.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "route53resolver.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "route53resolver.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "route53resolver.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "route53resolver.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "route53resolver.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "route53resolver.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "route53resolver.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "route53resolver.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "route53resolver.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "route53resolver.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "route53resolver.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "route53resolver.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "route53resolver-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "route53resolver-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53resolver.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "route53resolver-fips.ca-central-1.amazonaws.com" + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "route53resolver-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "route53resolver-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53resolver.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1-fips" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "route53resolver-fips.ca-west-1.amazonaws.com" + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "route53resolver.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "route53resolver.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "route53resolver.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "route53resolver.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "route53resolver.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "route53resolver.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "route53resolver.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "route53resolver.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "route53resolver.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "route53resolver.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "route53resolver.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "route53resolver.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "route53resolver.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "route53resolver-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "route53resolver-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53resolver.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "route53resolver-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "route53resolver-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "route53resolver-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53resolver.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "route53resolver-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "route53resolver-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "route53resolver-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53resolver.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "route53resolver-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "route53resolver-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "route53resolver-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53resolver.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "route53resolver-fips.us-west-2.amazonaws.com" + } + } + }, + "rum" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "runtime-v2-lex" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "runtime.lex" : { + "defaults" : { + "credentialScope" : { + "service" : "lex" + }, + "variants" : [ { + "hostname" : "runtime-fips.lex.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "runtime-fips.lex.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "runtime-fips.lex.us-east-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "runtime-fips.lex.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "runtime-fips.lex.us-west-2.amazonaws.com" + } + } + }, + "runtime.sagemaker" : { + "defaults" : { + "variants" : [ { + "hostname" : "runtime-fips.sagemaker.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "runtime-fips.sagemaker.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "runtime-fips.sagemaker.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "runtime-fips.sagemaker.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "runtime-fips.sagemaker.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "runtime-fips.sagemaker.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "runtime-fips.sagemaker.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "runtime-fips.sagemaker.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "runtime-fips.sagemaker.us-west-2.amazonaws.com" + } + } + }, + "s3" : { + "defaults" : { + "protocols" : [ "http", "https" ], + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "dnsSuffix" : "amazonaws.com", + "hostname" : "{service}-fips.dualstack.{region}.{dnsSuffix}", + "tags" : [ "dualstack", "fips" ] + }, { + "dnsSuffix" : "amazonaws.com", + "hostname" : "{service}.dualstack.{region}.{dnsSuffix}", + "tags" : [ "dualstack" ] + } ] + }, + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "s3.dualstack.af-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "s3.dualstack.ap-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "hostname" : "s3.ap-northeast-1.amazonaws.com", + "signatureVersions" : [ "s3", "s3v4" ], + "variants" : [ { + "hostname" : "s3.dualstack.ap-northeast-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "s3.dualstack.ap-northeast-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "s3.dualstack.ap-northeast-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "s3.dualstack.ap-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "s3.dualstack.ap-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "hostname" : "s3.ap-southeast-1.amazonaws.com", + "signatureVersions" : [ "s3", "s3v4" ], + "variants" : [ { + "hostname" : "s3.dualstack.ap-southeast-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "hostname" : "s3.ap-southeast-2.amazonaws.com", + "signatureVersions" : [ "s3", "s3v4" ], + "variants" : [ { + "hostname" : "s3.dualstack.ap-southeast-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "s3.dualstack.ap-southeast-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "s3.dualstack.ap-southeast-4.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "s3.dualstack.ap-southeast-5.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "s3.dualstack.ap-southeast-7.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "s3.amazonaws.com", + "signatureVersions" : [ "s3", "s3v4" ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "s3-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3-fips.dualstack.ca-central-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3.dualstack.ca-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "s3-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3-fips.dualstack.ca-west-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3.dualstack.ca-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "s3.dualstack.eu-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "s3.dualstack.eu-central-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "s3.dualstack.eu-north-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "s3.dualstack.eu-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "s3.dualstack.eu-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "hostname" : "s3.eu-west-1.amazonaws.com", + "signatureVersions" : [ "s3", "s3v4" ], + "variants" : [ { + "hostname" : "s3.dualstack.eu-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "s3.dualstack.eu-west-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "s3.dualstack.eu-west-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "s3-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "s3-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "s3-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "s3-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "s3-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "s3-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "s3.dualstack.il-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "s3.dualstack.me-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "s3.dualstack.me-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "s3.dualstack.mx-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "s3-external-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "s3-external-1.amazonaws.com", + "signatureVersions" : [ "s3", "s3v4" ] + }, + "sa-east-1" : { + "hostname" : "s3.sa-east-1.amazonaws.com", + "signatureVersions" : [ "s3", "s3v4" ], + "variants" : [ { + "hostname" : "s3.dualstack.sa-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "hostname" : "s3.us-east-1.amazonaws.com", + "signatureVersions" : [ "s3", "s3v4" ], + "variants" : [ { + "hostname" : "s3-fips.dualstack.us-east-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3.dualstack.us-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "s3-fips.dualstack.us-east-2.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3.dualstack.us-east-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "hostname" : "s3.us-west-1.amazonaws.com", + "signatureVersions" : [ "s3", "s3v4" ], + "variants" : [ { + "hostname" : "s3-fips.dualstack.us-west-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3.dualstack.us-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "hostname" : "s3.us-west-2.amazonaws.com", + "signatureVersions" : [ "s3", "s3v4" ], + "variants" : [ { + "hostname" : "s3-fips.dualstack.us-west-2.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3.dualstack.us-west-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + } + }, + "isRegionalized" : true, + "partitionEndpoint" : "aws-global" + }, + "s3-control" : { + "defaults" : { + "protocols" : [ "https" ], + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "dnsSuffix" : "amazonaws.com", + "hostname" : "{service}-fips.dualstack.{region}.{dnsSuffix}", + "tags" : [ "dualstack", "fips" ] + }, { + "dnsSuffix" : "amazonaws.com", + "hostname" : "{service}.dualstack.{region}.{dnsSuffix}", + "tags" : [ "dualstack" ] + } ] + }, + "endpoints" : { + "af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "hostname" : "s3-control.af-south-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.af-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "hostname" : "s3-control.ap-east-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.ap-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "s3-control.ap-northeast-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.ap-northeast-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "s3-control.ap-northeast-2.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.ap-northeast-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "hostname" : "s3-control.ap-northeast-3.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.ap-northeast-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "s3-control.ap-south-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.ap-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "s3-control.ap-south-2.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.ap-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "s3-control.ap-southeast-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.ap-southeast-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "s3-control.ap-southeast-2.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.ap-southeast-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "hostname" : "s3-control.ap-southeast-3.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.ap-southeast-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "hostname" : "s3-control.ap-southeast-4.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.ap-southeast-4.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "s3-control.ca-central-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3-control-fips.dualstack.ca-central-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-control.dualstack.ca-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "s3-control-fips.ca-central-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ] + }, + "ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "hostname" : "s3-control.ca-west-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3-control-fips.dualstack.ca-west-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-control.dualstack.ca-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1-fips" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "s3-control-fips.ca-west-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ] + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "s3-control.eu-central-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.eu-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "s3-control.eu-central-2.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.eu-central-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "hostname" : "s3-control.eu-north-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.eu-north-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "hostname" : "s3-control.eu-south-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.eu-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "s3-control.eu-south-2.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.eu-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "s3-control.eu-west-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.eu-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "s3-control.eu-west-2.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.eu-west-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "hostname" : "s3-control.eu-west-3.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.eu-west-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "s3-control.il-central-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.il-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "hostname" : "s3-control.me-central-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.me-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "hostname" : "s3-control.me-south-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.me-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "s3-control.sa-east-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.sa-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "s3-control.us-east-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control-fips.dualstack.us-east-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-control-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3-control.dualstack.us-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "s3-control-fips.us-east-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ] + }, + "us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "s3-control.us-east-2.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control-fips.dualstack.us-east-2.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-control-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3-control.dualstack.us-east-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "s3-control-fips.us-east-2.amazonaws.com", + "signatureVersions" : [ "s3v4" ] + }, + "us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "s3-control.us-west-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control-fips.dualstack.us-west-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-control-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3-control.dualstack.us-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "s3-control-fips.us-west-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ] + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "s3-control.us-west-2.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control-fips.dualstack.us-west-2.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-control-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3-control.dualstack.us-west-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "s3-control-fips.us-west-2.amazonaws.com", + "signatureVersions" : [ "s3v4" ] + } + } + }, + "s3-outposts" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "deprecated" : true + }, + "fips-us-east-1" : { + "deprecated" : true + }, + "fips-us-east-2" : { + "deprecated" : true + }, + "fips-us-west-1" : { + "deprecated" : true + }, + "fips-us-west-2" : { + "deprecated" : true + }, + "il-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + } + } + }, + "sagemaker-geospatial" : { + "endpoints" : { + "us-west-2" : { } + } + }, + "savingsplans" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "savingsplans.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "scheduler" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "schemas" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "sdb" : { + "defaults" : { + "protocols" : [ "http", "https" ], + "signatureVersions" : [ "v2" ] + }, + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-west-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "hostname" : "sdb.amazonaws.com" + }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "secretsmanager" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "deprecated" : true + }, + "ca-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "ca-west-1-fips" : { + "deprecated" : true + }, + "eu-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "deprecated" : true + }, + "us-east-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "deprecated" : true + }, + "us-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "deprecated" : true + }, + "us-west-2" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "deprecated" : true + } + } + }, + "securityhub" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "securityhub.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "securityhub.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "securityhub.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "securityhub.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "securityhub.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "securityhub.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "securityhub.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "securityhub.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "securityhub.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "securityhub.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "securityhub.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "securityhub.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "securityhub.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "securityhub.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "securityhub.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "securityhub.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "securityhub.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "securityhub.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "securityhub.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "securityhub.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "securityhub.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "securityhub.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "securityhub.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "securityhub-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "securityhub-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "securityhub-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "securityhub-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "securityhub.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "securityhub.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "securityhub.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "securityhub.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "securityhub.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "securityhub-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "securityhub.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "securityhub-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "securityhub.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "securityhub-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "securityhub.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "securityhub-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "securityhub.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "securitylake" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "securitylake-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "securitylake-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "securitylake-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "securitylake-fips.us-west-2.amazonaws.com" + }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "securitylake-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "securitylake-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "securitylake-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "securitylake-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "securitylake-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "securitylake-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "securitylake-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "securitylake-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + } + } + }, + "serverlessrepo" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "ap-east-1" : { + "protocols" : [ "https" ] + }, + "ap-northeast-1" : { + "protocols" : [ "https" ] + }, + "ap-northeast-2" : { + "protocols" : [ "https" ] + }, + "ap-south-1" : { + "protocols" : [ "https" ] + }, + "ap-southeast-1" : { + "protocols" : [ "https" ] + }, + "ap-southeast-2" : { + "protocols" : [ "https" ] + }, + "ca-central-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "serverlessrepo-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "serverlessrepo-fips.ca-central-1.amazonaws.com" + }, + "eu-central-1" : { + "protocols" : [ "https" ] + }, + "eu-north-1" : { + "protocols" : [ "https" ] + }, + "eu-west-1" : { + "protocols" : [ "https" ] + }, + "eu-west-2" : { + "protocols" : [ "https" ] + }, + "eu-west-3" : { + "protocols" : [ "https" ] + }, + "me-south-1" : { + "protocols" : [ "https" ] + }, + "sa-east-1" : { + "protocols" : [ "https" ] + }, + "us-east-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "serverlessrepo-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "serverlessrepo-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "serverlessrepo-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "serverlessrepo-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "serverlessrepo-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "serverlessrepo-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "serverlessrepo-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "serverlessrepo-fips.us-west-2.amazonaws.com" + } + } + }, + "servicecatalog" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-6" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "servicecatalog-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "servicecatalog-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "servicecatalog-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "servicecatalog-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "servicecatalog-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "servicecatalog-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "servicecatalog-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "servicecatalog-fips.us-west-2.amazonaws.com" + } + } + }, + "servicecatalog-appregistry" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "servicecatalog-appregistry-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "servicecatalog-appregistry-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "servicecatalog-appregistry-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "servicecatalog-appregistry-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "servicecatalog-appregistry-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "servicecatalog-appregistry-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "servicecatalog-appregistry-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "servicecatalog-appregistry-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "servicecatalog-appregistry-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "servicecatalog-appregistry-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "servicediscovery" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "servicediscovery.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "servicediscovery-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "servicediscovery.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.ca-central-1.amazonaws.com" + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "servicediscovery-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "servicediscovery.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1-fips" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.ca-west-1.amazonaws.com" + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "servicediscovery.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "servicediscovery.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "servicediscovery.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "servicediscovery.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "servicediscovery.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "servicediscovery-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "servicediscovery.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "servicediscovery-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "servicediscovery.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "servicediscovery-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "servicediscovery.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "servicediscovery-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "servicediscovery.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.us-west-2.amazonaws.com" + } + } + }, + "servicequotas" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "shield" : { + "defaults" : { + "protocols" : [ "https" ], + "sslCommonName" : "shield.us-east-1.amazonaws.com" + }, + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "shield.us-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "shield-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "fips-aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "shield-fips.us-east-1.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "signer" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-west-2.amazonaws.com" + }, + "fips-verification-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "verification.signer-fips.us-east-1.amazonaws.com" + }, + "fips-verification-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "verification.signer-fips.us-east-2.amazonaws.com" + }, + "fips-verification-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "verification.signer-fips.us-west-1.amazonaws.com" + }, + "fips-verification-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "verification.signer-fips.us-west-2.amazonaws.com" + }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "signer-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "signer-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "signer-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "signer-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "verification-af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "hostname" : "verification.signer.af-south-1.amazonaws.com" + }, + "verification-ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "hostname" : "verification.signer.ap-east-1.amazonaws.com" + }, + "verification-ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "verification.signer.ap-northeast-1.amazonaws.com" + }, + "verification-ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "verification.signer.ap-northeast-2.amazonaws.com" + }, + "verification-ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "verification.signer.ap-south-1.amazonaws.com" + }, + "verification-ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "verification.signer.ap-southeast-1.amazonaws.com" + }, + "verification-ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "verification.signer.ap-southeast-2.amazonaws.com" + }, + "verification-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "verification.signer.ca-central-1.amazonaws.com" + }, + "verification-eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "verification.signer.eu-central-1.amazonaws.com" + }, + "verification-eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "hostname" : "verification.signer.eu-north-1.amazonaws.com" + }, + "verification-eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "hostname" : "verification.signer.eu-south-1.amazonaws.com" + }, + "verification-eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "verification.signer.eu-west-1.amazonaws.com" + }, + "verification-eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "verification.signer.eu-west-2.amazonaws.com" + }, + "verification-eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "hostname" : "verification.signer.eu-west-3.amazonaws.com" + }, + "verification-me-south-1" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "hostname" : "verification.signer.me-south-1.amazonaws.com" + }, + "verification-sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "verification.signer.sa-east-1.amazonaws.com" + }, + "verification-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "verification.signer.us-east-1.amazonaws.com" + }, + "verification-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "verification.signer.us-east-2.amazonaws.com" + }, + "verification-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "verification.signer.us-west-1.amazonaws.com" + }, + "verification-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "verification.signer.us-west-2.amazonaws.com" + } + } + }, + "sms-voice" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "sms-voice.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "sms-voice.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "sms-voice.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "sms-voice.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "sms-voice.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "sms-voice.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "sms-voice.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "sms-voice.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "sms-voice.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "sms-voice.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "sms-voice-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sms-voice-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "sms-voice.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "sms-voice-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sms-voice-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "sms-voice.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "sms-voice.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "sms-voice.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "sms-voice.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "sms-voice.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "sms-voice.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "sms-voice.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "sms-voice.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "sms-voice.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "sms-voice.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "sms-voice.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "sms-voice.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "sms-voice.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "sms-voice-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sms-voice-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "sms-voice.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "sms-voice-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sms-voice-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "sms-voice.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "sms-voice-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sms-voice-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "sms-voice.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "sms-voice-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sms-voice-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "sms-voice.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "snowball" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "snowball-fips.af-south-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.af-south-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "snowball-fips.ap-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.ap-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "snowball-fips.ap-northeast-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.ap-northeast-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "snowball-fips.ap-northeast-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.ap-northeast-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "snowball-fips.ap-northeast-3.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.ap-northeast-3.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "snowball-fips.ap-south-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.ap-south-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "snowball-fips.ap-southeast-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.ap-southeast-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "snowball-fips.ap-southeast-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.ap-southeast-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "snowball-fips.ap-southeast-3.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.ap-southeast-3.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "snowball-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "snowball-fips.eu-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.eu-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "snowball-fips.eu-north-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.eu-north-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "snowball-fips.eu-south-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.eu-south-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "snowball-fips.eu-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.eu-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "snowball-fips.eu-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.eu-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "snowball-fips.eu-west-3.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.eu-west-3.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.af-south-1.amazonaws.com" + }, + "fips-ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.ap-east-1.amazonaws.com" + }, + "fips-ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.ap-northeast-1.amazonaws.com" + }, + "fips-ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "deprecated" : true, + "hostname" : "snowball-fips.ap-northeast-2.amazonaws.com" + }, + "fips-ap-northeast-3" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "deprecated" : true, + "hostname" : "snowball-fips.ap-northeast-3.amazonaws.com" + }, + "fips-ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.ap-south-1.amazonaws.com" + }, + "fips-ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.ap-southeast-1.amazonaws.com" + }, + "fips-ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "deprecated" : true, + "hostname" : "snowball-fips.ap-southeast-2.amazonaws.com" + }, + "fips-ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "deprecated" : true, + "hostname" : "snowball-fips.ap-southeast-3.amazonaws.com" + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.ca-central-1.amazonaws.com" + }, + "fips-eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.eu-central-1.amazonaws.com" + }, + "fips-eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.eu-north-1.amazonaws.com" + }, + "fips-eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.eu-south-1.amazonaws.com" + }, + "fips-eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.eu-west-1.amazonaws.com" + }, + "fips-eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "deprecated" : true, + "hostname" : "snowball-fips.eu-west-2.amazonaws.com" + }, + "fips-eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "deprecated" : true, + "hostname" : "snowball-fips.eu-west-3.amazonaws.com" + }, + "fips-il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.il-central-1.amazonaws.com" + }, + "fips-me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.me-central-1.amazonaws.com" + }, + "fips-sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.sa-east-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "snowball-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "snowball-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "snowball-fips.il-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.il-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "snowball-fips.me-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.me-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "snowball-fips.sa-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.sa-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "snowball-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "snowball-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "snowball-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "snowball-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "sns" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "sns.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "sns.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "sns.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "sns.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "sns.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "sns.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "sns.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "sns.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "sns.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "sns.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "sns.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "sns.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "sns.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "sns.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "sns-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sns.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "sns.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "sns.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "sns.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "sns.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "sns.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "sns.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "sns.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "sns.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "sns-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "sns-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "sns-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "sns-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "sns-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "sns.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "sns.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "sns.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "sns.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "sns.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "sns-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sns.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "sns-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sns.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "sns-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sns.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "sns-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sns.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "sqs" : { + "defaults" : { + "protocols" : [ "http", "https" ], + "sslCommonName" : "{region}.queue.{dnsSuffix}" + }, + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "sqs.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "sqs.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "sqs.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "sqs.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "sqs.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "sqs.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "sqs.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "sqs.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "sqs.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "sqs.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "sqs.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "sqs.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "sqs.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "sqs-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sqs.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "sqs-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sqs.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "sqs.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "sqs.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "sqs.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "sqs.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "sqs.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "sqs.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "sqs.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "sqs.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "sqs-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "sqs-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "sqs-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "sqs-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "sqs-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "sqs-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "sqs.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "sqs.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "sqs.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "sqs.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "sqs.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "sslCommonName" : "queue.{dnsSuffix}", + "variants" : [ { + "hostname" : "sqs-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sqs.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "sqs-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sqs.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "sqs-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sqs.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "sqs-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sqs.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "ssm" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "ssm-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "ssm-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "ssm-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "ssm-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ssm-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ssm-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ssm-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ssm-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "ssm-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "ssm-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "ssm-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "ssm-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "ssm-contacts" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ssm-contacts-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ssm-contacts-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ssm-contacts-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ssm-contacts-fips.us-west-2.amazonaws.com" + }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "ssm-contacts-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "ssm-contacts-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "ssm-contacts-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "ssm-contacts-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "ssm-incidents" : { + "endpoints" : { + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "ssm-incidents.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "ssm-incidents.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "ssm-incidents.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "ssm-incidents.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "ssm-incidents.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "ssm-incidents-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ssm-incidents-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ssm-incidents.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "ssm-incidents.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "ssm-incidents.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "ssm-incidents.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "ssm-incidents.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "ssm-incidents.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "ssm-incidents-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ssm-incidents-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ssm-incidents-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ssm-incidents-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ssm-incidents-fips.us-west-2.amazonaws.com" + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "ssm-incidents.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "ssm-incidents-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ssm-incidents-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ssm-incidents.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "ssm-incidents-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ssm-incidents-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ssm-incidents.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "ssm-incidents-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ssm-incidents-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ssm-incidents.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "ssm-incidents-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ssm-incidents-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ssm-incidents.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "ssm-quicksetup" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "ssm-quicksetup-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "ssm-quicksetup-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ssm-quicksetup-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ssm-quicksetup-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ssm-quicksetup-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ssm-quicksetup-fips.us-west-2.amazonaws.com" + }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "ssm-quicksetup-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "ssm-quicksetup-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "ssm-quicksetup-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "ssm-quicksetup-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "ssm-sap" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "ssm-sap.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "ssm-sap.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "ssm-sap.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "ssm-sap.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "ssm-sap.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "ssm-sap.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "ssm-sap.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "ssm-sap.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "ssm-sap.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "ssm-sap.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "ssm-sap.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "ssm-sap-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ssm-sap-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ssm-sap.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "ssm-sap.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "ssm-sap.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "ssm-sap.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "ssm-sap.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "ssm-sap.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "ssm-sap.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "ssm-sap.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "ssm-sap.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "ssm-sap-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ssm-sap-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ssm-sap-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ssm-sap-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ssm-sap-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "ssm-sap.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "ssm-sap.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "ssm-sap.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "ssm-sap.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "ssm-sap-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ssm-sap-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ssm-sap.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "ssm-sap-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ssm-sap-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ssm-sap.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "ssm-sap-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ssm-sap-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ssm-sap.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "ssm-sap-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ssm-sap-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ssm-sap.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "sso" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "states" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "states-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "states-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "states-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "states-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "states-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "states-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "states-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "states-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "states-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "states-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "states-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "states-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "storagegateway" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "storagegateway-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "storagegateway-fips.ca-central-1.amazonaws.com" + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "storagegateway-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1-fips" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "storagegateway-fips.ca-west-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "storagegateway-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "storagegateway-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "storagegateway-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "storagegateway-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "storagegateway-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "storagegateway-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "storagegateway-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "storagegateway-fips.us-west-2.amazonaws.com" + } + } + }, + "streams.dynamodb" : { + "defaults" : { + "credentialScope" : { + "service" : "dynamodb" + }, + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "local" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "localhost:8000", + "protocols" : [ "http" ] + }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "sts" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "sts.amazonaws.com" + }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "sts-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "sts-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "sts-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "sts-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "sts-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "sts-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "sts-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "sts-fips.us-west-2.amazonaws.com" + } + }, + "partitionEndpoint" : "aws-global" + }, + "support" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "support.us-east-1.amazonaws.com" + } + }, + "partitionEndpoint" : "aws-global" + }, + "supportapp" : { + "endpoints" : { + "eu-west-1" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "swf" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "swf-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "swf-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "swf-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "swf-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "swf-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "swf-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "swf-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "swf-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "swf-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "swf-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "swf-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "swf-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "synthetics" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "synthetics.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "synthetics.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "synthetics.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "synthetics.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "synthetics.ap-northeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "synthetics.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "synthetics.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "synthetics.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "synthetics.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "synthetics.ap-southeast-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "synthetics.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { + "variants" : [ { + "hostname" : "synthetics.ap-southeast-5.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "variants" : [ { + "hostname" : "synthetics.ap-southeast-7.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "synthetics-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "synthetics-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "synthetics.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "synthetics-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "synthetics-fips.ca-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "synthetics.ca-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "synthetics.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "synthetics.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "synthetics.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "synthetics.eu-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "synthetics.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "synthetics.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "synthetics.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "synthetics.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "synthetics-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "synthetics-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "synthetics-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "synthetics-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "synthetics-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "synthetics-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "variants" : [ { + "hostname" : "synthetics.il-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "synthetics.me-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "synthetics.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "mx-central-1" : { + "variants" : [ { + "hostname" : "synthetics.mx-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "synthetics.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "synthetics-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "synthetics-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "synthetics.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "synthetics-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "synthetics-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "synthetics.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "synthetics-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "synthetics-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "synthetics.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "synthetics-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "synthetics-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "synthetics.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "tagging" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "tax" : { + "endpoints" : { + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "tax.us-east-1.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "textract" : { + "endpoints" : { + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "textract.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "textract.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "textract.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "textract.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "textract-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "textract-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "textract.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "textract.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "textract.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "textract.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "textract.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "textract.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "textract-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "textract-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "textract-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "textract-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "textract-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "textract-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "textract-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "textract.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "textract-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "textract-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "textract.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "textract-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "textract-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "textract.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "textract-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "textract-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "textract.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "thinclient" : { + "endpoints" : { + "ap-south-1" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "tnb" : { + "endpoints" : { + "ap-northeast-2" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "transcribe" : { + "defaults" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "fips.transcribe.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "transcribe.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "transcribe.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "transcribe.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "transcribe.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "transcribe.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "transcribe.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "transcribe.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "fips.transcribe.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "transcribe-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "transcribe.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "transcribe.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "transcribe.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "transcribe.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "transcribe.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "transcribe.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "fips.transcribe.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "fips.transcribe.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "fips.transcribe.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "fips.transcribe.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "fips.transcribe.us-west-2.amazonaws.com" + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "transcribe.me-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "transcribe.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "fips.transcribe.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "transcribe-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "transcribe.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "fips.transcribe.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "transcribe-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "transcribe.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "fips.transcribe.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "transcribe-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "transcribe.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "fips.transcribe.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "transcribe-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "transcribe.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "transcribestreaming" : { + "endpoints" : { + "af-south-1" : { + "variants" : [ { + "hostname" : "transcribestreaming.af-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "transcribestreaming.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "transcribestreaming.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "transcribestreaming.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "transcribestreaming.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "transcribestreaming.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-5" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "transcribestreaming-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "transcribestreaming-fips.ca-central-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "transcribestreaming.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "transcribestreaming.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "transcribestreaming.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "transcribestreaming.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "transcribestreaming-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "transcribestreaming-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "transcribestreaming-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "transcribestreaming-fips.us-west-2.amazonaws.com" + }, + "mx-central-1" : { }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "transcribestreaming.sa-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "transcribestreaming-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "transcribestreaming-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "transcribestreaming.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "transcribestreaming-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "transcribestreaming-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "transcribestreaming.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "transcribestreaming-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "transcribestreaming-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "transcribestreaming.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "transfer" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "transfer-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "transfer-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "transfer-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "transfer-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "transfer-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "transfer-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "transfer-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "transfer-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "transfer-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "transfer-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "transfer-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "transfer-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "translate" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "ap-east-1" : { + "variants" : [ { + "hostname" : "translate.ap-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "translate.ap-northeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "translate.ap-northeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "translate.ap-south-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "translate.ap-southeast-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "translate.ap-southeast-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "translate.ca-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "translate.eu-central-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "translate.eu-north-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "translate.eu-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "translate.eu-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "translate.eu-west-3.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "translate-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "translate-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "translate.us-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "translate-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "translate-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "translate-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "translate.us-east-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "translate-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "translate-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "translate-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "translate.us-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "translate-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "translate-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "translate-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "translate.us-west-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "translate-fips.us-west-2.amazonaws.com" + } + } + }, + "trustedadvisor" : { + "endpoints" : { + "ap-northeast-2" : { }, + "ap-southeast-2" : { }, + "eu-west-1" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "trustedadvisor-fips.us-east-1.api.aws" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "trustedadvisor-fips.us-east-2.api.aws" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "trustedadvisor-fips.us-west-2.api.aws" + }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "verifiedpermissions" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "verifiedpermissions-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "variants" : [ { + "hostname" : "verifiedpermissions-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "verifiedpermissions-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "verifiedpermissions-fips.ca-west-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "verifiedpermissions-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "verifiedpermissions-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "verifiedpermissions-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "verifiedpermissions-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "verifiedpermissions-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "verifiedpermissions-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "verifiedpermissions-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "verifiedpermissions-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "voice-chime" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "voice-chime-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "voice-chime-fips.ca-central-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "voice-chime-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "voice-chime-fips.us-east-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "voice-chime-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "voice-chime-fips.us-west-2.amazonaws.com" + } + } + }, + "voiceid" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "voiceid-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-west-2" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "voiceid-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "voiceid-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "voiceid-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "voiceid-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "voiceid-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "vpc-lattice" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "waf" : { + "endpoints" : { + "aws" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "waf-fips.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "aws-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "waf-fips.amazonaws.com" + }, + "aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "waf.amazonaws.com", + "variants" : [ { + "hostname" : "waf-fips.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "aws-global-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "waf-fips.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "waf-regional" : { + "endpoints" : { + "af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "hostname" : "waf-regional.af-south-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.af-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "hostname" : "waf-regional.ap-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "waf-regional.ap-northeast-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-northeast-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "waf-regional.ap-northeast-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-northeast-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-northeast-3" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "hostname" : "waf-regional.ap-northeast-3.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-northeast-3.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "waf-regional.ap-south-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "waf-regional.ap-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "waf-regional.ap-southeast-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-southeast-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "waf-regional.ap-southeast-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-southeast-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "hostname" : "waf-regional.ap-southeast-3.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-southeast-3.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "hostname" : "waf-regional.ap-southeast-4.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-southeast-4.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "waf-regional.ca-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "waf-regional.eu-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.eu-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "waf-regional.eu-central-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.eu-central-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "hostname" : "waf-regional.eu-north-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.eu-north-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "hostname" : "waf-regional.eu-south-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.eu-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "waf-regional.eu-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.eu-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "waf-regional.eu-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.eu-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "waf-regional.eu-west-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.eu-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "hostname" : "waf-regional.eu-west-3.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.eu-west-3.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "fips-af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.af-south-1.amazonaws.com" + }, + "fips-ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-east-1.amazonaws.com" + }, + "fips-ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-northeast-1.amazonaws.com" + }, + "fips-ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-northeast-2.amazonaws.com" + }, + "fips-ap-northeast-3" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-northeast-3.amazonaws.com" + }, + "fips-ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-south-1.amazonaws.com" + }, + "fips-ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-south-2.amazonaws.com" + }, + "fips-ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-southeast-1.amazonaws.com" + }, + "fips-ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-southeast-2.amazonaws.com" + }, + "fips-ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-southeast-3.amazonaws.com" + }, + "fips-ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-southeast-4.amazonaws.com" + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ca-central-1.amazonaws.com" + }, + "fips-eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.eu-central-1.amazonaws.com" + }, + "fips-eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.eu-central-2.amazonaws.com" + }, + "fips-eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.eu-north-1.amazonaws.com" + }, + "fips-eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.eu-south-1.amazonaws.com" + }, + "fips-eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.eu-south-2.amazonaws.com" + }, + "fips-eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.eu-west-1.amazonaws.com" + }, + "fips-eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.eu-west-2.amazonaws.com" + }, + "fips-eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.eu-west-3.amazonaws.com" + }, + "fips-il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.il-central-1.amazonaws.com" + }, + "fips-me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.me-central-1.amazonaws.com" + }, + "fips-me-south-1" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.me-south-1.amazonaws.com" + }, + "fips-sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.sa-east-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "waf-regional.il-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.il-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "hostname" : "waf-regional.me-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.me-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "me-south-1" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "hostname" : "waf-regional.me-south-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.me-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "waf-regional.sa-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.sa-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "waf-regional.us-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "waf-regional.us-east-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "waf-regional.us-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "waf-regional.us-west-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "wafv2" : { + "endpoints" : { + "af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "hostname" : "wafv2.af-south-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.af-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "hostname" : "wafv2.ap-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-east-2" : { }, + "ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "hostname" : "wafv2.ap-northeast-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-northeast-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "hostname" : "wafv2.ap-northeast-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-northeast-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-northeast-3" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "hostname" : "wafv2.ap-northeast-3.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-northeast-3.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "hostname" : "wafv2.ap-south-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "wafv2.ap-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "wafv2.ap-southeast-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-southeast-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "hostname" : "wafv2.ap-southeast-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-southeast-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "hostname" : "wafv2.ap-southeast-3.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-southeast-3.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "hostname" : "wafv2.ap-southeast-4.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-southeast-4.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-5" : { + "credentialScope" : { + "region" : "ap-southeast-5" + }, + "hostname" : "wafv2.ap-southeast-5.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-southeast-5.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { + "credentialScope" : { + "region" : "ap-southeast-7" + }, + "hostname" : "wafv2.ap-southeast-7.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-southeast-7.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "hostname" : "wafv2.ca-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "hostname" : "wafv2.ca-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ca-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "wafv2.eu-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.eu-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "wafv2.eu-central-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.eu-central-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "hostname" : "wafv2.eu-north-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.eu-north-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "hostname" : "wafv2.eu-south-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.eu-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "wafv2.eu-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.eu-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "wafv2.eu-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.eu-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "wafv2.eu-west-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.eu-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "hostname" : "wafv2.eu-west-3.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.eu-west-3.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "fips-af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.af-south-1.amazonaws.com" + }, + "fips-ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-east-1.amazonaws.com" + }, + "fips-ap-northeast-1" : { + "credentialScope" : { + "region" : "ap-northeast-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-northeast-1.amazonaws.com" + }, + "fips-ap-northeast-2" : { + "credentialScope" : { + "region" : "ap-northeast-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-northeast-2.amazonaws.com" + }, + "fips-ap-northeast-3" : { + "credentialScope" : { + "region" : "ap-northeast-3" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-northeast-3.amazonaws.com" + }, + "fips-ap-south-1" : { + "credentialScope" : { + "region" : "ap-south-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-south-1.amazonaws.com" + }, + "fips-ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-south-2.amazonaws.com" + }, + "fips-ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-southeast-1.amazonaws.com" + }, + "fips-ap-southeast-2" : { + "credentialScope" : { + "region" : "ap-southeast-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-southeast-2.amazonaws.com" + }, + "fips-ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-southeast-3.amazonaws.com" + }, + "fips-ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-southeast-4.amazonaws.com" + }, + "fips-ap-southeast-5" : { + "credentialScope" : { + "region" : "ap-southeast-5" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-southeast-5.amazonaws.com" + }, + "fips-ap-southeast-7" : { + "credentialScope" : { + "region" : "ap-southeast-7" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-southeast-7.amazonaws.com" + }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ca-central-1.amazonaws.com" + }, + "fips-ca-west-1" : { + "credentialScope" : { + "region" : "ca-west-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ca-west-1.amazonaws.com" + }, + "fips-eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.eu-central-1.amazonaws.com" + }, + "fips-eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.eu-central-2.amazonaws.com" + }, + "fips-eu-north-1" : { + "credentialScope" : { + "region" : "eu-north-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.eu-north-1.amazonaws.com" + }, + "fips-eu-south-1" : { + "credentialScope" : { + "region" : "eu-south-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.eu-south-1.amazonaws.com" + }, + "fips-eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.eu-south-2.amazonaws.com" + }, + "fips-eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.eu-west-1.amazonaws.com" + }, + "fips-eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.eu-west-2.amazonaws.com" + }, + "fips-eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.eu-west-3.amazonaws.com" + }, + "fips-il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.il-central-1.amazonaws.com" + }, + "fips-me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.me-central-1.amazonaws.com" + }, + "fips-me-south-1" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.me-south-1.amazonaws.com" + }, + "fips-mx-central-1" : { + "credentialScope" : { + "region" : "mx-central-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.mx-central-1.amazonaws.com" + }, + "fips-sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.sa-east-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "wafv2.il-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.il-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "hostname" : "wafv2.me-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.me-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "me-south-1" : { + "credentialScope" : { + "region" : "me-south-1" + }, + "hostname" : "wafv2.me-south-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.me-south-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "mx-central-1" : { + "credentialScope" : { + "region" : "mx-central-1" + }, + "hostname" : "wafv2.mx-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.mx-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "wafv2.sa-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.sa-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "wafv2.us-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "hostname" : "wafv2.us-east-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "wafv2.us-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "wafv2.us-west-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "wellarchitected" : { + "endpoints" : { + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "wisdom" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-west-2" : { }, + "fips-ca-central-1" : { + "deprecated" : true + }, + "fips-us-east-1" : { + "deprecated" : true + }, + "fips-us-west-2" : { + "deprecated" : true + }, + "ui-ap-northeast-1" : { }, + "ui-ap-northeast-2" : { }, + "ui-ap-southeast-1" : { }, + "ui-ap-southeast-2" : { }, + "ui-ca-central-1" : { }, + "ui-eu-central-1" : { }, + "ui-eu-west-2" : { }, + "ui-us-east-1" : { }, + "ui-us-west-2" : { }, + "us-east-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + } + } + }, + "workdocs" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-west-1" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "workdocs-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "workdocs-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "workdocs-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "workdocs-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "workmail" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "eu-west-1" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "workspaces" : { + "endpoints" : { + "af-south-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-5" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "workspaces-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "workspaces-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "workspaces-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { }, + "us-west-2" : { + "variants" : [ { + "hostname" : "workspaces-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "workspaces-web" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "workspaces-web-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "workspaces-web-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "workspaces-web-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "workspaces-web-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "xray" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-east-2" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ap-southeast-5" : { }, + "ap-southeast-6" : { }, + "ap-southeast-7" : { }, + "ca-central-1" : { }, + "ca-west-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "xray-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "xray-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "xray-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "xray-fips.us-west-2.amazonaws.com" + }, + "il-central-1" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "mx-central-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "xray-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "xray-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "xray-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "xray-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + } + } + }, { + "defaults" : { + "hostname" : "{service}.{region}.{dnsSuffix}", + "protocols" : [ "https" ], + "signatureVersions" : [ "v4" ], + "variants" : [ { + "dnsSuffix" : "amazonaws.com.cn", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + }, { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "dualstack", "fips" ] + }, { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "{service}.{region}.{dnsSuffix}", + "tags" : [ "dualstack" ] + } ] + }, + "dnsSuffix" : "amazonaws.com.cn", + "partition" : "aws-cn", + "partitionName" : "AWS China", + "regionRegex" : "^cn\\-\\w+\\-\\d+$", + "regions" : { + "cn-north-1" : { + "description" : "China (Beijing)" + }, + "cn-northwest-1" : { + "description" : "China (Ningxia)" + } + }, + "services" : { + "access-analyzer" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "access-analyzer.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "access-analyzer.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "account" : { + "endpoints" : { + "aws-cn-global" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "account.cn-northwest-1.amazonaws.com.cn" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-cn-global" + }, + "acm" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "acm-pca" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "airflow" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "api.ecr" : { + "endpoints" : { + "cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "api.ecr.cn-north-1.amazonaws.com.cn", + "variants" : [ { + "hostname" : "ecr.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "api.ecr.cn-northwest-1.amazonaws.com.cn", + "variants" : [ { + "hostname" : "ecr.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "api.pricing" : { + "defaults" : { + "credentialScope" : { + "service" : "pricing" + } + }, + "endpoints" : { + "cn-northwest-1" : { } + } + }, + "api.sagemaker" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "api.tunneling.iot" : { + "defaults" : { + "variants" : [ { + "dnsSuffix" : "amazonaws.com.cn", + "hostname" : "api.tunneling.iot-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + }, { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "api.iot-tunneling-fips.{region}.{dnsSuffix}", + "tags" : [ "dualstack", "fips" ] + }, { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "api.iot-tunneling.{region}.{dnsSuffix}", + "tags" : [ "dualstack" ] + } ] + }, + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "apigateway" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "appconfig" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "appconfigdata" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "application-autoscaling" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "applicationinsights" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "applicationinsights.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "applicationinsights.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "appmesh" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "appmesh.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "appmesh.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "appsync" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "appsync.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "appsync.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "arc-zonal-shift" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "athena" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "athena.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "athena.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "autoscaling" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "autoscaling-plans" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "backup" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "batch" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "budgets" : { + "endpoints" : { + "aws-cn-global" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "budgets.amazonaws.com.cn" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-cn-global" + }, + "cassandra" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "ce" : { + "endpoints" : { + "aws-cn-global" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "ce.cn-northwest-1.amazonaws.com.cn" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-cn-global" + }, + "cloudcontrolapi" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "cloudformation" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "cloudfront" : { + "endpoints" : { + "aws-cn-global" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "cloudfront.cn-northwest-1.amazonaws.com.cn", + "protocols" : [ "http", "https" ] + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-cn-global" + }, + "cloudtrail" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "codebuild" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "codecommit" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "codedeploy" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "codepipeline" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "cognito-identity" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "cognito-identity.cn-north-1.amazonaws.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "compute-optimizer" : { + "endpoints" : { + "cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "compute-optimizer.cn-north-1.amazonaws.com.cn" + }, + "cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "compute-optimizer.cn-northwest-1.amazonaws.com.cn" + } + } + }, + "config" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "cur" : { + "endpoints" : { + "cn-northwest-1" : { } + } + }, + "data-ats.iot" : { + "defaults" : { + "credentialScope" : { + "service" : "iotdata" + }, + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { + "hostname" : "data.ats.iot.cn-north-1.amazonaws.com.cn", + "protocols" : [ "https" ] + }, + "cn-northwest-1" : { } + } + }, + "data.iot" : { + "defaults" : { + "credentialScope" : { + "service" : "iotdata" + }, + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "data.jobs.iot" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "databrew" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "datasync" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "datasync.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "datasync.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "datazone" : { + "defaults" : { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "variants" : [ { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "cn-north-1" : { + "hostname" : "datazone.cn-north-1.api.amazonwebservices.com.cn" + }, + "cn-northwest-1" : { + "hostname" : "datazone.cn-northwest-1.api.amazonwebservices.com.cn" + } + } + }, + "dax" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "directconnect" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "dlm" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "dlm.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "dlm.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "dms" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "docdb" : { + "endpoints" : { + "cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "rds.cn-northwest-1.amazonaws.com.cn" + } + } + }, + "ds" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "dynamodb" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "ebs" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "ec2" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "ecs" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "eks" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "eks-auth" : { + "defaults" : { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "variants" : [ { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "cn-north-1" : { + "hostname" : "eks-auth.cn-north-1.api.amazonwebservices.com.cn" + }, + "cn-northwest-1" : { + "hostname" : "eks-auth.cn-northwest-1.api.amazonwebservices.com.cn" + } + } + }, + "elasticache" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "elasticbeanstalk" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "elasticbeanstalk.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "elasticfilesystem" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.cn-north-1.amazonaws.com.cn", + "tags" : [ "fips" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.cn-northwest-1.amazonaws.com.cn", + "tags" : [ "fips" ] + } ] + }, + "fips-cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.cn-north-1.amazonaws.com.cn" + }, + "fips-cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.cn-northwest-1.amazonaws.com.cn" + } + } + }, + "elasticloadbalancing" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "elasticmapreduce" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "elasticmapreduce.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "elasticmapreduce.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "emr-containers" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "emr-serverless" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "entitlement.marketplace" : { + "endpoints" : { + "cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "entitlement-marketplace.cn-northwest-1.amazonaws.com.cn", + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "entitlement-marketplace.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "es" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "aos.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "aos.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "events" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "events.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "events.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "firehose" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "firehose.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "firehose.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "fms" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "fsx" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "gamelift" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "gameliftstreams" : { + "defaults" : { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "variants" : [ { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "cn-north-1" : { + "hostname" : "gameliftstreams.cn-north-1.api.amazonwebservices.com.cn" + }, + "cn-northwest-1" : { + "hostname" : "gameliftstreams.cn-northwest-1.api.amazonwebservices.com.cn" + } + } + }, + "glacier" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "glue" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "glue.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "glue.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "greengrass" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { } + }, + "isRegionalized" : true + }, + "guardduty" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + }, + "isRegionalized" : true + }, + "health" : { + "defaults" : { + "protocols" : [ "https" ], + "sslCommonName" : "health.cn-northwest-1.amazonaws.com.cn" + }, + "endpoints" : { + "aws-cn-global" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "global.health.amazonaws.com.cn" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-cn-global" + }, + "iam" : { + "endpoints" : { + "aws-cn-global" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "iam.cn-north-1.amazonaws.com.cn" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-cn-global" + }, + "identitystore" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "inspector2" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "internetmonitor" : { + "defaults" : { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "variants" : [ { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "cn-north-1" : { + "hostname" : "internetmonitor.cn-north-1.api.amazonwebservices.com.cn" + }, + "cn-northwest-1" : { + "hostname" : "internetmonitor.cn-northwest-1.api.amazonwebservices.com.cn" + } + } + }, + "iot" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "iotsecuredtunneling" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "iotsitewise" : { + "endpoints" : { + "cn-north-1" : { } + } + }, + "iottwinmaker" : { + "endpoints" : { + "api-cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "api.iottwinmaker.cn-north-1.amazonaws.com.cn" + }, + "cn-north-1" : { }, + "data-cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "data.iottwinmaker.cn-north-1.amazonaws.com.cn" + } + } + }, + "kafka" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "kafkaconnect" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "kendra-ranking" : { + "defaults" : { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "variants" : [ { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "cn-north-1" : { + "hostname" : "kendra-ranking.cn-north-1.api.amazonwebservices.com.cn" + }, + "cn-northwest-1" : { + "hostname" : "kendra-ranking.cn-northwest-1.api.amazonwebservices.com.cn" + } + } + }, + "kinesis" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "kinesisanalytics" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "kinesisvideo" : { + "endpoints" : { + "cn-north-1" : { } + } + }, + "kms" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "lakeformation" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "lakeformation.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "lakeformation.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "lambda" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "lambda.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "lambda.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "license-manager" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "license-manager-linux-subscriptions" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "logs" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "logs.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "logs.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "mediaconvert" : { + "endpoints" : { + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "mediaconvert.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "memory-db" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "metering.marketplace" : { + "defaults" : { + "credentialScope" : { + "service" : "aws-marketplace" + } + }, + "endpoints" : { + "cn-northwest-1" : { } + } + }, + "metrics.sagemaker" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "monitoring" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "mq" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "neptune" : { + "endpoints" : { + "cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "rds.cn-north-1.amazonaws.com.cn" + }, + "cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "rds.cn-northwest-1.amazonaws.com.cn" + } + } + }, + "network-firewall" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "notifications" : { + "defaults" : { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "variants" : [ { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "cn-north-1" : { + "hostname" : "notifications.cn-north-1.api.amazonwebservices.com.cn" + }, + "cn-northwest-1" : { + "hostname" : "notifications.cn-northwest-1.api.amazonwebservices.com.cn" + } + } + }, + "oam" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "oidc" : { + "endpoints" : { + "cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "oidc.cn-north-1.amazonaws.com.cn" + }, + "cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "oidc.cn-northwest-1.amazonaws.com.cn" + } + } + }, + "organizations" : { + "endpoints" : { + "aws-cn-global" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "organizations.cn-northwest-1.amazonaws.com.cn" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-cn-global" + }, + "personalize" : { + "endpoints" : { + "cn-north-1" : { } + } + }, + "pi" : { + "endpoints" : { + "cn-north-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "pipes" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "polly" : { + "endpoints" : { + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "polly.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "portal.sso" : { + "endpoints" : { + "cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "portal.sso.cn-north-1.amazonaws.com.cn" + }, + "cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "portal.sso.cn-northwest-1.amazonaws.com.cn" + } + } + }, + "qbusiness" : { + "defaults" : { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "variants" : [ { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "cn-north-1" : { + "hostname" : "qbusiness.cn-north-1.api.amazonwebservices.com.cn" + }, + "cn-northwest-1" : { + "hostname" : "qbusiness.cn-northwest-1.api.amazonwebservices.com.cn" + } + } + }, + "quicksight" : { + "endpoints" : { + "cn-north-1" : { } + } + }, + "ram" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + } + } + }, + "rbin" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "rbin.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "rbin.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "rds" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "redshift" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "redshift-serverless" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "resource-explorer-2" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "resource-groups" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "rolesanywhere" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "route53" : { + "endpoints" : { + "aws-cn-global" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "route53.amazonaws.com.cn" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-cn-global" + }, + "route53profiles" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "route53resolver" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "route53resolver.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "route53resolver.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "runtime.sagemaker" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "s3" : { + "defaults" : { + "protocols" : [ "http", "https" ], + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "dnsSuffix" : "amazonaws.com.cn", + "hostname" : "{service}.dualstack.{region}.{dnsSuffix}", + "tags" : [ "dualstack" ] + } ] + }, + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "s3.dualstack.cn-north-1.amazonaws.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "s3.dualstack.cn-northwest-1.amazonaws.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "s3-control" : { + "defaults" : { + "protocols" : [ "https" ], + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "dnsSuffix" : "amazonaws.com.cn", + "hostname" : "{service}.dualstack.{region}.{dnsSuffix}", + "tags" : [ "dualstack" ] + } ] + }, + "endpoints" : { + "cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "s3-control.cn-north-1.amazonaws.com.cn", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.cn-north-1.amazonaws.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "s3-control.cn-northwest-1.amazonaws.com.cn", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control.dualstack.cn-northwest-1.amazonaws.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "savingsplans" : { + "endpoints" : { + "cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "savingsplans.cn-north-1.amazonaws.com.cn" + }, + "cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "savingsplans.cn-northwest-1.amazonaws.com.cn" + } + }, + "isRegionalized" : true + }, + "scheduler" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "schemas" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "secretsmanager" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + } ] + } + } + }, + "securityhub" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "serverlessrepo" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { + "protocols" : [ "https" ] + }, + "cn-northwest-1" : { + "protocols" : [ "https" ] + } + } + }, + "servicecatalog" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "servicediscovery" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "servicediscovery.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "servicediscovery.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "servicequotas" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "signer" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { }, + "verification-cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "verification.signer.cn-north-1.amazonaws.com.cn" + }, + "verification-cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "verification.signer.cn-northwest-1.amazonaws.com.cn" + } + } + }, + "snowball" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "snowball-fips.cn-north-1.amazonaws.com.cn", + "tags" : [ "fips" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "snowball-fips.cn-northwest-1.amazonaws.com.cn", + "tags" : [ "fips" ] + } ] + }, + "fips-cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.cn-north-1.amazonaws.com.cn" + }, + "fips-cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.cn-northwest-1.amazonaws.com.cn" + } + } + }, + "sns" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "sns.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "sns.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "sqs" : { + "defaults" : { + "protocols" : [ "http", "https" ], + "sslCommonName" : "{region}.queue.{dnsSuffix}" + }, + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "sqs.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "sqs.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "ssm" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "sso" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "states" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "states.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "states.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "storagegateway" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "streams.dynamodb" : { + "defaults" : { + "credentialScope" : { + "service" : "dynamodb" + }, + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "sts" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "support" : { + "endpoints" : { + "aws-cn-global" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "support.cn-north-1.amazonaws.com.cn" + } + }, + "partitionEndpoint" : "aws-cn-global" + }, + "swf" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "synthetics" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "synthetics.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "synthetics.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "tagging" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "transcribe" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "cn.transcribe.cn-north-1.amazonaws.com.cn", + "variants" : [ { + "hostname" : "transcribe.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "cn.transcribe.cn-northwest-1.amazonaws.com.cn", + "variants" : [ { + "hostname" : "transcribe.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "transcribestreaming" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "transcribestreaming.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "transcribestreaming.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "transfer" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "verifiedpermissions" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "waf-regional" : { + "endpoints" : { + "cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "waf-regional.cn-north-1.amazonaws.com.cn", + "variants" : [ { + "hostname" : "waf-regional-fips.cn-north-1.amazonaws.com.cn", + "tags" : [ "fips" ] + } ] + }, + "cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "waf-regional.cn-northwest-1.amazonaws.com.cn", + "variants" : [ { + "hostname" : "waf-regional-fips.cn-northwest-1.amazonaws.com.cn", + "tags" : [ "fips" ] + } ] + }, + "fips-cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.cn-north-1.amazonaws.com.cn" + }, + "fips-cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.cn-northwest-1.amazonaws.com.cn" + } + } + }, + "wafv2" : { + "endpoints" : { + "cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "hostname" : "wafv2.cn-north-1.amazonaws.com.cn", + "variants" : [ { + "hostname" : "wafv2-fips.cn-north-1.amazonaws.com.cn", + "tags" : [ "fips" ] + } ] + }, + "cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "hostname" : "wafv2.cn-northwest-1.amazonaws.com.cn", + "variants" : [ { + "hostname" : "wafv2-fips.cn-northwest-1.amazonaws.com.cn", + "tags" : [ "fips" ] + } ] + }, + "fips-cn-north-1" : { + "credentialScope" : { + "region" : "cn-north-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.cn-north-1.amazonaws.com.cn" + }, + "fips-cn-northwest-1" : { + "credentialScope" : { + "region" : "cn-northwest-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.cn-northwest-1.amazonaws.com.cn" + } + } + }, + "workspaces" : { + "endpoints" : { + "cn-northwest-1" : { } + } + }, + "xray" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + } + } + }, { + "defaults" : { + "hostname" : "{service}.{region}.{dnsSuffix}", + "protocols" : [ "https" ], + "signatureVersions" : [ "v4" ], + "variants" : [ { + "dnsSuffix" : "amazonaws.com", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + }, { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "dualstack", "fips" ] + }, { + "dnsSuffix" : "api.aws", + "hostname" : "{service}.{region}.{dnsSuffix}", + "tags" : [ "dualstack" ] + } ] + }, + "dnsSuffix" : "amazonaws.com", + "partition" : "aws-us-gov", + "partitionName" : "AWS GovCloud (US)", + "regionRegex" : "^us\\-gov\\-\\w+\\-\\d+$", + "regions" : { + "us-gov-east-1" : { + "description" : "AWS GovCloud (US-East)" + }, + "us-gov-west-1" : { + "description" : "AWS GovCloud (US-West)" + } + }, + "services" : { + "access-analyzer" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "access-analyzer.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "access-analyzer.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "access-analyzer.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "access-analyzer.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "access-analyzer.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "access-analyzer.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "access-analyzer.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "access-analyzer.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "access-analyzer.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "access-analyzer.us-gov-west-1.amazonaws.com" + } + } + }, + "acm" : { + "defaults" : { + "variants" : [ { + "hostname" : "acm.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "acm.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "acm.us-gov-west-1.amazonaws.com" + } + } + }, + "acm-pca" : { + "defaults" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "acm-pca.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "acm-pca.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "acm-pca.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "acm-pca.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "acm-pca.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "aoss" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "api.detective" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "api.detective-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "detective-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "detective.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "api.detective-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "api.detective-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "detective-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "detective.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "api.detective-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "api.ecr" : { + "defaults" : { + "variants" : [ { + "hostname" : "ecr-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "dkr-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "ecr-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "dkr-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "ecr-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "fips-dkr-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "ecr-fips.us-gov-east-1.amazonaws.com" + }, + "fips-dkr-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "ecr-fips.us-gov-west-1.amazonaws.com" + }, + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "ecr-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "ecr-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "api.ecr.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ecr-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ecr.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "api.ecr.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "ecr-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ecr-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "ecr.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "api.sagemaker" : { + "defaults" : { + "variants" : [ { + "hostname" : "api-fips.sagemaker.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "api-fips.sagemaker.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "api-fips.sagemaker.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "api-fips.sagemaker.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "api-fips.sagemaker.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1-fips-secondary" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "api.sagemaker.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1-secondary" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "api.sagemaker.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "api.tunneling.iot" : { + "defaults" : { + "variants" : [ { + "dnsSuffix" : "amazonaws.com", + "hostname" : "api.tunneling.iot-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + }, { + "dnsSuffix" : "api.aws", + "hostname" : "api.iot-tunneling-fips.{region}.{dnsSuffix}", + "tags" : [ "dualstack", "fips" ] + }, { + "dnsSuffix" : "api.aws", + "hostname" : "api.iot-tunneling.{region}.{dnsSuffix}", + "tags" : [ "dualstack" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "api.iot-tunneling.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "api.tunneling.iot-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "api.iot-tunneling-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "api.iot-tunneling.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "api.tunneling.iot-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "apigateway" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "appconfig" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "appconfig.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "appconfig.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "appconfig.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "appconfig.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "appconfigdata" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "appconfigdata.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "appconfigdata.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "appconfigdata.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "appconfigdata.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "application-autoscaling" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-gov-east-1" : { + "hostname" : "application-autoscaling.us-gov-east-1.amazonaws.com", + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "application-autoscaling.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "deprecated" : true, + "hostname" : "application-autoscaling.us-gov-east-1.amazonaws.com", + "protocols" : [ "http", "https" ] + }, + "us-gov-west-1" : { + "hostname" : "application-autoscaling.us-gov-west-1.amazonaws.com", + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "application-autoscaling.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "deprecated" : true, + "hostname" : "application-autoscaling.us-gov-west-1.amazonaws.com", + "protocols" : [ "http", "https" ] + } + } + }, + "applicationinsights" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "applicationinsights-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "applicationinsights-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "applicationinsights-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "applicationinsights-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "applicationinsights.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "applicationinsights-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "applicationinsights-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "applicationinsights.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "appstream2" : { + "defaults" : { + "credentialScope" : { + "service" : "appstream" + }, + "protocols" : [ "https" ] + }, + "endpoints" : { + "fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "appstream2-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "appstream2-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "appstream2-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "appstream2-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "appstream2-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "aps" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "arc-zonal-shift" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "athena" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "athena-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "athena-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "athena-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "athena.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "athena-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "athena.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "autoscaling" : { + "defaults" : { + "variants" : [ { + "hostname" : "autoscaling.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "protocols" : [ "http", "https" ] + }, + "us-gov-west-1" : { + "protocols" : [ "http", "https" ] + } + } + }, + "autoscaling-plans" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "autoscaling-plans.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "deprecated" : true, + "hostname" : "autoscaling-plans.us-gov-east-1.amazonaws.com", + "protocols" : [ "http", "https" ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "autoscaling-plans.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "deprecated" : true, + "hostname" : "autoscaling-plans.us-gov-west-1.amazonaws.com", + "protocols" : [ "http", "https" ] + } + } + }, + "backup" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "backup-gateway" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "batch" : { + "defaults" : { + "variants" : [ { + "hostname" : "batch.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "batch.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "batch.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "batch.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "batch.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "bedrock" : { + "endpoints" : { + "bedrock-fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "bedrock-fips.us-gov-east-1.amazonaws.com" + }, + "bedrock-fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "bedrock-fips.us-gov-west-1.amazonaws.com" + }, + "bedrock-runtime-fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "bedrock-runtime-fips.us-gov-east-1.amazonaws.com" + }, + "bedrock-runtime-fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "bedrock-runtime-fips.us-gov-west-1.amazonaws.com" + }, + "bedrock-runtime-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "bedrock-runtime.us-gov-east-1.amazonaws.com" + }, + "bedrock-runtime-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "bedrock-runtime.us-gov-west-1.amazonaws.com" + }, + "bedrock-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "bedrock.us-gov-east-1.amazonaws.com" + }, + "bedrock-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "bedrock.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "cassandra" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "cassandra.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "cassandra.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "cassandra.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "cassandra.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "cassandra.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "cassandra.us-gov-west-1.amazonaws.com" + } + } + }, + "cloudcontrolapi" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "cloudcontrolapi-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "cloudcontrolapi-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cloudcontrolapi-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cloudcontrolapi.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "cloudcontrolapi-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cloudcontrolapi-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cloudcontrolapi.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "clouddirectory" : { + "endpoints" : { + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "clouddirectory.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "clouddirectory.us-gov-west-1.amazonaws.com" + } + } + }, + "cloudformation" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "cloudformation.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "cloudformation.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "cloudformation.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "cloudformation.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "cloudformation.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "cloudformation.us-gov-west-1.amazonaws.com" + } + } + }, + "cloudhsm" : { + "endpoints" : { + "us-gov-west-1" : { } + } + }, + "cloudhsmv2" : { + "defaults" : { + "credentialScope" : { + "service" : "cloudhsm" + } + }, + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "cloudhsmv2.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "cloudtrail" : { + "defaults" : { + "variants" : [ { + "hostname" : "cloudtrail.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "cloudtrail.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "cloudtrail.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "cloudtrail.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "cloudtrail.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "codebuild" : { + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "codebuild-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "codebuild-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "codebuild-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "codebuild-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "codecommit" : { + "endpoints" : { + "fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "codecommit-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "codecommit-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "codecommit-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "codecommit-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "codecommit-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "codedeploy" : { + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "codedeploy-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "codedeploy-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "codedeploy-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "codedeploy-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "codepipeline" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "codepipeline-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "codepipeline-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "codepipeline-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "codepipeline-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "codestar-connections" : { + "endpoints" : { + "us-gov-east-1" : { } + } + }, + "cognito-identity" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "cognito-identity-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "cognito-identity.us-gov-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "cognito-identity-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cognito-identity-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cognito-identity.us-gov-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "cognito-idp" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "cognito-idp-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "cognito-idp.us-gov-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "cognito-idp-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "cognito-idp-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "cognito-idp.us-gov-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "comprehend" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "comprehend-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "comprehend-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "comprehend-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "comprehend.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "comprehendmedical" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "comprehendmedical-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "comprehendmedical-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "compute-optimizer" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "compute-optimizer-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "compute-optimizer-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "config" : { + "defaults" : { + "variants" : [ { + "hostname" : "config.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "config.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "config.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "config.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "config.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "connect" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "connect.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "connect.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "controltower" : { + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "controltower-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "controltower-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "controltower-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "controltower-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "data-ats.iot" : { + "defaults" : { + "credentialScope" : { + "service" : "iotdata" + }, + "protocols" : [ "https" ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "data.iot-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "data.iot-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "data.iot" : { + "defaults" : { + "credentialScope" : { + "service" : "iotdata" + }, + "protocols" : [ "https" ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "service" : "iotdata" + }, + "deprecated" : true, + "hostname" : "data.iot-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "data.iot-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "data.iot-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "data.jobs.iot" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "data.jobs.iot-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "data.jobs.iot-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "data.jobs.iot-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "data.jobs.iot-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "databrew" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "databrew.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "databrew.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "datasync" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "datasync-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "datasync-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "datasync-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "datasync-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "datasync.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "datasync-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "datasync-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "datasync.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "datazone" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "hostname" : "datazone.us-gov-east-1.api.aws" + }, + "us-gov-west-1" : { + "hostname" : "datazone.us-gov-west-1.api.aws" + } + } + }, + "directconnect" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "directconnect-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "directconnect-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "directconnect-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "directconnect-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "dlm" : { + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "dlm-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "dlm.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "dlm.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "dlm.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "dlm-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "dlm.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "dlm.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "dlm.us-gov-west-1.amazonaws.com" + } + } + }, + "dms" : { + "defaults" : { + "variants" : [ { + "hostname" : "dms.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "dms" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "dms.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "dms-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "dms.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "dms.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "dms.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "dms.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "dms.us-gov-west-1.amazonaws.com" + } + } + }, + "docdb" : { + "endpoints" : { + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "rds.us-gov-west-1.amazonaws.com" + } + } + }, + "drs" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "drs-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "drs-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "drs-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "drs-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "ds" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "ds-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "ds-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "ds-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "ds-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "dynamodb" : { + "defaults" : { + "variants" : [ { + "hostname" : "dynamodb.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "dynamodb-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "dynamodb-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "dynamodb-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "dynamodb-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "ebs" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "ec2" : { + "defaults" : { + "variants" : [ { + "hostname" : "ec2.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "ec2.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "ec2.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "ec2.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "ec2.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "ecs" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "ecs-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "ecs-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "ecs-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "ecs-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "eks" : { + "defaults" : { + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "eks.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "eks.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "eks.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "eks.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "eks.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "eks-auth" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "hostname" : "eks-auth.us-gov-east-1.api.aws" + }, + "us-gov-west-1" : { + "hostname" : "eks-auth.us-gov-west-1.api.aws" + } + } + }, + "elasticache" : { + "defaults" : { + "variants" : [ { + "hostname" : "elasticache.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "elasticache.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "elasticache.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "elasticache.us-gov-west-1.amazonaws.com" + } + } + }, + "elasticbeanstalk" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "elasticbeanstalk.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "elasticbeanstalk.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "elasticbeanstalk.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "elasticbeanstalk.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "elasticbeanstalk.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "elasticbeanstalk.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "elasticbeanstalk.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "elasticbeanstalk.us-gov-west-1.amazonaws.com" + } + } + }, + "elasticfilesystem" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "elasticloadbalancing" : { + "defaults" : { + "variants" : [ { + "hostname" : "elasticloadbalancing.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "elasticloadbalancing.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "elasticloadbalancing.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "elasticloadbalancing.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "elasticloadbalancing.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "elasticmapreduce" : { + "defaults" : { + "variants" : [ { + "hostname" : "elasticmapreduce.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "elasticmapreduce.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "elasticmapreduce.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "elasticmapreduce.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "elasticmapreduce.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "elasticmapreduce.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "elasticmapreduce.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "email" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "email-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "email-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "email-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "email-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "emr-containers" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "emr-containers.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "emr-containers.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "emr-containers.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "emr-containers.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "emr-serverless" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "emr-serverless.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "emr-serverless.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "emr-serverless.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "emr-serverless.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "es" : { + "endpoints" : { + "fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "es-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "aos.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "es-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "es-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "aos.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "es-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "es-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "events" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "events.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "events.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "events.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "events.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "firehose" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "firehose-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "firehose-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "firehose-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "firehose-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "fms" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "fms-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "fms-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "fms-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "fsx" : { + "endpoints" : { + "fips-prod-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-gov-east-1.amazonaws.com" + }, + "fips-prod-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-gov-west-1.amazonaws.com" + }, + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-gov-west-1.amazonaws.com" + }, + "prod-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "fsx-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "prod-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "fsx-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "fsx-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "fsx-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "gameliftstreams" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "hostname" : "gameliftstreams.us-gov-east-1.api.aws" + }, + "us-gov-west-1" : { + "hostname" : "gameliftstreams.us-gov-west-1.api.aws" + } + } + }, + "geo" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "geo-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "geo-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "glacier" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "glacier.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "glacier.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "glacier.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "glacier.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "glue" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "glue-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "glue-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "glue-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "glue-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "glue.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "glue-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "glue-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "glue.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "grafana" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "greengrass" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "dataplane-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "greengrass-ats.iot.us-gov-east-1.amazonaws.com" + }, + "dataplane-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "greengrass-ats.iot.us-gov-west-1.amazonaws.com" + }, + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "greengrass.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "greengrass.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "greengrass.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "greengrass.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + }, + "isRegionalized" : true + }, + "guardduty" : { + "defaults" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "guardduty.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "guardduty.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "guardduty.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "guardduty.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "guardduty.us-gov-west-1.amazonaws.com" + } + }, + "isRegionalized" : true + }, + "health" : { + "defaults" : { + "protocols" : [ "https" ], + "sslCommonName" : "health.us-gov-west-1.amazonaws.com" + }, + "endpoints" : { + "aws-us-gov-global" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "global.health.us-gov.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "health-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "health-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "iam" : { + "endpoints" : { + "aws-us-gov-global" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "iam.us-gov.amazonaws.com", + "variants" : [ { + "hostname" : "iam.us-gov.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "aws-us-gov-global-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "iam.us-gov.amazonaws.com" + }, + "iam-govcloud" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "iam.us-gov.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "iam-govcloud-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "iam.us-gov.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-us-gov-global" + }, + "identitystore" : { + "defaults" : { + "variants" : [ { + "hostname" : "identitystore.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "identitystore.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "identitystore.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "identitystore.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "identitystore.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "ingest.timestream" : { + "endpoints" : { + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "ingest.timestream.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "ingest.timestream.us-gov-west-1.amazonaws.com" + } + } + }, + "inspector" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "inspector-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "inspector-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "inspector-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "inspector-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "inspector2" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "inspector2-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "inspector2-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "inspector2-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "inspector2-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "internetmonitor" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "hostname" : "internetmonitor.us-gov-east-1.api.aws" + }, + "us-gov-west-1" : { + "hostname" : "internetmonitor.us-gov-west-1.api.aws" + } + } + }, + "iot" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "deprecated" : true, + "hostname" : "iot-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "deprecated" : true, + "hostname" : "iot-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "iot-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "iot-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "iotsecuredtunneling" : { + "defaults" : { + "variants" : [ { + "hostname" : "api.tunneling.iot-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "api.tunneling.iot-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "api.tunneling.iot-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "api.tunneling.iot-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "iotsitewise" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "iotsitewise-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "iotsitewise-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "iottwinmaker" : { + "endpoints" : { + "api-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "api.iottwinmaker.us-gov-west-1.amazonaws.com" + }, + "data-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "data.iottwinmaker.us-gov-west-1.amazonaws.com" + }, + "fips-api-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "api.iottwinmaker-fips.us-gov-west-1.amazonaws.com" + }, + "fips-data-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "data.iottwinmaker-fips.us-gov-west-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "iottwinmaker-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "iottwinmaker-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "kafka" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "kafka.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "kafka.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "kafka.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "kafka.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "kafka.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "kafka.us-gov-west-1.amazonaws.com" + } + } + }, + "kafkaconnect" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "kendra" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "kendra-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "kendra-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "kendra-ranking" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "hostname" : "kendra-ranking.us-gov-east-1.api.aws" + }, + "us-gov-west-1" : { + "hostname" : "kendra-ranking.us-gov-west-1.api.aws" + } + } + }, + "kinesis" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "kinesis.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "kinesis.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "kinesis.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "kinesis.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "kinesis.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "kinesis.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "kinesisanalytics" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "kinesisanalytics-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "kinesisanalytics-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "kinesisanalytics-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "kinesisanalytics-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "kinesisvideo" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "kinesisvideo-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "kinesisvideo-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "kinesisvideo-fips.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "kinesisvideo-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "kinesisvideo-fips.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "kinesisvideo-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "kms" : { + "endpoints" : { + "ProdFips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "kms-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "kms-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "lakeformation" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "lakeformation-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "lakeformation-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "lakeformation-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "lakeformation-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "lakeformation.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "lakeformation-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "lakeformation-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "lakeformation.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "lambda" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "lambda-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "lambda-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "lambda-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "lambda.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "lambda-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "lambda.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "license-manager" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "license-manager-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "license-manager-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "license-manager-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "license-manager-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "license-manager-linux-subscriptions" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "license-manager-user-subscriptions" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "logs" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "logs.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "logs.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "logs.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "logs.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "logs.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "logs.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "m2" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "deprecated" : true + }, + "fips-us-gov-west-1" : { + "deprecated" : true + }, + "us-gov-east-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + } + } + }, + "managedblockchain" : { + "endpoints" : { + "us-gov-west-1" : { } + } + }, + "mediaconvert" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "mediaconvert.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "mediaconvert.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "mediaconvert.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "mediaconvert.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + } + } + }, + "meetings-chime" : { + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "meetings-chime-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "meetings-chime-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "meetings-chime-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "meetings-chime-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "memory-db" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "metering.marketplace" : { + "defaults" : { + "credentialScope" : { + "service" : "aws-marketplace" + } + }, + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "metering-marketplace.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "metrics.sagemaker" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "mgn" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "mgn-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "mgn-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "models-v2-lex" : { + "endpoints" : { + "us-gov-west-1" : { } + } + }, + "models.lex" : { + "defaults" : { + "credentialScope" : { + "service" : "lex" + }, + "variants" : [ { + "hostname" : "models-fips.lex.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "models-fips.lex.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "models-fips.lex.us-gov-west-1.amazonaws.com" + } + } + }, + "monitoring" : { + "defaults" : { + "variants" : [ { + "hostname" : "monitoring.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "monitoring.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "monitoring.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "monitoring.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "monitoring.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "mq" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "mq-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "mq-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "mq-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "mq-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "neptune" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "rds.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "rds.us-gov-west-1.amazonaws.com" + } + } + }, + "network-firewall" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "network-firewall-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "network-firewall-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "network-firewall-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "network-firewall-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "networkmanager" : { + "endpoints" : { + "aws-us-gov-global" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "networkmanager.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "networkmanager.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "networkmanager.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "networkmanager.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "fips-aws-us-gov-global" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "networkmanager.us-gov-west-1.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-us-gov-global" + }, + "notifications" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "hostname" : "notifications.us-gov-east-1.api.aws" + }, + "us-gov-west-1" : { + "hostname" : "notifications.us-gov-west-1.api.aws" + } + } + }, + "oam" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "oidc" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "oidc.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "oidc.us-gov-west-1.amazonaws.com" + } + } + }, + "organizations" : { + "endpoints" : { + "aws-us-gov-global" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "organizations.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "organizations.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "fips-aws-us-gov-global" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "organizations.us-gov-west-1.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-us-gov-global" + }, + "osis" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "outposts" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "outposts.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "outposts.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "outposts.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "outposts.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "participant.connect" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "participant.connect.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "participant.connect.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "pi" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "pi-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "pi-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "pi-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "pi.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "pi-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "pi-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "pi.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "pinpoint" : { + "defaults" : { + "credentialScope" : { + "service" : "mobiletargeting" + } + }, + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "pinpoint-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "pinpoint.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "pinpoint-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "polly" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "polly-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "polly-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "polly-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "polly.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "portal.sso" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "portal.sso.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "portal.sso.us-gov-west-1.amazonaws.com" + } + } + }, + "qbusiness" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "hostname" : "qbusiness.us-gov-east-1.api.aws" + }, + "us-gov-west-1" : { + "hostname" : "qbusiness.us-gov-west-1.api.aws" + } + } + }, + "query.timestream" : { + "endpoints" : { + "us-gov-west-1" : { } + } + }, + "quicksight" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "ram" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "ram.us-gov-east-1.amazonaws.com", + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "ram.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ramus-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "ram.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "ram.us-gov-west-1.amazonaws.com", + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "hostname" : "ram.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "ramus-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "ram.us-gov-west-1.amazonaws.com" + } + } + }, + "rbin" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "rbin-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "rbin-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "rbin-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rbin-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rbin.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "rbin-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rbin-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rbin.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "rds" : { + "defaults" : { + "variants" : [ { + "hostname" : "rds.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "rds.us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "rds.us-gov-east-1.amazonaws.com" + }, + "rds.us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "rds.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "rds.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "rds.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "rds.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "rds.us-gov-west-1.amazonaws.com" + } + } + }, + "redshift" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "redshift.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "redshift.us-gov-west-1.amazonaws.com" + } + } + }, + "redshift-serverless" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "redshift-serverless-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "redshift-serverless-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "redshift-serverless-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "redshift-serverless-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "rekognition" : { + "endpoints" : { + "rekognition-fips.us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "rekognition-fips.us-gov-west-1.amazonaws.com" + }, + "rekognition.us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "rekognition-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "rekognition-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "rekognition-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "rekognition.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "rekognition-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "resiliencehub" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "resiliencehub-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "resiliencehub-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "resiliencehub-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "resiliencehub-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "resiliencehub.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "resiliencehub-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "resiliencehub-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "resiliencehub.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "resource-explorer-2" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "resource-groups" : { + "defaults" : { + "variants" : [ { + "hostname" : "resource-groups.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "resource-groups.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "resource-groups.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "resource-groups.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "resource-groups.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "rolesanywhere" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "rolesanywhere-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "rolesanywhere-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "rolesanywhere-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "rolesanywhere-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "route53" : { + "endpoints" : { + "aws-us-gov-global" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "route53.us-gov.amazonaws.com", + "variants" : [ { + "hostname" : "route53.us-gov.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "fips-aws-us-gov-global" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "route53.us-gov.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-us-gov-global" + }, + "route53profiles" : { + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "route53profiles-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53profiles.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "route53profiles-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "route53profiles.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "route53resolver" : { + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "route53resolver.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "route53resolver.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "route53resolver.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "deprecated" : true, + "hostname" : "route53resolver.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "route53resolver.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "route53resolver.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + }, { + "hostname" : "route53resolver.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "deprecated" : true, + "hostname" : "route53resolver.us-gov-west-1.amazonaws.com" + } + } + }, + "rum" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "runtime-v2-lex" : { + "endpoints" : { + "us-gov-west-1" : { } + } + }, + "runtime.lex" : { + "defaults" : { + "credentialScope" : { + "service" : "lex" + }, + "variants" : [ { + "hostname" : "runtime-fips.lex.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "runtime-fips.lex.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "runtime-fips.lex.us-gov-west-1.amazonaws.com" + } + } + }, + "runtime.sagemaker" : { + "defaults" : { + "variants" : [ { + "hostname" : "runtime.sagemaker.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "runtime.sagemaker.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "runtime.sagemaker.us-gov-west-1.amazonaws.com" + } + } + }, + "s3" : { + "defaults" : { + "signatureVersions" : [ "s3", "s3v4" ], + "variants" : [ { + "dnsSuffix" : "amazonaws.com", + "hostname" : "{service}-fips.dualstack.{region}.{dnsSuffix}", + "tags" : [ "dualstack", "fips" ] + }, { + "dnsSuffix" : "amazonaws.com", + "hostname" : "{service}.dualstack.{region}.{dnsSuffix}", + "tags" : [ "dualstack" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "s3-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "s3-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "hostname" : "s3.us-gov-east-1.amazonaws.com", + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "s3-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3.dualstack.us-gov-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "hostname" : "s3.us-gov-west-1.amazonaws.com", + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "s3-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3.dualstack.us-gov-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "s3-control" : { + "defaults" : { + "protocols" : [ "https" ], + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "dnsSuffix" : "amazonaws.com", + "hostname" : "{service}-fips.dualstack.{region}.{dnsSuffix}", + "tags" : [ "dualstack", "fips" ] + }, { + "dnsSuffix" : "amazonaws.com", + "hostname" : "{service}.dualstack.{region}.{dnsSuffix}", + "tags" : [ "dualstack" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "s3-control.us-gov-east-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control-fips.dualstack.us-gov-east-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-control-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3-control.dualstack.us-gov-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "s3-control-fips.us-gov-east-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ] + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "s3-control.us-gov-west-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control-fips.dualstack.us-gov-west-1.amazonaws.com", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-control-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "s3-control.dualstack.us-gov-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "s3-control-fips.us-gov-west-1.amazonaws.com", + "signatureVersions" : [ "s3v4" ] + } + } + }, + "s3-outposts" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "deprecated" : true + }, + "fips-us-gov-west-1" : { + "deprecated" : true + }, + "us-gov-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + } + } + }, + "scheduler" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "schemas" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "secretsmanager" : { + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "deprecated" : true + }, + "us-gov-west-1" : { + "variants" : [ { + "tags" : [ "dualstack" ] + }, { + "tags" : [ "dualstack", "fips" ] + }, { + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "deprecated" : true + } + } + }, + "securityhub" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "securityhub-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "securityhub-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "securityhub-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "securityhub.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "securityhub-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "securityhub.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "securitylake" : { + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "securitylake.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "securitylake.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "securitylake.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "securitylake.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "securitylake.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "securitylake.us-gov-west-1.amazonaws.com" + } + } + }, + "serverlessrepo" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "us-gov-east-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "serverlessrepo.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "serverlessrepo.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "serverlessrepo.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "serverlessrepo.us-gov-west-1.amazonaws.com" + } + } + }, + "servicecatalog" : { + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "servicecatalog-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "servicecatalog-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "servicecatalog-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "servicecatalog-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "servicecatalog-appregistry" : { + "defaults" : { + "variants" : [ { + "hostname" : "servicecatalog-appregistry.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "servicediscovery" : { + "endpoints" : { + "servicediscovery" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "servicediscovery-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "servicediscovery-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "servicediscovery-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "servicediscovery.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "servicediscovery-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "servicediscovery.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "servicequotas" : { + "defaults" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "servicequotas.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "servicequotas.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "servicequotas.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "servicequotas.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "servicequotas.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "signer" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-gov-west-1.amazonaws.com" + }, + "fips-verification-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "verification.signer-fips.us-gov-east-1.amazonaws.com" + }, + "fips-verification-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "verification.signer-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "signer-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "signer-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "verification-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "verification.signer.us-gov-east-1.amazonaws.com" + }, + "verification-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "verification.signer.us-gov-west-1.amazonaws.com" + } + } + }, + "sms-voice" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "sms-voice-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sms-voice-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "sms-voice.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "sms-voice-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "sms-voice-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "sms-voice.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "snowball" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "snowball-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "snowball-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "snowball-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "snowball-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "snowball.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "sns" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "sns.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "sns.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "sns.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "sns.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "sqs" : { + "defaults" : { + "variants" : [ { + "hostname" : "sqs.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "sqs.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "sqs.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "sqs.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "protocols" : [ "http", "https" ], + "sslCommonName" : "{region}.queue.{dnsSuffix}", + "variants" : [ { + "hostname" : "sqs.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "ssm" : { + "defaults" : { + "variants" : [ { + "hostname" : "ssm.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "ssm.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "ssm.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "ssm.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "ssm.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "sso" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "sso.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "sso.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "sso.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "sso.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "sso.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "sso.us-gov-west-1.amazonaws.com" + } + } + }, + "states" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "states-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "states.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "states-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "states.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "storagegateway" : { + "endpoints" : { + "fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "storagegateway-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "storagegateway-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "storagegateway-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "storagegateway-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "storagegateway-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "streams.dynamodb" : { + "defaults" : { + "credentialScope" : { + "service" : "dynamodb" + }, + "variants" : [ { + "hostname" : "streams.dynamodb.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "sts" : { + "defaults" : { + "variants" : [ { + "hostname" : "sts.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "sts.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "sts.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "sts.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "sts.us-gov-west-1.amazonaws.com" + } + } + }, + "support" : { + "endpoints" : { + "aws-us-gov-global" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "support.us-gov-west-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "support.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "support.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + }, + "partitionEndpoint" : "aws-us-gov-global" + }, + "swf" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "swf.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "swf.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "swf.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "swf.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "swf.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "swf.us-gov-west-1.amazonaws.com" + } + } + }, + "synthetics" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "synthetics-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "synthetics-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "synthetics-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "synthetics-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "synthetics.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "synthetics-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "synthetics-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "synthetics.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "tagging" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "textract" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "textract-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "textract-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "textract-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "textract-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "textract.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "textract-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "textract-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "textract.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "transcribe" : { + "defaults" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "fips.transcribe.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "fips.transcribe.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "fips.transcribe.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "fips.transcribe.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "transcribe-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "transcribe.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "fips.transcribe.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "transcribe-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "transcribe.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "transcribestreaming" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "transcribestreaming-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "transcribestreaming-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "transcribestreaming-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "transcribestreaming-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "transcribestreaming.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "transcribestreaming-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "transcribestreaming-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "transcribestreaming.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "transfer" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "transfer-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "transfer-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "transfer-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "transfer-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "translate" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "translate-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "translate-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "translate.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "translate-fips.us-gov-west-1.amazonaws.com" + } + } + }, + "verifiedpermissions" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "verifiedpermissions-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "verifiedpermissions-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "verifiedpermissions-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "verifiedpermissions-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "waf-regional" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "waf-regional.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "waf-regional.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "wafv2" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "wafv2.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "wafv2.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "wellarchitected" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "workspaces" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "workspaces-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "workspaces-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "workspaces-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "workspaces-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "xray" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "xray-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "xray-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "xray-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "xray-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + } + } + }, { + "defaults" : { + "hostname" : "{service}.{region}.{dnsSuffix}", + "protocols" : [ "https" ], + "signatureVersions" : [ "v4" ], + "variants" : [ { + "dnsSuffix" : "c2s.ic.gov", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "dnsSuffix" : "c2s.ic.gov", + "partition" : "aws-iso", + "partitionName" : "AWS ISO (US)", + "regionRegex" : "^us\\-iso\\-\\w+\\-\\d+$", + "regions" : { + "us-iso-east-1" : { + "description" : "US ISO East" + }, + "us-iso-west-1" : { + "description" : "US ISO WEST" + } + }, + "services" : { + "acm" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "acm-pca" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "agreement-marketplace" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "agreement-marketplace-fips.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "agreement-marketplace-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "api.ecr" : { + "endpoints" : { + "us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "hostname" : "api.ecr.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "hostname" : "api.ecr.us-iso-west-1.c2s.ic.gov" + } + } + }, + "api.pricing" : { + "defaults" : { + "credentialScope" : { + "service" : "pricing" + } + }, + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "api.sagemaker" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "apigateway" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "appconfig" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "appconfigdata" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "application-autoscaling" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "appstream2" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "arc-zonal-shift" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "athena" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "autoscaling" : { + "endpoints" : { + "us-iso-east-1" : { + "protocols" : [ "http", "https" ] + }, + "us-iso-west-1" : { } + } + }, + "backup" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "batch" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "bedrock" : { + "endpoints" : { + "bedrock-runtime-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "hostname" : "bedrock-runtime.us-iso-east-1.c2s.ic.gov" + }, + "bedrock-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "hostname" : "bedrock.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-east-1" : { } + } + }, + "budgets" : { + "endpoints" : { + "aws-iso-global" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "hostname" : "budgets.c2s.ic.gov" + }, + "us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "hostname" : "budgets.c2s.ic.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-global" + }, + "ce" : { + "endpoints" : { + "aws-iso-global" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "hostname" : "ce.us-iso-east-1.c2s.ic.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-global" + }, + "cloudcontrolapi" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "cloudformation" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "cloudtrail" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "cloudtrail-fips.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "cloudtrail-fips.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "cloudtrail-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "cloudtrail-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "codebuild" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "codedeploy" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "comprehend" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "comprehend-fips.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "comprehend-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "compute-optimizer" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "config" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "config-fips.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "config-fips.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "config-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "config-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "cost-optimization-hub" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "datapipeline" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "datasync" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "datasync-fips.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "datasync-fips.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "datasync-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "datasync-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "directconnect" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "dlm" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "dms" : { + "defaults" : { + "variants" : [ { + "hostname" : "dms.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "dms" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "dms.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "dms-fips" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "dms.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "dms.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-east-1-fips" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "dms.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "dms.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1-fips" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "dms.us-iso-west-1.c2s.ic.gov" + } + } + }, + "ds" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "ds-fips.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "ds-fips.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "ds-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "ds-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "dynamodb" : { + "endpoints" : { + "us-iso-east-1" : { + "protocols" : [ "http", "https" ] + }, + "us-iso-west-1" : { } + } + }, + "ebs" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "ec2" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "ecs" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "eks" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "elasticache" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "elasticfilesystem" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "elasticloadbalancing" : { + "endpoints" : { + "us-iso-east-1" : { + "protocols" : [ "http", "https" ] + }, + "us-iso-west-1" : { } + } + }, + "elasticmapreduce" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "elasticmapreduce.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "elasticmapreduce.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "elasticmapreduce.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "elasticmapreduce.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "emr-serverless" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "es" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "events" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "firehose" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "fsx" : { + "endpoints" : { + "fips-prod-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "fsx-fips.us-iso-east-1.c2s.ic.gov" + }, + "prod-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "fsx-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "fsx-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { } + } + }, + "glacier" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "glacier-fips.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "glacier-fips.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "glacier-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "glacier-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "glue" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "guardduty" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + }, + "isRegionalized" : true + }, + "health" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "iam" : { + "endpoints" : { + "aws-iso-global" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "hostname" : "iam.us-iso-east-1.c2s.ic.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-global" + }, + "kinesis" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "kinesisanalytics" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "kinesisvideo" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "kms" : { + "endpoints" : { + "ProdFips" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "kms-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-east-1-fips" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "kms-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1-fips" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-iso-west-1.c2s.ic.gov" + } + } + }, + "lakeformation" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "lambda" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "license-manager" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "logs" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "medialive" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "medialive-fips.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "medialive-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "mediapackage" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "metrics.sagemaker" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "monitoring" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "mq" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "network-firewall" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "oam" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "organizations" : { + "endpoints" : { + "aws-iso-global" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "hostname" : "organizations.us-iso-east-1.c2s.ic.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-global" + }, + "outposts" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "pi" : { + "endpoints" : { + "us-iso-east-1" : { + "protocols" : [ "https" ] + }, + "us-iso-west-1" : { + "protocols" : [ "https" ] + } + } + }, + "ram" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "rbin" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "rbin-fips.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "rbin-fips.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "rbin-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "rbin-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "rds" : { + "endpoints" : { + "rds.us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "rds.us-iso-east-1.c2s.ic.gov" + }, + "rds.us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "rds.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "rds.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-east-1-fips" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "rds.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "rds.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1-fips" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "rds.us-iso-west-1.c2s.ic.gov" + } + } + }, + "redshift" : { + "endpoints" : { + "us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "hostname" : "redshift.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "hostname" : "redshift.us-iso-west-1.c2s.ic.gov" + } + } + }, + "resource-groups" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "route53" : { + "endpoints" : { + "aws-iso-global" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "hostname" : "route53.c2s.ic.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-global" + }, + "route53resolver" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "runtime.sagemaker" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "s3" : { + "defaults" : { + "signatureVersions" : [ "s3v4" ] + }, + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "s3-fips.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "s3-fips.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "protocols" : [ "http", "https" ], + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-fips.dualstack.us-iso-east-1.c2s.ic.gov", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "s3-fips.dualstack.us-iso-west-1.c2s.ic.gov", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "s3-control" : { + "defaults" : { + "protocols" : [ "https" ], + "signatureVersions" : [ "s3v4" ] + }, + "endpoints" : { + "us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "hostname" : "s3-control.us-iso-east-1.c2s.ic.gov", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control-fips.dualstack.us-iso-east-1.c2s.ic.gov", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-control-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + }, { + "hostname" : "s3-control.dualstack.us-iso-east-1.c2s.ic.gov", + "tags" : [ "dualstack" ] + } ] + }, + "us-iso-east-1-fips" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "s3-control-fips.us-iso-east-1.c2s.ic.gov", + "signatureVersions" : [ "s3v4" ] + }, + "us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "hostname" : "s3-control.us-iso-west-1.c2s.ic.gov", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control-fips.dualstack.us-iso-west-1.c2s.ic.gov", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-control-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + }, { + "hostname" : "s3-control.dualstack.us-iso-west-1.c2s.ic.gov", + "tags" : [ "dualstack" ] + } ] + }, + "us-iso-west-1-fips" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "s3-control-fips.us-iso-west-1.c2s.ic.gov", + "signatureVersions" : [ "s3v4" ] + } + } + }, + "s3-outposts" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "deprecated" : true + }, + "us-iso-east-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + } + } + }, + "scheduler" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "secretsmanager" : { + "endpoints" : { + "us-iso-east-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-iso-east-1-fips" : { + "deprecated" : true + }, + "us-iso-west-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1-fips" : { + "deprecated" : true + } + } + }, + "securityhub" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "servicediscovery" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "servicequotas" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "snowball" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "sns" : { + "endpoints" : { + "us-iso-east-1" : { + "protocols" : [ "http", "https" ] + }, + "us-iso-west-1" : { } + } + }, + "sqs" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "sqs.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "sqs.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "sqs.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "sqs.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "ssm" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "states" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "states-fips.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "states-fips.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "states-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "states-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "storagegateway" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "streams.dynamodb" : { + "defaults" : { + "credentialScope" : { + "service" : "dynamodb" + } + }, + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "sts" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "support" : { + "endpoints" : { + "aws-iso-global" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "hostname" : "support.us-iso-east-1.c2s.ic.gov" + } + }, + "partitionEndpoint" : "aws-iso-global" + }, + "swf" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "swf-fips.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "swf-fips.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "swf-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "swf-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "synthetics" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "tagging" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, + "textract" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "transcribe" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "fips.transcribe.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "fips.transcribe.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "transcribestreaming" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "translate" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "translate-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-east-1-fips" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "translate-fips.us-iso-east-1.c2s.ic.gov" + } + } + }, + "wafv2" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, + "workspaces" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "workspaces-fips.us-iso-east-1.c2s.ic.gov" + }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "workspaces-fips.us-iso-west-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "workspaces-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "workspaces-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "xray" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + } + } + }, { + "defaults" : { + "hostname" : "{service}.{region}.{dnsSuffix}", + "protocols" : [ "https" ], + "signatureVersions" : [ "v4" ], + "variants" : [ { + "dnsSuffix" : "sc2s.sgov.gov", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "dnsSuffix" : "sc2s.sgov.gov", + "partition" : "aws-iso-b", + "partitionName" : "AWS ISOB (US)", + "regionRegex" : "^us\\-isob\\-\\w+\\-\\d+$", + "regions" : { + "us-isob-east-1" : { + "description" : "US ISOB East (Ohio)" + }, + "us-isob-west-1" : { + "description" : "US ISOB West" + } + }, + "services" : { + "acm" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "agreement-marketplace" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "api.ecr" : { + "endpoints" : { + "us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "hostname" : "api.ecr.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-west-1" : { } + } + }, + "api.pricing" : { + "defaults" : { + "credentialScope" : { + "service" : "pricing" + } + }, + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "api.sagemaker" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "apigateway" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "appconfig" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "appconfigdata" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "application-autoscaling" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "appstream2" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "arc-zonal-shift" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "athena" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "autoscaling" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "backup" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "batch" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "bedrock" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "budgets" : { + "endpoints" : { + "aws-iso-b-global" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "hostname" : "budgets.global.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "hostname" : "budgets.global.sc2s.sgov.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-b-global" + }, + "ce" : { + "endpoints" : { + "aws-iso-b-global" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "hostname" : "ce.us-isob-east-1.sc2s.sgov.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-b-global" + }, + "cloudcontrolapi" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "cloudformation" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "cloudtrail" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "cloudtrail-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "cloudtrail-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isob-west-1" : { } + } + }, + "codebuild" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "codedeploy" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "compute-optimizer" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "config" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "config-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "config-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isob-west-1" : { } + } + }, + "cost-optimization-hub" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "datasync" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "directconnect" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "dlm" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "dms" : { + "defaults" : { + "variants" : [ { + "hostname" : "dms.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "dms" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "dms.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "dms-fips" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "dms.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "dms.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isob-east-1-fips" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "dms.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-west-1" : { } + } + }, + "ds" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "ds-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "ds-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "dynamodb" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "ebs" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "ec2" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "ecs" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "eks" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "elasticache" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "elasticfilesystem" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isob-west-1" : { } + } + }, + "elasticloadbalancing" : { + "endpoints" : { + "us-isob-east-1" : { + "protocols" : [ "https" ] + }, + "us-isob-west-1" : { } + } + }, + "elasticmapreduce" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "elasticmapreduce.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "elasticmapreduce.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isob-west-1" : { } + } + }, + "emr-serverless" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "es" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "events" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "firehose" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "fsx" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "glacier" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "glacier-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "glacier-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "glue" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "guardduty" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "health" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "iam" : { + "endpoints" : { + "aws-iso-b-global" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "hostname" : "iam.us-isob-east-1.sc2s.sgov.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-b-global" + }, + "kinesis" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "kinesisanalytics" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "kms" : { + "endpoints" : { + "ProdFips" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "kms-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isob-east-1-fips" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-west-1" : { } + } + }, + "lakeformation" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "lambda" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "license-manager" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "logs" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "medialive" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "medialive-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "medialive-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "mediapackage" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "metering.marketplace" : { + "defaults" : { + "credentialScope" : { + "service" : "aws-marketplace" + } + }, + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "metrics.sagemaker" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "monitoring" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "network-firewall" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "oam" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "organizations" : { + "endpoints" : { + "aws-iso-b-global" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "hostname" : "organizations.us-isob-east-1.sc2s.sgov.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-b-global" + }, + "outposts" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "pi" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "ram" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "rbin" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "rbin-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "rbin-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isob-west-1" : { } + } + }, + "rds" : { + "endpoints" : { + "rds.us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "rds.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "rds.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isob-east-1-fips" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "rds.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-west-1" : { } + } + }, + "redshift" : { + "endpoints" : { + "us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "hostname" : "redshift.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-west-1" : { } + } + }, + "resource-groups" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "route53" : { + "endpoints" : { + "aws-iso-b-global" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "hostname" : "route53.sc2s.sgov.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-b-global" + }, + "route53resolver" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "runtime.sagemaker" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "s3" : { + "defaults" : { + "protocols" : [ "http", "https" ], + "signatureVersions" : [ "s3v4" ] + }, + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "s3-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "s3-fips.dualstack.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isob-west-1" : { } + } + }, + "s3-control" : { + "defaults" : { + "protocols" : [ "https" ], + "signatureVersions" : [ "s3v4" ] + }, + "endpoints" : { + "us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "hostname" : "s3-control.us-isob-east-1.sc2s.sgov.gov", + "signatureVersions" : [ "s3v4" ], + "variants" : [ { + "hostname" : "s3-control-fips.dualstack.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "s3-control-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + }, { + "hostname" : "s3-control.dualstack.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "dualstack" ] + } ] + }, + "us-isob-east-1-fips" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "s3-control-fips.us-isob-east-1.sc2s.sgov.gov", + "signatureVersions" : [ "s3v4" ] + } + } + }, + "s3-outposts" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "deprecated" : true + }, + "us-isob-east-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + } + } + }, + "scheduler" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "secretsmanager" : { + "endpoints" : { + "us-isob-east-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-isob-east-1-fips" : { + "deprecated" : true + }, + "us-isob-west-1" : { } + } + }, + "securityhub" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "servicediscovery" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "servicequotas" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "snowball" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "sns" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "sqs" : { + "defaults" : { + "protocols" : [ "http", "https" ], + "sslCommonName" : "{region}.queue.{dnsSuffix}" + }, + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "sqs.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "sqs.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isob-west-1" : { } + } + }, + "ssm" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "states" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "states-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "states-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isob-west-1" : { } + } + }, + "storagegateway" : { + "endpoints" : { + "fips" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "storagegateway-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "storagegateway-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isob-east-1-fips" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "storagegateway-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-west-1" : { } + } + }, + "streams.dynamodb" : { + "defaults" : { + "credentialScope" : { + "service" : "dynamodb" + }, + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "sts" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "support" : { + "endpoints" : { + "aws-iso-b-global" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "hostname" : "support.us-isob-east-1.sc2s.sgov.gov" + } + }, + "partitionEndpoint" : "aws-iso-b-global" + }, + "swf" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "swf-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "swf-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isob-west-1" : { } + } + }, + "synthetics" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "tagging" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + }, + "wafv2" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, + "workspaces" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "workspaces-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "workspaces-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "xray" : { + "endpoints" : { + "us-isob-east-1" : { }, + "us-isob-west-1" : { } + } + } + } + }, { + "defaults" : { + "hostname" : "{service}.{region}.{dnsSuffix}", + "protocols" : [ "https" ], + "signatureVersions" : [ "v4" ], + "variants" : [ { + "dnsSuffix" : "cloud.adc-e.uk", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "dnsSuffix" : "cloud.adc-e.uk", + "partition" : "aws-iso-e", + "partitionName" : "AWS ISOE (Europe)", + "regionRegex" : "^eu\\-isoe\\-\\w+\\-\\d+$", + "regions" : { + "eu-isoe-west-1" : { + "description" : "EU ISOE West" + } + }, + "services" : { + "access-analyzer" : { + "endpoints" : { + "eu-isoe-west-1" : { + "variants" : [ { + "hostname" : "access-analyzer.eu-isoe-west-1.api.cloud-aws.adc-e.uk", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "acm" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "acm-pca" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "api.ecr" : { + "endpoints" : { + "eu-isoe-west-1" : { + "credentialScope" : { + "region" : "eu-isoe-west-1" + }, + "hostname" : "api.ecr.eu-isoe-west-1.cloud.adc-e.uk" + } + } + }, + "api.pricing" : { + "defaults" : { + "credentialScope" : { + "service" : "pricing" + } + }, + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "api.sagemaker" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "apigateway" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "appconfig" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "appconfigdata" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "application-autoscaling" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "arc-zonal-shift" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "athena" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "autoscaling" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "batch" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "budgets" : { + "endpoints" : { + "aws-iso-e-global" : { + "credentialScope" : { + "region" : "eu-isoe-west-1" + }, + "hostname" : "budgets.global.cloud.adc-e.uk" + }, + "eu-isoe-west-1" : { + "credentialScope" : { + "region" : "eu-isoe-west-1" + }, + "hostname" : "budgets.global.cloud.adc-e.uk" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-e-global" + }, + "cloudcontrolapi" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "cloudformation" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "cloudtrail" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "cloudtrail-data" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "codedeploy" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "compute-optimizer" : { + "endpoints" : { + "eu-isoe-west-1" : { + "credentialScope" : { + "region" : "eu-isoe-west-1" + }, + "hostname" : "compute-optimizer.eu-isoe-west-1.cloud.adc-e.uk" + } + } + }, + "config" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "cost-optimization-hub" : { + "endpoints" : { + "eu-isoe-west-1" : { + "credentialScope" : { + "region" : "eu-isoe-west-1" + }, + "hostname" : "cost-optimization-hub.eu-isoe-west-1.cloud.adc-e.uk" + } + } + }, + "directconnect" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "dlm" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "dms" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "ds" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "dynamodb" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "ebs" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "ec2" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "ecs" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "eks" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "elasticache" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "elasticfilesystem" : { + "endpoints" : { + "eu-isoe-west-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.eu-isoe-west-1.cloud.adc-e.uk", + "tags" : [ "fips" ] + } ] + }, + "fips-eu-isoe-west-1" : { + "credentialScope" : { + "region" : "eu-isoe-west-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.eu-isoe-west-1.cloud.adc-e.uk" + } + } + }, + "elasticloadbalancing" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "elasticmapreduce" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "emr-serverless" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "es" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "events" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "firehose" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "glue" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "health" : { + "endpoints" : { + "eu-isoe-west-1" : { + "deprecated" : true + } + } + }, + "kinesis" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "kms" : { + "endpoints" : { + "ProdFips" : { + "credentialScope" : { + "region" : "eu-isoe-west-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.eu-isoe-west-1.cloud.adc-e.uk" + }, + "eu-isoe-west-1" : { + "variants" : [ { + "hostname" : "kms-fips.eu-isoe-west-1.cloud.adc-e.uk", + "tags" : [ "fips" ] + } ] + }, + "eu-isoe-west-1-fips" : { + "credentialScope" : { + "region" : "eu-isoe-west-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.eu-isoe-west-1.cloud.adc-e.uk" + } + } + }, + "lakeformation" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "lambda" : { + "endpoints" : { + "eu-isoe-west-1" : { + "variants" : [ { + "hostname" : "lambda.eu-isoe-west-1.api.cloud-aws.adc-e.uk", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "license-manager" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "logs" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "metrics.sagemaker" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "monitoring" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "oam" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "organizations" : { + "endpoints" : { + "aws-iso-e-global" : { + "credentialScope" : { + "region" : "eu-isoe-west-1" + }, + "hostname" : "organizations.eu-isoe-west-1.cloud.adc-e.uk" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-e-global" + }, + "pi" : { + "endpoints" : { + "eu-isoe-west-1" : { + "protocols" : [ "https" ] + } + } + }, + "pipes" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "ram" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "rbin" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "rds" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "redshift" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "redshift-serverless" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "rekognition" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "resource-groups" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "rolesanywhere" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "route53" : { + "endpoints" : { + "aws-iso-e-global" : { + "credentialScope" : { + "region" : "eu-isoe-west-1" + }, + "hostname" : "route53.cloud.adc-e.uk" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-e-global" + }, + "route53profiles" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "route53resolver" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "s3" : { + "defaults" : { + "protocols" : [ "http", "https" ], + "signatureVersions" : [ "s3v4" ] + }, + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "savingsplans" : { + "endpoints" : { + "aws-iso-e-global" : { + "credentialScope" : { + "region" : "eu-isoe-west-1" + }, + "hostname" : "savingsplans.cloud.adc-e.uk" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-e-global" + }, + "scheduler" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "schemas" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "secretsmanager" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "servicecatalog" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "servicediscovery" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "servicequotas" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "sns" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "sqs" : { + "defaults" : { + "protocols" : [ "http", "https" ], + "sslCommonName" : "{region}.queue.{dnsSuffix}" + }, + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "ssm" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "states" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "streams.dynamodb" : { + "defaults" : { + "credentialScope" : { + "service" : "dynamodb" + }, + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "sts" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "swf" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "synthetics" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "tagging" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "trustedadvisor" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + }, + "xray" : { + "endpoints" : { + "eu-isoe-west-1" : { } + } + } + } + }, { + "defaults" : { + "hostname" : "{service}.{region}.{dnsSuffix}", + "protocols" : [ "https" ], + "signatureVersions" : [ "v4" ], + "variants" : [ { + "dnsSuffix" : "csp.hci.ic.gov", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "dnsSuffix" : "csp.hci.ic.gov", + "partition" : "aws-iso-f", + "partitionName" : "AWS ISOF", + "regionRegex" : "^us\\-isof\\-\\w+\\-\\d+$", + "regions" : { + "us-isof-east-1" : { + "description" : "US ISOF EAST" + }, + "us-isof-south-1" : { + "description" : "US ISOF SOUTH" + } + }, + "services" : { + "access-analyzer" : { + "endpoints" : { + "us-isof-east-1" : { + "variants" : [ { + "hostname" : "access-analyzer.us-isof-east-1.api.aws.hci.ic.gov", + "tags" : [ "dualstack" ] + } ] + }, + "us-isof-south-1" : { + "variants" : [ { + "hostname" : "access-analyzer.us-isof-south-1.api.aws.hci.ic.gov", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "acm" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "acm-pca" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "agreement-marketplace" : { + "endpoints" : { + "us-isof-south-1" : { } + } + }, + "api.ecr" : { + "endpoints" : { + "us-isof-east-1" : { + "credentialScope" : { + "region" : "us-isof-east-1" + }, + "hostname" : "api.ecr.us-isof-east-1.csp.hci.ic.gov" + }, + "us-isof-south-1" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "hostname" : "api.ecr.us-isof-south-1.csp.hci.ic.gov" + } + } + }, + "api.pricing" : { + "defaults" : { + "credentialScope" : { + "service" : "pricing" + } + }, + "endpoints" : { + "us-isof-south-1" : { } + } + }, + "api.sagemaker" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "appconfig" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "appconfigdata" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "application-autoscaling" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "arc-zonal-shift" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "athena" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "autoscaling" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "backup" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "batch" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "bedrock" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "budgets" : { + "endpoints" : { + "aws-iso-f-global" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "hostname" : "budgets.global.csp.hci.ic.gov" + }, + "us-isof-south-1" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "hostname" : "budgets.global.csp.hci.ic.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-f-global" + }, + "ce" : { + "endpoints" : { + "aws-iso-f-global" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "hostname" : "ce.us-isof-south-1.csp.hci.ic.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-f-global" + }, + "cloudcontrolapi" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "cloudformation" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "cloudtrail" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "cloudtrail-data" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "codebuild" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "codedeploy" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "codepipeline" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "comprehend" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "compute-optimizer" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "hostname" : "compute-optimizer.us-isof-south-1.csp.hci.ic.gov" + } + } + }, + "config" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "cost-optimization-hub" : { + "endpoints" : { + "us-isof-south-1" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "hostname" : "cost-optimization-hub.us-isof-south-1.csp.hci.ic.gov" + } + } + }, + "directconnect" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "dlm" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "dms" : { + "endpoints" : { + "dms" : { + "credentialScope" : { + "region" : "us-isof-east-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "dms.us-isof-east-1.csp.hci.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "dms-fips" : { + "credentialScope" : { + "region" : "us-isof-east-1" + }, + "deprecated" : true, + "hostname" : "dms.us-isof-east-1.csp.hci.ic.gov" + }, + "us-isof-east-1" : { + "variants" : [ { + "hostname" : "dms.us-isof-east-1.csp.hci.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isof-east-1-fips" : { + "credentialScope" : { + "region" : "us-isof-east-1" + }, + "deprecated" : true, + "hostname" : "dms.us-isof-east-1.csp.hci.ic.gov" + }, + "us-isof-south-1" : { + "variants" : [ { + "hostname" : "dms.us-isof-south-1.csp.hci.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isof-south-1-fips" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "deprecated" : true, + "hostname" : "dms.us-isof-south-1.csp.hci.ic.gov" + } + } + }, + "ds" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "dynamodb" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "ebs" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "ec2" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "ecs" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "eks" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "elasticache" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "elasticfilesystem" : { + "endpoints" : { + "fips-us-isof-east-1" : { + "credentialScope" : { + "region" : "us-isof-east-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.us-isof-east-1.csp.hci.ic.gov" + }, + "fips-us-isof-south-1" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.us-isof-south-1.csp.hci.ic.gov" + }, + "us-isof-east-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.us-isof-east-1.csp.hci.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isof-south-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.us-isof-south-1.csp.hci.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "elasticloadbalancing" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "elasticmapreduce" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "es" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "events" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "firehose" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "fsx" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "glue" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "guardduty" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + }, + "isRegionalized" : true + }, + "health" : { + "endpoints" : { + "us-isof-south-1" : { + "deprecated" : true + } + } + }, + "iam" : { + "endpoints" : { + "aws-iso-f-global" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "hostname" : "iam.us-isof-south-1.csp.hci.ic.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-f-global" + }, + "identitystore" : { + "endpoints" : { + "us-isof-east-1" : { } + } + }, + "kinesis" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "kms" : { + "endpoints" : { + "ProdFips" : { + "credentialScope" : { + "region" : "us-isof-east-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-isof-east-1.csp.hci.ic.gov" + }, + "us-isof-east-1" : { + "variants" : [ { + "hostname" : "kms-fips.us-isof-east-1.csp.hci.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isof-east-1-fips" : { + "credentialScope" : { + "region" : "us-isof-east-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-isof-east-1.csp.hci.ic.gov" + }, + "us-isof-south-1" : { + "variants" : [ { + "hostname" : "kms-fips.us-isof-south-1.csp.hci.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isof-south-1-fips" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "deprecated" : true, + "hostname" : "kms-fips.us-isof-south-1.csp.hci.ic.gov" + } + } + }, + "lakeformation" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "lambda" : { + "endpoints" : { + "us-isof-east-1" : { + "variants" : [ { + "hostname" : "lambda.us-isof-east-1.api.aws.hci.ic.gov", + "tags" : [ "dualstack" ] + } ] + }, + "us-isof-south-1" : { + "variants" : [ { + "hostname" : "lambda.us-isof-south-1.api.aws.hci.ic.gov", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "license-manager" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "logs" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "metrics.sagemaker" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "monitoring" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "oam" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "organizations" : { + "endpoints" : { + "aws-iso-f-global" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "hostname" : "organizations.us-isof-south-1.csp.hci.ic.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-f-global" + }, + "pi" : { + "endpoints" : { + "us-isof-east-1" : { + "protocols" : [ "https" ] + }, + "us-isof-south-1" : { + "protocols" : [ "https" ] + } + } + }, + "pipes" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "quicksight" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "ram" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "rbin" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "rds" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "redshift" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "redshift-serverless" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "rekognition" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "resource-groups" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "rolesanywhere" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "route53" : { + "endpoints" : { + "aws-iso-f-global" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "hostname" : "route53.csp.hci.ic.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-f-global" + }, + "route53profiles" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "route53resolver" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "runtime.sagemaker" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "s3" : { + "defaults" : { + "protocols" : [ "http", "https" ], + "signatureVersions" : [ "s3v4" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "savingsplans" : { + "endpoints" : { + "aws-iso-f-global" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "hostname" : "savingsplans.csp.hci.ic.gov" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-iso-f-global" + }, + "scheduler" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "schemas" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "secretsmanager" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "securityhub" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "servicediscovery" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "servicequotas" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "sns" : { + "defaults" : { + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "sqs" : { + "defaults" : { + "protocols" : [ "http", "https" ], + "sslCommonName" : "{region}.queue.{dnsSuffix}" + }, + "endpoints" : { + "fips-us-isof-east-1" : { + "credentialScope" : { + "region" : "us-isof-east-1" + }, + "deprecated" : true, + "hostname" : "sqs.us-isof-east-1.csp.hci.ic.gov" + }, + "fips-us-isof-south-1" : { + "credentialScope" : { + "region" : "us-isof-south-1" + }, + "deprecated" : true, + "hostname" : "sqs.us-isof-south-1.csp.hci.ic.gov" + }, + "us-isof-east-1" : { + "variants" : [ { + "hostname" : "sqs.us-isof-east-1.csp.hci.ic.gov", + "tags" : [ "fips" ] + } ] + }, + "us-isof-south-1" : { + "variants" : [ { + "hostname" : "sqs.us-isof-south-1.csp.hci.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, + "ssm" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "states" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "streams.dynamodb" : { + "defaults" : { + "credentialScope" : { + "service" : "dynamodb" + }, + "protocols" : [ "http", "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "sts" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "swf" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "synthetics" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "tagging" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "textract" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "transcribe" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "transcribestreaming" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "translate" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + }, + "trustedadvisor" : { + "endpoints" : { + "us-isof-south-1" : { } + } + }, + "xray" : { + "endpoints" : { + "us-isof-east-1" : { }, + "us-isof-south-1" : { } + } + } + } + }, { + "defaults" : { + "hostname" : "{service}.{region}.{dnsSuffix}", + "protocols" : [ "https" ], + "signatureVersions" : [ "v4" ], + "variants" : [ { + "dnsSuffix" : "amazonaws.eu", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + }, { + "dnsSuffix" : "api.amazonwebservices.eu", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "dualstack", "fips" ] + }, { + "dnsSuffix" : "api.amazonwebservices.eu", + "hostname" : "{service}.{region}.{dnsSuffix}", + "tags" : [ "dualstack" ] + } ] + }, + "dnsSuffix" : "amazonaws.eu", + "partition" : "aws-eusc", + "partitionName" : "AWS EUSC", + "regionRegex" : "^eusc\\-(de)\\-\\w+\\-\\d+$", + "regions" : { + "eusc-de-east-1" : { + "description" : "AWS European Sovereign Cloud (Germany)" + } + }, + "services" : { + "access-analyzer" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "acm" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "acm-pca" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "agreement-marketplace" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "api.ecr" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "api.pricing" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "api.sagemaker" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "apigateway" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "appconfig" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "appconfigdata" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "application-autoscaling" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "arc-zonal-shift" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "athena" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "autoscaling" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "backup" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "batch" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "bedrock" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "cloudcontrolapi" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "cloudformation" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "cloudtrail" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "codedeploy" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "cognito-identity" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "cognito-idp" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "compute-optimizer" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "config" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "controltower" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "cost-optimization-hub" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "datasync" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "datazone" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "directconnect" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "dlm" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "dms" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "drs" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "ds" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "dynamodb" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "ebs" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "ec2" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "ecs" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "eks" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "eks-auth" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "elasticache" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "elasticfilesystem" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "elasticloadbalancing" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "elasticmapreduce" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "email" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "emr-serverless" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "entitlement.marketplace" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "es" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "events" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "firehose" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "fsx" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "gameliftstreams" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "glue" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "guardduty" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "health" : { + "endpoints" : { + "eusc-de-east-1" : { + "deprecated" : true + } + } + }, + "identitystore" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "internetmonitor" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "kafka" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "kendra-ranking" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "kinesis" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "kinesisanalytics" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "kms" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "lakeformation" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "lambda" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "license-manager" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "logs" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "metering.marketplace" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "metrics.sagemaker" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "monitoring" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "mq" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "network-firewall" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "notifications" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "oam" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "oidc" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "pi" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "polly" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "portal.sso" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "qbusiness" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "ram" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "rbin" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "rds" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "redshift" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "redshift-serverless" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "resource-groups" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "rolesanywhere" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "route53profiles" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "route53resolver" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "rum" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "runtime.sagemaker" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "s3" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "s3-control" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "scheduler" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "secretsmanager" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "securityhub" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "servicediscovery" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "servicequotas" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "signer" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "sms-voice" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "sns" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "sqs" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "ssm" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "sso" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "states" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "storagegateway" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "streams.dynamodb" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "sts" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "swf" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "synthetics" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "tagging" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "transfer" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "trustedadvisor" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "wafv2" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + }, + "xray" : { + "endpoints" : { + "eusc-de-east-1" : { } + } + } + } + } ], + "version" : 3 +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/entityresolution/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/entityresolution/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b6e4a57 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/entityresolution/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/entityresolution/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/entityresolution/2018-05-10/paginators-1.json new file mode 100644 index 0000000..b34d382 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/entityresolution/2018-05-10/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "ListMatchingJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobs" + }, + "ListMatchingWorkflows": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workflowSummaries" + }, + "ListSchemaMappings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "schemaList" + }, + "ListIdMappingJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobs" + }, + "ListIdMappingWorkflows": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workflowSummaries" + }, + "ListProviderServices": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "providerServiceSummaries" + }, + "ListIdNamespaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "idNamespaceSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/entityresolution/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/entityresolution/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..0936c73 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/entityresolution/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/es/2015-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/es/2015-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4e3a98b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/es/2015-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/es/2015-01-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/es/2015-01-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/es/2015-01-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/es/2015-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/es/2015-01-01/paginators-1.json new file mode 100644 index 0000000..4c0f24e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/es/2015-01-01/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListElasticsearchInstanceTypes": { + "result_key": "ElasticsearchInstanceTypes", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListElasticsearchVersions": { + "result_key": "ElasticsearchVersions", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "DescribeReservedElasticsearchInstanceOfferings": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ReservedElasticsearchInstanceOfferings" + }, + "DescribeReservedElasticsearchInstances": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ReservedElasticsearchInstances" + }, + "GetUpgradeHistory": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "UpgradeHistories" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/es/2015-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/es/2015-01-01/service-2.json.gz new file mode 100644 index 0000000..1868d68 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/es/2015-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/eventbridgev2/2025-05-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/eventbridgev2/2025-05-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..da9d716 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/eventbridgev2/2025-05-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/eventbridgev2/2025-05-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/eventbridgev2/2025-05-15/paginators-1.json new file mode 100644 index 0000000..462a282 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/eventbridgev2/2025-05-15/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListEventBuses": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EventBuses" + }, + "ListEventSources": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EventSources" + }, + "ListResourcePolicies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PolicySummaries" + }, + "ListSubscribers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Subscribers" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/eventbridgev2/2025-05-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/eventbridgev2/2025-05-15/service-2.json.gz new file mode 100644 index 0000000..031280b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/eventbridgev2/2025-05-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/eventbridgev2/2025-05-15/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/eventbridgev2/2025-05-15/waiters-2.json new file mode 100644 index 0000000..9e98b0b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/eventbridgev2/2025-05-15/waiters-2.json @@ -0,0 +1,48 @@ +{ + "version" : 2, + "waiters" : { + "EventBusActive" : { + "description" : "Waits until the event bus reports ACTIVE. Use after CreateEventBus (bus starts in CREATING) or UpdateEventBus (bus moves to UPDATING) before invoking operations that require an ACTIVE bus. Fails fast on the terminal failure states CREATE_FAILED, UPDATE_FAILED, and DELETE_FAILED, surfacing StateReason. CREATING, UPDATING, and DELETING are intentionally unmodeled intermediate states that fall through to the implicit retry.", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "DescribeEventBus", + "acceptors" : [ { + "matcher" : "path", + "argument" : "State", + "state" : "failure", + "expected" : "CREATE_FAILED" + }, { + "matcher" : "path", + "argument" : "State", + "state" : "failure", + "expected" : "UPDATE_FAILED" + }, { + "matcher" : "path", + "argument" : "State", + "state" : "failure", + "expected" : "DELETE_FAILED" + }, { + "matcher" : "path", + "argument" : "State", + "state" : "success", + "expected" : "ACTIVE" + } ] + }, + "EventBusDeleted" : { + "description" : "Waits until the event bus is fully deleted, signaled by DescribeEventBus returning ResourceNotFoundException. Use after DeleteEventBus. Fails fast on the terminal DELETE_FAILED state, surfacing StateReason; retry DeleteEventBus after removing subscribers.", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "DescribeEventBus", + "acceptors" : [ { + "matcher" : "path", + "argument" : "State", + "state" : "failure", + "expected" : "DELETE_FAILED" + }, { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/events/2014-02-03/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/events/2014-02-03/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..31239b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/events/2014-02-03/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/events/2014-02-03/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/events/2014-02-03/service-2.json.gz new file mode 100644 index 0000000..06bf430 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/events/2014-02-03/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/events/2015-10-07/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/events/2015-10-07/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f54b06b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/events/2015-10-07/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/events/2015-10-07/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/events/2015-10-07/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/events/2015-10-07/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/events/2015-10-07/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/events/2015-10-07/paginators-1.json new file mode 100644 index 0000000..501a322 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/events/2015-10-07/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListRuleNamesByTarget": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "RuleNames" + }, + "ListRules": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Rules" + }, + "ListTargetsByRule": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Targets" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/events/2015-10-07/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/events/2015-10-07/service-2.json.gz new file mode 100644 index 0000000..ed1f0e2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/events/2015-10-07/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/evs/2023-07-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/evs/2023-07-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..5e2a8e3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/evs/2023-07-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/evs/2023-07-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/evs/2023-07-27/paginators-1.json new file mode 100644 index 0000000..064a6a8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/evs/2023-07-27/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListEnvironmentHosts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "environmentHosts" + }, + "ListEnvironmentVlans": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "environmentVlans" + }, + "ListEnvironments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "environmentSummaries" + }, + "ListEnvironmentConnectors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "connectors" + }, + "ListVmEntitlements": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "entitlements" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/evs/2023-07-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/evs/2023-07-27/service-2.json.gz new file mode 100644 index 0000000..a852308 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/evs/2023-07-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/evs/2023-07-27/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/evs/2023-07-27/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/evs/2023-07-27/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/finspace-data/2020-07-13/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/finspace-data/2020-07-13/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6adf15a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/finspace-data/2020-07-13/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/finspace-data/2020-07-13/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/finspace-data/2020-07-13/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/finspace-data/2020-07-13/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/finspace-data/2020-07-13/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/finspace-data/2020-07-13/paginators-1.json new file mode 100644 index 0000000..aa6632a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/finspace-data/2020-07-13/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListChangesets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "changesets" + }, + "ListDataViews": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dataViews" + }, + "ListDatasets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "datasets" + }, + "ListPermissionGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "permissionGroups" + }, + "ListUsers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "users" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/finspace-data/2020-07-13/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/finspace-data/2020-07-13/service-2.json.gz new file mode 100644 index 0000000..ce12ad6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/finspace-data/2020-07-13/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/finspace/2021-03-12/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/finspace/2021-03-12/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7cd64a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/finspace/2021-03-12/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/finspace/2021-03-12/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/finspace/2021-03-12/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/finspace/2021-03-12/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/finspace/2021-03-12/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/finspace/2021-03-12/paginators-1.json new file mode 100644 index 0000000..741ca79 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/finspace/2021-03-12/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListKxEnvironments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "environments" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/finspace/2021-03-12/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/finspace/2021-03-12/service-2.json.gz new file mode 100644 index 0000000..01a5346 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/finspace/2021-03-12/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/firehose/2015-08-04/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/firehose/2015-08-04/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..64abbfc Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/firehose/2015-08-04/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/firehose/2015-08-04/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/firehose/2015-08-04/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/firehose/2015-08-04/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/firehose/2015-08-04/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/firehose/2015-08-04/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/firehose/2015-08-04/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/firehose/2015-08-04/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/firehose/2015-08-04/service-2.json.gz new file mode 100644 index 0000000..8593237 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/firehose/2015-08-04/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/fis/2020-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/fis/2020-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..69a3880 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/fis/2020-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/fis/2020-12-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/fis/2020-12-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/fis/2020-12-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/fis/2020-12-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/fis/2020-12-01/paginators-1.json new file mode 100644 index 0000000..740e56e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/fis/2020-12-01/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "actions" + }, + "ListExperimentResolvedTargets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "resolvedTargets" + }, + "ListExperimentTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "experimentTemplates" + }, + "ListExperiments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "experiments" + }, + "ListTargetAccountConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "targetAccountConfigurations" + }, + "ListTargetResourceTypes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "targetResourceTypes" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/fis/2020-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/fis/2020-12-01/service-2.json.gz new file mode 100644 index 0000000..7eed6b1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/fis/2020-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/fms/2018-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/fms/2018-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..dfd8f11 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/fms/2018-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/fms/2018-01-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/fms/2018-01-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/fms/2018-01-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/fms/2018-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/fms/2018-01-01/paginators-1.json new file mode 100644 index 0000000..730571d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/fms/2018-01-01/paginators-1.json @@ -0,0 +1,52 @@ +{ + "pagination": { + "ListComplianceStatus": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PolicyComplianceStatusList" + }, + "ListMemberAccounts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MemberAccounts" + }, + "ListPolicies": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PolicyList" + }, + "ListAppsLists": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AppsLists" + }, + "ListProtocolsLists": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ProtocolsLists" + }, + "ListThirdPartyFirewallFirewallPolicies": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ThirdPartyFirewallFirewallPolicies" + }, + "ListAdminAccountsForOrganization": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AdminAccounts" + }, + "ListAdminsManagingAccount": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AdminAccounts" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/fms/2018-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/fms/2018-01-01/service-2.json.gz new file mode 100644 index 0000000..0d72b7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/fms/2018-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/forecast/2018-06-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/forecast/2018-06-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8e12cc3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/forecast/2018-06-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/forecast/2018-06-26/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/forecast/2018-06-26/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/forecast/2018-06-26/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/forecast/2018-06-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/forecast/2018-06-26/paginators-1.json new file mode 100644 index 0000000..853dee4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/forecast/2018-06-26/paginators-1.json @@ -0,0 +1,88 @@ +{ + "pagination": { + "ListDatasetGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DatasetGroups" + }, + "ListDatasetImportJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DatasetImportJobs" + }, + "ListDatasets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Datasets" + }, + "ListForecastExportJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ForecastExportJobs" + }, + "ListForecasts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Forecasts" + }, + "ListPredictors": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Predictors" + }, + "ListPredictorBacktestExportJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PredictorBacktestExportJobs" + }, + "ListExplainabilities": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Explainabilities" + }, + "ListExplainabilityExports": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ExplainabilityExports" + }, + "ListMonitorEvaluations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PredictorMonitorEvaluations" + }, + "ListMonitors": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Monitors" + }, + "ListWhatIfAnalyses": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "WhatIfAnalyses" + }, + "ListWhatIfForecastExports": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "WhatIfForecastExports" + }, + "ListWhatIfForecasts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "WhatIfForecasts" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/forecast/2018-06-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/forecast/2018-06-26/service-2.json.gz new file mode 100644 index 0000000..ea50a69 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/forecast/2018-06-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/forecastquery/2018-06-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/forecastquery/2018-06-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..5968288 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/forecastquery/2018-06-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/forecastquery/2018-06-26/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/forecastquery/2018-06-26/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/forecastquery/2018-06-26/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/forecastquery/2018-06-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/forecastquery/2018-06-26/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/forecastquery/2018-06-26/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/forecastquery/2018-06-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/forecastquery/2018-06-26/service-2.json.gz new file mode 100644 index 0000000..c46d607 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/forecastquery/2018-06-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/frauddetector/2019-11-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/frauddetector/2019-11-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a47f0c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/frauddetector/2019-11-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/frauddetector/2019-11-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/frauddetector/2019-11-15/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/frauddetector/2019-11-15/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/frauddetector/2019-11-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/frauddetector/2019-11-15/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/frauddetector/2019-11-15/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/frauddetector/2019-11-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/frauddetector/2019-11-15/service-2.json.gz new file mode 100644 index 0000000..85ee76d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/frauddetector/2019-11-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/freetier/2023-09-07/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/freetier/2023-09-07/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a16aae7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/freetier/2023-09-07/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/freetier/2023-09-07/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/freetier/2023-09-07/paginators-1.json new file mode 100644 index 0000000..f01e3ed --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/freetier/2023-09-07/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "GetFreeTierUsage": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "freeTierUsages" + }, + "ListAccountActivities": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "activities" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/freetier/2023-09-07/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/freetier/2023-09-07/service-2.json.gz new file mode 100644 index 0000000..735966d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/freetier/2023-09-07/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/freetier/2023-09-07/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/freetier/2023-09-07/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/freetier/2023-09-07/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/fsx/2018-03-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/fsx/2018-03-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ecc6d88 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/fsx/2018-03-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/fsx/2018-03-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/fsx/2018-03-01/examples-1.json new file mode 100644 index 0000000..1993a23 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/fsx/2018-03-01/examples-1.json @@ -0,0 +1,438 @@ +{ + "version": "1.0", + "examples": { + "CopyBackup": [ + { + "input": { + "SourceBackupId": "backup-03e3c82e0183b7b6b", + "SourceRegion": "us-east-2" + }, + "output": { + "Backup": { + "BackupId": "backup-0a3364eded1014b28", + "CreationTime": 1617954808.068, + "FileSystem": { + "FileSystemId": "fs-0498eed5fe91001ec", + "FileSystemType": "LUSTRE", + "LustreConfiguration": { + "AutomaticBackupRetentionDays": 0, + "DeploymentType": "PERSISTENT_1", + "PerUnitStorageThroughput": 50, + "WeeklyMaintenanceStartTime": "1:05:00" + }, + "ResourceARN": "arn:aws:fsx:us-east-1:012345678912:file-system/fs-0f5179e395f597e66", + "StorageCapacity": 2400, + "StorageType": "SSD" + }, + "KmsKeyId": "arn:aws:fsx:us-east-1:012345678912:key/d1234e22-543a-12b7-a98f-e12c2b54001a", + "Lifecycle": "COPYING", + "OwnerId": "123456789012", + "ResourceARN": "arn:aws:fsx:us-east-1:012345678912:backup/backup-0a3364eded1014b28", + "Tags": [ + { + "Key": "Name", + "Value": "MyBackup" + } + ], + "Type": "USER_INITIATED" + } + }, + "comments": { + }, + "description": "This operation copies an Amazon FSx backup.", + "id": "to-copy-a-backup-1481847318640", + "title": "To copy a backup" + } + ], + "CreateBackup": [ + { + "input": { + "FileSystemId": "fs-0498eed5fe91001ec", + "Tags": [ + { + "Key": "Name", + "Value": "MyBackup" + } + ] + }, + "output": { + "Backup": { + "BackupId": "backup-03e3c82e0183b7b6b", + "CreationTime": "1481841524.0", + "FileSystem": { + "FileSystemId": "fs-0498eed5fe91001ec", + "OwnerId": "012345678912", + "StorageCapacity": 300, + "WindowsConfiguration": { + "ActiveDirectoryId": "d-1234abcd12", + "AutomaticBackupRetentionDays": 30, + "DailyAutomaticBackupStartTime": "05:00", + "WeeklyMaintenanceStartTime": "1:05:00" + } + }, + "Lifecycle": "CREATING", + "ProgressPercent": 0, + "ResourceARN": "arn:aws:fsx:us-east-1:012345678912:backup/backup-03e3c82e0183b7b6b", + "Tags": [ + { + "Key": "Name", + "Value": "MyBackup" + } + ], + "Type": "USER_INITIATED" + } + }, + "comments": { + }, + "description": "This operation creates a new backup.", + "id": "to-create-a-new-backup-1481840798597", + "title": "To create a new backup" + } + ], + "CreateFileSystem": [ + { + "input": { + "ClientRequestToken": "a8ca07e4-61ec-4399-99f4-19853801bcd5", + "FileSystemType": "WINDOWS", + "KmsKeyId": "arn:aws:kms:us-east-1:012345678912:key/1111abcd-2222-3333-4444-55556666eeff", + "SecurityGroupIds": [ + "sg-edcd9784" + ], + "StorageCapacity": 3200, + "StorageType": "HDD", + "SubnetIds": [ + "subnet-1234abcd" + ], + "Tags": [ + { + "Key": "Name", + "Value": "MyFileSystem" + } + ], + "WindowsConfiguration": { + "ActiveDirectoryId": "d-1234abcd12", + "Aliases": [ + "accounting.corp.example.com" + ], + "AutomaticBackupRetentionDays": 30, + "DailyAutomaticBackupStartTime": "05:00", + "ThroughputCapacity": 32, + "WeeklyMaintenanceStartTime": "1:05:00" + } + }, + "output": { + "FileSystem": { + "CreationTime": "1481841524.0", + "DNSName": "fs-0123456789abcdef0.fsx.com", + "FileSystemId": "fs-0123456789abcdef0", + "KmsKeyId": "arn:aws:kms:us-east-1:012345678912:key/1111abcd-2222-3333-4444-55556666eeff", + "Lifecycle": "CREATING", + "OwnerId": "012345678912", + "ResourceARN": "arn:aws:fsx:us-east-1:012345678912:file-system/fs-0123456789abcdef0", + "StorageCapacity": 3200, + "StorageType": "HDD", + "SubnetIds": [ + "subnet-1234abcd" + ], + "Tags": [ + { + "Key": "Name", + "Value": "MyFileSystem" + } + ], + "VpcId": "vpc-ab1234cd", + "WindowsConfiguration": { + "ActiveDirectoryId": "d-1234abcd12", + "Aliases": [ + { + "Lifecycle": "CREATING", + "Name": "accounting.corp.example.com" + } + ], + "AutomaticBackupRetentionDays": 30, + "DailyAutomaticBackupStartTime": "05:00", + "ThroughputCapacity": 32, + "WeeklyMaintenanceStartTime": "1:05:00" + } + } + }, + "comments": { + }, + "description": "This operation creates a new Amazon FSx for Windows File Server file system.", + "id": "to-create-a-new-file-system-1481840798547", + "title": "To create a new file system" + } + ], + "CreateFileSystemFromBackup": [ + { + "input": { + "BackupId": "backup-03e3c82e0183b7b6b", + "ClientRequestToken": "f4c94ed7-238d-4c46-93db-48cd62ec33b7", + "SecurityGroupIds": [ + "sg-edcd9784" + ], + "SubnetIds": [ + "subnet-1234abcd" + ], + "Tags": [ + { + "Key": "Name", + "Value": "MyFileSystem" + } + ], + "WindowsConfiguration": { + "ThroughputCapacity": 8 + } + }, + "output": { + "FileSystem": { + "CreationTime": "1481841524.0", + "DNSName": "fs-0498eed5fe91001ec.fsx.com", + "FileSystemId": "fs-0498eed5fe91001ec", + "KmsKeyId": "arn:aws:kms:us-east-1:012345678912:key/0ff3ea8d-130e-4133-877f-93908b6fdbd6", + "Lifecycle": "CREATING", + "OwnerId": "012345678912", + "ResourceARN": "arn:aws:fsx:us-east-1:012345678912:file-system/fs-0498eed5fe91001ec", + "StorageCapacity": 300, + "SubnetIds": [ + "subnet-1234abcd" + ], + "Tags": [ + { + "Key": "Name", + "Value": "MyFileSystem" + } + ], + "VpcId": "vpc-ab1234cd", + "WindowsConfiguration": { + "ActiveDirectoryId": "d-1234abcd12", + "AutomaticBackupRetentionDays": 30, + "DailyAutomaticBackupStartTime": "05:00", + "ThroughputCapacity": 8, + "WeeklyMaintenanceStartTime": "1:05:00" + } + } + }, + "comments": { + }, + "description": "This operation creates a new file system from backup.", + "id": "to-create-a-new-file-system-from-backup-1481840798598", + "title": "To create a new file system from backup" + } + ], + "DeleteBackup": [ + { + "input": { + "BackupId": "backup-03e3c82e0183b7b6b" + }, + "output": { + "BackupId": "backup-03e3c82e0183b7b6b", + "Lifecycle": "DELETED" + }, + "comments": { + }, + "description": "This operation deletes an Amazon FSx file system backup.", + "id": "to-delete-a-file-system-1481847318399", + "title": "To delete a backup" + } + ], + "DeleteFileSystem": [ + { + "input": { + "FileSystemId": "fs-0498eed5fe91001ec" + }, + "output": { + "FileSystemId": "fs-0498eed5fe91001ec", + "Lifecycle": "DELETING" + }, + "comments": { + }, + "description": "This operation deletes an Amazon FSx file system.", + "id": "to-delete-a-file-system-1481847318348", + "title": "To delete a file system" + } + ], + "DescribeBackups": [ + { + "input": { + }, + "output": { + "Backups": [ + { + "BackupId": "backup-03e3c82e0183b7b6b", + "CreationTime": "1481841524.0", + "FileSystem": { + "FileSystemId": "fs-0498eed5fe91001ec", + "OwnerId": "012345678912", + "StorageCapacity": 300, + "WindowsConfiguration": { + "ActiveDirectoryId": "d-1234abcd12", + "AutomaticBackupRetentionDays": 30, + "DailyAutomaticBackupStartTime": "05:00", + "WeeklyMaintenanceStartTime": "1:05:00" + } + }, + "Lifecycle": "AVAILABLE", + "ResourceARN": "arn:aws:fsx:us-east-1:012345678912:backup/backup-03e3c82e0183b7b6b", + "Tags": [ + { + "Key": "Name", + "Value": "MyBackup" + } + ], + "Type": "USER_INITIATED" + } + ] + }, + "comments": { + }, + "description": "This operation describes all of the Amazon FSx backups in an account.", + "id": "to-describe-backups-1481848448499", + "title": "To describe Amazon FSx backups" + } + ], + "DescribeFileSystems": [ + { + "input": { + }, + "output": { + "FileSystems": [ + { + "CreationTime": "1481841524.0", + "DNSName": "fs-0498eed5fe91001ec.fsx.com", + "FileSystemId": "fs-0498eed5fe91001ec", + "KmsKeyId": "arn:aws:kms:us-east-1:012345678912:key/0ff3ea8d-130e-4133-877f-93908b6fdbd6", + "Lifecycle": "AVAILABLE", + "NetworkInterfaceIds": [ + "eni-abcd1234" + ], + "OwnerId": "012345678912", + "ResourceARN": "arn:aws:fsx:us-east-1:012345678912:file-system/fs-0498eed5fe91001ec", + "StorageCapacity": 300, + "SubnetIds": [ + "subnet-1234abcd" + ], + "Tags": [ + { + "Key": "Name", + "Value": "MyFileSystem" + } + ], + "VpcId": "vpc-ab1234cd", + "WindowsConfiguration": { + "ActiveDirectoryId": "d-1234abcd12", + "AutomaticBackupRetentionDays": 30, + "DailyAutomaticBackupStartTime": "05:00", + "ThroughputCapacity": 8, + "WeeklyMaintenanceStartTime": "1:05:00" + } + } + ] + }, + "comments": { + }, + "description": "This operation describes all of the Amazon FSx file systems in an account.", + "id": "to-describe-a-file-systems-1481848448460", + "title": "To describe an Amazon FSx file system" + } + ], + "ListTagsForResource": [ + { + "input": { + "ResourceARN": "arn:aws:fsx:us-east-1:012345678912:file-system/fs-0498eed5fe91001ec" + }, + "output": { + "Tags": [ + { + "Key": "Name", + "Value": "MyFileSystem" + } + ] + }, + "comments": { + }, + "description": "This operation lists tags for an Amazon FSx resource.", + "id": "to-list-tags-for-a-fsx-resource-1481847318372", + "title": "To list tags for a resource" + } + ], + "TagResource": [ + { + "input": { + "ResourceARN": "arn:aws:fsx:us-east-1:012345678912:file-system/fs-0498eed5fe91001ec", + "Tags": [ + { + "Key": "Name", + "Value": "MyFileSystem" + } + ] + }, + "comments": { + }, + "description": "This operation tags an Amazon FSx resource.", + "id": "to-tag-a-fsx-resource-1481847318371", + "title": "To tag a resource" + } + ], + "UntagResource": [ + { + "input": { + "ResourceARN": "arn:aws:fsx:us-east-1:012345678912:file-system/fs-0498eed5fe91001ec", + "TagKeys": [ + "Name" + ] + }, + "comments": { + }, + "description": "This operation untags an Amazon FSx resource.", + "id": "to-untag-a-fsx-resource-1481847318373", + "title": "To untag a resource" + } + ], + "UpdateFileSystem": [ + { + "input": { + "FileSystemId": "fs-0498eed5fe91001ec", + "WindowsConfiguration": { + "AutomaticBackupRetentionDays": 10, + "DailyAutomaticBackupStartTime": "06:00", + "WeeklyMaintenanceStartTime": "3:06:00" + } + }, + "output": { + "FileSystem": { + "CreationTime": "1481841524.0", + "DNSName": "fs-0498eed5fe91001ec.fsx.com", + "FileSystemId": "fs-0498eed5fe91001ec", + "KmsKeyId": "arn:aws:kms:us-east-1:012345678912:key/0ff3ea8d-130e-4133-877f-93908b6fdbd6", + "Lifecycle": "AVAILABLE", + "OwnerId": "012345678912", + "ResourceARN": "arn:aws:fsx:us-east-1:012345678912:file-system/fs-0498eed5fe91001ec", + "StorageCapacity": 300, + "SubnetIds": [ + "subnet-1234abcd" + ], + "Tags": [ + { + "Key": "Name", + "Value": "MyFileSystem" + } + ], + "VpcId": "vpc-ab1234cd", + "WindowsConfiguration": { + "AutomaticBackupRetentionDays": 10, + "DailyAutomaticBackupStartTime": "06:00", + "ThroughputCapacity": 8, + "WeeklyMaintenanceStartTime": "3:06:00" + } + } + }, + "comments": { + }, + "description": "This operation updates an existing file system.", + "id": "to-update-a-file-system-1481840798595", + "title": "To update an existing file system" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/fsx/2018-03-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/fsx/2018-03-01/paginators-1.json new file mode 100644 index 0000000..6b645bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/fsx/2018-03-01/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "DescribeBackups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Backups" + }, + "DescribeFileSystems": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "FileSystems" + }, + "ListTagsForResource": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Tags" + }, + "DescribeStorageVirtualMachines": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "StorageVirtualMachines" + }, + "DescribeVolumes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Volumes" + }, + "DescribeS3AccessPointAttachments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "S3AccessPointAttachments" + }, + "DescribeSnapshots": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Snapshots" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/fsx/2018-03-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/fsx/2018-03-01/service-2.json.gz new file mode 100644 index 0000000..e4493c5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/fsx/2018-03-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/gamelift/2015-10-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/gamelift/2015-10-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b8418bc Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/gamelift/2015-10-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/gamelift/2015-10-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/gamelift/2015-10-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/gamelift/2015-10-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/gamelift/2015-10-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/gamelift/2015-10-01/paginators-1.json new file mode 100644 index 0000000..a5a9183 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/gamelift/2015-10-01/paginators-1.json @@ -0,0 +1,160 @@ +{ + "pagination": { + "DescribeFleetAttributes": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "FleetAttributes" + }, + "DescribeFleetCapacity": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "FleetCapacity" + }, + "DescribeFleetEvents": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "Events" + }, + "DescribeFleetUtilization": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "FleetUtilization" + }, + "DescribeGameSessionDetails": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "GameSessionDetails" + }, + "DescribeGameSessionQueues": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "GameSessionQueues" + }, + "DescribeGameSessions": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "GameSessions" + }, + "DescribeInstances": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "Instances" + }, + "DescribeMatchmakingConfigurations": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "Configurations" + }, + "DescribeMatchmakingRuleSets": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "RuleSets" + }, + "DescribePlayerSessions": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "PlayerSessions" + }, + "DescribeScalingPolicies": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "ScalingPolicies" + }, + "ListAliases": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "Aliases" + }, + "ListBuilds": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "Builds" + }, + "ListFleets": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "FleetIds" + }, + "SearchGameSessions": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "GameSessions" + }, + "DescribeGameServerInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "GameServerInstances" + }, + "ListGameServerGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "GameServerGroups" + }, + "ListGameServers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "GameServers" + }, + "ListScripts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Scripts" + }, + "ListCompute": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "ComputeList" + }, + "ListLocations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Locations" + }, + "ListContainerGroupDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "ContainerGroupDefinitions" + }, + "ListContainerFleets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "ContainerFleets" + }, + "ListContainerGroupDefinitionVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "ContainerGroupDefinitions" + }, + "ListFleetDeployments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "FleetDeployments" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/gamelift/2015-10-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/gamelift/2015-10-01/service-2.json.gz new file mode 100644 index 0000000..210cd73 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/gamelift/2015-10-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/gameliftstreams/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/gameliftstreams/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..471740e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/gameliftstreams/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/gameliftstreams/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/gameliftstreams/2018-05-10/paginators-1.json new file mode 100644 index 0000000..50ae824 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/gameliftstreams/2018-05-10/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListApplications": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListStreamGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListStreamSessions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListStreamSessionsByAccount": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListStreamUrls": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/gameliftstreams/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/gameliftstreams/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..631e5c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/gameliftstreams/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/gameliftstreams/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/gameliftstreams/2018-05-10/waiters-2.json new file mode 100644 index 0000000..2c064b0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/gameliftstreams/2018-05-10/waiters-2.json @@ -0,0 +1,88 @@ +{ + "version" : 2, + "waiters" : { + "ApplicationDeleted" : { + "description" : "Waits until an application is deleted", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "GetApplication", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + } ] + }, + "ApplicationReady" : { + "description" : "Waits until an application is ready", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "GetApplication", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Status", + "state" : "success", + "expected" : "READY" + }, { + "matcher" : "path", + "argument" : "Status", + "state" : "failure", + "expected" : "ERROR" + } ] + }, + "StreamGroupActive" : { + "description" : "Waits until a stream group is active", + "delay" : 30, + "maxAttempts" : 120, + "operation" : "GetStreamGroup", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "Status", + "state" : "failure", + "expected" : "ERROR" + }, { + "matcher" : "path", + "argument" : "Status", + "state" : "failure", + "expected" : "ACTIVE_WITH_ERRORS" + }, { + "matcher" : "path", + "argument" : "Status", + "state" : "failure", + "expected" : "DELETING" + } ] + }, + "StreamGroupDeleted" : { + "description" : "Waits until a stream group is deleted", + "delay" : 30, + "maxAttempts" : 60, + "operation" : "GetStreamGroup", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + } ] + }, + "StreamSessionActive" : { + "description" : "Waits until a stream session is active", + "delay" : 2, + "maxAttempts" : 60, + "operation" : "GetStreamSession", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "Status", + "state" : "failure", + "expected" : "ERROR" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/geo-maps/2020-11-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/geo-maps/2020-11-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..fcedd79 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/geo-maps/2020-11-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/geo-maps/2020-11-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/geo-maps/2020-11-19/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/geo-maps/2020-11-19/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/geo-maps/2020-11-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/geo-maps/2020-11-19/service-2.json.gz new file mode 100644 index 0000000..1d7934b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/geo-maps/2020-11-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/geo-maps/2020-11-19/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/geo-maps/2020-11-19/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/geo-maps/2020-11-19/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/geo-places/2020-11-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/geo-places/2020-11-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f52c223 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/geo-places/2020-11-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/geo-places/2020-11-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/geo-places/2020-11-19/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/geo-places/2020-11-19/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/geo-places/2020-11-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/geo-places/2020-11-19/service-2.json.gz new file mode 100644 index 0000000..99a5958 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/geo-places/2020-11-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/geo-places/2020-11-19/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/geo-places/2020-11-19/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/geo-places/2020-11-19/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/geo-routes/2020-11-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/geo-routes/2020-11-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..83cf3e0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/geo-routes/2020-11-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/geo-routes/2020-11-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/geo-routes/2020-11-19/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/geo-routes/2020-11-19/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/geo-routes/2020-11-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/geo-routes/2020-11-19/service-2.json.gz new file mode 100644 index 0000000..38020fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/geo-routes/2020-11-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/geo-routes/2020-11-19/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/geo-routes/2020-11-19/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/geo-routes/2020-11-19/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..bc9e4c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/examples-1.json new file mode 100644 index 0000000..7ecea25 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/examples-1.json @@ -0,0 +1,806 @@ +{ + "version": "1.0", + "examples": { + "AbortMultipartUpload": [ + { + "input": { + "accountId": "-", + "uploadId": "19gaRezEXAMPLES6Ry5YYdqthHOC_kGRCT03L9yetr220UmPtBYKk-OssZtLqyFu7sY1_lR7vgFuJV6NtcV5zpsJ", + "vaultName": "my-vault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example deletes an in-progress multipart upload to a vault named my-vault:", + "id": "f3d907f6-e71c-420c-8f71-502346a2c48a", + "title": "To abort a multipart upload identified by the upload ID" + } + ], + "AbortVaultLock": [ + { + "input": { + "accountId": "-", + "vaultName": "examplevault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example aborts the vault locking process if the vault lock is not in the Locked state for the vault named examplevault.", + "id": "to-abort-a-vault-lock-1481839357947", + "title": "To abort a vault lock" + } + ], + "AddTagsToVault": [ + { + "input": { + "Tags": { + "examplekey1": "examplevalue1", + "examplekey2": "examplevalue2" + }, + "accountId": "-", + "vaultName": "my-vault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example adds two tags to a my-vault.", + "id": "add-tags-to-vault-post-tags-add-1481663457694", + "title": "To add tags to a vault" + } + ], + "CompleteMultipartUpload": [ + { + "input": { + "accountId": "-", + "archiveSize": "3145728", + "checksum": "9628195fcdbcbbe76cdde456d4646fa7de5f219fb39823836d81f0cc0e18aa67", + "uploadId": "19gaRezEXAMPLES6Ry5YYdqthHOC_kGRCT03L9yetr220UmPtBYKk-OssZtLqyFu7sY1_lR7vgFuJV6NtcV5zpsJ", + "vaultName": "my-vault" + }, + "output": { + "archiveId": "NkbByEejwEggmBz2fTHgJrg0XBoDfjP4q6iu87-TjhqG6eGoOY9Z8i1_AUyUsuhPAdTqLHy8pTl5nfCFJmDl2yEZONi5L26Omw12vcs01MNGntHEQL8MBfGlqrEXAMPLEArchiveId", + "checksum": "9628195fcdbcbbe76cdde456d4646fa7de5f219fb39823836d81f0cc0e18aa67", + "location": "/111122223333/vaults/my-vault/archives/NkbByEejwEggmBz2fTHgJrg0XBoDfjP4q6iu87-TjhqG6eGoOY9Z8i1_AUyUsuhPAdTqLHy8pTl5nfCFJmDl2yEZONi5L26Omw12vcs01MNGntHEQL8MBfGlqrEXAMPLEArchiveId" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example completes a multipart upload for a 3 MiB archive.", + "id": "272aa0b8-e44c-4a64-add2-ad905a37984d", + "title": "To complete a multipart upload" + } + ], + "CompleteVaultLock": [ + { + "input": { + "accountId": "-", + "lockId": "AE863rKkWZU53SLW5be4DUcW", + "vaultName": "example-vault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example completes the vault locking process by transitioning the vault lock from the InProgress state to the Locked state.", + "id": "to-complete-a-vault-lock-1481839721312", + "title": "To complete a vault lock" + } + ], + "CreateVault": [ + { + "input": { + "accountId": "-", + "vaultName": "my-vault" + }, + "output": { + "location": "/111122223333/vaults/my-vault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a new vault named my-vault.", + "id": "1dc0313d-ace1-4e6c-9d13-1ec7813b14b7", + "title": "To create a new vault" + } + ], + "DeleteArchive": [ + { + "input": { + "accountId": "-", + "archiveId": "NkbByEejwEggmBz2fTHgJrg0XBoDfjP4q6iu87-TjhqG6eGoOY9Z8i1_AUyUsuhPAdTqLHy8pTl5nfCFJmDl2yEZONi5L26Omw12vcs01MNGntHEQL8MBfGlqrEXAMPLEArchiveId", + "vaultName": "examplevault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example deletes the archive specified by the archive ID.", + "id": "delete-archive-1481667809463", + "title": "To delete an archive" + } + ], + "DeleteVault": [ + { + "input": { + "accountId": "-", + "vaultName": "my-vault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example deletes a vault named my-vault:", + "id": "7f7f000b-4bdb-40d2-91e6-7c902f60f60f", + "title": "To delete a vault" + } + ], + "DeleteVaultAccessPolicy": [ + { + "input": { + "accountId": "-", + "vaultName": "examplevault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example deletes the access policy associated with the vault named examplevault.", + "id": "to-delete-the-vault-access-policy-1481840424677", + "title": "To delete the vault access policy" + } + ], + "DeleteVaultNotifications": [ + { + "input": { + "accountId": "-", + "vaultName": "examplevault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example deletes the notification configuration set for the vault named examplevault.", + "id": "to-delete-the-notification-configuration-set-for-a-vault-1481840646090", + "title": "To delete the notification configuration set for a vault" + } + ], + "DescribeJob": [ + { + "input": { + "accountId": "-", + "jobId": "zbxcm3Z_3z5UkoroF7SuZKrxgGoDc3RloGduS7Eg-RO47Yc6FxsdGBgf_Q2DK5Ejh18CnTS5XW4_XqlNHS61dsO4Cn", + "vaultName": "my-vault" + }, + "output": { + "Action": "InventoryRetrieval", + "Completed": false, + "CreationDate": "2015-07-17T20:23:41.616Z", + "InventoryRetrievalParameters": { + "Format": "JSON" + }, + "JobId": "zbxcm3Z_3z5UkoroF7SuZKrxgGoDc3RloGduS7Eg-RO47Yc6FxsdGBgf_Q2DK5Ejh18CnTS5XW4_XqlNHS61dsO4CnMW", + "StatusCode": "InProgress", + "VaultARN": "arn:aws:glacier:us-west-2:0123456789012:vaults/my-vault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example returns information about the previously initiated job specified by the job ID.", + "id": "to-get-information-about-a-job-you-previously-initiated-1481840928592", + "title": "To get information about a previously initiated job" + } + ], + "DescribeVault": [ + { + "input": { + "accountId": "-", + "vaultName": "my-vault" + }, + "output": { + "CreationDate": "2016-09-23T19:27:18.665Z", + "NumberOfArchives": 0, + "SizeInBytes": 0, + "VaultARN": "arn:aws:glacier:us-west-2:111122223333:vaults/my-vault", + "VaultName": "my-vault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example retrieves data about a vault named my-vault.", + "id": "3c1c6e9d-f5a2-427a-aa6a-f439eacfc05f", + "title": "To retrieve information about a vault" + } + ], + "GetDataRetrievalPolicy": [ + { + "input": { + "accountId": "-" + }, + "output": { + "Policy": { + "Rules": [ + { + "BytesPerHour": 10737418240, + "Strategy": "BytesPerHour" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example returns the current data retrieval policy for the account.", + "id": "to-get-the-current-data-retrieval-policy-for-the-account-1481851580439", + "title": "To get the current data retrieval policy for an account" + } + ], + "GetJobOutput": [ + { + "input": { + "accountId": "-", + "jobId": "zbxcm3Z_3z5UkoroF7SuZKrxgGoDc3RloGduS7Eg-RO47Yc6FxsdGBgf_Q2DK5Ejh18CnTS5XW4_XqlNHS61dsO4CnMW", + "range": "", + "vaultName": "my-vaul" + }, + "output": { + "acceptRanges": "bytes", + "body": "inventory-data", + "contentType": "application/json", + "status": 200 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example downloads the output of a previously initiated inventory retrieval job that is identified by the job ID.", + "id": "to-get-the-output-of-a-previously-initiated-job-1481848550859", + "title": "To get the output of a previously initiated job" + } + ], + "GetVaultAccessPolicy": [ + { + "input": { + "accountId": "-", + "vaultName": "example-vault" + }, + "output": { + "policy": { + "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"Define-owner-access-rights\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::999999999999:root\"},\"Action\":\"glacier:DeleteArchive\",\"Resource\":\"arn:aws:glacier:us-west-2:999999999999:vaults/examplevault\"}]}" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example retrieves the access-policy set on the vault named example-vault.", + "id": "to--get-the-access-policy-set-on-the-vault-1481936004590", + "title": "To get the access-policy set on the vault" + } + ], + "GetVaultLock": [ + { + "input": { + "accountId": "-", + "vaultName": "examplevault" + }, + "output": { + "CreationDate": "exampledate", + "ExpirationDate": "exampledate", + "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"Define-vault-lock\",\"Effect\":\"Deny\",\"Principal\":{\"AWS\":\"arn:aws:iam::999999999999:root\"},\"Action\":\"glacier:DeleteArchive\",\"Resource\":\"arn:aws:glacier:us-west-2:999999999999:vaults/examplevault\",\"Condition\":{\"NumericLessThanEquals\":{\"glacier:ArchiveAgeinDays\":\"365\"}}}]}", + "State": "InProgress" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example retrieves the attributes from the lock-policy subresource set on the vault named examplevault.", + "id": "to-retrieve-vault-lock-policy-related-attributes-that-are-set-on-a-vault-1481851363097", + "title": "To retrieve vault lock-policy related attributes that are set on a vault" + } + ], + "GetVaultNotifications": [ + { + "input": { + "accountId": "-", + "vaultName": "my-vault" + }, + "output": { + "vaultNotificationConfig": { + "Events": [ + "InventoryRetrievalCompleted", + "ArchiveRetrievalCompleted" + ], + "SNSTopic": "arn:aws:sns:us-west-2:0123456789012:my-vault" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example retrieves the notification-configuration for the vault named my-vault.", + "id": "to-get-the-notification-configuration-for-the-specified-vault-1481918746677", + "title": "To get the notification-configuration for the specified vault" + } + ], + "InitiateJob": [ + { + "input": { + "accountId": "-", + "jobParameters": { + "Description": "My inventory job", + "Format": "CSV", + "SNSTopic": "arn:aws:sns:us-west-2:111111111111:Glacier-InventoryRetrieval-topic-Example", + "Type": "inventory-retrieval" + }, + "vaultName": "examplevault" + }, + "output": { + "jobId": " HkF9p6o7yjhFx-K3CGl6fuSm6VzW9T7esGQfco8nUXVYwS0jlb5gq1JZ55yHgt5vP54ZShjoQzQVVh7vEXAMPLEjobID", + "location": "/111122223333/vaults/examplevault/jobs/HkF9p6o7yjhFx-K3CGl6fuSm6VzW9T7esGQfco8nUXVYwS0jlb5gq1JZ55yHgt5vP54ZShjoQzQVVh7vEXAMPLEjobID" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example initiates an inventory-retrieval job for the vault named examplevault.", + "id": "to-initiate-an-inventory-retrieval-job-1482186883826", + "title": "To initiate an inventory-retrieval job" + } + ], + "InitiateMultipartUpload": [ + { + "input": { + "accountId": "-", + "partSize": "1048576", + "vaultName": "my-vault" + }, + "output": { + "location": "/111122223333/vaults/my-vault/multipart-uploads/19gaRezEXAMPLES6Ry5YYdqthHOC_kGRCT03L9yetr220UmPtBYKk-OssZtLqyFu7sY1_lR7vgFuJV6NtcV5zpsJ", + "uploadId": "19gaRezEXAMPLES6Ry5YYdqthHOC_kGRCT03L9yetr220UmPtBYKk-OssZtLqyFu7sY1_lR7vgFuJV6NtcV5zpsJ" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example initiates a multipart upload to a vault named my-vault with a part size of 1 MiB (1024 x 1024 bytes) per file.", + "id": "72f2db19-3d93-4c74-b2ed-38703baacf49", + "title": "To initiate a multipart upload" + } + ], + "InitiateVaultLock": [ + { + "input": { + "accountId": "-", + "policy": { + "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"Define-vault-lock\",\"Effect\":\"Deny\",\"Principal\":{\"AWS\":\"arn:aws:iam::999999999999:root\"},\"Action\":\"glacier:DeleteArchive\",\"Resource\":\"arn:aws:glacier:us-west-2:999999999999:vaults/examplevault\",\"Condition\":{\"NumericLessThanEquals\":{\"glacier:ArchiveAgeinDays\":\"365\"}}}]}" + }, + "vaultName": "my-vault" + }, + "output": { + "lockId": "AE863rKkWZU53SLW5be4DUcW" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example initiates the vault locking process for the vault named my-vault.", + "id": "to-initiate-the-vault-locking-process-1481919693394", + "title": "To initiate the vault locking process" + } + ], + "ListJobs": [ + { + "input": { + "accountId": "-", + "vaultName": "my-vault" + }, + "output": { + "JobList": [ + { + "Action": "ArchiveRetrieval", + "ArchiveId": "kKB7ymWJVpPSwhGP6ycSOAekp9ZYe_--zM_mw6k76ZFGEIWQX-ybtRDvc2VkPSDtfKmQrj0IRQLSGsNuDp-AJVlu2ccmDSyDUmZwKbwbpAdGATGDiB3hHO0bjbGehXTcApVud_wyDw", + "ArchiveSHA256TreeHash": "9628195fcdbcbbe76cdde932d4646fa7de5f219fb39823836d81f0cc0e18aa67", + "ArchiveSizeInBytes": 3145728, + "Completed": false, + "CreationDate": "2015-07-17T21:16:13.840Z", + "JobDescription": "Retrieve archive on 2015-07-17", + "JobId": "l7IL5-EkXyEY9Ws95fClzIbk2O5uLYaFdAYOi-azsX_Z8V6NH4yERHzars8wTKYQMX6nBDI9cMNHzyZJO59-8N9aHWav", + "RetrievalByteRange": "0-3145727", + "SHA256TreeHash": "9628195fcdbcbbe76cdde932d4646fa7de5f219fb39823836d81f0cc0e18aa67", + "SNSTopic": "arn:aws:sns:us-west-2:0123456789012:my-vault", + "StatusCode": "InProgress", + "VaultARN": "arn:aws:glacier:us-west-2:0123456789012:vaults/my-vault" + }, + { + "Action": "InventoryRetrieval", + "Completed": false, + "CreationDate": "2015-07-17T20:23:41.616Z", + "InventoryRetrievalParameters": { + "Format": "JSON" + }, + "JobId": "zbxcm3Z_3z5UkoroF7SuZKrxgGoDc3RloGduS7Eg-RO47Yc6FxsdGBgf_Q2DK5Ejh18CnTS5XW4_XqlNHS61dsO4CnMW", + "StatusCode": "InProgress", + "VaultARN": "arn:aws:glacier:us-west-2:0123456789012:vaults/my-vault" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example lists jobs for the vault named my-vault.", + "id": "to-list-jobs-for-a-vault-1481920530537", + "title": "To list jobs for a vault" + } + ], + "ListMultipartUploads": [ + { + "input": { + "accountId": "-", + "vaultName": "examplevault" + }, + "output": { + "Marker": "null", + "UploadsList": [ + { + "ArchiveDescription": "archive 1", + "CreationDate": "2012-03-19T23:20:59.130Z", + "MultipartUploadId": "xsQdFIRsfJr20CW2AbZBKpRZAFTZSJIMtL2hYf8mvp8dM0m4RUzlaqoEye6g3h3ecqB_zqwB7zLDMeSWhwo65re4C4Ev", + "PartSizeInBytes": 4194304, + "VaultARN": "arn:aws:glacier:us-west-2:012345678901:vaults/examplevault" + }, + { + "ArchiveDescription": "archive 2", + "CreationDate": "2012-04-01T15:00:00.000Z", + "MultipartUploadId": "nPyGOnyFcx67qqX7E-0tSGiRi88hHMOwOxR-_jNyM6RjVMFfV29lFqZ3rNsSaWBugg6OP92pRtufeHdQH7ClIpSF6uJc", + "PartSizeInBytes": 4194304, + "VaultARN": "arn:aws:glacier:us-west-2:012345678901:vaults/examplevault" + }, + { + "ArchiveDescription": "archive 3", + "CreationDate": "2012-03-20T17:03:43.221Z", + "MultipartUploadId": "qt-RBst_7yO8gVIonIBsAxr2t-db0pE4s8MNeGjKjGdNpuU-cdSAcqG62guwV9r5jh5mLyFPzFEitTpNE7iQfHiu1XoV", + "PartSizeInBytes": 4194304, + "VaultARN": "arn:aws:glacier:us-west-2:012345678901:vaults/examplevault" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example lists all the in-progress multipart uploads for the vault named examplevault.", + "id": "to-list-all-the-in-progress-multipart-uploads-for-a-vault-1481935250590", + "title": "To list all the in-progress multipart uploads for a vault" + } + ], + "ListParts": [ + { + "input": { + "accountId": "-", + "uploadId": "OW2fM5iVylEpFEMM9_HpKowRapC3vn5sSL39_396UW9zLFUWVrnRHaPjUJddQ5OxSHVXjYtrN47NBZ-khxOjyEXAMPLE", + "vaultName": "examplevault" + }, + "output": { + "ArchiveDescription": "archive description", + "CreationDate": "2012-03-20T17:03:43.221Z", + "Marker": "null", + "MultipartUploadId": "OW2fM5iVylEpFEMM9_HpKowRapC3vn5sSL39_396UW9zLFUWVrnRHaPjUJddQ5OxSHVXjYtrN47NBZ-khxOjyEXAMPLE", + "PartSizeInBytes": 4194304, + "Parts": [ + { + "RangeInBytes": "0-4194303", + "SHA256TreeHash": "01d34dabf7be316472c93b1ef80721f5d4" + }, + { + "RangeInBytes": "4194304-8388607", + "SHA256TreeHash": "0195875365afda349fc21c84c099987164" + } + ], + "VaultARN": "arn:aws:glacier:us-west-2:012345678901:vaults/demo1-vault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example lists all the parts of a multipart upload.", + "id": "to-list-the-parts-of-an-archive-that-have-been-uploaded-in-a-multipart-upload-1481921767590", + "title": "To list the parts of an archive that have been uploaded in a multipart upload" + } + ], + "ListProvisionedCapacity": [ + { + "input": { + "accountId": "-" + }, + "output": { + "ProvisionedCapacityList": [ + { + "CapacityId": "zSaq7NzHFQDANTfQkDen4V7z", + "ExpirationDate": "2016-12-12T00:00:00.000Z", + "StartDate": "2016-11-11T20:11:51.095Z" + }, + { + "CapacityId": "yXaq7NzHFQNADTfQkDen4V7z", + "ExpirationDate": "2017-01-15T00:00:00.000Z", + "StartDate": "2016-12-13T20:11:51.095Z" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example lists the provisioned capacity units for an account.", + "id": "to-list-the-provisioned-capacity-units-for-an-account-1481923656130", + "title": "To list the provisioned capacity units for an account" + } + ], + "ListTagsForVault": [ + { + "input": { + "accountId": "-", + "vaultName": "examplevault" + }, + "output": { + "Tags": { + "date": "july2015", + "id": "1234" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example lists all the tags attached to the vault examplevault.", + "id": "list-tags-for-vault-1481755839720", + "title": "To list the tags for a vault" + } + ], + "ListVaults": [ + { + "input": { + "accountId": "-", + "limit": "", + "marker": "" + }, + "output": { + "VaultList": [ + { + "CreationDate": "2015-04-06T21:23:45.708Z", + "LastInventoryDate": "2015-04-07T00:26:19.028Z", + "NumberOfArchives": 1, + "SizeInBytes": 3178496, + "VaultARN": "arn:aws:glacier:us-west-2:0123456789012:vaults/my-vault", + "VaultName": "my-vault" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example lists all vaults owned by the specified AWS account.", + "id": "list-vaults-1481753006990", + "title": "To list all vaults owned by the calling user's account" + } + ], + "PurchaseProvisionedCapacity": [ + { + "input": { + "accountId": "-" + }, + "output": { + "capacityId": "zSaq7NzHFQDANTfQkDen4V7z" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example purchases provisioned capacity unit for an AWS account.", + "id": "to-purchases-a-provisioned-capacity-unit-for-an-aws-account-1481927446662", + "title": "To purchases a provisioned capacity unit for an AWS account" + } + ], + "RemoveTagsFromVault": [ + { + "input": { + "TagKeys": [ + "examplekey1", + "examplekey2" + ], + "accountId": "-", + "vaultName": "examplevault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example removes two tags from the vault named examplevault.", + "id": "remove-tags-from-vault-1481754998801", + "title": "To remove tags from a vault" + } + ], + "SetDataRetrievalPolicy": [ + { + "input": { + "Policy": { + "Rules": [ + { + "BytesPerHour": 10737418240, + "Strategy": "BytesPerHour" + } + ] + }, + "accountId": "-" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example sets and then enacts a data retrieval policy.", + "id": "to-set-and-then-enact-a-data-retrieval-policy--1481928352408", + "title": "To set and then enact a data retrieval policy " + } + ], + "SetVaultAccessPolicy": [ + { + "input": { + "accountId": "-", + "policy": { + "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"Define-owner-access-rights\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::999999999999:root\"},\"Action\":\"glacier:DeleteArchive\",\"Resource\":\"arn:aws:glacier:us-west-2:999999999999:vaults/examplevault\"}]}" + }, + "vaultName": "examplevault" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example configures an access policy for the vault named examplevault.", + "id": "to--set-the-access-policy-on-a-vault-1482185872517", + "title": "To set the access-policy on a vault" + } + ], + "SetVaultNotifications": [ + { + "input": { + "accountId": "-", + "vaultName": "examplevault", + "vaultNotificationConfig": { + "Events": [ + "ArchiveRetrievalCompleted", + "InventoryRetrievalCompleted" + ], + "SNSTopic": "arn:aws:sns:us-west-2:012345678901:mytopic" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example sets the examplevault notification configuration.", + "id": "to-configure-a-vault-to-post-a-message-to-an-amazon-simple-notification-service-amazon-sns-topic-when-jobs-complete-1482186397475", + "title": "To configure a vault to post a message to an Amazon SNS topic when jobs complete" + } + ], + "UploadArchive": [ + { + "input": { + "accountId": "-", + "archiveDescription": "", + "body": "example-data-to-upload", + "checksum": "", + "vaultName": "my-vault" + }, + "output": { + "archiveId": "kKB7ymWJVpPSwhGP6ycSOAekp9ZYe_--zM_mw6k76ZFGEIWQX-ybtRDvc2VkPSDtfKmQrj0IRQLSGsNuDp-AJVlu2ccmDSyDUmZwKbwbpAdGATGDiB3hHO0bjbGehXTcApVud_wyDw", + "checksum": "969fb39823836d81f0cc028195fcdbcbbe76cdde932d4646fa7de5f21e18aa67", + "location": "/0123456789012/vaults/my-vault/archives/kKB7ymWJVpPSwhGP6ycSOAekp9ZYe_--zM_mw6k76ZFGEIWQX-ybtRDvc2VkPSDtfKmQrj0IRQLSGsNuDp-AJVlu2ccmDSyDUmZwKbwbpAdGATGDiB3hHO0bjbGehXTcApVud_wyDw" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example adds an archive to a vault.", + "id": "upload-archive-1481668510494", + "title": "To upload an archive" + } + ], + "UploadMultipartPart": [ + { + "input": { + "accountId": "-", + "body": "part1", + "checksum": "c06f7cd4baacb087002a99a5f48bf953", + "range": "bytes 0-1048575/*", + "uploadId": "19gaRezEXAMPLES6Ry5YYdqthHOC_kGRCT03L9yetr220UmPtBYKk-OssZtLqyFu7sY1_lR7vgFuJV6NtcV5zpsJ", + "vaultName": "examplevault" + }, + "output": { + "checksum": "c06f7cd4baacb087002a99a5f48bf953" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The example uploads the first 1 MiB (1024 x 1024 bytes) part of an archive.", + "id": "to-upload-the-first-part-of-an-archive-1481835899519", + "title": "To upload the first part of an archive" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/paginators-1.json new file mode 100644 index 0000000..6969143 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListJobs": { + "input_token": "marker", + "output_token": "Marker", + "limit_key": "limit", + "result_key": "JobList" + }, + "ListMultipartUploads": { + "input_token": "marker", + "output_token": "Marker", + "limit_key": "limit", + "result_key": "UploadsList" + }, + "ListParts": { + "input_token": "marker", + "output_token": "Marker", + "limit_key": "limit", + "result_key": "Parts" + }, + "ListVaults": { + "input_token": "marker", + "output_token": "Marker", + "limit_key": "limit", + "result_key": "VaultList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/service-2.json.gz new file mode 100644 index 0000000..de3a1cb Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/waiters-2.json new file mode 100644 index 0000000..07a64a0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/glacier/2012-06-01/waiters-2.json @@ -0,0 +1,39 @@ +{ + "version": 2, + "waiters": { + "VaultExists": { + "operation": "DescribeVault", + "delay": 3, + "maxAttempts": 15, + "acceptors": [ + { + "state": "success", + "matcher": "status", + "expected": 200 + }, + { + "state": "retry", + "matcher": "error", + "expected": "ResourceNotFoundException" + } + ] + }, + "VaultNotExists": { + "operation": "DescribeVault", + "delay": 3, + "maxAttempts": 15, + "acceptors": [ + { + "state": "retry", + "matcher": "status", + "expected": 200 + }, + { + "state": "success", + "matcher": "error", + "expected": "ResourceNotFoundException" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/globalaccelerator/2018-08-08/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/globalaccelerator/2018-08-08/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..176779a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/globalaccelerator/2018-08-08/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/globalaccelerator/2018-08-08/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/globalaccelerator/2018-08-08/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/globalaccelerator/2018-08-08/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/globalaccelerator/2018-08-08/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/globalaccelerator/2018-08-08/paginators-1.json new file mode 100644 index 0000000..2a0f825 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/globalaccelerator/2018-08-08/paginators-1.json @@ -0,0 +1,70 @@ +{ + "pagination": { + "ListAccelerators": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Accelerators" + }, + "ListEndpointGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EndpointGroups" + }, + "ListListeners": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Listeners" + }, + "ListByoipCidrs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ByoipCidrs" + }, + "ListCustomRoutingAccelerators": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Accelerators" + }, + "ListCustomRoutingListeners": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Listeners" + }, + "ListCustomRoutingPortMappings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PortMappings" + }, + "ListCustomRoutingPortMappingsByDestination": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DestinationPortMappings" + }, + "ListCustomRoutingEndpointGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EndpointGroups" + }, + "ListCrossAccountAttachments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CrossAccountAttachments" + }, + "ListCrossAccountResources": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CrossAccountResources" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/globalaccelerator/2018-08-08/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/globalaccelerator/2018-08-08/service-2.json.gz new file mode 100644 index 0000000..55149c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/globalaccelerator/2018-08-08/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..90ba2bb Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/paginators-1.json new file mode 100644 index 0000000..eb4ad1d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/paginators-1.json @@ -0,0 +1,217 @@ +{ + "pagination": { + "GetJobs": { + "result_key": "Jobs", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetPartitions": { + "result_key": "Partitions", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetDatabases": { + "result_key": "DatabaseList", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetClassifiers": { + "result_key": "Classifiers", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetTableVersions": { + "result_key": "TableVersions", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetCrawlers": { + "result_key": "Crawlers", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetDevEndpoints": { + "result_key": "DevEndpoints", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetJobRuns": { + "result_key": "JobRuns", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetTriggers": { + "result_key": "Triggers", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetTables": { + "result_key": "TableList", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetUserDefinedFunctions": { + "result_key": "UserDefinedFunctions", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetCrawlerMetrics": { + "result_key": "CrawlerMetricsList", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetConnections": { + "result_key": "ConnectionList", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetSecurityConfigurations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SecurityConfigurations" + }, + "GetPartitionIndexes": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "PartitionIndexDescriptorList" + }, + "GetResourcePolicies": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "GetResourcePoliciesResponseList" + }, + "ListRegistries": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Registries" + }, + "ListSchemaVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Schemas" + }, + "ListSchemas": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Schemas" + }, + "ListUsageProfiles": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Profiles" + }, + "GetWorkflowRuns": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Runs" + }, + "ListBlueprints": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Blueprints" + }, + "ListJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "JobNames" + }, + "ListTriggers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TriggerNames" + }, + "ListWorkflows": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Workflows" + }, + "ListTableOptimizerRuns": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TableOptimizerRuns" + }, + "DescribeEntity": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Fields" + }, + "ListConnectionTypes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ConnectionTypes" + }, + "ListEntities": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Entities" + }, + "ListMaterializedViewRefreshTaskRuns": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MaterializedViewRefreshTaskRuns" + }, + "ListAssetTypes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "ListFormTypes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "ListGlossaries": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "ListGlossaryTerms": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "ListIterableForms": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "SearchAssets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/paginators-1.sdk-extras.json new file mode 100644 index 0000000..4d788b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/paginators-1.sdk-extras.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "merge": { + "pagination": { + "ListTableOptimizerRuns": { + "non_aggregate_keys": [ + "CatalogId", + "DatabaseName", + "TableName" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/service-2.json.gz new file mode 100644 index 0000000..4caa8c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/glue/2017-03-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..276980d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/paginators-1.json new file mode 100644 index 0000000..55d05f2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListPermissions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "permissions" + }, + "ListWorkspaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workspaces" + }, + "ListVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "grafanaVersions" + }, + "ListWorkspaceServiceAccountTokens": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "serviceAccountTokens" + }, + "ListWorkspaceServiceAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "serviceAccounts" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/paginators-1.sdk-extras.json new file mode 100644 index 0000000..421e2f6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/paginators-1.sdk-extras.json @@ -0,0 +1,18 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListWorkspaceServiceAccounts": { + "non_aggregate_keys": [ + "workspaceId" + ] + }, + "ListWorkspaceServiceAccountTokens": { + "non_aggregate_keys": [ + "serviceAccountId", + "workspaceId" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/service-2.json.gz new file mode 100644 index 0000000..9b297e2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/grafana/2020-08-18/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/greengrass/2017-06-07/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/greengrass/2017-06-07/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..335b2ef Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/greengrass/2017-06-07/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/greengrass/2017-06-07/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/greengrass/2017-06-07/paginators-1.json new file mode 100644 index 0000000..303b438 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/greengrass/2017-06-07/paginators-1.json @@ -0,0 +1,118 @@ +{ + "pagination": { + "ListBulkDeploymentDetailedReports": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Deployments" + }, + "ListBulkDeployments": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "BulkDeployments" + }, + "ListConnectorDefinitionVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Versions" + }, + "ListConnectorDefinitions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Definitions" + }, + "ListCoreDefinitionVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Versions" + }, + "ListCoreDefinitions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Definitions" + }, + "ListDeployments": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Deployments" + }, + "ListDeviceDefinitionVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Versions" + }, + "ListDeviceDefinitions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Definitions" + }, + "ListFunctionDefinitionVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Versions" + }, + "ListFunctionDefinitions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Definitions" + }, + "ListGroupVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Versions" + }, + "ListGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Groups" + }, + "ListLoggerDefinitionVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Versions" + }, + "ListLoggerDefinitions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Definitions" + }, + "ListResourceDefinitionVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Versions" + }, + "ListResourceDefinitions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Definitions" + }, + "ListSubscriptionDefinitionVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Versions" + }, + "ListSubscriptionDefinitions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Definitions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/greengrass/2017-06-07/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/greengrass/2017-06-07/service-2.json.gz new file mode 100644 index 0000000..29e87f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/greengrass/2017-06-07/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/greengrassv2/2020-11-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/greengrassv2/2020-11-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..335b2ef Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/greengrassv2/2020-11-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/greengrassv2/2020-11-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/greengrassv2/2020-11-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/greengrassv2/2020-11-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/greengrassv2/2020-11-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/greengrassv2/2020-11-30/paginators-1.json new file mode 100644 index 0000000..2e2af05 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/greengrassv2/2020-11-30/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "ListComponentVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "componentVersions" + }, + "ListComponents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "components" + }, + "ListCoreDevices": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "coreDevices" + }, + "ListDeployments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "deployments" + }, + "ListEffectiveDeployments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "effectiveDeployments" + }, + "ListInstalledComponents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "installedComponents" + }, + "ListClientDevicesAssociatedWithCoreDevice": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "associatedClientDevices" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/greengrassv2/2020-11-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/greengrassv2/2020-11-30/service-2.json.gz new file mode 100644 index 0000000..a23319f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/greengrassv2/2020-11-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d410650 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/paginators-1.json new file mode 100644 index 0000000..58c44ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "ListConfigs": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "configList" + }, + "ListContacts": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "contactList" + }, + "ListDataflowEndpointGroups": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "dataflowEndpointGroupList" + }, + "ListMissionProfiles": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "missionProfileList" + }, + "ListGroundStations": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "groundStationList" + }, + "ListSatellites": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "satellites" + }, + "ListEphemerides": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "ephemerides" + }, + "ListAntennas": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "antennaList" + }, + "ListContactVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "contactVersionsList" + }, + "ListGroundStationReservations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "reservationList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/service-2.json.gz new file mode 100644 index 0000000..f600b63 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/waiters-2.json new file mode 100644 index 0000000..0210b36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/groundstation/2019-05-23/waiters-2.json @@ -0,0 +1,39 @@ +{ + "version" : 2, + "waiters" : { + "ContactScheduled" : { + "description" : "Waits until a contact has been scheduled", + "delay" : 5, + "maxAttempts" : 180, + "operation" : "DescribeContact", + "acceptors" : [ { + "matcher" : "path", + "argument" : "contactStatus", + "state" : "failure", + "expected" : "FAILED_TO_SCHEDULE" + }, { + "matcher" : "path", + "argument" : "contactStatus", + "state" : "success", + "expected" : "SCHEDULED" + } ] + }, + "ContactUpdated" : { + "description" : "Waits until a contact has been updated", + "delay" : 5, + "maxAttempts" : 180, + "operation" : "DescribeContactVersion", + "acceptors" : [ { + "matcher" : "path", + "argument" : "version.status", + "state" : "failure", + "expected" : "FAILED_TO_UPDATE" + }, { + "matcher" : "path", + "argument" : "version.status", + "state" : "success", + "expected" : "ACTIVE" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/guardduty/2017-11-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/guardduty/2017-11-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..87601ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/guardduty/2017-11-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/guardduty/2017-11-28/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/guardduty/2017-11-28/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/guardduty/2017-11-28/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/guardduty/2017-11-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/guardduty/2017-11-28/paginators-1.json new file mode 100644 index 0000000..33bb87d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/guardduty/2017-11-28/paginators-1.json @@ -0,0 +1,106 @@ +{ + "pagination": { + "ListDetectors": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DetectorIds" + }, + "ListFindings": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "FindingIds" + }, + "ListIPSets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "IpSetIds" + }, + "ListThreatIntelSets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ThreatIntelSetIds" + }, + "ListInvitations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Invitations" + }, + "ListMembers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Members" + }, + "ListFilters": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "FilterNames" + }, + "ListOrganizationAdminAccounts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AdminAccounts" + }, + "DescribeMalwareScans": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Scans" + }, + "ListCoverage": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Resources" + }, + "ListThreatEntitySets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ThreatEntitySetIds" + }, + "ListTrustedEntitySets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TrustedEntitySetIds" + }, + "ListMalwareScans": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Scans" + }, + "ListInvestigations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Investigations" + }, + "ListCustomDetectionRuleAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RuleAssociations" + }, + "ListCustomDetectionRuleOrgConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Configurations" + }, + "ListCustomDetectionRules": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Rules" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/guardduty/2017-11-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/guardduty/2017-11-28/service-2.json.gz new file mode 100644 index 0000000..f060eaf Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/guardduty/2017-11-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/health/2016-08-04/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/health/2016-08-04/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..13536f5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/health/2016-08-04/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/health/2016-08-04/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/health/2016-08-04/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/health/2016-08-04/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/health/2016-08-04/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/health/2016-08-04/paginators-1.json new file mode 100644 index 0000000..5109481 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/health/2016-08-04/paginators-1.json @@ -0,0 +1,52 @@ +{ + "pagination": { + "DescribeAffectedEntities": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "entities" + }, + "DescribeEventAggregates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "eventAggregates" + }, + "DescribeEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "events" + }, + "DescribeEventTypes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "eventTypes" + }, + "DescribeAffectedAccountsForOrganization": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "affectedAccounts", + "non_aggregate_keys": [ + "eventScopeCode" + ] + }, + "DescribeAffectedEntitiesForOrganization": { + "input_token": "nextToken", + "limit_key": "maxResults", + "non_aggregate_keys": [ + "failedSet" + ], + "output_token": "nextToken", + "result_key": "entities" + }, + "DescribeEventsForOrganization": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "events" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/health/2016-08-04/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/health/2016-08-04/service-2.json.gz new file mode 100644 index 0000000..9847686 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/health/2016-08-04/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1daac95 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/paginators-1.json new file mode 100644 index 0000000..dcc39b6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListDataTransformationJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListDataTransformationProfileVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListDataTransformationProfiles": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/service-2.json.gz new file mode 100644 index 0000000..154348a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/waiters-2.json new file mode 100644 index 0000000..60b26c0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/healthlake/2017-07-01/waiters-2.json @@ -0,0 +1,114 @@ +{ + "version" : 2, + "waiters" : { + "DataTransformationJobCompleted" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "DescribeDataTransformationJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "TransformationJobProperties.JobStatus", + "state" : "success", + "expected" : "COMPLETED" + }, { + "matcher" : "path", + "argument" : "TransformationJobProperties.JobStatus", + "state" : "success", + "expected" : "COMPLETED_WITH_ERRORS" + }, { + "matcher" : "path", + "argument" : "TransformationJobProperties.JobStatus", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "ResourceNotFoundException" + } ] + }, + "FHIRDatastoreActive" : { + "delay" : 60, + "maxAttempts" : 5, + "operation" : "DescribeFHIRDatastore", + "acceptors" : [ { + "matcher" : "path", + "argument" : "DatastoreProperties.DatastoreStatus", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "DatastoreProperties.DatastoreStatus", + "state" : "failure", + "expected" : "CREATE_FAILED" + }, { + "matcher" : "path", + "argument" : "DatastoreProperties.DatastoreStatus", + "state" : "failure", + "expected" : "DELETED" + } ] + }, + "FHIRDatastoreDeleted" : { + "delay" : 120, + "maxAttempts" : 5, + "operation" : "DescribeFHIRDatastore", + "acceptors" : [ { + "matcher" : "path", + "argument" : "DatastoreProperties.DatastoreStatus", + "state" : "success", + "expected" : "DELETED" + } ] + }, + "FHIRExportJobCompleted" : { + "delay" : 120, + "maxAttempts" : 5, + "operation" : "DescribeFHIRExportJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "ExportJobProperties.JobStatus", + "state" : "success", + "expected" : "COMPLETED" + }, { + "matcher" : "path", + "argument" : "ExportJobProperties.JobStatus", + "state" : "success", + "expected" : "COMPLETED_WITH_ERRORS" + }, { + "matcher" : "path", + "argument" : "ExportJobProperties.JobStatus", + "state" : "failure", + "expected" : "CANCEL_COMPLETED" + }, { + "matcher" : "path", + "argument" : "ExportJobProperties.JobStatus", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "ExportJobProperties.JobStatus", + "state" : "failure", + "expected" : "CANCEL_FAILED" + } ] + }, + "FHIRImportJobCompleted" : { + "delay" : 120, + "maxAttempts" : 5, + "operation" : "DescribeFHIRImportJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "ImportJobProperties.JobStatus", + "state" : "success", + "expected" : "COMPLETED" + }, { + "matcher" : "path", + "argument" : "ImportJobProperties.JobStatus", + "state" : "success", + "expected" : "COMPLETED_WITH_ERRORS" + }, { + "matcher" : "path", + "argument" : "ImportJobProperties.JobStatus", + "state" : "failure", + "expected" : "FAILED" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/iam-toolbox/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iam-toolbox/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..bad13b9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iam-toolbox/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iam-toolbox/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/iam-toolbox/2018-05-10/paginators-1.json new file mode 100644 index 0000000..b75364b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iam-toolbox/2018-05-10/paginators-1.json @@ -0,0 +1,9 @@ +{ + "pagination": { + "GetRequestAuthorizationDetails": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "evaluations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iam-toolbox/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iam-toolbox/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..d43a07b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iam-toolbox/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iam-toolbox/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/iam-toolbox/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iam-toolbox/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..378f3f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/examples-1.json new file mode 100644 index 0000000..cd3a94a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/examples-1.json @@ -0,0 +1,1577 @@ +{ + "version": "1.0", + "examples": { + "AddClientIDToOpenIDConnectProvider": [ + { + "input": { + "ClientID": "my-application-ID", + "OpenIDConnectProviderArn": "arn:aws:iam::123456789012:oidc-provider/server.example.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following add-client-id-to-open-id-connect-provider command adds the client ID my-application-ID to the OIDC provider named server.example.com:", + "id": "028e91f4-e2a6-4d59-9e3b-4965a3fb19be", + "title": "To add a client ID (audience) to an Open-ID Connect (OIDC) provider" + } + ], + "AddRoleToInstanceProfile": [ + { + "input": { + "InstanceProfileName": "Webserver", + "RoleName": "S3Access" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command adds the role named S3Access to the instance profile named Webserver:", + "id": "c107fac3-edb6-4827-8a71-8863ec91c81f", + "title": "To add a role to an instance profile" + } + ], + "AddUserToGroup": [ + { + "input": { + "GroupName": "Admins", + "UserName": "Bob" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command adds an IAM user named Bob to the IAM group named Admins:", + "id": "619c7e6b-09f8-4036-857b-51a6ea5027ca", + "title": "To add a user to an IAM group" + } + ], + "AttachGroupPolicy": [ + { + "input": { + "GroupName": "Finance", + "PolicyArn": "arn:aws:iam::aws:policy/ReadOnlyAccess" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command attaches the AWS managed policy named ReadOnlyAccess to the IAM group named Finance.", + "id": "87551489-86f0-45db-9889-759936778f2b", + "title": "To attach a managed policy to an IAM group" + } + ], + "AttachRolePolicy": [ + { + "input": { + "PolicyArn": "arn:aws:iam::aws:policy/ReadOnlyAccess", + "RoleName": "ReadOnlyRole" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command attaches the AWS managed policy named ReadOnlyAccess to the IAM role named ReadOnlyRole.", + "id": "3e1b8c7c-99c8-4fc4-a20c-131fe3f22c7e", + "title": "To attach a managed policy to an IAM role" + } + ], + "AttachUserPolicy": [ + { + "input": { + "PolicyArn": "arn:aws:iam::aws:policy/AdministratorAccess", + "UserName": "Alice" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command attaches the AWS managed policy named AdministratorAccess to the IAM user named Alice.", + "id": "1372ebd8-9475-4b1a-a479-23b6fd4b8b3e", + "title": "To attach a managed policy to an IAM user" + } + ], + "ChangePassword": [ + { + "input": { + "NewPassword": "]35d/{pB9Fo9wJ", + "OldPassword": "3s0K_;xh4~8XXI" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command changes the password for the current IAM user.", + "id": "3a80c66f-bffb-46df-947c-1e8fa583b470", + "title": "To change the password for your IAM user" + } + ], + "CreateAccessKey": [ + { + "input": { + "UserName": "Bob" + }, + "output": { + "AccessKey": { + "AccessKeyId": "AKIAIOSFODNN7EXAMPLE", + "CreateDate": "2015-03-09T18:39:23.411Z", + "SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY", + "Status": "Active", + "UserName": "Bob" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command creates an access key (access key ID and secret access key) for the IAM user named Bob.", + "id": "1fbb3211-4cf2-41db-8c20-ba58d9f5802d", + "title": "To create an access key for an IAM user" + } + ], + "CreateAccountAlias": [ + { + "input": { + "AccountAlias": "examplecorp" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command associates the alias examplecorp to your AWS account.", + "id": "5adaf6fb-94fc-4ca2-b825-2fbc2062add1", + "title": "To create an account alias" + } + ], + "CreateGroup": [ + { + "input": { + "GroupName": "Admins" + }, + "output": { + "Group": { + "Arn": "arn:aws:iam::123456789012:group/Admins", + "CreateDate": "2015-03-09T20:30:24.940Z", + "GroupId": "AIDGPMS9RO4H3FEXAMPLE", + "GroupName": "Admins", + "Path": "/" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command creates an IAM group named Admins.", + "id": "d5da2a90-5e69-4ef7-8ae8-4c33dc21fd21", + "title": "To create an IAM group" + } + ], + "CreateInstanceProfile": [ + { + "input": { + "InstanceProfileName": "Webserver" + }, + "output": { + "InstanceProfile": { + "Arn": "arn:aws:iam::123456789012:instance-profile/Webserver", + "CreateDate": "2015-03-09T20:33:19.626Z", + "InstanceProfileId": "AIPAJMBYC7DLSPEXAMPLE", + "InstanceProfileName": "Webserver", + "Path": "/", + "Roles": [ + + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command creates an instance profile named Webserver that is ready to have a role attached and then be associated with an EC2 instance.", + "id": "5d84e6ae-5921-4e39-8454-10232cd9ff9a", + "title": "To create an instance profile" + } + ], + "CreateLoginProfile": [ + { + "input": { + "Password": "h]6EszR}vJ*m", + "PasswordResetRequired": true, + "UserName": "Bob" + }, + "output": { + "LoginProfile": { + "CreateDate": "2015-03-10T20:55:40.274Z", + "PasswordResetRequired": true, + "UserName": "Bob" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command changes IAM user Bob's password and sets the flag that required Bob to change the password the next time he signs in.", + "id": "c63795bc-3444-40b3-89df-83c474ef88be", + "title": "To create an instance profile" + } + ], + "CreateOpenIDConnectProvider": [ + { + "input": { + "ClientIDList": [ + "my-application-id" + ], + "ThumbprintList": [ + "3768084dfb3d2b68b7897bf5f565da8efEXAMPLE" + ], + "Url": "https://server.example.com" + }, + "output": { + "OpenIDConnectProviderArn": "arn:aws:iam::123456789012:oidc-provider/server.example.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example defines a new OIDC provider in IAM with a client ID of my-application-id and pointing at the server with a URL of https://server.example.com.", + "id": "4e4a6bff-cc97-4406-922e-0ab4a82cdb63", + "title": "To create an instance profile" + } + ], + "CreateRole": [ + { + "input": { + "AssumeRolePolicyDocument": "", + "Path": "/", + "RoleName": "Test-Role" + }, + "output": { + "Role": { + "Arn": "arn:aws:iam::123456789012:role/Test-Role", + "AssumeRolePolicyDocument": "", + "CreateDate": "2013-06-07T20:43:32.821Z", + "Path": "/", + "RoleId": "AKIAIOSFODNN7EXAMPLE", + "RoleName": "Test-Role" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command creates a role named Test-Role and attaches a trust policy that you must convert from JSON to a string. Upon success, the response includes the same policy as a URL-encoded JSON string.", + "id": "eaaa4b5f-51f1-4f73-b0d3-30127040eff8", + "title": "To create an IAM role" + } + ], + "CreateUser": [ + { + "input": { + "UserName": "Bob" + }, + "output": { + "User": { + "Arn": "arn:aws:iam::123456789012:user/Bob", + "CreateDate": "2013-06-08T03:20:41.270Z", + "Path": "/", + "UserId": "AKIAIOSFODNN7EXAMPLE", + "UserName": "Bob" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following create-user command creates an IAM user named Bob in the current account.", + "id": "eb15f90b-e5f5-4af8-a594-e4e82b181a62", + "title": "To create an IAM user" + } + ], + "DeleteAccessKey": [ + { + "input": { + "AccessKeyId": "AKIDPMS9RO4H3FEXAMPLE", + "UserName": "Bob" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command deletes one access key (access key ID and secret access key) assigned to the IAM user named Bob.", + "id": "61a785a7-d30a-415a-ae18-ab9236e56871", + "title": "To delete an access key for an IAM user" + } + ], + "DeleteAccountAlias": [ + { + "input": { + "AccountAlias": "mycompany" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command removes the alias mycompany from the current AWS account:", + "id": "7abeca65-04a8-4500-a890-47f1092bf766", + "title": "To delete an account alias" + } + ], + "DeleteAccountPasswordPolicy": [ + { + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command removes the password policy from the current AWS account:", + "id": "9ddf755e-495c-49bc-ae3b-ea6cc9b8ebcf", + "title": "To delete the current account password policy" + } + ], + "DeleteGroupPolicy": [ + { + "input": { + "GroupName": "Admins", + "PolicyName": "ExamplePolicy" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command deletes the policy named ExamplePolicy from the group named Admins:", + "id": "e683f2bd-98a4-4fe0-bb66-33169c692d4a", + "title": "To delete a policy from an IAM group" + } + ], + "DeleteInstanceProfile": [ + { + "input": { + "InstanceProfileName": "ExampleInstanceProfile" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command deletes the instance profile named ExampleInstanceProfile", + "id": "12d74fb8-3433-49db-8171-a1fc764e354d", + "title": "To delete an instance profile" + } + ], + "DeleteLoginProfile": [ + { + "input": { + "UserName": "Bob" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command deletes the password for the IAM user named Bob.", + "id": "1fe57059-fc73-42e2-b992-517b7d573b5c", + "title": "To delete a password for an IAM user" + } + ], + "DeleteRole": [ + { + "input": { + "RoleName": "Test-Role" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command removes the role named Test-Role.", + "id": "053cdf74-9bda-44b8-bdbb-140fd5a32603", + "title": "To delete an IAM role" + } + ], + "DeleteRolePolicy": [ + { + "input": { + "PolicyName": "ExamplePolicy", + "RoleName": "Test-Role" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command removes the policy named ExamplePolicy from the role named Test-Role.", + "id": "9c667336-fde3-462c-b8f3-950800821e27", + "title": "To remove a policy from an IAM role" + } + ], + "DeleteSigningCertificate": [ + { + "input": { + "CertificateId": "TA7SMP42TDN5Z26OBPJE7EXAMPLE", + "UserName": "Anika" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command deletes the specified signing certificate for the IAM user named Anika.", + "id": "e3357586-ba9c-4070-b35b-d1a899b71987", + "title": "To delete a signing certificate for an IAM user" + } + ], + "DeleteUser": [ + { + "input": { + "UserName": "Bob" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command removes the IAM user named Bob from the current account.", + "id": "a13dc3f9-59fe-42d9-abbb-fb98b204fdf0", + "title": "To delete an IAM user" + } + ], + "DeleteUserPolicy": [ + { + "input": { + "PolicyName": "ExamplePolicy", + "UserName": "Juan" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following delete-user-policy command removes the specified policy from the IAM user named Juan:", + "id": "34f07ddc-9bc1-4f52-bc59-cd0a3ccd06c8", + "title": "To remove a policy from an IAM user" + } + ], + "DeleteVirtualMFADevice": [ + { + "input": { + "SerialNumber": "arn:aws:iam::123456789012:mfa/ExampleName" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following delete-virtual-mfa-device command removes the specified MFA device from the current AWS account.", + "id": "2933b08b-dbe7-4b89-b8c1-fdf75feea1ee", + "title": "To remove a virtual MFA device" + } + ], + "GenerateOrganizationsAccessReport": [ + { + "input": { + "EntityPath": "o-a1b2c3d4e5/r-f6g7h8i9j0example/ou-1a2b3c-k9l8m7n6o5example" + }, + "output": { + "JobId": "examplea-1234-b567-cde8-90fg123abcd4" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation generates a report for the organizational unit ou-rge0-awexample", + "id": "generateorganizationsaccessreport-ou", + "title": "To generate a service last accessed data report for an organizational unit" + } + ], + "GenerateServiceLastAccessedDetails": [ + { + "input": { + "Arn": "arn:aws:iam::123456789012:policy/ExamplePolicy1" + }, + "output": { + "JobId": "examplef-1305-c245-eba4-71fe298bcda7" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation generates a report for the policy: ExamplePolicy1", + "id": "generateaccessdata-policy-1541695178514", + "title": "To generate a service last accessed data report for a policy" + } + ], + "GetAccountPasswordPolicy": [ + { + "output": { + "PasswordPolicy": { + "AllowUsersToChangePassword": false, + "ExpirePasswords": false, + "HardExpiry": false, + "MaxPasswordAge": 90, + "MinimumPasswordLength": 8, + "PasswordReusePrevention": 12, + "RequireLowercaseCharacters": false, + "RequireNumbers": true, + "RequireSymbols": true, + "RequireUppercaseCharacters": false + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command displays details about the password policy for the current AWS account.", + "id": "5e4598c7-c425-431f-8af1-19073b3c4a5f", + "title": "To see the current account password policy" + } + ], + "GetAccountSummary": [ + { + "output": { + "SummaryMap": { + "AccessKeysPerUserQuota": 2, + "AccountAccessKeysPresent": 1, + "AccountMFAEnabled": 0, + "AccountSigningCertificatesPresent": 0, + "AttachedPoliciesPerGroupQuota": 10, + "AttachedPoliciesPerRoleQuota": 10, + "AttachedPoliciesPerUserQuota": 10, + "GlobalEndpointTokenVersion": 2, + "GroupPolicySizeQuota": 5120, + "Groups": 15, + "GroupsPerUserQuota": 10, + "GroupsQuota": 100, + "MFADevices": 6, + "MFADevicesInUse": 3, + "Policies": 8, + "PoliciesQuota": 1000, + "PolicySizeQuota": 5120, + "PolicyVersionsInUse": 22, + "PolicyVersionsInUseQuota": 10000, + "ServerCertificates": 1, + "ServerCertificatesQuota": 20, + "SigningCertificatesPerUserQuota": 2, + "UserPolicySizeQuota": 2048, + "Users": 27, + "UsersQuota": 5000, + "VersionsPerPolicyQuota": 5 + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command returns information about the IAM entity quotas and usage in the current AWS account.", + "id": "9d8447af-f344-45de-8219-2cebc3cce7f2", + "title": "To get information about IAM entity quotas and usage in the current account" + } + ], + "GetInstanceProfile": [ + { + "input": { + "InstanceProfileName": "ExampleInstanceProfile" + }, + "output": { + "InstanceProfile": { + "Arn": "arn:aws:iam::336924118301:instance-profile/ExampleInstanceProfile", + "CreateDate": "2013-06-12T23:52:02Z", + "InstanceProfileId": "AID2MAB8DPLSRHEXAMPLE", + "InstanceProfileName": "ExampleInstanceProfile", + "Path": "/", + "Roles": [ + { + "Arn": "arn:aws:iam::336924118301:role/Test-Role", + "AssumeRolePolicyDocument": "", + "CreateDate": "2013-01-09T06:33:26Z", + "Path": "/", + "RoleId": "AIDGPMS9RO4H3FEXAMPLE", + "RoleName": "Test-Role" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command gets information about the instance profile named ExampleInstanceProfile.", + "id": "463b9ba5-18cc-4608-9ccb-5a7c6b6e5fe7", + "title": "To get information about an instance profile" + } + ], + "GetLoginProfile": [ + { + "input": { + "UserName": "Anika" + }, + "output": { + "LoginProfile": { + "CreateDate": "2012-09-21T23:03:39Z", + "UserName": "Anika" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command gets information about the password for the IAM user named Anika.", + "id": "d6b580cc-909f-4925-9caa-d425cbc1ad47", + "title": "To get password information for an IAM user" + } + ], + "GetOrganizationsAccessReport": [ + { + "input": { + "JobId": "examplea-1234-b567-cde8-90fg123abcd4" + }, + "output": { + "AccessDetails": [ + { + "EntityPath": "o-a1b2c3d4e5/r-f6g7h8i9j0example/ou-1a2b3c-k9l8m7n6o5example/111122223333", + "LastAuthenticatedTime": "2019-05-25T16:29:52Z", + "Region": "us-east-1", + "ServiceName": "Amazon DynamoDB", + "ServiceNamespace": "dynamodb", + "TotalAuthenticatedEntities": 2 + }, + { + "EntityPath": "o-a1b2c3d4e5/r-f6g7h8i9j0example/ou-1a2b3c-k9l8m7n6o5example/123456789012", + "LastAuthenticatedTime": "2019-06-15T13:12:06Z", + "Region": "us-east-1", + "ServiceName": "AWS Identity and Access Management", + "ServiceNamespace": "iam", + "TotalAuthenticatedEntities": 4 + }, + { + "ServiceName": "Amazon Simple Storage Service", + "ServiceNamespace": "s3", + "TotalAuthenticatedEntities": 0 + } + ], + "IsTruncated": false, + "JobCompletionDate": "2019-06-18T19:47:35.241Z", + "JobCreationDate": "2019-06-18T19:47:31.466Z", + "JobStatus": "COMPLETED", + "NumberOfServicesAccessible": 3, + "NumberOfServicesNotAccessed": 1 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation gets details about the report with the job ID: examplea-1234-b567-cde8-90fg123abcd4", + "id": "getorganizationsaccessreport-ou", + "title": "To get details from a previously generated organizational unit report" + } + ], + "GetRole": [ + { + "input": { + "RoleName": "Test-Role" + }, + "output": { + "Role": { + "Arn": "arn:aws:iam::123456789012:role/Test-Role", + "AssumeRolePolicyDocument": "", + "CreateDate": "2013-04-18T05:01:58Z", + "MaxSessionDuration": 3600, + "Path": "/", + "RoleId": "AROADBQP57FF2AEXAMPLE", + "RoleLastUsed": { + "LastUsedDate": "2019-11-18T05:01:58Z", + "Region": "us-east-1" + }, + "RoleName": "Test-Role" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command gets information about the role named Test-Role.", + "id": "5b7d03a6-340c-472d-aa77-56425950d8b0", + "title": "To get information about an IAM role" + } + ], + "GetServiceLastAccessedDetails": [ + { + "input": { + "JobId": "examplef-1305-c245-eba4-71fe298bcda7" + }, + "output": { + "IsTruncated": false, + "JobCompletionDate": "2018-10-24T19:47:35.241Z", + "JobCreationDate": "2018-10-24T19:47:31.466Z", + "JobStatus": "COMPLETED", + "ServicesLastAccessed": [ + { + "LastAuthenticated": "2018-10-24T19:11:00Z", + "LastAuthenticatedEntity": "arn:aws:iam::123456789012:user/AWSExampleUser01", + "ServiceName": "AWS Identity and Access Management", + "ServiceNamespace": "iam", + "TotalAuthenticatedEntities": 2 + }, + { + "ServiceName": "Amazon Simple Storage Service", + "ServiceNamespace": "s3", + "TotalAuthenticatedEntities": 0 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation gets details about the report with the job ID: examplef-1305-c245-eba4-71fe298bcda7", + "id": "getserviceaccessdetails-policy-1541696298085", + "title": "To get details from a previously-generated report" + } + ], + "GetServiceLastAccessedDetailsWithEntities": [ + { + "input": { + "JobId": "examplef-1305-c245-eba4-71fe298bcda7", + "ServiceNamespace": "iam" + }, + "output": { + "EntityDetailsList": [ + { + "EntityInfo": { + "Arn": "arn:aws:iam::123456789012:user/AWSExampleUser01", + "Id": "AIDAEX2EXAMPLEB6IGCDC", + "Name": "AWSExampleUser01", + "Path": "/", + "Type": "USER" + }, + "LastAuthenticated": "2018-10-24T19:10:00Z" + }, + { + "EntityInfo": { + "Arn": "arn:aws:iam::123456789012:role/AWSExampleRole01", + "Id": "AROAEAEXAMPLEIANXSIU4", + "Name": "AWSExampleRole01", + "Path": "/", + "Type": "ROLE" + } + } + ], + "IsTruncated": false, + "JobCompletionDate": "2018-10-24T19:47:35.241Z", + "JobCreationDate": "2018-10-24T19:47:31.466Z", + "JobStatus": "COMPLETED" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation returns details about the entities that attempted to access the IAM service.", + "id": "getserviceaccessdetailsentity-policy-1541697621384", + "title": "To get sntity details from a previously-generated report" + } + ], + "GetUser": [ + { + "input": { + "UserName": "Bob" + }, + "output": { + "User": { + "Arn": "arn:aws:iam::123456789012:user/Bob", + "CreateDate": "2012-09-21T23:03:13Z", + "Path": "/", + "UserId": "AKIAIOSFODNN7EXAMPLE", + "UserName": "Bob" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command gets information about the IAM user named Bob.", + "id": "ede000a1-9e4c-40db-bd0a-d4f95e41a6ab", + "title": "To get information about an IAM user" + } + ], + "ListAccessKeys": [ + { + "input": { + "UserName": "Alice" + }, + "output": { + "AccessKeyMetadata": [ + { + "AccessKeyId": "AKIA111111111EXAMPLE", + "CreateDate": "2016-12-01T22:19:58Z", + "Status": "Active", + "UserName": "Alice" + }, + { + "AccessKeyId": "AKIA222222222EXAMPLE", + "CreateDate": "2016-12-01T22:20:01Z", + "Status": "Active", + "UserName": "Alice" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command lists the access keys IDs for the IAM user named Alice.", + "id": "15571463-ebea-411a-a021-1c76bd2a3625", + "title": "To list the access key IDs for an IAM user" + } + ], + "ListAccountAliases": [ + { + "input": { + }, + "output": { + "AccountAliases": [ + "exmaple-corporation" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command lists the aliases for the current account.", + "id": "e27b457a-16f9-4e05-a006-3df7b3472741", + "title": "To list account aliases" + } + ], + "ListGroupPolicies": [ + { + "input": { + "GroupName": "Admins" + }, + "output": { + "PolicyNames": [ + "AdminRoot", + "KeyPolicy" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command lists the names of in-line policies that are embedded in the IAM group named Admins.", + "id": "02de5095-2410-4d3a-ac1b-cc40234af68f", + "title": "To list the in-line policies for an IAM group" + } + ], + "ListGroups": [ + { + "input": { + }, + "output": { + "Groups": [ + { + "Arn": "arn:aws:iam::123456789012:group/Admins", + "CreateDate": "2016-12-15T21:40:08.121Z", + "GroupId": "AGPA1111111111EXAMPLE", + "GroupName": "Admins", + "Path": "/division_abc/subdivision_xyz/" + }, + { + "Arn": "arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/engineering/Test", + "CreateDate": "2016-11-30T14:10:01.156Z", + "GroupId": "AGP22222222222EXAMPLE", + "GroupName": "Test", + "Path": "/division_abc/subdivision_xyz/product_1234/engineering/" + }, + { + "Arn": "arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/Managers", + "CreateDate": "2016-06-12T20:14:52.032Z", + "GroupId": "AGPI3333333333EXAMPLE", + "GroupName": "Managers", + "Path": "/division_abc/subdivision_xyz/product_1234/" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command lists the IAM groups in the current account:", + "id": "b3ab1380-2a21-42fb-8e85-503f65512c66", + "title": "To list the IAM groups for the current account" + } + ], + "ListGroupsForUser": [ + { + "input": { + "UserName": "Bob" + }, + "output": { + "Groups": [ + { + "Arn": "arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/engineering/Test", + "CreateDate": "2016-11-30T14:10:01.156Z", + "GroupId": "AGP2111111111EXAMPLE", + "GroupName": "Test", + "Path": "/division_abc/subdivision_xyz/product_1234/engineering/" + }, + { + "Arn": "arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/Managers", + "CreateDate": "2016-06-12T20:14:52.032Z", + "GroupId": "AGPI222222222SEXAMPLE", + "GroupName": "Managers", + "Path": "/division_abc/subdivision_xyz/product_1234/" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command displays the groups that the IAM user named Bob belongs to.", + "id": "278ec2ee-fc28-4136-83fb-433af0ae46a2", + "title": "To list the groups that an IAM user belongs to" + } + ], + "ListPoliciesGrantingServiceAccess": [ + { + "input": { + "Arn": "arn:aws:iam::123456789012:user/ExampleUser01", + "ServiceNamespaces": [ + "iam", + "ec2" + ] + }, + "output": { + "IsTruncated": false, + "PoliciesGrantingServiceAccess": [ + { + "Policies": [ + { + "PolicyArn": "arn:aws:iam::123456789012:policy/ExampleIamPolicy", + "PolicyName": "ExampleIamPolicy", + "PolicyType": "MANAGED" + }, + { + "EntityName": "AWSExampleGroup1", + "EntityType": "GROUP", + "PolicyName": "ExampleGroup1Policy", + "PolicyType": "INLINE" + } + ], + "ServiceNamespace": "iam" + }, + { + "Policies": [ + { + "PolicyArn": "arn:aws:iam::123456789012:policy/ExampleEc2Policy", + "PolicyName": "ExampleEc2Policy", + "PolicyType": "MANAGED" + } + ], + "ServiceNamespace": "ec2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following operation lists policies that allow ExampleUser01 to access IAM or EC2.", + "id": "listpoliciesaccess-user-1541698749508", + "title": "To list policies that allow access to a service" + } + ], + "ListRoleTags": [ + { + "input": { + "RoleName": "taggedrole1" + }, + "output": { + "IsTruncated": false, + "Tags": [ + { + "Key": "Dept", + "Value": "12345" + }, + { + "Key": "Team", + "Value": "Accounting" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to list the tags attached to a role.", + "id": "to-list-the-tags-attached-to-an-iam-role-1506719238376", + "title": "To list the tags attached to an IAM role" + } + ], + "ListSigningCertificates": [ + { + "input": { + "UserName": "Bob" + }, + "output": { + "Certificates": [ + { + "CertificateBody": "-----BEGIN CERTIFICATE----------END CERTIFICATE-----", + "CertificateId": "TA7SMP42TDN5Z26OBPJE7EXAMPLE", + "Status": "Active", + "UploadDate": "2013-06-06T21:40:08Z", + "UserName": "Bob" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command lists the signing certificates for the IAM user named Bob.", + "id": "b4c10256-4fc9-457e-b3fd-4a110d4d73dc", + "title": "To list the signing certificates for an IAM user" + } + ], + "ListUserTags": [ + { + "input": { + "UserName": "anika" + }, + "output": { + "IsTruncated": false, + "Tags": [ + { + "Key": "Dept", + "Value": "12345" + }, + { + "Key": "Team", + "Value": "Accounting" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to list the tags attached to a user.", + "id": "to-list-the-tags-attached-to-an-iam-user-1506719473186", + "title": "To list the tags attached to an IAM user" + } + ], + "ListUsers": [ + { + "input": { + }, + "output": { + "Users": [ + { + "Arn": "arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/engineering/Juan", + "CreateDate": "2012-09-05T19:38:48Z", + "PasswordLastUsed": "2016-09-08T21:47:36Z", + "Path": "/division_abc/subdivision_xyz/engineering/", + "UserId": "AID2MAB8DPLSRHEXAMPLE", + "UserName": "Juan" + }, + { + "Arn": "arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/engineering/Anika", + "CreateDate": "2014-04-09T15:43:45Z", + "PasswordLastUsed": "2016-09-24T16:18:07Z", + "Path": "/division_abc/subdivision_xyz/engineering/", + "UserId": "AIDIODR4TAW7CSEXAMPLE", + "UserName": "Anika" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command lists the IAM users in the current account.", + "id": "9edfbd73-03d8-4d8a-9a79-76c85e8c8298", + "title": "To list IAM users" + } + ], + "ListVirtualMFADevices": [ + { + "input": { + }, + "output": { + "VirtualMFADevices": [ + { + "SerialNumber": "arn:aws:iam::123456789012:mfa/ExampleMFADevice" + }, + { + "SerialNumber": "arn:aws:iam::123456789012:mfa/Juan" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command lists the virtual MFA devices that have been configured for the current account.", + "id": "54f9ac18-5100-4070-bec4-fe5f612710d5", + "title": "To list virtual MFA devices" + } + ], + "PutGroupPolicy": [ + { + "input": { + "GroupName": "Admins", + "PolicyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}", + "PolicyName": "AllPerms" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command adds a policy named AllPerms to the IAM group named Admins.", + "id": "4bc17418-758f-4d0f-ab0c-4d00265fec2e", + "title": "To add a policy to a group" + } + ], + "PutRolePolicy": [ + { + "input": { + "PolicyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"s3:*\",\"Resource\":\"*\"}}", + "PolicyName": "S3AccessPolicy", + "RoleName": "S3Access" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command adds a permissions policy to the role named Test-Role.", + "id": "de62fd00-46c7-4601-9e0d-71d5fbb11ecb", + "title": "To attach a permissions policy to an IAM role" + } + ], + "PutUserPolicy": [ + { + "input": { + "PolicyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}", + "PolicyName": "AllAccessPolicy", + "UserName": "Bob" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command attaches a policy to the IAM user named Bob.", + "id": "2551ffc6-3576-4d39-823f-30b60bffc2c7", + "title": "To attach a policy to an IAM user" + } + ], + "RemoveRoleFromInstanceProfile": [ + { + "input": { + "InstanceProfileName": "ExampleInstanceProfile", + "RoleName": "Test-Role" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command removes the role named Test-Role from the instance profile named ExampleInstanceProfile.", + "id": "6d9f46f1-9f4a-4873-b403-51a85c5c627c", + "title": "To remove a role from an instance profile" + } + ], + "RemoveUserFromGroup": [ + { + "input": { + "GroupName": "Admins", + "UserName": "Bob" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command removes the user named Bob from the IAM group named Admins.", + "id": "fb54d5b4-0caf-41d8-af0e-10a84413f174", + "title": "To remove a user from an IAM group" + } + ], + "SetSecurityTokenServicePreferences": [ + { + "input": { + "GlobalEndpointTokenVersion": "v2Token" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command sets the STS global endpoint token to version 2. Version 2 tokens are valid in all Regions.", + "id": "61a785a7-d30a-415a-ae18-ab9236e56871", + "title": "To delete an access key for an IAM user" + } + ], + "TagRole": [ + { + "input": { + "RoleName": "taggedrole", + "Tags": [ + { + "Key": "Dept", + "Value": "Accounting" + }, + { + "Key": "CostCenter", + "Value": "12345" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to add tags to an existing role.", + "id": "to-add-a-tag-key-and-value-to-an-iam-role-1506718791513", + "title": "To add a tag key and value to an IAM role" + } + ], + "TagUser": [ + { + "input": { + "Tags": [ + { + "Key": "Dept", + "Value": "Accounting" + }, + { + "Key": "CostCenter", + "Value": "12345" + } + ], + "UserName": "anika" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to add tags to an existing user.", + "id": "to-add-a-tag-key-and-value-to-an-iam-user-1506719044227", + "title": "To add a tag key and value to an IAM user" + } + ], + "UntagRole": [ + { + "input": { + "RoleName": "taggedrole", + "TagKeys": [ + "Dept" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to remove a tag with the key 'Dept' from a role named 'taggedrole'.", + "id": "to-remove-a-tag-from-an-iam-role-1506719589943", + "title": "To remove a tag from an IAM role" + } + ], + "UntagUser": [ + { + "input": { + "TagKeys": [ + "Dept" + ], + "UserName": "anika" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to remove tags that are attached to a user named 'anika'.", + "id": "to-remove-a-tag-from-an-iam-user-1506719725554", + "title": "To remove a tag from an IAM user" + } + ], + "UpdateAccessKey": [ + { + "input": { + "AccessKeyId": "AKIAIOSFODNN7EXAMPLE", + "Status": "Inactive", + "UserName": "Bob" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command deactivates the specified access key (access key ID and secret access key) for the IAM user named Bob.", + "id": "02b556fd-e673-49b7-ab6b-f2f9035967d0", + "title": "To activate or deactivate an access key for an IAM user" + } + ], + "UpdateAccountPasswordPolicy": [ + { + "input": { + "MinimumPasswordLength": 8, + "RequireNumbers": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command sets the password policy to require a minimum length of eight characters and to require one or more numbers in the password:", + "id": "c263a1af-37dc-4423-8dba-9790284ef5e0", + "title": "To set or change the current account password policy" + } + ], + "UpdateAssumeRolePolicy": [ + { + "input": { + "PolicyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}", + "RoleName": "S3AccessForEC2Instances" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command updates the role trust policy for the role named Test-Role:", + "id": "c9150063-d953-4e99-9576-9685872006c6", + "title": "To update the trust policy for an IAM role" + } + ], + "UpdateGroup": [ + { + "input": { + "GroupName": "Test", + "NewGroupName": "Test-1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command changes the name of the IAM group Test to Test-1.", + "id": "f0cf1662-91ae-4278-a80e-7db54256ccba", + "title": "To rename an IAM group" + } + ], + "UpdateLoginProfile": [ + { + "input": { + "Password": "SomeKindOfPassword123!@#", + "UserName": "Bob" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command creates or changes the password for the IAM user named Bob.", + "id": "036d9498-ecdb-4ed6-a8d8-366c383d1487", + "title": "To change the password for an IAM user" + } + ], + "UpdateSigningCertificate": [ + { + "input": { + "CertificateId": "TA7SMP42TDN5Z26OBPJE7EXAMPLE", + "Status": "Inactive", + "UserName": "Bob" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command changes the status of a signing certificate for a user named Bob to Inactive.", + "id": "829aee7b-efc5-4b3b-84a5-7f899b38018d", + "title": "To change the active status of a signing certificate for an IAM user" + } + ], + "UpdateUser": [ + { + "input": { + "NewUserName": "Robert", + "UserName": "Bob" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command changes the name of the IAM user Bob to Robert. It does not change the user's path.", + "id": "275d53ed-347a-44e6-b7d0-a96276154352", + "title": "To change an IAM user's name" + } + ], + "UploadServerCertificate": [ + { + "input": { + "CertificateBody": "-----BEGIN CERTIFICATE----------END CERTIFICATE-----", + "Path": "/company/servercerts/", + "PrivateKey": "-----BEGIN DSA PRIVATE KEY----------END DSA PRIVATE KEY-----", + "ServerCertificateName": "ProdServerCert" + }, + "output": { + "ServerCertificateMetadata": { + "Arn": "arn:aws:iam::123456789012:server-certificate/company/servercerts/ProdServerCert", + "Expiration": "2012-05-08T01:02:03.004Z", + "Path": "/company/servercerts/", + "ServerCertificateId": "ASCA1111111111EXAMPLE", + "ServerCertificateName": "ProdServerCert", + "UploadDate": "2010-05-08T01:02:03.004Z" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following upload-server-certificate command uploads a server certificate to your AWS account:", + "id": "06eab6d1-ebf2-4bd9-839d-f7508b9a38b6", + "title": "To upload a server certificate to your AWS account" + } + ], + "UploadSigningCertificate": [ + { + "input": { + "CertificateBody": "-----BEGIN CERTIFICATE----------END CERTIFICATE-----", + "UserName": "Bob" + }, + "output": { + "Certificate": { + "CertificateBody": "-----BEGIN CERTIFICATE----------END CERTIFICATE-----", + "CertificateId": "ID123456789012345EXAMPLE", + "Status": "Active", + "UploadDate": "2015-06-06T21:40:08.121Z", + "UserName": "Bob" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following command uploads a signing certificate for the IAM user named Bob.", + "id": "e67489b6-7b73-4e30-9ed3-9a9e0231e458", + "title": "To upload a signing certificate for an IAM user" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/paginators-1.json new file mode 100644 index 0000000..91c09a2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/paginators-1.json @@ -0,0 +1,254 @@ +{ + "pagination": { + "GetAccountAuthorizationDetails": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": [ + "UserDetailList", + "GroupDetailList", + "RoleDetailList", + "Policies" + ] + }, + "GetGroup": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Users", + "non_aggregate_keys": [ + "Group" + ] + }, + "ListAccessKeys": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "AccessKeyMetadata" + }, + "ListAccountAliases": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "AccountAliases" + }, + "ListAttachedGroupPolicies": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "AttachedPolicies" + }, + "ListAttachedRolePolicies": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "AttachedPolicies" + }, + "ListAttachedUserPolicies": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "AttachedPolicies" + }, + "ListEntitiesForPolicy": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": [ + "PolicyGroups", + "PolicyUsers", + "PolicyRoles" + ] + }, + "ListGroupPolicies": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "PolicyNames" + }, + "ListGroups": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Groups" + }, + "ListGroupsForUser": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Groups" + }, + "ListInstanceProfiles": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "InstanceProfiles" + }, + "ListInstanceProfilesForRole": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "InstanceProfiles" + }, + "ListMFADevices": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "MFADevices" + }, + "ListPolicies": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Policies" + }, + "ListPolicyVersions": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Versions" + }, + "ListRolePolicies": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "PolicyNames" + }, + "ListRoles": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Roles" + }, + "ListServerCertificates": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "ServerCertificateMetadataList" + }, + "ListSigningCertificates": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Certificates" + }, + "ListSSHPublicKeys": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "SSHPublicKeys" + }, + "ListUserPolicies": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "PolicyNames" + }, + "ListUsers": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Users" + }, + "ListVirtualMFADevices": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "VirtualMFADevices" + }, + "SimulateCustomPolicy": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "EvaluationResults" + }, + "SimulatePrincipalPolicy": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "EvaluationResults" + }, + "ListUserTags": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Tags" + }, + "ListInstanceProfileTags": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Tags" + }, + "ListMFADeviceTags": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Tags" + }, + "ListOpenIDConnectProviderTags": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Tags" + }, + "ListPolicyTags": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Tags" + }, + "ListRoleTags": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Tags" + }, + "ListSAMLProviderTags": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Tags" + }, + "ListServerCertificateTags": { + "input_token": "Marker", + "limit_key": "MaxItems", + "more_results": "IsTruncated", + "output_token": "Marker", + "result_key": "Tags" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/service-2.json.gz new file mode 100644 index 0000000..27d013b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/waiters-2.json new file mode 100644 index 0000000..6248041 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iam/2010-05-08/waiters-2.json @@ -0,0 +1,73 @@ +{ + "version": 2, + "waiters": { + "InstanceProfileExists": { + "delay": 1, + "operation": "GetInstanceProfile", + "maxAttempts": 40, + "acceptors": [ + { + "expected": 200, + "matcher": "status", + "state": "success" + }, + { + "state": "retry", + "matcher": "status", + "expected": 404 + } + ] + }, + "UserExists": { + "delay": 1, + "operation": "GetUser", + "maxAttempts": 20, + "acceptors": [ + { + "state": "success", + "matcher": "status", + "expected": 200 + }, + { + "state": "retry", + "matcher": "error", + "expected": "NoSuchEntity" + } + ] + }, + "RoleExists": { + "delay": 1, + "operation": "GetRole", + "maxAttempts": 20, + "acceptors": [ + { + "state": "success", + "matcher": "status", + "expected": 200 + }, + { + "state": "retry", + "matcher": "error", + "expected": "NoSuchEntity" + } + ] + }, + "PolicyExists": { + "delay": 1, + "operation": "GetPolicy", + "maxAttempts": 20, + "acceptors": [ + { + "state": "success", + "matcher": "status", + "expected": 200 + }, + { + "state": "retry", + "matcher": "error", + "expected": "NoSuchEntity" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/identitystore/2020-06-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/identitystore/2020-06-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0081c88 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/identitystore/2020-06-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/identitystore/2020-06-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/identitystore/2020-06-15/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/identitystore/2020-06-15/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/identitystore/2020-06-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/identitystore/2020-06-15/paginators-1.json new file mode 100644 index 0000000..766e7c4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/identitystore/2020-06-15/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListGroupMemberships": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "GroupMemberships" + }, + "ListGroupMembershipsForMember": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "GroupMemberships" + }, + "ListGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Groups" + }, + "ListUsers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Users" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/identitystore/2020-06-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/identitystore/2020-06-15/service-2.json.gz new file mode 100644 index 0000000..807dd55 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/identitystore/2020-06-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b8ee6fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/paginators-1.json new file mode 100644 index 0000000..f259294 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/paginators-1.json @@ -0,0 +1,129 @@ +{ + "pagination": { + "ListComponentBuildVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "componentSummaryList" + }, + "ListComponents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "componentVersionList" + }, + "ListContainerRecipes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "containerRecipeSummaryList" + }, + "ListDistributionConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "distributionConfigurationSummaryList" + }, + "ListImageBuildVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "imageSummaryList" + }, + "ListImagePackages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "imagePackageList" + }, + "ListImagePipelineImages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "imageSummaryList" + }, + "ListImagePipelines": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "imagePipelineList" + }, + "ListImageRecipes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "imageRecipeSummaryList" + }, + "ListImageScanFindingAggregations": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "responses" + }, + "ListImageScanFindings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findings" + }, + "ListImages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "imageVersionList" + }, + "ListInfrastructureConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "infrastructureConfigurationSummaryList" + }, + "ListLifecycleExecutionResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "resources" + }, + "ListLifecycleExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "lifecycleExecutions" + }, + "ListLifecyclePolicies": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "lifecyclePolicySummaryList" + }, + "ListWaitingWorkflowSteps": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "steps" + }, + "ListWorkflowBuildVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workflowSummaryList" + }, + "ListWorkflowExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workflowExecutions" + }, + "ListWorkflowStepExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "steps" + }, + "ListWorkflows": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workflowVersionList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/paginators-1.sdk-extras.json new file mode 100644 index 0000000..4106060 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/paginators-1.sdk-extras.json @@ -0,0 +1,95 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListComponentBuildVersions": { + "non_aggregate_keys": [ + "requestId" + ] + }, + "ListComponents": { + "non_aggregate_keys": [ + "requestId" + ] + }, + "ListContainerRecipes": { + "non_aggregate_keys": [ + "requestId" + ] + }, + "ListDistributionConfigurations": { + "non_aggregate_keys": [ + "requestId" + ] + }, + "ListImageBuildVersions": { + "non_aggregate_keys": [ + "requestId" + ] + }, + "ListImagePackages": { + "non_aggregate_keys": [ + "requestId" + ] + }, + "ListImagePipelineImages": { + "non_aggregate_keys": [ + "requestId" + ] + }, + "ListImagePipelines": { + "non_aggregate_keys": [ + "requestId" + ] + }, + "ListImageRecipes": { + "non_aggregate_keys": [ + "requestId" + ] + }, + "ListImageScanFindingAggregations": { + "non_aggregate_keys": [ + "requestId", + "aggregationType" + ] + }, + "ListImageScanFindings": { + "non_aggregate_keys": [ + "requestId" + ] + }, + "ListImages": { + "non_aggregate_keys": [ + "requestId" + ] + }, + "ListInfrastructureConfigurations": { + "non_aggregate_keys": [ + "requestId" + ] + }, + "ListLifecycleExecutionResources": { + "non_aggregate_keys": [ + "lifecycleExecutionId", + "lifecycleExecutionState" + ] + }, + "ListWorkflowExecutions": { + "non_aggregate_keys": [ + "requestId", + "imageBuildVersionArn", + "message" + ] + }, + "ListWorkflowStepExecutions": { + "non_aggregate_keys": [ + "requestId", + "workflowBuildVersionArn", + "workflowExecutionId", + "imageBuildVersionArn", + "message" + ] + } + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/service-2.json.gz new file mode 100644 index 0000000..ad84db6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/imagebuilder/2019-12-02/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/importexport/2010-06-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/importexport/2010-06-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..16bd94a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/importexport/2010-06-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/importexport/2010-06-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/importexport/2010-06-01/paginators-1.json new file mode 100644 index 0000000..702385e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/importexport/2010-06-01/paginators-1.json @@ -0,0 +1,11 @@ +{ + "pagination": { + "ListJobs": { + "input_token": "Marker", + "output_token": "Jobs[-1].JobId", + "more_results": "IsTruncated", + "limit_key": "MaxJobs", + "result_key": "Jobs" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/importexport/2010-06-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/importexport/2010-06-01/service-2.json.gz new file mode 100644 index 0000000..d03a547 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/importexport/2010-06-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector-scan/2023-08-08/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/inspector-scan/2023-08-08/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..58bd03e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/inspector-scan/2023-08-08/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector-scan/2023-08-08/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/inspector-scan/2023-08-08/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/inspector-scan/2023-08-08/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector-scan/2023-08-08/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/inspector-scan/2023-08-08/service-2.json.gz new file mode 100644 index 0000000..f937da1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/inspector-scan/2023-08-08/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector/2015-08-18/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/inspector/2015-08-18/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7d1ed80 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/inspector/2015-08-18/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector/2015-08-18/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/inspector/2015-08-18/service-2.json.gz new file mode 100644 index 0000000..71e664d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/inspector/2015-08-18/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector/2016-02-16/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/inspector/2016-02-16/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8d5c4c5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/inspector/2016-02-16/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector/2016-02-16/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/inspector/2016-02-16/examples-1.json new file mode 100644 index 0000000..05b541f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/inspector/2016-02-16/examples-1.json @@ -0,0 +1,1148 @@ +{ + "version": "1.0", + "examples": { + "AddAttributesToFindings": [ + { + "input": { + "attributes": [ + { + "key": "Example", + "value": "example" + } + ], + "findingArns": [ + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-8l1VIE0D/run/0-Z02cjjug/finding/0-T8yM9mEU" + ] + }, + "output": { + "failedItems": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Assigns attributes (key and value pairs) to the findings that are specified by the ARNs of the findings.", + "id": "add-attributes-to-findings-1481063856401", + "title": "Add attributes to findings" + } + ], + "CreateAssessmentTarget": [ + { + "input": { + "assessmentTargetName": "ExampleAssessmentTarget", + "resourceGroupArn": "arn:aws:inspector:us-west-2:123456789012:resourcegroup/0-AB6DMKnv" + }, + "output": { + "assessmentTargetArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a new assessment target using the ARN of the resource group that is generated by CreateResourceGroup. You can create up to 50 assessment targets per AWS account. You can run up to 500 concurrent agents per AWS account.", + "id": "create-assessment-target-1481063953657", + "title": "Create assessment target" + } + ], + "CreateAssessmentTemplate": [ + { + "input": { + "assessmentTargetArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX", + "assessmentTemplateName": "ExampleAssessmentTemplate", + "durationInSeconds": 180, + "rulesPackageArns": [ + "arn:aws:inspector:us-west-2:758058086616:rulespackage/0-11B9DBXp" + ], + "userAttributesForFindings": [ + { + "key": "Example", + "value": "example" + } + ] + }, + "output": { + "assessmentTemplateArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX/template/0-it5r2S4T" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates an assessment template for the assessment target that is specified by the ARN of the assessment target.", + "id": "create-assessment-template-1481064046719", + "title": "Create assessment template" + } + ], + "CreateResourceGroup": [ + { + "input": { + "resourceGroupTags": [ + { + "key": "Name", + "value": "example" + } + ] + }, + "output": { + "resourceGroupArn": "arn:aws:inspector:us-west-2:123456789012:resourcegroup/0-AB6DMKnv" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a resource group using the specified set of tags (key and value pairs) that are used to select the EC2 instances to be included in an Amazon Inspector assessment target. The created resource group is then used to create an Amazon Inspector assessment target. ", + "id": "create-resource-group-1481064169037", + "title": "Create resource group" + } + ], + "DeleteAssessmentRun": [ + { + "input": { + "assessmentRunArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX/template/0-it5r2S4T/run/0-11LMTAVe" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the assessment run that is specified by the ARN of the assessment run.", + "id": "delete-assessment-run-1481064251629", + "title": "Delete assessment run" + } + ], + "DeleteAssessmentTarget": [ + { + "input": { + "assessmentTargetArn": "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the assessment target that is specified by the ARN of the assessment target.", + "id": "delete-assessment-target-1481064309029", + "title": "Delete assessment target" + } + ], + "DeleteAssessmentTemplate": [ + { + "input": { + "assessmentTemplateArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX/template/0-it5r2S4T" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the assessment template that is specified by the ARN of the assessment template.", + "id": "delete-assessment-template-1481064364074", + "title": "Delete assessment template" + } + ], + "DescribeAssessmentRuns": [ + { + "input": { + "assessmentRunArns": [ + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw/run/0-MKkpXXPE" + ] + }, + "output": { + "assessmentRuns": [ + { + "name": "Run 1 for ExampleAssessmentTemplate", + "arn": "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw/run/0-MKkpXXPE", + "assessmentTemplateArn": "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw", + "completedAt": "1458680301.4", + "createdAt": "1458680170.035", + "dataCollected": true, + "durationInSeconds": 3600, + "findingCounts": { + "High": 14, + "Informational": 0, + "Low": 0, + "Medium": 2, + "Undefined": 0 + }, + "notifications": [ + + ], + "rulesPackageArns": [ + "arn:aws:inspector:us-west-2:758058086616:rulespackage/0-X1KXtawP" + ], + "startedAt": "1458680170.161", + "state": "COMPLETED", + "stateChangedAt": "1458680301.4", + "stateChanges": [ + { + "state": "CREATED", + "stateChangedAt": "1458680170.035" + }, + { + "state": "START_DATA_COLLECTION_PENDING", + "stateChangedAt": "1458680170.065" + }, + { + "state": "START_DATA_COLLECTION_IN_PROGRESS", + "stateChangedAt": "1458680170.096" + }, + { + "state": "COLLECTING_DATA", + "stateChangedAt": "1458680170.161" + }, + { + "state": "STOP_DATA_COLLECTION_PENDING", + "stateChangedAt": "1458680239.883" + }, + { + "state": "DATA_COLLECTED", + "stateChangedAt": "1458680299.847" + }, + { + "state": "EVALUATING_RULES", + "stateChangedAt": "1458680300.099" + }, + { + "state": "COMPLETED", + "stateChangedAt": "1458680301.4" + } + ], + "userAttributesForFindings": [ + + ] + } + ], + "failedItems": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Describes the assessment runs that are specified by the ARNs of the assessment runs.", + "id": "describte-assessment-runs-1481064424352", + "title": "Describte assessment runs" + } + ], + "DescribeAssessmentTargets": [ + { + "input": { + "assessmentTargetArns": [ + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq" + ] + }, + "output": { + "assessmentTargets": [ + { + "name": "ExampleAssessmentTarget", + "arn": "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq", + "createdAt": "1458074191.459", + "resourceGroupArn": "arn:aws:inspector:us-west-2:123456789012:resourcegroup/0-PyGXopAI", + "updatedAt": "1458074191.459" + } + ], + "failedItems": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Describes the assessment targets that are specified by the ARNs of the assessment targets.", + "id": "describte-assessment-targets-1481064527735", + "title": "Describte assessment targets" + } + ], + "DescribeAssessmentTemplates": [ + { + "input": { + "assessmentTemplateArns": [ + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw" + ] + }, + "output": { + "assessmentTemplates": [ + { + "name": "ExampleAssessmentTemplate", + "arn": "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw", + "assessmentRunCount": 0, + "assessmentTargetArn": "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq", + "createdAt": "1458074191.844", + "durationInSeconds": 3600, + "rulesPackageArns": [ + "arn:aws:inspector:us-west-2:758058086616:rulespackage/0-X1KXtawP" + ], + "userAttributesForFindings": [ + + ] + } + ], + "failedItems": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Describes the assessment templates that are specified by the ARNs of the assessment templates.", + "id": "describte-assessment-templates-1481064606829", + "title": "Describte assessment templates" + } + ], + "DescribeCrossAccountAccessRole": [ + { + "output": { + "registeredAt": "1458069182.826", + "roleArn": "arn:aws:iam::123456789012:role/inspector", + "valid": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Describes the IAM role that enables Amazon Inspector to access your AWS account.", + "id": "describte-cross-account-access-role-1481064682267", + "title": "Describte cross account access role" + } + ], + "DescribeFindings": [ + { + "input": { + "findingArns": [ + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw/run/0-MKkpXXPE/finding/0-HwPnsDm4" + ] + }, + "output": { + "failedItems": { + }, + "findings": [ + { + "arn": "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw/run/0-MKkpXXPE/finding/0-HwPnsDm4", + "assetAttributes": { + "ipv4Addresses": [ + + ], + "schemaVersion": 1 + }, + "assetType": "ec2-instance", + "attributes": [ + + ], + "confidence": 10, + "createdAt": "1458680301.37", + "description": "Amazon Inspector did not find any potential security issues during this assessment.", + "indicatorOfCompromise": false, + "numericSeverity": 0, + "recommendation": "No remediation needed.", + "schemaVersion": 1, + "service": "Inspector", + "serviceAttributes": { + "assessmentRunArn": "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw/run/0-MKkpXXPE", + "rulesPackageArn": "arn:aws:inspector:us-west-2:758058086616:rulespackage/0-X1KXtawP", + "schemaVersion": 1 + }, + "severity": "Informational", + "title": "No potential security issues found", + "updatedAt": "1458680301.37", + "userAttributes": [ + + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Describes the findings that are specified by the ARNs of the findings.", + "id": "describte-findings-1481064771803", + "title": "Describe findings" + } + ], + "DescribeResourceGroups": [ + { + "input": { + "resourceGroupArns": [ + "arn:aws:inspector:us-west-2:123456789012:resourcegroup/0-PyGXopAI" + ] + }, + "output": { + "failedItems": { + }, + "resourceGroups": [ + { + "arn": "arn:aws:inspector:us-west-2:123456789012:resourcegroup/0-PyGXopAI", + "createdAt": "1458074191.098", + "tags": [ + { + "key": "Name", + "value": "example" + } + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Describes the resource groups that are specified by the ARNs of the resource groups.", + "id": "describe-resource-groups-1481065787743", + "title": "Describe resource groups" + } + ], + "DescribeRulesPackages": [ + { + "input": { + "rulesPackageArns": [ + "arn:aws:inspector:us-west-2:758058086616:rulespackage/0-JJOtZiqQ" + ] + }, + "output": { + "failedItems": { + }, + "rulesPackages": [ + { + "version": "1.1", + "name": "Security Best Practices", + "arn": "arn:aws:inspector:us-west-2:758058086616:rulespackage/0-JJOtZiqQ", + "description": "The rules in this package help determine whether your systems are configured securely.", + "provider": "Amazon Web Services, Inc." + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Describes the rules packages that are specified by the ARNs of the rules packages.", + "id": "describe-rules-packages-1481069641979", + "title": "Describe rules packages" + } + ], + "GetTelemetryMetadata": [ + { + "input": { + "assessmentRunArn": "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw/run/0-MKkpXXPE" + }, + "output": { + "telemetryMetadata": [ + { + "count": 2, + "dataSize": 345, + "messageType": "InspectorDuplicateProcess" + }, + { + "count": 3, + "dataSize": 255, + "messageType": "InspectorTimeEventMsg" + }, + { + "count": 4, + "dataSize": 1082, + "messageType": "InspectorNetworkInterface" + }, + { + "count": 2, + "dataSize": 349, + "messageType": "InspectorDnsEntry" + }, + { + "count": 11, + "dataSize": 2514, + "messageType": "InspectorDirectoryInfoMsg" + }, + { + "count": 1, + "dataSize": 179, + "messageType": "InspectorTcpV6ListeningPort" + }, + { + "count": 101, + "dataSize": 10949, + "messageType": "InspectorTerminal" + }, + { + "count": 26, + "dataSize": 5916, + "messageType": "InspectorUser" + }, + { + "count": 282, + "dataSize": 32148, + "messageType": "InspectorDynamicallyLoadedCodeModule" + }, + { + "count": 18, + "dataSize": 10172, + "messageType": "InspectorCreateProcess" + }, + { + "count": 3, + "dataSize": 8001, + "messageType": "InspectorProcessPerformance" + }, + { + "count": 1, + "dataSize": 360, + "messageType": "InspectorOperatingSystem" + }, + { + "count": 6, + "dataSize": 546, + "messageType": "InspectorStopProcess" + }, + { + "count": 1, + "dataSize": 1553, + "messageType": "InspectorInstanceMetaData" + }, + { + "count": 2, + "dataSize": 434, + "messageType": "InspectorTcpV4Connection" + }, + { + "count": 474, + "dataSize": 2960322, + "messageType": "InspectorPackageInfo" + }, + { + "count": 3, + "dataSize": 2235, + "messageType": "InspectorSystemPerformance" + }, + { + "count": 105, + "dataSize": 46048, + "messageType": "InspectorCodeModule" + }, + { + "count": 1, + "dataSize": 182, + "messageType": "InspectorUdpV6ListeningPort" + }, + { + "count": 2, + "dataSize": 371, + "messageType": "InspectorUdpV4ListeningPort" + }, + { + "count": 18, + "dataSize": 8362, + "messageType": "InspectorKernelModule" + }, + { + "count": 29, + "dataSize": 48788, + "messageType": "InspectorConfigurationInfo" + }, + { + "count": 1, + "dataSize": 79, + "messageType": "InspectorMonitoringStart" + }, + { + "count": 5, + "dataSize": 0, + "messageType": "InspectorSplitMsgBegin" + }, + { + "count": 51, + "dataSize": 4593, + "messageType": "InspectorGroup" + }, + { + "count": 1, + "dataSize": 184, + "messageType": "InspectorTcpV4ListeningPort" + }, + { + "count": 1159, + "dataSize": 3146579, + "messageType": "Total" + }, + { + "count": 5, + "dataSize": 0, + "messageType": "InspectorSplitMsgEnd" + }, + { + "count": 1, + "dataSize": 612, + "messageType": "InspectorLoadImageInProcess" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Information about the data that is collected for the specified assessment run.", + "id": "get-telemetry-metadata-1481066021297", + "title": "Get telemetry metadata" + } + ], + "ListAssessmentRunAgents": [ + { + "input": { + "assessmentRunArn": "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw/run/0-MKkpXXPE", + "maxResults": 123 + }, + "output": { + "assessmentRunAgents": [ + { + "agentHealth": "HEALTHY", + "agentHealthCode": "RUNNING", + "agentId": "i-49113b93", + "assessmentRunArn": "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw/run/0-MKkpXXPE", + "telemetryMetadata": [ + { + "count": 2, + "dataSize": 345, + "messageType": "InspectorDuplicateProcess" + }, + { + "count": 3, + "dataSize": 255, + "messageType": "InspectorTimeEventMsg" + }, + { + "count": 4, + "dataSize": 1082, + "messageType": "InspectorNetworkInterface" + }, + { + "count": 2, + "dataSize": 349, + "messageType": "InspectorDnsEntry" + }, + { + "count": 11, + "dataSize": 2514, + "messageType": "InspectorDirectoryInfoMsg" + }, + { + "count": 1, + "dataSize": 179, + "messageType": "InspectorTcpV6ListeningPort" + }, + { + "count": 101, + "dataSize": 10949, + "messageType": "InspectorTerminal" + }, + { + "count": 26, + "dataSize": 5916, + "messageType": "InspectorUser" + }, + { + "count": 282, + "dataSize": 32148, + "messageType": "InspectorDynamicallyLoadedCodeModule" + }, + { + "count": 18, + "dataSize": 10172, + "messageType": "InspectorCreateProcess" + }, + { + "count": 3, + "dataSize": 8001, + "messageType": "InspectorProcessPerformance" + }, + { + "count": 1, + "dataSize": 360, + "messageType": "InspectorOperatingSystem" + }, + { + "count": 6, + "dataSize": 546, + "messageType": "InspectorStopProcess" + }, + { + "count": 1, + "dataSize": 1553, + "messageType": "InspectorInstanceMetaData" + }, + { + "count": 2, + "dataSize": 434, + "messageType": "InspectorTcpV4Connection" + }, + { + "count": 474, + "dataSize": 2960322, + "messageType": "InspectorPackageInfo" + }, + { + "count": 3, + "dataSize": 2235, + "messageType": "InspectorSystemPerformance" + }, + { + "count": 105, + "dataSize": 46048, + "messageType": "InspectorCodeModule" + }, + { + "count": 1, + "dataSize": 182, + "messageType": "InspectorUdpV6ListeningPort" + }, + { + "count": 2, + "dataSize": 371, + "messageType": "InspectorUdpV4ListeningPort" + }, + { + "count": 18, + "dataSize": 8362, + "messageType": "InspectorKernelModule" + }, + { + "count": 29, + "dataSize": 48788, + "messageType": "InspectorConfigurationInfo" + }, + { + "count": 1, + "dataSize": 79, + "messageType": "InspectorMonitoringStart" + }, + { + "count": 5, + "dataSize": 0, + "messageType": "InspectorSplitMsgBegin" + }, + { + "count": 51, + "dataSize": 4593, + "messageType": "InspectorGroup" + }, + { + "count": 1, + "dataSize": 184, + "messageType": "InspectorTcpV4ListeningPort" + }, + { + "count": 1159, + "dataSize": 3146579, + "messageType": "Total" + }, + { + "count": 5, + "dataSize": 0, + "messageType": "InspectorSplitMsgEnd" + }, + { + "count": 1, + "dataSize": 612, + "messageType": "InspectorLoadImageInProcess" + } + ] + } + ], + "nextToken": "1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists the agents of the assessment runs that are specified by the ARNs of the assessment runs.", + "id": "list-assessment-run-agents-1481918140642", + "title": "List assessment run agents" + } + ], + "ListAssessmentRuns": [ + { + "input": { + "assessmentTemplateArns": [ + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw" + ], + "maxResults": 123 + }, + "output": { + "assessmentRunArns": [ + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw/run/0-MKkpXXPE", + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw/run/0-v5D6fI3v" + ], + "nextToken": "1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists the assessment runs that correspond to the assessment templates that are specified by the ARNs of the assessment templates.", + "id": "list-assessment-runs-1481066340844", + "title": "List assessment runs" + } + ], + "ListAssessmentTargets": [ + { + "input": { + "maxResults": 123 + }, + "output": { + "assessmentTargetArns": [ + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq" + ], + "nextToken": "1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists the ARNs of the assessment targets within this AWS account. ", + "id": "list-assessment-targets-1481066540849", + "title": "List assessment targets" + } + ], + "ListAssessmentTemplates": [ + { + "input": { + "assessmentTargetArns": [ + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq" + ], + "maxResults": 123 + }, + "output": { + "assessmentTemplateArns": [ + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw", + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-Uza6ihLh" + ], + "nextToken": "1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists the assessment templates that correspond to the assessment targets that are specified by the ARNs of the assessment targets.", + "id": "list-assessment-templates-1481066623520", + "title": "List assessment templates" + } + ], + "ListEventSubscriptions": [ + { + "input": { + "maxResults": 123, + "resourceArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX/template/0-7sbz2Kz0" + }, + "output": { + "nextToken": "1", + "subscriptions": [ + { + "eventSubscriptions": [ + { + "event": "ASSESSMENT_RUN_COMPLETED", + "subscribedAt": "1459455440.867" + } + ], + "resourceArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX/template/0-7sbz2Kz0", + "topicArn": "arn:aws:sns:us-west-2:123456789012:exampletopic" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists all the event subscriptions for the assessment template that is specified by the ARN of the assessment template. ", + "id": "list-event-subscriptions-1481068376945", + "title": "List event subscriptions" + } + ], + "ListFindings": [ + { + "input": { + "assessmentRunArns": [ + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw/run/0-MKkpXXPE" + ], + "maxResults": 123 + }, + "output": { + "findingArns": [ + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw/run/0-MKkpXXPE/finding/0-HwPnsDm4", + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-4r1V2mAw/run/0-v5D6fI3v/finding/0-tyvmqBLy" + ], + "nextToken": "1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists findings that are generated by the assessment runs that are specified by the ARNs of the assessment runs.", + "id": "list-findings-1481066840611", + "title": "List findings" + } + ], + "ListRulesPackages": [ + { + "input": { + "maxResults": 123 + }, + "output": { + "nextToken": "1", + "rulesPackageArns": [ + "arn:aws:inspector:us-west-2:758058086616:rulespackage/0-9hgA516p", + "arn:aws:inspector:us-west-2:758058086616:rulespackage/0-H5hpSawc", + "arn:aws:inspector:us-west-2:758058086616:rulespackage/0-JJOtZiqQ", + "arn:aws:inspector:us-west-2:758058086616:rulespackage/0-vg5GGHSD" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists all available Amazon Inspector rules packages.", + "id": "list-rules-packages-1481066954883", + "title": "List rules packages" + } + ], + "ListTagsForResource": [ + { + "input": { + "resourceArn": "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-gcwFliYu" + }, + "output": { + "tags": [ + { + "key": "Name", + "value": "Example" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists all tags associated with an assessment template.", + "id": "list-tags-for-resource-1481067025240", + "title": "List tags for resource" + } + ], + "PreviewAgents": [ + { + "input": { + "maxResults": 123, + "previewAgentsArn": "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq" + }, + "output": { + "agentPreviews": [ + { + "agentId": "i-49113b93" + } + ], + "nextToken": "1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Previews the agents installed on the EC2 instances that are part of the specified assessment target.", + "id": "preview-agents-1481067101888", + "title": "Preview agents" + } + ], + "RegisterCrossAccountAccessRole": [ + { + "input": { + "roleArn": "arn:aws:iam::123456789012:role/inspector" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Registers the IAM role that Amazon Inspector uses to list your EC2 instances at the start of the assessment run or when you call the PreviewAgents action.", + "id": "register-cross-account-access-role-1481067178301", + "title": "Register cross account access role" + } + ], + "RemoveAttributesFromFindings": [ + { + "input": { + "attributeKeys": [ + "key=Example,value=example" + ], + "findingArns": [ + "arn:aws:inspector:us-west-2:123456789012:target/0-0kFIPusq/template/0-8l1VIE0D/run/0-Z02cjjug/finding/0-T8yM9mEU" + ] + }, + "output": { + "failedItems": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Removes entire attributes (key and value pairs) from the findings that are specified by the ARNs of the findings where an attribute with the specified key exists.", + "id": "remove-attributes-from-findings-1481067246548", + "title": "Remove attributes from findings" + } + ], + "SetTagsForResource": [ + { + "input": { + "resourceArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX/template/0-7sbz2Kz0", + "tags": [ + { + "key": "Example", + "value": "example" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Sets tags (key and value pairs) to the assessment template that is specified by the ARN of the assessment template.", + "id": "set-tags-for-resource-1481067329646", + "title": "Set tags for resource" + } + ], + "StartAssessmentRun": [ + { + "input": { + "assessmentRunName": "examplerun", + "assessmentTemplateArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX/template/0-it5r2S4T" + }, + "output": { + "assessmentRunArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX/template/0-it5r2S4T/run/0-jOoroxyY" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Starts the assessment run specified by the ARN of the assessment template. For this API to function properly, you must not exceed the limit of running up to 500 concurrent agents per AWS account.", + "id": "start-assessment-run-1481067407484", + "title": "Start assessment run" + } + ], + "StopAssessmentRun": [ + { + "input": { + "assessmentRunArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX/template/0-it5r2S4T/run/0-11LMTAVe" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Stops the assessment run that is specified by the ARN of the assessment run.", + "id": "stop-assessment-run-1481067502857", + "title": "Stop assessment run" + } + ], + "SubscribeToEvent": [ + { + "input": { + "event": "ASSESSMENT_RUN_COMPLETED", + "resourceArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX/template/0-7sbz2Kz0", + "topicArn": "arn:aws:sns:us-west-2:123456789012:exampletopic" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Enables the process of sending Amazon Simple Notification Service (SNS) notifications about a specified event to a specified SNS topic.", + "id": "subscribe-to-event-1481067686031", + "title": "Subscribe to event" + } + ], + "UnsubscribeFromEvent": [ + { + "input": { + "event": "ASSESSMENT_RUN_COMPLETED", + "resourceArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX/template/0-7sbz2Kz0", + "topicArn": "arn:aws:sns:us-west-2:123456789012:exampletopic" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Disables the process of sending Amazon Simple Notification Service (SNS) notifications about a specified event to a specified SNS topic.", + "id": "unsubscribe-from-event-1481067781705", + "title": "Unsubscribe from event" + } + ], + "UpdateAssessmentTarget": [ + { + "input": { + "assessmentTargetArn": "arn:aws:inspector:us-west-2:123456789012:target/0-nvgVhaxX", + "assessmentTargetName": "Example", + "resourceGroupArn": "arn:aws:inspector:us-west-2:123456789012:resourcegroup/0-yNbgL5Pt" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Updates the assessment target that is specified by the ARN of the assessment target.", + "id": "update-assessment-target-1481067866692", + "title": "Update assessment target" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector/2016-02-16/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/inspector/2016-02-16/paginators-1.json new file mode 100644 index 0000000..8dec041 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/inspector/2016-02-16/paginators-1.json @@ -0,0 +1,58 @@ +{ + "pagination": { + "ListFindings": { + "result_key": "findingArns", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "ListAssessmentTemplates": { + "result_key": "assessmentTemplateArns", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "PreviewAgents": { + "result_key": "agentPreviews", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "ListEventSubscriptions": { + "result_key": "subscriptions", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "ListRulesPackages": { + "result_key": "rulesPackageArns", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "ListAssessmentRunAgents": { + "result_key": "assessmentRunAgents", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "ListAssessmentRuns": { + "result_key": "assessmentRunArns", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "ListAssessmentTargets": { + "result_key": "assessmentTargetArns", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "ListExclusions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "exclusionArns" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector/2016-02-16/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/inspector/2016-02-16/service-2.json.gz new file mode 100644 index 0000000..e14cc5b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/inspector/2016-02-16/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d326ef9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/paginators-1.json new file mode 100644 index 0000000..d20637a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/paginators-1.json @@ -0,0 +1,110 @@ +{ + "pagination": { + "ListAccountPermissions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "permissions" + }, + "ListCoverage": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "coveredResources" + }, + "ListCoverageStatistics": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "countsByGroup" + }, + "ListDelegatedAdminAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "delegatedAdminAccounts" + }, + "ListFilters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "filters" + }, + "ListFindingAggregations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "responses" + }, + "ListFindings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findings" + }, + "ListMembers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "members" + }, + "ListUsageTotals": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "totals" + }, + "SearchVulnerabilities": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "vulnerabilities" + }, + "GetCisScanResultDetails": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "scanResultDetails" + }, + "ListCisScanConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "scanConfigurations" + }, + "ListCisScanResultsAggregatedByChecks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "checkAggregations" + }, + "ListCisScanResultsAggregatedByTargetResource": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "targetResourceAggregations" + }, + "ListCisScans": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "scans" + }, + "GetClustersForImage": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "cluster" + }, + "ListConnectorScanConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "scanConfigurations" + }, + "ListConnectors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/paginators-1.sdk-extras.json new file mode 100644 index 0000000..b01a0bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/paginators-1.sdk-extras.json @@ -0,0 +1,17 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListFindingAggregations": { + "non_aggregate_keys": [ + "aggregationType" + ] + }, + "ListCoverageStatistics": { + "non_aggregate_keys": [ + "totalCounts" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/service-2.json.gz new file mode 100644 index 0000000..49e94f9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/waiters-2.json new file mode 100644 index 0000000..33928bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/inspector2/2020-06-08/waiters-2.json @@ -0,0 +1,66 @@ +{ + "version" : 2, + "waiters" : { + "ConnectorConnected" : { + "description" : "Wait until a connector reaches the CONNECTED health status, typically after CreateConnector or UpdateConnector + ConnectorEnabled. Caller MUST filter ListConnectors by the specific connector ARN so the matchers apply only to the target connector. Success when all returned items have health.connectorStatus == CONNECTED. Failure when any item is in health.connectorStatus == FAILED_TO_CONNECT or enablementStatus == FAILED_TO_ENABLE.", + "delay" : 30, + "maxAttempts" : 5, + "operation" : "ListConnectors", + "acceptors" : [ { + "matcher" : "pathAll", + "argument" : "items[].health.connectorStatus", + "state" : "success", + "expected" : "CONNECTED" + }, { + "matcher" : "pathAny", + "argument" : "items[].health.connectorStatus", + "state" : "failure", + "expected" : "FAILED_TO_CONNECT" + }, { + "matcher" : "pathAny", + "argument" : "items[].enablementStatus", + "state" : "failure", + "expected" : "FAILED_TO_ENABLE" + } ] + }, + "ConnectorDeleted" : { + "description" : "Wait until a connector is no longer returned by ListConnectors. Caller MUST filter ListConnectors by the specific connector ARN; success is reached when the filtered result returns zero items. Failure when the deletion terminally fails (FAILED_TO_DELETE).", + "delay" : 30, + "maxAttempts" : 5, + "operation" : "ListConnectors", + "acceptors" : [ { + "matcher" : "path", + "argument" : "length(items) == `0`", + "state" : "success", + "expected" : true + }, { + "matcher" : "pathAny", + "argument" : "items[].enablementStatus", + "state" : "failure", + "expected" : "FAILED_TO_DELETE" + } ] + }, + "ConnectorEnabled" : { + "description" : "Wait until a connector reaches the ENABLED state, whether after CreateConnector or UpdateConnector. Caller MUST filter ListConnectors by the specific connector ARN so the matchers apply only to the target connector. Success when all returned items have enablementStatus == ENABLED. Failure when any item is in FAILED_TO_ENABLE or FAILED_TO_UPDATE.", + "delay" : 30, + "maxAttempts" : 5, + "operation" : "ListConnectors", + "acceptors" : [ { + "matcher" : "pathAll", + "argument" : "items[].enablementStatus", + "state" : "success", + "expected" : "ENABLED" + }, { + "matcher" : "pathAny", + "argument" : "items[].enablementStatus", + "state" : "failure", + "expected" : "FAILED_TO_ENABLE" + }, { + "matcher" : "pathAny", + "argument" : "items[].enablementStatus", + "state" : "failure", + "expected" : "FAILED_TO_UPDATE" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/interconnect/2022-07-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/interconnect/2022-07-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b9855a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/interconnect/2022-07-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/interconnect/2022-07-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/interconnect/2022-07-26/paginators-1.json new file mode 100644 index 0000000..39dbf38 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/interconnect/2022-07-26/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListAttachPoints": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "attachPoints" + }, + "ListConnections": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "connections" + }, + "ListEnvironments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "environments" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/interconnect/2022-07-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/interconnect/2022-07-26/service-2.json.gz new file mode 100644 index 0000000..9b1104e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/interconnect/2022-07-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/interconnect/2022-07-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/interconnect/2022-07-26/waiters-2.json new file mode 100644 index 0000000..6f64829 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/interconnect/2022-07-26/waiters-2.json @@ -0,0 +1,37 @@ +{ + "version" : 2, + "waiters" : { + "ConnectionAvailable" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetConnection", + "acceptors" : [ { + "matcher" : "path", + "argument" : "connection.state", + "state" : "success", + "expected" : "available" + }, { + "matcher" : "path", + "argument" : "connection.state", + "state" : "failure", + "expected" : "deleted" + }, { + "matcher" : "path", + "argument" : "connection.state", + "state" : "failure", + "expected" : "down" + } ] + }, + "ConnectionDeleted" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetConnection", + "acceptors" : [ { + "matcher" : "path", + "argument" : "connection.state", + "state" : "success", + "expected" : "deleted" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/internetmonitor/2021-06-03/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/internetmonitor/2021-06-03/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c545e65 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/internetmonitor/2021-06-03/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/internetmonitor/2021-06-03/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/internetmonitor/2021-06-03/paginators-1.json new file mode 100644 index 0000000..c08ea4f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/internetmonitor/2021-06-03/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListHealthEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "HealthEvents" + }, + "ListMonitors": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Monitors" + }, + "ListInternetEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InternetEvents" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/internetmonitor/2021-06-03/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/internetmonitor/2021-06-03/service-2.json.gz new file mode 100644 index 0000000..f2a5d19 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/internetmonitor/2021-06-03/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/internetmonitor/2021-06-03/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/internetmonitor/2021-06-03/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/internetmonitor/2021-06-03/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/invoicing/2024-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/invoicing/2024-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a1b1793 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/invoicing/2024-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/invoicing/2024-12-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/invoicing/2024-12-01/paginators-1.json new file mode 100644 index 0000000..86e047f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/invoicing/2024-12-01/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListInvoiceUnits": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InvoiceUnits" + }, + "ListInvoiceSummaries": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InvoiceSummaries" + }, + "ListProcurementPortalPreferences": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ProcurementPortalPreferences" + }, + "ListProcurementPortalSuppliers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ProcurementPortalSuppliers" + }, + "ListProcurementPortals": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ProcurementPortals" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/invoicing/2024-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/invoicing/2024-12-01/service-2.json.gz new file mode 100644 index 0000000..9817df3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/invoicing/2024-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/invoicing/2024-12-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/invoicing/2024-12-01/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/invoicing/2024-12-01/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot-data/2015-05-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iot-data/2015-05-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3732287 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iot-data/2015-05-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot-data/2015-05-28/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/iot-data/2015-05-28/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iot-data/2015-05-28/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot-data/2015-05-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/iot-data/2015-05-28/paginators-1.json new file mode 100644 index 0000000..f3a23d8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iot-data/2015-05-28/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListRetainedMessages": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "retainedTopics" + }, + "ListSubscriptions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "subscriptions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot-data/2015-05-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iot-data/2015-05-28/service-2.json.gz new file mode 100644 index 0000000..00ae05f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iot-data/2015-05-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot-jobs-data/2017-09-29/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iot-jobs-data/2017-09-29/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..eb7c85d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iot-jobs-data/2017-09-29/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot-jobs-data/2017-09-29/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/iot-jobs-data/2017-09-29/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iot-jobs-data/2017-09-29/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot-jobs-data/2017-09-29/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/iot-jobs-data/2017-09-29/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iot-jobs-data/2017-09-29/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot-jobs-data/2017-09-29/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iot-jobs-data/2017-09-29/service-2.json.gz new file mode 100644 index 0000000..63b0506 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iot-jobs-data/2017-09-29/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot-managed-integrations/2025-03-03/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iot-managed-integrations/2025-03-03/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..83db3e2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iot-managed-integrations/2025-03-03/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot-managed-integrations/2025-03-03/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/iot-managed-integrations/2025-03-03/paginators-1.json new file mode 100644 index 0000000..0815ead --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iot-managed-integrations/2025-03-03/paginators-1.json @@ -0,0 +1,106 @@ +{ + "pagination": { + "ListCredentialLockers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListDestinations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DestinationList" + }, + "ListEventLogConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EventLogConfigurationList" + }, + "ListManagedThingSchemas": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListManagedThings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListNotificationConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "NotificationConfigurationList" + }, + "ListOtaTaskConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListOtaTaskExecutions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ExecutionSummaries" + }, + "ListOtaTasks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Tasks" + }, + "ListProvisioningProfiles": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListSchemaVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListAccountAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListCloudConnectors": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListConnectorDestinations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ConnectorDestinationList" + }, + "ListDeviceDiscoveries": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListDiscoveredDevices": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListManagedThingAccountAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot-managed-integrations/2025-03-03/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iot-managed-integrations/2025-03-03/service-2.json.gz new file mode 100644 index 0000000..2519c1c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iot-managed-integrations/2025-03-03/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot/2015-05-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iot/2015-05-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b82d577 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iot/2015-05-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot/2015-05-28/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/iot/2015-05-28/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iot/2015-05-28/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot/2015-05-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/iot/2015-05-28/paginators-1.json new file mode 100644 index 0000000..4b227cd --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iot/2015-05-28/paginators-1.json @@ -0,0 +1,390 @@ +{ + "pagination": { + "ListCACertificates": { + "input_token": "marker", + "output_token": "nextMarker", + "limit_key": "pageSize", + "result_key": "certificates" + }, + "ListCertificates": { + "input_token": "marker", + "output_token": "nextMarker", + "limit_key": "pageSize", + "result_key": "certificates" + }, + "ListCertificatesByCA": { + "input_token": "marker", + "output_token": "nextMarker", + "limit_key": "pageSize", + "result_key": "certificates" + }, + "ListOutgoingCertificates": { + "input_token": "marker", + "output_token": "nextMarker", + "limit_key": "pageSize", + "result_key": "outgoingCertificates" + }, + "ListPolicies": { + "input_token": "marker", + "output_token": "nextMarker", + "limit_key": "pageSize", + "result_key": "policies" + }, + "ListPolicyPrincipals": { + "input_token": "marker", + "output_token": "nextMarker", + "limit_key": "pageSize", + "result_key": "principals" + }, + "ListPrincipalPolicies": { + "input_token": "marker", + "output_token": "nextMarker", + "limit_key": "pageSize", + "result_key": "policies" + }, + "ListPrincipalThings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "things" + }, + "ListThingTypes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "thingTypes" + }, + "ListThings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "things" + }, + "ListTopicRules": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "rules" + }, + "ListActiveViolations": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "activeViolations" + }, + "ListAttachedPolicies": { + "input_token": "marker", + "limit_key": "pageSize", + "output_token": "nextMarker", + "result_key": "policies" + }, + "ListAuditFindings": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "findings" + }, + "ListAuditTasks": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "tasks" + }, + "ListAuthorizers": { + "input_token": "marker", + "limit_key": "pageSize", + "output_token": "nextMarker", + "result_key": "authorizers" + }, + "ListBillingGroups": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "billingGroups" + }, + "ListIndices": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "indexNames" + }, + "ListJobExecutionsForJob": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "executionSummaries" + }, + "ListJobExecutionsForThing": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "executionSummaries" + }, + "ListJobs": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "jobs" + }, + "ListOTAUpdates": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "otaUpdates" + }, + "ListRoleAliases": { + "input_token": "marker", + "limit_key": "pageSize", + "output_token": "nextMarker", + "result_key": "roleAliases" + }, + "ListScheduledAudits": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "scheduledAudits" + }, + "ListSecurityProfiles": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "securityProfileIdentifiers" + }, + "ListSecurityProfilesForTarget": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "securityProfileTargetMappings" + }, + "ListStreams": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "streams" + }, + "ListTagsForResource": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "tags" + }, + "ListTargetsForPolicy": { + "input_token": "marker", + "limit_key": "pageSize", + "output_token": "nextMarker", + "result_key": "targets" + }, + "ListTargetsForSecurityProfile": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "securityProfileTargets" + }, + "ListThingGroups": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "thingGroups" + }, + "ListThingGroupsForThing": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "thingGroups" + }, + "ListThingRegistrationTasks": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "taskIds" + }, + "ListThingsInBillingGroup": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "things" + }, + "ListThingsInThingGroup": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "things" + }, + "ListV2LoggingLevels": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "logTargetConfigurations" + }, + "ListViolationEvents": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "violationEvents" + }, + "ListAuditMitigationActionsExecutions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "actionsExecutions" + }, + "ListAuditMitigationActionsTasks": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "tasks" + }, + "ListAuditSuppressions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "suppressions" + }, + "ListDimensions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "dimensionNames" + }, + "ListDomainConfigurations": { + "input_token": "marker", + "limit_key": "pageSize", + "output_token": "nextMarker", + "result_key": "domainConfigurations" + }, + "ListMitigationActions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "actionIdentifiers" + }, + "ListProvisioningTemplateVersions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "versions" + }, + "ListProvisioningTemplates": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "templates" + }, + "ListThingRegistrationTaskReports": { + "input_token": "nextToken", + "limit_key": "maxResults", + "non_aggregate_keys": [ + "reportType" + ], + "output_token": "nextToken", + "result_key": "resourceLinks" + }, + "ListTopicRuleDestinations": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "destinationSummaries" + }, + "ListThingPrincipals": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "principals" + }, + "GetBehaviorModelTrainingSummaries": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "summaries" + }, + "ListCustomMetrics": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "metricNames" + }, + "ListDetectMitigationActionsExecutions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "actionsExecutions" + }, + "ListDetectMitigationActionsTasks": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "tasks" + }, + "ListFleetMetrics": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "fleetMetrics" + }, + "ListJobTemplates": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "jobTemplates" + }, + "ListMetricValues": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "metricDatumList" + }, + "ListManagedJobTemplates": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "managedJobTemplates" + }, + "ListRelatedResourcesForAuditFinding": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "relatedResources" + }, + "ListPackageVersions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "packageVersionSummaries" + }, + "ListPackages": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "packageSummaries" + }, + "ListSbomValidationResults": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "validationResultSummaries" + }, + "ListPrincipalThingsV2": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "principalThingObjects" + }, + "ListThingPrincipalsV2": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "thingPrincipalObjects" + }, + "ListCommandExecutions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "commandExecutions" + }, + "ListCommands": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "commands" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iot/2015-05-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iot/2015-05-28/service-2.json.gz new file mode 100644 index 0000000..3bd99e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iot/2015-05-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotdeviceadvisor/2020-09-18/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iotdeviceadvisor/2020-09-18/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..05411e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iotdeviceadvisor/2020-09-18/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotdeviceadvisor/2020-09-18/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/iotdeviceadvisor/2020-09-18/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotdeviceadvisor/2020-09-18/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotdeviceadvisor/2020-09-18/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/iotdeviceadvisor/2020-09-18/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotdeviceadvisor/2020-09-18/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotdeviceadvisor/2020-09-18/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iotdeviceadvisor/2020-09-18/service-2.json.gz new file mode 100644 index 0000000..42d6173 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iotdeviceadvisor/2020-09-18/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotfleetwise/2021-06-17/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iotfleetwise/2021-06-17/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..99321c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iotfleetwise/2021-06-17/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotfleetwise/2021-06-17/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/iotfleetwise/2021-06-17/paginators-1.json new file mode 100644 index 0000000..cc8dcba --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotfleetwise/2021-06-17/paginators-1.json @@ -0,0 +1,88 @@ +{ + "pagination": { + "GetVehicleStatus": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "campaigns" + }, + "ListCampaigns": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "campaignSummaries" + }, + "ListDecoderManifestNetworkInterfaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "networkInterfaces" + }, + "ListDecoderManifestSignals": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "signalDecoders" + }, + "ListDecoderManifests": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "summaries" + }, + "ListFleets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "fleetSummaries" + }, + "ListFleetsForVehicle": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "fleets" + }, + "ListModelManifestNodes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "nodes" + }, + "ListModelManifests": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "summaries" + }, + "ListSignalCatalogNodes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "nodes" + }, + "ListSignalCatalogs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "summaries" + }, + "ListVehicles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "vehicleSummaries" + }, + "ListVehiclesInFleet": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "vehicles" + }, + "ListStateTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "summaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotfleetwise/2021-06-17/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iotfleetwise/2021-06-17/service-2.json.gz new file mode 100644 index 0000000..2799097 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iotfleetwise/2021-06-17/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotfleetwise/2021-06-17/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/iotfleetwise/2021-06-17/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotfleetwise/2021-06-17/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotsecuretunneling/2018-10-05/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iotsecuretunneling/2018-10-05/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..13f3647 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iotsecuretunneling/2018-10-05/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotsecuretunneling/2018-10-05/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/iotsecuretunneling/2018-10-05/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotsecuretunneling/2018-10-05/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotsecuretunneling/2018-10-05/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/iotsecuretunneling/2018-10-05/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotsecuretunneling/2018-10-05/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotsecuretunneling/2018-10-05/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iotsecuretunneling/2018-10-05/service-2.json.gz new file mode 100644 index 0000000..712d473 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iotsecuretunneling/2018-10-05/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..de1ac01 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/paginators-1.json new file mode 100644 index 0000000..b89232c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/paginators-1.json @@ -0,0 +1,250 @@ +{ + "pagination": { + "GetAssetPropertyAggregates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "aggregatedValues" + }, + "GetAssetPropertyValueHistory": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assetPropertyValueHistory" + }, + "ListAccessPolicies": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "accessPolicySummaries" + }, + "ListAssetModels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assetModelSummaries" + }, + "ListAssets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assetSummaries" + }, + "ListAssociatedAssets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assetSummaries" + }, + "ListDashboards": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dashboardSummaries" + }, + "ListGateways": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "gatewaySummaries" + }, + "ListPortals": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "portalSummaries" + }, + "ListProjectAssets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assetIds" + }, + "ListProjects": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "projectSummaries" + }, + "ListAssetRelationships": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assetRelationshipSummaries" + }, + "GetInterpolatedAssetPropertyValues": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "interpolatedAssetPropertyValues" + }, + "ListTimeSeries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "TimeSeriesSummaries" + }, + "ListBulkImportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobSummaries" + }, + "ListAssetModelProperties": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assetModelPropertySummaries" + }, + "ListAssetProperties": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assetPropertySummaries" + }, + "ExecuteQuery": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "rows" + }, + "ListActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "actionSummaries" + }, + "ListAssetModelCompositeModels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assetModelCompositeModelSummaries" + }, + "ListCompositionRelationships": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "compositionRelationshipSummaries" + }, + "ListDatasets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "datasetSummaries" + }, + "ListComputationModelDataBindingUsages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dataBindingUsageSummaries" + }, + "ListComputationModelResolveToResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "computationModelResolveToResourceSummaries" + }, + "ListComputationModels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "computationModelSummaries" + }, + "ListExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "executionSummaries" + }, + "ListInterfaceRelationships": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "interfaceRelationshipSummaries" + }, + "DescribePipelineExecution": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "computeNodeExecutionDetails" + }, + "GetQueryResults": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "rows" + }, + "GetSearchResults": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "searchResults" + }, + "ListApplications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "applications" + }, + "ListDatasetDataSegmentRelationships": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dataSegmentRelationshipSummaries" + }, + "ListDatasetDataSegments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dataSegments" + }, + "ListDatasetExportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobs" + }, + "ListEnrichmentJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobs" + }, + "ListPipelineExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "pipelineExecutionSummaries" + }, + "ListPipelines": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "pipelineSummaries" + }, + "ListQueries": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "queries" + }, + "ListSearches": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "searchSummaries" + }, + "ListTasks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "taskSummaries" + }, + "ListWorkspaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workspaceSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/paginators-1.sdk-extras.json new file mode 100644 index 0000000..740f798 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/paginators-1.sdk-extras.json @@ -0,0 +1,40 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ExecuteQuery": { + "non_aggregate_keys": [ + "columns" + ] + }, + "DescribePipelineExecution": { + "non_aggregate_keys": [ + "pipelineExecutionId", + "pipelineName", + "workspaceName", + "pipelineVersion", + "status", + "startTime", + "endTime", + "requestEnvironmentVariables", + "executionPriority" + ] + }, + "GetQueryResults": { + "non_aggregate_keys": [ + "columnInfo" + ] + }, + "ListDatasets": { + "non_aggregate_keys": [ + "workspaceName" + ] + }, + "ListTimeSeries": { + "non_aggregate_keys": [ + "workspaceName" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/service-2.json.gz new file mode 100644 index 0000000..98e6e9c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/waiters-2.json new file mode 100644 index 0000000..e51df5f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotsitewise/2019-12-02/waiters-2.json @@ -0,0 +1,92 @@ +{ + "version": 2, + "waiters": { + "AssetModelNotExists": { + "delay": 3, + "maxAttempts": 20, + "operation": "DescribeAssetModel", + "acceptors": [ + { + "state": "success", + "matcher": "error", + "expected": "ResourceNotFoundException" + } + ] + }, + "AssetModelActive": { + "delay": 3, + "maxAttempts": 20, + "operation": "DescribeAssetModel", + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "assetModelStatus.state", + "expected": "ACTIVE" + }, + { + "state": "failure", + "matcher": "path", + "argument": "assetModelStatus.state", + "expected": "FAILED" + } + ] + }, + "AssetNotExists": { + "delay": 3, + "maxAttempts": 20, + "operation": "DescribeAsset", + "acceptors": [ + { + "state": "success", + "matcher": "error", + "expected": "ResourceNotFoundException" + } + ] + }, + "AssetActive": { + "delay": 3, + "maxAttempts": 20, + "operation": "DescribeAsset", + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "assetStatus.state", + "expected": "ACTIVE" + }, + { + "state": "failure", + "matcher": "path", + "argument": "assetStatus.state", + "expected": "FAILED" + } + ] + }, + "PortalNotExists": { + "delay": 3, + "maxAttempts": 20, + "operation": "DescribePortal", + "acceptors": [ + { + "state": "success", + "matcher": "error", + "expected": "ResourceNotFoundException" + } + ] + }, + "PortalActive": { + "delay": 3, + "maxAttempts": 20, + "operation": "DescribePortal", + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "portalStatus.state", + "expected": "ACTIVE" + } + ] + } + } + } diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotthingsgraph/2018-09-06/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iotthingsgraph/2018-09-06/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..90fc66b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iotthingsgraph/2018-09-06/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotthingsgraph/2018-09-06/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/iotthingsgraph/2018-09-06/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotthingsgraph/2018-09-06/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotthingsgraph/2018-09-06/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/iotthingsgraph/2018-09-06/paginators-1.json new file mode 100644 index 0000000..bc92f84 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotthingsgraph/2018-09-06/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "GetFlowTemplateRevisions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "summaries" + }, + "GetSystemTemplateRevisions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "summaries" + }, + "ListFlowExecutionMessages": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "messages" + }, + "ListTagsForResource": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "tags" + }, + "SearchEntities": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "descriptions" + }, + "SearchFlowExecutions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "summaries" + }, + "SearchFlowTemplates": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "summaries" + }, + "SearchSystemInstances": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "summaries" + }, + "SearchSystemTemplates": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "summaries" + }, + "SearchThings": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "things" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotthingsgraph/2018-09-06/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iotthingsgraph/2018-09-06/service-2.json.gz new file mode 100644 index 0000000..124eb24 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iotthingsgraph/2018-09-06/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..e1c7c76 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/service-2.json.gz new file mode 100644 index 0000000..b8e7204 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iottwinmaker/2021-11-29/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotwireless/2020-11-22/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iotwireless/2020-11-22/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3ef326b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iotwireless/2020-11-22/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotwireless/2020-11-22/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/iotwireless/2020-11-22/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotwireless/2020-11-22/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotwireless/2020-11-22/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/iotwireless/2020-11-22/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/iotwireless/2020-11-22/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/iotwireless/2020-11-22/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/iotwireless/2020-11-22/service-2.json.gz new file mode 100644 index 0000000..a17ef76 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/iotwireless/2020-11-22/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ivs-realtime/2020-07-14/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ivs-realtime/2020-07-14/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..9365fd4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ivs-realtime/2020-07-14/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ivs-realtime/2020-07-14/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ivs-realtime/2020-07-14/paginators-1.json new file mode 100644 index 0000000..8eb0208 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ivs-realtime/2020-07-14/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListPublicKeys": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "publicKeys" + }, + "ListIngestConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "ingestConfigurations" + }, + "ListParticipantReplicas": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "replicas" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ivs-realtime/2020-07-14/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ivs-realtime/2020-07-14/service-2.json.gz new file mode 100644 index 0000000..0daf221 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ivs-realtime/2020-07-14/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ivs-realtime/2020-07-14/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ivs-realtime/2020-07-14/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ivs-realtime/2020-07-14/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ivs/2020-07-14/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ivs/2020-07-14/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1b610b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ivs/2020-07-14/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ivs/2020-07-14/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ivs/2020-07-14/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ivs/2020-07-14/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ivs/2020-07-14/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ivs/2020-07-14/paginators-1.json new file mode 100644 index 0000000..74044bb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ivs/2020-07-14/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListChannels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "channels" + }, + "ListStreamKeys": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "streamKeys" + }, + "ListStreams": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "streams" + }, + "ListPlaybackKeyPairs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "keyPairs" + }, + "ListRecordingConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recordingConfigurations" + }, + "ListAdConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "adConfigurations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ivs/2020-07-14/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ivs/2020-07-14/service-2.json.gz new file mode 100644 index 0000000..0752e46 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ivs/2020-07-14/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..49eb10b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/service-2.json.gz new file mode 100644 index 0000000..9cac700 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ivschat/2020-07-14/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kafka/2018-11-14/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kafka/2018-11-14/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3e053b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kafka/2018-11-14/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kafka/2018-11-14/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/kafka/2018-11-14/paginators-1.json new file mode 100644 index 0000000..158c50c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kafka/2018-11-14/paginators-1.json @@ -0,0 +1,88 @@ +{ + "pagination": { + "ListClusters": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ClusterInfoList" + }, + "ListNodes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "NodeInfoList" + }, + "ListConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Configurations" + }, + "ListClusterOperations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ClusterOperationInfoList" + }, + "ListConfigurationRevisions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Revisions" + }, + "ListKafkaVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "KafkaVersions" + }, + "ListScramSecrets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SecretArnList" + }, + "ListClustersV2": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ClusterInfoList" + }, + "ListVpcConnections": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VpcConnections" + }, + "ListClientVpcConnections": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ClientVpcConnections" + }, + "ListClusterOperationsV2": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ClusterOperationInfoList" + }, + "ListReplicators": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Replicators" + }, + "ListTopics": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Topics" + }, + "DescribeTopicPartitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Partitions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kafka/2018-11-14/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kafka/2018-11-14/service-2.json.gz new file mode 100644 index 0000000..889d0a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kafka/2018-11-14/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..60cd70c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/paginators-1.json new file mode 100644 index 0000000..d84b26f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListConnectors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "connectors" + }, + "ListCustomPlugins": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "customPlugins" + }, + "ListWorkerConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workerConfigurations" + }, + "ListConnectorOperations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "connectorOperations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/service-2.json.gz new file mode 100644 index 0000000..b233f63 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kafkaconnect/2021-09-14/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kendra-ranking/2022-10-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kendra-ranking/2022-10-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..e22096d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kendra-ranking/2022-10-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kendra-ranking/2022-10-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/kendra-ranking/2022-10-19/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kendra-ranking/2022-10-19/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kendra-ranking/2022-10-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kendra-ranking/2022-10-19/service-2.json.gz new file mode 100644 index 0000000..d9cbc20 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kendra-ranking/2022-10-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kendra/2019-02-03/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kendra/2019-02-03/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..51e3f4e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kendra/2019-02-03/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kendra/2019-02-03/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/kendra/2019-02-03/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kendra/2019-02-03/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kendra/2019-02-03/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/kendra/2019-02-03/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kendra/2019-02-03/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kendra/2019-02-03/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kendra/2019-02-03/service-2.json.gz new file mode 100644 index 0000000..7b9359d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kendra/2019-02-03/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d9a610e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/paginators-1.json new file mode 100644 index 0000000..4145b28 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListKeyspaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "keyspaces" + }, + "ListTables": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tables" + }, + "ListTagsForResource": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tags" + }, + "ListTypes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "types" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/service-2.json.gz new file mode 100644 index 0000000..8f9668b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/waiters-2.json new file mode 100644 index 0000000..4b20636 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/keyspaces/2022-02-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/keyspacesstreams/2024-09-09/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/keyspacesstreams/2024-09-09/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..16ecdea Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/keyspacesstreams/2024-09-09/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/keyspacesstreams/2024-09-09/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/keyspacesstreams/2024-09-09/paginators-1.json new file mode 100644 index 0000000..570c7a7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/keyspacesstreams/2024-09-09/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "GetStream": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "shards" + }, + "ListStreams": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "streams" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/keyspacesstreams/2024-09-09/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/keyspacesstreams/2024-09-09/paginators-1.sdk-extras.json new file mode 100644 index 0000000..166cb24 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/keyspacesstreams/2024-09-09/paginators-1.sdk-extras.json @@ -0,0 +1,18 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetStream": { + "non_aggregate_keys": [ + "streamStatus", + "streamLabel", + "creationRequestDateTime", + "keyspaceName", + "tableName", + "streamArn", + "streamViewType" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/keyspacesstreams/2024-09-09/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/keyspacesstreams/2024-09-09/service-2.json.gz new file mode 100644 index 0000000..3a10a75 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/keyspacesstreams/2024-09-09/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-archived-media/2017-09-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-archived-media/2017-09-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..55cb4c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-archived-media/2017-09-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-archived-media/2017-09-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-archived-media/2017-09-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-archived-media/2017-09-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-archived-media/2017-09-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-archived-media/2017-09-30/paginators-1.json new file mode 100644 index 0000000..a9a7041 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-archived-media/2017-09-30/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListFragments": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Fragments" + }, + "GetImages": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Images" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-archived-media/2017-09-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-archived-media/2017-09-30/service-2.json.gz new file mode 100644 index 0000000..97a139b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-archived-media/2017-09-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-media/2017-09-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-media/2017-09-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..55cb4c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-media/2017-09-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-media/2017-09-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-media/2017-09-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-media/2017-09-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-media/2017-09-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-media/2017-09-30/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-media/2017-09-30/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-media/2017-09-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-media/2017-09-30/service-2.json.gz new file mode 100644 index 0000000..44002d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-media/2017-09-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-signaling/2019-12-04/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-signaling/2019-12-04/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..55cb4c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-signaling/2019-12-04/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-signaling/2019-12-04/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-signaling/2019-12-04/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-signaling/2019-12-04/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-signaling/2019-12-04/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-signaling/2019-12-04/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-signaling/2019-12-04/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-signaling/2019-12-04/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-signaling/2019-12-04/service-2.json.gz new file mode 100644 index 0000000..57741e8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-signaling/2019-12-04/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-webrtc-storage/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-webrtc-storage/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..2c09f5b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-webrtc-storage/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-webrtc-storage/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-webrtc-storage/2018-05-10/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-webrtc-storage/2018-05-10/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-webrtc-storage/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-webrtc-storage/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..b049822 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesis-video-webrtc-storage/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4210560 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/paginators-1.json new file mode 100644 index 0000000..6d8e50b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/paginators-1.json @@ -0,0 +1,49 @@ +{ + "pagination": { + "DescribeStream": { + "input_token": "ExclusiveStartShardId", + "limit_key": "Limit", + "more_results": "StreamDescription.HasMoreShards", + "output_token": "StreamDescription.Shards[-1].ShardId", + "result_key": "StreamDescription.Shards", + "non_aggregate_keys": [ + "StreamDescription.StreamARN", + "StreamDescription.StreamName", + "StreamDescription.StreamStatus", + "StreamDescription.RetentionPeriodHours", + "StreamDescription.EnhancedMonitoring", + "StreamDescription.EncryptionType", + "StreamDescription.KeyId", + "StreamDescription.StreamCreationTimestamp" + ] + }, + "ListStreams": { + "input_token": "NextToken", + "limit_key": "Limit", + "more_results": "HasMoreStreams", + "output_token": "NextToken", + "result_key": [ + "StreamNames", + "StreamSummaries" + ] + }, + "ListShards": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Shards" + }, + "ListStreamConsumers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Consumers" + }, + "ListChannels": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ChannelSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/service-2.json.gz new file mode 100644 index 0000000..787a412 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/waiters-2.json new file mode 100644 index 0000000..8d42f57 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesis/2013-12-02/waiters-2.json @@ -0,0 +1,67 @@ +{ + "version": 2, + "waiters": { + "StreamExists": { + "delay": 10, + "operation": "DescribeStream", + "maxAttempts": 18, + "acceptors": [ + { + "expected": "ACTIVE", + "matcher": "path", + "state": "success", + "argument": "StreamDescription.StreamStatus" + } + ] + }, + "StreamNotExists": { + "delay": 10, + "operation": "DescribeStream", + "maxAttempts": 18, + "acceptors": [ + { + "expected": "ResourceNotFoundException", + "matcher": "error", + "state": "success" + } + ] + }, + "ChannelActive": { + "delay": 10, + "operation": "DescribeChannel", + "maxAttempts": 18, + "acceptors": [ + { + "expected": "ACTIVE", + "matcher": "path", + "state": "success", + "argument": "ChannelDescription.ChannelStatus" + }, + { + "expected": "CREATING", + "matcher": "path", + "state": "retry", + "argument": "ChannelDescription.ChannelStatus" + }, + { + "expected": "UPDATING", + "matcher": "path", + "state": "retry", + "argument": "ChannelDescription.ChannelStatus" + }, + { + "expected": "DELETING", + "matcher": "path", + "state": "failure", + "argument": "ChannelDescription.ChannelStatus" + }, + { + "expected": "FAILED", + "matcher": "path", + "state": "failure", + "argument": "ChannelDescription.ChannelStatus" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesisanalytics/2015-08-14/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalytics/2015-08-14/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8175a9d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalytics/2015-08-14/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesisanalytics/2015-08-14/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalytics/2015-08-14/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalytics/2015-08-14/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesisanalytics/2015-08-14/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalytics/2015-08-14/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalytics/2015-08-14/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesisanalytics/2015-08-14/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalytics/2015-08-14/service-2.json.gz new file mode 100644 index 0000000..1832707 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalytics/2015-08-14/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesisanalyticsv2/2018-05-23/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalyticsv2/2018-05-23/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8175a9d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalyticsv2/2018-05-23/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesisanalyticsv2/2018-05-23/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalyticsv2/2018-05-23/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalyticsv2/2018-05-23/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesisanalyticsv2/2018-05-23/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalyticsv2/2018-05-23/paginators-1.json new file mode 100644 index 0000000..eb315fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalyticsv2/2018-05-23/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListApplicationSnapshots": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "SnapshotSummaries" + }, + "ListApplications": { + "input_token": "NextToken", + "limit_key": "Limit", + "output_token": "NextToken", + "result_key": "ApplicationSummaries" + }, + "ListApplicationOperations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "ApplicationOperationInfoList" + }, + "ListApplicationVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "ApplicationVersionSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesisanalyticsv2/2018-05-23/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalyticsv2/2018-05-23/service-2.json.gz new file mode 100644 index 0000000..2b6f6a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesisanalyticsv2/2018-05-23/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesisvideo/2017-09-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesisvideo/2017-09-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..55cb4c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesisvideo/2017-09-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesisvideo/2017-09-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesisvideo/2017-09-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesisvideo/2017-09-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesisvideo/2017-09-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/kinesisvideo/2017-09-30/paginators-1.json new file mode 100644 index 0000000..9d83731 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kinesisvideo/2017-09-30/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListStreams": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "StreamInfoList" + }, + "ListSignalingChannels": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ChannelInfoList" + }, + "DescribeMappedResourceConfiguration": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MappedResourceConfigurationList" + }, + "ListEdgeAgentConfigurations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EdgeConfigs" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kinesisvideo/2017-09-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kinesisvideo/2017-09-30/service-2.json.gz new file mode 100644 index 0000000..004d632 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kinesisvideo/2017-09-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kms/2014-11-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kms/2014-11-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..05d54ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kms/2014-11-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/kms/2014-11-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/kms/2014-11-01/examples-1.json new file mode 100644 index 0000000..c770d0e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kms/2014-11-01/examples-1.json @@ -0,0 +1,1750 @@ +{ + "version": "1.0", + "examples": { + "CancelKeyDeletion": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "output": { + "KeyId": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key whose deletion you are canceling. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + }, + "output": { + "KeyId": "The ARN of the KMS key whose deletion you canceled." + } + }, + "description": "The following example cancels deletion of the specified KMS key.", + "id": "to-cancel-deletion-of-a-cmk-1477428535102", + "title": "To cancel deletion of a KMS key" + } + ], + "ConnectCustomKeyStore": [ + { + "input": { + "CustomKeyStoreId": "cks-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + "CustomKeyStoreId": "The ID of the AWS KMS custom key store." + }, + "output": { + } + }, + "description": "This example connects an AWS KMS custom key store to its AWS CloudHSM cluster. This operation does not return any data. To verify that the custom key store is connected, use the DescribeCustomKeyStores operation.", + "id": "to-connect-a-custom-key-store-to-its-cloudhsm-cluster-1628626947750", + "title": "To connect a custom key store to its CloudHSM cluster" + } + ], + "CreateAlias": [ + { + "input": { + "AliasName": "alias/ExampleAlias", + "TargetKeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "AliasName": "The alias to create. Aliases must begin with 'alias/'. Do not use aliases that begin with 'alias/aws' because they are reserved for use by AWS.", + "TargetKeyId": "The identifier of the KMS key whose alias you are creating. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + } + }, + "description": "The following example creates an alias for the specified KMS key.", + "id": "to-create-an-alias-1477505685119", + "title": "To create an alias" + } + ], + "CreateCustomKeyStore": [ + { + "input": { + "CloudHsmClusterId": "cluster-1a23b4cdefg", + "CustomKeyStoreName": "ExampleKeyStore", + "KeyStorePassword": "kmsPswd", + "TrustAnchorCertificate": "" + }, + "output": { + "CustomKeyStoreId": "cks-1234567890abcdef0" + }, + "comments": { + "input": { + "CloudHsmClusterId": "The ID of the CloudHSM cluster.", + "CustomKeyStoreName": "A friendly name for the custom key store.", + "KeyStorePassword": "The password for the kmsuser CU account in the specified cluster.", + "TrustAnchorCertificate": "The content of the customerCA.crt file that you created when you initialized the cluster." + }, + "output": { + "CustomKeyStoreId": "The ID of the new custom key store." + } + }, + "description": "This example creates a custom key store that is associated with an AWS CloudHSM cluster.", + "id": "to-create-an-aws-cloudhsm-custom-key-store-1628627769469", + "title": "To create an AWS CloudHSM custom key store" + } + ], + "CreateGrant": [ + { + "input": { + "GranteePrincipal": "arn:aws:iam::111122223333:role/ExampleRole", + "KeyId": "arn:aws:kms:us-east-2:444455556666:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "Operations": [ + "Encrypt", + "Decrypt" + ] + }, + "output": { + "GrantId": "0c237476b39f8bc44e45212e08498fbe3151305030726c0590dd8d3e9f3d6a60", + "GrantToken": "AQpAM2RhZTk1MGMyNTk2ZmZmMzEyYWVhOWViN2I1MWM4Mzc0MWFiYjc0ZDE1ODkyNGFlNTIzODZhMzgyZjBlNGY3NiKIAgEBAgB4Pa6VDCWW__MSrqnre1HIN0Grt00ViSSuUjhqOC8OT3YAAADfMIHcBgkqhkiG9w0BBwaggc4wgcsCAQAwgcUGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMmqLyBTAegIn9XlK5AgEQgIGXZQjkBcl1dykDdqZBUQ6L1OfUivQy7JVYO2-ZJP7m6f1g8GzV47HX5phdtONAP7K_HQIflcgpkoCqd_fUnE114mSmiagWkbQ5sqAVV3ov-VeqgrvMe5ZFEWLMSluvBAqdjHEdMIkHMlhlj4ENZbzBfo9Wxk8b8SnwP4kc4gGivedzFXo-dwN8fxjjq_ZZ9JFOj2ijIbj5FyogDCN0drOfi8RORSEuCEmPvjFRMFAwcmwFkN2NPp89amA" + }, + "comments": { + "input": { + "GranteePrincipal": "The identity that is given permission to perform the operations specified in the grant.", + "KeyId": "The identifier of the KMS key to which the grant applies. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key.", + "Operations": "A list of operations that the grant allows." + }, + "output": { + "GrantId": "The unique identifier of the grant.", + "GrantToken": "The grant token." + } + }, + "description": "The following example creates a grant that allows the specified IAM role to encrypt data with the specified KMS key.", + "id": "to-create-a-grant-1477972226782", + "title": "To create a grant" + } + ], + "CreateKey": [ + { + "input": { + }, + "output": { + "KeyMetadata": { + "AWSAccountId": "111122223333", + "Arn": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "CreationDate": "2017-07-05T14:04:55-07:00", + "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT", + "Description": "", + "Enabled": true, + "EncryptionAlgorithms": [ + "SYMMETRIC_DEFAULT" + ], + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "KeyManager": "CUSTOMER", + "KeySpec": "SYMMETRIC_DEFAULT", + "KeyState": "Enabled", + "KeyUsage": "ENCRYPT_DECRYPT", + "MultiRegion": false, + "Origin": "AWS_KMS" + } + }, + "comments": { + "input": { + "Tags": "One or more tags. Each tag consists of a tag key and a tag value." + }, + "output": { + "KeyMetadata": "Detailed information about the KMS key that this operation creates." + } + }, + "description": "The following example creates a symmetric KMS key for encryption and decryption. No parameters are required for this operation.", + "id": "to-create-a-cmk-1478028992966", + "title": "To create a KMS key" + }, + { + "input": { + "KeySpec": "RSA_4096", + "KeyUsage": "ENCRYPT_DECRYPT" + }, + "output": { + "KeyMetadata": { + "AWSAccountId": "111122223333", + "Arn": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "CreationDate": "2021-04-05T14:04:55-07:00", + "CustomerMasterKeySpec": "RSA_4096", + "Description": "", + "Enabled": true, + "EncryptionAlgorithms": [ + "RSAES_OAEP_SHA_1", + "RSAES_OAEP_SHA_256" + ], + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "KeyManager": "CUSTOMER", + "KeySpec": "RSA_4096", + "KeyState": "Enabled", + "KeyUsage": "ENCRYPT_DECRYPT", + "MultiRegion": false, + "Origin": "AWS_KMS" + } + }, + "comments": { + "input": { + "KeySpec": "Describes the type of key material in the KMS key.", + "KeyUsage": "The cryptographic operations for which you can use the KMS key." + }, + "output": { + "KeyMetadata": "Detailed information about the KMS key that this operation creates." + } + }, + "description": "This example creates a KMS key that contains an asymmetric RSA key pair for encryption and decryption. The key spec and key usage can't be changed after the key is created.", + "id": "to-create-an-asymmetric-rsa-kms-key-for-encryption-and-decryption-1630533897833", + "title": "To create an asymmetric RSA KMS key for encryption and decryption" + }, + { + "input": { + "KeySpec": "ECC_NIST_P521", + "KeyUsage": "SIGN_VERIFY" + }, + "output": { + "KeyMetadata": { + "AWSAccountId": "111122223333", + "Arn": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "CreationDate": "2019-12-02T07:48:55-07:00", + "CustomerMasterKeySpec": "ECC_NIST_P521", + "Description": "", + "Enabled": true, + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "KeyManager": "CUSTOMER", + "KeySpec": "ECC_NIST_P521", + "KeyState": "Enabled", + "KeyUsage": "SIGN_VERIFY", + "MultiRegion": false, + "Origin": "AWS_KMS", + "SigningAlgorithms": [ + "ECDSA_SHA_512" + ] + } + }, + "comments": { + "input": { + "KeySpec": "Describes the type of key material in the KMS key.", + "KeyUsage": "The cryptographic operations for which you can use the KMS key." + }, + "output": { + "KeyMetadata": "Detailed information about the KMS key that this operation creates." + } + }, + "description": "This example creates a KMS key that contains an asymmetric elliptic curve (ECC) key pair for signing and verification. The key usage is required even though \"SIGN_VERIFY\" is the only valid value for ECC KMS keys. The key spec and key usage can't be changed after the key is created.", + "id": "to-create-an-asymmetric-elliptic-curve-kms-key-for-signing-and-verification-1630541089401", + "title": "To create an asymmetric elliptic curve KMS key for signing and verification" + }, + { + "input": { + "MultiRegion": true + }, + "output": { + "KeyMetadata": { + "AWSAccountId": "111122223333", + "Arn": "arn:aws:kms:us-west-2:111122223333:key/mrk-1234abcd12ab34cd56ef12345678990ab", + "CreationDate": "2021-09-02T016:15:21-09:00", + "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT", + "Description": "", + "Enabled": true, + "EncryptionAlgorithms": [ + "SYMMETRIC_DEFAULT" + ], + "KeyId": "mrk-1234abcd12ab34cd56ef12345678990ab", + "KeyManager": "CUSTOMER", + "KeySpec": "SYMMETRIC_DEFAULT", + "KeyState": "Enabled", + "KeyUsage": "ENCRYPT_DECRYPT", + "MultiRegion": true, + "MultiRegionConfiguration": { + "MultiRegionKeyType": "PRIMARY", + "PrimaryKey": { + "Arn": "arn:aws:kms:us-west-2:111122223333:key/mrk-1234abcd12ab34cd56ef12345678990ab", + "Region": "us-west-2" + }, + "ReplicaKeys": [ + + ] + }, + "Origin": "AWS_KMS" + } + }, + "comments": { + "input": { + "MultiRegion": "Indicates whether the KMS key is a multi-Region (True) or regional (False) key." + }, + "output": { + "KeyMetadata": "Detailed information about the KMS key that this operation creates." + } + }, + "description": "This example creates a multi-Region primary symmetric encryption key. Because the default values for all parameters create a symmetric encryption key, only the MultiRegion parameter is required for this KMS key.", + "id": "to-create-a-multi-region-primary-kms-key-1630599158567", + "title": "To create a multi-Region primary KMS key" + }, + { + "input": { + "Origin": "EXTERNAL" + }, + "output": { + "KeyMetadata": { + "AWSAccountId": "111122223333", + "Arn": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "CreationDate": "2019-12-02T07:48:55-07:00", + "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT", + "Description": "", + "Enabled": false, + "EncryptionAlgorithms": [ + "SYMMETRIC_DEFAULT" + ], + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "KeyManager": "CUSTOMER", + "KeySpec": "SYMMETRIC_DEFAULT", + "KeyState": "PendingImport", + "KeyUsage": "ENCRYPT_DECRYPT", + "MultiRegion": false, + "Origin": "EXTERNAL" + } + }, + "comments": { + "input": { + "Origin": "The source of the key material for the KMS key." + }, + "output": { + "KeyMetadata": "Detailed information about the KMS key that this operation creates." + } + }, + "description": "This example creates a KMS key with no key material. When the operation is complete, you can import your own key material into the KMS key. To create this KMS key, set the Origin parameter to EXTERNAL. ", + "id": "to-create-a-kms-key-for-imported-key-material-1630603607560", + "title": "To create a KMS key for imported key material" + }, + { + "input": { + "CustomKeyStoreId": "cks-1234567890abcdef0", + "Origin": "AWS_CLOUDHSM" + }, + "output": { + "KeyMetadata": { + "AWSAccountId": "111122223333", + "Arn": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "CloudHsmClusterId": "cluster-1a23b4cdefg", + "CreationDate": "2019-12-02T07:48:55-07:00", + "CustomKeyStoreId": "cks-1234567890abcdef0", + "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT", + "Description": "", + "Enabled": true, + "EncryptionAlgorithms": [ + "SYMMETRIC_DEFAULT" + ], + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "KeyManager": "CUSTOMER", + "KeySpec": "SYMMETRIC_DEFAULT", + "KeyState": "Enabled", + "KeyUsage": "ENCRYPT_DECRYPT", + "MultiRegion": false, + "Origin": "AWS_CLOUDHSM" + } + }, + "comments": { + "input": { + "CustomKeyStoreId": "Identifies the custom key store that hosts the KMS key.", + "Origin": "Indicates the source of the key material for the KMS key." + }, + "output": { + "KeyMetadata": "Detailed information about the KMS key that this operation creates." + } + }, + "description": "This example creates a KMS key in the specified custom key store. The operation creates the KMS key and its metadata in AWS KMS and the key material in the AWS CloudHSM cluster associated with the custom key store. This example requires the Origin and CustomKeyStoreId parameters.", + "id": "to-create-a-kms-key-in-a-custom-key-store-1630604382908", + "title": "To create a KMS key in a custom key store" + }, + { + "input": { + "KeySpec": "HMAC_384", + "KeyUsage": "GENERATE_VERIFY_MAC" + }, + "output": { + "KeyMetadata": { + "AWSAccountId": "111122223333", + "Arn": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "CreationDate": "2022-04-05T14:04:55-07:00", + "CustomerMasterKeySpec": "HMAC_384", + "Description": "", + "Enabled": true, + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "KeyManager": "CUSTOMER", + "KeySpec": "HMAC_384", + "KeyState": "Enabled", + "KeyUsage": "GENERATE_VERIFY_MAC", + "MacAlgorithms": [ + "HMAC_SHA_384" + ], + "MultiRegion": false, + "Origin": "AWS_KMS" + } + }, + "comments": { + "input": { + "KeySpec": "Describes the type of key material in the KMS key.", + "KeyUsage": "The cryptographic operations for which you can use the KMS key." + }, + "output": { + "KeyMetadata": "Detailed information about the KMS key that this operation creates." + } + }, + "description": "This example creates a 384-bit symmetric HMAC KMS key. The GENERATE_VERIFY_MAC key usage value is required even though it's the only valid value for HMAC KMS keys. The key spec and key usage can't be changed after the key is created. ", + "id": "to-create-an-hmac-kms-key-1630628752841", + "title": "To create an HMAC KMS key" + } + ], + "Decrypt": [ + { + "input": { + "CiphertextBlob": "", + "KeyId": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "output": { + "KeyId": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "Plaintext": "" + }, + "comments": { + "input": { + "CiphertextBlob": "The encrypted data (ciphertext).", + "KeyId": "A key identifier for the KMS key to use to decrypt the data." + }, + "output": { + "KeyId": "The Amazon Resource Name (ARN) of the KMS key that was used to decrypt the data.", + "Plaintext": "The decrypted (plaintext) data." + } + }, + "description": "The following example decrypts data that was encrypted with a KMS key.", + "id": "to-decrypt-data-1478281622886", + "title": "To decrypt data" + } + ], + "DeleteAlias": [ + { + "input": { + "AliasName": "alias/ExampleAlias" + }, + "comments": { + "input": { + "AliasName": "The alias to delete." + } + }, + "description": "The following example deletes the specified alias.", + "id": "to-delete-an-alias-1478285209338", + "title": "To delete an alias" + } + ], + "DeleteCustomKeyStore": [ + { + "input": { + "CustomKeyStoreId": "cks-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + "CustomKeyStoreId": "The ID of the custom key store to be deleted." + }, + "output": { + } + }, + "description": "This example deletes a custom key store from AWS KMS. This operation does not delete the AWS CloudHSM cluster that was associated with the CloudHSM cluster. This operation doesn't return any data. To verify that the operation was successful, use the DescribeCustomKeyStores operation. ", + "id": "to-delete-a-custom-key-store-from-aws-kms-1628630837145", + "title": "To delete a custom key store from AWS KMS" + } + ], + "DeleteImportedKeyMaterial": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key whose imported key material you are deleting. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + } + }, + "description": "The following example deletes the imported key material from the specified KMS key.", + "id": "to-delete-imported-key-material-1478561674507", + "title": "To delete imported key material" + } + ], + "DescribeCustomKeyStores": [ + { + "input": { + }, + "output": { + "CustomKeyStores": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + "CustomKeyStores": "Details about each custom key store in the account and Region." + } + }, + "description": "This example gets detailed information about all AWS KMS custom key stores in an AWS account and Region. To get all key stores, do not enter a custom key store name or ID.", + "id": "to-get-detailed-information-about-custom-key-stores-in-the-account-and-region-1628628556811", + "title": "To get detailed information about custom key stores in the account and Region" + }, + { + "input": { + "CustomKeyStoreName": "ExampleKeyStore" + }, + "output": { + "CustomKeyStores": [ + { + "CloudHsmClusterId": "cluster-1a23b4cdefg", + "ConnectionState": "CONNECTED", + "CreationDate": "1.499288695918E9", + "CustomKeyStoreId": "cks-1234567890abcdef0", + "CustomKeyStoreName": "ExampleKeyStore", + "TrustAnchorCertificate": "" + } + ] + }, + "comments": { + "input": { + "CustomKeyStoreName": "The friendly name of the custom key store." + }, + "output": { + "CustomKeyStores": "Detailed information about the specified custom key store." + } + }, + "description": "This example gets detailed information about a particular AWS KMS custom key store that is associate with an AWS CloudHSM cluster. To limit the output to a particular custom key store, provide the custom key store name or ID. ", + "id": "to-get-detailed-information-about-a-custom-key-store-associated-with-a-cloudhsm-cluster-1628628885843", + "title": "To get detailed information about a custom key store associated with a CloudHSM cluster." + } + ], + "DescribeKey": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "output": { + "KeyMetadata": { + "AWSAccountId": "111122223333", + "Arn": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "CreationDate": "2017-07-05T14:04:55-07:00", + "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT", + "Description": "", + "Enabled": true, + "EncryptionAlgorithms": [ + "SYMMETRIC_DEFAULT" + ], + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "KeyManager": "CUSTOMER", + "KeySpec": "SYMMETRIC_DEFAULT", + "KeyState": "Enabled", + "KeyUsage": "ENCRYPT_DECRYPT", + "MultiRegion": false, + "Origin": "AWS_KMS" + } + }, + "comments": { + "input": { + "KeyId": "An identifier for the KMS key. You can use the key ID, key ARN, alias name, alias ARN of the KMS key." + }, + "output": { + "KeyMetadata": "An object that contains information about the specified KMS key." + } + }, + "description": "The following example gets metadata for a symmetric encryption KMS key.", + "id": "get-key-details-1478565820907", + "title": "To get details about a KMS key" + }, + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "output": { + "KeyMetadata": { + "AWSAccountId": "111122223333", + "Arn": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "CreationDate": 1571767572.317, + "CustomerMasterKeySpec": "RSA_2048", + "Description": "", + "Enabled": false, + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "KeyManager": "CUSTOMER", + "KeySpec": "RSA_2048", + "KeyState": "Disabled", + "KeyUsage": "SIGN_VERIFY", + "MultiRegion": false, + "Origin": "AWS_KMS", + "SigningAlgorithms": [ + "RSASSA_PKCS1_V1_5_SHA_256", + "RSASSA_PKCS1_V1_5_SHA_384", + "RSASSA_PKCS1_V1_5_SHA_512", + "RSASSA_PSS_SHA_256", + "RSASSA_PSS_SHA_384", + "RSASSA_PSS_SHA_512" + ] + } + }, + "comments": { + "input": { + "KeyId": "An identifier for the KMS key. You can use the key ID, key ARN, alias name, alias ARN of the KMS key." + }, + "output": { + "KeyMetadata": "An object that contains information about the specified KMS key." + } + }, + "description": "The following example gets metadata for an asymmetric RSA KMS key used for signing and verification.", + "id": "to-get-details-about-an-rsa-asymmetric-kms-key-1637971611761", + "title": "To get details about an RSA asymmetric KMS key" + }, + { + "input": { + "KeyId": "arn:aws:kms:ap-northeast-1:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab" + }, + "output": { + "KeyMetadata": { + "AWSAccountId": "111122223333", + "Arn": "arn:aws:kms:ap-northeast-1:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab", + "CreationDate": 1586329200.918, + "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT", + "Description": "", + "Enabled": true, + "EncryptionAlgorithms": [ + "SYMMETRIC_DEFAULT" + ], + "KeyId": "mrk-1234abcd12ab34cd56ef1234567890ab", + "KeyManager": "CUSTOMER", + "KeyState": "Enabled", + "KeyUsage": "ENCRYPT_DECRYPT", + "MultiRegion": true, + "MultiRegionConfiguration": { + "MultiRegionKeyType": "PRIMARY", + "PrimaryKey": { + "Arn": "arn:aws:kms:us-west-2:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab", + "Region": "us-west-2" + }, + "ReplicaKeys": [ + { + "Arn": "arn:aws:kms:eu-west-1:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab", + "Region": "eu-west-1" + }, + { + "Arn": "arn:aws:kms:ap-northeast-1:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab", + "Region": "ap-northeast-1" + }, + { + "Arn": "arn:aws:kms:sa-east-1:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab", + "Region": "sa-east-1" + } + ] + }, + "Origin": "AWS_KMS" + } + }, + "comments": { + "input": { + "KeyId": "An identifier for the KMS key. You can use the key ID, key ARN, alias name, alias ARN of the KMS key." + }, + "output": { + "KeyMetadata": "An object that contains information about the specified KMS key." + } + }, + "description": "The following example gets metadata for a multi-Region replica key. This multi-Region key is a symmetric encryption key. DescribeKey returns information about the primary key and all of its replicas.", + "id": "to-get-details-about-a-multi-region-key-1637969624239", + "title": "To get details about a multi-Region key" + }, + { + "input": { + "KeyId": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "output": { + "KeyMetadata": { + "AWSAccountId": "123456789012", + "Arn": "arn:aws:kms:us-west-2:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "CreationDate": 1566160362.664, + "CustomerMasterKeySpec": "HMAC_256", + "Description": "Development test key", + "Enabled": true, + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "KeyManager": "CUSTOMER", + "KeyState": "Enabled", + "KeyUsage": "GENERATE_VERIFY_MAC", + "MacAlgorithms": [ + "HMAC_SHA_256" + ], + "MultiRegion": false, + "Origin": "AWS_KMS" + } + }, + "comments": { + "input": { + "KeyId": "An identifier for the KMS key. You can use the key ID, key ARN, alias name, alias ARN of the KMS key." + }, + "output": { + "KeyMetadata": "An object that contains information about the specified KMS key." + } + }, + "description": "The following example gets the metadata of an HMAC KMS key. ", + "id": "to-get-details-about-an-hmac-kms-key-1637970472619", + "title": "To get details about an HMAC KMS key" + } + ], + "DisableKey": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key to disable. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + } + }, + "description": "The following example disables the specified KMS key.", + "id": "to-disable-a-cmk-1478566583659", + "title": "To disable a KMS key" + } + ], + "DisableKeyRotation": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key whose key material will no longer be rotated. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + } + }, + "description": "The following example disables automatic annual rotation of the key material for the specified KMS key.", + "id": "to-disable-automatic-rotation-of-key-material-1478624396092", + "title": "To disable automatic rotation of key material" + } + ], + "DisconnectCustomKeyStore": [ + { + "input": { + "CustomKeyStoreId": "cks-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + "CustomKeyStoreId": "The ID of the custom key store." + }, + "output": { + } + }, + "description": "This example disconnects an AWS KMS custom key store from its AWS CloudHSM cluster. This operation doesn't return any data. To verify that the custom key store is disconnected, use the DescribeCustomKeyStores operation.", + "id": "to-disconnect-a-custom-key-store-from-its-cloudhsm-cluster-1628627955156", + "title": "To disconnect a custom key store from its CloudHSM cluster" + } + ], + "EnableKey": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key to enable. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + } + }, + "description": "The following example enables the specified KMS key.", + "id": "to-enable-a-cmk-1478627501129", + "title": "To enable a KMS key" + } + ], + "EnableKeyRotation": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key whose key material will be rotated annually. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + } + }, + "description": "The following example enables automatic annual rotation of the key material for the specified KMS key.", + "id": "to-enable-automatic-rotation-of-key-material-1478629109677", + "title": "To enable automatic rotation of key material" + } + ], + "Encrypt": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "Plaintext": "" + }, + "output": { + "CiphertextBlob": "", + "KeyId": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key to use for encryption. You can use the key ID or Amazon Resource Name (ARN) of the KMS key, or the name or ARN of an alias that refers to the KMS key.", + "Plaintext": "The data to encrypt." + }, + "output": { + "CiphertextBlob": "The encrypted data (ciphertext).", + "KeyId": "The ARN of the KMS key that was used to encrypt the data." + } + }, + "description": "The following example encrypts data with the specified KMS key.", + "id": "to-encrypt-data-1478906026012", + "title": "To encrypt data" + } + ], + "GenerateDataKey": [ + { + "input": { + "KeyId": "alias/ExampleAlias", + "KeySpec": "AES_256" + }, + "output": { + "CiphertextBlob": "", + "KeyId": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "Plaintext": "" + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key to use to encrypt the data key. You can use the key ID or Amazon Resource Name (ARN) of the KMS key, or the name or ARN of an alias that refers to the KMS key.", + "KeySpec": "Specifies the type of data key to return." + }, + "output": { + "CiphertextBlob": "The encrypted data key.", + "KeyId": "The ARN of the KMS key that was used to encrypt the data key.", + "Plaintext": "The unencrypted (plaintext) data key." + } + }, + "description": "The following example generates a 256-bit symmetric data encryption key (data key) in two formats. One is the unencrypted (plainext) data key, and the other is the data key encrypted with the specified KMS key.", + "id": "to-generate-a-data-key-1478912956062", + "title": "To generate a data key" + } + ], + "GenerateDataKeyPair": [ + { + "input": { + "KeyId": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "KeyPairSpec": "RSA_3072" + }, + "output": { + "KeyId": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "KeyPairSpec": "RSA_3072", + "PrivateKeyCiphertextBlob": "", + "PrivateKeyPlaintext": "", + "PublicKey": "" + }, + "comments": { + "input": { + "KeyId": "The key ID of the symmetric encryption KMS key that encrypts the private RSA key in the data key pair.", + "KeyPairSpec": "The requested key spec of the RSA data key pair." + }, + "output": { + "KeyId": "The key ARN of the symmetric encryption KMS key that was used to encrypt the private key.", + "KeyPairSpec": "The actual key spec of the RSA data key pair.", + "PrivateKeyCiphertextBlob": "The encrypted private key of the RSA data key pair.", + "PrivateKeyPlaintext": "The plaintext private key of the RSA data key pair.", + "PublicKey": "The public key (plaintext) of the RSA data key pair." + } + }, + "description": "This example generates an RSA data key pair for encryption and decryption. The operation returns a plaintext public key and private key, and a copy of the private key that is encrypted under a symmetric encryption KMS key that you specify.", + "id": "to-generate-an-rsa-key-pair-for-encryption-and-decryption-1628619376878", + "title": "To generate an RSA key pair for encryption and decryption" + } + ], + "GenerateDataKeyPairWithoutPlaintext": [ + { + "input": { + "KeyId": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "KeyPairSpec": "ECC_NIST_P521" + }, + "output": { + "KeyId": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "KeyPairSpec": "ECC_NIST_P521", + "PrivateKeyCiphertextBlob": "", + "PublicKey": "" + }, + "comments": { + "input": { + "KeyId": "The symmetric encryption KMS key that encrypts the private key of the ECC data key pair.", + "KeyPairSpec": "The requested key spec of the ECC asymmetric data key pair." + }, + "output": { + "KeyId": "The key ARN of the symmetric encryption KMS key that encrypted the private key in the ECC asymmetric data key pair.", + "KeyPairSpec": "The actual key spec of the ECC asymmetric data key pair.", + "PrivateKeyCiphertextBlob": "The encrypted private key of the asymmetric ECC data key pair.", + "PublicKey": "The public key (plaintext)." + } + }, + "description": "This example returns an asymmetric elliptic curve (ECC) data key pair. The private key is encrypted under the symmetric encryption KMS key that you specify. This operation doesn't return a plaintext (unencrypted) private key.", + "id": "to-generate-an-asymmetric-data-key-pair-without-a-plaintext-key-1628620971564", + "title": "To generate an asymmetric data key pair without a plaintext key" + } + ], + "GenerateDataKeyWithoutPlaintext": [ + { + "input": { + "KeyId": "alias/ExampleAlias", + "KeySpec": "AES_256" + }, + "output": { + "CiphertextBlob": "", + "KeyId": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key to use to encrypt the data key. You can use the key ID or Amazon Resource Name (ARN) of the KMS key, or the name or ARN of an alias that refers to the KMS key.", + "KeySpec": "Specifies the type of data key to return." + }, + "output": { + "CiphertextBlob": "The encrypted data key.", + "KeyId": "The ARN of the KMS key that was used to encrypt the data key." + } + }, + "description": "The following example generates an encrypted copy of a 256-bit symmetric data encryption key (data key). The data key is encrypted with the specified KMS key.", + "id": "to-generate-an-encrypted-data-key-1478914121134", + "title": "To generate an encrypted data key" + } + ], + "GenerateMac": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "MacAlgorithm": "HMAC_SHA_384", + "Message": "Hello World" + }, + "output": { + "KeyId": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "Mac": "", + "MacAlgorithm": "HMAC_SHA_384" + }, + "comments": { + "input": { + "KeyId": "The HMAC KMS key input to the HMAC algorithm.", + "MacAlgorithm": "The HMAC algorithm requested for the operation.", + "Message": "The message input to the HMAC algorithm." + }, + "output": { + "KeyId": "The key ARN of the HMAC KMS key used in the operation.", + "Mac": "The HMAC tag that results from this operation.", + "MacAlgorithm": "The HMAC algorithm used in the operation." + } + }, + "description": "This example generates an HMAC for a message, an HMAC KMS key, and a MAC algorithm. The algorithm must be supported by the specified HMAC KMS key.", + "id": "to-generate-an-hmac-for-a-message-1631570135665", + "title": "To generate an HMAC for a message" + } + ], + "GenerateRandom": [ + { + "input": { + "NumberOfBytes": 32 + }, + "output": { + "Plaintext": "" + }, + "comments": { + "input": { + "NumberOfBytes": "The length of the random data, specified in number of bytes." + }, + "output": { + "Plaintext": "The random data." + } + }, + "description": "The following example generates 32 bytes of random data.", + "id": "to-generate-random-data-1479163645600", + "title": "To generate random data" + } + ], + "GetKeyPolicy": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "PolicyName": "default" + }, + "output": { + "Policy": "{\n \"Version\" : \"2012-10-17\",\n \"Id\" : \"key-default-1\",\n \"Statement\" : [ {\n \"Sid\" : \"Enable IAM User Permissions\",\n \"Effect\" : \"Allow\",\n \"Principal\" : {\n \"AWS\" : \"arn:aws:iam::111122223333:root\"\n },\n \"Action\" : \"kms:*\",\n \"Resource\" : \"*\"\n } ]\n}" + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key whose key policy you want to retrieve. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key.", + "PolicyName": "The name of the key policy to retrieve." + }, + "output": { + "Policy": "The key policy document." + } + }, + "description": "The following example retrieves the key policy for the specified KMS key.", + "id": "to-retrieve-a-key-policy-1479170128325", + "title": "To retrieve a key policy" + } + ], + "GetKeyRotationStatus": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "output": { + "KeyRotationEnabled": true + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key whose key material rotation status you want to retrieve. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + }, + "output": { + "KeyRotationEnabled": "A boolean that indicates the key material rotation status. Returns true when automatic annual rotation of the key material is enabled, or false when it is not." + } + }, + "description": "The following example retrieves the status of automatic annual rotation of the key material for the specified KMS key.", + "id": "to-retrieve-the-rotation-status-for-a-cmk-1479172287408", + "title": "To retrieve the rotation status for a KMS key" + } + ], + "GetParametersForImport": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "WrappingAlgorithm": "RSAES_OAEP_SHA_1", + "WrappingKeySpec": "RSA_2048" + }, + "output": { + "ImportToken": "", + "KeyId": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "ParametersValidTo": "2016-12-01T14:52:17-08:00", + "PublicKey": "" + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key for which to retrieve the public key and import token. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key.", + "WrappingAlgorithm": "The algorithm that you will use to encrypt the key material before importing it.", + "WrappingKeySpec": "The type of wrapping key (public key) to return in the response." + }, + "output": { + "ImportToken": "The import token to send with a subsequent ImportKeyMaterial request.", + "KeyId": "The ARN of the KMS key for which you are retrieving the public key and import token. This is the same KMS key specified in the request.", + "ParametersValidTo": "The time at which the import token and public key are no longer valid.", + "PublicKey": "The public key to use to encrypt the key material before importing it." + } + }, + "description": "The following example retrieves the public key and import token for the specified KMS key.", + "id": "to-retrieve-the-public-key-and-import-token-for-a-cmk-1480626483211", + "title": "To retrieve the public key and import token for a KMS key" + } + ], + "GetPublicKey": [ + { + "input": { + "KeyId": "arn:aws:kms:us-west-2:111122223333:key/0987dcba-09fe-87dc-65ba-ab0987654321" + }, + "output": { + "CustomerMasterKeySpec": "RSA_4096", + "EncryptionAlgorithms": [ + "RSAES_OAEP_SHA_1", + "RSAES_OAEP_SHA_256" + ], + "KeyId": "arn:aws:kms:us-west-2:111122223333:key/0987dcba-09fe-87dc-65ba-ab0987654321", + "KeyUsage": "ENCRYPT_DECRYPT", + "PublicKey": "" + }, + "comments": { + "input": { + "KeyId": "The key ARN of the asymmetric KMS key." + }, + "output": { + "CustomerMasterKeySpec": "The key spec of the asymmetric KMS key from which the public key was downloaded.", + "EncryptionAlgorithms": "The encryption algorithms supported by the asymmetric KMS key that was downloaded.", + "KeyId": "The key ARN of the asymmetric KMS key from which the public key was downloaded.", + "KeyUsage": "The key usage of the asymmetric KMS key from which the public key was downloaded.", + "PublicKey": "The public key (plaintext) of the asymmetric KMS key." + } + }, + "description": "This example gets the public key of an asymmetric RSA KMS key used for encryption and decryption. The operation returns the key spec, key usage, and encryption or signing algorithms to help you use the public key correctly outside of AWS KMS.", + "id": "to-download-the-public-key-of-an-asymmetric-kms-key-1628621691873", + "title": "To download the public key of an asymmetric KMS key" + } + ], + "ImportKeyMaterial": [ + { + "input": { + "EncryptedKeyMaterial": "", + "ExpirationModel": "KEY_MATERIAL_DOES_NOT_EXPIRE", + "ImportToken": "", + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "EncryptedKeyMaterial": "The encrypted key material to import.", + "ExpirationModel": "A value that specifies whether the key material expires.", + "ImportToken": "The import token that you received in the response to a previous GetParametersForImport request.", + "KeyId": "The identifier of the KMS key to import the key material into. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + } + }, + "description": "The following example imports key material into the specified KMS key.", + "id": "to-import-key-material-into-a-cmk-1480630551969", + "title": "To import key material into a KMS key" + } + ], + "ListAliases": [ + { + "output": { + "Aliases": [ + { + "AliasArn": "arn:aws:kms:us-east-2:111122223333:alias/aws/acm", + "AliasName": "alias/aws/acm", + "TargetKeyId": "da03f6f7-d279-427a-9cae-de48d07e5b66" + }, + { + "AliasArn": "arn:aws:kms:us-east-2:111122223333:alias/aws/ebs", + "AliasName": "alias/aws/ebs", + "TargetKeyId": "25a217e7-7170-4b8c-8bf6-045ea5f70e5b" + }, + { + "AliasArn": "arn:aws:kms:us-east-2:111122223333:alias/aws/rds", + "AliasName": "alias/aws/rds", + "TargetKeyId": "7ec3104e-c3f2-4b5c-bf42-bfc4772c6685" + }, + { + "AliasArn": "arn:aws:kms:us-east-2:111122223333:alias/aws/redshift", + "AliasName": "alias/aws/redshift", + "TargetKeyId": "08f7a25a-69e2-4fb5-8f10-393db27326fa" + }, + { + "AliasArn": "arn:aws:kms:us-east-2:111122223333:alias/aws/s3", + "AliasName": "alias/aws/s3", + "TargetKeyId": "d2b0f1a3-580d-4f79-b836-bc983be8cfa5" + }, + { + "AliasArn": "arn:aws:kms:us-east-2:111122223333:alias/example1", + "AliasName": "alias/example1", + "TargetKeyId": "4da1e216-62d0-46c5-a7c0-5f3a3d2f8046" + }, + { + "AliasArn": "arn:aws:kms:us-east-2:111122223333:alias/example2", + "AliasName": "alias/example2", + "TargetKeyId": "f32fef59-2cc2-445b-8573-2d73328acbee" + }, + { + "AliasArn": "arn:aws:kms:us-east-2:111122223333:alias/example3", + "AliasName": "alias/example3", + "TargetKeyId": "1374ef38-d34e-4d5f-b2c9-4e0daee38855" + } + ], + "Truncated": false + }, + "comments": { + "output": { + "Aliases": "A list of aliases, including the key ID of the KMS key that each alias refers to.", + "Truncated": "A boolean that indicates whether there are more items in the list. Returns true when there are more items, or false when there are not." + } + }, + "description": "The following example lists aliases.", + "id": "to-list-aliases-1480729693349", + "title": "To list aliases" + } + ], + "ListGrants": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "output": { + "Grants": [ + { + "CreationDate": "2016-10-25T14:37:41-07:00", + "GrantId": "91ad875e49b04a9d1f3bdeb84d821f9db6ea95e1098813f6d47f0c65fbe2a172", + "GranteePrincipal": "acm.us-east-2.amazonaws.com", + "IssuingAccount": "arn:aws:iam::111122223333:root", + "KeyId": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "Operations": [ + "Encrypt", + "ReEncryptFrom", + "ReEncryptTo" + ], + "RetiringPrincipal": "acm.us-east-2.amazonaws.com" + }, + { + "CreationDate": "2016-10-25T14:37:41-07:00", + "GrantId": "a5d67d3e207a8fc1f4928749ee3e52eb0440493a8b9cf05bbfad91655b056200", + "GranteePrincipal": "acm.us-east-2.amazonaws.com", + "IssuingAccount": "arn:aws:iam::111122223333:root", + "KeyId": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "Operations": [ + "ReEncryptFrom", + "ReEncryptTo" + ], + "RetiringPrincipal": "acm.us-east-2.amazonaws.com" + }, + { + "CreationDate": "2016-10-25T14:37:41-07:00", + "GrantId": "c541aaf05d90cb78846a73b346fc43e65be28b7163129488c738e0c9e0628f4f", + "GranteePrincipal": "acm.us-east-2.amazonaws.com", + "IssuingAccount": "arn:aws:iam::111122223333:root", + "KeyId": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "Operations": [ + "Encrypt", + "ReEncryptFrom", + "ReEncryptTo" + ], + "RetiringPrincipal": "acm.us-east-2.amazonaws.com" + }, + { + "CreationDate": "2016-10-25T14:37:41-07:00", + "GrantId": "dd2052c67b4c76ee45caf1dc6a1e2d24e8dc744a51b36ae2f067dc540ce0105c", + "GranteePrincipal": "acm.us-east-2.amazonaws.com", + "IssuingAccount": "arn:aws:iam::111122223333:root", + "KeyId": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "Operations": [ + "Encrypt", + "ReEncryptFrom", + "ReEncryptTo" + ], + "RetiringPrincipal": "acm.us-east-2.amazonaws.com" + } + ], + "Truncated": true + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key whose grants you want to list. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + }, + "output": { + "Grants": "A list of grants.", + "Truncated": "A boolean that indicates whether there are more items in the list. Returns true when there are more items, or false when there are not." + } + }, + "description": "The following example lists grants for the specified KMS key.", + "id": "to-list-grants-for-a-cmk-1481067365389", + "title": "To list grants for a KMS key" + } + ], + "ListKeyPolicies": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "output": { + "PolicyNames": [ + "default" + ], + "Truncated": false + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key whose key policies you want to list. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + }, + "output": { + "PolicyNames": "A list of key policy names.", + "Truncated": "A boolean that indicates whether there are more items in the list. Returns true when there are more items, or false when there are not." + } + }, + "description": "The following example lists key policies for the specified KMS key.", + "id": "to-list-key-policies-for-a-cmk-1481069780998", + "title": "To list key policies for a KMS key" + } + ], + "ListKeys": [ + { + "output": { + "Keys": [ + { + "KeyArn": "arn:aws:kms:us-east-2:111122223333:key/0d990263-018e-4e65-a703-eff731de951e", + "KeyId": "0d990263-018e-4e65-a703-eff731de951e" + }, + { + "KeyArn": "arn:aws:kms:us-east-2:111122223333:key/144be297-0ae1-44ac-9c8f-93cd8c82f841", + "KeyId": "144be297-0ae1-44ac-9c8f-93cd8c82f841" + }, + { + "KeyArn": "arn:aws:kms:us-east-2:111122223333:key/21184251-b765-428e-b852-2c7353e72571", + "KeyId": "21184251-b765-428e-b852-2c7353e72571" + }, + { + "KeyArn": "arn:aws:kms:us-east-2:111122223333:key/214fe92f-5b03-4ae1-b350-db2a45dbe10c", + "KeyId": "214fe92f-5b03-4ae1-b350-db2a45dbe10c" + }, + { + "KeyArn": "arn:aws:kms:us-east-2:111122223333:key/339963f2-e523-49d3-af24-a0fe752aa458", + "KeyId": "339963f2-e523-49d3-af24-a0fe752aa458" + }, + { + "KeyArn": "arn:aws:kms:us-east-2:111122223333:key/b776a44b-df37-4438-9be4-a27494e4271a", + "KeyId": "b776a44b-df37-4438-9be4-a27494e4271a" + }, + { + "KeyArn": "arn:aws:kms:us-east-2:111122223333:key/deaf6c9e-cf2c-46a6-bf6d-0b6d487cffbb", + "KeyId": "deaf6c9e-cf2c-46a6-bf6d-0b6d487cffbb" + } + ], + "Truncated": false + }, + "comments": { + "output": { + "Keys": "A list of KMS keys, including the key ID and Amazon Resource Name (ARN) of each one.", + "Truncated": "A boolean that indicates whether there are more items in the list. Returns true when there are more items, or false when there are not." + } + }, + "description": "The following example lists KMS keys.", + "id": "to-list-cmks-1481071643069", + "title": "To list KMS keys" + } + ], + "ListResourceTags": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "output": { + "Tags": [ + { + "TagKey": "CostCenter", + "TagValue": "87654" + }, + { + "TagKey": "CreatedBy", + "TagValue": "ExampleUser" + }, + { + "TagKey": "Purpose", + "TagValue": "Test" + } + ], + "Truncated": false + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key whose tags you are listing. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + }, + "output": { + "Tags": "A list of tags.", + "Truncated": "A boolean that indicates whether there are more items in the list. Returns true when there are more items, or false when there are not." + } + }, + "description": "The following example lists tags for a KMS key.", + "id": "to-list-tags-for-a-cmk-1483996855796", + "title": "To list tags for a KMS key" + } + ], + "ListRetirableGrants": [ + { + "input": { + "RetiringPrincipal": "arn:aws:iam::111122223333:role/ExampleRole" + }, + "output": { + "Grants": [ + { + "CreationDate": "2016-12-07T11:09:35-08:00", + "GrantId": "0c237476b39f8bc44e45212e08498fbe3151305030726c0590dd8d3e9f3d6a60", + "GranteePrincipal": "arn:aws:iam::111122223333:role/ExampleRole", + "IssuingAccount": "arn:aws:iam::444455556666:root", + "KeyId": "arn:aws:kms:us-east-2:444455556666:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "Operations": [ + "Decrypt", + "Encrypt" + ], + "RetiringPrincipal": "arn:aws:iam::111122223333:role/ExampleRole" + } + ], + "Truncated": false + }, + "comments": { + "input": { + "RetiringPrincipal": "The retiring principal whose grants you want to list. Use the Amazon Resource Name (ARN) of a principal such as an AWS account (root), IAM user, federated user, or assumed role user." + }, + "output": { + "Grants": "A list of grants that the specified principal can retire.", + "Truncated": "A boolean that indicates whether there are more items in the list. Returns true when there are more items, or false when there are not." + } + }, + "description": "The following example lists the grants that the specified principal (identity) can retire.", + "id": "to-list-grants-that-the-specified-principal-can-retire-1481140499620", + "title": "To list grants that the specified principal can retire" + } + ], + "PutKeyPolicy": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "Policy": "{\n \"Version\": \"2012-10-17\",\n \"Id\": \"custom-policy-2016-12-07\",\n \"Statement\": [\n {\n \"Sid\": \"Enable IAM User Permissions\",\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"AWS\": \"arn:aws:iam::111122223333:root\"\n },\n \"Action\": \"kms:*\",\n \"Resource\": \"*\"\n },\n {\n \"Sid\": \"Allow access for Key Administrators\",\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"AWS\": [\n \"arn:aws:iam::111122223333:user/ExampleAdminUser\",\n \"arn:aws:iam::111122223333:role/ExampleAdminRole\"\n ]\n },\n \"Action\": [\n \"kms:Create*\",\n \"kms:Describe*\",\n \"kms:Enable*\",\n \"kms:List*\",\n \"kms:Put*\",\n \"kms:Update*\",\n \"kms:Revoke*\",\n \"kms:Disable*\",\n \"kms:Get*\",\n \"kms:Delete*\",\n \"kms:ScheduleKeyDeletion\",\n \"kms:CancelKeyDeletion\"\n ],\n \"Resource\": \"*\"\n },\n {\n \"Sid\": \"Allow use of the key\",\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"AWS\": \"arn:aws:iam::111122223333:role/ExamplePowerUserRole\"\n },\n \"Action\": [\n \"kms:Encrypt\",\n \"kms:Decrypt\",\n \"kms:ReEncrypt*\",\n \"kms:GenerateDataKey*\",\n \"kms:DescribeKey\"\n ],\n \"Resource\": \"*\"\n },\n {\n \"Sid\": \"Allow attachment of persistent resources\",\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"AWS\": \"arn:aws:iam::111122223333:role/ExamplePowerUserRole\"\n },\n \"Action\": [\n \"kms:CreateGrant\",\n \"kms:ListGrants\",\n \"kms:RevokeGrant\"\n ],\n \"Resource\": \"*\",\n \"Condition\": {\n \"Bool\": {\n \"kms:GrantIsForAWSResource\": \"true\"\n }\n }\n }\n ]\n}\n", + "PolicyName": "default" + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key to attach the key policy to. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key.", + "Policy": "The key policy document.", + "PolicyName": "The name of the key policy." + } + }, + "description": "The following example attaches a key policy to the specified KMS key.", + "id": "to-attach-a-key-policy-to-a-cmk-1481147345018", + "title": "To attach a key policy to a KMS key" + } + ], + "ReEncrypt": [ + { + "input": { + "CiphertextBlob": "", + "DestinationKeyId": "0987dcba-09fe-87dc-65ba-ab0987654321" + }, + "output": { + "CiphertextBlob": "", + "KeyId": "arn:aws:kms:us-east-2:111122223333:key/0987dcba-09fe-87dc-65ba-ab0987654321", + "SourceKeyId": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "CiphertextBlob": "The data to reencrypt.", + "DestinationKeyId": "The identifier of the KMS key to use to reencrypt the data. You can use any valid key identifier.", + "SourceKeyId": "The identifier of the KMS key to use to decrypt the data. You can use any valid key identifier." + }, + "output": { + "CiphertextBlob": "The reencrypted data.", + "KeyId": "The ARN of the KMS key that was used to reencrypt the data.", + "SourceKeyId": "The ARN of the KMS key that was originally used to encrypt the data." + } + }, + "description": "The following example reencrypts data with the specified KMS key.", + "id": "to-reencrypt-data-1481230358001", + "title": "To reencrypt data" + } + ], + "ReplicateKey": [ + { + "input": { + "KeyId": "arn:aws:kms:us-east-1:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab", + "ReplicaRegion": "us-west-2" + }, + "output": { + "ReplicaKeyMetadata": { + "AWSAccountId": "111122223333", + "Arn": "arn:aws:kms:us-west-2:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab", + "CreationDate": 1607472987.918, + "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT", + "Description": "", + "Enabled": true, + "EncryptionAlgorithms": [ + "SYMMETRIC_DEFAULT" + ], + "KeyId": "mrk-1234abcd12ab34cd56ef1234567890ab", + "KeyManager": "CUSTOMER", + "KeyState": "Enabled", + "KeyUsage": "ENCRYPT_DECRYPT", + "MultiRegion": true, + "MultiRegionConfiguration": { + "MultiRegionKeyType": "REPLICA", + "PrimaryKey": { + "Arn": "arn:aws:kms:us-east-1:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab", + "Region": "us-east-1" + }, + "ReplicaKeys": [ + { + "Arn": "arn:aws:kms:us-west-2:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab", + "Region": "us-west-2" + } + ] + }, + "Origin": "AWS_KMS" + }, + "ReplicaPolicy": "{\n \"Version\" : \"2012-10-17\",\n \"Id\" : \"key-default-1\",...}", + "ReplicaTags": [ + + ] + }, + "comments": { + "input": { + "KeyId": "The key ID or key ARN of the multi-Region primary key", + "ReplicaRegion": "The Region of the new replica." + }, + "output": { + "ReplicaKeyMetadata": "An object that displays detailed information about the replica key.", + "ReplicaPolicy": "The key policy of the replica key. If you don't specify a key policy, the replica key gets the default key policy for a KMS key.", + "ReplicaTags": "The tags on the replica key, if any." + } + }, + "description": "This example creates a multi-Region replica key in us-west-2 of a multi-Region primary key in us-east-1. ", + "id": "to-replicate-a-multi-region-key-in-a-different-aws-region-1628622402887", + "title": "To replicate a multi-Region key in a different AWS Region" + } + ], + "RetireGrant": [ + { + "input": { + "GrantId": "0c237476b39f8bc44e45212e08498fbe3151305030726c0590dd8d3e9f3d6a60", + "KeyId": "arn:aws:kms:us-east-2:444455556666:key/1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "GrantId": "The identifier of the grant to retire.", + "KeyId": "The Amazon Resource Name (ARN) of the KMS key associated with the grant." + } + }, + "description": "The following example retires a grant.", + "id": "to-retire-a-grant-1481327028297", + "title": "To retire a grant" + } + ], + "RevokeGrant": [ + { + "input": { + "GrantId": "0c237476b39f8bc44e45212e08498fbe3151305030726c0590dd8d3e9f3d6a60", + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "GrantId": "The identifier of the grant to revoke.", + "KeyId": "The identifier of the KMS key associated with the grant. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + } + }, + "description": "The following example revokes a grant.", + "id": "to-revoke-a-grant-1481329549302", + "title": "To revoke a grant" + } + ], + "ScheduleKeyDeletion": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "PendingWindowInDays": 7 + }, + "output": { + "DeletionDate": "2016-12-17T16:00:00-08:00", + "KeyId": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key to schedule for deletion. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key.", + "PendingWindowInDays": "The waiting period, specified in number of days. After the waiting period ends, KMS deletes the KMS key." + }, + "output": { + "DeletionDate": "The date and time after which KMS deletes the KMS key.", + "KeyId": "The ARN of the KMS key that is scheduled for deletion." + } + }, + "description": "The following example schedules the specified KMS key for deletion.", + "id": "to-schedule-a-cmk-for-deletion-1481331111094", + "title": "To schedule a KMS key for deletion" + } + ], + "Sign": [ + { + "input": { + "KeyId": "alias/ECC_signing_key", + "Message": "", + "MessageType": "RAW", + "SigningAlgorithm": "ECDSA_SHA_384" + }, + "output": { + "KeyId": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "Signature": "", + "SigningAlgorithm": "ECDSA_SHA_384" + }, + "comments": { + "input": { + "KeyId": "The asymmetric KMS key to be used to generate the digital signature. This example uses an alias of the KMS key.", + "Message": "Message to be signed. Use Base-64 for the CLI.", + "MessageType": "Indicates whether the message is RAW or a DIGEST.", + "SigningAlgorithm": "The requested signing algorithm. This must be an algorithm that the KMS key supports." + }, + "output": { + "KeyId": "The key ARN of the asymmetric KMS key that was used to sign the message.", + "Signature": "The digital signature of the message.", + "SigningAlgorithm": "The actual signing algorithm that was used to generate the signature." + } + }, + "description": "This operation uses the private key in an asymmetric elliptic curve (ECC) KMS key to generate a digital signature for a given message.", + "id": "to-digitally-sign-a-message-with-an-asymmetric-kms-key-1628631433832", + "title": "To digitally sign a message with an asymmetric KMS key." + } + ], + "TagResource": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "Tags": [ + { + "TagKey": "Purpose", + "TagValue": "Test" + } + ] + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key you are tagging. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key.", + "Tags": "A list of tags." + } + }, + "description": "The following example tags a KMS key.", + "id": "to-tag-a-cmk-1483997246518", + "title": "To tag a KMS key" + } + ], + "UntagResource": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "TagKeys": [ + "Purpose", + "CostCenter" + ] + }, + "comments": { + "input": { + "KeyId": "The identifier of the KMS key whose tags you are removing.", + "TagKeys": "A list of tag keys. Provide only the tag keys, not the tag values." + } + }, + "description": "The following example removes tags from a KMS key.", + "id": "to-remove-tags-from-a-cmk-1483997590962", + "title": "To remove tags from a KMS key" + } + ], + "UpdateAlias": [ + { + "input": { + "AliasName": "alias/ExampleAlias", + "TargetKeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "AliasName": "The alias to update.", + "TargetKeyId": "The identifier of the KMS key that the alias will refer to after this operation succeeds. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + } + }, + "description": "The following example updates the specified alias to refer to the specified KMS key.", + "id": "to-update-an-alias-1481572726920", + "title": "To update an alias" + } + ], + "UpdateCustomKeyStore": [ + { + "input": { + "CustomKeyStoreId": "cks-1234567890abcdef0", + "KeyStorePassword": "ExamplePassword" + }, + "output": { + }, + "comments": { + "input": { + "CustomKeyStoreId": "The ID of the custom key store that you are updating.", + "KeyStorePassword": "The password for the kmsuser crypto user in the CloudHSM cluster." + }, + "output": { + } + }, + "description": "This example tells KMS the password for the kmsuser crypto user in the AWS CloudHSM cluster that is associated with the AWS KMS custom key store. (It does not change the password in the CloudHSM cluster.) This operation does not return any data.", + "id": "to-edit-the-properties-of-a-custom-key-store-1628629851834", + "title": "To edit the password of a custom key store" + }, + { + "input": { + "CustomKeyStoreId": "cks-1234567890abcdef0", + "NewCustomKeyStoreName": "DevelopmentKeys" + }, + "output": { + }, + "comments": { + "input": { + "CustomKeyStoreId": "The ID of the custom key store that you are updating.", + "NewCustomKeyStoreName": "A new friendly name for the custom key store." + }, + "output": { + } + }, + "description": "This example changes the friendly name of the AWS KMS custom key store to the name that you specify. This operation does not return any data. To verify that the operation worked, use the DescribeCustomKeyStores operation.", + "id": "to-edit-the-friendly-name-of-a-custom-key-store-1630451340904", + "title": "To edit the friendly name of a custom key store" + }, + { + "input": { + "CloudHsmClusterId": "cluster-1a23b4cdefg", + "CustomKeyStoreId": "cks-1234567890abcdef0" + }, + "output": { + }, + "comments": { + "input": { + "CloudHsmClusterId": "The ID of the AWS CloudHSM cluster that you want to associate with the custom key store. This cluster must be related to the original CloudHSM cluster for this key store.", + "CustomKeyStoreId": "The ID of the custom key store that you are updating." + }, + "output": { + } + }, + "description": "This example changes the cluster that is associated with a custom key store to a related cluster, such as a different backup of the same cluster. This operation does not return any data. To verify that the operation worked, use the DescribeCustomKeyStores operation.", + "id": "to-associate-the-custom-key-store-with-a-different-but-related-aws-cloudhsm-cluster-1630451842438", + "title": "To associate the custom key store with a different, but related, AWS CloudHSM cluster." + } + ], + "UpdateKeyDescription": [ + { + "input": { + "Description": "Example description that indicates the intended use of this KMS key.", + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "comments": { + "input": { + "Description": "The updated description.", + "KeyId": "The identifier of the KMS key whose description you are updating. You can use the key ID or the Amazon Resource Name (ARN) of the KMS key." + } + }, + "description": "The following example updates the description of the specified KMS key.", + "id": "to-update-the-description-of-a-cmk-1481574808619", + "title": "To update the description of a KMS key" + } + ], + "Verify": [ + { + "input": { + "KeyId": "alias/ECC_signing_key", + "Message": "", + "MessageType": "RAW", + "Signature": "", + "SigningAlgorithm": "ECDSA_SHA_384" + }, + "output": { + "KeyId": "arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "SignatureValid": true, + "SigningAlgorithm": "ECDSA_SHA_384" + }, + "comments": { + "input": { + "KeyId": "The asymmetric KMS key to be used to verify the digital signature. This example uses an alias to identify the KMS key.", + "Message": "The message that was signed.", + "MessageType": "Indicates whether the message is RAW or a DIGEST.", + "Signature": "The signature to be verified.", + "SigningAlgorithm": "The signing algorithm to be used to verify the signature." + }, + "output": { + "KeyId": "The key ARN of the asymmetric KMS key that was used to verify the digital signature.", + "SignatureValid": "A value of 'true' Indicates that the signature was verified. If verification fails, the call to Verify fails.", + "SigningAlgorithm": "The signing algorithm that was used to verify the signature." + } + }, + "description": "This operation uses the public key in an elliptic curve (ECC) asymmetric key to verify a digital signature within AWS KMS. ", + "id": "to-use-an-asymmetric-kms-key-to-verify-a-digital-signature-1628633365663", + "title": "To use an asymmetric KMS key to verify a digital signature" + } + ], + "VerifyMac": [ + { + "input": { + "KeyId": "1234abcd-12ab-34cd-56ef-1234567890ab", + "Mac": "", + "MacAlgorithm": "HMAC_SHA_384", + "Message": "Hello World" + }, + "output": { + "KeyId": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + "MacAlgorithm": "HMAC_SHA_384", + "MacValid": true + }, + "comments": { + "input": { + "KeyId": "The HMAC KMS key input to the HMAC algorithm.", + "Mac": "The HMAC to be verified.", + "MacAlgorithm": "The HMAC algorithm requested for the operation.", + "Message": "The message input to the HMAC algorithm." + }, + "output": { + "KeyId": "The key ARN of the HMAC key used in the operation.", + "MacAlgorithm": "The HMAC algorithm used in the operation.", + "MacValid": "A value of 'true' indicates that verification succeeded. If verification fails, the call to VerifyMac fails." + } + }, + "description": "This example verifies an HMAC for a particular message, HMAC KMS keys, and MAC algorithm. A value of 'true' in the MacValid value in the response indicates that the HMAC is valid.", + "id": "to-verify-an-hmac-1631570863401", + "title": "To verify an HMAC" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kms/2014-11-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/kms/2014-11-01/paginators-1.json new file mode 100644 index 0000000..0c48130 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/kms/2014-11-01/paginators-1.json @@ -0,0 +1,60 @@ +{ + "pagination": { + "ListAliases": { + "limit_key": "Limit", + "input_token": "Marker", + "output_token": "NextMarker", + "more_results": "Truncated", + "result_key": "Aliases" + }, + "ListGrants": { + "limit_key": "Limit", + "input_token": "Marker", + "output_token": "NextMarker", + "more_results": "Truncated", + "result_key": "Grants" + }, + "ListKeyPolicies": { + "limit_key": "Limit", + "input_token": "Marker", + "output_token": "NextMarker", + "more_results": "Truncated", + "result_key": "PolicyNames" + }, + "ListKeys": { + "limit_key": "Limit", + "input_token": "Marker", + "output_token": "NextMarker", + "more_results": "Truncated", + "result_key": "Keys" + }, + "DescribeCustomKeyStores": { + "input_token": "Marker", + "limit_key": "Limit", + "more_results": "Truncated", + "output_token": "NextMarker", + "result_key": "CustomKeyStores" + }, + "ListResourceTags": { + "input_token": "Marker", + "limit_key": "Limit", + "more_results": "Truncated", + "output_token": "NextMarker", + "result_key": "Tags" + }, + "ListRetirableGrants": { + "input_token": "Marker", + "limit_key": "Limit", + "more_results": "Truncated", + "output_token": "NextMarker", + "result_key": "Grants" + }, + "ListKeyRotations": { + "input_token": "Marker", + "limit_key": "Limit", + "more_results": "Truncated", + "output_token": "NextMarker", + "result_key": "Rotations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/kms/2014-11-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/kms/2014-11-01/service-2.json.gz new file mode 100644 index 0000000..ee6094e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/kms/2014-11-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cb9b23f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/paginators-1.json new file mode 100644 index 0000000..5970057 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "GetWorkUnits": { + "input_token": "NextToken", + "limit_key": "PageSize", + "output_token": "NextToken", + "result_key": "WorkUnitRanges" + }, + "ListDataCellsFilter": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DataCellsFilters" + }, + "ListLFTags": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LFTags" + }, + "SearchDatabasesByLFTags": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DatabaseList" + }, + "SearchTablesByLFTags": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TableList" + }, + "ListLFTagExpressions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LFTagExpressions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/paginators-1.sdk-extras.json new file mode 100644 index 0000000..aea980d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetWorkUnits": { + "non_aggregate_keys": [ + "QueryId" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/service-2.json.gz new file mode 100644 index 0000000..3434fd5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lakeformation/2017-03-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda-core/2026-04-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lambda-core/2026-04-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..2e7abcf Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lambda-core/2026-04-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda-core/2026-04-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/lambda-core/2026-04-30/paginators-1.json new file mode 100644 index 0000000..fb0221e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lambda-core/2026-04-30/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListNetworkConnectors": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "NetworkConnectors" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda-core/2026-04-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lambda-core/2026-04-30/service-2.json.gz new file mode 100644 index 0000000..258cae4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lambda-core/2026-04-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda-microvms/2025-09-09/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lambda-microvms/2025-09-09/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..2e7abcf Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lambda-microvms/2025-09-09/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda-microvms/2025-09-09/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/lambda-microvms/2025-09-09/paginators-1.json new file mode 100644 index 0000000..14bc086 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lambda-microvms/2025-09-09/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListManagedMicrovmImageVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListManagedMicrovmImages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListMicrovmImageBuilds": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListMicrovmImageVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListMicrovmImages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListMicrovms": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda-microvms/2025-09-09/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lambda-microvms/2025-09-09/service-2.json.gz new file mode 100644 index 0000000..b0b7d0f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lambda-microvms/2025-09-09/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda/2014-11-11/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lambda/2014-11-11/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7eee08c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lambda/2014-11-11/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda/2014-11-11/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lambda/2014-11-11/service-2.json.gz new file mode 100644 index 0000000..b608238 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lambda/2014-11-11/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..85550d4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/examples-1.json new file mode 100644 index 0000000..c33c1bb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/examples-1.json @@ -0,0 +1,1513 @@ +{ + "version": "1.0", + "examples": { + "AddLayerVersionPermission": [ + { + "input": { + "Action": "lambda:GetLayerVersion", + "LayerName": "my-layer", + "Principal": "223456789012", + "StatementId": "xaccount", + "VersionNumber": 1 + }, + "output": { + "RevisionId": "35d87451-f796-4a3f-a618-95a3671b0a0c", + "Statement": "{\"Sid\":\"xaccount\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::223456789012:root\"},\"Action\":\"lambda:GetLayerVersion\",\"Resource\":\"arn:aws:lambda:us-east-2:123456789012:layer:my-layer:1\"}" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example grants permission for the account 223456789012 to use version 1 of a layer named my-layer.", + "id": "to-add-permissions-to-a-layer-version-1586479797163", + "title": "To add permissions to a layer version" + } + ], + "AddPermission": [ + { + "input": { + "Action": "lambda:InvokeFunction", + "FunctionName": "my-function", + "Principal": "s3.amazonaws.com", + "SourceAccount": "123456789012", + "SourceArn": "arn:aws:s3:::my-bucket-1xpuxmplzrlbh/*", + "StatementId": "s3" + }, + "output": { + "Statement": "{\"Sid\":\"s3\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"s3.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-east-2:123456789012:function:my-function\",\"Condition\":{\"StringEquals\":{\"AWS:SourceAccount\":\"123456789012\"},\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:s3:::my-bucket-1xpuxmplzrlbh\"}}}" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example adds permission for Amazon S3 to invoke a Lambda function named my-function for notifications from a bucket named my-bucket-1xpuxmplzrlbh in account 123456789012.", + "id": "add-permission-1474651469455", + "title": "To grant Amazon S3 permission to invoke a function" + }, + { + "input": { + "Action": "lambda:InvokeFunction", + "FunctionName": "my-function", + "Principal": "223456789012", + "StatementId": "xaccount" + }, + "output": { + "Statement": "{\"Sid\":\"xaccount\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::223456789012:root\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-east-2:123456789012:function:my-function\"}" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example adds permission for account 223456789012 invoke a Lambda function named my-function.", + "id": "add-permission-1474651469456", + "title": "To grant another account permission to invoke a function" + } + ], + "CreateAlias": [ + { + "input": { + "Description": "alias for live version of function", + "FunctionName": "my-function", + "FunctionVersion": "1", + "Name": "LIVE" + }, + "output": { + "AliasArn": "arn:aws:lambda:us-east-2:123456789012:function:my-function:LIVE", + "Description": "alias for live version of function", + "FunctionVersion": "1", + "Name": "LIVE", + "RevisionId": "873282ed-xmpl-4dc8-a069-d0c647e470c6" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates an alias named LIVE that points to version 1 of the my-function Lambda function.", + "id": "to-create-an-alias-for-a-lambda-function-1586480324259", + "title": "To create an alias for a Lambda function" + } + ], + "CreateEventSourceMapping": [ + { + "input": { + "BatchSize": 5, + "EventSourceArn": "arn:aws:sqs:us-west-2:123456789012:my-queue", + "FunctionName": "my-function" + }, + "output": { + "BatchSize": 5, + "EventSourceArn": "arn:aws:sqs:us-west-2:123456789012:my-queue", + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function", + "LastModified": 1569284520.333, + "State": "Creating", + "StateTransitionReason": "USER_INITIATED", + "UUID": "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a mapping between an SQS queue and the my-function Lambda function.", + "id": "to-create-a-mapping-between-an-event-source-and-an-aws-lambda-function-1586480555467", + "title": "To create a mapping between an event source and an AWS Lambda function" + } + ], + "CreateFunction": [ + { + "input": { + "Code": { + "S3Bucket": "my-bucket-1xpuxmplzrlbh", + "S3Key": "function.zip" + }, + "Description": "Process image objects from Amazon S3.", + "Environment": { + "Variables": { + "BUCKET": "my-bucket-1xpuxmplzrlbh", + "PREFIX": "inbound" + } + }, + "FunctionName": "my-function", + "Handler": "index.handler", + "KMSKeyArn": "arn:aws:kms:us-west-2:123456789012:key/b0844d6c-xmpl-4463-97a4-d49f50839966", + "MemorySize": 256, + "Publish": true, + "Role": "arn:aws:iam::123456789012:role/lambda-role", + "Runtime": "nodejs12.x", + "Tags": { + "DEPARTMENT": "Assets" + }, + "Timeout": 15, + "TracingConfig": { + "Mode": "Active" + } + }, + "output": { + "CodeSha256": "YFgDgEKG3ugvF1+pX64gV6tu9qNuIYNUdgJm8nCxsm4=", + "CodeSize": 5797206, + "Description": "Process image objects from Amazon S3.", + "Environment": { + "Variables": { + "BUCKET": "my-bucket-1xpuxmplzrlbh", + "PREFIX": "inbound" + } + }, + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function", + "FunctionName": "my-function", + "Handler": "index.handler", + "KMSKeyArn": "arn:aws:kms:us-west-2:123456789012:key/b0844d6c-xmpl-4463-97a4-d49f50839966", + "LastModified": "2020-04-10T19:06:32.563+0000", + "LastUpdateStatus": "Successful", + "MemorySize": 256, + "RevisionId": "b75dcd81-xmpl-48a8-a75a-93ba8b5b9727", + "Role": "arn:aws:iam::123456789012:role/lambda-role", + "Runtime": "nodejs12.x", + "State": "Active", + "Timeout": 15, + "TracingConfig": { + "Mode": "Active" + }, + "Version": "1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a function with a deployment package in Amazon S3 and enables X-Ray tracing and environment variable encryption.", + "id": "to-create-a-function-1586492061186", + "title": "To create a function" + } + ], + "DeleteAlias": [ + { + "input": { + "FunctionName": "my-function", + "Name": "BLUE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an alias named BLUE from a function named my-function", + "id": "to-delete-a-lambda-function-alias-1481660370804", + "title": "To delete a Lambda function alias" + } + ], + "DeleteEventSourceMapping": [ + { + "input": { + "UUID": "14e0db71-xmpl-4eb5-b481-8945cf9d10c2" + }, + "output": { + "BatchSize": 5, + "EventSourceArn": "arn:aws:sqs:us-west-2:123456789012:my-queue", + "FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:my-function", + "LastModified": "2016-11-21T19:49:20.006+0000", + "State": "Enabled", + "StateTransitionReason": "USER_INITIATED", + "UUID": "14e0db71-xmpl-4eb5-b481-8945cf9d10c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an event source mapping. To get a mapping's UUID, use ListEventSourceMappings.", + "id": "to-delete-a-lambda-function-event-source-mapping-1481658973862", + "title": "To delete a Lambda function event source mapping" + } + ], + "DeleteFunction": [ + { + "input": { + "FunctionName": "my-function", + "Qualifier": "1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes version 1 of a Lambda function named my-function.", + "id": "to-delete-a-lambda-function-1481648553696", + "title": "To delete a version of a Lambda function" + } + ], + "DeleteFunctionConcurrency": [ + { + "input": { + "FunctionName": "my-function" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes the reserved concurrent execution limit from a function named my-function.", + "id": "to-remove-the-reserved-concurrent-execution-limit-from-a-function-1586480714680", + "title": "To remove the reserved concurrent execution limit from a function" + } + ], + "DeleteFunctionEventInvokeConfig": [ + { + "input": { + "FunctionName": "my-function", + "Qualifier": "GREEN" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes the asynchronous invocation configuration for the GREEN alias of a function named my-function.", + "id": "to-delete-an-asynchronous-invocation-configuration-1586481102187", + "title": "To delete an asynchronous invocation configuration" + } + ], + "DeleteLayerVersion": [ + { + "input": { + "LayerName": "my-layer", + "VersionNumber": 2 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes version 2 of a layer named my-layer.", + "id": "to-delete-a-version-of-a-lambda-layer-1586481157547", + "title": "To delete a version of a Lambda layer" + } + ], + "DeleteProvisionedConcurrencyConfig": [ + { + "input": { + "FunctionName": "my-function", + "Qualifier": "GREEN" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes the provisioned concurrency configuration for the GREEN alias of a function named my-function.", + "id": "to-delete-a-provisioned-concurrency-configuration-1586481032551", + "title": "To delete a provisioned concurrency configuration" + } + ], + "GetAccountSettings": [ + { + "input": { + }, + "output": { + "AccountLimit": { + "CodeSizeUnzipped": 262144000, + "CodeSizeZipped": 52428800, + "ConcurrentExecutions": 1000, + "TotalCodeSize": 80530636800, + "UnreservedConcurrentExecutions": 1000 + }, + "AccountUsage": { + "FunctionCount": 4, + "TotalCodeSize": 9426 + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation takes no parameters and returns details about storage and concurrency quotas in the current Region.", + "id": "to-get-account-settings-1481657495274", + "title": "To get account settings" + } + ], + "GetAlias": [ + { + "input": { + "FunctionName": "my-function", + "Name": "BLUE" + }, + "output": { + "AliasArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function:BLUE", + "Description": "Production environment BLUE.", + "FunctionVersion": "3", + "Name": "BLUE", + "RevisionId": "594f41fb-xmpl-4c20-95c7-6ca5f2a92c93" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns details about an alias named BLUE for a function named my-function", + "id": "to-retrieve-a-lambda-function-alias-1481648742254", + "title": "To get a Lambda function alias" + } + ], + "GetEventSourceMapping": [ + { + "input": { + "UUID": "14e0db71-xmpl-4eb5-b481-8945cf9d10c2" + }, + "output": { + "BatchSize": 500, + "BisectBatchOnFunctionError": false, + "DestinationConfig": { + }, + "EventSourceArn": "arn:aws:sqs:us-east-2:123456789012:mySQSqueue", + "FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:myFunction", + "LastModified": "2016-11-21T19:49:20.006+0000", + "LastProcessingResult": "No records processed", + "MaximumRecordAgeInSeconds": 604800, + "MaximumRetryAttempts": 10000, + "State": "Creating", + "StateTransitionReason": "User action", + "UUID": "14e0db71-xmpl-4eb5-b481-8945cf9d10c2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns details about an event source mapping. To get a mapping's UUID, use ListEventSourceMappings.", + "id": "to-get-a-lambda-functions-event-source-mapping-1481661622799", + "title": "To get a Lambda function's event source mapping" + } + ], + "GetFunction": [ + { + "input": { + "FunctionName": "my-function", + "Qualifier": "1" + }, + "output": { + "Code": { + "Location": "https://awslambda-us-west-2-tasks.s3.us-west-2.amazonaws.com/snapshots/123456789012/my-function-e7d9d1ed-xmpl-4f79-904a-4b87f2681f30?versionId=sH3TQwBOaUy...", + "RepositoryType": "S3" + }, + "Configuration": { + "CodeSha256": "YFgDgEKG3ugvF1+pX64gV6tu9qNuIYNUdgJm8nCxsm4=", + "CodeSize": 5797206, + "Description": "Process image objects from Amazon S3.", + "Environment": { + "Variables": { + "BUCKET": "my-bucket-1xpuxmplzrlbh", + "PREFIX": "inbound" + } + }, + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function", + "FunctionName": "my-function", + "Handler": "index.handler", + "KMSKeyArn": "arn:aws:kms:us-west-2:123456789012:key/b0844d6c-xmpl-4463-97a4-d49f50839966", + "LastModified": "2020-04-10T19:06:32.563+0000", + "LastUpdateStatus": "Successful", + "MemorySize": 256, + "RevisionId": "b75dcd81-xmpl-48a8-a75a-93ba8b5b9727", + "Role": "arn:aws:iam::123456789012:role/lambda-role", + "Runtime": "nodejs12.x", + "State": "Active", + "Timeout": 15, + "TracingConfig": { + "Mode": "Active" + }, + "Version": "$LATEST" + }, + "Tags": { + "DEPARTMENT": "Assets" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns code and configuration details for version 1 of a function named my-function.", + "id": "to-get-a-lambda-function-1481661622799", + "title": "To get a Lambda function" + } + ], + "GetFunctionConcurrency": [ + { + "input": { + "FunctionName": "my-function" + }, + "output": { + "ReservedConcurrentExecutions": 250 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the reserved concurrency setting for a function named my-function.", + "id": "to-get-the-reserved-concurrency-setting-for-a-function-1586481279992", + "title": "To get the reserved concurrency setting for a function" + } + ], + "GetFunctionConfiguration": [ + { + "input": { + "FunctionName": "my-function", + "Qualifier": "1" + }, + "output": { + "CodeSha256": "YFgDgEKG3ugvF1+pX64gV6tu9qNuIYNUdgJm8nCxsm4=", + "CodeSize": 5797206, + "Description": "Process image objects from Amazon S3.", + "Environment": { + "Variables": { + "BUCKET": "my-bucket-1xpuxmplzrlbh", + "PREFIX": "inbound" + } + }, + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function", + "FunctionName": "my-function", + "Handler": "index.handler", + "KMSKeyArn": "arn:aws:kms:us-west-2:123456789012:key/b0844d6c-xmpl-4463-97a4-d49f50839966", + "LastModified": "2020-04-10T19:06:32.563+0000", + "LastUpdateStatus": "Successful", + "MemorySize": 256, + "RevisionId": "b75dcd81-xmpl-48a8-a75a-93ba8b5b9727", + "Role": "arn:aws:iam::123456789012:role/lambda-role", + "Runtime": "nodejs12.x", + "State": "Active", + "Timeout": 15, + "TracingConfig": { + "Mode": "Active" + }, + "Version": "$LATEST" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns and configuration details for version 1 of a function named my-function.", + "id": "to-get-a-lambda-functions-event-source-mapping-1481661622799", + "title": "To get a Lambda function's event source mapping" + } + ], + "GetFunctionEventInvokeConfig": [ + { + "input": { + "FunctionName": "my-function", + "Qualifier": "BLUE" + }, + "output": { + "DestinationConfig": { + "OnFailure": { + "Destination": "arn:aws:sqs:us-east-2:123456789012:failed-invocations" + }, + "OnSuccess": { + } + }, + "FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:my-function:BLUE", + "LastModified": "2016-11-21T19:49:20.006+0000", + "MaximumEventAgeInSeconds": 3600, + "MaximumRetryAttempts": 0 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the asynchronous invocation configuration for the BLUE alias of a function named my-function.", + "id": "to-get-an-asynchronous-invocation-configuration-1586481338463", + "title": "To get an asynchronous invocation configuration" + } + ], + "GetLayerVersion": [ + { + "input": { + "LayerName": "my-layer", + "VersionNumber": 1 + }, + "output": { + "CompatibleRuntimes": [ + "python3.6", + "python3.7" + ], + "Content": { + "CodeSha256": "tv9jJO+rPbXUUXuRKi7CwHzKtLDkDRJLB3cC3Z/ouXo=", + "CodeSize": 169, + "Location": "https://awslambda-us-east-2-layers.s3.us-east-2.amazonaws.com/snapshots/123456789012/my-layer-4aaa2fbb-ff77-4b0a-ad92-5b78a716a96a?versionId=27iWyA73cCAYqyH..." + }, + "CreatedDate": "2018-11-14T23:03:52.894+0000", + "Description": "My Python layer", + "LayerArn": "arn:aws:lambda:us-east-2:123456789012:layer:my-layer", + "LayerVersionArn": "arn:aws:lambda:us-east-2:123456789012:layer:my-layer:1", + "LicenseInfo": "MIT", + "Version": 1 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns information for version 1 of a layer named my-layer.", + "id": "to-get-information-about-a-lambda-layer-version-1586481457839", + "title": "To get information about a Lambda layer version" + } + ], + "GetLayerVersionByArn": [ + { + "input": { + "Arn": "arn:aws:lambda:ca-central-1:123456789012:layer:blank-python-lib:3" + }, + "output": { + "CompatibleRuntimes": [ + "python3.8" + ], + "Content": { + "CodeSha256": "6x+xmpl/M3BnQUk7gS9sGmfeFsR/npojXoA3fZUv4eU=", + "CodeSize": 9529009, + "Location": "https://awslambda-us-east-2-layers.s3.us-east-2.amazonaws.com/snapshots/123456789012/blank-python-lib-e5212378-xmpl-44ee-8398-9d8ec5113949?versionId=WbZnvf..." + }, + "CreatedDate": "2020-03-31T00:35:18.949+0000", + "Description": "Dependencies for the blank-python sample app.", + "LayerArn": "arn:aws:lambda:us-east-2:123456789012:layer:blank-python-lib", + "LayerVersionArn": "arn:aws:lambda:us-east-2:123456789012:layer:blank-python-lib:3", + "Version": 3 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns information about the layer version with the specified Amazon Resource Name (ARN).", + "id": "to-get-information-about-a-lambda-layer-version-1586481457839", + "title": "To get information about a Lambda layer version" + } + ], + "GetPolicy": [ + { + "input": { + "FunctionName": "my-function", + "Qualifier": "1" + }, + "output": { + "Policy": "{\"Version\":\"2012-10-17\",\"Id\":\"default\",\"Statement\":[{\"Sid\":\"xaccount\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-east-2:123456789012:function:my-function:1\"}]}", + "RevisionId": "4843f2f6-7c59-4fda-b484-afd0bc0e22b8" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the resource-based policy for version 1 of a Lambda function named my-function.", + "id": "to-retrieve-a-lambda-function-policy-1481649319053", + "title": "To retrieve a Lambda function policy" + } + ], + "GetProvisionedConcurrencyConfig": [ + { + "input": { + "FunctionName": "my-function", + "Qualifier": "BLUE" + }, + "output": { + "AllocatedProvisionedConcurrentExecutions": 100, + "AvailableProvisionedConcurrentExecutions": 100, + "LastModified": "2019-12-31T20:28:49+0000", + "RequestedProvisionedConcurrentExecutions": 100, + "Status": "READY" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns details for the provisioned concurrency configuration for the BLUE alias of the specified function.", + "id": "to-get-a-provisioned-concurrency-configuration-1586490192690", + "title": "To get a provisioned concurrency configuration" + }, + { + "input": { + "FunctionName": "my-function", + "Qualifier": "BLUE" + }, + "output": { + "AllocatedProvisionedConcurrentExecutions": 100, + "AvailableProvisionedConcurrentExecutions": 100, + "LastModified": "2019-12-31T20:28:49+0000", + "RequestedProvisionedConcurrentExecutions": 100, + "Status": "READY" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example displays details for the provisioned concurrency configuration for the BLUE alias of the specified function.", + "id": "to-view-a-provisioned-concurrency-configuration-1586490192690", + "title": "To view a provisioned concurrency configuration" + } + ], + "Invoke": [ + { + "input": { + "FunctionName": "my-function", + "Payload": "{}", + "Qualifier": "1" + }, + "output": { + "Payload": "200 SUCCESS", + "StatusCode": 200 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example invokes version 1 of a function named my-function with an empty event payload.", + "id": "to-invoke-a-lambda-function-1481659683915", + "title": "To invoke a Lambda function" + }, + { + "input": { + "FunctionName": "my-function", + "InvocationType": "Event", + "Payload": "{}", + "Qualifier": "1" + }, + "output": { + "Payload": "", + "StatusCode": 202 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example invokes version 1 of a function named my-function asynchronously.", + "id": "to-invoke-a-lambda-function-async-1481659683915", + "title": "To invoke a Lambda function asynchronously" + } + ], + "InvokeAsync": [ + { + "input": { + "FunctionName": "my-function", + "InvokeArgs": "{}" + }, + "output": { + "Status": 202 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example invokes a Lambda function asynchronously", + "id": "to-invoke-a-lambda-function-asynchronously-1481649694923", + "title": "To invoke a Lambda function asynchronously" + } + ], + "ListAliases": [ + { + "input": { + "FunctionName": "my-function" + }, + "output": { + "Aliases": [ + { + "AliasArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function:BETA", + "Description": "Production environment BLUE.", + "FunctionVersion": "2", + "Name": "BLUE", + "RevisionId": "a410117f-xmpl-494e-8035-7e204bb7933b", + "RoutingConfig": { + "AdditionalVersionWeights": { + "1": 0.7 + } + } + }, + { + "AliasArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function:LIVE", + "Description": "Production environment GREEN.", + "FunctionVersion": "1", + "Name": "GREEN", + "RevisionId": "21d40116-xmpl-40ba-9360-3ea284da1bb5" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns a list of aliases for a function named my-function.", + "id": "to-list-a-functions-aliases-1481650199732", + "title": "To list a function's aliases" + } + ], + "ListEventSourceMappings": [ + { + "input": { + "FunctionName": "my-function" + }, + "output": { + "EventSourceMappings": [ + { + "BatchSize": 5, + "EventSourceArn": "arn:aws:sqs:us-west-2:123456789012:mySQSqueue", + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function", + "LastModified": 1569284520.333, + "State": "Enabled", + "StateTransitionReason": "USER_INITIATED", + "UUID": "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns a list of the event source mappings for a function named my-function.", + "id": "to-list-the-event-source-mappings-for-a-function-1586490285906", + "title": "To list the event source mappings for a function" + } + ], + "ListFunctionEventInvokeConfigs": [ + { + "input": { + "FunctionName": "my-function" + }, + "output": { + "FunctionEventInvokeConfigs": [ + { + "FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:my-function:GREEN", + "LastModified": 1577824406.719, + "MaximumEventAgeInSeconds": 1800, + "MaximumRetryAttempts": 2 + }, + { + "FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:my-function:BLUE", + "LastModified": 1577824396.653, + "MaximumEventAgeInSeconds": 3600, + "MaximumRetryAttempts": 0 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns a list of asynchronous invocation configurations for a function named my-function.", + "id": "to-view-a-list-of-asynchronous-invocation-configurations-1586490355611", + "title": "To view a list of asynchronous invocation configurations" + } + ], + "ListFunctions": [ + { + "input": { + }, + "output": { + "Functions": [ + { + "CodeSha256": "dBG9m8SGdmlEjw/JYXlhhvCrAv5TxvXsbL/RMr0fT/I=", + "CodeSize": 294, + "Description": "", + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:helloworld", + "FunctionName": "helloworld", + "Handler": "helloworld.handler", + "LastModified": "2019-09-23T18:32:33.857+0000", + "MemorySize": 128, + "RevisionId": "1718e831-badf-4253-9518-d0644210af7b", + "Role": "arn:aws:iam::123456789012:role/service-role/MyTestFunction-role-zgur6bf4", + "Runtime": "nodejs10.x", + "Timeout": 3, + "TracingConfig": { + "Mode": "PassThrough" + }, + "Version": "$LATEST" + }, + { + "CodeSha256": "sU0cJ2/hOZevwV/lTxCuQqK3gDZP3i8gUoqUUVRmY6E=", + "CodeSize": 266, + "Description": "", + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function", + "FunctionName": "my-function", + "Handler": "index.handler", + "LastModified": "2019-10-01T16:47:28.490+0000", + "MemorySize": 256, + "RevisionId": "93017fc9-59cb-41dc-901b-4845ce4bf668", + "Role": "arn:aws:iam::123456789012:role/service-role/helloWorldPython-role-uy3l9qyq", + "Runtime": "nodejs10.x", + "Timeout": 3, + "TracingConfig": { + "Mode": "PassThrough" + }, + "Version": "$LATEST", + "VpcConfig": { + "SecurityGroupIds": [ + + ], + "SubnetIds": [ + + ], + "VpcId": "" + } + } + ], + "NextMarker": "" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation returns a list of Lambda functions.", + "id": "to-get-a-list-of-lambda-functions-1481650507425", + "title": "To get a list of Lambda functions" + } + ], + "ListLayerVersions": [ + { + "input": { + "LayerName": "blank-java-lib" + }, + "output": { + "LayerVersions": [ + { + "CompatibleRuntimes": [ + "java8" + ], + "CreatedDate": "2020-03-18T23:38:42.284+0000", + "Description": "Dependencies for the blank-java sample app.", + "LayerVersionArn": "arn:aws:lambda:us-east-2:123456789012:layer:blank-java-lib:7", + "Version": 7 + }, + { + "CompatibleRuntimes": [ + "java8" + ], + "CreatedDate": "2020-03-17T07:24:21.960+0000", + "Description": "Dependencies for the blank-java sample app.", + "LayerVersionArn": "arn:aws:lambda:us-east-2:123456789012:layer:blank-java-lib:6", + "Version": 6 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example displays information about the versions for the layer named blank-java-lib", + "id": "to-list-versions-of-a-layer-1586490857297", + "title": "To list versions of a layer" + } + ], + "ListLayers": [ + { + "input": { + "CompatibleRuntime": "python3.7" + }, + "output": { + "Layers": [ + { + "LatestMatchingVersion": { + "CompatibleRuntimes": [ + "python3.6", + "python3.7" + ], + "CreatedDate": "2018-11-15T00:37:46.592+0000", + "Description": "My layer", + "LayerVersionArn": "arn:aws:lambda:us-east-2:123456789012:layer:my-layer:2", + "Version": 2 + }, + "LayerArn": "arn:aws:lambda:us-east-2:123456789012:layer:my-layer", + "LayerName": "my-layer" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns information about layers that are compatible with the Python 3.7 runtime.", + "id": "to-list-the-layers-that-are-compatible-with-your-functions-runtime-1586490857297", + "title": "To list the layers that are compatible with your function's runtime" + } + ], + "ListProvisionedConcurrencyConfigs": [ + { + "input": { + "FunctionName": "my-function" + }, + "output": { + "ProvisionedConcurrencyConfigs": [ + { + "AllocatedProvisionedConcurrentExecutions": 100, + "AvailableProvisionedConcurrentExecutions": 100, + "FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:my-function:GREEN", + "LastModified": "2019-12-31T20:29:00+0000", + "RequestedProvisionedConcurrentExecutions": 100, + "Status": "READY" + }, + { + "AllocatedProvisionedConcurrentExecutions": 100, + "AvailableProvisionedConcurrentExecutions": 100, + "FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:my-function:BLUE", + "LastModified": "2019-12-31T20:28:49+0000", + "RequestedProvisionedConcurrentExecutions": 100, + "Status": "READY" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns a list of provisioned concurrency configurations for a function named my-function.", + "id": "to-get-a-list-of-provisioned-concurrency-configurations-1586491032592", + "title": "To get a list of provisioned concurrency configurations" + } + ], + "ListTags": [ + { + "input": { + "Resource": "arn:aws:lambda:us-west-2:123456789012:function:my-function" + }, + "output": { + "Tags": { + "Category": "Web Tools", + "Department": "Sales" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example displays the tags attached to the my-function Lambda function.", + "id": "to-retrieve-the-list-of-tags-for-a-lambda-function-1586491111498", + "title": "To retrieve the list of tags for a Lambda function" + } + ], + "ListVersionsByFunction": [ + { + "input": { + "FunctionName": "my-function" + }, + "output": { + "Versions": [ + { + "CodeSha256": "YFgDgEKG3ugvF1+pX64gV6tu9qNuIYNUdgJm8nCxsm4=", + "CodeSize": 5797206, + "Description": "Process image objects from Amazon S3.", + "Environment": { + "Variables": { + "BUCKET": "my-bucket-1xpuxmplzrlbh", + "PREFIX": "inbound" + } + }, + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function", + "FunctionName": "my-function", + "Handler": "index.handler", + "KMSKeyArn": "arn:aws:kms:us-west-2:123456789012:key/b0844d6c-xmpl-4463-97a4-d49f50839966", + "LastModified": "2020-04-10T19:06:32.563+0000", + "MemorySize": 256, + "RevisionId": "850ca006-2d98-4ff4-86db-8766e9d32fe9", + "Role": "arn:aws:iam::123456789012:role/lambda-role", + "Runtime": "nodejs12.x", + "Timeout": 15, + "TracingConfig": { + "Mode": "Active" + }, + "Version": "$LATEST" + }, + { + "CodeSha256": "YFgDgEKG3ugvF1+pX64gV6tu9qNuIYNUdgJm8nCxsm4=", + "CodeSize": 5797206, + "Description": "Process image objects from Amazon S3.", + "Environment": { + "Variables": { + "BUCKET": "my-bucket-1xpuxmplzrlbh", + "PREFIX": "inbound" + } + }, + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function", + "FunctionName": "my-function", + "Handler": "index.handler", + "KMSKeyArn": "arn:aws:kms:us-west-2:123456789012:key/b0844d6c-xmpl-4463-97a4-d49f50839966", + "LastModified": "2020-04-10T19:06:32.563+0000", + "MemorySize": 256, + "RevisionId": "b75dcd81-xmpl-48a8-a75a-93ba8b5b9727", + "Role": "arn:aws:iam::123456789012:role/lambda-role", + "Runtime": "nodejs12.x", + "Timeout": 5, + "TracingConfig": { + "Mode": "Active" + }, + "Version": "1" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns a list of versions of a function named my-function", + "id": "to-list-versions-1481650603750", + "title": "To list versions of a function" + } + ], + "PublishLayerVersion": [ + { + "input": { + "CompatibleRuntimes": [ + "python3.6", + "python3.7" + ], + "Content": { + "S3Bucket": "lambda-layers-us-west-2-123456789012", + "S3Key": "layer.zip" + }, + "Description": "My Python layer", + "LayerName": "my-layer", + "LicenseInfo": "MIT" + }, + "output": { + "CompatibleRuntimes": [ + "python3.6", + "python3.7" + ], + "Content": { + "CodeSha256": "tv9jJO+rPbXUUXuRKi7CwHzKtLDkDRJLB3cC3Z/ouXo=", + "CodeSize": 169, + "Location": "https://awslambda-us-west-2-layers.s3.us-west-2.amazonaws.com/snapshots/123456789012/my-layer-4aaa2fbb-ff77-4b0a-ad92-5b78a716a96a?versionId=27iWyA73cCAYqyH..." + }, + "CreatedDate": "2018-11-14T23:03:52.894+0000", + "Description": "My Python layer", + "LayerArn": "arn:aws:lambda:us-west-2:123456789012:layer:my-layer", + "LayerVersionArn": "arn:aws:lambda:us-west-2:123456789012:layer:my-layer:1", + "LicenseInfo": "MIT", + "Version": 1 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a new Python library layer version. The command retrieves the layer content a file named layer.zip in the specified S3 bucket.", + "id": "to-create-a-lambda-layer-version-1586491213595", + "title": "To create a Lambda layer version" + } + ], + "PublishVersion": [ + { + "input": { + "CodeSha256": "", + "Description": "", + "FunctionName": "myFunction" + }, + "output": { + "CodeSha256": "YFgDgEKG3ugvF1+pX64gV6tu9qNuIYNUdgJm8nCxsm4=", + "CodeSize": 5797206, + "Description": "Process image objects from Amazon S3.", + "Environment": { + "Variables": { + "BUCKET": "my-bucket-1xpuxmplzrlbh", + "PREFIX": "inbound" + } + }, + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function", + "FunctionName": "my-function", + "Handler": "index.handler", + "KMSKeyArn": "arn:aws:kms:us-west-2:123456789012:key/b0844d6c-xmpl-4463-97a4-d49f50839966", + "LastModified": "2020-04-10T19:06:32.563+0000", + "LastUpdateStatus": "Successful", + "MemorySize": 256, + "RevisionId": "b75dcd81-xmpl-48a8-a75a-93ba8b5b9727", + "Role": "arn:aws:iam::123456789012:role/lambda-role", + "Runtime": "nodejs12.x", + "State": "Active", + "Timeout": 5, + "TracingConfig": { + "Mode": "Active" + }, + "Version": "1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation publishes a version of a Lambda function", + "id": "to-publish-a-version-of-a-lambda-function-1481650704986", + "title": "To publish a version of a Lambda function" + } + ], + "PutFunctionConcurrency": [ + { + "input": { + "FunctionName": "my-function", + "ReservedConcurrentExecutions": 100 + }, + "output": { + "ReservedConcurrentExecutions": 100 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example configures 100 reserved concurrent executions for the my-function function.", + "id": "to-configure-a-reserved-concurrency-limit-for-a-function-1586491405956", + "title": "To configure a reserved concurrency limit for a function" + } + ], + "PutFunctionEventInvokeConfig": [ + { + "input": { + "FunctionName": "my-function", + "MaximumEventAgeInSeconds": 3600, + "MaximumRetryAttempts": 0 + }, + "output": { + "DestinationConfig": { + "OnFailure": { + }, + "OnSuccess": { + } + }, + "FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:my-function:$LATEST", + "LastModified": "2016-11-21T19:49:20.006+0000", + "MaximumEventAgeInSeconds": 3600, + "MaximumRetryAttempts": 0 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example sets a maximum event age of one hour and disables retries for the specified function.", + "id": "to-configure-error-handling-for-asynchronous-invocation-1586491524021", + "title": "To configure error handling for asynchronous invocation" + } + ], + "PutProvisionedConcurrencyConfig": [ + { + "input": { + "FunctionName": "my-function", + "ProvisionedConcurrentExecutions": 100, + "Qualifier": "BLUE" + }, + "output": { + "AllocatedProvisionedConcurrentExecutions": 0, + "LastModified": "2019-11-21T19:32:12+0000", + "RequestedProvisionedConcurrentExecutions": 100, + "Status": "IN_PROGRESS" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example allocates 100 provisioned concurrency for the BLUE alias of the specified function.", + "id": "to-allocate-provisioned-concurrency-1586491651377", + "title": "To allocate provisioned concurrency" + } + ], + "RemoveLayerVersionPermission": [ + { + "input": { + "LayerName": "my-layer", + "StatementId": "xaccount", + "VersionNumber": 1 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes permission for an account to configure a layer version.", + "id": "to-delete-layer-version-permissions-1586491829416", + "title": "To delete layer-version permissions" + } + ], + "RemovePermission": [ + { + "input": { + "FunctionName": "my-function", + "Qualifier": "PROD", + "StatementId": "xaccount" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example removes a permissions statement named xaccount from the PROD alias of a function named my-function.", + "id": "to-remove-a-lambda-functions-permissions-1481661337021", + "title": "To remove a Lambda function's permissions" + } + ], + "TagResource": [ + { + "input": { + "Resource": "arn:aws:lambda:us-west-2:123456789012:function:my-function", + "Tags": { + "DEPARTMENT": "Department A" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example adds a tag with the key name DEPARTMENT and a value of 'Department A' to the specified Lambda function.", + "id": "to-add-tags-to-an-existing-lambda-function-1586491890446", + "title": "To add tags to an existing Lambda function" + } + ], + "UntagResource": [ + { + "input": { + "Resource": "arn:aws:lambda:us-west-2:123456789012:function:my-function", + "TagKeys": [ + "DEPARTMENT" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example removes the tag with the key name DEPARTMENT tag from the my-function Lambda function.", + "id": "to-remove-tags-from-an-existing-lambda-function-1586491956425", + "title": "To remove tags from an existing Lambda function" + } + ], + "UpdateAlias": [ + { + "input": { + "FunctionName": "my-function", + "FunctionVersion": "2", + "Name": "BLUE", + "RoutingConfig": { + "AdditionalVersionWeights": { + "1": 0.7 + } + } + }, + "output": { + "AliasArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function:BLUE", + "Description": "Production environment BLUE.", + "FunctionVersion": "2", + "Name": "BLUE", + "RevisionId": "594f41fb-xmpl-4c20-95c7-6ca5f2a92c93", + "RoutingConfig": { + "AdditionalVersionWeights": { + "1": 0.7 + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example updates the alias named BLUE to send 30% of traffic to version 2 and 70% to version 1.", + "id": "to-update-a-function-alias-1481650817950", + "title": "To update a function alias" + } + ], + "UpdateEventSourceMapping": [ + { + "input": { + "BatchSize": 123, + "Enabled": true, + "FunctionName": "myFunction", + "UUID": "1234xCy789012" + }, + "output": { + "BatchSize": 123, + "EventSourceArn": "arn:aws:s3:::examplebucket/*", + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:myFunction", + "LastModified": "2016-11-21T19:49:20.006+0000", + "LastProcessingResult": "", + "State": "", + "StateTransitionReason": "", + "UUID": "1234xCy789012" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation updates a Lambda function event source mapping", + "id": "to-update-a-lambda-function-event-source-mapping-1481650907413", + "title": "To update a Lambda function event source mapping" + } + ], + "UpdateFunctionCode": [ + { + "input": { + "FunctionName": "my-function", + "S3Bucket": "my-bucket-1xpuxmplzrlbh", + "S3Key": "function.zip" + }, + "output": { + "CodeSha256": "PFn4S+er27qk+UuZSTKEQfNKG/XNn7QJs90mJgq6oH8=", + "CodeSize": 308, + "Description": "", + "FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:my-function", + "FunctionName": "my-function", + "Handler": "index.handler", + "LastModified": "2019-08-14T22:26:11.234+0000", + "MemorySize": 128, + "RevisionId": "873282ed-xmpl-4dc8-a069-d0c647e470c6", + "Role": "arn:aws:iam::123456789012:role/lambda-role", + "Runtime": "nodejs12.x", + "Timeout": 3, + "TracingConfig": { + "Mode": "PassThrough" + }, + "Version": "$LATEST" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example replaces the code of the unpublished ($LATEST) version of a function named my-function with the contents of the specified zip file in Amazon S3.", + "id": "to-update-a-lambda-functions-code-1481650992672", + "title": "To update a Lambda function's code" + } + ], + "UpdateFunctionConfiguration": [ + { + "input": { + "FunctionName": "my-function", + "MemorySize": 256 + }, + "output": { + "CodeSha256": "PFn4S+er27qk+UuZSTKEQfNKG/XNn7QJs90mJgq6oH8=", + "CodeSize": 308, + "Description": "", + "FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:my-function", + "FunctionName": "my-function", + "Handler": "index.handler", + "LastModified": "2019-08-14T22:26:11.234+0000", + "MemorySize": 256, + "RevisionId": "873282ed-xmpl-4dc8-a069-d0c647e470c6", + "Role": "arn:aws:iam::123456789012:role/lambda-role", + "Runtime": "nodejs12.x", + "Timeout": 3, + "TracingConfig": { + "Mode": "PassThrough" + }, + "Version": "$LATEST" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example modifies the memory size to be 256 MB for the unpublished ($LATEST) version of a function named my-function.", + "id": "to-update-a-lambda-functions-configuration-1481651096447", + "title": "To update a Lambda function's configuration" + } + ], + "UpdateFunctionEventInvokeConfig": [ + { + "input": { + "DestinationConfig": { + "OnFailure": { + "Destination": "arn:aws:sqs:us-east-2:123456789012:destination" + } + }, + "FunctionName": "my-function" + }, + "output": { + "DestinationConfig": { + "OnFailure": { + "Destination": "arn:aws:sqs:us-east-2:123456789012:destination" + }, + "OnSuccess": { + } + }, + "FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:my-function:$LATEST", + "LastModified": 1573687896.493, + "MaximumEventAgeInSeconds": 3600, + "MaximumRetryAttempts": 0 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example adds an on-failure destination to the existing asynchronous invocation configuration for a function named my-function.", + "id": "to-update-an-asynchronous-invocation-configuration-1586492061186", + "title": "To update an asynchronous invocation configuration" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/paginators-1.json new file mode 100644 index 0000000..a8a19ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/paginators-1.json @@ -0,0 +1,100 @@ +{ + "pagination": { + "ListEventSourceMappings": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "EventSourceMappings" + }, + "ListFunctions": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "Functions" + }, + "ListAliases": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "Aliases" + }, + "ListLayerVersions": { + "input_token": "Marker", + "limit_key": "MaxItems", + "output_token": "NextMarker", + "result_key": "LayerVersions" + }, + "ListLayers": { + "input_token": "Marker", + "limit_key": "MaxItems", + "output_token": "NextMarker", + "result_key": "Layers" + }, + "ListVersionsByFunction": { + "input_token": "Marker", + "limit_key": "MaxItems", + "output_token": "NextMarker", + "result_key": "Versions" + }, + "ListFunctionEventInvokeConfigs": { + "input_token": "Marker", + "limit_key": "MaxItems", + "output_token": "NextMarker", + "result_key": "FunctionEventInvokeConfigs" + }, + "ListProvisionedConcurrencyConfigs": { + "input_token": "Marker", + "limit_key": "MaxItems", + "output_token": "NextMarker", + "result_key": "ProvisionedConcurrencyConfigs" + }, + "ListCodeSigningConfigs": { + "input_token": "Marker", + "limit_key": "MaxItems", + "output_token": "NextMarker", + "result_key": "CodeSigningConfigs" + }, + "ListFunctionsByCodeSigningConfig": { + "input_token": "Marker", + "limit_key": "MaxItems", + "output_token": "NextMarker", + "result_key": "FunctionArns" + }, + "ListFunctionUrlConfigs": { + "input_token": "Marker", + "limit_key": "MaxItems", + "output_token": "NextMarker", + "result_key": "FunctionUrlConfigs" + }, + "ListCapacityProviders": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "CapacityProviders" + }, + "ListFunctionVersionsByCapacityProvider": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "FunctionVersions" + }, + "GetDurableExecutionHistory": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "Events" + }, + "GetDurableExecutionState": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "Operations" + }, + "ListDurableExecutionsByFunction": { + "input_token": "Marker", + "output_token": "NextMarker", + "limit_key": "MaxItems", + "result_key": "DurableExecutions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/paginators-1.sdk-extras.json new file mode 100644 index 0000000..98cc059 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListFunctionVersionsByCapacityProvider": { + "non_aggregate_keys": [ + "CapacityProviderArn" + ] + } + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/service-2.json.gz new file mode 100644 index 0000000..37e132f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/waiters-2.json new file mode 100644 index 0000000..92defa1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lambda/2015-03-31/waiters-2.json @@ -0,0 +1,129 @@ +{ + "version" : 2, + "waiters" : { + "FunctionActive" : { + "description" : "Waits for the function's State to be Active. This waiter uses GetFunctionConfiguration API. This should be used after new function creation.", + "delay" : 5, + "maxAttempts" : 60, + "operation" : "GetFunctionConfiguration", + "acceptors" : [ { + "matcher" : "path", + "argument" : "State", + "state" : "success", + "expected" : "Active" + }, { + "matcher" : "path", + "argument" : "State", + "state" : "failure", + "expected" : "Failed" + }, { + "matcher" : "path", + "argument" : "State", + "state" : "retry", + "expected" : "Pending" + } ] + }, + "FunctionActiveV2" : { + "description" : "Waits for the function's State to be Active. This waiter uses GetFunction API. This should be used after new function creation.", + "delay" : 1, + "maxAttempts" : 300, + "operation" : "GetFunction", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Configuration.State", + "state" : "success", + "expected" : "Active" + }, { + "matcher" : "path", + "argument" : "Configuration.State", + "state" : "failure", + "expected" : "Failed" + }, { + "matcher" : "path", + "argument" : "Configuration.State", + "state" : "retry", + "expected" : "Pending" + } ] + }, + "FunctionExists" : { + "delay" : 1, + "maxAttempts" : 20, + "operation" : "GetFunction", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : false + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "ResourceNotFoundException" + } ] + }, + "FunctionUpdated" : { + "description" : "Waits for the function's LastUpdateStatus to be Successful. This waiter uses GetFunctionConfiguration API. This should be used after function updates.", + "delay" : 5, + "maxAttempts" : 60, + "operation" : "GetFunctionConfiguration", + "acceptors" : [ { + "matcher" : "path", + "argument" : "LastUpdateStatus", + "state" : "success", + "expected" : "Successful" + }, { + "matcher" : "path", + "argument" : "LastUpdateStatus", + "state" : "failure", + "expected" : "Failed" + }, { + "matcher" : "path", + "argument" : "LastUpdateStatus", + "state" : "retry", + "expected" : "InProgress" + } ] + }, + "FunctionUpdatedV2" : { + "description" : "Waits for the function's LastUpdateStatus to be Successful. This waiter uses GetFunction API. This should be used after function updates.", + "delay" : 1, + "maxAttempts" : 300, + "operation" : "GetFunction", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Configuration.LastUpdateStatus", + "state" : "success", + "expected" : "Successful" + }, { + "matcher" : "path", + "argument" : "Configuration.LastUpdateStatus", + "state" : "failure", + "expected" : "Failed" + }, { + "matcher" : "path", + "argument" : "Configuration.LastUpdateStatus", + "state" : "retry", + "expected" : "InProgress" + } ] + }, + "PublishedVersionActive" : { + "description" : "Waits for the published version's State to be Active. This waiter uses GetFunctionConfiguration API. This should be used after new version is published.", + "delay" : 5, + "maxAttempts" : 312, + "operation" : "GetFunctionConfiguration", + "acceptors" : [ { + "matcher" : "path", + "argument" : "State", + "state" : "success", + "expected" : "Active" + }, { + "matcher" : "path", + "argument" : "State", + "state" : "failure", + "expected" : "Failed" + }, { + "matcher" : "path", + "argument" : "State", + "state" : "retry", + "expected" : "Pending" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/launch-wizard/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/launch-wizard/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..61a64bc Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/launch-wizard/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/launch-wizard/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/launch-wizard/2018-05-10/paginators-1.json new file mode 100644 index 0000000..753e03e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/launch-wizard/2018-05-10/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListDeploymentEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "deploymentEvents" + }, + "ListDeployments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "deployments" + }, + "ListWorkloadDeploymentPatterns": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workloadDeploymentPatterns" + }, + "ListWorkloads": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workloads" + }, + "ListDeploymentPatternVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "deploymentPatternVersions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/launch-wizard/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/launch-wizard/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..5faea76 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/launch-wizard/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/launch-wizard/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/launch-wizard/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/launch-wizard/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lex-models/2017-04-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lex-models/2017-04-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f97e4fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lex-models/2017-04-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lex-models/2017-04-19/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/lex-models/2017-04-19/examples-1.json new file mode 100644 index 0000000..0982d97 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lex-models/2017-04-19/examples-1.json @@ -0,0 +1,758 @@ +{ + "version": "1.0", + "examples": { + "GetBot": [ + { + "input": { + "name": "DocOrderPizza", + "versionOrAlias": "$LATEST" + }, + "output": { + "version": "$LATEST", + "name": "DocOrderPizzaBot", + "abortStatement": { + "messages": [ + { + "content": "I don't understand. Can you try again?", + "contentType": "PlainText" + }, + { + "content": "I'm sorry, I don't understand.", + "contentType": "PlainText" + } + ] + }, + "checksum": "20172ee3-fa06-49b2-bbc5-667c090303e9", + "childDirected": true, + "clarificationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "I'm sorry, I didn't hear that. Can you repeate what you just said?", + "contentType": "PlainText" + }, + { + "content": "Can you say that again?", + "contentType": "PlainText" + } + ] + }, + "createdDate": 1494360160.133, + "description": "Orders a pizza from a local pizzeria.", + "idleSessionTTLInSeconds": 300, + "intents": [ + { + "intentName": "DocOrderPizza", + "intentVersion": "$LATEST" + } + ], + "lastUpdatedDate": 1494360160.133, + "locale": "en-US", + "status": "NOT_BUILT" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows how to get configuration information for a bot.", + "id": "to-get-information-about-a-bot-1494431724188", + "title": "To get information about a bot" + } + ], + "GetBots": [ + { + "input": { + "maxResults": 5, + "nextToken": "" + }, + "output": { + "bots": [ + { + "version": "$LATEST", + "name": "DocOrderPizzaBot", + "createdDate": 1494360160.133, + "description": "Orders a pizza from a local pizzeria.", + "lastUpdatedDate": 1494360160.133, + "status": "NOT_BUILT" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows how to get a list of all of the bots in your account.", + "id": "to-get-a-list-of-bots-1494432220036", + "title": "To get a list of bots" + } + ], + "GetIntent": [ + { + "input": { + "version": "$LATEST", + "name": "DocOrderPizza" + }, + "output": { + "version": "$LATEST", + "name": "DocOrderPizza", + "checksum": "ca9bc13d-afc8-4706-bbaf-091f7a5935d6", + "conclusionStatement": { + "messages": [ + { + "content": "All right, I ordered you a {Crust} crust {Type} pizza with {Sauce} sauce.", + "contentType": "PlainText" + }, + { + "content": "OK, your {Crust} crust {Type} pizza with {Sauce} sauce is on the way.", + "contentType": "PlainText" + } + ], + "responseCard": "foo" + }, + "confirmationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "Should I order your {Crust} crust {Type} pizza with {Sauce} sauce?", + "contentType": "PlainText" + } + ] + }, + "createdDate": 1494359783.453, + "description": "Order a pizza from a local pizzeria.", + "fulfillmentActivity": { + "type": "ReturnIntent" + }, + "lastUpdatedDate": 1494359783.453, + "rejectionStatement": { + "messages": [ + { + "content": "Ok, I'll cancel your order.", + "contentType": "PlainText" + }, + { + "content": "I cancelled your order.", + "contentType": "PlainText" + } + ] + }, + "sampleUtterances": [ + "Order me a pizza.", + "Order me a {Type} pizza.", + "I want a {Crust} crust {Type} pizza", + "I want a {Crust} crust {Type} pizza with {Sauce} sauce." + ], + "slots": [ + { + "name": "Type", + "description": "The type of pizza to order.", + "priority": 1, + "sampleUtterances": [ + "Get me a {Type} pizza.", + "A {Type} pizza please.", + "I'd like a {Type} pizza." + ], + "slotConstraint": "Required", + "slotType": "DocPizzaType", + "slotTypeVersion": "$LATEST", + "valueElicitationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "What type of pizza would you like?", + "contentType": "PlainText" + }, + { + "content": "Vegie or cheese pizza?", + "contentType": "PlainText" + }, + { + "content": "I can get you a vegie or a cheese pizza.", + "contentType": "PlainText" + } + ] + } + }, + { + "name": "Crust", + "description": "The type of pizza crust to order.", + "priority": 2, + "sampleUtterances": [ + "Make it a {Crust} crust.", + "I'd like a {Crust} crust." + ], + "slotConstraint": "Required", + "slotType": "DocPizzaCrustType", + "slotTypeVersion": "$LATEST", + "valueElicitationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "What type of crust would you like?", + "contentType": "PlainText" + }, + { + "content": "Thick or thin crust?", + "contentType": "PlainText" + } + ] + } + }, + { + "name": "Sauce", + "description": "The type of sauce to use on the pizza.", + "priority": 3, + "sampleUtterances": [ + "Make it {Sauce} sauce.", + "I'd like {Sauce} sauce." + ], + "slotConstraint": "Required", + "slotType": "DocPizzaSauceType", + "slotTypeVersion": "$LATEST", + "valueElicitationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "White or red sauce?", + "contentType": "PlainText" + }, + { + "content": "Garlic or tomato sauce?", + "contentType": "PlainText" + } + ] + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows how to get information about an intent.", + "id": "to-get-a-information-about-an-intent-1494432574147", + "title": "To get a information about an intent" + } + ], + "GetIntents": [ + { + "input": { + "maxResults": 10, + "nextToken": "" + }, + "output": { + "intents": [ + { + "version": "$LATEST", + "name": "DocOrderPizza", + "createdDate": 1494359783.453, + "description": "Order a pizza from a local pizzeria.", + "lastUpdatedDate": 1494359783.453 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows how to get a list of all of the intents in your account.", + "id": "to-get-a-list-of-intents-1494432416363", + "title": "To get a list of intents" + } + ], + "GetSlotType": [ + { + "input": { + "version": "$LATEST", + "name": "DocPizzaCrustType" + }, + "output": { + "version": "$LATEST", + "name": "DocPizzaCrustType", + "checksum": "210b3d5a-90a3-4b22-ac7e-f50c2c71095f", + "createdDate": 1494359274.403, + "description": "Available crust types", + "enumerationValues": [ + { + "value": "thick" + }, + { + "value": "thin" + } + ], + "lastUpdatedDate": 1494359274.403 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows how to get information about a slot type.", + "id": "to-get-information-about-a-slot-type-1494432961004", + "title": "To get information about a slot type" + } + ], + "GetSlotTypes": [ + { + "input": { + "maxResults": 10, + "nextToken": "" + }, + "output": { + "slotTypes": [ + { + "version": "$LATEST", + "name": "DocPizzaCrustType", + "createdDate": 1494359274.403, + "description": "Available crust types", + "lastUpdatedDate": 1494359274.403 + }, + { + "version": "$LATEST", + "name": "DocPizzaSauceType", + "createdDate": 1494356442.23, + "description": "Available pizza sauces", + "lastUpdatedDate": 1494356442.23 + }, + { + "version": "$LATEST", + "name": "DocPizzaType", + "createdDate": 1494359198.656, + "description": "Available pizzas", + "lastUpdatedDate": 1494359198.656 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows how to get a list of all of the slot types in your account.", + "id": "to-get-a-list-of-slot-types-1494432757458", + "title": "To get a list of slot types" + } + ], + "PutBot": [ + { + "input": { + "name": "DocOrderPizzaBot", + "abortStatement": { + "messages": [ + { + "content": "I don't understand. Can you try again?", + "contentType": "PlainText" + }, + { + "content": "I'm sorry, I don't understand.", + "contentType": "PlainText" + } + ] + }, + "childDirected": true, + "clarificationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "I'm sorry, I didn't hear that. Can you repeat what you just said?", + "contentType": "PlainText" + }, + { + "content": "Can you say that again?", + "contentType": "PlainText" + } + ] + }, + "description": "Orders a pizza from a local pizzeria.", + "idleSessionTTLInSeconds": 300, + "intents": [ + { + "intentName": "DocOrderPizza", + "intentVersion": "$LATEST" + } + ], + "locale": "en-US", + "processBehavior": "SAVE" + }, + "output": { + "version": "$LATEST", + "name": "DocOrderPizzaBot", + "abortStatement": { + "messages": [ + { + "content": "I don't understand. Can you try again?", + "contentType": "PlainText" + }, + { + "content": "I'm sorry, I don't understand.", + "contentType": "PlainText" + } + ] + }, + "checksum": "20172ee3-fa06-49b2-bbc5-667c090303e9", + "childDirected": true, + "clarificationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "I'm sorry, I didn't hear that. Can you repeate what you just said?", + "contentType": "PlainText" + }, + { + "content": "Can you say that again?", + "contentType": "PlainText" + } + ] + }, + "createdDate": 1494360160.133, + "description": "Orders a pizza from a local pizzeria.", + "idleSessionTTLInSeconds": 300, + "intents": [ + { + "intentName": "DocOrderPizza", + "intentVersion": "$LATEST" + } + ], + "lastUpdatedDate": 1494360160.133, + "locale": "en-US", + "status": "NOT_BUILT" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows how to create a bot for ordering pizzas.", + "id": "to-create-a-bot-1494360003886", + "title": "To create a bot" + } + ], + "PutIntent": [ + { + "input": { + "name": "DocOrderPizza", + "conclusionStatement": { + "messages": [ + { + "content": "All right, I ordered you a {Crust} crust {Type} pizza with {Sauce} sauce.", + "contentType": "PlainText" + }, + { + "content": "OK, your {Crust} crust {Type} pizza with {Sauce} sauce is on the way.", + "contentType": "PlainText" + } + ], + "responseCard": "foo" + }, + "confirmationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "Should I order your {Crust} crust {Type} pizza with {Sauce} sauce?", + "contentType": "PlainText" + } + ] + }, + "description": "Order a pizza from a local pizzeria.", + "fulfillmentActivity": { + "type": "ReturnIntent" + }, + "rejectionStatement": { + "messages": [ + { + "content": "Ok, I'll cancel your order.", + "contentType": "PlainText" + }, + { + "content": "I cancelled your order.", + "contentType": "PlainText" + } + ] + }, + "sampleUtterances": [ + "Order me a pizza.", + "Order me a {Type} pizza.", + "I want a {Crust} crust {Type} pizza", + "I want a {Crust} crust {Type} pizza with {Sauce} sauce." + ], + "slots": [ + { + "name": "Type", + "description": "The type of pizza to order.", + "priority": 1, + "sampleUtterances": [ + "Get me a {Type} pizza.", + "A {Type} pizza please.", + "I'd like a {Type} pizza." + ], + "slotConstraint": "Required", + "slotType": "DocPizzaType", + "slotTypeVersion": "$LATEST", + "valueElicitationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "What type of pizza would you like?", + "contentType": "PlainText" + }, + { + "content": "Vegie or cheese pizza?", + "contentType": "PlainText" + }, + { + "content": "I can get you a vegie or a cheese pizza.", + "contentType": "PlainText" + } + ] + } + }, + { + "name": "Crust", + "description": "The type of pizza crust to order.", + "priority": 2, + "sampleUtterances": [ + "Make it a {Crust} crust.", + "I'd like a {Crust} crust." + ], + "slotConstraint": "Required", + "slotType": "DocPizzaCrustType", + "slotTypeVersion": "$LATEST", + "valueElicitationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "What type of crust would you like?", + "contentType": "PlainText" + }, + { + "content": "Thick or thin crust?", + "contentType": "PlainText" + } + ] + } + }, + { + "name": "Sauce", + "description": "The type of sauce to use on the pizza.", + "priority": 3, + "sampleUtterances": [ + "Make it {Sauce} sauce.", + "I'd like {Sauce} sauce." + ], + "slotConstraint": "Required", + "slotType": "DocPizzaSauceType", + "slotTypeVersion": "$LATEST", + "valueElicitationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "White or red sauce?", + "contentType": "PlainText" + }, + { + "content": "Garlic or tomato sauce?", + "contentType": "PlainText" + } + ] + } + } + ] + }, + "output": { + "version": "$LATEST", + "name": "DocOrderPizza", + "checksum": "ca9bc13d-afc8-4706-bbaf-091f7a5935d6", + "conclusionStatement": { + "messages": [ + { + "content": "All right, I ordered you a {Crust} crust {Type} pizza with {Sauce} sauce.", + "contentType": "PlainText" + }, + { + "content": "OK, your {Crust} crust {Type} pizza with {Sauce} sauce is on the way.", + "contentType": "PlainText" + } + ], + "responseCard": "foo" + }, + "confirmationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "Should I order your {Crust} crust {Type} pizza with {Sauce} sauce?", + "contentType": "PlainText" + } + ] + }, + "createdDate": 1494359783.453, + "description": "Order a pizza from a local pizzeria.", + "fulfillmentActivity": { + "type": "ReturnIntent" + }, + "lastUpdatedDate": 1494359783.453, + "rejectionStatement": { + "messages": [ + { + "content": "Ok, I'll cancel your order.", + "contentType": "PlainText" + }, + { + "content": "I cancelled your order.", + "contentType": "PlainText" + } + ] + }, + "sampleUtterances": [ + "Order me a pizza.", + "Order me a {Type} pizza.", + "I want a {Crust} crust {Type} pizza", + "I want a {Crust} crust {Type} pizza with {Sauce} sauce." + ], + "slots": [ + { + "name": "Sauce", + "description": "The type of sauce to use on the pizza.", + "priority": 3, + "sampleUtterances": [ + "Make it {Sauce} sauce.", + "I'd like {Sauce} sauce." + ], + "slotConstraint": "Required", + "slotType": "DocPizzaSauceType", + "slotTypeVersion": "$LATEST", + "valueElicitationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "White or red sauce?", + "contentType": "PlainText" + }, + { + "content": "Garlic or tomato sauce?", + "contentType": "PlainText" + } + ] + } + }, + { + "name": "Type", + "description": "The type of pizza to order.", + "priority": 1, + "sampleUtterances": [ + "Get me a {Type} pizza.", + "A {Type} pizza please.", + "I'd like a {Type} pizza." + ], + "slotConstraint": "Required", + "slotType": "DocPizzaType", + "slotTypeVersion": "$LATEST", + "valueElicitationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "What type of pizza would you like?", + "contentType": "PlainText" + }, + { + "content": "Vegie or cheese pizza?", + "contentType": "PlainText" + }, + { + "content": "I can get you a vegie or a cheese pizza.", + "contentType": "PlainText" + } + ] + } + }, + { + "name": "Crust", + "description": "The type of pizza crust to order.", + "priority": 2, + "sampleUtterances": [ + "Make it a {Crust} crust.", + "I'd like a {Crust} crust." + ], + "slotConstraint": "Required", + "slotType": "DocPizzaCrustType", + "slotTypeVersion": "$LATEST", + "valueElicitationPrompt": { + "maxAttempts": 1, + "messages": [ + { + "content": "What type of crust would you like?", + "contentType": "PlainText" + }, + { + "content": "Thick or thin crust?", + "contentType": "PlainText" + } + ] + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows how to create an intent for ordering pizzas.", + "id": "to-create-an-intent-1494358144659", + "title": "To create an intent" + } + ], + "PutSlotType": [ + { + "input": { + "name": "PizzaSauceType", + "description": "Available pizza sauces", + "enumerationValues": [ + { + "value": "red" + }, + { + "value": "white" + } + ] + }, + "output": { + "version": "$LATEST", + "name": "DocPizzaSauceType", + "checksum": "cfd00ed1-775d-4357-947c-aca7e73b44ba", + "createdDate": 1494356442.23, + "description": "Available pizza sauces", + "enumerationValues": [ + { + "value": "red" + }, + { + "value": "white" + } + ], + "lastUpdatedDate": 1494356442.23 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows how to create a slot type that describes pizza sauces.", + "id": "to-create-a-slot-type-1494357262258", + "title": "To Create a Slot Type" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lex-models/2017-04-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/lex-models/2017-04-19/paginators-1.json new file mode 100644 index 0000000..02d2308 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lex-models/2017-04-19/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "GetSlotTypeVersions": { + "result_key": "slotTypes", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "GetSlotTypes": { + "result_key": "slotTypes", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "GetIntents": { + "result_key": "intents", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "GetBotChannelAssociations": { + "result_key": "botChannelAssociations", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "GetBots": { + "result_key": "bots", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "GetBuiltinSlotTypes": { + "result_key": "slotTypes", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "GetIntentVersions": { + "result_key": "intents", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "GetBotAliases": { + "result_key": "BotAliases", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "GetBuiltinIntents": { + "result_key": "intents", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + }, + "GetBotVersions": { + "result_key": "bots", + "output_token": "nextToken", + "input_token": "nextToken", + "limit_key": "maxResults" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lex-models/2017-04-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lex-models/2017-04-19/service-2.json.gz new file mode 100644 index 0000000..9a2497b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lex-models/2017-04-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lex-runtime/2016-11-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lex-runtime/2016-11-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..acc96ba Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lex-runtime/2016-11-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lex-runtime/2016-11-28/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/lex-runtime/2016-11-28/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lex-runtime/2016-11-28/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lex-runtime/2016-11-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/lex-runtime/2016-11-28/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lex-runtime/2016-11-28/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lex-runtime/2016-11-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lex-runtime/2016-11-28/service-2.json.gz new file mode 100644 index 0000000..a74a243 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lex-runtime/2016-11-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..37db468 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/paginators-1.json new file mode 100644 index 0000000..2af0d7f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "DescribeBotAnalyzerRecommendation": { + "input_token": "nextToken", + "limit_key": "maxResults", + "non_aggregate_keys": [ + "creationDateTime", + "localeId", + "botId", + "botVersion", + "botAnalyzerStatus" + ], + "output_token": "nextToken", + "result_key": "botAnalyzerRecommendationList" + }, + "ListBotAnalyzerHistory": { + "input_token": "nextToken", + "limit_key": "maxResults", + "non_aggregate_keys": [ + "localeId", + "botId", + "botVersion" + ], + "output_token": "nextToken", + "result_key": "botAnalyzerHistoryList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/service-2.json.gz new file mode 100644 index 0000000..5f2336f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/waiters-2.json new file mode 100644 index 0000000..1ec9604 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lexv2-models/2020-08-07/waiters-2.json @@ -0,0 +1,255 @@ +{ + "version":2, + "waiters":{ + "BotAvailable":{ + "delay":10, + "operation":"DescribeBot", + "maxAttempts":35, + "description":"Wait until a bot is available", + "acceptors":[ + { + "expected":"Available", + "matcher":"path", + "state":"success", + "argument":"botStatus" + }, + { + "expected":"Deleting", + "matcher":"path", + "state":"failure", + "argument":"botStatus" + }, + { + "expected":"Failed", + "matcher":"path", + "state":"failure", + "argument":"botStatus" + }, + { + "expected":"Inactive", + "matcher":"path", + "state":"failure", + "argument":"botStatus" + } + ] + }, + "BotAliasAvailable":{ + "delay":10, + "operation":"DescribeBotAlias", + "maxAttempts":35, + "description":"Wait until a bot alias is available", + "acceptors":[ + { + "expected":"Available", + "matcher":"path", + "state":"success", + "argument":"botAliasStatus" + }, + { + "expected":"Failed", + "matcher":"path", + "state":"failure", + "argument":"botAliasStatus" + }, + { + "expected":"Deleting", + "matcher":"path", + "state":"failure", + "argument":"botAliasStatus" + } + ] + }, + "BotExportCompleted":{ + "delay":10, + "operation":"DescribeExport", + "maxAttempts":35, + "description":"Wait until a bot has been exported", + "acceptors":[ + { + "expected":"Completed", + "matcher":"path", + "state":"success", + "argument":"exportStatus" + }, + { + "expected":"Deleting", + "matcher":"path", + "state":"failure", + "argument":"exportStatus" + }, + { + "expected":"Failed", + "matcher":"path", + "state":"failure", + "argument":"exportStatus" + } + ] + }, + "BotImportCompleted":{ + "delay":10, + "operation":"DescribeImport", + "maxAttempts":35, + "description":"Wait until a bot has been imported", + "acceptors":[ + { + "expected":"Completed", + "matcher":"path", + "state":"success", + "argument":"importStatus" + }, + { + "expected":"Deleting", + "matcher":"path", + "state":"failure", + "argument":"importStatus" + }, + { + "expected":"Failed", + "matcher":"path", + "state":"failure", + "argument":"importStatus" + } + ] + }, + "BotLocaleBuilt":{ + "delay":10, + "operation":"DescribeBotLocale", + "maxAttempts":35, + "description":"Wait until a bot locale is built", + "acceptors":[ + { + "expected":"Built", + "matcher":"path", + "state":"success", + "argument":"botLocaleStatus" + }, + { + "expected":"Deleting", + "matcher":"path", + "state":"failure", + "argument":"botLocaleStatus" + }, + { + "expected":"Failed", + "matcher":"path", + "state":"failure", + "argument":"botLocaleStatus" + }, + { + "expected":"NotBuilt", + "matcher":"path", + "state":"failure", + "argument":"botLocaleStatus" + } + ] + }, + "BotLocaleExpressTestingAvailable":{ + "delay":10, + "operation":"DescribeBotLocale", + "maxAttempts":35, + "description":"Wait until a bot locale build is ready for express testing", + "acceptors":[ + { + "expected":"Built", + "matcher":"path", + "state":"success", + "argument":"botLocaleStatus" + }, + { + "expected":"ReadyExpressTesting", + "matcher":"path", + "state":"success", + "argument":"botLocaleStatus" + }, + { + "expected":"Deleting", + "matcher":"path", + "state":"failure", + "argument":"botLocaleStatus" + }, + { + "expected":"Failed", + "matcher":"path", + "state":"failure", + "argument":"botLocaleStatus" + }, + { + "expected":"NotBuilt", + "matcher":"path", + "state":"failure", + "argument":"botLocaleStatus" + } + ] + }, + "BotVersionAvailable":{ + "delay":10, + "operation":"DescribeBotVersion", + "maxAttempts":35, + "description":"Wait until a bot version is available", + "acceptors":[ + { + "expected":"Available", + "matcher":"path", + "state":"success", + "argument":"botStatus" + }, + { + "expected":"Deleting", + "matcher":"path", + "state":"failure", + "argument":"botStatus" + }, + { + "expected":"Failed", + "matcher":"path", + "state":"failure", + "argument":"botStatus" + }, + { + "state":"retry", + "matcher":"status", + "expected":404 + } + ] + }, + "BotLocaleCreated":{ + "delay":10, + "operation":"DescribeBotLocale", + "maxAttempts":35, + "description":"Wait unit a bot locale is created", + "acceptors":[ + { + "expected":"Built", + "matcher":"path", + "state":"success", + "argument":"botLocaleStatus" + }, + { + "expected":"ReadyExpressTesting", + "matcher":"path", + "state":"success", + "argument":"botLocaleStatus" + }, + { + "expected":"NotBuilt", + "matcher":"path", + "state":"success", + "argument":"botLocaleStatus" + }, + { + "expected":"Deleting", + "matcher":"path", + "state":"failure", + "argument":"botLocaleStatus" + }, + { + "expected":"Failed", + "matcher":"path", + "state":"failure", + "argument":"botLocaleStatus" + } + ] + } + } +} + diff --git a/venv/lib/python3.12/site-packages/botocore/data/lexv2-runtime/2020-08-07/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lexv2-runtime/2020-08-07/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1f67fd1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lexv2-runtime/2020-08-07/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lexv2-runtime/2020-08-07/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/lexv2-runtime/2020-08-07/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lexv2-runtime/2020-08-07/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lexv2-runtime/2020-08-07/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/lexv2-runtime/2020-08-07/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lexv2-runtime/2020-08-07/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lexv2-runtime/2020-08-07/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lexv2-runtime/2020-08-07/service-2.json.gz new file mode 100644 index 0000000..62d9e7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lexv2-runtime/2020-08-07/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/license-manager-linux-subscriptions/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/license-manager-linux-subscriptions/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d404373 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/license-manager-linux-subscriptions/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/license-manager-linux-subscriptions/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/license-manager-linux-subscriptions/2018-05-10/paginators-1.json new file mode 100644 index 0000000..6d74d22 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/license-manager-linux-subscriptions/2018-05-10/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListLinuxSubscriptionInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Instances" + }, + "ListLinuxSubscriptions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Subscriptions" + }, + "ListRegisteredSubscriptionProviders": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RegisteredSubscriptionProviders" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/license-manager-linux-subscriptions/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/license-manager-linux-subscriptions/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..cdcea12 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/license-manager-linux-subscriptions/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/license-manager-user-subscriptions/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/license-manager-user-subscriptions/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c4933aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/license-manager-user-subscriptions/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/license-manager-user-subscriptions/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/license-manager-user-subscriptions/2018-05-10/paginators-1.json new file mode 100644 index 0000000..413006e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/license-manager-user-subscriptions/2018-05-10/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListIdentityProviders": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "IdentityProviderSummaries" + }, + "ListInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceSummaries" + }, + "ListProductSubscriptions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ProductUserSummaries" + }, + "ListUserAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceUserSummaries" + }, + "ListLicenseServerEndpoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "LicenseServerEndpoints" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/license-manager-user-subscriptions/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/license-manager-user-subscriptions/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..3cdd1d5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/license-manager-user-subscriptions/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/license-manager-user-subscriptions/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/license-manager-user-subscriptions/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/license-manager-user-subscriptions/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/license-manager/2018-08-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/license-manager/2018-08-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b1b8047 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/license-manager/2018-08-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/license-manager/2018-08-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/license-manager/2018-08-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/license-manager/2018-08-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/license-manager/2018-08-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/license-manager/2018-08-01/paginators-1.json new file mode 100644 index 0000000..03a3ca4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/license-manager/2018-08-01/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListAssociationsForLicenseConfiguration": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LicenseConfigurationAssociations" + }, + "ListLicenseConfigurations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LicenseConfigurations" + }, + "ListLicenseSpecificationsForResource": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LicenseSpecifications" + }, + "ListResourceInventory": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ResourceInventoryList" + }, + "ListUsageForLicenseConfiguration": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LicenseConfigurationUsageList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/license-manager/2018-08-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/license-manager/2018-08-01/service-2.json.gz new file mode 100644 index 0000000..70940b7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/license-manager/2018-08-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lightsail/2016-11-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lightsail/2016-11-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..114320b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lightsail/2016-11-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lightsail/2016-11-28/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/lightsail/2016-11-28/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lightsail/2016-11-28/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lightsail/2016-11-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/lightsail/2016-11-28/paginators-1.json new file mode 100644 index 0000000..fbea938 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lightsail/2016-11-28/paginators-1.json @@ -0,0 +1,104 @@ +{ + "pagination": { + "GetActiveNames": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "activeNames" + }, + "GetBlueprints": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "blueprints" + }, + "GetBundles": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "bundles" + }, + "GetDomains": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "domains" + }, + "GetInstanceSnapshots": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "instanceSnapshots" + }, + "GetInstances": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "instances" + }, + "GetKeyPairs": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "keyPairs" + }, + "GetOperations": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "operations" + }, + "GetStaticIps": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "staticIps" + }, + "GetCloudFormationStackRecords": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "cloudFormationStackRecords" + }, + "GetDiskSnapshots": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "diskSnapshots" + }, + "GetDisks": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "disks" + }, + "GetExportSnapshotRecords": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "exportSnapshotRecords" + }, + "GetLoadBalancers": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "loadBalancers" + }, + "GetRelationalDatabaseBlueprints": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "blueprints" + }, + "GetRelationalDatabaseBundles": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "bundles" + }, + "GetRelationalDatabaseEvents": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "relationalDatabaseEvents" + }, + "GetRelationalDatabaseParameters": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "parameters" + }, + "GetRelationalDatabaseSnapshots": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "relationalDatabaseSnapshots" + }, + "GetRelationalDatabases": { + "input_token": "pageToken", + "output_token": "nextPageToken", + "result_key": "relationalDatabases" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lightsail/2016-11-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lightsail/2016-11-28/service-2.json.gz new file mode 100644 index 0000000..6144495 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lightsail/2016-11-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..bff2442 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/paginators-1.json new file mode 100644 index 0000000..e5226b0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/paginators-1.json @@ -0,0 +1,76 @@ +{ + "pagination": { + "GetDevicePositionHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "DevicePositions", + "limit_key": "MaxResults" + }, + "ListGeofenceCollections": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Entries" + }, + "ListGeofences": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Entries", + "limit_key": "MaxResults" + }, + "ListMaps": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Entries" + }, + "ListPlaceIndexes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Entries" + }, + "ListTrackerConsumers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ConsumerArns" + }, + "ListTrackers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Entries" + }, + "ListDevicePositions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Entries" + }, + "ListRouteCalculators": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Entries" + }, + "ListKeys": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Entries" + }, + "ForecastGeofenceEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ForecastedEvents" + }, + "ListJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Entries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/paginators-1.sdk-extras.json new file mode 100644 index 0000000..2aba0bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/paginators-1.sdk-extras.json @@ -0,0 +1,13 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ForecastGeofenceEvents": { + "non_aggregate_keys": [ + "DistanceUnit", + "SpeedUnit" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/service-2.json.gz new file mode 100644 index 0000000..bef4c14 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/waiters-2.json new file mode 100644 index 0000000..071accb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/location/2020-11-19/waiters-2.json @@ -0,0 +1,26 @@ +{ + "version" : 2, + "waiters" : { + "JobCompleted" : { + "delay" : 60, + "maxAttempts" : 5, + "operation" : "GetJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Status", + "state" : "success", + "expected" : "Completed" + }, { + "matcher" : "path", + "argument" : "Status", + "state" : "failure", + "expected" : "Failed" + }, { + "matcher" : "path", + "argument" : "Status", + "state" : "failure", + "expected" : "Cancelled" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..06a4916 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/paginators-1.json new file mode 100644 index 0000000..ac2062d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/paginators-1.json @@ -0,0 +1,127 @@ +{ + "pagination": { + "DescribeDestinations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "limit", + "result_key": "destinations" + }, + "DescribeLogGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "limit", + "result_key": "logGroups" + }, + "DescribeLogStreams": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "limit", + "result_key": "logStreams" + }, + "DescribeMetricFilters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "limit", + "result_key": "metricFilters" + }, + "DescribeSubscriptionFilters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "limit", + "result_key": "subscriptionFilters" + }, + "FilterLogEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "limit", + "result_key": [ + "events", + "searchedLogStreams" + ] + }, + "DescribeExportTasks": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "exportTasks" + }, + "DescribeQueries": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "queries" + }, + "DescribeResourcePolicies": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "resourcePolicies" + }, + "DescribeDeliveries": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "deliveries" + }, + "DescribeDeliveryDestinations": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "deliveryDestinations" + }, + "DescribeDeliverySources": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "deliverySources" + }, + "ListAnomalies": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "anomalies" + }, + "ListLogAnomalyDetectors": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "anomalyDetectors" + }, + "DescribeConfigurationTemplates": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "configurationTemplates" + }, + "ListLogGroupsForQuery": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "logGroupIdentifiers" + }, + "GetScheduledQueryHistory": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "triggerHistory" + }, + "ListScheduledQueries": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "scheduledQueries" + }, + "ListSourcesForS3TableIntegration": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "sources" + }, + "ListAggregateLogGroupSummaries": { + "input_token": "nextToken", + "limit_key": "limit", + "output_token": "nextToken", + "result_key": "aggregateLogGroupSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/paginators-1.sdk-extras.json new file mode 100644 index 0000000..23b4966 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/paginators-1.sdk-extras.json @@ -0,0 +1,13 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetScheduledQueryHistory": { + "non_aggregate_keys": [ + "name", + "scheduledQueryArn" + ] + } + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/service-2.json.gz new file mode 100644 index 0000000..a829f8d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/logs/2014-03-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lookoutequipment/2020-12-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lookoutequipment/2020-12-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..585961e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lookoutequipment/2020-12-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/lookoutequipment/2020-12-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/lookoutequipment/2020-12-15/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lookoutequipment/2020-12-15/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lookoutequipment/2020-12-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/lookoutequipment/2020-12-15/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/lookoutequipment/2020-12-15/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/lookoutequipment/2020-12-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/lookoutequipment/2020-12-15/service-2.json.gz new file mode 100644 index 0000000..577149d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/lookoutequipment/2020-12-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/m2/2021-04-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/m2/2021-04-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..527301e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/m2/2021-04-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/m2/2021-04-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/m2/2021-04-28/paginators-1.json new file mode 100644 index 0000000..a6ccf11 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/m2/2021-04-28/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "ListApplicationVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "applicationVersions" + }, + "ListApplications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "applications" + }, + "ListBatchJobDefinitions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "batchJobDefinitions" + }, + "ListBatchJobExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "batchJobExecutions" + }, + "ListDataSetImportHistory": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dataSetImportTasks" + }, + "ListDataSets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dataSets" + }, + "ListDeployments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "deployments" + }, + "ListEngineVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "engineVersions" + }, + "ListEnvironments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "environments" + }, + "ListDataSetExportHistory": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dataSetExportTasks" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/m2/2021-04-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/m2/2021-04-28/service-2.json.gz new file mode 100644 index 0000000..bdc055c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/m2/2021-04-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..67f4f3c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/paginators-1.json new file mode 100644 index 0000000..c13ce65 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "DescribeBatchPredictions": { + "limit_key": "Limit", + "output_token": "NextToken", + "input_token": "NextToken", + "result_key": "Results" + }, + "DescribeDataSources": { + "limit_key": "Limit", + "output_token": "NextToken", + "input_token": "NextToken", + "result_key": "Results" + }, + "DescribeEvaluations": { + "limit_key": "Limit", + "output_token": "NextToken", + "input_token": "NextToken", + "result_key": "Results" + }, + "DescribeMLModels": { + "limit_key": "Limit", + "output_token": "NextToken", + "input_token": "NextToken", + "result_key": "Results" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/service-2.json.gz new file mode 100644 index 0000000..8981a95 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/waiters-2.json new file mode 100644 index 0000000..da6b1c9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/machinelearning/2014-12-12/waiters-2.json @@ -0,0 +1,81 @@ +{ + "version": 2, + "waiters": { + "DataSourceAvailable": { + "delay": 30, + "operation": "DescribeDataSources", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "COMPLETED", + "matcher": "pathAll", + "state": "success", + "argument": "Results[].Status" + }, + { + "expected": "FAILED", + "matcher": "pathAny", + "state": "failure", + "argument": "Results[].Status" + } + ] + }, + "MLModelAvailable": { + "delay": 30, + "operation": "DescribeMLModels", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "COMPLETED", + "matcher": "pathAll", + "state": "success", + "argument": "Results[].Status" + }, + { + "expected": "FAILED", + "matcher": "pathAny", + "state": "failure", + "argument": "Results[].Status" + } + ] + }, + "EvaluationAvailable": { + "delay": 30, + "operation": "DescribeEvaluations", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "COMPLETED", + "matcher": "pathAll", + "state": "success", + "argument": "Results[].Status" + }, + { + "expected": "FAILED", + "matcher": "pathAny", + "state": "failure", + "argument": "Results[].Status" + } + ] + }, + "BatchPredictionAvailable": { + "delay": 30, + "operation": "DescribeBatchPredictions", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "COMPLETED", + "matcher": "pathAll", + "state": "success", + "argument": "Results[].Status" + }, + { + "expected": "FAILED", + "matcher": "pathAny", + "state": "failure", + "argument": "Results[].Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/macie2/2020-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/macie2/2020-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..33709a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/macie2/2020-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/macie2/2020-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/macie2/2020-01-01/paginators-1.json new file mode 100644 index 0000000..8037ac7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/macie2/2020-01-01/paginators-1.json @@ -0,0 +1,106 @@ +{ + "pagination": { + "DescribeBuckets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "buckets" + }, + "GetUsageStatistics": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "records", + "non_aggregate_keys": [ + "timeRange" + ] + }, + "ListClassificationJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListCustomDataIdentifiers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListFindings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findingIds" + }, + "ListFindingsFilters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findingsFilterListItems" + }, + "ListInvitations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "invitations" + }, + "ListMembers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "members" + }, + "ListOrganizationAdminAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "adminAccounts" + }, + "SearchResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "matchingResources" + }, + "ListClassificationScopes": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "classificationScopes" + }, + "ListAllowLists": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "allowLists" + }, + "ListManagedDataIdentifiers": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "items" + }, + "ListResourceProfileDetections": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "detections" + }, + "ListSensitivityInspectionTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sensitivityInspectionTemplates" + }, + "ListResourceProfileArtifacts": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "artifacts" + }, + "ListAutomatedDiscoveryAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/macie2/2020-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/macie2/2020-01-01/service-2.json.gz new file mode 100644 index 0000000..5d80c50 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/macie2/2020-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/macie2/2020-01-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/macie2/2020-01-01/waiters-2.json new file mode 100644 index 0000000..12c4a4a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/macie2/2020-01-01/waiters-2.json @@ -0,0 +1,25 @@ +{ + "version": 2, + "waiters": { + "FindingRevealed": { + "description": "Wait until the sensitive data occurrences are ready.", + "delay": 2, + "maxAttempts": 60, + "operation": "GetSensitiveDataOccurrences", + "acceptors": [ + { + "matcher": "path", + "argument": "status", + "state": "success", + "expected": "SUCCESS" + }, + { + "matcher": "path", + "argument": "status", + "state": "success", + "expected": "ERROR" + } + ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/mailmanager/2023-10-17/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mailmanager/2023-10-17/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a4e9be6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mailmanager/2023-10-17/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mailmanager/2023-10-17/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mailmanager/2023-10-17/paginators-1.json new file mode 100644 index 0000000..a87e968 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mailmanager/2023-10-17/paginators-1.json @@ -0,0 +1,76 @@ +{ + "pagination": { + "ListAddonInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "AddonInstances" + }, + "ListAddonSubscriptions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "AddonSubscriptions" + }, + "ListArchiveExports": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "Exports" + }, + "ListArchiveSearches": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "Searches" + }, + "ListArchives": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "Archives" + }, + "ListIngressPoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "IngressPoints" + }, + "ListRelays": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "Relays" + }, + "ListRuleSets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "RuleSets" + }, + "ListTrafficPolicies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "TrafficPolicies" + }, + "ListAddressListImportJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "ImportJobs" + }, + "ListAddressLists": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "AddressLists" + }, + "ListMembersOfAddressList": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "Addresses" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mailmanager/2023-10-17/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mailmanager/2023-10-17/service-2.json.gz new file mode 100644 index 0000000..102a776 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mailmanager/2023-10-17/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mailmanager/2023-10-17/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/mailmanager/2023-10-17/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mailmanager/2023-10-17/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/managedblockchain-query/2023-05-04/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain-query/2023-05-04/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..00cfe1a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain-query/2023-05-04/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/managedblockchain-query/2023-05-04/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain-query/2023-05-04/paginators-1.json new file mode 100644 index 0000000..61eb591 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain-query/2023-05-04/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListTokenBalances": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tokenBalances" + }, + "ListTransactionEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "events" + }, + "ListTransactions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "transactions" + }, + "ListAssetContracts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "contracts" + }, + "ListFilteredTransactionEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "events" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/managedblockchain-query/2023-05-04/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain-query/2023-05-04/service-2.json.gz new file mode 100644 index 0000000..e3d48e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain-query/2023-05-04/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/managedblockchain-query/2023-05-04/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain-query/2023-05-04/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain-query/2023-05-04/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/managedblockchain/2018-09-24/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain/2018-09-24/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..65ac4b7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain/2018-09-24/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/managedblockchain/2018-09-24/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain/2018-09-24/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain/2018-09-24/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/managedblockchain/2018-09-24/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain/2018-09-24/paginators-1.json new file mode 100644 index 0000000..8d30a03 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain/2018-09-24/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListAccessors": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Accessors" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/managedblockchain/2018-09-24/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain/2018-09-24/service-2.json.gz new file mode 100644 index 0000000..140b08d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/managedblockchain/2018-09-24/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-agreement/2020-03-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplace-agreement/2020-03-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7698267 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplace-agreement/2020-03-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-agreement/2020-03-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/marketplace-agreement/2020-03-01/paginators-1.json new file mode 100644 index 0000000..875347c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplace-agreement/2020-03-01/paginators-1.json @@ -0,0 +1,52 @@ +{ + "pagination": { + "ListAgreementPaymentRequests": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAgreementCancellationRequests": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAgreementInvoiceLineItems": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agreementInvoiceLineItemGroupSummaries" + }, + "ListBillingAdjustmentRequests": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "GetAgreementEntitlements": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agreementEntitlements" + }, + "GetAgreementTerms": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "acceptedTerms" + }, + "ListAgreementCharges": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "SearchAgreements": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agreementViewSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-agreement/2020-03-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplace-agreement/2020-03-01/service-2.json.gz new file mode 100644 index 0000000..202a4f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplace-agreement/2020-03-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-agreement/2020-03-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/marketplace-agreement/2020-03-01/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplace-agreement/2020-03-01/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6a96dad Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/paginators-1.json new file mode 100644 index 0000000..7a06f63 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListChangeSets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ChangeSetSummaryList" + }, + "ListEntities": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EntitySummaryList" + }, + "DescribeAssessment": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ControlAssessments" + }, + "ListAssessments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AssessmentSummaryList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/paginators-1.sdk-extras.json new file mode 100644 index 0000000..f131c26 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/paginators-1.sdk-extras.json @@ -0,0 +1,19 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "DescribeAssessment": { + "non_aggregate_keys": [ + "ExpiresAt", + "FrameworkSummary", + "AssessmentResult", + "AssessmentTargetSummary", + "AssessmentId", + "AssessmentArn", + "FrameworkId", + "CreatedAt" + ] + } + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/service-2.json.gz new file mode 100644 index 0000000..fb22707 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplace-catalog/2018-09-17/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-deployment/2023-01-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplace-deployment/2023-01-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a281986 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplace-deployment/2023-01-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-deployment/2023-01-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/marketplace-deployment/2023-01-25/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplace-deployment/2023-01-25/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-deployment/2023-01-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplace-deployment/2023-01-25/service-2.json.gz new file mode 100644 index 0000000..01f52d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplace-deployment/2023-01-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b861e2d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/paginators-1.json new file mode 100644 index 0000000..6f9bf7d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/paginators-1.json @@ -0,0 +1,33 @@ +{ + "pagination": { + "GetOfferTerms": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "offerTerms" + }, + "ListFulfillmentOptions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "fulfillmentOptions" + }, + "ListPurchaseOptions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "purchaseOptions" + }, + "SearchFacets": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "listingFacets" + }, + "SearchListings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "listingSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/paginators-1.sdk-extras.json new file mode 100644 index 0000000..5d12f7e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/paginators-1.sdk-extras.json @@ -0,0 +1,17 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "SearchFacets": { + "non_aggregate_keys": [ + "totalResults" + ] + }, + "SearchListings": { + "non_aggregate_keys": [ + "totalResults" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/service-2.json.gz new file mode 100644 index 0000000..40a4c0f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplace-discovery/2026-02-05/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-entitlement/2017-01-11/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplace-entitlement/2017-01-11/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..9dfe758 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplace-entitlement/2017-01-11/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-entitlement/2017-01-11/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/marketplace-entitlement/2017-01-11/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplace-entitlement/2017-01-11/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-entitlement/2017-01-11/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/marketplace-entitlement/2017-01-11/paginators-1.json new file mode 100644 index 0000000..8dbf525 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplace-entitlement/2017-01-11/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "GetEntitlements": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Entitlements" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-entitlement/2017-01-11/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplace-entitlement/2017-01-11/service-2.json.gz new file mode 100644 index 0000000..42041fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplace-entitlement/2017-01-11/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-reporting/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplace-reporting/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3f57541 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplace-reporting/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-reporting/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/marketplace-reporting/2018-05-10/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplace-reporting/2018-05-10/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-reporting/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplace-reporting/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..f47a674 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplace-reporting/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplace-reporting/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/marketplace-reporting/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplace-reporting/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplacecommerceanalytics/2015-07-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplacecommerceanalytics/2015-07-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8936fac Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplacecommerceanalytics/2015-07-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplacecommerceanalytics/2015-07-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/marketplacecommerceanalytics/2015-07-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplacecommerceanalytics/2015-07-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplacecommerceanalytics/2015-07-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/marketplacecommerceanalytics/2015-07-01/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/marketplacecommerceanalytics/2015-07-01/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/marketplacecommerceanalytics/2015-07-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/marketplacecommerceanalytics/2015-07-01/service-2.json.gz new file mode 100644 index 0000000..77c4b89 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/marketplacecommerceanalytics/2015-07-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediaconnect/2018-11-14/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediaconnect/2018-11-14/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d04417c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediaconnect/2018-11-14/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediaconnect/2018-11-14/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mediaconnect/2018-11-14/paginators-1.json new file mode 100644 index 0000000..16736a6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mediaconnect/2018-11-14/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "ListFlows": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Flows" + }, + "ListEntitlements": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Entitlements" + }, + "ListOfferings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Offerings" + }, + "ListReservations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Reservations" + }, + "ListBridges": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Bridges" + }, + "ListGatewayInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Instances" + }, + "ListGateways": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Gateways" + }, + "ListRouterInputs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RouterInputs" + }, + "ListRouterNetworkInterfaces": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RouterNetworkInterfaces" + }, + "ListRouterOutputs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RouterOutputs" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediaconnect/2018-11-14/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediaconnect/2018-11-14/service-2.json.gz new file mode 100644 index 0000000..4fa6ee2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediaconnect/2018-11-14/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediaconnect/2018-11-14/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/mediaconnect/2018-11-14/waiters-2.json new file mode 100644 index 0000000..8e4007e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mediaconnect/2018-11-14/waiters-2.json @@ -0,0 +1,352 @@ +{ + "version" : 2, + "waiters" : { + "FlowActive" : { + "description" : "Wait until a flow is active", + "delay" : 3, + "maxAttempts" : 40, + "operation" : "DescribeFlow", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Flow.Status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "Flow.Status", + "state" : "retry", + "expected" : "STARTING" + }, { + "matcher" : "path", + "argument" : "Flow.Status", + "state" : "retry", + "expected" : "UPDATING" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "InternalServerErrorException" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "ServiceUnavailableException" + }, { + "matcher" : "path", + "argument" : "Flow.Status", + "state" : "failure", + "expected" : "STANDBY" + }, { + "matcher" : "path", + "argument" : "Flow.Status", + "state" : "failure", + "expected" : "ERROR" + } ] + }, + "FlowDeleted" : { + "description" : "Wait until a flow is deleted", + "delay" : 3, + "maxAttempts" : 40, + "operation" : "DescribeFlow", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "NotFoundException" + }, { + "matcher" : "path", + "argument" : "Flow.Status", + "state" : "retry", + "expected" : "DELETING" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "InternalServerErrorException" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "ServiceUnavailableException" + }, { + "matcher" : "path", + "argument" : "Flow.Status", + "state" : "failure", + "expected" : "ERROR" + } ] + }, + "FlowStandby" : { + "description" : "Wait until a flow is in standby mode", + "delay" : 3, + "maxAttempts" : 40, + "operation" : "DescribeFlow", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Flow.Status", + "state" : "success", + "expected" : "STANDBY" + }, { + "matcher" : "path", + "argument" : "Flow.Status", + "state" : "retry", + "expected" : "STOPPING" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "InternalServerErrorException" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "ServiceUnavailableException" + }, { + "matcher" : "path", + "argument" : "Flow.Status", + "state" : "failure", + "expected" : "ERROR" + } ] + }, + "InputActive" : { + "description" : "Wait until the Input is ACTIVE", + "delay" : 3, + "maxAttempts" : 40, + "operation" : "GetRouterInput", + "acceptors" : [ { + "matcher" : "path", + "argument" : "RouterInput.State", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "RouterInput.State", + "state" : "retry", + "expected" : "STARTING" + }, { + "matcher" : "path", + "argument" : "RouterInput.State", + "state" : "retry", + "expected" : "UPDATING" + }, { + "matcher" : "path", + "argument" : "RouterInput.State", + "state" : "retry", + "expected" : "MIGRATING" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "InternalServerErrorException" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "ServiceUnavailableException" + }, { + "matcher" : "path", + "argument" : "RouterInput.State", + "state" : "failure", + "expected" : "ERROR" + } ] + }, + "InputDeleted" : { + "description" : "Wait until the Input is deleted", + "delay" : 3, + "maxAttempts" : 40, + "operation" : "GetRouterInput", + "acceptors" : [ { + "matcher" : "path", + "argument" : "RouterInput.State", + "state" : "retry", + "expected" : "DELETING" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "InternalServerErrorException" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "ServiceUnavailableException" + }, { + "matcher" : "path", + "argument" : "RouterInput.State", + "state" : "failure", + "expected" : "ERROR" + }, { + "matcher" : "error", + "state" : "success", + "expected" : "NotFoundException" + } ] + }, + "InputStandby" : { + "description" : "Wait until the Input is STANDBY", + "delay" : 3, + "maxAttempts" : 40, + "operation" : "GetRouterInput", + "acceptors" : [ { + "matcher" : "path", + "argument" : "RouterInput.State", + "state" : "success", + "expected" : "STANDBY" + }, { + "matcher" : "path", + "argument" : "RouterInput.State", + "state" : "retry", + "expected" : "STOPPING" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "InternalServerErrorException" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "ServiceUnavailableException" + }, { + "matcher" : "path", + "argument" : "RouterInput.State", + "state" : "failure", + "expected" : "ERROR" + } ] + }, + "OutputActive" : { + "description" : "Wait until the Output is ACTIVE", + "delay" : 3, + "maxAttempts" : 40, + "operation" : "GetRouterOutput", + "acceptors" : [ { + "matcher" : "path", + "argument" : "RouterOutput.State", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "RouterOutput.State", + "state" : "retry", + "expected" : "STARTING" + }, { + "matcher" : "path", + "argument" : "RouterOutput.State", + "state" : "retry", + "expected" : "UPDATING" + }, { + "matcher" : "path", + "argument" : "RouterOutput.State", + "state" : "retry", + "expected" : "MIGRATING" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "InternalServerErrorException" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "ServiceUnavailableException" + }, { + "matcher" : "path", + "argument" : "RouterOutput.State", + "state" : "failure", + "expected" : "ERROR" + } ] + }, + "OutputDeleted" : { + "description" : "Wait until the Output is deleted", + "delay" : 3, + "maxAttempts" : 40, + "operation" : "GetRouterOutput", + "acceptors" : [ { + "matcher" : "path", + "argument" : "RouterOutput.State", + "state" : "retry", + "expected" : "DELETING" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "InternalServerErrorException" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "ServiceUnavailableException" + }, { + "matcher" : "path", + "argument" : "RouterOutput.State", + "state" : "failure", + "expected" : "ERROR" + }, { + "matcher" : "error", + "state" : "success", + "expected" : "NotFoundException" + } ] + }, + "OutputRouted" : { + "description" : "Wait until the Output is ROUTED", + "delay" : 3, + "maxAttempts" : 40, + "operation" : "GetRouterOutput", + "acceptors" : [ { + "matcher" : "path", + "argument" : "RouterOutput.RoutedState", + "state" : "success", + "expected" : "ROUTED" + }, { + "matcher" : "path", + "argument" : "RouterOutput.RoutedState", + "state" : "retry", + "expected" : "ROUTING" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "InternalServerErrorException" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "ServiceUnavailableException" + } ] + }, + "OutputStandby" : { + "description" : "Wait until the Output is STANDBY", + "delay" : 3, + "maxAttempts" : 40, + "operation" : "GetRouterOutput", + "acceptors" : [ { + "matcher" : "path", + "argument" : "RouterOutput.State", + "state" : "success", + "expected" : "STANDBY" + }, { + "matcher" : "path", + "argument" : "RouterOutput.State", + "state" : "retry", + "expected" : "STOPPING" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "InternalServerErrorException" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "ServiceUnavailableException" + }, { + "matcher" : "path", + "argument" : "RouterOutput.State", + "state" : "failure", + "expected" : "ERROR" + } ] + }, + "OutputUnrouted" : { + "description" : "Wait until the Output is UNROUTED", + "delay" : 3, + "maxAttempts" : 40, + "operation" : "GetRouterOutput", + "acceptors" : [ { + "matcher" : "path", + "argument" : "RouterOutput.RoutedState", + "state" : "success", + "expected" : "UNROUTED" + }, { + "matcher" : "path", + "argument" : "RouterOutput.RoutedState", + "state" : "retry", + "expected" : "ROUTING" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "InternalServerErrorException" + }, { + "matcher" : "error", + "state" : "retry", + "expected" : "ServiceUnavailableException" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediaconvert/2017-08-29/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediaconvert/2017-08-29/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6b2af59 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediaconvert/2017-08-29/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediaconvert/2017-08-29/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mediaconvert/2017-08-29/paginators-1.json new file mode 100644 index 0000000..1f93efd --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mediaconvert/2017-08-29/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "DescribeEndpoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Endpoints" + }, + "ListJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Jobs" + }, + "ListPresets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Presets" + }, + "ListJobTemplates": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "JobTemplates" + }, + "ListQueues": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Queues" + }, + "SearchJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Jobs" + }, + "ListVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Versions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediaconvert/2017-08-29/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/mediaconvert/2017-08-29/paginators-1.sdk-extras.json new file mode 100644 index 0000000..c121990 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mediaconvert/2017-08-29/paginators-1.sdk-extras.json @@ -0,0 +1,13 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListQueues": { + "non_aggregate_keys": [ + "TotalConcurrentJobs", + "UnallocatedConcurrentJobs" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediaconvert/2017-08-29/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediaconvert/2017-08-29/service-2.json.gz new file mode 100644 index 0000000..9f4766d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediaconvert/2017-08-29/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/medialive/2017-10-14/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/medialive/2017-10-14/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..5645bd6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/medialive/2017-10-14/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/medialive/2017-10-14/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/medialive/2017-10-14/paginators-1.json new file mode 100644 index 0000000..2a60014 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/medialive/2017-10-14/paginators-1.json @@ -0,0 +1,142 @@ +{ + "pagination": { + "ListInputs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Inputs" + }, + "ListChannels": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Channels" + }, + "ListInputSecurityGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InputSecurityGroups" + }, + "ListOfferings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Offerings" + }, + "ListReservations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Reservations" + }, + "DescribeSchedule": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ScheduleActions" + }, + "ListMultiplexPrograms": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "MultiplexPrograms" + }, + "ListMultiplexes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Multiplexes" + }, + "ListInputDevices": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InputDevices" + }, + "ListInputDeviceTransfers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InputDeviceTransfers" + }, + "ListSignalMaps": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SignalMaps" + }, + "ListCloudWatchAlarmTemplates": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CloudWatchAlarmTemplates" + }, + "ListCloudWatchAlarmTemplateGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CloudWatchAlarmTemplateGroups" + }, + "ListEventBridgeRuleTemplates": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EventBridgeRuleTemplates" + }, + "ListEventBridgeRuleTemplateGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EventBridgeRuleTemplateGroups" + }, + "ListNodes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Nodes" + }, + "ListClusters": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Clusters" + }, + "ListChannelPlacementGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ChannelPlacementGroups" + }, + "ListNetworks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Networks" + }, + "ListSdiSources": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SdiSources" + }, + "ListAlerts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Alerts" + }, + "ListClusterAlerts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Alerts" + }, + "ListMultiplexAlerts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Alerts" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/medialive/2017-10-14/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/medialive/2017-10-14/service-2.json.gz new file mode 100644 index 0000000..20d0049 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/medialive/2017-10-14/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/medialive/2017-10-14/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/medialive/2017-10-14/waiters-2.json new file mode 100644 index 0000000..2481f5a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/medialive/2017-10-14/waiters-2.json @@ -0,0 +1,641 @@ +{ + "version": 2, + "waiters": { + "ChannelCreated": { + "description": "Wait until a channel has been created", + "operation": "DescribeChannel", + "delay": 3, + "maxAttempts": 5, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "IDLE" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "CREATING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + }, + { + "state": "failure", + "matcher": "path", + "argument": "State", + "expected": "CREATE_FAILED" + } + ] + }, + "ChannelRunning": { + "description": "Wait until a channel is running", + "operation": "DescribeChannel", + "delay": 5, + "maxAttempts": 120, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "RUNNING" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "STARTING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "ChannelStopped": { + "description": "Wait until a channel is not running", + "operation": "DescribeChannel", + "delay": 5, + "maxAttempts": 60, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "IDLE" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "STOPPING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + }, + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "DELETING" + }, + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "DELETED" + }, + { + "state": "failure", + "matcher": "error", + "expected": "NotFoundException" + } + ] + }, + "ChannelDeleted": { + "description": "Wait until a channel has been deleted", + "operation": "DescribeChannel", + "delay": 5, + "maxAttempts": 84, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "DELETED" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "DELETING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "InputAttached": { + "description": "Wait until an input has been attached", + "operation": "DescribeInput", + "delay": 5, + "maxAttempts": 20, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "ATTACHED" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "DETACHED" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "InputDetached": { + "description": "Wait until an input has been detached", + "operation": "DescribeInput", + "delay": 5, + "maxAttempts": 84, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "DETACHED" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "CREATING" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "ATTACHED" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "InputDeleted": { + "description": "Wait until an input has been deleted", + "operation": "DescribeInput", + "delay": 5, + "maxAttempts": 20, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "DELETED" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "DELETING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "MultiplexCreated": { + "description": "Wait until a multiplex has been created", + "operation": "DescribeMultiplex", + "delay": 3, + "maxAttempts": 5, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "IDLE" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "CREATING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + }, + { + "state": "failure", + "matcher": "path", + "argument": "State", + "expected": "CREATE_FAILED" + } + ] + }, + "MultiplexRunning": { + "description": "Wait until a multiplex is running", + "operation": "DescribeMultiplex", + "delay": 5, + "maxAttempts": 120, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "RUNNING" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "STARTING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "MultiplexStopped": { + "description": "Wait until a multiplex has is stopped", + "operation": "DescribeMultiplex", + "delay": 5, + "maxAttempts": 28, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "IDLE" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "STOPPING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "MultiplexDeleted": { + "description": "Wait until a multiplex has been deleted", + "operation": "DescribeMultiplex", + "delay": 5, + "maxAttempts": 20, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "DELETED" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "DELETING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "SignalMapCreated": { + "description": "Wait until a signal map has been created", + "delay": 5, + "maxAttempts": 60, + "operation": "GetSignalMap", + "acceptors": [ + { + "matcher": "path", + "argument": "Status", + "state": "success", + "expected": "CREATE_COMPLETE" + }, + { + "matcher": "path", + "argument": "Status", + "state": "retry", + "expected": "CREATE_IN_PROGRESS" + }, + { + "matcher": "path", + "argument": "Status", + "state": "failure", + "expected": "CREATE_FAILED" + } + ] + }, + "SignalMapMonitorDeleted": { + "description": "Wait until a signal map's monitor has been deleted", + "delay": 5, + "maxAttempts": 120, + "operation": "GetSignalMap", + "acceptors": [ + { + "matcher": "path", + "argument": "MonitorDeployment.Status", + "state": "success", + "expected": "DELETE_COMPLETE" + }, + { + "matcher": "path", + "argument": "MonitorDeployment.Status", + "state": "retry", + "expected": "DELETE_IN_PROGRESS" + }, + { + "matcher": "path", + "argument": "MonitorDeployment.Status", + "state": "failure", + "expected": "DELETE_FAILED" + } + ] + }, + "SignalMapMonitorDeployed": { + "description": "Wait until a signal map's monitor has been deployed", + "delay": 5, + "maxAttempts": 120, + "operation": "GetSignalMap", + "acceptors": [ + { + "matcher": "path", + "argument": "MonitorDeployment.Status", + "state": "success", + "expected": "DRY_RUN_DEPLOYMENT_COMPLETE" + }, + { + "matcher": "path", + "argument": "MonitorDeployment.Status", + "state": "success", + "expected": "DEPLOYMENT_COMPLETE" + }, + { + "matcher": "path", + "argument": "MonitorDeployment.Status", + "state": "retry", + "expected": "DRY_RUN_DEPLOYMENT_IN_PROGRESS" + }, + { + "matcher": "path", + "argument": "MonitorDeployment.Status", + "state": "retry", + "expected": "DEPLOYMENT_IN_PROGRESS" + }, + { + "matcher": "path", + "argument": "MonitorDeployment.Status", + "state": "failure", + "expected": "DRY_RUN_DEPLOYMENT_FAILED" + }, + { + "matcher": "path", + "argument": "MonitorDeployment.Status", + "state": "failure", + "expected": "DEPLOYMENT_FAILED" + } + ] + }, + "SignalMapUpdated": { + "description": "Wait until a signal map has been updated", + "delay": 5, + "maxAttempts": 60, + "operation": "GetSignalMap", + "acceptors": [ + { + "matcher": "path", + "argument": "Status", + "state": "success", + "expected": "UPDATE_COMPLETE" + }, + { + "matcher": "path", + "argument": "Status", + "state": "retry", + "expected": "UPDATE_IN_PROGRESS" + }, + { + "matcher": "path", + "argument": "Status", + "state": "failure", + "expected": "UPDATE_FAILED" + }, + { + "matcher": "path", + "argument": "Status", + "state": "failure", + "expected": "UPDATE_REVERTED" + } + ] + }, + "ClusterCreated": { + "description": "Wait until a cluster has been created", + "operation": "DescribeCluster", + "delay": 3, + "maxAttempts": 5, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "ACTIVE" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "CREATING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + }, + { + "state": "failure", + "matcher": "path", + "argument": "State", + "expected": "CREATE_FAILED" + } + ] + }, + "ClusterDeleted": { + "description": "Wait until a cluster has been deleted", + "operation": "DescribeCluster", + "delay": 5, + "maxAttempts": 20, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "DELETED" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "DELETING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "NodeRegistered": { + "description": "Wait until a node has been registered", + "operation": "DescribeNode", + "delay": 3, + "maxAttempts": 5, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "ACTIVE" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "REGISTERING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 404 + }, + { + "state": "failure", + "matcher": "path", + "argument": "State", + "expected": "REGISTRATION_FAILED" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "NodeDeregistered": { + "description": "Wait until a node has been deregistered", + "operation": "DescribeNode", + "delay": 5, + "maxAttempts": 20, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "DEREGISTERED" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "DEREGISTERING" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "DRAINING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "ChannelPlacementGroupAssigned": { + "description": "Wait until the channel placement group has been assigned", + "operation": "DescribeChannelPlacementGroup", + "delay": 3, + "maxAttempts": 5, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "ASSIGNED" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "ASSIGNING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "ChannelPlacementGroupUnassigned": { + "description": "Wait until the channel placement group has been unassigned", + "operation": "DescribeChannelPlacementGroup", + "delay": 5, + "maxAttempts": 20, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "UNASSIGNED" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "UNASSIGNING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "ChannelPlacementGroupDeleted": { + "description": "Wait until the channel placement group has been deleted", + "operation": "DescribeChannelPlacementGroup", + "delay": 5, + "maxAttempts": 20, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "State", + "expected": "DELETED" + }, + { + "state": "retry", + "matcher": "path", + "argument": "State", + "expected": "DELETING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediapackage-vod/2018-11-07/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediapackage-vod/2018-11-07/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..398df92 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediapackage-vod/2018-11-07/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediapackage-vod/2018-11-07/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mediapackage-vod/2018-11-07/paginators-1.json new file mode 100644 index 0000000..df498b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mediapackage-vod/2018-11-07/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListAssets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Assets" + }, + "ListPackagingConfigurations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PackagingConfigurations" + }, + "ListPackagingGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PackagingGroups" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediapackage-vod/2018-11-07/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediapackage-vod/2018-11-07/service-2.json.gz new file mode 100644 index 0000000..d8741a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediapackage-vod/2018-11-07/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediapackage/2017-10-12/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediapackage/2017-10-12/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d4fa6a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediapackage/2017-10-12/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediapackage/2017-10-12/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mediapackage/2017-10-12/paginators-1.json new file mode 100644 index 0000000..24e4410 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mediapackage/2017-10-12/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListChannels": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Channels" + }, + "ListOriginEndpoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "OriginEndpoints" + }, + "ListHarvestJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "HarvestJobs" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediapackage/2017-10-12/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediapackage/2017-10-12/service-2.json.gz new file mode 100644 index 0000000..7840625 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediapackage/2017-10-12/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediapackagev2/2022-12-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediapackagev2/2022-12-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7cc69eb Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediapackagev2/2022-12-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediapackagev2/2022-12-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mediapackagev2/2022-12-25/paginators-1.json new file mode 100644 index 0000000..531aa7c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mediapackagev2/2022-12-25/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListChannelGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListChannels": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListOriginEndpoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListHarvestJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediapackagev2/2022-12-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediapackagev2/2022-12-25/service-2.json.gz new file mode 100644 index 0000000..55797c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediapackagev2/2022-12-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediapackagev2/2022-12-25/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/mediapackagev2/2022-12-25/waiters-2.json new file mode 100644 index 0000000..138579f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mediapackagev2/2022-12-25/waiters-2.json @@ -0,0 +1,36 @@ +{ + "version" : 2, + "waiters" : { + "HarvestJobFinished" : { + "delay" : 2, + "maxAttempts" : 60, + "operation" : "GetHarvestJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Status", + "state" : "success", + "expected" : "COMPLETED" + }, { + "matcher" : "path", + "argument" : "Status", + "state" : "success", + "expected" : "CANCELLED" + }, { + "matcher" : "path", + "argument" : "Status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "Status", + "state" : "retry", + "expected" : "QUEUED" + }, { + "matcher" : "path", + "argument" : "Status", + "state" : "retry", + "expected" : "IN_PROGRESS" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediastore-data/2017-09-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediastore-data/2017-09-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..35e1356 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediastore-data/2017-09-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediastore-data/2017-09-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/mediastore-data/2017-09-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mediastore-data/2017-09-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediastore-data/2017-09-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mediastore-data/2017-09-01/paginators-1.json new file mode 100644 index 0000000..7b1c0f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mediastore-data/2017-09-01/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListItems": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediastore-data/2017-09-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediastore-data/2017-09-01/service-2.json.gz new file mode 100644 index 0000000..0fd0d64 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediastore-data/2017-09-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediastore/2017-09-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediastore/2017-09-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c3770f8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediastore/2017-09-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediastore/2017-09-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/mediastore/2017-09-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mediastore/2017-09-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediastore/2017-09-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mediastore/2017-09-01/paginators-1.json new file mode 100644 index 0000000..ed3ece0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mediastore/2017-09-01/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListContainers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Containers" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediastore/2017-09-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediastore/2017-09-01/service-2.json.gz new file mode 100644 index 0000000..8d8b949 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediastore/2017-09-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediatailor/2018-04-23/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediatailor/2018-04-23/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..e9b8a11 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediatailor/2018-04-23/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediatailor/2018-04-23/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mediatailor/2018-04-23/paginators-1.json new file mode 100644 index 0000000..350cd02 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mediatailor/2018-04-23/paginators-1.json @@ -0,0 +1,58 @@ +{ + "pagination": { + "ListPlaybackConfigurations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "GetChannelSchedule": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "ListChannels": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "ListSourceLocations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "ListVodSources": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "ListAlerts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "ListPrefetchSchedules": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "ListLiveSources": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Items" + }, + "ListFunctions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mediatailor/2018-04-23/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mediatailor/2018-04-23/service-2.json.gz new file mode 100644 index 0000000..c54fc67 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mediatailor/2018-04-23/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c775b0b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/paginators-1.json new file mode 100644 index 0000000..807d052 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListDICOMImportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "jobSummaries" + }, + "ListDatastores": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "datastoreSummaries" + }, + "ListImageSetVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "imageSetPropertiesList" + }, + "SearchImageSets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "imageSetsMetadataSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/paginators-1.sdk-extras.json new file mode 100644 index 0000000..383da17 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "SearchImageSets": { + "non_aggregate_keys": [ + "sort" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/service-2.json.gz new file mode 100644 index 0000000..ef82a1b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/medical-imaging/2023-07-19/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/memorydb/2021-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/memorydb/2021-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..449150b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/memorydb/2021-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/memorydb/2021-01-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/memorydb/2021-01-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/memorydb/2021-01-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/memorydb/2021-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/memorydb/2021-01-01/paginators-1.json new file mode 100644 index 0000000..abafc00 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/memorydb/2021-01-01/paginators-1.json @@ -0,0 +1,82 @@ +{ + "pagination": { + "DescribeACLs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ACLs" + }, + "DescribeClusters": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Clusters" + }, + "DescribeEngineVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EngineVersions" + }, + "DescribeEvents": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Events" + }, + "DescribeParameterGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ParameterGroups" + }, + "DescribeParameters": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Parameters" + }, + "DescribeReservedNodes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ReservedNodes" + }, + "DescribeReservedNodesOfferings": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ReservedNodesOfferings" + }, + "DescribeServiceUpdates": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ServiceUpdates" + }, + "DescribeSnapshots": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Snapshots" + }, + "DescribeSubnetGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SubnetGroups" + }, + "DescribeUsers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Users" + }, + "DescribeMultiRegionClusters": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MultiRegionClusters" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/memorydb/2021-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/memorydb/2021-01-01/service-2.json.gz new file mode 100644 index 0000000..a195489 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/memorydb/2021-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/meteringmarketplace/2016-01-14/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/meteringmarketplace/2016-01-14/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..75c0437 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/meteringmarketplace/2016-01-14/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/meteringmarketplace/2016-01-14/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/meteringmarketplace/2016-01-14/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/meteringmarketplace/2016-01-14/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/meteringmarketplace/2016-01-14/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/meteringmarketplace/2016-01-14/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/meteringmarketplace/2016-01-14/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/meteringmarketplace/2016-01-14/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/meteringmarketplace/2016-01-14/service-2.json.gz new file mode 100644 index 0000000..80a3838 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/meteringmarketplace/2016-01-14/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mgh/2017-05-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mgh/2017-05-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cc77882 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mgh/2017-05-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mgh/2017-05-31/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/mgh/2017-05-31/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mgh/2017-05-31/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mgh/2017-05-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mgh/2017-05-31/paginators-1.json new file mode 100644 index 0000000..db029bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mgh/2017-05-31/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "ListCreatedArtifacts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CreatedArtifactList" + }, + "ListDiscoveredResources": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DiscoveredResourceList" + }, + "ListMigrationTasks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MigrationTaskSummaryList" + }, + "ListProgressUpdateStreams": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ProgressUpdateStreamSummaryList" + }, + "ListApplicationStates": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ApplicationStateList" + }, + "ListMigrationTaskUpdates": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "MigrationTaskUpdateList" + }, + "ListSourceResources": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SourceResourceList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mgh/2017-05-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mgh/2017-05-31/service-2.json.gz new file mode 100644 index 0000000..3932411 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mgh/2017-05-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c1a931a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/paginators-1.json new file mode 100644 index 0000000..6375659 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/paginators-1.json @@ -0,0 +1,178 @@ +{ + "pagination": { + "DescribeJobLogItems": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "DescribeJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "DescribeReplicationConfigurationTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "DescribeSourceServers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "DescribeVcenterClients": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "DescribeLaunchConfigurationTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListApplications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListSourceServerActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListTemplateActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListWaves": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListExportErrors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListExports": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListImportErrors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListImports": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListManagedAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListConnectors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListImportFileEnrichments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNetworkMigrationAnalyses": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNetworkMigrationAnalysisResults": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNetworkMigrationCodeGenerationSegments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNetworkMigrationCodeGenerations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNetworkMigrationDefinitions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNetworkMigrationDeployedStacks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNetworkMigrationDeployments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNetworkMigrationExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNetworkMigrationMapperSegmentConstructs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNetworkMigrationMapperSegments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNetworkMigrationMappingUpdates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListNetworkMigrationMappings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/service-2.json.gz new file mode 100644 index 0000000..9e5072d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mgn/2020-02-26/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/migration-hub-refactor-spaces/2021-10-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/migration-hub-refactor-spaces/2021-10-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ff61be8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/migration-hub-refactor-spaces/2021-10-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/migration-hub-refactor-spaces/2021-10-26/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/migration-hub-refactor-spaces/2021-10-26/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/migration-hub-refactor-spaces/2021-10-26/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/migration-hub-refactor-spaces/2021-10-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/migration-hub-refactor-spaces/2021-10-26/paginators-1.json new file mode 100644 index 0000000..79ae0ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/migration-hub-refactor-spaces/2021-10-26/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListApplications": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ApplicationSummaryList" + }, + "ListEnvironmentVpcs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EnvironmentVpcList" + }, + "ListEnvironments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EnvironmentSummaryList" + }, + "ListRoutes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RouteSummaryList" + }, + "ListServices": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ServiceSummaryList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/migration-hub-refactor-spaces/2021-10-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/migration-hub-refactor-spaces/2021-10-26/service-2.json.gz new file mode 100644 index 0000000..2ef0bfa Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/migration-hub-refactor-spaces/2021-10-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/migrationhub-config/2019-06-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/migrationhub-config/2019-06-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..209f049 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/migrationhub-config/2019-06-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/migrationhub-config/2019-06-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/migrationhub-config/2019-06-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/migrationhub-config/2019-06-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/migrationhub-config/2019-06-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/migrationhub-config/2019-06-30/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/migrationhub-config/2019-06-30/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/migrationhub-config/2019-06-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/migrationhub-config/2019-06-30/service-2.json.gz new file mode 100644 index 0000000..53c7f48 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/migrationhub-config/2019-06-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/migrationhuborchestrator/2021-08-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/migrationhuborchestrator/2021-08-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d3dd781 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/migrationhuborchestrator/2021-08-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/migrationhuborchestrator/2021-08-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/migrationhuborchestrator/2021-08-28/paginators-1.json new file mode 100644 index 0000000..4c45222 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/migrationhuborchestrator/2021-08-28/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "ListPlugins": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "plugins" + }, + "ListTemplateStepGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "templateStepGroupSummary" + }, + "ListTemplateSteps": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "templateStepSummaryList" + }, + "ListTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "templateSummary" + }, + "ListWorkflowStepGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workflowStepGroupsSummary" + }, + "ListWorkflowSteps": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workflowStepsSummary" + }, + "ListWorkflows": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "migrationWorkflowSummary" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/migrationhuborchestrator/2021-08-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/migrationhuborchestrator/2021-08-28/service-2.json.gz new file mode 100644 index 0000000..08edd33 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/migrationhuborchestrator/2021-08-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/migrationhuborchestrator/2021-08-28/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/migrationhuborchestrator/2021-08-28/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/migrationhuborchestrator/2021-08-28/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8aaae11 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/paginators-1.json new file mode 100644 index 0000000..889a45e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "GetServerDetails": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "associatedApplications" + }, + "ListApplicationComponents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "applicationComponentInfos" + }, + "ListCollectors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "Collectors" + }, + "ListImportFileTask": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "taskInfos" + }, + "ListServers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "serverInfos" + }, + "ListAnalyzableServers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "analyzableServers" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/paginators-1.sdk-extras.json new file mode 100644 index 0000000..2524463 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetServerDetails": { + "non_aggregate_keys": [ + "serverDetail" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/service-2.json.gz new file mode 100644 index 0000000..824bd09 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/migrationhubstrategy/2020-02-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mpa/2022-07-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mpa/2022-07-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6b92f5e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mpa/2022-07-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mpa/2022-07-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mpa/2022-07-26/paginators-1.json new file mode 100644 index 0000000..f3f8b1b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mpa/2022-07-26/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListApprovalTeams": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ApprovalTeams" + }, + "ListIdentitySources": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "IdentitySources" + }, + "ListPolicies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Policies" + }, + "ListPolicyVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PolicyVersions" + }, + "ListResourcePolicies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResourcePolicies" + }, + "ListSessions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Sessions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mpa/2022-07-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mpa/2022-07-26/service-2.json.gz new file mode 100644 index 0000000..d575781 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mpa/2022-07-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mpa/2022-07-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/mpa/2022-07-26/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mpa/2022-07-26/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mq/2017-11-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mq/2017-11-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a4152fd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mq/2017-11-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mq/2017-11-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mq/2017-11-27/paginators-1.json new file mode 100644 index 0000000..55eac44 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mq/2017-11-27/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListBrokers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "BrokerSummaries" + }, + "DescribeSharedResources": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SharedResources" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mq/2017-11-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mq/2017-11-27/service-2.json.gz new file mode 100644 index 0000000..899bbad Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mq/2017-11-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mturk/2017-01-17/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mturk/2017-01-17/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..9b5f5eb Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mturk/2017-01-17/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mturk/2017-01-17/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/mturk/2017-01-17/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mturk/2017-01-17/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mturk/2017-01-17/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mturk/2017-01-17/paginators-1.json new file mode 100644 index 0000000..ea50cac --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mturk/2017-01-17/paginators-1.json @@ -0,0 +1,58 @@ +{ + "pagination": { + "ListAssignmentsForHIT": { + "result_key": "Assignments", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListQualificationTypes": { + "result_key": "QualificationTypes", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListHITs": { + "result_key": "HITs", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListWorkerBlocks": { + "result_key": "WorkerBlocks", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListReviewableHITs": { + "result_key": "HITs", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListHITsForQualificationType": { + "result_key": "HITs", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListQualificationRequests": { + "result_key": "QualificationRequests", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListWorkersWithQualificationType": { + "result_key": "Qualifications", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListBonusPayments": { + "result_key": "BonusPayments", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mturk/2017-01-17/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mturk/2017-01-17/service-2.json.gz new file mode 100644 index 0000000..55c0fd2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mturk/2017-01-17/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mwaa-serverless/2024-07-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mwaa-serverless/2024-07-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..159352d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mwaa-serverless/2024-07-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mwaa-serverless/2024-07-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mwaa-serverless/2024-07-26/paginators-1.json new file mode 100644 index 0000000..27dae07 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mwaa-serverless/2024-07-26/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListTaskInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TaskInstances" + }, + "ListWorkflowRuns": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "WorkflowRuns" + }, + "ListWorkflowVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "WorkflowVersions" + }, + "ListWorkflows": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Workflows" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mwaa-serverless/2024-07-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mwaa-serverless/2024-07-26/service-2.json.gz new file mode 100644 index 0000000..c19d242 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mwaa-serverless/2024-07-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mwaa-serverless/2024-07-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/mwaa-serverless/2024-07-26/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mwaa-serverless/2024-07-26/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mwaa/2020-07-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mwaa/2020-07-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b58b27a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mwaa/2020-07-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/mwaa/2020-07-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/mwaa/2020-07-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mwaa/2020-07-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mwaa/2020-07-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/mwaa/2020-07-01/paginators-1.json new file mode 100644 index 0000000..5e218e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/mwaa/2020-07-01/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListEnvironments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Environments" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/mwaa/2020-07-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/mwaa/2020-07-01/service-2.json.gz new file mode 100644 index 0000000..ba7205c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/mwaa/2020-07-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/neptune-graph/2023-11-29/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/neptune-graph/2023-11-29/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1a86b11 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/neptune-graph/2023-11-29/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/neptune-graph/2023-11-29/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/neptune-graph/2023-11-29/paginators-1.json new file mode 100644 index 0000000..7ce2e6d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/neptune-graph/2023-11-29/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListGraphSnapshots": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "graphSnapshots" + }, + "ListGraphs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "graphs" + }, + "ListImportTasks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tasks" + }, + "ListPrivateGraphEndpoints": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "privateGraphEndpoints" + }, + "ListExportTasks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tasks" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/neptune-graph/2023-11-29/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/neptune-graph/2023-11-29/service-2.json.gz new file mode 100644 index 0000000..0c55f28 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/neptune-graph/2023-11-29/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/neptune-graph/2023-11-29/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/neptune-graph/2023-11-29/waiters-2.json new file mode 100644 index 0000000..b3b59fb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/neptune-graph/2023-11-29/waiters-2.json @@ -0,0 +1,229 @@ +{ + "version" : 2, + "waiters" : { + "ExportTaskCancelled" : { + "description" : "Wait until Export Task is Cancelled", + "delay" : 60, + "maxAttempts" : 60, + "operation" : "GetExportTask", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status != 'CANCELLING' && status != 'CANCELLED'", + "state" : "failure", + "expected" : true + }, { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "CANCELLED" + } ] + }, + "ExportTaskSuccessful" : { + "description" : "Wait until Export Task is Successful", + "delay" : 60, + "maxAttempts" : 480, + "operation" : "GetExportTask", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CANCELLING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CANCELLED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "SUCCEEDED" + } ] + }, + "GraphAvailable" : { + "description" : "Wait until Graph is Available", + "delay" : 60, + "maxAttempts" : 480, + "operation" : "GetGraph", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "AVAILABLE" + } ] + }, + "GraphDeleted" : { + "description" : "Wait until Graph is Deleted", + "delay" : 60, + "maxAttempts" : 60, + "operation" : "GetGraph", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status != 'DELETING'", + "state" : "failure", + "expected" : true + }, { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + } ] + }, + "GraphSnapshotAvailable" : { + "description" : "Wait until GraphSnapshot is Available", + "delay" : 60, + "maxAttempts" : 120, + "operation" : "GetGraphSnapshot", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "AVAILABLE" + } ] + }, + "GraphSnapshotDeleted" : { + "description" : "Wait until GraphSnapshot is Deleted", + "delay" : 60, + "maxAttempts" : 60, + "operation" : "GetGraphSnapshot", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status != 'DELETING'", + "state" : "failure", + "expected" : true + }, { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + } ] + }, + "GraphStopped" : { + "description" : "Wait until Graph is Stopped", + "delay" : 20, + "maxAttempts" : 90, + "operation" : "GetGraph", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "STOPPED" + }, { + "matcher" : "path", + "argument" : "status != 'STOPPING'", + "state" : "failure", + "expected" : true + } ] + }, + "ImportTaskCancelled" : { + "description" : "Wait until Import Task is Cancelled", + "delay" : 60, + "maxAttempts" : 60, + "operation" : "GetImportTask", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status != 'CANCELLING' && status != 'CANCELLED'", + "state" : "failure", + "expected" : true + }, { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "CANCELLED" + } ] + }, + "ImportTaskSuccessful" : { + "description" : "Wait until Import Task is Successful", + "delay" : 60, + "maxAttempts" : 480, + "operation" : "GetImportTask", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CANCELLING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CANCELLED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "ROLLING_BACK" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "SUCCEEDED" + } ] + }, + "PrivateGraphEndpointAvailable" : { + "description" : "Wait until PrivateGraphEndpoint is Available", + "delay" : 10, + "maxAttempts" : 180, + "operation" : "GetPrivateGraphEndpoint", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "AVAILABLE" + } ] + }, + "PrivateGraphEndpointDeleted" : { + "description" : "Wait until PrivateGraphEndpoint is Deleted", + "delay" : 10, + "maxAttempts" : 180, + "operation" : "GetPrivateGraphEndpoint", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status != 'DELETING'", + "state" : "failure", + "expected" : true + }, { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..bab9cec Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/paginators-1.json new file mode 100644 index 0000000..1de1303 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/paginators-1.json @@ -0,0 +1,100 @@ +{ + "pagination": { + "DescribeDBEngineVersions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBEngineVersions" + }, + "DescribeDBInstances": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBInstances" + }, + "DescribeDBParameterGroups": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBParameterGroups" + }, + "DescribeDBParameters": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "Parameters" + }, + "DescribeDBSubnetGroups": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBSubnetGroups" + }, + "DescribeEngineDefaultParameters": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "EngineDefaults.Marker", + "result_key": "EngineDefaults.Parameters" + }, + "DescribeEventSubscriptions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "EventSubscriptionsList" + }, + "DescribeEvents": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "Events" + }, + "DescribeOrderableDBInstanceOptions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "OrderableDBInstanceOptions" + }, + "DescribeDBClusterParameterGroups": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBClusterParameterGroups" + }, + "DescribeDBClusterParameters": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "Parameters" + }, + "DescribeDBClusterSnapshots": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBClusterSnapshots" + }, + "DescribeDBClusters": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBClusters" + }, + "DescribePendingMaintenanceActions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "PendingMaintenanceActions" + }, + "DescribeDBClusterEndpoints": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBClusterEndpoints" + }, + "DescribeGlobalClusters": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "GlobalClusters" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/service-2.json.gz new file mode 100644 index 0000000..00cb118 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/service-2.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/service-2.sdk-extras.json new file mode 100644 index 0000000..85e8a10 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/service-2.sdk-extras.json @@ -0,0 +1,23 @@ + { + "version": 1.0, + "merge": { + "shapes": { + "CopyDBClusterSnapshotMessage": { + "members": { + "SourceRegion": { + "shape": "String", + "documentation": "

The ID of the region that contains the snapshot to be copied.

" + } + } + }, + "CreateDBClusterMessage": { + "members": { + "SourceRegion": { + "shape": "String", + "documentation": "

The ID of the region that contains the source for the db cluster.

" + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/waiters-2.json new file mode 100644 index 0000000..e75f03b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/neptune/2014-10-31/waiters-2.json @@ -0,0 +1,90 @@ +{ + "version": 2, + "waiters": { + "DBInstanceAvailable": { + "delay": 30, + "operation": "DescribeDBInstances", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "failed", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "incompatible-restore", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "incompatible-parameters", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + } + ] + }, + "DBInstanceDeleted": { + "delay": 30, + "operation": "DescribeDBInstances", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "DBInstanceNotFound", + "matcher": "error", + "state": "success" + }, + { + "expected": "creating", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "modifying", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "rebooting", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "resetting-master-credentials", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/neptunedata/2023-08-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/neptunedata/2023-08-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a33786e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/neptunedata/2023-08-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/neptunedata/2023-08-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/neptunedata/2023-08-01/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/neptunedata/2023-08-01/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/neptunedata/2023-08-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/neptunedata/2023-08-01/service-2.json.gz new file mode 100644 index 0000000..9b82159 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/neptunedata/2023-08-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4f871d5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/paginators-1.json new file mode 100644 index 0000000..211a11a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/paginators-1.json @@ -0,0 +1,88 @@ +{ + "pagination": { + "ListFirewallPolicies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FirewallPolicies" + }, + "ListFirewalls": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Firewalls" + }, + "ListRuleGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RuleGroups" + }, + "ListTagsForResource": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Tags" + }, + "ListTLSInspectionConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TLSInspectionConfigurations" + }, + "GetAnalysisReportResults": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AnalysisReportResults" + }, + "ListAnalysisReports": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AnalysisReports" + }, + "ListFlowOperationResults": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Flows" + }, + "ListFlowOperations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FlowOperations" + }, + "ListVpcEndpointAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VpcEndpointAssociations" + }, + "ListProxies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Proxies" + }, + "ListProxyConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ProxyConfigurations" + }, + "ListProxyRuleGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ProxyRuleGroups" + }, + "ListContainerAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ContainerAssociations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/paginators-1.sdk-extras.json new file mode 100644 index 0000000..a0bc4ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/paginators-1.sdk-extras.json @@ -0,0 +1,28 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetAnalysisReportResults": { + "non_aggregate_keys": [ + "EndTime", + "ReportTime", + "Status", + "AnalysisType", + "StartTime" + ] + }, + "ListFlowOperationResults": { + "non_aggregate_keys": [ + "FirewallArn", + "FlowOperationId", + "AvailabilityZone", + "FlowOperationStatus", + "FlowRequestTimestamp", + "StatusMessage", + "VpcEndpointAssociationArn", + "VpcEndpointId" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/service-2.json.gz new file mode 100644 index 0000000..0d4d205 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/network-firewall/2020-11-12/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/network-security-manager/2025-10-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/network-security-manager/2025-10-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cac8ec3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/network-security-manager/2025-10-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/network-security-manager/2025-10-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/network-security-manager/2025-10-30/paginators-1.json new file mode 100644 index 0000000..663914f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/network-security-manager/2025-10-30/paginators-1.json @@ -0,0 +1,88 @@ +{ + "pagination": { + "ListAdminAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "adminAccounts" + }, + "ListAggregateResourceSynchronizationStatuses": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "resourceSynchronizationStatuses" + }, + "ListDeploymentSnapshots": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "snapshots" + }, + "ListDeployments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "deployments" + }, + "ListPolicies": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policies" + }, + "ListPolicySnapshots": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "snapshots" + }, + "ListResourceAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "resourceAssociations" + }, + "ListResourceSynchronizationStatuses": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "resourceSynchronizationStatuses" + }, + "ListRuleSnapshots": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "snapshots" + }, + "ListRules": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "rules" + }, + "ListScopeSnapshots": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "snapshots" + }, + "ListScopes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "scopes" + }, + "ListTemplateSnapshots": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "snapshots" + }, + "ListTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "templates" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/network-security-manager/2025-10-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/network-security-manager/2025-10-30/service-2.json.gz new file mode 100644 index 0000000..deb871b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/network-security-manager/2025-10-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/network-security-manager/2025-10-30/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/network-security-manager/2025-10-30/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/network-security-manager/2025-10-30/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..bc44864 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/paginators-1.json new file mode 100644 index 0000000..b41617d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "GetQueryResultsMonitorTopContributors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "topContributors" + }, + "GetQueryResultsWorkloadInsightsTopContributors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "topContributors" + }, + "GetQueryResultsWorkloadInsightsTopContributorsData": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "datapoints" + }, + "ListMonitors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "monitors" + }, + "ListScopes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "scopes" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/paginators-1.sdk-extras.json new file mode 100644 index 0000000..8fe3188 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/paginators-1.sdk-extras.json @@ -0,0 +1,17 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetQueryResultsMonitorTopContributors": { + "non_aggregate_keys": [ + "unit" + ] + }, + "GetQueryResultsWorkloadInsightsTopContributorsData": { + "non_aggregate_keys": [ + "unit" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/service-2.json.gz new file mode 100644 index 0000000..9784d65 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/networkflowmonitor/2023-04-19/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/networkmanager/2019-07-05/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/networkmanager/2019-07-05/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1bc95ab Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/networkmanager/2019-07-05/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/networkmanager/2019-07-05/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/networkmanager/2019-07-05/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/networkmanager/2019-07-05/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/networkmanager/2019-07-05/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/networkmanager/2019-07-05/paginators-1.json new file mode 100644 index 0000000..b93e201 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/networkmanager/2019-07-05/paginators-1.json @@ -0,0 +1,148 @@ +{ + "pagination": { + "DescribeGlobalNetworks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "GlobalNetworks" + }, + "GetCustomerGatewayAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CustomerGatewayAssociations" + }, + "GetDevices": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Devices" + }, + "GetLinkAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "LinkAssociations" + }, + "GetLinks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Links" + }, + "GetSites": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Sites" + }, + "GetTransitGatewayRegistrations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TransitGatewayRegistrations" + }, + "GetConnections": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Connections" + }, + "GetTransitGatewayConnectPeerAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TransitGatewayConnectPeerAssociations" + }, + "GetNetworkResourceCounts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "NetworkResourceCounts" + }, + "GetNetworkResourceRelationships": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Relationships" + }, + "GetNetworkResources": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "NetworkResources" + }, + "GetNetworkTelemetry": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "NetworkTelemetry" + }, + "GetConnectPeerAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ConnectPeerAssociations" + }, + "GetCoreNetworkChangeSet": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CoreNetworkChanges" + }, + "ListAttachments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Attachments" + }, + "ListConnectPeers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ConnectPeers" + }, + "ListCoreNetworkPolicyVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CoreNetworkPolicyVersions" + }, + "ListCoreNetworks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CoreNetworks" + }, + "GetCoreNetworkChangeEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CoreNetworkChangeEvents" + }, + "ListPeerings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Peerings" + }, + "ListAttachmentRoutingPolicyAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AttachmentRoutingPolicyAssociations" + }, + "ListCoreNetworkPrefixListAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PrefixListAssociations" + }, + "ListCoreNetworkRoutingInformation": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CoreNetworkRoutingInformation" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/networkmanager/2019-07-05/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/networkmanager/2019-07-05/service-2.json.gz new file mode 100644 index 0000000..d79d2e5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/networkmanager/2019-07-05/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/networkmonitor/2023-08-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/networkmonitor/2023-08-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..e662ab5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/networkmonitor/2023-08-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/networkmonitor/2023-08-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/networkmonitor/2023-08-01/paginators-1.json new file mode 100644 index 0000000..d885a0f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/networkmonitor/2023-08-01/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListMonitors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "monitors" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/networkmonitor/2023-08-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/networkmonitor/2023-08-01/service-2.json.gz new file mode 100644 index 0000000..59de0b3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/networkmonitor/2023-08-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/networkmonitor/2023-08-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/networkmonitor/2023-08-01/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/networkmonitor/2023-08-01/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/notifications/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/notifications/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b2826ba Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/notifications/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/notifications/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/notifications/2018-05-10/paginators-1.json new file mode 100644 index 0000000..b167aaa --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/notifications/2018-05-10/paginators-1.json @@ -0,0 +1,70 @@ +{ + "pagination": { + "ListChannels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "channels" + }, + "ListEventRules": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "eventRules" + }, + "ListNotificationConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "notificationConfigurations" + }, + "ListNotificationEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "notificationEvents" + }, + "ListNotificationHubs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "notificationHubs" + }, + "ListManagedNotificationChannelAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "channelAssociations" + }, + "ListManagedNotificationChildEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "managedNotificationChildEvents" + }, + "ListManagedNotificationConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "managedNotificationConfigurations" + }, + "ListManagedNotificationEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "managedNotificationEvents" + }, + "ListMemberAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "memberAccounts" + }, + "ListOrganizationalUnits": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "organizationalUnits" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/notifications/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/notifications/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..803a4af Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/notifications/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/notifications/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/notifications/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/notifications/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/notificationscontacts/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/notificationscontacts/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..254c568 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/notificationscontacts/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/notificationscontacts/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/notificationscontacts/2018-05-10/paginators-1.json new file mode 100644 index 0000000..39f96a3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/notificationscontacts/2018-05-10/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListEmailContacts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "emailContacts" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/notificationscontacts/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/notificationscontacts/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..d34f735 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/notificationscontacts/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/notificationscontacts/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/notificationscontacts/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/notificationscontacts/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/nova-act/2025-08-22/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/nova-act/2025-08-22/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cb0d9e5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/nova-act/2025-08-22/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/nova-act/2025-08-22/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/nova-act/2025-08-22/paginators-1.json new file mode 100644 index 0000000..d50ded2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/nova-act/2025-08-22/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListActs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "actSummaries" + }, + "ListSessions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sessionSummaries" + }, + "ListWorkflowDefinitions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workflowDefinitionSummaries" + }, + "ListWorkflowRuns": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workflowRunSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/nova-act/2025-08-22/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/nova-act/2025-08-22/service-2.json.gz new file mode 100644 index 0000000..626f52b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/nova-act/2025-08-22/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/nova-act/2025-08-22/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/nova-act/2025-08-22/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/nova-act/2025-08-22/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/oam/2022-06-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/oam/2022-06-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f3a751d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/oam/2022-06-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/oam/2022-06-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/oam/2022-06-10/paginators-1.json new file mode 100644 index 0000000..3595f00 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/oam/2022-06-10/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListAttachedLinks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListLinks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + }, + "ListSinks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/oam/2022-06-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/oam/2022-06-10/service-2.json.gz new file mode 100644 index 0000000..43174a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/oam/2022-06-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/observabilityadmin/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/observabilityadmin/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..bb64aeb Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/observabilityadmin/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/observabilityadmin/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/observabilityadmin/2018-05-10/paginators-1.json new file mode 100644 index 0000000..593cf1d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/observabilityadmin/2018-05-10/paginators-1.json @@ -0,0 +1,52 @@ +{ + "pagination": { + "ListResourceTelemetry": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TelemetryConfigurations" + }, + "ListResourceTelemetryForOrganization": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TelemetryConfigurations" + }, + "ListTelemetryRules": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TelemetryRuleSummaries" + }, + "ListTelemetryRulesForOrganization": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TelemetryRuleSummaries" + }, + "ListCentralizationRulesForOrganization": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CentralizationRuleSummaries" + }, + "ListS3TableIntegrations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "IntegrationSummaries" + }, + "ListTelemetryPipelines": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PipelineSummaries" + }, + "ListDatasetIntegrations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DatasetIntegrationSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/observabilityadmin/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/observabilityadmin/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..15cc201 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/observabilityadmin/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/observabilityadmin/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/observabilityadmin/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/observabilityadmin/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/odb/2024-08-20/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/odb/2024-08-20/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1d27457 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/odb/2024-08-20/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/odb/2024-08-20/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/odb/2024-08-20/paginators-1.json new file mode 100644 index 0000000..2216866 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/odb/2024-08-20/paginators-1.json @@ -0,0 +1,130 @@ +{ + "pagination": { + "ListAutonomousVirtualMachines": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "autonomousVirtualMachines" + }, + "ListCloudAutonomousVmClusters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "cloudAutonomousVmClusters" + }, + "ListCloudExadataInfrastructures": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "cloudExadataInfrastructures" + }, + "ListCloudVmClusters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "cloudVmClusters" + }, + "ListDbNodes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dbNodes" + }, + "ListDbServers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dbServers" + }, + "ListDbSystemShapes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dbSystemShapes" + }, + "ListGiVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "giVersions" + }, + "ListOdbNetworks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "odbNetworks" + }, + "ListOdbPeeringConnections": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "odbPeeringConnections" + }, + "ListSystemVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "systemVersions" + }, + "ListAutonomousDatabaseBackups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "autonomousDatabaseBackups" + }, + "ListAutonomousDatabaseCharacterSets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "autonomousDatabaseCharacterSets" + }, + "ListAutonomousDatabaseClones": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "autonomousDatabaseClones" + }, + "ListAutonomousDatabasePeers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "autonomousDatabasePeers" + }, + "ListAutonomousDatabaseVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "autonomousDatabaseVersions" + }, + "ListAutonomousDatabases": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "autonomousDatabases" + }, + "ListExadbVmClusters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "exadbVmClusters" + }, + "ListExascaleDbStorageVaults": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "exascaleDbStorageVaults" + }, + "ListGiMinorVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "giMinorVersions" + }, + "ListFlexComponents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "flexComponents" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/odb/2024-08-20/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/odb/2024-08-20/service-2.json.gz new file mode 100644 index 0000000..b74293e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/odb/2024-08-20/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/odb/2024-08-20/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/odb/2024-08-20/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/odb/2024-08-20/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/omics/2022-11-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/omics/2022-11-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f58997a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/omics/2022-11-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/omics/2022-11-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/omics/2022-11-28/paginators-1.json new file mode 100644 index 0000000..bf4852e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/omics/2022-11-28/paginators-1.json @@ -0,0 +1,154 @@ +{ + "pagination": { + "ListAnnotationImportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "annotationImportJobs" + }, + "ListAnnotationStores": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "annotationStores" + }, + "ListReadSetActivationJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "activationJobs" + }, + "ListReadSetExportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "exportJobs" + }, + "ListReadSetImportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "importJobs" + }, + "ListReadSets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "readSets" + }, + "ListReferenceImportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "importJobs" + }, + "ListReferenceStores": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "referenceStores" + }, + "ListReferences": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "references" + }, + "ListRunGroups": { + "input_token": "startingToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListRunTasks": { + "input_token": "startingToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListRuns": { + "input_token": "startingToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListSequenceStores": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sequenceStores" + }, + "ListVariantImportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "variantImportJobs" + }, + "ListVariantStores": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "variantStores" + }, + "ListWorkflows": { + "input_token": "startingToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListMultipartReadSetUploads": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "uploads" + }, + "ListReadSetUploadParts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "parts" + }, + "ListAnnotationStoreVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "annotationStoreVersions" + }, + "ListShares": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "shares" + }, + "ListRunCaches": { + "input_token": "startingToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListWorkflowVersions": { + "input_token": "startingToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListBatch": { + "input_token": "startingToken", + "output_token": "nextToken", + "limit_key": "maxItems", + "result_key": "items" + }, + "ListRunsInBatch": { + "input_token": "startingToken", + "output_token": "nextToken", + "limit_key": "maxItems", + "result_key": "runs" + }, + "ListConfigurations": { + "input_token": "startingToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/omics/2022-11-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/omics/2022-11-28/service-2.json.gz new file mode 100644 index 0000000..7466837 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/omics/2022-11-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/omics/2022-11-28/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/omics/2022-11-28/waiters-2.json new file mode 100644 index 0000000..c11adf7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/omics/2022-11-28/waiters-2.json @@ -0,0 +1,573 @@ +{ + "version" : 2, + "waiters" : { + "AnnotationImportJobCreated" : { + "description" : "Wait until an annotation import is completed", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetAnnotationImportJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "SUBMITTED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "IN_PROGRESS" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "CANCELLED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "COMPLETED" + } ] + }, + "AnnotationStoreCreated" : { + "description" : "Wait until an annotation store is created", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetAnnotationStore", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "CREATING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "UPDATING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "AnnotationStoreDeleted" : { + "description" : "Wait until an annotation store is deleted.", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetAnnotationStore", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "DELETED" + }, { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "DELETING" + } ] + }, + "AnnotationStoreVersionCreated" : { + "description" : "Wait until an annotation store version is created", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetAnnotationStoreVersion", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "CREATING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "UPDATING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "AnnotationStoreVersionDeleted" : { + "description" : "Wait until an annotation store version is deleted.", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetAnnotationStoreVersion", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "DELETED" + }, { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "DELETING" + } ] + }, + "ReadSetActivationJobCompleted" : { + "description" : "Wait until a job is completed.", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetReadSetActivationJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "COMPLETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "SUBMITTED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "IN_PROGRESS" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "CANCELLING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CANCELLED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "COMPLETED_WITH_FAILURES" + } ] + }, + "ReadSetExportJobCompleted" : { + "description" : "Wait until a job is completed.", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetReadSetExportJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "COMPLETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "SUBMITTED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "IN_PROGRESS" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "CANCELLING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CANCELLED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "COMPLETED_WITH_FAILURES" + } ] + }, + "ReadSetImportJobCompleted" : { + "description" : "Wait until a job is completed.", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetReadSetImportJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "COMPLETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "SUBMITTED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "IN_PROGRESS" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "CANCELLING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CANCELLED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "COMPLETED_WITH_FAILURES" + } ] + }, + "ReferenceImportJobCompleted" : { + "description" : "Wait until a job is completed.", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetReferenceImportJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "COMPLETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "SUBMITTED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "IN_PROGRESS" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "CANCELLING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CANCELLED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "COMPLETED_WITH_FAILURES" + } ] + }, + "RunCompleted" : { + "description" : "Wait until a run is completed.", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetRun", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "COMPLETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "PENDING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "STARTING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "RUNNING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "STOPPING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "RunRunning" : { + "description" : "Wait until a run is running.", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetRun", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "RUNNING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "PENDING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "STARTING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CANCELLED" + } ] + }, + "TaskCompleted" : { + "description" : "Wait until a task is completed.", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetRunTask", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "COMPLETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "PENDING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "STARTING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "RUNNING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "STOPPING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "TaskRunning" : { + "description" : "Wait until a task is running.", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetRunTask", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "RUNNING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "PENDING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "STARTING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CANCELLED" + } ] + }, + "VariantImportJobCreated" : { + "description" : "Wait until variant import is completed", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetVariantImportJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "SUBMITTED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "IN_PROGRESS" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "CANCELLED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "COMPLETED" + } ] + }, + "VariantStoreCreated" : { + "description" : "Wait until a variant store is created", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetVariantStore", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "CREATING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "UPDATING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "VariantStoreDeleted" : { + "description" : "Wait until a variant store is deleted.", + "delay" : 30, + "maxAttempts" : 20, + "operation" : "GetVariantStore", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "DELETED" + }, { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "DELETING" + } ] + }, + "WorkflowActive" : { + "description" : "Wait until a workflow is active.", + "delay" : 3, + "maxAttempts" : 10, + "operation" : "GetWorkflow", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "CREATING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "UPDATING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "WorkflowVersionActive" : { + "description" : "Wait until a workflow version is active.", + "delay" : 3, + "maxAttempts" : 10, + "operation" : "GetWorkflowVersion", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "CREATING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "UPDATING" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/opensearch/2021-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/opensearch/2021-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c03a35a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/opensearch/2021-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/opensearch/2021-01-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/opensearch/2021-01-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/opensearch/2021-01-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/opensearch/2021-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/opensearch/2021-01-01/paginators-1.json new file mode 100644 index 0000000..1f134cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/opensearch/2021-01-01/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListApplications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "ApplicationSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/opensearch/2021-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/opensearch/2021-01-01/service-2.json.gz new file mode 100644 index 0000000..3a00107 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/opensearch/2021-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/opensearchserverless/2021-11-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/opensearchserverless/2021-11-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..49e8228 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/opensearchserverless/2021-11-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/opensearchserverless/2021-11-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/opensearchserverless/2021-11-01/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/opensearchserverless/2021-11-01/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/opensearchserverless/2021-11-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/opensearchserverless/2021-11-01/service-2.json.gz new file mode 100644 index 0000000..629f4cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/opensearchserverless/2021-11-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/opensearchserverless/2021-11-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/opensearchserverless/2021-11-01/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/opensearchserverless/2021-11-01/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..eabccfd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/examples-1.json new file mode 100644 index 0000000..8e39290 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/examples-1.json @@ -0,0 +1,1409 @@ +{ + "version": "1.0", + "examples": { + "AcceptHandshake": [ + { + "input": { + "HandshakeId": "h-examplehandshakeid111" + }, + "output": { + "Handshake": { + "Action": "INVITE", + "Arn": "arn:aws:organizations::111111111111:handshake/o-exampleorgid/invite/h-examplehandshakeid111", + "ExpirationTimestamp": "20170228T1215Z", + "Id": "h-examplehandshakeid111", + "Parties": [ + { + "Id": "o-exampleorgid", + "Type": "ORGANIZATION" + }, + { + "Id": "juan@example.com", + "Type": "EMAIL" + } + ], + "RequestedTimestamp": "20170214T1215Z", + "Resources": [ + { + "Resources": [ + { + "Type": "MASTER_EMAIL", + "Value": "bill@amazon.com" + }, + { + "Type": "MASTER_NAME", + "Value": "Org Master Account" + }, + { + "Type": "ORGANIZATION_FEATURE_SET", + "Value": "ALL" + } + ], + "Type": "ORGANIZATION", + "Value": "o-exampleorgid" + }, + { + "Type": "ACCOUNT", + "Value": "222222222222" + } + ], + "State": "ACCEPTED" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Bill is the owner of an organization, and he invites Juan's account (222222222222) to join his organization. The following example shows Juan's account accepting the handshake and thus agreeing to the invitation.", + "id": "to-accept-a-handshake-from-another-account-1472500561150", + "title": "To accept a handshake from another account" + } + ], + "AttachPolicy": [ + { + "input": { + "PolicyId": "p-examplepolicyid111", + "TargetId": "ou-examplerootid111-exampleouid111" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to attach a service control policy (SCP) to an OU:\n", + "id": "to-attach-a-policy-to-an-ou", + "title": "To attach a policy to an OU" + }, + { + "input": { + "PolicyId": "p-examplepolicyid111", + "TargetId": "333333333333" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to attach a service control policy (SCP) to an account:\n", + "id": "to-attach-a-policy-to-an-account", + "title": "To attach a policy to an account" + } + ], + "CancelHandshake": [ + { + "input": { + "HandshakeId": "h-examplehandshakeid111" + }, + "output": { + "Handshake": { + "Action": "INVITE", + "Arn": "arn:aws:organizations::111111111111:handshake/o-exampleorgid/invite/h-examplehandshakeid111", + "ExpirationTimestamp": "20170228T1215Z", + "Id": "h-examplehandshakeid111", + "Parties": [ + { + "Id": "o-exampleorgid", + "Type": "ORGANIZATION" + }, + { + "Id": "susan@example.com", + "Type": "EMAIL" + } + ], + "RequestedTimestamp": "20170214T1215Z", + "Resources": [ + { + "Resources": [ + { + "Type": "MASTER_EMAIL", + "Value": "bill@example.com" + }, + { + "Type": "MASTER_NAME", + "Value": "Master Account" + }, + { + "Type": "ORGANIZATION_FEATURE_SET", + "Value": "CONSOLIDATED_BILLING" + } + ], + "Type": "ORGANIZATION", + "Value": "o-exampleorgid" + }, + { + "Type": "ACCOUNT", + "Value": "222222222222" + }, + { + "Type": "NOTES", + "Value": "This is a request for Susan's account to join Bob's organization." + } + ], + "State": "CANCELED" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Bill previously sent an invitation to Susan's account to join his organization. He changes his mind and decides to cancel the invitation before Susan accepts it. The following example shows Bill's cancellation:\n", + "id": "to-cancel-a-handshake-sent-to-a-member-account-1472501320506", + "title": "To cancel a handshake sent to a member account" + } + ], + "CreateAccount": [ + { + "input": { + "AccountName": "Production Account", + "Email": "susan@example.com" + }, + "output": { + "CreateAccountStatus": { + "Id": "car-examplecreateaccountrequestid111", + "State": "IN_PROGRESS" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The owner of an organization creates a member account in the organization. The following example shows that when the organization owner creates the member account, the account is preconfigured with the name \"Production Account\" and an owner email address of susan@example.com. An IAM role is automatically created using the default name because the roleName parameter is not used. AWS Organizations sends Susan a \"Welcome to AWS\" email:\n\n", + "id": "to-create-a-new-account-that-is-automatically-part-of-the-organization-1472501463507", + "title": "To create a new account that is automatically part of the organization" + } + ], + "CreateOrganization": [ + { + "input": { + }, + "output": { + "Organization": { + "Arn": "arn:aws:organizations::111111111111:organization/o-exampleorgid", + "AvailablePolicyTypes": [ + { + "Status": "ENABLED", + "Type": "SERVICE_CONTROL_POLICY" + } + ], + "FeatureSet": "ALL", + "Id": "o-exampleorgid", + "MasterAccountArn": "arn:aws:organizations::111111111111:account/o-exampleorgid/111111111111", + "MasterAccountEmail": "bill@example.com", + "MasterAccountId": "111111111111" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Bill wants to create an organization using credentials from account 111111111111. The following example shows that the account becomes the master account in the new organization. Because he does not specify a feature set, the new organization defaults to all features enabled and service control policies enabled on the root:\n\n", + "id": "to-create-a-new-organization-with-all-features enabled", + "title": "To create a new organization with all features enabled" + }, + { + "input": { + "FeatureSet": "CONSOLIDATED_BILLING" + }, + "output": { + "Organization": { + "Arn": "arn:aws:organizations::111111111111:organization/o-exampleorgid", + "AvailablePolicyTypes": [ + + ], + "FeatureSet": "CONSOLIDATED_BILLING", + "Id": "o-exampleorgid", + "MasterAccountArn": "arn:aws:organizations::111111111111:account/o-exampleorgid/111111111111", + "MasterAccountEmail": "bill@example.com", + "MasterAccountId": "111111111111" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "In the following example, Bill creates an organization using credentials from account 111111111111, and configures the organization to support only the consolidated billing feature set:\n\n", + "id": "to-create-a-new-organization-with-consolidated-billing-features-only", + "title": "To create a new organization with consolidated billing features only" + } + ], + "CreateOrganizationalUnit": [ + { + "input": { + "Name": "AccountingOU", + "ParentId": "r-examplerootid111" + }, + "output": { + "OrganizationalUnit": { + "Arn": "arn:aws:organizations::111111111111:ou/o-exampleorgid/ou-examplerootid111-exampleouid111", + "Id": "ou-examplerootid111-exampleouid111", + "Name": "AccountingOU" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to create an OU that is named AccountingOU. The new OU is directly under the root.:\n\n", + "id": "to-create-a-new-organizational-unit", + "title": "To create a new organization unit" + } + ], + "CreatePolicy": [ + { + "input": { + "Content": "{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":{\\\"Effect\\\":\\\"Allow\\\",\\\"Action\\\":\\\"s3:*\\\"}}", + "Description": "Enables admins of attached accounts to delegate all S3 permissions", + "Name": "AllowAllS3Actions", + "Type": "SERVICE_CONTROL_POLICY" + }, + "output": { + "Policy": { + "Content": "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"s3:*\"}}", + "PolicySummary": { + "Arn": "arn:aws:organizations::111111111111:policy/o-exampleorgid/service_control_policy/p-examplepolicyid111", + "Description": "Allows delegation of all S3 actions", + "Name": "AllowAllS3Actions", + "Type": "SERVICE_CONTROL_POLICY" + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to create a service control policy (SCP) that is named AllowAllS3Actions. The JSON string in the content parameter specifies the content in the policy. The parameter string is escaped with backslashes to ensure that the embedded double quotes in the JSON policy are treated as literals in the parameter, which itself is surrounded by double quotes:\n\n", + "id": "to-create-a-service-control-policy", + "title": "To create a service control policy" + } + ], + "DeclineHandshake": [ + { + "input": { + "HandshakeId": "h-examplehandshakeid111" + }, + "output": { + "Handshake": { + "Action": "INVITE", + "Arn": "arn:aws:organizations::111111111111:handshake/o-exampleorgid/invite/h-examplehandshakeid111", + "ExpirationTimestamp": "2016-12-15T19:27:58Z", + "Id": "h-examplehandshakeid111", + "Parties": [ + { + "Id": "222222222222", + "Type": "ACCOUNT" + }, + { + "Id": "o-exampleorgid", + "Type": "ORGANIZATION" + } + ], + "RequestedTimestamp": "2016-11-30T19:27:58Z", + "Resources": [ + { + "Resources": [ + { + "Type": "MASTER_EMAIL", + "Value": "bill@example.com" + }, + { + "Type": "MASTER_NAME", + "Value": "Master Account" + } + ], + "Type": "ORGANIZATION", + "Value": "o-exampleorgid" + }, + { + "Type": "ACCOUNT", + "Value": "222222222222" + }, + { + "Type": "NOTES", + "Value": "This is an invitation to Susan's account to join the Bill's organization." + } + ], + "State": "DECLINED" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows Susan declining an invitation to join Bill's organization. The DeclineHandshake operation returns a handshake object, showing that the state is now DECLINED:", + "id": "to-decline-a-handshake-sent-from-the-master-account-1472502666967", + "title": "To decline a handshake sent from the master account" + } + ], + "DeleteOrganizationalUnit": [ + { + "input": { + "OrganizationalUnitId": "ou-examplerootid111-exampleouid111" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to delete an OU. The example assumes that you previously removed all accounts and other OUs from the OU:\n\n", + "id": "to-delete-an-organizational-unit", + "title": "To delete an organization unit" + } + ], + "DeletePolicy": [ + { + "input": { + "PolicyId": "p-examplepolicyid111" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to delete a policy from an organization. The example assumes that you previously detached the policy from all entities:\n\n", + "id": "to-delete-a-policy", + "title": "To delete a policy" + } + ], + "DescribeAccount": [ + { + "input": { + "AccountId": "555555555555" + }, + "output": { + "Account": { + "Arn": "arn:aws:organizations::111111111111:account/o-exampleorgid/555555555555", + "Email": "anika@example.com", + "Id": "555555555555", + "Name": "Beta Account" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows a user in the master account (111111111111) asking for details about account 555555555555:", + "id": "to-get-the-details-about-an-account-1472503166868", + "title": "To get the details about an account" + } + ], + "DescribeCreateAccountStatus": [ + { + "input": { + "CreateAccountRequestId": "car-exampleaccountcreationrequestid" + }, + "output": { + "CreateAccountStatus": { + "AccountId": "333333333333", + "Id": "car-exampleaccountcreationrequestid", + "State": "SUCCEEDED" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to request the status about a previous request to create an account in an organization. This operation can be called only by a principal from the organization's master account. In the example, the specified \"createAccountRequestId\" comes from the response of the original call to \"CreateAccount\":", + "id": "to-get-information-about-a-request-to-create-an-account-1472503727223", + "title": "To get information about a request to create an account" + } + ], + "DescribeHandshake": [ + { + "input": { + "HandshakeId": "h-examplehandshakeid111" + }, + "output": { + "Handshake": { + "Action": "INVITE", + "Arn": "arn:aws:organizations::111111111111:handshake/o-exampleorgid/invite/h-examplehandshakeid111", + "ExpirationTimestamp": "2016-11-30T17:24:58.046Z", + "Id": "h-examplehandshakeid111", + "Parties": [ + { + "Id": "o-exampleorgid", + "Type": "ORGANIZATION" + }, + { + "Id": "333333333333", + "Type": "ACCOUNT" + } + ], + "RequestedTimestamp": "2016-11-30T17:24:58.046Z", + "Resources": [ + { + "Resources": [ + { + "Type": "MASTER_EMAIL", + "Value": "bill@example.com" + }, + { + "Type": "MASTER_NAME", + "Value": "Master Account" + } + ], + "Type": "ORGANIZATION", + "Value": "o-exampleorgid" + }, + { + "Type": "ACCOUNT", + "Value": "333333333333" + } + ], + "State": "OPEN" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows you how to request details about a handshake. The handshake ID comes either from the original call to \"InviteAccountToOrganization\", or from a call to \"ListHandshakesForAccount\" or \"ListHandshakesForOrganization\":", + "id": "to-get-information-about-a-handshake-1472503400505", + "title": "To get information about a handshake" + } + ], + "DescribeOrganization": [ + { + "output": { + "Organization": { + "Arn": "arn:aws:organizations::111111111111:organization/o-exampleorgid", + "AvailablePolicyTypes": [ + { + "Status": "ENABLED", + "Type": "SERVICE_CONTROL_POLICY" + } + ], + "FeatureSet": "ALL", + "Id": "o-exampleorgid", + "MasterAccountArn": "arn:aws:organizations::111111111111:account/o-exampleorgid/111111111111", + "MasterAccountEmail": "bill@example.com" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to request information about the current user's organization:/n/n", + "id": "to-get-information-about-an-organization-1472503400505", + "title": "To get information about an organization" + } + ], + "DescribeOrganizationalUnit": [ + { + "input": { + "OrganizationalUnitId": "ou-examplerootid111-exampleouid111" + }, + "output": { + "OrganizationalUnit": { + "Arn": "arn:aws:organizations::111111111111:ou/o-exampleorgid/ou-examplerootid111-exampleouid111", + "Id": "ou-examplerootid111-exampleouid111", + "Name": "Accounting Group" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to request details about an OU:/n/n", + "id": "to-get-information-about-an-organizational-unit", + "title": "To get information about an organizational unit" + } + ], + "DescribePolicy": [ + { + "input": { + "PolicyId": "p-examplepolicyid111" + }, + "output": { + "Policy": { + "Content": "{\\n \\\"Version\\\": \\\"2012-10-17\\\",\\n \\\"Statement\\\": [\\n {\\n \\\"Effect\\\": \\\"Allow\\\",\\n \\\"Action\\\": \\\"*\\\",\\n \\\"Resource\\\": \\\"*\\\"\\n }\\n ]\\n}", + "PolicySummary": { + "Arn": "arn:aws:organizations::111111111111:policy/o-exampleorgid/service_control_policy/p-examplepolicyid111", + "AwsManaged": false, + "Description": "Enables admins to delegate S3 permissions", + "Id": "p-examplepolicyid111", + "Name": "AllowAllS3Actions", + "Type": "SERVICE_CONTROL_POLICY" + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to request information about a policy:/n/n", + "id": "to-get-information-about-a-policy", + "title": "To get information about a policy" + } + ], + "DetachPolicy": [ + { + "input": { + "PolicyId": "p-examplepolicyid111", + "TargetId": "ou-examplerootid111-exampleouid111" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to detach a policy from an OU:/n/n", + "id": "to-detach-a-policy-from-a-root-ou-or-account", + "title": "To detach a policy from a root, OU, or account" + } + ], + "DisablePolicyType": [ + { + "input": { + "PolicyType": "SERVICE_CONTROL_POLICY", + "RootId": "r-examplerootid111" + }, + "output": { + "Root": { + "Arn": "arn:aws:organizations::111111111111:root/o-exampleorgid/r-examplerootid111", + "Id": "r-examplerootid111", + "Name": "Root", + "PolicyTypes": [ + + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to disable the service control policy (SCP) policy type in a root. The response shows that the PolicyTypes response element no longer includes SERVICE_CONTROL_POLICY:/n/n", + "id": "to-disable-a-policy-type-in-a-root", + "title": "To disable a policy type in a root" + } + ], + "EnableAllFeatures": [ + { + "input": { + }, + "output": { + "Handshake": { + "Action": "ENABLE_ALL_FEATURES", + "Arn": "arn:aws:organizations::111111111111:handshake/o-exampleorgid/enable_all_features/h-examplehandshakeid111", + "ExpirationTimestamp": "2017-02-28T09:35:40.05Z", + "Id": "h-examplehandshakeid111", + "Parties": [ + { + "Id": "o-exampleorgid", + "Type": "ORGANIZATION" + } + ], + "RequestedTimestamp": "2017-02-13T09:35:40.05Z", + "Resources": [ + { + "Type": "ORGANIZATION", + "Value": "o-exampleorgid" + } + ], + "State": "REQUESTED" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows the administrator asking all the invited accounts in the organization to approve enabling all features in the organization. AWS Organizations sends an email to the address that is registered with every invited member account asking the owner to approve the change by accepting the handshake that is sent. After all invited member accounts accept the handshake, the organization administrator can finalize the change to enable all features, and those with appropriate permissions can create policies and apply them to roots, OUs, and accounts:/n/n", + "id": "to-enable-all-features-in-an-organization", + "title": "To enable all features in an organization" + } + ], + "EnablePolicyType": [ + { + "input": { + "PolicyType": "SERVICE_CONTROL_POLICY", + "RootId": "r-examplerootid111" + }, + "output": { + "Root": { + "Arn": "arn:aws:organizations::111111111111:root/o-exampleorgid/r-examplerootid111", + "Id": "r-examplerootid111", + "Name": "Root", + "PolicyTypes": [ + { + "Status": "ENABLED", + "Type": "SERVICE_CONTROL_POLICY" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to enable the service control policy (SCP) policy type in a root. The output shows a root object with a PolicyTypes response element showing that SCPs are now enabled:/n/n", + "id": "to-enable-a-policy-type-in-a-root", + "title": "To enable a policy type in a root" + } + ], + "InviteAccountToOrganization": [ + { + "input": { + "Notes": "This is a request for Juan's account to join Bill's organization", + "Target": { + "Id": "juan@example.com", + "Type": "EMAIL" + } + }, + "output": { + "Handshake": { + "Action": "INVITE", + "Arn": "arn:aws:organizations::111111111111:handshake/o-exampleorgid/invite/h-examplehandshakeid111", + "ExpirationTimestamp": "2017-02-16T09:36:05.02Z", + "Id": "h-examplehandshakeid111", + "Parties": [ + { + "Id": "o-exampleorgid", + "Type": "ORGANIZATION" + }, + { + "Id": "juan@example.com", + "Type": "EMAIL" + } + ], + "RequestedTimestamp": "2017-02-01T09:36:05.02Z", + "Resources": [ + { + "Resources": [ + { + "Type": "MASTER_EMAIL", + "Value": "bill@amazon.com" + }, + { + "Type": "MASTER_NAME", + "Value": "Org Master Account" + }, + { + "Type": "ORGANIZATION_FEATURE_SET", + "Value": "FULL" + } + ], + "Type": "ORGANIZATION", + "Value": "o-exampleorgid" + }, + { + "Type": "EMAIL", + "Value": "juan@example.com" + } + ], + "State": "OPEN" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows the admin of the master account owned by bill@example.com inviting the account owned by juan@example.com to join an organization.", + "id": "to-invite-an-account-to-join-an-organization-1472508594110", + "title": "To invite an account to join an organization" + } + ], + "LeaveOrganization": [ + { + "comments": { + "input": { + }, + "output": { + } + }, + "description": "TThe following example shows how to remove your member account from an organization:", + "id": "to-leave-an-organization-as-a-member-account-1472508784736", + "title": "To leave an organization as a member account" + } + ], + "ListAccounts": [ + { + "input": { + }, + "output": { + "Accounts": [ + { + "Arn": "arn:aws:organizations::111111111111:account/o-exampleorgid/111111111111", + "Email": "bill@example.com", + "Id": "111111111111", + "JoinedMethod": "INVITED", + "JoinedTimestamp": "20161215T193015Z", + "Name": "Master Account", + "Status": "ACTIVE" + }, + { + "Arn": "arn:aws:organizations::111111111111:account/o-exampleorgid/222222222222", + "Email": "alice@example.com", + "Id": "222222222222", + "JoinedMethod": "INVITED", + "JoinedTimestamp": "20161215T210221Z", + "Name": "Developer Account", + "Status": "ACTIVE" + }, + { + "Arn": "arn:aws:organizations::111111111111:account/o-exampleorgid/333333333333", + "Email": "juan@example.com", + "Id": "333333333333", + "JoinedMethod": "INVITED", + "JoinedTimestamp": "20161215T210347Z", + "Name": "Test Account", + "Status": "ACTIVE" + }, + { + "Arn": "arn:aws:organizations::111111111111:account/o-exampleorgid/444444444444", + "Email": "anika@example.com", + "Id": "444444444444", + "JoinedMethod": "INVITED", + "JoinedTimestamp": "20161215T210332Z", + "Name": "Production Account", + "Status": "ACTIVE" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows you how to request a list of the accounts in an organization:", + "id": "to-retrieve-a-list-of-all-of-the-accounts-in-an-organization-1472509590974", + "title": "To retrieve a list of all of the accounts in an organization" + } + ], + "ListAccountsForParent": [ + { + "input": { + "ParentId": "ou-examplerootid111-exampleouid111" + }, + "output": { + "Accounts": [ + { + "Arn": "arn:aws:organizations::111111111111:account/o-exampleorgid/333333333333", + "Email": "juan@example.com", + "Id": "333333333333", + "JoinedMethod": "INVITED", + "JoinedTimestamp": 1481835795.536, + "Name": "Development Account", + "Status": "ACTIVE" + }, + { + "Arn": "arn:aws:organizations::111111111111:account/o-exampleorgid/444444444444", + "Email": "anika@example.com", + "Id": "444444444444", + "JoinedMethod": "INVITED", + "JoinedTimestamp": 1481835812.143, + "Name": "Test Account", + "Status": "ACTIVE" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to request a list of the accounts in an OU:/n/n", + "id": "to-retrieve-a-list-of-all-of-the-accounts-in-a-root-or-ou-1472509590974", + "title": "To retrieve a list of all of the accounts in a root or OU" + } + ], + "ListChildren": [ + { + "input": { + "ChildType": "ORGANIZATIONAL_UNIT", + "ParentId": "ou-examplerootid111-exampleouid111" + }, + "output": { + "Children": [ + { + "Id": "ou-examplerootid111-exampleouid111", + "Type": "ORGANIZATIONAL_UNIT" + }, + { + "Id": "ou-examplerootid111-exampleouid222", + "Type": "ORGANIZATIONAL_UNIT" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to request a list of the child OUs in a parent root or OU:/n/n", + "id": "to-retrieve-a-list-of-all-of-the-child-accounts-and-OUs-in-a-parent-container", + "title": "To retrieve a list of all of the child accounts and OUs in a parent root or OU" + } + ], + "ListCreateAccountStatus": [ + { + "input": { + "States": [ + "SUCCEEDED" + ] + }, + "output": { + "CreateAccountStatuses": [ + { + "AccountId": "444444444444", + "AccountName": "Developer Test Account", + "CompletedTimestamp": "2017-01-15T13:45:23.6Z", + "Id": "car-exampleaccountcreationrequestid1", + "RequestedTimestamp": "2017-01-15T13:45:23.01Z", + "State": "SUCCEEDED" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows a user requesting a list of only the completed account creation requests made for the current organization:", + "id": "to-get-a-list-of-completed-account-creation-requests-made-in-the-organization", + "title": "To get a list of completed account creation requests made in the organization" + }, + { + "input": { + "States": [ + "IN_PROGRESS" + ] + }, + "output": { + "CreateAccountStatuses": [ + { + "AccountName": "Production Account", + "Id": "car-exampleaccountcreationrequestid2", + "RequestedTimestamp": "2017-01-15T13:45:23.01Z", + "State": "IN_PROGRESS" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows a user requesting a list of only the in-progress account creation requests made for the current organization:", + "id": "to-get-a-list-of-all-account-creation-requests-made-in-the-organization-1472509174532", + "title": "To get a list of all account creation requests made in the organization" + } + ], + "ListHandshakesForAccount": [ + { + "output": { + "Handshakes": [ + { + "Action": "INVITE", + "Arn": "arn:aws:organizations::111111111111:handshake/o-exampleorgid/invite/h-examplehandshakeid111", + "ExpirationTimestamp": "2017-01-28T14:35:23.3Z", + "Id": "h-examplehandshakeid111", + "Parties": [ + { + "Id": "o-exampleorgid", + "Type": "ORGANIZATION" + }, + { + "Id": "juan@example.com", + "Type": "EMAIL" + } + ], + "RequestedTimestamp": "2017-01-13T14:35:23.3Z", + "Resources": [ + { + "Resources": [ + { + "Type": "MASTER_EMAIL", + "Value": "bill@amazon.com" + }, + { + "Type": "MASTER_NAME", + "Value": "Org Master Account" + }, + { + "Type": "ORGANIZATION_FEATURE_SET", + "Value": "FULL" + } + ], + "Type": "ORGANIZATION", + "Value": "o-exampleorgid" + }, + { + "Type": "EMAIL", + "Value": "juan@example.com" + } + ], + "State": "OPEN" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows you how to get a list of handshakes that are associated with the account of the credentials used to call the operation:", + "id": "to-retrieve-a-list-of-the-handshakes-sent-to-an-account-1472510214747", + "title": "To retrieve a list of the handshakes sent to an account" + } + ], + "ListHandshakesForOrganization": [ + { + "output": { + "Handshakes": [ + { + "Action": "INVITE", + "Arn": "arn:aws:organizations::111111111111:handshake/o-exampleorgid/invite/h-examplehandshakeid111", + "ExpirationTimestamp": "2017-01-28T14:35:23.3Z", + "Id": "h-examplehandshakeid111", + "Parties": [ + { + "Id": "o-exampleorgid", + "Type": "ORGANIZATION" + }, + { + "Id": "juan@example.com", + "Type": "EMAIL" + } + ], + "RequestedTimestamp": "2017-01-13T14:35:23.3Z", + "Resources": [ + { + "Resources": [ + { + "Type": "MASTER_EMAIL", + "Value": "bill@amazon.com" + }, + { + "Type": "MASTER_NAME", + "Value": "Org Master Account" + }, + { + "Type": "ORGANIZATION_FEATURE_SET", + "Value": "FULL" + } + ], + "Type": "ORGANIZATION", + "Value": "o-exampleorgid" + }, + { + "Type": "EMAIL", + "Value": "juan@example.com" + } + ], + "State": "OPEN" + }, + { + "Action": "INVITE", + "Arn": "arn:aws:organizations::111111111111:handshake/o-exampleorgid/invite/h-examplehandshakeid111", + "ExpirationTimestamp": "2017-01-28T14:35:23.3Z", + "Id": "h-examplehandshakeid222", + "Parties": [ + { + "Id": "o-exampleorgid", + "Type": "ORGANIZATION" + }, + { + "Id": "anika@example.com", + "Type": "EMAIL" + } + ], + "RequestedTimestamp": "2017-01-13T14:35:23.3Z", + "Resources": [ + { + "Resources": [ + { + "Type": "MASTER_EMAIL", + "Value": "bill@example.com" + }, + { + "Type": "MASTER_NAME", + "Value": "Master Account" + } + ], + "Type": "ORGANIZATION", + "Value": "o-exampleorgid" + }, + { + "Type": "EMAIL", + "Value": "anika@example.com" + }, + { + "Type": "NOTES", + "Value": "This is an invitation to Anika's account to join Bill's organization." + } + ], + "State": "ACCEPTED" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows you how to get a list of handshakes associated with the current organization:", + "id": "to-retrieve-a-list-of-the-handshakes-associated-with-an-organization-1472511206653", + "title": "To retrieve a list of the handshakes associated with an organization" + } + ], + "ListOrganizationalUnitsForParent": [ + { + "input": { + "ParentId": "r-examplerootid111" + }, + "output": { + "OrganizationalUnits": [ + { + "Arn": "arn:aws:organizations::111111111111:ou/o-exampleorgid/ou-examlerootid111-exampleouid111", + "Id": "ou-examplerootid111-exampleouid111", + "Name": "Development" + }, + { + "Arn": "arn:aws:organizations::111111111111:ou/o-exampleorgid/ou-examlerootid111-exampleouid222", + "Id": "ou-examplerootid111-exampleouid222", + "Name": "Production" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to get a list of OUs in a specified root:/n/n", + "id": "to-retrieve-a-list-of-all-of-the-OUs-in-a-parent-container", + "title": "To retrieve a list of all of the child OUs in a parent root or OU" + } + ], + "ListParents": [ + { + "input": { + "ChildId": "444444444444" + }, + "output": { + "Parents": [ + { + "Id": "ou-examplerootid111-exampleouid111", + "Type": "ORGANIZATIONAL_UNIT" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to list the root or OUs that contain account 444444444444:/n/n", + "id": "to-retrieve-a-list-of-all-of-the-parents-of-a-child-ou-or-account", + "title": "To retrieve a list of all of the parents of a child OU or account" + } + ], + "ListPolicies": [ + { + "input": { + "Filter": "SERVICE_CONTROL_POLICY" + }, + "output": { + "Policies": [ + { + "Arn": "arn:aws:organizations::111111111111:policy/o-exampleorgid/service_control_policy/p-examplepolicyid111", + "AwsManaged": false, + "Description": "Enables account admins to delegate permissions for any S3 actions to users and roles in their accounts.", + "Id": "p-examplepolicyid111", + "Name": "AllowAllS3Actions", + "Type": "SERVICE_CONTROL_POLICY" + }, + { + "Arn": "arn:aws:organizations::111111111111:policy/o-exampleorgid/service_control_policy/p-examplepolicyid222", + "AwsManaged": false, + "Description": "Enables account admins to delegate permissions for any EC2 actions to users and roles in their accounts.", + "Id": "p-examplepolicyid222", + "Name": "AllowAllEC2Actions", + "Type": "SERVICE_CONTROL_POLICY" + }, + { + "Arn": "arn:aws:organizations::aws:policy/service_control_policy/p-FullAWSAccess", + "AwsManaged": true, + "Description": "Allows access to every operation", + "Id": "p-FullAWSAccess", + "Name": "FullAWSAccess", + "Type": "SERVICE_CONTROL_POLICY" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to get a list of service control policies (SCPs):/n/n", + "id": "to-retrieve-a-list-of--policies-in-the-organization", + "title": "To retrieve a list policies in the organization" + } + ], + "ListPoliciesForTarget": [ + { + "input": { + "Filter": "SERVICE_CONTROL_POLICY", + "TargetId": "444444444444" + }, + "output": { + "Policies": [ + { + "Arn": "arn:aws:organizations::111111111111:policy/o-exampleorgid/service_control_policy/p-examplepolicyid222", + "AwsManaged": false, + "Description": "Enables account admins to delegate permissions for any EC2 actions to users and roles in their accounts.", + "Id": "p-examplepolicyid222", + "Name": "AllowAllEC2Actions", + "Type": "SERVICE_CONTROL_POLICY" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to get a list of all service control policies (SCPs) of the type specified by the Filter parameter, that are directly attached to an account. The returned list does not include policies that apply to the account because of inheritance from its location in an OU hierarchy:/n/n", + "id": "to-retrieve-a-list-of-policies-attached-to-a-root-ou-or-account", + "title": "To retrieve a list policies attached to a root, OU, or account" + } + ], + "ListRoots": [ + { + "input": { + }, + "output": { + "Roots": [ + { + "Arn": "arn:aws:organizations::111111111111:root/o-exampleorgid/r-examplerootid111", + "Id": "r-examplerootid111", + "Name": "Root", + "PolicyTypes": [ + { + "Status": "ENABLED", + "Type": "SERVICE_CONTROL_POLICY" + } + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to get the list of the roots in the current organization:/n/n", + "id": "to-retrieve-a-list-of-roots-in-the-organization", + "title": "To retrieve a list of roots in the organization" + } + ], + "ListTargetsForPolicy": [ + { + "input": { + "PolicyId": "p-FullAWSAccess" + }, + "output": { + "Targets": [ + { + "Arn": "arn:aws:organizations::111111111111:root/o-exampleorgid/r-examplerootid111", + "Name": "Root", + "TargetId": "r-examplerootid111", + "Type": "ROOT" + }, + { + "Arn": "arn:aws:organizations::111111111111:account/o-exampleorgid/333333333333;", + "Name": "Developer Test Account", + "TargetId": "333333333333", + "Type": "ACCOUNT" + }, + { + "Arn": "arn:aws:organizations::111111111111:ou/o-exampleorgid/ou-examplerootid111-exampleouid111", + "Name": "Accounting", + "TargetId": "ou-examplerootid111-exampleouid111", + "Type": "ORGANIZATIONAL_UNIT" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to get the list of roots, OUs, and accounts to which the specified policy is attached:/n/n", + "id": "to-retrieve-a-list-of-roots-ous-and-accounts-to-which-a-policy-is-attached", + "title": "To retrieve a list of roots, OUs, and accounts to which a policy is attached" + } + ], + "MoveAccount": [ + { + "input": { + "AccountId": "333333333333", + "DestinationParentId": "ou-examplerootid111-exampleouid111", + "SourceParentId": "r-examplerootid111" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to move a member account from the root to an OU:/n/n", + "id": "to-move-an-ou-or-account-to-another-ou-or-the-root", + "title": "To move an OU or account to another OU or the root" + } + ], + "RemoveAccountFromOrganization": [ + { + "input": { + "AccountId": "333333333333" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows you how to remove an account from an organization:", + "id": "to-remove-an-account-from-an-organization-as-the-master-account", + "title": "To remove an account from an organization as the master account" + } + ], + "UpdateOrganizationalUnit": [ + { + "input": { + "Name": "AccountingOU", + "OrganizationalUnitId": "ou-examplerootid111-exampleouid111" + }, + "output": { + "OrganizationalUnit": { + "Arn": "arn:aws:organizations::111111111111:ou/o-exampleorgid/ou-examplerootid111-exampleouid111", + "Id": "ou-examplerootid111-exampleouid111", + "Name": "AccountingOU" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to rename an OU. The output confirms the new name:/n/n", + "id": "to-rename-an-organizational-unit", + "title": "To rename an organizational unit" + } + ], + "UpdatePolicy": [ + { + "input": { + "Description": "This description replaces the original.", + "Name": "Renamed-Policy", + "PolicyId": "p-examplepolicyid111" + }, + "output": { + "Policy": { + "Content": "{ \"Version\": \"2012-10-17\", \"Statement\": { \"Effect\": \"Allow\", \"Action\": \"ec2:*\", \"Resource\": \"*\" } }", + "PolicySummary": { + "Arn": "arn:aws:organizations::111111111111:policy/o-exampleorgid/service_control_policy/p-examplepolicyid111", + "AwsManaged": false, + "Description": "This description replaces the original.", + "Id": "p-examplepolicyid111", + "Name": "Renamed-Policy", + "Type": "SERVICE_CONTROL_POLICY" + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to rename a policy and give it a new description and new content. The output confirms the new name and description text:/n/n", + "id": "to-update-the-details-of-a-policy", + "title": "To update the details of a policy" + }, + { + "input": { + "Content": "{ \\\"Version\\\": \\\"2012-10-17\\\", \\\"Statement\\\": {\\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": \\\"s3:*\\\", \\\"Resource\\\": \\\"*\\\" } }", + "PolicyId": "p-examplepolicyid111" + }, + "output": { + "Policy": { + "Content": "{ \\\"Version\\\": \\\"2012-10-17\\\", \\\"Statement\\\": { \\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": \\\"s3:*\\\", \\\"Resource\\\": \\\"*\\\" } }", + "PolicySummary": { + "Arn": "arn:aws:organizations::111111111111:policy/o-exampleorgid/service_control_policy/p-examplepolicyid111", + "AwsManaged": false, + "Description": "This description replaces the original.", + "Id": "p-examplepolicyid111", + "Name": "Renamed-Policy", + "Type": "SERVICE_CONTROL_POLICY" + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to replace the JSON text of the SCP from the preceding example with a new JSON policy text string that allows S3 actions instead of EC2 actions:/n/n", + "id": "to-update-the-content-of-a-policy", + "title": "To update the content of a policy" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/paginators-1.json new file mode 100644 index 0000000..7d04f56 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/paginators-1.json @@ -0,0 +1,111 @@ +{ + "pagination": { + "ListAccounts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Accounts" + }, + "ListAccountsForParent": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Accounts" + }, + "ListChildren": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Children" + }, + "ListCreateAccountStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CreateAccountStatuses" + }, + "ListHandshakesForAccount": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Handshakes" + }, + "ListHandshakesForOrganization": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Handshakes" + }, + "ListOrganizationalUnitsForParent": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "OrganizationalUnits" + }, + "ListParents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Parents" + }, + "ListPolicies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Policies" + }, + "ListPoliciesForTarget": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Policies" + }, + "ListRoots": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Roots" + }, + "ListTargetsForPolicy": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Targets" + }, + "ListAWSServiceAccessForOrganization": { + "result_key": "EnabledServicePrincipals", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListTagsForResource": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Tags" + }, + "ListDelegatedAdministrators": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DelegatedAdministrators" + }, + "ListDelegatedServicesForAccount": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DelegatedServices" + }, + "ListAccountsWithInvalidEffectivePolicy": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Accounts" + }, + "ListEffectivePolicyValidationErrors": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EffectivePolicyValidationErrors" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/paginators-1.sdk-extras.json new file mode 100644 index 0000000..16e6eac --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/paginators-1.sdk-extras.json @@ -0,0 +1,20 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListAccountsWithInvalidEffectivePolicy": { + "non_aggregate_keys": [ + "PolicyType" + ] + }, + "ListEffectivePolicyValidationErrors": { + "non_aggregate_keys": [ + "PolicyType", + "AccountId", + "EvaluationTimestamp", + "Path" + ] + } + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/service-2.json.gz new file mode 100644 index 0000000..e8bcba1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/organizations/2016-11-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/osis/2022-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/osis/2022-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..17953f7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/osis/2022-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/osis/2022-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/osis/2022-01-01/paginators-1.json new file mode 100644 index 0000000..ace2992 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/osis/2022-01-01/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListPipelineEndpointConnections": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PipelineEndpointConnections" + }, + "ListPipelineEndpoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PipelineEndpoints" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/osis/2022-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/osis/2022-01-01/service-2.json.gz new file mode 100644 index 0000000..7136e74 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/osis/2022-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..542f37b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/paginators-1.json new file mode 100644 index 0000000..9ab0877 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/paginators-1.json @@ -0,0 +1,82 @@ +{ + "pagination": { + "GetOutpostInstanceTypes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceTypes" + }, + "ListAssets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Assets" + }, + "ListCatalogItems": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CatalogItems" + }, + "ListOrders": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Orders" + }, + "ListOutposts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Outposts" + }, + "ListSites": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Sites" + }, + "GetOutpostSupportedInstanceTypes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceTypes" + }, + "ListCapacityTasks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CapacityTasks" + }, + "ListAssetInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AssetInstances" + }, + "ListBlockingInstancesForCapacityTask": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BlockingInstances" + }, + "GetOutpostBillingInformation": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Subscriptions" + }, + "ListOrderableInstanceTypes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceTypes" + }, + "ListQuotes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Quotes" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/paginators-1.sdk-extras.json new file mode 100644 index 0000000..c44517c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/paginators-1.sdk-extras.json @@ -0,0 +1,20 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetOutpostInstanceTypes": { + "non_aggregate_keys": [ + "OutpostArn", + "OutpostId" + ] + }, + "GetOutpostBillingInformation": { + "non_aggregate_keys": [ + "ContractEndDate", + "PaymentOption", + "PaymentTerm" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/service-2.json.gz new file mode 100644 index 0000000..86a8cb7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/outposts/2019-12-03/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/partitions.json b/venv/lib/python3.12/site-packages/botocore/data/partitions.json new file mode 100644 index 0000000..fb9ebb1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/partitions.json @@ -0,0 +1,267 @@ +{ + "partitions" : [ { + "id" : "aws", + "outputs" : { + "dnsSuffix" : "amazonaws.com", + "dualStackDnsSuffix" : "api.aws", + "implicitGlobalRegion" : "us-east-1", + "name" : "aws", + "supportsDualStack" : true, + "supportsFIPS" : true + }, + "regionRegex" : "^(us|eu|ap|sa|ca|me|af|il|mx)\\-\\w+\\-\\d+$", + "regions" : { + "af-south-1" : { + "description" : "Africa (Cape Town)" + }, + "ap-east-1" : { + "description" : "Asia Pacific (Hong Kong)" + }, + "ap-east-2" : { + "description" : "Asia Pacific (Taipei)" + }, + "ap-northeast-1" : { + "description" : "Asia Pacific (Tokyo)" + }, + "ap-northeast-2" : { + "description" : "Asia Pacific (Seoul)" + }, + "ap-northeast-3" : { + "description" : "Asia Pacific (Osaka)" + }, + "ap-south-1" : { + "description" : "Asia Pacific (Mumbai)" + }, + "ap-south-2" : { + "description" : "Asia Pacific (Hyderabad)" + }, + "ap-southeast-1" : { + "description" : "Asia Pacific (Singapore)" + }, + "ap-southeast-2" : { + "description" : "Asia Pacific (Sydney)" + }, + "ap-southeast-3" : { + "description" : "Asia Pacific (Jakarta)" + }, + "ap-southeast-4" : { + "description" : "Asia Pacific (Melbourne)" + }, + "ap-southeast-5" : { + "description" : "Asia Pacific (Malaysia)" + }, + "ap-southeast-6" : { + "description" : "Asia Pacific (New Zealand)" + }, + "ap-southeast-7" : { + "description" : "Asia Pacific (Thailand)" + }, + "aws-global" : { + "description" : "aws global region" + }, + "ca-central-1" : { + "description" : "Canada (Central)" + }, + "ca-west-1" : { + "description" : "Canada West (Calgary)" + }, + "eu-central-1" : { + "description" : "Europe (Frankfurt)" + }, + "eu-central-2" : { + "description" : "Europe (Zurich)" + }, + "eu-north-1" : { + "description" : "Europe (Stockholm)" + }, + "eu-south-1" : { + "description" : "Europe (Milan)" + }, + "eu-south-2" : { + "description" : "Europe (Spain)" + }, + "eu-west-1" : { + "description" : "Europe (Ireland)" + }, + "eu-west-2" : { + "description" : "Europe (London)" + }, + "eu-west-3" : { + "description" : "Europe (Paris)" + }, + "il-central-1" : { + "description" : "Israel (Tel Aviv)" + }, + "me-central-1" : { + "description" : "Middle East (UAE)" + }, + "me-south-1" : { + "description" : "Middle East (Bahrain)" + }, + "mx-central-1" : { + "description" : "Mexico (Central)" + }, + "sa-east-1" : { + "description" : "South America (Sao Paulo)" + }, + "us-east-1" : { + "description" : "US East (N. Virginia)" + }, + "us-east-2" : { + "description" : "US East (Ohio)" + }, + "us-west-1" : { + "description" : "US West (N. California)" + }, + "us-west-2" : { + "description" : "US West (Oregon)" + } + } + }, { + "id" : "aws-cn", + "outputs" : { + "dnsSuffix" : "amazonaws.com.cn", + "dualStackDnsSuffix" : "api.amazonwebservices.com.cn", + "implicitGlobalRegion" : "cn-northwest-1", + "name" : "aws-cn", + "supportsDualStack" : true, + "supportsFIPS" : true + }, + "regionRegex" : "^cn\\-\\w+\\-\\d+$", + "regions" : { + "aws-cn-global" : { + "description" : "aws-cn global region" + }, + "cn-north-1" : { + "description" : "China (Beijing)" + }, + "cn-northwest-1" : { + "description" : "China (Ningxia)" + } + } + }, { + "id" : "aws-eusc", + "outputs" : { + "dnsSuffix" : "amazonaws.eu", + "dualStackDnsSuffix" : "api.amazonwebservices.eu", + "implicitGlobalRegion" : "eusc-de-east-1", + "name" : "aws-eusc", + "supportsDualStack" : true, + "supportsFIPS" : true + }, + "regionRegex" : "^eusc\\-(de)\\-\\w+\\-\\d+$", + "regions" : { + "eusc-de-east-1" : { + "description" : "AWS European Sovereign Cloud (Germany)" + } + } + }, { + "id" : "aws-iso", + "outputs" : { + "dnsSuffix" : "c2s.ic.gov", + "dualStackDnsSuffix" : "api.aws.ic.gov", + "implicitGlobalRegion" : "us-iso-east-1", + "name" : "aws-iso", + "supportsDualStack" : true, + "supportsFIPS" : true + }, + "regionRegex" : "^us\\-iso\\-\\w+\\-\\d+$", + "regions" : { + "aws-iso-global" : { + "description" : "aws-iso global region" + }, + "us-iso-east-1" : { + "description" : "US ISO East" + }, + "us-iso-west-1" : { + "description" : "US ISO WEST" + } + } + }, { + "id" : "aws-iso-b", + "outputs" : { + "dnsSuffix" : "sc2s.sgov.gov", + "dualStackDnsSuffix" : "api.aws.scloud", + "implicitGlobalRegion" : "us-isob-east-1", + "name" : "aws-iso-b", + "supportsDualStack" : true, + "supportsFIPS" : true + }, + "regionRegex" : "^us\\-isob\\-\\w+\\-\\d+$", + "regions" : { + "aws-iso-b-global" : { + "description" : "aws-iso-b global region" + }, + "us-isob-east-1" : { + "description" : "US ISOB East (Ohio)" + }, + "us-isob-west-1" : { + "description" : "US ISOB West" + } + } + }, { + "id" : "aws-iso-e", + "outputs" : { + "dnsSuffix" : "cloud.adc-e.uk", + "dualStackDnsSuffix" : "api.cloud-aws.adc-e.uk", + "implicitGlobalRegion" : "eu-isoe-west-1", + "name" : "aws-iso-e", + "supportsDualStack" : true, + "supportsFIPS" : true + }, + "regionRegex" : "^eu\\-isoe\\-\\w+\\-\\d+$", + "regions" : { + "aws-iso-e-global" : { + "description" : "aws-iso-e global region" + }, + "eu-isoe-west-1" : { + "description" : "EU ISOE West" + } + } + }, { + "id" : "aws-iso-f", + "outputs" : { + "dnsSuffix" : "csp.hci.ic.gov", + "dualStackDnsSuffix" : "api.aws.hci.ic.gov", + "implicitGlobalRegion" : "us-isof-south-1", + "name" : "aws-iso-f", + "supportsDualStack" : true, + "supportsFIPS" : true + }, + "regionRegex" : "^us\\-isof\\-\\w+\\-\\d+$", + "regions" : { + "aws-iso-f-global" : { + "description" : "aws-iso-f global region" + }, + "us-isof-east-1" : { + "description" : "US ISOF EAST" + }, + "us-isof-south-1" : { + "description" : "US ISOF SOUTH" + } + } + }, { + "id" : "aws-us-gov", + "outputs" : { + "dnsSuffix" : "amazonaws.com", + "dualStackDnsSuffix" : "api.aws", + "implicitGlobalRegion" : "us-gov-west-1", + "name" : "aws-us-gov", + "supportsDualStack" : true, + "supportsFIPS" : true + }, + "regionRegex" : "^us\\-gov\\-\\w+\\-\\d+$", + "regions" : { + "aws-us-gov-global" : { + "description" : "aws-us-gov global region" + }, + "us-gov-east-1" : { + "description" : "AWS GovCloud (US-East)" + }, + "us-gov-west-1" : { + "description" : "AWS GovCloud (US-West)" + } + } + } ], + "version" : "1.1" +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-account/2025-04-04/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-account/2025-04-04/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4de279b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-account/2025-04-04/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-account/2025-04-04/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-account/2025-04-04/paginators-1.json new file mode 100644 index 0000000..aa4ba4c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-account/2025-04-04/paginators-1.json @@ -0,0 +1,21 @@ +{ + "pagination": { + "ListConnectionInvitations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ConnectionInvitationSummaries" + }, + "ListConnections": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ConnectionSummaries" + }, + "ListPartners": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "PartnerSummaryList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-account/2025-04-04/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-account/2025-04-04/service-2.json.gz new file mode 100644 index 0000000..c145e8d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-account/2025-04-04/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-account/2025-04-04/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-account/2025-04-04/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-account/2025-04-04/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-benefits/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-benefits/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..5559f48 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-benefits/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-benefits/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-benefits/2018-05-10/paginators-1.json new file mode 100644 index 0000000..348398a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-benefits/2018-05-10/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListBenefitAllocations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BenefitAllocationSummaries" + }, + "ListBenefitApplications": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BenefitApplicationSummaries" + }, + "ListBenefits": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "BenefitSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-benefits/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-benefits/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..0dcf6b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-benefits/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-benefits/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-benefits/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-benefits/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-channel/2024-03-18/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-channel/2024-03-18/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d2a71db Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-channel/2024-03-18/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-channel/2024-03-18/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-channel/2024-03-18/paginators-1.json new file mode 100644 index 0000000..1e923f4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-channel/2024-03-18/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListChannelHandshakes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListProgramManagementAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListRelationships": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-channel/2024-03-18/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-channel/2024-03-18/service-2.json.gz new file mode 100644 index 0000000..1a1bde9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-channel/2024-03-18/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-channel/2024-03-18/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-channel/2024-03-18/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-channel/2024-03-18/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-revenue-measurement/2022-07-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-revenue-measurement/2022-07-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f484620 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-revenue-measurement/2022-07-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-revenue-measurement/2022-07-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-revenue-measurement/2022-07-26/paginators-1.json new file mode 100644 index 0000000..c7ca57a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-revenue-measurement/2022-07-26/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListMarketplaceRevenueShareAllocations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "MarketplaceRevenueShareAllocationSummaries" + }, + "ListMarketplaceRevenueShares": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "MarketplaceRevenueShareSummaries" + }, + "ListRevenueAttributionAllocations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RevenueAttributionAllocationSummaries" + }, + "ListRevenueAttributions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RevenueAttributionSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-revenue-measurement/2022-07-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-revenue-measurement/2022-07-26/service-2.json.gz new file mode 100644 index 0000000..f691103 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-revenue-measurement/2022-07-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-revenue-measurement/2022-07-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-revenue-measurement/2022-07-26/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-revenue-measurement/2022-07-26/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-selling/2022-07-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-selling/2022-07-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..acf3f11 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-selling/2022-07-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-selling/2022-07-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-selling/2022-07-26/paginators-1.json new file mode 100644 index 0000000..69b037a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-selling/2022-07-26/paginators-1.json @@ -0,0 +1,76 @@ +{ + "pagination": { + "ListEngagementInvitations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EngagementInvitationSummaries" + }, + "ListOpportunities": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "OpportunitySummaries" + }, + "ListSolutions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SolutionSummaries" + }, + "ListEngagementByAcceptingInvitationTasks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TaskSummaries" + }, + "ListEngagementFromOpportunityTasks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TaskSummaries" + }, + "ListEngagementMembers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EngagementMemberList" + }, + "ListEngagementResourceAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EngagementResourceAssociationSummaries" + }, + "ListEngagements": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EngagementSummaryList" + }, + "ListResourceSnapshotJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResourceSnapshotJobSummaries" + }, + "ListResourceSnapshots": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResourceSnapshotSummaries" + }, + "ListOpportunityFromEngagementTasks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TaskSummaries" + }, + "ListProspectingFromEngagementTasks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TaskSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-selling/2022-07-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-selling/2022-07-26/service-2.json.gz new file mode 100644 index 0000000..6618b72 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-selling/2022-07-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/partnercentral-selling/2022-07-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-selling/2022-07-26/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/partnercentral-selling/2022-07-26/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography-data/2022-02-03/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography-data/2022-02-03/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3d1b3a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography-data/2022-02-03/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography-data/2022-02-03/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography-data/2022-02-03/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography-data/2022-02-03/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography-data/2022-02-03/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography-data/2022-02-03/service-2.json.gz new file mode 100644 index 0000000..d0189b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography-data/2022-02-03/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography-data/2022-02-03/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography-data/2022-02-03/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography-data/2022-02-03/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography/2021-09-14/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography/2021-09-14/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..5ab28b1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography/2021-09-14/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography/2021-09-14/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography/2021-09-14/paginators-1.json new file mode 100644 index 0000000..02af499 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography/2021-09-14/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListAliases": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Aliases" + }, + "ListKeys": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Keys" + }, + "ListTagsForResource": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Tags" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography/2021-09-14/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography/2021-09-14/service-2.json.gz new file mode 100644 index 0000000..9d3b165 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography/2021-09-14/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography/2021-09-14/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography/2021-09-14/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/payment-cryptography/2021-09-14/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pca-connector-ad/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-ad/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0c6c99d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-ad/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pca-connector-ad/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-ad/2018-05-10/paginators-1.json new file mode 100644 index 0000000..8923477 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-ad/2018-05-10/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListConnectors": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Connectors" + }, + "ListDirectoryRegistrations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DirectoryRegistrations" + }, + "ListServicePrincipalNames": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ServicePrincipalNames" + }, + "ListTemplateGroupAccessControlEntries": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AccessControlEntries" + }, + "ListTemplates": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Templates" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pca-connector-ad/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-ad/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..2b9e93a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-ad/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pca-connector-scep/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-scep/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0e5ce22 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-scep/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pca-connector-scep/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-scep/2018-05-10/paginators-1.json new file mode 100644 index 0000000..7a913db --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-scep/2018-05-10/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListChallengeMetadata": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Challenges" + }, + "ListConnectors": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Connectors" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pca-connector-scep/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-scep/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..ab3a1f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-scep/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pca-connector-scep/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-scep/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pca-connector-scep/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pcs/2023-02-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pcs/2023-02-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0dbd1cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pcs/2023-02-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pcs/2023-02-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/pcs/2023-02-10/paginators-1.json new file mode 100644 index 0000000..555a266 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pcs/2023-02-10/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListClusters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "clusters" + }, + "ListComputeNodeGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "computeNodeGroups" + }, + "ListQueues": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "queues" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pcs/2023-02-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pcs/2023-02-10/service-2.json.gz new file mode 100644 index 0000000..a0e364e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pcs/2023-02-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pcs/2023-02-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/pcs/2023-02-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pcs/2023-02-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/personalize-events/2018-03-22/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/personalize-events/2018-03-22/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..740bfe5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/personalize-events/2018-03-22/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/personalize-events/2018-03-22/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/personalize-events/2018-03-22/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/personalize-events/2018-03-22/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/personalize-events/2018-03-22/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/personalize-events/2018-03-22/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/personalize-events/2018-03-22/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/personalize-events/2018-03-22/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/personalize-events/2018-03-22/service-2.json.gz new file mode 100644 index 0000000..ffac6bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/personalize-events/2018-03-22/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/personalize-runtime/2018-05-22/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/personalize-runtime/2018-05-22/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..23a4723 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/personalize-runtime/2018-05-22/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/personalize-runtime/2018-05-22/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/personalize-runtime/2018-05-22/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/personalize-runtime/2018-05-22/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/personalize-runtime/2018-05-22/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/personalize-runtime/2018-05-22/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/personalize-runtime/2018-05-22/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/personalize-runtime/2018-05-22/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/personalize-runtime/2018-05-22/service-2.json.gz new file mode 100644 index 0000000..ed30a76 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/personalize-runtime/2018-05-22/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/personalize/2018-05-22/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/personalize/2018-05-22/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..09ae09e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/personalize/2018-05-22/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/personalize/2018-05-22/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/personalize/2018-05-22/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/personalize/2018-05-22/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/personalize/2018-05-22/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/personalize/2018-05-22/paginators-1.json new file mode 100644 index 0000000..ea43852 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/personalize/2018-05-22/paginators-1.json @@ -0,0 +1,100 @@ +{ + "pagination": { + "ListCampaigns": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "campaigns" + }, + "ListDatasetGroups": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "datasetGroups" + }, + "ListDatasetImportJobs": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "datasetImportJobs" + }, + "ListDatasets": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "datasets" + }, + "ListEventTrackers": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "eventTrackers" + }, + "ListRecipes": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "recipes" + }, + "ListSchemas": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "schemas" + }, + "ListSolutionVersions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "solutionVersions" + }, + "ListSolutions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "solutions" + }, + "ListBatchInferenceJobs": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "batchInferenceJobs" + }, + "ListDatasetExportJobs": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "datasetExportJobs" + }, + "ListFilters": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "Filters" + }, + "ListBatchSegmentJobs": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "batchSegmentJobs" + }, + "ListRecommenders": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "recommenders" + }, + "ListMetricAttributionMetrics": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "metrics" + }, + "ListMetricAttributions": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "metricAttributions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/personalize/2018-05-22/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/personalize/2018-05-22/service-2.json.gz new file mode 100644 index 0000000..7cd9df6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/personalize/2018-05-22/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pi/2018-02-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pi/2018-02-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a1805bb Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pi/2018-02-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pi/2018-02-27/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/pi/2018-02-27/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pi/2018-02-27/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pi/2018-02-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/pi/2018-02-27/paginators-1.json new file mode 100644 index 0000000..ab732fb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pi/2018-02-27/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListPerformanceAnalysisReportRecommendations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Recommendations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pi/2018-02-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pi/2018-02-27/service-2.json.gz new file mode 100644 index 0000000..c794990 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pi/2018-02-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint-email/2018-07-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-email/2018-07-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a4fff94 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-email/2018-07-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint-email/2018-07-26/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-email/2018-07-26/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-email/2018-07-26/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint-email/2018-07-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-email/2018-07-26/paginators-1.json new file mode 100644 index 0000000..f2693b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-email/2018-07-26/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "GetDedicatedIps": { + "input_token": "NextToken", + "limit_key": "PageSize", + "output_token": "NextToken", + "result_key": "DedicatedIps" + }, + "ListConfigurationSets": { + "input_token": "NextToken", + "limit_key": "PageSize", + "output_token": "NextToken", + "result_key": "ConfigurationSets" + }, + "ListDedicatedIpPools": { + "input_token": "NextToken", + "limit_key": "PageSize", + "output_token": "NextToken", + "result_key": "DedicatedIpPools" + }, + "ListDeliverabilityTestReports": { + "input_token": "NextToken", + "limit_key": "PageSize", + "output_token": "NextToken", + "result_key": "DeliverabilityTestReports" + }, + "ListEmailIdentities": { + "input_token": "NextToken", + "limit_key": "PageSize", + "output_token": "NextToken", + "result_key": "EmailIdentities" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint-email/2018-07-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-email/2018-07-26/service-2.json.gz new file mode 100644 index 0000000..8230dfe Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-email/2018-07-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..87c5ecf Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/paginators-1.json new file mode 100644 index 0000000..486ef6f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/paginators-1.json @@ -0,0 +1,172 @@ +{ + "pagination": { + "DescribeAccountAttributes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AccountAttributes" + }, + "DescribeAccountLimits": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AccountLimits" + }, + "DescribeConfigurationSets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ConfigurationSets" + }, + "DescribeKeywords": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Keywords" + }, + "DescribeOptOutLists": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "OptOutLists" + }, + "DescribeOptedOutNumbers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "OptedOutNumbers" + }, + "DescribePhoneNumbers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PhoneNumbers" + }, + "DescribePools": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Pools" + }, + "DescribeSenderIds": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SenderIds" + }, + "DescribeSpendLimits": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpendLimits" + }, + "ListPoolOriginationIdentities": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "OriginationIdentities" + }, + "DescribeRegistrationAttachments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RegistrationAttachments" + }, + "DescribeRegistrationFieldDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RegistrationFieldDefinitions" + }, + "DescribeRegistrationFieldValues": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RegistrationFieldValues" + }, + "DescribeRegistrationSectionDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RegistrationSectionDefinitions" + }, + "DescribeRegistrationTypeDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RegistrationTypeDefinitions" + }, + "DescribeRegistrationVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RegistrationVersions" + }, + "DescribeRegistrations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Registrations" + }, + "DescribeVerifiedDestinationNumbers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VerifiedDestinationNumbers" + }, + "ListRegistrationAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RegistrationAssociations" + }, + "DescribeProtectConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ProtectConfigurations" + }, + "ListProtectConfigurationRuleSetNumberOverrides": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RuleSetNumberOverrides" + }, + "DescribeNotifyConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "NotifyConfigurations" + }, + "DescribeNotifyTemplates": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "NotifyTemplates" + }, + "DescribeRcsAgentCountryLaunchStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CountryLaunchStatus" + }, + "DescribeRcsAgents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RcsAgents" + }, + "ListNotifyCountries": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "NotifyCountries" + }, + "ListAvailablePhoneNumbers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AvailablePhoneNumbers" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/paginators-1.sdk-extras.json new file mode 100644 index 0000000..0b3f05b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/paginators-1.sdk-extras.json @@ -0,0 +1,67 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "DescribeKeywords": { + "non_aggregate_keys": [ + "OriginationIdentity", + "OriginationIdentityArn" + ] + }, + "DescribeRegistrationFieldDefinitions": { + "non_aggregate_keys": [ + "RegistrationType" + ] + }, + "DescribeRegistrationFieldValues": { + "non_aggregate_keys": [ + "RegistrationId", + "RegistrationArn", + "VersionNumber" + ] + }, + "DescribeRegistrationSectionDefinitions": { + "non_aggregate_keys": [ + "RegistrationType" + ] + }, + "DescribeRegistrationVersions": { + "non_aggregate_keys": [ + "RegistrationId", + "RegistrationArn" + ] + }, + "DescribeOptedOutNumbers": { + "non_aggregate_keys": [ + "OptOutListArn", + "OptOutListName" + ] + }, + "ListPoolOriginationIdentities": { + "non_aggregate_keys": [ + "PoolArn", + "PoolId" + ] + }, + "ListProtectConfigurationRuleSetNumberOverrides": { + "non_aggregate_keys": [ + "ProtectConfigurationId", + "ProtectConfigurationArn" + ] + }, + "ListRegistrationAssociations": { + "non_aggregate_keys": [ + "RegistrationId", + "RegistrationArn", + "RegistrationType" + ] + }, + "DescribeRcsAgentCountryLaunchStatus": { + "non_aggregate_keys": [ + "RcsAgentArn", + "RcsAgentId" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/service-2.json.gz new file mode 100644 index 0000000..f7a4d9b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice-v2/2022-03-31/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice/2018-09-05/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice/2018-09-05/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..e4b7b9a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice/2018-09-05/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice/2018-09-05/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice/2018-09-05/service-2.json.gz new file mode 100644 index 0000000..7f6dcc2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pinpoint-sms-voice/2018-09-05/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint/2016-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pinpoint/2016-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6fca298 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pinpoint/2016-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint/2016-12-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/pinpoint/2016-12-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pinpoint/2016-12-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pinpoint/2016-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pinpoint/2016-12-01/service-2.json.gz new file mode 100644 index 0000000..d24a245 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pinpoint/2016-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pipes/2015-10-07/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pipes/2015-10-07/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..171a2aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pipes/2015-10-07/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pipes/2015-10-07/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/pipes/2015-10-07/paginators-1.json new file mode 100644 index 0000000..4663077 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pipes/2015-10-07/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListPipes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Pipes" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pipes/2015-10-07/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pipes/2015-10-07/service-2.json.gz new file mode 100644 index 0000000..e14a93b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pipes/2015-10-07/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pipes/2015-10-07/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/pipes/2015-10-07/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pipes/2015-10-07/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/polly/2016-06-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/polly/2016-06-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..2e4eeb1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/polly/2016-06-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/polly/2016-06-10/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/polly/2016-06-10/examples-1.json new file mode 100644 index 0000000..a0e354e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/polly/2016-06-10/examples-1.json @@ -0,0 +1,171 @@ +{ + "version": "1.0", + "examples": { + "DeleteLexicon": [ + { + "input": { + "Name": "example" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes a specified pronunciation lexicon stored in an AWS Region.", + "id": "to-delete-a-lexicon-1481922498332", + "title": "To delete a lexicon" + } + ], + "DescribeVoices": [ + { + "input": { + "LanguageCode": "en-GB" + }, + "output": { + "Voices": [ + { + "Gender": "Female", + "Id": "Emma", + "LanguageCode": "en-GB", + "LanguageName": "British English", + "Name": "Emma" + }, + { + "Gender": "Male", + "Id": "Brian", + "LanguageCode": "en-GB", + "LanguageName": "British English", + "Name": "Brian" + }, + { + "Gender": "Female", + "Id": "Amy", + "LanguageCode": "en-GB", + "LanguageName": "British English", + "Name": "Amy" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns the list of voices that are available for use when requesting speech synthesis. Displayed languages are those within the specified language code. If no language code is specified, voices for all available languages are displayed.", + "id": "to-describe-available-voices-1482180557753", + "title": "To describe available voices" + } + ], + "GetLexicon": [ + { + "input": { + "Name": "" + }, + "output": { + "Lexicon": { + "Content": "\r\n\r\n \r\n W3C\r\n World Wide Web Consortium\r\n \r\n", + "Name": "example" + }, + "LexiconAttributes": { + "Alphabet": "ipa", + "LanguageCode": "en-US", + "LastModified": 1478542980.117, + "LexemesCount": 1, + "LexiconArn": "arn:aws:polly:us-east-1:123456789012:lexicon/example", + "Size": 503 + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns the content of the specified pronunciation lexicon stored in an AWS Region.", + "id": "to-retrieve-a-lexicon-1481912870836", + "title": "To retrieve a lexicon" + } + ], + "ListLexicons": [ + { + "input": { + }, + "output": { + "Lexicons": [ + { + "Attributes": { + "Alphabet": "ipa", + "LanguageCode": "en-US", + "LastModified": 1478542980.117, + "LexemesCount": 1, + "LexiconArn": "arn:aws:polly:us-east-1:123456789012:lexicon/example", + "Size": 503 + }, + "Name": "example" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns a list of pronunciation lexicons stored in an AWS Region.", + "id": "to-list-all-lexicons-in-a-region-1481842106487", + "title": "To list all lexicons in a region" + } + ], + "PutLexicon": [ + { + "input": { + "Content": "", + "Name": "W3C" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Stores a pronunciation lexicon in an AWS Region.", + "id": "to-save-a-lexicon-1482272584088", + "title": "To save a lexicon" + } + ], + "SynthesizeSpeech": [ + { + "input": { + "LexiconNames": [ + "example" + ], + "OutputFormat": "mp3", + "SampleRate": "8000", + "Text": "All Gaul is divided into three parts", + "TextType": "text", + "VoiceId": "Joanna" + }, + "output": { + "AudioStream": "TEXT", + "ContentType": "audio/mpeg", + "RequestCharacters": 37 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Synthesizes plain text or SSML into a file of human-like speech.", + "id": "to-synthesize-speech-1482186064046", + "title": "To synthesize speech" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/polly/2016-06-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/polly/2016-06-10/paginators-1.json new file mode 100644 index 0000000..dc76e7c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/polly/2016-06-10/paginators-1.json @@ -0,0 +1,20 @@ +{ + "pagination": { + "DescribeVoices": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Voices" + }, + "ListLexicons": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Lexicons" + }, + "ListSpeechSynthesisTasks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SynthesisTasks" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/polly/2016-06-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/polly/2016-06-10/service-2.json.gz new file mode 100644 index 0000000..b9e020c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/polly/2016-06-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pricing-plan-manager/2025-08-05/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pricing-plan-manager/2025-08-05/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..48cb666 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pricing-plan-manager/2025-08-05/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pricing-plan-manager/2025-08-05/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/pricing-plan-manager/2025-08-05/paginators-1.json new file mode 100644 index 0000000..9c8834b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pricing-plan-manager/2025-08-05/paginators-1.json @@ -0,0 +1,9 @@ +{ + "pagination": { + "ListSubscriptions": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "subscriptionSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pricing-plan-manager/2025-08-05/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pricing-plan-manager/2025-08-05/service-2.json.gz new file mode 100644 index 0000000..e9d2c26 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pricing-plan-manager/2025-08-05/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..5c92538 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/examples-1.json new file mode 100644 index 0000000..abc1c59 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/examples-1.json @@ -0,0 +1,104 @@ +{ + "version": "1.0", + "examples": { + "DescribeServices": [ + { + "input": { + "FormatVersion": "aws_v1", + "MaxResults": 1, + "ServiceCode": "AmazonEC2" + }, + "output": { + "FormatVersion": "aws_v1", + "NextToken": "abcdefg123", + "Services": [ + { + "AttributeNames": [ + "volumeType", + "maxIopsvolume", + "instanceCapacity10xlarge", + "locationType", + "operation" + ], + "ServiceCode": "AmazonEC2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Retrieves the service for the given Service Code.", + "id": "to-retrieve-service-metadata", + "title": "To retrieve a list of services and service codes" + } + ], + "GetAttributeValues": [ + { + "input": { + "AttributeName": "volumeType", + "MaxResults": 2, + "ServiceCode": "AmazonEC2" + }, + "output": { + "AttributeValues": [ + { + "Value": "Throughput Optimized HDD" + }, + { + "Value": "Provisioned IOPS" + } + ], + "NextToken": "GpgauEXAMPLEezucl5LV0w==:7GzYJ0nw0DBTJ2J66EoTIIynE6O1uXwQtTRqioJzQadBnDVgHPzI1en4BUQnPCLpzeBk9RQQAWaFieA4+DapFAGLgk+Z/9/cTw9GldnPOHN98+FdmJP7wKU3QQpQ8MQr5KOeBkIsAqvAQYdL0DkL7tHwPtE5iCEByAmg9gcC/yBU1vAOsf7R3VaNN4M5jMDv3woSWqASSIlBVB6tgW78YL22KhssoItM/jWW+aP6Jqtq4mldxp/ct6DWAl+xLFwHU/CbketimPPXyqHF3/UXDw==" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation returns a list of values available for the given attribute.", + "id": "to-retreive-attribute-values", + "title": "To retrieve a list of attribute values" + } + ], + "GetProducts": [ + { + "input": { + "Filters": [ + { + "Field": "ServiceCode", + "Type": "TERM_MATCH", + "Value": "AmazonEC2" + }, + { + "Field": "volumeType", + "Type": "TERM_MATCH", + "Value": "Provisioned IOPS" + } + ], + "FormatVersion": "aws_v1", + "MaxResults": 1 + }, + "output": { + "FormatVersion": "aws_v1", + "NextToken": "57r3EXAMPLEujbzWfHF7Ciw==:ywSmZsD3mtpQmQLQ5XfOsIMkYybSj+vAT+kGmwMFq+K9DGmIoJkz7lunVeamiOPgthdWSO2a7YKojCO+zY4dJmuNl2QvbNhXs+AJ2Ufn7xGmJncNI2TsEuAsVCUfTAvAQNcwwamtk6XuZ4YdNnooV62FjkV3ZAn40d9+wAxV7+FImvhUHi/+f8afgZdGh2zPUlH8jlV9uUtj0oHp8+DhPUuHXh+WBII1E/aoKpPSm3c=", + "PriceList": [ + "{\"product\":{\"productFamily\":\"Storage\",\"attributes\":{\"storageMedia\":\"SSD-backed\",\"maxThroughputvolume\":\"320 MB/sec\",\"volumeType\":\"Provisioned IOPS\",\"maxIopsvolume\":\"20000\",\"servicecode\":\"AmazonEC2\",\"usagetype\":\"CAN1-EBS:VolumeUsage.piops\",\"locationType\":\"AWS Region\",\"location\":\"Canada (Central)\",\"servicename\":\"Amazon Elastic Compute Cloud\",\"maxVolumeSize\":\"16 TiB\",\"operation\":\"\"},\"sku\":\"WQGC34PB2AWS8R4U\"},\"serviceCode\":\"AmazonEC2\",\"terms\":{\"OnDemand\":{\"WQGC34PB2AWS8R4U.JRTCKXETXF\":{\"priceDimensions\":{\"WQGC34PB2AWS8R4U.JRTCKXETXF.6YS6EN2CT7\":{\"unit\":\"GB-Mo\",\"endRange\":\"Inf\",\"description\":\"$0.138 per GB-month of Provisioned IOPS SSD (io1) provisioned storage - Canada (Central)\",\"appliesTo\":[],\"rateCode\":\"WQGC34PB2AWS8R4U.JRTCKXETXF.6YS6EN2CT7\",\"beginRange\":\"0\",\"pricePerUnit\":{\"USD\":\"0.1380000000\"}}},\"sku\":\"WQGC34PB2AWS8R4U\",\"effectiveDate\":\"2017-08-01T00:00:00Z\",\"offerTermCode\":\"JRTCKXETXF\",\"termAttributes\":{}}}},\"version\":\"20170901182201\",\"publicationDate\":\"2017-09-01T18:22:01Z\"}" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation returns a list of products that match the given criteria.", + "id": "to-retrieve-available products", + "title": "To retrieve a list of products" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/paginators-1.json new file mode 100644 index 0000000..0f2ce4e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "DescribeServices": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Services", + "non_aggregate_keys": [ + "FormatVersion" + ] + }, + "GetAttributeValues": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AttributeValues" + }, + "GetProducts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PriceList", + "non_aggregate_keys": [ + "FormatVersion" + ] + }, + "ListPriceLists": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PriceLists" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/service-2.json.gz new file mode 100644 index 0000000..6e86225 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/pricing/2017-10-15/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1821d95 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/paginators-1.json new file mode 100644 index 0000000..a52075c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/paginators-1.json @@ -0,0 +1,121 @@ +{ + "pagination": { + "ListEnvironmentAccountConnections": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "environmentAccountConnections" + }, + "ListEnvironmentTemplateVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "templateVersions" + }, + "ListEnvironmentTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "templates" + }, + "ListEnvironments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "environments" + }, + "ListServiceInstances": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "serviceInstances" + }, + "ListServiceTemplateVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "templateVersions" + }, + "ListServiceTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "templates" + }, + "ListServices": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "services" + }, + "ListTagsForResource": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tags" + }, + "ListEnvironmentOutputs": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "outputs" + }, + "ListEnvironmentProvisionedResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "provisionedResources" + }, + "ListRepositories": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "repositories" + }, + "ListRepositorySyncDefinitions": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "syncDefinitions" + }, + "ListServiceInstanceOutputs": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "outputs" + }, + "ListServiceInstanceProvisionedResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "provisionedResources" + }, + "ListServicePipelineOutputs": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "outputs" + }, + "ListServicePipelineProvisionedResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "provisionedResources" + }, + "ListComponentOutputs": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "outputs" + }, + "ListComponentProvisionedResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "provisionedResources" + }, + "ListComponents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "components" + }, + "ListDeployments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "deployments" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/service-2.json.gz new file mode 100644 index 0000000..1a69dc3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/waiters-2.json new file mode 100644 index 0000000..f99a6fe --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/proton/2020-07-20/waiters-2.json @@ -0,0 +1,208 @@ +{ + "version" : 2, + "waiters" : { + "ComponentDeleted" : { + "description" : "Wait until a Component is deleted. Use this after invoking DeleteComponent", + "delay" : 5, + "maxAttempts" : 999, + "operation" : "GetComponent", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "component.deploymentStatus", + "state" : "failure", + "expected" : "DELETE_FAILED" + } ] + }, + "ComponentDeployed" : { + "description" : "Wait until a Component is deployed. Use this after invoking CreateComponent or UpdateComponent", + "delay" : 5, + "maxAttempts" : 999, + "operation" : "GetComponent", + "acceptors" : [ { + "matcher" : "path", + "argument" : "component.deploymentStatus", + "state" : "success", + "expected" : "SUCCEEDED" + }, { + "matcher" : "path", + "argument" : "component.deploymentStatus", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "EnvironmentDeployed" : { + "description" : "Wait until an Environment is deployed. Use this after invoking CreateEnvironment or UpdateEnvironment", + "delay" : 5, + "maxAttempts" : 999, + "operation" : "GetEnvironment", + "acceptors" : [ { + "matcher" : "path", + "argument" : "environment.deploymentStatus", + "state" : "success", + "expected" : "SUCCEEDED" + }, { + "matcher" : "path", + "argument" : "environment.deploymentStatus", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "EnvironmentTemplateVersionRegistered" : { + "description" : "Wait until an EnvironmentTemplateVersion is registered. Use this after invoking CreateEnvironmentTemplateVersion", + "delay" : 2, + "maxAttempts" : 150, + "operation" : "GetEnvironmentTemplateVersion", + "acceptors" : [ { + "matcher" : "path", + "argument" : "environmentTemplateVersion.status", + "state" : "success", + "expected" : "DRAFT" + }, { + "matcher" : "path", + "argument" : "environmentTemplateVersion.status", + "state" : "success", + "expected" : "PUBLISHED" + }, { + "matcher" : "path", + "argument" : "environmentTemplateVersion.status", + "state" : "failure", + "expected" : "REGISTRATION_FAILED" + } ] + }, + "ServiceCreated" : { + "description" : "Wait until an Service has deployed its instances and possibly pipeline. Use this after invoking CreateService", + "delay" : 5, + "maxAttempts" : 999, + "operation" : "GetService", + "acceptors" : [ { + "matcher" : "path", + "argument" : "service.status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "service.status", + "state" : "failure", + "expected" : "CREATE_FAILED_CLEANUP_COMPLETE" + }, { + "matcher" : "path", + "argument" : "service.status", + "state" : "failure", + "expected" : "CREATE_FAILED_CLEANUP_FAILED" + }, { + "matcher" : "path", + "argument" : "service.status", + "state" : "failure", + "expected" : "CREATE_FAILED" + } ] + }, + "ServiceDeleted" : { + "description" : "Wait until a Service, its instances, and possibly pipeline have been deleted after DeleteService is invoked", + "delay" : 5, + "maxAttempts" : 999, + "operation" : "GetService", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "service.status", + "state" : "failure", + "expected" : "DELETE_FAILED" + } ] + }, + "ServiceInstanceDeployed" : { + "description" : "Wait until a ServiceInstance is deployed. Use this after invoking CreateService or UpdateServiceInstance", + "delay" : 5, + "maxAttempts" : 999, + "operation" : "GetServiceInstance", + "acceptors" : [ { + "matcher" : "path", + "argument" : "serviceInstance.deploymentStatus", + "state" : "success", + "expected" : "SUCCEEDED" + }, { + "matcher" : "path", + "argument" : "serviceInstance.deploymentStatus", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "ServicePipelineDeployed" : { + "description" : "Wait until an ServicePipeline is deployed. Use this after invoking CreateService or UpdateServicePipeline", + "delay" : 10, + "maxAttempts" : 360, + "operation" : "GetService", + "acceptors" : [ { + "matcher" : "path", + "argument" : "service.pipeline.deploymentStatus", + "state" : "success", + "expected" : "SUCCEEDED" + }, { + "matcher" : "path", + "argument" : "service.pipeline.deploymentStatus", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "ServiceTemplateVersionRegistered" : { + "description" : "Wait until a ServiceTemplateVersion is registered. Use this after invoking CreateServiceTemplateVersion", + "delay" : 2, + "maxAttempts" : 150, + "operation" : "GetServiceTemplateVersion", + "acceptors" : [ { + "matcher" : "path", + "argument" : "serviceTemplateVersion.status", + "state" : "success", + "expected" : "DRAFT" + }, { + "matcher" : "path", + "argument" : "serviceTemplateVersion.status", + "state" : "success", + "expected" : "PUBLISHED" + }, { + "matcher" : "path", + "argument" : "serviceTemplateVersion.status", + "state" : "failure", + "expected" : "REGISTRATION_FAILED" + } ] + }, + "ServiceUpdated" : { + "description" : "Wait until a Service, its instances, and possibly pipeline have been deployed after UpdateService is invoked", + "delay" : 5, + "maxAttempts" : 999, + "operation" : "GetService", + "acceptors" : [ { + "matcher" : "path", + "argument" : "service.status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "service.status", + "state" : "failure", + "expected" : "UPDATE_FAILED_CLEANUP_COMPLETE" + }, { + "matcher" : "path", + "argument" : "service.status", + "state" : "failure", + "expected" : "UPDATE_FAILED_CLEANUP_FAILED" + }, { + "matcher" : "path", + "argument" : "service.status", + "state" : "failure", + "expected" : "UPDATE_FAILED" + }, { + "matcher" : "path", + "argument" : "service.status", + "state" : "failure", + "expected" : "UPDATE_COMPLETE_CLEANUP_FAILED" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/qapps/2023-11-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/qapps/2023-11-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..39a9bb2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/qapps/2023-11-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/qapps/2023-11-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/qapps/2023-11-27/paginators-1.json new file mode 100644 index 0000000..0d13e6c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/qapps/2023-11-27/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListLibraryItems": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "limit", + "result_key": "libraryItems" + }, + "ListQApps": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "limit", + "result_key": "apps" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/qapps/2023-11-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/qapps/2023-11-27/service-2.json.gz new file mode 100644 index 0000000..09e784d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/qapps/2023-11-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/qapps/2023-11-27/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/qapps/2023-11-27/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/qapps/2023-11-27/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..244232b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/paginators-1.json new file mode 100644 index 0000000..47e8498 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/paginators-1.json @@ -0,0 +1,124 @@ +{ + "pagination": { + "GetChatControlsConfiguration": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "topicConfigurations" + }, + "ListApplications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "applications" + }, + "ListConversations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "conversations" + }, + "ListDataSourceSyncJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "history" + }, + "ListDataSources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dataSources" + }, + "ListDocuments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "documentDetailList" + }, + "ListGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListIndices": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "indices" + }, + "ListMessages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "messages" + }, + "ListPlugins": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "plugins" + }, + "ListRetrievers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "retrievers" + }, + "ListWebExperiences": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "webExperiences" + }, + "ListAttachments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "attachments" + }, + "ListDataAccessors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dataAccessors" + }, + "ListPluginActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListPluginTypeActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListPluginTypeMetadata": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "SearchRelevantContent": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "relevantContent" + }, + "ListSubscriptions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "subscriptions" + }, + "ListChatResponseConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "chatResponseConfigurations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/paginators-1.sdk-extras.json new file mode 100644 index 0000000..b5a1e6e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/paginators-1.sdk-extras.json @@ -0,0 +1,16 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetChatControlsConfiguration": { + "non_aggregate_keys": [ + "responseScope", + "blockedPhrases", + "creatorModeConfiguration", + "orchestrationConfiguration", + "hallucinationReductionConfiguration" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/service-2.json.gz new file mode 100644 index 0000000..49a8fee Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/qbusiness/2023-11-27/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/qconnect/2020-10-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/qconnect/2020-10-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7fc1256 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/qconnect/2020-10-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/qconnect/2020-10-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/qconnect/2020-10-19/paginators-1.json new file mode 100644 index 0000000..10f07b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/qconnect/2020-10-19/paginators-1.json @@ -0,0 +1,142 @@ +{ + "pagination": { + "ListAssistantAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assistantAssociationSummaries" + }, + "ListAssistants": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assistantSummaries" + }, + "ListContents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "contentSummaries" + }, + "ListImportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "importJobSummaries" + }, + "ListKnowledgeBases": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "knowledgeBaseSummaries" + }, + "ListQuickResponses": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "quickResponseSummaries" + }, + "QueryAssistant": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "results" + }, + "SearchContent": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "contentSummaries" + }, + "SearchQuickResponses": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "results" + }, + "SearchSessions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sessionSummaries" + }, + "ListContentAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "contentAssociationSummaries" + }, + "ListAIAgentVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "aiAgentVersionSummaries" + }, + "ListAIAgents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "aiAgentSummaries" + }, + "ListAIPromptVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "aiPromptVersionSummaries" + }, + "ListAIPrompts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "aiPromptSummaries" + }, + "ListMessageTemplateVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "messageTemplateVersionSummaries" + }, + "ListMessageTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "messageTemplateSummaries" + }, + "SearchMessageTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "results" + }, + "ListAIGuardrailVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "aiGuardrailVersionSummaries" + }, + "ListAIGuardrails": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "aiGuardrailSummaries" + }, + "ListMessages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "messages" + }, + "ListSpans": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "spans" + }, + "ListModels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "modelSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/qconnect/2020-10-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/qconnect/2020-10-19/service-2.json.gz new file mode 100644 index 0000000..3f73ae4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/qconnect/2020-10-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/qconnect/2020-10-19/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/qconnect/2020-10-19/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/qconnect/2020-10-19/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4e0061b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/paginators-1.json new file mode 100644 index 0000000..ffb57bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/paginators-1.json @@ -0,0 +1,298 @@ +{ + "pagination": { + "ListAnalyses": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AnalysisSummaryList" + }, + "ListDashboardVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DashboardVersionSummaryList" + }, + "ListDashboards": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DashboardSummaryList" + }, + "ListDataSets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DataSetSummaries" + }, + "ListDataSources": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DataSources" + }, + "ListIngestions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Ingestions" + }, + "ListNamespaces": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Namespaces" + }, + "ListTemplateAliases": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TemplateAliasList" + }, + "ListTemplateVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TemplateVersionSummaryList" + }, + "ListTemplates": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TemplateSummaryList" + }, + "ListThemeVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ThemeVersionSummaryList" + }, + "ListThemes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ThemeSummaryList" + }, + "SearchAnalyses": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AnalysisSummaryList" + }, + "SearchDashboards": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DashboardSummaryList" + }, + "SearchDataSets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DataSetSummaries" + }, + "SearchDataSources": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DataSourceSummaries" + }, + "ListAssetBundleExportJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AssetBundleExportJobSummaryList" + }, + "ListAssetBundleImportJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AssetBundleImportJobSummaryList" + }, + "ListGroupMemberships": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "GroupMemberList" + }, + "ListGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "GroupList" + }, + "ListIAMPolicyAssignments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "IAMPolicyAssignments" + }, + "ListIAMPolicyAssignmentsForUser": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ActiveAssignments" + }, + "ListUserGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "GroupList" + }, + "ListUsers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "UserList" + }, + "SearchGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "GroupList" + }, + "DescribeFolderPermissions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Permissions" + }, + "DescribeFolderResolvedPermissions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Permissions" + }, + "ListFolderMembers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FolderMemberList" + }, + "ListFolders": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FolderSummaryList" + }, + "SearchFolders": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FolderSummaryList" + }, + "ListRoleMemberships": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "MembersList" + }, + "ListFoldersForResource": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Folders" + }, + "ListBrands": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Brands" + }, + "ListCustomPermissions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CustomPermissionsList" + }, + "SearchTopics": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TopicSummaryList" + }, + "ListActionConnectors": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ActionConnectorSummaries" + }, + "ListFlows": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FlowSummaryList" + }, + "SearchActionConnectors": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ActionConnectorSummaries" + }, + "SearchFlows": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FlowSummaryList" + }, + "ListOAuthClientApplications": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "OAuthClientApplications" + }, + "ListKnowledgeBases": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "KnowledgeBaseSummaries" + }, + "SearchKnowledgeBases": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "KnowledgeBaseSummaries" + }, + "ListTopicsV2": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TopicSummaryList" + }, + "SearchTopicsV2": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TopicSummaryList" + }, + "ListApprovalPolicies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Policies" + }, + "ListDlpSettings": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DlpSettingSummaries" + }, + "ListLimitsProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "profiles" + }, + "ListApps": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AppSummaryList" + }, + "SearchApps": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AppSummaryList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/paginators-1.sdk-extras.json new file mode 100644 index 0000000..6dcca8e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/paginators-1.sdk-extras.json @@ -0,0 +1,269 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListAnalyses": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListDashboardVersions": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListTemplateAliases": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListTemplateVersions": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListTemplates": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListThemeVersions": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListThemes": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "SearchAnalyses": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "SearchDashboards": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "SearchDataSets": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "SearchDataSources": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListNamespaces": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListOAuthClientApplications": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListIngestions": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListDataSources": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListDataSets": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListDashboards": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListAssetBundleExportJobs": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListAssetBundleImportJobs": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListGroupMemberships": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListGroups": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListIAMPolicyAssignments": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListIAMPolicyAssignmentsForUser": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListUserGroups": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListUsers": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "SearchGroups": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListFolders": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListFolderMembers": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "SearchFolders": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "DescribeFolderPermissions": { + "non_aggregate_keys": [ + "Status", + "RequestId", + "Arn", + "FolderId" + ] + }, + "DescribeFolderResolvedPermissions": { + "non_aggregate_keys": [ + "Status", + "RequestId", + "Arn", + "FolderId" + ] + }, + "ListRoleMemberships": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListFoldersForResource": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListCustomPermissions": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "SearchTopics": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListActionConnectors": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListFlows": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "SearchActionConnectors": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "SearchFlows": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListKnowledgeBases": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "SearchKnowledgeBases": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "ListTopicsV2": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + }, + "SearchTopicsV2": { + "non_aggregate_keys": [ + "Status", + "RequestId" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/service-2.json.gz new file mode 100644 index 0000000..6022ac2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/quicksight/2018-04-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ram/2018-01-04/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ram/2018-01-04/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3876916 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ram/2018-01-04/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ram/2018-01-04/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ram/2018-01-04/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ram/2018-01-04/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ram/2018-01-04/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ram/2018-01-04/paginators-1.json new file mode 100644 index 0000000..366355d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ram/2018-01-04/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "GetResourcePolicies": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "policies" + }, + "GetResourceShareAssociations": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "resourceShareAssociations" + }, + "GetResourceShareInvitations": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "resourceShareInvitations" + }, + "GetResourceShares": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "resourceShares" + }, + "ListPrincipals": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "principals" + }, + "ListResources": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "resources" + }, + "ListSourceAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sourceAssociations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ram/2018-01-04/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ram/2018-01-04/service-2.json.gz new file mode 100644 index 0000000..154b587 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ram/2018-01-04/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rbin/2021-06-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rbin/2021-06-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d35102e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rbin/2021-06-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rbin/2021-06-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/rbin/2021-06-15/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rbin/2021-06-15/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rbin/2021-06-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/rbin/2021-06-15/paginators-1.json new file mode 100644 index 0000000..bdbfafb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rbin/2021-06-15/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListRules": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Rules" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rbin/2021-06-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rbin/2021-06-15/service-2.json.gz new file mode 100644 index 0000000..1a70ef1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rbin/2021-06-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds-data/2018-08-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rds-data/2018-08-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ac45acb Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rds-data/2018-08-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds-data/2018-08-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/rds-data/2018-08-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rds-data/2018-08-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds-data/2018-08-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/rds-data/2018-08-01/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rds-data/2018-08-01/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds-data/2018-08-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rds-data/2018-08-01/service-2.json.gz new file mode 100644 index 0000000..a0c784a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rds-data/2018-08-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds/2014-09-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-09-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..645e3f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-09-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds/2014-09-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-09-01/paginators-1.json new file mode 100644 index 0000000..76c4f3a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-09-01/paginators-1.json @@ -0,0 +1,107 @@ +{ + "pagination": { + "DescribeDBEngineVersions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBEngineVersions" + }, + "DescribeDBInstances": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBInstances" + }, + "DescribeDBLogFiles": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DescribeDBLogFiles" + }, + "DescribeDBParameterGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBParameterGroups" + }, + "DescribeDBParameters": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Parameters" + }, + "DescribeDBSecurityGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBSecurityGroups" + }, + "DescribeDBSnapshots": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBSnapshots" + }, + "DescribeDBSubnetGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBSubnetGroups" + }, + "DescribeEngineDefaultParameters": { + "input_token": "Marker", + "output_token": "EngineDefaults.Marker", + "limit_key": "MaxRecords", + "result_key": "EngineDefaults.Parameters" + }, + "DescribeEventSubscriptions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "EventSubscriptionsList" + }, + "DescribeEvents": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Events" + }, + "DescribeOptionGroupOptions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "OptionGroupOptions" + }, + "DescribeOptionGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "OptionGroupsList" + }, + "DescribeOrderableDBInstanceOptions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "OrderableDBInstanceOptions" + }, + "DescribeReservedDBInstances": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ReservedDBInstances" + }, + "DescribeReservedDBInstancesOfferings": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ReservedDBInstancesOfferings" + }, + "DownloadDBLogFilePortion": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "NumberOfLines", + "more_results": "AdditionalDataPending", + "result_key": "LogFileData" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds/2014-09-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-09-01/service-2.json.gz new file mode 100644 index 0000000..95724fd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-09-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds/2014-09-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-09-01/waiters-2.json new file mode 100644 index 0000000..b015007 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-09-01/waiters-2.json @@ -0,0 +1,97 @@ +{ + "version": 2, + "waiters": { + "DBInstanceAvailable": { + "delay": 30, + "operation": "DescribeDBInstances", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "failed", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "incompatible-restore", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "incompatible-parameters", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "incompatible-parameters", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "incompatible-restore", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + } + ] + }, + "DBInstanceDeleted": { + "delay": 30, + "operation": "DescribeDBInstances", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "deleted", + "matcher": "pathAll", + "state": "success", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "creating", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "modifying", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "rebooting", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + }, + { + "expected": "resetting-master-credentials", + "matcher": "pathAny", + "state": "failure", + "argument": "DBInstances[].DBInstanceStatus" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..bab9cec Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/examples-1.json new file mode 100644 index 0000000..e72a328 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/examples-1.json @@ -0,0 +1,1951 @@ +{ + "version": "1.0", + "examples": { + "AddSourceIdentifierToSubscription": [ + { + "input": { + "SourceIdentifier": "mymysqlinstance", + "SubscriptionName": "mymysqleventsubscription" + }, + "output": { + "EventSubscription": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example add a source identifier to an event notification subscription.", + "id": "add-source-identifier-to-subscription-93fb6a15-0a59-4577-a7b5-e12db9752c14", + "title": "To add a source identifier to an event notification subscription" + } + ], + "AddTagsToResource": [ + { + "input": { + "ResourceName": "arn:aws:rds:us-east-1:992648334831:og:mymysqloptiongroup", + "Tags": [ + { + "Key": "Staging", + "Value": "LocationDB" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds a tag to an option group.", + "id": "add-tags-to-resource-fa99ef50-228b-449d-b893-ca4d4e9768ab", + "title": "To add tags to a resource" + } + ], + "ApplyPendingMaintenanceAction": [ + { + "input": { + "ApplyAction": "system-update", + "OptInType": "immediate", + "ResourceIdentifier": "arn:aws:rds:us-east-1:992648334831:db:mymysqlinstance" + }, + "output": { + "ResourcePendingMaintenanceActions": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example immediately applies a pending system update to a DB instance.", + "id": "apply-pending-maintenance-action-2a026047-8bbb-47fc-b695-abad9f308c24", + "title": "To apply a pending maintenance action" + } + ], + "AuthorizeDBSecurityGroupIngress": [ + { + "input": { + "CIDRIP": "203.0.113.5/32", + "DBSecurityGroupName": "mydbsecuritygroup" + }, + "output": { + "DBSecurityGroup": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example authorizes access to the specified security group by the specified CIDR block.", + "id": "authorize-db-security-group-ingress-ebf9ab91-8912-4b07-a32e-ca150668164f", + "title": "To authorize DB security group integress" + } + ], + "CopyDBClusterParameterGroup": [ + { + "input": { + "SourceDBClusterParameterGroupIdentifier": "mydbclusterparametergroup", + "TargetDBClusterParameterGroupDescription": "My DB cluster parameter group copy", + "TargetDBClusterParameterGroupIdentifier": "mydbclusterparametergroup-copy" + }, + "output": { + "DBClusterParameterGroup": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example copies a DB cluster parameter group.", + "id": "copy-db-cluster-parameter-group-6fefaffe-cde9-4dba-9f0b-d3f593572fe4", + "title": "To copy a DB cluster parameter group" + } + ], + "CopyDBClusterSnapshot": [ + { + "input": { + "SourceDBClusterSnapshotIdentifier": "rds:sample-cluster-2016-09-14-10-38", + "TargetDBClusterSnapshotIdentifier": "cluster-snapshot-copy-1" + }, + "output": { + "DBClusterSnapshot": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example copies an automated snapshot of a DB cluster to a new DB cluster snapshot.", + "id": "to-copy-a-db-cluster-snapshot-1473879770564", + "title": "To copy a DB cluster snapshot" + } + ], + "CopyDBParameterGroup": [ + { + "input": { + "SourceDBParameterGroupIdentifier": "mymysqlparametergroup", + "TargetDBParameterGroupDescription": "My MySQL parameter group copy", + "TargetDBParameterGroupIdentifier": "mymysqlparametergroup-copy" + }, + "output": { + "DBParameterGroup": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example copies a DB parameter group.", + "id": "copy-db-parameter-group-610d4dba-2c87-467f-ae5d-edd7f8e47349", + "title": "To copy a DB parameter group" + } + ], + "CopyDBSnapshot": [ + { + "input": { + "SourceDBSnapshotIdentifier": "mydbsnapshot", + "TargetDBSnapshotIdentifier": "mydbsnapshot-copy" + }, + "output": { + "DBSnapshot": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example copies a DB snapshot.", + "id": "copy-db-snapshot-1b2f0210-bc67-415d-9822-6eecf447dc86", + "title": "To copy a DB snapshot" + } + ], + "CopyOptionGroup": [ + { + "input": { + "SourceOptionGroupIdentifier": "mymysqloptiongroup", + "TargetOptionGroupDescription": "My MySQL option group copy", + "TargetOptionGroupIdentifier": "mymysqloptiongroup-copy" + }, + "output": { + "OptionGroup": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example copies an option group.", + "id": "copy-option-group-8d5c01c3-8846-4e9c-a4b0-1b7237f7d0ec", + "title": "To copy an option group" + } + ], + "CreateDBCluster": [ + { + "input": { + "AvailabilityZones": [ + "us-east-1a" + ], + "BackupRetentionPeriod": 1, + "DBClusterIdentifier": "mydbcluster", + "DBClusterParameterGroupName": "mydbclusterparametergroup", + "DatabaseName": "myauroradb", + "Engine": "aurora", + "EngineVersion": "5.6.10a", + "MasterUserPassword": "mypassword", + "MasterUsername": "myuser", + "Port": 3306, + "StorageEncrypted": true + }, + "output": { + "DBCluster": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a DB cluster.", + "id": "create-db-cluster-423b998d-eba9-40dd-8e19-96c5b6e5f31d", + "title": "To create a DB cluster" + } + ], + "CreateDBClusterParameterGroup": [ + { + "input": { + "DBClusterParameterGroupName": "mydbclusterparametergroup", + "DBParameterGroupFamily": "aurora5.6", + "Description": "My DB cluster parameter group" + }, + "output": { + "DBClusterParameterGroup": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a DB cluster parameter group.", + "id": "create-db-cluster-parameter-group-8eb1c3ae-1965-4262-afe3-ee134c4430b1", + "title": "To create a DB cluster parameter group" + } + ], + "CreateDBClusterSnapshot": [ + { + "input": { + "DBClusterIdentifier": "mydbcluster", + "DBClusterSnapshotIdentifier": "mydbclustersnapshot" + }, + "output": { + "DBClusterSnapshot": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a DB cluster snapshot.", + "id": "create-db-cluster-snapshot-", + "title": "To create a DB cluster snapshot" + } + ], + "CreateDBInstance": [ + { + "input": { + "AllocatedStorage": 5, + "DBInstanceClass": "db.t2.micro", + "DBInstanceIdentifier": "mymysqlinstance", + "Engine": "MySQL", + "MasterUserPassword": "MyPassword", + "MasterUsername": "MyUser" + }, + "output": { + "DBInstance": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a DB instance.", + "id": "create-db-instance-57eb5d16-8bf8-4c84-9709-1700322b37b9", + "title": "To create a DB instance." + } + ], + "CreateDBInstanceReadReplica": [ + { + "input": { + "AvailabilityZone": "us-east-1a", + "CopyTagsToSnapshot": true, + "DBInstanceClass": "db.t2.micro", + "DBInstanceIdentifier": "mydbreadreplica", + "PubliclyAccessible": true, + "SourceDBInstanceIdentifier": "mymysqlinstance", + "StorageType": "gp2", + "Tags": [ + { + "Key": "mydbreadreplicakey", + "Value": "mydbreadreplicavalue" + } + ] + }, + "output": { + "DBInstance": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a DB instance read replica.", + "id": "create-db-instance-read-replica-81b41cd5-2871-4dae-bc59-3e264449d5fe", + "title": "To create a DB instance read replica." + } + ], + "CreateDBParameterGroup": [ + { + "input": { + "DBParameterGroupFamily": "mysql5.6", + "DBParameterGroupName": "mymysqlparametergroup", + "Description": "My MySQL parameter group" + }, + "output": { + "DBParameterGroup": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a DB parameter group.", + "id": "create-db-parameter-group-42afcc37-12e9-4b6a-a55c-b8a141246e87", + "title": "To create a DB parameter group." + } + ], + "CreateDBSecurityGroup": [ + { + "input": { + "DBSecurityGroupDescription": "My DB security group", + "DBSecurityGroupName": "mydbsecuritygroup" + }, + "output": { + "DBSecurityGroup": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a DB security group.", + "id": "create-db-security-group-41b6786a-539e-42a5-a645-a8bc3cf99353", + "title": "To create a DB security group." + } + ], + "CreateDBSnapshot": [ + { + "input": { + "DBInstanceIdentifier": "mymysqlinstance", + "DBSnapshotIdentifier": "mydbsnapshot" + }, + "output": { + "DBSnapshot": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a DB snapshot.", + "id": "create-db-snapshot-e10e0e2c-9ac4-426d-9b17-6b6a3e382ce2", + "title": "To create a DB snapshot." + } + ], + "CreateDBSubnetGroup": [ + { + "input": { + "DBSubnetGroupDescription": "My DB subnet group", + "DBSubnetGroupName": "mydbsubnetgroup", + "SubnetIds": [ + "subnet-1fab8a69", + "subnet-d43a468c" + ] + }, + "output": { + "DBSubnetGroup": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a DB subnet group.", + "id": "create-db-subnet-group-c3d162c2-0ec4-4955-ba89-18967615fdb8", + "title": "To create a DB subnet group." + } + ], + "CreateEventSubscription": [ + { + "input": { + "Enabled": true, + "EventCategories": [ + "availability" + ], + "SnsTopicArn": "arn:aws:sns:us-east-1:992648334831:MyDemoSNSTopic", + "SourceIds": [ + "mymysqlinstance" + ], + "SourceType": "db-instance", + "SubscriptionName": "mymysqleventsubscription" + }, + "output": { + "EventSubscription": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an event notification subscription.", + "id": "create-event-subscription-00dd0ee6-0e0f-4a38-ae83-e5f2ded5f69a", + "title": "To create an event notification subscription" + } + ], + "CreateOptionGroup": [ + { + "input": { + "EngineName": "MySQL", + "MajorEngineVersion": "5.6", + "OptionGroupDescription": "My MySQL 5.6 option group", + "OptionGroupName": "mymysqloptiongroup" + }, + "output": { + "OptionGroup": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an option group.", + "id": "create-option-group-a7708c87-1b79-4a5e-a762-21cf8fc62b78", + "title": "To create an option group" + } + ], + "DeleteDBCluster": [ + { + "input": { + "DBClusterIdentifier": "mydbcluster", + "SkipFinalSnapshot": true + }, + "output": { + "DBCluster": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified DB cluster.", + "id": "delete-db-cluster-927fc2c8-6c67-4075-b1ba-75490be0f7d6", + "title": "To delete a DB cluster." + } + ], + "DeleteDBClusterParameterGroup": [ + { + "input": { + "DBClusterParameterGroupName": "mydbclusterparametergroup" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified DB cluster parameter group.", + "id": "delete-db-cluster-parameter-group-364f5555-ba0a-4cc8-979c-e769098924fc", + "title": "To delete a DB cluster parameter group." + } + ], + "DeleteDBClusterSnapshot": [ + { + "input": { + "DBClusterSnapshotIdentifier": "mydbclustersnapshot" + }, + "output": { + "DBClusterSnapshot": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified DB cluster snapshot.", + "id": "delete-db-cluster-snapshot-c67e0d95-670e-4fb5-af90-6d9a70a91b07", + "title": "To delete a DB cluster snapshot." + } + ], + "DeleteDBInstance": [ + { + "input": { + "DBInstanceIdentifier": "mymysqlinstance", + "SkipFinalSnapshot": true + }, + "output": { + "DBInstance": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified DB instance.", + "id": "delete-db-instance-4412e650-949c-488a-b32a-7d3038ebccc4", + "title": "To delete a DB instance." + } + ], + "DeleteDBParameterGroup": [ + { + "input": { + "DBParameterGroupName": "mydbparamgroup3" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a DB parameter group.", + "id": "to-delete-a-db-parameter-group-1473888796509", + "title": "To delete a DB parameter group" + } + ], + "DeleteDBSecurityGroup": [ + { + "input": { + "DBSecurityGroupName": "mysecgroup" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a DB security group.", + "id": "to-delete-a-db-security-group-1473960141889", + "title": "To delete a DB security group" + } + ], + "DeleteDBSnapshot": [ + { + "input": { + "DBSnapshotIdentifier": "mydbsnapshot" + }, + "output": { + "DBSnapshot": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified DB snapshot.", + "id": "delete-db-snapshot-505d6b4e-8ced-479c-856a-c460a33fe07b", + "title": "To delete a DB cluster snapshot." + } + ], + "DeleteDBSubnetGroup": [ + { + "input": { + "DBSubnetGroupName": "mydbsubnetgroup" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified DB subnetgroup.", + "id": "delete-db-subnet-group-4ae00375-511e-443d-a01d-4b9f552244aa", + "title": "To delete a DB subnet group." + } + ], + "DeleteEventSubscription": [ + { + "input": { + "SubscriptionName": "myeventsubscription" + }, + "output": { + "EventSubscription": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified DB event subscription.", + "id": "delete-db-event-subscription-d33567e3-1d5d-48ff-873f-0270453f4a75", + "title": "To delete a DB event subscription." + } + ], + "DeleteOptionGroup": [ + { + "input": { + "OptionGroupName": "mydboptiongroup" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified option group.", + "id": "delete-db-option-group-578be2be-3095-431a-9ea4-9a3c3b0daef4", + "title": "To delete an option group." + } + ], + "DescribeAccountAttributes": [ + { + "input": { + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists account attributes.", + "id": "describe-account-attributes-683d3ff7-5524-421a-8da5-e88f1ea2222b", + "title": "To list account attributes" + } + ], + "DescribeCertificates": [ + { + "input": { + "CertificateIdentifier": "rds-ca-2015", + "MaxRecords": 20 + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists up to 20 certificates for the specified certificate identifier.", + "id": "describe-certificates-9d71a70d-7908-4444-b43f-321d842c62dc", + "title": "To list certificates" + } + ], + "DescribeDBClusterParameterGroups": [ + { + "input": { + "DBClusterParameterGroupName": "mydbclusterparametergroup" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists settings for the specified DB cluster parameter group.", + "id": "describe-db-cluster-parameter-groups-cf9c6e66-664e-4f57-8e29-a9080abfc013", + "title": "To list DB cluster parameter group settings" + } + ], + "DescribeDBClusterParameters": [ + { + "input": { + "DBClusterParameterGroupName": "mydbclusterparametergroup", + "Source": "system" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists system parameters for the specified DB cluster parameter group.", + "id": "describe-db-cluster-parameters-98043c28-e489-41a7-b118-bfd96dc779a1", + "title": "To list DB cluster parameters" + } + ], + "DescribeDBClusterSnapshotAttributes": [ + { + "input": { + "DBClusterSnapshotIdentifier": "mydbclustersnapshot" + }, + "output": { + "DBClusterSnapshotAttributesResult": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists attributes for the specified DB cluster snapshot.", + "id": "describe-db-cluster-snapshot-attributes-6752ade3-0c7b-4b06-a8e4-b76bf4e2d3571", + "title": "To list DB cluster snapshot attributes" + } + ], + "DescribeDBClusterSnapshots": [ + { + "input": { + "DBClusterSnapshotIdentifier": "mydbclustersnapshot", + "SnapshotType": "manual" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists settings for the specified, manually-created cluster snapshot.", + "id": "describe-db-cluster-snapshots-52f38af1-3431-4a51-9a6a-e6bb8c961b32", + "title": "To list DB cluster snapshots" + } + ], + "DescribeDBClusters": [ + { + "input": { + "DBClusterIdentifier": "mynewdbcluster" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists settings for the specified DB cluster.", + "id": "describe-db-clusters-7aae8861-cb95-4b3b-9042-f62df7698635", + "title": "To list DB clusters" + } + ], + "DescribeDBEngineVersions": [ + { + "input": { + "DBParameterGroupFamily": "mysql5.6", + "DefaultOnly": true, + "Engine": "mysql", + "EngineVersion": "5.6", + "ListSupportedCharacterSets": true + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists settings for the specified DB engine version.", + "id": "describe-db-engine-versions-8e698cf2-2162-425a-a854-111cdaceb52b", + "title": "To list DB engine version settings" + } + ], + "DescribeDBInstances": [ + { + "input": { + "DBInstanceIdentifier": "mymysqlinstance" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists settings for the specified DB instance.", + "id": "describe-db-instances-0e11a8c5-4ec3-4463-8cbf-f7254d04c4fc", + "title": "To list DB instance settings" + } + ], + "DescribeDBLogFiles": [ + { + "input": { + "DBInstanceIdentifier": "mymysqlinstance", + "FileLastWritten": 1470873600000, + "FileSize": 0, + "FilenameContains": "error" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists matching log file names for the specified DB instance, file name pattern, last write date in POSIX time with milleseconds, and minimum file size.", + "id": "describe-db-log-files-5f002d8d-5c1d-44c2-b5f4-bd284c0f1285", + "title": "To list DB log file names" + } + ], + "DescribeDBParameterGroups": [ + { + "input": { + "DBParameterGroupName": "mymysqlparametergroup" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists information about the specified DB parameter group.", + "id": "describe-db-parameter-groups-", + "title": "To list information about DB parameter groups" + } + ], + "DescribeDBParameters": [ + { + "input": { + "DBParameterGroupName": "mymysqlparametergroup", + "MaxRecords": 20, + "Source": "system" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists information for up to the first 20 system parameters for the specified DB parameter group.", + "id": "describe-db-parameters-09db4201-ef4f-4d97-a4b5-d71c0715b901", + "title": "To list information about DB parameters" + } + ], + "DescribeDBSecurityGroups": [ + { + "input": { + "DBSecurityGroupName": "mydbsecuritygroup" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists settings for the specified security group.", + "id": "describe-db-security-groups-66fe9ea1-17dd-4275-b82e-f771cee0c849", + "title": "To list DB security group settings" + } + ], + "DescribeDBSnapshotAttributes": [ + { + "input": { + "DBSnapshotIdentifier": "mydbsnapshot" + }, + "output": { + "DBSnapshotAttributesResult": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists attributes for the specified DB snapshot.", + "id": "describe-db-snapshot-attributes-1d4fb750-34f6-4e43-8b3d-b2751d796a95", + "title": "To list DB snapshot attributes" + } + ], + "DescribeDBSnapshots": [ + { + "input": { + "DBInstanceIdentifier": "mymysqlinstance", + "IncludePublic": false, + "IncludeShared": true, + "SnapshotType": "manual" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists all manually-created, shared snapshots for the specified DB instance.", + "id": "describe-db-snapshots-2c935989-a1ef-4c85-aea4-1d0f45f17f26", + "title": "To list DB snapshot attributes" + } + ], + "DescribeDBSubnetGroups": [ + { + "input": { + "DBSubnetGroupName": "mydbsubnetgroup" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists information about the specified DB subnet group.", + "id": "describe-db-subnet-groups-1d97b340-682f-4dd6-9653-8ed72a8d1221", + "title": "To list information about DB subnet groups" + } + ], + "DescribeEngineDefaultClusterParameters": [ + { + "input": { + "DBParameterGroupFamily": "aurora5.6" + }, + "output": { + "EngineDefaults": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists default parameters for the specified DB cluster engine.", + "id": "describe-engine-default-cluster-parameters-f130374a-7bee-434b-b51d-da20b6e000e0", + "title": "To list default parameters for a DB cluster engine" + } + ], + "DescribeEngineDefaultParameters": [ + { + "input": { + "DBParameterGroupFamily": "mysql5.6" + }, + "output": { + "EngineDefaults": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists default parameters for the specified DB engine.", + "id": "describe-engine-default-parameters-35d5108e-1d44-4fac-8aeb-04b8fdfface1", + "title": "To list default parameters for a DB engine" + } + ], + "DescribeEventCategories": [ + { + "input": { + "SourceType": "db-instance" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists all DB instance event categories.", + "id": "describe-event-categories-97bd4c77-12da-4be6-b42f-edf77771428b", + "title": "To list event categories." + } + ], + "DescribeEventSubscriptions": [ + { + "input": { + "SubscriptionName": "mymysqleventsubscription" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists information for the specified DB event notification subscription.", + "id": "describe-event-subscriptions-11184a82-e58a-4d0c-b558-f3a7489e0850", + "title": "To list information about DB event notification subscriptions" + } + ], + "DescribeEvents": [ + { + "input": { + "Duration": 10080, + "EventCategories": [ + "backup" + ], + "SourceIdentifier": "mymysqlinstance", + "SourceType": "db-instance" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists information for all backup-related events for the specified DB instance for the past 7 days (7 days * 24 hours * 60 minutes = 10,080 minutes).", + "id": "describe-events-3836e5ed-3913-4f76-8452-c77fcad5016b", + "title": "To list information about events" + } + ], + "DescribeOptionGroupOptions": [ + { + "input": { + "EngineName": "mysql", + "MajorEngineVersion": "5.6" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists information for all option group options for the specified DB engine.", + "id": "describe-option-group-options-30d735a4-81f1-49e4-b3f2-5dc45d50c8ed", + "title": "To list information about DB option group options" + } + ], + "DescribeOptionGroups": [ + { + "input": { + "EngineName": "mysql", + "MajorEngineVersion": "5.6" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists information for all option groups for the specified DB engine.", + "id": "describe-option-groups-4ef478a1-66d5-45f2-bec3-e608720418a4", + "title": "To list information about DB option groups" + } + ], + "DescribeOrderableDBInstanceOptions": [ + { + "input": { + "DBInstanceClass": "db.t2.micro", + "Engine": "mysql", + "EngineVersion": "5.6.27", + "LicenseModel": "general-public-license", + "Vpc": true + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists information for all orderable DB instance options for the specified DB engine, engine version, DB instance class, license model, and VPC settings.", + "id": "describe-orderable-db-instance-options-7444d3ed-82eb-42b9-9ed9-896b8c27a782", + "title": "To list information about orderable DB instance options" + } + ], + "DescribePendingMaintenanceActions": [ + { + "input": { + "ResourceIdentifier": "arn:aws:rds:us-east-1:992648334831:db:mymysqlinstance" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists information for all pending maintenance actions for the specified DB instance.", + "id": "describe-pending-maintenance-actions-e6021f7e-58ae-49cc-b874-11996176835c", + "title": "To list information about pending maintenance actions" + } + ], + "DescribeReservedDBInstances": [ + { + "input": { + "DBInstanceClass": "db.t2.micro", + "Duration": "1y", + "MultiAZ": false, + "OfferingType": "No Upfront", + "ProductDescription": "mysql" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists information for all reserved DB instances for the specified DB instance class, duration, product, offering type, and availability zone settings.", + "id": "describe-reserved-db-instances-d45adaca-2e30-407c-a0f3-aa7b98bea17f", + "title": "To list information about reserved DB instances" + } + ], + "DescribeReservedDBInstancesOfferings": [ + { + "input": { + "DBInstanceClass": "db.t2.micro", + "Duration": "1y", + "MultiAZ": false, + "OfferingType": "No Upfront", + "ProductDescription": "mysql" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists information for all reserved DB instance offerings for the specified DB instance class, duration, product, offering type, and availability zone settings.", + "id": "describe-reserved-db-instances-offerings-9de7d1fd-d6a6-4a72-84ae-b2ef58d47d8d", + "title": "To list information about reserved DB instance offerings" + } + ], + "DescribeSourceRegions": [ + { + "input": { + }, + "output": { + "SourceRegions": [ + { + "Endpoint": "https://rds.ap-northeast-1.amazonaws.com", + "RegionName": "ap-northeast-1", + "Status": "available" + }, + { + "Endpoint": "https://rds.ap-northeast-2.amazonaws.com", + "RegionName": "ap-northeast-2", + "Status": "available" + }, + { + "Endpoint": "https://rds.ap-south-1.amazonaws.com", + "RegionName": "ap-south-1", + "Status": "available" + }, + { + "Endpoint": "https://rds.ap-southeast-1.amazonaws.com", + "RegionName": "ap-southeast-1", + "Status": "available" + }, + { + "Endpoint": "https://rds.ap-southeast-2.amazonaws.com", + "RegionName": "ap-southeast-2", + "Status": "available" + }, + { + "Endpoint": "https://rds.eu-central-1.amazonaws.com", + "RegionName": "eu-central-1", + "Status": "available" + }, + { + "Endpoint": "https://rds.eu-west-1.amazonaws.com", + "RegionName": "eu-west-1", + "Status": "available" + }, + { + "Endpoint": "https://rds.sa-east-1.amazonaws.com", + "RegionName": "sa-east-1", + "Status": "available" + }, + { + "Endpoint": "https://rds.us-west-1.amazonaws.com", + "RegionName": "us-west-1", + "Status": "available" + }, + { + "Endpoint": "https://rds.us-west-2.amazonaws.com", + "RegionName": "us-west-2", + "Status": "available" + } + ] + }, + "comments": { + }, + "description": "To list the AWS regions where a Read Replica can be created.", + "id": "to-describe-source-regions-1473457722410", + "title": "To describe source regions" + } + ], + "DownloadDBLogFilePortion": [ + { + "input": { + "DBInstanceIdentifier": "mymysqlinstance", + "LogFileName": "mysqlUpgrade" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists information for the specified log file for the specified DB instance.", + "id": "download-db-log-file-portion-54a82731-a441-4fc7-a010-8eccae6fa202", + "title": "To list information about DB log files" + } + ], + "FailoverDBCluster": [ + { + "input": { + "DBClusterIdentifier": "myaurorainstance-cluster", + "TargetDBInstanceIdentifier": "myaurorareplica" + }, + "output": { + "DBCluster": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example performs a failover for the specified DB cluster to the specified DB instance.", + "id": "failover-db-cluster-9e7f2f93-d98c-42c7-bb0e-d6c485c096d6", + "title": "To perform a failover for a DB cluster" + } + ], + "ListTagsForResource": [ + { + "input": { + "ResourceName": "arn:aws:rds:us-east-1:992648334831:og:mymysqloptiongroup" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists information about all tags associated with the specified DB option group.", + "id": "list-tags-for-resource-8401f3c2-77cd-4f90-bfd5-b523f0adcc2f", + "title": "To list information about tags associated with a resource" + } + ], + "ModifyDBCluster": [ + { + "input": { + "ApplyImmediately": true, + "DBClusterIdentifier": "mydbcluster", + "MasterUserPassword": "mynewpassword", + "NewDBClusterIdentifier": "mynewdbcluster", + "PreferredBackupWindow": "04:00-04:30", + "PreferredMaintenanceWindow": "Tue:05:00-Tue:05:30" + }, + "output": { + "DBCluster": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example changes the specified settings for the specified DB cluster.", + "id": "modify-db-cluster-a370ee1b-768d-450a-853b-707cb1ab663d", + "title": "To change DB cluster settings" + } + ], + "ModifyDBClusterParameterGroup": [ + { + "input": { + "DBClusterParameterGroupName": "mydbclusterparametergroup", + "Parameters": [ + { + "ApplyMethod": "immediate", + "ParameterName": "time_zone", + "ParameterValue": "America/Phoenix" + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example immediately changes the specified setting for the specified DB cluster parameter group.", + "id": "modify-db-cluster-parameter-group-f9156bc9-082a-442e-8d12-239542c1a113", + "title": "To change DB cluster parameter group settings" + } + ], + "ModifyDBClusterSnapshotAttribute": [ + { + "input": { + "AttributeName": "restore", + "DBClusterSnapshotIdentifier": "manual-cluster-snapshot1", + "ValuesToAdd": [ + "123451234512", + "123456789012" + ], + "ValuesToRemove": [ + "all" + ] + }, + "output": { + "DBClusterSnapshotAttributesResult": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example gives two AWS accounts access to a manual DB cluster snapshot and ensures that the DB cluster snapshot is private by removing the value \"all\".", + "id": "to-add-or-remove-access-to-a-manual-db-cluster-snapshot-1473889426431", + "title": "To add or remove access to a manual DB cluster snapshot" + } + ], + "ModifyDBInstance": [ + { + "input": { + "AllocatedStorage": 10, + "ApplyImmediately": true, + "BackupRetentionPeriod": 1, + "DBInstanceClass": "db.t2.small", + "DBInstanceIdentifier": "mymysqlinstance", + "MasterUserPassword": "mynewpassword", + "PreferredBackupWindow": "04:00-04:30", + "PreferredMaintenanceWindow": "Tue:05:00-Tue:05:30" + }, + "output": { + "DBInstance": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example immediately changes the specified settings for the specified DB instance.", + "id": "modify-db-instance-6979a368-6254-467b-8a8d-61103f4fcde9", + "title": "To change DB instance settings" + } + ], + "ModifyDBParameterGroup": [ + { + "input": { + "DBParameterGroupName": "mymysqlparametergroup", + "Parameters": [ + { + "ApplyMethod": "immediate", + "ParameterName": "time_zone", + "ParameterValue": "America/Phoenix" + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example immediately changes the specified setting for the specified DB parameter group.", + "id": "modify-db-parameter-group-f3a4e52a-68e4-4b88-b559-f912d34c457a", + "title": "To change DB parameter group settings" + } + ], + "ModifyDBSnapshotAttribute": [ + { + "input": { + "AttributeName": "restore", + "DBSnapshotIdentifier": "mydbsnapshot", + "ValuesToAdd": [ + "all" + ] + }, + "output": { + "DBSnapshotAttributesResult": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds the specified attribute for the specified DB snapshot.", + "id": "modify-db-snapshot-attribute-2e66f120-2b21-4a7c-890b-4474da88bde6", + "title": "To change DB snapshot attributes" + } + ], + "ModifyDBSubnetGroup": [ + { + "input": { + "DBSubnetGroupName": "mydbsubnetgroup", + "SubnetIds": [ + "subnet-70e1975a", + "subnet-747a5c49" + ] + }, + "output": { + "DBSubnetGroup": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example changes the specified setting for the specified DB subnet group.", + "id": "modify-db-subnet-group-e34a97d9-8fe6-4239-a4ed-ad6e73a956b0", + "title": "To change DB subnet group settings" + } + ], + "ModifyEventSubscription": [ + { + "input": { + "Enabled": true, + "EventCategories": [ + "deletion", + "low storage" + ], + "SourceType": "db-instance", + "SubscriptionName": "mymysqleventsubscription" + }, + "output": { + "EventSubscription": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example changes the specified setting for the specified event notification subscription.", + "id": "modify-event-subscription-405ac869-1f02-42cd-b8f4-6950a435f30e", + "title": "To change event notification subscription settings" + } + ], + "ModifyOptionGroup": [ + { + "input": { + "ApplyImmediately": true, + "OptionGroupName": "myawsuser-og02", + "OptionsToInclude": [ + { + "DBSecurityGroupMemberships": [ + "default" + ], + "OptionName": "MEMCACHED" + } + ] + }, + "output": { + "OptionGroup": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example adds an option to an option group.", + "id": "to-modify-an-option-group-1473890247875", + "title": "To modify an option group" + } + ], + "PromoteReadReplica": [ + { + "input": { + "BackupRetentionPeriod": 1, + "DBInstanceIdentifier": "mydbreadreplica", + "PreferredBackupWindow": "03:30-04:00" + }, + "output": { + "DBInstance": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example promotes the specified read replica and sets its backup retention period and preferred backup window.", + "id": "promote-read-replica-cc580039-c55d-4035-838a-def4a1ae4181", + "title": "To promote a read replica" + } + ], + "PurchaseReservedDBInstancesOffering": [ + { + "input": { + "ReservedDBInstanceId": "myreservationid", + "ReservedDBInstancesOfferingId": "fb29428a-646d-4390-850e-5fe89926e727" + }, + "output": { + "ReservedDBInstance": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example purchases a reserved DB instance offering that matches the specified settings.", + "id": "purchase-reserved-db-instances-offfering-f423c736-8413-429b-ba13-850fd4fa4dcd", + "title": "To purchase a reserved DB instance offering" + } + ], + "RebootDBInstance": [ + { + "input": { + "DBInstanceIdentifier": "mymysqlinstance", + "ForceFailover": false + }, + "output": { + "DBInstance": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example reboots the specified DB instance without forcing a failover.", + "id": "reboot-db-instance-b9ce8a0a-2920-451d-a1f3-01d288aa7366", + "title": "To reboot a DB instance" + } + ], + "RemoveSourceIdentifierFromSubscription": [ + { + "input": { + "SourceIdentifier": "mymysqlinstance", + "SubscriptionName": "myeventsubscription" + }, + "output": { + "EventSubscription": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example removes the specified source identifier from the specified DB event subscription.", + "id": "remove-source-identifier-from-subscription-30d25493-c19d-4cf7-b4e5-68371d0d8770", + "title": "To remove a source identifier from a DB event subscription" + } + ], + "RemoveTagsFromResource": [ + { + "input": { + "ResourceName": "arn:aws:rds:us-east-1:992648334831:og:mydboptiongroup", + "TagKeys": [ + "MyKey" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example removes the specified tag associated with the specified DB option group.", + "id": "remove-tags-from-resource-49f00574-38f6-4d01-ac89-d3c668449ce3", + "title": "To remove tags from a resource" + } + ], + "ResetDBClusterParameterGroup": [ + { + "input": { + "DBClusterParameterGroupName": "mydbclusterparametergroup", + "ResetAllParameters": true + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example resets all parameters for the specified DB cluster parameter group to their default values.", + "id": "reset-db-cluster-parameter-group-b04aeaf7-7f73-49e1-9bb4-857573ea3ee4", + "title": "To reset the values of a DB cluster parameter group" + } + ], + "ResetDBParameterGroup": [ + { + "input": { + "DBParameterGroupName": "mydbparametergroup", + "ResetAllParameters": true + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example resets all parameters for the specified DB parameter group to their default values.", + "id": "reset-db-parameter-group-ed2ed723-de0d-4824-8af5-3c65fa130abf", + "title": "To reset the values of a DB parameter group" + } + ], + "RestoreDBClusterFromSnapshot": [ + { + "input": { + "DBClusterIdentifier": "restored-cluster1", + "Engine": "aurora", + "SnapshotIdentifier": "sample-cluster-snapshot1" + }, + "output": { + "DBCluster": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example restores an Amazon Aurora DB cluster from a DB cluster snapshot.", + "id": "to-restore-an-amazon-aurora-db-cluster-from-a-db-cluster-snapshot-1473958144325", + "title": "To restore an Amazon Aurora DB cluster from a DB cluster snapshot" + } + ], + "RestoreDBClusterToPointInTime": [ + { + "input": { + "DBClusterIdentifier": "sample-restored-cluster1", + "RestoreToTime": "2016-09-13T18:45:00Z", + "SourceDBClusterIdentifier": "sample-cluster1" + }, + "output": { + "DBCluster": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example restores a DB cluster to a new DB cluster at a point in time from the source DB cluster.", + "id": "to-restore-a-db-cluster-to-a-point-in-time-1473962082214", + "title": "To restore a DB cluster to a point in time." + } + ], + "RestoreDBInstanceFromDBSnapshot": [ + { + "input": { + "DBInstanceIdentifier": "mysqldb-restored", + "DBSnapshotIdentifier": "rds:mysqldb-2014-04-22-08-15" + }, + "output": { + "DBInstance": { + "AllocatedStorage": 200, + "AutoMinorVersionUpgrade": true, + "AvailabilityZone": "us-west-2b", + "BackupRetentionPeriod": 7, + "CACertificateIdentifier": "rds-ca-2015", + "CopyTagsToSnapshot": false, + "DBInstanceArn": "arn:aws:rds:us-west-2:123456789012:db:mysqldb-restored", + "DBInstanceClass": "db.t2.small", + "DBInstanceIdentifier": "mysqldb-restored", + "DBInstanceStatus": "available", + "DBName": "sample", + "DBParameterGroups": [ + { + "DBParameterGroupName": "default.mysql5.6", + "ParameterApplyStatus": "in-sync" + } + ], + "DBSecurityGroups": [ + + ], + "DBSubnetGroup": { + "DBSubnetGroupDescription": "default", + "DBSubnetGroupName": "default", + "SubnetGroupStatus": "Complete", + "Subnets": [ + { + "SubnetAvailabilityZone": { + "Name": "us-west-2a" + }, + "SubnetIdentifier": "subnet-77e8db03", + "SubnetStatus": "Active" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-west-2b" + }, + "SubnetIdentifier": "subnet-c39989a1", + "SubnetStatus": "Active" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-west-2c" + }, + "SubnetIdentifier": "subnet-4b267b0d", + "SubnetStatus": "Active" + } + ], + "VpcId": "vpc-c1c5b3a3" + }, + "DbInstancePort": 0, + "DbiResourceId": "db-VNZUCCBTEDC4WR7THXNJO72HVQ", + "DomainMemberships": [ + + ], + "Engine": "mysql", + "EngineVersion": "5.6.27", + "LicenseModel": "general-public-license", + "MasterUsername": "mymasteruser", + "MonitoringInterval": 0, + "MultiAZ": false, + "OptionGroupMemberships": [ + { + "OptionGroupName": "default:mysql-5-6", + "Status": "in-sync" + } + ], + "PendingModifiedValues": { + }, + "PreferredBackupWindow": "12:58-13:28", + "PreferredMaintenanceWindow": "tue:10:16-tue:10:46", + "PubliclyAccessible": true, + "ReadReplicaDBInstanceIdentifiers": [ + + ], + "StorageEncrypted": false, + "StorageType": "gp2", + "VpcSecurityGroups": [ + { + "Status": "active", + "VpcSecurityGroupId": "sg-e5e5b0d2" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example restores a DB instance from a DB snapshot.", + "id": "to-restore-a-db-instance-from-a-db-snapshot-1473961657311", + "title": "To restore a DB instance from a DB snapshot." + } + ], + "RestoreDBInstanceToPointInTime": [ + { + "input": { + "RestoreTime": "2016-09-13T18:45:00Z", + "SourceDBInstanceIdentifier": "mysql-sample", + "TargetDBInstanceIdentifier": "mysql-sample-restored" + }, + "output": { + "DBInstance": { + "AllocatedStorage": 200, + "AutoMinorVersionUpgrade": true, + "AvailabilityZone": "us-west-2b", + "BackupRetentionPeriod": 7, + "CACertificateIdentifier": "rds-ca-2015", + "CopyTagsToSnapshot": false, + "DBInstanceArn": "arn:aws:rds:us-west-2:123456789012:db:mysql-sample-restored", + "DBInstanceClass": "db.t2.small", + "DBInstanceIdentifier": "mysql-sample-restored", + "DBInstanceStatus": "available", + "DBName": "sample", + "DBParameterGroups": [ + { + "DBParameterGroupName": "default.mysql5.6", + "ParameterApplyStatus": "in-sync" + } + ], + "DBSecurityGroups": [ + + ], + "DBSubnetGroup": { + "DBSubnetGroupDescription": "default", + "DBSubnetGroupName": "default", + "SubnetGroupStatus": "Complete", + "Subnets": [ + { + "SubnetAvailabilityZone": { + "Name": "us-west-2a" + }, + "SubnetIdentifier": "subnet-77e8db03", + "SubnetStatus": "Active" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-west-2b" + }, + "SubnetIdentifier": "subnet-c39989a1", + "SubnetStatus": "Active" + }, + { + "SubnetAvailabilityZone": { + "Name": "us-west-2c" + }, + "SubnetIdentifier": "subnet-4b267b0d", + "SubnetStatus": "Active" + } + ], + "VpcId": "vpc-c1c5b3a3" + }, + "DbInstancePort": 0, + "DbiResourceId": "db-VNZUCCBTEDC4WR7THXNJO72HVQ", + "DomainMemberships": [ + + ], + "Engine": "mysql", + "EngineVersion": "5.6.27", + "LicenseModel": "general-public-license", + "MasterUsername": "mymasteruser", + "MonitoringInterval": 0, + "MultiAZ": false, + "OptionGroupMemberships": [ + { + "OptionGroupName": "default:mysql-5-6", + "Status": "in-sync" + } + ], + "PendingModifiedValues": { + }, + "PreferredBackupWindow": "12:58-13:28", + "PreferredMaintenanceWindow": "tue:10:16-tue:10:46", + "PubliclyAccessible": true, + "ReadReplicaDBInstanceIdentifiers": [ + + ], + "StorageEncrypted": false, + "StorageType": "gp2", + "VpcSecurityGroups": [ + { + "Status": "active", + "VpcSecurityGroupId": "sg-e5e5b0d2" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example restores a DB instance to a new DB instance at a point in time from the source DB instance.", + "id": "to-restore-a-db-instance-to-a-point-in-time-1473962652154", + "title": "To restore a DB instance to a point in time." + } + ], + "RevokeDBSecurityGroupIngress": [ + { + "input": { + "CIDRIP": "203.0.113.5/32", + "DBSecurityGroupName": "mydbsecuritygroup" + }, + "output": { + "DBSecurityGroup": { + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example revokes ingress for the specified CIDR block associated with the specified DB security group.", + "id": "revoke-db-security-group-ingress-ce5b2c1c-bd4e-4809-b04a-6d78ec448813", + "title": "To revoke ingress for a DB security group" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/paginators-1.json new file mode 100644 index 0000000..844d9f9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/paginators-1.json @@ -0,0 +1,257 @@ +{ + "pagination": { + "DescribeCertificates": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Certificates" + }, + "DescribeDBClusterBacktracks": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBClusterBacktracks" + }, + "DescribeDBClusterParameterGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBClusterParameterGroups" + }, + "DescribeDBClusterParameters": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Parameters" + }, + "DescribeDBClusterSnapshots": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBClusterSnapshots" + }, + "DescribeDBClusters": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBClusters" + }, + "DescribeDBEngineVersions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBEngineVersions" + }, + "DescribeDBInstanceAutomatedBackups": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBInstanceAutomatedBackups" + }, + "DescribeDBInstances": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBInstances" + }, + "DescribeDBLogFiles": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DescribeDBLogFiles" + }, + "DescribeDBParameterGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBParameterGroups" + }, + "DescribeDBParameters": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Parameters" + }, + "DescribeDBSecurityGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBSecurityGroups" + }, + "DescribeDBSnapshots": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBSnapshots" + }, + "DescribeDBSubnetGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "DBSubnetGroups" + }, + "DescribeEngineDefaultClusterParameters": { + "input_token": "Marker", + "output_token": "EngineDefaults.Marker", + "limit_key": "MaxRecords", + "result_key": "EngineDefaults.Parameters" + }, + "DescribeEngineDefaultParameters": { + "input_token": "Marker", + "output_token": "EngineDefaults.Marker", + "limit_key": "MaxRecords", + "result_key": "EngineDefaults.Parameters" + }, + "DescribeEventSubscriptions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "EventSubscriptionsList" + }, + "DescribeEvents": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Events" + }, + "DescribeGlobalClusters": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "GlobalClusters" + }, + "DescribeOptionGroupOptions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "OptionGroupOptions" + }, + "DescribeOptionGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "OptionGroupsList" + }, + "DescribeOrderableDBInstanceOptions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "OrderableDBInstanceOptions" + }, + "DescribePendingMaintenanceActions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "PendingMaintenanceActions" + }, + "DescribeReservedDBInstances": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ReservedDBInstances" + }, + "DescribeReservedDBInstancesOfferings": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ReservedDBInstancesOfferings" + }, + "DescribeSourceRegions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "SourceRegions" + }, + "DownloadDBLogFilePortion": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "NumberOfLines", + "more_results": "AdditionalDataPending", + "result_key": "LogFileData" + }, + "DescribeDBClusterEndpoints": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBClusterEndpoints" + }, + "DescribeDBProxies": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBProxies" + }, + "DescribeDBProxyTargetGroups": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "TargetGroups" + }, + "DescribeDBProxyTargets": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "Targets" + }, + "DescribeExportTasks": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "ExportTasks" + }, + "DescribeDBProxyEndpoints": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBProxyEndpoints" + }, + "DescribeBlueGreenDeployments": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "BlueGreenDeployments" + }, + "DescribeDBClusterAutomatedBackups": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBClusterAutomatedBackups" + }, + "DescribeIntegrations": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "Integrations" + }, + "DescribeDBSnapshotTenantDatabases": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBSnapshotTenantDatabases" + }, + "DescribeTenantDatabases": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "TenantDatabases" + }, + "DescribeDBRecommendations": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBRecommendations" + }, + "DescribeDBMajorEngineVersions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DBMajorEngineVersions" + }, + "DescribeServerlessV2PlatformVersions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "ServerlessV2PlatformVersions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/paginators-1.sdk-extras.json new file mode 100644 index 0000000..5016e02 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "DescribeCertificates": { + "non_aggregate_keys": [ + "DefaultCertificateForNewLaunches" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/service-2.json.gz new file mode 100644 index 0000000..efa686d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/service-2.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/service-2.sdk-extras.json new file mode 100644 index 0000000..36aea1f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/service-2.sdk-extras.json @@ -0,0 +1,47 @@ + { + "version": 1.0, + "merge": { + "shapes": { + "CopyDBClusterSnapshotMessage": { + "members": { + "SourceRegion": { + "shape": "String", + "documentation": "

The ID of the region that contains the snapshot to be copied.

" + } + } + }, + "CreateDBClusterMessage": { + "members": { + "SourceRegion": { + "shape": "String", + "documentation": "

The ID of the region that contains the source for the db cluster.

" + } + } + }, + "CopyDBSnapshotMessage": { + "members": { + "SourceRegion": { + "shape": "String", + "documentation": "

The ID of the region that contains the snapshot to be copied.

" + } + } + }, + "CreateDBInstanceReadReplicaMessage": { + "members": { + "SourceRegion": { + "shape": "String", + "documentation": "

The ID of the region that contains the source for the read replica.

" + } + } + }, + "StartDBInstanceAutomatedBackupsReplicationMessage": { + "members": { + "SourceRegion": { + "shape": "String", + "documentation": "

The ID of the region that contains the source for the db instance.

" + } + } + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/waiters-2.json new file mode 100644 index 0000000..a47d552 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rds/2014-10-31/waiters-2.json @@ -0,0 +1,412 @@ +{ + "version": 2, + "waiters": { + "DBClusterAvailable": { + "delay": 30, + "maxAttempts": 60, + "operation": "DescribeDBClusters", + "acceptors": [ + { + "matcher": "pathAll", + "argument": "DBClusters[].Status", + "state": "success", + "expected": "available" + }, + { + "matcher": "pathAny", + "argument": "DBClusters[].Status", + "state": "failure", + "expected": "deleted" + }, + { + "matcher": "pathAny", + "argument": "DBClusters[].Status", + "state": "failure", + "expected": "deleting" + }, + { + "matcher": "pathAny", + "argument": "DBClusters[].Status", + "state": "failure", + "expected": "failed" + }, + { + "matcher": "pathAny", + "argument": "DBClusters[].Status", + "state": "failure", + "expected": "incompatible-restore" + }, + { + "matcher": "pathAny", + "argument": "DBClusters[].Status", + "state": "failure", + "expected": "incompatible-parameters" + } + ] + }, + "DBClusterDeleted": { + "delay": 30, + "maxAttempts": 60, + "operation": "DescribeDBClusters", + "acceptors": [ + { + "matcher": "path", + "argument": "length(DBClusters) == `0`", + "state": "success", + "expected": true + }, + { + "matcher": "error", + "state": "success", + "expected": "DBClusterNotFoundFault" + }, + { + "matcher": "pathAny", + "argument": "DBClusters[].Status", + "state": "failure", + "expected": "creating" + }, + { + "matcher": "pathAny", + "argument": "DBClusters[].Status", + "state": "failure", + "expected": "modifying" + }, + { + "matcher": "pathAny", + "argument": "DBClusters[].Status", + "state": "failure", + "expected": "rebooting" + }, + { + "matcher": "pathAny", + "argument": "DBClusters[].Status", + "state": "failure", + "expected": "resetting-master-credentials" + } + ] + }, + "DBClusterSnapshotAvailable": { + "delay": 30, + "maxAttempts": 60, + "operation": "DescribeDBClusterSnapshots", + "acceptors": [ + { + "matcher": "pathAll", + "argument": "DBClusterSnapshots[].Status", + "state": "success", + "expected": "available" + }, + { + "matcher": "pathAny", + "argument": "DBClusterSnapshots[].Status", + "state": "failure", + "expected": "deleted" + }, + { + "matcher": "pathAny", + "argument": "DBClusterSnapshots[].Status", + "state": "failure", + "expected": "deleting" + }, + { + "matcher": "pathAny", + "argument": "DBClusterSnapshots[].Status", + "state": "failure", + "expected": "failed" + }, + { + "matcher": "pathAny", + "argument": "DBClusterSnapshots[].Status", + "state": "failure", + "expected": "incompatible-restore" + }, + { + "matcher": "pathAny", + "argument": "DBClusterSnapshots[].Status", + "state": "failure", + "expected": "incompatible-parameters" + } + ] + }, + "DBClusterSnapshotDeleted": { + "delay": 30, + "maxAttempts": 60, + "operation": "DescribeDBClusterSnapshots", + "acceptors": [ + { + "matcher": "path", + "argument": "length(DBClusterSnapshots) == `0`", + "state": "success", + "expected": true + }, + { + "matcher": "error", + "state": "success", + "expected": "DBClusterSnapshotNotFoundFault" + }, + { + "matcher": "pathAny", + "argument": "DBClusterSnapshots[].Status", + "state": "failure", + "expected": "creating" + }, + { + "matcher": "pathAny", + "argument": "DBClusterSnapshots[].Status", + "state": "failure", + "expected": "modifying" + }, + { + "matcher": "pathAny", + "argument": "DBClusterSnapshots[].Status", + "state": "failure", + "expected": "rebooting" + }, + { + "matcher": "pathAny", + "argument": "DBClusterSnapshots[].Status", + "state": "failure", + "expected": "resetting-master-credentials" + } + ] + }, + "DBInstanceAvailable": { + "delay": 30, + "maxAttempts": 60, + "operation": "DescribeDBInstances", + "acceptors": [ + { + "matcher": "pathAll", + "argument": "DBInstances[].DBInstanceStatus", + "state": "success", + "expected": "available" + }, + { + "matcher": "pathAny", + "argument": "DBInstances[].DBInstanceStatus", + "state": "failure", + "expected": "deleted" + }, + { + "matcher": "pathAny", + "argument": "DBInstances[].DBInstanceStatus", + "state": "failure", + "expected": "deleting" + }, + { + "matcher": "pathAny", + "argument": "DBInstances[].DBInstanceStatus", + "state": "failure", + "expected": "failed" + }, + { + "matcher": "pathAny", + "argument": "DBInstances[].DBInstanceStatus", + "state": "failure", + "expected": "incompatible-restore" + }, + { + "matcher": "pathAny", + "argument": "DBInstances[].DBInstanceStatus", + "state": "failure", + "expected": "incompatible-parameters" + } + ] + }, + "DBInstanceDeleted": { + "delay": 30, + "maxAttempts": 60, + "operation": "DescribeDBInstances", + "acceptors": [ + { + "matcher": "path", + "argument": "length(DBInstances) == `0`", + "state": "success", + "expected": true + }, + { + "matcher": "error", + "state": "success", + "expected": "DBInstanceNotFound" + }, + { + "matcher": "pathAny", + "argument": "DBInstances[].DBInstanceStatus", + "state": "failure", + "expected": "creating" + }, + { + "matcher": "pathAny", + "argument": "DBInstances[].DBInstanceStatus", + "state": "failure", + "expected": "modifying" + }, + { + "matcher": "pathAny", + "argument": "DBInstances[].DBInstanceStatus", + "state": "failure", + "expected": "rebooting" + }, + { + "matcher": "pathAny", + "argument": "DBInstances[].DBInstanceStatus", + "state": "failure", + "expected": "resetting-master-credentials" + } + ] + }, + "DBSnapshotAvailable": { + "delay": 30, + "maxAttempts": 60, + "operation": "DescribeDBSnapshots", + "acceptors": [ + { + "matcher": "pathAll", + "argument": "DBSnapshots[].Status", + "state": "success", + "expected": "available" + }, + { + "matcher": "pathAny", + "argument": "DBSnapshots[].Status", + "state": "failure", + "expected": "deleted" + }, + { + "matcher": "pathAny", + "argument": "DBSnapshots[].Status", + "state": "failure", + "expected": "deleting" + }, + { + "matcher": "pathAny", + "argument": "DBSnapshots[].Status", + "state": "failure", + "expected": "failed" + }, + { + "matcher": "pathAny", + "argument": "DBSnapshots[].Status", + "state": "failure", + "expected": "incompatible-restore" + }, + { + "matcher": "pathAny", + "argument": "DBSnapshots[].Status", + "state": "failure", + "expected": "incompatible-parameters" + } + ] + }, + "DBSnapshotDeleted": { + "delay": 30, + "maxAttempts": 60, + "operation": "DescribeDBSnapshots", + "acceptors": [ + { + "matcher": "path", + "argument": "length(DBSnapshots) == `0`", + "state": "success", + "expected": true + }, + { + "matcher": "error", + "state": "success", + "expected": "DBSnapshotNotFound" + }, + { + "matcher": "pathAny", + "argument": "DBSnapshots[].Status", + "state": "failure", + "expected": "creating" + }, + { + "matcher": "pathAny", + "argument": "DBSnapshots[].Status", + "state": "failure", + "expected": "modifying" + }, + { + "matcher": "pathAny", + "argument": "DBSnapshots[].Status", + "state": "failure", + "expected": "rebooting" + }, + { + "matcher": "pathAny", + "argument": "DBSnapshots[].Status", + "state": "failure", + "expected": "resetting-master-credentials" + } + ] + }, + "TenantDatabaseAvailable": { + "delay": 30, + "maxAttempts": 60, + "operation": "DescribeTenantDatabases", + "acceptors": [ + { + "matcher": "pathAll", + "argument": "TenantDatabases[].Status", + "state": "success", + "expected": "available" + }, + { + "matcher": "pathAny", + "argument": "TenantDatabases[].Status", + "state": "failure", + "expected": "deleted" + }, + { + "matcher": "pathAny", + "argument": "TenantDatabases[].Status", + "state": "failure", + "expected": "incompatible-parameters" + }, + { + "matcher": "pathAny", + "argument": "TenantDatabases[].Status", + "state": "failure", + "expected": "incompatible-restore" + } + ] + }, + "TenantDatabaseDeleted": { + "delay": 30, + "maxAttempts": 60, + "operation": "DescribeTenantDatabases", + "acceptors": [ + { + "matcher": "path", + "argument": "length(TenantDatabases) == `0`", + "state": "success", + "expected": true + }, + { + "matcher": "error", + "state": "success", + "expected": "DBInstanceNotFoundFault" + } + ] + }, + "DBSnapshotCompleted": { + "delay": 15, + "operation": "DescribeDBSnapshots", + "maxAttempts": 40, + "acceptors": [ + { + "expected": "DBSnapshotNotFound", + "matcher": "error", + "state": "success" + }, + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "DBSnapshots[].Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3d00d89 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/paginators-1.json new file mode 100644 index 0000000..7c81ceb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/paginators-1.json @@ -0,0 +1,50 @@ +{ + "pagination": { + "DescribeTable": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ColumnList" + }, + "GetStatementResult": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Records" + }, + "ListDatabases": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Databases" + }, + "ListSchemas": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Schemas" + }, + "ListStatements": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Statements" + }, + "ListTables": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Tables" + }, + "GetStatementResultV2": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Records" + }, + "ListSessions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Sessions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/paginators-1.sdk-extras.json new file mode 100644 index 0000000..8a06016 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/paginators-1.sdk-extras.json @@ -0,0 +1,25 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetStatementResult": { + "non_aggregate_keys": [ + "ColumnMetadata", + "TotalNumRows" + ] + }, + "GetStatementResultV2": { + "non_aggregate_keys": [ + "ColumnMetadata", + "TotalNumRows", + "ResultFormat" + ] + }, + "DescribeTable": { + "non_aggregate_keys": [ + "TableName" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/service-2.json.gz new file mode 100644 index 0000000..1ed5f9c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/redshift-data/2019-12-20/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/redshift-serverless/2021-04-21/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/redshift-serverless/2021-04-21/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f013495 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/redshift-serverless/2021-04-21/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/redshift-serverless/2021-04-21/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/redshift-serverless/2021-04-21/paginators-1.json new file mode 100644 index 0000000..0b5e6ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/redshift-serverless/2021-04-21/paginators-1.json @@ -0,0 +1,88 @@ +{ + "pagination": { + "ListEndpointAccess": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "endpoints" + }, + "ListNamespaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "namespaces" + }, + "ListRecoveryPoints": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recoveryPoints" + }, + "ListSnapshots": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "snapshots" + }, + "ListUsageLimits": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "usageLimits" + }, + "ListWorkgroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "workgroups" + }, + "ListTableRestoreStatus": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tableRestoreStatuses" + }, + "ListCustomDomainAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "associations" + }, + "ListScheduledActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "scheduledActions" + }, + "ListSnapshotCopyConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "snapshotCopyConfigurations" + }, + "ListManagedWorkgroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "managedWorkgroups" + }, + "ListTracks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tracks" + }, + "ListReservationOfferings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "reservationOfferingsList" + }, + "ListReservations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "reservationsList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/redshift-serverless/2021-04-21/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/redshift-serverless/2021-04-21/service-2.json.gz new file mode 100644 index 0000000..0c8604c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/redshift-serverless/2021-04-21/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f3d51fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/paginators-1.json new file mode 100644 index 0000000..8bd0443 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/paginators-1.json @@ -0,0 +1,232 @@ +{ + "pagination": { + "DescribeClusterParameterGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ParameterGroups" + }, + "DescribeClusterParameters": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Parameters" + }, + "DescribeClusterSecurityGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ClusterSecurityGroups" + }, + "DescribeClusterSnapshots": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Snapshots" + }, + "DescribeClusterSubnetGroups": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ClusterSubnetGroups" + }, + "DescribeClusterVersions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ClusterVersions" + }, + "DescribeClusters": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Clusters" + }, + "DescribeDefaultClusterParameters": { + "input_token": "Marker", + "output_token": "DefaultClusterParameters.Marker", + "limit_key": "MaxRecords", + "result_key": "DefaultClusterParameters.Parameters" + }, + "DescribeEventSubscriptions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "EventSubscriptionsList" + }, + "DescribeEvents": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "Events" + }, + "DescribeHsmClientCertificates": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "HsmClientCertificates" + }, + "DescribeHsmConfigurations": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "HsmConfigurations" + }, + "DescribeOrderableClusterOptions": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "OrderableClusterOptions" + }, + "DescribeReservedNodeOfferings": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ReservedNodeOfferings" + }, + "DescribeReservedNodes": { + "input_token": "Marker", + "output_token": "Marker", + "limit_key": "MaxRecords", + "result_key": "ReservedNodes" + }, + "DescribeClusterDbRevisions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "ClusterDbRevisions" + }, + "DescribeClusterTracks": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "MaintenanceTracks" + }, + "DescribeSnapshotCopyGrants": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "SnapshotCopyGrants" + }, + "DescribeSnapshotSchedules": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "SnapshotSchedules" + }, + "DescribeTableRestoreStatus": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "TableRestoreStatusDetails" + }, + "DescribeTags": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "TaggedResources" + }, + "GetReservedNodeExchangeOfferings": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "ReservedNodeOfferings" + }, + "DescribeNodeConfigurationOptions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "NodeConfigurationOptionList" + }, + "DescribeScheduledActions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "ScheduledActions" + }, + "DescribeUsageLimits": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "UsageLimits" + }, + "DescribeEndpointAccess": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "EndpointAccessList" + }, + "DescribeEndpointAuthorization": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "EndpointAuthorizationList" + }, + "DescribeDataShares": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DataShares" + }, + "DescribeDataSharesForConsumer": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DataShares" + }, + "DescribeDataSharesForProducer": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "DataShares" + }, + "DescribeReservedNodeExchangeStatus": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "ReservedNodeExchangeStatusDetails" + }, + "GetReservedNodeExchangeConfigurationOptions": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "ReservedNodeConfigurationOptionList" + }, + "DescribeCustomDomainAssociations": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "Associations" + }, + "DescribeInboundIntegrations": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "InboundIntegrations" + }, + "DescribeRedshiftIdcApplications": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "RedshiftIdcApplications" + }, + "ListRecommendations": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "Recommendations" + }, + "DescribeIntegrations": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "Integrations" + }, + "DescribeQev2IdcApplications": { + "input_token": "Marker", + "limit_key": "MaxRecords", + "output_token": "Marker", + "result_key": "Qev2IdcApplications" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/service-2.json.gz new file mode 100644 index 0000000..a2b0b33 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/waiters-2.json new file mode 100644 index 0000000..164e9b0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/redshift/2012-12-01/waiters-2.json @@ -0,0 +1,97 @@ +{ + "version": 2, + "waiters": { + "ClusterAvailable": { + "delay": 60, + "operation": "DescribeClusters", + "maxAttempts": 30, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Clusters[].ClusterStatus" + }, + { + "expected": "deleting", + "matcher": "pathAny", + "state": "failure", + "argument": "Clusters[].ClusterStatus" + }, + { + "expected": "ClusterNotFound", + "matcher": "error", + "state": "retry" + } + ] + }, + "ClusterDeleted": { + "delay": 60, + "operation": "DescribeClusters", + "maxAttempts": 30, + "acceptors": [ + { + "expected": "ClusterNotFound", + "matcher": "error", + "state": "success" + }, + { + "expected": "creating", + "matcher": "pathAny", + "state": "failure", + "argument": "Clusters[].ClusterStatus" + }, + { + "expected": "modifying", + "matcher": "pathAny", + "state": "failure", + "argument": "Clusters[].ClusterStatus" + } + ] + }, + "ClusterRestored": { + "operation": "DescribeClusters", + "maxAttempts": 30, + "delay": 60, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Clusters[].RestoreStatus.Status", + "expected": "completed" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "Clusters[].ClusterStatus", + "expected": "deleting" + } + ] + }, + "SnapshotAvailable": { + "delay": 15, + "operation": "DescribeClusterSnapshots", + "maxAttempts": 20, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "Snapshots[].Status" + }, + { + "expected": "failed", + "matcher": "pathAny", + "state": "failure", + "argument": "Snapshots[].Status" + }, + { + "expected": "deleted", + "matcher": "pathAny", + "state": "failure", + "argument": "Snapshots[].Status" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6ea3733 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/examples-1.json new file mode 100644 index 0000000..039e04d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/examples-1.json @@ -0,0 +1,651 @@ +{ + "version": "1.0", + "examples": { + "CompareFaces": [ + { + "input": { + "SimilarityThreshold": 90, + "SourceImage": { + "S3Object": { + "Bucket": "mybucket", + "Name": "mysourceimage" + } + }, + "TargetImage": { + "S3Object": { + "Bucket": "mybucket", + "Name": "mytargetimage" + } + } + }, + "output": { + "FaceMatches": [ + { + "Face": { + "BoundingBox": { + "Height": 0.33481481671333313, + "Left": 0.31888890266418457, + "Top": 0.4933333396911621, + "Width": 0.25 + }, + "Confidence": 99.9991226196289 + }, + "Similarity": 100 + } + ], + "SourceImageFace": { + "BoundingBox": { + "Height": 0.33481481671333313, + "Left": 0.31888890266418457, + "Top": 0.4933333396911621, + "Width": 0.25 + }, + "Confidence": 99.9991226196289 + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation compares the largest face detected in the source image with each face detected in the target image.", + "id": "to-compare-two-images-1482181985581", + "title": "To compare two images" + } + ], + "CreateCollection": [ + { + "input": { + "CollectionId": "myphotos" + }, + "output": { + "CollectionArn": "aws:rekognition:us-west-2:123456789012:collection/myphotos", + "StatusCode": 200 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation creates a Rekognition collection for storing image data.", + "id": "to-create-a-collection-1481833313674", + "title": "To create a collection" + } + ], + "DeleteCollection": [ + { + "input": { + "CollectionId": "myphotos" + }, + "output": { + "StatusCode": 200 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation deletes a Rekognition collection.", + "id": "to-delete-a-collection-1481838179973", + "title": "To delete a collection" + } + ], + "DeleteFaces": [ + { + "input": { + "CollectionId": "myphotos", + "FaceIds": [ + "ff43d742-0c13-5d16-a3e8-03d3f58e980b" + ] + }, + "output": { + "DeletedFaces": [ + "ff43d742-0c13-5d16-a3e8-03d3f58e980b" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation deletes one or more faces from a Rekognition collection.", + "id": "to-delete-a-face-1482182799377", + "title": "To delete a face" + } + ], + "DetectFaces": [ + { + "input": { + "Image": { + "S3Object": { + "Bucket": "mybucket", + "Name": "myphoto" + } + } + }, + "output": { + "FaceDetails": [ + { + "BoundingBox": { + "Height": 0.18000000715255737, + "Left": 0.5555555820465088, + "Top": 0.33666667342185974, + "Width": 0.23999999463558197 + }, + "Confidence": 100, + "Landmarks": [ + { + "Type": "eyeLeft", + "X": 0.6394737362861633, + "Y": 0.40819624066352844 + }, + { + "Type": "eyeRight", + "X": 0.7266660928726196, + "Y": 0.41039225459098816 + }, + { + "Type": "eyeRight", + "X": 0.6912462115287781, + "Y": 0.44240960478782654 + }, + { + "Type": "mouthDown", + "X": 0.6306198239326477, + "Y": 0.46700039505958557 + }, + { + "Type": "mouthUp", + "X": 0.7215608954429626, + "Y": 0.47114261984825134 + } + ], + "Pose": { + "Pitch": 4.050806522369385, + "Roll": 0.9950747489929199, + "Yaw": 13.693790435791016 + }, + "Quality": { + "Brightness": 37.60169982910156, + "Sharpness": 80 + } + } + ], + "OrientationCorrection": "ROTATE_0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation detects faces in an image stored in an AWS S3 bucket.", + "id": "to-detect-faces-in-an-image-1481841782793", + "title": "To detect faces in an image" + } + ], + "DetectLabels": [ + { + "input": { + "Image": { + "S3Object": { + "Bucket": "mybucket", + "Name": "myphoto" + } + }, + "MaxLabels": 123, + "MinConfidence": 70 + }, + "output": { + "Labels": [ + { + "Confidence": 99.25072479248047, + "Name": "People" + }, + { + "Confidence": 99.25074005126953, + "Name": "Person" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation detects labels in the supplied image", + "id": "to-detect-labels-1481834255770", + "title": "To detect labels" + } + ], + "IndexFaces": [ + { + "input": { + "CollectionId": "myphotos", + "DetectionAttributes": [ + + ], + "ExternalImageId": "myphotoid", + "Image": { + "S3Object": { + "Bucket": "mybucket", + "Name": "myphoto" + } + } + }, + "output": { + "FaceRecords": [ + { + "Face": { + "BoundingBox": { + "Height": 0.33481481671333313, + "Left": 0.31888890266418457, + "Top": 0.4933333396911621, + "Width": 0.25 + }, + "Confidence": 99.9991226196289, + "FaceId": "ff43d742-0c13-5d16-a3e8-03d3f58e980b", + "ImageId": "465f4e93-763e-51d0-b030-b9667a2d94b1" + }, + "FaceDetail": { + "BoundingBox": { + "Height": 0.33481481671333313, + "Left": 0.31888890266418457, + "Top": 0.4933333396911621, + "Width": 0.25 + }, + "Confidence": 99.9991226196289, + "Landmarks": [ + { + "Type": "eyeLeft", + "X": 0.3976764678955078, + "Y": 0.6248345971107483 + }, + { + "Type": "eyeRight", + "X": 0.4810936450958252, + "Y": 0.6317117214202881 + }, + { + "Type": "noseLeft", + "X": 0.41986238956451416, + "Y": 0.7111940383911133 + }, + { + "Type": "mouthDown", + "X": 0.40525302290916443, + "Y": 0.7497701048851013 + }, + { + "Type": "mouthUp", + "X": 0.4753248989582062, + "Y": 0.7558549642562866 + } + ], + "Pose": { + "Pitch": -9.713645935058594, + "Roll": 4.707281112670898, + "Yaw": -24.438663482666016 + }, + "Quality": { + "Brightness": 29.23358917236328, + "Sharpness": 80 + } + } + }, + { + "Face": { + "BoundingBox": { + "Height": 0.32592591643333435, + "Left": 0.5144444704055786, + "Top": 0.15111111104488373, + "Width": 0.24444444477558136 + }, + "Confidence": 99.99950408935547, + "FaceId": "8be04dba-4e58-520d-850e-9eae4af70eb2", + "ImageId": "465f4e93-763e-51d0-b030-b9667a2d94b1" + }, + "FaceDetail": { + "BoundingBox": { + "Height": 0.32592591643333435, + "Left": 0.5144444704055786, + "Top": 0.15111111104488373, + "Width": 0.24444444477558136 + }, + "Confidence": 99.99950408935547, + "Landmarks": [ + { + "Type": "eyeLeft", + "X": 0.6006892323493958, + "Y": 0.290842205286026 + }, + { + "Type": "eyeRight", + "X": 0.6808141469955444, + "Y": 0.29609042406082153 + }, + { + "Type": "noseLeft", + "X": 0.6395332217216492, + "Y": 0.3522595763206482 + }, + { + "Type": "mouthDown", + "X": 0.5892083048820496, + "Y": 0.38689887523651123 + }, + { + "Type": "mouthUp", + "X": 0.674560010433197, + "Y": 0.394125759601593 + } + ], + "Pose": { + "Pitch": -4.683138370513916, + "Roll": 2.1029529571533203, + "Yaw": 6.716655254364014 + }, + "Quality": { + "Brightness": 34.951698303222656, + "Sharpness": 160 + } + } + } + ], + "OrientationCorrection": "ROTATE_0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation detects faces in an image and adds them to the specified Rekognition collection.", + "id": "to-add-a-face-to-a-collection-1482179542923", + "title": "To add a face to a collection" + } + ], + "ListCollections": [ + { + "input": { + }, + "output": { + "CollectionIds": [ + "myphotos" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation returns a list of Rekognition collections.", + "id": "to-list-the-collections-1482179199088", + "title": "To list the collections" + } + ], + "ListFaces": [ + { + "input": { + "CollectionId": "myphotos", + "MaxResults": 20 + }, + "output": { + "Faces": [ + { + "BoundingBox": { + "Height": 0.18000000715255737, + "Left": 0.5555559992790222, + "Top": 0.336667001247406, + "Width": 0.23999999463558197 + }, + "Confidence": 100, + "FaceId": "1c62e8b5-69a7-5b7d-b3cd-db4338a8a7e7", + "ImageId": "147fdf82-7a71-52cf-819b-e786c7b9746e" + }, + { + "BoundingBox": { + "Height": 0.16555599868297577, + "Left": 0.30963000655174255, + "Top": 0.7066670060157776, + "Width": 0.22074100375175476 + }, + "Confidence": 100, + "FaceId": "29a75abe-397b-5101-ba4f-706783b2246c", + "ImageId": "147fdf82-7a71-52cf-819b-e786c7b9746e" + }, + { + "BoundingBox": { + "Height": 0.3234420120716095, + "Left": 0.3233329951763153, + "Top": 0.5, + "Width": 0.24222199618816376 + }, + "Confidence": 99.99829864501953, + "FaceId": "38271d79-7bc2-5efb-b752-398a8d575b85", + "ImageId": "d5631190-d039-54e4-b267-abd22c8647c5" + }, + { + "BoundingBox": { + "Height": 0.03555560111999512, + "Left": 0.37388700246810913, + "Top": 0.2477779984474182, + "Width": 0.04747769981622696 + }, + "Confidence": 99.99210357666016, + "FaceId": "3b01bef0-c883-5654-ba42-d5ad28b720b3", + "ImageId": "812d9f04-86f9-54fc-9275-8d0dcbcb6784" + }, + { + "BoundingBox": { + "Height": 0.05333330109715462, + "Left": 0.2937690019607544, + "Top": 0.35666701197624207, + "Width": 0.07121659815311432 + }, + "Confidence": 99.99919891357422, + "FaceId": "4839a608-49d0-566c-8301-509d71b534d1", + "ImageId": "812d9f04-86f9-54fc-9275-8d0dcbcb6784" + }, + { + "BoundingBox": { + "Height": 0.3249259889125824, + "Left": 0.5155559778213501, + "Top": 0.1513350009918213, + "Width": 0.24333299696445465 + }, + "Confidence": 99.99949645996094, + "FaceId": "70008e50-75e4-55d0-8e80-363fb73b3a14", + "ImageId": "d5631190-d039-54e4-b267-abd22c8647c5" + }, + { + "BoundingBox": { + "Height": 0.03777780011296272, + "Left": 0.7002969980239868, + "Top": 0.18777799606323242, + "Width": 0.05044509842991829 + }, + "Confidence": 99.92639923095703, + "FaceId": "7f5f88ed-d684-5a88-b0df-01e4a521552b", + "ImageId": "812d9f04-86f9-54fc-9275-8d0dcbcb6784" + }, + { + "BoundingBox": { + "Height": 0.05555560067296028, + "Left": 0.13946600258350372, + "Top": 0.46333301067352295, + "Width": 0.07270029932260513 + }, + "Confidence": 99.99469757080078, + "FaceId": "895b4e2c-81de-5902-a4bd-d1792bda00b2", + "ImageId": "812d9f04-86f9-54fc-9275-8d0dcbcb6784" + }, + { + "BoundingBox": { + "Height": 0.3259260058403015, + "Left": 0.5144439935684204, + "Top": 0.15111100673675537, + "Width": 0.24444399774074554 + }, + "Confidence": 99.99949645996094, + "FaceId": "8be04dba-4e58-520d-850e-9eae4af70eb2", + "ImageId": "465f4e93-763e-51d0-b030-b9667a2d94b1" + }, + { + "BoundingBox": { + "Height": 0.18888899683952332, + "Left": 0.3783380091190338, + "Top": 0.2355560064315796, + "Width": 0.25222599506378174 + }, + "Confidence": 99.9999008178711, + "FaceId": "908544ad-edc3-59df-8faf-6a87cc256cf5", + "ImageId": "3c731605-d772-541a-a5e7-0375dbc68a07" + }, + { + "BoundingBox": { + "Height": 0.33481499552726746, + "Left": 0.31888899207115173, + "Top": 0.49333301186561584, + "Width": 0.25 + }, + "Confidence": 99.99909973144531, + "FaceId": "ff43d742-0c13-5d16-a3e8-03d3f58e980b", + "ImageId": "465f4e93-763e-51d0-b030-b9667a2d94b1" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation lists the faces in a Rekognition collection.", + "id": "to-list-the-faces-in-a-collection-1482181416530", + "title": "To list the faces in a collection" + } + ], + "SearchFaces": [ + { + "input": { + "CollectionId": "myphotos", + "FaceId": "70008e50-75e4-55d0-8e80-363fb73b3a14", + "FaceMatchThreshold": 90, + "MaxFaces": 10 + }, + "output": { + "FaceMatches": [ + { + "Face": { + "BoundingBox": { + "Height": 0.3259260058403015, + "Left": 0.5144439935684204, + "Top": 0.15111100673675537, + "Width": 0.24444399774074554 + }, + "Confidence": 99.99949645996094, + "FaceId": "8be04dba-4e58-520d-850e-9eae4af70eb2", + "ImageId": "465f4e93-763e-51d0-b030-b9667a2d94b1" + }, + "Similarity": 99.97222137451172 + }, + { + "Face": { + "BoundingBox": { + "Height": 0.16555599868297577, + "Left": 0.30963000655174255, + "Top": 0.7066670060157776, + "Width": 0.22074100375175476 + }, + "Confidence": 100, + "FaceId": "29a75abe-397b-5101-ba4f-706783b2246c", + "ImageId": "147fdf82-7a71-52cf-819b-e786c7b9746e" + }, + "Similarity": 97.04154968261719 + }, + { + "Face": { + "BoundingBox": { + "Height": 0.18888899683952332, + "Left": 0.3783380091190338, + "Top": 0.2355560064315796, + "Width": 0.25222599506378174 + }, + "Confidence": 99.9999008178711, + "FaceId": "908544ad-edc3-59df-8faf-6a87cc256cf5", + "ImageId": "3c731605-d772-541a-a5e7-0375dbc68a07" + }, + "Similarity": 95.94520568847656 + } + ], + "SearchedFaceId": "70008e50-75e4-55d0-8e80-363fb73b3a14" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation searches for matching faces in the collection the supplied face belongs to.", + "id": "to-delete-a-face-1482182799377", + "title": "To delete a face" + } + ], + "SearchFacesByImage": [ + { + "input": { + "CollectionId": "myphotos", + "FaceMatchThreshold": 95, + "Image": { + "S3Object": { + "Bucket": "mybucket", + "Name": "myphoto" + } + }, + "MaxFaces": 5 + }, + "output": { + "FaceMatches": [ + { + "Face": { + "BoundingBox": { + "Height": 0.3234420120716095, + "Left": 0.3233329951763153, + "Top": 0.5, + "Width": 0.24222199618816376 + }, + "Confidence": 99.99829864501953, + "FaceId": "38271d79-7bc2-5efb-b752-398a8d575b85", + "ImageId": "d5631190-d039-54e4-b267-abd22c8647c5" + }, + "Similarity": 99.97036743164062 + } + ], + "SearchedFaceBoundingBox": { + "Height": 0.33481481671333313, + "Left": 0.31888890266418457, + "Top": 0.4933333396911621, + "Width": 0.25 + }, + "SearchedFaceConfidence": 99.9991226196289 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation searches for faces in a Rekognition collection that match the largest face in an S3 bucket stored image.", + "id": "to-search-for-faces-matching-a-supplied-image-1482175994491", + "title": "To search for faces matching a supplied image" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/paginators-1.json new file mode 100644 index 0000000..436503d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "ListCollections": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": [ + "CollectionIds", + "FaceModelVersions" + ] + }, + "ListFaces": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Faces", + "non_aggregate_keys": [ + "FaceModelVersion" + ] + }, + "ListStreamProcessors": { + "result_key": "StreamProcessors", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "DescribeProjectVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ProjectVersionDescriptions" + }, + "DescribeProjects": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ProjectDescriptions" + }, + "ListDatasetEntries": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DatasetEntries" + }, + "ListDatasetLabels": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DatasetLabelDescriptions" + }, + "ListProjectPolicies": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ProjectPolicies" + }, + "ListUsers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Users" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/service-2.json.gz new file mode 100644 index 0000000..0119d61 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/waiters-2.json new file mode 100644 index 0000000..c67dc62 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rekognition/2016-06-27/waiters-2.json @@ -0,0 +1,45 @@ +{ + "version": 2, + "waiters": { + "ProjectVersionTrainingCompleted": { + "description": "Wait until the ProjectVersion training completes.", + "operation": "DescribeProjectVersions", + "delay": 120, + "maxAttempts": 360, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "ProjectVersionDescriptions[].Status", + "expected": "TRAINING_COMPLETED" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "ProjectVersionDescriptions[].Status", + "expected": "TRAINING_FAILED" + } + ] + }, + "ProjectVersionRunning": { + "description": "Wait until the ProjectVersion is running.", + "delay": 30, + "maxAttempts": 40, + "operation": "DescribeProjectVersions", + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "ProjectVersionDescriptions[].Status", + "expected": "RUNNING" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "ProjectVersionDescriptions[].Status", + "expected": "FAILED" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/repostspace/2022-05-13/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/repostspace/2022-05-13/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..16c50e0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/repostspace/2022-05-13/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/repostspace/2022-05-13/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/repostspace/2022-05-13/paginators-1.json new file mode 100644 index 0000000..005e53f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/repostspace/2022-05-13/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListSpaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "spaces" + }, + "ListChannels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "channels" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/repostspace/2022-05-13/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/repostspace/2022-05-13/service-2.json.gz new file mode 100644 index 0000000..5bab049 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/repostspace/2022-05-13/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/repostspace/2022-05-13/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/repostspace/2022-05-13/waiters-2.json new file mode 100644 index 0000000..3439ab1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/repostspace/2022-05-13/waiters-2.json @@ -0,0 +1,97 @@ +{ + "version" : 2, + "waiters" : { + "ChannelCreated" : { + "delay" : 2, + "maxAttempts" : 60, + "operation" : "GetChannel", + "acceptors" : [ { + "matcher" : "path", + "argument" : "channelStatus", + "state" : "success", + "expected" : "CREATED" + }, { + "matcher" : "path", + "argument" : "channelStatus", + "state" : "failure", + "expected" : "CREATE_FAILED" + }, { + "matcher" : "path", + "argument" : "channelStatus", + "state" : "retry", + "expected" : "CREATING" + } ] + }, + "ChannelDeleted" : { + "delay" : 2, + "maxAttempts" : 60, + "operation" : "GetChannel", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "channelStatus", + "state" : "success", + "expected" : "DELETED" + }, { + "matcher" : "path", + "argument" : "channelStatus", + "state" : "failure", + "expected" : "DELETE_FAILED" + }, { + "matcher" : "path", + "argument" : "channelStatus", + "state" : "retry", + "expected" : "DELETING" + } ] + }, + "SpaceCreated" : { + "delay" : 300, + "maxAttempts" : 24, + "operation" : "GetSpace", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "CREATED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "CREATE_FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "CREATING" + } ] + }, + "SpaceDeleted" : { + "delay" : 300, + "maxAttempts" : 24, + "operation" : "GetSpace", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "DELETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETE_FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "retry", + "expected" : "DELETING" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/resiliencehub/2020-04-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/resiliencehub/2020-04-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..5e7164c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/resiliencehub/2020-04-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/resiliencehub/2020-04-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/resiliencehub/2020-04-30/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/resiliencehub/2020-04-30/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/resiliencehub/2020-04-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/resiliencehub/2020-04-30/paginators-1.json new file mode 100644 index 0000000..665877a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/resiliencehub/2020-04-30/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListAppAssessmentResourceDrifts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "resourceDrifts" + }, + "ListResourceGroupingRecommendations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "groupingRecommendations" + }, + "ListMetrics": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "rows" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/resiliencehub/2020-04-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/resiliencehub/2020-04-30/service-2.json.gz new file mode 100644 index 0000000..3bc61c6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/resiliencehub/2020-04-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..90cd4a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/paginators-1.json new file mode 100644 index 0000000..a9150c5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/paginators-1.json @@ -0,0 +1,148 @@ +{ + "pagination": { + "ListAssertions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assertions" + }, + "ListDependencies": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dependencySummaries" + }, + "ListFailureModeAssessments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assessmentSummaries" + }, + "ListFailureModeFindings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findingsSummary" + }, + "ListInputSources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "inputSourceSummaries" + }, + "ListPolicies": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policySummaries" + }, + "ListReports": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "reportGenerationResults" + }, + "ListResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "serviceResources" + }, + "ListServiceEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "events" + }, + "ListServiceFunctions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "serviceFunctions" + }, + "ListServiceTopologyEdges": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "serviceTopologyEdgeSummaries" + }, + "ListServices": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "serviceSummaries" + }, + "ListSystemEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "events" + }, + "ListSystems": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "systemSummaries" + }, + "ListUserJourneys": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "userJourneySummaries" + }, + "ListResolvedTestRunTargetResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "resolvedTargetResources" + }, + "ListTestRunEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "events" + }, + "ListTestRunSources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "testRunSources" + }, + "ListTestRuns": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "testRuns" + }, + "ListTestSources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "testSources" + }, + "ListTests": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tests" + }, + "ListTestRunDependencies": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dependencies" + }, + "ListTestRunSourceEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "testRunSourceEvents" + }, + "ListPolicyEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "events" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/paginators-1.sdk-extras.json new file mode 100644 index 0000000..2861518 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListResources": { + "non_aggregate_keys": [ + "serviceFunctionId" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/service-2.json.gz new file mode 100644 index 0000000..e83969a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/waiters-2.json new file mode 100644 index 0000000..fba4409 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/resiliencehubv2/2026-02-17/waiters-2.json @@ -0,0 +1,88 @@ +{ + "version" : 2, + "waiters" : { + "FailureModeAssessmentSuccess" : { + "description" : "Wait until a failure mode assessment completes successfully", + "delay" : 30, + "maxAttempts" : 5, + "operation" : "ListFailureModeAssessments", + "acceptors" : [ { + "matcher" : "path", + "argument" : "length(assessmentSummaries[]) == `0`", + "state" : "retry", + "expected" : true + }, { + "matcher" : "pathAll", + "argument" : "assessmentSummaries[].assessmentStatus", + "state" : "success", + "expected" : "SUCCESS" + }, { + "matcher" : "pathAny", + "argument" : "assessmentSummaries[].assessmentStatus", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "ReportSucceeded" : { + "description" : "Wait until report generation succeeds", + "delay" : 30, + "maxAttempts" : 5, + "operation" : "ListReports", + "acceptors" : [ { + "matcher" : "path", + "argument" : "length(reportGenerationResults[]) == `0`", + "state" : "retry", + "expected" : true + }, { + "matcher" : "pathAll", + "argument" : "reportGenerationResults[].status", + "state" : "success", + "expected" : "SUCCEEDED" + }, { + "matcher" : "pathAny", + "argument" : "reportGenerationResults[].status", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "ServiceAssessmentCompleted" : { + "description" : "Wait until a service assessment completes successfully", + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetService", + "acceptors" : [ { + "matcher" : "path", + "argument" : "service.assessmentStatus", + "state" : "success", + "expected" : "SUCCESS" + }, { + "matcher" : "path", + "argument" : "service.assessmentStatus", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "ServiceResourceDiscoveryCompleted" : { + "description" : "Wait until service resource discovery completes", + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetService", + "acceptors" : [ { + "matcher" : "path", + "argument" : "service.resourceDiscovery.status", + "state" : "success", + "expected" : "SUCCEEDED" + }, { + "matcher" : "path", + "argument" : "service.resourceDiscovery.status", + "state" : "success", + "expected" : "COMPLETED_WITH_FAILURES" + }, { + "matcher" : "path", + "argument" : "service.resourceDiscovery.status", + "state" : "failure", + "expected" : "FAILED" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0fe28a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/paginators-1.json new file mode 100644 index 0000000..205f17d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/paginators-1.json @@ -0,0 +1,70 @@ +{ + "pagination": { + "ListIndexes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Indexes" + }, + "ListSupportedResourceTypes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResourceTypes" + }, + "ListViews": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Views" + }, + "Search": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Resources" + }, + "ListIndexesForMembers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Indexes" + }, + "ListResources": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Resources" + }, + "ListManagedViews": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ManagedViews" + }, + "GetResourceExplorerSetup": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Regions" + }, + "ListServiceIndexes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Indexes" + }, + "ListServiceViews": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ServiceViews" + }, + "ListStreamingAccessForServices": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "StreamingAccessForServices" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/paginators-1.sdk-extras.json new file mode 100644 index 0000000..92a00fe --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/paginators-1.sdk-extras.json @@ -0,0 +1,18 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "Search": { + "non_aggregate_keys": [ + "ViewArn", + "Count" + ] + }, + "ListResources": { + "non_aggregate_keys": [ + "ViewArn" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/service-2.json.gz new file mode 100644 index 0000000..5817bad Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/resource-explorer-2/2022-07-28/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..02d65de Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/paginators-1.json new file mode 100644 index 0000000..1210885 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListGroups": { + "result_key": [ + "GroupIdentifiers", + "Groups" + ], + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "SearchResources": { + "result_key": "ResourceIdentifiers", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListGroupResources": { + "result_key": [ + "ResourceIdentifiers", + "Resources" + ], + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListGroupingStatuses": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "GroupingStatuses" + }, + "ListTagSyncTasks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TagSyncTasks" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/paginators-1.sdk-extras.json new file mode 100644 index 0000000..eaa5232 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListGroupingStatuses": { + "non_aggregate_keys": [ + "Group" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/service-2.json.gz new file mode 100644 index 0000000..d8b61af Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/resource-groups/2017-11-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/resourcegroupstaggingapi/2017-01-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/resourcegroupstaggingapi/2017-01-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3cec45f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/resourcegroupstaggingapi/2017-01-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/resourcegroupstaggingapi/2017-01-26/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/resourcegroupstaggingapi/2017-01-26/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/resourcegroupstaggingapi/2017-01-26/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/resourcegroupstaggingapi/2017-01-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/resourcegroupstaggingapi/2017-01-26/paginators-1.json new file mode 100644 index 0000000..bbf7282 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/resourcegroupstaggingapi/2017-01-26/paginators-1.json @@ -0,0 +1,32 @@ +{ + "pagination": { + "GetResources": { + "input_token": "PaginationToken", + "limit_key": "ResourcesPerPage", + "output_token": "PaginationToken", + "result_key": "ResourceTagMappingList" + }, + "GetTagKeys": { + "input_token": "PaginationToken", + "output_token": "PaginationToken", + "result_key": "TagKeys" + }, + "GetTagValues": { + "input_token": "PaginationToken", + "output_token": "PaginationToken", + "result_key": "TagValues" + }, + "GetComplianceSummary": { + "input_token": "PaginationToken", + "limit_key": "MaxResults", + "output_token": "PaginationToken", + "result_key": "SummaryList" + }, + "ListRequiredTags": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "RequiredTags" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/resourcegroupstaggingapi/2017-01-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/resourcegroupstaggingapi/2017-01-26/service-2.json.gz new file mode 100644 index 0000000..8e37b8d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/resourcegroupstaggingapi/2017-01-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rolesanywhere/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rolesanywhere/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4db2a81 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rolesanywhere/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rolesanywhere/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/rolesanywhere/2018-05-10/paginators-1.json new file mode 100644 index 0000000..9729861 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rolesanywhere/2018-05-10/paginators-1.json @@ -0,0 +1,24 @@ +{ + "pagination": { + "ListCrls": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "crls" + }, + "ListProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "profiles" + }, + "ListSubjects": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "subjects" + }, + "ListTrustAnchors": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "trustAnchors" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rolesanywhere/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rolesanywhere/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..6e60236 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rolesanywhere/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rolesanywhere/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/rolesanywhere/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rolesanywhere/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-cluster/2019-12-02/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-cluster/2019-12-02/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7d5fbf9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-cluster/2019-12-02/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-cluster/2019-12-02/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-cluster/2019-12-02/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-cluster/2019-12-02/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-cluster/2019-12-02/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-cluster/2019-12-02/paginators-1.json new file mode 100644 index 0000000..a2ef01b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-cluster/2019-12-02/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListRoutingControls": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RoutingControls" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-cluster/2019-12-02/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-cluster/2019-12-02/service-2.json.gz new file mode 100644 index 0000000..ca2eab8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-cluster/2019-12-02/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-control-config/2020-11-02/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-control-config/2020-11-02/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..442b5f9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-control-config/2020-11-02/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-control-config/2020-11-02/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-control-config/2020-11-02/paginators-1.json new file mode 100644 index 0000000..024682b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-control-config/2020-11-02/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListAssociatedRoute53HealthChecks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "HealthCheckIds" + }, + "ListClusters": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Clusters" + }, + "ListControlPanels": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ControlPanels" + }, + "ListRoutingControls": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RoutingControls" + }, + "ListSafetyRules": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SafetyRules" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-control-config/2020-11-02/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-control-config/2020-11-02/service-2.json.gz new file mode 100644 index 0000000..fc1adc7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-control-config/2020-11-02/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-control-config/2020-11-02/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-control-config/2020-11-02/waiters-2.json new file mode 100644 index 0000000..1794757 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-control-config/2020-11-02/waiters-2.json @@ -0,0 +1,152 @@ +{ + "version": 2, + "waiters": { + "ClusterCreated": { + "description": "Wait until a cluster is created", + "operation": "DescribeCluster", + "delay": 5, + "maxAttempts": 26, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "Cluster.Status", + "expected": "DEPLOYED" + }, + { + "state": "retry", + "matcher": "path", + "argument": "Cluster.Status", + "expected": "PENDING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "ClusterDeleted": { + "description": "Wait for a cluster to be deleted", + "operation": "DescribeCluster", + "delay": 5, + "maxAttempts": 26, + "acceptors": [ + { + "state": "success", + "matcher": "status", + "expected": 404 + }, + { + "state": "retry", + "matcher": "path", + "argument": "Cluster.Status", + "expected": "PENDING_DELETION" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "ControlPanelCreated": { + "description": "Wait until a control panel is created", + "operation": "DescribeControlPanel", + "delay": 5, + "maxAttempts": 26, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "ControlPanel.Status", + "expected": "DEPLOYED" + }, + { + "state": "retry", + "matcher": "path", + "argument": "ControlPanel.Status", + "expected": "PENDING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "ControlPanelDeleted": { + "description": "Wait until a control panel is deleted", + "operation": "DescribeControlPanel", + "delay": 5, + "maxAttempts": 26, + "acceptors": [ + { + "state": "success", + "matcher": "status", + "expected": 404 + }, + { + "state": "retry", + "matcher": "path", + "argument": "ControlPanel.Status", + "expected": "PENDING_DELETION" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "RoutingControlCreated": { + "description": "Wait until a routing control is created", + "operation": "DescribeRoutingControl", + "delay": 5, + "maxAttempts": 26, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "RoutingControl.Status", + "expected": "DEPLOYED" + }, + { + "state": "retry", + "matcher": "path", + "argument": "RoutingControl.Status", + "expected": "PENDING" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + }, + "RoutingControlDeleted": { + "description": "Wait for a routing control to be deleted", + "operation": "DescribeRoutingControl", + "delay": 5, + "maxAttempts": 26, + "acceptors": [ + { + "state": "success", + "matcher": "status", + "expected": 404 + }, + { + "state": "retry", + "matcher": "path", + "argument": "RoutingControl.Status", + "expected": "PENDING_DELETION" + }, + { + "state": "retry", + "matcher": "status", + "expected": 500 + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-readiness/2019-12-02/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-readiness/2019-12-02/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..51cff5c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-readiness/2019-12-02/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-readiness/2019-12-02/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-readiness/2019-12-02/paginators-1.json new file mode 100644 index 0000000..a71f088 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-readiness/2019-12-02/paginators-1.json @@ -0,0 +1,77 @@ +{ + "pagination": { + "ListReadinessChecks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ReadinessChecks" + }, + "ListResourceSets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResourceSets" + }, + "ListCells": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Cells" + }, + "ListRecoveryGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RecoveryGroups" + }, + "ListRules": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Rules" + }, + "ListCrossAccountAuthorizations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CrossAccountAuthorizations" + }, + "GetCellReadinessSummary": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ReadinessChecks", + "non_aggregate_keys": [ + "Readiness" + ] + }, + "GetRecoveryGroupReadinessSummary": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ReadinessChecks", + "non_aggregate_keys": [ + "Readiness" + ] + }, + "GetReadinessCheckStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Resources", + "non_aggregate_keys": [ + "Readiness", + "Messages" + ] + }, + "GetReadinessCheckResourceStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Rules", + "non_aggregate_keys": [ + "Readiness" + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-readiness/2019-12-02/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-readiness/2019-12-02/service-2.json.gz new file mode 100644 index 0000000..c6c29a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53-recovery-readiness/2019-12-02/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..cdd96ff Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/examples-1.json new file mode 100644 index 0000000..d757c2b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/examples-1.json @@ -0,0 +1,762 @@ +{ + "version": "1.0", + "examples": { + "AssociateVPCWithHostedZone": [ + { + "input": { + "Comment": "", + "HostedZoneId": "Z3M3LMPEXAMPLE", + "VPC": { + "VPCId": "vpc-1a2b3c4d", + "VPCRegion": "us-east-2" + } + }, + "output": { + "ChangeInfo": { + "Comment": "", + "Id": "/change/C3HC6WDB2UANE2", + "Status": "INSYNC", + "SubmittedAt": "2017-01-31T01:36:41.958Z" + } + }, + "comments": { + "input": { + }, + "output": { + "Status": "Valid values are PENDING and INSYNC.", + "SubmittedAt": "The date and time are in Coordinated Universal Time (UTC) and ISO 8601 format." + } + }, + "description": "The following example associates the VPC with ID vpc-1a2b3c4d with the hosted zone with ID Z3M3LMPEXAMPLE.", + "id": "to-associate-a-vpc-with-a-hosted-zone-1484069228699", + "title": "To associate a VPC with a hosted zone" + } + ], + "ChangeResourceRecordSets": [ + { + "input": { + "ChangeBatch": { + "Changes": [ + { + "Action": "CREATE", + "ResourceRecordSet": { + "Name": "example.com", + "ResourceRecords": [ + { + "Value": "192.0.2.44" + } + ], + "TTL": 60, + "Type": "A" + } + } + ], + "Comment": "Web server for example.com" + }, + "HostedZoneId": "Z3M3LMPEXAMPLE" + }, + "output": { + "ChangeInfo": { + "Comment": "Web server for example.com", + "Id": "/change/C2682N5HXP0BZ4", + "Status": "PENDING", + "SubmittedAt": "2017-02-10T01:36:41.958Z" + } + }, + "comments": { + "input": { + "Action": "Valid values: CREATE, DELETE, UPSERT", + "TTL": "The amount of time in seconds that you want DNS resolvers to cache the values in this resource record set before submitting another request to Route 53", + "Value": "The value that is applicable to the value of Type. For example, if Type is A, Value is an IPv4 address" + }, + "output": { + "SubmittedAt": "The date and time are in Coordinated Universal Time (UTC) and ISO 8601 format." + } + }, + "description": "The following example creates a resource record set that routes Internet traffic to a resource with an IP address of 192.0.2.44.", + "id": "to-create-update-or-delete-resource-record-sets-1484344703668", + "title": "To create a basic resource record set" + }, + { + "input": { + "ChangeBatch": { + "Changes": [ + { + "Action": "CREATE", + "ResourceRecordSet": { + "HealthCheckId": "abcdef11-2222-3333-4444-555555fedcba", + "Name": "example.com", + "ResourceRecords": [ + { + "Value": "192.0.2.44" + } + ], + "SetIdentifier": "Seattle data center", + "TTL": 60, + "Type": "A", + "Weight": 100 + } + }, + { + "Action": "CREATE", + "ResourceRecordSet": { + "HealthCheckId": "abcdef66-7777-8888-9999-000000fedcba", + "Name": "example.com", + "ResourceRecords": [ + { + "Value": "192.0.2.45" + } + ], + "SetIdentifier": "Portland data center", + "TTL": 60, + "Type": "A", + "Weight": 200 + } + } + ], + "Comment": "Web servers for example.com" + }, + "HostedZoneId": "Z3M3LMPEXAMPLE" + }, + "output": { + "ChangeInfo": { + "Comment": "Web servers for example.com", + "Id": "/change/C2682N5HXP0BZ4", + "Status": "PENDING", + "SubmittedAt": "2017-02-10T01:36:41.958Z" + } + }, + "comments": { + "input": { + "Action": "Valid values: CREATE, DELETE, UPSERT", + "TTL": "The amount of time in seconds that you want DNS resolvers to cache the values in this resource record set before submitting another request to Route 53. TTLs must be the same for all weighted resource record sets that have the same name and type.", + "Value": "The value that is applicable to the value of Type. For example, if Type is A, Value is an IPv4 address" + }, + "output": { + "SubmittedAt": "The date and time are in Coordinated Universal Time (UTC) and ISO 8601 format." + } + }, + "description": "The following example creates two weighted resource record sets. The resource with a Weight of 100 will get 1/3rd of traffic (100/100+200), and the other resource will get the rest of the traffic for example.com.", + "id": "to-create-weighted-resource-record-sets-1484348208522", + "title": "To create weighted resource record sets" + }, + { + "input": { + "ChangeBatch": { + "Changes": [ + { + "Action": "CREATE", + "ResourceRecordSet": { + "AliasTarget": { + "DNSName": "d123rk29d0stfj.cloudfront.net", + "EvaluateTargetHealth": false, + "HostedZoneId": "Z2FDTNDATAQYW2" + }, + "Name": "example.com", + "Type": "A" + } + } + ], + "Comment": "CloudFront distribution for example.com" + }, + "HostedZoneId": "Z3M3LMPEXAMPLE" + }, + "output": { + "ChangeInfo": { + "Comment": "CloudFront distribution for example.com", + "Id": "/change/C2682N5HXP0BZ4", + "Status": "PENDING", + "SubmittedAt": "2017-02-10T01:36:41.958Z" + } + }, + "comments": { + "input": { + "Action": "Valid values: CREATE, DELETE, UPSERT", + "DNSName": "The DNS name assigned to the resource", + "HostedZoneId": "Depends on the type of resource that you want to route traffic to", + "Type": "A or AAAA, depending on the type of resource that you want to route traffic to" + }, + "output": { + "SubmittedAt": "The date and time are in Coordinated Universal Time (UTC) and ISO 8601 format." + } + }, + "description": "The following example creates an alias resource record set that routes traffic to a CloudFront distribution.", + "id": "to-create-an-alias-resource-record-set-1484348404062", + "title": "To create an alias resource record set" + }, + { + "input": { + "ChangeBatch": { + "Changes": [ + { + "Action": "CREATE", + "ResourceRecordSet": { + "AliasTarget": { + "DNSName": "example-com-123456789.us-east-2.elb.amazonaws.com ", + "EvaluateTargetHealth": true, + "HostedZoneId": "Z3AADJGX6KTTL2" + }, + "Name": "example.com", + "SetIdentifier": "Ohio region", + "Type": "A", + "Weight": 100 + } + }, + { + "Action": "CREATE", + "ResourceRecordSet": { + "AliasTarget": { + "DNSName": "example-com-987654321.us-west-2.elb.amazonaws.com ", + "EvaluateTargetHealth": true, + "HostedZoneId": "Z1H1FL5HABSF5" + }, + "Name": "example.com", + "SetIdentifier": "Oregon region", + "Type": "A", + "Weight": 200 + } + } + ], + "Comment": "ELB load balancers for example.com" + }, + "HostedZoneId": "Z3M3LMPEXAMPLE" + }, + "output": { + "ChangeInfo": { + "Comment": "ELB load balancers for example.com", + "Id": "/change/C2682N5HXP0BZ4", + "Status": "PENDING", + "SubmittedAt": "2017-02-10T01:36:41.958Z" + } + }, + "comments": { + "input": { + "Action": "Valid values: CREATE, DELETE, UPSERT", + "DNSName": "The DNS name assigned to the resource", + "HostedZoneId": "Depends on the type of resource that you want to route traffic to", + "Type": "A or AAAA, depending on the type of resource that you want to route traffic to" + }, + "output": { + "SubmittedAt": "The date and time are in Coordinated Universal Time (UTC) and ISO 8601 format." + } + }, + "description": "The following example creates two weighted alias resource record sets that route traffic to ELB load balancers. The resource with a Weight of 100 will get 1/3rd of traffic (100/100+200), and the other resource will get the rest of the traffic for example.com.", + "id": "to-create-weighted-alias-resource-record-sets-1484349467416", + "title": "To create weighted alias resource record sets" + }, + { + "input": { + "ChangeBatch": { + "Changes": [ + { + "Action": "CREATE", + "ResourceRecordSet": { + "HealthCheckId": "abcdef11-2222-3333-4444-555555fedcba", + "Name": "example.com", + "Region": "us-east-2", + "ResourceRecords": [ + { + "Value": "192.0.2.44" + } + ], + "SetIdentifier": "Ohio region", + "TTL": 60, + "Type": "A" + } + }, + { + "Action": "CREATE", + "ResourceRecordSet": { + "HealthCheckId": "abcdef66-7777-8888-9999-000000fedcba", + "Name": "example.com", + "Region": "us-west-2", + "ResourceRecords": [ + { + "Value": "192.0.2.45" + } + ], + "SetIdentifier": "Oregon region", + "TTL": 60, + "Type": "A" + } + } + ], + "Comment": "EC2 instances for example.com" + }, + "HostedZoneId": "Z3M3LMPEXAMPLE" + }, + "output": { + "ChangeInfo": { + "Comment": "EC2 instances for example.com", + "Id": "/change/C2682N5HXP0BZ4", + "Status": "PENDING", + "SubmittedAt": "2017-02-10T01:36:41.958Z" + } + }, + "comments": { + "input": { + "Action": "Valid values: CREATE, DELETE, UPSERT", + "TTL": "The amount of time in seconds that you want DNS resolvers to cache the values in this resource record set before submitting another request to Route 53", + "Value": "The value that is applicable to the value of Type. For example, if Type is A, Value is an IPv4 address" + }, + "output": { + "SubmittedAt": "The date and time are in Coordinated Universal Time (UTC) and ISO 8601 format." + } + }, + "description": "The following example creates two latency resource record sets that route traffic to EC2 instances. Traffic for example.com is routed either to the Ohio region or the Oregon region, depending on the latency between the user and those regions.", + "id": "to-create-latency-resource-record-sets-1484350219917", + "title": "To create latency resource record sets" + }, + { + "input": { + "ChangeBatch": { + "Changes": [ + { + "Action": "CREATE", + "ResourceRecordSet": { + "AliasTarget": { + "DNSName": "example-com-123456789.us-east-2.elb.amazonaws.com ", + "EvaluateTargetHealth": true, + "HostedZoneId": "Z3AADJGX6KTTL2" + }, + "Name": "example.com", + "Region": "us-east-2", + "SetIdentifier": "Ohio region", + "Type": "A" + } + }, + { + "Action": "CREATE", + "ResourceRecordSet": { + "AliasTarget": { + "DNSName": "example-com-987654321.us-west-2.elb.amazonaws.com ", + "EvaluateTargetHealth": true, + "HostedZoneId": "Z1H1FL5HABSF5" + }, + "Name": "example.com", + "Region": "us-west-2", + "SetIdentifier": "Oregon region", + "Type": "A" + } + } + ], + "Comment": "ELB load balancers for example.com" + }, + "HostedZoneId": "Z3M3LMPEXAMPLE" + }, + "output": { + "ChangeInfo": { + "Comment": "ELB load balancers for example.com", + "Id": "/change/C2682N5HXP0BZ4", + "Status": "PENDING", + "SubmittedAt": "2017-02-10T01:36:41.958Z" + } + }, + "comments": { + "input": { + "Action": "Valid values: CREATE, DELETE, UPSERT", + "DNSName": "The DNS name assigned to the resource", + "HostedZoneId": "Depends on the type of resource that you want to route traffic to", + "Type": "A or AAAA, depending on the type of resource that you want to route traffic to" + }, + "output": { + "SubmittedAt": "The date and time are in Coordinated Universal Time (UTC) and ISO 8601 format." + } + }, + "description": "The following example creates two latency alias resource record sets that route traffic for example.com to ELB load balancers. Requests are routed either to the Ohio region or the Oregon region, depending on the latency between the user and those regions.", + "id": "to-create-latency-alias-resource-record-sets-1484601774179", + "title": "To create latency alias resource record sets" + }, + { + "input": { + "ChangeBatch": { + "Changes": [ + { + "Action": "CREATE", + "ResourceRecordSet": { + "Failover": "PRIMARY", + "HealthCheckId": "abcdef11-2222-3333-4444-555555fedcba", + "Name": "example.com", + "ResourceRecords": [ + { + "Value": "192.0.2.44" + } + ], + "SetIdentifier": "Ohio region", + "TTL": 60, + "Type": "A" + } + }, + { + "Action": "CREATE", + "ResourceRecordSet": { + "Failover": "SECONDARY", + "HealthCheckId": "abcdef66-7777-8888-9999-000000fedcba", + "Name": "example.com", + "ResourceRecords": [ + { + "Value": "192.0.2.45" + } + ], + "SetIdentifier": "Oregon region", + "TTL": 60, + "Type": "A" + } + } + ], + "Comment": "Failover configuration for example.com" + }, + "HostedZoneId": "Z3M3LMPEXAMPLE" + }, + "output": { + "ChangeInfo": { + "Comment": "Failover configuration for example.com", + "Id": "/change/C2682N5HXP0BZ4", + "Status": "PENDING", + "SubmittedAt": "2017-02-10T01:36:41.958Z" + } + }, + "comments": { + "input": { + "Action": "Valid values: CREATE, DELETE, UPSERT", + "TTL": "The amount of time in seconds that you want DNS resolvers to cache the values in this resource record set before submitting another request to Route 53", + "Value": "The value that is applicable to the value of Type. For example, if Type is A, Value is an IPv4 address" + }, + "output": { + "SubmittedAt": "The date and time are in Coordinated Universal Time (UTC) and ISO 8601 format." + } + }, + "description": "The following example creates primary and secondary failover resource record sets that route traffic to EC2 instances. Traffic is generally routed to the primary resource, in the Ohio region. If that resource is unavailable, traffic is routed to the secondary resource, in the Oregon region.", + "id": "to-create-failover-resource-record-sets-1484604541740", + "title": "To create failover resource record sets" + }, + { + "input": { + "ChangeBatch": { + "Changes": [ + { + "Action": "CREATE", + "ResourceRecordSet": { + "AliasTarget": { + "DNSName": "example-com-123456789.us-east-2.elb.amazonaws.com ", + "EvaluateTargetHealth": true, + "HostedZoneId": "Z3AADJGX6KTTL2" + }, + "Failover": "PRIMARY", + "Name": "example.com", + "SetIdentifier": "Ohio region", + "Type": "A" + } + }, + { + "Action": "CREATE", + "ResourceRecordSet": { + "AliasTarget": { + "DNSName": "example-com-987654321.us-west-2.elb.amazonaws.com ", + "EvaluateTargetHealth": true, + "HostedZoneId": "Z1H1FL5HABSF5" + }, + "Failover": "SECONDARY", + "Name": "example.com", + "SetIdentifier": "Oregon region", + "Type": "A" + } + } + ], + "Comment": "Failover alias configuration for example.com" + }, + "HostedZoneId": "Z3M3LMPEXAMPLE" + }, + "output": { + "ChangeInfo": { + "Comment": "Failover alias configuration for example.com", + "Id": "/change/C2682N5HXP0BZ4", + "Status": "PENDING", + "SubmittedAt": "2017-02-10T01:36:41.958Z" + } + }, + "comments": { + "input": { + "Action": "Valid values: CREATE, DELETE, UPSERT", + "DNSName": "The DNS name assigned to the resource", + "HostedZoneId": "Depends on the type of resource that you want to route traffic to", + "Type": "A or AAAA, depending on the type of resource that you want to route traffic to" + }, + "output": { + "SubmittedAt": "The date and time are in Coordinated Universal Time (UTC) and ISO 8601 format." + } + }, + "description": "The following example creates primary and secondary failover alias resource record sets that route traffic to ELB load balancers. Traffic is generally routed to the primary resource, in the Ohio region. If that resource is unavailable, traffic is routed to the secondary resource, in the Oregon region.", + "id": "to-create-failover-alias-resource-record-sets-1484607497724", + "title": "To create failover alias resource record sets" + }, + { + "input": { + "ChangeBatch": { + "Changes": [ + { + "Action": "CREATE", + "ResourceRecordSet": { + "GeoLocation": { + "ContinentCode": "NA" + }, + "Name": "example.com", + "ResourceRecords": [ + { + "Value": "192.0.2.44" + } + ], + "SetIdentifier": "North America", + "TTL": 60, + "Type": "A" + } + }, + { + "Action": "CREATE", + "ResourceRecordSet": { + "GeoLocation": { + "ContinentCode": "SA" + }, + "Name": "example.com", + "ResourceRecords": [ + { + "Value": "192.0.2.45" + } + ], + "SetIdentifier": "South America", + "TTL": 60, + "Type": "A" + } + }, + { + "Action": "CREATE", + "ResourceRecordSet": { + "GeoLocation": { + "ContinentCode": "EU" + }, + "Name": "example.com", + "ResourceRecords": [ + { + "Value": "192.0.2.46" + } + ], + "SetIdentifier": "Europe", + "TTL": 60, + "Type": "A" + } + }, + { + "Action": "CREATE", + "ResourceRecordSet": { + "GeoLocation": { + "CountryCode": "*" + }, + "Name": "example.com", + "ResourceRecords": [ + { + "Value": "192.0.2.47" + } + ], + "SetIdentifier": "Other locations", + "TTL": 60, + "Type": "A" + } + } + ], + "Comment": "Geolocation configuration for example.com" + }, + "HostedZoneId": "Z3M3LMPEXAMPLE" + }, + "output": { + "ChangeInfo": { + "Comment": "Geolocation configuration for example.com", + "Id": "/change/C2682N5HXP0BZ4", + "Status": "PENDING", + "SubmittedAt": "2017-02-10T01:36:41.958Z" + } + }, + "comments": { + "input": { + "Action": "Valid values: CREATE, DELETE, UPSERT", + "TTL": "The amount of time in seconds that you want DNS resolvers to cache the values in this resource record set before submitting another request to Route 53", + "Value": "The value that is applicable to the value of Type. For example, if Type is A, Value is an IPv4 address" + }, + "output": { + "SubmittedAt": "The date and time are in Coordinated Universal Time (UTC) and ISO 8601 format." + } + }, + "description": "The following example creates four geolocation resource record sets that use IPv4 addresses to route traffic to resources such as web servers running on EC2 instances. Traffic is routed to one of four IP addresses, for North America (NA), for South America (SA), for Europe (EU), and for all other locations (*).", + "id": "to-create-geolocation-resource-record-sets-1484612462466", + "title": "To create geolocation resource record sets" + }, + { + "input": { + "ChangeBatch": { + "Changes": [ + { + "Action": "CREATE", + "ResourceRecordSet": { + "AliasTarget": { + "DNSName": "example-com-123456789.us-east-2.elb.amazonaws.com ", + "EvaluateTargetHealth": true, + "HostedZoneId": "Z3AADJGX6KTTL2" + }, + "GeoLocation": { + "ContinentCode": "NA" + }, + "Name": "example.com", + "SetIdentifier": "North America", + "Type": "A" + } + }, + { + "Action": "CREATE", + "ResourceRecordSet": { + "AliasTarget": { + "DNSName": "example-com-234567890.sa-east-1.elb.amazonaws.com ", + "EvaluateTargetHealth": true, + "HostedZoneId": "Z2P70J7HTTTPLU" + }, + "GeoLocation": { + "ContinentCode": "SA" + }, + "Name": "example.com", + "SetIdentifier": "South America", + "Type": "A" + } + }, + { + "Action": "CREATE", + "ResourceRecordSet": { + "AliasTarget": { + "DNSName": "example-com-234567890.eu-central-1.elb.amazonaws.com ", + "EvaluateTargetHealth": true, + "HostedZoneId": "Z215JYRZR1TBD5" + }, + "GeoLocation": { + "ContinentCode": "EU" + }, + "Name": "example.com", + "SetIdentifier": "Europe", + "Type": "A" + } + }, + { + "Action": "CREATE", + "ResourceRecordSet": { + "AliasTarget": { + "DNSName": "example-com-234567890.ap-southeast-1.elb.amazonaws.com ", + "EvaluateTargetHealth": true, + "HostedZoneId": "Z1LMS91P8CMLE5" + }, + "GeoLocation": { + "CountryCode": "*" + }, + "Name": "example.com", + "SetIdentifier": "Other locations", + "Type": "A" + } + } + ], + "Comment": "Geolocation alias configuration for example.com" + }, + "HostedZoneId": "Z3M3LMPEXAMPLE" + }, + "output": { + "ChangeInfo": { + "Comment": "Geolocation alias configuration for example.com", + "Id": "/change/C2682N5HXP0BZ4", + "Status": "PENDING", + "SubmittedAt": "2017-02-10T01:36:41.958Z" + } + }, + "comments": { + "input": { + "Action": "Valid values: CREATE, DELETE, UPSERT", + "DNSName": "The DNS name assigned to the resource", + "HostedZoneId": "Depends on the type of resource that you want to route traffic to", + "Type": "A or AAAA, depending on the type of resource that you want to route traffic to" + }, + "output": { + "SubmittedAt": "The date and time are in Coordinated Universal Time (UTC) and ISO 8601 format." + } + }, + "description": "The following example creates four geolocation alias resource record sets that route traffic to ELB load balancers. Traffic is routed to one of four IP addresses, for North America (NA), for South America (SA), for Europe (EU), and for all other locations (*).", + "id": "to-create-geolocation-alias-resource-record-sets-1484612871203", + "title": "To create geolocation alias resource record sets" + } + ], + "ChangeTagsForResource": [ + { + "input": { + "AddTags": [ + { + "Key": "apex", + "Value": "3874" + }, + { + "Key": "acme", + "Value": "4938" + } + ], + "RemoveTagKeys": [ + "Nadir" + ], + "ResourceId": "Z3M3LMPEXAMPLE", + "ResourceType": "hostedzone" + }, + "output": { + }, + "comments": { + "input": { + "ResourceType": "Valid values are healthcheck and hostedzone." + }, + "output": { + } + }, + "description": "The following example adds two tags and removes one tag from the hosted zone with ID Z3M3LMPEXAMPLE.", + "id": "to-add-or-remove-tags-from-a-hosted-zone-or-health-check-1484084752409", + "title": "To add or remove tags from a hosted zone or health check" + } + ], + "GetHostedZone": [ + { + "input": { + "Id": "Z3M3LMPEXAMPLE" + }, + "output": { + "DelegationSet": { + "NameServers": [ + "ns-2048.awsdns-64.com", + "ns-2049.awsdns-65.net", + "ns-2050.awsdns-66.org", + "ns-2051.awsdns-67.co.uk" + ] + }, + "HostedZone": { + "CallerReference": "C741617D-04E4-F8DE-B9D7-0D150FC61C2E", + "Config": { + "PrivateZone": false + }, + "Id": "/hostedzone/Z3M3LMPEXAMPLE", + "Name": "myawsbucket.com.", + "ResourceRecordSetCount": 8 + } + }, + "comments": { + "input": { + }, + "output": { + "Id": "The ID of the hosted zone that you specified in the GetHostedZone request.", + "Name": "The name of the hosted zone.", + "NameServers": "The servers that you specify in your domain configuration.", + "PrivateZone": "True if this is a private hosted zone, false if it's a public hosted zone." + } + }, + "description": "The following example gets information about the Z3M3LMPEXAMPLE hosted zone.", + "id": "to-get-information-about-a-hosted-zone-1481752361124", + "title": "To get information about a hosted zone" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/paginators-1.json new file mode 100644 index 0000000..2c37096 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/paginators-1.json @@ -0,0 +1,67 @@ +{ + "pagination": { + "ListHealthChecks": { + "input_token": "Marker", + "output_token": "NextMarker", + "more_results": "IsTruncated", + "limit_key": "MaxItems", + "result_key": "HealthChecks" + }, + "ListHostedZones": { + "input_token": "Marker", + "output_token": "NextMarker", + "more_results": "IsTruncated", + "limit_key": "MaxItems", + "result_key": "HostedZones" + }, + "ListResourceRecordSets": { + "more_results": "IsTruncated", + "limit_key": "MaxItems", + "result_key": "ResourceRecordSets", + "input_token": [ + "StartRecordName", + "StartRecordType", + "StartRecordIdentifier" + ], + "output_token": [ + "NextRecordName", + "NextRecordType", + "NextRecordIdentifier" + ] + }, + "ListVPCAssociationAuthorizations": { + "input_token": "NextToken", + "output_token": "NextToken", + "non_aggregate_keys": [ + "HostedZoneId" + ], + "result_key": [ + "VPCs" + ] + }, + "ListQueryLoggingConfigs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "QueryLoggingConfigs" + }, + "ListCidrBlocks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CidrBlocks" + }, + "ListCidrCollections": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CidrCollections" + }, + "ListCidrLocations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CidrLocations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/service-2.json.gz new file mode 100644 index 0000000..43949b9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/waiters-2.json new file mode 100644 index 0000000..94aad39 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53/2013-04-01/waiters-2.json @@ -0,0 +1,18 @@ +{ + "version": 2, + "waiters": { + "ResourceRecordSetsChanged": { + "delay": 30, + "maxAttempts": 60, + "operation": "GetChange", + "acceptors": [ + { + "matcher": "path", + "expected": "INSYNC", + "argument": "ChangeInfo.Status", + "state": "success" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53domains/2014-05-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53domains/2014-05-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ffdf480 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53domains/2014-05-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53domains/2014-05-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/route53domains/2014-05-15/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53domains/2014-05-15/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53domains/2014-05-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/route53domains/2014-05-15/paginators-1.json new file mode 100644 index 0000000..c2f5cbc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53domains/2014-05-15/paginators-1.json @@ -0,0 +1,29 @@ +{ + "version": "1.0", + "pagination": { + "ListDomains": { + "limit_key": "MaxItems", + "input_token": "Marker", + "output_token": "NextPageMarker", + "result_key": "Domains" + }, + "ListOperations": { + "limit_key": "MaxItems", + "input_token": "Marker", + "output_token": "NextPageMarker", + "result_key": "Operations" + }, + "ViewBilling": { + "input_token": "Marker", + "limit_key": "MaxItems", + "output_token": "NextPageMarker", + "result_key": "BillingRecords" + }, + "ListPrices": { + "input_token": "Marker", + "limit_key": "MaxItems", + "output_token": "NextPageMarker", + "result_key": "Prices" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53domains/2014-05-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53domains/2014-05-15/service-2.json.gz new file mode 100644 index 0000000..94c47e8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53domains/2014-05-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53globalresolver/2022-09-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53globalresolver/2022-09-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..5dd1245 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53globalresolver/2022-09-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53globalresolver/2022-09-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/route53globalresolver/2022-09-27/paginators-1.json new file mode 100644 index 0000000..7b86e94 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53globalresolver/2022-09-27/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "ListAccessSources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "accessSources" + }, + "ListAccessTokens": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "accessTokens" + }, + "ListDNSViews": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dnsViews" + }, + "ListFirewallDomainLists": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "firewallDomainLists" + }, + "ListFirewallDomains": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "domains" + }, + "ListFirewallRules": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "firewallRules" + }, + "ListGlobalResolvers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "globalResolvers" + }, + "ListHostedZoneAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "hostedZoneAssociations" + }, + "ListManagedFirewallDomainLists": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "managedFirewallDomainLists" + }, + "ListSharedDNSViews": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dnsViews" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53globalresolver/2022-09-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53globalresolver/2022-09-27/service-2.json.gz new file mode 100644 index 0000000..60eeddb Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53globalresolver/2022-09-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53globalresolver/2022-09-27/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/route53globalresolver/2022-09-27/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53globalresolver/2022-09-27/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53profiles/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53profiles/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6a67791 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53profiles/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53profiles/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/route53profiles/2018-05-10/paginators-1.json new file mode 100644 index 0000000..a321bb7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53profiles/2018-05-10/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListProfileAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ProfileAssociations" + }, + "ListProfileResourceAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ProfileResourceAssociations" + }, + "ListProfiles": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ProfileSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53profiles/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53profiles/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..971a5c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53profiles/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c120c81 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/paginators-1.json new file mode 100644 index 0000000..9d93588 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/paginators-1.json @@ -0,0 +1,106 @@ +{ + "pagination": { + "ListTagsForResource": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Tags" + }, + "ListResolverEndpointIpAddresses": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "IpAddresses" + }, + "ListResolverEndpoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResolverEndpoints" + }, + "ListResolverQueryLogConfigAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResolverQueryLogConfigAssociations" + }, + "ListResolverQueryLogConfigs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResolverQueryLogConfigs" + }, + "ListResolverRuleAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResolverRuleAssociations" + }, + "ListResolverRules": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResolverRules" + }, + "ListResolverDnssecConfigs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResolverDnssecConfigs" + }, + "ListFirewallConfigs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FirewallConfigs" + }, + "ListFirewallDomainLists": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FirewallDomainLists" + }, + "ListFirewallDomains": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Domains" + }, + "ListFirewallRuleGroupAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FirewallRuleGroupAssociations" + }, + "ListFirewallRuleGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FirewallRuleGroups" + }, + "ListFirewallRules": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FirewallRules" + }, + "ListResolverConfigs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResolverConfigs" + }, + "ListOutpostResolvers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "OutpostResolvers" + }, + "ListFirewallRuleTypes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FirewallRuleTypes" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/paginators-1.sdk-extras.json new file mode 100644 index 0000000..6808793 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/paginators-1.sdk-extras.json @@ -0,0 +1,39 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListResolverEndpointIpAddresses": { + "non_aggregate_keys": [ + "MaxResults" + ] + }, + "ListResolverEndpoints": { + "non_aggregate_keys": [ + "MaxResults" + ] + }, + "ListResolverQueryLogConfigAssociations": { + "non_aggregate_keys": [ + "TotalCount", + "TotalFilteredCount" + ] + }, + "ListResolverQueryLogConfigs": { + "non_aggregate_keys": [ + "TotalCount", + "TotalFilteredCount" + ] + }, + "ListResolverRuleAssociations": { + "non_aggregate_keys": [ + "MaxResults" + ] + }, + "ListResolverRules": { + "non_aggregate_keys": [ + "MaxResults" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/service-2.json.gz new file mode 100644 index 0000000..32b5028 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/route53resolver/2018-04-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rtbfabric/2023-05-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rtbfabric/2023-05-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3c7d01b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rtbfabric/2023-05-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rtbfabric/2023-05-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/rtbfabric/2023-05-15/paginators-1.json new file mode 100644 index 0000000..7b17fb6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rtbfabric/2023-05-15/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListLinks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "links" + }, + "ListRequesterGateways": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "gatewayIds" + }, + "ListResponderGateways": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "gatewayIds" + }, + "ListCertificateAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "certificateAssociations" + }, + "ListLinkRoutingRules": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "rules" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rtbfabric/2023-05-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rtbfabric/2023-05-15/service-2.json.gz new file mode 100644 index 0000000..3f68bab Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rtbfabric/2023-05-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rtbfabric/2023-05-15/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/rtbfabric/2023-05-15/waiters-2.json new file mode 100644 index 0000000..fcd71ed --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rtbfabric/2023-05-15/waiters-2.json @@ -0,0 +1,360 @@ +{ + "version" : 2, + "waiters" : { + "CertificateAssociated" : { + "delay" : 15, + "maxAttempts" : 8, + "operation" : "GetCertificateAssociation", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ASSOCIATED" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DISASSOCIATED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "PENDING_DISASSOCIATION" + } ] + }, + "CertificateDisassociated" : { + "delay" : 15, + "maxAttempts" : 8, + "operation" : "GetCertificateAssociation", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "DISASSOCIATED" + }, { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "PENDING_ASSOCIATION" + } ] + }, + "InboundExternalLinkActive" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetInboundExternalLink", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "REJECTED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "ISOLATED" + } ] + }, + "InboundExternalLinkDeleted" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetInboundExternalLink", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "DELETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "REJECTED" + } ] + }, + "LinkAccepted" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetLink", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACCEPTED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "REJECTED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETED" + } ] + }, + "LinkActive" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetLink", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "REJECTED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETED" + } ] + }, + "LinkDeleted" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetLink", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "DELETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "REJECTED" + } ] + }, + "LinkRoutingRuleActive" : { + "delay" : 5, + "maxAttempts" : 24, + "operation" : "GetLinkRoutingRule", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETED" + } ] + }, + "LinkRoutingRuleDeleted" : { + "delay" : 5, + "maxAttempts" : 24, + "operation" : "GetLinkRoutingRule", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "DELETED" + }, { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "OutboundExternalLinkActive" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetOutboundExternalLink", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "REJECTED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "ISOLATED" + } ] + }, + "OutboundExternalLinkDeleted" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetOutboundExternalLink", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "DELETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "FAILED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "REJECTED" + } ] + }, + "RequesterGatewayActive" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetRequesterGateway", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "ERROR" + } ] + }, + "RequesterGatewayDeleted" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetRequesterGateway", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "DELETED" + }, { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "ERROR" + } ] + }, + "ResponderGatewayActive" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetResponderGateway", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "DELETED" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "ERROR" + } ] + }, + "ResponderGatewayDeleted" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetResponderGateway", + "acceptors" : [ { + "matcher" : "path", + "argument" : "status", + "state" : "success", + "expected" : "DELETED" + }, { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "status", + "state" : "failure", + "expected" : "ERROR" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..e1d2fab Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/paginators-1.json new file mode 100644 index 0000000..1a04492 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "GetAppMonitorData": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Events" + }, + "ListAppMonitors": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AppMonitorSummaries" + }, + "BatchGetRumMetricDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "MetricDefinitions" + }, + "ListRumMetricsDestinations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Destinations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..e980e79 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/rum/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d162cb6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/examples-1.json new file mode 100644 index 0000000..38a47bb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/examples-1.json @@ -0,0 +1,1843 @@ +{ + "version": "1.0", + "examples": { + "AbortMultipartUpload": [ + { + "input": { + "Bucket": "examplebucket", + "Key": "bigobject", + "UploadId": "xadcOB_7YPBOJuoFiQ9cz4P3Pe6FIZwO4f7wN93uHsNBEw97pl5eNwzExg0LAT2dUN91cOmrEQHDsP3WA60CEg--" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example aborts a multipart upload.", + "id": "to-abort-a-multipart-upload-1481853354987", + "title": "To abort a multipart upload" + } + ], + "CompleteMultipartUpload": [ + { + "input": { + "Bucket": "examplebucket", + "Key": "bigobject", + "MultipartUpload": { + "Parts": [ + { + "ETag": "\"d8c2eafd90c266e19ab9dcacc479f8af\"", + "PartNumber": "1" + }, + { + "ETag": "\"d8c2eafd90c266e19ab9dcacc479f8af\"", + "PartNumber": "2" + } + ] + }, + "UploadId": "7YPBOJuoFiQ9cz4P3Pe6FIZwO4f7wN93uHsNBEw97pl5eNwzExg0LAT2dUN91cOmrEQHDsP3WA60CEg--" + }, + "output": { + "Bucket": "acexamplebucket", + "ETag": "\"4d9031c7644d8081c2829f4ea23c55f7-2\"", + "Key": "bigobject", + "Location": "https://examplebucket.s3.amazonaws.com/bigobject" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example completes a multipart upload.", + "id": "to-complete-multipart-upload-1481851590483", + "title": "To complete multipart upload" + } + ], + "CopyObject": [ + { + "input": { + "Bucket": "destinationbucket", + "CopySource": "/sourcebucket/HappyFacejpg", + "Key": "HappyFaceCopyjpg" + }, + "output": { + "CopyObjectResult": { + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "LastModified": "2016-12-15T17:38:53.000Z" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example copies an object from one bucket to another.", + "id": "to-copy-an-object-1481823186878", + "title": "To copy an object" + } + ], + "CreateBucket": [ + { + "input": { + "Bucket": "examplebucket", + "CreateBucketConfiguration": { + "LocationConstraint": "eu-west-1" + } + }, + "output": { + "Location": "http://examplebucket.s3.amazonaws.com/" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a bucket. The request specifies an AWS region where to create the bucket.", + "id": "to-create-a-bucket-in-a-specific-region-1483399072992", + "title": "To create a bucket in a specific region" + }, + { + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Location": "/examplebucket" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a bucket.", + "id": "to-create-a-bucket--1472851826060", + "title": "To create a bucket " + } + ], + "CreateMultipartUpload": [ + { + "input": { + "Bucket": "examplebucket", + "Key": "largeobject" + }, + "output": { + "Bucket": "examplebucket", + "Key": "largeobject", + "UploadId": "ibZBv_75gd9r8lH_gqXatLdxMVpAlj6ZQjEs.OwyF3953YdwbcQnMA2BLGn8Lx12fQNICtMw5KyteFeHw.Sjng--" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example initiates a multipart upload.", + "id": "to-initiate-a-multipart-upload-1481836794513", + "title": "To initiate a multipart upload" + } + ], + "DeleteBucket": [ + { + "input": { + "Bucket": "forrandall2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes the specified bucket.", + "id": "to-delete-a-bucket-1473108514262", + "title": "To delete a bucket" + } + ], + "DeleteBucketCors": [ + { + "input": { + "Bucket": "examplebucket" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes CORS configuration on a bucket.", + "id": "to-delete-cors-configuration-on-a-bucket-1483042856112", + "title": "To delete cors configuration on a bucket." + } + ], + "DeleteBucketLifecycle": [ + { + "input": { + "Bucket": "examplebucket" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes lifecycle configuration on a bucket.", + "id": "to-delete-lifecycle-configuration-on-a-bucket-1483043310583", + "title": "To delete lifecycle configuration on a bucket." + } + ], + "DeleteBucketPolicy": [ + { + "input": { + "Bucket": "examplebucket" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes bucket policy on the specified bucket.", + "id": "to-delete-bucket-policy-1483043406577", + "title": "To delete bucket policy" + } + ], + "DeleteBucketReplication": [ + { + "input": { + "Bucket": "example" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes replication configuration set on bucket.", + "id": "to-delete-bucket-replication-configuration-1483043684668", + "title": "To delete bucket replication configuration" + } + ], + "DeleteBucketTagging": [ + { + "input": { + "Bucket": "examplebucket" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes bucket tags.", + "id": "to-delete-bucket-tags-1483043846509", + "title": "To delete bucket tags" + } + ], + "DeleteBucketWebsite": [ + { + "input": { + "Bucket": "examplebucket" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes bucket website configuration.", + "id": "to-delete-bucket-website-configuration-1483043937825", + "title": "To delete bucket website configuration" + } + ], + "DeleteObject": [ + { + "input": { + "Bucket": "examplebucket", + "Key": "objectkey.jpg" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an object from an S3 bucket.", + "id": "to-delete-an-object-1472850136595", + "title": "To delete an object" + }, + { + "input": { + "Bucket": "ExampleBucket", + "Key": "HappyFace.jpg" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an object from a non-versioned bucket.", + "id": "to-delete-an-object-from-a-non-versioned-bucket-1481588533089", + "title": "To delete an object (from a non-versioned bucket)" + } + ], + "DeleteObjectTagging": [ + { + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg", + "VersionId": "ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI" + }, + "output": { + "VersionId": "ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example removes tag set associated with the specified object version. The request specifies both the object key and object version.", + "id": "to-remove-tag-set-from-an-object-version-1483145285913", + "title": "To remove tag set from an object version" + }, + { + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg" + }, + "output": { + "VersionId": "null" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example removes tag set associated with the specified object. If the bucket is versioning enabled, the operation removes tag set from the latest object version.", + "id": "to-remove-tag-set-from-an-object-1483145342862", + "title": "To remove tag set from an object" + } + ], + "DeleteObjects": [ + { + "input": { + "Bucket": "examplebucket", + "Delete": { + "Objects": [ + { + "Key": "HappyFace.jpg", + "VersionId": "2LWg7lQLnY41.maGB5Z6SWW.dcq0vx7b" + }, + { + "Key": "HappyFace.jpg", + "VersionId": "yoz3HB.ZhCS_tKVEmIOr7qYyyAaZSKVd" + } + ], + "Quiet": false + } + }, + "output": { + "Deleted": [ + { + "Key": "HappyFace.jpg", + "VersionId": "yoz3HB.ZhCS_tKVEmIOr7qYyyAaZSKVd" + }, + { + "Key": "HappyFace.jpg", + "VersionId": "2LWg7lQLnY41.maGB5Z6SWW.dcq0vx7b" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes objects from a bucket. The request specifies object versions. S3 deletes specific object versions and returns the key and versions of deleted objects in the response.", + "id": "to-delete-multiple-object-versions-from-a-versioned-bucket-1483147087737", + "title": "To delete multiple object versions from a versioned bucket" + }, + { + "input": { + "Bucket": "examplebucket", + "Delete": { + "Objects": [ + { + "Key": "objectkey1" + }, + { + "Key": "objectkey2" + } + ], + "Quiet": false + } + }, + "output": { + "Deleted": [ + { + "DeleteMarker": "true", + "DeleteMarkerVersionId": "A._w1z6EFiCF5uhtQMDal9JDkID9tQ7F", + "Key": "objectkey1" + }, + { + "DeleteMarker": "true", + "DeleteMarkerVersionId": "iOd_ORxhkKe_e8G8_oSGxt2PjsCZKlkt", + "Key": "objectkey2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes objects from a bucket. The bucket is versioned, and the request does not specify the object version to delete. In this case, all versions remain in the bucket and S3 adds a delete marker.", + "id": "to-delete-multiple-objects-from-a-versioned-bucket-1483146248805", + "title": "To delete multiple objects from a versioned bucket" + } + ], + "GetBucketCors": [ + { + "input": { + "Bucket": "examplebucket" + }, + "output": { + "CORSRules": [ + { + "AllowedHeaders": [ + "Authorization" + ], + "AllowedMethods": [ + "GET" + ], + "AllowedOrigins": [ + "*" + ], + "MaxAgeSeconds": 3000 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns cross-origin resource sharing (CORS) configuration set on a bucket.", + "id": "to-get-cors-configuration-set-on-a-bucket-1481596855475", + "title": "To get cors configuration set on a bucket" + } + ], + "GetBucketLifecycle": [ + { + "input": { + "Bucket": "acl1" + }, + "output": { + "Rules": [ + { + "Expiration": { + "Days": 1 + }, + "ID": "delete logs", + "Prefix": "123/", + "Status": "Enabled" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example gets ACL on the specified bucket.", + "id": "to-get-a-bucket-acl-1474413606503", + "title": "To get a bucket acl" + } + ], + "GetBucketLifecycleConfiguration": [ + { + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Rules": [ + { + "ID": "Rule for TaxDocs/", + "Prefix": "TaxDocs", + "Status": "Enabled", + "Transitions": [ + { + "Days": 365, + "StorageClass": "STANDARD_IA" + } + ] + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves lifecycle configuration on set on a bucket. ", + "id": "to-get-lifecycle-configuration-on-a-bucket-1481666063200", + "title": "To get lifecycle configuration on a bucket" + } + ], + "GetBucketLocation": [ + { + "input": { + "Bucket": "examplebucket" + }, + "output": { + "LocationConstraint": "us-west-2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns bucket location.", + "id": "to-get-bucket-location-1481594573609", + "title": "To get bucket location" + } + ], + "GetBucketNotification": [ + { + "input": { + "Bucket": "examplebucket" + }, + "output": { + "QueueConfiguration": { + "Event": "s3:ObjectCreated:Put", + "Events": [ + "s3:ObjectCreated:Put" + ], + "Id": "MDQ2OGQ4NDEtOTBmNi00YTM4LTk0NzYtZDIwN2I3NWQ1NjIx", + "Queue": "arn:aws:sqs:us-east-1:acct-id:S3ObjectCreatedEventQueue" + }, + "TopicConfiguration": { + "Event": "s3:ObjectCreated:Copy", + "Events": [ + "s3:ObjectCreated:Copy" + ], + "Id": "YTVkMWEzZGUtNTY1NS00ZmE2LWJjYjktMmRlY2QwODFkNTJi", + "Topic": "arn:aws:sns:us-east-1:acct-id:S3ObjectCreatedEventTopic" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns notification configuration set on a bucket.", + "id": "to-get-notification-configuration-set-on-a-bucket-1481594028667", + "title": "To get notification configuration set on a bucket" + }, + { + "input": { + "Bucket": "examplebucket" + }, + "output": { + "QueueConfiguration": { + "Event": "s3:ObjectCreated:Put", + "Events": [ + "s3:ObjectCreated:Put" + ], + "Id": "MDQ2OGQ4NDEtOTBmNi00YTM4LTk0NzYtZDIwN2I3NWQ1NjIx", + "Queue": "arn:aws:sqs:us-east-1:acct-id:S3ObjectCreatedEventQueue" + }, + "TopicConfiguration": { + "Event": "s3:ObjectCreated:Copy", + "Events": [ + "s3:ObjectCreated:Copy" + ], + "Id": "YTVkMWEzZGUtNTY1NS00ZmE2LWJjYjktMmRlY2QwODFkNTJi", + "Topic": "arn:aws:sns:us-east-1:acct-id:S3ObjectCreatedEventTopic" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns notification configuration set on a bucket.", + "id": "to-get-notification-configuration-set-on-a-bucket-1481594028667", + "title": "To get notification configuration set on a bucket" + } + ], + "GetBucketPolicy": [ + { + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Policy": "{\"Version\":\"2008-10-17\",\"Id\":\"LogPolicy\",\"Statement\":[{\"Sid\":\"Enables the log delivery group to publish logs to your bucket \",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"111122223333\"},\"Action\":[\"s3:GetBucketAcl\",\"s3:GetObjectAcl\",\"s3:PutObject\"],\"Resource\":[\"arn:aws:s3:::policytest1/*\",\"arn:aws:s3:::policytest1\"]}]}" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns bucket policy associated with a bucket.", + "id": "to-get-bucket-policy-1481595098424", + "title": "To get bucket policy" + } + ], + "GetBucketReplication": [ + { + "input": { + "Bucket": "examplebucket" + }, + "output": { + "ReplicationConfiguration": { + "Role": "arn:aws:iam::acct-id:role/example-role", + "Rules": [ + { + "Destination": { + "Bucket": "arn:aws:s3:::destination-bucket" + }, + "ID": "MWIwNTkwZmItMTE3MS00ZTc3LWJkZDEtNzRmODQwYzc1OTQy", + "Prefix": "Tax", + "Status": "Enabled" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns replication configuration set on a bucket.", + "id": "to-get-replication-configuration-set-on-a-bucket-1481593597175", + "title": "To get replication configuration set on a bucket" + } + ], + "GetBucketRequestPayment": [ + { + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Payer": "BucketOwner" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves bucket versioning configuration.", + "id": "to-get-bucket-versioning-configuration-1483037183929", + "title": "To get bucket versioning configuration" + } + ], + "GetBucketTagging": [ + { + "input": { + "Bucket": "examplebucket" + }, + "output": { + "TagSet": [ + { + "Key": "key1", + "Value": "value1" + }, + { + "Key": "key2", + "Value": "value2" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns tag set associated with a bucket", + "id": "to-get-tag-set-associated-with-a-bucket-1481593232107", + "title": "To get tag set associated with a bucket" + } + ], + "GetBucketVersioning": [ + { + "input": { + "Bucket": "examplebucket" + }, + "output": { + "MFADelete": "Disabled", + "Status": "Enabled" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves bucket versioning configuration.", + "id": "to-get-bucket-versioning-configuration-1483037183929", + "title": "To get bucket versioning configuration" + } + ], + "GetBucketWebsite": [ + { + "input": { + "Bucket": "examplebucket" + }, + "output": { + "ErrorDocument": { + "Key": "error.html" + }, + "IndexDocument": { + "Suffix": "index.html" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves website configuration of a bucket.", + "id": "to-get-bucket-website-configuration-1483037016926", + "title": "To get bucket website configuration" + } + ], + "GetObject": [ + { + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg" + }, + "output": { + "AcceptRanges": "bytes", + "ContentLength": "3191", + "ContentType": "image/jpeg", + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "LastModified": "Thu, 15 Dec 2016 01:19:41 GMT", + "Metadata": { + }, + "TagCount": 2, + "VersionId": "null" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves an object for an S3 bucket.", + "id": "to-retrieve-an-object-1481827837012", + "title": "To retrieve an object" + }, + { + "input": { + "Bucket": "examplebucket", + "Key": "SampleFile.txt", + "Range": "bytes=0-9" + }, + "output": { + "AcceptRanges": "bytes", + "ContentLength": "10", + "ContentRange": "bytes 0-9/43", + "ContentType": "text/plain", + "ETag": "\"0d94420ffd0bc68cd3d152506b97a9cc\"", + "LastModified": "Thu, 09 Oct 2014 22:57:28 GMT", + "Metadata": { + }, + "VersionId": "null" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves an object for an S3 bucket. The request specifies the range header to retrieve a specific byte range.", + "id": "to-retrieve-a-byte-range-of-an-object--1481832674603", + "title": "To retrieve a byte range of an object " + } + ], + "GetObjectAcl": [ + { + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg" + }, + "output": { + "Grants": [ + { + "Grantee": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc", + "Type": "CanonicalUser" + }, + "Permission": "WRITE" + }, + { + "Grantee": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc", + "Type": "CanonicalUser" + }, + "Permission": "WRITE_ACP" + }, + { + "Grantee": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc", + "Type": "CanonicalUser" + }, + "Permission": "READ" + }, + { + "Grantee": { + "DisplayName": "owner-display-name", + "ID": "852b113eexamplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc", + "Type": "CanonicalUser" + }, + "Permission": "READ_ACP" + } + ], + "Owner": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves access control list (ACL) of an object.", + "id": "to-retrieve-object-acl-1481833557740", + "title": "To retrieve object ACL" + } + ], + "GetObjectTagging": [ + { + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg" + }, + "output": { + "TagSet": [ + { + "Key": "Key4", + "Value": "Value4" + }, + { + "Key": "Key3", + "Value": "Value3" + } + ], + "VersionId": "null" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves tag set of an object.", + "id": "to-retrieve-tag-set-of-an-object-1481833847896", + "title": "To retrieve tag set of an object" + }, + { + "input": { + "Bucket": "examplebucket", + "Key": "exampleobject", + "VersionId": "ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI" + }, + "output": { + "TagSet": [ + { + "Key": "Key1", + "Value": "Value1" + } + ], + "VersionId": "ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves tag set of an object. The request specifies object version.", + "id": "to-retrieve-tag-set-of-a-specific-object-version-1483400283663", + "title": "To retrieve tag set of a specific object version" + } + ], + "GetObjectTorrent": [ + { + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves torrent files of an object.", + "id": "to-retrieve-torrent-files-for-an-object-1481834115959", + "title": "To retrieve torrent files for an object" + } + ], + "HeadBucket": [ + { + "input": { + "Bucket": "acl1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation checks to see if a bucket exists.", + "id": "to-determine-if-bucket-exists-1473110292262", + "title": "To determine if bucket exists" + } + ], + "HeadObject": [ + { + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg" + }, + "output": { + "AcceptRanges": "bytes", + "ContentLength": "3191", + "ContentType": "image/jpeg", + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "LastModified": "Thu, 15 Dec 2016 01:19:41 GMT", + "Metadata": { + }, + "VersionId": "null" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves an object metadata.", + "id": "to-retrieve-metadata-of-an-object-without-returning-the-object-itself-1481834820480", + "title": "To retrieve metadata of an object without returning the object itself" + } + ], + "ListMultipartUploads": [ + { + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Uploads": [ + { + "Initiated": "2014-05-01T05:40:58.000Z", + "Initiator": { + "DisplayName": "display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Key": "JavaFile", + "Owner": { + "DisplayName": "display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "StorageClass": "STANDARD", + "UploadId": "examplelUa.CInXklLQtSMJITdUnoZ1Y5GACB5UckOtspm5zbDMCkPF_qkfZzMiFZ6dksmcnqxJyIBvQMG9X9Q--" + }, + { + "Initiated": "2014-05-01T05:41:27.000Z", + "Initiator": { + "DisplayName": "display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Key": "JavaFile", + "Owner": { + "DisplayName": "display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "StorageClass": "STANDARD", + "UploadId": "examplelo91lv1iwvWpvCiJWugw2xXLPAD7Z8cJyX9.WiIRgNrdG6Ldsn.9FtS63TCl1Uf5faTB.1U5Ckcbmdw--" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example lists in-progress multipart uploads on a specific bucket.", + "id": "to-list-in-progress-multipart-uploads-on-a-bucket-1481852775260", + "title": "To list in-progress multipart uploads on a bucket" + }, + { + "input": { + "Bucket": "examplebucket", + "KeyMarker": "nextkeyfrompreviousresponse", + "MaxUploads": "2", + "UploadIdMarker": "valuefrompreviousresponse" + }, + "output": { + "Bucket": "acl1", + "IsTruncated": true, + "KeyMarker": "", + "MaxUploads": "2", + "NextKeyMarker": "someobjectkey", + "NextUploadIdMarker": "examplelo91lv1iwvWpvCiJWugw2xXLPAD7Z8cJyX9.WiIRgNrdG6Ldsn.9FtS63TCl1Uf5faTB.1U5Ckcbmdw--", + "UploadIdMarker": "", + "Uploads": [ + { + "Initiated": "2014-05-01T05:40:58.000Z", + "Initiator": { + "DisplayName": "ownder-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Key": "JavaFile", + "Owner": { + "DisplayName": "mohanataws", + "ID": "852b113e7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "StorageClass": "STANDARD", + "UploadId": "gZ30jIqlUa.CInXklLQtSMJITdUnoZ1Y5GACB5UckOtspm5zbDMCkPF_qkfZzMiFZ6dksmcnqxJyIBvQMG9X9Q--" + }, + { + "Initiated": "2014-05-01T05:41:27.000Z", + "Initiator": { + "DisplayName": "ownder-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Key": "JavaFile", + "Owner": { + "DisplayName": "ownder-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "StorageClass": "STANDARD", + "UploadId": "b7tZSqIlo91lv1iwvWpvCiJWugw2xXLPAD7Z8cJyX9.WiIRgNrdG6Ldsn.9FtS63TCl1Uf5faTB.1U5Ckcbmdw--" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example specifies the upload-id-marker and key-marker from previous truncated response to retrieve next setup of multipart uploads.", + "id": "list-next-set-of-multipart-uploads-when-previous-result-is-truncated-1482428106748", + "title": "List next set of multipart uploads when previous result is truncated" + } + ], + "ListObjectVersions": [ + { + "input": { + "Bucket": "examplebucket", + "Prefix": "HappyFace.jpg" + }, + "output": { + "Versions": [ + { + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "IsLatest": true, + "Key": "HappyFace.jpg", + "LastModified": "2016-12-15T01:19:41.000Z", + "Owner": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Size": 3191, + "StorageClass": "STANDARD", + "VersionId": "null" + }, + { + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "IsLatest": false, + "Key": "HappyFace.jpg", + "LastModified": "2016-12-13T00:58:26.000Z", + "Owner": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Size": 3191, + "StorageClass": "STANDARD", + "VersionId": "PHtexPGjH2y.zBgT8LmB7wwLI2mpbz.k" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example return versions of an object with specific key name prefix. The request limits the number of items returned to two. If there are are more than two object version, S3 returns NextToken in the response. You can specify this token value in your next request to fetch next set of object versions.", + "id": "to-list-object-versions-1481910996058", + "title": "To list object versions" + } + ], + "ListObjects": [ + { + "input": { + "Bucket": "examplebucket", + "MaxKeys": "2" + }, + "output": { + "Contents": [ + { + "ETag": "\"70ee1738b6b21e2c8a43f3a5ab0eee71\"", + "Key": "example1.jpg", + "LastModified": "2014-11-21T19:40:05.000Z", + "Owner": { + "DisplayName": "myname", + "ID": "12345example25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Size": 11, + "StorageClass": "STANDARD" + }, + { + "ETag": "\"9c8af9a76df052144598c115ef33e511\"", + "Key": "example2.jpg", + "LastModified": "2013-11-15T01:10:49.000Z", + "Owner": { + "DisplayName": "myname", + "ID": "12345example25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Size": 713193, + "StorageClass": "STANDARD" + } + ], + "NextMarker": "eyJNYXJrZXIiOiBudWxsLCAiYm90b190cnVuY2F0ZV9hbW91bnQiOiAyfQ==" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example list two objects in a bucket.", + "id": "to-list-objects-in-a-bucket-1473447646507", + "title": "To list objects in a bucket" + } + ], + "ListObjectsV2": [ + { + "input": { + "Bucket": "examplebucket", + "MaxKeys": "2" + }, + "output": { + "Contents": [ + { + "ETag": "\"70ee1738b6b21e2c8a43f3a5ab0eee71\"", + "Key": "happyface.jpg", + "LastModified": "2014-11-21T19:40:05.000Z", + "Size": 11, + "StorageClass": "STANDARD" + }, + { + "ETag": "\"becf17f89c30367a9a44495d62ed521a-1\"", + "Key": "test.jpg", + "LastModified": "2014-05-02T04:51:50.000Z", + "Size": 4192256, + "StorageClass": "STANDARD" + } + ], + "IsTruncated": true, + "KeyCount": "2", + "MaxKeys": "2", + "Name": "examplebucket", + "NextContinuationToken": "1w41l63U0xa8q7smH50vCxyTQqdxo69O3EmK28Bi5PcROI4wI/EyIJg==", + "Prefix": "" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves object list. The request specifies max keys to limit response to include only 2 object keys. ", + "id": "to-get-object-list", + "title": "To get object list" + } + ], + "ListParts": [ + { + "input": { + "Bucket": "examplebucket", + "Key": "bigobject", + "UploadId": "example7YPBOJuoFiQ9cz4P3Pe6FIZwO4f7wN93uHsNBEw97pl5eNwzExg0LAT2dUN91cOmrEQHDsP3WA60CEg--" + }, + "output": { + "Initiator": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Owner": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Parts": [ + { + "ETag": "\"d8c2eafd90c266e19ab9dcacc479f8af\"", + "LastModified": "2016-12-16T00:11:42.000Z", + "PartNumber": "1", + "Size": 26246026 + }, + { + "ETag": "\"d8c2eafd90c266e19ab9dcacc479f8af\"", + "LastModified": "2016-12-16T00:15:01.000Z", + "PartNumber": "2", + "Size": 26246026 + } + ], + "StorageClass": "STANDARD" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example lists parts uploaded for a specific multipart upload.", + "id": "to-list-parts-of-a-multipart-upload-1481852006923", + "title": "To list parts of a multipart upload." + } + ], + "PutBucketAcl": [ + { + "input": { + "Bucket": "examplebucket", + "GrantFullControl": "id=examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484", + "GrantWrite": "uri=http://acs.amazonaws.com/groups/s3/LogDelivery" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example replaces existing ACL on a bucket. The ACL grants the bucket owner (specified using the owner ID) and write permission to the LogDelivery group. Because this is a replace operation, you must specify all the grants in your request. To incrementally add or remove ACL grants, you might use the console.", + "id": "put-bucket-acl-1482260397033", + "title": "Put bucket acl" + } + ], + "PutBucketCors": [ + { + "input": { + "Bucket": "", + "CORSConfiguration": { + "CORSRules": [ + { + "AllowedHeaders": [ + "*" + ], + "AllowedMethods": [ + "PUT", + "POST", + "DELETE" + ], + "AllowedOrigins": [ + "http://www.example.com" + ], + "ExposeHeaders": [ + "x-amz-server-side-encryption" + ], + "MaxAgeSeconds": 3000 + }, + { + "AllowedHeaders": [ + "Authorization" + ], + "AllowedMethods": [ + "GET" + ], + "AllowedOrigins": [ + "*" + ], + "MaxAgeSeconds": 3000 + } + ] + }, + "ContentMD5": "" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example enables PUT, POST, and DELETE requests from www.example.com, and enables GET requests from any domain.", + "id": "to-set-cors-configuration-on-a-bucket-1483037818805", + "title": "To set cors configuration on a bucket." + } + ], + "PutBucketLifecycleConfiguration": [ + { + "input": { + "Bucket": "examplebucket", + "LifecycleConfiguration": { + "Rules": [ + { + "Expiration": { + "Days": 3650 + }, + "Filter": { + "Prefix": "documents/" + }, + "ID": "TestOnly", + "Status": "Enabled", + "Transitions": [ + { + "Days": 365, + "StorageClass": "GLACIER" + } + ] + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example replaces existing lifecycle configuration, if any, on the specified bucket. ", + "id": "put-bucket-lifecycle-1482264533092", + "title": "Put bucket lifecycle" + } + ], + "PutBucketLogging": [ + { + "input": { + "Bucket": "sourcebucket", + "BucketLoggingStatus": { + "LoggingEnabled": { + "TargetBucket": "targetbucket", + "TargetGrants": [ + { + "Grantee": { + "Type": "Group", + "URI": "http://acs.amazonaws.com/groups/global/AllUsers" + }, + "Permission": "READ" + } + ], + "TargetPrefix": "MyBucketLogs/" + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example sets logging policy on a bucket. For the Log Delivery group to deliver logs to the destination bucket, it needs permission for the READ_ACP action which the policy grants.", + "id": "set-logging-configuration-for-a-bucket-1482269119909", + "title": "Set logging configuration for a bucket" + } + ], + "PutBucketNotificationConfiguration": [ + { + "input": { + "Bucket": "examplebucket", + "NotificationConfiguration": { + "TopicConfigurations": [ + { + "Events": [ + "s3:ObjectCreated:*" + ], + "TopicArn": "arn:aws:sns:us-west-2:123456789012:s3-notification-topic" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example sets notification configuration on a bucket to publish the object created events to an SNS topic.", + "id": "set-notification-configuration-for-a-bucket-1482270296426", + "title": "Set notification configuration for a bucket" + } + ], + "PutBucketPolicy": [ + { + "input": { + "Bucket": "examplebucket", + "Policy": "{\"Version\": \"2012-10-17\", \"Statement\": [{ \"Sid\": \"id-1\",\"Effect\": \"Allow\",\"Principal\": {\"AWS\": \"arn:aws:iam::123456789012:root\"}, \"Action\": [ \"s3:PutObject\",\"s3:PutObjectAcl\"], \"Resource\": [\"arn:aws:s3:::acl3/*\" ] } ]}" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example sets a permission policy on a bucket.", + "id": "set-bucket-policy-1482448903302", + "title": "Set bucket policy" + } + ], + "PutBucketReplication": [ + { + "input": { + "Bucket": "examplebucket", + "ReplicationConfiguration": { + "Role": "arn:aws:iam::123456789012:role/examplerole", + "Rules": [ + { + "Destination": { + "Bucket": "arn:aws:s3:::destinationbucket", + "StorageClass": "STANDARD" + }, + "Prefix": "", + "Status": "Enabled" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example sets replication configuration on a bucket.", + "id": "id-1", + "title": "Set replication configuration on a bucket" + } + ], + "PutBucketRequestPayment": [ + { + "input": { + "Bucket": "examplebucket", + "RequestPaymentConfiguration": { + "Payer": "Requester" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example sets request payment configuration on a bucket so that person requesting the download is charged.", + "id": "set-request-payment-configuration-on-a-bucket-1482343596680", + "title": "Set request payment configuration on a bucket." + } + ], + "PutBucketTagging": [ + { + "input": { + "Bucket": "examplebucket", + "Tagging": { + "TagSet": [ + { + "Key": "Key1", + "Value": "Value1" + }, + { + "Key": "Key2", + "Value": "Value2" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example sets tags on a bucket. Any existing tags are replaced.", + "id": "set-tags-on-a-bucket-1482346269066", + "title": "Set tags on a bucket" + } + ], + "PutBucketVersioning": [ + { + "input": { + "Bucket": "examplebucket", + "VersioningConfiguration": { + "MFADelete": "Disabled", + "Status": "Enabled" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example sets versioning configuration on bucket. The configuration enables versioning on the bucket.", + "id": "set-versioning-configuration-on-a-bucket-1482344186279", + "title": "Set versioning configuration on a bucket" + } + ], + "PutBucketWebsite": [ + { + "input": { + "Bucket": "examplebucket", + "ContentMD5": "", + "WebsiteConfiguration": { + "ErrorDocument": { + "Key": "error.html" + }, + "IndexDocument": { + "Suffix": "index.html" + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example adds website configuration to a bucket.", + "id": "set-website-configuration-on-a-bucket-1482346836261", + "title": "Set website configuration on a bucket" + } + ], + "PutObject": [ + { + "input": { + "Body": "filetoupload", + "Bucket": "examplebucket", + "Key": "objectkey" + }, + "output": { + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "VersionId": "Bvq0EDKxOcXLJXNo_Lkz37eM3R4pfzyQ" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates an object. If the bucket is versioning enabled, S3 returns version ID in response.", + "id": "to-create-an-object-1483147613675", + "title": "To create an object." + }, + { + "input": { + "Body": "HappyFace.jpg", + "Bucket": "examplebucket", + "Key": "HappyFace.jpg", + "ServerSideEncryption": "AES256", + "StorageClass": "STANDARD_IA" + }, + "output": { + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "ServerSideEncryption": "AES256", + "VersionId": "CG612hodqujkf8FaaNfp8U..FIhLROcp" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example uploads an object. The request specifies optional request headers to directs S3 to use specific storage class and use server-side encryption.", + "id": "to-upload-an-object-(specify-optional-headers)", + "title": "To upload an object (specify optional headers)" + }, + { + "input": { + "ACL": "authenticated-read", + "Body": "filetoupload", + "Bucket": "examplebucket", + "Key": "exampleobject" + }, + "output": { + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "VersionId": "Kirh.unyZwjQ69YxcQLA8z4F5j3kJJKr" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example uploads and object. The request specifies optional canned ACL (access control list) to all READ access to authenticated users. If the bucket is versioning enabled, S3 returns version ID in response.", + "id": "to-upload-an-object-and-specify-canned-acl-1483397779571", + "title": "To upload an object and specify canned ACL." + }, + { + "input": { + "Body": "HappyFace.jpg", + "Bucket": "examplebucket", + "Key": "HappyFace.jpg" + }, + "output": { + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "VersionId": "tpf3zF08nBplQK1XLOefGskR7mGDwcDk" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example uploads an object to a versioning-enabled bucket. The source file is specified using Windows file syntax. S3 returns VersionId of the newly created object.", + "id": "to-upload-an-object-1481760101010", + "title": "To upload an object" + }, + { + "input": { + "Body": "filetoupload", + "Bucket": "examplebucket", + "Key": "exampleobject", + "Metadata": { + "metadata1": "value1", + "metadata2": "value2" + } + }, + "output": { + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "VersionId": "pSKidl4pHBiNwukdbcPXAIs.sshFFOc0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates an object. The request also specifies optional metadata. If the bucket is versioning enabled, S3 returns version ID in response.", + "id": "to-upload-object-and-specify-user-defined-metadata-1483396974757", + "title": "To upload object and specify user-defined metadata" + }, + { + "input": { + "Body": "c:\\HappyFace.jpg", + "Bucket": "examplebucket", + "Key": "HappyFace.jpg", + "Tagging": "key1=value1&key2=value2" + }, + "output": { + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "VersionId": "psM2sYY4.o1501dSx8wMvnkOzSBB.V4a" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example uploads an object. The request specifies optional object tags. The bucket is versioned, therefore S3 returns version ID of the newly created object.", + "id": "to-upload-an-object-and-specify-optional-tags-1481762310955", + "title": "To upload an object and specify optional tags" + }, + { + "input": { + "Body": "filetoupload", + "Bucket": "examplebucket", + "Key": "exampleobject", + "ServerSideEncryption": "AES256", + "Tagging": "key1=value1&key2=value2" + }, + "output": { + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "ServerSideEncryption": "AES256", + "VersionId": "Ri.vC6qVlA4dEnjgRV4ZHsHoFIjqEMNt" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example uploads and object. The request specifies the optional server-side encryption option. The request also specifies optional object tags. If the bucket is versioning enabled, S3 returns version ID in response.", + "id": "to-upload-an-object-and-specify-server-side-encryption-and-object-tags-1483398331831", + "title": "To upload an object and specify server-side encryption and object tags" + } + ], + "PutObjectAcl": [ + { + "input": { + "AccessControlPolicy": { + }, + "Bucket": "examplebucket", + "GrantFullControl": "emailaddress=user1@example.com,emailaddress=user2@example.com", + "GrantRead": "uri=http://acs.amazonaws.com/groups/global/AllUsers", + "Key": "HappyFace.jpg" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example adds grants to an object ACL. The first permission grants user1 and user2 FULL_CONTROL and the AllUsers group READ permission.", + "id": "to-grant-permissions-using-object-acl-1481835549285", + "title": "To grant permissions using object ACL" + } + ], + "PutObjectTagging": [ + { + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg", + "Tagging": { + "TagSet": [ + { + "Key": "Key3", + "Value": "Value3" + }, + { + "Key": "Key4", + "Value": "Value4" + } + ] + } + }, + "output": { + "VersionId": "null" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example adds tags to an existing object.", + "id": "to-add-tags-to-an-existing-object-1481764668793", + "title": "To add tags to an existing object" + } + ], + "RestoreObject": [ + { + "input": { + "Bucket": "examplebucket", + "Key": "archivedobjectkey", + "RestoreRequest": { + "Days": 1, + "GlacierJobParameters": { + "Tier": "Expedited" + } + } + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example restores for one day an archived copy of an object back into Amazon S3 bucket.", + "id": "to-restore-an-archived-object-1483049329953", + "title": "To restore an archived object" + } + ], + "UploadPart": [ + { + "input": { + "Body": "fileToUpload", + "Bucket": "examplebucket", + "Key": "examplelargeobject", + "PartNumber": "1", + "UploadId": "xadcOB_7YPBOJuoFiQ9cz4P3Pe6FIZwO4f7wN93uHsNBEw97pl5eNwzExg0LAT2dUN91cOmrEQHDsP3WA60CEg--" + }, + "output": { + "ETag": "\"d8c2eafd90c266e19ab9dcacc479f8af\"" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example uploads part 1 of a multipart upload. The example specifies a file name for the part data. The Upload ID is same that is returned by the initiate multipart upload.", + "id": "to-upload-a-part-1481847914943", + "title": "To upload a part" + } + ], + "UploadPartCopy": [ + { + "input": { + "Bucket": "examplebucket", + "CopySource": "/bucketname/sourceobjectkey", + "Key": "examplelargeobject", + "PartNumber": "1", + "UploadId": "exampleuoh_10OhKhT7YukE9bjzTPRiuaCotmZM_pFngJFir9OZNrSr5cWa3cq3LZSUsfjI4FI7PkP91We7Nrw--" + }, + "output": { + "CopyPartResult": { + "ETag": "\"b0c6f0e7e054ab8fa2536a2677f8734d\"", + "LastModified": "2016-12-29T21:24:43.000Z" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example uploads a part of a multipart upload by copying data from an existing object as data source.", + "id": "to-upload-a-part-by-copying-data-from-an-existing-object-as-data-source-1483046746348", + "title": "To upload a part by copying data from an existing object as data source" + }, + { + "input": { + "Bucket": "examplebucket", + "CopySource": "/bucketname/sourceobjectkey", + "CopySourceRange": "bytes=1-100000", + "Key": "examplelargeobject", + "PartNumber": "2", + "UploadId": "exampleuoh_10OhKhT7YukE9bjzTPRiuaCotmZM_pFngJFir9OZNrSr5cWa3cq3LZSUsfjI4FI7PkP91We7Nrw--" + }, + "output": { + "CopyPartResult": { + "ETag": "\"65d16d19e65a7508a51f043180edcc36\"", + "LastModified": "2016-12-29T21:44:28.000Z" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example uploads a part of a multipart upload by copying a specified byte range from an existing object as data source.", + "id": "to-upload-a-part-by-copying-byte-range-from-an-existing-object-as-data-source-1483048068594", + "title": "To upload a part by copying byte range from an existing object as data source" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/paginators-1.json new file mode 100644 index 0000000..c265ae3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/paginators-1.json @@ -0,0 +1,82 @@ +{ + "pagination": { + "ListMultipartUploads": { + "limit_key": "MaxUploads", + "more_results": "IsTruncated", + "output_token": [ + "NextKeyMarker", + "NextUploadIdMarker" + ], + "input_token": [ + "KeyMarker", + "UploadIdMarker" + ], + "result_key": [ + "Uploads", + "CommonPrefixes" + ] + }, + "ListObjectVersions": { + "more_results": "IsTruncated", + "limit_key": "MaxKeys", + "output_token": [ + "NextKeyMarker", + "NextVersionIdMarker" + ], + "input_token": [ + "KeyMarker", + "VersionIdMarker" + ], + "result_key": [ + "Versions", + "DeleteMarkers", + "CommonPrefixes" + ] + }, + "ListObjects": { + "more_results": "IsTruncated", + "limit_key": "MaxKeys", + "output_token": "NextMarker || Contents[-1].Key", + "input_token": "Marker", + "result_key": [ + "Contents", + "CommonPrefixes" + ] + }, + "ListObjectsV2": { + "more_results": "IsTruncated", + "limit_key": "MaxKeys", + "output_token": "NextContinuationToken", + "input_token": "ContinuationToken", + "result_key": [ + "Contents", + "CommonPrefixes" + ] + }, + "ListParts": { + "more_results": "IsTruncated", + "limit_key": "MaxParts", + "output_token": "NextPartNumberMarker", + "input_token": "PartNumberMarker", + "result_key": "Parts" + }, + "ListDirectoryBuckets": { + "input_token": "ContinuationToken", + "limit_key": "MaxDirectoryBuckets", + "output_token": "ContinuationToken", + "result_key": "Buckets" + }, + "ListBuckets": { + "input_token": "ContinuationToken", + "limit_key": "MaxBuckets", + "output_token": "ContinuationToken", + "result_key": "Buckets" + }, + "ListObjectAnnotations": { + "input_token": "ContinuationToken", + "limit_key": "MaxAnnotationResults", + "output_token": "NextContinuationToken", + "result_key": "Annotations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/paginators-1.sdk-extras.json new file mode 100644 index 0000000..1024352 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/paginators-1.sdk-extras.json @@ -0,0 +1,56 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListBuckets": { + "non_aggregate_keys": [ + "Owner", + "Prefix" + ] + }, + "ListMultipartUploads": { + "non_aggregate_keys": [ + "RequestCharged", + "Prefix" + ] + }, + "ListObjectVersions": { + "non_aggregate_keys": [ + "RequestCharged", + "Prefix" + ] + }, + "ListObjects": { + "non_aggregate_keys": [ + "RequestCharged", + "Prefix" + ] + }, + "ListObjectsV2": { + "non_aggregate_keys": [ + "RequestCharged", + "Prefix" + ] + }, + "ListObjectAnnotations": { + "non_aggregate_keys": [ + "AnnotationCount", + "AnnotationPrefix", + "Bucket", + "Key", + "ObjectVersionId", + "RequestCharged" + ] + }, + "ListParts": { + "non_aggregate_keys": [ + "ChecksumAlgorithm", + "Initiator", + "Owner", + "StorageClass", + "ChecksumType" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/service-2.json.gz new file mode 100644 index 0000000..9a49d9b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/service-2.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/service-2.sdk-extras.json new file mode 100644 index 0000000..d04e9d0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/service-2.sdk-extras.json @@ -0,0 +1,8 @@ +{ + "version": 1.0, + "merge": { + "shapes": { + "Expires":{"type":"timestamp"} + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/waiters-2.json new file mode 100644 index 0000000..b508a8f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3/2006-03-01/waiters-2.json @@ -0,0 +1,73 @@ +{ + "version": 2, + "waiters": { + "BucketExists": { + "delay": 5, + "operation": "HeadBucket", + "maxAttempts": 20, + "acceptors": [ + { + "expected": 200, + "matcher": "status", + "state": "success" + }, + { + "expected": 301, + "matcher": "status", + "state": "success" + }, + { + "expected": 403, + "matcher": "status", + "state": "success" + }, + { + "expected": 404, + "matcher": "status", + "state": "retry" + } + ] + }, + "BucketNotExists": { + "delay": 5, + "operation": "HeadBucket", + "maxAttempts": 20, + "acceptors": [ + { + "expected": 404, + "matcher": "status", + "state": "success" + } + ] + }, + "ObjectExists": { + "delay": 5, + "operation": "HeadObject", + "maxAttempts": 20, + "acceptors": [ + { + "expected": 200, + "matcher": "status", + "state": "success" + }, + { + "expected": 404, + "matcher": "status", + "state": "retry" + } + ] + }, + "ObjectNotExists": { + "delay": 5, + "operation": "HeadObject", + "maxAttempts": 20, + "acceptors": [ + { + "expected": 404, + "matcher": "status", + "state": "success" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3control/2018-08-20/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/s3control/2018-08-20/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ca9a6ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/s3control/2018-08-20/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3control/2018-08-20/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/s3control/2018-08-20/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3control/2018-08-20/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3control/2018-08-20/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/s3control/2018-08-20/paginators-1.json new file mode 100644 index 0000000..d843965 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3control/2018-08-20/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListAccessPointsForObjectLambda": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ObjectLambdaAccessPointList" + }, + "ListCallerAccessGrants": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CallerAccessGrantsList" + }, + "ListAccessPointsForDirectoryBuckets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AccessPointList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3control/2018-08-20/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/s3control/2018-08-20/service-2.json.gz new file mode 100644 index 0000000..66df193 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/s3control/2018-08-20/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3files/2025-05-05/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/s3files/2025-05-05/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..209625c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/s3files/2025-05-05/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3files/2025-05-05/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/s3files/2025-05-05/paginators-1.json new file mode 100644 index 0000000..4213361 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3files/2025-05-05/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListAccessPoints": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "accessPoints" + }, + "ListFileSystems": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "fileSystems" + }, + "ListMountTargets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "mountTargets" + }, + "ListTagsForResource": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tags" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3files/2025-05-05/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/s3files/2025-05-05/service-2.json.gz new file mode 100644 index 0000000..3377d43 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/s3files/2025-05-05/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3files/2025-05-05/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/s3files/2025-05-05/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3files/2025-05-05/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3outposts/2017-07-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/s3outposts/2017-07-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f857250 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/s3outposts/2017-07-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3outposts/2017-07-25/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/s3outposts/2017-07-25/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3outposts/2017-07-25/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3outposts/2017-07-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/s3outposts/2017-07-25/paginators-1.json new file mode 100644 index 0000000..5a8fa86 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3outposts/2017-07-25/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListEndpoints": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Endpoints" + }, + "ListSharedEndpoints": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Endpoints" + }, + "ListOutpostsWithS3": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Outposts" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3outposts/2017-07-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/s3outposts/2017-07-25/service-2.json.gz new file mode 100644 index 0000000..a05d198 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/s3outposts/2017-07-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3tables/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/s3tables/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..48577b3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/s3tables/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3tables/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/s3tables/2018-05-10/paginators-1.json new file mode 100644 index 0000000..2215b7b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3tables/2018-05-10/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListNamespaces": { + "input_token": "continuationToken", + "output_token": "continuationToken", + "limit_key": "maxNamespaces", + "result_key": "namespaces" + }, + "ListTableBuckets": { + "input_token": "continuationToken", + "output_token": "continuationToken", + "limit_key": "maxBuckets", + "result_key": "tableBuckets" + }, + "ListTables": { + "input_token": "continuationToken", + "output_token": "continuationToken", + "limit_key": "maxTables", + "result_key": "tables" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3tables/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/s3tables/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..83a7237 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/s3tables/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3tables/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/s3tables/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3tables/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..35ef867 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/paginators-1.json new file mode 100644 index 0000000..1606532 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/paginators-1.json @@ -0,0 +1,27 @@ +{ + "pagination": { + "ListIndexes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "indexes" + }, + "ListVectorBuckets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "vectorBuckets" + }, + "ListVectors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "vectors" + }, + "QueryVectors": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "vectors" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/paginators-1.sdk-extras.json new file mode 100644 index 0000000..76dfd8c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "QueryVectors": { + "non_aggregate_keys": [ + "distanceMetric" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/service-2.json.gz new file mode 100644 index 0000000..bb696ef Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/s3vectors/2025-07-15/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-a2i-runtime/2019-11-07/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-a2i-runtime/2019-11-07/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4f5cb25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-a2i-runtime/2019-11-07/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-a2i-runtime/2019-11-07/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-a2i-runtime/2019-11-07/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-a2i-runtime/2019-11-07/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-a2i-runtime/2019-11-07/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-a2i-runtime/2019-11-07/paginators-1.json new file mode 100644 index 0000000..b19128c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-a2i-runtime/2019-11-07/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListHumanLoops": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "HumanLoopSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-a2i-runtime/2019-11-07/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-a2i-runtime/2019-11-07/service-2.json.gz new file mode 100644 index 0000000..25f5966 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-a2i-runtime/2019-11-07/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-edge/2020-09-23/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-edge/2020-09-23/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6dba34f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-edge/2020-09-23/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-edge/2020-09-23/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-edge/2020-09-23/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-edge/2020-09-23/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-edge/2020-09-23/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-edge/2020-09-23/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-edge/2020-09-23/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-edge/2020-09-23/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-edge/2020-09-23/service-2.json.gz new file mode 100644 index 0000000..d493e12 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-edge/2020-09-23/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-featurestore-runtime/2020-07-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-featurestore-runtime/2020-07-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..05c50a4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-featurestore-runtime/2020-07-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-featurestore-runtime/2020-07-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-featurestore-runtime/2020-07-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-featurestore-runtime/2020-07-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-featurestore-runtime/2020-07-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-featurestore-runtime/2020-07-01/paginators-1.json new file mode 100644 index 0000000..87221db --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-featurestore-runtime/2020-07-01/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListRecords": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RecordIdentifiers" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-featurestore-runtime/2020-07-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-featurestore-runtime/2020-07-01/service-2.json.gz new file mode 100644 index 0000000..ed65048 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-featurestore-runtime/2020-07-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-geospatial/2020-05-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-geospatial/2020-05-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a4e6d6e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-geospatial/2020-05-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-geospatial/2020-05-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-geospatial/2020-05-27/paginators-1.json new file mode 100644 index 0000000..8802d94 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-geospatial/2020-05-27/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListEarthObservationJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EarthObservationJobSummaries" + }, + "ListRasterDataCollections": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RasterDataCollectionSummaries" + }, + "ListVectorEnrichmentJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "VectorEnrichmentJobSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-geospatial/2020-05-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-geospatial/2020-05-27/service-2.json.gz new file mode 100644 index 0000000..8f2e882 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-geospatial/2020-05-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-metrics/2022-09-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-metrics/2022-09-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4d48985 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-metrics/2022-09-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-metrics/2022-09-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-metrics/2022-09-30/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-metrics/2022-09-30/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-metrics/2022-09-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-metrics/2022-09-30/service-2.json.gz new file mode 100644 index 0000000..f8318a4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-metrics/2022-09-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-runtime/2017-05-13/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-runtime/2017-05-13/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..79df9ff Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-runtime/2017-05-13/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-runtime/2017-05-13/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-runtime/2017-05-13/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-runtime/2017-05-13/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-runtime/2017-05-13/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-runtime/2017-05-13/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-runtime/2017-05-13/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker-runtime/2017-05-13/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-runtime/2017-05-13/service-2.json.gz new file mode 100644 index 0000000..6d14003 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker-runtime/2017-05-13/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ef0a9aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/paginators-1.json new file mode 100644 index 0000000..cee248f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/paginators-1.json @@ -0,0 +1,538 @@ +{ + "pagination": { + "ListTrainingJobs": { + "result_key": "TrainingJobSummaries", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListEndpoints": { + "result_key": "Endpoints", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListEndpointConfigs": { + "result_key": "EndpointConfigs", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListNotebookInstances": { + "result_key": "NotebookInstances", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListTags": { + "result_key": "Tags", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListModels": { + "result_key": "Models", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListAlgorithms": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AlgorithmSummaryList" + }, + "ListCodeRepositories": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CodeRepositorySummaryList" + }, + "ListCompilationJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CompilationJobSummaries" + }, + "ListHyperParameterTuningJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "HyperParameterTuningJobSummaries" + }, + "ListLabelingJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LabelingJobSummaryList" + }, + "ListLabelingJobsForWorkteam": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LabelingJobSummaryList" + }, + "ListModelPackages": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ModelPackageSummaryList" + }, + "ListNotebookInstanceLifecycleConfigs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "NotebookInstanceLifecycleConfigs" + }, + "ListSubscribedWorkteams": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SubscribedWorkteams" + }, + "ListTrainingJobsForHyperParameterTuningJob": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TrainingJobSummaries" + }, + "ListTransformJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TransformJobSummaries" + }, + "ListWorkteams": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Workteams" + }, + "Search": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Results" + }, + "ListApps": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Apps" + }, + "ListAutoMLJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AutoMLJobSummaries" + }, + "ListCandidatesForAutoMLJob": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Candidates" + }, + "ListDomains": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Domains" + }, + "ListExperiments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ExperimentSummaries" + }, + "ListFlowDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FlowDefinitionSummaries" + }, + "ListHumanTaskUis": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "HumanTaskUiSummaries" + }, + "ListMonitoringExecutions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "MonitoringExecutionSummaries" + }, + "ListMonitoringSchedules": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "MonitoringScheduleSummaries" + }, + "ListProcessingJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ProcessingJobSummaries" + }, + "ListTrialComponents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TrialComponentSummaries" + }, + "ListTrials": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TrialSummaries" + }, + "ListUserProfiles": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "UserProfiles" + }, + "ListWorkforces": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Workforces" + }, + "ListImageVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ImageVersions" + }, + "ListImages": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Images" + }, + "ListActions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ActionSummaries" + }, + "ListAppImageConfigs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AppImageConfigs" + }, + "ListArtifacts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ArtifactSummaries" + }, + "ListAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AssociationSummaries" + }, + "ListContexts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ContextSummaries" + }, + "ListFeatureGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FeatureGroupSummaries" + }, + "ListModelPackageGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ModelPackageGroupSummaryList" + }, + "ListPipelineExecutionSteps": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PipelineExecutionSteps" + }, + "ListPipelineExecutions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PipelineExecutionSummaries" + }, + "ListPipelineParametersForExecution": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PipelineParameters" + }, + "ListPipelines": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PipelineSummaries" + }, + "ListDataQualityJobDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "JobDefinitionSummaries" + }, + "ListDeviceFleets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DeviceFleetSummaries" + }, + "ListDevices": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DeviceSummaries" + }, + "ListEdgePackagingJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EdgePackagingJobSummaries" + }, + "ListModelBiasJobDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "JobDefinitionSummaries" + }, + "ListModelExplainabilityJobDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "JobDefinitionSummaries" + }, + "ListModelQualityJobDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "JobDefinitionSummaries" + }, + "ListStudioLifecycleConfigs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "StudioLifecycleConfigs" + }, + "ListInferenceRecommendationsJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InferenceRecommendationsJobs" + }, + "ListLineageGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "LineageGroupSummaries" + }, + "ListModelMetadata": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ModelMetadataSummaries" + }, + "ListEdgeDeploymentPlans": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "EdgeDeploymentPlanSummaries" + }, + "ListStageDevices": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DeviceDeploymentSummaries" + }, + "ListInferenceRecommendationsJobSteps": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Steps" + }, + "ListInferenceExperiments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InferenceExperiments" + }, + "ListModelCardExportJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ModelCardExportJobSummaries" + }, + "ListModelCardVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ModelCardVersionSummaryList" + }, + "ListModelCards": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ModelCardSummaries" + }, + "ListMonitoringAlertHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "MonitoringAlertHistory" + }, + "ListMonitoringAlerts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "MonitoringAlertSummaries" + }, + "ListSpaces": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Spaces" + }, + "ListAliases": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SageMakerImageVersionAliases" + }, + "ListResourceCatalogs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ResourceCatalogs" + }, + "ListClusterNodes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ClusterNodeSummaries" + }, + "ListClusters": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ClusterSummaries" + }, + "ListInferenceComponents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InferenceComponents" + }, + "ListMlflowTrackingServers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TrackingServerSummaries" + }, + "ListOptimizationJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "OptimizationJobSummaries" + }, + "ListClusterSchedulerConfigs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ClusterSchedulerConfigSummaries" + }, + "ListComputeQuotas": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ComputeQuotaSummaries" + }, + "ListPartnerApps": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Summaries" + }, + "ListTrainingPlans": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TrainingPlanSummaries" + }, + "CreateHubContentPresignedUrls": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AuthorizedUrlConfigs" + }, + "ListPipelineVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PipelineVersionSummaries" + }, + "ListClusterEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Events" + }, + "ListUltraServersByReservedCapacity": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "UltraServers" + }, + "ListMlflowApps": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Summaries" + }, + "DescribeTrainingPlanExtensionHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TrainingPlanExtensions" + }, + "ListAIBenchmarkJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AIBenchmarkJobs" + }, + "ListAIRecommendationJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AIRecommendationJobs" + }, + "ListAIWorkloadConfigs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AIWorkloadConfigs" + }, + "ListJobSchemaVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "JobConfigSchemas" + }, + "ListJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "JobSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/paginators-1.sdk-extras.json new file mode 100644 index 0000000..5ab3083 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "Search": { + "non_aggregate_keys": [ + "TotalHits" + ] + } + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/service-2.json.gz new file mode 100644 index 0000000..d1c339e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/waiters-2.json new file mode 100644 index 0000000..d01aa3f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemaker/2017-07-24/waiters-2.json @@ -0,0 +1,260 @@ +{ + "version" : 2, + "waiters" : { + "EndpointDeleted" : { + "delay" : 30, + "maxAttempts" : 60, + "operation" : "DescribeEndpoint", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ValidationException" + }, { + "matcher" : "path", + "argument" : "EndpointStatus", + "state" : "failure", + "expected" : "Failed" + } ] + }, + "EndpointInService" : { + "delay" : 30, + "maxAttempts" : 120, + "operation" : "DescribeEndpoint", + "acceptors" : [ { + "matcher" : "path", + "argument" : "EndpointStatus", + "state" : "success", + "expected" : "InService" + }, { + "matcher" : "path", + "argument" : "EndpointStatus", + "state" : "failure", + "expected" : "Failed" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "ValidationException" + } ] + }, + "ImageCreated" : { + "delay" : 60, + "maxAttempts" : 60, + "operation" : "DescribeImage", + "acceptors" : [ { + "matcher" : "path", + "argument" : "ImageStatus", + "state" : "success", + "expected" : "CREATED" + }, { + "matcher" : "path", + "argument" : "ImageStatus", + "state" : "failure", + "expected" : "CREATE_FAILED" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "ValidationException" + } ] + }, + "ImageDeleted" : { + "delay" : 60, + "maxAttempts" : 60, + "operation" : "DescribeImage", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "ImageStatus", + "state" : "failure", + "expected" : "DELETE_FAILED" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "ValidationException" + } ] + }, + "ImageUpdated" : { + "delay" : 60, + "maxAttempts" : 60, + "operation" : "DescribeImage", + "acceptors" : [ { + "matcher" : "path", + "argument" : "ImageStatus", + "state" : "success", + "expected" : "CREATED" + }, { + "matcher" : "path", + "argument" : "ImageStatus", + "state" : "failure", + "expected" : "UPDATE_FAILED" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "ValidationException" + } ] + }, + "ImageVersionCreated" : { + "delay" : 60, + "maxAttempts" : 60, + "operation" : "DescribeImageVersion", + "acceptors" : [ { + "matcher" : "path", + "argument" : "ImageVersionStatus", + "state" : "success", + "expected" : "CREATED" + }, { + "matcher" : "path", + "argument" : "ImageVersionStatus", + "state" : "failure", + "expected" : "CREATE_FAILED" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "ValidationException" + } ] + }, + "ImageVersionDeleted" : { + "delay" : 60, + "maxAttempts" : 60, + "operation" : "DescribeImageVersion", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "ImageVersionStatus", + "state" : "failure", + "expected" : "DELETE_FAILED" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "ValidationException" + } ] + }, + "NotebookInstanceDeleted" : { + "delay" : 30, + "maxAttempts" : 60, + "operation" : "DescribeNotebookInstance", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ValidationException" + }, { + "matcher" : "path", + "argument" : "NotebookInstanceStatus", + "state" : "failure", + "expected" : "Failed" + } ] + }, + "NotebookInstanceInService" : { + "delay" : 30, + "maxAttempts" : 60, + "operation" : "DescribeNotebookInstance", + "acceptors" : [ { + "matcher" : "path", + "argument" : "NotebookInstanceStatus", + "state" : "success", + "expected" : "InService" + }, { + "matcher" : "path", + "argument" : "NotebookInstanceStatus", + "state" : "failure", + "expected" : "Failed" + } ] + }, + "NotebookInstanceStopped" : { + "delay" : 30, + "maxAttempts" : 60, + "operation" : "DescribeNotebookInstance", + "acceptors" : [ { + "matcher" : "path", + "argument" : "NotebookInstanceStatus", + "state" : "success", + "expected" : "Stopped" + }, { + "matcher" : "path", + "argument" : "NotebookInstanceStatus", + "state" : "failure", + "expected" : "Failed" + } ] + }, + "ProcessingJobCompletedOrStopped" : { + "delay" : 60, + "maxAttempts" : 60, + "operation" : "DescribeProcessingJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "ProcessingJobStatus", + "state" : "success", + "expected" : "Completed" + }, { + "matcher" : "path", + "argument" : "ProcessingJobStatus", + "state" : "success", + "expected" : "Stopped" + }, { + "matcher" : "path", + "argument" : "ProcessingJobStatus", + "state" : "failure", + "expected" : "Failed" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "ValidationException" + } ] + }, + "TrainingJobCompletedOrStopped" : { + "delay" : 120, + "maxAttempts" : 180, + "operation" : "DescribeTrainingJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "TrainingJobStatus", + "state" : "success", + "expected" : "Completed" + }, { + "matcher" : "path", + "argument" : "TrainingJobStatus", + "state" : "success", + "expected" : "Stopped" + }, { + "matcher" : "path", + "argument" : "TrainingJobStatus", + "state" : "failure", + "expected" : "Failed" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "ValidationException" + } ] + }, + "TransformJobCompletedOrStopped" : { + "delay" : 60, + "maxAttempts" : 60, + "operation" : "DescribeTransformJob", + "acceptors" : [ { + "matcher" : "path", + "argument" : "TransformJobStatus", + "state" : "success", + "expected" : "Completed" + }, { + "matcher" : "path", + "argument" : "TransformJobStatus", + "state" : "success", + "expected" : "Stopped" + }, { + "matcher" : "path", + "argument" : "TransformJobStatus", + "state" : "failure", + "expected" : "Failed" + }, { + "matcher" : "error", + "state" : "failure", + "expected" : "ValidationException" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemakerjobruntime/2026-02-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemakerjobruntime/2026-02-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..32ef39f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemakerjobruntime/2026-02-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemakerjobruntime/2026-02-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sagemakerjobruntime/2026-02-01/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sagemakerjobruntime/2026-02-01/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sagemakerjobruntime/2026-02-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sagemakerjobruntime/2026-02-01/service-2.json.gz new file mode 100644 index 0000000..9e70891 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sagemakerjobruntime/2026-02-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/savingsplans/2019-06-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/savingsplans/2019-06-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..e202ee7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/savingsplans/2019-06-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/savingsplans/2019-06-28/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/savingsplans/2019-06-28/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/savingsplans/2019-06-28/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/savingsplans/2019-06-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/savingsplans/2019-06-28/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/savingsplans/2019-06-28/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/savingsplans/2019-06-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/savingsplans/2019-06-28/service-2.json.gz new file mode 100644 index 0000000..6b775be Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/savingsplans/2019-06-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/scheduler/2021-06-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/scheduler/2021-06-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..096a522 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/scheduler/2021-06-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/scheduler/2021-06-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/scheduler/2021-06-30/paginators-1.json new file mode 100644 index 0000000..93b98ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/scheduler/2021-06-30/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListScheduleGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ScheduleGroups" + }, + "ListSchedules": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Schedules" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/scheduler/2021-06-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/scheduler/2021-06-30/service-2.json.gz new file mode 100644 index 0000000..83aa2df Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/scheduler/2021-06-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/schemas/2019-12-02/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/schemas/2019-12-02/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d6a80da Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/schemas/2019-12-02/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/schemas/2019-12-02/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/schemas/2019-12-02/paginators-1.json new file mode 100644 index 0000000..ef2fe19 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/schemas/2019-12-02/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListDiscoverers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Discoverers" + }, + "ListRegistries": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Registries" + }, + "ListSchemaVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "SchemaVersions" + }, + "ListSchemas": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Schemas" + }, + "SearchSchemas": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Schemas" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/schemas/2019-12-02/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/schemas/2019-12-02/service-2.json.gz new file mode 100644 index 0000000..f7d1382 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/schemas/2019-12-02/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/schemas/2019-12-02/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/schemas/2019-12-02/waiters-2.json new file mode 100644 index 0000000..4f642f6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/schemas/2019-12-02/waiters-2.json @@ -0,0 +1,36 @@ +{ + "version": 2, + "waiters": { + "CodeBindingExists": { + "description": "Wait until code binding is generated", + "delay": 2, + "operation": "DescribeCodeBinding", + "maxAttempts": 30, + "acceptors": [ + { + "expected": "CREATE_COMPLETE", + "matcher": "path", + "state": "success", + "argument": "Status" + }, + { + "expected": "CREATE_IN_PROGRESS", + "matcher": "path", + "state": "retry", + "argument": "Status" + }, + { + "expected": "CREATE_FAILED", + "matcher": "path", + "state": "failure", + "argument": "Status" + }, + { + "matcher": "error", + "expected": "NotFoundException", + "state": "failure" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sdb/2009-04-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sdb/2009-04-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..02210d6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sdb/2009-04-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sdb/2009-04-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sdb/2009-04-15/paginators-1.json new file mode 100644 index 0000000..2362098 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sdb/2009-04-15/paginators-1.json @@ -0,0 +1,15 @@ +{ + "pagination": { + "ListDomains": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxNumberOfDomains", + "result_key": "DomainNames" + }, + "Select": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sdb/2009-04-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sdb/2009-04-15/service-2.json.gz new file mode 100644 index 0000000..64bbe58 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sdb/2009-04-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sdk-default-configuration.json b/venv/lib/python3.12/site-packages/botocore/data/sdk-default-configuration.json new file mode 100644 index 0000000..3db13b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sdk-default-configuration.json @@ -0,0 +1,55 @@ +{ + "version": 1, + "base": { + "retryMode": "standard", + "stsRegionalEndpoints": "regional", + "s3UsEast1RegionalEndpoints": "regional", + "connectTimeoutInMillis": 1100, + "tlsNegotiationTimeoutInMillis": 1100 + }, + "modes": { + "standard": { + "connectTimeoutInMillis": { + "override": 3100 + }, + "tlsNegotiationTimeoutInMillis": { + "override": 3100 + } + }, + "in-region": { + }, + "cross-region": { + "connectTimeoutInMillis": { + "override": 3100 + }, + "tlsNegotiationTimeoutInMillis": { + "override": 3100 + } + }, + "mobile": { + "connectTimeoutInMillis": { + "override": 30000 + }, + "tlsNegotiationTimeoutInMillis": { + "override": 30000 + } + } + }, + "documentation": { + "modes": { + "standard": "

The STANDARD mode provides the latest recommended default values that should be safe to run in most scenarios

Note that the default values vended from this mode might change as best practices may evolve. As a result, it is encouraged to perform tests when upgrading the SDK

", + "in-region": "

The IN_REGION mode builds on the standard mode and includes optimization tailored for applications which call AWS services from within the same AWS region

Note that the default values vended from this mode might change as best practices may evolve. As a result, it is encouraged to perform tests when upgrading the SDK

", + "cross-region": "

The CROSS_REGION mode builds on the standard mode and includes optimization tailored for applications which call AWS services in a different region

Note that the default values vended from this mode might change as best practices may evolve. As a result, it is encouraged to perform tests when upgrading the SDK

", + "mobile": "

The MOBILE mode builds on the standard mode and includes optimization tailored for mobile applications

Note that the default values vended from this mode might change as best practices may evolve. As a result, it is encouraged to perform tests when upgrading the SDK

", + "auto": "

The AUTO mode is an experimental mode that builds on the standard mode. The SDK will attempt to discover the execution environment to determine the appropriate settings automatically.

Note that the auto detection is heuristics-based and does not guarantee 100% accuracy. STANDARD mode will be used if the execution environment cannot be determined. The auto detection might query EC2 Instance Metadata service, which might introduce latency. Therefore we recommend choosing an explicit defaults_mode instead if startup latency is critical to your application

", + "legacy": "

The LEGACY mode provides default settings that vary per SDK and were used prior to establishment of defaults_mode

" + }, + "configuration": { + "retryMode": "

A retry mode specifies how the SDK attempts retries. See Retry Mode

", + "stsRegionalEndpoints": "

Specifies how the SDK determines the AWS service endpoint that it uses to talk to the AWS Security Token Service (AWS STS). See Setting STS Regional endpoints

", + "s3UsEast1RegionalEndpoints": "

Specifies how the SDK determines the AWS service endpoint that it uses to talk to the Amazon S3 for the us-east-1 region

", + "connectTimeoutInMillis": "

The amount of time after making an initial connection attempt on a socket, where if the client does not receive a completion of the connect handshake, the client gives up and fails the operation

", + "tlsNegotiationTimeoutInMillis": "

The maximum amount of time that a TLS handshake is allowed to take from the time the CLIENT HELLO message is sent to ethe time the client and server have fully negotiated ciphers and exchanged keys

" + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..888aeb7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/examples-1.json new file mode 100644 index 0000000..43a3ec4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/examples-1.json @@ -0,0 +1,596 @@ +{ + "version": "1.0", + "examples": { + "CancelRotateSecret": [ + { + "input": { + "SecretId": "MyTestDatabaseSecret" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "Name" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to cancel rotation for a secret. The operation sets the RotationEnabled field to false and cancels all scheduled rotations. To resume scheduled rotations, you must re-enable rotation by calling the rotate-secret operation.", + "id": "to-cancel-scheduled-rotation-for-a-secret-1523996016032", + "title": "To cancel scheduled rotation for a secret" + } + ], + "CreateSecret": [ + { + "input": { + "ClientRequestToken": "EXAMPLE1-90ab-cdef-fedc-ba987SECRET1", + "Description": "My test database secret created with the CLI", + "Name": "MyTestDatabaseSecret", + "SecretString": "{\"username\":\"david\",\"password\":\"EXAMPLE-PASSWORD\"}" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret", + "VersionId": "EXAMPLE1-90ab-cdef-fedc-ba987SECRET1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to create a secret. The credentials stored in the encrypted secret value are retrieved from a file on disk named mycreds.json.", + "id": "to-create-a-basic-secret-1523996473658", + "title": "To create a basic secret" + } + ], + "DeleteResourcePolicy": [ + { + "input": { + "SecretId": "MyTestDatabaseSecret" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseMasterSecret-a1b2c3", + "Name": "MyTestDatabaseSecret" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to delete the resource-based policy that is attached to a secret.", + "id": "to-delete-the-resource-based-policy-attached-to-a-secret-1530209419204", + "title": "To delete the resource-based policy attached to a secret" + } + ], + "DeleteSecret": [ + { + "input": { + "RecoveryWindowInDays": 7, + "SecretId": "MyTestDatabaseSecret1" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "DeletionDate": "1524085349.095", + "Name": "MyTestDatabaseSecret" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to delete a secret. The secret stays in your account in a deprecated and inaccessible state until the recovery window ends. After the date and time in the DeletionDate response field has passed, you can no longer recover this secret with restore-secret.", + "id": "to-delete-a-secret-1523996905092", + "title": "To delete a secret" + } + ], + "DescribeSecret": [ + { + "input": { + "SecretId": "MyTestDatabaseSecret" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Description": "My test database secret", + "KmsKeyId": "arn:aws:kms:us-west-2:123456789012:key/EXAMPLE1-90ab-cdef-fedc-ba987KMSKEY1", + "LastAccessedDate": "1523923200", + "LastChangedDate": 1523477145.729, + "LastRotatedDate": 1525747253.72, + "Name": "MyTestDatabaseSecret", + "RotationEnabled": true, + "RotationLambdaARN": "arn:aws:lambda:us-west-2:123456789012:function:MyTestRotationLambda", + "RotationRules": { + "AutomaticallyAfterDays": 14, + "Duration": "2h", + "ScheduleExpression": "cron(0 16 1,15 * ? *)" + }, + "Tags": [ + { + "Key": "SecondTag", + "Value": "AnotherValue" + }, + { + "Key": "FirstTag", + "Value": "SomeValue" + } + ], + "VersionIdsToStages": { + "EXAMPLE1-90ab-cdef-fedc-ba987EXAMPLE": [ + "AWSPREVIOUS" + ], + "EXAMPLE2-90ab-cdef-fedc-ba987EXAMPLE": [ + "AWSCURRENT" + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to get the details about a secret.", + "id": "to-retrieve-the-details-of-a-secret-1524000138629", + "title": "To retrieve the details of a secret" + } + ], + "GetRandomPassword": [ + { + "input": { + "IncludeSpace": true, + "PasswordLength": 20, + "RequireEachIncludedType": true + }, + "output": { + "RandomPassword": "EXAMPLE-PASSWORD" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to request a randomly generated password. This example includes the optional flags to require spaces and at least one character of each included type. It specifies a length of 20 characters.", + "id": "to-generate-a-random-password-1524000546092", + "title": "To generate a random password" + } + ], + "GetResourcePolicy": [ + { + "input": { + "SecretId": "MyTestDatabaseSecret" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret", + "ResourcePolicy": "{\n\"Version\":\"2012-10-17\",\n\"Statement\":[{\n\"Effect\":\"Allow\",\n\"Principal\":{\n\"AWS\":\"arn:aws:iam::123456789012:root\"\n},\n\"Action\":\"secretsmanager:GetSecretValue\",\n\"Resource\":\"*\"\n}]\n}" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to retrieve the resource-based policy that is attached to a secret.", + "id": "to-retrieve-the-resource-based-policy-attached-to-a-secret-1530209677536", + "title": "To retrieve the resource-based policy attached to a secret" + } + ], + "GetSecretValue": [ + { + "input": { + "SecretId": "MyTestDatabaseSecret" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "CreatedDate": 1523477145.713, + "Name": "MyTestDatabaseSecret", + "SecretString": "{\n \"username\":\"david\",\n \"password\":\"EXAMPLE-PASSWORD\"\n}\n", + "VersionId": "EXAMPLE1-90ab-cdef-fedc-ba987SECRET1", + "VersionStages": [ + "AWSPREVIOUS" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to retrieve a secret string value.", + "id": "to-retrieve-the-encrypted-secret-value-of-a-secret-1524000702484", + "title": "To retrieve the encrypted secret value of a secret" + } + ], + "ListSecretVersionIds": [ + { + "input": { + "IncludeDeprecated": true, + "SecretId": "MyTestDatabaseSecret" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret", + "Versions": [ + { + "CreatedDate": 1523477145.713, + "VersionId": "EXAMPLE1-90ab-cdef-fedc-ba987EXAMPLE", + "VersionStages": [ + "AWSPREVIOUS" + ] + }, + { + "CreatedDate": 1523486221.391, + "VersionId": "EXAMPLE2-90ab-cdef-fedc-ba987EXAMPLE", + "VersionStages": [ + "AWSCURRENT" + ] + }, + { + "CreatedDate": 1511974462.36, + "VersionId": "EXAMPLE3-90ab-cdef-fedc-ba987EXAMPLE;" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to retrieve a list of all of the versions of a secret, including those without any staging labels.", + "id": "to-list-all-of-the-secret-versions-associated-with-a-secret-1524000999164", + "title": "To list all of the secret versions associated with a secret" + } + ], + "ListSecrets": [ + { + "input": { + }, + "output": { + "SecretList": [ + { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Description": "My test database secret", + "LastChangedDate": 1523477145.729, + "Name": "MyTestDatabaseSecret", + "SecretVersionsToStages": { + "EXAMPLE1-90ab-cdef-fedc-ba987EXAMPLE": [ + "AWSCURRENT" + ] + } + }, + { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret1-d4e5f6", + "Description": "Another secret created for a different database", + "LastChangedDate": 1523482025.685, + "Name": "MyTestDatabaseSecret1", + "SecretVersionsToStages": { + "EXAMPLE2-90ab-cdef-fedc-ba987EXAMPLE": [ + "AWSCURRENT" + ] + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to list all of the secrets in your account.", + "id": "to-list-the-secrets-in-your-account-1524001246087", + "title": "To list the secrets in your account" + } + ], + "PutResourcePolicy": [ + { + "input": { + "ResourcePolicy": "{\n\"Version\":\"2012-10-17\",\n\"Statement\":[{\n\"Effect\":\"Allow\",\n\"Principal\":{\n\"AWS\":\"arn:aws:iam::123456789012:root\"\n},\n\"Action\":\"secretsmanager:GetSecretValue\",\n\"Resource\":\"*\"\n}]\n}", + "SecretId": "MyTestDatabaseSecret" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to add a resource-based policy to a secret.", + "id": "to-add-a-resource-based-policy-to-a-secret-1530209881839", + "title": "To add a resource-based policy to a secret" + } + ], + "PutSecretValue": [ + { + "input": { + "ClientRequestToken": "EXAMPLE2-90ab-cdef-fedc-ba987EXAMPLE", + "SecretId": "MyTestDatabaseSecret", + "SecretString": "{\"username\":\"david\",\"password\":\"EXAMPLE-PASSWORD\"}" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret", + "VersionId": "EXAMPLE2-90ab-cdef-fedc-ba987EXAMPLE", + "VersionStages": [ + "AWSCURRENT" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to create a new version of the secret. Alternatively, you can use the update-secret command.", + "id": "to-store-a-secret-value-in-a-new-version-of-a-secret-1524001393971", + "title": "To store a secret value in a new version of a secret" + } + ], + "RestoreSecret": [ + { + "input": { + "SecretId": "MyTestDatabaseSecret" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to restore a secret that you previously scheduled for deletion.", + "id": "to-restore-a-previously-deleted-secret-1524001513930", + "title": "To restore a previously deleted secret" + } + ], + "RotateSecret": [ + { + "input": { + "RotationLambdaARN": "arn:aws:lambda:us-west-2:123456789012:function:MyTestDatabaseRotationLambda", + "RotationRules": { + "Duration": "2h", + "ScheduleExpression": "cron(0 16 1,15 * ? *)" + }, + "SecretId": "MyTestDatabaseSecret" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret", + "VersionId": "EXAMPLE2-90ab-cdef-fedc-ba987SECRET2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example configures rotation for a secret using a cron expression. The first rotation happens immediately after the changes are stored in the secret. The rotation schedule is the first and 15th day of every month. The rotation window begins at 4:00 PM UTC and ends at 6:00 PM.", + "id": "to-configure-rotation-for-a-secret-1524001629475", + "title": "To configure rotation for a secret" + }, + { + "input": { + "SecretId": "MyTestDatabaseSecret" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret", + "VersionId": "EXAMPLE2-90ab-cdef-fedc-ba987SECRET2" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example requests an immediate invocation of the secret's Lambda rotation function. It assumes that the specified secret already has rotation configured. The rotation function runs asynchronously in the background.", + "id": "to-request-an-immediate-rotation-for-a-secret-1524001949004", + "title": "To request an immediate rotation for a secret" + } + ], + "TagResource": [ + { + "input": { + "SecretId": "MyExampleSecret", + "Tags": [ + { + "Key": "FirstTag", + "Value": "SomeValue" + }, + { + "Key": "SecondTag", + "Value": "AnotherValue" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to attach two tags each with a Key and Value to a secret. There is no output from this API. To see the result, use the DescribeSecret operation.", + "id": "to-add-tags-to-a-secret-1524002106718", + "title": "To add tags to a secret" + } + ], + "UntagResource": [ + { + "input": { + "SecretId": "MyTestDatabaseSecret", + "TagKeys": [ + "FirstTag", + "SecondTag" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to remove two tags from a secret's metadata. For each, both the tag and the associated value are removed. There is no output from this API. To see the result, use the DescribeSecret operation.", + "id": "to-remove-tags-from-a-secret-1524002239065", + "title": "To remove tags from a secret" + } + ], + "UpdateSecret": [ + { + "input": { + "ClientRequestToken": "EXAMPLE1-90ab-cdef-fedc-ba987EXAMPLE", + "Description": "This is a new description for the secret.", + "SecretId": "MyTestDatabaseSecret" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to modify the description of a secret.", + "id": "to-update-the-description-of-a-secret-1524002349094", + "title": "To update the description of a secret" + }, + { + "input": { + "KmsKeyId": "arn:aws:kms:us-west-2:123456789012:key/EXAMPLE2-90ab-cdef-fedc-ba987EXAMPLE", + "SecretId": "MyTestDatabaseSecret" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows how to update the KMS customer managed key (CMK) used to encrypt the secret value. The KMS CMK must be in the same region as the secret.", + "id": "to-update-the-kms-key-associated-with-a-secret-1524002421563", + "title": "To update the KMS key associated with a secret" + }, + { + "input": { + "SecretId": "MyTestDatabaseSecret", + "SecretString": "{JSON STRING WITH CREDENTIALS}" + }, + "output": { + "ARN": "aws:arn:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret", + "VersionId": "EXAMPLE1-90ab-cdef-fedc-ba987EXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to create a new version of the secret by updating the SecretString field. Alternatively, you can use the put-secret-value operation.", + "id": "to-create-a-new-version-of-the-encrypted-secret-value-1524004651836", + "title": "To create a new version of the encrypted secret value" + } + ], + "UpdateSecretVersionStage": [ + { + "input": { + "MoveToVersionId": "EXAMPLE1-90ab-cdef-fedc-ba987SECRET1", + "SecretId": "MyTestDatabaseSecret", + "VersionStage": "STAGINGLABEL1" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows you how to add a staging label to a version of a secret. You can review the results by running the operation ListSecretVersionIds and viewing the VersionStages response field for the affected version.", + "id": "to-add-a-staging-label-attached-to-a-version-of-a-secret-1524004783841", + "title": "To add a staging label attached to a version of a secret" + }, + { + "input": { + "RemoveFromVersionId": "EXAMPLE1-90ab-cdef-fedc-ba987SECRET1", + "SecretId": "MyTestDatabaseSecret", + "VersionStage": "STAGINGLABEL1" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows you how to delete a staging label that is attached to a version of a secret. You can review the results by running the operation ListSecretVersionIds and viewing the VersionStages response field for the affected version.", + "id": "to-delete-a-staging-label-attached-to-a-version-of-a-secret-1524004862181", + "title": "To delete a staging label attached to a version of a secret" + }, + { + "input": { + "MoveToVersionId": "EXAMPLE2-90ab-cdef-fedc-ba987SECRET2", + "RemoveFromVersionId": "EXAMPLE1-90ab-cdef-fedc-ba987SECRET1", + "SecretId": "MyTestDatabaseSecret", + "VersionStage": "AWSCURRENT" + }, + "output": { + "ARN": "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3", + "Name": "MyTestDatabaseSecret" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows you how to move a staging label that is attached to one version of a secret to a different version. You can review the results by running the operation ListSecretVersionIds and viewing the VersionStages response field for the affected version.", + "id": "to-move-a-staging-label-from-one-version-of-a-secret-to-another-1524004963841", + "title": "To move a staging label from one version of a secret to another" + } + ], + "ValidateResourcePolicy": [ + { + "input": { + "ResourcePolicy": "{\n\"Version\":\"2012-10-17\",\n\"Statement\":[{\n\"Effect\":\"Allow\",\n\"Principal\":{\n\"AWS\":\"arn:aws:iam::123456789012:root\"\n},\n\"Action\":\"secretsmanager:GetSecretValue\",\n\"Resource\":\"*\"\n}]\n}", + "SecretId": "MyTestDatabaseSecret" + }, + "output": { + "PolicyValidationPassed": true, + "ValidationErrors": [ + + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows how to validate a resource-based policy to a secret.", + "id": "to-validate-the-resource-policy-of-a-secret-1524000138629", + "title": "To validate a resource-based policy to a secret" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/paginators-1.json new file mode 100644 index 0000000..0f62e8e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListSecrets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SecretList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/service-2.json.gz new file mode 100644 index 0000000..b8e5924 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/service-2.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/service-2.sdk-extras.json new file mode 100644 index 0000000..dc78f89 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/secretsmanager/2017-10-17/service-2.sdk-extras.json @@ -0,0 +1,8 @@ +{ + "version": 1.0, + "merge": { + "metadata": { + "serviceId": "Secrets Manager" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8401b84 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/paginators-1.json new file mode 100644 index 0000000..9f78bef --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListCaseEdits": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListCases": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListComments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListMemberships": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListInvestigations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "investigationActions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/paginators-1.sdk-extras.json new file mode 100644 index 0000000..74820d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/paginators-1.sdk-extras.json @@ -0,0 +1,22 @@ +{ + "version": 1, + "merge": { + "pagination": { + "ListCaseEdits": { + "non_aggregate_keys": [ + "total" + ] + }, + "ListCases": { + "non_aggregate_keys": [ + "total" + ] + }, + "ListComments": { + "non_aggregate_keys": [ + "total" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..14efd91 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/security-ir/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/securityagent/2025-09-06/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/securityagent/2025-09-06/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..90f0203 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/securityagent/2025-09-06/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/securityagent/2025-09-06/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/securityagent/2025-09-06/paginators-1.json new file mode 100644 index 0000000..b857621 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/securityagent/2025-09-06/paginators-1.json @@ -0,0 +1,142 @@ +{ + "pagination": { + "ListAgentSpaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agentSpaceSummaries" + }, + "ListApplications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "applicationSummaries" + }, + "ListArtifacts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "artifactSummaries" + }, + "ListDiscoveredEndpoints": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "discoveredEndpoints" + }, + "ListFindings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findingsSummaries" + }, + "ListIntegratedResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "integratedResourceSummaries" + }, + "ListIntegrations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "integrationSummaries" + }, + "ListMemberships": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "membershipSummaries" + }, + "ListPentestJobTasks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "taskSummaries" + }, + "ListPentestJobsForPentest": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "pentestJobSummaries" + }, + "ListPentests": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "pentestSummaries" + }, + "ListTargetDomains": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "targetDomainSummaries" + }, + "ListCodeReviewJobTasks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "codeReviewJobTaskSummaries" + }, + "ListCodeReviewJobsForCodeReview": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "codeReviewJobSummaries" + }, + "ListCodeReviews": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "codeReviewSummaries" + }, + "ListPrivateConnections": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "privateConnections" + }, + "ListSecurityRequirementPacks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "securityRequirementPackSummaries" + }, + "ListSecurityRequirements": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "securityRequirementSummaries" + }, + "ListThreatModelJobTasks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "threatModelJobTaskSummaries" + }, + "ListThreatModelJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "threatModelJobSummaries" + }, + "ListThreatModels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "threatModelSummaries" + }, + "ListThreats": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "threats" + }, + "ListActorMessages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "messages" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/securityagent/2025-09-06/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/securityagent/2025-09-06/service-2.json.gz new file mode 100644 index 0000000..6e9f887 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/securityagent/2025-09-06/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/securityagent/2025-09-06/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/securityagent/2025-09-06/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/securityagent/2025-09-06/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..ec152cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/paginators-1.json new file mode 100644 index 0000000..ce9e027 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/paginators-1.json @@ -0,0 +1,154 @@ +{ + "pagination": { + "GetEnabledStandards": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "StandardsSubscriptions" + }, + "GetFindings": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Findings" + }, + "GetInsights": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Insights" + }, + "ListEnabledProductsForImport": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ProductSubscriptions" + }, + "ListInvitations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Invitations" + }, + "ListMembers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Members" + }, + "DescribeActionTargets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ActionTargets" + }, + "DescribeProducts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Products" + }, + "DescribeStandards": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Standards" + }, + "DescribeStandardsControls": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Controls" + }, + "ListOrganizationAdminAccounts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AdminAccounts" + }, + "ListFindingAggregators": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FindingAggregators" + }, + "ListSecurityControlDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SecurityControlDefinitions" + }, + "ListStandardsControlAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "StandardsControlAssociationSummaries" + }, + "GetFindingHistory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Records" + }, + "ListConfigurationPolicies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ConfigurationPolicySummaries" + }, + "ListConfigurationPolicyAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ConfigurationPolicyAssociationSummaries" + }, + "DescribeProductsV2": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ProductsV2" + }, + "GetFindingsV2": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Findings" + }, + "GetResourcesV2": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Resources" + }, + "ListAggregatorsV2": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AggregatorsV2" + }, + "GetFindingsTrendsV2": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TrendsMetrics" + }, + "GetResourcesTrendsV2": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TrendsMetrics" + }, + "GetRecommendedPolicyV2": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RecommendationSteps" + }, + "ListFreeTrialStatusesV2": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AccountFreeTrialStatuses" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/paginators-1.sdk-extras.json new file mode 100644 index 0000000..05d38bb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/paginators-1.sdk-extras.json @@ -0,0 +1,30 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListOrganizationAdminAccounts": { + "non_aggregate_keys": [ + "Feature" + ] + }, + "GetFindingsTrendsV2": { + "non_aggregate_keys": [ + "Granularity" + ] + }, + "GetResourcesTrendsV2": { + "non_aggregate_keys": [ + "Granularity" + ] + }, + "GetRecommendedPolicyV2": { + "non_aggregate_keys": [ + "Status", + "RecommendationType", + "Error", + "ResourceArn" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/service-2.json.gz new file mode 100644 index 0000000..9948232 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/securityhub/2018-10-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/securitylake/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/securitylake/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..aba1185 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/securitylake/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/securitylake/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/securitylake/2018-05-10/paginators-1.json new file mode 100644 index 0000000..19e482b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/securitylake/2018-05-10/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "GetDataLakeSources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dataLakeSources" + }, + "ListDataLakeExceptions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "exceptions" + }, + "ListLogSources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sources" + }, + "ListSubscribers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "subscribers" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/securitylake/2018-05-10/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/securitylake/2018-05-10/paginators-1.sdk-extras.json new file mode 100644 index 0000000..41ae7fe --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/securitylake/2018-05-10/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "GetDataLakeSources": { + "non_aggregate_keys": [ + "dataLakeArn" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/securitylake/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/securitylake/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..b7c2d07 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/securitylake/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/serverlessrepo/2017-09-08/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/serverlessrepo/2017-09-08/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..53821e3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/serverlessrepo/2017-09-08/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/serverlessrepo/2017-09-08/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/serverlessrepo/2017-09-08/paginators-1.json new file mode 100644 index 0000000..a39e547 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/serverlessrepo/2017-09-08/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListApplicationDependencies": { + "input_token": "NextToken", + "limit_key": "MaxItems", + "output_token": "NextToken", + "result_key": "Dependencies" + }, + "ListApplicationVersions": { + "input_token": "NextToken", + "limit_key": "MaxItems", + "output_token": "NextToken", + "result_key": "Versions" + }, + "ListApplications": { + "input_token": "NextToken", + "limit_key": "MaxItems", + "output_token": "NextToken", + "result_key": "Applications" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/serverlessrepo/2017-09-08/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/serverlessrepo/2017-09-08/service-2.json.gz new file mode 100644 index 0000000..0ba7056 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/serverlessrepo/2017-09-08/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/service-quotas/2019-06-24/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/service-quotas/2019-06-24/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..2922d53 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/service-quotas/2019-06-24/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/service-quotas/2019-06-24/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/service-quotas/2019-06-24/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/service-quotas/2019-06-24/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/service-quotas/2019-06-24/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/service-quotas/2019-06-24/paginators-1.json new file mode 100644 index 0000000..e0d4547 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/service-quotas/2019-06-24/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListAWSDefaultServiceQuotas": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Quotas" + }, + "ListRequestedServiceQuotaChangeHistory": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "RequestedQuotas" + }, + "ListRequestedServiceQuotaChangeHistoryByQuota": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "RequestedQuotas" + }, + "ListServiceQuotaIncreaseRequestsInTemplate": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ServiceQuotaIncreaseRequestInTemplateList" + }, + "ListServiceQuotas": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Quotas" + }, + "ListServices": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Services" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/service-quotas/2019-06-24/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/service-quotas/2019-06-24/service-2.json.gz new file mode 100644 index 0000000..8d42caa Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/service-quotas/2019-06-24/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/servicecatalog-appregistry/2020-06-24/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog-appregistry/2020-06-24/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3ff423b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog-appregistry/2020-06-24/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/servicecatalog-appregistry/2020-06-24/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog-appregistry/2020-06-24/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog-appregistry/2020-06-24/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/servicecatalog-appregistry/2020-06-24/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog-appregistry/2020-06-24/paginators-1.json new file mode 100644 index 0000000..55281fb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog-appregistry/2020-06-24/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListApplications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "applications" + }, + "ListAssociatedAttributeGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "attributeGroups" + }, + "ListAssociatedResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "resources" + }, + "ListAttributeGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "attributeGroups" + }, + "ListAttributeGroupsForApplication": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "attributeGroupsDetails" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/servicecatalog-appregistry/2020-06-24/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog-appregistry/2020-06-24/service-2.json.gz new file mode 100644 index 0000000..8877789 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog-appregistry/2020-06-24/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/servicecatalog/2015-12-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog/2015-12-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..17ebcc4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog/2015-12-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/servicecatalog/2015-12-10/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog/2015-12-10/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog/2015-12-10/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/servicecatalog/2015-12-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog/2015-12-10/paginators-1.json new file mode 100644 index 0000000..5770fef --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog/2015-12-10/paginators-1.json @@ -0,0 +1,100 @@ +{ + "pagination": { + "SearchProductsAsAdmin": { + "result_key": "ProductViewDetails", + "output_token": "NextPageToken", + "input_token": "PageToken", + "limit_key": "PageSize" + }, + "ListAcceptedPortfolioShares": { + "result_key": "PortfolioDetails", + "output_token": "NextPageToken", + "input_token": "PageToken", + "limit_key": "PageSize" + }, + "ListPortfolios": { + "result_key": "PortfolioDetails", + "output_token": "NextPageToken", + "input_token": "PageToken", + "limit_key": "PageSize" + }, + "ListConstraintsForPortfolio": { + "result_key": "ConstraintDetails", + "output_token": "NextPageToken", + "input_token": "PageToken", + "limit_key": "PageSize" + }, + "ListLaunchPaths": { + "result_key": "LaunchPathSummaries", + "output_token": "NextPageToken", + "input_token": "PageToken", + "limit_key": "PageSize" + }, + "ListTagOptions": { + "result_key": "TagOptionDetails", + "output_token": "PageToken", + "input_token": "PageToken", + "limit_key": "PageSize" + }, + "ListPortfoliosForProduct": { + "result_key": "PortfolioDetails", + "output_token": "NextPageToken", + "input_token": "PageToken", + "limit_key": "PageSize" + }, + "ListPrincipalsForPortfolio": { + "result_key": "Principals", + "output_token": "NextPageToken", + "input_token": "PageToken", + "limit_key": "PageSize" + }, + "ListResourcesForTagOption": { + "result_key": "ResourceDetails", + "output_token": "PageToken", + "input_token": "PageToken", + "limit_key": "PageSize" + }, + "ListOrganizationPortfolioAccess": { + "input_token": "PageToken", + "limit_key": "PageSize", + "output_token": "NextPageToken", + "result_key": "OrganizationNodes" + }, + "ListProvisionedProductPlans": { + "input_token": "PageToken", + "limit_key": "PageSize", + "output_token": "NextPageToken", + "result_key": "ProvisionedProductPlans" + }, + "ListProvisioningArtifactsForServiceAction": { + "input_token": "PageToken", + "limit_key": "PageSize", + "output_token": "NextPageToken", + "result_key": "ProvisioningArtifactViews" + }, + "ListRecordHistory": { + "input_token": "PageToken", + "limit_key": "PageSize", + "output_token": "NextPageToken", + "result_key": "RecordDetails" + }, + "ListServiceActions": { + "input_token": "PageToken", + "limit_key": "PageSize", + "output_token": "NextPageToken", + "result_key": "ServiceActionSummaries" + }, + "ListServiceActionsForProvisioningArtifact": { + "input_token": "PageToken", + "limit_key": "PageSize", + "output_token": "NextPageToken", + "result_key": "ServiceActionSummaries" + }, + "ScanProvisionedProducts": { + "input_token": "PageToken", + "limit_key": "PageSize", + "output_token": "NextPageToken", + "result_key": "ProvisionedProducts" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/servicecatalog/2015-12-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog/2015-12-10/service-2.json.gz new file mode 100644 index 0000000..37f430f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/servicecatalog/2015-12-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..86bce91 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/examples-1.json new file mode 100644 index 0000000..cc99fe4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/examples-1.json @@ -0,0 +1,672 @@ +{ + "version": "1.0", + "examples": { + "CreateHttpNamespace": [ + { + "input": { + "CreatorRequestId": "example-creator-request-id-0001", + "Description": "Example.com AWS Cloud Map HTTP Namespace", + "Name": "example-http.com" + }, + "output": { + "OperationId": "httpvoqozuhfet5kzxoxg-a-response-example" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates an HTTP namespace.", + "id": "createhttpnamespace-example-1590114811304", + "title": "CreateHttpNamespace example" + } + ], + "CreatePrivateDnsNamespace": [ + { + "input": { + "CreatorRequestId": "eedd6892-50f3-41b2-8af9-611d6e1d1a8c", + "Name": "example.com", + "Vpc": "vpc-1c56417b" + }, + "output": { + "OperationId": "gv4g5meo7ndmeh4fqskygvk23d2fijwa-k9302yzd" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Example: Create private DNS namespace", + "id": "example-create-private-dns-namespace-1587058592930", + "title": "Example: Create private DNS namespace" + } + ], + "CreatePublicDnsNamespace": [ + { + "input": { + "CreatorRequestId": "example-creator-request-id-0003", + "Description": "Example.com AWS Cloud Map Public DNS Namespace", + "Name": "example-public-dns.com" + }, + "output": { + "OperationId": "dns2voqozuhfet5kzxoxg-a-response-example" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example creates a public namespace based on DNS.", + "id": "createpublicdnsnamespace-example-1590114940910", + "title": "CreatePublicDnsNamespace example" + } + ], + "CreateService": [ + { + "input": { + "CreatorRequestId": "567c1193-6b00-4308-bd57-ad38a8822d25", + "DnsConfig": { + "DnsRecords": [ + { + "TTL": 60, + "Type": "A" + } + ], + "NamespaceId": "ns-ylexjili4cdxy3xm", + "RoutingPolicy": "MULTIVALUE" + }, + "Name": "myservice", + "NamespaceId": "ns-ylexjili4cdxy3xm" + }, + "output": { + "Service": { + "Arn": "arn:aws:servicediscovery:us-west-2:123456789012:service/srv-p5zdwlg5uvvzjita", + "CreateDate": 1587081768.334, + "CreatorRequestId": "567c1193-6b00-4308-bd57-ad38a8822d25", + "DnsConfig": { + "DnsRecords": [ + { + "TTL": 60, + "Type": "A" + } + ], + "NamespaceId": "ns-ylexjili4cdxy3xm", + "RoutingPolicy": "MULTIVALUE" + }, + "Id": "srv-p5zdwlg5uvvzjita", + "Name": "myservice", + "NamespaceId": "ns-ylexjili4cdxy3xm" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Example: Create service", + "id": "example-create-service-1587235913584", + "title": "Example: Create service" + } + ], + "DeleteNamespace": [ + { + "input": { + "Id": "ns-ylexjili4cdxy3xm" + }, + "output": { + "OperationId": "gv4g5meo7ndmeh4fqskygvk23d2fijwa-k98y6drk" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Example: Delete namespace", + "id": "example-delete-namespace-1587416093508", + "title": "Example: Delete namespace" + } + ], + "DeleteService": [ + { + "input": { + "Id": "srv-p5zdwlg5uvvzjita" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Example: Delete service", + "id": "example-delete-service-1587416462902", + "title": "Example: Delete service" + } + ], + "DeregisterInstance": [ + { + "input": { + "InstanceId": "myservice-53", + "ServiceId": "srv-p5zdwlg5uvvzjita" + }, + "output": { + "OperationId": "4yejorelbukcjzpnr6tlmrghsjwpngf4-k98rnaiq" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Example: Deregister a service instance", + "id": "example-deregister-a-service-instance-1587416305738", + "title": "Example: Deregister a service instance" + } + ], + "DiscoverInstances": [ + { + "input": { + "HealthStatus": "ALL", + "MaxResults": 10, + "NamespaceName": "example.com", + "ServiceName": "myservice" + }, + "output": { + "Instances": [ + { + "Attributes": { + "AWS_INSTANCE_IPV4": "172.2.1.3", + "AWS_INSTANCE_PORT": "808" + }, + "HealthStatus": "UNKNOWN", + "InstanceId": "myservice-53", + "NamespaceName": "example.com", + "ServiceName": "myservice" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Example: Discover registered instances", + "id": "example-discover-registered-instances-1587236343568", + "title": "Example: Discover registered instances" + } + ], + "GetInstance": [ + { + "input": { + "InstanceId": "i-abcd1234", + "ServiceId": "srv-e4anhexample0004" + }, + "output": { + "Instance": { + "Attributes": { + "AWS_INSTANCE_IPV4": "192.0.2.44", + "AWS_INSTANCE_PORT": "80", + "color": "green", + "region": "us-west-2", + "stage": "beta" + }, + "Id": "i-abcd1234" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example gets information about a specified instance.", + "id": "getinstance-example-1590115065598", + "title": "GetInstance example" + } + ], + "GetInstancesHealthStatus": [ + { + "input": { + "ServiceId": "srv-e4anhexample0004" + }, + "output": { + "Status": { + "i-abcd1234": "HEALTHY", + "i-abcd1235": "UNHEALTHY" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example gets the current health status of one or more instances that are associate with a specified service.", + "id": "getinstanceshealthstatus-example-1590115176146", + "title": "GetInstancesHealthStatus example" + } + ], + "GetNamespace": [ + { + "input": { + "Id": "ns-e4anhexample0004" + }, + "output": { + "Namespace": { + "Arn": "arn:aws:servicediscovery:us-west-2: 123456789120:namespace/ns-e1tpmexample0001", + "CreateDate": "20181118T211712Z", + "CreatorRequestId": "example-creator-request-id-0001", + "Description": "Example.com AWS Cloud Map HTTP Namespace", + "Id": "ns-e1tpmexample0001", + "Name": "example-http.com", + "Properties": { + "DnsProperties": { + }, + "HttpProperties": { + "HttpName": "example-http.com" + } + }, + "Type": "HTTP" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example gets information about a specified namespace.", + "id": "getnamespace-example-1590115383708", + "title": "GetNamespace example" + } + ], + "GetOperation": [ + { + "input": { + "OperationId": "gv4g5meo7ndmeh4fqskygvk23d2fijwa-k9302yzd" + }, + "output": { + "Operation": { + "CreateDate": 1587055860.121, + "Id": "gv4g5meo7ndmeh4fqskygvk23d2fijwa-k9302yzd", + "Status": "SUCCESS", + "Targets": { + "NAMESPACE": "ns-ylexjili4cdxy3xm" + }, + "Type": "CREATE_NAMESPACE", + "UpdateDate": 1587055900.469 + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Example: Get operation result", + "id": "example-get-operation-result-1587073807124", + "title": "Example: Get operation result" + } + ], + "GetService": [ + { + "input": { + "Id": "srv-e4anhexample0004" + }, + "output": { + "Service": { + "Arn": "arn:aws:servicediscovery:us-west-2:123456789120:service/srv-e4anhexample0004", + "CreateDate": "20181118T211707Z", + "CreatorRequestId": "example-creator-request-id-0004", + "Description": "Example.com AWS Cloud Map HTTP Service", + "HealthCheckConfig": { + "FailureThreshold": 3, + "ResourcePath": "/", + "Type": "HTTPS" + }, + "Id": "srv-e4anhexample0004", + "Name": "example-http-service", + "NamespaceId": "ns-e4anhexample0004" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example gets the settings for a specified service.", + "id": "getservice-example-1590117234294", + "title": "GetService Example" + } + ], + "ListInstances": [ + { + "input": { + "ServiceId": "srv-qzpwvt2tfqcegapy" + }, + "output": { + "Instances": [ + { + "Attributes": { + "AWS_INSTANCE_IPV4": "172.2.1.3", + "AWS_INSTANCE_PORT": "808" + }, + "Id": "i-06bdabbae60f65a4e" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Example: List service instances", + "id": "example-list-service-instances-1587236237008", + "title": "Example: List service instances" + } + ], + "ListNamespaces": [ + { + "input": { + }, + "output": { + "Namespaces": [ + { + "Arn": "arn:aws:servicediscovery:us-west-2:123456789012:namespace/ns-a3ccy2e7e3a7rile", + "CreateDate": 1585354387.357, + "Id": "ns-a3ccy2e7e3a7rile", + "Name": "local", + "Properties": { + "DnsProperties": { + "HostedZoneId": "Z06752353VBUDTC32S84S" + }, + "HttpProperties": { + "HttpName": "local" + } + }, + "Type": "DNS_PRIVATE" + }, + { + "Arn": "arn:aws:servicediscovery:us-west-2:123456789012:namespace/ns-pocfyjtrsmwtvcxx", + "CreateDate": 1586468974.698, + "Description": "My second namespace", + "Id": "ns-pocfyjtrsmwtvcxx", + "Name": "My-second-namespace", + "Properties": { + "DnsProperties": { + }, + "HttpProperties": { + "HttpName": "My-second-namespace" + } + }, + "Type": "HTTP" + }, + { + "Arn": "arn:aws:servicediscovery:us-west-2:123456789012:namespace/ns-ylexjili4cdxy3xm", + "CreateDate": 1587055896.798, + "Id": "ns-ylexjili4cdxy3xm", + "Name": "example.com", + "Properties": { + "DnsProperties": { + "HostedZoneId": "Z09983722P0QME1B3KC8I" + }, + "HttpProperties": { + "HttpName": "example.com" + } + }, + "Type": "DNS_PRIVATE" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Example: List namespaces", + "id": "example-list-namespaces-1587401553154", + "title": "Example: List namespaces" + } + ], + "ListOperations": [ + { + "input": { + "Filters": [ + { + "Condition": "IN", + "Name": "STATUS", + "Values": [ + "PENDING", + "SUCCESS" + ] + } + ] + }, + "output": { + "Operations": [ + { + "Id": "76yy8ovhpdz0plmjzbsnqgnrqvpv2qdt-kexample", + "Status": "SUCCESS" + }, + { + "Id": "prysnyzpji3u2ciy45nke83x2zanl7yk-dexample", + "Status": "SUCCESS" + }, + { + "Id": "ko4ekftir7kzlbechsh7xvcdgcpk66gh-7example", + "Status": "PENDING" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example gets the operations that have a STATUS of either PENDING or SUCCESS.", + "id": "listoperations-example-1590117354396", + "title": "ListOperations Example" + } + ], + "ListServices": [ + { + "input": { + }, + "output": { + "Services": [ + { + "Arn": "arn:aws:servicediscovery:us-west-2:123456789012:service/srv-p5zdwlg5uvvzjita", + "CreateDate": 1587081768.334, + "DnsConfig": { + "DnsRecords": [ + { + "TTL": 60, + "Type": "A" + } + ], + "RoutingPolicy": "MULTIVALUE" + }, + "Id": "srv-p5zdwlg5uvvzjita", + "Name": "myservice" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Example: List services", + "id": "example-list-services-1587236889840", + "title": "Example: List services" + } + ], + "ListTagsForResource": [ + { + "input": { + "ResourceARN": "arn:aws:servicediscovery:us-east-1:123456789012:namespace/ns-ylexjili4cdxy3xm" + }, + "output": { + "Tags": [ + { + "Key": "Project", + "Value": "Zeta" + }, + { + "Key": "Department", + "Value": "Engineering" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example lists the tags of a resource.", + "id": "listtagsforresource-example-1590093928416", + "title": "ListTagsForResource example" + } + ], + "RegisterInstance": [ + { + "input": { + "Attributes": { + "AWS_INSTANCE_IPV4": "172.2.1.3", + "AWS_INSTANCE_PORT": "808" + }, + "CreatorRequestId": "7a48a98a-72e6-4849-bfa7-1a458e030d7b", + "InstanceId": "myservice-53", + "ServiceId": "srv-p5zdwlg5uvvzjita" + }, + "output": { + "OperationId": "4yejorelbukcjzpnr6tlmrghsjwpngf4-k95yg2u7" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Example: Register Instance", + "id": "example-register-instance-1587236116314", + "title": "Example: Register Instance" + } + ], + "TagResource": [ + { + "input": { + "ResourceARN": "arn:aws:servicediscovery:us-east-1:123456789012:namespace/ns-ylexjili4cdxy3xm", + "Tags": [ + { + "Key": "Department", + "Value": "Engineering" + }, + { + "Key": "Project", + "Value": "Zeta" + } + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example adds \"Department\" and \"Project\" tags to a resource.", + "id": "tagresource-example-1590093532240", + "title": "TagResource example" + } + ], + "UntagResource": [ + { + "input": { + "ResourceARN": "arn:aws:servicediscovery:us-east-1:123456789012:namespace/ns-ylexjili4cdxy3xm", + "TagKeys": [ + "Project", + "Department" + ] + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example removes the \"Department\" and \"Project\" tags from a resource.", + "id": "untagresource-example-1590094024672", + "title": "UntagResource example" + } + ], + "UpdateInstanceCustomHealthStatus": [ + { + "input": { + "InstanceId": "i-abcd1234", + "ServiceId": "srv-e4anhexample0004", + "Status": "HEALTHY" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example submits a request to change the health status of an instance associated with a service with a custom health check to HEALTHY.", + "id": "updateinstancecustomhealthstatus-example-1590118408574", + "title": "UpdateInstanceCustomHealthStatus Example" + } + ], + "UpdateService": [ + { + "input": { + "Id": "srv-e4anhexample0004", + "Service": { + "DnsConfig": { + "DnsRecords": [ + { + "TTL": 60, + "Type": "A" + } + ] + }, + "HealthCheckConfig": { + "FailureThreshold": 2, + "ResourcePath": "/", + "Type": "HTTP" + } + } + }, + "output": { + "OperationId": "m35hsdrkxwjffm3xef4bxyy6vc3ewakx-jdn3y5g5" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example submits a request to replace the DnsConfig and HealthCheckConfig settings of a specified service.", + "id": "updateservice-example-1590117830880", + "title": "UpdateService Example" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/paginators-1.json new file mode 100644 index 0000000..f58df70 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListServices": { + "result_key": "Services", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListInstances": { + "result_key": "Instances", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListNamespaces": { + "result_key": "Namespaces", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListOperations": { + "result_key": "Operations", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/paginators-1.sdk-extras.json b/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/paginators-1.sdk-extras.json new file mode 100644 index 0000000..1e17fb6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/paginators-1.sdk-extras.json @@ -0,0 +1,12 @@ +{ + "version": 1.0, + "merge": { + "pagination": { + "ListInstances": { + "non_aggregate_keys": [ + "ResourceOwner" + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/service-2.json.gz new file mode 100644 index 0000000..2bcb0c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/servicediscovery/2017-03-14/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a4fff94 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/examples-1.json new file mode 100644 index 0000000..e569033 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/examples-1.json @@ -0,0 +1,1021 @@ +{ + "version": "1.0", + "examples": { + "CloneReceiptRuleSet": [ + { + "input": { + "OriginalRuleSetName": "RuleSetToClone", + "RuleSetName": "RuleSetToCreate" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a receipt rule set by cloning an existing one:", + "id": "clonereceiptruleset-1469055039770", + "title": "CloneReceiptRuleSet" + } + ], + "CreateReceiptFilter": [ + { + "input": { + "Filter": { + "IpFilter": { + "Cidr": "1.2.3.4/24", + "Policy": "Allow" + }, + "Name": "MyFilter" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a new IP address filter:", + "id": "createreceiptfilter-1469122681253", + "title": "CreateReceiptFilter" + } + ], + "CreateReceiptRule": [ + { + "input": { + "After": "", + "Rule": { + "Actions": [ + { + "S3Action": { + "BucketName": "MyBucket", + "ObjectKeyPrefix": "email" + } + } + ], + "Enabled": true, + "Name": "MyRule", + "ScanEnabled": true, + "TlsPolicy": "Optional" + }, + "RuleSetName": "MyRuleSet" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a new receipt rule:", + "id": "createreceiptrule-1469122946515", + "title": "CreateReceiptRule" + } + ], + "CreateReceiptRuleSet": [ + { + "input": { + "RuleSetName": "MyRuleSet" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates an empty receipt rule set:", + "id": "createreceiptruleset-1469058761646", + "title": "CreateReceiptRuleSet" + } + ], + "DeleteIdentity": [ + { + "input": { + "Identity": "user@example.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an identity from the list of identities that have been submitted for verification with Amazon SES:", + "id": "deleteidentity-1469047858906", + "title": "DeleteIdentity" + } + ], + "DeleteIdentityPolicy": [ + { + "input": { + "Identity": "user@example.com", + "PolicyName": "MyPolicy" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a sending authorization policy for an identity:", + "id": "deleteidentitypolicy-1469055282499", + "title": "DeleteIdentityPolicy" + } + ], + "DeleteReceiptFilter": [ + { + "input": { + "FilterName": "MyFilter" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an IP address filter:", + "id": "deletereceiptfilter-1469055456835", + "title": "DeleteReceiptFilter" + } + ], + "DeleteReceiptRule": [ + { + "input": { + "RuleName": "MyRule", + "RuleSetName": "MyRuleSet" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a receipt rule:", + "id": "deletereceiptrule-1469055563599", + "title": "DeleteReceiptRule" + } + ], + "DeleteReceiptRuleSet": [ + { + "input": { + "RuleSetName": "MyRuleSet" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a receipt rule set:", + "id": "deletereceiptruleset-1469055713690", + "title": "DeleteReceiptRuleSet" + } + ], + "DeleteVerifiedEmailAddress": [ + { + "input": { + "EmailAddress": "user@example.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an email address from the list of identities that have been submitted for verification with Amazon SES:", + "id": "deleteverifiedemailaddress-1469051086444", + "title": "DeleteVerifiedEmailAddress" + } + ], + "DescribeActiveReceiptRuleSet": [ + { + "input": { + }, + "output": { + "Metadata": { + "CreatedTimestamp": "2016-07-15T16:25:59.607Z", + "Name": "default-rule-set" + }, + "Rules": [ + { + "Actions": [ + { + "S3Action": { + "BucketName": "MyBucket", + "ObjectKeyPrefix": "email" + } + } + ], + "Enabled": true, + "Name": "MyRule", + "ScanEnabled": true, + "TlsPolicy": "Optional" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the metadata and receipt rules for the receipt rule set that is currently active:", + "id": "describeactivereceiptruleset-1469121611502", + "title": "DescribeActiveReceiptRuleSet" + } + ], + "DescribeReceiptRule": [ + { + "input": { + "RuleName": "MyRule", + "RuleSetName": "MyRuleSet" + }, + "output": { + "Rule": { + "Actions": [ + { + "S3Action": { + "BucketName": "MyBucket", + "ObjectKeyPrefix": "email" + } + } + ], + "Enabled": true, + "Name": "MyRule", + "ScanEnabled": true, + "TlsPolicy": "Optional" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of a receipt rule:", + "id": "describereceiptrule-1469055813118", + "title": "DescribeReceiptRule" + } + ], + "DescribeReceiptRuleSet": [ + { + "input": { + "RuleSetName": "MyRuleSet" + }, + "output": { + "Metadata": { + "CreatedTimestamp": "2016-07-15T16:25:59.607Z", + "Name": "MyRuleSet" + }, + "Rules": [ + { + "Actions": [ + { + "S3Action": { + "BucketName": "MyBucket", + "ObjectKeyPrefix": "email" + } + } + ], + "Enabled": true, + "Name": "MyRule", + "ScanEnabled": true, + "TlsPolicy": "Optional" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the metadata and receipt rules of a receipt rule set:", + "id": "describereceiptruleset-1469121240385", + "title": "DescribeReceiptRuleSet" + } + ], + "GetAccountSendingEnabled": [ + { + "output": { + "Enabled": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns if sending status for an account is enabled. (true / false):", + "id": "getaccountsendingenabled-1469047741333", + "title": "GetAccountSendingEnabled" + } + ], + "GetIdentityDkimAttributes": [ + { + "input": { + "Identities": [ + "example.com", + "user@example.com" + ] + }, + "output": { + "DkimAttributes": { + "example.com": { + "DkimEnabled": true, + "DkimTokens": [ + "EXAMPLEjcs5xoyqytjsotsijas7236gr", + "EXAMPLEjr76cvoc6mysspnioorxsn6ep", + "EXAMPLEkbmkqkhlm2lyz77ppkulerm4k" + ], + "DkimVerificationStatus": "Success" + }, + "user@example.com": { + "DkimEnabled": false, + "DkimVerificationStatus": "NotStarted" + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example retrieves the Amazon SES Easy DKIM attributes for a list of identities:", + "id": "getidentitydkimattributes-1469050695628", + "title": "GetIdentityDkimAttributes" + } + ], + "GetIdentityMailFromDomainAttributes": [ + { + "input": { + "Identities": [ + "example.com" + ] + }, + "output": { + "MailFromDomainAttributes": { + "example.com": { + "BehaviorOnMXFailure": "UseDefaultValue", + "MailFromDomain": "bounces.example.com", + "MailFromDomainStatus": "Success" + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the custom MAIL FROM attributes for an identity:", + "id": "getidentitymailfromdomainattributes-1469123114860", + "title": "GetIdentityMailFromDomainAttributes" + } + ], + "GetIdentityNotificationAttributes": [ + { + "input": { + "Identities": [ + "example.com" + ] + }, + "output": { + "NotificationAttributes": { + "example.com": { + "BounceTopic": "arn:aws:sns:us-east-1:EXAMPLE65304:ExampleTopic", + "ComplaintTopic": "arn:aws:sns:us-east-1:EXAMPLE65304:ExampleTopic", + "DeliveryTopic": "arn:aws:sns:us-east-1:EXAMPLE65304:ExampleTopic", + "ForwardingEnabled": true, + "HeadersInBounceNotificationsEnabled": false, + "HeadersInComplaintNotificationsEnabled": false, + "HeadersInDeliveryNotificationsEnabled": false + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the notification attributes for an identity:", + "id": "getidentitynotificationattributes-1469123466947", + "title": "GetIdentityNotificationAttributes" + } + ], + "GetIdentityPolicies": [ + { + "input": { + "Identity": "example.com", + "PolicyNames": [ + "MyPolicy" + ] + }, + "output": { + "Policies": { + "MyPolicy": "{\"Version\":\"2008-10-17\",\"Statement\":[{\"Sid\":\"stmt1469123904194\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"},\"Action\":[\"ses:SendEmail\",\"ses:SendRawEmail\"],\"Resource\":\"arn:aws:ses:us-east-1:EXAMPLE65304:identity/example.com\"}]}" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns a sending authorization policy for an identity:", + "id": "getidentitypolicies-1469123949351", + "title": "GetIdentityPolicies" + } + ], + "GetIdentityVerificationAttributes": [ + { + "input": { + "Identities": [ + "example.com" + ] + }, + "output": { + "VerificationAttributes": { + "example.com": { + "VerificationStatus": "Success", + "VerificationToken": "EXAMPLE3VYb9EDI2nTOQRi/Tf6MI/6bD6THIGiP1MVY=" + } + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the verification status and the verification token for a domain identity:", + "id": "getidentityverificationattributes-1469124205897", + "title": "GetIdentityVerificationAttributes" + } + ], + "GetSendQuota": [ + { + "output": { + "Max24HourSend": 200, + "MaxSendRate": 1, + "SentLast24Hours": 1 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the Amazon SES sending limits for an AWS account:", + "id": "getsendquota-1469047324508", + "title": "GetSendQuota" + } + ], + "GetSendStatistics": [ + { + "output": { + "SendDataPoints": [ + { + "Bounces": 0, + "Complaints": 0, + "DeliveryAttempts": 5, + "Rejects": 0, + "Timestamp": "2016-07-13T22:43:00Z" + }, + { + "Bounces": 0, + "Complaints": 0, + "DeliveryAttempts": 3, + "Rejects": 0, + "Timestamp": "2016-07-13T23:13:00Z" + }, + { + "Bounces": 0, + "Complaints": 0, + "DeliveryAttempts": 1, + "Rejects": 0, + "Timestamp": "2016-07-13T21:13:00Z" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns Amazon SES sending statistics:", + "id": "getsendstatistics-1469047741329", + "title": "GetSendStatistics" + } + ], + "ListIdentities": [ + { + "input": { + "IdentityType": "EmailAddress", + "MaxItems": 123, + "NextToken": "" + }, + "output": { + "Identities": [ + "user@example.com" + ], + "NextToken": "" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example lists the email address identities that have been submitted for verification with Amazon SES:", + "id": "listidentities-1469048638493", + "title": "ListIdentities" + } + ], + "ListIdentityPolicies": [ + { + "input": { + "Identity": "example.com" + }, + "output": { + "PolicyNames": [ + "MyPolicy" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns a list of sending authorization policies that are attached to an identity:", + "id": "listidentitypolicies-1469124417674", + "title": "ListIdentityPolicies" + } + ], + "ListReceiptFilters": [ + { + "output": { + "Filters": [ + { + "IpFilter": { + "Cidr": "1.2.3.4/24", + "Policy": "Block" + }, + "Name": "MyFilter" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example lists the IP address filters that are associated with an AWS account:", + "id": "listreceiptfilters-1469120786789", + "title": "ListReceiptFilters" + } + ], + "ListReceiptRuleSets": [ + { + "input": { + "NextToken": "" + }, + "output": { + "NextToken": "", + "RuleSets": [ + { + "CreatedTimestamp": "2016-07-15T16:25:59.607Z", + "Name": "MyRuleSet" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example lists the receipt rule sets that exist under an AWS account:", + "id": "listreceiptrulesets-1469121037235", + "title": "ListReceiptRuleSets" + } + ], + "ListVerifiedEmailAddresses": [ + { + "output": { + "VerifiedEmailAddresses": [ + "user1@example.com", + "user2@example.com" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example lists all email addresses that have been submitted for verification with Amazon SES:", + "id": "listverifiedemailaddresses-1469051402570", + "title": "ListVerifiedEmailAddresses" + } + ], + "PutIdentityPolicy": [ + { + "input": { + "Identity": "example.com", + "Policy": "{\"Version\":\"2008-10-17\",\"Statement\":[{\"Sid\":\"stmt1469123904194\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"},\"Action\":[\"ses:SendEmail\",\"ses:SendRawEmail\"],\"Resource\":\"arn:aws:ses:us-east-1:EXAMPLE65304:identity/example.com\"}]}", + "PolicyName": "MyPolicy" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example adds a sending authorization policy to an identity:", + "id": "putidentitypolicy-1469124560016", + "title": "PutIdentityPolicy" + } + ], + "ReorderReceiptRuleSet": [ + { + "input": { + "RuleNames": [ + "MyRule", + "MyOtherRule" + ], + "RuleSetName": "MyRuleSet" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example reorders the receipt rules within a receipt rule set:", + "id": "reorderreceiptruleset-1469058156806", + "title": "ReorderReceiptRuleSet" + } + ], + "SendEmail": [ + { + "input": { + "Destination": { + "BccAddresses": [ + + ], + "CcAddresses": [ + "recipient3@example.com" + ], + "ToAddresses": [ + "recipient1@example.com", + "recipient2@example.com" + ] + }, + "Message": { + "Body": { + "Html": { + "Charset": "UTF-8", + "Data": "This message body contains HTML formatting. It can, for example, contain links like this one: Amazon SES Developer Guide." + }, + "Text": { + "Charset": "UTF-8", + "Data": "This is the message body in text format." + } + }, + "Subject": { + "Charset": "UTF-8", + "Data": "Test email" + } + }, + "ReplyToAddresses": [ + + ], + "ReturnPath": "", + "ReturnPathArn": "", + "Source": "sender@example.com", + "SourceArn": "" + }, + "output": { + "MessageId": "EXAMPLE78603177f-7a5433e7-8edb-42ae-af10-f0181f34d6ee-000000" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example sends a formatted email:", + "id": "sendemail-1469049656296", + "title": "SendEmail" + } + ], + "SendRawEmail": [ + { + "input": { + "Destinations": [ + + ], + "FromArn": "", + "RawMessage": { + "Data": "From: sender@example.com\\nTo: recipient@example.com\\nSubject: Test email (contains an attachment)\\nMIME-Version: 1.0\\nContent-type: Multipart/Mixed; boundary=\"NextPart\"\\n\\n--NextPart\\nContent-Type: text/plain\\n\\nThis is the message body.\\n\\n--NextPart\\nContent-Type: text/plain;\\nContent-Disposition: attachment; filename=\"attachment.txt\"\\n\\nThis is the text in the attachment.\\n\\n--NextPart--" + }, + "ReturnPathArn": "", + "Source": "", + "SourceArn": "" + }, + "output": { + "MessageId": "EXAMPLEf3f73d99b-c63fb06f-d263-41f8-a0fb-d0dc67d56c07-000000" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example sends an email with an attachment:", + "id": "sendrawemail-1469118548649", + "title": "SendRawEmail" + } + ], + "SetActiveReceiptRuleSet": [ + { + "input": { + "RuleSetName": "RuleSetToActivate" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example sets the active receipt rule set:", + "id": "setactivereceiptruleset-1469058391329", + "title": "SetActiveReceiptRuleSet" + } + ], + "SetIdentityDkimEnabled": [ + { + "input": { + "DkimEnabled": true, + "Identity": "user@example.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example configures Amazon SES to Easy DKIM-sign the email sent from an identity:", + "id": "setidentitydkimenabled-1469057485202", + "title": "SetIdentityDkimEnabled" + } + ], + "SetIdentityFeedbackForwardingEnabled": [ + { + "input": { + "ForwardingEnabled": true, + "Identity": "user@example.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example configures Amazon SES to forward an identity's bounces and complaints via email:", + "id": "setidentityfeedbackforwardingenabled-1469056811329", + "title": "SetIdentityFeedbackForwardingEnabled" + } + ], + "SetIdentityHeadersInNotificationsEnabled": [ + { + "input": { + "Enabled": true, + "Identity": "user@example.com", + "NotificationType": "Bounce" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example configures Amazon SES to include the original email headers in the Amazon SNS bounce notifications for an identity:", + "id": "setidentityheadersinnotificationsenabled-1469057295001", + "title": "SetIdentityHeadersInNotificationsEnabled" + } + ], + "SetIdentityMailFromDomain": [ + { + "input": { + "BehaviorOnMXFailure": "UseDefaultValue", + "Identity": "user@example.com", + "MailFromDomain": "bounces.example.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example configures Amazon SES to use a custom MAIL FROM domain for an identity:", + "id": "setidentitymailfromdomain-1469057693908", + "title": "SetIdentityMailFromDomain" + } + ], + "SetIdentityNotificationTopic": [ + { + "input": { + "Identity": "user@example.com", + "NotificationType": "Bounce", + "SnsTopic": "arn:aws:sns:us-west-2:111122223333:MyTopic" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example sets the Amazon SNS topic to which Amazon SES will publish bounce, complaint, and/or delivery notifications for emails sent with the specified identity as the Source:", + "id": "setidentitynotificationtopic-1469057854966", + "title": "SetIdentityNotificationTopic" + } + ], + "SetReceiptRulePosition": [ + { + "input": { + "After": "PutRuleAfterThisRule", + "RuleName": "RuleToReposition", + "RuleSetName": "MyRuleSet" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example sets the position of a receipt rule in a receipt rule set:", + "id": "setreceiptruleposition-1469058530550", + "title": "SetReceiptRulePosition" + } + ], + "UpdateAccountSendingEnabled": [ + { + "input": { + "Enabled": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example updated the sending status for this account.", + "id": "updateaccountsendingenabled-1469047741333", + "title": "UpdateAccountSendingEnabled" + } + ], + "UpdateConfigurationSetReputationMetricsEnabled": [ + { + "input": { + "ConfigurationSetName": "foo", + "Enabled": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Set the reputationMetricsEnabled flag for a specific configuration set.", + "id": "updateconfigurationsetreputationmetricsenabled-2362747741333", + "title": "UpdateConfigurationSetReputationMetricsEnabled" + } + ], + "UpdateConfigurationSetSendingEnabled": [ + { + "input": { + "ConfigurationSetName": "foo", + "Enabled": true + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Set the sending enabled flag for a specific configuration set.", + "id": "updateconfigurationsetsendingenabled-2362747741333", + "title": "UpdateConfigurationSetReputationMetricsEnabled" + } + ], + "UpdateReceiptRule": [ + { + "input": { + "Rule": { + "Actions": [ + { + "S3Action": { + "BucketName": "MyBucket", + "ObjectKeyPrefix": "email" + } + } + ], + "Enabled": true, + "Name": "MyRule", + "ScanEnabled": true, + "TlsPolicy": "Optional" + }, + "RuleSetName": "MyRuleSet" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example updates a receipt rule to use an Amazon S3 action:", + "id": "updatereceiptrule-1469051756940", + "title": "UpdateReceiptRule" + } + ], + "VerifyDomainDkim": [ + { + "input": { + "Domain": "example.com" + }, + "output": { + "DkimTokens": [ + "EXAMPLEq76owjnks3lnluwg65scbemvw", + "EXAMPLEi3dnsj67hstzaj673klariwx2", + "EXAMPLEwfbtcukvimehexktmdtaz6naj" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example generates DKIM tokens for a domain that has been verified with Amazon SES:", + "id": "verifydomaindkim-1469049503083", + "title": "VerifyDomainDkim" + } + ], + "VerifyDomainIdentity": [ + { + "input": { + "Domain": "example.com" + }, + "output": { + "VerificationToken": "eoEmxw+YaYhb3h3iVJHuXMJXqeu1q1/wwmvjuEXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example starts the domain verification process with Amazon SES:", + "id": "verifydomainidentity-1469049165936", + "title": "VerifyDomainIdentity" + } + ], + "VerifyEmailAddress": [ + { + "input": { + "EmailAddress": "user@example.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example starts the email address verification process with Amazon SES:", + "id": "verifyemailaddress-1469048849187", + "title": "VerifyEmailAddress" + } + ], + "VerifyEmailIdentity": [ + { + "input": { + "EmailAddress": "user@example.com" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example starts the email address verification process with Amazon SES:", + "id": "verifyemailidentity-1469049068623", + "title": "VerifyEmailIdentity" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/paginators-1.json new file mode 100644 index 0000000..1eb0054 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/paginators-1.json @@ -0,0 +1,33 @@ +{ + "pagination": { + "ListIdentities": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxItems", + "result_key": "Identities" + }, + "ListCustomVerificationEmailTemplates": { + "result_key": "CustomVerificationEmailTemplates", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListConfigurationSets": { + "input_token": "NextToken", + "limit_key": "MaxItems", + "output_token": "NextToken", + "result_key": "ConfigurationSets" + }, + "ListReceiptRuleSets": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "RuleSets" + }, + "ListTemplates": { + "input_token": "NextToken", + "limit_key": "MaxItems", + "output_token": "NextToken", + "result_key": "TemplatesMetadata" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/service-2.json.gz new file mode 100644 index 0000000..0481056 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/waiters-2.json new file mode 100644 index 0000000..b585d30 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ses/2010-12-01/waiters-2.json @@ -0,0 +1,18 @@ +{ + "version": 2, + "waiters": { + "IdentityExists": { + "delay": 3, + "operation": "GetIdentityVerificationAttributes", + "maxAttempts": 20, + "acceptors": [ + { + "expected": "Success", + "matcher": "pathAll", + "state": "success", + "argument": "VerificationAttributes.*.VerificationStatus" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sesv2/2019-09-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sesv2/2019-09-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..abea7ac Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sesv2/2019-09-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sesv2/2019-09-27/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/sesv2/2019-09-27/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sesv2/2019-09-27/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sesv2/2019-09-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sesv2/2019-09-27/paginators-1.json new file mode 100644 index 0000000..c07227e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sesv2/2019-09-27/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListMultiRegionEndpoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "MultiRegionEndpoints" + }, + "ListReputationEntities": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "ReputationEntities" + }, + "ListResourceTenants": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "ResourceTenants" + }, + "ListTenantResources": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "TenantResources" + }, + "ListTenants": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "Tenants" + }, + "ListEmailIdentityCertificates": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize", + "result_key": "Certificates" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sesv2/2019-09-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sesv2/2019-09-27/service-2.json.gz new file mode 100644 index 0000000..ad8eb23 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sesv2/2019-09-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/shield/2016-06-02/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/shield/2016-06-02/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..445893c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/shield/2016-06-02/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/shield/2016-06-02/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/shield/2016-06-02/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/shield/2016-06-02/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/shield/2016-06-02/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/shield/2016-06-02/paginators-1.json new file mode 100644 index 0000000..c5ded64 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/shield/2016-06-02/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListProtections": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Protections" + }, + "ListAttacks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AttackSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/shield/2016-06-02/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/shield/2016-06-02/service-2.json.gz new file mode 100644 index 0000000..60b72db Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/shield/2016-06-02/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/signer-data/2017-08-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/signer-data/2017-08-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0c9ed15 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/signer-data/2017-08-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/signer-data/2017-08-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/signer-data/2017-08-25/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/signer-data/2017-08-25/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/signer-data/2017-08-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/signer-data/2017-08-25/service-2.json.gz new file mode 100644 index 0000000..2b13003 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/signer-data/2017-08-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/signer-data/2017-08-25/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/signer-data/2017-08-25/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/signer-data/2017-08-25/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..aae22dc Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/paginators-1.json new file mode 100644 index 0000000..1e049e7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListSigningJobs": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "jobs" + }, + "ListSigningPlatforms": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "platforms" + }, + "ListSigningProfiles": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "profiles" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/service-2.json.gz new file mode 100644 index 0000000..536cb51 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/waiters-2.json new file mode 100644 index 0000000..a0890ad --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/signer/2017-08-25/waiters-2.json @@ -0,0 +1,29 @@ +{ + "version": 2, + "waiters": { + "SuccessfulSigningJob": { + "delay": 20, + "operation": "DescribeSigningJob", + "maxAttempts": 25, + "acceptors": [ + { + "expected": "Succeeded", + "matcher": "path", + "state": "success", + "argument": "status" + }, + { + "expected": "Failed", + "matcher": "path", + "state": "failure", + "argument": "status" + }, + { + "expected": "ResourceNotFoundException", + "matcher": "error", + "state": "failure" + } + ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/signin/2023-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/signin/2023-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..81a8016 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/signin/2023-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/signin/2023-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/signin/2023-01-01/paginators-1.json new file mode 100644 index 0000000..f60c369 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/signin/2023-01-01/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListResourcePermissionStatements": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "permissionStatements" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/signin/2023-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/signin/2023-01-01/service-2.json.gz new file mode 100644 index 0000000..dd20ffe Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/signin/2023-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/simpledbv2/2025-09-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/simpledbv2/2025-09-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..63de069 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/simpledbv2/2025-09-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/simpledbv2/2025-09-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/simpledbv2/2025-09-26/paginators-1.json new file mode 100644 index 0000000..f78efa8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/simpledbv2/2025-09-26/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListExports": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "exportSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/simpledbv2/2025-09-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/simpledbv2/2025-09-26/service-2.json.gz new file mode 100644 index 0000000..af00d4f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/simpledbv2/2025-09-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/simpledbv2/2025-09-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/simpledbv2/2025-09-26/waiters-2.json new file mode 100644 index 0000000..6818725 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/simpledbv2/2025-09-26/waiters-2.json @@ -0,0 +1,21 @@ +{ + "version" : 2, + "waiters" : { + "ExportSucceeded" : { + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetExport", + "acceptors" : [ { + "matcher" : "path", + "argument" : "exportStatus", + "state" : "success", + "expected" : "SUCCEEDED" + }, { + "matcher" : "path", + "argument" : "exportStatus", + "state" : "failure", + "expected" : "FAILED" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/sms-voice/2018-09-05/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sms-voice/2018-09-05/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..44bb5ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sms-voice/2018-09-05/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sms-voice/2018-09-05/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sms-voice/2018-09-05/service-2.json.gz new file mode 100644 index 0000000..7f6dcc2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sms-voice/2018-09-05/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/snow-device-management/2021-08-04/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/snow-device-management/2021-08-04/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..120f5a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/snow-device-management/2021-08-04/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/snow-device-management/2021-08-04/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/snow-device-management/2021-08-04/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/snow-device-management/2021-08-04/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/snow-device-management/2021-08-04/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/snow-device-management/2021-08-04/paginators-1.json new file mode 100644 index 0000000..8b11209 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/snow-device-management/2021-08-04/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListDeviceResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "resources" + }, + "ListDevices": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "devices" + }, + "ListExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "executions" + }, + "ListTasks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "tasks" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/snow-device-management/2021-08-04/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/snow-device-management/2021-08-04/service-2.json.gz new file mode 100644 index 0000000..e16df9a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/snow-device-management/2021-08-04/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/snowball/2016-06-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/snowball/2016-06-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..979b71f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/snowball/2016-06-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/snowball/2016-06-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/snowball/2016-06-30/examples-1.json new file mode 100644 index 0000000..2b13f7b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/snowball/2016-06-30/examples-1.json @@ -0,0 +1,442 @@ +{ + "version": "1.0", + "examples": { + "CancelCluster": [ + { + "input": { + "ClusterId": "CID123e4567-e89b-12d3-a456-426655440000" + }, + "comments": { + }, + "description": "This operation cancels a cluster job. You can only cancel a cluster job while it's in the AwaitingQuorum status.", + "id": "to-cancel-a-cluster-job-1482533760554", + "title": "To cancel a cluster job" + } + ], + "CancelJob": [ + { + "input": { + "JobId": "JID123e4567-e89b-12d3-a456-426655440000" + }, + "comments": { + }, + "description": "This operation cancels a job. You can only cancel a job before its JobState value changes to PreparingAppliance.", + "id": "to-cancel-a-job-for-a-snowball-device-1482534699477", + "title": "To cancel a job for a Snowball device" + } + ], + "CreateAddress": [ + { + "input": { + "Address": { + "City": "Seattle", + "Company": "My Company's Name", + "Country": "USA", + "Name": "My Name", + "PhoneNumber": "425-555-5555", + "PostalCode": "98101", + "StateOrProvince": "WA", + "Street1": "123 Main Street" + } + }, + "output": { + "AddressId": "ADID1234ab12-3eec-4eb3-9be6-9374c10eb51b" + }, + "comments": { + }, + "description": "This operation creates an address for a job. Addresses are validated at the time of creation. The address you provide must be located within the serviceable area of your region. If the address is invalid or unsupported, then an exception is thrown.", + "id": "to-create-an-address-for-a-job-1482535416294", + "title": "To create an address for a job" + } + ], + "CreateCluster": [ + { + "input": { + "AddressId": "ADID1234ab12-3eec-4eb3-9be6-9374c10eb51b", + "Description": "MyCluster", + "JobType": "LOCAL_USE", + "KmsKeyARN": "arn:aws:kms:us-east-1:123456789012:key/abcd1234-12ab-34cd-56ef-123456123456", + "Notification": { + "JobStatesToNotify": [ + + ], + "NotifyAll": false + }, + "Resources": { + "S3Resources": [ + { + "BucketArn": "arn:aws:s3:::MyBucket", + "KeyRange": { + } + } + ] + }, + "RoleARN": "arn:aws:iam::123456789012:role/snowball-import-S3-role", + "ShippingOption": "SECOND_DAY", + "SnowballType": "EDGE" + }, + "output": { + "ClusterId": "CID123e4567-e89b-12d3-a456-426655440000" + }, + "comments": { + }, + "description": "Creates an empty cluster. Each cluster supports five nodes. You use the CreateJob action separately to create the jobs for each of these nodes. The cluster does not ship until these five node jobs have been created.", + "id": "to-create-a-cluster-1482864724077", + "title": "To create a cluster" + } + ], + "CreateJob": [ + { + "input": { + "AddressId": "ADID1234ab12-3eec-4eb3-9be6-9374c10eb51b", + "Description": "My Job", + "JobType": "IMPORT", + "KmsKeyARN": "arn:aws:kms:us-east-1:123456789012:key/abcd1234-12ab-34cd-56ef-123456123456", + "Notification": { + "JobStatesToNotify": [ + + ], + "NotifyAll": false + }, + "Resources": { + "S3Resources": [ + { + "BucketArn": "arn:aws:s3:::MyBucket", + "KeyRange": { + } + } + ] + }, + "RoleARN": "arn:aws:iam::123456789012:role/snowball-import-S3-role", + "ShippingOption": "SECOND_DAY", + "SnowballCapacityPreference": "T80", + "SnowballType": "STANDARD" + }, + "output": { + "JobId": "JID123e4567-e89b-12d3-a456-426655440000" + }, + "comments": { + }, + "description": "Creates a job to import or export data between Amazon S3 and your on-premises data center. Your AWS account must have the right trust policies and permissions in place to create a job for Snowball. If you're creating a job for a node in a cluster, you only need to provide the clusterId value; the other job attributes are inherited from the cluster.", + "id": "to-create-a-job-1482864834886", + "title": "To create a job" + } + ], + "DescribeAddress": [ + { + "input": { + "AddressId": "ADID1234ab12-3eec-4eb3-9be6-9374c10eb51b" + }, + "output": { + "Address": { + "AddressId": "ADID5643ec50-3eec-4eb3-9be6-9374c10eb51b", + "City": "Seattle", + "Company": "My Company", + "Country": "US", + "Name": "My Name", + "PhoneNumber": "425-555-5555", + "PostalCode": "98101", + "StateOrProvince": "WA", + "Street1": "123 Main Street" + } + }, + "comments": { + }, + "description": "This operation describes an address for a job.", + "id": "to-describe-an-address-for-a-job-1482538608745", + "title": "To describe an address for a job" + } + ], + "DescribeAddresses": [ + { + "input": { + }, + "output": { + "Addresses": [ + { + "AddressId": "ADID1234ab12-3eec-4eb3-9be6-9374c10eb51b", + "City": "Seattle", + "Company": "My Company", + "Country": "US", + "Name": "My Name", + "PhoneNumber": "425-555-5555", + "PostalCode": "98101", + "StateOrProvince": "WA", + "Street1": "123 Main Street" + } + ] + }, + "comments": { + }, + "description": "This operation describes all the addresses that you've created for AWS Snowball. Calling this API in one of the US regions will return addresses from the list of all addresses associated with this account in all US regions.", + "id": "to-describe-all-the-addresses-youve-created-for-aws-snowball-1482538936603", + "title": "To describe all the addresses you've created for AWS Snowball" + } + ], + "DescribeCluster": [ + { + "input": { + "ClusterId": "CID123e4567-e89b-12d3-a456-426655440000" + }, + "output": { + "ClusterMetadata": { + "AddressId": "ADID1234ab12-3eec-4eb3-9be6-9374c10eb51b", + "ClusterId": "CID123e4567-e89b-12d3-a456-426655440000", + "ClusterState": "Pending", + "CreationDate": "1480475517.0", + "Description": "MyCluster", + "JobType": "LOCAL_USE", + "KmsKeyARN": "arn:aws:kms:us-east-1:123456789012:key/abcd1234-12ab-34cd-56ef-123456123456", + "Notification": { + "JobStatesToNotify": [ + + ], + "NotifyAll": false + }, + "Resources": { + "S3Resources": [ + { + "BucketArn": "arn:aws:s3:::MyBucket", + "KeyRange": { + } + } + ] + }, + "RoleARN": "arn:aws:iam::123456789012:role/snowball-import-S3-role", + "ShippingOption": "SECOND_DAY" + } + }, + "comments": { + }, + "description": "Returns information about a specific cluster including shipping information, cluster status, and other important metadata.", + "id": "to-describe-a-cluster-1482864218396", + "title": "To describe a cluster" + } + ], + "DescribeJob": [ + { + "input": { + "JobId": "JID123e4567-e89b-12d3-a456-426655440000" + }, + "output": { + "JobMetadata": { + "AddressId": "ADID1234ab12-3eec-4eb3-9be6-9374c10eb51b", + "CreationDate": "1475626164", + "Description": "My Job", + "JobId": "JID123e4567-e89b-12d3-a456-426655440000", + "JobState": "New", + "JobType": "IMPORT", + "KmsKeyARN": "arn:aws:kms:us-east-1:123456789012:key/abcd1234-12ab-34cd-56ef-123456123456", + "Notification": { + "JobStatesToNotify": [ + + ], + "NotifyAll": false + }, + "Resources": { + "S3Resources": [ + { + "BucketArn": "arn:aws:s3:::MyBucket", + "KeyRange": { + } + } + ] + }, + "RoleARN": "arn:aws:iam::123456789012:role/snowball-import-S3-role", + "ShippingDetails": { + "ShippingOption": "SECOND_DAY" + }, + "SnowballCapacityPreference": "T80", + "SnowballType": "STANDARD" + } + }, + "comments": { + }, + "description": "This operation describes a job you've created for AWS Snowball.", + "id": "to-describe-a-job-youve-created-for-aws-snowball-1482539500180", + "title": "To describe a job you've created for AWS Snowball" + } + ], + "GetJobManifest": [ + { + "input": { + "JobId": "JID123e4567-e89b-12d3-a456-426655440000" + }, + "output": { + "ManifestURI": "https://awsie-frosty-manifests-prod.s3.amazonaws.com/JID123e4567-e89b-12d3-a456-426655440000_manifest.bin?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20161224T005115Z&X-Amz-SignedHeaders=..." + }, + "comments": { + }, + "description": "Returns a link to an Amazon S3 presigned URL for the manifest file associated with the specified JobId value. You can access the manifest file for up to 60 minutes after this request has been made. To access the manifest file after 60 minutes have passed, you'll have to make another call to the GetJobManifest action.\n\nThe manifest is an encrypted file that you can download after your job enters the WithCustomer status. The manifest is decrypted by using the UnlockCode code value, when you pass both values to the Snowball through the Snowball client when the client is started for the first time.\n\nAs a best practice, we recommend that you don't save a copy of an UnlockCode value in the same location as the manifest file for that job. Saving these separately helps prevent unauthorized parties from gaining access to the Snowball associated with that job.\n\nThe credentials of a given job, including its manifest file and unlock code, expire 90 days after the job is created.", + "id": "to-get-the-manifest-for-a-job-youve-created-for-aws-snowball-1482540389246", + "title": "To get the manifest for a job you've created for AWS Snowball" + } + ], + "GetJobUnlockCode": [ + { + "input": { + "JobId": "JID123e4567-e89b-12d3-a456-426655440000" + }, + "output": { + "UnlockCode": "12345-abcde-56789-fghij-01234" + }, + "comments": { + }, + "description": "Returns the UnlockCode code value for the specified job. A particular UnlockCode value can be accessed for up to 90 days after the associated job has been created.\n\nThe UnlockCode value is a 29-character code with 25 alphanumeric characters and 4 hyphens. This code is used to decrypt the manifest file when it is passed along with the manifest to the Snowball through the Snowball client when the client is started for the first time.\n\nAs a best practice, we recommend that you don't save a copy of the UnlockCode in the same location as the manifest file for that job. Saving these separately helps prevent unauthorized parties from gaining access to the Snowball associated with that job.", + "id": "to-get-the-unlock-code-for-a-job-youve-created-for-aws-snowball-1482541987286", + "title": "To get the unlock code for a job you've created for AWS Snowball" + } + ], + "GetSnowballUsage": [ + { + "input": { + }, + "output": { + "SnowballLimit": 1, + "SnowballsInUse": 0 + }, + "comments": { + }, + "description": "Returns information about the Snowball service limit for your account, and also the number of Snowballs your account has in use.\n\nThe default service limit for the number of Snowballs that you can have at one time is 1. If you want to increase your service limit, contact AWS Support.", + "id": "to-see-your-snowball-service-limit-and-the-number-of-snowballs-you-have-in-use-1482863394588", + "title": "To see your Snowball service limit and the number of Snowballs you have in use" + } + ], + "ListClusterJobs": [ + { + "input": { + "ClusterId": "CID123e4567-e89b-12d3-a456-426655440000" + }, + "output": { + "JobListEntries": [ + { + "CreationDate": "1480475524.0", + "Description": "MyClustrer-node-001", + "IsMaster": false, + "JobId": "JID123e4567-e89b-12d3-a456-426655440000", + "JobState": "New", + "JobType": "LOCAL_USE", + "SnowballType": "EDGE" + }, + { + "CreationDate": "1480475525.0", + "Description": "MyClustrer-node-002", + "IsMaster": false, + "JobId": "JID123e4567-e89b-12d3-a456-426655440001", + "JobState": "New", + "JobType": "LOCAL_USE", + "SnowballType": "EDGE" + }, + { + "CreationDate": "1480475525.0", + "Description": "MyClustrer-node-003", + "IsMaster": false, + "JobId": "JID123e4567-e89b-12d3-a456-426655440002", + "JobState": "New", + "JobType": "LOCAL_USE", + "SnowballType": "EDGE" + }, + { + "CreationDate": "1480475525.0", + "Description": "MyClustrer-node-004", + "IsMaster": false, + "JobId": "JID123e4567-e89b-12d3-a456-426655440003", + "JobState": "New", + "JobType": "LOCAL_USE", + "SnowballType": "EDGE" + }, + { + "CreationDate": "1480475525.0", + "Description": "MyClustrer-node-005", + "IsMaster": false, + "JobId": "JID123e4567-e89b-12d3-a456-426655440004", + "JobState": "New", + "JobType": "LOCAL_USE", + "SnowballType": "EDGE" + } + ] + }, + "comments": { + }, + "description": "Returns an array of JobListEntry objects of the specified length. Each JobListEntry object is for a job in the specified cluster and contains a job's state, a job's ID, and other information.", + "id": "to-get-a-list-of-jobs-in-a-cluster-that-youve-created-for-aws-snowball-1482863105773", + "title": "To get a list of jobs in a cluster that you've created for AWS Snowball" + } + ], + "ListClusters": [ + { + "input": { + }, + "output": { + "ClusterListEntries": [ + { + "ClusterId": "CID123e4567-e89b-12d3-a456-426655440000", + "ClusterState": "Pending", + "CreationDate": "1480475517.0", + "Description": "MyCluster" + } + ] + }, + "comments": { + }, + "description": "Returns an array of ClusterListEntry objects of the specified length. Each ClusterListEntry object contains a cluster's state, a cluster's ID, and other important status information.", + "id": "to-get-a-list-of-clusters-that-youve-created-for-aws-snowball-1482862223003", + "title": "To get a list of clusters that you've created for AWS Snowball" + } + ], + "ListJobs": [ + { + "input": { + }, + "output": { + "JobListEntries": [ + { + "CreationDate": "1460678186.0", + "Description": "MyJob", + "IsMaster": false, + "JobId": "JID123e4567-e89b-12d3-a456-426655440000", + "JobState": "New", + "JobType": "IMPORT", + "SnowballType": "STANDARD" + } + ] + }, + "comments": { + }, + "description": "Returns an array of JobListEntry objects of the specified length. Each JobListEntry object contains a job's state, a job's ID, and a value that indicates whether the job is a job part, in the case of export jobs. Calling this API action in one of the US regions will return jobs from the list of all jobs associated with this account in all US regions.", + "id": "to-get-a-list-of-jobs-that-youve-created-for-aws-snowball-1482542167627", + "title": "To get a list of jobs that you've created for AWS Snowball" + } + ], + "UpdateCluster": [ + { + "input": { + "AddressId": "ADID1234ab12-3eec-4eb3-9be6-9374c10eb51b", + "ClusterId": "CID123e4567-e89b-12d3-a456-426655440000", + "Description": "updated-cluster-name" + }, + "comments": { + }, + "description": "This action allows you to update certain parameters for a cluster. Once the cluster changes to a different state, usually within 60 minutes of it being created, this action is no longer available.", + "id": "to-update-a-cluster-1482863900595", + "title": "To update a cluster" + } + ], + "UpdateJob": [ + { + "input": { + "AddressId": "ADID1234ab12-3eec-4eb3-9be6-9374c10eb51b", + "Description": "updated-job-name", + "JobId": "JID123e4567-e89b-12d3-a456-426655440000", + "ShippingOption": "NEXT_DAY", + "SnowballCapacityPreference": "T100" + }, + "comments": { + }, + "description": "This action allows you to update certain parameters for a job. Once the job changes to a different job state, usually within 60 minutes of the job being created, this action is no longer available.", + "id": "to-update-a-job-1482863556886", + "title": "To update a job" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/snowball/2016-06-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/snowball/2016-06-30/paginators-1.json new file mode 100644 index 0000000..05a7ea8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/snowball/2016-06-30/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListJobs": { + "limit_key": "MaxResults", + "output_token": "NextToken", + "input_token": "NextToken", + "result_key": "JobListEntries" + }, + "DescribeAddresses": { + "limit_key": "MaxResults", + "output_token": "NextToken", + "input_token": "NextToken", + "result_key": "Addresses" + }, + "ListClusterJobs": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "JobListEntries" + }, + "ListClusters": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ClusterListEntries" + }, + "ListCompatibleImages": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CompatibleImages" + }, + "ListLongTermPricing": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "LongTermPricingEntries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/snowball/2016-06-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/snowball/2016-06-30/service-2.json.gz new file mode 100644 index 0000000..f1db707 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/snowball/2016-06-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sns/2010-03-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sns/2010-03-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..03151b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sns/2010-03-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sns/2010-03-31/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/sns/2010-03-31/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sns/2010-03-31/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sns/2010-03-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sns/2010-03-31/paginators-1.json new file mode 100644 index 0000000..5be5250 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sns/2010-03-31/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "ListEndpointsByPlatformApplication": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Endpoints" + }, + "ListPlatformApplications": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "PlatformApplications" + }, + "ListSubscriptions": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Subscriptions" + }, + "ListSubscriptionsByTopic": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Subscriptions" + }, + "ListTopics": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Topics" + }, + "ListPhoneNumbersOptedOut": { + "input_token": "nextToken", + "output_token": "nextToken", + "result_key": "phoneNumbers" + }, + "ListOriginationNumbers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PhoneNumbers" + }, + "ListSMSSandboxPhoneNumbers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PhoneNumbers" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sns/2010-03-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sns/2010-03-31/service-2.json.gz new file mode 100644 index 0000000..6694632 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sns/2010-03-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/socialmessaging/2024-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/socialmessaging/2024-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..66eb32d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/socialmessaging/2024-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/socialmessaging/2024-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/socialmessaging/2024-01-01/paginators-1.json new file mode 100644 index 0000000..9313a60 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/socialmessaging/2024-01-01/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListLinkedWhatsAppBusinessAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "linkedAccounts" + }, + "ListWhatsAppMessageTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "templates" + }, + "ListWhatsAppTemplateLibrary": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "metaLibraryTemplates" + }, + "ListWhatsAppFlowAssets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "flowAssets" + }, + "ListWhatsAppFlows": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "flows" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/socialmessaging/2024-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/socialmessaging/2024-01-01/service-2.json.gz new file mode 100644 index 0000000..4988705 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/socialmessaging/2024-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sqs/2012-11-05/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sqs/2012-11-05/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..58ee562 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sqs/2012-11-05/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sqs/2012-11-05/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/sqs/2012-11-05/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sqs/2012-11-05/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sqs/2012-11-05/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sqs/2012-11-05/paginators-1.json new file mode 100644 index 0000000..7c22d43 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sqs/2012-11-05/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListDeadLetterSourceQueues": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "queueUrls" + }, + "ListQueues": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "QueueUrls" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sqs/2012-11-05/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sqs/2012-11-05/service-2.json.gz new file mode 100644 index 0000000..8d2eddb Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sqs/2012-11-05/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-contacts/2021-05-03/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ssm-contacts/2021-05-03/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1ff4de7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ssm-contacts/2021-05-03/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-contacts/2021-05-03/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ssm-contacts/2021-05-03/examples-1.json new file mode 100644 index 0000000..d7c714d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ssm-contacts/2021-05-03/examples-1.json @@ -0,0 +1,714 @@ +{ + "version": "1.0", + "examples": { + "AcceptPage": [ + { + "input": { + "AcceptCode": "425440", + "AcceptType": "READ", + "PageId": "arn:aws:ssm-contacts:us-east-2:682428703967:page/akuam/94ea0c7b-56d9-46c3-b84a-a37c8b067ad3" + }, + "output": { + }, + "comments": { + }, + "description": "The following accept-page operation uses an accept code sent to the contact channel to accept a page.", + "id": "to-accept-a-page-during-and-engagement-1630357840187", + "title": "To accept a page during and engagement" + } + ], + "ActivateContactChannel": [ + { + "input": { + "ActivationCode": "466136", + "ContactChannelId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/fc7405c4-46b2-48b7-87b2-93e2f225b90d" + }, + "output": { + }, + "comments": { + }, + "description": "The following activate-contact-channel example activates a contact channel and makes it usable as part of an incident.", + "id": "activate-a-contacts-contact-channel-1630359780075", + "title": "Activate a contact's contact channel" + } + ], + "CreateContact": [ + { + "input": { + "Alias": "akuam", + "DisplayName": "Akua Mansa", + "Plan": { + "Stages": [ + + ] + }, + "Type": "PERSONAL" + }, + "output": { + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam" + }, + "comments": { + }, + "description": "The following create-contact example creates a contact in your environment with a blank plan. The plan can be updated after creating contact channels. Use the create-contact-channel operation with the output ARN of this command. After you have created contact channels for this contact use update-contact to update the plan.", + "id": "to-create-a-contact-1630360152750", + "title": "To create a contact" + } + ], + "CreateContactChannel": [ + { + "input": { + "ContactId": "arn:aws:ssm-contacts:us-east-1:111122223333:contact/akuam", + "DeliveryAddress": { + "SimpleAddress": "+15005550199" + }, + "Name": "akuas sms-test", + "Type": "SMS" + }, + "output": { + "ContactChannelArn": "arn:aws:ssm-contacts:us-east-1:111122223333:contact-channel/akuam/02f506b9-ea5d-4764-af89-2daa793ff024" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a contact channel of type SMS for the contact Akua Mansa. Contact channels can be created of type SMS, EMAIL, or VOICE.", + "id": "to-create-a-contact-channel-1630360447010", + "title": "To create a contact channel" + } + ], + "DeactivateContactChannel": [ + { + "input": { + "ContactChannelId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/fc7405c4-46b2-48b7-87b2-93e2f225b90d" + }, + "output": { + }, + "comments": { + }, + "description": "The following ``deactivate-contact-channel`` example deactivates a contact channel. Deactivating a contact channel means the contact channel will no longer be paged during an incident. You can also reactivate a contact channel at any time using the activate-contact-channel operation.", + "id": "to-deactivate-a-contact-channel-1630360853894", + "title": "To deactivate a contact channel" + } + ], + "DeleteContact": [ + { + "input": { + "ContactId": "arn:aws:ssm-contacts:us-east-1:111122223333:contact/alejr" + }, + "output": { + }, + "comments": { + }, + "description": "The following delete-contact example deletes a contact. The contact will no longer be reachable from any escalation plan that refers to them.", + "id": "to-delete-a-contact-1630361093863", + "title": "To delete a contact" + } + ], + "DeleteContactChannel": [ + { + "input": { + "ContactChannelId": "arn:aws:ssm-contacts:us-east-1:111122223333:contact-channel/akuam/13149bad-52ee-45ea-ae1e-45857f78f9b2" + }, + "output": { + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following delete-contact-channel example deletes a contact channel. Deleting a contact channel ensures the contact channel will not be paged during an incident.", + "id": "to-delete-a-contact-channel-1630364616682", + "title": "To delete a contact channel" + } + ], + "DescribeEngagement": [ + { + "input": { + "EngagementId": "arn:aws:ssm-contacts:us-east-2:111122223333:engagement/example_escalation/69e40ce1-8dbb-4d57-8962-5fbe7fc53356" + }, + "output": { + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/example_escalation", + "Content": "Testing engagements", + "EngagementArn": "arn:aws:ssm-contacts:us-east-2:111122223333:engagement/example_escalation/69e40ce1-8dbb-4d57-8962-5fbe7fc53356", + "PublicContent": "Testing engagements", + "PublicSubject": "test", + "Sender": "tester", + "StartTime": "2021-05-18T18:25:41.151000+00:00", + "Subject": "test" + }, + "comments": { + }, + "description": "The following describe-engagement example lists the details of an engagement to a contact or escalation plan. The subject and content are sent to the contact channels.", + "id": "to-describe-the-details-of-an-engagement-1630364719475", + "title": "To describe the details of an engagement" + } + ], + "DescribePage": [ + { + "input": { + "PageId": "arn:aws:ssm-contacts:us-east-2:111122223333:page/akuam/ad0052bd-e606-498a-861b-25726292eb93" + }, + "output": { + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam", + "Content": "Testing engagements", + "DeliveryTime": "2021-05-18T18:43:55.265000+00:00", + "EngagementArn": "arn:aws:ssm-contacts:us-east-2:111122223333:engagement/akuam/78a29753-3674-4ac5-9f83-0468563567f0", + "PageArn": "arn:aws:ssm-contacts:us-east-2:111122223333:page/akuam/ad0052bd-e606-498a-861b-25726292eb93", + "PublicContent": "Testing engagements", + "PublicSubject": "test", + "ReadTime": "2021-05-18T18:43:55.708000+00:00", + "Sender": "tester", + "SentTime": "2021-05-18T18:43:29.301000+00:00", + "Subject": "test" + }, + "comments": { + }, + "description": "The following describe-page example lists details of a page to a contact channel. The page will include the subject and content provided.", + "id": "to-list-the-details-of-a-page-to-a-contact-channel-1630364907282", + "title": "To list the details of a page to a contact channel" + } + ], + "GetContact": [ + { + "input": { + "ContactId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam" + }, + "output": { + "Alias": "akuam", + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam", + "DisplayName": "Akua Mansa", + "Plan": { + "Stages": [ + { + "DurationInMinutes": 5, + "Targets": [ + { + "ChannelTargetInfo": { + "ContactChannelId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/beb25840-5ac8-4644-95cc-7a8de390fa65", + "RetryIntervalInMinutes": 1 + } + } + ] + }, + { + "DurationInMinutes": 5, + "Targets": [ + { + "ChannelTargetInfo": { + "ContactChannelId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/49f3c24d-5f9f-4638-ae25-3f49e04229ad", + "RetryIntervalInMinutes": 1 + } + } + ] + }, + { + "DurationInMinutes": 5, + "Targets": [ + { + "ChannelTargetInfo": { + "ContactChannelId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/77d4f447-f619-4954-afff-85551e369c2a", + "RetryIntervalInMinutes": 1 + } + } + ] + } + ] + }, + "Type": "PERSONAL" + }, + "comments": { + }, + "description": "The following get-contact example describes a contact.", + "id": "example-1-to-describe-a-contact-plan-1630365360005", + "title": "Example 1: To describe a contact plan" + }, + { + "input": { + "ContactId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/example_escalation" + }, + "output": { + "Alias": "example_escalation", + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/example_escalation", + "DisplayName": "Example Escalation Plan", + "Plan": { + "Stages": [ + { + "DurationInMinutes": 5, + "Targets": [ + { + "ContactTargetInfo": { + "ContactId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam", + "IsEssential": true + } + } + ] + }, + { + "DurationInMinutes": 5, + "Targets": [ + { + "ContactTargetInfo": { + "ContactId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/alejr", + "IsEssential": false + } + } + ] + }, + { + "DurationInMinutes": 0, + "Targets": [ + { + "ContactTargetInfo": { + "ContactId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/anasi", + "IsEssential": false + } + } + ] + } + ] + }, + "Type": "ESCALATION" + }, + "comments": { + }, + "description": "The following get-contact example describes an escalation plan.", + "id": "example-2-to-describe-an-escalation-plan-1630365515731", + "title": "Example 2: To describe an escalation plan" + } + ], + "GetContactChannel": [ + { + "input": { + "ContactChannelId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/fc7405c4-46b2-48b7-87b2-93e2f225b90d" + }, + "output": { + "ActivationStatus": "ACTIVATED", + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam", + "ContactChannelArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/fc7405c4-46b2-48b7-87b2-93e2f225b90d", + "DeliveryAddress": { + "SimpleAddress": "+15005550199" + }, + "Name": "akuas sms", + "Type": "SMS" + }, + "comments": { + }, + "description": "The following get-contact-channel example lists the details of a contact channel.", + "id": "to-list-the-details-of-a-contact-channel-1630365682730", + "title": "To list the details of a contact channel" + } + ], + "GetContactPolicy": [ + { + "input": { + "ContactArn": "arn:aws:ssm-contacts:us-east-1:111122223333:contact/akuam" + }, + "output": { + "ContactArn": "arn:aws:ssm-contacts:us-east-1:111122223333:contact/akuam", + "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"SharePolicyForDocumentationDralia\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"222233334444\"},\"Action\":[\"ssm-contacts:GetContact\",\"ssm-contacts:StartEngagement\",\"ssm-contacts:DescribeEngagement\",\"ssm-contacts:ListPagesByEngagement\",\"ssm-contacts:StopEngagement\"],\"Resource\":[\"arn:aws:ssm-contacts:*:111122223333:contact/akuam\",\"arn:aws:ssm-contacts:*:111122223333:engagement/akuam/*\"]}]}" + }, + "comments": { + }, + "description": "The following get-contact-policy example lists the resource policies associated with the specified contact.", + "id": "to-list-the-details-of-a-contact-channel-1630365682730", + "title": "To list the resource policies of a contact" + } + ], + "ListContactChannels": [ + { + "input": { + "ContactId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam" + }, + "output": { + "ContactChannels": [ + { + "ActivationStatus": "ACTIVATED", + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam", + "ContactChannelArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/fc7405c4-46b2-48b7-87b2-93e2f225b90d", + "DeliveryAddress": { + "SimpleAddress": "+15005550100" + }, + "Name": "akuas sms", + "Type": "SMS" + } + ] + }, + "comments": { + }, + "description": "The following list-contact-channels example lists the available contact channels of the specified contact.", + "id": "to-list-the-contact-channels-of-a-contact-1630366544252", + "title": "To list the contact channels of a contact" + } + ], + "ListContacts": [ + { + "input": { + }, + "output": { + "Contacts": [ + { + "Alias": "akuam", + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam", + "DisplayName": "Akua Mansa", + "Type": "PERSONAL" + }, + { + "Alias": "alejr", + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/alejr", + "DisplayName": "Alejandro Rosalez", + "Type": "PERSONAL" + }, + { + "Alias": "anasi", + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/anasi", + "DisplayName": "Ana Carolina Silva", + "Type": "PERSONAL" + }, + { + "Alias": "example_escalation", + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/example_escalation", + "DisplayName": "Example Escalation", + "Type": "ESCALATION" + } + ] + }, + "comments": { + }, + "description": "The following list-contacts example lists the contacts and escalation plans in your account.", + "id": "to-list-all-escalation-plans-and-contacts-1630367103082", + "title": "To list all escalation plans and contacts" + } + ], + "ListEngagements": [ + { + "input": { + }, + "output": { + "Engagements": [ + { + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam", + "EngagementArn": "arn:aws:ssm-contacts:us-east-2:111122223333:engagement/akuam/91792571-0b53-4821-9f73-d25d13d9e529", + "Sender": "cli", + "StartTime": "2021-05-18T20:37:50.300000+00:00" + }, + { + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam", + "EngagementArn": "arn:aws:ssm-contacts:us-east-2:111122223333:engagement/akuam/78a29753-3674-4ac5-9f83-0468563567f0", + "Sender": "cli", + "StartTime": "2021-05-18T18:40:26.666000+00:00" + }, + { + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/example_escalation", + "EngagementArn": "arn:aws:ssm-contacts:us-east-2:111122223333:engagement/example_escalation/69e40ce1-8dbb-4d57-8962-5fbe7fc53356", + "Sender": "cli", + "StartTime": "2021-05-18T18:25:41.151000+00:00" + }, + { + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam", + "EngagementArn": "arn:aws:ssm-contacts:us-east-2:111122223333:engagement/akuam/607ced0e-e8fa-4ea7-8958-a237b8803f8f", + "Sender": "cli", + "StartTime": "2021-05-18T18:20:58.093000+00:00" + } + ] + }, + "comments": { + }, + "description": "The following list-engagements example lists engagements to escalation plans and contacts. You can also list engagements for a single incident.", + "id": "to-list-all-engagements-1630367432635", + "title": "To list all engagements" + } + ], + "ListPageReceipts": [ + { + "input": { + "PageId": "arn:aws:ssm-contacts:us-east-2:111122223333:page/akuam/94ea0c7b-56d9-46c3-b84a-a37c8b067ad3" + }, + "output": { + "Receipts": [ + { + "ContactChannelArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/fc7405c4-46b2-48b7-87b2-93e2f225b90d", + "ReceiptInfo": "425440", + "ReceiptTime": "2021-05-18T20:42:57.485000+00:00", + "ReceiptType": "DELIVERED" + }, + { + "ContactChannelArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/fc7405c4-46b2-48b7-87b2-93e2f225b90d", + "ReceiptInfo": "425440", + "ReceiptTime": "2021-05-18T20:42:57.907000+00:00", + "ReceiptType": "READ" + }, + { + "ContactChannelArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/fc7405c4-46b2-48b7-87b2-93e2f225b90d", + "ReceiptInfo": "SM6656c19132f1465f9c9c1123a5dde7c9", + "ReceiptTime": "2021-05-18T20:40:52.962000+00:00", + "ReceiptType": "SENT" + } + ] + }, + "comments": { + }, + "description": "The following command-name example lists whether a page was received or not by a contact.", + "id": "to-list-page-receipts-1630367706869", + "title": "To list page receipts" + } + ], + "ListPagesByContact": [ + { + "input": { + "ContactId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam" + }, + "output": { + "Pages": [ + { + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam", + "DeliveryTime": "2021-05-18T18:43:55.265000+00:00", + "EngagementArn": "arn:aws:ssm-contacts:us-east-2:111122223333:engagement/akuam/78a29753-3674-4ac5-9f83-0468563567f0", + "PageArn": "arn:aws:ssm-contacts:us-east-2:111122223333:page/akuam/ad0052bd-e606-498a-861b-25726292eb93", + "ReadTime": "2021-05-18T18:43:55.708000+00:00", + "Sender": "cli", + "SentTime": "2021-05-18T18:43:29.301000+00:00" + } + ] + }, + "comments": { + }, + "description": "The following list-pages-by-contact example lists all pages to the specified contact.", + "id": "to-list-pages-by-contact-1630435789132", + "title": "To list pages by contact" + } + ], + "ListPagesByEngagement": [ + { + "input": { + "EngagementId": "arn:aws:ssm-contacts:us-east-2:111122223333:engagement/akuam/78a29753-3674-4ac5-9f83-0468563567f0" + }, + "output": { + "Pages": [ + { + "ContactArn": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam", + "EngagementArn": "arn:aws:ssm-contacts:us-east-2:111122223333:engagement/akuam/78a29753-3674-4ac5-9f83-0468563567f0", + "PageArn": "arn:aws:ssm-contacts:us-east-2:111122223333:page/akuam/ad0052bd-e606-498a-861b-25726292eb93", + "Sender": "cli", + "SentTime": "2021-05-18T18:40:27.245000+00:00" + } + ] + }, + "comments": { + }, + "description": "The following list-pages-by-engagement example lists the pages that occurred while engaging the defined engagement plan.", + "id": "to-list-pages-to-contact-channels-started-from-an-engagement-1630435864674", + "title": "To list pages to contact channels started from an engagement." + } + ], + "ListTagsForResource": [ + { + "input": { + "ResourceARN": "arn:aws:ssm-contacts:us-east-1:111122223333:contact/akuam" + }, + "output": { + "Tags": [ + { + "Key": "group1", + "Value": "1" + } + ] + }, + "comments": { + }, + "description": "The following list-tags-for-resource example lists the tags of the specified contact.", + "id": "to-list-tags-for-a-contact-1630436051681", + "title": "To list tags for a contact" + } + ], + "PutContactPolicy": [ + { + "input": { + "ContactArn": "arn:aws:ssm-contacts:us-east-1:111122223333:contact/akuam", + "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"ExampleResourcePolicy\",\"Action\":[\"ssm-contacts:GetContact\",\"ssm-contacts:StartEngagement\",\"ssm-contacts:DescribeEngagement\",\"ssm-contacts:ListPagesByEngagement\",\"ssm-contacts:StopEngagement\"],\"Principal\":{\"AWS\":\"222233334444\"},\"Effect\":\"Allow\",\"Resource\":[\"arn:aws:ssm-contacts:*:111122223333:contact/akuam\",\"arn:aws:ssm-contacts:*:111122223333:engagement/akuam/*\"]}]}" + }, + "output": { + }, + "comments": { + }, + "description": "The following put-contact-policy example adds a resource policy to the contact Akua that shares the contact and related engagements with the principal.", + "id": "to-share-a-contact-and-engagements-1630436278898", + "title": "To share a contact and engagements" + } + ], + "SendActivationCode": [ + { + "input": { + "ContactChannelId": "arn:aws:ssm-contacts:us-east-1:111122223333:contact-channel/akuam/8ddae2d1-12c8-4e45-b852-c8587266c400" + }, + "output": { + }, + "comments": { + }, + "description": "The following send-activation-code example sends an activation code and message to the specified contact channel.", + "id": "to-send-an-activation-code-1630436453574", + "title": "To send an activation code" + } + ], + "StartEngagement": [ + { + "input": { + "ContactId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam", + "Content": "Testing engagements", + "PublicContent": "Testing engagements", + "PublicSubject": "test", + "Sender": "tester", + "Subject": "test" + }, + "output": { + "EngagementArn": "arn:aws:ssm-contacts:us-east-2:111122223333:engagement/akuam/607ced0e-e8fa-4ea7-8958-a237b8803f8f" + }, + "comments": { + }, + "description": "The following start-engagement pages contact's contact channels. Sender, subject, public-subject, and public-content are all free from fields. Incident Manager sends the subject and content to the provided VOICE or EMAIL contact channels. Incident Manager sends the public-subject and public-content to the provided SMS contact channels. Sender is used to track who started the engagement.", + "id": "example-1-to-page-a-contacts-contact-channels-1630436634872", + "title": "Example 1: To page a contact's contact channels" + }, + { + "input": { + "ContactId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/example_escalation", + "Content": "Testing engagements", + "PublicContent": "Testing engagements", + "PublicSubject": "test", + "Sender": "tester", + "Subject": "test" + }, + "output": { + "EngagementArn": "arn:aws:ssm-contacts:us-east-2:111122223333:engagement/example_escalation/69e40ce1-8dbb-4d57-8962-5fbe7fc53356" + }, + "comments": { + }, + "description": "The following start-engagement engages contact's through an escalation plan. Each contact is paged according to their engagement plan.", + "id": "example-2-to-page-a-contact-in-the-provided-escalation-plan-1630436808480", + "title": "Example 2: To page a contact in the provided escalation plan." + } + ], + "StopEngagement": [ + { + "input": { + "EngagementId": "arn:aws:ssm-contacts:us-east-2:111122223333:engagement/example_escalation/69e40ce1-8dbb-4d57-8962-5fbe7fc53356" + }, + "output": { + }, + "comments": { + }, + "description": "The following stop-engagement example stops an engagement from paging further contacts and contact channels.", + "id": "to-stop-an-engagement-1630436882864", + "title": "To stop an engagement" + } + ], + "TagResource": [ + { + "input": { + "ResourceARN": "arn:aws:ssm-contacts:us-east-1:111122223333:contact/akuam", + "Tags": [ + { + "Key": "group1", + "Value": "1" + } + ] + }, + "output": { + }, + "comments": { + }, + "description": "The following tag-resource example tags a specified contact with the provided tag key value pair.", + "id": "to-tag-a-contact-1630437124572", + "title": "To tag a contact" + } + ], + "UntagResource": [ + { + "input": { + "ResourceARN": "arn:aws:ssm-contacts:us-east-1:111122223333:contact/akuam", + "TagKeys": [ + "group1" + ] + }, + "output": { + }, + "comments": { + }, + "description": "The following untag-resource example removes the group1 tag from the specified contact.", + "id": "to-remove-tags-from-a-contact-1630437251110", + "title": "To remove tags from a contact" + } + ], + "UpdateContact": [ + { + "input": { + "ContactId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact/akuam", + "Plan": { + "Stages": [ + { + "DurationInMinutes": 5, + "Targets": [ + { + "ChannelTargetInfo": { + "ContactChannelId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/beb25840-5ac8-4644-95cc-7a8de390fa65", + "RetryIntervalInMinutes": 1 + } + } + ] + }, + { + "DurationInMinutes": 5, + "Targets": [ + { + "ChannelTargetInfo": { + "ContactChannelId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/49f3c24d-5f9f-4638-ae25-3f49e04229ad", + "RetryIntervalInMinutes": 1 + } + } + ] + }, + { + "DurationInMinutes": 5, + "Targets": [ + { + "ChannelTargetInfo": { + "ContactChannelId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/77d4f447-f619-4954-afff-85551e369c2a", + "RetryIntervalInMinutes": 1 + } + } + ] + } + ] + } + }, + "output": { + }, + "comments": { + }, + "description": "The following update-contact example updates the engagement plan of the contact Akua to include the three types of contacts channels. This is done after creating contact channels for Akua.", + "id": "to-update-the-engagement-plan-of-contact-1630437436599", + "title": "To update the engagement plan of contact" + } + ], + "UpdateContactChannel": [ + { + "input": { + "ContactChannelId": "arn:aws:ssm-contacts:us-east-2:111122223333:contact-channel/akuam/49f3c24d-5f9f-4638-ae25-3f49e04229ad", + "DeliveryAddress": { + "SimpleAddress": "+15005550198" + }, + "Name": "akuas voice channel" + }, + "output": { + }, + "comments": { + }, + "description": "The following update-contact-channel example updates the name and delivery address of a contact channel.", + "id": "to-update-a-contact-channel-1630437610256", + "title": "To update a contact channel" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-contacts/2021-05-03/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ssm-contacts/2021-05-03/paginators-1.json new file mode 100644 index 0000000..621bde8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ssm-contacts/2021-05-03/paginators-1.json @@ -0,0 +1,69 @@ +{ + "pagination": { + "ListContactChannels": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ContactChannels" + }, + "ListContacts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Contacts" + }, + "ListEngagements": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Engagements" + }, + "ListPageReceipts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Receipts" + }, + "ListPagesByContact": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Pages" + }, + "ListPagesByEngagement": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Pages" + }, + "ListPageResolutions": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "PageResolutions" + }, + "ListPreviewRotationShifts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "RotationShifts" + }, + "ListRotationOverrides": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "RotationOverrides" + }, + "ListRotationShifts": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "RotationShifts" + }, + "ListRotations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Rotations" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-contacts/2021-05-03/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ssm-contacts/2021-05-03/service-2.json.gz new file mode 100644 index 0000000..691894f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ssm-contacts/2021-05-03/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-guiconnect/2021-05-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ssm-guiconnect/2021-05-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..54c3cb9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ssm-guiconnect/2021-05-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-guiconnect/2021-05-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ssm-guiconnect/2021-05-01/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ssm-guiconnect/2021-05-01/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-guiconnect/2021-05-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ssm-guiconnect/2021-05-01/service-2.json.gz new file mode 100644 index 0000000..7870fd7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ssm-guiconnect/2021-05-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..756072d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/paginators-1.json new file mode 100644 index 0000000..662c714 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "GetResourcePolicies": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "resourcePolicies" + }, + "ListIncidentRecords": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "incidentRecordSummaries" + }, + "ListRelatedItems": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "relatedItems" + }, + "ListReplicationSets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "replicationSetArns" + }, + "ListResponsePlans": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "responsePlanSummaries" + }, + "ListTimelineEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "eventSummaries" + }, + "ListIncidentFindings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findings" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..4500676 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/waiters-2.json new file mode 100644 index 0000000..47c19b3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ssm-incidents/2018-05-10/waiters-2.json @@ -0,0 +1,53 @@ +{ + "version" : 2, + "waiters" : { + "WaitForReplicationSetActive" : { + "description" : "Wait for a replication set to become ACTIVE", + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetReplicationSet", + "acceptors" : [ { + "matcher" : "path", + "argument" : "replicationSet.status", + "state" : "success", + "expected" : "ACTIVE" + }, { + "matcher" : "path", + "argument" : "replicationSet.status", + "state" : "retry", + "expected" : "CREATING" + }, { + "matcher" : "path", + "argument" : "replicationSet.status", + "state" : "retry", + "expected" : "UPDATING" + }, { + "matcher" : "path", + "argument" : "replicationSet.status", + "state" : "failure", + "expected" : "FAILED" + } ] + }, + "WaitForReplicationSetDeleted" : { + "description" : "Wait for a replication set to be deleted", + "delay" : 30, + "maxAttempts" : 5, + "operation" : "GetReplicationSet", + "acceptors" : [ { + "matcher" : "error", + "state" : "success", + "expected" : "ResourceNotFoundException" + }, { + "matcher" : "path", + "argument" : "replicationSet.status", + "state" : "retry", + "expected" : "DELETING" + }, { + "matcher" : "path", + "argument" : "replicationSet.status", + "state" : "failure", + "expected" : "FAILED" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-quicksetup/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ssm-quicksetup/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a1c543e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ssm-quicksetup/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-quicksetup/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ssm-quicksetup/2018-05-10/paginators-1.json new file mode 100644 index 0000000..fed0f28 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ssm-quicksetup/2018-05-10/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListConfigurationManagers": { + "input_token": "StartingToken", + "output_token": "NextToken", + "limit_key": "MaxItems", + "result_key": "ConfigurationManagersList" + }, + "ListConfigurations": { + "input_token": "StartingToken", + "output_token": "NextToken", + "limit_key": "MaxItems", + "result_key": "ConfigurationsList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-quicksetup/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ssm-quicksetup/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..02e71ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ssm-quicksetup/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-sap/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ssm-sap/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d6655dd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ssm-sap/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-sap/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ssm-sap/2018-05-10/paginators-1.json new file mode 100644 index 0000000..52391d7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ssm-sap/2018-05-10/paginators-1.json @@ -0,0 +1,58 @@ +{ + "pagination": { + "ListApplications": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Applications" + }, + "ListComponents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Components" + }, + "ListDatabases": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Databases" + }, + "ListOperations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Operations" + }, + "ListOperationEvents": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "OperationEvents" + }, + "ListConfigurationCheckDefinitions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ConfigurationChecks" + }, + "ListConfigurationCheckOperations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ConfigurationCheckOperations" + }, + "ListSubCheckResults": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SubCheckResults" + }, + "ListSubCheckRuleResults": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RuleResults" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm-sap/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ssm-sap/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..a6bf45d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ssm-sap/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c1023f2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/paginators-1.json new file mode 100644 index 0000000..e2f93d2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/paginators-1.json @@ -0,0 +1,316 @@ +{ + "pagination": { + "ListAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Associations" + }, + "ListCommandInvocations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CommandInvocations" + }, + "ListCommands": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Commands" + }, + "ListDocuments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DocumentIdentifiers" + }, + "DescribeInstanceInformation": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceInformationList" + }, + "DescribeActivations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ActivationList" + }, + "DescribeParameters": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Parameters" + }, + "DescribeAssociationExecutions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AssociationExecutions" + }, + "DescribeAssociationExecutionTargets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AssociationExecutionTargets" + }, + "GetInventory": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Entities" + }, + "GetParametersByPath": { + "result_key": "Parameters", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "GetParameterHistory": { + "result_key": "Parameters", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "DescribeAutomationExecutions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AutomationExecutionMetadataList" + }, + "DescribeAutomationStepExecutions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "StepExecutions" + }, + "DescribeAvailablePatches": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Patches" + }, + "DescribeEffectiveInstanceAssociations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Associations" + }, + "DescribeEffectivePatchesForPatchBaseline": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EffectivePatches" + }, + "DescribeInstanceAssociationsStatus": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InstanceAssociationStatusInfos" + }, + "DescribeInstancePatchStates": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InstancePatchStates" + }, + "DescribeInstancePatchStatesForPatchGroup": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InstancePatchStates" + }, + "DescribeInstancePatches": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Patches" + }, + "DescribeInventoryDeletions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InventoryDeletions" + }, + "DescribeMaintenanceWindowExecutionTaskInvocations": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "WindowExecutionTaskInvocationIdentities" + }, + "DescribeMaintenanceWindowExecutionTasks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "WindowExecutionTaskIdentities" + }, + "DescribeMaintenanceWindowExecutions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "WindowExecutions" + }, + "DescribeMaintenanceWindowSchedule": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ScheduledWindowExecutions" + }, + "DescribeMaintenanceWindowTargets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Targets" + }, + "DescribeMaintenanceWindowTasks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Tasks" + }, + "DescribeMaintenanceWindows": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "WindowIdentities" + }, + "DescribeMaintenanceWindowsForTarget": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "WindowIdentities" + }, + "DescribePatchBaselines": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "BaselineIdentities" + }, + "DescribePatchGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Mappings" + }, + "DescribeSessions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Sessions" + }, + "GetInventorySchema": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Schemas" + }, + "ListAssociationVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AssociationVersions" + }, + "ListComplianceItems": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ComplianceItems" + }, + "ListComplianceSummaries": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ComplianceSummaryItems" + }, + "ListDocumentVersions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "DocumentVersions" + }, + "ListResourceComplianceSummaries": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ResourceComplianceSummaryItems" + }, + "ListResourceDataSync": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ResourceDataSyncItems" + }, + "DescribeOpsItems": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "OpsItemSummaries" + }, + "DescribePatchProperties": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Properties" + }, + "GetOpsSummary": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Entities" + }, + "ListOpsItemEvents": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Summaries" + }, + "ListOpsMetadata": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "OpsMetadataList" + }, + "ListOpsItemRelatedItems": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Summaries" + }, + "GetResourcePolicies": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Policies" + }, + "DescribeInstanceProperties": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "InstanceProperties" + }, + "ListNodes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Nodes" + }, + "ListNodesSummary": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Summary" + }, + "ListCloudConnectors": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CloudConnectors" + }, + "ValidateCloudConnector": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ValidationFindings" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/service-2.json.gz new file mode 100644 index 0000000..df2b82e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/waiters-2.json new file mode 100644 index 0000000..43f5237 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/ssm/2014-11-06/waiters-2.json @@ -0,0 +1,65 @@ +{ + "version": 2, + "waiters": { + "CommandExecuted": { + "delay": 5, + "operation": "GetCommandInvocation", + "maxAttempts": 20, + "acceptors": [ + { + "expected": "Pending", + "matcher": "path", + "state": "retry", + "argument": "Status" + }, + { + "expected": "InProgress", + "matcher": "path", + "state": "retry", + "argument": "Status" + }, + { + "expected": "Delayed", + "matcher": "path", + "state": "retry", + "argument": "Status" + }, + { + "expected": "Success", + "matcher": "path", + "state": "success", + "argument": "Status" + }, + { + "expected": "Cancelled", + "matcher": "path", + "state": "failure", + "argument": "Status" + }, + { + "expected": "TimedOut", + "matcher": "path", + "state": "failure", + "argument": "Status" + }, + { + "expected": "Failed", + "matcher": "path", + "state": "failure", + "argument": "Status" + }, + { + "expected": "Cancelling", + "matcher": "path", + "state": "failure", + "argument": "Status" + }, + { + "state": "retry", + "matcher": "error", + "expected": "InvocationDoesNotExist" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..03662d5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/paginators-1.json new file mode 100644 index 0000000..569c18c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/paginators-1.json @@ -0,0 +1,127 @@ +{ + "pagination": { + "ListAccountAssignmentCreationStatus": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AccountAssignmentsCreationStatus" + }, + "ListAccountAssignmentDeletionStatus": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AccountAssignmentsDeletionStatus" + }, + "ListAccountAssignments": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AccountAssignments" + }, + "ListAccountsForProvisionedPermissionSet": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AccountIds" + }, + "ListInstances": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Instances" + }, + "ListManagedPoliciesInPermissionSet": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AttachedManagedPolicies" + }, + "ListPermissionSetProvisioningStatus": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PermissionSetsProvisioningStatus" + }, + "ListPermissionSets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PermissionSets" + }, + "ListPermissionSetsProvisionedToAccount": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "PermissionSets" + }, + "ListTagsForResource": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Tags" + }, + "ListCustomerManagedPolicyReferencesInPermissionSet": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "CustomerManagedPolicyReferences" + }, + "ListAccountAssignmentsForPrincipal": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AccountAssignments" + }, + "ListApplicationAccessScopes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Scopes" + }, + "ListApplicationAssignments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ApplicationAssignments" + }, + "ListApplicationAssignmentsForPrincipal": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ApplicationAssignments" + }, + "ListApplicationAuthenticationMethods": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "AuthenticationMethods" + }, + "ListApplicationGrants": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Grants" + }, + "ListApplicationProviders": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "ApplicationProviders" + }, + "ListApplications": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Applications" + }, + "ListTrustedTokenIssuers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TrustedTokenIssuers" + }, + "ListRegions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Regions" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/service-2.json.gz new file mode 100644 index 0000000..0722c84 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sso-admin/2020-07-20/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sso-oidc/2019-06-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sso-oidc/2019-06-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..27eb581 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sso-oidc/2019-06-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sso-oidc/2019-06-10/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/sso-oidc/2019-06-10/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sso-oidc/2019-06-10/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sso-oidc/2019-06-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sso-oidc/2019-06-10/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sso-oidc/2019-06-10/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sso-oidc/2019-06-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sso-oidc/2019-06-10/service-2.json.gz new file mode 100644 index 0000000..d42f287 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sso-oidc/2019-06-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sso/2019-06-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sso/2019-06-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..128c90d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sso/2019-06-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sso/2019-06-10/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/sso/2019-06-10/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sso/2019-06-10/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sso/2019-06-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sso/2019-06-10/paginators-1.json new file mode 100644 index 0000000..daaed6f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sso/2019-06-10/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListAccountRoles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "roleList" + }, + "ListAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "accountList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sso/2019-06-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sso/2019-06-10/service-2.json.gz new file mode 100644 index 0000000..54403d4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sso/2019-06-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/stepfunctions/2016-11-23/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/stepfunctions/2016-11-23/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c5165db Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/stepfunctions/2016-11-23/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/stepfunctions/2016-11-23/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/stepfunctions/2016-11-23/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/stepfunctions/2016-11-23/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/stepfunctions/2016-11-23/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/stepfunctions/2016-11-23/paginators-1.json new file mode 100644 index 0000000..fb8eb5e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/stepfunctions/2016-11-23/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "GetExecutionHistory": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "events" + }, + "ListActivities": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "activities" + }, + "ListExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "executions" + }, + "ListStateMachines": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "stateMachines" + }, + "ListMapRuns": { + "input_token": "nextToken", + "limit_key": "maxResults", + "output_token": "nextToken", + "result_key": "mapRuns" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/stepfunctions/2016-11-23/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/stepfunctions/2016-11-23/service-2.json.gz new file mode 100644 index 0000000..65e7cc9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/stepfunctions/2016-11-23/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/storagegateway/2013-06-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/storagegateway/2013-06-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..9f7a15f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/storagegateway/2013-06-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/storagegateway/2013-06-30/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/storagegateway/2013-06-30/examples-1.json new file mode 100644 index 0000000..7cc0d7d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/storagegateway/2013-06-30/examples-1.json @@ -0,0 +1,1381 @@ +{ + "version": "1.0", + "examples": { + "ActivateGateway": [ + { + "input": { + "ActivationKey": "29AV1-3OFV9-VVIUB-NKT0I-LRO6V", + "GatewayName": "My_Gateway", + "GatewayRegion": "us-east-1", + "GatewayTimezone": "GMT-12:00", + "GatewayType": "STORED", + "MediumChangerType": "AWS-Gateway-VTL", + "TapeDriveType": "IBM-ULT3580-TD5" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-11A2222B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Activates the gateway you previously deployed on your host.", + "id": "to-activate-the-gateway-1471281611207", + "title": "To activate the gateway" + } + ], + "AddCache": [ + { + "input": { + "DiskIds": [ + "pci-0000:03:00.0-scsi-0:0:0:0", + "pci-0000:03:00.0-scsi-0:0:1:0" + ], + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example shows a request that activates a gateway-stored volume.", + "id": "to-add-a-cache-1471043606854", + "title": "To add a cache" + } + ], + "AddTagsToResource": [ + { + "input": { + "ResourceARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-11A2222B", + "Tags": [ + { + "Key": "Dev Gatgeway Region", + "Value": "East Coast" + } + ] + }, + "output": { + "ResourceARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-11A2222B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Adds one or more tags to the specified resource.", + "id": "to-add-tags-to-resource-1471283689460", + "title": "To add tags to resource" + } + ], + "AddUploadBuffer": [ + { + "input": { + "DiskIds": [ + "pci-0000:03:00.0-scsi-0:0:0:0", + "pci-0000:03:00.0-scsi-0:0:1:0" + ], + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Configures one or more gateway local disks as upload buffer for a specified gateway.", + "id": "to-add-upload-buffer-on-local-disk-1471293902847", + "title": "To add upload buffer on local disk" + } + ], + "AddWorkingStorage": [ + { + "input": { + "DiskIds": [ + "pci-0000:03:00.0-scsi-0:0:0:0", + "pci-0000:03:00.0-scsi-0:0:1:0" + ], + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Configures one or more gateway local disks as working storage for a gateway. (Working storage is also referred to as upload buffer.)", + "id": "to-add-storage-on-local-disk-1471294305401", + "title": "To add storage on local disk" + } + ], + "CancelArchival": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999999:tape/AMZN01A2A4" + }, + "output": { + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999999:tape/AMZN01A2A4" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Cancels archiving of a virtual tape to the virtual tape shelf (VTS) after the archiving process is initiated.", + "id": "to-cancel-virtual-tape-archiving-1471294865203", + "title": "To cancel virtual tape archiving" + } + ], + "CancelRetrieval": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999999:tape/AMZN01A2A4" + }, + "output": { + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999999:tape/AMZN01A2A4" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Cancels retrieval of a virtual tape from the virtual tape shelf (VTS) to a gateway after the retrieval process is initiated.", + "id": "to-cancel-virtual-tape-retrieval-1471295704491", + "title": "To cancel virtual tape retrieval" + } + ], + "CreateCachediSCSIVolume": [ + { + "input": { + "ClientToken": "cachedvol112233", + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "NetworkInterfaceId": "10.1.1.1", + "SnapshotId": "snap-f47b7b94", + "TargetName": "my-volume", + "VolumeSizeInBytes": 536870912000 + }, + "output": { + "TargetARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/target/iqn.1997-05.com.amazon:myvolume", + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a cached volume on a specified cached gateway.", + "id": "to-create-a-cached-iscsi-volume-1471296661787", + "title": "To create a cached iSCSI volume" + } + ], + "CreateSnapshot": [ + { + "input": { + "SnapshotDescription": "My root volume snapshot as of 10/03/2017", + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + }, + "output": { + "SnapshotId": "snap-78e22663", + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Initiates an ad-hoc snapshot of a gateway volume.", + "id": "to-create-a-snapshot-of-a-gateway-volume-1471301469561", + "title": "To create a snapshot of a gateway volume" + } + ], + "CreateSnapshotFromVolumeRecoveryPoint": [ + { + "input": { + "SnapshotDescription": "My root volume snapshot as of 2017-06-30T10:10:10.000Z", + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + }, + "output": { + "SnapshotId": "snap-78e22663", + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB", + "VolumeRecoveryPointTime": "2017-06-30T10:10:10.000Z" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Initiates a snapshot of a gateway from a volume recovery point.", + "id": "to-create-a-snapshot-of-a-gateway-volume-1471301469561", + "title": "To create a snapshot of a gateway volume" + } + ], + "CreateStorediSCSIVolume": [ + { + "input": { + "DiskId": "pci-0000:03:00.0-scsi-0:0:0:0", + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "NetworkInterfaceId": "10.1.1.1", + "PreserveExistingData": true, + "SnapshotId": "snap-f47b7b94", + "TargetName": "my-volume" + }, + "output": { + "TargetARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/target/iqn.1997-05.com.amazon:myvolume", + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB", + "VolumeSizeInBytes": 1099511627776 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a stored volume on a specified stored gateway.", + "id": "to-create-a-stored-iscsi-volume-1471367662813", + "title": "To create a stored iSCSI volume" + } + ], + "CreateTapeWithBarcode": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B", + "TapeBarcode": "TEST12345", + "TapeSizeInBytes": 107374182400 + }, + "output": { + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999999:tape/TEST12345" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates a virtual tape by using your own barcode.", + "id": "to-create-a-virtual-tape-using-a-barcode-1471371842452", + "title": "To create a virtual tape using a barcode" + } + ], + "CreateTapes": [ + { + "input": { + "ClientToken": "77777", + "GatewayARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B", + "NumTapesToCreate": 3, + "TapeBarcodePrefix": "TEST", + "TapeSizeInBytes": 107374182400 + }, + "output": { + "TapeARNs": [ + "arn:aws:storagegateway:us-east-1:999999999999:tape/TEST38A29D", + "arn:aws:storagegateway:us-east-1:204469490176:tape/TEST3AA29F", + "arn:aws:storagegateway:us-east-1:204469490176:tape/TEST3BA29E" + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Creates one or more virtual tapes.", + "id": "to-create-a-virtual-tape-1471372061659", + "title": "To create a virtual tape" + } + ], + "DeleteBandwidthRateLimit": [ + { + "input": { + "BandwidthType": "All", + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the bandwidth rate limits of a gateway; either the upload or download limit, or both.", + "id": "to-delete-bandwidth-rate-limits-of-gateway-1471373225520", + "title": "To delete bandwidth rate limits of gateway" + } + ], + "DeleteChapCredentials": [ + { + "input": { + "InitiatorName": "iqn.1991-05.com.microsoft:computername.domain.example.com", + "TargetARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/target/iqn.1997-05.com.amazon:myvolume" + }, + "output": { + "InitiatorName": "iqn.1991-05.com.microsoft:computername.domain.example.com", + "TargetARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/target/iqn.1997-05.com.amazon:myvolume" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes Challenge-Handshake Authentication Protocol (CHAP) credentials for a specified iSCSI target and initiator pair.", + "id": "to-delete-chap-credentials-1471375025612", + "title": "To delete CHAP credentials" + } + ], + "DeleteGateway": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation deletes the gateway, but not the gateway's VM from the host computer.", + "id": "to-delete-a-gatgeway-1471381697333", + "title": "To delete a gatgeway" + } + ], + "DeleteSnapshotSchedule": [ + { + "input": { + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + }, + "output": { + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This action enables you to delete a snapshot schedule for a volume.", + "id": "to-delete-a-snapshot-of-a-volume-1471382234377", + "title": "To delete a snapshot of a volume" + } + ], + "DeleteTape": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:204469490176:gateway/sgw-12A3456B", + "TapeARN": "arn:aws:storagegateway:us-east-1:204469490176:tape/TEST05A2A0" + }, + "output": { + "TapeARN": "arn:aws:storagegateway:us-east-1:204469490176:tape/TEST05A2A0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example deletes the specified virtual tape.", + "id": "to-delete-a-virtual-tape-1471382444157", + "title": "To delete a virtual tape" + } + ], + "DeleteTapeArchive": [ + { + "input": { + "TapeARN": "arn:aws:storagegateway:us-east-1:204469490176:tape/TEST05A2A0" + }, + "output": { + "TapeARN": "arn:aws:storagegateway:us-east-1:204469490176:tape/TEST05A2A0" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the specified virtual tape from the virtual tape shelf (VTS).", + "id": "to-delete-a-virtual-tape-from-the-shelf-vts-1471383964329", + "title": "To delete a virtual tape from the shelf (VTS)" + } + ], + "DeleteVolume": [ + { + "input": { + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + }, + "output": { + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Deletes the specified gateway volume that you previously created using the CreateCachediSCSIVolume or CreateStorediSCSIVolume API.", + "id": "to-delete-a-gateway-volume-1471384418416", + "title": "To delete a gateway volume" + } + ], + "DescribeBandwidthRateLimit": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "AverageDownloadRateLimitInBitsPerSec": 204800, + "AverageUploadRateLimitInBitsPerSec": 102400, + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns a value for a bandwidth rate limit if set. If not set, then only the gateway ARN is returned.", + "id": "to-describe-the-bandwidth-rate-limits-of-a-gateway-1471384826404", + "title": "To describe the bandwidth rate limits of a gateway" + } + ], + "DescribeCache": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "CacheAllocatedInBytes": 2199023255552, + "CacheDirtyPercentage": 0.07, + "CacheHitPercentage": 99.68, + "CacheMissPercentage": 0.32, + "CacheUsedPercentage": 0.07, + "DiskIds": [ + "pci-0000:03:00.0-scsi-0:0:0:0", + "pci-0000:04:00.0-scsi-0:1:0:0" + ], + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns information about the cache of a gateway.", + "id": "to-describe-cache-information-1471385756036", + "title": "To describe cache information" + } + ], + "DescribeCachediSCSIVolumes": [ + { + "input": { + "VolumeARNs": [ + "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + ] + }, + "output": { + "CachediSCSIVolumes": [ + { + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB", + "VolumeId": "vol-1122AABB", + "VolumeSizeInBytes": 1099511627776, + "VolumeStatus": "AVAILABLE", + "VolumeType": "CACHED iSCSI", + "VolumeiSCSIAttributes": { + "ChapEnabled": true, + "LunNumber": 1, + "NetworkInterfaceId": "10.243.43.207", + "NetworkInterfacePort": 3260, + "TargetARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/target/iqn.1997-05.com.amazon:myvolume" + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns a description of the gateway cached iSCSI volumes specified in the request.", + "id": "to-describe-gateway-cached-iscsi-volumes-1471458094649", + "title": "To describe gateway cached iSCSI volumes" + } + ], + "DescribeChapCredentials": [ + { + "input": { + "TargetARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/target/iqn.1997-05.com.amazon:myvolume" + }, + "output": { + "ChapCredentials": [ + { + "InitiatorName": "iqn.1991-05.com.microsoft:computername.domain.example.com", + "SecretToAuthenticateInitiator": "111111111111", + "SecretToAuthenticateTarget": "222222222222", + "TargetARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/target/iqn.1997-05.com.amazon:myvolume" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns an array of Challenge-Handshake Authentication Protocol (CHAP) credentials information for a specified iSCSI target, one for each target-initiator pair.", + "id": "to-describe-chap-credetnitals-for-an-iscsi-1471467462967", + "title": "To describe CHAP credetnitals for an iSCSI" + } + ], + "DescribeGatewayInformation": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "GatewayId": "sgw-AABB1122", + "GatewayName": "My_Gateway", + "GatewayNetworkInterfaces": [ + { + "Ipv4Address": "10.35.69.216" + } + ], + "GatewayState": "STATE_RUNNING", + "GatewayTimezone": "GMT-8:00", + "GatewayType": "STORED", + "LastSoftwareUpdate": "2016-01-02T16:00:00", + "NextUpdateAvailabilityDate": "2017-01-02T16:00:00" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns metadata about a gateway such as its name, network interfaces, configured time zone, and the state (whether the gateway is running or not).", + "id": "to-describe-metadata-about-the-gateway-1471467849079", + "title": "To describe metadata about the gateway" + } + ], + "DescribeMaintenanceStartTime": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "DayOfWeek": 2, + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "HourOfDay": 15, + "MinuteOfHour": 35, + "Timezone": "GMT+7:00" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns your gateway's weekly maintenance start time including the day and time of the week.", + "id": "to-describe-gateways-maintenance-start-time-1471470727387", + "title": "To describe gateway's maintenance start time" + } + ], + "DescribeSnapshotSchedule": [ + { + "input": { + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + }, + "output": { + "Description": "sgw-AABB1122:vol-AABB1122:Schedule", + "RecurrenceInHours": 24, + "StartAt": 6, + "Timezone": "GMT+7:00", + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Describes the snapshot schedule for the specified gateway volume including intervals at which snapshots are automatically initiated.", + "id": "to-describe-snapshot-schedule-for-gateway-volume-1471471139538", + "title": "To describe snapshot schedule for gateway volume" + } + ], + "DescribeStorediSCSIVolumes": [ + { + "input": { + "VolumeARNs": [ + "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + ] + }, + "output": { + "StorediSCSIVolumes": [ + { + "PreservedExistingData": false, + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB", + "VolumeDiskId": "pci-0000:03:00.0-scsi-0:0:0:0", + "VolumeId": "vol-1122AABB", + "VolumeProgress": 23.7, + "VolumeSizeInBytes": 1099511627776, + "VolumeStatus": "BOOTSTRAPPING", + "VolumeiSCSIAttributes": { + "ChapEnabled": true, + "NetworkInterfaceId": "10.243.43.207", + "NetworkInterfacePort": 3260, + "TargetARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/target/iqn.1997-05.com.amazon:myvolume" + } + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns the description of the gateway volumes specified in the request belonging to the same gateway.", + "id": "to-describe-the-volumes-of-a-gateway-1471472640660", + "title": "To describe the volumes of a gateway" + } + ], + "DescribeTapeArchives": [ + { + "input": { + "Limit": 123, + "Marker": "1", + "TapeARNs": [ + "arn:aws:storagegateway:us-east-1:999999999999:tape/AM08A1AD", + "arn:aws:storagegateway:us-east-1:999999999999:tape/AMZN01A2A4" + ] + }, + "output": { + "Marker": "1", + "TapeArchives": [ + { + "CompletionTime": "2016-12-16T13:50Z", + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999:tape/AM08A1AD", + "TapeBarcode": "AM08A1AD", + "TapeSizeInBytes": 107374182400, + "TapeStatus": "ARCHIVED" + }, + { + "CompletionTime": "2016-12-16T13:59Z", + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999:tape/AMZN01A2A4", + "TapeBarcode": "AMZN01A2A4", + "TapeSizeInBytes": 429496729600, + "TapeStatus": "ARCHIVED" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns a description of specified virtual tapes in the virtual tape shelf (VTS).", + "id": "to-describe-virtual-tapes-in-the-vts-1471473188198", + "title": "To describe virtual tapes in the VTS" + } + ], + "DescribeTapeRecoveryPoints": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "Limit": 1, + "Marker": "1" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "Marker": "1", + "TapeRecoveryPointInfos": [ + { + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999:tape/AMZN01A2A4", + "TapeRecoveryPointTime": "2016-12-16T13:50Z", + "TapeSizeInBytes": 1471550497, + "TapeStatus": "AVAILABLE" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns a list of virtual tape recovery points that are available for the specified gateway-VTL.", + "id": "to-describe-virtual-tape-recovery-points-1471542042026", + "title": "To describe virtual tape recovery points" + } + ], + "DescribeTapes": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B", + "Limit": 2, + "Marker": "1", + "TapeARNs": [ + "arn:aws:storagegateway:us-east-1:999999999999:tape/TEST04A2A1", + "arn:aws:storagegateway:us-east-1:999999999999:tape/TEST05A2A0" + ] + }, + "output": { + "Marker": "1", + "Tapes": [ + { + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999999:tape/TEST04A2A1", + "TapeBarcode": "TEST04A2A1", + "TapeSizeInBytes": 107374182400, + "TapeStatus": "AVAILABLE" + }, + { + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999999:tape/TEST05A2A0", + "TapeBarcode": "TEST05A2A0", + "TapeSizeInBytes": 107374182400, + "TapeStatus": "AVAILABLE" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns a description of the specified Amazon Resource Name (ARN) of virtual tapes. If a TapeARN is not specified, returns a description of all virtual tapes.", + "id": "to-describe-virtual-tapes-associated-with-gateway-1471629287727", + "title": "To describe virtual tape(s) associated with gateway" + } + ], + "DescribeUploadBuffer": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "DiskIds": [ + "pci-0000:03:00.0-scsi-0:0:0:0", + "pci-0000:04:00.0-scsi-0:1:0:0" + ], + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "UploadBufferAllocatedInBytes": 0, + "UploadBufferUsedInBytes": 161061273600 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns information about the upload buffer of a gateway including disk IDs and the amount of upload buffer space allocated/used.", + "id": "to-describe-upload-buffer-of-gateway-1471631099003", + "title": "To describe upload buffer of gateway" + }, + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "DiskIds": [ + "pci-0000:03:00.0-scsi-0:0:0:0", + "pci-0000:04:00.0-scsi-0:1:0:0" + ], + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "UploadBufferAllocatedInBytes": 161061273600, + "UploadBufferUsedInBytes": 0 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns information about the upload buffer of a gateway including disk IDs and the amount of upload buffer space allocated and used.", + "id": "to-describe-upload-buffer-of-a-gateway--1471904566370", + "title": "To describe upload buffer of a gateway" + } + ], + "DescribeVTLDevices": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B", + "Limit": 123, + "Marker": "1", + "VTLDeviceARNs": [ + + ] + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B", + "Marker": "1", + "VTLDevices": [ + { + "DeviceiSCSIAttributes": { + "ChapEnabled": false, + "NetworkInterfaceId": "10.243.43.207", + "NetworkInterfacePort": 3260, + "TargetARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B/target/iqn.1997-05.com.amazon:sgw-1fad4876-mediachanger" + }, + "VTLDeviceARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B/device/AMZN_SGW-1FAD4876_MEDIACHANGER_00001", + "VTLDeviceProductIdentifier": "L700", + "VTLDeviceType": "Medium Changer", + "VTLDeviceVendor": "STK" + }, + { + "DeviceiSCSIAttributes": { + "ChapEnabled": false, + "NetworkInterfaceId": "10.243.43.209", + "NetworkInterfacePort": 3260, + "TargetARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B/target/iqn.1997-05.com.amazon:sgw-1fad4876-tapedrive-01" + }, + "VTLDeviceARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B/device/AMZN_SGW-1FAD4876_TAPEDRIVE_00001", + "VTLDeviceProductIdentifier": "ULT3580-TD5", + "VTLDeviceType": "Tape Drive", + "VTLDeviceVendor": "IBM" + }, + { + "DeviceiSCSIAttributes": { + "ChapEnabled": false, + "NetworkInterfaceId": "10.243.43.209", + "NetworkInterfacePort": 3260, + "TargetARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B/target/iqn.1997-05.com.amazon:sgw-1fad4876-tapedrive-02" + }, + "VTLDeviceARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B/device/AMZN_SGW-1FAD4876_TAPEDRIVE_00002", + "VTLDeviceProductIdentifier": "ULT3580-TD5", + "VTLDeviceType": "Tape Drive", + "VTLDeviceVendor": "IBM" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Returns a description of virtual tape library (VTL) devices for the specified gateway.", + "id": "to-describe-virtual-tape-library-vtl-devices-of-a-single-gateway-1471906071410", + "title": "To describe virtual tape library (VTL) devices of a single gateway" + } + ], + "DescribeWorkingStorage": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "DiskIds": [ + "pci-0000:03:00.0-scsi-0:0:0:0", + "pci-0000:03:00.0-scsi-0:0:1:0" + ], + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "WorkingStorageAllocatedInBytes": 2199023255552, + "WorkingStorageUsedInBytes": 789207040 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation is supported only for the gateway-stored volume architecture. This operation is deprecated in cached-volumes API version (20120630). Use DescribeUploadBuffer instead.", + "id": "to-describe-the-working-storage-of-a-gateway-depreciated-1472070842332", + "title": "To describe the working storage of a gateway [Depreciated]" + } + ], + "DisableGateway": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Disables a gateway when the gateway is no longer functioning. Use this operation for a gateway-VTL that is not reachable or not functioning.", + "id": "to-disable-a-gateway-when-it-is-no-longer-functioning-1472076046936", + "title": "To disable a gateway when it is no longer functioning" + } + ], + "ListGateways": [ + { + "input": { + "Limit": 2, + "Marker": "1" + }, + "output": { + "Gateways": [ + { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-23A4567C" + } + ], + "Marker": "1" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists gateways owned by an AWS account in a specified region as requested. Results are sorted by gateway ARN up to a maximum of 100 gateways.", + "id": "to-lists-region-specific-gateways-per-aws-account-1472077860657", + "title": "To lists region specific gateways per AWS account" + } + ], + "ListLocalDisks": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "Disks": [ + { + "DiskAllocationType": "CACHE_STORAGE", + "DiskId": "pci-0000:03:00.0-scsi-0:0:0:0", + "DiskNode": "SCSI(0:0)", + "DiskPath": "/dev/sda", + "DiskSizeInBytes": 1099511627776, + "DiskStatus": "missing" + }, + { + "DiskAllocationResource": "", + "DiskAllocationType": "UPLOAD_BUFFER", + "DiskId": "pci-0000:03:00.0-scsi-0:0:1:0", + "DiskNode": "SCSI(0:1)", + "DiskPath": "/dev/sdb", + "DiskSizeInBytes": 1099511627776, + "DiskStatus": "present" + } + ], + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The request returns a list of all disks, specifying which are configured as working storage, cache storage, or stored volume or not configured at all.", + "id": "to-list-the-gateways-local-disks-1472079564618", + "title": "To list the gateway's local disks" + } + ], + "ListTagsForResource": [ + { + "input": { + "Limit": 1, + "Marker": "1", + "ResourceARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-11A2222B" + }, + "output": { + "Marker": "1", + "ResourceARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-11A2222B", + "Tags": [ + { + "Key": "Dev Gatgeway Region", + "Value": "East Coast" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists the tags that have been added to the specified resource.", + "id": "to-list-tags-that-have-been-added-to-a-resource-1472080268972", + "title": "To list tags that have been added to a resource" + } + ], + "ListVolumeRecoveryPoints": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "VolumeRecoveryPointInfos": [ + { + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB", + "VolumeRecoveryPointTime": "2012-09-04T21:08:44.627Z", + "VolumeSizeInBytes": 536870912000 + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists the recovery points for a specified gateway in which all data of the volume is consistent and can be used to create a snapshot.", + "id": "to-list-recovery-points-for-a-gateway-1472143015088", + "title": "To list recovery points for a gateway" + } + ], + "ListVolumes": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "Limit": 2, + "Marker": "1" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "Marker": "1", + "VolumeInfos": [ + { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "GatewayId": "sgw-12A3456B", + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB", + "VolumeId": "vol-1122AABB", + "VolumeSizeInBytes": 107374182400, + "VolumeType": "STORED" + }, + { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-13B4567C", + "GatewayId": "sgw-gw-13B4567C", + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-13B4567C/volume/vol-3344CCDD", + "VolumeId": "vol-1122AABB", + "VolumeSizeInBytes": 107374182400, + "VolumeType": "STORED" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists the iSCSI stored volumes of a gateway. Results are sorted by volume ARN up to a maximum of 100 volumes.", + "id": "to-list-the-iscsi-stored-volumes-of-a-gateway-1472145723653", + "title": "To list the iSCSI stored volumes of a gateway" + } + ], + "RemoveTagsFromResource": [ + { + "input": { + "ResourceARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-11A2222B", + "TagKeys": [ + "Dev Gatgeway Region", + "East Coast" + ] + }, + "output": { + "ResourceARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-11A2222B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Lists the iSCSI stored volumes of a gateway. Removes one or more tags from the specified resource.", + "id": "to-remove-tags-from-a-resource-1472147210553", + "title": "To remove tags from a resource" + } + ], + "ResetCache": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-13B4567C" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-13B4567C" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Resets all cache disks that have encountered a error and makes the disks available for reconfiguration as cache storage.", + "id": "to-reset-cache-disks-in-error-status-1472148909807", + "title": "To reset cache disks in error status" + } + ], + "RetrieveTapeArchive": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B", + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999999:tape/TEST0AA2AF" + }, + "output": { + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999999:tape/TEST0AA2AF" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Retrieves an archived virtual tape from the virtual tape shelf (VTS) to a gateway-VTL. Virtual tapes archived in the VTS are not associated with any gateway.", + "id": "to-retrieve-an-archived-tape-from-the-vts-1472149812358", + "title": "To retrieve an archived tape from the VTS" + } + ], + "RetrieveTapeRecoveryPoint": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B", + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999999:tape/TEST0AA2AF" + }, + "output": { + "TapeARN": "arn:aws:storagegateway:us-east-1:999999999999:tape/TEST0AA2AF" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Retrieves the recovery point for the specified virtual tape.", + "id": "to-retrieve-the-recovery-point-of-a-virtual-tape-1472150014805", + "title": "To retrieve the recovery point of a virtual tape" + } + ], + "SetLocalConsolePassword": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B", + "LocalConsolePassword": "PassWordMustBeAtLeast6Chars." + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Sets the password for your VM local console.", + "id": "to-set-a-password-for-your-vm-1472150202632", + "title": "To set a password for your VM" + } + ], + "ShutdownGateway": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This operation shuts down the gateway service component running in the storage gateway's virtual machine (VM) and not the VM.", + "id": "to-shut-down-a-gateway-service-1472150508835", + "title": "To shut down a gateway service" + } + ], + "StartGateway": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Starts a gateway service that was previously shut down.", + "id": "to-start-a-gateway-service-1472150722315", + "title": "To start a gateway service" + } + ], + "UpdateBandwidthRateLimit": [ + { + "input": { + "AverageDownloadRateLimitInBitsPerSec": 102400, + "AverageUploadRateLimitInBitsPerSec": 51200, + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Updates the bandwidth rate limits of a gateway. Both the upload and download bandwidth rate limit can be set, or either one of the two. If a new limit is not set, the existing rate limit remains.", + "id": "to-update-the-bandwidth-rate-limits-of-a-gateway-1472151016202", + "title": "To update the bandwidth rate limits of a gateway" + } + ], + "UpdateChapCredentials": [ + { + "input": { + "InitiatorName": "iqn.1991-05.com.microsoft:computername.domain.example.com", + "SecretToAuthenticateInitiator": "111111111111", + "SecretToAuthenticateTarget": "222222222222", + "TargetARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/target/iqn.1997-05.com.amazon:myvolume" + }, + "output": { + "InitiatorName": "iqn.1991-05.com.microsoft:computername.domain.example.com", + "TargetARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/target/iqn.1997-05.com.amazon:myvolume" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Updates the Challenge-Handshake Authentication Protocol (CHAP) credentials for a specified iSCSI target.", + "id": "to-update-chap-credentials-for-an-iscsi-target-1472151325795", + "title": "To update CHAP credentials for an iSCSI target" + } + ], + "UpdateGatewayInformation": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "GatewayName": "MyGateway2", + "GatewayTimezone": "GMT-12:00" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "GatewayName": "" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Updates a gateway's metadata, which includes the gateway's name and time zone.", + "id": "to-update-a-gateways-metadata-1472151688693", + "title": "To update a gateway's metadata" + } + ], + "UpdateGatewaySoftwareNow": [ + { + "input": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Updates the gateway virtual machine (VM) software. The request immediately triggers the software update.", + "id": "to-update-a-gateways-vm-software-1472152020929", + "title": "To update a gateway's VM software" + } + ], + "UpdateMaintenanceStartTime": [ + { + "input": { + "DayOfWeek": 2, + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B", + "HourOfDay": 0, + "MinuteOfHour": 30 + }, + "output": { + "GatewayARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Updates a gateway's weekly maintenance start time information, including day and time of the week. The maintenance time is in your gateway's time zone.", + "id": "to-update-a-gateways-maintenance-start-time-1472152552031", + "title": "To update a gateway's maintenance start time" + } + ], + "UpdateSnapshotSchedule": [ + { + "input": { + "Description": "Hourly snapshot", + "RecurrenceInHours": 1, + "StartAt": 0, + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + }, + "output": { + "VolumeARN": "arn:aws:storagegateway:us-east-1:111122223333:gateway/sgw-12A3456B/volume/vol-1122AABB" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Updates a snapshot schedule configured for a gateway volume.", + "id": "to-update-a-volume-snapshot-schedule-1472152757068", + "title": "To update a volume snapshot schedule" + } + ], + "UpdateVTLDeviceType": [ + { + "input": { + "DeviceType": "Medium Changer", + "VTLDeviceARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B/device/AMZN_SGW-1FAD4876_MEDIACHANGER_00001" + }, + "output": { + "VTLDeviceARN": "arn:aws:storagegateway:us-east-1:999999999999:gateway/sgw-12A3456B/device/AMZN_SGW-1FAD4876_MEDIACHANGER_00001" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "Updates the type of medium changer in a gateway-VTL after a gateway-VTL is activated.", + "id": "to-update-a-vtl-device-type-1472153012967", + "title": "To update a VTL device type" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/storagegateway/2013-06-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/storagegateway/2013-06-30/paginators-1.json new file mode 100644 index 0000000..e43b890 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/storagegateway/2013-06-30/paginators-1.json @@ -0,0 +1,84 @@ +{ + "pagination": { + "DescribeTapeArchives": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "TapeArchives" + }, + "DescribeTapeRecoveryPoints": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "TapeRecoveryPointInfos" + }, + "DescribeTapes": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "Tapes" + }, + "DescribeVTLDevices": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "VTLDevices" + }, + "ListGateways": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "Gateways" + }, + "ListVolumes": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "VolumeInfos" + }, + "ListTapes": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "TapeInfos" + }, + "ListFileShares": { + "input_token": "Marker", + "limit_key": "Limit", + "non_aggregate_keys": [ + "Marker" + ], + "output_token": "NextMarker", + "result_key": "FileShareInfoList" + }, + "ListTagsForResource": { + "input_token": "Marker", + "limit_key": "Limit", + "non_aggregate_keys": [ + "ResourceARN" + ], + "output_token": "Marker", + "result_key": "Tags" + }, + "ListTapePools": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "PoolInfos" + }, + "ListFileSystemAssociations": { + "input_token": "Marker", + "limit_key": "Limit", + "non_aggregate_keys": [ + "Marker" + ], + "output_token": "NextMarker", + "result_key": "FileSystemAssociationSummaryList" + }, + "ListCacheReports": { + "input_token": "Marker", + "output_token": "Marker", + "result_key": "CacheReportList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/storagegateway/2013-06-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/storagegateway/2013-06-30/service-2.json.gz new file mode 100644 index 0000000..876bea4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/storagegateway/2013-06-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sts/2011-06-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sts/2011-06-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..66df12c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sts/2011-06-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sts/2011-06-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/sts/2011-06-15/examples-1.json new file mode 100644 index 0000000..7396aef --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sts/2011-06-15/examples-1.json @@ -0,0 +1,271 @@ +{ + "version": "1.0", + "examples": { + "AssumeRole": [ + { + "input": { + "ExternalId": "123ABC", + "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"Stmt1\",\"Effect\":\"Allow\",\"Action\":\"s3:ListAllMyBuckets\",\"Resource\":\"*\"}]}", + "RoleArn": "arn:aws:iam::123456789012:role/demo", + "RoleSessionName": "testAssumeRoleSession", + "Tags": [ + { + "Key": "Project", + "Value": "Unicorn" + }, + { + "Key": "Team", + "Value": "Automation" + }, + { + "Key": "Cost-Center", + "Value": "12345" + } + ], + "TransitiveTagKeys": [ + "Project", + "Cost-Center" + ] + }, + "output": { + "AssumedRoleUser": { + "Arn": "arn:aws:sts::123456789012:assumed-role/demo/Bob", + "AssumedRoleId": "ARO123EXAMPLE123:Bob" + }, + "Credentials": { + "AccessKeyId": "AKIAIOSFODNN7EXAMPLE", + "Expiration": "2011-07-15T23:28:33.359Z", + "SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY", + "SessionToken": "AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==" + }, + "PackedPolicySize": 8 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "to-assume-a-role-1480532402212", + "title": "To assume a role" + } + ], + "AssumeRoleWithSAML": [ + { + "input": { + "DurationSeconds": 3600, + "PrincipalArn": "arn:aws:iam::123456789012:saml-provider/SAML-test", + "RoleArn": "arn:aws:iam::123456789012:role/TestSaml", + "SAMLAssertion": "VERYLONGENCODEDASSERTIONEXAMPLExzYW1sOkF1ZGllbmNlPmJsYW5rPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6dHJhbnNpZW50Ij5TYW1sRXhhbXBsZTwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxOS0xMS0wMVQyMDoyNTowNS4xNDVaIiBSZWNpcGllbnQ9Imh0dHBzOi8vc2lnbmluLmF3cy5hbWF6b24uY29tL3NhbWwiLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRoPD94bWwgdmpSZXNwb25zZT4=" + }, + "output": { + "AssumedRoleUser": { + "Arn": "arn:aws:sts::123456789012:assumed-role/TestSaml", + "AssumedRoleId": "ARO456EXAMPLE789:TestSaml" + }, + "Audience": "https://signin.aws.amazon.com/saml", + "Credentials": { + "AccessKeyId": "ASIAV3ZUEFP6EXAMPLE", + "Expiration": "2019-11-01T20:26:47Z", + "SecretAccessKey": "8P+SQvWIuLnKhh8d++jpw0nNmQRBZvNEXAMPLEKEY", + "SessionToken": "IQoJb3JpZ2luX2VjEOz////////////////////wEXAMPLEtMSJHMEUCIDoKK3JH9uGQE1z0sINr5M4jk+Na8KHDcCYRVjJCZEvOAiEA3OvJGtw1EcViOleS2vhs8VdCKFJQWPQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==" + }, + "Issuer": "https://integ.example.com/idp/shibboleth", + "NameQualifier": "SbdGOnUkh1i4+EXAMPLExL/jEvs=", + "PackedPolicySize": 6, + "Subject": "SamlExample", + "SubjectType": "transient" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "to-assume-role-with-saml-14882749597814", + "title": "To assume a role using a SAML assertion" + } + ], + "AssumeRoleWithWebIdentity": [ + { + "input": { + "DurationSeconds": 3600, + "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"Stmt1\",\"Effect\":\"Allow\",\"Action\":\"s3:ListAllMyBuckets\",\"Resource\":\"*\"}]}", + "ProviderId": "www.amazon.com", + "RoleArn": "arn:aws:iam::123456789012:role/FederatedWebIdentityRole", + "RoleSessionName": "app1", + "WebIdentityToken": "Atza%7CIQEBLjAsAhRFiXuWpUXuRvQ9PZL3GMFcYevydwIUFAHZwXZXXXXXXXXJnrulxKDHwy87oGKPznh0D6bEQZTSCzyoCtL_8S07pLpr0zMbn6w1lfVZKNTBdDansFBmtGnIsIapjI6xKR02Yc_2bQ8LZbUXSGm6Ry6_BG7PrtLZtj_dfCTj92xNGed-CrKqjG7nPBjNIL016GGvuS5gSvPRUxWES3VYfm1wl7WTI7jn-Pcb6M-buCgHhFOzTQxod27L9CqnOLio7N3gZAGpsp6n1-AJBOCJckcyXe2c6uD0srOJeZlKUm2eTDVMf8IehDVI0r1QOnTV6KzzAI3OY87Vd_cVMQ" + }, + "output": { + "AssumedRoleUser": { + "Arn": "arn:aws:sts::123456789012:assumed-role/FederatedWebIdentityRole/app1", + "AssumedRoleId": "AROACLKWSDQRAOEXAMPLE:app1" + }, + "Audience": "client.5498841531868486423.1548@apps.example.com", + "Credentials": { + "AccessKeyId": "AKIAIOSFODNN7EXAMPLE", + "Expiration": "2014-10-24T23:00:23Z", + "SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY", + "SessionToken": "AQoDYXdzEE0a8ANXXXXXXXXNO1ewxE5TijQyp+IEXAMPLE" + }, + "PackedPolicySize": 123, + "Provider": "www.amazon.com", + "SubjectFromWebIdentityToken": "amzn1.account.AF6RHO7KZU5XRVQJGXK6HEXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "to-assume-a-role-as-an-openid-connect-federated-user-1480533445696", + "title": "To assume a role as an OpenID Connect-federated user" + } + ], + "DecodeAuthorizationMessage": [ + { + "input": { + "EncodedMessage": "" + }, + "output": { + "DecodedMessage": "{\"allowed\": \"false\",\"explicitDeny\": \"false\",\"matchedStatements\": \"\",\"failures\": \"\",\"context\": {\"principal\": {\"id\": \"AIDACKCEVSQ6C2EXAMPLE\",\"name\": \"Bob\",\"arn\": \"arn:aws:iam::123456789012:user/Bob\"},\"action\": \"ec2:StopInstances\",\"resource\": \"arn:aws:ec2:us-east-1:123456789012:instance/i-dd01c9bd\",\"conditions\": [{\"item\": {\"key\": \"ec2:Tenancy\",\"values\": [\"default\"]},{\"item\": {\"key\": \"ec2:ResourceTag/elasticbeanstalk:environment-name\",\"values\": [\"Default-Environment\"]}},(Additional items ...)]}}" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "to-decode-information-about-an-authorization-status-of-a-request-1480533854499", + "title": "To decode information about an authorization status of a request" + } + ], + "GetCallerIdentity": [ + { + "input": { + }, + "output": { + "Account": "123456789012", + "Arn": "arn:aws:iam::123456789012:user/Alice", + "UserId": "AKIAI44QH8DHBEXAMPLE" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows a request and response made with the credentials for a user named Alice in the AWS account 123456789012.", + "id": "to-get-details-about-a-calling-iam-user-1480540050376", + "title": "To get details about a calling IAM user" + }, + { + "input": { + }, + "output": { + "Account": "123456789012", + "Arn": "arn:aws:sts::123456789012:assumed-role/my-role-name/my-role-session-name", + "UserId": "AKIAI44QH8DHBEXAMPLE:my-role-session-name" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows a request and response made with temporary credentials created by AssumeRole. The name of the assumed role is my-role-name, and the RoleSessionName is set to my-role-session-name.", + "id": "to-get-details-about-a-calling-user-federated-with-assumerole-1480540158545", + "title": "To get details about a calling user federated with AssumeRole" + }, + { + "input": { + }, + "output": { + "Account": "123456789012", + "Arn": "arn:aws:sts::123456789012:federated-user/my-federated-user-name", + "UserId": "123456789012:my-federated-user-name" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "This example shows a request and response made with temporary credentials created by using GetFederationToken. The Name parameter is set to my-federated-user-name.", + "id": "to-get-details-about-a-calling-user-federated-with-getfederationtoken-1480540231316", + "title": "To get details about a calling user federated with GetFederationToken" + } + ], + "GetFederationToken": [ + { + "input": { + "DurationSeconds": 3600, + "Name": "testFedUserSession", + "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"Stmt1\",\"Effect\":\"Allow\",\"Action\":\"s3:ListAllMyBuckets\",\"Resource\":\"*\"}]}", + "Tags": [ + { + "Key": "Project", + "Value": "Pegasus" + }, + { + "Key": "Cost-Center", + "Value": "98765" + } + ] + }, + "output": { + "Credentials": { + "AccessKeyId": "AKIAIOSFODNN7EXAMPLE", + "Expiration": "2011-07-15T23:28:33.359Z", + "SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY", + "SessionToken": "AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==" + }, + "FederatedUser": { + "Arn": "arn:aws:sts::123456789012:federated-user/Bob", + "FederatedUserId": "123456789012:Bob" + }, + "PackedPolicySize": 8 + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "to-get-temporary-credentials-for-a-role-by-using-getfederationtoken-1480540749900", + "title": "To get temporary credentials for a role by using GetFederationToken" + } + ], + "GetSessionToken": [ + { + "input": { + "DurationSeconds": 3600, + "SerialNumber": "YourMFASerialNumber", + "TokenCode": "123456" + }, + "output": { + "Credentials": { + "AccessKeyId": "AKIAIOSFODNN7EXAMPLE", + "Expiration": "2011-07-11T19:55:29.611Z", + "SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY", + "SessionToken": "AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/LTo6UDdyJwOOvEVPvLXCrrrUtdnniCEXAMPLE/IvU1dYUg2RVAJBanLiHb4IgRmpRV3zrkuWJOgQs8IZZaIv2BXIa2R4OlgkBN9bkUDNCJiBeb/AXlzBBko7b15fjrBs2+cTQtpZ3CYWFXG8C5zqx37wnOE49mRl/+OtkIKGO7fAE" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "", + "id": "to-get-temporary-credentials-for-an-iam-user-or-an-aws-account-1480540814038", + "title": "To get temporary credentials for an IAM user or an AWS account" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sts/2011-06-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sts/2011-06-15/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sts/2011-06-15/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sts/2011-06-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sts/2011-06-15/service-2.json.gz new file mode 100644 index 0000000..6310f81 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sts/2011-06-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/supplychain/2024-01-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/supplychain/2024-01-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..f99e4bc Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/supplychain/2024-01-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/supplychain/2024-01-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/supplychain/2024-01-01/paginators-1.json new file mode 100644 index 0000000..cd55b30 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/supplychain/2024-01-01/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListDataIntegrationFlows": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "flows" + }, + "ListDataLakeDatasets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "datasets" + }, + "ListInstances": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "instances" + }, + "ListDataIntegrationEvents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "events" + }, + "ListDataIntegrationFlowExecutions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "flowExecutions" + }, + "ListDataLakeNamespaces": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "namespaces" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/supplychain/2024-01-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/supplychain/2024-01-01/service-2.json.gz new file mode 100644 index 0000000..456d63d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/supplychain/2024-01-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/support-app/2021-08-20/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/support-app/2021-08-20/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..d012c83 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/support-app/2021-08-20/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/support-app/2021-08-20/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/support-app/2021-08-20/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/support-app/2021-08-20/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/support-app/2021-08-20/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/support-app/2021-08-20/service-2.json.gz new file mode 100644 index 0000000..f9b434f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/support-app/2021-08-20/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/support/2013-04-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/support/2013-04-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..af1fa28 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/support/2013-04-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/support/2013-04-15/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/support/2013-04-15/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/support/2013-04-15/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/support/2013-04-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/support/2013-04-15/paginators-1.json new file mode 100644 index 0000000..11bdb62 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/support/2013-04-15/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "DescribeCases": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "cases" + }, + "DescribeCommunications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "communications" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/support/2013-04-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/support/2013-04-15/service-2.json.gz new file mode 100644 index 0000000..048dcf4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/support/2013-04-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/supportauthz/2026-06-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/supportauthz/2026-06-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..9c38bbd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/supportauthz/2026-06-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/supportauthz/2026-06-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/supportauthz/2026-06-30/paginators-1.json new file mode 100644 index 0000000..aa0084f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/supportauthz/2026-06-30/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListActions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "actionSummaries" + }, + "ListSupportPermitRequests": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "supportPermitRequests" + }, + "ListSupportPermits": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "supportPermits" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/supportauthz/2026-06-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/supportauthz/2026-06-30/service-2.json.gz new file mode 100644 index 0000000..590a2a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/supportauthz/2026-06-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/supportauthz/2026-06-30/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/supportauthz/2026-06-30/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/supportauthz/2026-06-30/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sustainability/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sustainability/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..61170c3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sustainability/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/sustainability/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/sustainability/2018-05-10/paginators-1.json new file mode 100644 index 0000000..41156c5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/sustainability/2018-05-10/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "GetEstimatedCarbonEmissions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Results" + }, + "GetEstimatedCarbonEmissionsDimensionValues": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Results" + }, + "GetEstimatedWaterAllocation": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Results" + }, + "GetEstimatedWaterAllocationDimensionValues": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Results" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/sustainability/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/sustainability/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..10d543e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/sustainability/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/swf/2012-01-25/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/swf/2012-01-25/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a5de69f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/swf/2012-01-25/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/swf/2012-01-25/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/swf/2012-01-25/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/swf/2012-01-25/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/swf/2012-01-25/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/swf/2012-01-25/paginators-1.json new file mode 100644 index 0000000..e92bfeb --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/swf/2012-01-25/paginators-1.json @@ -0,0 +1,53 @@ +{ + "pagination": { + "GetWorkflowExecutionHistory": { + "limit_key": "maximumPageSize", + "input_token": "nextPageToken", + "output_token": "nextPageToken", + "result_key": "events" + }, + "ListActivityTypes": { + "limit_key": "maximumPageSize", + "input_token": "nextPageToken", + "output_token": "nextPageToken", + "result_key": "typeInfos" + }, + "ListClosedWorkflowExecutions": { + "limit_key": "maximumPageSize", + "input_token": "nextPageToken", + "output_token": "nextPageToken", + "result_key": "executionInfos" + }, + "ListDomains": { + "limit_key": "maximumPageSize", + "input_token": "nextPageToken", + "output_token": "nextPageToken", + "result_key": "domainInfos" + }, + "ListOpenWorkflowExecutions": { + "limit_key": "maximumPageSize", + "input_token": "nextPageToken", + "output_token": "nextPageToken", + "result_key": "executionInfos" + }, + "ListWorkflowTypes": { + "limit_key": "maximumPageSize", + "input_token": "nextPageToken", + "output_token": "nextPageToken", + "result_key": "typeInfos" + }, + "PollForDecisionTask": { + "limit_key": "maximumPageSize", + "input_token": "nextPageToken", + "output_token": "nextPageToken", + "result_key": "events", + "non_aggregate_keys": [ + "taskToken", + "startedEventId", + "workflowExecution", + "workflowType", + "previousStartedEventId" + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/swf/2012-01-25/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/swf/2012-01-25/service-2.json.gz new file mode 100644 index 0000000..39a6fd1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/swf/2012-01-25/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/synthetics/2017-10-11/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/synthetics/2017-10-11/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..579cbb3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/synthetics/2017-10-11/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/synthetics/2017-10-11/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/synthetics/2017-10-11/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/synthetics/2017-10-11/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/synthetics/2017-10-11/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/synthetics/2017-10-11/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/synthetics/2017-10-11/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/synthetics/2017-10-11/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/synthetics/2017-10-11/service-2.json.gz new file mode 100644 index 0000000..92f48fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/synthetics/2017-10-11/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/taxsettings/2018-05-10/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/taxsettings/2018-05-10/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1c3ae20 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/taxsettings/2018-05-10/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/taxsettings/2018-05-10/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/taxsettings/2018-05-10/paginators-1.json new file mode 100644 index 0000000..bd57abc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/taxsettings/2018-05-10/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListTaxRegistrations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "accountDetails" + }, + "ListSupplementalTaxRegistrations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "taxRegistrations" + }, + "ListTaxExemptions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "taxExemptionDetailsMap" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/taxsettings/2018-05-10/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/taxsettings/2018-05-10/service-2.json.gz new file mode 100644 index 0000000..a59ca61 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/taxsettings/2018-05-10/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/taxsettings/2018-05-10/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/taxsettings/2018-05-10/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/taxsettings/2018-05-10/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/textract/2018-06-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/textract/2018-06-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4b8e7aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/textract/2018-06-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/textract/2018-06-27/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/textract/2018-06-27/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/textract/2018-06-27/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/textract/2018-06-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/textract/2018-06-27/paginators-1.json new file mode 100644 index 0000000..f0d0405 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/textract/2018-06-27/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListAdapterVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AdapterVersions" + }, + "ListAdapters": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Adapters" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/textract/2018-06-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/textract/2018-06-27/service-2.json.gz new file mode 100644 index 0000000..b7372d4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/textract/2018-06-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/timestream-influxdb/2023-01-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/timestream-influxdb/2023-01-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8ed0900 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/timestream-influxdb/2023-01-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/timestream-influxdb/2023-01-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/timestream-influxdb/2023-01-27/paginators-1.json new file mode 100644 index 0000000..10eecd3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/timestream-influxdb/2023-01-27/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListDbInstances": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDbParameterGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDbClusters": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDbInstancesForCluster": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDbBackups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/timestream-influxdb/2023-01-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/timestream-influxdb/2023-01-27/service-2.json.gz new file mode 100644 index 0000000..f513f78 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/timestream-influxdb/2023-01-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/timestream-query/2018-11-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/timestream-query/2018-11-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4b73a81 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/timestream-query/2018-11-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/timestream-query/2018-11-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/timestream-query/2018-11-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/timestream-query/2018-11-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/timestream-query/2018-11-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/timestream-query/2018-11-01/paginators-1.json new file mode 100644 index 0000000..a20456b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/timestream-query/2018-11-01/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "Query": { + "input_token": "NextToken", + "limit_key": "MaxRows", + "non_aggregate_keys": [ + "ColumnInfo", + "QueryId", + "QueryStatus", + "QueryInsightsResponse" + ], + "output_token": "NextToken", + "result_key": "Rows" + }, + "ListScheduledQueries": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ScheduledQueries" + }, + "ListTagsForResource": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Tags" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/timestream-query/2018-11-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/timestream-query/2018-11-01/service-2.json.gz new file mode 100644 index 0000000..ae62f62 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/timestream-query/2018-11-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/timestream-write/2018-11-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/timestream-write/2018-11-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3dd8cd7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/timestream-write/2018-11-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/timestream-write/2018-11-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/timestream-write/2018-11-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/timestream-write/2018-11-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/timestream-write/2018-11-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/timestream-write/2018-11-01/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/timestream-write/2018-11-01/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/timestream-write/2018-11-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/timestream-write/2018-11-01/service-2.json.gz new file mode 100644 index 0000000..aad67a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/timestream-write/2018-11-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/tnb/2008-10-21/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/tnb/2008-10-21/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6778164 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/tnb/2008-10-21/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/tnb/2008-10-21/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/tnb/2008-10-21/paginators-1.json new file mode 100644 index 0000000..18ac477 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/tnb/2008-10-21/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListSolFunctionInstances": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "functionInstances" + }, + "ListSolFunctionPackages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "functionPackages" + }, + "ListSolNetworkInstances": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "networkInstances" + }, + "ListSolNetworkOperations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "networkOperations" + }, + "ListSolNetworkPackages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "networkPackages" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/tnb/2008-10-21/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/tnb/2008-10-21/service-2.json.gz new file mode 100644 index 0000000..ecb3e0e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/tnb/2008-10-21/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..7f0d916 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/service-2.json.gz new file mode 100644 index 0000000..0ba0a1b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/waiters-2.json new file mode 100644 index 0000000..db0329a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/transcribe/2017-10-26/waiters-2.json @@ -0,0 +1,138 @@ +{ + "version": 2, + "waiters": { + "CallAnalyticsJobCompleted": { + "operation": "GetCallAnalyticsJob", + "delay": 10, + "maxAttempts": 180, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "CallAnalyticsJob.CallAnalyticsJobStatus", + "expected": "COMPLETED" + }, + { + "state": "failure", + "matcher": "path", + "argument": "CallAnalyticsJob.CallAnalyticsJobStatus", + "expected": "FAILED" + } + ] + }, + "LanguageModelCompleted": { + "operation": "DescribeLanguageModel", + "delay": 120, + "maxAttempts": 180, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "LanguageModel.ModelStatus", + "expected": "COMPLETED" + }, + { + "state": "failure", + "matcher": "path", + "argument": "LanguageModel.ModelStatus", + "expected": "FAILED" + } + ] + }, + "MedicalScribeJobCompleted": { + "operation": "GetMedicalScribeJob", + "delay": 10, + "maxAttempts": 180, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "MedicalScribeJob.MedicalScribeJobStatus", + "expected": "COMPLETED" + }, + { + "state": "failure", + "matcher": "path", + "argument": "MedicalScribeJob.MedicalScribeJobStatus", + "expected": "FAILED" + } + ] + }, + "MedicalTranscriptionJobCompleted": { + "operation": "GetMedicalTranscriptionJob", + "delay": 10, + "maxAttempts": 180, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "MedicalTranscriptionJob.TranscriptionJobStatus", + "expected": "COMPLETED" + }, + { + "state": "failure", + "matcher": "path", + "argument": "MedicalTranscriptionJob.TranscriptionJobStatus", + "expected": "FAILED" + } + ] + }, + "MedicalVocabularyReady": { + "operation": "GetMedicalVocabulary", + "delay": 10, + "maxAttempts": 180, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "VocabularyState", + "expected": "READY" + }, + { + "state": "failure", + "matcher": "path", + "argument": "VocabularyState", + "expected": "FAILED" + } + ] + }, + "TranscriptionJobCompleted": { + "operation": "GetTranscriptionJob", + "delay": 10, + "maxAttempts": 180, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "TranscriptionJob.TranscriptionJobStatus", + "expected": "COMPLETED" + }, + { + "state": "failure", + "matcher": "path", + "argument": "TranscriptionJob.TranscriptionJobStatus", + "expected": "FAILED" + } + ] + }, + "VocabularyReady": { + "operation": "GetVocabulary", + "delay": 10, + "maxAttempts": 180, + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "VocabularyState", + "expected": "READY" + }, + { + "state": "failure", + "matcher": "path", + "argument": "VocabularyState", + "expected": "FAILED" + } + ] + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4b7d615 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/paginators-1.json new file mode 100644 index 0000000..a343b61 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/paginators-1.json @@ -0,0 +1,94 @@ +{ + "pagination": { + "ListServers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Servers" + }, + "ListAccesses": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ServerId" + ], + "output_token": "NextToken", + "result_key": "Accesses" + }, + "ListExecutions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "WorkflowId" + ], + "output_token": "NextToken", + "result_key": "Executions" + }, + "ListSecurityPolicies": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "SecurityPolicyNames" + }, + "ListTagsForResource": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "Arn" + ], + "output_token": "NextToken", + "result_key": "Tags" + }, + "ListUsers": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "non_aggregate_keys": [ + "ServerId" + ], + "output_token": "NextToken", + "result_key": "Users" + }, + "ListWorkflows": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Workflows" + }, + "ListAgreements": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Agreements" + }, + "ListCertificates": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Certificates" + }, + "ListConnectors": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Connectors" + }, + "ListProfiles": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Profiles" + }, + "ListFileTransferResults": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FileTransferResults" + }, + "ListWebApps": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "WebApps" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/service-2.json.gz new file mode 100644 index 0000000..cad4ac9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/waiters-2.json new file mode 100644 index 0000000..ddcd604 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/transfer/2018-11-05/waiters-2.json @@ -0,0 +1,37 @@ +{ + "version" : 2, + "waiters" : { + "ServerOffline" : { + "delay" : 30, + "maxAttempts" : 120, + "operation" : "DescribeServer", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Server.State", + "state" : "success", + "expected" : "OFFLINE" + }, { + "matcher" : "path", + "argument" : "Server.State", + "state" : "failure", + "expected" : "STOP_FAILED" + } ] + }, + "ServerOnline" : { + "delay" : 30, + "maxAttempts" : 120, + "operation" : "DescribeServer", + "acceptors" : [ { + "matcher" : "path", + "argument" : "Server.State", + "state" : "success", + "expected" : "ONLINE" + }, { + "matcher" : "path", + "argument" : "Server.State", + "state" : "failure", + "expected" : "START_FAILED" + } ] + } + } +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/botocore/data/translate/2017-07-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/translate/2017-07-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..3706f1c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/translate/2017-07-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/translate/2017-07-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/translate/2017-07-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/translate/2017-07-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/translate/2017-07-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/translate/2017-07-01/paginators-1.json new file mode 100644 index 0000000..6898cd4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/translate/2017-07-01/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListTerminologies": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "TerminologyPropertiesList" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/translate/2017-07-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/translate/2017-07-01/service-2.json.gz new file mode 100644 index 0000000..78be4c6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/translate/2017-07-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/trustedadvisor/2022-09-15/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/trustedadvisor/2022-09-15/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b61ce60 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/trustedadvisor/2022-09-15/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/trustedadvisor/2022-09-15/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/trustedadvisor/2022-09-15/paginators-1.json new file mode 100644 index 0000000..f21fde7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/trustedadvisor/2022-09-15/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "ListChecks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "checkSummaries" + }, + "ListOrganizationRecommendationAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "accountRecommendationLifecycleSummaries" + }, + "ListOrganizationRecommendationResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "organizationRecommendationResourceSummaries" + }, + "ListOrganizationRecommendations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "organizationRecommendationSummaries" + }, + "ListRecommendationResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recommendationResourceSummaries" + }, + "ListRecommendations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recommendationSummaries" + }, + "ListRecommendationsForResource": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "recommendationForResourceSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/trustedadvisor/2022-09-15/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/trustedadvisor/2022-09-15/service-2.json.gz new file mode 100644 index 0000000..2531cd9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/trustedadvisor/2022-09-15/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/trustedadvisor/2022-09-15/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/trustedadvisor/2022-09-15/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/trustedadvisor/2022-09-15/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/uxc/2024-07-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/uxc/2024-07-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..67120a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/uxc/2024-07-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/uxc/2024-07-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/uxc/2024-07-01/paginators-1.json new file mode 100644 index 0000000..fdf6e28 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/uxc/2024-07-01/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListServices": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "services" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/uxc/2024-07-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/uxc/2024-07-01/service-2.json.gz new file mode 100644 index 0000000..f162302 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/uxc/2024-07-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/uxc/2024-07-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/uxc/2024-07-01/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/uxc/2024-07-01/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/verifiedpermissions/2021-12-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/verifiedpermissions/2021-12-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..2b95b0d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/verifiedpermissions/2021-12-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/verifiedpermissions/2021-12-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/verifiedpermissions/2021-12-01/paginators-1.json new file mode 100644 index 0000000..d9d43b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/verifiedpermissions/2021-12-01/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListIdentitySources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "identitySources" + }, + "ListPolicies": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policies" + }, + "ListPolicyStores": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policyStores" + }, + "ListPolicyTemplates": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policyTemplates" + }, + "ListPolicyStoreAliases": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "policyStoreAliases" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/verifiedpermissions/2021-12-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/verifiedpermissions/2021-12-01/service-2.json.gz new file mode 100644 index 0000000..53ed98c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/verifiedpermissions/2021-12-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/verifiedpermissions/2021-12-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/verifiedpermissions/2021-12-01/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/verifiedpermissions/2021-12-01/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/voice-id/2021-09-27/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/voice-id/2021-09-27/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1bb6655 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/voice-id/2021-09-27/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/voice-id/2021-09-27/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/voice-id/2021-09-27/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/voice-id/2021-09-27/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/voice-id/2021-09-27/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/voice-id/2021-09-27/paginators-1.json new file mode 100644 index 0000000..49dd7cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/voice-id/2021-09-27/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListDomains": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DomainSummaries" + }, + "ListFraudsterRegistrationJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "JobSummaries" + }, + "ListSpeakerEnrollmentJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "JobSummaries" + }, + "ListSpeakers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "SpeakerSummaries" + }, + "ListFraudsters": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "FraudsterSummaries" + }, + "ListWatchlists": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "WatchlistSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/voice-id/2021-09-27/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/voice-id/2021-09-27/service-2.json.gz new file mode 100644 index 0000000..234383a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/voice-id/2021-09-27/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/vpc-lattice/2022-11-30/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/vpc-lattice/2022-11-30/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..4bcbd1b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/vpc-lattice/2022-11-30/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/vpc-lattice/2022-11-30/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/vpc-lattice/2022-11-30/paginators-1.json new file mode 100644 index 0000000..7bb8082 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/vpc-lattice/2022-11-30/paginators-1.json @@ -0,0 +1,94 @@ +{ + "pagination": { + "ListAccessLogSubscriptions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListListeners": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListRules": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListServiceNetworkServiceAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListServiceNetworkVpcAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListServiceNetworks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListServices": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListTargetGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListTargets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListResourceConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListResourceEndpointAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListResourceGateways": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListServiceNetworkResourceAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListServiceNetworkVpcEndpointAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListDomainVerifications": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/vpc-lattice/2022-11-30/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/vpc-lattice/2022-11-30/service-2.json.gz new file mode 100644 index 0000000..2b95263 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/vpc-lattice/2022-11-30/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/vpc-lattice/2022-11-30/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/vpc-lattice/2022-11-30/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/vpc-lattice/2022-11-30/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/waf-regional/2016-11-28/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/waf-regional/2016-11-28/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..1ac3f68 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/waf-regional/2016-11-28/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/waf-regional/2016-11-28/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/waf-regional/2016-11-28/examples-1.json new file mode 100644 index 0000000..eee5b6f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/waf-regional/2016-11-28/examples-1.json @@ -0,0 +1,1017 @@ +{ + "version": "1.0", + "examples": { + "CreateIPSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Name": "MyIPSetFriendlyName" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "IPSet": { + "IPSetDescriptors": [ + { + "Type": "IPV4", + "Value": "192.0.2.44/32" + } + ], + "IPSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "Name": "MyIPSetFriendlyName" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates an IP match set named MyIPSetFriendlyName.", + "id": "createipset-1472501003122", + "title": "To create an IP set" + } + ], + "CreateRule": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "MetricName": "WAFByteHeaderRule", + "Name": "WAFByteHeaderRule" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Rule": { + "MetricName": "WAFByteHeaderRule", + "Name": "WAFByteHeaderRule", + "Predicates": [ + { + "DataId": "MyByteMatchSetID", + "Negated": false, + "Type": "ByteMatch" + } + ], + "RuleId": "WAFRule-1-Example" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a rule named WAFByteHeaderRule.", + "id": "createrule-1474072675555", + "title": "To create a rule" + } + ], + "CreateSizeConstraintSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Name": "MySampleSizeConstraintSet" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "SizeConstraintSet": { + "Name": "MySampleSizeConstraintSet", + "SizeConstraintSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "SizeConstraints": [ + { + "ComparisonOperator": "GT", + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "Size": 0, + "TextTransformation": "NONE" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates size constraint set named MySampleSizeConstraintSet.", + "id": "createsizeconstraint-1474299140754", + "title": "To create a size constraint" + } + ], + "CreateSqlInjectionMatchSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Name": "MySQLInjectionMatchSet" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "SqlInjectionMatchSet": { + "Name": "MySQLInjectionMatchSet", + "SqlInjectionMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "SqlInjectionMatchTuples": [ + { + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "TextTransformation": "URL_DECODE" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a SQL injection match set named MySQLInjectionMatchSet.", + "id": "createsqlinjectionmatchset-1474492796105", + "title": "To create a SQL injection match set" + } + ], + "CreateWebACL": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "DefaultAction": { + "Type": "ALLOW" + }, + "MetricName": "CreateExample", + "Name": "CreateExample" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "WebACL": { + "DefaultAction": { + "Type": "ALLOW" + }, + "MetricName": "CreateExample", + "Name": "CreateExample", + "Rules": [ + { + "Action": { + "Type": "ALLOW" + }, + "Priority": 1, + "RuleId": "WAFRule-1-Example" + } + ], + "WebACLId": "example-46da-4444-5555-example" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a web ACL named CreateExample.", + "id": "createwebacl-1472061481310", + "title": "To create a web ACL" + } + ], + "CreateXssMatchSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Name": "MySampleXssMatchSet" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "XssMatchSet": { + "Name": "MySampleXssMatchSet", + "XssMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "XssMatchTuples": [ + { + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "TextTransformation": "URL_DECODE" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates an XSS match set named MySampleXssMatchSet.", + "id": "createxssmatchset-1474560868500", + "title": "To create an XSS match set" + } + ], + "DeleteByteMatchSet": [ + { + "input": { + "ByteMatchSetId": "exampleIDs3t-46da-4fdb-b8d5-abc321j569j5", + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a byte match set with the ID exampleIDs3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "deletebytematchset-1473367566229", + "title": "To delete a byte match set" + } + ], + "DeleteIPSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "IPSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an IP match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "deleteipset-1472767434306", + "title": "To delete an IP set" + } + ], + "DeleteRule": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "RuleId": "WAFRule-1-Example" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a rule with the ID WAFRule-1-Example.", + "id": "deleterule-1474073108749", + "title": "To delete a rule" + } + ], + "DeleteSizeConstraintSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "SizeConstraintSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a size constraint set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "deletesizeconstraintset-1474299857905", + "title": "To delete a size constraint set" + } + ], + "DeleteSqlInjectionMatchSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "SqlInjectionMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a SQL injection match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "deletesqlinjectionmatchset-1474493373197", + "title": "To delete a SQL injection match set" + } + ], + "DeleteWebACL": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "WebACLId": "example-46da-4444-5555-example" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a web ACL with the ID example-46da-4444-5555-example.", + "id": "deletewebacl-1472767755931", + "title": "To delete a web ACL" + } + ], + "DeleteXssMatchSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "XssMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an XSS match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "deletexssmatchset-1474561302618", + "title": "To delete an XSS match set" + } + ], + "GetByteMatchSet": [ + { + "input": { + "ByteMatchSetId": "exampleIDs3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "ByteMatchSet": { + "ByteMatchSetId": "exampleIDs3t-46da-4fdb-b8d5-abc321j569j5", + "ByteMatchTuples": [ + { + "FieldToMatch": { + "Data": "referer", + "Type": "HEADER" + }, + "PositionalConstraint": "CONTAINS", + "TargetString": "badrefer1", + "TextTransformation": "NONE" + } + ], + "Name": "ByteMatchNameExample" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of a byte match set with the ID exampleIDs3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "getbytematchset-1473273311532", + "title": "To get a byte match set" + } + ], + "GetChangeToken": [ + { + "input": { + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns a change token to use for a create, update or delete operation.", + "id": "get-change-token-example-1471635120794", + "title": "To get a change token" + } + ], + "GetChangeTokenStatus": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "output": { + "ChangeTokenStatus": "PENDING" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the status of a change token with the ID abcd12f2-46da-4fdb-b8d5-fbd4c466928f.", + "id": "getchangetokenstatus-1474658417107", + "title": "To get the change token status" + } + ], + "GetIPSet": [ + { + "input": { + "IPSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "IPSet": { + "IPSetDescriptors": [ + { + "Type": "IPV4", + "Value": "192.0.2.44/32" + } + ], + "IPSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "Name": "MyIPSetFriendlyName" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of an IP match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "getipset-1474658688675", + "title": "To get an IP set" + } + ], + "GetRule": [ + { + "input": { + "RuleId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "Rule": { + "MetricName": "WAFByteHeaderRule", + "Name": "WAFByteHeaderRule", + "Predicates": [ + { + "DataId": "MyByteMatchSetID", + "Negated": false, + "Type": "ByteMatch" + } + ], + "RuleId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of a rule with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "getrule-1474659238790", + "title": "To get a rule" + } + ], + "GetSampledRequests": [ + { + "input": { + "MaxItems": 100, + "RuleId": "WAFRule-1-Example", + "TimeWindow": { + "EndTime": "2016-09-27T15:50Z", + "StartTime": "2016-09-27T15:50Z" + }, + "WebAclId": "createwebacl-1472061481310" + }, + "output": { + "PopulationSize": 50, + "SampledRequests": [ + { + "Action": "BLOCK", + "Request": { + "ClientIP": "192.0.2.44", + "Country": "US", + "HTTPVersion": "HTTP/1.1", + "Headers": [ + { + "Name": "User-Agent", + "Value": "BadBot " + } + ], + "Method": "HEAD" + }, + "Timestamp": "2016-09-27T14:55Z", + "Weight": 1 + } + ], + "TimeWindow": { + "EndTime": "2016-09-27T15:50Z", + "StartTime": "2016-09-27T14:50Z" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns detailed information about 100 requests --a sample-- that AWS WAF randomly selects from among the first 5,000 requests that your AWS resource received between the time period 2016-09-27T15:50Z to 2016-09-27T15:50Z.", + "id": "getsampledrequests-1474927997195", + "title": "To get a sampled requests" + } + ], + "GetSizeConstraintSet": [ + { + "input": { + "SizeConstraintSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "SizeConstraintSet": { + "Name": "MySampleSizeConstraintSet", + "SizeConstraintSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "SizeConstraints": [ + { + "ComparisonOperator": "GT", + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "Size": 0, + "TextTransformation": "NONE" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of a size constraint match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "getsizeconstraintset-1475005422493", + "title": "To get a size constraint set" + } + ], + "GetSqlInjectionMatchSet": [ + { + "input": { + "SqlInjectionMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "SqlInjectionMatchSet": { + "Name": "MySQLInjectionMatchSet", + "SqlInjectionMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "SqlInjectionMatchTuples": [ + { + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "TextTransformation": "URL_DECODE" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of a SQL injection match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "getsqlinjectionmatchset-1475005940137", + "title": "To get a SQL injection match set" + } + ], + "GetWebACL": [ + { + "input": { + "WebACLId": "createwebacl-1472061481310" + }, + "output": { + "WebACL": { + "DefaultAction": { + "Type": "ALLOW" + }, + "MetricName": "CreateExample", + "Name": "CreateExample", + "Rules": [ + { + "Action": { + "Type": "ALLOW" + }, + "Priority": 1, + "RuleId": "WAFRule-1-Example" + } + ], + "WebACLId": "createwebacl-1472061481310" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of a web ACL with the ID createwebacl-1472061481310.", + "id": "getwebacl-1475006348525", + "title": "To get a web ACL" + } + ], + "GetXssMatchSet": [ + { + "input": { + "XssMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "XssMatchSet": { + "Name": "MySampleXssMatchSet", + "XssMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "XssMatchTuples": [ + { + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "TextTransformation": "URL_DECODE" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of an XSS match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "getxssmatchset-1475187879017", + "title": "To get an XSS match set" + } + ], + "ListIPSets": [ + { + "input": { + "Limit": 100 + }, + "output": { + "IPSets": [ + { + "IPSetId": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Name": "MyIPSetFriendlyName" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns an array of up to 100 IP match sets.", + "id": "listipsets-1472235676229", + "title": "To list IP sets" + } + ], + "ListRules": [ + { + "input": { + "Limit": 100 + }, + "output": { + "Rules": [ + { + "Name": "WAFByteHeaderRule", + "RuleId": "WAFRule-1-Example" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns an array of up to 100 rules.", + "id": "listrules-1475258406433", + "title": "To list rules" + } + ], + "ListSizeConstraintSets": [ + { + "input": { + "Limit": 100 + }, + "output": { + "SizeConstraintSets": [ + { + "Name": "MySampleSizeConstraintSet", + "SizeConstraintSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns an array of up to 100 size contraint match sets.", + "id": "listsizeconstraintsets-1474300067597", + "title": "To list a size constraint sets" + } + ], + "ListSqlInjectionMatchSets": [ + { + "input": { + "Limit": 100 + }, + "output": { + "SqlInjectionMatchSets": [ + { + "Name": "MySQLInjectionMatchSet", + "SqlInjectionMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns an array of up to 100 SQL injection match sets.", + "id": "listsqlinjectionmatchset-1474493560103", + "title": "To list SQL injection match sets" + } + ], + "ListWebACLs": [ + { + "input": { + "Limit": 100 + }, + "output": { + "WebACLs": [ + { + "Name": "WebACLexample", + "WebACLId": "webacl-1472061481310" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns an array of up to 100 web ACLs.", + "id": "listwebacls-1475258732691", + "title": "To list Web ACLs" + } + ], + "ListXssMatchSets": [ + { + "input": { + "Limit": 100 + }, + "output": { + "XssMatchSets": [ + { + "Name": "MySampleXssMatchSet", + "XssMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns an array of up to 100 XSS match sets.", + "id": "listxssmatchsets-1474561481168", + "title": "To list XSS match sets" + } + ], + "UpdateByteMatchSet": [ + { + "input": { + "ByteMatchSetId": "exampleIDs3t-46da-4fdb-b8d5-abc321j569j5", + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Updates": [ + { + "Action": "DELETE", + "ByteMatchTuple": { + "FieldToMatch": { + "Data": "referer", + "Type": "HEADER" + }, + "PositionalConstraint": "CONTAINS", + "TargetString": "badrefer1", + "TextTransformation": "NONE" + } + } + ] + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a ByteMatchTuple object (filters) in an byte match set with the ID exampleIDs3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "updatebytematchset-1475259074558", + "title": "To update a byte match set" + } + ], + "UpdateIPSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "IPSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "Updates": [ + { + "Action": "DELETE", + "IPSetDescriptor": { + "Type": "IPV4", + "Value": "192.0.2.44/32" + } + } + ] + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an IPSetDescriptor object in an IP match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "updateipset-1475259733625", + "title": "To update an IP set" + } + ], + "UpdateRule": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "RuleId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "Updates": [ + { + "Action": "DELETE", + "Predicate": { + "DataId": "MyByteMatchSetID", + "Negated": false, + "Type": "ByteMatch" + } + } + ] + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a Predicate object in a rule with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "updaterule-1475260064720", + "title": "To update a rule" + } + ], + "UpdateSizeConstraintSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "SizeConstraintSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "Updates": [ + { + "Action": "DELETE", + "SizeConstraint": { + "ComparisonOperator": "GT", + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "Size": 0, + "TextTransformation": "NONE" + } + } + ] + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a SizeConstraint object (filters) in a size constraint set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "updatesizeconstraintset-1475531697891", + "title": "To update a size constraint set" + } + ], + "UpdateSqlInjectionMatchSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "SqlInjectionMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "Updates": [ + { + "Action": "DELETE", + "SqlInjectionMatchTuple": { + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "TextTransformation": "URL_DECODE" + } + } + ] + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a SqlInjectionMatchTuple object (filters) in a SQL injection match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "updatesqlinjectionmatchset-1475532094686", + "title": "To update a SQL injection match set" + } + ], + "UpdateWebACL": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "DefaultAction": { + "Type": "ALLOW" + }, + "Updates": [ + { + "Action": "DELETE", + "ActivatedRule": { + "Action": { + "Type": "ALLOW" + }, + "Priority": 1, + "RuleId": "WAFRule-1-Example" + } + } + ], + "WebACLId": "webacl-1472061481310" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an ActivatedRule object in a WebACL with the ID webacl-1472061481310.", + "id": "updatewebacl-1475533627385", + "title": "To update a Web ACL" + } + ], + "UpdateXssMatchSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Updates": [ + { + "Action": "DELETE", + "XssMatchTuple": { + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "TextTransformation": "URL_DECODE" + } + } + ], + "XssMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an XssMatchTuple object (filters) in an XssMatchSet with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "updatexssmatchset-1475534098881", + "title": "To update an XSS match set" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/waf-regional/2016-11-28/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/waf-regional/2016-11-28/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/waf-regional/2016-11-28/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/waf-regional/2016-11-28/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/waf-regional/2016-11-28/service-2.json.gz new file mode 100644 index 0000000..ac33119 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/waf-regional/2016-11-28/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/waf/2015-08-24/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/waf/2015-08-24/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..60768a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/waf/2015-08-24/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/waf/2015-08-24/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/waf/2015-08-24/examples-1.json new file mode 100644 index 0000000..eee5b6f --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/waf/2015-08-24/examples-1.json @@ -0,0 +1,1017 @@ +{ + "version": "1.0", + "examples": { + "CreateIPSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Name": "MyIPSetFriendlyName" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "IPSet": { + "IPSetDescriptors": [ + { + "Type": "IPV4", + "Value": "192.0.2.44/32" + } + ], + "IPSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "Name": "MyIPSetFriendlyName" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates an IP match set named MyIPSetFriendlyName.", + "id": "createipset-1472501003122", + "title": "To create an IP set" + } + ], + "CreateRule": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "MetricName": "WAFByteHeaderRule", + "Name": "WAFByteHeaderRule" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Rule": { + "MetricName": "WAFByteHeaderRule", + "Name": "WAFByteHeaderRule", + "Predicates": [ + { + "DataId": "MyByteMatchSetID", + "Negated": false, + "Type": "ByteMatch" + } + ], + "RuleId": "WAFRule-1-Example" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a rule named WAFByteHeaderRule.", + "id": "createrule-1474072675555", + "title": "To create a rule" + } + ], + "CreateSizeConstraintSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Name": "MySampleSizeConstraintSet" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "SizeConstraintSet": { + "Name": "MySampleSizeConstraintSet", + "SizeConstraintSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "SizeConstraints": [ + { + "ComparisonOperator": "GT", + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "Size": 0, + "TextTransformation": "NONE" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates size constraint set named MySampleSizeConstraintSet.", + "id": "createsizeconstraint-1474299140754", + "title": "To create a size constraint" + } + ], + "CreateSqlInjectionMatchSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Name": "MySQLInjectionMatchSet" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "SqlInjectionMatchSet": { + "Name": "MySQLInjectionMatchSet", + "SqlInjectionMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "SqlInjectionMatchTuples": [ + { + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "TextTransformation": "URL_DECODE" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a SQL injection match set named MySQLInjectionMatchSet.", + "id": "createsqlinjectionmatchset-1474492796105", + "title": "To create a SQL injection match set" + } + ], + "CreateWebACL": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "DefaultAction": { + "Type": "ALLOW" + }, + "MetricName": "CreateExample", + "Name": "CreateExample" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "WebACL": { + "DefaultAction": { + "Type": "ALLOW" + }, + "MetricName": "CreateExample", + "Name": "CreateExample", + "Rules": [ + { + "Action": { + "Type": "ALLOW" + }, + "Priority": 1, + "RuleId": "WAFRule-1-Example" + } + ], + "WebACLId": "example-46da-4444-5555-example" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates a web ACL named CreateExample.", + "id": "createwebacl-1472061481310", + "title": "To create a web ACL" + } + ], + "CreateXssMatchSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Name": "MySampleXssMatchSet" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "XssMatchSet": { + "Name": "MySampleXssMatchSet", + "XssMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "XssMatchTuples": [ + { + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "TextTransformation": "URL_DECODE" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example creates an XSS match set named MySampleXssMatchSet.", + "id": "createxssmatchset-1474560868500", + "title": "To create an XSS match set" + } + ], + "DeleteByteMatchSet": [ + { + "input": { + "ByteMatchSetId": "exampleIDs3t-46da-4fdb-b8d5-abc321j569j5", + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a byte match set with the ID exampleIDs3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "deletebytematchset-1473367566229", + "title": "To delete a byte match set" + } + ], + "DeleteIPSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "IPSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an IP match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "deleteipset-1472767434306", + "title": "To delete an IP set" + } + ], + "DeleteRule": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "RuleId": "WAFRule-1-Example" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a rule with the ID WAFRule-1-Example.", + "id": "deleterule-1474073108749", + "title": "To delete a rule" + } + ], + "DeleteSizeConstraintSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "SizeConstraintSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a size constraint set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "deletesizeconstraintset-1474299857905", + "title": "To delete a size constraint set" + } + ], + "DeleteSqlInjectionMatchSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "SqlInjectionMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a SQL injection match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "deletesqlinjectionmatchset-1474493373197", + "title": "To delete a SQL injection match set" + } + ], + "DeleteWebACL": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "WebACLId": "example-46da-4444-5555-example" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a web ACL with the ID example-46da-4444-5555-example.", + "id": "deletewebacl-1472767755931", + "title": "To delete a web ACL" + } + ], + "DeleteXssMatchSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "XssMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an XSS match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "deletexssmatchset-1474561302618", + "title": "To delete an XSS match set" + } + ], + "GetByteMatchSet": [ + { + "input": { + "ByteMatchSetId": "exampleIDs3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "ByteMatchSet": { + "ByteMatchSetId": "exampleIDs3t-46da-4fdb-b8d5-abc321j569j5", + "ByteMatchTuples": [ + { + "FieldToMatch": { + "Data": "referer", + "Type": "HEADER" + }, + "PositionalConstraint": "CONTAINS", + "TargetString": "badrefer1", + "TextTransformation": "NONE" + } + ], + "Name": "ByteMatchNameExample" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of a byte match set with the ID exampleIDs3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "getbytematchset-1473273311532", + "title": "To get a byte match set" + } + ], + "GetChangeToken": [ + { + "input": { + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns a change token to use for a create, update or delete operation.", + "id": "get-change-token-example-1471635120794", + "title": "To get a change token" + } + ], + "GetChangeTokenStatus": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "output": { + "ChangeTokenStatus": "PENDING" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the status of a change token with the ID abcd12f2-46da-4fdb-b8d5-fbd4c466928f.", + "id": "getchangetokenstatus-1474658417107", + "title": "To get the change token status" + } + ], + "GetIPSet": [ + { + "input": { + "IPSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "IPSet": { + "IPSetDescriptors": [ + { + "Type": "IPV4", + "Value": "192.0.2.44/32" + } + ], + "IPSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "Name": "MyIPSetFriendlyName" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of an IP match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "getipset-1474658688675", + "title": "To get an IP set" + } + ], + "GetRule": [ + { + "input": { + "RuleId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "Rule": { + "MetricName": "WAFByteHeaderRule", + "Name": "WAFByteHeaderRule", + "Predicates": [ + { + "DataId": "MyByteMatchSetID", + "Negated": false, + "Type": "ByteMatch" + } + ], + "RuleId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of a rule with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "getrule-1474659238790", + "title": "To get a rule" + } + ], + "GetSampledRequests": [ + { + "input": { + "MaxItems": 100, + "RuleId": "WAFRule-1-Example", + "TimeWindow": { + "EndTime": "2016-09-27T15:50Z", + "StartTime": "2016-09-27T15:50Z" + }, + "WebAclId": "createwebacl-1472061481310" + }, + "output": { + "PopulationSize": 50, + "SampledRequests": [ + { + "Action": "BLOCK", + "Request": { + "ClientIP": "192.0.2.44", + "Country": "US", + "HTTPVersion": "HTTP/1.1", + "Headers": [ + { + "Name": "User-Agent", + "Value": "BadBot " + } + ], + "Method": "HEAD" + }, + "Timestamp": "2016-09-27T14:55Z", + "Weight": 1 + } + ], + "TimeWindow": { + "EndTime": "2016-09-27T15:50Z", + "StartTime": "2016-09-27T14:50Z" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns detailed information about 100 requests --a sample-- that AWS WAF randomly selects from among the first 5,000 requests that your AWS resource received between the time period 2016-09-27T15:50Z to 2016-09-27T15:50Z.", + "id": "getsampledrequests-1474927997195", + "title": "To get a sampled requests" + } + ], + "GetSizeConstraintSet": [ + { + "input": { + "SizeConstraintSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "SizeConstraintSet": { + "Name": "MySampleSizeConstraintSet", + "SizeConstraintSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "SizeConstraints": [ + { + "ComparisonOperator": "GT", + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "Size": 0, + "TextTransformation": "NONE" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of a size constraint match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "getsizeconstraintset-1475005422493", + "title": "To get a size constraint set" + } + ], + "GetSqlInjectionMatchSet": [ + { + "input": { + "SqlInjectionMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "SqlInjectionMatchSet": { + "Name": "MySQLInjectionMatchSet", + "SqlInjectionMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "SqlInjectionMatchTuples": [ + { + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "TextTransformation": "URL_DECODE" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of a SQL injection match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "getsqlinjectionmatchset-1475005940137", + "title": "To get a SQL injection match set" + } + ], + "GetWebACL": [ + { + "input": { + "WebACLId": "createwebacl-1472061481310" + }, + "output": { + "WebACL": { + "DefaultAction": { + "Type": "ALLOW" + }, + "MetricName": "CreateExample", + "Name": "CreateExample", + "Rules": [ + { + "Action": { + "Type": "ALLOW" + }, + "Priority": 1, + "RuleId": "WAFRule-1-Example" + } + ], + "WebACLId": "createwebacl-1472061481310" + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of a web ACL with the ID createwebacl-1472061481310.", + "id": "getwebacl-1475006348525", + "title": "To get a web ACL" + } + ], + "GetXssMatchSet": [ + { + "input": { + "XssMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "XssMatchSet": { + "Name": "MySampleXssMatchSet", + "XssMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "XssMatchTuples": [ + { + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "TextTransformation": "URL_DECODE" + } + ] + } + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns the details of an XSS match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "getxssmatchset-1475187879017", + "title": "To get an XSS match set" + } + ], + "ListIPSets": [ + { + "input": { + "Limit": 100 + }, + "output": { + "IPSets": [ + { + "IPSetId": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Name": "MyIPSetFriendlyName" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns an array of up to 100 IP match sets.", + "id": "listipsets-1472235676229", + "title": "To list IP sets" + } + ], + "ListRules": [ + { + "input": { + "Limit": 100 + }, + "output": { + "Rules": [ + { + "Name": "WAFByteHeaderRule", + "RuleId": "WAFRule-1-Example" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns an array of up to 100 rules.", + "id": "listrules-1475258406433", + "title": "To list rules" + } + ], + "ListSizeConstraintSets": [ + { + "input": { + "Limit": 100 + }, + "output": { + "SizeConstraintSets": [ + { + "Name": "MySampleSizeConstraintSet", + "SizeConstraintSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns an array of up to 100 size contraint match sets.", + "id": "listsizeconstraintsets-1474300067597", + "title": "To list a size constraint sets" + } + ], + "ListSqlInjectionMatchSets": [ + { + "input": { + "Limit": 100 + }, + "output": { + "SqlInjectionMatchSets": [ + { + "Name": "MySQLInjectionMatchSet", + "SqlInjectionMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns an array of up to 100 SQL injection match sets.", + "id": "listsqlinjectionmatchset-1474493560103", + "title": "To list SQL injection match sets" + } + ], + "ListWebACLs": [ + { + "input": { + "Limit": 100 + }, + "output": { + "WebACLs": [ + { + "Name": "WebACLexample", + "WebACLId": "webacl-1472061481310" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns an array of up to 100 web ACLs.", + "id": "listwebacls-1475258732691", + "title": "To list Web ACLs" + } + ], + "ListXssMatchSets": [ + { + "input": { + "Limit": 100 + }, + "output": { + "XssMatchSets": [ + { + "Name": "MySampleXssMatchSet", + "XssMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + } + ] + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example returns an array of up to 100 XSS match sets.", + "id": "listxssmatchsets-1474561481168", + "title": "To list XSS match sets" + } + ], + "UpdateByteMatchSet": [ + { + "input": { + "ByteMatchSetId": "exampleIDs3t-46da-4fdb-b8d5-abc321j569j5", + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Updates": [ + { + "Action": "DELETE", + "ByteMatchTuple": { + "FieldToMatch": { + "Data": "referer", + "Type": "HEADER" + }, + "PositionalConstraint": "CONTAINS", + "TargetString": "badrefer1", + "TextTransformation": "NONE" + } + } + ] + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a ByteMatchTuple object (filters) in an byte match set with the ID exampleIDs3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "updatebytematchset-1475259074558", + "title": "To update a byte match set" + } + ], + "UpdateIPSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "IPSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "Updates": [ + { + "Action": "DELETE", + "IPSetDescriptor": { + "Type": "IPV4", + "Value": "192.0.2.44/32" + } + } + ] + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an IPSetDescriptor object in an IP match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "updateipset-1475259733625", + "title": "To update an IP set" + } + ], + "UpdateRule": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "RuleId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "Updates": [ + { + "Action": "DELETE", + "Predicate": { + "DataId": "MyByteMatchSetID", + "Negated": false, + "Type": "ByteMatch" + } + } + ] + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a Predicate object in a rule with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "updaterule-1475260064720", + "title": "To update a rule" + } + ], + "UpdateSizeConstraintSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "SizeConstraintSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "Updates": [ + { + "Action": "DELETE", + "SizeConstraint": { + "ComparisonOperator": "GT", + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "Size": 0, + "TextTransformation": "NONE" + } + } + ] + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a SizeConstraint object (filters) in a size constraint set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "updatesizeconstraintset-1475531697891", + "title": "To update a size constraint set" + } + ], + "UpdateSqlInjectionMatchSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "SqlInjectionMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5", + "Updates": [ + { + "Action": "DELETE", + "SqlInjectionMatchTuple": { + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "TextTransformation": "URL_DECODE" + } + } + ] + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes a SqlInjectionMatchTuple object (filters) in a SQL injection match set with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "updatesqlinjectionmatchset-1475532094686", + "title": "To update a SQL injection match set" + } + ], + "UpdateWebACL": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "DefaultAction": { + "Type": "ALLOW" + }, + "Updates": [ + { + "Action": "DELETE", + "ActivatedRule": { + "Action": { + "Type": "ALLOW" + }, + "Priority": 1, + "RuleId": "WAFRule-1-Example" + } + } + ], + "WebACLId": "webacl-1472061481310" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an ActivatedRule object in a WebACL with the ID webacl-1472061481310.", + "id": "updatewebacl-1475533627385", + "title": "To update a Web ACL" + } + ], + "UpdateXssMatchSet": [ + { + "input": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f", + "Updates": [ + { + "Action": "DELETE", + "XssMatchTuple": { + "FieldToMatch": { + "Type": "QUERY_STRING" + }, + "TextTransformation": "URL_DECODE" + } + } + ], + "XssMatchSetId": "example1ds3t-46da-4fdb-b8d5-abc321j569j5" + }, + "output": { + "ChangeToken": "abcd12f2-46da-4fdb-b8d5-fbd4c466928f" + }, + "comments": { + "input": { + }, + "output": { + } + }, + "description": "The following example deletes an XssMatchTuple object (filters) in an XssMatchSet with the ID example1ds3t-46da-4fdb-b8d5-abc321j569j5.", + "id": "updatexssmatchset-1475534098881", + "title": "To update an XSS match set" + } + ] + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/waf/2015-08-24/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/waf/2015-08-24/paginators-1.json new file mode 100644 index 0000000..9f2eba8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/waf/2015-08-24/paginators-1.json @@ -0,0 +1,99 @@ +{ + "pagination": { + "ListByteMatchSets": { + "input_token": "NextMarker", + "output_token": "NextMarker", + "limit_key": "Limit", + "result_key": "ByteMatchSets" + }, + "ListIPSets": { + "input_token": "NextMarker", + "output_token": "NextMarker", + "limit_key": "Limit", + "result_key": "IPSets" + }, + "ListRules": { + "input_token": "NextMarker", + "output_token": "NextMarker", + "limit_key": "Limit", + "result_key": "Rules" + }, + "ListSizeConstraintSets": { + "input_token": "NextMarker", + "output_token": "NextMarker", + "limit_key": "Limit", + "result_key": "SizeConstraintSets" + }, + "ListSqlInjectionMatchSets": { + "input_token": "NextMarker", + "output_token": "NextMarker", + "limit_key": "Limit", + "result_key": "SqlInjectionMatchSets" + }, + "ListWebACLs": { + "input_token": "NextMarker", + "output_token": "NextMarker", + "limit_key": "Limit", + "result_key": "WebACLs" + }, + "ListXssMatchSets": { + "input_token": "NextMarker", + "output_token": "NextMarker", + "limit_key": "Limit", + "result_key": "XssMatchSets" + }, + "GetRateBasedRuleManagedKeys": { + "input_token": "NextMarker", + "output_token": "NextMarker", + "result_key": "ManagedKeys" + }, + "ListActivatedRulesInRuleGroup": { + "input_token": "NextMarker", + "limit_key": "Limit", + "output_token": "NextMarker", + "result_key": "ActivatedRules" + }, + "ListGeoMatchSets": { + "input_token": "NextMarker", + "limit_key": "Limit", + "output_token": "NextMarker", + "result_key": "GeoMatchSets" + }, + "ListLoggingConfigurations": { + "input_token": "NextMarker", + "limit_key": "Limit", + "output_token": "NextMarker", + "result_key": "LoggingConfigurations" + }, + "ListRateBasedRules": { + "input_token": "NextMarker", + "limit_key": "Limit", + "output_token": "NextMarker", + "result_key": "Rules" + }, + "ListRegexMatchSets": { + "input_token": "NextMarker", + "limit_key": "Limit", + "output_token": "NextMarker", + "result_key": "RegexMatchSets" + }, + "ListRegexPatternSets": { + "input_token": "NextMarker", + "limit_key": "Limit", + "output_token": "NextMarker", + "result_key": "RegexPatternSets" + }, + "ListRuleGroups": { + "input_token": "NextMarker", + "limit_key": "Limit", + "output_token": "NextMarker", + "result_key": "RuleGroups" + }, + "ListSubscribedRuleGroups": { + "input_token": "NextMarker", + "limit_key": "Limit", + "output_token": "NextMarker", + "result_key": "RuleGroups" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/waf/2015-08-24/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/waf/2015-08-24/service-2.json.gz new file mode 100644 index 0000000..68912c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/waf/2015-08-24/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/wafv2/2019-07-29/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/wafv2/2019-07-29/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..a40ea7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/wafv2/2019-07-29/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/wafv2/2019-07-29/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/wafv2/2019-07-29/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/wafv2/2019-07-29/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/wafv2/2019-07-29/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/wafv2/2019-07-29/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/wafv2/2019-07-29/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/wafv2/2019-07-29/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/wafv2/2019-07-29/service-2.json.gz new file mode 100644 index 0000000..8b97b8a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/wafv2/2019-07-29/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/wellarchitected/2020-03-31/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/wellarchitected/2020-03-31/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c23a5c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/wellarchitected/2020-03-31/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/wellarchitected/2020-03-31/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/wellarchitected/2020-03-31/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/wellarchitected/2020-03-31/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/wellarchitected/2020-03-31/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/wellarchitected/2020-03-31/paginators-1.json new file mode 100644 index 0000000..ccdf3e7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/wellarchitected/2020-03-31/paginators-1.json @@ -0,0 +1,40 @@ +{ + "pagination": { + "ListAgentContexts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAgentGoals": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAgentProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAgentRecommendationGenerations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAgentRecommendationItems": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + }, + "ListAgentRecommendations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/wellarchitected/2020-03-31/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/wellarchitected/2020-03-31/service-2.json.gz new file mode 100644 index 0000000..994fc9b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/wellarchitected/2020-03-31/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/wickr/2024-02-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/wickr/2024-02-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..8837008 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/wickr/2024-02-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/wickr/2024-02-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/wickr/2024-02-01/paginators-1.json new file mode 100644 index 0000000..f51f71e --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/wickr/2024-02-01/paginators-1.json @@ -0,0 +1,52 @@ +{ + "pagination": { + "ListBlockedGuestUsers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "blocklist" + }, + "ListBots": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "bots" + }, + "ListDevicesForUser": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "devices" + }, + "ListGuestUsers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "guestlist" + }, + "ListNetworks": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "networks" + }, + "ListSecurityGroupUsers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "users" + }, + "ListSecurityGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "securityGroups" + }, + "ListUsers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "users" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/wickr/2024-02-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/wickr/2024-02-01/service-2.json.gz new file mode 100644 index 0000000..5302749 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/wickr/2024-02-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/wickr/2024-02-01/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/wickr/2024-02-01/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/wickr/2024-02-01/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/wisdom/2020-10-19/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/wisdom/2020-10-19/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..6c865bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/wisdom/2020-10-19/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/wisdom/2020-10-19/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/wisdom/2020-10-19/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/wisdom/2020-10-19/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/wisdom/2020-10-19/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/wisdom/2020-10-19/paginators-1.json new file mode 100644 index 0000000..60b1dca --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/wisdom/2020-10-19/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "ListAssistantAssociations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assistantAssociationSummaries" + }, + "ListAssistants": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "assistantSummaries" + }, + "ListContents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "contentSummaries" + }, + "ListKnowledgeBases": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "knowledgeBaseSummaries" + }, + "QueryAssistant": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "results" + }, + "SearchContent": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "contentSummaries" + }, + "SearchSessions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sessionSummaries" + }, + "ListImportJobs": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "importJobSummaries" + }, + "ListQuickResponses": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "quickResponseSummaries" + }, + "SearchQuickResponses": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "results" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/wisdom/2020-10-19/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/wisdom/2020-10-19/service-2.json.gz new file mode 100644 index 0000000..f3d64a8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/wisdom/2020-10-19/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workdocs/2016-05-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workdocs/2016-05-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c490694 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workdocs/2016-05-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workdocs/2016-05-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/workdocs/2016-05-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workdocs/2016-05-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workdocs/2016-05-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/workdocs/2016-05-01/paginators-1.json new file mode 100644 index 0000000..ff2f410 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workdocs/2016-05-01/paginators-1.json @@ -0,0 +1,67 @@ +{ + "pagination": { + "DescribeDocumentVersions": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "DocumentVersions" + }, + "DescribeFolderContents": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": [ + "Folders", + "Documents" + ] + }, + "DescribeUsers": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "Users" + }, + "DescribeActivities": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "UserActivities" + }, + "DescribeComments": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "Comments" + }, + "DescribeGroups": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "Groups" + }, + "DescribeNotificationSubscriptions": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "Subscriptions" + }, + "DescribeResourcePermissions": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "Principals" + }, + "DescribeRootFolders": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "Folders" + }, + "SearchResources": { + "input_token": "Marker", + "limit_key": "Limit", + "output_token": "Marker", + "result_key": "Items" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workdocs/2016-05-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workdocs/2016-05-01/service-2.json.gz new file mode 100644 index 0000000..a7c228b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workdocs/2016-05-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workmail/2017-10-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workmail/2017-10-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..0928000 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workmail/2017-10-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workmail/2017-10-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/workmail/2017-10-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workmail/2017-10-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workmail/2017-10-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/workmail/2017-10-01/paginators-1.json new file mode 100644 index 0000000..a4d5bd2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workmail/2017-10-01/paginators-1.json @@ -0,0 +1,64 @@ +{ + "pagination": { + "ListUsers": { + "result_key": "Users", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListGroupMembers": { + "result_key": "Members", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListOrganizations": { + "result_key": "OrganizationSummaries", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListGroups": { + "result_key": "Groups", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListResources": { + "result_key": "Resources", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListAliases": { + "result_key": "Aliases", + "output_token": "NextToken", + "input_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListMailboxPermissions": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Permissions" + }, + "ListResourceDelegates": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Delegates" + }, + "ListAvailabilityConfigurations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "AvailabilityConfigurations" + }, + "ListPersonalAccessTokens": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "PersonalAccessTokenSummaries" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workmail/2017-10-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workmail/2017-10-01/service-2.json.gz new file mode 100644 index 0000000..5d8ce54 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workmail/2017-10-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workmailmessageflow/2019-05-01/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workmailmessageflow/2019-05-01/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..c29fed4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workmailmessageflow/2019-05-01/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workmailmessageflow/2019-05-01/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/workmailmessageflow/2019-05-01/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workmailmessageflow/2019-05-01/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workmailmessageflow/2019-05-01/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/workmailmessageflow/2019-05-01/paginators-1.json new file mode 100644 index 0000000..ea14245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workmailmessageflow/2019-05-01/paginators-1.json @@ -0,0 +1,3 @@ +{ + "pagination": {} +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workmailmessageflow/2019-05-01/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workmailmessageflow/2019-05-01/service-2.json.gz new file mode 100644 index 0000000..9c30c61 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workmailmessageflow/2019-05-01/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces-instances/2022-07-26/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workspaces-instances/2022-07-26/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..9127ba0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workspaces-instances/2022-07-26/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces-instances/2022-07-26/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/workspaces-instances/2022-07-26/paginators-1.json new file mode 100644 index 0000000..988974d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workspaces-instances/2022-07-26/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListInstanceTypes": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "InstanceTypes" + }, + "ListRegions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Regions" + }, + "ListWorkspaceInstances": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "WorkspaceInstances" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces-instances/2022-07-26/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workspaces-instances/2022-07-26/service-2.json.gz new file mode 100644 index 0000000..0b771e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workspaces-instances/2022-07-26/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces-instances/2022-07-26/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/workspaces-instances/2022-07-26/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workspaces-instances/2022-07-26/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces-thin-client/2023-08-22/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workspaces-thin-client/2023-08-22/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..e74412a Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workspaces-thin-client/2023-08-22/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces-thin-client/2023-08-22/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/workspaces-thin-client/2023-08-22/paginators-1.json new file mode 100644 index 0000000..9fe21a4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workspaces-thin-client/2023-08-22/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListDevices": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "devices" + }, + "ListEnvironments": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "environments" + }, + "ListSoftwareSets": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "softwareSets" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces-thin-client/2023-08-22/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workspaces-thin-client/2023-08-22/service-2.json.gz new file mode 100644 index 0000000..4b5aa8d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workspaces-thin-client/2023-08-22/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..adf6df5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/paginators-1.json new file mode 100644 index 0000000..73aba4d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListSessions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sessions" + }, + "ListDataProtectionSettings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "dataProtectionSettings" + }, + "ListSessionLoggers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "sessionLoggers" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/service-2.json.gz new file mode 100644 index 0000000..63e4064 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/waiters-2.json b/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/waiters-2.json new file mode 100644 index 0000000..13f60ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workspaces-web/2020-07-08/waiters-2.json @@ -0,0 +1,5 @@ +{ + "version": 2, + "waiters": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces/2015-04-08/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workspaces/2015-04-08/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..b7e55ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workspaces/2015-04-08/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces/2015-04-08/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/workspaces/2015-04-08/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workspaces/2015-04-08/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces/2015-04-08/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/workspaces/2015-04-08/paginators-1.json new file mode 100644 index 0000000..01176ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/workspaces/2015-04-08/paginators-1.json @@ -0,0 +1,54 @@ +{ + "pagination": { + "DescribeWorkspaceBundles": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Bundles" + }, + "DescribeWorkspaceDirectories": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Directories" + }, + "DescribeWorkspaces": { + "limit_key": "Limit", + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Workspaces" + }, + "DescribeAccountModifications": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "AccountModifications" + }, + "DescribeIpGroups": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Result" + }, + "DescribeWorkspaceImages": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Images" + }, + "DescribeWorkspacesConnectionStatus": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "WorkspacesConnectionStatus" + }, + "ListAvailableManagementCidrRanges": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "ManagementCidrRanges" + }, + "ListAccountLinks": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "AccountLinks" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/workspaces/2015-04-08/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/workspaces/2015-04-08/service-2.json.gz new file mode 100644 index 0000000..b58f877 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/workspaces/2015-04-08/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/xray/2016-04-12/endpoint-rule-set-1.json.gz b/venv/lib/python3.12/site-packages/botocore/data/xray/2016-04-12/endpoint-rule-set-1.json.gz new file mode 100644 index 0000000..80be37e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/xray/2016-04-12/endpoint-rule-set-1.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/data/xray/2016-04-12/examples-1.json b/venv/lib/python3.12/site-packages/botocore/data/xray/2016-04-12/examples-1.json new file mode 100644 index 0000000..0ea7e3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/xray/2016-04-12/examples-1.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "examples": { + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/xray/2016-04-12/paginators-1.json b/venv/lib/python3.12/site-packages/botocore/data/xray/2016-04-12/paginators-1.json new file mode 100644 index 0000000..0f65898 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/data/xray/2016-04-12/paginators-1.json @@ -0,0 +1,69 @@ +{ + "pagination": { + "BatchGetTraces": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Traces", + "non_aggregate_keys": [ + "UnprocessedTraceIds" + ] + }, + "GetServiceGraph": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Services", + "non_aggregate_keys": [ + "StartTime", + "EndTime", + "ContainsOldGroupVersions" + ] + }, + "GetTraceGraph": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Services" + }, + "GetTraceSummaries": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "TraceSummaries", + "non_aggregate_keys": [ + "TracesProcessedCount", + "ApproximateTime" + ] + }, + "GetGroups": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Groups" + }, + "GetSamplingRules": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "SamplingRuleRecords" + }, + "GetSamplingStatisticSummaries": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "SamplingStatisticSummaries" + }, + "GetTimeSeriesServiceStatistics": { + "input_token": "NextToken", + "non_aggregate_keys": [ + "ContainsOldGroupVersions" + ], + "output_token": "NextToken", + "result_key": "TimeSeriesServiceStatistics" + }, + "ListResourcePolicies": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "ResourcePolicies" + }, + "ListTagsForResource": { + "input_token": "NextToken", + "output_token": "NextToken", + "result_key": "Tags" + } + } +} diff --git a/venv/lib/python3.12/site-packages/botocore/data/xray/2016-04-12/service-2.json.gz b/venv/lib/python3.12/site-packages/botocore/data/xray/2016-04-12/service-2.json.gz new file mode 100644 index 0000000..45f092b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/data/xray/2016-04-12/service-2.json.gz differ diff --git a/venv/lib/python3.12/site-packages/botocore/discovery.py b/venv/lib/python3.12/site-packages/botocore/discovery.py new file mode 100644 index 0000000..3d16e97 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/discovery.py @@ -0,0 +1,282 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import logging +import time +import weakref + +from botocore import xform_name +from botocore.exceptions import BotoCoreError, ConnectionError, HTTPClientError +from botocore.model import OperationNotFoundError +from botocore.utils import CachedProperty + +logger = logging.getLogger(__name__) + + +class EndpointDiscoveryException(BotoCoreError): + pass + + +class EndpointDiscoveryRequired(EndpointDiscoveryException): + """Endpoint Discovery is disabled but is required for this operation.""" + + fmt = 'Endpoint Discovery is not enabled but this operation requires it.' + + +class EndpointDiscoveryRefreshFailed(EndpointDiscoveryException): + """Endpoint Discovery failed to the refresh the known endpoints.""" + + fmt = 'Endpoint Discovery failed to refresh the required endpoints.' + + +def block_endpoint_discovery_required_operations(model, **kwargs): + endpoint_discovery = model.endpoint_discovery + if endpoint_discovery and endpoint_discovery.get('required'): + raise EndpointDiscoveryRequired() + + +class EndpointDiscoveryModel: + def __init__(self, service_model): + self._service_model = service_model + + @CachedProperty + def discovery_operation_name(self): + discovery_operation = self._service_model.endpoint_discovery_operation + return xform_name(discovery_operation.name) + + @CachedProperty + def discovery_operation_keys(self): + discovery_operation = self._service_model.endpoint_discovery_operation + keys = [] + if discovery_operation.input_shape: + keys = list(discovery_operation.input_shape.members.keys()) + return keys + + def discovery_required_for(self, operation_name): + try: + operation_model = self._service_model.operation_model( + operation_name + ) + return operation_model.endpoint_discovery.get('required', False) + except OperationNotFoundError: + return False + + def discovery_operation_kwargs(self, **kwargs): + input_keys = self.discovery_operation_keys + # Operation and Identifiers are only sent if there are Identifiers + if not kwargs.get('Identifiers'): + kwargs.pop('Operation', None) + kwargs.pop('Identifiers', None) + return {k: v for k, v in kwargs.items() if k in input_keys} + + def gather_identifiers(self, operation, params): + return self._gather_ids(operation.input_shape, params) + + def _gather_ids(self, shape, params, ids=None): + # Traverse the input shape and corresponding parameters, gathering + # any input fields labeled as an endpoint discovery id + if ids is None: + ids = {} + for member_name, member_shape in shape.members.items(): + if member_shape.metadata.get('endpointdiscoveryid'): + ids[member_name] = params[member_name] + elif ( + member_shape.type_name == 'structure' and member_name in params + ): + self._gather_ids(member_shape, params[member_name], ids) + return ids + + +class EndpointDiscoveryManager: + def __init__( + self, client, cache=None, current_time=None, always_discover=True + ): + if cache is None: + cache = {} + self._cache = cache + self._failed_attempts = {} + if current_time is None: + current_time = time.time + self._time = current_time + self._always_discover = always_discover + + # This needs to be a weak ref in order to prevent memory leaks on + # python 2.6 + self._client = weakref.proxy(client) + self._model = EndpointDiscoveryModel(client.meta.service_model) + + def _parse_endpoints(self, response): + endpoints = response['Endpoints'] + current_time = self._time() + for endpoint in endpoints: + cache_time = endpoint.get('CachePeriodInMinutes') + endpoint['Expiration'] = current_time + cache_time * 60 + return endpoints + + def _cache_item(self, value): + if isinstance(value, dict): + return tuple(sorted(value.items())) + else: + return value + + def _create_cache_key(self, **kwargs): + kwargs = self._model.discovery_operation_kwargs(**kwargs) + return tuple(self._cache_item(v) for k, v in sorted(kwargs.items())) + + def gather_identifiers(self, operation, params): + return self._model.gather_identifiers(operation, params) + + def delete_endpoints(self, **kwargs): + cache_key = self._create_cache_key(**kwargs) + if cache_key in self._cache: + del self._cache[cache_key] + + def _describe_endpoints(self, **kwargs): + # This is effectively a proxy to whatever name/kwargs the service + # supports for endpoint discovery. + kwargs = self._model.discovery_operation_kwargs(**kwargs) + operation_name = self._model.discovery_operation_name + discovery_operation = getattr(self._client, operation_name) + logger.debug('Discovering endpoints with kwargs: %s', kwargs) + return discovery_operation(**kwargs) + + def _get_current_endpoints(self, key): + if key not in self._cache: + return None + now = self._time() + return [e for e in self._cache[key] if now < e['Expiration']] + + def _refresh_current_endpoints(self, **kwargs): + cache_key = self._create_cache_key(**kwargs) + try: + response = self._describe_endpoints(**kwargs) + endpoints = self._parse_endpoints(response) + self._cache[cache_key] = endpoints + self._failed_attempts.pop(cache_key, None) + return endpoints + except (ConnectionError, HTTPClientError): + self._failed_attempts[cache_key] = self._time() + 60 + return None + + def _recently_failed(self, cache_key): + if cache_key in self._failed_attempts: + now = self._time() + if now < self._failed_attempts[cache_key]: + return True + del self._failed_attempts[cache_key] + return False + + def _select_endpoint(self, endpoints): + return endpoints[0]['Address'] + + def describe_endpoint(self, **kwargs): + operation = kwargs['Operation'] + discovery_required = self._model.discovery_required_for(operation) + + if not self._always_discover and not discovery_required: + # Discovery set to only run on required operations + logger.debug( + 'Optional discovery disabled. Skipping discovery for Operation: %s', + operation, + ) + return None + + # Get the endpoint for the provided operation and identifiers + cache_key = self._create_cache_key(**kwargs) + endpoints = self._get_current_endpoints(cache_key) + if endpoints: + return self._select_endpoint(endpoints) + # All known endpoints are stale + recently_failed = self._recently_failed(cache_key) + if not recently_failed: + # We haven't failed to discover recently, go ahead and refresh + endpoints = self._refresh_current_endpoints(**kwargs) + if endpoints: + return self._select_endpoint(endpoints) + # Discovery has failed recently, do our best to get an endpoint + logger.debug('Endpoint Discovery has failed for: %s', kwargs) + stale_entries = self._cache.get(cache_key, None) + if stale_entries: + # We have stale entries, use those while discovery is failing + return self._select_endpoint(stale_entries) + if discovery_required: + # It looks strange to be checking recently_failed again but, + # this informs us as to whether or not we tried to refresh earlier + if recently_failed: + # Discovery is required and we haven't already refreshed + endpoints = self._refresh_current_endpoints(**kwargs) + if endpoints: + return self._select_endpoint(endpoints) + # No endpoints even refresh, raise hard error + raise EndpointDiscoveryRefreshFailed() + # Discovery is optional, just use the default endpoint for now + return None + + +class EndpointDiscoveryHandler: + def __init__(self, manager): + self._manager = manager + + def register(self, events, service_id): + events.register( + f'before-parameter-build.{service_id}', self.gather_identifiers + ) + events.register_first( + f'request-created.{service_id}', self.discover_endpoint + ) + events.register(f'needs-retry.{service_id}', self.handle_retries) + + def gather_identifiers(self, params, model, context, **kwargs): + endpoint_discovery = model.endpoint_discovery + # Only continue if the operation supports endpoint discovery + if endpoint_discovery is None: + return + ids = self._manager.gather_identifiers(model, params) + context['discovery'] = {'identifiers': ids} + + def discover_endpoint(self, request, operation_name, **kwargs): + ids = request.context.get('discovery', {}).get('identifiers') + if ids is None: + return + endpoint = self._manager.describe_endpoint( + Operation=operation_name, Identifiers=ids + ) + if endpoint is None: + logger.debug('Failed to discover and inject endpoint') + return + if not endpoint.startswith('http'): + endpoint = 'https://' + endpoint + logger.debug('Injecting discovered endpoint: %s', endpoint) + request.url = endpoint + + def handle_retries(self, request_dict, response, operation, **kwargs): + if response is None: + return None + + _, response = response + status = response.get('ResponseMetadata', {}).get('HTTPStatusCode') + error_code = response.get('Error', {}).get('Code') + if status != 421 and error_code != 'InvalidEndpointException': + return None + + context = request_dict.get('context', {}) + ids = context.get('discovery', {}).get('identifiers') + if ids is None: + return None + + # Delete the cached endpoints, forcing a refresh on retry + # TODO: Improve eviction behavior to only evict the bad endpoint if + # there are multiple. This will almost certainly require a lock. + self._manager.delete_endpoints( + Operation=operation.name, Identifiers=ids + ) + return 0 diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__init__.py b/venv/lib/python3.12/site-packages/botocore/docs/__init__.py new file mode 100644 index 0000000..844f5de --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/__init__.py @@ -0,0 +1,54 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import os + +from botocore.docs.service import ServiceDocumenter + +DEPRECATED_SERVICE_NAMES = {'sms-voice'} + + +def generate_docs(root_dir, session): + """Generates the reference documentation for botocore + + This will go through every available AWS service and output ReSTructured + text files documenting each service. + + :param root_dir: The directory to write the reference files to. Each + service's reference documentation is loacated at + root_dir/reference/services/service-name.rst + """ + # Create the root directory where all service docs live. + services_dir_path = os.path.join(root_dir, 'reference', 'services') + if not os.path.exists(services_dir_path): + os.makedirs(services_dir_path) + + # Prevents deprecated service names from being generated in docs. + available_services = [ + service + for service in session.get_available_services() + if service not in DEPRECATED_SERVICE_NAMES + ] + + # Generate reference docs and write them out. + for service_name in available_services: + docs = ServiceDocumenter( + service_name, session, services_dir_path + ).document_service() + + # Write the main service documentation page. + # Path: /reference/services//index.rst + service_file_path = os.path.join( + services_dir_path, f'{service_name}.rst' + ) + with open(service_file_path, 'wb') as f: + f.write(docs) diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0f7361b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/client.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/client.cpython-312.pyc new file mode 100644 index 0000000..a9bc26e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/client.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/docstring.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/docstring.cpython-312.pyc new file mode 100644 index 0000000..fa8589c Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/docstring.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/example.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/example.cpython-312.pyc new file mode 100644 index 0000000..d56d2eb Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/example.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/method.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/method.cpython-312.pyc new file mode 100644 index 0000000..7425724 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/method.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/paginator.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/paginator.cpython-312.pyc new file mode 100644 index 0000000..7b677c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/paginator.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/params.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/params.cpython-312.pyc new file mode 100644 index 0000000..8efaecb Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/service.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/service.cpython-312.pyc new file mode 100644 index 0000000..8666edf Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/service.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/shape.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/shape.cpython-312.pyc new file mode 100644 index 0000000..6d176c2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/shape.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/sharedexample.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/sharedexample.cpython-312.pyc new file mode 100644 index 0000000..830b4b9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/sharedexample.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/translator.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/translator.cpython-312.pyc new file mode 100644 index 0000000..775a070 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/translator.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..3eabf67 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/waiter.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/waiter.cpython-312.pyc new file mode 100644 index 0000000..574f397 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/__pycache__/waiter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__init__.py b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__init__.py new file mode 100644 index 0000000..b687f69 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +__version__ = '0.16.0' diff --git a/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..34a67d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__pycache__/docstringparser.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__pycache__/docstringparser.cpython-312.pyc new file mode 100644 index 0000000..ae5711e Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__pycache__/docstringparser.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__pycache__/restdoc.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__pycache__/restdoc.cpython-312.pyc new file mode 100644 index 0000000..332964d Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__pycache__/restdoc.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__pycache__/style.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__pycache__/style.cpython-312.pyc new file mode 100644 index 0000000..d875d61 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/__pycache__/style.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/docstringparser.py b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/docstringparser.py new file mode 100644 index 0000000..ebe16ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/docstringparser.py @@ -0,0 +1,315 @@ +# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from html.parser import HTMLParser +from itertools import zip_longest + +PRIORITY_PARENT_TAGS = ('code', 'a') +OMIT_NESTED_TAGS = ('span', 'i', 'code', 'a') +OMIT_SELF_TAGS = ('i', 'b') +HTML_BLOCK_DISPLAY_TAGS = ('p', 'note', 'ul', 'li') + + +class DocStringParser(HTMLParser): + """ + A simple HTML parser. Focused on converting the subset of HTML + that appears in the documentation strings of the JSON models into + simple ReST format. + """ + + def __init__(self, doc): + self.tree = None + self.doc = doc + super().__init__() + + def reset(self): + HTMLParser.reset(self) + self.tree = HTMLTree(self.doc) + + def feed(self, data): + super().feed(data) + self.tree.write() + self.tree = HTMLTree(self.doc) + + def close(self): + super().close() + # Write if there is anything remaining. + self.tree.write() + self.tree = HTMLTree(self.doc) + + def handle_starttag(self, tag, attrs): + self.tree.add_tag(tag, attrs=attrs) + + def handle_endtag(self, tag): + self.tree.add_tag(tag, is_start=False) + + def handle_data(self, data): + self.tree.add_data(data) + + +class HTMLTree: + """ + A tree which handles HTML nodes. Designed to work with a python HTML parser, + meaning that the current_node will be the most recently opened tag. When + a tag is closed, the current_node moves up to the parent node. + """ + + def __init__(self, doc): + self.doc = doc + self.head = StemNode() + self.current_node = self.head + self.unhandled_tags = [] + + def add_tag(self, tag, attrs=None, is_start=True): + if not self._doc_has_handler(tag, is_start): + self.unhandled_tags.append(tag) + return + + if is_start: + node = TagNode(tag, attrs) + self.current_node.add_child(node) + self.current_node = node + else: + self.current_node = self.current_node.parent + + def _doc_has_handler(self, tag, is_start): + if is_start: + handler_name = f'start_{tag}' + else: + handler_name = f'end_{tag}' + + return hasattr(self.doc.style, handler_name) + + def add_data(self, data): + self.current_node.add_child(DataNode(data)) + + def write(self): + self.head.write(self.doc) + + +class Node: + def __init__(self, parent=None): + self.parent = parent + + def write(self, doc): + raise NotImplementedError + + +class StemNode(Node): + def __init__(self, parent=None): + super().__init__(parent) + self.children = [] + + def add_child(self, child): + child.parent = self + self.children.append(child) + + def write(self, doc): + self.collapse_whitespace() + self._write_children(doc) + + def _write_children(self, doc): + for child, next_child in zip_longest(self.children, self.children[1:]): + if isinstance(child, TagNode) and next_child is not None: + child.write(doc, next_child) + else: + child.write(doc) + + def is_whitespace(self): + return all(child.is_whitespace() for child in self.children) + + def startswith_whitespace(self): + return self.children and self.children[0].startswith_whitespace() + + def endswith_whitespace(self): + return self.children and self.children[-1].endswith_whitespace() + + def lstrip(self): + while self.children and self.children[0].is_whitespace(): + self.children = self.children[1:] + if self.children: + self.children[0].lstrip() + + def rstrip(self): + while self.children and self.children[-1].is_whitespace(): + self.children = self.children[:-1] + if self.children: + self.children[-1].rstrip() + + def collapse_whitespace(self): + """Remove collapsible white-space from HTML. + + HTML in docstrings often contains extraneous white-space around tags, + for readability. Browsers would collapse this white-space before + rendering. If not removed before conversion to RST where white-space is + part of the syntax, for example for indentation, it can result in + incorrect output. + """ + self.lstrip() + self.rstrip() + for child in self.children: + child.collapse_whitespace() + + +class TagNode(StemNode): + """ + A generic Tag node. It will verify that handlers exist before writing. + """ + + def __init__(self, tag, attrs=None, parent=None): + super().__init__(parent) + self.attrs = attrs + self.tag = tag + + def _has_nested_tags(self): + # Returns True if any children are TagNodes and False otherwise. + return any(isinstance(child, TagNode) for child in self.children) + + def write(self, doc, next_child=None): + prioritize_nested_tags = ( + self.tag in OMIT_SELF_TAGS and self._has_nested_tags() + ) + prioritize_parent_tag = ( + isinstance(self.parent, TagNode) + and self.parent.tag in PRIORITY_PARENT_TAGS + and self.tag in OMIT_NESTED_TAGS + ) + if prioritize_nested_tags or prioritize_parent_tag: + self._write_children(doc) + return + + self._write_start(doc) + self._write_children(doc) + self._write_end(doc, next_child) + + def collapse_whitespace(self): + """Remove collapsible white-space. + + All tags collapse internal whitespace. Block-display HTML tags also + strip all leading and trailing whitespace. + + Approximately follows the specification used in browsers: + https://www.w3.org/TR/css-text-3/#white-space-rules + https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace + """ + if self.tag in HTML_BLOCK_DISPLAY_TAGS: + self.lstrip() + self.rstrip() + # Collapse whitespace in situations like `` foo`` into + # `` foo``. + for prev, cur in zip(self.children[:-1], self.children[1:]): + if ( + isinstance(prev, DataNode) + and prev.endswith_whitespace() + and cur.startswith_whitespace() + ): + cur.lstrip() + # Same logic, but for situations like ``bar ``: + for cur, nxt in zip(self.children[:-1], self.children[1:]): + if ( + isinstance(nxt, DataNode) + and cur.endswith_whitespace() + and nxt.startswith_whitespace() + ): + cur.rstrip() + # Recurse into children + for child in self.children: + child.collapse_whitespace() + + def _write_start(self, doc): + handler_name = f'start_{self.tag}' + if hasattr(doc.style, handler_name): + getattr(doc.style, handler_name)(self.attrs) + + def _write_end(self, doc, next_child): + handler_name = f'end_{self.tag}' + if hasattr(doc.style, handler_name): + if handler_name == 'end_a': + # We use lookahead to determine if a space is needed after a link node + getattr(doc.style, handler_name)(next_child) + else: + getattr(doc.style, handler_name)() + + +class DataNode(Node): + """ + A Node that contains only string data. + """ + + def __init__(self, data, parent=None): + super().__init__(parent) + if not isinstance(data, str): + raise ValueError(f"Expecting string type, {type(data)} given.") + self._leading_whitespace = '' + self._trailing_whitespace = '' + self._stripped_data = '' + if data == '': + return + if data.isspace(): + self._trailing_whitespace = data + return + first_non_space = next( + idx for idx, ch in enumerate(data) if not ch.isspace() + ) + last_non_space = len(data) - next( + idx for idx, ch in enumerate(reversed(data)) if not ch.isspace() + ) + self._leading_whitespace = data[:first_non_space] + self._trailing_whitespace = data[last_non_space:] + self._stripped_data = data[first_non_space:last_non_space] + + @property + def data(self): + return ( + f'{self._leading_whitespace}{self._stripped_data}' + f'{self._trailing_whitespace}' + ) + + def is_whitespace(self): + return self._stripped_data == '' and ( + self._leading_whitespace != '' or self._trailing_whitespace != '' + ) + + def startswith_whitespace(self): + return self._leading_whitespace != '' or ( + self._stripped_data == '' and self._trailing_whitespace != '' + ) + + def endswith_whitespace(self): + return self._trailing_whitespace != '' or ( + self._stripped_data == '' and self._leading_whitespace != '' + ) + + def lstrip(self): + if self._leading_whitespace != '': + self._leading_whitespace = '' + elif self._stripped_data == '': + self.rstrip() + + def rstrip(self): + if self._trailing_whitespace != '': + self._trailing_whitespace = '' + elif self._stripped_data == '': + self.lstrip() + + def collapse_whitespace(self): + """Noop, ``DataNode.write`` always collapses whitespace""" + return + + def write(self, doc): + words = doc.translate_words(self._stripped_data.split()) + str_data = ( + f'{self._leading_whitespace}{" ".join(words)}' + f'{self._trailing_whitespace}' + ) + if str_data != '': + doc.handle_data(str_data) diff --git a/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/restdoc.py b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/restdoc.py new file mode 100644 index 0000000..3868126 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/restdoc.py @@ -0,0 +1,285 @@ +# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import logging +import os +import re + +from botocore.compat import OrderedDict +from botocore.docs.bcdoc.docstringparser import DocStringParser +from botocore.docs.bcdoc.style import ReSTStyle + +DEFAULT_AWS_DOCS_LINK = 'https://docs.aws.amazon.com/index.html' +DOCUMENTATION_LINK_REGEX = re.compile( + r'`AWS API Documentation ' + r'`_' +) +LARGE_SECTION_MESSAGE = """ + + **{}** + :: + + # This section is too large to render. + # Please see the AWS API Documentation linked below. + + {} + """ +LOG = logging.getLogger('bcdocs') +SECTION_LINE_LIMIT_CONFIG = { + 'response-example': {'name': 'Response Syntax', 'line_limit': 1500}, + 'description': {'name': 'Response Structure', 'line_limit': 5000}, + 'request-example': {'name': 'Request Syntax', 'line_limit': 1500}, + 'request-params': {'name': 'Parameters', 'line_limit': 5000}, +} +SECTION_METHOD_PATH_DEPTH = { + 'client-api': 4, + 'paginator-api': 3, + 'waiter-api': 3, +} + + +class ReSTDocument: + def __init__(self, target='man'): + self.style = ReSTStyle(self) + self.target = target + self.parser = DocStringParser(self) + self.keep_data = True + self.do_translation = False + self.translation_map = {} + self.hrefs = {} + self._writes = [] + self._last_doc_string = None + + def _write(self, s): + if self.keep_data and s is not None: + self._writes.append(s) + + def write(self, content): + """ + Write content into the document. + """ + self._write(content) + + def writeln(self, content): + """ + Write content on a newline. + """ + self._write(f'{self.style.spaces()}{content}\n') + + def peek_write(self): + """ + Returns the last content written to the document without + removing it from the stack. + """ + return self._writes[-1] + + def pop_write(self): + """ + Removes and returns the last content written to the stack. + """ + return self._writes.pop() if len(self._writes) > 0 else None + + def push_write(self, s): + """ + Places new content on the stack. + """ + self._writes.append(s) + + def getvalue(self): + """ + Returns the current content of the document as a string. + """ + if self.hrefs: + self.style.new_paragraph() + for refname, link in self.hrefs.items(): + self.style.link_target_definition(refname, link) + return ''.join(self._writes).encode('utf-8') + + def translate_words(self, words): + return [self.translation_map.get(w, w) for w in words] + + def handle_data(self, data): + if data and self.keep_data: + self._write(data) + + def include_doc_string(self, doc_string): + if doc_string: + try: + start = len(self._writes) + self.parser.feed(doc_string) + self.parser.close() + end = len(self._writes) + self._last_doc_string = (start, end) + except Exception: + LOG.debug('Error parsing doc string', exc_info=True) + LOG.debug(doc_string) + + def remove_last_doc_string(self): + # Removes all writes inserted by last doc string + if self._last_doc_string is not None: + start, end = self._last_doc_string + del self._writes[start:end] + + +class DocumentStructure(ReSTDocument): + def __init__(self, name, section_names=None, target='man', context=None): + """Provides a Hierarichial structure to a ReSTDocument + + You can write to it similiar to as you can to a ReSTDocument but + has an innate structure for more orginaztion and abstraction. + + :param name: The name of the document + :param section_names: A list of sections to be included + in the document. + :param target: The target documentation of the Document structure + :param context: A dictionary of data to store with the strucuture. These + are only stored per section not the entire structure. + """ + super().__init__(target=target) + self._name = name + self._structure = OrderedDict() + self._path = [self._name] + self._context = {} + if context is not None: + self._context = context + if section_names is not None: + self._generate_structure(section_names) + + @property + def name(self): + """The name of the document structure""" + return self._name + + @property + def path(self): + """ + A list of where to find a particular document structure in the + overlying document structure. + """ + return self._path + + @path.setter + def path(self, value): + self._path = value + + @property + def available_sections(self): + return list(self._structure) + + @property + def context(self): + return self._context + + def _generate_structure(self, section_names): + for section_name in section_names: + self.add_new_section(section_name) + + def add_new_section(self, name, context=None): + """Adds a new section to the current document structure + + This document structure will be considered a section to the + current document structure but will in itself be an entirely + new document structure that can be written to and have sections + as well + + :param name: The name of the section. + :param context: A dictionary of data to store with the strucuture. These + are only stored per section not the entire structure. + :rtype: DocumentStructure + :returns: A new document structure to add to but lives as a section + to the document structure it was instantiated from. + """ + # Add a new section + section = self.__class__( + name=name, target=self.target, context=context + ) + section.path = self.path + [name] + # Indent the section apporpriately as well + section.style.indentation = self.style.indentation + section.translation_map = self.translation_map + section.hrefs = self.hrefs + self._structure[name] = section + return section + + def get_section(self, name): + """Retrieve a section""" + return self._structure[name] + + def has_section(self, name): + return name in self._structure + + def delete_section(self, name): + """Delete a section""" + del self._structure[name] + + def flush_structure(self, docs_link=None): + """Flushes a doc structure to a ReSTructed string + + The document is flushed out in a DFS style where sections and their + subsections' values are added to the string as they are visited. + """ + # We are at the root flush the links at the beginning of the + # document + path_length = len(self.path) + if path_length == 1: + if self.hrefs: + self.style.new_paragraph() + for refname, link in self.hrefs.items(): + self.style.link_target_definition(refname, link) + # Clear docs_link at the correct depth to prevent passing a non-related link. + elif path_length == SECTION_METHOD_PATH_DEPTH.get(self.path[1]): + docs_link = None + value = self.getvalue() + for name, section in self._structure.items(): + # Checks is the AWS API Documentation link has been generated. + # If it has been generated, it gets passed as a the doc_link parameter. + match = DOCUMENTATION_LINK_REGEX.search(value.decode()) + docs_link = ( + f'{match.group(0)}\n\n'.encode() if match else docs_link + ) + value += section.flush_structure(docs_link) + + # Replace response/request sections if the line number exceeds our limit. + # The section is replaced with a message linking to AWS API Documentation. + line_count = len(value.splitlines()) + section_config = SECTION_LINE_LIMIT_CONFIG.get(self.name) + aws_docs_link = ( + docs_link.decode() + if docs_link is not None + else DEFAULT_AWS_DOCS_LINK + ) + if section_config and line_count > section_config['line_limit']: + value = LARGE_SECTION_MESSAGE.format( + section_config['name'], aws_docs_link + ).encode() + return value + + def getvalue(self): + return ''.join(self._writes).encode('utf-8') + + def remove_all_sections(self): + self._structure = OrderedDict() + + def clear_text(self): + self._writes = [] + + def add_title_section(self, title): + title_section = self.add_new_section('title') + title_section.style.h1(title) + return title_section + + def write_to_file(self, full_path, file_name): + if not os.path.exists(full_path): + os.makedirs(full_path) + sub_resource_file_path = os.path.join(full_path, f'{file_name}.rst') + with open(sub_resource_file_path, 'wb') as f: + f.write(self.flush_structure()) diff --git a/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/style.py b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/style.py new file mode 100644 index 0000000..205d238 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/bcdoc/style.py @@ -0,0 +1,447 @@ +# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import logging + +logger = logging.getLogger('bcdocs') +# Terminal punctuation where a space is not needed before. +PUNCTUATION_CHARACTERS = ('.', ',', '?', '!', ':', ';') + + +class BaseStyle: + def __init__(self, doc, indent_width=2): + self.doc = doc + self.indent_width = indent_width + self._indent = 0 + self.keep_data = True + + @property + def indentation(self): + return self._indent + + @indentation.setter + def indentation(self, value): + self._indent = value + + def new_paragraph(self): + return f'\n{self.spaces()}' + + def indent(self): + self._indent += 1 + + def dedent(self): + if self._indent > 0: + self._indent -= 1 + + def spaces(self): + return ' ' * (self._indent * self.indent_width) + + def bold(self, s): + return s + + def ref(self, link, title=None): + return link + + def h2(self, s): + return s + + def h3(self, s): + return s + + def underline(self, s): + return s + + def italics(self, s): + return s + + def add_trailing_space_to_previous_write(self): + # Adds a trailing space if none exists. This is mainly used for + # ensuring inline code and links are separated from surrounding text. + last_write = self.doc.pop_write() + if last_write is None: + last_write = '' + if last_write != '' and last_write[-1] != ' ': + last_write += ' ' + self.doc.push_write(last_write) + + +class ReSTStyle(BaseStyle): + def __init__(self, doc, indent_width=2): + BaseStyle.__init__(self, doc, indent_width) + self.do_p = True + self.a_href = None + self.list_depth = 0 + + def new_paragraph(self): + self.doc.write(f'\n\n{self.spaces()}') + + def new_line(self): + self.doc.write(f'\n{self.spaces()}') + + def _start_inline(self, markup): + # Insert space between any directly adjacent bold and italic inlines to + # avoid situations like ``**abc***def*``. + try: + last_write = self.doc.peek_write() + except IndexError: + pass + else: + if last_write in ('*', '**') and markup in ('*', '**'): + self.doc.write(' ') + self.doc.write(markup) + + def _end_inline(self, markup): + # Remove empty and self-closing tags like ```` and ````. + # If we simply translate that directly then we end up with something + # like ****, which rst will assume is a heading instead of an empty + # bold. + last_write = self.doc.pop_write() + if last_write == markup: + return + self.doc.push_write(last_write) + self.doc.write(markup) + + def start_bold(self, attrs=None): + self._start_inline('**') + + def end_bold(self): + self._end_inline('**') + + def start_b(self, attrs=None): + self.doc.do_translation = True + self.start_bold(attrs) + + def end_b(self): + self.doc.do_translation = False + self.end_bold() + + def bold(self, s): + if s: + self.start_bold() + self.doc.write(s) + self.end_bold() + + def ref(self, title, link=None): + if link is None: + link = title + self.doc.write(f':doc:`{title} <{link}>`') + + def _heading(self, s, border_char): + border = border_char * len(s) + self.new_paragraph() + self.doc.write(f'{border}\n{s}\n{border}') + self.new_paragraph() + + def h1(self, s): + self._heading(s, '*') + + def h2(self, s): + self._heading(s, '=') + + def h3(self, s): + self._heading(s, '-') + + def start_italics(self, attrs=None): + self._start_inline('*') + + def end_italics(self): + self._end_inline('*') + + def italics(self, s): + if s: + self.start_italics() + self.doc.write(s) + self.end_italics() + + def start_p(self, attrs=None): + if self.do_p: + self.doc.write(f'\n\n{self.spaces()}') + + def end_p(self): + if self.do_p: + self.doc.write(f'\n\n{self.spaces()}') + + def start_code(self, attrs=None): + self.doc.do_translation = True + self.add_trailing_space_to_previous_write() + self._start_inline('``') + + def end_code(self): + self.doc.do_translation = False + self._end_inline('``') + + def code(self, s): + if s: + self.start_code() + self.doc.write(s) + self.end_code() + + def start_note(self, attrs=None): + self.new_paragraph() + self.doc.write('.. note::') + self.indent() + self.new_paragraph() + + def end_note(self): + self.dedent() + self.new_paragraph() + + def start_important(self, attrs=None): + self.new_paragraph() + self.doc.write('.. warning::') + self.indent() + self.new_paragraph() + + def end_important(self): + self.dedent() + self.new_paragraph() + + def start_danger(self, attrs=None): + self.new_paragraph() + self.doc.write('.. danger::') + self.indent() + self.new_paragraph() + + def end_danger(self): + self.dedent() + self.new_paragraph() + + def start_a(self, attrs=None): + # Write an empty space to guard against zero whitespace + # before an "a" tag. Example: hiExample + self.add_trailing_space_to_previous_write() + if attrs: + for attr_key, attr_value in attrs: + if attr_key == 'href': + # Removes unnecessary whitespace around the href link. + # Example: Example + self.a_href = attr_value.strip() + self.doc.write('`') + else: + # There are some model documentation that + # looks like this: DescribeInstances. + # In this case we just write out an empty + # string. + self.doc.write(' ') + self.doc.do_translation = True + + def link_target_definition(self, refname, link): + self.doc.writeln(f'.. _{refname}: {link}') + + def sphinx_reference_label(self, label, text=None): + if text is None: + text = label + if self.doc.target == 'html': + self.doc.write(f':ref:`{text} <{label}>`') + else: + self.doc.write(text) + + def _clean_link_text(self): + doc = self.doc + # Pop till we reach the link start character to retrieve link text. + last_write = doc.pop_write() + while not last_write.startswith('`'): + last_write = doc.pop_write() + last_write + if last_write != '': + # Remove whitespace from the start of link text. + if last_write.startswith('` '): + last_write = f'`{last_write[1:].lstrip(" ")}' + doc.push_write(last_write) + + def end_a(self, next_child=None): + self.doc.do_translation = False + if self.a_href: + self._clean_link_text() + last_write = self.doc.pop_write() + last_write = last_write.rstrip(' ') + if last_write and last_write != '`': + if ':' in last_write: + last_write = last_write.replace(':', r'\:') + self.doc.push_write(last_write) + self.doc.push_write(f' <{self.a_href}>`__') + elif last_write == '`': + # Look at start_a(). It will do a self.doc.write('`') + # which is the start of the link title. If that is the + # case then there was no link text. We should just + # use an inline link. The syntax of this is + # ``_ + self.doc.push_write(f'`<{self.a_href}>`__') + else: + self.doc.push_write(self.a_href) + self.doc.hrefs[self.a_href] = self.a_href + self.doc.write('`__') + self.a_href = None + + def start_i(self, attrs=None): + self.doc.do_translation = True + self.start_italics() + + def end_i(self): + self.doc.do_translation = False + self.end_italics() + + def start_li(self, attrs=None): + self.new_line() + self.do_p = False + self.doc.write('* ') + + def end_li(self): + self.do_p = True + self.new_line() + + def li(self, s): + if s: + self.start_li() + self.doc.writeln(s) + self.end_li() + + def start_ul(self, attrs=None): + if self.list_depth != 0: + self.indent() + self.list_depth += 1 + self.new_paragraph() + + def end_ul(self): + self.list_depth -= 1 + if self.list_depth != 0: + self.dedent() + self.new_paragraph() + + def start_ol(self, attrs=None): + # TODO: Need to control the bullets used for LI items + if self.list_depth != 0: + self.indent() + self.list_depth += 1 + self.new_paragraph() + + def end_ol(self): + self.list_depth -= 1 + if self.list_depth != 0: + self.dedent() + self.new_paragraph() + + def start_examples(self, attrs=None): + self.doc.keep_data = False + + def end_examples(self): + self.doc.keep_data = True + + def start_fullname(self, attrs=None): + self.doc.keep_data = False + + def end_fullname(self): + self.doc.keep_data = True + + def start_codeblock(self, attrs=None): + self.doc.write('::') + self.indent() + self.new_paragraph() + + def end_codeblock(self): + self.dedent() + self.new_paragraph() + + def codeblock(self, code): + """ + Literal code blocks are introduced by ending a paragraph with + the special marker ::. The literal block must be indented + (and, like all paragraphs, separated from the surrounding + ones by blank lines). + """ + self.start_codeblock() + self.doc.writeln(code) + self.end_codeblock() + + def toctree(self): + if self.doc.target == 'html': + self.doc.write('\n.. toctree::\n') + self.doc.write(' :maxdepth: 1\n') + self.doc.write(' :titlesonly:\n\n') + else: + self.start_ul() + + def tocitem(self, item, file_name=None): + if self.doc.target == 'man': + self.li(item) + else: + if file_name: + self.doc.writeln(f' {file_name}') + else: + self.doc.writeln(f' {item}') + + def hidden_toctree(self): + if self.doc.target == 'html': + self.doc.write('\n.. toctree::\n') + self.doc.write(' :maxdepth: 1\n') + self.doc.write(' :hidden:\n\n') + + def hidden_tocitem(self, item): + if self.doc.target == 'html': + self.tocitem(item) + + def table_of_contents(self, title=None, depth=None): + self.doc.write('.. contents:: ') + if title is not None: + self.doc.writeln(title) + if depth is not None: + self.doc.writeln(f' :depth: {depth}') + + def start_sphinx_py_class(self, class_name): + self.new_paragraph() + self.doc.write(f'.. py:class:: {class_name}') + self.indent() + self.new_paragraph() + + def end_sphinx_py_class(self): + self.dedent() + self.new_paragraph() + + def start_sphinx_py_method(self, method_name, parameters=None): + self.new_paragraph() + content = f'.. py:method:: {method_name}' + if parameters is not None: + content += f'({parameters})' + self.doc.write(content) + self.indent() + self.new_paragraph() + + def end_sphinx_py_method(self): + self.dedent() + self.new_paragraph() + + def start_sphinx_py_attr(self, attr_name): + self.new_paragraph() + self.doc.write(f'.. py:attribute:: {attr_name}') + self.indent() + self.new_paragraph() + + def end_sphinx_py_attr(self): + self.dedent() + self.new_paragraph() + + def write_py_doc_string(self, docstring): + docstring_lines = docstring.splitlines() + for docstring_line in docstring_lines: + self.doc.writeln(docstring_line) + + def external_link(self, title, link): + if self.doc.target == 'html': + self.doc.write(f'`{title} <{link}>`_') + else: + self.doc.write(title) + + def internal_link(self, title, page): + if self.doc.target == 'html': + self.doc.write(f':doc:`{title} <{page}>`') + else: + self.doc.write(title) diff --git a/venv/lib/python3.12/site-packages/botocore/docs/client.py b/venv/lib/python3.12/site-packages/botocore/docs/client.py new file mode 100644 index 0000000..26bb604 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/client.py @@ -0,0 +1,451 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import os + +from botocore import xform_name +from botocore.compat import OrderedDict +from botocore.docs.bcdoc.restdoc import DocumentStructure +from botocore.docs.example import ResponseExampleDocumenter +from botocore.docs.method import ( + document_custom_method, + document_model_driven_method, + get_instance_public_methods, +) +from botocore.docs.params import ResponseParamsDocumenter +from botocore.docs.sharedexample import document_shared_examples +from botocore.docs.utils import DocumentedShape, get_official_service_name + + +def _allowlist_generate_presigned_url(method_name, service_name, **kwargs): + if method_name != 'generate_presigned_url': + return None + return service_name in ['s3'] + + +class ClientDocumenter: + _CLIENT_METHODS_FILTERS = [ + _allowlist_generate_presigned_url, + ] + + def __init__(self, client, root_docs_path, shared_examples=None): + self._client = client + self._client_class_name = self._client.__class__.__name__ + self._root_docs_path = root_docs_path + self._shared_examples = shared_examples + if self._shared_examples is None: + self._shared_examples = {} + self._service_name = self._client.meta.service_model.service_name + + def document_client(self, section): + """Documents a client and its methods + + :param section: The section to write to. + """ + self._add_title(section) + self._add_class_signature(section) + client_methods = self._get_client_methods() + self._add_client_intro(section, client_methods) + self._add_client_methods(client_methods) + + def _get_client_methods(self): + client_methods = get_instance_public_methods(self._client) + return self._filter_client_methods(client_methods) + + def _filter_client_methods(self, client_methods): + filtered_methods = {} + for method_name, method in client_methods.items(): + include = self._filter_client_method( + method=method, + method_name=method_name, + service_name=self._service_name, + ) + if include: + filtered_methods[method_name] = method + return filtered_methods + + def _filter_client_method(self, **kwargs): + # Apply each filter to the method + for filter in self._CLIENT_METHODS_FILTERS: + filter_include = filter(**kwargs) + # Use the first non-None value returned by any of the filters + if filter_include is not None: + return filter_include + # Otherwise default to including it + return True + + def _add_title(self, section): + section.style.h2('Client') + + def _add_client_intro(self, section, client_methods): + section = section.add_new_section('intro') + # Write out the top level description for the client. + official_service_name = get_official_service_name( + self._client.meta.service_model + ) + section.write( + f"A low-level client representing {official_service_name}" + ) + section.style.new_line() + section.include_doc_string( + self._client.meta.service_model.documentation + ) + + # Write out the client example instantiation. + self._add_client_creation_example(section) + + # List out all of the possible client methods. + section.style.dedent() + section.style.new_paragraph() + section.writeln('These are the available methods:') + section.style.toctree() + for method_name in sorted(client_methods): + section.style.tocitem(f'{self._service_name}/client/{method_name}') + + def _add_class_signature(self, section): + section.style.start_sphinx_py_class( + class_name=f'{self._client_class_name}.Client' + ) + + def _add_client_creation_example(self, section): + section.style.start_codeblock() + section.style.new_line() + section.write( + f'client = session.create_client(\'{self._service_name}\')' + ) + section.style.end_codeblock() + + def _add_client_methods(self, client_methods): + for method_name in sorted(client_methods): + # Create a new DocumentStructure for each client method and add contents. + method_doc_structure = DocumentStructure( + method_name, target='html' + ) + self._add_client_method( + method_doc_structure, method_name, client_methods[method_name] + ) + # Write client methods in individual/nested files. + # Path: /reference/services//client/.rst + client_dir_path = os.path.join( + self._root_docs_path, self._service_name, 'client' + ) + method_doc_structure.write_to_file(client_dir_path, method_name) + + def _add_client_method(self, section, method_name, method): + breadcrumb_section = section.add_new_section('breadcrumb') + breadcrumb_section.style.ref( + self._client_class_name, f'../../{self._service_name}' + ) + breadcrumb_section.write(f' / Client / {method_name}') + section.add_title_section(method_name) + method_section = section.add_new_section( + method_name, + context={'qualifier': f'{self._client_class_name}.Client.'}, + ) + if self._is_custom_method(method_name): + self._add_custom_method( + method_section, + method_name, + method, + ) + else: + self._add_model_driven_method(method_section, method_name) + + def _is_custom_method(self, method_name): + return method_name not in self._client.meta.method_to_api_mapping + + def _add_custom_method(self, section, method_name, method): + document_custom_method(section, method_name, method) + + def _add_method_exceptions_list(self, section, operation_model): + error_section = section.add_new_section('exceptions') + error_section.style.new_line() + error_section.style.bold('Exceptions') + error_section.style.new_line() + for error in operation_model.error_shapes: + class_name = ( + f'{self._client_class_name}.Client.exceptions.{error.name}' + ) + error_section.style.li(f':py:class:`{class_name}`') + + def _add_model_driven_method(self, section, method_name): + service_model = self._client.meta.service_model + operation_name = self._client.meta.method_to_api_mapping[method_name] + operation_model = service_model.operation_model(operation_name) + + example_prefix = f'response = client.{method_name}' + full_method_name = ( + f"{section.context.get('qualifier', '')}{method_name}" + ) + document_model_driven_method( + section, + full_method_name, + operation_model, + event_emitter=self._client.meta.events, + method_description=operation_model.documentation, + example_prefix=example_prefix, + ) + + # Add any modeled exceptions + if operation_model.error_shapes: + self._add_method_exceptions_list(section, operation_model) + + # Add the shared examples + shared_examples = self._shared_examples.get(operation_name) + if shared_examples: + document_shared_examples( + section, operation_model, example_prefix, shared_examples + ) + + +class ClientExceptionsDocumenter: + _USER_GUIDE_LINK = ( + 'https://docs.aws.amazon.com/boto3/latest/guide/error-handling.html' + ) + _GENERIC_ERROR_SHAPE = DocumentedShape( + name='Error', + type_name='structure', + documentation=('Normalized access to common exception attributes.'), + members=OrderedDict( + [ + ( + 'Code', + DocumentedShape( + name='Code', + type_name='string', + documentation=( + 'An identifier specifying the exception type.' + ), + ), + ), + ( + 'Message', + DocumentedShape( + name='Message', + type_name='string', + documentation=( + 'A descriptive message explaining why the exception ' + 'occured.' + ), + ), + ), + ] + ), + ) + + def __init__(self, client, root_docs_path): + self._client = client + self._client_class_name = self._client.__class__.__name__ + self._service_name = self._client.meta.service_model.service_name + self._root_docs_path = root_docs_path + + def document_exceptions(self, section): + self._add_title(section) + self._add_overview(section) + self._add_exceptions_list(section) + self._add_exception_classes() + + def _add_title(self, section): + section.style.h2('Client Exceptions') + + def _add_overview(self, section): + section.style.new_line() + section.write( + 'Client exceptions are available on a client instance ' + 'via the ``exceptions`` property. For more detailed instructions ' + 'and examples on the exact usage of client exceptions, see the ' + 'error handling ' + ) + section.style.external_link( + title='user guide', + link=self._USER_GUIDE_LINK, + ) + section.write('.') + section.style.new_line() + + def _exception_class_name(self, shape): + return f'{self._client_class_name}.Client.exceptions.{shape.name}' + + def _add_exceptions_list(self, section): + error_shapes = self._client.meta.service_model.error_shapes + if not error_shapes: + section.style.new_line() + section.write('This client has no modeled exception classes.') + section.style.new_line() + return + section.style.new_line() + section.writeln('The available client exceptions are:') + section.style.toctree() + for shape in error_shapes: + section.style.tocitem( + f'{self._service_name}/client/exceptions/{shape.name}' + ) + + def _add_exception_classes(self): + for shape in self._client.meta.service_model.error_shapes: + # Create a new DocumentStructure for each exception method and add contents. + exception_doc_structure = DocumentStructure( + shape.name, target='html' + ) + self._add_exception_class(exception_doc_structure, shape) + # Write exceptions in individual/nested files. + # Path: /reference/services//client/exceptions/.rst + exception_dir_path = os.path.join( + self._root_docs_path, + self._service_name, + 'client', + 'exceptions', + ) + exception_doc_structure.write_to_file( + exception_dir_path, shape.name + ) + + def _add_exception_class(self, section, shape): + breadcrumb_section = section.add_new_section('breadcrumb') + breadcrumb_section.style.ref( + self._client_class_name, f'../../../{self._service_name}' + ) + breadcrumb_section.write(f' / Client / exceptions / {shape.name}') + section.add_title_section(shape.name) + class_section = section.add_new_section(shape.name) + class_name = self._exception_class_name(shape) + class_section.style.start_sphinx_py_class(class_name=class_name) + self._add_top_level_documentation(class_section, shape) + self._add_exception_catch_example(class_section, shape) + self._add_response_attr(class_section, shape) + class_section.style.end_sphinx_py_class() + + def _add_top_level_documentation(self, section, shape): + if shape.documentation: + section.style.new_line() + section.include_doc_string(shape.documentation) + section.style.new_line() + + def _add_exception_catch_example(self, section, shape): + section.style.new_line() + section.style.bold('Example') + section.style.new_paragraph() + section.style.start_codeblock() + section.write('try:') + section.style.indent() + section.style.new_line() + section.write('...') + section.style.dedent() + section.style.new_line() + section.write(f'except client.exceptions.{shape.name} as e:') + section.style.indent() + section.style.new_line() + section.write('print(e.response)') + section.style.dedent() + section.style.end_codeblock() + + def _add_response_attr(self, section, shape): + response_section = section.add_new_section('response') + response_section.style.start_sphinx_py_attr('response') + self._add_response_attr_description(response_section) + self._add_response_example(response_section, shape) + self._add_response_params(response_section, shape) + response_section.style.end_sphinx_py_attr() + + def _add_response_attr_description(self, section): + section.style.new_line() + section.include_doc_string( + 'The parsed error response. All exceptions have a top level ' + '``Error`` key that provides normalized access to common ' + 'exception atrributes. All other keys are specific to this ' + 'service or exception class.' + ) + section.style.new_line() + + def _add_response_example(self, section, shape): + example_section = section.add_new_section('syntax') + example_section.style.new_line() + example_section.style.bold('Syntax') + example_section.style.new_paragraph() + documenter = ResponseExampleDocumenter( + service_name=self._service_name, + operation_name=None, + event_emitter=self._client.meta.events, + ) + documenter.document_example( + example_section, + shape, + include=[self._GENERIC_ERROR_SHAPE], + ) + + def _add_response_params(self, section, shape): + params_section = section.add_new_section('Structure') + params_section.style.new_line() + params_section.style.bold('Structure') + params_section.style.new_paragraph() + documenter = ResponseParamsDocumenter( + service_name=self._service_name, + operation_name=None, + event_emitter=self._client.meta.events, + ) + documenter.document_params( + params_section, + shape, + include=[self._GENERIC_ERROR_SHAPE], + ) + + +class ClientContextParamsDocumenter: + _CONFIG_GUIDE_LINK = ( + 'https://docs.aws.amazon.com/boto3/latest/guide/configuration.html' + ) + + OMITTED_CONTEXT_PARAMS = { + 's3': ( + 'Accelerate', + 'DisableMultiRegionAccessPoints', + 'ForcePathStyle', + 'UseArnRegion', + ), + 's3control': ('UseArnRegion',), + } + + def __init__(self, service_name, context_params): + self._service_name = service_name + self._context_params = context_params + + def document_context_params(self, section): + self._add_title(section) + self._add_overview(section) + self._add_context_params_list(section) + + def _add_title(self, section): + section.style.h2('Client Context Parameters') + + def _add_overview(self, section): + section.style.new_line() + section.write( + 'Client context parameters are configurable on a client ' + 'instance via the ``client_context_params`` parameter in the ' + '``Config`` object. For more detailed instructions and examples ' + 'on the exact usage of context params see the ' + ) + section.style.external_link( + title='configuration guide', + link=self._CONFIG_GUIDE_LINK, + ) + section.write('.') + section.style.new_line() + + def _add_context_params_list(self, section): + section.style.new_line() + sn = f'``{self._service_name}``' + section.writeln(f'The available {sn} client context params are:') + for param in self._context_params: + section.style.new_line() + name = f'``{xform_name(param.name)}``' + section.write(f'* {name} ({param.type}) - {param.documentation}') diff --git a/venv/lib/python3.12/site-packages/botocore/docs/docstring.py b/venv/lib/python3.12/site-packages/botocore/docs/docstring.py new file mode 100644 index 0000000..93b2e6b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/docstring.py @@ -0,0 +1,97 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from botocore.docs.bcdoc.restdoc import DocumentStructure +from botocore.docs.method import document_model_driven_method +from botocore.docs.paginator import document_paginate_method +from botocore.docs.waiter import document_wait_method + + +class LazyLoadedDocstring(str): + """Used for lazily loading docstrings + + You can instantiate this class and assign it to a __doc__ value. + The docstring will not be generated till accessed via __doc__ or + help(). Note that all docstring classes **must** subclass from + this class. It cannot be used directly as a docstring. + """ + + def __init__(self, *args, **kwargs): + """ + The args and kwargs are the same as the underlying document + generation function. These just get proxied to the underlying + function. + """ + super().__init__() + self._gen_args = args + self._gen_kwargs = kwargs + self._docstring = None + + def __new__(cls, *args, **kwargs): + # Needed in order to sub class from str with args and kwargs + return super().__new__(cls) + + def _write_docstring(self, *args, **kwargs): + raise NotImplementedError( + '_write_docstring is not implemented. Please subclass from ' + 'this class and provide your own _write_docstring method' + ) + + def expandtabs(self, tabsize=8): + """Expands tabs to spaces + + So this is a big hack in order to get lazy loaded docstring work + for the ``help()``. In the ``help()`` function, ``pydoc`` and + ``inspect`` are used. At some point the ``inspect.cleandoc`` + method is called. To clean the docs ``expandtabs`` is called + and that is where we override the method to generate and return the + docstrings. + """ + if self._docstring is None: + self._generate() + return self._docstring.expandtabs(tabsize) + + def __str__(self): + return self._generate() + + # __doc__ of target will use either __repr__ or __str__ of this class. + __repr__ = __str__ + + def _generate(self): + # Generate the docstring if it is not already cached. + if self._docstring is None: + self._docstring = self._create_docstring() + return self._docstring + + def _create_docstring(self): + docstring_structure = DocumentStructure('docstring', target='html') + # Call the document method function with the args and kwargs + # passed to the class. + self._write_docstring( + docstring_structure, *self._gen_args, **self._gen_kwargs + ) + return docstring_structure.flush_structure().decode('utf-8') + + +class ClientMethodDocstring(LazyLoadedDocstring): + def _write_docstring(self, *args, **kwargs): + document_model_driven_method(*args, **kwargs) + + +class WaiterDocstring(LazyLoadedDocstring): + def _write_docstring(self, *args, **kwargs): + document_wait_method(*args, **kwargs) + + +class PaginatorDocstring(LazyLoadedDocstring): + def _write_docstring(self, *args, **kwargs): + document_paginate_method(*args, **kwargs) diff --git a/venv/lib/python3.12/site-packages/botocore/docs/example.py b/venv/lib/python3.12/site-packages/botocore/docs/example.py new file mode 100644 index 0000000..cb43db5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/example.py @@ -0,0 +1,236 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from botocore.docs.shape import ShapeDocumenter +from botocore.docs.utils import py_default + + +class BaseExampleDocumenter(ShapeDocumenter): + def document_example( + self, section, shape, prefix=None, include=None, exclude=None + ): + """Generates an example based on a shape + + :param section: The section to write the documentation to. + + :param shape: The shape of the operation. + + :param prefix: Anything to be included before the example + + :type include: Dictionary where keys are parameter names and + values are the shapes of the parameter names. + :param include: The parameter shapes to include in the documentation. + + :type exclude: List of the names of the parameters to exclude. + :param exclude: The names of the parameters to exclude from + documentation. + """ + history = [] + section.style.new_line() + section.style.start_codeblock() + if prefix is not None: + section.write(prefix) + self.traverse_and_document_shape( + section=section, + shape=shape, + history=history, + include=include, + exclude=exclude, + ) + final_blank_line_section = section.add_new_section('final-blank-line') + final_blank_line_section.style.new_line() + + def document_recursive_shape(self, section, shape, **kwargs): + section.write('{\'... recursive ...\'}') + + def document_shape_default( + self, section, shape, history, include=None, exclude=None, **kwargs + ): + py_type = self._get_special_py_default(shape) + if py_type is None: + py_type = py_default(shape.type_name) + + if self._context.get('streaming_shape') == shape: + py_type = 'StreamingBody()' + section.write(py_type) + + def document_shape_type_string( + self, section, shape, history, include=None, exclude=None, **kwargs + ): + if 'enum' in shape.metadata: + for i, enum in enumerate(shape.metadata['enum']): + section.write(f'\'{enum}\'') + if i < len(shape.metadata['enum']) - 1: + section.write('|') + else: + self.document_shape_default(section, shape, history) + + def document_shape_type_list( + self, section, shape, history, include=None, exclude=None, **kwargs + ): + param_shape = shape.member + list_section = section.add_new_section('list-value') + self._start_nested_param(list_section, '[') + param_section = list_section.add_new_section( + 'member', context={'shape': param_shape.name} + ) + self.traverse_and_document_shape( + section=param_section, shape=param_shape, history=history + ) + ending_comma_section = list_section.add_new_section('ending-comma') + ending_comma_section.write(',') + ending_bracket_section = list_section.add_new_section('ending-bracket') + self._end_nested_param(ending_bracket_section, ']') + + def document_shape_type_structure( + self, section, shape, history, include=None, exclude=None, **kwargs + ): + if not shape.members: + section.write('{}') + return + + section = section.add_new_section('structure-value') + self._start_nested_param(section, '{') + + input_members = self._add_members_to_shape(shape.members, include) + + for i, param in enumerate(input_members): + if exclude and param in exclude: + continue + param_section = section.add_new_section(param) + param_section.write(f'\'{param}\': ') + param_shape = input_members[param] + param_value_section = param_section.add_new_section( + 'member-value', context={'shape': param_shape.name} + ) + self.traverse_and_document_shape( + section=param_value_section, + shape=param_shape, + history=history, + name=param, + ) + if i < len(input_members) - 1: + ending_comma_section = param_section.add_new_section( + 'ending-comma' + ) + ending_comma_section.write(',') + ending_comma_section.style.new_line() + self._end_structure(section, '{', '}') + + def document_shape_type_map( + self, section, shape, history, include=None, exclude=None, **kwargs + ): + map_section = section.add_new_section('map-value') + self._start_nested_param(map_section, '{') + value_shape = shape.value + key_section = map_section.add_new_section( + 'key', context={'shape': shape.key.name} + ) + key_section.write('\'string\': ') + value_section = map_section.add_new_section( + 'value', context={'shape': value_shape.name} + ) + self.traverse_and_document_shape( + section=value_section, shape=value_shape, history=history + ) + end_bracket_section = map_section.add_new_section('ending-bracket') + self._end_nested_param(end_bracket_section, '}') + + def _add_members_to_shape(self, members, include): + if include: + members = members.copy() + for param in include: + members[param.name] = param + return members + + def _start_nested_param(self, section, start=None): + if start is not None: + section.write(start) + section.style.indent() + section.style.indent() + section.style.new_line() + + def _end_nested_param(self, section, end=None): + section.style.dedent() + section.style.dedent() + section.style.new_line() + if end is not None: + section.write(end) + + def _end_structure(self, section, start, end): + # If there are no members in the strucuture, then make sure the + # start and the end bracket are on the same line, by removing all + # previous text and writing the start and end. + if not section.available_sections: + section.clear_text() + section.write(start + end) + self._end_nested_param(section) + else: + end_bracket_section = section.add_new_section('ending-bracket') + self._end_nested_param(end_bracket_section, end) + + +class ResponseExampleDocumenter(BaseExampleDocumenter): + EVENT_NAME = 'response-example' + + def document_shape_type_event_stream( + self, section, shape, history, **kwargs + ): + section.write('EventStream(') + self.document_shape_type_structure(section, shape, history, **kwargs) + end_section = section.add_new_section('event-stream-end') + end_section.write(')') + + +class RequestExampleDocumenter(BaseExampleDocumenter): + EVENT_NAME = 'request-example' + + def document_shape_type_structure( + self, section, shape, history, include=None, exclude=None, **kwargs + ): + param_format = '\'%s\'' + operator = ': ' + start = '{' + end = '}' + + if len(history) <= 1: + operator = '=' + start = '(' + end = ')' + param_format = '%s' + section = section.add_new_section('structure-value') + self._start_nested_param(section, start) + input_members = self._add_members_to_shape(shape.members, include) + + for i, param in enumerate(input_members): + if exclude and param in exclude: + continue + param_section = section.add_new_section(param) + param_section.write(param_format % param) + param_section.write(operator) + param_shape = input_members[param] + param_value_section = param_section.add_new_section( + 'member-value', context={'shape': param_shape.name} + ) + self.traverse_and_document_shape( + section=param_value_section, + shape=param_shape, + history=history, + name=param, + ) + if i < len(input_members) - 1: + ending_comma_section = param_section.add_new_section( + 'ending-comma' + ) + ending_comma_section.write(',') + ending_comma_section.style.new_line() + self._end_structure(section, start, end) diff --git a/venv/lib/python3.12/site-packages/botocore/docs/method.py b/venv/lib/python3.12/site-packages/botocore/docs/method.py new file mode 100644 index 0000000..5db906c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/method.py @@ -0,0 +1,328 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import inspect +import types + +from botocore.docs.example import ( + RequestExampleDocumenter, + ResponseExampleDocumenter, +) +from botocore.docs.params import ( + RequestParamsDocumenter, + ResponseParamsDocumenter, +) + +AWS_DOC_BASE = 'https://docs.aws.amazon.com/goto/WebAPI' + + +def get_instance_public_methods(instance): + """Retrieves an objects public methods + + :param instance: The instance of the class to inspect + :rtype: dict + :returns: A dictionary that represents an instance's methods where + the keys are the name of the methods and the + values are the handler to the method. + """ + instance_members = inspect.getmembers(instance) + instance_methods = {} + for name, member in instance_members: + if not name.startswith('_'): + if inspect.ismethod(member): + instance_methods[name] = member + return instance_methods + + +def document_model_driven_signature( + section, name, operation_model, include=None, exclude=None +): + """Documents the signature of a model-driven method + + :param section: The section to write the documentation to. + + :param name: The name of the method + + :param operation_model: The operation model for the method + + :type include: Dictionary where keys are parameter names and + values are the shapes of the parameter names. + :param include: The parameter shapes to include in the documentation. + + :type exclude: List of the names of the parameters to exclude. + :param exclude: The names of the parameters to exclude from + documentation. + """ + params = {} + if operation_model.input_shape: + params = operation_model.input_shape.members + + parameter_names = list(params.keys()) + + if include is not None: + for member in include: + parameter_names.append(member.name) + + if exclude is not None: + for member in exclude: + if member in parameter_names: + parameter_names.remove(member) + + signature_params = '' + if parameter_names: + signature_params = '**kwargs' + section.style.start_sphinx_py_method(name, signature_params) + + +def document_custom_signature( + section, name, method, include=None, exclude=None +): + """Documents the signature of a custom method + + :param section: The section to write the documentation to. + + :param name: The name of the method + + :param method: The handle to the method being documented + + :type include: Dictionary where keys are parameter names and + values are the shapes of the parameter names. + :param include: The parameter shapes to include in the documentation. + + :type exclude: List of the names of the parameters to exclude. + :param exclude: The names of the parameters to exclude from + documentation. + """ + signature = inspect.signature(method) + # "raw" class methods are FunctionType and they include "self" param + # object methods are MethodType and they skip the "self" param + if isinstance(method, types.FunctionType): + self_param = next(iter(signature.parameters)) + self_kind = signature.parameters[self_param].kind + # safety check that we got the right parameter + assert self_kind == inspect.Parameter.POSITIONAL_OR_KEYWORD + new_params = signature.parameters.copy() + del new_params[self_param] + signature = signature.replace(parameters=new_params.values()) + signature_params = str(signature).lstrip('(') + signature_params = signature_params.rstrip(')') + section.style.start_sphinx_py_method(name, signature_params) + + +def document_custom_method(section, method_name, method): + """Documents a non-data driven method + + :param section: The section to write the documentation to. + + :param method_name: The name of the method + + :param method: The handle to the method being documented + """ + full_method_name = f"{section.context.get('qualifier', '')}{method_name}" + document_custom_signature(section, full_method_name, method) + method_intro_section = section.add_new_section('method-intro') + method_intro_section.writeln('') + doc_string = inspect.getdoc(method) + if doc_string is not None: + method_intro_section.style.write_py_doc_string(doc_string) + + +def document_model_driven_method( + section, + method_name, + operation_model, + event_emitter, + method_description=None, + example_prefix=None, + include_input=None, + include_output=None, + exclude_input=None, + exclude_output=None, + document_output=True, + include_signature=True, +): + """Documents an individual method + + :param section: The section to write to + + :param method_name: The name of the method + + :param operation_model: The model of the operation + + :param event_emitter: The event emitter to use to emit events + + :param example_prefix: The prefix to use in the method example. + + :type include_input: Dictionary where keys are parameter names and + values are the shapes of the parameter names. + :param include_input: The parameter shapes to include in the + input documentation. + + :type include_output: Dictionary where keys are parameter names and + values are the shapes of the parameter names. + :param include_input: The parameter shapes to include in the + output documentation. + + :type exclude_input: List of the names of the parameters to exclude. + :param exclude_input: The names of the parameters to exclude from + input documentation. + + :type exclude_output: List of the names of the parameters to exclude. + :param exclude_input: The names of the parameters to exclude from + output documentation. + + :param document_output: A boolean flag to indicate whether to + document the output. + + :param include_signature: Whether or not to include the signature. + It is useful for generating docstrings. + """ + # Add the signature if specified. + if include_signature: + document_model_driven_signature( + section, + method_name, + operation_model, + include=include_input, + exclude=exclude_input, + ) + + # Add the description for the method. + method_intro_section = section.add_new_section('method-intro') + method_intro_section.include_doc_string(method_description) + if operation_model.deprecated: + method_intro_section.style.start_danger() + method_intro_section.writeln( + 'This operation is deprecated and may not function as ' + 'expected. This operation should not be used going forward ' + 'and is only kept for the purpose of backwards compatiblity.' + ) + method_intro_section.style.end_danger() + service_uid = operation_model.service_model.metadata.get('uid') + if service_uid is not None: + method_intro_section.style.new_paragraph() + method_intro_section.write("See also: ") + link = f"{AWS_DOC_BASE}/{service_uid}/{operation_model.name}" + method_intro_section.style.external_link( + title="AWS API Documentation", link=link + ) + method_intro_section.writeln('') + + # Add the example section. + example_section = section.add_new_section('request-example') + example_section.style.new_paragraph() + example_section.style.bold('Request Syntax') + + context = { + 'special_shape_types': { + 'streaming_input_shape': operation_model.get_streaming_input(), + 'streaming_output_shape': operation_model.get_streaming_output(), + 'eventstream_output_shape': operation_model.get_event_stream_output(), + }, + } + + if operation_model.input_shape: + RequestExampleDocumenter( + service_name=operation_model.service_model.service_name, + operation_name=operation_model.name, + event_emitter=event_emitter, + context=context, + ).document_example( + example_section, + operation_model.input_shape, + prefix=example_prefix, + include=include_input, + exclude=exclude_input, + ) + else: + example_section.style.new_paragraph() + example_section.style.start_codeblock() + example_section.write(example_prefix + '()') + + # Add the request parameter documentation. + request_params_section = section.add_new_section('request-params') + if operation_model.input_shape: + RequestParamsDocumenter( + service_name=operation_model.service_model.service_name, + operation_name=operation_model.name, + event_emitter=event_emitter, + context=context, + ).document_params( + request_params_section, + operation_model.input_shape, + include=include_input, + exclude=exclude_input, + ) + + # Add the return value documentation + return_section = section.add_new_section('return') + return_section.style.new_line() + if operation_model.output_shape is not None and document_output: + return_section.write(':rtype: dict') + return_section.style.new_line() + return_section.write(':returns: ') + return_section.style.indent() + return_section.style.new_line() + + # If the operation is an event stream, describe the tagged union + event_stream_output = operation_model.get_event_stream_output() + if event_stream_output: + event_section = return_section.add_new_section('event-stream') + event_section.style.new_paragraph() + event_section.write( + 'The response of this operation contains an ' + ':class:`.EventStream` member. When iterated the ' + ':class:`.EventStream` will yield events based on the ' + 'structure below, where only one of the top level keys ' + 'will be present for any given event.' + ) + event_section.style.new_line() + + # Add an example return value + return_example_section = return_section.add_new_section( + 'response-example' + ) + return_example_section.style.new_line() + return_example_section.style.bold('Response Syntax') + return_example_section.style.new_paragraph() + ResponseExampleDocumenter( + service_name=operation_model.service_model.service_name, + operation_name=operation_model.name, + event_emitter=event_emitter, + context=context, + ).document_example( + return_example_section, + operation_model.output_shape, + include=include_output, + exclude=exclude_output, + ) + + # Add a description for the return value + return_description_section = return_section.add_new_section( + 'description' + ) + return_description_section.style.new_line() + return_description_section.style.bold('Response Structure') + return_description_section.style.new_paragraph() + ResponseParamsDocumenter( + service_name=operation_model.service_model.service_name, + operation_name=operation_model.name, + event_emitter=event_emitter, + context=context, + ).document_params( + return_description_section, + operation_model.output_shape, + include=include_output, + exclude=exclude_output, + ) + else: + return_section.write(':returns: None') diff --git a/venv/lib/python3.12/site-packages/botocore/docs/paginator.py b/venv/lib/python3.12/site-packages/botocore/docs/paginator.py new file mode 100644 index 0000000..e177125 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/paginator.py @@ -0,0 +1,240 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import os + +from botocore import xform_name +from botocore.compat import OrderedDict +from botocore.docs.bcdoc.restdoc import DocumentStructure +from botocore.docs.method import document_model_driven_method +from botocore.docs.utils import DocumentedShape +from botocore.utils import get_service_module_name + + +class PaginatorDocumenter: + def __init__(self, client, service_paginator_model, root_docs_path): + self._client = client + self._client_class_name = self._client.__class__.__name__ + self._service_name = self._client.meta.service_model.service_name + self._service_paginator_model = service_paginator_model + self._root_docs_path = root_docs_path + self._USER_GUIDE_LINK = ( + 'https://docs.aws.amazon.com/boto3/latest/guide/paginators.html' + ) + + def document_paginators(self, section): + """Documents the various paginators for a service + + param section: The section to write to. + """ + section.style.h2('Paginators') + self._add_overview(section) + section.style.new_line() + section.writeln('The available paginators are:') + section.style.toctree() + + paginator_names = sorted( + self._service_paginator_model._paginator_config + ) + + # List the available paginators and then document each paginator. + for paginator_name in paginator_names: + section.style.tocitem( + f'{self._service_name}/paginator/{paginator_name}' + ) + # Create a new DocumentStructure for each paginator and add contents. + paginator_doc_structure = DocumentStructure( + paginator_name, target='html' + ) + self._add_paginator(paginator_doc_structure, paginator_name) + # Write paginators in individual/nested files. + # Path: /reference/services//paginator/.rst + paginator_dir_path = os.path.join( + self._root_docs_path, self._service_name, 'paginator' + ) + paginator_doc_structure.write_to_file( + paginator_dir_path, paginator_name + ) + + def _add_paginator(self, section, paginator_name): + breadcrumb_section = section.add_new_section('breadcrumb') + breadcrumb_section.style.ref( + self._client_class_name, f'../../{self._service_name}' + ) + breadcrumb_section.write(f' / Paginator / {paginator_name}') + section.add_title_section(paginator_name) + + # Docment the paginator class + paginator_section = section.add_new_section(paginator_name) + paginator_section.style.start_sphinx_py_class( + class_name=( + f'{self._client_class_name}.Paginator.{paginator_name}' + ) + ) + paginator_section.style.start_codeblock() + paginator_section.style.new_line() + + # Document how to instantiate the paginator. + paginator_section.write( + f"paginator = client.get_paginator('{xform_name(paginator_name)}')" + ) + paginator_section.style.end_codeblock() + paginator_section.style.new_line() + # Get the pagination model for the particular paginator. + paginator_config = self._service_paginator_model.get_paginator( + paginator_name + ) + document_paginate_method( + section=paginator_section, + paginator_name=paginator_name, + event_emitter=self._client.meta.events, + service_model=self._client.meta.service_model, + paginator_config=paginator_config, + ) + + def _add_overview(self, section): + section.style.new_line() + section.write( + 'Paginators are available on a client instance ' + 'via the ``get_paginator`` method. For more detailed instructions ' + 'and examples on the usage of paginators, see the ' + 'paginators ' + ) + section.style.external_link( + title='user guide', + link=self._USER_GUIDE_LINK, + ) + section.write('.') + section.style.new_line() + + +def document_paginate_method( + section, + paginator_name, + event_emitter, + service_model, + paginator_config, + include_signature=True, +): + """Documents the paginate method of a paginator + + :param section: The section to write to + + :param paginator_name: The name of the paginator. It is snake cased. + + :param event_emitter: The event emitter to use to emit events + + :param service_model: The service model + + :param paginator_config: The paginator config associated to a particular + paginator. + + :param include_signature: Whether or not to include the signature. + It is useful for generating docstrings. + """ + # Retrieve the operation model of the underlying operation. + operation_model = service_model.operation_model(paginator_name) + + # Add representations of the request and response parameters + # we want to include in the description of the paginate method. + # These are parameters we expose via the botocore interface. + pagination_config_members = OrderedDict() + + pagination_config_members['MaxItems'] = DocumentedShape( + name='MaxItems', + type_name='integer', + documentation=( + '

The total number of items to return. If the total ' + 'number of items available is more than the value ' + 'specified in max-items then a NextToken ' + 'will be provided in the output that you can use to ' + 'resume pagination.

' + ), + ) + + if paginator_config.get('limit_key', None): + pagination_config_members['PageSize'] = DocumentedShape( + name='PageSize', + type_name='integer', + documentation='

The size of each page.

', + ) + + pagination_config_members['StartingToken'] = DocumentedShape( + name='StartingToken', + type_name='string', + documentation=( + '

A token to specify where to start paginating. ' + 'This is the NextToken from a previous ' + 'response.

' + ), + ) + + botocore_pagination_params = [ + DocumentedShape( + name='PaginationConfig', + type_name='structure', + documentation=( + '

A dictionary that provides parameters to control ' + 'pagination.

' + ), + members=pagination_config_members, + ) + ] + + botocore_pagination_response_params = [ + DocumentedShape( + name='NextToken', + type_name='string', + documentation=('

A token to resume pagination.

'), + ) + ] + + service_pagination_params = [] + + # Add the normal input token of the method to a list + # of input paramters that we wish to hide since we expose our own. + if isinstance(paginator_config['input_token'], list): + service_pagination_params += paginator_config['input_token'] + else: + service_pagination_params.append(paginator_config['input_token']) + + # Hide the limit key in the documentation. + if paginator_config.get('limit_key', None): + service_pagination_params.append(paginator_config['limit_key']) + + # Hide the output tokens in the documentation. + service_pagination_response_params = [] + if isinstance(paginator_config['output_token'], list): + service_pagination_response_params += paginator_config['output_token'] + else: + service_pagination_response_params.append( + paginator_config['output_token'] + ) + + paginate_description = ( + 'Creates an iterator that will paginate through responses ' + f'from :py:meth:`{get_service_module_name(service_model)}.Client.{xform_name(paginator_name)}`.' + ) + + document_model_driven_method( + section, + 'paginate', + operation_model, + event_emitter=event_emitter, + method_description=paginate_description, + example_prefix='response_iterator = paginator.paginate', + include_input=botocore_pagination_params, + include_output=botocore_pagination_response_params, + exclude_input=service_pagination_params, + exclude_output=service_pagination_response_params, + include_signature=include_signature, + ) diff --git a/venv/lib/python3.12/site-packages/botocore/docs/params.py b/venv/lib/python3.12/site-packages/botocore/docs/params.py new file mode 100644 index 0000000..7e0f398 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/params.py @@ -0,0 +1,302 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from botocore.docs.shape import ShapeDocumenter +from botocore.docs.utils import py_type_name + + +class BaseParamsDocumenter(ShapeDocumenter): + def document_params(self, section, shape, include=None, exclude=None): + """Fills out the documentation for a section given a model shape. + + :param section: The section to write the documentation to. + + :param shape: The shape of the operation. + + :type include: Dictionary where keys are parameter names and + values are the shapes of the parameter names. + :param include: The parameter shapes to include in the documentation. + + :type exclude: List of the names of the parameters to exclude. + :param exclude: The names of the parameters to exclude from + documentation. + """ + history = [] + self.traverse_and_document_shape( + section=section, + shape=shape, + history=history, + name=None, + include=include, + exclude=exclude, + ) + + def document_recursive_shape(self, section, shape, **kwargs): + self._add_member_documentation(section, shape, **kwargs) + + def document_shape_default( + self, section, shape, history, include=None, exclude=None, **kwargs + ): + self._add_member_documentation(section, shape, **kwargs) + + def document_shape_type_list( + self, section, shape, history, include=None, exclude=None, **kwargs + ): + self._add_member_documentation(section, shape, **kwargs) + param_shape = shape.member + param_section = section.add_new_section( + param_shape.name, context={'shape': shape.member.name} + ) + self._start_nested_param(param_section) + self.traverse_and_document_shape( + section=param_section, + shape=param_shape, + history=history, + name=None, + ) + section = section.add_new_section('end-list') + self._end_nested_param(section) + + def document_shape_type_map( + self, section, shape, history, include=None, exclude=None, **kwargs + ): + self._add_member_documentation(section, shape, **kwargs) + + key_section = section.add_new_section( + 'key', context={'shape': shape.key.name} + ) + self._start_nested_param(key_section) + self._add_member_documentation(key_section, shape.key) + + param_section = section.add_new_section( + shape.value.name, context={'shape': shape.value.name} + ) + param_section.style.indent() + self._start_nested_param(param_section) + self.traverse_and_document_shape( + section=param_section, + shape=shape.value, + history=history, + name=None, + ) + + end_section = section.add_new_section('end-map') + self._end_nested_param(end_section) + self._end_nested_param(end_section) + + def document_shape_type_structure( + self, + section, + shape, + history, + include=None, + exclude=None, + name=None, + **kwargs, + ): + members = self._add_members_to_shape(shape.members, include) + self._add_member_documentation(section, shape, name=name) + for param in members: + if exclude and param in exclude: + continue + param_shape = members[param] + param_section = section.add_new_section( + param, context={'shape': param_shape.name} + ) + self._start_nested_param(param_section) + self.traverse_and_document_shape( + section=param_section, + shape=param_shape, + history=history, + name=param, + ) + section = section.add_new_section('end-structure') + self._end_nested_param(section) + + def _add_member_documentation(self, section, shape, **kwargs): + pass + + def _add_members_to_shape(self, members, include): + if include: + members = members.copy() + for param in include: + members[param.name] = param + return members + + def _document_non_top_level_param_type(self, type_section, shape): + special_py_type = self._get_special_py_type_name(shape) + py_type = py_type_name(shape.type_name) + + type_format = '(%s) --' + if special_py_type is not None: + # Special type can reference a linked class. + # Italicizing it blows away the link. + type_section.write(type_format % special_py_type) + else: + type_section.style.italics(type_format % py_type) + type_section.write(' ') + + def _start_nested_param(self, section): + section.style.indent() + section.style.new_line() + + def _end_nested_param(self, section): + section.style.dedent() + section.style.new_line() + + +class ResponseParamsDocumenter(BaseParamsDocumenter): + """Generates the description for the response parameters""" + + EVENT_NAME = 'response-params' + + def _add_member_documentation(self, section, shape, name=None, **kwargs): + name_section = section.add_new_section('param-name') + name_section.write('- ') + if name is not None: + name_section.style.bold(f'{name}') + name_section.write(' ') + type_section = section.add_new_section('param-type') + self._document_non_top_level_param_type(type_section, shape) + + documentation_section = section.add_new_section('param-documentation') + if shape.documentation: + documentation_section.style.indent() + if getattr(shape, 'is_tagged_union', False): + tagged_union_docs = section.add_new_section( + 'param-tagged-union-docs' + ) + note = ( + '.. note::' + ' This is a Tagged Union structure. Only one of the ' + ' following top level keys will be set: %s. ' + ' If a client receives an unknown member it will ' + ' set ``SDK_UNKNOWN_MEMBER`` as the top level key, ' + ' which maps to the name or tag of the unknown ' + ' member. The structure of ``SDK_UNKNOWN_MEMBER`` is ' + ' as follows' + ) + tagged_union_members_str = ', '.join( + [f'``{key}``' for key in shape.members.keys()] + ) + unknown_code_example = ( + '\'SDK_UNKNOWN_MEMBER\': {\'name\': \'UnknownMemberName\'}' + ) + tagged_union_docs.write(note % (tagged_union_members_str)) + example = section.add_new_section('param-unknown-example') + example.style.codeblock(unknown_code_example) + documentation_section.include_doc_string(shape.documentation) + section.style.new_paragraph() + + def document_shape_type_event_stream( + self, section, shape, history, **kwargs + ): + self.document_shape_type_structure(section, shape, history, **kwargs) + + +class RequestParamsDocumenter(BaseParamsDocumenter): + """Generates the description for the request parameters""" + + EVENT_NAME = 'request-params' + + def document_shape_type_structure( + self, section, shape, history, include=None, exclude=None, **kwargs + ): + if len(history) > 1: + self._add_member_documentation(section, shape, **kwargs) + section.style.indent() + members = self._add_members_to_shape(shape.members, include) + for i, param in enumerate(members): + if exclude and param in exclude: + continue + param_shape = members[param] + param_section = section.add_new_section( + param, context={'shape': param_shape.name} + ) + param_section.style.new_line() + is_required = param in shape.required_members + self.traverse_and_document_shape( + section=param_section, + shape=param_shape, + history=history, + name=param, + is_required=is_required, + ) + section = section.add_new_section('end-structure') + if len(history) > 1: + section.style.dedent() + section.style.new_line() + + def _add_member_documentation( + self, + section, + shape, + name=None, + is_top_level_param=False, + is_required=False, + **kwargs, + ): + py_type = self._get_special_py_type_name(shape) + if py_type is None: + py_type = py_type_name(shape.type_name) + if is_top_level_param: + type_section = section.add_new_section('param-type') + type_section.write(f':type {name}: {py_type}') + end_type_section = type_section.add_new_section('end-param-type') + end_type_section.style.new_line() + name_section = section.add_new_section('param-name') + name_section.write(f':param {name}: ') + + else: + name_section = section.add_new_section('param-name') + name_section.write('- ') + if name is not None: + name_section.style.bold(f'{name}') + name_section.write(' ') + type_section = section.add_new_section('param-type') + self._document_non_top_level_param_type(type_section, shape) + + if is_required: + is_required_section = section.add_new_section('is-required') + is_required_section.style.indent() + is_required_section.style.bold('[REQUIRED]') + is_required_section.write(' ') + if shape.documentation: + documentation_section = section.add_new_section( + 'param-documentation' + ) + documentation_section.style.indent() + if getattr(shape, 'is_tagged_union', False): + tagged_union_docs = section.add_new_section( + 'param-tagged-union-docs' + ) + note = ( + '.. note::' + ' This is a Tagged Union structure. Only one of the ' + ' following top level keys can be set: %s. ' + ) + tagged_union_members_str = ', '.join( + [f'``{key}``' for key in shape.members.keys()] + ) + tagged_union_docs.write(note % (tagged_union_members_str)) + documentation_section.include_doc_string(shape.documentation) + self._add_special_trait_documentation(documentation_section, shape) + end_param_section = section.add_new_section('end-param') + end_param_section.style.new_paragraph() + + def _add_special_trait_documentation(self, section, shape): + if 'idempotencyToken' in shape.metadata: + self._append_idempotency_documentation(section) + + def _append_idempotency_documentation(self, section): + docstring = 'This field is autopopulated if not provided.' + section.write(docstring) diff --git a/venv/lib/python3.12/site-packages/botocore/docs/service.py b/venv/lib/python3.12/site-packages/botocore/docs/service.py new file mode 100644 index 0000000..d20a889 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/service.py @@ -0,0 +1,133 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from botocore.docs.bcdoc.restdoc import DocumentStructure +from botocore.docs.client import ( + ClientContextParamsDocumenter, + ClientDocumenter, + ClientExceptionsDocumenter, +) +from botocore.docs.paginator import PaginatorDocumenter +from botocore.docs.waiter import WaiterDocumenter +from botocore.exceptions import DataNotFoundError + + +class ServiceDocumenter: + def __init__(self, service_name, session, root_docs_path): + self._session = session + self._service_name = service_name + self._root_docs_path = root_docs_path + + self._client = self._session.create_client( + service_name, + region_name='us-east-1', + aws_access_key_id='foo', + aws_secret_access_key='bar', + ) + self._event_emitter = self._client.meta.events + + self.sections = [ + 'title', + 'client-api', + 'client-exceptions', + 'paginator-api', + 'waiter-api', + 'client-context-params', + ] + + def document_service(self): + """Documents an entire service. + + :returns: The reStructured text of the documented service. + """ + doc_structure = DocumentStructure( + self._service_name, section_names=self.sections, target='html' + ) + self.title(doc_structure.get_section('title')) + self.client_api(doc_structure.get_section('client-api')) + self.client_exceptions(doc_structure.get_section('client-exceptions')) + self.paginator_api(doc_structure.get_section('paginator-api')) + self.waiter_api(doc_structure.get_section('waiter-api')) + context_params_section = doc_structure.get_section( + 'client-context-params' + ) + self.client_context_params(context_params_section) + return doc_structure.flush_structure() + + def title(self, section): + section.style.h1(self._client.__class__.__name__) + self._event_emitter.emit( + f"docs.title.{self._service_name}", section=section + ) + + def table_of_contents(self, section): + section.style.table_of_contents(title='Table of Contents', depth=2) + + def client_api(self, section): + examples = None + try: + examples = self.get_examples(self._service_name) + except DataNotFoundError: + pass + + ClientDocumenter( + self._client, self._root_docs_path, examples + ).document_client(section) + + def client_exceptions(self, section): + ClientExceptionsDocumenter( + self._client, self._root_docs_path + ).document_exceptions(section) + + def paginator_api(self, section): + try: + service_paginator_model = self._session.get_paginator_model( + self._service_name + ) + except DataNotFoundError: + return + if service_paginator_model._paginator_config: + paginator_documenter = PaginatorDocumenter( + self._client, service_paginator_model, self._root_docs_path + ) + paginator_documenter.document_paginators(section) + + def waiter_api(self, section): + if self._client.waiter_names: + service_waiter_model = self._session.get_waiter_model( + self._service_name + ) + waiter_documenter = WaiterDocumenter( + self._client, service_waiter_model, self._root_docs_path + ) + waiter_documenter.document_waiters(section) + + def get_examples(self, service_name, api_version=None): + loader = self._session.get_component('data_loader') + examples = loader.load_service_model( + service_name, 'examples-1', api_version + ) + return examples['examples'] + + def client_context_params(self, section): + omitted_params = ClientContextParamsDocumenter.OMITTED_CONTEXT_PARAMS + params_to_omit = omitted_params.get(self._service_name, []) + service_model = self._client.meta.service_model + raw_context_params = service_model.client_context_parameters + context_params = [ + p for p in raw_context_params if p.name not in params_to_omit + ] + if context_params: + context_param_documenter = ClientContextParamsDocumenter( + self._service_name, context_params + ) + context_param_documenter.document_context_params(section) diff --git a/venv/lib/python3.12/site-packages/botocore/docs/shape.py b/venv/lib/python3.12/site-packages/botocore/docs/shape.py new file mode 100644 index 0000000..640a5d1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/shape.py @@ -0,0 +1,135 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + + +# NOTE: This class should not be instantiated and its +# ``traverse_and_document_shape`` method called directly. It should be +# inherited from a Documenter class with the appropriate methods +# and attributes. +from botocore.utils import is_json_value_header + + +class ShapeDocumenter: + EVENT_NAME = '' + + def __init__( + self, service_name, operation_name, event_emitter, context=None + ): + self._service_name = service_name + self._operation_name = operation_name + self._event_emitter = event_emitter + self._context = context + if context is None: + self._context = {'special_shape_types': {}} + + def traverse_and_document_shape( + self, + section, + shape, + history, + include=None, + exclude=None, + name=None, + is_required=False, + ): + """Traverses and documents a shape + + Will take a self class and call its appropriate methods as a shape + is traversed. + + :param section: The section to document. + + :param history: A list of the names of the shapes that have been + traversed. + + :type include: Dictionary where keys are parameter names and + values are the shapes of the parameter names. + :param include: The parameter shapes to include in the documentation. + + :type exclude: List of the names of the parameters to exclude. + :param exclude: The names of the parameters to exclude from + documentation. + + :param name: The name of the shape. + + :param is_required: If the shape is a required member. + """ + param_type = shape.type_name + if getattr(shape, 'serialization', {}).get('eventstream'): + param_type = 'event_stream' + if shape.name in history: + self.document_recursive_shape(section, shape, name=name) + else: + history.append(shape.name) + is_top_level_param = len(history) == 2 + if hasattr(shape, 'is_document_type') and shape.is_document_type: + param_type = 'document' + getattr( + self, + f"document_shape_type_{param_type}", + self.document_shape_default, + )( + section, + shape, + history=history, + name=name, + include=include, + exclude=exclude, + is_top_level_param=is_top_level_param, + is_required=is_required, + ) + if is_top_level_param: + self._event_emitter.emit( + f"docs.{self.EVENT_NAME}.{self._service_name}.{self._operation_name}.{name}", + section=section, + ) + at_overlying_method_section = len(history) == 1 + if at_overlying_method_section: + self._event_emitter.emit( + f"docs.{self.EVENT_NAME}.{self._service_name}.{self._operation_name}.complete-section", + section=section, + ) + history.pop() + + def _get_special_py_default(self, shape): + special_defaults = { + 'document_type': '{...}|[...]|123|123.4|\'string\'|True|None', + 'jsonvalue_header': '{...}|[...]|123|123.4|\'string\'|True|None', + 'streaming_input_shape': 'b\'bytes\'|file', + 'streaming_output_shape': 'StreamingBody()', + 'eventstream_output_shape': 'EventStream()', + } + return self._get_value_for_special_type(shape, special_defaults) + + def _get_special_py_type_name(self, shape): + special_type_names = { + 'document_type': ':ref:`document`', + 'jsonvalue_header': 'JSON serializable', + 'streaming_input_shape': 'bytes or seekable file-like object', + 'streaming_output_shape': ':class:`.StreamingBody`', + 'eventstream_output_shape': ':class:`.EventStream`', + } + return self._get_value_for_special_type(shape, special_type_names) + + def _get_value_for_special_type(self, shape, special_type_map): + if is_json_value_header(shape): + return special_type_map['jsonvalue_header'] + if hasattr(shape, 'is_document_type') and shape.is_document_type: + return special_type_map['document_type'] + for special_type, marked_shape in self._context[ + 'special_shape_types' + ].items(): + if special_type in special_type_map: + if shape == marked_shape: + return special_type_map[special_type] + return None diff --git a/venv/lib/python3.12/site-packages/botocore/docs/sharedexample.py b/venv/lib/python3.12/site-packages/botocore/docs/sharedexample.py new file mode 100644 index 0000000..29d3df5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/sharedexample.py @@ -0,0 +1,227 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import numbers +import re + +from botocore.docs.utils import escape_controls +from botocore.utils import parse_timestamp + + +class SharedExampleDocumenter: + def document_shared_example( + self, example, prefix, section, operation_model + ): + """Documents a single shared example based on its definition. + + :param example: The model of the example + + :param prefix: The prefix to use in the method example. + + :param section: The section to write to. + + :param operation_model: The model of the operation used in the example + """ + section.style.new_paragraph() + section.write(example.get('description')) + section.style.new_line() + self.document_input( + section, example, prefix, operation_model.input_shape + ) + self.document_output(section, example, operation_model.output_shape) + + def document_input(self, section, example, prefix, shape): + input_section = section.add_new_section('input') + input_section.style.start_codeblock() + if prefix is not None: + input_section.write(prefix) + params = example.get('input', {}) + comments = example.get('comments') + if comments: + comments = comments.get('input') + param_section = input_section.add_new_section('parameters') + self._document_params(param_section, params, comments, [], shape) + closing_section = input_section.add_new_section('input-close') + closing_section.style.new_line() + closing_section.style.new_line() + closing_section.write('print(response)') + closing_section.style.end_codeblock() + + def document_output(self, section, example, shape): + output_section = section.add_new_section('output') + output_section.style.new_line() + output_section.write('Expected Output:') + output_section.style.new_line() + output_section.style.start_codeblock() + params = example.get('output', {}) + + # There might not be an output, but we will return metadata anyway + params['ResponseMetadata'] = {"...": "..."} + comments = example.get('comments') + if comments: + comments = comments.get('output') + self._document_dict(output_section, params, comments, [], shape, True) + closing_section = output_section.add_new_section('output-close') + closing_section.style.end_codeblock() + + def _document(self, section, value, comments, path, shape): + """ + :param section: The section to add the docs to. + + :param value: The input / output values representing the parameters that + are included in the example. + + :param comments: The dictionary containing all the comments to be + applied to the example. + + :param path: A list describing where the documenter is in traversing the + parameters. This is used to find the equivalent location + in the comments dictionary. + """ + if isinstance(value, dict): + self._document_dict(section, value, comments, path, shape) + elif isinstance(value, list): + self._document_list(section, value, comments, path, shape) + elif isinstance(value, numbers.Number): + self._document_number(section, value, path) + elif shape and shape.type_name == 'timestamp': + self._document_datetime(section, value, path) + else: + self._document_str(section, value, path) + + def _document_dict( + self, section, value, comments, path, shape, top_level=False + ): + dict_section = section.add_new_section('dict-value') + self._start_nested_value(dict_section, '{') + for key, val in value.items(): + path.append(f'.{key}') + item_section = dict_section.add_new_section(key) + item_section.style.new_line() + item_comment = self._get_comment(path, comments) + if item_comment: + item_section.write(item_comment) + item_section.style.new_line() + item_section.write(f"'{key}': ") + + # Shape could be none if there is no output besides ResponseMetadata + item_shape = None + if shape: + if shape.type_name == 'structure': + item_shape = shape.members.get(key) + elif shape.type_name == 'map': + item_shape = shape.value + self._document(item_section, val, comments, path, item_shape) + path.pop() + dict_section_end = dict_section.add_new_section('ending-brace') + self._end_nested_value(dict_section_end, '}') + if not top_level: + dict_section_end.write(',') + + def _document_params(self, section, value, comments, path, shape): + param_section = section.add_new_section('param-values') + self._start_nested_value(param_section, '(') + for key, val in value.items(): + path.append(f'.{key}') + item_section = param_section.add_new_section(key) + item_section.style.new_line() + item_comment = self._get_comment(path, comments) + if item_comment: + item_section.write(item_comment) + item_section.style.new_line() + item_section.write(key + '=') + + # Shape could be none if there are no input parameters + item_shape = None + if shape: + item_shape = shape.members.get(key) + self._document(item_section, val, comments, path, item_shape) + path.pop() + param_section_end = param_section.add_new_section('ending-parenthesis') + self._end_nested_value(param_section_end, ')') + + def _document_list(self, section, value, comments, path, shape): + list_section = section.add_new_section('list-section') + self._start_nested_value(list_section, '[') + item_shape = shape.member + for index, val in enumerate(value): + item_section = list_section.add_new_section(index) + item_section.style.new_line() + path.append(f'[{index}]') + item_comment = self._get_comment(path, comments) + if item_comment: + item_section.write(item_comment) + item_section.style.new_line() + self._document(item_section, val, comments, path, item_shape) + path.pop() + list_section_end = list_section.add_new_section('ending-bracket') + self._end_nested_value(list_section_end, '],') + + def _document_str(self, section, value, path): + # We do the string conversion because this might accept a type that + # we don't specifically address. + safe_value = escape_controls(value) + section.write(f"'{safe_value}',") + + def _document_number(self, section, value, path): + section.write(f"{str(value)},") + + def _document_datetime(self, section, value, path): + datetime_tuple = parse_timestamp(value).timetuple() + datetime_str = str(datetime_tuple[0]) + for i in range(1, len(datetime_tuple)): + datetime_str += ", " + str(datetime_tuple[i]) + section.write(f"datetime({datetime_str}),") + + def _get_comment(self, path, comments): + key = re.sub(r'^\.', '', ''.join(path)) + if comments and key in comments: + return '# ' + comments[key] + else: + return '' + + def _start_nested_value(self, section, start): + section.write(start) + section.style.indent() + section.style.indent() + + def _end_nested_value(self, section, end): + section.style.dedent() + section.style.dedent() + section.style.new_line() + section.write(end) + + +def document_shared_examples( + section, operation_model, example_prefix, shared_examples +): + """Documents the shared examples + + :param section: The section to write to. + + :param operation_model: The model of the operation. + + :param example_prefix: The prefix to use in the method example. + + :param shared_examples: The shared JSON examples from the model. + """ + container_section = section.add_new_section('shared-examples') + container_section.style.new_paragraph() + container_section.style.bold('Examples') + documenter = SharedExampleDocumenter() + for example in shared_examples: + documenter.document_shared_example( + example=example, + section=container_section.add_new_section(example['id']), + prefix=example_prefix, + operation_model=operation_model, + ) diff --git a/venv/lib/python3.12/site-packages/botocore/docs/translator.py b/venv/lib/python3.12/site-packages/botocore/docs/translator.py new file mode 100644 index 0000000..0b0a308 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/translator.py @@ -0,0 +1,62 @@ +# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from docutils import nodes +from sphinx.locale import admonitionlabels +from sphinx.writers.html5 import HTML5Translator as SphinxHTML5Translator + + +class BotoHTML5Translator(SphinxHTML5Translator): + """Extension of Sphinx's ``HTML5Translator`` for Botocore documentation.""" + + IGNORE_IMPLICIT_HEADINGS = [ + '[REQUIRED]', + ] + + def visit_admonition(self, node, name=""): + """Uses the h3 tag for admonition titles instead of the p tag.""" + self.body.append( + self.starttag(node, "div", CLASS=("admonition " + name)) + ) + if name: + title = ( + f"

{admonitionlabels[name]}

" + ) + self.body.append(title) + + def is_implicit_heading(self, node): + """Determines if a node is an implicit heading. + + An implicit heading is represented by a paragraph node whose only + child is a strong node with text that isnt in `IGNORE_IMPLICIT_HEADINGS`. + """ + return ( + len(node) == 1 + and isinstance(node[0], nodes.strong) + and len(node[0]) == 1 + and isinstance(node[0][0], nodes.Text) + and node[0][0].astext() not in self.IGNORE_IMPLICIT_HEADINGS + ) + + def visit_paragraph(self, node): + """Visit a paragraph HTML element. + + Replaces implicit headings with an h3 tag and defers to default + behavior for normal paragraph elements. + """ + if self.is_implicit_heading(node): + text = node[0][0] + self.body.append(f'

{text}

\n') + # Do not visit the current nodes children or call its depart method. + raise nodes.SkipNode + else: + super().visit_paragraph(node) diff --git a/venv/lib/python3.12/site-packages/botocore/docs/utils.py b/venv/lib/python3.12/site-packages/botocore/docs/utils.py new file mode 100644 index 0000000..715a819 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/utils.py @@ -0,0 +1,306 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import re +from collections import namedtuple + + +def py_type_name(type_name): + """Get the Python type name for a given model type. + + >>> py_type_name('list') + 'list' + >>> py_type_name('structure') + 'dict' + + :rtype: string + """ + return { + 'blob': 'bytes', + 'character': 'string', + 'double': 'float', + 'long': 'integer', + 'map': 'dict', + 'structure': 'dict', + 'timestamp': 'datetime', + }.get(type_name, type_name) + + +def py_default(type_name): + """Get the Python default value for a given model type. + + >>> py_default('string') + '\'string\'' + >>> py_default('list') + '[...]' + >>> py_default('unknown') + '...' + + :rtype: string + """ + return { + 'double': '123.0', + 'long': '123', + 'integer': '123', + 'string': "'string'", + 'blob': "b'bytes'", + 'boolean': 'True|False', + 'list': '[...]', + 'map': '{...}', + 'structure': '{...}', + 'timestamp': 'datetime(2015, 1, 1)', + }.get(type_name, '...') + + +def get_official_service_name(service_model): + """Generate the official name of an AWS Service + + :param service_model: The service model representing the service + """ + official_name = service_model.metadata.get('serviceFullName') + short_name = service_model.metadata.get('serviceAbbreviation', '') + if short_name.startswith('Amazon'): + short_name = short_name[7:] + if short_name.startswith('AWS'): + short_name = short_name[4:] + if short_name and short_name.lower() not in official_name.lower(): + official_name += f' ({short_name})' + return official_name + + +_DocumentedShape = namedtuple( + 'DocumentedShape', + [ + 'name', + 'type_name', + 'documentation', + 'metadata', + 'members', + 'required_members', + ], +) + + +class DocumentedShape(_DocumentedShape): + """Use this class to inject new shapes into a model for documentation""" + + def __new__( + cls, + name, + type_name, + documentation, + metadata=None, + members=None, + required_members=None, + ): + if metadata is None: + metadata = [] + if members is None: + members = [] + if required_members is None: + required_members = [] + return super().__new__( + cls, + name, + type_name, + documentation, + metadata, + members, + required_members, + ) + + +class AutoPopulatedParam: + def __init__(self, name, param_description=None): + self.name = name + self.param_description = param_description + if param_description is None: + self.param_description = ( + 'Please note that this parameter is automatically populated ' + 'if it is not provided. Including this parameter is not ' + 'required\n' + ) + + def document_auto_populated_param(self, event_name, section, **kwargs): + """Documents auto populated parameters + + It will remove any required marks for the parameter, remove the + parameter from the example, and add a snippet about the parameter + being autopopulated in the description. + """ + if event_name.startswith('docs.request-params'): + if self.name in section.available_sections: + section = section.get_section(self.name) + if 'is-required' in section.available_sections: + section.delete_section('is-required') + description_section = section.get_section( + 'param-documentation' + ) + description_section.writeln(self.param_description) + elif event_name.startswith('docs.request-example'): + section = section.get_section('structure-value') + if self.name in section.available_sections: + section.delete_section(self.name) + + +class HideParamFromOperations: + """Hides a single parameter from multiple operations. + + This method will remove a parameter from documentation and from + examples. This method is typically used for things that are + automatically populated because a user would be unable to provide + a value (e.g., a checksum of a serialized XML request body).""" + + def __init__(self, service_name, parameter_name, operation_names): + """ + :type service_name: str + :param service_name: Name of the service to modify. + + :type parameter_name: str + :param parameter_name: Name of the parameter to modify. + + :type operation_names: list + :param operation_names: Operation names to modify. + """ + self._parameter_name = parameter_name + self._params_events = set() + self._example_events = set() + # Build up the sets of relevant event names. + param_template = 'docs.request-params.%s.%s.complete-section' + example_template = 'docs.request-example.%s.%s.complete-section' + for name in operation_names: + self._params_events.add(param_template % (service_name, name)) + self._example_events.add(example_template % (service_name, name)) + + def hide_param(self, event_name, section, **kwargs): + if event_name in self._example_events: + # Modify the structure value for example events. + section = section.get_section('structure-value') + elif event_name not in self._params_events: + return + if self._parameter_name in section.available_sections: + section.delete_section(self._parameter_name) + + +class AppendParamDocumentation: + """Appends documentation to a specific parameter""" + + def __init__(self, parameter_name, doc_string): + self._parameter_name = parameter_name + self._doc_string = doc_string + + def append_documentation(self, event_name, section, **kwargs): + if self._parameter_name in section.available_sections: + section = section.get_section(self._parameter_name) + description_section = section.get_section('param-documentation') + description_section.writeln(self._doc_string) + + +class DocumentModifiedShape: + """Modifies the documentation for a shape to reflect runtime transformations. + + This is used when a handler transforms a shape's value at runtime + (e.g., decoding a JSON string into a dict) and the documentation + needs to reflect the actual Python type returned rather than the + modeled type. + """ + + def __init__( + self, + shape_name, + new_type, + new_description, + new_example_value, + ): + self._shape_name = shape_name + self._new_type = new_type + self._new_description = new_description + self._new_example_value = new_example_value + + def replace_documentation_for_matching_shape( + self, event_name, section, **kwargs + ): + if self._shape_name == section.context.get('shape'): + self._replace_documentation(event_name, section) + for section_name in section.available_sections: + sub_section = section.get_section(section_name) + if self._shape_name == sub_section.context.get('shape'): + self._replace_documentation(event_name, sub_section) + else: + self.replace_documentation_for_matching_shape( + event_name, sub_section + ) + + def _replace_documentation(self, event_name, section): + if event_name.startswith( + 'docs.request-example' + ) or event_name.startswith('docs.response-example'): + section.remove_all_sections() + section.clear_text() + section.write(self._new_example_value) + + if event_name.startswith( + 'docs.request-params' + ) or event_name.startswith('docs.response-params'): + allowed_sections = ( + 'param-name', + 'param-documentation', + 'end-structure', + 'param-type', + 'end-param', + ) + for section_name in section.available_sections: + # Delete any extra members as a new shape is being + # used. + if section_name not in allowed_sections: + section.delete_section(section_name) + + # Update the documentation. + if ( + self._new_description is not None + and 'param-documentation' in section.available_sections + ): + description_section = section.get_section( + 'param-documentation' + ) + description_section.clear_text() + description_section.write(self._new_description) + + # Update the param type + type_section = section.get_section('param-type') + if type_section.getvalue().decode('utf-8').startswith(':type'): + type_section.clear_text() + type_section.write(f':type {section.name}: {self._new_type}') + else: + type_section.clear_text() + type_section.style.italics(f'({self._new_type}) --') + type_section.write(' ') + + +_CONTROLS = { + '\n': '\\n', + '\r': '\\r', + '\t': '\\t', + '\b': '\\b', + '\f': '\\f', +} +# Combines all CONTROLS keys into a big or regular expression +_ESCAPE_CONTROLS_RE = re.compile('|'.join(map(re.escape, _CONTROLS))) + + +# Based on the match get the appropriate replacement from CONTROLS +def _CONTROLS_MATCH_HANDLER(match): + return _CONTROLS[match.group(0)] + + +def escape_controls(value): + return _ESCAPE_CONTROLS_RE.sub(_CONTROLS_MATCH_HANDLER, value) diff --git a/venv/lib/python3.12/site-packages/botocore/docs/waiter.py b/venv/lib/python3.12/site-packages/botocore/docs/waiter.py new file mode 100644 index 0000000..fcc8ed4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/docs/waiter.py @@ -0,0 +1,180 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import os + +from botocore import xform_name +from botocore.compat import OrderedDict +from botocore.docs.bcdoc.restdoc import DocumentStructure +from botocore.docs.method import document_model_driven_method +from botocore.docs.utils import DocumentedShape +from botocore.utils import get_service_module_name + + +class WaiterDocumenter: + def __init__(self, client, service_waiter_model, root_docs_path): + self._client = client + self._client_class_name = self._client.__class__.__name__ + self._service_name = self._client.meta.service_model.service_name + self._service_waiter_model = service_waiter_model + self._root_docs_path = root_docs_path + self._USER_GUIDE_LINK = ( + 'https://docs.aws.amazon.com/' + 'boto3/latest/guide/clients.html#waiters' + ) + + def document_waiters(self, section): + """Documents the various waiters for a service. + + :param section: The section to write to. + """ + section.style.h2('Waiters') + self._add_overview(section) + section.style.new_line() + section.writeln('The available waiters are:') + section.style.toctree() + for waiter_name in self._service_waiter_model.waiter_names: + section.style.tocitem(f'{self._service_name}/waiter/{waiter_name}') + # Create a new DocumentStructure for each waiter and add contents. + waiter_doc_structure = DocumentStructure( + waiter_name, target='html' + ) + self._add_single_waiter(waiter_doc_structure, waiter_name) + # Write waiters in individual/nested files. + # Path: /reference/services//waiter/.rst + waiter_dir_path = os.path.join( + self._root_docs_path, self._service_name, 'waiter' + ) + waiter_doc_structure.write_to_file(waiter_dir_path, waiter_name) + + def _add_single_waiter(self, section, waiter_name): + breadcrumb_section = section.add_new_section('breadcrumb') + breadcrumb_section.style.ref( + self._client_class_name, f'../../{self._service_name}' + ) + breadcrumb_section.write(f' / Waiter / {waiter_name}') + section.add_title_section(waiter_name) + waiter_section = section.add_new_section(waiter_name) + waiter_section.style.start_sphinx_py_class( + class_name=f"{self._client_class_name}.Waiter.{waiter_name}" + ) + + # Add example on how to instantiate waiter. + waiter_section.style.start_codeblock() + waiter_section.style.new_line() + waiter_section.write( + f'waiter = client.get_waiter(\'{xform_name(waiter_name)}\')' + ) + waiter_section.style.end_codeblock() + + # Add information on the wait() method + waiter_section.style.new_line() + document_wait_method( + section=waiter_section, + waiter_name=waiter_name, + event_emitter=self._client.meta.events, + service_model=self._client.meta.service_model, + service_waiter_model=self._service_waiter_model, + ) + + def _add_overview(self, section): + section.style.new_line() + section.write( + 'Waiters are available on a client instance ' + 'via the ``get_waiter`` method. For more detailed instructions ' + 'and examples on the usage or waiters, see the ' + 'waiters ' + ) + section.style.external_link( + title='user guide', + link=self._USER_GUIDE_LINK, + ) + section.write('.') + section.style.new_line() + + +def document_wait_method( + section, + waiter_name, + event_emitter, + service_model, + service_waiter_model, + include_signature=True, +): + """Documents a the wait method of a waiter + + :param section: The section to write to + + :param waiter_name: The name of the waiter + + :param event_emitter: The event emitter to use to emit events + + :param service_model: The service model + + :param service_waiter_model: The waiter model associated to the service + + :param include_signature: Whether or not to include the signature. + It is useful for generating docstrings. + """ + waiter_model = service_waiter_model.get_waiter(waiter_name) + operation_model = service_model.operation_model(waiter_model.operation) + + waiter_config_members = OrderedDict() + + waiter_config_members['Delay'] = DocumentedShape( + name='Delay', + type_name='integer', + documentation=( + '

The amount of time in seconds to wait between ' + f'attempts. Default: {waiter_model.delay}

' + ), + ) + + waiter_config_members['MaxAttempts'] = DocumentedShape( + name='MaxAttempts', + type_name='integer', + documentation=( + '

The maximum number of attempts to be made. ' + f'Default: {waiter_model.max_attempts}

' + ), + ) + + botocore_waiter_params = [ + DocumentedShape( + name='WaiterConfig', + type_name='structure', + documentation=( + '

A dictionary that provides parameters to control ' + 'waiting behavior.

' + ), + members=waiter_config_members, + ) + ] + + wait_description = ( + f'Polls :py:meth:`{get_service_module_name(service_model)}.Client.' + f'{xform_name(waiter_model.operation)}` every {waiter_model.delay} ' + 'seconds until a successful state is reached. An error is ' + f'raised after {waiter_model.max_attempts} failed checks.' + ) + + document_model_driven_method( + section, + 'wait', + operation_model, + event_emitter=event_emitter, + method_description=wait_description, + example_prefix='waiter.wait', + include_input=botocore_waiter_params, + document_output=False, + include_signature=include_signature, + ) diff --git a/venv/lib/python3.12/site-packages/botocore/endpoint.py b/venv/lib/python3.12/site-packages/botocore/endpoint.py new file mode 100644 index 0000000..60359c3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/endpoint.py @@ -0,0 +1,465 @@ +# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import datetime +import logging +import os +import threading +import time +import uuid + +from botocore import parsers +from botocore.awsrequest import create_request_object +from botocore.compat import get_current_datetime +from botocore.exceptions import HTTPClientError, InvalidConfigError +from botocore.history import get_global_history_recorder +from botocore.hooks import first_non_none_response +from botocore.httpchecksum import handle_checksum_body +from botocore.httpsession import URLLib3Session +from botocore.response import StreamingBody +from botocore.utils import ( + get_environ_proxies, + is_valid_endpoint_url, + is_valid_ipv6_endpoint_url, +) + +logger = logging.getLogger(__name__) +history_recorder = get_global_history_recorder() +DEFAULT_TIMEOUT = 60 +MAX_POOL_CONNECTIONS = 10 + + +def convert_to_response_dict(http_response, operation_model): + """Convert an HTTP response object to a request dict. + + This converts the HTTP response object to a dictionary. + + :type http_response: botocore.awsrequest.AWSResponse + :param http_response: The HTTP response from an AWS service request. + + :rtype: dict + :return: A response dictionary which will contain the following keys: + * headers (dict) + * status_code (int) + * body (string or file-like object) + + """ + response_dict = { + 'headers': http_response.headers, + 'status_code': http_response.status_code, + 'context': { + 'operation_name': operation_model.name, + }, + } + if response_dict['status_code'] >= 300: + response_dict['body'] = http_response.content + elif operation_model.has_event_stream_output: + response_dict['body'] = http_response.raw + elif operation_model.has_streaming_output: + length = response_dict['headers'].get('content-length') + response_dict['body'] = StreamingBody(http_response.raw, length) + else: + response_dict['body'] = http_response.content + return response_dict + + +class Endpoint: + """ + Represents an endpoint for a particular service in a specific + region. Only an endpoint can make requests. + + :ivar service: The Service object that describes this endpoints + service. + :ivar host: The fully qualified endpoint hostname. + :ivar session: The session object. + """ + + def __init__( + self, + host, + endpoint_prefix, + event_emitter, + response_parser_factory=None, + http_session=None, + ): + self._endpoint_prefix = endpoint_prefix + self._event_emitter = event_emitter + self.host = host + self._lock = threading.Lock() + if response_parser_factory is None: + response_parser_factory = parsers.ResponseParserFactory() + self._response_parser_factory = response_parser_factory + self.http_session = http_session + if self.http_session is None: + self.http_session = URLLib3Session() + + def __repr__(self): + return f'{self._endpoint_prefix}({self.host})' + + def close(self): + self.http_session.close() + + def make_request(self, operation_model, request_dict): + logger.debug( + "Making request for %s with params: %s", + operation_model, + request_dict, + ) + return self._send_request(request_dict, operation_model) + + def create_request(self, params, operation_model=None): + request = create_request_object(params) + if operation_model: + request.stream_output = any( + [ + operation_model.has_streaming_output, + operation_model.has_event_stream_output, + ] + ) + service_id = operation_model.service_model.service_id.hyphenize() + event_name = f'request-created.{service_id}.{operation_model.name}' + self._event_emitter.emit( + event_name, + request=request, + operation_name=operation_model.name, + ) + prepared_request = self.prepare_request(request) + return prepared_request + + def _encode_headers(self, headers): + # In place encoding of headers to utf-8 if they are unicode. + for key, value in headers.items(): + if isinstance(value, str): + headers[key] = value.encode('utf-8') + + def prepare_request(self, request): + self._encode_headers(request.headers) + return request.prepare() + + def _calculate_ttl( + self, response_received_timestamp, date_header, read_timeout + ): + local_timestamp = get_current_datetime() + date_conversion = datetime.datetime.strptime( + date_header, "%a, %d %b %Y %H:%M:%S %Z" + ) + estimated_skew = date_conversion - response_received_timestamp + ttl = ( + local_timestamp + + datetime.timedelta(seconds=read_timeout) + + estimated_skew + ) + return ttl.strftime('%Y%m%dT%H%M%SZ') + + def _set_ttl(self, retries_context, read_timeout, success_response): + response_date_header = success_response[0].headers.get('Date') + has_streaming_input = retries_context.get('has_streaming_input') + if response_date_header and not has_streaming_input: + try: + response_received_timestamp = get_current_datetime() + retries_context['ttl'] = self._calculate_ttl( + response_received_timestamp, + response_date_header, + read_timeout, + ) + except Exception: + logger.debug( + "Exception received when updating retries context with TTL", + exc_info=True, + ) + + def _update_retries_context(self, context, attempt, success_response=None): + retries_context = context.setdefault('retries', {}) + retries_context['attempt'] = attempt + if 'invocation-id' not in retries_context: + retries_context['invocation-id'] = str(uuid.uuid4()) + + if success_response: + read_timeout = context.get('read_timeout') + if read_timeout is None: + read_timeout = context['client_config'].read_timeout + self._set_ttl(retries_context, read_timeout, success_response) + + def _send_request(self, request_dict, operation_model): + attempts = 1 + context = request_dict['context'] + self._update_retries_context(context, attempts) + request = self.create_request(request_dict, operation_model) + success_response, exception = self._get_response( + request, operation_model, context + ) + while self._needs_retry( + attempts, + operation_model, + request_dict, + success_response, + exception, + ): + attempts += 1 + self._update_retries_context(context, attempts, success_response) + # If there is a stream associated with the request, we need + # to reset it before attempting to send the request again. + # This will ensure that we resend the entire contents of the + # body. + request.reset_stream() + # Create a new request when retried (including a new signature). + request = self.create_request(request_dict, operation_model) + success_response, exception = self._get_response( + request, operation_model, context + ) + if ( + success_response is not None + and 'ResponseMetadata' in success_response[1] + ): + # We want to share num retries, not num attempts. + total_retries = attempts - 1 + success_response[1]['ResponseMetadata']['RetryAttempts'] = ( + total_retries + ) + if exception is not None: + raise exception + else: + return success_response + + def _get_response(self, request, operation_model, context): + # This will return a tuple of (success_response, exception) + # and success_response is itself a tuple of + # (http_response, parsed_dict). + # If an exception occurs then the success_response is None. + # If no exception occurs then exception is None. + success_response, exception = self._do_get_response( + request, operation_model, context + ) + kwargs_to_emit = { + 'response_dict': None, + 'parsed_response': None, + 'context': context, + 'exception': exception, + } + if success_response is not None: + http_response, parsed_response = success_response + kwargs_to_emit['parsed_response'] = parsed_response + kwargs_to_emit['response_dict'] = convert_to_response_dict( + http_response, operation_model + ) + service_id = operation_model.service_model.service_id.hyphenize() + self._event_emitter.emit( + f"response-received.{service_id}.{operation_model.name}", + **kwargs_to_emit, + ) + return success_response, exception + + def _do_get_response(self, request, operation_model, context): + try: + logger.debug("Sending http request: %s", request) + history_recorder.record( + 'HTTP_REQUEST', + { + 'method': request.method, + 'headers': request.headers, + 'streaming': operation_model.has_streaming_input, + 'url': request.url, + 'body': request.body, + }, + ) + service_id = operation_model.service_model.service_id.hyphenize() + event_name = f"before-send.{service_id}.{operation_model.name}" + responses = self._event_emitter.emit(event_name, request=request) + http_response = first_non_none_response(responses) + if http_response is None: + http_response = self._send(request) + except HTTPClientError as e: + return (None, e) + except Exception as e: + logger.debug( + "Exception received when sending HTTP request.", exc_info=True + ) + return (None, e) + # This returns the http_response and the parsed_data. + response_dict = convert_to_response_dict( + http_response, operation_model + ) + handle_checksum_body( + http_response, + response_dict, + context, + operation_model, + ) + + http_response_record_dict = response_dict.copy() + http_response_record_dict['streaming'] = ( + operation_model.has_streaming_output + ) + history_recorder.record('HTTP_RESPONSE', http_response_record_dict) + + protocol = operation_model.service_model.resolved_protocol + customized_response_dict = {} + self._event_emitter.emit( + f"before-parse.{service_id}.{operation_model.name}", + operation_model=operation_model, + response_dict=response_dict, + customized_response_dict=customized_response_dict, + ) + parser = self._response_parser_factory.create_parser(protocol) + parsed_response = parser.parse( + response_dict, operation_model.output_shape + ) + parsed_response.update(customized_response_dict) + # Do a second parsing pass to pick up on any modeled error fields + # NOTE: Ideally, we would push this down into the parser classes but + # they currently have no reference to the operation or service model + # The parsers should probably take the operation model instead of + # output shape but we can't change that now + if http_response.status_code >= 300: + self._add_modeled_error_fields( + response_dict, + parsed_response, + operation_model, + parser, + ) + history_recorder.record('PARSED_RESPONSE', parsed_response) + return (http_response, parsed_response), None + + def _add_modeled_error_fields( + self, + response_dict, + parsed_response, + operation_model, + parser, + ): + error_code = parsed_response.get("Error", {}).get("Code") + if error_code is None: + return + service_model = operation_model.service_model + error_shape = service_model.shape_for_error_code(error_code) + if error_shape is None: + return + modeled_parse = parser.parse(response_dict, error_shape) + # TODO: avoid naming conflicts with ResponseMetadata and Error + parsed_response.update(modeled_parse) + + def _needs_retry( + self, + attempts, + operation_model, + request_dict, + response=None, + caught_exception=None, + ): + service_id = operation_model.service_model.service_id.hyphenize() + event_name = f"needs-retry.{service_id}.{operation_model.name}" + responses = self._event_emitter.emit( + event_name, + response=response, + endpoint=self, + operation=operation_model, + attempts=attempts, + caught_exception=caught_exception, + request_dict=request_dict, + ) + handler_response = first_non_none_response(responses) + if handler_response is None or handler_response is False: + return False + else: + # Request needs to be retried, and we need to sleep + # for the specified number of times. + logger.debug( + "Response received to retry, sleeping for %s seconds", + handler_response, + ) + time.sleep(handler_response) + return True + + def _send(self, request): + return self.http_session.send(request) + + +class EndpointCreator: + def __init__(self, event_emitter): + self._event_emitter = event_emitter + + def create_endpoint( + self, + service_model, + region_name, + endpoint_url, + verify=None, + response_parser_factory=None, + timeout=DEFAULT_TIMEOUT, + max_pool_connections=MAX_POOL_CONNECTIONS, + http_session_cls=URLLib3Session, + proxies=None, + socket_options=None, + client_cert=None, + proxies_config=None, + ): + if not is_valid_endpoint_url( + endpoint_url + ) and not is_valid_ipv6_endpoint_url(endpoint_url): + raise ValueError(f"Invalid endpoint: {endpoint_url}") + + if proxies is None: + proxies = self._get_proxies(endpoint_url) + endpoint_prefix = service_model.endpoint_prefix + + logger.debug('Setting %s timeout as %s', endpoint_prefix, timeout) + http_session = http_session_cls( + timeout=timeout, + proxies=proxies, + verify=self._get_verify_value(verify), + max_pool_connections=max_pool_connections, + socket_options=socket_options, + client_cert=client_cert, + proxies_config=proxies_config, + ) + + return Endpoint( + endpoint_url, + endpoint_prefix=endpoint_prefix, + event_emitter=self._event_emitter, + response_parser_factory=response_parser_factory, + http_session=http_session, + ) + + def _get_proxies(self, url): + # We could also support getting proxies from a config file, + # but for now proxy support is taken from the environment. + return get_environ_proxies(url) + + def _get_verify_value(self, verify): + # This is to account for: + # https://github.com/kennethreitz/requests/issues/1436 + # where we need to honor REQUESTS_CA_BUNDLE because we're creating our + # own request objects. + # First, if verify is not None, then the user explicitly specified + # a value so this automatically wins. + if verify is not None: + return self._validate_verify_value(verify) + # Otherwise use the value from REQUESTS_CA_BUNDLE, or default to + # True if the env var does not exist. + return self._validate_verify_value( + os.environ.get('REQUESTS_CA_BUNDLE', True) + ) + + def _validate_verify_value(self, verify): + if isinstance(verify, str) and not verify.strip(): + raise InvalidConfigError( + error_msg=( + 'Invalid CA bundle: the configured value (ca_bundle, ' + 'AWS_CA_BUNDLE, REQUESTS_CA_BUNDLE, or verify) resolved ' + 'to an empty or whitespace-only string. Provide a valid ' + 'path to a CA bundle file.' + ) + ) + return verify diff --git a/venv/lib/python3.12/site-packages/botocore/endpoint_provider.py b/venv/lib/python3.12/site-packages/botocore/endpoint_provider.py new file mode 100644 index 0000000..69f9a11 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/endpoint_provider.py @@ -0,0 +1,733 @@ +# Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +""" +NOTE: All classes and functions in this module are considered private and are +subject to abrupt breaking changes. Please do not use them directly. + +To view the raw JSON that the objects in this module represent, please +go to any `endpoint-rule-set.json` file in /botocore/data/// +or you can look at the test files in /tests/unit/data/endpoints/valid-rules/ +""" + +import logging +import re +from enum import Enum +from string import Formatter +from typing import NamedTuple + +from botocore import xform_name +from botocore.compat import IPV4_RE, quote, urlparse +from botocore.exceptions import EndpointResolutionError +from botocore.utils import ( + ArnParser, + InvalidArnException, + is_valid_ipv4_endpoint_url, + is_valid_ipv6_endpoint_url, + lru_cache_weakref, + normalize_url_path, + percent_encode, +) + +logger = logging.getLogger(__name__) + +TEMPLATE_STRING_RE = re.compile(r"\{[a-zA-Z#]+\}") +GET_ATTR_RE = re.compile(r"(\w*)\[(\d+)\]") +VALID_HOST_LABEL_RE = re.compile( + r"^(?!-)[a-zA-Z\d-]{1,63}(?= len(value): + return None + return value[index] + else: + value = value[part] + return value + + def format_partition_output(self, partition): + output = partition["outputs"] + output["name"] = partition["id"] + return output + + def is_partition_match(self, region, partition): + matches_regex = re.match(partition["regionRegex"], region) is not None + return region in partition["regions"] or matches_regex + + def aws_partition(self, value): + """Match a region string to an AWS partition. + + :type value: str + :rtype: dict + """ + partitions = self.partitions_data['partitions'] + + if value is not None: + for partition in partitions: + if self.is_partition_match(value, partition): + return self.format_partition_output(partition) + + # return the default partition if no matches were found + aws_partition = partitions[0] + return self.format_partition_output(aws_partition) + + def aws_parse_arn(self, value): + """Parse and validate string for ARN components. + + :type value: str + :rtype: dict + """ + if value is None or not value.startswith("arn:"): + return None + + try: + arn_dict = ARN_PARSER.parse_arn(value) + except InvalidArnException: + return None + + # partition, resource, and service are required + if not all( + (arn_dict["partition"], arn_dict["service"], arn_dict["resource"]) + ): + return None + + arn_dict["accountId"] = arn_dict.pop("account") + + resource = arn_dict.pop("resource") + arn_dict["resourceId"] = resource.replace(":", "/").split("/") + + return arn_dict + + def is_valid_host_label(self, value, allow_subdomains): + """Evaluates whether a value is a valid host label per + RFC 1123. If allow_subdomains is True, split on `.` and validate + each component separately. + + :type value: str + :type allow_subdomains: bool + :rtype: bool + """ + if value is None or allow_subdomains is False and value.count(".") > 0: + return False + + if allow_subdomains is True: + return all( + self.is_valid_host_label(label, False) + for label in value.split(".") + ) + + return VALID_HOST_LABEL_RE.match(value) is not None + + def string_equals(self, value1, value2): + """Evaluates two string values for equality. + + :type value1: str + :type value2: str + :rtype: bool + """ + if not all(isinstance(val, str) for val in (value1, value2)): + msg = f"Both values must be strings, not {type(value1)} and {type(value2)}." + raise EndpointResolutionError(msg=msg) + return value1 == value2 + + def uri_encode(self, value): + """Perform percent-encoding on an input string. + + :type value: str + :rytpe: str + """ + if value is None: + return None + + return percent_encode(value) + + def parse_url(self, value): + """Parse a URL string into components. + + :type value: str + :rtype: dict + """ + if value is None: + return None + + url_components = urlparse(value) + try: + # url_parse may assign non-integer values to + # `port` and will fail when accessed. + url_components.port + except ValueError: + return None + + scheme = url_components.scheme + query = url_components.query + # URLs with queries are not supported + if scheme not in ("https", "http") or len(query) > 0: + return None + + path = url_components.path + normalized_path = quote(normalize_url_path(path)) + if not normalized_path.endswith("/"): + normalized_path = f"{normalized_path}/" + + return { + "scheme": scheme, + "authority": url_components.netloc, + "path": path, + "normalizedPath": normalized_path, + "isIp": is_valid_ipv4_endpoint_url(value) + or is_valid_ipv6_endpoint_url(value), + } + + def boolean_equals(self, value1, value2): + """Evaluates two boolean values for equality. + + :type value1: bool + :type value2: bool + :rtype: bool + """ + if not all(isinstance(val, bool) for val in (value1, value2)): + msg = f"Both arguments must be bools, not {type(value1)} and {type(value2)}." + raise EndpointResolutionError(msg=msg) + return value1 is value2 + + def is_ascii(self, value): + """Evaluates if a string only contains ASCII characters. + + :type value: str + :rtype: bool + """ + try: + value.encode("ascii") + return True + except UnicodeEncodeError: + return False + + def substring(self, value, start, stop, reverse): + """Computes a substring given the start index and end index. If `reverse` is + True, slice the string from the end instead. + + :type value: str + :type start: int + :type end: int + :type reverse: bool + :rtype: str + """ + if not isinstance(value, str): + msg = f"Input must be a string, not {type(value)}." + raise EndpointResolutionError(msg=msg) + if start >= stop or len(value) < stop or not self.is_ascii(value): + return None + + if reverse is True: + r_start = len(value) - stop + r_stop = len(value) - start + return value[r_start:r_stop] + + return value[start:stop] + + def _not(self, value): + """A function implementation of the logical operator `not`. + + :type value: Any + :rtype: bool + """ + return not value + + def aws_is_virtual_hostable_s3_bucket(self, value, allow_subdomains): + """Evaluates whether a value is a valid bucket name for virtual host + style bucket URLs. To pass, the value must meet the following criteria: + 1. is_valid_host_label(value) is True + 2. length between 3 and 63 characters (inclusive) + 3. does not contain uppercase characters + 4. is not formatted as an IP address + + If allow_subdomains is True, split on `.` and validate + each component separately. + + :type value: str + :type allow_subdomains: bool + :rtype: bool + """ + if ( + value is None + or len(value) < 3 + or value.lower() != value + or IPV4_RE.match(value) is not None + ): + return False + + return self.is_valid_host_label( + value, allow_subdomains=allow_subdomains + ) + + +# maintains backwards compatibility as `Library` was misspelled +# in earlier versions +RuleSetStandardLibary = RuleSetStandardLibrary + + +class BaseRule: + """Base interface for individual endpoint rules.""" + + def __init__(self, conditions, documentation=None): + self.conditions = conditions + self.documentation = documentation + + def evaluate(self, scope_vars, rule_lib): + raise NotImplementedError() + + def evaluate_conditions(self, scope_vars, rule_lib): + """Determine if all conditions in a rule are met. + + :type scope_vars: dict + :type rule_lib: RuleSetStandardLibrary + :rtype: bool + """ + for func_signature in self.conditions: + result = rule_lib.call_function(func_signature, scope_vars) + if result is False or result is None: + return False + return True + + +class RuleSetEndpoint(NamedTuple): + """A resolved endpoint object returned by a rule.""" + + url: str + properties: dict + headers: dict + + +class EndpointRule(BaseRule): + def __init__(self, endpoint, **kwargs): + super().__init__(**kwargs) + self.endpoint = endpoint + + def evaluate(self, scope_vars, rule_lib): + """Determine if conditions are met to provide a valid endpoint. + + :type scope_vars: dict + :rtype: RuleSetEndpoint + """ + if self.evaluate_conditions(scope_vars, rule_lib): + url = rule_lib.resolve_value(self.endpoint["url"], scope_vars) + properties = self.resolve_properties( + self.endpoint.get("properties", {}), + scope_vars, + rule_lib, + ) + headers = self.resolve_headers(scope_vars, rule_lib) + return RuleSetEndpoint( + url=url, properties=properties, headers=headers + ) + + return None + + def resolve_properties(self, properties, scope_vars, rule_lib): + """Traverse `properties` attribute, resolving any template strings. + + :type properties: dict/list/str + :type scope_vars: dict + :type rule_lib: RuleSetStandardLibrary + :rtype: dict + """ + if isinstance(properties, list): + return [ + self.resolve_properties(prop, scope_vars, rule_lib) + for prop in properties + ] + elif isinstance(properties, dict): + return { + key: self.resolve_properties(value, scope_vars, rule_lib) + for key, value in properties.items() + } + elif rule_lib.is_template(properties): + return rule_lib.resolve_template_string(properties, scope_vars) + + return properties + + def resolve_headers(self, scope_vars, rule_lib): + """Iterate through headers attribute resolving all values. + + :type scope_vars: dict + :type rule_lib: RuleSetStandardLibrary + :rtype: dict + """ + resolved_headers = {} + headers = self.endpoint.get("headers", {}) + + for header, values in headers.items(): + resolved_headers[header] = [ + rule_lib.resolve_value(item, scope_vars) for item in values + ] + return resolved_headers + + +class ErrorRule(BaseRule): + def __init__(self, error, **kwargs): + super().__init__(**kwargs) + self.error = error + + def evaluate(self, scope_vars, rule_lib): + """If an error rule's conditions are met, raise an error rule. + + :type scope_vars: dict + :type rule_lib: RuleSetStandardLibrary + :rtype: EndpointResolutionError + """ + if self.evaluate_conditions(scope_vars, rule_lib): + error = rule_lib.resolve_value(self.error, scope_vars) + raise EndpointResolutionError(msg=error) + return None + + +class TreeRule(BaseRule): + """A tree rule is non-terminal meaning it will never be returned to a provider. + Additionally this means it has no attributes that need to be resolved. + """ + + def __init__(self, rules, **kwargs): + super().__init__(**kwargs) + self.rules = [RuleCreator.create(**rule) for rule in rules] + + def evaluate(self, scope_vars, rule_lib): + """If a tree rule's conditions are met, iterate its sub-rules + and return first result found. + + :type scope_vars: dict + :type rule_lib: RuleSetStandardLibrary + :rtype: RuleSetEndpoint/EndpointResolutionError + """ + if self.evaluate_conditions(scope_vars, rule_lib): + for rule in self.rules: + # don't share scope_vars between rules + rule_result = rule.evaluate(scope_vars.copy(), rule_lib) + if rule_result: + return rule_result + return None + + +class RuleCreator: + endpoint = EndpointRule + error = ErrorRule + tree = TreeRule + + @classmethod + def create(cls, **kwargs): + """Create a rule instance from metadata. + + :rtype: TreeRule/EndpointRule/ErrorRule + """ + rule_type = kwargs.pop("type") + try: + rule_class = getattr(cls, rule_type) + except AttributeError: + raise EndpointResolutionError( + msg=f"Unknown rule type: {rule_type}. A rule must " + "be of type tree, endpoint or error." + ) + else: + return rule_class(**kwargs) + + +class ParameterType(Enum): + """Translation from `type` attribute to native Python type.""" + + string = str + boolean = bool + stringarray = tuple + + +class ParameterDefinition: + """The spec of an individual parameter defined in a RuleSet.""" + + def __init__( + self, + name, + parameter_type, + documentation=None, + builtIn=None, + default=None, + required=None, + deprecated=None, + ): + self.name = name + try: + self.parameter_type = getattr( + ParameterType, parameter_type.lower() + ).value + except AttributeError: + raise EndpointResolutionError( + msg=f"Unknown parameter type: {parameter_type}. " + "A parameter must be of type string, boolean, or stringarray." + ) + self.documentation = documentation + self.builtin = builtIn + self.default = default + self.required = required + self.deprecated = deprecated + + def validate_input(self, value): + """Perform base validation on parameter input. + + :type value: Any + :raises: EndpointParametersError + """ + + if not isinstance(value, self.parameter_type): + raise EndpointResolutionError( + msg=f"Value ({self.name}) is the wrong " + f"type. Must be {self.parameter_type}." + ) + if self.deprecated is not None: + depr_str = f"{self.name} has been deprecated." + msg = self.deprecated.get("message") + since = self.deprecated.get("since") + if msg: + depr_str += f"\n{msg}" + if since: + depr_str += f"\nDeprecated since {since}." + logger.info(depr_str) + + return None + + def process_input(self, value): + """Process input against spec, applying default if value is None.""" + if value is None: + if self.default is not None: + return self.default + if self.required: + raise EndpointResolutionError( + msg=f"Cannot find value for required parameter {self.name}" + ) + # in all other cases, the parameter will keep the value None + else: + self.validate_input(value) + return value + + +class RuleSet: + """Collection of rules to derive a routable service endpoint.""" + + def __init__( + self, version, parameters, rules, partitions, documentation=None + ): + self.version = version + self.parameters = self._ingest_parameter_spec(parameters) + self.rules = [RuleCreator.create(**rule) for rule in rules] + self.rule_lib = RuleSetStandardLibrary(partitions) + self.documentation = documentation + + def _ingest_parameter_spec(self, parameters): + return { + name: ParameterDefinition( + name, + spec["type"], + spec.get("documentation"), + spec.get("builtIn"), + spec.get("default"), + spec.get("required"), + spec.get("deprecated"), + ) + for name, spec in parameters.items() + } + + def process_input_parameters(self, input_params): + """Process each input parameter against its spec. + + :type input_params: dict + """ + for name, spec in self.parameters.items(): + value = spec.process_input(input_params.get(name)) + if value is not None: + input_params[name] = value + return None + + def evaluate(self, input_parameters): + """Evaluate input parameters against rules returning first match. + + :type input_parameters: dict + """ + self.process_input_parameters(input_parameters) + for rule in self.rules: + evaluation = rule.evaluate(input_parameters.copy(), self.rule_lib) + if evaluation is not None: + return evaluation + return None + + +class EndpointProvider: + """Derives endpoints from a RuleSet for given input parameters.""" + + def __init__(self, ruleset_data, partition_data, excluded_params=None): + self.ruleset = RuleSet(**ruleset_data, partitions=partition_data) + self._excluded_params = excluded_params or frozenset() + + def resolve_endpoint(self, **input_parameters): + for param in self._excluded_params: + input_parameters.pop(param, None) + return self._resolve_endpoint(**input_parameters) + + @lru_cache_weakref(maxsize=CACHE_SIZE) + def _resolve_endpoint(self, **input_parameters): + """Match input parameters to a rule. + + :type input_parameters: dict + :rtype: RuleSetEndpoint + """ + params_for_error = input_parameters.copy() + endpoint = self.ruleset.evaluate(input_parameters) + if endpoint is None: + param_string = "\n".join( + [f"{key}: {value}" for key, value in params_for_error.items()] + ) + raise EndpointResolutionError( + msg=f"No endpoint found for parameters:\n{param_string}" + ) + return endpoint diff --git a/venv/lib/python3.12/site-packages/botocore/errorfactory.py b/venv/lib/python3.12/site-packages/botocore/errorfactory.py new file mode 100644 index 0000000..6084e51 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/errorfactory.py @@ -0,0 +1,90 @@ +# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from botocore.exceptions import ClientError +from botocore.utils import get_service_module_name + + +class BaseClientExceptions: + ClientError = ClientError + + def __init__(self, code_to_exception): + """Base class for exceptions object on a client + + :type code_to_exception: dict + :param code_to_exception: Mapping of error codes (strings) to exception + class that should be raised when encountering a particular + error code. + """ + self._code_to_exception = code_to_exception + + def from_code(self, error_code): + """Retrieves the error class based on the error code + + This is helpful for identifying the exception class needing to be + caught based on the ClientError.parsed_reponse['Error']['Code'] value + + :type error_code: string + :param error_code: The error code associated to a ClientError exception + + :rtype: ClientError or a subclass of ClientError + :returns: The appropriate modeled exception class for that error + code. If the error code does not match any of the known + modeled exceptions then return a generic ClientError. + """ + return self._code_to_exception.get(error_code, self.ClientError) + + def __getattr__(self, name): + exception_cls_names = [ + exception_cls.__name__ + for exception_cls in self._code_to_exception.values() + ] + raise AttributeError( + rf"{self} object has no attribute {name}. " + rf"Valid exceptions are: {', '.join(exception_cls_names)}" + ) + + +class ClientExceptionsFactory: + def __init__(self): + self._client_exceptions_cache = {} + + def create_client_exceptions(self, service_model): + """Creates a ClientExceptions object for the particular service client + + :type service_model: botocore.model.ServiceModel + :param service_model: The service model for the client + + :rtype: object that subclasses from BaseClientExceptions + :returns: The exceptions object of a client that can be used + to grab the various different modeled exceptions. + """ + service_name = service_model.service_name + if service_name not in self._client_exceptions_cache: + client_exceptions = self._create_client_exceptions(service_model) + self._client_exceptions_cache[service_name] = client_exceptions + return self._client_exceptions_cache[service_name] + + def _create_client_exceptions(self, service_model): + cls_props = {} + code_to_exception = {} + for error_shape in service_model.error_shapes: + exception_name = str(error_shape.name) + exception_cls = type(exception_name, (ClientError,), {}) + cls_props[exception_name] = exception_cls + code = str(error_shape.error_code) + code_to_exception[code] = exception_cls + cls_name = str(get_service_module_name(service_model) + 'Exceptions') + client_exceptions_cls = type( + cls_name, (BaseClientExceptions,), cls_props + ) + return client_exceptions_cls(code_to_exception) diff --git a/venv/lib/python3.12/site-packages/botocore/eventstream.py b/venv/lib/python3.12/site-packages/botocore/eventstream.py new file mode 100644 index 0000000..865e65b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/eventstream.py @@ -0,0 +1,622 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Binary Event Stream Decoding""" + +from binascii import crc32 +from struct import unpack + +from botocore.exceptions import EventStreamError + +# byte length of the prelude (total_length + header_length + prelude_crc) +_PRELUDE_LENGTH = 12 +_MAX_HEADERS_LENGTH = 128 * 1024 # 128 Kb +_MAX_PAYLOAD_LENGTH = 24 * 1024 * 1024 # 24 Mb + + +class ParserError(Exception): + """Base binary flow encoding parsing exception.""" + + pass + + +class DuplicateHeader(ParserError): + """Duplicate header found in the event.""" + + def __init__(self, header): + message = f'Duplicate header present: "{header}"' + super().__init__(message) + + +class InvalidHeadersLength(ParserError): + """Headers length is longer than the maximum.""" + + def __init__(self, length): + message = f'Header length of {length} exceeded the maximum of {_MAX_HEADERS_LENGTH}' + super().__init__(message) + + +class InvalidPayloadLength(ParserError): + """Payload length is longer than the maximum.""" + + def __init__(self, length): + message = f'Payload length of {length} exceeded the maximum of {_MAX_PAYLOAD_LENGTH}' + super().__init__(message) + + +class ChecksumMismatch(ParserError): + """Calculated checksum did not match the expected checksum.""" + + def __init__(self, expected, calculated): + message = f'Checksum mismatch: expected 0x{expected:08x}, calculated 0x{calculated:08x}' + super().__init__(message) + + +class NoInitialResponseError(ParserError): + """An event of type initial-response was not received. + + This exception is raised when the event stream produced no events or + the first event in the stream was not of the initial-response type. + """ + + def __init__(self): + message = 'First event was not of the initial-response type' + super().__init__(message) + + +class DecodeUtils: + """Unpacking utility functions used in the decoder. + + All methods on this class take raw bytes and return a tuple containing + the value parsed from the bytes and the number of bytes consumed to parse + that value. + """ + + UINT8_BYTE_FORMAT = '!B' + UINT16_BYTE_FORMAT = '!H' + UINT32_BYTE_FORMAT = '!I' + INT8_BYTE_FORMAT = '!b' + INT16_BYTE_FORMAT = '!h' + INT32_BYTE_FORMAT = '!i' + INT64_BYTE_FORMAT = '!q' + PRELUDE_BYTE_FORMAT = '!III' + + # uint byte size to unpack format + UINT_BYTE_FORMAT = { + 1: UINT8_BYTE_FORMAT, + 2: UINT16_BYTE_FORMAT, + 4: UINT32_BYTE_FORMAT, + } + + @staticmethod + def unpack_true(data): + """This method consumes none of the provided bytes and returns True. + + :type data: bytes + :param data: The bytes to parse from. This is ignored in this method. + + :rtype: tuple + :rtype: (bool, int) + :returns: The tuple (True, 0) + """ + return True, 0 + + @staticmethod + def unpack_false(data): + """This method consumes none of the provided bytes and returns False. + + :type data: bytes + :param data: The bytes to parse from. This is ignored in this method. + + :rtype: tuple + :rtype: (bool, int) + :returns: The tuple (False, 0) + """ + return False, 0 + + @staticmethod + def unpack_uint8(data): + """Parse an unsigned 8-bit integer from the bytes. + + :type data: bytes + :param data: The bytes to parse from. + + :rtype: (int, int) + :returns: A tuple containing the (parsed integer value, bytes consumed) + """ + value = unpack(DecodeUtils.UINT8_BYTE_FORMAT, data[:1])[0] + return value, 1 + + @staticmethod + def unpack_uint32(data): + """Parse an unsigned 32-bit integer from the bytes. + + :type data: bytes + :param data: The bytes to parse from. + + :rtype: (int, int) + :returns: A tuple containing the (parsed integer value, bytes consumed) + """ + value = unpack(DecodeUtils.UINT32_BYTE_FORMAT, data[:4])[0] + return value, 4 + + @staticmethod + def unpack_int8(data): + """Parse a signed 8-bit integer from the bytes. + + :type data: bytes + :param data: The bytes to parse from. + + :rtype: (int, int) + :returns: A tuple containing the (parsed integer value, bytes consumed) + """ + value = unpack(DecodeUtils.INT8_BYTE_FORMAT, data[:1])[0] + return value, 1 + + @staticmethod + def unpack_int16(data): + """Parse a signed 16-bit integer from the bytes. + + :type data: bytes + :param data: The bytes to parse from. + + :rtype: tuple + :rtype: (int, int) + :returns: A tuple containing the (parsed integer value, bytes consumed) + """ + value = unpack(DecodeUtils.INT16_BYTE_FORMAT, data[:2])[0] + return value, 2 + + @staticmethod + def unpack_int32(data): + """Parse a signed 32-bit integer from the bytes. + + :type data: bytes + :param data: The bytes to parse from. + + :rtype: tuple + :rtype: (int, int) + :returns: A tuple containing the (parsed integer value, bytes consumed) + """ + value = unpack(DecodeUtils.INT32_BYTE_FORMAT, data[:4])[0] + return value, 4 + + @staticmethod + def unpack_int64(data): + """Parse a signed 64-bit integer from the bytes. + + :type data: bytes + :param data: The bytes to parse from. + + :rtype: tuple + :rtype: (int, int) + :returns: A tuple containing the (parsed integer value, bytes consumed) + """ + value = unpack(DecodeUtils.INT64_BYTE_FORMAT, data[:8])[0] + return value, 8 + + @staticmethod + def unpack_byte_array(data, length_byte_size=2): + """Parse a variable length byte array from the bytes. + + The bytes are expected to be in the following format: + [ length ][0 ... length bytes] + where length is an unsigned integer represented in the smallest number + of bytes to hold the maximum length of the array. + + :type data: bytes + :param data: The bytes to parse from. + + :type length_byte_size: int + :param length_byte_size: The byte size of the preceding integer that + represents the length of the array. Supported values are 1, 2, and 4. + + :rtype: (bytes, int) + :returns: A tuple containing the (parsed byte array, bytes consumed). + """ + uint_byte_format = DecodeUtils.UINT_BYTE_FORMAT[length_byte_size] + length = unpack(uint_byte_format, data[:length_byte_size])[0] + bytes_end = length + length_byte_size + array_bytes = data[length_byte_size:bytes_end] + return array_bytes, bytes_end + + @staticmethod + def unpack_utf8_string(data, length_byte_size=2): + """Parse a variable length utf-8 string from the bytes. + + The bytes are expected to be in the following format: + [ length ][0 ... length bytes] + where length is an unsigned integer represented in the smallest number + of bytes to hold the maximum length of the array and the following + bytes are a valid utf-8 string. + + :type data: bytes + :param bytes: The bytes to parse from. + + :type length_byte_size: int + :param length_byte_size: The byte size of the preceding integer that + represents the length of the array. Supported values are 1, 2, and 4. + + :rtype: (str, int) + :returns: A tuple containing the (utf-8 string, bytes consumed). + """ + array_bytes, consumed = DecodeUtils.unpack_byte_array( + data, length_byte_size + ) + return array_bytes.decode('utf-8'), consumed + + @staticmethod + def unpack_uuid(data): + """Parse a 16-byte uuid from the bytes. + + :type data: bytes + :param data: The bytes to parse from. + + :rtype: (bytes, int) + :returns: A tuple containing the (uuid bytes, bytes consumed). + """ + return data[:16], 16 + + @staticmethod + def unpack_prelude(data): + """Parse the prelude for an event stream message from the bytes. + + The prelude for an event stream message has the following format: + [total_length][header_length][prelude_crc] + where each field is an unsigned 32-bit integer. + + :rtype: ((int, int, int), int) + :returns: A tuple of ((total_length, headers_length, prelude_crc), + consumed) + """ + return (unpack(DecodeUtils.PRELUDE_BYTE_FORMAT, data), _PRELUDE_LENGTH) + + +def _validate_checksum(data, checksum, crc=0): + # To generate the same numeric value across all Python versions and + # platforms use crc32(data) & 0xffffffff. + computed_checksum = crc32(data, crc) & 0xFFFFFFFF + if checksum != computed_checksum: + raise ChecksumMismatch(checksum, computed_checksum) + + +class MessagePrelude: + """Represents the prelude of an event stream message.""" + + def __init__(self, total_length, headers_length, crc): + self.total_length = total_length + self.headers_length = headers_length + self.crc = crc + + @property + def payload_length(self): + """Calculates the total payload length. + + The extra minus 4 bytes is for the message CRC. + + :rtype: int + :returns: The total payload length. + """ + return self.total_length - self.headers_length - _PRELUDE_LENGTH - 4 + + @property + def payload_end(self): + """Calculates the byte offset for the end of the message payload. + + The extra minus 4 bytes is for the message CRC. + + :rtype: int + :returns: The byte offset from the beginning of the event stream + message to the end of the payload. + """ + return self.total_length - 4 + + @property + def headers_end(self): + """Calculates the byte offset for the end of the message headers. + + :rtype: int + :returns: The byte offset from the beginning of the event stream + message to the end of the headers. + """ + return _PRELUDE_LENGTH + self.headers_length + + +class EventStreamMessage: + """Represents an event stream message.""" + + def __init__(self, prelude, headers, payload, crc): + self.prelude = prelude + self.headers = headers + self.payload = payload + self.crc = crc + + def to_response_dict(self, status_code=200): + message_type = self.headers.get(':message-type') + if message_type == 'error' or message_type == 'exception': + status_code = 400 + return { + 'status_code': status_code, + 'headers': self.headers, + 'body': self.payload, + } + + +class EventStreamHeaderParser: + """Parses the event headers from an event stream message. + + Expects all of the header data upfront and creates a dictionary of headers + to return. This object can be reused multiple times to parse the headers + from multiple event stream messages. + """ + + # Maps header type to appropriate unpacking function + # These unpacking functions return the value and the amount unpacked + _HEADER_TYPE_MAP = { + # boolean_true + 0: DecodeUtils.unpack_true, + # boolean_false + 1: DecodeUtils.unpack_false, + # byte + 2: DecodeUtils.unpack_int8, + # short + 3: DecodeUtils.unpack_int16, + # integer + 4: DecodeUtils.unpack_int32, + # long + 5: DecodeUtils.unpack_int64, + # byte_array + 6: DecodeUtils.unpack_byte_array, + # string + 7: DecodeUtils.unpack_utf8_string, + # timestamp + 8: DecodeUtils.unpack_int64, + # uuid + 9: DecodeUtils.unpack_uuid, + } + + def __init__(self): + self._data = None + + def parse(self, data): + """Parses the event stream headers from an event stream message. + + :type data: bytes + :param data: The bytes that correspond to the headers section of an + event stream message. + + :rtype: dict + :returns: A dictionary of header key, value pairs. + """ + self._data = data + return self._parse_headers() + + def _parse_headers(self): + headers = {} + while self._data: + name, value = self._parse_header() + if name in headers: + raise DuplicateHeader(name) + headers[name] = value + return headers + + def _parse_header(self): + name = self._parse_name() + value = self._parse_value() + return name, value + + def _parse_name(self): + name, consumed = DecodeUtils.unpack_utf8_string(self._data, 1) + self._advance_data(consumed) + return name + + def _parse_type(self): + type, consumed = DecodeUtils.unpack_uint8(self._data) + self._advance_data(consumed) + return type + + def _parse_value(self): + header_type = self._parse_type() + value_unpacker = self._HEADER_TYPE_MAP[header_type] + value, consumed = value_unpacker(self._data) + self._advance_data(consumed) + return value + + def _advance_data(self, consumed): + self._data = self._data[consumed:] + + +class EventStreamBuffer: + """Streaming based event stream buffer + + A buffer class that wraps bytes from an event stream providing parsed + messages as they become available via an iterable interface. + """ + + def __init__(self): + self._data = b'' + self._prelude = None + self._header_parser = EventStreamHeaderParser() + + def add_data(self, data): + """Add data to the buffer. + + :type data: bytes + :param data: The bytes to add to the buffer to be used when parsing + """ + self._data += data + + def _validate_prelude(self, prelude): + if prelude.headers_length > _MAX_HEADERS_LENGTH: + raise InvalidHeadersLength(prelude.headers_length) + + if prelude.payload_length > _MAX_PAYLOAD_LENGTH: + raise InvalidPayloadLength(prelude.payload_length) + + def _parse_prelude(self): + prelude_bytes = self._data[:_PRELUDE_LENGTH] + raw_prelude, _ = DecodeUtils.unpack_prelude(prelude_bytes) + prelude = MessagePrelude(*raw_prelude) + # The minus 4 removes the prelude crc from the bytes to be checked + _validate_checksum(prelude_bytes[: _PRELUDE_LENGTH - 4], prelude.crc) + self._validate_prelude(prelude) + return prelude + + def _parse_headers(self): + header_bytes = self._data[_PRELUDE_LENGTH : self._prelude.headers_end] + return self._header_parser.parse(header_bytes) + + def _parse_payload(self): + prelude = self._prelude + payload_bytes = self._data[prelude.headers_end : prelude.payload_end] + return payload_bytes + + def _parse_message_crc(self): + prelude = self._prelude + crc_bytes = self._data[prelude.payload_end : prelude.total_length] + message_crc, _ = DecodeUtils.unpack_uint32(crc_bytes) + return message_crc + + def _parse_message_bytes(self): + # The minus 4 includes the prelude crc to the bytes to be checked + message_bytes = self._data[ + _PRELUDE_LENGTH - 4 : self._prelude.payload_end + ] + return message_bytes + + def _validate_message_crc(self): + message_crc = self._parse_message_crc() + message_bytes = self._parse_message_bytes() + _validate_checksum(message_bytes, message_crc, crc=self._prelude.crc) + return message_crc + + def _parse_message(self): + crc = self._validate_message_crc() + headers = self._parse_headers() + payload = self._parse_payload() + message = EventStreamMessage(self._prelude, headers, payload, crc) + self._prepare_for_next_message() + return message + + def _prepare_for_next_message(self): + # Advance the data and reset the current prelude + self._data = self._data[self._prelude.total_length :] + self._prelude = None + + def next(self): + """Provides the next available message parsed from the stream + + :rtype: EventStreamMessage + :returns: The next event stream message + """ + if len(self._data) < _PRELUDE_LENGTH: + raise StopIteration() + + if self._prelude is None: + self._prelude = self._parse_prelude() + + if len(self._data) < self._prelude.total_length: + raise StopIteration() + + return self._parse_message() + + def __next__(self): + return self.next() + + def __iter__(self): + return self + + +class EventStream: + """Wrapper class for an event stream body. + + This wraps the underlying streaming body, parsing it for individual events + and yielding them as they come available through the iterator interface. + + The following example uses the S3 select API to get structured data out of + an object stored in S3 using an event stream. + + **Example:** + :: + from botocore.session import Session + + s3 = Session().create_client('s3') + response = s3.select_object_content( + Bucket='bucketname', + Key='keyname', + ExpressionType='SQL', + RequestProgress={'Enabled': True}, + Expression="SELECT * FROM S3Object s", + InputSerialization={'CSV': {}}, + OutputSerialization={'CSV': {}}, + ) + # This is the event stream in the response + event_stream = response['Payload'] + end_event_received = False + with open('output', 'wb') as f: + # Iterate over events in the event stream as they come + for event in event_stream: + # If we received a records event, write the data to a file + if 'Records' in event: + data = event['Records']['Payload'] + f.write(data) + # If we received a progress event, print the details + elif 'Progress' in event: + print(event['Progress']['Details']) + # End event indicates that the request finished successfully + elif 'End' in event: + print('Result is complete') + end_event_received = True + if not end_event_received: + raise Exception("End event not received, request incomplete.") + """ + + def __init__(self, raw_stream, output_shape, parser, operation_name): + self._raw_stream = raw_stream + self._output_shape = output_shape + self._operation_name = operation_name + self._parser = parser + self._event_generator = self._create_raw_event_generator() + + def __iter__(self): + for event in self._event_generator: + parsed_event = self._parse_event(event) + if parsed_event: + yield parsed_event + + def _create_raw_event_generator(self): + event_stream_buffer = EventStreamBuffer() + for chunk in self._raw_stream.stream(): + event_stream_buffer.add_data(chunk) + yield from event_stream_buffer + + def _parse_event(self, event): + response_dict = event.to_response_dict() + parsed_response = self._parser.parse(response_dict, self._output_shape) + if response_dict['status_code'] == 200: + return parsed_response + else: + raise EventStreamError(parsed_response, self._operation_name) + + def get_initial_response(self): + try: + initial_event = next(self._event_generator) + event_type = initial_event.headers.get(':event-type') + if event_type == 'initial-response': + return initial_event + except StopIteration: + pass + raise NoInitialResponseError() + + def close(self): + """Closes the underlying streaming body.""" + self._raw_stream.close() diff --git a/venv/lib/python3.12/site-packages/botocore/exceptions.py b/venv/lib/python3.12/site-packages/botocore/exceptions.py new file mode 100644 index 0000000..2a015b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/exceptions.py @@ -0,0 +1,863 @@ +# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +from botocore.vendored import requests +from botocore.vendored.requests.packages import urllib3 + + +def _exception_from_packed_args(exception_cls, args=None, kwargs=None): + # This is helpful for reducing Exceptions that only accept kwargs as + # only positional arguments can be provided for __reduce__ + # Ideally, this would also be a class method on the BotoCoreError + # but instance methods cannot be pickled. + if args is None: + args = () + if kwargs is None: + kwargs = {} + return exception_cls(*args, **kwargs) + + +class BotoCoreError(Exception): + """ + The base exception class for BotoCore exceptions. + + :ivar msg: The descriptive message associated with the error. + """ + + fmt = 'An unspecified error occurred' + + def __init__(self, **kwargs): + msg = self.fmt.format(**kwargs) + Exception.__init__(self, msg) + self.kwargs = kwargs + + def __reduce__(self): + return _exception_from_packed_args, (self.__class__, None, self.kwargs) + + +class DataNotFoundError(BotoCoreError): + """ + The data associated with a particular path could not be loaded. + + :ivar data_path: The data path that the user attempted to load. + """ + + fmt = 'Unable to load data for: {data_path}' + + +class UnknownServiceError(DataNotFoundError): + """Raised when trying to load data for an unknown service. + + :ivar service_name: The name of the unknown service. + + """ + + fmt = ( + "Unknown service: '{service_name}'. Valid service names are: " + "{known_service_names}" + ) + + +class UnknownRegionError(BotoCoreError): + """Raised when trying to load data for an unknown region. + + :ivar region_name: The name of the unknown region. + + """ + + fmt = "Unknown region: '{region_name}'. {error_msg}" + + +class ApiVersionNotFoundError(BotoCoreError): + """ + The data associated with either the API version or a compatible one + could not be loaded. + + :ivar data_path: The data path that the user attempted to load. + :ivar api_version: The API version that the user attempted to load. + """ + + fmt = 'Unable to load data {data_path} for: {api_version}' + + +class HTTPClientError(BotoCoreError): + fmt = 'An HTTP Client raised an unhandled exception: {error}' + + def __init__(self, request=None, response=None, **kwargs): + self.request = request + self.response = response + super().__init__(**kwargs) + + def __reduce__(self): + return _exception_from_packed_args, ( + self.__class__, + (self.request, self.response), + self.kwargs, + ) + + +class ConnectionError(BotoCoreError): + fmt = 'An HTTP Client failed to establish a connection: {error}' + + +class InvalidIMDSEndpointError(BotoCoreError): + fmt = 'Invalid endpoint EC2 Instance Metadata endpoint: {endpoint}' + + +class InvalidIMDSEndpointModeError(BotoCoreError): + fmt = ( + 'Invalid EC2 Instance Metadata endpoint mode: {mode}' + ' Valid endpoint modes (case-insensitive): {valid_modes}.' + ) + + +class EndpointConnectionError(ConnectionError): + fmt = 'Could not connect to the endpoint URL: "{endpoint_url}"' + + +class SSLError(ConnectionError, requests.exceptions.SSLError): + fmt = 'SSL validation failed for {endpoint_url} {error}' + + +class ConnectionClosedError(HTTPClientError): + fmt = ( + 'Connection was closed before we received a valid response ' + 'from endpoint URL: "{endpoint_url}".' + ) + + +class ReadTimeoutError( + HTTPClientError, + requests.exceptions.ReadTimeout, + urllib3.exceptions.ReadTimeoutError, +): + fmt = 'Read timeout on endpoint URL: "{endpoint_url}"' + + +class ConnectTimeoutError(ConnectionError, requests.exceptions.ConnectTimeout): + fmt = 'Connect timeout on endpoint URL: "{endpoint_url}"' + + +class ProxyConnectionError(ConnectionError, requests.exceptions.ProxyError): + fmt = 'Failed to connect to proxy URL: "{proxy_url}"' + + +class ResponseStreamingError(HTTPClientError): + fmt = 'An error occurred while reading from response stream: {error}' + + +class NoCredentialsError(BotoCoreError): + """ + No credentials could be found. + """ + + fmt = 'Unable to locate credentials' + + +class NoAuthTokenError(BotoCoreError): + """ + No authorization token could be found. + """ + + fmt = 'Unable to locate authorization token' + + +class TokenRetrievalError(BotoCoreError): + """ + Error attempting to retrieve a token from a remote source. + + :ivar provider: The name of the token provider. + :ivar error_msg: The msg explaining why the token could not be retrieved. + + """ + + fmt = 'Error when retrieving token from {provider}: {error_msg}' + + +class UnknownTokenProviderError(BotoCoreError): + """Tried to insert before/after an unregistered token provider.""" + + fmt = 'Token provider named {name} not found.' + + +class PartialCredentialsError(BotoCoreError): + """ + Only partial credentials were found. + + :ivar cred_var: The missing credential variable name. + + """ + + fmt = 'Partial credentials found in {provider}, missing: {cred_var}' + + +class CredentialRetrievalError(BotoCoreError): + """ + Error attempting to retrieve credentials from a remote source. + + :ivar provider: The name of the credential provider. + :ivar error_msg: The msg explaining why credentials could not be + retrieved. + + """ + + fmt = 'Error when retrieving credentials from {provider}: {error_msg}' + + +class UnknownSignatureVersionError(BotoCoreError): + """ + Requested Signature Version is not known. + + :ivar signature_version: The name of the requested signature version. + """ + + fmt = 'Unknown Signature Version: {signature_version}.' + + +class ServiceNotInRegionError(BotoCoreError): + """ + The service is not available in requested region. + + :ivar service_name: The name of the service. + :ivar region_name: The name of the region. + """ + + fmt = 'Service {service_name} not available in region {region_name}' + + +class BaseEndpointResolverError(BotoCoreError): + """Base error for endpoint resolving errors. + + Should never be raised directly, but clients can catch + this exception if they want to generically handle any errors + during the endpoint resolution process. + + """ + + +class NoRegionError(BaseEndpointResolverError): + """No region was specified.""" + + fmt = 'You must specify a region.' + + +class EndpointVariantError(BaseEndpointResolverError): + """ + Could not construct modeled endpoint variant. + + :ivar error_msg: The message explaining why the modeled endpoint variant + is unable to be constructed. + + """ + + fmt = ( + 'Unable to construct a modeled endpoint with the following ' + 'variant(s) {tags}: ' + ) + + +class UnknownEndpointError(BaseEndpointResolverError, ValueError): + """ + Could not construct an endpoint. + + :ivar service_name: The name of the service. + :ivar region_name: The name of the region. + """ + + fmt = ( + 'Unable to construct an endpoint for ' + '{service_name} in region {region_name}' + ) + + +class UnknownFIPSEndpointError(BaseEndpointResolverError): + """ + Could not construct a FIPS endpoint. + + :ivar service_name: The name of the service. + :ivar region_name: The name of the region. + """ + + fmt = ( + 'The provided FIPS pseudo-region "{region_name}" is not known for ' + 'the service "{service_name}". A FIPS compliant endpoint cannot be ' + 'constructed.' + ) + + +class ProfileNotFound(BotoCoreError): + """ + The specified configuration profile was not found in the + configuration file. + + :ivar profile: The name of the profile the user attempted to load. + """ + + fmt = 'The config profile ({profile}) could not be found' + + +class ConfigParseError(BotoCoreError): + """ + The configuration file could not be parsed. + + :ivar path: The path to the configuration file. + """ + + fmt = 'Unable to parse config file: {path}' + + +class ConfigNotFound(BotoCoreError): + """ + The specified configuration file could not be found. + + :ivar path: The path to the configuration file. + """ + + fmt = 'The specified config file ({path}) could not be found.' + + +class MissingParametersError(BotoCoreError): + """ + One or more required parameters were not supplied. + + :ivar object: The object that has missing parameters. + This can be an operation or a parameter (in the + case of inner params). The str() of this object + will be used so it doesn't need to implement anything + other than str(). + :ivar missing: The names of the missing parameters. + """ + + fmt = ( + 'The following required parameters are missing for ' + '{object_name}: {missing}' + ) + + +class ValidationError(BotoCoreError): + """ + An exception occurred validating parameters. + + Subclasses must accept a ``value`` and ``param`` + argument in their ``__init__``. + + :ivar value: The value that was being validated. + :ivar param: The parameter that failed validation. + :ivar type_name: The name of the underlying type. + """ + + fmt = "Invalid value ('{value}') for param {param} of type {type_name} " + + +class ParamValidationError(BotoCoreError): + fmt = 'Parameter validation failed:\n{report}' + + +# These exceptions subclass from ValidationError so that code +# can just 'except ValidationError' to catch any possibly validation +# error. +class UnknownKeyError(ValidationError): + """ + Unknown key in a struct parameter. + + :ivar value: The value that was being checked. + :ivar param: The name of the parameter. + :ivar choices: The valid choices the value can be. + """ + + fmt = ( + "Unknown key '{value}' for param '{param}'. Must be one of: {choices}" + ) + + +class RangeError(ValidationError): + """ + A parameter value was out of the valid range. + + :ivar value: The value that was being checked. + :ivar param: The parameter that failed validation. + :ivar min_value: The specified minimum value. + :ivar max_value: The specified maximum value. + """ + + fmt = ( + 'Value out of range for param {param}: ' + '{min_value} <= {value} <= {max_value}' + ) + + +class UnknownParameterError(ValidationError): + """ + Unknown top level parameter. + + :ivar name: The name of the unknown parameter. + :ivar operation: The name of the operation. + :ivar choices: The valid choices the parameter name can be. + """ + + fmt = ( + "Unknown parameter '{name}' for operation {operation}. Must be one " + "of: {choices}" + ) + + +class InvalidRegionError(ValidationError, ValueError): + """ + Invalid region_name provided to client or resource. + + :ivar region_name: region_name that was being validated. + """ + + fmt = "Provided region_name '{region_name}' doesn't match a supported format." + + +class AliasConflictParameterError(ValidationError): + """ + Error when an alias is provided for a parameter as well as the original. + + :ivar original: The name of the original parameter. + :ivar alias: The name of the alias + :ivar operation: The name of the operation. + """ + + fmt = ( + "Parameter '{original}' and its alias '{alias}' were provided " + "for operation {operation}. Only one of them may be used." + ) + + +class UnknownServiceStyle(BotoCoreError): + """ + Unknown style of service invocation. + + :ivar service_style: The style requested. + """ + + fmt = 'The service style ({service_style}) is not understood.' + + +class PaginationError(BotoCoreError): + fmt = 'Error during pagination: {message}' + + +class OperationNotPageableError(BotoCoreError): + fmt = 'Operation cannot be paginated: {operation_name}' + + +class ChecksumError(BotoCoreError): + """The expected checksum did not match the calculated checksum.""" + + fmt = ( + 'Checksum {checksum_type} failed, expected checksum ' + '{expected_checksum} did not match calculated checksum ' + '{actual_checksum}.' + ) + + +class UnseekableStreamError(BotoCoreError): + """Need to seek a stream, but stream does not support seeking.""" + + fmt = ( + 'Need to rewind the stream {stream_object}, but stream ' + 'is not seekable.' + ) + + +class WaiterError(BotoCoreError): + """Waiter failed to reach desired state.""" + + fmt = 'Waiter {name} failed: {reason}' + + def __init__(self, name, reason, last_response): + super().__init__(name=name, reason=reason) + self.last_response = last_response + + +class IncompleteReadError(BotoCoreError): + """HTTP response did not return expected number of bytes.""" + + fmt = '{actual_bytes} read, but total bytes expected is {expected_bytes}.' + + +class InvalidExpressionError(BotoCoreError): + """Expression is either invalid or too complex.""" + + fmt = 'Invalid expression {expression}: Only dotted lookups are supported.' + + +class UnknownCredentialError(BotoCoreError): + """Tried to insert before/after an unregistered credential type.""" + + fmt = 'Credential named {name} not found.' + + +class WaiterConfigError(BotoCoreError): + """Error when processing waiter configuration.""" + + fmt = 'Error processing waiter config: {error_msg}' + + +class UnknownClientMethodError(BotoCoreError): + """Error when trying to access a method on a client that does not exist.""" + + fmt = 'Client does not have method: {method_name}' + + +class UnsupportedSignatureVersionError(BotoCoreError): + """Error when trying to use an unsupported Signature Version.""" + + fmt = 'Signature version(s) are not supported: {signature_version}' + + +class ClientError(Exception): + MSG_TEMPLATE = ( + 'An error occurred ({error_code}) when calling the {operation_name} ' + 'operation{retry_info}: {error_message}' + ) + + def __init__(self, error_response, operation_name): + retry_info = self._get_retry_info(error_response) + error = error_response.get('Error', {}) + msg = self.MSG_TEMPLATE.format( + error_code=error.get('Code', 'Unknown'), + error_message=error.get('Message', 'Unknown'), + operation_name=operation_name, + retry_info=retry_info, + ) + super().__init__(msg) + self.response = error_response + self.operation_name = operation_name + + def _get_retry_info(self, response): + retry_info = '' + if 'ResponseMetadata' in response: + metadata = response['ResponseMetadata'] + if metadata.get('MaxAttemptsReached', False): + if 'RetryAttempts' in metadata: + retry_info = ( + f" (reached max retries: {metadata['RetryAttempts']})" + ) + return retry_info + + def __reduce__(self): + # Subclasses of ClientError's are dynamically generated and + # cannot be pickled unless they are attributes of a + # module. So at the very least return a ClientError back. + return ClientError, (self.response, self.operation_name) + + +class EventStreamError(ClientError): + pass + + +class UnsupportedTLSVersionWarning(Warning): + """Warn when an openssl version that uses TLS 1.2 is required""" + + pass + + +class ImminentRemovalWarning(Warning): + pass + + +class InvalidDNSNameError(BotoCoreError): + """Error when virtual host path is forced on a non-DNS compatible bucket""" + + fmt = ( + 'Bucket named {bucket_name} is not DNS compatible. Virtual ' + 'hosted-style addressing cannot be used. The addressing style ' + 'can be configured by removing the addressing_style value ' + 'or setting that value to \'path\' or \'auto\' in the AWS Config ' + 'file or in the botocore.client.Config object.' + ) + + +class InvalidS3AddressingStyleError(BotoCoreError): + """Error when an invalid path style is specified""" + + fmt = ( + 'S3 addressing style {s3_addressing_style} is invalid. Valid options ' + 'are: \'auto\', \'virtual\', and \'path\'' + ) + + +class UnsupportedS3ArnError(BotoCoreError): + """Error when S3 ARN provided to Bucket parameter is not supported""" + + fmt = ( + 'S3 ARN {arn} provided to "Bucket" parameter is invalid. Only ' + 'ARNs for S3 access-points are supported.' + ) + + +class UnsupportedS3ControlArnError(BotoCoreError): + """Error when S3 ARN provided to S3 control parameter is not supported""" + + fmt = 'S3 ARN "{arn}" provided is invalid for this operation. {msg}' + + +class InvalidHostLabelError(BotoCoreError): + """Error when an invalid host label would be bound to an endpoint""" + + fmt = ( + 'Invalid host label to be bound to the hostname of the endpoint: ' + '"{label}".' + ) + + +class UnsupportedOutpostResourceError(BotoCoreError): + """Error when S3 Outpost ARN provided to Bucket parameter is incomplete""" + + fmt = ( + 'S3 Outpost ARN resource "{resource_name}" provided to "Bucket" ' + 'parameter is invalid. Only ARNs for S3 Outpost arns with an ' + 'access-point sub-resource are supported.' + ) + + +class UnsupportedS3ConfigurationError(BotoCoreError): + """Error when an unsupported configuration is used with access-points""" + + fmt = 'Unsupported configuration when using S3: {msg}' + + +class UnsupportedS3AccesspointConfigurationError(BotoCoreError): + """Error when an unsupported configuration is used with access-points""" + + fmt = 'Unsupported configuration when using S3 access-points: {msg}' + + +class InvalidEndpointDiscoveryConfigurationError(BotoCoreError): + """Error when invalid value supplied for endpoint_discovery_enabled""" + + fmt = ( + 'Unsupported configuration value for endpoint_discovery_enabled. ' + 'Expected one of ("true", "false", "auto") but got {config_value}.' + ) + + +class UnsupportedS3ControlConfigurationError(BotoCoreError): + """Error when an unsupported configuration is used with S3 Control""" + + fmt = 'Unsupported configuration when using S3 Control: {msg}' + + +class InvalidRetryConfigurationError(BotoCoreError): + """Error when invalid retry configuration is specified""" + + fmt = ( + 'Cannot provide retry configuration for "{retry_config_option}". ' + 'Valid retry configuration options are: {valid_options}' + ) + + +class InvalidMaxRetryAttemptsError(InvalidRetryConfigurationError): + """Error when invalid retry configuration is specified""" + + fmt = ( + 'Value provided to "max_attempts": {provided_max_attempts} must ' + 'be an integer greater than or equal to {min_value}.' + ) + + +class InvalidRetryModeError(InvalidRetryConfigurationError): + """Error when invalid retry mode configuration is specified""" + + fmt = ( + 'Invalid value provided to "mode": "{provided_retry_mode}" must ' + 'be one of: {valid_modes}' + ) + + +class InvalidS3UsEast1RegionalEndpointConfigError(BotoCoreError): + """Error for invalid s3 us-east-1 regional endpoints configuration""" + + fmt = ( + 'S3 us-east-1 regional endpoint option ' + '{s3_us_east_1_regional_endpoint_config} is ' + 'invalid. Valid options are: "legacy", "regional"' + ) + + +class InvalidSTSRegionalEndpointsConfigError(BotoCoreError): + """Error when invalid sts regional endpoints configuration is specified""" + + fmt = ( + 'STS regional endpoints option {sts_regional_endpoints_config} is ' + 'invalid. Valid options are: "legacy", "regional"' + ) + + +class StubResponseError(BotoCoreError): + fmt = ( + 'Error getting response stub for operation {operation_name}: {reason}' + ) + + +class StubAssertionError(StubResponseError, AssertionError): + pass + + +class UnStubbedResponseError(StubResponseError): + pass + + +class InvalidConfigError(BotoCoreError): + fmt = '{error_msg}' + + +class InfiniteLoopConfigError(InvalidConfigError): + fmt = ( + 'Infinite loop in credential configuration detected. Attempting to ' + 'load from profile {source_profile} which has already been visited. ' + 'Visited profiles: {visited_profiles}' + ) + + +class RefreshWithMFAUnsupportedError(BotoCoreError): + fmt = 'Cannot refresh credentials: MFA token required.' + + +class MD5UnavailableError(BotoCoreError): + fmt = "This system does not support MD5 generation." + + +class MissingDependencyException(BotoCoreError): + fmt = "Missing Dependency: {msg}" + + +class MetadataRetrievalError(BotoCoreError): + fmt = "Error retrieving metadata: {error_msg}" + + +class UndefinedModelAttributeError(Exception): + pass + + +class MissingServiceIdError(UndefinedModelAttributeError): + fmt = ( + "The model being used for the service {service_name} is missing the " + "serviceId metadata property, which is required." + ) + + def __init__(self, **kwargs): + msg = self.fmt.format(**kwargs) + Exception.__init__(self, msg) + self.kwargs = kwargs + + +class SSOError(BotoCoreError): + fmt = ( + "An unspecified error happened when resolving AWS credentials or an " + "access token from SSO." + ) + + +class SSOTokenLoadError(SSOError): + fmt = "Error loading SSO Token: {error_msg}" + + +class UnauthorizedSSOTokenError(SSOError): + fmt = ( + "The SSO session associated with this profile has expired or is " + "otherwise invalid. To refresh this SSO session run aws sso login " + "with the corresponding profile." + ) + + +class LoginError(BotoCoreError): + fmt = ( + "An unspecified error happened when resolving AWS credentials or " + "refreshing a login session profile." + ) + + +class LoginRefreshRequired(LoginError): + fmt = "Your session has expired or credentials have changed. Please reauthenticate using 'aws login'." + + +class LoginInsufficientPermissions(LoginError): + fmt = ( + "Unable to create or refresh login credentials due to insufficient " + "permissions. You may be missing permission for the 'signin:CreateOAuth2Token' action." + ) + + +class LoginTokenLoadError(LoginError): + fmt = "Error loading login session token: {error_msg}" + + +class LoginAuthorizationCodeError(LoginError): + fmt = "Error loading or redeeming a login authorization code: {error_msg} " + + +class CapacityNotAvailableError(BotoCoreError): + fmt = 'Insufficient request capacity available.' + + +class InvalidProxiesConfigError(BotoCoreError): + fmt = 'Invalid configuration value(s) provided for proxies_config.' + + +class InvalidDefaultsMode(BotoCoreError): + fmt = ( + 'Client configured with invalid defaults mode: {mode}. ' + 'Valid defaults modes include: {valid_modes}.' + ) + + +class AwsChunkedWrapperError(BotoCoreError): + fmt = '{error_msg}' + + +class FlexibleChecksumError(BotoCoreError): + fmt = '{error_msg}' + + +class InvalidEndpointConfigurationError(BotoCoreError): + fmt = 'Invalid endpoint configuration: {msg}' + + +class EndpointProviderError(BotoCoreError): + """Base error for the EndpointProvider class""" + + fmt = '{msg}' + + +class EndpointResolutionError(EndpointProviderError): + """Error when input parameters resolve to an error rule""" + + fmt = '{msg}' + + +class UnknownEndpointResolutionBuiltInName(EndpointProviderError): + fmt = 'Unknown builtin variable name: {name}' + + +class InvalidChecksumConfigError(BotoCoreError): + """Error when an invalid checksum config value is supplied.""" + + fmt = ( + 'Unsupported configuration value for {config_key}. ' + 'Expected one of {valid_options} but got {config_value}.' + ) + + +class UnsupportedServiceProtocolsError(BotoCoreError): + """Error when a service does not use any protocol supported by botocore.""" + + fmt = ( + 'Botocore supports {botocore_supported_protocols}, but service {service} only ' + 'supports {service_supported_protocols}.' + ) diff --git a/venv/lib/python3.12/site-packages/botocore/handlers.py b/venv/lib/python3.12/site-packages/botocore/handlers.py new file mode 100644 index 0000000..11e9f47 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/handlers.py @@ -0,0 +1,1792 @@ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +"""Builtin event handlers. + +This module contains builtin handlers for events emitted by botocore. +""" + +import base64 +import copy +import logging +import os +import re +import uuid +import warnings +from io import BytesIO + +import botocore +import botocore.auth +from botocore import ( + retryhandler, # noqa: F401 + translate, # noqa: F401 + utils, +) +from botocore.args import ClientConfigString +from botocore.compat import ( + MD5_AVAILABLE, # noqa: F401 + ETree, + OrderedDict, + XMLParseError, + ensure_bytes, + get_md5, + json, + quote, + unquote, + unquote_str, + urlsplit, + urlunsplit, +) +from botocore.docs.utils import ( + AppendParamDocumentation, + AutoPopulatedParam, + DocumentModifiedShape, + HideParamFromOperations, +) +from botocore.endpoint_provider import VALID_HOST_LABEL_RE +from botocore.exceptions import ( + AliasConflictParameterError, + MissingServiceIdError, # noqa: F401 + ParamValidationError, + UnsupportedTLSVersionWarning, +) +from botocore.regions import EndpointResolverBuiltins +from botocore.signers import ( + add_dsql_generate_db_auth_token_methods, + add_generate_db_auth_token, + add_generate_presigned_post, + add_generate_presigned_url, +) +from botocore.useragent import register_feature_id +from botocore.utils import ( + SAFE_CHARS, + SERVICE_NAME_ALIASES, # noqa: F401 + ArnParser, + get_token_from_environment, + hyphenize_service_id, # noqa: F401 + is_global_accesspoint, # noqa: F401 + percent_encode, + switch_host_with_param, +) + +logger = logging.getLogger(__name__) + +REGISTER_FIRST = object() +REGISTER_LAST = object() +# From the S3 docs: +# The rules for bucket names in the US Standard region allow bucket names +# to be as long as 255 characters, and bucket names can contain any +# combination of uppercase letters, lowercase letters, numbers, periods +# (.), hyphens (-), and underscores (_). +VALID_BUCKET = re.compile(r'^[a-zA-Z0-9.\-_]{1,255}$') +_ACCESSPOINT_ARN = ( + r'^arn:(aws).*:(s3|s3-object-lambda):[a-z\-0-9]*:[0-9]{12}:accesspoint[/:]' + r'[a-zA-Z0-9\-.]{1,63}$' +) +_OUTPOST_ARN = ( + r'^arn:(aws).*:s3-outposts:[a-z\-0-9]+:[0-9]{12}:outpost[/:]' + r'[a-zA-Z0-9\-]{1,63}[/:]accesspoint[/:][a-zA-Z0-9\-]{1,63}$' +) +VALID_S3_ARN = re.compile('|'.join([_ACCESSPOINT_ARN, _OUTPOST_ARN])) +# signing names used for the services s3 and s3-control, for example in +# botocore/data/s3/2006-03-01/endpoints-rule-set-1.json +S3_SIGNING_NAMES = ('s3', 's3-outposts', 's3-object-lambda', 's3express') +VERSION_ID_SUFFIX = re.compile(r'\?versionId=[^\s]+$') + + +def handle_service_name_alias(service_name, **kwargs): + return SERVICE_NAME_ALIASES.get(service_name, service_name) + + +def add_recursion_detection_header(params, **kwargs): + has_lambda_name = 'AWS_LAMBDA_FUNCTION_NAME' in os.environ + trace_id = os.environ.get('_X_AMZN_TRACE_ID') + if has_lambda_name and trace_id: + headers = params['headers'] + if 'X-Amzn-Trace-Id' not in headers: + headers['X-Amzn-Trace-Id'] = quote(trace_id, safe='-=;:+&[]{}"\',') + + +def escape_xml_payload(params, **kwargs): + # Replace \r and \n with the escaped sequence over the whole XML document + # to avoid linebreak normalization modifying customer input when the + # document is parsed. Ideally, we would do this in ElementTree.tostring, + # but it doesn't allow us to override entity escaping for text fields. For + # this operation \r and \n can only appear in the XML document if they were + # passed as part of the customer input. + body = params['body'] + if b'\r' in body: + body = body.replace(b'\r', b' ') + if b'\n' in body: + body = body.replace(b'\n', b' ') + + params['body'] = body + + +def check_for_200_error(response, **kwargs): + """This function has been deprecated, but is kept for backwards compatibility.""" + # From: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectCOPY.html + # There are two opportunities for a copy request to return an error. One + # can occur when Amazon S3 receives the copy request and the other can + # occur while Amazon S3 is copying the files. If the error occurs before + # the copy operation starts, you receive a standard Amazon S3 error. If the + # error occurs during the copy operation, the error response is embedded in + # the 200 OK response. This means that a 200 OK response can contain either + # a success or an error. Make sure to design your application to parse the + # contents of the response and handle it appropriately. + # + # So this handler checks for this case. Even though the server sends a + # 200 response, conceptually this should be handled exactly like a + # 500 response (with respect to raising exceptions, retries, etc.) + # We're connected *before* all the other retry logic handlers, so as long + # as we switch the error code to 500, we'll retry the error as expected. + if response is None: + # A None response can happen if an exception is raised while + # trying to retrieve the response. See Endpoint._get_response(). + return + http_response, parsed = response + if _looks_like_special_case_error( + http_response.status_code, http_response.content + ): + logger.debug( + "Error found for response with 200 status code, " + "errors: %s, changing status code to " + "500.", + parsed, + ) + http_response.status_code = 500 + + +def _looks_like_special_case_error(status_code, body): + if status_code == 200 and body: + try: + parser = ETree.XMLParser( + target=ETree.TreeBuilder(), encoding='utf-8' + ) + parser.feed(body) + root = parser.close() + except XMLParseError: + # In cases of network disruptions, we may end up with a partial + # streamed response from S3. We need to treat these cases as + # 500 Service Errors and try again. + return True + if root.tag == 'Error': + return True + return False + + +def set_operation_specific_signer(context, signing_name, **kwargs): + """Choose the operation-specific signer. + + Individual operations may have a different auth type than the service as a + whole. This will most often manifest as operations that should not be + authenticated at all, but can include other auth modes such as sigv4 + without body signing. + """ + auth_type = context.get('auth_type') + + # Auth type will be None if the operation doesn't have a configured auth + # type. + if not auth_type: + return + + # Auth type will be the string value 'none' if the operation should not + # be signed at all. + if auth_type == 'none': + return botocore.UNSIGNED + + if auth_type == 'bearer': + return 'bearer' + + # If the operation needs an unsigned body, we set additional context + # allowing the signer to be aware of this. + if context.get('unsigned_payload') or auth_type == 'v4-unsigned-body': + context['payload_signing_enabled'] = False + + if auth_type.startswith('v4'): + if auth_type == 'v4-s3express': + return auth_type + + if auth_type == 'v4a': + _set_sigv4a_signing_context(context, signing_name) + signature_version = 'v4a' + else: + signature_version = 'v4' + + # Signing names used by s3 and s3-control use customized signers "s3v4" + # and "s3v4a". + if signing_name in S3_SIGNING_NAMES: + signature_version = f's3{signature_version}' + + return signature_version + + +def _handle_sqs_compatible_error(parsed, context, **kwargs): + """ + Ensures backward compatibility for SQS errors. + + SQS's migration from the Query protocol to JSON was done prior to SDKs allowing a + service to support multiple protocols. Because of this, SQS is missing the "error" + key from its modeled exceptions, which is used by most query compatible services + to map error codes to the proper exception. Instead, SQS uses the error's shape name, + which is preserved in the QueryErrorCode key. + """ + parsed_error = parsed.get("Error", {}) + if not parsed_error: + return + + if query_code := parsed_error.get("QueryErrorCode"): + context['error_code_override'] = query_code + + +def _resolve_sigv4a_region(context): + region = None + if 'client_config' in context: + region = context['client_config'].sigv4a_signing_region_set + if not region and context.get('signing', {}).get('region'): + region = context['signing']['region'] + return region or '*' + + +def _set_sigv4a_signing_context(context, signing_name): + # SigV4A signs for a region set rather than a single credential scope + # region, so ensure the request context reflects the configured region set. + region = _resolve_sigv4a_region(context) + signing = {'region': region, 'signing_name': signing_name} + if 'signing' in context: + context['signing'].update(signing) + else: + context['signing'] = signing + + +def decode_console_output(parsed, **kwargs): + if 'Output' in parsed: + try: + # We're using 'replace' for errors because it is + # possible that console output contains non string + # chars we can't utf-8 decode. + value = base64.b64decode( + bytes(parsed['Output'], 'latin-1') + ).decode('utf-8', 'replace') + parsed['Output'] = value + except (ValueError, TypeError, AttributeError): + logger.debug('Error decoding base64', exc_info=True) + + +def generate_idempotent_uuid(params, model, **kwargs): + for name in model.idempotent_members: + if name not in params: + params[name] = str(uuid.uuid4()) + logger.debug( + "injecting idempotency token (%s) into param '%s'.", + params[name], + name, + ) + + +def decode_quoted_jsondoc(value): + try: + value = json.loads(unquote(value)) + except (ValueError, TypeError): + logger.debug('Error loading quoted JSON', exc_info=True) + return value + + +def json_decode_template_body(parsed, **kwargs): + if 'TemplateBody' in parsed: + try: + value = json.loads( + parsed['TemplateBody'], object_pairs_hook=OrderedDict + ) + parsed['TemplateBody'] = value + except (ValueError, TypeError): + logger.debug('error loading JSON', exc_info=True) + + +def validate_bucket_name(params, **kwargs): + if 'Bucket' not in params: + return + bucket = params['Bucket'] + if not VALID_BUCKET.search(bucket) and not VALID_S3_ARN.search(bucket): + error_msg = ( + f'Invalid bucket name "{bucket}": Bucket name must match ' + f'the regex "{VALID_BUCKET.pattern}" or be an ARN matching ' + f'the regex "{VALID_S3_ARN.pattern}"' + ) + raise ParamValidationError(report=error_msg) + + +def sse_md5(params, **kwargs): + """ + S3 server-side encryption requires the encryption key to be sent to the + server base64 encoded, as well as a base64-encoded MD5 hash of the + encryption key. This handler does both if the MD5 has not been set by + the caller. + """ + _sse_md5(params, 'SSECustomer') + + +def copy_source_sse_md5(params, **kwargs): + """ + S3 server-side encryption requires the encryption key to be sent to the + server base64 encoded, as well as a base64-encoded MD5 hash of the + encryption key. This handler does both if the MD5 has not been set by + the caller specifically if the parameter is for the copy-source sse-c key. + """ + _sse_md5(params, 'CopySourceSSECustomer') + + +def _sse_md5(params, sse_member_prefix='SSECustomer'): + if not _needs_s3_sse_customization(params, sse_member_prefix): + return + + sse_key_member = sse_member_prefix + 'Key' + sse_md5_member = sse_member_prefix + 'KeyMD5' + key_as_bytes = params[sse_key_member] + if isinstance(key_as_bytes, str): + key_as_bytes = key_as_bytes.encode('utf-8') + md5_val = get_md5(key_as_bytes, usedforsecurity=False).digest() + key_md5_str = base64.b64encode(md5_val).decode('utf-8') + key_b64_encoded = base64.b64encode(key_as_bytes).decode('utf-8') + params[sse_key_member] = key_b64_encoded + params[sse_md5_member] = key_md5_str + + +def _needs_s3_sse_customization(params, sse_member_prefix): + return ( + params.get(sse_member_prefix + 'Key') is not None + and sse_member_prefix + 'KeyMD5' not in params + ) + + +def disable_signing(**kwargs): + """ + This handler disables request signing by setting the signer + name to a special sentinel value. + """ + return botocore.UNSIGNED + + +def add_expect_header(model, params, **kwargs): + if model.http.get('method', '') not in ['PUT', 'POST']: + return + if 'body' in params: + body = params['body'] + if hasattr(body, 'read'): + check_body = utils.ensure_boolean( + os.environ.get( + 'BOTO_EXPERIMENTAL__NO_EMPTY_CONTINUE', + False, + ) + ) + if check_body and utils.determine_content_length(body) == 0: + return + # Any file like object will use an expect 100-continue + # header regardless of size. + logger.debug("Adding expect 100 continue header to request.") + params['headers']['Expect'] = '100-continue' + + +class DeprecatedServiceDocumenter: + def __init__(self, replacement_service_name): + self._replacement_service_name = replacement_service_name + + def inject_deprecation_notice(self, section, event_name, **kwargs): + section.style.start_important() + section.write('This service client is deprecated. Please use ') + section.style.ref( + self._replacement_service_name, + self._replacement_service_name, + ) + section.write(' instead.') + section.style.end_important() + + +def document_copy_source_form(section, event_name, **kwargs): + if 'request-example' in event_name: + parent = section.get_section('structure-value') + param_line = parent.get_section('CopySource') + value_portion = param_line.get_section('member-value') + value_portion.clear_text() + value_portion.write( + "'string' or {'Bucket': 'string', " + "'Key': 'string', 'VersionId': 'string'}" + ) + elif 'request-params' in event_name: + param_section = section.get_section('CopySource') + type_section = param_section.get_section('param-type') + type_section.clear_text() + type_section.write(':type CopySource: str or dict') + doc_section = param_section.get_section('param-documentation') + doc_section.clear_text() + doc_section.write( + "The name of the source bucket, key name of the source object, " + "and optional version ID of the source object. You can either " + "provide this value as a string or a dictionary. The " + "string form is {bucket}/{key} or " + "{bucket}/{key}?versionId={versionId} if you want to copy a " + "specific version. You can also provide this value as a " + "dictionary. The dictionary format is recommended over " + "the string format because it is more explicit. The dictionary " + "format is: {'Bucket': 'bucket', 'Key': 'key', 'VersionId': 'id'}." + " Note that the VersionId key is optional and may be omitted." + " To specify an S3 access point, provide the access point" + " ARN for the ``Bucket`` key in the copy source dictionary. If you" + " want to provide the copy source for an S3 access point as a" + " string instead of a dictionary, the ARN provided must be the" + " full S3 access point object ARN" + " (i.e. {accesspoint_arn}/object/{key})" + ) + + +def handle_copy_source_param(params, **kwargs): + """Convert CopySource param for CopyObject/UploadPartCopy. + + This handler will deal with two cases: + + * CopySource provided as a string. We'll make a best effort + to URL encode the key name as required. This will require + parsing the bucket and version id from the CopySource value + and only encoding the key. + * CopySource provided as a dict. In this case we're + explicitly given the Bucket, Key, and VersionId so we're + able to encode the key and ensure this value is serialized + and correctly sent to S3. + + """ + source = params.get('CopySource') + if source is None: + # The call will eventually fail but we'll let the + # param validator take care of this. It will + # give a better error message. + return + if isinstance(source, str): + params['CopySource'] = _quote_source_header(source) + elif isinstance(source, dict): + params['CopySource'] = _quote_source_header_from_dict(source) + + +def _quote_source_header_from_dict(source_dict): + try: + bucket = source_dict['Bucket'] + key = source_dict['Key'] + version_id = source_dict.get('VersionId') + if VALID_S3_ARN.search(bucket): + final = f'{bucket}/object/{key}' + else: + final = f'{bucket}/{key}' + except KeyError as e: + raise ParamValidationError( + report=f'Missing required parameter: {str(e)}' + ) + final = percent_encode(final, safe=SAFE_CHARS + '/') + if version_id is not None: + final += f'?versionId={version_id}' + return final + + +def _quote_source_header(value): + result = VERSION_ID_SUFFIX.search(value) + if result is None: + return percent_encode(value, safe=SAFE_CHARS + '/') + else: + first, version_id = value[: result.start()], value[result.start() :] + return percent_encode(first, safe=SAFE_CHARS + '/') + version_id + + +def _get_cross_region_presigned_url( + request_signer, request_dict, model, source_region, destination_region +): + # The better way to do this is to actually get the + # endpoint_resolver and get the endpoint_url given the + # source region. In this specific case, we know that + # we can safely replace the dest region with the source + # region because of the supported EC2 regions, but in + # general this is not a safe assumption to make. + # I think eventually we should try to plumb through something + # that allows us to resolve endpoints from regions. + request_dict_copy = copy.deepcopy(request_dict) + request_dict_copy['body']['DestinationRegion'] = destination_region + request_dict_copy['url'] = request_dict['url'].replace( + destination_region, source_region + ) + request_dict_copy['method'] = 'GET' + request_dict_copy['headers'] = {} + return request_signer.generate_presigned_url( + request_dict_copy, region_name=source_region, operation_name=model.name + ) + + +def _get_presigned_url_source_and_destination_regions(request_signer, params): + # Gets the source and destination regions to be used + destination_region = request_signer._region_name + source_region = params.get('SourceRegion') + return source_region, destination_region + + +def inject_presigned_url_ec2(params, request_signer, model, **kwargs): + # The customer can still provide this, so we should pass if they do. + if 'PresignedUrl' in params['body']: + return + src, dest = _get_presigned_url_source_and_destination_regions( + request_signer, params['body'] + ) + url = _get_cross_region_presigned_url( + request_signer, params, model, src, dest + ) + params['body']['PresignedUrl'] = url + # EC2 Requires that the destination region be sent over the wire in + # addition to the source region. + params['body']['DestinationRegion'] = dest + + +def inject_presigned_url_rds(params, request_signer, model, **kwargs): + # SourceRegion is not required for RDS operations, so it's possible that + # it isn't set. In that case it's probably a local copy so we don't need + # to do anything else. + if 'SourceRegion' not in params['body']: + return + + src, dest = _get_presigned_url_source_and_destination_regions( + request_signer, params['body'] + ) + + # Since SourceRegion isn't actually modeled for RDS, it needs to be + # removed from the request params before we send the actual request. + del params['body']['SourceRegion'] + + if 'PreSignedUrl' in params['body']: + return + + url = _get_cross_region_presigned_url( + request_signer, params, model, src, dest + ) + params['body']['PreSignedUrl'] = url + + +def json_decode_policies(parsed, model, **kwargs): + # Any time an IAM operation returns a policy document + # it is a string that is json that has been urlencoded, + # i.e urlencode(json.dumps(policy_document)). + # To give users something more useful, we will urldecode + # this value and json.loads() the result so that they have + # the policy document as a dictionary. + output_shape = model.output_shape + if output_shape is not None: + _decode_policy_types(parsed, model.output_shape) + + +def _decode_policy_types(parsed, shape): + # IAM consistently uses the policyDocumentType shape to indicate + # strings that have policy documents. + shape_name = 'policyDocumentType' + if shape.type_name == 'structure': + for member_name, member_shape in shape.members.items(): + if ( + member_shape.type_name == 'string' + and member_shape.name == shape_name + and member_name in parsed + ): + parsed[member_name] = decode_quoted_jsondoc( + parsed[member_name] + ) + elif member_name in parsed: + _decode_policy_types(parsed[member_name], member_shape) + if shape.type_name == 'list': + shape_member = shape.member + for item in parsed: + _decode_policy_types(item, shape_member) + + +def parse_get_bucket_location(parsed, http_response, **kwargs): + # s3.GetBucketLocation cannot be modeled properly. To + # account for this we just manually parse the XML document. + # The "parsed" passed in only has the ResponseMetadata + # filled out. This handler will fill in the LocationConstraint + # value. + if http_response.raw is None: + return + response_body = http_response.content + parser = ETree.XMLParser(target=ETree.TreeBuilder(), encoding='utf-8') + parser.feed(response_body) + root = parser.close() + region = root.text + parsed['LocationConstraint'] = region + + +def base64_encode_user_data(params, **kwargs): + if 'UserData' in params: + if isinstance(params['UserData'], str): + # Encode it to bytes if it is text. + params['UserData'] = params['UserData'].encode('utf-8') + params['UserData'] = base64.b64encode(params['UserData']).decode( + 'utf-8' + ) + + +def document_base64_encoding(param): + description = ( + '**This value will be base64 encoded automatically. Do ' + 'not base64 encode this value prior to performing the ' + 'operation.**' + ) + append = AppendParamDocumentation(param, description) + return append.append_documentation + + +def validate_ascii_metadata(params, **kwargs): + """Verify S3 Metadata only contains ascii characters. + + From: http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html + + "Amazon S3 stores user-defined metadata in lowercase. Each name, value pair + must conform to US-ASCII when using REST and UTF-8 when using SOAP or + browser-based uploads via POST." + + """ + metadata = params.get('Metadata') + if not metadata or not isinstance(metadata, dict): + # We have to at least type check the metadata as a dict type + # because this handler is called before param validation. + # We'll go ahead and return because the param validator will + # give a descriptive error message for us. + # We might need a post-param validation event. + return + for key, value in metadata.items(): + try: + key.encode('ascii') + value.encode('ascii') + except UnicodeEncodeError: + error_msg = ( + 'Non ascii characters found in S3 metadata ' + f'for key "{key}", value: "{value}". \nS3 metadata can only ' + 'contain ASCII characters. ' + ) + raise ParamValidationError(report=error_msg) + + +def fix_route53_ids(params, model, **kwargs): + """ + Check for and split apart Route53 resource IDs, setting + only the last piece. This allows the output of one operation + (e.g. ``'foo/1234'``) to be used as input in another + operation (e.g. it expects just ``'1234'``). + """ + input_shape = model.input_shape + if not input_shape or not hasattr(input_shape, 'members'): + return + + members = [ + name + for (name, shape) in input_shape.members.items() + if shape.name in ['ResourceId', 'DelegationSetId', 'ChangeId'] + ] + + for name in members: + if name in params: + orig_value = params[name] + params[name] = orig_value.split('/')[-1] + logger.debug('%s %s -> %s', name, orig_value, params[name]) + + +def inject_account_id(params, **kwargs): + if params.get('accountId') is None: + # Glacier requires accountId, but allows you + # to specify '-' for the current owners account. + # We add this default value if the user does not + # provide the accountId as a convenience. + params['accountId'] = '-' + + +def add_glacier_version(model, params, **kwargs): + request_dict = params + request_dict['headers']['x-amz-glacier-version'] = model.metadata[ + 'apiVersion' + ] + + +def add_accept_header(model, params, **kwargs): + if params['headers'].get('Accept', None) is None: + request_dict = params + request_dict['headers']['Accept'] = 'application/json' + + +def add_glacier_checksums(params, **kwargs): + """Add glacier checksums to the http request. + + This will add two headers to the http request: + + * x-amz-content-sha256 + * x-amz-sha256-tree-hash + + These values will only be added if they are not present + in the HTTP request. + + """ + request_dict = params + headers = request_dict['headers'] + body = request_dict['body'] + if isinstance(body, bytes): + # If the user provided a bytes type instead of a file + # like object, we're temporarily create a BytesIO object + # so we can use the util functions to calculate the + # checksums which assume file like objects. Note that + # we're not actually changing the body in the request_dict. + body = BytesIO(body) + starting_position = body.tell() + if 'x-amz-content-sha256' not in headers: + headers['x-amz-content-sha256'] = utils.calculate_sha256( + body, as_hex=True + ) + body.seek(starting_position) + if 'x-amz-sha256-tree-hash' not in headers: + headers['x-amz-sha256-tree-hash'] = utils.calculate_tree_hash(body) + body.seek(starting_position) + + +def document_glacier_tree_hash_checksum(): + doc = ''' + This is a required field. + + Ideally you will want to compute this value with checksums from + previous uploaded parts, using the algorithm described in + `Glacier documentation `_. + + But if you prefer, you can also use botocore.utils.calculate_tree_hash() + to compute it from raw file by:: + + checksum = calculate_tree_hash(open('your_file.txt', 'rb')) + + ''' + return AppendParamDocumentation('checksum', doc).append_documentation + + +def document_cloudformation_get_template_return_type( + section, event_name, **kwargs +): + if 'response-params' in event_name: + template_body_section = section.get_section('TemplateBody') + type_section = template_body_section.get_section('param-type') + type_section.clear_text() + type_section.write('(*dict*) --') + elif 'response-example' in event_name: + parent = section.get_section('structure-value') + param_line = parent.get_section('TemplateBody') + value_portion = param_line.get_section('member-value') + value_portion.clear_text() + value_portion.write('{}') + + +def switch_host_machinelearning(request, **kwargs): + switch_host_with_param(request, 'PredictEndpoint') + + +def check_openssl_supports_tls_version_1_2(**kwargs): + import ssl + + try: + openssl_version_tuple = ssl.OPENSSL_VERSION_INFO + if openssl_version_tuple < (1, 0, 1): + warnings.warn( + f'Currently installed openssl version: {ssl.OPENSSL_VERSION} does not ' + 'support TLS 1.2, which is required for use of iot-data. ' + 'Please use python installed with openssl version 1.0.1 or ' + 'higher.', + UnsupportedTLSVersionWarning, + ) + # We cannot check the openssl version on python2.6, so we should just + # pass on this conveniency check. + except AttributeError: + pass + + +def change_get_to_post(request, **kwargs): + # This is useful when we need to change a potentially large GET request + # into a POST with x-www-form-urlencoded encoding. + if request.method == 'GET' and '?' in request.url: + request.headers['Content-Type'] = 'application/x-www-form-urlencoded' + request.method = 'POST' + request.url, request.data = request.url.split('?', 1) + + +def set_list_objects_encoding_type_url(params, context, **kwargs): + if 'EncodingType' not in params: + # We set this context so that we know it wasn't the customer that + # requested the encoding. + context['encoding_type_auto_set'] = True + params['EncodingType'] = 'url' + + +def decode_list_object(parsed, context, **kwargs): + # This is needed because we are passing url as the encoding type. Since the + # paginator is based on the key, we need to handle it before it can be + # round tripped. + # + # From the documentation: If you specify encoding-type request parameter, + # Amazon S3 includes this element in the response, and returns encoded key + # name values in the following response elements: + # Delimiter, Marker, Prefix, NextMarker, Key. + _decode_list_object( + top_level_keys=['Delimiter', 'Marker', 'NextMarker'], + nested_keys=[('Contents', 'Key'), ('CommonPrefixes', 'Prefix')], + parsed=parsed, + context=context, + ) + + +def decode_list_object_v2(parsed, context, **kwargs): + # From the documentation: If you specify encoding-type request parameter, + # Amazon S3 includes this element in the response, and returns encoded key + # name values in the following response elements: + # Delimiter, Prefix, ContinuationToken, Key, and StartAfter. + _decode_list_object( + top_level_keys=['Delimiter', 'Prefix', 'StartAfter'], + nested_keys=[('Contents', 'Key'), ('CommonPrefixes', 'Prefix')], + parsed=parsed, + context=context, + ) + + +def decode_list_object_versions(parsed, context, **kwargs): + # From the documentation: If you specify encoding-type request parameter, + # Amazon S3 includes this element in the response, and returns encoded key + # name values in the following response elements: + # KeyMarker, NextKeyMarker, Prefix, Key, and Delimiter. + _decode_list_object( + top_level_keys=[ + 'KeyMarker', + 'NextKeyMarker', + 'Prefix', + 'Delimiter', + ], + nested_keys=[ + ('Versions', 'Key'), + ('DeleteMarkers', 'Key'), + ('CommonPrefixes', 'Prefix'), + ], + parsed=parsed, + context=context, + ) + + +def _decode_list_object(top_level_keys, nested_keys, parsed, context): + if parsed.get('EncodingType') == 'url' and context.get( + 'encoding_type_auto_set' + ): + # URL decode top-level keys in the response if present. + for key in top_level_keys: + if key in parsed: + parsed[key] = unquote_str(parsed[key]) + # URL decode nested keys from the response if present. + for top_key, child_key in nested_keys: + if top_key in parsed: + for member in parsed[top_key]: + member[child_key] = unquote_str(member[child_key]) + + +def convert_body_to_file_like_object(params, **kwargs): + if 'Body' in params: + if isinstance(params['Body'], str): + params['Body'] = BytesIO(ensure_bytes(params['Body'])) + elif isinstance(params['Body'], bytes): + params['Body'] = BytesIO(params['Body']) + + +def _add_parameter_aliases(handler_list): + # Mapping of original parameter to parameter alias. + # The key is ..parameter + # The first part of the key is used for event registration. + # The last part is the original parameter name and the value is the + # alias to expose in documentation. + aliases = { + 'ec2.*.Filter': 'Filters', + 'logs.CreateExportTask.from': 'fromTime', + 'cloudsearchdomain.Search.return': 'returnFields', + } + + for original, new_name in aliases.items(): + event_portion, original_name = original.rsplit('.', 1) + parameter_alias = ParameterAlias(original_name, new_name) + + # Add the handlers to the list of handlers. + # One handler is to handle when users provide the alias. + # The other handler is to update the documentation to show only + # the alias. + parameter_build_event_handler_tuple = ( + 'before-parameter-build.' + event_portion, + parameter_alias.alias_parameter_in_call, + REGISTER_FIRST, + ) + docs_event_handler_tuple = ( + 'docs.*.' + event_portion + '.complete-section', + parameter_alias.alias_parameter_in_documentation, + ) + handler_list.append(parameter_build_event_handler_tuple) + handler_list.append(docs_event_handler_tuple) + + +class ParameterAlias: + def __init__(self, original_name, alias_name): + self._original_name = original_name + self._alias_name = alias_name + + def alias_parameter_in_call(self, params, model, **kwargs): + if model.input_shape: + # Only consider accepting the alias if it is modeled in the + # input shape. + if self._original_name in model.input_shape.members: + if self._alias_name in params: + if self._original_name in params: + raise AliasConflictParameterError( + original=self._original_name, + alias=self._alias_name, + operation=model.name, + ) + # Remove the alias parameter value and use the old name + # instead. + params[self._original_name] = params.pop(self._alias_name) + + def alias_parameter_in_documentation(self, event_name, section, **kwargs): + if event_name.startswith('docs.request-params'): + if self._original_name not in section.available_sections: + return + # Replace the name for parameter type + param_section = section.get_section(self._original_name) + param_type_section = param_section.get_section('param-type') + self._replace_content(param_type_section) + + # Replace the name for the parameter description + param_name_section = param_section.get_section('param-name') + self._replace_content(param_name_section) + elif event_name.startswith('docs.request-example'): + section = section.get_section('structure-value') + if self._original_name not in section.available_sections: + return + # Replace the name for the example + param_section = section.get_section(self._original_name) + self._replace_content(param_section) + + def _replace_content(self, section): + content = section.getvalue().decode('utf-8') + updated_content = content.replace( + self._original_name, self._alias_name + ) + section.clear_text() + section.write(updated_content) + + +class ClientMethodAlias: + def __init__(self, actual_name): + """Aliases a non-extant method to an existing method. + + :param actual_name: The name of the method that actually exists on + the client. + """ + self._actual = actual_name + + def __call__(self, client, **kwargs): + return getattr(client, self._actual) + + +# TODO: Remove this class as it is no longer used +class HeaderToHostHoister: + """Takes a header and moves it to the front of the hoststring.""" + + _VALID_HOSTNAME = re.compile(r'(?!-)[a-z\d-]{1,63}(? 1 and ArnParser.is_arn( + unquote(auth_path_parts[1]) + ): + request.auth_path = '/'.join(['', *auth_path_parts[2:]]) + + +def customize_endpoint_resolver_builtins( + builtins, model, params, context, **kwargs +): + """Modify builtin parameter values for endpoint resolver + + Modifies the builtins dict in place. Changes are in effect for one call. + The corresponding event is emitted only if at least one builtin parameter + value is required for endpoint resolution for the operation. + """ + bucket_name = params.get('Bucket') + bucket_is_arn = bucket_name is not None and ArnParser.is_arn(bucket_name) + # In some situations the host will return AuthorizationHeaderMalformed + # when the signing region of a sigv4 request is not the bucket's + # region (which is likely unknown by the user of GetBucketLocation). + # Avoid this by always using path-style addressing. + if model.name == 'GetBucketLocation': + builtins[EndpointResolverBuiltins.AWS_S3_FORCE_PATH_STYLE] = True + # All situations where the bucket name is an ARN are not compatible + # with path style addressing. + elif bucket_is_arn: + builtins[EndpointResolverBuiltins.AWS_S3_FORCE_PATH_STYLE] = False + + # Bucket names that are invalid host labels require path-style addressing. + # If path-style addressing was specifically requested, the default builtin + # value is already set. + path_style_required = ( + bucket_name is not None and not VALID_HOST_LABEL_RE.match(bucket_name) + ) + path_style_requested = builtins[ + EndpointResolverBuiltins.AWS_S3_FORCE_PATH_STYLE + ] + + # Path-style addressing is incompatible with the global endpoint for + # presigned URLs. If the bucket name is an ARN, the ARN's region should be + # used in the endpoint. + if ( + context.get('use_global_endpoint') + and not path_style_required + and not path_style_requested + and not bucket_is_arn + and not utils.is_s3express_bucket(bucket_name) + ): + builtins[EndpointResolverBuiltins.AWS_REGION] = 'aws-global' + builtins[EndpointResolverBuiltins.AWS_S3_USE_GLOBAL_ENDPOINT] = True + + +def remove_content_type_header_for_presigning(request, **kwargs): + if ( + request.context.get('is_presign_request') is True + and 'Content-Type' in request.headers + ): + del request.headers['Content-Type'] + + +def handle_expires_header( + operation_model, response_dict, customized_response_dict, **kwargs +): + if _has_expires_shape(operation_model.output_shape): + if expires_value := response_dict.get('headers', {}).get('Expires'): + customized_response_dict['ExpiresString'] = expires_value + try: + utils.parse_timestamp(expires_value) + except (ValueError, RuntimeError): + logger.warning( + 'Failed to parse the "Expires" member as a timestamp: %s. ' + 'The unparsed value is available in the response under "ExpiresString".', + expires_value, + ) + del response_dict['headers']['Expires'] + + +def _has_expires_shape(shape): + if not shape: + return False + return any( + member_shape.name == 'Expires' + and member_shape.serialization.get('name') == 'Expires' + for member_shape in shape.members.values() + ) + + +def document_expires_shape(section, event_name, **kwargs): + # Updates the documentation for S3 operations that include the 'Expires' member + # in their response structure. Documents a synthetic member 'ExpiresString' and + # includes a deprecation notice for 'Expires'. + if 'response-example' in event_name: + if not section.has_section('structure-value'): + return + parent = section.get_section('structure-value') + if not parent.has_section('Expires'): + return + param_line = parent.get_section('Expires') + param_line.add_new_section('ExpiresString') + new_param_line = param_line.get_section('ExpiresString') + new_param_line.write("'ExpiresString': 'string',") + new_param_line.style.new_line() + elif 'response-params' in event_name: + if not section.has_section('Expires'): + return + param_section = section.get_section('Expires') + # Add a deprecation notice for the "Expires" param + doc_section = param_section.get_section('param-documentation') + doc_section.style.start_note() + doc_section.write( + 'This member has been deprecated. Please use ``ExpiresString`` instead.' + ) + doc_section.style.end_note() + # Document the "ExpiresString" param + new_param_section = param_section.add_new_section('ExpiresString') + new_param_section.style.new_paragraph() + new_param_section.write('- **ExpiresString** *(string) --*') + new_param_section.style.indent() + new_param_section.style.new_paragraph() + new_param_section.write( + 'The raw, unparsed value of the ``Expires`` field.' + ) + + +def _handle_200_error(operation_model, response_dict, **kwargs): + # S3 can return a 200 response with an error embedded in the body. + # Convert the 200 to a 500 for retry resolution in ``_update_status_code``. + if not _should_handle_200_error(operation_model, response_dict): + # Operations with streaming response blobs are excluded as they + # can't be reliably distinguished from an S3 error. + return + if _looks_like_special_case_error( + response_dict['status_code'], response_dict['body'] + ): + response_dict['status_code'] = 500 + logger.debug( + "Error found for response with 200 status code: %s.", + response_dict['body'], + ) + + +def _should_handle_200_error(operation_model, response_dict): + output_shape = operation_model.output_shape + if ( + not response_dict + or operation_model.has_event_stream_output + or not output_shape + ): + return False + payload = output_shape.serialization.get('payload') + if payload is not None: + payload_shape = output_shape.members[payload] + if payload_shape.type_name in ('blob', 'string'): + return False + return True + + +def _map_oauth2_errors(response_dict, **kwargs): + # SSO OIDC follows the OAuth2 standard, which returns error messages in + # 'error_description' instead of the 'Message' field botocore expects. + try: + if response_dict.get('status_code') < 400: + return + body = json.loads(response_dict.get('body', b'{}')) + if message := body.get('error_description'): + if not body.get('Message', body.get("message")): + body['Message'] = message + response_dict['body'] = json.dumps(body).encode('utf-8') + except (ValueError, AttributeError, TypeError): + pass + + +def _update_status_code(response, **kwargs): + # Update the http_response status code when the parsed response has been + # modified in a handler. This enables retries for cases like ``_handle_200_error``. + if response is None: + return + http_response, parsed = response + parsed_status_code = parsed.get('ResponseMetadata', {}).get( + 'HTTPStatusCode', http_response.status_code + ) + if http_response.status_code != parsed_status_code: + http_response.status_code = parsed_status_code + + +def _handle_request_validation_mode_member(params, model, **kwargs): + client_config = kwargs.get("context", {}).get("client_config") + if client_config is None: + return + response_checksum_validation = client_config.response_checksum_validation + http_checksum = model.http_checksum + mode_member = http_checksum.get("requestValidationModeMember") + if ( + mode_member is not None + and response_checksum_validation == "when_supported" + ): + params.setdefault(mode_member, "ENABLED") + + +def _set_extra_headers_for_unsigned_request( + request, signature_version, **kwargs +): + # When sending a checksum in the trailer of an unsigned chunked request, S3 + # requires us to set the "X-Amz-Content-SHA256" header to "STREAMING-UNSIGNED-PAYLOAD-TRAILER". + checksum_context = request.context.get("checksum", {}) + algorithm = checksum_context.get("request_algorithm", {}) + in_trailer = algorithm.get("in") == "trailer" + headers = request.headers + if signature_version == botocore.UNSIGNED and in_trailer: + headers["X-Amz-Content-SHA256"] = "STREAMING-UNSIGNED-PAYLOAD-TRAILER" + + +def _set_auth_scheme_preference_signer(context, signing_name, **kwargs): + """ + Determines the appropriate signer to use based on the client configuration, + authentication scheme preferences, and the availability of a bearer token. + """ + client_config = context.get('client_config') + if client_config is None: + return + + signature_version = client_config.signature_version + auth_scheme_preference = client_config.auth_scheme_preference + auth_options = context.get('auth_options') + + signature_version_set_in_code = ( + isinstance(signature_version, ClientConfigString) + or signature_version is botocore.UNSIGNED + ) + auth_preference_set_in_code = isinstance( + auth_scheme_preference, ClientConfigString + ) + has_in_code_configuration = ( + signature_version_set_in_code or auth_preference_set_in_code + ) + + resolved_signature_version = signature_version + + # If signature version was not set in code, but an auth scheme preference + # is available, resolve it based on the preferred schemes and supported auth + # options for this service. + if ( + not signature_version_set_in_code + and auth_scheme_preference + and auth_options + ): + preferred_schemes = auth_scheme_preference.split(',') + resolved = botocore.auth.resolve_auth_scheme_preference( + preferred_schemes, auth_options + ) + resolved_signature_version = ( + botocore.UNSIGNED if resolved == 'none' else resolved + ) + + # Prefer 'bearer' signature version if a bearer token is available, and it + # is allowed for this service. This can override earlier resolution if the + # config object didn't explicitly set a signature version. + if _should_prefer_bearer_auth( + has_in_code_configuration, + signing_name, + resolved_signature_version, + auth_options, + ): + register_feature_id('BEARER_SERVICE_ENV_VARS') + resolved_signature_version = 'bearer' + + if resolved_signature_version == 'v4a': + _set_sigv4a_signing_context(context, signing_name) + + if resolved_signature_version == signature_version: + return None + return resolved_signature_version + + +def _should_prefer_bearer_auth( + has_in_code_configuration, + signing_name, + resolved_signature_version, + auth_options, +): + if signing_name not in get_bearer_auth_supported_services(): + return False + + if not auth_options or 'smithy.api#httpBearerAuth' not in auth_options: + return False + + has_token = get_token_from_environment(signing_name) is not None + + # Prefer 'bearer' if a bearer token is available, and either: + # Bearer was already resolved, or + # No auth-related values were explicitly set in code + return has_token and ( + resolved_signature_version == 'bearer' or not has_in_code_configuration + ) + + +def get_bearer_auth_supported_services(): + """ + Returns a set of services that support bearer token authentication. + These values correspond to the service's `signingName` property as defined + in model.py, falling back to `endpointPrefix` if `signingName` is not set. + + Warning: This is a private interface and is subject to abrupt breaking changes, + including removal, in any botocore release. It is not intended for external use, + and its usage outside of botocore is not advised or supported. + """ + return {'bedrock'} + + +# This is a list of (event_name, handler). +# When a Session is created, everything in this list will be +# automatically registered with that Session. + +BUILTIN_HANDLERS = [ + ('choose-service-name', handle_service_name_alias), + ( + 'getattr.mturk.list_hi_ts_for_qualification_type', + ClientMethodAlias('list_hits_for_qualification_type'), + ), + ( + 'getattr.socialmessaging.delete_whatsapp_media_message', + ClientMethodAlias('delete_whatsapp_message_media'), + ), + ( + 'getattr.cloudwatch.get_o_tel_enrichment', + ClientMethodAlias('get_otel_enrichment'), + ), + ( + 'getattr.cloudwatch.start_o_tel_enrichment', + ClientMethodAlias('start_otel_enrichment'), + ), + ( + 'getattr.cloudwatch.stop_o_tel_enrichment', + ClientMethodAlias('stop_otel_enrichment'), + ), + ( + 'getattr.cloudwatch.update_o_tel_enrichment', + ClientMethodAlias('update_otel_enrichment'), + ), + ( + 'getattr.signin.create_o_auth2_token', + ClientMethodAlias('create_oauth2_token'), + ), + ( + 'before-parameter-build.s3.UploadPart', + convert_body_to_file_like_object, + REGISTER_LAST, + ), + ( + 'before-parameter-build.s3.PutObject', + convert_body_to_file_like_object, + REGISTER_LAST, + ), + ('creating-client-class', add_generate_presigned_url), + ('creating-client-class.s3', add_generate_presigned_post), + ('creating-client-class.iot-data', check_openssl_supports_tls_version_1_2), + ('creating-client-class.lex-runtime-v2', remove_lex_v2_start_conversation), + ('creating-client-class.qbusiness', remove_qbusiness_chat), + ( + 'creating-client-class.bedrock-runtime', + remove_bedrock_runtime_invoke_model_with_bidirectional_stream, + ), + ( + 'creating-client-class.connecthealth', + remove_connecthealth_start_medical_scribe_listening_session, + ), + ( + 'creating-client-class.polly', + remove_polly_start_speech_synthesis_stream, + ), + ('after-call.iam', json_decode_policies), + ( + 'docs.*.iam.*.complete-section', + DocumentModifiedShape( + 'policyDocumentType', + new_type='dict', + new_description=None, + new_example_value='{}', + ).replace_documentation_for_matching_shape, + ), + ('after-call.ec2.GetConsoleOutput', decode_console_output), + ('after-call.cloudformation.GetTemplate', json_decode_template_body), + ('after-call.s3.GetBucketLocation', parse_get_bucket_location), + ( + 'after-call.sqs.*', + _handle_sqs_compatible_error, + ), + ('before-parse.s3.*', handle_expires_header), + ('before-parse.s3.*', _handle_200_error, REGISTER_FIRST), + ('before-parse.sso-oidc.*', _map_oauth2_errors), + ('before-parameter-build', generate_idempotent_uuid), + ('before-parameter-build', _handle_request_validation_mode_member), + ('before-parameter-build.s3', validate_bucket_name), + ('before-parameter-build.s3', remove_bucket_from_url_paths_from_model), + ( + 'before-parameter-build.s3.ListObjects', + set_list_objects_encoding_type_url, + ), + ( + 'before-parameter-build.s3.ListObjectsV2', + set_list_objects_encoding_type_url, + ), + ( + 'before-parameter-build.s3.ListObjectVersions', + set_list_objects_encoding_type_url, + ), + ('before-parameter-build.s3.CopyObject', handle_copy_source_param), + ('before-parameter-build.s3.UploadPartCopy', handle_copy_source_param), + ('before-parameter-build.s3.CopyObject', validate_ascii_metadata), + ('before-parameter-build.s3.PutObject', validate_ascii_metadata), + ( + 'before-parameter-build.s3.CreateMultipartUpload', + validate_ascii_metadata, + ), + ('before-parameter-build.s3-control', remove_accid_host_prefix_from_model), + ('docs.*.s3.CopyObject.complete-section', document_copy_source_form), + ('docs.*.s3.UploadPartCopy.complete-section', document_copy_source_form), + ('docs.response-example.s3.*.complete-section', document_expires_shape), + ('docs.response-params.s3.*.complete-section', document_expires_shape), + ('before-endpoint-resolution.s3', customize_endpoint_resolver_builtins), + ('before-call', add_recursion_detection_header), + ('before-call.s3', add_expect_header), + ('before-call.glacier', add_glacier_version), + ('before-call.apigateway', add_accept_header), + ('before-call.s3.DeleteObjects', escape_xml_payload), + ('before-call.s3.PutBucketLifecycleConfiguration', escape_xml_payload), + ('before-call.glacier.UploadArchive', add_glacier_checksums), + ('before-call.glacier.UploadMultipartPart', add_glacier_checksums), + ('before-call.ec2.CopySnapshot', inject_presigned_url_ec2), + ('request-created', add_retry_headers), + ('request-created.machinelearning.Predict', switch_host_machinelearning), + ('needs-retry.s3.*', _update_status_code, REGISTER_FIRST), + ('choose-signer.cognito-identity.GetId', disable_signing), + ('choose-signer.cognito-identity.GetOpenIdToken', disable_signing), + ('choose-signer.cognito-identity.UnlinkIdentity', disable_signing), + ( + 'choose-signer.cognito-identity.GetCredentialsForIdentity', + disable_signing, + ), + ('choose-signer.sts.AssumeRoleWithSAML', disable_signing), + ('choose-signer.sts.AssumeRoleWithWebIdentity', disable_signing), + ('choose-signer', set_operation_specific_signer), + ('choose-signer', _set_auth_scheme_preference_signer), + ('before-parameter-build.s3.HeadObject', sse_md5), + ('before-parameter-build.s3.GetObject', sse_md5), + ('before-parameter-build.s3.PutObject', sse_md5), + ('before-parameter-build.s3.CopyObject', sse_md5), + ('before-parameter-build.s3.CopyObject', copy_source_sse_md5), + ('before-parameter-build.s3.CreateMultipartUpload', sse_md5), + ('before-parameter-build.s3.UploadPart', sse_md5), + ('before-parameter-build.s3.UploadPartCopy', sse_md5), + ('before-parameter-build.s3.UploadPartCopy', copy_source_sse_md5), + ('before-parameter-build.s3.CompleteMultipartUpload', sse_md5), + ('before-parameter-build.s3.SelectObjectContent', sse_md5), + ('before-parameter-build.ec2.RunInstances', base64_encode_user_data), + ( + 'before-parameter-build.autoscaling.CreateLaunchConfiguration', + base64_encode_user_data, + ), + ('before-parameter-build.route53', fix_route53_ids), + ('before-parameter-build.glacier', inject_account_id), + ('before-sign.s3', remove_arn_from_signing_path), + ('before-sign.s3', _set_extra_headers_for_unsigned_request), + ( + 'before-sign.polly.SynthesizeSpeech', + remove_content_type_header_for_presigning, + ), + ('after-call.s3.ListObjects', decode_list_object), + ('after-call.s3.ListObjectsV2', decode_list_object_v2), + ('after-call.s3.ListObjectVersions', decode_list_object_versions), + # Cloudsearchdomain search operation will be sent by HTTP POST + ('request-created.cloudsearchdomain.Search', change_get_to_post), + # Glacier documentation customizations + ( + 'docs.*.glacier.*.complete-section', + AutoPopulatedParam( + 'accountId', + 'Note: this parameter is set to "-" by' + 'default if no value is not specified.', + ).document_auto_populated_param, + ), + ( + 'docs.*.glacier.UploadArchive.complete-section', + AutoPopulatedParam('checksum').document_auto_populated_param, + ), + ( + 'docs.*.glacier.UploadMultipartPart.complete-section', + AutoPopulatedParam('checksum').document_auto_populated_param, + ), + ( + 'docs.request-params.glacier.CompleteMultipartUpload.complete-section', + document_glacier_tree_hash_checksum(), + ), + # Cloudformation documentation customizations + ( + 'docs.*.cloudformation.GetTemplate.complete-section', + document_cloudformation_get_template_return_type, + ), + # UserData base64 encoding documentation customizations + ( + 'docs.*.ec2.RunInstances.complete-section', + document_base64_encoding('UserData'), + ), + ( + 'docs.*.autoscaling.CreateLaunchConfiguration.complete-section', + document_base64_encoding('UserData'), + ), + # EC2 CopySnapshot documentation customizations + ( + 'docs.*.ec2.CopySnapshot.complete-section', + AutoPopulatedParam('PresignedUrl').document_auto_populated_param, + ), + ( + 'docs.*.ec2.CopySnapshot.complete-section', + AutoPopulatedParam('DestinationRegion').document_auto_populated_param, + ), + # S3 SSE documentation modifications + ( + 'docs.*.s3.*.complete-section', + AutoPopulatedParam('SSECustomerKeyMD5').document_auto_populated_param, + ), + # S3 SSE Copy Source documentation modifications + ( + 'docs.*.s3.*.complete-section', + AutoPopulatedParam( + 'CopySourceSSECustomerKeyMD5' + ).document_auto_populated_param, + ), + # Add base64 information to Lambda + ( + 'docs.*.lambda.UpdateFunctionCode.complete-section', + document_base64_encoding('ZipFile'), + ), + # The following S3 operations cannot actually accept a ContentMD5 + ( + 'docs.*.s3.*.complete-section', + HideParamFromOperations( + 's3', + 'ContentMD5', + [ + 'DeleteObjects', + 'PutBucketAcl', + 'PutBucketCors', + 'PutBucketLifecycle', + 'PutBucketLogging', + 'PutBucketNotification', + 'PutBucketPolicy', + 'PutBucketReplication', + 'PutBucketRequestPayment', + 'PutBucketTagging', + 'PutBucketVersioning', + 'PutBucketWebsite', + 'PutObjectAcl', + ], + ).hide_param, + ), + ############# + # DSQL + ############# + ('creating-client-class.dsql', add_dsql_generate_db_auth_token_methods), + ############# + # RDS + ############# + ('creating-client-class.rds', add_generate_db_auth_token), + ('before-call.rds.CopyDBClusterSnapshot', inject_presigned_url_rds), + ('before-call.rds.CreateDBCluster', inject_presigned_url_rds), + ('before-call.rds.CopyDBSnapshot', inject_presigned_url_rds), + ('before-call.rds.CreateDBInstanceReadReplica', inject_presigned_url_rds), + ( + 'before-call.rds.StartDBInstanceAutomatedBackupsReplication', + inject_presigned_url_rds, + ), + # RDS PresignedUrl documentation customizations + ( + 'docs.*.rds.CopyDBClusterSnapshot.complete-section', + AutoPopulatedParam('PreSignedUrl').document_auto_populated_param, + ), + ( + 'docs.*.rds.CreateDBCluster.complete-section', + AutoPopulatedParam('PreSignedUrl').document_auto_populated_param, + ), + ( + 'docs.*.rds.CopyDBSnapshot.complete-section', + AutoPopulatedParam('PreSignedUrl').document_auto_populated_param, + ), + ( + 'docs.*.rds.CreateDBInstanceReadReplica.complete-section', + AutoPopulatedParam('PreSignedUrl').document_auto_populated_param, + ), + ( + 'docs.*.rds.StartDBInstanceAutomatedBackupsReplication.complete-section', + AutoPopulatedParam('PreSignedUrl').document_auto_populated_param, + ), + ############# + # Neptune + ############# + ('before-call.neptune.CopyDBClusterSnapshot', inject_presigned_url_rds), + ('before-call.neptune.CreateDBCluster', inject_presigned_url_rds), + # Neptune PresignedUrl documentation customizations + ( + 'docs.*.neptune.CopyDBClusterSnapshot.complete-section', + AutoPopulatedParam('PreSignedUrl').document_auto_populated_param, + ), + ( + 'docs.*.neptune.CreateDBCluster.complete-section', + AutoPopulatedParam('PreSignedUrl').document_auto_populated_param, + ), + ############# + # DocDB + ############# + ('before-call.docdb.CopyDBClusterSnapshot', inject_presigned_url_rds), + ('before-call.docdb.CreateDBCluster', inject_presigned_url_rds), + # DocDB PresignedUrl documentation customizations + ( + 'docs.*.docdb.CopyDBClusterSnapshot.complete-section', + AutoPopulatedParam('PreSignedUrl').document_auto_populated_param, + ), + ( + 'docs.*.docdb.CreateDBCluster.complete-section', + AutoPopulatedParam('PreSignedUrl').document_auto_populated_param, + ), + ('before-call', inject_api_version_header_if_needed), +] +_add_parameter_aliases(BUILTIN_HANDLERS) diff --git a/venv/lib/python3.12/site-packages/botocore/history.py b/venv/lib/python3.12/site-packages/botocore/history.py new file mode 100644 index 0000000..59d9481 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/history.py @@ -0,0 +1,55 @@ +# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import logging + +HISTORY_RECORDER = None +logger = logging.getLogger(__name__) + + +class BaseHistoryHandler: + def emit(self, event_type, payload, source): + raise NotImplementedError('emit()') + + +class HistoryRecorder: + def __init__(self): + self._enabled = False + self._handlers = [] + + def enable(self): + self._enabled = True + + def disable(self): + self._enabled = False + + def add_handler(self, handler): + self._handlers.append(handler) + + def record(self, event_type, payload, source='BOTOCORE'): + if self._enabled and self._handlers: + for handler in self._handlers: + try: + handler.emit(event_type, payload, source) + except Exception: + # Never let the process die because we had a failure in + # a record collection handler. + logger.debug( + "Exception raised in %s.", handler, exc_info=True + ) + + +def get_global_history_recorder(): + global HISTORY_RECORDER + if HISTORY_RECORDER is None: + HISTORY_RECORDER = HistoryRecorder() + return HISTORY_RECORDER diff --git a/venv/lib/python3.12/site-packages/botocore/hooks.py b/venv/lib/python3.12/site-packages/botocore/hooks.py new file mode 100644 index 0000000..c672b1c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/hooks.py @@ -0,0 +1,660 @@ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import copy +import logging +from collections import deque, namedtuple + +from botocore.compat import accepts_kwargs +from botocore.utils import EVENT_ALIASES + +logger = logging.getLogger(__name__) + + +_NodeList = namedtuple('NodeList', ['first', 'middle', 'last']) +_FIRST = 0 +_MIDDLE = 1 +_LAST = 2 + + +class NodeList(_NodeList): + def __copy__(self): + first_copy = copy.copy(self.first) + middle_copy = copy.copy(self.middle) + last_copy = copy.copy(self.last) + copied = NodeList(first_copy, middle_copy, last_copy) + return copied + + +def first_non_none_response(responses, default=None): + """Find first non None response in a list of tuples. + + This function can be used to find the first non None response from + handlers connected to an event. This is useful if you are interested + in the returned responses from event handlers. Example usage:: + + print(first_non_none_response([(func1, None), (func2, 'foo'), + (func3, 'bar')])) + # This will print 'foo' + + :type responses: list of tuples + :param responses: The responses from the ``EventHooks.emit`` method. + This is a list of tuples, and each tuple is + (handler, handler_response). + + :param default: If no non-None responses are found, then this default + value will be returned. + + :return: The first non-None response in the list of tuples. + + """ + for response in responses: + if response[1] is not None: + return response[1] + return default + + +class BaseEventHooks: + def emit(self, event_name, **kwargs): + """Call all handlers subscribed to an event. + + :type event_name: str + :param event_name: The name of the event to emit. + + :type **kwargs: dict + :param **kwargs: Arbitrary kwargs to pass through to the + subscribed handlers. The ``event_name`` will be injected + into the kwargs so it's not necessary to add this to **kwargs. + + :rtype: list of tuples + :return: A list of ``(handler_func, handler_func_return_value)`` + + """ + return [] + + def register( + self, event_name, handler, unique_id=None, unique_id_uses_count=False + ): + """Register an event handler for a given event. + + If a ``unique_id`` is given, the handler will not be registered + if a handler with the ``unique_id`` has already been registered. + + Handlers are called in the order they have been registered. + Note handlers can also be registered with ``register_first()`` + and ``register_last()``. All handlers registered with + ``register_first()`` are called before handlers registered + with ``register()`` which are called before handlers registered + with ``register_last()``. + + """ + self._verify_and_register( + event_name, + handler, + unique_id, + register_method=self._register, + unique_id_uses_count=unique_id_uses_count, + ) + + def register_first( + self, event_name, handler, unique_id=None, unique_id_uses_count=False + ): + """Register an event handler to be called first for an event. + + All event handlers registered with ``register_first()`` will + be called before handlers registered with ``register()`` and + ``register_last()``. + + """ + self._verify_and_register( + event_name, + handler, + unique_id, + register_method=self._register_first, + unique_id_uses_count=unique_id_uses_count, + ) + + def register_last( + self, event_name, handler, unique_id=None, unique_id_uses_count=False + ): + """Register an event handler to be called last for an event. + + All event handlers registered with ``register_last()`` will be called + after handlers registered with ``register_first()`` and ``register()``. + + """ + self._verify_and_register( + event_name, + handler, + unique_id, + register_method=self._register_last, + unique_id_uses_count=unique_id_uses_count, + ) + + def _verify_and_register( + self, + event_name, + handler, + unique_id, + register_method, + unique_id_uses_count, + ): + self._verify_is_callable(handler) + self._verify_accept_kwargs(handler) + register_method(event_name, handler, unique_id, unique_id_uses_count) + + def unregister( + self, + event_name, + handler=None, + unique_id=None, + unique_id_uses_count=False, + ): + """Unregister an event handler for a given event. + + If no ``unique_id`` was given during registration, then the + first instance of the event handler is removed (if the event + handler has been registered multiple times). + + """ + pass + + def _verify_is_callable(self, func): + if not callable(func): + raise ValueError(f"Event handler {func} must be callable.") + + def _verify_accept_kwargs(self, func): + """Verifies a callable accepts kwargs + + :type func: callable + :param func: A callable object. + + :returns: True, if ``func`` accepts kwargs, otherwise False. + + """ + try: + if not accepts_kwargs(func): + raise ValueError( + f"Event handler {func} must accept keyword " + f"arguments (**kwargs)" + ) + except TypeError: + return False + + +class HierarchicalEmitter(BaseEventHooks): + def __init__(self): + # We keep a reference to the handlers for quick + # read only access (we never modify self._handlers). + # A cache of event name to handler list. + self._lookup_cache = {} + self._handlers = _PrefixTrie() + # This is used to ensure that unique_id's are only + # registered once. + self._unique_id_handlers = {} + + def _emit(self, event_name, kwargs, stop_on_response=False): + """ + Emit an event with optional keyword arguments. + + :type event_name: string + :param event_name: Name of the event + :type kwargs: dict + :param kwargs: Arguments to be passed to the handler functions. + :type stop_on_response: boolean + :param stop_on_response: Whether to stop on the first non-None + response. If False, then all handlers + will be called. This is especially useful + to handlers which mutate data and then + want to stop propagation of the event. + :rtype: list + :return: List of (handler, response) tuples from all processed + handlers. + """ + responses = [] + # Invoke the event handlers from most specific + # to least specific, each time stripping off a dot. + handlers_to_call = self._lookup_cache.get(event_name) + if handlers_to_call is None: + handlers_to_call = self._handlers.prefix_search(event_name) + self._lookup_cache[event_name] = handlers_to_call + elif not handlers_to_call: + # Short circuit and return an empty response is we have + # no handlers to call. This is the common case where + # for the majority of signals, nothing is listening. + return [] + kwargs['event_name'] = event_name + responses = [] + for handler in handlers_to_call: + logger.debug('Event %s: calling handler %s', event_name, handler) + response = handler(**kwargs) + responses.append((handler, response)) + if stop_on_response and response is not None: + return responses + return responses + + def emit(self, event_name, **kwargs): + """ + Emit an event by name with arguments passed as keyword args. + + >>> responses = emitter.emit( + ... 'my-event.service.operation', arg1='one', arg2='two') + + :rtype: list + :return: List of (handler, response) tuples from all processed + handlers. + """ + return self._emit(event_name, kwargs) + + def emit_until_response(self, event_name, **kwargs): + """ + Emit an event by name with arguments passed as keyword args, + until the first non-``None`` response is received. This + method prevents subsequent handlers from being invoked. + + >>> handler, response = emitter.emit_until_response( + 'my-event.service.operation', arg1='one', arg2='two') + + :rtype: tuple + :return: The first (handler, response) tuple where the response + is not ``None``, otherwise (``None``, ``None``). + """ + responses = self._emit(event_name, kwargs, stop_on_response=True) + if responses: + return responses[-1] + else: + return (None, None) + + def _register( + self, event_name, handler, unique_id=None, unique_id_uses_count=False + ): + self._register_section( + event_name, + handler, + unique_id, + unique_id_uses_count, + section=_MIDDLE, + ) + + def _register_first( + self, event_name, handler, unique_id=None, unique_id_uses_count=False + ): + self._register_section( + event_name, + handler, + unique_id, + unique_id_uses_count, + section=_FIRST, + ) + + def _register_last( + self, event_name, handler, unique_id, unique_id_uses_count=False + ): + self._register_section( + event_name, handler, unique_id, unique_id_uses_count, section=_LAST + ) + + def _register_section( + self, event_name, handler, unique_id, unique_id_uses_count, section + ): + if unique_id is not None: + if unique_id in self._unique_id_handlers: + # We've already registered a handler using this unique_id + # so we don't need to register it again. + count = self._unique_id_handlers[unique_id].get('count', None) + if unique_id_uses_count: + if not count: + raise ValueError( + f"Initial registration of unique id {unique_id} was " + "specified to use a counter. Subsequent register " + "calls to unique id must specify use of a counter " + "as well." + ) + else: + self._unique_id_handlers[unique_id]['count'] += 1 + else: + if count: + raise ValueError( + f"Initial registration of unique id {unique_id} was " + "specified to not use a counter. Subsequent " + "register calls to unique id must specify not to " + "use a counter as well." + ) + return + else: + # Note that the trie knows nothing about the unique + # id. We track uniqueness in this class via the + # _unique_id_handlers. + self._handlers.append_item( + event_name, handler, section=section + ) + unique_id_handler_item = {'handler': handler} + if unique_id_uses_count: + unique_id_handler_item['count'] = 1 + self._unique_id_handlers[unique_id] = unique_id_handler_item + else: + self._handlers.append_item(event_name, handler, section=section) + # Super simple caching strategy for now, if we change the registrations + # clear the cache. This has the opportunity for smarter invalidations. + self._lookup_cache = {} + + def unregister( + self, + event_name, + handler=None, + unique_id=None, + unique_id_uses_count=False, + ): + if unique_id is not None: + try: + count = self._unique_id_handlers[unique_id].get('count', None) + except KeyError: + # There's no handler matching that unique_id so we have + # nothing to unregister. + return + if unique_id_uses_count: + if count is None: + raise ValueError( + f"Initial registration of unique id {unique_id} was specified to " + "use a counter. Subsequent unregister calls to unique " + "id must specify use of a counter as well." + ) + elif count == 1: + handler = self._unique_id_handlers.pop(unique_id)[ + 'handler' + ] + else: + self._unique_id_handlers[unique_id]['count'] -= 1 + return + else: + if count: + raise ValueError( + f"Initial registration of unique id {unique_id} was specified " + "to not use a counter. Subsequent unregister calls " + "to unique id must specify not to use a counter as " + "well." + ) + handler = self._unique_id_handlers.pop(unique_id)['handler'] + try: + self._handlers.remove_item(event_name, handler) + self._lookup_cache = {} + except ValueError: + pass + + def __copy__(self): + new_instance = self.__class__() + new_state = self.__dict__.copy() + new_state['_handlers'] = copy.copy(self._handlers) + new_state['_unique_id_handlers'] = copy.copy(self._unique_id_handlers) + new_instance.__dict__ = new_state + return new_instance + + +class EventAliaser(BaseEventHooks): + def __init__(self, event_emitter, event_aliases=None): + self._event_aliases = event_aliases + if event_aliases is None: + self._event_aliases = EVENT_ALIASES + self._alias_name_cache = {} + self._emitter = event_emitter + + def emit(self, event_name, **kwargs): + aliased_event_name = self._alias_event_name(event_name) + return self._emitter.emit(aliased_event_name, **kwargs) + + def emit_until_response(self, event_name, **kwargs): + aliased_event_name = self._alias_event_name(event_name) + return self._emitter.emit_until_response(aliased_event_name, **kwargs) + + def register( + self, event_name, handler, unique_id=None, unique_id_uses_count=False + ): + aliased_event_name = self._alias_event_name(event_name) + return self._emitter.register( + aliased_event_name, handler, unique_id, unique_id_uses_count + ) + + def register_first( + self, event_name, handler, unique_id=None, unique_id_uses_count=False + ): + aliased_event_name = self._alias_event_name(event_name) + return self._emitter.register_first( + aliased_event_name, handler, unique_id, unique_id_uses_count + ) + + def register_last( + self, event_name, handler, unique_id=None, unique_id_uses_count=False + ): + aliased_event_name = self._alias_event_name(event_name) + return self._emitter.register_last( + aliased_event_name, handler, unique_id, unique_id_uses_count + ) + + def unregister( + self, + event_name, + handler=None, + unique_id=None, + unique_id_uses_count=False, + ): + aliased_event_name = self._alias_event_name(event_name) + return self._emitter.unregister( + aliased_event_name, handler, unique_id, unique_id_uses_count + ) + + def _alias_event_name(self, event_name): + if event_name in self._alias_name_cache: + return self._alias_name_cache[event_name] + + for old_part, new_part in self._event_aliases.items(): + # We can't simply do a string replace for everything, otherwise we + # might end up translating substrings that we never intended to + # translate. When there aren't any dots in the old event name + # part, then we can quickly replace the item in the list if it's + # there. + event_parts = event_name.split('.') + if '.' not in old_part: + try: + # Theoretically a given event name could have the same part + # repeated, but in practice this doesn't happen + event_parts[event_parts.index(old_part)] = new_part + except ValueError: + continue + + # If there's dots in the name, it gets more complicated. Now we + # have to replace multiple sections of the original event. + elif old_part in event_name: + old_parts = old_part.split('.') + self._replace_subsection(event_parts, old_parts, new_part) + else: + continue + + new_name = '.'.join(event_parts) + logger.debug( + "Changing event name from %s to %s", event_name, new_name + ) + self._alias_name_cache[event_name] = new_name + return new_name + + self._alias_name_cache[event_name] = event_name + return event_name + + def _replace_subsection(self, sections, old_parts, new_part): + for i in range(len(sections)): + if ( + sections[i] == old_parts[0] + and sections[i : i + len(old_parts)] == old_parts + ): + sections[i : i + len(old_parts)] = [new_part] + return + + def __copy__(self): + return self.__class__( + copy.copy(self._emitter), copy.copy(self._event_aliases) + ) + + +class _PrefixTrie: + """Specialized prefix trie that handles wildcards. + + The prefixes in this case are based on dot separated + names so 'foo.bar.baz' is:: + + foo -> bar -> baz + + Wildcard support just means that having a key such as 'foo.bar.*.baz' will + be matched with a call to ``get_items(key='foo.bar.ANYTHING.baz')``. + + You can think of this prefix trie as the equivalent as defaultdict(list), + except that it can do prefix searches: + + foo.bar.baz -> A + foo.bar -> B + foo -> C + + Calling ``get_items('foo.bar.baz')`` will return [A + B + C], from + most specific to least specific. + + """ + + def __init__(self): + # Each dictionary can be though of as a node, where a node + # has values associated with the node, and children is a link + # to more nodes. So 'foo.bar' would have a 'foo' node with + # a 'bar' node as a child of foo. + # {'foo': {'children': {'bar': {...}}}}. + self._root = {'chunk': None, 'children': {}, 'values': None} + + def append_item(self, key, value, section=_MIDDLE): + """Add an item to a key. + + If a value is already associated with that key, the new + value is appended to the list for the key. + """ + key_parts = key.split('.') + current = self._root + for part in key_parts: + if part not in current['children']: + new_child = {'chunk': part, 'values': None, 'children': {}} + current['children'][part] = new_child + current = new_child + else: + current = current['children'][part] + if current['values'] is None: + current['values'] = NodeList([], [], []) + current['values'][section].append(value) + + def prefix_search(self, key): + """Collect all items that are prefixes of key. + + Prefix in this case are delineated by '.' characters so + 'foo.bar.baz' is a 3 chunk sequence of 3 "prefixes" ( + "foo", "bar", and "baz"). + + """ + collected = deque() + key_parts = key.split('.') + current = self._root + self._get_items(current, key_parts, collected, 0) + return collected + + def _get_items(self, starting_node, key_parts, collected, starting_index): + stack = [(starting_node, starting_index)] + key_parts_len = len(key_parts) + # Traverse down the nodes, where at each level we add the + # next part from key_parts as well as the wildcard element '*'. + # This means for each node we see we potentially add two more + # elements to our stack. + while stack: + current_node, index = stack.pop() + if current_node['values']: + # We're using extendleft because we want + # the values associated with the node furthest + # from the root to come before nodes closer + # to the root. extendleft() also adds its items + # in right-left order so .extendleft([1, 2, 3]) + # will result in final_list = [3, 2, 1], which is + # why we reverse the lists. + node_list = current_node['values'] + complete_order = ( + node_list.first + node_list.middle + node_list.last + ) + collected.extendleft(reversed(complete_order)) + if not index == key_parts_len: + children = current_node['children'] + directs = children.get(key_parts[index]) + wildcard = children.get('*') + next_index = index + 1 + if wildcard is not None: + stack.append((wildcard, next_index)) + if directs is not None: + stack.append((directs, next_index)) + + def remove_item(self, key, value): + """Remove an item associated with a key. + + If the value is not associated with the key a ``ValueError`` + will be raised. If the key does not exist in the trie, a + ``ValueError`` will be raised. + + """ + key_parts = key.split('.') + current = self._root + self._remove_item(current, key_parts, value, index=0) + + def _remove_item(self, current_node, key_parts, value, index): + if current_node is None: + return + elif index < len(key_parts): + next_node = current_node['children'].get(key_parts[index]) + if next_node is not None: + self._remove_item(next_node, key_parts, value, index + 1) + if index == len(key_parts) - 1: + node_list = next_node['values'] + if value in node_list.first: + node_list.first.remove(value) + elif value in node_list.middle: + node_list.middle.remove(value) + elif value in node_list.last: + node_list.last.remove(value) + if not next_node['children'] and not next_node['values']: + # Then this is a leaf node with no values so + # we can just delete this link from the parent node. + # This makes subsequent search faster in the case + # where a key does not exist. + del current_node['children'][key_parts[index]] + else: + raise ValueError(f"key is not in trie: {'.'.join(key_parts)}") + + def __copy__(self): + # The fact that we're using a nested dict under the covers + # is an implementation detail, and the user shouldn't have + # to know that they'd normally need a deepcopy so we expose + # __copy__ instead of __deepcopy__. + new_copy = self.__class__() + copied_attrs = self._recursive_copy(self.__dict__) + new_copy.__dict__ = copied_attrs + return new_copy + + def _recursive_copy(self, node): + # We can't use copy.deepcopy because we actually only want to copy + # the structure of the trie, not the handlers themselves. + # Each node has a chunk, children, and values. + copied_node = {} + for key, value in node.items(): + if isinstance(value, NodeList): + copied_node[key] = copy.copy(value) + elif isinstance(value, dict): + copied_node[key] = self._recursive_copy(value) + else: + copied_node[key] = value + return copied_node diff --git a/venv/lib/python3.12/site-packages/botocore/httpchecksum.py b/venv/lib/python3.12/site-packages/botocore/httpchecksum.py new file mode 100644 index 0000000..a868501 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/httpchecksum.py @@ -0,0 +1,677 @@ +# Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +"""The interfaces in this module are not intended for public use. + +This module defines interfaces for applying checksums to HTTP requests within +the context of botocore. This involves both resolving the checksum to be used +based on client configuration and environment, as well as application of the +checksum to the request. +""" + +import base64 +import io +import logging +from binascii import crc32 +from hashlib import sha1, sha256, sha512 + +from botocore.compat import HAS_CRT, has_minimum_crt_version, urlparse +from botocore.exceptions import ( + AwsChunkedWrapperError, + FlexibleChecksumError, + MissingDependencyException, +) +from botocore.model import StructureShape +from botocore.response import StreamingBody +from botocore.useragent import register_feature_id +from botocore.utils import ( + conditionally_calculate_md5, + determine_content_length, + get_checksum_algorithm_headers, + has_checksum_header, +) + +if HAS_CRT: + from awscrt import checksums as crt_checksums +else: + crt_checksums = None + +logger = logging.getLogger(__name__) + +DEFAULT_CHECKSUM_ALGORITHM = "CRC32" + + +class BaseChecksum: + _CHUNK_SIZE = 1024 * 1024 + + def update(self, chunk): + pass + + def digest(self): + pass + + def b64digest(self): + bs = self.digest() + return base64.b64encode(bs).decode("ascii") + + def _handle_fileobj(self, fileobj): + start_position = fileobj.tell() + for chunk in iter(lambda: fileobj.read(self._CHUNK_SIZE), b""): + self.update(chunk) + fileobj.seek(start_position) + + def handle(self, body): + if isinstance(body, (bytes, bytearray)): + self.update(body) + else: + self._handle_fileobj(body) + return self.b64digest() + + +class Crc32Checksum(BaseChecksum): + def __init__(self): + self._int_crc32 = 0 + + def update(self, chunk): + self._int_crc32 = crc32(chunk, self._int_crc32) & 0xFFFFFFFF + + def digest(self): + return self._int_crc32.to_bytes(4, byteorder="big") + + +class CrtCrc32Checksum(BaseChecksum): + # Note: This class is only used if the CRT is available + def __init__(self): + self._int_crc32 = 0 + + def update(self, chunk): + new_checksum = crt_checksums.crc32(chunk, self._int_crc32) + self._int_crc32 = new_checksum & 0xFFFFFFFF + + def digest(self): + return self._int_crc32.to_bytes(4, byteorder="big") + + +class CrtCrc32cChecksum(BaseChecksum): + # Note: This class is only used if the CRT is available + def __init__(self): + self._int_crc32c = 0 + + def update(self, chunk): + new_checksum = crt_checksums.crc32c(chunk, self._int_crc32c) + self._int_crc32c = new_checksum & 0xFFFFFFFF + + def digest(self): + return self._int_crc32c.to_bytes(4, byteorder="big") + + +class CrtCrc64NvmeChecksum(BaseChecksum): + # Note: This class is only used if the CRT is available + def __init__(self): + self._int_crc64nvme = 0 + + def update(self, chunk): + new_checksum = crt_checksums.crc64nvme(chunk, self._int_crc64nvme) + self._int_crc64nvme = new_checksum & 0xFFFFFFFFFFFFFFFF + + def digest(self): + return self._int_crc64nvme.to_bytes(8, byteorder="big") + + +class CrtXxhash64Checksum(BaseChecksum): + # Note: This class is only used if the CRT is available + def __init__(self): + self._xxhash = crt_checksums.XXHash.new_xxhash64() + + def update(self, chunk): + self._xxhash.update(chunk) + + def digest(self): + return self._xxhash.finalize() + + +class CrtXxhash3Checksum(BaseChecksum): + # Note: This class is only used if the CRT is available + def __init__(self): + self._xxhash = crt_checksums.XXHash.new_xxhash3_64() + + def update(self, chunk): + self._xxhash.update(chunk) + + def digest(self): + return self._xxhash.finalize() + + +class CrtXxhash128Checksum(BaseChecksum): + # Note: This class is only used if the CRT is available + def __init__(self): + self._xxhash = crt_checksums.XXHash.new_xxhash3_128() + + def update(self, chunk): + self._xxhash.update(chunk) + + def digest(self): + return self._xxhash.finalize() + + +class Sha1Checksum(BaseChecksum): + def __init__(self): + self._checksum = sha1() + + def update(self, chunk): + self._checksum.update(chunk) + + def digest(self): + return self._checksum.digest() + + +class Sha256Checksum(BaseChecksum): + def __init__(self): + self._checksum = sha256() + + def update(self, chunk): + self._checksum.update(chunk) + + def digest(self): + return self._checksum.digest() + + +class Sha512Checksum(BaseChecksum): + def __init__(self): + self._checksum = sha512() + + def update(self, chunk): + self._checksum.update(chunk) + + def digest(self): + return self._checksum.digest() + + +class AwsChunkedWrapper: + _DEFAULT_CHUNK_SIZE = 1024 * 1024 + + def __init__( + self, + raw, + checksum_cls=None, + checksum_name="x-amz-checksum", + chunk_size=None, + ): + self._raw = raw + self._checksum_name = checksum_name + self._checksum_cls = checksum_cls + self._reset() + + if chunk_size is None: + chunk_size = self._DEFAULT_CHUNK_SIZE + self._chunk_size = chunk_size + + def _reset(self): + self._remaining = b"" + self._complete = False + self._checksum = None + if self._checksum_cls: + self._checksum = self._checksum_cls() + + def seek(self, offset, whence=0): + if offset != 0 or whence != 0: + raise AwsChunkedWrapperError( + error_msg="Can only seek to start of stream" + ) + self._reset() + self._raw.seek(0) + + def read(self, size=None): + # Normalize "read all" size values to None + if size is not None and size <= 0: + size = None + + # If the underlying body is done and we have nothing left then + # end the stream + if self._complete and not self._remaining: + return b"" + + # While we're not done and want more bytes + want_more_bytes = size is None or size > len(self._remaining) + while not self._complete and want_more_bytes: + self._remaining += self._make_chunk() + want_more_bytes = size is None or size > len(self._remaining) + + # If size was None, we want to return everything + if size is None: + size = len(self._remaining) + + # Return a chunk up to the size asked for + to_return = self._remaining[:size] + self._remaining = self._remaining[size:] + return to_return + + def _make_chunk(self): + # NOTE: Chunk size is not deterministic as read could return less. This + # means we cannot know the content length of the encoded aws-chunked + # stream ahead of time without ensuring a consistent chunk size + raw_chunk = self._raw.read(self._chunk_size) + hex_len = hex(len(raw_chunk))[2:].encode("ascii") + self._complete = not raw_chunk + + if self._checksum: + self._checksum.update(raw_chunk) + + if self._checksum and self._complete: + name = self._checksum_name.encode("ascii") + checksum = self._checksum.b64digest().encode("ascii") + return b"0\r\n%s:%s\r\n\r\n" % (name, checksum) + + return b"%s\r\n%s\r\n" % (hex_len, raw_chunk) + + def __iter__(self): + while not self._complete: + yield self._make_chunk() + + +class StreamingChecksumBody(StreamingBody): + def __init__(self, raw_stream, content_length, checksum, expected): + super().__init__(raw_stream, content_length) + self._checksum = checksum + self._expected = expected + + @property + def checksum(self): + return self._checksum + + def read(self, amt=None): + chunk = super().read(amt=amt) + self._checksum.update(chunk) + if amt is None or (not chunk and amt > 0): + self._validate_checksum() + return chunk + + def readinto(self, b): + amount_read = super().readinto(b) + if amount_read == len(b): + view = b + else: + view = memoryview(b)[:amount_read] + self._checksum.update(view) + if amount_read == 0 and len(b) > 0: + self._validate_checksum() + return amount_read + + def _validate_checksum(self): + if self._expected is None: + return + if self._checksum.digest() != base64.b64decode(self._expected): + error_msg = ( + f"Expected checksum {self._expected} did not match calculated " + f"checksum: {self._checksum.b64digest()}" + ) + raise FlexibleChecksumError(error_msg=error_msg) + + +def resolve_checksum_context(request, operation_model, params): + resolve_request_checksum_algorithm(request, operation_model, params) + resolve_response_checksum_algorithms(request, operation_model, params) + _register_checksum_feature_ids(request) + + +def resolve_request_checksum_algorithm( + request, + operation_model, + params, + supported_algorithms=None, +): + # If the header is already set by the customer, skip calculation + if has_checksum_header(request): + return + + checksum_context = request["context"].get("checksum", {}) + request_checksum_calculation = request["context"][ + "client_config" + ].request_checksum_calculation + http_checksum = operation_model.http_checksum + request_checksum_required = ( + operation_model.http_checksum_required + or http_checksum.get("requestChecksumRequired") + ) + algorithm_member = http_checksum.get("requestAlgorithmMember") + if algorithm_member and algorithm_member in params: + # If the client has opted into using flexible checksums and the + # request supports it, use that instead of checksum required + if supported_algorithms is None: + supported_algorithms = _SUPPORTED_CHECKSUM_ALGORITHMS + + algorithm_name = params[algorithm_member].lower() + if algorithm_name not in supported_algorithms: + if not HAS_CRT and algorithm_name in _CRT_CHECKSUM_ALGORITHMS: + raise MissingDependencyException( + msg=( + f"Using {algorithm_name.upper()} requires an " + "additional dependency. You will need to pip install " + "botocore[crt] before proceeding." + ) + ) + raise FlexibleChecksumError( + error_msg=f"Unsupported checksum algorithm: {algorithm_name}" + ) + elif request_checksum_required or ( + algorithm_member and request_checksum_calculation == "when_supported" + ): + # Don't use a default checksum for presigned requests. + if request["context"].get("is_presign_request"): + return + algorithm_name = DEFAULT_CHECKSUM_ALGORITHM.lower() + algorithm_member_header = _get_request_algorithm_member_header( + operation_model, request, algorithm_member + ) + if algorithm_member_header is not None: + checksum_context["request_algorithm_header"] = { + "name": algorithm_member_header, + "value": DEFAULT_CHECKSUM_ALGORITHM, + } + else: + return + + location_type = "header" + if ( + operation_model.has_streaming_input + and urlparse(request["url"]).scheme == "https" + ): + if request["context"]["client_config"].signature_version != 's3': + # Operations with streaming input must support trailers. + # We only support unsigned trailer checksums currently. As this + # disables payload signing we'll only use trailers over TLS. + location_type = "trailer" + + algorithm = { + "algorithm": algorithm_name, + "in": location_type, + "name": f"x-amz-checksum-{algorithm_name}", + } + + checksum_context["request_algorithm"] = algorithm + request["context"]["checksum"] = checksum_context + + +def _get_request_algorithm_member_header( + operation_model, request, algorithm_member +): + """Get the name of the header targeted by the "requestAlgorithmMember".""" + operation_input_shape = operation_model.input_shape + if not isinstance(operation_input_shape, StructureShape): + return + + algorithm_member_shape = operation_input_shape.members.get( + algorithm_member + ) + + if algorithm_member_shape: + return algorithm_member_shape.serialization.get("name") + + +def apply_request_checksum(request): + checksum_context = request.get("context", {}).get("checksum", {}) + algorithm = checksum_context.get("request_algorithm") + + if not algorithm: + return + + if algorithm == "conditional-md5": + # Special case to handle the http checksum required trait + conditionally_calculate_md5(request) + elif algorithm["in"] == "header": + _apply_request_header_checksum(request) + elif algorithm["in"] == "trailer": + _apply_request_trailer_checksum(request) + else: + raise FlexibleChecksumError( + error_msg="Unknown checksum variant: {}".format(algorithm["in"]) + ) + if "request_algorithm_header" in checksum_context: + request_algorithm_header = checksum_context["request_algorithm_header"] + request["headers"][request_algorithm_header["name"]] = ( + request_algorithm_header["value"] + ) + + +def _apply_request_header_checksum(request): + checksum_context = request.get("context", {}).get("checksum", {}) + algorithm = checksum_context.get("request_algorithm") + location_name = algorithm["name"] + if location_name in request["headers"]: + # If the header is already set by the customer, skip calculation + return + checksum_cls = _CHECKSUM_CLS.get(algorithm["algorithm"]) + digest = checksum_cls().handle(request["body"]) + request["headers"][location_name] = digest + + +def _apply_request_trailer_checksum(request): + checksum_context = request.get("context", {}).get("checksum", {}) + algorithm = checksum_context.get("request_algorithm") + location_name = algorithm["name"] + checksum_cls = _CHECKSUM_CLS.get(algorithm["algorithm"]) + + headers = request["headers"] + body = request["body"] + + if location_name in headers: + # If the header is already set by the customer, skip calculation + return + + headers["Transfer-Encoding"] = "chunked" + if "Content-Encoding" in headers: + # We need to preserve the existing content encoding and add + # aws-chunked as a new content encoding. + headers["Content-Encoding"] += ",aws-chunked" + else: + headers["Content-Encoding"] = "aws-chunked" + headers["X-Amz-Trailer"] = location_name + + content_length = determine_content_length(body) + if content_length is None and "Content-Length" in headers: + # determine_content_length() cannot resolve the length of non-seekable + # bodies, but the caller may have set Content-Length explicitly. Reuse + # that value for X-Amz-Decoded-Content-Length before the header is + # removed for chunked transfer encoding. + content_length = int(headers["Content-Length"]) + if content_length is not None: + # Send the decoded content length if we can determine it. Some + # services such as S3 may require the decoded content length + headers["X-Amz-Decoded-Content-Length"] = str(content_length) + + if "Content-Length" in headers: + del headers["Content-Length"] + logger.debug( + "Removing the Content-Length header since 'chunked' is specified for Transfer-Encoding." + ) + + if isinstance(body, (bytes, bytearray)): + body = io.BytesIO(body) + + request["body"] = AwsChunkedWrapper( + body, + checksum_cls=checksum_cls, + checksum_name=location_name, + ) + + +def _register_checksum_feature_ids(request): + """Register feature IDs for checksum algorithms used in the request.""" + if algorithm_headers := get_checksum_algorithm_headers(request): + for header in algorithm_headers: + header = header.upper() + if header not in ( + "X-AMZ-CHECKSUM-ALGORITHM", + "X-AMZ-CHECKSUM-MODE", + "X-AMZ-CHECKSUM-TYPE", + ): + algorithm_name = header.removeprefix("X-AMZ-CHECKSUM-") + _register_checksum_algorithm_feature_id(algorithm_name) + return + # If no checksum header exists yet, check the resolved context for + # an algorithm that will be applied later by apply_request_checksum. + checksum_context = request.get("context", {}).get("checksum", {}) + algorithm = checksum_context.get("request_algorithm") + if algorithm and isinstance(algorithm, dict): + _register_checksum_algorithm_feature_id(algorithm["algorithm"]) + + +def _register_checksum_algorithm_feature_id(algorithm): + checksum_algorithm_name = algorithm.upper() + if checksum_algorithm_name == "CRC64NVME": + checksum_algorithm_name = "CRC64" + checksum_algorithm_name_feature_id = ( + f"FLEXIBLE_CHECKSUMS_REQ_{checksum_algorithm_name}" + ) + register_feature_id(checksum_algorithm_name_feature_id) + + +def resolve_response_checksum_algorithms( + request, operation_model, params, supported_algorithms=None +): + http_checksum = operation_model.http_checksum + mode_member = http_checksum.get("requestValidationModeMember") + if mode_member and mode_member in params: + if supported_algorithms is None: + supported_algorithms = _SUPPORTED_CHECKSUM_ALGORITHMS + response_algorithms = { + a.lower() for a in http_checksum.get("responseAlgorithms", []) + } + + usable_algorithms = [] + for algorithm in _ALGORITHMS_PRIORITY_LIST: + if algorithm not in response_algorithms: + continue + if algorithm in supported_algorithms: + usable_algorithms.append(algorithm) + + checksum_context = request["context"].get("checksum", {}) + checksum_context["response_algorithms"] = usable_algorithms + request["context"]["checksum"] = checksum_context + + +def handle_checksum_body(http_response, response, context, operation_model): + headers = response["headers"] + checksum_context = context.get("checksum", {}) + algorithms = checksum_context.get("response_algorithms") + + if not algorithms: + return + + for algorithm in algorithms: + header_name = f"x-amz-checksum-{algorithm}" + # If the header is not found, check the next algorithm + if header_name not in headers: + continue + + # If a - is in the checksum this is not valid Base64. S3 returns + # checksums that include a -# suffix to indicate a checksum derived + # from the hash of all part checksums. We cannot wrap this response + if "-" in headers[header_name]: + continue + + if operation_model.has_streaming_output: + response["body"] = _handle_streaming_response( + http_response, response, algorithm + ) + else: + response["body"] = _handle_bytes_response( + http_response, response, algorithm + ) + + # Expose metadata that the checksum check actually occurred + checksum_context = response["context"].get("checksum", {}) + checksum_context["response_algorithm"] = algorithm + response["context"]["checksum"] = checksum_context + return + + logger.debug( + 'Skipping checksum validation. Response did not contain one of the following algorithms: %s.', + algorithms, + ) + + +def _handle_streaming_response(http_response, response, algorithm): + checksum_cls = _CHECKSUM_CLS.get(algorithm) + header_name = f"x-amz-checksum-{algorithm}" + return StreamingChecksumBody( + http_response.raw, + response["headers"].get("content-length"), + checksum_cls(), + response["headers"][header_name], + ) + + +def _handle_bytes_response(http_response, response, algorithm): + body = http_response.content + header_name = f"x-amz-checksum-{algorithm}" + checksum_cls = _CHECKSUM_CLS.get(algorithm) + checksum = checksum_cls() + checksum.update(body) + expected = response["headers"][header_name] + if checksum.digest() != base64.b64decode(expected): + error_msg = ( + f"Expected checksum {expected} did not match calculated " + f"checksum: {checksum.b64digest()}" + ) + raise FlexibleChecksumError(error_msg=error_msg) + return body + + +_CHECKSUM_CLS = { + "crc32": Crc32Checksum, + "sha1": Sha1Checksum, + "sha256": Sha256Checksum, + "sha512": Sha512Checksum, +} +_CRT_CHECKSUM_ALGORITHMS = [ + "crc32", + "crc32c", + "crc64nvme", + "xxhash64", + "xxhash3", + "xxhash128", +] +if HAS_CRT: + # Use CRT checksum implementations if available + _CRT_CHECKSUM_CLS = { + "crc32": CrtCrc32Checksum, + "crc32c": CrtCrc32cChecksum, + } + + if has_minimum_crt_version((0, 23, 4)): + # CRC64NVME support wasn't officially added until 0.23.4 + _CRT_CHECKSUM_CLS["crc64nvme"] = CrtCrc64NvmeChecksum + + if has_minimum_crt_version((0, 31, 2)): + _CRT_CHECKSUM_CLS["xxhash64"] = CrtXxhash64Checksum + _CRT_CHECKSUM_CLS["xxhash3"] = CrtXxhash3Checksum + _CRT_CHECKSUM_CLS["xxhash128"] = CrtXxhash128Checksum + + _CHECKSUM_CLS.update(_CRT_CHECKSUM_CLS) + # Validate this list isn't out of sync with _CRT_CHECKSUM_ALGORITHMS keys + assert all( + name in _CRT_CHECKSUM_ALGORITHMS for name in _CRT_CHECKSUM_CLS.keys() + ) +_SUPPORTED_CHECKSUM_ALGORITHMS = list(_CHECKSUM_CLS.keys()) +_ALGORITHMS_PRIORITY_LIST = [ + 'xxhash128', + 'xxhash3', + 'crc64nvme', + 'xxhash64', + 'crc32c', + 'crc32', + 'sha1', + 'sha256', + 'sha512', +] diff --git a/venv/lib/python3.12/site-packages/botocore/httpsession.py b/venv/lib/python3.12/site-packages/botocore/httpsession.py new file mode 100644 index 0000000..0286e98 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/httpsession.py @@ -0,0 +1,557 @@ +import logging +import os +import os.path +import socket +import sys +import warnings +from base64 import b64encode +from concurrent.futures import CancelledError + +from urllib3 import PoolManager, Timeout, proxy_from_url +from urllib3.exceptions import ( + ConnectTimeoutError as URLLib3ConnectTimeoutError, +) +from urllib3.exceptions import ( + LocationParseError, + NewConnectionError, + ProtocolError, + ProxyError, +) +from urllib3.exceptions import ReadTimeoutError as URLLib3ReadTimeoutError +from urllib3.exceptions import SSLError as URLLib3SSLError +from urllib3.poolmanager import PoolKey +from urllib3.util.retry import Retry +from urllib3.util.ssl_ import ( + OP_NO_COMPRESSION, + PROTOCOL_TLS, + OP_NO_SSLv2, + OP_NO_SSLv3, + is_ipaddress, + ssl, +) +from urllib3.util.url import parse_url + +try: + from urllib3.util.ssl_ import OP_NO_TICKET, PROTOCOL_TLS_CLIENT +except ImportError: + # Fallback directly to ssl for version of urllib3 before 1.26. + # They are available in the standard library starting in Python 3.6. + from ssl import OP_NO_TICKET, PROTOCOL_TLS_CLIENT + +try: + # pyopenssl will be removed in urllib3 2.0, we'll fall back to ssl_ at that point. + # This can be removed once our urllib3 floor is raised to >= 2.0. + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=DeprecationWarning) + # Always import the original SSLContext, even if it has been patched + from urllib3.contrib.pyopenssl import ( + orig_util_SSLContext as SSLContext, + ) +except (AttributeError, ImportError): + from urllib3.util.ssl_ import SSLContext + +try: + from urllib3.util.ssl_ import DEFAULT_CIPHERS +except ImportError: + # Defer to system configuration starting with + # urllib3 2.0. This will choose the ciphers provided by + # Openssl 1.1.1+ or secure system defaults. + DEFAULT_CIPHERS = None + +import botocore.awsrequest +from botocore.compat import ( + IPV6_ADDRZ_RE, + ensure_bytes, + filter_ssl_warnings, + unquote, + urlparse, +) +from botocore.exceptions import ( + ConnectionClosedError, + ConnectTimeoutError, + EndpointConnectionError, + HTTPClientError, + InvalidProxiesConfigError, + ProxyConnectionError, + ReadTimeoutError, + SSLError, +) + +filter_ssl_warnings() +logger = logging.getLogger(__name__) +DEFAULT_TIMEOUT = 60 +MAX_POOL_CONNECTIONS = 10 +DEFAULT_CA_BUNDLE = os.path.join(os.path.dirname(__file__), 'cacert.pem') +BUFFER_SIZE = None +if hasattr(PoolKey, 'key_blocksize'): + # urllib3 2.0 implemented its own chunking logic and set + # a default blocksize of 16KB. This creates a noticeable + # performance bottleneck when transferring objects + # larger than 100MB. Based on experiments, a blocksize + # of 128KB significantly improves throughput before + # getting diminishing returns. + BUFFER_SIZE = 1024 * 128 + +try: + from certifi import where +except ImportError: + + def where(): + return DEFAULT_CA_BUNDLE + + +def get_cert_path(verify): + if verify is not True: + return verify + + cert_path = where() + logger.debug("Certificate path: %s", cert_path) + + return cert_path + + +def create_urllib3_context( + ssl_version=None, cert_reqs=None, options=None, ciphers=None +): + """This function is a vendored version of the same function in urllib3 + + We vendor this function to ensure that the SSL contexts we construct + always use the std lib SSLContext instead of pyopenssl. + """ + # PROTOCOL_TLS is deprecated in Python 3.10 + if not ssl_version or ssl_version == PROTOCOL_TLS: + ssl_version = PROTOCOL_TLS_CLIENT + + context = SSLContext(ssl_version) + + if ciphers: + context.set_ciphers(ciphers) + elif DEFAULT_CIPHERS: + context.set_ciphers(DEFAULT_CIPHERS) + + # Setting the default here, as we may have no ssl module on import + cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs + + if options is None: + options = 0 + # SSLv2 is easily broken and is considered harmful and dangerous + options |= OP_NO_SSLv2 + # SSLv3 has several problems and is now dangerous + options |= OP_NO_SSLv3 + # Disable compression to prevent CRIME attacks for OpenSSL 1.0+ + # (issue urllib3#309) + options |= OP_NO_COMPRESSION + # TLSv1.2 only. Unless set explicitly, do not request tickets. + # This may save some bandwidth on wire, and although the ticket is encrypted, + # there is a risk associated with it being on wire, + # if the server is not rotating its ticketing keys properly. + options |= OP_NO_TICKET + + context.options |= options + + # Enable post-handshake authentication for TLS 1.3, see GH #1634. PHA is + # necessary for conditional client cert authentication with TLS 1.3. + # The attribute is None for OpenSSL <= 1.1.0 or does not exist in older + # versions of Python. We only enable on Python 3.7.4+ or if certificate + # verification is enabled to work around Python issue #37428 + # See: https://bugs.python.org/issue37428 + if ( + cert_reqs == ssl.CERT_REQUIRED or sys.version_info >= (3, 7, 4) + ) and getattr(context, "post_handshake_auth", None) is not None: + context.post_handshake_auth = True + + def disable_check_hostname(): + if ( + getattr(context, "check_hostname", None) is not None + ): # Platform-specific: Python 3.2 + # We do our own verification, including fingerprints and alternative + # hostnames. So disable it here + context.check_hostname = False + + # The order of the below lines setting verify_mode and check_hostname + # matter due to safe-guards SSLContext has to prevent an SSLContext with + # check_hostname=True, verify_mode=NONE/OPTIONAL. This is made even more + # complex because we don't know whether PROTOCOL_TLS_CLIENT will be used + # or not so we don't know the initial state of the freshly created SSLContext. + if cert_reqs == ssl.CERT_REQUIRED: + context.verify_mode = cert_reqs + disable_check_hostname() + else: + disable_check_hostname() + context.verify_mode = cert_reqs + + # Enable logging of TLS session keys via defacto standard environment variable + # 'SSLKEYLOGFILE', if the feature is available (Python 3.8+). Skip empty values. + if hasattr(context, "keylog_filename"): + sslkeylogfile = os.environ.get("SSLKEYLOGFILE") + if sslkeylogfile and not sys.flags.ignore_environment: + context.keylog_filename = sslkeylogfile + + return context + + +def ensure_boolean(val): + """Ensures a boolean value if a string or boolean is provided + + For strings, the value for True/False is case insensitive + """ + if isinstance(val, bool): + return val + else: + return val.lower() == 'true' + + +def mask_proxy_url(proxy_url): + """ + Mask proxy url credentials. + + :type proxy_url: str + :param proxy_url: The proxy url, i.e. https://username:password@proxy.com + + :return: Masked proxy url, i.e. https://***:***@proxy.com + """ + mask = '*' * 3 + parsed_url = urlparse(proxy_url) + if parsed_url.username: + proxy_url = proxy_url.replace(parsed_url.username, mask, 1) + if parsed_url.password: + proxy_url = proxy_url.replace(parsed_url.password, mask, 1) + return proxy_url + + +def _is_ipaddress(host): + """Wrap urllib3's is_ipaddress to support bracketed IPv6 addresses.""" + return is_ipaddress(host) or bool(IPV6_ADDRZ_RE.match(host)) + + +class ProxyConfiguration: + """Represents a proxy configuration dictionary and additional settings. + + This class represents a proxy configuration dictionary and provides utility + functions to retrieve well structured proxy urls and proxy headers from the + proxy configuration dictionary. + """ + + def __init__(self, proxies=None, proxies_settings=None): + if proxies is None: + proxies = {} + if proxies_settings is None: + proxies_settings = {} + + self._proxies = proxies + self._proxies_settings = proxies_settings + + def proxy_url_for(self, url): + """Retrieves the corresponding proxy url for a given url.""" + parsed_url = urlparse(url) + proxy = self._proxies.get(parsed_url.scheme) + if proxy: + proxy = self._fix_proxy_url(proxy) + return proxy + + def proxy_headers_for(self, proxy_url): + """Retrieves the corresponding proxy headers for a given proxy url.""" + headers = {} + username, password = self._get_auth_from_url(proxy_url) + if username and password: + basic_auth = self._construct_basic_auth(username, password) + headers['Proxy-Authorization'] = basic_auth + return headers + + @property + def settings(self): + return self._proxies_settings + + def _fix_proxy_url(self, proxy_url): + if proxy_url.startswith('http:') or proxy_url.startswith('https:'): + return proxy_url + elif proxy_url.startswith('//'): + return 'http:' + proxy_url + else: + return 'http://' + proxy_url + + def _construct_basic_auth(self, username, password): + auth_str = f'{username}:{password}' + encoded_str = b64encode(auth_str.encode('ascii')).strip().decode() + return f'Basic {encoded_str}' + + def _get_auth_from_url(self, url): + parsed_url = urlparse(url) + try: + return unquote(parsed_url.username), unquote(parsed_url.password) + except (AttributeError, TypeError): + return None, None + + +class URLLib3Session: + """A basic HTTP client that supports connection pooling and proxies. + + This class is inspired by requests.adapters.HTTPAdapter, but has been + boiled down to meet the use cases needed by botocore. For the most part + this classes matches the functionality of HTTPAdapter in requests v2.7.0 + (the same as our vendored version). The only major difference of note is + that we currently do not support sending chunked requests. While requests + v2.7.0 implemented this themselves, later version urllib3 support this + directly via a flag to urlopen so enabling it if needed should be trivial. + """ + + def __init__( + self, + verify=True, + proxies=None, + timeout=None, + max_pool_connections=MAX_POOL_CONNECTIONS, + socket_options=None, + client_cert=None, + proxies_config=None, + ): + self._verify = verify + self._proxy_config = ProxyConfiguration( + proxies=proxies, proxies_settings=proxies_config + ) + self._pool_classes_by_scheme = { + 'http': botocore.awsrequest.AWSHTTPConnectionPool, + 'https': botocore.awsrequest.AWSHTTPSConnectionPool, + } + if timeout is None: + timeout = DEFAULT_TIMEOUT + if not isinstance(timeout, (int, float)): + timeout = Timeout(connect=timeout[0], read=timeout[1]) + + self._cert_file = None + self._key_file = None + if isinstance(client_cert, str): + self._cert_file = client_cert + elif isinstance(client_cert, tuple): + self._cert_file, self._key_file = client_cert + + self._timeout = timeout + self._max_pool_connections = max_pool_connections + self._socket_options = socket_options + if socket_options is None: + self._socket_options = [] + self._proxy_managers = {} + self._manager = PoolManager(**self._get_pool_manager_kwargs()) + self._manager.pool_classes_by_scheme = self._pool_classes_by_scheme + + def _proxies_kwargs(self, **kwargs): + proxies_settings = self._proxy_config.settings + proxies_kwargs = { + 'use_forwarding_for_https': proxies_settings.get( + 'proxy_use_forwarding_for_https' + ), + **kwargs, + } + return {k: v for k, v in proxies_kwargs.items() if v is not None} + + def _get_pool_manager_kwargs(self, **extra_kwargs): + pool_manager_kwargs = { + 'timeout': self._timeout, + 'maxsize': self._max_pool_connections, + 'ssl_context': self._get_ssl_context(), + 'socket_options': self._socket_options, + 'cert_file': self._cert_file, + 'key_file': self._key_file, + } + if BUFFER_SIZE: + pool_manager_kwargs['blocksize'] = BUFFER_SIZE + pool_manager_kwargs.update(**extra_kwargs) + return pool_manager_kwargs + + def _get_ssl_context(self): + return create_urllib3_context() + + def _get_proxy_manager(self, proxy_url): + if proxy_url not in self._proxy_managers: + proxy_headers = self._proxy_config.proxy_headers_for(proxy_url) + proxy_ssl_context = self._setup_proxy_ssl_context(proxy_url) + proxy_manager_kwargs = self._get_pool_manager_kwargs( + proxy_headers=proxy_headers + ) + proxy_manager_kwargs.update( + self._proxies_kwargs(proxy_ssl_context=proxy_ssl_context) + ) + proxy_manager = proxy_from_url(proxy_url, **proxy_manager_kwargs) + proxy_manager.pool_classes_by_scheme = self._pool_classes_by_scheme + self._proxy_managers[proxy_url] = proxy_manager + + return self._proxy_managers[proxy_url] + + def _path_url(self, url): + parsed_url = urlparse(url) + path = parsed_url.path + if not path: + path = '/' + if parsed_url.query: + path = path + '?' + parsed_url.query + return path + + def _setup_ssl_cert(self, conn, url, verify): + if url.lower().startswith('https') and verify: + conn.cert_reqs = 'CERT_REQUIRED' + conn.ca_certs = get_cert_path(verify) + else: + conn.cert_reqs = 'CERT_NONE' + conn.ca_certs = None + + def _setup_proxy_ssl_context(self, proxy_url): + proxies_settings = self._proxy_config.settings + proxy_ca_bundle = proxies_settings.get('proxy_ca_bundle') + proxy_cert = proxies_settings.get('proxy_client_cert') + if proxy_ca_bundle is None and proxy_cert is None: + return None + + context = self._get_ssl_context() + try: + url = parse_url(proxy_url) + # urllib3 disables this by default but we need it for proper + # proxy tls negotiation when proxy_url is not an IP Address + if not _is_ipaddress(url.host): + context.check_hostname = True + if proxy_ca_bundle is not None: + context.load_verify_locations(cafile=proxy_ca_bundle) + + if isinstance(proxy_cert, tuple): + context.load_cert_chain(proxy_cert[0], keyfile=proxy_cert[1]) + elif isinstance(proxy_cert, str): + context.load_cert_chain(proxy_cert) + + return context + except (OSError, URLLib3SSLError, LocationParseError) as e: + raise InvalidProxiesConfigError(error=e) + + def _get_connection_manager(self, url, proxy_url=None): + if proxy_url: + manager = self._get_proxy_manager(proxy_url) + else: + manager = self._manager + return manager + + def _get_request_target(self, url, proxy_url): + has_proxy = proxy_url is not None + + if not has_proxy: + return self._path_url(url) + + # HTTP proxies expect the request_target to be the absolute url to know + # which host to establish a connection to. urllib3 also supports + # forwarding for HTTPS through the 'use_forwarding_for_https' parameter. + proxy_scheme = urlparse(proxy_url).scheme + using_https_forwarding_proxy = ( + proxy_scheme == 'https' + and self._proxies_kwargs().get('use_forwarding_for_https', False) + ) + + if using_https_forwarding_proxy or url.startswith('http:'): + return url + else: + return self._path_url(url) + + def _chunked(self, headers): + transfer_encoding = headers.get('Transfer-Encoding', b'') + transfer_encoding = ensure_bytes(transfer_encoding) + return transfer_encoding.lower() == b'chunked' + + def _get_request_timeout(self, request): + """Resolve a per-request timeout override from the request context. + + Returns a ``urllib3.Timeout`` when the request context contains a + ``read_timeout`` override, otherwise ``None`` to defer to the + connection pool's default timeout which is configured through client + config. + + """ + # We could use `request.context` directly, but we're being + # intentionally cautious here. `context` is the first addition to + # AWSPreparedRequest in many years, and it's possible we might + # see an AWSPreparedRequest that doesn't have the `context` attribute. + context = getattr(request, 'context', None) + if context is None: + return None + read_timeout = context.get('read_timeout') + if read_timeout is None: + return None + if isinstance(self._timeout, Timeout): + connect_timeout = self._timeout.connect_timeout + else: + connect_timeout = self._timeout + return Timeout(connect=connect_timeout, read=read_timeout) + + def close(self): + self._manager.clear() + for manager in self._proxy_managers.values(): + manager.clear() + + def send(self, request): + try: + proxy_url = self._proxy_config.proxy_url_for(request.url) + manager = self._get_connection_manager(request.url, proxy_url) + conn = manager.connection_from_url(request.url) + self._setup_ssl_cert(conn, request.url, self._verify) + if ensure_boolean( + os.environ.get('BOTO_EXPERIMENTAL__ADD_PROXY_HOST_HEADER', '') + ): + # This is currently an "experimental" feature which provides + # no guarantees of backwards compatibility. It may be subject + # to change or removal in any patch version. Anyone opting in + # to this feature should strictly pin botocore. + host = urlparse(request.url).hostname + conn.proxy_headers['host'] = host + + request_target = self._get_request_target(request.url, proxy_url) + extra_kwargs = {} + request_timeout = self._get_request_timeout(request) + if request_timeout is not None: + # Only include the timeout kwarg when overridden; passing + # timeout=None to urlopen would disable timeouts entirely + # rather than defer to the pool's default timeout. + extra_kwargs['timeout'] = request_timeout + urllib_response = conn.urlopen( + method=request.method, + url=request_target, + body=request.body, + headers=request.headers, + retries=Retry(False), + assert_same_host=False, + preload_content=False, + decode_content=False, + chunked=self._chunked(request.headers), + **extra_kwargs, + ) + + http_response = botocore.awsrequest.AWSResponse( + request.url, + urllib_response.status, + urllib_response.headers, + urllib_response, + ) + + if not request.stream_output: + # Cause the raw stream to be exhausted immediately. We do it + # this way instead of using preload_content because + # preload_content will never buffer chunked responses + http_response.content + + return http_response + except URLLib3SSLError as e: + raise SSLError(endpoint_url=request.url, error=e) + except (NewConnectionError, socket.gaierror) as e: + raise EndpointConnectionError(endpoint_url=request.url, error=e) + except ProxyError as e: + raise ProxyConnectionError( + proxy_url=mask_proxy_url(proxy_url), error=e + ) + except URLLib3ConnectTimeoutError as e: + raise ConnectTimeoutError(endpoint_url=request.url, error=e) + except URLLib3ReadTimeoutError as e: + raise ReadTimeoutError(endpoint_url=request.url, error=e) + except ProtocolError as e: + raise ConnectionClosedError( + error=e, request=request, endpoint_url=request.url + ) + except CancelledError: + raise + except Exception as e: + message = 'Exception received when sending urllib3 HTTP request' + logger.debug(message, exc_info=True) + raise HTTPClientError(error=e) diff --git a/venv/lib/python3.12/site-packages/botocore/loaders.py b/venv/lib/python3.12/site-packages/botocore/loaders.py new file mode 100644 index 0000000..0714b1a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/loaders.py @@ -0,0 +1,525 @@ +# Copyright 2012-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Module for loading various model files. + +This module provides the classes that are used to load models used +by botocore. This can include: + + * Service models (e.g. the model for EC2, S3, DynamoDB, etc.) + * Service model extras which customize the service models + * Other models associated with a service (pagination, waiters) + * Non service-specific config (Endpoint data, retry config) + +Loading a module is broken down into several steps: + + * Determining the path to load + * Search the data_path for files to load + * The mechanics of loading the file + * Searching for extras and applying them to the loaded file + +The last item is used so that other faster loading mechanism +besides the default JSON loader can be used. + +The Search Path +=============== + +Similar to how the PATH environment variable is to finding executables +and the PYTHONPATH environment variable is to finding python modules +to import, the botocore loaders have the concept of a data path exposed +through AWS_DATA_PATH. + +This enables end users to provide additional search paths where we +will attempt to load models outside of the models we ship with +botocore. When you create a ``Loader``, there are two paths +automatically added to the model search path: + + * /data/ + * ~/.aws/models + +The first value is the path where all the model files shipped with +botocore are located. + +The second path is so that users can just drop new model files in +``~/.aws/models`` without having to mess around with the AWS_DATA_PATH. + +The AWS_DATA_PATH using the platform specific path separator to +separate entries (typically ``:`` on linux and ``;`` on windows). + + +Directory Layout +================ + +The Loader expects a particular directory layout. In order for any +directory specified in AWS_DATA_PATH to be considered, it must have +this structure for service models:: + + + | + |-- servicename1 + | |-- 2012-10-25 + | |-- service-2.json + |-- ec2 + | |-- 2014-01-01 + | | |-- paginators-1.json + | | |-- service-2.json + | | |-- waiters-2.json + | |-- 2015-03-01 + | |-- paginators-1.json + | |-- service-2.json + | |-- waiters-2.json + | |-- service-2.sdk-extras.json + + +That is: + + * The root directory contains sub directories that are the name + of the services. + * Within each service directory, there's a sub directory for each + available API version. + * Within each API version, there are model specific files, including + (but not limited to): service-2.json, waiters-2.json, paginators-1.json + +The ``-1`` and ``-2`` suffix at the end of the model files denote which version +schema is used within the model. Even though this information is available in +the ``version`` key within the model, this version is also part of the filename +so that code does not need to load the JSON model in order to determine which +version to use. + +The ``sdk-extras`` and similar files represent extra data that needs to be +applied to the model after it is loaded. Data in these files might represent +information that doesn't quite fit in the original models, but is still needed +for the sdk. For instance, additional operation parameters might be added here +which don't represent the actual service api. +""" + +import logging +import os + +from botocore import BOTOCORE_ROOT +from botocore.compat import HAS_GZIP, OrderedDict, json +from botocore.exceptions import DataNotFoundError, UnknownServiceError +from botocore.utils import deep_merge + +_JSON_OPEN_METHODS = { + '.json': open, +} + + +if HAS_GZIP: + from gzip import open as gzip_open + + _JSON_OPEN_METHODS['.json.gz'] = gzip_open + + +logger = logging.getLogger(__name__) + + +def instance_cache(func): + """Cache the result of a method on a per instance basis. + + This is not a general purpose caching decorator. In order + for this to be used, it must be used on methods on an + instance, and that instance *must* provide a + ``self._cache`` dictionary. + + """ + + def _wrapper(self, *args, **kwargs): + key = (func.__name__,) + args + for pair in sorted(kwargs.items()): + key += pair + if key in self._cache: + return self._cache[key] + data = func(self, *args, **kwargs) + self._cache[key] = data + return data + + return _wrapper + + +class JSONFileLoader: + """Loader JSON files. + + This class can load the default format of models, which is a JSON file. + + """ + + def exists(self, file_path): + """Checks if the file exists. + + :type file_path: str + :param file_path: The full path to the file to load without + the '.json' extension. + + :return: True if file path exists, False otherwise. + + """ + for ext in _JSON_OPEN_METHODS: + if os.path.isfile(file_path + ext): + return True + return False + + def _load_file(self, full_path, open_method): + if not os.path.isfile(full_path): + return + + # By default the file will be opened with locale encoding on Python 3. + # We specify "utf8" here to ensure the correct behavior. + with open_method(full_path, 'rb') as fp: + payload = fp.read().decode('utf-8') + + logger.debug("Loading JSON file: %s", full_path) + return json.loads(payload, object_pairs_hook=OrderedDict) + + def load_file(self, file_path): + """Attempt to load the file path. + + :type file_path: str + :param file_path: The full path to the file to load without + the '.json' extension. + + :return: The loaded data if it exists, otherwise None. + + """ + for ext, open_method in _JSON_OPEN_METHODS.items(): + data = self._load_file(file_path + ext, open_method) + if data is not None: + return data + return None + + +def create_loader(search_path_string=None): + """Create a Loader class. + + This factory function creates a loader given a search string path. + + :type search_string_path: str + :param search_string_path: The AWS_DATA_PATH value. A string + of data path values separated by the ``os.path.pathsep`` value, + which is typically ``:`` on POSIX platforms and ``;`` on + windows. + + :return: A ``Loader`` instance. + + """ + if search_path_string is None: + return Loader() + paths = [] + extra_paths = search_path_string.split(os.pathsep) + for path in extra_paths: + path = os.path.expanduser(os.path.expandvars(path)) + paths.append(path) + return Loader(extra_search_paths=paths) + + +class Loader: + """Find and load data models. + + This class will handle searching for and loading data models. + + The main method used here is ``load_service_model``, which is a + convenience method over ``load_data`` and ``determine_latest_version``. + + """ + + FILE_LOADER_CLASS = JSONFileLoader + # The included models in botocore/data/ that we ship with botocore. + BUILTIN_DATA_PATH = os.path.join(BOTOCORE_ROOT, 'data') + # For convenience we automatically add ~/.aws/models to the data path. + CUSTOMER_DATA_PATH = os.path.join( + os.path.expanduser('~'), '.aws', 'models' + ) + BUILTIN_EXTRAS_TYPES = ['sdk'] + + def __init__( + self, + extra_search_paths=None, + file_loader=None, + cache=None, + include_default_search_paths=True, + include_default_extras=True, + ): + self._cache = {} + if file_loader is None: + file_loader = self.FILE_LOADER_CLASS() + self.file_loader = file_loader + if extra_search_paths is not None: + self._search_paths = extra_search_paths + else: + self._search_paths = [] + if include_default_search_paths: + self._search_paths.extend( + [self.CUSTOMER_DATA_PATH, self.BUILTIN_DATA_PATH] + ) + + self._extras_types = [] + if include_default_extras: + self._extras_types.extend(self.BUILTIN_EXTRAS_TYPES) + + self._extras_processor = ExtrasProcessor() + + @property + def search_paths(self): + return self._search_paths + + @property + def extras_types(self): + return self._extras_types + + @instance_cache + def list_available_services(self, type_name): + """List all known services. + + This will traverse the search path and look for all known + services. + + :type type_name: str + :param type_name: The type of the service (service-2, + paginators-1, waiters-2, etc). This is needed because + the list of available services depends on the service + type. For example, the latest API version available for + a resource-1.json file may not be the latest API version + available for a services-2.json file. + + :return: A list of all services. The list of services will + be sorted. + + """ + services = set() + for possible_path in self._potential_locations(): + # Any directory in the search path is potentially a service. + # We'll collect any initial list of potential services, + # but we'll then need to further process these directories + # by searching for the corresponding type_name in each + # potential directory. + possible_services = [ + d + for d in os.listdir(possible_path) + if os.path.isdir(os.path.join(possible_path, d)) + ] + for service_name in possible_services: + full_dirname = os.path.join(possible_path, service_name) + api_versions = os.listdir(full_dirname) + for api_version in api_versions: + full_load_path = os.path.join( + full_dirname, api_version, type_name + ) + if self.file_loader.exists(full_load_path): + services.add(service_name) + break + return sorted(services) + + @instance_cache + def determine_latest_version(self, service_name, type_name): + """Find the latest API version available for a service. + + :type service_name: str + :param service_name: The name of the service. + + :type type_name: str + :param type_name: The type of the service (service-2, + paginators-1, waiters-2, etc). This is needed because + the latest API version available can depend on the service + type. For example, the latest API version available for + a resource-1.json file may not be the latest API version + available for a services-2.json file. + + :rtype: str + :return: The latest API version. If the service does not exist + or does not have any available API data, then a + ``DataNotFoundError`` exception will be raised. + + """ + return max(self.list_api_versions(service_name, type_name)) + + @instance_cache + def list_api_versions(self, service_name, type_name): + """List all API versions available for a particular service type + + :type service_name: str + :param service_name: The name of the service + + :type type_name: str + :param type_name: The type name for the service (i.e service-2, + paginators-1, etc.) + + :rtype: list + :return: A list of API version strings in sorted order. + + """ + known_api_versions = set() + for possible_path in self._potential_locations( + service_name, must_exist=True, is_dir=True + ): + for dirname in os.listdir(possible_path): + full_path = os.path.join(possible_path, dirname, type_name) + # Only add to the known_api_versions if the directory + # contains a service-2, paginators-1, etc. file corresponding + # to the type_name passed in. + if self.file_loader.exists(full_path): + known_api_versions.add(dirname) + if not known_api_versions: + raise DataNotFoundError(data_path=service_name) + return sorted(known_api_versions) + + @instance_cache + def load_service_model(self, service_name, type_name, api_version=None): + """Load a botocore service model + + This is the main method for loading botocore models (e.g. a service + model, pagination configs, waiter configs, etc.). + + :type service_name: str + :param service_name: The name of the service (e.g ``ec2``, ``s3``). + + :type type_name: str + :param type_name: The model type. Valid types include, but are not + limited to: ``service-2``, ``paginators-1``, ``waiters-2``. + + :type api_version: str + :param api_version: The API version to load. If this is not + provided, then the latest API version will be used. + + :type load_extras: bool + :param load_extras: Whether or not to load the tool extras which + contain additional data to be added to the model. + + :raises: UnknownServiceError if there is no known service with + the provided service_name. + + :raises: DataNotFoundError if no data could be found for the + service_name/type_name/api_version. + + :return: The loaded data, as a python type (e.g. dict, list, etc). + """ + # Wrapper around the load_data. This will calculate the path + # to call load_data with. + known_services = self.list_available_services(type_name) + if service_name not in known_services: + raise UnknownServiceError( + service_name=service_name, + known_service_names=', '.join(known_services), + ) + if api_version is None: + api_version = self.determine_latest_version( + service_name, type_name + ) + full_path = os.path.join(service_name, api_version, type_name) + model = self.load_data(full_path) + + # Load in all the extras + extras_data = self._find_extras(service_name, type_name, api_version) + self._extras_processor.process(model, extras_data) + + return model + + def _find_extras(self, service_name, type_name, api_version): + """Creates an iterator over all the extras data.""" + for extras_type in self.extras_types: + extras_name = f'{type_name}.{extras_type}-extras' + full_path = os.path.join(service_name, api_version, extras_name) + + try: + yield self.load_data(full_path) + except DataNotFoundError: + pass + + @instance_cache + def load_data_with_path(self, name): + """Same as ``load_data`` but returns file path as second return value. + + :type name: str + :param name: The data path, i.e ``ec2/2015-03-01/service-2``. + + :return: Tuple of the loaded data and the path to the data file + where the data was loaded from. If no data could be found then a + DataNotFoundError is raised. + """ + for possible_path in self._potential_locations(name): + found = self.file_loader.load_file(possible_path) + if found is not None: + return found, possible_path + + # We didn't find anything that matched on any path. + raise DataNotFoundError(data_path=name) + + def load_data(self, name): + """Load data given a data path. + + This is a low level method that will search through the various + search paths until it's able to load a value. This is typically + only needed to load *non* model files (such as _endpoints and + _retry). If you need to load model files, you should prefer + ``load_service_model``. Use ``load_data_with_path`` to get the + data path of the data file as second return value. + + :type name: str + :param name: The data path, i.e ``ec2/2015-03-01/service-2``. + + :return: The loaded data. If no data could be found then + a DataNotFoundError is raised. + """ + data, _ = self.load_data_with_path(name) + return data + + def _potential_locations(self, name=None, must_exist=False, is_dir=False): + # Will give an iterator over the full path of potential locations + # according to the search path. + for path in self.search_paths: + if os.path.isdir(path): + full_path = path + if name is not None: + full_path = os.path.join(path, name) + if not must_exist: + yield full_path + else: + if is_dir and os.path.isdir(full_path): + yield full_path + elif os.path.exists(full_path): + yield full_path + + def is_builtin_path(self, path): + """Whether a given path is within the package's data directory. + + This method can be used together with load_data_with_path(name) + to determine if data has been loaded from a file bundled with the + package, as opposed to a file in a separate location. + + :type path: str + :param path: The file path to check. + + :return: Whether the given path is within the package's data directory. + """ + path = os.path.expanduser(os.path.expandvars(path)) + return path.startswith(self.BUILTIN_DATA_PATH) + + +class ExtrasProcessor: + """Processes data from extras files into service models.""" + + def process(self, original_model, extra_models): + """Processes data from a list of loaded extras files into a model + + :type original_model: dict + :param original_model: The service model to load all the extras into. + + :type extra_models: iterable of dict + :param extra_models: A list of loaded extras models. + """ + for extras in extra_models: + self._process(original_model, extras) + + def _process(self, model, extra_model): + """Process a single extras model into a service model.""" + if 'merge' in extra_model: + deep_merge(model, extra_model['merge']) diff --git a/venv/lib/python3.12/site-packages/botocore/model.py b/venv/lib/python3.12/site-packages/botocore/model.py new file mode 100644 index 0000000..4cd62b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/model.py @@ -0,0 +1,1006 @@ +# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Abstractions to interact with service models.""" + +from collections import defaultdict +from typing import NamedTuple + +from botocore.auth import resolve_auth_type +from botocore.compat import OrderedDict +from botocore.exceptions import ( + MissingServiceIdError, + UndefinedModelAttributeError, + UnsupportedServiceProtocolsError, +) +from botocore.utils import ( + PRIORITY_ORDERED_SUPPORTED_PROTOCOLS, + CachedProperty, + hyphenize_service_id, + instance_cache, +) + +NOT_SET = object() + + +class NoShapeFoundError(Exception): + pass + + +class InvalidShapeError(Exception): + pass + + +class OperationNotFoundError(Exception): + pass + + +class InvalidShapeReferenceError(Exception): + pass + + +class ServiceId(str): + def hyphenize(self): + return hyphenize_service_id(self) + + +class Shape: + """Object representing a shape from the service model.""" + + # To simplify serialization logic, all shape params that are + # related to serialization are moved from the top level hash into + # a 'serialization' hash. This list below contains the names of all + # the attributes that should be moved. + SERIALIZED_ATTRS = [ + 'locationName', + 'queryName', + 'flattened', + 'location', + 'payload', + 'streaming', + 'timestampFormat', + 'xmlNamespace', + 'resultWrapper', + 'xmlAttribute', + 'eventstream', + 'event', + 'eventheader', + 'eventpayload', + 'jsonvalue', + 'timestampFormat', + 'hostLabel', + ] + METADATA_ATTRS = [ + 'required', + 'min', + 'max', + 'pattern', + 'sensitive', + 'enum', + 'idempotencyToken', + 'error', + 'exception', + 'endpointdiscoveryid', + 'retryable', + 'document', + 'union', + 'contextParam', + 'clientContextParams', + 'requiresLength', + ] + MAP_TYPE = OrderedDict + + def __init__(self, shape_name, shape_model, shape_resolver=None): + """ + + :type shape_name: string + :param shape_name: The name of the shape. + + :type shape_model: dict + :param shape_model: The shape model. This would be the value + associated with the key in the "shapes" dict of the + service model (i.e ``model['shapes'][shape_name]``) + + :type shape_resolver: botocore.model.ShapeResolver + :param shape_resolver: A shape resolver object. This is used to + resolve references to other shapes. For scalar shape types + (string, integer, boolean, etc.), this argument is not + required. If a shape_resolver is not provided for a complex + type, then a ``ValueError`` will be raised when an attempt + to resolve a shape is made. + + """ + self.name = shape_name + self.type_name = shape_model['type'] + self.documentation = shape_model.get('documentation', '') + self._shape_model = shape_model + if shape_resolver is None: + # If a shape_resolver is not provided, we create an object + # that will throw errors if you attempt to resolve + # a shape. This is actually ok for scalar shapes + # because they don't need to resolve shapes and shouldn't + # be required to provide an object they won't use. + shape_resolver = UnresolvableShapeMap() + self._shape_resolver = shape_resolver + self._cache = {} + + @CachedProperty + def serialization(self): + """Serialization information about the shape. + + This contains information that may be needed for input serialization + or response parsing. This can include: + + * name + * queryName + * flattened + * location + * payload + * streaming + * xmlNamespace + * resultWrapper + * xmlAttribute + * jsonvalue + * timestampFormat + + :rtype: dict + :return: Serialization information about the shape. + + """ + model = self._shape_model + serialization = {} + for attr in self.SERIALIZED_ATTRS: + if attr in self._shape_model: + serialization[attr] = model[attr] + # For consistency, locationName is renamed to just 'name'. + if 'locationName' in serialization: + serialization['name'] = serialization.pop('locationName') + return serialization + + @CachedProperty + def metadata(self): + """Metadata about the shape. + + This requires optional information about the shape, including: + + * min + * max + * pattern + * enum + * sensitive + * required + * idempotencyToken + * document + * union + * contextParam + * clientContextParams + * requiresLength + + :rtype: dict + :return: Metadata about the shape. + + """ + model = self._shape_model + metadata = {} + for attr in self.METADATA_ATTRS: + if attr in self._shape_model: + metadata[attr] = model[attr] + return metadata + + @CachedProperty + def required_members(self): + """A list of members that are required. + + A structure shape can define members that are required. + This value will return a list of required members. If there + are no required members an empty list is returned. + + """ + return self.metadata.get('required', []) + + def _resolve_shape_ref(self, shape_ref): + return self._shape_resolver.resolve_shape_ref(shape_ref) + + def __repr__(self): + return f"<{self.__class__.__name__}({self.name})>" + + @property + def event_stream_name(self): + return None + + +class StructureShape(Shape): + @CachedProperty + def members(self): + members = self._shape_model.get('members', self.MAP_TYPE()) + # The members dict looks like: + # 'members': { + # 'MemberName': {'shape': 'shapeName'}, + # 'MemberName2': {'shape': 'shapeName'}, + # } + # We return a dict of member name to Shape object. + shape_members = self.MAP_TYPE() + for name, shape_ref in members.items(): + shape_members[name] = self._resolve_shape_ref(shape_ref) + return shape_members + + @CachedProperty + def event_stream_name(self): + for member_name, member in self.members.items(): + if member.serialization.get('eventstream'): + return member_name + return None + + @CachedProperty + def error_code(self): + if not self.metadata.get('exception', False): + return None + error_metadata = self.metadata.get("error", {}) + code = error_metadata.get("code") + if code: + return code + # Use the exception name if there is no explicit code modeled + return self.name + + @CachedProperty + def is_document_type(self): + return self.metadata.get('document', False) + + @CachedProperty + def is_tagged_union(self): + return self.metadata.get('union', False) + + +class ListShape(Shape): + @CachedProperty + def member(self): + return self._resolve_shape_ref(self._shape_model['member']) + + +class MapShape(Shape): + @CachedProperty + def key(self): + return self._resolve_shape_ref(self._shape_model['key']) + + @CachedProperty + def value(self): + return self._resolve_shape_ref(self._shape_model['value']) + + +class StringShape(Shape): + @CachedProperty + def enum(self): + return self.metadata.get('enum', []) + + +class StaticContextParameter(NamedTuple): + name: str + value: bool | str + + +class ContextParameter(NamedTuple): + name: str + member_name: str + + +class ClientContextParameter(NamedTuple): + name: str + type: str + documentation: str + + +class ServiceModel: + """ + + :ivar service_description: The parsed service description dictionary. + + """ + + def __init__(self, service_description, service_name=None): + """ + + :type service_description: dict + :param service_description: The service description model. This value + is obtained from a botocore.loader.Loader, or from directly loading + the file yourself:: + + service_description = json.load( + open('/path/to/service-description-model.json')) + model = ServiceModel(service_description) + + :type service_name: str + :param service_name: The name of the service. Normally this is + the endpoint prefix defined in the service_description. However, + you can override this value to provide a more convenient name. + This is done in a few places in botocore (ses instead of email, + emr instead of elasticmapreduce). If this value is not provided, + it will default to the endpointPrefix defined in the model. + + """ + self._service_description = service_description + # We want clients to be able to access metadata directly. + self.metadata = service_description.get('metadata', {}) + self._shape_resolver = ShapeResolver( + service_description.get('shapes', {}) + ) + self._signature_version = NOT_SET + self._service_name = service_name + self._instance_cache = {} + + def shape_for(self, shape_name, member_traits=None): + return self._shape_resolver.get_shape_by_name( + shape_name, member_traits + ) + + def shape_for_error_code(self, error_code): + return self._error_code_cache.get(error_code, None) + + @CachedProperty + def _error_code_cache(self): + error_code_cache = {} + for error_shape in self.error_shapes: + code = error_shape.error_code + error_code_cache[code] = error_shape + return error_code_cache + + def resolve_shape_ref(self, shape_ref): + return self._shape_resolver.resolve_shape_ref(shape_ref) + + @CachedProperty + def shape_names(self): + return list(self._service_description.get('shapes', {})) + + @CachedProperty + def error_shapes(self): + error_shapes = [] + for shape_name in self.shape_names: + error_shape = self.shape_for(shape_name) + if error_shape.metadata.get('exception', False): + error_shapes.append(error_shape) + return error_shapes + + @instance_cache + def operation_model(self, operation_name): + try: + model = self._service_description['operations'][operation_name] + except KeyError: + raise OperationNotFoundError(operation_name) + return OperationModel(model, self, operation_name) + + @CachedProperty + def documentation(self): + return self._service_description.get('documentation', '') + + @CachedProperty + def operation_names(self): + return list(self._service_description.get('operations', [])) + + @CachedProperty + def service_name(self): + """The name of the service. + + This defaults to the endpointPrefix defined in the service model. + However, this value can be overriden when a ``ServiceModel`` is + created. If a service_name was not provided when the ``ServiceModel`` + was created and if there is no endpointPrefix defined in the + service model, then an ``UndefinedModelAttributeError`` exception + will be raised. + + """ + if self._service_name is not None: + return self._service_name + else: + return self.endpoint_prefix + + @CachedProperty + def service_id(self): + try: + return ServiceId(self._get_metadata_property('serviceId')) + except UndefinedModelAttributeError: + raise MissingServiceIdError(service_name=self._service_name) + + @CachedProperty + def signing_name(self): + """The name to use when computing signatures. + + If the model does not define a signing name, this + value will be the endpoint prefix defined in the model. + """ + signing_name = self.metadata.get('signingName') + if signing_name is None: + signing_name = self.endpoint_prefix + return signing_name + + @CachedProperty + def api_version(self): + return self._get_metadata_property('apiVersion') + + @CachedProperty + def protocol(self): + return self._get_metadata_property('protocol') + + @CachedProperty + def protocols(self): + return self._get_metadata_property('protocols') + + @CachedProperty + def resolved_protocol(self): + # We need to ensure `protocols` exists in the metadata before attempting to + # access it directly since referencing service_model.protocols directly will + # raise an UndefinedModelAttributeError if protocols is not defined + if self.metadata.get('protocols'): + for protocol in PRIORITY_ORDERED_SUPPORTED_PROTOCOLS: + if protocol in self.protocols: + return protocol + raise UnsupportedServiceProtocolsError( + botocore_supported_protocols=PRIORITY_ORDERED_SUPPORTED_PROTOCOLS, + service_supported_protocols=self.protocols, + service=self.service_name, + ) + # If a service does not have a `protocols` trait, fall back to the legacy + # `protocol` trait + return self.protocol + + @CachedProperty + def endpoint_prefix(self): + return self._get_metadata_property('endpointPrefix') + + @CachedProperty + def endpoint_discovery_operation(self): + for operation in self.operation_names: + model = self.operation_model(operation) + if model.is_endpoint_discovery_operation: + return model + + @CachedProperty + def endpoint_discovery_required(self): + for operation in self.operation_names: + model = self.operation_model(operation) + if ( + model.endpoint_discovery is not None + and model.endpoint_discovery.get('required') + ): + return True + return False + + @CachedProperty + def client_context_parameters(self): + params = self._service_description.get('clientContextParams', {}) + return [ + ClientContextParameter( + name=param_name, + type=param_val['type'], + documentation=param_val['documentation'], + ) + for param_name, param_val in params.items() + ] + + def _get_metadata_property(self, name): + try: + return self.metadata[name] + except KeyError: + raise UndefinedModelAttributeError( + f'"{name}" not defined in the metadata of the model: {self}' + ) + + # Signature version is one of the rare properties + # that can be modified so a CachedProperty is not used here. + + @property + def signature_version(self): + if self._signature_version is NOT_SET: + signature_version = self.metadata.get('signatureVersion') + self._signature_version = signature_version + return self._signature_version + + @signature_version.setter + def signature_version(self, value): + self._signature_version = value + + @CachedProperty + def is_query_compatible(self): + return 'awsQueryCompatible' in self.metadata + + def __repr__(self): + return f'{self.__class__.__name__}({self.service_name})' + + +class OperationModel: + def __init__(self, operation_model, service_model, name=None): + """ + + :type operation_model: dict + :param operation_model: The operation model. This comes from the + service model, and is the value associated with the operation + name in the service model (i.e ``model['operations'][op_name]``). + + :type service_model: botocore.model.ServiceModel + :param service_model: The service model associated with the operation. + + :type name: string + :param name: The operation name. This is the operation name exposed to + the users of this model. This can potentially be different from + the "wire_name", which is the operation name that *must* by + provided over the wire. For example, given:: + + "CreateCloudFrontOriginAccessIdentity":{ + "name":"CreateCloudFrontOriginAccessIdentity2014_11_06", + ... + } + + The ``name`` would be ``CreateCloudFrontOriginAccessIdentity``, + but the ``self.wire_name`` would be + ``CreateCloudFrontOriginAccessIdentity2014_11_06``, which is the + value we must send in the corresponding HTTP request. + + """ + self._operation_model = operation_model + self._service_model = service_model + self._api_name = name + # Clients can access '.name' to get the operation name + # and '.metadata' to get the top level metdata of the service. + self._wire_name = operation_model.get('name') + self.metadata = service_model.metadata + self.http = operation_model.get('http', {}) + + @CachedProperty + def name(self): + if self._api_name is not None: + return self._api_name + else: + return self.wire_name + + @property + def wire_name(self): + """The wire name of the operation. + + In many situations this is the same value as the + ``name``, value, but in some services, the operation name + exposed to the user is different from the operation name + we send across the wire (e.g cloudfront). + + Any serialization code should use ``wire_name``. + + """ + return self._operation_model.get('name') + + @property + def service_model(self): + return self._service_model + + @CachedProperty + def documentation(self): + return self._operation_model.get('documentation', '') + + @CachedProperty + def deprecated(self): + return self._operation_model.get('deprecated', False) + + @CachedProperty + def endpoint_discovery(self): + # Explicit None default. An empty dictionary for this trait means it is + # enabled but not required to be used. + return self._operation_model.get('endpointdiscovery', None) + + @CachedProperty + def is_endpoint_discovery_operation(self): + return self._operation_model.get('endpointoperation', False) + + @CachedProperty + def input_shape(self): + if 'input' not in self._operation_model: + # Some operations do not accept any input and do not define an + # input shape. + return None + return self._service_model.resolve_shape_ref( + self._operation_model['input'] + ) + + @CachedProperty + def output_shape(self): + if 'output' not in self._operation_model: + # Some operations do not define an output shape, + # in which case we return None to indicate the + # operation has no expected output. + return None + return self._service_model.resolve_shape_ref( + self._operation_model['output'] + ) + + @CachedProperty + def idempotent_members(self): + input_shape = self.input_shape + if not input_shape: + return [] + + return [ + name + for (name, shape) in input_shape.members.items() + if 'idempotencyToken' in shape.metadata + and shape.metadata['idempotencyToken'] + ] + + @CachedProperty + def static_context_parameters(self): + params = self._operation_model.get('staticContextParams', {}) + return [ + StaticContextParameter(name=name, value=props.get('value')) + for name, props in params.items() + ] + + @CachedProperty + def context_parameters(self): + if not self.input_shape: + return [] + + return [ + ContextParameter( + name=shape.metadata['contextParam']['name'], + member_name=name, + ) + for name, shape in self.input_shape.members.items() + if 'contextParam' in shape.metadata + and 'name' in shape.metadata['contextParam'] + ] + + @CachedProperty + def operation_context_parameters(self): + return self._operation_model.get('operationContextParams', []) + + @CachedProperty + def request_compression(self): + return self._operation_model.get('requestcompression') + + @CachedProperty + def auth(self): + return self._operation_model.get('auth') + + @CachedProperty + def auth_type(self): + return self._operation_model.get('authtype') + + @CachedProperty + def resolved_auth_type(self): + if self.auth: + return resolve_auth_type(self.auth) + return self.auth_type + + @CachedProperty + def unsigned_payload(self): + return self._operation_model.get('unsignedPayload') + + @CachedProperty + def error_shapes(self): + shapes = self._operation_model.get("errors", []) + return list(self._service_model.resolve_shape_ref(s) for s in shapes) + + @CachedProperty + def endpoint(self): + return self._operation_model.get('endpoint') + + @CachedProperty + def http_checksum_required(self): + return self._operation_model.get('httpChecksumRequired', False) + + @CachedProperty + def http_checksum(self): + return self._operation_model.get('httpChecksum', {}) + + @CachedProperty + def has_event_stream_input(self): + return self.get_event_stream_input() is not None + + @CachedProperty + def has_event_stream_output(self): + return self.get_event_stream_output() is not None + + def get_event_stream_input(self): + return self._get_event_stream(self.input_shape) + + def get_event_stream_output(self): + return self._get_event_stream(self.output_shape) + + def _get_event_stream(self, shape): + """Returns the event stream member's shape if any or None otherwise.""" + if shape is None: + return None + event_name = shape.event_stream_name + if event_name: + return shape.members[event_name] + return None + + @CachedProperty + def has_streaming_input(self): + return self.get_streaming_input() is not None + + @CachedProperty + def has_streaming_output(self): + return self.get_streaming_output() is not None + + def get_streaming_input(self): + return self._get_streaming_body(self.input_shape) + + def get_streaming_output(self): + return self._get_streaming_body(self.output_shape) + + def _get_streaming_body(self, shape): + """Returns the streaming member's shape if any; or None otherwise.""" + if shape is None: + return None + payload = shape.serialization.get('payload') + if payload is not None: + payload_shape = shape.members[payload] + if payload_shape.type_name == 'blob': + return payload_shape + return None + + def __repr__(self): + return f'{self.__class__.__name__}(name={self.name})' + + +class ShapeResolver: + """Resolves shape references.""" + + # Any type not in this mapping will default to the Shape class. + SHAPE_CLASSES = { + 'structure': StructureShape, + 'list': ListShape, + 'map': MapShape, + 'string': StringShape, + } + + def __init__(self, shape_map): + self._shape_map = shape_map + self._shape_cache = {} + + def get_shape_by_name(self, shape_name, member_traits=None): + try: + shape_model = self._shape_map[shape_name] + except KeyError: + raise NoShapeFoundError(shape_name) + try: + shape_cls = self.SHAPE_CLASSES.get(shape_model['type'], Shape) + except KeyError: + raise InvalidShapeError( + f"Shape is missing required key 'type': {shape_model}" + ) + if member_traits: + shape_model = shape_model.copy() + shape_model.update(member_traits) + result = shape_cls(shape_name, shape_model, self) + return result + + def resolve_shape_ref(self, shape_ref): + # A shape_ref is a dict that has a 'shape' key that + # refers to a shape name as well as any additional + # member traits that are then merged over the shape + # definition. For example: + # {"shape": "StringType", "locationName": "Foobar"} + if len(shape_ref) == 1 and 'shape' in shape_ref: + # It's just a shape ref with no member traits, we can avoid + # a .copy(). This is the common case so it's specifically + # called out here. + return self.get_shape_by_name(shape_ref['shape']) + else: + member_traits = shape_ref.copy() + try: + shape_name = member_traits.pop('shape') + except KeyError: + raise InvalidShapeReferenceError( + f"Invalid model, missing shape reference: {shape_ref}" + ) + return self.get_shape_by_name(shape_name, member_traits) + + +class UnresolvableShapeMap: + """A ShapeResolver that will throw ValueErrors when shapes are resolved.""" + + def get_shape_by_name(self, shape_name, member_traits=None): + raise ValueError( + f"Attempted to lookup shape '{shape_name}', but no shape map was provided." + ) + + def resolve_shape_ref(self, shape_ref): + raise ValueError( + f"Attempted to resolve shape '{shape_ref}', but no shape " + f"map was provided." + ) + + +class DenormalizedStructureBuilder: + """Build a StructureShape from a denormalized model. + + This is a convenience builder class that makes it easy to construct + ``StructureShape``s based on a denormalized model. + + It will handle the details of creating unique shape names and creating + the appropriate shape map needed by the ``StructureShape`` class. + + Example usage:: + + builder = DenormalizedStructureBuilder() + shape = builder.with_members({ + 'A': { + 'type': 'structure', + 'members': { + 'B': { + 'type': 'structure', + 'members': { + 'C': { + 'type': 'string', + } + } + } + } + } + }).build_model() + # ``shape`` is now an instance of botocore.model.StructureShape + + :type dict_type: class + :param dict_type: The dictionary type to use, allowing you to opt-in + to using OrderedDict or another dict type. This can + be particularly useful for testing when order + matters, such as for documentation. + + """ + + SCALAR_TYPES = ( + 'string', + 'integer', + 'boolean', + 'blob', + 'float', + 'timestamp', + 'long', + 'double', + 'char', + ) + + def __init__(self, name=None): + self.members = OrderedDict() + self._name_generator = ShapeNameGenerator() + if name is None: + self.name = self._name_generator.new_shape_name('structure') + + def with_members(self, members): + """ + + :type members: dict + :param members: The denormalized members. + + :return: self + + """ + self._members = members + return self + + def build_model(self): + """Build the model based on the provided members. + + :rtype: botocore.model.StructureShape + :return: The built StructureShape object. + + """ + shapes = OrderedDict() + denormalized = { + 'type': 'structure', + 'members': self._members, + } + self._build_model(denormalized, shapes, self.name) + resolver = ShapeResolver(shape_map=shapes) + return StructureShape( + shape_name=self.name, + shape_model=shapes[self.name], + shape_resolver=resolver, + ) + + def _build_model(self, model, shapes, shape_name): + if model['type'] == 'structure': + shapes[shape_name] = self._build_structure(model, shapes) + elif model['type'] == 'list': + shapes[shape_name] = self._build_list(model, shapes) + elif model['type'] == 'map': + shapes[shape_name] = self._build_map(model, shapes) + elif model['type'] in self.SCALAR_TYPES: + shapes[shape_name] = self._build_scalar(model) + else: + raise InvalidShapeError(f"Unknown shape type: {model['type']}") + + def _build_structure(self, model, shapes): + members = OrderedDict() + shape = self._build_initial_shape(model) + shape['members'] = members + + for name, member_model in model.get('members', OrderedDict()).items(): + member_shape_name = self._get_shape_name(member_model) + members[name] = {'shape': member_shape_name} + self._build_model(member_model, shapes, member_shape_name) + return shape + + def _build_list(self, model, shapes): + member_shape_name = self._get_shape_name(model) + shape = self._build_initial_shape(model) + shape['member'] = {'shape': member_shape_name} + self._build_model(model['member'], shapes, member_shape_name) + return shape + + def _build_map(self, model, shapes): + key_shape_name = self._get_shape_name(model['key']) + value_shape_name = self._get_shape_name(model['value']) + shape = self._build_initial_shape(model) + shape['key'] = {'shape': key_shape_name} + shape['value'] = {'shape': value_shape_name} + self._build_model(model['key'], shapes, key_shape_name) + self._build_model(model['value'], shapes, value_shape_name) + return shape + + def _build_initial_shape(self, model): + shape = { + 'type': model['type'], + } + if 'documentation' in model: + shape['documentation'] = model['documentation'] + for attr in Shape.METADATA_ATTRS: + if attr in model: + shape[attr] = model[attr] + return shape + + def _build_scalar(self, model): + return self._build_initial_shape(model) + + def _get_shape_name(self, model): + if 'shape_name' in model: + return model['shape_name'] + else: + return self._name_generator.new_shape_name(model['type']) + + +class ShapeNameGenerator: + """Generate unique shape names for a type. + + This class can be used in conjunction with the DenormalizedStructureBuilder + to generate unique shape names for a given type. + + """ + + def __init__(self): + self._name_cache = defaultdict(int) + + def new_shape_name(self, type_name): + """Generate a unique shape name. + + This method will guarantee a unique shape name each time it is + called with the same type. + + :: + + >>> s = ShapeNameGenerator() + >>> s.new_shape_name('structure') + 'StructureType1' + >>> s.new_shape_name('structure') + 'StructureType2' + >>> s.new_shape_name('list') + 'ListType1' + >>> s.new_shape_name('list') + 'ListType2' + + + :type type_name: string + :param type_name: The type name (structure, list, map, string, etc.) + + :rtype: string + :return: A unique shape name for the given type + + """ + self._name_cache[type_name] += 1 + current_index = self._name_cache[type_name] + return f'{type_name.capitalize()}Type{current_index}' diff --git a/venv/lib/python3.12/site-packages/botocore/monitoring.py b/venv/lib/python3.12/site-packages/botocore/monitoring.py new file mode 100644 index 0000000..d57cf0a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/monitoring.py @@ -0,0 +1,586 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import json +import logging +import re +import time + +from botocore.compat import ensure_bytes, ensure_unicode, urlparse +from botocore.retryhandler import EXCEPTION_MAP as RETRYABLE_EXCEPTIONS + +logger = logging.getLogger(__name__) + + +class Monitor: + _EVENTS_TO_REGISTER = [ + 'before-parameter-build', + 'request-created', + 'response-received', + 'after-call', + 'after-call-error', + ] + + def __init__(self, adapter, publisher): + """Abstraction for monitoring clients API calls + + :param adapter: An adapter that takes event emitter events + and produces monitor events + + :param publisher: A publisher for generated monitor events + """ + self._adapter = adapter + self._publisher = publisher + + def register(self, event_emitter): + """Register an event emitter to the monitor""" + for event_to_register in self._EVENTS_TO_REGISTER: + event_emitter.register_last(event_to_register, self.capture) + + def capture(self, event_name, **payload): + """Captures an incoming event from the event emitter + + It will feed an event emitter event to the monitor's adaptor to create + a monitor event and then publish that event to the monitor's publisher. + """ + try: + monitor_event = self._adapter.feed(event_name, payload) + if monitor_event: + self._publisher.publish(monitor_event) + except Exception as e: + logger.debug( + 'Exception %s raised by client monitor in handling event %s', + e, + event_name, + exc_info=True, + ) + + +class MonitorEventAdapter: + def __init__(self, time=time.time): + """Adapts event emitter events to produce monitor events + + :type time: callable + :param time: A callable that produces the current time + """ + self._time = time + + def feed(self, emitter_event_name, emitter_payload): + """Feed an event emitter event to generate a monitor event + + :type emitter_event_name: str + :param emitter_event_name: The name of the event emitted + + :type emitter_payload: dict + :param emitter_payload: The payload to associated to the event + emitted + + :rtype: BaseMonitorEvent + :returns: A monitor event based on the event emitter events + fired + """ + return self._get_handler(emitter_event_name)(**emitter_payload) + + def _get_handler(self, event_name): + return getattr( + self, '_handle_' + event_name.split('.')[0].replace('-', '_') + ) + + def _handle_before_parameter_build(self, model, context, **kwargs): + context['current_api_call_event'] = APICallEvent( + service=model.service_model.service_id, + operation=model.wire_name, + timestamp=self._get_current_time(), + ) + + def _handle_request_created(self, request, **kwargs): + context = request.context + new_attempt_event = context[ + 'current_api_call_event' + ].new_api_call_attempt(timestamp=self._get_current_time()) + new_attempt_event.request_headers = request.headers + new_attempt_event.url = request.url + context['current_api_call_attempt_event'] = new_attempt_event + + def _handle_response_received( + self, parsed_response, context, exception, **kwargs + ): + attempt_event = context.pop('current_api_call_attempt_event') + attempt_event.latency = self._get_latency(attempt_event) + if parsed_response is not None: + attempt_event.http_status_code = parsed_response[ + 'ResponseMetadata' + ]['HTTPStatusCode'] + attempt_event.response_headers = parsed_response[ + 'ResponseMetadata' + ]['HTTPHeaders'] + attempt_event.parsed_error = parsed_response.get('Error') + else: + attempt_event.wire_exception = exception + return attempt_event + + def _handle_after_call(self, context, parsed, **kwargs): + context['current_api_call_event'].retries_exceeded = parsed[ + 'ResponseMetadata' + ].get('MaxAttemptsReached', False) + return self._complete_api_call(context) + + def _handle_after_call_error(self, context, exception, **kwargs): + # If the after-call-error was emitted and the error being raised + # was a retryable connection error, then the retries must have exceeded + # for that exception as this event gets emitted **after** retries + # happen. + context[ + 'current_api_call_event' + ].retries_exceeded = self._is_retryable_exception(exception) + return self._complete_api_call(context) + + def _is_retryable_exception(self, exception): + return isinstance( + exception, tuple(RETRYABLE_EXCEPTIONS['GENERAL_CONNECTION_ERROR']) + ) + + def _complete_api_call(self, context): + call_event = context.pop('current_api_call_event') + call_event.latency = self._get_latency(call_event) + return call_event + + def _get_latency(self, event): + return self._get_current_time() - event.timestamp + + def _get_current_time(self): + return int(self._time() * 1000) + + +class BaseMonitorEvent: + def __init__(self, service, operation, timestamp): + """Base monitor event + + :type service: str + :param service: A string identifying the service associated to + the event + + :type operation: str + :param operation: A string identifying the operation of service + associated to the event + + :type timestamp: int + :param timestamp: Epoch time in milliseconds from when the event began + """ + self.service = service + self.operation = operation + self.timestamp = timestamp + + def __repr__(self): + return f'{self.__class__.__name__}({self.__dict__!r})' + + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.__dict__ == other.__dict__ + return False + + +class APICallEvent(BaseMonitorEvent): + def __init__( + self, + service, + operation, + timestamp, + latency=None, + attempts=None, + retries_exceeded=False, + ): + """Monitor event for a single API call + + This event corresponds to a single client method call, which includes + every HTTP requests attempt made in order to complete the client call + + :type service: str + :param service: A string identifying the service associated to + the event + + :type operation: str + :param operation: A string identifying the operation of service + associated to the event + + :type timestamp: int + :param timestamp: Epoch time in milliseconds from when the event began + + :type latency: int + :param latency: The time in milliseconds to complete the client call + + :type attempts: list + :param attempts: The list of APICallAttempts associated to the + APICall + + :type retries_exceeded: bool + :param retries_exceeded: True if API call exceeded retries. False + otherwise + """ + super().__init__( + service=service, operation=operation, timestamp=timestamp + ) + self.latency = latency + self.attempts = attempts + if attempts is None: + self.attempts = [] + self.retries_exceeded = retries_exceeded + + def new_api_call_attempt(self, timestamp): + """Instantiates APICallAttemptEvent associated to the APICallEvent + + :type timestamp: int + :param timestamp: Epoch time in milliseconds to associate to the + APICallAttemptEvent + """ + attempt_event = APICallAttemptEvent( + service=self.service, operation=self.operation, timestamp=timestamp + ) + self.attempts.append(attempt_event) + return attempt_event + + +class APICallAttemptEvent(BaseMonitorEvent): + def __init__( + self, + service, + operation, + timestamp, + latency=None, + url=None, + http_status_code=None, + request_headers=None, + response_headers=None, + parsed_error=None, + wire_exception=None, + ): + """Monitor event for a single API call attempt + + This event corresponds to a single HTTP request attempt in completing + the entire client method call. + + :type service: str + :param service: A string identifying the service associated to + the event + + :type operation: str + :param operation: A string identifying the operation of service + associated to the event + + :type timestamp: int + :param timestamp: Epoch time in milliseconds from when the HTTP request + started + + :type latency: int + :param latency: The time in milliseconds to complete the HTTP request + whether it succeeded or failed + + :type url: str + :param url: The URL the attempt was sent to + + :type http_status_code: int + :param http_status_code: The HTTP status code of the HTTP response + if there was a response + + :type request_headers: dict + :param request_headers: The HTTP headers sent in making the HTTP + request + + :type response_headers: dict + :param response_headers: The HTTP headers returned in the HTTP response + if there was a response + + :type parsed_error: dict + :param parsed_error: The error parsed if the service returned an + error back + + :type wire_exception: Exception + :param wire_exception: The exception raised in sending the HTTP + request (i.e. ConnectionError) + """ + super().__init__( + service=service, operation=operation, timestamp=timestamp + ) + self.latency = latency + self.url = url + self.http_status_code = http_status_code + self.request_headers = request_headers + self.response_headers = response_headers + self.parsed_error = parsed_error + self.wire_exception = wire_exception + + +class CSMSerializer: + _MAX_CLIENT_ID_LENGTH = 255 + _MAX_EXCEPTION_CLASS_LENGTH = 128 + _MAX_ERROR_CODE_LENGTH = 128 + _MAX_USER_AGENT_LENGTH = 256 + _MAX_MESSAGE_LENGTH = 512 + _RESPONSE_HEADERS_TO_EVENT_ENTRIES = { + 'x-amzn-requestid': 'XAmznRequestId', + 'x-amz-request-id': 'XAmzRequestId', + 'x-amz-id-2': 'XAmzId2', + } + _AUTH_REGEXS = { + 'v4': re.compile( + r'AWS4-HMAC-SHA256 ' + r'Credential=(?P\w+)/\d+/' + r'(?P[a-z0-9-]+)/' + ), + 's3': re.compile(r'AWS (?P\w+):'), + } + _SERIALIZEABLE_EVENT_PROPERTIES = [ + 'service', + 'operation', + 'timestamp', + 'attempts', + 'latency', + 'retries_exceeded', + 'url', + 'request_headers', + 'http_status_code', + 'response_headers', + 'parsed_error', + 'wire_exception', + ] + + def __init__(self, csm_client_id): + """Serializes monitor events to CSM (Client Side Monitoring) format + + :type csm_client_id: str + :param csm_client_id: The application identifier to associate + to the serialized events + """ + self._validate_client_id(csm_client_id) + self.csm_client_id = csm_client_id + + def _validate_client_id(self, csm_client_id): + if len(csm_client_id) > self._MAX_CLIENT_ID_LENGTH: + raise ValueError( + f'The value provided for csm_client_id: {csm_client_id} exceeds ' + f'the maximum length of {self._MAX_CLIENT_ID_LENGTH} characters' + ) + + def serialize(self, event): + """Serializes a monitor event to the CSM format + + :type event: BaseMonitorEvent + :param event: The event to serialize to bytes + + :rtype: bytes + :returns: The CSM serialized form of the event + """ + event_dict = self._get_base_event_dict(event) + event_type = self._get_event_type(event) + event_dict['Type'] = event_type + for attr in self._SERIALIZEABLE_EVENT_PROPERTIES: + value = getattr(event, attr, None) + if value is not None: + getattr(self, '_serialize_' + attr)( + value, event_dict, event_type=event_type + ) + return ensure_bytes(json.dumps(event_dict, separators=(',', ':'))) + + def _get_base_event_dict(self, event): + return { + 'Version': 1, + 'ClientId': self.csm_client_id, + } + + def _serialize_service(self, service, event_dict, **kwargs): + event_dict['Service'] = service + + def _serialize_operation(self, operation, event_dict, **kwargs): + event_dict['Api'] = operation + + def _serialize_timestamp(self, timestamp, event_dict, **kwargs): + event_dict['Timestamp'] = timestamp + + def _serialize_attempts(self, attempts, event_dict, **kwargs): + event_dict['AttemptCount'] = len(attempts) + if attempts: + self._add_fields_from_last_attempt(event_dict, attempts[-1]) + + def _add_fields_from_last_attempt(self, event_dict, last_attempt): + if last_attempt.request_headers: + # It does not matter which attempt to use to grab the region + # for the ApiCall event, but SDKs typically do the last one. + region = self._get_region(last_attempt.request_headers) + if region is not None: + event_dict['Region'] = region + event_dict['UserAgent'] = self._get_user_agent( + last_attempt.request_headers + ) + if last_attempt.http_status_code is not None: + event_dict['FinalHttpStatusCode'] = last_attempt.http_status_code + if last_attempt.parsed_error is not None: + self._serialize_parsed_error( + last_attempt.parsed_error, event_dict, 'ApiCall' + ) + if last_attempt.wire_exception is not None: + self._serialize_wire_exception( + last_attempt.wire_exception, event_dict, 'ApiCall' + ) + + def _serialize_latency(self, latency, event_dict, event_type): + if event_type == 'ApiCall': + event_dict['Latency'] = latency + elif event_type == 'ApiCallAttempt': + event_dict['AttemptLatency'] = latency + + def _serialize_retries_exceeded( + self, retries_exceeded, event_dict, **kwargs + ): + event_dict['MaxRetriesExceeded'] = 1 if retries_exceeded else 0 + + def _serialize_url(self, url, event_dict, **kwargs): + event_dict['Fqdn'] = urlparse(url).netloc + + def _serialize_request_headers( + self, request_headers, event_dict, **kwargs + ): + event_dict['UserAgent'] = self._get_user_agent(request_headers) + if self._is_signed(request_headers): + event_dict['AccessKey'] = self._get_access_key(request_headers) + region = self._get_region(request_headers) + if region is not None: + event_dict['Region'] = region + if 'X-Amz-Security-Token' in request_headers: + event_dict['SessionToken'] = request_headers[ + 'X-Amz-Security-Token' + ] + + def _serialize_http_status_code( + self, http_status_code, event_dict, **kwargs + ): + event_dict['HttpStatusCode'] = http_status_code + + def _serialize_response_headers( + self, response_headers, event_dict, **kwargs + ): + for header, entry in self._RESPONSE_HEADERS_TO_EVENT_ENTRIES.items(): + if header in response_headers: + event_dict[entry] = response_headers[header] + + def _serialize_parsed_error( + self, parsed_error, event_dict, event_type, **kwargs + ): + field_prefix = 'Final' if event_type == 'ApiCall' else '' + event_dict[field_prefix + 'AwsException'] = self._truncate( + parsed_error['Code'], self._MAX_ERROR_CODE_LENGTH + ) + event_dict[field_prefix + 'AwsExceptionMessage'] = self._truncate( + parsed_error['Message'], self._MAX_MESSAGE_LENGTH + ) + + def _serialize_wire_exception( + self, wire_exception, event_dict, event_type, **kwargs + ): + field_prefix = 'Final' if event_type == 'ApiCall' else '' + event_dict[field_prefix + 'SdkException'] = self._truncate( + wire_exception.__class__.__name__, self._MAX_EXCEPTION_CLASS_LENGTH + ) + event_dict[field_prefix + 'SdkExceptionMessage'] = self._truncate( + str(wire_exception), self._MAX_MESSAGE_LENGTH + ) + + def _get_event_type(self, event): + if isinstance(event, APICallEvent): + return 'ApiCall' + elif isinstance(event, APICallAttemptEvent): + return 'ApiCallAttempt' + + def _get_access_key(self, request_headers): + auth_val = self._get_auth_value(request_headers) + _, auth_match = self._get_auth_match(auth_val) + return auth_match.group('access_key') + + def _get_region(self, request_headers): + if not self._is_signed(request_headers): + return None + auth_val = self._get_auth_value(request_headers) + signature_version, auth_match = self._get_auth_match(auth_val) + if signature_version != 'v4': + return None + return auth_match.group('signing_region') + + def _get_user_agent(self, request_headers): + return self._truncate( + ensure_unicode(request_headers.get('User-Agent', '')), + self._MAX_USER_AGENT_LENGTH, + ) + + def _is_signed(self, request_headers): + return 'Authorization' in request_headers + + def _get_auth_value(self, request_headers): + return ensure_unicode(request_headers['Authorization']) + + def _get_auth_match(self, auth_val): + for signature_version, regex in self._AUTH_REGEXS.items(): + match = regex.match(auth_val) + if match: + return signature_version, match + return None, None + + def _truncate(self, text, max_length): + if len(text) > max_length: + logger.debug( + 'Truncating following value to maximum length of %s: %s', + text, + max_length, + ) + return text[:max_length] + return text + + +class SocketPublisher: + _MAX_MONITOR_EVENT_LENGTH = 8 * 1024 + + def __init__(self, socket, host, port, serializer): + """Publishes monitor events to a socket + + :type socket: socket.socket + :param socket: The socket object to use to publish events + + :type host: string + :param host: The host to send events to + + :type port: integer + :param port: The port on the host to send events to + + :param serializer: The serializer to use to serialize the event + to a form that can be published to the socket. This must + have a `serialize()` method that accepts a monitor event + and return bytes + """ + self._socket = socket + self._address = (host, port) + self._serializer = serializer + + def publish(self, event): + """Publishes a specified monitor event + + :type event: BaseMonitorEvent + :param event: The monitor event to be sent + over the publisher's socket to the desired address. + """ + serialized_event = self._serializer.serialize(event) + if len(serialized_event) > self._MAX_MONITOR_EVENT_LENGTH: + logger.debug( + 'Serialized event of size %s exceeds the maximum length ' + 'allowed: %s. Not sending event to socket.', + len(serialized_event), + self._MAX_MONITOR_EVENT_LENGTH, + ) + return + self._socket.sendto(serialized_event, self._address) diff --git a/venv/lib/python3.12/site-packages/botocore/paginate.py b/venv/lib/python3.12/site-packages/botocore/paginate.py new file mode 100644 index 0000000..63b4a29 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/paginate.py @@ -0,0 +1,728 @@ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import base64 +import json +import logging +from functools import partial +from itertools import tee + +import jmespath + +from botocore.context import with_current_context +from botocore.exceptions import PaginationError +from botocore.useragent import register_feature_id +from botocore.utils import merge_dicts, set_value_from_jmespath + +log = logging.getLogger(__name__) + + +class TokenEncoder: + """Encodes dictionaries into opaque strings. + + This for the most part json dumps + base64 encoding, but also supports + having bytes in the dictionary in addition to the types that json can + handle by default. + + This is intended for use in encoding pagination tokens, which in some + cases can be complex structures and / or contain bytes. + """ + + def encode(self, token): + """Encodes a dictionary to an opaque string. + + :type token: dict + :param token: A dictionary containing pagination information, + particularly the service pagination token(s) but also other boto + metadata. + + :rtype: str + :returns: An opaque string + """ + try: + # Try just using json dumps first to avoid having to traverse + # and encode the dict. In 99.9999% of cases this will work. + json_string = json.dumps(token) + except (TypeError, UnicodeDecodeError): + # If normal dumping failed, go through and base64 encode all bytes. + encoded_token, encoded_keys = self._encode(token, []) + + # Save the list of all the encoded key paths. We can safely + # assume that no service will ever use this key. + encoded_token['boto_encoded_keys'] = encoded_keys + + # Now that the bytes are all encoded, dump the json. + json_string = json.dumps(encoded_token) + + # base64 encode the json string to produce an opaque token string. + return base64.b64encode(json_string.encode('utf-8')).decode('utf-8') + + def _encode(self, data, path): + """Encode bytes in given data, keeping track of the path traversed.""" + if isinstance(data, dict): + return self._encode_dict(data, path) + elif isinstance(data, list): + return self._encode_list(data, path) + elif isinstance(data, bytes): + return self._encode_bytes(data, path) + else: + return data, [] + + def _encode_list(self, data, path): + """Encode any bytes in a list, noting the index of what is encoded.""" + new_data = [] + encoded = [] + for i, value in enumerate(data): + new_path = path + [i] + new_value, new_encoded = self._encode(value, new_path) + new_data.append(new_value) + encoded.extend(new_encoded) + return new_data, encoded + + def _encode_dict(self, data, path): + """Encode any bytes in a dict, noting the index of what is encoded.""" + new_data = {} + encoded = [] + for key, value in data.items(): + new_path = path + [key] + new_value, new_encoded = self._encode(value, new_path) + new_data[key] = new_value + encoded.extend(new_encoded) + return new_data, encoded + + def _encode_bytes(self, data, path): + """Base64 encode a byte string.""" + return base64.b64encode(data).decode('utf-8'), [path] + + +class TokenDecoder: + """Decodes token strings back into dictionaries. + + This performs the inverse operation to the TokenEncoder, accepting + opaque strings and decoding them into a useable form. + """ + + def decode(self, token): + """Decodes an opaque string to a dictionary. + + :type token: str + :param token: A token string given by the botocore pagination + interface. + + :rtype: dict + :returns: A dictionary containing pagination information, + particularly the service pagination token(s) but also other boto + metadata. + """ + json_string = base64.b64decode(token.encode('utf-8')).decode('utf-8') + decoded_token = json.loads(json_string) + + # Remove the encoding metadata as it is read since it will no longer + # be needed. + encoded_keys = decoded_token.pop('boto_encoded_keys', None) + if encoded_keys is None: + return decoded_token + else: + return self._decode(decoded_token, encoded_keys) + + def _decode(self, token, encoded_keys): + """Find each encoded value and decode it.""" + for key in encoded_keys: + encoded = self._path_get(token, key) + decoded = base64.b64decode(encoded.encode('utf-8')) + self._path_set(token, key, decoded) + return token + + def _path_get(self, data, path): + """Return the nested data at the given path. + + For instance: + data = {'foo': ['bar', 'baz']} + path = ['foo', 0] + ==> 'bar' + """ + # jmespath isn't used here because it would be difficult to actually + # create the jmespath query when taking all of the unknowns of key + # structure into account. Gross though this is, it is simple and not + # very error prone. + d = data + for step in path: + d = d[step] + return d + + def _path_set(self, data, path, value): + """Set the value of a key in the given data. + + Example: + data = {'foo': ['bar', 'baz']} + path = ['foo', 1] + value = 'bin' + ==> data = {'foo': ['bar', 'bin']} + """ + container = self._path_get(data, path[:-1]) + container[path[-1]] = value + + +class PaginatorModel: + def __init__(self, paginator_config): + self._paginator_config = paginator_config['pagination'] + + def get_paginator(self, operation_name): + try: + single_paginator_config = self._paginator_config[operation_name] + except KeyError: + raise ValueError( + f"Paginator for operation does not exist: {operation_name}" + ) + return single_paginator_config + + +class PageIterator: + """An iterable object to paginate API results. + Please note it is NOT a python iterator. + Use ``iter`` to wrap this as a generator. + """ + + def __init__( + self, + method, + input_token, + output_token, + more_results, + result_keys, + non_aggregate_keys, + limit_key, + max_items, + starting_token, + page_size, + op_kwargs, + ): + self._method = method + self._input_token = input_token + self._output_token = output_token + self._more_results = more_results + self._result_keys = result_keys + self._max_items = max_items + self._limit_key = limit_key + self._starting_token = starting_token + self._page_size = page_size + self._op_kwargs = op_kwargs + self._resume_token = None + self._non_aggregate_key_exprs = non_aggregate_keys + self._non_aggregate_part = {} + self._token_encoder = TokenEncoder() + self._token_decoder = TokenDecoder() + + @property + def result_keys(self): + return self._result_keys + + @property + def resume_token(self): + """Token to specify to resume pagination.""" + return self._resume_token + + @resume_token.setter + def resume_token(self, value): + if not isinstance(value, dict): + raise ValueError(f"Bad starting token: {value}") + + if 'boto_truncate_amount' in value: + token_keys = sorted(self._input_token + ['boto_truncate_amount']) + else: + token_keys = sorted(self._input_token) + dict_keys = sorted(value.keys()) + + if token_keys == dict_keys: + self._resume_token = self._token_encoder.encode(value) + else: + raise ValueError(f"Bad starting token: {value}") + + @property + def non_aggregate_part(self): + return self._non_aggregate_part + + def __iter__(self): + current_kwargs = self._op_kwargs + previous_next_token = None + next_token = {key: None for key in self._input_token} + if self._starting_token is not None: + # If the starting token exists, populate the next_token with the + # values inside it. This ensures that we have the service's + # pagination token on hand if we need to truncate after the + # first response. + next_token = self._parse_starting_token()[0] + # The number of items from result_key we've seen so far. + total_items = 0 + first_request = True + primary_result_key = self.result_keys[0] + starting_truncation = 0 + self._inject_starting_params(current_kwargs) + while True: + response = self._make_request(current_kwargs) + parsed = self._extract_parsed_response(response) + if first_request: + # The first request is handled differently. We could + # possibly have a resume/starting token that tells us where + # to index into the retrieved page. + if self._starting_token is not None: + starting_truncation = self._handle_first_request( + parsed, primary_result_key, starting_truncation + ) + first_request = False + self._record_non_aggregate_key_values(parsed) + else: + # If this isn't the first request, we have already sliced into + # the first request and had to make additional requests after. + # We no longer need to add this to truncation. + starting_truncation = 0 + current_response = primary_result_key.search(parsed) + if current_response is None: + current_response = [] + num_current_response = len(current_response) + truncate_amount = 0 + if self._max_items is not None: + truncate_amount = ( + total_items + num_current_response - self._max_items + ) + if truncate_amount > 0: + self._truncate_response( + parsed, + primary_result_key, + truncate_amount, + starting_truncation, + next_token, + ) + yield response + break + else: + yield response + total_items += num_current_response + next_token = self._get_next_token(parsed) + if all(t is None for t in next_token.values()): + break + if ( + self._max_items is not None + and total_items == self._max_items + ): + # We're on a page boundary so we can set the current + # next token to be the resume token. + self.resume_token = next_token + break + if ( + previous_next_token is not None + and previous_next_token == next_token + ): + message = ( + f"The same next token was received twice: {next_token}" + ) + raise PaginationError(message=message) + self._inject_token_into_kwargs(current_kwargs, next_token) + previous_next_token = next_token + + def search(self, expression): + """Applies a JMESPath expression to a paginator + + Each page of results is searched using the provided JMESPath + expression. If the result is not a list, it is yielded + directly. If the result is a list, each element in the result + is yielded individually (essentially implementing a flatmap in + which the JMESPath search is the mapping function). + + :type expression: str + :param expression: JMESPath expression to apply to each page. + + :return: Returns an iterator that yields the individual + elements of applying a JMESPath expression to each page of + results. + """ + compiled = jmespath.compile(expression) + for page in self: + results = compiled.search(page) + if isinstance(results, list): + yield from results + else: + # Yield result directly if it is not a list. + yield results + + @with_current_context(partial(register_feature_id, 'PAGINATOR')) + def _make_request(self, current_kwargs): + return self._method(**current_kwargs) + + def _extract_parsed_response(self, response): + return response + + def _record_non_aggregate_key_values(self, response): + non_aggregate_keys = {} + for expression in self._non_aggregate_key_exprs: + result = expression.search(response) + set_value_from_jmespath( + non_aggregate_keys, expression.expression, result + ) + self._non_aggregate_part = non_aggregate_keys + + def _inject_starting_params(self, op_kwargs): + # If the user has specified a starting token we need to + # inject that into the operation's kwargs. + if self._starting_token is not None: + # Don't need to do anything special if there is no starting + # token specified. + next_token = self._parse_starting_token()[0] + self._inject_token_into_kwargs(op_kwargs, next_token) + if self._page_size is not None: + # Pass the page size as the parameter name for limiting + # page size, also known as the limit_key. + op_kwargs[self._limit_key] = self._page_size + + def _inject_token_into_kwargs(self, op_kwargs, next_token): + for name, token in next_token.items(): + if (token is not None) and (token != 'None'): + op_kwargs[name] = token + elif name in op_kwargs: + del op_kwargs[name] + + def _handle_first_request( + self, parsed, primary_result_key, starting_truncation + ): + # If the payload is an array or string, we need to slice into it + # and only return the truncated amount. + starting_truncation = self._parse_starting_token()[1] + all_data = primary_result_key.search(parsed) + if isinstance(all_data, (list, str)): + data = all_data[starting_truncation:] + else: + data = None + set_value_from_jmespath(parsed, primary_result_key.expression, data) + # We also need to truncate any secondary result keys + # because they were not truncated in the previous last + # response. + for token in self.result_keys: + if token == primary_result_key: + continue + sample = token.search(parsed) + if isinstance(sample, list): + empty_value = [] + elif isinstance(sample, str): + empty_value = '' + elif isinstance(sample, (int, float)): + # Even though we may be resuming from a truncated page, we + # still start from the actual numeric secondary result. For + # DynamoDB's Count/ScannedCount, this will still show how many + # items the server evaluated, even if the client is truncating + # due to a StartingToken. + empty_value = sample + else: + empty_value = None + set_value_from_jmespath(parsed, token.expression, empty_value) + return starting_truncation + + def _truncate_response( + self, + parsed, + primary_result_key, + truncate_amount, + starting_truncation, + next_token, + ): + original = primary_result_key.search(parsed) + if original is None: + original = [] + amount_to_keep = len(original) - truncate_amount + truncated = original[:amount_to_keep] + set_value_from_jmespath( + parsed, primary_result_key.expression, truncated + ) + # The issue here is that even though we know how much we've truncated + # we need to account for this globally including any starting + # left truncation. For example: + # Raw response: [0,1,2,3] + # Starting index: 1 + # Max items: 1 + # Starting left truncation: [1, 2, 3] + # End right truncation for max items: [1] + # However, even though we only kept 1, this is post + # left truncation so the next starting index should be 2, not 1 + # (left_truncation + amount_to_keep). + next_token['boto_truncate_amount'] = ( + amount_to_keep + starting_truncation + ) + self.resume_token = next_token + + def _get_next_token(self, parsed): + if self._more_results is not None: + if not self._more_results.search(parsed): + return {} + next_tokens = {} + for output_token, input_key in zip( + self._output_token, self._input_token + ): + next_token = output_token.search(parsed) + # We do not want to include any empty strings as actual tokens. + # Treat them as None. + if next_token: + next_tokens[input_key] = next_token + else: + next_tokens[input_key] = None + return next_tokens + + def result_key_iters(self): + teed_results = tee(self, len(self.result_keys)) + return [ + ResultKeyIterator(i, result_key) + for i, result_key in zip(teed_results, self.result_keys) + ] + + def build_full_result(self): + complete_result = {} + for response in self: + page = response + # We want to try to catch operation object pagination + # and format correctly for those. They come in the form + # of a tuple of two elements: (http_response, parsed_responsed). + # We want the parsed_response as that is what the page iterator + # uses. We can remove it though once operation objects are removed. + if isinstance(response, tuple) and len(response) == 2: + page = response[1] + # We're incrementally building the full response page + # by page. For each page in the response we need to + # inject the necessary components from the page + # into the complete_result. + for result_expression in self.result_keys: + # In order to incrementally update a result key + # we need to search the existing value from complete_result, + # then we need to search the _current_ page for the + # current result key value. Then we append the current + # value onto the existing value, and re-set that value + # as the new value. + result_value = result_expression.search(page) + if result_value is None: + continue + existing_value = result_expression.search(complete_result) + if existing_value is None: + # Set the initial result + set_value_from_jmespath( + complete_result, + result_expression.expression, + result_value, + ) + continue + # Now both result_value and existing_value contain something + if isinstance(result_value, list): + existing_value.extend(result_value) + elif isinstance(result_value, (int, float, str)): + # Modify the existing result with the sum or concatenation + set_value_from_jmespath( + complete_result, + result_expression.expression, + existing_value + result_value, + ) + merge_dicts(complete_result, self.non_aggregate_part) + if self.resume_token is not None: + complete_result['NextToken'] = self.resume_token + return complete_result + + def _parse_starting_token(self): + if self._starting_token is None: + return None + + # The starting token is a dict passed as a base64 encoded string. + next_token = self._starting_token + try: + next_token = self._token_decoder.decode(next_token) + index = 0 + if 'boto_truncate_amount' in next_token: + index = next_token.get('boto_truncate_amount') + del next_token['boto_truncate_amount'] + except (ValueError, TypeError): + next_token, index = self._parse_starting_token_deprecated() + return next_token, index + + def _parse_starting_token_deprecated(self): + """ + This handles parsing of old style starting tokens, and attempts to + coerce them into the new style. + """ + log.debug( + "Attempting to fall back to old starting token parser. For token: %s", + self._starting_token, + ) + if self._starting_token is None: + return None + + parts = self._starting_token.split('___') + next_token = [] + index = 0 + if len(parts) == len(self._input_token) + 1: + try: + index = int(parts.pop()) + except ValueError: + # This doesn't look like a valid old-style token, so we're + # passing it along as an opaque service token. + parts = [self._starting_token] + + for part in parts: + if part == 'None': + next_token.append(None) + else: + next_token.append(part) + return self._convert_deprecated_starting_token(next_token), index + + def _convert_deprecated_starting_token(self, deprecated_token): + """ + This attempts to convert a deprecated starting token into the new + style. + """ + len_deprecated_token = len(deprecated_token) + len_input_token = len(self._input_token) + if len_deprecated_token > len_input_token: + raise ValueError(f"Bad starting token: {self._starting_token}") + elif len_deprecated_token < len_input_token: + log.debug( + "Old format starting token does not contain all input " + "tokens. Setting the rest, in order, as None." + ) + for i in range(len_input_token - len_deprecated_token): + deprecated_token.append(None) + return dict(zip(self._input_token, deprecated_token)) + + +class Paginator: + PAGE_ITERATOR_CLS = PageIterator + + def __init__(self, method, pagination_config, model): + self._model = model + self._method = method + self._pagination_cfg = pagination_config + self._output_token = self._get_output_tokens(self._pagination_cfg) + self._input_token = self._get_input_tokens(self._pagination_cfg) + self._more_results = self._get_more_results_token(self._pagination_cfg) + self._non_aggregate_keys = self._get_non_aggregate_keys( + self._pagination_cfg + ) + self._result_keys = self._get_result_keys(self._pagination_cfg) + self._limit_key = self._get_limit_key(self._pagination_cfg) + + @property + def result_keys(self): + return self._result_keys + + def _get_non_aggregate_keys(self, config): + keys = [] + for key in config.get('non_aggregate_keys', []): + keys.append(jmespath.compile(key)) + return keys + + def _get_output_tokens(self, config): + output = [] + output_token = config['output_token'] + if not isinstance(output_token, list): + output_token = [output_token] + for config in output_token: + output.append(jmespath.compile(config)) + return output + + def _get_input_tokens(self, config): + input_token = self._pagination_cfg['input_token'] + if not isinstance(input_token, list): + input_token = [input_token] + return input_token + + def _get_more_results_token(self, config): + more_results = config.get('more_results') + if more_results is not None: + return jmespath.compile(more_results) + + def _get_result_keys(self, config): + result_key = config.get('result_key') + if result_key is not None: + if not isinstance(result_key, list): + result_key = [result_key] + result_key = [jmespath.compile(rk) for rk in result_key] + return result_key + + def _get_limit_key(self, config): + return config.get('limit_key') + + def paginate(self, **kwargs): + """Create paginator object for an operation. + + This returns an iterable object. Iterating over + this object will yield a single page of a response + at a time. + + """ + page_params = self._extract_paging_params(kwargs) + return self.PAGE_ITERATOR_CLS( + self._method, + self._input_token, + self._output_token, + self._more_results, + self._result_keys, + self._non_aggregate_keys, + self._limit_key, + page_params['MaxItems'], + page_params['StartingToken'], + page_params['PageSize'], + kwargs, + ) + + def _extract_paging_params(self, kwargs): + pagination_config = kwargs.pop('PaginationConfig', {}) + max_items = pagination_config.get('MaxItems', None) + if max_items is not None: + max_items = int(max_items) + page_size = pagination_config.get('PageSize', None) + if page_size is not None: + if self._limit_key is None: + raise PaginationError( + message="PageSize parameter is not supported for the " + "pagination interface for this operation." + ) + input_members = self._model.input_shape.members + limit_key_shape = input_members.get(self._limit_key) + if limit_key_shape.type_name == 'string': + if not isinstance(page_size, str): + page_size = str(page_size) + else: + page_size = int(page_size) + return { + 'MaxItems': max_items, + 'StartingToken': pagination_config.get('StartingToken', None), + 'PageSize': page_size, + } + + +class ResultKeyIterator: + """Iterates over the results of paginated responses. + + Each iterator is associated with a single result key. + Iterating over this object will give you each element in + the result key list. + + :param pages_iterator: An iterator that will give you + pages of results (a ``PageIterator`` class). + :param result_key: The JMESPath expression representing + the result key. + + """ + + def __init__(self, pages_iterator, result_key): + self._pages_iterator = pages_iterator + self.result_key = result_key + + def __iter__(self): + for page in self._pages_iterator: + results = self.result_key.search(page) + if results is None: + results = [] + yield from results diff --git a/venv/lib/python3.12/site-packages/botocore/parsers.py b/venv/lib/python3.12/site-packages/botocore/parsers.py new file mode 100644 index 0000000..51977c6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/parsers.py @@ -0,0 +1,1489 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Response parsers for the various protocol types. + +The module contains classes that can take an HTTP response, and given +an output shape, parse the response into a dict according to the +rules in the output shape. + +There are many similarities amongst the different protocols with regard +to response parsing, and the code is structured in a way to avoid +code duplication when possible. The diagram below is a diagram +showing the inheritance hierarchy of the response classes. + +:: + + + +-------------------+ + | ResponseParser | + +-------------------+ + ^ ^ ^ ^ ^ + | | | | | + | | | | +--------------------------------------------+ + | | | +-----------------------------+ | + | | | | | + +--------------------+ | +----------------+ | | + | | | | | ++----------+----------+ +------+-------+ +-------+------+ +------+-------+ +------+--------+ +|BaseXMLResponseParser| |BaseRestParser| |BaseJSONParser| |BaseCBORParser| |BaseRpcV2Parser| ++---------------------+ +--------------+ +--------------+ +----------+---+ +-+-------------+ + ^ ^ ^ ^ ^ ^ ^ ^ + | | | | | | | | + | | | | | | | | + | ++----------+-+ +-+--------+---+ | +---+---------+-+ + | |RestXMLParser| |RestJSONParser| | |RpcV2CBORParser| + +-----+-----+ +-------------+ +--------------+ | +---+---------+-+ + |QueryParser| | + +-----------+ +----+-----+ + |JSONParser| + +----------+ + +The diagram above shows that there is a base class, ``ResponseParser`` that +contains logic that is similar amongst all the different protocols (``query``, +``json``, ``rest-json``, ``rest-xml``, ``smithy-rpc-v2-cbor``). Amongst the various services +there is shared logic that can be grouped several ways: + +* The ``query`` and ``rest-xml`` both have XML bodies that are parsed in the + same way. +* The ``json`` and ``rest-json`` protocols both have JSON bodies that are + parsed in the same way. +* The ``rest-json`` and ``rest-xml`` protocols have additional attributes + besides body parameters that are parsed the same (headers, query string, + status code). + +This is reflected in the class diagram above. The ``BaseXMLResponseParser`` +and the BaseJSONParser contain logic for parsing the XML/JSON body, +and the BaseRestParser contains logic for parsing out attributes that +come from other parts of the HTTP response. Classes like the +``RestXMLParser`` inherit from the ``BaseXMLResponseParser`` to get the +XML body parsing logic and the ``BaseRestParser`` to get the HTTP +header/status code/query string parsing. + +Additionally, there are event stream parsers that are used by the other parsers +to wrap streaming bodies that represent a stream of events. The +BaseEventStreamParser extends from ResponseParser and defines the logic for +parsing values from the headers and payload of a message from the underlying +binary encoding protocol. Currently, event streams support parsing bodies +encoded as JSON and XML through the following hierarchy. + + + +--------------+ + |ResponseParser| + +--------------+ + ^ ^ ^ + +--------------------+ | +------------------+ + | | | + +----------+----------+ +----------+----------+ +-------+------+ + |BaseXMLResponseParser| |BaseEventStreamParser| |BaseJSONParser| + +---------------------+ +---------------------+ +--------------+ + ^ ^ ^ ^ + | | | | + | | | | + +-+----------------+-+ +-+-----------------+-+ + |EventStreamXMLParser| |EventStreamJSONParser| + +--------------------+ +---------------------+ + +Return Values +============= + +Each call to ``parse()`` returns a dict has this form:: + + Standard Response + + { + "ResponseMetadata": {"RequestId": } + + } + + Error response + + { + "ResponseMetadata": {"RequestId": } + "Error": { + "Code": , + "Message": , + "Type": , + + } + } + +""" + +import base64 +import http.client +import io +import json +import logging +import os +import re +import struct + +from botocore.compat import ETree, XMLParseError +from botocore.eventstream import EventStream, NoInitialResponseError +from botocore.utils import ( + CachedProperty, + ensure_boolean, + is_json_value_header, + lowercase_dict, + merge_dicts, + parse_timestamp, +) + +LOG = logging.getLogger(__name__) + +DEFAULT_TIMESTAMP_PARSER = parse_timestamp + + +class ResponseParserFactory: + def __init__(self): + self._defaults = {} + + def set_parser_defaults(self, **kwargs): + """Set default arguments when a parser instance is created. + + You can specify any kwargs that are allowed by a ResponseParser + class. There are currently two arguments: + + * timestamp_parser - A callable that can parse a timestamp string + * blob_parser - A callable that can parse a blob type + + """ + self._defaults.update(kwargs) + + def create_parser(self, protocol_name): + parser_cls = PROTOCOL_PARSERS[protocol_name] + return parser_cls(**self._defaults) + + +def create_parser(protocol): + return ResponseParserFactory().create_parser(protocol) + + +def _text_content(func): + # This decorator hides the difference between + # an XML node with text or a plain string. It's used + # to ensure that scalar processing operates only on text + # strings, which allows the same scalar handlers to be used + # for XML nodes from the body and HTTP headers. + def _get_text_content(self, shape, node_or_string): + if hasattr(node_or_string, 'text'): + text = node_or_string.text + if text is None: + # If an XML node is empty , + # we want to parse that as an empty string, + # not as a null/None value. + text = '' + else: + text = node_or_string + return func(self, shape, text) + + return _get_text_content + + +class ResponseParserError(Exception): + pass + + +class ResponseParser: + """Base class for response parsing. + + This class represents the interface that all ResponseParsers for the + various protocols must implement. + + This class will take an HTTP response and a model shape and parse the + HTTP response into a dictionary. + + There is a single public method exposed: ``parse``. See the ``parse`` + docstring for more info. + + """ + + DEFAULT_ENCODING = 'utf-8' + EVENT_STREAM_PARSER_CLS = None + # This is a list of known values for the 'location' key in the + # serialization dict. The location key tells us where in the response + # to parse the value. Members with locations that aren't in this list + # will be parsed from the body. + KNOWN_LOCATIONS = ('header', 'headers', 'statusCode') + + def __init__(self, timestamp_parser=None, blob_parser=None): + if timestamp_parser is None: + timestamp_parser = DEFAULT_TIMESTAMP_PARSER + self._timestamp_parser = timestamp_parser + if blob_parser is None: + blob_parser = self._default_blob_parser + self._blob_parser = blob_parser + self._event_stream_parser = None + if self.EVENT_STREAM_PARSER_CLS is not None: + self._event_stream_parser = self.EVENT_STREAM_PARSER_CLS( + timestamp_parser, blob_parser + ) + + def _default_blob_parser(self, value): + # Blobs are always returned as bytes type (this matters on python3). + # We don't decode this to a str because it's entirely possible that the + # blob contains binary data that actually can't be decoded. + return base64.b64decode(value) + + def parse(self, response, shape): + """Parse the HTTP response given a shape. + + :param response: The HTTP response dictionary. This is a dictionary + that represents the HTTP request. The dictionary must have the + following keys, ``body``, ``headers``, and ``status_code``. + + :param shape: The model shape describing the expected output. + :return: Returns a dictionary representing the parsed response + described by the model. In addition to the shape described from + the model, each response will also have a ``ResponseMetadata`` + which contains metadata about the response, which contains at least + two keys containing ``RequestId`` and ``HTTPStatusCode``. Some + responses may populate additional keys, but ``RequestId`` will + always be present. + + """ + LOG.debug('Response headers: %r', response['headers']) + LOG.debug('Response body:\n%r', response['body']) + if response['status_code'] >= 301: + if self._is_generic_error_response(response): + parsed = self._do_generic_error_parse(response) + elif self._is_modeled_error_shape(shape): + parsed = self._do_modeled_error_parse(response, shape) + # We don't want to decorate the modeled fields with metadata + return parsed + else: + parsed = self._do_error_parse(response, shape) + else: + parsed = self._do_parse(response, shape) + + # We don't want to decorate event stream responses with metadata + if shape and shape.serialization.get('eventstream'): + return parsed + + # Add ResponseMetadata if it doesn't exist and inject the HTTP + # status code and headers from the response. + if isinstance(parsed, dict): + response_metadata = parsed.get('ResponseMetadata', {}) + response_metadata['HTTPStatusCode'] = response['status_code'] + # Ensure that the http header keys are all lower cased. Older + # versions of urllib3 (< 1.11) would unintentionally do this for us + # (see urllib3#633). We need to do this conversion manually now. + headers = response['headers'] + response_metadata['HTTPHeaders'] = lowercase_dict(headers) + parsed['ResponseMetadata'] = response_metadata + self._add_checksum_response_metadata(response, response_metadata) + return parsed + + def _add_checksum_response_metadata(self, response, response_metadata): + checksum_context = response.get('context', {}).get('checksum', {}) + algorithm = checksum_context.get('response_algorithm') + if algorithm: + response_metadata['ChecksumAlgorithm'] = algorithm + + def _is_modeled_error_shape(self, shape): + return shape is not None and shape.metadata.get('exception', False) + + def _is_generic_error_response(self, response): + # There are times when a service will respond with a generic + # error response such as: + # 'Http/1.1 Service Unavailable' + # + # This can also happen if you're going through a proxy. + # In this case the protocol specific _do_error_parse will either + # fail to parse the response (in the best case) or silently succeed + # and treat the HTML above as an XML response and return + # non sensical parsed data. + # To prevent this case from happening we first need to check + # whether or not this response looks like the generic response. + if response['status_code'] >= 500: + if 'body' not in response or response['body'] is None: + return True + + body = response['body'].strip() + return body.startswith(b'') or not body + + def _do_generic_error_parse(self, response): + # There's not really much we can do when we get a generic + # html response. + LOG.debug( + "Received a non protocol specific error response from the " + "service, unable to populate error code and message." + ) + return { + 'Error': { + 'Code': str(response['status_code']), + 'Message': http.client.responses.get( + response['status_code'], '' + ), + }, + 'ResponseMetadata': {}, + } + + def _do_parse(self, response, shape): + raise NotImplementedError(f"{self.__class__.__name__}._do_parse") + + def _do_error_parse(self, response, shape): + raise NotImplementedError(f"{self.__class__.__name__}._do_error_parse") + + def _do_modeled_error_parse(self, response, shape, parsed): + raise NotImplementedError( + f"{self.__class__.__name__}._do_modeled_error_parse" + ) + + def _parse_shape(self, shape, node): + handler = getattr( + self, f'_handle_{shape.type_name}', self._default_handle + ) + return handler(shape, node) + + def _handle_list(self, shape, node): + # Enough implementations share list serialization that it's moved + # up here in the base class. + parsed = [] + member_shape = shape.member + for item in node: + # Treat all lists as sparse during parsing to safely handle null + # elements that may be present in service responses. + if item is None: + parsed.append(None) + else: + parsed.append(self._parse_shape(member_shape, item)) + return parsed + + def _default_handle(self, shape, value): + return value + + def _create_event_stream(self, response, shape): + parser = self._event_stream_parser + name = response['context'].get('operation_name') + return EventStream(response['body'], shape, parser, name) + + def _get_first_key(self, value): + return list(value)[0] + + def _has_unknown_tagged_union_member(self, shape, value): + if shape.is_tagged_union: + cleaned_value = value.copy() + cleaned_value.pop("__type", None) + cleaned_value = { + k: v for k, v in cleaned_value.items() if v is not None + } + if len(cleaned_value) != 1: + error_msg = ( + "Invalid service response: %s must have one and only " + "one member set." + ) + raise ResponseParserError(error_msg % shape.name) + tag = self._get_first_key(cleaned_value) + serialized_member_names = [ + shape.members[member].serialization.get('name', member) + for member in shape.members + ] + if tag not in serialized_member_names: + LOG.info( + "Received a tagged union response with member unknown to client: %s. " + "Please upgrade SDK for full response support.", + tag, + ) + return True + return False + + def _handle_unknown_tagged_union_member(self, tag): + return {'SDK_UNKNOWN_MEMBER': {'name': tag}} + + def _do_query_compatible_error_parse(self, code, headers, error): + """ + Error response may contain an x-amzn-query-error header to translate + errors codes from former `query` services into other protocols. We use this + to do our lookup in the errorfactory for modeled errors. + """ + query_error = headers['x-amzn-query-error'] + query_error_components = query_error.split(';') + + if len(query_error_components) == 2 and query_error_components[0]: + error['Error']['QueryErrorCode'] = code + error['Error']['Type'] = query_error_components[1] + return query_error_components[0] + return code + + +class BaseXMLResponseParser(ResponseParser): + def __init__(self, timestamp_parser=None, blob_parser=None): + super().__init__(timestamp_parser, blob_parser) + self._namespace_re = re.compile('{.*}') + + def _handle_map(self, shape, node): + parsed = {} + key_shape = shape.key + value_shape = shape.value + key_location_name = key_shape.serialization.get('name') or 'key' + value_location_name = value_shape.serialization.get('name') or 'value' + if shape.serialization.get('flattened') and not isinstance(node, list): + node = [node] + for keyval_node in node: + for single_pair in keyval_node: + # Within each there's a and a + tag_name = self._node_tag(single_pair) + if tag_name == key_location_name: + key_name = self._parse_shape(key_shape, single_pair) + elif tag_name == value_location_name: + val_name = self._parse_shape(value_shape, single_pair) + else: + raise ResponseParserError(f"Unknown tag: {tag_name}") + parsed[key_name] = val_name + return parsed + + def _node_tag(self, node): + return self._namespace_re.sub('', node.tag) + + def _handle_list(self, shape, node): + # When we use _build_name_to_xml_node, repeated elements are aggregated + # into a list. However, we can't tell the difference between a scalar + # value and a single element flattened list. So before calling the + # real _handle_list, we know that "node" should actually be a list if + # it's flattened, and if it's not, then we make it a one element list. + if shape.serialization.get('flattened') and not isinstance(node, list): + node = [node] + return super()._handle_list(shape, node) + + def _handle_structure(self, shape, node): + parsed = {} + members = shape.members + if shape.metadata.get('exception', False): + node = self._get_error_root(node) + xml_dict = self._build_name_to_xml_node(node) + if self._has_unknown_tagged_union_member(shape, xml_dict): + tag = self._get_first_key(xml_dict) + return self._handle_unknown_tagged_union_member(tag) + for member_name in members: + member_shape = members[member_name] + location = member_shape.serialization.get('location') + if ( + location in self.KNOWN_LOCATIONS + or member_shape.serialization.get('eventheader') + ): + # All members with known locations have already been handled, + # so we don't need to parse these members. + continue + xml_name = self._member_key_name(member_shape, member_name) + member_node = xml_dict.get(xml_name) + if member_node is not None: + parsed[member_name] = self._parse_shape( + member_shape, member_node + ) + elif member_shape.serialization.get('xmlAttribute'): + attribs = {} + location_name = member_shape.serialization['name'] + for key, value in node.attrib.items(): + new_key = self._namespace_re.sub( + location_name.split(':')[0] + ':', key + ) + attribs[new_key] = value + if location_name in attribs: + parsed[member_name] = attribs[location_name] + return parsed + + def _get_error_root(self, original_root): + if self._node_tag(original_root) == 'ErrorResponse': + for child in original_root: + if self._node_tag(child) == 'Error': + return child + return original_root + + def _member_key_name(self, shape, member_name): + # This method is needed because we have to special case flattened list + # with a serialization name. If this is the case we use the + # locationName from the list's member shape as the key name for the + # surrounding structure. + if shape.type_name == 'list' and shape.serialization.get('flattened'): + list_member_serialized_name = shape.member.serialization.get( + 'name' + ) + if list_member_serialized_name is not None: + return list_member_serialized_name + serialized_name = shape.serialization.get('name') + if serialized_name is not None: + return serialized_name + return member_name + + def _build_name_to_xml_node(self, parent_node): + # If the parent node is actually a list. We should not be trying + # to serialize it to a dictionary. Instead, return the first element + # in the list. + if isinstance(parent_node, list): + return self._build_name_to_xml_node(parent_node[0]) + xml_dict = {} + for item in parent_node: + key = self._node_tag(item) + if key in xml_dict: + # If the key already exists, the most natural + # way to handle this is to aggregate repeated + # keys into a single list. + # 12 -> {'foo': [Node(1), Node(2)]} + if isinstance(xml_dict[key], list): + xml_dict[key].append(item) + else: + # Convert from a scalar to a list. + xml_dict[key] = [xml_dict[key], item] + else: + xml_dict[key] = item + return xml_dict + + def _parse_xml_string_to_dom(self, xml_string): + try: + parser = ETree.XMLParser( + target=ETree.TreeBuilder(), encoding=self.DEFAULT_ENCODING + ) + parser.feed(xml_string) + root = parser.close() + except XMLParseError as e: + raise ResponseParserError( + f"Unable to parse response ({e}), " + f"invalid XML received. Further retries may succeed:\n{xml_string}" + ) + return root + + def _replace_nodes(self, parsed): + for key, value in parsed.items(): + if list(value): + sub_dict = self._build_name_to_xml_node(value) + parsed[key] = self._replace_nodes(sub_dict) + else: + parsed[key] = value.text + return parsed + + @_text_content + def _handle_boolean(self, shape, text): + if text == 'true': + return True + else: + return False + + @_text_content + def _handle_float(self, shape, text): + return float(text) + + @_text_content + def _handle_timestamp(self, shape, text): + return self._timestamp_parser(text) + + @_text_content + def _handle_integer(self, shape, text): + return int(text) + + @_text_content + def _handle_string(self, shape, text): + return text + + @_text_content + def _handle_blob(self, shape, text): + return self._blob_parser(text) + + _handle_character = _handle_string + _handle_double = _handle_float + _handle_long = _handle_integer + + +class QueryParser(BaseXMLResponseParser): + def _do_error_parse(self, response, shape): + xml_contents = response['body'] + root = self._parse_xml_string_to_dom(xml_contents) + parsed = self._build_name_to_xml_node(root) + self._replace_nodes(parsed) + # Once we've converted xml->dict, we need to make one or two + # more adjustments to extract nested errors and to be consistent + # with ResponseMetadata for non-error responses: + # 1. {"Errors": {"Error": {...}}} -> {"Error": {...}} + # 2. {"RequestId": "id"} -> {"ResponseMetadata": {"RequestId": "id"}} + if 'Errors' in parsed: + parsed.update(parsed.pop('Errors')) + if 'RequestId' in parsed: + parsed['ResponseMetadata'] = {'RequestId': parsed.pop('RequestId')} + return parsed + + def _do_modeled_error_parse(self, response, shape): + return self._parse_body_as_xml(response, shape, inject_metadata=False) + + def _do_parse(self, response, shape): + return self._parse_body_as_xml(response, shape, inject_metadata=True) + + def _parse_body_as_xml(self, response, shape, inject_metadata=True): + xml_contents = response['body'] + root = self._parse_xml_string_to_dom(xml_contents) + parsed = {} + if shape is not None: + start = root + if 'resultWrapper' in shape.serialization: + start = self._find_result_wrapped_shape( + shape.serialization['resultWrapper'], root + ) + parsed = self._parse_shape(shape, start) + if inject_metadata: + self._inject_response_metadata(root, parsed) + return parsed + + def _find_result_wrapped_shape(self, element_name, xml_root_node): + mapping = self._build_name_to_xml_node(xml_root_node) + return mapping[element_name] + + def _inject_response_metadata(self, node, inject_into): + mapping = self._build_name_to_xml_node(node) + child_node = mapping.get('ResponseMetadata') + if child_node is not None: + sub_mapping = self._build_name_to_xml_node(child_node) + for key, value in sub_mapping.items(): + sub_mapping[key] = value.text + inject_into['ResponseMetadata'] = sub_mapping + + +class EC2QueryParser(QueryParser): + def _inject_response_metadata(self, node, inject_into): + mapping = self._build_name_to_xml_node(node) + child_node = mapping.get('requestId') + if child_node is not None: + inject_into['ResponseMetadata'] = {'RequestId': child_node.text} + + def _do_error_parse(self, response, shape): + # EC2 errors look like: + # + # + # + # InvalidInstanceID.Malformed + # Invalid id: "1343124" + # + # + # 12345 + # + # This is different from QueryParser in that it's RequestID, + # not RequestId + original = super()._do_error_parse(response, shape) + if 'RequestID' in original: + original['ResponseMetadata'] = { + 'RequestId': original.pop('RequestID') + } + return original + + def _get_error_root(self, original_root): + for child in original_root: + if self._node_tag(child) == 'Errors': + for errors_child in child: + if self._node_tag(errors_child) == 'Error': + return errors_child + return original_root + + +class BaseJSONParser(ResponseParser): + def _handle_structure(self, shape, value): + final_parsed = {} + if shape.is_document_type: + final_parsed = value + else: + member_shapes = shape.members + if value is None: + # If the comes across the wire as "null" (None in python), + # we should be returning this unchanged, instead of as an + # empty dict. + return None + final_parsed = {} + if self._has_unknown_tagged_union_member(shape, value): + tag = self._get_first_key(value) + return self._handle_unknown_tagged_union_member(tag) + for member_name in member_shapes: + member_shape = member_shapes[member_name] + json_name = member_shape.serialization.get('name', member_name) + raw_value = value.get(json_name) + if raw_value is not None: + final_parsed[member_name] = self._parse_shape( + member_shapes[member_name], raw_value + ) + return final_parsed + + def _handle_map(self, shape, value): + parsed = {} + key_shape = shape.key + value_shape = shape.value + for key, value in value.items(): + actual_key = self._parse_shape(key_shape, key) + actual_value = self._parse_shape(value_shape, value) + parsed[actual_key] = actual_value + return parsed + + def _handle_blob(self, shape, value): + return self._blob_parser(value) + + def _handle_timestamp(self, shape, value): + return self._timestamp_parser(value) + + def _do_error_parse(self, response, shape): + body = self._parse_body_as_json(response['body']) + error = {"Error": {"Message": '', "Code": ''}, "ResponseMetadata": {}} + headers = response['headers'] + # Error responses can have slightly different structures for json. + # The basic structure is: + # + # {"__type":"ConnectClientException", + # "message":"The error message."} + + # The error message can either come in the 'message' or 'Message' key + # so we need to check for both. + error['Error']['Message'] = body.get( + 'message', body.get('Message', '') + ) + # if the message did not contain an error code + # include the response status code + response_code = response.get('status_code') + + code = body.get('__type', response_code and str(response_code)) + if code is not None: + # code has a couple forms as well: + # * "com.aws.dynamodb.vAPI#ProvisionedThroughputExceededException" + # * "ResourceNotFoundException" + if ':' in code: + code = code.split(':', 1)[0] + if '#' in code: + code = code.rsplit('#', 1)[1] + if 'x-amzn-query-error' in headers: + code = self._do_query_compatible_error_parse( + code, headers, error + ) + error['Error']['Code'] = code + self._inject_response_metadata(error, response['headers']) + return error + + def _inject_response_metadata(self, parsed, headers): + if 'x-amzn-requestid' in headers: + parsed.setdefault('ResponseMetadata', {})['RequestId'] = headers[ + 'x-amzn-requestid' + ] + + def _parse_body_as_json(self, body_contents): + if not body_contents: + return {} + body = body_contents.decode(self.DEFAULT_ENCODING) + try: + original_parsed = json.loads(body) + return original_parsed + except ValueError: + # if the body cannot be parsed, include + # the literal string as the message + return {'message': body} + + +class BaseCBORParser(ResponseParser): + INDEFINITE_ITEM_ADDITIONAL_INFO = 31 + BREAK_CODE = 0xFF + + @CachedProperty + def major_type_to_parsing_method_map(self): + return { + 0: self._parse_unsigned_integer, + 1: self._parse_negative_integer, + 2: self._parse_byte_string, + 3: self._parse_text_string, + 4: self._parse_array, + 5: self._parse_map, + 6: self._parse_tag, + 7: self._parse_simple_and_float, + } + + def get_peekable_stream_from_bytes(self, bytes): + return io.BufferedReader(io.BytesIO(bytes)) + + def parse_data_item(self, stream): + # CBOR data is divided into "data items", and each data item starts + # with an initial byte that describes how the following bytes should be parsed + initial_byte = self._read_bytes_as_int(stream, 1) + # The highest order three bits of the initial byte describe the CBOR major type + major_type = initial_byte >> 5 + # The lowest order 5 bits of the initial byte tells us more information about + # how the bytes should be parsed that will be used + additional_info = initial_byte & 0b00011111 + + if major_type in self.major_type_to_parsing_method_map: + method = self.major_type_to_parsing_method_map[major_type] + return method(stream, additional_info) + else: + raise ResponseParserError( + f"Unsupported inital byte found for data item- " + f"Major type:{major_type}, Additional info: " + f"{additional_info}" + ) + + # Major type 0 - unsigned integers + def _parse_unsigned_integer(self, stream, additional_info): + additional_info_to_num_bytes = { + 24: 1, + 25: 2, + 26: 4, + 27: 8, + } + # Values under 24 don't need a full byte to be stored; their values are + # instead stored as the "additional info" in the initial byte + if additional_info < 24: + return additional_info + elif additional_info in additional_info_to_num_bytes: + num_bytes = additional_info_to_num_bytes[additional_info] + return self._read_bytes_as_int(stream, num_bytes) + else: + raise ResponseParserError( + "Invalid CBOR integer returned from the service; unparsable " + f"additional info found for major type 0 or 1: {additional_info}" + ) + + # Major type 1 - negative integers + def _parse_negative_integer(self, stream, additional_info): + return -1 - self._parse_unsigned_integer(stream, additional_info) + + # Major type 2 - byte string + def _parse_byte_string(self, stream, additional_info): + if additional_info != self.INDEFINITE_ITEM_ADDITIONAL_INFO: + length = self._parse_unsigned_integer(stream, additional_info) + return self._read_from_stream(stream, length) + else: + chunks = [] + while True: + if self._handle_break_code(stream): + break + initial_byte = self._read_bytes_as_int(stream, 1) + additional_info = initial_byte & 0b00011111 + length = self._parse_unsigned_integer(stream, additional_info) + chunks.append(self._read_from_stream(stream, length)) + return b''.join(chunks) + + # Major type 3 - text string + def _parse_text_string(self, stream, additional_info): + return self._parse_byte_string(stream, additional_info).decode('utf-8') + + # Major type 4 - lists + def _parse_array(self, stream, additional_info): + if additional_info != self.INDEFINITE_ITEM_ADDITIONAL_INFO: + length = self._parse_unsigned_integer(stream, additional_info) + return [self.parse_data_item(stream) for _ in range(length)] + else: + items = [] + while not self._handle_break_code(stream): + items.append(self.parse_data_item(stream)) + return items + + # Major type 5 - maps + def _parse_map(self, stream, additional_info): + items = {} + if additional_info != self.INDEFINITE_ITEM_ADDITIONAL_INFO: + length = self._parse_unsigned_integer(stream, additional_info) + for _ in range(length): + self._parse_key_value_pair(stream, items) + return items + + else: + while not self._handle_break_code(stream): + self._parse_key_value_pair(stream, items) + return items + + def _parse_key_value_pair(self, stream, items): + key = self.parse_data_item(stream) + value = self.parse_data_item(stream) + if value is not None: + items[key] = value + + # Major type 6 is tags. The only tag we currently support is tag 1 for unix + # timestamps + def _parse_tag(self, stream, additional_info): + tag = self._parse_unsigned_integer(stream, additional_info) + value = self.parse_data_item(stream) + if tag == 1: # Epoch-based date/time in milliseconds + return self._parse_datetime(value) + else: + raise ResponseParserError( + f"Found CBOR tag not supported by botocore: {tag}" + ) + + def _parse_datetime(self, value): + if isinstance(value, (int, float)): + return self._timestamp_parser(value) + else: + raise ResponseParserError( + f"Unable to parse datetime value: {value}" + ) + + # Major type 7 includes floats and "simple" types. Supported simple types are + # currently boolean values, CBOR's null, and CBOR's undefined type. All other + # values are either floats or invalid. + def _parse_simple_and_float(self, stream, additional_info): + # For major type 7, values 20-23 correspond to CBOR "simple" values + additional_info_simple_values = { + 20: False, # CBOR false + 21: True, # CBOR true + 22: None, # CBOR null + 23: None, # CBOR undefined + } + # First we check if the additional info corresponds to a supported simple value + if additional_info in additional_info_simple_values: + return additional_info_simple_values[additional_info] + + # If it's not a simple value, we need to parse it into the correct format and + # number fo bytes + float_formats = { + 25: ('>e', 2), + 26: ('>f', 4), + 27: ('>d', 8), + } + + if additional_info in float_formats: + float_format, num_bytes = float_formats[additional_info] + return struct.unpack( + float_format, self._read_from_stream(stream, num_bytes) + )[0] + raise ResponseParserError( + f"Invalid additional info found for major type 7: {additional_info}. " + f"This indicates an unsupported simple type or an indefinite float value" + ) + + # This helper method is intended for use when parsing indefinite length items. + # It does nothing if the next byte is not the break code. If the next byte is + # the break code, it advances past that byte and returns True so the calling + # method knows to stop parsing that data item. + def _handle_break_code(self, stream): + if int.from_bytes(stream.peek(1)[:1], 'big') == self.BREAK_CODE: + stream.seek(1, os.SEEK_CUR) + return True + + def _read_bytes_as_int(self, stream, num_bytes): + byte = self._read_from_stream(stream, num_bytes) + return int.from_bytes(byte, 'big') + + def _read_from_stream(self, stream, num_bytes): + value = stream.read(num_bytes) + if len(value) != num_bytes: + raise ResponseParserError( + "End of stream reached; this indicates a " + "malformed CBOR response from the server or an " + "issue in botocore" + ) + return value + + +class BaseEventStreamParser(ResponseParser): + def _do_parse(self, response, shape): + final_parsed = {} + if shape.serialization.get('eventstream'): + event_type = response['headers'].get(':event-type') + event_shape = shape.members.get(event_type) + if event_shape: + final_parsed[event_type] = self._do_parse( + response, event_shape + ) + else: + self._parse_non_payload_attrs( + response, shape, shape.members, final_parsed + ) + self._parse_payload(response, shape, shape.members, final_parsed) + return final_parsed + + def _do_error_parse(self, response, shape): + exception_type = response['headers'].get(':exception-type') + exception_shape = shape.members.get(exception_type) + if exception_shape is not None: + original_parsed = self._initial_body_parse(response['body']) + body = self._parse_shape(exception_shape, original_parsed) + error = { + 'Error': { + 'Code': exception_type, + 'Message': body.get('Message', body.get('message', '')), + } + } + else: + error = { + 'Error': { + 'Code': response['headers'].get(':error-code', ''), + 'Message': response['headers'].get(':error-message', ''), + } + } + return error + + def _parse_payload(self, response, shape, member_shapes, final_parsed): + if shape.serialization.get('event'): + for name in member_shapes: + member_shape = member_shapes[name] + if member_shape.serialization.get('eventpayload'): + body = response['body'] + if member_shape.type_name == 'blob': + parsed_body = body + elif member_shape.type_name == 'string': + parsed_body = body.decode(self.DEFAULT_ENCODING) + else: + raw_parse = self._initial_body_parse(body) + parsed_body = self._parse_shape( + member_shape, raw_parse + ) + final_parsed[name] = parsed_body + return + # If we didn't find an explicit payload, use the current shape + original_parsed = self._initial_body_parse(response['body']) + body_parsed = self._parse_shape(shape, original_parsed) + final_parsed.update(body_parsed) + + def _parse_non_payload_attrs( + self, response, shape, member_shapes, final_parsed + ): + headers = response['headers'] + for name in member_shapes: + member_shape = member_shapes[name] + if member_shape.serialization.get('eventheader'): + if name in headers: + value = headers[name] + if member_shape.type_name == 'timestamp': + # Event stream timestamps are an in milleseconds so we + # divide by 1000 to convert to seconds. + value = self._timestamp_parser(value / 1000.0) + final_parsed[name] = value + + def _initial_body_parse(self, body_contents): + # This method should do the initial xml/json parsing of the + # body. We still need to walk the parsed body in order + # to convert types, but this method will do the first round + # of parsing. + raise NotImplementedError("_initial_body_parse") + + +class EventStreamJSONParser(BaseEventStreamParser, BaseJSONParser): + def _initial_body_parse(self, body_contents): + return self._parse_body_as_json(body_contents) + + +class EventStreamXMLParser(BaseEventStreamParser, BaseXMLResponseParser): + def _initial_body_parse(self, xml_string): + if not xml_string: + return ETree.Element('') + return self._parse_xml_string_to_dom(xml_string) + + +class EventStreamCBORParser(BaseEventStreamParser, BaseCBORParser): + def _initial_body_parse(self, body_contents): + if body_contents == b'': + return {} + return self.parse_data_item( + self.get_peekable_stream_from_bytes(body_contents) + ) + + +class JSONParser(BaseJSONParser): + EVENT_STREAM_PARSER_CLS = EventStreamJSONParser + + """Response parser for the "json" protocol.""" + + def _do_parse(self, response, shape): + parsed = {} + if shape is not None: + event_name = shape.event_stream_name + if event_name: + parsed = self._handle_event_stream(response, shape, event_name) + else: + parsed = self._handle_json_body(response['body'], shape) + self._inject_response_metadata(parsed, response['headers']) + return parsed + + def _do_modeled_error_parse(self, response, shape): + return self._handle_json_body(response['body'], shape) + + def _handle_event_stream(self, response, shape, event_name): + event_stream_shape = shape.members[event_name] + event_stream = self._create_event_stream(response, event_stream_shape) + try: + event = event_stream.get_initial_response() + except NoInitialResponseError: + error_msg = 'First event was not of type initial-response' + raise ResponseParserError(error_msg) + parsed = self._handle_json_body(event.payload, shape) + parsed[event_name] = event_stream + return parsed + + def _handle_json_body(self, raw_body, shape): + # The json.loads() gives us the primitive JSON types, + # but we need to traverse the parsed JSON data to convert + # to richer types (blobs, timestamps, etc. + parsed_json = self._parse_body_as_json(raw_body) + return self._parse_shape(shape, parsed_json) + + +class BaseRestParser(ResponseParser): + def _do_parse(self, response, shape): + final_parsed = {} + final_parsed['ResponseMetadata'] = self._populate_response_metadata( + response + ) + self._add_modeled_parse(response, shape, final_parsed) + return final_parsed + + def _add_modeled_parse(self, response, shape, final_parsed): + if shape is None: + return final_parsed + member_shapes = shape.members + self._parse_non_payload_attrs( + response, shape, member_shapes, final_parsed + ) + self._parse_payload(response, shape, member_shapes, final_parsed) + + def _do_modeled_error_parse(self, response, shape): + final_parsed = {} + self._add_modeled_parse(response, shape, final_parsed) + return final_parsed + + def _populate_response_metadata(self, response): + metadata = {} + headers = response['headers'] + if 'x-amzn-requestid' in headers: + metadata['RequestId'] = headers['x-amzn-requestid'] + elif 'x-amz-request-id' in headers: + metadata['RequestId'] = headers['x-amz-request-id'] + # HostId is what it's called whenever this value is returned + # in an XML response body, so to be consistent, we'll always + # call is HostId. + metadata['HostId'] = headers.get('x-amz-id-2', '') + return metadata + + def _parse_payload(self, response, shape, member_shapes, final_parsed): + if 'payload' in shape.serialization: + # If a payload is specified in the output shape, then only that + # shape is used for the body payload. + payload_member_name = shape.serialization['payload'] + body_shape = member_shapes[payload_member_name] + if body_shape.serialization.get('eventstream'): + body = self._create_event_stream(response, body_shape) + final_parsed[payload_member_name] = body + elif body_shape.type_name in ['string', 'blob']: + # This is a stream + body = response['body'] + if isinstance(body, bytes): + body = body.decode(self.DEFAULT_ENCODING) + final_parsed[payload_member_name] = body + else: + original_parsed = self._initial_body_parse(response['body']) + final_parsed[payload_member_name] = self._parse_shape( + body_shape, original_parsed + ) + else: + original_parsed = self._initial_body_parse(response['body']) + body_parsed = self._parse_shape(shape, original_parsed) + final_parsed.update(body_parsed) + + def _parse_non_payload_attrs( + self, response, shape, member_shapes, final_parsed + ): + headers = response['headers'] + for name in member_shapes: + member_shape = member_shapes[name] + location = member_shape.serialization.get('location') + if location is None: + continue + elif location == 'statusCode': + final_parsed[name] = self._parse_shape( + member_shape, response['status_code'] + ) + elif location == 'headers': + final_parsed[name] = self._parse_header_map( + member_shape, headers + ) + elif location == 'header': + header_name = member_shape.serialization.get('name', name) + if header_name in headers: + final_parsed[name] = self._parse_shape( + member_shape, headers[header_name] + ) + + def _parse_header_map(self, shape, headers): + # Note that headers are case insensitive, so we .lower() + # all header names and header prefixes. + parsed = {} + prefix = shape.serialization.get('name', '').lower() + for header_name in headers: + if header_name.lower().startswith(prefix): + # The key name inserted into the parsed hash + # strips off the prefix. + name = header_name[len(prefix) :] + parsed[name] = headers[header_name] + return parsed + + def _initial_body_parse(self, body_contents): + # This method should do the initial xml/json parsing of the + # body. We still need to walk the parsed body in order + # to convert types, but this method will do the first round + # of parsing. + raise NotImplementedError("_initial_body_parse") + + def _handle_string(self, shape, value): + parsed = value + if is_json_value_header(shape): + decoded = base64.b64decode(value).decode(self.DEFAULT_ENCODING) + parsed = json.loads(decoded) + return parsed + + def _handle_list(self, shape, node): + location = shape.serialization.get('location') + if location == 'header' and not isinstance(node, list): + # List in headers may be a comma separated string as per RFC7230 + node = [e.strip() for e in node.split(',')] + return super()._handle_list(shape, node) + + +class BaseRpcV2Parser(ResponseParser): + def _do_parse(self, response, shape): + parsed = {} + if shape is not None: + event_stream_name = shape.event_stream_name + if event_stream_name: + parsed = self._handle_event_stream( + response, shape, event_stream_name + ) + else: + parsed = {} + self._parse_payload(response, shape, parsed) + parsed['ResponseMetadata'] = self._populate_response_metadata( + response + ) + return parsed + + def _add_modeled_parse(self, response, shape, final_parsed): + if shape is None: + return final_parsed + self._parse_payload(response, shape, final_parsed) + + def _do_modeled_error_parse(self, response, shape): + final_parsed = {} + self._add_modeled_parse(response, shape, final_parsed) + return final_parsed + + def _populate_response_metadata(self, response): + metadata = {} + headers = response['headers'] + if 'x-amzn-requestid' in headers: + metadata['RequestId'] = headers['x-amzn-requestid'] + return metadata + + def _handle_structure(self, shape, node): + parsed = {} + members = shape.members + if shape.is_tagged_union: + cleaned_value = node.copy() + cleaned_value.pop("__type", None) + cleaned_value = { + k: v for k, v in cleaned_value.items() if v is not None + } + if len(cleaned_value) != 1: + error_msg = ( + "Invalid service response: %s must have one and only " + "one member set." + ) + raise ResponseParserError(error_msg % shape.name) + for member_name in members: + member_shape = members[member_name] + member_node = node.get(member_name) + if member_node is not None: + parsed[member_name] = self._parse_shape( + member_shape, member_node + ) + return parsed + + def _parse_payload(self, response, shape, final_parsed): + original_parsed = self._initial_body_parse(response['body']) + body_parsed = self._parse_shape(shape, original_parsed) + final_parsed.update(body_parsed) + + def _initial_body_parse(self, body_contents): + # This method should do the initial parsing of the + # body. We still need to walk the parsed body in order + # to convert types, but this method will do the first round + # of parsing. + raise NotImplementedError("_initial_body_parse") + + +class RestJSONParser(BaseRestParser, BaseJSONParser): + EVENT_STREAM_PARSER_CLS = EventStreamJSONParser + + def _initial_body_parse(self, body_contents): + return self._parse_body_as_json(body_contents) + + def _do_error_parse(self, response, shape): + error = super()._do_error_parse(response, shape) + self._inject_error_code(error, response) + return error + + def _inject_error_code(self, error, response): + # The "Code" value can come from either a response + # header or a value in the JSON body. + body = self._initial_body_parse(response['body']) + code = None + if 'x-amzn-errortype' in response['headers']: + code = response['headers']['x-amzn-errortype'] + elif 'code' in body or 'Code' in body: + code = body.get('code', body.get('Code', '')) + if code is None: + return + if isinstance(code, str): + code = code.split(':', 1)[0].rsplit('#', 1)[-1] + error['Error']['Code'] = code + + def _handle_boolean(self, shape, value): + return ensure_boolean(value) + + def _handle_integer(self, shape, value): + return int(value) + + def _handle_float(self, shape, value): + return float(value) + + _handle_long = _handle_integer + _handle_double = _handle_float + + +class RpcV2CBORParser(BaseRpcV2Parser, BaseCBORParser): + EVENT_STREAM_PARSER_CLS = EventStreamCBORParser + + def _initial_body_parse(self, body_contents): + if body_contents == b'': + return body_contents + body_contents_stream = self.get_peekable_stream_from_bytes( + body_contents + ) + return self.parse_data_item(body_contents_stream) + + def _do_error_parse(self, response, shape): + body = self._initial_body_parse(response['body']) + error = { + "Error": { + "Message": body.get('message', body.get('Message', '')), + "Code": '', + }, + "ResponseMetadata": {}, + } + headers = response['headers'] + + code = body.get('__type') + if code is None: + response_code = response.get('status_code') + if response_code is not None: + code = str(response_code) + if code is not None: + if ':' in code: + code = code.split(':', 1)[0] + if '#' in code: + code = code.rsplit('#', 1)[1] + if 'x-amzn-query-error' in headers: + code = self._do_query_compatible_error_parse( + code, headers, error + ) + error['Error']['Code'] = code + if 'x-amzn-requestid' in headers: + error.setdefault('ResponseMetadata', {})['RequestId'] = headers[ + 'x-amzn-requestid' + ] + return error + + def _handle_event_stream(self, response, shape, event_name): + event_stream_shape = shape.members[event_name] + event_stream = self._create_event_stream(response, event_stream_shape) + try: + event = event_stream.get_initial_response() + except NoInitialResponseError: + error_msg = 'First event was not of type initial-response' + raise ResponseParserError(error_msg) + parsed = self._initial_body_parse(event.payload) + parsed[event_name] = event_stream + return parsed + + +class RestXMLParser(BaseRestParser, BaseXMLResponseParser): + EVENT_STREAM_PARSER_CLS = EventStreamXMLParser + + def _initial_body_parse(self, xml_string): + if not xml_string: + return ETree.Element('') + return self._parse_xml_string_to_dom(xml_string) + + def _do_error_parse(self, response, shape): + # We're trying to be service agnostic here, but S3 does have a slightly + # different response structure for its errors compared to other + # rest-xml serivces (route53/cloudfront). We handle this by just + # trying to parse both forms. + # First: + # + # + # Sender + # InvalidInput + # Invalid resource type: foo + # + # request-id + # + if response['body']: + # If the body ends up being invalid xml, the xml parser should not + # blow up. It should at least try to pull information about the + # the error response from other sources like the HTTP status code. + try: + return self._parse_error_from_body(response) + except ResponseParserError: + LOG.debug( + 'Exception caught when parsing error response body:', + exc_info=True, + ) + return self._parse_error_from_http_status(response) + + def _parse_error_from_http_status(self, response): + return { + 'Error': { + 'Code': str(response['status_code']), + 'Message': http.client.responses.get( + response['status_code'], '' + ), + }, + 'ResponseMetadata': { + 'RequestId': response['headers'].get('x-amz-request-id', ''), + 'HostId': response['headers'].get('x-amz-id-2', ''), + }, + } + + def _parse_error_from_body(self, response): + xml_contents = response['body'] + root = self._parse_xml_string_to_dom(xml_contents) + parsed = self._build_name_to_xml_node(root) + self._replace_nodes(parsed) + if root.tag == 'Error': + # This is an S3 error response. First we'll populate the + # response metadata. + metadata = self._populate_response_metadata(response) + # The RequestId and the HostId are already in the + # ResponseMetadata, but are also duplicated in the XML + # body. We don't need these values in both places, + # we'll just remove them from the parsed XML body. + parsed.pop('RequestId', '') + parsed.pop('HostId', '') + return {'Error': parsed, 'ResponseMetadata': metadata} + elif 'RequestId' in parsed: + # Other rest-xml services: + parsed['ResponseMetadata'] = {'RequestId': parsed.pop('RequestId')} + default = {'Error': {'Message': '', 'Code': ''}} + merge_dicts(default, parsed) + return default + + @_text_content + def _handle_string(self, shape, text): + text = super()._handle_string(shape, text) + return text + + +PROTOCOL_PARSERS = { + 'ec2': EC2QueryParser, + 'query': QueryParser, + 'json': JSONParser, + 'rest-json': RestJSONParser, + 'rest-xml': RestXMLParser, + 'smithy-rpc-v2-cbor': RpcV2CBORParser, +} diff --git a/venv/lib/python3.12/site-packages/botocore/plugin.py b/venv/lib/python3.12/site-packages/botocore/plugin.py new file mode 100644 index 0000000..7556152 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/plugin.py @@ -0,0 +1,84 @@ +# Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +""" +NOTE: This module is considered private and is subject to abrupt breaking +changes without prior announcement. Please do not use it directly. +""" + +import importlib +import logging +import os +from contextvars import ContextVar +from dataclasses import dataclass + +log = logging.getLogger(__name__) + + +@dataclass +class PluginContext: + """ + Encapsulation of plugins tracked within the `_plugin_context` context variable. + """ + + plugins: str | None = None + + +_plugin_context = ContextVar("_plugin_context") + + +def get_plugin_context(): + """Get the current `_plugin_context` context variable if set, else None.""" + return _plugin_context.get(None) + + +def set_plugin_context(ctx): + """Set the current `_plugin_context` context variable.""" + token = _plugin_context.set(ctx) + return token + + +def reset_plugin_context(token): + """Reset the current `_plugin_context` context variable.""" + _plugin_context.reset(token) + + +def get_botocore_plugins(): + context = get_plugin_context() + if context is not None: + plugins = context.plugins + if plugins is None: + context.plugins = os.environ.get('BOTOCORE_EXPERIMENTAL__PLUGINS') + else: + return plugins + return os.environ.get('BOTOCORE_EXPERIMENTAL__PLUGINS') + + +def load_client_plugins(client, plugins): + for plugin_name, module_name in plugins.items(): + log.debug( + "Importing client plugin %s from module %s", + plugin_name, + module_name, + ) + try: + module = importlib.import_module(module_name) + module.initialize_client_plugin(client) + except ModuleNotFoundError: + log.debug( + "Failed to locate the following plugin module: %s.", + plugin_name, + ) + except Exception as e: + log.debug( + "Error raised during the loading of %s: %s", plugin_name, e + ) diff --git a/venv/lib/python3.12/site-packages/botocore/regions.py b/venv/lib/python3.12/site-packages/botocore/regions.py new file mode 100644 index 0000000..e1144b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/regions.py @@ -0,0 +1,898 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Resolves regions and endpoints. + +This module implements endpoint resolution, including resolving endpoints for a +given service and region and resolving the available endpoints for a service +in a specific AWS partition. +""" + +import copy +import logging +import re +from enum import Enum + +import jmespath + +from botocore import UNSIGNED, xform_name +from botocore.auth import ( + AUTH_TYPE_MAPS, + HAS_CRT, + resolve_auth_scheme_preference, +) +from botocore.crt import CRT_SUPPORTED_AUTH_TYPES +from botocore.endpoint_provider import S3_UNREFERENCED_PARAMS, EndpointProvider +from botocore.exceptions import ( + EndpointProviderError, + EndpointVariantError, + InvalidEndpointConfigurationError, + InvalidHostLabelError, + MissingDependencyException, + NoRegionError, + ParamValidationError, + UnknownEndpointResolutionBuiltInName, + UnknownRegionError, + UnknownSignatureVersionError, + UnsupportedS3AccesspointConfigurationError, + UnsupportedS3ConfigurationError, + UnsupportedS3ControlArnError, + UnsupportedS3ControlConfigurationError, +) +from botocore.useragent import register_feature_id +from botocore.utils import ensure_boolean, instance_cache + +LOG = logging.getLogger(__name__) +DEFAULT_URI_TEMPLATE = '{service}.{region}.{dnsSuffix}' # noqa +DEFAULT_SERVICE_DATA = {'endpoints': {}} + + +class BaseEndpointResolver: + """Resolves regions and endpoints. Must be subclassed.""" + + def construct_endpoint(self, service_name, region_name=None): + """Resolves an endpoint for a service and region combination. + + :type service_name: string + :param service_name: Name of the service to resolve an endpoint for + (e.g., s3) + + :type region_name: string + :param region_name: Region/endpoint name to resolve (e.g., us-east-1) + if no region is provided, the first found partition-wide endpoint + will be used if available. + + :rtype: dict + :return: Returns a dict containing the following keys: + - partition: (string, required) Resolved partition name + - endpointName: (string, required) Resolved endpoint name + - hostname: (string, required) Hostname to use for this endpoint + - sslCommonName: (string) sslCommonName to use for this endpoint. + - credentialScope: (dict) Signature version 4 credential scope + - region: (string) region name override when signing. + - service: (string) service name override when signing. + - signatureVersions: (list) A list of possible signature + versions, including s3, v4, v2, and s3v4 + - protocols: (list) A list of supported protocols + (e.g., http, https) + - ...: Other keys may be included as well based on the metadata + """ + raise NotImplementedError + + def get_available_partitions(self): + """Lists the partitions available to the endpoint resolver. + + :return: Returns a list of partition names (e.g., ["aws", "aws-cn"]). + """ + raise NotImplementedError + + def get_available_endpoints( + self, service_name, partition_name='aws', allow_non_regional=False + ): + """Lists the endpoint names of a particular partition. + + :type service_name: string + :param service_name: Name of a service to list endpoint for (e.g., s3) + + :type partition_name: string + :param partition_name: Name of the partition to limit endpoints to. + (e.g., aws for the public AWS endpoints, aws-cn for AWS China + endpoints, aws-us-gov for AWS GovCloud (US) Endpoints, etc. + + :type allow_non_regional: bool + :param allow_non_regional: Set to True to include endpoints that are + not regional endpoints (e.g., s3-external-1, + fips-us-gov-west-1, etc). + :return: Returns a list of endpoint names (e.g., ["us-east-1"]). + """ + raise NotImplementedError + + +class EndpointResolver(BaseEndpointResolver): + """Resolves endpoints based on partition endpoint metadata""" + + _UNSUPPORTED_DUALSTACK_PARTITIONS = ['aws-iso', 'aws-iso-b'] + + def __init__(self, endpoint_data, uses_builtin_data=False): + """ + :type endpoint_data: dict + :param endpoint_data: A dict of partition data. + + :type uses_builtin_data: boolean + :param uses_builtin_data: Whether the endpoint data originates in the + package's data directory. + """ + if 'partitions' not in endpoint_data: + raise ValueError('Missing "partitions" in endpoint data') + self._endpoint_data = endpoint_data + self.uses_builtin_data = uses_builtin_data + + def get_service_endpoints_data(self, service_name, partition_name='aws'): + for partition in self._endpoint_data['partitions']: + if partition['partition'] != partition_name: + continue + services = partition['services'] + if service_name not in services: + continue + return services[service_name]['endpoints'] + + def get_available_partitions(self): + result = [] + for partition in self._endpoint_data['partitions']: + result.append(partition['partition']) + return result + + def get_available_endpoints( + self, + service_name, + partition_name='aws', + allow_non_regional=False, + endpoint_variant_tags=None, + ): + result = [] + for partition in self._endpoint_data['partitions']: + if partition['partition'] != partition_name: + continue + services = partition['services'] + if service_name not in services: + continue + service_endpoints = services[service_name]['endpoints'] + for endpoint_name in service_endpoints: + is_regional_endpoint = endpoint_name in partition['regions'] + # Only regional endpoints can be modeled with variants + if endpoint_variant_tags and is_regional_endpoint: + variant_data = self._retrieve_variant_data( + service_endpoints[endpoint_name], endpoint_variant_tags + ) + if variant_data: + result.append(endpoint_name) + elif allow_non_regional or is_regional_endpoint: + result.append(endpoint_name) + return result + + def get_partition_dns_suffix( + self, partition_name, endpoint_variant_tags=None + ): + for partition in self._endpoint_data['partitions']: + if partition['partition'] == partition_name: + if endpoint_variant_tags: + variant = self._retrieve_variant_data( + partition.get('defaults'), endpoint_variant_tags + ) + if variant and 'dnsSuffix' in variant: + return variant['dnsSuffix'] + else: + return partition['dnsSuffix'] + return None + + def construct_endpoint( + self, + service_name, + region_name=None, + partition_name=None, + use_dualstack_endpoint=False, + use_fips_endpoint=False, + ): + if ( + service_name == 's3' + and use_dualstack_endpoint + and region_name is None + ): + region_name = 'us-east-1' + + if partition_name is not None: + valid_partition = None + for partition in self._endpoint_data['partitions']: + if partition['partition'] == partition_name: + valid_partition = partition + + if valid_partition is not None: + result = self._endpoint_for_partition( + valid_partition, + service_name, + region_name, + use_dualstack_endpoint, + use_fips_endpoint, + True, + ) + return result + return None + + # Iterate over each partition until a match is found. + for partition in self._endpoint_data['partitions']: + if use_dualstack_endpoint and ( + partition['partition'] + in self._UNSUPPORTED_DUALSTACK_PARTITIONS + ): + continue + result = self._endpoint_for_partition( + partition, + service_name, + region_name, + use_dualstack_endpoint, + use_fips_endpoint, + ) + if result: + return result + + def get_partition_for_region(self, region_name): + for partition in self._endpoint_data['partitions']: + if self._region_match(partition, region_name): + return partition['partition'] + raise UnknownRegionError( + region_name=region_name, + error_msg='No partition found for provided region_name.', + ) + + def _endpoint_for_partition( + self, + partition, + service_name, + region_name, + use_dualstack_endpoint, + use_fips_endpoint, + force_partition=False, + ): + partition_name = partition["partition"] + if ( + use_dualstack_endpoint + and partition_name in self._UNSUPPORTED_DUALSTACK_PARTITIONS + ): + error_msg = ( + "Dualstack endpoints are currently not supported" + f" for {partition_name} partition" + ) + raise EndpointVariantError(tags=['dualstack'], error_msg=error_msg) + + # Get the service from the partition, or an empty template. + service_data = partition['services'].get( + service_name, DEFAULT_SERVICE_DATA + ) + # Use the partition endpoint if no region is supplied. + if region_name is None: + if 'partitionEndpoint' in service_data: + region_name = service_data['partitionEndpoint'] + else: + raise NoRegionError() + + resolve_kwargs = { + 'partition': partition, + 'service_name': service_name, + 'service_data': service_data, + 'endpoint_name': region_name, + 'use_dualstack_endpoint': use_dualstack_endpoint, + 'use_fips_endpoint': use_fips_endpoint, + } + + # Attempt to resolve the exact region for this partition. + if region_name in service_data['endpoints']: + return self._resolve(**resolve_kwargs) + + # Check to see if the endpoint provided is valid for the partition. + if self._region_match(partition, region_name) or force_partition: + # Use the partition endpoint if set and not regionalized. + partition_endpoint = service_data.get('partitionEndpoint') + is_regionalized = service_data.get('isRegionalized', True) + if partition_endpoint and not is_regionalized: + LOG.debug( + 'Using partition endpoint for %s, %s: %s', + service_name, + region_name, + partition_endpoint, + ) + resolve_kwargs['endpoint_name'] = partition_endpoint + return self._resolve(**resolve_kwargs) + LOG.debug( + 'Creating a regex based endpoint for %s, %s', + service_name, + region_name, + ) + return self._resolve(**resolve_kwargs) + + def _region_match(self, partition, region_name): + if region_name in partition['regions']: + return True + if 'regionRegex' in partition: + return re.compile(partition['regionRegex']).match(region_name) + return False + + def _retrieve_variant_data(self, endpoint_data, tags): + variants = endpoint_data.get('variants', []) + for variant in variants: + if set(variant['tags']) == set(tags): + result = variant.copy() + return result + + def _create_tag_list(self, use_dualstack_endpoint, use_fips_endpoint): + tags = [] + if use_dualstack_endpoint: + tags.append('dualstack') + if use_fips_endpoint: + tags.append('fips') + return tags + + def _resolve_variant( + self, tags, endpoint_data, service_defaults, partition_defaults + ): + result = {} + for variants in [endpoint_data, service_defaults, partition_defaults]: + variant = self._retrieve_variant_data(variants, tags) + if variant: + self._merge_keys(variant, result) + return result + + def _resolve( + self, + partition, + service_name, + service_data, + endpoint_name, + use_dualstack_endpoint, + use_fips_endpoint, + ): + endpoint_data = service_data.get('endpoints', {}).get( + endpoint_name, {} + ) + + if endpoint_data.get('deprecated'): + LOG.warning( + 'Client is configured with the deprecated endpoint: %s', + endpoint_name, + ) + + service_defaults = service_data.get('defaults', {}) + partition_defaults = partition.get('defaults', {}) + tags = self._create_tag_list(use_dualstack_endpoint, use_fips_endpoint) + + if tags: + result = self._resolve_variant( + tags, endpoint_data, service_defaults, partition_defaults + ) + if result == {}: + error_msg = ( + f"Endpoint does not exist for {service_name} " + f"in region {endpoint_name}" + ) + raise EndpointVariantError(tags=tags, error_msg=error_msg) + self._merge_keys(endpoint_data, result) + else: + result = endpoint_data + + # If dnsSuffix has not already been consumed from a variant definition + if 'dnsSuffix' not in result: + result['dnsSuffix'] = partition['dnsSuffix'] + + result['partition'] = partition['partition'] + result['endpointName'] = endpoint_name + + # Merge in the service defaults then the partition defaults. + self._merge_keys(service_defaults, result) + self._merge_keys(partition_defaults, result) + + result['hostname'] = self._expand_template( + partition, + result['hostname'], + service_name, + endpoint_name, + result['dnsSuffix'], + ) + if 'sslCommonName' in result: + result['sslCommonName'] = self._expand_template( + partition, + result['sslCommonName'], + service_name, + endpoint_name, + result['dnsSuffix'], + ) + + return result + + def _merge_keys(self, from_data, result): + for key in from_data: + if key not in result: + result[key] = from_data[key] + + def _expand_template( + self, partition, template, service_name, endpoint_name, dnsSuffix + ): + return template.format( + service=service_name, region=endpoint_name, dnsSuffix=dnsSuffix + ) + + +class EndpointResolverBuiltins(str, Enum): + # The AWS Region configured for the SDK client (str) + AWS_REGION = "AWS::Region" + # Whether the UseFIPSEndpoint configuration option has been enabled for + # the SDK client (bool) + AWS_USE_FIPS = "AWS::UseFIPS" + # Whether the UseDualStackEndpoint configuration option has been enabled + # for the SDK client (bool) + AWS_USE_DUALSTACK = "AWS::UseDualStack" + # Whether the global endpoint should be used with STS, rather than the + # regional endpoint for us-east-1 (bool) + AWS_STS_USE_GLOBAL_ENDPOINT = "AWS::STS::UseGlobalEndpoint" + # Whether the global endpoint should be used with S3, rather than the + # regional endpoint for us-east-1 (bool) + AWS_S3_USE_GLOBAL_ENDPOINT = "AWS::S3::UseGlobalEndpoint" + # Whether S3 Transfer Acceleration has been requested (bool) + AWS_S3_ACCELERATE = "AWS::S3::Accelerate" + # Whether S3 Force Path Style has been enabled (bool) + AWS_S3_FORCE_PATH_STYLE = "AWS::S3::ForcePathStyle" + # Whether to use the ARN region or raise an error when ARN and client + # region differ (for s3 service only, bool) + AWS_S3_USE_ARN_REGION = "AWS::S3::UseArnRegion" + # Whether to use S3 Express session authentication, or fallback to default + # authentication (for s3 service only, bool). + AWS_S3_DISABLE_EXPRESS_SESSION_AUTH = ( + "AWS::S3::DisableS3ExpressSessionAuth" + ) + # Whether to use the ARN region or raise an error when ARN and client + # region differ (for s3-control service only, bool) + AWS_S3CONTROL_USE_ARN_REGION = 'AWS::S3Control::UseArnRegion' + # Whether multi-region access points (MRAP) should be disabled (bool) + AWS_S3_DISABLE_MRAP = "AWS::S3::DisableMultiRegionAccessPoints" + # Whether a custom endpoint has been configured (str) + SDK_ENDPOINT = "SDK::Endpoint" + # An AWS account ID that can be optionally configured for the SDK client (str) + ACCOUNT_ID = "AWS::Auth::AccountId" + # Whether an endpoint should include an account ID (str) + ACCOUNT_ID_ENDPOINT_MODE = "AWS::Auth::AccountIdEndpointMode" + + +class EndpointRulesetResolver: + """Resolves endpoints using a service's endpoint ruleset""" + + def __init__( + self, + endpoint_ruleset_data, + partition_data, + service_model, + builtins, + client_context, + event_emitter, + use_ssl=True, + requested_auth_scheme=None, + auth_scheme_preference=None, + ): + self._provider = EndpointProvider( + ruleset_data=endpoint_ruleset_data, + partition_data=partition_data, + excluded_params=( + S3_UNREFERENCED_PARAMS + if service_model.service_name == 's3' + else None + ), + ) + self._param_definitions = self._provider.ruleset.parameters + self._service_model = service_model + self._builtins = builtins + self._client_context = client_context + self._event_emitter = event_emitter + self._use_ssl = use_ssl + self._requested_auth_scheme = requested_auth_scheme + self._auth_scheme_preference = auth_scheme_preference + self._instance_cache = {} + + def construct_endpoint( + self, + operation_model, + call_args, + request_context, + ): + """Invokes the provider with params defined in the service's ruleset""" + if call_args is None: + call_args = {} + + if request_context is None: + request_context = {} + + provider_params = self._get_provider_params( + operation_model, call_args, request_context + ) + LOG.debug( + 'Calling endpoint provider with parameters: %s', provider_params + ) + try: + provider_result = self._provider.resolve_endpoint( + **provider_params + ) + except EndpointProviderError as ex: + botocore_exception = self.ruleset_error_to_botocore_exception( + ex, provider_params + ) + if botocore_exception is None: + raise + else: + raise botocore_exception from ex + LOG.debug('Endpoint provider result: %s', provider_result.url) + + # The endpoint provider does not support non-secure transport. + if ( + not self._use_ssl + and provider_result.url.startswith('https://') + and 'Endpoint' not in provider_params + ): + provider_result = provider_result._replace( + url=f'http://{provider_result.url[8:]}' + ) + + # Multi-valued headers are not supported in botocore. Replace the list + # of values returned for each header with just its first entry, + # dropping any additionally entries. + provider_result = provider_result._replace( + headers={ + key: val[0] for key, val in provider_result.headers.items() + } + ) + + return provider_result + + def _get_provider_params( + self, operation_model, call_args, request_context + ): + """Resolve a value for each parameter defined in the service's ruleset + + The resolution order for parameter values is: + 1. Operation-specific static context values from the service definition + 2. Operation-specific dynamic context values from API parameters + 3. Client-specific context parameters + 4. Built-in values such as region, FIPS usage, ... + """ + provider_params = {} + # Builtin values can be customized for each operation by hooks + # subscribing to the ``before-endpoint-resolution.*`` event. + customized_builtins = self._get_customized_builtins( + operation_model, call_args, request_context + ) + for param_name, param_def in self._param_definitions.items(): + param_val = self._resolve_param_from_context( + param_name=param_name, + operation_model=operation_model, + call_args=call_args, + ) + if param_val is None and param_def.builtin is not None: + param_val = self._resolve_param_as_builtin( + builtin_name=param_def.builtin, + builtins=customized_builtins, + ) + if param_val is not None: + provider_params[param_name] = param_val + self._register_endpoint_feature_ids(param_name, param_val) + + return provider_params + + def _resolve_param_from_context( + self, param_name, operation_model, call_args + ): + static = self._resolve_param_as_static_context_param( + param_name, operation_model + ) + if static is not None: + return static + dynamic = self._resolve_param_as_dynamic_context_param( + param_name, operation_model, call_args + ) + if dynamic is not None: + return dynamic + operation_context_params = ( + self._resolve_param_as_operation_context_param( + param_name, operation_model, call_args + ) + ) + if operation_context_params is not None: + return operation_context_params + return self._resolve_param_as_client_context_param(param_name) + + def _resolve_param_as_static_context_param( + self, param_name, operation_model + ): + static_ctx_params = self._get_static_context_params(operation_model) + return static_ctx_params.get(param_name) + + def _resolve_param_as_dynamic_context_param( + self, param_name, operation_model, call_args + ): + dynamic_ctx_params = self._get_dynamic_context_params(operation_model) + if param_name in dynamic_ctx_params: + member_name = dynamic_ctx_params[param_name] + return call_args.get(member_name) + + def _resolve_param_as_client_context_param(self, param_name): + client_ctx_params = self._get_client_context_params() + if param_name in client_ctx_params: + client_ctx_varname = client_ctx_params[param_name] + return self._client_context.get(client_ctx_varname) + + def _resolve_param_as_operation_context_param( + self, param_name, operation_model, call_args + ): + operation_ctx_params = operation_model.operation_context_parameters + if param_name in operation_ctx_params: + path = operation_ctx_params[param_name]['path'] + return jmespath.search(path, call_args) + + def _resolve_param_as_builtin(self, builtin_name, builtins): + if builtin_name not in EndpointResolverBuiltins.__members__.values(): + raise UnknownEndpointResolutionBuiltInName(name=builtin_name) + builtin = builtins.get(builtin_name) + if callable(builtin): + return builtin() + return builtin + + @instance_cache + def _get_static_context_params(self, operation_model): + """Mapping of param names to static param value for an operation""" + return { + param.name: param.value + for param in operation_model.static_context_parameters + } + + @instance_cache + def _get_dynamic_context_params(self, operation_model): + """Mapping of param names to member names for an operation""" + return { + param.name: param.member_name + for param in operation_model.context_parameters + } + + @instance_cache + def _get_client_context_params(self): + """Mapping of param names to client configuration variable""" + return { + param.name: xform_name(param.name) + for param in self._service_model.client_context_parameters + } + + def _get_customized_builtins( + self, operation_model, call_args, request_context + ): + service_id = self._service_model.service_id.hyphenize() + customized_builtins = copy.copy(self._builtins) + # Handlers are expected to modify the builtins dict in place. + self._event_emitter.emit( + f'before-endpoint-resolution.{service_id}', + builtins=customized_builtins, + model=operation_model, + params=call_args, + context=request_context, + ) + return customized_builtins + + def auth_schemes_to_signing_ctx(self, auth_schemes): + """Convert an Endpoint's authSchemes property to a signing_context dict + + :type auth_schemes: list + :param auth_schemes: A list of dictionaries taken from the + ``authSchemes`` property of an Endpoint object returned by + ``EndpointProvider``. + + :rtype: str, dict + :return: Tuple of auth type string (to be used in + ``request_context['auth_type']``) and signing context dict (for use + in ``request_context['signing']``). + """ + if not isinstance(auth_schemes, list) or len(auth_schemes) == 0: + raise TypeError("auth_schemes must be a non-empty list.") + + LOG.debug( + 'Selecting from endpoint provider\'s list of auth schemes: %s. ' + 'User selected auth scheme is: "%s"', + ', '.join([f'"{s.get("name")}"' for s in auth_schemes]), + self._requested_auth_scheme, + ) + + if self._requested_auth_scheme == UNSIGNED: + return 'none', {} + + available_ruleset_names = [ + s['name'].split('#')[-1] for s in auth_schemes + ] + auth_schemes = [ + {**scheme, 'name': self._strip_sig_prefix(scheme['name'])} + for scheme in auth_schemes + ] + if self._requested_auth_scheme is not None: + try: + # Use the first scheme that matches the requested scheme, + # after accounting for naming differences between botocore and + # endpoint rulesets. Keep the requested name. + name, scheme = next( + (self._requested_auth_scheme, s) + for s in auth_schemes + if self._does_botocore_authname_match_ruleset_authname( + self._requested_auth_scheme, s['name'] + ) + ) + except StopIteration: + # For legacy signers, no match will be found. Do not raise an + # exception, instead default to the logic in botocore + # customizations. + return None, {} + elif self._auth_scheme_preference is not None: + prefs = self._auth_scheme_preference.split(',') + auth_schemes_by_auth_type = { + self._strip_sig_prefix(s['name'].split('#')[-1]): s + for s in auth_schemes + } + name = resolve_auth_scheme_preference( + prefs, available_ruleset_names + ) + scheme = auth_schemes_by_auth_type[name] + else: + try: + name, scheme = next( + (s['name'], s) + for s in auth_schemes + if s['name'] in AUTH_TYPE_MAPS + ) + except StopIteration: + # If no auth scheme was specifically requested and an + # authSchemes list is present in the Endpoint object but none + # of the entries are supported, raise an exception. + fixable_with_crt = False + auth_type_options = [s['name'] for s in auth_schemes] + if not HAS_CRT: + fixable_with_crt = any( + scheme in CRT_SUPPORTED_AUTH_TYPES + for scheme in auth_type_options + ) + + if fixable_with_crt: + raise MissingDependencyException( + msg='This operation requires an additional dependency.' + ' Use pip install botocore[crt] before proceeding.' + ) + else: + raise UnknownSignatureVersionError( + signature_version=', '.join(auth_type_options) + ) + + signing_context = {} + if 'signingRegion' in scheme: + signing_context['region'] = scheme['signingRegion'] + elif 'signingRegionSet' in scheme: + if len(scheme['signingRegionSet']) > 0: + signing_context['region'] = ','.join( + scheme['signingRegionSet'] + ) + if 'signingName' in scheme: + signing_context.update(signing_name=scheme['signingName']) + if 'disableDoubleEncoding' in scheme: + signing_context['disableDoubleEncoding'] = ensure_boolean( + scheme['disableDoubleEncoding'] + ) + + LOG.debug( + 'Selected auth type "%s" as "%s" with signing context params: %s', + scheme['name'], # original name without "sig" + name, # chosen name can differ when `signature_version` is set + signing_context, + ) + return name, signing_context + + def _strip_sig_prefix(self, auth_name): + """Normalize auth type names by removing any "sig" prefix""" + return auth_name[3:] if auth_name.startswith('sig') else auth_name + + def _does_botocore_authname_match_ruleset_authname(self, botoname, rsname): + """ + Whether a valid string provided as signature_version parameter for + client construction refers to the same auth methods as a string + returned by the endpoint ruleset provider. This accounts for: + + * The ruleset prefixes auth names with "sig" + * The s3 and s3control rulesets don't distinguish between v4[a] and + s3v4[a] signers + * The v2, v3, and HMAC v1 based signers (s3, s3-*) are botocore legacy + features and do not exist in the rulesets + * Only characters up to the first dash are considered + + Example matches: + * v4, sigv4 + * v4, v4 + * s3v4, sigv4 + * s3v7, sigv7 (hypothetical example) + * s3v4a, sigv4a + * s3v4-query, sigv4 + + Example mismatches: + * v4a, sigv4 + * s3, sigv4 + * s3-presign-post, sigv4 + """ + rsname = self._strip_sig_prefix(rsname) + botoname = botoname.split('-')[0] + if botoname != 's3' and botoname.startswith('s3'): + botoname = botoname[2:] + return rsname == botoname + + def ruleset_error_to_botocore_exception(self, ruleset_exception, params): + """Attempts to translate ruleset errors to pre-existing botocore + exception types by string matching exception strings. + """ + msg = ruleset_exception.kwargs.get('msg') + if msg is None: + return + + if msg.startswith('Invalid region in ARN: '): + # Example message: + # "Invalid region in ARN: `us-we$t-2` (invalid DNS name)" + try: + label = msg.split('`')[1] + except IndexError: + label = msg + return InvalidHostLabelError(label=label) + + service_name = self._service_model.service_name + if service_name == 's3': + if ( + msg == 'S3 Object Lambda does not support S3 Accelerate' + or msg == 'Accelerate cannot be used with FIPS' + ): + return UnsupportedS3ConfigurationError(msg=msg) + if ( + msg.startswith('S3 Outposts does not support') + or msg.startswith('S3 MRAP does not support') + or msg.startswith('S3 Object Lambda does not support') + or msg.startswith('Access Points do not support') + or msg.startswith('Invalid configuration:') + or msg.startswith('Client was configured for partition') + ): + return UnsupportedS3AccesspointConfigurationError(msg=msg) + if msg.lower().startswith('invalid arn:'): + return ParamValidationError(report=msg) + if service_name == 's3control': + if msg.startswith('Invalid ARN:'): + arn = params.get('Bucket') + return UnsupportedS3ControlArnError(arn=arn, msg=msg) + if msg.startswith('Invalid configuration:') or msg.startswith( + 'Client was configured for partition' + ): + return UnsupportedS3ControlConfigurationError(msg=msg) + if msg == "AccountId is required but not set": + return ParamValidationError(report=msg) + if service_name == 'events': + if msg.startswith( + 'Invalid Configuration: FIPS is not supported with ' + 'EventBridge multi-region endpoints.' + ): + return InvalidEndpointConfigurationError(msg=msg) + if msg == 'EndpointId must be a valid host label.': + return InvalidEndpointConfigurationError(msg=msg) + return None + + def _register_endpoint_feature_ids(self, param_name, param_val): + if param_name == 'AccountIdEndpointMode': + register_feature_id(f'ACCOUNT_ID_MODE_{param_val.upper()}') + elif param_name == 'AccountId': + register_feature_id('RESOLVED_ACCOUNT_ID') diff --git a/venv/lib/python3.12/site-packages/botocore/response.py b/venv/lib/python3.12/site-packages/botocore/response.py new file mode 100644 index 0000000..e123b54 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/response.py @@ -0,0 +1,216 @@ +# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import logging +from io import IOBase + +from urllib3.exceptions import ProtocolError as URLLib3ProtocolError +from urllib3.exceptions import ReadTimeoutError as URLLib3ReadTimeoutError + +from botocore import ( + ScalarTypes, # noqa: F401 + parsers, +) +from botocore.compat import ( + XMLParseError, # noqa: F401 + set_socket_timeout, +) +from botocore.exceptions import ( + IncompleteReadError, + ReadTimeoutError, + ResponseStreamingError, +) +from botocore.hooks import first_non_none_response # noqa + +logger = logging.getLogger(__name__) + + +class StreamingBody(IOBase): + """Wrapper class for an http response body. + + This provides a few additional conveniences that do not exist + in the urllib3 model: + + * Set the timeout on the socket (i.e read() timeouts) + * Auto validation of content length, if the amount of bytes + we read does not match the content length, an exception + is raised. + + """ + + _DEFAULT_CHUNK_SIZE = 1024 + + def __init__(self, raw_stream, content_length): + self._raw_stream = raw_stream + self._content_length = content_length + self._amount_read = 0 + + def __del__(self): + # Extending destructor in order to preserve the underlying raw_stream. + # The ability to add custom cleanup logic introduced in Python3.4+. + # https://www.python.org/dev/peps/pep-0442/ + pass + + def set_socket_timeout(self, timeout): + """Set the timeout seconds on the socket.""" + # The problem we're trying to solve is to prevent .read() calls from + # hanging. This can happen in rare cases. What we'd like to ideally + # do is set a timeout on the .read() call so that callers can retry + # the request. + # Unfortunately, this isn't currently possible in requests. + # See: https://github.com/kennethreitz/requests/issues/1803 + # So what we're going to do is reach into the guts of the stream and + # grab the socket object, which we can set the timeout on. We're + # putting in a check here so in case this interface goes away, we'll + # know. + try: + set_socket_timeout(self._raw_stream, timeout) + except AttributeError: + logger.exception( + "Cannot access the socket object of a streaming response. " + "It's possible the interface has changed." + ) + raise + + def readable(self): + try: + return self._raw_stream.readable() + except AttributeError: + return False + + def read(self, amt=None): + """Read at most amt bytes from the stream. + + If the amt argument is omitted, read all data. + """ + try: + chunk = self._raw_stream.read(amt) + except URLLib3ReadTimeoutError as e: + # TODO: the url will be None as urllib3 isn't setting it yet + raise ReadTimeoutError(endpoint_url=e.url, error=e) + except URLLib3ProtocolError as e: + raise ResponseStreamingError(error=e) + self._amount_read += len(chunk) + if amt is None or (not chunk and amt > 0): + # If the server sends empty contents or + # we ask to read all of the contents, then we know + # we need to verify the content length. + self._verify_content_length() + return chunk + + def readinto(self, b): + """Read bytes into a pre-allocated, writable bytes-like object b, and return the number of bytes read.""" + try: + amount_read = self._raw_stream.readinto(b) + except URLLib3ReadTimeoutError as e: + # TODO: the url will be None as urllib3 isn't setting it yet + raise ReadTimeoutError(endpoint_url=e.url, error=e) + except URLLib3ProtocolError as e: + raise ResponseStreamingError(error=e) + self._amount_read += amount_read + if amount_read == 0 and len(b) > 0: + # If the server sends empty contents then we know we need to verify + # the content length. + self._verify_content_length() + return amount_read + + def readlines(self): + return self._raw_stream.readlines() + + def __iter__(self): + """Return an iterator to yield 1k chunks from the raw stream.""" + return self.iter_chunks(self._DEFAULT_CHUNK_SIZE) + + def __next__(self): + """Return the next 1k chunk from the raw stream.""" + current_chunk = self.read(self._DEFAULT_CHUNK_SIZE) + if current_chunk: + return current_chunk + raise StopIteration() + + def __enter__(self): + return self._raw_stream + + def __exit__(self, type, value, traceback): + self._raw_stream.close() + + next = __next__ + + def iter_lines(self, chunk_size=_DEFAULT_CHUNK_SIZE, keepends=False): + """Return an iterator to yield lines from the raw stream. + + This is achieved by reading chunk of bytes (of size chunk_size) at a + time from the raw stream, and then yielding lines from there. + """ + pending = b'' + for chunk in self.iter_chunks(chunk_size): + lines = (pending + chunk).splitlines(True) + for line in lines[:-1]: + yield line.splitlines(keepends)[0] + pending = lines[-1] + if pending: + yield pending.splitlines(keepends)[0] + + def iter_chunks(self, chunk_size=_DEFAULT_CHUNK_SIZE): + """Return an iterator to yield chunks of chunk_size bytes from the raw + stream. + """ + while True: + current_chunk = self.read(chunk_size) + if current_chunk == b"": + break + yield current_chunk + + def _verify_content_length(self): + # See: https://github.com/kennethreitz/requests/issues/1855 + # Basically, our http library doesn't do this for us, so we have + # to do this ourself. + if self._content_length is not None and self._amount_read != int( + self._content_length + ): + raise IncompleteReadError( + actual_bytes=self._amount_read, + expected_bytes=int(self._content_length), + ) + + def tell(self): + return self._raw_stream.tell() + + def close(self): + """Close the underlying http response stream.""" + self._raw_stream.close() + + +def get_response(operation_model, http_response): + protocol = operation_model.service_model.resolved_protocol + response_dict = { + 'headers': http_response.headers, + 'status_code': http_response.status_code, + } + # TODO: Unfortunately, we have to have error logic here. + # If it looks like an error, in the streaming response case we + # need to actually grab the contents. + if response_dict['status_code'] >= 300: + response_dict['body'] = http_response.content + elif operation_model.has_streaming_output: + response_dict['body'] = StreamingBody( + http_response.raw, response_dict['headers'].get('content-length') + ) + else: + response_dict['body'] = http_response.content + + parser = parsers.create_parser(protocol) + return http_response, parser.parse( + response_dict, operation_model.output_shape + ) diff --git a/venv/lib/python3.12/site-packages/botocore/retries/__init__.py b/venv/lib/python3.12/site-packages/botocore/retries/__init__.py new file mode 100644 index 0000000..a6d6b37 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/retries/__init__.py @@ -0,0 +1,6 @@ +"""New retry v2 handlers. + +This package obsoletes the botocore/retryhandler.py module and contains +new retry logic. + +""" diff --git a/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a583612 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/adaptive.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/adaptive.cpython-312.pyc new file mode 100644 index 0000000..d0376de Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/adaptive.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..c76f0c5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/bucket.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/bucket.cpython-312.pyc new file mode 100644 index 0000000..0ec2154 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/bucket.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/quota.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/quota.cpython-312.pyc new file mode 100644 index 0000000..4973b28 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/quota.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/special.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/special.cpython-312.pyc new file mode 100644 index 0000000..8824ddd Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/special.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/standard.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/standard.cpython-312.pyc new file mode 100644 index 0000000..32c4416 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/standard.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/throttling.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/throttling.cpython-312.pyc new file mode 100644 index 0000000..cd97dc3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/retries/__pycache__/throttling.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/retries/adaptive.py b/venv/lib/python3.12/site-packages/botocore/retries/adaptive.py new file mode 100644 index 0000000..5e638dd --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/retries/adaptive.py @@ -0,0 +1,132 @@ +import logging +import math +import threading + +from botocore.retries import bucket, standard, throttling + +logger = logging.getLogger(__name__) + + +def register_retry_handler(client): + clock = bucket.Clock() + rate_adjustor = throttling.CubicCalculator( + starting_max_rate=0, start_time=clock.current_time() + ) + token_bucket = bucket.TokenBucket(max_rate=1, clock=clock) + rate_clocker = RateClocker(clock) + throttling_detector = standard.ThrottlingErrorDetector( + retry_event_adapter=standard.RetryEventAdapter(), + ) + limiter = ClientRateLimiter( + rate_adjustor=rate_adjustor, + rate_clocker=rate_clocker, + token_bucket=token_bucket, + throttling_detector=throttling_detector, + clock=clock, + ) + client.meta.events.register( + 'before-send', + limiter.on_sending_request, + ) + client.meta.events.register( + 'needs-retry', + limiter.on_receiving_response, + ) + return limiter + + +class ClientRateLimiter: + _MAX_RATE_ADJUST_SCALE = 2.0 + + def __init__( + self, + rate_adjustor, + rate_clocker, + token_bucket, + throttling_detector, + clock, + ): + self._rate_adjustor = rate_adjustor + self._rate_clocker = rate_clocker + self._token_bucket = token_bucket + self._throttling_detector = throttling_detector + self._clock = clock + self._enabled = False + self._lock = threading.Lock() + + def on_sending_request(self, request, **kwargs): + if self._enabled: + self._token_bucket.acquire() + + # Hooked up to needs-retry. + def on_receiving_response(self, **kwargs): + measured_rate = self._rate_clocker.record() + timestamp = self._clock.current_time() + with self._lock: + if not self._throttling_detector.is_throttling_error(**kwargs): + new_rate = self._rate_adjustor.success_received(timestamp) + else: + if not self._enabled: + rate_to_use = measured_rate + else: + rate_to_use = min( + measured_rate, self._token_bucket.max_rate + ) + new_rate = self._rate_adjustor.error_received( + rate_to_use, timestamp + ) + logger.debug( + "Throttling response received, new send rate: %s " + "measured rate: %s, token bucket capacity " + "available: %s", + new_rate, + measured_rate, + self._token_bucket.available_capacity, + ) + self._enabled = True + self._token_bucket.max_rate = min( + new_rate, self._MAX_RATE_ADJUST_SCALE * measured_rate + ) + + +class RateClocker: + """Tracks the rate at which a client is sending a request.""" + + _DEFAULT_SMOOTHING = 0.8 + # Update the rate every _TIME_BUCKET_RANGE seconds. + _TIME_BUCKET_RANGE = 0.5 + + def __init__( + self, + clock, + smoothing=_DEFAULT_SMOOTHING, + time_bucket_range=_TIME_BUCKET_RANGE, + ): + self._clock = clock + self._measured_rate = 0 + self._smoothing = smoothing + self._last_bucket = math.floor(self._clock.current_time()) + self._time_bucket_scale = 1 / self._TIME_BUCKET_RANGE + self._count = 0 + self._lock = threading.Lock() + + def record(self, amount=1): + with self._lock: + t = self._clock.current_time() + bucket = ( + math.floor(t * self._time_bucket_scale) + / self._time_bucket_scale + ) + self._count += amount + if bucket > self._last_bucket: + current_rate = self._count / float(bucket - self._last_bucket) + self._measured_rate = (current_rate * self._smoothing) + ( + self._measured_rate * (1 - self._smoothing) + ) + self._count = 0 + self._last_bucket = bucket + return self._measured_rate + + @property + def measured_rate(self): + return self._measured_rate diff --git a/venv/lib/python3.12/site-packages/botocore/retries/base.py b/venv/lib/python3.12/site-packages/botocore/retries/base.py new file mode 100644 index 0000000..108bfed --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/retries/base.py @@ -0,0 +1,26 @@ +class BaseRetryBackoff: + def delay_amount(self, context): + """Calculate how long we should delay before retrying. + + :type context: RetryContext + + """ + raise NotImplementedError("delay_amount") + + +class BaseRetryableChecker: + """Base class for determining if a retry should happen. + + This base class checks for specific retryable conditions. + A single retryable checker doesn't necessarily indicate a retry + will happen. It's up to the ``RetryPolicy`` to use its + ``BaseRetryableCheckers`` to make the final decision on whether a retry + should happen. + """ + + def is_retryable(self, context): + """Returns True if retryable, False if not. + + :type context: RetryContext + """ + raise NotImplementedError("is_retryable") diff --git a/venv/lib/python3.12/site-packages/botocore/retries/bucket.py b/venv/lib/python3.12/site-packages/botocore/retries/bucket.py new file mode 100644 index 0000000..09d33c7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/retries/bucket.py @@ -0,0 +1,115 @@ +"""This module implements token buckets used for client side throttling.""" + +import threading +import time + +from botocore.exceptions import CapacityNotAvailableError + + +class Clock: + def __init__(self): + pass + + def sleep(self, amount): + time.sleep(amount) + + def current_time(self): + return time.time() + + +class TokenBucket: + _MIN_RATE = 0.5 + + def __init__(self, max_rate, clock, min_rate=_MIN_RATE): + self._fill_rate = None + self._max_capacity = None + self._current_capacity = 0 + self._clock = clock + self._last_timestamp = None + self._min_rate = min_rate + self._lock = threading.Lock() + self._new_fill_rate_condition = threading.Condition(self._lock) + self.max_rate = max_rate + + @property + def max_rate(self): + return self._fill_rate + + @max_rate.setter + def max_rate(self, value): + with self._new_fill_rate_condition: + # Before we can change the rate we need to fill any pending + # tokens we might have based on the current rate. If we don't + # do this it means everything since the last recorded timestamp + # will accumulate at the rate we're about to set which isn't + # correct. + self._refill() + self._fill_rate = max(value, self._min_rate) + if value >= 1: + self._max_capacity = value + else: + self._max_capacity = 1 + # If we're scaling down, we also can't have a capacity that's + # more than our max_capacity. + self._current_capacity = min( + self._current_capacity, self._max_capacity + ) + self._new_fill_rate_condition.notify() + + @property + def max_capacity(self): + return self._max_capacity + + @property + def available_capacity(self): + return self._current_capacity + + def acquire(self, amount=1, block=True): + """Acquire token or return amount of time until next token available. + + If block is True, then this method will block until there's sufficient + capacity to acquire the desired amount. + + If block is False, then this method will return True is capacity + was successfully acquired, False otherwise. + + """ + with self._new_fill_rate_condition: + return self._acquire(amount=amount, block=block) + + def _acquire(self, amount, block): + self._refill() + if amount <= self._current_capacity: + self._current_capacity -= amount + return True + else: + if not block: + raise CapacityNotAvailableError() + # Not enough capacity. + sleep_amount = self._sleep_amount(amount) + while sleep_amount > 0: + # Until python3.2, wait() always returned None so we can't + # tell if a timeout occurred waiting on the cond var. + # Because of this we'll unconditionally call _refill(). + # The downside to this is that we were waken up via + # a notify(), we're calling unnecessarily calling _refill() an + # extra time. + self._new_fill_rate_condition.wait(sleep_amount) + self._refill() + sleep_amount = self._sleep_amount(amount) + self._current_capacity -= amount + return True + + def _sleep_amount(self, amount): + return (amount - self._current_capacity) / self._fill_rate + + def _refill(self): + timestamp = self._clock.current_time() + if self._last_timestamp is None: + self._last_timestamp = timestamp + return + current_capacity = self._current_capacity + fill_amount = (timestamp - self._last_timestamp) * self._fill_rate + new_capacity = min(self._max_capacity, current_capacity + fill_amount) + self._current_capacity = new_capacity + self._last_timestamp = timestamp diff --git a/venv/lib/python3.12/site-packages/botocore/retries/quota.py b/venv/lib/python3.12/site-packages/botocore/retries/quota.py new file mode 100644 index 0000000..f039429 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/retries/quota.py @@ -0,0 +1,54 @@ +"""Retry quota implementation.""" + +import threading + + +class RetryQuota: + INITIAL_CAPACITY = 500 + + def __init__(self, initial_capacity=INITIAL_CAPACITY, lock=None): + self._max_capacity = initial_capacity + self._available_capacity = initial_capacity + if lock is None: + lock = threading.Lock() + self._lock = lock + + def acquire(self, capacity_amount): + """Attempt to aquire a certain amount of capacity. + + If there's not sufficient amount of capacity available, ``False`` + is returned. Otherwise, ``True`` is returned, which indicates that + capacity was successfully allocated. + + """ + # The acquire() is only called when we encounter a retryable + # response so we aren't worried about locking the entire method. + with self._lock: + if capacity_amount > self._available_capacity: + return False + self._available_capacity -= capacity_amount + return True + + def release(self, capacity_amount): + """Release capacity back to the retry quota. + + The capacity being released will be truncated if necessary + to ensure the max capacity is never exceeded. + + """ + # Implementation note: The release() method is called as part + # of the "after-call" event, which means it gets invoked for + # every API call. In the common case where the request is + # successful and we're at full capacity, we can avoid locking. + # We can't exceed max capacity so there's no work we have to do. + if self._max_capacity == self._available_capacity: + return + with self._lock: + amount = min( + self._max_capacity - self._available_capacity, capacity_amount + ) + self._available_capacity += amount + + @property + def available_capacity(self): + return self._available_capacity diff --git a/venv/lib/python3.12/site-packages/botocore/retries/special.py b/venv/lib/python3.12/site-packages/botocore/retries/special.py new file mode 100644 index 0000000..9b78260 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/retries/special.py @@ -0,0 +1,51 @@ +"""Special cased retries. + +These are additional retry cases we still have to handle from the legacy +retry handler. They don't make sense as part of the standard mode retry +module. Ideally we should be able to remove this module. + +""" + +import logging +from binascii import crc32 + +from botocore.retries.base import BaseRetryableChecker + +logger = logging.getLogger(__name__) + + +# TODO: This is an ideal candidate for the retryable trait once that's +# available. +class RetryIDPCommunicationError(BaseRetryableChecker): + _SERVICE_NAME = 'sts' + + def is_retryable(self, context): + service_name = context.operation_model.service_model.service_name + if service_name != self._SERVICE_NAME: + return False + error_code = context.get_error_code() + return error_code == 'IDPCommunicationError' + + +class RetryDDBChecksumError(BaseRetryableChecker): + _CHECKSUM_HEADER = 'x-amz-crc32' + _SERVICE_NAME = 'dynamodb' + + def is_retryable(self, context): + service_name = context.operation_model.service_model.service_name + if service_name != self._SERVICE_NAME: + return False + if context.http_response is None: + return False + checksum = context.http_response.headers.get(self._CHECKSUM_HEADER) + if checksum is None: + return False + actual_crc32 = crc32(context.http_response.content) & 0xFFFFFFFF + if actual_crc32 != int(checksum): + logger.debug( + "DynamoDB crc32 checksum does not match, " + "expected: %s, actual: %s", + checksum, + actual_crc32, + ) + return True diff --git a/venv/lib/python3.12/site-packages/botocore/retries/standard.py b/venv/lib/python3.12/site-packages/botocore/retries/standard.py new file mode 100644 index 0000000..9964605 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/retries/standard.py @@ -0,0 +1,738 @@ +"""Standard retry behavior. + +This contains the default standard retry behavior. +It provides consistent behavior with other AWS SDKs. + +The key base classes uses for retries: + + * ``BaseRetryableChecker`` - Use to check a specific condition that + indicates a retry should happen. This can include things like + max attempts, HTTP status code checks, error code checks etc. + * ``RetryBackoff`` - Use to determine how long we should backoff until + we retry a request. This is the class that will implement delay such + as exponential backoff. + * ``RetryPolicy`` - Main class that determines if a retry should + happen. It can combine data from a various BaseRetryableCheckers + to make a final call as to whether or not a retry should happen. + It then uses a ``BaseRetryBackoff`` to determine how long to delay. + * ``RetryHandler`` - The bridge between botocore's event system + used by endpoint.py to manage retries and the interfaces defined + in this module. + +This allows us to define an API that has minimal coupling to the event +based API used by botocore. + +""" + +import logging +import random +import time + +# This is not a public interface and is subject to abrupt breaking changes. +# Currently it's only available to internal users for testing and validation. +# Any usage is not advised or supported in external code bases. +from botocore.configprovider import NEW_RETRIES_ENABLED +from botocore.exceptions import ( + ConnectionError, + ConnectTimeoutError, + HTTPClientError, + ReadTimeoutError, +) +from botocore.retries import quota, special +from botocore.retries.base import BaseRetryableChecker, BaseRetryBackoff + +DEFAULT_MAX_ATTEMPTS = 3 +_SERVICE_MAX_ATTEMPTS = { + 'dynamodb': 4, + 'dynamodb-streams': 4, +} +logger = logging.getLogger(__name__) + + +def register_retry_handler(client, max_attempts=None): + service_id = client.meta.service_model.service_id + service_event_name = service_id.hyphenize() + retry_event_adapter = RetryEventAdapter() + + if NEW_RETRIES_ENABLED: + if ( + max_attempts is None + and service_event_name in _SERVICE_MAX_ATTEMPTS + ): + max_attempts = _SERVICE_MAX_ATTEMPTS[service_event_name] + elif max_attempts is None: + max_attempts = DEFAULT_MAX_ATTEMPTS + # The ``max`` token of the ``amz-sdk-request`` header is otherwise + # only populated once a retry is attempted, so it's missing from + # the initial attempt. Seeding it here ensures that it's present + # on every attempt. This handler runs before ``add_retry_headers`` + # because the emitter invokes more specific events first, and + # ``add_retry_headers`` is registered on the generic + # ``request-created``. + client.meta.events.register( + f'request-created.{service_event_name}', + MaxAttemptsSeeder(max_attempts).seed_max_attempts, + unique_id=f'seed-max-attempts-{service_event_name}', + ) + throttling_detector = ThrottlingErrorDetector(retry_event_adapter) + retry_quota = RetryQuotaChecker( + quota.RetryQuota(), throttling_detector + ) + handler = RetryHandler( + retry_policy=RetryPolicy( + retry_checker=StandardRetryConditions( + max_attempts=max_attempts + ), + retry_backoff=ExponentialBackoff( + service_name=service_event_name, + throttling_detector=throttling_detector, + ), + ), + retry_event_adapter=retry_event_adapter, + retry_quota=retry_quota, + service_name=service_event_name, + ) + else: + retry_quota = RetryQuotaChecker(quota.RetryQuota()) + handler = RetryHandler( + retry_policy=RetryPolicy( + retry_checker=StandardRetryConditions( + max_attempts=max_attempts or DEFAULT_MAX_ATTEMPTS + ), + retry_backoff=ExponentialBackoff(), + ), + retry_event_adapter=retry_event_adapter, + retry_quota=retry_quota, + ) + + client.meta.events.register( + f'after-call.{service_event_name}', retry_quota.release_retry_quota + ) + unique_id = f'retry-config-{service_event_name}' + client.meta.events.register( + f'needs-retry.{service_event_name}', + handler.needs_retry, + unique_id=unique_id, + ) + return handler + + +class MaxAttemptsSeeder: + """Adds the resolved max attempts to the retries context. + + The ``max`` value is otherwise only added to the retries context by + ``MaxAttemptsChecker`` when a retry is evaluated, which means it's + absent from the initial attempt's ``amz-sdk-request`` header. + + """ + + def __init__(self, max_attempts): + self._max_attempts = max_attempts + + def seed_max_attempts(self, request, **kwargs): + retries_context = request.context.setdefault('retries', {}) + retries_context['max'] = self._max_attempts + + +class RetryHandler: + """Bridge between botocore's event system and this module. + + This class is intended to be hooked to botocore's event system + as an event handler. + """ + + # Temporary hard-coded list of long-polling operations. This will be + # replaced by the aws.api#longPoll modeled trait once it is available + # in service models. + _LONG_POLLING_OPERATIONS = { + 'sqs': {'ReceiveMessage'}, + 'sfn': {'GetActivityTask'}, + 'swf': {'PollForActivityTask', 'PollForDecisionTask'}, + } + + def __init__( + self, + retry_policy, + retry_event_adapter, + retry_quota, + service_name=None, + sleep=time.sleep, + ): + self._retry_policy = retry_policy + self._retry_event_adapter = retry_event_adapter + self._retry_quota = retry_quota + self._service_name = service_name + self._sleep = sleep + + def needs_retry(self, **kwargs): + """Connect as a handler to the needs-retry event.""" + retry_delay = None + context = self._retry_event_adapter.create_retry_context(**kwargs) + if self._retry_policy.should_retry(context): + # Before we can retry we need to ensure we have sufficient + # capacity in our retry quota. + if self._retry_quota.acquire_retry_quota(context): + retry_delay = self._retry_policy.compute_retry_delay(context) + logger.debug( + "Retry needed, retrying request after delay of: %s", + retry_delay, + ) + else: + if NEW_RETRIES_ENABLED: + if self._is_long_polling_operation(context): + polling_delay = self._retry_policy.compute_retry_delay( + context + ) + self._sleep(polling_delay) + logger.debug( + "Retry needed but retry quota reached, " + "not retrying request." + ) + self._retry_event_adapter.adapt_retry_response_from_context( + context + ) + # Return False (non-None) to prevent any later needs-retry + # handler from returning a delay that would cause + # _needs_retry in endpoint.py to sleep again. + return False + logger.debug( + "Retry needed but retry quota reached, " + "not retrying request." + ) + else: + logger.debug("Not retrying request.") + self._retry_event_adapter.adapt_retry_response_from_context(context) + return retry_delay + + def _is_long_polling_operation(self, context): + # TODO: Replace this hard-coded list with a model check once + # aws.api#longPoll trait is available in service models. + if self._service_name is None or context.operation_model is None: + return False + operations = self._LONG_POLLING_OPERATIONS.get(self._service_name) + return ( + operations is not None + and context.operation_model.name in operations + ) + + +class RetryEventAdapter: + """Adapter to existing retry interface used in the endpoints layer. + + This existing interface for determining if a retry needs to happen + is event based and used in ``botocore.endpoint``. The interface has + grown organically over the years and could use some cleanup. This + adapter converts that interface into the interface used by the + new retry strategies. + + """ + + def create_retry_context(self, **kwargs): + """Create context based on needs-retry kwargs.""" + response = kwargs['response'] + if response is None: + # If response is None it means that an exception was raised + # because we never received a response from the service. This + # could be something like a ConnectionError we get from our + # http layer. + http_response = None + parsed_response = None + else: + http_response, parsed_response = response + # This provides isolation between the kwargs emitted in the + # needs-retry event, and what this module uses to check for + # retries. + context = RetryContext( + attempt_number=kwargs['attempts'], + operation_model=kwargs['operation'], + http_response=http_response, + parsed_response=parsed_response, + caught_exception=kwargs['caught_exception'], + request_context=kwargs['request_dict']['context'], + ) + return context + + def adapt_retry_response_from_context(self, context): + """Modify response back to user back from context.""" + # This will mutate attributes that are returned back to the end + # user. We do it this way so that all the various retry classes + # don't mutate any input parameters from the needs-retry event. + metadata = context.get_retry_metadata() + if context.parsed_response is not None: + context.parsed_response.setdefault('ResponseMetadata', {}).update( + metadata + ) + + +# Implementation note: this is meant to encapsulate all the misc stuff +# that gets sent in the needs-retry event. This is mapped so that params +# are more clear and explicit. +class RetryContext: + """Normalize a response that we use to check if a retry should occur. + + This class smoothes over the different types of responses we may get + from a service including: + + * A modeled error response from the service that contains a service + code and error message. + * A raw HTTP response that doesn't contain service protocol specific + error keys. + * An exception received while attempting to retrieve a response. + This could be a ConnectionError we receive from our HTTP layer which + could represent that we weren't able to receive a response from + the service. + + This class guarantees that at least one of the above attributes will be + non None. + + This class is meant to provide a read-only view into the properties + associated with a possible retryable response. None of the properties + are meant to be modified directly. + + """ + + def __init__( + self, + attempt_number, + operation_model=None, + parsed_response=None, + http_response=None, + caught_exception=None, + request_context=None, + ): + # 1-based attempt number. + self.attempt_number = attempt_number + self.operation_model = operation_model + # This is the parsed response dictionary we get from parsing + # the HTTP response from the service. + self.parsed_response = parsed_response + # This is an instance of botocore.awsrequest.AWSResponse. + self.http_response = http_response + # This is a subclass of Exception that will be non None if + # an exception was raised when retrying to retrieve a response. + self.caught_exception = caught_exception + # This is the request context dictionary that's added to the + # request dict. This is used to story any additional state + # about the request. We use this for storing retry quota + # capacity. + if request_context is None: + request_context = {} + self.request_context = request_context + self._retry_metadata = {} + + # These are misc helper methods to avoid duplication in the various + # checkers. + def get_error_code(self): + """Check if there was a parsed response with an error code. + + If we could not find any error codes, ``None`` is returned. + + """ + if self.parsed_response is None: + return + error = self.parsed_response.get('Error', {}) + if not isinstance(error, dict): + return + return error.get('Code') + + def add_retry_metadata(self, **kwargs): + """Add key/value pairs to the retry metadata. + + This allows any objects during the retry process to add + metadata about any checks/validations that happened. + + This gets added to the response metadata in the retry handler. + + """ + self._retry_metadata.update(**kwargs) + + def get_retry_metadata(self): + return self._retry_metadata.copy() + + +class RetryPolicy: + def __init__(self, retry_checker, retry_backoff): + self._retry_checker = retry_checker + self._retry_backoff = retry_backoff + + def should_retry(self, context): + return self._retry_checker.is_retryable(context) + + def compute_retry_delay(self, context): + return self._retry_backoff.delay_amount(context) + + +class ExponentialBackoff(BaseRetryBackoff): + _BASE = 2 + _MAX_BACKOFF = 20 + _RETRY_AFTER_HEADER = 'x-amz-retry-after' + _RETRY_AFTER_MAX_ADDITIONAL = 5 # seconds + + _DEFAULT_BACKOFF_CONFIG = { + 'throttling_base_scale': 1, + 'non_throttling_base_scale': 0.05, + } + + _SERVICE_BACKOFF_CONFIG = { + 'dynamodb': {'non_throttling_base_scale': 0.025}, + 'dynamodb-streams': {'non_throttling_base_scale': 0.025}, + } + + def __init__( + self, + max_backoff=20, + random=random.random, + service_name=None, + throttling_detector=None, + ): + self._base = self._BASE + self._max_backoff = max_backoff + self._random = random + self._service_name = service_name + self._throttling_detector = throttling_detector + + def delay_amount(self, context): + """Calculates delay based on exponential backoff. + + This class implements truncated binary exponential backoff + with jitter:: + + t_i = rand(0, 1) * min(2 ** attempt, MAX_BACKOFF) + + where ``i`` is the request attempt (0 based). + + """ + # The context.attempt_number is a 1-based value, but we have + # to calculate the delay based on i based a 0-based value. We + # want the first delay to just be ``rand(0, 1)``. + if NEW_RETRIES_ENABLED: + t_i = self._random() * min( + self._get_base_scale(context) + * (self._base ** (context.attempt_number - 1)), + self._max_backoff, + ) + + # Check for x-amz-retry-after header + retry_after = self._get_retry_after_delay(context) + if retry_after is not None: + # min is 't_i', max is 't_i + 5' + return max( + t_i, + min(retry_after, self._RETRY_AFTER_MAX_ADDITIONAL + t_i), + ) + + return t_i + else: + return self._random() * min( + (self._base ** (context.attempt_number - 1)), + self._max_backoff, + ) + + def _get_base_scale(self, context): + if ( + self._throttling_detector + and self._throttling_detector.is_throttling_error_from_context( + context + ) + ): + return self._DEFAULT_BACKOFF_CONFIG['throttling_base_scale'] + if self._service_name in self._SERVICE_BACKOFF_CONFIG: + return self._SERVICE_BACKOFF_CONFIG[self._service_name][ + 'non_throttling_base_scale' + ] + return self._DEFAULT_BACKOFF_CONFIG['non_throttling_base_scale'] + + def _get_retry_after_delay(self, context): + if context.http_response is None: + return None + retry_after_ms = context.http_response.headers.get( + self._RETRY_AFTER_HEADER + ) + if retry_after_ms is None: + return None + try: + value = int(retry_after_ms) / 1000.0 + if value < 0: + raise ValueError("Negative retry-after value") + return value + except (ValueError, OverflowError) as e: + logger.debug( + "Invalid %s header value: %s, ignoring. Error: %s", + self._RETRY_AFTER_HEADER, + retry_after_ms, + e, + ) + return None + + +class MaxAttemptsChecker(BaseRetryableChecker): + def __init__(self, max_attempts): + self._max_attempts = max_attempts + + def is_retryable(self, context): + under_max_attempts = context.attempt_number < self._max_attempts + retries_context = context.request_context.get('retries') + if retries_context: + retries_context['max'] = max( + retries_context.get('max', 0), self._max_attempts + ) + if not under_max_attempts: + logger.debug("Max attempts of %s reached.", self._max_attempts) + context.add_retry_metadata(MaxAttemptsReached=True) + return under_max_attempts + + +class TransientRetryableChecker(BaseRetryableChecker): + _TRANSIENT_ERROR_CODES = [ + 'RequestTimeout', + 'RequestTimeoutException', + 'PriorRequestNotComplete', + ] + _TRANSIENT_STATUS_CODES = [500, 502, 503, 504] + _TRANSIENT_EXCEPTION_CLS = ( + ConnectionError, + HTTPClientError, + ) + + def __init__( + self, + transient_error_codes=None, + transient_status_codes=None, + transient_exception_cls=None, + ): + if transient_error_codes is None: + transient_error_codes = self._TRANSIENT_ERROR_CODES[:] + if transient_status_codes is None: + transient_status_codes = self._TRANSIENT_STATUS_CODES[:] + if transient_exception_cls is None: + transient_exception_cls = self._TRANSIENT_EXCEPTION_CLS + self._transient_error_codes = transient_error_codes + self._transient_status_codes = transient_status_codes + self._transient_exception_cls = transient_exception_cls + + def is_retryable(self, context): + if context.get_error_code() in self._transient_error_codes: + return True + if context.http_response is not None: + if ( + context.http_response.status_code + in self._transient_status_codes + ): + return True + if context.caught_exception is not None: + return isinstance( + context.caught_exception, self._transient_exception_cls + ) + return False + + +class ThrottledRetryableChecker(BaseRetryableChecker): + # This is the union of all error codes we've seen that represent + # a throttled error. + _THROTTLED_ERROR_CODES = [ + 'Throttling', + 'ThrottlingException', + 'ThrottledException', + 'RequestThrottledException', + 'TooManyRequestsException', + 'ProvisionedThroughputExceededException', + 'TransactionInProgressException', + 'RequestLimitExceeded', + 'BandwidthLimitExceeded', + 'LimitExceededException', + 'RequestThrottled', + 'SlowDown', + 'PriorRequestNotComplete', + 'EC2ThrottledException', + ] + + def __init__(self, throttled_error_codes=None): + if throttled_error_codes is None: + throttled_error_codes = self._THROTTLED_ERROR_CODES[:] + self._throttled_error_codes = throttled_error_codes + + def is_retryable(self, context): + # Only the error code from a parsed service response is used + # to determine if the response is a throttled response. + return context.get_error_code() in self._throttled_error_codes + + +class ModeledRetryableChecker(BaseRetryableChecker): + """Check if an error has been modeled as retryable.""" + + def __init__(self): + self._error_detector = ModeledRetryErrorDetector() + + def is_retryable(self, context): + error_code = context.get_error_code() + if error_code is None: + return False + return self._error_detector.detect_error_type(context) is not None + + +class ModeledRetryErrorDetector: + """Checks whether or not an error is a modeled retryable error.""" + + # There are return values from the detect_error_type() method. + TRANSIENT_ERROR = 'TRANSIENT_ERROR' + THROTTLING_ERROR = 'THROTTLING_ERROR' + # This class is lower level than ModeledRetryableChecker, which + # implements BaseRetryableChecker. This object allows you to distinguish + # between the various types of retryable errors. + + def detect_error_type(self, context): + """Detect the error type associated with an error code and model. + + This will either return: + + * ``self.TRANSIENT_ERROR`` - If the error is a transient error + * ``self.THROTTLING_ERROR`` - If the error is a throttling error + * ``None`` - If the error is neither type of error. + + """ + error_code = context.get_error_code() + op_model = context.operation_model + if op_model is None or not op_model.error_shapes: + return + for shape in op_model.error_shapes: + if shape.metadata.get('retryable') is not None: + # Check if this error code matches the shape. This can + # be either by name or by a modeled error code. + error_code_to_check = ( + shape.metadata.get('error', {}).get('code') or shape.name + ) + if error_code == error_code_to_check: + if shape.metadata['retryable'].get('throttling'): + return self.THROTTLING_ERROR + return self.TRANSIENT_ERROR + + +class ThrottlingErrorDetector: + def __init__(self, retry_event_adapter): + self._modeled_error_detector = ModeledRetryErrorDetector() + self._fixed_error_code_detector = ThrottledRetryableChecker() + self._retry_event_adapter = retry_event_adapter + + # This expects the kwargs from needs-retry to be passed through. + def is_throttling_error(self, **kwargs): + context = self._retry_event_adapter.create_retry_context(**kwargs) + return self.is_throttling_error_from_context(context) + + def is_throttling_error_from_context(self, context): + if self._fixed_error_code_detector.is_retryable(context): + return True + error_type = self._modeled_error_detector.detect_error_type(context) + return error_type == self._modeled_error_detector.THROTTLING_ERROR + + +class StandardRetryConditions(BaseRetryableChecker): + """Concrete class that implements the standard retry policy checks. + + Specifically: + + not max_attempts and (transient or throttled or modeled_retry) + + """ + + def __init__(self, max_attempts=DEFAULT_MAX_ATTEMPTS): + # Note: This class is for convenience so you can have the + # standard retry condition in a single class. + self._max_attempts_checker = MaxAttemptsChecker(max_attempts) + self._additional_checkers = OrRetryChecker( + [ + TransientRetryableChecker(), + ThrottledRetryableChecker(), + ModeledRetryableChecker(), + OrRetryChecker( + [ + special.RetryIDPCommunicationError(), + special.RetryDDBChecksumError(), + ] + ), + ] + ) + + def is_retryable(self, context): + return self._max_attempts_checker.is_retryable( + context + ) and self._additional_checkers.is_retryable(context) + + +class OrRetryChecker(BaseRetryableChecker): + def __init__(self, checkers): + self._checkers = checkers + + def is_retryable(self, context): + return any(checker.is_retryable(context) for checker in self._checkers) + + +class RetryQuotaChecker: + _RETRY_COST = 5 + _RETRY_COST_V2 = 14 + _NO_RETRY_INCREMENT = 1 + _THROTTLING_RETRY_COST = 5 + _TIMEOUT_RETRY_REQUEST = 10 + _TIMEOUT_EXCEPTIONS = (ConnectTimeoutError, ReadTimeoutError) + + # Implementation note: We're not making this a BaseRetryableChecker + # because this isn't just a check if we can retry. This also changes + # state so we have to careful when/how we call this. Making it + # a BaseRetryableChecker implies you can call .is_retryable(context) + # as many times as you want and not affect anything. + + def __init__(self, quota, throttling_detector=None): + self._quota = quota + self._throttling_detector = throttling_detector + # This tracks the last amount + self._last_amount_acquired = None + + def acquire_retry_quota(self, context): + if NEW_RETRIES_ENABLED: + if self._is_throttling_error(context): + capacity_amount = self._THROTTLING_RETRY_COST + else: + capacity_amount = self._RETRY_COST_V2 + else: + if self._is_timeout_error(context): + capacity_amount = self._TIMEOUT_RETRY_REQUEST + else: + capacity_amount = self._RETRY_COST + success = self._quota.acquire(capacity_amount) + if success: + # We add the capacity amount to the request context so we know + # how much to release later. The capacity amount can vary based + # on the error. + context.request_context['retry_quota_capacity'] = capacity_amount + return True + context.add_retry_metadata(RetryQuotaReached=True) + return False + + def _is_throttling_error(self, context): + if self._throttling_detector is None: + return False + return self._throttling_detector.is_throttling_error_from_context( + context + ) + + def _is_timeout_error(self, context): + return isinstance(context.caught_exception, self._TIMEOUT_EXCEPTIONS) + + # This is intended to be hooked up to ``after-call``. + def release_retry_quota(self, context, http_response, **kwargs): + # There's three possible options. + # 1. The HTTP response did not have a 2xx response. In that case we + # give no quota back. + # 2. The HTTP request was successful and was never retried. In + # that case we give _NO_RETRY_INCREMENT back. + # 3. The API call had retries, and we eventually receive an HTTP + # response with a 2xx status code. In that case we give back + # whatever quota was associated with the last acquisition. + if http_response is None: + return + status_code = http_response.status_code + if 200 <= status_code < 300: + if 'retry_quota_capacity' not in context: + self._quota.release(self._NO_RETRY_INCREMENT) + else: + capacity_amount = context['retry_quota_capacity'] + self._quota.release(capacity_amount) diff --git a/venv/lib/python3.12/site-packages/botocore/retries/throttling.py b/venv/lib/python3.12/site-packages/botocore/retries/throttling.py new file mode 100644 index 0000000..34ab417 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/retries/throttling.py @@ -0,0 +1,55 @@ +from collections import namedtuple + +CubicParams = namedtuple('CubicParams', ['w_max', 'k', 'last_fail']) + + +class CubicCalculator: + _SCALE_CONSTANT = 0.4 + _BETA = 0.7 + + def __init__( + self, + starting_max_rate, + start_time, + scale_constant=_SCALE_CONSTANT, + beta=_BETA, + ): + self._w_max = starting_max_rate + self._scale_constant = scale_constant + self._beta = beta + self._k = self._calculate_zero_point() + self._last_fail = start_time + + def _calculate_zero_point(self): + scaled_value = (self._w_max * (1 - self._beta)) / self._scale_constant + k = scaled_value ** (1 / 3.0) + return k + + def success_received(self, timestamp): + dt = timestamp - self._last_fail + new_rate = self._scale_constant * (dt - self._k) ** 3 + self._w_max + return new_rate + + def error_received(self, current_rate, timestamp): + # Consider not having this be the current measured rate. + + # We have a new max rate, which is the current rate we were sending + # at when we received an error response. + self._w_max = current_rate + self._k = self._calculate_zero_point() + self._last_fail = timestamp + return current_rate * self._beta + + def get_params_snapshot(self): + """Return a read-only object of the current cubic parameters. + + These parameters are intended to be used for debug/troubleshooting + purposes. These object is a read-only snapshot and cannot be used + to modify the behavior of the CUBIC calculations. + + New parameters may be added to this object in the future. + + """ + return CubicParams( + w_max=self._w_max, k=self._k, last_fail=self._last_fail + ) diff --git a/venv/lib/python3.12/site-packages/botocore/retryhandler.py b/venv/lib/python3.12/site-packages/botocore/retryhandler.py new file mode 100644 index 0000000..c2eed1d --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/retryhandler.py @@ -0,0 +1,416 @@ +# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import functools +import logging +import random +from binascii import crc32 + +from botocore.exceptions import ( + ChecksumError, + ConnectionClosedError, + ConnectionError, + EndpointConnectionError, + ReadTimeoutError, +) + +logger = logging.getLogger(__name__) +# The only supported error for now is GENERAL_CONNECTION_ERROR +# which maps to requests generic ConnectionError. If we're able +# to get more specific exceptions from requests we can update +# this mapping with more specific exceptions. +EXCEPTION_MAP = { + 'GENERAL_CONNECTION_ERROR': [ + ConnectionError, + ConnectionClosedError, + ReadTimeoutError, + EndpointConnectionError, + ], +} + + +def delay_exponential(base, growth_factor, attempts): + """Calculate time to sleep based on exponential function. + + The format is:: + + base * growth_factor ^ (attempts - 1) + + If ``base`` is set to 'rand' then a random number between + 0 and 1 will be used as the base. + Base must be greater than 0, otherwise a ValueError will be + raised. + + """ + if base == 'rand': + base = random.random() + elif base <= 0: + raise ValueError( + f"The 'base' param must be greater than 0, got: {base}" + ) + time_to_sleep = base * (growth_factor ** (attempts - 1)) + return time_to_sleep + + +def create_exponential_delay_function(base, growth_factor): + """Create an exponential delay function based on the attempts. + + This is used so that you only have to pass it the attempts + parameter to calculate the delay. + + """ + return functools.partial( + delay_exponential, base=base, growth_factor=growth_factor + ) + + +def create_retry_handler(config, operation_name=None): + checker = create_checker_from_retry_config( + config, operation_name=operation_name + ) + action = create_retry_action_from_config( + config, operation_name=operation_name + ) + return RetryHandler(checker=checker, action=action) + + +def create_retry_action_from_config(config, operation_name=None): + # The spec has the possibility of supporting per policy + # actions, but right now, we assume this comes from the + # default section, which means that delay functions apply + # for every policy in the retry config (per service). + delay_config = config['__default__']['delay'] + if delay_config['type'] == 'exponential': + return create_exponential_delay_function( + base=delay_config['base'], + growth_factor=delay_config['growth_factor'], + ) + + +def create_checker_from_retry_config(config, operation_name=None): + checkers = [] + max_attempts = None + retryable_exceptions = [] + if '__default__' in config: + policies = config['__default__'].get('policies', []) + max_attempts = config['__default__']['max_attempts'] + for key in policies: + current_config = policies[key] + checkers.append(_create_single_checker(current_config)) + retry_exception = _extract_retryable_exception(current_config) + if retry_exception is not None: + retryable_exceptions.extend(retry_exception) + if operation_name is not None and config.get(operation_name) is not None: + operation_policies = config[operation_name]['policies'] + for key in operation_policies: + checkers.append(_create_single_checker(operation_policies[key])) + retry_exception = _extract_retryable_exception( + operation_policies[key] + ) + if retry_exception is not None: + retryable_exceptions.extend(retry_exception) + if len(checkers) == 1: + # Don't need to use a MultiChecker + return MaxAttemptsDecorator(checkers[0], max_attempts=max_attempts) + else: + multi_checker = MultiChecker(checkers) + return MaxAttemptsDecorator( + multi_checker, + max_attempts=max_attempts, + retryable_exceptions=tuple(retryable_exceptions), + ) + + +def _create_single_checker(config): + if 'response' in config['applies_when']: + return _create_single_response_checker( + config['applies_when']['response'] + ) + elif 'socket_errors' in config['applies_when']: + return ExceptionRaiser() + + +def _create_single_response_checker(response): + if 'service_error_code' in response: + checker = ServiceErrorCodeChecker( + status_code=response['http_status_code'], + error_code=response['service_error_code'], + ) + elif 'http_status_code' in response: + checker = HTTPStatusCodeChecker( + status_code=response['http_status_code'] + ) + elif 'crc32body' in response: + checker = CRC32Checker(header=response['crc32body']) + else: + # TODO: send a signal. + raise ValueError("Unknown retry policy") + return checker + + +def _extract_retryable_exception(config): + applies_when = config['applies_when'] + if 'crc32body' in applies_when.get('response', {}): + return [ChecksumError] + elif 'socket_errors' in applies_when: + exceptions = [] + for name in applies_when['socket_errors']: + exceptions.extend(EXCEPTION_MAP[name]) + return exceptions + + +class RetryHandler: + """Retry handler. + + The retry handler takes two params, ``checker`` object + and an ``action`` object. + + The ``checker`` object must be a callable object and based on a response + and an attempt number, determines whether or not sufficient criteria for + a retry has been met. If this is the case then the ``action`` object + (which also is a callable) determines what needs to happen in the event + of a retry. + + """ + + def __init__(self, checker, action): + self._checker = checker + self._action = action + + def __call__(self, attempts, response, caught_exception, **kwargs): + """Handler for a retry. + + Intended to be hooked up to an event handler (hence the **kwargs), + this will process retries appropriately. + + """ + checker_kwargs = { + 'attempt_number': attempts, + 'response': response, + 'caught_exception': caught_exception, + } + if isinstance(self._checker, MaxAttemptsDecorator): + retries_context = kwargs['request_dict']['context'].get('retries') + checker_kwargs.update({'retries_context': retries_context}) + + if self._checker(**checker_kwargs): + result = self._action(attempts=attempts) + logger.debug("Retry needed, action of: %s", result) + return result + logger.debug("No retry needed.") + + +class BaseChecker: + """Base class for retry checkers. + + Each class is responsible for checking a single criteria that determines + whether or not a retry should not happen. + + """ + + def __call__(self, attempt_number, response, caught_exception): + """Determine if retry criteria matches. + + Note that either ``response`` is not None and ``caught_exception`` is + None or ``response`` is None and ``caught_exception`` is not None. + + :type attempt_number: int + :param attempt_number: The total number of times we've attempted + to send the request. + + :param response: The HTTP response (if one was received). + + :type caught_exception: Exception + :param caught_exception: Any exception that was caught while trying to + send the HTTP response. + + :return: True, if the retry criteria matches (and therefore a retry + should occur. False if the criteria does not match. + + """ + # The default implementation allows subclasses to not have to check + # whether or not response is None or not. + if response is not None: + return self._check_response(attempt_number, response) + elif caught_exception is not None: + return self._check_caught_exception( + attempt_number, caught_exception + ) + else: + raise ValueError("Both response and caught_exception are None.") + + def _check_response(self, attempt_number, response): + pass + + def _check_caught_exception(self, attempt_number, caught_exception): + pass + + +class MaxAttemptsDecorator(BaseChecker): + """Allow retries up to a maximum number of attempts. + + This will pass through calls to the decorated retry checker, provided + that the number of attempts does not exceed max_attempts. It will + also catch any retryable_exceptions passed in. Once max_attempts has + been exceeded, then False will be returned or the retryable_exceptions + that was previously being caught will be raised. + + """ + + def __init__(self, checker, max_attempts, retryable_exceptions=None): + self._checker = checker + self._max_attempts = max_attempts + self._retryable_exceptions = retryable_exceptions + + def __call__( + self, attempt_number, response, caught_exception, retries_context + ): + if retries_context: + retries_context['max'] = max( + retries_context.get('max', 0), self._max_attempts + ) + + should_retry = self._should_retry( + attempt_number, response, caught_exception + ) + if should_retry: + if attempt_number >= self._max_attempts: + # explicitly set MaxAttemptsReached + if response is not None and 'ResponseMetadata' in response[1]: + response[1]['ResponseMetadata']['MaxAttemptsReached'] = ( + True + ) + logger.debug( + "Reached the maximum number of retry attempts: %s", + attempt_number, + ) + return False + else: + return should_retry + else: + return False + + def _should_retry(self, attempt_number, response, caught_exception): + if self._retryable_exceptions and attempt_number < self._max_attempts: + try: + return self._checker( + attempt_number, response, caught_exception + ) + except self._retryable_exceptions as e: + logger.debug( + "retry needed, retryable exception caught: %s", + e, + exc_info=True, + ) + return True + else: + # If we've exceeded the max attempts we just let the exception + # propagate if one has occurred. + return self._checker(attempt_number, response, caught_exception) + + +class HTTPStatusCodeChecker(BaseChecker): + def __init__(self, status_code): + self._status_code = status_code + + def _check_response(self, attempt_number, response): + if response[0].status_code == self._status_code: + logger.debug( + "retry needed: retryable HTTP status code received: %s", + self._status_code, + ) + return True + else: + return False + + +class ServiceErrorCodeChecker(BaseChecker): + def __init__(self, status_code, error_code): + self._status_code = status_code + self._error_code = error_code + + def _check_response(self, attempt_number, response): + if response[0].status_code == self._status_code: + actual_error_code = response[1].get('Error', {}).get('Code') + if actual_error_code == self._error_code: + logger.debug( + "retry needed: matching HTTP status and error code seen: " + "%s, %s", + self._status_code, + self._error_code, + ) + return True + return False + + +class MultiChecker(BaseChecker): + def __init__(self, checkers): + self._checkers = checkers + + def __call__(self, attempt_number, response, caught_exception): + for checker in self._checkers: + checker_response = checker( + attempt_number, response, caught_exception + ) + if checker_response: + return checker_response + return False + + +class CRC32Checker(BaseChecker): + def __init__(self, header): + # The header where the expected crc32 is located. + self._header_name = header + + def _check_response(self, attempt_number, response): + http_response = response[0] + expected_crc = http_response.headers.get(self._header_name) + if expected_crc is None: + logger.debug( + "crc32 check skipped, the %s header is not " + "in the http response.", + self._header_name, + ) + else: + actual_crc32 = crc32(response[0].content) & 0xFFFFFFFF + if not actual_crc32 == int(expected_crc): + logger.debug( + "retry needed: crc32 check failed, expected != actual: " + "%s != %s", + int(expected_crc), + actual_crc32, + ) + raise ChecksumError( + checksum_type='crc32', + expected_checksum=int(expected_crc), + actual_checksum=actual_crc32, + ) + + +class ExceptionRaiser(BaseChecker): + """Raise any caught exceptions. + + This class will raise any non None ``caught_exception``. + + """ + + def _check_caught_exception(self, attempt_number, caught_exception): + # This is implementation specific, but this class is useful by + # coordinating with the MaxAttemptsDecorator. + # The MaxAttemptsDecorator has a list of exceptions it should catch + # and retry, but something needs to come along and actually raise the + # caught_exception. That's what this class is being used for. If + # the MaxAttemptsDecorator is not interested in retrying the exception + # then this exception just propagates out past the retry code. + raise caught_exception diff --git a/venv/lib/python3.12/site-packages/botocore/serialize.py b/venv/lib/python3.12/site-packages/botocore/serialize.py new file mode 100644 index 0000000..547d5aa --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/serialize.py @@ -0,0 +1,1278 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Protocol input serializes. + +This module contains classes that implement input serialization +for the various AWS protocol types. + +These classes essentially take user input, a model object that +represents what the expected input should look like, and it returns +a dictionary that contains the various parts of a request. A few +high level design decisions: + + +* Each protocol type maps to a separate class, all inherit from + ``Serializer``. +* The return value for ``serialize_to_request`` (the main entry + point) returns a dictionary that represents a request. This + will have keys like ``url_path``, ``query_string``, etc. This + is done so that it's a) easy to test and b) not tied to a + particular HTTP library. See the ``serialize_to_request`` docstring + for more details. + +Unicode +------- + +The input to the serializers should be text (str/unicode), not bytes, +with the exception of blob types. Those are assumed to be binary, +and if a str/unicode type is passed in, it will be encoded as utf-8. +""" + +import base64 +import calendar +import datetime +import decimal +import json +import math +import re +import struct +from xml.etree import ElementTree + +from botocore import validate +from botocore.compat import formatdate +from botocore.exceptions import ParamValidationError +from botocore.useragent import register_feature_id +from botocore.utils import ( + has_header, + is_json_value_header, + parse_to_aware_datetime, + percent_encode, +) + +# From the spec, the default timestamp format if not specified is iso8601. +DEFAULT_TIMESTAMP_FORMAT = 'iso8601' +ISO8601 = '%Y-%m-%dT%H:%M:%SZ' +# Same as ISO8601, but with microsecond precision. +ISO8601_MICRO = '%Y-%m-%dT%H:%M:%S.%fZ' +HOST_PREFIX_RE = re.compile(r"^[A-Za-z0-9\.\-]+$") + +TIMESTAMP_PRECISION_DEFAULT = 'default' +TIMESTAMP_PRECISION_MILLISECOND = 'millisecond' +TIMESTAMP_PRECISION_LEGACY = 'legacy' +TIMESTAMP_PRECISION_OPTIONS = ( + TIMESTAMP_PRECISION_DEFAULT, + TIMESTAMP_PRECISION_MILLISECOND, + TIMESTAMP_PRECISION_LEGACY, +) + + +def create_serializer( + protocol_name, + include_validation=True, + timestamp_precision=TIMESTAMP_PRECISION_DEFAULT, +): + """Create a serializer for the given protocol. + :param protocol_name: The protocol name to create a serializer for. + :type protocol_name: str + :param include_validation: Whether to include parameter validation. + :type include_validation: bool + :param timestamp_precision: Timestamp precision level. + - 'default': Full precision of the provided value (up to + microseconds) for ISO and Unix timestamps, seconds for RFC + - 'millisecond': Millisecond precision (ISO/Unix), seconds for RFC + - 'legacy': Behavior prior to sub-second Unix timestamp support. + Microseconds for ISO timestamps, seconds for Unix and RFC + :type timestamp_precision: str + :return: A serializer instance for the given protocol. + """ + # TODO: Unknown protocols. + serializer = SERIALIZERS[protocol_name]( + timestamp_precision=timestamp_precision + ) + if include_validation: + validator = validate.ParamValidator() + serializer = validate.ParamValidationDecorator(validator, serializer) + return serializer + + +class Serializer: + DEFAULT_METHOD = 'POST' + # Clients can change this to a different MutableMapping + # (i.e OrderedDict) if they want. This is used in the + # compliance test to match the hash ordering used in the + # tests. + MAP_TYPE = dict + DEFAULT_ENCODING = 'utf-8' + + def __init__(self, timestamp_precision=TIMESTAMP_PRECISION_DEFAULT): + if timestamp_precision not in TIMESTAMP_PRECISION_OPTIONS: + raise ValueError( + f"Invalid timestamp precision found while creating serializer: {timestamp_precision}" + ) + self._timestamp_precision = timestamp_precision + + def serialize_to_request(self, parameters, operation_model): + """Serialize parameters into an HTTP request. + + This method takes user provided parameters and a shape + model and serializes the parameters to an HTTP request. + More specifically, this method returns information about + parts of the HTTP request, it does not enforce a particular + interface or standard for an HTTP request. It instead returns + a dictionary of: + + * 'url_path' + * 'host_prefix' + * 'query_string' + * 'headers' + * 'body' + * 'method' + + It is then up to consumers to decide how to map this to a Request + object of their HTTP library of choice. Below is an example + return value:: + + {'body': {'Action': 'OperationName', + 'Bar': 'val2', + 'Foo': 'val1', + 'Version': '2014-01-01'}, + 'headers': {}, + 'method': 'POST', + 'query_string': '', + 'host_prefix': 'value.', + 'url_path': '/'} + + :param parameters: The dictionary input parameters for the + operation (i.e the user input). + :param operation_model: The OperationModel object that describes + the operation. + """ + raise NotImplementedError("serialize_to_request") + + def _create_default_request(self): + # Creates a boilerplate default request dict that subclasses + # can use as a starting point. + serialized = { + 'url_path': '/', + 'query_string': '', + 'method': self.DEFAULT_METHOD, + 'headers': {}, + # An empty body is represented as an empty byte string. + 'body': b'', + } + return serialized + + # Some extra utility methods subclasses can use. + + def _timestamp_iso8601(self, value): + """Return ISO8601 timestamp with precision based on timestamp_precision.""" + # Smithy's standard is milliseconds, so we truncate the timestamp if the millisecond flag is set to true + if self._timestamp_precision == TIMESTAMP_PRECISION_MILLISECOND: + milliseconds = value.microsecond // 1000 + return ( + value.strftime('%Y-%m-%dT%H:%M:%S') + f'.{milliseconds:03d}Z' + ) + else: + # Otherwise we continue supporting microseconds in iso8601 for legacy reasons + if value.microsecond > 0: + timestamp_format = ISO8601_MICRO + else: + timestamp_format = ISO8601 + return value.strftime(timestamp_format) + + def _timestamp_unixtimestamp(self, value): + """Return unix timestamp with precision based on timestamp_precision.""" + timestamp = calendar.timegm(value.timetuple()) + if self._timestamp_precision == TIMESTAMP_PRECISION_MILLISECOND: + milliseconds = (value.microsecond // 1000) / 1000.0 + return timestamp + milliseconds + elif self._timestamp_precision == TIMESTAMP_PRECISION_LEGACY: + return timestamp + elif value.microsecond > 0: + # Add the microseconds as integers before dividing so the float is only + # rounded once. Dividing first and then adding rounds twice, which can + # produce a slightly different value, e.g. 1 + 3691 / 10**6 gives + # 1.0036909999999999 instead of 1.003691. + return (timestamp * 10**6 + value.microsecond) / 10**6 + return timestamp + + def _timestamp_rfc822(self, value): + """Return RFC822 timestamp (always second precision - RFC doesn't support sub-second).""" + # RFC 2822 doesn't support sub-second precision, so always use second precision format + if isinstance(value, datetime.datetime): + value = int(calendar.timegm(value.timetuple())) + return formatdate(value, usegmt=True) + + def _convert_timestamp_to_str(self, value, timestamp_format=None): + if timestamp_format is None: + timestamp_format = self.TIMESTAMP_FORMAT + timestamp_format = timestamp_format.lower() + datetime_obj = parse_to_aware_datetime(value) + converter = getattr(self, f'_timestamp_{timestamp_format}') + final_value = converter(datetime_obj) + return final_value + + def _get_serialized_name(self, shape, default_name): + # Returns the serialized name for the shape if it exists. + # Otherwise it will return the passed in default_name. + return shape.serialization.get('name', default_name) + + def _get_base64(self, value): + # Returns the base64-encoded version of value, handling + # both strings and bytes. The returned value is a string + # via the default encoding. + if isinstance(value, str): + value = value.encode(self.DEFAULT_ENCODING) + return base64.b64encode(value).strip().decode(self.DEFAULT_ENCODING) + + def _expand_host_prefix(self, parameters, operation_model): + operation_endpoint = operation_model.endpoint + if ( + operation_endpoint is None + or 'hostPrefix' not in operation_endpoint + ): + return None + + host_prefix_expression = operation_endpoint['hostPrefix'] + if operation_model.input_shape is None: + return host_prefix_expression + input_members = operation_model.input_shape.members + host_labels = [ + member + for member, shape in input_members.items() + if shape.serialization.get('hostLabel') + ] + format_kwargs = {} + bad_labels = [] + for name in host_labels: + param = parameters[name] + if not HOST_PREFIX_RE.match(param): + bad_labels.append(name) + format_kwargs[name] = param + if bad_labels: + raise ParamValidationError( + report=( + f"Invalid value for parameter(s): {', '.join(bad_labels)}. " + "Must contain only alphanumeric characters, hyphen, " + "or period." + ) + ) + return host_prefix_expression.format(**format_kwargs) + + def _is_shape_flattened(self, shape): + return shape.serialization.get('flattened') + + def _handle_float(self, value): + if value == float("Infinity"): + value = "Infinity" + elif value == float("-Infinity"): + value = "-Infinity" + elif math.isnan(value): + value = "NaN" + return value + + def _handle_query_compatible_trait(self, operation_model, serialized): + if operation_model.service_model.is_query_compatible: + serialized['headers']['x-amzn-query-mode'] = 'true' + + +class QuerySerializer(Serializer): + TIMESTAMP_FORMAT = 'iso8601' + + def serialize_to_request(self, parameters, operation_model): + shape = operation_model.input_shape + serialized = self._create_default_request() + serialized['method'] = operation_model.http.get( + 'method', self.DEFAULT_METHOD + ) + serialized['headers'] = { + 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' + } + # The query serializer only deals with body params so + # that's what we hand off the _serialize_* methods. + body_params = self.MAP_TYPE() + body_params['Action'] = operation_model.name + body_params['Version'] = operation_model.metadata['apiVersion'] + if shape is not None: + self._serialize(body_params, parameters, shape) + serialized['body'] = body_params + + host_prefix = self._expand_host_prefix(parameters, operation_model) + if host_prefix is not None: + serialized['host_prefix'] = host_prefix + + return serialized + + def _serialize(self, serialized, value, shape, prefix=''): + # serialized: The dict that is incrementally added to with the + # final serialized parameters. + # value: The current user input value. + # shape: The shape object that describes the structure of the + # input. + # prefix: The incrementally built up prefix for the serialized + # key (i.e Foo.bar.members.1). + method = getattr( + self, + f'_serialize_type_{shape.type_name}', + self._default_serialize, + ) + method(serialized, value, shape, prefix=prefix) + + def _serialize_type_structure(self, serialized, value, shape, prefix=''): + members = shape.members + for key, value in value.items(): + member_shape = members[key] + member_prefix = self._get_serialized_name(member_shape, key) + if prefix: + member_prefix = f'{prefix}.{member_prefix}' + self._serialize(serialized, value, member_shape, member_prefix) + + def _serialize_type_list(self, serialized, value, shape, prefix=''): + if not value: + # The query protocol serializes empty lists. + serialized[prefix] = '' + return + if self._is_shape_flattened(shape): + list_prefix = prefix + if shape.member.serialization.get('name'): + name = self._get_serialized_name(shape.member, default_name='') + # Replace '.Original' with '.{name}'. + list_prefix = '.'.join(prefix.split('.')[:-1] + [name]) + else: + list_name = shape.member.serialization.get('name', 'member') + list_prefix = f'{prefix}.{list_name}' + for i, element in enumerate(value, 1): + element_prefix = f'{list_prefix}.{i}' + element_shape = shape.member + self._serialize(serialized, element, element_shape, element_prefix) + + def _serialize_type_map(self, serialized, value, shape, prefix=''): + if self._is_shape_flattened(shape): + full_prefix = prefix + else: + full_prefix = f'{prefix}.entry' + template = full_prefix + '.{i}.{suffix}' + key_shape = shape.key + value_shape = shape.value + key_suffix = self._get_serialized_name(key_shape, default_name='key') + value_suffix = self._get_serialized_name(value_shape, 'value') + for i, key in enumerate(value, 1): + key_prefix = template.format(i=i, suffix=key_suffix) + value_prefix = template.format(i=i, suffix=value_suffix) + self._serialize(serialized, key, key_shape, key_prefix) + self._serialize(serialized, value[key], value_shape, value_prefix) + + def _serialize_type_blob(self, serialized, value, shape, prefix=''): + # Blob args must be base64 encoded. + serialized[prefix] = self._get_base64(value) + + def _serialize_type_timestamp(self, serialized, value, shape, prefix=''): + serialized[prefix] = self._convert_timestamp_to_str( + value, shape.serialization.get('timestampFormat') + ) + + def _serialize_type_boolean(self, serialized, value, shape, prefix=''): + if value: + serialized[prefix] = 'true' + else: + serialized[prefix] = 'false' + + def _default_serialize(self, serialized, value, shape, prefix=''): + serialized[prefix] = value + + def _serialize_type_float(self, serialized, value, shape, prefix=''): + serialized[prefix] = self._handle_float(value) + + def _serialize_type_double(self, serialized, value, shape, prefix=''): + self._serialize_type_float(serialized, value, shape, prefix) + + +class EC2Serializer(QuerySerializer): + """EC2 specific customizations to the query protocol serializers. + + The EC2 model is almost, but not exactly, similar to the query protocol + serializer. This class encapsulates those differences. The model + will have be marked with a ``protocol`` of ``ec2``, so you don't need + to worry about wiring this class up correctly. + + """ + + def _get_serialized_name(self, shape, default_name): + # Returns the serialized name for the shape if it exists. + # Otherwise it will return the passed in capitalized default_name. + if 'queryName' in shape.serialization: + return shape.serialization['queryName'] + elif 'name' in shape.serialization: + # A locationName is always capitalized + # on input for the ec2 protocol. + name = shape.serialization['name'] + return name[0].upper() + name[1:] + else: + return default_name + + def _serialize_type_list(self, serialized, value, shape, prefix=''): + for i, element in enumerate(value, 1): + element_prefix = f'{prefix}.{i}' + element_shape = shape.member + self._serialize(serialized, element, element_shape, element_prefix) + + +class JSONSerializer(Serializer): + TIMESTAMP_FORMAT = 'unixtimestamp' + + def serialize_to_request(self, parameters, operation_model): + target = '{}.{}'.format( + operation_model.metadata['targetPrefix'], + operation_model.name, + ) + json_version = operation_model.metadata['jsonVersion'] + serialized = self._create_default_request() + serialized['method'] = operation_model.http.get( + 'method', self.DEFAULT_METHOD + ) + serialized['headers'] = { + 'X-Amz-Target': target, + 'Content-Type': f'application/x-amz-json-{json_version}', + } + self._handle_query_compatible_trait(operation_model, serialized) + + body = self.MAP_TYPE() + input_shape = operation_model.input_shape + if input_shape is not None: + self._serialize(body, parameters, input_shape) + serialized['body'] = json.dumps(body).encode(self.DEFAULT_ENCODING) + + host_prefix = self._expand_host_prefix(parameters, operation_model) + if host_prefix is not None: + serialized['host_prefix'] = host_prefix + + return serialized + + def _serialize(self, serialized, value, shape, key=None): + method = getattr( + self, + f'_serialize_type_{shape.type_name}', + self._default_serialize, + ) + method(serialized, value, shape, key) + + def _serialize_type_structure(self, serialized, value, shape, key): + if shape.is_document_type: + serialized[key] = value + else: + if key is not None: + # If a key is provided, this is a result of a recursive + # call so we need to add a new child dict as the value + # of the passed in serialized dict. We'll then add + # all the structure members as key/vals in the new serialized + # dictionary we just created. + new_serialized = self.MAP_TYPE() + serialized[key] = new_serialized + serialized = new_serialized + members = shape.members + for member_key, member_value in value.items(): + member_shape = members[member_key] + if 'name' in member_shape.serialization: + member_key = member_shape.serialization['name'] + self._serialize( + serialized, member_value, member_shape, member_key + ) + + def _serialize_type_map(self, serialized, value, shape, key): + map_obj = self.MAP_TYPE() + serialized[key] = map_obj + for sub_key, sub_value in value.items(): + self._serialize(map_obj, sub_value, shape.value, sub_key) + + def _serialize_type_list(self, serialized, value, shape, key): + list_obj = [] + serialized[key] = list_obj + for list_item in value: + wrapper = {} + # The JSON list serialization is the only case where we aren't + # setting a key on a dict. We handle this by using + # a __current__ key on a wrapper dict to serialize each + # list item before appending it to the serialized list. + self._serialize(wrapper, list_item, shape.member, "__current__") + list_obj.append(wrapper["__current__"]) + + def _default_serialize(self, serialized, value, shape, key): + serialized[key] = value + + def _serialize_type_timestamp(self, serialized, value, shape, key): + serialized[key] = self._convert_timestamp_to_str( + value, shape.serialization.get('timestampFormat') + ) + + def _serialize_type_blob(self, serialized, value, shape, key): + serialized[key] = self._get_base64(value) + + def _serialize_type_float(self, serialized, value, shape, prefix=''): + if isinstance(value, decimal.Decimal): + value = float(value) + serialized[prefix] = self._handle_float(value) + + def _serialize_type_double(self, serialized, value, shape, prefix=''): + self._serialize_type_float(serialized, value, shape, prefix) + + +class CBORSerializer(Serializer): + UNSIGNED_INT_MAJOR_TYPE = 0 + NEGATIVE_INT_MAJOR_TYPE = 1 + BLOB_MAJOR_TYPE = 2 + STRING_MAJOR_TYPE = 3 + LIST_MAJOR_TYPE = 4 + MAP_MAJOR_TYPE = 5 + TAG_MAJOR_TYPE = 6 + FLOAT_AND_SIMPLE_MAJOR_TYPE = 7 + + def _serialize_data_item(self, serialized, value, shape, key=None): + method = getattr(self, f'_serialize_type_{shape.type_name}') + if method is None: + raise ValueError( + f"Unrecognized C2J type: {shape.type_name}, unable to " + f"serialize request" + ) + method(serialized, value, shape, key) + + def _serialize_type_integer(self, serialized, value, shape, key): + if value >= 0: + major_type = self.UNSIGNED_INT_MAJOR_TYPE + else: + major_type = self.NEGATIVE_INT_MAJOR_TYPE + # The only differences in serializing negative and positive integers is + # that for negative, we set the major type to 1 and set the value to -1 + # minus the value + value = -1 - value + additional_info, num_bytes = self._get_additional_info_and_num_bytes( + value + ) + initial_byte = self._get_initial_byte(major_type, additional_info) + if num_bytes == 0: + serialized.extend(initial_byte) + else: + serialized.extend(initial_byte + value.to_bytes(num_bytes, "big")) + + def _serialize_type_long(self, serialized, value, shape, key): + self._serialize_type_integer(serialized, value, shape, key) + + def _serialize_type_blob(self, serialized, value, shape, key): + if isinstance(value, str): + value = value.encode('utf-8') + elif not isinstance(value, (bytes, bytearray)): + # We support file-like objects for blobs; these already have been + # validated to ensure they have a read method + value = value.read() + length = len(value) + additional_info, num_bytes = self._get_additional_info_and_num_bytes( + length + ) + initial_byte = self._get_initial_byte( + self.BLOB_MAJOR_TYPE, additional_info + ) + if num_bytes == 0: + serialized.extend(initial_byte) + else: + serialized.extend(initial_byte + length.to_bytes(num_bytes, "big")) + serialized.extend(value) + + def _serialize_type_string(self, serialized, value, shape, key): + encoded = value.encode('utf-8') + length = len(encoded) + additional_info, num_bytes = self._get_additional_info_and_num_bytes( + length + ) + initial_byte = self._get_initial_byte( + self.STRING_MAJOR_TYPE, additional_info + ) + if num_bytes == 0: + serialized.extend(initial_byte + encoded) + else: + serialized.extend( + initial_byte + length.to_bytes(num_bytes, "big") + encoded + ) + + def _serialize_type_list(self, serialized, value, shape, key): + length = len(value) + additional_info, num_bytes = self._get_additional_info_and_num_bytes( + length + ) + initial_byte = self._get_initial_byte( + self.LIST_MAJOR_TYPE, additional_info + ) + if num_bytes == 0: + serialized.extend(initial_byte) + else: + serialized.extend(initial_byte + length.to_bytes(num_bytes, "big")) + for item in value: + self._serialize_data_item(serialized, item, shape.member) + + def _serialize_type_map(self, serialized, value, shape, key): + length = len(value) + additional_info, num_bytes = self._get_additional_info_and_num_bytes( + length + ) + initial_byte = self._get_initial_byte( + self.MAP_MAJOR_TYPE, additional_info + ) + if num_bytes == 0: + serialized.extend(initial_byte) + else: + serialized.extend(initial_byte + length.to_bytes(num_bytes, "big")) + for key_item, item in value.items(): + self._serialize_data_item(serialized, key_item, shape.key) + self._serialize_data_item(serialized, item, shape.value) + + def _serialize_type_structure(self, serialized, value, shape, key): + if key is not None: + # For nested structures, we need to serialize the key first + self._serialize_data_item(serialized, key, shape.key_shape) + + # Remove `None` values from the dictionary + value = {k: v for k, v in value.items() if v is not None} + + map_length = len(value) + additional_info, num_bytes = self._get_additional_info_and_num_bytes( + map_length + ) + initial_byte = self._get_initial_byte( + self.MAP_MAJOR_TYPE, additional_info + ) + if num_bytes == 0: + serialized.extend(initial_byte) + else: + serialized.extend( + initial_byte + map_length.to_bytes(num_bytes, "big") + ) + + members = shape.members + for member_key, member_value in value.items(): + member_shape = members[member_key] + if 'name' in member_shape.serialization: + member_key = member_shape.serialization['name'] + if member_value is not None: + self._serialize_type_string(serialized, member_key, None, None) + self._serialize_data_item( + serialized, member_value, member_shape + ) + + def _serialize_type_timestamp(self, serialized, value, shape, key): + timestamp = self._convert_timestamp_to_str(value) + tag = 1 # Use tag 1 for unix timestamp + initial_byte = self._get_initial_byte(self.TAG_MAJOR_TYPE, tag) + serialized.extend(initial_byte) # Tagging the timestamp + # Tag 1 permits either an integer or a floating-point epoch seconds + # value; a float is used when sub-second precision is present. + if isinstance(timestamp, float): + self._serialize_type_double(serialized, timestamp, shape, key) + else: + self._serialize_type_integer(serialized, timestamp, shape, key) + + def _serialize_type_float(self, serialized, value, shape, key): + if self._is_special_number(value): + serialized.extend( + self._get_bytes_for_special_numbers(value) + ) # Handle special values like NaN or Infinity + else: + initial_byte = self._get_initial_byte( + self.FLOAT_AND_SIMPLE_MAJOR_TYPE, 26 + ) + serialized.extend(initial_byte + struct.pack(">f", value)) + + def _serialize_type_double(self, serialized, value, shape, key): + if self._is_special_number(value): + serialized.extend( + self._get_bytes_for_special_numbers(value) + ) # Handle special values like NaN or Infinity + else: + initial_byte = self._get_initial_byte( + self.FLOAT_AND_SIMPLE_MAJOR_TYPE, 27 + ) + serialized.extend(initial_byte + struct.pack(">d", value)) + + def _serialize_type_boolean(self, serialized, value, shape, key): + additional_info = 21 if value else 20 + serialized.extend( + self._get_initial_byte( + self.FLOAT_AND_SIMPLE_MAJOR_TYPE, additional_info + ) + ) + + def _get_additional_info_and_num_bytes(self, value): + # Values under 24 can be stored in the initial byte and don't need further + # encoding + if value < 24: + return value, 0 + # Values between 24 and 255 (inclusive) can be stored in 1 byte and + # correspond to additional info 24 + elif value < 256: + return 24, 1 + # Values up to 65535 can be stored in two bytes and correspond to additional + # info 25 + elif value < 65536: + return 25, 2 + # Values up to 4294967296 can be stored in four bytes and correspond to + # additional info 26 + elif value < 4294967296: + return 26, 4 + # The maximum number of bytes in a definite length data items is 8 which + # to additional info 27 + else: + return 27, 8 + + def _get_initial_byte(self, major_type, additional_info): + # The highest order three bits are the major type, so we need to bitshift the + # major type by 5 + major_type_bytes = major_type << 5 + return (major_type_bytes | additional_info).to_bytes(1, "big") + + def _is_special_number(self, value): + return any( + [ + value == float('inf'), + value == float('-inf'), + math.isnan(value), + ] + ) + + def _get_bytes_for_special_numbers(self, value): + additional_info = 25 + initial_byte = self._get_initial_byte( + self.FLOAT_AND_SIMPLE_MAJOR_TYPE, additional_info + ) + if value == float('inf'): + return initial_byte + struct.pack(">H", 0x7C00) + elif value == float('-inf'): + return initial_byte + struct.pack(">H", 0xFC00) + elif math.isnan(value): + return initial_byte + struct.pack(">H", 0x7E00) + + +class BaseRestSerializer(Serializer): + """Base class for rest protocols. + + The only variance between the various rest protocols is the + way that the body is serialized. All other aspects (headers, uri, etc.) + are the same and logic for serializing those aspects lives here. + + Subclasses must implement the ``_serialize_body_params`` method. + + """ + + QUERY_STRING_TIMESTAMP_FORMAT = 'iso8601' + HEADER_TIMESTAMP_FORMAT = 'rfc822' + # This is a list of known values for the "location" key in the + # serialization dict. The location key tells us where on the request + # to put the serialized value. + KNOWN_LOCATIONS = ['uri', 'querystring', 'header', 'headers'] + + def serialize_to_request(self, parameters, operation_model): + serialized = self._create_default_request() + serialized['method'] = operation_model.http.get( + 'method', self.DEFAULT_METHOD + ) + shape = operation_model.input_shape + + host_prefix = self._expand_host_prefix(parameters, operation_model) + if host_prefix is not None: + serialized['host_prefix'] = host_prefix + + if shape is None: + serialized['url_path'] = operation_model.http['requestUri'] + return serialized + shape_members = shape.members + # While the ``serialized`` key holds the final serialized request + # data, we need interim dicts for the various locations of the + # request. We need this for the uri_path_kwargs and the + # query_string_kwargs because they are templated, so we need + # to gather all the needed data for the string template, + # then we render the template. The body_kwargs is needed + # because once we've collected them all, we run them through + # _serialize_body_params, which for rest-json, creates JSON, + # and for rest-xml, will create XML. This is what the + # ``partitioned`` dict below is for. + partitioned = { + 'uri_path_kwargs': self.MAP_TYPE(), + 'query_string_kwargs': self.MAP_TYPE(), + 'body_kwargs': self.MAP_TYPE(), + 'headers': self.MAP_TYPE(), + } + for param_name, param_value in parameters.items(): + if param_value is None: + # Don't serialize any parameter with a None value. + continue + self._partition_parameters( + partitioned, param_name, param_value, shape_members + ) + serialized['url_path'] = self._render_uri_template( + operation_model.http['requestUri'], partitioned['uri_path_kwargs'] + ) + + if 'authPath' in operation_model.http: + serialized['auth_path'] = self._render_uri_template( + operation_model.http['authPath'], + partitioned['uri_path_kwargs'], + ) + # Note that we lean on the http implementation to handle the case + # where the requestUri path already has query parameters. + # The bundled http client, requests, already supports this. + serialized['query_string'] = partitioned['query_string_kwargs'] + if partitioned['headers']: + serialized['headers'] = partitioned['headers'] + self._serialize_payload( + partitioned, parameters, serialized, shape, shape_members + ) + self._serialize_content_type(serialized, shape, shape_members) + + return serialized + + def _render_uri_template(self, uri_template, params): + # We need to handle two cases:: + # + # /{Bucket}/foo + # /{Key+}/bar + # A label ending with '+' is greedy. There can only + # be one greedy key. + encoded_params = {} + for template_param in re.findall(r'{(.*?)}', uri_template): + if template_param.endswith('+'): + encoded_params[template_param] = percent_encode( + params[template_param[:-1]], safe='/~' + ) + else: + encoded_params[template_param] = percent_encode( + params[template_param] + ) + return uri_template.format(**encoded_params) + + def _serialize_payload( + self, partitioned, parameters, serialized, shape, shape_members + ): + # partitioned - The user input params partitioned by location. + # parameters - The user input params. + # serialized - The final serialized request dict. + # shape - Describes the expected input shape + # shape_members - The members of the input struct shape + payload_member = shape.serialization.get('payload') + if self._has_streaming_payload(payload_member, shape_members): + # If it's streaming, then the body is just the + # value of the payload. + body_payload = parameters.get(payload_member, b'') + body_payload = self._encode_payload(body_payload) + serialized['body'] = body_payload + elif payload_member is not None: + # If there's a payload member, we serialized that + # member to they body. + body_params = parameters.get(payload_member) + if body_params is not None: + serialized['body'] = self._serialize_body_params( + body_params, shape_members[payload_member] + ) + else: + serialized['body'] = self._serialize_empty_body() + elif partitioned['body_kwargs']: + serialized['body'] = self._serialize_body_params( + partitioned['body_kwargs'], shape + ) + elif self._requires_empty_body(shape): + serialized['body'] = self._serialize_empty_body() + + def _serialize_empty_body(self): + return b'' + + def _serialize_content_type(self, serialized, shape, shape_members): + """ + Some protocols require varied Content-Type headers + depending on user input. This allows subclasses to apply + this conditionally. + """ + pass + + def _requires_empty_body(self, shape): + """ + Some protocols require a specific body to represent an empty + payload. This allows subclasses to apply this conditionally. + """ + return False + + def _has_streaming_payload(self, payload, shape_members): + """Determine if payload is streaming (a blob or string).""" + return payload is not None and shape_members[payload].type_name in ( + 'blob', + 'string', + ) + + def _encode_payload(self, body): + if isinstance(body, str): + return body.encode(self.DEFAULT_ENCODING) + return body + + def _partition_parameters( + self, partitioned, param_name, param_value, shape_members + ): + # This takes the user provided input parameter (``param``) + # and figures out where they go in the request dict. + # Some params are HTTP headers, some are used in the URI, some + # are in the request body. This method deals with this. + member = shape_members[param_name] + location = member.serialization.get('location') + key_name = member.serialization.get('name', param_name) + if location == 'uri': + uri_path_value = self._get_uri_and_query_string_value( + param_value, member + ) + partitioned['uri_path_kwargs'][key_name] = uri_path_value + elif location == 'querystring': + if isinstance(param_value, dict): + partitioned['query_string_kwargs'].update(param_value) + elif member.type_name == 'list': + new_param = [ + self._get_uri_and_query_string_value(value, member.member) + for value in param_value + ] + partitioned['query_string_kwargs'][key_name] = new_param + else: + new_param = self._get_uri_and_query_string_value( + param_value, member + ) + partitioned['query_string_kwargs'][key_name] = new_param + elif location == 'header': + shape = shape_members[param_name] + if not param_value and shape.type_name == 'list': + # Empty lists should not be set on the headers + return + partitioned['headers'][key_name] = self._convert_header_value( + shape, param_value + ) + elif location == 'headers': + # 'headers' is a bit of an oddball. The ``key_name`` + # is actually really a prefix for the header names: + header_prefix = key_name + # The value provided by the user is a dict so we'll be + # creating multiple header key/val pairs. The key + # name to use for each header is the header_prefix (``key_name``) + # plus the key provided by the user. + self._do_serialize_header_map( + header_prefix, partitioned['headers'], param_value + ) + else: + partitioned['body_kwargs'][param_name] = param_value + + def _get_uri_and_query_string_value(self, param_value, member): + if member.type_name == 'boolean': + return str(param_value).lower() + elif member.type_name == 'timestamp': + timestamp_format = member.serialization.get( + 'timestampFormat', self.QUERY_STRING_TIMESTAMP_FORMAT + ) + return self._convert_timestamp_to_str( + param_value, timestamp_format + ) + elif member.type_name in ['float', 'double']: + return str(self._handle_float(param_value)) + return param_value + + def _do_serialize_header_map(self, header_prefix, headers, user_input): + for key, val in user_input.items(): + full_key = header_prefix + key + headers[full_key] = val + + def _serialize_body_params(self, params, shape): + raise NotImplementedError('_serialize_body_params') + + def _convert_header_value(self, shape, value): + if shape.type_name == 'timestamp': + datetime_obj = parse_to_aware_datetime(value) + timestamp = calendar.timegm(datetime_obj.utctimetuple()) + timestamp_format = shape.serialization.get( + 'timestampFormat', self.HEADER_TIMESTAMP_FORMAT + ) + return str( + self._convert_timestamp_to_str(timestamp, timestamp_format) + ) + elif shape.type_name == 'list': + if shape.member.type_name == "string": + converted_value = [ + self._escape_header_list_string(v) + for v in value + if v is not None + ] + else: + converted_value = [ + self._convert_header_value(shape.member, v) + for v in value + if v is not None + ] + return ",".join(converted_value) + elif is_json_value_header(shape): + # Serialize with no spaces after separators to save space in + # the header. + return self._get_base64(json.dumps(value, separators=(',', ':'))) + elif shape.type_name == 'boolean': + return str(value).lower() + elif shape.type_name in ['float', 'double']: + return str(self._handle_float(value)) + else: + return str(value) + + def _escape_header_list_string(self, value): + # Escapes a header list string by wrapping it in double quotes if it contains + # a comma or a double quote, and escapes any internal double quotes. + if '"' in value or ',' in value: + return '"' + value.replace('"', '\\"') + '"' + else: + return value + + +class BaseRpcV2Serializer(Serializer): + """Base class for RPCv2 protocols. + + The only variance between the various RPCv2 protocols is the + way that the body is serialized. All other aspects (headers, uri, etc.) + are the same and logic for serializing those aspects lives here. + + Subclasses must implement the ``_serialize_body_params`` and + ``_serialize_headers`` methods. + + """ + + def serialize_to_request(self, parameters, operation_model): + serialized = self._create_default_request() + service_name = operation_model.service_model.metadata['targetPrefix'] + operation_name = operation_model.name + serialized['url_path'] = ( + f'/service/{service_name}/operation/{operation_name}' + ) + + input_shape = operation_model.input_shape + if input_shape is not None: + self._serialize_payload(parameters, serialized, input_shape) + + host_prefix = self._expand_host_prefix(parameters, operation_model) + if host_prefix is not None: + serialized['host_prefix'] = host_prefix + + self._serialize_headers(serialized, operation_model) + + return serialized + + def _serialize_payload(self, parameters, serialized, shape): + body_payload = self._serialize_body_params(parameters, shape) + serialized['body'] = body_payload + + def _serialize_headers(self, serialized, operation_model): + raise NotImplementedError("_serialize_headers") + + def _serialize_body_params(self, parameters, shape): + raise NotImplementedError("_serialize_body_params") + + +class RestJSONSerializer(BaseRestSerializer, JSONSerializer): + def _serialize_empty_body(self): + return b'{}' + + def _requires_empty_body(self, shape): + """ + Serialize an empty JSON object whenever the shape has + members not targeting a location. + """ + for member, val in shape.members.items(): + if 'location' not in val.serialization: + return True + return False + + def _serialize_content_type(self, serialized, shape, shape_members): + """Set Content-Type to application/json for all structured bodies.""" + payload = shape.serialization.get('payload') + if self._has_streaming_payload(payload, shape_members): + # Don't apply content-type to streaming bodies + return + + has_body = serialized['body'] != b'' + has_content_type = has_header('Content-Type', serialized['headers']) + if has_body and not has_content_type: + serialized['headers']['Content-Type'] = 'application/json' + + def _serialize_body_params(self, params, shape): + serialized_body = self.MAP_TYPE() + self._serialize(serialized_body, params, shape) + return json.dumps(serialized_body).encode(self.DEFAULT_ENCODING) + + +class RestXMLSerializer(BaseRestSerializer): + TIMESTAMP_FORMAT = 'iso8601' + + def _serialize_body_params(self, params, shape): + root_name = shape.serialization['name'] + pseudo_root = ElementTree.Element('') + self._serialize(shape, params, pseudo_root, root_name) + real_root = list(pseudo_root)[0] + return ElementTree.tostring(real_root, encoding=self.DEFAULT_ENCODING) + + def _serialize(self, shape, params, xmlnode, name): + method = getattr( + self, + f'_serialize_type_{shape.type_name}', + self._default_serialize, + ) + method(xmlnode, params, shape, name) + + def _serialize_type_structure(self, xmlnode, params, shape, name): + structure_node = ElementTree.SubElement(xmlnode, name) + + self._add_xml_namespace(shape, structure_node) + for key, value in params.items(): + member_shape = shape.members[key] + member_name = member_shape.serialization.get('name', key) + # We need to special case member shapes that are marked as an + # xmlAttribute. Rather than serializing into an XML child node, + # we instead serialize the shape to an XML attribute of the + # *current* node. + if value is None: + # Don't serialize any param whose value is None. + return + if member_shape.serialization.get('xmlAttribute'): + # xmlAttributes must have a serialization name. + xml_attribute_name = member_shape.serialization['name'] + structure_node.attrib[xml_attribute_name] = value + continue + self._serialize(member_shape, value, structure_node, member_name) + + def _serialize_type_list(self, xmlnode, params, shape, name): + member_shape = shape.member + if shape.serialization.get('flattened'): + element_name = name + list_node = xmlnode + else: + element_name = member_shape.serialization.get('name', 'member') + list_node = ElementTree.SubElement(xmlnode, name) + self._add_xml_namespace(shape, list_node) + for item in params: + self._serialize(member_shape, item, list_node, element_name) + + def _serialize_type_map(self, xmlnode, params, shape, name): + # Given the ``name`` of MyMap, and input of {"key1": "val1"} + # we serialize this as: + # + # + # key1 + # val1 + # + # + if not self._is_shape_flattened(shape): + node = ElementTree.SubElement(xmlnode, name) + self._add_xml_namespace(shape, node) + + for key, value in params.items(): + sub_node = ( + ElementTree.SubElement(xmlnode, name) + if self._is_shape_flattened(shape) + else ElementTree.SubElement(node, 'entry') + ) + key_name = self._get_serialized_name(shape.key, default_name='key') + val_name = self._get_serialized_name( + shape.value, default_name='value' + ) + self._serialize(shape.key, key, sub_node, key_name) + self._serialize(shape.value, value, sub_node, val_name) + + def _serialize_type_boolean(self, xmlnode, params, shape, name): + # For scalar types, the 'params' attr is actually just a scalar + # value representing the data we need to serialize as a boolean. + # It will either be 'true' or 'false' + node = ElementTree.SubElement(xmlnode, name) + if params: + str_value = 'true' + else: + str_value = 'false' + node.text = str_value + self._add_xml_namespace(shape, node) + + def _serialize_type_blob(self, xmlnode, params, shape, name): + node = ElementTree.SubElement(xmlnode, name) + node.text = self._get_base64(params) + self._add_xml_namespace(shape, node) + + def _serialize_type_timestamp(self, xmlnode, params, shape, name): + node = ElementTree.SubElement(xmlnode, name) + node.text = str( + self._convert_timestamp_to_str( + params, shape.serialization.get('timestampFormat') + ) + ) + self._add_xml_namespace(shape, node) + + def _serialize_type_float(self, xmlnode, params, shape, name): + node = ElementTree.SubElement(xmlnode, name) + node.text = str(self._handle_float(params)) + self._add_xml_namespace(shape, node) + + def _serialize_type_double(self, xmlnode, params, shape, name): + self._serialize_type_float(xmlnode, params, shape, name) + + def _default_serialize(self, xmlnode, params, shape, name): + node = ElementTree.SubElement(xmlnode, name) + node.text = str(params) + self._add_xml_namespace(shape, node) + + def _add_xml_namespace(self, shape, structure_node): + if 'xmlNamespace' in shape.serialization: + namespace_metadata = shape.serialization['xmlNamespace'] + attribute_name = 'xmlns' + if isinstance(namespace_metadata, dict): + if namespace_metadata.get('prefix'): + attribute_name += f":{namespace_metadata['prefix']}" + structure_node.attrib[attribute_name] = namespace_metadata[ + 'uri' + ] + elif isinstance(namespace_metadata, str): + structure_node.attrib[attribute_name] = namespace_metadata + + +class RpcV2CBORSerializer(BaseRpcV2Serializer, CBORSerializer): + TIMESTAMP_FORMAT = 'unixtimestamp' + + def serialize_to_request(self, parameters, operation_model): + register_feature_id('PROTOCOL_RPC_V2_CBOR') + return super().serialize_to_request(parameters, operation_model) + + def _serialize_body_params(self, parameters, input_shape): + body = bytearray() + self._serialize_data_item(body, parameters, input_shape) + return bytes(body) + + def _serialize_headers(self, serialized, operation_model): + serialized['headers']['smithy-protocol'] = 'rpc-v2-cbor' + + if operation_model.has_event_stream_output: + header_val = 'application/vnd.amazon.eventstream' + else: + header_val = 'application/cbor' + self._handle_query_compatible_trait(operation_model, serialized) + + has_body = serialized['body'] != b'' + has_content_type = has_header('Content-Type', serialized['headers']) + + serialized['headers']['Accept'] = header_val + if not has_content_type and has_body: + serialized['headers']['Content-Type'] = header_val + + +SERIALIZERS = { + 'ec2': EC2Serializer, + 'query': QuerySerializer, + 'json': JSONSerializer, + 'rest-json': RestJSONSerializer, + 'rest-xml': RestXMLSerializer, + 'smithy-rpc-v2-cbor': RpcV2CBORSerializer, +} diff --git a/venv/lib/python3.12/site-packages/botocore/session.py b/venv/lib/python3.12/site-packages/botocore/session.py new file mode 100644 index 0000000..c5a61b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/session.py @@ -0,0 +1,1330 @@ +# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +""" +This module contains the main interface to the botocore package, the +Session object. +""" + +import copy +import logging +import os +import platform +import socket +import warnings + +import botocore.client +import botocore.configloader +import botocore.credentials +import botocore.tokens +from botocore import ( + UNSIGNED, + __version__, + handlers, + invoke_initializers, + monitoring, + paginate, + retryhandler, + translate, + waiter, +) +from botocore.compat import ( + HAS_CRT, # noqa: F401 + MutableMapping, +) +from botocore.configprovider import ( + BOTOCORE_DEFAUT_SESSION_VARIABLES, + ConfigChainFactory, + ConfiguredEndpointProvider, + ConfigValueStore, + DefaultConfigResolver, + SmartDefaultsConfigStoreFactory, + create_botocore_default_config_mapping, +) +from botocore.context import get_context, with_current_context +from botocore.errorfactory import ClientExceptionsFactory +from botocore.exceptions import ( + ConfigNotFound, + InvalidDefaultsMode, + PartialCredentialsError, + ProfileNotFound, + UnknownServiceError, +) +from botocore.hooks import ( + EventAliaser, + HierarchicalEmitter, + first_non_none_response, +) +from botocore.loaders import create_loader +from botocore.model import ServiceModel +from botocore.parsers import ResponseParserFactory +from botocore.plugin import get_botocore_plugins, load_client_plugins +from botocore.regions import EndpointResolver +from botocore.useragent import UserAgentString, register_feature_id +from botocore.utils import ( + EVENT_ALIASES, + IMDSRegionProvider, + validate_region_name, +) + +logger = logging.getLogger(__name__) + + +class Session: + """ + The Session object collects together useful functionality + from `botocore` as well as important data such as configuration + information and credentials into a single, easy-to-use object. + + :ivar available_profiles: A list of profiles defined in the config + file associated with this session. + :ivar profile: The current profile. + """ + + SESSION_VARIABLES = copy.copy(BOTOCORE_DEFAUT_SESSION_VARIABLES) + + #: The default format string to use when configuring the botocore logger. + LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' + + def __init__( + self, + session_vars=None, + event_hooks=None, + include_builtin_handlers=True, + profile=None, + ): + """ + Create a new Session object. + + :type session_vars: dict + :param session_vars: A dictionary that is used to override some or all + of the environment variables associated with this session. The + key/value pairs defined in this dictionary will override the + corresponding variables defined in ``SESSION_VARIABLES``. + + :type event_hooks: BaseEventHooks + :param event_hooks: The event hooks object to use. If one is not + provided, an event hooks object will be automatically created + for you. + + :type include_builtin_handlers: bool + :param include_builtin_handlers: Indicates whether or not to + automatically register builtin handlers. + + :type profile: str + :param profile: The name of the profile to use for this + session. Note that the profile can only be set when + the session is created. + + """ + if event_hooks is None: + self._original_handler = HierarchicalEmitter() + else: + self._original_handler = event_hooks + self._events = EventAliaser(self._original_handler) + if include_builtin_handlers: + self._register_builtin_handlers(self._events) + self.user_agent_name = 'Botocore' + self.user_agent_version = __version__ + self.user_agent_extra = '' + # The _profile attribute is just used to cache the value + # of the current profile to avoid going through the normal + # config lookup process each access time. + self._profile = None + self._config = None + self._credentials = None + self._auth_token = None + self._profile_map = None + # This is a dict that stores per session specific config variable + # overrides via set_config_variable(). + self._session_instance_vars = {} + if profile is not None: + self._session_instance_vars['profile'] = profile + self._client_config = None + self._last_client_region_used = None + self._components = ComponentLocator() + self._internal_components = ComponentLocator() + self._register_components() + self.session_var_map = SessionVarDict(self, self.SESSION_VARIABLES) + if session_vars is not None: + self.session_var_map.update(session_vars) + invoke_initializers(self) + + def _register_components(self): + self._register_credential_provider() + self._register_token_provider() + self._register_data_loader() + self._register_endpoint_resolver() + self._register_event_emitter() + self._register_response_parser_factory() + self._register_exceptions_factory() + self._register_config_store() + self._register_monitor() + self._register_default_config_resolver() + self._register_smart_defaults_factory() + self._register_user_agent_creator() + + def _register_event_emitter(self): + self._components.register_component('event_emitter', self._events) + + def _register_token_provider(self): + self._components.lazy_register_component( + 'token_provider', self._create_token_resolver + ) + + def _create_token_resolver(self): + return botocore.tokens.create_token_resolver(self) + + def _register_credential_provider(self): + self._components.lazy_register_component( + 'credential_provider', self._create_credential_resolver + ) + + def _create_credential_resolver(self): + return botocore.credentials.create_credential_resolver( + self, region_name=self._last_client_region_used + ) + + def _register_data_loader(self): + self._components.lazy_register_component( + 'data_loader', + lambda: create_loader(self.get_config_variable('data_path')), + ) + + def _register_endpoint_resolver(self): + def create_default_resolver(): + loader = self.get_component('data_loader') + endpoints, path = loader.load_data_with_path('endpoints') + uses_builtin = loader.is_builtin_path(path) + return EndpointResolver(endpoints, uses_builtin_data=uses_builtin) + + self._internal_components.lazy_register_component( + 'endpoint_resolver', create_default_resolver + ) + + def _register_default_config_resolver(self): + def create_default_config_resolver(): + loader = self.get_component('data_loader') + defaults = loader.load_data('sdk-default-configuration') + return DefaultConfigResolver(defaults) + + self._internal_components.lazy_register_component( + 'default_config_resolver', create_default_config_resolver + ) + + def _register_smart_defaults_factory(self): + def create_smart_defaults_factory(): + default_config_resolver = self._get_internal_component( + 'default_config_resolver' + ) + imds_region_provider = IMDSRegionProvider(session=self) + return SmartDefaultsConfigStoreFactory( + default_config_resolver, imds_region_provider + ) + + self._internal_components.lazy_register_component( + 'smart_defaults_factory', create_smart_defaults_factory + ) + + def _register_response_parser_factory(self): + self._components.register_component( + 'response_parser_factory', ResponseParserFactory() + ) + + def _register_exceptions_factory(self): + self._internal_components.register_component( + 'exceptions_factory', ClientExceptionsFactory() + ) + + def _register_builtin_handlers(self, events): + for spec in handlers.BUILTIN_HANDLERS: + if len(spec) == 2: + event_name, handler = spec + self.register(event_name, handler) + else: + event_name, handler, register_type = spec + if register_type is handlers.REGISTER_FIRST: + self._events.register_first(event_name, handler) + elif register_type is handlers.REGISTER_LAST: + self._events.register_last(event_name, handler) + + def _register_config_store(self): + config_store_component = ConfigValueStore( + mapping=create_botocore_default_config_mapping(self) + ) + self._components.register_component( + 'config_store', config_store_component + ) + + def _register_monitor(self): + self._internal_components.lazy_register_component( + 'monitor', self._create_csm_monitor + ) + + def _register_user_agent_creator(self): + uas = UserAgentString.from_environment() + self._components.register_component('user_agent_creator', uas) + + def _create_csm_monitor(self): + if self.get_config_variable('csm_enabled'): + client_id = self.get_config_variable('csm_client_id') + host = self.get_config_variable('csm_host') + port = self.get_config_variable('csm_port') + handler = monitoring.Monitor( + adapter=monitoring.MonitorEventAdapter(), + publisher=monitoring.SocketPublisher( + socket=socket.socket(socket.AF_INET, socket.SOCK_DGRAM), + host=host, + port=port, + serializer=monitoring.CSMSerializer( + csm_client_id=client_id + ), + ), + ) + return handler + return None + + def _get_crt_version(self): + user_agent_creator = self.get_component('user_agent_creator') + return user_agent_creator._crt_version or 'Unknown' + + @property + def available_profiles(self): + return list(self._build_profile_map().keys()) + + def _build_profile_map(self): + # This will build the profile map if it has not been created, + # otherwise it will return the cached value. The profile map + # is a list of profile names, to the config values for the profile. + if self._profile_map is None: + self._profile_map = self.full_config['profiles'] + return self._profile_map + + @property + def profile(self): + if self._profile is None: + profile = self.get_config_variable('profile') + self._profile = profile + return self._profile + + def get_config_variable(self, logical_name, methods=None): + if methods is not None: + return self._get_config_variable_with_custom_methods( + logical_name, methods + ) + return self.get_component('config_store').get_config_variable( + logical_name + ) + + def _get_config_variable_with_custom_methods(self, logical_name, methods): + # If a custom list of methods was supplied we need to perserve the + # behavior with the new system. To do so a new chain that is a copy of + # the old one will be constructed, but only with the supplied methods + # being added to the chain. This chain will be consulted for a value + # and then thrown out. This is not efficient, nor is the methods arg + # used in botocore, this is just for backwards compatibility. + chain_builder = SubsetChainConfigFactory(session=self, methods=methods) + mapping = create_botocore_default_config_mapping(self) + for name, config_options in self.session_var_map.items(): + config_name, env_vars, default, typecast = config_options + build_chain_config_args = { + 'conversion_func': typecast, + 'default': default, + } + if 'instance' in methods: + build_chain_config_args['instance_name'] = name + if 'env' in methods: + build_chain_config_args['env_var_names'] = env_vars + if 'config' in methods: + build_chain_config_args['config_property_name'] = config_name + mapping[name] = chain_builder.create_config_chain( + **build_chain_config_args + ) + config_store_component = ConfigValueStore(mapping=mapping) + value = config_store_component.get_config_variable(logical_name) + return value + + def set_config_variable(self, logical_name, value): + """Set a configuration variable to a specific value. + + By using this method, you can override the normal lookup + process used in ``get_config_variable`` by explicitly setting + a value. Subsequent calls to ``get_config_variable`` will + use the ``value``. This gives you per-session specific + configuration values. + + :: + >>> # Assume logical name 'foo' maps to env var 'FOO' + >>> os.environ['FOO'] = 'myvalue' + >>> s.get_config_variable('foo') + 'myvalue' + >>> s.set_config_variable('foo', 'othervalue') + >>> s.get_config_variable('foo') + 'othervalue' + + :type logical_name: str + :param logical_name: The logical name of the session variable + you want to set. These are the keys in ``SESSION_VARIABLES``. + :param value: The value to associate with the config variable. + + """ + logger.debug( + "Setting config variable for %s to %r", + logical_name, + value, + ) + self._session_instance_vars[logical_name] = value + + def instance_variables(self): + return copy.copy(self._session_instance_vars) + + def get_scoped_config(self): + """ + Returns the config values from the config file scoped to the current + profile. + + The configuration data is loaded **only** from the config file. + It does not resolve variables based on different locations + (e.g. first from the session instance, then from environment + variables, then from the config file). If you want this lookup + behavior, use the ``get_config_variable`` method instead. + + Note that this configuration is specific to a single profile (the + ``profile`` session variable). + + If the ``profile`` session variable is set and the profile does + not exist in the config file, a ``ProfileNotFound`` exception + will be raised. + + :raises: ConfigNotFound, ConfigParseError, ProfileNotFound + :rtype: dict + + """ + profile_name = self.get_config_variable('profile') + profile_map = self._build_profile_map() + # If a profile is not explicitly set return the default + # profile config or an empty config dict if we don't have + # a default profile. + if profile_name is None: + return profile_map.get('default', {}) + elif profile_name not in profile_map: + # Otherwise if they specified a profile, it has to + # exist (even if it's the default profile) otherwise + # we complain. + raise ProfileNotFound(profile=profile_name) + else: + return profile_map[profile_name] + + @property + def full_config(self): + """Return the parsed config file. + + The ``get_config`` method returns the config associated with the + specified profile. This property returns the contents of the + **entire** config file. + + :rtype: dict + """ + if self._config is None: + try: + config_file = self.get_config_variable('config_file') + self._config = botocore.configloader.load_config(config_file) + except ConfigNotFound: + self._config = {'profiles': {}} + try: + # Now we need to inject the profiles from the + # credentials file. We don't actually need the values + # in the creds file, only the profile names so that we + # can validate the user is not referring to a nonexistent + # profile. + cred_file = self.get_config_variable('credentials_file') + cred_profiles = botocore.configloader.raw_config_parse( + cred_file + ) + for profile in cred_profiles: + cred_vars = cred_profiles[profile] + if profile not in self._config['profiles']: + self._config['profiles'][profile] = cred_vars + else: + self._config['profiles'][profile].update(cred_vars) + except ConfigNotFound: + pass + return self._config + + def get_default_client_config(self): + """Retrieves the default config for creating clients + + :rtype: botocore.client.Config + :returns: The default client config object when creating clients. If + the value is ``None`` then there is no default config object + attached to the session. + """ + return self._client_config + + def set_default_client_config(self, client_config): + """Sets the default config for creating clients + + :type client_config: botocore.client.Config + :param client_config: The default client config object when creating + clients. If the value is ``None`` then there is no default config + object attached to the session. + """ + self._client_config = client_config + + def set_credentials( + self, access_key, secret_key, token=None, account_id=None + ): + """ + Manually create credentials for this session. If you would + prefer to use botocore without a config file, environment variables, + or IAM roles, you can pass explicit credentials into this + method to establish credentials for this session. + + :type access_key: str + :param access_key: The access key part of the credentials. + + :type secret_key: str + :param secret_key: The secret key part of the credentials. + + :type token: str + :param token: An option session token used by STS session + credentials. + + :type account_id: str + :param account_id: An optional account ID part of the credentials. + """ + self._credentials = botocore.credentials.Credentials( + access_key, secret_key, token, account_id=account_id + ) + + def get_credentials(self): + """ + Return the :class:`botocore.credential.Credential` object + associated with this session. If the credentials have not + yet been loaded, this will attempt to load them. If they + have already been loaded, this will return the cached + credentials. + + """ + if self._credentials is None: + self._credentials = self._components.get_component( + 'credential_provider' + ).load_credentials() + return self._credentials + + def get_auth_token(self, **kwargs): + """ + Return the :class:`botocore.tokens.AuthToken` object associated with + this session. If the authorization token has not yet been loaded, this + will attempt to load it. If it has already been loaded, this will + return the cached authorization token. + + """ + provider = self._components.get_component('token_provider') + + signing_name = kwargs.get('signing_name') + if signing_name is not None: + auth_token = provider.load_token(signing_name=signing_name) + if auth_token is not None: + return auth_token + + if self._auth_token is None: + self._auth_token = provider.load_token() + return self._auth_token + + def user_agent(self): + """ + Return a string suitable for use as a User-Agent header. + The string will be of the form: + + / Python/ / + + Where: + + - agent_name is the value of the `user_agent_name` attribute + of the session object (`Botocore` by default). + - agent_version is the value of the `user_agent_version` + attribute of the session object (the botocore version by default). + by default. + - py_ver is the version of the Python interpreter beng used. + - plat_name is the name of the platform (e.g. Darwin) + - plat_ver is the version of the platform + - exec_env is exec-env/$AWS_EXECUTION_ENV + + If ``user_agent_extra`` is not empty, then this value will be + appended to the end of the user agent string. + + """ + base = ( + f'{self.user_agent_name}/{self.user_agent_version} ' + f'Python/{platform.python_version()} ' + f'{platform.system()}/{platform.release()}' + ) + if HAS_CRT: + base += f' awscrt/{self._get_crt_version()}' + if os.environ.get('AWS_EXECUTION_ENV') is not None: + base += ' exec-env/{}'.format(os.environ.get('AWS_EXECUTION_ENV')) + if self.user_agent_extra: + base += f' {self.user_agent_extra}' + + return base + + def get_data(self, data_path): + """ + Retrieve the data associated with `data_path`. + + :type data_path: str + :param data_path: The path to the data you wish to retrieve. + """ + return self.get_component('data_loader').load_data(data_path) + + def get_service_model(self, service_name, api_version=None): + """Get the service model object. + + :type service_name: string + :param service_name: The service name + + :type api_version: string + :param api_version: The API version of the service. If none is + provided, then the latest API version will be used. + + :rtype: L{botocore.model.ServiceModel} + :return: The botocore service model for the service. + + """ + service_description = self.get_service_data(service_name, api_version) + return ServiceModel(service_description, service_name=service_name) + + def get_waiter_model(self, service_name, api_version=None): + loader = self.get_component('data_loader') + waiter_config = loader.load_service_model( + service_name, 'waiters-2', api_version + ) + return waiter.WaiterModel(waiter_config) + + def get_paginator_model(self, service_name, api_version=None): + loader = self.get_component('data_loader') + paginator_config = loader.load_service_model( + service_name, 'paginators-1', api_version + ) + return paginate.PaginatorModel(paginator_config) + + def get_service_data(self, service_name, api_version=None): + """ + Retrieve the fully merged data associated with a service. + """ + data_path = service_name + service_data = self.get_component('data_loader').load_service_model( + data_path, type_name='service-2', api_version=api_version + ) + service_id = EVENT_ALIASES.get(service_name, service_name) + self._events.emit( + f'service-data-loaded.{service_id}', + service_data=service_data, + service_name=service_name, + session=self, + ) + return service_data + + def get_available_services(self): + """ + Return a list of names of available services. + """ + return self.get_component('data_loader').list_available_services( + type_name='service-2' + ) + + def set_debug_logger(self, logger_name='botocore'): + """ + Convenience function to quickly configure full debug output + to go to the console. + """ + self.set_stream_logger(logger_name, logging.DEBUG) + + def set_stream_logger( + self, logger_name, log_level, stream=None, format_string=None + ): + """ + Convenience method to configure a stream logger. + + :type logger_name: str + :param logger_name: The name of the logger to configure + + :type log_level: str + :param log_level: The log level to set for the logger. This + is any param supported by the ``.setLevel()`` method of + a ``Log`` object. + + :type stream: file + :param stream: A file like object to log to. If none is provided + then sys.stderr will be used. + + :type format_string: str + :param format_string: The format string to use for the log + formatter. If none is provided this will default to + ``self.LOG_FORMAT``. + + """ + log = logging.getLogger(logger_name) + log.setLevel(logging.DEBUG) + + ch = logging.StreamHandler(stream) + ch.setLevel(log_level) + + # create formatter + if format_string is None: + format_string = self.LOG_FORMAT + formatter = logging.Formatter(format_string) + + # add formatter to ch + ch.setFormatter(formatter) + + # add ch to logger + log.addHandler(ch) + + def set_file_logger(self, log_level, path, logger_name='botocore'): + """ + Convenience function to quickly configure any level of logging + to a file. + + :type log_level: int + :param log_level: A log level as specified in the `logging` module + + :type path: string + :param path: Path to the log file. The file will be created + if it doesn't already exist. + """ + log = logging.getLogger(logger_name) + log.setLevel(logging.DEBUG) + + # create console handler and set level to debug + ch = logging.FileHandler(path) + ch.setLevel(log_level) + + # create formatter + formatter = logging.Formatter(self.LOG_FORMAT) + + # add formatter to ch + ch.setFormatter(formatter) + + # add ch to logger + log.addHandler(ch) + + def register( + self, event_name, handler, unique_id=None, unique_id_uses_count=False + ): + """Register a handler with an event. + + :type event_name: str + :param event_name: The name of the event. + + :type handler: callable + :param handler: The callback to invoke when the event + is emitted. This object must be callable, and must + accept ``**kwargs``. If either of these preconditions are + not met, a ``ValueError`` will be raised. + + :type unique_id: str + :param unique_id: An optional identifier to associate with the + registration. A unique_id can only be used once for + the entire session registration (unless it is unregistered). + This can be used to prevent an event handler from being + registered twice. + + :param unique_id_uses_count: boolean + :param unique_id_uses_count: Specifies if the event should maintain + a count when a ``unique_id`` is registered and unregisted. The + event can only be completely unregistered once every register call + using the unique id has been matched by an ``unregister`` call. + If ``unique_id`` is specified, subsequent ``register`` + calls must use the same value for ``unique_id_uses_count`` + as the ``register`` call that first registered the event. + + :raises ValueError: If the call to ``register`` uses ``unique_id`` + but the value for ``unique_id_uses_count`` differs from the + ``unique_id_uses_count`` value declared by the very first + ``register`` call for that ``unique_id``. + """ + self._events.register( + event_name, + handler, + unique_id, + unique_id_uses_count=unique_id_uses_count, + ) + + def unregister( + self, + event_name, + handler=None, + unique_id=None, + unique_id_uses_count=False, + ): + """Unregister a handler with an event. + + :type event_name: str + :param event_name: The name of the event. + + :type handler: callable + :param handler: The callback to unregister. + + :type unique_id: str + :param unique_id: A unique identifier identifying the callback + to unregister. You can provide either the handler or the + unique_id, you do not have to provide both. + + :param unique_id_uses_count: boolean + :param unique_id_uses_count: Specifies if the event should maintain + a count when a ``unique_id`` is registered and unregisted. The + event can only be completely unregistered once every ``register`` + call using the ``unique_id`` has been matched by an ``unregister`` + call. If the ``unique_id`` is specified, subsequent + ``unregister`` calls must use the same value for + ``unique_id_uses_count`` as the ``register`` call that first + registered the event. + + :raises ValueError: If the call to ``unregister`` uses ``unique_id`` + but the value for ``unique_id_uses_count`` differs from the + ``unique_id_uses_count`` value declared by the very first + ``register`` call for that ``unique_id``. + """ + self._events.unregister( + event_name, + handler=handler, + unique_id=unique_id, + unique_id_uses_count=unique_id_uses_count, + ) + + def emit(self, event_name, **kwargs): + return self._events.emit(event_name, **kwargs) + + def emit_first_non_none_response(self, event_name, **kwargs): + responses = self._events.emit(event_name, **kwargs) + return first_non_none_response(responses) + + def get_component(self, name): + try: + return self._components.get_component(name) + except ValueError: + if name in ['endpoint_resolver', 'exceptions_factory']: + warnings.warn( + f'Fetching the {name} component with the get_component() ' + 'method is deprecated as the component has always been ' + 'considered an internal interface of botocore', + DeprecationWarning, + ) + return self._internal_components.get_component(name) + raise + + def _get_internal_component(self, name): + # While this method may be called by botocore classes outside of the + # Session, this method should **never** be used by a class that lives + # outside of botocore. + return self._internal_components.get_component(name) + + def _register_internal_component(self, name, component): + # While this method may be called by botocore classes outside of the + # Session, this method should **never** be used by a class that lives + # outside of botocore. + return self._internal_components.register_component(name, component) + + def register_component(self, name, component): + self._components.register_component(name, component) + + def lazy_register_component(self, name, component): + self._components.lazy_register_component(name, component) + + @with_current_context() + def create_client( + self, + service_name, + region_name=None, + api_version=None, + use_ssl=True, + verify=None, + endpoint_url=None, + aws_access_key_id=None, + aws_secret_access_key=None, + aws_session_token=None, + config=None, + aws_account_id=None, + ): + """Create a botocore client. + + :type service_name: string + :param service_name: The name of the service for which a client will + be created. You can use the ``Session.get_available_services()`` + method to get a list of all available service names. + + :type region_name: string + :param region_name: The name of the region associated with the client. + A client is associated with a single region. + + :type api_version: string + :param api_version: The API version to use. By default, botocore will + use the latest API version when creating a client. You only need + to specify this parameter if you want to use a previous API version + of the client. + + :type use_ssl: boolean + :param use_ssl: Whether or not to use SSL. By default, SSL is used. + Note that not all services support non-ssl connections. + + :type verify: boolean/string + :param verify: Whether or not to verify SSL certificates. + By default SSL certificates are verified. You can provide the + following values: + + * False - do not validate SSL certificates. SSL will still be + used (unless use_ssl is False), but SSL certificates + will not be verified. + * path/to/cert/bundle.pem - A filename of the CA cert bundle to + uses. You can specify this argument if you want to use a + different CA cert bundle than the one used by botocore. + + :type endpoint_url: string + :param endpoint_url: The complete URL to use for the constructed + client. Normally, botocore will automatically construct the + appropriate URL to use when communicating with a service. You can + specify a complete URL (including the "http/https" scheme) to + override this behavior. If this value is provided, then + ``use_ssl`` is ignored. + + :type aws_access_key_id: string + :param aws_access_key_id: The access key to use when creating + the client. This is entirely optional, and if not provided, + the credentials configured for the session will automatically + be used. You only need to provide this argument if you want + to override the credentials used for this specific client. + + :type aws_secret_access_key: string + :param aws_secret_access_key: The secret key to use when creating + the client. Same semantics as aws_access_key_id above. + + :type aws_session_token: string + :param aws_session_token: The session token to use when creating + the client. Same semantics as aws_access_key_id above. + + :type config: botocore.client.Config + :param config: Advanced client configuration options. If a value + is specified in the client config, its value will take precedence + over environment variables and configuration values, but not over + a value passed explicitly to the method. If a default config + object is set on the session, the config object used when creating + the client will be the result of calling ``merge()`` on the + default config with the config provided to this call. + + :type aws_account_id: string + :param aws_account_id: The account id to use when creating + the client. Same semantics as aws_access_key_id above. + + :rtype: botocore.client.BaseClient + :return: A botocore client instance + + """ + default_client_config = self.get_default_client_config() + # If a config is provided and a default config is set, then + # use the config resulting from merging the two. + if config is not None and default_client_config is not None: + config = default_client_config.merge(config) + # If a config was not provided then use the default + # client config from the session + elif default_client_config is not None: + config = default_client_config + + region_name = self._resolve_region_name(region_name, config) + + # Figure out the verify value base on the various + # configuration options. + if verify is None: + verify = self.get_config_variable('ca_bundle') + + if api_version is None: + api_version = self.get_config_variable('api_versions').get( + service_name, None + ) + + loader = self.get_component('data_loader') + event_emitter = self.get_component('event_emitter') + response_parser_factory = self.get_component('response_parser_factory') + if config is not None and config.signature_version is UNSIGNED: + credentials = None + elif ( + aws_access_key_id is not None and aws_secret_access_key is not None + ): + credentials = botocore.credentials.Credentials( + access_key=aws_access_key_id, + secret_key=aws_secret_access_key, + token=aws_session_token, + account_id=aws_account_id, + ) + elif self._missing_cred_vars(aws_access_key_id, aws_secret_access_key): + raise PartialCredentialsError( + provider='explicit', + cred_var=self._missing_cred_vars( + aws_access_key_id, aws_secret_access_key + ), + ) + else: + if ignored_credentials := self._get_ignored_credentials( + aws_session_token, aws_account_id + ): + logger.debug( + "Ignoring the following credential-related values which were set without " + "an access key id and secret key on the session or client: %s", + ignored_credentials, + ) + credentials = self.get_credentials() + if getattr(credentials, 'method', None) == 'explicit': + register_feature_id('CREDENTIALS_CODE') + auth_token = self.get_auth_token() + endpoint_resolver = self._get_internal_component('endpoint_resolver') + exceptions_factory = self._get_internal_component('exceptions_factory') + config_store = copy.copy(self.get_component('config_store')) + user_agent_creator = self.get_component('user_agent_creator') + # Session configuration values for the user agent string are applied + # just before each client creation because they may have been modified + # at any time between session creation and client creation. + user_agent_creator.set_session_config( + session_user_agent_name=self.user_agent_name, + session_user_agent_version=self.user_agent_version, + session_user_agent_extra=self.user_agent_extra, + ) + defaults_mode = self._resolve_defaults_mode(config, config_store) + if defaults_mode != 'legacy': + smart_defaults_factory = self._get_internal_component( + 'smart_defaults_factory' + ) + smart_defaults_factory.merge_smart_defaults( + config_store, defaults_mode, region_name + ) + + self._add_configured_endpoint_provider( + client_name=service_name, + config_store=config_store, + ) + + user_agent_creator.set_client_features(get_context().features) + + client_creator = botocore.client.ClientCreator( + loader, + endpoint_resolver, + self.user_agent(), + event_emitter, + retryhandler, + translate, + response_parser_factory, + exceptions_factory, + config_store, + user_agent_creator=user_agent_creator, + auth_token_resolver=self.get_auth_token, + ) + client = client_creator.create_client( + service_name=service_name, + region_name=region_name, + is_secure=use_ssl, + endpoint_url=endpoint_url, + verify=verify, + credentials=credentials, + scoped_config=self.get_scoped_config(), + client_config=config, + api_version=api_version, + auth_token=auth_token, + ) + monitor = self._get_internal_component('monitor') + if monitor is not None: + monitor.register(client.meta.events) + self._register_client_plugins(client) + return client + + def _resolve_region_name(self, region_name, config): + # Figure out the user-provided region based on the various + # configuration options. + if region_name is None: + if config and config.region_name is not None: + region_name = config.region_name + else: + region_name = self.get_config_variable('region') + + validate_region_name(region_name) + # For any client that we create in retrieving credentials + # we want to create it using the same region as specified in + # creating this client. It is important to note though that the + # credentials client is only created once per session. So if a new + # client is created with a different region, its credential resolver + # will use the region of the first client. However, that is not an + # issue as of now because the credential resolver uses only STS and + # the credentials returned at regional endpoints are valid across + # all regions in the partition. + self._last_client_region_used = region_name + return region_name + + def _resolve_defaults_mode(self, client_config, config_store): + mode = config_store.get_config_variable('defaults_mode') + + if client_config and client_config.defaults_mode: + mode = client_config.defaults_mode + + default_config_resolver = self._get_internal_component( + 'default_config_resolver' + ) + default_modes = default_config_resolver.get_default_modes() + lmode = mode.lower() + if lmode not in default_modes: + raise InvalidDefaultsMode( + mode=mode, valid_modes=', '.join(default_modes) + ) + + return lmode + + def _add_configured_endpoint_provider(self, client_name, config_store): + chain = ConfiguredEndpointProvider( + full_config=self.full_config, + scoped_config=self.get_scoped_config(), + client_name=client_name, + ) + config_store.set_config_provider( + logical_name='endpoint_url', + provider=chain, + ) + + def _missing_cred_vars(self, access_key, secret_key): + if access_key is not None and secret_key is None: + return 'aws_secret_access_key' + if secret_key is not None and access_key is None: + return 'aws_access_key_id' + return None + + def get_available_partitions(self): + """Lists the available partitions found on disk + + :rtype: list + :return: Returns a list of partition names (e.g., ["aws", "aws-cn"]) + """ + resolver = self._get_internal_component('endpoint_resolver') + return resolver.get_available_partitions() + + def get_partition_for_region(self, region_name): + """Lists the partition name of a particular region. + + :type region_name: string + :param region_name: Name of the region to list partition for (e.g., + us-east-1). + + :rtype: string + :return: Returns the respective partition name (e.g., aws). + """ + resolver = self._get_internal_component('endpoint_resolver') + return resolver.get_partition_for_region(region_name) + + def get_available_regions( + self, service_name, partition_name='aws', allow_non_regional=False + ): + """Lists the region and endpoint names of a particular partition. + + :type service_name: string + :param service_name: Name of a service to list endpoint for (e.g., s3). + This parameter accepts a service name (e.g., "elb") or endpoint + prefix (e.g., "elasticloadbalancing"). + + :type partition_name: string + :param partition_name: Name of the partition to limit endpoints to. + (e.g., aws for the public AWS endpoints, aws-cn for AWS China + endpoints, aws-us-gov for AWS GovCloud (US) Endpoints, etc. + + :type allow_non_regional: bool + :param allow_non_regional: Set to True to include endpoints that are + not regional endpoints (e.g., s3-external-1, + fips-us-gov-west-1, etc). + :return: Returns a list of endpoint names (e.g., ["us-east-1"]). + """ + resolver = self._get_internal_component('endpoint_resolver') + results = [] + try: + service_data = self.get_service_data(service_name) + endpoint_prefix = service_data['metadata'].get( + 'endpointPrefix', service_name + ) + results = resolver.get_available_endpoints( + endpoint_prefix, partition_name, allow_non_regional + ) + except UnknownServiceError: + pass + return results + + def _get_ignored_credentials(self, aws_session_token, aws_account_id): + credential_inputs = [] + if aws_session_token: + credential_inputs.append('aws_session_token') + if aws_account_id: + credential_inputs.append('aws_account_id') + return ', '.join(credential_inputs) if credential_inputs else None + + def _register_client_plugins(self, client): + plugins_list = get_botocore_plugins() + if plugins_list == "DISABLED" or not plugins_list: + return + + client_plugins = {} + for plugin in plugins_list.split(','): + try: + name, module = [part.strip() for part in plugin.split('=')] + client_plugins[name] = module + except ValueError: + logger.warning( + "Invalid plugin format: %s. Expected 'name=module'", plugin + ) + + if client_plugins: + load_client_plugins(client, client_plugins) + + +class ComponentLocator: + """Service locator for session components.""" + + def __init__(self): + self._components = {} + self._deferred = {} + + def get_component(self, name): + if name in self._deferred: + factory = self._deferred[name] + self._components[name] = factory() + # Only delete the component from the deferred dict after + # successfully creating the object from the factory as well as + # injecting the instantiated value into the _components dict. + try: + del self._deferred[name] + except KeyError: + # If we get here, it's likely that get_component was called + # concurrently from multiple threads, and another thread + # already deleted the entry. This means the factory was + # probably called twice, but cleaning up the deferred entry + # should not crash outright. + pass + try: + return self._components[name] + except KeyError: + raise ValueError(f"Unknown component: {name}") + + def register_component(self, name, component): + self._components[name] = component + try: + del self._deferred[name] + except KeyError: + pass + + def lazy_register_component(self, name, no_arg_factory): + self._deferred[name] = no_arg_factory + try: + del self._components[name] + except KeyError: + pass + + +class SessionVarDict(MutableMapping): + def __init__(self, session, session_vars): + self._session = session + self._store = copy.copy(session_vars) + + def __getitem__(self, key): + return self._store[key] + + def __setitem__(self, key, value): + self._store[key] = value + self._update_config_store_from_session_vars(key, value) + + def __delitem__(self, key): + del self._store[key] + + def __iter__(self): + return iter(self._store) + + def __len__(self): + return len(self._store) + + def _update_config_store_from_session_vars( + self, logical_name, config_options + ): + # This is for backwards compatibility. The new preferred way to + # modify configuration logic is to use the component system to get + # the config_store component from the session, and then update + # a key with a custom config provider(s). + # This backwards compatibility method takes the old session_vars + # list of tuples and and transforms that into a set of updates to + # the config_store component. + config_chain_builder = ConfigChainFactory(session=self._session) + config_name, env_vars, default, typecast = config_options + config_store = self._session.get_component('config_store') + config_store.set_config_provider( + logical_name, + config_chain_builder.create_config_chain( + instance_name=logical_name, + env_var_names=env_vars, + config_property_names=config_name, + default=default, + conversion_func=typecast, + ), + ) + + +class SubsetChainConfigFactory: + """A class for creating backwards compatible configuration chains. + + This class can be used instead of + :class:`botocore.configprovider.ConfigChainFactory` to make it honor the + methods argument to get_config_variable. This class can be used to filter + out providers that are not in the methods tuple when creating a new config + chain. + """ + + def __init__(self, session, methods, environ=None): + self._factory = ConfigChainFactory(session, environ) + self._supported_methods = methods + + def create_config_chain( + self, + instance_name=None, + env_var_names=None, + config_property_name=None, + default=None, + conversion_func=None, + ): + """Build a config chain following the standard botocore pattern. + + This config chain factory will omit any providers not in the methods + tuple provided at initialization. For example if given the tuple + ('instance', 'config',) it will not inject the environment provider + into the standard config chain. This lets the botocore session support + the custom ``methods`` argument for all the default botocore config + variables when calling ``get_config_variable``. + """ + if 'instance' not in self._supported_methods: + instance_name = None + if 'env' not in self._supported_methods: + env_var_names = None + if 'config' not in self._supported_methods: + config_property_name = None + return self._factory.create_config_chain( + instance_name=instance_name, + env_var_names=env_var_names, + config_property_names=config_property_name, + default=default, + conversion_func=conversion_func, + ) + + +def get_session(env_vars=None): + """ + Return a new session object. + """ + return Session(env_vars) diff --git a/venv/lib/python3.12/site-packages/botocore/signers.py b/venv/lib/python3.12/site-packages/botocore/signers.py new file mode 100644 index 0000000..eb6c57b --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/signers.py @@ -0,0 +1,1007 @@ +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import base64 +import datetime +import json +import weakref + +import botocore +import botocore.auth +from botocore.awsrequest import create_request_object, prepare_request_dict +from botocore.compat import OrderedDict, get_current_datetime +from botocore.exceptions import ( + ParamValidationError, + UnknownClientMethodError, + UnknownSignatureVersionError, + UnsupportedSignatureVersionError, +) +from botocore.tokens import FrozenAuthToken +from botocore.utils import ( + ArnParser, + datetime2timestamp, + fix_s3_host, # noqa: F401 +) + + +class RequestSigner: + """ + An object to sign requests before they go out over the wire using + one of the authentication mechanisms defined in ``auth.py``. This + class fires two events scoped to a service and operation name: + + * choose-signer: Allows overriding the auth signer name. + * before-sign: Allows mutating the request before signing. + + Together these events allow for customization of the request + signing pipeline, including overrides, request path manipulation, + and disabling signing per operation. + + + :type service_id: botocore.model.ServiceId + :param service_id: The service id for the service, e.g. ``S3`` + + :type region_name: string + :param region_name: Name of the service region, e.g. ``us-east-1`` + + :type signing_name: string + :param signing_name: Service signing name. This is usually the + same as the service name, but can differ. E.g. + ``emr`` vs. ``elasticmapreduce``. + + :type signature_version: string + :param signature_version: Signature name like ``v4``. + + :type credentials: :py:class:`~botocore.credentials.Credentials` + :param credentials: User credentials with which to sign requests. + + :type event_emitter: :py:class:`~botocore.hooks.BaseEventHooks` + :param event_emitter: Extension mechanism to fire events. + """ + + def __init__( + self, + service_id, + region_name, + signing_name, + signature_version, + credentials, + event_emitter, + auth_token=None, + ): + self._region_name = region_name + self._signing_name = signing_name + self._signature_version = signature_version + self._credentials = credentials + self._auth_token = auth_token + self._service_id = service_id + + # We need weakref to prevent leaking memory in Python 2.6 on Linux 2.6 + self._event_emitter = weakref.proxy(event_emitter) + + @property + def region_name(self): + return self._region_name + + @property + def signature_version(self): + return self._signature_version + + @property + def signing_name(self): + return self._signing_name + + def handler(self, operation_name=None, request=None, **kwargs): + # This is typically hooked up to the "request-created" event + # from a client's event emitter. When a new request is created + # this method is invoked to sign the request. + # Don't call this method directly. + return self.sign(operation_name, request) + + def sign( + self, + operation_name, + request, + region_name=None, + signing_type='standard', + expires_in=None, + signing_name=None, + ): + """Sign a request before it goes out over the wire. + + :type operation_name: string + :param operation_name: The name of the current operation, e.g. + ``ListBuckets``. + :type request: AWSRequest + :param request: The request object to be sent over the wire. + + :type region_name: str + :param region_name: The region to sign the request for. + + :type signing_type: str + :param signing_type: The type of signing to perform. This can be one of + three possible values: + + * 'standard' - This should be used for most requests. + * 'presign-url' - This should be used when pre-signing a request. + * 'presign-post' - This should be used when pre-signing an S3 post. + + :type expires_in: int + :param expires_in: The number of seconds the presigned url is valid + for. This parameter is only valid for signing type 'presign-url'. + + :type signing_name: str + :param signing_name: The name to use for the service when signing. + """ + explicit_region_name = region_name + if region_name is None: + region_name = self._region_name + + if signing_name is None: + signing_name = self._signing_name + + signature_version = self._choose_signer( + operation_name, signing_type, request.context + ) + + # Allow mutating request before signing + self._event_emitter.emit( + f'before-sign.{self._service_id.hyphenize()}.{operation_name}', + request=request, + signing_name=signing_name, + region_name=self._region_name, + signature_version=signature_version, + request_signer=self, + operation_name=operation_name, + ) + + if signature_version != botocore.UNSIGNED: + kwargs = { + 'signing_name': signing_name, + 'region_name': region_name, + 'signature_version': signature_version, + } + if expires_in is not None: + kwargs['expires'] = expires_in + signing_context = request.context.get('signing', {}) + if not explicit_region_name and signing_context.get('region'): + kwargs['region_name'] = signing_context['region'] + if signing_context.get('signing_name'): + kwargs['signing_name'] = signing_context['signing_name'] + if signing_context.get('request_credentials'): + kwargs['request_credentials'] = signing_context[ + 'request_credentials' + ] + if signing_context.get('identity_cache') is not None: + self._resolve_identity_cache( + kwargs, + signing_context['identity_cache'], + signing_context['cache_key'], + ) + try: + auth = self.get_auth_instance(**kwargs) + except UnknownSignatureVersionError as e: + if signing_type != 'standard': + raise UnsupportedSignatureVersionError( + signature_version=signature_version + ) + else: + raise e + + auth.add_auth(request) + + def _resolve_identity_cache(self, kwargs, cache, cache_key): + kwargs['identity_cache'] = cache + kwargs['cache_key'] = cache_key + + def _choose_signer(self, operation_name, signing_type, context): + """ + Allow setting the signature version via the choose-signer event. + A value of `botocore.UNSIGNED` means no signing will be performed. + + :param operation_name: The operation to sign. + :param signing_type: The type of signing that the signer is to be used + for. + :return: The signature version to sign with. + """ + signing_type_suffix_map = { + 'presign-post': '-presign-post', + 'presign-url': '-query', + } + suffix = signing_type_suffix_map.get(signing_type, '') + + # operation specific signing context takes precedent over client-level + # defaults + signature_version = context.get('auth_type') or self._signature_version + signing = context.get('signing', {}) + signing_name = signing.get('signing_name', self._signing_name) + region_name = signing.get('region', self._region_name) + if ( + signature_version is not botocore.UNSIGNED + and not signature_version.endswith(suffix) + ): + signature_version += suffix + + handler, response = self._event_emitter.emit_until_response( + f'choose-signer.{self._service_id.hyphenize()}.{operation_name}', + signing_name=signing_name, + region_name=region_name, + signature_version=signature_version, + context=context, + ) + + if response is not None: + signature_version = response + # The suffix needs to be checked again in case we get an improper + # signature version from choose-signer. + if ( + signature_version is not botocore.UNSIGNED + and not signature_version.endswith(suffix) + ): + signature_version += suffix + + return signature_version + + def get_auth_instance( + self, + signing_name, + region_name, + signature_version=None, + request_credentials=None, + **kwargs, + ): + """ + Get an auth instance which can be used to sign a request + using the given signature version. + + :type signing_name: string + :param signing_name: Service signing name. This is usually the + same as the service name, but can differ. E.g. + ``emr`` vs. ``elasticmapreduce``. + + :type region_name: string + :param region_name: Name of the service region, e.g. ``us-east-1`` + + :type signature_version: string + :param signature_version: Signature name like ``v4``. + + :rtype: :py:class:`~botocore.auth.BaseSigner` + :return: Auth instance to sign a request. + """ + if signature_version is None: + signature_version = self._signature_version + + cls = botocore.auth.AUTH_TYPE_MAPS.get(signature_version) + if cls is None: + raise UnknownSignatureVersionError( + signature_version=signature_version + ) + + if cls.REQUIRES_TOKEN is True: + if self._auth_token and not isinstance( + self._auth_token, FrozenAuthToken + ): + frozen_token = self._auth_token.get_frozen_token() + else: + frozen_token = self._auth_token + auth = cls(frozen_token) + return auth + + credentials = request_credentials or self._credentials + if getattr(cls, "REQUIRES_IDENTITY_CACHE", None) is True: + cache = kwargs["identity_cache"] + key = kwargs["cache_key"] + credentials = cache.get_credentials(key) + del kwargs["cache_key"] + + # If there's no credentials provided (i.e credentials is None), + # then we'll pass a value of "None" over to the auth classes, + # which already handle the cases where no credentials have + # been provided. + frozen_credentials = None + if credentials is not None: + frozen_credentials = credentials.get_frozen_credentials() + kwargs['credentials'] = frozen_credentials + if cls.REQUIRES_REGION: + if self._region_name is None: + raise botocore.exceptions.NoRegionError() + kwargs['region_name'] = region_name + kwargs['service_name'] = signing_name + auth = cls(**kwargs) + return auth + + # Alias get_auth for backwards compatibility. + get_auth = get_auth_instance + + def generate_presigned_url( + self, + request_dict, + operation_name, + expires_in=3600, + region_name=None, + signing_name=None, + ): + """Generates a presigned url + + :type request_dict: dict + :param request_dict: The prepared request dictionary returned by + ``botocore.awsrequest.prepare_request_dict()`` + + :type operation_name: str + :param operation_name: The operation being signed. + + :type expires_in: int + :param expires_in: The number of seconds the presigned url is valid + for. By default it expires in an hour (3600 seconds) + + :type region_name: string + :param region_name: The region name to sign the presigned url. + + :type signing_name: str + :param signing_name: The name to use for the service when signing. + + :returns: The presigned url + """ + request = create_request_object(request_dict) + self.sign( + operation_name, + request, + region_name, + 'presign-url', + expires_in, + signing_name, + ) + + request.prepare() + return request.url + + +class CloudFrontSigner: + '''A signer to create a signed CloudFront URL. + + First you create a cloudfront signer based on a normalized RSA signer:: + + import rsa + def rsa_signer(message): + private_key = open('private_key.pem', 'r').read() + return rsa.sign( + message, + rsa.PrivateKey.load_pkcs1(private_key.encode('utf8')), + 'SHA-1') # CloudFront requires SHA-1 hash + cf_signer = CloudFrontSigner(key_id, rsa_signer) + + To sign with a canned policy:: + + signed_url = cf_signer.generate_signed_url( + url, date_less_than=datetime(2015, 12, 1)) + + To sign with a custom policy:: + + signed_url = cf_signer.generate_signed_url(url, policy=my_policy) + ''' + + def __init__(self, key_id, rsa_signer): + """Create a CloudFrontSigner. + + :type key_id: str + :param key_id: The CloudFront Key Pair ID + + :type rsa_signer: callable + :param rsa_signer: An RSA signer. + Its only input parameter will be the message to be signed, + and its output will be the signed content as a binary string. + The hash algorithm needed by CloudFront is SHA-1. + """ + self.key_id = key_id + self.rsa_signer = rsa_signer + + def generate_presigned_url(self, url, date_less_than=None, policy=None): + """Creates a signed CloudFront URL based on given parameters. + + :type url: str + :param url: The URL of the protected object + + :type date_less_than: datetime + :param date_less_than: The URL will expire after that date and time + + :type policy: str + :param policy: The custom policy, possibly built by self.build_policy() + + :rtype: str + :return: The signed URL. + """ + both_args_supplied = date_less_than is not None and policy is not None + neither_arg_supplied = date_less_than is None and policy is None + if both_args_supplied or neither_arg_supplied: + e = 'Need to provide either date_less_than or policy, but not both' + raise ValueError(e) + if date_less_than is not None: + # We still need to build a canned policy for signing purpose + policy = self.build_policy(url, date_less_than) + if isinstance(policy, str): + policy = policy.encode('utf8') + if date_less_than is not None: + params = [f'Expires={int(datetime2timestamp(date_less_than))}'] + else: + params = [f"Policy={self._url_b64encode(policy).decode('utf8')}"] + signature = self.rsa_signer(policy) + params.extend( + [ + f"Signature={self._url_b64encode(signature).decode('utf8')}", + f"Key-Pair-Id={self.key_id}", + ] + ) + return self._build_url(url, params) + + def _build_url(self, base_url, extra_params): + separator = '&' if '?' in base_url else '?' + return base_url + separator + '&'.join(extra_params) + + def build_policy( + self, resource, date_less_than, date_greater_than=None, ip_address=None + ): + """A helper to build policy. + + :type resource: str + :param resource: The URL or the stream filename of the protected object + + :type date_less_than: datetime + :param date_less_than: The URL will expire after the time has passed + + :type date_greater_than: datetime + :param date_greater_than: The URL will not be valid until this time + + :type ip_address: str + :param ip_address: Use 'x.x.x.x' for an IP, or 'x.x.x.x/x' for a subnet + + :rtype: str + :return: The policy in a compact string. + """ + # Note: + # 1. Order in canned policy is significant. Special care has been taken + # to ensure the output will match the order defined by the document. + # There is also a test case to ensure that order. + # SEE: http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned-policy.html#private-content-canned-policy-creating-policy-statement + # 2. Albeit the order in custom policy is not required by CloudFront, + # we still use OrderedDict internally to ensure the result is stable + # and also matches canned policy requirement. + # SEE: http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html + moment = int(datetime2timestamp(date_less_than)) + condition = OrderedDict({"DateLessThan": {"AWS:EpochTime": moment}}) + if ip_address: + if '/' not in ip_address: + ip_address += '/32' + condition["IpAddress"] = {"AWS:SourceIp": ip_address} + if date_greater_than: + moment = int(datetime2timestamp(date_greater_than)) + condition["DateGreaterThan"] = {"AWS:EpochTime": moment} + ordered_payload = [('Resource', resource), ('Condition', condition)] + custom_policy = {"Statement": [OrderedDict(ordered_payload)]} + return json.dumps(custom_policy, separators=(',', ':')) + + def _url_b64encode(self, data): + # Required by CloudFront. See also: + # http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-linux-openssl.html + return ( + base64.b64encode(data) + .replace(b'+', b'-') + .replace(b'=', b'_') + .replace(b'/', b'~') + ) + + +def add_generate_db_auth_token(class_attributes, **kwargs): + class_attributes['generate_db_auth_token'] = generate_db_auth_token + + +def add_dsql_generate_db_auth_token_methods(class_attributes, **kwargs): + class_attributes['generate_db_connect_auth_token'] = ( + dsql_generate_db_connect_auth_token + ) + class_attributes['generate_db_connect_admin_auth_token'] = ( + dsql_generate_db_connect_admin_auth_token + ) + + +def generate_db_auth_token(self, DBHostname, Port, DBUsername, Region=None): + """Generates an auth token used to connect to a db with IAM credentials. + + :type DBHostname: str + :param DBHostname: The hostname of the database to connect to. + + :type Port: int + :param Port: The port number the database is listening on. + + :type DBUsername: str + :param DBUsername: The username to log in as. + + :type Region: str + :param Region: The region the database is in. If None, the client + region will be used. + + :return: A presigned url which can be used as an auth token. + """ + region = Region + if region is None: + region = self.meta.region_name + + params = { + 'Action': 'connect', + 'DBUser': DBUsername, + } + + request_dict = { + 'url_path': '/', + 'query_string': '', + 'headers': {}, + 'body': params, + 'method': 'GET', + } + + # RDS requires that the scheme not be set when sent over. This can cause + # issues when signing because the Python url parsing libraries follow + # RFC 1808 closely, which states that a netloc must be introduced by `//`. + # Otherwise the url is presumed to be relative, and thus the whole + # netloc would be treated as a path component. To work around this we + # introduce https here and remove it once we're done processing it. + scheme = 'https://' + endpoint_url = f'{scheme}{DBHostname}:{Port}' + prepare_request_dict(request_dict, endpoint_url) + presigned_url = self._request_signer.generate_presigned_url( + operation_name='connect', + request_dict=request_dict, + region_name=region, + expires_in=900, + signing_name='rds-db', + ) + return presigned_url[len(scheme) :] + + +def _dsql_generate_db_auth_token( + self, Hostname, Action, Region=None, ExpiresIn=900 +): + """Generate a DSQL database token for an arbitrary action. + + :type Hostname: str + :param Hostname: The DSQL endpoint host name. + + :type Action: str + :param Action: Action to perform on the cluster (DbConnectAdmin or DbConnect). + + :type Region: str + :param Region: The AWS region where the DSQL Cluster is hosted. If None, the client region will be used. + + :type ExpiresIn: int + :param ExpiresIn: The token expiry duration in seconds (default is 900 seconds). + + :return: A presigned url which can be used as an auth token. + """ + possible_actions = ("DbConnect", "DbConnectAdmin") + + if Action not in possible_actions: + raise ParamValidationError( + report=f"Received {Action} for action but expected one of: {', '.join(possible_actions)}" + ) + + if Region is None: + Region = self.meta.region_name + + request_dict = { + 'url_path': '/', + 'query_string': '', + 'headers': {}, + 'body': { + 'Action': Action, + }, + 'method': 'GET', + } + scheme = 'https://' + endpoint_url = f'{scheme}{Hostname}' + prepare_request_dict(request_dict, endpoint_url) + presigned_url = self._request_signer.generate_presigned_url( + operation_name=Action, + request_dict=request_dict, + region_name=Region, + expires_in=ExpiresIn, + signing_name='dsql', + ) + return presigned_url[len(scheme) :] + + +def dsql_generate_db_connect_auth_token( + self, Hostname, Region=None, ExpiresIn=900 +): + """Generate a DSQL database token for the "DbConnect" action. + + :type Hostname: str + :param Hostname: The DSQL endpoint host name. + + :type Region: str + :param Region: The AWS region where the DSQL Cluster is hosted. If None, the client region will be used. + + :type ExpiresIn: int + :param ExpiresIn: The token expiry duration in seconds (default is 900 seconds). + + :return: A presigned url which can be used as an auth token. + """ + return _dsql_generate_db_auth_token( + self, Hostname, "DbConnect", Region, ExpiresIn + ) + + +def dsql_generate_db_connect_admin_auth_token( + self, Hostname, Region=None, ExpiresIn=900 +): + """Generate a DSQL database token for the "DbConnectAdmin" action. + + :type Hostname: str + :param Hostname: The DSQL endpoint host name. + + :type Region: str + :param Region: The AWS region where the DSQL Cluster is hosted. If None, the client region will be used. + + :type ExpiresIn: int + :param ExpiresIn: The token expiry duration in seconds (default is 900 seconds). + + :return: A presigned url which can be used as an auth token. + """ + return _dsql_generate_db_auth_token( + self, Hostname, "DbConnectAdmin", Region, ExpiresIn + ) + + +class S3PostPresigner: + def __init__(self, request_signer): + self._request_signer = request_signer + + def generate_presigned_post( + self, + request_dict, + fields=None, + conditions=None, + expires_in=3600, + region_name=None, + ): + """Generates the url and the form fields used for a presigned s3 post + + :type request_dict: dict + :param request_dict: The prepared request dictionary returned by + ``botocore.awsrequest.prepare_request_dict()`` + + :type fields: dict + :param fields: A dictionary of prefilled form fields to build on top + of. + + :type conditions: list + :param conditions: A list of conditions to include in the policy. Each + element can be either a list or a structure. For example: + + .. code:: python + + [ + {"acl": "public-read"}, + {"bucket": "amzn-s3-demo-bucket"}, + ["starts-with", "$key", "mykey"] + ] + + :type expires_in: int + :param expires_in: The number of seconds the presigned post is valid + for. + + :type region_name: string + :param region_name: The region name to sign the presigned post to. + + :rtype: dict + :returns: A dictionary with two elements: ``url`` and ``fields``. + Url is the url to post to. Fields is a dictionary filled with + the form fields and respective values to use when submitting the + post. For example: + + .. code:: python + + { + 'url': 'https://amzn-s3-demo-bucket.s3.amazonaws.com', + 'fields': { + 'acl': 'public-read', + 'key': 'mykey', + 'signature': 'mysignature', + 'policy': 'mybase64 encoded policy' + } + } + """ + if fields is None: + fields = {} + + if conditions is None: + conditions = [] + + # Create the policy for the post. + policy = {} + + # Create an expiration date for the policy + datetime_now = get_current_datetime() + expire_date = datetime_now + datetime.timedelta(seconds=expires_in) + policy['expiration'] = expire_date.strftime(botocore.auth.ISO8601) + + # Append all of the conditions that the user supplied. + policy['conditions'] = [] + for condition in conditions: + policy['conditions'].append(condition) + + # Store the policy and the fields in the request for signing + request = create_request_object(request_dict) + request.context['s3-presign-post-fields'] = fields + request.context['s3-presign-post-policy'] = policy + + self._request_signer.sign( + 'PutObject', request, region_name, 'presign-post' + ) + # Return the url and the fields for th form to post. + return {'url': request.url, 'fields': fields} + + +def add_generate_presigned_url(class_attributes, **kwargs): + class_attributes['generate_presigned_url'] = generate_presigned_url + + +def generate_presigned_url( + self, ClientMethod, Params=None, ExpiresIn=3600, HttpMethod=None +): + """Generate a presigned url given a client, its method, and arguments + + .. warning:: + + For backwards compatibility, S3 presigned URLs use Signature + Version 2 by default in regions where S3 supports it. As a result, + some ``Params`` entries are not signed. Some headers, including the + conditionals and ``Range``, are also silently dropped. + + It is recommended to configure the S3 client with Signature + Version 4 by setting ``signature_version='s3v4'`` in the client's + ``Config``. + + :type ClientMethod: string + :param ClientMethod: The client method to presign for + + :type Params: dict + :param Params: The parameters normally passed to + ``ClientMethod``. + + :type ExpiresIn: int + :param ExpiresIn: The number of seconds the presigned url is valid + for. By default it expires in an hour (3600 seconds) + + :type HttpMethod: string + :param HttpMethod: The http method to use on the generated url. By + default, the http method is whatever is used in the method's model. + + :returns: The presigned url + """ + client_method = ClientMethod + params = Params + if params is None: + params = {} + expires_in = ExpiresIn + http_method = HttpMethod + context = { + 'is_presign_request': True, + 'use_global_endpoint': _should_use_global_endpoint(self), + } + + request_signer = self._request_signer + + try: + operation_name = self._PY_TO_OP_NAME[client_method] + except KeyError: + raise UnknownClientMethodError(method_name=client_method) + + operation_model = self.meta.service_model.operation_model(operation_name) + params = self._emit_api_params( + api_params=params, + operation_model=operation_model, + context=context, + ) + bucket_is_arn = ArnParser.is_arn(params.get('Bucket', '')) + ( + endpoint_url, + additional_headers, + properties, + ) = self._resolve_endpoint_ruleset( + operation_model, + params, + context, + ignore_signing_region=(not bucket_is_arn), + ) + + request_dict = self._convert_to_request_dict( + api_params=params, + operation_model=operation_model, + endpoint_url=endpoint_url, + context=context, + headers=additional_headers, + set_user_agent_header=False, + ) + + # Switch out the http method if user specified it. + if http_method is not None: + request_dict['method'] = http_method + + # Generate the presigned url. + return request_signer.generate_presigned_url( + request_dict=request_dict, + expires_in=expires_in, + operation_name=operation_name, + ) + + +def add_generate_presigned_post(class_attributes, **kwargs): + class_attributes['generate_presigned_post'] = generate_presigned_post + + +def generate_presigned_post( + self, Bucket, Key, Fields=None, Conditions=None, ExpiresIn=3600 +): + """Builds the url and the form fields used for a presigned s3 post + + :type Bucket: string + :param Bucket: The name of the bucket to presign the post to. Note that + bucket related conditions should not be included in the + ``conditions`` parameter. + + :type Key: string + :param Key: Key name, optionally add ${filename} to the end to + attach the submitted filename. Note that key related conditions and + fields are filled out for you and should not be included in the + ``Fields`` or ``Conditions`` parameter. + + :type Fields: dict + :param Fields: A dictionary of prefilled form fields to build on top + of. Elements that may be included are acl, Cache-Control, + Content-Type, Content-Disposition, Content-Encoding, Expires, + success_action_redirect, redirect, success_action_status, + and x-amz-meta-. + + Note that if a particular element is included in the fields + dictionary it will not be automatically added to the conditions + list. You must specify a condition for the element as well. + + :type Conditions: list + :param Conditions: A list of conditions to include in the policy. Each + element can be either a list or a structure. For example: + + .. code:: python + + [ + {"acl": "public-read"}, + ["content-length-range", 2, 5], + ["starts-with", "$success_action_redirect", ""] + ] + + Conditions that are included may pertain to acl, + content-length-range, Cache-Control, Content-Type, + Content-Disposition, Content-Encoding, Expires, + success_action_redirect, redirect, success_action_status, + and/or x-amz-meta-. + + Note that if you include a condition, you must specify + a valid value in the fields dictionary as well. A value will + not be added automatically to the fields dictionary based on the + conditions. + + :type ExpiresIn: int + :param ExpiresIn: The number of seconds the presigned post + is valid for. + + :rtype: dict + :returns: A dictionary with two elements: ``url`` and ``fields``. + Url is the url to post to. Fields is a dictionary filled with + the form fields and respective values to use when submitting the + post. For example: + + .. code:: python + + { + 'url': 'https://amzn-s3-demo-bucket.s3.amazonaws.com', + 'fields': { + 'acl': 'public-read', + 'key': 'mykey', + 'signature': 'mysignature', + 'policy': 'mybase64 encoded policy' + } + } + """ + bucket = Bucket + key = Key + fields = Fields + conditions = Conditions + expires_in = ExpiresIn + + if fields is None: + fields = {} + else: + fields = fields.copy() + + if conditions is None: + conditions = [] + + context = { + 'is_presign_request': True, + 'use_global_endpoint': _should_use_global_endpoint(self), + } + + post_presigner = S3PostPresigner(self._request_signer) + + # We choose the CreateBucket operation model because its url gets + # serialized to what a presign post requires. + operation_model = self.meta.service_model.operation_model('CreateBucket') + params = self._emit_api_params( + api_params={'Bucket': bucket}, + operation_model=operation_model, + context=context, + ) + bucket_is_arn = ArnParser.is_arn(params.get('Bucket', '')) + ( + endpoint_url, + additional_headers, + properties, + ) = self._resolve_endpoint_ruleset( + operation_model, + params, + context, + ignore_signing_region=(not bucket_is_arn), + ) + + request_dict = self._convert_to_request_dict( + api_params=params, + operation_model=operation_model, + endpoint_url=endpoint_url, + context=context, + headers=additional_headers, + set_user_agent_header=False, + ) + + # Append that the bucket name to the list of conditions. + conditions.append({'bucket': bucket}) + + # If the key ends with filename, the only constraint that can be + # imposed is if it starts with the specified prefix. + if key.endswith('${filename}'): + conditions.append(["starts-with", '$key', key[: -len('${filename}')]]) + else: + conditions.append({'key': key}) + + # Add the key to the fields. + fields['key'] = key + + return post_presigner.generate_presigned_post( + request_dict=request_dict, + fields=fields, + conditions=conditions, + expires_in=expires_in, + ) + + +def _should_use_global_endpoint(client): + if client.meta.partition != 'aws': + return False + s3_config = client.meta.config.s3 + if s3_config: + if s3_config.get('use_dualstack_endpoint', False): + return False + if ( + s3_config.get('us_east_1_regional_endpoint') == 'regional' + and client.meta.config.region_name == 'us-east-1' + ): + return False + if s3_config.get('addressing_style') == 'virtual': + return False + return True diff --git a/venv/lib/python3.12/site-packages/botocore/stub.py b/venv/lib/python3.12/site-packages/botocore/stub.py new file mode 100644 index 0000000..d85294a --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/stub.py @@ -0,0 +1,452 @@ +# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import copy +from collections import deque +from pprint import pformat + +from botocore.awsrequest import AWSResponse +from botocore.exceptions import ( + ParamValidationError, + StubAssertionError, + StubResponseError, + UnStubbedResponseError, +) +from botocore.validate import validate_parameters + + +class _ANY: + """ + A helper object that compares equal to everything. Copied from + unittest.mock + """ + + def __eq__(self, other): + return True + + def __ne__(self, other): + return False + + def __repr__(self): + return '' + + +ANY = _ANY() + + +class Stubber: + """ + This class will allow you to stub out requests so you don't have to hit + an endpoint to write tests. Responses are returned first in, first out. + If operations are called out of order, or are called with no remaining + queued responses, an error will be raised. + + **Example:** + :: + import datetime + import botocore.session + from botocore.stub import Stubber + + + s3 = botocore.session.get_session().create_client('s3') + stubber = Stubber(s3) + + response = { + 'IsTruncated': False, + 'Name': 'test-bucket', + 'MaxKeys': 1000, 'Prefix': '', + 'Contents': [{ + 'Key': 'test.txt', + 'ETag': '"abc123"', + 'StorageClass': 'STANDARD', + 'LastModified': datetime.datetime(2016, 1, 20, 22, 9), + 'Owner': {'ID': 'abc123', 'DisplayName': 'myname'}, + 'Size': 14814 + }], + 'EncodingType': 'url', + 'ResponseMetadata': { + 'RequestId': 'abc123', + 'HTTPStatusCode': 200, + 'HostId': 'abc123' + }, + 'Marker': '' + } + + expected_params = {'Bucket': 'test-bucket'} + + stubber.add_response('list_objects', response, expected_params) + stubber.activate() + + service_response = s3.list_objects(Bucket='test-bucket') + assert service_response == response + + + This class can also be called as a context manager, which will handle + activation / deactivation for you. + + **Example:** + :: + import datetime + import botocore.session + from botocore.stub import Stubber + + + s3 = botocore.session.get_session().create_client('s3') + + response = { + "Owner": { + "ID": "foo", + "DisplayName": "bar" + }, + "Buckets": [{ + "CreationDate": datetime.datetime(2016, 1, 20, 22, 9), + "Name": "baz" + }] + } + + + with Stubber(s3) as stubber: + stubber.add_response('list_buckets', response, {}) + service_response = s3.list_buckets() + + assert service_response == response + + + If you have an input parameter that is a randomly generated value, or you + otherwise don't care about its value, you can use ``stub.ANY`` to ignore + it in validation. + + **Example:** + :: + import datetime + import botocore.session + from botocore.stub import Stubber, ANY + + + s3 = botocore.session.get_session().create_client('s3') + stubber = Stubber(s3) + + response = { + 'IsTruncated': False, + 'Name': 'test-bucket', + 'MaxKeys': 1000, 'Prefix': '', + 'Contents': [{ + 'Key': 'test.txt', + 'ETag': '"abc123"', + 'StorageClass': 'STANDARD', + 'LastModified': datetime.datetime(2016, 1, 20, 22, 9), + 'Owner': {'ID': 'abc123', 'DisplayName': 'myname'}, + 'Size': 14814 + }], + 'EncodingType': 'url', + 'ResponseMetadata': { + 'RequestId': 'abc123', + 'HTTPStatusCode': 200, + 'HostId': 'abc123' + }, + 'Marker': '' + } + + expected_params = {'Bucket': ANY} + stubber.add_response('list_objects', response, expected_params) + + with stubber: + service_response = s3.list_objects(Bucket='test-bucket') + + assert service_response == response + """ + + def __init__(self, client): + """ + :param client: The client to add your stubs to. + """ + self.client = client + self._event_id = 'boto_stubber' + self._expected_params_event_id = 'boto_stubber_expected_params' + self._stub_account_id_event_id = 'boto_stubber_stub_account_id' + self._queue = deque() + + def __enter__(self): + self.activate() + return self + + def __exit__(self, exception_type, exception_value, traceback): + self.deactivate() + + def activate(self): + """ + Activates the stubber on the client + """ + self.client.meta.events.register_first( + 'before-parameter-build.*.*', + self._assert_expected_params, + unique_id=self._expected_params_event_id, + ) + self.client.meta.events.register( + 'before-call.*.*', + self._get_response_handler, + unique_id=self._event_id, + ) + self.client.meta.events.register( + 'before-endpoint-resolution.*', + self._set_account_id_for_endpoint_resolution, + unique_id=self._stub_account_id_event_id, + ) + + def deactivate(self): + """ + Deactivates the stubber on the client + """ + self.client.meta.events.unregister( + 'before-parameter-build.*.*', + self._assert_expected_params, + unique_id=self._expected_params_event_id, + ) + self.client.meta.events.unregister( + 'before-call.*.*', + self._get_response_handler, + unique_id=self._event_id, + ) + self.client.meta.events.unregister( + 'before-endpoint-resolution.*', + self._stub_account_id_event_id, + unique_id=self._stub_account_id_event_id, + ) + + def add_response(self, method, service_response, expected_params=None): + """ + Adds a service response to the response queue. This will be validated + against the service model to ensure correctness. It should be noted, + however, that while missing attributes are often considered correct, + your code may not function properly if you leave them out. Therefore + you should always fill in every value you see in a typical response for + your particular request. + + :param method: The name of the client method to stub. + :type method: str + + :param service_response: A dict response stub. Provided parameters will + be validated against the service model. + :type service_response: dict + + :param expected_params: A dictionary of the expected parameters to + be called for the provided service response. The parameters match + the names of keyword arguments passed to that client call. If + any of the parameters differ a ``StubResponseError`` is thrown. + You can use stub.ANY to indicate a particular parameter to ignore + in validation. stub.ANY is only valid for top level params. + """ + self._add_response(method, service_response, expected_params) + + def _add_response(self, method, service_response, expected_params): + if not hasattr(self.client, method): + raise ValueError( + f"Client {self.client.meta.service_model.service_name} " + f"does not have method: {method}" + ) + + # Create a successful http response + http_response = AWSResponse(None, 200, {}, None) + + operation_name = self.client.meta.method_to_api_mapping.get(method) + self._validate_operation_response(operation_name, service_response) + + # Add the service_response to the queue for returning responses + response = { + 'operation_name': operation_name, + 'response': (http_response, service_response), + 'expected_params': expected_params, + } + self._queue.append(response) + + def add_client_error( + self, + method, + service_error_code='', + service_message='', + http_status_code=400, + service_error_meta=None, + expected_params=None, + response_meta=None, + modeled_fields=None, + ): + """ + Adds a ``ClientError`` to the response queue. + + :param method: The name of the service method to return the error on. + :type method: str + + :param service_error_code: The service error code to return, + e.g. ``NoSuchBucket`` + :type service_error_code: str + + :param service_message: The service message to return, e.g. + 'The specified bucket does not exist.' + :type service_message: str + + :param http_status_code: The HTTP status code to return, e.g. 404, etc + :type http_status_code: int + + :param service_error_meta: Additional keys to be added to the + service Error + :type service_error_meta: dict + + :param expected_params: A dictionary of the expected parameters to + be called for the provided service response. The parameters match + the names of keyword arguments passed to that client call. If + any of the parameters differ a ``StubResponseError`` is thrown. + You can use stub.ANY to indicate a particular parameter to ignore + in validation. + + :param response_meta: Additional keys to be added to the + response's ResponseMetadata + :type response_meta: dict + + :param modeled_fields: Additional keys to be added to the response + based on fields that are modeled for the particular error code. + These keys will be validated against the particular error shape + designated by the error code. + :type modeled_fields: dict + + """ + http_response = AWSResponse(None, http_status_code, {}, None) + + # We don't look to the model to build this because the caller would + # need to know the details of what the HTTP body would need to + # look like. + parsed_response = { + 'ResponseMetadata': {'HTTPStatusCode': http_status_code}, + 'Error': {'Message': service_message, 'Code': service_error_code}, + } + + if service_error_meta is not None: + parsed_response['Error'].update(service_error_meta) + + if response_meta is not None: + parsed_response['ResponseMetadata'].update(response_meta) + + if modeled_fields is not None: + service_model = self.client.meta.service_model + shape = service_model.shape_for_error_code(service_error_code) + self._validate_response(shape, modeled_fields) + parsed_response.update(modeled_fields) + + operation_name = self.client.meta.method_to_api_mapping.get(method) + # Note that we do not allow for expected_params while + # adding errors into the queue yet. + response = { + 'operation_name': operation_name, + 'response': (http_response, parsed_response), + 'expected_params': expected_params, + } + self._queue.append(response) + + def assert_no_pending_responses(self): + """ + Asserts that all expected calls were made. + """ + remaining = len(self._queue) + if remaining != 0: + raise AssertionError(f"{remaining} responses remaining in queue.") + + def _assert_expected_call_order(self, model, params): + if not self._queue: + raise UnStubbedResponseError( + operation_name=model.name, + reason=( + 'Unexpected API Call: A call was made but no additional ' + 'calls expected. Either the API Call was not stubbed or ' + 'it was called multiple times.' + ), + ) + + name = self._queue[0]['operation_name'] + if name != model.name: + raise StubResponseError( + operation_name=model.name, + reason=f'Operation mismatch: found response for {name}.', + ) + + def _set_account_id_for_endpoint_resolution(self, builtins, **kwargs): + # Account ID comes from credentials and will try to resolve on endpoint resolution + # when it's a builtin. This breaks any stubber in environments where credentials + # are not available. We mock it to be a None value so that we don't attempt to + # resolve credentials. + if 'AWS::Auth::AccountId' in builtins: + builtins['AWS::Auth::AccountId'] = None + + def _get_response_handler(self, model, params, context, **kwargs): + self._assert_expected_call_order(model, params) + # Pop off the entire response once everything has been validated + return self._queue.popleft()['response'] + + def _assert_expected_params(self, model, params, context, **kwargs): + if self._should_not_stub(context): + return + self._assert_expected_call_order(model, params) + expected_params = self._queue[0]['expected_params'] + if expected_params is None: + return + + # Validate the parameters are equal + for param, value in expected_params.items(): + if param not in params or expected_params[param] != params[param]: + raise StubAssertionError( + operation_name=model.name, + reason=( + f'Expected parameters:\n{pformat(expected_params)},\n' + f'but received:\n{pformat(params)}' + ), + ) + + # Ensure there are no extra params hanging around + if sorted(expected_params.keys()) != sorted(params.keys()): + raise StubAssertionError( + operation_name=model.name, + reason=( + f'Expected parameters:\n{pformat(expected_params)},\n' + f'but received:\n{pformat(params)}' + ), + ) + + def _should_not_stub(self, context): + # Do not include presign requests when processing stubbed client calls + # as a presign request will never have an HTTP request sent over the + # wire for it and therefore not receive a response back. + if context and context.get('is_presign_request'): + return True + + def _validate_operation_response(self, operation_name, service_response): + service_model = self.client.meta.service_model + operation_model = service_model.operation_model(operation_name) + output_shape = operation_model.output_shape + + # Remove ResponseMetadata so that the validator doesn't attempt to + # perform validation on it. + response = service_response + if 'ResponseMetadata' in response: + response = copy.copy(service_response) + del response['ResponseMetadata'] + + self._validate_response(output_shape, response) + + def _validate_response(self, shape, response): + if shape is not None: + validate_parameters(response, shape) + elif response: + # If the output shape is None, that means the response should be + # empty apart from ResponseMetadata + raise ParamValidationError( + report=( + "Service response should only contain ResponseMetadata." + ) + ) diff --git a/venv/lib/python3.12/site-packages/botocore/tokens.py b/venv/lib/python3.12/site-packages/botocore/tokens.py new file mode 100644 index 0000000..3335916 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/tokens.py @@ -0,0 +1,428 @@ +# Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import json +import logging +import os +import threading +from datetime import datetime, timedelta +from typing import NamedTuple + +import dateutil.parser +from dateutil.tz import tzutc + +from botocore import UNSIGNED +from botocore.compat import total_seconds +from botocore.config import Config +from botocore.exceptions import ( + ClientError, + InvalidConfigError, + TokenRetrievalError, + UnknownTokenProviderError, +) +from botocore.utils import ( + CachedProperty, + JSONFileCache, + SSOTokenLoader, + create_nested_client, + get_token_from_environment, +) + +logger = logging.getLogger(__name__) + + +def _utc_now(): + return datetime.now(tzutc()) + + +def create_token_resolver(session): + providers = [ + ScopedEnvTokenProvider(session), + SSOTokenProvider(session), + ] + return TokenProviderChain(providers=providers) + + +def _serialize_utc_timestamp(obj): + if isinstance(obj, datetime): + return obj.strftime("%Y-%m-%dT%H:%M:%SZ") + return obj + + +def _sso_json_dumps(obj): + return json.dumps(obj, default=_serialize_utc_timestamp) + + +class FrozenAuthToken(NamedTuple): + token: str + expiration: datetime | None = None + + +class DeferredRefreshableToken: + # The time at which we'll attempt to refresh, but not block if someone else + # is refreshing. + _advisory_refresh_timeout = 15 * 60 + # The time at which all threads will block waiting for a refreshed token + _mandatory_refresh_timeout = 10 * 60 + # Refresh at most once every minute to avoid blocking every request + _attempt_timeout = 60 + + def __init__(self, method, refresh_using, time_fetcher=_utc_now): + self._time_fetcher = time_fetcher + self._refresh_using = refresh_using + self.method = method + + # The frozen token is protected by this lock + self._refresh_lock = threading.Lock() + self._frozen_token = None + self._next_refresh = None + + def get_frozen_token(self): + self._refresh() + return self._frozen_token + + def _refresh(self): + # If we don't need to refresh just return + refresh_type = self._should_refresh() + if not refresh_type: + return None + + # Block for refresh if we're in the mandatory refresh window + block_for_refresh = refresh_type == "mandatory" + if self._refresh_lock.acquire(block_for_refresh): + try: + self._protected_refresh() + finally: + self._refresh_lock.release() + + def _protected_refresh(self): + # This should only be called after acquiring the refresh lock + # Another thread may have already refreshed, double check refresh + refresh_type = self._should_refresh() + if not refresh_type: + return None + + try: + now = self._time_fetcher() + self._next_refresh = now + timedelta(seconds=self._attempt_timeout) + self._frozen_token = self._refresh_using() + except Exception: + logger.warning( + "Refreshing token failed during the %s refresh period.", + refresh_type, + exc_info=True, + ) + if refresh_type == "mandatory": + # This refresh was mandatory, error must be propagated back + raise + + if self._is_expired(): + # Fresh credentials should never be expired + raise TokenRetrievalError( + provider=self.method, + error_msg="Token has expired and refresh failed", + ) + + def _is_expired(self): + if self._frozen_token is None: + return False + + expiration = self._frozen_token.expiration + remaining = total_seconds(expiration - self._time_fetcher()) + return remaining <= 0 + + def _should_refresh(self): + if self._frozen_token is None: + # We don't have a token yet, mandatory refresh + return "mandatory" + + expiration = self._frozen_token.expiration + if expiration is None: + # No expiration, so assume we don't need to refresh. + return None + + now = self._time_fetcher() + if now < self._next_refresh: + return None + + remaining = total_seconds(expiration - now) + + if remaining < self._mandatory_refresh_timeout: + return "mandatory" + elif remaining < self._advisory_refresh_timeout: + return "advisory" + + return None + + +class TokenProviderChain: + def __init__(self, providers=None): + if providers is None: + providers = [] + self._providers = providers + + def insert_before(self, name, token_provider): + """Insert a token provider before an existing provider in the chain. + + :param name: The name of the token provider to insert before + (e.g. ``env`` or ``sso``). Existing names can be discovered + by accessing provider ``METHOD`` attributes. + :type name: str + + :param token_provider: The token provider instance to insert. + + :raises UnknownTokenProviderError: If no provider named + ``name`` exists in the chain. + """ + offset = self._get_provider_offset(name) + self._providers.insert(offset, token_provider) + + def insert_after(self, name, token_provider): + """Insert a token provider after an existing provider in the chain. + + :param name: The name of the token provider to insert after + (e.g. ``env`` or ``sso``). Existing names can be discovered + by accessing provider ``METHOD`` attributes. + :type name: str + + :param token_provider: The token provider instance to insert. + + :raises UnknownTokenProviderError: If no provider named + ``name`` exists in the chain. + """ + offset = self._get_provider_offset(name) + self._providers.insert(offset + 1, token_provider) + + def remove(self, name): + """Remove a token provider from the chain by name. + + If no provider with the given name exists, this is a no-op. + + :param name: The name of the token provider to remove. + :type name: str + """ + available_methods = [p.METHOD for p in self._providers] + if name not in available_methods: + return + + offset = available_methods.index(name) + self._providers.pop(offset) + + def get_provider(self, name): + """Return a token provider by name. + + :param name: The name of the provider to retrieve. + :type name: str + + :raises UnknownTokenProviderError: If no provider named + ``name`` exists in the chain. + """ + return self._providers[self._get_provider_offset(name)] + + def _get_provider_offset(self, name): + try: + return [p.METHOD for p in self._providers].index(name) + except ValueError: + raise UnknownTokenProviderError(name=name) + + def load_token(self, **kwargs): + for provider in self._providers: + token = provider.load_token(**kwargs) + if token is not None: + return token + return None + + +class SSOTokenProvider: + METHOD = "sso" + _REFRESH_WINDOW = 15 * 60 + _SSO_TOKEN_CACHE_DIR = os.path.expanduser( + os.path.join("~", ".aws", "sso", "cache") + ) + _SSO_CONFIG_VARS = [ + "sso_start_url", + "sso_region", + ] + _GRANT_TYPE = "refresh_token" + DEFAULT_CACHE_CLS = JSONFileCache + + def __init__( + self, session, cache=None, time_fetcher=_utc_now, profile_name=None + ): + self._session = session + if cache is None: + cache = self.DEFAULT_CACHE_CLS( + self._SSO_TOKEN_CACHE_DIR, + dumps_func=_sso_json_dumps, + ) + self._now = time_fetcher + self._cache = cache + self._token_loader = SSOTokenLoader(cache=self._cache) + self._profile_name = ( + profile_name + or self._session.get_config_variable("profile") + or 'default' + ) + + def _load_sso_config(self): + loaded_config = self._session.full_config + profiles = loaded_config.get("profiles", {}) + sso_sessions = loaded_config.get("sso_sessions", {}) + profile_config = profiles.get(self._profile_name, {}) + + if "sso_session" not in profile_config: + return + + sso_session_name = profile_config["sso_session"] + sso_config = sso_sessions.get(sso_session_name, None) + + if not sso_config: + error_msg = ( + f'The profile "{self._profile_name}" is configured to use the SSO ' + f'token provider but the "{sso_session_name}" sso_session ' + f"configuration does not exist." + ) + raise InvalidConfigError(error_msg=error_msg) + + missing_configs = [] + for var in self._SSO_CONFIG_VARS: + if var not in sso_config: + missing_configs.append(var) + + if missing_configs: + error_msg = ( + f'The profile "{self._profile_name}" is configured to use the SSO ' + f"token provider but is missing the following configuration: " + f"{missing_configs}." + ) + raise InvalidConfigError(error_msg=error_msg) + + return { + "session_name": sso_session_name, + "sso_region": sso_config["sso_region"], + "sso_start_url": sso_config["sso_start_url"], + } + + @CachedProperty + def _sso_config(self): + return self._load_sso_config() + + @CachedProperty + def _client(self): + config = Config( + region_name=self._sso_config["sso_region"], + signature_version=UNSIGNED, + ) + return create_nested_client(self._session, "sso-oidc", config=config) + + def _attempt_create_token(self, token): + response = self._client.create_token( + grantType=self._GRANT_TYPE, + clientId=token["clientId"], + clientSecret=token["clientSecret"], + refreshToken=token["refreshToken"], + ) + expires_in = timedelta(seconds=response["expiresIn"]) + new_token = { + "startUrl": self._sso_config["sso_start_url"], + "region": self._sso_config["sso_region"], + "accessToken": response["accessToken"], + "expiresAt": self._now() + expires_in, + # Cache the registration alongside the token + "clientId": token["clientId"], + "clientSecret": token["clientSecret"], + "registrationExpiresAt": token["registrationExpiresAt"], + } + if "refreshToken" in response: + new_token["refreshToken"] = response["refreshToken"] + logger.info("SSO Token refresh succeeded") + return new_token + + def _refresh_access_token(self, token): + keys = ( + "refreshToken", + "clientId", + "clientSecret", + "registrationExpiresAt", + ) + missing_keys = [k for k in keys if k not in token] + if missing_keys: + msg = f"Unable to refresh SSO token: missing keys: {missing_keys}" + logger.info(msg) + return None + + expiry = dateutil.parser.parse(token["registrationExpiresAt"]) + if total_seconds(expiry - self._now()) <= 0: + logger.info("SSO token registration expired at %s", expiry) + return None + + try: + return self._attempt_create_token(token) + except ClientError: + logger.warning("SSO token refresh attempt failed", exc_info=True) + return None + + def _refresher(self): + start_url = self._sso_config["sso_start_url"] + session_name = self._sso_config["session_name"] + logger.info("Loading cached SSO token for %s", session_name) + token_dict = self._token_loader(start_url, session_name=session_name) + expiration = dateutil.parser.parse(token_dict["expiresAt"]) + logger.debug("Cached SSO token expires at %s", expiration) + + remaining = total_seconds(expiration - self._now()) + if remaining < self._REFRESH_WINDOW: + new_token_dict = self._refresh_access_token(token_dict) + if new_token_dict is not None: + token_dict = new_token_dict + expiration = token_dict["expiresAt"] + self._token_loader.save_token( + start_url, token_dict, session_name=session_name + ) + + return FrozenAuthToken( + token_dict["accessToken"], expiration=expiration + ) + + def load_token(self, **kwargs): + if self._sso_config is None: + return None + + return DeferredRefreshableToken( + self.METHOD, self._refresher, time_fetcher=self._now + ) + + +class ScopedEnvTokenProvider: + """ + Token provider that loads tokens from environment variables scoped to + a specific `signing_name`. + """ + + METHOD = 'env' + + def __init__(self, session, environ=None): + self._session = session + if environ is None: + environ = os.environ + self.environ = environ + + def load_token(self, **kwargs): + signing_name = kwargs.get("signing_name") + if signing_name is None: + return None + + token = get_token_from_environment(signing_name, self.environ) + + if token is not None: + logger.info("Found token in environment variables.") + return FrozenAuthToken(token) diff --git a/venv/lib/python3.12/site-packages/botocore/translate.py b/venv/lib/python3.12/site-packages/botocore/translate.py new file mode 100644 index 0000000..ecfe3bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/translate.py @@ -0,0 +1,78 @@ +# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/ +# Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import copy + +from botocore.utils import merge_dicts + + +def build_retry_config( + endpoint_prefix, retry_model, definitions, client_retry_config=None +): + service_config = retry_model.get(endpoint_prefix, {}) + resolve_references(service_config, definitions) + # We want to merge the global defaults with the service specific + # defaults, with the service specific defaults taking precedence. + # So we use the global defaults as the base. + # + # A deepcopy is done on the retry defaults because it ensures the + # retry model has no chance of getting mutated when the service specific + # configuration or client retry config is merged in. + final_retry_config = { + '__default__': copy.deepcopy(retry_model.get('__default__', {})) + } + resolve_references(final_retry_config, definitions) + # The merge the service specific config on top. + merge_dicts(final_retry_config, service_config) + if client_retry_config is not None: + _merge_client_retry_config(final_retry_config, client_retry_config) + return final_retry_config + + +def _merge_client_retry_config(retry_config, client_retry_config): + max_retry_attempts_override = client_retry_config.get('max_attempts') + if max_retry_attempts_override is not None: + # In the retry config, the max_attempts refers to the maximum number + # of requests in general will be made. However, for the client's + # retry config it refers to how many retry attempts will be made at + # most. So to translate this number from the client config, one is + # added to convert it to the maximum number request that will be made + # by including the initial request. + # + # It is also important to note that if we ever support per operation + # configuration in the retry model via the client, we will need to + # revisit this logic to make sure max_attempts gets applied + # per operation. + retry_config['__default__']['max_attempts'] = ( + max_retry_attempts_override + 1 + ) + + +def resolve_references(config, definitions): + """Recursively replace $ref keys. + + To cut down on duplication, common definitions can be declared + (and passed in via the ``definitions`` attribute) and then + references as {"$ref": "name"}, when this happens the reference + dict is placed with the value from the ``definition`` dict. + + This is recursively done. + + """ + for key, value in config.items(): + if isinstance(value, dict): + if len(value) == 1 and list(value.keys())[0] == '$ref': + # Then we need to resolve this reference. + config[key] = definitions[list(value.values())[0]] + else: + resolve_references(value, definitions) diff --git a/venv/lib/python3.12/site-packages/botocore/useragent.py b/venv/lib/python3.12/site-packages/botocore/useragent.py new file mode 100644 index 0000000..c4be585 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/useragent.py @@ -0,0 +1,678 @@ +# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +""" +NOTE: All classes and functions in this module are considered private and are +subject to abrupt breaking changes. Please do not use them directly. + +To modify the User-Agent header sent by botocore, use one of these +configuration options: +* The ``AWS_SDK_UA_APP_ID`` environment variable. +* The ``sdk_ua_app_id`` setting in the shared AWS config file. +* The ``user_agent_appid`` field in the :py:class:`botocore.config.Config`. +* The ``user_agent_extra`` field in the :py:class:`botocore.config.Config`. + +""" + +import logging +import os +import platform +from copy import copy +from string import ascii_letters, digits +from typing import NamedTuple + +from botocore import __version__ as botocore_version +from botocore.compat import HAS_CRT +from botocore.context import get_context + +logger = logging.getLogger(__name__) + + +_USERAGENT_ALLOWED_CHARACTERS = ascii_letters + digits + "!$%&'*+-.^_`|~," +_USERAGENT_ALLOWED_OS_NAMES = ( + 'windows', + 'linux', + 'macos', + 'android', + 'ios', + 'watchos', + 'tvos', + 'other', +) +_USERAGENT_PLATFORM_NAME_MAPPINGS = {'darwin': 'macos'} +# The name by which botocore is identified in the User-Agent header. While most +# AWS SDKs follow a naming pattern of "aws-sdk-*", botocore and boto3 continue +# using their existing values. Uses uppercase "B" with all other characters +# lowercase. +_USERAGENT_SDK_NAME = 'Botocore' +_USERAGENT_FEATURE_MAPPINGS = { + 'WAITER': 'B', + 'PAGINATOR': 'C', + "RETRY_MODE_LEGACY": "D", + "RETRY_MODE_STANDARD": "E", + "RETRY_MODE_ADAPTIVE": "F", + 'S3_TRANSFER': 'G', + 'GZIP_REQUEST_COMPRESSION': 'L', + 'PROTOCOL_RPC_V2_CBOR': 'M', + 'ENDPOINT_OVERRIDE': 'N', + 'ACCOUNT_ID_MODE_PREFERRED': 'P', + 'ACCOUNT_ID_MODE_DISABLED': 'Q', + 'ACCOUNT_ID_MODE_REQUIRED': 'R', + 'SIGV4A_SIGNING': 'S', + 'RESOLVED_ACCOUNT_ID': 'T', + 'FLEXIBLE_CHECKSUMS_REQ_CRC32': 'U', + 'FLEXIBLE_CHECKSUMS_REQ_CRC32C': 'V', + 'FLEXIBLE_CHECKSUMS_REQ_CRC64': 'W', + 'FLEXIBLE_CHECKSUMS_REQ_SHA1': 'X', + 'FLEXIBLE_CHECKSUMS_REQ_SHA256': 'Y', + 'FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED': 'Z', + 'FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED': 'a', + 'FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED': 'b', + 'FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED': 'c', + 'CREDENTIALS_CODE': 'e', + 'CREDENTIALS_ENV_VARS': 'g', + 'CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN': 'h', + 'CREDENTIALS_STS_ASSUME_ROLE': 'i', + 'CREDENTIALS_STS_ASSUME_ROLE_WEB_ID': 'k', + 'CREDENTIALS_PROFILE': 'n', + 'CREDENTIALS_PROFILE_SOURCE_PROFILE': 'o', + 'CREDENTIALS_PROFILE_NAMED_PROVIDER': 'p', + 'CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN': 'q', + 'CREDENTIALS_PROFILE_SSO': 'r', + 'CREDENTIALS_SSO': 's', + 'CREDENTIALS_PROFILE_SSO_LEGACY': 't', + 'CREDENTIALS_SSO_LEGACY': 'u', + 'CREDENTIALS_PROFILE_PROCESS': 'v', + 'CREDENTIALS_PROCESS': 'w', + 'CREDENTIALS_BOTO2_CONFIG_FILE': 'x', + 'CREDENTIALS_HTTP': 'z', + 'CREDENTIALS_IMDS': '0', + 'BEARER_SERVICE_ENV_VARS': '3', + 'CLI_V1_TO_V2_MIGRATION_DEBUG_MODE': '-', + 'CREDENTIALS_PROFILE_LOGIN': 'AC', + 'CREDENTIALS_LOGIN': 'AD', + 'FLEXIBLE_CHECKSUMS_REQ_MD5': 'AE', + 'FLEXIBLE_CHECKSUMS_REQ_SHA512': 'AF', + 'FLEXIBLE_CHECKSUMS_REQ_XXHASH3': 'AG', + 'FLEXIBLE_CHECKSUMS_REQ_XXHASH64': 'AH', + 'FLEXIBLE_CHECKSUMS_REQ_XXHASH128': 'AI', + 'S3_REGION_REDIRECT': 'Ah', +} + + +def register_feature_id(feature_id): + """Adds metric value to the current context object's ``features`` set. + + :type feature_id: str + :param feature_id: The name of the feature to register. Value must be a key + in the ``_USERAGENT_FEATURE_MAPPINGS`` dict. + """ + ctx = get_context() + if ctx is None: + # Never register features outside the scope of a + # ``botocore.context.start_as_current_context`` context manager. + # Otherwise, the context variable won't be reset and features will + # bleed into all subsequent requests. Return instead of raising an + # exception since this function could be invoked in a public interface. + return + if val := _USERAGENT_FEATURE_MAPPINGS.get(feature_id): + ctx.features.add(val) + + +def register_feature_ids(feature_ids): + """Adds multiple feature IDs to the current context object's ``features`` set. + + :type feature_ids: iterable of str + :param feature_ids: An iterable of feature ID strings to register. Each + value must be a key in the ``_USERAGENT_FEATURE_MAPPINGS`` dict. + """ + for feature_id in feature_ids: + register_feature_id(feature_id) + + +def sanitize_user_agent_string_component(raw_str, allow_hash): + """Replaces all not allowed characters in the string with a dash ("-"). + + Allowed characters are ASCII alphanumerics and ``!$%&'*+-.^_`|~,``. If + ``allow_hash`` is ``True``, "#"``" is also allowed. + + :type raw_str: str + :param raw_str: The input string to be sanitized. + + :type allow_hash: bool + :param allow_hash: Whether "#" is considered an allowed character. + """ + return ''.join( + c + if c in _USERAGENT_ALLOWED_CHARACTERS or (allow_hash and c == '#') + else '-' + for c in raw_str + ) + + +class UserAgentComponentSizeConfig: + """ + Configures the max size of a built user agent string component and the + delimiter used to truncate the string if the size is above the max. + """ + + def __init__(self, max_size_in_bytes: int, delimiter: str): + self.max_size_in_bytes = max_size_in_bytes + self.delimiter = delimiter + self._validate_input() + + def _validate_input(self): + if self.max_size_in_bytes < 1: + raise ValueError( + f'Invalid `max_size_in_bytes`: {self.max_size_in_bytes}. ' + 'Value must be a positive integer.' + ) + + +class UserAgentComponent(NamedTuple): + """ + Component of a Botocore User-Agent header string in the standard format. + + Each component consists of a prefix, a name, a value, and a size_config. + In the string representation these are combined in the format + ``prefix/name#value``. + + ``size_config`` configures the max size and truncation strategy for the + built user agent string component. + + This class is considered private and is subject to abrupt breaking changes. + """ + + prefix: str + name: str + value: str | None = None + size_config: UserAgentComponentSizeConfig | None = None + + def to_string(self): + """Create string like 'prefix/name#value' from a UserAgentComponent.""" + clean_prefix = sanitize_user_agent_string_component( + self.prefix, allow_hash=True + ) + clean_name = sanitize_user_agent_string_component( + self.name, allow_hash=False + ) + if self.value is None or self.value == '': + clean_string = f'{clean_prefix}/{clean_name}' + else: + clean_value = sanitize_user_agent_string_component( + self.value, allow_hash=True + ) + clean_string = f'{clean_prefix}/{clean_name}#{clean_value}' + if self.size_config is not None: + clean_string = self._truncate_string( + clean_string, + self.size_config.max_size_in_bytes, + self.size_config.delimiter, + ) + return clean_string + + def _truncate_string(self, string, max_size, delimiter): + """ + Pop ``delimiter``-separated values until encoded string is less than or + equal to ``max_size``. + """ + orig = string + while len(string.encode('utf-8')) > max_size: + parts = string.split(delimiter) + parts.pop() + string = delimiter.join(parts) + + if string == '': + logger.debug( + "User agent component `%s` could not be truncated to " + "`%s` bytes with delimiter " + "`%s` without losing all contents. " + "Value will be omitted from user agent string.", + orig, + max_size, + delimiter, + ) + return string + + +class RawStringUserAgentComponent: + """ + UserAgentComponent interface wrapper around ``str``. + + Use for User-Agent header components that are not constructed from + prefix+name+value but instead are provided as strings. No sanitization is + performed. + """ + + def __init__(self, value): + self._value = value + + def to_string(self): + return self._value + + +# This is not a public interface and is subject to abrupt breaking changes. +# Any usage is not advised or supported in external code bases. +try: + from botocore.customizations.useragent import modify_components +except ImportError: + # Default implementation that returns unmodified User-Agent components. + def modify_components(components): + return components + + +class UserAgentString: + """ + Generator for AWS SDK User-Agent header strings. + + The User-Agent header format contains information from session, client, and + request context. ``UserAgentString`` provides methods for collecting the + information and ``to_string`` for assembling it into the standardized + string format. + + Example usage: + + ua_session = UserAgentString.from_environment() + ua_session.set_session_config(...) + ua_client = ua_session.with_client_config(Config(...)) + ua_string = ua_request.to_string() + + For testing or when information from all sources is available at the same + time, the methods can be chained: + + ua_string = ( + UserAgentString + .from_environment() + .set_session_config(...) + .with_client_config(Config(...)) + .to_string() + ) + + """ + + def __init__( + self, + platform_name, + platform_version, + platform_machine, + python_version, + python_implementation, + execution_env, + crt_version=None, + ): + """ + :type platform_name: str + :param platform_name: Name of the operating system or equivalent + platform name. Should be sourced from :py:meth:`platform.system`. + :type platform_version: str + :param platform_version: Version of the operating system or equivalent + platform name. Should be sourced from :py:meth:`platform.version`. + :type platform_machine: str + :param platform_version: Processor architecture or machine type. For + example "x86_64". Should be sourced from :py:meth:`platform.machine`. + :type python_version: str + :param python_version: Version of the python implementation as str. + Should be sourced from :py:meth:`platform.python_version`. + :type python_implementation: str + :param python_implementation: Name of the python implementation. + Should be sourced from :py:meth:`platform.python_implementation`. + :type execution_env: str + :param execution_env: The value of the AWS execution environment. + Should be sourced from the ``AWS_EXECUTION_ENV` environment + variable. + :type crt_version: str + :param crt_version: Version string of awscrt package, if installed. + """ + self._platform_name = platform_name + self._platform_version = platform_version + self._platform_machine = platform_machine + self._python_version = python_version + self._python_implementation = python_implementation + self._execution_env = execution_env + self._crt_version = crt_version + + # Components that can be added with ``set_session_config()`` + self._session_user_agent_name = None + self._session_user_agent_version = None + self._session_user_agent_extra = None + + self._client_config = None + + # Component that can be set with ``set_client_features()`` + self._client_features = None + + @classmethod + def from_environment(cls): + crt_version = None + if HAS_CRT: + crt_version = _get_crt_version() or 'Unknown' + return cls( + platform_name=platform.system(), + platform_version=platform.release(), + platform_machine=platform.machine(), + python_version=platform.python_version(), + python_implementation=platform.python_implementation(), + execution_env=os.environ.get('AWS_EXECUTION_ENV'), + crt_version=crt_version, + ) + + def set_session_config( + self, + session_user_agent_name, + session_user_agent_version, + session_user_agent_extra, + ): + """ + Set the user agent configuration values that apply at session level. + + :param user_agent_name: The user agent name configured in the + :py:class:`botocore.session.Session` object. For backwards + compatibility, this will always be at the beginning of the + User-Agent string, together with ``user_agent_version``. + :param user_agent_version: The user agent version configured in the + :py:class:`botocore.session.Session` object. + :param user_agent_extra: The user agent "extra" configured in the + :py:class:`botocore.session.Session` object. + """ + self._session_user_agent_name = session_user_agent_name + self._session_user_agent_version = session_user_agent_version + self._session_user_agent_extra = session_user_agent_extra + return self + + def set_client_features(self, features): + """ + Persist client-specific features registered before or during client + creation. + + :type features: Set[str] + :param features: A set of client-specific features. + """ + self._client_features = features + + def with_client_config(self, client_config): + """ + Create a copy with all original values and client-specific values. + + :type client_config: botocore.config.Config + :param client_config: The client configuration object. + """ + cp = copy(self) + cp._client_config = client_config + return cp + + def to_string(self): + """ + Build User-Agent header string from the object's properties. + """ + config_ua_override = None + if self._client_config: + if hasattr(self._client_config, '_supplied_user_agent'): + config_ua_override = self._client_config._supplied_user_agent + else: + config_ua_override = self._client_config.user_agent + + if config_ua_override is not None: + return self._build_legacy_ua_string(config_ua_override) + + components = [ + *self._build_sdk_metadata(), + RawStringUserAgentComponent('ua/2.1'), + *self._build_os_metadata(), + *self._build_architecture_metadata(), + *self._build_language_metadata(), + *self._build_execution_env_metadata(), + *self._build_feature_metadata(), + *self._build_config_metadata(), + *self._build_app_id(), + *self._build_extra(), + ] + + components = modify_components(components) + + return ' '.join( + [comp.to_string() for comp in components if comp.to_string()] + ) + + def _build_sdk_metadata(self): + """ + Build the SDK name and version component of the User-Agent header. + + For backwards-compatibility both session-level and client-level config + of custom tool names are honored. If this removes the Botocore + information from the start of the string, Botocore's name and version + are included as a separate field with "md" prefix. + """ + sdk_md = [] + if ( + self._session_user_agent_name + and self._session_user_agent_version + and ( + self._session_user_agent_name != _USERAGENT_SDK_NAME + or self._session_user_agent_version != botocore_version + ) + ): + sdk_md.extend( + [ + UserAgentComponent( + self._session_user_agent_name, + self._session_user_agent_version, + ), + UserAgentComponent( + 'md', _USERAGENT_SDK_NAME, botocore_version + ), + ] + ) + else: + sdk_md.append( + UserAgentComponent(_USERAGENT_SDK_NAME, botocore_version) + ) + + if self._crt_version is not None: + sdk_md.append( + UserAgentComponent('md', 'awscrt', self._crt_version) + ) + + return sdk_md + + def _build_os_metadata(self): + """ + Build the OS/platform components of the User-Agent header string. + + For recognized platform names that match or map to an entry in the list + of standardized OS names, a single component with prefix "os" is + returned. Otherwise, one component "os/other" is returned and a second + with prefix "md" and the raw platform name. + + String representations of example return values: + * ``os/macos#10.13.6`` + * ``os/linux`` + * ``os/other`` + * ``os/other md/foobar#1.2.3`` + """ + if self._platform_name is None: + return [UserAgentComponent('os', 'other')] + + plt_name_lower = self._platform_name.lower() + if plt_name_lower in _USERAGENT_ALLOWED_OS_NAMES: + os_family = plt_name_lower + elif plt_name_lower in _USERAGENT_PLATFORM_NAME_MAPPINGS: + os_family = _USERAGENT_PLATFORM_NAME_MAPPINGS[plt_name_lower] + else: + os_family = None + + if os_family is not None: + return [ + UserAgentComponent('os', os_family, self._platform_version) + ] + else: + return [ + UserAgentComponent('os', 'other'), + UserAgentComponent( + 'md', self._platform_name, self._platform_version + ), + ] + + def _build_architecture_metadata(self): + """ + Build architecture component of the User-Agent header string. + + Returns the machine type with prefix "md" and name "arch", if one is + available. Common values include "x86_64", "arm64", "i386". + """ + if self._platform_machine: + return [ + UserAgentComponent( + 'md', 'arch', self._platform_machine.lower() + ) + ] + return [] + + def _build_language_metadata(self): + """ + Build the language components of the User-Agent header string. + + Returns the Python version in a component with prefix "lang" and name + "python". The Python implementation (e.g. CPython, PyPy) is returned as + separate metadata component with prefix "md" and name "pyimpl". + + String representation of an example return value: + ``lang/python#3.10.4 md/pyimpl#CPython`` + """ + lang_md = [ + UserAgentComponent('lang', 'python', self._python_version), + ] + if self._python_implementation: + lang_md.append( + UserAgentComponent('md', 'pyimpl', self._python_implementation) + ) + return lang_md + + def _build_execution_env_metadata(self): + """ + Build the execution environment component of the User-Agent header. + + Returns a single component prefixed with "exec-env", usually sourced + from the environment variable AWS_EXECUTION_ENV. + """ + if self._execution_env: + return [UserAgentComponent('exec-env', self._execution_env)] + else: + return [] + + def _build_feature_metadata(self): + """ + Build the features component of the User-Agent header string. + + Returns a single component with prefix "m" followed by a list of + comma-separated metric values. + """ + ctx = get_context() + context_features = set() if ctx is None else ctx.features + client_features = self._client_features or set() + features = client_features.union(context_features) + if not features: + return [] + size_config = UserAgentComponentSizeConfig(1024, ',') + return [ + UserAgentComponent( + 'm', ','.join(features), size_config=size_config + ) + ] + + def _build_config_metadata(self): + """ + Build the configuration components of the User-Agent header string. + + Returns a list of components with prefix "cfg" followed by the config + setting name and its value. Tracked configuration settings may be + added or removed in future versions. + """ + if not self._client_config or not self._client_config.retries: + return [] + retry_mode = self._client_config.retries.get('mode') + cfg_md = [UserAgentComponent('cfg', 'retry-mode', retry_mode)] + if self._client_config.endpoint_discovery_enabled: + cfg_md.append(UserAgentComponent('cfg', 'endpoint-discovery')) + return cfg_md + + def _build_app_id(self): + """ + Build app component of the User-Agent header string. + + Returns a single component with prefix "app" and value sourced from the + ``user_agent_appid`` field in :py:class:`botocore.config.Config` or + the ``sdk_ua_app_id`` setting in the shared configuration file, or the + ``AWS_SDK_UA_APP_ID`` environment variable. These are the recommended + ways for apps built with Botocore to insert their identifer into the + User-Agent header. + """ + if self._client_config and self._client_config.user_agent_appid: + appid = sanitize_user_agent_string_component( + raw_str=self._client_config.user_agent_appid, allow_hash=True + ) + return [RawStringUserAgentComponent(f'app/{appid}')] + else: + return [] + + def _build_extra(self): + """User agent string components based on legacy "extra" settings. + + Creates components from the session-level and client-level + ``user_agent_extra`` setting, if present. Both are passed through + verbatim and should be appended at the end of the string. + + Preferred ways to inject application-specific information into + botocore's User-Agent header string are the ``user_agent_appid` field + in :py:class:`botocore.config.Config`. The ``AWS_SDK_UA_APP_ID`` + environment variable and the ``sdk_ua_app_id`` configuration file + setting are alternative ways to set the ``user_agent_appid`` config. + """ + extra = [] + if self._session_user_agent_extra: + extra.append( + RawStringUserAgentComponent(self._session_user_agent_extra) + ) + if self._client_config and self._client_config.user_agent_extra: + extra.append( + RawStringUserAgentComponent( + self._client_config.user_agent_extra + ) + ) + return extra + + def _build_legacy_ua_string(self, config_ua_override): + components = [config_ua_override] + if self._session_user_agent_extra: + components.append(self._session_user_agent_extra) + if self._client_config.user_agent_extra: + components.append(self._client_config.user_agent_extra) + return ' '.join(components) + + def rebuild_and_replace_user_agent_handler( + self, operation_name, request, **kwargs + ): + ua_string = self.to_string() + if request.headers.get('User-Agent'): + request.headers.replace_header('User-Agent', ua_string) + + +def _get_crt_version(): + """ + This function is considered private and is subject to abrupt breaking + changes. + """ + try: + import awscrt + + return awscrt.__version__ + except AttributeError: + return None diff --git a/venv/lib/python3.12/site-packages/botocore/utils.py b/venv/lib/python3.12/site-packages/botocore/utils.py new file mode 100644 index 0000000..96cee50 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/utils.py @@ -0,0 +1,3742 @@ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import base64 +import binascii +import datetime +import email.message +import functools +import hashlib +import io +import logging +import os +import random +import re +import socket +import tempfile +import time +import warnings +import weakref +from datetime import datetime as _DatetimeClass +from ipaddress import ip_address +from pathlib import Path +from urllib.request import getproxies, proxy_bypass + +import dateutil.parser +from dateutil.tz import tzutc +from urllib3.exceptions import LocationParseError + +import botocore +import botocore.awsrequest +import botocore.httpsession + +# IP Regexes retained for backwards compatibility +from botocore.compat import ( + HAS_CRT, + HEX_PAT, # noqa: F401 + IPV4_PAT, # noqa: F401 + IPV4_RE, + IPV6_ADDRZ_PAT, # noqa: F401 + IPV6_ADDRZ_RE, + IPV6_PAT, # noqa: F401 + LS32_PAT, # noqa: F401 + MD5_AVAILABLE, + UNRESERVED_PAT, # noqa: F401 + UNSAFE_URL_CHARS, + ZONE_ID_PAT, # noqa: F401 + OrderedDict, + get_current_datetime, + get_md5, + get_tzinfo_options, + json, + quote, + urlparse, + urlsplit, + urlunsplit, + zip_longest, +) +from botocore.exceptions import ( + ClientError, + ConfigNotFound, + ConnectionClosedError, + ConnectTimeoutError, + EndpointConnectionError, + HTTPClientError, + InvalidDNSNameError, + InvalidEndpointConfigurationError, + InvalidExpressionError, + InvalidHostLabelError, + InvalidIMDSEndpointError, + InvalidIMDSEndpointModeError, + InvalidRegionError, + MetadataRetrievalError, + MissingDependencyException, + ReadTimeoutError, + SSOTokenLoadError, + UnsupportedOutpostResourceError, + UnsupportedS3AccesspointConfigurationError, + UnsupportedS3ArnError, + UnsupportedS3ConfigurationError, + UnsupportedS3ControlArnError, + UnsupportedS3ControlConfigurationError, +) +from botocore.plugin import ( + PluginContext, + reset_plugin_context, + set_plugin_context, +) +from botocore.useragent import register_feature_id + +logger = logging.getLogger(__name__) +DEFAULT_METADATA_SERVICE_TIMEOUT = 1 +METADATA_BASE_URL = 'http://169.254.169.254/' +METADATA_BASE_URL_IPv6 = 'http://[fd00:ec2::254]/' +METADATA_ENDPOINT_MODES = ('ipv4', 'ipv6') + +# These are chars that do not need to be urlencoded. +# Based on rfc2986, section 2.3 +SAFE_CHARS = '-._~' +LABEL_RE = re.compile(r'[a-z0-9][a-z0-9\-]*[a-z0-9]') +RETRYABLE_HTTP_ERRORS = ( + ReadTimeoutError, + EndpointConnectionError, + ConnectionClosedError, + ConnectTimeoutError, +) +S3_ACCELERATE_WHITELIST = ['dualstack'] +# In switching events from using service name / endpoint prefix to service +# id, we have to preserve compatibility. This maps the instances where either +# is different than the transformed service id. +EVENT_ALIASES = { + "api.mediatailor": "mediatailor", + "api.pricing": "pricing", + "api.sagemaker": "sagemaker", + "apigateway": "api-gateway", + "application-autoscaling": "application-auto-scaling", + "appstream2": "appstream", + "autoscaling": "auto-scaling", + "autoscaling-plans": "auto-scaling-plans", + "ce": "cost-explorer", + "cloudhsmv2": "cloudhsm-v2", + "cloudsearchdomain": "cloudsearch-domain", + "cognito-idp": "cognito-identity-provider", + "config": "config-service", + "cur": "cost-and-usage-report-service", + "data.iot": "iot-data-plane", + "data.jobs.iot": "iot-jobs-data-plane", + "data.mediastore": "mediastore-data", + "datapipeline": "data-pipeline", + "devicefarm": "device-farm", + "directconnect": "direct-connect", + "discovery": "application-discovery-service", + "dms": "database-migration-service", + "ds": "directory-service", + "dynamodbstreams": "dynamodb-streams", + "elasticbeanstalk": "elastic-beanstalk", + "elasticfilesystem": "efs", + "elasticloadbalancing": "elastic-load-balancing", + "elasticmapreduce": "emr", + "elb": "elastic-load-balancing", + "elbv2": "elastic-load-balancing-v2", + "email": "ses", + "entitlement.marketplace": "marketplace-entitlement-service", + "es": "elasticsearch-service", + "events": "eventbridge", + "cloudwatch-events": "eventbridge", + "iot-data": "iot-data-plane", + "iot-jobs-data": "iot-jobs-data-plane", + "kinesisanalytics": "kinesis-analytics", + "kinesisvideo": "kinesis-video", + "lex-models": "lex-model-building-service", + "lex-runtime": "lex-runtime-service", + "logs": "cloudwatch-logs", + "machinelearning": "machine-learning", + "marketplace-entitlement": "marketplace-entitlement-service", + "marketplacecommerceanalytics": "marketplace-commerce-analytics", + "metering.marketplace": "marketplace-metering", + "meteringmarketplace": "marketplace-metering", + "mgh": "migration-hub", + "models.lex": "lex-model-building-service", + "monitoring": "cloudwatch", + "mturk-requester": "mturk", + "resourcegroupstaggingapi": "resource-groups-tagging-api", + "route53": "route-53", + "route53domains": "route-53-domains", + "runtime.lex": "lex-runtime-service", + "runtime.sagemaker": "sagemaker-runtime", + "sdb": "simpledb", + "secretsmanager": "secrets-manager", + "serverlessrepo": "serverlessapplicationrepository", + "servicecatalog": "service-catalog", + "states": "sfn", + "stepfunctions": "sfn", + "storagegateway": "storage-gateway", + "streams.dynamodb": "dynamodb-streams", + "tagging": "resource-groups-tagging-api", +} + + +# This pattern can be used to detect if a header is a flexible checksum header +CHECKSUM_HEADER_PATTERN = re.compile( + r'^X-Amz-Checksum-([a-z0-9]*)$', + flags=re.IGNORECASE, +) + +PRIORITY_ORDERED_SUPPORTED_PROTOCOLS = ( + 'json', + 'rest-json', + 'rest-xml', + 'smithy-rpc-v2-cbor', + 'query', + 'ec2', +) + + +def ensure_boolean(val): + """Ensures a boolean value if a string or boolean is provided + + For strings, the value for True/False is case insensitive + """ + if isinstance(val, bool): + return val + elif isinstance(val, str): + return val.lower() == 'true' + else: + return False + + +def resolve_imds_endpoint_mode(session): + """Resolving IMDS endpoint mode to either IPv6 or IPv4. + + ec2_metadata_service_endpoint_mode takes precedence over imds_use_ipv6. + """ + endpoint_mode = session.get_config_variable( + 'ec2_metadata_service_endpoint_mode' + ) + if endpoint_mode is not None: + lendpoint_mode = endpoint_mode.lower() + if lendpoint_mode not in METADATA_ENDPOINT_MODES: + error_msg_kwargs = { + 'mode': endpoint_mode, + 'valid_modes': METADATA_ENDPOINT_MODES, + } + raise InvalidIMDSEndpointModeError(**error_msg_kwargs) + return lendpoint_mode + elif session.get_config_variable('imds_use_ipv6'): + return 'ipv6' + return 'ipv4' + + +def is_json_value_header(shape): + """Determines if the provided shape is the special header type jsonvalue. + + :type shape: botocore.shape + :param shape: Shape to be inspected for the jsonvalue trait. + + :return: True if this type is a jsonvalue, False otherwise + :rtype: Bool + """ + return ( + hasattr(shape, 'serialization') + and shape.serialization.get('jsonvalue', False) + and shape.serialization.get('location') == 'header' + and shape.type_name == 'string' + ) + + +def has_header(header_name, headers): + """Case-insensitive check for header key.""" + if header_name is None: + return False + elif isinstance(headers, botocore.awsrequest.HeadersDict): + return header_name in headers + else: + return header_name.lower() in [key.lower() for key in headers.keys()] + + +def get_service_module_name(service_model): + """Returns the module name for a service + + This is the value used in both the documentation and client class name + """ + name = service_model.metadata.get( + 'serviceAbbreviation', + service_model.metadata.get( + 'serviceFullName', service_model.service_name + ), + ) + name = name.replace('Amazon', '') + name = name.replace('AWS', '') + name = re.sub(r'\W+', '', name) + return name + + +def normalize_url_path(path): + if not path: + return '/' + return remove_dot_segments(path) + + +def normalize_boolean(val): + """Returns None if val is None, otherwise ensure value + converted to boolean""" + if val is None: + return val + else: + return ensure_boolean(val) + + +def remove_dot_segments(url): + # RFC 3986, section 5.2.4 "Remove Dot Segments" + # Also, AWS services require consecutive slashes to be removed, + # so that's done here as well + if not url: + return '' + input_url = url.split('/') + output_list = [] + for x in input_url: + if x and x != '.': + if x == '..': + if output_list: + output_list.pop() + else: + output_list.append(x) + + if url[0] == '/': + first = '/' + else: + first = '' + if url[-1] == '/' and output_list: + last = '/' + else: + last = '' + return first + '/'.join(output_list) + last + + +def validate_jmespath_for_set(expression): + # Validates a limited jmespath expression to determine if we can set a + # value based on it. Only works with dotted paths. + if not expression or expression == '.': + raise InvalidExpressionError(expression=expression) + + for invalid in ['[', ']', '*']: + if invalid in expression: + raise InvalidExpressionError(expression=expression) + + +def set_value_from_jmespath(source, expression, value, is_first=True): + # This takes a (limited) jmespath-like expression & can set a value based + # on it. + # Limitations: + # * Only handles dotted lookups + # * No offsets/wildcards/slices/etc. + if is_first: + validate_jmespath_for_set(expression) + + bits = expression.split('.', 1) + current_key, remainder = bits[0], bits[1] if len(bits) > 1 else '' + + if not current_key: + raise InvalidExpressionError(expression=expression) + + if remainder: + if current_key not in source: + # We've got something in the expression that's not present in the + # source (new key). If there's any more bits, we'll set the key + # with an empty dictionary. + source[current_key] = {} + + return set_value_from_jmespath( + source[current_key], remainder, value, is_first=False + ) + + # If we're down to a single key, set it. + source[current_key] = value + + +def is_global_accesspoint(context): + """Determine if request is intended for an MRAP accesspoint.""" + s3_accesspoint = context.get('s3_accesspoint', {}) + is_global = s3_accesspoint.get('region') == '' + return is_global + + +def create_nested_client(session, service_name, **kwargs): + # If a client is created from within a plugin based on the environment variable, + # an infinite loop could arise. Any clients created from within another client + # must use this method to prevent infinite loops. + ctx = PluginContext(plugins="DISABLED") + token = set_plugin_context(ctx) + try: + return session.create_client(service_name, **kwargs) + finally: + reset_plugin_context(token) + + +class _RetriesExceededError(Exception): + """Internal exception used when the number of retries are exceeded.""" + + pass + + +class BadIMDSRequestError(Exception): + def __init__(self, request): + self.request = request + + +class IMDSFetcher: + _RETRIES_EXCEEDED_ERROR_CLS = _RetriesExceededError + _TOKEN_PATH = 'latest/api/token' + _TOKEN_TTL = '21600' + + def __init__( + self, + timeout=DEFAULT_METADATA_SERVICE_TIMEOUT, + num_attempts=1, + base_url=METADATA_BASE_URL, + env=None, + user_agent=None, + config=None, + ): + self._timeout = timeout + self._num_attempts = num_attempts + if config is None: + config = {} + self._base_url = self._select_base_url(base_url, config) + self._config = config + + if env is None: + env = os.environ.copy() + self._disabled = ( + env.get('AWS_EC2_METADATA_DISABLED', 'false').lower() == 'true' + ) + self._imds_v1_disabled = config.get('ec2_metadata_v1_disabled') + self._user_agent = user_agent + self._session = botocore.httpsession.URLLib3Session( + timeout=self._timeout, + proxies=get_environ_proxies(self._base_url), + ) + + def get_base_url(self): + return self._base_url + + def _select_base_url(self, base_url, config): + if config is None: + config = {} + + requires_ipv6 = ( + config.get('ec2_metadata_service_endpoint_mode') == 'ipv6' + ) + custom_metadata_endpoint = config.get('ec2_metadata_service_endpoint') + + if requires_ipv6 and custom_metadata_endpoint: + logger.warning( + "Custom endpoint and IMDS_USE_IPV6 are both set. Using custom endpoint." + ) + + chosen_base_url = None + + if base_url != METADATA_BASE_URL: + chosen_base_url = base_url + elif custom_metadata_endpoint: + chosen_base_url = custom_metadata_endpoint + elif requires_ipv6: + chosen_base_url = METADATA_BASE_URL_IPv6 + else: + chosen_base_url = METADATA_BASE_URL + + logger.debug("IMDS ENDPOINT: %s", chosen_base_url) + if not is_valid_uri(chosen_base_url): + raise InvalidIMDSEndpointError(endpoint=chosen_base_url) + + return chosen_base_url + + def _construct_url(self, path): + sep = '' + if self._base_url and not self._base_url.endswith('/'): + sep = '/' + return f'{self._base_url}{sep}{path}' + + def _fetch_metadata_token(self): + self._assert_enabled() + url = self._construct_url(self._TOKEN_PATH) + headers = { + 'x-aws-ec2-metadata-token-ttl-seconds': self._TOKEN_TTL, + } + self._add_user_agent(headers) + request = botocore.awsrequest.AWSRequest( + method='PUT', url=url, headers=headers + ) + for i in range(self._num_attempts): + try: + response = self._session.send(request.prepare()) + if response.status_code == 200: + return response.text + elif response.status_code in (404, 403, 405): + return None + elif response.status_code in (400,): + raise BadIMDSRequestError(request) + except ReadTimeoutError: + return None + except RETRYABLE_HTTP_ERRORS as e: + logger.debug( + "Caught retryable HTTP exception while making metadata " + "service request to %s: %s", + url, + e, + exc_info=True, + ) + except HTTPClientError as e: + if isinstance(e.kwargs.get('error'), LocationParseError): + raise InvalidIMDSEndpointError(endpoint=url, error=e) + else: + raise + return None + + def _get_request(self, url_path, retry_func, token=None): + """Make a get request to the Instance Metadata Service. + + :type url_path: str + :param url_path: The path component of the URL to make a get request. + This arg is appended to the base_url that was provided in the + initializer. + + :type retry_func: callable + :param retry_func: A function that takes the response as an argument + and determines if it needs to retry. By default empty and non + 200 OK responses are retried. + + :type token: str + :param token: Metadata token to send along with GET requests to IMDS. + """ + self._assert_enabled() + if not token: + self._assert_v1_enabled() + if retry_func is None: + retry_func = self._default_retry + url = self._construct_url(url_path) + headers = {} + if token is not None: + headers['x-aws-ec2-metadata-token'] = token + self._add_user_agent(headers) + for i in range(self._num_attempts): + try: + request = botocore.awsrequest.AWSRequest( + method='GET', url=url, headers=headers + ) + response = self._session.send(request.prepare()) + if not retry_func(response): + return response + except RETRYABLE_HTTP_ERRORS as e: + logger.debug( + "Caught retryable HTTP exception while making metadata " + "service request to %s: %s", + url, + e, + exc_info=True, + ) + raise self._RETRIES_EXCEEDED_ERROR_CLS() + + def _add_user_agent(self, headers): + if self._user_agent is not None: + headers['User-Agent'] = self._user_agent + + def _assert_enabled(self): + if self._disabled: + logger.debug("Access to EC2 metadata has been disabled.") + raise self._RETRIES_EXCEEDED_ERROR_CLS() + + def _assert_v1_enabled(self): + if self._imds_v1_disabled: + raise MetadataRetrievalError( + error_msg="Unable to retrieve token for use in IMDSv2 call and IMDSv1 has been disabled" + ) + + def _default_retry(self, response): + return self._is_non_ok_response(response) or self._is_empty(response) + + def _is_non_ok_response(self, response): + if response.status_code != 200: + self._log_imds_response(response, 'non-200', log_body=True) + return True + return False + + def _is_empty(self, response): + if not response.content: + self._log_imds_response(response, 'no body', log_body=True) + return True + return False + + def _log_imds_response(self, response, reason_to_log, log_body=False): + statement = ( + "Metadata service returned %s response " + "with status code of %s for url: %s" + ) + logger_args = [reason_to_log, response.status_code, response.url] + if log_body: + statement += ", content body: %s" + logger_args.append(response.content) + logger.debug(statement, *logger_args) + + +class InstanceMetadataFetcher(IMDSFetcher): + _URL_PATH = 'latest/meta-data/iam/security-credentials/' + _REQUIRED_CREDENTIAL_FIELDS = [ + 'AccessKeyId', + 'SecretAccessKey', + 'Token', + 'Expiration', + ] + + def retrieve_iam_role_credentials(self): + try: + token = self._fetch_metadata_token() + role_name = self._get_iam_role(token) + credentials = self._get_credentials(role_name, token) + if self._contains_all_credential_fields(credentials): + credentials = { + 'role_name': role_name, + 'access_key': credentials['AccessKeyId'], + 'secret_key': credentials['SecretAccessKey'], + 'token': credentials['Token'], + 'expiry_time': credentials['Expiration'], + } + self._evaluate_expiration(credentials) + return credentials + else: + # IMDS can return a 200 response that has a JSON formatted + # error message (i.e. if ec2 is not trusted entity for the + # attached role). We do not necessarily want to retry for + # these and we also do not necessarily want to raise a key + # error. So at least log the problematic response and return + # an empty dictionary to signal that it was not able to + # retrieve credentials. These error will contain both a + # Code and Message key. + if 'Code' in credentials and 'Message' in credentials: + logger.debug( + 'Error response received when retrieving' + 'credentials: %s.', + credentials, + ) + return {} + except self._RETRIES_EXCEEDED_ERROR_CLS: + logger.debug( + "Max number of attempts exceeded (%s) when " + "attempting to retrieve data from metadata service.", + self._num_attempts, + ) + except BadIMDSRequestError as e: + logger.debug("Bad IMDS request: %s", e.request) + return {} + + def _get_iam_role(self, token=None): + return self._get_request( + url_path=self._URL_PATH, + retry_func=self._needs_retry_for_role_name, + token=token, + ).text + + def _get_credentials(self, role_name, token=None): + r = self._get_request( + url_path=self._URL_PATH + role_name, + retry_func=self._needs_retry_for_credentials, + token=token, + ) + return json.loads(r.text) + + def _is_invalid_json(self, response): + try: + json.loads(response.text) + return False + except ValueError: + self._log_imds_response(response, 'invalid json') + return True + + def _needs_retry_for_role_name(self, response): + return self._is_non_ok_response(response) or self._is_empty(response) + + def _needs_retry_for_credentials(self, response): + return ( + self._is_non_ok_response(response) + or self._is_empty(response) + or self._is_invalid_json(response) + ) + + def _contains_all_credential_fields(self, credentials): + for field in self._REQUIRED_CREDENTIAL_FIELDS: + if field not in credentials: + logger.debug( + 'Retrieved credentials is missing required field: %s', + field, + ) + return False + return True + + def _evaluate_expiration(self, credentials): + expiration = credentials.get("expiry_time") + if expiration is None: + return + try: + expiration = datetime.datetime.strptime( + expiration, "%Y-%m-%dT%H:%M:%SZ" + ) + refresh_interval = self._config.get( + "ec2_credential_refresh_window", 60 * 10 + ) + jitter = random.randint(120, 600) # Between 2 to 10 minutes + refresh_interval_with_jitter = refresh_interval + jitter + current_time = get_current_datetime() + refresh_offset = datetime.timedelta( + seconds=refresh_interval_with_jitter + ) + extension_time = expiration - refresh_offset + if current_time >= extension_time: + new_time = current_time + refresh_offset + credentials["expiry_time"] = new_time.strftime( + "%Y-%m-%dT%H:%M:%SZ" + ) + logger.info( + "Attempting credential expiration extension due to a " + "credential service availability issue. A refresh of " + "these credentials will be attempted again within " + "the next %.0f minutes.", + refresh_interval_with_jitter / 60, + ) + except ValueError: + logger.debug( + "Unable to parse expiry_time in %s", credentials['expiry_time'] + ) + + +class IMDSRegionProvider: + def __init__(self, session, environ=None, fetcher=None): + """Initialize IMDSRegionProvider. + :type session: :class:`botocore.session.Session` + :param session: The session is needed to look up configuration for + how to contact the instance metadata service. Specifically the + whether or not it should use the IMDS region at all, and if so how + to configure the timeout and number of attempts to reach the + service. + :type environ: None or dict + :param environ: A dictionary of environment variables to use. If + ``None`` is the argument then ``os.environ`` will be used by + default. + :type fecther: :class:`botocore.utils.InstanceMetadataRegionFetcher` + :param fetcher: The class to actually handle the fetching of the region + from the IMDS. If not provided a default one will be created. + """ + self._session = session + if environ is None: + environ = os.environ + self._environ = environ + self._fetcher = fetcher + + def provide(self): + """Provide the region value from IMDS.""" + instance_region = self._get_instance_metadata_region() + return instance_region + + def _get_instance_metadata_region(self): + fetcher = self._get_fetcher() + region = fetcher.retrieve_region() + return region + + def _get_fetcher(self): + if self._fetcher is None: + self._fetcher = self._create_fetcher() + return self._fetcher + + def _create_fetcher(self): + metadata_timeout = self._session.get_config_variable( + 'metadata_service_timeout' + ) + metadata_num_attempts = self._session.get_config_variable( + 'metadata_service_num_attempts' + ) + imds_config = { + 'ec2_metadata_service_endpoint': self._session.get_config_variable( + 'ec2_metadata_service_endpoint' + ), + 'ec2_metadata_service_endpoint_mode': resolve_imds_endpoint_mode( + self._session + ), + 'ec2_metadata_v1_disabled': self._session.get_config_variable( + 'ec2_metadata_v1_disabled' + ), + } + fetcher = InstanceMetadataRegionFetcher( + timeout=metadata_timeout, + num_attempts=metadata_num_attempts, + env=self._environ, + user_agent=self._session.user_agent(), + config=imds_config, + ) + return fetcher + + +class InstanceMetadataRegionFetcher(IMDSFetcher): + _URL_PATH = 'latest/meta-data/placement/availability-zone/' + + def retrieve_region(self): + """Get the current region from the instance metadata service. + :rvalue: str + :returns: The region the current instance is running in or None + if the instance metadata service cannot be contacted or does not + give a valid response. + :rtype: None or str + :returns: Returns the region as a string if it is configured to use + IMDS as a region source. Otherwise returns ``None``. It will also + return ``None`` if it fails to get the region from IMDS due to + exhausting its retries or not being able to connect. + """ + try: + region = self._get_region() + return region + except self._RETRIES_EXCEEDED_ERROR_CLS: + logger.debug( + "Max number of attempts exceeded (%s) when " + "attempting to retrieve data from metadata service.", + self._num_attempts, + ) + return None + + def _get_region(self): + token = self._fetch_metadata_token() + response = self._get_request( + url_path=self._URL_PATH, + retry_func=self._default_retry, + token=token, + ) + availability_zone = response.text + region = availability_zone[:-1] + return region + + +def merge_dicts(dict1, dict2, append_lists=False): + """Given two dict, merge the second dict into the first. + + The dicts can have arbitrary nesting. + + :param append_lists: If true, instead of clobbering a list with the new + value, append all of the new values onto the original list. + """ + for key in dict2: + if isinstance(dict2[key], dict): + if key in dict1 and key in dict2: + merge_dicts(dict1[key], dict2[key]) + else: + dict1[key] = dict2[key] + # If the value is a list and the ``append_lists`` flag is set, + # append the new values onto the original list + elif isinstance(dict2[key], list) and append_lists: + # The value in dict1 must be a list in order to append new + # values onto it. + if key in dict1 and isinstance(dict1[key], list): + dict1[key].extend(dict2[key]) + else: + dict1[key] = dict2[key] + else: + # At scalar types, we iterate and merge the + # current dict that we're on. + dict1[key] = dict2[key] + + +def lowercase_dict(original): + """Copies the given dictionary ensuring all keys are lowercase strings.""" + copy = {} + for key in original: + copy[key.lower()] = original[key] + return copy + + +def parse_key_val_file(filename, _open=open): + try: + with _open(filename) as f: + contents = f.read() + return parse_key_val_file_contents(contents) + except OSError: + raise ConfigNotFound(path=filename) + + +def parse_key_val_file_contents(contents): + # This was originally extracted from the EC2 credential provider, which was + # fairly lenient in its parsing. We only try to parse key/val pairs if + # there's a '=' in the line. + final = {} + for line in contents.splitlines(): + if '=' not in line: + continue + key, val = line.split('=', 1) + key = key.strip() + val = val.strip() + final[key] = val + return final + + +def percent_encode_sequence(mapping, safe=SAFE_CHARS): + """Urlencode a dict or list into a string. + + This is similar to urllib.urlencode except that: + + * It uses quote, and not quote_plus + * It has a default list of safe chars that don't need + to be encoded, which matches what AWS services expect. + + If any value in the input ``mapping`` is a list type, + then each list element wil be serialized. This is the equivalent + to ``urlencode``'s ``doseq=True`` argument. + + This function should be preferred over the stdlib + ``urlencode()`` function. + + :param mapping: Either a dict to urlencode or a list of + ``(key, value)`` pairs. + + """ + encoded_pairs = [] + if hasattr(mapping, 'items'): + pairs = mapping.items() + else: + pairs = mapping + for key, value in pairs: + if isinstance(value, list): + for element in value: + encoded_pairs.append( + f'{percent_encode(key)}={percent_encode(element)}' + ) + else: + encoded_pairs.append( + f'{percent_encode(key)}={percent_encode(value)}' + ) + return '&'.join(encoded_pairs) + + +def percent_encode(input_str, safe=SAFE_CHARS): + """Urlencodes a string. + + Whereas percent_encode_sequence handles taking a dict/sequence and + producing a percent encoded string, this function deals only with + taking a string (not a dict/sequence) and percent encoding it. + + If given the binary type, will simply URL encode it. If given the + text type, will produce the binary type by UTF-8 encoding the + text. If given something else, will convert it to the text type + first. + """ + # If its not a binary or text string, make it a text string. + if not isinstance(input_str, (bytes, str)): + input_str = str(input_str) + # If it's not bytes, make it bytes by UTF-8 encoding it. + if not isinstance(input_str, bytes): + input_str = input_str.encode('utf-8') + return quote(input_str, safe=safe) + + +def _epoch_seconds_to_datetime(value, tzinfo): + """Parse numerical epoch timestamps (seconds since 1970) into a + ``datetime.datetime`` in UTC using ``datetime.timedelta``. This is intended + as fallback when ``fromtimestamp`` raises ``OverflowError`` or ``OSError``. + + :type value: float or int + :param value: The Unix timestamps as number. + + :type tzinfo: callable + :param tzinfo: A ``datetime.tzinfo`` class or compatible callable. + """ + epoch_zero = datetime.datetime(1970, 1, 1, 0, 0, 0, tzinfo=tzutc()) + epoch_zero_localized = epoch_zero.astimezone(tzinfo()) + return epoch_zero_localized + datetime.timedelta(seconds=value) + + +def _parse_timestamp_with_tzinfo(value, tzinfo): + """Parse timestamp with pluggable tzinfo options.""" + if isinstance(value, (int, float)): + # Possibly an epoch time. + return datetime.datetime.fromtimestamp(value, tzinfo()) + else: + try: + return datetime.datetime.fromtimestamp(float(value), tzinfo()) + except (TypeError, ValueError): + pass + try: + # In certain cases, a timestamp marked with GMT can be parsed into a + # different time zone, so here we provide a context which will + # enforce that GMT == UTC. + return dateutil.parser.parse(value, tzinfos={'GMT': tzutc()}) + except (TypeError, ValueError) as e: + raise ValueError(f'Invalid timestamp "{value}": {e}') + + +def parse_timestamp(value): + """Parse a timestamp into a datetime object. + + Supported formats: + + * iso8601 + * rfc822 + * epoch (value is an integer) + + This will return a ``datetime.datetime`` object. + + """ + tzinfo_options = get_tzinfo_options() + for tzinfo in tzinfo_options: + try: + return _parse_timestamp_with_tzinfo(value, tzinfo) + except (OSError, OverflowError) as e: + logger.debug( + 'Unable to parse timestamp with "%s" timezone info.', + tzinfo.__name__, + exc_info=e, + ) + # For numeric values attempt fallback to using fromtimestamp-free method. + # From Python's ``datetime.datetime.fromtimestamp`` documentation: "This + # may raise ``OverflowError``, if the timestamp is out of the range of + # values supported by the platform C localtime() function, and ``OSError`` + # on localtime() failure. It's common for this to be restricted to years + # from 1970 through 2038." + try: + numeric_value = float(value) + except (TypeError, ValueError): + pass + else: + try: + for tzinfo in tzinfo_options: + return _epoch_seconds_to_datetime(numeric_value, tzinfo=tzinfo) + except (OSError, OverflowError) as e: + logger.debug( + 'Unable to parse timestamp using fallback method with "%s" ' + 'timezone info.', + tzinfo.__name__, + exc_info=e, + ) + raise RuntimeError( + f'Unable to calculate correct timezone offset for "{value}"' + ) + + +def parse_to_aware_datetime(value): + """Converted the passed in value to a datetime object with tzinfo. + + This function can be used to normalize all timestamp inputs. This + function accepts a number of different types of inputs, but + will always return a datetime.datetime object with time zone + information. + + The input param ``value`` can be one of several types: + + * A datetime object (both naive and aware) + * An integer representing the epoch time (can also be a string + of the integer, i.e '0', instead of 0). The epoch time is + considered to be UTC. + * An iso8601 formatted timestamp. This does not need to be + a complete timestamp, it can contain just the date portion + without the time component. + + The returned value will be a datetime object that will have tzinfo. + If no timezone info was provided in the input value, then UTC is + assumed, not local time. + + """ + # This is a general purpose method that handles several cases of + # converting the provided value to a string timestamp suitable to be + # serialized to an http request. It can handle: + # 1) A datetime.datetime object. + if isinstance(value, _DatetimeClass): + datetime_obj = value + else: + # 2) A string object that's formatted as a timestamp. + # We document this as being an iso8601 timestamp, although + # parse_timestamp is a bit more flexible. + datetime_obj = parse_timestamp(value) + if datetime_obj.tzinfo is None: + # I think a case would be made that if no time zone is provided, + # we should use the local time. However, to restore backwards + # compat, the previous behavior was to assume UTC, which is + # what we're going to do here. + datetime_obj = datetime_obj.replace(tzinfo=tzutc()) + else: + datetime_obj = datetime_obj.astimezone(tzutc()) + return datetime_obj + + +def datetime2timestamp(dt, default_timezone=None): + """Calculate the timestamp based on the given datetime instance. + + :type dt: datetime + :param dt: A datetime object to be converted into timestamp + :type default_timezone: tzinfo + :param default_timezone: If it is provided as None, we treat it as tzutc(). + But it is only used when dt is a naive datetime. + :returns: The timestamp + """ + epoch = datetime.datetime(1970, 1, 1) + if dt.tzinfo is None: + if default_timezone is None: + default_timezone = tzutc() + dt = dt.replace(tzinfo=default_timezone) + d = dt.replace(tzinfo=None) - dt.utcoffset() - epoch + return d.total_seconds() + + +def calculate_sha256(body, as_hex=False): + """Calculate a sha256 checksum. + + This method will calculate the sha256 checksum of a file like + object. Note that this method will iterate through the entire + file contents. The caller is responsible for ensuring the proper + starting position of the file and ``seek()``'ing the file back + to its starting location if other consumers need to read from + the file like object. + + :param body: Any file like object. The file must be opened + in binary mode such that a ``.read()`` call returns bytes. + :param as_hex: If True, then the hex digest is returned. + If False, then the digest (as binary bytes) is returned. + + :returns: The sha256 checksum + + """ + checksum = hashlib.sha256() + for chunk in iter(lambda: body.read(1024 * 1024), b''): + checksum.update(chunk) + if as_hex: + return checksum.hexdigest() + else: + return checksum.digest() + + +def calculate_tree_hash(body): + """Calculate a tree hash checksum. + + For more information see: + + http://docs.aws.amazon.com/amazonglacier/latest/dev/checksum-calculations.html + + :param body: Any file like object. This has the same constraints as + the ``body`` param in calculate_sha256 + + :rtype: str + :returns: The hex version of the calculated tree hash + + """ + chunks = [] + required_chunk_size = 1024 * 1024 + sha256 = hashlib.sha256 + for chunk in iter(lambda: body.read(required_chunk_size), b''): + chunks.append(sha256(chunk).digest()) + if not chunks: + return sha256(b'').hexdigest() + while len(chunks) > 1: + new_chunks = [] + for first, second in _in_pairs(chunks): + if second is not None: + new_chunks.append(sha256(first + second).digest()) + else: + # We're at the end of the list and there's no pair left. + new_chunks.append(first) + chunks = new_chunks + return binascii.hexlify(chunks[0]).decode('ascii') + + +def _in_pairs(iterable): + # Creates iterator that iterates over the list in pairs: + # for a, b in _in_pairs([0, 1, 2, 3, 4]): + # print(a, b) + # + # will print: + # 0, 1 + # 2, 3 + # 4, None + shared_iter = iter(iterable) + # Note that zip_longest is a compat import that uses + # the itertools izip_longest. This creates an iterator, + # this call below does _not_ immediately create the list + # of pairs. + return zip_longest(shared_iter, shared_iter) + + +class CachedProperty: + """A read only property that caches the initially computed value. + + This descriptor will only call the provided ``fget`` function once. + Subsequent access to this property will return the cached value. + + """ + + def __init__(self, fget): + self._fget = fget + + def __get__(self, obj, cls): + if obj is None: + return self + else: + computed_value = self._fget(obj) + obj.__dict__[self._fget.__name__] = computed_value + return computed_value + + +class ArgumentGenerator: + """Generate sample input based on a shape model. + + This class contains a ``generate_skeleton`` method that will take + an input/output shape (created from ``botocore.model``) and generate + a sample dictionary corresponding to the input/output shape. + + The specific values used are place holder values. For strings either an + empty string or the member name can be used, for numbers 0 or 0.0 is used. + The intended usage of this class is to generate the *shape* of the input + structure. + + This can be useful for operations that have complex input shapes. + This allows a user to just fill in the necessary data instead of + worrying about the specific structure of the input arguments. + + Example usage:: + + s = botocore.session.get_session() + ddb = s.get_service_model('dynamodb') + arg_gen = ArgumentGenerator() + sample_input = arg_gen.generate_skeleton( + ddb.operation_model('CreateTable').input_shape) + print("Sample input for dynamodb.CreateTable: %s" % sample_input) + + """ + + def __init__(self, use_member_names=False): + self._use_member_names = use_member_names + + def generate_skeleton(self, shape): + """Generate a sample input. + + :type shape: ``botocore.model.Shape`` + :param shape: The input shape. + + :return: The generated skeleton input corresponding to the + provided input shape. + + """ + stack = [] + return self._generate_skeleton(shape, stack) + + def _generate_skeleton(self, shape, stack, name=''): + stack.append(shape.name) + try: + if shape.type_name == 'structure': + return self._generate_type_structure(shape, stack) + elif shape.type_name == 'list': + return self._generate_type_list(shape, stack) + elif shape.type_name == 'map': + return self._generate_type_map(shape, stack) + elif shape.type_name == 'string': + if self._use_member_names: + return name + if shape.enum: + return random.choice(shape.enum) + return '' + elif shape.type_name in ['integer', 'long']: + return 0 + elif shape.type_name in ['float', 'double']: + return 0.0 + elif shape.type_name == 'boolean': + return True + elif shape.type_name == 'timestamp': + return datetime.datetime(1970, 1, 1, 0, 0, 0) + finally: + stack.pop() + + def _generate_type_structure(self, shape, stack): + if stack.count(shape.name) > 1: + return {} + skeleton = OrderedDict() + for member_name, member_shape in shape.members.items(): + skeleton[member_name] = self._generate_skeleton( + member_shape, stack, name=member_name + ) + return skeleton + + def _generate_type_list(self, shape, stack): + # For list elements we've arbitrarily decided to + # return two elements for the skeleton list. + name = '' + if self._use_member_names: + name = shape.member.name + return [ + self._generate_skeleton(shape.member, stack, name), + ] + + def _generate_type_map(self, shape, stack): + key_shape = shape.key + value_shape = shape.value + assert key_shape.type_name == 'string' + return OrderedDict( + [ + ('KeyName', self._generate_skeleton(value_shape, stack)), + ] + ) + + +def is_valid_ipv6_endpoint_url(endpoint_url): + if UNSAFE_URL_CHARS.intersection(endpoint_url): + return False + hostname = f'[{urlparse(endpoint_url).hostname}]' + return IPV6_ADDRZ_RE.match(hostname) is not None + + +def is_valid_ipv4_endpoint_url(endpoint_url): + hostname = urlparse(endpoint_url).hostname + return IPV4_RE.match(hostname) is not None + + +def is_valid_endpoint_url(endpoint_url): + """Verify the endpoint_url is valid. + + :type endpoint_url: string + :param endpoint_url: An endpoint_url. Must have at least a scheme + and a hostname. + + :return: True if the endpoint url is valid. False otherwise. + + """ + # post-bpo-43882 urlsplit() strips unsafe characters from URL, causing + # it to pass hostname validation below. Detect them early to fix that. + if UNSAFE_URL_CHARS.intersection(endpoint_url): + return False + parts = urlsplit(endpoint_url) + hostname = parts.hostname + if hostname is None: + return False + if len(hostname) > 255: + return False + if hostname[-1] == ".": + hostname = hostname[:-1] + allowed = re.compile( + r"^((?!-)[A-Z\d-]{1,63}(? 63: + # Wrong length + return False + match = LABEL_RE.match(bucket_name) + if match is None or match.end() != len(bucket_name): + return False + return True + + +def fix_s3_host( + request, + signature_version, + region_name, + default_endpoint_url=None, + **kwargs, +): + """ + This handler looks at S3 requests just before they are signed. + If there is a bucket name on the path (true for everything except + ListAllBuckets) it checks to see if that bucket name conforms to + the DNS naming conventions. If it does, it alters the request to + use ``virtual hosting`` style addressing rather than ``path-style`` + addressing. + + """ + if request.context.get('use_global_endpoint', False): + default_endpoint_url = 's3.amazonaws.com' + try: + switch_to_virtual_host_style( + request, signature_version, default_endpoint_url + ) + except InvalidDNSNameError as e: + bucket_name = e.kwargs['bucket_name'] + logger.debug( + 'Not changing URI, bucket is not DNS compatible: %s', bucket_name + ) + + +def switch_to_virtual_host_style( + request, signature_version, default_endpoint_url=None, **kwargs +): + """ + This is a handler to force virtual host style s3 addressing no matter + the signature version (which is taken in consideration for the default + case). If the bucket is not DNS compatible an InvalidDNSName is thrown. + + :param request: A AWSRequest object that is about to be sent. + :param signature_version: The signature version to sign with + :param default_endpoint_url: The endpoint to use when switching to a + virtual style. If None is supplied, the virtual host will be + constructed from the url of the request. + """ + if request.auth_path is not None: + # The auth_path has already been applied (this may be a + # retried request). We don't need to perform this + # customization again. + return + elif _is_get_bucket_location_request(request): + # For the GetBucketLocation response, we should not be using + # the virtual host style addressing so we can avoid any sigv4 + # issues. + logger.debug( + "Request is GetBucketLocation operation, not checking " + "for DNS compatibility." + ) + return + parts = urlsplit(request.url) + request.auth_path = parts.path + path_parts = parts.path.split('/') + + # Retrieve what the endpoint we will be prepending the bucket name to. + if default_endpoint_url is None: + default_endpoint_url = parts.netloc + + if len(path_parts) > 1: + bucket_name = path_parts[1] + if not bucket_name: + # If the bucket name is empty we should not be checking for + # dns compatibility. + return + logger.debug('Checking for DNS compatible bucket for: %s', request.url) + if check_dns_name(bucket_name): + # If the operation is on a bucket, the auth_path must be + # terminated with a '/' character. + if len(path_parts) == 2: + if request.auth_path[-1] != '/': + request.auth_path += '/' + path_parts.remove(bucket_name) + # At the very least the path must be a '/', such as with the + # CreateBucket operation when DNS style is being used. If this + # is not used you will get an empty path which is incorrect. + path = '/'.join(path_parts) or '/' + global_endpoint = default_endpoint_url + host = bucket_name + '.' + global_endpoint + new_tuple = (parts.scheme, host, path, parts.query, '') + new_uri = urlunsplit(new_tuple) + request.url = new_uri + logger.debug('URI updated to: %s', new_uri) + else: + raise InvalidDNSNameError(bucket_name=bucket_name) + + +def _is_get_bucket_location_request(request): + return request.url.endswith('?location') + + +def instance_cache(func): + """Method decorator for caching method calls to a single instance. + + **This is not a general purpose caching decorator.** + + In order to use this, you *must* provide an ``_instance_cache`` + attribute on the instance. + + This decorator is used to cache method calls. The cache is only + scoped to a single instance though such that multiple instances + will maintain their own cache. In order to keep things simple, + this decorator requires that you provide an ``_instance_cache`` + attribute on your instance. + + """ + func_name = func.__name__ + + @functools.wraps(func) + def _cache_guard(self, *args, **kwargs): + cache_key = (func_name, args) + if kwargs: + kwarg_items = tuple(sorted(kwargs.items())) + cache_key = (func_name, args, kwarg_items) + result = self._instance_cache.get(cache_key) + if result is not None: + return result + result = func(self, *args, **kwargs) + self._instance_cache[cache_key] = result + return result + + return _cache_guard + + +def lru_cache_weakref(*cache_args, **cache_kwargs): + """ + Version of functools.lru_cache that stores a weak reference to ``self``. + + Serves the same purpose as :py:func:`instance_cache` but uses Python's + functools implementation which offers ``max_size`` and ``typed`` properties. + + lru_cache is a global cache even when used on a method. The cache's + reference to ``self`` will prevent garbage collection of the object. This + wrapper around functools.lru_cache replaces the reference to ``self`` with + a weak reference to not interfere with garbage collection. + """ + + def wrapper(func): + @functools.lru_cache(*cache_args, **cache_kwargs) + def func_with_weakref(weakref_to_self, *args, **kwargs): + return func(weakref_to_self(), *args, **kwargs) + + @functools.wraps(func) + def inner(self, *args, **kwargs): + for kwarg_key, kwarg_value in kwargs.items(): + if isinstance(kwarg_value, list): + kwargs[kwarg_key] = tuple(kwarg_value) + return func_with_weakref(weakref.ref(self), *args, **kwargs) + + inner.cache_info = func_with_weakref.cache_info + return inner + + return wrapper + + +def switch_host_s3_accelerate(request, operation_name, **kwargs): + """Switches the current s3 endpoint with an S3 Accelerate endpoint""" + + # Note that when registered the switching of the s3 host happens + # before it gets changed to virtual. So we are not concerned with ensuring + # that the bucket name is translated to the virtual style here and we + # can hard code the Accelerate endpoint. + parts = urlsplit(request.url).netloc.split('.') + parts = [p for p in parts if p in S3_ACCELERATE_WHITELIST] + endpoint = 'https://s3-accelerate.' + if len(parts) > 0: + endpoint += '.'.join(parts) + '.' + endpoint += 'amazonaws.com' + + if operation_name in ['ListBuckets', 'CreateBucket', 'DeleteBucket']: + return + _switch_hosts(request, endpoint, use_new_scheme=False) + + +def switch_host_with_param(request, param_name): + """Switches the host using a parameter value from a JSON request body""" + request_json = json.loads(request.data.decode('utf-8')) + if request_json.get(param_name): + new_endpoint = request_json[param_name] + _switch_hosts(request, new_endpoint) + + +def _switch_hosts(request, new_endpoint, use_new_scheme=True): + final_endpoint = _get_new_endpoint( + request.url, new_endpoint, use_new_scheme + ) + request.url = final_endpoint + + +def _get_new_endpoint(original_endpoint, new_endpoint, use_new_scheme=True): + new_endpoint_components = urlsplit(new_endpoint) + original_endpoint_components = urlsplit(original_endpoint) + scheme = original_endpoint_components.scheme + if use_new_scheme: + scheme = new_endpoint_components.scheme + final_endpoint_components = ( + scheme, + new_endpoint_components.netloc, + original_endpoint_components.path, + original_endpoint_components.query, + '', + ) + final_endpoint = urlunsplit(final_endpoint_components) + logger.debug( + 'Updating URI from %s to %s', original_endpoint, final_endpoint + ) + return final_endpoint + + +def deep_merge(base, extra): + """Deeply two dictionaries, overriding existing keys in the base. + + :param base: The base dictionary which will be merged into. + :param extra: The dictionary to merge into the base. Keys from this + dictionary will take precedence. + """ + for key in extra: + # If the key represents a dict on both given dicts, merge the sub-dicts + if ( + key in base + and isinstance(base[key], dict) + and isinstance(extra[key], dict) + ): + deep_merge(base[key], extra[key]) + continue + + # Otherwise, set the key on the base to be the value of the extra. + base[key] = extra[key] + + +def hyphenize_service_id(service_id): + """Translate the form used for event emitters. + + :param service_id: The service_id to convert. + """ + return service_id.replace(' ', '-').lower() + + +class IdentityCache: + """Base IdentityCache implementation for storing and retrieving + highly accessed credentials. + + This class is not intended to be instantiated in user code. + """ + + METHOD = "base_identity_cache" + + def __init__(self, client, credential_cls): + self._client = client + self._credential_cls = credential_cls + + def get_credentials(self, **kwargs): + callback = self.build_refresh_callback(**kwargs) + metadata = callback() + credential_entry = self._credential_cls.create_from_metadata( + metadata=metadata, + refresh_using=callback, + method=self.METHOD, + advisory_timeout=45, + mandatory_timeout=10, + ) + return credential_entry + + def build_refresh_callback(**kwargs): + """Callback to be implemented by subclasses. + + Returns a set of metadata to be converted into a new + credential instance. + """ + raise NotImplementedError() + + +class S3ExpressIdentityCache(IdentityCache): + """S3Express IdentityCache for retrieving and storing + credentials from CreateSession calls. + + This class is not intended to be instantiated in user code. + """ + + METHOD = "s3express" + + def __init__(self, client, credential_cls): + self._client = client + self._credential_cls = credential_cls + + @functools.lru_cache(maxsize=100) + def get_credentials(self, bucket): + return super().get_credentials(bucket=bucket) + + def build_refresh_callback(self, bucket): + def refresher(): + response = self._client.create_session(Bucket=bucket) + creds = response['Credentials'] + expiration = self._serialize_if_needed( + creds['Expiration'], iso=True + ) + return { + "access_key": creds['AccessKeyId'], + "secret_key": creds['SecretAccessKey'], + "token": creds['SessionToken'], + "expiry_time": expiration, + } + + return refresher + + def _serialize_if_needed(self, value, iso=False): + if isinstance(value, _DatetimeClass): + if iso: + return value.isoformat() + return value.strftime('%Y-%m-%dT%H:%M:%S%Z') + return value + + +class S3ExpressIdentityResolver: + def __init__(self, client, credential_cls, cache=None): + self._client = weakref.proxy(client) + + if cache is None: + cache = S3ExpressIdentityCache(self._client, credential_cls) + self._cache = cache + + def register(self, event_emitter=None): + logger.debug('Registering S3Express Identity Resolver') + emitter = event_emitter or self._client.meta.events + emitter.register('before-call.s3', self.apply_signing_cache_key) + emitter.register('before-sign.s3', self.resolve_s3express_identity) + + def apply_signing_cache_key(self, params, context, **kwargs): + endpoint_properties = context.get('endpoint_properties', {}) + backend = endpoint_properties.get('backend', None) + + # Add cache key if Bucket supplied for s3express request + bucket_name = context.get('input_params', {}).get('Bucket') + if backend == 'S3Express' and bucket_name is not None: + context.setdefault('signing', {}) + context['signing']['cache_key'] = bucket_name + + def resolve_s3express_identity( + self, + request, + signing_name, + region_name, + signature_version, + request_signer, + operation_name, + **kwargs, + ): + signing_context = request.context.get('signing', {}) + signing_name = signing_context.get('signing_name') + if signing_name == 's3express' and signature_version.startswith( + 'v4-s3express' + ): + signing_context['identity_cache'] = self._cache + if 'cache_key' not in signing_context: + signing_context['cache_key'] = ( + request.context.get('s3_redirect', {}) + .get('params', {}) + .get('Bucket') + ) + + +class S3RegionRedirectorv2: + """Updated version of S3RegionRedirector for use when + EndpointRulesetResolver is in use for endpoint resolution. + + This class is considered private and subject to abrupt breaking changes or + removal without prior announcement. Please do not use it directly. + """ + + def __init__(self, endpoint_bridge, client, cache=None): + self._cache = cache or {} + self._client = weakref.proxy(client) + + def register(self, event_emitter=None): + logger.debug('Registering S3 region redirector handler') + emitter = event_emitter or self._client.meta.events + emitter.register('needs-retry.s3', self.redirect_from_error) + emitter.register( + 'before-parameter-build.s3', self.annotate_request_context + ) + emitter.register( + 'before-endpoint-resolution.s3', self.redirect_from_cache + ) + + def redirect_from_error(self, request_dict, response, operation, **kwargs): + """ + An S3 request sent to the wrong region will return an error that + contains the endpoint the request should be sent to. This handler + will add the redirect information to the signing context and then + redirect the request. + """ + if response is None: + # This could be none if there was a ConnectionError or other + # transport error. + return + + redirect_ctx = request_dict.get('context', {}).get('s3_redirect', {}) + if ArnParser.is_arn(redirect_ctx.get('bucket')): + logger.debug( + 'S3 request was previously for an Accesspoint ARN, not ' + 'redirecting.' + ) + return + + if redirect_ctx.get('redirected'): + logger.debug( + 'S3 request was previously redirected, not redirecting.' + ) + return + + error = response[1].get('Error', {}) + error_code = error.get('Code') + response_metadata = response[1].get('ResponseMetadata', {}) + + # We have to account for 400 responses because + # if we sign a Head* request with the wrong region, + # we'll get a 400 Bad Request but we won't get a + # body saying it's an "AuthorizationHeaderMalformed". + is_special_head_object = ( + error_code in ('301', '400') and operation.name == 'HeadObject' + ) + is_special_head_bucket = ( + error_code in ('301', '400') + and operation.name == 'HeadBucket' + and 'x-amz-bucket-region' + in response_metadata.get('HTTPHeaders', {}) + ) + is_wrong_signing_region = ( + error_code == 'AuthorizationHeaderMalformed' and 'Region' in error + ) + is_redirect_status = response[0] is not None and response[ + 0 + ].status_code in (301, 302, 307) + is_permanent_redirect = error_code == 'PermanentRedirect' + is_opt_in_region_redirect = ( + error_code == 'IllegalLocationConstraintException' + and operation.name != 'CreateBucket' + ) + if not any( + [ + is_special_head_object, + is_wrong_signing_region, + is_permanent_redirect, + is_special_head_bucket, + is_redirect_status, + is_opt_in_region_redirect, + ] + ): + return + + bucket = request_dict['context']['s3_redirect']['bucket'] + client_region = request_dict['context'].get('client_region') + new_region = self.get_bucket_region(bucket, response) + + if new_region is None: + logger.debug( + "S3 client configured for region %s but the " + "bucket %s is not in that region and the proper region " + "could not be automatically determined.", + client_region, + bucket, + ) + return + + logger.debug( + "S3 client configured for region %s but the bucket %s " + "is in region %s; Please configure the proper region to " + "avoid multiple unnecessary redirects and signing attempts.", + client_region, + bucket, + new_region, + ) + register_feature_id('S3_REGION_REDIRECT') + + # Adding the new region to _cache will make construct_endpoint() to + # use the new region as value for the AWS::Region builtin parameter. + self._cache[bucket] = new_region + + # Re-resolve endpoint with new region and modify request_dict with + # the new URL, auth scheme, and signing context. + ep_resolver = self._client._ruleset_resolver + ep_info = ep_resolver.construct_endpoint( + operation_model=operation, + call_args=request_dict['context']['s3_redirect']['params'], + request_context=request_dict['context'], + ) + request_dict['url'] = self.set_request_url( + request_dict['url'], ep_info.url + ) + request_dict['context']['s3_redirect']['redirected'] = True + auth_schemes = ep_info.properties.get('authSchemes') + if auth_schemes is not None: + auth_info = ep_resolver.auth_schemes_to_signing_ctx(auth_schemes) + auth_type, signing_context = auth_info + request_dict['context']['auth_type'] = auth_type + request_dict['context']['signing'] = { + **request_dict['context'].get('signing', {}), + **signing_context, + } + + # Return 0 so it doesn't wait to retry + return 0 + + def get_bucket_region(self, bucket, response): + """ + There are multiple potential sources for the new region to redirect to, + but they aren't all universally available for use. This will try to + find region from response elements, but will fall back to calling + HEAD on the bucket if all else fails. + + :param bucket: The bucket to find the region for. This is necessary if + the region is not available in the error response. + :param response: A response representing a service request that failed + due to incorrect region configuration. + """ + # First try to source the region from the headers. + service_response = response[1] + response_headers = service_response['ResponseMetadata']['HTTPHeaders'] + if 'x-amz-bucket-region' in response_headers: + region = response_headers['x-amz-bucket-region'] + # Next, check the error body + elif r := service_response.get('Error', {}).get('Region', None): + region = r + else: + # Finally, HEAD the bucket. No other choice sadly. + try: + response = self._client.head_bucket(Bucket=bucket) + headers = response['ResponseMetadata']['HTTPHeaders'] + except ClientError as e: + headers = e.response['ResponseMetadata']['HTTPHeaders'] + region = headers.get('x-amz-bucket-region', None) + validate_region_name(region) + return region + + def set_request_url(self, old_url, new_endpoint, **kwargs): + """ + Splice a new endpoint into an existing URL. Note that some endpoints + from the the endpoint provider have a path component which will be + discarded by this function. + """ + return _get_new_endpoint(old_url, new_endpoint, False) + + def redirect_from_cache(self, builtins, params, **kwargs): + """ + If a bucket name has been redirected before, it is in the cache. This + handler will update the AWS::Region endpoint resolver builtin param + to use the region from cache instead of the client region to avoid the + redirect. + """ + bucket = params.get('Bucket') + if bucket is not None and bucket in self._cache: + new_region = self._cache.get(bucket) + builtins['AWS::Region'] = new_region + register_feature_id('S3_REGION_REDIRECT') + + def annotate_request_context(self, params, context, **kwargs): + """Store the bucket name in context for later use when redirecting. + The bucket name may be an access point ARN or alias. + """ + bucket = params.get('Bucket') + context['s3_redirect'] = { + 'redirected': False, + 'bucket': bucket, + 'params': params, + } + + +class S3RegionRedirector: + """This handler has been replaced by S3RegionRedirectorv2. The original + version remains in place for any third-party libraries that import it. + """ + + def __init__(self, endpoint_bridge, client, cache=None): + self._endpoint_resolver = endpoint_bridge + self._cache = cache + if self._cache is None: + self._cache = {} + + # This needs to be a weak ref in order to prevent memory leaks on + # python 2.6 + self._client = weakref.proxy(client) + + warnings.warn( + 'The S3RegionRedirector class has been deprecated for a new ' + 'internal replacement. A future version of botocore may remove ' + 'this class.', + category=FutureWarning, + ) + + def register(self, event_emitter=None): + emitter = event_emitter or self._client.meta.events + emitter.register('needs-retry.s3', self.redirect_from_error) + emitter.register('before-call.s3', self.set_request_url) + emitter.register('before-parameter-build.s3', self.redirect_from_cache) + + def redirect_from_error(self, request_dict, response, operation, **kwargs): + """ + An S3 request sent to the wrong region will return an error that + contains the endpoint the request should be sent to. This handler + will add the redirect information to the signing context and then + redirect the request. + """ + if response is None: + # This could be none if there was a ConnectionError or other + # transport error. + return + + if self._is_s3_accesspoint(request_dict.get('context', {})): + logger.debug( + 'S3 request was previously to an accesspoint, not redirecting.' + ) + return + + if request_dict.get('context', {}).get('s3_redirected'): + logger.debug( + 'S3 request was previously redirected, not redirecting.' + ) + return + + error = response[1].get('Error', {}) + error_code = error.get('Code') + response_metadata = response[1].get('ResponseMetadata', {}) + + # We have to account for 400 responses because + # if we sign a Head* request with the wrong region, + # we'll get a 400 Bad Request but we won't get a + # body saying it's an "AuthorizationHeaderMalformed". + is_special_head_object = ( + error_code in ('301', '400') and operation.name == 'HeadObject' + ) + is_special_head_bucket = ( + error_code in ('301', '400') + and operation.name == 'HeadBucket' + and 'x-amz-bucket-region' + in response_metadata.get('HTTPHeaders', {}) + ) + is_wrong_signing_region = ( + error_code == 'AuthorizationHeaderMalformed' and 'Region' in error + ) + is_redirect_status = response[0] is not None and response[ + 0 + ].status_code in (301, 302, 307) + is_permanent_redirect = error_code == 'PermanentRedirect' + if not any( + [ + is_special_head_object, + is_wrong_signing_region, + is_permanent_redirect, + is_special_head_bucket, + is_redirect_status, + ] + ): + return + + bucket = request_dict['context']['signing']['bucket'] + client_region = request_dict['context'].get('client_region') + new_region = self.get_bucket_region(bucket, response) + + if new_region is None: + logger.debug( + "S3 client configured for region %s but the bucket %s is not " + "in that region and the proper region could not be " + "automatically determined.", + client_region, + bucket, + ) + return + + logger.debug( + "S3 client configured for region %s but the bucket %s is in region" + " %s; Please configure the proper region to avoid multiple " + "unnecessary redirects and signing attempts.", + client_region, + bucket, + new_region, + ) + endpoint = self._endpoint_resolver.resolve('s3', new_region) + endpoint = endpoint['endpoint_url'] + + signing_context = { + 'region': new_region, + 'bucket': bucket, + 'endpoint': endpoint, + } + request_dict['context']['signing'] = signing_context + + self._cache[bucket] = signing_context + self.set_request_url(request_dict, request_dict['context']) + + request_dict['context']['s3_redirected'] = True + + # Return 0 so it doesn't wait to retry + return 0 + + def get_bucket_region(self, bucket, response): + """ + There are multiple potential sources for the new region to redirect to, + but they aren't all universally available for use. This will try to + find region from response elements, but will fall back to calling + HEAD on the bucket if all else fails. + + :param bucket: The bucket to find the region for. This is necessary if + the region is not available in the error response. + :param response: A response representing a service request that failed + due to incorrect region configuration. + """ + # First try to source the region from the headers. + service_response = response[1] + response_headers = service_response['ResponseMetadata']['HTTPHeaders'] + if 'x-amz-bucket-region' in response_headers: + return response_headers['x-amz-bucket-region'] + + # Next, check the error body + region = service_response.get('Error', {}).get('Region', None) + if region is not None: + return region + + # Finally, HEAD the bucket. No other choice sadly. + try: + response = self._client.head_bucket(Bucket=bucket) + headers = response['ResponseMetadata']['HTTPHeaders'] + except ClientError as e: + headers = e.response['ResponseMetadata']['HTTPHeaders'] + + region = headers.get('x-amz-bucket-region', None) + return region + + def set_request_url(self, params, context, **kwargs): + endpoint = context.get('signing', {}).get('endpoint', None) + if endpoint is not None: + params['url'] = _get_new_endpoint(params['url'], endpoint, False) + + def redirect_from_cache(self, params, context, **kwargs): + """ + This handler retrieves a given bucket's signing context from the cache + and adds it into the request context. + """ + if self._is_s3_accesspoint(context): + return + bucket = params.get('Bucket') + signing_context = self._cache.get(bucket) + if signing_context is not None: + context['signing'] = signing_context + else: + context['signing'] = {'bucket': bucket} + + def _is_s3_accesspoint(self, context): + return 's3_accesspoint' in context + + +class InvalidArnException(ValueError): + pass + + +class ArnParser: + def parse_arn(self, arn): + arn_parts = arn.split(':', 5) + if len(arn_parts) < 6: + raise InvalidArnException( + f'Provided ARN: {arn} must be of the format: ' + 'arn:partition:service:region:account:resource' + ) + return { + 'partition': arn_parts[1], + 'service': arn_parts[2], + 'region': arn_parts[3], + 'account': arn_parts[4], + 'resource': arn_parts[5], + } + + @staticmethod + def is_arn(value): + if not isinstance(value, str) or not value.startswith('arn:'): + return False + arn_parser = ArnParser() + try: + arn_parser.parse_arn(value) + return True + except InvalidArnException: + return False + + +class S3ArnParamHandler: + _RESOURCE_REGEX = re.compile( + r'^(?Paccesspoint|outpost)[/:](?P.+)$' + ) + _OUTPOST_RESOURCE_REGEX = re.compile( + r'^(?P[a-zA-Z0-9\-]{1,63})[/:]accesspoint[/:]' + r'(?P[a-zA-Z0-9\-]{1,63}$)' + ) + _BLACKLISTED_OPERATIONS = ['CreateBucket'] + + def __init__(self, arn_parser=None): + self._arn_parser = arn_parser + if arn_parser is None: + self._arn_parser = ArnParser() + + def register(self, event_emitter): + event_emitter.register('before-parameter-build.s3', self.handle_arn) + + def handle_arn(self, params, model, context, **kwargs): + if model.name in self._BLACKLISTED_OPERATIONS: + return + arn_details = self._get_arn_details_from_bucket_param(params) + if arn_details is None: + return + if arn_details['resource_type'] == 'accesspoint': + self._store_accesspoint(params, context, arn_details) + elif arn_details['resource_type'] == 'outpost': + self._store_outpost(params, context, arn_details) + + def _get_arn_details_from_bucket_param(self, params): + if 'Bucket' in params: + try: + arn = params['Bucket'] + arn_details = self._arn_parser.parse_arn(arn) + self._add_resource_type_and_name(arn, arn_details) + return arn_details + except InvalidArnException: + pass + return None + + def _add_resource_type_and_name(self, arn, arn_details): + match = self._RESOURCE_REGEX.match(arn_details['resource']) + if match: + arn_details['resource_type'] = match.group('resource_type') + arn_details['resource_name'] = match.group('resource_name') + else: + raise UnsupportedS3ArnError(arn=arn) + + def _store_accesspoint(self, params, context, arn_details): + # Ideally the access-point would be stored as a parameter in the + # request where the serializer would then know how to serialize it, + # but access-points are not modeled in S3 operations so it would fail + # validation. Instead, we set the access-point to the bucket parameter + # to have some value set when serializing the request and additional + # information on the context from the arn to use in forming the + # access-point endpoint. + params['Bucket'] = arn_details['resource_name'] + context['s3_accesspoint'] = { + 'name': arn_details['resource_name'], + 'account': arn_details['account'], + 'partition': arn_details['partition'], + 'region': arn_details['region'], + 'service': arn_details['service'], + } + + def _store_outpost(self, params, context, arn_details): + resource_name = arn_details['resource_name'] + match = self._OUTPOST_RESOURCE_REGEX.match(resource_name) + if not match: + raise UnsupportedOutpostResourceError(resource_name=resource_name) + # Because we need to set the bucket name to something to pass + # validation we're going to use the access point name to be consistent + # with normal access point arns. + accesspoint_name = match.group('accesspoint_name') + params['Bucket'] = accesspoint_name + context['s3_accesspoint'] = { + 'outpost_name': match.group('outpost_name'), + 'name': accesspoint_name, + 'account': arn_details['account'], + 'partition': arn_details['partition'], + 'region': arn_details['region'], + 'service': arn_details['service'], + } + + +class S3EndpointSetter: + _DEFAULT_PARTITION = 'aws' + _DEFAULT_DNS_SUFFIX = 'amazonaws.com' + + def __init__( + self, + endpoint_resolver, + region=None, + s3_config=None, + endpoint_url=None, + partition=None, + use_fips_endpoint=False, + ): + # This is calling the endpoint_resolver in regions.py + self._endpoint_resolver = endpoint_resolver + self._region = region + self._s3_config = s3_config + self._use_fips_endpoint = use_fips_endpoint + if s3_config is None: + self._s3_config = {} + self._endpoint_url = endpoint_url + self._partition = partition + if partition is None: + self._partition = self._DEFAULT_PARTITION + + def register(self, event_emitter): + event_emitter.register('before-sign.s3', self.set_endpoint) + event_emitter.register('choose-signer.s3', self.set_signer) + event_emitter.register( + 'before-call.s3.WriteGetObjectResponse', + self.update_endpoint_to_s3_object_lambda, + ) + + def update_endpoint_to_s3_object_lambda(self, params, context, **kwargs): + if self._use_accelerate_endpoint: + raise UnsupportedS3ConfigurationError( + msg='S3 client does not support accelerate endpoints for S3 Object Lambda operations', + ) + + self._override_signing_name(context, 's3-object-lambda') + if self._endpoint_url: + # Only update the url if an explicit url was not provided + return + + resolver = self._endpoint_resolver + # Constructing endpoints as s3-object-lambda as region + resolved = resolver.construct_endpoint( + 's3-object-lambda', self._region + ) + + # Ideally we would be able to replace the endpoint before + # serialization but there's no event to do that currently + # host_prefix is all the arn/bucket specs + new_endpoint = 'https://{host_prefix}{hostname}'.format( + host_prefix=params['host_prefix'], + hostname=resolved['hostname'], + ) + + params['url'] = _get_new_endpoint(params['url'], new_endpoint, False) + + def set_endpoint(self, request, **kwargs): + if self._use_accesspoint_endpoint(request): + self._validate_accesspoint_supported(request) + self._validate_fips_supported(request) + self._validate_global_regions(request) + region_name = self._resolve_region_for_accesspoint_endpoint( + request + ) + self._resolve_signing_name_for_accesspoint_endpoint(request) + self._switch_to_accesspoint_endpoint(request, region_name) + return + if self._use_accelerate_endpoint: + if self._use_fips_endpoint: + raise UnsupportedS3ConfigurationError( + msg=( + 'Client is configured to use the FIPS psuedo region ' + f'for "{self._region}", but S3 Accelerate does not have any FIPS ' + 'compatible endpoints.' + ) + ) + switch_host_s3_accelerate(request=request, **kwargs) + if self._s3_addressing_handler: + self._s3_addressing_handler(request=request, **kwargs) + + def _use_accesspoint_endpoint(self, request): + return 's3_accesspoint' in request.context + + def _validate_fips_supported(self, request): + if not self._use_fips_endpoint: + return + if 'fips' in request.context['s3_accesspoint']['region']: + raise UnsupportedS3AccesspointConfigurationError( + msg={'Invalid ARN, FIPS region not allowed in ARN.'} + ) + if 'outpost_name' in request.context['s3_accesspoint']: + raise UnsupportedS3AccesspointConfigurationError( + msg=( + f'Client is configured to use the FIPS psuedo-region "{self._region}", ' + 'but outpost ARNs do not support FIPS endpoints.' + ) + ) + # Transforming psuedo region to actual region + accesspoint_region = request.context['s3_accesspoint']['region'] + if accesspoint_region != self._region: + if not self._s3_config.get('use_arn_region', True): + # TODO: Update message to reflect use_arn_region + # is not set + raise UnsupportedS3AccesspointConfigurationError( + msg=( + 'Client is configured to use the FIPS psuedo-region ' + f'for "{self._region}", but the access-point ARN provided is for ' + f'the "{accesspoint_region}" region. For clients using a FIPS ' + 'psuedo-region calls to access-point ARNs in another ' + 'region are not allowed.' + ) + ) + + def _validate_global_regions(self, request): + if self._s3_config.get('use_arn_region', True): + return + if self._region in ['aws-global', 's3-external-1']: + raise UnsupportedS3AccesspointConfigurationError( + msg=( + 'Client is configured to use the global psuedo-region ' + f'"{self._region}". When providing access-point ARNs a regional ' + 'endpoint must be specified.' + ) + ) + + def _validate_accesspoint_supported(self, request): + if self._use_accelerate_endpoint: + raise UnsupportedS3AccesspointConfigurationError( + msg=( + 'Client does not support s3 accelerate configuration ' + 'when an access-point ARN is specified.' + ) + ) + request_partition = request.context['s3_accesspoint']['partition'] + if request_partition != self._partition: + raise UnsupportedS3AccesspointConfigurationError( + msg=( + f'Client is configured for "{self._partition}" partition, but access-point' + f' ARN provided is for "{request_partition}" partition. The client and ' + ' access-point partition must be the same.' + ) + ) + s3_service = request.context['s3_accesspoint'].get('service') + if s3_service == 's3-object-lambda' and self._s3_config.get( + 'use_dualstack_endpoint' + ): + raise UnsupportedS3AccesspointConfigurationError( + msg=( + 'Client does not support s3 dualstack configuration ' + 'when an S3 Object Lambda access point ARN is specified.' + ) + ) + outpost_name = request.context['s3_accesspoint'].get('outpost_name') + if outpost_name and self._s3_config.get('use_dualstack_endpoint'): + raise UnsupportedS3AccesspointConfigurationError( + msg=( + 'Client does not support s3 dualstack configuration ' + 'when an outpost ARN is specified.' + ) + ) + self._validate_mrap_s3_config(request) + + def _validate_mrap_s3_config(self, request): + if not is_global_accesspoint(request.context): + return + if self._s3_config.get('s3_disable_multiregion_access_points'): + raise UnsupportedS3AccesspointConfigurationError( + msg=( + 'Invalid configuration, Multi-Region Access Point ' + 'ARNs are disabled.' + ) + ) + elif self._s3_config.get('use_dualstack_endpoint'): + raise UnsupportedS3AccesspointConfigurationError( + msg=( + 'Client does not support s3 dualstack configuration ' + 'when a Multi-Region Access Point ARN is specified.' + ) + ) + + def _resolve_region_for_accesspoint_endpoint(self, request): + if is_global_accesspoint(request.context): + # Requests going to MRAP endpoints MUST be set to any (*) region. + self._override_signing_region(request, '*') + elif self._s3_config.get('use_arn_region', True): + accesspoint_region = request.context['s3_accesspoint']['region'] + # If we are using the region from the access point, + # we will also want to make sure that we set it as the + # signing region as well + self._override_signing_region(request, accesspoint_region) + return accesspoint_region + return self._region + + def set_signer(self, context, **kwargs): + if is_global_accesspoint(context): + if HAS_CRT: + return 's3v4a' + else: + raise MissingDependencyException( + msg="Using S3 with an MRAP arn requires an additional " + "dependency. You will need to pip install " + "botocore[crt] before proceeding." + ) + + def _resolve_signing_name_for_accesspoint_endpoint(self, request): + accesspoint_service = request.context['s3_accesspoint']['service'] + self._override_signing_name(request.context, accesspoint_service) + + def _switch_to_accesspoint_endpoint(self, request, region_name): + original_components = urlsplit(request.url) + accesspoint_endpoint = urlunsplit( + ( + original_components.scheme, + self._get_netloc(request.context, region_name), + self._get_accesspoint_path( + original_components.path, request.context + ), + original_components.query, + '', + ) + ) + logger.debug( + 'Updating URI from %s to %s', request.url, accesspoint_endpoint + ) + request.url = accesspoint_endpoint + + def _get_netloc(self, request_context, region_name): + if is_global_accesspoint(request_context): + return self._get_mrap_netloc(request_context) + else: + return self._get_accesspoint_netloc(request_context, region_name) + + def _get_mrap_netloc(self, request_context): + s3_accesspoint = request_context['s3_accesspoint'] + region_name = 's3-global' + mrap_netloc_components = [s3_accesspoint['name']] + if self._endpoint_url: + endpoint_url_netloc = urlsplit(self._endpoint_url).netloc + mrap_netloc_components.append(endpoint_url_netloc) + else: + partition = s3_accesspoint['partition'] + mrap_netloc_components.extend( + [ + 'accesspoint', + region_name, + self._get_partition_dns_suffix(partition), + ] + ) + return '.'.join(mrap_netloc_components) + + def _get_accesspoint_netloc(self, request_context, region_name): + s3_accesspoint = request_context['s3_accesspoint'] + accesspoint_netloc_components = [ + '{}-{}'.format(s3_accesspoint['name'], s3_accesspoint['account']), + ] + outpost_name = s3_accesspoint.get('outpost_name') + if self._endpoint_url: + if outpost_name: + accesspoint_netloc_components.append(outpost_name) + endpoint_url_netloc = urlsplit(self._endpoint_url).netloc + accesspoint_netloc_components.append(endpoint_url_netloc) + else: + if outpost_name: + outpost_host = [outpost_name, 's3-outposts'] + accesspoint_netloc_components.extend(outpost_host) + elif s3_accesspoint['service'] == 's3-object-lambda': + component = self._inject_fips_if_needed( + 's3-object-lambda', request_context + ) + accesspoint_netloc_components.append(component) + else: + component = self._inject_fips_if_needed( + 's3-accesspoint', request_context + ) + accesspoint_netloc_components.append(component) + if self._s3_config.get('use_dualstack_endpoint'): + accesspoint_netloc_components.append('dualstack') + accesspoint_netloc_components.extend( + [region_name, self._get_dns_suffix(region_name)] + ) + return '.'.join(accesspoint_netloc_components) + + def _inject_fips_if_needed(self, component, request_context): + if self._use_fips_endpoint: + return f'{component}-fips' + return component + + def _get_accesspoint_path(self, original_path, request_context): + # The Bucket parameter was substituted with the access-point name as + # some value was required in serializing the bucket name. Now that + # we are making the request directly to the access point, we will + # want to remove that access-point name from the path. + name = request_context['s3_accesspoint']['name'] + # All S3 operations require at least a / in their path. + return original_path.replace('/' + name, '', 1) or '/' + + def _get_partition_dns_suffix(self, partition_name): + dns_suffix = self._endpoint_resolver.get_partition_dns_suffix( + partition_name + ) + if dns_suffix is None: + dns_suffix = self._DEFAULT_DNS_SUFFIX + return dns_suffix + + def _get_dns_suffix(self, region_name): + resolved = self._endpoint_resolver.construct_endpoint( + 's3', region_name + ) + dns_suffix = self._DEFAULT_DNS_SUFFIX + if resolved and 'dnsSuffix' in resolved: + dns_suffix = resolved['dnsSuffix'] + return dns_suffix + + def _override_signing_region(self, request, region_name): + signing_context = request.context.get('signing', {}) + # S3SigV4Auth will use the context['signing']['region'] value to + # sign with if present. This is used by the Bucket redirector + # as well but we should be fine because the redirector is never + # used in combination with the accesspoint setting logic. + signing_context['region'] = region_name + request.context['signing'] = signing_context + + def _override_signing_name(self, context, signing_name): + signing_context = context.get('signing', {}) + # S3SigV4Auth will use the context['signing']['signing_name'] value to + # sign with if present. This is used by the Bucket redirector + # as well but we should be fine because the redirector is never + # used in combination with the accesspoint setting logic. + signing_context['signing_name'] = signing_name + context['signing'] = signing_context + + @CachedProperty + def _use_accelerate_endpoint(self): + # Enable accelerate if the configuration is set to to true or the + # endpoint being used matches one of the accelerate endpoints. + + # Accelerate has been explicitly configured. + if self._s3_config.get('use_accelerate_endpoint'): + return True + + # Accelerate mode is turned on automatically if an endpoint url is + # provided that matches the accelerate scheme. + if self._endpoint_url is None: + return False + + # Accelerate is only valid for Amazon endpoints. + netloc = urlsplit(self._endpoint_url).netloc + if not netloc.endswith('amazonaws.com'): + return False + + # The first part of the url should always be s3-accelerate. + parts = netloc.split('.') + if parts[0] != 's3-accelerate': + return False + + # Url parts between 's3-accelerate' and 'amazonaws.com' which + # represent different url features. + feature_parts = parts[1:-2] + + # There should be no duplicate url parts. + if len(feature_parts) != len(set(feature_parts)): + return False + + # Remaining parts must all be in the whitelist. + return all(p in S3_ACCELERATE_WHITELIST for p in feature_parts) + + @CachedProperty + def _addressing_style(self): + # Use virtual host style addressing if accelerate is enabled or if + # the given endpoint url is an accelerate endpoint. + if self._use_accelerate_endpoint: + return 'virtual' + + # If a particular addressing style is configured, use it. + configured_addressing_style = self._s3_config.get('addressing_style') + if configured_addressing_style: + return configured_addressing_style + + @CachedProperty + def _s3_addressing_handler(self): + # If virtual host style was configured, use it regardless of whether + # or not the bucket looks dns compatible. + if self._addressing_style == 'virtual': + logger.debug("Using S3 virtual host style addressing.") + return switch_to_virtual_host_style + + # If path style is configured, no additional steps are needed. If + # endpoint_url was specified, don't default to virtual. We could + # potentially default provided endpoint urls to virtual hosted + # style, but for now it is avoided. + if self._addressing_style == 'path' or self._endpoint_url is not None: + logger.debug("Using S3 path style addressing.") + return None + + logger.debug( + "Defaulting to S3 virtual host style addressing with " + "path style addressing fallback." + ) + + # By default, try to use virtual style with path fallback. + return fix_s3_host + + +class S3ControlEndpointSetter: + _DEFAULT_PARTITION = 'aws' + _DEFAULT_DNS_SUFFIX = 'amazonaws.com' + _HOST_LABEL_REGEX = re.compile(r'^[a-zA-Z0-9\-]{1,63}$') + + def __init__( + self, + endpoint_resolver, + region=None, + s3_config=None, + endpoint_url=None, + partition=None, + use_fips_endpoint=False, + ): + self._endpoint_resolver = endpoint_resolver + self._region = region + self._s3_config = s3_config + self._use_fips_endpoint = use_fips_endpoint + if s3_config is None: + self._s3_config = {} + self._endpoint_url = endpoint_url + self._partition = partition + if partition is None: + self._partition = self._DEFAULT_PARTITION + + def register(self, event_emitter): + event_emitter.register('before-sign.s3-control', self.set_endpoint) + + def set_endpoint(self, request, **kwargs): + if self._use_endpoint_from_arn_details(request): + self._validate_endpoint_from_arn_details_supported(request) + region_name = self._resolve_region_from_arn_details(request) + self._resolve_signing_name_from_arn_details(request) + self._resolve_endpoint_from_arn_details(request, region_name) + self._add_headers_from_arn_details(request) + elif self._use_endpoint_from_outpost_id(request): + self._validate_outpost_redirection_valid(request) + self._override_signing_name(request, 's3-outposts') + new_netloc = self._construct_outpost_endpoint(self._region) + self._update_request_netloc(request, new_netloc) + + def _use_endpoint_from_arn_details(self, request): + return 'arn_details' in request.context + + def _use_endpoint_from_outpost_id(self, request): + return 'outpost_id' in request.context + + def _validate_endpoint_from_arn_details_supported(self, request): + if 'fips' in request.context['arn_details']['region']: + raise UnsupportedS3ControlArnError( + arn=request.context['arn_details']['original'], + msg='Invalid ARN, FIPS region not allowed in ARN.', + ) + if not self._s3_config.get('use_arn_region', False): + arn_region = request.context['arn_details']['region'] + if arn_region != self._region: + error_msg = ( + 'The use_arn_region configuration is disabled but ' + f'received arn for "{arn_region}" when the client is configured ' + f'to use "{self._region}"' + ) + raise UnsupportedS3ControlConfigurationError(msg=error_msg) + request_partion = request.context['arn_details']['partition'] + if request_partion != self._partition: + raise UnsupportedS3ControlConfigurationError( + msg=( + f'Client is configured for "{self._partition}" partition, but arn ' + f'provided is for "{request_partion}" partition. The client and ' + 'arn partition must be the same.' + ) + ) + if self._s3_config.get('use_accelerate_endpoint'): + raise UnsupportedS3ControlConfigurationError( + msg='S3 control client does not support accelerate endpoints', + ) + if 'outpost_name' in request.context['arn_details']: + self._validate_outpost_redirection_valid(request) + + def _validate_outpost_redirection_valid(self, request): + if self._s3_config.get('use_dualstack_endpoint'): + raise UnsupportedS3ControlConfigurationError( + msg=( + 'Client does not support s3 dualstack configuration ' + 'when an outpost is specified.' + ) + ) + + def _resolve_region_from_arn_details(self, request): + if self._s3_config.get('use_arn_region', False): + arn_region = request.context['arn_details']['region'] + # If we are using the region from the expanded arn, we will also + # want to make sure that we set it as the signing region as well + self._override_signing_region(request, arn_region) + return arn_region + return self._region + + def _resolve_signing_name_from_arn_details(self, request): + arn_service = request.context['arn_details']['service'] + self._override_signing_name(request, arn_service) + return arn_service + + def _resolve_endpoint_from_arn_details(self, request, region_name): + new_netloc = self._resolve_netloc_from_arn_details( + request, region_name + ) + self._update_request_netloc(request, new_netloc) + + def _update_request_netloc(self, request, new_netloc): + original_components = urlsplit(request.url) + arn_details_endpoint = urlunsplit( + ( + original_components.scheme, + new_netloc, + original_components.path, + original_components.query, + '', + ) + ) + logger.debug( + 'Updating URI from %s to %s', request.url, arn_details_endpoint + ) + request.url = arn_details_endpoint + + def _resolve_netloc_from_arn_details(self, request, region_name): + arn_details = request.context['arn_details'] + if 'outpost_name' in arn_details: + return self._construct_outpost_endpoint(region_name) + account = arn_details['account'] + return self._construct_s3_control_endpoint(region_name, account) + + def _is_valid_host_label(self, label): + return self._HOST_LABEL_REGEX.match(label) + + def _validate_host_labels(self, *labels): + for label in labels: + if not self._is_valid_host_label(label): + raise InvalidHostLabelError(label=label) + + def _construct_s3_control_endpoint(self, region_name, account): + self._validate_host_labels(region_name, account) + if self._endpoint_url: + endpoint_url_netloc = urlsplit(self._endpoint_url).netloc + netloc = [account, endpoint_url_netloc] + else: + netloc = [ + account, + 's3-control', + ] + self._add_dualstack(netloc) + dns_suffix = self._get_dns_suffix(region_name) + netloc.extend([region_name, dns_suffix]) + return self._construct_netloc(netloc) + + def _construct_outpost_endpoint(self, region_name): + self._validate_host_labels(region_name) + if self._endpoint_url: + return urlsplit(self._endpoint_url).netloc + else: + netloc = [ + 's3-outposts', + region_name, + self._get_dns_suffix(region_name), + ] + self._add_fips(netloc) + return self._construct_netloc(netloc) + + def _construct_netloc(self, netloc): + return '.'.join(netloc) + + def _add_fips(self, netloc): + if self._use_fips_endpoint: + netloc[0] = netloc[0] + '-fips' + + def _add_dualstack(self, netloc): + if self._s3_config.get('use_dualstack_endpoint'): + netloc.append('dualstack') + + def _get_dns_suffix(self, region_name): + resolved = self._endpoint_resolver.construct_endpoint( + 's3', region_name + ) + dns_suffix = self._DEFAULT_DNS_SUFFIX + if resolved and 'dnsSuffix' in resolved: + dns_suffix = resolved['dnsSuffix'] + return dns_suffix + + def _override_signing_region(self, request, region_name): + signing_context = request.context.get('signing', {}) + # S3SigV4Auth will use the context['signing']['region'] value to + # sign with if present. This is used by the Bucket redirector + # as well but we should be fine because the redirector is never + # used in combination with the accesspoint setting logic. + signing_context['region'] = region_name + request.context['signing'] = signing_context + + def _override_signing_name(self, request, signing_name): + signing_context = request.context.get('signing', {}) + # S3SigV4Auth will use the context['signing']['signing_name'] value to + # sign with if present. This is used by the Bucket redirector + # as well but we should be fine because the redirector is never + # used in combination with the accesspoint setting logic. + signing_context['signing_name'] = signing_name + request.context['signing'] = signing_context + + def _add_headers_from_arn_details(self, request): + arn_details = request.context['arn_details'] + outpost_name = arn_details.get('outpost_name') + if outpost_name: + self._add_outpost_id_header(request, outpost_name) + + def _add_outpost_id_header(self, request, outpost_name): + request.headers['x-amz-outpost-id'] = outpost_name + + +class S3ControlArnParamHandler: + """This handler has been replaced by S3ControlArnParamHandlerv2. The + original version remains in place for any third-party importers. + """ + + _RESOURCE_SPLIT_REGEX = re.compile(r'[/:]') + + def __init__(self, arn_parser=None): + self._arn_parser = arn_parser + if arn_parser is None: + self._arn_parser = ArnParser() + warnings.warn( + 'The S3ControlArnParamHandler class has been deprecated for a new ' + 'internal replacement. A future version of botocore may remove ' + 'this class.', + category=FutureWarning, + ) + + def register(self, event_emitter): + event_emitter.register( + 'before-parameter-build.s3-control', + self.handle_arn, + ) + + def handle_arn(self, params, model, context, **kwargs): + if model.name in ('CreateBucket', 'ListRegionalBuckets'): + # CreateBucket and ListRegionalBuckets are special cases that do + # not obey ARN based redirection but will redirect based off of the + # presence of the OutpostId parameter + self._handle_outpost_id_param(params, model, context) + else: + self._handle_name_param(params, model, context) + self._handle_bucket_param(params, model, context) + + def _get_arn_details_from_param(self, params, param_name): + if param_name not in params: + return None + try: + arn = params[param_name] + arn_details = self._arn_parser.parse_arn(arn) + arn_details['original'] = arn + arn_details['resources'] = self._split_resource(arn_details) + return arn_details + except InvalidArnException: + return None + + def _split_resource(self, arn_details): + return self._RESOURCE_SPLIT_REGEX.split(arn_details['resource']) + + def _override_account_id_param(self, params, arn_details): + account_id = arn_details['account'] + if 'AccountId' in params and params['AccountId'] != account_id: + error_msg = ( + 'Account ID in arn does not match the AccountId parameter ' + 'provided: "{}"' + ).format(params['AccountId']) + raise UnsupportedS3ControlArnError( + arn=arn_details['original'], + msg=error_msg, + ) + params['AccountId'] = account_id + + def _handle_outpost_id_param(self, params, model, context): + if 'OutpostId' not in params: + return + context['outpost_id'] = params['OutpostId'] + + def _handle_name_param(self, params, model, context): + # CreateAccessPoint is a special case that does not expand Name + if model.name == 'CreateAccessPoint': + return + arn_details = self._get_arn_details_from_param(params, 'Name') + if arn_details is None: + return + if self._is_outpost_accesspoint(arn_details): + self._store_outpost_accesspoint(params, context, arn_details) + else: + error_msg = 'The Name parameter does not support the provided ARN' + raise UnsupportedS3ControlArnError( + arn=arn_details['original'], + msg=error_msg, + ) + + def _is_outpost_accesspoint(self, arn_details): + if arn_details['service'] != 's3-outposts': + return False + resources = arn_details['resources'] + if len(resources) != 4: + return False + # Resource must be of the form outpost/op-123/accesspoint/name + return resources[0] == 'outpost' and resources[2] == 'accesspoint' + + def _store_outpost_accesspoint(self, params, context, arn_details): + self._override_account_id_param(params, arn_details) + accesspoint_name = arn_details['resources'][3] + params['Name'] = accesspoint_name + arn_details['accesspoint_name'] = accesspoint_name + arn_details['outpost_name'] = arn_details['resources'][1] + context['arn_details'] = arn_details + + def _handle_bucket_param(self, params, model, context): + arn_details = self._get_arn_details_from_param(params, 'Bucket') + if arn_details is None: + return + if self._is_outpost_bucket(arn_details): + self._store_outpost_bucket(params, context, arn_details) + else: + error_msg = ( + 'The Bucket parameter does not support the provided ARN' + ) + raise UnsupportedS3ControlArnError( + arn=arn_details['original'], + msg=error_msg, + ) + + def _is_outpost_bucket(self, arn_details): + if arn_details['service'] != 's3-outposts': + return False + resources = arn_details['resources'] + if len(resources) != 4: + return False + # Resource must be of the form outpost/op-123/bucket/name + return resources[0] == 'outpost' and resources[2] == 'bucket' + + def _store_outpost_bucket(self, params, context, arn_details): + self._override_account_id_param(params, arn_details) + bucket_name = arn_details['resources'][3] + params['Bucket'] = bucket_name + arn_details['bucket_name'] = bucket_name + arn_details['outpost_name'] = arn_details['resources'][1] + context['arn_details'] = arn_details + + +class S3ControlArnParamHandlerv2(S3ControlArnParamHandler): + """Updated version of S3ControlArnParamHandler for use when + EndpointRulesetResolver is in use for endpoint resolution. + + This class is considered private and subject to abrupt breaking changes or + removal without prior announcement. Please do not use it directly. + """ + + def __init__(self, arn_parser=None): + self._arn_parser = arn_parser + if arn_parser is None: + self._arn_parser = ArnParser() + + def register(self, event_emitter): + event_emitter.register( + 'before-endpoint-resolution.s3-control', + self.handle_arn, + ) + + def _handle_name_param(self, params, model, context): + # CreateAccessPoint is a special case that does not expand Name + if model.name == 'CreateAccessPoint': + return + arn_details = self._get_arn_details_from_param(params, 'Name') + if arn_details is None: + return + self._raise_for_fips_pseudo_region(arn_details) + self._raise_for_accelerate_endpoint(context) + if self._is_outpost_accesspoint(arn_details): + self._store_outpost_accesspoint(params, context, arn_details) + else: + error_msg = 'The Name parameter does not support the provided ARN' + raise UnsupportedS3ControlArnError( + arn=arn_details['original'], + msg=error_msg, + ) + + def _store_outpost_accesspoint(self, params, context, arn_details): + self._override_account_id_param(params, arn_details) + + def _handle_bucket_param(self, params, model, context): + arn_details = self._get_arn_details_from_param(params, 'Bucket') + if arn_details is None: + return + self._raise_for_fips_pseudo_region(arn_details) + self._raise_for_accelerate_endpoint(context) + if self._is_outpost_bucket(arn_details): + self._store_outpost_bucket(params, context, arn_details) + else: + error_msg = ( + 'The Bucket parameter does not support the provided ARN' + ) + raise UnsupportedS3ControlArnError( + arn=arn_details['original'], + msg=error_msg, + ) + + def _store_outpost_bucket(self, params, context, arn_details): + self._override_account_id_param(params, arn_details) + + def _raise_for_fips_pseudo_region(self, arn_details): + # FIPS pseudo region names cannot be used in ARNs + arn_region = arn_details['region'] + if arn_region.startswith('fips-') or arn_region.endswith('fips-'): + raise UnsupportedS3ControlArnError( + arn=arn_details['original'], + msg='Invalid ARN, FIPS region not allowed in ARN.', + ) + + def _raise_for_accelerate_endpoint(self, context): + s3_config = context['client_config'].s3 or {} + if s3_config.get('use_accelerate_endpoint'): + raise UnsupportedS3ControlConfigurationError( + msg='S3 control client does not support accelerate endpoints', + ) + + +class ContainerMetadataFetcher: + TIMEOUT_SECONDS = 2 + RETRY_ATTEMPTS = 3 + SLEEP_TIME = 1 + IP_ADDRESS = '169.254.170.2' + _ALLOWED_HOSTS = [ + IP_ADDRESS, + '169.254.170.23', + 'fd00:ec2::23', + 'localhost', + ] + + def __init__(self, session=None, sleep=time.sleep): + if session is None: + session = botocore.httpsession.URLLib3Session( + timeout=self.TIMEOUT_SECONDS + ) + self._session = session + self._sleep = sleep + + def retrieve_full_uri(self, full_url, headers=None): + """Retrieve JSON metadata from container metadata. + + :type full_url: str + :param full_url: The full URL of the metadata service. + This should include the scheme as well, e.g + "http://localhost:123/foo" + + """ + self._validate_allowed_url(full_url) + return self._retrieve_credentials(full_url, headers) + + def _validate_allowed_url(self, full_url): + parsed = botocore.compat.urlparse(full_url) + + if parsed.scheme == 'https': + return + if self._is_loopback_address(parsed.hostname): + return + is_whitelisted_host = self._check_if_whitelisted_host(parsed.hostname) + if not is_whitelisted_host: + raise ValueError( + f"Unsupported host '{parsed.hostname}'. Can only retrieve metadata " + f"from a loopback address or one of these hosts: {', '.join(self._ALLOWED_HOSTS)}" + ) + + def _is_loopback_address(self, hostname): + try: + ip = ip_address(hostname) + return ip.is_loopback + except ValueError: + return False + + def _check_if_whitelisted_host(self, host): + if host in self._ALLOWED_HOSTS: + return True + return False + + def retrieve_uri(self, relative_uri): + """Retrieve JSON metadata from container metadata. + + :type relative_uri: str + :param relative_uri: A relative URI, e.g "/foo/bar?id=123" + + :return: The parsed JSON response. + + """ + full_url = self.full_url(relative_uri) + return self._retrieve_credentials(full_url) + + def _retrieve_credentials(self, full_url, extra_headers=None): + headers = {'Accept': 'application/json'} + if extra_headers is not None: + headers.update(extra_headers) + attempts = 0 + while True: + try: + return self._get_response( + full_url, headers, self.TIMEOUT_SECONDS + ) + except MetadataRetrievalError as e: + logger.debug( + "Received error when attempting to retrieve " + "container metadata: %s", + e, + exc_info=True, + ) + self._sleep(self.SLEEP_TIME) + attempts += 1 + if attempts >= self.RETRY_ATTEMPTS: + raise + + def _get_response(self, full_url, headers, timeout): + try: + AWSRequest = botocore.awsrequest.AWSRequest + request = AWSRequest(method='GET', url=full_url, headers=headers) + response = self._session.send(request.prepare()) + response_text = response.content.decode('utf-8') + if response.status_code != 200: + raise MetadataRetrievalError( + error_msg=( + f"Received non 200 response {response.status_code} " + f"from container metadata: {response_text}" + ) + ) + try: + return json.loads(response_text) + except ValueError: + error_msg = "Unable to parse JSON returned from container metadata services" + logger.debug('%s:%s', error_msg, response_text) + raise MetadataRetrievalError(error_msg=error_msg) + except RETRYABLE_HTTP_ERRORS as e: + error_msg = ( + "Received error when attempting to retrieve " + f"container metadata: {e}" + ) + raise MetadataRetrievalError(error_msg=error_msg) + + def full_url(self, relative_uri): + return f'http://{self.IP_ADDRESS}{relative_uri}' + + +def get_environ_proxies(url): + if should_bypass_proxies(url): + return {} + else: + return getproxies() + + +def should_bypass_proxies(url): + """ + Returns whether we should bypass proxies or not. + """ + # NOTE: requests allowed for ip/cidr entries in no_proxy env that we don't + # support current as urllib only checks DNS suffix + # If the system proxy settings indicate that this URL should be bypassed, + # don't proxy. + # The proxy_bypass function is incredibly buggy on OS X in early versions + # of Python 2.6, so allow this call to fail. Only catch the specific + # exceptions we've seen, though: this call failing in other ways can reveal + # legitimate problems. + try: + if proxy_bypass(urlparse(url).netloc): + return True + except (TypeError, socket.gaierror): + pass + + return False + + +def determine_content_length(body): + # No body, content length of 0 + if not body: + return 0 + + # Try asking the body for it's length + try: + return len(body) + except (AttributeError, TypeError): + pass + + # Try getting the length from a seekable stream + if hasattr(body, 'seek') and hasattr(body, 'tell'): + try: + orig_pos = body.tell() + body.seek(0, 2) + end_file_pos = body.tell() + body.seek(orig_pos) + return end_file_pos - orig_pos + except io.UnsupportedOperation: + # in case when body is, for example, io.BufferedIOBase object + # it has "seek" method which throws "UnsupportedOperation" + # exception in such case we want to fall back to "chunked" + # encoding + pass + # Failed to determine the length + return None + + +def get_encoding_from_headers(headers, default='ISO-8859-1'): + """Returns encodings from given HTTP Header Dict. + + :param headers: dictionary to extract encoding from. + :param default: default encoding if the content-type is text + """ + + content_type = headers.get('content-type') + + if not content_type: + return None + + message = email.message.Message() + message['content-type'] = content_type + charset = message.get_param("charset") + + if charset is not None: + return charset + + if 'text' in content_type: + return default + + +def calculate_md5(body, **kwargs): + """This function has been deprecated, but is kept for backwards compatibility.""" + if isinstance(body, (bytes, bytearray)): + binary_md5 = _calculate_md5_from_bytes(body) + else: + binary_md5 = _calculate_md5_from_file(body) + return base64.b64encode(binary_md5).decode('ascii') + + +def _calculate_md5_from_bytes(body_bytes): + """This function has been deprecated, but is kept for backwards compatibility.""" + md5 = get_md5(body_bytes, usedforsecurity=False) + return md5.digest() + + +def _calculate_md5_from_file(fileobj): + """This function has been deprecated, but is kept for backwards compatibility.""" + start_position = fileobj.tell() + md5 = get_md5(usedforsecurity=False) + for chunk in iter(lambda: fileobj.read(1024 * 1024), b''): + md5.update(chunk) + fileobj.seek(start_position) + return md5.digest() + + +def _is_s3express_request(params): + endpoint_properties = params.get('context', {}).get( + 'endpoint_properties', {} + ) + return endpoint_properties.get('backend') == 'S3Express' + + +def get_checksum_algorithm_headers(params): + """ + Returns the a list of header names from the request which start with + "x-amz-checksum-", otherwise returns an empty list. + + This function is considered private and subject to abrupt breaking changes or + removal without prior announcement. Please do not use it directly. + """ + headers = params['headers'] + checksum_headers = [] + + # If a header matching the x-amz-checksum-* pattern is present, we + # extract and return the algorithm name. + for header in headers: + match = CHECKSUM_HEADER_PATTERN.match(header) + if match: + checksum_headers.append(header) + return checksum_headers + + +def has_checksum_header(params): + """ + Checks if a header starting with "x-amz-checksum-" is provided in a request. + + This function is considered private and subject to abrupt breaking changes or + removal without prior announcement. Please do not use it directly. + """ + return bool(get_checksum_algorithm_headers(params)) + + +def conditionally_calculate_checksum(params, **kwargs): + """This function has been deprecated, but is kept for backwards compatibility.""" + if not has_checksum_header(params): + conditionally_calculate_md5(params, **kwargs) + conditionally_enable_crc32(params, **kwargs) + + +def conditionally_enable_crc32(params, **kwargs): + """This function has been deprecated, but is kept for backwards compatibility.""" + checksum_context = params.get('context', {}).get('checksum', {}) + checksum_algorithm = checksum_context.get('request_algorithm') + if ( + _is_s3express_request(params) + and params['body'] is not None + and checksum_algorithm in (None, "conditional-md5") + ): + params['context']['checksum'] = { + 'request_algorithm': { + 'algorithm': 'crc32', + 'in': 'header', + 'name': 'x-amz-checksum-crc32', + } + } + + +def conditionally_calculate_md5(params, **kwargs): + """Only add a Content-MD5 if the system supports it. + + This function has been deprecated, but is kept for backwards compatibility. + """ + body = params['body'] + checksum_context = params.get('context', {}).get('checksum', {}) + checksum_algorithm = checksum_context.get('request_algorithm') + if checksum_algorithm and checksum_algorithm != 'conditional-md5': + # Skip for requests that will have a flexible checksum applied + return + + if has_checksum_header(params): + # Don't add a new header if one is already available. + return + + if _is_s3express_request(params): + # S3Express doesn't support MD5 + return + + if MD5_AVAILABLE and body is not None: + md5_digest = calculate_md5(body, **kwargs) + params['headers']['Content-MD5'] = md5_digest + + +class FileWebIdentityTokenLoader: + def __init__(self, web_identity_token_path, _open=open): + self._web_identity_token_path = web_identity_token_path + self._open = _open + + def __call__(self): + with self._open(self._web_identity_token_path) as token_file: + return token_file.read() + + +class SSOTokenLoader: + def __init__(self, cache=None): + if cache is None: + cache = {} + self._cache = cache + + def _generate_cache_key(self, start_url, session_name): + input_str = start_url + if session_name is not None: + input_str = session_name + return hashlib.sha1(input_str.encode('utf-8')).hexdigest() + + def save_token(self, start_url, token, session_name=None): + cache_key = self._generate_cache_key(start_url, session_name) + self._cache[cache_key] = token + + def __call__(self, start_url, session_name=None): + cache_key = self._generate_cache_key(start_url, session_name) + logger.debug('Checking for cached token at: %s', cache_key) + if cache_key not in self._cache: + name = start_url + if session_name is not None: + name = session_name + error_msg = f'Token for {name} does not exist' + raise SSOTokenLoadError(error_msg=error_msg) + + token = self._cache[cache_key] + if 'accessToken' not in token or 'expiresAt' not in token: + error_msg = f'Token for {start_url} is invalid' + raise SSOTokenLoadError(error_msg=error_msg) + return token + + +class EventbridgeSignerSetter: + _DEFAULT_PARTITION = 'aws' + _DEFAULT_DNS_SUFFIX = 'amazonaws.com' + + def __init__(self, endpoint_resolver, region=None, endpoint_url=None): + self._endpoint_resolver = endpoint_resolver + self._region = region + self._endpoint_url = endpoint_url + + def register(self, event_emitter): + event_emitter.register( + 'before-parameter-build.events.PutEvents', + self.check_for_global_endpoint, + ) + event_emitter.register( + 'before-call.events.PutEvents', self.set_endpoint_url + ) + + def set_endpoint_url(self, params, context, **kwargs): + if 'eventbridge_endpoint' in context: + endpoint = context['eventbridge_endpoint'] + logger.debug( + "Rewriting URL from %s to %s", params['url'], endpoint + ) + params['url'] = endpoint + + def check_for_global_endpoint(self, params, context, **kwargs): + endpoint = params.get('EndpointId') + if endpoint is None: + return + + if len(endpoint) == 0: + raise InvalidEndpointConfigurationError( + msg='EndpointId must not be a zero length string' + ) + + if not HAS_CRT: + raise MissingDependencyException( + msg="Using EndpointId requires an additional " + "dependency. You will need to pip install " + "botocore[crt] before proceeding." + ) + + config = context.get('client_config') + endpoint_variant_tags = None + if config is not None: + if config.use_fips_endpoint: + raise InvalidEndpointConfigurationError( + msg="FIPS is not supported with EventBridge " + "multi-region endpoints." + ) + if config.use_dualstack_endpoint: + endpoint_variant_tags = ['dualstack'] + + if self._endpoint_url is None: + # Validate endpoint is a valid hostname component + parts = urlparse(f'https://{endpoint}') + if parts.hostname != endpoint: + raise InvalidEndpointConfigurationError( + msg='EndpointId is not a valid hostname component.' + ) + resolved_endpoint = self._get_global_endpoint( + endpoint, endpoint_variant_tags=endpoint_variant_tags + ) + else: + resolved_endpoint = self._endpoint_url + + context['eventbridge_endpoint'] = resolved_endpoint + context['auth_type'] = 'v4a' + + def _get_global_endpoint(self, endpoint, endpoint_variant_tags=None): + resolver = self._endpoint_resolver + + partition = resolver.get_partition_for_region(self._region) + if partition is None: + partition = self._DEFAULT_PARTITION + dns_suffix = resolver.get_partition_dns_suffix( + partition, endpoint_variant_tags=endpoint_variant_tags + ) + if dns_suffix is None: + dns_suffix = self._DEFAULT_DNS_SUFFIX + + return f"https://{endpoint}.endpoint.events.{dns_suffix}/" + + +def is_s3_accelerate_url(url): + """Does the URL match the S3 Accelerate endpoint scheme? + + Virtual host naming style with bucket names in the netloc part of the URL + are not allowed by this function. + """ + if url is None: + return False + + # Accelerate is only valid for Amazon endpoints. + url_parts = urlsplit(url) + if not url_parts.netloc.endswith( + 'amazonaws.com' + ) or url_parts.scheme not in ['https', 'http']: + return False + + # The first part of the URL must be s3-accelerate. + parts = url_parts.netloc.split('.') + if parts[0] != 's3-accelerate': + return False + + # Url parts between 's3-accelerate' and 'amazonaws.com' which + # represent different url features. + feature_parts = parts[1:-2] + + # There should be no duplicate URL parts. + if len(feature_parts) != len(set(feature_parts)): + return False + + # Remaining parts must all be in the whitelist. + return all(p in S3_ACCELERATE_WHITELIST for p in feature_parts) + + +class JSONFileCache: + """JSON file cache. + This provides a dict like interface that stores JSON serializable + objects. + The objects are serialized to JSON and stored in a file. These + values can be retrieved at a later time. + """ + + CACHE_DIR = os.path.expanduser(os.path.join('~', '.aws', 'boto', 'cache')) + + def __init__(self, working_dir=CACHE_DIR, dumps_func=None): + self._working_dir = working_dir + if dumps_func is None: + dumps_func = self._default_dumps + self._dumps = dumps_func + + def _default_dumps(self, obj): + return json.dumps(obj, default=self._serialize_if_needed) + + def __contains__(self, cache_key): + actual_key = self._convert_cache_key(cache_key) + return os.path.isfile(actual_key) + + def __getitem__(self, cache_key): + """Retrieve value from a cache key.""" + actual_key = self._convert_cache_key(cache_key) + try: + with open(actual_key) as f: + return json.load(f) + except (OSError, ValueError): + raise KeyError(cache_key) + + def __delitem__(self, cache_key): + actual_key = self._convert_cache_key(cache_key) + try: + key_path = Path(actual_key) + key_path.unlink() + except FileNotFoundError: + raise KeyError(cache_key) + + def __setitem__(self, cache_key, value): + full_key = self._convert_cache_key(cache_key) + try: + file_content = self._dumps(value) + except (TypeError, ValueError): + raise ValueError( + f"Value cannot be cached, must be JSON serializable: {value}" + ) + if not os.path.isdir(self._working_dir): + os.makedirs(self._working_dir, exist_ok=True) + + temp_fd, temp_path = tempfile.mkstemp( + dir=self._working_dir, suffix='.tmp' + ) + with os.fdopen(temp_fd, 'w') as f: + f.write(file_content) + f.flush() + os.fsync(f.fileno()) + + os.replace(temp_path, full_key) + + def _convert_cache_key(self, cache_key): + full_path = os.path.join(self._working_dir, cache_key + '.json') + return full_path + + def _serialize_if_needed(self, value, iso=False): + if isinstance(value, _DatetimeClass): + if iso: + return value.isoformat() + return value.strftime('%Y-%m-%dT%H:%M:%S%Z') + return value + + +def generate_login_cache_key(sign_in_session_name): + return hashlib.sha256(sign_in_session_name.encode('utf-8')).hexdigest() + + +def is_s3express_bucket(bucket): + if bucket is None: + return False + return bucket.endswith('--x-s3') + + +def get_token_from_environment(signing_name, environ=None): + if not isinstance(signing_name, str) or not signing_name.strip(): + return None + + if environ is None: + environ = os.environ + env_var = _get_bearer_env_var_name(signing_name) + return environ.get(env_var) + + +def _get_bearer_env_var_name(signing_name): + bearer_name = signing_name.replace('-', '_').replace(' ', '_').upper() + return f"AWS_BEARER_TOKEN_{bearer_name}" + + +# This parameter is not part of the public interface and is subject to abrupt +# breaking changes or removal without prior announcement. +# Mapping of services that have been renamed for backwards compatibility reasons. +# Keys are the previous name that should be allowed, values are the documented +# and preferred client name. +SERVICE_NAME_ALIASES = {'runtime.sagemaker': 'sagemaker-runtime'} + + +# This parameter is not part of the public interface and is subject to abrupt +# breaking changes or removal without prior announcement. +# Mapping to determine the service ID for services that do not use it as the +# model data directory name. The keys are the data directory name and the +# values are the transformed service IDs (lower case and hyphenated). +CLIENT_NAME_TO_HYPHENIZED_SERVICE_ID_OVERRIDES = { + # Actual service name we use -> Allowed computed service name. + 'apigateway': 'api-gateway', + 'application-autoscaling': 'application-auto-scaling', + 'appmesh': 'app-mesh', + 'autoscaling': 'auto-scaling', + 'autoscaling-plans': 'auto-scaling-plans', + 'ce': 'cost-explorer', + 'cloudhsmv2': 'cloudhsm-v2', + 'cloudsearchdomain': 'cloudsearch-domain', + 'cognito-idp': 'cognito-identity-provider', + 'config': 'config-service', + 'cur': 'cost-and-usage-report-service', + 'datapipeline': 'data-pipeline', + 'directconnect': 'direct-connect', + 'devicefarm': 'device-farm', + 'discovery': 'application-discovery-service', + 'dms': 'database-migration-service', + 'ds': 'directory-service', + 'ds-data': 'directory-service-data', + 'dynamodbstreams': 'dynamodb-streams', + 'elasticbeanstalk': 'elastic-beanstalk', + 'elb': 'elastic-load-balancing', + 'elbv2': 'elastic-load-balancing-v2', + 'es': 'elasticsearch-service', + 'events': 'eventbridge', + 'globalaccelerator': 'global-accelerator', + 'iot-data': 'iot-data-plane', + 'iot-jobs-data': 'iot-jobs-data-plane', + 'iotwireless': 'iot-wireless', + 'kinesisanalytics': 'kinesis-analytics', + 'kinesisanalyticsv2': 'kinesis-analytics-v2', + 'kinesisvideo': 'kinesis-video', + 'lex-models': 'lex-model-building-service', + 'lexv2-models': 'lex-models-v2', + 'lex-runtime': 'lex-runtime-service', + 'lexv2-runtime': 'lex-runtime-v2', + 'logs': 'cloudwatch-logs', + 'machinelearning': 'machine-learning', + 'marketplacecommerceanalytics': 'marketplace-commerce-analytics', + 'marketplace-entitlement': 'marketplace-entitlement-service', + 'meteringmarketplace': 'marketplace-metering', + 'mgh': 'migration-hub', + 'sms-voice': 'pinpoint-sms-voice', + 'resourcegroupstaggingapi': 'resource-groups-tagging-api', + 'route53': 'route-53', + 'route53domains': 'route-53-domains', + 's3control': 's3-control', + 'sdb': 'simpledb', + 'secretsmanager': 'secrets-manager', + 'serverlessrepo': 'serverlessapplicationrepository', + 'servicecatalog': 'service-catalog', + 'servicecatalog-appregistry': 'service-catalog-appregistry', + 'stepfunctions': 'sfn', + 'storagegateway': 'storage-gateway', +} + + +def get_login_token_cache_directory(): + """Returns which directory contains the login_session token files""" + if 'AWS_LOGIN_CACHE_DIRECTORY' in os.environ: + path = os.path.expandvars(os.environ['AWS_LOGIN_CACHE_DIRECTORY']) + path = os.path.expanduser(path) + return path + else: + return os.path.expanduser(os.path.join('~', '.aws', 'login', 'cache')) + + +class LoginTokenLoader: + """Loads and saves login access tokens to disk""" + + def __init__(self, cache=None): + if cache is None: + cache = {} + self._cache = cache + + def save_token(self, session_name, token): + cache_key = generate_login_cache_key(session_name) + self._cache[cache_key] = token + + def load_token(self, session_name): + cache_key = generate_login_cache_key(session_name) + if cache_key not in self._cache: + return None + return self._cache[cache_key] diff --git a/venv/lib/python3.12/site-packages/botocore/validate.py b/venv/lib/python3.12/site-packages/botocore/validate.py new file mode 100644 index 0000000..059dae7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/validate.py @@ -0,0 +1,427 @@ +"""User input parameter validation. + +This module handles user input parameter validation +against a provided input model. + +Note that the objects in this module do *not* mutate any +arguments. No type version happens here. It is up to another +layer to properly convert arguments to any required types. + +Validation Errors +----------------- + + +""" + +import decimal +import json +from datetime import datetime + +from botocore.exceptions import ParamValidationError +from botocore.utils import is_json_value_header, parse_to_aware_datetime + + +def validate_parameters(params, shape): + """Validates input parameters against a schema. + + This is a convenience function that validates parameters against a schema. + You can also instantiate and use the ParamValidator class directly if you + want more control. + + If there are any validation errors then a ParamValidationError + will be raised. If there are no validation errors than no exception + is raised and a value of None is returned. + + :param params: The user provided input parameters. + + :type shape: botocore.model.Shape + :param shape: The schema which the input parameters should + adhere to. + + :raise: ParamValidationError + + """ + validator = ParamValidator() + report = validator.validate(params, shape) + if report.has_errors(): + raise ParamValidationError(report=report.generate_report()) + + +def type_check(valid_types): + def _create_type_check_guard(func): + def _on_passes_type_check(self, param, shape, errors, name): + if _type_check(param, errors, name): + return func(self, param, shape, errors, name) + + def _type_check(param, errors, name): + if not isinstance(param, valid_types): + valid_type_names = [str(t) for t in valid_types] + errors.report( + name, + 'invalid type', + param=param, + valid_types=valid_type_names, + ) + return False + return True + + return _on_passes_type_check + + return _create_type_check_guard + + +def range_check(name, value, shape, error_type, errors): + failed = False + min_allowed = float('-inf') + if 'min' in shape.metadata: + min_allowed = shape.metadata['min'] + if value < min_allowed: + failed = True + elif hasattr(shape, 'serialization'): + # Members that can be bound to the host have an implicit min of 1 + if shape.serialization.get('hostLabel'): + min_allowed = 1 + if value < min_allowed: + failed = True + if failed: + errors.report(name, error_type, param=value, min_allowed=min_allowed) + + +class ValidationErrors: + def __init__(self): + self._errors = [] + + def has_errors(self): + if self._errors: + return True + return False + + def generate_report(self): + error_messages = [] + for error in self._errors: + error_messages.append(self._format_error(error)) + return '\n'.join(error_messages) + + def _format_error(self, error): + error_type, name, additional = error + name = self._get_name(name) + if error_type == 'missing required field': + return ( + f"Missing required parameter in {name}: " + f"\"{additional['required_name']}\"" + ) + elif error_type == 'unknown field': + unknown_param = additional['unknown_param'] + valid_names = ', '.join(additional['valid_names']) + return ( + f'Unknown parameter in {name}: "{unknown_param}", ' + f'must be one of: {valid_names}' + ) + elif error_type == 'invalid type': + param = additional['param'] + param_type = type(param) + valid_types = ', '.join(additional['valid_types']) + return ( + f'Invalid type for parameter {name}, value: {param}, ' + f'type: {param_type}, valid types: {valid_types}' + ) + elif error_type == 'invalid range': + param = additional['param'] + min_allowed = additional['min_allowed'] + return ( + f'Invalid value for parameter {name}, value: {param}, ' + f'valid min value: {min_allowed}' + ) + elif error_type == 'invalid length': + param = additional['param'] + min_allowed = additional['min_allowed'] + return ( + f'Invalid length for parameter {name}, value: {param}, ' + f'valid min length: {min_allowed}' + ) + elif error_type == 'unable to encode to json': + return 'Invalid parameter {} must be json serializable: {}'.format( + name, + additional['type_error'], + ) + elif error_type == 'invalid type for document': + param = additional['param'] + param_type = type(param) + valid_types = ', '.join(additional['valid_types']) + return ( + f'Invalid type for document parameter {name}, value: {param}, ' + f'type: {param_type}, valid types: {valid_types}' + ) + elif error_type == 'more than one input': + members = ', '.join(additional['members']) + return ( + f'Invalid number of parameters set for tagged union structure ' + f'{name}. Can only set one of the following keys: ' + f'{members}.' + ) + elif error_type == 'empty input': + members = ', '.join(additional['members']) + return ( + f'Must set one of the following keys for tagged union' + f'structure {name}: {members}.' + ) + + def _get_name(self, name): + if not name: + return 'input' + elif name.startswith('.'): + return name[1:] + else: + return name + + def report(self, name, reason, **kwargs): + self._errors.append((reason, name, kwargs)) + + +class ParamValidator: + """Validates parameters against a shape model.""" + + # Valid Python types for scalar c2j types + SCALAR_TYPES = { + 'float': (float, decimal.Decimal, int), + 'double': (float, decimal.Decimal, int), + 'integer': (int,), + 'long': (int,), + 'boolean': (bool,), + 'string': (str,), + } + + # Valid Python types for container c2j types + CONTAINER_TYPES = { + 'structure': (dict,), + 'map': (dict,), + 'list': (list, tuple), + } + + # Metadata attributes that we validate beyond type checking + VALIDATED_METADATA_ATTRS = {'required', 'min', 'document', 'union'} + + def _shape_has_constraints(self, shape): + """Whether the shape has validated constraints beyond type checking.""" + return bool(self.VALIDATED_METADATA_ATTRS & set(shape.metadata.keys())) + + def validate(self, params, shape): + """Validate parameters against a shape model. + + This method will validate the parameters against a provided shape model. + All errors will be collected before returning to the caller. This means + that this method will not stop at the first error, it will return all + possible errors. + + :param params: User provided dict of parameters + :param shape: A shape model describing the expected input. + + :return: A list of errors. + + """ + errors = ValidationErrors() + self._validate(params, shape, errors, name='') + return errors + + def _check_special_validation_cases(self, shape): + if is_json_value_header(shape): + return self._validate_jsonvalue_string + if shape.type_name == 'structure' and shape.is_document_type: + return self._validate_document + + def _validate(self, params, shape, errors, name): + special_validator = self._check_special_validation_cases(shape) + if special_validator: + special_validator(params, shape, errors, name) + else: + getattr(self, f'_validate_{shape.type_name}')( + params, shape, errors, name + ) + + def _validate_jsonvalue_string(self, params, shape, errors, name): + # Check to see if a value marked as a jsonvalue can be dumped to + # a json string. + try: + json.dumps(params) + except (ValueError, TypeError) as e: + errors.report(name, 'unable to encode to json', type_error=e) + + def _validate_document(self, params, shape, errors, name): + if params is None: + return + + if isinstance(params, dict): + for key in params: + self._validate_document(params[key], shape, errors, key) + elif isinstance(params, list): + for index, entity in enumerate(params): + self._validate_document( + entity, shape, errors, f'{name}[{index}]' + ) + elif not isinstance(params, ((str,), int, bool, float)): + valid_types = (str, int, bool, float, list, dict) + valid_type_names = [str(t) for t in valid_types] + errors.report( + name, + 'invalid type for document', + param=params, + param_type=type(params), + valid_types=valid_type_names, + ) + + @type_check(valid_types=CONTAINER_TYPES['structure']) + def _validate_structure(self, params, shape, errors, name): + if shape.is_tagged_union: + if len(params) == 0: + errors.report(name, 'empty input', members=shape.members) + elif len(params) > 1: + errors.report( + name, 'more than one input', members=shape.members + ) + + # Validate required fields. + for required_member in shape.metadata.get('required', []): + if required_member not in params: + errors.report( + name, + 'missing required field', + required_name=required_member, + user_params=params, + ) + members = shape.members + known_params = [] + # Validate known params. + for param in params: + if param not in members: + errors.report( + name, + 'unknown field', + unknown_param=param, + valid_names=list(members), + ) + else: + known_params.append(param) + # Validate structure members. + for param in known_params: + self._validate( + params[param], + shape.members[param], + errors, + f'{name}.{param}', + ) + + @type_check(valid_types=SCALAR_TYPES['string']) + def _validate_string(self, param, shape, errors, name): + # Validate range. For a string, the min/max constraints + # are of the string length. + # Looks like: + # "WorkflowId":{ + # "type":"string", + # "min":1, + # "max":256 + # } + range_check(name, len(param), shape, 'invalid length', errors) + + @type_check(valid_types=CONTAINER_TYPES['list']) + def _validate_list(self, param, shape, errors, name): + member_shape = shape.member + range_check(name, len(param), shape, 'invalid length', errors) + + # If a list member does not have validation constraints, we will only check the type + member_type = member_shape.type_name + if ( + member_type in self.SCALAR_TYPES + and not self._shape_has_constraints(member_shape) + ): + valid_types = self.SCALAR_TYPES[member_type] + for i, item in enumerate(param): + if not isinstance(item, valid_types): + valid_type_names = [str(t) for t in valid_types] + errors.report( + f'{name}[{i}]', + 'invalid type', + param=item, + valid_types=valid_type_names, + ) + return + + for i, item in enumerate(param): + self._validate(item, member_shape, errors, f'{name}[{i}]') + + @type_check(valid_types=CONTAINER_TYPES['map']) + def _validate_map(self, param, shape, errors, name): + key_shape = shape.key + value_shape = shape.value + for key, value in param.items(): + self._validate(key, key_shape, errors, f"{name} (key: {key})") + self._validate(value, value_shape, errors, f'{name}.{key}') + + @type_check(valid_types=SCALAR_TYPES['integer']) + def _validate_integer(self, param, shape, errors, name): + range_check(name, param, shape, 'invalid range', errors) + + def _validate_blob(self, param, shape, errors, name): + if isinstance(param, (bytes, bytearray, str)): + return + elif hasattr(param, 'read'): + # File like objects are also allowed for blob types. + return + else: + errors.report( + name, + 'invalid type', + param=param, + valid_types=[str(bytes), str(bytearray), 'file-like object'], + ) + + @type_check(valid_types=SCALAR_TYPES['boolean']) + def _validate_boolean(self, param, shape, errors, name): + pass + + @type_check(valid_types=SCALAR_TYPES['double']) + def _validate_double(self, param, shape, errors, name): + range_check(name, param, shape, 'invalid range', errors) + + _validate_float = _validate_double + + @type_check(valid_types=SCALAR_TYPES['long']) + def _validate_long(self, param, shape, errors, name): + range_check(name, param, shape, 'invalid range', errors) + + def _validate_timestamp(self, param, shape, errors, name): + # We don't use @type_check because datetimes are a bit + # more flexible. You can either provide a datetime + # object, or a string that parses to a datetime. + is_valid_type = self._type_check_datetime(param) + if not is_valid_type: + valid_type_names = [str(datetime), 'timestamp-string'] + errors.report( + name, 'invalid type', param=param, valid_types=valid_type_names + ) + + def _type_check_datetime(self, value): + try: + parse_to_aware_datetime(value) + return True + except (TypeError, ValueError, AttributeError): + # Yes, dateutil can sometimes raise an AttributeError + # when parsing timestamps. + return False + + +class ParamValidationDecorator: + def __init__(self, param_validator, serializer): + self._param_validator = param_validator + self._serializer = serializer + + def serialize_to_request(self, parameters, operation_model): + input_shape = operation_model.input_shape + if input_shape is not None: + report = self._param_validator.validate( + parameters, operation_model.input_shape + ) + if report.has_errors(): + raise ParamValidationError(report=report.generate_report()) + return self._serializer.serialize_to_request( + parameters, operation_model + ) diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/__init__.py b/venv/lib/python3.12/site-packages/botocore/vendored/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/vendored/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0738e0b Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/vendored/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/__pycache__/six.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/vendored/__pycache__/six.cpython-312.pyc new file mode 100644 index 0000000..519af62 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/vendored/__pycache__/six.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/requests/__init__.py b/venv/lib/python3.12/site-packages/botocore/vendored/requests/__init__.py new file mode 100644 index 0000000..0ada6e0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/vendored/requests/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- + +# __ +# /__) _ _ _ _ _/ _ +# / ( (- (/ (/ (- _) / _) +# / +from .exceptions import ( + RequestException, Timeout, URLRequired, + TooManyRedirects, HTTPError, ConnectionError +) diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/requests/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/vendored/requests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..9bffdca Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/vendored/requests/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/requests/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/vendored/requests/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..74abb83 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/vendored/requests/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/requests/exceptions.py b/venv/lib/python3.12/site-packages/botocore/vendored/requests/exceptions.py new file mode 100644 index 0000000..89135a8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/vendored/requests/exceptions.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- + +""" +requests.exceptions +~~~~~~~~~~~~~~~~~~~ + +This module contains the set of Requests' exceptions. + +""" +from .packages.urllib3.exceptions import HTTPError as BaseHTTPError + + +class RequestException(IOError): + """There was an ambiguous exception that occurred while handling your + request.""" + + def __init__(self, *args, **kwargs): + """ + Initialize RequestException with `request` and `response` objects. + """ + response = kwargs.pop('response', None) + self.response = response + self.request = kwargs.pop('request', None) + if (response is not None and not self.request and + hasattr(response, 'request')): + self.request = self.response.request + super(RequestException, self).__init__(*args, **kwargs) + + +class HTTPError(RequestException): + """An HTTP error occurred.""" + + +class ConnectionError(RequestException): + """A Connection error occurred.""" + + +class ProxyError(ConnectionError): + """A proxy error occurred.""" + + +class SSLError(ConnectionError): + """An SSL error occurred.""" + + +class Timeout(RequestException): + """The request timed out. + + Catching this error will catch both + :exc:`~requests.exceptions.ConnectTimeout` and + :exc:`~requests.exceptions.ReadTimeout` errors. + """ + + +class ConnectTimeout(ConnectionError, Timeout): + """The request timed out while trying to connect to the remote server. + + Requests that produced this error are safe to retry. + """ + + +class ReadTimeout(Timeout): + """The server did not send any data in the allotted amount of time.""" + + +class URLRequired(RequestException): + """A valid URL is required to make a request.""" + + +class TooManyRedirects(RequestException): + """Too many redirects.""" + + +class MissingSchema(RequestException, ValueError): + """The URL schema (e.g. http or https) is missing.""" + + +class InvalidSchema(RequestException, ValueError): + """See defaults.py for valid schemas.""" + + +class InvalidURL(RequestException, ValueError): + """ The URL provided was somehow invalid. """ + + +class ChunkedEncodingError(RequestException): + """The server declared chunked encoding but sent an invalid chunk.""" + + +class ContentDecodingError(RequestException, BaseHTTPError): + """Failed to decode response content""" + + +class StreamConsumedError(RequestException, TypeError): + """The content for this response was already consumed""" + + +class RetryError(RequestException): + """Custom retries logic failed""" diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/__init__.py b/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/__init__.py new file mode 100644 index 0000000..d62c4b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/__init__.py @@ -0,0 +1,3 @@ +from __future__ import absolute_import + +from . import urllib3 diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6a7acc3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/urllib3/__init__.py b/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/urllib3/__init__.py new file mode 100644 index 0000000..8869701 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/urllib3/__init__.py @@ -0,0 +1,10 @@ +""" +urllib3 - Thread-safe connection pooling and re-using. +""" + +__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)' +__license__ = 'MIT' +__version__ = '' + + +from . import exceptions diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/urllib3/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/urllib3/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..265f80f Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/urllib3/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/urllib3/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/urllib3/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..070db37 Binary files /dev/null and b/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/urllib3/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/urllib3/exceptions.py b/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/urllib3/exceptions.py new file mode 100644 index 0000000..31bda1c --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/vendored/requests/packages/urllib3/exceptions.py @@ -0,0 +1,169 @@ + +## Base Exceptions + +class HTTPError(Exception): + "Base exception used by this module." + pass + +class HTTPWarning(Warning): + "Base warning used by this module." + pass + + + +class PoolError(HTTPError): + "Base exception for errors caused within a pool." + def __init__(self, pool, message): + self.pool = pool + HTTPError.__init__(self, "%s: %s" % (pool, message)) + + def __reduce__(self): + # For pickling purposes. + return self.__class__, (None, None) + + +class RequestError(PoolError): + "Base exception for PoolErrors that have associated URLs." + def __init__(self, pool, url, message): + self.url = url + PoolError.__init__(self, pool, message) + + def __reduce__(self): + # For pickling purposes. + return self.__class__, (None, self.url, None) + + +class SSLError(HTTPError): + "Raised when SSL certificate fails in an HTTPS connection." + pass + + +class ProxyError(HTTPError): + "Raised when the connection to a proxy fails." + pass + + +class DecodeError(HTTPError): + "Raised when automatic decoding based on Content-Type fails." + pass + + +class ProtocolError(HTTPError): + "Raised when something unexpected happens mid-request/response." + pass + + +#: Renamed to ProtocolError but aliased for backwards compatibility. +ConnectionError = ProtocolError + + +## Leaf Exceptions + +class MaxRetryError(RequestError): + """Raised when the maximum number of retries is exceeded. + + :param pool: The connection pool + :type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool` + :param string url: The requested Url + :param exceptions.Exception reason: The underlying error + + """ + + def __init__(self, pool, url, reason=None): + self.reason = reason + + message = "Max retries exceeded with url: %s (Caused by %r)" % ( + url, reason) + + RequestError.__init__(self, pool, url, message) + + +class HostChangedError(RequestError): + "Raised when an existing pool gets a request for a foreign host." + + def __init__(self, pool, url, retries=3): + message = "Tried to open a foreign host with url: %s" % url + RequestError.__init__(self, pool, url, message) + self.retries = retries + + +class TimeoutStateError(HTTPError): + """ Raised when passing an invalid state to a timeout """ + pass + + +class TimeoutError(HTTPError): + """ Raised when a socket timeout error occurs. + + Catching this error will catch both :exc:`ReadTimeoutErrors + ` and :exc:`ConnectTimeoutErrors `. + """ + pass + + +class ReadTimeoutError(TimeoutError, RequestError): + "Raised when a socket timeout occurs while receiving data from a server" + pass + + +# This timeout error does not have a URL attached and needs to inherit from the +# base HTTPError +class ConnectTimeoutError(TimeoutError): + "Raised when a socket timeout occurs while connecting to a server" + pass + + +class EmptyPoolError(PoolError): + "Raised when a pool runs out of connections and no more are allowed." + pass + + +class ClosedPoolError(PoolError): + "Raised when a request enters a pool after the pool has been closed." + pass + + +class LocationValueError(ValueError, HTTPError): + "Raised when there is something wrong with a given URL input." + pass + + +class LocationParseError(LocationValueError): + "Raised when get_host or similar fails to parse the URL input." + + def __init__(self, location): + message = "Failed to parse: %s" % location + HTTPError.__init__(self, message) + + self.location = location + + +class ResponseError(HTTPError): + "Used as a container for an error reason supplied in a MaxRetryError." + GENERIC_ERROR = 'too many error responses' + SPECIFIC_ERROR = 'too many {status_code} error responses' + + +class SecurityWarning(HTTPWarning): + "Warned when perfoming security reducing actions" + pass + + +class InsecureRequestWarning(SecurityWarning): + "Warned when making an unverified HTTPS request." + pass + + +class SystemTimeWarning(SecurityWarning): + "Warned when system time is suspected to be wrong" + pass + + +class InsecurePlatformWarning(SecurityWarning): + "Warned when certain SSL configuration is not available on a platform." + pass + + +class ResponseNotChunked(ProtocolError, ValueError): + "Response needs to be chunked in order to read it as chunks." + pass diff --git a/venv/lib/python3.12/site-packages/botocore/vendored/six.py b/venv/lib/python3.12/site-packages/botocore/vendored/six.py new file mode 100644 index 0000000..4e15675 --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/vendored/six.py @@ -0,0 +1,998 @@ +# Copyright (c) 2010-2020 Benjamin Peterson +# +# 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. + +"""Utilities for writing code that runs on Python 2 and 3""" + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson " +__version__ = "1.16.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + +if PY34: + from importlib.util import spec_from_loader +else: + spec_from_loader = None + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def find_spec(self, fullname, path, target=None): + if fullname in self.known_modules: + return spec_from_loader(fullname, self) + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("getoutput", "commands", "subprocess"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("splitvalue", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), + MovedAttribute("parse_http_list", "urllib2", "urllib.request"), + MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + del io + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" + _assertNotRegex = "assertNotRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +def assertNotRegex(self, *args, **kwargs): + return getattr(self, _assertNotRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + try: + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None + tb = None + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + try: + raise tp, value, tb + finally: + tb = None +""") + + +if sys.version_info[:2] > (3,): + exec_("""def raise_from(value, from_value): + try: + raise value from from_value + finally: + value = None +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + # This does exactly the same what the :func:`py3:functools.update_wrapper` + # function does on Python versions after 3.2. It sets the ``__wrapped__`` + # attribute on ``wrapper`` object and it doesn't raise an error if any of + # the attributes mentioned in ``assigned`` and ``updated`` are missing on + # ``wrapped`` object. + def _update_wrapper(wrapper, wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + continue + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + wrapper.__wrapped__ = wrapped + return wrapper + _update_wrapper.__doc__ = functools.update_wrapper.__doc__ + + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + return functools.partial(_update_wrapper, wrapped=wrapped, + assigned=assigned, updated=updated) + wraps.__doc__ = functools.wraps.__doc__ + +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + + def __new__(cls, name, this_bases, d): + if sys.version_info[:2] >= (3, 7): + # This version introduced PEP 560 that requires a bit + # of extra care (we mimic what is done by __build_class__). + resolved_bases = types.resolve_bases(bases) + if resolved_bases is not bases: + d['__orig_bases__'] = bases + else: + resolved_bases = bases + return meta(name, resolved_bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + if hasattr(cls, '__qualname__'): + orig_vars['__qualname__'] = cls.__qualname__ + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def ensure_binary(s, encoding='utf-8', errors='strict'): + """Coerce **s** to six.binary_type. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> encoded to `bytes` + - `bytes` -> `bytes` + """ + if isinstance(s, binary_type): + return s + if isinstance(s, text_type): + return s.encode(encoding, errors) + raise TypeError("not expecting type '%s'" % type(s)) + + +def ensure_str(s, encoding='utf-8', errors='strict'): + """Coerce *s* to `str`. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + # Optimization: Fast return for the common case. + if type(s) is str: + return s + if PY2 and isinstance(s, text_type): + return s.encode(encoding, errors) + elif PY3 and isinstance(s, binary_type): + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) + return s + + +def ensure_text(s, encoding='utf-8', errors='strict'): + """Coerce *s* to six.text_type. + + For Python 2: + - `unicode` -> `unicode` + - `str` -> `unicode` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if isinstance(s, binary_type): + return s.decode(encoding, errors) + elif isinstance(s, text_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + +def python_2_unicode_compatible(klass): + """ + A class decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/venv/lib/python3.12/site-packages/botocore/waiter.py b/venv/lib/python3.12/site-packages/botocore/waiter.py new file mode 100644 index 0000000..86110dd --- /dev/null +++ b/venv/lib/python3.12/site-packages/botocore/waiter.py @@ -0,0 +1,396 @@ +# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +import logging +import time +from functools import partial + +import jmespath + +from botocore.context import with_current_context +from botocore.docs.docstring import WaiterDocstring +from botocore.useragent import register_feature_id +from botocore.utils import get_service_module_name + +from . import xform_name +from .exceptions import ClientError, WaiterConfigError, WaiterError + +logger = logging.getLogger(__name__) + + +def create_waiter_with_client(waiter_name, waiter_model, client): + """ + + :type waiter_name: str + :param waiter_name: The name of the waiter. The name should match + the name (including the casing) of the key name in the waiter + model file (typically this is CamelCasing). + + :type waiter_model: botocore.waiter.WaiterModel + :param waiter_model: The model for the waiter configuration. + + :type client: botocore.client.BaseClient + :param client: The botocore client associated with the service. + + :rtype: botocore.waiter.Waiter + :return: The waiter object. + + """ + single_waiter_config = waiter_model.get_waiter(waiter_name) + operation_name = xform_name(single_waiter_config.operation) + operation_method = NormalizedOperationMethod( + getattr(client, operation_name) + ) + + # Create a new wait method that will serve as a proxy to the underlying + # Waiter.wait method. This is needed to attach a docstring to the + # method. + def wait(self, **kwargs): + Waiter.wait(self, **kwargs) + + wait.__doc__ = WaiterDocstring( + waiter_name=waiter_name, + event_emitter=client.meta.events, + service_model=client.meta.service_model, + service_waiter_model=waiter_model, + include_signature=False, + ) + + # Rename the waiter class based on the type of waiter. + waiter_class_name = str( + f'{get_service_module_name(client.meta.service_model)}.Waiter.{waiter_name}' + ) + + # Create the new waiter class + documented_waiter_cls = type(waiter_class_name, (Waiter,), {'wait': wait}) + + # Return an instance of the new waiter class. + return documented_waiter_cls( + waiter_name, single_waiter_config, operation_method + ) + + +def is_valid_waiter_error(response): + error = response.get('Error') + if isinstance(error, dict) and 'Code' in error: + return True + return False + + +class NormalizedOperationMethod: + def __init__(self, client_method): + self._client_method = client_method + + def __call__(self, **kwargs): + try: + return self._client_method(**kwargs) + except ClientError as e: + return e.response + + +class WaiterModel: + SUPPORTED_VERSION = 2 + + def __init__(self, waiter_config): + """ + + Note that the WaiterModel takes ownership of the waiter_config. + It may or may not mutate the waiter_config. If this is a concern, + it is best to make a copy of the waiter config before passing it to + the WaiterModel. + + :type waiter_config: dict + :param waiter_config: The loaded waiter config + from the *.waiters.json file. This can be + obtained from a botocore Loader object as well. + + """ + self._waiter_config = waiter_config['waiters'] + + # These are part of the public API. Changing these + # will result in having to update the consuming code, + # so don't change unless you really need to. + version = waiter_config.get('version', 'unknown') + self._verify_supported_version(version) + self.version = version + self.waiter_names = list(sorted(waiter_config['waiters'].keys())) + + def _verify_supported_version(self, version): + if version != self.SUPPORTED_VERSION: + raise WaiterConfigError( + error_msg=( + "Unsupported waiter version, supported version " + f"must be: {self.SUPPORTED_VERSION}, but version " + f"of waiter config is: {version}" + ) + ) + + def get_waiter(self, waiter_name): + try: + single_waiter_config = self._waiter_config[waiter_name] + except KeyError: + raise ValueError(f"Waiter does not exist: {waiter_name}") + return SingleWaiterConfig(single_waiter_config) + + +class SingleWaiterConfig: + """Represents the waiter configuration for a single waiter. + + A single waiter is considered the configuration for a single + value associated with a named waiter (i.e TableExists). + + """ + + def __init__(self, single_waiter_config): + self._config = single_waiter_config + + # These attributes are part of the public API. + self.description = single_waiter_config.get('description', '') + # Per the spec, these three fields are required. + self.operation = single_waiter_config['operation'] + self.delay = single_waiter_config['delay'] + self.max_attempts = single_waiter_config['maxAttempts'] + + @property + def acceptors(self): + acceptors = [] + for acceptor_config in self._config['acceptors']: + acceptor = AcceptorConfig(acceptor_config) + acceptors.append(acceptor) + return acceptors + + +class AcceptorConfig: + def __init__(self, config): + self.state = config['state'] + self.matcher = config['matcher'] + self.expected = config['expected'] + self.argument = config.get('argument') + self.matcher_func = self._create_matcher_func() + + @property + def explanation(self): + if self.matcher == 'path': + return f'For expression "{self.argument}" we matched expected path: "{self.expected}"' + elif self.matcher == 'pathAll': + return ( + f'For expression "{self.argument}" all members matched ' + f'expected path: "{self.expected}"' + ) + elif self.matcher == 'pathAny': + return ( + f'For expression "{self.argument}" we matched expected ' + f'path: "{self.expected}" at least once' + ) + elif self.matcher == 'status': + return f'Matched expected HTTP status code: {self.expected}' + elif self.matcher == 'error': + return f'Matched expected service error code: {self.expected}' + else: + return f'No explanation for unknown waiter type: "{self.matcher}"' + + def _create_matcher_func(self): + # An acceptor function is a callable that takes a single value. The + # parsed AWS response. Note that the parsed error response is also + # provided in the case of errors, so it's entirely possible to + # handle all the available matcher capabilities in the future. + # There's only three supported matchers, so for now, this is all + # contained to a single method. If this grows, we can expand this + # out to separate methods or even objects. + + if self.matcher == 'path': + return self._create_path_matcher() + elif self.matcher == 'pathAll': + return self._create_path_all_matcher() + elif self.matcher == 'pathAny': + return self._create_path_any_matcher() + elif self.matcher == 'status': + return self._create_status_matcher() + elif self.matcher == 'error': + return self._create_error_matcher() + else: + raise WaiterConfigError( + error_msg=f"Unknown acceptor: {self.matcher}" + ) + + def _create_path_matcher(self): + expression = jmespath.compile(self.argument) + expected = self.expected + + def acceptor_matches(response): + if is_valid_waiter_error(response): + return + return expression.search(response) == expected + + return acceptor_matches + + def _create_path_all_matcher(self): + expression = jmespath.compile(self.argument) + expected = self.expected + + def acceptor_matches(response): + if is_valid_waiter_error(response): + return + result = expression.search(response) + if not isinstance(result, list) or not result: + # pathAll matcher must result in a list. + # Also we require at least one element in the list, + # that is, an empty list should not result in this + # acceptor match. + return False + for element in result: + if element != expected: + return False + return True + + return acceptor_matches + + def _create_path_any_matcher(self): + expression = jmespath.compile(self.argument) + expected = self.expected + + def acceptor_matches(response): + if is_valid_waiter_error(response): + return + result = expression.search(response) + if not isinstance(result, list) or not result: + # pathAny matcher must result in a list. + # Also we require at least one element in the list, + # that is, an empty list should not result in this + # acceptor match. + return False + for element in result: + if element == expected: + return True + return False + + return acceptor_matches + + def _create_status_matcher(self): + expected = self.expected + + def acceptor_matches(response): + # We don't have any requirements on the expected incoming data + # other than it is a dict, so we don't assume there's + # a ResponseMetadata.HTTPStatusCode. + status_code = response.get('ResponseMetadata', {}).get( + 'HTTPStatusCode' + ) + return status_code == expected + + return acceptor_matches + + def _create_error_matcher(self): + expected = self.expected + + def acceptor_matches(response): + # When the client encounters an error, it will normally raise + # an exception. However, the waiter implementation will catch + # this exception, and instead send us the parsed error + # response. So response is still a dictionary, and in the case + # of an error response will contain the "Error" and + # "ResponseMetadata" key. + # When expected is True, accept any error code. + # When expected is False, check if any errors were encountered. + # Otherwise, check for a specific AWS error code. + if expected is True: + return "Error" in response and "Code" in response["Error"] + elif expected is False: + return "Error" not in response + else: + return response.get("Error", {}).get("Code", "") == expected + + return acceptor_matches + + +class Waiter: + def __init__(self, name, config, operation_method): + """ + + :type name: string + :param name: The name of the waiter + + :type config: botocore.waiter.SingleWaiterConfig + :param config: The configuration for the waiter. + + :type operation_method: callable + :param operation_method: A callable that accepts **kwargs + and returns a response. For example, this can be + a method from a botocore client. + + """ + self._operation_method = operation_method + # The two attributes are exposed to allow for introspection + # and documentation. + self.name = name + self.config = config + + @with_current_context(partial(register_feature_id, 'WAITER')) + def wait(self, **kwargs): + acceptors = list(self.config.acceptors) + current_state = 'waiting' + # pop the invocation specific config + config = kwargs.pop('WaiterConfig', {}) + sleep_amount = config.get('Delay', self.config.delay) + max_attempts = config.get('MaxAttempts', self.config.max_attempts) + last_matched_acceptor = None + num_attempts = 0 + + while True: + response = self._operation_method(**kwargs) + num_attempts += 1 + for acceptor in acceptors: + if acceptor.matcher_func(response): + last_matched_acceptor = acceptor + current_state = acceptor.state + break + else: + # If none of the acceptors matched, we should + # transition to the failure state if an error + # response was received. + if is_valid_waiter_error(response): + # Transition to a failure state, which we + # can just handle here by raising an exception. + raise WaiterError( + name=self.name, + reason='An error occurred ({}): {}'.format( + response['Error'].get('Code', 'Unknown'), + response['Error'].get('Message', 'Unknown'), + ), + last_response=response, + ) + if current_state == 'success': + logger.debug( + "Waiting complete, waiter matched the success state." + ) + return + if current_state == 'failure': + reason = f'Waiter encountered a terminal failure state: {acceptor.explanation}' + raise WaiterError( + name=self.name, + reason=reason, + last_response=response, + ) + if num_attempts >= max_attempts: + if last_matched_acceptor is None: + reason = 'Max attempts exceeded' + else: + reason = ( + f'Max attempts exceeded. Previously accepted state: ' + f'{acceptor.explanation}' + ) + raise WaiterError( + name=self.name, + reason=reason, + last_response=response, + ) + time.sleep(sleep_amount) diff --git a/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/METADATA b/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/METADATA new file mode 100644 index 0000000..eb460c2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/METADATA @@ -0,0 +1,78 @@ +Metadata-Version: 2.4 +Name: certifi +Version: 2026.7.22 +Summary: Python package for providing Mozilla's CA Bundle. +Home-page: https://github.com/certifi/python-certifi +Author: Kenneth Reitz +Author-email: me@kennethreitz.com +License: MPL-2.0 +Project-URL: Source, https://github.com/certifi/python-certifi +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) +Classifier: Natural Language :: English +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.7 +License-File: LICENSE +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: project-url +Dynamic: requires-python +Dynamic: summary + +Certifi: Python SSL Certificates +================================ + +Certifi provides Mozilla's carefully curated collection of Root Certificates for +validating the trustworthiness of SSL certificates while verifying the identity +of TLS hosts. It has been extracted from the `Requests`_ project. + +Installation +------------ + +``certifi`` is available on PyPI. Simply install it with ``pip``:: + + $ pip install certifi + +Usage +----- + +To reference the installed certificate authority (CA) bundle, you can use the +built-in function:: + + >>> import certifi + + >>> certifi.where() + '/usr/local/lib/python3.7/site-packages/certifi/cacert.pem' + +Or from the command line:: + + $ python -m certifi + /usr/local/lib/python3.7/site-packages/certifi/cacert.pem + +Enjoy! + +.. _`Requests`: https://requests.readthedocs.io/en/latest/ + +Addition/Removal of Certificates +-------------------------------- + +Certifi does not support any addition/removal or other modification of the +CA trust store content. This project is intended to provide a reliable and +highly portable root of trust to python deployments. Look to upstream projects +for methods to use alternate trust. diff --git a/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/RECORD b/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/RECORD new file mode 100644 index 0000000..0fe19f4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/RECORD @@ -0,0 +1,18 @@ +certifi-2026.7.22.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +certifi-2026.7.22.dist-info/METADATA,sha256=71rxY4-7I2dqw8V3ffz8LNnDSP5Bcu1bo9J3ZVskgJA,2474 +certifi-2026.7.22.dist-info/RECORD,, +certifi-2026.7.22.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91 +certifi-2026.7.22.dist-info/licenses/LICENSE,sha256=6TcW2mucDVpKHfYP5pWzcPBpVgPSH2-D8FPkLPwQyvc,989 +certifi-2026.7.22.dist-info/top_level.txt,sha256=KMu4vUCfsjLrkPbSNdgdekS-pVJzBAJFO__nI8NF6-U,8 +certifi/__init__.py,sha256=elLl9CBfmz0SoxiYzqp-P2g30ZzCMHALyuGp2R0UhsE,94 +certifi/__main__.py,sha256=xBBoj905TUWBLRGANOcf7oi6e-3dMP4cEoG9OyMs11g,243 +certifi/__pycache__/__init__.cpython-312.pyc,, +certifi/__pycache__/__main__.cpython-312.pyc,, +certifi/__pycache__/core.cpython-312.pyc,, +certifi/cacert.pem,sha256=nMKndLUZjc_xTZvh5mCR9TiXXYZ84Cmpa84VpV39cw8,240216 +certifi/core.py,sha256=XFXycndG5pf37ayeF8N32HUuDafsyhkVMbO4BAPWHa0,3394 +certifi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +certifi/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +certifi/tests/__pycache__/__init__.cpython-312.pyc,, +certifi/tests/__pycache__/test_certify.cpython-312.pyc,, +certifi/tests/test_certify.py,sha256=fBedHt839-kSsTNmNzX7RsYzjV0OX4kV0Rw7wofSJ7g,467 diff --git a/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/WHEEL b/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/WHEEL new file mode 100644 index 0000000..1d472b6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (83.0.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/licenses/LICENSE new file mode 100644 index 0000000..62b076c --- /dev/null +++ b/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/licenses/LICENSE @@ -0,0 +1,20 @@ +This package contains a modified version of ca-bundle.crt: + +ca-bundle.crt -- Bundle of CA Root Certificates + +This is a bundle of X.509 certificates of public Certificate Authorities +(CA). These were automatically extracted from Mozilla's root certificates +file (certdata.txt). This file can be found in the mozilla source tree: +https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt +It contains the certificates in PEM format and therefore +can be directly used with curl / libcurl / php_curl, or with +an Apache+mod_ssl webserver for SSL client authentication. +Just configure this file as the SSLCACertificateFile.# + +***** BEGIN LICENSE BLOCK ***** +This Source Code Form is subject to the terms of the Mozilla Public License, +v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain +one at http://mozilla.org/MPL/2.0/. + +***** END LICENSE BLOCK ***** +@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $ diff --git a/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/top_level.txt new file mode 100644 index 0000000..963eac5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/certifi-2026.7.22.dist-info/top_level.txt @@ -0,0 +1 @@ +certifi diff --git a/venv/lib/python3.12/site-packages/certifi/__init__.py b/venv/lib/python3.12/site-packages/certifi/__init__.py new file mode 100644 index 0000000..66dae9e --- /dev/null +++ b/venv/lib/python3.12/site-packages/certifi/__init__.py @@ -0,0 +1,4 @@ +from .core import contents, where + +__all__ = ["contents", "where"] +__version__ = "2026.07.22" diff --git a/venv/lib/python3.12/site-packages/certifi/__main__.py b/venv/lib/python3.12/site-packages/certifi/__main__.py new file mode 100644 index 0000000..8945b5d --- /dev/null +++ b/venv/lib/python3.12/site-packages/certifi/__main__.py @@ -0,0 +1,12 @@ +import argparse + +from certifi import contents, where + +parser = argparse.ArgumentParser() +parser.add_argument("-c", "--contents", action="store_true") +args = parser.parse_args() + +if args.contents: + print(contents()) +else: + print(where()) diff --git a/venv/lib/python3.12/site-packages/certifi/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/certifi/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7c4ac50 Binary files /dev/null and b/venv/lib/python3.12/site-packages/certifi/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/certifi/__pycache__/__main__.cpython-312.pyc b/venv/lib/python3.12/site-packages/certifi/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000..63a62f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/certifi/__pycache__/__main__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/certifi/__pycache__/core.cpython-312.pyc b/venv/lib/python3.12/site-packages/certifi/__pycache__/core.cpython-312.pyc new file mode 100644 index 0000000..5c0c1e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/certifi/__pycache__/core.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/certifi/cacert.pem b/venv/lib/python3.12/site-packages/certifi/cacert.pem new file mode 100644 index 0000000..9a40f43 --- /dev/null +++ b/venv/lib/python3.12/site-packages/certifi/cacert.pem @@ -0,0 +1,3959 @@ + +# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Label: "COMODO ECC Certification Authority" +# Serial: 41578283867086692638256921589707938090 +# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 +# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 +# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Subject: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Label: "NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny" +# Serial: 80544274841616 +# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 +# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 +# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Label: "Microsec e-Szigno Root CA 2009" +# Serial: 14014712776195784473 +# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 +# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e +# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Label: "GlobalSign Root CA - R3" +# Serial: 4835703278459759426209954 +# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 +# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad +# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +# Issuer: CN=Izenpe.com O=IZENPE S.A. +# Subject: CN=Izenpe.com O=IZENPE S.A. +# Label: "Izenpe.com" +# Serial: 917563065490389241595536686991402621 +# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 +# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 +# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Services Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 +# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f +# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA" +# Serial: 279744 +# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 +# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e +# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Label: "TWCA Root Certification Authority" +# Serial: 1 +# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 +# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 +# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Label: "Security Communication RootCA2" +# Serial: 0 +# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 +# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 +# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Label: "Actalis Authentication Root CA" +# Serial: 6271844772424770508 +# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 +# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac +# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 2 Root CA" +# Serial: 2 +# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 +# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 +# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 3 Root CA" +# Serial: 2 +# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec +# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 +# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 3" +# Serial: 1 +# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef +# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 +# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 2009" +# Serial: 623603 +# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f +# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 +# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 EV 2009" +# Serial: 623604 +# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 +# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 +# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R2 O=Disig a.s. +# Subject: CN=CA Disig Root R2 O=Disig a.s. +# Label: "CA Disig Root R2" +# Serial: 10572350602393338211 +# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 +# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 +# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Label: "ACCVRAIZ1" +# Serial: 6828503384748696800 +# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 +# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 +# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA Global Root CA" +# Serial: 3262 +# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 +# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 +# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 2" +# Serial: 1 +# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a +# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 +# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 1 G3" +# Serial: 687049649626669250736271037606554624078720034195 +# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab +# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 +# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2 G3" +# Serial: 390156079458959257446133169266079962026824725800 +# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 +# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 +# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3 G3" +# Serial: 268090761170461462463995952157327242137089239581 +# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 +# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d +# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Label: "IdenTrust Commercial Root CA 1" +# Serial: 13298821034946342390520003877796839426 +# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 +# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 +# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Label: "IdenTrust Public Sector Root CA 1" +# Serial: 13298821034946342390521976156843933698 +# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba +# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd +# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority +# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority +# Label: "CFCA EV ROOT" +# Serial: 407555286 +# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 +# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 +# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GB CA" +# Serial: 157768595616588414422159278966750757568 +# MD5 Fingerprint: a4:eb:b9:61:28:2e:b7:2f:98:b0:35:26:90:99:51:1d +# SHA1 Fingerprint: 0f:f9:40:76:18:d3:d7:6a:4b:98:f0:a8:35:9e:0c:fd:27:ac:cc:ed +# SHA256 Fingerprint: 6b:9c:08:e8:6e:b0:f7:67:cf:ad:65:cd:98:b6:21:49:e5:49:4a:67:f5:84:5e:7b:d1:ed:01:9f:27:b8:6b:d6 +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +# Issuer: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Subject: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Label: "SZAFIR ROOT CA2" +# Serial: 357043034767186914217277344587386743377558296292 +# MD5 Fingerprint: 11:64:c1:89:b0:24:b1:8c:b1:07:7e:89:9e:51:9e:99 +# SHA1 Fingerprint: e2:52:fa:95:3f:ed:db:24:60:bd:6e:28:f3:9c:cc:cf:5e:b3:3f:de +# SHA256 Fingerprint: a1:33:9d:33:28:1a:0b:56:e5:57:d3:d3:2b:1c:e7:f9:36:7e:b0:94:bd:5f:a7:2a:7e:50:04:c8:de:d7:ca:fe +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 +ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw +NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg +Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN +QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT +3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw +3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 +3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 +BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN +XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF +AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw +8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG +nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP +oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy +d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg +LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA 2" +# Serial: 44979900017204383099463764357512596969 +# MD5 Fingerprint: 6d:46:9e:d9:25:6d:08:23:5b:5e:74:7d:1e:27:db:f2 +# SHA1 Fingerprint: d3:dd:48:3e:2b:bf:4c:05:e8:af:10:f5:fa:76:26:cf:d3:dc:30:92 +# SHA256 Fingerprint: b6:76:f2:ed:da:e8:77:5c:d3:6c:b0:f6:3c:d1:d4:60:39:61:f4:9e:62:65:ba:01:3a:2f:03:07:b6:d0:b8:04 +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: ca:ff:e2:db:03:d9:cb:4b:e9:0f:ad:84:fd:7b:18:ce +# SHA1 Fingerprint: 01:0c:06:95:a6:98:19:14:ff:bf:5f:c6:b0:b6:95:ea:29:e9:12:a6 +# SHA256 Fingerprint: a0:40:92:9a:02:ce:53:b4:ac:f4:f2:ff:c6:98:1c:e4:49:6f:75:5e:6d:45:fe:0b:2a:69:2b:cd:52:52:3f:36 +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix +DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k +IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT +N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v +dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG +A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh +ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx +QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA +4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 +AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 +4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C +ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV +9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD +gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 +Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq +NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko +LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd +ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I +XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI +M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot +9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V +Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea +j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh +X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ +l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf +bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 +pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK +e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 +vm9qp/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions ECC RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: 81:e5:b4:17:eb:c2:f5:e1:4b:0d:41:7b:49:92:fe:ef +# SHA1 Fingerprint: 9f:f1:71:8d:92:d5:9a:f3:7d:74:97:b4:bc:6f:84:68:0b:ba:b6:66 +# SHA256 Fingerprint: 44:b5:45:aa:8a:25:e6:5a:73:ca:15:dc:27:fc:36:d2:4c:1c:b9:95:3a:06:65:39:b1:15:82:dc:48:7b:48:33 +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN +BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl +bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv +b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ +BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj +YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 +MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 +dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg +QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa +jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi +C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep +lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof +TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X1 O=Internet Security Research Group +# Subject: CN=ISRG Root X1 O=Internet Security Research Group +# Label: "ISRG Root X1" +# Serial: 172886928669790476064670243504169061120 +# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e +# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8 +# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6 +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +# Issuer: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Subject: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Label: "AC RAIZ FNMT-RCM" +# Serial: 485876308206448804701554682760554759 +# MD5 Fingerprint: e2:09:04:b4:d3:bd:d1:a0:14:fd:1a:d2:47:c4:57:1d +# SHA1 Fingerprint: ec:50:35:07:b2:15:c4:95:62:19:e2:a8:9a:5b:42:99:2c:4c:2c:20 +# SHA256 Fingerprint: eb:c5:57:0c:29:01:8c:4d:67:b1:aa:12:7b:af:12:f7:03:b4:61:1e:bc:17:b7:da:b5:57:38:94:17:9b:93:fa +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx +CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ +WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ +BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG +Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ +yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf +BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz +WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF +tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z +374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC +IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL +mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 +wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS +MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 +ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet +UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H +YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 +LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 +RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM +LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf +77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N +JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm +fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp +6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp +1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B +9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok +RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv +uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 1 O=Amazon +# Subject: CN=Amazon Root CA 1 O=Amazon +# Label: "Amazon Root CA 1" +# Serial: 143266978916655856878034712317230054538369994 +# MD5 Fingerprint: 43:c6:bf:ae:ec:fe:ad:2f:18:c6:88:68:30:fc:c8:e6 +# SHA1 Fingerprint: 8d:a7:f9:65:ec:5e:fc:37:91:0f:1c:6e:59:fd:c1:cc:6a:6e:de:16 +# SHA256 Fingerprint: 8e:cd:e6:88:4f:3d:87:b1:12:5b:a3:1a:c3:fc:b1:3d:70:16:de:7f:57:cc:90:4f:e1:cb:97:c6:ae:98:19:6e +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 2 O=Amazon +# Subject: CN=Amazon Root CA 2 O=Amazon +# Label: "Amazon Root CA 2" +# Serial: 143266982885963551818349160658925006970653239 +# MD5 Fingerprint: c8:e5:8d:ce:a8:42:e2:7a:c0:2a:5c:7c:9e:26:bf:66 +# SHA1 Fingerprint: 5a:8c:ef:45:d7:a6:98:59:76:7a:8c:8b:44:96:b5:78:cf:47:4b:1a +# SHA256 Fingerprint: 1b:a5:b2:aa:8c:65:40:1a:82:96:01:18:f8:0b:ec:4f:62:30:4d:83:ce:c4:71:3a:19:c3:9c:01:1e:a4:6d:b4 +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 3 O=Amazon +# Subject: CN=Amazon Root CA 3 O=Amazon +# Label: "Amazon Root CA 3" +# Serial: 143266986699090766294700635381230934788665930 +# MD5 Fingerprint: a0:d4:ef:0b:f7:b5:d8:49:95:2a:ec:f5:c4:fc:81:87 +# SHA1 Fingerprint: 0d:44:dd:8c:3c:8c:1a:1a:58:75:64:81:e9:0f:2e:2a:ff:b3:d2:6e +# SHA256 Fingerprint: 18:ce:6c:fe:7b:f1:4e:60:b2:e3:47:b8:df:e8:68:cb:31:d0:2e:bb:3a:da:27:15:69:f5:03:43:b4:6d:b3:a4 +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 4 O=Amazon +# Subject: CN=Amazon Root CA 4 O=Amazon +# Label: "Amazon Root CA 4" +# Serial: 143266989758080763974105200630763877849284878 +# MD5 Fingerprint: 89:bc:27:d5:eb:17:8d:06:6a:69:d5:fd:89:47:b4:cd +# SHA1 Fingerprint: f6:10:84:07:d6:f8:bb:67:98:0c:c2:e2:44:c2:eb:ae:1c:ef:63:be +# SHA256 Fingerprint: e3:5d:28:41:9e:d0:20:25:cf:a6:90:38:cd:62:39:62:45:8d:a5:c6:95:fb:de:a3:c2:2b:0b:fb:25:89:70:92 +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +# Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" +# Serial: 1 +# MD5 Fingerprint: dc:00:81:dc:69:2f:3e:2f:b0:3b:f6:3d:5a:91:8e:49 +# SHA1 Fingerprint: 31:43:64:9b:ec:ce:27:ec:ed:3a:3f:0b:8f:0d:e4:e8:91:dd:ee:ca +# SHA256 Fingerprint: 46:ed:c3:68:90:46:d5:3a:45:3f:b3:10:4a:b8:0d:ca:ec:65:8b:26:60:ea:16:29:dd:7e:86:79:90:64:87:16 +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx +GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp +bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w +KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 +BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy +dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG +EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll +IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU +QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT +TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg +LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 +a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr +LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr +N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X +YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ +iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f +AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH +V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf +IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 +lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c +8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf +lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +# Issuer: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Subject: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Label: "GDCA TrustAUTH R5 ROOT" +# Serial: 9009899650740120186 +# MD5 Fingerprint: 63:cc:d9:3d:34:35:5c:6f:53:a3:e2:08:70:48:1f:b4 +# SHA1 Fingerprint: 0f:36:38:5b:81:1a:25:c3:9b:31:4e:83:ca:e9:34:66:70:cc:74:b4 +# SHA256 Fingerprint: bf:ff:8f:d0:44:33:48:7d:6a:8a:a6:0c:1a:29:76:7a:9f:c2:bb:b0:5e:42:0f:71:3a:13:b9:92:89:1d:38:93 +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE +BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 +MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w +HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj +Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj +TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u +KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj +qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm +MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 +ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP +zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk +L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC +jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA +HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC +AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm +DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 +COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry +L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf +JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg +IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io +2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV +09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ +XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq +T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe +MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Label: "SSL.com Root Certification Authority RSA" +# Serial: 8875640296558310041 +# MD5 Fingerprint: 86:69:12:c0:70:f1:ec:ac:ac:c2:d5:bc:a5:5b:a1:29 +# SHA1 Fingerprint: b7:ab:33:08:d1:ea:44:77:ba:14:80:12:5a:6f:bd:a9:36:49:0c:bb +# SHA256 Fingerprint: 85:66:6a:56:2e:e0:be:5c:e9:25:c1:d8:89:0a:6f:76:a8:7e:c1:6d:4d:7d:5f:29:ea:74:19:cf:20:12:3b:69 +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK +DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz +OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R +xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX +qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC +C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 +6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh +/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF +YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E +JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc +US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 +ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm ++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi +M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G +A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV +cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc +Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs +PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ +q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 +cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr +a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I +H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y +K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu +nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf +oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY +Ic2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com Root Certification Authority ECC" +# Serial: 8495723813297216424 +# MD5 Fingerprint: 2e:da:e4:39:7f:9c:8f:37:d1:70:9f:26:17:51:3a:8e +# SHA1 Fingerprint: c3:19:7c:39:24:e6:54:af:1b:c4:ab:20:95:7a:e2:c3:0e:13:02:6a +# SHA256 Fingerprint: 34:17:bb:06:cc:60:07:da:1b:96:1c:92:0b:8a:b4:ce:3f:ad:82:0e:4a:a3:0b:9a:cb:c4:a7:4e:bd:ce:bc:65 +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz +WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 +b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS +b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI +7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg +CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD +VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T +kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ +gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority RSA R2" +# Serial: 6248227494352943350 +# MD5 Fingerprint: e1:1e:31:58:1a:ae:54:53:02:f6:17:6a:11:7b:4d:95 +# SHA1 Fingerprint: 74:3a:f0:52:9b:d0:32:a0:f4:4a:83:cd:d4:ba:a9:7b:7c:2e:c4:9a +# SHA256 Fingerprint: 2e:7b:f1:6c:c2:24:85:a7:bb:e2:aa:86:96:75:07:61:b0:ae:39:be:3b:2f:e9:d0:cc:6d:4e:f7:34:91:42:5c +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE +CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy +MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G +A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD +DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq +M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf +OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa +4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 +HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR +aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA +b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ +Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV +PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO +pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu +UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY +MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 +9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW +s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 +Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg +cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM +79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz +/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt +ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm +Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK +QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ +w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi +S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 +mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority ECC" +# Serial: 3182246526754555285 +# MD5 Fingerprint: 59:53:22:65:83:42:01:54:c0:ce:42:b9:5a:7c:f2:90 +# SHA1 Fingerprint: 4c:dd:51:a3:d1:f5:20:32:14:b0:c6:c5:32:23:03:91:c7:46:42:6d +# SHA256 Fingerprint: 22:a2:c1:f7:bd:ed:70:4c:c1:e7:01:b5:f4:08:c3:10:88:0f:e9:56:b5:de:2a:4a:44:f9:9c:87:3a:25:a7:c8 +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx +NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv +bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA +VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku +WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX +5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ +ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg +h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Label: "GlobalSign Root CA - R6" +# Serial: 1417766617973444989252670301619537 +# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae +# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1 +# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69 +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg +MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx +MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET +MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI +xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k +ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD +aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw +LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw +1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX +k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 +SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h +bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n +WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY +rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce +MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu +bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt +Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 +55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj +vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf +cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz +oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp +nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs +pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v +JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R +8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 +5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GC CA" +# Serial: 44084345621038548146064804565436152554 +# MD5 Fingerprint: a9:d6:b9:2d:2f:93:64:f8:a5:69:ca:91:e9:68:07:23 +# SHA1 Fingerprint: e0:11:84:5e:34:de:be:88:81:b9:9c:f6:16:26:d1:96:1f:c3:b9:31 +# SHA256 Fingerprint: 85:60:f9:1c:36:24:da:ba:95:70:b5:fe:a0:db:e3:6f:f1:1a:83:23:be:94:86:85:4f:b3:f3:4a:55:71:19:8d +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw +CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 +bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg +Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ +BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu +ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS +b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni +eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W +p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T +rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV +57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg +Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +# Issuer: CN=UCA Global G2 Root O=UniTrust +# Subject: CN=UCA Global G2 Root O=UniTrust +# Label: "UCA Global G2 Root" +# Serial: 124779693093741543919145257850076631279 +# MD5 Fingerprint: 80:fe:f0:c4:4a:f0:5c:62:32:9f:1c:ba:78:a9:50:f8 +# SHA1 Fingerprint: 28:f9:78:16:19:7a:ff:18:25:18:aa:44:fe:c1:a0:ce:5c:b6:4c:8a +# SHA256 Fingerprint: 9b:ea:11:c9:76:fe:01:47:64:c1:be:56:a6:f9:14:b5:a5:60:31:7a:bd:99:88:39:33:82:e5:16:1a:a0:49:3c +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9 +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH +bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x +CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds +b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr +b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9 +kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm +VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R +VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc +C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj +tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY +D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv +j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl +NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6 +iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP +O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV +ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj +L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl +1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU +b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV +PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj +y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb +EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg +DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI ++Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy +YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX +UB+K+wb1whnw0A== +-----END CERTIFICATE----- + +# Issuer: CN=UCA Extended Validation Root O=UniTrust +# Subject: CN=UCA Extended Validation Root O=UniTrust +# Label: "UCA Extended Validation Root" +# Serial: 106100277556486529736699587978573607008 +# MD5 Fingerprint: a1:f3:5f:43:c6:34:9b:da:bf:8c:7e:05:53:ad:96:e2 +# SHA1 Fingerprint: a3:a1:b0:6f:24:61:23:4a:e3:36:a5:c2:37:fc:a6:ff:dd:f0:d7:3a +# SHA256 Fingerprint: d4:3a:f9:b3:54:73:75:5c:96:84:fc:06:d7:d8:cb:70:ee:5c:28:e7:73:fb:29:4e:b4:1e:e7:17:22:92:4d:24 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF +eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx +MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV +BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog +D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS +sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop +O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk +sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi +c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj +VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz +KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/ +TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G +sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs +1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD +fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN +l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ +VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5 +c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp +4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s +t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj +2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO +vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C +xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx +cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM +fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax +-----END CERTIFICATE----- + +# Issuer: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Subject: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Label: "Certigna Root CA" +# Serial: 269714418870597844693661054334862075617 +# MD5 Fingerprint: 0e:5c:30:62:27:eb:5b:bc:d7:ae:62:ba:e9:d5:df:77 +# SHA1 Fingerprint: 2d:0d:52:14:ff:9e:ad:99:24:01:74:20:47:6e:6c:85:27:27:f5:43 +# SHA256 Fingerprint: d4:8d:3d:23:ee:db:50:a4:59:e5:51:97:60:1c:27:77:4b:9d:7b:18:c9:4d:5a:05:95:11:a1:02:50:b9:31:68 +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw +WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw +MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x +MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD +VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX +BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO +ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M +CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu +I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm +TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh +C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf +ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz +IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT +Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k +JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5 +hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB +GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov +L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo +dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr +aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq +hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L +6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG +HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6 +0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB +lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi +o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1 +gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v +faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 +Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh +jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw +3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign Root CA - G1" +# Serial: 235931866688319308814040 +# MD5 Fingerprint: 9c:42:84:57:dd:cb:0b:a7:2e:95:ad:b6:f3:da:bc:ac +# SHA1 Fingerprint: 8a:c7:ad:8f:73:ac:4e:c1:b5:75:4d:a5:40:f4:fc:cf:7c:b5:8e:8c +# SHA256 Fingerprint: 40:f6:af:03:46:a9:9a:a1:cd:1d:55:5a:4e:9c:ce:62:c7:f9:63:46:03:ee:40:66:15:83:3d:c8:c8:d0:03:67 +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD +VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU +ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH +MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO +MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv +Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz +f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO +8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq +d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM +tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt +Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB +o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x +PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM +wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d +GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH +6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby +RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx +iN66zB+Afko= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign ECC Root CA - G3" +# Serial: 287880440101571086945156 +# MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40 +# SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1 +# SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG +EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo +bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ +TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s +b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0 +WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS +fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB +zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq +hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB +CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD ++JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Label: "emSign Root CA - C1" +# Serial: 825510296613316004955058 +# MD5 Fingerprint: d8:e3:5d:01:21:fa:78:5a:b0:df:ba:d2:ee:2a:5f:68 +# SHA1 Fingerprint: e7:2e:f1:df:fc:b2:09:28:cf:5d:d4:d5:67:37:b1:51:cb:86:4f:01 +# SHA256 Fingerprint: 12:56:09:aa:30:1d:a0:a2:49:b9:7a:82:39:cb:6a:34:21:6f:44:dc:ac:9f:39:54:b1:42:92:f2:e8:c8:60:8f +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG +A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg +SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v +dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ +BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ +HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH +3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH +GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c +xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1 +aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq +TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87 +/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4 +kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG +YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT ++xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo +WXzhriKi4gp6D/piq1JM4fHfyr6DDUI= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Label: "emSign ECC Root CA - C3" +# Serial: 582948710642506000014504 +# MD5 Fingerprint: 3e:53:b3:a3:81:ee:d7:10:f8:d3:b0:1d:17:92:f5:d5 +# SHA1 Fingerprint: b6:af:43:c2:9b:81:53:7d:f6:ef:6b:c3:1f:1f:60:15:0c:ee:48:66 +# SHA256 Fingerprint: bc:4d:80:9b:15:18:9d:78:db:3e:1d:8c:f4:f9:72:6a:79:5d:a1:64:3c:a5:f1:35:8e:1d:db:0e:dc:0d:7e:b3 +-----BEGIN CERTIFICATE----- +MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG +EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx +IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND +IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci +MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti +sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O +BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB +Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c +3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J +0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Label: "Hongkong Post Root CA 3" +# Serial: 46170865288971385588281144162979347873371282084 +# MD5 Fingerprint: 11:fc:9f:bd:73:30:02:8a:fd:3f:f3:58:b9:cb:20:f0 +# SHA1 Fingerprint: 58:a2:d0:ec:20:52:81:5b:c1:f3:f8:64:02:24:4e:c2:8e:02:4b:02 +# SHA256 Fingerprint: 5a:2f:c0:3f:0c:83:b0:90:bb:fa:40:60:4b:09:88:44:6c:76:36:18:3d:f9:84:6e:17:10:1a:44:7f:b8:ef:d6 +-----BEGIN CERTIFICATE----- +MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL +BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ +SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n +a29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5 +NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcT +CUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9u +Z2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFO +dem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI +VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV +9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY +2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKY +vLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+Tt +bNe/JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZb +x39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+ +l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YK +TE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKvQv83j+Gj +Hno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e +i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEw +DQYJKoZIhvcNAQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG +7BJ8dNVI0lkUmcDrudHr9EgwW62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCk +MpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWldy8joRTnU+kLBEUx3XZL7av9YROXr +gZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov+BS5gLNdTaqX4fnk +GMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDceqFS +3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJm +Ozj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+ +l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6c +JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP +L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa +LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG +mpv0 +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft ECC Root Certificate Authority 2017" +# Serial: 136839042543790627607696632466672567020 +# MD5 Fingerprint: dd:a1:03:e6:4a:93:10:d1:bf:f0:19:42:cb:fe:ed:67 +# SHA1 Fingerprint: 99:9a:64:c3:7f:f4:7d:9f:ab:95:f1:47:69:89:14:60:ee:c4:c3:c5 +# SHA256 Fingerprint: 35:8d:f3:9d:76:4a:f9:e1:b7:66:e9:c9:72:df:35:2e:e1:5c:fa:c2:27:af:6a:d1:d7:0e:8e:4a:6e:dc:ba:02 +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD +VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw +MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy +b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR +ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb +hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 +FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV +L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB +iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft RSA Root Certificate Authority 2017" +# Serial: 40975477897264996090493496164228220339 +# MD5 Fingerprint: 10:ff:00:ff:cf:c9:f8:c7:7a:c0:ee:35:8e:c9:0f:47 +# SHA1 Fingerprint: 73:a5:e6:4a:3b:ff:83:16:ff:0e:dc:cc:61:8a:90:6e:4e:ae:4d:74 +# SHA256 Fingerprint: c7:41:f7:0f:4b:2a:8d:88:bf:2e:71:c1:41:22:ef:53:ef:10:eb:a0:cf:a5:e6:4c:fa:20:f4:18:85:30:73:e0 +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N +aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ +Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 +ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 +HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm +gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ +jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc +aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG +YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 +W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K +UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH ++FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q +W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC +LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC +gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 +tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh +SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 +TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 +pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR +xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp +GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 +dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN +AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB +RA+GsCyRxj3qrg+E +-----END CERTIFICATE----- + +# Issuer: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Subject: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Label: "e-Szigno Root CA 2017" +# Serial: 411379200276854331539784714 +# MD5 Fingerprint: de:1f:f6:9e:84:ae:a7:b4:21:ce:1e:58:7d:d1:84:98 +# SHA1 Fingerprint: 89:d4:83:03:4f:9e:9a:48:80:5f:72:37:d4:a9:a6:ef:cb:7c:1f:d1 +# SHA256 Fingerprint: be:b0:0b:30:83:9b:9b:c3:2c:32:e4:44:79:05:95:06:41:f2:64:21:b1:5e:d0:89:19:8b:51:8a:e2:ea:1b:99 +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV +BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk +LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv +b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ +BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg +THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v +IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv +xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H +Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB +eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo +jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ ++efcMQ== +-----END CERTIFICATE----- + +# Issuer: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Subject: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Label: "certSIGN Root CA G2" +# Serial: 313609486401300475190 +# MD5 Fingerprint: 8c:f1:75:8a:c6:19:cf:94:b7:f7:65:20:87:c3:97:c7 +# SHA1 Fingerprint: 26:f9:93:b4:ed:3d:28:27:b0:b9:4b:a7:e9:15:1d:a3:8d:92:e5:32 +# SHA256 Fingerprint: 65:7c:fe:2f:a7:3f:aa:38:46:25:71:f3:32:a2:36:3a:46:fc:e7:02:09:51:71:07:02:cd:fb:b6:ee:da:33:05 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV +BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g +Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ +BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ +R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF +dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw +vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ +uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp +n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs +cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW +xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P +rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF +DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx +DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy +LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C +eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ +d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq +kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl +qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0 +OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c +NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk +ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO +pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj +03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk +PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE +1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX +QRBdJ3NghVdJIgc= +-----END CERTIFICATE----- + +# Issuer: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Subject: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Label: "NAVER Global Root Certification Authority" +# Serial: 9013692873798656336226253319739695165984492813 +# MD5 Fingerprint: c8:7e:41:f6:25:3b:f5:09:b3:17:e8:46:3d:bf:d0:9b +# SHA1 Fingerprint: 8f:6b:f2:a9:27:4a:da:14:a0:c4:f4:8e:61:27:f9:c0:1e:78:5d:d1 +# SHA256 Fingerprint: 88:f4:38:dc:f8:ff:d1:fa:8f:42:91:15:ff:e5:f8:2a:e1:e0:6e:0c:70:c3:75:fa:ad:71:7b:34:a4:9e:72:65 +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM +BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG +T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkx +CzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVTUyBQTEFURk9STSBD +b3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVA +iQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH +38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE +HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZaz +kVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYP +szuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8Obtoq +vC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHf +nZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaG +YQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo +0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3a +CJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzClrkvJE+2K +AQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I +36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoN +qo0hV4/GPnrK21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatj +cu3cvuzHV+YwIHHW1xDBE1UBjCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm ++LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bxhYTeodoS76TiEJd6eN4MUZeoIUCL +hr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTgE34h5prCy8VCZLQe +lHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTHD8z7 +p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8 +piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR +LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX +5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO +dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul +9XXeifdy +-----END CERTIFICATE----- + +# Issuer: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Subject: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Label: "AC RAIZ FNMT-RCM SERVIDORES SEGUROS" +# Serial: 131542671362353147877283741781055151509 +# MD5 Fingerprint: 19:36:9c:52:03:2f:d2:d1:bb:23:cc:dd:1e:12:55:bb +# SHA1 Fingerprint: 62:ff:d9:9e:c0:65:0d:03:ce:75:93:d2:ed:3f:2d:32:c9:e3:e5:4a +# SHA256 Fingerprint: 55:41:53:b1:3d:2c:f9:dd:b7:53:bf:be:1a:4e:0a:e0:8d:0a:a4:18:70:58:fe:60:a2:b8:62:b2:e4:b8:7b:cb +-----BEGIN CERTIFICATE----- +MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw +CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw +FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S +Q00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5 +MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQL +DAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNBQyBS +QUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LH +sbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK +Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu +SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC +MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy +v+c= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Label: "GlobalSign Root R46" +# Serial: 1552617688466950547958867513931858518042577 +# MD5 Fingerprint: c4:14:30:e4:fa:66:43:94:2a:6a:1b:24:5f:19:d0:ef +# SHA1 Fingerprint: 53:a2:b0:4b:ca:6b:d6:45:e6:39:8a:8e:c4:0d:d2:bf:77:c3:a2:90 +# SHA256 Fingerprint: 4f:a3:12:6d:8d:3a:11:d1:c4:85:5a:4f:80:7c:ba:d6:cf:91:9d:3a:5a:88:b0:3b:ea:2c:63:72:d9:3c:40:c9 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA +MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD +VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy +MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt +c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ +OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG +vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud +316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo +0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE +y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF +zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE ++cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN +I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs +x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa +ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC +4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4 +7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti +2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk +pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF +FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt +rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk +ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5 +u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP +4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 +N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 +vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Label: "GlobalSign Root E46" +# Serial: 1552617690338932563915843282459653771421763 +# MD5 Fingerprint: b5:b8:66:ed:de:08:83:e3:c9:e2:01:34:06:ac:51:6f +# SHA1 Fingerprint: 39:b4:6c:d5:fe:80:06:eb:e2:2f:4a:bb:08:33:a0:af:db:b9:dd:84 +# SHA256 Fingerprint: cb:b9:c4:4d:84:b8:04:3e:10:50:ea:31:a6:9f:51:49:55:d7:bf:d2:e2:c6:b4:93:01:01:9a:d6:1d:9f:50:58 +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx +CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD +ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw +MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex +HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq +R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd +yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ +7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 ++RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- + +# Issuer: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Subject: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Label: "ANF Secure Server Root CA" +# Serial: 996390341000653745 +# MD5 Fingerprint: 26:a6:44:5a:d9:af:4e:2f:b2:1d:b6:65:b0:4e:e8:96 +# SHA1 Fingerprint: 5b:6e:68:d0:cc:15:b6:a0:5f:1e:c1:5f:ae:02:fc:6b:2f:5d:6f:74 +# SHA256 Fingerprint: fb:8f:ec:75:91:69:b9:10:6b:1e:51:16:44:c6:18:c5:13:04:37:3f:6c:06:43:08:8d:8b:ef:fd:1b:99:75:99 +-----BEGIN CERTIFICATE----- +MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV +BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk +YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV +BAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3QgQ0EwHhcNMTkwOTA0MTAwMDM4WhcN +MzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEwMQswCQYDVQQGEwJF +UzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQwEgYD +VQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9v +dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCj +cqQZAZ2cC4Ffc0m6p6zzBE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9q +yGFOtibBTI3/TO80sh9l2Ll49a2pcbnvT1gdpd50IJeh7WhM3pIXS7yr/2WanvtH +2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcvB2VSAKduyK9o7PQUlrZX +H1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXsezx76W0OL +zc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyR +p1RMVwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQz +W7i1o0TJrH93PB0j7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/ +SiOL9V8BY9KHcyi1Swr1+KuCLH5zJTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJn +LNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe8TZBAQIvfXOn3kLMTOmJDVb3 +n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVOHj1tyRRM4y5B +u8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj +o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC +AgEATh65isagmD9uw2nAalxJUqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L +9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzxj6ptBZNscsdW699QIyjlRRA96Gej +rw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDtdD+4E5UGUcjohybK +pFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM5gf0 +vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjq +OknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ +/zo1PqVUSlJZS2Db7v54EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ9 +2zg/LFis6ELhDtjTO0wugumDLmsx2d1Hhk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI ++PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2 +MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoo +tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= +-----END CERTIFICATE----- + +# Issuer: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum EC-384 CA" +# Serial: 160250656287871593594747141429395092468 +# MD5 Fingerprint: b6:65:b3:96:60:97:12:a1:ec:4e:e1:3d:a3:c6:c9:f1 +# SHA1 Fingerprint: f3:3e:78:3c:ac:df:f4:a2:cc:ac:67:55:69:56:d7:e5:16:3c:e1:ed +# SHA256 Fingerprint: 6b:32:80:85:62:53:18:aa:50:d1:73:c9:8d:8b:da:09:d5:7e:27:41:3d:11:4c:f7:87:a0:f5:d0:6c:03:0c:f6 +-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw +CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw +JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT +EENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0 +WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBT +LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAX +BgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATE +KI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7Tm +Fy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8 +EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J +UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn +nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Root CA" +# Serial: 40870380103424195783807378461123655149 +# MD5 Fingerprint: 51:e1:c2:e7:fe:4c:84:af:59:0e:2f:f4:54:6f:ea:29 +# SHA1 Fingerprint: c8:83:44:c0:18:ae:9f:cc:f1:87:b7:8f:22:d1:c5:d7:45:84:ba:e5 +# SHA256 Fingerprint: fe:76:96:57:38:55:77:3e:37:a9:5e:7a:d4:d9:cc:96:c3:01:57:c1:5d:31:76:5b:a9:b1:57:04:e1:ae:78:fd +-----BEGIN CERTIFICATE----- +MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6 +MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu +MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV +BAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMw +MzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEg +U3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZ +n0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q +p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQq +NwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF +8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3 +HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGa +mqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi +7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSF +ytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0P +qafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khUHL4wvYBQ +v3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6 +Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 +vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQAD +ggIBAEii1QALLtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4 +WxmB82M+w85bj/UvXgF2Ez8sALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvo +zMrnadyHncI013nR03e4qllY/p0m+jiGPp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR +5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8CYyqOhNf6DR5UMEQ +GfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA4kZf +5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq +0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D +P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTM +qJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP +0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZf +E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb +-----END CERTIFICATE----- + +# Issuer: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique +# Subject: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique +# Label: "TunTrust Root CA" +# Serial: 108534058042236574382096126452369648152337120275 +# MD5 Fingerprint: 85:13:b9:90:5b:36:5c:b6:5e:b8:5a:f8:e0:31:57:b4 +# SHA1 Fingerprint: cf:e9:70:84:0f:e0:73:0f:9d:f6:0c:7f:2c:4b:ee:20:46:34:9c:bb +# SHA256 Fingerprint: 2e:44:10:2a:b5:8c:b8:54:19:45:1c:8e:19:d9:ac:f3:66:2c:af:bc:61:4b:6a:53:96:0a:30:f7:d0:e2:eb:41 +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL +BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg +Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv +b3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQG +EwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9u +IEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZ +n56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd +2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF +VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZ +GoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOF +li/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnU +r8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2 +eY8fTpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIb +MlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISg +jwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB +7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdOvsC9qnyW +5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE +ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 +90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+z +xiD2BkewhpMl0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYu +QEkHDVneixCwSQXi/5E/S7fdAo74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4 +FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH +22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJpadbGNjHh/PqAulxP +xOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65xxBzn +dFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5 +Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b +nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQ +CvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZH +u/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujj +d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= +-----END CERTIFICATE----- + +# Issuer: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Subject: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Label: "HARICA TLS RSA Root CA 2021" +# Serial: 76817823531813593706434026085292783742 +# MD5 Fingerprint: 65:47:9b:58:86:dd:2c:f0:fc:a2:84:1f:1e:96:c4:91 +# SHA1 Fingerprint: 02:2d:05:82:fa:88:ce:14:0c:06:79:de:7f:14:10:e9:45:d7:a5:6d +# SHA256 Fingerprint: d9:5d:0e:8e:da:79:52:5b:f9:be:b1:1b:14:d2:10:0d:32:94:98:5f:0c:62:d9:fa:bd:9c:d9:99:ec:cb:7b:1d +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs +MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg +Um9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUzOFoXDTQ1MDIxMzEwNTUzN1owbDEL +MAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNBIFJv +b3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569l +mwVnlskNJLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE +4VGC/6zStGndLuwRo0Xua2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uv +a9of08WRiFukiZLRgeaMOVig1mlDqa2YUlhu2wr7a89o+uOkXjpFc5gH6l8Cct4M +pbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K5FrZx40d/JiZ+yykgmvw +Kh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEvdmn8kN3b +LW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcY +AuUR0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqB +AGMUuTNe3QvboEUHGjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYq +E613TBoYm5EPWNgGVMWX+Ko/IIqmhaZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHr +W2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQCPxrvrNQKlr9qEgYRtaQQJKQ +CoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAU +X15QvWiWkKQUEapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3 +f5Z2EMVGpdAgS1D0NTsY9FVqQRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxaja +H6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxDQpSbIPDRzbLrLFPCU3hKTwSUQZqP +JzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcRj88YxeMn/ibvBZ3P +zzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5vZSt +jBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0 +/L5H9MG0qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pT +BGIBnfHAT+7hOtSLIBD6Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79 +aPib8qXPMThcFarmlwDB31qlpzmq6YR/PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YW +xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU +63ZTGI0RmLo= +-----END CERTIFICATE----- + +# Issuer: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Subject: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Label: "HARICA TLS ECC Root CA 2021" +# Serial: 137515985548005187474074462014555733966 +# MD5 Fingerprint: ae:f7:4c:e5:66:35:d1:b7:9b:8c:22:93:74:d3:4b:b0 +# SHA1 Fingerprint: bc:b0:c1:9d:e9:98:92:70:19:38:57:e9:8d:a7:b4:5d:6e:ee:01:48 +# SHA256 Fingerprint: 3f:99:cc:47:4a:cf:ce:4d:fe:d5:87:94:66:5e:47:8d:15:47:73:9f:2e:78:0f:1b:b4:ca:9b:13:30:97:d4:01 +-----BEGIN CERTIFICATE----- +MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw +CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh +cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v +dCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkG +A1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJvb3Qg +Q0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7 +KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9Y +STHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQD +AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw +SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN +nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 1977337328857672817 +# MD5 Fingerprint: 4e:6e:9b:54:4c:ca:b7:fa:48:e4:90:b1:15:4b:1c:a3 +# SHA1 Fingerprint: 0b:be:c2:27:22:49:cb:39:aa:db:35:5c:53:e3:8c:ae:78:ff:b6:fe +# SHA256 Fingerprint: 57:de:05:83:ef:d2:b2:6e:03:61:da:99:da:9d:f4:64:8d:ef:7e:e8:44:1c:3b:72:8a:fa:9b:cd:e0:f9:b2:6a +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1 +MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc +tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd +IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j +b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC +AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw +ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m +iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF +Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ +hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P +Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE +EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV +1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t +CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR +5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw +f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9 +ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK +GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV +-----END CERTIFICATE----- + +# Issuer: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. +# Subject: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. +# Label: "vTrus ECC Root CA" +# Serial: 630369271402956006249506845124680065938238527194 +# MD5 Fingerprint: de:4b:c1:f5:52:8c:9b:43:e1:3e:8f:55:54:17:8d:85 +# SHA1 Fingerprint: f6:9c:db:b0:fc:f6:02:13:b6:52:32:a6:a3:91:3f:16:70:da:c3:e1 +# SHA256 Fingerprint: 30:fb:ba:2c:32:23:8e:2a:98:54:7a:f9:79:31:e5:50:42:8b:9b:3f:1c:8e:eb:66:33:dc:fa:86:c5:b2:7d:d3 +-----BEGIN CERTIFICATE----- +MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMw +RzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAY +BgNVBAMTEXZUcnVzIEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDcz +MTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28u +LEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+cToL0 +v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUd +e4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw +V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sA +AjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEG +GJTO +-----END CERTIFICATE----- + +# Issuer: CN=vTrus Root CA O=iTrusChina Co.,Ltd. +# Subject: CN=vTrus Root CA O=iTrusChina Co.,Ltd. +# Label: "vTrus Root CA" +# Serial: 387574501246983434957692974888460947164905180485 +# MD5 Fingerprint: b8:c9:37:df:fa:6b:31:84:64:c5:ea:11:6a:1b:75:fc +# SHA1 Fingerprint: 84:1a:69:fb:f5:cd:1a:25:34:13:3d:e3:f8:fc:b8:99:d0:c9:14:b7 +# SHA256 Fingerprint: 8a:71:de:65:59:33:6f:42:6c:26:e5:38:80:d0:0d:88:a1:8d:a4:c6:a9:1f:0d:cb:61:94:e2:06:c5:c9:63:87 +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQEL +BQAwQzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4x +FjAUBgNVBAMTDXZUcnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMx +MDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4s +THRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZotsSKYc +IrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykU +AyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+ +GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9 +8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdH +flqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajt +J8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim +0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZN +pGvu/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQ +UqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHW +OXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMB +AAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet +8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd +nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1j +bhd47F18iMjrjld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvM +Kar5CKXiNxTKsbhm7xqC5PD48acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIiv +TDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJnxDHO2zTlJQNgJXtxmOTAGytfdELS +S8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554WgicEFOwE30z9J4nfr +I8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4sEb9 +b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNB +UvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P +Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929ven +sBxXVsFy6K2ir40zSbofitzmdHxghm+Hl3s= +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X2 O=Internet Security Research Group +# Subject: CN=ISRG Root X2 O=Internet Security Research Group +# Label: "ISRG Root X2" +# Serial: 87493402998870891108772069816698636114 +# MD5 Fingerprint: d3:9e:c4:1e:23:3c:a6:df:cf:a3:7e:6d:e0:14:e6:e5 +# SHA1 Fingerprint: bd:b1:b9:3c:d5:97:8d:45:c6:26:14:55:f8:db:95:c7:5a:d1:53:af +# SHA256 Fingerprint: 69:72:9b:8e:15:a8:6e:fc:17:7a:57:af:b7:17:1d:fc:64:ad:d2:8c:2f:ca:8c:f1:50:7e:34:45:3c:cb:14:70 +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw +CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg +R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 +MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT +ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW ++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 +ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI +zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW +tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 +/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- + +# Issuer: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. +# Subject: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. +# Label: "HiPKI Root CA - G1" +# Serial: 60966262342023497858655262305426234976 +# MD5 Fingerprint: 69:45:df:16:65:4b:e8:68:9a:8f:76:5f:ff:80:9e:d3 +# SHA1 Fingerprint: 6a:92:e4:a8:ee:1b:ec:96:45:37:e3:29:57:49:cd:96:e3:e5:d2:60 +# SHA256 Fingerprint: f0:15:ce:3c:c2:39:bf:ef:06:4b:e9:f1:d2:c4:17:e1:a0:26:4a:0a:94:be:1f:0c:8d:12:18:64:eb:69:49:cc +-----BEGIN CERTIFICATE----- +MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa +Fw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3 +YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0o9Qw +qNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twv +Vcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6 +lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnz +Qs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRtU6M9/Aes1MU3guvklQgZ +KILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfdhSi8MEyr48KxRURHH+CK +FgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj1jOXTyFj +HluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDr +y+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ +/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgM +a/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6 +fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQDAgGGMA0GCSqG +SIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi +7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqc +SE5XCV0vrPSltJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6Fza +ZsT0pPBWGTMpWmWSBUdGSquEwx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9Tc +XzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07QJNBAsNB1CI69aO4I1258EHBGG3zg +iLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv5wiZqAxeJoBF1Pho +L5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+GpzjLrF +Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr +kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+ +vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU +YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 159662223612894884239637590694 +# MD5 Fingerprint: 26:29:f8:6d:e1:88:bf:a2:65:7f:aa:c4:cd:0f:7f:fc +# SHA1 Fingerprint: 6b:a0:b0:98:e1:71:ef:5a:ad:fe:48:15:80:77:10:f4:bd:6f:0b:28 +# SHA256 Fingerprint: b0:85:d7:0b:96:4f:19:1a:73:e4:af:0d:54:ae:7a:0e:07:aa:fd:af:9b:71:dd:08:62:13:8a:b7:32:5a:24:a2 +-----BEGIN CERTIFICATE----- +MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD +VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw +MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g +UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx +uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV +HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/ ++wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147 +bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R1 O=Google Trust Services LLC +# Subject: CN=GTS Root R1 O=Google Trust Services LLC +# Label: "GTS Root R1" +# Serial: 159662320309726417404178440727 +# MD5 Fingerprint: 05:fe:d0:bf:71:a8:a3:76:63:da:01:e0:d8:52:dc:40 +# SHA1 Fingerprint: e5:8c:1c:c4:91:3b:38:63:4b:e9:10:6e:e3:ad:8e:6b:9d:d9:81:4a +# SHA256 Fingerprint: d9:47:43:2a:bd:e7:b7:fa:90:fc:2e:6b:59:10:1b:12:80:e0:e1:c7:e4:e4:0f:a3:c6:88:7f:ff:57:a7:f4:cf +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo +27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w +Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw +TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl +qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH +szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 +Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk +MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p +aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN +VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb +C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy +h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 +7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J +ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef +MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ +Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT +6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ +0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm +2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb +bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R3 O=Google Trust Services LLC +# Subject: CN=GTS Root R3 O=Google Trust Services LLC +# Label: "GTS Root R3" +# Serial: 159662495401136852707857743206 +# MD5 Fingerprint: 3e:e7:9d:58:02:94:46:51:94:e5:e0:22:4a:8b:e7:73 +# SHA1 Fingerprint: ed:e5:71:80:2b:c8:92:b9:5b:83:3c:d2:32:68:3f:09:cd:a0:1e:46 +# SHA256 Fingerprint: 34:d8:a7:3e:e2:08:d9:bc:db:0d:95:65:20:93:4b:4e:40:e6:94:82:59:6e:8b:6f:73:c8:42:6b:01:0a:6f:48 +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G +jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2 +4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7 +VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm +ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R4 O=Google Trust Services LLC +# Subject: CN=GTS Root R4 O=Google Trust Services LLC +# Label: "GTS Root R4" +# Serial: 159662532700760215368942768210 +# MD5 Fingerprint: 43:96:83:77:19:4d:76:b3:9d:65:52:e4:1d:22:a5:e8 +# SHA1 Fingerprint: 77:d3:03:67:b5:e0:0c:15:f6:0c:38:61:df:7c:e1:3b:92:46:4d:47 +# SHA256 Fingerprint: 34:9d:fa:40:58:c5:e2:63:12:3b:39:8a:e7:95:57:3c:4e:13:13:c8:3f:e6:8f:93:55:6c:d5:e8:03:1b:3c:7d +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi +QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR +HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D +9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 +p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD +-----END CERTIFICATE----- + +# Issuer: CN=Telia Root CA v2 O=Telia Finland Oyj +# Subject: CN=Telia Root CA v2 O=Telia Finland Oyj +# Label: "Telia Root CA v2" +# Serial: 7288924052977061235122729490515358 +# MD5 Fingerprint: 0e:8f:ac:aa:82:df:85:b1:f4:dc:10:1c:fc:99:d9:48 +# SHA1 Fingerprint: b9:99:cd:d1:73:50:8a:c4:47:05:08:9c:8c:88:fb:be:a0:2b:40:cd +# SHA256 Fingerprint: 24:2b:69:74:2f:cb:1e:5b:2a:bf:98:89:8b:94:57:21:87:54:4e:5b:4d:99:11:78:65:73:62:1f:6a:74:b8:2c +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx +CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE +AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1 +NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZ +MBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ76zBq +AMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9 +vVYiQJ3q9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9 +lRdU2HhE8Qx3FZLgmEKnpNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTOD +n3WhUidhOPFZPY5Q4L15POdslv5e2QJltI5c0BE0312/UqeBAMN/mUWZFdUXyApT +7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW5olWK8jjfN7j/4nlNW4o +6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNrRBH0pUPC +TEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6 +WT0EBXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63R +DolUK5X6wK0dmBR4M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZI +pEYslOqodmJHixBTB0hXbOKSTbauBcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGj +YzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7Wxy+G2CQ5MB0GA1UdDgQWBBRy +rOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ +8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi +0f6X+J8wfBj5tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMM +A8iZGok1GTzTyVR8qPAs5m4HeW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBS +SRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+Cy748fdHif64W1lZYudogsYMVoe+K +TTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygCQMez2P2ccGrGKMOF +6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15h2Er +3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMt +Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT +VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW +ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA +rBPuUBQemMc= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH +# Subject: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH +# Label: "D-TRUST BR Root CA 1 2020" +# Serial: 165870826978392376648679885835942448534 +# MD5 Fingerprint: b5:aa:4b:d5:ed:f7:e3:55:2e:8f:72:0a:f3:75:b8:ed +# SHA1 Fingerprint: 1f:5b:98:f0:e3:b5:f7:74:3c:ed:e6:b0:36:7d:32:cd:f4:09:41:67 +# SHA256 Fingerprint: e5:9a:aa:81:60:09:c2:2b:ff:5b:25:ba:d3:7d:f3:06:f0:49:79:7c:1f:81:d8:5a:b0:89:e6:57:bd:8f:00:44 +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5 +NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7dPYS +zuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0 +QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/ +VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW +wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV +dWNbFJWcHwHP2NVypw87 +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH +# Subject: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH +# Label: "D-TRUST EV Root CA 1 2020" +# Serial: 126288379621884218666039612629459926992 +# MD5 Fingerprint: 8c:2d:9d:70:9f:48:99:11:06:11:fb:e9:cb:30:c0:6e +# SHA1 Fingerprint: 61:db:8c:21:59:69:03:90:d8:7c:9c:12:86:54:cf:9d:3d:f4:dd:07 +# SHA256 Fingerprint: 08:17:0d:1a:a3:64:53:90:1a:2f:95:92:45:e3:47:db:0c:8d:37:ab:aa:bc:56:b8:1a:a1:00:dc:95:89:70:db +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5 +NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8ZRCC +/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rD +wpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3 +OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA +y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb +gfM0agPnIjhQW+0ZT0MW +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. +# Subject: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. +# Label: "DigiCert TLS ECC P384 Root G5" +# Serial: 13129116028163249804115411775095713523 +# MD5 Fingerprint: d3:71:04:6a:43:1c:db:a6:59:e1:a8:a3:aa:c5:71:ed +# SHA1 Fingerprint: 17:f3:de:5e:9f:0f:19:e9:8e:f6:1f:32:26:6e:20:c4:07:ae:30:ee +# SHA256 Fingerprint: 01:8e:13:f0:77:25:32:cf:80:9b:d1:b1:72:81:86:72:83:fc:48:c6:e1:3b:e9:c6:98:12:85:4a:49:0c:1b:05 +-----BEGIN CERTIFICATE----- +MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp +Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2 +MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ +bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS +7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp +0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS +B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 +BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ +LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4 +DXZDjC5Ty3zfDBeWUA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. +# Subject: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. +# Label: "DigiCert TLS RSA4096 Root G5" +# Serial: 11930366277458970227240571539258396554 +# MD5 Fingerprint: ac:fe:f7:34:96:a9:f2:b3:b4:12:4b:e4:27:41:6f:e1 +# SHA1 Fingerprint: a7:88:49:dc:5d:7c:75:8c:8c:de:39:98:56:b3:aa:d0:b2:a5:71:35 +# SHA256 Fingerprint: 37:1a:00:dc:05:33:b3:72:1a:7e:eb:40:e8:41:9e:70:79:9d:2b:0a:0f:2c:1d:80:69:31:65:f7:ce:c4:ad:75 +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT +HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN +NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs +IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+ +ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0 +2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp +wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM +pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD +nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po +sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx +Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd +Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX +KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe +XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL +tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv +TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN +AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw +GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H +PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF +O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ +REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik +AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv +/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+ +p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw +MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF +qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK +ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+ +-----END CERTIFICATE----- + +# Issuer: CN=Certainly Root R1 O=Certainly +# Subject: CN=Certainly Root R1 O=Certainly +# Label: "Certainly Root R1" +# Serial: 188833316161142517227353805653483829216 +# MD5 Fingerprint: 07:70:d4:3e:82:87:a0:fa:33:36:13:f4:fa:33:e7:12 +# SHA1 Fingerprint: a0:50:ee:0f:28:71:f4:27:b2:12:6d:6f:50:96:25:ba:cc:86:42:af +# SHA256 Fingerprint: 77:b8:2c:d8:64:4c:43:05:f7:ac:c5:cb:15:6b:45:67:50:04:03:3d:51:c6:0c:62:02:a8:e0:c3:34:67:d3:a0 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw +PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy +dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9 +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0 +YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2 +1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT +vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed +aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0 +1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5 +r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5 +cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ +wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ +6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA +2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH +Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR +eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB +/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u +d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr +PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d +8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi +1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd +rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di +taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7 +lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj +yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn +Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy +yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n +wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6 +OV+KmalBWQewLK8= +-----END CERTIFICATE----- + +# Issuer: CN=Certainly Root E1 O=Certainly +# Subject: CN=Certainly Root E1 O=Certainly +# Label: "Certainly Root E1" +# Serial: 8168531406727139161245376702891150584 +# MD5 Fingerprint: 0a:9e:ca:cd:3e:52:50:c6:36:f3:4b:a3:ed:a7:53:e9 +# SHA1 Fingerprint: f9:e1:6d:dc:01:89:cf:d5:82:45:63:3e:c5:37:7d:c2:eb:93:6f:2b +# SHA256 Fingerprint: b4:58:5f:22:e4:ac:75:6a:4e:86:12:a1:36:1c:5d:9d:03:1a:93:fd:84:fe:bb:77:8f:a3:06:8b:0f:c4:2d:c2 +-----BEGIN CERTIFICATE----- +MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw +CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu +bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ +BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s +eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK ++IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2 +QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4 +hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm +ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG +BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR +-----END CERTIFICATE----- + +# Issuer: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. +# Subject: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. +# Label: "Security Communication ECC RootCA1" +# Serial: 15446673492073852651 +# MD5 Fingerprint: 7e:43:b0:92:68:ec:05:43:4c:98:ab:5d:35:2e:7e:86 +# SHA1 Fingerprint: b8:0e:26:a9:bf:d2:b2:3b:c0:ef:46:c9:ba:c7:bb:f6:1d:0d:41:41 +# SHA256 Fingerprint: e7:4f:bd:a5:5b:d5:64:c4:73:a3:6b:44:1a:a7:99:c8:a6:8e:07:74:40:e8:28:8b:9f:a1:e5:0e:4b:ba:ca:11 +-----BEGIN CERTIFICATE----- +MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT +AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD +VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx +NjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoT +HFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNVBAMTIlNlY3VyaXR5 +IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+Cnnfdl +dB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpK +ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu +9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O +be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k= +-----END CERTIFICATE----- + +# Issuer: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY +# Subject: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY +# Label: "BJCA Global Root CA1" +# Serial: 113562791157148395269083148143378328608 +# MD5 Fingerprint: 42:32:99:76:43:33:36:24:35:07:82:9b:28:f9:d0:90 +# SHA1 Fingerprint: d5:ec:8d:7b:4c:ba:79:f4:e7:e8:cb:9d:6b:ae:77:83:10:03:21:6a +# SHA256 Fingerprint: f3:89:6f:88:fe:7c:0a:88:27:66:a7:fa:6a:d2:74:9f:b5:7a:7f:3e:98:fb:76:9c:1f:a7:b0:9c:2c:44:d5:ae +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBU +MQswCQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRI +T1JJVFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAz +MTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJF +SUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2Jh +bCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFmCL3Z +xRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZ +spDyRhySsTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O5 +58dnJCNPYwpj9mZ9S1WnP3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgR +at7GGPZHOiJBhyL8xIkoVNiMpTAK+BcWyqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll +5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRjeulumijWML3mG90Vr4Tq +nMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNnMoH1V6XK +V0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/ +pj+bOT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZO +z2nxbkRs1CTqjSShGL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXn +jSXWgXSHRtQpdaJCbPdzied9v3pKH9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+ +WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMBAAGjQjBAMB0GA1UdDgQWBBTF +7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4 +YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3Kli +awLwQ8hOnThJdMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u ++2D2/VnGKhs/I0qUJDAnyIm860Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88 +X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuhTaRjAv04l5U/BXCga99igUOLtFkN +SoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW4AB+dAb/OMRyHdOo +P2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmpGQrI ++pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRz +znfSxqxx4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9 +eVzYH6Eze9mCUAyTF6ps3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2 +YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4SSPfSKcOYKMryMguTjClPPGAyzQWWYezy +r/6zcCwupvI= +-----END CERTIFICATE----- + +# Issuer: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY +# Subject: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY +# Label: "BJCA Global Root CA2" +# Serial: 58605626836079930195615843123109055211 +# MD5 Fingerprint: 5e:0a:f6:47:5f:a6:14:e8:11:01:95:3f:4d:01:eb:3c +# SHA1 Fingerprint: f4:27:86:eb:6e:b8:6d:88:31:67:02:fb:ba:66:a4:53:00:aa:7a:a6 +# SHA256 Fingerprint: 57:4d:f6:93:1e:27:80:39:66:7b:72:0a:fd:c1:60:0f:c2:7e:b6:6d:d3:09:29:79:fb:73:85:64:87:21:28:82 +-----BEGIN CERTIFICATE----- +MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw +CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ +VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy +MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ +TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS +b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B +IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+ ++kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK +sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA +94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B +43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w== +-----END CERTIFICATE----- + +# Issuer: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited +# Subject: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited +# Label: "Sectigo Public Server Authentication Root E46" +# Serial: 88989738453351742415770396670917916916 +# MD5 Fingerprint: 28:23:f8:b2:98:5c:37:16:3b:3e:46:13:4e:b0:b3:01 +# SHA1 Fingerprint: ec:8a:39:6c:40:f0:2e:bc:42:75:d4:9f:ab:1c:1a:5b:67:be:d2:9a +# SHA256 Fingerprint: c9:0f:26:f0:fb:1b:40:18:b2:22:27:51:9b:5c:a2:b5:3e:2c:a5:b3:be:5c:f1:8e:fe:1b:ef:47:38:0c:53:83 +-----BEGIN CERTIFICATE----- +MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw +CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T +ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN +MjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYG +A1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT +ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccC +WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+ +6xnOQ6OjQjBAMB0GA1UdDgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8B +Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa +qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q +4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw== +-----END CERTIFICATE----- + +# Issuer: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited +# Subject: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited +# Label: "Sectigo Public Server Authentication Root R46" +# Serial: 156256931880233212765902055439220583700 +# MD5 Fingerprint: 32:10:09:52:00:d5:7e:6c:43:df:15:c0:b1:16:93:e5 +# SHA1 Fingerprint: ad:98:f9:f3:e4:7d:75:3b:65:d4:82:b3:a4:52:17:bb:6e:f5:e4:38 +# SHA256 Fingerprint: 7b:b6:47:a6:2a:ee:ac:88:bf:25:7a:a5:22:d0:1f:fe:a3:95:e0:ab:45:c7:3f:93:f6:56:54:ec:38:f2:5a:06 +-----BEGIN CERTIFICATE----- +MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD +Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw +HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY +MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp +YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa +ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz +SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf +iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X +ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3 +IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS +VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE +SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu ++Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt +8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L +HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt +zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P +AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c +mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ +YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52 +gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA +Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB +JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX +DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui +TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5 +dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65 +LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp +0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY +QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation +# Subject: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation +# Label: "SSL.com TLS RSA Root CA 2022" +# Serial: 148535279242832292258835760425842727825 +# MD5 Fingerprint: d8:4e:c6:59:30:d8:fe:a0:d6:7a:5a:2c:2c:69:78:da +# SHA1 Fingerprint: ec:2c:83:40:72:af:26:95:10:ff:0e:f2:03:ee:31:70:f6:78:9d:ca +# SHA256 Fingerprint: 8f:af:7d:2e:2c:b4:70:9b:b8:e0:b3:36:66:bf:75:a5:dd:45:b5:de:48:0f:8e:a8:d4:bf:e6:be:bc:17:f2:ed +-----BEGIN CERTIFICATE----- +MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO +MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD +DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX +DTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw +b3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJvb3QgQ0EgMjAyMjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u9nTP +L3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OY +t6/wNr/y7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0ins +S657Lb85/bRi3pZ7QcacoOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3 +PnxEX4MN8/HdIGkWCVDi1FW24IBydm5MR7d1VVm0U3TZlMZBrViKMWYPHqIbKUBO +L9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDGD6C1vBdOSHtRwvzpXGk3 +R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEWTO6Af77w +dr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS ++YCk8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYS +d66UNHsef8JmAOSqg+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoG +AtUjHBPW6dvbxrB6y3snm/vg1UYk7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2f +gTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsuN+7jhHonLs0Z +NbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt +hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsM +QtfhWsSWTVTNj8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvf +R4iyrT7gJ4eLSYwfqUdYe5byiB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJ +DPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjUo3KUQyxi4U5cMj29TH0ZR6LDSeeW +P4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqoENjwuSfr98t67wVy +lrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7EgkaibMOlq +bLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2w +AgDHbICivRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3q +r5nsLFR+jM4uElZI7xc7P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sji +Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU +98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA= +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation +# Subject: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation +# Label: "SSL.com TLS ECC Root CA 2022" +# Serial: 26605119622390491762507526719404364228 +# MD5 Fingerprint: 99:d7:5c:f1:51:36:cc:e9:ce:d9:19:2e:77:71:56:c5 +# SHA1 Fingerprint: 9f:5f:d9:1a:54:6d:f5:0c:71:f0:ee:7a:bd:17:49:98:84:73:e2:39 +# SHA256 Fingerprint: c3:2f:fd:9f:46:f9:36:d1:6c:36:73:99:09:59:43:4b:9a:d6:0a:af:bb:9e:7c:f3:36:54:f1:44:cc:1b:a1:43 +-----BEGIN CERTIFICATE----- +MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT +U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2 +MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh +dGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3QgQ0EgMjAyMjB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWyJGYm +acCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFN +SeR7T5v15wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME +GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW +uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp +15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN +b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos +# Subject: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos +# Label: "Atos TrustedRoot Root CA ECC TLS 2021" +# Serial: 81873346711060652204712539181482831616 +# MD5 Fingerprint: 16:9f:ad:f1:70:ad:79:d6:ed:29:b4:d1:c5:79:70:a8 +# SHA1 Fingerprint: 9e:bc:75:10:42:b3:02:f3:81:f4:f7:30:62:d4:8f:c3:a7:51:b2:dd +# SHA256 Fingerprint: b2:fa:e5:3e:14:cc:d7:ab:92:12:06:47:01:ae:27:9c:1d:89:88:fa:cb:77:5f:a8:a0:08:91:4e:66:39:88:a8 +-----BEGIN CERTIFICATE----- +MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w +LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w +CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0 +MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBF +Q0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMHYwEAYHKoZI +zj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6KDP/X +tXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4 +AjJn8ZQSb+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2 +KCXWfeBmmnoJsmo7jjPXNtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMD +aAAwZQIwW5kp85wxtolrbNa9d+F851F+uDrNozZffPc8dz7kUK2o59JZDCaOMDtu +CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo +9H1/IISpQuQo +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos +# Subject: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos +# Label: "Atos TrustedRoot Root CA RSA TLS 2021" +# Serial: 111436099570196163832749341232207667876 +# MD5 Fingerprint: d4:d3:46:b8:9a:c0:9c:76:5d:9e:3a:c3:b9:99:31:d2 +# SHA1 Fingerprint: 18:52:3b:0d:06:37:e4:d6:3a:df:23:e4:98:fb:5b:16:fb:86:74:48 +# SHA256 Fingerprint: 81:a9:08:8e:a5:9f:b3:64:c5:48:a6:f8:55:59:09:9b:6f:04:05:ef:bf:18:e5:32:4e:c9:f4:57:ba:00:11:2f +-----BEGIN CERTIFICATE----- +MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBM +MS4wLAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIx +MQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00 +MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBD +QSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BBl01Z +4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYv +Ye+W/CBGvevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZ +kmGbzSoXfduP9LVq6hdKZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDs +GY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt0xU6kGpn8bRrZtkh68rZYnxGEFzedUln +nkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVKPNe0OwANwI8f4UDErmwh +3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMYsluMWuPD +0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzy +geBYBr3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8 +ANSbhqRAvNncTFd+rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezB +c6eUWsuSZIKmAMFwoW4sKeFYV+xafJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lI +pw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +dEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +DAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS +4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPs +o0UvFJ/1TCplQ3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJ +qM7F78PRreBrAwA0JrRUITWXAdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuyw +xfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9GslA9hGCZcbUztVdF5kJHdWoOsAgM +rr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2VktafcxBPTy+av5EzH4 +AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9qTFsR +0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuY +o7Ey7Nmj1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5 +dDTedk+SKlOxJTnbPP/lPqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcE +oji2jbDwN/zIIX8/syQbPYtuzE2wFg2WHYMfRsCbvUOZ58SWLs5fyQ== +-----END CERTIFICATE----- + +# Issuer: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc. +# Subject: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc. +# Label: "TrustAsia Global Root CA G3" +# Serial: 576386314500428537169965010905813481816650257167 +# MD5 Fingerprint: 30:42:1b:b7:bb:81:75:35:e4:16:4f:53:d2:94:de:04 +# SHA1 Fingerprint: 63:cf:b6:c1:27:2b:56:e4:88:8e:1c:23:9a:b6:2e:81:47:24:c3:c7 +# SHA256 Fingerprint: e0:d3:22:6a:eb:11:63:c2:e4:8f:f9:be:3b:50:b4:c6:43:1b:e7:bb:1e:ac:c5:c3:6b:5d:5e:c5:09:03:9a:08 +-----BEGIN CERTIFICATE----- +MIIFpTCCA42gAwIBAgIUZPYOZXdhaqs7tOqFhLuxibhxkw8wDQYJKoZIhvcNAQEM +BQAwWjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dp +ZXMsIEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHMzAe +Fw0yMTA1MjAwMjEwMTlaFw00NjA1MTkwMjEwMTlaMFoxCzAJBgNVBAYTAkNOMSUw +IwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtU +cnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDAMYJhkuSUGwoqZdC+BqmHO1ES6nBBruL7dOoKjbmzTNyPtxNS +T1QY4SxzlZHFZjtqz6xjbYdT8PfxObegQ2OwxANdV6nnRM7EoYNl9lA+sX4WuDqK +AtCWHwDNBSHvBm3dIZwZQ0WhxeiAysKtQGIXBsaqvPPW5vxQfmZCHzyLpnl5hkA1 +nyDvP+uLRx+PjsXUjrYsyUQE49RDdT/VP68czH5GX6zfZBCK70bwkPAPLfSIC7Ep +qq+FqklYqL9joDiR5rPmd2jE+SoZhLsO4fWvieylL1AgdB4SQXMeJNnKziyhWTXA +yB1GJ2Faj/lN03J5Zh6fFZAhLf3ti1ZwA0pJPn9pMRJpxx5cynoTi+jm9WAPzJMs +hH/x/Gr8m0ed262IPfN2dTPXS6TIi/n1Q1hPy8gDVI+lhXgEGvNz8teHHUGf59gX +zhqcD0r83ERoVGjiQTz+LISGNzzNPy+i2+f3VANfWdP3kXjHi3dqFuVJhZBFcnAv +kV34PmVACxmZySYgWmjBNb9Pp1Hx2BErW+Canig7CjoKH8GB5S7wprlppYiU5msT +f9FkPz2ccEblooV7WIQn3MSAPmeamseaMQ4w7OYXQJXZRe0Blqq/DPNL0WP3E1jA +uPP6Z92bfW1K/zJMtSU7/xxnD4UiWQWRkUF3gdCFTIcQcf+eQxuulXUtgQIDAQAB +o2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEDk5PIj7zjKsK5Xf/Ih +MBY027ySMB0GA1UdDgQWBBRA5OTyI+84yrCuV3/yITAWNNu8kjAOBgNVHQ8BAf8E +BAMCAQYwDQYJKoZIhvcNAQEMBQADggIBACY7UeFNOPMyGLS0XuFlXsSUT9SnYaP4 +wM8zAQLpw6o1D/GUE3d3NZ4tVlFEbuHGLige/9rsR82XRBf34EzC4Xx8MnpmyFq2 +XFNFV1pF1AWZLy4jVe5jaN/TG3inEpQGAHUNcoTpLrxaatXeL1nHo+zSh2bbt1S1 +JKv0Q3jbSwTEb93mPmY+KfJLaHEih6D4sTNjduMNhXJEIlU/HHzp/LgV6FL6qj6j +ITk1dImmasI5+njPtqzn59ZW/yOSLlALqbUHM/Q4X6RJpstlcHboCoWASzY9M/eV +VHUl2qzEc4Jl6VL1XP04lQJqaTDFHApXB64ipCz5xUG3uOyfT0gA+QEEVcys+TIx +xHWVBqB/0Y0n3bOppHKH/lmLmnp0Ft0WpWIp6zqW3IunaFnT63eROfjXy9mPX1on +AX1daBli2MjN9LdyR75bl87yraKZk62Uy5P2EgmVtqvXO9A/EcswFi55gORngS1d +7XB4tmBZrOFdRWOPyN9yaFvqHbgB8X7754qz41SgOAngPN5C8sLtLpvzHzW2Ntjj +gKGLzZlkD8Kqq7HK9W+eQ42EVJmzbsASZthwEPEGNTNDqJwuuhQxzhB/HIbjj9LV ++Hfsm6vxL2PZQl/gZ4FkkfGXL/xuJvYz+NO1+MRiqzFRJQJ6+N1rZdVtTTDIZbpo +FGWsJwt0ivKH +-----END CERTIFICATE----- + +# Issuer: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc. +# Subject: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc. +# Label: "TrustAsia Global Root CA G4" +# Serial: 451799571007117016466790293371524403291602933463 +# MD5 Fingerprint: 54:dd:b2:d7:5f:d8:3e:ed:7c:e0:0b:2e:cc:ed:eb:eb +# SHA1 Fingerprint: 57:73:a5:61:5d:80:b2:e6:ac:38:82:fc:68:07:31:ac:9f:b5:92:5a +# SHA256 Fingerprint: be:4b:56:cb:50:56:c0:13:6a:52:6d:f4:44:50:8d:aa:36:a0:b5:4f:42:e4:ac:38:f7:2a:f4:70:e4:79:65:4c +-----BEGIN CERTIFICATE----- +MIICVTCCAdygAwIBAgIUTyNkuI6XY57GU4HBdk7LKnQV1tcwCgYIKoZIzj0EAwMw +WjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs +IEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHNDAeFw0y +MTA1MjAwMjEwMjJaFw00NjA1MTkwMjEwMjJaMFoxCzAJBgNVBAYTAkNOMSUwIwYD +VQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtUcnVz +dEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATx +s8045CVD5d4ZCbuBeaIVXxVjAd7Cq92zphtnS4CDr5nLrBfbK5bKfFJV4hrhPVbw +LxYI+hW8m7tH5j/uqOFMjPXTNvk4XatwmkcN4oFBButJ+bAp3TPsUKV/eSm4IJij +YzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUpbtKl86zK3+kMd6Xg1mD +pm9xy94wHQYDVR0OBBYEFKW7SpfOsyt/pDHel4NZg6ZvccveMA4GA1UdDwEB/wQE +AwIBBjAKBggqhkjOPQQDAwNnADBkAjBe8usGzEkxn0AAbbd+NvBNEU/zy4k6LHiR +UKNbwMp1JvK/kF0LgoxgKJ/GcJpo5PECMFxYDlZ2z1jD1xCMuo6u47xkdUfFVZDj +/bpV6wfEU6s3qe4hsiFbYI89MvHVI5TWWA== +-----END CERTIFICATE----- + +# Issuer: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH +# Subject: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH +# Label: "Telekom Security TLS ECC Root 2020" +# Serial: 72082518505882327255703894282316633856 +# MD5 Fingerprint: c1:ab:fe:6a:10:2c:03:8d:bc:1c:22:32:c0:85:a7:fd +# SHA1 Fingerprint: c0:f8:96:c5:a9:3b:01:06:21:07:da:18:42:48:bc:e9:9d:88:d5:ec +# SHA256 Fingerprint: 57:8a:f4:de:d0:85:3f:4e:59:98:db:4a:ea:f9:cb:ea:8d:94:5f:60:b6:20:a3:8d:1a:3c:13:b2:bc:7b:a8:e1 +-----BEGIN CERTIFICATE----- +MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQsw +CQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBH +bWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIw +MB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIzNTk1OVowYzELMAkGA1UEBhMCREUx +JzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkGA1UE +AwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/O +tdKPD/M12kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDP +f8iAC8GXs7s1J8nCG6NCMEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6f +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA +MGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZMo7k+5Dck2TOrbRBR2Di +z6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdUga/sf+Rn +27iQ7t0l +-----END CERTIFICATE----- + +# Issuer: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH +# Subject: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH +# Label: "Telekom Security TLS RSA Root 2023" +# Serial: 44676229530606711399881795178081572759 +# MD5 Fingerprint: bf:5b:eb:54:40:cd:48:71:c4:20:8d:7d:de:0a:42:f2 +# SHA1 Fingerprint: 54:d3:ac:b3:bd:57:56:f6:85:9d:ce:e5:c3:21:e2:d4:ad:83:d0:93 +# SHA256 Fingerprint: ef:c6:5c:ad:bb:59:ad:b6:ef:e8:4d:a2:23:11:b3:56:24:b7:1b:3b:1e:a0:da:8b:66:55:17:4e:c8:97:86:46 +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIQIZxULej27HF3+k7ow3BXlzANBgkqhkiG9w0BAQwFADBj +MQswCQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0 +eSBHbWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBSU0EgUm9vdCAy +MDIzMB4XDTIzMDMyODEyMTY0NVoXDTQ4MDMyNzIzNTk1OVowYzELMAkGA1UEBhMC +REUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkG +A1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgUlNBIFJvb3QgMjAyMzCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAO01oYGA88tKaVvC+1GDrib94W7zgRJ9 +cUD/h3VCKSHtgVIs3xLBGYSJwb3FKNXVS2xE1kzbB5ZKVXrKNoIENqil/Cf2SfHV +cp6R+SPWcHu79ZvB7JPPGeplfohwoHP89v+1VmLhc2o0mD6CuKyVU/QBoCcHcqMA +U6DksquDOFczJZSfvkgdmOGjup5czQRxUX11eKvzWarE4GC+j4NSuHUaQTXtvPM6 +Y+mpFEXX5lLRbtLevOP1Czvm4MS9Q2QTps70mDdsipWol8hHD/BeEIvnHRz+sTug +BTNoBUGCwQMrAcjnj02r6LX2zWtEtefdi+zqJbQAIldNsLGyMcEWzv/9FIS3R/qy +8XDe24tsNlikfLMR0cN3f1+2JeANxdKz+bi4d9s3cXFH42AYTyS2dTd4uaNir73J +co4vzLuu2+QVUhkHM/tqty1LkCiCc/4YizWN26cEar7qwU02OxY2kTLvtkCJkUPg +8qKrBC7m8kwOFjQgrIfBLX7JZkcXFBGk8/ehJImr2BrIoVyxo/eMbcgByU/J7MT8 +rFEz0ciD0cmfHdRHNCk+y7AO+oMLKFjlKdw/fKifybYKu6boRhYPluV75Gp6SG12 +mAWl3G0eQh5C2hrgUve1g8Aae3g1LDj1H/1Joy7SWWO/gLCMk3PLNaaZlSJhZQNg ++y+TS/qanIA7AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtqeX +gj10hZv3PJ+TmpV5dVKMbUcwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS2 +p5eCPXSFm/c8n5OalXl1UoxtRzANBgkqhkiG9w0BAQwFAAOCAgEAqMxhpr51nhVQ +pGv7qHBFfLp+sVr8WyP6Cnf4mHGCDG3gXkaqk/QeoMPhk9tLrbKmXauw1GLLXrtm +9S3ul0A8Yute1hTWjOKWi0FpkzXmuZlrYrShF2Y0pmtjxrlO8iLpWA1WQdH6DErw +M807u20hOq6OcrXDSvvpfeWxm4bu4uB9tPcy/SKE8YXJN3nptT+/XOR0so8RYgDd +GGah2XsjX/GO1WfoVNpbOms2b/mBsTNHM3dA+VKq3dSDz4V4mZqTuXNnQkYRIer+ +CqkbGmVps4+uFrb2S1ayLfmlyOw7YqPta9BO1UAJpB+Y1zqlklkg5LB9zVtzaL1t +xKITDmcZuI1CfmwMmm6gJC3VRRvcxAIU/oVbZZfKTpBQCHpCNfnqwmbU+AGuHrS+ +w6jv/naaoqYfRvaE7fzbzsQCzndILIyy7MMAo+wsVRjBfhnu4S/yrYObnqsZ38aK +L4x35bcF7DvB7L6Gs4a8wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+lj +X273CXE2whJdV/LItM3z7gLfEdxquVeEHVlNjM7IDiPCtyaaEBRx/pOyiriA8A4Q +ntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0o82bNSQ3+pCTE4FCxpgm +dTdmQRCsu/WU48IxK63nI1bMNSWSs1A= +-----END CERTIFICATE----- + +# Issuer: CN=TWCA CYBER Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA CYBER Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA CYBER Root CA" +# Serial: 85076849864375384482682434040119489222 +# MD5 Fingerprint: 0b:33:a0:97:52:95:d4:a9:fd:bb:db:6e:a3:55:5b:51 +# SHA1 Fingerprint: f6:b1:1c:1a:83:38:e9:7b:db:b3:a8:c8:33:24:e0:2d:9c:7f:26:66 +# SHA256 Fingerprint: 3f:63:bb:28:14:be:17:4e:c8:b6:43:9c:f0:8d:6d:56:f0:b7:c4:05:88:3a:56:48:a3:34:42:4d:6b:3e:c5:58 +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQ +MQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290 +IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5 +WhcNNDcxMTIyMTU1OTU5WjBQMQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FO +LUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG+Moe2Qkgfh1sTs6P +40czRJzHyWmqOlt47nDSkvgEs1JSHWdyKKHfi12VCv7qze33Kc7wb3+szT3vsxxF +avcokPFhV8UMxKNQXd7UtcsZyoC5dc4pztKFIuwCY8xEMCDa6pFbVuYdHNWdZsc/ +34bKS1PE2Y2yHer43CdTo0fhYcx9tbD47nORxc5zb87uEB8aBs/pJ2DFTxnk684i +JkXXYJndzk834H/nY62wuFm40AZoNWDTNq5xQwTxaWV4fPMf88oon1oglWa0zbfu +j3ikRRjpJi+NmykosaS3Om251Bw4ckVYsV7r8Cibt4LK/c/WMw+f+5eesRycnupf +Xtuq3VTpMCEobY5583WSjCb+3MX2w7DfRFlDo7YDKPYIMKoNM+HvnKkHIuNZW0CP +2oi3aQiotyMuRAlZN1vH4xfyIutuOVLF3lSnmMlLIJXcRolftBL5hSmO68gnFSDA +S9TMfAxsNAwmmyYxpjyn9tnQS6Jk/zuZQXLB4HCX8SS7K8R0IrGsayIyJNN4KsDA +oS/xUgXJP+92ZuJF2A09rZXIx4kmyA+upwMu+8Ff+iDhcK2wZSA3M2Cw1a/XDBzC +kHDXShi8fgGwsOsVHkQGzaRP6AzRwyAQ4VRlnrZR0Bp2a0JaWHY06rc3Ga4udfmW +5cFZ95RXKSWNOkyrTZpB0F8mAwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSdhWEUfMFib5do5E83QOGt4A1WNzAd +BgNVHQ4EFgQUnYVhFHzBYm+XaORPN0DhreANVjcwDQYJKoZIhvcNAQEMBQADggIB +AGSPesRiDrWIzLjHhg6hShbNcAu3p4ULs3a2D6f/CIsLJc+o1IN1KriWiLb73y0t +tGlTITVX1olNc79pj3CjYcya2x6a4CD4bLubIp1dhDGaLIrdaqHXKGnK/nZVekZn +68xDiBaiA9a5F/gZbG0jAn/xX9AKKSM70aoK7akXJlQKTcKlTfjF/biBzysseKNn +TKkHmvPfXvt89YnNdJdhEGoHK4Fa0o635yDRIG4kqIQnoVesqlVYL9zZyvpoBJ7t +RCT5dEA7IzOrg1oYJkK2bVS1FmAwbLGg+LhBoF1JSdJlBTrq/p1hvIbZv97Tujqx +f36SNI7JAG7cmL3c7IAFrQI932XtCwP39xaEBDG6k5TY8hL4iuO/Qq+n1M0RFxbI +Qh0UqEL20kCGoE8jypZFVmAGzbdVAaYBlGX+bgUJurSkquLvWL69J1bY73NxW0Qz +8ppy6rBePm6pUlvscG21h483XjyMnM7k8M4MZ0HMzvaAq07MTFb1wWFZk7Q+ptq4 +NxKfKjLji7gh7MMrZQzvIt6IKTtM1/r+t+FHvpw+PoP7UV31aPcuIYXcv/Fa4nzX +xeSDwWrruoBa3lwtcHb4yOWHh8qgnaHlIhInD0Q9HWzq1MKLL295q39QpsQZp6F6 +t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign Root CA14 O=Cybertrust Japan Co., Ltd. +# Subject: CN=SecureSign Root CA14 O=Cybertrust Japan Co., Ltd. +# Label: "SecureSign Root CA14" +# Serial: 575790784512929437950770173562378038616896959179 +# MD5 Fingerprint: 71:0d:72:fa:92:19:65:5e:89:04:ac:16:33:f0:bc:d5 +# SHA1 Fingerprint: dd:50:c0:f7:79:b3:64:2e:74:a2:b8:9d:9f:d3:40:dd:bb:f0:f2:4f +# SHA256 Fingerprint: 4b:00:9c:10:34:49:4f:9a:b5:6b:ba:3b:a1:d6:27:31:fc:4d:20:d8:95:5a:dc:ec:10:a9:25:60:72:61:e3:38 +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIUZNtaDCBO6Ncpd8hQJ6JaJ90t8sswDQYJKoZIhvcNAQEM +BQAwUTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28u +LCBMdGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNDAeFw0yMDA0MDgw +NzA2MTlaFw00NTA0MDgwNzA2MTlaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpD +eWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBS +b290IENBMTQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF0nqh1oq/ +FjHQmNE6lPxauG4iwWL3pwon71D2LrGeaBLwbCRjOfHw3xDG3rdSINVSW0KZnvOg +vlIfX8xnbacuUKLBl422+JX1sLrcneC+y9/3OPJH9aaakpUqYllQC6KxNedlsmGy +6pJxaeQp8E+BgQQ8sqVb1MWoWWd7VRxJq3qdwudzTe/NCcLEVxLbAQ4jeQkHO6Lo +/IrPj8BGJJw4J+CDnRugv3gVEOuGTgpa/d/aLIJ+7sr2KeH6caH3iGicnPCNvg9J +kdjqOvn90Ghx2+m1K06Ckm9mH+Dw3EzsytHqunQG+bOEkJTRX45zGRBdAuVwpcAQ +0BB8b8VYSbSwbprafZX1zNoCr7gsfXmPvkPx+SgojQlD+Ajda8iLLCSxjVIHvXib +y8posqTdDEx5YMaZ0ZPxMBoH064iwurO8YQJzOAUbn8/ftKChazcqRZOhaBgy/ac +18izju3Gm5h1DVXoX+WViwKkrkMpKBGk5hIwAUt1ax5mnXkvpXYvHUC0bcl9eQjs +0Wq2XSqypWa9a4X0dFbD9ed1Uigspf9mR6XU/v6eVL9lfgHWMI+lNpyiUBzuOIAB +SMbHdPTGrMNASRZhdCyvjG817XsYAFs2PJxQDcqSMxDxJklt33UkN4Ii1+iW/RVL +ApY+B3KVfqs9TC7XyvDf4Fg/LS8EmjijAQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUBpOjCl4oaTeqYR3r6/wtbyPk +86AwDQYJKoZIhvcNAQEMBQADggIBAJaAcgkGfpzMkwQWu6A6jZJOtxEaCnFxEM0E +rX+lRVAQZk5KQaID2RFPeje5S+LGjzJmdSX7684/AykmjbgWHfYfM25I5uj4V7Ib +ed87hwriZLoAymzvftAj63iP/2SbNDefNWWipAA9EiOWWF3KY4fGoweITedpdopT +zfFP7ELyk+OZpDc8h7hi2/DsHzc/N19DzFGdtfCXwreFamgLRB7lUe6TzktuhsHS +DCRZNhqfLJGP4xjblJUK7ZGqDpncllPjYYPGFrojutzdfhrGe0K22VoF3Jpf1d+4 +2kd92jjbrDnVHmtsKheMYc2xbXIBw8MgAGJoFjHVdqqGuw6qnsb58Nn4DSEC5MUo +FlkRudlpcyqSeLiSV5sI8jrlL5WwWLdrIBRtFO8KvH7YVdiI2i/6GaX7i+B/OfVy +K4XELKzvGUWSTLNhB9xNH27SgRNcmvMSZ4PPmz+Ln52kuaiWA3rF7iDeM9ovnhp6 +dB7h7sxaOgTdsxoEqBRjrLdHEoOabPXm6RUVkRqEGQ6UROcSjiVbgGcZ3GOTEAtl +Lor6CZpO2oYofaphNdgOpygau1LgePhsumywbrmHXumZNTfxPWQrqaA0k89jL9WB +365jJ6UeTo3cKXhZ+PmhIIynJkBugnLNeLLIjzwec+fBH7/PzqUqm9tEZDKgu39c +JRNItX+S +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign Root CA15 O=Cybertrust Japan Co., Ltd. +# Subject: CN=SecureSign Root CA15 O=Cybertrust Japan Co., Ltd. +# Label: "SecureSign Root CA15" +# Serial: 126083514594751269499665114766174399806381178503 +# MD5 Fingerprint: 13:30:fc:c4:62:a6:a9:de:b5:c1:68:af:b5:d2:31:47 +# SHA1 Fingerprint: cb:ba:83:c8:c1:5a:5d:f1:f9:73:6f:ca:d7:ef:28:13:06:4a:07:7d +# SHA256 Fingerprint: e7:78:f0:f0:95:fe:84:37:29:cd:1a:00:82:17:9e:53:14:a9:c2:91:44:28:05:e1:fb:1d:8f:b6:b8:88:6c:3a +-----BEGIN CERTIFICATE----- +MIICIzCCAamgAwIBAgIUFhXHw9hJp75pDIqI7fBw+d23PocwCgYIKoZIzj0EAwMw +UTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBM +dGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNTAeFw0yMDA0MDgwODMy +NTZaFw00NTA0MDgwODMyNTZaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpDeWJl +cnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBSb290 +IENBMTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQLUHSNZDKZmbPSYAi4Io5GdCx4 +wCtELW1fHcmuS1Iggz24FG1Th2CeX2yF2wYUleDHKP+dX+Sq8bOLbe1PL0vJSpSR +ZHX+AezB2Ot6lHhWGENfa4HL9rzatAy2KZMIaY+jQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTrQciu/NWeUUj1vYv0hyCTQSvT +9DAKBggqhkjOPQQDAwNoADBlAjEA2S6Jfl5OpBEHvVnCB96rMjhTKkZEBhd6zlHp +4P9mLQlO4E/0BdGF9jVg3PVys0Z9AjBEmEYagoUeYWmJSwdLZrWeqrqgHkHZAXQ6 +bkU6iYAZezKYVWOr62Nuk22rGwlgMU4= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST BR Root CA 2 2023 O=D-Trust GmbH +# Subject: CN=D-TRUST BR Root CA 2 2023 O=D-Trust GmbH +# Label: "D-TRUST BR Root CA 2 2023" +# Serial: 153168538924886464690566649552453098598 +# MD5 Fingerprint: e1:09:ed:d3:60:d4:56:1b:47:1f:b7:0c:5f:1b:5f:85 +# SHA1 Fingerprint: 2d:b0:70:ee:71:94:af:69:68:17:db:79:ce:58:9f:a0:6b:96:f7:87 +# SHA256 Fingerprint: 05:52:e6:f8:3f:df:65:e8:fa:96:70:e6:66:df:28:a4:e2:13:40:b5:10:cb:e5:25:66:f9:7c:4f:b9:4b:2b:d1 +-----BEGIN CERTIFICATE----- +MIIFqTCCA5GgAwIBAgIQczswBEhb2U14LnNLyaHcZjANBgkqhkiG9w0BAQ0FADBI +MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlE +LVRSVVNUIEJSIFJvb3QgQ0EgMiAyMDIzMB4XDTIzMDUwOTA4NTYzMVoXDTM4MDUw +OTA4NTYzMFowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEi +MCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDIgMjAyMzCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAK7/CVmRgApKaOYkP7in5Mg6CjoWzckjYaCTcfKr +i3OPoGdlYNJUa2NRb0kz4HIHE304zQaSBylSa053bATTlfrdTIzZXcFhfUvnKLNE +gXtRr90zsWh81k5M/itoucpmacTsXld/9w3HnDY25QdgrMBM6ghs7wZ8T1soegj8 +k12b9py0i4a6Ibn08OhZWiihNIQaJZG2tY/vsvmA+vk9PBFy2OMvhnbFeSzBqZCT +Rphny4NqoFAjpzv2gTng7fC5v2Xx2Mt6++9zA84A9H3X4F07ZrjcjrqDy4d2A/wl +2ecjbwb9Z/Pg/4S8R7+1FhhGaRTMBffb00msa8yr5LULQyReS2tNZ9/WtT5PeB+U +cSTq3nD88ZP+npNa5JRal1QMNXtfbO4AHyTsA7oC9Xb0n9Sa7YUsOCIvx9gvdhFP +/Wxc6PWOJ4d/GUohR5AdeY0cW/jPSoXk7bNbjb7EZChdQcRurDhaTyN0dKkSw/bS +uREVMweR2Ds3OmMwBtHFIjYoYiMQ4EbMl6zWK11kJNXuHA7e+whadSr2Y23OC0K+ +0bpwHJwh5Q8xaRfX/Aq03u2AnMuStIv13lmiWAmlY0cL4UEyNEHZmrHZqLAbWt4N +DfTisl01gLmB1IRpkQLLddCNxbU9CZEJjxShFHR5PtbJFR2kWVki3PaKRT08EtY+ +XTIvAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUZ5Dw1t61 +GNVGKX5cq/ieCLxklRAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRCMEAwPqA8oDqG +OGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfYnJfcm9vdF9jYV8y +XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQA097N3U9swFrktpSHxQCF16+tI +FoE9c+CeJyrrd6kTpGoKWloUMz1oH4Guaf2Mn2VsNELZLdB/eBaxOqwjMa1ef67n +riv6uvw8l5VAk1/DLQOj7aRvU9f6QA4w9QAgLABMjDu0ox+2v5Eyq6+SmNMW5tTR +VFxDWy6u71cqqLRvpO8NVhTaIasgdp4D/Ca4nj8+AybmTNudX0KEPUUDAxxZiMrc +LmEkWqTqJwtzEr5SswrPMhfiHocaFpVIbVrg0M8JkiZmkdijYQ6qgYF/6FKC0ULn +4B0Y+qSFNueG4A3rvNTJ1jxD8V1Jbn6Bm2m1iWKPiFLY1/4nwSPFyysCu7Ff/vtD +hQNGvl3GyiEm/9cCnnRK3PgTFbGBVzbLZVzRHTF36SXDw7IyN9XxmAnkbWOACKsG +koHU6XCPpz+y7YaMgmo1yEJagtFSGkUPFaUA8JR7ZSdXOUPPfH/mvTWze/EZTN46 +ls/pdu4D58JDUjxqgejBWoC9EV2Ta/vH5mQ/u2kc6d0li690yVRAysuTEwrt+2aS +Ecr1wPrYg1UDfNPFIkZ1cGt5SAYqgpq/5usWDiJFAbzdNpQ0qTUmiteXue4Icr80 +knCDgKs4qllo3UCkGJCy89UDyibK79XH4I9TjvAA46jtn/mtd+ArY0+ew+43u3gJ +hJ65bvspmZDogNOfJA== +-----END CERTIFICATE----- + +# Issuer: CN=TrustAsia TLS ECC Root CA O=TrustAsia Technologies, Inc. +# Subject: CN=TrustAsia TLS ECC Root CA O=TrustAsia Technologies, Inc. +# Label: "TrustAsia TLS ECC Root CA" +# Serial: 310892014698942880364840003424242768478804666567 +# MD5 Fingerprint: 09:48:04:77:d2:fc:65:93:71:66:b1:11:95:4f:06:8c +# SHA1 Fingerprint: b5:ec:39:f3:a1:66:37:ae:c3:05:94:57:e2:be:11:be:b7:a1:7f:36 +# SHA256 Fingerprint: c0:07:6b:9e:f0:53:1f:b1:a6:56:d6:7c:4e:be:97:cd:5d:ba:a4:1e:f4:45:98:ac:c2:48:98:78:c9:2d:87:11 +-----BEGIN CERTIFICATE----- +MIICMTCCAbegAwIBAgIUNnThTXxlE8msg1UloD5Sfi9QaMcwCgYIKoZIzj0EAwMw +WDELMAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs +IEluYy4xIjAgBgNVBAMTGVRydXN0QXNpYSBUTFMgRUNDIFJvb3QgQ0EwHhcNMjQw +NTE1MDU0MTU2WhcNNDQwNTE1MDU0MTU1WjBYMQswCQYDVQQGEwJDTjElMCMGA1UE +ChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjEiMCAGA1UEAxMZVHJ1c3RB +c2lhIFRMUyBFQ0MgUm9vdCBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLh/pVs/ +AT598IhtrimY4ZtcU5nb9wj/1WrgjstEpvDBjL1P1M7UiFPoXlfXTr4sP/MSpwDp +guMqWzJ8S5sUKZ74LYO1644xST0mYekdcouJtgq7nDM1D9rs3qlKH8kzsaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQULIVTu7FDzTLqnqOH/qKYqKaT6RAw +DgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMFRH18MtYYZI9HlaVQ01 +L18N9mdsd0AaRuf4aFtOJx24mH1/k78ITcTaRTChD15KeAIxAKORh/IRM4PDwYqR +OkwrULG9IpRdNYlzg8WbGf60oenUoWa2AaU2+dhoYSi3dOGiMQ== +-----END CERTIFICATE----- + +# Issuer: CN=TrustAsia TLS RSA Root CA O=TrustAsia Technologies, Inc. +# Subject: CN=TrustAsia TLS RSA Root CA O=TrustAsia Technologies, Inc. +# Label: "TrustAsia TLS RSA Root CA" +# Serial: 160405846464868906657516898462547310235378010780 +# MD5 Fingerprint: 3b:9e:c3:86:0f:34:3c:6b:c5:46:c4:8e:1d:e7:19:12 +# SHA1 Fingerprint: a5:46:50:c5:62:ea:95:9a:1a:a7:04:6f:17:58:c7:29:53:3d:03:fa +# SHA256 Fingerprint: 06:c0:8d:7d:af:d8:76:97:1e:b1:12:4f:e6:7f:84:7e:c0:c7:a1:58:d3:ea:53:cb:e9:40:e2:ea:97:91:f4:c3 +-----BEGIN CERTIFICATE----- +MIIFgDCCA2igAwIBAgIUHBjYz+VTPyI1RlNUJDxsR9FcSpwwDQYJKoZIhvcNAQEM +BQAwWDELMAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dp +ZXMsIEluYy4xIjAgBgNVBAMTGVRydXN0QXNpYSBUTFMgUlNBIFJvb3QgQ0EwHhcN +MjQwNTE1MDU0MTU3WhcNNDQwNTE1MDU0MTU2WjBYMQswCQYDVQQGEwJDTjElMCMG +A1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjEiMCAGA1UEAxMZVHJ1 +c3RBc2lhIFRMUyBSU0EgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBAMMWuBtqpERz5dZO9LnPWwvB0ZqB9WOwj0PBuwhaGnrhB3YmH49pVr7+ +NmDQDIPNlOrnxS1cLwUWAp4KqC/lYCZUlviYQB2srp10Zy9U+5RjmOMmSoPGlbYJ +Q1DNDX3eRA5gEk9bNb2/mThtfWza4mhzH/kxpRkQcwUqwzIZheo0qt1CHjCNP561 +HmHVb70AcnKtEj+qpklz8oYVlQwQX1Fkzv93uMltrOXVmPGZLmzjyUT5tUMnCE32 +ft5EebuyjBza00tsLtbDeLdM1aTk2tyKjg7/D8OmYCYozza/+lcK7Fs/6TAWe8Tb +xNRkoDD75f0dcZLdKY9BWN4ArTr9PXwaqLEX8E40eFgl1oUh63kd0Nyrz2I8sMeX +i9bQn9P+PN7F4/w6g3CEIR0JwqH8uyghZVNgepBtljhb//HXeltt08lwSUq6HTrQ +UNoyIBnkiz/r1RYmNzz7dZ6wB3C4FGB33PYPXFIKvF1tjVEK2sUYyJtt3LCDs3+j +TnhMmCWr8n4uIF6CFabW2I+s5c0yhsj55NqJ4js+k8UTav/H9xj8Z7XvGCxUq0DT +bE3txci3OE9kxJRMT6DNrqXGJyV1J23G2pyOsAWZ1SgRxSHUuPzHlqtKZFlhaxP8 +S8ySpg+kUb8OWJDZgoM5pl+z+m6Ss80zDoWo8SnTq1mt1tve1CuBAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLgHkXlcBvRG/XtZylomkadFK/hT +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQwFAAOCAgEAIZtqBSBdGBanEqT3 +Rz/NyjuujsCCztxIJXgXbODgcMTWltnZ9r96nBO7U5WS/8+S4PPFJzVXqDuiGev4 +iqME3mmL5Dw8veWv0BIb5Ylrc5tvJQJLkIKvQMKtuppgJFqBTQUYo+IzeXoLH5Pt +7DlK9RME7I10nYEKqG/odv6LTytpEoYKNDbdgptvT+Bz3Ul/KD7JO6NXBNiT2Twp +2xIQaOHEibgGIOcberyxk2GaGUARtWqFVwHxtlotJnMnlvm5P1vQiJ3koP26TpUJ +g3933FEFlJ0gcXax7PqJtZwuhfG5WyRasQmr2soaB82G39tp27RIGAAtvKLEiUUj +pQ7hRGU+isFqMB3iYPg6qocJQrmBktwliJiJ8Xw18WLK7nn4GS/+X/jbh87qqA8M +pugLoDzga5SYnH+tBuYc6kIQX+ImFTw3OffXvO645e8D7r0i+yiGNFjEWn9hongP +XvPKnbwbPKfILfanIhHKA9jnZwqKDss1jjQ52MjqjZ9k4DewbNfFj8GQYSbbJIwe +SsCI3zWQzj8C9GRh3sfIB5XeMhg6j6JCQCTl1jNdfK7vsU1P1FeQNWrcrgSXSYk0 +ly4wBOeY99sLAZDBHwo/+ML+TvrbmnNzFrwFuHnYWa8G5z9nODmxfKuU4CkUpijy +323imttUQ/hHWKNddBWcwauwxzQ= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST EV Root CA 2 2023 O=D-Trust GmbH +# Subject: CN=D-TRUST EV Root CA 2 2023 O=D-Trust GmbH +# Label: "D-TRUST EV Root CA 2 2023" +# Serial: 139766439402180512324132425437959641711 +# MD5 Fingerprint: 96:b4:78:09:f0:09:cb:77:eb:bb:1b:4d:6f:36:bc:b6 +# SHA1 Fingerprint: a5:5b:d8:47:6c:8f:19:f7:4c:f4:6d:6b:b6:c2:79:82:22:df:54:8b +# SHA256 Fingerprint: 8e:82:21:b2:e7:d4:00:78:36:a1:67:2f:0d:cc:29:9c:33:bc:07:d3:16:f1:32:fa:1a:20:6d:58:71:50:f1:ce +-----BEGIN CERTIFICATE----- +MIIFqTCCA5GgAwIBAgIQaSYJfoBLTKCnjHhiU19abzANBgkqhkiG9w0BAQ0FADBI +MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlE +LVRSVVNUIEVWIFJvb3QgQ0EgMiAyMDIzMB4XDTIzMDUwOTA5MTAzM1oXDTM4MDUw +OTA5MTAzMlowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEi +MCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDIgMjAyMzCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBANiOo4mAC7JXUtypU0w3uX9jFxPvp1sjW2l1sJkK +F8GLxNuo4MwxusLyzV3pt/gdr2rElYfXR8mV2IIEUD2BCP/kPbOx1sWy/YgJ25yE +7CUXFId/MHibaljJtnMoPDT3mfd/06b4HEV8rSyMlD/YZxBTfiLNTiVR8CUkNRFe +EMbsh2aJgWi6zCudR3Mfvc2RpHJqnKIbGKBv7FD0fUDCqDDPvXPIEysQEx6Lmqg6 +lHPTGGkKSv/BAQP/eX+1SH977ugpbzZMlWGG2Pmic4ruri+W7mjNPU0oQvlFKzIb +RlUWaqZLKfm7lVa/Rh3sHZMdwGWyH6FDrlaeoLGPaxK3YG14C8qKXO0elg6DpkiV +jTujIcSuWMYAsoS0I6SWhjW42J7YrDRJmGOVxcttSEfi8i4YHtAxq9107PncjLgc +jmgjutDzUNzPZY9zOjLHfP7KgiJPvo5iR2blzYfi6NUPGJ/lBHJLRjwQ8kTCZFZx +TnXonMkmdMV9WdEKWw9t/p51HBjGGjp82A0EzM23RWV6sY+4roRIPrN6TagD4uJ+ +ARZZaBhDM7DS3LAaQzXupdqpRlyuhoFBAUp0JuyfBr/CBTdkdXgpaP3F9ev+R/nk +hbDhezGdpn9yo7nELC7MmVcOIQxFAZRl62UJxmMiCzNJkkg8/M3OsD6Onov4/knF +NXJHAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUqvyREBuH +kV8Wub9PS5FeAByxMoAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRCMEAwPqA8oDqG +OGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfZXZfcm9vdF9jYV8y +XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQCTy6UfmRHsmg1fLBWTxj++EI14 +QvBukEdHjqOSMo1wj/Zbjb6JzkcBahsgIIlbyIIQbODnmaprxiqgYzWRaoUlrRc4 +pZt+UPJ26oUFKidBK7GB0aL2QHWpDsvxVUjY7NHss+jOFKE17MJeNRqrphYBBo7q +3C+jisosketSjl8MmxfPy3MHGcRqwnNU73xDUmPBEcrCRbH0O1P1aa4846XerOhU +t7KR/aypH/KH5BfGSah82ApB9PI+53c0BFLd6IHyTS9URZ0V4U/M5d40VxDJI3IX +cI1QcB9WbMy5/zpaT2N6w25lBx2Eof+pDGOJbbJAiDnXH3dotfyc1dZnaVuodNv8 +ifYbMvekJKZ2t0dT741Jj6m2g1qllpBFYfXeA08mD6iL8AOWsKwV0HFaanuU5nCT +2vFp4LJiTZ6P/4mdm13NRemUAiKN4DV/6PEEeXFsVIP4M7kFMhtYVRFP0OUnR3Hs +7dpn1mKmS00PaaLJvOwiS5THaJQXfuKOKD62xur1NGyfN4gHONuGcfrNlUhDbqNP +gofXNJhuS5N5YHVpD/Aa1VP6IQzCP+k/HxiMkl14p3ZnGbuy6n/pcAlWVqOwDAst +Nl7F6cTVg8uGF5csbBNvh1qvSaYd2804BC5f4ko1Di1L+KIkBI3Y4WNeApI02phh +XBxvWHZks/wCuPWdCg== +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign RSA TLS Root CA 2022 - 1 O=SwissSign AG +# Subject: CN=SwissSign RSA TLS Root CA 2022 - 1 O=SwissSign AG +# Label: "SwissSign RSA TLS Root CA 2022 - 1" +# Serial: 388078645722908516278762308316089881486363258315 +# MD5 Fingerprint: 16:2e:e4:19:76:81:85:ba:8e:91:58:f1:15:ef:72:39 +# SHA1 Fingerprint: 81:34:0a:be:4c:cd:ce:cc:e7:7d:cc:8a:d4:57:e2:45:a0:77:5d:ce +# SHA256 Fingerprint: 19:31:44:f4:31:e0:fd:db:74:07:17:d4:de:92:6a:57:11:33:88:4b:43:60:d3:0e:27:29:13:cb:e6:60:ce:41 +-----BEGIN CERTIFICATE----- +MIIFkzCCA3ugAwIBAgIUQ/oMX04bgBhE79G0TzUfRPSA7cswDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzErMCkGA1UE +AxMiU3dpc3NTaWduIFJTQSBUTFMgUm9vdCBDQSAyMDIyIC0gMTAeFw0yMjA2MDgx +MTA4MjJaFw00NzA2MDgxMTA4MjJaMFExCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxT +d2lzc1NpZ24gQUcxKzApBgNVBAMTIlN3aXNzU2lnbiBSU0EgVExTIFJvb3QgQ0Eg +MjAyMiAtIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDLKmjiC8NX +vDVjvHClO/OMPE5Xlm7DTjak9gLKHqquuN6orx122ro10JFwB9+zBvKK8i5VUXu7 +LCTLf5ImgKO0lPaCoaTo+nUdWfMHamFk4saMla+ju45vVs9xzF6BYQ1t8qsCLqSX +5XH8irCRIFucdFJtrhUnWXjyCcplDn/L9Ovn3KlMd/YrFgSVrpxxpT8q2kFC5zyE +EPThPYxr4iuRR1VPuFa+Rd4iUU1OKNlfGUEGjw5NBuBwQCMBauTLE5tzrE0USJIt +/m2n+IdreXXhvhCxqohAWVTXz8TQm0SzOGlkjIHRI36qOTw7D59Ke4LKa2/KIj4x +0LDQKhySio/YGZxH5D4MucLNvkEM+KRHBdvBFzA4OmnczcNpI/2aDwLOEGrOyvi5 +KaM2iYauC8BPY7kGWUleDsFpswrzd34unYyzJ5jSmY0lpx+Gs6ZUcDj8fV3oT4MM +0ZPlEuRU2j7yrTrePjxF8CgPBrnh25d7mUWe3f6VWQQvdT/TromZhqwUtKiE+shd +OxtYk8EXlFXIC+OCeYSf8wCENO7cMdWP8vpPlkwGqnj73mSiI80fPsWMvDdUDrta +clXvyFu1cvh43zcgTFeRc5JzrBh3Q4IgaezprClG5QtO+DdziZaKHG29777YtvTK +wP1H8K4LWCDFyB02rpeNUIMmJCn3nTsPBQIDAQABo2MwYTAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBRvjmKLk0Ow4UD2p8P98Q+4 +DxU4pTAdBgNVHQ4EFgQUb45ii5NDsOFA9qfD/fEPuA8VOKUwDQYJKoZIhvcNAQEL +BQADggIBAKwsKUF9+lz1GpUYvyypiqkkVHX1uECry6gkUSsYP2OprphWKwVDIqO3 +10aewCoSPY6WlkDfDDOLazeROpW7OSltwAJsipQLBwJNGD77+3v1dj2b9l4wBlgz +Hqp41eZUBDqyggmNzhYzWUUo8aWjlw5DI/0LIICQ/+Mmz7hkkeUFjxOgdg3XNwwQ +iJb0Pr6VvfHDffCjw3lHC1ySFWPtUnWK50Zpy1FVCypM9fJkT6lc/2cyjlUtMoIc +gC9qkfjLvH4YoiaoLqNTKIftV+Vlek4ASltOU8liNr3CjlvrzG4ngRhZi0Rjn9UM +ZfQpZX+RLOV/fuiJz48gy20HQhFRJjKKLjpHE7iNvUcNCfAWpO2Whi4Z2L6MOuhF +LhG6rlrnub+xzI/goP+4s9GFe3lmozm1O2bYQL7Pt2eLSMkZJVX8vY3PXtpOpvJp +zv1/THfQwUY1mFwjmwJFQ5Ra3bxHrSL+ul4vkSkphnsh3m5kt8sNjzdbowhq6/Td +Ao9QAwKxuDdollDruF/UKIqlIgyKhPBZLtU30WHlQnNYKoH3dtvi4k0NX/a3vgW0 +rk4N3hY9A4GzJl5LuEsAz/+MF7psYC0nhzck5npgL7XTgwSqT0N1osGDsieYK7EO +gLrAhV5Cud+xYJHT6xh+cHiudoO+cVrQkOPKwRYlZ0rwtnu64ZzZ +-----END CERTIFICATE----- + +# Issuer: CN=OISTE Server Root ECC G1 O=OISTE Foundation +# Subject: CN=OISTE Server Root ECC G1 O=OISTE Foundation +# Label: "OISTE Server Root ECC G1" +# Serial: 47819833811561661340092227008453318557 +# MD5 Fingerprint: 42:a7:d2:35:ae:02:92:db:19:76:08:de:2f:05:b4:d4 +# SHA1 Fingerprint: 3b:f6:8b:09:ae:2a:92:7b:ba:e3:8d:3f:11:95:d9:e6:44:0c:45:e2 +# SHA256 Fingerprint: ee:c9:97:c0:c3:0f:21:6f:7e:3b:8b:30:7d:2b:ae:42:41:2d:75:3f:c8:21:9d:af:d1:52:0b:25:72:85:0f:49 +-----BEGIN CERTIFICATE----- +MIICNTCCAbqgAwIBAgIQI/nD1jWvjyhLH/BU6n6XnTAKBggqhkjOPQQDAzBLMQsw +CQYDVQQGEwJDSDEZMBcGA1UECgwQT0lTVEUgRm91bmRhdGlvbjEhMB8GA1UEAwwY +T0lTVEUgU2VydmVyIFJvb3QgRUNDIEcxMB4XDTIzMDUzMTE0NDIyOFoXDTQ4MDUy +NDE0NDIyN1owSzELMAkGA1UEBhMCQ0gxGTAXBgNVBAoMEE9JU1RFIEZvdW5kYXRp +b24xITAfBgNVBAMMGE9JU1RFIFNlcnZlciBSb290IEVDQyBHMTB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABBcv+hK8rBjzCvRE1nZCnrPoH7d5qVi2+GXROiFPqOujvqQy +cvO2Ackr/XeFblPdreqqLiWStukhEaivtUwL85Zgmjvn6hp4LrQ95SjeHIC6XG4N +2xml4z+cKrhAS93mT6NjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBQ3 +TYhlz/w9itWj8UnATgwQb0K0nDAdBgNVHQ4EFgQUN02IZc/8PYrVo/FJwE4MEG9C +tJwwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2kAMGYCMQCpKjAd0MKfkFFR +QD6VVCHNFmb3U2wIFjnQEnx/Yxvf4zgAOdktUyBFCxxgZzFDJe0CMQCSia7pXGKD +YmH5LVerVrkR3SW+ak5KGoJr3M/TvEqzPNcum9v4KGm8ay3sMaE641c= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE Server Root RSA G1 O=OISTE Foundation +# Subject: CN=OISTE Server Root RSA G1 O=OISTE Foundation +# Label: "OISTE Server Root RSA G1" +# Serial: 113845518112613905024960613408179309848 +# MD5 Fingerprint: 23:a7:9e:d4:70:b8:b9:14:57:41:8a:7e:44:59:e2:68 +# SHA1 Fingerprint: f7:00:34:25:94:88:68:31:e4:34:87:3f:70:fe:86:b3:86:9f:f0:6e +# SHA256 Fingerprint: 9a:e3:62:32:a5:18:9f:fd:db:35:3d:fd:26:52:0c:01:53:95:d2:27:77:da:c5:9d:b5:7b:98:c0:89:a6:51:e6 +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIQVaXZZ5Qoxu0M+ifdWwFNGDANBgkqhkiG9w0BAQwFADBL +MQswCQYDVQQGEwJDSDEZMBcGA1UECgwQT0lTVEUgRm91bmRhdGlvbjEhMB8GA1UE +AwwYT0lTVEUgU2VydmVyIFJvb3QgUlNBIEcxMB4XDTIzMDUzMTE0MzcxNloXDTQ4 +MDUyNDE0MzcxNVowSzELMAkGA1UEBhMCQ0gxGTAXBgNVBAoMEE9JU1RFIEZvdW5k +YXRpb24xITAfBgNVBAMMGE9JU1RFIFNlcnZlciBSb290IFJTQSBHMTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAKqu9KuCz/vlNwvn1ZatkOhLKdxVYOPM +vLO8LZK55KN68YG0nnJyQ98/qwsmtO57Gmn7KNByXEptaZnwYx4M0rH/1ow00O7b +rEi56rAUjtgHqSSY3ekJvqgiG1k50SeH3BzN+Puz6+mTeO0Pzjd8JnduodgsIUzk +ik/HEzxux9UTl7Ko2yRpg1bTacuCErudG/L4NPKYKyqOBGf244ehHa1uzjZ0Dl4z +O8vbUZeUapU8zhhabkvG/AePLhq5SvdkNCncpo1Q4Y2LS+VIG24ugBA/5J8bZT8R +tOpXaZ+0AOuFJJkk9SGdl6r7NH8CaxWQrbueWhl/pIzY+m0o/DjH40ytas7ZTpOS +jswMZ78LS5bOZmdTaMsXEY5Z96ycG7mOaES3GK/m5Q9l3JUJsJMStR8+lKXHiHUh +sd4JJCpM4rzsTGdHwimIuQq6+cF0zowYJmXa92/GjHtoXAvuY8BeS/FOzJ8vD+Ho +mnqT8eDI278n5mUpezbgMxVz8p1rhAhoKzYHKyfMeNhqhw5HdPSqoBNdZH702xSu ++zrkL8Fl47l6QGzwBrd7KJvX4V84c5Ss2XCTLdyEr0YconosP4EmQufU2MVshGYR +i3drVByjtdgQ8K4p92cIiBdcuJd5z+orKu5YM+Vt6SmqZQENghPsJQtdLEByFSnT +kCz3GkPVavBpAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU +8snBDw1jALvsRQ5KH7WxszbNDo0wHQYDVR0OBBYEFPLJwQ8NYwC77EUOSh+1sbM2 +zQ6NMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQwFAAOCAgEANGd5sjrG5T33 +I3K5Ce+SrScfoE4KsvXaFwyihdJ+klH9FWXXXGtkFu6KRcoMQzZENdl//nk6HOjG +5D1rd9QhEOP28yBOqb6J8xycqd+8MDoX0TJD0KqKchxRKEzdNsjkLWd9kYccnbz8 +qyiWXmFcuCIzGEgWUOrKL+mlSdx/PKQZvDatkuK59EvV6wit53j+F8Bdh3foZ3dP +AGav9LEDOr4SfEE15fSmG0eLy3n31r8Xbk5l8PjaV8GUgeV6Vg27Rn9vkf195hfk +gSe7BYhW3SCl95gtkRlpMV+bMPKZrXJAlszYd2abtNUOshD+FKrDgHGdPY3ofRRs +YWSGRqbXVMW215AWRqWFyp464+YTFrYVI8ypKVL9AMb2kI5Wj4kI3Zaq5tNqqYY1 +9tVFeEJKRvwDyF7YZvZFZSS0vod7VSCd9521Kvy5YhnLbDuv0204bKt7ph6N/Ome +/msVuduCmsuY33OhkKCgxeDoAaijFJzIwZqsFVAzje18KotzlUBDJvyBpCpfOZC3 +J8tRd/iWkx7P8nd9H0aTolkelUTFLXVksNb54Dxp6gS1HAviRkRNQzuXSXERvSS2 +wq1yVAb+axj5d9spLFKebXd7Yv0PTY6YMjAwcRLWJTXjn/hvnLXrahut6hDTlhZy +BiElxky8j3C7DOReIoMt0r7+hVu05L0= +-----END CERTIFICATE----- + +# Issuer: CN=e-Szigno TLS Root CA 2023 O=Microsec Ltd. +# Subject: CN=e-Szigno TLS Root CA 2023 O=Microsec Ltd. +# Label: "e-Szigno TLS Root CA 2023" +# Serial: 71934828665710877219916191754 +# MD5 Fingerprint: 6a:e9:99:74:a5:da:5e:f1:d9:2e:f2:c8:d1:86:8b:71 +# SHA1 Fingerprint: 6f:9a:d5:d5:df:e8:2c:eb:be:37:07:ee:4f:4f:52:58:29:41:d1:fe +# SHA256 Fingerprint: b4:91:41:50:2d:00:66:3d:74:0f:2e:7e:c3:40:c5:28:00:96:26:66:12:1a:36:d0:9c:f7:dd:2b:90:38:4f:b4 +-----BEGIN CERTIFICATE----- +MIICzzCCAjGgAwIBAgINAOhvGHvWOWuYSkmYCjAKBggqhkjOPQQDBDB1MQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xFzAVBgNVBGEMDlZBVEhVLTIzNTg0NDk3MSIwIAYDVQQDDBllLVN6aWdubyBU +TFMgUm9vdCBDQSAyMDIzMB4XDTIzMDcxNzE0MDAwMFoXDTM4MDcxNzE0MDAwMFow +dTELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRYwFAYDVQQKDA1NaWNy +b3NlYyBMdGQuMRcwFQYDVQRhDA5WQVRIVS0yMzU4NDQ5NzEiMCAGA1UEAwwZZS1T +emlnbm8gVExTIFJvb3QgQ0EgMjAyMzCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAE +AGgP36J8PKp0iGEKjcJMpQEiFNT3YHdCnAo4YKGMZz6zY+n6kbCLS+Y53wLCMAFS +AL/fjO1ZrTJlqwlZULUZwmgcAOAFX9pQJhzDrAQixTpN7+lXWDajwRlTEArRzT/v +SzUaQ49CE0y5LBqcvjC2xN7cS53kpDzLLtmt3999Cd8ukv+ho2MwYTAPBgNVHRMB +Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUWYQCYlpGePVd3I8K +ECgj3NXW+0UwHwYDVR0jBBgwFoAUWYQCYlpGePVd3I8KECgj3NXW+0UwCgYIKoZI +zj0EAwQDgYsAMIGHAkIBLdqu9S54tma4n7Zwf2Z0z+yOfP7AAXmazlIC58PRDHpt +y7Ve7hekm9sEdu4pKeiv+62sUvTXK9Z3hBC9xdIoaDQCQTV2WnXzkoYI9bIeCvZl +C9p2x1L/Cx6AcCIwwzPbGO2E14vs7dOoY4G1VnxHx1YwlGhza9IuqbnZLBwpvQy6 +uWWL +-----END CERTIFICATE----- + +# Issuer: CN=SECOM TLS RSA Root CA 2024 O=SECOM Trust Systems Co., Ltd. +# Subject: CN=SECOM TLS RSA Root CA 2024 O=SECOM Trust Systems Co., Ltd. +# Label: "SECOM TLS RSA Root CA 2024" +# Serial: 17188327524208271538 +# MD5 Fingerprint: d0:a4:db:32:eb:44:98:d2:62:0b:3e:bc:4d:7c:5c:e9 +# SHA1 Fingerprint: fb:97:96:7c:ef:8d:98:63:06:c0:3b:b6:11:f8:e0:13:97:a2:98:d3 +# SHA256 Fingerprint: 14:35:f2:25:c5:d2:52:d7:a2:19:48:cc:3c:e6:2a:ec:fa:88:00:1e:3d:d7:2d:1c:c3:55:51:00:eb:37:2f:93 +-----BEGIN CERTIFICATE----- +MIIFmjCCA4KgAwIBAgIJAO6JNNDLgOCyMA0GCSqGSIb3DQEBDAUAMFoxCzAJBgNV +BAYTAkpQMSYwJAYDVQQKEx1TRUNPTSBUcnVzdCBTeXN0ZW1zIENvLiwgTHRkLjEj +MCEGA1UEAxMaU0VDT00gVExTIFJTQSBSb290IENBIDIwMjQwHhcNMjQwMTMxMDUx +MTU1WhcNNDkwMTE0MDUxMTU1WjBaMQswCQYDVQQGEwJKUDEmMCQGA1UEChMdU0VD +T00gVHJ1c3QgU3lzdGVtcyBDby4sIEx0ZC4xIzAhBgNVBAMTGlNFQ09NIFRMUyBS +U0EgUm9vdCBDQSAyMDI0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +4TjizUwzxbInq8Tx11gaFYNk5fO+34y7TyM4neh0UgL5JIZbJNLTz2x//L/B71+5 +m6X6nGIr7d4lFJBGtjO677hXOz93zkcWaUTm3VbOAjBlt4YWxlcccBHXuZ7o3Q+4 +R+ormrBdHeJ1CTUEG8ttQbKIl3G7OZYbnH8/pP8cjPub/0kDVNuMzp7xsVRROOis +Qt53fMoJLlYgoebbuMphOqMCtjkJ7R6efEMfLp8UAVi9ZaLRn76ET/CJkk925ndu +uufC4BatS4mnXFmxN0vUXb0ij9B8O/D8gixQEsVSD4GK8FWRPh3bVd/6bzdkHGJj +y21XI0yejVomZUbRrOfNuz0boPGV1pt18fFC39IHQEth3OFqb5NDO3L+A9bNqTgA +yUgRmIn4ucgDc/Ri/Km3V51ueZjy1/yk0qwJVadAVVrCt56iNeXOyEvzJADGgDQ8 +E1Pdaqct8Cynz/47ReQM62vFYO08wcQkrjmX/tesiko1V1yyaf6EfPzUFzmaGy9x +vkCwdbm15EdTolOjE0H2Vb5/APDOyCFEokiYGmXTLdAUl0wKZ4IyjkHGzy0jhpaX +EXE/GJcEvI6VzEchjaBL03EJ0h9pG4OqeIOycKvAo3A+TbetyfsrgYyHzU0a7/qU +jGat1AAq1nVljMpKqpinPTsf/d9H39FTUeJL7TpzzjUCAwEAAaNjMGEwHQYDVR0O +BBYEFCzrchKOWHdkNRVWNQFXB6l9DTbmMB8GA1UdIwQYMBaAFCzrchKOWHdkNRVW +NQFXB6l9DTbmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBDAUAA4ICAQAVwsvluSafaez5tFPR/hRTBzRxEyMMQF3XJXCVi3yegZyK +oec7hmE6jx2ZM8KgM1kn2yJRwFXHX8zUW9nBLEDWc4wuE8LrlZqhGZM9pJQXGmGz +ResDJV6JgRBna+j4sA1M7yIdlvL0sAfFXFCTRaWTD4E1V99RLrFzWfTcC+e180hD +uNMpqOEo46+lMeW/Wvh7ifOQs+kiK0O2gHxQDNxslSavnCs4V7l8HRDJ2La10o70 +Bo7VLzf1W8MBvv0VTnxB+NjT5qTAbhGFh9Gvp4BaJpmdUf0C5CEP6dbQlfgxWfzY +r69yVT6dPQB+GFEaY03IMY+AcBCs+om1fNxrQXt9zoofMBNFbLhvpNH/JsXWdGUz +fNbO12uswTa5wah8LB18FTQN2/zPHYmvBEoLuyUgZ09VNLJo5YA0kXItVYkLjMe2 +SixzK4scUHv81IK99I91DWx7FwMVKw2xgFp+ZLYB2dnpQQrqwlW64glHUcK2N9BD +snjLSxeZ+UPECh9RxH4WAcKiZW+cqaKMmhP2WBfR4IcR7NOL32ml11ds87hhV1CZ +WWFCJAcCidYZz6CZa8exzHojP9SB5RH0/v1KdHAisqhSjtJl/UIAHIQ48elOn8wr +TdFap4Yb5aHglmMeNx+fAIhDluWVfxTO7H4dTPU+SFVRMLAh+wwKZfqb94nMeQ== +-----END CERTIFICATE----- + +# Issuer: CN=SECOM TLS ECC Root CA 2024 O=SECOM Trust Systems Co., Ltd. +# Subject: CN=SECOM TLS ECC Root CA 2024 O=SECOM Trust Systems Co., Ltd. +# Label: "SECOM TLS ECC Root CA 2024" +# Serial: 9329818985461676612 +# MD5 Fingerprint: 99:d3:9d:e4:d2:b1:2d:f0:2a:04:67:85:f3:df:46:d6 +# SHA1 Fingerprint: 7a:1f:22:2d:72:b2:c3:19:87:44:db:61:69:e8:a6:4b:d7:0d:44:0e +# SHA256 Fingerprint: 6a:b2:ab:75:f5:1c:b4:f4:f0:15:62:03:fb:f6:f6:46:23:2f:51:4b:e0:59:f6:28:33:30:8b:82:b4:d7:2d:b1 +-----BEGIN CERTIFICATE----- +MIICTDCCAdGgAwIBAgIJAIF6LO+PI3pEMAoGCCqGSM49BAMDMFoxCzAJBgNVBAYT +AkpQMSYwJAYDVQQKEx1TRUNPTSBUcnVzdCBTeXN0ZW1zIENvLiwgTHRkLjEjMCEG +A1UEAxMaU0VDT00gVExTIEVDQyBSb290IENBIDIwMjQwHhcNMjQwMTMxMDU1MjM0 +WhcNNDkwMTE0MDU1MjM0WjBaMQswCQYDVQQGEwJKUDEmMCQGA1UEChMdU0VDT00g +VHJ1c3QgU3lzdGVtcyBDby4sIEx0ZC4xIzAhBgNVBAMTGlNFQ09NIFRMUyBFQ0Mg +Um9vdCBDQSAyMDI0MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE7NzFMtu9dzQXSNC1 +2fabk0+GlC5finB3R7XaZonRUd20aFiWObtuNBCLUZSfk6QXAE55BjEXsXQ/NG8y +UqicXjsu9ksDK3JZBgCwLOVh6+nwJXTvso/dEj/GUYH5mBdoo2MwYTAdBgNVHQ4E +FgQUO3YReyl04k4GTFaCQNAhL3qzydUwHwYDVR0jBBgwFoAUO3YReyl04k4GTFaC +QNAhL3qzydUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZI +zj0EAwMDaQAwZgIxAN3ib8fi1pMYtAPjMilB5e5/H+t5CL0xPL+cZ5oTTZuSCjpA +n1v7F/VAr8bFxQXAowIxAKsBVO1ACFp7skwzPvdv1EUY5a897WGLT4lb+bjxFAWy +l8wDcZJdwGZ/pAHxt1AJ1g== +-----END CERTIFICATE----- + +# Issuer: CN=Telia EC TLS Root CA v3 O=Telia Company AB +# Subject: CN=Telia EC TLS Root CA v3 O=Telia Company AB +# Label: "Telia EC TLS Root CA v3" +# Serial: 8028200332079443110287896228123317 +# MD5 Fingerprint: b6:fa:6a:5c:42:fb:c5:67:72:c0:e0:2f:72:fb:5a:44 +# SHA1 Fingerprint: b4:d6:07:c2:a5:95:bc:5b:f4:67:4d:c9:dc:6f:6f:0a:00:7a:a5:35 +# SHA256 Fingerprint: 09:8e:08:a9:1d:bb:f7:74:78:b9:6c:ce:b8:9b:14:13:a5:da:37:b7:c8:62:60:6a:95:5d:eb:07:17:9f:43:26 +-----BEGIN CERTIFICATE----- +MIICMjCCAbegAwIBAgIPAYvSIlRjTQSLbOVHH9K1MAoGCCqGSM49BAMDMEoxCzAJ +BgNVBAYTAlNFMRkwFwYDVQQKDBBUZWxpYSBDb21wYW55IEFCMSAwHgYDVQQDDBdU +ZWxpYSBFQyBUTFMgUm9vdCBDQSB2MzAeFw0yMzExMTUwODU1MjZaFw00ODA1MjMx +MTAwMDBaMEoxCzAJBgNVBAYTAlNFMRkwFwYDVQQKDBBUZWxpYSBDb21wYW55IEFC +MSAwHgYDVQQDDBdUZWxpYSBFQyBUTFMgUm9vdCBDQSB2MzB2MBAGByqGSM49AgEG +BSuBBAAiA2IABMHIlhVDLbmFKUpW0iK4dpryT6emYOeS31JPwWnWPmkWRrAkTbPX +40sQfHI9mpR7Rbktu3ngg6W+BBSXSechtMCnBmWXj/EaVlmV5cY1jD2HoTfhBQ3A +acpCNMLJK4NpZaNjMGEwHwYDVR0jBBgwFoAU1GToQ4g6cy/QGnGCNgtehd7H3kMw +HQYDVR0OBBYEFNRk6EOIOnMv0BpxgjYLXoXex95DMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2kAMGYCMQCXAUdS/9bbJ8A1JYaG +f/bWt/s7Ta0ot5Ulno8OjSNYRWQIlS4tVWldvTAVA7heOFgCMQCvKr8+Z2Rn+OBr +5UHzlgBObpad1LuwNTRcdNgUJxIWadcki+UBLEi1/AURKV5md2M= +-----END CERTIFICATE----- + +# Issuer: CN=Telia RSA TLS Root CA v3 O=Telia Company AB +# Subject: CN=Telia RSA TLS Root CA v3 O=Telia Company AB +# Label: "Telia RSA TLS Root CA v3" +# Serial: 8028214673410753541795188766939845 +# MD5 Fingerprint: f4:8b:9c:f3:f8:63:cf:dc:25:8f:b4:bb:a2:e9:9d:e2 +# SHA1 Fingerprint: b5:2e:88:4e:40:c1:11:fb:50:c7:e2:4f:ac:18:2b:bd:68:15:d2:34 +# SHA256 Fingerprint: d1:3d:b1:29:4c:45:eb:c6:fc:86:c6:bb:f6:9f:a2:9b:df:e6:92:df:f7:c7:13:c2:43:c7:a9:56:c6:a2:28:4c +-----BEGIN CERTIFICATE----- +MIIFgjCCA2qgAwIBAgIPAYvSUKtCVSxHWr2h3BrFMA0GCSqGSIb3DQEBDAUAMEsx +CzAJBgNVBAYTAlNFMRkwFwYDVQQKDBBUZWxpYSBDb21wYW55IEFCMSEwHwYDVQQD +DBhUZWxpYSBSU0EgVExTIFJvb3QgQ0EgdjMwHhcNMjMxMTE1MDk0NzQyWhcNNDgw +NTIzMTEwMDAwWjBLMQswCQYDVQQGEwJTRTEZMBcGA1UECgwQVGVsaWEgQ29tcGFu +eSBBQjEhMB8GA1UEAwwYVGVsaWEgUlNBIFRMUyBSb290IENBIHYzMIICIjANBgkq +hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsV89KG19hCf4S1Fvk8D3TyDERhmcvx8F +7Kmb4WATx3ije1id3KHxRE0TKmcNCbAQ57bvHFEYa4hR2l20VjVadExqOW+2ld99 +MbEiO+jRVOz+BbxLxJnmGwCqI+BfuTjjVReDxsxjQvjgBsClaO/sm5i70nlZcWGR +tIkvWDK3NNkT5RtwXc/O8NTFVpbUqT6cRjIj3olAblR+lRf4Ffy5o+Q9fabjYn9Z +9S4itruElcEFf9Ljk7fwdTycT/rvJW9w/B3G2a3r0f/zXNOVruIBcqE6pkSospAC +U2bG42fYKrbM/GWnp7u+p9Frz4jaNwpb4YHuEeS8BratNcP8X62jXIvvKHxlsMDJ +Cnb4U8JzFOLsU6mohVY58BdZrvi0Gk9UOuqmgoG6dskHoksjZTlK61D/InzmEoA1 +yAYJFDVysjRxDUOu9cAwANbqmq77WIFL6BpnZgVqPtMfG6wN8BrTKdapvilVsYR5 +9BFgIsAVBMxrGh+W+QcvmJafUpASvlArKvVG2FI4i6PiLjSBT0+6F6EQLrYqefOQ +F/fBNEXb+njUQ0SUVrAqtH4Y+OjCI/a4/JJQppxeemZcQ0SUShgiI5AM5xHO5iya +UrTjYH4zxUz9j+1FEbDH/xpstr1gXBykspup+hRTaJcbA+UbpJqtWZndAPddJmt6 +YJQ+dU3pDu8CAwEAAaNjMGEwHwYDVR0jBBgwFoAUsMep0t2yKFZzBJSMFFxIbzdS +kqgwHQYDVR0OBBYEFLDHqdLdsihWcwSUjBRcSG83UpKoMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQBdYzFsNGDRk7bR +/AgRKq+5637YuOW+w6uhpoS0VnKMUpyHCwku86hEvqivakPtfmlm4bFwt++sb/8O +XsWBqtfbXMaBNDTZl8XRMJuLWOW2JrbKkRzgG0eBUcvsadG1rrhbmZqYvFXaAZO7 +o4TdOZzxhBB5GOAWWXB3IeraNP4J63zyo9n8Gqw3sJBG44em5hoYjBffP+npibys +lnslRi4L6xHsCYj/Pab+OlqbMCB6v+sTCLeEIukRVzoR9aQ45pEK7Z1QBnSsbAKQ +tss0JKD9d/mX143H1xePjPhTXlv5JCkhrcj+SShz0P9+EHoWe6m9lyUEOIVn0rp+ +yVJWNbmyDv3VkwFxHC1ApSQsgSimjGQ4wtr6cSmordYxkV+Ro8lOIIhRksXPyDk2 +7gW6IjUXCkZKpxFjkL3jiBSc8SkxnwCWtXg8xwNwdFVNBGLCCuJnsneYXjJNqzRq +UcoGwzsvF3Qi/ZnHUNvISdevlgIAXL4Wvrxaqvoa01wB+GCfs57RTGE4TvAGhKNK +us8K3hRT1BSpigzMIRzSxtAOrqPN6j//QSmW9f8Jcncri4j2ihSpVrFU0NdNkMhZ +eAKidTFPsxCVFuW4Aniz7jqiw5sWtjbQrlW035izIEU4sYwQoC1Nx0Svy+mMTRai +50LqFQ+A1/Hq6xHHDNx7CI83d23Erw== +-----END CERTIFICATE----- diff --git a/venv/lib/python3.12/site-packages/certifi/core.py b/venv/lib/python3.12/site-packages/certifi/core.py new file mode 100644 index 0000000..1c9661c --- /dev/null +++ b/venv/lib/python3.12/site-packages/certifi/core.py @@ -0,0 +1,83 @@ +""" +certifi.py +~~~~~~~~~~ + +This module returns the installation location of cacert.pem or its contents. +""" +import sys +import atexit + +def exit_cacert_ctx() -> None: + _CACERT_CTX.__exit__(None, None, None) # type: ignore[union-attr] + + +if sys.version_info >= (3, 11): + + from importlib.resources import as_file, files + + _CACERT_CTX = None + _CACERT_PATH = None + + def where() -> str: + # This is slightly terrible, but we want to delay extracting the file + # in cases where we're inside of a zipimport situation until someone + # actually calls where(), but we don't want to re-extract the file + # on every call of where(), so we'll do it once then store it in a + # global variable. + global _CACERT_CTX + global _CACERT_PATH + if _CACERT_PATH is None: + # This is slightly janky, the importlib.resources API wants you to + # manage the cleanup of this file, so it doesn't actually return a + # path, it returns a context manager that will give you the path + # when you enter it and will do any cleanup when you leave it. In + # the common case of not needing a temporary file, it will just + # return the file system location and the __exit__() is a no-op. + # + # We also have to hold onto the actual context manager, because + # it will do the cleanup whenever it gets garbage collected, so + # we will also store that at the global level as well. + _CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem")) + _CACERT_PATH = str(_CACERT_CTX.__enter__()) + atexit.register(exit_cacert_ctx) + + return _CACERT_PATH + + def contents() -> str: + return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii") + +else: + + from importlib.resources import path as get_path, read_text + + _CACERT_CTX = None + _CACERT_PATH = None + + def where() -> str: + # This is slightly terrible, but we want to delay extracting the + # file in cases where we're inside of a zipimport situation until + # someone actually calls where(), but we don't want to re-extract + # the file on every call of where(), so we'll do it once then store + # it in a global variable. + global _CACERT_CTX + global _CACERT_PATH + if _CACERT_PATH is None: + # This is slightly janky, the importlib.resources API wants you + # to manage the cleanup of this file, so it doesn't actually + # return a path, it returns a context manager that will give + # you the path when you enter it and will do any cleanup when + # you leave it. In the common case of not needing a temporary + # file, it will just return the file system location and the + # __exit__() is a no-op. + # + # We also have to hold onto the actual context manager, because + # it will do the cleanup whenever it gets garbage collected, so + # we will also store that at the global level as well. + _CACERT_CTX = get_path("certifi", "cacert.pem") + _CACERT_PATH = str(_CACERT_CTX.__enter__()) + atexit.register(exit_cacert_ctx) + + return _CACERT_PATH + + def contents() -> str: + return read_text("certifi", "cacert.pem", encoding="ascii") diff --git a/venv/lib/python3.12/site-packages/certifi/py.typed b/venv/lib/python3.12/site-packages/certifi/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/certifi/tests/__init__.py b/venv/lib/python3.12/site-packages/certifi/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/certifi/tests/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/certifi/tests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f68522d Binary files /dev/null and b/venv/lib/python3.12/site-packages/certifi/tests/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/certifi/tests/__pycache__/test_certify.cpython-312.pyc b/venv/lib/python3.12/site-packages/certifi/tests/__pycache__/test_certify.cpython-312.pyc new file mode 100644 index 0000000..07d0ade Binary files /dev/null and b/venv/lib/python3.12/site-packages/certifi/tests/__pycache__/test_certify.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/certifi/tests/test_certify.py b/venv/lib/python3.12/site-packages/certifi/tests/test_certify.py new file mode 100755 index 0000000..54670ea --- /dev/null +++ b/venv/lib/python3.12/site-packages/certifi/tests/test_certify.py @@ -0,0 +1,18 @@ +import os +import unittest + +import certifi + + +class TestCertifi(unittest.TestCase): + def test_cabundle_exists(self) -> None: + assert os.path.exists(certifi.where()) + + def test_read_contents(self) -> None: + content = certifi.contents() + assert "-----BEGIN CERTIFICATE-----" in content + + def test_py_typed_exists(self) -> None: + assert os.path.exists( + os.path.join(os.path.dirname(certifi.__file__), 'py.typed') + ) diff --git a/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/METADATA b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/METADATA new file mode 100644 index 0000000..7c07654 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/METADATA @@ -0,0 +1,67 @@ +Metadata-Version: 2.4 +Name: cffi +Version: 2.1.1 +Summary: Foreign Function Interface for Python calling C code. +Author: Armin Rigo, Maciej Fijalkowski +Maintainer: Matt Davis, Matt Clay +License-Expression: MIT-0 +Project-URL: Documentation, https://cffi.readthedocs.io/ +Project-URL: Changelog, https://cffi.readthedocs.io/en/latest/whatsnew.html +Project-URL: Download, https://github.com/python-cffi/cffi/releases +Project-URL: Contact, https://groups.google.com/forum/#!forum/python-cffi +Project-URL: Source Code, https://github.com/python-cffi/cffi +Project-URL: Issue Tracker, https://github.com/python-cffi/cffi/issues +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: 3.15 +Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta +Classifier: Programming Language :: Python :: Implementation :: CPython +Requires-Python: >=3.10 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: pycparser; implementation_name != "PyPy" +Dynamic: license-file + +[![GitHub Actions Status](https://github.com/python-cffi/cffi/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/python-cffi/cffi/actions/workflows/ci.yaml?query=branch%3Amain++) +[![PyPI version](https://img.shields.io/pypi/v/cffi.svg)](https://pypi.org/project/cffi) +[![Read the Docs](https://img.shields.io/badge/docs-latest-blue.svg)][Documentation] + + +CFFI +==== + +Foreign Function Interface for Python calling C code. + +Please see the [Documentation] or uncompiled in the `doc/` subdirectory. + +Download +-------- + +[Download page](https://github.com/python-cffi/cffi/releases) + +Source Code +----------- + +Source code is publicly available on +[GitHub](https://github.com/python-cffi/cffi). + +Contact +------- + +[Mailing list](https://groups.google.com/forum/#!forum/python-cffi) + +Testing/development tips +------------------------ + +After `git clone` or `wget && tar`, we will get a directory called `cffi` or `cffi-x.x.x`. We call it `repo-directory`. To run tests under CPython, run the following in the `repo-directory`: + + pip install pytest + pip install -e . # editable install of CFFI for local development + pytest src/c/ testing/ + +[Documentation]: http://cffi.readthedocs.org/ diff --git a/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/RECORD new file mode 100644 index 0000000..a9ec150 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/RECORD @@ -0,0 +1,53 @@ +../../../bin/cffi-gen-src,sha256=G-DlPmw4YxxX5CSNVvrWo9B-mk9c_smtcWYrgzEiP6M,188 +_cffi_backend.cpython-312-x86_64-linux-gnu.so,sha256=m8HQgHIlWl7C2xD8dd8d2ecwl6dc2RUM_odevWcS8Xw,348856 +cffi-2.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +cffi-2.1.1.dist-info/METADATA,sha256=VYuvuQk6jnkBddns_5aIOCmB10SHfa2G_Wdw2w9NyRg,2527 +cffi-2.1.1.dist-info/RECORD,, +cffi-2.1.1.dist-info/WHEEL,sha256=UErIDMlRkMma6hd1UFY9eTm4_KaxQxwBwNQ150r7vQ4,151 +cffi-2.1.1.dist-info/entry_points.txt,sha256=prYQgBH44nBQ_lJpu6iMgFG0FGl4gNOwtvvjoDoLABw,132 +cffi-2.1.1.dist-info/licenses/LICENSE,sha256=W6JN3FcGf5JJrdZEw6_EGl1tw34jQz73Wdld83Cwr2M,1123 +cffi-2.1.1.dist-info/top_level.txt,sha256=rE7WR3rZfNKxWI9-jn6hsHCAl7MDkB-FmuQbxWjFehQ,19 +cffi/__init__.py,sha256=xHstsoxfGX4kLU3Ur5W367DaZXY_x2lyyv0G-T6ULNo,511 +cffi/__pycache__/__init__.cpython-312.pyc,, +cffi/__pycache__/_cffi_gen_src.cpython-312.pyc,, +cffi/__pycache__/_imp_emulation.cpython-312.pyc,, +cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc,, +cffi/__pycache__/api.cpython-312.pyc,, +cffi/__pycache__/backend_ctypes.cpython-312.pyc,, +cffi/__pycache__/cffi_opcode.cpython-312.pyc,, +cffi/__pycache__/commontypes.cpython-312.pyc,, +cffi/__pycache__/cparser.cpython-312.pyc,, +cffi/__pycache__/error.cpython-312.pyc,, +cffi/__pycache__/ffiplatform.cpython-312.pyc,, +cffi/__pycache__/gen_src.cpython-312.pyc,, +cffi/__pycache__/lock.cpython-312.pyc,, +cffi/__pycache__/model.cpython-312.pyc,, +cffi/__pycache__/pkgconfig.cpython-312.pyc,, +cffi/__pycache__/recompiler.cpython-312.pyc,, +cffi/__pycache__/setuptools_ext.cpython-312.pyc,, +cffi/__pycache__/vengine_cpy.cpython-312.pyc,, +cffi/__pycache__/vengine_gen.cpython-312.pyc,, +cffi/__pycache__/verifier.cpython-312.pyc,, +cffi/_cffi_errors.h,sha256=Wzg-0Bu1_K2GEK0pSU5b6Q2GAAudcaF87HNG8LoFmWw,3583 +cffi/_cffi_gen_src.py,sha256=yUR53pFSbsa3ssF7_VOqXTaKeL5AJTHM_dyZ9IGY9sw,7044 +cffi/_cffi_include.h,sha256=zY6KFoo_bpa02PagQs1hzeW9O56snioloyxqvRKAot8,15234 +cffi/_embedding.h,sha256=PhAdAMcKClATVZ-lBcZeDEc12F6FqVdaHtaJ2Pkg6E8,17554 +cffi/_imp_emulation.py,sha256=RxREG8zAbI2RPGBww90u_5fi8sWdahpdipOoPzkp7C0,2960 +cffi/_shimmed_dist_utils.py,sha256=Bjj2wm8yZbvFvWEx5AEfmqaqZyZFhYfoyLLQHkXZuao,2230 +cffi/api.py,sha256=cWX1ZlscmOyTkvthiBpUWLcSRBtqlpn-moBu4esBmzI,42013 +cffi/backend_ctypes.py,sha256=T7CSc14P5vyloi5hyox68qhBWX079owHXaGDbY_U7bo,42283 +cffi/cffi_opcode.py,sha256=5-1cU6ppHGzsstF_eoglD_8tCDk76_Zhkzj3fW4Sic0,5723 +cffi/commontypes.py,sha256=7N6zPtCFlvxXMWhHV08psUjdYIK2XgsN3yo5dgua_v4,2805 +cffi/cparser.py,sha256=WY-ox4UGSuVSYTtgm02FmGgbmcuaaBK3moFlkdFSmsU,44754 +cffi/error.py,sha256=v6xTiS4U0kvDcy4h_BDRo5v39ZQuj-IMRYLv5ETddZs,877 +cffi/ffiplatform.py,sha256=avxFjdikYGJoEtmJO7ewVmwG_VEVl6EZ_WaNhZYCqv4,3584 +cffi/gen_src.py,sha256=TU_ZVDwoLnHPlal8_mPmpxqyWJ7U752jTc3UsTmE1QI,132 +cffi/lock.py,sha256=l9TTdwMIMpi6jDkJGnQgE9cvTIR7CAntIJr8EGHt3pY,747 +cffi/model.py,sha256=cA-bEAibcHUbtbqfgMq0CLQjFPAjV0JSRzMyKViNgF4,21752 +cffi/parse_c_type.h,sha256=OdwQfwM9ktq6vlCB43exFQmxDBtj2MBNdK8LYl15tjw,5976 +cffi/pkgconfig.py,sha256=uyt117TQeea3djagM1W5go8RpnySGTgoCzN8QHyZdlc,4365 +cffi/recompiler.py,sha256=TJVF07mFzYGTV6szp2zPrJd2dJKHN-eB_SuiJiGl9Kw,65140 +cffi/setuptools_ext.py,sha256=pZrnTqVCkGMqu8NxIxkdbItl65gehMP1LoraUdT7-4U,9566 +cffi/vengine_cpy.py,sha256=rRouYQ6BllAIXNjZGTPUW54rB952qi3gcm16SWXWKLc,43030 +cffi/vengine_gen.py,sha256=Z_KqaF6r54n5i4hjQThZdb-1DBTEimnU4y4E_KxSCDg,26931 +cffi/verifier.py,sha256=xIzV8iDMhZ1-qhX3RRX4DWo2k07WpcJxdhhAFXAjrmY,11154 diff --git a/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/WHEEL b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/WHEEL new file mode 100644 index 0000000..50a7ad2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (83.0.0) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 + diff --git a/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/entry_points.txt new file mode 100644 index 0000000..46bb21b --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/entry_points.txt @@ -0,0 +1,5 @@ +[console_scripts] +cffi-gen-src = cffi._cffi_gen_src:run + +[distutils.setup_keywords] +cffi_modules = cffi.setuptools_ext:cffi_modules diff --git a/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/licenses/LICENSE new file mode 100644 index 0000000..0a1dbfb --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/licenses/LICENSE @@ -0,0 +1,23 @@ + +Except when otherwise stated (look for LICENSE files in directories or +information at the beginning of each file) all software and +documentation is licensed as follows: + + MIT No Attribution + + 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. + + 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. + diff --git a/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/top_level.txt new file mode 100644 index 0000000..f645779 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi-2.1.1.dist-info/top_level.txt @@ -0,0 +1,2 @@ +_cffi_backend +cffi diff --git a/venv/lib/python3.12/site-packages/cffi/__init__.py b/venv/lib/python3.12/site-packages/cffi/__init__.py new file mode 100644 index 0000000..93dcdb3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/__init__.py @@ -0,0 +1,14 @@ +__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError', + 'FFIError'] + +from .api import FFI +from .error import CDefError, FFIError, VerificationError, VerificationMissing +from .error import PkgConfigError + +__version__ = "2.1.1" +__version_info__ = (2, 1, 1) + +# The verifier module file names are based on the CRC32 of a string that +# contains the following version number. It may be older than __version__ +# if nothing is clearly incompatible. +__version_verifier_modules__ = "0.8.6" diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d298500 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/_cffi_gen_src.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/_cffi_gen_src.cpython-312.pyc new file mode 100644 index 0000000..a3086d5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/_cffi_gen_src.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/_imp_emulation.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/_imp_emulation.cpython-312.pyc new file mode 100644 index 0000000..1078520 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/_imp_emulation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc new file mode 100644 index 0000000..dab857e Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/api.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/api.cpython-312.pyc new file mode 100644 index 0000000..396d9d5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/api.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/backend_ctypes.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/backend_ctypes.cpython-312.pyc new file mode 100644 index 0000000..bf8ef1f Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/backend_ctypes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/cffi_opcode.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/cffi_opcode.cpython-312.pyc new file mode 100644 index 0000000..d7d2a13 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/cffi_opcode.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/commontypes.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/commontypes.cpython-312.pyc new file mode 100644 index 0000000..8a54a28 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/commontypes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/cparser.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/cparser.cpython-312.pyc new file mode 100644 index 0000000..6eaec32 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/cparser.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/error.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/error.cpython-312.pyc new file mode 100644 index 0000000..cd9afdb Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/ffiplatform.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/ffiplatform.cpython-312.pyc new file mode 100644 index 0000000..1a36c60 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/ffiplatform.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/gen_src.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/gen_src.cpython-312.pyc new file mode 100644 index 0000000..a4f5631 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/gen_src.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/lock.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/lock.cpython-312.pyc new file mode 100644 index 0000000..8de30f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/lock.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/model.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/model.cpython-312.pyc new file mode 100644 index 0000000..2565a6e Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/pkgconfig.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/pkgconfig.cpython-312.pyc new file mode 100644 index 0000000..37328c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/pkgconfig.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/recompiler.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/recompiler.cpython-312.pyc new file mode 100644 index 0000000..be42536 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/recompiler.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/setuptools_ext.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/setuptools_ext.cpython-312.pyc new file mode 100644 index 0000000..4eff526 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/setuptools_ext.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/vengine_cpy.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/vengine_cpy.cpython-312.pyc new file mode 100644 index 0000000..befd8ee Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/vengine_cpy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/vengine_gen.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/vengine_gen.cpython-312.pyc new file mode 100644 index 0000000..a3f0b1d Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/vengine_gen.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/__pycache__/verifier.cpython-312.pyc b/venv/lib/python3.12/site-packages/cffi/__pycache__/verifier.cpython-312.pyc new file mode 100644 index 0000000..299eeca Binary files /dev/null and b/venv/lib/python3.12/site-packages/cffi/__pycache__/verifier.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cffi/_cffi_errors.h b/venv/lib/python3.12/site-packages/cffi/_cffi_errors.h new file mode 100644 index 0000000..0931379 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/_cffi_errors.h @@ -0,0 +1,135 @@ +#ifndef CFFI_MESSAGEBOX +# ifdef _MSC_VER +# define CFFI_MESSAGEBOX 1 +# else +# define CFFI_MESSAGEBOX 0 +# endif +#endif + + +#if CFFI_MESSAGEBOX +/* Windows only: logic to take the Python-CFFI embedding logic + initialization errors and display them in a background thread + with MessageBox. The idea is that if the whole program closes + as a result of this problem, then likely it is already a console + program and you can read the stderr output in the console too. + If it is not a console program, then it will likely show its own + dialog to complain, or generally not abruptly close, and for this + case the background thread should stay alive. +*/ +static void *volatile _cffi_bootstrap_text; + +static PyObject *_cffi_start_error_capture(void) +{ + PyObject *result = NULL; + PyObject *x, *m, *bi; + + if (InterlockedCompareExchangePointer(&_cffi_bootstrap_text, + (void *)1, NULL) != NULL) + return (PyObject *)1; + + m = PyImport_AddModule("_cffi_error_capture"); + if (m == NULL) + goto error; + + result = PyModule_GetDict(m); + if (result == NULL) + goto error; + + bi = PyImport_ImportModule("builtins"); + if (bi == NULL) + goto error; + PyDict_SetItemString(result, "__builtins__", bi); + Py_DECREF(bi); + + x = PyRun_String( + "import sys\n" + "class FileLike:\n" + " def write(self, x):\n" + " try:\n" + " of.write(x)\n" + " except: pass\n" + " self.buf += x\n" + " def flush(self):\n" + " pass\n" + "fl = FileLike()\n" + "fl.buf = ''\n" + "of = sys.stderr\n" + "sys.stderr = fl\n" + "def done():\n" + " sys.stderr = of\n" + " return fl.buf\n", /* make sure the returned value stays alive */ + Py_file_input, + result, result); + Py_XDECREF(x); + + error: + if (PyErr_Occurred()) + { + PyErr_WriteUnraisable(Py_None); + PyErr_Clear(); + } + return result; +} + +#pragma comment(lib, "user32.lib") + +static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored) +{ + Sleep(666); /* may be interrupted if the whole process is closing */ + MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text, + L"Python-CFFI error", + MB_OK | MB_ICONERROR); + _cffi_bootstrap_text = NULL; + return 0; +} + +static void _cffi_stop_error_capture(PyObject *ecap) +{ + PyObject *s; + void *text; + + if (ecap == (PyObject *)1) + return; + + if (ecap == NULL) + goto error; + + s = PyRun_String("done()", Py_eval_input, ecap, ecap); + if (s == NULL) + goto error; + + /* Show a dialog box, but in a background thread, and + never show multiple dialog boxes at once. */ + text = PyUnicode_AsWideCharString(s, NULL); + + _cffi_bootstrap_text = text; + + if (text != NULL) + { + HANDLE h; + h = CreateThread(NULL, 0, _cffi_bootstrap_dialog, + NULL, 0, NULL); + if (h != NULL) + CloseHandle(h); + } + /* decref the string, but it should stay alive as 'fl.buf' + in the small module above. It will really be freed only if + we later get another similar error. So it's a leak of at + most one copy of the small module. That's fine for this + situation which is usually a "fatal error" anyway. */ + Py_DECREF(s); + PyErr_Clear(); + return; + + error: + _cffi_bootstrap_text = NULL; + PyErr_Clear(); +} + +#else + +static PyObject *_cffi_start_error_capture(void) { return NULL; } +static void _cffi_stop_error_capture(PyObject *ecap) { } + +#endif diff --git a/venv/lib/python3.12/site-packages/cffi/_cffi_gen_src.py b/venv/lib/python3.12/site-packages/cffi/_cffi_gen_src.py new file mode 100644 index 0000000..3d4fb7e --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/_cffi_gen_src.py @@ -0,0 +1,216 @@ +# Integrated from the cffi-buildtool project by Rose Davidson +# (https://github.com/inklesspen/cffi-buildtool), under the following +# license: +# +# MIT License +# +# Copyright (c) 2024, Rose Davidson +# +# 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 (including the +# next paragraph) 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. +"""Implementation of the ``cffi-gen-src`` command-line tool. + +This module is private; the command line is the only supported +interface. Two subcommands: + +``exec-python`` + Execute a Python script that constructs a :class:`cffi.FFI` + (the same kind of script that the CFFI docs' "Main mode of usage" + describes) and emit the generated C source. + +``read-sources`` + Create the :class:`cffi.FFI` from a separate ``cdef`` file and C + source prelude, then emit the generated C source. +""" + +import argparse +import io +import os +import sys + +from .api import FFI + + +def _execfile(pysrc, filename, globs): + compiled = compile(source=pysrc, filename=filename, mode='exec') + exec(compiled, globs, globs) + + +def find_ffi_in_python_script(pysrc, filename, ffivar): + """Execute ``pysrc`` and return the :class:`FFI` object it defines. + + The script is executed with ``__name__`` set to ``"cffi.gen_src"``, + so a trailing ``if __name__ == "__main__": ffibuilder.compile()`` + block in the script is skipped. + + ``ffivar`` is the name bound by the script to the :class:`FFI` + object, or to a callable that returns one. + + Raises :class:`NameError` if the name is not bound by the script, + or :class:`TypeError` if the name does not resolve to an + :class:`FFI` instance. + """ + filename = os.path.abspath(filename) + globs = {'__name__': 'cffi.gen_src', '__file__': filename} + old_path = sys.path[:] + sys.path.insert(0, os.path.dirname(filename)) + try: + _execfile(pysrc, filename, globs) + if ffivar not in globs: + raise NameError( + "Expected to find the FFI object with the name %r, " + "but it was not found." % (ffivar,) + ) + ffi = globs[ffivar] + if not isinstance(ffi, FFI) and callable(ffi): + # Maybe it's a callable that returns a FFI + ffi = ffi() + if not isinstance(ffi, FFI): + raise TypeError( + "Found an object with the name %r but it was not an " + "instance of cffi.api.FFI" % (ffivar,) + ) + return ffi + finally: + sys.path[:] = old_path + + +def make_ffi_from_sources(modulename, cdef, csrc): + """Create an :class:`FFI` from ``cdef`` text and a C source prelude.""" + ffibuilder = FFI() + ffibuilder.cdef(cdef) + ffibuilder.set_source(modulename, csrc) + return ffibuilder + + +def generate_c_source(ffi): + """Return the C source that :meth:`FFI.emit_c_code` would write.""" + output = io.StringIO() + ffi.emit_c_code(output) + return output.getvalue() + + +def write_c_source(output, generated): + if output == '-': + sys.stdout.write(generated) + return + with open(output, 'w', encoding='utf-8') as f: + f.write(generated) + + +def same_input_file(file1, file2): + if file1 is file2: + return True + try: + return os.path.samefile(file1.name, file2.name) + except OSError: + return False + + +def exec_python(*, output, pyfile, ffi_var): + with pyfile: + ffi = find_ffi_in_python_script(pyfile.read(), pyfile.name, ffi_var) + generated = generate_c_source(ffi) + write_c_source(output, generated) + + +def read_sources(*, output, module_name, cdef_input, csrc_input): + with csrc_input, cdef_input: + csrc = csrc_input.read() + cdef = cdef_input.read() + ffi = make_ffi_from_sources(module_name, cdef, csrc) + generated = generate_c_source(ffi) + write_c_source(output, generated) + + +def _prog(): + # The same parser serves both documented invocations; make --help + # and usage messages show the one that was actually used. + argv0 = os.path.basename(sys.argv[0]) if sys.argv else '' + if argv0.startswith('cffi-gen-src'): + return 'cffi-gen-src' + return 'python -m cffi.gen_src' + + +parser = argparse.ArgumentParser( + prog=_prog(), + description='Generate CFFI C source for extension modules.', +) +subparsers = parser.add_subparsers(dest='mode') + +exec_python_parser = subparsers.add_parser( + 'exec-python', + help='Execute a Python script that defines an FFI object', +) +exec_python_parser.add_argument( + '--ffi-var', + default='ffibuilder', + help="Name of the FFI object in the Python script; defaults to 'ffibuilder'.", +) +exec_python_parser.add_argument( + 'pyfile', + type=argparse.FileType('r', encoding='utf-8'), + help='Path to the Python script', +) +exec_python_parser.add_argument( + 'output', + help='Output path for the C source', +) + +read_sources_parser = subparsers.add_parser( + 'read-sources', + help='Read cdef and C source prelude files that define an FFI object', +) +read_sources_parser.add_argument( + 'module_name', + help='Full name of the generated module, including packages', +) +read_sources_parser.add_argument( + 'cdef', + type=argparse.FileType('r', encoding='utf-8'), + help='File containing C definitions', +) +read_sources_parser.add_argument( + 'csrc', + type=argparse.FileType('r', encoding='utf-8'), + help='File containing C source prelude', +) +read_sources_parser.add_argument( + 'output', + help='Output path for the C source', +) + + +def run(args=None): + args = parser.parse_args(args=args) + if args.mode == 'exec-python': + exec_python(output=args.output, pyfile=args.pyfile, ffi_var=args.ffi_var) + elif args.mode == 'read-sources': + if same_input_file(args.cdef, args.csrc): + parser.error('cdef and csrc are the same file and should not be') + read_sources( + output=args.output, + module_name=args.module_name, + cdef_input=args.cdef, + csrc_input=args.csrc, + ) + else: + parser.error('a subcommand is required: exec-python or read-sources') + parser.exit(0) diff --git a/venv/lib/python3.12/site-packages/cffi/_cffi_include.h b/venv/lib/python3.12/site-packages/cffi/_cffi_include.h new file mode 100644 index 0000000..c8c6d34 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/_cffi_include.h @@ -0,0 +1,397 @@ +#define _CFFI_ + +/* We try to define Py_LIMITED_API before including Python.h. + + Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and + Py_REF_DEBUG are not defined. This is a best-effort approximation: + we can learn about Py_DEBUG from pyconfig.h, but it is unclear if + the same works for the other two macros. Py_DEBUG implies them, + but not the other way around. + + The implementation is messy (issue #350): on Windows, with _MSC_VER, + we have to define Py_LIMITED_API even before including pyconfig.h. + In that case, we guess what pyconfig.h will do to the macros above, + and check our guess after the #include. + + Note that on Windows, with CPython 3.x, you need >= 3.5 and virtualenv + version >= 16.0.0. With older versions of either, you don't get a + copy of PYTHON3.DLL in the virtualenv. We can't check the version of + CPython *before* we even include pyconfig.h. ffi.set_source() puts + a ``#define _CFFI_NO_LIMITED_API'' at the start of this file if it is + running on Windows < 3.5, as an attempt at fixing it, but that's + arguably wrong because it may not be the target version of Python. + Still better than nothing I guess. As another workaround, you can + remove the definition of Py_LIMITED_API here. + + See also 'py_limited_api' in cffi/setuptools_ext.py. +*/ +#if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) +# ifdef _MSC_VER +# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) +# if !defined(Py_GIL_DISABLED) +# define Py_LIMITED_API +# else +# define Py_LIMITED_API 0x030f0000 +# endif +# endif + +# include + /* sanity-check: Py_LIMITED_API will cause crashes if any of these + are also defined. Normally, the Python file PC/pyconfig.h does not + cause any of these to be defined, with the exception that _DEBUG + causes Py_DEBUG. Double-check that. */ +# ifdef Py_LIMITED_API +# if defined(Py_DEBUG) +# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" +# endif +# if defined(Py_TRACE_REFS) +# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" +# endif +# if defined(Py_REF_DEBUG) +# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" +# endif +# endif +# else +# include +# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) +# if !defined(Py_GIL_DISABLED) +# define Py_LIMITED_API +# else +# define Py_LIMITED_API 0x030f0000 +# endif +# endif +# endif +#endif + +#include +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include + +#include "parse_c_type.h" + +/* this block of #ifs should be kept exactly identical between + c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py + and cffi/_cffi_include.h */ +#if defined(_MSC_VER) +# include /* for alloca() */ +# if _MSC_VER < 1600 /* MSVC < 2010 */ + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; +# else +# include +# endif +# if _MSC_VER < 1800 /* MSVC < 2013 */ +# ifndef __cplusplus + typedef unsigned char _Bool; +# endif +# endif +# define _cffi_float_complex_t _Fcomplex /* include for it */ +# define _cffi_double_complex_t _Dcomplex /* include for it */ +#else +# include +# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) +# include +# endif +# define _cffi_float_complex_t float _Complex +# define _cffi_double_complex_t double _Complex +#endif + +#ifdef __GNUC__ +# define _CFFI_UNUSED_FN __attribute__((unused)) +#else +# define _CFFI_UNUSED_FN /* nothing */ +#endif + +#ifdef __cplusplus +# ifndef _Bool + typedef bool _Bool; /* semi-hackish: C++ has no _Bool; bool is builtin */ +# endif +#endif + +/********** CPython-specific section **********/ +#ifndef PYPY_VERSION + + +#define _cffi_from_c_double PyFloat_FromDouble +#define _cffi_from_c_float PyFloat_FromDouble +#define _cffi_from_c_long PyLong_FromLong +#define _cffi_from_c_ulong PyLong_FromUnsignedLong +#define _cffi_from_c_longlong PyLong_FromLongLong +#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong +#define _cffi_from_c__Bool PyBool_FromLong + +#define _cffi_to_c_double PyFloat_AsDouble +#define _cffi_to_c_float PyFloat_AsDouble + +#define _cffi_from_c_int(x, type) \ + (((type)-1) > 0 ? /* unsigned */ \ + (sizeof(type) < sizeof(long) ? \ + PyLong_FromLong((long)x) : \ + sizeof(type) == sizeof(long) ? \ + PyLong_FromUnsignedLong((unsigned long)x) : \ + PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ + (sizeof(type) <= sizeof(long) ? \ + PyLong_FromLong((long)x) : \ + PyLong_FromLongLong((long long)x))) + +#define _cffi_to_c_int(o, type) \ + ((type)( \ + sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ + : (type)_cffi_to_c_i8(o)) : \ + sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ + : (type)_cffi_to_c_i16(o)) : \ + sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ + : (type)_cffi_to_c_i32(o)) : \ + sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ + : (type)_cffi_to_c_i64(o)) : \ + (Py_FatalError("unsupported size for type " #type), (type)0))) + +#define _cffi_to_c_i8 \ + ((int(*)(PyObject *))_cffi_exports[1]) +#define _cffi_to_c_u8 \ + ((int(*)(PyObject *))_cffi_exports[2]) +#define _cffi_to_c_i16 \ + ((int(*)(PyObject *))_cffi_exports[3]) +#define _cffi_to_c_u16 \ + ((int(*)(PyObject *))_cffi_exports[4]) +#define _cffi_to_c_i32 \ + ((int(*)(PyObject *))_cffi_exports[5]) +#define _cffi_to_c_u32 \ + ((unsigned int(*)(PyObject *))_cffi_exports[6]) +#define _cffi_to_c_i64 \ + ((long long(*)(PyObject *))_cffi_exports[7]) +#define _cffi_to_c_u64 \ + ((unsigned long long(*)(PyObject *))_cffi_exports[8]) +#define _cffi_to_c_char \ + ((int(*)(PyObject *))_cffi_exports[9]) +#define _cffi_from_c_pointer \ + ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[10]) +#define _cffi_to_c_pointer \ + ((char *(*)(PyObject *, struct _cffi_ctypedescr *))_cffi_exports[11]) +#define _cffi_get_struct_layout \ + not used any more +#define _cffi_restore_errno \ + ((void(*)(void))_cffi_exports[13]) +#define _cffi_save_errno \ + ((void(*)(void))_cffi_exports[14]) +#define _cffi_from_c_char \ + ((PyObject *(*)(char))_cffi_exports[15]) +#define _cffi_from_c_deref \ + ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[16]) +#define _cffi_to_c \ + ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[17]) +#define _cffi_from_c_struct \ + ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[18]) +#define _cffi_to_c_wchar_t \ + ((_cffi_wchar_t(*)(PyObject *))_cffi_exports[19]) +#define _cffi_from_c_wchar_t \ + ((PyObject *(*)(_cffi_wchar_t))_cffi_exports[20]) +#define _cffi_to_c_long_double \ + ((long double(*)(PyObject *))_cffi_exports[21]) +#define _cffi_to_c__Bool \ + ((_Bool(*)(PyObject *))_cffi_exports[22]) +#define _cffi_prepare_pointer_call_argument \ + ((Py_ssize_t(*)(struct _cffi_ctypedescr *, \ + PyObject *, char **))_cffi_exports[23]) +#define _cffi_convert_array_from_object \ + ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[24]) +#define _CFFI_CPIDX 25 +#define _cffi_call_python \ + ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX]) +#define _cffi_to_c_wchar3216_t \ + ((int(*)(PyObject *))_cffi_exports[26]) +#define _cffi_from_c_wchar3216_t \ + ((PyObject *(*)(int))_cffi_exports[27]) +#define _CFFI_NUM_EXPORTS 28 + +struct _cffi_ctypedescr; + +static void *_cffi_exports[_CFFI_NUM_EXPORTS]; + +#define _cffi_type(index) ( \ + assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \ + (struct _cffi_ctypedescr *)_cffi_types[index]) + +static PyObject *_cffi_init(const char *module_name, Py_ssize_t version, + const struct _cffi_type_context_s *ctx) +{ + PyObject *module, *o_arg, *new_module; + void *raw[] = { + (void *)module_name, + (void *)version, + (void *)_cffi_exports, + (void *)ctx, + }; + + module = PyImport_ImportModule("_cffi_backend"); + if (module == NULL) + goto failure; + + o_arg = PyLong_FromVoidPtr((void *)raw); + if (o_arg == NULL) + goto failure; + + new_module = PyObject_CallMethod( + module, (char *)"_init_cffi_1_0_external_module", (char *)"O", o_arg); + + Py_DECREF(o_arg); + Py_DECREF(module); + return new_module; + + failure: + Py_XDECREF(module); + return NULL; +} + + +#ifdef HAVE_WCHAR_H +typedef wchar_t _cffi_wchar_t; +#else +typedef uint16_t _cffi_wchar_t; /* same random pick as _cffi_backend.c */ +#endif + +_CFFI_UNUSED_FN static uint16_t _cffi_to_c_char16_t(PyObject *o) +{ + if (sizeof(_cffi_wchar_t) == 2) + return (uint16_t)_cffi_to_c_wchar_t(o); + else + return (uint16_t)_cffi_to_c_wchar3216_t(o); +} + +_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x) +{ + if (sizeof(_cffi_wchar_t) == 2) + return _cffi_from_c_wchar_t((_cffi_wchar_t)x); + else + return _cffi_from_c_wchar3216_t((int)x); +} + +_CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o) +{ + if (sizeof(_cffi_wchar_t) == 4) + return (int)_cffi_to_c_wchar_t(o); + else + return (int)_cffi_to_c_wchar3216_t(o); +} + +_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(unsigned int x) +{ + if (sizeof(_cffi_wchar_t) == 4) + return _cffi_from_c_wchar_t((_cffi_wchar_t)x); + else + return _cffi_from_c_wchar3216_t((int)x); +} + +union _cffi_union_alignment_u { + unsigned char m_char; + unsigned short m_short; + unsigned int m_int; + unsigned long m_long; + unsigned long long m_longlong; + float m_float; + double m_double; + long double m_longdouble; +}; + +struct _cffi_freeme_s { + struct _cffi_freeme_s *next; + union _cffi_union_alignment_u alignment; +}; + +_CFFI_UNUSED_FN static int +_cffi_convert_array_argument(struct _cffi_ctypedescr *ctptr, PyObject *arg, + char **output_data, Py_ssize_t datasize, + struct _cffi_freeme_s **freeme) +{ + char *p; + if (datasize < 0) + return -1; + + p = *output_data; + if (p == NULL) { + struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( + offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); + if (fp == NULL) + return -1; + fp->next = *freeme; + *freeme = fp; + p = *output_data = (char *)&fp->alignment; + } + memset((void *)p, 0, (size_t)datasize); + return _cffi_convert_array_from_object(p, ctptr, arg); +} + +_CFFI_UNUSED_FN static void +_cffi_free_array_arguments(struct _cffi_freeme_s *freeme) +{ + do { + void *p = (void *)freeme; + freeme = freeme->next; + PyObject_Free(p); + } while (freeme != NULL); +} + +/********** end CPython-specific section **********/ +#else +_CFFI_UNUSED_FN +static void (*_cffi_call_python_org)(struct _cffi_externpy_s *, char *); +# define _cffi_call_python _cffi_call_python_org +#endif + + +#define _cffi_array_len(array) (sizeof(array) / sizeof((array)[0])) + +#define _cffi_prim_int(size, sign) \ + ((size) == 1 ? ((sign) ? _CFFI_PRIM_INT8 : _CFFI_PRIM_UINT8) : \ + (size) == 2 ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) : \ + (size) == 4 ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) : \ + (size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) : \ + _CFFI__UNKNOWN_PRIM) + +#define _cffi_prim_float(size) \ + ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT : \ + (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE : \ + (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE : \ + _CFFI__UNKNOWN_FLOAT_PRIM) + +#define _cffi_check_int(got, got_nonpos, expected) \ + ((got_nonpos) == (expected <= 0) && \ + (got) == (unsigned long long)expected) + +#ifdef MS_WIN32 +# define _cffi_stdcall __stdcall +#else +# define _cffi_stdcall /* nothing */ +#endif + +#ifdef __cplusplus +} +#endif diff --git a/venv/lib/python3.12/site-packages/cffi/_embedding.h b/venv/lib/python3.12/site-packages/cffi/_embedding.h new file mode 100644 index 0000000..ba6e79b --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/_embedding.h @@ -0,0 +1,512 @@ + +/***** Support code for embedding *****/ + +#ifdef __cplusplus +extern "C" { +#endif + + +#if defined(_WIN32) +# define CFFI_DLLEXPORT __declspec(dllexport) +#elif defined(__GNUC__) +# define CFFI_DLLEXPORT __attribute__((visibility("default"))) +#else +# define CFFI_DLLEXPORT /* nothing */ +#endif + + +/* There are two global variables of type _cffi_call_python_fnptr: + + * _cffi_call_python, which we declare just below, is the one called + by ``extern "Python"`` implementations. + + * _cffi_call_python_org, which on CPython is actually part of the + _cffi_exports[] array, is the function pointer copied from + _cffi_backend. If _cffi_start_python() fails, then this is set + to NULL; otherwise, it should never be NULL. + + After initialization is complete, both are equal. However, the + first one remains equal to &_cffi_start_and_call_python until the + very end of initialization, when we are (or should be) sure that + concurrent threads also see a completely initialized world, and + only then is it changed. +*/ +#undef _cffi_call_python +typedef void (*_cffi_call_python_fnptr)(struct _cffi_externpy_s *, char *); +static void _cffi_start_and_call_python(struct _cffi_externpy_s *, char *); +static _cffi_call_python_fnptr _cffi_call_python = &_cffi_start_and_call_python; + + +#ifndef _MSC_VER + /* --- Assuming a GCC not infinitely old --- */ +# define cffi_compare_and_swap(l,o,n) __sync_bool_compare_and_swap(l,o,n) +# define cffi_write_barrier() __sync_synchronize() +# if !defined(__amd64__) && !defined(__x86_64__) && \ + !defined(__i386__) && !defined(__i386) +# define cffi_read_barrier() __sync_synchronize() +# else +# define cffi_read_barrier() (void)0 +# endif +#else + /* --- Windows threads version --- */ +# include +# define cffi_compare_and_swap(l,o,n) \ + (InterlockedCompareExchangePointer(l,n,o) == (o)) +# define cffi_write_barrier() InterlockedCompareExchange(&_cffi_dummy,0,0) +# define cffi_read_barrier() (void)0 +static volatile LONG _cffi_dummy; +#endif + +#ifdef WITH_THREAD +# ifndef _MSC_VER +# include + static pthread_mutex_t _cffi_embed_startup_lock; +# else + static CRITICAL_SECTION _cffi_embed_startup_lock; +# endif + static char _cffi_embed_startup_lock_ready = 0; +#endif + +static void _cffi_acquire_reentrant_mutex(void) +{ + static void *volatile lock = NULL; + + while (!cffi_compare_and_swap(&lock, NULL, (void *)1)) { + /* should ideally do a spin loop instruction here, but + hard to do it portably and doesn't really matter I + think: pthread_mutex_init() should be very fast, and + this is only run at start-up anyway. */ + } + +#ifdef WITH_THREAD + if (!_cffi_embed_startup_lock_ready) { +# ifndef _MSC_VER + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&_cffi_embed_startup_lock, &attr); +# else + InitializeCriticalSection(&_cffi_embed_startup_lock); +# endif + _cffi_embed_startup_lock_ready = 1; + } +#endif + + while (!cffi_compare_and_swap(&lock, (void *)1, NULL)) + ; + +#ifndef _MSC_VER + pthread_mutex_lock(&_cffi_embed_startup_lock); +#else + EnterCriticalSection(&_cffi_embed_startup_lock); +#endif +} + +static void _cffi_release_reentrant_mutex(void) +{ +#ifndef _MSC_VER + pthread_mutex_unlock(&_cffi_embed_startup_lock); +#else + LeaveCriticalSection(&_cffi_embed_startup_lock); +#endif +} + + +/********** CPython-specific section **********/ +#ifndef PYPY_VERSION + +#include "_cffi_errors.h" + + +#define _cffi_call_python_org _cffi_exports[_CFFI_CPIDX] + +PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(void); /* forward */ + +static void _cffi_py_initialize(void) +{ + /* XXX use initsigs=0, which "skips initialization registration of + signal handlers, which might be useful when Python is + embedded" according to the Python docs. But review and think + if it should be a user-controllable setting. + + XXX we should also give a way to write errors to a buffer + instead of to stderr. + + XXX if importing 'site' fails, CPython (any version) calls + exit(). Should we try to work around this behavior here? + */ + Py_InitializeEx(0); +} + +static int _cffi_initialize_python(void) +{ + /* This initializes Python, imports _cffi_backend, and then the + present .dll/.so is set up as a CPython C extension module. + */ + int result; + PyGILState_STATE state; + PyObject *pycode=NULL, *global_dict=NULL, *x; + PyObject *builtins; + + state = PyGILState_Ensure(); + + /* Call the initxxx() function from the present module. It will + create and initialize us as a CPython extension module, instead + of letting the startup Python code do it---it might reimport + the same .dll/.so and get maybe confused on some platforms. + It might also have troubles locating the .dll/.so again for all + I know. + */ + (void)_CFFI_PYTHON_STARTUP_FUNC(); + if (PyErr_Occurred()) + goto error; + + /* Now run the Python code provided to ffi.embedding_init_code(). + */ + pycode = Py_CompileString(_CFFI_PYTHON_STARTUP_CODE, + "", + Py_file_input); + if (pycode == NULL) + goto error; + global_dict = PyDict_New(); + if (global_dict == NULL) + goto error; + builtins = PyEval_GetBuiltins(); + if (builtins == NULL) + goto error; + if (PyDict_SetItemString(global_dict, "__builtins__", builtins) < 0) + goto error; + x = PyEval_EvalCode( + pycode, global_dict, global_dict); + if (x == NULL) + goto error; + Py_DECREF(x); + + /* Done! Now if we've been called from + _cffi_start_and_call_python() in an ``extern "Python"``, we can + only hope that the Python code did correctly set up the + corresponding @ffi.def_extern() function. Otherwise, the + general logic of ``extern "Python"`` functions (inside the + _cffi_backend module) will find that the reference is still + missing and print an error. + */ + result = 0; + done: + Py_XDECREF(pycode); + Py_XDECREF(global_dict); + PyGILState_Release(state); + return result; + + error:; + { + /* Print as much information as potentially useful. + Debugging load-time failures with embedding is not fun + */ + PyObject *ecap; + PyObject *exception, *v, *tb, *f, *modules, *mod; + PyErr_Fetch(&exception, &v, &tb); + ecap = _cffi_start_error_capture(); + f = PySys_GetObject((char *)"stderr"); + if (f != NULL && f != Py_None) { + PyFile_WriteString( + "Failed to initialize the Python-CFFI embedding logic:\n\n", f); + } + + if (exception != NULL) { + PyErr_NormalizeException(&exception, &v, &tb); + PyErr_Display(exception, v, tb); + } + Py_XDECREF(exception); + Py_XDECREF(v); + Py_XDECREF(tb); + + if (f != NULL && f != Py_None) { + PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME + "\ncompiled with cffi version: 2.1.1" + "\n_cffi_backend module: ", f); + modules = PyImport_GetModuleDict(); + mod = PyDict_GetItemString(modules, "_cffi_backend"); + if (mod == NULL) { + PyFile_WriteString("not loaded", f); + } + else { + v = PyObject_GetAttrString(mod, "__file__"); + PyFile_WriteObject(v, f, 0); + Py_XDECREF(v); + } + PyFile_WriteString("\nsys.path: ", f); + PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0); + PyFile_WriteString("\n\n", f); + } + _cffi_stop_error_capture(ecap); + } + result = -1; + goto done; +} + +static int _cffi_carefully_make_gil(void) +{ + /* This does the basic initialization of Python. It can be called + completely concurrently from unrelated threads. It assumes + that we don't hold the GIL before (if it exists), and we don't + hold it afterwards. + + (What it really does used to be completely different in Python 2 + and Python 3, with the Python 2 solution avoiding the spin-lock + around the Py_InitializeEx() call. However, after recent changes + to CPython 2.7 (issue #358) it no longer works. So we use the + Python 3 solution everywhere.) + + This initializes Python by calling Py_InitializeEx(). + Important: this must not be called concurrently at all. + So we use a global variable as a simple spin lock. This global + variable must be from 'libpythonX.Y.so', not from this + cffi-based extension module, because it must be shared from + different cffi-based extension modules. + + In Python < 3.8, we choose + _PyParser_TokenNames[0] as a completely arbitrary pointer value + that is never written to. The default is to point to the + string "ENDMARKER". We change it temporarily to point to the + next character in that string. (Yes, I know it's REALLY + obscure.) + + In Python >= 3.8, this string array is no longer writable, so + instead we pick PyCapsuleType.tp_version_tag. We can't change + Python < 3.8 because someone might use a mixture of cffi + embedded modules, some of which were compiled before this file + changed. + + In Python >= 3.12, this stopped working because that particular + tp_version_tag gets modified during interpreter startup. It's + arguably a bad idea before 3.12 too, but again we can't change + that because someone might use a mixture of cffi embedded + modules, and no-one reported a bug so far. In Python >= 3.12 + we go instead for PyCapsuleType.tp_as_buffer, which is supposed + to always be NULL. We write to it temporarily a pointer to + a struct full of NULLs, which is semantically the same. + */ + +#ifdef WITH_THREAD +# if PY_VERSION_HEX < 0x030C0000 + int volatile *lock = (int volatile *)&PyCapsule_Type.tp_version_tag; + int old_value, locked_value = -42; + assert(!(PyCapsule_Type.tp_flags & Py_TPFLAGS_HAVE_VERSION_TAG)); +# else + static struct ebp_s { PyBufferProcs buf; int mark; } empty_buffer_procs; + empty_buffer_procs.mark = -42; + PyBufferProcs *volatile *lock = (PyBufferProcs *volatile *) + &PyCapsule_Type.tp_as_buffer; + PyBufferProcs *old_value, *locked_value = &empty_buffer_procs.buf; +# endif + + while (1) { /* spin loop */ + old_value = *lock; + if (old_value == 0) { + if (cffi_compare_and_swap(lock, old_value, locked_value)) + break; + } + else { +# if PY_VERSION_HEX < 0x030C0000 + assert(old_value == locked_value); +# else + /* The pointer should point to a possibly different + empty_buffer_procs from another C extension module */ + assert(((struct ebp_s *)old_value)->mark == -42); +# endif + /* should ideally do a spin loop instruction here, but + hard to do it portably and doesn't really matter I + think: PyEval_InitThreads() should be very fast, and + this is only run at start-up anyway. */ + } + } +#endif + + /* call Py_InitializeEx() */ + if (!Py_IsInitialized()) { + _cffi_py_initialize(); + PyEval_SaveThread(); /* release the GIL */ + /* the returned tstate must be the one that has been stored into the + autoTLSkey by _PyGILState_Init() called from Py_Initialize(). */ + } + else { + } + +#ifdef WITH_THREAD + /* release the lock */ + while (!cffi_compare_and_swap(lock, locked_value, old_value)) + ; +#endif + + return 0; +} + +/********** end CPython-specific section **********/ + + +#else + + +/********** PyPy-specific section **********/ + +PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]); /* forward */ + +static struct _cffi_pypy_init_s { + const char *name; + void *func; /* function pointer */ + const char *code; +} _cffi_pypy_init = { + _CFFI_MODULE_NAME, + _CFFI_PYTHON_STARTUP_FUNC, + _CFFI_PYTHON_STARTUP_CODE, +}; + +extern int pypy_carefully_make_gil(const char *); +extern int pypy_init_embedded_cffi_module(int, struct _cffi_pypy_init_s *); + +static int _cffi_carefully_make_gil(void) +{ + return pypy_carefully_make_gil(_CFFI_MODULE_NAME); +} + +static int _cffi_initialize_python(void) +{ + return pypy_init_embedded_cffi_module(0xB011, &_cffi_pypy_init); +} + +/********** end PyPy-specific section **********/ + + +#endif + + +#ifdef __GNUC__ +__attribute__((noinline)) +#endif +static _cffi_call_python_fnptr _cffi_start_python(void) +{ + /* Delicate logic to initialize Python. This function can be + called multiple times concurrently, e.g. when the process calls + its first ``extern "Python"`` functions in multiple threads at + once. It can also be called recursively, in which case we must + ignore it. We also have to consider what occurs if several + different cffi-based extensions reach this code in parallel + threads---it is a different copy of the code, then, and we + can't have any shared global variable unless it comes from + 'libpythonX.Y.so'. + + Idea: + + * _cffi_carefully_make_gil(): "carefully" call + PyEval_InitThreads() (possibly with Py_InitializeEx() first). + + * then we use a (local) custom lock to make sure that a call to this + cffi-based extension will wait if another call to the *same* + extension is running the initialization in another thread. + It is reentrant, so that a recursive call will not block, but + only one from a different thread. + + * then we grab the GIL and (Python 2) we call Py_InitializeEx(). + At this point, concurrent calls to Py_InitializeEx() are not + possible: we have the GIL. + + * do the rest of the specific initialization, which may + temporarily release the GIL but not the custom lock. + Only release the custom lock when we are done. + */ + static char called = 0; + + if (_cffi_carefully_make_gil() != 0) + return NULL; + + _cffi_acquire_reentrant_mutex(); + + /* Here the GIL exists, but we don't have it. We're only protected + from concurrency by the reentrant mutex. */ + + /* This file only initializes the embedded module once, the first + time this is called, even if there are subinterpreters. */ + if (!called) { + called = 1; /* invoke _cffi_initialize_python() only once, + but don't set '_cffi_call_python' right now, + otherwise concurrent threads won't call + this function at all (we need them to wait) */ + if (_cffi_initialize_python() == 0) { + /* now initialization is finished. Switch to the fast-path. */ + + /* We would like nobody to see the new value of + '_cffi_call_python' without also seeing the rest of the + data initialized. However, this is not possible. But + the new value of '_cffi_call_python' is the function + 'cffi_call_python()' from _cffi_backend. So: */ + cffi_write_barrier(); + /* ^^^ we put a write barrier here, and a corresponding + read barrier at the start of cffi_call_python(). This + ensures that after that read barrier, we see everything + done here before the write barrier. + */ + + assert(_cffi_call_python_org != NULL); + _cffi_call_python = (_cffi_call_python_fnptr)_cffi_call_python_org; + } + else { + /* initialization failed. Reset this to NULL, even if it was + already set to some other value. Future calls to + _cffi_start_python() are still forced to occur, and will + always return NULL from now on. */ + _cffi_call_python_org = NULL; + } + } + + _cffi_release_reentrant_mutex(); + + return (_cffi_call_python_fnptr)_cffi_call_python_org; +} + +static +void _cffi_start_and_call_python(struct _cffi_externpy_s *externpy, char *args) +{ + _cffi_call_python_fnptr fnptr; + int current_err = errno; +#ifdef _MSC_VER + int current_lasterr = GetLastError(); +#endif + fnptr = _cffi_start_python(); + if (fnptr == NULL) { + fprintf(stderr, "function %s() called, but initialization code " + "failed. Returning 0.\n", externpy->name); + memset(args, 0, externpy->size_of_result); + } +#ifdef _MSC_VER + SetLastError(current_lasterr); +#endif + errno = current_err; + + if (fnptr != NULL) + fnptr(externpy, args); +} + + +/* The cffi_start_python() function makes sure Python is initialized + and our cffi module is set up. It can be called manually from the + user C code. The same effect is obtained automatically from any + dll-exported ``extern "Python"`` function. This function returns + -1 if initialization failed, 0 if all is OK. */ +_CFFI_UNUSED_FN +static int cffi_start_python(void) +{ + if (_cffi_call_python == &_cffi_start_and_call_python) { + if (_cffi_start_python() == NULL) + return -1; + } + cffi_read_barrier(); + return 0; +} + +#undef cffi_compare_and_swap +#undef cffi_write_barrier +#undef cffi_read_barrier + +#ifdef __cplusplus +} +#endif diff --git a/venv/lib/python3.12/site-packages/cffi/_imp_emulation.py b/venv/lib/python3.12/site-packages/cffi/_imp_emulation.py new file mode 100644 index 0000000..136abdd --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/_imp_emulation.py @@ -0,0 +1,83 @@ + +try: + # this works on Python < 3.12 + from imp import * + +except ImportError: + # this is a limited emulation for Python >= 3.12. + # Note that this is used only for tests or for the old ffi.verify(). + # This is copied from the source code of Python 3.11. + + from _imp import (acquire_lock, release_lock, + is_builtin, is_frozen) + + from importlib._bootstrap import _load + + from importlib import machinery + import os + import sys + import tokenize + + SEARCH_ERROR = 0 + PY_SOURCE = 1 + PY_COMPILED = 2 + C_EXTENSION = 3 + PY_RESOURCE = 4 + PKG_DIRECTORY = 5 + C_BUILTIN = 6 + PY_FROZEN = 7 + PY_CODERESOURCE = 8 + IMP_HOOK = 9 + + def get_suffixes(): + extensions = [(s, 'rb', C_EXTENSION) + for s in machinery.EXTENSION_SUFFIXES] + source = [(s, 'r', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES] + bytecode = [(s, 'rb', PY_COMPILED) for s in machinery.BYTECODE_SUFFIXES] + return extensions + source + bytecode + + def find_module(name, path=None): + if not isinstance(name, str): + raise TypeError("'name' must be a str, not {}".format(type(name))) + elif not isinstance(path, (type(None), list)): + # Backwards-compatibility + raise RuntimeError("'path' must be None or a list, " + "not {}".format(type(path))) + + if path is None: + if is_builtin(name): + return None, None, ('', '', C_BUILTIN) + elif is_frozen(name): + return None, None, ('', '', PY_FROZEN) + else: + path = sys.path + + for entry in path: + package_directory = os.path.join(entry, name) + for suffix in ['.py', machinery.BYTECODE_SUFFIXES[0]]: + package_file_name = '__init__' + suffix + file_path = os.path.join(package_directory, package_file_name) + if os.path.isfile(file_path): + return None, package_directory, ('', '', PKG_DIRECTORY) + for suffix, mode, type_ in get_suffixes(): + file_name = name + suffix + file_path = os.path.join(entry, file_name) + if os.path.isfile(file_path): + break + else: + continue + break # Break out of outer loop when breaking out of inner loop. + else: + raise ImportError(name, name=name) + + encoding = None + if 'b' not in mode: + with open(file_path, 'rb') as file: + encoding = tokenize.detect_encoding(file.readline)[0] + file = open(file_path, mode, encoding=encoding) + return file, file_path, (suffix, mode, type_) + + def load_dynamic(name, path, file=None): + loader = machinery.ExtensionFileLoader(name, path) + spec = machinery.ModuleSpec(name=name, loader=loader, origin=path) + return _load(spec) diff --git a/venv/lib/python3.12/site-packages/cffi/_shimmed_dist_utils.py b/venv/lib/python3.12/site-packages/cffi/_shimmed_dist_utils.py new file mode 100644 index 0000000..c3d2312 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/_shimmed_dist_utils.py @@ -0,0 +1,45 @@ +""" +Temporary shim module to indirect the bits of distutils we need from setuptools/distutils while providing useful +error messages beyond `No module named 'distutils' on Python >= 3.12, or when setuptools' vendored distutils is broken. + +This is a compromise to avoid a hard-dep on setuptools for Python >= 3.12, since many users don't need runtime compilation support from CFFI. +""" +import sys + +try: + # import setuptools first; this is the most robust way to ensure its embedded distutils is available + # (the .pth shim should usually work, but this is even more robust) + import setuptools +except Exception as ex: + if sys.version_info >= (3, 12): + # Python 3.12 has no built-in distutils to fall back on, so any import problem is fatal + raise Exception("This CFFI feature requires setuptools on Python >= 3.12. The setuptools module is missing or non-functional.") from ex + + # silently ignore on older Pythons (support fallback to stdlib distutils where available) +else: + del setuptools + +try: + # bring in just the bits of distutils we need, whether they really came from setuptools or stdlib-embedded distutils + from distutils import log, sysconfig + from distutils.ccompiler import CCompiler + from distutils.command.build_ext import build_ext + from distutils.core import Distribution, Extension + from distutils.dir_util import mkpath + from distutils.errors import DistutilsSetupError, CompileError, LinkError + from distutils.log import set_threshold, set_verbosity + + if sys.platform == 'win32': + try: + # FUTURE: msvc9compiler module was removed in setuptools 74; consider removing, as it's only used by an ancient patch in `recompiler` + from distutils.msvc9compiler import MSVCCompiler + except ImportError: + MSVCCompiler = None +except Exception as ex: + if sys.version_info >= (3, 12): + raise Exception("This CFFI feature requires setuptools on Python >= 3.12. Please install the setuptools package.") from ex + + # anything older, just let the underlying distutils import error fly + raise Exception("This CFFI feature requires distutils. Please install the distutils or setuptools package.") from ex + +del sys diff --git a/venv/lib/python3.12/site-packages/cffi/api.py b/venv/lib/python3.12/site-packages/cffi/api.py new file mode 100644 index 0000000..ec8ee7d --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/api.py @@ -0,0 +1,960 @@ +import sys, types +from .lock import allocate_lock +from .error import CDefError +from . import model + +try: + basestring +except NameError: + # Python 3.x + basestring = str + +_unspecified = object() + + + +class FFI: + r''' + The main top-level class that you instantiate once, or once per module. + + Example usage: + + ffi = FFI() + ffi.cdef(""" + int printf(const char *, ...); + """) + + C = ffi.dlopen(None) # standard library + -or- + C = ffi.verify() # use a C compiler: verify the decl above is right + + C.printf("hello, %s!\n", ffi.new("char[]", "world")) + ''' + + def __init__(self, backend=None): + """Create an FFI instance. The 'backend' argument is used to + select a non-default backend, mostly for tests. + """ + if backend is None: + # You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with + # _cffi_backend.so compiled. + import _cffi_backend as backend + from . import __version__ + if backend.__version__ != __version__: + # bad version! Try to be as explicit as possible. + if hasattr(backend, '__file__'): + # CPython + raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. When we import the top-level '_cffi_backend' extension module, we get version %s, located in %r. The two versions should be equal; check your installation." % ( + __version__, __file__, + backend.__version__, backend.__file__)) + else: + # PyPy + raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. This interpreter comes with a built-in '_cffi_backend' module, which is version %s. The two versions should be equal; check your installation." % ( + __version__, __file__, backend.__version__)) + # (If you insist you can also try to pass the option + # 'backend=backend_ctypes.CTypesBackend()', but don't + # rely on it! It's probably not going to work well.) + + from . import cparser + self._backend = backend + self._lock = allocate_lock() + self._parser = cparser.Parser() + self._cached_btypes = {} + self._parsed_types = types.ModuleType('parsed_types').__dict__ + self._new_types = types.ModuleType('new_types').__dict__ + self._function_caches = [] + self._libraries = [] + self._cdefsources = [] + self._included_ffis = [] + self._windows_unicode = None + self._init_once_cache = {} + self._cdef_version = None + self._embedding = None + self._typecache = model.get_typecache(backend) + if hasattr(backend, 'set_ffi'): + backend.set_ffi(self) + for name in list(backend.__dict__): + if name.startswith('RTLD_'): + setattr(self, name, getattr(backend, name)) + # + with self._lock: + self.BVoidP = self._get_cached_btype(model.voidp_type) + self.BCharA = self._get_cached_btype(model.char_array_type) + if isinstance(backend, types.ModuleType): + # _cffi_backend: attach these constants to the class + if not hasattr(FFI, 'NULL'): + FFI.NULL = self.cast(self.BVoidP, 0) + FFI.CData, FFI.CType = backend._get_types() + else: + # ctypes backend: attach these constants to the instance + self.NULL = self.cast(self.BVoidP, 0) + self.CData, self.CType = backend._get_types() + self.buffer = backend.buffer + + def cdef(self, csource, override=False, packed=False, pack=None): + """Parse the given C source. This registers all declared functions, + types, and global variables. The functions and global variables can + then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'. + The types can be used in 'ffi.new()' and other functions. + If 'packed' is specified as True, all structs declared inside this + cdef are packed, i.e. laid out without any field alignment at all. + Alternatively, 'pack' can be a small integer, and requests for + alignment greater than that are ignored (pack=1 is equivalent to + packed=True). + """ + self._cdef(csource, override=override, packed=packed, pack=pack) + + def embedding_api(self, csource, packed=False, pack=None): + self._cdef(csource, packed=packed, pack=pack, dllexport=True) + if self._embedding is None: + self._embedding = '' + + def _cdef(self, csource, override=False, **options): + if not isinstance(csource, str): # unicode, on Python 2 + if not isinstance(csource, basestring): + raise TypeError("cdef() argument must be a string") + csource = csource.encode('ascii') + with self._lock: + self._cdef_version = object() + self._parser.parse(csource, override=override, **options) + self._cdefsources.append(csource) + if override: + for cache in self._function_caches: + cache.clear() + finishlist = self._parser._recomplete + if finishlist: + self._parser._recomplete = [] + for tp in finishlist: + tp.finish_backend_type(self, finishlist) + + def dlopen(self, name, flags=0): + """Load and return a dynamic library identified by 'name'. + The standard C library can be loaded by passing None. + Note that functions and types declared by 'ffi.cdef()' are not + linked to a particular library, just like C headers; in the + library we only look for the actual (untyped) symbols. + """ + if not (isinstance(name, basestring) or + name is None or + isinstance(name, self.CData)): + raise TypeError("dlopen(name): name must be a file name, None, " + "or an already-opened 'void *' handle") + with self._lock: + lib, function_cache = _make_ffi_library(self, name, flags) + self._function_caches.append(function_cache) + self._libraries.append(lib) + return lib + + def dlclose(self, lib): + """Close a library obtained with ffi.dlopen(). After this call, + access to functions or variables from the library will fail + (possibly with a segmentation fault). + """ + type(lib).__cffi_close__(lib) + + def _typeof_locked(self, cdecl): + # call me with the lock! + key = cdecl + if key in self._parsed_types: + return self._parsed_types[key] + # + if not isinstance(cdecl, str): # unicode, on Python 2 + cdecl = cdecl.encode('ascii') + # + type = self._parser.parse_type(cdecl) + really_a_function_type = type.is_raw_function + if really_a_function_type: + type = type.as_function_pointer() + btype = self._get_cached_btype(type) + result = btype, really_a_function_type + self._parsed_types[key] = result + return result + + def _typeof(self, cdecl, consider_function_as_funcptr=False): + # string -> ctype object + try: + result = self._parsed_types[cdecl] + except KeyError: + with self._lock: + result = self._typeof_locked(cdecl) + # + btype, really_a_function_type = result + if really_a_function_type and not consider_function_as_funcptr: + raise CDefError("the type %r is a function type, not a " + "pointer-to-function type" % (cdecl,)) + return btype + + def typeof(self, cdecl): + """Parse the C type given as a string and return the + corresponding object. + It can also be used on 'cdata' instance to get its C type. + """ + if isinstance(cdecl, basestring): + return self._typeof(cdecl) + if isinstance(cdecl, self.CData): + return self._backend.typeof(cdecl) + if isinstance(cdecl, types.BuiltinFunctionType): + res = _builtin_function_type(cdecl) + if res is not None: + return res + if (isinstance(cdecl, types.FunctionType) + and hasattr(cdecl, '_cffi_base_type')): + with self._lock: + return self._get_cached_btype(cdecl._cffi_base_type) + raise TypeError(type(cdecl)) + + def sizeof(self, cdecl): + """Return the size in bytes of the argument. It can be a + string naming a C type, or a 'cdata' instance. + """ + if isinstance(cdecl, basestring): + BType = self._typeof(cdecl) + return self._backend.sizeof(BType) + else: + return self._backend.sizeof(cdecl) + + def alignof(self, cdecl): + """Return the natural alignment size in bytes of the C type + given as a string. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._backend.alignof(cdecl) + + def offsetof(self, cdecl, *fields_or_indexes): + """Return the offset of the named field inside the given + structure or array, which must be given as a C type name. + You can give several field names in case of nested structures. + You can also give numeric values which correspond to array + items, in case of an array type. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._typeoffsetof(cdecl, *fields_or_indexes)[1] + + def new(self, cdecl, init=None): + """Allocate an instance according to the specified C type and + return a pointer to it. The specified C type must be either a + pointer or an array: ``new('X *')`` allocates an X and returns + a pointer to it, whereas ``new('X[n]')`` allocates an array of + n X'es and returns an array referencing it (which works + mostly like a pointer, like in C). You can also use + ``new('X[]', n)`` to allocate an array of a non-constant + length n. + + The memory is initialized following the rules of declaring a + global variable in C: by default it is zero-initialized, but + an explicit initializer can be given which can be used to + fill all or part of the memory. + + When the returned object goes out of scope, the memory + is freed. In other words the returned object has + ownership of the value of type 'cdecl' that it points to. This + means that the raw data can be used as long as this object is + kept alive, but must not be used for a longer time. Be careful + about that when copying the pointer to the memory somewhere + else, e.g. into another structure. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._backend.newp(cdecl, init) + + def new_allocator(self, alloc=None, free=None, + should_clear_after_alloc=True): + """Return a new allocator, i.e. a function that behaves like ffi.new() + but uses the provided low-level 'alloc' and 'free' functions. + + 'alloc' is called with the size as argument. If it returns NULL, a + MemoryError is raised. 'free' is called with the result of 'alloc' + as argument. Both can be either Python function or directly C + functions. If 'free' is None, then no free function is called. + If both 'alloc' and 'free' are None, the default is used. + + If 'should_clear_after_alloc' is set to False, then the memory + returned by 'alloc' is assumed to be already cleared (or you are + fine with garbage); otherwise CFFI will clear it. + """ + compiled_ffi = self._backend.FFI() + allocator = compiled_ffi.new_allocator(alloc, free, + should_clear_after_alloc) + def allocate(cdecl, init=None): + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return allocator(cdecl, init) + return allocate + + def cast(self, cdecl, source): + """Similar to a C cast: returns an instance of the named C + type initialized with the given 'source'. The source is + casted between integers or pointers of any type. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._backend.cast(cdecl, source) + + def string(self, cdata, maxlen=-1): + """Return a Python string (or unicode string) from the 'cdata'. + If 'cdata' is a pointer or array of characters or bytes, returns + the null-terminated string. The returned string extends until + the first null character, or at most 'maxlen' characters. If + 'cdata' is an array then 'maxlen' defaults to its length. + + If 'cdata' is a pointer or array of wchar_t, returns a unicode + string following the same rules. + + If 'cdata' is a single character or byte or a wchar_t, returns + it as a string or unicode string. + + If 'cdata' is an enum, returns the value of the enumerator as a + string, or 'NUMBER' if the value is out of range. + """ + return self._backend.string(cdata, maxlen) + + def unpack(self, cdata, length): + """Unpack an array of C data of the given length, + returning a Python string/unicode/list. + + If 'cdata' is a pointer to 'char', returns a byte string. + It does not stop at the first null. This is equivalent to: + ffi.buffer(cdata, length)[:] + + If 'cdata' is a pointer to 'wchar_t', returns a unicode string. + 'length' is measured in wchar_t's; it is not the size in bytes. + + If 'cdata' is a pointer to anything else, returns a list of + 'length' items. This is a faster equivalent to: + [cdata[i] for i in range(length)] + """ + return self._backend.unpack(cdata, length) + + #def buffer(self, cdata, size=-1): + # """Return a read-write buffer object that references the raw C data + # pointed to by the given 'cdata'. The 'cdata' must be a pointer or + # an array. Can be passed to functions expecting a buffer, or directly + # manipulated with: + # + # buf[:] get a copy of it in a regular string, or + # buf[idx] as a single character + # buf[:] = ... + # buf[idx] = ... change the content + # """ + # note that 'buffer' is a type, set on this instance by __init__ + + def from_buffer(self, cdecl, python_buffer=_unspecified, + require_writable=False): + """Return a cdata of the given type pointing to the data of the + given Python object, which must support the buffer interface. + Note that this is not meant to be used on the built-in types + str or unicode (you can build 'char[]' arrays explicitly) + but only on objects containing large quantities of raw data + in some other format, like 'array.array' or numpy arrays. + + The first argument is optional and default to 'char[]'. + """ + if python_buffer is _unspecified: + cdecl, python_buffer = self.BCharA, cdecl + elif isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._backend.from_buffer(cdecl, python_buffer, + require_writable) + + def memmove(self, dest, src, n): + """ffi.memmove(dest, src, n) copies n bytes of memory from src to dest. + + Like the C function memmove(), the memory areas may overlap; + apart from that it behaves like the C function memcpy(). + + 'src' can be any cdata ptr or array, or any Python buffer object. + 'dest' can be any cdata ptr or array, or a writable Python buffer + object. The size to copy, 'n', is always measured in bytes. + + Unlike other methods, this one supports all Python buffer including + byte strings and bytearrays---but it still does not support + non-contiguous buffers. + """ + return self._backend.memmove(dest, src, n) + + def callback(self, cdecl, python_callable=None, error=None, onerror=None): + """Return a callback object or a decorator making such a + callback object. 'cdecl' must name a C function pointer type. + The callback invokes the specified 'python_callable' (which may + be provided either directly or via a decorator). Important: the + callback object must be manually kept alive for as long as the + callback may be invoked from the C level. + """ + def callback_decorator_wrap(python_callable): + if not callable(python_callable): + raise TypeError("the 'python_callable' argument " + "is not callable") + return self._backend.callback(cdecl, python_callable, + error, onerror) + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl, consider_function_as_funcptr=True) + if python_callable is None: + return callback_decorator_wrap # decorator mode + else: + return callback_decorator_wrap(python_callable) # direct mode + + def getctype(self, cdecl, replace_with=''): + """Return a string giving the C type 'cdecl', which may be itself + a string or a object. If 'replace_with' is given, it gives + extra text to append (or insert for more complicated C types), like + a variable name, or '*' to get actually the C type 'pointer-to-cdecl'. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + replace_with = replace_with.strip() + if (replace_with.startswith('*') + and '&[' in self._backend.getcname(cdecl, '&')): + replace_with = '(%s)' % replace_with + elif replace_with and replace_with[0] not in '[(': + replace_with = ' ' + replace_with + return self._backend.getcname(cdecl, replace_with) + + def gc(self, cdata, destructor, size=0): + """Return a new cdata object that points to the same + data. Later, when this new cdata object is garbage-collected, + 'destructor(old_cdata_object)' will be called. + + The optional 'size' gives an estimate of the size, used to + trigger the garbage collection more eagerly. So far only used + on PyPy. It tells the GC that the returned object keeps alive + roughly 'size' bytes of external memory. + """ + return self._backend.gcp(cdata, destructor, size) + + def _get_cached_btype(self, type): + assert self._lock.acquire(False) is False + # call me with the lock! + try: + BType = self._cached_btypes[type] + except KeyError: + finishlist = [] + BType = type.get_cached_btype(self, finishlist) + for type in finishlist: + type.finish_backend_type(self, finishlist) + return BType + + def verify(self, source='', tmpdir=None, **kwargs): + """Verify that the current ffi signatures compile on this + machine, and return a dynamic library object. The dynamic + library can be used to call functions and access global + variables declared in this 'ffi'. The library is compiled + by the C compiler: it gives you C-level API compatibility + (including calling macros). This is unlike 'ffi.dlopen()', + which requires binary compatibility in the signatures. + """ + from .verifier import Verifier, _caller_dir_pycache + # + # If set_unicode(True) was called, insert the UNICODE and + # _UNICODE macro declarations + if self._windows_unicode: + self._apply_windows_unicode(kwargs) + # + # Set the tmpdir here, and not in Verifier.__init__: it picks + # up the caller's directory, which we want to be the caller of + # ffi.verify(), as opposed to the caller of Veritier(). + tmpdir = tmpdir or _caller_dir_pycache() + # + # Make a Verifier() and use it to load the library. + self.verifier = Verifier(self, source, tmpdir, **kwargs) + lib = self.verifier.load_library() + # + # Save the loaded library for keep-alive purposes, even + # if the caller doesn't keep it alive itself (it should). + self._libraries.append(lib) + return lib + + def _get_errno(self): + return self._backend.get_errno() + def _set_errno(self, errno): + self._backend.set_errno(errno) + errno = property(_get_errno, _set_errno, None, + "the value of 'errno' from/to the C calls") + + def getwinerror(self, code=-1): + return self._backend.getwinerror(code) + + def _pointer_to(self, ctype): + with self._lock: + return model.pointer_cache(self, ctype) + + def addressof(self, cdata, *fields_or_indexes): + """Return the address of a . + If 'fields_or_indexes' are given, returns the address of that + field or array item in the structure or array, recursively in + case of nested structures. + """ + try: + ctype = self._backend.typeof(cdata) + except TypeError: + if '__addressof__' in type(cdata).__dict__: + return type(cdata).__addressof__(cdata, *fields_or_indexes) + raise + if fields_or_indexes: + ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes) + else: + if ctype.kind == "pointer": + raise TypeError("addressof(pointer)") + offset = 0 + ctypeptr = self._pointer_to(ctype) + return self._backend.rawaddressof(ctypeptr, cdata, offset) + + def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes): + ctype, offset = self._backend.typeoffsetof(ctype, field_or_index) + for field1 in fields_or_indexes: + ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1) + offset += offset1 + return ctype, offset + + def include(self, ffi_to_include): + """Includes the typedefs, structs, unions and enums defined + in another FFI instance. Usage is similar to a #include in C, + where a part of the program might include types defined in + another part for its own usage. Note that the include() + method has no effect on functions, constants and global + variables, which must anyway be accessed directly from the + lib object returned by the original FFI instance. + """ + if not isinstance(ffi_to_include, FFI): + raise TypeError("ffi.include() expects an argument that is also of" + " type cffi.FFI, not %r" % ( + type(ffi_to_include).__name__,)) + if ffi_to_include is self: + raise ValueError("self.include(self)") + with ffi_to_include._lock: + with self._lock: + self._parser.include(ffi_to_include._parser) + self._cdefsources.append('[') + self._cdefsources.extend(ffi_to_include._cdefsources) + self._cdefsources.append(']') + self._included_ffis.append(ffi_to_include) + + def new_handle(self, x): + return self._backend.newp_handle(self.BVoidP, x) + + def from_handle(self, x): + return self._backend.from_handle(x) + + def release(self, x): + self._backend.release(x) + + def set_unicode(self, enabled_flag): + """Windows: if 'enabled_flag' is True, enable the UNICODE and + _UNICODE defines in C, and declare the types like TCHAR and LPTCSTR + to be (pointers to) wchar_t. If 'enabled_flag' is False, + declare these types to be (pointers to) plain 8-bit characters. + This is mostly for backward compatibility; you usually want True. + """ + if self._windows_unicode is not None: + raise ValueError("set_unicode() can only be called once") + enabled_flag = bool(enabled_flag) + if enabled_flag: + self.cdef("typedef wchar_t TBYTE;" + "typedef wchar_t TCHAR;" + "typedef const wchar_t *LPCTSTR;" + "typedef const wchar_t *PCTSTR;" + "typedef wchar_t *LPTSTR;" + "typedef wchar_t *PTSTR;" + "typedef TBYTE *PTBYTE;" + "typedef TCHAR *PTCHAR;") + else: + self.cdef("typedef char TBYTE;" + "typedef char TCHAR;" + "typedef const char *LPCTSTR;" + "typedef const char *PCTSTR;" + "typedef char *LPTSTR;" + "typedef char *PTSTR;" + "typedef TBYTE *PTBYTE;" + "typedef TCHAR *PTCHAR;") + self._windows_unicode = enabled_flag + + def _apply_windows_unicode(self, kwds): + defmacros = kwds.get('define_macros', ()) + if not isinstance(defmacros, (list, tuple)): + raise TypeError("'define_macros' must be a list or tuple") + defmacros = list(defmacros) + [('UNICODE', '1'), + ('_UNICODE', '1')] + kwds['define_macros'] = defmacros + + def _apply_embedding_fix(self, kwds): + # must include an argument like "-lpython2.7" for the compiler + def ensure(key, value): + lst = kwds.setdefault(key, []) + if value not in lst: + lst.append(value) + # + if '__pypy__' in sys.builtin_module_names: + import os + if sys.platform == "win32": + # we need 'libpypy-c.lib'. Current distributions of + # pypy (>= 4.1) contain it as 'libs/python27.lib'. + pythonlib = "python{0[0]}{0[1]}".format(sys.version_info) + if hasattr(sys, 'prefix'): + ensure('library_dirs', os.path.join(sys.prefix, 'libs')) + else: + # we need 'libpypy-c.{so,dylib}', which should be by + # default located in 'sys.prefix/bin' for installed + # systems. + if sys.version_info < (3,): + pythonlib = "pypy-c" + else: + pythonlib = "pypy3-c" + if hasattr(sys, 'prefix'): + ensure('library_dirs', os.path.join(sys.prefix, 'bin')) + # On uninstalled pypy's, the libpypy-c is typically found in + # .../pypy/goal/. + if hasattr(sys, 'prefix'): + ensure('library_dirs', os.path.join(sys.prefix, 'pypy', 'goal')) + else: + if sys.platform == "win32": + template = "python%d%d" + if hasattr(sys, 'gettotalrefcount'): + template += '_d' + else: + try: + import sysconfig + except ImportError: # 2.6 + from cffi._shimmed_dist_utils import sysconfig + template = "python%d.%d" + if sysconfig.get_config_var('DEBUG_EXT'): + template += sysconfig.get_config_var('DEBUG_EXT') + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + if hasattr(sys, 'abiflags'): + pythonlib += sys.abiflags + ensure('libraries', pythonlib) + if sys.platform == "win32": + ensure('extra_link_args', '/MANIFEST') + + def set_source(self, module_name, source, source_extension='.c', **kwds): + import os + if hasattr(self, '_assigned_source'): + raise ValueError("set_source() cannot be called several times " + "per ffi object") + if not isinstance(module_name, basestring): + raise TypeError("'module_name' must be a string") + if os.sep in module_name or (os.altsep and os.altsep in module_name): + raise ValueError("'module_name' must not contain '/': use a dotted " + "name to make a 'package.module' location") + self._assigned_source = (str(module_name), source, + source_extension, kwds) + + def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, + source_extension='.c', **kwds): + from . import pkgconfig + if not isinstance(pkgconfig_libs, list): + raise TypeError("the pkgconfig_libs argument must be a list " + "of package names") + kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) + pkgconfig.merge_flags(kwds, kwds2) + self.set_source(module_name, source, source_extension, **kwds) + + def distutils_extension(self, tmpdir='build', verbose=True): + from cffi._shimmed_dist_utils import mkpath + from .recompiler import recompile + # + if not hasattr(self, '_assigned_source'): + if hasattr(self, 'verifier'): # fallback, 'tmpdir' ignored + return self.verifier.get_extension() + raise ValueError("set_source() must be called before" + " distutils_extension()") + module_name, source, source_extension, kwds = self._assigned_source + if source is None: + raise TypeError("distutils_extension() is only for C extension " + "modules, not for dlopen()-style pure Python " + "modules") + mkpath(tmpdir) + ext, updated = recompile(self, module_name, + source, tmpdir=tmpdir, extradir=tmpdir, + source_extension=source_extension, + call_c_compiler=False, **kwds) + if verbose: + if updated: + sys.stderr.write("regenerated: %r\n" % (ext.sources[0],)) + else: + sys.stderr.write("not modified: %r\n" % (ext.sources[0],)) + return ext + + def emit_c_code(self, filename): + from .recompiler import recompile + # + if not hasattr(self, '_assigned_source'): + raise ValueError("set_source() must be called before emit_c_code()") + module_name, source, source_extension, kwds = self._assigned_source + if source is None: + raise TypeError("emit_c_code() is only for C extension modules, " + "not for dlopen()-style pure Python modules") + recompile(self, module_name, source, + c_file=filename, call_c_compiler=False, + uses_ffiplatform=False, **kwds) + + def emit_python_code(self, filename): + from .recompiler import recompile + # + if not hasattr(self, '_assigned_source'): + raise ValueError("set_source() must be called before emit_c_code()") + module_name, source, source_extension, kwds = self._assigned_source + if source is not None: + raise TypeError("emit_python_code() is only for dlopen()-style " + "pure Python modules, not for C extension modules") + recompile(self, module_name, source, + c_file=filename, call_c_compiler=False, + uses_ffiplatform=False, **kwds) + + def compile(self, tmpdir='.', verbose=0, target=None, debug=None): + """The 'target' argument gives the final file name of the + compiled DLL. Use '*' to force distutils' choice, suitable for + regular CPython C API modules. Use a file name ending in '.*' + to ask for the system's default extension for dynamic libraries + (.so/.dll/.dylib). + + The default is '*' when building a non-embedded C API extension, + and (module_name + '.*') when building an embedded library. + """ + from .recompiler import recompile + # + if not hasattr(self, '_assigned_source'): + raise ValueError("set_source() must be called before compile()") + module_name, source, source_extension, kwds = self._assigned_source + return recompile(self, module_name, source, tmpdir=tmpdir, + target=target, source_extension=source_extension, + compiler_verbose=verbose, debug=debug, **kwds) + + def init_once(self, func, tag): + # Read _init_once_cache[tag], which is either (False, lock) if + # we're calling the function now in some thread, or (True, result). + # Don't call setdefault() in most cases, to avoid allocating and + # immediately freeing a lock; but still use setdefaut() to avoid + # races. + try: + x = self._init_once_cache[tag] + except KeyError: + x = self._init_once_cache.setdefault(tag, (False, allocate_lock())) + # Common case: we got (True, result), so we return the result. + if x[0]: + return x[1] + # Else, it's a lock. Acquire it to serialize the following tests. + with x[1]: + # Read again from _init_once_cache the current status. + x = self._init_once_cache[tag] + if x[0]: + return x[1] + # Call the function and store the result back. + result = func() + self._init_once_cache[tag] = (True, result) + return result + + def embedding_init_code(self, pysource): + if self._embedding: + raise ValueError("embedding_init_code() can only be called once") + # fix 'pysource' before it gets dumped into the C file: + # - remove empty lines at the beginning, so it starts at "line 1" + # - dedent, if all non-empty lines are indented + # - check for SyntaxErrors + import re + match = re.match(r'\s*\n', pysource) + if match: + pysource = pysource[match.end():] + lines = pysource.splitlines() or [''] + prefix = re.match(r'\s*', lines[0]).group() + for i in range(1, len(lines)): + line = lines[i] + if line.rstrip(): + while not line.startswith(prefix): + prefix = prefix[:-1] + i = len(prefix) + lines = [line[i:]+'\n' for line in lines] + pysource = ''.join(lines) + # + compile(pysource, "cffi_init", "exec") + # + self._embedding = pysource + + def def_extern(self, *args, **kwds): + raise ValueError("ffi.def_extern() is only available on API-mode FFI " + "objects") + + def list_types(self): + """Returns the user type names known to this FFI instance. + This returns a tuple containing three lists of names: + (typedef_names, names_of_structs, names_of_unions) + """ + typedefs = [] + structs = [] + unions = [] + for key in self._parser._declarations: + if key.startswith('typedef '): + typedefs.append(key[8:]) + elif key.startswith('struct '): + structs.append(key[7:]) + elif key.startswith('union '): + unions.append(key[6:]) + typedefs.sort() + structs.sort() + unions.sort() + return (typedefs, structs, unions) + + +def _load_backend_lib(backend, name, flags): + import os + if not isinstance(name, basestring): + if sys.platform != "win32" or name is not None: + return backend.load_library(name, flags) + name = "c" # Windows: load_library(None) fails, but this works + # on Python 2 (backward compatibility hack only) + first_error = None + if '.' in name or '/' in name or os.sep in name: + try: + return backend.load_library(name, flags) + except OSError as e: + first_error = e + import ctypes.util + path = ctypes.util.find_library(name) + if path is None: + if name == "c" and sys.platform == "win32" and sys.version_info >= (3,): + raise OSError("dlopen(None) cannot work on Windows for Python 3 " + "(see http://bugs.python.org/issue23606)") + msg = ("ctypes.util.find_library() did not manage " + "to locate a library called %r" % (name,)) + if first_error is not None: + msg = "%s. Additionally, %s" % (first_error, msg) + raise OSError(msg) + return backend.load_library(path, flags) + +def _make_ffi_library(ffi, libname, flags): + backend = ffi._backend + backendlib = _load_backend_lib(backend, libname, flags) + # + def accessor_function(name): + key = 'function ' + name + tp, _ = ffi._parser._declarations[key] + BType = ffi._get_cached_btype(tp) + value = backendlib.load_function(BType, name) + library.__dict__[name] = value + # + def accessor_variable(name): + key = 'variable ' + name + tp, _ = ffi._parser._declarations[key] + BType = ffi._get_cached_btype(tp) + read_variable = backendlib.read_variable + write_variable = backendlib.write_variable + setattr(FFILibrary, name, property( + lambda self: read_variable(BType, name), + lambda self, value: write_variable(BType, name, value))) + # + def addressof_var(name): + try: + return addr_variables[name] + except KeyError: + with ffi._lock: + if name not in addr_variables: + key = 'variable ' + name + tp, _ = ffi._parser._declarations[key] + BType = ffi._get_cached_btype(tp) + if BType.kind != 'array': + BType = model.pointer_cache(ffi, BType) + p = backendlib.load_function(BType, name) + addr_variables[name] = p + return addr_variables[name] + # + def accessor_constant(name): + raise NotImplementedError("non-integer constant '%s' cannot be " + "accessed from a dlopen() library" % (name,)) + # + def accessor_int_constant(name): + library.__dict__[name] = ffi._parser._int_constants[name] + # + accessors = {} + accessors_version = [False] + addr_variables = {} + # + def update_accessors(): + if accessors_version[0] is ffi._cdef_version: + return + # + for key, (tp, _) in ffi._parser._declarations.items(): + if not isinstance(tp, model.EnumType): + tag, name = key.split(' ', 1) + if tag == 'function': + accessors[name] = accessor_function + elif tag == 'variable': + accessors[name] = accessor_variable + elif tag == 'constant': + accessors[name] = accessor_constant + else: + for i, enumname in enumerate(tp.enumerators): + def accessor_enum(name, tp=tp, i=i): + tp.check_not_partial() + library.__dict__[name] = tp.enumvalues[i] + accessors[enumname] = accessor_enum + for name in ffi._parser._int_constants: + accessors.setdefault(name, accessor_int_constant) + accessors_version[0] = ffi._cdef_version + # + def make_accessor(name): + with ffi._lock: + if name in library.__dict__ or name in FFILibrary.__dict__: + return # added by another thread while waiting for the lock + if name not in accessors: + update_accessors() + if name not in accessors: + raise AttributeError(name) + accessors[name](name) + # + class FFILibrary: + def __getattr__(self, name): + make_accessor(name) + return getattr(self, name) + def __setattr__(self, name, value): + try: + property = getattr(self.__class__, name) + except AttributeError: + make_accessor(name) + setattr(self, name, value) + else: + property.__set__(self, value) + def __dir__(self): + with ffi._lock: + update_accessors() + return accessors.keys() + def __addressof__(self, name): + if name in library.__dict__: + return library.__dict__[name] + if name in FFILibrary.__dict__: + return addressof_var(name) + make_accessor(name) + if name in library.__dict__: + return library.__dict__[name] + if name in FFILibrary.__dict__: + return addressof_var(name) + raise AttributeError("cffi library has no function or " + "global variable named '%s'" % (name,)) + def __cffi_close__(self): + backendlib.close_lib() + self.__dict__.clear() + # + if isinstance(libname, basestring): + try: + if not isinstance(libname, str): # unicode, on Python 2 + libname = libname.encode('utf-8') + FFILibrary.__name__ = 'FFILibrary_%s' % libname + except UnicodeError: + pass + library = FFILibrary() + return library, library.__dict__ + +def _builtin_function_type(func): + # a hack to make at least ffi.typeof(builtin_function) work, + # if the builtin function was obtained by 'vengine_cpy'. + import sys + try: + module = sys.modules[func.__module__] + ffi = module._cffi_original_ffi + types_of_builtin_funcs = module._cffi_types_of_builtin_funcs + tp = types_of_builtin_funcs[func] + except (KeyError, AttributeError, TypeError): + return None + else: + with ffi._lock: + return ffi._get_cached_btype(tp) diff --git a/venv/lib/python3.12/site-packages/cffi/backend_ctypes.py b/venv/lib/python3.12/site-packages/cffi/backend_ctypes.py new file mode 100644 index 0000000..12cd162 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/backend_ctypes.py @@ -0,0 +1,1120 @@ +import ctypes, ctypes.util, operator, sys +from . import model + +if sys.version_info < (3,): + bytechr = chr +else: + unicode = str + long = int + xrange = range + bytechr = lambda num: bytes([num]) + +class CTypesType(type): + pass + +class CTypesData: + __metaclass__ = CTypesType + __slots__ = ['__weakref__'] + __name__ = '' + + def __init__(self, *args): + raise TypeError("cannot instantiate %r" % (self.__class__,)) + + @classmethod + def _newp(cls, init): + raise TypeError("expected a pointer or array ctype, got '%s'" + % (cls._get_c_name(),)) + + @staticmethod + def _to_ctypes(value): + raise TypeError + + @classmethod + def _arg_to_ctypes(cls, *value): + try: + ctype = cls._ctype + except AttributeError: + raise TypeError("cannot create an instance of %r" % (cls,)) + if value: + res = cls._to_ctypes(*value) + if not isinstance(res, ctype): + res = cls._ctype(res) + else: + res = cls._ctype() + return res + + @classmethod + def _create_ctype_obj(cls, init): + if init is None: + return cls._arg_to_ctypes() + else: + return cls._arg_to_ctypes(init) + + @staticmethod + def _from_ctypes(ctypes_value): + raise TypeError + + @classmethod + def _get_c_name(cls, replace_with=''): + return cls._reftypename.replace(' &', replace_with) + + @classmethod + def _fix_class(cls): + cls.__name__ = 'CData<%s>' % (cls._get_c_name(),) + cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),) + cls.__module__ = 'ffi' + + def _get_own_repr(self): + raise NotImplementedError + + def _addr_repr(self, address): + if address == 0: + return 'NULL' + else: + if address < 0: + address += 1 << (8*ctypes.sizeof(ctypes.c_void_p)) + return '0x%x' % address + + def __repr__(self, c_name=None): + own = self._get_own_repr() + return '' % (c_name or self._get_c_name(), own) + + def _convert_to_address(self, BClass): + if BClass is None: + raise TypeError("cannot convert %r to an address" % ( + self._get_c_name(),)) + else: + raise TypeError("cannot convert %r to %r" % ( + self._get_c_name(), BClass._get_c_name())) + + @classmethod + def _get_size(cls): + return ctypes.sizeof(cls._ctype) + + def _get_size_of_instance(self): + return ctypes.sizeof(self._ctype) + + @classmethod + def _cast_from(cls, source): + raise TypeError("cannot cast to %r" % (cls._get_c_name(),)) + + def _cast_to_integer(self): + return self._convert_to_address(None) + + @classmethod + def _alignment(cls): + return ctypes.alignment(cls._ctype) + + def __iter__(self): + raise TypeError("cdata %r does not support iteration" % ( + self._get_c_name()),) + + def _make_cmp(name): + cmpfunc = getattr(operator, name) + def cmp(self, other): + v_is_ptr = not isinstance(self, CTypesGenericPrimitive) + w_is_ptr = (isinstance(other, CTypesData) and + not isinstance(other, CTypesGenericPrimitive)) + if v_is_ptr and w_is_ptr: + return cmpfunc(self._convert_to_address(None), + other._convert_to_address(None)) + elif v_is_ptr or w_is_ptr: + return NotImplemented + else: + if isinstance(self, CTypesGenericPrimitive): + self = self._value + if isinstance(other, CTypesGenericPrimitive): + other = other._value + return cmpfunc(self, other) + cmp.func_name = name + return cmp + + __eq__ = _make_cmp('__eq__') + __ne__ = _make_cmp('__ne__') + __lt__ = _make_cmp('__lt__') + __le__ = _make_cmp('__le__') + __gt__ = _make_cmp('__gt__') + __ge__ = _make_cmp('__ge__') + + def __hash__(self): + return hash(self._convert_to_address(None)) + + def _to_string(self, maxlen): + raise TypeError("string(): %r" % (self,)) + + +class CTypesGenericPrimitive(CTypesData): + __slots__ = [] + + def __hash__(self): + return hash(self._value) + + def _get_own_repr(self): + return repr(self._from_ctypes(self._value)) + + +class CTypesGenericArray(CTypesData): + __slots__ = [] + + @classmethod + def _newp(cls, init): + return cls(init) + + def __iter__(self): + for i in xrange(len(self)): + yield self[i] + + def _get_own_repr(self): + return self._addr_repr(ctypes.addressof(self._blob)) + + +class CTypesGenericPtr(CTypesData): + __slots__ = ['_address', '_as_ctype_ptr'] + _automatic_casts = False + kind = "pointer" + + @classmethod + def _newp(cls, init): + return cls(init) + + @classmethod + def _cast_from(cls, source): + if source is None: + address = 0 + elif isinstance(source, CTypesData): + address = source._cast_to_integer() + elif isinstance(source, (int, long)): + address = source + else: + raise TypeError("bad type for cast to %r: %r" % + (cls, type(source).__name__)) + return cls._new_pointer_at(address) + + @classmethod + def _new_pointer_at(cls, address): + self = cls.__new__(cls) + self._address = address + self._as_ctype_ptr = ctypes.cast(address, cls._ctype) + return self + + def _get_own_repr(self): + try: + return self._addr_repr(self._address) + except AttributeError: + return '???' + + def _cast_to_integer(self): + return self._address + + def __nonzero__(self): + return bool(self._address) + __bool__ = __nonzero__ + + @classmethod + def _to_ctypes(cls, value): + if not isinstance(value, CTypesData): + raise TypeError("unexpected %s object" % type(value).__name__) + address = value._convert_to_address(cls) + return ctypes.cast(address, cls._ctype) + + @classmethod + def _from_ctypes(cls, ctypes_ptr): + address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0 + return cls._new_pointer_at(address) + + @classmethod + def _initialize(cls, ctypes_ptr, value): + if value: + ctypes_ptr.contents = cls._to_ctypes(value).contents + + def _convert_to_address(self, BClass): + if (BClass in (self.__class__, None) or BClass._automatic_casts + or self._automatic_casts): + return self._address + else: + return CTypesData._convert_to_address(self, BClass) + + +class CTypesBaseStructOrUnion(CTypesData): + __slots__ = ['_blob'] + + @classmethod + def _create_ctype_obj(cls, init): + # may be overridden + raise TypeError("cannot instantiate opaque type %s" % (cls,)) + + def _get_own_repr(self): + return self._addr_repr(ctypes.addressof(self._blob)) + + @classmethod + def _offsetof(cls, fieldname): + return getattr(cls._ctype, fieldname).offset + + def _convert_to_address(self, BClass): + if getattr(BClass, '_BItem', None) is self.__class__: + return ctypes.addressof(self._blob) + else: + return CTypesData._convert_to_address(self, BClass) + + @classmethod + def _from_ctypes(cls, ctypes_struct_or_union): + self = cls.__new__(cls) + self._blob = ctypes_struct_or_union + return self + + @classmethod + def _to_ctypes(cls, value): + return value._blob + + def __repr__(self, c_name=None): + return CTypesData.__repr__(self, c_name or self._get_c_name(' &')) + + +class CTypesBackend: + + PRIMITIVE_TYPES = { + 'char': ctypes.c_char, + 'short': ctypes.c_short, + 'int': ctypes.c_int, + 'long': ctypes.c_long, + 'long long': ctypes.c_longlong, + 'signed char': ctypes.c_byte, + 'unsigned char': ctypes.c_ubyte, + 'unsigned short': ctypes.c_ushort, + 'unsigned int': ctypes.c_uint, + 'unsigned long': ctypes.c_ulong, + 'unsigned long long': ctypes.c_ulonglong, + 'float': ctypes.c_float, + 'double': ctypes.c_double, + '_Bool': ctypes.c_bool, + } + + for _name in ['unsigned long long', 'unsigned long', + 'unsigned int', 'unsigned short', 'unsigned char']: + _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) + PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_void_p): + PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_size_t): + PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name] + + for _name in ['long long', 'long', 'int', 'short', 'signed char']: + _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) + PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_void_p): + PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name] + PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_size_t): + PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name] + + + def __init__(self): + self.RTLD_LAZY = 0 # not supported anyway by ctypes + self.RTLD_NOW = 0 + self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL + self.RTLD_LOCAL = ctypes.RTLD_LOCAL + + def set_ffi(self, ffi): + self.ffi = ffi + + def _get_types(self): + return CTypesData, CTypesType + + def load_library(self, path, flags=0): + cdll = ctypes.CDLL(path, flags) + return CTypesLibrary(self, cdll) + + def new_void_type(self): + class CTypesVoid(CTypesData): + __slots__ = [] + _reftypename = 'void &' + @staticmethod + def _from_ctypes(novalue): + return None + @staticmethod + def _to_ctypes(novalue): + if novalue is not None: + raise TypeError("None expected, got %s object" % + (type(novalue).__name__,)) + CTypesVoid._fix_class() + return CTypesVoid + + def new_primitive_type(self, name): + if name == 'wchar_t': + raise NotImplementedError(name) + ctype = self.PRIMITIVE_TYPES[name] + if name == 'char': + kind = 'char' + elif name in ('float', 'double'): + kind = 'float' + else: + if name in ('signed char', 'unsigned char'): + kind = 'byte' + elif name == '_Bool': + kind = 'bool' + else: + kind = 'int' + is_signed = (ctype(-1).value == -1) + # + def _cast_source_to_int(source): + if isinstance(source, (int, long, float)): + source = int(source) + elif isinstance(source, CTypesData): + source = source._cast_to_integer() + elif isinstance(source, bytes): + source = ord(source) + elif source is None: + source = 0 + else: + raise TypeError("bad type for cast to %r: %r" % + (CTypesPrimitive, type(source).__name__)) + return source + # + kind1 = kind + class CTypesPrimitive(CTypesGenericPrimitive): + __slots__ = ['_value'] + _ctype = ctype + _reftypename = '%s &' % name + kind = kind1 + + def __init__(self, value): + self._value = value + + @staticmethod + def _create_ctype_obj(init): + if init is None: + return ctype() + return ctype(CTypesPrimitive._to_ctypes(init)) + + if kind in {'int', 'byte'}: + @classmethod + def _cast_from(cls, source): + source = _cast_source_to_int(source) + source = ctype(source).value # cast within range + return cls(source) + def __int__(self): + return self._value + + if kind == 'bool': + @classmethod + def _cast_from(cls, source): + if not isinstance(source, (int, long, float)): + source = _cast_source_to_int(source) + return cls(bool(source)) + def __int__(self): + return int(self._value) + + if kind == 'char': + @classmethod + def _cast_from(cls, source): + source = _cast_source_to_int(source) + source = bytechr(source & 0xFF) + return cls(source) + def __int__(self): + return ord(self._value) + + if kind == 'float': + @classmethod + def _cast_from(cls, source): + if isinstance(source, float): + pass + elif isinstance(source, CTypesGenericPrimitive): + if hasattr(source, '__float__'): + source = float(source) + else: + source = int(source) + else: + source = _cast_source_to_int(source) + source = ctype(source).value # fix precision + return cls(source) + def __int__(self): + return int(self._value) + def __float__(self): + return self._value + + _cast_to_integer = __int__ + + if kind in {'int', 'byte', 'bool'}: + @staticmethod + def _to_ctypes(x): + if not isinstance(x, (int, long)): + if isinstance(x, CTypesData): + x = int(x) + else: + raise TypeError("integer expected, got %s" % + type(x).__name__) + if ctype(x).value != x: + if not is_signed and x < 0: + raise OverflowError("%s: negative integer" % name) + else: + raise OverflowError("%s: integer out of bounds" + % name) + return x + + if kind == 'char': + @staticmethod + def _to_ctypes(x): + if isinstance(x, bytes) and len(x) == 1: + return x + if isinstance(x, CTypesPrimitive): # > + return x._value + raise TypeError("character expected, got %s" % + type(x).__name__) + def __nonzero__(self): + return ord(self._value) != 0 + else: + def __nonzero__(self): + return self._value != 0 + __bool__ = __nonzero__ + + if kind == 'float': + @staticmethod + def _to_ctypes(x): + if not isinstance(x, (int, long, float, CTypesData)): + raise TypeError("float expected, got %s" % + type(x).__name__) + return ctype(x).value + + @staticmethod + def _from_ctypes(value): + return getattr(value, 'value', value) + + @staticmethod + def _initialize(blob, init): + blob.value = CTypesPrimitive._to_ctypes(init) + + if kind == 'char': + def _to_string(self, maxlen): + return self._value + if kind == 'byte': + def _to_string(self, maxlen): + return chr(self._value & 0xff) + # + CTypesPrimitive._fix_class() + return CTypesPrimitive + + def new_pointer_type(self, BItem): + getbtype = self.ffi._get_cached_btype + if BItem is getbtype(model.PrimitiveType('char')): + kind = 'charp' + elif BItem in (getbtype(model.PrimitiveType('signed char')), + getbtype(model.PrimitiveType('unsigned char'))): + kind = 'bytep' + elif BItem is getbtype(model.void_type): + kind = 'voidp' + else: + kind = 'generic' + # + class CTypesPtr(CTypesGenericPtr): + __slots__ = ['_own'] + if kind == 'charp': + __slots__ += ['__as_strbuf'] + _BItem = BItem + if hasattr(BItem, '_ctype'): + _ctype = ctypes.POINTER(BItem._ctype) + _bitem_size = ctypes.sizeof(BItem._ctype) + else: + _ctype = ctypes.c_void_p + if issubclass(BItem, CTypesGenericArray): + _reftypename = BItem._get_c_name('(* &)') + else: + _reftypename = BItem._get_c_name(' * &') + + def __init__(self, init): + ctypeobj = BItem._create_ctype_obj(init) + if kind == 'charp': + self.__as_strbuf = ctypes.create_string_buffer( + ctypeobj.value + b'\x00') + self._as_ctype_ptr = ctypes.cast( + self.__as_strbuf, self._ctype) + else: + self._as_ctype_ptr = ctypes.pointer(ctypeobj) + self._address = ctypes.cast(self._as_ctype_ptr, + ctypes.c_void_p).value + self._own = True + + def __add__(self, other): + if isinstance(other, (int, long)): + return self._new_pointer_at(self._address + + other * self._bitem_size) + else: + return NotImplemented + + def __sub__(self, other): + if isinstance(other, (int, long)): + return self._new_pointer_at(self._address - + other * self._bitem_size) + elif type(self) is type(other): + return (self._address - other._address) // self._bitem_size + else: + return NotImplemented + + def __getitem__(self, index): + if getattr(self, '_own', False) and index != 0: + raise IndexError + return BItem._from_ctypes(self._as_ctype_ptr[index]) + + def __setitem__(self, index, value): + self._as_ctype_ptr[index] = BItem._to_ctypes(value) + + if kind in {'charp', 'voidp'}: + @classmethod + def _arg_to_ctypes(cls, *value): + if value and isinstance(value[0], bytes): + return ctypes.c_char_p(value[0]) + else: + return super()._arg_to_ctypes(*value) + + if kind in {'charp', 'bytep'}: + def _to_string(self, maxlen): + if maxlen < 0: + maxlen = sys.maxsize + p = ctypes.cast(self._as_ctype_ptr, + ctypes.POINTER(ctypes.c_char)) + n = 0 + while n < maxlen and p[n] != b'\x00': + n += 1 + return b''.join([p[i] for i in range(n)]) + + def _get_own_repr(self): + if getattr(self, '_own', False): + return 'owning %d bytes' % ( + ctypes.sizeof(self._as_ctype_ptr.contents),) + return super()._get_own_repr() + # + if (BItem is self.ffi._get_cached_btype(model.void_type) or + BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))): + CTypesPtr._automatic_casts = True + # + CTypesPtr._fix_class() + return CTypesPtr + + def new_array_type(self, CTypesPtr, length): + if length is None: + brackets = ' &[]' + else: + brackets = ' &[%d]' % length + BItem = CTypesPtr._BItem + getbtype = self.ffi._get_cached_btype + if BItem is getbtype(model.PrimitiveType('char')): + kind = 'char' + elif BItem in (getbtype(model.PrimitiveType('signed char')), + getbtype(model.PrimitiveType('unsigned char'))): + kind = 'byte' + else: + kind = 'generic' + # + class CTypesArray(CTypesGenericArray): + __slots__ = ['_blob', '_own'] + if length is not None: + _ctype = BItem._ctype * length + else: + __slots__.append('_ctype') + _reftypename = BItem._get_c_name(brackets) + _declared_length = length + _CTPtr = CTypesPtr + + def __init__(self, init): + if length is None: + if isinstance(init, (int, long)): + len1 = init + init = None + elif kind == 'char' and isinstance(init, bytes): + len1 = len(init) + 1 # extra null + else: + init = tuple(init) + len1 = len(init) + self._ctype = BItem._ctype * len1 + self._blob = self._ctype() + self._own = True + if init is not None: + self._initialize(self._blob, init) + + @staticmethod + def _initialize(blob, init): + if isinstance(init, bytes): + init = [init[i:i+1] for i in range(len(init))] + else: + if isinstance(init, CTypesGenericArray): + if (len(init) != len(blob) or + not isinstance(init, CTypesArray)): + raise TypeError("length/type mismatch: %s" % (init,)) + init = tuple(init) + if len(init) > len(blob): + raise IndexError("too many initializers") + addr = ctypes.cast(blob, ctypes.c_void_p).value + PTR = ctypes.POINTER(BItem._ctype) + itemsize = ctypes.sizeof(BItem._ctype) + for i, value in enumerate(init): + p = ctypes.cast(addr + i * itemsize, PTR) + BItem._initialize(p.contents, value) + + def __len__(self): + return len(self._blob) + + def __getitem__(self, index): + if not (0 <= index < len(self._blob)): + raise IndexError + return BItem._from_ctypes(self._blob[index]) + + def __setitem__(self, index, value): + if not (0 <= index < len(self._blob)): + raise IndexError + self._blob[index] = BItem._to_ctypes(value) + + if kind in {'char', 'byte'}: + def _to_string(self, maxlen): + if maxlen < 0: + maxlen = len(self._blob) + p = ctypes.cast(self._blob, + ctypes.POINTER(ctypes.c_char)) + n = 0 + while n < maxlen and p[n] != b'\x00': + n += 1 + return b''.join([p[i] for i in range(n)]) + + def _get_own_repr(self): + if getattr(self, '_own', False): + return 'owning %d bytes' % (ctypes.sizeof(self._blob),) + return super()._get_own_repr() + + def _convert_to_address(self, BClass): + if BClass in (CTypesPtr, None) or BClass._automatic_casts: + return ctypes.addressof(self._blob) + else: + return CTypesData._convert_to_address(self, BClass) + + @staticmethod + def _from_ctypes(ctypes_array): + self = CTypesArray.__new__(CTypesArray) + self._blob = ctypes_array + return self + + @staticmethod + def _arg_to_ctypes(value): + return CTypesPtr._arg_to_ctypes(value) + + def __add__(self, other): + if isinstance(other, (int, long)): + return CTypesPtr._new_pointer_at( + ctypes.addressof(self._blob) + + other * ctypes.sizeof(BItem._ctype)) + else: + return NotImplemented + + @classmethod + def _cast_from(cls, source): + raise NotImplementedError("casting to %r" % ( + cls._get_c_name(),)) + # + CTypesArray._fix_class() + return CTypesArray + + def _new_struct_or_union(self, kind, name, base_ctypes_class): + # + class struct_or_union(base_ctypes_class): + pass + struct_or_union.__name__ = '%s_%s' % (kind, name) + kind1 = kind + # + class CTypesStructOrUnion(CTypesBaseStructOrUnion): + __slots__ = ['_blob'] + _ctype = struct_or_union + _reftypename = '%s &' % (name,) + _kind = kind = kind1 + # + CTypesStructOrUnion._fix_class() + return CTypesStructOrUnion + + def new_struct_type(self, name): + return self._new_struct_or_union('struct', name, ctypes.Structure) + + def new_union_type(self, name): + return self._new_struct_or_union('union', name, ctypes.Union) + + def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp, + totalsize=-1, totalalignment=-1, sflags=0, + pack=0): + if totalsize >= 0 or totalalignment >= 0: + raise NotImplementedError("the ctypes backend of CFFI does not support " + "structures completed by verify(); please " + "compile and install the _cffi_backend module.") + struct_or_union = CTypesStructOrUnion._ctype + fnames = [fname for (fname, BField, bitsize) in fields] + btypes = [BField for (fname, BField, bitsize) in fields] + bitfields = [bitsize for (fname, BField, bitsize) in fields] + # + bfield_types = {} + cfields = [] + for (fname, BField, bitsize) in fields: + if bitsize < 0: + cfields.append((fname, BField._ctype)) + bfield_types[fname] = BField + else: + cfields.append((fname, BField._ctype, bitsize)) + bfield_types[fname] = Ellipsis + if sflags & 8: + struct_or_union._pack_ = 1 + elif pack: + struct_or_union._pack_ = pack + struct_or_union._fields_ = cfields + CTypesStructOrUnion._bfield_types = bfield_types + # + @staticmethod + def _create_ctype_obj(init): + result = struct_or_union() + if init is not None: + initialize(result, init) + return result + CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj + # + def initialize(blob, init): + if is_union: + if len(init) > 1: + raise ValueError("union initializer: %d items given, but " + "only one supported (use a dict if needed)" + % (len(init),)) + if not isinstance(init, dict): + if isinstance(init, (bytes, unicode)): + raise TypeError("union initializer: got a str") + init = tuple(init) + if len(init) > len(fnames): + raise ValueError("too many values for %s initializer" % + CTypesStructOrUnion._get_c_name()) + init = dict(zip(fnames, init)) + addr = ctypes.addressof(blob) + for fname, value in init.items(): + BField, bitsize = name2fieldtype[fname] + assert bitsize < 0, \ + "not implemented: initializer with bit fields" + offset = CTypesStructOrUnion._offsetof(fname) + PTR = ctypes.POINTER(BField._ctype) + p = ctypes.cast(addr + offset, PTR) + BField._initialize(p.contents, value) + is_union = CTypesStructOrUnion._kind == 'union' + name2fieldtype = dict(zip(fnames, zip(btypes, bitfields))) + # + for fname, BField, bitsize in fields: + if fname == '': + raise NotImplementedError("nested anonymous structs/unions") + if hasattr(CTypesStructOrUnion, fname): + raise ValueError("the field name %r conflicts in " + "the ctypes backend" % fname) + if bitsize < 0: + def getter(self, fname=fname, BField=BField, + offset=CTypesStructOrUnion._offsetof(fname), + PTR=ctypes.POINTER(BField._ctype)): + addr = ctypes.addressof(self._blob) + p = ctypes.cast(addr + offset, PTR) + return BField._from_ctypes(p.contents) + def setter(self, value, fname=fname, BField=BField): + setattr(self._blob, fname, BField._to_ctypes(value)) + # + if issubclass(BField, CTypesGenericArray): + setter = None + if BField._declared_length == 0: + def getter(self, fname=fname, BFieldPtr=BField._CTPtr, + offset=CTypesStructOrUnion._offsetof(fname), + PTR=ctypes.POINTER(BField._ctype)): + addr = ctypes.addressof(self._blob) + p = ctypes.cast(addr + offset, PTR) + return BFieldPtr._from_ctypes(p) + # + else: + def getter(self, fname=fname, BField=BField): + return BField._from_ctypes(getattr(self._blob, fname)) + def setter(self, value, fname=fname, BField=BField): + # xxx obscure workaround + value = BField._to_ctypes(value) + oldvalue = getattr(self._blob, fname) + setattr(self._blob, fname, value) + if value != getattr(self._blob, fname): + setattr(self._blob, fname, oldvalue) + raise OverflowError("value too large for bitfield") + setattr(CTypesStructOrUnion, fname, property(getter, setter)) + # + CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp)) + for fname in fnames: + if hasattr(CTypesPtr, fname): + raise ValueError("the field name %r conflicts in " + "the ctypes backend" % fname) + def getter(self, fname=fname): + return getattr(self[0], fname) + def setter(self, value, fname=fname): + setattr(self[0], fname, value) + setattr(CTypesPtr, fname, property(getter, setter)) + + def new_function_type(self, BArgs, BResult, has_varargs): + nameargs = [BArg._get_c_name() for BArg in BArgs] + if has_varargs: + nameargs.append('...') + nameargs = ', '.join(nameargs) + # + class CTypesFunctionPtr(CTypesGenericPtr): + __slots__ = ['_own_callback', '_name'] + _ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None), + *[BArg._ctype for BArg in BArgs], + use_errno=True) + _reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,)) + + def __init__(self, init, error=None): + # create a callback to the Python callable init() + import traceback + assert not has_varargs, "varargs not supported for callbacks" + if getattr(BResult, '_ctype', None) is not None: + error = BResult._from_ctypes( + BResult._create_ctype_obj(error)) + else: + error = None + def callback(*args): + args2 = [] + for arg, BArg in zip(args, BArgs): + args2.append(BArg._from_ctypes(arg)) + try: + res2 = init(*args2) + res2 = BResult._to_ctypes(res2) + except: + traceback.print_exc() + res2 = error + if issubclass(BResult, CTypesGenericPtr): + if res2: + res2 = ctypes.cast(res2, ctypes.c_void_p).value + # .value: http://bugs.python.org/issue1574593 + else: + res2 = None + #print repr(res2) + return res2 + if issubclass(BResult, CTypesGenericPtr): + # The only pointers callbacks can return are void*s: + # http://bugs.python.org/issue5710 + callback_ctype = ctypes.CFUNCTYPE( + ctypes.c_void_p, + *[BArg._ctype for BArg in BArgs], + use_errno=True) + else: + callback_ctype = CTypesFunctionPtr._ctype + self._as_ctype_ptr = callback_ctype(callback) + self._address = ctypes.cast(self._as_ctype_ptr, + ctypes.c_void_p).value + self._own_callback = init + + @staticmethod + def _initialize(ctypes_ptr, value): + if value: + raise NotImplementedError("ctypes backend: not supported: " + "initializers for function pointers") + + def __repr__(self): + c_name = getattr(self, '_name', None) + if c_name: + i = self._reftypename.index('(* &)') + if self._reftypename[i-1] not in ' )*': + c_name = ' ' + c_name + c_name = self._reftypename.replace('(* &)', c_name) + return CTypesData.__repr__(self, c_name) + + def _get_own_repr(self): + if getattr(self, '_own_callback', None) is not None: + return 'calling %r' % (self._own_callback,) + return super()._get_own_repr() + + def __call__(self, *args): + if has_varargs: + assert len(args) >= len(BArgs) + extraargs = args[len(BArgs):] + args = args[:len(BArgs)] + else: + assert len(args) == len(BArgs) + ctypes_args = [] + for arg, BArg in zip(args, BArgs): + ctypes_args.append(BArg._arg_to_ctypes(arg)) + if has_varargs: + for i, arg in enumerate(extraargs): + if arg is None: + ctypes_args.append(ctypes.c_void_p(0)) # NULL + continue + if not isinstance(arg, CTypesData): + raise TypeError( + "argument %d passed in the variadic part " + "needs to be a cdata object (got %s)" % + (1 + len(BArgs) + i, type(arg).__name__)) + ctypes_args.append(arg._arg_to_ctypes(arg)) + result = self._as_ctype_ptr(*ctypes_args) + return BResult._from_ctypes(result) + # + CTypesFunctionPtr._fix_class() + return CTypesFunctionPtr + + def new_enum_type(self, name, enumerators, enumvalues, CTypesInt): + assert isinstance(name, str) + reverse_mapping = dict(zip(reversed(enumvalues), + reversed(enumerators))) + # + class CTypesEnum(CTypesInt): + __slots__ = [] + _reftypename = '%s &' % name + + def _get_own_repr(self): + value = self._value + try: + return '%d: %s' % (value, reverse_mapping[value]) + except KeyError: + return str(value) + + def _to_string(self, maxlen): + value = self._value + try: + return reverse_mapping[value] + except KeyError: + return str(value) + # + CTypesEnum._fix_class() + return CTypesEnum + + def get_errno(self): + return ctypes.get_errno() + + def set_errno(self, value): + ctypes.set_errno(value) + + def string(self, b, maxlen=-1): + return b._to_string(maxlen) + + def buffer(self, bptr, size=-1): + raise NotImplementedError("buffer() with ctypes backend") + + def sizeof(self, cdata_or_BType): + if isinstance(cdata_or_BType, CTypesData): + return cdata_or_BType._get_size_of_instance() + else: + assert issubclass(cdata_or_BType, CTypesData) + return cdata_or_BType._get_size() + + def alignof(self, BType): + assert issubclass(BType, CTypesData) + return BType._alignment() + + def newp(self, BType, source): + if not issubclass(BType, CTypesData): + raise TypeError + return BType._newp(source) + + def cast(self, BType, source): + return BType._cast_from(source) + + def callback(self, BType, source, error, onerror): + assert onerror is None # XXX not implemented + return BType(source, error) + + _weakref_cache_ref = None + + def gcp(self, cdata, destructor, size=0): + if self._weakref_cache_ref is None: + import weakref + class MyRef(weakref.ref): + def __eq__(self, other): + myref = self() + return self is other or ( + myref is not None and myref is other()) + def __ne__(self, other): + return not (self == other) + def __hash__(self): + try: + return self._hash + except AttributeError: + self._hash = hash(self()) + return self._hash + self._weakref_cache_ref = {}, MyRef + weak_cache, MyRef = self._weakref_cache_ref + + if destructor is None: + try: + del weak_cache[MyRef(cdata)] + except KeyError: + raise TypeError("Can remove destructor only on a object " + "previously returned by ffi.gc()") + return None + + def remove(k): + cdata, destructor = weak_cache.pop(k, (None, None)) + if destructor is not None: + destructor(cdata) + + new_cdata = self.cast(self.typeof(cdata), cdata) + assert new_cdata is not cdata + weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor) + return new_cdata + + typeof = type + + def getcname(self, BType, replace_with): + return BType._get_c_name(replace_with) + + def typeoffsetof(self, BType, fieldname, num=0): + if isinstance(fieldname, str): + if num == 0 and issubclass(BType, CTypesGenericPtr): + BType = BType._BItem + if not issubclass(BType, CTypesBaseStructOrUnion): + raise TypeError("expected a struct or union ctype") + BField = BType._bfield_types[fieldname] + if BField is Ellipsis: + raise TypeError("not supported for bitfields") + return (BField, BType._offsetof(fieldname)) + elif isinstance(fieldname, (int, long)): + if issubclass(BType, CTypesGenericArray): + BType = BType._CTPtr + if not issubclass(BType, CTypesGenericPtr): + raise TypeError("expected an array or ptr ctype") + BItem = BType._BItem + offset = BItem._get_size() * fieldname + if offset > sys.maxsize: + raise OverflowError + return (BItem, offset) + else: + raise TypeError(type(fieldname)) + + def rawaddressof(self, BTypePtr, cdata, offset=None): + if isinstance(cdata, CTypesBaseStructOrUnion): + ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) + elif isinstance(cdata, CTypesGenericPtr): + if offset is None or not issubclass(type(cdata)._BItem, + CTypesBaseStructOrUnion): + raise TypeError("unexpected cdata type") + ptr = type(cdata)._to_ctypes(cdata) + elif isinstance(cdata, CTypesGenericArray): + ptr = type(cdata)._to_ctypes(cdata) + else: + raise TypeError("expected a ") + if offset: + ptr = ctypes.cast( + ctypes.c_void_p( + ctypes.cast(ptr, ctypes.c_void_p).value + offset), + type(ptr)) + return BTypePtr._from_ctypes(ptr) + + +class CTypesLibrary: + + def __init__(self, backend, cdll): + self.backend = backend + self.cdll = cdll + + def load_function(self, BType, name): + c_func = getattr(self.cdll, name) + funcobj = BType._from_ctypes(c_func) + funcobj._name = name + return funcobj + + def read_variable(self, BType, name): + try: + ctypes_obj = BType._ctype.in_dll(self.cdll, name) + except AttributeError as e: + raise NotImplementedError(e) + return BType._from_ctypes(ctypes_obj) + + def write_variable(self, BType, name, value): + new_ctypes_obj = BType._to_ctypes(value) + ctypes_obj = BType._ctype.in_dll(self.cdll, name) + ctypes.memmove(ctypes.addressof(ctypes_obj), + ctypes.addressof(new_ctypes_obj), + ctypes.sizeof(BType._ctype)) diff --git a/venv/lib/python3.12/site-packages/cffi/cffi_opcode.py b/venv/lib/python3.12/site-packages/cffi/cffi_opcode.py new file mode 100644 index 0000000..c964ba1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/cffi_opcode.py @@ -0,0 +1,187 @@ +from .error import VerificationError + +class CffiOp: + def __init__(self, op, arg): + self.op = op + self.arg = arg + + def as_c_expr(self): + if self.op is None: + assert isinstance(self.arg, str) + return '(_cffi_opcode_t)(%s)' % (self.arg,) + classname = CLASS_NAME[self.op] + return '_CFFI_OP(_CFFI_OP_%s, %s)' % (classname, self.arg) + + def as_python_bytes(self): + if self.op is None and self.arg.isdigit(): + value = int(self.arg) # non-negative: '-' not in self.arg + if value >= 2**31: + raise OverflowError("cannot emit %r: limited to 2**31-1" + % (self.arg,)) + return format_four_bytes(value) + if isinstance(self.arg, str): + raise VerificationError("cannot emit to Python: %r" % (self.arg,)) + return format_four_bytes((self.arg << 8) | self.op) + + def __str__(self): + classname = CLASS_NAME.get(self.op, self.op) + return '(%s %s)' % (classname, self.arg) + +def format_four_bytes(num): + return '\\x%02X\\x%02X\\x%02X\\x%02X' % ( + (num >> 24) & 0xFF, + (num >> 16) & 0xFF, + (num >> 8) & 0xFF, + (num ) & 0xFF) + +OP_PRIMITIVE = 1 +OP_POINTER = 3 +OP_ARRAY = 5 +OP_OPEN_ARRAY = 7 +OP_STRUCT_UNION = 9 +OP_ENUM = 11 +OP_FUNCTION = 13 +OP_FUNCTION_END = 15 +OP_NOOP = 17 +OP_BITFIELD = 19 +OP_TYPENAME = 21 +OP_CPYTHON_BLTN_V = 23 # varargs +OP_CPYTHON_BLTN_N = 25 # noargs +OP_CPYTHON_BLTN_O = 27 # O (i.e. a single arg) +OP_CONSTANT = 29 +OP_CONSTANT_INT = 31 +OP_GLOBAL_VAR = 33 +OP_DLOPEN_FUNC = 35 +OP_DLOPEN_CONST = 37 +OP_GLOBAL_VAR_F = 39 +OP_EXTERN_PYTHON = 41 + +PRIM_VOID = 0 +PRIM_BOOL = 1 +PRIM_CHAR = 2 +PRIM_SCHAR = 3 +PRIM_UCHAR = 4 +PRIM_SHORT = 5 +PRIM_USHORT = 6 +PRIM_INT = 7 +PRIM_UINT = 8 +PRIM_LONG = 9 +PRIM_ULONG = 10 +PRIM_LONGLONG = 11 +PRIM_ULONGLONG = 12 +PRIM_FLOAT = 13 +PRIM_DOUBLE = 14 +PRIM_LONGDOUBLE = 15 + +PRIM_WCHAR = 16 +PRIM_INT8 = 17 +PRIM_UINT8 = 18 +PRIM_INT16 = 19 +PRIM_UINT16 = 20 +PRIM_INT32 = 21 +PRIM_UINT32 = 22 +PRIM_INT64 = 23 +PRIM_UINT64 = 24 +PRIM_INTPTR = 25 +PRIM_UINTPTR = 26 +PRIM_PTRDIFF = 27 +PRIM_SIZE = 28 +PRIM_SSIZE = 29 +PRIM_INT_LEAST8 = 30 +PRIM_UINT_LEAST8 = 31 +PRIM_INT_LEAST16 = 32 +PRIM_UINT_LEAST16 = 33 +PRIM_INT_LEAST32 = 34 +PRIM_UINT_LEAST32 = 35 +PRIM_INT_LEAST64 = 36 +PRIM_UINT_LEAST64 = 37 +PRIM_INT_FAST8 = 38 +PRIM_UINT_FAST8 = 39 +PRIM_INT_FAST16 = 40 +PRIM_UINT_FAST16 = 41 +PRIM_INT_FAST32 = 42 +PRIM_UINT_FAST32 = 43 +PRIM_INT_FAST64 = 44 +PRIM_UINT_FAST64 = 45 +PRIM_INTMAX = 46 +PRIM_UINTMAX = 47 +PRIM_FLOATCOMPLEX = 48 +PRIM_DOUBLECOMPLEX = 49 +PRIM_CHAR16 = 50 +PRIM_CHAR32 = 51 + +_NUM_PRIM = 52 +_UNKNOWN_PRIM = -1 +_UNKNOWN_FLOAT_PRIM = -2 +_UNKNOWN_LONG_DOUBLE = -3 + +_IO_FILE_STRUCT = -1 + +PRIMITIVE_TO_INDEX = { + 'char': PRIM_CHAR, + 'short': PRIM_SHORT, + 'int': PRIM_INT, + 'long': PRIM_LONG, + 'long long': PRIM_LONGLONG, + 'signed char': PRIM_SCHAR, + 'unsigned char': PRIM_UCHAR, + 'unsigned short': PRIM_USHORT, + 'unsigned int': PRIM_UINT, + 'unsigned long': PRIM_ULONG, + 'unsigned long long': PRIM_ULONGLONG, + 'float': PRIM_FLOAT, + 'double': PRIM_DOUBLE, + 'long double': PRIM_LONGDOUBLE, + '_cffi_float_complex_t': PRIM_FLOATCOMPLEX, + '_cffi_double_complex_t': PRIM_DOUBLECOMPLEX, + '_Bool': PRIM_BOOL, + 'wchar_t': PRIM_WCHAR, + 'char16_t': PRIM_CHAR16, + 'char32_t': PRIM_CHAR32, + 'int8_t': PRIM_INT8, + 'uint8_t': PRIM_UINT8, + 'int16_t': PRIM_INT16, + 'uint16_t': PRIM_UINT16, + 'int32_t': PRIM_INT32, + 'uint32_t': PRIM_UINT32, + 'int64_t': PRIM_INT64, + 'uint64_t': PRIM_UINT64, + 'intptr_t': PRIM_INTPTR, + 'uintptr_t': PRIM_UINTPTR, + 'ptrdiff_t': PRIM_PTRDIFF, + 'size_t': PRIM_SIZE, + 'ssize_t': PRIM_SSIZE, + 'int_least8_t': PRIM_INT_LEAST8, + 'uint_least8_t': PRIM_UINT_LEAST8, + 'int_least16_t': PRIM_INT_LEAST16, + 'uint_least16_t': PRIM_UINT_LEAST16, + 'int_least32_t': PRIM_INT_LEAST32, + 'uint_least32_t': PRIM_UINT_LEAST32, + 'int_least64_t': PRIM_INT_LEAST64, + 'uint_least64_t': PRIM_UINT_LEAST64, + 'int_fast8_t': PRIM_INT_FAST8, + 'uint_fast8_t': PRIM_UINT_FAST8, + 'int_fast16_t': PRIM_INT_FAST16, + 'uint_fast16_t': PRIM_UINT_FAST16, + 'int_fast32_t': PRIM_INT_FAST32, + 'uint_fast32_t': PRIM_UINT_FAST32, + 'int_fast64_t': PRIM_INT_FAST64, + 'uint_fast64_t': PRIM_UINT_FAST64, + 'intmax_t': PRIM_INTMAX, + 'uintmax_t': PRIM_UINTMAX, + } + +F_UNION = 0x01 +F_CHECK_FIELDS = 0x02 +F_PACKED = 0x04 +F_EXTERNAL = 0x08 +F_OPAQUE = 0x10 + +G_FLAGS = dict([('_CFFI_' + _key, globals()[_key]) + for _key in ['F_UNION', 'F_CHECK_FIELDS', 'F_PACKED', + 'F_EXTERNAL', 'F_OPAQUE']]) + +CLASS_NAME = {} +for _name, _value in list(globals().items()): + if _name.startswith('OP_') and isinstance(_value, int): + CLASS_NAME[_value] = _name[3:] diff --git a/venv/lib/python3.12/site-packages/cffi/commontypes.py b/venv/lib/python3.12/site-packages/cffi/commontypes.py new file mode 100644 index 0000000..d4dae35 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/commontypes.py @@ -0,0 +1,82 @@ +import sys +from . import model +from .error import FFIError + + +COMMON_TYPES = {} + +try: + # fetch "bool" and all simple Windows types + from _cffi_backend import _get_common_types + _get_common_types(COMMON_TYPES) +except ImportError: + pass + +COMMON_TYPES['FILE'] = model.unknown_type('FILE', '_IO_FILE') +COMMON_TYPES['bool'] = '_Bool' # in case we got ImportError above +COMMON_TYPES['float _Complex'] = '_cffi_float_complex_t' +COMMON_TYPES['double _Complex'] = '_cffi_double_complex_t' + +for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES: + if _type.endswith('_t'): + COMMON_TYPES[_type] = _type +del _type + +_CACHE = {} + +def resolve_common_type(parser, commontype): + try: + return _CACHE[commontype] + except KeyError: + cdecl = COMMON_TYPES.get(commontype, commontype) + if not isinstance(cdecl, str): + result, quals = cdecl, 0 # cdecl is already a BaseType + elif cdecl in model.PrimitiveType.ALL_PRIMITIVE_TYPES: + result, quals = model.PrimitiveType(cdecl), 0 + elif cdecl == 'set-unicode-needed': + raise FFIError("The Windows type %r is only available after " + "you call ffi.set_unicode()" % (commontype,)) + else: + if commontype == cdecl: + raise FFIError( + "Unsupported type: %r. Please look at " + "http://cffi.readthedocs.io/en/latest/cdef.html#ffi-cdef-limitations " + "and file an issue if you think this type should really " + "be supported." % (commontype,)) + result, quals = parser.parse_type_and_quals(cdecl) # recursive + + assert isinstance(result, model.BaseTypeByIdentity) + _CACHE[commontype] = result, quals + return result, quals + + +# ____________________________________________________________ +# extra types for Windows (most of them are in commontypes.c) + + +def win_common_types(): + return { + "UNICODE_STRING": model.StructType( + "_UNICODE_STRING", + ["Length", + "MaximumLength", + "Buffer"], + [model.PrimitiveType("unsigned short"), + model.PrimitiveType("unsigned short"), + model.PointerType(model.PrimitiveType("wchar_t"))], + [-1, -1, -1]), + "PUNICODE_STRING": "UNICODE_STRING *", + "PCUNICODE_STRING": "const UNICODE_STRING *", + + "TBYTE": "set-unicode-needed", + "TCHAR": "set-unicode-needed", + "LPCTSTR": "set-unicode-needed", + "PCTSTR": "set-unicode-needed", + "LPTSTR": "set-unicode-needed", + "PTSTR": "set-unicode-needed", + "PTBYTE": "set-unicode-needed", + "PTCHAR": "set-unicode-needed", + } + +if sys.platform == 'win32': + COMMON_TYPES.update(win_common_types()) diff --git a/venv/lib/python3.12/site-packages/cffi/cparser.py b/venv/lib/python3.12/site-packages/cffi/cparser.py new file mode 100644 index 0000000..1896b7e --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/cparser.py @@ -0,0 +1,1014 @@ +from . import model +from .commontypes import COMMON_TYPES, resolve_common_type +from .error import FFIError, CDefError +try: + from . import _pycparser as pycparser +except ImportError: + import pycparser +import weakref, re, sys + +try: + if sys.version_info < (3,): + import thread as _thread + else: + import _thread + lock = _thread.allocate_lock() +except ImportError: + lock = None + +def _workaround_for_static_import_finders(): + # Issue #392: packaging tools like cx_Freeze can not find these + # because pycparser uses exec dynamic import. This is an obscure + # workaround. This function is never called. + import pycparser.yacctab + import pycparser.lextab + +CDEF_SOURCE_STRING = "" +_r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$", + re.DOTALL | re.MULTILINE) +_r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)" + r"\b((?:[^\n\\]|\\.)*?)$", + re.DOTALL | re.MULTILINE) +_r_line_directive = re.compile(r"^[ \t]*#[ \t]*(?:line|\d+)\b.*$", re.MULTILINE) +_r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}") +_r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$") +_r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]") +_r_words = re.compile(r"\w+|\S") +_parser_cache = None +_r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE) +_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b") +_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b") +_r_cdecl = re.compile(r"\b__cdecl\b") +_r_extern_python = re.compile(r'\bextern\s*"' + r'(Python|Python\s*\+\s*C|C\s*\+\s*Python)"\s*.') +_r_star_const_space = re.compile( # matches "* const " + r"[*]\s*((const|volatile|restrict)\b\s*)+") +_r_int_dotdotdot = re.compile(r"(\b(int|long|short|signed|unsigned|char)\s*)+" + r"\.\.\.") +_r_float_dotdotdot = re.compile(r"\b(double|float)\s*\.\.\.") + +def _get_parser(): + global _parser_cache + if _parser_cache is None: + _parser_cache = pycparser.CParser() + return _parser_cache + +def _workaround_for_old_pycparser(csource): + # Workaround for a pycparser issue (fixed between pycparser 2.10 and + # 2.14): "char*const***" gives us a wrong syntax tree, the same as + # for "char***(*const)". This means we can't tell the difference + # afterwards. But "char(*const(***))" gives us the right syntax + # tree. The issue only occurs if there are several stars in + # sequence with no parenthesis in between, just possibly qualifiers. + # Attempt to fix it by adding some parentheses in the source: each + # time we see "* const" or "* const *", we add an opening + # parenthesis before each star---the hard part is figuring out where + # to close them. + parts = [] + while True: + match = _r_star_const_space.search(csource) + if not match: + break + #print repr(''.join(parts)+csource), '=>', + parts.append(csource[:match.start()]) + parts.append('('); closing = ')' + parts.append(match.group()) # e.g. "* const " + endpos = match.end() + if csource.startswith('*', endpos): + parts.append('('); closing += ')' + level = 0 + i = endpos + while i < len(csource): + c = csource[i] + if c == '(': + level += 1 + elif c == ')': + if level == 0: + break + level -= 1 + elif c in ',;=': + if level == 0: + break + i += 1 + csource = csource[endpos:i] + closing + csource[i:] + #print repr(''.join(parts)+csource) + parts.append(csource) + return ''.join(parts) + +def _preprocess_extern_python(csource): + # input: `extern "Python" int foo(int);` or + # `extern "Python" { int foo(int); }` + # output: + # void __cffi_extern_python_start; + # int foo(int); + # void __cffi_extern_python_stop; + # + # input: `extern "Python+C" int foo(int);` + # output: + # void __cffi_extern_python_plus_c_start; + # int foo(int); + # void __cffi_extern_python_stop; + parts = [] + while True: + match = _r_extern_python.search(csource) + if not match: + break + endpos = match.end() - 1 + #print + #print ''.join(parts)+csource + #print '=>' + parts.append(csource[:match.start()]) + if 'C' in match.group(1): + parts.append('void __cffi_extern_python_plus_c_start; ') + else: + parts.append('void __cffi_extern_python_start; ') + if csource[endpos] == '{': + # grouping variant + closing = csource.find('}', endpos) + if closing < 0: + raise CDefError("'extern \"Python\" {': no '}' found") + if csource.find('{', endpos + 1, closing) >= 0: + raise NotImplementedError("cannot use { } inside a block " + "'extern \"Python\" { ... }'") + parts.append(csource[endpos+1:closing]) + csource = csource[closing+1:] + else: + # non-grouping variant + semicolon = csource.find(';', endpos) + if semicolon < 0: + raise CDefError("'extern \"Python\": no ';' found") + parts.append(csource[endpos:semicolon+1]) + csource = csource[semicolon+1:] + parts.append(' void __cffi_extern_python_stop;') + #print ''.join(parts)+csource + #print + parts.append(csource) + return ''.join(parts) + +def _warn_for_string_literal(csource): + if '"' not in csource: + return + for line in csource.splitlines(): + if '"' in line and not line.lstrip().startswith('#'): + import warnings + warnings.warn("String literal found in cdef() or type source. " + "String literals are ignored here, but you should " + "remove them anyway because some character sequences " + "confuse pre-parsing.") + break + +def _warn_for_non_extern_non_static_global_variable(decl): + if not decl.storage: + import warnings + warnings.warn("Global variable '%s' in cdef(): for consistency " + "with C it should have a storage class specifier " + "(usually 'extern')" % (decl.name,)) + +def _remove_line_directives(csource): + # _r_line_directive matches whole lines, without the final \n, if they + # start with '#line' with some spacing allowed, or '#NUMBER'. This + # function stores them away and replaces them with exactly the string + # '#line@N', where N is the index in the list 'line_directives'. + line_directives = [] + def replace(m): + i = len(line_directives) + line_directives.append(m.group()) + return '#line@%d' % i + csource = _r_line_directive.sub(replace, csource) + return csource, line_directives + +def _put_back_line_directives(csource, line_directives): + def replace(m): + s = m.group() + if not s.startswith('#line@'): + raise AssertionError("unexpected #line directive " + "(should have been processed and removed") + return line_directives[int(s[6:])] + return _r_line_directive.sub(replace, csource) + +def _preprocess(csource): + # First, remove the lines of the form '#line N "filename"' because + # the "filename" part could confuse the rest + csource, line_directives = _remove_line_directives(csource) + # Remove comments. NOTE: this only work because the cdef() section + # should not contain any string literals (except in line directives)! + def replace_keeping_newlines(m): + return ' ' + m.group().count('\n') * '\n' + csource = _r_comment.sub(replace_keeping_newlines, csource) + # Remove the "#define FOO x" lines + macros = {} + for match in _r_define.finditer(csource): + macroname, macrovalue = match.groups() + macrovalue = macrovalue.replace('\\\n', '').strip() + macros[macroname] = macrovalue + csource = _r_define.sub('', csource) + # + if pycparser.__version__ < '2.14': + csource = _workaround_for_old_pycparser(csource) + # + # BIG HACK: replace WINAPI or __stdcall with "volatile const". + # It doesn't make sense for the return type of a function to be + # "volatile volatile const", so we abuse it to detect __stdcall... + # Hack number 2 is that "int(volatile *fptr)();" is not valid C + # syntax, so we place the "volatile" before the opening parenthesis. + csource = _r_stdcall2.sub(' volatile volatile const(', csource) + csource = _r_stdcall1.sub(' volatile volatile const ', csource) + csource = _r_cdecl.sub(' ', csource) + # + # Replace `extern "Python"` with start/end markers + csource = _preprocess_extern_python(csource) + # + # Now there should not be any string literal left; warn if we get one + _warn_for_string_literal(csource) + # + # Replace "[...]" with "[__dotdotdotarray__]" + csource = _r_partial_array.sub('[__dotdotdotarray__]', csource) + # + # Replace "...}" with "__dotdotdotNUM__}". This construction should + # occur only at the end of enums; at the end of structs we have "...;}" + # and at the end of vararg functions "...);". Also replace "=...[,}]" + # with ",__dotdotdotNUM__[,}]": this occurs in the enums too, when + # giving an unknown value. + matches = list(_r_partial_enum.finditer(csource)) + for number, match in enumerate(reversed(matches)): + p = match.start() + if csource[p] == '=': + p2 = csource.find('...', p, match.end()) + assert p2 > p + csource = '%s,__dotdotdot%d__ %s' % (csource[:p], number, + csource[p2+3:]) + else: + assert csource[p:p+3] == '...' + csource = '%s __dotdotdot%d__ %s' % (csource[:p], number, + csource[p+3:]) + # Replace "int ..." or "unsigned long int..." with "__dotdotdotint__" + csource = _r_int_dotdotdot.sub(' __dotdotdotint__ ', csource) + # Replace "float ..." or "double..." with "__dotdotdotfloat__" + csource = _r_float_dotdotdot.sub(' __dotdotdotfloat__ ', csource) + # Replace all remaining "..." with the same name, "__dotdotdot__", + # which is declared with a typedef for the purpose of C parsing. + csource = csource.replace('...', ' __dotdotdot__ ') + # Finally, put back the line directives + csource = _put_back_line_directives(csource, line_directives) + return csource, macros + +def _common_type_names(csource): + # Look in the source for what looks like usages of types from the + # list of common types. A "usage" is approximated here as the + # appearance of the word, minus a "definition" of the type, which + # is the last word in a "typedef" statement. Approximative only + # but should be fine for all the common types. + look_for_words = set(COMMON_TYPES) + look_for_words.add(';') + look_for_words.add(',') + look_for_words.add('(') + look_for_words.add(')') + look_for_words.add('typedef') + words_used = set() + is_typedef = False + paren = 0 + previous_word = '' + for word in _r_words.findall(csource): + if word in look_for_words: + if word == ';': + if is_typedef: + words_used.discard(previous_word) + look_for_words.discard(previous_word) + is_typedef = False + elif word == 'typedef': + is_typedef = True + paren = 0 + elif word == '(': + paren += 1 + elif word == ')': + paren -= 1 + elif word == ',': + if is_typedef and paren == 0: + words_used.discard(previous_word) + look_for_words.discard(previous_word) + else: # word in COMMON_TYPES + words_used.add(word) + previous_word = word + return words_used + + +class Parser: + + def __init__(self): + self._declarations = {} + self._included_declarations = set() + self._anonymous_counter = 0 + self._structnode2type = weakref.WeakKeyDictionary() + self._options = {} + self._int_constants = {} + self._recomplete = [] + self._uses_new_feature = None + + def _parse(self, csource): + csource, macros = _preprocess(csource) + # XXX: for more efficiency we would need to poke into the + # internals of CParser... the following registers the + # typedefs, because their presence or absence influences the + # parsing itself (but what they are typedef'ed to plays no role) + ctn = _common_type_names(csource) + typenames = [] + for name in sorted(self._declarations): + if name.startswith('typedef '): + name = name[8:] + typenames.append(name) + ctn.discard(name) + typenames += sorted(ctn) + # + csourcelines = [] + csourcelines.append('# 1 ""') + for typename in typenames: + csourcelines.append('typedef int %s;' % typename) + csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,' + ' __dotdotdot__;') + # this forces pycparser to consider the following in the file + # called from line 1 + csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,)) + csourcelines.append(csource) + csourcelines.append('') # see test_missing_newline_bug + fullcsource = '\n'.join(csourcelines) + if lock is not None: + lock.acquire() # pycparser is not thread-safe... + try: + ast = _get_parser().parse(fullcsource) + except pycparser.c_parser.ParseError as e: + self.convert_pycparser_error(e, csource) + finally: + if lock is not None: + lock.release() + # csource will be used to find buggy source text + return ast, macros, csource + + def _convert_pycparser_error(self, e, csource): + # xxx look for ":NUM:" at the start of str(e) + # and interpret that as a line number. This will not work if + # the user gives explicit ``# NUM "FILE"`` directives. + line = None + msg = str(e) + match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg) + if match: + linenum = int(match.group(1), 10) + csourcelines = csource.splitlines() + if 1 <= linenum <= len(csourcelines): + line = csourcelines[linenum-1] + return line + + def convert_pycparser_error(self, e, csource): + line = self._convert_pycparser_error(e, csource) + + msg = str(e) + if line: + msg = 'cannot parse "%s"\n%s' % (line.strip(), msg) + else: + msg = 'parse error\n%s' % (msg,) + raise CDefError(msg) + + def parse(self, csource, override=False, packed=False, pack=None, + dllexport=False): + if packed: + if packed != True: + raise ValueError("'packed' should be False or True; use " + "'pack' to give another value") + if pack: + raise ValueError("cannot give both 'pack' and 'packed'") + pack = 1 + elif pack: + if pack & (pack - 1): + raise ValueError("'pack' must be a power of two, not %r" % + (pack,)) + else: + pack = 0 + prev_options = self._options + try: + self._options = {'override': override, + 'packed': pack, + 'dllexport': dllexport} + self._internal_parse(csource) + finally: + self._options = prev_options + + def _internal_parse(self, csource): + ast, macros, csource = self._parse(csource) + # add the macros + self._process_macros(macros) + # find the first "__dotdotdot__" and use that as a separator + # between the repeated typedefs and the real csource + iterator = iter(ast.ext) + for decl in iterator: + if decl.name == '__dotdotdot__': + break + else: + assert 0 + current_decl = None + # + try: + self._inside_extern_python = '__cffi_extern_python_stop' + for decl in iterator: + current_decl = decl + if isinstance(decl, pycparser.c_ast.Decl): + self._parse_decl(decl) + elif isinstance(decl, pycparser.c_ast.Typedef): + if not decl.name: + raise CDefError("typedef does not declare any name", + decl) + quals = 0 + if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and + decl.type.type.names[-1].startswith('__dotdotdot')): + realtype = self._get_unknown_type(decl) + elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and + isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and + isinstance(decl.type.type.type, + pycparser.c_ast.IdentifierType) and + decl.type.type.type.names[-1].startswith('__dotdotdot')): + realtype = self._get_unknown_ptr_type(decl) + else: + realtype, quals = self._get_type_and_quals( + decl.type, name=decl.name, partial_length_ok=True, + typedef_example="*(%s *)0" % (decl.name,)) + self._declare('typedef ' + decl.name, realtype, quals=quals) + elif decl.__class__.__name__ == 'Pragma': + # skip pragma, only in pycparser 2.15 + import warnings + warnings.warn( + "#pragma in cdef() are entirely ignored. " + "They should be removed for now, otherwise your " + "code might behave differently in a future version " + "of CFFI if #pragma support gets added. Note that " + "'#pragma pack' needs to be replaced with the " + "'packed' keyword argument to cdef().") + else: + raise CDefError("unexpected <%s>: this construct is valid " + "C but not valid in cdef()" % + decl.__class__.__name__, decl) + except CDefError as e: + if len(e.args) == 1: + e.args = e.args + (current_decl,) + raise + except FFIError as e: + msg = self._convert_pycparser_error(e, csource) + if msg: + e.args = (e.args[0] + "\n *** Err: %s" % msg,) + raise + + def _add_constants(self, key, val): + if key in self._int_constants: + if self._int_constants[key] == val: + return # ignore identical double declarations + raise FFIError( + "multiple declarations of constant: %s" % (key,)) + self._int_constants[key] = val + + def _add_integer_constant(self, name, int_str): + int_str = int_str.lower().rstrip("ul") + neg = int_str.startswith('-') + if neg: + int_str = int_str[1:] + # "010" is not valid oct in py3 + if (int_str.startswith("0") and int_str != '0' + and not int_str.startswith("0x")): + int_str = "0o" + int_str[1:] + pyvalue = int(int_str, 0) + if neg: + pyvalue = -pyvalue + self._add_constants(name, pyvalue) + self._declare('macro ' + name, pyvalue) + + def _process_macros(self, macros): + for key, value in macros.items(): + value = value.strip() + if _r_int_literal.match(value): + self._add_integer_constant(key, value) + elif value == '...': + self._declare('macro ' + key, value) + else: + raise CDefError( + 'only supports one of the following syntax:\n' + ' #define %s ... (literally dot-dot-dot)\n' + ' #define %s NUMBER (with NUMBER an integer' + ' constant, decimal/hex/octal)\n' + 'got:\n' + ' #define %s %s' + % (key, key, key, value)) + + def _declare_function(self, tp, quals, decl): + tp = self._get_type_pointer(tp, quals) + if self._options.get('dllexport'): + tag = 'dllexport_python ' + elif self._inside_extern_python == '__cffi_extern_python_start': + tag = 'extern_python ' + elif self._inside_extern_python == '__cffi_extern_python_plus_c_start': + tag = 'extern_python_plus_c ' + else: + tag = 'function ' + self._declare(tag + decl.name, tp) + + def _parse_decl(self, decl): + node = decl.type + if isinstance(node, pycparser.c_ast.FuncDecl): + tp, quals = self._get_type_and_quals(node, name=decl.name) + assert isinstance(tp, model.RawFunctionType) + self._declare_function(tp, quals, decl) + else: + if isinstance(node, pycparser.c_ast.Struct): + self._get_struct_union_enum_type('struct', node) + elif isinstance(node, pycparser.c_ast.Union): + self._get_struct_union_enum_type('union', node) + elif isinstance(node, pycparser.c_ast.Enum): + self._get_struct_union_enum_type('enum', node) + elif not decl.name: + raise CDefError("construct does not declare any variable", + decl) + # + if decl.name: + tp, quals = self._get_type_and_quals(node, + partial_length_ok=True) + if tp.is_raw_function: + self._declare_function(tp, quals, decl) + elif (tp.is_integer_type() and + hasattr(decl, 'init') and + hasattr(decl.init, 'value') and + _r_int_literal.match(decl.init.value)): + self._add_integer_constant(decl.name, decl.init.value) + elif (tp.is_integer_type() and + isinstance(decl.init, pycparser.c_ast.UnaryOp) and + decl.init.op == '-' and + hasattr(decl.init.expr, 'value') and + _r_int_literal.match(decl.init.expr.value)): + self._add_integer_constant(decl.name, + '-' + decl.init.expr.value) + elif (tp is model.void_type and + decl.name.startswith('__cffi_extern_python_')): + # hack: `extern "Python"` in the C source is replaced + # with "void __cffi_extern_python_start;" and + # "void __cffi_extern_python_stop;" + self._inside_extern_python = decl.name + else: + if self._inside_extern_python !='__cffi_extern_python_stop': + raise CDefError( + "cannot declare constants or " + "variables with 'extern \"Python\"'") + if (quals & model.Q_CONST) and not tp.is_array_type: + self._declare('constant ' + decl.name, tp, quals=quals) + else: + _warn_for_non_extern_non_static_global_variable(decl) + self._declare('variable ' + decl.name, tp, quals=quals) + + def parse_type(self, cdecl): + return self.parse_type_and_quals(cdecl)[0] + + def parse_type_and_quals(self, cdecl): + ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2] + assert not macros + exprnode = ast.ext[-1].type.args.params[0] + if isinstance(exprnode, pycparser.c_ast.ID): + raise CDefError("unknown identifier '%s'" % (exprnode.name,)) + return self._get_type_and_quals(exprnode.type) + + def _declare(self, name, obj, included=False, quals=0): + if name in self._declarations: + prevobj, prevquals = self._declarations[name] + if prevobj is obj and prevquals == quals: + return + if not self._options.get('override'): + raise FFIError( + "multiple declarations of %s (for interactive usage, " + "try cdef(xx, override=True))" % (name,)) + assert '__dotdotdot__' not in name.split() + self._declarations[name] = (obj, quals) + if included: + self._included_declarations.add(obj) + + def _extract_quals(self, type): + quals = 0 + if isinstance(type, (pycparser.c_ast.TypeDecl, + pycparser.c_ast.PtrDecl)): + if 'const' in type.quals: + quals |= model.Q_CONST + if 'volatile' in type.quals: + quals |= model.Q_VOLATILE + if 'restrict' in type.quals: + quals |= model.Q_RESTRICT + return quals + + def _get_type_pointer(self, type, quals, declname=None): + if isinstance(type, model.RawFunctionType): + return type.as_function_pointer() + if (isinstance(type, model.StructOrUnionOrEnum) and + type.name.startswith('$') and type.name[1:].isdigit() and + type.forcename is None and declname is not None): + return model.NamedPointerType(type, declname, quals) + return model.PointerType(type, quals) + + def _get_type_and_quals(self, typenode, name=None, partial_length_ok=False, + typedef_example=None): + # first, dereference typedefs, if we have it already parsed, we're good + if (isinstance(typenode, pycparser.c_ast.TypeDecl) and + isinstance(typenode.type, pycparser.c_ast.IdentifierType) and + len(typenode.type.names) == 1 and + ('typedef ' + typenode.type.names[0]) in self._declarations): + tp, quals = self._declarations['typedef ' + typenode.type.names[0]] + quals |= self._extract_quals(typenode) + return tp, quals + # + if isinstance(typenode, pycparser.c_ast.ArrayDecl): + # array type + if typenode.dim is None: + length = None + else: + length = self._parse_constant( + typenode.dim, partial_length_ok=partial_length_ok) + # a hack: in 'typedef int foo_t[...][...];', don't use '...' as + # the length but use directly the C expression that would be + # generated by recompiler.py. This lets the typedef be used in + # many more places within recompiler.py + if typedef_example is not None: + if length == '...': + length = '_cffi_array_len(%s)' % (typedef_example,) + typedef_example = "*" + typedef_example + # + tp, quals = self._get_type_and_quals(typenode.type, + partial_length_ok=partial_length_ok, + typedef_example=typedef_example) + return model.ArrayType(tp, length), quals + # + if isinstance(typenode, pycparser.c_ast.PtrDecl): + # pointer type + itemtype, itemquals = self._get_type_and_quals(typenode.type) + tp = self._get_type_pointer(itemtype, itemquals, declname=name) + quals = self._extract_quals(typenode) + return tp, quals + # + if isinstance(typenode, pycparser.c_ast.TypeDecl): + quals = self._extract_quals(typenode) + type = typenode.type + if isinstance(type, pycparser.c_ast.IdentifierType): + # assume a primitive type. get it from .names, but reduce + # synonyms to a single chosen combination + names = list(type.names) + if names != ['signed', 'char']: # keep this unmodified + prefixes = {} + while names: + name = names[0] + if name in ('short', 'long', 'signed', 'unsigned'): + prefixes[name] = prefixes.get(name, 0) + 1 + del names[0] + else: + break + # ignore the 'signed' prefix below, and reorder the others + newnames = [] + for prefix in ('unsigned', 'short', 'long'): + for i in range(prefixes.get(prefix, 0)): + newnames.append(prefix) + if not names: + names = ['int'] # implicitly + if names == ['int']: # but kill it if 'short' or 'long' + if 'short' in prefixes or 'long' in prefixes: + names = [] + names = newnames + names + ident = ' '.join(names) + if ident == 'void': + return model.void_type, quals + if ident == '__dotdotdot__': + raise FFIError(':%d: bad usage of "..."' % + typenode.coord.line) + tp0, quals0 = resolve_common_type(self, ident) + return tp0, (quals | quals0) + # + if isinstance(type, pycparser.c_ast.Struct): + # 'struct foobar' + tp = self._get_struct_union_enum_type('struct', type, name) + return tp, quals + # + if isinstance(type, pycparser.c_ast.Union): + # 'union foobar' + tp = self._get_struct_union_enum_type('union', type, name) + return tp, quals + # + if isinstance(type, pycparser.c_ast.Enum): + # 'enum foobar' + tp = self._get_struct_union_enum_type('enum', type, name) + return tp, quals + # + if isinstance(typenode, pycparser.c_ast.FuncDecl): + # a function type + return self._parse_function_type(typenode, name), 0 + # + # nested anonymous structs or unions end up here + if isinstance(typenode, pycparser.c_ast.Struct): + return self._get_struct_union_enum_type('struct', typenode, name, + nested=True), 0 + if isinstance(typenode, pycparser.c_ast.Union): + return self._get_struct_union_enum_type('union', typenode, name, + nested=True), 0 + # + raise FFIError(":%d: bad or unsupported type declaration" % + typenode.coord.line) + + def _parse_function_type(self, typenode, funcname=None): + params = list(getattr(typenode.args, 'params', [])) + for i, arg in enumerate(params): + if not hasattr(arg, 'type'): + raise CDefError("%s arg %d: unknown type '%s'" + " (if you meant to use the old C syntax of giving" + " untyped arguments, it is not supported)" + % (funcname or 'in expression', i + 1, + getattr(arg, 'name', '?'))) + ellipsis = ( + len(params) > 0 and + isinstance(params[-1].type, pycparser.c_ast.TypeDecl) and + isinstance(params[-1].type.type, + pycparser.c_ast.IdentifierType) and + params[-1].type.type.names == ['__dotdotdot__']) + if ellipsis: + params.pop() + if not params: + raise CDefError( + "%s: a function with only '(...)' as argument" + " is not correct C" % (funcname or 'in expression')) + args = [self._as_func_arg(*self._get_type_and_quals(argdeclnode.type)) + for argdeclnode in params] + if not ellipsis and args == [model.void_type]: + args = [] + result, quals = self._get_type_and_quals(typenode.type) + # the 'quals' on the result type are ignored. HACK: we absure them + # to detect __stdcall functions: we textually replace "__stdcall" + # with "volatile volatile const" above. + abi = None + if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway + if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']: + abi = '__stdcall' + return model.RawFunctionType(tuple(args), result, ellipsis, abi) + + def _as_func_arg(self, type, quals): + if isinstance(type, model.ArrayType): + return model.PointerType(type.item, quals) + elif isinstance(type, model.RawFunctionType): + return type.as_function_pointer() + else: + return type + + def _get_struct_union_enum_type(self, kind, type, name=None, nested=False): + # First, a level of caching on the exact 'type' node of the AST. + # This is obscure, but needed because pycparser "unrolls" declarations + # such as "typedef struct { } foo_t, *foo_p" and we end up with + # an AST that is not a tree, but a DAG, with the "type" node of the + # two branches foo_t and foo_p of the trees being the same node. + # It's a bit silly but detecting "DAG-ness" in the AST tree seems + # to be the only way to distinguish this case from two independent + # structs. See test_struct_with_two_usages. + try: + return self._structnode2type[type] + except KeyError: + pass + # + # Note that this must handle parsing "struct foo" any number of + # times and always return the same StructType object. Additionally, + # one of these times (not necessarily the first), the fields of + # the struct can be specified with "struct foo { ...fields... }". + # If no name is given, then we have to create a new anonymous struct + # with no caching; in this case, the fields are either specified + # right now or never. + # + force_name = name + name = type.name + # + # get the type or create it if needed + if name is None: + # 'force_name' is used to guess a more readable name for + # anonymous structs, for the common case "typedef struct { } foo". + if force_name is not None: + explicit_name = '$%s' % force_name + else: + self._anonymous_counter += 1 + explicit_name = '$%d' % self._anonymous_counter + tp = None + else: + explicit_name = name + key = '%s %s' % (kind, name) + tp, _ = self._declarations.get(key, (None, None)) + # + if tp is None: + if kind == 'struct': + tp = model.StructType(explicit_name, None, None, None) + elif kind == 'union': + tp = model.UnionType(explicit_name, None, None, None) + elif kind == 'enum': + if explicit_name == '__dotdotdot__': + raise CDefError("Enums cannot be declared with ...") + tp = self._build_enum_type(explicit_name, type.values) + else: + raise AssertionError("kind = %r" % (kind,)) + if name is not None: + self._declare(key, tp) + elif kind == 'enum' and type.values is not None: + raise NotImplementedError( + "enum %s: the '{}' declaration should appear on the first " + "time the enum is mentioned, not later" % explicit_name) + if not tp.forcename: + tp.force_the_name(force_name) + if tp.forcename and '$' in tp.name: + self._declare('anonymous %s' % tp.forcename, tp) + # + self._structnode2type[type] = tp + # + # enums: done here + if kind == 'enum': + return tp + # + # is there a 'type.decls'? If yes, then this is the place in the + # C sources that declare the fields. If no, then just return the + # existing type, possibly still incomplete. + if type.decls is None: + return tp + # + if tp.fldnames is not None: + raise CDefError("duplicate declaration of struct %s" % name) + fldnames = [] + fldtypes = [] + fldbitsize = [] + fldquals = [] + for decl in type.decls: + if (isinstance(decl.type, pycparser.c_ast.IdentifierType) and + ''.join(decl.type.names) == '__dotdotdot__'): + # XXX pycparser is inconsistent: 'names' should be a list + # of strings, but is sometimes just one string. Use + # str.join() as a way to cope with both. + self._make_partial(tp, nested) + continue + if decl.bitsize is None: + bitsize = -1 + else: + bitsize = self._parse_constant(decl.bitsize) + self._partial_length = False + type, fqual = self._get_type_and_quals(decl.type, + partial_length_ok=True) + if self._partial_length: + self._make_partial(tp, nested) + if isinstance(type, model.StructType) and type.partial: + self._make_partial(tp, nested) + fldnames.append(decl.name or '') + fldtypes.append(type) + fldbitsize.append(bitsize) + fldquals.append(fqual) + tp.fldnames = tuple(fldnames) + tp.fldtypes = tuple(fldtypes) + tp.fldbitsize = tuple(fldbitsize) + tp.fldquals = tuple(fldquals) + if fldbitsize != [-1] * len(fldbitsize): + if isinstance(tp, model.StructType) and tp.partial: + raise NotImplementedError("%s: using both bitfields and '...;'" + % (tp,)) + tp.packed = self._options.get('packed') + if tp.completed: # must be re-completed: it is not opaque any more + tp.completed = 0 + self._recomplete.append(tp) + return tp + + def _make_partial(self, tp, nested): + if not isinstance(tp, model.StructOrUnion): + raise CDefError("%s cannot be partial" % (tp,)) + if not tp.has_c_name() and not nested: + raise NotImplementedError("%s is partial but has no C name" %(tp,)) + tp.partial = True + + def _parse_constant(self, exprnode, partial_length_ok=False): + # for now, limited to expressions that are an immediate number + # or positive/negative number + if isinstance(exprnode, pycparser.c_ast.Constant): + s = exprnode.value + if '0' <= s[0] <= '9': + s = s.rstrip('uUlL') + try: + if s.startswith('0'): + return int(s, 8) + else: + return int(s, 10) + except ValueError: + if len(s) > 1: + if s.lower()[0:2] == '0x': + return int(s, 16) + elif s.lower()[0:2] == '0b': + return int(s, 2) + raise CDefError("invalid constant %r" % (s,)) + elif s[0] == "'" and s[-1] == "'" and ( + len(s) == 3 or (len(s) == 4 and s[1] == "\\")): + return ord(s[-2]) + else: + raise CDefError("invalid constant %r" % (s,)) + # + if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and + exprnode.op == '+'): + return self._parse_constant(exprnode.expr) + # + if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and + exprnode.op == '-'): + return -self._parse_constant(exprnode.expr) + # load previously defined int constant + if (isinstance(exprnode, pycparser.c_ast.ID) and + exprnode.name in self._int_constants): + return self._int_constants[exprnode.name] + # + if (isinstance(exprnode, pycparser.c_ast.ID) and + exprnode.name == '__dotdotdotarray__'): + if partial_length_ok: + self._partial_length = True + return '...' + raise FFIError(":%d: unsupported '[...]' here, cannot derive " + "the actual array length in this context" + % exprnode.coord.line) + # + if isinstance(exprnode, pycparser.c_ast.BinaryOp): + left = self._parse_constant(exprnode.left) + right = self._parse_constant(exprnode.right) + if exprnode.op == '+': + return left + right + elif exprnode.op == '-': + return left - right + elif exprnode.op == '*': + return left * right + elif exprnode.op == '/': + return self._c_div(left, right) + elif exprnode.op == '%': + return left - self._c_div(left, right) * right + elif exprnode.op == '<<': + return left << right + elif exprnode.op == '>>': + return left >> right + elif exprnode.op == '&': + return left & right + elif exprnode.op == '|': + return left | right + elif exprnode.op == '^': + return left ^ right + # + raise FFIError(":%d: unsupported expression: expected a " + "simple numeric constant" % exprnode.coord.line) + + def _c_div(self, a, b): + result = a // b + if ((a < 0) ^ (b < 0)) and (a % b) != 0: + result += 1 + return result + + def _build_enum_type(self, explicit_name, decls): + if decls is not None: + partial = False + enumerators = [] + enumvalues = [] + nextenumvalue = 0 + for enum in decls.enumerators: + if _r_enum_dotdotdot.match(enum.name): + partial = True + continue + if enum.value is not None: + nextenumvalue = self._parse_constant(enum.value) + enumerators.append(enum.name) + enumvalues.append(nextenumvalue) + self._add_constants(enum.name, nextenumvalue) + nextenumvalue += 1 + enumerators = tuple(enumerators) + enumvalues = tuple(enumvalues) + tp = model.EnumType(explicit_name, enumerators, enumvalues) + tp.partial = partial + else: # opaque enum + tp = model.EnumType(explicit_name, (), ()) + return tp + + def include(self, other): + for name, (tp, quals) in other._declarations.items(): + if name.startswith('anonymous $enum_$'): + continue # fix for test_anonymous_enum_include + kind = name.split(' ', 1)[0] + if kind in ('struct', 'union', 'enum', 'anonymous', 'typedef'): + self._declare(name, tp, included=True, quals=quals) + for k, v in other._int_constants.items(): + self._add_constants(k, v) + + def _get_unknown_type(self, decl): + typenames = decl.type.type.names + if typenames == ['__dotdotdot__']: + return model.unknown_type(decl.name) + + if typenames == ['__dotdotdotint__']: + if self._uses_new_feature is None: + self._uses_new_feature = "'typedef int... %s'" % decl.name + return model.UnknownIntegerType(decl.name) + + if typenames == ['__dotdotdotfloat__']: + # note: not for 'long double' so far + if self._uses_new_feature is None: + self._uses_new_feature = "'typedef float... %s'" % decl.name + return model.UnknownFloatType(decl.name) + + raise FFIError(':%d: unsupported usage of "..." in typedef' + % decl.coord.line) + + def _get_unknown_ptr_type(self, decl): + if decl.type.type.type.names == ['__dotdotdot__']: + return model.unknown_ptr_type(decl.name) + raise FFIError(':%d: unsupported usage of "..." in typedef' + % decl.coord.line) diff --git a/venv/lib/python3.12/site-packages/cffi/error.py b/venv/lib/python3.12/site-packages/cffi/error.py new file mode 100644 index 0000000..0a27247 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/error.py @@ -0,0 +1,31 @@ + +class FFIError(Exception): + __module__ = 'cffi' + +class CDefError(Exception): + __module__ = 'cffi' + def __str__(self): + try: + current_decl = self.args[1] + filename = current_decl.coord.file + linenum = current_decl.coord.line + prefix = '%s:%d: ' % (filename, linenum) + except (AttributeError, TypeError, IndexError): + prefix = '' + return '%s%s' % (prefix, self.args[0]) + +class VerificationError(Exception): + """ An error raised when verification fails + """ + __module__ = 'cffi' + +class VerificationMissing(Exception): + """ An error raised when incomplete structures are passed into + cdef, but no verification has been done + """ + __module__ = 'cffi' + +class PkgConfigError(Exception): + """ An error raised for missing modules in pkg-config + """ + __module__ = 'cffi' diff --git a/venv/lib/python3.12/site-packages/cffi/ffiplatform.py b/venv/lib/python3.12/site-packages/cffi/ffiplatform.py new file mode 100644 index 0000000..adca28f --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/ffiplatform.py @@ -0,0 +1,113 @@ +import sys, os +from .error import VerificationError + + +LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs', + 'extra_objects', 'depends'] + +def get_extension(srcfilename, modname, sources=(), **kwds): + from cffi._shimmed_dist_utils import Extension + allsources = [srcfilename] + for src in sources: + allsources.append(os.path.normpath(src)) + return Extension(name=modname, sources=allsources, **kwds) + +def compile(tmpdir, ext, compiler_verbose=0, debug=None): + """Compile a C extension module using distutils.""" + + saved_environ = os.environ.copy() + try: + outputfilename = _build(tmpdir, ext, compiler_verbose, debug) + outputfilename = os.path.abspath(outputfilename) + finally: + # workaround for a distutils bugs where some env vars can + # become longer and longer every time it is used + for key, value in saved_environ.items(): + if os.environ.get(key) != value: + os.environ[key] = value + return outputfilename + +def _build(tmpdir, ext, compiler_verbose=0, debug=None): + # XXX compact but horrible :-( + from cffi._shimmed_dist_utils import Distribution, CompileError, LinkError, set_threshold, set_verbosity + + dist = Distribution({'ext_modules': [ext]}) + dist.parse_config_files() + options = dist.get_option_dict('build_ext') + if debug is None: + debug = sys.flags.debug + options['debug'] = ('ffiplatform', debug) + options['force'] = ('ffiplatform', True) + options['build_lib'] = ('ffiplatform', tmpdir) + options['build_temp'] = ('ffiplatform', tmpdir) + # + try: + old_level = set_threshold(0) or 0 + try: + set_verbosity(compiler_verbose) + dist.run_command('build_ext') + cmd_obj = dist.get_command_obj('build_ext') + [soname] = cmd_obj.get_outputs() + finally: + set_threshold(old_level) + except (CompileError, LinkError) as e: + raise VerificationError('%s: %s' % (e.__class__.__name__, e)) + # + return soname + +try: + from os.path import samefile +except ImportError: + def samefile(f1, f2): + return os.path.abspath(f1) == os.path.abspath(f2) + +def maybe_relative_path(path): + if not os.path.isabs(path): + return path # already relative + dir = path + names = [] + while True: + prevdir = dir + dir, name = os.path.split(prevdir) + if dir == prevdir or not dir: + return path # failed to make it relative + names.append(name) + try: + if samefile(dir, os.curdir): + names.reverse() + return os.path.join(*names) + except OSError: + pass + +# ____________________________________________________________ + +try: + int_or_long = (int, long) + import cStringIO +except NameError: + int_or_long = int # Python 3 + import io as cStringIO + +def _flatten(x, f): + if isinstance(x, str): + f.write('%ds%s' % (len(x), x)) + elif isinstance(x, dict): + keys = sorted(x.keys()) + f.write('%dd' % len(keys)) + for key in keys: + _flatten(key, f) + _flatten(x[key], f) + elif isinstance(x, (list, tuple)): + f.write('%dl' % len(x)) + for value in x: + _flatten(value, f) + elif isinstance(x, int_or_long): + f.write('%di' % (x,)) + else: + raise TypeError( + "the keywords to verify() contains unsupported object %r" % (x,)) + +def flatten(x): + f = cStringIO.StringIO() + _flatten(x, f) + return f.getvalue() diff --git a/venv/lib/python3.12/site-packages/cffi/gen_src.py b/venv/lib/python3.12/site-packages/cffi/gen_src.py new file mode 100644 index 0000000..8eb57b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/gen_src.py @@ -0,0 +1,5 @@ +"""Entry point so ``python -m cffi.gen_src`` works.""" + +if __name__ == '__main__': + from cffi._cffi_gen_src import run + run() diff --git a/venv/lib/python3.12/site-packages/cffi/lock.py b/venv/lib/python3.12/site-packages/cffi/lock.py new file mode 100644 index 0000000..db91b71 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/lock.py @@ -0,0 +1,30 @@ +import sys + +if sys.version_info < (3,): + try: + from thread import allocate_lock + except ImportError: + from dummy_thread import allocate_lock +else: + try: + from _thread import allocate_lock + except ImportError: + from _dummy_thread import allocate_lock + + +##import sys +##l1 = allocate_lock + +##class allocate_lock(object): +## def __init__(self): +## self._real = l1() +## def __enter__(self): +## for i in range(4, 0, -1): +## print sys._getframe(i).f_code +## print +## return self._real.__enter__() +## def __exit__(self, *args): +## return self._real.__exit__(*args) +## def acquire(self, f): +## assert f is False +## return self._real.acquire(f) diff --git a/venv/lib/python3.12/site-packages/cffi/model.py b/venv/lib/python3.12/site-packages/cffi/model.py new file mode 100644 index 0000000..8bade1d --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/model.py @@ -0,0 +1,617 @@ +import types +import weakref + +from .lock import allocate_lock +from .error import CDefError, VerificationError, VerificationMissing + +# type qualifiers +Q_CONST = 0x01 +Q_RESTRICT = 0x02 +Q_VOLATILE = 0x04 + +def qualify(quals, replace_with): + if quals & Q_CONST: + replace_with = ' const ' + replace_with.lstrip() + if quals & Q_VOLATILE: + replace_with = ' volatile ' + replace_with.lstrip() + if quals & Q_RESTRICT: + # It seems that __restrict is supported by gcc and msvc. + # If you hit some different compiler, add a #define in + # _cffi_include.h for it (and in its copies, documented there) + replace_with = ' __restrict ' + replace_with.lstrip() + return replace_with + + +class BaseTypeByIdentity: + is_array_type = False + is_raw_function = False + + def get_c_name(self, replace_with='', context='a C file', quals=0): + result = self.c_name_with_marker + assert result.count('&') == 1 + # some logic duplication with ffi.getctype()... :-( + replace_with = replace_with.strip() + if replace_with: + if replace_with.startswith('*') and '&[' in result: + replace_with = '(%s)' % replace_with + elif replace_with[0] not in '[(': + replace_with = ' ' + replace_with + replace_with = qualify(quals, replace_with) + result = result.replace('&', replace_with) + if '$' in result: + raise VerificationError( + "cannot generate '%s' in %s: unknown type name" + % (self._get_c_name(), context)) + return result + + def _get_c_name(self): + return self.c_name_with_marker.replace('&', '') + + def has_c_name(self): + return '$' not in self._get_c_name() + + def is_integer_type(self): + return False + + def get_cached_btype(self, ffi, finishlist, can_delay=False): + try: + BType = ffi._cached_btypes[self] + except KeyError: + BType = self.build_backend_type(ffi, finishlist) + BType2 = ffi._cached_btypes.setdefault(self, BType) + assert BType2 is BType + return BType + + def __repr__(self): + return '<%s>' % (self._get_c_name(),) + + def _get_items(self): + return [(name, getattr(self, name)) for name in self._attrs_] + + +class BaseType(BaseTypeByIdentity): + + def __eq__(self, other): + return (self.__class__ == other.__class__ and + self._get_items() == other._get_items()) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.__class__, tuple(self._get_items()))) + + +class VoidType(BaseType): + _attrs_ = () + + def __init__(self): + self.c_name_with_marker = 'void&' + + def build_backend_type(self, ffi, finishlist): + return global_cache(self, ffi, 'new_void_type') + +void_type = VoidType() + + +class BasePrimitiveType(BaseType): + def is_complex_type(self): + return False + + +class PrimitiveType(BasePrimitiveType): + _attrs_ = ('name',) + + ALL_PRIMITIVE_TYPES = { + 'char': 'c', + 'short': 'i', + 'int': 'i', + 'long': 'i', + 'long long': 'i', + 'signed char': 'i', + 'unsigned char': 'i', + 'unsigned short': 'i', + 'unsigned int': 'i', + 'unsigned long': 'i', + 'unsigned long long': 'i', + 'float': 'f', + 'double': 'f', + 'long double': 'f', + '_cffi_float_complex_t': 'j', + '_cffi_double_complex_t': 'j', + '_Bool': 'i', + # the following types are not primitive in the C sense + 'wchar_t': 'c', + 'char16_t': 'c', + 'char32_t': 'c', + 'int8_t': 'i', + 'uint8_t': 'i', + 'int16_t': 'i', + 'uint16_t': 'i', + 'int32_t': 'i', + 'uint32_t': 'i', + 'int64_t': 'i', + 'uint64_t': 'i', + 'int_least8_t': 'i', + 'uint_least8_t': 'i', + 'int_least16_t': 'i', + 'uint_least16_t': 'i', + 'int_least32_t': 'i', + 'uint_least32_t': 'i', + 'int_least64_t': 'i', + 'uint_least64_t': 'i', + 'int_fast8_t': 'i', + 'uint_fast8_t': 'i', + 'int_fast16_t': 'i', + 'uint_fast16_t': 'i', + 'int_fast32_t': 'i', + 'uint_fast32_t': 'i', + 'int_fast64_t': 'i', + 'uint_fast64_t': 'i', + 'intptr_t': 'i', + 'uintptr_t': 'i', + 'intmax_t': 'i', + 'uintmax_t': 'i', + 'ptrdiff_t': 'i', + 'size_t': 'i', + 'ssize_t': 'i', + } + + def __init__(self, name): + assert name in self.ALL_PRIMITIVE_TYPES + self.name = name + self.c_name_with_marker = name + '&' + + def is_char_type(self): + return self.ALL_PRIMITIVE_TYPES[self.name] == 'c' + def is_integer_type(self): + return self.ALL_PRIMITIVE_TYPES[self.name] == 'i' + def is_float_type(self): + return self.ALL_PRIMITIVE_TYPES[self.name] == 'f' + def is_complex_type(self): + return self.ALL_PRIMITIVE_TYPES[self.name] == 'j' + + def build_backend_type(self, ffi, finishlist): + return global_cache(self, ffi, 'new_primitive_type', self.name) + + +class UnknownIntegerType(BasePrimitiveType): + _attrs_ = ('name',) + + def __init__(self, name): + self.name = name + self.c_name_with_marker = name + '&' + + def is_integer_type(self): + return True + + def build_backend_type(self, ffi, finishlist): + raise NotImplementedError("integer type '%s' can only be used after " + "compilation" % self.name) + +class UnknownFloatType(BasePrimitiveType): + _attrs_ = ('name', ) + + def __init__(self, name): + self.name = name + self.c_name_with_marker = name + '&' + + def build_backend_type(self, ffi, finishlist): + raise NotImplementedError("float type '%s' can only be used after " + "compilation" % self.name) + + +class BaseFunctionType(BaseType): + _attrs_ = ('args', 'result', 'ellipsis', 'abi') + + def __init__(self, args, result, ellipsis, abi=None): + self.args = args + self.result = result + self.ellipsis = ellipsis + self.abi = abi + # + reprargs = [arg._get_c_name() for arg in self.args] + if self.ellipsis: + reprargs.append('...') + reprargs = reprargs or ['void'] + replace_with = self._base_pattern % (', '.join(reprargs),) + if abi is not None: + replace_with = replace_with[:1] + abi + ' ' + replace_with[1:] + self.c_name_with_marker = ( + self.result.c_name_with_marker.replace('&', replace_with)) + + +class RawFunctionType(BaseFunctionType): + # Corresponds to a C type like 'int(int)', which is the C type of + # a function, but not a pointer-to-function. The backend has no + # notion of such a type; it's used temporarily by parsing. + _base_pattern = '(&)(%s)' + is_raw_function = True + + def build_backend_type(self, ffi, finishlist): + raise CDefError("cannot render the type %r: it is a function " + "type, not a pointer-to-function type" % (self,)) + + def as_function_pointer(self): + return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi) + + +class FunctionPtrType(BaseFunctionType): + _base_pattern = '(*&)(%s)' + + def build_backend_type(self, ffi, finishlist): + result = self.result.get_cached_btype(ffi, finishlist) + args = [] + for tp in self.args: + args.append(tp.get_cached_btype(ffi, finishlist)) + abi_args = () + if self.abi == "__stdcall": + if not self.ellipsis: # __stdcall ignored for variadic funcs + try: + abi_args = (ffi._backend.FFI_STDCALL,) + except AttributeError: + pass + return global_cache(self, ffi, 'new_function_type', + tuple(args), result, self.ellipsis, *abi_args) + + def as_raw_function(self): + return RawFunctionType(self.args, self.result, self.ellipsis, self.abi) + + +class PointerType(BaseType): + _attrs_ = ('totype', 'quals') + + def __init__(self, totype, quals=0): + self.totype = totype + self.quals = quals + extra = " *&" + if totype.is_array_type: + extra = "(%s)" % (extra.lstrip(),) + extra = qualify(quals, extra) + self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra) + + def build_backend_type(self, ffi, finishlist): + BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True) + return global_cache(self, ffi, 'new_pointer_type', BItem) + +voidp_type = PointerType(void_type) + +def ConstPointerType(totype): + return PointerType(totype, Q_CONST) + +const_voidp_type = ConstPointerType(void_type) + + +class NamedPointerType(PointerType): + _attrs_ = ('totype', 'name') + + def __init__(self, totype, name, quals=0): + PointerType.__init__(self, totype, quals) + self.name = name + self.c_name_with_marker = name + '&' + + +class ArrayType(BaseType): + _attrs_ = ('item', 'length') + is_array_type = True + + def __init__(self, item, length): + self.item = item + self.length = length + # + if length is None: + brackets = '&[]' + elif length == '...': + brackets = '&[/*...*/]' + else: + brackets = '&[%s]' % length + self.c_name_with_marker = ( + self.item.c_name_with_marker.replace('&', brackets)) + + def length_is_unknown(self): + return isinstance(self.length, str) + + def resolve_length(self, newlength): + return ArrayType(self.item, newlength) + + def build_backend_type(self, ffi, finishlist): + if self.length_is_unknown(): + raise CDefError("cannot render the type %r: unknown length" % + (self,)) + self.item.get_cached_btype(ffi, finishlist) # force the item BType + BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist) + return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length) + +char_array_type = ArrayType(PrimitiveType('char'), None) + + +class StructOrUnionOrEnum(BaseTypeByIdentity): + _attrs_ = ('name',) + forcename = None + + def build_c_name_with_marker(self): + name = self.forcename or '%s %s' % (self.kind, self.name) + self.c_name_with_marker = name + '&' + + def force_the_name(self, forcename): + self.forcename = forcename + self.build_c_name_with_marker() + + def get_official_name(self): + assert self.c_name_with_marker.endswith('&') + return self.c_name_with_marker[:-1] + + +class StructOrUnion(StructOrUnionOrEnum): + fixedlayout = None + completed = 0 + partial = False + packed = 0 + + def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None): + self.name = name + self.fldnames = fldnames + self.fldtypes = fldtypes + self.fldbitsize = fldbitsize + self.fldquals = fldquals + self.build_c_name_with_marker() + + def anonymous_struct_fields(self): + if self.fldtypes is not None: + for name, type in zip(self.fldnames, self.fldtypes): + if name == '' and isinstance(type, StructOrUnion): + yield type + + def enumfields(self, expand_anonymous_struct_union=True): + fldquals = self.fldquals + if fldquals is None: + fldquals = (0,) * len(self.fldnames) + for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes, + self.fldbitsize, fldquals): + if (name == '' and isinstance(type, StructOrUnion) + and expand_anonymous_struct_union): + # nested anonymous struct/union + yield from type.enumfields() + else: + yield (name, type, bitsize, quals) + + def force_flatten(self): + # force the struct or union to have a declaration that lists + # directly all fields returned by enumfields(), flattening + # nested anonymous structs/unions. + names = [] + types = [] + bitsizes = [] + fldquals = [] + for name, type, bitsize, quals in self.enumfields(): + names.append(name) + types.append(type) + bitsizes.append(bitsize) + fldquals.append(quals) + self.fldnames = tuple(names) + self.fldtypes = tuple(types) + self.fldbitsize = tuple(bitsizes) + self.fldquals = tuple(fldquals) + + def get_cached_btype(self, ffi, finishlist, can_delay=False): + BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist, + can_delay) + if not can_delay: + self.finish_backend_type(ffi, finishlist) + return BType + + def finish_backend_type(self, ffi, finishlist): + if self.completed: + if self.completed != 2: + raise NotImplementedError("recursive structure declaration " + "for '%s'" % (self.name,)) + return + BType = ffi._cached_btypes[self] + # + self.completed = 1 + # + if self.fldtypes is None: + pass # not completing it: it's an opaque struct + # + elif self.fixedlayout is None: + fldtypes = [tp.get_cached_btype(ffi, finishlist) + for tp in self.fldtypes] + lst = list(zip(self.fldnames, fldtypes, self.fldbitsize)) + extra_flags = () + if self.packed: + if self.packed == 1: + extra_flags = (8,) # SF_PACKED + else: + extra_flags = (0, self.packed) + ffi._backend.complete_struct_or_union(BType, lst, self, + -1, -1, *extra_flags) + # + else: + fldtypes = [] + fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout + for i in range(len(self.fldnames)): + fsize = fieldsize[i] + ftype = self.fldtypes[i] + # + if isinstance(ftype, ArrayType) and ftype.length_is_unknown(): + # fix the length to match the total size + BItemType = ftype.item.get_cached_btype(ffi, finishlist) + nlen, nrest = divmod(fsize, ffi.sizeof(BItemType)) + if nrest != 0: + self._verification_error( + "field '%s.%s' has a bogus size?" % ( + self.name, self.fldnames[i] or '{}')) + ftype = ftype.resolve_length(nlen) + self.fldtypes = (self.fldtypes[:i] + (ftype,) + + self.fldtypes[i+1:]) + # + BFieldType = ftype.get_cached_btype(ffi, finishlist) + if isinstance(ftype, ArrayType) and ftype.length is None: + assert fsize == 0 + else: + bitemsize = ffi.sizeof(BFieldType) + if bitemsize != fsize: + self._verification_error( + "field '%s.%s' is declared as %d bytes, but is " + "really %d bytes" % (self.name, + self.fldnames[i] or '{}', + bitemsize, fsize)) + fldtypes.append(BFieldType) + # + lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs)) + ffi._backend.complete_struct_or_union(BType, lst, self, + totalsize, totalalignment) + self.completed = 2 + + def _verification_error(self, msg): + raise VerificationError(msg) + + def check_not_partial(self): + if self.partial and self.fixedlayout is None: + raise VerificationMissing(self._get_c_name()) + + def build_backend_type(self, ffi, finishlist): + self.check_not_partial() + finishlist.append(self) + # + return global_cache(self, ffi, 'new_%s_type' % self.kind, + self.get_official_name(), key=self) + + +class StructType(StructOrUnion): + kind = 'struct' + + +class UnionType(StructOrUnion): + kind = 'union' + + +class EnumType(StructOrUnionOrEnum): + kind = 'enum' + partial = False + partial_resolved = False + + def __init__(self, name, enumerators, enumvalues, baseinttype=None): + self.name = name + self.enumerators = enumerators + self.enumvalues = enumvalues + self.baseinttype = baseinttype + self.build_c_name_with_marker() + + def force_the_name(self, forcename): + StructOrUnionOrEnum.force_the_name(self, forcename) + if self.forcename is None: + name = self.get_official_name() + self.forcename = '$' + name.replace(' ', '_') + + def check_not_partial(self): + if self.partial and not self.partial_resolved: + raise VerificationMissing(self._get_c_name()) + + def build_backend_type(self, ffi, finishlist): + self.check_not_partial() + base_btype = self.build_baseinttype(ffi, finishlist) + return global_cache(self, ffi, 'new_enum_type', + self.get_official_name(), + self.enumerators, self.enumvalues, + base_btype, key=self) + + def build_baseinttype(self, ffi, finishlist): + if self.baseinttype is not None: + return self.baseinttype.get_cached_btype(ffi, finishlist) + # + if self.enumvalues: + smallest_value = min(self.enumvalues) + largest_value = max(self.enumvalues) + else: + import warnings + try: + # XXX! The goal is to ensure that the warnings.warn() + # will not suppress the warning. We want to get it + # several times if we reach this point several times. + __warningregistry__.clear() + except NameError: + pass + warnings.warn("%r has no values explicitly defined; " + "guessing that it is equivalent to 'unsigned int'" + % self._get_c_name()) + smallest_value = largest_value = 0 + if smallest_value < 0: # needs a signed type + sign = 1 + candidate1 = PrimitiveType("int") + candidate2 = PrimitiveType("long") + else: + sign = 0 + candidate1 = PrimitiveType("unsigned int") + candidate2 = PrimitiveType("unsigned long") + btype1 = candidate1.get_cached_btype(ffi, finishlist) + btype2 = candidate2.get_cached_btype(ffi, finishlist) + size1 = ffi.sizeof(btype1) + size2 = ffi.sizeof(btype2) + if (smallest_value >= ((-1) << (8*size1-1)) and + largest_value < (1 << (8*size1-sign))): + return btype1 + if (smallest_value >= ((-1) << (8*size2-1)) and + largest_value < (1 << (8*size2-sign))): + return btype2 + raise CDefError("%s values don't all fit into either 'long' " + "or 'unsigned long'" % self._get_c_name()) + +def unknown_type(name, structname=None): + if structname is None: + structname = '$%s' % name + tp = StructType(structname, None, None, None) + tp.force_the_name(name) + tp.origin = "unknown_type" + return tp + +def unknown_ptr_type(name, structname=None): + if structname is None: + structname = '$$%s' % name + tp = StructType(structname, None, None, None) + return NamedPointerType(tp, name) + + +global_lock = allocate_lock() +_typecache_cffi_backend = weakref.WeakValueDictionary() + +def get_typecache(backend): + # returns _typecache_cffi_backend if backend is the _cffi_backend + # module, or type(backend).__typecache if backend is an instance of + # CTypesBackend (or some FakeBackend class during tests) + if isinstance(backend, types.ModuleType): + return _typecache_cffi_backend + with global_lock: + if not hasattr(type(backend), '__typecache'): + type(backend).__typecache = weakref.WeakValueDictionary() + return type(backend).__typecache + +def global_cache(srctype, ffi, funcname, *args, **kwds): + key = kwds.pop('key', (funcname, args)) + assert not kwds + try: + return ffi._typecache[key] + except KeyError: + pass + try: + res = getattr(ffi._backend, funcname)(*args) + except NotImplementedError as e: + raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e)) + # note that setdefault() on WeakValueDictionary is not atomic + # and contains a rare bug (http://bugs.python.org/issue19542); + # we have to use a lock and do it ourselves + cache = ffi._typecache + with global_lock: + res1 = cache.get(key) + if res1 is None: + cache[key] = res + return res + else: + return res1 + +def pointer_cache(ffi, BType): + return global_cache('?', ffi, 'new_pointer_type', BType) + +def attach_exception_info(e, name): + if e.args and type(e.args[0]) is str: + e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:] diff --git a/venv/lib/python3.12/site-packages/cffi/parse_c_type.h b/venv/lib/python3.12/site-packages/cffi/parse_c_type.h new file mode 100644 index 0000000..84e4ef8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/parse_c_type.h @@ -0,0 +1,181 @@ + +/* This part is from file 'cffi/parse_c_type.h'. It is copied at the + beginning of C sources generated by CFFI's ffi.set_source(). */ + +typedef void *_cffi_opcode_t; + +#define _CFFI_OP(opcode, arg) (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8)) +#define _CFFI_GETOP(cffi_opcode) ((unsigned char)(uintptr_t)cffi_opcode) +#define _CFFI_GETARG(cffi_opcode) (((intptr_t)cffi_opcode) >> 8) + +#define _CFFI_OP_PRIMITIVE 1 +#define _CFFI_OP_POINTER 3 +#define _CFFI_OP_ARRAY 5 +#define _CFFI_OP_OPEN_ARRAY 7 +#define _CFFI_OP_STRUCT_UNION 9 +#define _CFFI_OP_ENUM 11 +#define _CFFI_OP_FUNCTION 13 +#define _CFFI_OP_FUNCTION_END 15 +#define _CFFI_OP_NOOP 17 +#define _CFFI_OP_BITFIELD 19 +#define _CFFI_OP_TYPENAME 21 +#define _CFFI_OP_CPYTHON_BLTN_V 23 // varargs +#define _CFFI_OP_CPYTHON_BLTN_N 25 // noargs +#define _CFFI_OP_CPYTHON_BLTN_O 27 // O (i.e. a single arg) +#define _CFFI_OP_CONSTANT 29 +#define _CFFI_OP_CONSTANT_INT 31 +#define _CFFI_OP_GLOBAL_VAR 33 +#define _CFFI_OP_DLOPEN_FUNC 35 +#define _CFFI_OP_DLOPEN_CONST 37 +#define _CFFI_OP_GLOBAL_VAR_F 39 +#define _CFFI_OP_EXTERN_PYTHON 41 + +#define _CFFI_PRIM_VOID 0 +#define _CFFI_PRIM_BOOL 1 +#define _CFFI_PRIM_CHAR 2 +#define _CFFI_PRIM_SCHAR 3 +#define _CFFI_PRIM_UCHAR 4 +#define _CFFI_PRIM_SHORT 5 +#define _CFFI_PRIM_USHORT 6 +#define _CFFI_PRIM_INT 7 +#define _CFFI_PRIM_UINT 8 +#define _CFFI_PRIM_LONG 9 +#define _CFFI_PRIM_ULONG 10 +#define _CFFI_PRIM_LONGLONG 11 +#define _CFFI_PRIM_ULONGLONG 12 +#define _CFFI_PRIM_FLOAT 13 +#define _CFFI_PRIM_DOUBLE 14 +#define _CFFI_PRIM_LONGDOUBLE 15 + +#define _CFFI_PRIM_WCHAR 16 +#define _CFFI_PRIM_INT8 17 +#define _CFFI_PRIM_UINT8 18 +#define _CFFI_PRIM_INT16 19 +#define _CFFI_PRIM_UINT16 20 +#define _CFFI_PRIM_INT32 21 +#define _CFFI_PRIM_UINT32 22 +#define _CFFI_PRIM_INT64 23 +#define _CFFI_PRIM_UINT64 24 +#define _CFFI_PRIM_INTPTR 25 +#define _CFFI_PRIM_UINTPTR 26 +#define _CFFI_PRIM_PTRDIFF 27 +#define _CFFI_PRIM_SIZE 28 +#define _CFFI_PRIM_SSIZE 29 +#define _CFFI_PRIM_INT_LEAST8 30 +#define _CFFI_PRIM_UINT_LEAST8 31 +#define _CFFI_PRIM_INT_LEAST16 32 +#define _CFFI_PRIM_UINT_LEAST16 33 +#define _CFFI_PRIM_INT_LEAST32 34 +#define _CFFI_PRIM_UINT_LEAST32 35 +#define _CFFI_PRIM_INT_LEAST64 36 +#define _CFFI_PRIM_UINT_LEAST64 37 +#define _CFFI_PRIM_INT_FAST8 38 +#define _CFFI_PRIM_UINT_FAST8 39 +#define _CFFI_PRIM_INT_FAST16 40 +#define _CFFI_PRIM_UINT_FAST16 41 +#define _CFFI_PRIM_INT_FAST32 42 +#define _CFFI_PRIM_UINT_FAST32 43 +#define _CFFI_PRIM_INT_FAST64 44 +#define _CFFI_PRIM_UINT_FAST64 45 +#define _CFFI_PRIM_INTMAX 46 +#define _CFFI_PRIM_UINTMAX 47 +#define _CFFI_PRIM_FLOATCOMPLEX 48 +#define _CFFI_PRIM_DOUBLECOMPLEX 49 +#define _CFFI_PRIM_CHAR16 50 +#define _CFFI_PRIM_CHAR32 51 + +#define _CFFI__NUM_PRIM 52 +#define _CFFI__UNKNOWN_PRIM (-1) +#define _CFFI__UNKNOWN_FLOAT_PRIM (-2) +#define _CFFI__UNKNOWN_LONG_DOUBLE (-3) + +#define _CFFI__IO_FILE_STRUCT (-1) + + +struct _cffi_global_s { + const char *name; + void *address; + _cffi_opcode_t type_op; + void *size_or_direct_fn; // OP_GLOBAL_VAR: size, or 0 if unknown + // OP_CPYTHON_BLTN_*: addr of direct function +}; + +struct _cffi_getconst_s { + unsigned long long value; + const struct _cffi_type_context_s *ctx; + int gindex; +}; + +struct _cffi_struct_union_s { + const char *name; + int type_index; // -> _cffi_types, on a OP_STRUCT_UNION + int flags; // _CFFI_F_* flags below + size_t size; + int alignment; + int first_field_index; // -> _cffi_fields array + int num_fields; +}; +#define _CFFI_F_UNION 0x01 // is a union, not a struct +#define _CFFI_F_CHECK_FIELDS 0x02 // complain if fields are not in the + // "standard layout" or if some are missing +#define _CFFI_F_PACKED 0x04 // for CHECK_FIELDS, assume a packed struct +#define _CFFI_F_EXTERNAL 0x08 // in some other ffi.include() +#define _CFFI_F_OPAQUE 0x10 // opaque + +struct _cffi_field_s { + const char *name; + size_t field_offset; + size_t field_size; + _cffi_opcode_t field_type_op; +}; + +struct _cffi_enum_s { + const char *name; + int type_index; // -> _cffi_types, on a OP_ENUM + int type_prim; // _CFFI_PRIM_xxx + const char *enumerators; // comma-delimited string +}; + +struct _cffi_typename_s { + const char *name; + int type_index; /* if opaque, points to a possibly artificial + OP_STRUCT which is itself opaque */ +}; + +struct _cffi_type_context_s { + _cffi_opcode_t *types; + const struct _cffi_global_s *globals; + const struct _cffi_field_s *fields; + const struct _cffi_struct_union_s *struct_unions; + const struct _cffi_enum_s *enums; + const struct _cffi_typename_s *typenames; + int num_globals; + int num_struct_unions; + int num_enums; + int num_typenames; + const char *const *includes; + int num_types; + int flags; /* future extension */ +}; + +struct _cffi_parse_info_s { + const struct _cffi_type_context_s *ctx; + _cffi_opcode_t *output; + unsigned int output_size; + size_t error_location; + const char *error_message; +}; + +struct _cffi_externpy_s { + const char *name; + size_t size_of_result; + void *reserved1, *reserved2; +}; + +#ifdef _CFFI_INTERNAL +static int parse_c_type(struct _cffi_parse_info_s *info, const char *input); +static int search_in_globals(const struct _cffi_type_context_s *ctx, + const char *search, size_t search_len); +static int search_in_struct_unions(const struct _cffi_type_context_s *ctx, + const char *search, size_t search_len); +#endif diff --git a/venv/lib/python3.12/site-packages/cffi/pkgconfig.py b/venv/lib/python3.12/site-packages/cffi/pkgconfig.py new file mode 100644 index 0000000..e1034ad --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/pkgconfig.py @@ -0,0 +1,121 @@ +# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi +import sys, os, subprocess + +from .error import PkgConfigError + + +def merge_flags(cfg1, cfg2): + """Merge values from cffi config flags cfg2 to cf1 + + Example: + merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) + {"libraries": ["one", "two"]} + """ + for key, value in cfg2.items(): + if key not in cfg1: + cfg1[key] = value + else: + if not isinstance(cfg1[key], list): + raise TypeError("cfg1[%r] should be a list of strings" % (key,)) + if not isinstance(value, list): + raise TypeError("cfg2[%r] should be a list of strings" % (key,)) + cfg1[key].extend(value) + return cfg1 + + +def call(libname, flag, encoding=sys.getfilesystemencoding()): + """Calls pkg-config and returns the output if found + """ + a = ["pkg-config", "--print-errors"] + a.append(flag) + a.append(libname) + try: + pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except OSError as e: + raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) + + bout, berr = pc.communicate() + if pc.returncode != 0: + try: + berr = berr.decode(encoding) + except Exception: + pass + raise PkgConfigError(berr.strip()) + + if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x + try: + bout = bout.decode(encoding) + except UnicodeDecodeError: + raise PkgConfigError("pkg-config %s %s returned bytes that cannot " + "be decoded with encoding %r:\n%r" % + (flag, libname, encoding, bout)) + + if os.altsep != '\\' and '\\' in bout: + raise PkgConfigError("pkg-config %s %s returned an unsupported " + "backslash-escaped output:\n%r" % + (flag, libname, bout)) + return bout + + +def flags_from_pkgconfig(libs): + r"""Return compiler line flags for FFI.set_source based on pkg-config output + + Usage + ... + ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) + + If pkg-config is installed on build machine, then arguments include_dirs, + library_dirs, libraries, define_macros, extra_compile_args and + extra_link_args are extended with an output of pkg-config for libfoo and + libbar. + + Raises PkgConfigError in case the pkg-config call fails. + """ + + def get_include_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-I")] + + def get_library_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-L")] + + def get_libraries(string): + return [x[2:] for x in string.split() if x.startswith("-l")] + + # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils + def get_macros(string): + def _macro(x): + x = x[2:] # drop "-D" + if '=' in x: + return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") + else: + return (x, None) # "-Dfoo" => ("foo", None) + return [_macro(x) for x in string.split() if x.startswith("-D")] + + def get_other_cflags(string): + return [x for x in string.split() if not x.startswith("-I") and + not x.startswith("-D")] + + def get_other_libs(string): + return [x for x in string.split() if not x.startswith("-L") and + not x.startswith("-l")] + + # return kwargs for given libname + def kwargs(libname): + fse = sys.getfilesystemencoding() + all_cflags = call(libname, "--cflags") + all_libs = call(libname, "--libs") + return { + "include_dirs": get_include_dirs(all_cflags), + "library_dirs": get_library_dirs(all_libs), + "libraries": get_libraries(all_libs), + "define_macros": get_macros(all_cflags), + "extra_compile_args": get_other_cflags(all_cflags), + "extra_link_args": get_other_libs(all_libs), + } + + # merge all arguments together + ret = {} + for libname in libs: + lib_flags = kwargs(libname) + merge_flags(ret, lib_flags) + return ret diff --git a/venv/lib/python3.12/site-packages/cffi/recompiler.py b/venv/lib/python3.12/site-packages/cffi/recompiler.py new file mode 100644 index 0000000..5e71aed --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/recompiler.py @@ -0,0 +1,1586 @@ +import io, os, sys, sysconfig +from . import ffiplatform, model +from .error import VerificationError +from .cffi_opcode import * + +VERSION_BASE = 0x2601 +VERSION_EMBEDDED = 0x2701 +VERSION_CHAR16CHAR32 = 0x2801 + +FREE_THREADED_BUILD = sysconfig.get_config_var("Py_GIL_DISABLED") +USE_LIMITED_API = ((sys.platform != 'win32' or sys.version_info < (3, 0) or + sys.version_info >= (3, 5)) and + # free-threaded build doesn't support the stable ABI until Python 3.15 + (not FREE_THREADED_BUILD or sys.version_info >= (3, 15))) + +class GlobalExpr: + def __init__(self, name, address, type_op, size=0, check_value=0): + self.name = name + self.address = address + self.type_op = type_op + self.size = size + self.check_value = check_value + + def as_c_expr(self): + return ' { "%s", (void *)%s, %s, (void *)%s },' % ( + self.name, self.address, self.type_op.as_c_expr(), self.size) + + def as_python_expr(self): + return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name, + self.check_value) + +class FieldExpr: + def __init__(self, name, field_offset, field_size, fbitsize, field_type_op): + self.name = name + self.field_offset = field_offset + self.field_size = field_size + self.fbitsize = fbitsize + self.field_type_op = field_type_op + + def as_c_expr(self): + spaces = " " * len(self.name) + return (' { "%s", %s,\n' % (self.name, self.field_offset) + + ' %s %s,\n' % (spaces, self.field_size) + + ' %s %s },' % (spaces, self.field_type_op.as_c_expr())) + + def as_python_expr(self): + raise NotImplementedError + + def as_field_python_expr(self): + if self.field_type_op.op == OP_NOOP: + size_expr = '' + elif self.field_type_op.op == OP_BITFIELD: + size_expr = format_four_bytes(self.fbitsize) + else: + raise NotImplementedError + return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(), + size_expr, + self.name) + +class StructUnionExpr: + def __init__(self, name, type_index, flags, size, alignment, comment, + first_field_index, c_fields): + self.name = name + self.type_index = type_index + self.flags = flags + self.size = size + self.alignment = alignment + self.comment = comment + self.first_field_index = first_field_index + self.c_fields = c_fields + + def as_c_expr(self): + return (' { "%s", %d, %s,' % (self.name, self.type_index, self.flags) + + '\n %s, %s, ' % (self.size, self.alignment) + + '%d, %d ' % (self.first_field_index, len(self.c_fields)) + + ('/* %s */ ' % self.comment if self.comment else '') + + '},') + + def as_python_expr(self): + flags = eval(self.flags, G_FLAGS) + fields_expr = [c_field.as_field_python_expr() + for c_field in self.c_fields] + return "(b'%s%s%s',%s)" % ( + format_four_bytes(self.type_index), + format_four_bytes(flags), + self.name, + ','.join(fields_expr)) + +class EnumExpr: + def __init__(self, name, type_index, size, signed, allenums): + self.name = name + self.type_index = type_index + self.size = size + self.signed = signed + self.allenums = allenums + + def as_c_expr(self): + lines = [' { "%s", %d, _cffi_prim_int(%s, %s),' % (self.name, self.type_index, + self.size, self.signed,)] + pending = 0 + while len(self.allenums) > pending + 110: + j = self.allenums.find(',', pending + 100) + if j < 0: + break + j += 1 + lines.append(' "%s"' % (self.allenums[pending:j],)) + pending = j + lines.append(' "%s" },' % (self.allenums[pending:],)) + return '\n'.join(lines) + + def as_python_expr(self): + prim_index = { + (1, 0): PRIM_UINT8, (1, 1): PRIM_INT8, + (2, 0): PRIM_UINT16, (2, 1): PRIM_INT16, + (4, 0): PRIM_UINT32, (4, 1): PRIM_INT32, + (8, 0): PRIM_UINT64, (8, 1): PRIM_INT64, + }[self.size, self.signed] + return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index), + format_four_bytes(prim_index), + self.name, self.allenums) + +class TypenameExpr: + def __init__(self, name, type_index): + self.name = name + self.type_index = type_index + + def as_c_expr(self): + return ' { "%s", %d },' % (self.name, self.type_index) + + def as_python_expr(self): + return "b'%s%s'" % (format_four_bytes(self.type_index), self.name) + + +# ____________________________________________________________ + + +class Recompiler: + _num_externpy = 0 + + def __init__(self, ffi, module_name, target_is_python=False): + self.ffi = ffi + self.module_name = module_name + self.target_is_python = target_is_python + self._version = VERSION_BASE + + def needs_version(self, ver): + self._version = max(self._version, ver) + + def collect_type_table(self): + self._typesdict = {} + self._generate("collecttype") + # + all_decls = sorted(self._typesdict, key=str) + # + # prepare all FUNCTION bytecode sequences first + self.cffi_types = [] + for tp in all_decls: + if tp.is_raw_function: + assert self._typesdict[tp] is None + self._typesdict[tp] = len(self.cffi_types) + self.cffi_types.append(tp) # placeholder + for tp1 in tp.args: + assert isinstance(tp1, (model.VoidType, + model.BasePrimitiveType, + model.PointerType, + model.StructOrUnionOrEnum, + model.FunctionPtrType)) + if self._typesdict[tp1] is None: + self._typesdict[tp1] = len(self.cffi_types) + self.cffi_types.append(tp1) # placeholder + self.cffi_types.append('END') # placeholder + # + # prepare all OTHER bytecode sequences + for tp in all_decls: + if not tp.is_raw_function and self._typesdict[tp] is None: + self._typesdict[tp] = len(self.cffi_types) + self.cffi_types.append(tp) # placeholder + if tp.is_array_type and tp.length is not None: + self.cffi_types.append('LEN') # placeholder + assert None not in self._typesdict.values() + # + # collect all structs and unions and enums + self._struct_unions = {} + self._enums = {} + for tp in all_decls: + if isinstance(tp, model.StructOrUnion): + self._struct_unions[tp] = None + elif isinstance(tp, model.EnumType): + self._enums[tp] = None + for i, tp in enumerate(sorted(self._struct_unions, + key=lambda tp: tp.name)): + self._struct_unions[tp] = i + for i, tp in enumerate(sorted(self._enums, + key=lambda tp: tp.name)): + self._enums[tp] = i + # + # emit all bytecode sequences now + for tp in all_decls: + method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__) + method(tp, self._typesdict[tp]) + # + # consistency check + for op in self.cffi_types: + assert isinstance(op, CffiOp) + self.cffi_types = tuple(self.cffi_types) # don't change any more + + def _enum_fields(self, tp): + # When producing C, expand all anonymous struct/union fields. + # That's necessary to have C code checking the offsets of the + # individual fields contained in them. When producing Python, + # don't do it and instead write it like it is, with the + # corresponding fields having an empty name. Empty names are + # recognized at runtime when we import the generated Python + # file. + expand_anonymous_struct_union = not self.target_is_python + return tp.enumfields(expand_anonymous_struct_union) + + def _do_collect_type(self, tp): + if not isinstance(tp, model.BaseTypeByIdentity): + if isinstance(tp, tuple): + for x in tp: + self._do_collect_type(x) + return + if tp not in self._typesdict: + self._typesdict[tp] = None + if isinstance(tp, model.FunctionPtrType): + self._do_collect_type(tp.as_raw_function()) + elif isinstance(tp, model.StructOrUnion): + if tp.fldtypes is not None and ( + tp not in self.ffi._parser._included_declarations): + for name1, tp1, _, _ in self._enum_fields(tp): + self._do_collect_type(self._field_type(tp, name1, tp1)) + else: + for _, x in tp._get_items(): + self._do_collect_type(x) + + def _generate(self, step_name): + lst = self.ffi._parser._declarations.items() + for name, (tp, quals) in sorted(lst): + kind, realname = name.split(' ', 1) + try: + method = getattr(self, '_generate_cpy_%s_%s' % (kind, + step_name)) + except AttributeError: + raise VerificationError( + "not implemented in recompile(): %r" % name) + try: + self._current_quals = quals + method(tp, realname) + except Exception as e: + model.attach_exception_info(e, name) + raise + + # ---------- + + ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"] + + def collect_step_tables(self): + # collect the declarations for '_cffi_globals', '_cffi_typenames', etc. + self._lsts = {} + for step_name in self.ALL_STEPS: + self._lsts[step_name] = [] + self._seen_struct_unions = set() + self._generate("ctx") + self._add_missing_struct_unions() + # + for step_name in self.ALL_STEPS: + lst = self._lsts[step_name] + if step_name != "field": + lst.sort(key=lambda entry: entry.name) + self._lsts[step_name] = tuple(lst) # don't change any more + # + # check for a possible internal inconsistency: _cffi_struct_unions + # should have been generated with exactly self._struct_unions + lst = self._lsts["struct_union"] + for tp, i in self._struct_unions.items(): + assert i < len(lst) + assert lst[i].name == tp.name + assert len(lst) == len(self._struct_unions) + # same with enums + lst = self._lsts["enum"] + for tp, i in self._enums.items(): + assert i < len(lst) + assert lst[i].name == tp.name + assert len(lst) == len(self._enums) + + # ---------- + + def _prnt(self, what=''): + self._f.write(what + '\n') + + def write_source_to_f(self, f, preamble): + if self.target_is_python: + assert preamble is None + self.write_py_source_to_f(f) + else: + assert preamble is not None + self.write_c_source_to_f(f, preamble) + + def _rel_readlines(self, filename): + with open(os.path.join(os.path.dirname(__file__), filename), 'r') as g: + return g.readlines() + + def write_c_source_to_f(self, f, preamble): + self._f = f + prnt = self._prnt + if self.ffi._embedding is not None: + prnt('#define _CFFI_USE_EMBEDDING') + if not USE_LIMITED_API: + prnt('#define _CFFI_NO_LIMITED_API') + # + # first the '#include' (actually done by inlining the file's content) + lines = self._rel_readlines('_cffi_include.h') + i = lines.index('#include "parse_c_type.h"\n') + lines[i:i+1] = self._rel_readlines('parse_c_type.h') + prnt(''.join(lines)) + # + # if we have ffi._embedding != None, we give it here as a macro + # and include an extra file + base_module_name = self.module_name.split('.')[-1] + if self.ffi._embedding is not None: + prnt('#define _CFFI_MODULE_NAME "%s"' % (self.module_name,)) + prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {') + self._print_string_literal_in_array(self.ffi._embedding) + prnt('0 };') + prnt('#ifdef PYPY_VERSION') + prnt('# define _CFFI_PYTHON_STARTUP_FUNC _cffi_pypyinit_%s' % ( + base_module_name,)) + prnt('#else') + prnt('# define _CFFI_PYTHON_STARTUP_FUNC PyInit_%s' % ( + base_module_name,)) + prnt('#endif') + lines = self._rel_readlines('_embedding.h') + i = lines.index('#include "_cffi_errors.h"\n') + lines[i:i+1] = self._rel_readlines('_cffi_errors.h') + prnt(''.join(lines)) + self.needs_version(VERSION_EMBEDDED) + # + # then paste the C source given by the user, verbatim. + prnt('/************************************************************/') + prnt() + prnt(preamble) + prnt() + prnt('/************************************************************/') + prnt() + # + # the declaration of '_cffi_types' + prnt('static void *_cffi_types[] = {') + typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) + for i, op in enumerate(self.cffi_types): + comment = '' + if i in typeindex2type: + comment = ' // ' + typeindex2type[i]._get_c_name() + prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment)) + if not self.cffi_types: + prnt(' 0') + prnt('};') + prnt() + # + # call generate_cpy_xxx_decl(), for every xxx found from + # ffi._parser._declarations. This generates all the functions. + self._seen_constants = set() + self._generate("decl") + # + # the declaration of '_cffi_globals' and '_cffi_typenames' + nums = {} + for step_name in self.ALL_STEPS: + lst = self._lsts[step_name] + nums[step_name] = len(lst) + if nums[step_name] > 0: + prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % ( + step_name, step_name)) + for entry in lst: + prnt(entry.as_c_expr()) + prnt('};') + prnt() + # + # the declaration of '_cffi_includes' + if self.ffi._included_ffis: + prnt('static const char * const _cffi_includes[] = {') + for ffi_to_include in self.ffi._included_ffis: + try: + included_module_name, included_source = ( + ffi_to_include._assigned_source[:2]) + except AttributeError: + raise VerificationError( + "ffi object %r includes %r, but the latter has not " + "been prepared with set_source()" % ( + self.ffi, ffi_to_include,)) + if included_source is None: + raise VerificationError( + "not implemented yet: ffi.include() of a Python-based " + "ffi inside a C-based ffi") + prnt(' "%s",' % (included_module_name,)) + prnt(' NULL') + prnt('};') + prnt() + # + # the declaration of '_cffi_type_context' + prnt('static const struct _cffi_type_context_s _cffi_type_context = {') + prnt(' _cffi_types,') + for step_name in self.ALL_STEPS: + if nums[step_name] > 0: + prnt(' _cffi_%ss,' % step_name) + else: + prnt(' NULL, /* no %ss */' % step_name) + for step_name in self.ALL_STEPS: + if step_name != "field": + prnt(' %d, /* num_%ss */' % (nums[step_name], step_name)) + if self.ffi._included_ffis: + prnt(' _cffi_includes,') + else: + prnt(' NULL, /* no includes */') + prnt(' %d, /* num_types */' % (len(self.cffi_types),)) + flags = 0 + if self._num_externpy > 0 or self.ffi._embedding is not None: + flags |= 1 # set to mean that we use extern "Python" + prnt(' %d, /* flags */' % flags) + prnt('};') + prnt() + # + # the init function + prnt('#ifdef __GNUC__') + prnt('# pragma GCC visibility push(default) /* for -fvisibility= */') + prnt('#endif') + prnt() + prnt('#ifdef PYPY_VERSION') + prnt('PyMODINIT_FUNC') + prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,)) + prnt('{') + if flags & 1: + prnt(' if (((intptr_t)p[0]) >= 0x0A03) {') + prnt(' _cffi_call_python_org = ' + '(void(*)(struct _cffi_externpy_s *, char *))p[1];') + prnt(' }') + prnt(' p[0] = (const void *)0x%x;' % self._version) + prnt(' p[1] = &_cffi_type_context;') + prnt(' return NULL;') + prnt('}') + # on Windows, distutils insists on putting init_cffi_xyz in + # 'export_symbols', so instead of fighting it, just give up and + # give it one + prnt('# ifdef _MSC_VER') + prnt(' PyMODINIT_FUNC') + prnt(' PyInit_%s(void) { return NULL; }' % (base_module_name,)) + prnt('# endif') + prnt('#else') + prnt('PyMODINIT_FUNC') + prnt('PyInit_%s(void)' % (base_module_name,)) + prnt('{') + prnt(' return _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( + self.module_name, self._version)) + prnt('}') + prnt('#endif') + prnt() + prnt('#ifdef __GNUC__') + prnt('# pragma GCC visibility pop') + prnt('#endif') + self._version = None + + def _to_py(self, x): + if isinstance(x, str): + return "b'%s'" % (x,) + if isinstance(x, (list, tuple)): + rep = [self._to_py(item) for item in x] + if len(rep) == 1: + rep.append('') + return "(%s)" % (','.join(rep),) + return x.as_python_expr() # Py2: unicode unexpected; Py3: bytes unexp. + + def write_py_source_to_f(self, f): + self._f = f + prnt = self._prnt + # + # header + prnt("# auto-generated file") + prnt("import _cffi_backend") + # + # the 'import' of the included ffis + num_includes = len(self.ffi._included_ffis or ()) + for i in range(num_includes): + ffi_to_include = self.ffi._included_ffis[i] + try: + included_module_name, included_source = ( + ffi_to_include._assigned_source[:2]) + except AttributeError: + raise VerificationError( + "ffi object %r includes %r, but the latter has not " + "been prepared with set_source()" % ( + self.ffi, ffi_to_include,)) + if included_source is not None: + raise VerificationError( + "not implemented yet: ffi.include() of a C-based " + "ffi inside a Python-based ffi") + prnt('from %s import ffi as _ffi%d' % (included_module_name, i)) + prnt() + prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,)) + prnt(" _version = 0x%x," % (self._version,)) + self._version = None + # + # the '_types' keyword argument + self.cffi_types = tuple(self.cffi_types) # don't change any more + types_lst = [op.as_python_bytes() for op in self.cffi_types] + prnt(' _types = %s,' % (self._to_py(''.join(types_lst)),)) + typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) + # + # the keyword arguments from ALL_STEPS + for step_name in self.ALL_STEPS: + lst = self._lsts[step_name] + if len(lst) > 0 and step_name != "field": + prnt(' _%ss = %s,' % (step_name, self._to_py(lst))) + # + # the '_includes' keyword argument + if num_includes > 0: + prnt(' _includes = (%s,),' % ( + ', '.join(['_ffi%d' % i for i in range(num_includes)]),)) + # + # the footer + prnt(')') + + # ---------- + + def _gettypenum(self, type): + # a KeyError here is a bug. please report it! :-) + return self._typesdict[type] + + def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): + extraarg = '' + if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type(): + if tp.is_integer_type() and tp.name != '_Bool': + converter = '_cffi_to_c_int' + extraarg = ', %s' % tp.name + elif isinstance(tp, model.UnknownFloatType): + # don't check with is_float_type(): it may be a 'long + # double' here, and _cffi_to_c_double would loose precision + converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),) + else: + cname = tp.get_c_name('') + converter = '(%s)_cffi_to_c_%s' % (cname, + tp.name.replace(' ', '_')) + if cname in ('char16_t', 'char32_t'): + self.needs_version(VERSION_CHAR16CHAR32) + errvalue = '-1' + # + elif isinstance(tp, model.PointerType): + self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, + tovar, errcode) + return + # + elif (isinstance(tp, (model.StructOrUnionOrEnum, model.BasePrimitiveType))): + # a struct (not a struct pointer) as a function argument; + # or, a complex (the same code works) + self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' + % (tovar, self._gettypenum(tp), fromvar)) + self._prnt(' %s;' % errcode) + return + # + elif isinstance(tp, model.FunctionPtrType): + converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') + extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) + errvalue = 'NULL' + # + else: + raise NotImplementedError(tp) + # + self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) + self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( + tovar, tp.get_c_name(''), errvalue)) + self._prnt(' %s;' % errcode) + + def _extra_local_variables(self, tp, localvars, freelines): + if isinstance(tp, model.PointerType): + localvars.add('Py_ssize_t datasize') + localvars.add('struct _cffi_freeme_s *large_args_free = NULL') + freelines.add('if (large_args_free != NULL)' + ' _cffi_free_array_arguments(large_args_free);') + + def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): + self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') + self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( + self._gettypenum(tp), fromvar, tovar)) + self._prnt(' if (datasize != 0) {') + self._prnt(' %s = ((size_t)datasize) <= 640 ? ' + '(%s)alloca((size_t)datasize) : NULL;' % ( + tovar, tp.get_c_name(''))) + self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' + '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) + self._prnt(' datasize, &large_args_free) < 0)') + self._prnt(' %s;' % errcode) + self._prnt(' }') + + def _convert_expr_from_c(self, tp, var, context): + if isinstance(tp, model.BasePrimitiveType): + if tp.is_integer_type() and tp.name != '_Bool': + return '_cffi_from_c_int(%s, %s)' % (var, tp.name) + elif isinstance(tp, model.UnknownFloatType): + return '_cffi_from_c_double(%s)' % (var,) + elif tp.name != 'long double' and not tp.is_complex_type(): + cname = tp.name.replace(' ', '_') + if cname in ('char16_t', 'char32_t'): + self.needs_version(VERSION_CHAR16CHAR32) + return '_cffi_from_c_%s(%s)' % (cname, var) + else: + return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): + return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, model.ArrayType): + return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( + var, self._gettypenum(model.PointerType(tp.item))) + elif isinstance(tp, model.StructOrUnion): + if tp.fldnames is None: + raise TypeError("'%s' is used as %s, but is opaque" % ( + tp._get_c_name(), context)) + return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, model.EnumType): + return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + else: + raise NotImplementedError(tp) + + # ---------- + # typedefs + + def _typedef_type(self, tp, name): + return self._global_type(tp, "(*(%s *)0)" % (name,)) + + def _generate_cpy_typedef_collecttype(self, tp, name): + self._do_collect_type(self._typedef_type(tp, name)) + + def _generate_cpy_typedef_decl(self, tp, name): + pass + + def _typedef_ctx(self, tp, name): + type_index = self._typesdict[tp] + self._lsts["typename"].append(TypenameExpr(name, type_index)) + + def _generate_cpy_typedef_ctx(self, tp, name): + tp = self._typedef_type(tp, name) + self._typedef_ctx(tp, name) + if getattr(tp, "origin", None) == "unknown_type": + self._struct_ctx(tp, tp.name, approxname=None) + elif isinstance(tp, model.NamedPointerType): + self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name, + named_ptr=tp) + + # ---------- + # function declarations + + def _generate_cpy_function_collecttype(self, tp, name): + self._do_collect_type(tp.as_raw_function()) + if tp.ellipsis and not self.target_is_python: + self._do_collect_type(tp) + + def _generate_cpy_function_decl(self, tp, name): + assert not self.target_is_python + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + # cannot support vararg functions better than this: check for its + # exact type (including the fixed arguments), and build it as a + # constant function pointer (no CPython wrapper) + self._generate_cpy_constant_decl(tp, name) + return + prnt = self._prnt + numargs = len(tp.args) + if numargs == 0: + argname = 'noarg' + elif numargs == 1: + argname = 'arg0' + else: + argname = 'args' + # + # ------------------------------ + # the 'd' version of the function, only for addressof(lib, 'func') + arguments = [] + call_arguments = [] + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + arguments.append(type.get_c_name(' x%d' % i, context)) + call_arguments.append('x%d' % i) + repr_arguments = ', '.join(arguments) + repr_arguments = repr_arguments or 'void' + if tp.abi: + abi = tp.abi + ' ' + else: + abi = '' + name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments) + prnt('static %s' % (tp.result.get_c_name(name_and_arguments),)) + prnt('{') + call_arguments = ', '.join(call_arguments) + result_code = 'return ' + if isinstance(tp.result, model.VoidType): + result_code = '' + prnt(' %s%s(%s);' % (result_code, name, call_arguments)) + prnt('}') + # + prnt('#ifndef PYPY_VERSION') # ------------------------------ + # + prnt('static PyObject *') + prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) + prnt('{') + # + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + arg = type.get_c_name(' x%d' % i, context) + prnt(' %s;' % arg) + # + localvars = set() + freelines = set() + for type in tp.args: + self._extra_local_variables(type, localvars, freelines) + for decl in sorted(localvars): + prnt(' %s;' % (decl,)) + # + if not isinstance(tp.result, model.VoidType): + result_code = 'result = ' + context = 'result of %s' % name + result_decl = ' %s;' % tp.result.get_c_name(' result', context) + prnt(result_decl) + prnt(' PyObject *pyresult;') + else: + result_decl = None + result_code = '' + # + if len(tp.args) > 1: + rng = range(len(tp.args)) + for i in rng: + prnt(' PyObject *arg%d;' % i) + prnt() + prnt(' if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % ( + name, len(rng), len(rng), + ', '.join(['&arg%d' % i for i in rng]))) + prnt(' return NULL;') + prnt() + # + for i, type in enumerate(tp.args): + self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, + 'return NULL') + prnt() + # + prnt(' Py_BEGIN_ALLOW_THREADS') + prnt(' _cffi_restore_errno();') + call_arguments = ['x%d' % i for i in range(len(tp.args))] + call_arguments = ', '.join(call_arguments) + prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) + prnt(' _cffi_save_errno();') + prnt(' Py_END_ALLOW_THREADS') + prnt() + # + prnt(' (void)self; /* unused */') + if numargs == 0: + prnt(' (void)noarg; /* unused */') + if result_code: + prnt(' pyresult = %s;' % + self._convert_expr_from_c(tp.result, 'result', 'result type')) + for freeline in freelines: + prnt(' ' + freeline) + prnt(' return pyresult;') + else: + for freeline in freelines: + prnt(' ' + freeline) + prnt(' Py_INCREF(Py_None);') + prnt(' return Py_None;') + prnt('}') + # + prnt('#else') # ------------------------------ + # + # the PyPy version: need to replace struct/union arguments with + # pointers, and if the result is a struct/union, insert a first + # arg that is a pointer to the result. We also do that for + # complex args and return type. + def need_indirection(type): + return (isinstance(type, model.StructOrUnion) or + (isinstance(type, model.PrimitiveType) and + type.is_complex_type())) + difference = False + arguments = [] + call_arguments = [] + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + indirection = '' + if need_indirection(type): + indirection = '*' + difference = True + arg = type.get_c_name(' %sx%d' % (indirection, i), context) + arguments.append(arg) + call_arguments.append('%sx%d' % (indirection, i)) + tp_result = tp.result + if need_indirection(tp_result): + context = 'result of %s' % name + arg = tp_result.get_c_name(' *result', context) + arguments.insert(0, arg) + tp_result = model.void_type + result_decl = None + result_code = '*result = ' + difference = True + if difference: + repr_arguments = ', '.join(arguments) + repr_arguments = repr_arguments or 'void' + name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name, + repr_arguments) + prnt('static %s' % (tp_result.get_c_name(name_and_arguments),)) + prnt('{') + if result_decl: + prnt(result_decl) + call_arguments = ', '.join(call_arguments) + prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) + if result_decl: + prnt(' return result;') + prnt('}') + else: + prnt('# define _cffi_f_%s _cffi_d_%s' % (name, name)) + # + prnt('#endif') # ------------------------------ + prnt() + + def _generate_cpy_function_ctx(self, tp, name): + if tp.ellipsis and not self.target_is_python: + self._generate_cpy_constant_ctx(tp, name) + return + type_index = self._typesdict[tp.as_raw_function()] + numargs = len(tp.args) + if self.target_is_python: + meth_kind = OP_DLOPEN_FUNC + elif numargs == 0: + meth_kind = OP_CPYTHON_BLTN_N # 'METH_NOARGS' + elif numargs == 1: + meth_kind = OP_CPYTHON_BLTN_O # 'METH_O' + else: + meth_kind = OP_CPYTHON_BLTN_V # 'METH_VARARGS' + self._lsts["global"].append( + GlobalExpr(name, '_cffi_f_%s' % name, + CffiOp(meth_kind, type_index), + size='_cffi_d_%s' % name)) + + # ---------- + # named structs or unions + + def _field_type(self, tp_struct, field_name, tp_field): + if isinstance(tp_field, model.ArrayType): + actual_length = tp_field.length + if actual_length == '...': + ptr_struct_name = tp_struct.get_c_name('*') + actual_length = '_cffi_array_len(((%s)0)->%s)' % ( + ptr_struct_name, field_name) + tp_item = self._field_type(tp_struct, '%s[0]' % field_name, + tp_field.item) + tp_field = model.ArrayType(tp_item, actual_length) + return tp_field + + def _struct_collecttype(self, tp): + self._do_collect_type(tp) + if self.target_is_python: + # also requires nested anon struct/unions in ABI mode, recursively + for fldtype in tp.anonymous_struct_fields(): + self._struct_collecttype(fldtype) + + def _struct_decl(self, tp, cname, approxname): + if tp.fldtypes is None: + return + prnt = self._prnt + checkfuncname = '_cffi_checkfld_%s' % (approxname,) + prnt('_CFFI_UNUSED_FN') + prnt('static void %s(%s *p)' % (checkfuncname, cname)) + prnt('{') + prnt(' /* only to generate compile-time warnings or errors */') + prnt(' (void)p;') + for fname, ftype, fbitsize, fqual in self._enum_fields(tp): + try: + if ftype.is_integer_type() or fbitsize >= 0: + # accept all integers, but complain on float or double + if fname != '': + prnt(" (void)((p->%s) | 0); /* check that '%s.%s' is " + "an integer */" % (fname, cname, fname)) + continue + # only accept exactly the type declared, except that '[]' + # is interpreted as a '*' and so will match any array length. + # (It would also match '*', but that's harder to detect...) + while (isinstance(ftype, model.ArrayType) + and (ftype.length is None or ftype.length == '...')): + ftype = ftype.item + fname = fname + '[0]' + prnt(' { %s = &p->%s; (void)tmp; }' % ( + ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), + fname)) + except VerificationError as e: + prnt(' /* %s */' % str(e)) # cannot verify it, ignore + prnt('}') + prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname)) + prnt() + + def _struct_ctx(self, tp, cname, approxname, named_ptr=None): + type_index = self._typesdict[tp] + reason_for_not_expanding = None + flags = [] + if isinstance(tp, model.UnionType): + flags.append("_CFFI_F_UNION") + if tp.fldtypes is None: + flags.append("_CFFI_F_OPAQUE") + reason_for_not_expanding = "opaque" + if (tp not in self.ffi._parser._included_declarations and + (named_ptr is None or + named_ptr not in self.ffi._parser._included_declarations)): + if tp.fldtypes is None: + pass # opaque + elif tp.partial or any(tp.anonymous_struct_fields()): + pass # field layout obtained silently from the C compiler + else: + flags.append("_CFFI_F_CHECK_FIELDS") + if tp.packed: + if tp.packed > 1: + raise NotImplementedError( + "%r is declared with 'pack=%r'; only 0 or 1 are " + "supported in API mode (try to use \"...;\", which " + "does not require a 'pack' declaration)" % + (tp, tp.packed)) + flags.append("_CFFI_F_PACKED") + else: + flags.append("_CFFI_F_EXTERNAL") + reason_for_not_expanding = "external" + flags = '|'.join(flags) or '0' + c_fields = [] + if reason_for_not_expanding is None: + enumfields = list(self._enum_fields(tp)) + for fldname, fldtype, fbitsize, fqual in enumfields: + fldtype = self._field_type(tp, fldname, fldtype) + self._check_not_opaque(fldtype, + "field '%s.%s'" % (tp.name, fldname)) + # cname is None for _add_missing_struct_unions() only + op = OP_NOOP + if fbitsize >= 0: + op = OP_BITFIELD + size = '%d /* bits */' % fbitsize + elif cname is None or ( + isinstance(fldtype, model.ArrayType) and + fldtype.length is None): + size = '(size_t)-1' + else: + size = 'sizeof(((%s)0)->%s)' % ( + tp.get_c_name('*') if named_ptr is None + else named_ptr.name, + fldname) + if cname is None or fbitsize >= 0: + offset = '(size_t)-1' + elif named_ptr is not None: + offset = '(size_t)(((char *)&((%s)4096)->%s) - (char *)4096)' % ( + named_ptr.name, fldname) + else: + offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname) + c_fields.append( + FieldExpr(fldname, offset, size, fbitsize, + CffiOp(op, self._typesdict[fldtype]))) + first_field_index = len(self._lsts["field"]) + self._lsts["field"].extend(c_fields) + # + if cname is None: # unknown name, for _add_missing_struct_unions + size = '(size_t)-2' + align = -2 + comment = "unnamed" + else: + if named_ptr is not None: + size = 'sizeof(*(%s)0)' % (named_ptr.name,) + align = '-1 /* unknown alignment */' + else: + size = 'sizeof(%s)' % (cname,) + align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,) + comment = None + else: + size = '(size_t)-1' + align = -1 + first_field_index = -1 + comment = reason_for_not_expanding + self._lsts["struct_union"].append( + StructUnionExpr(tp.name, type_index, flags, size, align, comment, + first_field_index, c_fields)) + self._seen_struct_unions.add(tp) + + def _check_not_opaque(self, tp, location): + while isinstance(tp, model.ArrayType): + tp = tp.item + if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None: + raise TypeError( + "%s is of an opaque type (not declared in cdef())" % location) + + def _add_missing_struct_unions(self): + # not very nice, but some struct declarations might be missing + # because they don't have any known C name. Check that they are + # not partial (we can't complete or verify them!) and emit them + # anonymously. + lst = list(self._struct_unions.items()) + lst.sort(key=lambda tp_order: tp_order[1]) + for tp, order in lst: + if tp not in self._seen_struct_unions: + if tp.partial: + raise NotImplementedError("internal inconsistency: %r is " + "partial but was not seen at " + "this point" % (tp,)) + if tp.name.startswith('$') and tp.name[1:].isdigit(): + approxname = tp.name[1:] + elif tp.name == '_IO_FILE' and tp.forcename == 'FILE': + approxname = 'FILE' + self._typedef_ctx(tp, 'FILE') + else: + raise NotImplementedError("internal inconsistency: %r" % + (tp,)) + self._struct_ctx(tp, None, approxname) + + def _generate_cpy_struct_collecttype(self, tp, name): + self._struct_collecttype(tp) + _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype + + def _struct_names(self, tp): + cname = tp.get_c_name('') + if ' ' in cname: + return cname, cname.replace(' ', '_') + else: + return cname, '_' + cname + + def _generate_cpy_struct_decl(self, tp, name): + self._struct_decl(tp, *self._struct_names(tp)) + _generate_cpy_union_decl = _generate_cpy_struct_decl + + def _generate_cpy_struct_ctx(self, tp, name): + self._struct_ctx(tp, *self._struct_names(tp)) + _generate_cpy_union_ctx = _generate_cpy_struct_ctx + + # ---------- + # 'anonymous' declarations. These are produced for anonymous structs + # or unions; the 'name' is obtained by a typedef. + + def _generate_cpy_anonymous_collecttype(self, tp, name): + if isinstance(tp, model.EnumType): + self._generate_cpy_enum_collecttype(tp, name) + else: + self._struct_collecttype(tp) + + def _generate_cpy_anonymous_decl(self, tp, name): + if isinstance(tp, model.EnumType): + self._generate_cpy_enum_decl(tp) + else: + self._struct_decl(tp, name, 'typedef_' + name) + + def _generate_cpy_anonymous_ctx(self, tp, name): + if isinstance(tp, model.EnumType): + self._enum_ctx(tp, name) + else: + self._struct_ctx(tp, name, 'typedef_' + name) + + # ---------- + # constants, declared with "static const ..." + + def _generate_cpy_const(self, is_int, name, tp=None, category='const', + check_value=None): + if (category, name) in self._seen_constants: + raise VerificationError( + "duplicate declaration of %s '%s'" % (category, name)) + self._seen_constants.add((category, name)) + # + prnt = self._prnt + funcname = '_cffi_%s_%s' % (category, name) + if is_int: + prnt('static int %s(unsigned long long *o)' % funcname) + prnt('{') + prnt(' int n = (%s) <= 0;' % (name,)) + prnt(' *o = (unsigned long long)((%s) | 0);' + ' /* check that %s is an integer */' % (name, name)) + if check_value is not None: + if check_value > 0: + check_value = '%dU' % (check_value,) + prnt(' if (!_cffi_check_int(*o, n, %s))' % (check_value,)) + prnt(' n |= 2;') + prnt(' return n;') + prnt('}') + else: + assert check_value is None + prnt('static void %s(char *o)' % funcname) + prnt('{') + prnt(' *(%s)o = %s;' % (tp.get_c_name('*'), name)) + prnt('}') + prnt() + + def _generate_cpy_constant_collecttype(self, tp, name): + is_int = tp.is_integer_type() + if not is_int or self.target_is_python: + self._do_collect_type(tp) + + def _generate_cpy_constant_decl(self, tp, name): + is_int = tp.is_integer_type() + self._generate_cpy_const(is_int, name, tp) + + def _generate_cpy_constant_ctx(self, tp, name): + if not self.target_is_python and tp.is_integer_type(): + type_op = CffiOp(OP_CONSTANT_INT, -1) + else: + if self.target_is_python: + const_kind = OP_DLOPEN_CONST + else: + const_kind = OP_CONSTANT + type_index = self._typesdict[tp] + type_op = CffiOp(const_kind, type_index) + self._lsts["global"].append( + GlobalExpr(name, '_cffi_const_%s' % name, type_op)) + + # ---------- + # enums + + def _generate_cpy_enum_collecttype(self, tp, name): + self._do_collect_type(tp) + + def _generate_cpy_enum_decl(self, tp, name=None): + for enumerator in tp.enumerators: + self._generate_cpy_const(True, enumerator) + + def _enum_ctx(self, tp, cname): + type_index = self._typesdict[tp] + type_op = CffiOp(OP_ENUM, -1) + if self.target_is_python: + tp.check_not_partial() + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + self._lsts["global"].append( + GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op, + check_value=enumvalue)) + # + if cname is not None and '$' not in cname and not self.target_is_python: + size = "sizeof(%s)" % cname + signed = "((%s)-1) <= 0" % cname + else: + basetp = tp.build_baseinttype(self.ffi, []) + size = self.ffi.sizeof(basetp) + signed = int(int(self.ffi.cast(basetp, -1)) < 0) + allenums = ",".join(tp.enumerators) + self._lsts["enum"].append( + EnumExpr(tp.name, type_index, size, signed, allenums)) + + def _generate_cpy_enum_ctx(self, tp, name): + self._enum_ctx(tp, tp._get_c_name()) + + # ---------- + # macros: for now only for integers + + def _generate_cpy_macro_collecttype(self, tp, name): + pass + + def _generate_cpy_macro_decl(self, tp, name): + if tp == '...': + check_value = None + else: + check_value = tp # an integer + self._generate_cpy_const(True, name, check_value=check_value) + + def _generate_cpy_macro_ctx(self, tp, name): + if tp == '...': + if self.target_is_python: + raise VerificationError( + "cannot use the syntax '...' in '#define %s ...' when " + "using the ABI mode" % (name,)) + check_value = None + else: + check_value = tp # an integer + type_op = CffiOp(OP_CONSTANT_INT, -1) + self._lsts["global"].append( + GlobalExpr(name, '_cffi_const_%s' % name, type_op, + check_value=check_value)) + + # ---------- + # global variables + + def _global_type(self, tp, global_name): + if isinstance(tp, model.ArrayType): + actual_length = tp.length + if actual_length == '...': + actual_length = '_cffi_array_len(%s)' % (global_name,) + tp_item = self._global_type(tp.item, '%s[0]' % global_name) + tp = model.ArrayType(tp_item, actual_length) + return tp + + def _generate_cpy_variable_collecttype(self, tp, name): + self._do_collect_type(self._global_type(tp, name)) + + def _generate_cpy_variable_decl(self, tp, name): + prnt = self._prnt + tp = self._global_type(tp, name) + if isinstance(tp, model.ArrayType) and tp.length is None: + tp = tp.item + ampersand = '' + else: + ampersand = '&' + # This code assumes that casts from "tp *" to "void *" is a + # no-op, i.e. a function that returns a "tp *" can be called + # as if it returned a "void *". This should be generally true + # on any modern machine. The only exception to that rule (on + # uncommon architectures, and as far as I can tell) might be + # if 'tp' were a function type, but that is not possible here. + # (If 'tp' is a function _pointer_ type, then casts from "fn_t + # **" to "void *" are again no-ops, as far as I can tell.) + decl = '*_cffi_var_%s(void)' % (name,) + prnt('static ' + tp.get_c_name(decl, quals=self._current_quals)) + prnt('{') + prnt(' return %s(%s);' % (ampersand, name)) + prnt('}') + prnt() + + def _generate_cpy_variable_ctx(self, tp, name): + tp = self._global_type(tp, name) + type_index = self._typesdict[tp] + if self.target_is_python: + op = OP_GLOBAL_VAR + else: + op = OP_GLOBAL_VAR_F + self._lsts["global"].append( + GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index))) + + # ---------- + # extern "Python" + + def _generate_cpy_extern_python_collecttype(self, tp, name): + assert isinstance(tp, model.FunctionPtrType) + self._do_collect_type(tp) + _generate_cpy_dllexport_python_collecttype = \ + _generate_cpy_extern_python_plus_c_collecttype = \ + _generate_cpy_extern_python_collecttype + + def _extern_python_decl(self, tp, name, tag_and_space): + prnt = self._prnt + if isinstance(tp.result, model.VoidType): + size_of_result = '0' + else: + context = 'result of %s' % name + size_of_result = '(int)sizeof(%s)' % ( + tp.result.get_c_name('', context),) + prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name) + prnt(' { "%s.%s", %s, 0, 0 };' % ( + self.module_name, name, size_of_result)) + prnt() + # + arguments = [] + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + arg = type.get_c_name(' a%d' % i, context) + arguments.append(arg) + # + repr_arguments = ', '.join(arguments) + repr_arguments = repr_arguments or 'void' + name_and_arguments = '%s(%s)' % (name, repr_arguments) + if tp.abi == "__stdcall": + name_and_arguments = '_cffi_stdcall ' + name_and_arguments + # + def may_need_128_bits(tp): + return (isinstance(tp, model.PrimitiveType) and + tp.name == 'long double') + # + size_of_a = max(len(tp.args)*8, 8) + if may_need_128_bits(tp.result): + size_of_a = max(size_of_a, 16) + if isinstance(tp.result, model.StructOrUnion): + size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % ( + tp.result.get_c_name(''), size_of_a, + tp.result.get_c_name(''), size_of_a) + prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments))) + prnt('{') + prnt(' char a[%s];' % size_of_a) + prnt(' char *p = a;') + for i, type in enumerate(tp.args): + arg = 'a%d' % i + if (isinstance(type, model.StructOrUnion) or + may_need_128_bits(type)): + arg = '&' + arg + type = model.PointerType(type) + prnt(' *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg)) + prnt(' _cffi_call_python(&_cffi_externpy__%s, p);' % name) + if not isinstance(tp.result, model.VoidType): + prnt(' return *(%s)p;' % (tp.result.get_c_name('*'),)) + prnt('}') + prnt() + self._num_externpy += 1 + + def _generate_cpy_extern_python_decl(self, tp, name): + self._extern_python_decl(tp, name, 'static ') + + def _generate_cpy_dllexport_python_decl(self, tp, name): + self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ') + + def _generate_cpy_extern_python_plus_c_decl(self, tp, name): + self._extern_python_decl(tp, name, '') + + def _generate_cpy_extern_python_ctx(self, tp, name): + if self.target_is_python: + raise VerificationError( + "cannot use 'extern \"Python\"' in the ABI mode") + if tp.ellipsis: + raise NotImplementedError("a vararg function is extern \"Python\"") + type_index = self._typesdict[tp] + type_op = CffiOp(OP_EXTERN_PYTHON, type_index) + self._lsts["global"].append( + GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name)) + + _generate_cpy_dllexport_python_ctx = \ + _generate_cpy_extern_python_plus_c_ctx = \ + _generate_cpy_extern_python_ctx + + def _print_string_literal_in_array(self, s): + prnt = self._prnt + prnt('// # NB. this is not a string because of a size limit in MSVC') + if not isinstance(s, bytes): # unicode + s = s.encode('utf-8') # -> bytes + else: + s.decode('utf-8') # got bytes, check for valid utf-8 + for line in s.splitlines(True): + comment = line + if type('//') is bytes: # python2 + line = map(ord, line) # make a list of integers + else: # python3 + # type(line) is bytes, which enumerates like a list of integers + comment = ascii(comment)[1:-1] + prnt(('// ' + comment).rstrip()) + printed_line = '' + for c in line: + if len(printed_line) >= 76: + prnt(printed_line) + printed_line = '' + printed_line += '%d,' % (c,) + prnt(printed_line) + + # ---------- + # emitting the opcodes for individual types + + def _emit_bytecode_VoidType(self, tp, index): + self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID) + + def _emit_bytecode_PrimitiveType(self, tp, index): + prim_index = PRIMITIVE_TO_INDEX[tp.name] + self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index) + + def _emit_bytecode_UnknownIntegerType(self, tp, index): + s = ('_cffi_prim_int(sizeof(%s), (\n' + ' ((%s)-1) | 0 /* check that %s is an integer type */\n' + ' ) <= 0)' % (tp.name, tp.name, tp.name)) + self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) + + def _emit_bytecode_UnknownFloatType(self, tp, index): + s = ('_cffi_prim_float(sizeof(%s) *\n' + ' (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n' + ' )' % (tp.name, tp.name)) + self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) + + def _emit_bytecode_RawFunctionType(self, tp, index): + self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result]) + index += 1 + for tp1 in tp.args: + realindex = self._typesdict[tp1] + if index != realindex: + if isinstance(tp1, model.PrimitiveType): + self._emit_bytecode_PrimitiveType(tp1, index) + else: + self.cffi_types[index] = CffiOp(OP_NOOP, realindex) + index += 1 + flags = int(tp.ellipsis) + if tp.abi is not None: + if tp.abi == '__stdcall': + flags |= 2 + else: + raise NotImplementedError("abi=%r" % (tp.abi,)) + self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags) + + def _emit_bytecode_PointerType(self, tp, index): + self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype]) + + _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType + _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType + + def _emit_bytecode_FunctionPtrType(self, tp, index): + raw = tp.as_raw_function() + self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw]) + + def _emit_bytecode_ArrayType(self, tp, index): + item_index = self._typesdict[tp.item] + if tp.length is None: + self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index) + elif tp.length == '...': + raise VerificationError( + "type %s badly placed: the '...' array length can only be " + "used on global arrays or on fields of structures" % ( + str(tp).replace('/*...*/', '...'),)) + else: + assert self.cffi_types[index + 1] == 'LEN' + self.cffi_types[index] = CffiOp(OP_ARRAY, item_index) + self.cffi_types[index + 1] = CffiOp(None, str(tp.length)) + + def _emit_bytecode_StructType(self, tp, index): + struct_index = self._struct_unions[tp] + self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index) + _emit_bytecode_UnionType = _emit_bytecode_StructType + + def _emit_bytecode_EnumType(self, tp, index): + enum_index = self._enums[tp] + self.cffi_types[index] = CffiOp(OP_ENUM, enum_index) + + +if sys.version_info >= (3,): + NativeIO = io.StringIO +else: + class NativeIO(io.BytesIO): + def write(self, s): + if isinstance(s, unicode): + s = s.encode('ascii') + super().write(s) + +def _is_file_like(maybefile): + # compare to xml.etree.ElementTree._get_writer + return hasattr(maybefile, 'write') + +def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose): + if verbose: + print("generating %s" % (target_file,)) + recompiler = Recompiler(ffi, module_name, + target_is_python=(preamble is None)) + recompiler.collect_type_table() + recompiler.collect_step_tables() + if _is_file_like(target_file): + recompiler.write_source_to_f(target_file, preamble) + return True + f = NativeIO() + recompiler.write_source_to_f(f, preamble) + output = f.getvalue() + try: + with open(target_file, 'r') as f1: + if f1.read(len(output) + 1) != output: + raise OSError + if verbose: + print("(already up-to-date)") + return False # already up-to-date + except OSError: + tmp_file = '%s.~%d' % (target_file, os.getpid()) + with open(tmp_file, 'w') as f1: + f1.write(output) + try: + os.rename(tmp_file, target_file) + except OSError: + os.unlink(target_file) + os.rename(tmp_file, target_file) + return True + +def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False): + assert preamble is not None + return _make_c_or_py_source(ffi, module_name, preamble, target_c_file, + verbose) + +def make_py_source(ffi, module_name, target_py_file, verbose=False): + return _make_c_or_py_source(ffi, module_name, None, target_py_file, + verbose) + +def _modname_to_file(outputdir, modname, extension): + parts = modname.split('.') + try: + os.makedirs(os.path.join(outputdir, *parts[:-1])) + except OSError: + pass + parts[-1] += extension + return os.path.join(outputdir, *parts), parts + + +# Aaargh. Distutils is not tested at all for the purpose of compiling +# DLLs that are not extension modules. Here are some hacks to work +# around that, in the _patch_for_*() functions... + +def _patch_meth(patchlist, cls, name, new_meth): + old = getattr(cls, name) + patchlist.append((cls, name, old)) + setattr(cls, name, new_meth) + return old + +def _unpatch_meths(patchlist): + for cls, name, old_meth in reversed(patchlist): + setattr(cls, name, old_meth) + +def _patch_for_embedding(patchlist): + if sys.platform == 'win32': + # we must not remove the manifest when building for embedding! + # FUTURE: this module was removed in setuptools 74; this is likely dead code and should be removed, + # since the toolchain it supports (VS2005-2008) is also long dead. + from cffi._shimmed_dist_utils import MSVCCompiler + if MSVCCompiler is not None: + _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref', + lambda self, manifest_file: manifest_file) + + if sys.platform == 'darwin': + # we must not make a '-bundle', but a '-dynamiclib' instead + from cffi._shimmed_dist_utils import CCompiler + def my_link_shared_object(self, *args, **kwds): + if '-bundle' in self.linker_so: + self.linker_so = list(self.linker_so) + i = self.linker_so.index('-bundle') + self.linker_so[i] = '-dynamiclib' + return old_link_shared_object(self, *args, **kwds) + old_link_shared_object = _patch_meth(patchlist, CCompiler, + 'link_shared_object', + my_link_shared_object) + +def _patch_for_target(patchlist, target): + from cffi._shimmed_dist_utils import build_ext + # if 'target' is different from '*', we need to patch some internal + # method to just return this 'target' value, instead of having it + # built from module_name + if target.endswith('.*'): + target = target[:-2] + if sys.platform == 'win32': + target += '.dll' + elif sys.platform == 'darwin': + target += '.dylib' + else: + target += '.so' + _patch_meth(patchlist, build_ext, 'get_ext_filename', + lambda self, ext_name: target) + + +def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True, + c_file=None, source_extension='.c', extradir=None, + compiler_verbose=1, target=None, debug=None, + uses_ffiplatform=True, **kwds): + if not isinstance(module_name, str): + module_name = module_name.encode('ascii') + if ffi._windows_unicode: + ffi._apply_windows_unicode(kwds) + if preamble is not None: + if call_c_compiler and _is_file_like(c_file): + raise TypeError("Writing to file-like objects is not supported " + "with call_c_compiler=True") + embedding = (ffi._embedding is not None) + if embedding: + ffi._apply_embedding_fix(kwds) + if c_file is None: + c_file, parts = _modname_to_file(tmpdir, module_name, + source_extension) + if extradir: + parts = [extradir] + parts + ext_c_file = os.path.join(*parts) + else: + ext_c_file = c_file + # + if target is None: + if embedding: + target = '%s.*' % module_name + else: + target = '*' + # + if uses_ffiplatform: + ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds) + else: + ext = None + updated = make_c_source(ffi, module_name, preamble, c_file, + verbose=compiler_verbose) + if call_c_compiler: + patchlist = [] + cwd = os.getcwd() + try: + if embedding: + _patch_for_embedding(patchlist) + if target != '*': + _patch_for_target(patchlist, target) + if compiler_verbose: + if tmpdir == '.': + msg = 'the current directory is' + else: + msg = 'setting the current directory to' + print('%s %r' % (msg, os.path.abspath(tmpdir))) + os.chdir(tmpdir) + outputfilename = ffiplatform.compile('.', ext, + compiler_verbose, debug) + finally: + os.chdir(cwd) + _unpatch_meths(patchlist) + return outputfilename + else: + return ext, updated + else: + if c_file is None: + c_file, _ = _modname_to_file(tmpdir, module_name, '.py') + updated = make_py_source(ffi, module_name, c_file, + verbose=compiler_verbose) + if call_c_compiler: + return c_file + else: + return None, updated + diff --git a/venv/lib/python3.12/site-packages/cffi/setuptools_ext.py b/venv/lib/python3.12/site-packages/cffi/setuptools_ext.py new file mode 100644 index 0000000..946afb4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/setuptools_ext.py @@ -0,0 +1,231 @@ +import os +import sys +import sysconfig + +try: + basestring +except NameError: + # Python 3.x + basestring = str + +def error(msg): + from cffi._shimmed_dist_utils import DistutilsSetupError + raise DistutilsSetupError(msg) + + +def execfile(filename, glob): + # We use execfile() (here rewritten for Python 3) instead of + # __import__() to load the build script. The problem with + # a normal import is that in some packages, the intermediate + # __init__.py files may already try to import the file that + # we are generating. + with open(filename) as f: + src = f.read() + src += '\n' # Python 2.6 compatibility + code = compile(src, filename, 'exec') + exec(code, glob, glob) + + +def add_cffi_module(dist, mod_spec): + from cffi.api import FFI + + if not isinstance(mod_spec, basestring): + error("argument to 'cffi_modules=...' must be a str or a list of str," + " not %r" % (type(mod_spec).__name__,)) + mod_spec = str(mod_spec) + try: + build_file_name, ffi_var_name = mod_spec.split(':') + except ValueError: + error("%r must be of the form 'path/build.py:ffi_variable'" % + (mod_spec,)) + if not os.path.exists(build_file_name): + ext = '' + rewritten = build_file_name.replace('.', '/') + '.py' + if os.path.exists(rewritten): + ext = ' (rewrite cffi_modules to [%r])' % ( + rewritten + ':' + ffi_var_name,) + error("%r does not name an existing file%s" % (build_file_name, ext)) + + mod_vars = {'__name__': '__cffi__', '__file__': build_file_name} + execfile(build_file_name, mod_vars) + + try: + ffi = mod_vars[ffi_var_name] + except KeyError: + error("%r: object %r not found in module" % (mod_spec, + ffi_var_name)) + if not isinstance(ffi, FFI): + ffi = ffi() # maybe it's a function instead of directly an ffi + if not isinstance(ffi, FFI): + error("%r is not an FFI instance (got %r)" % (mod_spec, + type(ffi).__name__)) + if not hasattr(ffi, '_assigned_source'): + error("%r: the set_source() method was not called" % (mod_spec,)) + module_name, source, source_extension, kwds = ffi._assigned_source + if ffi._windows_unicode: + kwds = kwds.copy() + ffi._apply_windows_unicode(kwds) + + if source is None: + _add_py_module(dist, ffi, module_name) + else: + _add_c_module(dist, ffi, module_name, source, source_extension, kwds) + +def _set_py_limited_api(Extension, kwds): + """ + Add py_limited_api to kwds if setuptools >= 26 is in use. + Do not alter the setting if it already exists. + Setuptools takes care of ignoring the flag on Python 2 and PyPy. + + CPython itself should ignore the flag in a debugging version + (by not listing .abi3.so in the extensions it supports), but + it doesn't so far, creating troubles. That's why we check + for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent + of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) + + On Windows, with CPython <= 3.4, it's better not to use py_limited_api + because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. + Recently (2020) we started shipping only >= 3.5 wheels, though. So + we'll give it another try and set py_limited_api on Windows >= 3.5. + """ + from cffi._shimmed_dist_utils import log + from cffi import recompiler + + if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') + and recompiler.USE_LIMITED_API): + import setuptools + try: + setuptools_major_version = int(setuptools.__version__.partition('.')[0]) + if setuptools_major_version >= 26: + kwds['py_limited_api'] = True + except ValueError: # certain development versions of setuptools + # If we don't know the version number of setuptools, we + # try to set 'py_limited_api' anyway. At worst, we get a + # warning. + kwds['py_limited_api'] = True + + if sysconfig.get_config_var("Py_GIL_DISABLED"): + if sys.version_info < (3, 15): + if kwds.get('py_limited_api'): + log.info("Ignoring py_limited_api=True for free-threaded build.") + kwds['py_limited_api'] = False + else: + kwds.setdefault("define_macros", []).append(("Py_TARGET_ABI3T", "0x030f0000")) + + if kwds.get('py_limited_api') is False: + # avoid setting Py_LIMITED_API if py_limited_api=False + # which _cffi_include.h does unless _CFFI_NO_LIMITED_API is defined + kwds.setdefault("define_macros", []).append(("_CFFI_NO_LIMITED_API", None)) + return kwds + +def _add_c_module(dist, ffi, module_name, source, source_extension, kwds): + # We are a setuptools extension. Need this build_ext for py_limited_api. + from setuptools.command.build_ext import build_ext + from cffi._shimmed_dist_utils import Extension, log, mkpath + from cffi import recompiler + + allsources = ['$PLACEHOLDER'] + allsources.extend(kwds.pop('sources', [])) + kwds = _set_py_limited_api(Extension, kwds) + ext = Extension(name=module_name, sources=allsources, **kwds) + + def make_mod(tmpdir, pre_run=None): + c_file = os.path.join(tmpdir, module_name + source_extension) + log.info("generating cffi module %r" % c_file) + mkpath(tmpdir) + # a setuptools-only, API-only hook: called with the "ext" and "ffi" + # arguments just before we turn the ffi into C code. To use it, + # subclass the 'distutils.command.build_ext.build_ext' class and + # add a method 'def pre_run(self, ext, ffi)'. + if pre_run is not None: + pre_run(ext, ffi) + updated = recompiler.make_c_source(ffi, module_name, source, c_file) + if not updated: + log.info("already up-to-date") + return c_file + + if dist.ext_modules is None: + dist.ext_modules = [] + dist.ext_modules.append(ext) + + base_class = dist.cmdclass.get('build_ext', build_ext) + class build_ext_make_mod(base_class): + def run(self): + if ext.sources[0] == '$PLACEHOLDER': + pre_run = getattr(self, 'pre_run', None) + ext.sources[0] = make_mod(self.build_temp, pre_run) + base_class.run(self) + dist.cmdclass['build_ext'] = build_ext_make_mod + # NB. multiple runs here will create multiple 'build_ext_make_mod' + # classes. Even in this case the 'build_ext' command should be + # run once; but just in case, the logic above does nothing if + # called again. + + +def _add_py_module(dist, ffi, module_name): + from setuptools.command.build_py import build_py + from setuptools.command.build_ext import build_ext + from cffi._shimmed_dist_utils import log, mkpath + from cffi import recompiler + + def generate_mod(py_file): + log.info("generating cffi module %r" % py_file) + mkpath(os.path.dirname(py_file)) + updated = recompiler.make_py_source(ffi, module_name, py_file) + if not updated: + log.info("already up-to-date") + + base_class = dist.cmdclass.get('build_py', build_py) + class build_py_make_mod(base_class): + def run(self): + base_class.run(self) + module_path = module_name.split('.') + module_path[-1] += '.py' + generate_mod(os.path.join(self.build_lib, *module_path)) + def get_source_files(self): + # This is called from 'setup.py sdist' only. Exclude + # the generate .py module in this case. + saved_py_modules = self.py_modules + try: + if saved_py_modules: + self.py_modules = [m for m in saved_py_modules + if m != module_name] + return base_class.get_source_files(self) + finally: + self.py_modules = saved_py_modules + dist.cmdclass['build_py'] = build_py_make_mod + + # distutils and setuptools have no notion I could find of a + # generated python module. If we don't add module_name to + # dist.py_modules, then things mostly work but there are some + # combination of options (--root and --record) that will miss + # the module. So we add it here, which gives a few apparently + # harmless warnings about not finding the file outside the + # build directory. + # Then we need to hack more in get_source_files(); see above. + if dist.py_modules is None: + dist.py_modules = [] + dist.py_modules.append(module_name) + + # the following is only for "build_ext -i" + base_class_2 = dist.cmdclass.get('build_ext', build_ext) + class build_ext_make_mod(base_class_2): + def run(self): + base_class_2.run(self) + if self.inplace: + # from get_ext_fullpath() in distutils/command/build_ext.py + module_path = module_name.split('.') + package = '.'.join(module_path[:-1]) + build_py = self.get_finalized_command('build_py') + package_dir = build_py.get_package_dir(package) + file_name = module_path[-1] + '.py' + generate_mod(os.path.join(package_dir, file_name)) + dist.cmdclass['build_ext'] = build_ext_make_mod + +def cffi_modules(dist, attr, value): + assert attr == 'cffi_modules' + if isinstance(value, basestring): + value = [value] + + for cffi_module in value: + add_cffi_module(dist, cffi_module) diff --git a/venv/lib/python3.12/site-packages/cffi/vengine_cpy.py b/venv/lib/python3.12/site-packages/cffi/vengine_cpy.py new file mode 100644 index 0000000..4d2ea43 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/vengine_cpy.py @@ -0,0 +1,1058 @@ +# +# DEPRECATED: implementation for ffi.verify() +# +import sys +from . import model +from .error import VerificationError +from . import _imp_emulation as imp + + +class VCPythonEngine: + _class_key = 'x' + _gen_python_module = True + + def __init__(self, verifier): + self.verifier = verifier + self.ffi = verifier.ffi + self._struct_pending_verification = {} + self._types_of_builtin_functions = {} + + def patch_extension_kwds(self, kwds): + pass + + def find_module(self, module_name, path, so_suffixes): + try: + f, filename, descr = imp.find_module(module_name, path) + except ImportError: + return None + if f is not None: + f.close() + # Note that after a setuptools installation, there are both .py + # and .so files with the same basename. The code here relies on + # imp.find_module() locating the .so in priority. + if descr[0] not in so_suffixes: + return None + return filename + + def collect_types(self): + self._typesdict = {} + self._generate("collecttype") + + def _prnt(self, what=''): + self._f.write(what + '\n') + + def _gettypenum(self, type): + # a KeyError here is a bug. please report it! :-) + return self._typesdict[type] + + def _do_collect_type(self, tp): + if ((not isinstance(tp, model.PrimitiveType) + or tp.name == 'long double') + and tp not in self._typesdict): + num = len(self._typesdict) + self._typesdict[tp] = num + + def write_source_to_f(self): + self.collect_types() + # + # The new module will have a _cffi_setup() function that receives + # objects from the ffi world, and that calls some setup code in + # the module. This setup code is split in several independent + # functions, e.g. one per constant. The functions are "chained" + # by ending in a tail call to each other. + # + # This is further split in two chained lists, depending on if we + # can do it at import-time or if we must wait for _cffi_setup() to + # provide us with the objects. This is needed because we + # need the values of the enum constants in order to build the + # that we may have to pass to _cffi_setup(). + # + # The following two 'chained_list_constants' items contains + # the head of these two chained lists, as a string that gives the + # call to do, if any. + self._chained_list_constants = ['((void)lib,0)', '((void)lib,0)'] + # + prnt = self._prnt + # first paste some standard set of lines that are mostly '#define' + prnt(cffimod_header) + prnt() + # then paste the C source given by the user, verbatim. + prnt(self.verifier.preamble) + prnt() + # + # call generate_cpy_xxx_decl(), for every xxx found from + # ffi._parser._declarations. This generates all the functions. + self._generate("decl") + # + # implement the function _cffi_setup_custom() as calling the + # head of the chained list. + self._generate_setup_custom() + prnt() + # + # produce the method table, including the entries for the + # generated Python->C function wrappers, which are done + # by generate_cpy_function_method(). + prnt('static PyMethodDef _cffi_methods[] = {') + self._generate("method") + prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS, NULL},') + prnt(' {NULL, NULL, 0, NULL} /* Sentinel */') + prnt('};') + prnt() + # + # standard init. + modname = self.verifier.get_module_name() + constants = self._chained_list_constants[False] + prnt('static struct PyModuleDef _cffi_module_def = {') + prnt(' PyModuleDef_HEAD_INIT,') + prnt(' "%s",' % modname) + prnt(' NULL,') + prnt(' -1,') + prnt(' _cffi_methods,') + prnt(' NULL, NULL, NULL, NULL') + prnt('};') + prnt() + prnt('PyMODINIT_FUNC') + prnt('PyInit_%s(void)' % modname) + prnt('{') + prnt(' PyObject *lib;') + prnt(' lib = PyModule_Create(&_cffi_module_def);') + prnt(' if (lib == NULL)') + prnt(' return NULL;') + prnt(' if (%s < 0 || _cffi_init() < 0) {' % (constants,)) + prnt(' Py_DECREF(lib);') + prnt(' return NULL;') + prnt(' }') + prnt('#if Py_GIL_DISABLED') + prnt(' PyUnstable_Module_SetGIL(lib, Py_MOD_GIL_NOT_USED);') + prnt('#endif') + prnt(' return lib;') + prnt('}') + prnt() + + def load_library(self, flags=None): + # XXX review all usages of 'self' here! + # import it as a new extension module + imp.acquire_lock() + try: + if hasattr(sys, "getdlopenflags"): + previous_flags = sys.getdlopenflags() + try: + if hasattr(sys, "setdlopenflags") and flags is not None: + sys.setdlopenflags(flags) + module = imp.load_dynamic(self.verifier.get_module_name(), + self.verifier.modulefilename) + except ImportError as e: + error = "importing %r: %s" % (self.verifier.modulefilename, e) + raise VerificationError(error) + finally: + if hasattr(sys, "setdlopenflags"): + sys.setdlopenflags(previous_flags) + finally: + imp.release_lock() + # + # call loading_cpy_struct() to get the struct layout inferred by + # the C compiler + self._load(module, 'loading') + # + # the C code will need the objects. Collect them in + # order in a list. + revmapping = dict([(value, key) + for (key, value) in self._typesdict.items()]) + lst = [revmapping[i] for i in range(len(revmapping))] + lst = list(map(self.ffi._get_cached_btype, lst)) + # + # build the FFILibrary class and instance and call _cffi_setup(). + # this will set up some fields like '_cffi_types', and only then + # it will invoke the chained list of functions that will really + # build (notably) the constant objects, as if they are + # pointers, and store them as attributes on the 'library' object. + class FFILibrary: + _cffi_python_module = module + _cffi_ffi = self.ffi + _cffi_dir = [] + def __dir__(self): + return FFILibrary._cffi_dir + list(self.__dict__) + library = FFILibrary() + if module._cffi_setup(lst, VerificationError, library): + import warnings + warnings.warn("reimporting %r might overwrite older definitions" + % (self.verifier.get_module_name())) + # + # finally, call the loaded_cpy_xxx() functions. This will perform + # the final adjustments, like copying the Python->C wrapper + # functions from the module to the 'library' object, and setting + # up the FFILibrary class with properties for the global C variables. + self._load(module, 'loaded', library=library) + module._cffi_original_ffi = self.ffi + module._cffi_types_of_builtin_funcs = self._types_of_builtin_functions + return library + + def _get_declarations(self): + lst = [(key, tp) for (key, (tp, qual)) in + self.ffi._parser._declarations.items()] + lst.sort() + return lst + + def _generate(self, step_name): + for name, tp in self._get_declarations(): + kind, realname = name.split(' ', 1) + try: + method = getattr(self, '_generate_cpy_%s_%s' % (kind, + step_name)) + except AttributeError: + raise VerificationError( + "not implemented in verify(): %r" % name) + try: + method(tp, realname) + except Exception as e: + model.attach_exception_info(e, name) + raise + + def _load(self, module, step_name, **kwds): + for name, tp in self._get_declarations(): + kind, realname = name.split(' ', 1) + method = getattr(self, '_%s_cpy_%s' % (step_name, kind)) + try: + method(tp, realname, module, **kwds) + except Exception as e: + model.attach_exception_info(e, name) + raise + + def _generate_nothing(self, tp, name): + pass + + def _loaded_noop(self, tp, name, module, **kwds): + pass + + # ---------- + + def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): + extraarg = '' + if isinstance(tp, model.PrimitiveType): + if tp.is_integer_type() and tp.name != '_Bool': + converter = '_cffi_to_c_int' + extraarg = ', %s' % tp.name + elif tp.is_complex_type(): + raise VerificationError( + "not implemented in verify(): complex types") + else: + converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''), + tp.name.replace(' ', '_')) + errvalue = '-1' + # + elif isinstance(tp, model.PointerType): + self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, + tovar, errcode) + return + # + elif isinstance(tp, (model.StructOrUnion, model.EnumType)): + # a struct (not a struct pointer) as a function argument + self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' + % (tovar, self._gettypenum(tp), fromvar)) + self._prnt(' %s;' % errcode) + return + # + elif isinstance(tp, model.FunctionPtrType): + converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') + extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) + errvalue = 'NULL' + # + else: + raise NotImplementedError(tp) + # + self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) + self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( + tovar, tp.get_c_name(''), errvalue)) + self._prnt(' %s;' % errcode) + + def _extra_local_variables(self, tp, localvars, freelines): + if isinstance(tp, model.PointerType): + localvars.add('Py_ssize_t datasize') + localvars.add('struct _cffi_freeme_s *large_args_free = NULL') + freelines.add('if (large_args_free != NULL)' + ' _cffi_free_array_arguments(large_args_free);') + + def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): + self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') + self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( + self._gettypenum(tp), fromvar, tovar)) + self._prnt(' if (datasize != 0) {') + self._prnt(' %s = ((size_t)datasize) <= 640 ? ' + 'alloca((size_t)datasize) : NULL;' % (tovar,)) + self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' + '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) + self._prnt(' datasize, &large_args_free) < 0)') + self._prnt(' %s;' % errcode) + self._prnt(' }') + + def _convert_expr_from_c(self, tp, var, context): + if isinstance(tp, model.PrimitiveType): + if tp.is_integer_type() and tp.name != '_Bool': + return '_cffi_from_c_int(%s, %s)' % (var, tp.name) + elif tp.name != 'long double': + return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var) + else: + return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): + return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, model.ArrayType): + return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( + var, self._gettypenum(model.PointerType(tp.item))) + elif isinstance(tp, model.StructOrUnion): + if tp.fldnames is None: + raise TypeError("'%s' is used as %s, but is opaque" % ( + tp._get_c_name(), context)) + return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, model.EnumType): + return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + else: + raise NotImplementedError(tp) + + # ---------- + # typedefs: generates no code so far + + _generate_cpy_typedef_collecttype = _generate_nothing + _generate_cpy_typedef_decl = _generate_nothing + _generate_cpy_typedef_method = _generate_nothing + _loading_cpy_typedef = _loaded_noop + _loaded_cpy_typedef = _loaded_noop + + # ---------- + # function declarations + + def _generate_cpy_function_collecttype(self, tp, name): + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + self._do_collect_type(tp) + else: + # don't call _do_collect_type(tp) in this common case, + # otherwise test_autofilled_struct_as_argument fails + for type in tp.args: + self._do_collect_type(type) + self._do_collect_type(tp.result) + + def _generate_cpy_function_decl(self, tp, name): + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + # cannot support vararg functions better than this: check for its + # exact type (including the fixed arguments), and build it as a + # constant function pointer (no CPython wrapper) + self._generate_cpy_const(False, name, tp) + return + prnt = self._prnt + numargs = len(tp.args) + if numargs == 0: + argname = 'noarg' + elif numargs == 1: + argname = 'arg0' + else: + argname = 'args' + prnt('static PyObject *') + prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) + prnt('{') + # + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + prnt(' %s;' % type.get_c_name(' x%d' % i, context)) + # + localvars = set() + freelines = set() + for type in tp.args: + self._extra_local_variables(type, localvars, freelines) + for decl in sorted(localvars): + prnt(' %s;' % (decl,)) + # + if not isinstance(tp.result, model.VoidType): + result_code = 'result = ' + context = 'result of %s' % name + prnt(' %s;' % tp.result.get_c_name(' result', context)) + prnt(' PyObject *pyresult;') + else: + result_code = '' + # + if len(tp.args) > 1: + rng = range(len(tp.args)) + for i in rng: + prnt(' PyObject *arg%d;' % i) + prnt() + prnt(' if (!PyArg_ParseTuple(args, "%s:%s", %s))' % ( + 'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng]))) + prnt(' return NULL;') + prnt() + # + for i, type in enumerate(tp.args): + self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, + 'return NULL') + prnt() + # + prnt(' Py_BEGIN_ALLOW_THREADS') + prnt(' _cffi_restore_errno();') + prnt(' { %s%s(%s); }' % ( + result_code, name, + ', '.join(['x%d' % i for i in range(len(tp.args))]))) + prnt(' _cffi_save_errno();') + prnt(' Py_END_ALLOW_THREADS') + prnt() + # + prnt(' (void)self; /* unused */') + if numargs == 0: + prnt(' (void)noarg; /* unused */') + if result_code: + prnt(' pyresult = %s;' % + self._convert_expr_from_c(tp.result, 'result', 'result type')) + for freeline in freelines: + prnt(' ' + freeline) + prnt(' return pyresult;') + else: + for freeline in freelines: + prnt(' ' + freeline) + prnt(' Py_INCREF(Py_None);') + prnt(' return Py_None;') + prnt('}') + prnt() + + def _generate_cpy_function_method(self, tp, name): + if tp.ellipsis: + return + numargs = len(tp.args) + if numargs == 0: + meth = 'METH_NOARGS' + elif numargs == 1: + meth = 'METH_O' + else: + meth = 'METH_VARARGS' + self._prnt(' {"%s", _cffi_f_%s, %s, NULL},' % (name, name, meth)) + + _loading_cpy_function = _loaded_noop + + def _loaded_cpy_function(self, tp, name, module, library): + if tp.ellipsis: + return + func = getattr(module, name) + setattr(library, name, func) + self._types_of_builtin_functions[func] = tp + + # ---------- + # named structs + + _generate_cpy_struct_collecttype = _generate_nothing + def _generate_cpy_struct_decl(self, tp, name): + assert name == tp.name + self._generate_struct_or_union_decl(tp, 'struct', name) + def _generate_cpy_struct_method(self, tp, name): + self._generate_struct_or_union_method(tp, 'struct', name) + def _loading_cpy_struct(self, tp, name, module): + self._loading_struct_or_union(tp, 'struct', name, module) + def _loaded_cpy_struct(self, tp, name, module, **kwds): + self._loaded_struct_or_union(tp) + + _generate_cpy_union_collecttype = _generate_nothing + def _generate_cpy_union_decl(self, tp, name): + assert name == tp.name + self._generate_struct_or_union_decl(tp, 'union', name) + def _generate_cpy_union_method(self, tp, name): + self._generate_struct_or_union_method(tp, 'union', name) + def _loading_cpy_union(self, tp, name, module): + self._loading_struct_or_union(tp, 'union', name, module) + def _loaded_cpy_union(self, tp, name, module, **kwds): + self._loaded_struct_or_union(tp) + + def _generate_struct_or_union_decl(self, tp, prefix, name): + if tp.fldnames is None: + return # nothing to do with opaque structs + checkfuncname = '_cffi_check_%s_%s' % (prefix, name) + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + cname = ('%s %s' % (prefix, name)).strip() + # + prnt = self._prnt + prnt('static void %s(%s *p)' % (checkfuncname, cname)) + prnt('{') + prnt(' /* only to generate compile-time warnings or errors */') + prnt(' (void)p;') + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if (isinstance(ftype, model.PrimitiveType) + and ftype.is_integer_type()) or fbitsize >= 0: + # accept all integers, but complain on float or double + prnt(' (void)((p->%s) << 1);' % fname) + else: + # only accept exactly the type declared. + try: + prnt(' { %s = &p->%s; (void)tmp; }' % ( + ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), + fname)) + except VerificationError as e: + prnt(' /* %s */' % str(e)) # cannot verify it, ignore + prnt('}') + prnt('static PyObject *') + prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,)) + prnt('{') + prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) + prnt(' static Py_ssize_t nums[] = {') + prnt(' sizeof(%s),' % cname) + prnt(' offsetof(struct _cffi_aligncheck, y),') + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if fbitsize >= 0: + continue # xxx ignore fbitsize for now + prnt(' offsetof(%s, %s),' % (cname, fname)) + if isinstance(ftype, model.ArrayType) and ftype.length is None: + prnt(' 0, /* %s */' % ftype._get_c_name()) + else: + prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) + prnt(' -1') + prnt(' };') + prnt(' (void)self; /* unused */') + prnt(' (void)noarg; /* unused */') + prnt(' return _cffi_get_struct_layout(nums);') + prnt(' /* the next line is not executed, but compiled */') + prnt(' %s(0);' % (checkfuncname,)) + prnt('}') + prnt() + + def _generate_struct_or_union_method(self, tp, prefix, name): + if tp.fldnames is None: + return # nothing to do with opaque structs + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + self._prnt(' {"%s", %s, METH_NOARGS, NULL},' % (layoutfuncname, + layoutfuncname)) + + def _loading_struct_or_union(self, tp, prefix, name, module): + if tp.fldnames is None: + return # nothing to do with opaque structs + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + # + function = getattr(module, layoutfuncname) + layout = function() + if isinstance(tp, model.StructOrUnion) and tp.partial: + # use the function()'s sizes and offsets to guide the + # layout of the struct + totalsize = layout[0] + totalalignment = layout[1] + fieldofs = layout[2::2] + fieldsize = layout[3::2] + tp.force_flatten() + assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) + tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment + else: + cname = ('%s %s' % (prefix, name)).strip() + self._struct_pending_verification[tp] = layout, cname + + def _loaded_struct_or_union(self, tp): + if tp.fldnames is None: + return # nothing to do with opaque structs + self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered + + if tp in self._struct_pending_verification: + # check that the layout sizes and offsets match the real ones + def check(realvalue, expectedvalue, msg): + if realvalue != expectedvalue: + raise VerificationError( + "%s (we have %d, but C compiler says %d)" + % (msg, expectedvalue, realvalue)) + ffi = self.ffi + BStruct = ffi._get_cached_btype(tp) + layout, cname = self._struct_pending_verification.pop(tp) + check(layout[0], ffi.sizeof(BStruct), "wrong total size") + check(layout[1], ffi.alignof(BStruct), "wrong total alignment") + i = 2 + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if fbitsize >= 0: + continue # xxx ignore fbitsize for now + check(layout[i], ffi.offsetof(BStruct, fname), + "wrong offset for field %r" % (fname,)) + if layout[i+1] != 0: + BField = ffi._get_cached_btype(ftype) + check(layout[i+1], ffi.sizeof(BField), + "wrong size for field %r" % (fname,)) + i += 2 + assert i == len(layout) + + # ---------- + # 'anonymous' declarations. These are produced for anonymous structs + # or unions; the 'name' is obtained by a typedef. + + _generate_cpy_anonymous_collecttype = _generate_nothing + + def _generate_cpy_anonymous_decl(self, tp, name): + if isinstance(tp, model.EnumType): + self._generate_cpy_enum_decl(tp, name, '') + else: + self._generate_struct_or_union_decl(tp, '', name) + + def _generate_cpy_anonymous_method(self, tp, name): + if not isinstance(tp, model.EnumType): + self._generate_struct_or_union_method(tp, '', name) + + def _loading_cpy_anonymous(self, tp, name, module): + if isinstance(tp, model.EnumType): + self._loading_cpy_enum(tp, name, module) + else: + self._loading_struct_or_union(tp, '', name, module) + + def _loaded_cpy_anonymous(self, tp, name, module, **kwds): + if isinstance(tp, model.EnumType): + self._loaded_cpy_enum(tp, name, module, **kwds) + else: + self._loaded_struct_or_union(tp) + + # ---------- + # constants, likely declared with '#define' + + def _generate_cpy_const(self, is_int, name, tp=None, category='const', + vartp=None, delayed=True, size_too=False, + check_value=None): + prnt = self._prnt + funcname = '_cffi_%s_%s' % (category, name) + prnt('static int %s(PyObject *lib)' % funcname) + prnt('{') + prnt(' PyObject *o;') + prnt(' int res;') + if not is_int: + prnt(' %s;' % (vartp or tp).get_c_name(' i', name)) + else: + assert category == 'const' + # + if check_value is not None: + self._check_int_constant_value(name, check_value) + # + if not is_int: + if category == 'var': + realexpr = '&' + name + else: + realexpr = name + prnt(' i = (%s);' % (realexpr,)) + prnt(' o = %s;' % (self._convert_expr_from_c(tp, 'i', + 'variable type'),)) + assert delayed + else: + prnt(' o = _cffi_from_c_int_const(%s);' % name) + prnt(' if (o == NULL)') + prnt(' return -1;') + if size_too: + prnt(' {') + prnt(' PyObject *o1 = o;') + prnt(' o = Py_BuildValue("On", o1, (Py_ssize_t)sizeof(%s));' + % (name,)) + prnt(' Py_DECREF(o1);') + prnt(' if (o == NULL)') + prnt(' return -1;') + prnt(' }') + prnt(' res = PyObject_SetAttrString(lib, "%s", o);' % name) + prnt(' Py_DECREF(o);') + prnt(' if (res < 0)') + prnt(' return -1;') + prnt(' return %s;' % self._chained_list_constants[delayed]) + self._chained_list_constants[delayed] = funcname + '(lib)' + prnt('}') + prnt() + + def _generate_cpy_constant_collecttype(self, tp, name): + is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() + if not is_int: + self._do_collect_type(tp) + + def _generate_cpy_constant_decl(self, tp, name): + is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() + self._generate_cpy_const(is_int, name, tp) + + _generate_cpy_constant_method = _generate_nothing + _loading_cpy_constant = _loaded_noop + _loaded_cpy_constant = _loaded_noop + + # ---------- + # enums + + def _check_int_constant_value(self, name, value, err_prefix=''): + prnt = self._prnt + if value <= 0: + prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( + name, name, value)) + else: + prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( + name, name, value)) + prnt(' char buf[64];') + prnt(' if ((%s) <= 0)' % name) + prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % name) + prnt(' else') + prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' % + name) + prnt(' PyErr_Format(_cffi_VerificationError,') + prnt(' "%s%s has the real value %s, not %s",') + prnt(' "%s", "%s", buf, "%d");' % ( + err_prefix, name, value)) + prnt(' return -1;') + prnt(' }') + + def _enum_funcname(self, prefix, name): + # "$enum_$1" => "___D_enum____D_1" + name = name.replace('$', '___D_') + return '_cffi_e_%s_%s' % (prefix, name) + + def _generate_cpy_enum_decl(self, tp, name, prefix='enum'): + if tp.partial: + for enumerator in tp.enumerators: + self._generate_cpy_const(True, enumerator, delayed=False) + return + # + funcname = self._enum_funcname(prefix, name) + prnt = self._prnt + prnt('static int %s(PyObject *lib)' % funcname) + prnt('{') + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + self._check_int_constant_value(enumerator, enumvalue, + "enum %s: " % name) + prnt(' return %s;' % self._chained_list_constants[True]) + self._chained_list_constants[True] = funcname + '(lib)' + prnt('}') + prnt() + + _generate_cpy_enum_collecttype = _generate_nothing + _generate_cpy_enum_method = _generate_nothing + + def _loading_cpy_enum(self, tp, name, module): + if tp.partial: + enumvalues = [getattr(module, enumerator) + for enumerator in tp.enumerators] + tp.enumvalues = tuple(enumvalues) + tp.partial_resolved = True + + def _loaded_cpy_enum(self, tp, name, module, library): + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + setattr(library, enumerator, enumvalue) + + # ---------- + # macros: for now only for integers + + def _generate_cpy_macro_decl(self, tp, name): + if tp == '...': + check_value = None + else: + check_value = tp # an integer + self._generate_cpy_const(True, name, check_value=check_value) + + _generate_cpy_macro_collecttype = _generate_nothing + _generate_cpy_macro_method = _generate_nothing + _loading_cpy_macro = _loaded_noop + _loaded_cpy_macro = _loaded_noop + + # ---------- + # global variables + + def _generate_cpy_variable_collecttype(self, tp, name): + if isinstance(tp, model.ArrayType): + tp_ptr = model.PointerType(tp.item) + else: + tp_ptr = model.PointerType(tp) + self._do_collect_type(tp_ptr) + + def _generate_cpy_variable_decl(self, tp, name): + if isinstance(tp, model.ArrayType): + tp_ptr = model.PointerType(tp.item) + self._generate_cpy_const(False, name, tp, vartp=tp_ptr, + size_too = tp.length_is_unknown()) + else: + tp_ptr = model.PointerType(tp) + self._generate_cpy_const(False, name, tp_ptr, category='var') + + _generate_cpy_variable_method = _generate_nothing + _loading_cpy_variable = _loaded_noop + + def _loaded_cpy_variable(self, tp, name, module, library): + value = getattr(library, name) + if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the + # sense that "a=..." is forbidden + if tp.length_is_unknown(): + assert isinstance(value, tuple) + (value, size) = value + BItemType = self.ffi._get_cached_btype(tp.item) + length, rest = divmod(size, self.ffi.sizeof(BItemType)) + if rest != 0: + raise VerificationError( + "bad size: %r does not seem to be an array of %s" % + (name, tp.item)) + tp = tp.resolve_length(length) + # 'value' is a which we have to replace with + # a if the N is actually known + if tp.length is not None: + BArray = self.ffi._get_cached_btype(tp) + value = self.ffi.cast(BArray, value) + setattr(library, name, value) + return + # remove ptr= from the library instance, and replace + # it by a property on the class, which reads/writes into ptr[0]. + ptr = value + delattr(library, name) + def getter(library): + return ptr[0] + def setter(library, value): + ptr[0] = value + setattr(type(library), name, property(getter, setter)) + type(library)._cffi_dir.append(name) + + # ---------- + + def _generate_setup_custom(self): + prnt = self._prnt + prnt('static int _cffi_setup_custom(PyObject *lib)') + prnt('{') + prnt(' return %s;' % self._chained_list_constants[True]) + prnt('}') + +cffimod_header = r''' +#include +#include + +/* this block of #ifs should be kept exactly identical between + c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py + and cffi/_cffi_include.h */ +#if defined(_MSC_VER) +# include /* for alloca() */ +# if _MSC_VER < 1600 /* MSVC < 2010 */ + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; +# else +# include +# endif +# if _MSC_VER < 1800 /* MSVC < 2013 */ +# ifndef __cplusplus + typedef unsigned char _Bool; +# endif +# endif +# define _cffi_float_complex_t _Fcomplex /* include for it */ +# define _cffi_double_complex_t _Dcomplex /* include for it */ +#else +# include +# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) +# include +# endif +# define _cffi_float_complex_t float _Complex +# define _cffi_double_complex_t double _Complex +#endif + +#define _cffi_from_c_double PyFloat_FromDouble +#define _cffi_from_c_float PyFloat_FromDouble +#define _cffi_from_c_long PyLong_FromLong +#define _cffi_from_c_ulong PyLong_FromUnsignedLong +#define _cffi_from_c_longlong PyLong_FromLongLong +#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong +#define _cffi_from_c__Bool PyBool_FromLong + +#define _cffi_to_c_double PyFloat_AsDouble +#define _cffi_to_c_float PyFloat_AsDouble + +#define _cffi_from_c_int_const(x) \ + (((x) > 0) ? \ + ((unsigned long long)(x) <= (unsigned long long)LONG_MAX) ? \ + PyLong_FromLong((long)(x)) : \ + PyLong_FromUnsignedLongLong((unsigned long long)(x)) : \ + ((long long)(x) >= (long long)LONG_MIN) ? \ + PyLong_FromLong((long)(x)) : \ + PyLong_FromLongLong((long long)(x))) + +#define _cffi_from_c_int(x, type) \ + (((type)-1) > 0 ? /* unsigned */ \ + (sizeof(type) < sizeof(long) ? \ + PyLong_FromLong((long)x) : \ + sizeof(type) == sizeof(long) ? \ + PyLong_FromUnsignedLong((unsigned long)x) : \ + PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ + (sizeof(type) <= sizeof(long) ? \ + PyLong_FromLong((long)x) : \ + PyLong_FromLongLong((long long)x))) + +#define _cffi_to_c_int(o, type) \ + ((type)( \ + sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ + : (type)_cffi_to_c_i8(o)) : \ + sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ + : (type)_cffi_to_c_i16(o)) : \ + sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ + : (type)_cffi_to_c_i32(o)) : \ + sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ + : (type)_cffi_to_c_i64(o)) : \ + (Py_FatalError("unsupported size for type " #type), (type)0))) + +#define _cffi_to_c_i8 \ + ((int(*)(PyObject *))_cffi_exports[1]) +#define _cffi_to_c_u8 \ + ((int(*)(PyObject *))_cffi_exports[2]) +#define _cffi_to_c_i16 \ + ((int(*)(PyObject *))_cffi_exports[3]) +#define _cffi_to_c_u16 \ + ((int(*)(PyObject *))_cffi_exports[4]) +#define _cffi_to_c_i32 \ + ((int(*)(PyObject *))_cffi_exports[5]) +#define _cffi_to_c_u32 \ + ((unsigned int(*)(PyObject *))_cffi_exports[6]) +#define _cffi_to_c_i64 \ + ((long long(*)(PyObject *))_cffi_exports[7]) +#define _cffi_to_c_u64 \ + ((unsigned long long(*)(PyObject *))_cffi_exports[8]) +#define _cffi_to_c_char \ + ((int(*)(PyObject *))_cffi_exports[9]) +#define _cffi_from_c_pointer \ + ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10]) +#define _cffi_to_c_pointer \ + ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11]) +#define _cffi_get_struct_layout \ + ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12]) +#define _cffi_restore_errno \ + ((void(*)(void))_cffi_exports[13]) +#define _cffi_save_errno \ + ((void(*)(void))_cffi_exports[14]) +#define _cffi_from_c_char \ + ((PyObject *(*)(char))_cffi_exports[15]) +#define _cffi_from_c_deref \ + ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16]) +#define _cffi_to_c \ + ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17]) +#define _cffi_from_c_struct \ + ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18]) +#define _cffi_to_c_wchar_t \ + ((wchar_t(*)(PyObject *))_cffi_exports[19]) +#define _cffi_from_c_wchar_t \ + ((PyObject *(*)(wchar_t))_cffi_exports[20]) +#define _cffi_to_c_long_double \ + ((long double(*)(PyObject *))_cffi_exports[21]) +#define _cffi_to_c__Bool \ + ((_Bool(*)(PyObject *))_cffi_exports[22]) +#define _cffi_prepare_pointer_call_argument \ + ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23]) +#define _cffi_convert_array_from_object \ + ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24]) +#define _CFFI_NUM_EXPORTS 25 + +typedef struct _ctypedescr CTypeDescrObject; + +static void *_cffi_exports[_CFFI_NUM_EXPORTS]; +static PyObject *_cffi_types, *_cffi_VerificationError; + +static int _cffi_setup_custom(PyObject *lib); /* forward */ + +static PyObject *_cffi_setup(PyObject *self, PyObject *args) +{ + PyObject *library; + int was_alive = (_cffi_types != NULL); + (void)self; /* unused */ + if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError, + &library)) + return NULL; + Py_INCREF(_cffi_types); + Py_INCREF(_cffi_VerificationError); + if (_cffi_setup_custom(library) < 0) + return NULL; + return PyBool_FromLong(was_alive); +} + +union _cffi_union_alignment_u { + unsigned char m_char; + unsigned short m_short; + unsigned int m_int; + unsigned long m_long; + unsigned long long m_longlong; + float m_float; + double m_double; + long double m_longdouble; +}; + +struct _cffi_freeme_s { + struct _cffi_freeme_s *next; + union _cffi_union_alignment_u alignment; +}; + +#ifdef __GNUC__ + __attribute__((unused)) +#endif +static int _cffi_convert_array_argument(CTypeDescrObject *ctptr, PyObject *arg, + char **output_data, Py_ssize_t datasize, + struct _cffi_freeme_s **freeme) +{ + char *p; + if (datasize < 0) + return -1; + + p = *output_data; + if (p == NULL) { + struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( + offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); + if (fp == NULL) + return -1; + fp->next = *freeme; + *freeme = fp; + p = *output_data = (char *)&fp->alignment; + } + memset((void *)p, 0, (size_t)datasize); + return _cffi_convert_array_from_object(p, ctptr, arg); +} + +#ifdef __GNUC__ + __attribute__((unused)) +#endif +static void _cffi_free_array_arguments(struct _cffi_freeme_s *freeme) +{ + do { + void *p = (void *)freeme; + freeme = freeme->next; + PyObject_Free(p); + } while (freeme != NULL); +} + +static int _cffi_init(void) +{ + PyObject *module, *c_api_object = NULL; + + module = PyImport_ImportModule("_cffi_backend"); + if (module == NULL) + goto failure; + + c_api_object = PyObject_GetAttrString(module, "_C_API"); + if (c_api_object == NULL) + goto failure; + if (!PyCapsule_CheckExact(c_api_object)) { + PyErr_SetNone(PyExc_ImportError); + goto failure; + } + memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"), + _CFFI_NUM_EXPORTS * sizeof(void *)); + + Py_DECREF(module); + Py_DECREF(c_api_object); + return 0; + + failure: + Py_XDECREF(module); + Py_XDECREF(c_api_object); + return -1; +} + +#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num)) + +/**********/ +''' diff --git a/venv/lib/python3.12/site-packages/cffi/vengine_gen.py b/venv/lib/python3.12/site-packages/cffi/vengine_gen.py new file mode 100644 index 0000000..0209c79 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/vengine_gen.py @@ -0,0 +1,679 @@ +# +# DEPRECATED: implementation for ffi.verify() +# +import sys, os +import types + +from . import model +from .error import VerificationError + + +class VGenericEngine: + _class_key = 'g' + _gen_python_module = False + + def __init__(self, verifier): + self.verifier = verifier + self.ffi = verifier.ffi + self.export_symbols = [] + self._struct_pending_verification = {} + + def patch_extension_kwds(self, kwds): + # add 'export_symbols' to the dictionary. Note that we add the + # list before filling it. When we fill it, it will thus also show + # up in kwds['export_symbols']. + kwds.setdefault('export_symbols', self.export_symbols) + + def find_module(self, module_name, path, so_suffixes): + for so_suffix in so_suffixes: + basename = module_name + so_suffix + if path is None: + path = sys.path + for dirname in path: + filename = os.path.join(dirname, basename) + if os.path.isfile(filename): + return filename + + def collect_types(self): + pass # not needed in the generic engine + + def _prnt(self, what=''): + self._f.write(what + '\n') + + def write_source_to_f(self): + prnt = self._prnt + # first paste some standard set of lines that are mostly '#include' + prnt(cffimod_header) + # then paste the C source given by the user, verbatim. + prnt(self.verifier.preamble) + # + # call generate_gen_xxx_decl(), for every xxx found from + # ffi._parser._declarations. This generates all the functions. + self._generate('decl') + # + # on Windows, distutils insists on putting init_cffi_xyz in + # 'export_symbols', so instead of fighting it, just give up and + # give it one + if sys.platform == 'win32': + if sys.version_info >= (3,): + prefix = 'PyInit_' + else: + prefix = 'init' + modname = self.verifier.get_module_name() + prnt("void %s%s(void) { }\n" % (prefix, modname)) + + def load_library(self, flags=0): + # import it with the CFFI backend + backend = self.ffi._backend + # needs to make a path that contains '/', on Posix + filename = os.path.join(os.curdir, self.verifier.modulefilename) + module = backend.load_library(filename, flags) + # + # call loading_gen_struct() to get the struct layout inferred by + # the C compiler + self._load(module, 'loading') + + # build the FFILibrary class and instance, this is a module subclass + # because modules are expected to have usually-constant-attributes and + # in PyPy this means the JIT is able to treat attributes as constant, + # which we want. + class FFILibrary(types.ModuleType): + _cffi_generic_module = module + _cffi_ffi = self.ffi + _cffi_dir = [] + def __dir__(self): + return FFILibrary._cffi_dir + library = FFILibrary("") + # + # finally, call the loaded_gen_xxx() functions. This will set + # up the 'library' object. + self._load(module, 'loaded', library=library) + return library + + def _get_declarations(self): + lst = [(key, tp) for (key, (tp, qual)) in + self.ffi._parser._declarations.items()] + lst.sort() + return lst + + def _generate(self, step_name): + for name, tp in self._get_declarations(): + kind, realname = name.split(' ', 1) + try: + method = getattr(self, '_generate_gen_%s_%s' % (kind, + step_name)) + except AttributeError: + raise VerificationError( + "not implemented in verify(): %r" % name) + try: + method(tp, realname) + except Exception as e: + model.attach_exception_info(e, name) + raise + + def _load(self, module, step_name, **kwds): + for name, tp in self._get_declarations(): + kind, realname = name.split(' ', 1) + method = getattr(self, '_%s_gen_%s' % (step_name, kind)) + try: + method(tp, realname, module, **kwds) + except Exception as e: + model.attach_exception_info(e, name) + raise + + def _generate_nothing(self, tp, name): + pass + + def _loaded_noop(self, tp, name, module, **kwds): + pass + + # ---------- + # typedefs: generates no code so far + + _generate_gen_typedef_decl = _generate_nothing + _loading_gen_typedef = _loaded_noop + _loaded_gen_typedef = _loaded_noop + + # ---------- + # function declarations + + def _generate_gen_function_decl(self, tp, name): + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + # cannot support vararg functions better than this: check for its + # exact type (including the fixed arguments), and build it as a + # constant function pointer (no _cffi_f_%s wrapper) + self._generate_gen_const(False, name, tp) + return + prnt = self._prnt + numargs = len(tp.args) + argnames = [] + for i, type in enumerate(tp.args): + indirection = '' + if isinstance(type, model.StructOrUnion): + indirection = '*' + argnames.append('%sx%d' % (indirection, i)) + context = 'argument of %s' % name + arglist = [type.get_c_name(' %s' % arg, context) + for type, arg in zip(tp.args, argnames)] + tpresult = tp.result + if isinstance(tpresult, model.StructOrUnion): + arglist.insert(0, tpresult.get_c_name(' *r', context)) + tpresult = model.void_type + arglist = ', '.join(arglist) or 'void' + wrappername = '_cffi_f_%s' % name + self.export_symbols.append(wrappername) + if tp.abi: + abi = tp.abi + ' ' + else: + abi = '' + funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist) + context = 'result of %s' % name + prnt(tpresult.get_c_name(funcdecl, context)) + prnt('{') + # + if isinstance(tp.result, model.StructOrUnion): + result_code = '*r = ' + elif not isinstance(tp.result, model.VoidType): + result_code = 'return ' + else: + result_code = '' + prnt(' %s%s(%s);' % (result_code, name, ', '.join(argnames))) + prnt('}') + prnt() + + _loading_gen_function = _loaded_noop + + def _loaded_gen_function(self, tp, name, module, library): + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + newfunction = self._load_constant(False, tp, name, module) + else: + indirections = [] + base_tp = tp + if (any(isinstance(typ, model.StructOrUnion) for typ in tp.args) + or isinstance(tp.result, model.StructOrUnion)): + indirect_args = [] + for i, typ in enumerate(tp.args): + if isinstance(typ, model.StructOrUnion): + typ = model.PointerType(typ) + indirections.append((i, typ)) + indirect_args.append(typ) + indirect_result = tp.result + if isinstance(indirect_result, model.StructOrUnion): + if indirect_result.fldtypes is None: + raise TypeError("'%s' is used as result type, " + "but is opaque" % ( + indirect_result._get_c_name(),)) + indirect_result = model.PointerType(indirect_result) + indirect_args.insert(0, indirect_result) + indirections.insert(0, ("result", indirect_result)) + indirect_result = model.void_type + tp = model.FunctionPtrType(tuple(indirect_args), + indirect_result, tp.ellipsis) + BFunc = self.ffi._get_cached_btype(tp) + wrappername = '_cffi_f_%s' % name + newfunction = module.load_function(BFunc, wrappername) + for i, typ in indirections: + newfunction = self._make_struct_wrapper(newfunction, i, typ, + base_tp) + setattr(library, name, newfunction) + type(library)._cffi_dir.append(name) + + def _make_struct_wrapper(self, oldfunc, i, tp, base_tp): + backend = self.ffi._backend + BType = self.ffi._get_cached_btype(tp) + if i == "result": + ffi = self.ffi + def newfunc(*args): + res = ffi.new(BType) + oldfunc(res, *args) + return res[0] + else: + def newfunc(*args): + args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:] + return oldfunc(*args) + newfunc._cffi_base_type = base_tp + return newfunc + + # ---------- + # named structs + + def _generate_gen_struct_decl(self, tp, name): + assert name == tp.name + self._generate_struct_or_union_decl(tp, 'struct', name) + + def _loading_gen_struct(self, tp, name, module): + self._loading_struct_or_union(tp, 'struct', name, module) + + def _loaded_gen_struct(self, tp, name, module, **kwds): + self._loaded_struct_or_union(tp) + + def _generate_gen_union_decl(self, tp, name): + assert name == tp.name + self._generate_struct_or_union_decl(tp, 'union', name) + + def _loading_gen_union(self, tp, name, module): + self._loading_struct_or_union(tp, 'union', name, module) + + def _loaded_gen_union(self, tp, name, module, **kwds): + self._loaded_struct_or_union(tp) + + def _generate_struct_or_union_decl(self, tp, prefix, name): + if tp.fldnames is None: + return # nothing to do with opaque structs + checkfuncname = '_cffi_check_%s_%s' % (prefix, name) + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + cname = ('%s %s' % (prefix, name)).strip() + # + prnt = self._prnt + prnt('static void %s(%s *p)' % (checkfuncname, cname)) + prnt('{') + prnt(' /* only to generate compile-time warnings or errors */') + prnt(' (void)p;') + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if (isinstance(ftype, model.PrimitiveType) + and ftype.is_integer_type()) or fbitsize >= 0: + # accept all integers, but complain on float or double + prnt(' (void)((p->%s) << 1);' % fname) + else: + # only accept exactly the type declared. + try: + prnt(' { %s = &p->%s; (void)tmp; }' % ( + ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), + fname)) + except VerificationError as e: + prnt(' /* %s */' % str(e)) # cannot verify it, ignore + prnt('}') + self.export_symbols.append(layoutfuncname) + prnt('intptr_t %s(intptr_t i)' % (layoutfuncname,)) + prnt('{') + prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) + prnt(' static intptr_t nums[] = {') + prnt(' sizeof(%s),' % cname) + prnt(' offsetof(struct _cffi_aligncheck, y),') + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if fbitsize >= 0: + continue # xxx ignore fbitsize for now + prnt(' offsetof(%s, %s),' % (cname, fname)) + if isinstance(ftype, model.ArrayType) and ftype.length is None: + prnt(' 0, /* %s */' % ftype._get_c_name()) + else: + prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) + prnt(' -1') + prnt(' };') + prnt(' return nums[i];') + prnt(' /* the next line is not executed, but compiled */') + prnt(' %s(0);' % (checkfuncname,)) + prnt('}') + prnt() + + def _loading_struct_or_union(self, tp, prefix, name, module): + if tp.fldnames is None: + return # nothing to do with opaque structs + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + # + BFunc = self.ffi._typeof_locked("intptr_t(*)(intptr_t)")[0] + function = module.load_function(BFunc, layoutfuncname) + layout = [] + num = 0 + while True: + x = function(num) + if x < 0: break + layout.append(x) + num += 1 + if isinstance(tp, model.StructOrUnion) and tp.partial: + # use the function()'s sizes and offsets to guide the + # layout of the struct + totalsize = layout[0] + totalalignment = layout[1] + fieldofs = layout[2::2] + fieldsize = layout[3::2] + tp.force_flatten() + assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) + tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment + else: + cname = ('%s %s' % (prefix, name)).strip() + self._struct_pending_verification[tp] = layout, cname + + def _loaded_struct_or_union(self, tp): + if tp.fldnames is None: + return # nothing to do with opaque structs + self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered + + if tp in self._struct_pending_verification: + # check that the layout sizes and offsets match the real ones + def check(realvalue, expectedvalue, msg): + if realvalue != expectedvalue: + raise VerificationError( + "%s (we have %d, but C compiler says %d)" + % (msg, expectedvalue, realvalue)) + ffi = self.ffi + BStruct = ffi._get_cached_btype(tp) + layout, cname = self._struct_pending_verification.pop(tp) + check(layout[0], ffi.sizeof(BStruct), "wrong total size") + check(layout[1], ffi.alignof(BStruct), "wrong total alignment") + i = 2 + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if fbitsize >= 0: + continue # xxx ignore fbitsize for now + check(layout[i], ffi.offsetof(BStruct, fname), + "wrong offset for field %r" % (fname,)) + if layout[i+1] != 0: + BField = ffi._get_cached_btype(ftype) + check(layout[i+1], ffi.sizeof(BField), + "wrong size for field %r" % (fname,)) + i += 2 + assert i == len(layout) + + # ---------- + # 'anonymous' declarations. These are produced for anonymous structs + # or unions; the 'name' is obtained by a typedef. + + def _generate_gen_anonymous_decl(self, tp, name): + if isinstance(tp, model.EnumType): + self._generate_gen_enum_decl(tp, name, '') + else: + self._generate_struct_or_union_decl(tp, '', name) + + def _loading_gen_anonymous(self, tp, name, module): + if isinstance(tp, model.EnumType): + self._loading_gen_enum(tp, name, module, '') + else: + self._loading_struct_or_union(tp, '', name, module) + + def _loaded_gen_anonymous(self, tp, name, module, **kwds): + if isinstance(tp, model.EnumType): + self._loaded_gen_enum(tp, name, module, **kwds) + else: + self._loaded_struct_or_union(tp) + + # ---------- + # constants, likely declared with '#define' + + def _generate_gen_const(self, is_int, name, tp=None, category='const', + check_value=None): + prnt = self._prnt + funcname = '_cffi_%s_%s' % (category, name) + self.export_symbols.append(funcname) + if check_value is not None: + assert is_int + assert category == 'const' + prnt('int %s(char *out_error)' % funcname) + prnt('{') + self._check_int_constant_value(name, check_value) + prnt(' return 0;') + prnt('}') + elif is_int: + assert category == 'const' + prnt('int %s(long long *out_value)' % funcname) + prnt('{') + prnt(' *out_value = (long long)(%s);' % (name,)) + prnt(' return (%s) <= 0;' % (name,)) + prnt('}') + else: + assert tp is not None + assert check_value is None + if category == 'var': + ampersand = '&' + else: + ampersand = '' + extra = '' + if category == 'const' and isinstance(tp, model.StructOrUnion): + extra = 'const *' + ampersand = '&' + prnt(tp.get_c_name(' %s%s(void)' % (extra, funcname), name)) + prnt('{') + prnt(' return (%s%s);' % (ampersand, name)) + prnt('}') + prnt() + + def _generate_gen_constant_decl(self, tp, name): + is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() + self._generate_gen_const(is_int, name, tp) + + _loading_gen_constant = _loaded_noop + + def _load_constant(self, is_int, tp, name, module, check_value=None): + funcname = '_cffi_const_%s' % name + if check_value is not None: + assert is_int + self._load_known_int_constant(module, funcname) + value = check_value + elif is_int: + BType = self.ffi._typeof_locked("long long*")[0] + BFunc = self.ffi._typeof_locked("int(*)(long long*)")[0] + function = module.load_function(BFunc, funcname) + p = self.ffi.new(BType) + negative = function(p) + value = int(p[0]) + if value < 0 and not negative: + BLongLong = self.ffi._typeof_locked("long long")[0] + value += (1 << (8*self.ffi.sizeof(BLongLong))) + else: + assert check_value is None + fntypeextra = '(*)(void)' + if isinstance(tp, model.StructOrUnion): + fntypeextra = '*' + fntypeextra + BFunc = self.ffi._typeof_locked(tp.get_c_name(fntypeextra, name))[0] + function = module.load_function(BFunc, funcname) + value = function() + if isinstance(tp, model.StructOrUnion): + value = value[0] + return value + + def _loaded_gen_constant(self, tp, name, module, library): + is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() + value = self._load_constant(is_int, tp, name, module) + setattr(library, name, value) + type(library)._cffi_dir.append(name) + + # ---------- + # enums + + def _check_int_constant_value(self, name, value): + prnt = self._prnt + if value <= 0: + prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( + name, name, value)) + else: + prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( + name, name, value)) + prnt(' char buf[64];') + prnt(' if ((%s) <= 0)' % name) + prnt(' sprintf(buf, "%%ld", (long)(%s));' % name) + prnt(' else') + prnt(' sprintf(buf, "%%lu", (unsigned long)(%s));' % + name) + prnt(' sprintf(out_error, "%s has the real value %s, not %s",') + prnt(' "%s", buf, "%d");' % (name[:100], value)) + prnt(' return -1;') + prnt(' }') + + def _load_known_int_constant(self, module, funcname): + BType = self.ffi._typeof_locked("char[]")[0] + BFunc = self.ffi._typeof_locked("int(*)(char*)")[0] + function = module.load_function(BFunc, funcname) + p = self.ffi.new(BType, 256) + if function(p) < 0: + error = self.ffi.string(p) + if sys.version_info >= (3,): + error = str(error, 'utf-8') + raise VerificationError(error) + + def _enum_funcname(self, prefix, name): + # "$enum_$1" => "___D_enum____D_1" + name = name.replace('$', '___D_') + return '_cffi_e_%s_%s' % (prefix, name) + + def _generate_gen_enum_decl(self, tp, name, prefix='enum'): + if tp.partial: + for enumerator in tp.enumerators: + self._generate_gen_const(True, enumerator) + return + # + funcname = self._enum_funcname(prefix, name) + self.export_symbols.append(funcname) + prnt = self._prnt + prnt('int %s(char *out_error)' % funcname) + prnt('{') + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + self._check_int_constant_value(enumerator, enumvalue) + prnt(' return 0;') + prnt('}') + prnt() + + def _loading_gen_enum(self, tp, name, module, prefix='enum'): + if tp.partial: + enumvalues = [self._load_constant(True, tp, enumerator, module) + for enumerator in tp.enumerators] + tp.enumvalues = tuple(enumvalues) + tp.partial_resolved = True + else: + funcname = self._enum_funcname(prefix, name) + self._load_known_int_constant(module, funcname) + + def _loaded_gen_enum(self, tp, name, module, library): + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + setattr(library, enumerator, enumvalue) + type(library)._cffi_dir.append(enumerator) + + # ---------- + # macros: for now only for integers + + def _generate_gen_macro_decl(self, tp, name): + if tp == '...': + check_value = None + else: + check_value = tp # an integer + self._generate_gen_const(True, name, check_value=check_value) + + _loading_gen_macro = _loaded_noop + + def _loaded_gen_macro(self, tp, name, module, library): + if tp == '...': + check_value = None + else: + check_value = tp # an integer + value = self._load_constant(True, tp, name, module, + check_value=check_value) + setattr(library, name, value) + type(library)._cffi_dir.append(name) + + # ---------- + # global variables + + def _generate_gen_variable_decl(self, tp, name): + if isinstance(tp, model.ArrayType): + if tp.length_is_unknown(): + prnt = self._prnt + funcname = '_cffi_sizeof_%s' % (name,) + self.export_symbols.append(funcname) + prnt("size_t %s(void)" % funcname) + prnt("{") + prnt(" return sizeof(%s);" % (name,)) + prnt("}") + tp_ptr = model.PointerType(tp.item) + self._generate_gen_const(False, name, tp_ptr) + else: + tp_ptr = model.PointerType(tp) + self._generate_gen_const(False, name, tp_ptr, category='var') + + _loading_gen_variable = _loaded_noop + + def _loaded_gen_variable(self, tp, name, module, library): + if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the + # sense that "a=..." is forbidden + if tp.length_is_unknown(): + funcname = '_cffi_sizeof_%s' % (name,) + BFunc = self.ffi._typeof_locked('size_t(*)(void)')[0] + function = module.load_function(BFunc, funcname) + size = function() + BItemType = self.ffi._get_cached_btype(tp.item) + length, rest = divmod(size, self.ffi.sizeof(BItemType)) + if rest != 0: + raise VerificationError( + "bad size: %r does not seem to be an array of %s" % + (name, tp.item)) + tp = tp.resolve_length(length) + tp_ptr = model.PointerType(tp.item) + value = self._load_constant(False, tp_ptr, name, module) + # 'value' is a which we have to replace with + # a if the N is actually known + if tp.length is not None: + BArray = self.ffi._get_cached_btype(tp) + value = self.ffi.cast(BArray, value) + setattr(library, name, value) + type(library)._cffi_dir.append(name) + return + # remove ptr= from the library instance, and replace + # it by a property on the class, which reads/writes into ptr[0]. + funcname = '_cffi_var_%s' % name + BFunc = self.ffi._typeof_locked(tp.get_c_name('*(*)(void)', name))[0] + function = module.load_function(BFunc, funcname) + ptr = function() + def getter(library): + return ptr[0] + def setter(library, value): + ptr[0] = value + setattr(type(library), name, property(getter, setter)) + type(library)._cffi_dir.append(name) + +cffimod_header = r''' +#include +#include +#include +#include +#include /* XXX for ssize_t on some platforms */ + +/* this block of #ifs should be kept exactly identical between + c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py + and cffi/_cffi_include.h */ +#if defined(_MSC_VER) +# include /* for alloca() */ +# if _MSC_VER < 1600 /* MSVC < 2010 */ + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; +# else +# include +# endif +# if _MSC_VER < 1800 /* MSVC < 2013 */ +# ifndef __cplusplus + typedef unsigned char _Bool; +# endif +# endif +# define _cffi_float_complex_t _Fcomplex /* include for it */ +# define _cffi_double_complex_t _Dcomplex /* include for it */ +#else +# include +# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) +# include +# endif +# define _cffi_float_complex_t float _Complex +# define _cffi_double_complex_t double _Complex +#endif +''' diff --git a/venv/lib/python3.12/site-packages/cffi/verifier.py b/venv/lib/python3.12/site-packages/cffi/verifier.py new file mode 100644 index 0000000..96baadb --- /dev/null +++ b/venv/lib/python3.12/site-packages/cffi/verifier.py @@ -0,0 +1,306 @@ +# +# DEPRECATED: implementation for ffi.verify() +# +import sys, os, binascii, shutil, io +from . import __version_verifier_modules__ +from . import ffiplatform +from .error import VerificationError + +if sys.version_info >= (3, 3): + import importlib.machinery + def _extension_suffixes(): + return importlib.machinery.EXTENSION_SUFFIXES[:] +else: + import imp + def _extension_suffixes(): + return [suffix for suffix, _, type in imp.get_suffixes() + if type == imp.C_EXTENSION] + + +if sys.version_info >= (3,): + NativeIO = io.StringIO +else: + class NativeIO(io.BytesIO): + def write(self, s): + if isinstance(s, unicode): + s = s.encode('ascii') + super().write(s) + + +class Verifier: + + def __init__(self, ffi, preamble, tmpdir=None, modulename=None, + ext_package=None, tag='', force_generic_engine=False, + source_extension='.c', flags=None, relative_to=None, **kwds): + if ffi._parser._uses_new_feature: + raise VerificationError( + "feature not supported with ffi.verify(), but only " + "with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,)) + self.ffi = ffi + self.preamble = preamble + if not modulename: + flattened_kwds = ffiplatform.flatten(kwds) + vengine_class = _locate_engine_class(ffi, force_generic_engine) + self._vengine = vengine_class(self) + self._vengine.patch_extension_kwds(kwds) + self.flags = flags + self.kwds = self.make_relative_to(kwds, relative_to) + # + if modulename: + if tag: + raise TypeError("can't specify both 'modulename' and 'tag'") + else: + key = '\x00'.join(['%d.%d' % sys.version_info[:2], + __version_verifier_modules__, + preamble, flattened_kwds] + + ffi._cdefsources) + if sys.version_info >= (3,): + key = key.encode('utf-8') + k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff) + k1 = k1.lstrip('0x').rstrip('L') + k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff) + k2 = k2.lstrip('0').rstrip('L') + modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key, + k1, k2) + suffix = _get_so_suffixes()[0] + self.tmpdir = tmpdir or _caller_dir_pycache() + self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension) + self.modulefilename = os.path.join(self.tmpdir, modulename + suffix) + self.ext_package = ext_package + self._has_source = False + self._has_module = False + + def write_source(self, file=None): + """Write the C source code. It is produced in 'self.sourcefilename', + which can be tweaked beforehand.""" + with self.ffi._lock: + if self._has_source and file is None: + raise VerificationError( + "source code already written") + self._write_source(file) + + def compile_module(self): + """Write the C source code (if not done already) and compile it. + This produces a dynamic link library in 'self.modulefilename'.""" + with self.ffi._lock: + if self._has_module: + raise VerificationError("module already compiled") + if not self._has_source: + self._write_source() + self._compile_module() + + def load_library(self): + """Get a C module from this Verifier instance. + Returns an instance of a FFILibrary class that behaves like the + objects returned by ffi.dlopen(), but that delegates all + operations to the C module. If necessary, the C code is written + and compiled first. + """ + with self.ffi._lock: + if not self._has_module: + self._locate_module() + if not self._has_module: + if not self._has_source: + self._write_source() + self._compile_module() + return self._load_library() + + def get_module_name(self): + basename = os.path.basename(self.modulefilename) + # kill both the .so extension and the other .'s, as introduced + # by Python 3: 'basename.cpython-33m.so' + basename = basename.split('.', 1)[0] + # and the _d added in Python 2 debug builds --- but try to be + # conservative and not kill a legitimate _d + if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'): + basename = basename[:-2] + return basename + + def get_extension(self): + if not self._has_source: + with self.ffi._lock: + if not self._has_source: + self._write_source() + sourcename = ffiplatform.maybe_relative_path(self.sourcefilename) + modname = self.get_module_name() + return ffiplatform.get_extension(sourcename, modname, **self.kwds) + + def generates_python_module(self): + return self._vengine._gen_python_module + + def make_relative_to(self, kwds, relative_to): + if relative_to and os.path.dirname(relative_to): + dirname = os.path.dirname(relative_to) + kwds = kwds.copy() + for key in ffiplatform.LIST_OF_FILE_NAMES: + if key in kwds: + lst = kwds[key] + if not isinstance(lst, (list, tuple)): + raise TypeError("keyword '%s' should be a list or tuple" + % (key,)) + lst = [os.path.join(dirname, fn) for fn in lst] + kwds[key] = lst + return kwds + + # ---------- + + def _locate_module(self): + if not os.path.isfile(self.modulefilename): + if self.ext_package: + try: + pkg = __import__(self.ext_package, None, None, ['__doc__']) + except ImportError: + return # cannot import the package itself, give up + # (e.g. it might be called differently before installation) + path = pkg.__path__ + else: + path = None + filename = self._vengine.find_module(self.get_module_name(), path, + _get_so_suffixes()) + if filename is None: + return + self.modulefilename = filename + self._vengine.collect_types() + self._has_module = True + + def _write_source_to(self, file): + self._vengine._f = file + try: + self._vengine.write_source_to_f() + finally: + del self._vengine._f + + def _write_source(self, file=None): + if file is not None: + self._write_source_to(file) + else: + # Write our source file to an in memory file. + f = NativeIO() + self._write_source_to(f) + source_data = f.getvalue() + + # Determine if this matches the current file + if os.path.exists(self.sourcefilename): + with open(self.sourcefilename, "r") as fp: + needs_written = fp.read() != source_data + else: + needs_written = True + + # Actually write the file out if it doesn't match + if needs_written: + _ensure_dir(self.sourcefilename) + with open(self.sourcefilename, "w") as fp: + fp.write(source_data) + + # Set this flag + self._has_source = True + + def _compile_module(self): + # compile this C source + tmpdir = os.path.dirname(self.sourcefilename) + outputfilename = ffiplatform.compile(tmpdir, self.get_extension()) + try: + same = ffiplatform.samefile(outputfilename, self.modulefilename) + except OSError: + same = False + if not same: + _ensure_dir(self.modulefilename) + shutil.move(outputfilename, self.modulefilename) + self._has_module = True + + def _load_library(self): + assert self._has_module + if self.flags is not None: + return self._vengine.load_library(self.flags) + else: + return self._vengine.load_library() + +# ____________________________________________________________ + +_FORCE_GENERIC_ENGINE = False # for tests + +def _locate_engine_class(ffi, force_generic_engine): + if _FORCE_GENERIC_ENGINE: + force_generic_engine = True + if not force_generic_engine: + if '__pypy__' in sys.builtin_module_names: + force_generic_engine = True + else: + try: + import _cffi_backend + except ImportError: + _cffi_backend = '?' + if ffi._backend is not _cffi_backend: + force_generic_engine = True + if force_generic_engine: + from . import vengine_gen + return vengine_gen.VGenericEngine + else: + from . import vengine_cpy + return vengine_cpy.VCPythonEngine + +# ____________________________________________________________ + +_TMPDIR = None + +def _caller_dir_pycache(): + if _TMPDIR: + return _TMPDIR + result = os.environ.get('CFFI_TMPDIR') + if result: + return result + filename = sys._getframe(2).f_code.co_filename + return os.path.abspath(os.path.join(os.path.dirname(filename), + '__pycache__')) + +def set_tmpdir(dirname): + """Set the temporary directory to use instead of __pycache__.""" + global _TMPDIR + _TMPDIR = dirname + +def cleanup_tmpdir(tmpdir=None, keep_so=False): + """Clean up the temporary directory by removing all files in it + called `_cffi_*.{c,so}` as well as the `build` subdirectory.""" + tmpdir = tmpdir or _caller_dir_pycache() + try: + filelist = os.listdir(tmpdir) + except OSError: + return + if keep_so: + suffix = '.c' # only remove .c files + else: + suffix = _get_so_suffixes()[0].lower() + for fn in filelist: + if fn.lower().startswith('_cffi_') and ( + fn.lower().endswith(suffix) or fn.lower().endswith('.c')): + try: + os.unlink(os.path.join(tmpdir, fn)) + except OSError: + pass + clean_dir = [os.path.join(tmpdir, 'build')] + for dir in clean_dir: + try: + for fn in os.listdir(dir): + fn = os.path.join(dir, fn) + if os.path.isdir(fn): + clean_dir.append(fn) + else: + os.unlink(fn) + except OSError: + pass + +def _get_so_suffixes(): + suffixes = _extension_suffixes() + if not suffixes: + # bah, no C_EXTENSION available. Occurs on pypy without cpyext + if sys.platform == 'win32': + suffixes = [".pyd"] + else: + suffixes = [".so"] + + return suffixes + +def _ensure_dir(filename): + dirname = os.path.dirname(filename) + if dirname and not os.path.isdir(dirname): + os.makedirs(dirname) diff --git a/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/METADATA new file mode 100644 index 0000000..7b78530 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/METADATA @@ -0,0 +1,83 @@ +Metadata-Version: 2.4 +Name: click +Version: 8.5.0 +Summary: Composable command line interface toolkit +Maintainer-email: Pallets +Requires-Python: >=3.10 +Description-Content-Type: text/markdown +License-Expression: BSD-3-Clause +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Typing :: Typed +License-File: LICENSE.txt +Project-URL: Changes, https://click.palletsprojects.com/page/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/click/ + +
+ +# Click + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +## A Simple Example + +```python +import click + +@click.command() +@click.option("--count", default=1, help="Number of greetings.") +@click.option("--name", prompt="Your name", help="The person to greet.") +def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + +if __name__ == '__main__': + hello() +``` + +``` +$ python hello.py --count=3 +Your name: Click +Hello, Click! +Hello, Click! +Hello, Click! +``` + + +## Donate + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + +## Contributing + +See our [detailed contributing documentation][contrib] for many ways to +contribute, including reporting issues, requesting features, asking or answering +questions, and making PRs. + +[contrib]: https://palletsprojects.com/contributing/ + diff --git a/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/RECORD new file mode 100644 index 0000000..d2efde9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/RECORD @@ -0,0 +1,40 @@ +click-8.5.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.5.0.dist-info/METADATA,sha256=6HvOC9GU3nDfuOcI4KawSDAJ8lYRgE6vh7_rY-bCBQE,2567 +click-8.5.0.dist-info/RECORD,, +click-8.5.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +click-8.5.0.dist-info/licenses/LICENSE.txt,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click/__init__.py,sha256=Wr_FTTfUfMeIt-egXpUUeH-MWgt9tCnQ8k10isiZZMo,5145 +click/__pycache__/__init__.cpython-312.pyc,, +click/__pycache__/_compat.cpython-312.pyc,, +click/__pycache__/_termui_impl.cpython-312.pyc,, +click/__pycache__/_textwrap.cpython-312.pyc,, +click/__pycache__/_utils.cpython-312.pyc,, +click/__pycache__/_winconsole.cpython-312.pyc,, +click/__pycache__/core.cpython-312.pyc,, +click/__pycache__/decorators.cpython-312.pyc,, +click/__pycache__/exceptions.cpython-312.pyc,, +click/__pycache__/formatting.cpython-312.pyc,, +click/__pycache__/globals.cpython-312.pyc,, +click/__pycache__/parser.cpython-312.pyc,, +click/__pycache__/shell_completion.cpython-312.pyc,, +click/__pycache__/termui.cpython-312.pyc,, +click/__pycache__/testing.cpython-312.pyc,, +click/__pycache__/types.cpython-312.pyc,, +click/__pycache__/utils.cpython-312.pyc,, +click/_compat.py,sha256=jbHaKWXPPl3GbGxTgzy8YT6hzSRAudDraqg2DrRtSRM,17939 +click/_termui_impl.py,sha256=iLl9m3pblHsQoW2KsmmDe8hMflXCi6cupfL7M5ESM2o,32805 +click/_textwrap.py,sha256=7Z0N7Vmn-66TNSTUwp6OXJbcUXRmYET9h9c2ucD8oQQ,6270 +click/_utils.py,sha256=eCZCtwJtsYD5QYkkNWJ8MY_8ABIjy8MczgMMyVY32rQ,996 +click/_winconsole.py,sha256=KSxfNbMlYRa6GOJuCLgsg2Pb3dVkgJNPqLJPae-Pa10,8543 +click/core.py,sha256=TGWmE8HEB9zpB6ThI7Es7F_g9iCIqLn4b6vUtgxLbXg,147845 +click/decorators.py,sha256=FgaTV2FWkfzfyceUu1FfYAnrNarW96dsAXvc4G2ujVU,21680 +click/exceptions.py,sha256=HvSY34G4auj_bYRR8-T8CU8Jwq_1-OcsRU4ezfozeEk,11862 +click/formatting.py,sha256=8SW2KGkvjfz9Q1NbeojMHuZBN0cfnQJDs4mqDP6oXms,10444 +click/globals.py,sha256=gM-Nh6A4M0HB_SgkaF5M4ncGGMDHc_flHXu9_oh4GEU,1923 +click/parser.py,sha256=oJ-fU_3mvxugIuNtHaCATZ56lgEmHRggjJiSqEgYrjA,19052 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=GFIvdd3_5lXpQ5eNGVMlXER4QoZ9P9Z_vcFUPT332gk,26172 +click/termui.py,sha256=jsgCK45FKPhz7EExlKXRYU6W2lC2xKcfYmCzJnYqfpw,35370 +click/testing.py,sha256=dvTmSbxTAA1mfIwN7_FZGXD6A0sPCFogcWWDmyMBAZo,27584 +click/types.py,sha256=HpiJBKen9oq61jDH-5wDikNF-i5nAcl2puUfEL37Ut0,46842 +click/utils.py,sha256=9D1XQ9wHJA5hL-76CmnSsWl7aQAUn4-An_zZfstILb8,21483 diff --git a/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/WHEEL new file mode 100644 index 0000000..d8b9936 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/licenses/LICENSE.txt b/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..d12a849 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.5.0.dist-info/licenses/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2014 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/click/__init__.py b/venv/lib/python3.12/site-packages/click/__init__.py new file mode 100644 index 0000000..a34ab02 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/__init__.py @@ -0,0 +1,144 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" + +from __future__ import annotations + +from .core import Argument as Argument +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import Option as Option +from .core import Parameter as Parameter +from .core import ParameterSource as ParameterSource +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import custom_version_option as custom_version_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchCommand as NoSuchCommand +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import get_pager_file as get_pager_file +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import open_file as open_file + + +def __getattr__(name: str) -> object: + import warnings + + if name == "BaseCommand": + from .core import _BaseCommand + + warnings.warn( + "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Command' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _BaseCommand + + if name == "MultiCommand": + from .core import _MultiCommand + + warnings.warn( + "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Group' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _MultiCommand + + if name == "OptionParser": + from .parser import _OptionParser + + warnings.warn( + "'OptionParser' is deprecated and will be removed in Click 9.0. The" + " old parser is available in 'optparse'.", + DeprecationWarning, + stacklevel=2, + ) + return _OptionParser + + if name == "__version__": + import importlib.metadata + + warnings.warn( + "The '__version__' attribute is deprecated and will be removed in" + " Click 9.1. Use feature detection or" + " 'importlib.metadata.version(\"click\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("click") + + if name == "get_binary_stream": + from .utils import _get_binary_stream + + warnings.warn( + "'get_binary_stream' is deprecated and will be removed in Click 9.0.", + DeprecationWarning, + stacklevel=2, + ) + return _get_binary_stream + + if name == "get_text_stream": + from .utils import _get_text_stream + + warnings.warn( + "'get_text_stream' is deprecated and will be removed in Click 9.0.", + DeprecationWarning, + stacklevel=2, + ) + return _get_text_stream + + raise AttributeError(name) diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..edcdc2f Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_compat.cpython-312.pyc new file mode 100644 index 0000000..86f327e Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/_compat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_termui_impl.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_termui_impl.cpython-312.pyc new file mode 100644 index 0000000..440b7da Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/_termui_impl.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_textwrap.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_textwrap.cpython-312.pyc new file mode 100644 index 0000000..83e876d Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/_textwrap.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_utils.cpython-312.pyc new file mode 100644 index 0000000..16b35e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_winconsole.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_winconsole.cpython-312.pyc new file mode 100644 index 0000000..9abc78d Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/_winconsole.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/core.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/core.cpython-312.pyc new file mode 100644 index 0000000..6efce6e Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/core.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/decorators.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/decorators.cpython-312.pyc new file mode 100644 index 0000000..be5630a Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/decorators.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..3f35636 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/formatting.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/formatting.cpython-312.pyc new file mode 100644 index 0000000..9ce6110 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/formatting.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/globals.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/globals.cpython-312.pyc new file mode 100644 index 0000000..b3bc03f Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/globals.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/parser.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/parser.cpython-312.pyc new file mode 100644 index 0000000..b453897 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/parser.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/shell_completion.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/shell_completion.cpython-312.pyc new file mode 100644 index 0000000..ce82acf Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/shell_completion.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/termui.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/termui.cpython-312.pyc new file mode 100644 index 0000000..56b416e Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/termui.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/testing.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/testing.cpython-312.pyc new file mode 100644 index 0000000..37854d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/testing.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/types.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/types.cpython-312.pyc new file mode 100644 index 0000000..14dce02 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..343a42d Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/_compat.py b/venv/lib/python3.12/site-packages/click/_compat.py new file mode 100644 index 0000000..8507a50 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/_compat.py @@ -0,0 +1,590 @@ +from __future__ import annotations + +import codecs +import collections.abc as cabc +import io +import os +import re +import sys +import typing as t +from types import TracebackType +from weakref import WeakKeyDictionary + +CYGWIN = sys.platform.startswith("cygwin") +WIN = sys.platform.startswith("win") +# One CSI escape sequence per the ECMA-48 grammar: parameter bytes (0x30-0x3F), +# intermediate bytes (0x20-0x2F), then a final byte (0x40-0x7E). Broader than the +# SGR codes Click emits, so foreign sequences (colon-delimited true-color, mouse +# reporting) are stripped too. +_ansi_re = re.compile(r"\033\[[0-?]*[ -/]*[@-~]") + + +def _make_text_stream( + stream: t.BinaryIO, + encoding: str | None, + errors: str | None, + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if encoding is None: + encoding = get_best_encoding(stream) + if errors is None: + errors = "replace" + return _NonClosingTextIOWrapper( + stream, + encoding, + errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def is_ascii_encoding(encoding: str) -> bool: + """Checks if a given encoding is ascii.""" + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +def get_best_encoding(stream: t.IO[t.Any]) -> str: + """Returns the default stream encoding if not found.""" + rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() + if is_ascii_encoding(rv): + return "utf-8" + return rv + + +class _NonClosingTextIOWrapper(io.TextIOWrapper): + def __init__( + self, + stream: t.BinaryIO, + encoding: str | None, + errors: str | None, + force_readable: bool = False, + force_writable: bool = False, + **extra: t.Any, + ) -> None: + self._stream = stream = t.cast( + t.BinaryIO, _FixupStream(stream, force_readable, force_writable) + ) + super().__init__(stream, encoding, errors, **extra) + + def __del__(self) -> None: + try: + self.detach() + except Exception: + pass + + def isatty(self) -> bool: + # https://bitbucket.org/pypy/pypy/issue/1803 + return self._stream.isatty() + + +class _FixupStream: + """The new io interface needs more from streams than streams + traditionally implement. As such, this fix-up code is necessary in + some circumstances. + + The forcing of readable and writable flags are there because some tools + put badly patched objects on sys (one such offender are certain version + of jupyter notebook). + """ + + def __init__( + self, + stream: t.BinaryIO, + force_readable: bool = False, + force_writable: bool = False, + ): + self._stream = stream + self._force_readable = force_readable + self._force_writable = force_writable + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._stream, name) + + def read1(self, size: int) -> bytes: + f = getattr(self._stream, "read1", None) + + if f is not None: + return t.cast(bytes, f(size)) + + return self._stream.read(size) + + def readable(self) -> bool: + if self._force_readable: + return True + x = getattr(self._stream, "readable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.read(0) + except Exception: + return False + return True + + def writable(self) -> bool: + if self._force_writable: + return True + x = getattr(self._stream, "writable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.write(b"") + except Exception: + try: + self._stream.write(b"") + except Exception: + return False + return True + + def seekable(self) -> bool: + x = getattr(self._stream, "seekable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.seek(self._stream.tell()) + except Exception: + return False + return True + + +def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + return isinstance(stream.read(0), bytes) + except Exception: + return default + # This happens in some cases where the stream was already + # closed. In this case, we assume the default. + + +def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + stream.write(b"") + except Exception: + try: + stream.write("") + return False + except Exception: + pass + return default + return True + + +def _find_binary_reader(stream: t.IO[t.Any]) -> t.BinaryIO | None: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_reader(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_reader(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _find_binary_writer(stream: t.IO[t.Any]) -> t.BinaryIO | None: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_writer(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_writer(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _stream_is_misconfigured(stream: t.TextIO) -> bool: + """A stream is misconfigured if its encoding is ASCII.""" + # If the stream does not have an encoding set, we assume it's set + # to ASCII. This appears to happen in certain unittest + # environments. It's not quite clear what the correct behavior is + # but this at least will force Click to recover somehow. + return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") + + +def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: str | None) -> bool: + """A stream attribute is compatible if it is equal to the + desired value or the desired value is unset and the attribute + has a value. + """ + stream_value = getattr(stream, attr, None) + return stream_value == value or (value is None and stream_value is not None) + + +def _is_compatible_text_stream( + stream: t.TextIO, encoding: str | None, errors: str | None +) -> bool: + """Check if a stream's encoding and errors attributes are + compatible with the desired values. + """ + return _is_compat_stream_attr( + stream, "encoding", encoding + ) and _is_compat_stream_attr(stream, "errors", errors) + + +def _force_correct_text_stream( + text_stream: t.IO[t.Any], + encoding: str | None, + errors: str | None, + is_binary: t.Callable[[t.IO[t.Any], bool], bool], + find_binary: t.Callable[[t.IO[t.Any]], t.BinaryIO | None], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if is_binary(text_stream, False): + binary_reader = t.cast(t.BinaryIO, text_stream) + else: + text_stream = t.cast(t.TextIO, text_stream) + # If the stream looks compatible, and won't default to a + # misconfigured ascii encoding, return it as-is. + if _is_compatible_text_stream(text_stream, encoding, errors) and not ( + encoding is None and _stream_is_misconfigured(text_stream) + ): + return text_stream + + # Otherwise, get the underlying binary reader. + possible_binary_reader = find_binary(text_stream) + + # If that's not possible, silently use the original reader + # and get mojibake instead of exceptions. + if possible_binary_reader is None: + return text_stream + + binary_reader = possible_binary_reader + + # Default errors to replace instead of strict in order to get + # something that works. + if errors is None: + errors = "replace" + + # Wrap the binary stream in a text stream with the correct + # encoding parameters. + return _make_text_stream( + binary_reader, + encoding, + errors, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def _force_correct_text_reader( + text_reader: t.IO[t.Any], + encoding: str | None, + errors: str | None, + force_readable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_reader, + encoding, + errors, + _is_binary_reader, + _find_binary_reader, + force_readable=force_readable, + ) + + +def _force_correct_text_writer( + text_writer: t.IO[t.Any], + encoding: str | None, + errors: str | None, + force_writable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_writer, + encoding, + errors, + _is_binary_writer, + _find_binary_writer, + force_writable=force_writable, + ) + + +def get_binary_stdin() -> t.BinaryIO: + reader = _find_binary_reader(sys.stdin) + if reader is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdin.") + return reader + + +def get_binary_stdout() -> t.BinaryIO: + writer = _find_binary_writer(sys.stdout) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdout.") + return writer + + +def get_binary_stderr() -> t.BinaryIO: + writer = _find_binary_writer(sys.stderr) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stderr.") + return writer + + +def get_text_stdin(encoding: str | None = None, errors: str | None = None) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) + + +def get_text_stdout(encoding: str | None = None, errors: str | None = None) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) + + +def get_text_stderr(encoding: str | None = None, errors: str | None = None) -> t.TextIO: + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) + + +def _wrap_io_open( + file: str | os.PathLike[str] | int, + mode: str, + encoding: str | None, + errors: str | None, +) -> t.IO[t.Any]: + """Handles not passing ``encoding`` and ``errors`` in binary mode.""" + if "b" in mode: + return open(file, mode) + + return open(file, mode, encoding=encoding, errors=errors) + + +def open_stream( + filename: str | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + atomic: bool = False, +) -> tuple[t.IO[t.Any], bool]: + binary = "b" in mode + filename = os.fspath(filename) + + # Standard streams first. These are simple because they ignore the + # atomic flag. Use fsdecode to handle Path("-"). + if os.fsdecode(filename) == "-": + if any(m in mode for m in ["w", "a", "x"]): + if binary: + return get_binary_stdout(), False + return get_text_stdout(encoding=encoding, errors=errors), False + if binary: + return get_binary_stdin(), False + return get_text_stdin(encoding=encoding, errors=errors), False + + # Non-atomic writes directly go out through the regular open functions. + if not atomic: + return _wrap_io_open(filename, mode, encoding, errors), True + + # Some usability stuff for atomic writes + if "a" in mode: + raise ValueError( + "Appending to an existing file is not supported, because that" + " would involve an expensive `copy`-operation to a temporary" + " file. Open the file in normal `w`-mode and copy explicitly" + " if that's what you're after." + ) + if "x" in mode: + raise ValueError("Use the `overwrite`-parameter instead.") + if "w" not in mode: + raise ValueError("Atomic writes only make sense with `w`-mode.") + + # Atomic writes are more complicated. They work by opening a file + # as a proxy in the same folder and then using the fdopen + # functionality to wrap it in a Python file. Then we wrap it in an + # atomic file that moves the file over on close. + import errno + import random + + try: + perm: int | None = os.stat(filename).st_mode + except OSError: + perm = None + + flags = os.O_RDWR | os.O_CREAT | os.O_EXCL + + if binary: + flags |= getattr(os, "O_BINARY", 0) + + while True: + tmp_filename = os.path.join( + os.path.dirname(filename), + f".__atomic-write{random.randrange(1 << 32):08x}", + ) + try: + fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) + break + except OSError as e: + if e.errno == errno.EEXIST or ( + os.name == "nt" + and e.errno == errno.EACCES + and os.path.isdir(e.filename) + and os.access(e.filename, os.W_OK) + ): + continue + raise + + if perm is not None: + os.chmod(tmp_filename, perm) # in case perm includes bits in umask + + f = _wrap_io_open(fd, mode, encoding, errors) + af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) + return t.cast(t.IO[t.Any], af), True + + +class _AtomicFile: + def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None: + self._f = f + self._tmp_filename = tmp_filename + self._real_filename = real_filename + self.closed = False + + @property + def name(self) -> str: + return self._real_filename + + def close(self, delete: bool = False) -> None: + if self.closed: + return + self._f.close() + os.replace(self._tmp_filename, self._real_filename) + self.closed = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._f, name) + + def __enter__(self) -> _AtomicFile: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.close(delete=exc_type is not None) + + def __repr__(self) -> str: + return repr(self._f) + + +def strip_ansi(value: str) -> str: + return _ansi_re.sub("", value) + + +def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool: + while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): + stream = stream._stream + + return stream.__class__.__module__.startswith("ipykernel.") + + +def should_strip_ansi( + stream: t.IO[t.Any] | None = None, color: bool | None = None +) -> bool: + if color is None: + if stream is None: + stream = sys.stdin + elif hasattr(stream, "color"): + # ._termui_impl._PagerWriter handles stripping ansi itself, + # so we don't need to strip it here + return False + return not isatty(stream) and not _is_jupyter_kernel_output(stream) + return not color + + +# Double check is needed so mypy does not analyze this on Linux. +if sys.platform.startswith("win") and WIN: + from ._winconsole import _get_windows_console_stream + + def _get_argv_encoding() -> str: + import locale + + return locale.getpreferredencoding() + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: str | None, errors: str | None + ) -> t.TextIO | None: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO[t.Any]) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.TextIO | None], + wrapper_func: t.Callable[[], t.TextIO], +) -> t.Callable[[], t.TextIO | None]: + cache: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.TextIO | None: + stream = src_func() + + if stream is None: + return None + + try: + rv = cache.get(stream) + except Exception: + rv = None + if rv is not None: + return rv + rv = wrapper_func() + try: + cache[stream] = rv + except Exception: + pass + return rv + + return func + + +_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) +_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) +_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) + + +binary_streams: cabc.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: cabc.Mapping[str, t.Callable[[str | None, str | None], t.TextIO]] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/venv/lib/python3.12/site-packages/click/_termui_impl.py b/venv/lib/python3.12/site-packages/click/_termui_impl.py new file mode 100644 index 0000000..70aabf8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/_termui_impl.py @@ -0,0 +1,972 @@ +""" +This module contains implementations for the termui module. To keep the +import time of Click down, some infrequently used functionality is +placed in this module and only imported as needed. +""" + +from __future__ import annotations + +import collections.abc as cabc +import contextlib +import math +import os +import shlex +import sys +import time +import typing as t +from gettext import gettext as _ +from io import StringIO +from pathlib import Path +from types import TracebackType + +from ._compat import _default_text_stdout +from ._compat import CYGWIN +from ._compat import get_best_encoding +from ._compat import isatty +from ._compat import strip_ansi +from ._compat import term_len +from ._compat import WIN +from .exceptions import ClickException +from .utils import echo + +V = t.TypeVar("V") + + +if os.name == "nt": + BEFORE_BAR = "\r" + AFTER_BAR = "\n" +else: + BEFORE_BAR = "\r\033[?25l" + AFTER_BAR = "\033[?25h\n" + + +class ProgressBar(t.Generic[V]): + def __init__( + self, + iterable: cabc.Iterable[V] | None, + length: int | None = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + label: str | None = None, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, + width: int = 30, + ) -> None: + self.fill_char = fill_char + self.empty_char = empty_char + self.bar_template = bar_template + self.info_sep = info_sep + self.hidden = hidden + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label: str = label or "" + + if file is None: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + file = StringIO() + + self.file = file + self.color = color + self.update_min_steps = update_min_steps + self._completed_intervals = 0 + self.width: int = width + self.autowidth: bool = width == 0 + + if length is None: + from operator import length_hint + + length = length_hint(iterable, -1) + + if length == -1: + length = None + if iterable is None: + if length is None: + raise TypeError("iterable or length is required") + iterable = t.cast("cabc.Iterable[V]", range(length)) + self.iter: cabc.Iterable[V] = iter(iterable) + self.length = length + self.pos: int = 0 + self.avg: list[float] = [] + self.last_eta: float + self.start: float + self.start = self.last_eta = time.time() + self.eta_known: bool = False + self.finished: bool = False + self.max_width: int | None = None + self.entered: bool = False + self.current_item: V | None = None + self._is_atty = isatty(self.file) + self._last_line: str | None = None + + def __enter__(self) -> ProgressBar[V]: + self.entered = True + self.render_progress() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.render_finish() + + def __iter__(self) -> cabc.Iterator[V]: + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + self.render_progress() + return self.generator() + + def __next__(self) -> V: + # Iteration is defined in terms of a generator function, + # returned by iter(self); use that to define next(). This works + # because `self.iter` is an iterable consumed by that generator, + # so it is re-entry safe. Calling `next(self.generator())` + # twice works and does "what you want". + return next(iter(self)) + + def render_finish(self) -> None: + # Steps stay pending until they add up to update_min_steps, so a length + # that is not a multiple of it keeps a sub-threshold remainder that + # never reaches pos. Apply it so the bar settles on its true position. + if self._completed_intervals: + self.make_step(self._completed_intervals) + self._completed_intervals = 0 + self.render_progress() + + if self.hidden or not self._is_atty: + return + self.file.write(AFTER_BAR) + self.file.flush() + + @property + def pct(self) -> float: + if self.finished: + return 1.0 + return min(self.pos / (float(self.length or 1) or 1), 1.0) + + @property + def time_per_iteration(self) -> float: + if not self.avg: + return 0.0 + return sum(self.avg) / float(len(self.avg)) + + @property + def eta(self) -> float: + if self.length is not None and not self.finished: + return self.time_per_iteration * (self.length - self.pos) + return 0.0 + + def format_eta(self) -> str: + if self.eta_known: + t = int(self.eta) + seconds = t % 60 + t //= 60 + minutes = t % 60 + t //= 60 + hours = t % 24 + t //= 24 + if t > 0: + return "{d}{day_label} {h:02}:{m:02}:{s:02}".format( + d=t, + day_label=_("d"), + h=hours, + m=minutes, + s=seconds, + ) + else: + return f"{hours:02}:{minutes:02}:{seconds:02}" + return "" + + def format_pos(self) -> str: + pos = str(self.pos) + if self.length is not None: + pos += f"/{self.length}" + return pos + + def format_pct(self) -> str: + return f"{int(self.pct * 100): 4}%"[1:] + + def format_bar(self) -> str: + if self.length is not None: + bar_length = int(self.pct * self.width) + bar = self.fill_char * bar_length + bar += self.empty_char * (self.width - bar_length) + elif self.finished: + bar = self.fill_char * self.width + else: + chars = list(self.empty_char * (self.width or 1)) + if self.time_per_iteration != 0: + chars[ + int( + (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) + * self.width + ) + ] = self.fill_char + bar = "".join(chars) + return bar + + def format_progress_line(self) -> str: + show_percent = self.show_percent + + info_bits = [] + if self.length is not None and show_percent is None: + show_percent = not self.show_pos + + if self.show_pos: + info_bits.append(self.format_pos()) + if show_percent: + info_bits.append(self.format_pct()) + if self.show_eta and self.eta_known and not self.finished: + info_bits.append(self.format_eta()) + if self.item_show_func is not None: + item_info = self.item_show_func(self.current_item) + if item_info is not None: + info_bits.append(item_info) + + return ( + self.bar_template + % { + "label": self.label, + "bar": self.format_bar(), + "info": self.info_sep.join(info_bits), + } + ).rstrip() + + def render_progress(self) -> None: + if self.hidden: + return + + if not self._is_atty: + # Only output the label once if the output is not a TTY. + if self._last_line != self.label: + self._last_line = self.label + echo(self.label, file=self.file, color=self.color) + return + + buf = [] + # Update width in case the terminal has been resized + if self.autowidth: + import shutil + + old_width = self.width + self.width = 0 + clutter_length = term_len(self.format_progress_line()) + new_width = max(0, shutil.get_terminal_size().columns - clutter_length) + if new_width < old_width and self.max_width is not None: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) + self.max_width = new_width + self.width = new_width + + clear_width = self.width + if self.max_width is not None: + clear_width = self.max_width + + buf.append(BEFORE_BAR) + line = self.format_progress_line() + line_len = term_len(line) + if self.max_width is None or self.max_width < line_len: + self.max_width = line_len + + buf.append(line) + buf.append(" " * (clear_width - line_len)) + line = "".join(buf) + # Render the line only if it changed. + + if line != self._last_line: + self._last_line = line + echo(line, file=self.file, color=self.color, nl=False) + self.file.flush() + + def make_step(self, n_steps: int) -> None: + self.pos += n_steps + if self.length is not None and self.pos >= self.length: + self.finished = True + + if (time.time() - self.last_eta) < 1.0: + return + + self.last_eta = time.time() + + # self.avg is a rolling list of length <= 7 of steps where steps are + # defined as time elapsed divided by the total progress through + # self.length. + if self.pos: + step = (time.time() - self.start) / self.pos + else: + step = time.time() - self.start + + self.avg = self.avg[-6:] + [step] + + self.eta_known = self.length is not None + + def update(self, n_steps: int, current_item: V | None = None) -> None: + """Update the progress bar by advancing a specified number of + steps, and optionally set the ``current_item`` for this new + position. + + :param n_steps: Number of steps to advance. + :param current_item: Optional item to set as ``current_item`` + for the updated position. + + .. versionchanged:: 8.0 + Added the ``current_item`` optional parameter. + + .. versionchanged:: 8.0 + Only render when the number of steps meets the + ``update_min_steps`` threshold. + """ + if current_item is not None: + self.current_item = current_item + + self._completed_intervals += n_steps + + if self._completed_intervals >= self.update_min_steps: + self.make_step(self._completed_intervals) + self.render_progress() + self._completed_intervals = 0 + + def finish(self) -> None: + self.eta_known = False + self.current_item = None + self.finished = True + + def generator(self) -> cabc.Iterator[V]: + """Return a generator which yields the items added to the bar + during construction, and updates the progress bar *after* the + yielded block returns. + """ + # WARNING: the iterator interface for `ProgressBar` relies on + # this and only works because this is a simple generator which + # doesn't create or manage additional state. If this function + # changes, the impact should be evaluated both against + # `iter(bar)` and `next(bar)`. `next()` in particular may call + # `self.generator()` repeatedly, and this must remain safe in + # order for that interface to work. + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + + if not self._is_atty: + yield from self.iter + else: + for rv in self.iter: + self.current_item = rv + + # This allows show_item_func to be updated before the + # item is processed. Only trigger at the beginning of + # the update interval. + if self._completed_intervals == 0: + self.render_progress() + + yield rv + self.update(1) + + self.finish() + self.render_progress() + + +class _PagerWriter: + """Wrap a pager's output stream to strip ANSI styling when colors are + disabled. + + The wrapped stream is owned by the pager strategy that produced it, so this + wrapper never closes it: ``_pipepager`` closes its pipe to signal EOF to the + pager process, ``_tempfilepager`` closes and removes its temporary file, and + ``_nullpager`` leaves an external stream such as ``sys.stdout`` untouched. + + The ``color`` attribute lets :func:`click.echo` detect that ANSI stripping + is handled here, so it doesn't strip a second time (see + :func:`._compat.should_strip_ansi`). + """ + + def __init__(self, stream: t.TextIO, color: bool) -> None: + self._stream = stream + self.color = color + + def write(self, text: str) -> int: + if not self.color: + text = strip_ansi(text) + + return self._stream.write(text) + + def writelines(self, lines: cabc.Iterable[str]) -> None: + for line in lines: + self.write(line) + + def flush(self) -> None: + self._stream.flush() + + def close(self) -> None: + # The pager strategy owns the stream's lifecycle. Flush pending output, + # but never close the underlying stream from here. + self._stream.flush() + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._stream, name) + + +def _resolve_pager_command(cmd_parts: list[str]) -> tuple[Path, list[str]] | None: + """Resolve a pager ``argv`` to an absolute command path and its parameters. + + The command is looked up with :func:`shutil.which`, which the + :mod:`subprocess` docs recommend on every platform for an unqualified name: + https://docs.python.org/3/library/subprocess.html#popen-constructor + + Returns ``None`` when there is no command to run or it is not on the path, + leaving the caller to pick a fallback. + """ + if not cmd_parts: + return None + + import shutil + + cmd_filepath = shutil.which(cmd_parts[0]) + + if not cmd_filepath: + return None + + # Normalize to an absolute path without resolving symlinks: multi-call + # binaries such as busybox derive their identity from the link they are + # invoked through, so resolving less -> busybox makes them misbehave. + # See https://github.com/pallets/click/issues/2943 and + # https://github.com/pallets/click/pull/2944 + return Path(cmd_filepath).absolute(), cmd_parts[1:] + + +def _pager_contextmanager( + color: bool | None = None, +) -> t.ContextManager[tuple[t.TextIO, bool | None]]: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if stdout is None: + stdout = StringIO() + + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, color) + + # Split using POSIX mode (the default) so that quote characters are + # stripped from tokens and quoted Windows paths are preserved. + # Non-POSIX mode retains quotes in tokens, and wrapping tokens + # with shlex.quote re-introduces quoting issues on Windows. + pager_cmd_parts = shlex.split(os.environ.get("PAGER", "")) + + if pager_cmd_parts: + # Piping to `more` on Windows adds spurious \r\n, so it gets the temp + # file strategy whatever the user asked for. + use_tempfile = WIN + else: + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, color) + + use_tempfile = WIN or sys.platform.startswith("os2") + pager_cmd_parts = ["more"] if use_tempfile else ["less"] + + resolved = _resolve_pager_command(pager_cmd_parts) + + if resolved is None: + # Page through stdout, which _nullpager leaves open for its owner. + return _nullpager(stdout, color) + + cmd_path, cmd_params = resolved + + if use_tempfile: + return _tempfilepager(cmd_path, cmd_params, color) + + return _pipepager(cmd_path, cmd_params, color) + + +@contextlib.contextmanager +def get_pager_file(color: bool | None = None) -> t.Generator[t.TextIO, None, None]: + """Context manager. + + Yields a writable file-like object which can be used as an output pager. + + .. versionadded:: 8.4.0 + + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + with _pager_contextmanager(color=color) as (stream, color): + # Every pager strategy yields a text stream, so the only thing left to + # do is strip ANSI styling when colors are disabled. A strategy yields + # None when it has no opinion on colors, which settles as off here. The + # wrapper does not close the stream: each strategy owns its stream's + # lifecycle. + writer = _PagerWriter(stream, color=bool(color)) + try: + yield t.cast(t.TextIO, writer) + finally: + writer.flush() + + +def _less_uses_raw_mode(less_env: str, cmd_params: list[str]) -> bool: + """Detect a raw-control-characters request in a ``less`` invocation. + + Parses the tokens of the ``LESS`` environment variable and the command + line: only short-option clusters (``-R``, ``-rX``, ``-XrY``) and the + ``--raw-control-chars`` long option request raw mode. Filenames and + long-option values may carry the letter ``r`` without being such a + request. + """ + try: + env_tokens = shlex.split(less_env) + except ValueError: + # Unbalanced quotes make ``LESS`` unparsable: fall back to whitespace + # splitting rather than failing the pager over it. + env_tokens = less_env.split() + tokens = env_tokens + cmd_params + + for token in tokens: + if token.startswith("--"): + if token.casefold() == "--raw-control-chars": + return True + elif token.startswith("-") and len(token) > 1: + if "r" in token[1:].casefold(): + return True + + return False + + +@contextlib.contextmanager +def _pipepager( + cmd_path: Path, cmd_params: list[str], color: bool | None = None +) -> t.Iterator[tuple[t.TextIO, bool | None]]: + """Page through text by feeding it to another program. + + Invokes the pager via :class:`subprocess.Popen` with an ``argv`` list. + + Invoking a pager through this might support colors: if piping to + ``less`` and the user hasn't decided on colors, ``LESS=-R`` is set + automatically. + """ + import subprocess + + # Make a local copy of the environment to not affect the global one. + env = dict(os.environ) + + # If we're piping to less and the user hasn't decided on colors, we enable + # them: either the invocation already requests raw control characters, or + # ``LESS=-R`` is injected. Match on the stem so a resolved ``less.exe`` is + # recognized like bare ``less``, case-insensitively on Windows since its + # filesystems are. POSIX keeps an exact match. + cmd_name = cmd_path.stem + if WIN: + # ``_pager_contextmanager`` currently routes every Windows invocation + # to ``_tempfilepager``, so this branch is not reachable on Windows + # today: it keeps the detection correct should that routing ever + # change. + cmd_name = cmd_name.casefold() + + if color is None and cmd_name == "less": + less_env = os.environ.get("LESS", "") + if _less_uses_raw_mode(less_env, cmd_params): + color = True + elif not less_env and not cmd_params: + env["LESS"] = "-R" + color = True + + c = subprocess.Popen( + [str(cmd_path)] + cmd_params, + shell=False, + stdin=subprocess.PIPE, + env=env, + errors="replace", + text=True, + ) + # With ``text=True``, ``c.stdin`` is already a text stream that encodes + # writes for the pager process (honoring ``errors="replace"``). + stdin = t.cast(t.TextIO, c.stdin) + try: + yield stdin, color + except BrokenPipeError: + # In case the pager exited unexpectedly, ignore the broken pipe error. + pass + except Exception as e: + # In case there is an exception we want to close the pager immediately + # and let the caller handle it. + # Otherwise the pager will keep running, and the user may not notice + # the error message, or worse yet it may leave the terminal in a broken state. + c.terminate() + raise e + finally: + # We must close stdin and wait for the pager to exit before we continue + try: + stdin.close() + # Close implies flush, so it might throw a BrokenPipeError if the pager + # process exited already. + except BrokenPipeError: + pass + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting + # search or other commands inside less). + # + # That means when the user hits ^C, the parent process (click) terminates, + # but less is still alive, paging the output and messing up the terminal. + # + # If the user wants to make the pager exit on ^C, they should set + # `LESS='-K'`. It's not our decision to make. + while True: + try: + c.wait() + except KeyboardInterrupt: + pass + else: + break + + +@contextlib.contextmanager +def _tempfilepager( + cmd_path: Path, cmd_params: list[str], color: bool | None = None +) -> t.Iterator[tuple[t.TextIO, bool | None]]: + """Page through text by invoking a program on a temporary file. + + Used as the primary pager strategy on Windows (where piping to + ``more`` adds spurious ``\\r\\n``), and as a fallback on other + platforms. The command is invoked with any parameters from ``PAGER`` + followed by the temporary file name. + """ + import subprocess + import tempfile + + encoding = get_best_encoding(sys.stdout) + # On Windows, NamedTemporaryFile cannot be opened by another process + # while Python still has it open, so we use delete=False and clean up manually + # rather than using a contextmanager here. + f = tempfile.NamedTemporaryFile( + mode="w", encoding=encoding, errors="replace", delete=False + ) + try: + yield t.cast(t.TextIO, f), color + f.flush() + f.close() + subprocess.call([str(cmd_path), *cmd_params, f.name]) + finally: + # An error raised while paging skips the close() above, and Windows + # refuses to unlink a file the process still holds open. Closing here + # keeps that PermissionError from replacing the original exception. + # close() is idempotent, so this is a no-op on the success path. + f.close() + os.unlink(f.name) + + +@contextlib.contextmanager +def _nullpager( + stream: t.TextIO, color: bool | None = None +) -> t.Iterator[tuple[t.TextIO, bool | None]]: + """Simply print unformatted text. This is the ultimate fallback. + + The stream comes from elsewhere (typically ``sys.stdout``), so its lifecycle + is left untouched: :class:`_PagerWriter` never closes what it wraps, and it + is the only thing :func:`get_pager_file` hands to the caller. + """ + yield stream, color + + +class Editor: + def __init__( + self, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + ) -> None: + self.editor = editor + self.env = env + self.require_save = require_save + self.extension = extension + + def get_editor(self) -> str: + if self.editor is not None: + return self.editor + for key in "VISUAL", "EDITOR": + rv = os.environ.get(key) + if rv: + return rv + if WIN: + return "notepad" + + from shutil import which + + for editor in "sensible-editor", "vim", "nano": + if which(editor) is not None: + return editor + return "vi" + + def edit_files(self, filenames: cabc.Iterable[str | os.PathLike[str]]) -> None: + """Open files in the user's editor.""" + import shlex + import subprocess + + editor = self.get_editor() + environ: dict[str, str] | None = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + try: + # Split in POSIX mode (the default) for the same reasons as + # in pager(): strips quotes from tokens and preserves quoted + # Windows paths. + c = subprocess.Popen( + args=shlex.split(editor) + list(filenames), + env=environ, + ) + exit_code = c.wait() + if exit_code != 0: + raise ClickException( + _("{editor}: Editing failed").format(editor=editor) + ) + except OSError as e: + raise ClickException( + _("{editor}: Editing failed: {e}").format(editor=editor, e=e) + ) from e + + @t.overload + def edit(self, text: bytes | bytearray) -> bytes | None: ... + + # We cannot know whether or not the type expected is str or bytes when None + # is passed, so str is returned as that was what was done before. + @t.overload + def edit(self, text: str | None) -> str | None: ... + + def edit(self, text: str | bytes | bytearray | None) -> str | bytes | None: + import tempfile + + if text is None: + data: bytes | bytearray = b"" + elif isinstance(text, (bytes, bytearray)): + data = text + else: + if text and not text.endswith("\n"): + text += "\n" + + if WIN: + data = text.replace("\n", "\r\n").encode("utf-8-sig") + else: + data = text.encode("utf-8") + + fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) + f: t.BinaryIO + + try: + with os.fdopen(fd, "wb") as f: + f.write(data) + + # If the filesystem resolution is 1 second, like Mac OS + # 10.12 Extended, or 2 seconds, like FAT32, and the editor + # closes very fast, require_save can fail. Set the modified + # time to be 2 seconds in the past to work around this. + os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) + # Depending on the resolution, the exact value might not be + # recorded, so get the new recorded value. + timestamp = os.path.getmtime(name) + + self.edit_files((name,)) + + if self.require_save and os.path.getmtime(name) == timestamp: + return None + + with open(name, "rb") as f: + rv = f.read() + + if isinstance(text, (bytes, bytearray)): + return rv + + return rv.decode("utf-8-sig").replace("\r\n", "\n") + finally: + os.unlink(name) + + +def open_url(url: str, wait: bool = False, locate: bool = False) -> int: + import subprocess + + def _unquote_file(url: str) -> str: + from urllib.parse import unquote + + if url.startswith("file://"): + url = unquote(url[7:]) + + return url + + if sys.platform == "darwin": + args = ["open"] + if wait: + args.append("-W") + if locate: + args.append("-R") + args.append(_unquote_file(url)) + null = open("/dev/null", "w") + try: + return subprocess.Popen(args, stderr=null).wait() + finally: + null.close() + elif WIN: + if locate: + url = _unquote_file(url) + args = ["explorer", "/select,", url] + try: + return subprocess.call(args) + except OSError: + return 127 + else: + try: + os.startfile(url) # type: ignore[attr-defined] + except OSError: + return 127 + return 0 + elif CYGWIN: + if locate: + url = _unquote_file(url) + args = ["cygstart", os.path.dirname(url)] + else: + args = ["cygstart"] + if wait: + args.append("-w") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 + + try: + if locate: + url = os.path.dirname(_unquote_file(url)) or "." + else: + url = _unquote_file(url) + c = subprocess.Popen(["xdg-open", url]) + if wait: + return c.wait() + return 0 + except OSError: + if url.startswith(("http://", "https://")) and not locate and not wait: + import webbrowser + + webbrowser.open(url) + return 0 + return 1 + + +def _translate_ch_to_exc(ch: str) -> None: + if ch == "\x03": + raise KeyboardInterrupt() + + if ch == "\x04" and not WIN: # Unix-like, Ctrl+D + raise EOFError() + + if ch == "\x1a" and WIN: # Windows, Ctrl+Z + raise EOFError() + + +if sys.platform == "win32": + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> cabc.Iterator[int]: + yield -1 + + def getchar(echo: bool) -> str: + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. + + if echo: + func = t.cast(t.Callable[[], str], msvcrt.getwche) + else: + func = t.cast(t.Callable[[], str], msvcrt.getwch) + + rv = func() + + if rv in ("\x00", "\xe0"): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() + + _translate_ch_to_exc(rv) + return rv + +else: + import termios + import tty + + @contextlib.contextmanager + def raw_terminal() -> cabc.Iterator[int]: + f: t.TextIO | None + fd: int + + if not isatty(sys.stdin): + f = open("/dev/tty") + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + + try: + old_settings = termios.tcgetattr(fd) + + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo: bool) -> str: + with raw_terminal() as fd: + ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") + + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + + _translate_ch_to_exc(ch) + return ch diff --git a/venv/lib/python3.12/site-packages/click/_textwrap.py b/venv/lib/python3.12/site-packages/click/_textwrap.py new file mode 100644 index 0000000..82840f2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/_textwrap.py @@ -0,0 +1,188 @@ +from __future__ import annotations + +import collections.abc as cabc +import textwrap +from contextlib import contextmanager + +from ._compat import _ansi_re +from ._compat import term_len + + +def _truncate_visible(text: str, n: int) -> str: + """Return the longest prefix of ``text`` containing at most ``n`` visible + characters. + + ANSI escape sequences inside the prefix are kept intact and do not count + toward the visible width. A cut is never placed inside an escape sequence. + """ + if n <= 0: + return "" + + visible = 0 + i = 0 + cut = 0 + end = len(text) + while i < end: + m = _ansi_re.match(text, i) + if m is not None: + i = m.end() + continue + visible += 1 + i += 1 + cut = i + if visible >= n: + break + return text[:cut] + + +class TextWrapper(textwrap.TextWrapper): + """``textwrap.TextWrapper`` variant that measures widths by visible + character count. + + ANSI escape sequences embedded in chunks, indents, or the placeholder are + excluded from the width budget. Without this, styled help text (a styled + ``Usage:`` prefix, a colorized option name, ...) would be wrapped earlier + than its visible length warrants and tokens would split mid-word. + """ + + def _handle_long_word( + self, + reversed_chunks: list[str], + cur_line: list[str], + cur_len: int, + width: int, + ) -> None: + space_left = max(width - cur_len, 1) + + if self.break_long_words: + last = reversed_chunks[-1] + cut = _truncate_visible(last, space_left) + res = last[len(cut) :] + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + def _wrap_chunks(self, chunks: list[str]) -> list[str]: + """Wrap chunks counting widths in visible characters. + + Mirrors the algorithm of :meth:`textwrap.TextWrapper._wrap_chunks` + with every width measurement routed through + :func:`click._compat.term_len` instead of :func:`len`, so ANSI escape + bytes in chunks, indents, or the placeholder do not inflate the count. + + .. seealso:: + :class:`textwrap.TextWrapper` in the Python standard library documentation: + https://docs.python.org/3/library/textwrap.html#textwrap.TextWrapper + + Reference implementation in CPython: + https://github.com/python/cpython/blob/main/Lib/textwrap.py + """ + lines: list[str] = [] + if self.width <= 0: + raise ValueError(f"invalid width {self.width!r} (must be > 0)") + if self.max_lines is not None: + if self.max_lines > 1: + indent = self.subsequent_indent + else: + indent = self.initial_indent + if term_len(indent) + term_len(self.placeholder.lstrip()) > self.width: + raise ValueError("placeholder too large for max width") + + chunks.reverse() + + while chunks: + cur_line: list[str] = [] + cur_len = 0 + + if lines: + indent = self.subsequent_indent + else: + indent = self.initial_indent + + width = self.width - term_len(indent) + + if self.drop_whitespace and chunks[-1].strip() == "" and lines: + del chunks[-1] + + while chunks: + n = term_len(chunks[-1]) + + if cur_len + n <= width: + cur_line.append(chunks.pop()) + cur_len += n + + else: + break + + if chunks and term_len(chunks[-1]) > width: + self._handle_long_word(chunks, cur_line, cur_len, width) + cur_len = sum(map(term_len, cur_line)) + + if self.drop_whitespace and cur_line and cur_line[-1].strip() == "": + cur_len -= term_len(cur_line[-1]) + del cur_line[-1] + + if cur_line: + if ( + self.max_lines is None + or len(lines) + 1 < self.max_lines + or ( + not chunks + or self.drop_whitespace + and len(chunks) == 1 + and not chunks[0].strip() + ) + and cur_len <= width + ): + lines.append(indent + "".join(cur_line)) + else: + while cur_line: + if ( + cur_line[-1].strip() + and cur_len + term_len(self.placeholder) <= width + ): + cur_line.append(self.placeholder) + lines.append(indent + "".join(cur_line)) + break + cur_len -= term_len(cur_line[-1]) + del cur_line[-1] + else: + if lines: + prev_line = lines[-1].rstrip() + if ( + term_len(prev_line) + term_len(self.placeholder) + <= self.width + ): + lines[-1] = prev_line + self.placeholder + break + lines.append(indent + self.placeholder.lstrip()) + break + + return lines + + @contextmanager + def extra_indent(self, indent: str) -> cabc.Iterator[None]: + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text: str) -> str: + rv = [] + + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + + if idx > 0: + indent = self.subsequent_indent + + rv.append(f"{indent}{line}") + + return "\n".join(rv) diff --git a/venv/lib/python3.12/site-packages/click/_utils.py b/venv/lib/python3.12/site-packages/click/_utils.py new file mode 100644 index 0000000..05ee2e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/_utils.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +import enum +import typing as t + + +class Sentinel(enum.Enum): + """Enum used to define sentinel values. + + .. seealso:: + + `PEP 661 - Sentinel Values `_. + """ + + UNSET = object() + FLAG_NEEDS_VALUE = object() + + def __repr__(self) -> str: + return f"{self.__class__.__name__}.{self.name}" + + +UNSET: t.Literal[Sentinel.UNSET] = Sentinel.UNSET +"""Sentinel used to indicate that a value is not set.""" + +FLAG_NEEDS_VALUE: t.Literal[Sentinel.FLAG_NEEDS_VALUE] = Sentinel.FLAG_NEEDS_VALUE +"""Sentinel used to indicate an option was passed as a flag without a +value but is not a flag option. + +``Option.consume_value`` uses this to prompt or use the ``flag_value``. +""" + +T_UNSET: t.TypeAlias = t.Literal[Sentinel.UNSET] +"""Type hint for the :data:`UNSET` sentinel value.""" + +T_FLAG_NEEDS_VALUE: t.TypeAlias = t.Literal[Sentinel.FLAG_NEEDS_VALUE] +"""Type hint for the :data:`FLAG_NEEDS_VALUE` sentinel value.""" diff --git a/venv/lib/python3.12/site-packages/click/_winconsole.py b/venv/lib/python3.12/site-packages/click/_winconsole.py new file mode 100644 index 0000000..d25178d --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/_winconsole.py @@ -0,0 +1,297 @@ +# This module is based on the excellent work by Adam Bartoš who +# provided a lot of what went into the implementation here in +# the discussion to issue1602 in the Python bug tracker. +# +# There are some general differences in regards to how this works +# compared to the original patches as we do not need to patch +# the entire interpreter but just work in our little world of +# echo and prompt. +from __future__ import annotations + +import collections.abc as cabc +import io +import sys +import time +import typing as t +from ctypes import Array +from ctypes import byref +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_ssize_t +from ctypes import c_ulong +from ctypes import c_void_p +from ctypes import POINTER +from ctypes import py_object +from ctypes import Structure +from ctypes.wintypes import DWORD +from ctypes.wintypes import HANDLE +from ctypes.wintypes import LPCWSTR +from ctypes.wintypes import LPWSTR +from gettext import gettext as _ + +from ._compat import _NonClosingTextIOWrapper + +assert sys.platform == "win32" +import msvcrt # noqa: E402 +from ctypes import windll # noqa: E402 +from ctypes import WINFUNCTYPE # noqa: E402 + +c_ssize_p = POINTER(c_ssize_t) + +kernel32 = windll.kernel32 +GetStdHandle = kernel32.GetStdHandle +ReadConsoleW = kernel32.ReadConsoleW +WriteConsoleW = kernel32.WriteConsoleW +GetConsoleMode = kernel32.GetConsoleMode +GetLastError = kernel32.GetLastError +GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ("CommandLineToArgvW", windll.shell32) +) +LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) + +STDIN_HANDLE = GetStdHandle(-10) +STDOUT_HANDLE = GetStdHandle(-11) +STDERR_HANDLE = GetStdHandle(-12) + +PyBUF_SIMPLE = 0 +PyBUF_WRITABLE = 1 + +ERROR_SUCCESS = 0 +ERROR_NOT_ENOUGH_MEMORY = 8 +ERROR_OPERATION_ABORTED = 995 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EOF = b"\x1a" +MAX_BYTES_WRITTEN = 32767 + +if t.TYPE_CHECKING: + try: + # Using `typing_extensions.Buffer` instead of `collections.abc` + # on Windows for some reason does not have `Sized` implemented. + from collections.abc import Buffer # type: ignore + except ImportError: + from typing_extensions import Buffer + +try: + from ctypes import pythonapi +except ImportError: + # On PyPy we cannot get buffers so our ability to operate here is + # severely limited. + get_buffer = None +else: + + class Py_buffer(Structure): + _fields_ = [ # noqa: RUF012 + ("buf", c_void_p), + ("obj", py_object), + ("len", c_ssize_t), + ("itemsize", c_ssize_t), + ("readonly", c_int), + ("ndim", c_int), + ("format", c_char_p), + ("shape", c_ssize_p), + ("strides", c_ssize_p), + ("suboffsets", c_ssize_p), + ("internal", c_void_p), + ] + + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer + PyBuffer_Release = pythonapi.PyBuffer_Release + + def get_buffer(obj: Buffer, writable: bool = False) -> Array[c_char]: + buf = Py_buffer() + flags: int = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + out: Array[c_char] = buffer_type.from_address(buf.buf) + return out + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle: int | None) -> None: + self.handle = handle + + def isatty(self) -> t.Literal[True]: + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self) -> t.Literal[True]: + return True + + def readinto(self, b: Buffer) -> int: + bytes_to_be_read = len(b) + if not bytes_to_be_read: + return 0 + elif bytes_to_be_read % 2: + raise ValueError( + "cannot read odd number of bytes from UTF-16-LE encoded console" + ) + + buffer = get_buffer(b, writable=True) + code_units_to_be_read = bytes_to_be_read // 2 + code_units_read = c_ulong() + + rv = ReadConsoleW( + HANDLE(self.handle), + buffer, + code_units_to_be_read, + byref(code_units_read), + None, + ) + if GetLastError() == ERROR_OPERATION_ABORTED: + # wait for KeyboardInterrupt + time.sleep(0.1) + if not rv: + raise OSError(_("Windows error: {error}").format(error=GetLastError())) + + if buffer[0] == EOF: + return 0 + return 2 * code_units_read.value + + +class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): + def writable(self) -> t.Literal[True]: + return True + + @staticmethod + def _get_error_message(errno: int) -> str: + if errno == ERROR_SUCCESS: + return "ERROR_SUCCESS" + elif errno == ERROR_NOT_ENOUGH_MEMORY: + return "ERROR_NOT_ENOUGH_MEMORY" + return _("Windows error: {error}").format(error=errno) + + def write(self, b: Buffer) -> int: + bytes_to_be_written = len(b) + buf = get_buffer(b) + code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 + code_units_written = c_ulong() + + WriteConsoleW( + HANDLE(self.handle), + buf, + code_units_to_be_written, + byref(code_units_written), + None, + ) + bytes_written = 2 * code_units_written.value + + if bytes_written == 0 and bytes_to_be_written > 0: + raise OSError(self._get_error_message(GetLastError())) + return bytes_written + + +class ConsoleStream: + def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: + self._text_stream = text_stream + self.buffer = byte_stream + + @property + def name(self) -> str: + return self.buffer.name + + def write(self, x: t.AnyStr) -> int: + if isinstance(x, str): + return self._text_stream.write(x) + try: + self.flush() + except Exception: + pass + return self.buffer.write(x) + + def writelines(self, lines: cabc.Iterable[t.AnyStr]) -> None: + for line in lines: + self.write(line) + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._text_stream, name) + + def isatty(self) -> bool: + return self.buffer.isatty() + + def __repr__(self) -> str: + return f"" + + +def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +_stream_factories: cabc.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { + 0: _get_text_stdin, + 1: _get_text_stdout, + 2: _get_text_stderr, +} + + +def _is_console(f: t.TextIO) -> bool: + if not hasattr(f, "fileno"): + return False + + try: + fileno = f.fileno() + except (OSError, io.UnsupportedOperation): + return False + + handle = msvcrt.get_osfhandle(fileno) + return bool(GetConsoleMode(handle, byref(DWORD()))) + + +def _get_windows_console_stream( + f: t.TextIO, encoding: str | None, errors: str | None +) -> t.TextIO | None: + if ( + get_buffer is None + or encoding not in {"utf-16-le", None} + or errors not in {"strict", None} + or not _is_console(f) + ): + return None + + func = _stream_factories.get(f.fileno()) + if func is None: + return None + + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/venv/lib/python3.12/site-packages/click/core.py b/venv/lib/python3.12/site-packages/click/core.py new file mode 100644 index 0000000..de129ec --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/core.py @@ -0,0 +1,3799 @@ +from __future__ import annotations + +import collections.abc as cabc +import enum +import errno +import inspect +import os +import sys +import typing as t +from abc import ABC +from abc import abstractmethod +from collections import abc +from collections import Counter +from contextlib import AbstractContextManager +from contextlib import contextmanager +from contextlib import ExitStack +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat +from types import TracebackType + +from . import types +from ._utils import FLAG_NEEDS_VALUE +from ._utils import UNSET +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import NoArgsIsHelpError +from .exceptions import NoSuchCommand +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import _OptionParser +from .parser import _split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .utils import _detect_program_name +from .utils import _expand_args +from .utils import _make_default_short_help +from .utils import _PacifyFlushWrapper +from .utils import echo +from .utils import make_str + +if t.TYPE_CHECKING: + from typing_extensions import Self + + from .shell_completion import CompletionItem + +F = t.TypeVar("F", bound="t.Callable[..., t.Any]") +V = t.TypeVar("V") + +# Reserved storage name of the automatic help option. No user parameter is +# expected to claim it. +_HELP_OPTION_STORAGE_NAME = "_click_default_help" + + +def _complete_visible_commands( + ctx: Context, incomplete: str +) -> cabc.Iterator[tuple[str, Command]]: + """List all the subcommands of a group that start with the + incomplete value and aren't hidden. + + :param ctx: Invocation context for the group. + :param incomplete: Value being completed. May be empty. + """ + multi = t.cast(Group, ctx.command) + + for name in multi.list_commands(ctx): + if name.startswith(incomplete): + command = multi.get_command(ctx, name) + + if command is not None and not command.hidden: + yield name, command + + +def _check_nested_chain( + base_command: Group, cmd_name: str, cmd: Command, register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, Group): + return + + if register: + message = ( + f"It is not possible to add the group {cmd_name!r} to another" + f" group {base_command.name!r} that is in chain mode." + ) + else: + message = ( + f"Found the group {cmd_name!r} as subcommand to another group " + f" {base_command.name!r} that is in chain mode. This is not supported." + ) + + raise RuntimeError(message) + + +def _format_deprecated_label(deprecated: bool | str) -> str: + """Return the parenthesized deprecation label shown in help text.""" + label = _("deprecated").upper() + if isinstance(deprecated, str): + return f"({label}: {deprecated})" + return f"({label})" + + +def _format_deprecated_suffix(deprecated: bool | str) -> str: + """Return the trailing reason for a ``DeprecationWarning`` message, + prefixed with a space, or an empty string when no reason was given. + """ + if isinstance(deprecated, str): + return f" {deprecated}" + return "" + + +def batch(iterable: cabc.Iterable[V], batch_size: int) -> list[tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size), strict=False)) + + +@contextmanager +def augment_usage_errors( + ctx: Context, param: Parameter | None = None +) -> cabc.Generator[None]: + """Context manager that attaches extra information to exceptions.""" + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing( + invocation_order: cabc.Sequence[Parameter], + declaration_order: cabc.Sequence[Parameter], +) -> list[Parameter]: + """Returns all declared parameters in the order they should be processed. + + The declared parameters are re-shuffled depending on the order in which + they were invoked, as well as the eagerness of each parameters. + + The invocation order takes precedence over the declaration order. I.e. the + order in which the user provided them to the CLI is respected. + + This behavior and its effect on callback evaluation is detailed at: + https://click.palletsprojects.com/en/stable/advanced/#callback-evaluation-order + """ + + def sort_key(item: Parameter) -> tuple[bool, float]: + try: + idx: float = invocation_order.index(item) + except ValueError: + idx = float("inf") + + return not item.is_eager, idx + + return sorted(declaration_order, key=sort_key) + + +class ParameterSource(enum.IntEnum): + """This is an :class:`~enum.IntEnum` that indicates the source of a + parameter's value. + + Use :meth:`click.Context.get_parameter_source` to get the + source for a parameter by name. + + Members are ordered from most explicit to least explicit source. + This allows comparison to check if a value was explicitly provided: + + .. code-block:: python + + source = ctx.get_parameter_source("port") + if source < click.ParameterSource.DEFAULT_MAP: + ... # value was explicitly set + + .. versionchanged:: 8.3.3 + Use :class:`~enum.IntEnum` and reorder members from most to + least explicit. Supports comparison operators. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a value.""" + COMMANDLINE = enum.auto() + """The value was provided by the command line args.""" + ENVIRONMENT = enum.auto() + """The value was provided with an environment variable.""" + DEFAULT_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below that it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show the default value for commands. If this + value is not set, it defaults to the value from the parent + context. ``Command.show_default`` overrides this default for the + specific command. + + .. versionchanged:: 8.2 + The ``protected_args`` attribute is deprecated and will be removed in + Click 9.0. ``args`` will contain remaining unparsed tokens. + + .. versionchanged:: 8.1 + The ``show_default`` parameter is overridden by + ``Command.show_default``, instead of the other way around. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: type[HelpFormatter] = HelpFormatter + + parent: Context | None + command: Command + info_name: str | None + params: dict[str, t.Any] + args: list[str] + _protected_args: list[str] + _opt_prefixes: set[str] + obj: t.Any + _meta: dict[str, t.Any] + default_map: cabc.MutableMapping[str, t.Any] | None + invoked_subcommand: str | None + terminal_width: int | None + max_content_width: int | None + allow_extra_args: bool + allow_interspersed_args: bool + ignore_unknown_options: bool + help_option_names: list[str] + token_normalize_func: t.Callable[[str], str] | None + resilient_parsing: bool + auto_envvar_prefix: str | None + color: bool | None + show_default: bool | None + _close_callbacks: list[t.Callable[[], t.Any]] + _depth: int + _parameter_source: dict[str, ParameterSource] + _param_default_explicit: dict[str, bool] + _exit_stack: ExitStack + + def __init__( + self, + command: Command, + parent: Context | None = None, + info_name: str | None = None, + obj: t.Any | None = None, + auto_envvar_prefix: str | None = None, + default_map: cabc.MutableMapping[str, t.Any] | None = None, + terminal_width: int | None = None, + max_content_width: int | None = None, + resilient_parsing: bool = False, + allow_extra_args: bool | None = None, + allow_interspersed_args: bool | None = None, + ignore_unknown_options: bool | None = None, + help_option_names: list[str] | None = None, + token_normalize_func: t.Callable[[str], str] | None = None, + color: bool | None = None, + show_default: bool | None = None, + ) -> None: + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: Map of parameter names to their parsed values. Parameters + #: with ``expose_value=False`` are not stored. + self.params = {} + #: the leftover arguments. + self.args = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self._protected_args = [] + #: the collected prefixes of the command's options. + self._opt_prefixes = set(parent._opt_prefixes) if parent else set() + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj = obj + self._meta = getattr(parent, "meta", {}) + + #: A dictionary (-like object) with defaults for parameters. + if ( + default_map is None + and info_name is not None + and parent is not None + and parent.default_map is not None + ): + default_map = parent.default_map.get(info_name) + + self.default_map = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`result_callback`. + self.invoked_subcommand = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + + #: The width of the terminal (None is autodetection). + self.terminal_width = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ["--help"] + + #: The names for the help options. + self.help_option_names = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func = token_normalize_func + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = ( + f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" + ) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + + if auto_envvar_prefix is not None: + auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") + + self.auto_envvar_prefix = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color = color + + if show_default is None and parent is not None: + show_default = parent.show_default + + #: Show option default values when formatting help text. + self.show_default = show_default + + self._close_callbacks = [] + self._depth = 0 + self._parameter_source = {} + # Tracks whether the option that currently owns each parameter slot in + # :attr:`params` had its ``default`` set explicitly by the user. Used + # to tie-break feature-switch groups where multiple options share a + # parameter name and both fall back to their default value. + # Refs: https://github.com/pallets/click/issues/3403 + self._param_default_explicit = {} + self._exit_stack = ExitStack() + + @property + def protected_args(self) -> list[str]: + import warnings + + warnings.warn( + "'protected_args' is deprecated and will be removed in Click 9.0." + " 'args' will contain remaining unparsed tokens.", + DeprecationWarning, + stacklevel=2, + ) + return self._protected_args + + def to_info_dict(self) -> dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire CLI + structure. + + .. code-block:: python + + with Context(cli) as ctx: + info = ctx.to_info_dict() + + .. versionadded:: 8.0 + """ + return { + "command": self.command.to_info_dict(self), + "info_name": self.info_name, + "allow_extra_args": self.allow_extra_args, + "allow_interspersed_args": self.allow_interspersed_args, + "ignore_unknown_options": self.ignore_unknown_options, + "auto_envvar_prefix": self.auto_envvar_prefix, + } + + def __enter__(self) -> Self: + self._depth += 1 + push_context(self) + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> bool | None: + self._depth -= 1 + exit_result: bool | None = None + if self._depth == 0: + exit_result = self._close_with_exception_info(exc_type, exc_value, tb) + pop_context() + + return exit_result + + @contextmanager + def scope(self, cleanup: bool = True) -> cabc.Generator[Context]: + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self) -> dict[str, t.Any]: + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = f'{__name__}.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self) -> HelpFormatter: + """Creates the :class:`~click.HelpFormatter` for the help and + usage output. + + To quickly customize the formatter class used without overriding + this method, set the :attr:`formatter_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`formatter_class` attribute. + """ + return self.formatter_class( + width=self.terminal_width, max_width=self.max_content_width + ) + + def with_resource(self, context_manager: AbstractContextManager[V]) -> V: + """Register a resource as if it were used in a ``with`` + statement. The resource will be cleaned up when the context is + popped. + + Uses :meth:`contextlib.ExitStack.enter_context`. It calls the + resource's ``__enter__()`` method and returns the result. When + the context is popped, it closes the stack, which calls the + resource's ``__exit__()`` method. + + To register a cleanup function for something that isn't a + context manager, use :meth:`call_on_close`. Or use something + from :mod:`contextlib` to turn it into a context manager first. + + .. code-block:: python + + @click.group() + @click.option("--name") + @click.pass_context + def cli(ctx): + ctx.obj = ctx.with_resource(connect_db(name)) + + :param context_manager: The context manager to enter. + :return: Whatever ``context_manager.__enter__()`` returns. + + .. versionadded:: 8.0 + """ + return self._exit_stack.enter_context(context_manager) + + def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Register a function to be called when the context tears down. + + This can be used to close resources opened during the script + execution. Resources that support Python's context manager + protocol which would be used in a ``with`` statement should be + registered with :meth:`with_resource` instead. + + :param f: The function to execute on teardown. + """ + return self._exit_stack.callback(f) + + def close(self) -> None: + """Invoke all close callbacks registered with + :meth:`call_on_close`, and exit all context managers entered + with :meth:`with_resource`. + """ + self._close_with_exception_info(None, None, None) + + def _close_with_exception_info( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> bool | None: + """Unwind the exit stack by calling its :meth:`__exit__` providing the exception + information to allow for exception handling by the various resources registered + using :meth;`with_resource` + + :return: Whatever ``exit_stack.__exit__()`` returns. + """ + exit_result = self._exit_stack.__exit__(exc_type, exc_value, tb) + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + return exit_result + + @property + def command_path(self) -> str: + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = "" + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + parent_command_path = [self.parent.command_path] + + if isinstance(self.parent.command, Command): + for param in self.parent.command.get_params(self): + parent_command_path.extend(param.get_usage_pieces(self)) + + rv = f"{' '.join(parent_command_path)} {rv}" + return rv.lstrip() + + def find_root(self) -> Context: + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type: type[V]) -> V | None: + """Finds the closest object of a given type.""" + node: Context | None = self + + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + + node = node.parent + + return None + + def ensure_object(self, object_type: type[V]) -> V: + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + def _default_map_has(self, name: str | None) -> bool: + """Check if :attr:`default_map` contains a real value for ``name``. + + Returns ``False`` when the key is absent, the map is ``None``, + ``name`` is ``None``, or the stored value is the internal + :data:`UNSET` sentinel. + """ + return ( + name is not None + and self.default_map is not None + and name in self.default_map + and self.default_map[name] is not UNSET + ) + + @t.overload + def lookup_default( + self, name: str, call: t.Literal[True] = True + ) -> t.Any | None: ... + + @t.overload + def lookup_default( + self, name: str, call: t.Literal[False] = ... + ) -> t.Any | t.Callable[[], t.Any] | None: ... + + def lookup_default(self, name: str, call: bool = True) -> t.Any | None: + """Get the default for a parameter from :attr:`default_map`. + + :param name: Name of the parameter. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + if not self._default_map_has(name): + return None + + # Assert to make the type checker happy. + assert self.default_map is not None + value = self.default_map[name] + + if call and callable(value): + return value() + + return value + + def fail(self, message: str) -> t.NoReturn: + """Aborts the execution of the program with a specific error + message. + + :param message: the error message to fail with. + """ + raise UsageError(message, self) + + def abort(self) -> t.NoReturn: + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> t.NoReturn: + """Exits the application with a given exit code. + + .. versionchanged:: 8.2 + Callbacks and context managers registered with :meth:`call_on_close` + and :meth:`with_resource` are closed before exiting. + """ + self.close() + raise Exit(code) + + def get_usage(self) -> str: + """Helper method to get formatted usage string for the current + context and command. + """ + return self.command.get_usage(self) + + def get_help(self) -> str: + """Helper method to get formatted help page for the current + context and command. + """ + return self.command.get_help(self) + + def _make_sub_context(self, command: Command) -> Context: + """Create a new context of the same type as this context, but + for a new command. + + :meta private: + """ + return type(self)(command, info_name=command.name, parent=self) + + @t.overload + def invoke( + self, callback: t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any + ) -> V: ... + + @t.overload + def invoke(self, callback: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any: ... + + def invoke( + self, callback: Command | t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any + ) -> t.Any | V: + """Invokes a command callback in exactly the way it expects. There + are two ways to invoke this method: + + 1. the first argument can be a callback and all other arguments and + keyword arguments are forwarded directly to the function. + 2. the first argument is a click command object. In that case all + arguments are forwarded as well but proper click parameters + (options and click arguments) must be keyword arguments and Click + will fill in defaults. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + + .. versionchanged:: 3.2 + A new context is created, and missing arguments use default values. + """ + if isinstance(callback, Command): + other_cmd = callback + + if other_cmd.callback is None: + raise TypeError( + "The given command does not have a callback that can be invoked." + ) + else: + callback = t.cast("t.Callable[..., V]", other_cmd.callback) + + ctx = self._make_sub_context(other_cmd) + + for param in other_cmd.params: + if param.name not in kwargs and param.expose_value: + default_value = param.get_default(ctx) + # We explicitly hide the :attr:`UNSET` value to the user, as we + # choose to make it an implementation detail. And because ``invoke`` + # has been designed as part of Click public API, we return ``None`` + # instead. Refs: + # https://github.com/pallets/click/issues/3066 + # https://github.com/pallets/click/issues/3065 + # https://github.com/pallets/click/pull/3068 + if default_value is UNSET: + default_value = None + kwargs[param.name] = param.type_cast_value(ctx, default_value) + + # Track all kwargs as params, so that forward() will pass + # them on in subsequent calls. + ctx.params.update(kwargs) + else: + ctx = self + + with augment_usage_errors(self), ctx: + return callback(*args, **kwargs) + + def forward(self, cmd: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Similar to :meth:`invoke` but fills in default keyword + arguments from the current context if the other command expects + it. This cannot invoke callbacks directly, only other commands. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if ``forward`` is called at multiple levels. + """ + # Can only forward to other commands, not direct callbacks. + if not isinstance(cmd, Command): + raise TypeError("Callback is not a command.") + + for param in self.params: + if param not in kwargs: + kwargs[param] = self.params[param] + + return self.invoke(cmd, *args, **kwargs) + + def set_parameter_source(self, name: str, source: ParameterSource) -> None: + """Set the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + :param name: The name of the parameter. + :param source: A member of :class:`~click.core.ParameterSource`. + """ + self._parameter_source[name] = source + + def get_parameter_source(self, name: str) -> ParameterSource | None: + """Get the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + This can be useful for determining when a user specified a value + on the command line that is the same as the default value. It + will be :attr:`~click.core.ParameterSource.DEFAULT` only if the + value was actually taken from the default. + + :param name: The name of the parameter. + :rtype: ParameterSource + + .. versionchanged:: 8.0 + Returns ``None`` if the parameter was not provided from any + source. + """ + return self._parameter_source.get(name) + + +class Command: + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + :param deprecated: If ``True`` or non-empty string, issues a message + indicating that the command is deprecated and highlights + its deprecation in --help. The message can be customized + by using a string as the value. + + .. versionchanged:: 8.2 + This is the base class for all commands, not ``BaseCommand``. + ``deprecated`` can be set to a string as well to customize the + deprecation message. + + .. versionchanged:: 8.1 + ``help``, ``epilog``, and ``short_help`` are stored unprocessed, + all formatting is done when outputting help text, not at init, + and is done even if not using the ``@command`` decorator. + + .. versionchanged:: 8.0 + Added a ``repr`` showing the command name. + + .. versionchanged:: 7.1 + Added the ``no_args_is_help`` parameter. + + .. versionchanged:: 2.0 + Added the ``context_settings`` parameter. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: type[Context] = Context + + #: the default for the :attr:`Context.allow_extra_args` flag. + allow_extra_args = False + + #: the default for the :attr:`Context.allow_interspersed_args` flag. + allow_interspersed_args = True + + #: the default for the :attr:`Context.ignore_unknown_options` flag. + ignore_unknown_options = False + + name: str | None + context_settings: cabc.MutableMapping[str, t.Any] + callback: t.Callable[..., t.Any] | None + params: list[Parameter] + help: str | None + epilog: str | None + options_metavar: str | None + short_help: str | None + add_help_option: bool + _help_option: Option | None + no_args_is_help: bool + hidden: bool + deprecated: bool | str + + def __init__( + self, + name: str | None, + context_settings: cabc.MutableMapping[str, t.Any] | None = None, + callback: t.Callable[..., t.Any] | None = None, + params: list[Parameter] | None = None, + help: str | None = None, + epilog: str | None = None, + short_help: str | None = None, + options_metavar: str | None = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool | str = False, + ) -> None: + #: the name the command thinks it has. Upon registering a command + #: on a :class:`Group` the group will default the command name + #: with this information. You should instead use the + #: :class:`Context`\'s :attr:`~Context.info_name` attribute. + self.name = name + + if context_settings is None: + context_settings = {} + + #: an optional dictionary with defaults passed to the context. + self.context_settings = context_settings + + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params = params or [] + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self._help_option = None + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> dict[str, t.Any]: + return { + "name": self.name, + "params": [param.to_info_dict() for param in self.get_params(ctx)], + "help": self.help, + "epilog": self.epilog, + "short_help": self.short_help, + "hidden": self.hidden, + "deprecated": self.deprecated, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def get_usage(self, ctx: Context) -> str: + """Formats the usage line into a string and returns it. + + Calls :meth:`format_usage` internally. + """ + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_params(self, ctx: Context) -> list[Parameter]: + params = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + params = [*params, help_option] + + if __debug__: + import warnings + + opts = [opt for param in params for opt in param.opts] + opts_counter = Counter(opts) + duplicate_opts = (opt for opt, count in opts_counter.items() if count > 1) + + for duplicate_opt in duplicate_opts: + warnings.warn( + ( + f"The parameter {duplicate_opt} is used more than once. " + "Remove its duplicate as parameters should be unique." + ), + stacklevel=3, + ) + + # Options may deliberately share a storage name to compete for + # the same value (feature switches), but an argument sharing a + # name silently overwrites the other parameter's value. + names_counter = Counter(param.name for param in params) + duplicate_names = ( + name for name, count in names_counter.items() if count > 1 + ) + + for duplicate_name in duplicate_names: + sharers = [param for param in params if param.name == duplicate_name] + + if help_option in sharers: + warnings.warn( + ( + f"The name {duplicate_name!r} is reserved for the " + "automatic help option. Give the parameter a " + "different name." + ), + stacklevel=3, + ) + elif any(isinstance(param, Argument) for param in sharers): + warnings.warn( + ( + f"The name {duplicate_name!r} is used by an argument " + "and another parameter. They will overwrite each " + "other's value during parsing. Give each parameter " + "a unique name." + ), + stacklevel=3, + ) + + return params + + def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the usage line into the formatter. + + This is a low-level method called by :meth:`get_usage`. + """ + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, " ".join(pieces)) + + def collect_usage_pieces(self, ctx: Context) -> list[str]: + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] if self.options_metavar else [] + + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + + return rv + + def get_help_option_names(self, ctx: Context) -> list[str]: + """Returns the names for the help option. + + Drops duplicates and names already reserved by another parameter. Order of + :attr:`Context.help_option_names` is preserved, so the result is stable. + + .. versionchanged:: 8.5.0 + Names keep their declaration order. + """ + all_names = dict.fromkeys(ctx.help_option_names) + for param in self.params: + for name in (*param.opts, *param.secondary_opts): + all_names.pop(name, None) + return list(all_names) + + def get_help_option(self, ctx: Context) -> Option | None: + """Returns the help option object. + + Skipped if :attr:`add_help_option` is ``False``. + + .. versionchanged:: 8.5.0 + The help option stores its value under the reserved name + ``_click_default_help``, so a parameter named ``help`` no + longer breaks parsing. + + .. versionchanged:: 8.1.8 + The help option is now cached to avoid creating it multiple times. + """ + help_option_names = self.get_help_option_names(ctx) + + if not help_option_names or not self.add_help_option: + return None + + # Cache the help option object in private _help_option attribute to + # avoid creating it multiple times. Not doing this will break the + # callback ordering by iter_params_for_processing(), which relies on + # object comparison. + if self._help_option is None: + # Avoid circular import. + from .decorators import help_option + + # The help option never exposes its value, so it uses a reserved + # storage name, keeping it clear of user parameters (like an + # argument named "help") that would otherwise clobber its value. + help_option(*help_option_names, _HELP_OPTION_STORAGE_NAME)(self) + self._help_option = self.params.pop() # type: ignore[assignment] + + return self._help_option + + def make_parser(self, ctx: Context) -> _OptionParser: + """Creates the underlying option parser for this command.""" + parser = _OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + if self.short_help: + text = inspect.cleandoc(self.short_help) + elif self.help: + text = _make_default_short_help(self.help, limit) + else: + text = "" + + if self.deprecated: + text = f"{_(text)} {_format_deprecated_label(self.deprecated)}" + + return text.strip() + + def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help into the formatter if it exists. + + This is a low-level method called by :meth:`get_help`. + + This calls the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_arguments` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_arguments(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + if self.help is not None: + # truncate the help text to the first form feed + text = inspect.cleandoc(self.help).partition("\f")[0] + else: + text = "" + + if self.deprecated: + label = _format_deprecated_label(self.deprecated) + text = f"{_(text)} {label}" if text else label + + if text: + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(text) + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None and not isinstance(param, Argument): + opts.append(rv) + + if opts: + with formatter.section(_("Options")): + formatter.write_dl(opts) + + def format_arguments(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the arguments that have a help record into the formatter.""" + args = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None and isinstance(param, Argument): + args.append(rv) + + if args: + with formatter.section(_("Positional arguments")): + formatter.write_dl(args) + + def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + epilog = inspect.cleandoc(self.epilog) + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(epilog) + + def make_context( + self, + info_name: str | None, + args: list[str], + parent: Context | None = None, + **extra: t.Any, + ) -> Context: + """This function when given an info name and arguments will kick + off the parsing and create a new :class:`Context`. It does not + invoke the actual command callback though. + + To quickly customize the context class used without overriding + this method, set the :attr:`context_class` attribute. + + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it's usually + the name of the script, for commands below it's + the name of the command. + :param args: the arguments to parse as list of strings. + :param parent: the parent context if available. + :param extra: extra keyword arguments forwarded to the context + constructor. + + .. versionchanged:: 8.0 + Added the :attr:`context_class` attribute. + """ + for key, value in self.context_settings.items(): + if key not in extra: + extra[key] = value + + ctx = self.context_class(self, info_name=info_name, parent=parent, **extra) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + raise NoArgsIsHelpError(ctx) + + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing(param_order, self.get_params(ctx)): + _, args = param.handle_parse_result(ctx, opts, args) + + # We now have all parameters' values into `ctx.params`, but the data may contain + # the `UNSET` sentinel. + # Convert `UNSET` to `None` to ensure that the user doesn't see `UNSET`. + # + # Waiting until after the initial parse to convert allows us to treat `UNSET` + # more like a missing value when multiple params use the same name. + # Refs: + # https://github.com/pallets/click/issues/3071 + # https://github.com/pallets/click/pull/3079 + for name, value in ctx.params.items(): + if value is UNSET: + ctx.params[name] = None + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + ctx._opt_prefixes.update(parser._opt_prefixes) + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + message = _( + "DeprecationWarning: The command {name!r} is deprecated.{extra_message}" + ).format( + name=self.name, + extra_message=_format_deprecated_suffix(self.deprecated), + ) + echo(style(message, fg="red"), err=True) + + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: list[CompletionItem] = [] + + if incomplete and not incomplete[0].isalnum(): + for param in self.get_params(ctx): + if ( + not isinstance(param, Option) + or param.hidden + or ( + not param.multiple + and ctx.get_parameter_source(param.name) + is ParameterSource.COMMANDLINE + ) + ): + continue + + results.extend( + CompletionItem(name, help=param.help) + for name in [*param.opts, *param.secondary_opts] + if name.startswith(incomplete) + ) + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, Group) and ctx.command.chain: + results.extend( + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + if name not in ctx._protected_args + ) + + return results + + @t.overload + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: t.Literal[True] = True, + **extra: t.Any, + ) -> t.NoReturn: ... + + @t.overload + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: ... + + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: bool = True, + windows_expand_args: bool = True, + **extra: t.Any, + ) -> t.Any: + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param windows_expand_args: Expand glob patterns, user dir, and + env vars in command line args on Windows. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + + .. versionchanged:: 8.0.1 + Added the ``windows_expand_args`` parameter to allow + disabling command line arg expansion on Windows. + + .. versionchanged:: 8.0 + When taking arguments from ``sys.argv`` on Windows, glob + patterns, user dir, and env vars are expanded. + + .. versionchanged:: 3.0 + Added the ``standalone_mode`` parameter. + """ + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt) as e: + echo(file=sys.stderr) + raise Abort() from e + except ClickException as e: + if not standalone_mode: + raise + e.show() + sys.exit(e.exit_code) + except OSError as e: + if e.errno == errno.EPIPE: + sys.stdout = t.cast(t.TextIO, _PacifyFlushWrapper(sys.stdout)) + sys.stderr = t.cast(t.TextIO, _PacifyFlushWrapper(sys.stderr)) + sys.exit(1) + else: + raise + except Exit as e: + if standalone_mode: + sys.exit(e.exit_code) + else: + # in non-standalone mode, return the exit code + # note that this is only reached if `self.invoke` above raises + # an Exit explicitly -- thus bypassing the check there which + # would return its result + # the results of non-standalone execution may therefore be + # somewhat ambiguous: if there are codepaths which lead to + # `ctx.exit(1)` and to `return 1`, the caller won't be able to + # tell the difference between the two + return e.exit_code + except Abort: + if not standalone_mode: + raise + echo(_("Aborted!"), file=sys.stderr) + sys.exit(1) + + def _main_shell_completion( + self, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str | None = None, + ) -> None: + """Check if the shell is asking for tab completion, process + that, then exit early. Called from :meth:`main` before the + program is invoked. + + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. Defaults to + ``_{PROG_NAME}_COMPLETE``. + + .. versionchanged:: 8.2.0 + Dots (``.``) in ``prog_name`` are replaced with underscores (``_``). + """ + if complete_var is None: + complete_name = prog_name.replace("-", "_").replace(".", "_") + complete_var = f"_{complete_name}_COMPLETE".upper() + + instruction = os.environ.get(complete_var) + + if not instruction: + return + + from .shell_completion import shell_complete + + rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) + sys.exit(rv) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Alias for :meth:`main`.""" + return self.main(*args, **kwargs) + + +class _FakeSubclassCheck(type): + def __subclasscheck__(cls, subclass: type) -> bool: + return issubclass(subclass, cls.__bases__[0]) + + def __instancecheck__(cls, instance: t.Any) -> bool: + return isinstance(instance, cls.__bases__[0]) + + +class _BaseCommand(Command, metaclass=_FakeSubclassCheck): + """ + .. deprecated:: 8.2 + Will be removed in Click 9.0. Use ``Command`` instead. + """ + + +class Group(Command): + """A group is a command that nests other commands (or more groups). + + :param name: The name of the group command. + :param commands: Map names to :class:`Command` objects. Can be a list, which + will use :attr:`Command.name` as the keys. + :param invoke_without_command: Invoke the group's callback even if a + subcommand is not given. + :param no_args_is_help: If no arguments are given, show the group's help and + exit. Defaults to the opposite of ``invoke_without_command``. + :param subcommand_metavar: How to represent the subcommand argument in help. + The default will represent whether ``chain`` is set or not. + :param chain: Allow passing more than one subcommand argument. After parsing + a command's arguments, if any arguments remain another command will be + matched, and so on. + :param result_callback: A function to call after the group's and + subcommand's callbacks. The value returned by the subcommand is passed. + If ``chain`` is enabled, the value will be a list of values returned by + all the commands. If ``invoke_without_command`` is enabled, the value + will be the value returned by the group's callback, or an empty list if + ``chain`` is enabled. + :param kwargs: Other arguments passed to :class:`Command`. + + .. versionchanged:: 8.0 + The ``commands`` argument can be a list of command objects. + + .. versionchanged:: 8.2 + Merged with and replaces the ``MultiCommand`` base class. + """ + + allow_extra_args = True + allow_interspersed_args = False + + #: If set, this is used by the group's :meth:`command` decorator + #: as the default :class:`Command` class. This is useful to make all + #: subcommands use a custom command class. + #: + #: .. versionadded:: 8.0 + command_class: type[Command] | None = None + + #: If set, this is used by the group's :meth:`group` decorator + #: as the default :class:`Group` class. This is useful to make all + #: subgroups use a custom group class. + #: + #: If set to the special value :class:`type` (literally + #: ``group_class = type``), this group's class will be used as the + #: default class. This makes a custom group class continue to make + #: custom groups. + #: + #: .. versionadded:: 8.0 + group_class: type[Group | type] | None = None + # Literal[type] isn't valid, so use Type[type] + + commands: cabc.MutableMapping[str, Command] + invoke_without_command: bool + subcommand_metavar: str + chain: bool + _result_callback: t.Callable[..., t.Any] | None + + def __init__( + self, + name: str | None = None, + commands: cabc.MutableMapping[str, Command] + | cabc.Sequence[Command] + | None = None, + invoke_without_command: bool = False, + no_args_is_help: bool | None = None, + subcommand_metavar: str | None = None, + chain: bool = False, + result_callback: t.Callable[..., t.Any] | None = None, + **kwargs: t.Any, + ) -> None: + super().__init__(name, **kwargs) + + if commands is None: + commands = {} + elif isinstance(commands, abc.Sequence): + commands = {c.name: c for c in commands if c.name is not None} + + #: The registered subcommands by their exported names. + self.commands = commands + + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + + if subcommand_metavar is None: + # When the group can run without a subcommand, the leading command + # token is optional, so wrap it in brackets to reflect that. + if chain: + if invoke_without_command: + subcommand_metavar = "[COMMAND1] [ARGS]... [COMMAND2 [ARGS]...]..." + else: + subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." + elif invoke_without_command: + subcommand_metavar = "[COMMAND] [ARGS]..." + else: + subcommand_metavar = "COMMAND [ARGS]..." + + self.subcommand_metavar = subcommand_metavar + self.chain = chain + # The result callback that is stored. This can be set or + # overridden with the :func:`result_callback` decorator. + self._result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError( + "A group in chain mode cannot have optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + commands = {} + + for name in self.list_commands(ctx): + command = self.get_command(ctx, name) + + if command is None: + continue + + sub_ctx = ctx._make_sub_context(command) + + with sub_ctx.scope(cleanup=False): + commands[name] = command.to_info_dict(sub_ctx) + + info_dict.update(commands=commands, chain=self.chain) + return info_dict + + def add_command(self, cmd: Command, name: str | None = None) -> None: + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError("Command has no name.") + _check_nested_chain(self, name, cmd, register=True) + self.commands[name] = cmd + + @t.overload + def command(self, __func: t.Callable[..., t.Any]) -> Command: ... + + @t.overload + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: ... + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command] | Command: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + func: t.Callable[..., t.Any] | None = None + + if args and callable(args[0]): + assert len(args) == 1 and not kwargs, ( + "Use 'command(**kwargs)(callable)' to provide arguments." + ) + (func,) = args + args = () + + if self.command_class and kwargs.get("cls") is None: + kwargs["cls"] = self.command_class + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd: Command = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + @t.overload + def group(self, __func: t.Callable[..., t.Any]) -> Group: ... + + @t.overload + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Group]: ... + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Group] | Group: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + func: t.Callable[..., t.Any] | None = None + + if args and callable(args[0]): + assert len(args) == 1 and not kwargs, ( + "Use 'group(**kwargs)(callable)' to provide arguments." + ) + (func,) = args + args = () + + if self.group_class is not None and kwargs.get("cls") is None: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> Group: + cmd: Group = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: + """Adds a result callback to the command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.result_callback() + def process_result(result, input): + return result + input + + :param replace: if set to `True` an already existing result + callback will be removed. + + .. versionchanged:: 8.0 + Renamed from ``resultcallback``. + + .. versionadded:: 3.0 + """ + + def decorator(f: F) -> F: + old_callback = self._result_callback + + if old_callback is None or replace: + self._result_callback = f + return f + + def function(value: t.Any, /, *args: t.Any, **kwargs: t.Any) -> t.Any: + inner = old_callback(value, *args, **kwargs) + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv # type: ignore[return-value] + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> Command | None: + """Given a context and a command name, this returns a :class:`Command` + object if it exists or returns ``None``. + """ + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> list[str]: + """Returns a list of subcommand names in the order they should appear.""" + return sorted(self.commands) + + def collect_usage_pieces(self, ctx: Context) -> list[str]: + rv = super().collect_usage_pieces(ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + super().format_options(ctx, formatter) + self.format_commands(ctx, formatter) + + def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section(_("Commands")): + formatter.write_dl(rows) + + def parse_args(self, ctx: Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + raise NoArgsIsHelpError(ctx) + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx._protected_args = rest + ctx.args = [] + elif rest: + ctx._protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx._protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with the group return value for regular + # groups, or an empty list for chained groups. + with ctx: + rv = super().invoke(ctx) + return _process_result([] if self.chain else rv) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx._protected_args, *ctx.args] + ctx.args = [] + ctx._protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: list[str] + ) -> tuple[str | None, Command | None, list[str]]: + cmd_name = make_str(args[0]) + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if _split_opt(cmd_name)[0]: + self.parse_args(ctx, args) + raise NoSuchCommand(cmd_name, possibilities=self.commands, ctx=ctx) + return cmd_name if cmd else None, cmd, args[1:] + + def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: + """Return a list of completions for the incomplete value. Looks + at the names of options, subcommands, and chained + multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results = [ + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + ] + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class _MultiCommand(Group, metaclass=_FakeSubclassCheck): + """ + .. deprecated:: 8.2 + Will be removed in Click 9.0. Use ``Group`` instead. + """ + + +class CommandCollection(Group): + """A :class:`Group` that looks up subcommands on other groups. If a command + is not found on this group, each registered source is checked in order. + Parameters on a source are not added to this group, and a source's callback + is not invoked when invoking its commands. In other words, this "flattens" + commands in many groups into this one group. + + :param name: The name of the group command. + :param sources: A list of :class:`Group` objects to look up commands from. + :param kwargs: Other arguments passed to :class:`Group`. + + .. versionchanged:: 8.2 + This is a subclass of ``Group``. Commands are looked up first on this + group, then each of its sources. + """ + + sources: list[Group] + + def __init__( + self, + name: str | None = None, + sources: list[Group] | None = None, + **kwargs: t.Any, + ) -> None: + super().__init__(name, **kwargs) + #: The list of registered groups. + self.sources = sources or [] + + def add_source(self, group: Group) -> None: + """Add a group as a source of commands.""" + self.sources.append(group) + + def get_command(self, ctx: Context, cmd_name: str) -> Command | None: + rv = super().get_command(ctx, cmd_name) + + if rv is not None: + return rv + + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_nested_chain(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> list[str]: + rv: set[str] = set(super().list_commands(ctx)) + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: cabc.Iterable[V]) -> cabc.Iterator[V]: + """Check if the value is iterable but not a string. Raises a type + error, or return an iterator over the value. + """ + if isinstance(value, str): + raise TypeError + + return iter(value) + + +class Parameter(ABC): + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The latter is converted into the former + automatically if supported. + :param required: controls if this is optional or not. + :param default: the default value if omitted. This can also be a callable, + in which case it's invoked when the default is needed + without any arguments. + :param callback: A function to further process or validate the value + after type conversion. It is called as ``f(ctx, param, value)`` + and must return the value. It is called for all sources, + including prompts. + :param nargs: the number of arguments to match. If not ``1`` the return + value is a tuple instead of single value. The default for + nargs is ``1`` (except if the type is a tuple, then it's + the arity of the tuple). If ``nargs=-1``, all remaining + parameters are collected. + :param metavar: how the value is represented in the help page. + :param expose_value: if this is `True` then the value is passed onwards + to the command callback and stored on the context, + otherwise it's skipped. + :param is_eager: eager values are processed before non eager ones. This + should not be set for arguments or it will inverse the + order of processing. + :param envvar: environment variable(s) that are used to provide a default value for + this parameter. This can be a string or a sequence of strings. If a sequence is + given, only the first non-empty environment variable is used for the parameter. + :param shell_complete: A function that returns custom shell + completions. Used instead of the param's type completion if + given. Takes ``ctx, param, incomplete`` and must return a list + of :class:`~click.shell_completion.CompletionItem` or a list of + strings. + :param deprecated: If ``True`` or non-empty string, issues a message + indicating that the argument is deprecated and highlights + its deprecation in --help. The message can be customized + by using a string as the value. A deprecated parameter + cannot be required, a ValueError will be raised otherwise. + + .. versionchanged:: 8.2.0 + Introduction of ``deprecated``. + + .. versionchanged:: 8.2 + Adding duplicate parameter names to a :class:`~click.core.Command` will + result in a ``UserWarning`` being shown. + + .. versionchanged:: 8.2 + Adding duplicate parameter names to a :class:`~click.core.Command` will + result in a ``UserWarning`` being shown. + + .. versionchanged:: 8.0 + ``process_value`` validates required parameters and bounded + ``nargs``, and invokes the parameter callback before returning + the value. This allows the callback to validate prompts. + ``full_process_value`` is removed. + + .. versionchanged:: 8.0 + ``autocompletion`` is renamed to ``shell_complete`` and has new + semantics described above. The old name is deprecated and will + be removed in 8.1, until then it will be wrapped to match the + new requirements. + + .. versionchanged:: 8.0 + For ``multiple=True, nargs>1``, the default must be a list of + tuples. + + .. versionchanged:: 8.0 + Setting a default is no longer required for ``nargs>1``, it will + default to ``None``. ``multiple=True`` or ``nargs=-1`` will + default to ``()``. + + .. versionchanged:: 7.1 + Empty environment variables are ignored rather than taking the + empty string value. This makes it possible for scripts to clear + variables if they can't unset them. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. The old callback format will still work, but it will + raise a warning to give you a chance to migrate the code easier. + """ + + param_type_name = "parameter" + + name: str + opts: list[str] + secondary_opts: list[str] + # `Parameter.type` is annotated in `__init__` to avoid confusing mypy + required: bool + callback: t.Callable[[Context, Parameter, t.Any], t.Any] | None + nargs: int + multiple: bool + expose_value: bool + default: t.Any | t.Callable[[], t.Any] | None + _default_explicit: bool + is_eager: bool + metavar: str | None + envvar: str | cabc.Sequence[str] | None + _custom_shell_complete: ( + t.Callable[[Context, Parameter, str], list[CompletionItem] | list[str]] | None + ) + deprecated: bool | str + + def __init__( + self, + param_decls: cabc.Sequence[str] | None = None, + type: types.ParamType[t.Any] | t.Any | None = None, + required: bool = False, + # XXX The default historically embed two concepts: + # - the declaration of a Parameter object carrying the default (handy to + # arbitrage the default value of coupled Parameters sharing the same + # self.name, like flag options), + # - and the actual value of the default. + # It is confusing and is the source of many issues discussed in: + # https://github.com/pallets/click/pull/3030 + # In the future, we might think of splitting it in two, not unlike + # Option.is_flag and Option.flag_value: we could have something like + # Parameter.is_default and Parameter.default_value. + default: t.Any | t.Callable[[], t.Any] | None = UNSET, + callback: t.Callable[[Context, Parameter, t.Any], t.Any] | None = None, + nargs: int | None = None, + multiple: bool = False, + metavar: str | None = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: str | cabc.Sequence[str] | None = None, + shell_complete: t.Callable[ + [Context, Parameter, str], list[CompletionItem] | list[str] + ] + | None = None, + deprecated: bool | str = False, + ) -> None: + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type: types.ParamType[t.Any] = types.convert_type(type, default) + + # Default nargs to what the type tells us if we have that + # information available. + if nargs is None: + if self.type.is_composite: + nargs = self.type.arity + else: + nargs = 1 + + self.required = required + self.callback = callback + self.nargs = nargs + self.multiple = multiple + self.expose_value = expose_value + self.default = default + # Whether the user passed ``default`` explicitly to the constructor. + # Captured before any auto-derived default (like ``False`` for boolean + # flags in :class:`Option`) replaces the :data:`UNSET` sentinel, so it + # remains ``False`` when the default was inferred rather than chosen. + # Refs: https://github.com/pallets/click/issues/3403 + self._default_explicit = default is not UNSET + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + self._custom_shell_complete = shell_complete + self.deprecated = deprecated + + if __debug__: + if self.type.is_composite and nargs != self.type.arity: + raise ValueError( + f"'nargs' must be {self.type.arity} (or None) for" + f" type {self.type!r}, but it was {nargs}." + ) + + if required and deprecated: + raise ValueError( + f"The {self.param_type_name} '{self.human_readable_name}' " + "is deprecated and still required. A deprecated " + f"{self.param_type_name} cannot be required." + ) + + @staticmethod + def _hide_unset(value: t.Any) -> t.Any: + """Present the internal :data:`UNSET` sentinel as ``None`` at a boundary that + exposes a parameter's value (introspection, prompts), keeping the sentinel an + implementation detail. + """ + return None if value is UNSET else value + + def to_info_dict(self) -> dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionchanged:: 8.3.0 + Returns ``None`` for the :attr:`default` if it was not set. + + .. versionadded:: 8.0 + """ + return { + "name": self.name, + "param_type_name": self.param_type_name, + "opts": self.opts, + "secondary_opts": self.secondary_opts, + "type": self.type.to_info_dict(), + "required": self.required, + "nargs": self.nargs, + "multiple": self.multiple, + "default": self._hide_unset(self.default), + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + @abstractmethod + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str, list[str], list[str]]: ... + + @property + def human_readable_name(self) -> str: + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name + + def make_metavar(self, ctx: Context) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(param=self, ctx=ctx) + + if metavar is None: + metavar = self.type.name.upper() + + if self.nargs != 1: + metavar += "..." + + return metavar + + @t.overload + def get_default( + self, ctx: Context, call: t.Literal[True] = True + ) -> t.Any | None: ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Any | t.Callable[[], t.Any] | None: ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Any | t.Callable[[], t.Any] | None: + """Get the default for the parameter. Tries + :meth:`Context.lookup_default` first, then the local default. + + :param ctx: Current context. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0.2 + Type casting is no longer performed when getting a default. + + .. versionchanged:: 8.0.1 + Type casting can fail in resilient parsing mode. Invalid + defaults will not prevent showing help text. + + .. versionchanged:: 8.0 + Looks at ``ctx.default_map`` first. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + value = ctx.lookup_default(self.name, call=False) + + if value is None and not ctx._default_map_has(self.name): + value = self.default + + if call and callable(value): + value = value() + + return value + + @abstractmethod + def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: ... + + def consume_value( + self, ctx: Context, opts: cabc.Mapping[str, t.Any] + ) -> tuple[t.Any, ParameterSource]: + """Returns the parameter value produced by the parser. + + If the parser did not produce a value from user input, the value is either + sourced from the environment variable, the default map, or the parameter's + default value. In that order of precedence. + + If no value is found, an internal sentinel value is returned. + + :meta private: + """ + # Collect from the parse the value passed by the user to the CLI. + value = opts.get(self.name, UNSET) + # If the value is set, it means it was sourced from the command line by the + # parser, otherwise it left unset by default. + source = ( + ParameterSource.COMMANDLINE + if value is not UNSET + else ParameterSource.DEFAULT + ) + + if value is UNSET: + envvar_value = self.value_from_envvar(ctx) + if envvar_value is not None: + value = envvar_value + source = ParameterSource.ENVIRONMENT + + if value is UNSET: + default_map_value = ctx.lookup_default(self.name) + if default_map_value is not None or ctx._default_map_has(self.name): + value = default_map_value + source = ParameterSource.DEFAULT_MAP + + # A string from default_map must be split for multi-value + # parameters, matching value_from_envvar behavior. + if isinstance(value, str) and self.nargs != 1: + value = self.type.split_envvar_value(value) + + if value is UNSET: + default_value = self.get_default(ctx) + if default_value is not UNSET: + value = default_value + source = ParameterSource.DEFAULT + + return value, source + + def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: + """Convert and validate a value against the parameter's + :attr:`type`, :attr:`multiple`, and :attr:`nargs`. + """ + if value is None: + if self.multiple or self.nargs == -1: + return () + else: + return value + + def check_iter(value: t.Any) -> cabc.Iterator[t.Any]: + try: + return _check_iter(value) + except TypeError: + # This should only happen when passing in args manually, + # the parser should construct an iterable when parsing + # the command line. + raise BadParameter( + _("Value must be an iterable."), ctx=ctx, param=self + ) from None + + # Define the conversion function based on nargs and type. + + if self.nargs == 1 or self.type.is_composite: + + def convert(value: t.Any) -> t.Any: + return self.type(value, param=self, ctx=ctx) + + elif self.nargs == -1: + + def convert(value: t.Any) -> t.Any: # tuple[t.Any, ...] + return tuple(self.type(x, self, ctx) for x in check_iter(value)) + + else: # nargs > 1 + + def convert(value: t.Any) -> t.Any: # tuple[t.Any, ...] + value = tuple(check_iter(value)) + + if len(value) != self.nargs: + raise BadParameter( + ngettext( + "Takes {nargs} values but 1 was given.", + "Takes {nargs} values but {len} were given.", + len(value), + ).format(nargs=self.nargs, len=len(value)), + ctx=ctx, + param=self, + ) + + return tuple(self.type(x, self, ctx) for x in value) + + if self.multiple: + return tuple(convert(x) for x in check_iter(value)) + + return convert(value) + + def value_is_missing(self, value: t.Any) -> bool: + """A value is considered missing if: + + - it is :attr:`UNSET`, + - or if it is an empty sequence while the parameter is suppose to have + non-single value (i.e. :attr:`nargs` is not ``1`` or :attr:`multiple` is + set). + + :meta private: + """ + if value is UNSET: + return True + + if (self.nargs != 1 or self.multiple) and value == (): + return True + + return False + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + """Process the value of this parameter: + + 1. Type cast the value using :meth:`type_cast_value`. + 2. Check if the value is missing (see: :meth:`value_is_missing`), and raise + :exc:`MissingParameter` if it is required. + 3. If a :attr:`callback` is set, call it to have the value replaced by the + result of the callback. If the value was not set, the callback receive + ``None``. This keep the legacy behavior as it was before the introduction of + the :attr:`UNSET` sentinel. + + :meta private: + """ + # shelter `type_cast_value` from ever seeing an `UNSET` value by handling the + # cases in which `UNSET` gets special treatment explicitly at this layer + # + # Refs: + # https://github.com/pallets/click/issues/3069 + if value is UNSET: + if self.multiple or self.nargs == -1: + value = () + else: + value = self.type_cast_value(ctx, value) + + if self.required and self.value_is_missing(value): + raise MissingParameter(ctx=ctx, param=self) + + if self.callback is not None: + # Legacy case: UNSET is not exposed directly to the callback, but converted + # to None. + if value is UNSET: + value = None + + # Search for parameters with UNSET values in the context. + unset_keys = {k: None for k, v in ctx.params.items() if v is UNSET} + # No UNSET values, call the callback as usual. + if not unset_keys: + value = self.callback(ctx, self, value) + + # Legacy case: provide a temporarily manipulated context to the callback + # to hide UNSET values as None. + # + # Refs: + # https://github.com/pallets/click/issues/3136 + # https://github.com/pallets/click/pull/3137 + else: + # Add another layer to the context stack to clearly hint that the + # context is temporarily modified. + with ctx: + # Update the context parameters to replace UNSET with None. + ctx.params.update(unset_keys) + # Feed these fake context parameters to the callback. + value = self.callback(ctx, self, value) + # Restore the UNSET values in the context parameters. + ctx.params.update( + { + k: UNSET + for k in unset_keys + # Only restore keys that are present and still None, in case + # the callback modified other parameters. + if k in ctx.params and ctx.params[k] is None + } + ) + + return value + + def resolve_envvar_value(self, ctx: Context) -> str | None: + """Returns the value found in the environment variable(s) attached to this + parameter. + + Environment variables values are `always returned as strings + `_. + + This method returns ``None`` if: + + - the :attr:`envvar` property is not set on the :class:`Parameter`, + - the environment variable is not found in the environment, + - the variable is found in the environment but its value is empty (i.e. the + environment variable is present but has an empty string). + + If :attr:`envvar` is setup with multiple environment variables, + then only the first non-empty value is returned. + + .. caution:: + + The raw value extracted from the environment is not normalized and is + returned as-is. Any normalization or reconciliation is performed later by + the :class:`Parameter`'s :attr:`type`. + + :meta private: + """ + if not self.envvar: + return None + + if isinstance(self.envvar, str): + rv = os.environ.get(self.envvar) + + if rv: + return rv + else: + for envvar in self.envvar: + rv = os.environ.get(envvar) + + # Return the first non-empty value of the list of environment variables. + if rv: + return rv + # Else, absence of value is interpreted as an environment variable that + # is not set, so proceed to the next one. + + return None + + def value_from_envvar(self, ctx: Context) -> str | cabc.Sequence[str] | None: + """Process the raw environment variable string for this parameter. + + Returns the string as-is or splits it into a sequence of strings if the + parameter is expecting multiple values (i.e. its :attr:`nargs` property is set + to a value other than ``1``). + + :meta private: + """ + rv = self.resolve_envvar_value(ctx) + + if rv is not None and self.nargs != 1: + return self.type.split_envvar_value(rv) + + return rv + + def handle_parse_result( + self, ctx: Context, opts: cabc.Mapping[str, t.Any], args: list[str] + ) -> tuple[t.Any, list[str]]: + """Process the value produced by the parser from user input. + + Always process the value through the Parameter's :attr:`type`, wherever it + comes from. + + If the parameter is deprecated, this method warn the user about it. But only if + the value has been explicitly set by the user (and as such, is not coming from + a default). + + :meta private: + """ + # Capture the slot's existing state before we mutate + # ``_parameter_source`` so the write decision below can compare our + # incoming source against the source of the option that already wrote + # the slot (if any). + existing_value = ctx.params.get(self.name, UNSET) + existing_source = ctx.get_parameter_source(self.name) + existing_default_explicit = ctx._param_default_explicit.get(self.name, False) + + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + + # Record the source before processing so eager callbacks and type + # conversion can inspect it. Restored after arbitration if this + # option loses a feature-switch group. + ctx.set_parameter_source(self.name, source) + + # Display a deprecation warning if necessary. + if ( + self.deprecated + and value is not UNSET + and source < ParameterSource.DEFAULT_MAP + ): + message = _( + "DeprecationWarning: The {param_type} {name!r} is deprecated." + "{extra_message}" + ).format( + param_type=self.param_type_name, + name=self.human_readable_name, + extra_message=_format_deprecated_suffix(self.deprecated), + ) + echo(style(message, fg="red"), err=True) + + # Process the value through the parameter's type. + try: + value = self.process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + # In resilient parsing mode, we do not want to fail the command if the + # value is incompatible with the parameter type, so we reset the value + # to UNSET, which will be interpreted as a missing value. + value = UNSET + + # Arbitrate the slot when several parameters target the same variable + # name (feature-switch groups). See: https://github.com/pallets/click/issues/3403 + slot_empty = existing_value is UNSET + more_explicit = existing_source is not None and source < existing_source + same_source = existing_source is not None and source == existing_source + auto_would_downgrade_explicit = ( + same_source + and source == ParameterSource.DEFAULT + and existing_default_explicit + and not self._default_explicit + ) + is_winner = ( + slot_empty + or more_explicit + or (same_source and not auto_would_downgrade_explicit) + ) + + if is_winner: + if self.expose_value: + ctx.params[self.name] = value + ctx._param_default_explicit[self.name] = self._default_explicit + elif existing_source is not None: + # Lost arbitration; restore the winning option's source. + ctx.set_parameter_source(self.name, existing_source) + # else: ctx.params[self.name] was populated by code that bypassed + # handle_parse_result (from another option's callback for example). Keep + # the provisional source recorded before process_value so downstream + # lookups don't return ``None``. + + return value, args + + def get_help_record(self, ctx: Context) -> tuple[str, str] | None: + return None + + def get_usage_pieces(self, ctx: Context) -> list[str]: + return [] + + def get_error_hint(self, ctx: Context | None) -> str: + """Get a stringified version of the param for use in error messages to + indicate which param caused the error. + + .. versionchanged:: 8.4.0 + ``ctx`` can be ``None``. + """ + hint_list = self.opts or [self.human_readable_name] + return " / ".join(f"'{x}'" for x in hint_list) + + def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: + """Return a list of completions for the incomplete value. If a + ``shell_complete`` function was given during init, it is used. + Otherwise, the :attr:`type` + :meth:`~click.types.ParamType[t.Any].shell_complete` function is used. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + if self._custom_shell_complete is not None: + results = self._custom_shell_complete(ctx, self, incomplete) + + if results and isinstance(results[0], str): + from click.shell_completion import CompletionItem + + results = [CompletionItem(c) for c in results] + + return t.cast("list[CompletionItem]", results) + + return self.type.shell_complete(ctx, self, incomplete) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: Show the default value for this option in its + help text. Values are not shown by default, unless + :attr:`Context.show_default` is ``True``. If this value is a + string, it shows that string in parentheses instead of the + actual value. This is particularly useful for dynamic options. + For single option boolean flags, the default remains hidden if + its value is ``False``. + :param show_envvar: Controls if an environment variable should be + shown on the help page and error messages. + Normally, environment variables are not shown. + :param prompt: If set to ``True`` or a non empty string then the + user will be prompted for input. If set to ``True`` the prompt + will be the option name capitalized. A deprecated option cannot be + prompted. + :param confirmation_prompt: Prompt a second time to confirm the + value if it was prompted for. Can be set to a string instead of + ``True`` to customize the message. + :param prompt_required: If set to ``False``, the user will be + prompted for input only when the option was specified as a flag + without a value. + :param hide_input: If this is ``True`` then the input on the prompt + will be hidden from the user. This is useful for password input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + :param attrs: Other command arguments described in :class:`Parameter`. + + .. versionchanged:: 8.4.0 + Non-basic ``flag_value`` types (not ``str``, ``int``, ``float``, or + ``bool``) are passed through unchanged instead of being stringified. + Previously, ``type=click.UNPROCESSED`` was required to preserve them. + + .. versionchanged:: 8.2 + ``envvar`` used with ``flag_value`` will always use the ``flag_value``, + previously it would use the value of the environment variable. + + .. versionchanged:: 8.1 + Help text indentation is cleaned here instead of only in the + ``@option`` decorator. + + .. versionchanged:: 8.1 + The ``show_default`` parameter overrides + ``Context.show_default``. + + .. versionchanged:: 8.1 + The default of a single option boolean flag is not shown if the + default value is ``False``. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given, for basic Python + types (``str``, ``int``, ``float``, ``bool``). + """ + + param_type_name = "option" + + prompt: str | None + confirmation_prompt: bool | str + prompt_required: bool + hide_input: bool + hidden: bool + + _flag_needs_value: bool + is_flag: bool + flag_value: t.Any + type: types.ParamType[t.Any] + default: t.Any | t.Callable[[], t.Any] | None + + count: bool + allow_from_autoenv: bool + help: str | None + show_default: bool | str | None + show_choices: bool + show_envvar: bool + + def __init__( + self, + param_decls: cabc.Sequence[str] | None = None, + show_default: bool | str | None = None, + prompt: bool | str = False, + confirmation_prompt: bool | str = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: bool | None = None, + flag_value: t.Any = UNSET, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: types.ParamType[t.Any] | t.Any | None = None, + help: str | None = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + deprecated: bool | str = False, + **attrs: t.Any, + ) -> None: + if help: + help = inspect.cleandoc(help) + + super().__init__( + param_decls, type=type, multiple=multiple, deprecated=deprecated, **attrs + ) + + # Phase 1: prompt-related attributes. ``_infer_flag_kind`` reads ``self.prompt`` + # and ``self.prompt_required`` so this must run first. + if prompt is True: + if not self.name: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = prompt + + if deprecated: + label = _format_deprecated_label(deprecated) + help = f"{help} {label}" if help else label + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # Phase 2: flag-kind inference. + self.is_flag, self._flag_needs_value = self._infer_flag_kind( + is_flag, flag_value + ) + + # Phase 3: type inference. Override the type set by :meth:`Parameter.__init__` + # when this option is a flag or a count. + self.type = self._pick_type(type, flag_value, count, self.is_flag) + + # Phase 4: store the raw ``flag_value`` and ``count`` settings. + # ``self.flag_value`` and ``self.default`` deliberately keep the :data:`UNSET` + # sentinel when the user didn't pass them. Auto-derived values are resolved + # lazily in :meth:`_resolve_lazy_default` and :attr:`flag_activation_value`. + # Keeping the raw values means ``is UNSET`` reliably answers "did the user pass + # this?": #3403 needs it for arbitration, and any future feature needing the + # same distinction can reuse it without reintroducing parallel "was it + # explicit?" tracking. + self.flag_value = flag_value + self.count = count + if count and self.default is UNSET: + self.default = 0 + + self.allow_from_autoenv = allow_from_autoenv + self.help = help + self.show_default = show_default + self.show_choices = show_choices + self.show_envvar = show_envvar + + # Phase 5: validate. Raises on illegal kwarg combinations. + self._validate(prompt, deprecated) + + @property + def is_bool_flag(self) -> bool: + """``True`` when this option is a flag with a boolean type. + + Derived from :attr:`is_flag` and :attr:`type`; computed on access so it cannot + drift if a subclass replaces :attr:`type` after construction. + """ + return self.is_flag and isinstance(self.type, types.BoolParamType) + + @property + def flag_activation_value(self) -> t.Any: + """Value the function receives when this flag is activated on the command line. + + Resolves a missing :attr:`flag_value` to ``True`` for actual flag options and + ``None`` otherwise. Used by the parser bridge (:meth:`add_to_parser`) and the + runtime (:meth:`consume_value`) so :attr:`flag_value` can keep the :data:`UNSET` + sentinel for "did the user pass one?" introspection. + """ + if self.flag_value is UNSET: + return True if self.is_flag else None + return self.flag_value + + def _infer_flag_kind( + self, is_flag: bool | None, flag_value: t.Any + ) -> tuple[bool, bool]: + """Resolve ``is_flag`` and the parser hint ``_flag_needs_value``. + + Returns ``(is_flag, flag_needs_value)``, where ``_flag_needs_value`` tells the + parser this option is a flag that cannot be used standalone and needs a value. + The parser uses it to decide whether to treat the next CLI token as the flag's + value or as a new option. If ``prompt`` is enabled with + ``prompt_required=False``, it opens the door for an interactive value, hence the + initial condition. Ref: https://github.com/pallets/click/issues/3084 + """ + needs_value = self.prompt is not None and not self.prompt_required + + if is_flag is None: + # Implicitly a flag because flag_value was set. + if flag_value is not UNSET: + return True, needs_value + # Not a flag, but when used as a flag it shows a prompt. + if needs_value: + return False, needs_value + # Implicitly a flag because secondary options names were given. + if self.secondary_opts: + return True, needs_value + return False, needs_value + + if is_flag is False and not needs_value: + # Explicit ``is_flag=False`` with a flag-like value/default still makes the + # option flag-shaped to the parser. + needs_value = flag_value is not UNSET or self.default is UNSET + + return bool(is_flag), needs_value + + def _pick_type( + self, + type_arg: t.Any, + flag_value: t.Any, + count: bool, + is_flag: bool, + ) -> types.ParamType[t.Any]: + """Pick the final :class:`ParamType` for this option. + + :meth:`Parameter.__init__` already stored ``self.type`` from the explicit + ``type`` argument or from ``default``. This method either returns that as-is, or + overrides it for flag and count options (which are inferred from ``flag_value`` + rather than ``default``). + """ + if type_arg is not None: + return self.type + + if count: + return types.IntRange(min=0) + + if not is_flag: + return self.type + + # A flag without a flag_value is a boolean flag. + if flag_value is UNSET or isinstance(flag_value, bool): + return types.BoolParamType() + + guessed: types.ParamType[t.Any] = types.convert_type(None, flag_value) + if ( + isinstance(guessed, types.StringParamType) + and not isinstance(flag_value, str) + and flag_value is not None + ): + # The flag_value type couldn't be auto-detected (not str, int, float, or + # bool). Since flag_value is a programmer-provided Python object, not CLI + # input, pass it through unchanged instead of stringifying it. + return types.UNPROCESSED + return guessed + + def _resolve_lazy_default(self, value: t.Any) -> t.Any: + """Apply lazy auto-derivations to a default-style value. + + Shared between :meth:`get_default` (the runtime path) and :meth:`to_info_dict` + (the introspection path) so the two views cannot drift apart. Callables are + *not* invoked here: that is :meth:`get_default`'s responsibility. Rules: + + * ``UNSET`` resolves to ``False`` for a non-required boolean flag, and to ``()`` + for a non-required, non-prompted multi flag. + * ``True`` resolves to :attr:`flag_value` for a non-boolean flag (the "activate + this flag by default" shorthand). Boolean flags keep ``True`` as a literal. + """ + if value is UNSET and self.is_flag: + if self.multiple and not self.required and not self.prompt: + return () + if self.is_bool_flag and not self.required: + return False + if value is True and self.is_flag and not self.is_bool_flag: + # Use ``flag_activation_value`` so an unset ``flag_value`` resolves to + # ``True`` (the bool-flag activation value) rather than leaking the + # :data:`UNSET` sentinel. + return self.flag_activation_value + return value + + def _validate(self, prompt: bool | str, deprecated: bool | str) -> None: + """Raise :class:`TypeError` / :class:`ValueError` on illegal kwarg combinations. + + Called once, after every other attribute has been assigned, so each check can + read the final state. + """ + if not __debug__: + return + if deprecated and prompt: + raise ValueError("`deprecated` options cannot use `prompt`.") + if self.nargs == -1: + raise TypeError("nargs=-1 is not supported for options.") + if not self.is_bool_flag and self.secondary_opts: + raise TypeError("Secondary flag is not valid for non-boolean flag.") + if self.is_bool_flag and self.hide_input and self.prompt is not None: + raise TypeError("'prompt' with 'hide_input' is not valid for boolean flag.") + if self.count: + if self.multiple: + raise TypeError("'count' is not valid with 'multiple'.") + if self.is_flag: + raise TypeError("'count' is not valid with 'is_flag'.") + + def to_info_dict(self) -> dict[str, t.Any]: + """ + .. versionchanged:: 8.5.0 + ``default`` and ``flag_value`` reflect the auto-derived values (``False`` + for unset boolean-flag defaults, ``True`` for unset boolean-flag activation + values, etc.) when no explicit value was passed, matching what the function + would receive at call time. + + .. versionchanged:: 8.3.0 + Returns ``None`` for the :attr:`flag_value` if it was not set. + """ + info_dict = super().to_info_dict() + info_dict.update( + default=self._hide_unset(self._resolve_lazy_default(self.default)), + help=self.help, + prompt=self.prompt, + is_flag=self.is_flag, + flag_value=self.flag_activation_value, + count=self.count, + hidden=self.hidden, + ) + return info_dict + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Any | t.Callable[[], t.Any] | None: + """Return the default value for this option. + + Several auto-derived defaults are resolved lazily here rather than eagerly in + :meth:`__init__`. This keeps :attr:`default` raw at construction time, so + ``self.default is UNSET`` reliably answers "did the user pass a default?" for + feature-switch-group arbitration and for anything else that needs to distinguish + "absent" from a chosen value. The resolution rules live in + :meth:`_resolve_lazy_default`. + + .. versionchanged:: 8.5.0 + ``UNSET`` defaults for boolean and multi flags are now resolved here instead + of being coerced in :meth:`__init__`. Reading :attr:`default` directly + returns the user-supplied value (or ``UNSET`` if none was passed) rather + than the auto-derived one. + + .. versionchanged:: 8.3.3 + ``default=True`` is no longer substituted with ``flag_value`` for boolean + flags, fixing negative boolean flags like + ``flag_value=False, default=True``. + """ + raw = super().get_default(ctx, call=False) + value = self._resolve_lazy_default(raw) + # Only invoke the value as a callable when the lazy resolver passed it through + # unchanged. If the resolver substituted ``True`` -> :attr:`flag_value`, the + # result is the programmer-supplied ``flag_value`` (often a class or enum), + # which must NOT be instantiated here. See + # https://github.com/pallets/click/issues/3121. + if value is raw and call and callable(value): + value = value() + return value + + def get_error_hint(self, ctx: Context | None) -> str: + result = super().get_error_hint(ctx) + if self.show_envvar and self.envvar is not None: + result += f" (env var: '{self.envvar}')" + return result + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str, list[str], list[str]]: + opts = [] + secondary_opts = [] + name = None + possible_names = [] + + for decl in decls: + if decl.isidentifier(): + if name is not None: + raise TypeError(_("Name '{name}' defined twice").format(name=name)) + name = decl + else: + split_char = ";" if decl[:1] == "/" else "/" + if split_char in decl: + first, second = decl.split(split_char, 1) + first = first.rstrip() + if first: + possible_names.append(_split_opt(first)) + opts.append(first) + second = second.lstrip() + if second: + secondary_opts.append(second.lstrip()) + if first == second: + raise ValueError( + _( + "Boolean option {decl!r} cannot use the" + " same flag for true/false." + ).format(decl=decl) + ) + else: + possible_names.append(_split_opt(decl)) + opts.append(decl) + + if name is None and possible_names: + possible_names.sort(key=lambda x: -len(x[0])) # group long options first + name = possible_names[0][1].replace("-", "_").lower() + if not name.isidentifier(): + name = None + + if name is None: + if not expose_value: + return "", opts, secondary_opts + raise TypeError( + _( + "Could not determine name for option with declarations {decls!r}" + ).format(decls=decls) + ) + + if not opts and not secondary_opts: + raise TypeError( + _( + "No options defined but a name was passed ({name})." + " Did you mean to declare an argument instead? Did" + " you mean to pass '--{name}'?" + ).format(name=name) + ) + + return name, opts, secondary_opts + + def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: + if self.multiple: + action = "append" + elif self.count: + action = "count" + else: + action = "store" + + if self.is_flag: + action = f"{action}_const" + + if self.is_bool_flag and self.secondary_opts: + parser.add_option( + obj=self, opts=self.opts, dest=self.name, action=action, const=True + ) + parser.add_option( + obj=self, + opts=self.secondary_opts, + dest=self.name, + action=action, + const=False, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + # ``flag_activation_value`` resolves UNSET to the right parser-store + # constant (``True`` for bool flags) so the raw :attr:`flag_value` + # can keep the sentinel. + const=self.flag_activation_value, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + nargs=self.nargs, + ) + + def get_help_record(self, ctx: Context) -> tuple[str, str] | None: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: cabc.Sequence[str]) -> str: + nonlocal any_prefix_is_slash + + rv, any_slashes = join_options(opts) + + if any_slashes: + any_prefix_is_slash = True + + if not self.is_flag and not self.count: + rv += f" {self.make_metavar(ctx=ctx)}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + + extra = self.get_help_extra(ctx) + extra_items = [] + if "envvars" in extra: + extra_items.append( + _("env var: {var}").format(var=", ".join(extra["envvars"])) + ) + if "default" in extra: + extra_items.append(_("default: {default}").format(default=extra["default"])) + if "range" in extra: + extra_items.append(extra["range"]) + if "required" in extra: + extra_items.append(_(extra["required"])) + + if extra_items: + extra_str = "; ".join(extra_items) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + def get_help_extra(self, ctx: Context) -> types.OptionHelpExtra: + extra: types.OptionHelpExtra = {} + + if self.show_envvar: + envvar = self.envvar + + if envvar is None: + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + + if envvar is not None: + if isinstance(envvar, str): + extra["envvars"] = (envvar,) + else: + extra["envvars"] = tuple(str(d) for d in envvar) + + # Temporarily enable resilient parsing to avoid type casting + # failing for the default. Might be possible to extend this to + # help formatting in general. + resilient = ctx.resilient_parsing + ctx.resilient_parsing = True + + try: + default_value = self.get_default(ctx, call=False) + finally: + ctx.resilient_parsing = resilient + + show_default = False + show_default_is_str = False + + if self.show_default is not None: + if isinstance(self.show_default, str): + show_default_is_str = show_default = True + else: + show_default = self.show_default + elif ctx.show_default is not None: + show_default = ctx.show_default + + if show_default_is_str or ( + show_default and (default_value not in (None, UNSET)) + ): + if show_default_is_str: + default_string = f"({self.show_default})" + elif isinstance(default_value, (list, tuple)): + default_string = ", ".join(str(d) for d in default_value) + elif isinstance(default_value, enum.Enum): + default_string = default_value.name + elif inspect.isfunction(default_value): + default_string = _("(dynamic)") + elif self.is_bool_flag and self.secondary_opts: + # For boolean flags that have distinct True/False opts, + # use the opt without prefix instead of the value. + default_string = _split_opt( + (self.opts if default_value else self.secondary_opts)[0] + )[1] + elif self.is_bool_flag and not self.secondary_opts and not default_value: + default_string = "" + elif isinstance(default_value, str) and default_value == "": + default_string = '""' + else: + default_string = str(default_value) + + if default_string: + extra["default"] = default_string + + if ( + isinstance(self.type, types._NumberRangeBase) + # skip count with default range type + and not (self.count and self.type.min == 0 and self.type.max is None) + ): + range_str = self.type._describe_range() + + if range_str: + extra["range"] = range_str + + if self.required: + extra["required"] = "required" + + return extra + + def prompt_for_value(self, ctx: Context) -> t.Any: + """This is an alternative flow that can be activated in the full + value processing if a value does not exist. It will prompt the + user until a valid value exists and then returns the processed + value as result. + """ + assert self.prompt is not None + + # Calculate the default before prompting anything to lock in the value before + # attempting any user interaction. + default = self.get_default(ctx) + + # A boolean flag can use a simplified [y/n] confirmation prompt. + if self.is_bool_flag: + # If we have no boolean default, we force the user to explicitly provide + # one. + if default in (UNSET, None): + default = None + # Nothing prevent you to declare an option that is simultaneously: + # 1) auto-detected as a boolean flag, + # 2) allowed to prompt, and + # 3) still declare a non-boolean default. + # This forced casting into a boolean is necessary to align any non-boolean + # default to the prompt, which is going to be a [y/n]-style confirmation + # because the option is still a boolean flag. That way, instead of [y/n], + # we get [Y/n] or [y/N] depending on the truthy value of the default. + # Refs: https://github.com/pallets/click/pull/3030#discussion_r2289180249 + else: + default = bool(default) + return confirm(self.prompt, default) + + # If show_default is given, provide this to `prompt` as well, + # otherwise we use `prompt`'s default behavior + prompt_kwargs: t.Any = {} + if self.show_default is not None: + prompt_kwargs["show_default"] = self.show_default + + return prompt( + self.prompt, + # Use ``None`` to inform the prompt() function to reiterate until a valid + # value is provided by the user if we have no default. + default=self._hide_unset(default), + type=self.type, + hide_input=self.hide_input, + show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x), + **prompt_kwargs, + ) + + def resolve_envvar_value(self, ctx: Context) -> str | None: + """:class:`Option` resolves its environment variable the same way as + :func:`Parameter.resolve_envvar_value`, but it also supports + :attr:`Context.auto_envvar_prefix`. If we could not find an environment from + the :attr:`envvar` property, we fallback on :attr:`Context.auto_envvar_prefix` + to build dynamiccaly the environment variable name using the + :python:`{ctx.auto_envvar_prefix}_{self.name.upper()}` template. + + :meta private: + """ + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + return rv + + if self.allow_from_autoenv and ctx.auto_envvar_prefix is not None and self.name: + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Any: + """For :class:`Option`, this method processes the raw environment variable + string the same way as :func:`Parameter.value_from_envvar` does. + + But in the case of non-boolean flags, the value is analyzed to determine if the + flag is activated or not, and returns a boolean of its activation, or the + :attr:`flag_value` if the latter is set. + + This method also takes care of repeated options (i.e. options with + :attr:`multiple` set to ``True``). + + :meta private: + """ + rv = self.resolve_envvar_value(ctx) + + # Absent environment variable or an empty string is interpreted as unset. + if rv is None: + return None + + # Non-boolean flags are more liberal in what they accept. But a flag being a + # flag, its envvar value still needs to be analyzed to determine if the flag is + # activated or not. + if self.is_flag and not self.is_bool_flag: + # An exact match against ``flag_value`` (a non-bool flag always has one + # explicitly set) returns it directly. Otherwise the value is analyzed as a + # boolean: a truthy reading activates the flag and the function receives + # ``flag_value``; a falsy reading produces ``False``; an unrecognized + # reading falls through as ``None``. Folding the substitution here means + # :meth:`consume_value` no longer has to repeat it in a source-checked + # branch. + if rv == self.flag_value: + return self.flag_value + parsed = types.BoolParamType.str_to_bool(rv) + if parsed is None: + return None + return self.flag_value if parsed else False + + # Split the envvar value if it is allowed to be repeated. + value_depth = (self.nargs != 1) + bool(self.multiple) + if value_depth > 0: + multi_rv = self.type.split_envvar_value(rv) + if self.multiple and self.nargs != 1: + multi_rv = batch(multi_rv, self.nargs) # type: ignore[assignment] + + return multi_rv + + return rv + + def consume_value( + self, ctx: Context, opts: cabc.Mapping[str, Parameter] + ) -> tuple[t.Any, ParameterSource]: + """For :class:`Option`, the value can be collected from an interactive prompt + if the option is a flag that needs a value (and the :attr:`prompt` property is + set). + + Additionally, this method handles flag option that are activated without a + value, in which case the :attr:`flag_value` is returned. + + :meta private: + """ + value, source = super().consume_value(ctx, opts) + + # The parser emits a sentinel when a flag is allowed to be used without a value. + # Resolve it to a prompt or to the activation value depending on the option's + # configuration. + if value is FLAG_NEEDS_VALUE: + # If the option allows for a prompt, start an interaction with the user. + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + # Else the flag takes its activation value (resolves UNSET). + else: + value = self.flag_activation_value + source = ParameterSource.COMMANDLINE + + # Re-interpret a multiple option that the parser sent through as a list still + # containing the FLAG_NEEDS_VALUE sentinel, replacing each occurrence with the + # activation value. + elif ( + self.multiple + and value is not UNSET + and isinstance(value, cabc.Iterable) + and source < ParameterSource.DEFAULT_MAP + and any(v is FLAG_NEEDS_VALUE for v in value) + ): + value = [ + self.flag_activation_value if v is FLAG_NEEDS_VALUE else v + for v in value + ] + source = ParameterSource.COMMANDLINE + + # The value wasn't set, or used the param's default, prompt for one to the user + # if prompting is enabled. + elif ( + (value is UNSET or source >= ParameterSource.DEFAULT_MAP) + and self.prompt is not None + and (self.required or self.prompt_required) + and not ctx.resilient_parsing + ): + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + + return value, source + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + # process_value has to be overridden on Options in order to capture + # `value == UNSET` cases before `type_cast_value()` gets called. + # + # Refs: + # https://github.com/pallets/click/issues/3069 + if self.is_flag and not self.required and self.is_bool_flag and value is UNSET: + value = False + + if self.callback is not None: + value = self.callback(ctx, self, value) + + return value + + # in the normal case, rely on Parameter.process_value + return super().process_value(ctx, value) + + +class Argument(Parameter): + """Arguments are positional parameters to a command. They generally + provide fewer features than options but can have infinite ``nargs`` + and are required by default. + + All parameters are passed onwards to the constructor of :class:`Parameter`. + + :param help: the help string. + + .. versionchanged:: 8.5.0 + Added the ``help`` parameter. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: cabc.Sequence[str], + required: bool | None = None, + help: str | None = None, + **attrs: t.Any, + ) -> None: + # Auto-detect the requirement status of the argument if not explicitly set. + if required is None: + # The argument gets automatically required if it has no explicit default + # value set and is setup to match at least one value. + if attrs.get("default", UNSET) is UNSET: + required = attrs.get("nargs", 1) > 0 + # If the argument has a default value, it is not required. + else: + required = False + + if "multiple" in attrs: + raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") + + deprecated = attrs.get("deprecated", False) + + if help: + help = inspect.cleandoc(help) + + if deprecated: + label = _format_deprecated_label(deprecated) + help = f"{help} {label}" if help else label + + self.help = help + + super().__init__(param_decls, required=required, **attrs) + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update(help=self.help) + return info_dict + + @property + def human_readable_name(self) -> str: + if self.metavar is not None: + return self.metavar + return self.name.upper() + + def make_metavar(self, ctx: Context) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(param=self, ctx=ctx) + if not var: + var = self.name.upper() + # Types like ``Choice`` and ``DateTime`` already surround their metavar + # with square brackets to enumerate the allowed values. Reuse those + # outer brackets as the optional-argument indicator instead of wrapping + # the metavar in a second pair, which would produce ``[[a|b|c]]``. + already_bracketed = var.startswith("[") and var.endswith("]") + if self.deprecated: + var += "!" + if not self.required and not already_bracketed: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str, list[str], list[str]]: + if not decls: + if not expose_value: + return "", [], [] + raise TypeError("Argument is marked as exposed, but does not have a name.") + if len(decls) == 1: + name = arg = decls[0] + name = name.replace("-", "_").lower() + else: + raise TypeError( + _( + "Arguments take exactly one parameter declaration, got" + " {length}: {decls}." + ).format(length=len(decls), decls=decls) + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> list[str]: + return [self.make_metavar(ctx)] + + def get_help_record(self, ctx: Context) -> tuple[str, str] | None: + if self.help is None: + return None + + return self.make_metavar(ctx), self.help + + def get_error_hint(self, ctx: Context | None) -> str: + if ctx is not None: + return f"'{self.make_metavar(ctx)}'" + return f"'{self.human_readable_name}'" + + def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) + + +def __getattr__(name: str) -> object: + import warnings + + if name == "BaseCommand": + warnings.warn( + "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Command' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _BaseCommand + + if name == "MultiCommand": + warnings.warn( + "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Group' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _MultiCommand + + raise AttributeError(name) diff --git a/venv/lib/python3.12/site-packages/click/decorators.py b/venv/lib/python3.12/site-packages/click/decorators.py new file mode 100644 index 0000000..cc42e5d --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/decorators.py @@ -0,0 +1,627 @@ +from __future__ import annotations + +import inspect +import typing as t +from functools import update_wrapper +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .globals import get_current_context +from .utils import echo + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") +T = t.TypeVar("T") +_AnyCallable = t.Callable[..., t.Any] +FC = t.TypeVar("FC", bound="_AnyCallable | Command") + + +def pass_context(f: t.Callable[te.Concatenate[Context, P], R]) -> t.Callable[P, R]: + """Marks a callback as wanting to receive the current context + object as first argument. + """ + + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(new_func, f) + + +def pass_obj(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + """Similar to :func:`pass_context`, but only pass the object on the + context onwards (:attr:`Context.obj`). This is useful if that object + represents the state of a nested system. + """ + + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + return f(get_current_context().obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + +def make_pass_decorator( + object_type: type[T], ensure: bool = False +) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]: + """Given an object type this creates a decorator that will work + similar to :func:`pass_obj` but instead of passing the object of the + current context, it will find the innermost context of type + :func:`object_type`. + + This generates a decorator that works roughly like this:: + + from functools import update_wrapper + + def decorator(f): + @pass_context + def new_func(ctx, *args, **kwargs): + obj = ctx.find_object(object_type) + return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator + + :param object_type: the type of the object to pass. + :param ensure: if set to `True`, a new object will be created and + remembered on the context if it's not there yet. + """ + + def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + ctx = get_current_context() + + obj: T | None + if ensure: + obj = ctx.ensure_object(object_type) + else: + obj = ctx.find_object(object_type) + + if obj is None: + raise RuntimeError( + "Managed to invoke callback without a context" + f" object of type {object_type.__name__!r}" + " existing." + ) + + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + return decorator + + +def pass_meta_key( + key: str, *, doc_description: str | None = None +) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]: + """Create a decorator that passes a key from + :attr:`click.Context.meta` as the first argument to the decorated + function. + + :param key: Key in ``Context.meta`` to pass. + :param doc_description: Description of the object being passed, + inserted into the decorator's docstring. Defaults to "the 'key' + key from Context.meta". + + .. versionadded:: 8.0 + """ + + def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + ctx = get_current_context() + obj = ctx.meta[key] + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + if doc_description is None: + doc_description = f"the {key!r} key from :attr:`click.Context.meta`" + + decorator.__doc__ = ( + f"Decorator that passes {doc_description} as the first argument" + " to the decorated function." + ) + return decorator + + +CmdType = t.TypeVar("CmdType", bound=Command) + + +# variant: no call, directly as decorator for a function. +@t.overload +def command(name: _AnyCallable) -> Command: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) +@t.overload +def command( + name: str | None, + cls: type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) +@t.overload +def command( + name: None = None, + *, + cls: type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def command( + name: str | None = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Command]: ... + + +def command( + name: str | _AnyCallable | None = None, + cls: type[CmdType] | None = None, + **attrs: t.Any, +) -> Command | t.Callable[[_AnyCallable], Command | CmdType]: + r"""Creates a new :class:`Command` and uses the decorated function as + callback. This will also automatically attach all decorated + :func:`option`\s and :func:`argument`\s as parameters to the command. + + The name of the command defaults to the name of the function, converted to + lowercase, with underscores ``_`` replaced by dashes ``-``, and the suffixes + ``_command``, ``_cmd``, ``_group``, and ``_grp`` are removed. For example, + ``init_data_command`` becomes ``init-data``. + + All keyword arguments are forwarded to the underlying command class. + For the ``params`` argument, any decorated params are appended to + the end of the list. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: The name of the command. Defaults to modifying the function's + name as described above. + :param cls: The command class to create. Defaults to :class:`Command`. + + .. versionchanged:: 8.2 + The suffixes ``_command``, ``_cmd``, ``_group``, and ``_grp`` are + removed when generating the name. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.1 + The ``params`` argument can be used. Decorated params are + appended to the end of the list. + """ + + func: t.Callable[[_AnyCallable], t.Any] | None = None + + if callable(name): + func = name + name = None + assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." + assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." + + if cls is None: + cls = t.cast("type[CmdType]", Command) + + def decorator(f: _AnyCallable) -> CmdType: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + attr_params = attrs.pop("params", None) + params = attr_params if attr_params is not None else [] + + try: + decorator_params = f.__click_params__ # type: ignore + except AttributeError: + pass + else: + del f.__click_params__ # type: ignore + params.extend(reversed(decorator_params)) + + if attrs.get("help") is None: + attrs["help"] = f.__doc__ + + if t.TYPE_CHECKING: + assert cls is not None + assert not callable(name) + + if name is not None: + cmd_name = name + else: + cmd_name = f.__name__.lower().replace("_", "-") + cmd_left, sep, suffix = cmd_name.rpartition("-") + + if sep and suffix in {"command", "cmd", "group", "grp"}: + cmd_name = cmd_left + + cmd = cls(name=cmd_name, callback=f, params=params, **attrs) + cmd.__doc__ = f.__doc__ + return cmd + + if func is not None: + return decorator(func) + + return decorator + + +GrpType = t.TypeVar("GrpType", bound=Group) + + +# variant: no call, directly as decorator for a function. +@t.overload +def group(name: _AnyCallable) -> Group: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) +@t.overload +def group( + name: str | None, + cls: type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) +@t.overload +def group( + name: None = None, + *, + cls: type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def group( + name: str | None = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Group]: ... + + +def group( + name: str | _AnyCallable | None = None, + cls: type[GrpType] | None = None, + **attrs: t.Any, +) -> Group | t.Callable[[_AnyCallable], Group | GrpType]: + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + """ + if cls is None: + cls = t.cast("type[GrpType]", Group) + + if callable(name): + return command(cls=cls, **attrs)(name) + + return command(name, cls, **attrs) + + +def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None: + if isinstance(f, Command): + f.params.append(param) + else: + if not hasattr(f, "__click_params__"): + f.__click_params__ = [] # type: ignore + + f.__click_params__.append(param) # type: ignore + + +def argument( + *param_decls: str, cls: type[Argument] | None = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an argument to the command. All positional arguments are + passed as parameter declarations to :class:`Argument`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Argument` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default argument class, refer to :class:`Argument` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Argument + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def option( + *param_decls: str, cls: type[Option] | None = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an option to the command. All positional arguments are + passed as parameter declarations to :class:`Option`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Option` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default option class, refer to :class:`Option` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Option + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--yes`` option which shows a prompt before continuing if + not passed. If the prompt is declined, the program will exit. + + :param param_decls: One or more option names. Defaults to the single + value ``"--yes"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value: + ctx.abort() + + if not param_decls: + param_decls = ("--yes",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("callback", callback) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("prompt", _("Do you want to continue?")) + kwargs.setdefault("help", _("Confirm the action without prompting.")) + return option(*param_decls, **kwargs) + + +def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--password`` option which prompts for a password, hiding + input and asking to enter the value again for confirmation. + + :param param_decls: One or more option names. Defaults to the single + value ``"--password"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + if not param_decls: + param_decls = ("--password",) + + kwargs.setdefault("prompt", True) + kwargs.setdefault("confirmation_prompt", True) + kwargs.setdefault("hide_input", True) + return option(*param_decls, **kwargs) + + +def version_option( + version: str | None = None, + *param_decls: str, + package_name: str | None = None, + prog_name: str | None = None, + message: str | None = None, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option which immediately prints the version + number and exits the program. + + If ``version`` is not provided, Click will try to detect it using + :func:`importlib.metadata.version` to get the version for the + ``package_name``. + + If ``package_name`` is not provided, Click will try to detect it by + inspecting the stack frames. If the detected (or given) name does + not match an installed distribution, Click resolves it as an import + (top-level module) name via + :func:`importlib.metadata.packages_distributions`, so e.g. ``PIL`` + resolves to the ``Pillow`` distribution. + + .. note:: + The parameters and message variables accepted by this option are + frozen: no new slots will be added, to keep the common case simple + and predictable. If you need values it does not expose, such as a + file path, the Python version, or git metadata, use + :func:`custom_version_option` to render the output yourself. + + Rationale: `discussion #3527 + `_. + + :param version: The version number to show. If not provided, Click + will try to detect it. + :param param_decls: One or more option names. Defaults to the single + value ``"--version"``. + :param package_name: The package name to detect the version from. If + not provided, Click will try to detect it. + :param prog_name: The name of the CLI to show in the message. If not + provided, it will be detected from the command. + :param message: The message to show. The values ``%(prog)s``, + ``%(package)s``, and ``%(version)s`` are available. Defaults to + ``"%(prog)s, version %(version)s"``. + :param kwargs: Extra arguments are passed to :func:`option`. + :raise RuntimeError: ``version`` could not be detected. + + .. versionchanged:: 8.0 + Add the ``package_name`` parameter, and the ``%(package)s`` + value for messages. + + .. versionchanged:: 8.0 + Use :mod:`importlib.metadata` instead of ``pkg_resources``. The + version is detected based on the package name, not the entry + point name. The Python package name must match the installed + package name, or be passed with ``package_name=``. + + .. versionchanged:: 8.4.2 + When ``package_name`` does not match an installed distribution, + Click now resolves it as an import (top-level module). + """ + if message is None: + message = _("%(prog)s, version %(version)s") + + if version is None and package_name is None: + frame = inspect.currentframe() + f_back = frame.f_back if frame is not None else None + f_globals = f_back.f_globals if f_back is not None else None + # break reference cycle + # https://docs.python.org/3/library/inspect.html#the-interpreter-stack + del frame + + if f_globals is not None: + package_name = f_globals.get("__name__") + + if package_name == "__main__": + package_name = f_globals.get("__package__") + + if package_name: + package_name = package_name.partition(".")[0] + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + nonlocal prog_name + nonlocal version + nonlocal package_name + + if prog_name is None: + prog_name = ctx.find_root().info_name + + if version is None and package_name is not None: + import importlib.metadata + + try: + version = importlib.metadata.version(package_name) + except importlib.metadata.PackageNotFoundError: + # The given name didn't match an installed distribution. + # Try resolving it as an import (top-level module) name, + # e.g. ``PIL`` is provided by the ``Pillow`` distribution. + distributions = importlib.metadata.packages_distributions().get( + package_name, [] + ) + if len(distributions) == 1: + package_name = distributions[0] + version = importlib.metadata.version(package_name) + elif len(distributions) > 1: + raise RuntimeError( + f"{package_name!r} maps to multiple installed" + f" distributions ({', '.join(distributions)})." + " Pass 'package_name' to disambiguate." + ) from None + else: + raise RuntimeError( + f"{package_name!r} is not installed. Try passing" + " 'package_name' instead." + ) from None + + if version is None: + raise RuntimeError( + f"Could not determine the version for {package_name!r} automatically." + ) + + echo( + message % {"prog": prog_name, "package": package_name, "version": version}, + color=ctx.color, + ) + ctx.exit() + + if not param_decls: + param_decls = ("--version",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show the version and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) + + +def custom_version_option( + callback: t.Callable[[Context], str], + *param_decls: str, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option whose output is produced by ``callback``. + + This is the customizable companion to :func:`version_option`. Where + :func:`version_option` is intentionally limited to a fixed message and + a small set of values, this option calls ``callback`` to build the + whole string to print. Use it when you need values that + :func:`version_option` does not expose, such as a file path, the + Python version, or git metadata. + + :param callback: Called with the current :class:`Context` when the + option is invoked. Its return value is printed, then the program + exits. + :param param_decls: One or more option names. Defaults to the single + value ``--version``. + :param kwargs: Extra arguments are passed to :func:`option`. + + .. versionadded:: 8.5.0 + """ + + def show_version(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + echo(callback(ctx), color=ctx.color) + ctx.exit() + + if not param_decls: + param_decls = ("--version",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show the version and exit.")) + kwargs["callback"] = show_version + return option(*param_decls, **kwargs) + + +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Pre-configured ``--help`` option which immediately prints the help page + and exits the program. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def show_help(ctx: Context, param: Parameter, value: bool) -> None: + """Callback that print the help page on ```` and exits.""" + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + if not param_decls: + param_decls = ("--help",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs.setdefault("callback", show_help) + + return option(*param_decls, **kwargs) diff --git a/venv/lib/python3.12/site-packages/click/exceptions.py b/venv/lib/python3.12/site-packages/click/exceptions.py new file mode 100644 index 0000000..6272c38 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/exceptions.py @@ -0,0 +1,378 @@ +from __future__ import annotations + +import collections.abc as cabc +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +from .globals import resolve_color_default +from .utils import echo +from .utils import format_filename + +if t.TYPE_CHECKING: + from .core import Command + from .core import Context + from .core import Parameter + + +def _join_param_hints(param_hint: cabc.Sequence[str] | str | None) -> str | None: + if param_hint is not None and not isinstance(param_hint, str): + return " / ".join(repr(x) for x in param_hint) + + return param_hint + + +def _format_possibilities(possibilities: list[str]) -> str: + possibility_str = ", ".join(repr(p) for p in sorted(possibilities)) + return ngettext( + "Did you mean {possibility}?", + "(Did you mean one of: {possibilities}?)", + len(possibilities), + ).format(possibility=possibility_str, possibilities=possibility_str) + + +class ClickException(Exception): + """An exception that Click can handle and show to the user.""" + + #: The exit code for this exception. + exit_code: t.ClassVar[int] = 1 + + show_color: t.Final[bool | None] + message: t.Final[str] + + def __init__(self, message: str) -> None: + super().__init__(message) + # The context will be removed by the time we print the message, so cache + # the color settings here to be used later on (in `show`) + self.show_color = resolve_color_default() + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.IO[t.Any] | None = None) -> None: + if file is None: + file = get_text_stderr() + + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=self.show_color, + ) + + +class UsageError(ClickException): + """An internal exception that signals a usage error. This typically + aborts any further handling. + + :param message: the error message to display. + :param ctx: optionally the context that caused this error. Click will + fill in the context automatically in some situations. + """ + + exit_code: t.ClassVar[int] = 2 + + ctx: Context | None + cmd: t.Final[Command | None] + + def __init__(self, message: str, ctx: Context | None = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd = self.ctx.command if self.ctx else None + + def show(self, file: t.IO[t.Any] | None = None) -> None: + if file is None: + file = get_text_stderr() + color = None + hint = "" + if ( + self.ctx is not None + and self.ctx.command.get_help_option(self.ctx) is not None + ): + help_names = self.ctx.command.get_help_option_names(self.ctx) + # Pick the longest name (like ``--help`` over ``-h``) for + # readability in error messages. + hint = _("Try '{command} {option}' for help.").format( + command=self.ctx.command_path, + option=max(help_names, key=len), + ) + hint = f"{hint}\n" + if self.ctx is not None: + color = self.ctx.color + echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=color, + ) + + +class BadParameter(UsageError): + """An exception that formats out a standardized error message for a + bad parameter. This is useful when thrown from a callback or type as + Click will attach contextual information to it (for instance, which + parameter it is). + + .. versionadded:: 2.0 + + :param param: the parameter object that caused this error. This can + be left out, and Click will attach this info itself + if possible. + :param param_hint: a string that shows up as parameter name. This + can be used as alternative to `param` in cases + where custom validation should happen. If it is + a string it's used as such, if it's a list then + each item is quoted and separated. + """ + + param: Parameter | None + param_hint: cabc.Sequence[str] | str | None + + def __init__( + self, + message: str, + ctx: Context | None = None, + param: Parameter | None = None, + param_hint: cabc.Sequence[str] | str | None = None, + ) -> None: + super().__init__(message, ctx) + self.param = param + self.param_hint = param_hint + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) + else: + return _("Invalid value: {message}").format(message=self.message) + + return _("Invalid value for {param_hint}: {message}").format( + param_hint=_join_param_hints(param_hint), message=self.message + ) + + +class MissingParameter(BadParameter): + """Raised if click required an option or argument but it was not + provided when invoking the script. + + .. versionadded:: 4.0 + + :param param_type: a string that indicates the type of the parameter. + The default is to inherit the parameter type from + the given `param`. Valid values are ``'parameter'``, + ``'option'`` or ``'argument'``. + """ + + param_type: t.Final[str | None] + + def __init__( + self, + message: str | None = None, + ctx: Context | None = None, + param: Parameter | None = None, + param_hint: cabc.Sequence[str] | str | None = None, + param_type: str | None = None, + ) -> None: + super().__init__(message or "", ctx, param, param_hint) + self.param_type = param_type + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint: cabc.Sequence[str] | str | None = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) + else: + param_hint = None + + param_hint = _join_param_hints(param_hint) + param_hint = f" {param_hint}" if param_hint else "" + + param_type = self.param_type + if param_type is None and self.param is not None: + param_type = self.param.param_type_name + + msg = self.message + if self.param is not None: + msg_extra = self.param.type.get_missing_message( + param=self.param, ctx=self.ctx + ) + if msg_extra: + if msg: + msg += f". {msg_extra}" + else: + msg = msg_extra + + msg = f" {msg}" if msg else "" + + # Translate param_type for known types. + if param_type == "argument": + missing = _("Missing argument") + elif param_type == "option": + missing = _("Missing option") + elif param_type == "parameter": + missing = _("Missing parameter") + else: + missing = _("Missing {param_type}").format(param_type=param_type) + + return f"{missing}{param_hint}.{msg}" + + def __str__(self) -> str: + if not self.message: + param_name = self.param.name if self.param else None + return _("Missing parameter: {param_name}").format(param_name=param_name) + else: + return self.message + + +class NoSuchOption(UsageError): + """Raised if Click attempted to handle an option that does not exist. + + .. versionadded:: 4.0 + """ + + option_name: t.Final[str] + possibilities: t.Final[list[str] | None] + + def __init__( + self, + option_name: str, + message: str | None = None, + possibilities: cabc.Iterable[str] | None = None, + ctx: Context | None = None, + ) -> None: + if message is None: + message = _("No such option {name!r}.").format(name=option_name) + + super().__init__(message, ctx) + self.option_name = option_name + + if possibilities: + from difflib import get_close_matches + + possibilities_ = get_close_matches(option_name, possibilities) + else: + possibilities_ = None + self.possibilities = possibilities_ + + def format_message(self) -> str: + if not self.possibilities: + return self.message + return f"{self.message} {_format_possibilities(self.possibilities)}" + + +class NoSuchCommand(UsageError): + """Raised if Click attempted to handle a command that does not exist. + + .. versionadded:: 8.4.0 + """ + + command_name: t.Final[str] + possibilities: t.Final[list[str] | None] + + def __init__( + self, + command_name: str, + message: str | None = None, + possibilities: cabc.Iterable[str] | None = None, + ctx: Context | None = None, + ) -> None: + if message is None: + message = _("No such command {name!r}.").format(name=command_name) + + super().__init__(message, ctx) + self.command_name = command_name + + if possibilities: + from difflib import get_close_matches + + possibilities_ = get_close_matches(command_name, possibilities) + else: + possibilities_ = None + self.possibilities = possibilities_ + + def format_message(self) -> str: + if not self.possibilities: + return self.message + return f"{self.message} {_format_possibilities(self.possibilities)}" + + +class BadOptionUsage(UsageError): + """Raised if an option is generally supplied but the use of the option + was incorrect. This is for instance raised if the number of arguments + for an option is not correct. + + .. versionadded:: 4.0 + + :param option_name: the name of the option being used incorrectly. + """ + + option_name: t.Final[str] + + def __init__( + self, option_name: str, message: str, ctx: Context | None = None + ) -> None: + super().__init__(message, ctx) + self.option_name = option_name + + +class BadArgumentUsage(UsageError): + """Raised if an argument is generally supplied but the use of the argument + was incorrect. This is for instance raised if the number of values + for an argument is not correct. + + .. versionadded:: 6.0 + """ + + +class NoArgsIsHelpError(UsageError): + ctx: Context + + def __init__(self, ctx: Context) -> None: + super().__init__(ctx.get_help(), ctx=ctx) + + def show(self, file: t.IO[t.Any] | None = None) -> None: + echo(self.format_message(), file=file, err=True, color=self.ctx.color) + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + ui_filename: t.Final[str] + filename: t.Final[str] + + def __init__(self, filename: str, hint: str | None = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename = format_filename(filename) + self.filename = filename + + def format_message(self) -> str: + return _("Could not open file {filename!r}: {message}").format( + filename=self.ui_filename, message=self.message + ) + + +class Abort(RuntimeError): + """An internal signalling exception that signals Click to abort.""" + + +class Exit(RuntimeError): + """An exception that indicates that the application should exit with some + status code. + + :param code: the status code to exit with. + """ + + __slots__ = ("exit_code",) + + exit_code: t.Final[int] + + def __init__(self, code: int = 0) -> None: + self.exit_code = code diff --git a/venv/lib/python3.12/site-packages/click/formatting.py b/venv/lib/python3.12/site-packages/click/formatting.py new file mode 100644 index 0000000..c4aa2de --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/formatting.py @@ -0,0 +1,320 @@ +from __future__ import annotations + +import collections.abc as cabc +from contextlib import contextmanager +from gettext import gettext as _ + +from ._compat import term_len +from .parser import _split_opt + +# Can force a width. This is used by the test system +FORCED_WIDTH: int | None = None + + +def measure_table(rows: cabc.Iterable[tuple[str, str]]) -> tuple[int, ...]: + widths: dict[int, int] = {} + + for row in rows: + for idx, col in enumerate(row): + widths[idx] = max(widths.get(idx, 0), term_len(col)) + + return tuple(y for x, y in sorted(widths.items())) + + +def iter_rows( + rows: cabc.Iterable[tuple[str, str]], col_count: int +) -> cabc.Iterator[tuple[str, ...]]: + for row in rows: + yield row + ("",) * (col_count - len(row)) + + +def wrap_text( + text: str, + width: int = 78, + initial_indent: str = "", + subsequent_indent: str = "", + preserve_paragraphs: bool = False, +) -> str: + """A helper function that intelligently wraps text. By default, it + assumes that it operates on a single paragraph of text but if the + `preserve_paragraphs` parameter is provided it will intelligently + handle paragraphs (defined by two empty lines). + + If paragraphs are handled, a paragraph can be prefixed with an empty + line containing the ``\\b`` character (``\\x08``) to indicate that + no rewrapping should happen in that block. + + :param text: the text that should be rewrapped. + :param width: the maximum width for the text. + :param initial_indent: the initial indent that should be placed on the + first line as a string. + :param subsequent_indent: the indent string that should be placed on + each consecutive line. + :param preserve_paragraphs: if this flag is set then the wrapping will + intelligently handle paragraphs. + + .. versionchanged:: 8.4.0 + Width is measured in visible characters. ANSI escape sequences in + ``text``, ``initial_indent``, or ``subsequent_indent`` no longer + count toward the width budget, so styled input wraps based on what + the user sees instead of raw byte length. + """ + from ._textwrap import TextWrapper + + text = text.expandtabs() + wrapper = TextWrapper( + width, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False, + ) + if not preserve_paragraphs: + return wrapper.fill(text) + + p: list[tuple[int, bool, str]] = [] + buf: list[str] = [] + indent = None + + def _flush_par() -> None: + if not buf: + return + if buf[0].strip() == "\b": + p.append((indent or 0, True, "\n".join(buf[1:]))) + else: + p.append((indent or 0, False, " ".join(buf))) + del buf[:] + + for line in text.splitlines(): + if not line: + _flush_par() + indent = None + else: + if indent is None: + orig_len = term_len(line) + line = line.lstrip() + indent = orig_len - term_len(line) + buf.append(line) + _flush_par() + + rv = [] + for indent, raw, text in p: + with wrapper.extra_indent(" " * indent): + if raw: + rv.append(wrapper.indent_only(text)) + else: + rv.append(wrapper.fill(text)) + + return "\n\n".join(rv) + + +class HelpFormatter: + """This class helps with formatting text-based help pages. It's + usually just needed for very special internal cases, but it's also + exposed so that developers can write their own fancy outputs. + + At present, it always writes into memory. + + :param indent_increment: the additional increment for each level. + :param width: the width for the text. This defaults to the terminal + width clamped to a maximum of 78. + """ + + indent_increment: int + width: int + current_indent: int + buffer: list[str] + + def __init__( + self, + indent_increment: int = 2, + width: int | None = None, + max_width: int | None = None, + ) -> None: + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + import shutil + + width = FORCED_WIDTH + if width is None: + width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) + self.width = width + self.current_indent = 0 + self.buffer = [] + + def write(self, string: str) -> None: + """Writes a unicode string into the internal buffer.""" + self.buffer.append(string) + + def indent(self) -> None: + """Increases the indentation.""" + self.current_indent += self.indent_increment + + def dedent(self) -> None: + """Decreases the indentation.""" + self.current_indent -= self.indent_increment + + def write_usage(self, prog: str, args: str = "", prefix: str | None = None) -> None: + """Writes a usage line into the buffer. + + :param prog: the program name. + :param args: whitespace separated list of arguments. + :param prefix: The prefix for the first line. Defaults to + ``"Usage: "``. + """ + if prefix is None: + prefix = "{usage} ".format(usage=_("Usage:")) + + usage_prefix = f"{prefix:>{self.current_indent}}{prog} " + text_width = self.width - self.current_indent + + if not args: + # Without args, the prefix's trailing space and the wrap_text + # call that would normally place args on the line are both + # unnecessary. Emit just the prefix line. + self.write(usage_prefix.rstrip(" ")) + self.write("\n") + return + + if text_width >= (term_len(usage_prefix) + 20): + # The arguments will fit to the right of the prefix. + indent = " " * term_len(usage_prefix) + self.write( + wrap_text( + args, + text_width, + initial_indent=usage_prefix, + subsequent_indent=indent, + ) + ) + else: + # The prefix is too long, put the arguments on the next line. + self.write(usage_prefix) + self.write("\n") + indent = " " * (max(self.current_indent, term_len(prefix)) + 4) + self.write( + wrap_text( + args, text_width, initial_indent=indent, subsequent_indent=indent + ) + ) + + self.write("\n") + + def write_heading(self, heading: str) -> None: + """Writes a heading into the buffer.""" + self.write(f"{'':>{self.current_indent}}{heading}:\n") + + def write_paragraph(self) -> None: + """Writes a paragraph into the buffer.""" + if self.buffer: + self.write("\n") + + def write_text(self, text: str) -> None: + """Writes re-indented text into the buffer. This rewraps and + preserves paragraphs. + """ + indent = " " * self.current_indent + self.write( + wrap_text( + text, + self.width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True, + ) + ) + self.write("\n") + + def write_dl( + self, + rows: cabc.Iterable[tuple[str, str]], + col_max: int = 30, + col_spacing: int = 2, + ) -> None: + """Writes a definition list into the buffer. This is how options + and commands are usually formatted. + + :param rows: a list of two item tuples for the terms and values. + :param col_max: the maximum width of the first column. + :param col_spacing: the number of spaces between the first and + second column. + """ + rows = list(rows) + widths = measure_table(rows) + if len(widths) != 2: + raise TypeError("Expected two columns for definition list") + + first_col = min(widths[0], col_max) + col_spacing + + for first, second in iter_rows(rows, len(widths)): + self.write(f"{'':>{self.current_indent}}{first}") + if not second: + self.write("\n") + continue + if term_len(first) <= first_col - col_spacing: + self.write(" " * (first_col - term_len(first))) + else: + self.write("\n") + self.write(" " * (first_col + self.current_indent)) + + text_width = max(self.width - first_col - 2, 10) + wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) + lines = wrapped_text.splitlines() + + if lines: + self.write(f"{lines[0]}\n") + + for line in lines[1:]: + self.write(f"{'':>{first_col + self.current_indent}}{line}\n") + else: + self.write("\n") + + @contextmanager + def section(self, name: str) -> cabc.Generator[None]: + """Helpful context manager that writes a paragraph, a heading, + and the indents. + + :param name: the section name that is written as heading. + """ + self.write_paragraph() + self.write_heading(name) + self.indent() + try: + yield + finally: + self.dedent() + + @contextmanager + def indentation(self) -> cabc.Generator[None]: + """A context manager that increases the indentation.""" + self.indent() + try: + yield + finally: + self.dedent() + + def getvalue(self) -> str: + """Returns the buffer contents.""" + return "".join(self.buffer) + + +def join_options(options: cabc.Iterable[str]) -> tuple[str, bool]: + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + + for opt in options: + prefix = _split_opt(opt)[0] + + if prefix == "/": + any_prefix_is_slash = True + + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/venv/lib/python3.12/site-packages/click/globals.py b/venv/lib/python3.12/site-packages/click/globals.py new file mode 100644 index 0000000..a2f9172 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/globals.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +import typing as t +from threading import local + +if t.TYPE_CHECKING: + from .core import Context + +_local = local() + + +@t.overload +def get_current_context(silent: t.Literal[False] = False) -> Context: ... + + +@t.overload +def get_current_context(silent: bool = ...) -> Context | None: ... + + +def get_current_context(silent: bool = False) -> Context | None: + """Returns the current click context. This can be used as a way to + access the current context object from anywhere. This is a more implicit + alternative to the :func:`pass_context` decorator. This function is + primarily useful for helpers such as :func:`echo` which might be + interested in changing its behavior based on the current context. + + To push the current context, :meth:`Context.scope` can be used. + + .. versionadded:: 5.0 + + :param silent: if set to `True` the return value is `None` if no context + is available. The default behavior is to raise a + :exc:`RuntimeError`. + """ + try: + return t.cast("Context", _local.stack[-1]) + except (AttributeError, IndexError) as e: + if not silent: + raise RuntimeError("There is no active click context.") from e + + return None + + +def push_context(ctx: Context) -> None: + """Pushes a new context to the current stack.""" + _local.__dict__.setdefault("stack", []).append(ctx) + + +def pop_context() -> None: + """Removes the top level from the stack.""" + _local.stack.pop() + + +def resolve_color_default(color: bool | None = None) -> bool | None: + """Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + + ctx = get_current_context(silent=True) + + if ctx is not None: + return ctx.color + + return None diff --git a/venv/lib/python3.12/site-packages/click/parser.py b/venv/lib/python3.12/site-packages/click/parser.py new file mode 100644 index 0000000..4fcbf7c --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/parser.py @@ -0,0 +1,533 @@ +""" +This module started out as largely a copy paste from the stdlib's +optparse module with the features removed that we do not need from +optparse because we implement them in Click on a higher level (for +instance type handling, help formatting and a lot more). + +The plan is to remove more and more from here over time. + +The reason this is a different module and not optparse from the stdlib +is that there are differences in 2.x and 3.x about the error messages +generated and optparse in the stdlib uses gettext for no good reason +and might cause us issues. + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright 2001-2006 Gregory P. Ward. All rights reserved. +Copyright 2002-2006 Python Software Foundation. All rights reserved. +""" + +# This code uses parts of optparse written by Gregory P. Ward and +# maintained by the Python Software Foundation. +# Copyright 2001-2006 Gregory P. Ward +# Copyright 2002-2006 Python Software Foundation +from __future__ import annotations + +import collections.abc as cabc +import typing as t +from collections import deque +from gettext import gettext as _ +from gettext import ngettext + +from ._utils import FLAG_NEEDS_VALUE +from ._utils import UNSET +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError + +if t.TYPE_CHECKING: + from ._utils import T_FLAG_NEEDS_VALUE + from ._utils import T_UNSET + from .core import Argument as CoreArgument + from .core import Context + from .core import Option as CoreOption + from .core import Parameter as CoreParameter + +V = t.TypeVar("V") + + +def _unpack_args( + args: cabc.Sequence[str], nargs_spec: cabc.Sequence[int] +) -> tuple[cabc.Sequence[str | cabc.Sequence[str | T_UNSET] | T_UNSET], list[str]]: + """Given an iterable of arguments and an iterable of nargs specifications, + it returns a tuple with all the unpacked arguments at the first index + and all remaining arguments as the second. + + The nargs specification is the number of arguments that should be consumed + or `-1` to indicate that this position should eat up all the remainders. + + Missing items are filled with ``UNSET``. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv: list[str | tuple[str | T_UNSET, ...] | T_UNSET] = [] + spos: int | None = None + + def _fetch(c: deque[str]) -> str | T_UNSET: + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return UNSET + + while nargs_spec: + if spos is None: + nargs = nargs_spec.popleft() + else: + nargs = nargs_spec.pop() + + if nargs == 1: + rv.append(_fetch(args)) + elif nargs > 1: + x: list[str | T_UNSET] = [_fetch(args) for _ in range(nargs)] + + # If we're reversed, we're pulling in the arguments in reverse, + # so we need to turn them around. + if spos is not None: + x.reverse() + + rv.append(tuple(x)) + elif nargs < 0: + if spos is not None: + raise TypeError("Cannot have two nargs < 0") + + spos = len(rv) + rv.append(UNSET) + + # spos is the position of the wildcard (star). If it's not `None`, + # we fill it with the remainder. + if spos is not None: + rv[spos] = tuple(args) + args = [] + rv[spos + 1 :] = reversed(rv[spos + 1 :]) + + return tuple(rv), list(args) + + +def _split_opt(opt: str) -> tuple[str, str]: + first = opt[:1] + if first.isalnum(): + return "", opt + if opt[1:2] == first: + return opt[:2], opt[2:] + return first, opt[1:] + + +def _normalize_opt(opt: str, ctx: Context | None) -> str: + if ctx is None or ctx.token_normalize_func is None: + return opt + prefix, opt = _split_opt(opt) + return f"{prefix}{ctx.token_normalize_func(opt)}" + + +class _Option: + def __init__( + self, + obj: CoreOption, + opts: cabc.Sequence[str], + dest: str | None, + action: str | None = None, + nargs: int = 1, + const: t.Any | None = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes: set[str] = set() + + for opt in opts: + prefix, value = _split_opt(opt) + if not prefix: + raise ValueError( + _("Invalid start character for option ({option})").format( + option=opt + ) + ) + self.prefixes.add(prefix[0]) + if len(prefix) == 1 and len(value) == 1: + self._short_opts.append(opt) + else: + self._long_opts.append(opt) + self.prefixes.add(prefix) + + if action is None: + action = "store" + + self.dest = dest + self.action = action + self.nargs = nargs + self.const = const + self.obj = obj + + @property + def takes_value(self) -> bool: + return self.action in ("store", "append") + + def process(self, value: t.Any, state: _ParsingState) -> None: + if self.action == "store": + state.opts[self.dest] = value # type: ignore + elif self.action == "store_const": + state.opts[self.dest] = self.const # type: ignore + elif self.action == "append": + state.opts.setdefault(self.dest, []).append(value) # type: ignore + elif self.action == "append_const": + state.opts.setdefault(self.dest, []).append(self.const) # type: ignore + elif self.action == "count": + state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore + else: + raise ValueError(f"unknown action '{self.action}'") + state.order.append(self.obj) + + +class _Argument: + def __init__(self, obj: CoreArgument, dest: str | None, nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: str | cabc.Sequence[str | T_UNSET] | T_UNSET, + state: _ParsingState, + ) -> None: + if self.nargs > 1: + assert isinstance(value, cabc.Sequence) + holes = sum(x is UNSET for x in value) + if holes == len(value): + value = UNSET + elif holes != 0: + raise BadArgumentUsage( + _("Argument {name!r} takes {nargs} values.").format( + name=self.dest, nargs=self.nargs + ) + ) + + # We failed to collect any argument value so we consider the argument as unset. + if value == (): + value = UNSET + + state.opts[self.dest] = value # type: ignore + state.order.append(self.obj) + + +class _ParsingState: + def __init__(self, rargs: list[str]) -> None: + self.opts: dict[str, t.Any] = {} + self.largs: list[str] = [] + self.rargs = rargs + self.order: list[CoreParameter] = [] + + +class _OptionParser: + """The option parser is an internal class that is ultimately used to + parse options and arguments. It's modelled after optparse and brings + a similar but vastly simplified API. It should generally not be used + directly as the high level Click classes wrap it for you. + + It's not nearly as extensible as optparse or argparse as it does not + implement features that are implemented on a higher level (such as + types or defaults). + + :param ctx: optionally the :class:`~click.Context` where this parser + should go with. + + .. deprecated:: 8.2 + Will be removed in Click 9.0. + """ + + def __init__(self, ctx: Context | None = None) -> None: + #: The :class:`~click.Context` for this parser. This might be + #: `None` for some advanced use cases. + self.ctx = ctx + #: This controls how the parser deals with interspersed arguments. + #: If this is set to `False`, the parser will stop on the first + #: non-option. Click uses this to implement nested subcommands + #: safely. + self.allow_interspersed_args: bool = True + #: This tells the parser how to deal with unknown options. By + #: default it will error out (which is sensible), but there is a + #: second mode where it will ignore it and continue processing + #: after shifting all the unknown options into the resulting args. + self.ignore_unknown_options: bool = False + + if ctx is not None: + self.allow_interspersed_args = ctx.allow_interspersed_args + self.ignore_unknown_options = ctx.ignore_unknown_options + + self._short_opt: dict[str, _Option] = {} + self._long_opt: dict[str, _Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: list[_Argument] = [] + + def add_option( + self, + obj: CoreOption, + opts: cabc.Sequence[str], + dest: str | None, + action: str | None = None, + nargs: int = 1, + const: t.Any | None = None, + ) -> None: + """Adds a new option named `dest` to the parser. The destination + is not inferred (unlike with optparse) and needs to be explicitly + provided. Action can be any of ``store``, ``store_const``, + ``append``, ``append_const`` or ``count``. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + opts = [_normalize_opt(opt, self.ctx) for opt in opts] + option = _Option(obj, opts, dest, action=action, nargs=nargs, const=const) + self._opt_prefixes.update(option.prefixes) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + def add_argument(self, obj: CoreArgument, dest: str | None, nargs: int = 1) -> None: + """Adds a positional argument named `dest` to the parser. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + self._args.append(_Argument(obj, dest=dest, nargs=nargs)) + + def parse_args( + self, args: list[str] + ) -> tuple[dict[str, t.Any], list[str], list[CoreParameter]]: + """Parses positional arguments and returns ``(values, args, order)`` + for the parsed options and arguments as well as the leftover + arguments if there are any. The order is a list of objects as they + appear on the command line. If arguments appear multiple times they + will be memorized multiple times as well. + """ + state = _ParsingState(args) + try: + self._process_args_for_options(state) + self._process_args_for_args(state) + except UsageError: + if self.ctx is None or not self.ctx.resilient_parsing: + raise + return state.opts, state.largs, state.order + + def _process_args_for_args(self, state: _ParsingState) -> None: + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) + + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + def _process_args_for_options(self, state: _ParsingState) -> None: + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == "--": + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt( + self, opt: str, explicit_value: str | None, state: _ParsingState + ) -> None: + if opt not in self._long_opt: + raise NoSuchOption(opt, possibilities=self._long_opt, ctx=self.ctx) + + option = self._long_opt[opt] + if option.takes_value: + # At this point it's safe to modify rargs by injecting the + # explicit value, because no exception is raised in this + # branch. This means that the inserted value will be fully + # consumed. + if explicit_value is not None: + state.rargs.insert(0, explicit_value) + + value = self._get_value_from_state(opt, option, state) + + elif explicit_value is not None: + raise BadOptionUsage( + opt, _("Option {name!r} does not take a value.").format(name=opt) + ) + + else: + value = UNSET + + option.process(value, state) + + def _match_short_opt(self, arg: str, state: _ParsingState) -> None: + stop = False + i = 1 + prefix = arg[0] + unknown_options = [] + + for ch in arg[1:]: + opt = _normalize_opt(f"{prefix}{ch}", self.ctx) + option = self._short_opt.get(opt) + i += 1 + + if not option: + if self.ignore_unknown_options: + unknown_options.append(ch) + continue + raise NoSuchOption(opt, ctx=self.ctx) + if option.takes_value: + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + state.rargs.insert(0, arg[i:]) + stop = True + + value = self._get_value_from_state(opt, option, state) + + else: + value = UNSET + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we recombine the string of the + # remaining options and re-attach the prefix, then report that + # to the state as new large. This way there is basic combinatorics + # that can be achieved while still ignoring unknown arguments. + if self.ignore_unknown_options and unknown_options: + state.largs.append(f"{prefix}{''.join(unknown_options)}") + + def _get_value_from_state( + self, option_name: str, option: _Option, state: _ParsingState + ) -> str | cabc.Sequence[str] | T_UNSET | T_FLAG_NEEDS_VALUE: + nargs = option.nargs + + value: str | cabc.Sequence[str] | T_UNSET | T_FLAG_NEEDS_VALUE + + if len(state.rargs) < nargs: + if option.obj._flag_needs_value: + # Option allows omitting the value. + value = FLAG_NEEDS_VALUE + else: + raise BadOptionUsage( + option_name, + ngettext( + "Option {name!r} requires an argument.", + "Option {name!r} requires {nargs} arguments.", + nargs, + ).format(name=option_name, nargs=nargs), + ) + elif nargs == 1: + next_rarg = state.rargs[0] + + if ( + option.obj._flag_needs_value + and isinstance(next_rarg, str) + and next_rarg[:1] in self._opt_prefixes + and len(next_rarg) > 1 + ): + # The next arg looks like the start of an option, don't + # use it as the value if omitting the value is allowed. + value = FLAG_NEEDS_VALUE + else: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + return value + + def _process_opts(self, arg: str, state: _ParsingState) -> None: + explicit_value = None + # Long option handling happens in two parts. The first part is + # supporting explicitly attached values. In any case, we will try + # to long match the option first. + if "=" in arg: + long_opt, explicit_value = arg.split("=", 1) + else: + long_opt = arg + norm_long_opt = _normalize_opt(long_opt, self.ctx) + + # At this point we will match the (assumed) long option through + # the long option matching code. Note that this allows options + # like "-foo" to be matched as long options. + try: + self._match_long_opt(norm_long_opt, explicit_value, state) + except NoSuchOption: + # At this point the long option matching failed, and we need + # to try with short options. However there is a special rule + # which says, that if we have a two character options prefix + # (applies to "--foo" for instance), we do not dispatch to the + # short option code and will instead raise the no option + # error. + if arg[:2] not in self._opt_prefixes: + self._match_short_opt(arg, state) + return + + if not self.ignore_unknown_options: + raise + + state.largs.append(arg) + + +def __getattr__(name: str) -> object: + import warnings + + if name in { + "OptionParser", + "Argument", + "Option", + "split_opt", + "normalize_opt", + "ParsingState", + }: + warnings.warn( + f"'parser.{name}' is deprecated and will be removed in Click 9.0." + " The old parser is available in 'optparse'.", + DeprecationWarning, + stacklevel=2, + ) + return globals()[f"_{name}"] + + if name == "split_arg_string": + from .shell_completion import split_arg_string + + warnings.warn( + "Importing 'parser.split_arg_string' is deprecated, it will only be" + " available in 'shell_completion' in Click 9.0.", + DeprecationWarning, + stacklevel=2, + ) + return split_arg_string + + raise AttributeError(name) diff --git a/venv/lib/python3.12/site-packages/click/py.typed b/venv/lib/python3.12/site-packages/click/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/click/shell_completion.py b/venv/lib/python3.12/site-packages/click/shell_completion.py new file mode 100644 index 0000000..8a2668b --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/shell_completion.py @@ -0,0 +1,801 @@ +from __future__ import annotations + +import collections.abc as cabc +import os +import re +import typing as t +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .core import ParameterSource +from .utils import echo + + +def shell_complete( + cli: Command, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + instruction: str, +) -> t.Literal[0, 1]: + """Perform shell completion for the given CLI program. + + :param cli: Command being called. + :param ctx_args: Extra arguments to pass to + ``cli.make_context``. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + :param instruction: Value of ``complete_var`` with the completion + instruction and shell, in the form ``instruction_shell``. + :return: Status code to exit with. + """ + shell, _, instruction = instruction.partition("_") + comp_cls = get_completion_class(shell) + + if comp_cls is None: + return 1 + + comp = comp_cls(cli, ctx_args, prog_name, complete_var) + + # Write bytes, otherwise Windows text stdout translates LF to CRLF and breaks. + if instruction == "source": + echo(comp.source().encode(), nl=False) + return 0 + + if instruction == "complete": + echo(comp.complete().encode()) + return 0 + + return 1 + + +if t.TYPE_CHECKING: + from typing_extensions import TypeVar + + # `Any` is used as default for backwards compatibility (instead of e.g. `str`) + _ValueT_co = TypeVar("_ValueT_co", covariant=True, default=t.Any) +else: + _ValueT_co = t.TypeVar("_ValueT_co", covariant=True) + + +class CompletionItem(t.Generic[_ValueT_co]): + """Represents a completion value and metadata about the value. The + default metadata is ``type`` to indicate special shell handling, + and ``help`` if a shell supports showing a help string next to the + value. + + Arbitrary parameters can be passed when creating the object, and + accessed using ``item.attr``. If an attribute wasn't passed, + accessing it returns ``None``. + + :param value: The completion suggestion. + :param type: Tells the shell script to provide special completion + support for the type. Click uses ``"dir"`` and ``"file"``. + :param help: String shown next to the value if supported. + :param kwargs: Arbitrary metadata. The built-in implementations + don't use this, but custom type completions paired with custom + shell support could use it. + """ + + __slots__ = ("value", "type", "help", "_info") + + def __init__( + self, + value: _ValueT_co, + type: str = "plain", + help: str | None = None, + **kwargs: t.Any, + ) -> None: + self.value: _ValueT_co = value + self.type: str = type + self.help: str | None = help + self._info = kwargs + + def __getattr__(self, name: str) -> t.Any: + return self._info.get(name) + + +# Only Bash >= 4.4 has the nosort option. +_SOURCE_BASH = """\ +%(complete_func)s() { + local IFS=$'\\n' + local response + + response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ +%(complete_var)s=bash_complete $1) + + for completion in $response; do + IFS=',' read type value <<< "$completion" + + if [[ $type == 'dir' ]]; then + COMPREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMPREPLY=() + compopt -o default + elif [[ $type == 'plain' ]]; then + COMPREPLY+=($value) + fi + done + + return 0 +} + +%(complete_func)s_setup() { + complete -o nosort -F %(complete_func)s %(prog_name)s +} + +%(complete_func)s_setup; +""" + +# See ZshComplete.format_completion below, and issue #2703, before +# changing this script. +# +# (TL;DR: _describe is picky about the format, but this Zsh script snippet +# is already widely deployed. So freeze this script, and use clever-ish +# handling of colons in ZshComplet.format_completion.) +_SOURCE_ZSH = """\ +#compdef %(prog_name)s + +%(complete_func)s() { + local -a completions + local -a completions_with_descriptions + local -a response + (( ! $+commands[%(prog_name)s] )) && return 1 + + response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ +%(complete_var)s=zsh_complete %(prog_name)s)}") + + for type key descr in ${response}; do + if [[ "$type" == "plain" ]]; then + if [[ "$descr" == "_" ]]; then + completions+=("$key") + else + completions_with_descriptions+=("$key":"$descr") + fi + elif [[ "$type" == "dir" ]]; then + _path_files -/ + elif [[ "$type" == "file" ]]; then + _path_files -f + fi + done + + if [ -n "$completions_with_descriptions" ]; then + _describe -V unsorted completions_with_descriptions -U + fi + + if [ -n "$completions" ]; then + compadd -U -V unsorted -a completions + fi +} + +if [[ $zsh_eval_context[-1] == loadautofunc ]]; then + # autoload from fpath, call function directly + %(complete_func)s "$@" +else + # eval/source/. command, register function for later + compdef %(complete_func)s %(prog_name)s +fi +""" + +_SOURCE_FISH = """\ +function %(complete_func)s; + set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ +COMP_CWORD=(commandline -t) %(prog_name)s); + + for completion in $response; + set -l metadata (string split "," $completion); + + if test $metadata[1] = "dir"; + __fish_complete_directories $metadata[2]; + else if test $metadata[1] = "file"; + __fish_complete_path $metadata[2]; + else if test $metadata[1] = "plain"; + echo $metadata[2]; + end; + end; +end; + +complete --no-files --command %(prog_name)s --arguments \ +"(%(complete_func)s)"; +""" + +# Compatible with Windows PowerShell 5.1+ and PowerShell (pwsh) 7+. +# Uses Register-ArgumentCompleter -Native, which receives the command AST +# as parsed by PowerShell. The command text is forwarded verbatim through +# COMP_WORDS so the Python side can reuse the same shlex-based splitting +# used by the bash/zsh completers. +_SOURCE_POWERSHELL = """\ +Register-ArgumentCompleter -Native -CommandName %(prog_name)s -ScriptBlock { + param($wordToComplete, $commandAst, $cursorPosition) + + $prev_complete = $env:%(complete_var)s + $prev_words = $env:COMP_WORDS + $prev_cword = $env:COMP_CWORD + + $env:%(complete_var)s = 'powershell_complete' + $env:COMP_WORDS = $commandAst.ToString() + if ($wordToComplete) { + $env:COMP_CWORD = $commandAst.CommandElements.Count - 1 + } else { + $env:COMP_CWORD = $commandAst.CommandElements.Count + } + + try { + $response = & %(prog_name)s 2>$null + } finally { + $env:%(complete_var)s = $prev_complete + $env:COMP_WORDS = $prev_words + $env:COMP_CWORD = $prev_cword + } + + if (-not $response) { return } + + $prefix = "$wordToComplete*" + $lines = $response -split "`n" + for ($i = 0; $i + 2 -lt $lines.Count; $i += 3) { + $type = $lines[$i] + $value = $lines[$i + 1] + $descr = $lines[$i + 2] + if (-not $type) { continue } + + if ($type -eq 'plain') { + $tip = if ($descr -and $descr -ne '_') { $descr } else { $value } + [System.Management.Automation.CompletionResult]::new( + $value, $value, 'ParameterValue', $tip) + } elseif ($type -eq 'dir') { + Get-ChildItem -Directory -Path $prefix -ErrorAction SilentlyContinue | + ForEach-Object { + [System.Management.Automation.CompletionResult]::new( + $_.FullName, $_.Name, 'ProviderContainer', $_.FullName) + } + } elseif ($type -eq 'file') { + Get-ChildItem -Path $prefix -ErrorAction SilentlyContinue | + ForEach-Object { + $kind = 'ProviderItem' + if ($_.PSIsContainer) { $kind = 'ProviderContainer' } + [System.Management.Automation.CompletionResult]::new( + $_.FullName, $_.Name, $kind, $_.FullName) + } + } + } +} +""" + + +class _SourceVarsDict(t.TypedDict): + complete_func: str + complete_var: str + prog_name: str + + +class ShellComplete: + """Base class for providing shell completion support. A subclass for + a given shell will override attributes and methods to implement the + completion instructions (``source`` and ``complete``). + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + + .. versionadded:: 8.0 + """ + + name: t.ClassVar[str] + """Name to register the shell as with :func:`add_completion_class`. + This is used in completion instructions (``{name}_source`` and + ``{name}_complete``). + """ + + source_template: t.ClassVar[str] + """Completion script template formatted by :meth:`source`. This must + be provided by subclasses. + """ + + cli: Command + ctx_args: cabc.MutableMapping[str, t.Any] + prog_name: str + complete_var: str + + def __init__( + self, + cli: Command, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + ) -> None: + self.cli = cli + self.ctx_args = ctx_args + self.prog_name = prog_name + self.complete_var = complete_var + + @property + def func_name(self) -> str: + """The name of the shell function defined by the completion + script. + """ + safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII) + return f"_{safe_name}_completion" + + def source_vars(self) -> _SourceVarsDict: + """Vars for formatting :attr:`source_template`. + + By default this provides ``complete_func``, ``complete_var``, + and ``prog_name``. + """ + return { + "complete_func": self.func_name, + "complete_var": self.complete_var, + "prog_name": self.prog_name, + } + + def source(self) -> str: + """Produce the shell script that defines the completion + function. By default this ``%``-style formats + :attr:`source_template` with the dict returned by + :meth:`source_vars`. + """ + return self.source_template % self.source_vars() + + def get_completion_args(self) -> tuple[list[str], str]: + """Use the env vars defined by the shell script to return a + tuple of ``args, incomplete``. This must be implemented by + subclasses. + """ + raise NotImplementedError + + def get_completions( + self, args: list[str], incomplete: str + ) -> list[CompletionItem[str]]: + """Determine the context and last complete command or parameter + from the complete args. Call that object's ``shell_complete`` + method to get the completions for the incomplete value. + + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) + obj, incomplete = _resolve_incomplete(ctx, args, incomplete) + return obj.shell_complete(ctx, incomplete) + + def format_completion(self, item: CompletionItem[str]) -> str: + """Format a completion item into the form recognized by the + shell script. This must be implemented by subclasses. + + :param item: Completion item to format. + """ + raise NotImplementedError + + def complete(self) -> str: + """Produce the completion data to send back to the shell. + + By default this calls :meth:`get_completion_args`, gets the + completions, then calls :meth:`format_completion` for each + completion. + """ + args, incomplete = self.get_completion_args() + completions = self.get_completions(args, incomplete) + out = [self.format_completion(item) for item in completions] + return "\n".join(out) + + +class BashComplete(ShellComplete): + """Shell completion for Bash.""" + + name: t.ClassVar[str] = "bash" + source_template: t.ClassVar[str] = _SOURCE_BASH + + @staticmethod + def _check_version() -> None: + import shutil + import subprocess + + bash_exe = shutil.which("bash") + + if bash_exe is None: + match = None + else: + output = subprocess.run( + [bash_exe, "--norc", "-c", 'echo "${BASH_VERSION}"'], + stdout=subprocess.PIPE, + ) + match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) + + if match is not None: + major, minor = match.groups() + + if major < "4" or major == "4" and minor < "4": + echo( + _( + "Shell completion is not supported for Bash" + " versions older than 4.4." + ), + err=True, + ) + else: + echo( + _("Couldn't detect Bash version, shell completion is not supported."), + err=True, + ) + + def source(self) -> str: + self._check_version() + return super().source() + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem[t.Any]) -> str: + return f"{item.type},{item.value}" + + +class ZshComplete(ShellComplete): + """Shell completion for Zsh.""" + + name: t.ClassVar[str] = "zsh" + source_template: t.ClassVar[str] = _SOURCE_ZSH + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem[str]) -> str: + help_ = item.help or "_" + # The zsh completion script uses `_describe` on items with help + # texts (which splits the item help from the item value at the + # first unescaped colon) and `compadd` on items without help + # text (which uses the item value as-is and does not support + # colon escaping). So escape colons in the item value if and + # only if the item help is not the sentinel "_" value, as used + # by the completion script. + # + # (The zsh completion script is potentially widely deployed, and + # thus harder to fix than this method.) + # + # See issue #1812 and issue #2703 for further context. + value = item.value.replace(":", r"\:") if help_ != "_" else item.value + return f"{item.type}\n{value}\n{help_}" + + +class FishComplete(ShellComplete): + """Shell completion for Fish.""" + + name: t.ClassVar[str] = "fish" + source_template: t.ClassVar[str] = _SOURCE_FISH + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + incomplete = os.environ["COMP_CWORD"] + if incomplete: + incomplete = split_arg_string(incomplete)[0] + args = cwords[1:] + + # Fish stores the partial word in both COMP_WORDS and + # COMP_CWORD, remove it from complete args. + if incomplete and args and args[-1] == incomplete: + args.pop() + + return args, incomplete + + def format_completion(self, item: CompletionItem[str]) -> str: + """ + .. versionchanged:: 8.4.2 + Escape newlines and replace tabs with spaces in the help text to + fix completion errors with multi-line help strings. + """ + # According to https://fishshell.com/docs/current/cmds/complete.html + # Command substitutions found in ARGUMENTS should return a newline- + # separated list of arguments, and each argument may optionally have a tab + # character followed by the argument description. + if item.help: + help_ = item.help.replace("\n", "\\n").replace("\t", " ") + return f"{item.type},{item.value}\t{help_}" + + return f"{item.type},{item.value}" + + +class PowerShellComplete(ShellComplete): + """Shell completion for PowerShell (Windows PowerShell 5.1+ and pwsh 7+). + + .. versionadded:: 8.5.0 + """ + + name: t.ClassVar[str] = "powershell" + source_template: t.ClassVar[str] = _SOURCE_POWERSHELL + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem[str]) -> str: + # PowerShell parses the response by splitting on newlines and + # consuming three lines per completion (type, value, help). + # Multi-line help text would break that framing, so collapse + # newlines and carriage returns to spaces. The literal "_" + # sentinel matches what ZshComplete uses for "no help text". + if item.help: + help_ = item.help.replace("\r", " ").replace("\n", " ") + else: + help_ = "_" + return f"{item.type}\n{item.value}\n{help_}" + + +_available_shells: t.Final[dict[str, type[ShellComplete]]] = { + "bash": BashComplete, + "fish": FishComplete, + "powershell": PowerShellComplete, + "zsh": ZshComplete, +} + +_ShellCompleteT = t.TypeVar("_ShellCompleteT", bound="ShellComplete") + + +def add_completion_class( + cls: type[_ShellCompleteT], name: str | None = None +) -> type[_ShellCompleteT]: + """Register a :class:`ShellComplete` subclass under the given name. + The name will be provided by the completion instruction environment + variable during completion. + + :param cls: The completion class that will handle completion for the + shell. + :param name: Name to register the class under. Defaults to the + class's ``name`` attribute. + """ + if name is None: + name = cls.name + + _available_shells[name] = cls + + return cls + + +@t.overload +def get_completion_class(shell: t.Literal["bash"]) -> type[BashComplete]: ... +@t.overload +def get_completion_class(shell: t.Literal["fish"]) -> type[FishComplete]: ... +@t.overload +def get_completion_class(shell: t.Literal["zsh"]) -> type[ZshComplete]: ... +@t.overload +def get_completion_class(shell: str) -> type[ShellComplete] | None: ... +def get_completion_class(shell: str) -> type[ShellComplete] | None: + """Look up a registered :class:`ShellComplete` subclass by the name + provided by the completion instruction environment variable. If the + name isn't registered, returns ``None``. + + :param shell: Name the class is registered under. + """ + return _available_shells.get(shell) + + +def split_arg_string(string: str) -> list[str]: + """Split an argument string as with :func:`shlex.split`, but don't + fail if the string is incomplete. Ignores a missing closing quote or + incomplete escape sequence and uses the partial token as-is. + + .. code-block:: python + + split_arg_string("example 'my file") + ["example", "my file"] + + split_arg_string("example my\\") + ["example", "my"] + + :param string: String to split. + + .. versionchanged:: 8.2 + Moved to ``shell_completion`` from ``parser``. + """ + import shlex + + lex = shlex.shlex(string, posix=True) + lex.whitespace_split = True + lex.commenters = "" + out: list[str] = [] + + try: + out.extend(lex) + except ValueError: + # Raised when end-of-string is reached in an invalid state. Use + # the partial token as-is. The quote or escape character is in + # lex.state, not lex.token. + out.append(lex.token) + + return out + + +def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: + """Determine if the given parameter is an argument that can still + accept values. + + :param ctx: Invocation context for the command represented by the + parsed complete args. + :param param: Argument object being checked. + """ + if not isinstance(param, Argument): + return False + + value = ctx.params.get(param.name) + return ( + param.nargs == -1 + or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE + or ( + param.nargs > 1 + and isinstance(value, (tuple, list)) + and len(value) < param.nargs + ) + ) + + +def _start_of_option(ctx: Context, value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + return c in ctx._opt_prefixes + + +def _is_incomplete_option(ctx: Context, args: list[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag or param.count: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(ctx, arg): + last_option = arg + break + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: Command, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + args: list[str], +) -> Context: + """Produce the context hierarchy starting with the command and + traversing the complete arguments. This only follows the commands, + it doesn't trigger input prompts or callbacks. + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param args: List of complete args before the incomplete value. + """ + ctx_args["resilient_parsing"] = True + with cli.make_context(prog_name, args.copy(), **ctx_args) as ctx: + args = ctx._protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, Group): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + with cmd.make_context( + name, args, parent=ctx, resilient_parsing=True + ) as sub_ctx: + ctx = sub_ctx + args = ctx._protected_args + ctx.args + else: + sub_ctx = ctx + + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + with cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) as sub_sub_ctx: + sub_ctx = sub_sub_ctx + args = sub_ctx.args + + ctx = sub_ctx + args = [*sub_ctx._protected_args, *sub_ctx.args] + else: + break + + return ctx + + +def _resolve_incomplete( + ctx: Context, args: list[str], incomplete: str +) -> tuple[Command | Parameter, str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(ctx, incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(ctx, incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(ctx, args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/venv/lib/python3.12/site-packages/click/termui.py b/venv/lib/python3.12/site-packages/click/termui.py new file mode 100644 index 0000000..e1315f2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/termui.py @@ -0,0 +1,1014 @@ +from __future__ import annotations + +import collections.abc as cabc +import inspect +import io +import itertools +import os +import re +import sys +import typing as t +from contextlib import AbstractContextManager +from contextlib import redirect_stdout +from gettext import gettext as _ + +from . import _compat +from ._compat import isatty +from ._compat import strip_ansi +from .exceptions import Abort +from .exceptions import UsageError +from .globals import resolve_color_default +from .types import Choice +from .types import convert_type +from .types import ParamType +from .utils import _LazyFile +from .utils import echo + +if t.TYPE_CHECKING: + from ._termui_impl import ProgressBar + +V = t.TypeVar("V") + +# The prompt functions to use. The doc tools currently override these +# functions to customize how they work. +visible_prompt_func: t.Callable[[str], str] = input + +_ansi_colors = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, + "reset": 39, + "bright_black": 90, + "bright_red": 91, + "bright_green": 92, + "bright_yellow": 93, + "bright_blue": 94, + "bright_magenta": 95, + "bright_cyan": 96, + "bright_white": 97, +} +_ansi_reset_all = "\033[0m" + + +_HIDDEN_INPUT_MASK = "'***'" + + +def _mask_hidden_input(message: str, value: str) -> str: + """Replace occurrences of ``value`` in ``message`` with a fixed mask. + + Both ``repr(value)`` (the form built-in :class:`ParamType` errors use + via ``{value!r}``) and the raw value are masked. The raw-value pass + uses word-boundary lookarounds so a substring like ``"1"`` does not + match inside ``"10"``, and ``"ent"`` does not match inside + ``"Authentication"``. The empty string is skipped to avoid matching + at every boundary. + """ + message = message.replace(repr(value), _HIDDEN_INPUT_MASK) + if value: + message = re.sub( + rf"(? str: + import getpass + + return getpass.getpass(prompt) + + +def _readline_prompt(func: t.Callable[[str], str], text: str, err: bool) -> str: + """Call a prompt function, passing the full prompt so readline can + handle line editing and cursor positioning correctly. + + The prompt is handed to *func* (such as :func:`input`) rather than + written through :func:`echo`, so it has to strip ANSI color and style + codes itself when the destination stream does not support them. Without + this the prompt would keep codes that :func:`echo` removes from the + rest of the output. + """ + stream = sys.stderr if err else sys.stdout + + # Look up ``should_strip_ansi`` on the module so that ``CliRunner``, + # which patches it there during test isolation, is honored. + if _compat.should_strip_ansi(stream, resolve_color_default()): + text = strip_ansi(text) + + if err: + with redirect_stdout(sys.stderr): + return func(text) + return func(text) + + +def _build_prompt( + text: str, + suffix: str, + show_default: bool | str = False, + default: object | None = None, + show_choices: bool = True, + type: object | None = None, +) -> str: + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += f" ({', '.join(map(str, type.choices))})" + default_preview = "" + if show_default: + if isinstance(show_default, str): + default_preview = f" [({show_default})]" + elif default is not None: + default_preview = f" [{_format_default(default)}]" + return f"{prompt}{default_preview}{suffix}" + + +def _format_default(default: V) -> V | str: + if isinstance(default, (io.IOBase, _LazyFile)): + name = getattr(default, "name", None) + + if name is not None: + return str(name) + + return default + + +@t.overload +def prompt( + text: str, + default: str | None = None, + hide_input: bool = False, + confirmation_prompt: bool | str = False, + type: None = None, + value_proc: None = None, + prompt_suffix: str = ": ", + show_default: bool | str = True, + err: bool = False, + show_choices: bool = True, +) -> str: ... + + +@t.overload +def prompt( + text: str, + default: V | str | None = None, + hide_input: bool = False, + confirmation_prompt: bool | str = False, + type: ParamType[V, str] | type[V] | None = None, + value_proc: t.Callable[[str], V] | None = None, + prompt_suffix: str = ": ", + show_default: bool | str = True, + err: bool = False, + show_choices: bool = True, +) -> V: ... + + +def prompt( + text: str, + default: V | str | None = None, + hide_input: bool = False, + confirmation_prompt: bool | str = False, + type: ParamType[V, str] | type[V] | None = None, + value_proc: t.Callable[[str], V] | None = None, + prompt_suffix: str = ": ", + show_default: bool | str = True, + err: bool = False, + show_choices: bool = True, +) -> V: + """Prompts a user for input. This is a convenience function that can + be used to prompt a user for input later. + + If the user aborts the input by sending an interrupt signal, this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the text to show for the prompt. + :param default: the default value to use if no input happens. If this + is not given it will prompt until it's aborted. + :param hide_input: if this is set to true then the input value will + be hidden. + :param confirmation_prompt: Prompt a second time to confirm the + value. Can be set to a string instead of ``True`` to customize + the message. + :param type: the type to use to check the value against. + :param value_proc: if this parameter is provided it's a function that + is invoked instead of the type conversion to + convert a value. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + If this value is a string, it shows that string + in parentheses instead of the actual value. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + :param show_choices: Show or hide choices if the passed type is a Choice. + For example if type is a Choice of either day or week, + show_choices is true and text is "Group by" then the + prompt will be "Group by (day, week): ". + + .. versionchanged:: 8.5.0 + Generically typed: the return type is narrowed by ``type``, + ``value_proc``, or ``default`` instead of being ``Any``. Runtime + behavior is unchanged. + + .. versionchanged:: 8.3.3 + ``show_default`` can be a string to show a custom value instead + of the actual default, matching the help text behavior. + + .. versionchanged:: 8.3.1 + A space is no longer appended to the prompt. + + .. versionadded:: 8.0 + ``confirmation_prompt`` can be a custom string. + + .. versionadded:: 7.0 + Added the ``show_choices`` parameter. + + .. versionadded:: 6.0 + Added unicode support for cmd.exe on Windows. + + .. versionadded:: 4.0 + Added the `err` parameter. + + """ + + def prompt_func(text: str) -> str: + f = hidden_prompt_func if hide_input else visible_prompt_func + try: + return _readline_prompt(f, text, err) + except (KeyboardInterrupt, EOFError): + # getpass doesn't print a newline if the user aborts input with ^C. + # Allegedly this behavior is inherited from getpass(3). + # A doc bug has been filed at https://bugs.python.org/issue24711 + if hide_input: + echo(None, err=err) + raise Abort() from None + + if value_proc is None: + value_proc = convert_type(type, default) + + prompt = _build_prompt( + text, prompt_suffix, show_default, default, show_choices, type + ) + + if confirmation_prompt: + if confirmation_prompt is True: + confirmation_prompt = _("Repeat for confirmation") + + confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) + + while True: + while True: + value = prompt_func(prompt) + if value: + break + elif default is not None: + # Defaults of any type are accepted and round trip through + # value_proc like typed input, so the annotation is only + # accurate for typed input. + value = t.cast("str", default) + break + try: + result = value_proc(value) + except UsageError as e: + message = _mask_hidden_input(e.message, value) if hide_input else e.message + echo(_("Error: {message}").format(message=message), err=err) + continue + if not confirmation_prompt: + return result + while True: + value2 = prompt_func(confirmation_prompt) + is_empty = not value and not value2 + if value2 or is_empty: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: bool | None = False, + abort: bool = False, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, +) -> bool: + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the question to ask. + :param default: The default value to use when no input is given. If + ``None``, repeat until input is given. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + + .. versionchanged:: 8.3.1 + A space is no longer appended to the prompt. + + .. versionchanged:: 8.0 + Repeat until input is given if ``default`` is ``None``. + + .. versionadded:: 4.0 + Added the ``err`` parameter. + """ + prompt = _build_prompt( + text, + prompt_suffix, + show_default, + "y/n" if default is None else ("Y/n" if default else "y/N"), + ) + + while True: + try: + value = _readline_prompt(visible_prompt_func, prompt, err).lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() from None + if value in ("y", "yes"): + rv = True + elif value in ("n", "no"): + rv = False + elif default is not None and value == "": + rv = default + else: + echo(_("Error: invalid input"), err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def get_pager_file( + color: bool | None = None, +) -> t.ContextManager[t.TextIO]: + """Context manager. + + Yields a writable file-like object which can be used as an output pager. + + .. versionadded:: 8.4.0 + + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + from ._termui_impl import get_pager_file + + color = resolve_color_default(color) + + return get_pager_file(color=color) + + +def echo_via_pager( + text_or_generator: cabc.Iterable[str] | t.Callable[[], cabc.Iterable[str]] | str, + color: bool | None = None, +) -> None: + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + + if inspect.isgeneratorfunction(text_or_generator): + i = t.cast("t.Callable[[], cabc.Iterable[str]]", text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast("cabc.Iterable[str]", text_or_generator)) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, str) else str(el) for el in i) + + with get_pager_file(color=color) as pager: + for text in itertools.chain(text_generator, "\n"): + pager.write(text) + # Flush after each write so a slow generator streams to the pager + # incrementally rather than staying invisible until the pipe buffer + # fills (~8 KB). + pager.flush() + + +@t.overload +def progressbar( + *, + length: int, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[int]: ... + + +@t.overload +def progressbar( + iterable: cabc.Iterable[V] | None = None, + length: int | None = None, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[V]: ... + + +def progressbar( + iterable: cabc.Iterable[V] | None = None, + length: int | None = None, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[V]: + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already created. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + Note: The progress bar is currently designed for use cases where the + total progress can be expected to take at least several seconds. + Because of this, the ProgressBar class object won't display + progress that is considered too fast, and progress where the time + between steps is less than a second. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + The ``update()`` method also takes an optional value specifying the + ``current_item`` at the new position. This is useful when used + together with ``item_show_func`` to customize the output for each + manual step:: + + with click.progressbar( + length=total_size, + label='Unzipping archive', + item_show_func=lambda a: a.filename + ) as bar: + for archive in zip_file: + archive.extract() + bar.update(archive.size, archive) + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param hidden: hide the progressbar. Defaults to ``False``. When no tty is + detected, it will only print the progressbar label. Setting this to + ``False`` also disables that. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: A function called with the current item which + can return a string to show next to the progress bar. If the + function returns ``None`` nothing is shown. The current item can + be ``None``, such as when entering and exiting the bar. + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: The file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + :param update_min_steps: Render only when this many updates have + completed. This allows tuning for very fast iterators. + + .. versionadded:: 8.2 + The ``hidden`` argument. + + .. versionchanged:: 8.0 + Output is shown even if execution time is less than 0.5 seconds. + + .. versionchanged:: 8.0 + ``item_show_func`` shows the current item, not the previous one. + + .. versionchanged:: 8.0 + Labels are echoed if the output is not a TTY. Reverts a change + in 7.0 that removed all output. + + .. versionadded:: 8.0 + The ``update_min_steps`` parameter. + + .. versionadded:: 4.0 + The ``color`` parameter and ``update`` method. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + hidden=hidden, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + update_min_steps=update_min_steps, + ) + + +def clear() -> None: + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + + # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor + echo("\033[2J\033[1;1H", nl=False) + + +def _interpret_color(color: int | tuple[int, int, int] | str, offset: int = 0) -> str: + """Interprets a color value and returns the corresponding ANSI code.""" + if isinstance(color, str) and color in _ansi_colors: + return str(_ansi_colors[color] + offset) + + # bool is an int subclass: without the exclusion, True and False would + # silently render as the palette indices 1 and 0. + elif isinstance(color, int) and not isinstance(color, bool): + if 0 <= color <= 255: + return f"{38 + offset};5;{color:d}" + + elif ( + isinstance(color, (tuple, list)) + and len(color) == 3 + and all( + isinstance(c, int) and not isinstance(c, bool) and 0 <= c <= 255 + for c in color + ) + ): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + raise ValueError(_("Unknown color {colour!r}").format(colour=color)) + + +def style( + text: t.Any, + fg: int | tuple[int, int, int] | str | None = None, + bg: int | tuple[int, int, int] | str | None = None, + bold: bool | None = None, + dim: bool | None = None, + underline: bool | None = None, + overline: bool | None = None, + italic: bool | None = None, + blink: bool | None = None, + reverse: bool | None = None, + strikethrough: bool | None = None, + reset: bool = True, +) -> str: + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + If the terminal supports it, color may also be specified as: + + - An integer in the interval [0, 255]. The terminal must support + 8-bit/256-color mode. + - An RGB tuple of three integers in [0, 255]. The terminal must + support 24-bit/true-color mode. + + See https://en.wikipedia.org/wiki/ANSI_color and + https://gist.github.com/XVilka/8346728 for more information. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param overline: if provided this will enable or disable overline. + :param italic: if provided this will enable or disable italic. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param strikethrough: if provided this will enable or disable + striking through text. + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + + .. versionchanged:: 8.5.0 + All invalid color values raise :exc:`ValueError`. 256-color index + ``0`` is no longer ignored. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. + + .. versionchanged:: 8.0 + Added support for 256 and RGB color codes. + + .. versionchanged:: 8.0 + Added the ``strikethrough``, ``italic``, and ``overline`` + parameters. + + .. versionchanged:: 7.0 + Added support for bright colors. + + .. versionadded:: 2.0 + """ + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg is not None: + bits.append(f"\033[{_interpret_color(fg)}m") + + if bg is not None: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + + if bold is not None: + bits.append(f"\033[{1 if bold else 22}m") + if dim is not None: + bits.append(f"\033[{2 if dim else 22}m") + if underline is not None: + bits.append(f"\033[{4 if underline else 24}m") + if overline is not None: + bits.append(f"\033[{53 if overline else 55}m") + if italic is not None: + bits.append(f"\033[{3 if italic else 23}m") + if blink is not None: + bits.append(f"\033[{5 if blink else 25}m") + if reverse is not None: + bits.append(f"\033[{7 if reverse else 27}m") + if strikethrough is not None: + bits.append(f"\033[{9 if strikethrough else 29}m") + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return "".join(bits) + + +def unstyle(text: str) -> str: + """Removes ANSI styling information from a string. Usually it's not + necessary to use this function as Click's echo function will + automatically remove styling if necessary. + + .. versionadded:: 2.0 + + :param text: the text to remove style information from. + """ + return strip_ansi(text) + + +def secho( + message: t.Any | None = None, + file: t.IO[t.AnyStr] | None = None, + nl: bool = True, + err: bool = False, + color: bool | None = None, + **styles: t.Any, +) -> None: + """This function combines :func:`echo` and :func:`style` into one + call. As such the following two calls are the same:: + + click.secho('Hello World!', fg='green') + click.echo(click.style('Hello World!', fg='green')) + + All keyword arguments are forwarded to the underlying functions + depending on which one they go with. + + Non-string types will be converted to :class:`str`. However, + :class:`bytes` are passed directly to :meth:`echo` without applying + style. If you want to style bytes that represent text, call + :meth:`bytes.decode` first. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. Bytes are + passed through without style applied. + + .. versionadded:: 2.0 + """ + if message is not None and not isinstance(message, (bytes, bytearray)): + message = style(message, **styles) + + return echo(message, file=file, nl=nl, err=err, color=color) + + +@t.overload +def edit( + text: bytes | bytearray, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = False, + extension: str = ".txt", +) -> bytes | None: ... + + +@t.overload +def edit( + text: str, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", +) -> str | None: ... + + +@t.overload +def edit( + text: None = None, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + filename: str + | os.PathLike[str] + | cabc.Iterable[str | os.PathLike[str]] + | None = None, +) -> None: ... + + +def edit( + text: str | bytes | bytearray | None = None, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + filename: str + | os.PathLike[str] + | cabc.Iterable[str | os.PathLike[str]] + | None = None, +) -> str | bytes | bytearray | None: + r"""Edits the given text in the defined editor. If an editor is given + (should be the full path to the executable but the regular operating + system search path is used for finding the executable) it overrides + the detected editor. Optionally, some environment variables can be + used. If the editor is closed without changes, `None` is returned. In + case a file is edited directly the return value is always `None` and + `require_save` and `extension` are ignored. + + If the editor cannot be opened a :exc:`UsageError` is raised. + + Note for Windows: to simplify cross-platform usage, the newlines are + automatically converted from POSIX to Windows and vice versa. As such, + the message here will have ``\n`` as newline markers. + + :param text: the text to edit. + :param editor: optionally the editor to use. Defaults to automatic + detection. + :param env: environment variables to forward to the editor. + :param require_save: if this is true, then not saving in the editor + will make the return value become `None`. + :param extension: the extension to tell the editor about. This defaults + to `.txt` but changing this might change syntax + highlighting. + :param filename: if provided it will edit this file instead of the + provided text contents. It will not use a temporary + file as an indirection in that case. It accepts a path + or any iterable of paths. If the editor supports + editing multiple files at once, a sequence of files may + be passed as well. Invoke `click.file` once per file + instead if multiple files cannot be managed at once or + editing the files serially is desired. + + .. versionchanged:: 8.2.0 + ``filename`` now accepts any ``Iterable[str]`` in addition to a ``str`` + if the ``editor`` supports editing multiple files at once. + + .. versionchanged:: 8.5.0 + ``filename`` accepts ``os.PathLike`` values in addition to strings. + + """ + from ._termui_impl import Editor + + ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) + + if filename is None: + return ed.edit(text) + + if isinstance(filename, (str, os.PathLike)): + filename = (filename,) + + ed.edit_files(filenames=filename) + return None + + +def launch(url: str, wait: bool = False, locate: bool = False) -> int: + """This function launches the given URL (or filename) in the default + viewer application for this file type. If this is an executable, it + might launch the executable in a new session. The return value is + the exit code of the launched application. Usually, ``0`` indicates + success. + + Examples:: + + click.launch('https://click.palletsprojects.com/') + click.launch('/my/downloaded/file', locate=True) + + .. versionadded:: 2.0 + + :param url: URL or filename of the thing to launch. + :param wait: Wait for the program to exit before returning. This + only works if the launched program blocks. In particular, + ``xdg-open`` on Linux does not block. + :param locate: if this is set to `True` then instead of launching the + application associated with the URL it will attempt to + launch a file manager with the file located. This + might have weird effects if the URL does not point to + the filesystem. + """ + from ._termui_impl import open_url + + return open_url(url, wait=wait, locate=locate) + + +# If this is provided, getchar() calls into this instead. This is used +# for unittesting purposes. +_getchar: t.Callable[[bool], str] | None = None + + +def getchar(echo: bool = False) -> str: + """Fetches a single character from the terminal and returns it. This + will always return a unicode character and under certain rare + circumstances this might return more than one character. The + situations which more than one character is returned is when for + whatever reason multiple characters end up in the terminal buffer or + standard input was not actually a terminal. + + Note that this will always read from the terminal, even if something + is piped into the standard input. + + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + + .. versionadded:: 2.0 + + :param echo: if set to `True`, the character read will also show up on + the terminal. The default is to not show it. + """ + global _getchar + + if _getchar is None: + from ._termui_impl import getchar as f + + _getchar = f + + return _getchar(echo) + + +def raw_terminal() -> AbstractContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: str | None = None, err: bool = False) -> None: + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: The message to print before pausing. Defaults to + ``"Press any key to continue..."``. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + + if info is None: + info = _("Press any key to continue...") + + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/venv/lib/python3.12/site-packages/click/testing.py b/venv/lib/python3.12/site-packages/click/testing.py new file mode 100644 index 0000000..cc123d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/testing.py @@ -0,0 +1,798 @@ +from __future__ import annotations + +import collections.abc as cabc +import contextlib +import io +import os +import pdb +import shlex +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import _compat +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from _typeshed import ReadableBuffer + + from .core import Command + +if sys.platform == "win32": + CaptureMode: t.TypeAlias = t.Literal["sys"] # pyright: ignore[reportRedeclaration] +else: + CaptureMode: t.TypeAlias = t.Literal["sys", "fd"] # pyright: ignore[reportRedeclaration] +ExceptionInfo: t.TypeAlias = tuple[type[BaseException], BaseException, TracebackType] + + +class EchoingStdin: + _input: t.BinaryIO + _output: t.BinaryIO + _paused: bool + + def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: + self._input = input + self._output = output + self._paused = False + + def __getattr__(self, x: str) -> t.Any: + return getattr(self._input, x) + + def _echo(self, rv: bytes) -> bytes: + if not self._paused: + self._output.write(rv) + + return rv + + def read(self, n: int = -1) -> bytes: + return self._echo(self._input.read(n)) + + def read1(self, n: int = -1) -> bytes: + return self._echo(self._input.read1(n)) # type: ignore + + def readline(self, n: int = -1) -> bytes: + return self._echo(self._input.readline(n)) + + def readlines(self) -> list[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> cabc.Iterator[bytes]: + return iter(self._echo(x) for x in self._input) + + def __repr__(self) -> str: + return repr(self._input) + + +@contextlib.contextmanager +def _pause_echo(stream: EchoingStdin | None) -> cabc.Generator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +class _FDCapture: + """Redirect a file descriptor to a temporary file for capture. + + Saves the current target of *targetfd* via :func:`os.dup`, then + redirects it to a temporary file via :func:`os.dup2`. On + :meth:`stop`, restores the original ``fd`` and returns the captured + bytes. Inspired by Pytest's ``FDCapture``. + + .. versionadded:: 8.4.0 + """ + + _targetfd: int + saved_fd: int + _tmpfile: t.BinaryIO | None + + def __init__(self, targetfd: int) -> None: + self._targetfd = targetfd + self.saved_fd = -1 + self._tmpfile = None + + def start(self) -> None: + self.saved_fd = os.dup(self._targetfd) + self._tmpfile = tempfile.TemporaryFile(buffering=0) + os.dup2(self._tmpfile.fileno(), self._targetfd) + + def stop(self) -> bytes: + assert self._tmpfile is not None, "_FDCapture.start() was not called" + os.dup2(self.saved_fd, self._targetfd) + os.close(self.saved_fd) + self.saved_fd = -1 + self._tmpfile.seek(0) + data = self._tmpfile.read() + self._tmpfile.close() + self._tmpfile = None + return data + + +class BytesIOCopy(io.BytesIO): + """Patch ``io.BytesIO`` to let the written stream be copied to another. + + .. versionadded:: 8.2 + """ + + copy_to: io.BytesIO + + def __init__(self, copy_to: io.BytesIO) -> None: + super().__init__() + self.copy_to = copy_to + + def flush(self) -> None: + super().flush() + self.copy_to.flush() + + def write(self, b: ReadableBuffer) -> int: + self.copy_to.write(b) + return super().write(b) + + +class StreamMixer: + """Mixes `` and `` streams. + + The result is available in the ``output`` attribute. + + .. versionadded:: 8.2 + """ + + output: io.BytesIO + stdout: BytesIOCopy + stderr: BytesIOCopy + + def __init__(self) -> None: + self.output = io.BytesIO() + self.stdout = BytesIOCopy(copy_to=self.output) + self.stderr = BytesIOCopy(copy_to=self.output) + + +class _NamedTextIOWrapper(io.TextIOWrapper): + """A :class:`~io.TextIOWrapper` with custom ``name`` and ``mode`` + that does not close its underlying buffer. + + When ``CliRunner`` runs in ``fd`` mode, ``_original_fd`` is patched to + point at the saved (pre-redirection) ``fd``, so C-level consumers that call + :meth:`fileno` (like ``faulthandler`` or ``subprocess``) keep working. In + the default ``sys`` mode ``_original_fd`` stays at ``-1`` and + :meth:`fileno` raises :exc:`io.UnsupportedOperation`, matching the + pre-``8.3.3`` behavior. + """ + + _name: str + _mode: str + _original_fd: int + + def __init__( + self, + buffer: t.BinaryIO, + name: str, + mode: str, + **kwargs: t.Any, + ) -> None: + super().__init__(buffer, **kwargs) + self._name = name + self._mode = mode + self._original_fd = -1 + + def close(self) -> None: + """The buffer this object contains belongs to some other object, + so prevent the default ``__del__`` implementation from closing + that buffer. + + .. versionadded:: 8.3.2 + """ + + def fileno(self) -> int: + """Return the file descriptor of the saved original stream when + ``CliRunner`` runs in ``fd`` mode. Otherwise delegate to + :class:`~io.TextIOWrapper`, which raises + :exc:`io.UnsupportedOperation` for a ``BytesIO``-backed buffer. + """ + if self._original_fd >= 0: + return self._original_fd + return super().fileno() + + @property + def name(self) -> str: + return self._name + + @property + def mode(self) -> str: + return self._mode + + +def make_input_stream( + input: str | bytes | t.IO[t.Any] | None, charset: str +) -> t.BinaryIO: + # Is already an input stream. + if hasattr(input, "read"): + rv = _find_binary_reader(t.cast("t.IO[t.Any]", input)) + + if rv is not None: + return rv + + raise TypeError("Could not find binary reader for input stream.") + + if input is None: + input = b"" + elif isinstance(input, str): + input = input.encode(charset) + + return io.BytesIO(input) + + +class Result: + """Holds the captured result of an invoked CLI script. + + :param runner: The runner that created the result + :param stdout_bytes: The standard output as bytes. + :param stderr_bytes: The standard error as bytes. + :param output_bytes: A mix of ``stdout_bytes`` and ``stderr_bytes``, as the + user would see it in its terminal. + :param return_value: The value returned from the invoked command. + :param exit_code: The exit code as integer. + :param exception: The exception that happened if one did. + :param exc_info: Exception information (exception type, exception instance, + traceback type). + + .. versionchanged:: 8.2 + ``stderr_bytes`` no longer optional, ``output_bytes`` introduced and + ``mix_stderr`` has been removed. + + .. versionadded:: 8.0 + Added ``return_value``. + """ + + runner: CliRunner + stdout_bytes: bytes + stderr_bytes: bytes + output_bytes: bytes + return_value: t.Any + exit_code: int + exception: BaseException | None + exc_info: ExceptionInfo | None + + def __init__( + self, + runner: CliRunner, + stdout_bytes: bytes, + stderr_bytes: bytes, + output_bytes: bytes, + return_value: t.Any, + exit_code: int, + exception: BaseException | None, + exc_info: ExceptionInfo | None = None, + ) -> None: + self.runner = runner + self.stdout_bytes = stdout_bytes + self.stderr_bytes = stderr_bytes + self.output_bytes = output_bytes + self.return_value = return_value + self.exit_code = exit_code + self.exception = exception + self.exc_info = exc_info + + @property + def output(self) -> str: + """The terminal output as unicode string, as the user would see it. + + .. versionchanged:: 8.2 + No longer a proxy for ``self.stdout``. Now has its own independent stream + that is mixing `` and ``, in the order they were written. + """ + return self.output_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stdout(self) -> str: + """The standard output as unicode string.""" + return self.stdout_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stderr(self) -> str: + """The standard error as unicode string. + + .. versionchanged:: 8.2 + No longer raise an exception, always returns the `` string. + """ + return self.stderr_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + def __repr__(self) -> str: + exc_str = repr(self.exception) if self.exception else "okay" + return f"<{type(self).__name__} {exc_str}>" + + +class CliRunner: + """The CLI runner provides functionality to invoke a Click command line + script for unittesting purposes in a isolated environment. This only + works in single-threaded systems without any concurrency as it changes the + global interpreter state. + + :param charset: the character set for the input and output data. + :param env: a dictionary with environment variables for overriding. + :param echo_stdin: if this is set to `True`, then reading from `` writes + to ``. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param catch_exceptions: Whether to catch any exceptions other than + ``SystemExit`` when running :meth:`~CliRunner.invoke`. + :param capture: Selects the output capture strategy. ``sys`` (default) + captures Python-level writes only and leaves + :meth:`sys.stdout.fileno` raising :exc:`io.UnsupportedOperation`, so + user code that calls :func:`os.dup2` on ``sys.stdout.fileno()`` cannot + clobber the host runner's stdout. ``fd`` redirects file descriptors + ``1`` and ``2`` via :func:`os.dup2` to a temporary file, also catching + output from stale stream references, C extensions, and subprocesses. + ``fd`` is not supported on Windows. + + .. versionchanged:: 8.4.0 + Added the ``capture`` parameter. The default ``sys`` mode no longer + exposes the original fd through :meth:`fileno`, reverting the change + introduced in ``8.3.3`` that broke Pytest's ``fd``-level capture + teardown. Use ``capture="fd"`` to restore that behavior with proper + isolation. :issue:`3384` + + .. versionchanged:: 8.2 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 8.2 + ``mix_stderr`` parameter has been removed. + """ + + charset: str + env: cabc.Mapping[str, str | None] + echo_stdin: bool + catch_exceptions: bool + capture: CaptureMode + + def __init__( + self, + charset: str = "utf-8", + env: cabc.Mapping[str, str | None] | None = None, + echo_stdin: bool = False, + catch_exceptions: bool = True, + capture: CaptureMode = "sys", + ) -> None: + if capture not in {"sys", "fd"}: + raise ValueError( + f"capture={capture!r} is not valid. Choose from 'sys' or 'fd'." + ) + if capture == "fd" and sys.platform == "win32": + raise ValueError( + f"capture={capture!r} is not supported on Windows. Use 'sys'." + ) + self.charset = charset + self.env = env or {} + self.echo_stdin = echo_stdin + self.catch_exceptions = catch_exceptions + self.capture = capture + + def get_default_prog_name(self, cli: Command) -> str: + """Given a command object it will return the default program name + for it. The default is the `name` attribute or ``"root"`` if not + set. + """ + return cli.name or "root" + + def make_env( + self, overrides: cabc.Mapping[str, str | None] | None = None + ) -> cabc.Mapping[str, str | None]: + """Returns the environment overrides for invoking a script.""" + rv = dict(self.env) + if overrides: + rv.update(overrides) + return rv + + @contextlib.contextmanager + def isolation( + self, + input: str | bytes | t.IO[t.Any] | None = None, + env: cabc.Mapping[str, str | None] | None = None, + color: bool = False, + ) -> cabc.Generator[tuple[io.BytesIO, io.BytesIO, io.BytesIO]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up `` with the given input data + and `os.environ` with the overrides from the given dictionary. + This also rebinds some internals in Click to be mocked (like the + prompt functionality). + + This is automatically done in the :meth:`invoke` method. + + :param input: the input stream to put into `sys.stdin`. + :param env: the environment overrides as dictionary. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionadded:: 8.2 + An additional output stream is returned, which is a mix of + `` and `` streams. + + .. versionchanged:: 8.2 + Always returns the `` stream. + + .. versionchanged:: 8.0 + `` is opened with ``errors="backslashreplace"`` + instead of the default ``"strict"``. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + """ + bytes_input = make_input_stream(input, self.charset) + echo_input = None + + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + old_forced_width = formatting.FORCED_WIDTH + formatting.FORCED_WIDTH = 80 + + env = self.make_env(env) + + stream_mixer = StreamMixer() + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, stream_mixer.stdout) + ) + + sys.stdin = text_input = _NamedTextIOWrapper( + bytes_input, encoding=self.charset, name="", mode="r" + ) + + if self.echo_stdin: + # Force unbuffered reads, otherwise TextIOWrapper reads a + # large chunk which is echoed early. + text_input._CHUNK_SIZE = 1 # type: ignore + + sys.stdout = _NamedTextIOWrapper( + stream_mixer.stdout, + encoding=self.charset, + name="", + mode="w", + ) + + sys.stderr = _NamedTextIOWrapper( + stream_mixer.stderr, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: str | None = None) -> str: + sys.stdout.write(prompt or "") + try: + val = next(text_input).rstrip("\r\n") + except StopIteration as e: + raise EOFError() from e + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: str | None = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + try: + return next(text_input).rstrip("\r\n") + except StopIteration as e: + raise EOFError() from e + + @_pause_echo(echo_input) # type: ignore + def _getchar(echo: bool) -> str: + char = sys.stdin.read(1) + + if echo: + sys.stdout.write(char) + + sys.stdout.flush() + return char + + default_color = color + + def should_strip_ansi( + stream: t.IO[t.Any] | None = None, color: bool | None = None + ) -> bool: + if color is None: + return not default_color + return not color + + old_visible_prompt_func = termui.visible_prompt_func + old_hidden_prompt_func = termui.hidden_prompt_func + old__getchar_func = termui._getchar + old_should_strip_ansi = utils.should_strip_ansi # type: ignore + old__compat_should_strip_ansi = _compat.should_strip_ansi + old_pdb_init = pdb.Pdb.__init__ + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + _compat.should_strip_ansi = should_strip_ansi + + def _patched_pdb_init( + self: pdb.Pdb, + completekey: str = "tab", + stdin: t.IO[str] | None = None, + stdout: t.IO[str] | None = None, + **kwargs: t.Any, + ) -> None: + """Default ``pdb.Pdb`` to real terminal streams during + ``CliRunner`` isolation. + + Without this patch, ``pdb.Pdb.__init__`` inherits from + ``cmd.Cmd`` which falls back to ``sys.stdin``/``sys.stdout`` + when no explicit streams are provided. During isolation + those are ``BytesIO``-backed wrappers, so the debugger + reads from an empty buffer and writes to captured output, + making interactive debugging impossible. + + By defaulting to ``sys.__stdin__``/``sys.__stdout__`` (the + original terminal streams Python preserves regardless of + redirection), debuggers can interact with the user while + ``click.echo`` output is still captured normally. + + This covers ``pdb.set_trace()``, ``breakpoint()``, + ``pdb.post_mortem()``, and debuggers that subclass + ``pdb.Pdb`` (ipdb, pdbpp). Explicit ``stdin``/``stdout`` + arguments are honored and not overridden. Debuggers that + do not subclass ``pdb.Pdb`` (pudb, debugpy) are not + covered. + """ + if stdin is None: + stdin = sys.__stdin__ + if stdout is None: + stdout = sys.__stdout__ + old_pdb_init( + self, completekey=completekey, stdin=stdin, stdout=stdout, **kwargs + ) + + pdb.Pdb.__init__ = _patched_pdb_init # type: ignore[assignment] + + old_env = {} + try: + for key, value in env.items(): + old_env[key] = os.environ.get(key) + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + yield (stream_mixer.stdout, stream_mixer.stderr, stream_mixer.output) + finally: + for key, value in old_env.items(): + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + sys.stdout = old_stdout + sys.stderr = old_stderr + sys.stdin = old_stdin + termui.visible_prompt_func = old_visible_prompt_func + termui.hidden_prompt_func = old_hidden_prompt_func + termui._getchar = old__getchar_func + utils.should_strip_ansi = old_should_strip_ansi # type: ignore + _compat.should_strip_ansi = old__compat_should_strip_ansi + formatting.FORCED_WIDTH = old_forced_width + pdb.Pdb.__init__ = old_pdb_init # type: ignore[method-assign] + + def invoke( + self, + cli: Command, + args: str | cabc.Sequence[str] | None = None, + input: str | bytes | t.IO[t.Any] | None = None, + env: cabc.Mapping[str, str | None] | None = None, + catch_exceptions: bool | None = None, + color: bool = False, + **extra: t.Any, + ) -> Result: + """Invokes a command in an isolated environment. The arguments are + forwarded directly to the command line script, the `extra` keyword + arguments are passed to the :meth:`~clickpkg.Command.main` function of + the command. + + This returns a :class:`Result` object. + + :param cli: the command to invoke + :param args: the arguments to invoke. It may be given as an iterable + or a string. When given as string it will be interpreted + as a Unix shell command. More details at + :func:`shlex.split`. + :param input: the input data for `sys.stdin`. + :param env: the environment overrides. + :param catch_exceptions: Whether to catch any other exceptions than + ``SystemExit``. If :data:`None`, the value + from :class:`CliRunner` is used. + :param extra: the keyword arguments to pass to :meth:`main`. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionadded:: 8.2 + The result object has the ``output_bytes`` attribute with + the mix of ``stdout_bytes`` and ``stderr_bytes``, as the user would + see it in its terminal. + + .. versionchanged:: 8.2 + The result object always returns the ``stderr_bytes`` stream. + + .. versionchanged:: 8.0 + The result object has the ``return_value`` attribute with + the value returned from the invoked command. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionchanged:: 3.0 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 3.0 + The result object has the ``exc_info`` attribute with the + traceback if available. + """ + exc_info = None + if catch_exceptions is None: + catch_exceptions = self.catch_exceptions + + # Set up fd capture before isolation replaces sys.stdout and sys.stderr. + cap_out: _FDCapture | None = None + cap_err: _FDCapture | None = None + + if self.capture == "fd": + cap_out = _FDCapture(1) + cap_err = _FDCapture(2) + try: + cap_out.start() + cap_err.start() + except OSError: + cap_out = cap_err = None + + with self.isolation(input=input, env=env, color=color) as outstreams: + # Point the captured streams' fileno() at the saved (original) + # fd so that C-level consumers like faulthandler keep working + # while fd 1/2 are redirected to the capture tmpfile. + if cap_out is not None and cap_err is not None: + sys.stdout._original_fd = cap_out.saved_fd # type: ignore[union-attr] + sys.stderr._original_fd = cap_err.saved_fd # type: ignore[union-attr] + + return_value = None + exception: BaseException | None = None + exit_code = 0 + + if isinstance(args, str): + args = shlex.split(args) + + try: + prog_name = extra.pop("prog_name") + except KeyError: + prog_name = self.get_default_prog_name(cli) + + try: + return_value = cli.main(args=args or (), prog_name=prog_name, **extra) + except SystemExit as e: + exc_info = sys.exc_info() + e_code = t.cast("int | t.Any | None", e.code) + + if e_code is None: + e_code = 0 + + if e_code != 0: + exception = e + + if not isinstance(e_code, int): + sys.stdout.write(str(e_code)) + sys.stdout.write("\n") + e_code = 1 + + exit_code = e_code + + except Exception as e: + if not catch_exceptions: + raise + exception = e + exit_code = 1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + sys.stderr.flush() + + # Stop fd capture and merge the captured bytes into + # the stdout/stderr BytesIO streams. BytesIOCopy mirrors + # those writes into outstreams[2] automatically. + if cap_out is not None and cap_err is not None: + fd_out = cap_out.stop() + fd_err = cap_err.stop() + if fd_out: + outstreams[0].write(fd_out) + if fd_err: + outstreams[1].write(fd_err) + + stdout = outstreams[0].getvalue() + stderr = outstreams[1].getvalue() + output = outstreams[2].getvalue() + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + output_bytes=output, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: str | os.PathLike[str] | None = None + ) -> cabc.Generator[str]: + """A context manager that creates a temporary directory and + changes the current working directory to it. This isolates tests + that affect the contents of the CWD to prevent them from + interfering with each other. + + .. warning:: + This helper predates Python 3 and modern pytest, and is not + thread-safe: it relies on :func:`os.chdir`, which mutates + process-global state, and :meth:`invoke` swaps the + process-global standard streams too. Parallelize tests with + processes (``pytest-xdist``), not threads. Locking the + runner (:pr:`3511`, :pr:`3520`, :pr:`3530`) and a + ``set_filesystem()`` API (:issue:`3123`) were declined: + neither removes the global-state mutation. See :issue:`3700` + and :issue:`3501`. + + :param temp_dir: Create the temporary directory under this + directory. If given, the created directory is not removed + when exiting. + + .. deprecated:: 8.5.0 + Will be removed in Click 9.0. Use + :class:`tempfile.TemporaryDirectory` or pytest's + ``tmp_path`` fixture with absolute paths instead. + + .. versionchanged:: 8.0 + Added the ``temp_dir`` parameter. + """ + import warnings + + warnings.warn( + "'isolated_filesystem' is deprecated and will be removed in Click" + " 9.0. Use 'tempfile.TemporaryDirectory' or pytest's 'tmp_path'" + " fixture with absolute paths instead.", + DeprecationWarning, + stacklevel=3, + ) + + cwd = os.getcwd() + dt = tempfile.mkdtemp(dir=temp_dir) + os.chdir(dt) + + try: + yield dt + finally: + os.chdir(cwd) + + if temp_dir is None: + import shutil + + try: + shutil.rmtree(dt) + except OSError: + pass diff --git a/venv/lib/python3.12/site-packages/click/types.py b/venv/lib/python3.12/site-packages/click/types.py new file mode 100644 index 0000000..0eb8462 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/types.py @@ -0,0 +1,1422 @@ +from __future__ import annotations + +import abc +import collections.abc as cabc +import enum +import os +import stat +import sys +import typing as t +import uuid +from datetime import datetime +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import _get_argv_encoding +from ._compat import open_stream +from .exceptions import BadParameter +from .utils import _LazyFile +from .utils import _safecall +from .utils import format_filename + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .core import Context + from .core import Parameter + from .shell_completion import CompletionItem + +_ValueT = t.TypeVar("_ValueT") +_ValueT_contra = t.TypeVar("_ValueT_contra", contravariant=True) +_ValueT_co = t.TypeVar("_ValueT_co", covariant=True) + +# The input type parameter of ParamType defaults to Any. TypeVar defaults +# (PEP 696) landed in typing on Python 3.13, so type checkers get the +# default from typing_extensions, which is never imported at runtime. On +# older Pythons the runtime TypeVar carries no default; +# ParamType.__class_getitem__ fills in the omitted parameter instead. +if t.TYPE_CHECKING: + _InputT_contra = te.TypeVar("_InputT_contra", contravariant=True, default=t.Any) +elif sys.version_info >= (3, 13): + _InputT_contra = t.TypeVar("_InputT_contra", contravariant=True, default=t.Any) +else: + _InputT_contra = t.TypeVar("_InputT_contra", contravariant=True) + +_FloatValueT = t.TypeVar("_FloatValueT", bound=float) +_FloatValueT_co = t.TypeVar("_FloatValueT_co", bound=float, covariant=True) + + +class ParamTypeInfoDict(t.TypedDict): + param_type: str + name: str + + +class ParamType(t.Generic[_ValueT_co, _InputT_contra], abc.ABC): + """Represents the type of a parameter. Validates and converts values + from the command line or Python into the correct type. + + To implement a custom type, subclass and implement at least the + following: + + - The :attr:`name` class attribute must be set. + - Calling an instance of the type with ``None`` must return + ``None``. This is already implemented by default. + - :meth:`convert` must convert string values to the correct type. + - :meth:`convert` must accept values that are already the correct + type. + - It must be able to convert a value if the ``ctx`` and ``param`` + arguments are ``None``. This can occur when converting prompt + input. + + .. versionchanged:: 8.4.0 + Now a generic abstract base class. Parameterize with the + converted value type (``ParamType[int]`` for an integer-returning + type) so that :meth:`convert` and downstream consumers carry the + narrowed return type. + + .. versionchanged:: 8.5.0 + Accepts a second optional type parameter for the input value type + that :meth:`convert` accepts (``ParamType[int, str]`` for a type + converting strings to integers), defaulting to ``Any``. + """ + + is_composite: t.ClassVar[bool] = False + arity: int = 1 # read-only + + #: the descriptive name of this type + name: str + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter: t.ClassVar[str | None] = None + + if sys.version_info < (3, 13): + # ``_InputT_contra`` carries its ``Any`` default only for type + # checkers: ``TypeVar(default=...)`` (PEP 696) is unavailable at + # runtime before Python 3.13. Fill in the omitted input type + # parameter by hand so ``ParamType[int]`` keeps working. + def __class_getitem__(cls, params: t.Any) -> t.Any: + if cls is ParamType: + if not isinstance(params, tuple): + params = (params,) + if len(params) == 1: + params = (*params, t.Any) + # Checkers cannot see Generic.__class_getitem__ through super(). + return super().__class_getitem__(params) # type: ignore[misc] + + def to_info_dict(self) -> ParamTypeInfoDict: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + + # Custom subclasses might not remember to set a name. + if hasattr(self, "name"): + name = self.name + else: + name = param_type + + return {"param_type": param_type, "name": name} + + @t.overload + def __call__( + self, + value: None, + param: Parameter | None = None, + ctx: Context | None = None, + ) -> None: ... + + @t.overload + def __call__( + self, + value: _InputT_contra, + param: Parameter | None = None, + ctx: Context | None = None, + ) -> _ValueT_co: ... + + def __call__( + self, + value: _InputT_contra | None, + param: Parameter | None = None, + ctx: Context | None = None, + ) -> _ValueT_co | None: + if value is not None: + return self.convert(value, param, ctx) + return None + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: Parameter, ctx: Context | None) -> str | None: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: _InputT_contra, param: Parameter | None, ctx: Context | None + ) -> _ValueT_co: + """Convert the value to the correct type. This is not called if + the value is ``None`` (the missing value). + + This must accept string values from the command line, as well as + values that are already the correct type. It may also convert + other compatible types. + + The ``param`` and ``ctx`` arguments may be ``None`` in certain + situations, such as when converting prompt input. + + If the value cannot be converted, call :meth:`fail` with a + descriptive message. + + :param value: The value to convert. + :param param: The parameter that is using this type to convert + its value. May be ``None``. + :param ctx: The current context that arrived at this value. May + be ``None``. + """ + # The default returns the value as-is so subclasses that only customize + # metadata are not forced to redeclare ``convert``. + return t.cast("_ValueT_co", value) + + def split_envvar_value(self, rv: str) -> cabc.Sequence[str]: + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or "").split(self.envvar_list_splitter) + + def fail( + self, + message: str, + param: Parameter | None = None, + ctx: Context | None = None, + ) -> t.NoReturn: + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Return a list of + :class:`~click.shell_completion.CompletionItem` objects for the + incomplete value. Most types do not provide completions, but + some do, and this allows custom types to provide custom + completions as well. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + return [] + + +class CompositeParamType(ParamType[_ValueT_co]): + is_composite: t.ClassVar[bool] = True + + @property + @abc.abstractmethod + def arity(self) -> int: ... # type: ignore[override] + + +if t.TYPE_CHECKING: + # on Python 3.10 this will raise a TypeError + + class FuncParamTypeInfoDict( + ParamTypeInfoDict, + t.Generic[_ValueT_contra, _ValueT_co], + ): + func: t.Callable[[_ValueT_contra], _ValueT_co] +else: + + class FuncParamTypeInfoDict(ParamTypeInfoDict): + func: t.Callable[[t.Any], t.Any] + + +class FuncParamType(ParamType[_ValueT_co], t.Generic[_ValueT_contra, _ValueT_co]): + name: str + func: t.Callable[[_ValueT_contra], _ValueT_co] + + def __init__(self, func: t.Callable[[_ValueT_contra], _ValueT_co]) -> None: + self.name = func.__name__ + self.func = func + + def to_info_dict(self) -> FuncParamTypeInfoDict[_ValueT_contra, _ValueT_co]: + return {"func": self.func, **super().to_info_dict()} + + def convert( + self, value: _ValueT_contra, param: Parameter | None, ctx: Context | None + ) -> _ValueT_co: + try: + return self.func(value) + except ValueError as exc: + message = str(exc) + + if not message: + try: + message = str(value) + except UnicodeError: + message = t.cast("bytes", value).decode("utf-8", "replace") + + self.fail(message, param, ctx) + + +class UnprocessedParamType(ParamType[t.Any]): + name = "text" + + def convert( + self, value: _ValueT, param: Parameter | None, ctx: Context | None + ) -> _ValueT: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType[str]): + name = "text" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> str: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + return value.decode(enc) + except UnicodeError: + fs_enc = sys.getfilesystemencoding() + if fs_enc != enc: + try: + return value.decode(fs_enc) + except UnicodeError: + return value.decode("utf-8", "replace") + else: + return value.decode("utf-8", "replace") + return str(value) + + def __repr__(self) -> str: + return "STRING" + + +if t.TYPE_CHECKING: + # on Python 3.10 this will raise a TypeError + + class ChoiceInfoDict(ParamTypeInfoDict, t.Generic[_ValueT_co]): + choices: tuple[_ValueT_co, ...] + case_sensitive: bool +else: + + class ChoiceInfoDict(ParamTypeInfoDict): + choices: tuple[t.Any, ...] + case_sensitive: bool + + +class Choice(ParamType[_ValueT_co], t.Generic[_ValueT_co]): + """The choice type allows a value to be checked against a fixed set + of supported values. + + You may pass any iterable value which will be converted to a tuple + and thus will only be iterated once. + + The resulting value will always be one of the originally passed choices. + See :meth:`normalize_choice` for more info on the mapping of strings + to choices. See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + + .. versionchanged:: 8.4.0 + Now generic in the choice value type. Parameterize with the type of + the choice values (``Choice[HashType]`` for an enum, ``Choice[str]`` + for plain strings) to enable type-checked consumers. + + .. versionchanged:: 8.2.0 + Non-``str`` ``choices`` are now supported. It can additionally be any + iterable. Before you were not recommended to pass anything but a list or + tuple. + + .. versionadded:: 8.2.0 + Choice normalization can be overridden via :meth:`normalize_choice`. + """ + + name: str = "choice" + + choices: tuple[_ValueT_co, ...] + case_sensitive: bool + + def __init__( + self, choices: cabc.Iterable[_ValueT_co], case_sensitive: bool = True + ) -> None: + self.choices = tuple(choices) + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> ChoiceInfoDict[_ValueT_co]: + return { + "choices": self.choices, + "case_sensitive": self.case_sensitive, + **super().to_info_dict(), + } + + def _normalized_mapping( + self, ctx: Context | None = None + ) -> cabc.Mapping[_ValueT_co, str]: + """ + Returns mapping where keys are the original choices and the values are + the normalized values that are accepted via the command line. + + This is a simple wrapper around :meth:`normalize_choice`, use that + instead which is supported. + """ + return { + choice: self.normalize_choice( + choice=choice, + ctx=ctx, + ) + for choice in self.choices + } + + def normalize_choice(self, choice: object, ctx: Context | None) -> str: + """ + Normalize a choice value, used to map a passed string to a choice. + Each choice must have a unique normalized value. + + By default uses :meth:`Context.token_normalize_func` and if not case + sensitive, convert it to a casefolded value. + + .. versionadded:: 8.2.0 + """ + normed_value = choice.name if isinstance(choice, enum.Enum) else str(choice) + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(normed_value) + + if not self.case_sensitive: + normed_value = normed_value.casefold() + + return normed_value + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + if param.param_type_name == "option" and not param.show_choices: # type: ignore[attr-defined] + choice_metavars = [ + convert_type(type(choice)).name.upper() for choice in self.choices + ] + choices_str = "|".join([*dict.fromkeys(choice_metavars)]) + else: + choices_str = "|".join( + [str(i) for i in self._normalized_mapping(ctx=ctx).values()] + ) + + # Use curly braces to indicate a required argument. + if param.required and param.param_type_name == "argument": + return f"{{{choices_str}}}" + + # Use square braces to indicate an option or optional argument. + return f"[{choices_str}]" + + def get_missing_message(self, param: Parameter, ctx: Context | None) -> str: + """ + Message shown when no choice is passed. + + .. versionchanged:: 8.2.0 Added ``ctx`` argument. + """ + return _("Choose from:\n\t{choices}").format( + choices=",\n\t".join(self._normalized_mapping(ctx=ctx).values()) + ) + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> _ValueT_co: + """ + For a given value from the parser, normalize it and find its + matching normalized value in the list of choices. Then return the + matched "original" choice. + """ + normed_value = self.normalize_choice(choice=value, ctx=ctx) + normalized_mapping = self._normalized_mapping(ctx=ctx) + + try: + return next( + original + for original, normalized in normalized_mapping.items() + if normalized == normed_value + ) + except StopIteration: + self.fail( + self.get_invalid_choice_message(value=value, ctx=ctx), + param=param, + ctx=ctx, + ) + + def get_invalid_choice_message(self, value: t.Any, ctx: Context | None) -> str: + """Get the error message when the given choice is invalid. + + :param value: The invalid value. + + .. versionadded:: 8.2 + """ + choices_str = ", ".join(map(repr, self._normalized_mapping(ctx=ctx).values())) + return ngettext( + "{value!r} is not {choice}.", + "{value!r} is not one of {choices}.", + len(self.choices), + ).format(value=value, choice=choices_str, choices=choices_str) + + def __repr__(self) -> str: + return _("Choice({choices})").format(choices=list(self.choices)) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Complete choices that start with the incomplete value. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + str_choices = [self.normalize_choice(choice, ctx) for choice in self.choices] + if self.case_sensitive: + matched = (c for c in str_choices if c.startswith(incomplete)) + else: + incomplete = incomplete.lower() + matched = (c for c in str_choices if c.lower().startswith(incomplete)) + + return [CompletionItem(c) for c in matched] + + +class DateTimeInfoDict(ParamTypeInfoDict): + formats: cabc.Sequence[str] + + +class DateTime(ParamType[datetime]): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + + name = "datetime" + + formats: cabc.Sequence[str] + + def __init__(self, formats: cabc.Sequence[str] | None = None): + self.formats = formats or [ + "%Y-%m-%d", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%d %H:%M:%S", + ] + + def to_info_dict(self) -> DateTimeInfoDict: + return {"formats": self.formats, **super().to_info_dict()} + + def get_metavar(self, param: Parameter, ctx: Context) -> str: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> datetime | None: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> datetime: + if isinstance(value, datetime): + return value + + for format in self.formats: + converted = self._try_to_convert_date(value, format) + + if converted is not None: + return converted + + formats_str = ", ".join(map(repr, self.formats)) + self.fail( + ngettext( + "{value!r} does not match the format {format}.", + "{value!r} does not match the formats {formats}.", + len(self.formats), + ).format(value=value, format=formats_str, formats=formats_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return "DateTime" + + +class _NumberParamTypeBase( + ParamType[_ValueT_co], t.Generic[_ValueT_contra, _ValueT_co] +): + _number_class: t.Callable[[_ValueT_contra], _ValueT_co] + + def convert( + self, value: _ValueT_contra, param: Parameter | None, ctx: Context | None + ) -> _ValueT_co: + try: + return self._number_class(value) + except ValueError: + self.fail( + _("{value!r} is not a valid {number_type}.").format( + value=value, number_type=self.name + ), + param, + ctx, + ) + + +if t.TYPE_CHECKING: + # on Python 3.10 this will raise a TypeError + + class NumberRangeInfoDict(ParamTypeInfoDict, t.Generic[_FloatValueT_co]): + min: _FloatValueT_co | None + max: _FloatValueT_co | None + min_open: bool + max_open: bool + clamp: bool +else: + + class NumberRangeInfoDict(ParamTypeInfoDict): + min: t.Any | None + max: t.Any | None + min_open: bool + max_open: bool + clamp: bool + + +class _NumberRangeBase( + _NumberParamTypeBase[_ValueT_contra, _FloatValueT_co], + t.Generic[_ValueT_contra, _FloatValueT_co], +): + min: _FloatValueT_co | None + max: _FloatValueT_co | None + min_open: bool + max_open: bool + clamp: bool + + def __init__( + self, + min: _FloatValueT_co | None = None, + max: _FloatValueT_co | None = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + self.min = min + self.max = max + self.min_open = min_open + self.max_open = max_open + self.clamp = clamp + + def to_info_dict(self) -> NumberRangeInfoDict[_FloatValueT_co]: + return { + "min": self.min, + "max": self.max, + "min_open": self.min_open, + "max_open": self.max_open, + "clamp": self.clamp, + **super().to_info_dict(), + } + + def convert( + self, value: _ValueT_contra, param: Parameter | None, ctx: Context | None + ) -> _FloatValueT_co: + import operator + + rv = super().convert(value, param, ctx) + min = self.min + max = self.max + lt_min: bool = min is not None and ( + operator.le if self.min_open else operator.lt + )(rv, min) + gt_max: bool = max is not None and ( + operator.ge if self.max_open else operator.gt + )(rv, max) + + if self.clamp: + if min is not None and lt_min: + return self._clamp(min, 1, self.min_open) + + if max is not None and gt_max: + return self._clamp(max, -1, self.max_open) + + if lt_min or gt_max: + self.fail( + _("{value} is not in the range {range}.").format( + value=rv, range=self._describe_range() + ), + param, + ctx, + ) + + return rv + + @abc.abstractmethod + def _clamp( + # Covariant type variables cannot be used in input positions, so we use a + # separate method-scoped type variable instead. + self: _NumberRangeBase[t.Any, _FloatValueT], + bound: _FloatValueT, + dir: t.Literal[1, -1], + open: bool, + ) -> _FloatValueT: + """Find the valid value to clamp to bound in the given + direction. + + :param bound: The boundary value. + :param dir: 1 or -1 indicating the direction to move. + :param open: If true, the range does not include the bound. + """ + ... + + def _describe_range(self) -> str: + """Describe the range for use in help text.""" + if self.min is None: + op = "<" if self.max_open else "<=" + return f"x{op}{self.max}" + + if self.max is None: + op = ">" if self.min_open else ">=" + return f"x{op}{self.min}" + + lop = "<" if self.min_open else "<=" + rop = "<" if self.max_open else "<=" + return f"{self.min}{lop}x{rop}{self.max}" + + def __repr__(self) -> str: + clamp = " clamped" if self.clamp else "" + return f"<{type(self).__name__} {self._describe_range()}{clamp}>" + + +class IntParamType(_NumberParamTypeBase[t.SupportsInt | t.SupportsIndex, int]): + name = "integer" + _number_class = int + + def __repr__(self) -> str: + return "INT" + + +class IntRange(_NumberRangeBase[int, int], IntParamType): + """Restrict an :data:`click.INT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "integer range" + + def _clamp(self, bound: int, dir: t.Literal[1, -1], open: bool) -> int: + if not open: + return bound + + return bound + dir + + +class FloatParamType(_NumberParamTypeBase[t.SupportsFloat | t.SupportsIndex, float]): + name = "float" + _number_class = float + + def __repr__(self) -> str: + return "FLOAT" + + +class FloatRange(_NumberRangeBase[float, float], FloatParamType): + """Restrict a :data:`click.FLOAT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. This is not supported if either + boundary is marked ``open``. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "float range" + + def __init__( + self, + min: float | None = None, + max: float | None = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + super().__init__( + min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp + ) + + if (min_open or max_open) and clamp: + raise TypeError("Clamping is not supported for open bounds.") + + def _clamp(self, bound: float, dir: t.Literal[1, -1], open: bool) -> float: + if not open: + return bound + + # Could use math.nextafter here, but clamping an + # open float range doesn't seem to be particularly useful. It's + # left up to the user to write a callback to do it if needed. + raise RuntimeError("Clamping is not supported for open bounds.") + + +class BoolParamType(ParamType[bool]): + name = "boolean" + + bool_states: dict[str, bool] = { + "1": True, + "0": False, + "yes": True, + "no": False, + "true": True, + "false": False, + "on": True, + "off": False, + "t": True, + "f": False, + "y": True, + "n": False, + # Absence of value is considered False. + "": False, + } + """A mapping of string values to boolean states. + + Mapping is inspired by :py:attr:`configparser.ConfigParser.BOOLEAN_STATES` + and extends it. + + .. caution:: + String values are lower-cased, as the ``str_to_bool`` comparison function + below is case-insensitive. + + .. warning:: + The mapping is not exhaustive, and does not cover all possible boolean strings + representations. It will remains as it is to avoid endless bikeshedding. + + Future work my be considered to make this mapping user-configurable from public + API. + """ + + @staticmethod + def str_to_bool(value: str | bool) -> bool | None: + """Convert a string to a boolean value. + + If the value is already a boolean, it is returned as-is. If the value is a + string, it is stripped of whitespaces and lower-cased, then checked against + the known boolean states pre-defined in the `BoolParamType.bool_states` mapping + above. + + Returns `None` if the value does not match any known boolean state. + """ + if isinstance(value, bool): + return value + return BoolParamType.bool_states.get(value.strip().lower()) + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> bool: + normalized = self.str_to_bool(value) + if normalized is None: + self.fail( + _( + "{value!r} is not a valid boolean. Recognized values: {states}" + ).format(value=value, states=", ".join(sorted(self.bool_states))), + param, + ctx, + ) + return normalized + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType[uuid.UUID]): + name = "uuid" + + def convert( + self, value: uuid.UUID | str, param: Parameter | None, ctx: Context | None + ) -> uuid.UUID: + if isinstance(value, uuid.UUID): + return value + + value = value.strip() + + try: + return uuid.UUID(value) + except ValueError: + self.fail( + _("{value!r} is not a valid UUID.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "UUID" + + +class FileInfoDict(ParamTypeInfoDict): + mode: str + encoding: str | None + + +class File(ParamType[t.IO[t.Any]]): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Files can also be opened atomically in which case all writes go into a + separate file in the same folder and upon completion the file will + be moved over to the original location. This is useful if a file + regularly read by other users is modified. + + See :ref:`file-args` for more information. + + .. versionchanged:: 2.0 + Added the ``atomic`` parameter. + """ + + name = "filename" + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + mode: str + encoding: str | None + errors: str | None + lazy: bool | None + atomic: bool + + def __init__( + self, + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + lazy: bool | None = None, + atomic: bool = False, + ) -> None: + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def to_info_dict(self) -> FileInfoDict: + return { + "mode": self.mode, + "encoding": self.encoding, + **super().to_info_dict(), + } + + def resolve_lazy_flag(self, value: str | os.PathLike[str]) -> bool: + if self.lazy is not None: + return self.lazy + if os.fspath(value) == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, + value: str | os.PathLike[str] | t.IO[t.Any], + param: Parameter | None, + ctx: Context | None, + ) -> t.IO[t.Any]: + if _is_file_like(value): + return value + + try: + lazy = self.resolve_lazy_flag(value) + + if lazy: + lf = _LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + if ctx is not None: + ctx.call_on_close(lf.close_intelligently) + + return t.cast("t.IO[t.Any]", lf) + + f, should_close = open_stream( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + # If a context is provided, we automatically close the file + # at the end of the context execution (or flush out). If a + # context does not exist, it's the caller's responsibility to + # properly close the file. This for instance happens when the + # type is used with prompts. + if ctx is not None: + if should_close: + ctx.call_on_close(_safecall(f.close)) + else: + ctx.call_on_close(_safecall(f.flush)) + + return f + except OSError as e: + self.fail( + f"'{format_filename(value)}': {e.strerror}", + param, + ctx, + ) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Return a special completion marker that tells the completion + system to use the shell to provide file path completions. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + return [CompletionItem(incomplete, type="file")] + + +def _is_file_like(value: t.Any) -> te.TypeIs[t.IO[t.Any]]: + return hasattr(value, "read") or hasattr(value, "write") + + +class PathInfoDict(ParamTypeInfoDict): + exists: bool + file_okay: bool + dir_okay: bool + writable: bool + readable: bool + allow_dash: bool + + +class Path(ParamType[str | bytes | os.PathLike[str]]): + """The ``Path`` type is similar to the :class:`File` type, but + returns the filename instead of an open file. Various checks can be + enabled to validate the type of file and permissions. + + :param exists: The file or directory needs to exist for the value to + be valid. If this is not set to ``True``, and the file does not + exist, then all further checks are silently skipped. + :param file_okay: Allow a file as a value. + :param dir_okay: Allow a directory as a value. + :param readable: if true, a readable check is performed. + :param writable: if true, a writable check is performed. + :param executable: if true, an executable check is performed. + :param resolve_path: Make the value absolute and resolve any + symlinks. A ``~`` is not expanded, as this is supposed to be + done by the shell only. + :param allow_dash: Allow a single dash as a value, which indicates + a standard stream (but does not open it). Use + :func:`~click.open_file` to handle opening this value. + :param path_type: Convert the incoming path value to this type. If + ``None``, keep Python's default, which is ``str``. Useful to + convert to :class:`pathlib.Path`. + + .. versionchanged:: 8.1 + Added the ``executable`` parameter. + + .. versionchanged:: 8.0 + Allow passing ``path_type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + exists: bool + file_okay: bool + dir_okay: bool + readable: bool + writable: bool + executable: bool + resolve_path: bool + allow_dash: bool + name: str + + def __init__( + self, + exists: bool = False, + file_okay: bool = True, + dir_okay: bool = True, + writable: bool = False, + readable: bool = True, + resolve_path: bool = False, + allow_dash: bool = False, + path_type: type | None = None, + executable: bool = False, + ) -> None: + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.readable = readable + self.writable = writable + self.executable = executable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type: type | None = path_type + + if self.file_okay and not self.dir_okay: + self.name = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> PathInfoDict: + return { + "exists": self.exists, + "file_okay": self.file_okay, + "dir_okay": self.dir_okay, + "writable": self.writable, + "readable": self.readable, + "allow_dash": self.allow_dash, + **super().to_info_dict(), + } + + def coerce_path_result( + self, value: str | os.PathLike[str] + ) -> str | bytes | os.PathLike[str]: + if self.type is not None and not isinstance(value, self.type): + if self.type is str: + return os.fsdecode(value) + elif self.type is bytes: + return os.fsencode(value) + else: + return t.cast("os.PathLike[str]", self.type(value)) + + return value + + def convert( + self, + value: str | os.PathLike[str], + param: Parameter | None, + ctx: Context | None, + ) -> str | bytes | os.PathLike[str]: + rv = value + + dash = b"-" if isinstance(rv, bytes) else "-" + is_dash = self.file_okay and self.allow_dash and rv == dash + + if not is_dash: + if self.resolve_path: + rv = os.path.realpath(rv) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail( + _("{name} {filename!r} does not exist.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if not self.file_okay and stat.S_ISREG(st.st_mode): + self.fail( + _("{name} {filename!r} is a file.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + if not self.dir_okay and stat.S_ISDIR(st.st_mode): + self.fail( + _("{name} {filename!r} is a directory.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.readable and not os.access(rv, os.R_OK): + self.fail( + _("{name} {filename!r} is not readable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.writable and not os.access(rv, os.W_OK): + self.fail( + _("{name} {filename!r} is not writable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.executable and not os.access(value, os.X_OK): + self.fail( + _("{name} {filename!r} is not executable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + return self.coerce_path_result(rv) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Return a special completion marker that tells the completion + system to use the shell to provide path completions for only + directories or any paths. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + type = "dir" if self.dir_okay and not self.file_okay else "file" + return [CompletionItem(incomplete, type=type)] + + +class TupleInfoDict(ParamTypeInfoDict): + types: cabc.Sequence[ParamTypeInfoDict] + + +class Tuple(CompositeParamType[tuple[t.Any, ...]]): + """The default behavior of Click is to apply a type on a value directly. + This works well in most cases, except for when `nargs` is set to a fixed + count and different types should be used for different items. In this + case the :class:`Tuple` type can be used. This type can only be used + if `nargs` is set to a fixed number. + + For more information see :ref:`tuple-type`. + + This can be selected by using a Python tuple literal as a type. + + :param types: a list of types that should be used for the tuple items. + """ + + def __init__(self, types: cabc.Sequence[type[t.Any] | ParamType[t.Any]]) -> None: + self.types: cabc.Sequence[ParamType[t.Any]] = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> TupleInfoDict: + return { + "types": [ty.to_info_dict() for ty in self.types], + **super().to_info_dict(), + } + + @property + def name(self) -> str: # type: ignore[override] + return f"<{' '.join(ty.name for ty in self.types)}>" + + @property + def arity(self) -> int: # type: ignore[override] + return len(self.types) + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> tuple[t.Any, ...]: + len_type = len(self.types) + len_value = len(value) + + if len_value != len_type: + self.fail( + ngettext( + "{len_type} values are required, but {len_value} was given.", + "{len_type} values are required, but {len_value} were given.", + len_value, + ).format(len_type=len_type, len_value=len_value), + param=param, + ctx=ctx, + ) + + return tuple( + ty(x, param, ctx) for ty, x in zip(self.types, value, strict=False) + ) + + +def _guess_type( + ty: type[t.Any] | ParamType[t.Any] | None, + default: t.Any | None, +) -> type[t.Any] | tuple[type[t.Any], ...] | ParamType[t.Any] | None: + """Infer a type from *ty* or *default*. + + Returns *ty* unchanged when it is not ``None``. Otherwise inspects + *default* to produce a ``type``, a ``tuple`` of types (for tuple + defaults), or ``None``. + """ + if ty is not None: + return ty + + if default is None: + return None + + if not isinstance(default, (tuple, list)): + return type(default) + + # If the default is empty, return None so convert_type falls + # through to STRING. + if not default: + return None + + item = default[0] + + # A sequence of iterables needs to detect the inner types. + # Can't call convert_type recursively because that would + # incorrectly unwind the tuple to a single type. + if isinstance(item, (tuple, list)): + return tuple(map(type, item)) + + return type(item) + + +@t.overload +def convert_type(ty: None, default: None = None) -> StringParamType: ... +@t.overload +def convert_type( + ty: type | ParamType[t.Any], default: t.Any | None = None +) -> ParamType[t.Any]: ... +@t.overload +def convert_type( + ty: t.Any | None, default: t.Any | None = None +) -> ParamType[t.Any]: ... +def convert_type( + ty: t.Any | None = None, default: t.Any | None = None +) -> ParamType[t.Any]: + """Find the most appropriate :class:`ParamType` for the given Python + type. If the type isn't provided, it can be inferred from a default + value. + """ + guessed = _guess_type(ty, default) + is_guessed = guessed is not ty + + if isinstance(guessed, tuple): + return Tuple(guessed) + + if isinstance(guessed, ParamType): + return guessed + + if guessed is str or guessed is None: + return STRING + + if guessed is int: + return INT + + if guessed is float: + return FLOAT + + if guessed is bool: + return BOOL + + if is_guessed: + return STRING + + if __debug__: + try: + if issubclass(guessed, ParamType): + raise AssertionError( + f"Attempted to use an uninstantiated parameter type ({guessed})." + ) + except TypeError: + # guessed is an instance (correct), so issubclass fails. + pass + + return FuncParamType(guessed) + + +#: A dummy parameter type that just does nothing. From a user's +#: perspective this appears to just be the same as `STRING` but +#: internally no string conversion takes place if the input was bytes. +#: This is usually useful when working with file paths as they can +#: appear in bytes and unicode. +#: +#: For path related uses the :class:`Path` type is a better choice but +#: there are situations where an unprocessed type is useful which is why +#: it is provided. +#: +#: .. versionadded:: 4.0 +UNPROCESSED: t.Final[UnprocessedParamType] = UnprocessedParamType() + +#: A unicode string parameter type which is the implicit default. This +#: can also be selected by using ``str`` as type. +STRING: t.Final[StringParamType] = StringParamType() + +#: An integer parameter. This can also be selected by using ``int`` as +#: type. +INT: t.Final[IntParamType] = IntParamType() + +#: A floating point value parameter. This can also be selected by using +#: ``float`` as type. +FLOAT: t.Final[FloatParamType] = FloatParamType() + +#: A boolean parameter. This is the default for boolean flags. This can +#: also be selected by using ``bool`` as a type. +BOOL: t.Final[BoolParamType] = BoolParamType() + +#: A UUID parameter. +UUID: t.Final[UUIDParameterType] = UUIDParameterType() + + +class OptionHelpExtra(t.TypedDict, total=False): + envvars: tuple[str, ...] + default: str + range: str + required: str diff --git a/venv/lib/python3.12/site-packages/click/utils.py b/venv/lib/python3.12/site-packages/click/utils.py new file mode 100644 index 0000000..b529eb0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/utils.py @@ -0,0 +1,688 @@ +from __future__ import annotations + +import collections.abc as cabc +import os +import re +import sys +import typing as t +from functools import update_wrapper +from gettext import gettext as _ +from types import ModuleType +from types import TracebackType + +from ._compat import _default_text_stderr +from ._compat import _default_text_stdout +from ._compat import _find_binary_writer +from ._compat import binary_streams +from ._compat import open_stream +from ._compat import should_strip_ansi +from ._compat import strip_ansi +from ._compat import text_streams +from ._compat import WIN +from .globals import resolve_color_default + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def _safecall(func: t.Callable[P, R]) -> t.Callable[P, R | None]: + """Wraps a function so that it swallows exceptions. + + :meta private: + """ + + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R | None: + try: + return func(*args, **kwargs) + except Exception: + pass + return None + + return update_wrapper(wrapper, func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(sys.getfilesystemencoding()) + except UnicodeError: + return value.decode("utf-8", "replace") + return str(value) + + +def _make_default_short_help(help: str, max_length: int = 45) -> str: + """Returns a condensed version of help string. + + :meta private: + """ + # Consider only the first paragraph. + paragraph_end = help.find("\n\n") + + if paragraph_end != -1: + help = help[:paragraph_end] + + # Collapse newlines, tabs, and spaces. + words = help.split() + + if not words: + return "" + + # The first paragraph started with a "no rewrap" marker, ignore it. + if words[0] == "\b": + words = words[1:] + + total_length = 0 + last_index = len(words) - 1 + + for i, word in enumerate(words): + total_length += len(word) + (i > 0) + + if total_length > max_length: # too long, truncate + break + + if word[-1] == ".": # sentence end, truncate without "..." + return " ".join(words[: i + 1]) + + if total_length == max_length and i != last_index: + break # not at sentence end, truncate with "..." + else: + return " ".join(words) # no truncation needed + + # Account for the length of the suffix. + total_length += len("...") + + # remove words until the length is short enough + while i > 0: + total_length -= len(words[i]) + (i > 0) + + if total_length <= max_length: + break + + i -= 1 + + return " ".join(words[:i]) + "..." + + +class _LazyFile: + """A lazy file works like a regular file but it does not fully open + the file but it does perform some basic checks early to see if the + filename parameter does make sense. This is useful for safely opening + files for writing. + + :meta private: + """ + + name: str + mode: str + encoding: str | None + errors: str | None + atomic: bool + _f: t.IO[t.Any] | None + should_close: bool + + def __init__( + self, + filename: str | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + atomic: bool = False, + ) -> None: + self.name = os.fspath(filename) + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + + if self.name == "-": + self._f, self.should_close = open_stream(filename, mode, encoding, errors) + else: + if "r" in mode: + # Open and close the file in case we're opening it for + # reading so that we can catch at least some errors in + # some cases early. + open(filename, mode).close() + self._f = None + self.should_close = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self.open(), name) + + def __repr__(self) -> str: + if self._f is not None: + return repr(self._f) + return f"" + + def open(self) -> t.IO[t.Any]: + """Opens the file if it's not yet open. This call might fail with + a :exc:`FileError`. Not handling this error will produce an error + that Click shows. + """ + if self._f is not None: + return self._f + try: + rv, self.should_close = open_stream( + self.name, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + except OSError as e: + from .exceptions import FileError + + raise FileError(self.name, hint=e.strerror) from e + self._f = rv + return rv + + def close(self) -> None: + """Closes the underlying file, no matter what.""" + if self._f is not None: + self._f.close() + + def close_intelligently(self) -> None: + """This function only closes the file if it was opened by the lazy + file wrapper. For instance this will never close stdin. + """ + if self.should_close: + self.close() + + def __enter__(self) -> _LazyFile: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.close_intelligently() + + def __iter__(self) -> cabc.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class _KeepOpenFile: + """Proxy a file object but keep it open across a ``with`` block. + + Wraps a borrowed file (such as ``sys.stdin`` or ``sys.stdout``) so that + leaving a ``with`` block does not close it, as used by :func:`open_file` + for the ``-`` filename. The caller stays responsible for the file: an + explicit :meth:`close` still passes through to the wrapped object. + + Dunder methods are proxied explicitly: implicit special-method lookups + bypass :meth:`__getattr__`, because Python resolves them on the type rather + than the instance. + + :meta private: + """ + + _file: t.IO[t.Any] + + def __init__(self, file: t.IO[t.Any]) -> None: + self._file = file + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._file, name) + + def __enter__(self) -> _KeepOpenFile: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> cabc.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: object = None, + file: t.IO[t.Any] | None = None, + nl: bool = True, + err: bool = False, + color: bool | None = None, +) -> None: + """Print a message and newline to stdout or a file. This should be + used instead of :func:`print` because it provides better support + for different data, files, and environments. + + Compared to :func:`print`, this does the following: + + - Ensures that the output encoding is not misconfigured on Linux. + - Supports Unicode in the Windows console. + - Supports writing to binary outputs, and supports writing bytes + to text outputs. + - Removes ANSI color and style codes if the output does not look + like an interactive terminal. + - Always flushes the output. + + :param message: The string or bytes to output. Other objects are + converted to strings. + :param file: The file to write to. Defaults to ``stdout``. + :param err: Write to ``stderr`` instead of ``stdout``. + :param nl: Print a newline after the message. Enabled by default. + :param color: Force showing or hiding colors and other styles. By + default Click will remove color if the output does not look like + an interactive terminal. + + .. versionchanged:: 8.5.0 + Colorama is no longer used for color on Windows. + + .. versionchanged:: 6.0 + Support Unicode output on the Windows console. Click does not + modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` + will still not support Unicode. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionadded:: 3.0 + Added the ``err`` parameter. + + .. versionchanged:: 2.0 + Support colors on Windows if colorama is installed. + """ + if file is None: + if err: + file = _default_text_stderr() + else: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + return + + match message: + case str() | bytes() | bytearray(): + out = message + case None: + out = "" + case _: + out = str(message) + + if nl: + if isinstance(out, str): + out += "\n" + else: + out += b"\n" + + if not out: + file.flush() + return + + # If there is a message and the value looks like bytes, we manually + # need to find the binary stream and write the message in there. + # This is done separately so that most stream types will work as you + # would expect. Eg: you can write to StringIO for other cases. + if isinstance(out, (bytes, bytearray)): + binary_file = _find_binary_writer(file) + if binary_file is not None: + file.flush() + binary_file.write(out) + binary_file.flush() + return + + # ANSI style code support. For no message or bytes, nothing happens. + # When outputting to a file instead of a terminal, strip codes. + elif should_strip_ansi(file, resolve_color_default(color)): + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def _get_binary_stream(name: t.Literal["stdin", "stdout", "stderr"]) -> t.BinaryIO: + """Returns a system stream for byte processing. + + .. deprecated:: 8.5.0 + Will be removed in Click 9.0. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + + :meta private: + """ + opener = binary_streams.get(name) + if opener is None: + raise TypeError(_("Unknown standard stream '{name}'").format(name=name)) + return opener() + + +def _get_text_stream( + name: t.Literal["stdin", "stdout", "stderr"], + encoding: str | None = None, + errors: str | None = "strict", +) -> t.TextIO: + """Returns a system stream for text processing. + + .. deprecated:: 8.5.0 + Will be removed in Click 9.0. + + This usually returns a wrapped stream around a binary stream returned from + :func:`get_binary_stream` but it also can take shortcuts for already + correctly configured streams. + + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + :param encoding: overrides the detected default encoding. + :param errors: overrides the default error mode. + :meta private: + """ + opener = text_streams.get(name) + if opener is None: + raise TypeError(_("Unknown standard stream '{name}'").format(name=name)) + return opener(encoding, errors) + + +def open_file( + filename: str | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO[t.Any]: + """Open a file, with extra behavior to handle ``'-'`` to indicate + a standard stream, lazy open on write, and atomic write. Similar to + the behavior of the :class:`~click.File` param type. + + If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is + wrapped so that using it in a context manager will not close it. + This makes it possible to use the function without accidentally + closing a standard stream: + + .. code-block:: python + + with open_file(filename) as f: + ... + + :param filename: The name or Path of the file to open, or ``'-'`` for + ``stdin``/``stdout``. + :param mode: The mode in which to open the file. + :param encoding: The encoding to decode or encode a file opened in + text mode. + :param errors: The error handling mode. + :param lazy: Wait to open the file until it is accessed. For read + mode, the file is temporarily opened to raise access errors + early, then closed until it is read again. + :param atomic: Write to a temporary file and replace the given file + on close. + + .. versionadded:: 3.0 + """ + if lazy: + return t.cast( + "t.IO[t.Any]", _LazyFile(filename, mode, encoding, errors, atomic=atomic) + ) + + f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) + + if not should_close: + f = t.cast("t.IO[t.Any]", _KeepOpenFile(f)) + + return f + + +def format_filename( + filename: str | bytes | os.PathLike[str] | os.PathLike[bytes], + shorten: bool = False, +) -> str: + """Format a filename as a string for display. Ensures the filename can be + displayed by replacing any invalid bytes or surrogate escapes in the name + with the replacement character ``�``. + + Invalid bytes or surrogate escapes will raise an error when written to a + stream with ``errors="strict"``. This will typically happen with ``stdout`` + when the locale is something like ``en_GB.UTF-8``. + + Many scenarios *are* safe to write surrogates though, due to PEP 538 and + PEP 540, including: + + - Writing to ``stderr``, which uses ``errors="backslashreplace"``. + - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens + stdout and stderr with ``errors="surrogateescape"``. + - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``. + - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``. + Python opens stdout and stderr with ``errors="surrogateescape"``. + + :param filename: formats a filename for UI display. This will also convert + the filename into unicode without failing. + :param shorten: this optionally shortens the filename to strip of the + path that leads up to it. + """ + if shorten: + filename = os.path.basename(filename) + else: + filename = os.fspath(filename) + + if isinstance(filename, bytes): + filename = filename.decode(sys.getfilesystemencoding(), "replace") + else: + filename = filename.encode("utf-8", "surrogateescape").decode( + "utf-8", "replace" + ) + + return filename + + +def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: + r"""Returns the config folder for the application. The default behavior + is to return whatever is most appropriate for the operating system. + + To give you an idea, for an app called ``"Foo Bar"``, something like + the following folders could be returned: + + Mac OS X: + ``~/Library/Application Support/Foo Bar`` + Mac OS X (POSIX): + ``~/.foo-bar`` + Unix: + ``~/.config/foo-bar`` + Unix (POSIX): + ``~/.foo-bar`` + Windows (roaming): + ``C:\Users\\AppData\Roaming\Foo Bar`` + Windows (not roaming): + ``C:\Users\\AppData\Local\Foo Bar`` + + .. versionadded:: 2.0 + + :param app_name: the application name. This should be properly capitalized + and can contain whitespace. + :param roaming: controls if the folder should be roaming or not on Windows. + Has no effect otherwise. + :param force_posix: if this is set to `True` then on any POSIX system the + folder will be stored in the home folder with a leading + dot instead of the XDG config home or darwin's + application support folder. + """ + if WIN: + key = "APPDATA" if roaming else "LOCALAPPDATA" + folder = os.environ.get(key) + if folder is None: + folder = os.path.expanduser("~") + return os.path.join(folder, app_name) + if force_posix: + return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) + if sys.platform == "darwin": + return os.path.join( + os.path.expanduser("~/Library/Application Support"), app_name + ) + return os.path.join( + os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), + _posixify(app_name), + ) + + +class _PacifyFlushWrapper: + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + + :meta private: + """ + + wrapped: t.IO[t.Any] + + def __init__(self, wrapped: t.IO[t.Any]) -> None: + self.wrapped = wrapped + + def flush(self) -> None: + try: + self.wrapped.flush() + except OSError as e: + import errno + + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr: str) -> t.Any: + return getattr(self.wrapped, attr) + + +def _detect_program_name( + path: str | None = None, _main: ModuleType | None = None +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if _main is None: + _main = sys.modules["__main__"] + + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + # It is set to "" inside a Shiv or PEX zipapp. + if getattr(_main, "__package__", None) in {None, ""} or ( + os.name == "nt" + and _main.__package__ == "" + and not os.path.exists(path) + and os.path.exists(f"{path}.exe") + ): + # Executed a file, like "python app.py". + return os.path.basename(path) + + # Executed a module, like "python -m example". + # Rewritten by Python from "-m script" to "/path/to/script.py". + # Need to look at main module to determine how it was executed. + py_module = t.cast(str, _main.__package__) + name = os.path.splitext(os.path.basename(path))[0] + + # A submodule like "example.cli". + if name != "__main__": + py_module = f"{py_module}.{name}" + + return f"python -m {py_module.lstrip('.')}" + + +def _expand_args( + args: cabc.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> list[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This is intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionchanged:: 8.1 + Invalid glob patterns are treated as empty expansions rather + than raising an error. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + try: + matches = glob(arg, recursive=glob_recursive) + except re.error: + matches = [] + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out + + +def __getattr__(name: str) -> object: + import warnings + + if name in { + "LazyFile", + "KeepOpenFile", + "make_default_short_help", + "PacifyFlushWrapper", + "safecall", + "get_text_stream", + "get_binary_stream", + }: + warnings.warn( + f"'click.utils.{name}' is deprecated and will be removed in Click 9.0.", + DeprecationWarning, + stacklevel=2, + ) + return globals()[f"_{name}"] + + raise AttributeError(name) diff --git a/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/METADATA b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/METADATA new file mode 100644 index 0000000..530b398 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/METADATA @@ -0,0 +1,110 @@ +Metadata-Version: 2.4 +Name: cryptography +Version: 50.0.1 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: POSIX +Classifier: Operating System :: POSIX :: BSD +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Python :: Free Threading :: 3 - Stable +Classifier: Topic :: Security :: Cryptography +Requires-Dist: cffi>=2.0.0 ; platform_python_implementation != 'PyPy' +Requires-Dist: typing-extensions>=4.13.2 ; python_full_version < '3.11' +Requires-Dist: bcrypt>=3.1.5 ; extra == 'ssh' +Provides-Extra: ssh +License-File: LICENSE +License-File: LICENSE.APACHE +License-File: LICENSE.BSD +Summary: cryptography is a package which provides cryptographic recipes and primitives to Python developers. +Author-email: The Python Cryptographic Authority and individual contributors +License-Expression: Apache-2.0 OR BSD-3-Clause +Requires-Python: >=3.9, !=3.9.0, !=3.9.1 +Description-Content-Type: text/x-rst; charset=UTF-8 +Project-URL: changelog, https://cryptography.io/en/latest/changelog/ +Project-URL: documentation, https://cryptography.io/ +Project-URL: homepage, https://github.com/pyca/cryptography +Project-URL: issues, https://github.com/pyca/cryptography/issues +Project-URL: source, https://github.com/pyca/cryptography/ + +pyca/cryptography +================= + +.. image:: https://img.shields.io/pypi/v/cryptography.svg + :target: https://pypi.org/project/cryptography/ + :alt: Latest Version + +.. image:: https://readthedocs.org/projects/cryptography/badge/?version=latest + :target: https://cryptography.io + :alt: Latest Docs + +.. image:: https://github.com/pyca/cryptography/actions/workflows/ci.yml/badge.svg + :target: https://github.com/pyca/cryptography/actions/workflows/ci.yml?query=branch%3Amain + +``cryptography`` is a package which provides cryptographic recipes and +primitives to Python developers. Our goal is for it to be your "cryptographic +standard library". It supports Python 3.9+ and PyPy3 7.3.11+. + +``cryptography`` includes both high level recipes and low level interfaces to +common cryptographic algorithms such as symmetric ciphers, message digests, and +key derivation functions. For example, to encrypt something with +``cryptography``'s high level symmetric encryption recipe: + +.. code-block:: pycon + + >>> from cryptography.fernet import Fernet + >>> # Put this somewhere safe! + >>> key = Fernet.generate_key() + >>> f = Fernet(key) + >>> token = f.encrypt(b"A really secret message. Not for prying eyes.") + >>> token + b'...' + >>> f.decrypt(token) + b'A really secret message. Not for prying eyes.' + +You can find more information in the `documentation`_. + +You can install ``cryptography`` with: + +.. code-block:: console + + $ pip install cryptography + +For full details see `the installation documentation`_. + +Discussion +~~~~~~~~~~ + +If you run into bugs, you can file them in our `issue tracker`_. + +We maintain a `cryptography-dev`_ mailing list for development discussion. + +You can also join ``#pyca`` on ``irc.libera.chat`` to ask questions or get +involved. + +Security +~~~~~~~~ + +Need to report a security issue? Please consult our `security reporting`_ +documentation. + + +.. _`documentation`: https://cryptography.io/ +.. _`the installation documentation`: https://cryptography.io/en/latest/installation/ +.. _`issue tracker`: https://github.com/pyca/cryptography/issues +.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev +.. _`security reporting`: https://cryptography.io/en/latest/security/ + diff --git a/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/RECORD new file mode 100644 index 0000000..9be6492 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/RECORD @@ -0,0 +1,199 @@ +cryptography-50.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +cryptography-50.0.1.dist-info/METADATA,sha256=jptVcNpGhoSO5ca7g__8IkvN35eTE2cx3QUM4MBf_-I,4343 +cryptography-50.0.1.dist-info/RECORD,, +cryptography-50.0.1.dist-info/WHEEL,sha256=ObQlzmyJjoP4uM3utl2uaQ06ufI4LKnDhg53v200B3o,109 +cryptography-50.0.1.dist-info/licenses/LICENSE,sha256=Pgx8CRqUi4JTO6mP18u0BDLW8amsv4X1ki0vmak65rs,197 +cryptography-50.0.1.dist-info/licenses/LICENSE.APACHE,sha256=qsc7MUj20dcRHbyjIJn2jSbGRMaBOuHk8F9leaomY_4,11360 +cryptography-50.0.1.dist-info/licenses/LICENSE.BSD,sha256=YCxMdILeZHndLpeTzaJ15eY9dz2s0eymiSMqtwCPtPs,1532 +cryptography-50.0.1.dist-info/sboms/cryptography-rust.cyclonedx.json,sha256=m9rCnuTZv9tCQeBPD9vRLlti_jFwa9OovtKMjK_LVaA,45685 +cryptography-50.0.1.dist-info/sboms/sbom.json,sha256=S9XP7BDLleOELcWlLWOrYuGy3RtuaXtFh5A-yN5T9yA,1206 +cryptography/__about__.py,sha256=SUJRVMWtZoUu-WdZLfY7p_s2xm5YQjNLFj80YrUP9F0,445 +cryptography/__init__.py,sha256=mthuUrTd4FROCpUYrTIqhjz6s6T9djAZrV7nZ1oMm2o,364 +cryptography/__pycache__/__about__.cpython-312.pyc,, +cryptography/__pycache__/__init__.cpython-312.pyc,, +cryptography/__pycache__/cobblestone.cpython-312.pyc,, +cryptography/__pycache__/exceptions.cpython-312.pyc,, +cryptography/__pycache__/fernet.cpython-312.pyc,, +cryptography/__pycache__/utils.cpython-312.pyc,, +cryptography/cobblestone.py,sha256=T_-F08-AUf3ljaypZeklNTV1hWObH57oo45mVSzk9wc,693 +cryptography/exceptions.py,sha256=835EWILc2fwxw-gyFMriciC2SqhViETB10LBSytnDIc,1087 +cryptography/fernet.py,sha256=2sLCPOmejhOVbudcS3QsM1PIuHbxCeRcRtWkncQ3mCo,7687 +cryptography/hazmat/__init__.py,sha256=5IwrLWrVp0AjEr_4FdWG_V057NSJGY_W4egNNsuct0g,455 +cryptography/hazmat/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/__pycache__/_oid.cpython-312.pyc,, +cryptography/hazmat/_oid.py,sha256=Ohe2nUE6XjKx1UF6TAsGUy2v8GUJ-Vi9DS4yxSukt5U,18150 +cryptography/hazmat/asn1/__init__.py,sha256=YqYToHGFjLOKigRC00HGj6hrjEwbu1SPpAiu6-u-fao,812 +cryptography/hazmat/asn1/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/asn1/__pycache__/asn1.cpython-312.pyc,, +cryptography/hazmat/asn1/asn1.py,sha256=9mcfrmdvK7KtjhRYofMzAjW0u7nvqbfsvPfbhNQK1BQ,19152 +cryptography/hazmat/backends/__init__.py,sha256=O5jvKFQdZnXhKeqJ-HtulaEL9Ni7mr1mDzZY5kHlYhI,361 +cryptography/hazmat/backends/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/backends/openssl/__init__.py,sha256=p3jmJfnCag9iE5sdMrN6VvVEu55u46xaS_IjoI0SrmA,305 +cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-312.pyc,, +cryptography/hazmat/backends/openssl/backend.py,sha256=0NczFbiwGxfq9qi28oHYr08r_r7VQg4AXHjb0oFPwqA,10475 +cryptography/hazmat/bindings/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 +cryptography/hazmat/bindings/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/bindings/_rust.abi3.so,sha256=PWQjZkv_6Sb9kdGRPRxd3aXDHuf_HK4G_ItxxHyVAaE,14389392 +cryptography/hazmat/bindings/_rust/__init__.pyi,sha256=uUnnvOq_uameYnBNpmsv3SRAVO0WQGpjI58aR1lL4Kw,2285 +cryptography/hazmat/bindings/_rust/_openssl.pyi,sha256=T_5TpYO8bghIPjAQ3BtAABqw-CcYskHp92wm5PNY6dc,228 +cryptography/hazmat/bindings/_rust/asn1.pyi,sha256=BrGjC8J6nwuS-r3EVcdXJB8ndotfY9mbQYOfpbPG0HA,354 +cryptography/hazmat/bindings/_rust/cobblestone.pyi,sha256=9lUf1W1hTrszEJ4YpScJNIPSD9BOM7o-C82-mtSDG9o,1325 +cryptography/hazmat/bindings/_rust/declarative_asn1.pyi,sha256=MBmOZzLTdmPJ6-1K6Aq3PTuHJ1MGgRfIu0hIgag7ocE,3866 +cryptography/hazmat/bindings/_rust/exceptions.pyi,sha256=exXr2xw_0pB1kk93cYbM3MohbzoUkjOms1ZMUi0uQZE,640 +cryptography/hazmat/bindings/_rust/ocsp.pyi,sha256=VPVWuKHI9EMs09ZLRYAGvR0Iz0mCMmEzXAkgJHovpoM,4020 +cryptography/hazmat/bindings/_rust/openssl/__init__.pyi,sha256=O6GOyvDxt6ylicB2aFGuu5RcGZy9UjKnYqGR8rqzvdw,1578 +cryptography/hazmat/bindings/_rust/openssl/aead.pyi,sha256=7d--xdc0vuzRe4S2uuQaGm-9GGKv4NcSLQcLyWcaD6s,4582 +cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi,sha256=LhPzHWSXJq4grAJXn6zSvSSdV-aYIIscHDwIPlJGGPs,1315 +cryptography/hazmat/bindings/_rust/openssl/cmac.pyi,sha256=nPH0X57RYpsAkRowVpjQiHE566ThUTx7YXrsadmrmHk,564 +cryptography/hazmat/bindings/_rust/openssl/dh.pyi,sha256=Z3TC-G04-THtSdAOPLM1h2G7ml5bda1ElZUcn5wpuhk,1564 +cryptography/hazmat/bindings/_rust/openssl/dsa.pyi,sha256=qBtkgj2albt2qFcnZ9UDrhzoNhCVO7HTby5VSf1EXMI,1299 +cryptography/hazmat/bindings/_rust/openssl/ec.pyi,sha256=zJy0pRa5n-_p2dm45PxECB_-B6SVZyNKfjxFDpPqT38,1691 +cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi,sha256=VXfXd5G6hUivg399R1DYdmW3eTb0EebzDTqjRC2gaRw,532 +cryptography/hazmat/bindings/_rust/openssl/ed448.pyi,sha256=Yx49lqdnjsD7bxiDV1kcaMrDktug5evi5a6zerMiy2s,514 +cryptography/hazmat/bindings/_rust/openssl/hashes.pyi,sha256=bFe3T13UeyRInUJMD-R5ArGTGLqWDiWo2Lv2KGzUbf4,1076 +cryptography/hazmat/bindings/_rust/openssl/hmac.pyi,sha256=BXZn7NDjL3JAbYW0SQ8pg1iyC5DbQXVhUAiwsi8DFR8,702 +cryptography/hazmat/bindings/_rust/openssl/hpke.pyi,sha256=p_hGLn6YH-EarJF1V5bUsUtjipviEApcR5O0sUHJLtc,2914 +cryptography/hazmat/bindings/_rust/openssl/kdf.pyi,sha256=HiiLdEB9nqMsTvMwiFppQpfPz6fHUz9_gpjEB_XAOXA,6619 +cryptography/hazmat/bindings/_rust/openssl/keys.pyi,sha256=teIt8M6ZEMJrn4s3W0UnW0DZ-30Jd68WnSsKKG124l0,912 +cryptography/hazmat/bindings/_rust/openssl/keywrap.pyi,sha256=mGCJEoQ4lxi4CROTBEuUzb9U4ky8n52L3iDvCOXNBRI,733 +cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi,sha256=H84XDW0TQ3LHhdGJD5uPaDT5ijEiK6MZytXYc8rBNf4,1421 +cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi,sha256=byoeus4-lIOlWlEiRQdCc00qCkXpOB5peBY6yo9ik8s,835 +cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi,sha256=_SW9NtQ5FDlAbdclFtWpT4lGmxKIKHpN-4j8J2BzYfQ,585 +cryptography/hazmat/bindings/_rust/openssl/rsa.pyi,sha256=2OQCNSXkxgc-3uw1xiCCloIQTV6p9_kK79Yu0rhZgPc,1364 +cryptography/hazmat/bindings/_rust/openssl/x25519.pyi,sha256=ewn4GpQyb7zPwE-ni7GtyQgMC0A1mLuqYsSyqv6nI_s,523 +cryptography/hazmat/bindings/_rust/openssl/x448.pyi,sha256=juTZTmli8jO_5Vcufg-vHvx_tCyezmSLIh_9PU3TczI,505 +cryptography/hazmat/bindings/_rust/pkcs12.pyi,sha256=vEEd5wDiZvb8ZGFaziLCaWLzAwoG_tvPUxLQw5_uOl8,1605 +cryptography/hazmat/bindings/_rust/pkcs7.pyi,sha256=txGBJijqZshEcqra6byPNbnisIdlxzOSIHP2hl9arPs,1601 +cryptography/hazmat/bindings/_rust/test_support.pyi,sha256=PPhld-WkO743iXFPebeG0LtgK0aTzGdjcIsay1Gm5GE,757 +cryptography/hazmat/bindings/_rust/x509.pyi,sha256=LVT4htBNse5gijQh4GhVNYEbAO0E1ANgE28tWqw3g4I,10211 +cryptography/hazmat/bindings/openssl/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 +cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-312.pyc,, +cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-312.pyc,, +cryptography/hazmat/bindings/openssl/_conditional.py,sha256=vfzi-xHdStAcJQjLqqOlSos94L8D_tqFtBe7Ovw-KtY,5611 +cryptography/hazmat/bindings/openssl/binding.py,sha256=FsuLxbTnrqW60-dAWtakgufeq7TnqaQai1Pc7Dowrjo,3706 +cryptography/hazmat/decrepit/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 +cryptography/hazmat/decrepit/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/decrepit/ciphers/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 +cryptography/hazmat/decrepit/ciphers/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/decrepit/ciphers/__pycache__/algorithms.cpython-312.pyc,, +cryptography/hazmat/decrepit/ciphers/__pycache__/modes.cpython-312.pyc,, +cryptography/hazmat/decrepit/ciphers/algorithms.py,sha256=WSPjM2sXfrLDXrtN5R7N6rA1XF7b93I1e_lvD4yx2oA,3752 +cryptography/hazmat/decrepit/ciphers/modes.py,sha256=Oq_PEwCke5OLczOfr_vzAOJ6wPx-rlsvHAXPBuh5b9o,1649 +cryptography/hazmat/primitives/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 +cryptography/hazmat/primitives/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/_modes.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/_serialization.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/cmac.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/constant_time.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/hashes.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/hmac.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/hpke.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/keywrap.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/padding.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/poly1305.cpython-312.pyc,, +cryptography/hazmat/primitives/_asymmetric.py,sha256=RhgcouUB6HTiFDBrR1LxqkMjpUxIiNvQ1r_zJjRG6qQ,532 +cryptography/hazmat/primitives/_cipheralgorithm.py,sha256=Eh3i7lwedHfi0eLSsH93PZxQKzY9I6lkK67vL4V5tOc,1522 +cryptography/hazmat/primitives/_modes.py,sha256=_EiAOD8Jb6WpFXluwkcUT3ECz_TH8xjGvbQlGkNm59Q,3075 +cryptography/hazmat/primitives/_serialization.py,sha256=hi0xJblBAZ8pmZx2lWa-lruphxvvvG3g9DNK0G8Odfs,4440 +cryptography/hazmat/primitives/asymmetric/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 +cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/mldsa.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/mlkem.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/dh.py,sha256=Rnt2fLaDpQEjQSX8fNtlmTXOJHjlt_DKQZ1Vlfhdja4,5793 +cryptography/hazmat/primitives/asymmetric/dsa.py,sha256=kQzTViqAI9PiELNOBco_n6MfP3N9ehIPkSduOpupt0M,4482 +cryptography/hazmat/primitives/asymmetric/ec.py,sha256=LklVCLLxbbQzqiE6HRaYTRauLMphYUw3tE6c8oBIxYQ,10183 +cryptography/hazmat/primitives/asymmetric/ed25519.py,sha256=mbffLvs-3PgCDeg1rQXVXh7GLntlVL4ATjkIEc4pt5M,2998 +cryptography/hazmat/primitives/asymmetric/ed448.py,sha256=n0dK6y-VLxuw-zvrDxuPT4598hymgX-1Ry_jq9aUQhE,4002 +cryptography/hazmat/primitives/asymmetric/mldsa.py,sha256=LHMaaU0m66EsSrSodWf4e2kdmdDRTEGRC1y-fCKxGK0,13803 +cryptography/hazmat/primitives/asymmetric/mlkem.py,sha256=wvVkeFT-4of-VcUAER2-zI_5hXT6rwTJPMCQ8gF9GTA,7901 +cryptography/hazmat/primitives/asymmetric/padding.py,sha256=jbgajvGHYwkPjMDljHu5lxMBlDfmCxWUe2AF2S6KE10,3678 +cryptography/hazmat/primitives/asymmetric/rsa.py,sha256=lIE7lGe449W5URLTKCpvFGerITouKoZV9GwuTzScsSo,8492 +cryptography/hazmat/primitives/asymmetric/types.py,sha256=SjJhTrnFRLlTPdYrBD1poMi6cjYG-fh0fSIaQn3QUiw,2588 +cryptography/hazmat/primitives/asymmetric/utils.py,sha256=Qs8Re9GFPjW_tNp_73IeJEjPCf0slOIsWOxo6qymT6k,821 +cryptography/hazmat/primitives/asymmetric/x25519.py,sha256=lQcgUk-Piubj8ynFNWQeYme3tYZMgHQqkRKkIDOHLzo,3888 +cryptography/hazmat/primitives/asymmetric/x448.py,sha256=b37ig7k7poG6SJDry42j-ElCj5q4eQ0X5CFmN0Sn8z8,3913 +cryptography/hazmat/primitives/ciphers/__init__.py,sha256=eyEXmjk6_CZXaOPYDr7vAYGXr29QvzgWL2-4CSolLFs,680 +cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/aead.py,sha256=Fzlyx7w8KYQakzDp1zWgJnIr62zgZrgVh1u2h4exB54,634 +cryptography/hazmat/primitives/ciphers/algorithms.py,sha256=IGfCycYJOh1TvoI34rfw8KfJ_xmt1p_DpBht2Fghceo,3496 +cryptography/hazmat/primitives/ciphers/base.py,sha256=aBC7HHBBoixebmparVr0UlODs3VD0A7B6oz_AaRjDv8,4253 +cryptography/hazmat/primitives/ciphers/modes.py,sha256=H7AZSgJiSTBCi0grL97NxakS950JyFKYLsra8tjHPxQ,6003 +cryptography/hazmat/primitives/cmac.py,sha256=sz_s6H_cYnOvx-VNWdIKhRhe3Ymp8z8J0D3CBqOX3gg,338 +cryptography/hazmat/primitives/constant_time.py,sha256=xdunWT0nf8OvKdcqUhhlFKayGp4_PgVJRU2W1wLSr_A,422 +cryptography/hazmat/primitives/hashes.py,sha256=Srtwgc3_pFvZfIhRj9e1p4Wdi-F_PpleGHlqJARgtOg,7916 +cryptography/hazmat/primitives/hmac.py,sha256=RpB3z9z5skirCQrm7zQbtnp9pLMnAjrlTUvKqF5aDDc,423 +cryptography/hazmat/primitives/hpke.py,sha256=RsHissC5l-dTPn1p2JbcrIrAnDBrLqM5ugBFSGpmKu4,865 +cryptography/hazmat/primitives/kdf/__init__.py,sha256=v3yiYBGU272EojNXbwfYZdbbfI9cVOCCG3nXhTDda3k,1037 +cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/argon2.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/argon2.py,sha256=ZJx-enUlAA4o7b46C1wlzG_XAQQW0wIkFgUKkrM_wA8,632 +cryptography/hazmat/primitives/kdf/concatkdf.py,sha256=BFnOKS72txKF0yQvASr0Na7uhzvLHpUCckK5iNAC_aQ,591 +cryptography/hazmat/primitives/kdf/hkdf.py,sha256=M0lAEfRoc4kpp4-nwDj9yB-vNZukIOYEQrUlWsBNn9o,543 +cryptography/hazmat/primitives/kdf/kbkdf.py,sha256=C3w99vjFqhqzAxzFOH4zzvVoHkb5AC6Lz6AEQGV7d_g,737 +cryptography/hazmat/primitives/kdf/pbkdf2.py,sha256=Xchkk99s-Mk6b6KSCouqdk8FVyiNVOSFIenmnW3tLgQ,468 +cryptography/hazmat/primitives/kdf/scrypt.py,sha256=XyWUdUUmhuI9V6TqAPOvujCSMGv1XQdg0a21IWCmO-U,590 +cryptography/hazmat/primitives/kdf/x963kdf.py,sha256=QKjhRehuTsAstL384bKfvjuv1yfdamAHVhsBRWen2Vw,456 +cryptography/hazmat/primitives/keywrap.py,sha256=t9t8KZuzZ4Us30ASUhI27MZPPPKy4EERRDkAK-lM7TQ,735 +cryptography/hazmat/primitives/padding.py,sha256=QT-U-NvV2eQGO1wVPbDiNGNSc9keRDS-ig5cQOrLz0E,1865 +cryptography/hazmat/primitives/poly1305.py,sha256=P5EPQV-RB_FJPahpg01u0Ts4S_PnAmsroxIGXbGeRRo,355 +cryptography/hazmat/primitives/serialization/__init__.py,sha256=fXcu49ugL9rxhpRl_wZjnMqE9DCkhCMcsRxfj_qTveg,2217 +cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/base.py,sha256=SABrvo0S86QcoBlj7DpjeLWy2lzQMryiioJ6DrEYQVA,651 +cryptography/hazmat/primitives/serialization/pkcs12.py,sha256=mS9cFNG4afzvseoc5e1MWoY2VskfL8N8Y_OFjl67luY,5104 +cryptography/hazmat/primitives/serialization/pkcs7.py,sha256=mFM7OFuZ8cCxUGUoo4Cof6BnCHc_Ibx1AQjjfxnPlxo,13998 +cryptography/hazmat/primitives/serialization/ssh.py,sha256=FisXe_nUKdPGfqENVhKpmoazmaI8qH8Ge-VGbc0dykw,53592 +cryptography/hazmat/primitives/twofactor/__init__.py,sha256=tmMZGB-g4IU1r7lIFqASU019zr0uPp_wEBYcwdDCKCA,258 +cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-312.pyc,, +cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-312.pyc,, +cryptography/hazmat/primitives/twofactor/hotp.py,sha256=ivZo5BrcCGWLsqql4nZV0XXCjyGPi_iHfDFltGlOJwk,3256 +cryptography/hazmat/primitives/twofactor/totp.py,sha256=m5LPpRL00kp4zY8gTjr55Hfz9aMlPS53kHmVkSQCmdY,1652 +cryptography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +cryptography/utils.py,sha256=Ia7S0QZzS0ezwpIHQfY8G1e-o4GjrHijEXXjwAr1iuI,4635 +cryptography/x509/__init__.py,sha256=0KVNTmRtdYNX_i_ewsTNzbTm2e1qyfoOhc4emog5hL4,8253 +cryptography/x509/__pycache__/__init__.cpython-312.pyc,, +cryptography/x509/__pycache__/base.cpython-312.pyc,, +cryptography/x509/__pycache__/certificate_transparency.cpython-312.pyc,, +cryptography/x509/__pycache__/extensions.cpython-312.pyc,, +cryptography/x509/__pycache__/general_name.cpython-312.pyc,, +cryptography/x509/__pycache__/name.cpython-312.pyc,, +cryptography/x509/__pycache__/ocsp.cpython-312.pyc,, +cryptography/x509/__pycache__/oid.cpython-312.pyc,, +cryptography/x509/__pycache__/verification.cpython-312.pyc,, +cryptography/x509/base.py,sha256=UIMKkwmsx10BR3_gnBRBCBTwQp1-O8wkfVm7azH-_mE,28311 +cryptography/x509/certificate_transparency.py,sha256=JqoOIDhlwInrYMFW6IFn77WJ0viF-PB_rlZV3vs9MYc,797 +cryptography/x509/extensions.py,sha256=-iXcwWnIL1zl5Ml3BVJFkEo_71ncXe84ehvqzW1sK4M,77975 +cryptography/x509/general_name.py,sha256=sP_rV11Qlpsk4x3XXGJY_Mv0Q_s9dtjeLckHsjpLQoQ,7836 +cryptography/x509/name.py,sha256=oI2w6VdY8zgw7qpNhlxEx512W0AhWDXQrDYf5EsM2PY,15566 +cryptography/x509/ocsp.py,sha256=Yey6NdFV1MPjop24Mj_VenjEpg3kUaMopSWOK0AbeBs,12699 +cryptography/x509/oid.py,sha256=BUzgXXGVWilkBkdKPTm9R4qElE9gAGHgdYPMZAp7PJo,931 +cryptography/x509/verification.py,sha256=gR2C2c-XZQtblZhT5T5vjSKOtCb74ef2alPVmEcwFlM,958 diff --git a/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/WHEEL b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/WHEEL new file mode 100644 index 0000000..08b80b3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: maturin (1.14.1) +Root-Is-Purelib: false +Tag: cp311-abi3-manylinux_2_34_x86_64 + diff --git a/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/licenses/LICENSE new file mode 100644 index 0000000..b11f379 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/licenses/LICENSE @@ -0,0 +1,3 @@ +This software is made available under the terms of *either* of the licenses +found in LICENSE.APACHE or LICENSE.BSD. Contributions to cryptography are made +under the terms of *both* these licenses. diff --git a/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/licenses/LICENSE.APACHE b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/licenses/LICENSE.APACHE new file mode 100644 index 0000000..62589ed --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/licenses/LICENSE.APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/licenses/LICENSE.BSD b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/licenses/LICENSE.BSD new file mode 100644 index 0000000..ec1a29d --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/licenses/LICENSE.BSD @@ -0,0 +1,27 @@ +Copyright (c) Individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of PyCA Cryptography nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/sboms/cryptography-rust.cyclonedx.json b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/sboms/cryptography-rust.cyclonedx.json new file mode 100644 index 0000000..e7df0f2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/sboms/cryptography-rust.cyclonedx.json @@ -0,0 +1,1365 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "version": 1, + "serialNumber": "urn:uuid:8937a838-8d73-4fdd-9a20-bac2da7b0240", + "metadata": { + "timestamp": "2026-08-25T19:33:44.118311124Z", + "tools": [ + { + "vendor": "CycloneDX", + "name": "cargo-cyclonedx", + "version": "0.5.9" + } + ], + "authors": [ + { + "name": "The cryptography developers", + "email": "cryptography-dev@python.org" + } + ], + "component": { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust#cryptography-rust@0.50.1", + "author": "The cryptography developers ", + "name": "cryptography-rust", + "version": "0.50.1", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-rust@0.50.1?download_url=file://.", + "components": [ + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust#cryptography-rust@0.50.1 bin-target-0", + "name": "cryptography_rust", + "version": "0.50.1", + "purl": "pkg:cargo/cryptography-rust@0.50.1?download_url=file://.#src/lib.rs" + } + ] + }, + "properties": [ + { + "name": "cdx:rustc:sbom:target:all_targets", + "value": "true" + } + ] + }, + "components": [ + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-cffi#0.50.1", + "author": "The cryptography developers ", + "name": "cryptography-cffi", + "version": "0.50.1", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-cffi@0.50.1?download_url=file://cryptography-cffi" + }, + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-crypto#0.50.1", + "author": "The cryptography developers ", + "name": "cryptography-crypto", + "version": "0.50.1", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-crypto@0.50.1?download_url=file://cryptography-crypto" + }, + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-keepalive#0.50.1", + "author": "The cryptography developers ", + "name": "cryptography-keepalive", + "version": "0.50.1", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-keepalive@0.50.1?download_url=file://cryptography-keepalive" + }, + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-key-parsing#0.50.1", + "author": "The cryptography developers ", + "name": "cryptography-key-parsing", + "version": "0.50.1", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-key-parsing@0.50.1?download_url=file://cryptography-key-parsing" + }, + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-openssl#0.50.1", + "author": "The cryptography developers ", + "name": "cryptography-openssl", + "version": "0.50.1", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-openssl@0.50.1?download_url=file://cryptography-openssl" + }, + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-x509#0.50.1", + "author": "The cryptography developers ", + "name": "cryptography-x509", + "version": "0.50.1", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-x509@0.50.1?download_url=file://cryptography-x509" + }, + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-x509-verification#0.50.1", + "author": "The cryptography developers ", + "name": "cryptography-x509-verification", + "version": "0.50.1", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-x509-verification@0.50.1?download_url=file://cryptography-x509-verification" + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", + "author": "Alex Gaynor ", + "name": "asn1", + "version": "0.24.1", + "description": "ASN.1 (DER) parser and writer for Rust.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "c9795210620c0cb3f9a7ce4f882808c38e1ef7b347c90591dceae0886e031fb1" + } + ], + "licenses": [ + { + "expression": "BSD-3-Clause" + } + ], + "purl": "pkg:cargo/asn1@0.24.1", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/alex/rust-asn1" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1", + "author": "Alex Gaynor ", + "name": "asn1_derive", + "version": "0.24.1", + "description": "#[derive] support for asn1", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "909e307f1cc32bb8bccbd98f446e6d1bf03fa30f7b53a4337da7181ad30fa11a" + } + ], + "licenses": [ + { + "expression": "BSD-3-Clause" + } + ], + "purl": "pkg:cargo/asn1_derive@0.24.1", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/alex/rust-asn1" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#base64@0.23.0", + "author": "Marshall Pierce ", + "name": "base64", + "version": "0.23.0", + "description": "encodes and decodes base64 as bytes or utf8", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "b25655df2c3cdd83c5e5b293b88acd880332b2ddadd7c30ac43144fdc0033da9" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/base64@0.23.0", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/base64" + }, + { + "type": "vcs", + "url": "https://github.com/marshallpierce/rust-base64" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.1", + "author": "The Rust Project Developers", + "name": "bitflags", + "version": "2.13.1", + "description": "A macro to generate structures which behave like bitflags. ", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/bitflags@2.13.1", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/bitflags" + }, + { + "type": "website", + "url": "https://github.com/bitflags/bitflags" + }, + { + "type": "vcs", + "url": "https://github.com/bitflags/bitflags" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#cc@1.4.0", + "author": "Alex Crichton ", + "name": "cc", + "version": "1.4.0", + "description": "A build-time dependency for Cargo build scripts to assist in invoking the native C compiler to compile native C code into a static archive to be linked into Rust code. ", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/cc@1.4.0", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/cc" + }, + { + "type": "website", + "url": "https://github.com/rust-lang/cc-rs" + }, + { + "type": "vcs", + "url": "https://github.com/rust-lang/cc-rs" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", + "author": "Alex Crichton ", + "name": "cfg-if", + "version": "1.0.4", + "description": "A macro to ergonomically define an item depending on a large number of #[cfg] parameters. Structured like an if-else chain, the first matching branch is the item that gets emitted. ", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/cfg-if@1.0.4", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/rust-lang/cfg-if" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9", + "name": "find-msvc-tools", + "version": "0.1.9", + "description": "Find windows-specific tools, read MSVC versions from the registry and from COM interfaces", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/find-msvc-tools@0.1.9", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/find-msvc-tools" + }, + { + "type": "vcs", + "url": "https://github.com/rust-lang/cc-rs" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1", + "author": "Steven Fackler ", + "name": "foreign-types-shared", + "version": "0.1.1", + "description": "An internal crate used by foreign-types", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/foreign-types-shared@0.1.1", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/sfackler/foreign-types" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2", + "author": "Steven Fackler ", + "name": "foreign-types", + "version": "0.3.2", + "description": "A framework for Rust wrappers over C APIs", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/foreign-types@0.3.2", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/sfackler/foreign-types" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0", + "name": "heck", + "version": "0.5.0", + "description": "heck is a case conversion library.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/heck@0.5.0", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/withoutboats/heck" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18", + "author": "David Tolnay ", + "name": "itoa", + "version": "1.0.18", + "description": "Fast integer primitive to string conversion", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/itoa@1.0.18", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/itoa" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/itoa" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189", + "name": "libc", + "version": "0.2.189", + "description": "Raw FFI bindings to platform libraries like libc.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/libc@0.2.189", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/rust-lang/libc" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4", + "author": "Aleksey Kladov ", + "name": "once_cell", + "version": "1.21.4", + "description": "Single assignment cells and lazy values.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/once_cell@1.21.4", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/once_cell" + }, + { + "type": "vcs", + "url": "https://github.com/matklad/once_cell" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1", + "name": "openssl-macros", + "version": "0.1.1", + "description": "Internal macros used by the openssl crate.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/openssl-macros@0.1.1" + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117", + "author": "Alex Crichton , Steven Fackler ", + "name": "openssl-sys", + "version": "0.9.117", + "description": "FFI bindings to OpenSSL", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" + } + ], + "licenses": [ + { + "expression": "MIT" + } + ], + "purl": "pkg:cargo/openssl-sys@0.9.117", + "externalReferences": [ + { + "type": "other", + "url": "openssl" + }, + { + "type": "vcs", + "url": "https://github.com/rust-openssl/rust-openssl" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81", + "author": "Steven Fackler ", + "name": "openssl", + "version": "0.10.81", + "description": "OpenSSL bindings", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" + } + ], + "licenses": [ + { + "expression": "Apache-2.0" + } + ], + "purl": "pkg:cargo/openssl@0.10.81", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/rust-openssl/rust-openssl" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pem@4.0.0", + "author": "Jonathan Creekmore ", + "name": "pem", + "version": "4.0.0", + "description": "Parse and encode PEM-encoded data.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "d354a98a3d1251555de99e8fdd8afda05573c31b82f59063a7b0a29b5527f120" + } + ], + "licenses": [ + { + "expression": "MIT" + } + ], + "purl": "pkg:cargo/pem@4.0.0", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/pem/" + }, + { + "type": "website", + "url": "https://github.com/jcreekmore/pem-rs.git" + }, + { + "type": "vcs", + "url": "https://github.com/jcreekmore/pem-rs.git" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33", + "author": "Alex Crichton ", + "name": "pkg-config", + "version": "0.3.33", + "description": "A library to run the pkg-config system tool at build time in order to be used in Cargo build scripts. ", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pkg-config@0.3.33", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/pkg-config" + }, + { + "type": "vcs", + "url": "https://github.com/rust-lang/pkg-config-rs" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.14.0", + "name": "portable-atomic", + "version": "1.14.0", + "description": "Portable atomic types including support for 128-bit atomics, atomic float, etc. ", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "purl": "pkg:cargo/portable-atomic@1.14.0", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/taiki-e/portable-atomic" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107", + "author": "David Tolnay , Alex Crichton ", + "name": "proc-macro2", + "version": "1.0.107", + "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/proc-macro2@1.0.107", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/proc-macro2" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/proc-macro2" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0", + "author": "PyO3 Project and Contributors ", + "name": "pyo3-build-config", + "version": "0.29.0", + "description": "Build configuration for the PyO3 ecosystem", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3-build-config@0.29.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.0", + "author": "PyO3 Project and Contributors ", + "name": "pyo3-ffi", + "version": "0.29.0", + "description": "Python-API bindings for the PyO3 ecosystem", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3-ffi@0.29.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "other", + "url": "python" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.0", + "author": "PyO3 Project and Contributors ", + "name": "pyo3-macros-backend", + "version": "0.29.0", + "description": "Code generation for PyO3 package", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3-macros-backend@0.29.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.0", + "author": "PyO3 Project and Contributors ", + "name": "pyo3-macros", + "version": "0.29.0", + "description": "Proc macros for PyO3 package", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3-macros@0.29.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0", + "author": "PyO3 Project and Contributors ", + "name": "pyo3", + "version": "0.29.0", + "description": "Bindings to Python interpreter", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3@0.29.0", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/crate/pyo3/" + }, + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "other", + "url": "pyo3-python" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47", + "author": "David Tolnay ", + "name": "quote", + "version": "1.0.47", + "description": "Quasi-quoting macro quote!(...)", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/quote@1.0.47", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/quote/" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/quote" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#self_cell@1.3.0", + "author": "Lukas Bergdoll ", + "name": "self_cell", + "version": "1.3.0", + "description": "Safe-to-use proc-macro-free self-referential structs in stable Rust.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR GPL-2.0-only" + } + ], + "purl": "pkg:cargo/self_cell@1.3.0", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/self_cell" + }, + { + "type": "vcs", + "url": "https://github.com/Voultapher/self_cell" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1", + "author": "comex , Fenhl , Adrian Taylor , Alex Touchet , Daniel Parks , Garrett Berg ", + "name": "shlex", + "version": "2.0.1", + "description": "Split a string into shell words, like Python's shlex.", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/shlex@2.0.1", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/comex/rust-shlex" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119", + "author": "David Tolnay ", + "name": "syn", + "version": "2.0.119", + "description": "Parser for Rust source code", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/syn@2.0.119", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/syn" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/syn" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5", + "author": "Dan Gohman ", + "name": "target-lexicon", + "version": "0.13.5", + "description": "LLVM target triple types", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 WITH LLVM-exception" + } + ], + "purl": "pkg:cargo/target-lexicon@0.13.5", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/target-lexicon/" + }, + { + "type": "vcs", + "url": "https://github.com/bytecodealliance/target-lexicon" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24", + "author": "David Tolnay ", + "name": "unicode-ident", + "version": "1.0.24", + "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + } + ], + "licenses": [ + { + "expression": "(MIT OR Apache-2.0) AND Unicode-3.0" + } + ], + "purl": "pkg:cargo/unicode-ident@1.0.24", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/unicode-ident" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/unicode-ident" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15", + "author": "Jim McGrath ", + "name": "vcpkg", + "version": "0.2.15", + "description": "A library to find native dependencies in a vcpkg tree at build time in order to be used in Cargo build scripts. ", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/vcpkg@0.2.15", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/vcpkg" + }, + { + "type": "vcs", + "url": "https://github.com/mcgoo/vcpkg-rs" + } + ] + } + ], + "dependencies": [ + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust#cryptography-rust@0.50.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", + "registry+https://github.com/rust-lang/crates.io-index#base64@0.23.0", + "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-cffi#0.50.1", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-crypto#0.50.1", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-keepalive#0.50.1", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-key-parsing#0.50.1", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-openssl#0.50.1", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-x509#0.50.1", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-x509-verification#0.50.1", + "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1", + "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81", + "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117", + "registry+https://github.com/rust-lang/crates.io-index#pem@4.0.0", + "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0", + "registry+https://github.com/rust-lang/crates.io-index#self_cell@1.3.0" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-cffi#0.50.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#cc@1.4.0", + "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117", + "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-crypto#0.50.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-keepalive#0.50.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-key-parsing#0.50.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", + "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-crypto#0.50.1", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-openssl#0.50.1", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-x509#0.50.1", + "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81", + "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117", + "registry+https://github.com/rust-lang/crates.io-index#pem@4.0.0" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-openssl#0.50.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", + "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2", + "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1", + "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81", + "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-x509#0.50.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-x509-verification#0.50.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpHjnbGg/cryptography-50.0.1/src/rust/cryptography-x509#0.50.1" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1", + "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47", + "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#base64@0.23.0" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.1" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#cc@1.4.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9", + "registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47", + "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#cc@1.4.0", + "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189", + "registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33", + "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.1", + "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", + "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2", + "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189", + "registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1", + "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pem@4.0.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#base64@0.23.0" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.14.0" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0", + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47", + "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.0", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47", + "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189", + "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4", + "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.14.0", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.0", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.0" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#self_cell@1.3.0" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47", + "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15" + } + ] +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/sboms/sbom.json b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/sboms/sbom.json new file mode 100644 index 0000000..b35f4b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography-50.0.1.dist-info/sboms/sbom.json @@ -0,0 +1,43 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "version": 1, + "serialNumber": "urn:uuid:8993a62f-53da-4d3f-b423-fc6d6d57604c", + "metadata": { + "timestamp": "2026-08-25T18:07:43Z" + }, + "components": [ + { + "type": "library", + "name": "openssl", + "version": "4.0.2", + "purl": "pkg:generic/openssl@4.0.2?download_url=https://github.com/openssl/openssl/releases/download/openssl-4.0.2/openssl-4.0.2.tar.gz", + "hashes": [ + { + "alg": "SHA-256", + "content": "736b467530f916737b7031310ccb21d8218c6229e61e8e160cd1d3458cd543a8" + } + ], + "externalReferences": [ + { + "type": "distribution", + "url": "https://github.com/openssl/openssl/releases/download/openssl-4.0.2/openssl-4.0.2.tar.gz" + } + ], + "properties": [ + { + "name": "build:operating-system", + "value": "linux" + }, + { + "name": "build:architecture", + "value": "x86_64" + }, + { + "name": "build:flags", + "value": "no-zlib no-shared no-module no-comp no-apps no-docs no-sm2-precomp no-atexit enable-ec_nistp_64_gcc_128" + } + ] + } + ] +} diff --git a/venv/lib/python3.12/site-packages/cryptography/__about__.py b/venv/lib/python3.12/site-packages/cryptography/__about__.py new file mode 100644 index 0000000..d6fc1ae --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/__about__.py @@ -0,0 +1,17 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +__all__ = [ + "__author__", + "__copyright__", + "__version__", +] + +__version__ = "50.0.1" + + +__author__ = "The Python Cryptographic Authority and individual contributors" +__copyright__ = f"Copyright 2013-2026 {__author__}" diff --git a/venv/lib/python3.12/site-packages/cryptography/__init__.py b/venv/lib/python3.12/site-packages/cryptography/__init__.py new file mode 100644 index 0000000..d374f75 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/__init__.py @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.__about__ import __author__, __copyright__, __version__ + +__all__ = [ + "__author__", + "__copyright__", + "__version__", +] diff --git a/venv/lib/python3.12/site-packages/cryptography/__pycache__/__about__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/__pycache__/__about__.cpython-312.pyc new file mode 100644 index 0000000..0fa94db Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/__pycache__/__about__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5fc16f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/__pycache__/cobblestone.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/__pycache__/cobblestone.cpython-312.pyc new file mode 100644 index 0000000..5b09ea9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/__pycache__/cobblestone.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..bbbda89 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/__pycache__/fernet.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/__pycache__/fernet.cpython-312.pyc new file mode 100644 index 0000000..c549d5a Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/__pycache__/fernet.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..c8f1bd8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/cobblestone.py b/venv/lib/python3.12/site-packages/cryptography/cobblestone.py new file mode 100644 index 0000000..09f67af --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/cobblestone.py @@ -0,0 +1,21 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import ( + cobblestone as _cobblestone, +) + +Cobblestone128Decryptor = _cobblestone.Cobblestone128Decryptor +Cobblestone128Encryptor = _cobblestone.Cobblestone128Encryptor +Cobblestone256Decryptor = _cobblestone.Cobblestone256Decryptor +Cobblestone256Encryptor = _cobblestone.Cobblestone256Encryptor + +__all__ = [ + "Cobblestone128Decryptor", + "Cobblestone128Encryptor", + "Cobblestone256Decryptor", + "Cobblestone256Encryptor", +] diff --git a/venv/lib/python3.12/site-packages/cryptography/exceptions.py b/venv/lib/python3.12/site-packages/cryptography/exceptions.py new file mode 100644 index 0000000..fe125ea --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/exceptions.py @@ -0,0 +1,52 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography.hazmat.bindings._rust import exceptions as rust_exceptions + +if typing.TYPE_CHECKING: + from cryptography.hazmat.bindings._rust import openssl as rust_openssl + +_Reasons = rust_exceptions._Reasons + + +class UnsupportedAlgorithm(Exception): + def __init__(self, message: str, reason: _Reasons | None = None) -> None: + super().__init__(message) + self._reason = reason + + +class AlreadyFinalized(Exception): + pass + + +class AlreadyUpdated(Exception): + pass + + +class NotYetFinalized(Exception): + pass + + +class InvalidTag(Exception): + pass + + +class InvalidSignature(Exception): + pass + + +class InternalError(Exception): + def __init__( + self, msg: str, err_code: list[rust_openssl.OpenSSLError] + ) -> None: + super().__init__(msg) + self.err_code = err_code + + +class InvalidKey(Exception): + pass diff --git a/venv/lib/python3.12/site-packages/cryptography/fernet.py b/venv/lib/python3.12/site-packages/cryptography/fernet.py new file mode 100644 index 0000000..582e594 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/fernet.py @@ -0,0 +1,240 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import base64 +import binascii +import os +import time +import typing +from collections.abc import Iterable + +from cryptography import utils +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives import hashes, padding +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.hmac import HMAC + + +class InvalidToken(Exception): + pass + + +_MAX_CLOCK_SKEW = 60 + +# Hoisted to module level so each operation doesn't reconstruct them. +_PKCS7_128 = padding.PKCS7(128) +_SHA256 = hashes.SHA256() + + +class Fernet: + def __init__( + self, + key: bytes | str, + backend: typing.Any = None, + ) -> None: + try: + key = base64.urlsafe_b64decode(key) + except binascii.Error as exc: + raise ValueError( + "Fernet key must be 32 url-safe base64-encoded bytes." + ) from exc + if len(key) != 32: + raise ValueError( + "Fernet key must be 32 url-safe base64-encoded bytes." + ) + + self._signing_key = key[:16] + self._encryption_key = key[16:] + self._aes = algorithms.AES(self._encryption_key) + + @classmethod + def generate_key(cls) -> bytes: + return base64.urlsafe_b64encode(os.urandom(32)) + + def encrypt(self, data: bytes) -> bytes: + return self.encrypt_at_time(data, int(time.time())) + + def encrypt_at_time(self, data: bytes, current_time: int) -> bytes: + iv = os.urandom(16) + return self._encrypt_from_parts(data, current_time, iv) + + def _encrypt_from_parts( + self, data: bytes, current_time: int, iv: bytes + ) -> bytes: + utils._check_bytes("data", data) + + padder = _PKCS7_128.padder() + padded_data = padder.update(data) + padder.finalize() + encryptor = Cipher(self._aes, modes.CBC(iv)).encryptor() + ciphertext = encryptor.update(padded_data) + encryptor.finalize() + + basic_parts = ( + b"\x80" + + current_time.to_bytes(length=8, byteorder="big") + + iv + + ciphertext + ) + + h = HMAC(self._signing_key, _SHA256) + h.update(basic_parts) + hmac = h.finalize() + return base64.urlsafe_b64encode(basic_parts + hmac) + + def decrypt(self, token: bytes | str, ttl: int | None = None) -> bytes: + timestamp, data = Fernet._get_unverified_token_data(token) + if ttl is None: + time_info = None + else: + time_info = (ttl, int(time.time())) + return self._decrypt_data(data, timestamp, time_info) + + def decrypt_at_time( + self, token: bytes | str, ttl: int, current_time: int + ) -> bytes: + if ttl is None: + raise ValueError( + "decrypt_at_time() can only be used with a non-None ttl" + ) + timestamp, data = Fernet._get_unverified_token_data(token) + return self._decrypt_data(data, timestamp, (ttl, current_time)) + + def extract_timestamp(self, token: bytes | str) -> int: + timestamp, data = Fernet._get_unverified_token_data(token) + # Verify the token was not tampered with. + self._verify_signature(data) + return timestamp + + @staticmethod + def _get_unverified_token_data(token: bytes | str) -> tuple[int, bytes]: + if not isinstance(token, (str, bytes)): + raise TypeError("token must be bytes or str") + + try: + data = base64.urlsafe_b64decode(token) + except (TypeError, binascii.Error): + raise InvalidToken + + if not data or data[0] != 0x80: + raise InvalidToken + + if len(data) < 9: + raise InvalidToken + + timestamp = int.from_bytes(data[1:9], byteorder="big") + return timestamp, data + + def _verify_signature(self, data: bytes) -> None: + h = HMAC(self._signing_key, _SHA256) + h.update(memoryview(data)[:-32]) + try: + h.verify(data[-32:]) + except InvalidSignature: + raise InvalidToken + + def _decrypt_data( + self, + data: bytes, + timestamp: int, + time_info: tuple[int, int] | None, + ) -> bytes: + if time_info is not None: + ttl, current_time = time_info + if timestamp + ttl < current_time: + raise InvalidToken + + if current_time + _MAX_CLOCK_SKEW < timestamp: + raise InvalidToken + + self._verify_signature(data) + + mv = memoryview(data) + iv = data[9:25] + ciphertext = mv[25:-32] + decryptor = Cipher(self._aes, modes.CBC(iv)).decryptor() + plaintext_padded = decryptor.update(ciphertext) + try: + plaintext_padded += decryptor.finalize() + except ValueError: + raise InvalidToken + unpadder = _PKCS7_128.unpadder() + + unpadded = unpadder.update(plaintext_padded) + try: + unpadded += unpadder.finalize() + except ValueError: + raise InvalidToken + return unpadded + + +class MultiFernet: + def __init__(self, fernets: Iterable[Fernet]): + fernets = list(fernets) + if not fernets: + raise ValueError( + "MultiFernet requires at least one Fernet instance" + ) + self._fernets = fernets + + def encrypt(self, msg: bytes) -> bytes: + return self.encrypt_at_time(msg, int(time.time())) + + def encrypt_at_time(self, msg: bytes, current_time: int) -> bytes: + return self._fernets[0].encrypt_at_time(msg, current_time) + + def rotate(self, msg: bytes | str) -> bytes: + timestamp, data = Fernet._get_unverified_token_data(msg) + for f in self._fernets: + try: + p = f._decrypt_data(data, timestamp, None) + break + except InvalidToken: + pass + else: + raise InvalidToken + + iv = os.urandom(16) + return self._fernets[0]._encrypt_from_parts(p, timestamp, iv) + + def decrypt(self, msg: bytes | str, ttl: int | None = None) -> bytes: + if ttl is None: + time_info = None + else: + time_info = (ttl, int(time.time())) + # Parse the token once rather than once per key. + timestamp, data = Fernet._get_unverified_token_data(msg) + for f in self._fernets: + try: + return f._decrypt_data(data, timestamp, time_info) + except InvalidToken: + pass + raise InvalidToken + + def decrypt_at_time( + self, msg: bytes | str, ttl: int, current_time: int + ) -> bytes: + if ttl is None: + raise ValueError( + "decrypt_at_time() can only be used with a non-None ttl" + ) + # Parse the token once rather than once per key. + timestamp, data = Fernet._get_unverified_token_data(msg) + for f in self._fernets: + try: + return f._decrypt_data(data, timestamp, (ttl, current_time)) + except InvalidToken: + pass + raise InvalidToken + + def extract_timestamp(self, msg: bytes | str) -> int: + # Parse the token once rather than once per key. + timestamp, data = Fernet._get_unverified_token_data(msg) + for f in self._fernets: + try: + f._verify_signature(data) + return timestamp + except InvalidToken: + pass + raise InvalidToken diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/__init__.py new file mode 100644 index 0000000..b9f1187 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/__init__.py @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +""" +Hazardous Materials + +This is a "Hazardous Materials" module. You should ONLY use it if you're +100% absolutely sure that you know what you're doing because this module +is full of land mines, dragons, and dinosaurs with laser guns. +""" diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..73ef75c Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/__pycache__/_oid.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/__pycache__/_oid.cpython-312.pyc new file mode 100644 index 0000000..91c0036 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/__pycache__/_oid.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/_oid.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/_oid.py new file mode 100644 index 0000000..244a1b3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/_oid.py @@ -0,0 +1,373 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import ( + ObjectIdentifier as ObjectIdentifier, +) +from cryptography.hazmat.primitives import hashes + + +class ExtensionOID: + SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9") + SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14") + KEY_USAGE = ObjectIdentifier("2.5.29.15") + PRIVATE_KEY_USAGE_PERIOD = ObjectIdentifier("2.5.29.16") + SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17") + ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18") + BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19") + NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30") + CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31") + CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32") + POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33") + AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35") + POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36") + EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37") + FRESHEST_CRL = ObjectIdentifier("2.5.29.46") + INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54") + ISSUING_DISTRIBUTION_POINT = ObjectIdentifier("2.5.29.28") + AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1") + SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11") + OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5") + TLS_FEATURE = ObjectIdentifier("1.3.6.1.5.5.7.1.24") + CRL_NUMBER = ObjectIdentifier("2.5.29.20") + DELTA_CRL_INDICATOR = ObjectIdentifier("2.5.29.27") + PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier( + "1.3.6.1.4.1.11129.2.4.2" + ) + PRECERT_POISON = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3") + SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.5") + MS_CERTIFICATE_TEMPLATE = ObjectIdentifier("1.3.6.1.4.1.311.21.7") + ADMISSIONS = ObjectIdentifier("1.3.36.8.3.3") + + +class OCSPExtensionOID: + NONCE = ObjectIdentifier("1.3.6.1.5.5.7.48.1.2") + ACCEPTABLE_RESPONSES = ObjectIdentifier("1.3.6.1.5.5.7.48.1.4") + + +class CRLEntryExtensionOID: + CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29") + CRL_REASON = ObjectIdentifier("2.5.29.21") + INVALIDITY_DATE = ObjectIdentifier("2.5.29.24") + + +class NameOID: + COMMON_NAME = ObjectIdentifier("2.5.4.3") + COUNTRY_NAME = ObjectIdentifier("2.5.4.6") + LOCALITY_NAME = ObjectIdentifier("2.5.4.7") + STATE_OR_PROVINCE_NAME = ObjectIdentifier("2.5.4.8") + STREET_ADDRESS = ObjectIdentifier("2.5.4.9") + ORGANIZATION_IDENTIFIER = ObjectIdentifier("2.5.4.97") + ORGANIZATION_NAME = ObjectIdentifier("2.5.4.10") + ORGANIZATIONAL_UNIT_NAME = ObjectIdentifier("2.5.4.11") + SERIAL_NUMBER = ObjectIdentifier("2.5.4.5") + SURNAME = ObjectIdentifier("2.5.4.4") + GIVEN_NAME = ObjectIdentifier("2.5.4.42") + TITLE = ObjectIdentifier("2.5.4.12") + INITIALS = ObjectIdentifier("2.5.4.43") + GENERATION_QUALIFIER = ObjectIdentifier("2.5.4.44") + X500_UNIQUE_IDENTIFIER = ObjectIdentifier("2.5.4.45") + DN_QUALIFIER = ObjectIdentifier("2.5.4.46") + PSEUDONYM = ObjectIdentifier("2.5.4.65") + USER_ID = ObjectIdentifier("0.9.2342.19200300.100.1.1") + DOMAIN_COMPONENT = ObjectIdentifier("0.9.2342.19200300.100.1.25") + EMAIL_ADDRESS = ObjectIdentifier("1.2.840.113549.1.9.1") + JURISDICTION_COUNTRY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.3") + JURISDICTION_LOCALITY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.1") + JURISDICTION_STATE_OR_PROVINCE_NAME = ObjectIdentifier( + "1.3.6.1.4.1.311.60.2.1.2" + ) + BUSINESS_CATEGORY = ObjectIdentifier("2.5.4.15") + POSTAL_ADDRESS = ObjectIdentifier("2.5.4.16") + POSTAL_CODE = ObjectIdentifier("2.5.4.17") + INN = ObjectIdentifier("1.2.643.3.131.1.1") + OGRN = ObjectIdentifier("1.2.643.100.1") + SNILS = ObjectIdentifier("1.2.643.100.3") + UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2") + UNSIGNED = ObjectIdentifier("1.3.6.1.5.5.7.25.1") + + +class SignatureAlgorithmOID: + RSA_WITH_MD5 = ObjectIdentifier("1.2.840.113549.1.1.4") + RSA_WITH_SHA1 = ObjectIdentifier("1.2.840.113549.1.1.5") + # This is an alternate OID for RSA with SHA1 that is occasionally seen + _RSA_WITH_SHA1 = ObjectIdentifier("1.3.14.3.2.29") + RSA_WITH_SHA224 = ObjectIdentifier("1.2.840.113549.1.1.14") + RSA_WITH_SHA256 = ObjectIdentifier("1.2.840.113549.1.1.11") + RSA_WITH_SHA384 = ObjectIdentifier("1.2.840.113549.1.1.12") + RSA_WITH_SHA512 = ObjectIdentifier("1.2.840.113549.1.1.13") + RSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.13") + RSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.14") + RSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.15") + RSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.16") + RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10") + ECDSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10045.4.1") + ECDSA_WITH_SHA224 = ObjectIdentifier("1.2.840.10045.4.3.1") + ECDSA_WITH_SHA256 = ObjectIdentifier("1.2.840.10045.4.3.2") + ECDSA_WITH_SHA384 = ObjectIdentifier("1.2.840.10045.4.3.3") + ECDSA_WITH_SHA512 = ObjectIdentifier("1.2.840.10045.4.3.4") + ECDSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.9") + ECDSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.10") + ECDSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.11") + ECDSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.12") + DSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10040.4.3") + DSA_WITH_SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.3.1") + DSA_WITH_SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.3.2") + DSA_WITH_SHA384 = ObjectIdentifier("2.16.840.1.101.3.4.3.3") + DSA_WITH_SHA512 = ObjectIdentifier("2.16.840.1.101.3.4.3.4") + ED25519 = ObjectIdentifier("1.3.101.112") + ED448 = ObjectIdentifier("1.3.101.113") + ML_DSA_44 = ObjectIdentifier("2.16.840.1.101.3.4.3.17") + ML_DSA_65 = ObjectIdentifier("2.16.840.1.101.3.4.3.18") + ML_DSA_87 = ObjectIdentifier("2.16.840.1.101.3.4.3.19") + GOSTR3411_94_WITH_3410_2001 = ObjectIdentifier("1.2.643.2.2.3") + GOSTR3410_2012_WITH_3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2") + GOSTR3410_2012_WITH_3411_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3") + UNSIGNED = ObjectIdentifier("1.3.6.1.5.5.7.6.36") + + +_SIG_OIDS_TO_HASH: dict[ObjectIdentifier, hashes.HashAlgorithm | None] = { + SignatureAlgorithmOID.RSA_WITH_MD5: hashes.MD5(), + SignatureAlgorithmOID.RSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID._RSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID.RSA_WITH_SHA224: hashes.SHA224(), + SignatureAlgorithmOID.RSA_WITH_SHA256: hashes.SHA256(), + SignatureAlgorithmOID.RSA_WITH_SHA384: hashes.SHA384(), + SignatureAlgorithmOID.RSA_WITH_SHA512: hashes.SHA512(), + SignatureAlgorithmOID.RSA_WITH_SHA3_224: hashes.SHA3_224(), + SignatureAlgorithmOID.RSA_WITH_SHA3_256: hashes.SHA3_256(), + SignatureAlgorithmOID.RSA_WITH_SHA3_384: hashes.SHA3_384(), + SignatureAlgorithmOID.RSA_WITH_SHA3_512: hashes.SHA3_512(), + SignatureAlgorithmOID.ECDSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID.ECDSA_WITH_SHA224: hashes.SHA224(), + SignatureAlgorithmOID.ECDSA_WITH_SHA256: hashes.SHA256(), + SignatureAlgorithmOID.ECDSA_WITH_SHA384: hashes.SHA384(), + SignatureAlgorithmOID.ECDSA_WITH_SHA512: hashes.SHA512(), + SignatureAlgorithmOID.ECDSA_WITH_SHA3_224: hashes.SHA3_224(), + SignatureAlgorithmOID.ECDSA_WITH_SHA3_256: hashes.SHA3_256(), + SignatureAlgorithmOID.ECDSA_WITH_SHA3_384: hashes.SHA3_384(), + SignatureAlgorithmOID.ECDSA_WITH_SHA3_512: hashes.SHA3_512(), + SignatureAlgorithmOID.DSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID.DSA_WITH_SHA224: hashes.SHA224(), + SignatureAlgorithmOID.DSA_WITH_SHA256: hashes.SHA256(), + SignatureAlgorithmOID.ED25519: None, + SignatureAlgorithmOID.ED448: None, + SignatureAlgorithmOID.ML_DSA_44: None, + SignatureAlgorithmOID.ML_DSA_65: None, + SignatureAlgorithmOID.ML_DSA_87: None, + SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: None, + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: None, + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: None, + SignatureAlgorithmOID.UNSIGNED: None, +} + + +class HashAlgorithmOID: + SHA1 = ObjectIdentifier("1.3.14.3.2.26") + SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.2.4") + SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.2.1") + SHA384 = ObjectIdentifier("2.16.840.1.101.3.4.2.2") + SHA512 = ObjectIdentifier("2.16.840.1.101.3.4.2.3") + SHA3_224 = ObjectIdentifier("1.3.6.1.4.1.37476.3.2.1.99.7.224") + SHA3_256 = ObjectIdentifier("1.3.6.1.4.1.37476.3.2.1.99.7.256") + SHA3_384 = ObjectIdentifier("1.3.6.1.4.1.37476.3.2.1.99.7.384") + SHA3_512 = ObjectIdentifier("1.3.6.1.4.1.37476.3.2.1.99.7.512") + SHA3_224_NIST = ObjectIdentifier("2.16.840.1.101.3.4.2.7") + SHA3_256_NIST = ObjectIdentifier("2.16.840.1.101.3.4.2.8") + SHA3_384_NIST = ObjectIdentifier("2.16.840.1.101.3.4.2.9") + SHA3_512_NIST = ObjectIdentifier("2.16.840.1.101.3.4.2.10") + + +class PublicKeyAlgorithmOID: + DSA = ObjectIdentifier("1.2.840.10040.4.1") + EC_PUBLIC_KEY = ObjectIdentifier("1.2.840.10045.2.1") + RSAES_PKCS1_v1_5 = ObjectIdentifier("1.2.840.113549.1.1.1") + RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10") + X25519 = ObjectIdentifier("1.3.101.110") + X448 = ObjectIdentifier("1.3.101.111") + ED25519 = ObjectIdentifier("1.3.101.112") + ED448 = ObjectIdentifier("1.3.101.113") + ML_DSA_44 = ObjectIdentifier("2.16.840.1.101.3.4.3.17") + ML_DSA_65 = ObjectIdentifier("2.16.840.1.101.3.4.3.18") + ML_DSA_87 = ObjectIdentifier("2.16.840.1.101.3.4.3.19") + ML_KEM_768 = ObjectIdentifier("2.16.840.1.101.3.4.4.2") + ML_KEM_1024 = ObjectIdentifier("2.16.840.1.101.3.4.4.3") + + +class ExtendedKeyUsageOID: + SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1") + CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2") + CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3") + EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4") + TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8") + OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9") + ANY_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37.0") + SMARTCARD_LOGON = ObjectIdentifier("1.3.6.1.4.1.311.20.2.2") + KERBEROS_PKINIT_KDC = ObjectIdentifier("1.3.6.1.5.2.3.5") + IPSEC_IKE = ObjectIdentifier("1.3.6.1.5.5.7.3.17") + BUNDLE_SECURITY = ObjectIdentifier("1.3.6.1.5.5.7.3.35") + CERTIFICATE_TRANSPARENCY = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.4") + + +class OtherNameFormOID: + PERMANENT_IDENTIFIER = ObjectIdentifier("1.3.6.1.5.5.7.8.3") + HW_MODULE_NAME = ObjectIdentifier("1.3.6.1.5.5.7.8.4") + DNS_SRV = ObjectIdentifier("1.3.6.1.5.5.7.8.7") + NAI_REALM = ObjectIdentifier("1.3.6.1.5.5.7.8.8") + SMTP_UTF8_MAILBOX = ObjectIdentifier("1.3.6.1.5.5.7.8.9") + ACP_NODE_NAME = ObjectIdentifier("1.3.6.1.5.5.7.8.10") + BUNDLE_EID = ObjectIdentifier("1.3.6.1.5.5.7.8.11") + + +class AuthorityInformationAccessOID: + CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2") + OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1") + + +class SubjectInformationAccessOID: + CA_REPOSITORY = ObjectIdentifier("1.3.6.1.5.5.7.48.5") + + +class CertificatePoliciesOID: + CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1") + CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2") + ANY_POLICY = ObjectIdentifier("2.5.29.32.0") + + +class AttributeOID: + CHALLENGE_PASSWORD = ObjectIdentifier("1.2.840.113549.1.9.7") + UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2") + + +_OID_NAMES = { + NameOID.COMMON_NAME: "commonName", + NameOID.COUNTRY_NAME: "countryName", + NameOID.LOCALITY_NAME: "localityName", + NameOID.STATE_OR_PROVINCE_NAME: "stateOrProvinceName", + NameOID.STREET_ADDRESS: "streetAddress", + NameOID.ORGANIZATION_NAME: "organizationName", + NameOID.ORGANIZATIONAL_UNIT_NAME: "organizationalUnitName", + NameOID.SERIAL_NUMBER: "serialNumber", + NameOID.SURNAME: "surname", + NameOID.GIVEN_NAME: "givenName", + NameOID.TITLE: "title", + NameOID.GENERATION_QUALIFIER: "generationQualifier", + NameOID.X500_UNIQUE_IDENTIFIER: "x500UniqueIdentifier", + NameOID.DN_QUALIFIER: "dnQualifier", + NameOID.PSEUDONYM: "pseudonym", + NameOID.USER_ID: "userID", + NameOID.DOMAIN_COMPONENT: "domainComponent", + NameOID.EMAIL_ADDRESS: "emailAddress", + NameOID.JURISDICTION_COUNTRY_NAME: "jurisdictionCountryName", + NameOID.JURISDICTION_LOCALITY_NAME: "jurisdictionLocalityName", + NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME: ( + "jurisdictionStateOrProvinceName" + ), + NameOID.BUSINESS_CATEGORY: "businessCategory", + NameOID.POSTAL_ADDRESS: "postalAddress", + NameOID.POSTAL_CODE: "postalCode", + NameOID.INN: "INN", + NameOID.OGRN: "OGRN", + NameOID.SNILS: "SNILS", + NameOID.UNSTRUCTURED_NAME: "unstructuredName", + SignatureAlgorithmOID.RSA_WITH_MD5: "md5WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA1: "sha1WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA224: "sha224WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA256: "sha256WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA384: "sha384WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA512: "sha512WithRSAEncryption", + SignatureAlgorithmOID.RSASSA_PSS: "rsassaPss", + SignatureAlgorithmOID.ECDSA_WITH_SHA1: "ecdsa-with-SHA1", + SignatureAlgorithmOID.ECDSA_WITH_SHA224: "ecdsa-with-SHA224", + SignatureAlgorithmOID.ECDSA_WITH_SHA256: "ecdsa-with-SHA256", + SignatureAlgorithmOID.ECDSA_WITH_SHA384: "ecdsa-with-SHA384", + SignatureAlgorithmOID.ECDSA_WITH_SHA512: "ecdsa-with-SHA512", + SignatureAlgorithmOID.DSA_WITH_SHA1: "dsa-with-sha1", + SignatureAlgorithmOID.DSA_WITH_SHA224: "dsa-with-sha224", + SignatureAlgorithmOID.DSA_WITH_SHA256: "dsa-with-sha256", + SignatureAlgorithmOID.ED25519: "ed25519", + SignatureAlgorithmOID.ED448: "ed448", + SignatureAlgorithmOID.ML_DSA_44: "ML-DSA-44", + SignatureAlgorithmOID.ML_DSA_65: "ML-DSA-65", + SignatureAlgorithmOID.ML_DSA_87: "ML-DSA-87", + SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: ( + "GOST R 34.11-94 with GOST R 34.10-2001" + ), + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: ( + "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)" + ), + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: ( + "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)" + ), + HashAlgorithmOID.SHA1: "sha1", + HashAlgorithmOID.SHA224: "sha224", + HashAlgorithmOID.SHA256: "sha256", + HashAlgorithmOID.SHA384: "sha384", + HashAlgorithmOID.SHA512: "sha512", + HashAlgorithmOID.SHA3_224: "sha3_224", + HashAlgorithmOID.SHA3_256: "sha3_256", + HashAlgorithmOID.SHA3_384: "sha3_384", + HashAlgorithmOID.SHA3_512: "sha3_512", + HashAlgorithmOID.SHA3_224_NIST: "sha3_224", + HashAlgorithmOID.SHA3_256_NIST: "sha3_256", + HashAlgorithmOID.SHA3_384_NIST: "sha3_384", + HashAlgorithmOID.SHA3_512_NIST: "sha3_512", + PublicKeyAlgorithmOID.DSA: "dsaEncryption", + PublicKeyAlgorithmOID.EC_PUBLIC_KEY: "id-ecPublicKey", + PublicKeyAlgorithmOID.RSAES_PKCS1_v1_5: "rsaEncryption", + PublicKeyAlgorithmOID.X25519: "X25519", + PublicKeyAlgorithmOID.X448: "X448", + ExtendedKeyUsageOID.SERVER_AUTH: "serverAuth", + ExtendedKeyUsageOID.CLIENT_AUTH: "clientAuth", + ExtendedKeyUsageOID.CODE_SIGNING: "codeSigning", + ExtendedKeyUsageOID.EMAIL_PROTECTION: "emailProtection", + ExtendedKeyUsageOID.TIME_STAMPING: "timeStamping", + ExtendedKeyUsageOID.OCSP_SIGNING: "OCSPSigning", + ExtendedKeyUsageOID.SMARTCARD_LOGON: "msSmartcardLogin", + ExtendedKeyUsageOID.KERBEROS_PKINIT_KDC: "pkInitKDC", + ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES: "subjectDirectoryAttributes", + ExtensionOID.SUBJECT_KEY_IDENTIFIER: "subjectKeyIdentifier", + ExtensionOID.KEY_USAGE: "keyUsage", + ExtensionOID.PRIVATE_KEY_USAGE_PERIOD: "privateKeyUsagePeriod", + ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName", + ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName", + ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints", + ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: ( + "signedCertificateTimestampList" + ), + ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS: ( + "signedCertificateTimestampList" + ), + ExtensionOID.PRECERT_POISON: "ctPoison", + ExtensionOID.MS_CERTIFICATE_TEMPLATE: "msCertificateTemplate", + ExtensionOID.ADMISSIONS: "Admissions", + CRLEntryExtensionOID.CRL_REASON: "cRLReason", + CRLEntryExtensionOID.INVALIDITY_DATE: "invalidityDate", + CRLEntryExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer", + ExtensionOID.NAME_CONSTRAINTS: "nameConstraints", + ExtensionOID.CRL_DISTRIBUTION_POINTS: "cRLDistributionPoints", + ExtensionOID.CERTIFICATE_POLICIES: "certificatePolicies", + ExtensionOID.POLICY_MAPPINGS: "policyMappings", + ExtensionOID.AUTHORITY_KEY_IDENTIFIER: "authorityKeyIdentifier", + ExtensionOID.POLICY_CONSTRAINTS: "policyConstraints", + ExtensionOID.EXTENDED_KEY_USAGE: "extendedKeyUsage", + ExtensionOID.FRESHEST_CRL: "freshestCRL", + ExtensionOID.INHIBIT_ANY_POLICY: "inhibitAnyPolicy", + ExtensionOID.ISSUING_DISTRIBUTION_POINT: "issuingDistributionPoint", + ExtensionOID.AUTHORITY_INFORMATION_ACCESS: "authorityInfoAccess", + ExtensionOID.SUBJECT_INFORMATION_ACCESS: "subjectInfoAccess", + ExtensionOID.OCSP_NO_CHECK: "OCSPNoCheck", + ExtensionOID.CRL_NUMBER: "cRLNumber", + ExtensionOID.DELTA_CRL_INDICATOR: "deltaCRLIndicator", + ExtensionOID.TLS_FEATURE: "TLSFeature", + AuthorityInformationAccessOID.OCSP: "OCSP", + AuthorityInformationAccessOID.CA_ISSUERS: "caIssuers", + SubjectInformationAccessOID.CA_REPOSITORY: "caRepository", + CertificatePoliciesOID.CPS_QUALIFIER: "id-qt-cps", + CertificatePoliciesOID.CPS_USER_NOTICE: "id-qt-unotice", + OCSPExtensionOID.NONCE: "OCSPNonce", + AttributeOID.CHALLENGE_PASSWORD: "challengePassword", +} diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__init__.py new file mode 100644 index 0000000..d19e2a6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__init__.py @@ -0,0 +1,47 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.asn1.asn1 import ( + TLV, + BitString, + Default, + Explicit, + GeneralizedTime, + IA5String, + Implicit, + Null, + PrintableString, + SetOf, + Size, + UTCTime, + Variant, + decode_der, + encode_der, + sequence, + set, + value_set, +) + +__all__ = [ + "TLV", + "BitString", + "Default", + "Explicit", + "GeneralizedTime", + "IA5String", + "Implicit", + "Null", + "PrintableString", + "SetOf", + "Size", + "UTCTime", + "Variant", + "decode_der", + "encode_der", + "sequence", + "set", + "value_set", +] diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5cd3a8b Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__pycache__/asn1.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__pycache__/asn1.cpython-312.pyc new file mode 100644 index 0000000..77ad6ef Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__pycache__/asn1.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/asn1.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/asn1.py new file mode 100644 index 0000000..62d3811 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/asn1.py @@ -0,0 +1,533 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import builtins +import dataclasses +import enum +import sys +import types +import typing + +if sys.version_info < (3, 11): + import typing_extensions + + LiteralString = typing_extensions.LiteralString +else: + LiteralString = typing.LiteralString + +from cryptography.hazmat.bindings._rust import declarative_asn1 +from cryptography.hazmat.bindings._rust import x509 as rust_x509 + +if sys.version_info < (3, 10): + NoneType = type(None) +else: + NoneType = types.NoneType # type: ignore[nonetype-type] + +T = typing.TypeVar("T", covariant=True) +U = typing.TypeVar("U") +Tag = typing.TypeVar("Tag", bound=LiteralString) + + +@dataclasses.dataclass(frozen=True) +class Variant(typing.Generic[U, Tag]): + """ + A tagged variant for CHOICE fields with the same underlying type. + + Use this when you have multiple CHOICE alternatives with the same type + and need to distinguish between them: + + foo: ( + Annotated[Variant[int, typing.Literal["IntA"]], Implicit(0)] + | Annotated[Variant[int, typing.Literal["IntB"]], Implicit(1)] + ) + + Usage: + example = Example(foo=Variant(5, "IntA")) + decoded.foo.value # The int value + decoded.foo.tag # "IntA" or "IntB" + """ + + value: U + tag: str + + +decode_der = declarative_asn1.decode_der +encode_der = declarative_asn1.encode_der + + +_X509_TYPES = ( + rust_x509.Certificate, + rust_x509.CertificateSigningRequest, + rust_x509.CertificateRevocationList, +) + + +def _check_x509_field_annotations( + field_type: typing.Any, + annotation: declarative_asn1.Annotation, + field_name: str, +) -> None: + if field_type in _X509_TYPES and isinstance(annotation.encoding, Implicit): + raise TypeError( + f"field '{field_name}' has an IMPLICIT annotation, but " + "IMPLICIT annotations are not supported for X.509 types." + ) + + +def _is_union(field_type: type) -> bool: + # NOTE: types.UnionType for `T | U`, typing.Union for `Union[T, U]`. + # TODO: Drop the `hasattr()` once the minimum supported Python version + # is >= 3.10. + union_types = ( + (types.UnionType, typing.Union) + if hasattr(types, "UnionType") + else (typing.Union,) + ) + return typing.get_origin(field_type) in union_types + + +def _resolve_type_aliases(field_type: typing.Any) -> typing.Any: + # Recursively resolve PEP 695 (`type X = ...`) type aliases (Python + # 3.12+) to their underlying value, so that the rest of the + # normalization logic never encounters an alias. Aliases can refer + # to other aliases and can appear at any level of nesting (e.g. + # inside `Annotated[...]`, unions, or `list[...]`). + if sys.version_info < (3, 12): + return field_type + + while isinstance(field_type, typing.TypeAliasType): + field_type = field_type.__value__ + + args = typing.get_args(field_type) + resolved_args = tuple(_resolve_type_aliases(arg) for arg in args) + if resolved_args == args: + # No aliases anywhere inside: return the type unchanged. + return field_type + + if _is_union(field_type): + # `X | Y` unions can't be rebuilt through their origin like + # other generics below: `typing.get_origin` returns + # `types.UnionType` for them, which is only subscriptable on + # Python 3.14+. Rebuilding through `typing.Union` also + # flattens any nested union introduced by an alias of a union + # (e.g. `Time | int` where `type Time = UTCTime | + # GeneralizedTime`), just like `typing.Union` would have done + # if the alias had been written inline. + return typing.Union[resolved_args] + + # An alias appeared inside a generic (e.g. `Annotated[Time, ...]`, + # `list[MyInt]`, or `SetOf[MyInt]`): re-parameterize the generic + # with the resolved arguments. Subscripting with a tuple is + # equivalent to subscripting with multiple arguments. + return typing.get_origin(field_type)[resolved_args] + + +def _extract_annotation( + metadata: tuple, field_name: str +) -> declarative_asn1.Annotation: + default = None + encoding = None + size = None + for raw_annotation in metadata: + if isinstance(raw_annotation, Default): + if default is not None: + raise TypeError( + f"multiple DEFAULT annotations found in field " + f"'{field_name}'" + ) + default = raw_annotation.value + elif isinstance(raw_annotation, declarative_asn1.Encoding): + if encoding is not None: + raise TypeError( + f"multiple IMPLICIT/EXPLICIT annotations found in field " + f"'{field_name}'" + ) + encoding = raw_annotation + elif isinstance(raw_annotation, declarative_asn1.Size): + if size is not None: + raise TypeError( + f"multiple SIZE annotations found in field '{field_name}'" + ) + size = raw_annotation + else: + raise TypeError(f"unsupported annotation: {raw_annotation}") + + return declarative_asn1.Annotation( + default=default, encoding=encoding, size=size + ) + + +def _normalize_field_type( + field_type: typing.Any, field_name: str +) -> declarative_asn1.AnnotatedType: + field_type = _resolve_type_aliases(field_type) + + # Strip the `Annotated[...]` off, and populate the annotation + # from it if it exists. + if typing.get_origin(field_type) is typing.Annotated: + annotation = _extract_annotation(field_type.__metadata__, field_name) + field_type, *_ = typing.get_args(field_type) + else: + annotation = declarative_asn1.Annotation() + + if annotation.size is not None and ( + typing.get_origin(field_type) not in (builtins.list, SetOf) + and field_type + not in ( + builtins.bytes, + builtins.str, + BitString, + IA5String, + PrintableString, + ) + ): + raise TypeError( + f"field '{field_name}' has a SIZE annotation, but SIZE " + "annotations are only supported for fields of types: " + "[SEQUENCE OF, SET OF, BIT STRING, OCTET STRING, UTF8String, " + "PrintableString, IA5String]" + ) + + if field_type is TLV: + if isinstance(annotation.encoding, Implicit): + raise TypeError( + f"field '{field_name}' has an IMPLICIT annotation, but " + "IMPLICIT annotations are not supported for TLV types." + ) + elif annotation.default is not None: + raise TypeError( + f"field '{field_name}' has a DEFAULT annotation, but " + "DEFAULT annotations are not supported for TLV types." + ) + + _check_x509_field_annotations(field_type, annotation, field_name) + + if hasattr(field_type, "__asn1_root__"): + root_type = field_type.__asn1_root__ + if not isinstance( + root_type, + ( + declarative_asn1.Type.Sequence, + declarative_asn1.Type.Set, + declarative_asn1.Type.ValueSet, + ), + ): + raise TypeError(f"unsupported root type: {root_type}") + return declarative_asn1.AnnotatedType( + typing.cast(declarative_asn1.Type, root_type), annotation + ) + elif _is_union(field_type): + union_args = typing.get_args(field_type) + if len(union_args) == 2 and NoneType in union_args: + # A Union between a type and None is an OPTIONAL + optional_type = ( + union_args[0] if union_args[1] is type(None) else union_args[1] + ) + if optional_type is TLV: + raise TypeError( + "optional TLV types (`TLV | None`) are not " + "currently supported" + ) + # For optional types, the annotation is associated with the + # union, so we check it against the inner type here. + _check_x509_field_annotations( + optional_type, annotation, field_name + ) + annotated_type = _normalize_field_type(optional_type, field_name) + + if not annotated_type.annotation.is_empty(): + raise TypeError( + "optional (`X | None`) types cannot have `X` " + "annotated: annotations must apply to the union " + "(i.e: `Annotated[X | None, annotation]`)" + ) + + if annotation.default is not None: + raise TypeError( + "optional (`X | None`) types should not have a DEFAULT " + "annotation" + ) + + rust_field_type = declarative_asn1.Type.Option(annotated_type) + + else: + # Otherwise, the Union is a CHOICE + if isinstance(annotation.encoding, Implicit): + # CHOICEs cannot be IMPLICIT. See X.680 section 31.2.9. + raise TypeError( + "CHOICE (`X | Y | ...`) types should not have an IMPLICIT " + "annotation" + ) + variants = [ + _type_to_variant(arg, field_name) + for arg in union_args + if arg is not type(None) + ] + + # Union types should either be all Variants + # (`Variant[..] | Variant[..] | etc`) or all non Variants + are_union_types_tagged = variants[0].tag_name is not None + if any( + (v.tag_name is not None) != are_union_types_tagged + for v in variants + ): + raise TypeError( + "When using `asn1.Variant` in a union, all the other " + "types in the union must also be `asn1.Variant`" + ) + + if are_union_types_tagged: + tags = {v.tag_name for v in variants} + if len(variants) != len(tags): + raise TypeError( + "When using `asn1.Variant` in a union, the tags used " + "must be unique" + ) + + rust_choice_type = declarative_asn1.Type.Choice(variants) + # If None is part of the union types, this is an OPTIONAL CHOICE + rust_field_type = ( + declarative_asn1.Type.Option( + declarative_asn1.AnnotatedType( + rust_choice_type, declarative_asn1.Annotation() + ) + ) + if NoneType in union_args + else rust_choice_type + ) + + elif typing.get_origin(field_type) is builtins.list: + inner_type = _normalize_field_type( + typing.get_args(field_type)[0], field_name + ) + rust_field_type = declarative_asn1.Type.SequenceOf(inner_type) + elif typing.get_origin(field_type) is SetOf: + inner_type = _normalize_field_type( + typing.get_args(field_type)[0], field_name + ) + rust_field_type = declarative_asn1.Type.SetOf(inner_type) + else: + rust_field_type = declarative_asn1.non_root_python_to_rust(field_type) + + return declarative_asn1.AnnotatedType(rust_field_type, annotation) + + +# Convert a type to a Variant. Used with types inside Union +# annotations (T1, T2, etc in `Union[T1, T2, ...]`). +def _type_to_variant( + t: typing.Any, field_name: str +) -> declarative_asn1.Variant: + is_annotated = typing.get_origin(t) is typing.Annotated + inner_type = typing.get_args(t)[0] if is_annotated else t + + # Check if this is a Variant[T, Tag] type + if typing.get_origin(inner_type) is Variant: + value_type, tag_literal = typing.get_args(inner_type) + if typing.get_origin(tag_literal) is not typing.Literal: + raise TypeError( + "When using `asn1.Variant` in a type annotation, the second " + "type parameter must be a `typing.Literal` type. E.g: " + '`Variant[int, typing.Literal["MyInt"]]`.' + ) + tag_name = typing.get_args(tag_literal)[0] + + if hasattr(value_type, "__asn1_root__"): + rust_type = value_type.__asn1_root__ + else: + rust_type = declarative_asn1.non_root_python_to_rust(value_type) + + if is_annotated: + ann_type = declarative_asn1.AnnotatedType( + rust_type, + _extract_annotation(t.__metadata__, field_name), + ) + else: + ann_type = declarative_asn1.AnnotatedType( + rust_type, + declarative_asn1.Annotation(), + ) + + return declarative_asn1.Variant(Variant, ann_type, tag_name) + else: + # Plain type (not a tagged Variant) + return declarative_asn1.Variant( + inner_type, + _normalize_field_type(t, field_name), + None, + ) + + +def _annotate_fields( + raw_fields: dict[str, type], +) -> dict[str, declarative_asn1.AnnotatedType]: + fields = {} + for field_name, field_type in raw_fields.items(): + # Recursively normalize the field type into something that the + # Rust code can understand. + annotated_field_type = _normalize_field_type(field_type, field_name) + fields[field_name] = annotated_field_type + + return fields + + +def _register_asn1_sequence(cls: type[U]) -> None: + raw_fields = typing.get_type_hints(cls, include_extras=True) + root = declarative_asn1.Type.Sequence(cls, _annotate_fields(raw_fields)) + + setattr(cls, "__asn1_root__", root) + + +def _register_asn1_set(cls: type[U]) -> None: + raw_fields = typing.get_type_hints(cls, include_extras=True) + root = declarative_asn1.Type.Set(cls, _annotate_fields(raw_fields)) + + setattr(cls, "__asn1_root__", root) + + +# Due to https://github.com/python/mypy/issues/19731, we can't define an alias +# for `dataclass_transform` that conditionally points to `typing` or +# `typing_extensions` depending on the Python version. We work around it by +# making the whole decorated class conditional on the Python version. +if sys.version_info < (3, 11): + + @typing_extensions.dataclass_transform(kw_only_default=True) + def sequence(cls: type[U]) -> type[U]: + # We use `dataclasses.dataclass` to add an __init__ method + # to the class with keyword-only parameters. + if sys.version_info >= (3, 10): + dataclass_cls = dataclasses.dataclass( + repr=False, + eq=False, + # `match_args` was added in Python 3.10 and defaults + # to True + match_args=False, + # `kw_only` was added in Python 3.10 and defaults to + # False + kw_only=True, + )(cls) + else: + dataclass_cls = dataclasses.dataclass( + repr=False, + eq=False, + )(cls) + _register_asn1_sequence(dataclass_cls) + return dataclass_cls + + @typing_extensions.dataclass_transform(kw_only_default=True) + def set(cls: type[U]) -> type[U]: + # We use `dataclasses.dataclass` to add an __init__ method + # to the class with keyword-only parameters. + if sys.version_info >= (3, 10): + dataclass_cls = dataclasses.dataclass( + repr=False, + eq=False, + # `match_args` was added in Python 3.10 and defaults + # to True + match_args=False, + # `kw_only` was added in Python 3.10 and defaults to + # False + kw_only=True, + )(cls) + else: + dataclass_cls = dataclasses.dataclass( + repr=False, + eq=False, + )(cls) + _register_asn1_set(dataclass_cls) + return dataclass_cls + +else: + + @typing.dataclass_transform(kw_only_default=True) + def sequence(cls: type[U]) -> type[U]: + # Only add an __init__ method, with keyword-only + # parameters. + dataclass_cls = dataclasses.dataclass( + repr=False, + eq=False, + match_args=False, + kw_only=True, + )(cls) + _register_asn1_sequence(dataclass_cls) + return dataclass_cls + + @typing.dataclass_transform(kw_only_default=True) + def set(cls: type[U]) -> type[U]: + # Only add an __init__ method, with keyword-only + # parameters. + dataclass_cls = dataclasses.dataclass( + repr=False, + eq=False, + match_args=False, + kw_only=True, + )(cls) + _register_asn1_set(dataclass_cls) + return dataclass_cls + + +def value_set( + value_type: type, +) -> typing.Callable[[type[U]], type[U]]: + """ + A class decorator that registers an `enum.Enum` subclass as an + ASN.1 value set of the given underlying type. All the member + values must be instances of `value_type`. Members are encoded as + their value; decoding fails if the decoded value does not match + any member. + """ + rust_type = declarative_asn1.non_root_python_to_rust(value_type) + + def decorator(cls: type[U]) -> type[U]: + if not issubclass(cls, enum.Enum): + raise TypeError( + "value sets can only be defined from enum.Enum subclasses" + ) + members = list(cls) + if not members: + raise TypeError( + f"value set '{cls.__name__}' must have at least one member" + ) + for member in members: + if not isinstance(member.value, value_type): + raise TypeError( + f"member '{member.name}' of value set '{cls.__name__}' " + f"must have a value of type " + f"'{value_type.__name__}', got: " + f"'{type(member.value).__name__}'" + ) + inner = declarative_asn1.AnnotatedType( + rust_type, declarative_asn1.Annotation() + ) + # Map from member value to member, used for O(1) lookups when + # decoding. This requires the member values to be hashable. + value_map = {member.value: member for member in members} + root = declarative_asn1.Type.ValueSet(cls, inner, value_map) + + setattr(cls, "__asn1_root__", root) + return cls + + return decorator + + +# TODO: replace with `Default[U]` once the min Python version is >= 3.12 +@dataclasses.dataclass(frozen=True) +class Default(typing.Generic[U]): + value: U + + +SetOf = declarative_asn1.SetOf + +Explicit = declarative_asn1.Encoding.Explicit +Implicit = declarative_asn1.Encoding.Implicit +Size = declarative_asn1.Size + +PrintableString = declarative_asn1.PrintableString +IA5String = declarative_asn1.IA5String +UTCTime = declarative_asn1.UTCTime +GeneralizedTime = declarative_asn1.GeneralizedTime +BitString = declarative_asn1.BitString +TLV = declarative_asn1.Tlv +Null = declarative_asn1.Null diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__init__.py new file mode 100644 index 0000000..b4400aa --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__init__.py @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from typing import Any + + +def default_backend() -> Any: + from cryptography.hazmat.backends.openssl.backend import backend + + return backend diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..3bac292 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__init__.py new file mode 100644 index 0000000..51b0447 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__init__.py @@ -0,0 +1,9 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.backends.openssl.backend import backend + +__all__ = ["backend"] diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b38aa29 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-312.pyc new file mode 100644 index 0000000..7e838ba Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/backend.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/backend.py new file mode 100644 index 0000000..0d02c4f --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/backend.py @@ -0,0 +1,309 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.bindings.openssl import binding +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils +from cryptography.hazmat.primitives.asymmetric.padding import ( + MGF1, + OAEP, + PSS, + PKCS1v15, +) +from cryptography.hazmat.primitives.ciphers import ( + CipherAlgorithm, +) +from cryptography.hazmat.primitives.ciphers.algorithms import ( + AES, +) +from cryptography.hazmat.primitives.ciphers.modes import ( + CBC, + Mode, +) + + +class Backend: + """ + OpenSSL API binding interfaces. + """ + + name = "openssl" + + # TripleDES encryption is disallowed/deprecated throughout 2023 in + # FIPS 140-3. To keep it simple we denylist any use of TripleDES (TDEA). + _fips_ciphers = (AES,) + # Sometimes SHA1 is still permissible. That logic is contained + # within the various *_supported methods. + _fips_hashes = ( + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + hashes.SHA512_224, + hashes.SHA512_256, + hashes.SHA3_224, + hashes.SHA3_256, + hashes.SHA3_384, + hashes.SHA3_512, + hashes.SHAKE128, + hashes.SHAKE256, + ) + _fips_ecdh_curves = ( + ec.SECP224R1, + ec.SECP256R1, + ec.SECP384R1, + ec.SECP521R1, + ) + _fips_rsa_min_key_size = 2048 + _fips_rsa_min_public_exponent = 65537 + _fips_dsa_min_modulus = 1 << 2048 + _fips_dh_min_key_size = 2048 + _fips_dh_min_modulus = 1 << _fips_dh_min_key_size + + def __init__(self) -> None: + self._binding = binding.Binding() + self._ffi = self._binding.ffi + self._lib = self._binding.lib + self._fips_enabled = rust_openssl.is_fips_enabled() + + def __repr__(self) -> str: + return ( + f"" + ) + + def openssl_assert(self, ok: bool) -> None: + return binding._openssl_assert(ok) + + def _enable_fips(self) -> None: + # This function enables FIPS mode for OpenSSL 3.0.0 on installs that + # have the FIPS provider installed properly. + rust_openssl.enable_fips(rust_openssl._providers) + assert rust_openssl.is_fips_enabled() + self._fips_enabled = rust_openssl.is_fips_enabled() + + def openssl_version_text(self) -> str: + """ + Friendly string name of the loaded OpenSSL library. This is not + necessarily the same version as it was compiled against. + + Example: OpenSSL 3.2.1 30 Jan 2024 + """ + return rust_openssl.openssl_version_text() + + def openssl_version_number(self) -> int: + return rust_openssl.openssl_version() + + def hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + if self._fips_enabled and not isinstance(algorithm, self._fips_hashes): + return False + + return rust_openssl.hashes.hash_supported(algorithm) + + def signature_hash_supported( + self, algorithm: hashes.HashAlgorithm + ) -> bool: + # Dedicated check for hashing algorithm use in message digest for + # signatures, e.g. RSA PKCS#1 v1.5 SHA1 (sha1WithRSAEncryption). + if self._fips_enabled and isinstance(algorithm, hashes.SHA1): + return False + return self.hash_supported(algorithm) + + def scrypt_supported(self) -> bool: + if self._fips_enabled: + return False + else: + return hasattr(rust_openssl.kdf.Scrypt, "derive") + + def argon2_supported(self) -> bool: + if self._fips_enabled: + return False + else: + return hasattr(rust_openssl.kdf.Argon2id, "derive") + + def hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + # FIPS mode still allows SHA1 for HMAC + if self._fips_enabled and isinstance(algorithm, hashes.SHA1): + return True + if rust_openssl.CRYPTOGRAPHY_IS_AWSLC: + return isinstance( + algorithm, + ( + hashes.MD5, + hashes.SHA1, + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + hashes.SHA512_224, + hashes.SHA512_256, + hashes.SHA3_224, + hashes.SHA3_256, + hashes.SHA3_384, + hashes.SHA3_512, + ), + ) + return self.hash_supported(algorithm) + + def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool: + if self._fips_enabled: + # FIPS mode requires AES. TripleDES is disallowed/deprecated in + # FIPS 140-3. + if not isinstance(cipher, self._fips_ciphers): + return False + + return rust_openssl.ciphers.cipher_supported(cipher, mode) + + def _consume_errors(self) -> list[rust_openssl.OpenSSLError]: + return rust_openssl.capture_error_stack() + + def _oaep_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + if self._fips_enabled and isinstance(algorithm, hashes.SHA1): + return False + + return isinstance( + algorithm, + ( + hashes.SHA1, + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + ), + ) + + def rsa_padding_supported(self, padding: AsymmetricPadding) -> bool: + if isinstance(padding, PKCS1v15): + return True + elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1): + # FIPS 186-4 only allows salt length == digest length for PSS + # It is technically acceptable to set an explicit salt length + # equal to the digest length and this will incorrectly fail, but + # since we don't do that in the tests and this method is + # private, we'll ignore that until we need to do otherwise. + if ( + self._fips_enabled + and padding._salt_length != PSS.DIGEST_LENGTH + ): + return False + return self.hash_supported(padding._mgf._algorithm) + elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1): + return self._oaep_hash_supported( + padding._mgf._algorithm + ) and self._oaep_hash_supported(padding._algorithm) + else: + return False + + def rsa_encryption_supported(self, padding: AsymmetricPadding) -> bool: + if self._fips_enabled and isinstance(padding, PKCS1v15): + return False + else: + return self.rsa_padding_supported(padding) + + def dsa_supported(self) -> bool: + return ( + not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + and not self._fips_enabled + ) + + def dsa_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + if not self.dsa_supported(): + return False + return self.signature_hash_supported(algorithm) + + def cmac_algorithm_supported(self, algorithm) -> bool: + return self.cipher_supported( + algorithm, CBC(b"\x00" * algorithm.block_size) + ) + + def elliptic_curve_supported(self, curve: ec.EllipticCurve) -> bool: + if self._fips_enabled and not isinstance( + curve, self._fips_ecdh_curves + ): + return False + + return rust_openssl.ec.curve_supported(curve) + + def elliptic_curve_signature_algorithm_supported( + self, + signature_algorithm: ec.EllipticCurveSignatureAlgorithm, + curve: ec.EllipticCurve, + ) -> bool: + # We only support ECDSA right now. + if not isinstance(signature_algorithm, ec.ECDSA): + return False + + return self.elliptic_curve_supported(curve) and ( + isinstance(signature_algorithm.algorithm, asym_utils.Prehashed) + or self.hash_supported(signature_algorithm.algorithm) + ) + + def elliptic_curve_exchange_algorithm_supported( + self, algorithm: ec.ECDH, curve: ec.EllipticCurve + ) -> bool: + return self.elliptic_curve_supported(curve) and isinstance( + algorithm, ec.ECDH + ) + + def dh_supported(self) -> bool: + return not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + + def dh_x942_serialization_supported(self) -> bool: + return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1 + + def x25519_supported(self) -> bool: + return not self._fips_enabled + + def x448_supported(self) -> bool: + if self._fips_enabled: + return False + return ( + not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL + and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + and not rust_openssl.CRYPTOGRAPHY_IS_AWSLC + ) + + def mlkem_supported(self) -> bool: + return ( + rust_openssl.CRYPTOGRAPHY_IS_AWSLC + or rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + or rust_openssl.CRYPTOGRAPHY_OPENSSL_350_OR_GREATER + ) + + def mldsa_supported(self) -> bool: + return ( + rust_openssl.CRYPTOGRAPHY_IS_AWSLC + or rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + or rust_openssl.CRYPTOGRAPHY_OPENSSL_350_OR_GREATER + ) + + def ed25519_supported(self) -> bool: + return True + + def ed448_supported(self) -> bool: + return ( + not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL + and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + and not rust_openssl.CRYPTOGRAPHY_IS_AWSLC + ) + + def ecdsa_deterministic_supported(self) -> bool: + return ( + rust_openssl.CRYPTOGRAPHY_OPENSSL_320_OR_GREATER + and not self._fips_enabled + ) + + def poly1305_supported(self) -> bool: + if rust_openssl.CRYPTOGRAPHY_IS_AWSLC: + return True + return not self._fips_enabled + + +backend = Backend() diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__init__.py new file mode 100644 index 0000000..41d7318 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__init__.py @@ -0,0 +1,5 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..fb3aed7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust.abi3.so b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust.abi3.so new file mode 100755 index 0000000..fddbd2a Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust.abi3.so differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi new file mode 100644 index 0000000..c3148f1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi @@ -0,0 +1,67 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives import padding +from cryptography.hazmat.primitives._serialization import ( + KeySerializationEncryptionBuilder, +) +from cryptography.utils import Buffer + +class PKCS7PaddingContext(padding.PaddingContext): + def __init__(self, block_size: int) -> None: ... + def update(self, data: Buffer) -> bytes: ... + def finalize(self) -> bytes: ... + +class ANSIX923PaddingContext(padding.PaddingContext): + def __init__(self, block_size: int) -> None: ... + def update(self, data: Buffer) -> bytes: ... + def finalize(self) -> bytes: ... + +class PKCS7UnpaddingContext(padding.PaddingContext): + def __init__(self, block_size: int) -> None: ... + def update(self, data: Buffer) -> bytes: ... + def finalize(self) -> bytes: ... + +class ANSIX923UnpaddingContext(padding.PaddingContext): + def __init__(self, block_size: int) -> None: ... + def update(self, data: Buffer) -> bytes: ... + def finalize(self) -> bytes: ... + +class Encoding: + PEM: typing.ClassVar[Encoding] + DER: typing.ClassVar[Encoding] + OpenSSH: typing.ClassVar[Encoding] + Raw: typing.ClassVar[Encoding] + X962: typing.ClassVar[Encoding] + SMIME: typing.ClassVar[Encoding] + +class PrivateFormat: + PKCS8: typing.ClassVar[PrivateFormat] + TraditionalOpenSSL: typing.ClassVar[PrivateFormat] + Raw: typing.ClassVar[PrivateFormat] + OpenSSH: typing.ClassVar[PrivateFormat] + PKCS12: typing.ClassVar[PrivateFormat] + def encryption_builder(self) -> KeySerializationEncryptionBuilder: ... + +class PublicFormat: + SubjectPublicKeyInfo: typing.ClassVar[PublicFormat] + PKCS1: typing.ClassVar[PublicFormat] + OpenSSH: typing.ClassVar[PublicFormat] + Raw: typing.ClassVar[PublicFormat] + CompressedPoint: typing.ClassVar[PublicFormat] + UncompressedPoint: typing.ClassVar[PublicFormat] + +class ParameterFormat: + PKCS3: typing.ClassVar[ParameterFormat] + +class ObjectIdentifier: + def __init__(self, value: str) -> None: ... + @property + def dotted_string(self) -> str: ... + @property + def _name(self) -> str: ... + +T = typing.TypeVar("T") diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi new file mode 100644 index 0000000..3d4ea4e --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi @@ -0,0 +1,8 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +lib: typing.Any +ffi: typing.Any diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi new file mode 100644 index 0000000..3b5f208 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi @@ -0,0 +1,7 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +def decode_dss_signature(signature: bytes) -> tuple[int, int]: ... +def encode_dss_signature(r: int, s: int) -> bytes: ... +def parse_spki_for_data(data: bytes) -> bytes: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/cobblestone.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/cobblestone.pyi new file mode 100644 index 0000000..95e9583 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/cobblestone.pyi @@ -0,0 +1,33 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.utils import Buffer + +class Cobblestone128Encryptor: + def __init__(self, key: Buffer, context: Buffer) -> None: ... + @staticmethod + def generate_key() -> bytes: ... + def update(self, data: Buffer) -> bytes: ... + def update_into(self, data: Buffer, buf: Buffer) -> int: ... + def finalize(self) -> bytes: ... + +class Cobblestone128Decryptor: + def __init__(self, key: Buffer, context: Buffer) -> None: ... + def update(self, data: Buffer) -> bytes: ... + def update_into(self, data: Buffer, buf: Buffer) -> int: ... + def finalize(self) -> bytes: ... + +class Cobblestone256Encryptor: + def __init__(self, key: Buffer, context: Buffer) -> None: ... + @staticmethod + def generate_key() -> bytes: ... + def update(self, data: Buffer) -> bytes: ... + def update_into(self, data: Buffer, buf: Buffer) -> int: ... + def finalize(self) -> bytes: ... + +class Cobblestone256Decryptor: + def __init__(self, key: Buffer, context: Buffer) -> None: ... + def update(self, data: Buffer) -> bytes: ... + def update_into(self, data: Buffer, buf: Buffer) -> int: ... + def finalize(self) -> bytes: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi new file mode 100644 index 0000000..66fc065 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi @@ -0,0 +1,133 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import datetime +import typing + +T = typing.TypeVar("T") + +def decode_der(cls: type[T], value: bytes) -> T: ... +def encode_der(value: typing.Any) -> bytes: ... +def non_root_python_to_rust(cls: type) -> Type: ... + +# Type is a Rust enum with tuple variants. For now, we express the type +# annotations like this: +class Type: + Sequence: typing.ClassVar[type] + SequenceOf: typing.ClassVar[type] + Set: typing.ClassVar[type] + SetOf: typing.ClassVar[type] + Option: typing.ClassVar[type] + Choice: typing.ClassVar[type] + ValueSet: typing.ClassVar[type] + PyBool: typing.ClassVar[type] + PyInt: typing.ClassVar[type] + PyBytes: typing.ClassVar[type] + PyStr: typing.ClassVar[type] + +class Annotation: + default: typing.Any | None + encoding: Encoding | None + size: Size | None + def __new__( + cls, + default: typing.Any | None = None, + encoding: Encoding | None = None, + size: Size | None = None, + ) -> Annotation: ... + def is_empty(self) -> bool: ... + +# Encoding is a Rust enum with tuple variants. For now, we express the type +# annotations like this: +class Encoding: + Implicit: typing.ClassVar[type] + Explicit: typing.ClassVar[type] + +class Size: + min: int + max: int | None + + def __new__(cls, min: int, max: int | None) -> Size: ... + @staticmethod + def exact(n: int) -> Size: ... + +class AnnotatedType: + inner: Type + annotation: Annotation + + def __new__(cls, inner: Type, annotation: Annotation) -> AnnotatedType: ... + +class AnnotatedTypeObject: + annotated_type: AnnotatedType + value: typing.Any + + def __new__( + cls, annotated_type: AnnotatedType, value: typing.Any + ) -> AnnotatedTypeObject: ... + +class Variant: + python_class: type + ann_type: AnnotatedType + tag_name: str | None + + def __new__( + cls, + python_class: type, + ann_type: AnnotatedType, + tag_name: str | None, + ) -> Variant: ... + +class PrintableString: + def __new__(cls, inner: str) -> PrintableString: ... + def __repr__(self) -> str: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + def as_str(self) -> str: ... + +class IA5String: + def __new__(cls, inner: str) -> IA5String: ... + def __repr__(self) -> str: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + def as_str(self) -> str: ... + +class UTCTime: + def __new__(cls, inner: datetime.datetime) -> UTCTime: ... + def __repr__(self) -> str: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + def as_datetime(self) -> datetime.datetime: ... + +class GeneralizedTime: + def __new__(cls, inner: datetime.datetime) -> GeneralizedTime: ... + def __repr__(self) -> str: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + def as_datetime(self) -> datetime.datetime: ... + +class BitString: + def __new__(cls, data: bytes, padding_bits: int) -> BitString: ... + def __repr__(self) -> str: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + def as_bytes(self) -> bytes: ... + def padding_bits(self) -> int: ... + +class Tlv: + @property + def tag_bytes(self) -> bytes: ... + @property + def data(self) -> memoryview: ... + def parse(self, cls: type): ... + +class SetOf(typing.Generic[T]): + def __new__(cls, inner: list[T]) -> SetOf[T]: ... + def as_list(self) -> list[T]: ... + def __eq__(self, other: object) -> bool: ... + def __repr__(self) -> str: ... + +class Null: + def __new__(cls) -> Null: ... + def __repr__(self) -> str: ... + def __eq__(self, other: object) -> bool: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi new file mode 100644 index 0000000..09f46b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi @@ -0,0 +1,17 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +class _Reasons: + BACKEND_MISSING_INTERFACE: _Reasons + UNSUPPORTED_HASH: _Reasons + UNSUPPORTED_CIPHER: _Reasons + UNSUPPORTED_PADDING: _Reasons + UNSUPPORTED_MGF: _Reasons + UNSUPPORTED_PUBLIC_KEY_ALGORITHM: _Reasons + UNSUPPORTED_ELLIPTIC_CURVE: _Reasons + UNSUPPORTED_SERIALIZATION: _Reasons + UNSUPPORTED_X509: _Reasons + UNSUPPORTED_EXCHANGE_ALGORITHM: _Reasons + UNSUPPORTED_DIFFIE_HELLMAN: _Reasons + UNSUPPORTED_MAC: _Reasons diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi new file mode 100644 index 0000000..103e96c --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi @@ -0,0 +1,117 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import datetime +from collections.abc import Iterator + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes +from cryptography.x509 import ocsp + +class OCSPRequest: + @property + def issuer_key_hash(self) -> bytes: ... + @property + def issuer_name_hash(self) -> bytes: ... + @property + def hash_algorithm(self) -> hashes.HashAlgorithm: ... + @property + def serial_number(self) -> int: ... + def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... + @property + def extensions(self) -> x509.Extensions: ... + +class OCSPResponse: + @property + def responses(self) -> Iterator[OCSPSingleResponse]: ... + @property + def response_status(self) -> ocsp.OCSPResponseStatus: ... + @property + def signature_algorithm_oid(self) -> x509.ObjectIdentifier: ... + @property + def signature_hash_algorithm( + self, + ) -> hashes.HashAlgorithm | None: ... + @property + def signature(self) -> bytes: ... + @property + def tbs_response_bytes(self) -> bytes: ... + @property + def certificates(self) -> list[x509.Certificate]: ... + @property + def responder_key_hash(self) -> bytes | None: ... + @property + def responder_name(self) -> x509.Name | None: ... + @property + def produced_at(self) -> datetime.datetime: ... + @property + def produced_at_utc(self) -> datetime.datetime: ... + @property + def certificate_status(self) -> ocsp.OCSPCertStatus: ... + @property + def revocation_time(self) -> datetime.datetime | None: ... + @property + def revocation_time_utc(self) -> datetime.datetime | None: ... + @property + def revocation_reason(self) -> x509.ReasonFlags | None: ... + @property + def this_update(self) -> datetime.datetime: ... + @property + def this_update_utc(self) -> datetime.datetime: ... + @property + def next_update(self) -> datetime.datetime | None: ... + @property + def next_update_utc(self) -> datetime.datetime | None: ... + @property + def issuer_key_hash(self) -> bytes: ... + @property + def issuer_name_hash(self) -> bytes: ... + @property + def hash_algorithm(self) -> hashes.HashAlgorithm: ... + @property + def serial_number(self) -> int: ... + @property + def extensions(self) -> x509.Extensions: ... + @property + def single_extensions(self) -> x509.Extensions: ... + def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... + +class OCSPSingleResponse: + @property + def certificate_status(self) -> ocsp.OCSPCertStatus: ... + @property + def revocation_time(self) -> datetime.datetime | None: ... + @property + def revocation_time_utc(self) -> datetime.datetime | None: ... + @property + def revocation_reason(self) -> x509.ReasonFlags | None: ... + @property + def this_update(self) -> datetime.datetime: ... + @property + def this_update_utc(self) -> datetime.datetime: ... + @property + def next_update(self) -> datetime.datetime | None: ... + @property + def next_update_utc(self) -> datetime.datetime | None: ... + @property + def issuer_key_hash(self) -> bytes: ... + @property + def issuer_name_hash(self) -> bytes: ... + @property + def hash_algorithm(self) -> hashes.HashAlgorithm: ... + @property + def serial_number(self) -> int: ... + +def load_der_ocsp_request(data: bytes) -> ocsp.OCSPRequest: ... +def load_der_ocsp_response(data: bytes) -> ocsp.OCSPResponse: ... +def create_ocsp_request( + builder: ocsp.OCSPRequestBuilder, +) -> ocsp.OCSPRequest: ... +def create_ocsp_response( + status: ocsp.OCSPResponseStatus, + builder: ocsp.OCSPResponseBuilder | None, + private_key: PrivateKeyTypes | None, + hash_algorithm: hashes.HashAlgorithm | None, +) -> ocsp.OCSPResponse: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi new file mode 100644 index 0000000..e41ba78 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi @@ -0,0 +1,82 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.bindings._rust.openssl import ( + aead, + ciphers, + cmac, + dh, + dsa, + ec, + ed448, + ed25519, + hashes, + hmac, + hpke, + kdf, + keys, + keywrap, + mldsa, + mlkem, + poly1305, + rsa, + x448, + x25519, +) + +__all__ = [ + "aead", + "ciphers", + "cmac", + "dh", + "dsa", + "ec", + "ed448", + "ed25519", + "hashes", + "hmac", + "hpke", + "kdf", + "keys", + "keywrap", + "mldsa", + "mlkem", + "openssl_version", + "openssl_version_text", + "poly1305", + "raise_openssl_error", + "rsa", + "x448", + "x25519", +] + +CRYPTOGRAPHY_IS_LIBRESSL: bool +CRYPTOGRAPHY_IS_BORINGSSL: bool +CRYPTOGRAPHY_IS_AWSLC: bool +CRYPTOGRAPHY_OPENSSL_309_OR_GREATER: bool +CRYPTOGRAPHY_OPENSSL_320_OR_GREATER: bool +CRYPTOGRAPHY_OPENSSL_330_OR_GREATER: bool +CRYPTOGRAPHY_OPENSSL_350_OR_GREATER: bool + +class Providers: ... + +_legacy_provider_loaded: bool +_providers: Providers + +def openssl_version() -> int: ... +def openssl_version_text() -> str: ... +def raise_openssl_error() -> typing.NoReturn: ... +def capture_error_stack() -> list[OpenSSLError]: ... +def is_fips_enabled() -> bool: ... +def enable_fips(providers: Providers) -> None: ... + +class OpenSSLError: + @property + def lib(self) -> int: ... + @property + def reason(self) -> int: ... + @property + def reason_text(self) -> bytes: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi new file mode 100644 index 0000000..eb44608 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi @@ -0,0 +1,189 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from collections.abc import Sequence + +from cryptography.utils import Buffer + +class AESGCM: + def __init__(self, key: Buffer) -> None: ... + @staticmethod + def generate_key(bit_length: int) -> bytes: ... + def encrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def decrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def encrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + def decrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + +class ChaCha20Poly1305: + def __init__(self, key: Buffer) -> None: ... + @staticmethod + def generate_key() -> bytes: ... + def encrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def encrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + def decrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def decrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + +class AESCCM: + def __init__(self, key: Buffer, tag_length: int = 16) -> None: ... + @staticmethod + def generate_key(bit_length: int) -> bytes: ... + def encrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def encrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + def decrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def decrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + +class AESSIV: + def __init__(self, key: Buffer) -> None: ... + @staticmethod + def generate_key(bit_length: int) -> bytes: ... + def encrypt( + self, + data: Buffer, + associated_data: Sequence[Buffer] | None, + ) -> bytes: ... + def encrypt_into( + self, + data: Buffer, + associated_data: Sequence[Buffer] | None, + buf: Buffer, + ) -> int: ... + def decrypt( + self, + data: Buffer, + associated_data: Sequence[Buffer] | None, + ) -> bytes: ... + def decrypt_into( + self, + data: Buffer, + associated_data: Sequence[Buffer] | None, + buf: Buffer, + ) -> int: ... + +class AESOCB3: + def __init__(self, key: Buffer) -> None: ... + @staticmethod + def generate_key(bit_length: int) -> bytes: ... + def encrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def encrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + def decrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def decrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + +class AESGCMSIV: + def __init__(self, key: Buffer) -> None: ... + @staticmethod + def generate_key(bit_length: int) -> bytes: ... + def encrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def encrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + def decrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def decrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi new file mode 100644 index 0000000..a48fb01 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi @@ -0,0 +1,38 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives import ciphers +from cryptography.hazmat.primitives.ciphers import modes + +@typing.overload +def create_encryption_ctx( + algorithm: ciphers.CipherAlgorithm, mode: modes.ModeWithAuthenticationTag +) -> ciphers.AEADEncryptionContext: ... +@typing.overload +def create_encryption_ctx( + algorithm: ciphers.CipherAlgorithm, mode: modes.Mode | None +) -> ciphers.CipherContext: ... +@typing.overload +def create_decryption_ctx( + algorithm: ciphers.CipherAlgorithm, mode: modes.ModeWithAuthenticationTag +) -> ciphers.AEADDecryptionContext: ... +@typing.overload +def create_decryption_ctx( + algorithm: ciphers.CipherAlgorithm, mode: modes.Mode | None +) -> ciphers.CipherContext: ... +def cipher_supported( + algorithm: ciphers.CipherAlgorithm, mode: modes.Mode +) -> bool: ... +def _advance( + ctx: ciphers.AEADEncryptionContext | ciphers.AEADDecryptionContext, n: int +) -> None: ... +def _advance_aad( + ctx: ciphers.AEADEncryptionContext | ciphers.AEADDecryptionContext, n: int +) -> None: ... + +class CipherContext: ... +class AEADEncryptionContext: ... +class AEADDecryptionContext: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi new file mode 100644 index 0000000..9c03508 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi @@ -0,0 +1,18 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives import ciphers + +class CMAC: + def __init__( + self, + algorithm: ciphers.BlockCipherAlgorithm, + backend: typing.Any = None, + ) -> None: ... + def update(self, data: bytes) -> None: ... + def finalize(self) -> bytes: ... + def verify(self, signature: bytes) -> None: ... + def copy(self) -> CMAC: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi new file mode 100644 index 0000000..08733d7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi @@ -0,0 +1,51 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives.asymmetric import dh + +MIN_MODULUS_SIZE: int + +class DHPrivateKey: ... +class DHPublicKey: ... +class DHParameters: ... + +class DHPrivateNumbers: + def __init__(self, x: int, public_numbers: DHPublicNumbers) -> None: ... + def private_key(self, backend: typing.Any = None) -> dh.DHPrivateKey: ... + @property + def x(self) -> int: ... + @property + def public_numbers(self) -> DHPublicNumbers: ... + +class DHPublicNumbers: + def __init__( + self, y: int, parameter_numbers: DHParameterNumbers + ) -> None: ... + def public_key(self, backend: typing.Any = None) -> dh.DHPublicKey: ... + @property + def y(self) -> int: ... + @property + def parameter_numbers(self) -> DHParameterNumbers: ... + +class DHParameterNumbers: + def __init__(self, p: int, g: int, q: int | None = None) -> None: ... + def parameters(self, backend: typing.Any = None) -> dh.DHParameters: ... + @property + def p(self) -> int: ... + @property + def g(self) -> int: ... + @property + def q(self) -> int | None: ... + +def generate_parameters( + generator: int, key_size: int, backend: typing.Any = None +) -> dh.DHParameters: ... +def from_pem_parameters( + data: bytes, backend: typing.Any = None +) -> dh.DHParameters: ... +def from_der_parameters( + data: bytes, backend: typing.Any = None +) -> dh.DHParameters: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi new file mode 100644 index 0000000..0922a4c --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi @@ -0,0 +1,41 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives.asymmetric import dsa + +class DSAPrivateKey: ... +class DSAPublicKey: ... +class DSAParameters: ... + +class DSAPrivateNumbers: + def __init__(self, x: int, public_numbers: DSAPublicNumbers) -> None: ... + @property + def x(self) -> int: ... + @property + def public_numbers(self) -> DSAPublicNumbers: ... + def private_key(self, backend: typing.Any = None) -> dsa.DSAPrivateKey: ... + +class DSAPublicNumbers: + def __init__( + self, y: int, parameter_numbers: DSAParameterNumbers + ) -> None: ... + @property + def y(self) -> int: ... + @property + def parameter_numbers(self) -> DSAParameterNumbers: ... + def public_key(self, backend: typing.Any = None) -> dsa.DSAPublicKey: ... + +class DSAParameterNumbers: + def __init__(self, p: int, q: int, g: int) -> None: ... + @property + def p(self) -> int: ... + @property + def q(self) -> int: ... + @property + def g(self) -> int: ... + def parameters(self, backend: typing.Any = None) -> dsa.DSAParameters: ... + +def generate_parameters(key_size: int) -> dsa.DSAParameters: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi new file mode 100644 index 0000000..5c3b7bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi @@ -0,0 +1,52 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives.asymmetric import ec + +class ECPrivateKey: ... +class ECPublicKey: ... + +class EllipticCurvePrivateNumbers: + def __init__( + self, private_value: int, public_numbers: EllipticCurvePublicNumbers + ) -> None: ... + def private_key( + self, backend: typing.Any = None + ) -> ec.EllipticCurvePrivateKey: ... + @property + def private_value(self) -> int: ... + @property + def public_numbers(self) -> EllipticCurvePublicNumbers: ... + +class EllipticCurvePublicNumbers: + def __init__(self, x: int, y: int, curve: ec.EllipticCurve) -> None: ... + def public_key( + self, backend: typing.Any = None + ) -> ec.EllipticCurvePublicKey: ... + @property + def x(self) -> int: ... + @property + def y(self) -> int: ... + @property + def curve(self) -> ec.EllipticCurve: ... + def __eq__(self, other: object) -> bool: ... + +def curve_supported(curve: ec.EllipticCurve) -> bool: ... +def generate_private_key( + curve: ec.EllipticCurve, backend: typing.Any = None +) -> ec.EllipticCurvePrivateKey: ... +def from_private_numbers( + numbers: ec.EllipticCurvePrivateNumbers, +) -> ec.EllipticCurvePrivateKey: ... +def from_public_numbers( + numbers: ec.EllipticCurvePublicNumbers, +) -> ec.EllipticCurvePublicKey: ... +def from_public_bytes( + curve: ec.EllipticCurve, data: bytes +) -> ec.EllipticCurvePublicKey: ... +def derive_private_key( + private_value: int, curve: ec.EllipticCurve +) -> ec.EllipticCurvePrivateKey: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi new file mode 100644 index 0000000..f85b3d1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives.asymmetric import ed25519 +from cryptography.utils import Buffer + +class Ed25519PrivateKey: ... +class Ed25519PublicKey: ... + +def generate_key() -> ed25519.Ed25519PrivateKey: ... +def from_private_bytes(data: Buffer) -> ed25519.Ed25519PrivateKey: ... +def from_public_bytes(data: bytes) -> ed25519.Ed25519PublicKey: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi new file mode 100644 index 0000000..c8ca0ec --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives.asymmetric import ed448 +from cryptography.utils import Buffer + +class Ed448PrivateKey: ... +class Ed448PublicKey: ... + +def generate_key() -> ed448.Ed448PrivateKey: ... +def from_private_bytes(data: Buffer) -> ed448.Ed448PrivateKey: ... +def from_public_bytes(data: bytes) -> ed448.Ed448PublicKey: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi new file mode 100644 index 0000000..106b531 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi @@ -0,0 +1,30 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives import hashes +from cryptography.utils import Buffer + +class Hash(hashes.HashContext): + def __init__( + self, algorithm: hashes.HashAlgorithm, backend: typing.Any = None + ) -> None: ... + @property + def algorithm(self) -> hashes.HashAlgorithm: ... + def update(self, data: Buffer) -> None: ... + def finalize(self) -> bytes: ... + def copy(self) -> Hash: ... + @staticmethod + def hash(algorithm: hashes.HashAlgorithm, data: Buffer) -> bytes: ... + +def hash_supported(algorithm: hashes.HashAlgorithm) -> bool: ... + +class XOFHash: + def __init__(self, algorithm: hashes.ExtendableOutputFunction) -> None: ... + @property + def algorithm(self) -> hashes.ExtendableOutputFunction: ... + def update(self, data: Buffer) -> None: ... + def squeeze(self, length: int) -> bytes: ... + def copy(self) -> XOFHash: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi new file mode 100644 index 0000000..3883d1b --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi @@ -0,0 +1,22 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives import hashes +from cryptography.utils import Buffer + +class HMAC(hashes.HashContext): + def __init__( + self, + key: Buffer, + algorithm: hashes.HashAlgorithm, + backend: typing.Any = None, + ) -> None: ... + @property + def algorithm(self) -> hashes.HashAlgorithm: ... + def update(self, data: Buffer) -> None: ... + def finalize(self) -> bytes: ... + def verify(self, signature: bytes) -> None: ... + def copy(self) -> HMAC: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hpke.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hpke.pyi new file mode 100644 index 0000000..7ea1711 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hpke.pyi @@ -0,0 +1,109 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives.asymmetric import ec, mlkem, x25519 +from cryptography.utils import Buffer + +class KEM: + X25519: KEM + P256: KEM + P384: KEM + P521: KEM + MLKEM768: KEM + MLKEM1024: KEM + MLKEM768_X25519: KEM + MLKEM1024_P384: KEM + def enc_length(self) -> int: ... + +class KDF: + HKDF_SHA256: KDF + HKDF_SHA384: KDF + HKDF_SHA512: KDF + SHAKE128: KDF + SHAKE256: KDF + +class AEAD: + AES_128_GCM: AEAD + AES_256_GCM: AEAD + CHACHA20_POLY1305: AEAD + +class MLKEM768X25519PrivateKey: + def __init__( + self, + mlkem_key: mlkem.MLKEM768PrivateKey, + x25519_key: x25519.X25519PrivateKey, + ) -> None: ... + def public_key(self) -> MLKEM768X25519PublicKey: ... + +class MLKEM768X25519PublicKey: + def __init__( + self, + mlkem_key: mlkem.MLKEM768PublicKey, + x25519_key: x25519.X25519PublicKey, + ) -> None: ... + +class MLKEM1024P384PrivateKey: + def __init__( + self, + mlkem_key: mlkem.MLKEM1024PrivateKey, + p384_key: ec.EllipticCurvePrivateKey, + ) -> None: ... + def public_key(self) -> MLKEM1024P384PublicKey: ... + +class MLKEM1024P384PublicKey: + def __init__( + self, + mlkem_key: mlkem.MLKEM1024PublicKey, + p384_key: ec.EllipticCurvePublicKey, + ) -> None: ... + +class Suite: + def __init__(self, kem: KEM, kdf: KDF, aead: AEAD) -> None: ... + def encrypt( + self, + plaintext: Buffer, + public_key: x25519.X25519PublicKey + | ec.EllipticCurvePublicKey + | mlkem.MLKEM768PublicKey + | mlkem.MLKEM1024PublicKey + | MLKEM768X25519PublicKey + | MLKEM1024P384PublicKey, + info: Buffer | None = None, + ) -> bytes: ... + def decrypt( + self, + ciphertext: Buffer, + private_key: x25519.X25519PrivateKey + | ec.EllipticCurvePrivateKey + | mlkem.MLKEM768PrivateKey + | mlkem.MLKEM1024PrivateKey + | MLKEM768X25519PrivateKey + | MLKEM1024P384PrivateKey, + info: Buffer | None = None, + ) -> bytes: ... + +def _encrypt_with_aad( + suite: Suite, + plaintext: Buffer, + public_key: x25519.X25519PublicKey + | ec.EllipticCurvePublicKey + | mlkem.MLKEM768PublicKey + | mlkem.MLKEM1024PublicKey + | MLKEM768X25519PublicKey + | MLKEM1024P384PublicKey, + info: Buffer | None = None, + aad: Buffer | None = None, +) -> bytes: ... +def _decrypt_with_aad( + suite: Suite, + ciphertext: Buffer, + private_key: x25519.X25519PrivateKey + | ec.EllipticCurvePrivateKey + | mlkem.MLKEM768PrivateKey + | mlkem.MLKEM1024PrivateKey + | MLKEM768X25519PrivateKey + | MLKEM1024P384PrivateKey, + info: Buffer | None = None, + aad: Buffer | None = None, +) -> bytes: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi new file mode 100644 index 0000000..bb37b96 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi @@ -0,0 +1,205 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives.hashes import HashAlgorithm +from cryptography.hazmat.primitives.kdf.kbkdf import CounterLocation, Mode +from cryptography.utils import Buffer + +class PBKDF2HMAC: + def __init__( + self, + algorithm: HashAlgorithm, + length: int, + salt: bytes, + iterations: int, + backend: typing.Any = None, + ) -> None: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class Scrypt: + def __init__( + self, + salt: bytes, + length: int, + n: int, + r: int, + p: int, + backend: typing.Any = None, + ) -> None: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class Argon2d: + def __init__( + self, + *, + salt: bytes, + length: int, + iterations: int, + lanes: int, + memory_cost: int, + ad: bytes | None = None, + secret: bytes | None = None, + ) -> None: ... + def derive(self, key_material: bytes) -> bytes: ... + def derive_into(self, key_material: bytes, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + def derive_phc_encoded(self, key_material: bytes) -> str: ... + @classmethod + def verify_phc_encoded( + cls, key_material: bytes, phc_encoded: str, secret: bytes | None = None + ) -> None: ... + +class Argon2i: + def __init__( + self, + *, + salt: bytes, + length: int, + iterations: int, + lanes: int, + memory_cost: int, + ad: bytes | None = None, + secret: bytes | None = None, + ) -> None: ... + def derive(self, key_material: bytes) -> bytes: ... + def derive_into(self, key_material: bytes, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + def derive_phc_encoded(self, key_material: bytes) -> str: ... + @classmethod + def verify_phc_encoded( + cls, key_material: bytes, phc_encoded: str, secret: bytes | None = None + ) -> None: ... + +class Argon2id: + def __init__( + self, + *, + salt: bytes, + length: int, + iterations: int, + lanes: int, + memory_cost: int, + ad: bytes | None = None, + secret: bytes | None = None, + ) -> None: ... + def derive(self, key_material: bytes) -> bytes: ... + def derive_into(self, key_material: bytes, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + def derive_phc_encoded(self, key_material: bytes) -> str: ... + @classmethod + def verify_phc_encoded( + cls, key_material: bytes, phc_encoded: str, secret: bytes | None = None + ) -> None: ... + +class HKDF: + def __init__( + self, + algorithm: HashAlgorithm, + length: int, + salt: bytes | None, + info: bytes | None, + backend: typing.Any = None, + ): ... + @staticmethod + def extract( + algorithm: HashAlgorithm, salt: bytes | None, key_material: Buffer + ) -> bytes: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class HKDFExpand: + def __init__( + self, + algorithm: HashAlgorithm, + length: int, + info: bytes | None, + backend: typing.Any = None, + ): ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class X963KDF: + def __init__( + self, + algorithm: HashAlgorithm, + length: int, + sharedinfo: bytes | None, + backend: typing.Any = None, + ) -> None: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class ConcatKDFHash: + def __init__( + self, + algorithm: HashAlgorithm, + length: int, + otherinfo: bytes | None, + backend: typing.Any = None, + ) -> None: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class ConcatKDFHMAC: + def __init__( + self, + algorithm: HashAlgorithm, + length: int, + salt: bytes | None, + otherinfo: bytes | None, + backend: typing.Any = None, + ) -> None: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class KBKDFHMAC: + def __init__( + self, + algorithm: HashAlgorithm, + mode: Mode, + length: int, + rlen: int, + llen: int | None, + location: CounterLocation, + label: bytes | None, + context: bytes | None, + fixed: bytes | None, + backend: typing.Any = None, + *, + break_location: int | None = None, + ) -> None: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class KBKDFCMAC: + def __init__( + self, + algorithm: typing.Any, + mode: Mode, + length: int, + rlen: int, + llen: int | None, + location: CounterLocation, + label: bytes | None, + context: bytes | None, + fixed: bytes | None, + backend: typing.Any = None, + *, + break_location: int | None = None, + ) -> None: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi new file mode 100644 index 0000000..404057e --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi @@ -0,0 +1,34 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives.asymmetric.types import ( + PrivateKeyTypes, + PublicKeyTypes, +) +from cryptography.utils import Buffer + +def load_der_private_key( + data: Buffer, + password: bytes | None, + backend: typing.Any = None, + *, + unsafe_skip_rsa_key_validation: bool = False, +) -> PrivateKeyTypes: ... +def load_pem_private_key( + data: Buffer, + password: bytes | None, + backend: typing.Any = None, + *, + unsafe_skip_rsa_key_validation: bool = False, +) -> PrivateKeyTypes: ... +def load_der_public_key( + data: bytes, + backend: typing.Any = None, +) -> PublicKeyTypes: ... +def load_pem_public_key( + data: bytes, + backend: typing.Any = None, +) -> PublicKeyTypes: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keywrap.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keywrap.pyi new file mode 100644 index 0000000..dd445ea --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keywrap.pyi @@ -0,0 +1,28 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.utils import Buffer + +def aes_key_wrap( + wrapping_key: Buffer, + key_to_wrap: Buffer, + backend: typing.Any = None, +) -> bytes: ... +def aes_key_unwrap( + wrapping_key: Buffer, + wrapped_key: Buffer, + backend: typing.Any = None, +) -> bytes: ... +def aes_key_wrap_with_padding( + wrapping_key: Buffer, + key_to_wrap: Buffer, + backend: typing.Any = None, +) -> bytes: ... +def aes_key_unwrap_with_padding( + wrapping_key: Buffer, + wrapped_key: Buffer, + backend: typing.Any = None, +) -> bytes: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi new file mode 100644 index 0000000..cea9cb1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi @@ -0,0 +1,35 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives.asymmetric import mldsa +from cryptography.utils import Buffer + +class MLDSA44PrivateKey: ... +class MLDSA44PublicKey: ... +class MLDSA65PrivateKey: ... +class MLDSA65PublicKey: ... +class MLDSA87PrivateKey: ... +class MLDSA87PublicKey: ... + +class MLDSAMuHasher: + def __init__( + self, + public_key: mldsa.MLDSA44PublicKey + | mldsa.MLDSA65PublicKey + | mldsa.MLDSA87PublicKey, + context: Buffer | None = None, + ) -> None: ... + def update(self, data: Buffer) -> None: ... + def copy(self) -> MLDSAMuHasher: ... + def finalize(self) -> bytes: ... + +def generate_mldsa44_key() -> mldsa.MLDSA44PrivateKey: ... +def from_mldsa44_public_bytes(data: bytes) -> mldsa.MLDSA44PublicKey: ... +def from_mldsa44_seed_bytes(data: Buffer) -> mldsa.MLDSA44PrivateKey: ... +def generate_mldsa65_key() -> mldsa.MLDSA65PrivateKey: ... +def from_mldsa65_public_bytes(data: bytes) -> mldsa.MLDSA65PublicKey: ... +def from_mldsa65_seed_bytes(data: Buffer) -> mldsa.MLDSA65PrivateKey: ... +def generate_mldsa87_key() -> mldsa.MLDSA87PrivateKey: ... +def from_mldsa87_public_bytes(data: bytes) -> mldsa.MLDSA87PublicKey: ... +def from_mldsa87_seed_bytes(data: Buffer) -> mldsa.MLDSA87PrivateKey: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi new file mode 100644 index 0000000..768a340 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi @@ -0,0 +1,18 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives.asymmetric import mlkem +from cryptography.utils import Buffer + +class MLKEM768PrivateKey: ... +class MLKEM768PublicKey: ... +class MLKEM1024PrivateKey: ... +class MLKEM1024PublicKey: ... + +def generate_mlkem768_key() -> mlkem.MLKEM768PrivateKey: ... +def from_mlkem768_seed_bytes(data: Buffer) -> mlkem.MLKEM768PrivateKey: ... +def from_mlkem768_public_bytes(data: Buffer) -> mlkem.MLKEM768PublicKey: ... +def generate_mlkem1024_key() -> mlkem.MLKEM1024PrivateKey: ... +def from_mlkem1024_seed_bytes(data: Buffer) -> mlkem.MLKEM1024PrivateKey: ... +def from_mlkem1024_public_bytes(data: Buffer) -> mlkem.MLKEM1024PublicKey: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi new file mode 100644 index 0000000..45a2a39 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi @@ -0,0 +1,15 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.utils import Buffer + +class Poly1305: + def __init__(self, key: Buffer) -> None: ... + @staticmethod + def generate_tag(key: Buffer, data: Buffer) -> bytes: ... + @staticmethod + def verify_tag(key: Buffer, data: Buffer, tag: bytes) -> None: ... + def update(self, data: Buffer) -> None: ... + def finalize(self) -> bytes: ... + def verify(self, tag: bytes) -> None: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi new file mode 100644 index 0000000..ef7752d --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi @@ -0,0 +1,55 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives.asymmetric import rsa + +class RSAPrivateKey: ... +class RSAPublicKey: ... + +class RSAPrivateNumbers: + def __init__( + self, + p: int, + q: int, + d: int, + dmp1: int, + dmq1: int, + iqmp: int, + public_numbers: RSAPublicNumbers, + ) -> None: ... + @property + def p(self) -> int: ... + @property + def q(self) -> int: ... + @property + def d(self) -> int: ... + @property + def dmp1(self) -> int: ... + @property + def dmq1(self) -> int: ... + @property + def iqmp(self) -> int: ... + @property + def public_numbers(self) -> RSAPublicNumbers: ... + def private_key( + self, + backend: typing.Any = None, + *, + unsafe_skip_rsa_key_validation: bool = False, + ) -> rsa.RSAPrivateKey: ... + +class RSAPublicNumbers: + def __init__(self, e: int, n: int) -> None: ... + @property + def n(self) -> int: ... + @property + def e(self) -> int: ... + def public_key(self, backend: typing.Any = None) -> rsa.RSAPublicKey: ... + +def generate_private_key( + public_exponent: int, + key_size: int, +) -> rsa.RSAPrivateKey: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi new file mode 100644 index 0000000..38d2add --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives.asymmetric import x25519 +from cryptography.utils import Buffer + +class X25519PrivateKey: ... +class X25519PublicKey: ... + +def generate_key() -> x25519.X25519PrivateKey: ... +def from_private_bytes(data: Buffer) -> x25519.X25519PrivateKey: ... +def from_public_bytes(data: bytes) -> x25519.X25519PublicKey: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi new file mode 100644 index 0000000..3ac0980 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives.asymmetric import x448 +from cryptography.utils import Buffer + +class X448PrivateKey: ... +class X448PublicKey: ... + +def generate_key() -> x448.X448PrivateKey: ... +def from_private_bytes(data: Buffer) -> x448.X448PrivateKey: ... +def from_public_bytes(data: bytes) -> x448.X448PublicKey: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi new file mode 100644 index 0000000..b25becb --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi @@ -0,0 +1,52 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing +from collections.abc import Iterable + +from cryptography import x509 +from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes +from cryptography.hazmat.primitives.serialization import ( + KeySerializationEncryption, +) +from cryptography.hazmat.primitives.serialization.pkcs12 import ( + PKCS12KeyAndCertificates, + PKCS12PrivateKeyTypes, +) +from cryptography.utils import Buffer + +class PKCS12Certificate: + def __init__( + self, cert: x509.Certificate, friendly_name: bytes | None + ) -> None: ... + @property + def friendly_name(self) -> bytes | None: ... + @property + def certificate(self) -> x509.Certificate: ... + +def load_key_and_certificates( + data: Buffer, + password: Buffer | None, + backend: typing.Any = None, +) -> tuple[ + PrivateKeyTypes | None, + x509.Certificate | None, + list[x509.Certificate], +]: ... +def load_pkcs12( + data: bytes, + password: bytes | None, + backend: typing.Any = None, +) -> PKCS12KeyAndCertificates: ... +def serialize_java_truststore( + certs: Iterable[PKCS12Certificate], + encryption_algorithm: KeySerializationEncryption, +) -> bytes: ... +def serialize_key_and_certificates( + name: bytes | None, + key: PKCS12PrivateKeyTypes | None, + cert: x509.Certificate | None, + cas: Iterable[x509.Certificate | PKCS12Certificate] | None, + encryption_algorithm: KeySerializationEncryption, +) -> bytes: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi new file mode 100644 index 0000000..358b135 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi @@ -0,0 +1,50 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from collections.abc import Iterable + +from cryptography import x509 +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.serialization import pkcs7 + +def serialize_certificates( + certs: list[x509.Certificate], + encoding: serialization.Encoding, +) -> bytes: ... +def encrypt_and_serialize( + builder: pkcs7.PKCS7EnvelopeBuilder, + content_encryption_algorithm: pkcs7.ContentEncryptionAlgorithm, + encoding: serialization.Encoding, + options: Iterable[pkcs7.PKCS7Options], +) -> bytes: ... +def sign_and_serialize( + builder: pkcs7.PKCS7SignatureBuilder, + encoding: serialization.Encoding, + options: Iterable[pkcs7.PKCS7Options], +) -> bytes: ... +def decrypt_der( + data: bytes, + certificate: x509.Certificate, + private_key: rsa.RSAPrivateKey, + options: Iterable[pkcs7.PKCS7Options], +) -> bytes: ... +def decrypt_pem( + data: bytes, + certificate: x509.Certificate, + private_key: rsa.RSAPrivateKey, + options: Iterable[pkcs7.PKCS7Options], +) -> bytes: ... +def decrypt_smime( + data: bytes, + certificate: x509.Certificate, + private_key: rsa.RSAPrivateKey, + options: Iterable[pkcs7.PKCS7Options], +) -> bytes: ... +def load_pem_pkcs7_certificates( + data: bytes, +) -> list[x509.Certificate]: ... +def load_der_pkcs7_certificates( + data: bytes, +) -> list[x509.Certificate]: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi new file mode 100644 index 0000000..c6c6d0b --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi @@ -0,0 +1,23 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography import x509 +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.serialization import pkcs7 +from cryptography.utils import Buffer + +class TestCertificate: + not_after_tag: int + not_before_tag: int + issuer_value_tags: list[int] + subject_value_tags: list[int] + +def test_parse_certificate(data: bytes) -> TestCertificate: ... +def pkcs7_verify( + encoding: serialization.Encoding, + sig: bytes, + msg: Buffer | None, + certs: list[x509.Certificate], + options: list[pkcs7.PKCS7Options], +) -> None: ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi new file mode 100644 index 0000000..4438cc7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi @@ -0,0 +1,313 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import datetime +import typing +from collections.abc import Iterator + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric.ec import ECDSA +from cryptography.hazmat.primitives.asymmetric.padding import PSS, PKCS1v15 +from cryptography.hazmat.primitives.asymmetric.types import ( + CertificateIssuerPublicKeyTypes, + CertificatePublicKeyTypes, + PrivateKeyTypes, +) +from cryptography.x509 import certificate_transparency + +def load_pem_x509_certificate( + data: bytes, backend: typing.Any = None +) -> x509.Certificate: ... +def load_der_x509_certificate( + data: bytes, backend: typing.Any = None +) -> x509.Certificate: ... +def load_pem_x509_certificates( + data: bytes, +) -> list[x509.Certificate]: ... +def load_pem_x509_crl( + data: bytes, backend: typing.Any = None +) -> x509.CertificateRevocationList: ... +def load_der_x509_crl( + data: bytes, backend: typing.Any = None +) -> x509.CertificateRevocationList: ... +def load_pem_x509_csr( + data: bytes, backend: typing.Any = None +) -> x509.CertificateSigningRequest: ... +def load_der_x509_csr( + data: bytes, backend: typing.Any = None +) -> x509.CertificateSigningRequest: ... +def encode_name_bytes(name: x509.Name) -> bytes: ... +def parse_name_bytes(data: bytes) -> x509.Name: ... +def encode_extension_value(extension: x509.ExtensionType) -> bytes: ... +def create_x509_certificate( + builder: x509.CertificateBuilder, + private_key: PrivateKeyTypes | None, + hash_algorithm: hashes.HashAlgorithm | None, + rsa_padding: PKCS1v15 | PSS | None, + ecdsa_deterministic: bool | None, +) -> x509.Certificate: ... +def create_x509_csr( + builder: x509.CertificateSigningRequestBuilder, + private_key: PrivateKeyTypes, + hash_algorithm: hashes.HashAlgorithm | None, + rsa_padding: PKCS1v15 | PSS | None, + ecdsa_deterministic: bool | None, +) -> x509.CertificateSigningRequest: ... +def create_revoked_certificate( + builder: x509.RevokedCertificateBuilder, +) -> x509.RevokedCertificate: ... +def create_x509_crl( + builder: x509.CertificateRevocationListBuilder, + private_key: PrivateKeyTypes, + hash_algorithm: hashes.HashAlgorithm | None, + rsa_padding: PKCS1v15 | PSS | None, + ecdsa_deterministic: bool | None, +) -> x509.CertificateRevocationList: ... + +class Sct: + @property + def version(self) -> certificate_transparency.Version: ... + @property + def log_id(self) -> bytes: ... + @property + def timestamp(self) -> datetime.datetime: ... + @property + def entry_type(self) -> certificate_transparency.LogEntryType: ... + @property + def signature_hash_algorithm(self) -> hashes.HashAlgorithm: ... + @property + def signature_algorithm( + self, + ) -> certificate_transparency.SignatureAlgorithm: ... + @property + def signature(self) -> bytes: ... + @property + def extension_bytes(self) -> bytes: ... + +class Certificate: + def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes: ... + @property + def serial_number(self) -> int: ... + @property + def version(self) -> x509.Version: ... + def public_key(self) -> CertificatePublicKeyTypes: ... + @property + def public_key_algorithm_oid(self) -> x509.ObjectIdentifier: ... + @property + def not_valid_before(self) -> datetime.datetime: ... + @property + def not_valid_before_utc(self) -> datetime.datetime: ... + @property + def not_valid_after(self) -> datetime.datetime: ... + @property + def not_valid_after_utc(self) -> datetime.datetime: ... + @property + def issuer(self) -> x509.Name: ... + @property + def subject(self) -> x509.Name: ... + @property + def signature_hash_algorithm( + self, + ) -> hashes.HashAlgorithm | None: ... + @property + def signature_algorithm_oid(self) -> x509.ObjectIdentifier: ... + @property + def signature_algorithm_parameters( + self, + ) -> PSS | PKCS1v15 | ECDSA | None: ... + @property + def extensions(self) -> x509.Extensions: ... + @property + def signature(self) -> bytes: ... + @property + def tbs_certificate_bytes(self) -> bytes: ... + @property + def tbs_precertificate_bytes(self) -> bytes: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... + def verify_directly_issued_by(self, issuer: Certificate) -> None: ... + +class RevokedCertificate: + @property + def serial_number(self) -> int: ... + @property + def revocation_date(self) -> datetime.datetime: ... + @property + def revocation_date_utc(self) -> datetime.datetime: ... + @property + def extensions(self) -> x509.Extensions: ... + +class CertificateRevocationList: + def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... + def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes: ... + def get_revoked_certificate_by_serial_number( + self, serial_number: int + ) -> x509.RevokedCertificate | None: ... + @property + def signature_hash_algorithm( + self, + ) -> hashes.HashAlgorithm | None: ... + @property + def signature_algorithm_oid(self) -> x509.ObjectIdentifier: ... + @property + def signature_algorithm_parameters( + self, + ) -> PSS | PKCS1v15 | ECDSA | None: ... + @property + def issuer(self) -> x509.Name: ... + @property + def next_update(self) -> datetime.datetime | None: ... + @property + def next_update_utc(self) -> datetime.datetime | None: ... + @property + def last_update(self) -> datetime.datetime: ... + @property + def last_update_utc(self) -> datetime.datetime: ... + @property + def extensions(self) -> x509.Extensions: ... + @property + def signature(self) -> bytes: ... + @property + def tbs_certlist_bytes(self) -> bytes: ... + def __eq__(self, other: object) -> bool: ... + def __len__(self) -> int: ... + @typing.overload + def __getitem__(self, idx: int) -> x509.RevokedCertificate: ... + @typing.overload + def __getitem__(self, idx: slice) -> list[x509.RevokedCertificate]: ... + def __iter__(self) -> Iterator[x509.RevokedCertificate]: ... + def is_signature_valid( + self, public_key: CertificateIssuerPublicKeyTypes + ) -> bool: ... + +class CertificateSigningRequest: + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + def public_key(self) -> CertificatePublicKeyTypes: ... + @property + def subject(self) -> x509.Name: ... + @property + def signature_hash_algorithm( + self, + ) -> hashes.HashAlgorithm | None: ... + @property + def signature_algorithm_oid(self) -> x509.ObjectIdentifier: ... + @property + def signature_algorithm_parameters( + self, + ) -> PSS | PKCS1v15 | ECDSA | None: ... + @property + def extensions(self) -> x509.Extensions: ... + @property + def attributes(self) -> x509.Attributes: ... + def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... + @property + def signature(self) -> bytes: ... + @property + def tbs_certrequest_bytes(self) -> bytes: ... + @property + def is_signature_valid(self) -> bool: ... + +class PolicyBuilder: + def time(self, time: datetime.datetime) -> PolicyBuilder: ... + def store(self, store: Store) -> PolicyBuilder: ... + def max_chain_depth(self, max_chain_depth: int) -> PolicyBuilder: ... + def extension_policies( + self, *, ca_policy: ExtensionPolicy, ee_policy: ExtensionPolicy + ) -> PolicyBuilder: ... + def build_client_verifier(self) -> ClientVerifier: ... + def build_server_verifier( + self, subject: x509.verification.Subject + ) -> ServerVerifier: ... + +class Policy: + @property + def max_chain_depth(self) -> int: ... + @property + def subject(self) -> x509.verification.Subject | None: ... + @property + def validation_time(self) -> datetime.datetime: ... + @property + def extended_key_usage(self) -> x509.ObjectIdentifier: ... + @property + def minimum_rsa_modulus(self) -> int: ... + +class Criticality: + CRITICAL: Criticality + AGNOSTIC: Criticality + NON_CRITICAL: Criticality + +T = typing.TypeVar("T", contravariant=True, bound=x509.ExtensionType) + +MaybeExtensionValidatorCallback = typing.Callable[ + [ + Policy, + x509.Certificate, + T | None, + ], + None, +] + +PresentExtensionValidatorCallback = typing.Callable[ + [Policy, x509.Certificate, T], + None, +] + +class ExtensionPolicy: + @staticmethod + def permit_all() -> ExtensionPolicy: ... + @staticmethod + def webpki_defaults_ca() -> ExtensionPolicy: ... + @staticmethod + def webpki_defaults_ee() -> ExtensionPolicy: ... + def require_not_present( + self, extension_type: type[x509.ExtensionType] + ) -> ExtensionPolicy: ... + def may_be_present( + self, + extension_type: type[T], + criticality: Criticality, + validator: MaybeExtensionValidatorCallback[T] | None, + ) -> ExtensionPolicy: ... + def require_present( + self, + extension_type: type[T], + criticality: Criticality, + validator: PresentExtensionValidatorCallback[T] | None, + ) -> ExtensionPolicy: ... + +class VerifiedClient: + @property + def subjects(self) -> list[x509.GeneralName] | None: ... + @property + def chain(self) -> list[x509.Certificate]: ... + +class ClientVerifier: + @property + def policy(self) -> Policy: ... + @property + def store(self) -> Store: ... + def verify( + self, + leaf: x509.Certificate, + intermediates: list[x509.Certificate], + ) -> VerifiedClient: ... + +class ServerVerifier: + @property + def policy(self) -> Policy: ... + @property + def store(self) -> Store: ... + def verify( + self, + leaf: x509.Certificate, + intermediates: list[x509.Certificate], + ) -> list[x509.Certificate]: ... + +class Store: + def __init__(self, certs: list[x509.Certificate]) -> None: ... + +class VerificationError(Exception): ... diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__init__.py new file mode 100644 index 0000000..41d7318 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__init__.py @@ -0,0 +1,5 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1109809 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-312.pyc new file mode 100644 index 0000000..85398e3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-312.pyc new file mode 100644 index 0000000..07f422d Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py new file mode 100644 index 0000000..1e447a5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py @@ -0,0 +1,199 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + + +def cryptography_has_set_cert_cb() -> list[str]: + return [ + "SSL_CTX_set_cert_cb", + "SSL_set_cert_cb", + ] + + +def cryptography_has_ssl_st() -> list[str]: + return [ + "SSL_ST_BEFORE", + "SSL_ST_OK", + "SSL_ST_INIT", + "SSL_ST_RENEGOTIATE", + ] + + +def cryptography_has_tls_st() -> list[str]: + return [ + "TLS_ST_BEFORE", + "TLS_ST_OK", + ] + + +def cryptography_has_ssl_sigalgs() -> list[str]: + return [ + "SSL_CTX_set1_sigalgs_list", + ] + + +def cryptography_has_psk() -> list[str]: + return [ + "SSL_CTX_use_psk_identity_hint", + "SSL_CTX_set_psk_server_callback", + "SSL_CTX_set_psk_client_callback", + ] + + +def cryptography_has_psk_tlsv13() -> list[str]: + return [ + "SSL_CTX_set_psk_find_session_callback", + "SSL_CTX_set_psk_use_session_callback", + "Cryptography_SSL_SESSION_new", + "SSL_CIPHER_find", + "SSL_SESSION_set1_master_key", + "SSL_SESSION_set_cipher", + "SSL_SESSION_set_protocol_version", + ] + + +def cryptography_has_custom_ext() -> list[str]: + return [ + "SSL_CTX_add_client_custom_ext", + "SSL_CTX_add_server_custom_ext", + "SSL_extension_supported", + ] + + +def cryptography_has_tlsv13_functions() -> list[str]: + return [ + "SSL_CTX_set_ciphersuites", + ] + + +def cryptography_has_tlsv13_hs_functions() -> list[str]: + return [ + "SSL_VERIFY_POST_HANDSHAKE", + "SSL_verify_client_post_handshake", + "SSL_CTX_set_post_handshake_auth", + "SSL_set_post_handshake_auth", + "SSL_SESSION_get_max_early_data", + "SSL_write_early_data", + "SSL_read_early_data", + "SSL_CTX_set_max_early_data", + ] + + +def cryptography_has_ssl_verify_client_post_handshake() -> list[str]: + return [ + "SSL_verify_client_post_handshake", + ] + + +def cryptography_has_engine() -> list[str]: + return [ + "ENGINE_by_id", + "ENGINE_init", + "ENGINE_finish", + "ENGINE_get_default_RAND", + "ENGINE_set_default_RAND", + "ENGINE_unregister_RAND", + "ENGINE_ctrl_cmd", + "ENGINE_free", + "ENGINE_get_name", + "ENGINE_ctrl_cmd_string", + "ENGINE_load_builtin_engines", + "ENGINE_load_private_key", + "ENGINE_load_public_key", + "SSL_CTX_set_client_cert_engine", + ] + + +def cryptography_has_verified_chain() -> list[str]: + return [ + "SSL_get0_verified_chain", + ] + + +def cryptography_has_srtp() -> list[str]: + return [ + "SSL_CTX_set_tlsext_use_srtp", + "SSL_set_tlsext_use_srtp", + "SSL_get_selected_srtp_profile", + ] + + +def cryptography_has_dtls_get_data_mtu() -> list[str]: + return [ + "DTLS_get_data_mtu", + ] + + +def cryptography_has_ssl_cookie() -> list[str]: + return [ + "SSL_OP_COOKIE_EXCHANGE", + "DTLS1_COOKIE_LENGTH", + "DTLSv1_listen", + "SSL_CTX_set_cookie_generate_cb", + "SSL_CTX_set_cookie_verify_cb", + ] + + +def cryptography_has_prime_checks() -> list[str]: + return [ + "BN_prime_checks_for_size", + ] + + +def cryptography_has_unexpected_eof_while_reading() -> list[str]: + return ["SSL_R_UNEXPECTED_EOF_WHILE_READING"] + + +def cryptography_has_ssl_op_ignore_unexpected_eof() -> list[str]: + return [ + "SSL_OP_IGNORE_UNEXPECTED_EOF", + ] + + +def cryptography_has_get_extms_support() -> list[str]: + return ["SSL_get_extms_support"] + + +def cryptography_has_ssl_get0_group_name() -> list[str]: + return ["SSL_get0_group_name"] + + +# This is a mapping of +# {condition: function-returning-names-dependent-on-that-condition} so we can +# loop over them and delete unsupported names at runtime. It will be removed +# when cffi supports #if in cdef. We use functions instead of just a dict of +# lists so we can use coverage to measure which are used. +CONDITIONAL_NAMES = { + "Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb, + "Cryptography_HAS_SSL_ST": cryptography_has_ssl_st, + "Cryptography_HAS_TLS_ST": cryptography_has_tls_st, + "Cryptography_HAS_SIGALGS": cryptography_has_ssl_sigalgs, + "Cryptography_HAS_PSK": cryptography_has_psk, + "Cryptography_HAS_PSK_TLSv1_3": cryptography_has_psk_tlsv13, + "Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext, + "Cryptography_HAS_TLSv1_3_FUNCTIONS": cryptography_has_tlsv13_functions, + "Cryptography_HAS_TLSv1_3_HS_FUNCTIONS": ( + cryptography_has_tlsv13_hs_functions + ), + "Cryptography_HAS_SSL_VERIFY_CLIENT_POST_HANDSHAKE": ( + cryptography_has_ssl_verify_client_post_handshake + ), + "Cryptography_HAS_ENGINE": cryptography_has_engine, + "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain, + "Cryptography_HAS_SRTP": cryptography_has_srtp, + "Cryptography_HAS_DTLS_GET_DATA_MTU": cryptography_has_dtls_get_data_mtu, + "Cryptography_HAS_SSL_COOKIE": cryptography_has_ssl_cookie, + "Cryptography_HAS_PRIME_CHECKS": cryptography_has_prime_checks, + "Cryptography_HAS_UNEXPECTED_EOF_WHILE_READING": ( + cryptography_has_unexpected_eof_while_reading + ), + "Cryptography_HAS_SSL_OP_IGNORE_UNEXPECTED_EOF": ( + cryptography_has_ssl_op_ignore_unexpected_eof + ), + "Cryptography_HAS_GET_EXTMS_SUPPORT": cryptography_has_get_extms_support, + "Cryptography_HAS_SSL_GET0_GROUP_NAME": ( + cryptography_has_ssl_get0_group_name + ), +} diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/binding.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/binding.py new file mode 100644 index 0000000..6e13df1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/binding.py @@ -0,0 +1,107 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import threading +import types +import typing +from collections.abc import Callable, Mapping + +import cryptography +from cryptography.exceptions import InternalError +from cryptography.hazmat.bindings._rust import _openssl, openssl +from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES + + +def _openssl_assert(ok: bool) -> None: + if not ok: + errors = openssl.capture_error_stack() + + raise InternalError( + "Unknown OpenSSL error. This error is commonly encountered when " + "another library is not cleaning up the OpenSSL error stack. If " + "you are using cryptography with another library that uses " + "OpenSSL try disabling it before reporting a bug. Otherwise " + "please file an issue at https://github.com/pyca/cryptography/" + "issues with information on how to reproduce " + f"this. ({errors!r})", + errors, + ) + + +def build_conditional_library( + lib: typing.Any, + conditional_names: Mapping[str, Callable[[], list[str]]], +) -> typing.Any: + conditional_lib = types.ModuleType("lib") + conditional_lib._original_lib = lib # type: ignore[attr-defined] + excluded_names = set() + for condition, names_cb in conditional_names.items(): + if not getattr(lib, condition): + excluded_names.update(names_cb()) + + for attr in dir(lib): + if attr not in excluded_names: + setattr(conditional_lib, attr, getattr(lib, attr)) + + return conditional_lib + + +class Binding: + """ + OpenSSL API wrapper. + """ + + lib: typing.ClassVar[typing.Any] = None + ffi: typing.Any = _openssl.ffi + _lib_loaded = False + _init_lock = threading.Lock() + + def __init__(self) -> None: + self._ensure_ffi_initialized() + + @classmethod + def _ensure_ffi_initialized(cls) -> None: + with cls._init_lock: + if not cls._lib_loaded: + cls.lib = build_conditional_library( + _openssl.lib, CONDITIONAL_NAMES + ) + cls._lib_loaded = True + + @classmethod + def init_static_locks(cls) -> None: + cls._ensure_ffi_initialized() + + +def _verify_package_version(version: str) -> None: + # Occasionally we run into situations where the version of the Python + # package does not match the version of the shared object that is loaded. + # This may occur in environments where multiple versions of cryptography + # are installed and available in the python path. To avoid errors cropping + # up later this code checks that the currently imported package and the + # shared object that were loaded have the same version and raise an + # ImportError if they do not + so_package_version = _openssl.ffi.string( + _openssl.lib.CRYPTOGRAPHY_PACKAGE_VERSION + ) + if version.encode("ascii") != so_package_version: + raise ImportError( + "The version of cryptography does not match the loaded " + "shared object. This can happen if you have multiple copies of " + "cryptography installed in your Python path. Please try creating " + "a new virtual environment to resolve this issue. " + f"Loaded python version: {version}, " + f"shared object version: {so_package_version}" + ) + + _openssl_assert( + _openssl.lib.OpenSSL_version_num() == openssl.openssl_version(), + ) + + +_verify_package_version(cryptography.__version__) + +Binding.init_static_locks() diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__init__.py new file mode 100644 index 0000000..41d7318 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__init__.py @@ -0,0 +1,5 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..13b006d Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py new file mode 100644 index 0000000..41d7318 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py @@ -0,0 +1,5 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..65905df Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/algorithms.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/algorithms.cpython-312.pyc new file mode 100644 index 0000000..f8c9e84 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/algorithms.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/modes.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/modes.cpython-312.pyc new file mode 100644 index 0000000..40e9aa6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/modes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py new file mode 100644 index 0000000..72dad04 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py @@ -0,0 +1,145 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import warnings + +from cryptography import utils +from cryptography.hazmat.primitives._cipheralgorithm import ( + BlockCipherAlgorithm, + CipherAlgorithm, + _verify_key_size, +) + + +class ARC4(CipherAlgorithm): + name = "RC4" + key_sizes = frozenset([40, 56, 64, 80, 128, 160, 192, 256]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class TripleDES(BlockCipherAlgorithm): + name = "3DES" + block_size = 64 + key_sizes = frozenset([64, 128, 192]) + + def __init__(self, key: bytes): + # Check the key type before the length-based deprecation warnings so + # an invalid key type doesn't trigger a spurious warning. + utils._check_byteslike("key", key) + if len(key) == 8: + warnings.warn( + "Single-key TripleDES (8-byte keys) is deprecated and " + "support will be removed in a future release. Use 24-byte " + "keys instead (e.g., key + key + key).", + utils.DeprecatedIn47, + stacklevel=2, + ) + key = key + key + key + elif len(key) == 16: + warnings.warn( + "Two-key TripleDES (16-byte keys) is deprecated and " + "support will be removed in a future release. Use 24-byte " + "keys instead (e.g., key + key[:8]).", + utils.DeprecatedIn47, + stacklevel=2, + ) + key = key + key[:8] + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +# Not actually supported, marker for tests +class _DES: + key_size = 64 + + +class Blowfish(BlockCipherAlgorithm): + name = "Blowfish" + block_size = 64 + key_sizes = frozenset(range(32, 449, 8)) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class CAST5(BlockCipherAlgorithm): + name = "CAST5" + block_size = 64 + key_sizes = frozenset(range(40, 129, 8)) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class SEED(BlockCipherAlgorithm): + name = "SEED" + block_size = 128 + key_sizes = frozenset([128]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class IDEA(BlockCipherAlgorithm): + name = "IDEA" + block_size = 64 + key_sizes = frozenset([128]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class Camellia(BlockCipherAlgorithm): + name = "camellia" + block_size = 128 + key_sizes = frozenset([128, 192, 256]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +# This class only allows RC2 with a 128-bit key. No support for +# effective key bits or other key sizes is provided. +class RC2(BlockCipherAlgorithm): + name = "RC2" + block_size = 64 + key_sizes = frozenset([128]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/modes.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/modes.py new file mode 100644 index 0000000..1786bb0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/modes.py @@ -0,0 +1,53 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography import utils +from cryptography.hazmat.primitives._modes import ( + ModeWithInitializationVector, + _check_iv_and_key_length, +) + + +class OFB(ModeWithInitializationVector): + name = "OFB" + + def __init__(self, initialization_vector: utils.Buffer): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> utils.Buffer: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class CFB(ModeWithInitializationVector): + name = "CFB" + + def __init__(self, initialization_vector: utils.Buffer): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> utils.Buffer: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class CFB8(ModeWithInitializationVector): + name = "CFB8" + + def __init__(self, initialization_vector: utils.Buffer): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> utils.Buffer: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__init__.py new file mode 100644 index 0000000..41d7318 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__init__.py @@ -0,0 +1,5 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..98af16e Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-312.pyc new file mode 100644 index 0000000..211e5e8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-312.pyc new file mode 100644 index 0000000..9062f6b Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_modes.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_modes.cpython-312.pyc new file mode 100644 index 0000000..35ca5f5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_modes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_serialization.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_serialization.cpython-312.pyc new file mode 100644 index 0000000..05b69a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_serialization.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/cmac.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/cmac.cpython-312.pyc new file mode 100644 index 0000000..c1bcb04 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/cmac.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/constant_time.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/constant_time.cpython-312.pyc new file mode 100644 index 0000000..a7b9bd7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/constant_time.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hashes.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hashes.cpython-312.pyc new file mode 100644 index 0000000..cec2149 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hashes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hmac.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hmac.cpython-312.pyc new file mode 100644 index 0000000..1cc3c08 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hmac.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hpke.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hpke.cpython-312.pyc new file mode 100644 index 0000000..25bb4aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hpke.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/keywrap.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/keywrap.cpython-312.pyc new file mode 100644 index 0000000..35278c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/keywrap.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/padding.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/padding.cpython-312.pyc new file mode 100644 index 0000000..e8160aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/padding.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/poly1305.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/poly1305.cpython-312.pyc new file mode 100644 index 0000000..0c85f35 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/poly1305.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_asymmetric.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_asymmetric.py new file mode 100644 index 0000000..ea55ffd --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_asymmetric.py @@ -0,0 +1,19 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +# This exists to break an import cycle. It is normally accessible from the +# asymmetric padding module. + + +class AsymmetricPadding(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + A string naming this padding (e.g. "PSS", "PKCS1"). + """ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py new file mode 100644 index 0000000..305a9fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py @@ -0,0 +1,60 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography import utils + +# This exists to break an import cycle. It is normally accessible from the +# ciphers module. + + +class CipherAlgorithm(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + A string naming this mode (e.g. "AES", "Camellia"). + """ + + @property + @abc.abstractmethod + def key_sizes(self) -> frozenset[int]: + """ + Valid key sizes for this algorithm in bits + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The size of the key being used as an integer in bits (e.g. 128, 256). + """ + + +class BlockCipherAlgorithm(CipherAlgorithm): + key: utils.Buffer + + @property + @abc.abstractmethod + def block_size(self) -> int: + """ + The size of a block as an integer in bits (e.g. 64, 128). + """ + + +def _verify_key_size( + algorithm: CipherAlgorithm, key: utils.Buffer +) -> utils.Buffer: + # Verify that the key is instance of bytes + utils._check_byteslike("key", key) + + # Verify that the key size matches the expected key size + if len(key) * 8 not in algorithm.key_sizes: + raise ValueError( + f"Invalid key size ({len(key) * 8}) for {algorithm.name}." + ) + return key diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_modes.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_modes.py new file mode 100644 index 0000000..deae8bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_modes.py @@ -0,0 +1,105 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives._cipheralgorithm import ( + BlockCipherAlgorithm, + CipherAlgorithm, +) + + +class Mode(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + A string naming this mode (e.g. "ECB", "CBC"). + """ + + @abc.abstractmethod + def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: + """ + Checks that all the necessary invariants of this (mode, algorithm) + combination are met. + """ + + +class ModeWithInitializationVector(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def initialization_vector(self) -> utils.Buffer: + """ + The value of the initialization vector for this mode as bytes. + """ + + +class ModeWithTweak(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def tweak(self) -> utils.Buffer: + """ + The value of the tweak for this mode as bytes. + """ + + +class ModeWithNonce(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def nonce(self) -> utils.Buffer: + """ + The value of the nonce for this mode as bytes. + """ + + +class ModeWithAuthenticationTag(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def tag(self) -> bytes | None: + """ + The value of the tag supplied to the constructor of this mode. + """ + + +def _check_aes_key_length(self: Mode, algorithm: CipherAlgorithm) -> None: + if algorithm.key_size > 256 and algorithm.name == "AES": + raise ValueError( + "Only 128, 192, and 256 bit keys are allowed for this AES mode" + ) + + +def _check_iv_length( + self: ModeWithInitializationVector, algorithm: BlockCipherAlgorithm +) -> None: + iv_len = len(self.initialization_vector) + if iv_len * 8 != algorithm.block_size: + raise ValueError(f"Invalid IV size ({iv_len}) for {self.name}.") + + +def _check_nonce_length( + nonce: utils.Buffer, name: str, algorithm: CipherAlgorithm +) -> None: + if not isinstance(algorithm, BlockCipherAlgorithm): + raise UnsupportedAlgorithm( + f"{name} requires a block cipher algorithm", + _Reasons.UNSUPPORTED_CIPHER, + ) + if len(nonce) * 8 != algorithm.block_size: + raise ValueError(f"Invalid nonce size ({len(nonce)}) for {name}.") + + +def _check_iv_and_key_length( + self: ModeWithInitializationVector, algorithm: CipherAlgorithm +) -> None: + if not isinstance(algorithm, BlockCipherAlgorithm): + raise UnsupportedAlgorithm( + f"{self} requires a block cipher algorithm", + _Reasons.UNSUPPORTED_CIPHER, + ) + _check_aes_key_length(self, algorithm) + _check_iv_length(self, algorithm) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_serialization.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_serialization.py new file mode 100644 index 0000000..9c3c474 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_serialization.py @@ -0,0 +1,136 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography import utils +from cryptography.hazmat.bindings._rust import Encoding as Encoding +from cryptography.hazmat.bindings._rust import ( + ParameterFormat as ParameterFormat, +) +from cryptography.hazmat.bindings._rust import PrivateFormat as PrivateFormat +from cryptography.hazmat.bindings._rust import PublicFormat as PublicFormat +from cryptography.hazmat.primitives.hashes import HashAlgorithm + +# This exists to break an import cycle. These classes are normally accessible +# from the serialization module. + + +class PBES(utils.Enum): + PBESv1SHA1And3KeyTripleDESCBC = "PBESv1 using SHA1 and 3-Key TripleDES" + PBESv2SHA256AndAES256CBC = "PBESv2 using SHA256 PBKDF2 and AES256 CBC" + + +class KeySerializationEncryption(metaclass=abc.ABCMeta): + pass + + +class BestAvailableEncryption(KeySerializationEncryption): + def __init__(self, password: bytes): + if not isinstance(password, bytes) or len(password) == 0: + raise ValueError("Password must be 1 or more bytes.") + + self.password = password + + +class NoEncryption(KeySerializationEncryption): + pass + + +class KeySerializationEncryptionBuilder: + def __init__( + self, + format: PrivateFormat, + *, + _kdf_rounds: int | None = None, + _hmac_hash: HashAlgorithm | None = None, + _key_cert_algorithm: PBES | None = None, + ) -> None: + self._format = format + + self._kdf_rounds = _kdf_rounds + self._hmac_hash = _hmac_hash + self._key_cert_algorithm = _key_cert_algorithm + + def kdf_rounds(self, rounds: int) -> KeySerializationEncryptionBuilder: + if self._kdf_rounds is not None: + raise ValueError("kdf_rounds already set") + + if not isinstance(rounds, int): + raise TypeError("kdf_rounds must be an integer") + + if rounds < 1: + raise ValueError("kdf_rounds must be a positive integer") + + return KeySerializationEncryptionBuilder( + self._format, + _kdf_rounds=rounds, + _hmac_hash=self._hmac_hash, + _key_cert_algorithm=self._key_cert_algorithm, + ) + + def hmac_hash( + self, algorithm: HashAlgorithm + ) -> KeySerializationEncryptionBuilder: + if self._format is not PrivateFormat.PKCS12: + raise TypeError( + "hmac_hash only supported with PrivateFormat.PKCS12" + ) + + if self._hmac_hash is not None: + raise ValueError("hmac_hash already set") + return KeySerializationEncryptionBuilder( + self._format, + _kdf_rounds=self._kdf_rounds, + _hmac_hash=algorithm, + _key_cert_algorithm=self._key_cert_algorithm, + ) + + def key_cert_algorithm( + self, algorithm: PBES + ) -> KeySerializationEncryptionBuilder: + if self._format is not PrivateFormat.PKCS12: + raise TypeError( + "key_cert_algorithm only supported with PrivateFormat.PKCS12" + ) + if self._key_cert_algorithm is not None: + raise ValueError("key_cert_algorithm already set") + return KeySerializationEncryptionBuilder( + self._format, + _kdf_rounds=self._kdf_rounds, + _hmac_hash=self._hmac_hash, + _key_cert_algorithm=algorithm, + ) + + def build(self, password: bytes) -> KeySerializationEncryption: + if not isinstance(password, bytes) or len(password) == 0: + raise ValueError("Password must be 1 or more bytes.") + + return _KeySerializationEncryption( + self._format, + password, + kdf_rounds=self._kdf_rounds, + hmac_hash=self._hmac_hash, + key_cert_algorithm=self._key_cert_algorithm, + ) + + +class _KeySerializationEncryption(KeySerializationEncryption): + def __init__( + self, + format: PrivateFormat, + password: bytes, + *, + kdf_rounds: int | None, + hmac_hash: HashAlgorithm | None, + key_cert_algorithm: PBES | None, + ): + self._format = format + self.password = password + + self._kdf_rounds = kdf_rounds + self._hmac_hash = hmac_hash + self._key_cert_algorithm = key_cert_algorithm diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py new file mode 100644 index 0000000..41d7318 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py @@ -0,0 +1,5 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0824c42 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-312.pyc new file mode 100644 index 0000000..c6f3eb0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-312.pyc new file mode 100644 index 0000000..cdc0ec1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-312.pyc new file mode 100644 index 0000000..e9678e0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-312.pyc new file mode 100644 index 0000000..8254055 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-312.pyc new file mode 100644 index 0000000..661c3be Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/mldsa.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/mldsa.cpython-312.pyc new file mode 100644 index 0000000..d3907c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/mldsa.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/mlkem.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/mlkem.cpython-312.pyc new file mode 100644 index 0000000..af7421b Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/mlkem.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-312.pyc new file mode 100644 index 0000000..c5a280e Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-312.pyc new file mode 100644 index 0000000..5c8f279 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-312.pyc new file mode 100644 index 0000000..73023ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..ea9cc2e Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-312.pyc new file mode 100644 index 0000000..336cfd5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-312.pyc new file mode 100644 index 0000000..4e5f545 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py new file mode 100644 index 0000000..814fdd3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py @@ -0,0 +1,253 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography import utils +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization + +_FFDH_DEPRECATION_MSG = ( + "Diffie-Hellman over finite fields (FFDH) is deprecated and support " + "will be removed in a future release. Use a more modern key exchange " + "algorithm." +) + +generate_parameters = rust_openssl.dh.generate_parameters + + +DHPrivateNumbers = rust_openssl.dh.DHPrivateNumbers +DHPublicNumbers = rust_openssl.dh.DHPublicNumbers +DHParameterNumbers = rust_openssl.dh.DHParameterNumbers + + +class DHParameters(metaclass=abc.ABCMeta): + @abc.abstractmethod + def generate_private_key(self) -> DHPrivateKey: + """ + Generates and returns a DHPrivateKey. + """ + + @abc.abstractmethod + def parameter_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.ParameterFormat, + ) -> bytes: + """ + Returns the parameters serialized as bytes. + """ + + @abc.abstractmethod + def parameter_numbers(self) -> DHParameterNumbers: + """ + Returns a DHParameterNumbers. + """ + + +DHParametersWithSerialization = DHParameters +DHParameters.register(rust_openssl.dh.DHParameters) + + +class DHPublicKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def parameters(self) -> DHParameters: + """ + The DHParameters object associated with this public key. + """ + + @abc.abstractmethod + def public_numbers(self) -> DHPublicNumbers: + """ + Returns a DHPublicNumbers. + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> DHPublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> DHPublicKey: + """ + Returns a deep copy. + """ + + +DHPublicKeyWithSerialization = DHPublicKey +DHPublicKey.register(rust_openssl.dh.DHPublicKey) + + +class DHPrivateKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def public_key(self) -> DHPublicKey: + """ + The DHPublicKey associated with this private key. + """ + + @abc.abstractmethod + def parameters(self) -> DHParameters: + """ + The DHParameters object associated with this private key. + """ + + @abc.abstractmethod + def exchange(self, peer_public_key: DHPublicKey) -> bytes: + """ + Given peer's DHPublicKey, carry out the key exchange and + return shared key as bytes. + """ + + @abc.abstractmethod + def private_numbers(self) -> DHPrivateNumbers: + """ + Returns a DHPrivateNumbers. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def __copy__(self) -> DHPrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> DHPrivateKey: + """ + Returns a deep copy. + """ + + +DHPrivateKeyWithSerialization = DHPrivateKey +DHPrivateKey.register(rust_openssl.dh.DHPrivateKey) + +# Aliases that do not emit the deprecation warning on attribute access, for +# internal use (e.g. the unions in +# cryptography.hazmat.primitives.asymmetric.types, which are evaluated at +# import time). +_DHPublicKey = DHPublicKey +_DHPrivateKey = DHPrivateKey + +utils.deprecated( + generate_parameters, + __name__, + _FFDH_DEPRECATION_MSG, + utils.DeprecatedIn50, + name="generate_parameters", +) + +utils.deprecated( + DHPrivateNumbers, + __name__, + _FFDH_DEPRECATION_MSG, + utils.DeprecatedIn50, + name="DHPrivateNumbers", +) + +utils.deprecated( + DHPublicNumbers, + __name__, + _FFDH_DEPRECATION_MSG, + utils.DeprecatedIn50, + name="DHPublicNumbers", +) + +utils.deprecated( + DHParameterNumbers, + __name__, + _FFDH_DEPRECATION_MSG, + utils.DeprecatedIn50, + name="DHParameterNumbers", +) + +utils.deprecated( + DHParameters, + __name__, + _FFDH_DEPRECATION_MSG, + utils.DeprecatedIn50, + name="DHParameters", +) + +utils.deprecated( + DHParameters, + __name__, + _FFDH_DEPRECATION_MSG, + utils.DeprecatedIn50, + name="DHParametersWithSerialization", +) + +utils.deprecated( + DHPublicKey, + __name__, + _FFDH_DEPRECATION_MSG, + utils.DeprecatedIn50, + name="DHPublicKey", +) + +utils.deprecated( + DHPublicKey, + __name__, + _FFDH_DEPRECATION_MSG, + utils.DeprecatedIn50, + name="DHPublicKeyWithSerialization", +) + +utils.deprecated( + DHPrivateKey, + __name__, + _FFDH_DEPRECATION_MSG, + utils.DeprecatedIn50, + name="DHPrivateKey", +) + +utils.deprecated( + DHPrivateKey, + __name__, + _FFDH_DEPRECATION_MSG, + utils.DeprecatedIn50, + name="DHPrivateKeyWithSerialization", +) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py new file mode 100644 index 0000000..f245557 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -0,0 +1,179 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import typing + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization, hashes +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils +from cryptography.utils import Buffer + + +class DSAParameters(metaclass=abc.ABCMeta): + @abc.abstractmethod + def generate_private_key(self) -> DSAPrivateKey: + """ + Generates and returns a DSAPrivateKey. + """ + + @abc.abstractmethod + def parameter_numbers(self) -> DSAParameterNumbers: + """ + Returns a DSAParameterNumbers. + """ + + +DSAParametersWithNumbers = DSAParameters +DSAParameters.register(rust_openssl.dsa.DSAParameters) + + +class DSAPrivateKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def public_key(self) -> DSAPublicKey: + """ + The DSAPublicKey associated with this private key. + """ + + @abc.abstractmethod + def parameters(self) -> DSAParameters: + """ + The DSAParameters object associated with this private key. + """ + + @abc.abstractmethod + def sign( + self, + data: Buffer, + algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, + ) -> bytes: + """ + Signs the data + """ + + @abc.abstractmethod + def private_numbers(self) -> DSAPrivateNumbers: + """ + Returns a DSAPrivateNumbers. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def __copy__(self) -> DSAPrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> DSAPrivateKey: + """ + Returns a deep copy. + """ + + +DSAPrivateKeyWithSerialization = DSAPrivateKey +DSAPrivateKey.register(rust_openssl.dsa.DSAPrivateKey) + + +class DSAPublicKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def parameters(self) -> DSAParameters: + """ + The DSAParameters object associated with this public key. + """ + + @abc.abstractmethod + def public_numbers(self) -> DSAPublicNumbers: + """ + Returns a DSAPublicNumbers. + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def verify( + self, + signature: Buffer, + data: Buffer, + algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, + ) -> None: + """ + Verifies the signature of the data. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> DSAPublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> DSAPublicKey: + """ + Returns a deep copy. + """ + + +DSAPublicKeyWithSerialization = DSAPublicKey +DSAPublicKey.register(rust_openssl.dsa.DSAPublicKey) + +DSAPrivateNumbers = rust_openssl.dsa.DSAPrivateNumbers +DSAPublicNumbers = rust_openssl.dsa.DSAPublicNumbers +DSAParameterNumbers = rust_openssl.dsa.DSAParameterNumbers + + +def generate_parameters( + key_size: int, backend: typing.Any = None +) -> DSAParameters: + if key_size not in (1024, 2048, 3072, 4096): + raise ValueError("Key size must be 1024, 2048, 3072, or 4096 bits.") + + return rust_openssl.dsa.generate_parameters(key_size) + + +def generate_private_key( + key_size: int, backend: typing.Any = None +) -> DSAPrivateKey: + parameters = generate_parameters(key_size) + return parameters.generate_private_key() diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py new file mode 100644 index 0000000..39e6751 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py @@ -0,0 +1,369 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import typing + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat._oid import ObjectIdentifier +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization, hashes +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils + + +class EllipticCurveOID: + SECP192R1 = ObjectIdentifier("1.2.840.10045.3.1.1") + SECP224R1 = ObjectIdentifier("1.3.132.0.33") + SECP256K1 = ObjectIdentifier("1.3.132.0.10") + SECP256R1 = ObjectIdentifier("1.2.840.10045.3.1.7") + SECP384R1 = ObjectIdentifier("1.3.132.0.34") + SECP521R1 = ObjectIdentifier("1.3.132.0.35") + BRAINPOOLP256R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.7") + BRAINPOOLP384R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.11") + BRAINPOOLP512R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.13") + + +class EllipticCurve(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + The name of the curve. e.g. secp256r1. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + Bit size of a secret scalar for the curve. + """ + + @property + @abc.abstractmethod + def group_order(self) -> int: + """ + The order of the curve's group. + """ + + +class EllipticCurveSignatureAlgorithm(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def algorithm( + self, + ) -> asym_utils.Prehashed | hashes.HashAlgorithm: + """ + The digest algorithm used with this signature. + """ + + +class EllipticCurvePrivateKey(metaclass=abc.ABCMeta): + @abc.abstractmethod + def exchange( + self, algorithm: ECDH, peer_public_key: EllipticCurvePublicKey + ) -> bytes: + """ + Performs a key exchange operation using the provided algorithm with the + provided peer's public key. + """ + + @abc.abstractmethod + def public_key(self) -> EllipticCurvePublicKey: + """ + The EllipticCurvePublicKey for this private key. + """ + + @property + @abc.abstractmethod + def curve(self) -> EllipticCurve: + """ + The EllipticCurve that this key is on. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + Bit size of a secret scalar for the curve. + """ + + @abc.abstractmethod + def sign( + self, + data: utils.Buffer, + signature_algorithm: EllipticCurveSignatureAlgorithm, + ) -> bytes: + """ + Signs the data + """ + + @abc.abstractmethod + def private_numbers(self) -> EllipticCurvePrivateNumbers: + """ + Returns an EllipticCurvePrivateNumbers. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def __copy__(self) -> EllipticCurvePrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> EllipticCurvePrivateKey: + """ + Returns a deep copy. + """ + + +EllipticCurvePrivateKeyWithSerialization = EllipticCurvePrivateKey +EllipticCurvePrivateKey.register(rust_openssl.ec.ECPrivateKey) + + +class EllipticCurvePublicKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def curve(self) -> EllipticCurve: + """ + The EllipticCurve that this key is on. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + Bit size of a secret scalar for the curve. + """ + + @abc.abstractmethod + def public_numbers(self) -> EllipticCurvePublicNumbers: + """ + Returns an EllipticCurvePublicNumbers. + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def verify( + self, + signature: utils.Buffer, + data: utils.Buffer, + signature_algorithm: EllipticCurveSignatureAlgorithm, + ) -> None: + """ + Verifies the signature of the data. + """ + + @classmethod + def from_encoded_point( + cls, curve: EllipticCurve, data: bytes + ) -> EllipticCurvePublicKey: + utils._check_bytes("data", data) + + if len(data) == 0: + raise ValueError("data must not be an empty byte string") + + if data[0] not in [0x02, 0x03, 0x04]: + raise ValueError("Unsupported elliptic curve point type") + + return rust_openssl.ec.from_public_bytes(curve, data) + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> EllipticCurvePublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> EllipticCurvePublicKey: + """ + Returns a deep copy. + """ + + +EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey +EllipticCurvePublicKey.register(rust_openssl.ec.ECPublicKey) + +EllipticCurvePrivateNumbers = rust_openssl.ec.EllipticCurvePrivateNumbers +EllipticCurvePublicNumbers = rust_openssl.ec.EllipticCurvePublicNumbers + + +class SECP521R1(EllipticCurve): + name = "secp521r1" + key_size = 521 + group_order = 0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409 # noqa: E501 + + +class SECP384R1(EllipticCurve): + name = "secp384r1" + key_size = 384 + group_order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973 # noqa: E501 + + +class SECP256R1(EllipticCurve): + name = "secp256r1" + key_size = 256 + group_order = ( + 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551 + ) + + +class SECP256K1(EllipticCurve): + name = "secp256k1" + key_size = 256 + group_order = ( + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + ) + + +class SECP224R1(EllipticCurve): + name = "secp224r1" + key_size = 224 + group_order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D + + +class SECP192R1(EllipticCurve): + name = "secp192r1" + key_size = 192 + group_order = 0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831 + + +class BrainpoolP256R1(EllipticCurve): + name = "brainpoolP256r1" + key_size = 256 + group_order = ( + 0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 + ) + + +class BrainpoolP384R1(EllipticCurve): + name = "brainpoolP384r1" + key_size = 384 + group_order = 0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565 # noqa: E501 + + +class BrainpoolP512R1(EllipticCurve): + name = "brainpoolP512r1" + key_size = 512 + group_order = 0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069 # noqa: E501 + + +_CURVE_TYPES: dict[str, EllipticCurve] = { + "prime192v1": SECP192R1(), + "prime256v1": SECP256R1(), + "secp192r1": SECP192R1(), + "secp224r1": SECP224R1(), + "secp256r1": SECP256R1(), + "secp384r1": SECP384R1(), + "secp521r1": SECP521R1(), + "secp256k1": SECP256K1(), + "brainpoolP256r1": BrainpoolP256R1(), + "brainpoolP384r1": BrainpoolP384R1(), + "brainpoolP512r1": BrainpoolP512R1(), +} + + +class ECDSA(EllipticCurveSignatureAlgorithm): + def __init__( + self, + algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, + deterministic_signing: bool = False, + ): + from cryptography.hazmat.backends.openssl.backend import backend + + if ( + deterministic_signing + and not backend.ecdsa_deterministic_supported() + ): + raise UnsupportedAlgorithm( + "ECDSA with deterministic signature (RFC 6979) is not " + "supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + self._algorithm = algorithm + self._deterministic_signing = deterministic_signing + + @property + def algorithm( + self, + ) -> asym_utils.Prehashed | hashes.HashAlgorithm: + return self._algorithm + + @property + def deterministic_signing( + self, + ) -> bool: + return self._deterministic_signing + + +generate_private_key = rust_openssl.ec.generate_private_key + + +def derive_private_key( + private_value: int, + curve: EllipticCurve, + backend: typing.Any = None, +) -> EllipticCurvePrivateKey: + if not isinstance(private_value, int): + raise TypeError("private_value must be an integer type.") + + if private_value <= 0: + raise ValueError("private_value must be a positive integer.") + + return rust_openssl.ec.derive_private_key(private_value, curve) + + +class ECDH: + pass + + +_OID_TO_CURVE = { + EllipticCurveOID.SECP192R1: SECP192R1, + EllipticCurveOID.SECP224R1: SECP224R1, + EllipticCurveOID.SECP256K1: SECP256K1, + EllipticCurveOID.SECP256R1: SECP256R1, + EllipticCurveOID.SECP384R1: SECP384R1, + EllipticCurveOID.SECP521R1: SECP521R1, + EllipticCurveOID.BRAINPOOLP256R1: BrainpoolP256R1, + EllipticCurveOID.BRAINPOOLP384R1: BrainpoolP384R1, + EllipticCurveOID.BRAINPOOLP512R1: BrainpoolP512R1, +} + + +def get_curve_for_oid(oid: ObjectIdentifier) -> type[EllipticCurve]: + try: + return _OID_TO_CURVE[oid] + except KeyError: + raise LookupError( + "The provided object identifier has no matching elliptic " + "curve class" + ) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py new file mode 100644 index 0000000..70aec5b --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py @@ -0,0 +1,116 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization +from cryptography.utils import Buffer + + +class Ed25519PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> Ed25519PublicKey: + return rust_openssl.ed25519.from_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def public_bytes_raw(self) -> bytes: + """ + The raw bytes of the public key. + Equivalent to public_bytes(Raw, Raw). + """ + + @abc.abstractmethod + def verify(self, signature: Buffer, data: Buffer) -> None: + """ + Verify the signature. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> Ed25519PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> Ed25519PublicKey: + """ + Returns a deep copy. + """ + + +Ed25519PublicKey.register(rust_openssl.ed25519.Ed25519PublicKey) + + +class Ed25519PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> Ed25519PrivateKey: + return rust_openssl.ed25519.generate_key() + + @classmethod + def from_private_bytes(cls, data: Buffer) -> Ed25519PrivateKey: + return rust_openssl.ed25519.from_private_bytes(data) + + @abc.abstractmethod + def public_key(self) -> Ed25519PublicKey: + """ + The Ed25519PublicKey derived from the private key. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + """ + + @abc.abstractmethod + def sign(self, data: Buffer) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def __copy__(self) -> Ed25519PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> Ed25519PrivateKey: + """ + Returns a deep copy. + """ + + +Ed25519PrivateKey.register(rust_openssl.ed25519.Ed25519PrivateKey) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py new file mode 100644 index 0000000..9ecb478 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py @@ -0,0 +1,143 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization +from cryptography.utils import Buffer + + +class Ed448PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> Ed448PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed448_supported(): + raise UnsupportedAlgorithm( + "ed448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.ed448.from_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def public_bytes_raw(self) -> bytes: + """ + The raw bytes of the public key. + Equivalent to public_bytes(Raw, Raw). + """ + + @abc.abstractmethod + def verify(self, signature: Buffer, data: Buffer) -> None: + """ + Verify the signature. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> Ed448PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> Ed448PublicKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "ed448"): + Ed448PublicKey.register(rust_openssl.ed448.Ed448PublicKey) + + +class Ed448PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> Ed448PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed448_supported(): + raise UnsupportedAlgorithm( + "ed448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.ed448.generate_key() + + @classmethod + def from_private_bytes(cls, data: Buffer) -> Ed448PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed448_supported(): + raise UnsupportedAlgorithm( + "ed448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.ed448.from_private_bytes(data) + + @abc.abstractmethod + def public_key(self) -> Ed448PublicKey: + """ + The Ed448PublicKey derived from the private key. + """ + + @abc.abstractmethod + def sign(self, data: Buffer) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + """ + + @abc.abstractmethod + def __copy__(self) -> Ed448PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> Ed448PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "x448"): + Ed448PrivateKey.register(rust_openssl.ed448.Ed448PrivateKey) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mldsa.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mldsa.py new file mode 100644 index 0000000..beba90f --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mldsa.py @@ -0,0 +1,505 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization +from cryptography.utils import Buffer + + +class MLDSA44PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> MLDSA44PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-44 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.from_mldsa44_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def public_bytes_raw(self) -> bytes: + """ + The raw bytes of the public key. + Equivalent to public_bytes(Raw, Raw). + + The public key is 1,312 bytes for MLDSA-44. + """ + + @abc.abstractmethod + def verify( + self, + signature: Buffer, + data: Buffer, + context: Buffer | None = None, + ) -> None: + """ + Verify the signature. + """ + + @abc.abstractmethod + def verify_mu( + self, + signature: Buffer, + mu: Buffer, + ) -> None: + """ + Verify the signature over a precomputed mu (message representative). + + mu must be 64 bytes. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> MLDSA44PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLDSA44PublicKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mldsa"): + MLDSA44PublicKey.register(rust_openssl.mldsa.MLDSA44PublicKey) + + +class MLDSA44PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> MLDSA44PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-44 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.generate_mldsa44_key() + + @classmethod + def from_seed_bytes(cls, data: Buffer) -> MLDSA44PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-44 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.from_mldsa44_seed_bytes(data) + + @abc.abstractmethod + def public_key(self) -> MLDSA44PublicKey: + """ + The MLDSA44PublicKey derived from the private key. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + + This method only returns the serialization of the seed form of the + private key, never the expanded one. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + + This method only returns the seed form of the private key (32 bytes). + """ + + @abc.abstractmethod + def sign(self, data: Buffer, context: Buffer | None = None) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def sign_mu(self, mu: Buffer) -> bytes: + """ + Signs a precomputed mu (message representative). + + mu must be 64 bytes and already incorporates the context, so no + context is accepted here. + """ + + @abc.abstractmethod + def __copy__(self) -> MLDSA44PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLDSA44PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mldsa"): + MLDSA44PrivateKey.register(rust_openssl.mldsa.MLDSA44PrivateKey) + + +class MLDSA65PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> MLDSA65PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-65 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.from_mldsa65_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def public_bytes_raw(self) -> bytes: + """ + The raw bytes of the public key. + Equivalent to public_bytes(Raw, Raw). + + The public key is 1,952 bytes for MLDSA-65. + """ + + @abc.abstractmethod + def verify( + self, + signature: Buffer, + data: Buffer, + context: Buffer | None = None, + ) -> None: + """ + Verify the signature. + """ + + @abc.abstractmethod + def verify_mu( + self, + signature: Buffer, + mu: Buffer, + ) -> None: + """ + Verify the signature over a precomputed mu (message representative). + + mu must be 64 bytes. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> MLDSA65PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLDSA65PublicKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mldsa"): + MLDSA65PublicKey.register(rust_openssl.mldsa.MLDSA65PublicKey) + + +class MLDSA65PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> MLDSA65PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-65 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.generate_mldsa65_key() + + @classmethod + def from_seed_bytes(cls, data: Buffer) -> MLDSA65PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-65 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.from_mldsa65_seed_bytes(data) + + @abc.abstractmethod + def public_key(self) -> MLDSA65PublicKey: + """ + The MLDSA65PublicKey derived from the private key. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + + This method only returns the serialization of the seed form of the + private key, never the expanded one. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + + This method only returns the seed form of the private key (32 bytes). + """ + + @abc.abstractmethod + def sign(self, data: Buffer, context: Buffer | None = None) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def sign_mu(self, mu: Buffer) -> bytes: + """ + Signs a precomputed mu (message representative). + + mu must be 64 bytes and already incorporates the context, so no + context is accepted here. + """ + + @abc.abstractmethod + def __copy__(self) -> MLDSA65PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLDSA65PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mldsa"): + MLDSA65PrivateKey.register(rust_openssl.mldsa.MLDSA65PrivateKey) + + +class MLDSA87PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> MLDSA87PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-87 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.from_mldsa87_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def public_bytes_raw(self) -> bytes: + """ + The raw bytes of the public key. + Equivalent to public_bytes(Raw, Raw). + + The public key is 2,592 bytes for MLDSA-87. + """ + + @abc.abstractmethod + def verify( + self, + signature: Buffer, + data: Buffer, + context: Buffer | None = None, + ) -> None: + """ + Verify the signature. + """ + + @abc.abstractmethod + def verify_mu( + self, + signature: Buffer, + mu: Buffer, + ) -> None: + """ + Verify the signature over a precomputed mu (message representative). + + mu must be 64 bytes. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> MLDSA87PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLDSA87PublicKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mldsa"): + MLDSA87PublicKey.register(rust_openssl.mldsa.MLDSA87PublicKey) + + +class MLDSA87PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> MLDSA87PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-87 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.generate_mldsa87_key() + + @classmethod + def from_seed_bytes(cls, data: Buffer) -> MLDSA87PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-87 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.from_mldsa87_seed_bytes(data) + + @abc.abstractmethod + def public_key(self) -> MLDSA87PublicKey: + """ + The MLDSA87PublicKey derived from the private key. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + + This method only returns the serialization of the seed form of the + private key, never the expanded one. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + + This method only returns the seed form of the private key (32 bytes). + """ + + @abc.abstractmethod + def sign(self, data: Buffer, context: Buffer | None = None) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def sign_mu(self, mu: Buffer) -> bytes: + """ + Signs a precomputed mu (message representative). + + mu must be 64 bytes and already incorporates the context, so no + context is accepted here. + """ + + @abc.abstractmethod + def __copy__(self) -> MLDSA87PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLDSA87PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mldsa"): + MLDSA87PrivateKey.register(rust_openssl.mldsa.MLDSA87PrivateKey) + MLDSAMuHasher = rust_openssl.mldsa.MLDSAMuHasher diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mlkem.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mlkem.py new file mode 100644 index 0000000..64bca11 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mlkem.py @@ -0,0 +1,278 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization +from cryptography.utils import Buffer + + +class MLKEM768PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: Buffer) -> MLKEM768PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mlkem_supported(): + raise UnsupportedAlgorithm( + "ML-KEM-768 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mlkem.from_mlkem768_public_bytes(data) + + @abc.abstractmethod + def encapsulate(self) -> tuple[bytes, bytes]: + """ + Encapsulate: returns (shared_secret, ciphertext). + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def public_bytes_raw(self) -> bytes: + """ + The raw bytes of the public key. + Equivalent to public_bytes(Raw, Raw). + + The public key is 1,184 bytes for ML-KEM-768. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> MLKEM768PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLKEM768PublicKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mlkem"): + MLKEM768PublicKey.register(rust_openssl.mlkem.MLKEM768PublicKey) + + +class MLKEM768PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> MLKEM768PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mlkem_supported(): + raise UnsupportedAlgorithm( + "ML-KEM-768 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mlkem.generate_mlkem768_key() + + @classmethod + def from_seed_bytes(cls, data: Buffer) -> MLKEM768PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mlkem_supported(): + raise UnsupportedAlgorithm( + "ML-KEM-768 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mlkem.from_mlkem768_seed_bytes(data) + + @abc.abstractmethod + def decapsulate(self, ciphertext: Buffer) -> bytes: + """ + Decapsulate: returns shared_secret. + """ + + @abc.abstractmethod + def public_key(self) -> MLKEM768PublicKey: + """ + The MLKEM768PublicKey derived from this private key. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key (64-byte seed). + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + """ + + @abc.abstractmethod + def __copy__(self) -> MLKEM768PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLKEM768PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mlkem"): + MLKEM768PrivateKey.register(rust_openssl.mlkem.MLKEM768PrivateKey) + + +class MLKEM1024PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: Buffer) -> MLKEM1024PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mlkem_supported(): + raise UnsupportedAlgorithm( + "ML-KEM-1024 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mlkem.from_mlkem1024_public_bytes(data) + + @abc.abstractmethod + def encapsulate(self) -> tuple[bytes, bytes]: + """ + Encapsulate: returns (shared_secret, ciphertext). + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def public_bytes_raw(self) -> bytes: + """ + The raw bytes of the public key. + Equivalent to public_bytes(Raw, Raw). + + The public key is 1,568 bytes for ML-KEM-1024. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> MLKEM1024PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLKEM1024PublicKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mlkem"): + MLKEM1024PublicKey.register(rust_openssl.mlkem.MLKEM1024PublicKey) + + +class MLKEM1024PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> MLKEM1024PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mlkem_supported(): + raise UnsupportedAlgorithm( + "ML-KEM-1024 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mlkem.generate_mlkem1024_key() + + @classmethod + def from_seed_bytes(cls, data: Buffer) -> MLKEM1024PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mlkem_supported(): + raise UnsupportedAlgorithm( + "ML-KEM-1024 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mlkem.from_mlkem1024_seed_bytes(data) + + @abc.abstractmethod + def decapsulate(self, ciphertext: Buffer) -> bytes: + """ + Decapsulate: returns shared_secret. + """ + + @abc.abstractmethod + def public_key(self) -> MLKEM1024PublicKey: + """ + The MLKEM1024PublicKey derived from this private key. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key (64-byte seed). + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + """ + + @abc.abstractmethod + def __copy__(self) -> MLKEM1024PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLKEM1024PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mlkem"): + MLKEM1024PrivateKey.register(rust_openssl.mlkem.MLKEM1024PrivateKey) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py new file mode 100644 index 0000000..1fd2873 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py @@ -0,0 +1,141 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives._asymmetric import ( + AsymmetricPadding as AsymmetricPadding, +) +from cryptography.hazmat.primitives.asymmetric import rsa + + +class PKCS1v15(AsymmetricPadding): + name = "EMSA-PKCS1-v1_5" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PKCS1v15): + return NotImplemented + + return True + + +class _MaxLength: + "Sentinel value for `MAX_LENGTH`." + + +class _Auto: + "Sentinel value for `AUTO`." + + +class _DigestLength: + "Sentinel value for `DIGEST_LENGTH`." + + +class PSS(AsymmetricPadding): + MAX_LENGTH = _MaxLength() + AUTO = _Auto() + DIGEST_LENGTH = _DigestLength() + name = "EMSA-PSS" + _salt_length: int | _MaxLength | _Auto | _DigestLength + + def __init__( + self, + mgf: MGF, + salt_length: int | _MaxLength | _Auto | _DigestLength, + ) -> None: + self._mgf = mgf + + if not isinstance( + salt_length, (int, _MaxLength, _Auto, _DigestLength) + ): + raise TypeError( + "salt_length must be an integer, MAX_LENGTH, " + "DIGEST_LENGTH, or AUTO" + ) + + if isinstance(salt_length, int) and salt_length < 0: + raise ValueError("salt_length must be zero or greater.") + + self._salt_length = salt_length + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PSS): + return NotImplemented + + return ( + self._mgf == other._mgf and self._salt_length == other._salt_length + ) + + @property + def mgf(self) -> MGF: + return self._mgf + + +class OAEP(AsymmetricPadding): + name = "EME-OAEP" + + def __init__( + self, + mgf: MGF, + algorithm: hashes.HashAlgorithm, + label: bytes | None, + ): + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of hashes.HashAlgorithm.") + + self._mgf = mgf + self._algorithm = algorithm + self._label = label + + def __eq__(self, other: object) -> bool: + if not isinstance(other, OAEP): + return NotImplemented + + return ( + self._mgf == other._mgf + and self._algorithm == other._algorithm + and self._label == other._label + ) + + @property + def algorithm(self) -> hashes.HashAlgorithm: + return self._algorithm + + @property + def mgf(self) -> MGF: + return self._mgf + + +class MGF(metaclass=abc.ABCMeta): + _algorithm: hashes.HashAlgorithm + + +class MGF1(MGF): + def __init__(self, algorithm: hashes.HashAlgorithm): + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of hashes.HashAlgorithm.") + + self._algorithm = algorithm + + def __eq__(self, other: object) -> bool: + if not isinstance(other, MGF1): + return NotImplemented + + return self._algorithm == other._algorithm + + +def calculate_max_pss_salt_length( + key: rsa.RSAPrivateKey | rsa.RSAPublicKey, + hash_algorithm: hashes.HashAlgorithm, +) -> int: + if not isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)): + raise TypeError("key must be an RSA public or private key") + # bit length - 1 per RFC 3447 + emlen = (key.key_size + 6) // 8 + salt_length = emlen - hash_algorithm.digest_size - 2 + assert salt_length >= 0 + return salt_length diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py new file mode 100644 index 0000000..d730ceb --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -0,0 +1,295 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import random +import typing +from math import gcd, lcm + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization, hashes +from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils + + +class RSAPrivateKey(metaclass=abc.ABCMeta): + @abc.abstractmethod + def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes: + """ + Decrypts the provided ciphertext. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the public modulus. + """ + + @abc.abstractmethod + def public_key(self) -> RSAPublicKey: + """ + The RSAPublicKey associated with this private key. + """ + + @abc.abstractmethod + def sign( + self, + data: bytes, + padding: AsymmetricPadding, + algorithm: asym_utils.Prehashed + | hashes.HashAlgorithm + | asym_utils.NoDigestInfo, + ) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def private_numbers(self) -> RSAPrivateNumbers: + """ + Returns an RSAPrivateNumbers. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def __copy__(self) -> RSAPrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> RSAPrivateKey: + """ + Returns a deep copy. + """ + + +RSAPrivateKeyWithSerialization = RSAPrivateKey +RSAPrivateKey.register(rust_openssl.rsa.RSAPrivateKey) + + +class RSAPublicKey(metaclass=abc.ABCMeta): + @abc.abstractmethod + def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes: + """ + Encrypts the given plaintext. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the public modulus. + """ + + @abc.abstractmethod + def public_numbers(self) -> RSAPublicNumbers: + """ + Returns an RSAPublicNumbers + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def verify( + self, + signature: bytes, + data: bytes, + padding: AsymmetricPadding, + algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, + ) -> None: + """ + Verifies the signature of the data. + """ + + @abc.abstractmethod + def recover_data_from_signature( + self, + signature: bytes, + padding: AsymmetricPadding, + algorithm: hashes.HashAlgorithm | asym_utils.NoDigestInfo | None, + ) -> bytes: + """ + Recovers the original data from the signature. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> RSAPublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> RSAPublicKey: + """ + Returns a deep copy. + """ + + +RSAPublicKeyWithSerialization = RSAPublicKey +RSAPublicKey.register(rust_openssl.rsa.RSAPublicKey) + +RSAPrivateNumbers = rust_openssl.rsa.RSAPrivateNumbers +RSAPublicNumbers = rust_openssl.rsa.RSAPublicNumbers + + +def generate_private_key( + public_exponent: int, + key_size: int, + backend: typing.Any = None, +) -> RSAPrivateKey: + _verify_rsa_parameters(public_exponent, key_size) + return rust_openssl.rsa.generate_private_key(public_exponent, key_size) + + +def _verify_rsa_parameters(public_exponent: int, key_size: int) -> None: + if public_exponent not in (3, 65537): + raise ValueError( + "public_exponent must be either 3 (for legacy compatibility) or " + "65537. Almost everyone should choose 65537 here!" + ) + + if key_size < 1024: + raise ValueError("key_size must be at least 1024-bits.") + + +def _modinv(e: int, m: int) -> int: + """ + Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1 + """ + x1, x2 = 1, 0 + a, b = e, m + while b > 0: + q, r = divmod(a, b) + xn = x1 - q * x2 + a, b, x1, x2 = b, r, x2, xn + return x1 % m + + +def rsa_crt_iqmp(p: int, q: int) -> int: + """ + Compute the CRT (q ** -1) % p value from RSA primes p and q. + """ + if p <= 1 or q <= 1: + raise ValueError("Values can't be <= 1") + return _modinv(q, p) + + +def rsa_crt_dmp1(private_exponent: int, p: int) -> int: + """ + Compute the CRT private_exponent % (p - 1) value from the RSA + private_exponent (d) and p. + """ + if private_exponent <= 1 or p <= 1: + raise ValueError("Values can't be <= 1") + return private_exponent % (p - 1) + + +def rsa_crt_dmq1(private_exponent: int, q: int) -> int: + """ + Compute the CRT private_exponent % (q - 1) value from the RSA + private_exponent (d) and q. + """ + if private_exponent <= 1 or q <= 1: + raise ValueError("Values can't be <= 1") + return private_exponent % (q - 1) + + +def rsa_recover_private_exponent(e: int, p: int, q: int) -> int: + """ + Compute the RSA private_exponent (d) given the public exponent (e) + and the RSA primes p and q. + + This uses the Carmichael totient function to generate the + smallest possible working value of the private exponent. + """ + # This lambda_n is the Carmichael totient function. + # The original RSA paper uses the Euler totient function + # here: phi_n = (p - 1) * (q - 1) + # Either version of the private exponent will work, but the + # one generated by the older formulation may be larger + # than necessary. (lambda_n always divides phi_n) + if e <= 1 or p <= 1 or q <= 1: + raise ValueError("Values can't be <= 1") + return _modinv(e, lcm(p - 1, q - 1)) + + +# Controls the number of iterations rsa_recover_prime_factors will perform +# to obtain the prime factors. +_MAX_RECOVERY_ATTEMPTS = 500 + + +def rsa_recover_prime_factors(n: int, e: int, d: int) -> tuple[int, int]: + """ + Compute factors p and q from the private exponent d. We assume that n has + no more than two factors. This function is adapted from code in PyCrypto. + """ + # reject invalid values early + if d <= 1 or e <= 1: + raise ValueError("d, e can't be <= 1") + if 17 != pow(17, e * d, n): + raise ValueError("n, d, e don't match") + # See 8.2.2(i) in Handbook of Applied Cryptography. + ktot = d * e - 1 + # The quantity d*e-1 is a multiple of phi(n), even, + # and can be represented as t*2^s. + t = ktot + while t % 2 == 0: + t = t // 2 + # Cycle through all multiplicative inverses in Zn. + # The algorithm is non-deterministic, but there is a 50% chance + # any candidate a leads to successful factoring. + # See "Digitalized Signatures and Public Key Functions as Intractable + # as Factorization", M. Rabin, 1979 + spotted = False + tries = 0 + while not spotted and tries < _MAX_RECOVERY_ATTEMPTS: + a = random.randint(2, n - 1) + tries += 1 + k = t + # Cycle through all values a^{t*2^i}=a^k + while k < ktot: + cand = pow(a, k, n) + # Check if a^k is a non-trivial root of unity (mod n) + if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1: + # We have found a number such that (cand-1)(cand+1)=0 (mod n). + # Either of the terms divides n. + p = gcd(cand + 1, n) + spotted = True + break + k *= 2 + if not spotted: + raise ValueError("Unable to compute factors p and q from exponent d.") + # Found ! + q, r = divmod(n, p) + assert r == 0 + p, q = sorted((p, q), reverse=True) + return (p, q) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/types.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/types.py new file mode 100644 index 0000000..dbf4b74 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/types.py @@ -0,0 +1,94 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography.hazmat.primitives.asymmetric import ( + dh, + dsa, + ec, + ed448, + ed25519, + mldsa, + mlkem, + rsa, + x448, + x25519, +) + +# Every asymmetric key type. These use the private DH aliases so that +# importing this module doesn't trigger the FFDH deprecation warning. +PublicKeyTypes = typing.Union[ + dh._DHPublicKey, + dsa.DSAPublicKey, + rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey, + mldsa.MLDSA44PublicKey, + mldsa.MLDSA65PublicKey, + mldsa.MLDSA87PublicKey, + mlkem.MLKEM768PublicKey, + mlkem.MLKEM1024PublicKey, + x25519.X25519PublicKey, + x448.X448PublicKey, +] +# Every asymmetric key type +PrivateKeyTypes = typing.Union[ + dh._DHPrivateKey, + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, + mldsa.MLDSA44PrivateKey, + mldsa.MLDSA65PrivateKey, + mldsa.MLDSA87PrivateKey, + mlkem.MLKEM768PrivateKey, + mlkem.MLKEM1024PrivateKey, + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, + x25519.X25519PrivateKey, + x448.X448PrivateKey, +] +# Just the key types we allow to be used for x509 signing. This mirrors +# the certificate public key types +CertificateIssuerPrivateKeyTypes = typing.Union[ + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, + mldsa.MLDSA44PrivateKey, + mldsa.MLDSA65PrivateKey, + mldsa.MLDSA87PrivateKey, +] +# Just the key types we allow to be used for x509 signing. This mirrors +# the certificate private key types +CertificateIssuerPublicKeyTypes = typing.Union[ + dsa.DSAPublicKey, + rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey, + mldsa.MLDSA44PublicKey, + mldsa.MLDSA65PublicKey, + mldsa.MLDSA87PublicKey, +] +# This type removes DHPublicKey. x448/x25519/mlkem can be a public key +# but cannot be used in signing so they are allowed here. +CertificatePublicKeyTypes = typing.Union[ + dsa.DSAPublicKey, + rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey, + mldsa.MLDSA44PublicKey, + mldsa.MLDSA65PublicKey, + mldsa.MLDSA87PublicKey, + mlkem.MLKEM768PublicKey, + mlkem.MLKEM1024PublicKey, + x25519.X25519PublicKey, + x448.X448PublicKey, +] diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py new file mode 100644 index 0000000..c01c342 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py @@ -0,0 +1,28 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import asn1 +from cryptography.hazmat.primitives import hashes + +decode_dss_signature = asn1.decode_dss_signature +encode_dss_signature = asn1.encode_dss_signature + + +class NoDigestInfo: + pass + + +class Prehashed: + def __init__(self, algorithm: hashes.HashAlgorithm): + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of HashAlgorithm.") + + self._algorithm = algorithm + self._digest_size = algorithm.digest_size + + @property + def digest_size(self) -> int: + return self._digest_size diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py new file mode 100644 index 0000000..7498998 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py @@ -0,0 +1,134 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization +from cryptography.utils import Buffer + + +class X25519PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> X25519PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x25519_supported(): + raise UnsupportedAlgorithm( + "X25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return rust_openssl.x25519.from_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def public_bytes_raw(self) -> bytes: + """ + The raw bytes of the public key. + Equivalent to public_bytes(Raw, Raw). + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> X25519PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> X25519PublicKey: + """ + Returns a deep copy. + """ + + +X25519PublicKey.register(rust_openssl.x25519.X25519PublicKey) + + +class X25519PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> X25519PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x25519_supported(): + raise UnsupportedAlgorithm( + "X25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + return rust_openssl.x25519.generate_key() + + @classmethod + def from_private_bytes(cls, data: Buffer) -> X25519PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x25519_supported(): + raise UnsupportedAlgorithm( + "X25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return rust_openssl.x25519.from_private_bytes(data) + + @abc.abstractmethod + def public_key(self) -> X25519PublicKey: + """ + Returns the public key associated with this private key + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + """ + + @abc.abstractmethod + def exchange(self, peer_public_key: X25519PublicKey) -> bytes: + """ + Performs a key exchange operation using the provided peer's public key. + """ + + @abc.abstractmethod + def __copy__(self) -> X25519PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> X25519PrivateKey: + """ + Returns a deep copy. + """ + + +X25519PrivateKey.register(rust_openssl.x25519.X25519PrivateKey) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py new file mode 100644 index 0000000..b9dc826 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py @@ -0,0 +1,137 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization +from cryptography.utils import Buffer + + +class X448PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> X448PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x448_supported(): + raise UnsupportedAlgorithm( + "X448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return rust_openssl.x448.from_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def public_bytes_raw(self) -> bytes: + """ + The raw bytes of the public key. + Equivalent to public_bytes(Raw, Raw). + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> X448PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> X448PublicKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "x448"): + X448PublicKey.register(rust_openssl.x448.X448PublicKey) + + +class X448PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> X448PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x448_supported(): + raise UnsupportedAlgorithm( + "X448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return rust_openssl.x448.generate_key() + + @classmethod + def from_private_bytes(cls, data: Buffer) -> X448PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x448_supported(): + raise UnsupportedAlgorithm( + "X448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return rust_openssl.x448.from_private_bytes(data) + + @abc.abstractmethod + def public_key(self) -> X448PublicKey: + """ + Returns the public key associated with this private key + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + """ + + @abc.abstractmethod + def exchange(self, peer_public_key: X448PublicKey) -> bytes: + """ + Performs a key exchange operation using the provided peer's public key. + """ + + @abc.abstractmethod + def __copy__(self) -> X448PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> X448PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "x448"): + X448PrivateKey.register(rust_openssl.x448.X448PrivateKey) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py new file mode 100644 index 0000000..10c15d0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py @@ -0,0 +1,27 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.primitives._cipheralgorithm import ( + BlockCipherAlgorithm, + CipherAlgorithm, +) +from cryptography.hazmat.primitives.ciphers.base import ( + AEADCipherContext, + AEADDecryptionContext, + AEADEncryptionContext, + Cipher, + CipherContext, +) + +__all__ = [ + "AEADCipherContext", + "AEADDecryptionContext", + "AEADEncryptionContext", + "BlockCipherAlgorithm", + "Cipher", + "CipherAlgorithm", + "CipherContext", +] diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..db8eca2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-312.pyc new file mode 100644 index 0000000..9f4a01e Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-312.pyc new file mode 100644 index 0000000..0e1ce83 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..1613bc2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-312.pyc new file mode 100644 index 0000000..3c78bfa Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/aead.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/aead.py new file mode 100644 index 0000000..c8a582d --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/aead.py @@ -0,0 +1,23 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl + +__all__ = [ + "AESCCM", + "AESGCM", + "AESGCMSIV", + "AESOCB3", + "AESSIV", + "ChaCha20Poly1305", +] + +AESGCM = rust_openssl.aead.AESGCM +ChaCha20Poly1305 = rust_openssl.aead.ChaCha20Poly1305 +AESCCM = rust_openssl.aead.AESCCM +AESSIV = rust_openssl.aead.AESSIV +AESOCB3 = rust_openssl.aead.AESOCB3 +AESGCMSIV = rust_openssl.aead.AESGCMSIV diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py new file mode 100644 index 0000000..6b20dd5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py @@ -0,0 +1,138 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography import utils +from cryptography.hazmat.decrepit.ciphers.algorithms import ( + ARC4 as ARC4, +) +from cryptography.hazmat.decrepit.ciphers.algorithms import ( + CAST5 as CAST5, +) +from cryptography.hazmat.decrepit.ciphers.algorithms import ( + IDEA as IDEA, +) +from cryptography.hazmat.decrepit.ciphers.algorithms import ( + SEED as SEED, +) +from cryptography.hazmat.decrepit.ciphers.algorithms import ( + Blowfish as Blowfish, +) +from cryptography.hazmat.decrepit.ciphers.algorithms import ( + Camellia as Camellia, +) +from cryptography.hazmat.decrepit.ciphers.algorithms import ( + TripleDES as TripleDES, +) +from cryptography.hazmat.primitives._cipheralgorithm import _verify_key_size +from cryptography.hazmat.primitives.ciphers import ( + BlockCipherAlgorithm, + CipherAlgorithm, +) + + +class AES(BlockCipherAlgorithm): + name = "AES" + block_size = 128 + # 512 added to support AES-256-XTS, which uses 512-bit keys + key_sizes = frozenset([128, 192, 256, 512]) + + def __init__(self, key: utils.Buffer): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class AES128(BlockCipherAlgorithm): + name = "AES" + block_size = 128 + key_sizes = frozenset([128]) + key_size = 128 + + def __init__(self, key: utils.Buffer): + self.key = _verify_key_size(self, key) + + +class AES256(BlockCipherAlgorithm): + name = "AES" + block_size = 128 + key_sizes = frozenset([256]) + key_size = 256 + + def __init__(self, key: utils.Buffer): + self.key = _verify_key_size(self, key) + + +utils.deprecated( + Camellia, + __name__, + "Camellia has been moved to " + "cryptography.hazmat.decrepit.ciphers.algorithms.Camellia and " + "will be removed from " + "cryptography.hazmat.primitives.ciphers.algorithms in 49.0.0.", + utils.DeprecatedIn43, + name="Camellia", +) + + +utils.deprecated( + ARC4, + __name__, + "ARC4 has been moved to " + "cryptography.hazmat.decrepit.ciphers.algorithms.ARC4 and " + "will be removed from " + "cryptography.hazmat.primitives.ciphers.algorithms in 48.0.0.", + utils.DeprecatedIn43, + name="ARC4", +) + + +utils.deprecated( + TripleDES, + __name__, + "TripleDES has been moved to " + "cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES and " + "will be removed from " + "cryptography.hazmat.primitives.ciphers.algorithms in 48.0.0.", + utils.DeprecatedIn43, + name="TripleDES", +) + + +class ChaCha20(CipherAlgorithm): + name = "ChaCha20" + key_sizes = frozenset([256]) + + def __init__(self, key: utils.Buffer, nonce: utils.Buffer): + self.key = _verify_key_size(self, key) + utils._check_byteslike("nonce", nonce) + + if len(nonce) != 16: + raise ValueError("nonce must be 128-bits (16 bytes)") + + self._nonce = nonce + + @property + def nonce(self) -> utils.Buffer: + return self._nonce + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class SM4(BlockCipherAlgorithm): + name = "SM4" + block_size = 128 + key_sizes = frozenset([128]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/base.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/base.py new file mode 100644 index 0000000..24fceea --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/base.py @@ -0,0 +1,146 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import typing + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives._cipheralgorithm import CipherAlgorithm +from cryptography.hazmat.primitives.ciphers import modes +from cryptography.utils import Buffer + + +class CipherContext(metaclass=abc.ABCMeta): + @abc.abstractmethod + def update(self, data: Buffer) -> bytes: + """ + Processes the provided bytes through the cipher and returns the results + as bytes. + """ + + @abc.abstractmethod + def update_into(self, data: Buffer, buf: Buffer) -> int: + """ + Processes the provided bytes and writes the resulting data into the + provided buffer. Returns the number of bytes written. + """ + + @abc.abstractmethod + def finalize(self) -> bytes: + """ + Returns the results of processing the final block as bytes. + """ + + @abc.abstractmethod + def reset_nonce(self, nonce: bytes) -> None: + """ + Resets the nonce for the cipher context to the provided value. + Raises an exception if it does not support reset or if the + provided nonce does not have a valid length. + """ + + +class AEADCipherContext(CipherContext, metaclass=abc.ABCMeta): + @abc.abstractmethod + def authenticate_additional_data(self, data: Buffer) -> None: + """ + Authenticates the provided bytes. + """ + + +class AEADDecryptionContext(AEADCipherContext, metaclass=abc.ABCMeta): + @abc.abstractmethod + def finalize_with_tag(self, tag: bytes) -> bytes: + """ + Returns the results of processing the final block as bytes and allows + delayed passing of the authentication tag. + """ + + +class AEADEncryptionContext(AEADCipherContext, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def tag(self) -> bytes: + """ + Returns tag bytes. This is only available after encryption is + finalized. + """ + + +Mode = typing.TypeVar( + "Mode", bound=typing.Optional[modes.Mode], covariant=True +) + + +class Cipher(typing.Generic[Mode]): + def __init__( + self, + algorithm: CipherAlgorithm, + mode: Mode, + backend: typing.Any = None, + ) -> None: + if not isinstance(algorithm, CipherAlgorithm): + raise TypeError("Expected interface of CipherAlgorithm.") + + if mode is not None: + # mypy needs this assert to narrow the type from our generic + # type. Maybe it won't some time in the future. + assert isinstance(mode, modes.Mode) + mode.validate_for_algorithm(algorithm) + + self.algorithm = algorithm + self.mode = mode + + @typing.overload + def encryptor( + self: Cipher[modes.ModeWithAuthenticationTag], + ) -> AEADEncryptionContext: ... + + @typing.overload + def encryptor( + self: _CIPHER_TYPE, + ) -> CipherContext: ... + + def encryptor(self): + if isinstance(self.mode, modes.ModeWithAuthenticationTag): + if self.mode.tag is not None: + raise ValueError( + "Authentication tag must be None when encrypting." + ) + + return rust_openssl.ciphers.create_encryption_ctx( + self.algorithm, self.mode + ) + + @typing.overload + def decryptor( + self: Cipher[modes.ModeWithAuthenticationTag], + ) -> AEADDecryptionContext: ... + + @typing.overload + def decryptor( + self: _CIPHER_TYPE, + ) -> CipherContext: ... + + def decryptor(self): + return rust_openssl.ciphers.create_decryption_ctx( + self.algorithm, self.mode + ) + + +_CIPHER_TYPE = Cipher[ + typing.Union[ + modes.ModeWithNonce, + modes.ModeWithTweak, + modes.ECB, + modes.ModeWithInitializationVector, + None, + ] +] + +CipherContext.register(rust_openssl.ciphers.CipherContext) +AEADEncryptionContext.register(rust_openssl.ciphers.AEADEncryptionContext) +AEADDecryptionContext.register(rust_openssl.ciphers.AEADDecryptionContext) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/modes.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/modes.py new file mode 100644 index 0000000..c2008e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/modes.py @@ -0,0 +1,192 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.decrepit.ciphers.modes import CFB as CFB +from cryptography.hazmat.decrepit.ciphers.modes import CFB8 as CFB8 +from cryptography.hazmat.decrepit.ciphers.modes import OFB as OFB +from cryptography.hazmat.primitives._cipheralgorithm import ( + BlockCipherAlgorithm, + CipherAlgorithm, +) +from cryptography.hazmat.primitives._modes import ( + Mode as Mode, +) +from cryptography.hazmat.primitives._modes import ( + ModeWithAuthenticationTag as ModeWithAuthenticationTag, +) +from cryptography.hazmat.primitives._modes import ( + ModeWithInitializationVector as ModeWithInitializationVector, +) +from cryptography.hazmat.primitives._modes import ( + ModeWithNonce as ModeWithNonce, +) +from cryptography.hazmat.primitives._modes import ( + ModeWithTweak as ModeWithTweak, +) +from cryptography.hazmat.primitives._modes import ( + _check_aes_key_length, + _check_iv_and_key_length, + _check_nonce_length, +) +from cryptography.hazmat.primitives.ciphers import algorithms + + +class CBC(ModeWithInitializationVector): + name = "CBC" + + def __init__(self, initialization_vector: utils.Buffer): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> utils.Buffer: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class XTS(ModeWithTweak): + name = "XTS" + + def __init__(self, tweak: utils.Buffer): + utils._check_byteslike("tweak", tweak) + + if len(tweak) != 16: + raise ValueError("tweak must be 128-bits (16 bytes)") + + self._tweak = tweak + + @property + def tweak(self) -> utils.Buffer: + return self._tweak + + def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: + if isinstance(algorithm, (algorithms.AES128, algorithms.AES256)): + raise TypeError( + "The AES128 and AES256 classes do not support XTS, please use " + "the standard AES class instead." + ) + + if algorithm.key_size not in (256, 512): + raise ValueError( + "The XTS specification requires a 256-bit key for AES-128-XTS" + " and 512-bit key for AES-256-XTS" + ) + + +class ECB(Mode): + name = "ECB" + + validate_for_algorithm = _check_aes_key_length + + +class CTR(ModeWithNonce): + name = "CTR" + + def __init__(self, nonce: utils.Buffer): + utils._check_byteslike("nonce", nonce) + self._nonce = nonce + + @property + def nonce(self) -> utils.Buffer: + return self._nonce + + def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: + _check_aes_key_length(self, algorithm) + _check_nonce_length(self.nonce, self.name, algorithm) + + +class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag): + name = "GCM" + _MAX_ENCRYPTED_BYTES = (2**39 - 256) // 8 + _MAX_AAD_BYTES = (2**64 - 1) // 8 + + def __init__( + self, + initialization_vector: utils.Buffer, + tag: bytes | None = None, + min_tag_length: int = 16, + ): + # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive + # This is a sane limit anyway so we'll enforce it here. + utils._check_byteslike("initialization_vector", initialization_vector) + if len(initialization_vector) < 8 or len(initialization_vector) > 128: + raise ValueError( + "initialization_vector must be between 8 and 128 bytes (64 " + "and 1024 bits)." + ) + self._initialization_vector = initialization_vector + if min_tag_length < 4: + raise ValueError("min_tag_length must be >= 4") + if tag is not None: + utils._check_bytes("tag", tag) + if len(tag) < min_tag_length: + raise ValueError( + f"Authentication tag must be {min_tag_length} bytes or " + "longer." + ) + self._tag = tag + self._min_tag_length = min_tag_length + + @property + def tag(self) -> bytes | None: + return self._tag + + @property + def initialization_vector(self) -> utils.Buffer: + return self._initialization_vector + + def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: + _check_aes_key_length(self, algorithm) + if not isinstance(algorithm, BlockCipherAlgorithm): + raise UnsupportedAlgorithm( + "GCM requires a block cipher algorithm", + _Reasons.UNSUPPORTED_CIPHER, + ) + block_size_bytes = algorithm.block_size // 8 + if self._tag is not None and len(self._tag) > block_size_bytes: + raise ValueError( + f"Authentication tag cannot be more than {block_size_bytes} " + "bytes." + ) + + +utils.deprecated( + OFB, + __name__, + "OFB has been moved to " + "cryptography.hazmat.decrepit.ciphers.modes.OFB and " + "will be removed from " + "cryptography.hazmat.primitives.ciphers.modes in 49.0.0.", + utils.DeprecatedIn47, + name="OFB", +) + + +utils.deprecated( + CFB, + __name__, + "CFB has been moved to " + "cryptography.hazmat.decrepit.ciphers.modes.CFB and " + "will be removed from " + "cryptography.hazmat.primitives.ciphers.modes in 49.0.0.", + utils.DeprecatedIn47, + name="CFB", +) + + +utils.deprecated( + CFB8, + __name__, + "CFB8 has been moved to " + "cryptography.hazmat.decrepit.ciphers.modes.CFB8 and " + "will be removed from " + "cryptography.hazmat.primitives.ciphers.modes in 49.0.0.", + utils.DeprecatedIn47, + name="CFB8", +) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/cmac.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/cmac.py new file mode 100644 index 0000000..2c67ce2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/cmac.py @@ -0,0 +1,10 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl + +__all__ = ["CMAC"] +CMAC = rust_openssl.cmac.CMAC diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/constant_time.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/constant_time.py new file mode 100644 index 0000000..3975c71 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/constant_time.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import hmac + + +def bytes_eq(a: bytes, b: bytes) -> bool: + if not isinstance(a, bytes) or not isinstance(b, bytes): + raise TypeError("a and b must be bytes.") + + return hmac.compare_digest(a, b) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hashes.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hashes.py new file mode 100644 index 0000000..50ed355 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hashes.py @@ -0,0 +1,357 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import sys + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.utils import Buffer + +__all__ = [ + "MD5", + "SHA1", + "SHA3_224", + "SHA3_256", + "SHA3_384", + "SHA3_512", + "SHA224", + "SHA256", + "SHA384", + "SHA512", + "SHA512_224", + "SHA512_256", + "SHAKE128", + "SHAKE256", + "SM3", + "BLAKE2b", + "BLAKE2s", + "ExtendableOutputFunction", + "Hash", + "HashAlgorithm", + "HashContext", + "XOFHash", +] + + +class HashAlgorithm(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + A string naming this algorithm (e.g. "sha256", "md5"). + """ + + @property + @abc.abstractmethod + def digest_size(self) -> int: + """ + The size of the resulting digest in bytes. + """ + + @property + @abc.abstractmethod + def block_size(self) -> int | None: + """ + The internal block size of the hash function, or None if the hash + function does not use blocks internally (e.g. SHA3). + """ + + +class HashContext(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def algorithm(self) -> HashAlgorithm: + """ + A HashAlgorithm that will be used by this context. + """ + + @abc.abstractmethod + def update(self, data: Buffer) -> None: + """ + Processes the provided bytes through the hash. + """ + + @abc.abstractmethod + def finalize(self) -> bytes: + """ + Finalizes the hash context and returns the hash digest as bytes. + """ + + @abc.abstractmethod + def copy(self) -> HashContext: + """ + Return a HashContext that is a copy of the current context. + """ + + +Hash = rust_openssl.hashes.Hash +HashContext.register(Hash) + +XOFHash = rust_openssl.hashes.XOFHash + + +class ExtendableOutputFunction(metaclass=abc.ABCMeta): + """ + An interface for extendable output functions. + """ + + +class SHA1(HashAlgorithm): + name = "sha1" + digest_size = 20 + block_size = 64 + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SHA1): + return NotImplemented + + return True + + +class SHA512_224(HashAlgorithm): # noqa: N801 + name = "sha512-224" + digest_size = 28 + block_size = 128 + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SHA512_224): + return NotImplemented + + return True + + +class SHA512_256(HashAlgorithm): # noqa: N801 + name = "sha512-256" + digest_size = 32 + block_size = 128 + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SHA512_256): + return NotImplemented + + return True + + +class SHA224(HashAlgorithm): + name = "sha224" + digest_size = 28 + block_size = 64 + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SHA224): + return NotImplemented + + return True + + +class SHA256(HashAlgorithm): + name = "sha256" + digest_size = 32 + block_size = 64 + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SHA256): + return NotImplemented + + return True + + +class SHA384(HashAlgorithm): + name = "sha384" + digest_size = 48 + block_size = 128 + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SHA384): + return NotImplemented + + return True + + +class SHA512(HashAlgorithm): + name = "sha512" + digest_size = 64 + block_size = 128 + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SHA512): + return NotImplemented + + return True + + +class SHA3_224(HashAlgorithm): # noqa: N801 + name = "sha3-224" + digest_size = 28 + block_size = None + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SHA3_224): + return NotImplemented + + return True + + +class SHA3_256(HashAlgorithm): # noqa: N801 + name = "sha3-256" + digest_size = 32 + block_size = None + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SHA3_256): + return NotImplemented + + return True + + +class SHA3_384(HashAlgorithm): # noqa: N801 + name = "sha3-384" + digest_size = 48 + block_size = None + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SHA3_384): + return NotImplemented + + return True + + +class SHA3_512(HashAlgorithm): # noqa: N801 + name = "sha3-512" + digest_size = 64 + block_size = None + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SHA3_512): + return NotImplemented + + return True + + +class SHAKE128(HashAlgorithm, ExtendableOutputFunction): + name = "shake128" + block_size = None + + def __init__(self, digest_size: int): + if not isinstance(digest_size, int): + raise TypeError("digest_size must be an integer") + + if digest_size < 1: + raise ValueError("digest_size must be a positive integer") + + self._digest_size = digest_size + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SHAKE128): + return NotImplemented + + return self._digest_size == other._digest_size + + @property + def digest_size(self) -> int: + return self._digest_size + + @classmethod + def xof(cls): + return cls(sys.maxsize) + + +class SHAKE256(HashAlgorithm, ExtendableOutputFunction): + name = "shake256" + block_size = None + + def __init__(self, digest_size: int): + if not isinstance(digest_size, int): + raise TypeError("digest_size must be an integer") + + if digest_size < 1: + raise ValueError("digest_size must be a positive integer") + + self._digest_size = digest_size + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SHAKE256): + return NotImplemented + + return self._digest_size == other._digest_size + + @property + def digest_size(self) -> int: + return self._digest_size + + @classmethod + def xof(cls): + return cls(sys.maxsize) + + +class MD5(HashAlgorithm): + name = "md5" + digest_size = 16 + block_size = 64 + + def __eq__(self, other: object) -> bool: + if not isinstance(other, MD5): + return NotImplemented + + return True + + +class BLAKE2b(HashAlgorithm): + name = "blake2b" + _max_digest_size = 64 + _min_digest_size = 1 + block_size = 128 + + def __init__(self, digest_size: int): + if digest_size != 64: + raise ValueError("Digest size must be 64") + + self._digest_size = digest_size + + def __eq__(self, other: object) -> bool: + if not isinstance(other, BLAKE2b): + return NotImplemented + + return self._digest_size == other._digest_size + + @property + def digest_size(self) -> int: + return self._digest_size + + +class BLAKE2s(HashAlgorithm): + name = "blake2s" + block_size = 64 + _max_digest_size = 32 + _min_digest_size = 1 + + def __init__(self, digest_size: int): + if digest_size != 32: + raise ValueError("Digest size must be 32") + + self._digest_size = digest_size + + def __eq__(self, other: object) -> bool: + if not isinstance(other, BLAKE2s): + return NotImplemented + + return self._digest_size == other._digest_size + + @property + def digest_size(self) -> int: + return self._digest_size + + +class SM3(HashAlgorithm): + name = "sm3" + digest_size = 32 + block_size = 64 + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SM3): + return NotImplemented + + return True diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hmac.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hmac.py new file mode 100644 index 0000000..a9442d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hmac.py @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import hashes + +__all__ = ["HMAC"] + +HMAC = rust_openssl.hmac.HMAC +hashes.HashContext.register(HMAC) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hpke.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hpke.py new file mode 100644 index 0000000..c802808 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hpke.py @@ -0,0 +1,27 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl + +AEAD = rust_openssl.hpke.AEAD +KDF = rust_openssl.hpke.KDF +KEM = rust_openssl.hpke.KEM +MLKEM768X25519PrivateKey = rust_openssl.hpke.MLKEM768X25519PrivateKey +MLKEM768X25519PublicKey = rust_openssl.hpke.MLKEM768X25519PublicKey +MLKEM1024P384PrivateKey = rust_openssl.hpke.MLKEM1024P384PrivateKey +MLKEM1024P384PublicKey = rust_openssl.hpke.MLKEM1024P384PublicKey +Suite = rust_openssl.hpke.Suite + +__all__ = [ + "AEAD", + "KDF", + "KEM", + "MLKEM768X25519PrivateKey", + "MLKEM768X25519PublicKey", + "MLKEM1024P384PrivateKey", + "MLKEM1024P384PublicKey", + "Suite", +] diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__init__.py new file mode 100644 index 0000000..26c45bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__init__.py @@ -0,0 +1,32 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.utils import Buffer + + +class KeyDerivationFunction(metaclass=abc.ABCMeta): + @abc.abstractmethod + def derive(self, key_material: bytes) -> bytes: + """ + Deterministically generates and returns a new key based on the existing + key material. + """ + + @abc.abstractmethod + def derive_into(self, key_material: bytes, buffer: Buffer) -> None: + """ + Deterministically generates a new key based on the existing key + material and stores it in the provided buffer. + """ + + @abc.abstractmethod + def verify(self, key_material: bytes, expected_key: bytes) -> None: + """ + Checks whether the key generated by the key material matches the + expected derived key. Raises an exception if they do not match. + """ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6b445a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/argon2.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/argon2.cpython-312.pyc new file mode 100644 index 0000000..36070bf Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/argon2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-312.pyc new file mode 100644 index 0000000..dcb4074 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-312.pyc new file mode 100644 index 0000000..810c14d Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-312.pyc new file mode 100644 index 0000000..4d33310 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-312.pyc new file mode 100644 index 0000000..4053f94 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-312.pyc new file mode 100644 index 0000000..0b45335 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-312.pyc new file mode 100644 index 0000000..bfa290b Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/argon2.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/argon2.py new file mode 100644 index 0000000..03e84d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/argon2.py @@ -0,0 +1,17 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + +Argon2d = rust_openssl.kdf.Argon2d +Argon2i = rust_openssl.kdf.Argon2i +Argon2id = rust_openssl.kdf.Argon2id +KeyDerivationFunction.register(Argon2d) +KeyDerivationFunction.register(Argon2i) +KeyDerivationFunction.register(Argon2id) + +__all__ = ["Argon2d", "Argon2i", "Argon2id"] diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py new file mode 100644 index 0000000..398dc5d --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py @@ -0,0 +1,16 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + +ConcatKDFHash = rust_openssl.kdf.ConcatKDFHash +ConcatKDFHMAC = rust_openssl.kdf.ConcatKDFHMAC + +KeyDerivationFunction.register(ConcatKDFHash) +KeyDerivationFunction.register(ConcatKDFHMAC) + +__all__ = ["ConcatKDFHMAC", "ConcatKDFHash"] diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py new file mode 100644 index 0000000..1e162d9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py @@ -0,0 +1,16 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + +HKDF = rust_openssl.kdf.HKDF +HKDFExpand = rust_openssl.kdf.HKDFExpand + +KeyDerivationFunction.register(HKDF) +KeyDerivationFunction.register(HKDFExpand) + +__all__ = ["HKDF", "HKDFExpand"] diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py new file mode 100644 index 0000000..e559df8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py @@ -0,0 +1,26 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography import utils +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +class Mode(utils.Enum): + CounterMode = "ctr" + + +class CounterLocation(utils.Enum): + BeforeFixed = "before_fixed" + AfterFixed = "after_fixed" + MiddleFixed = "middle_fixed" + + +KBKDFHMAC = rust_openssl.kdf.KBKDFHMAC +KeyDerivationFunction.register(KBKDFHMAC) + +KBKDFCMAC = rust_openssl.kdf.KBKDFCMAC +KeyDerivationFunction.register(KBKDFCMAC) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py new file mode 100644 index 0000000..771d80d --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + +PBKDF2HMAC = rust_openssl.kdf.PBKDF2HMAC +KeyDerivationFunction.register(PBKDF2HMAC) + +__all__ = ["PBKDF2HMAC"] diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py new file mode 100644 index 0000000..f791cee --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py @@ -0,0 +1,19 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import sys + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + +# This is used by the scrypt tests to skip tests that require more memory +# than the MEM_LIMIT +_MEM_LIMIT = sys.maxsize // 2 + +Scrypt = rust_openssl.kdf.Scrypt +KeyDerivationFunction.register(Scrypt) + +__all__ = ["Scrypt"] diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py new file mode 100644 index 0000000..8c4e2d3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + +X963KDF = rust_openssl.kdf.X963KDF +KeyDerivationFunction.register(X963KDF) + +__all__ = ["X963KDF"] diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/keywrap.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/keywrap.py new file mode 100644 index 0000000..123d97b --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/keywrap.py @@ -0,0 +1,25 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl + +__all__ = [ + "InvalidUnwrap", + "aes_key_unwrap", + "aes_key_unwrap_with_padding", + "aes_key_wrap", + "aes_key_wrap_with_padding", +] + + +class InvalidUnwrap(Exception): + pass + + +aes_key_wrap = rust_openssl.keywrap.aes_key_wrap +aes_key_unwrap = rust_openssl.keywrap.aes_key_unwrap +aes_key_wrap_with_padding = rust_openssl.keywrap.aes_key_wrap_with_padding +aes_key_unwrap_with_padding = rust_openssl.keywrap.aes_key_unwrap_with_padding diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/padding.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/padding.py new file mode 100644 index 0000000..f9cd1f1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/padding.py @@ -0,0 +1,69 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography import utils +from cryptography.hazmat.bindings._rust import ( + ANSIX923PaddingContext, + ANSIX923UnpaddingContext, + PKCS7PaddingContext, + PKCS7UnpaddingContext, +) + + +class PaddingContext(metaclass=abc.ABCMeta): + @abc.abstractmethod + def update(self, data: utils.Buffer) -> bytes: + """ + Pads the provided bytes and returns any available data as bytes. + """ + + @abc.abstractmethod + def finalize(self) -> bytes: + """ + Finalize the padding, returns bytes. + """ + + +def _byte_padding_check(block_size: int) -> None: + if not (0 <= block_size <= 2040): + raise ValueError("block_size must be in range(0, 2041).") + + if block_size % 8 != 0: + raise ValueError("block_size must be a multiple of 8.") + + +class PKCS7: + def __init__(self, block_size: int): + _byte_padding_check(block_size) + self.block_size = block_size + + def padder(self) -> PaddingContext: + return PKCS7PaddingContext(self.block_size) + + def unpadder(self) -> PaddingContext: + return PKCS7UnpaddingContext(self.block_size) + + +PaddingContext.register(PKCS7PaddingContext) +PaddingContext.register(PKCS7UnpaddingContext) + + +class ANSIX923: + def __init__(self, block_size: int): + _byte_padding_check(block_size) + self.block_size = block_size + + def padder(self) -> PaddingContext: + return ANSIX923PaddingContext(self.block_size) + + def unpadder(self) -> PaddingContext: + return ANSIX923UnpaddingContext(self.block_size) + + +PaddingContext.register(ANSIX923PaddingContext) +PaddingContext.register(ANSIX923UnpaddingContext) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/poly1305.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/poly1305.py new file mode 100644 index 0000000..7f5a77a --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/poly1305.py @@ -0,0 +1,11 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl + +__all__ = ["Poly1305"] + +Poly1305 = rust_openssl.poly1305.Poly1305 diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__init__.py new file mode 100644 index 0000000..40beb21 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__init__.py @@ -0,0 +1,85 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography import utils +from cryptography.hazmat.primitives._serialization import ( + BestAvailableEncryption, + Encoding, + KeySerializationEncryption, + NoEncryption, + ParameterFormat, + PrivateFormat, + PublicFormat, + _KeySerializationEncryption, +) +from cryptography.hazmat.primitives.asymmetric.dh import _FFDH_DEPRECATION_MSG +from cryptography.hazmat.primitives.serialization.base import ( + load_der_parameters, + load_der_private_key, + load_der_public_key, + load_pem_parameters, + load_pem_private_key, + load_pem_public_key, +) +from cryptography.hazmat.primitives.serialization.ssh import ( + SSHCertificate, + SSHCertificateBuilder, + SSHCertificateType, + SSHCertPrivateKeyTypes, + SSHCertPublicKeyTypes, + SSHPrivateKeyTypes, + SSHPublicKeyTypes, + load_ssh_private_key, + load_ssh_public_identity, + load_ssh_public_key, + ssh_key_fingerprint, +) + +__all__ = [ + "BestAvailableEncryption", + "Encoding", + "KeySerializationEncryption", + "NoEncryption", + "ParameterFormat", + "PrivateFormat", + "PublicFormat", + "SSHCertPrivateKeyTypes", + "SSHCertPublicKeyTypes", + "SSHCertificate", + "SSHCertificateBuilder", + "SSHCertificateType", + "SSHPrivateKeyTypes", + "SSHPublicKeyTypes", + "_KeySerializationEncryption", + "load_der_parameters", + "load_der_private_key", + "load_der_public_key", + "load_pem_parameters", + "load_pem_private_key", + "load_pem_public_key", + "load_ssh_private_key", + "load_ssh_public_identity", + "load_ssh_public_key", + "ssh_key_fingerprint", +] + +# These can only load FFDH parameters, so the functions themselves are +# deprecated alongside the rest of FFDH. +utils.deprecated( + load_pem_parameters, + __name__, + _FFDH_DEPRECATION_MSG, + utils.DeprecatedIn50, + name="load_pem_parameters", +) + +utils.deprecated( + load_der_parameters, + __name__, + _FFDH_DEPRECATION_MSG, + utils.DeprecatedIn50, + name="load_der_parameters", +) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2f5a43f Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..45e5e8c Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-312.pyc new file mode 100644 index 0000000..0d785c5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-312.pyc new file mode 100644 index 0000000..53822c3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-312.pyc new file mode 100644 index 0000000..a49e98b Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/base.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/base.py new file mode 100644 index 0000000..f2b503b --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/base.py @@ -0,0 +1,16 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl + +load_pem_private_key = rust_openssl.keys.load_pem_private_key +load_der_private_key = rust_openssl.keys.load_der_private_key + +load_pem_public_key = rust_openssl.keys.load_pem_public_key +load_der_public_key = rust_openssl.keys.load_der_public_key + +load_pem_parameters = rust_openssl.dh.from_pem_parameters +load_der_parameters = rust_openssl.dh.from_der_parameters diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py new file mode 100644 index 0000000..58884ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py @@ -0,0 +1,176 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing +from collections.abc import Iterable + +from cryptography import x509 +from cryptography.hazmat.bindings._rust import pkcs12 as rust_pkcs12 +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives._serialization import PBES as PBES +from cryptography.hazmat.primitives.asymmetric import ( + dsa, + ec, + ed448, + ed25519, + rsa, +) +from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes + +__all__ = [ + "PBES", + "PKCS12Certificate", + "PKCS12KeyAndCertificates", + "PKCS12PrivateKeyTypes", + "load_key_and_certificates", + "load_pkcs12", + "serialize_java_truststore", + "serialize_key_and_certificates", +] + +PKCS12PrivateKeyTypes = typing.Union[ + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, +] + + +PKCS12Certificate = rust_pkcs12.PKCS12Certificate + + +class PKCS12KeyAndCertificates: + def __init__( + self, + key: PrivateKeyTypes | None, + cert: PKCS12Certificate | None, + additional_certs: list[PKCS12Certificate], + ): + if key is not None and not isinstance( + key, + ( + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, + ), + ): + raise TypeError( + "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448" + " private key, or None." + ) + if cert is not None and not isinstance(cert, PKCS12Certificate): + raise TypeError("cert must be a PKCS12Certificate object or None") + if not all( + isinstance(add_cert, PKCS12Certificate) + for add_cert in additional_certs + ): + raise TypeError( + "all values in additional_certs must be PKCS12Certificate" + " objects" + ) + self._key = key + self._cert = cert + self._additional_certs = additional_certs + + @property + def key(self) -> PrivateKeyTypes | None: + return self._key + + @property + def cert(self) -> PKCS12Certificate | None: + return self._cert + + @property + def additional_certs(self) -> list[PKCS12Certificate]: + return self._additional_certs + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PKCS12KeyAndCertificates): + return NotImplemented + + return ( + self.key == other.key + and self.cert == other.cert + and self.additional_certs == other.additional_certs + ) + + def __hash__(self) -> int: + return hash((self.key, self.cert, tuple(self.additional_certs))) + + def __repr__(self) -> str: + fmt = ( + "" + ) + return fmt.format(self.key, self.cert, self.additional_certs) + + +load_key_and_certificates = rust_pkcs12.load_key_and_certificates +load_pkcs12 = rust_pkcs12.load_pkcs12 + + +_PKCS12CATypes = typing.Union[ + x509.Certificate, + PKCS12Certificate, +] + + +def serialize_java_truststore( + certs: Iterable[PKCS12Certificate], + encryption_algorithm: serialization.KeySerializationEncryption, +) -> bytes: + if not certs: + raise ValueError("You must supply at least one cert") + + if not isinstance( + encryption_algorithm, serialization.KeySerializationEncryption + ): + raise TypeError( + "Key encryption algorithm must be a " + "KeySerializationEncryption instance" + ) + + return rust_pkcs12.serialize_java_truststore(certs, encryption_algorithm) + + +def serialize_key_and_certificates( + name: bytes | None, + key: PKCS12PrivateKeyTypes | None, + cert: x509.Certificate | None, + cas: Iterable[_PKCS12CATypes] | None, + encryption_algorithm: serialization.KeySerializationEncryption, +) -> bytes: + if key is not None and not isinstance( + key, + ( + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, + ), + ): + raise TypeError( + "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448" + " private key, or None." + ) + + if not isinstance( + encryption_algorithm, serialization.KeySerializationEncryption + ): + raise TypeError( + "Key encryption algorithm must be a " + "KeySerializationEncryption instance" + ) + + if key is None and cert is None and not cas: + raise ValueError("You must supply at least one of key, cert, or cas") + + return rust_pkcs12.serialize_key_and_certificates( + name, key, cert, cas, encryption_algorithm + ) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py new file mode 100644 index 0000000..76b667a --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py @@ -0,0 +1,412 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import email.base64mime +import email.generator +import email.message +import email.policy +import io +import typing +from collections.abc import Iterable + +from cryptography import utils, x509 +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.bindings._rust import pkcs7 as rust_pkcs7 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa +from cryptography.hazmat.primitives.ciphers import ( + algorithms, +) +from cryptography.utils import _check_byteslike + +load_pem_pkcs7_certificates = rust_pkcs7.load_pem_pkcs7_certificates + +load_der_pkcs7_certificates = rust_pkcs7.load_der_pkcs7_certificates + +serialize_certificates = rust_pkcs7.serialize_certificates + +PKCS7HashTypes = typing.Union[ + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, +] + +PKCS7PrivateKeyTypes = typing.Union[ + rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey +] + +ContentEncryptionAlgorithm = typing.Union[ + type[algorithms.AES128], type[algorithms.AES256] +] + + +class PKCS7Options(utils.Enum): + Text = "Add text/plain MIME type" + Binary = "Don't translate input data into canonical MIME format" + DetachedSignature = "Don't embed data in the PKCS7 structure" + NoCapabilities = "Don't embed SMIME capabilities" + NoAttributes = "Don't embed authenticatedAttributes" + NoCerts = "Don't embed signer certificate" + + +class PKCS7SignatureBuilder: + def __init__( + self, + data: utils.Buffer | None = None, + signers: list[ + tuple[ + x509.Certificate, + PKCS7PrivateKeyTypes, + PKCS7HashTypes, + padding.PSS | padding.PKCS1v15 | None, + ] + ] = [], + additional_certs: list[x509.Certificate] = [], + ): + self._data = data + self._signers = signers + self._additional_certs = additional_certs + + def set_data(self, data: utils.Buffer) -> PKCS7SignatureBuilder: + _check_byteslike("data", data) + if self._data is not None: + raise ValueError("data may only be set once") + + return PKCS7SignatureBuilder(data, self._signers) + + def add_signer( + self, + certificate: x509.Certificate, + private_key: PKCS7PrivateKeyTypes, + hash_algorithm: PKCS7HashTypes, + *, + rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, + ) -> PKCS7SignatureBuilder: + if not isinstance( + hash_algorithm, + ( + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + ), + ): + raise TypeError( + "hash_algorithm must be one of hashes.SHA224, " + "SHA256, SHA384, or SHA512" + ) + if not isinstance(certificate, x509.Certificate): + raise TypeError("certificate must be a x509.Certificate") + + if not isinstance( + private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey) + ): + raise TypeError("Only RSA & EC keys are supported at this time.") + + if rsa_padding is not None: + if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): + raise TypeError("Padding must be PSS or PKCS1v15") + if not isinstance(private_key, rsa.RSAPrivateKey): + raise TypeError("Padding is only supported for RSA keys") + + return PKCS7SignatureBuilder( + self._data, + [ + *self._signers, + (certificate, private_key, hash_algorithm, rsa_padding), + ], + ) + + def add_certificate( + self, certificate: x509.Certificate + ) -> PKCS7SignatureBuilder: + if not isinstance(certificate, x509.Certificate): + raise TypeError("certificate must be a x509.Certificate") + + return PKCS7SignatureBuilder( + self._data, self._signers, [*self._additional_certs, certificate] + ) + + def sign( + self, + encoding: serialization.Encoding, + options: Iterable[PKCS7Options], + backend: typing.Any = None, + ) -> bytes: + if len(self._signers) == 0: + raise ValueError("Must have at least one signer") + if self._data is None: + raise ValueError("You must add data to sign") + options = list(options) + if not all(isinstance(x, PKCS7Options) for x in options): + raise ValueError("options must be from the PKCS7Options enum") + if encoding not in ( + serialization.Encoding.PEM, + serialization.Encoding.DER, + serialization.Encoding.SMIME, + ): + raise ValueError( + "Must be PEM, DER, or SMIME from the Encoding enum" + ) + + # Text is a meaningless option unless it is accompanied by + # DetachedSignature + if ( + PKCS7Options.Text in options + and PKCS7Options.DetachedSignature not in options + ): + raise ValueError( + "When passing the Text option you must also pass " + "DetachedSignature" + ) + + if PKCS7Options.Text in options and encoding in ( + serialization.Encoding.DER, + serialization.Encoding.PEM, + ): + raise ValueError( + "The Text option is only available for SMIME serialization" + ) + + # No attributes implies no capabilities so we'll error if you try to + # pass both. + if ( + PKCS7Options.NoAttributes in options + and PKCS7Options.NoCapabilities in options + ): + raise ValueError( + "NoAttributes is a superset of NoCapabilities. Do not pass " + "both values." + ) + + return rust_pkcs7.sign_and_serialize(self, encoding, options) + + +class PKCS7EnvelopeBuilder: + def __init__( + self, + *, + _data: bytes | None = None, + _recipients: list[x509.Certificate] | None = None, + _content_encryption_algorithm: ContentEncryptionAlgorithm + | None = None, + ): + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + if not ossl.rsa_encryption_supported(padding=padding.PKCS1v15()): + raise UnsupportedAlgorithm( + "RSA with PKCS1 v1.5 padding is not supported by this version" + " of OpenSSL.", + _Reasons.UNSUPPORTED_PADDING, + ) + self._data = _data + self._recipients = _recipients if _recipients is not None else [] + self._content_encryption_algorithm = _content_encryption_algorithm + + def set_data(self, data: bytes) -> PKCS7EnvelopeBuilder: + _check_byteslike("data", data) + if self._data is not None: + raise ValueError("data may only be set once") + + return PKCS7EnvelopeBuilder( + _data=data, + _recipients=self._recipients, + _content_encryption_algorithm=self._content_encryption_algorithm, + ) + + def add_recipient( + self, + certificate: x509.Certificate, + ) -> PKCS7EnvelopeBuilder: + if not isinstance(certificate, x509.Certificate): + raise TypeError("certificate must be a x509.Certificate") + + if not isinstance(certificate.public_key(), rsa.RSAPublicKey): + raise TypeError("Only RSA keys are supported at this time.") + + return PKCS7EnvelopeBuilder( + _data=self._data, + _recipients=[ + *self._recipients, + certificate, + ], + _content_encryption_algorithm=self._content_encryption_algorithm, + ) + + def set_content_encryption_algorithm( + self, content_encryption_algorithm: ContentEncryptionAlgorithm + ) -> PKCS7EnvelopeBuilder: + if self._content_encryption_algorithm is not None: + raise ValueError("Content encryption algo may only be set once") + if content_encryption_algorithm not in { + algorithms.AES128, + algorithms.AES256, + }: + raise TypeError("Only AES128 and AES256 are supported") + + return PKCS7EnvelopeBuilder( + _data=self._data, + _recipients=self._recipients, + _content_encryption_algorithm=content_encryption_algorithm, + ) + + def encrypt( + self, + encoding: serialization.Encoding, + options: Iterable[PKCS7Options], + ) -> bytes: + if len(self._recipients) == 0: + raise ValueError("Must have at least one recipient") + if self._data is None: + raise ValueError("You must add data to encrypt") + + # The default content encryption algorithm is AES-128-CBC, which the + # S/MIME v3.2 RFC specifies as MUST support (https://datatracker.ietf.org/doc/html/rfc5751#section-2.7) + # however rest of S/MIME v3.2 is not currently supported + content_encryption_algorithm = ( + self._content_encryption_algorithm or algorithms.AES128 + ) + + options = list(options) + if not all(isinstance(x, PKCS7Options) for x in options): + raise ValueError("options must be from the PKCS7Options enum") + if encoding not in ( + serialization.Encoding.PEM, + serialization.Encoding.DER, + serialization.Encoding.SMIME, + ): + raise ValueError( + "Must be PEM, DER, or SMIME from the Encoding enum" + ) + + # Only allow options that make sense for encryption + if any( + opt not in [PKCS7Options.Text, PKCS7Options.Binary] + for opt in options + ): + raise ValueError( + "Only the following options are supported for encryption: " + "Text, Binary" + ) + elif PKCS7Options.Text in options and PKCS7Options.Binary in options: + # OpenSSL accepts both options at the same time, but ignores Text. + # We fail defensively to avoid unexpected outputs. + raise ValueError( + "Cannot use Binary and Text options at the same time" + ) + + return rust_pkcs7.encrypt_and_serialize( + self, content_encryption_algorithm, encoding, options + ) + + +pkcs7_decrypt_der = rust_pkcs7.decrypt_der +pkcs7_decrypt_pem = rust_pkcs7.decrypt_pem +pkcs7_decrypt_smime = rust_pkcs7.decrypt_smime + + +def _smime_signed_encode( + data: bytes, signature: bytes, micalg: str, text_mode: bool +) -> bytes: + # This function works pretty hard to replicate what OpenSSL does + # precisely. For good and for ill. + + m = email.message.Message() + m.add_header("MIME-Version", "1.0") + m.add_header( + "Content-Type", + "multipart/signed", + protocol="application/x-pkcs7-signature", + micalg=micalg, + ) + + m.preamble = "This is an S/MIME signed message\n" + + msg_part = OpenSSLMimePart() + msg_part.set_payload(data) + if text_mode: + msg_part.add_header("Content-Type", "text/plain") + m.attach(msg_part) + + sig_part = email.message.MIMEPart() + sig_part.add_header( + "Content-Type", "application/x-pkcs7-signature", name="smime.p7s" + ) + sig_part.add_header("Content-Transfer-Encoding", "base64") + sig_part.add_header( + "Content-Disposition", "attachment", filename="smime.p7s" + ) + sig_part.set_payload( + email.base64mime.body_encode(signature, maxlinelen=65) + ) + del sig_part["MIME-Version"] + m.attach(sig_part) + + fp = io.BytesIO() + g = email.generator.BytesGenerator( + fp, + maxheaderlen=0, + mangle_from_=False, + policy=m.policy.clone(linesep="\r\n"), + ) + g.flatten(m) + return fp.getvalue() + + +def _smime_enveloped_encode(data: bytes) -> bytes: + m = email.message.Message() + m.add_header("MIME-Version", "1.0") + m.add_header("Content-Disposition", "attachment", filename="smime.p7m") + m.add_header( + "Content-Type", + "application/pkcs7-mime", + smime_type="enveloped-data", + name="smime.p7m", + ) + m.add_header("Content-Transfer-Encoding", "base64") + + m.set_payload(email.base64mime.body_encode(data, maxlinelen=65)) + + return m.as_bytes(policy=m.policy.clone(linesep="\n", max_line_length=0)) + + +def _smime_enveloped_decode(data: bytes) -> bytes: + m = email.message_from_bytes(data) + if m.get_content_type() not in { + "application/x-pkcs7-mime", + "application/pkcs7-mime", + }: + raise ValueError("Not an S/MIME enveloped message") + return bytes(m.get_payload(decode=True)) + + +def _smime_remove_text_headers(data: bytes) -> bytes: + m = email.message_from_bytes(data) + # Using get() instead of get_content_type() since it has None as default, + # where the latter has "text/plain". Both methods are case-insensitive. + content_type = m.get("content-type") + if content_type is None: + raise ValueError( + "Decrypted MIME data has no 'Content-Type' header. " + "Please remove the 'Text' option to parse it manually." + ) + if "text/plain" not in content_type: + raise ValueError( + f"Decrypted MIME data content type is '{content_type}', not " + "'text/plain'. Remove the 'Text' option to parse it manually." + ) + return bytes(m.get_payload(decode=True)) + + +class OpenSSLMimePart(email.message.MIMEPart): + # A MIMEPart subclass that replicates OpenSSL's behavior of not including + # a newline if there are no headers. + def _write_headers(self, generator) -> None: + if list(self.raw_items()): + generator._write_headers(self) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/ssh.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/ssh.py new file mode 100644 index 0000000..230e147 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/ssh.py @@ -0,0 +1,1615 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import binascii +import enum +import os +import re +import typing +import warnings +from dataclasses import dataclass + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import ( + dsa, + ec, + ed25519, + padding, + rsa, +) +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils +from cryptography.hazmat.primitives.ciphers import ( + AEADDecryptionContext, + Cipher, + algorithms, + modes, +) +from cryptography.hazmat.primitives.serialization import ( + Encoding, + KeySerializationEncryption, + NoEncryption, + PrivateFormat, + PublicFormat, + _KeySerializationEncryption, +) + +try: + from bcrypt import kdf as _bcrypt_kdf + + _bcrypt_supported = True +except ImportError: + _bcrypt_supported = False + + def _bcrypt_kdf( + password: bytes, + salt: bytes, + desired_key_bytes: int, + rounds: int, + ignore_few_rounds: bool = False, + ) -> bytes: + raise UnsupportedAlgorithm("Need bcrypt module") + + +_SSH_ED25519 = b"ssh-ed25519" +_SSH_RSA = b"ssh-rsa" +_SSH_DSA = b"ssh-dss" +_ECDSA_NISTP256 = b"ecdsa-sha2-nistp256" +_ECDSA_NISTP384 = b"ecdsa-sha2-nistp384" +_ECDSA_NISTP521 = b"ecdsa-sha2-nistp521" +_CERT_SUFFIX = b"-cert-v01@openssh.com" + +# U2F application string suffixed pubkey +_SK_SSH_ED25519 = b"sk-ssh-ed25519@openssh.com" +_SK_SSH_ECDSA_NISTP256 = b"sk-ecdsa-sha2-nistp256@openssh.com" + +# These are not key types, only algorithms, so they cannot appear +# as a public key type +_SSH_RSA_SHA256 = b"rsa-sha2-256" +_SSH_RSA_SHA512 = b"rsa-sha2-512" + +_SSH_PUBKEY_RC = re.compile(rb"\A(\S+)[ \t]+(\S+)") +_SK_MAGIC = b"openssh-key-v1\0" +_SK_START = b"-----BEGIN OPENSSH PRIVATE KEY-----" +_SK_END = b"-----END OPENSSH PRIVATE KEY-----" +_BCRYPT = b"bcrypt" +_NONE = b"none" +_DEFAULT_CIPHER = b"aes256-ctr" +_DEFAULT_ROUNDS = 16 + +# re is only way to work on bytes-like data +_PEM_RC = re.compile(_SK_START + b"(.*?)" + _SK_END, re.DOTALL) + +# padding for max blocksize +_PADDING = memoryview(bytearray(range(1, 1 + 16))) + + +@dataclass +class _SSHCipher: + alg: type[algorithms.AES] + key_len: int + mode: type[modes.CTR] | type[modes.CBC] | type[modes.GCM] + block_len: int + iv_len: int + tag_len: int | None + is_aead: bool + + +# ciphers that are actually used in key wrapping +_SSH_CIPHERS: dict[bytes, _SSHCipher] = { + b"aes256-ctr": _SSHCipher( + alg=algorithms.AES, + key_len=32, + mode=modes.CTR, + block_len=16, + iv_len=16, + tag_len=None, + is_aead=False, + ), + b"aes256-cbc": _SSHCipher( + alg=algorithms.AES, + key_len=32, + mode=modes.CBC, + block_len=16, + iv_len=16, + tag_len=None, + is_aead=False, + ), + b"aes256-gcm@openssh.com": _SSHCipher( + alg=algorithms.AES, + key_len=32, + mode=modes.GCM, + block_len=16, + iv_len=12, + tag_len=16, + is_aead=True, + ), +} + +# map local curve name to key type +_ECDSA_KEY_TYPE = { + "secp256r1": _ECDSA_NISTP256, + "secp384r1": _ECDSA_NISTP384, + "secp521r1": _ECDSA_NISTP521, +} + + +def _get_ssh_key_type(key: SSHPrivateKeyTypes | SSHPublicKeyTypes) -> bytes: + if isinstance(key, ec.EllipticCurvePrivateKey): + key_type = _ecdsa_key_type(key.public_key()) + elif isinstance(key, ec.EllipticCurvePublicKey): + key_type = _ecdsa_key_type(key) + elif isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)): + key_type = _SSH_RSA + elif isinstance(key, (dsa.DSAPrivateKey, dsa.DSAPublicKey)): + key_type = _SSH_DSA + elif isinstance( + key, (ed25519.Ed25519PrivateKey, ed25519.Ed25519PublicKey) + ): + key_type = _SSH_ED25519 + else: + raise ValueError("Unsupported key type") + + return key_type + + +def _ecdsa_key_type(public_key: ec.EllipticCurvePublicKey) -> bytes: + """Return SSH key_type and curve_name for private key.""" + curve = public_key.curve + if curve.name not in _ECDSA_KEY_TYPE: + raise ValueError( + f"Unsupported curve for ssh private key: {curve.name!r}" + ) + return _ECDSA_KEY_TYPE[curve.name] + + +def _check_block_size(data: utils.Buffer, block_len: int) -> None: + """Require data to be full blocks""" + if not data or len(data) % block_len != 0: + raise ValueError("Corrupt data: missing padding") + + +def _check_empty(data: utils.Buffer) -> None: + """All data should have been parsed.""" + if data: + raise ValueError("Corrupt data: unparsed data") + + +def _init_cipher( + ciphername: bytes, + password: bytes | None, + salt: bytes, + rounds: int, +) -> Cipher[modes.CBC | modes.CTR | modes.GCM]: + """Generate key + iv and return cipher.""" + if not password: + raise TypeError( + "Key is password-protected, but password was not provided." + ) + + ciph = _SSH_CIPHERS[ciphername] + seed = _bcrypt_kdf( + password, salt, ciph.key_len + ciph.iv_len, rounds, True + ) + return Cipher( + ciph.alg(seed[: ciph.key_len]), + ciph.mode(seed[ciph.key_len :]), + ) + + +def _get_u32(data: memoryview) -> tuple[int, memoryview]: + """Uint32""" + if len(data) < 4: + raise ValueError("Invalid data") + return int.from_bytes(data[:4], byteorder="big"), data[4:] + + +def _get_u64(data: memoryview) -> tuple[int, memoryview]: + """Uint64""" + if len(data) < 8: + raise ValueError("Invalid data") + return int.from_bytes(data[:8], byteorder="big"), data[8:] + + +def _get_sshstr(data: memoryview) -> tuple[memoryview, memoryview]: + """Bytes with u32 length prefix""" + n, data = _get_u32(data) + if n > len(data): + raise ValueError("Invalid data") + return data[:n], data[n:] + + +def _get_mpint(data: memoryview) -> tuple[int, memoryview]: + """Big integer.""" + val, data = _get_sshstr(data) + if val and val[0] > 0x7F: + raise ValueError("Invalid data") + return int.from_bytes(val, "big"), data + + +def _to_mpint(val: int) -> bytes: + """Storage format for signed bigint.""" + if val < 0: + raise ValueError("negative mpint not allowed") + if not val: + return b"" + nbytes = (val.bit_length() + 8) // 8 + return utils.int_to_bytes(val, nbytes) + + +class _FragList: + """Build recursive structure without data copy.""" + + flist: list[utils.Buffer] + + def __init__(self, init: list[utils.Buffer] | None = None) -> None: + self.flist = [] + if init: + self.flist.extend(init) + + def put_raw(self, val: utils.Buffer) -> None: + """Add plain bytes""" + self.flist.append(val) + + def put_u32(self, val: int) -> None: + """Big-endian uint32""" + self.flist.append(val.to_bytes(length=4, byteorder="big")) + + def put_u64(self, val: int) -> None: + """Big-endian uint64""" + self.flist.append(val.to_bytes(length=8, byteorder="big")) + + def put_sshstr(self, val: bytes | _FragList) -> None: + """Bytes prefixed with u32 length""" + if isinstance(val, (bytes, memoryview, bytearray)): + self.put_u32(len(val)) + self.flist.append(val) + else: + self.put_u32(val.size()) + self.flist.extend(val.flist) + + def put_mpint(self, val: int) -> None: + """Big-endian bigint prefixed with u32 length""" + self.put_sshstr(_to_mpint(val)) + + def size(self) -> int: + """Current number of bytes""" + return sum(map(len, self.flist)) + + def render(self, dstbuf: memoryview, pos: int = 0) -> int: + """Write into bytearray""" + for frag in self.flist: + flen = len(frag) + start, pos = pos, pos + flen + dstbuf[start:pos] = frag + return pos + + def tobytes(self) -> bytes: + """Return as bytes""" + buf = memoryview(bytearray(self.size())) + self.render(buf) + return buf.tobytes() + + +class _SSHFormatRSA: + """Format for RSA keys. + + Public: + mpint e, n + Private: + mpint n, e, d, iqmp, p, q + """ + + def get_public( + self, data: memoryview + ) -> tuple[tuple[int, int], memoryview]: + """RSA public fields""" + e, data = _get_mpint(data) + n, data = _get_mpint(data) + return (e, n), data + + def load_public( + self, data: memoryview + ) -> tuple[rsa.RSAPublicKey, memoryview]: + """Make RSA public key from data.""" + (e, n), data = self.get_public(data) + public_numbers = rsa.RSAPublicNumbers(e, n) + public_key = public_numbers.public_key() + return public_key, data + + def load_private( + self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool + ) -> tuple[rsa.RSAPrivateKey, memoryview]: + """Make RSA private key from data.""" + n, data = _get_mpint(data) + e, data = _get_mpint(data) + d, data = _get_mpint(data) + iqmp, data = _get_mpint(data) + p, data = _get_mpint(data) + q, data = _get_mpint(data) + + if (e, n) != pubfields: + raise ValueError("Corrupt data: rsa field mismatch") + dmp1 = rsa.rsa_crt_dmp1(d, p) + dmq1 = rsa.rsa_crt_dmq1(d, q) + public_numbers = rsa.RSAPublicNumbers(e, n) + private_numbers = rsa.RSAPrivateNumbers( + p, q, d, dmp1, dmq1, iqmp, public_numbers + ) + private_key = private_numbers.private_key( + unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation + ) + return private_key, data + + def encode_public( + self, public_key: rsa.RSAPublicKey, f_pub: _FragList + ) -> None: + """Write RSA public key""" + pubn = public_key.public_numbers() + f_pub.put_mpint(pubn.e) + f_pub.put_mpint(pubn.n) + + def encode_private( + self, private_key: rsa.RSAPrivateKey, f_priv: _FragList + ) -> None: + """Write RSA private key""" + private_numbers = private_key.private_numbers() + public_numbers = private_numbers.public_numbers + + f_priv.put_mpint(public_numbers.n) + f_priv.put_mpint(public_numbers.e) + + f_priv.put_mpint(private_numbers.d) + f_priv.put_mpint(private_numbers.iqmp) + f_priv.put_mpint(private_numbers.p) + f_priv.put_mpint(private_numbers.q) + + +class _SSHFormatDSA: + """Format for DSA keys. + + Public: + mpint p, q, g, y + Private: + mpint p, q, g, y, x + """ + + def get_public(self, data: memoryview) -> tuple[tuple, memoryview]: + """DSA public fields""" + p, data = _get_mpint(data) + q, data = _get_mpint(data) + g, data = _get_mpint(data) + y, data = _get_mpint(data) + return (p, q, g, y), data + + def load_public( + self, data: memoryview + ) -> tuple[dsa.DSAPublicKey, memoryview]: + """Make DSA public key from data.""" + (p, q, g, y), data = self.get_public(data) + parameter_numbers = dsa.DSAParameterNumbers(p, q, g) + public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) + self._validate(public_numbers) + public_key = public_numbers.public_key() + return public_key, data + + def load_private( + self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool + ) -> tuple[dsa.DSAPrivateKey, memoryview]: + """Make DSA private key from data.""" + (p, q, g, y), data = self.get_public(data) + x, data = _get_mpint(data) + + if (p, q, g, y) != pubfields: + raise ValueError("Corrupt data: dsa field mismatch") + parameter_numbers = dsa.DSAParameterNumbers(p, q, g) + public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) + self._validate(public_numbers) + private_numbers = dsa.DSAPrivateNumbers(x, public_numbers) + private_key = private_numbers.private_key() + return private_key, data + + def encode_public( + self, public_key: dsa.DSAPublicKey, f_pub: _FragList + ) -> None: + """Write DSA public key""" + public_numbers = public_key.public_numbers() + parameter_numbers = public_numbers.parameter_numbers + self._validate(public_numbers) + + f_pub.put_mpint(parameter_numbers.p) + f_pub.put_mpint(parameter_numbers.q) + f_pub.put_mpint(parameter_numbers.g) + f_pub.put_mpint(public_numbers.y) + + def encode_private( + self, private_key: dsa.DSAPrivateKey, f_priv: _FragList + ) -> None: + """Write DSA private key""" + self.encode_public(private_key.public_key(), f_priv) + f_priv.put_mpint(private_key.private_numbers().x) + + def _validate(self, public_numbers: dsa.DSAPublicNumbers) -> None: + parameter_numbers = public_numbers.parameter_numbers + if parameter_numbers.p.bit_length() != 1024: + raise ValueError("SSH supports only 1024 bit DSA keys") + + +class _SSHFormatECDSA: + """Format for ECDSA keys. + + Public: + str curve + bytes point + Private: + str curve + bytes point + mpint secret + """ + + def __init__(self, ssh_curve_name: bytes, curve: ec.EllipticCurve): + self.ssh_curve_name = ssh_curve_name + self.curve = curve + + def get_public( + self, data: memoryview + ) -> tuple[tuple[memoryview, memoryview], memoryview]: + """ECDSA public fields""" + curve, data = _get_sshstr(data) + point, data = _get_sshstr(data) + if curve != self.ssh_curve_name: + raise ValueError("Curve name mismatch") + if len(point) == 0: + raise ValueError("Invalid EC point: empty data") + if point[0] != 4: + raise NotImplementedError("Need uncompressed point") + return (curve, point), data + + def load_public( + self, data: memoryview + ) -> tuple[ec.EllipticCurvePublicKey, memoryview]: + """Make ECDSA public key from data.""" + (_, point), data = self.get_public(data) + public_key = ec.EllipticCurvePublicKey.from_encoded_point( + self.curve, point.tobytes() + ) + return public_key, data + + def load_private( + self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool + ) -> tuple[ec.EllipticCurvePrivateKey, memoryview]: + """Make ECDSA private key from data.""" + (curve_name, point), data = self.get_public(data) + secret, data = _get_mpint(data) + + if (curve_name, point) != pubfields: + raise ValueError("Corrupt data: ecdsa field mismatch") + private_key = ec.derive_private_key(secret, self.curve) + return private_key, data + + def encode_public( + self, public_key: ec.EllipticCurvePublicKey, f_pub: _FragList + ) -> None: + """Write ECDSA public key""" + point = public_key.public_bytes( + Encoding.X962, PublicFormat.UncompressedPoint + ) + f_pub.put_sshstr(self.ssh_curve_name) + f_pub.put_sshstr(point) + + def encode_private( + self, private_key: ec.EllipticCurvePrivateKey, f_priv: _FragList + ) -> None: + """Write ECDSA private key""" + public_key = private_key.public_key() + private_numbers = private_key.private_numbers() + + self.encode_public(public_key, f_priv) + f_priv.put_mpint(private_numbers.private_value) + + +class _SSHFormatEd25519: + """Format for Ed25519 keys. + + Public: + bytes point + Private: + bytes point + bytes secret_and_point + """ + + def get_public( + self, data: memoryview + ) -> tuple[tuple[memoryview], memoryview]: + """Ed25519 public fields""" + point, data = _get_sshstr(data) + return (point,), data + + def load_public( + self, data: memoryview + ) -> tuple[ed25519.Ed25519PublicKey, memoryview]: + """Make Ed25519 public key from data.""" + (point,), data = self.get_public(data) + public_key = ed25519.Ed25519PublicKey.from_public_bytes( + point.tobytes() + ) + return public_key, data + + def load_private( + self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool + ) -> tuple[ed25519.Ed25519PrivateKey, memoryview]: + """Make Ed25519 private key from data.""" + (point,), data = self.get_public(data) + keypair, data = _get_sshstr(data) + + secret = keypair[:32] + point2 = keypair[32:] + if point != point2 or (point,) != pubfields: + raise ValueError("Corrupt data: ed25519 field mismatch") + private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret) + return private_key, data + + def encode_public( + self, public_key: ed25519.Ed25519PublicKey, f_pub: _FragList + ) -> None: + """Write Ed25519 public key""" + raw_public_key = public_key.public_bytes( + Encoding.Raw, PublicFormat.Raw + ) + f_pub.put_sshstr(raw_public_key) + + def encode_private( + self, private_key: ed25519.Ed25519PrivateKey, f_priv: _FragList + ) -> None: + """Write Ed25519 private key""" + public_key = private_key.public_key() + raw_private_key = private_key.private_bytes( + Encoding.Raw, PrivateFormat.Raw, NoEncryption() + ) + raw_public_key = public_key.public_bytes( + Encoding.Raw, PublicFormat.Raw + ) + f_keypair = _FragList([raw_private_key, raw_public_key]) + + self.encode_public(public_key, f_priv) + f_priv.put_sshstr(f_keypair) + + +def load_application(data) -> tuple[memoryview, memoryview]: + """ + U2F application strings + """ + application, data = _get_sshstr(data) + if not application.tobytes().startswith(b"ssh:"): + raise ValueError( + "U2F application string does not start with b'ssh:' " + f"({application})" + ) + return application, data + + +class _SSHFormatSKEd25519: + """ + The format of a sk-ssh-ed25519@openssh.com public key is: + + string "sk-ssh-ed25519@openssh.com" + string public key + string application (user-specified, but typically "ssh:") + """ + + def load_public( + self, data: memoryview + ) -> tuple[ed25519.Ed25519PublicKey, memoryview]: + """Make Ed25519 public key from data.""" + public_key, data = _lookup_kformat(_SSH_ED25519).load_public(data) + _, data = load_application(data) + return public_key, data + + def get_public(self, data: memoryview) -> typing.NoReturn: + # Confusingly `get_public` is an entry point used by private key + # loading. + raise UnsupportedAlgorithm( + "sk-ssh-ed25519 private keys cannot be loaded" + ) + + +class _SSHFormatSKECDSA: + """ + The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is: + + string "sk-ecdsa-sha2-nistp256@openssh.com" + string curve name + ec_point Q + string application (user-specified, but typically "ssh:") + """ + + def load_public( + self, data: memoryview + ) -> tuple[ec.EllipticCurvePublicKey, memoryview]: + """Make ECDSA public key from data.""" + public_key, data = _lookup_kformat(_ECDSA_NISTP256).load_public(data) + _, data = load_application(data) + return public_key, data + + def get_public(self, data: memoryview) -> typing.NoReturn: + # Confusingly `get_public` is an entry point used by private key + # loading. + raise UnsupportedAlgorithm( + "sk-ecdsa-sha2-nistp256 private keys cannot be loaded" + ) + + +_KEY_FORMATS = { + _SSH_RSA: _SSHFormatRSA(), + _SSH_DSA: _SSHFormatDSA(), + _SSH_ED25519: _SSHFormatEd25519(), + _ECDSA_NISTP256: _SSHFormatECDSA(b"nistp256", ec.SECP256R1()), + _ECDSA_NISTP384: _SSHFormatECDSA(b"nistp384", ec.SECP384R1()), + _ECDSA_NISTP521: _SSHFormatECDSA(b"nistp521", ec.SECP521R1()), + _SK_SSH_ED25519: _SSHFormatSKEd25519(), + _SK_SSH_ECDSA_NISTP256: _SSHFormatSKECDSA(), +} + + +def _lookup_kformat(key_type: utils.Buffer): + """Return valid format or throw error""" + if not isinstance(key_type, bytes): + key_type = memoryview(key_type).tobytes() + if key_type in _KEY_FORMATS: + return _KEY_FORMATS[key_type] + raise UnsupportedAlgorithm(f"Unsupported key type: {key_type!r}") + + +SSHPrivateKeyTypes = typing.Union[ + ec.EllipticCurvePrivateKey, + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ed25519.Ed25519PrivateKey, +] + + +def load_ssh_private_key( + data: utils.Buffer, + password: bytes | None, + backend: typing.Any = None, + *, + unsafe_skip_rsa_key_validation: bool = False, +) -> SSHPrivateKeyTypes: + """Load private key from OpenSSH custom encoding.""" + utils._check_byteslike("data", data) + if password is not None: + utils._check_bytes("password", password) + + m = _PEM_RC.search(data) + if not m: + raise ValueError("Not OpenSSH private key format") + p1 = m.start(1) + p2 = m.end(1) + data = binascii.a2b_base64(memoryview(data)[p1:p2]) + if not data.startswith(_SK_MAGIC): + raise ValueError("Not OpenSSH private key format") + data = memoryview(data)[len(_SK_MAGIC) :] + + # parse header + ciphername, data = _get_sshstr(data) + kdfname, data = _get_sshstr(data) + kdfoptions, data = _get_sshstr(data) + nkeys, data = _get_u32(data) + if nkeys != 1: + raise ValueError("Only one key supported") + + # load public key data + pubdata, data = _get_sshstr(data) + pub_key_type, pubdata = _get_sshstr(pubdata) + kformat = _lookup_kformat(pub_key_type) + pubfields, pubdata = kformat.get_public(pubdata) + _check_empty(pubdata) + + if ciphername != _NONE or kdfname != _NONE: + ciphername_bytes = ciphername.tobytes() + if ciphername_bytes not in _SSH_CIPHERS: + raise UnsupportedAlgorithm( + f"Unsupported cipher: {ciphername_bytes!r}" + ) + if kdfname != _BCRYPT: + raise UnsupportedAlgorithm(f"Unsupported KDF: {kdfname!r}") + blklen = _SSH_CIPHERS[ciphername_bytes].block_len + tag_len = _SSH_CIPHERS[ciphername_bytes].tag_len + # load secret data + edata, data = _get_sshstr(data) + # see https://bugzilla.mindrot.org/show_bug.cgi?id=3553 for + # information about how OpenSSH handles AEAD tags + if _SSH_CIPHERS[ciphername_bytes].is_aead: + tag = bytes(data) + if len(tag) != tag_len: + raise ValueError("Corrupt data: invalid tag length for cipher") + else: + _check_empty(data) + _check_block_size(edata, blklen) + salt, kbuf = _get_sshstr(kdfoptions) + rounds, kbuf = _get_u32(kbuf) + _check_empty(kbuf) + ciph = _init_cipher(ciphername_bytes, password, salt.tobytes(), rounds) + dec = ciph.decryptor() + edata = memoryview(dec.update(edata)) + if _SSH_CIPHERS[ciphername_bytes].is_aead: + assert isinstance(dec, AEADDecryptionContext) + _check_empty(dec.finalize_with_tag(tag)) + else: + # _check_block_size requires data to be a full block so there + # should be no output from finalize + _check_empty(dec.finalize()) + else: + if password: + raise TypeError( + "Password was given but private key is not encrypted." + ) + # load secret data + edata, data = _get_sshstr(data) + _check_empty(data) + blklen = 8 + _check_block_size(edata, blklen) + ck1, edata = _get_u32(edata) + ck2, edata = _get_u32(edata) + if ck1 != ck2: + raise ValueError("Corrupt data: broken checksum") + + # load per-key struct + key_type, edata = _get_sshstr(edata) + if key_type != pub_key_type: + raise ValueError("Corrupt data: key type mismatch") + private_key, edata = kformat.load_private( + edata, + pubfields, + unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation, + ) + # We don't use the comment + _, edata = _get_sshstr(edata) + + # yes, SSH does padding check *after* all other parsing is done. + # need to follow as it writes zero-byte padding too. + if edata != _PADDING[: len(edata)]: + raise ValueError("Corrupt data: invalid padding") + + if isinstance(private_key, dsa.DSAPrivateKey): + warnings.warn( + "SSH DSA keys are deprecated and will be removed in a future " + "release.", + utils.DeprecatedIn40, + stacklevel=2, + ) + + return private_key + + +def _serialize_ssh_private_key( + private_key: SSHPrivateKeyTypes, + password: bytes, + encryption_algorithm: KeySerializationEncryption, +) -> bytes: + """Serialize private key with OpenSSH custom encoding.""" + utils._check_bytes("password", password) + if isinstance(private_key, dsa.DSAPrivateKey): + warnings.warn( + "SSH DSA key support is deprecated and will be " + "removed in a future release", + utils.DeprecatedIn40, + stacklevel=4, + ) + + key_type = _get_ssh_key_type(private_key) + kformat = _lookup_kformat(key_type) + + # setup parameters + f_kdfoptions = _FragList() + if password: + ciphername = _DEFAULT_CIPHER + blklen = _SSH_CIPHERS[ciphername].block_len + kdfname = _BCRYPT + rounds = _DEFAULT_ROUNDS + if ( + isinstance(encryption_algorithm, _KeySerializationEncryption) + and encryption_algorithm._kdf_rounds is not None + ): + rounds = encryption_algorithm._kdf_rounds + salt = os.urandom(16) + f_kdfoptions.put_sshstr(salt) + f_kdfoptions.put_u32(rounds) + ciph = _init_cipher(ciphername, password, salt, rounds) + else: + ciphername = kdfname = _NONE + blklen = 8 + ciph = None + nkeys = 1 + checkval = os.urandom(4) + comment = b"" + + # encode public and private parts together + f_public_key = _FragList() + f_public_key.put_sshstr(key_type) + kformat.encode_public(private_key.public_key(), f_public_key) + + f_secrets = _FragList([checkval, checkval]) + f_secrets.put_sshstr(key_type) + kformat.encode_private(private_key, f_secrets) + f_secrets.put_sshstr(comment) + f_secrets.put_raw(_PADDING[: blklen - (f_secrets.size() % blklen)]) + + # top-level structure + f_main = _FragList() + f_main.put_raw(_SK_MAGIC) + f_main.put_sshstr(ciphername) + f_main.put_sshstr(kdfname) + f_main.put_sshstr(f_kdfoptions) + f_main.put_u32(nkeys) + f_main.put_sshstr(f_public_key) + f_main.put_sshstr(f_secrets) + + # copy result info bytearray + slen = f_secrets.size() + mlen = f_main.size() + buf = memoryview(bytearray(mlen + blklen)) + f_main.render(buf) + ofs = mlen - slen + + # encrypt in-place + if ciph is not None: + ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:]) + + return bytes(buf[:mlen]) + + +SSHPublicKeyTypes = typing.Union[ + ec.EllipticCurvePublicKey, + rsa.RSAPublicKey, + dsa.DSAPublicKey, + ed25519.Ed25519PublicKey, +] + +SSHCertPublicKeyTypes = typing.Union[ + ec.EllipticCurvePublicKey, + rsa.RSAPublicKey, + ed25519.Ed25519PublicKey, +] + + +class SSHCertificateType(enum.Enum): + USER = 1 + HOST = 2 + + +class SSHCertificate: + def __init__( + self, + _nonce: memoryview, + _public_key: SSHPublicKeyTypes, + _serial: int, + _cctype: int, + _key_id: memoryview, + _valid_principals: list[bytes], + _valid_after: int, + _valid_before: int, + _critical_options: dict[bytes, bytes], + _extensions: dict[bytes, bytes], + _sig_type: memoryview, + _sig_key: memoryview, + _inner_sig_type: memoryview, + _signature: memoryview, + _tbs_cert_body: memoryview, + _cert_key_type: bytes, + _cert_body: memoryview, + ): + self._nonce = _nonce + self._public_key = _public_key + self._serial = _serial + try: + self._type = SSHCertificateType(_cctype) + except ValueError: + raise ValueError("Invalid certificate type") + self._key_id = _key_id + self._valid_principals = _valid_principals + self._valid_after = _valid_after + self._valid_before = _valid_before + self._critical_options = _critical_options + self._extensions = _extensions + self._sig_type = _sig_type + self._sig_key = _sig_key + self._inner_sig_type = _inner_sig_type + self._signature = _signature + self._cert_key_type = _cert_key_type + self._cert_body = _cert_body + self._tbs_cert_body = _tbs_cert_body + + @property + def nonce(self) -> bytes: + return bytes(self._nonce) + + def public_key(self) -> SSHCertPublicKeyTypes: + # make mypy happy until we remove DSA support entirely and + # the underlying union won't have a disallowed type + return typing.cast(SSHCertPublicKeyTypes, self._public_key) + + @property + def serial(self) -> int: + return self._serial + + @property + def type(self) -> SSHCertificateType: + return self._type + + @property + def key_id(self) -> bytes: + return bytes(self._key_id) + + @property + def valid_principals(self) -> list[bytes]: + return self._valid_principals + + @property + def valid_before(self) -> int: + return self._valid_before + + @property + def valid_after(self) -> int: + return self._valid_after + + @property + def critical_options(self) -> dict[bytes, bytes]: + return self._critical_options + + @property + def extensions(self) -> dict[bytes, bytes]: + return self._extensions + + def signature_key(self) -> SSHCertPublicKeyTypes: + sigformat = _lookup_kformat(self._sig_type) + signature_key, sigkey_rest = sigformat.load_public(self._sig_key) + _check_empty(sigkey_rest) + return signature_key + + def public_bytes(self) -> bytes: + return ( + bytes(self._cert_key_type) + + b" " + + binascii.b2a_base64(bytes(self._cert_body), newline=False) + ) + + def verify_cert_signature(self) -> None: + signature_key = self.signature_key() + if isinstance(signature_key, ed25519.Ed25519PublicKey): + signature_key.verify( + bytes(self._signature), bytes(self._tbs_cert_body) + ) + elif isinstance(signature_key, ec.EllipticCurvePublicKey): + # The signature is encoded as a pair of big-endian integers + r, data = _get_mpint(self._signature) + s, data = _get_mpint(data) + _check_empty(data) + computed_sig = asym_utils.encode_dss_signature(r, s) + hash_alg = _get_ec_hash_alg(signature_key.curve) + signature_key.verify( + computed_sig, bytes(self._tbs_cert_body), ec.ECDSA(hash_alg) + ) + else: + assert isinstance(signature_key, rsa.RSAPublicKey) + if self._inner_sig_type == _SSH_RSA: + hash_alg = hashes.SHA1() + elif self._inner_sig_type == _SSH_RSA_SHA256: + hash_alg = hashes.SHA256() + else: + assert self._inner_sig_type == _SSH_RSA_SHA512 + hash_alg = hashes.SHA512() + signature_key.verify( + bytes(self._signature), + bytes(self._tbs_cert_body), + padding.PKCS1v15(), + hash_alg, + ) + + +def _get_ec_hash_alg(curve: ec.EllipticCurve) -> hashes.HashAlgorithm: + if isinstance(curve, ec.SECP256R1): + return hashes.SHA256() + elif isinstance(curve, ec.SECP384R1): + return hashes.SHA384() + else: + assert isinstance(curve, ec.SECP521R1) + return hashes.SHA512() + + +def _load_ssh_public_identity( + data: utils.Buffer, + _legacy_dsa_allowed=False, +) -> SSHCertificate | SSHPublicKeyTypes: + utils._check_byteslike("data", data) + + m = _SSH_PUBKEY_RC.match(data) + if not m: + raise ValueError("Invalid line format") + key_type = orig_key_type = m.group(1) + key_body = m.group(2) + with_cert = False + if key_type.endswith(_CERT_SUFFIX): + with_cert = True + key_type = key_type[: -len(_CERT_SUFFIX)] + if key_type == _SSH_DSA and not _legacy_dsa_allowed: + raise UnsupportedAlgorithm( + "DSA keys aren't supported in SSH certificates" + ) + kformat = _lookup_kformat(key_type) + + try: + rest = memoryview(binascii.a2b_base64(key_body)) + except (TypeError, binascii.Error): + raise ValueError("Invalid format") + + if with_cert: + cert_body = rest + inner_key_type, rest = _get_sshstr(rest) + if inner_key_type != orig_key_type: + raise ValueError("Invalid key format") + if with_cert: + nonce, rest = _get_sshstr(rest) + public_key, rest = kformat.load_public(rest) + if with_cert: + serial, rest = _get_u64(rest) + cctype, rest = _get_u32(rest) + key_id, rest = _get_sshstr(rest) + principals, rest = _get_sshstr(rest) + valid_principals = [] + while principals: + principal, principals = _get_sshstr(principals) + valid_principals.append(bytes(principal)) + valid_after, rest = _get_u64(rest) + valid_before, rest = _get_u64(rest) + crit_options, rest = _get_sshstr(rest) + critical_options = _parse_exts_opts(crit_options) + exts, rest = _get_sshstr(rest) + extensions = _parse_exts_opts(exts) + # Get the reserved field, which is unused. + _, rest = _get_sshstr(rest) + sig_key_raw, rest = _get_sshstr(rest) + sig_type, sig_key = _get_sshstr(sig_key_raw) + if sig_type == _SSH_DSA and not _legacy_dsa_allowed: + raise UnsupportedAlgorithm( + "DSA signatures aren't supported in SSH certificates" + ) + # Get the entire cert body and subtract the signature + tbs_cert_body = cert_body[: -len(rest)] + signature_raw, rest = _get_sshstr(rest) + _check_empty(rest) + inner_sig_type, sig_rest = _get_sshstr(signature_raw) + # RSA certs can have multiple algorithm types + if ( + sig_type == _SSH_RSA + and inner_sig_type + not in [_SSH_RSA_SHA256, _SSH_RSA_SHA512, _SSH_RSA] + ) or (sig_type != _SSH_RSA and inner_sig_type != sig_type): + raise ValueError("Signature key type does not match") + signature, sig_rest = _get_sshstr(sig_rest) + _check_empty(sig_rest) + return SSHCertificate( + nonce, + public_key, + serial, + cctype, + key_id, + valid_principals, + valid_after, + valid_before, + critical_options, + extensions, + sig_type, + sig_key, + inner_sig_type, + signature, + tbs_cert_body, + orig_key_type, + cert_body, + ) + else: + _check_empty(rest) + return public_key + + +def load_ssh_public_identity( + data: utils.Buffer, +) -> SSHCertificate | SSHPublicKeyTypes: + return _load_ssh_public_identity(data) + + +def _parse_exts_opts(exts_opts: memoryview) -> dict[bytes, bytes]: + result: dict[bytes, bytes] = {} + last_name = None + while exts_opts: + name, exts_opts = _get_sshstr(exts_opts) + bname: bytes = bytes(name) + if bname in result: + raise ValueError("Duplicate name") + if last_name is not None and bname < last_name: + raise ValueError("Fields not lexically sorted") + value, exts_opts = _get_sshstr(exts_opts) + if len(value) > 0: + value, extra = _get_sshstr(value) + if len(extra) > 0: + raise ValueError("Unexpected extra data after value") + result[bname] = bytes(value) + last_name = bname + return result + + +def ssh_key_fingerprint( + key: SSHPublicKeyTypes, + hash_algorithm: hashes.MD5 | hashes.SHA1 | hashes.SHA256, +) -> bytes: + if not isinstance( + hash_algorithm, + (hashes.MD5, hashes.SHA1, hashes.SHA256), + ): + raise TypeError("hash_algorithm must be either MD5, SHA1, or SHA256") + + key_type = _get_ssh_key_type(key) + kformat = _lookup_kformat(key_type) + + f_pub = _FragList() + f_pub.put_sshstr(key_type) + kformat.encode_public(key, f_pub) + + ssh_binary_data = f_pub.tobytes() + + # Hash the binary data + hash_obj = hashes.Hash(hash_algorithm) + hash_obj.update(ssh_binary_data) + return hash_obj.finalize() + + +def load_ssh_public_key( + data: utils.Buffer, backend: typing.Any = None +) -> SSHPublicKeyTypes: + cert_or_key = _load_ssh_public_identity(data, _legacy_dsa_allowed=True) + public_key: SSHPublicKeyTypes + if isinstance(cert_or_key, SSHCertificate): + public_key = cert_or_key.public_key() + else: + public_key = cert_or_key + + if isinstance(public_key, dsa.DSAPublicKey): + warnings.warn( + "SSH DSA keys are deprecated and will be removed in a future " + "release.", + utils.DeprecatedIn40, + stacklevel=2, + ) + return public_key + + +def serialize_ssh_public_key(public_key: SSHPublicKeyTypes) -> bytes: + """One-line public key format for OpenSSH""" + if isinstance(public_key, dsa.DSAPublicKey): + warnings.warn( + "SSH DSA key support is deprecated and will be " + "removed in a future release", + utils.DeprecatedIn40, + stacklevel=4, + ) + key_type = _get_ssh_key_type(public_key) + kformat = _lookup_kformat(key_type) + + f_pub = _FragList() + f_pub.put_sshstr(key_type) + kformat.encode_public(public_key, f_pub) + + pub = binascii.b2a_base64(f_pub.tobytes()).strip() + return b"".join([key_type, b" ", pub]) + + +SSHCertPrivateKeyTypes = typing.Union[ + ec.EllipticCurvePrivateKey, + rsa.RSAPrivateKey, + ed25519.Ed25519PrivateKey, +] + + +# This is an undocumented limit enforced in the openssh codebase for sshd and +# ssh-keygen, but it is undefined in the ssh certificates spec. +_SSHKEY_CERT_MAX_PRINCIPALS = 256 + + +class SSHCertificateBuilder: + def __init__( + self, + _public_key: SSHCertPublicKeyTypes | None = None, + _serial: int | None = None, + _type: SSHCertificateType | None = None, + _key_id: bytes | None = None, + _valid_principals: list[bytes] = [], + _valid_for_all_principals: bool = False, + _valid_before: int | None = None, + _valid_after: int | None = None, + _critical_options: list[tuple[bytes, bytes]] = [], + _extensions: list[tuple[bytes, bytes]] = [], + ): + self._public_key = _public_key + self._serial = _serial + self._type = _type + self._key_id = _key_id + self._valid_principals = _valid_principals + self._valid_for_all_principals = _valid_for_all_principals + self._valid_before = _valid_before + self._valid_after = _valid_after + self._critical_options = _critical_options + self._extensions = _extensions + + def public_key( + self, public_key: SSHCertPublicKeyTypes + ) -> SSHCertificateBuilder: + if not isinstance( + public_key, + ( + ec.EllipticCurvePublicKey, + rsa.RSAPublicKey, + ed25519.Ed25519PublicKey, + ), + ): + raise TypeError("Unsupported key type") + if self._public_key is not None: + raise ValueError("public_key already set") + + return SSHCertificateBuilder( + _public_key=public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def serial(self, serial: int) -> SSHCertificateBuilder: + if not isinstance(serial, int): + raise TypeError("serial must be an integer") + if not 0 <= serial < 2**64: + raise ValueError("serial must be between 0 and 2**64") + if self._serial is not None: + raise ValueError("serial already set") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def type(self, type: SSHCertificateType) -> SSHCertificateBuilder: + if not isinstance(type, SSHCertificateType): + raise TypeError("type must be an SSHCertificateType") + if self._type is not None: + raise ValueError("type already set") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def key_id(self, key_id: bytes) -> SSHCertificateBuilder: + if not isinstance(key_id, bytes): + raise TypeError("key_id must be bytes") + if self._key_id is not None: + raise ValueError("key_id already set") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def valid_principals( + self, valid_principals: list[bytes] + ) -> SSHCertificateBuilder: + if self._valid_for_all_principals: + raise ValueError( + "Principals can't be set because the cert is valid " + "for all principals" + ) + if ( + not all(isinstance(x, bytes) for x in valid_principals) + or not valid_principals + ): + raise TypeError( + "principals must be a list of bytes and can't be empty" + ) + if self._valid_principals: + raise ValueError("valid_principals already set") + + if len(valid_principals) > _SSHKEY_CERT_MAX_PRINCIPALS: + raise ValueError( + "Reached or exceeded the maximum number of valid_principals" + ) + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def valid_for_all_principals(self): + if self._valid_principals: + raise ValueError( + "valid_principals already set, can't set " + "valid_for_all_principals" + ) + if self._valid_for_all_principals: + raise ValueError("valid_for_all_principals already set") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=True, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def valid_before(self, valid_before: int | float) -> SSHCertificateBuilder: + if not isinstance(valid_before, (int, float)): + raise TypeError("valid_before must be an int or float") + valid_before = int(valid_before) + if valid_before < 0 or valid_before >= 2**64: + raise ValueError("valid_before must [0, 2**64)") + if self._valid_before is not None: + raise ValueError("valid_before already set") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def valid_after(self, valid_after: int | float) -> SSHCertificateBuilder: + if not isinstance(valid_after, (int, float)): + raise TypeError("valid_after must be an int or float") + valid_after = int(valid_after) + if valid_after < 0 or valid_after >= 2**64: + raise ValueError("valid_after must [0, 2**64)") + if self._valid_after is not None: + raise ValueError("valid_after already set") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def add_critical_option( + self, name: bytes, value: bytes + ) -> SSHCertificateBuilder: + if not isinstance(name, bytes) or not isinstance(value, bytes): + raise TypeError("name and value must be bytes") + # This is O(n**2) + if name in [name for name, _ in self._critical_options]: + raise ValueError("Duplicate critical option name") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=[*self._critical_options, (name, value)], + _extensions=self._extensions, + ) + + def add_extension( + self, name: bytes, value: bytes + ) -> SSHCertificateBuilder: + if not isinstance(name, bytes) or not isinstance(value, bytes): + raise TypeError("name and value must be bytes") + # This is O(n**2) + if name in [name for name, _ in self._extensions]: + raise ValueError("Duplicate extension name") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=[*self._extensions, (name, value)], + ) + + def sign(self, private_key: SSHCertPrivateKeyTypes) -> SSHCertificate: + if not isinstance( + private_key, + ( + ec.EllipticCurvePrivateKey, + rsa.RSAPrivateKey, + ed25519.Ed25519PrivateKey, + ), + ): + raise TypeError("Unsupported private key type") + + if self._public_key is None: + raise ValueError("public_key must be set") + + # Not required + serial = 0 if self._serial is None else self._serial + + if self._type is None: + raise ValueError("type must be set") + + # Not required + key_id = b"" if self._key_id is None else self._key_id + + # A zero length list is valid, but means the certificate + # is valid for any principal of the specified type. We require + # the user to explicitly set valid_for_all_principals to get + # that behavior. + if not self._valid_principals and not self._valid_for_all_principals: + raise ValueError( + "valid_principals must be set if valid_for_all_principals " + "is False" + ) + + if self._valid_before is None: + raise ValueError("valid_before must be set") + + if self._valid_after is None: + raise ValueError("valid_after must be set") + + if self._valid_after > self._valid_before: + raise ValueError("valid_after must be earlier than valid_before") + + # lexically sort our byte strings + self._critical_options.sort(key=lambda x: x[0]) + self._extensions.sort(key=lambda x: x[0]) + + key_type = _get_ssh_key_type(self._public_key) + cert_prefix = key_type + _CERT_SUFFIX + + # Marshal the bytes to be signed + nonce = os.urandom(32) + kformat = _lookup_kformat(key_type) + f = _FragList() + f.put_sshstr(cert_prefix) + f.put_sshstr(nonce) + kformat.encode_public(self._public_key, f) + f.put_u64(serial) + f.put_u32(self._type.value) + f.put_sshstr(key_id) + fprincipals = _FragList() + for p in self._valid_principals: + fprincipals.put_sshstr(p) + f.put_sshstr(fprincipals.tobytes()) + f.put_u64(self._valid_after) + f.put_u64(self._valid_before) + fcrit = _FragList() + for name, value in self._critical_options: + fcrit.put_sshstr(name) + if len(value) > 0: + foptval = _FragList() + foptval.put_sshstr(value) + fcrit.put_sshstr(foptval.tobytes()) + else: + fcrit.put_sshstr(value) + f.put_sshstr(fcrit.tobytes()) + fext = _FragList() + for name, value in self._extensions: + fext.put_sshstr(name) + if len(value) > 0: + fextval = _FragList() + fextval.put_sshstr(value) + fext.put_sshstr(fextval.tobytes()) + else: + fext.put_sshstr(value) + f.put_sshstr(fext.tobytes()) + f.put_sshstr(b"") # RESERVED FIELD + # encode CA public key + ca_type = _get_ssh_key_type(private_key) + caformat = _lookup_kformat(ca_type) + caf = _FragList() + caf.put_sshstr(ca_type) + caformat.encode_public(private_key.public_key(), caf) + f.put_sshstr(caf.tobytes()) + # Sigs according to the rules defined for the CA's public key + # (RFC4253 section 6.6 for ssh-rsa, RFC5656 for ECDSA, + # and RFC8032 for Ed25519). + if isinstance(private_key, ed25519.Ed25519PrivateKey): + signature = private_key.sign(f.tobytes()) + fsig = _FragList() + fsig.put_sshstr(ca_type) + fsig.put_sshstr(signature) + f.put_sshstr(fsig.tobytes()) + elif isinstance(private_key, ec.EllipticCurvePrivateKey): + hash_alg = _get_ec_hash_alg(private_key.curve) + signature = private_key.sign(f.tobytes(), ec.ECDSA(hash_alg)) + r, s = asym_utils.decode_dss_signature(signature) + fsig = _FragList() + fsig.put_sshstr(ca_type) + fsigblob = _FragList() + fsigblob.put_mpint(r) + fsigblob.put_mpint(s) + fsig.put_sshstr(fsigblob.tobytes()) + f.put_sshstr(fsig.tobytes()) + + else: + assert isinstance(private_key, rsa.RSAPrivateKey) + # Just like Golang, we're going to use SHA512 for RSA + # https://cs.opensource.google/go/x/crypto/+/refs/tags/ + # v0.4.0:ssh/certs.go;l=445 + # RFC 8332 defines SHA256 and 512 as options + fsig = _FragList() + fsig.put_sshstr(_SSH_RSA_SHA512) + signature = private_key.sign( + f.tobytes(), padding.PKCS1v15(), hashes.SHA512() + ) + fsig.put_sshstr(signature) + f.put_sshstr(fsig.tobytes()) + + cert_data = binascii.b2a_base64(f.tobytes()).strip() + # load_ssh_public_identity returns a union, but this is + # guaranteed to be an SSHCertificate, so we cast to make + # mypy happy. + return typing.cast( + SSHCertificate, + load_ssh_public_identity(b"".join([cert_prefix, b" ", cert_data])), + ) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py new file mode 100644 index 0000000..c1af423 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py @@ -0,0 +1,9 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + + +class InvalidToken(Exception): + pass diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..8c35d91 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-312.pyc new file mode 100644 index 0000000..efa06fd Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-312.pyc new file mode 100644 index 0000000..857d30c Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py new file mode 100644 index 0000000..21fb000 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py @@ -0,0 +1,101 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import base64 +import typing +from urllib.parse import quote, urlencode + +from cryptography.hazmat.primitives import constant_time, hmac +from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512 +from cryptography.hazmat.primitives.twofactor import InvalidToken +from cryptography.utils import Buffer + +HOTPHashTypes = typing.Union[SHA1, SHA256, SHA512] + + +def _generate_uri( + hotp: HOTP, + type_name: str, + account_name: str, + issuer: str | None, + extra_parameters: list[tuple[str, int]], +) -> str: + parameters = [ + ("digits", hotp._length), + ("secret", base64.b32encode(hotp._key)), + ("algorithm", hotp._algorithm.name.upper()), + ] + + if issuer is not None: + parameters.append(("issuer", issuer)) + + parameters.extend(extra_parameters) + + label = ( + f"{quote(issuer)}:{quote(account_name)}" + if issuer + else quote(account_name) + ) + return f"otpauth://{type_name}/{label}?{urlencode(parameters)}" + + +class HOTP: + def __init__( + self, + key: Buffer, + length: int, + algorithm: HOTPHashTypes, + backend: typing.Any = None, + enforce_key_length: bool = True, + ) -> None: + if len(key) < 16 and enforce_key_length is True: + raise ValueError("Key length has to be at least 128 bits.") + + if not isinstance(length, int): + raise TypeError("Length parameter must be an integer type.") + + if length < 6 or length > 8: + raise ValueError("Length of HOTP has to be between 6 and 8.") + + if not isinstance(algorithm, (SHA1, SHA256, SHA512)): + raise TypeError("Algorithm must be SHA1, SHA256 or SHA512.") + + self._key = key + self._length = length + self._algorithm = algorithm + + def generate(self, counter: int) -> bytes: + if not isinstance(counter, int): + raise TypeError("Counter parameter must be an integer type.") + + truncated_value = self._dynamic_truncate(counter) + hotp = truncated_value % (10**self._length) + return "{0:0{1}}".format(hotp, self._length).encode() + + def verify(self, hotp: bytes, counter: int) -> None: + if not constant_time.bytes_eq(self.generate(counter), hotp): + raise InvalidToken("Supplied HOTP value does not match.") + + def _dynamic_truncate(self, counter: int) -> int: + ctx = hmac.HMAC(self._key, self._algorithm) + + try: + ctx.update(counter.to_bytes(length=8, byteorder="big")) + except OverflowError: + raise ValueError(f"Counter must be between 0 and {2**64 - 1}.") + + hmac_value = ctx.finalize() + + offset = hmac_value[len(hmac_value) - 1] & 0b1111 + p = hmac_value[offset : offset + 4] + return int.from_bytes(p, byteorder="big") & 0x7FFFFFFF + + def get_provisioning_uri( + self, account_name: str, counter: int, issuer: str | None + ) -> str: + return _generate_uri( + self, "hotp", account_name, issuer, [("counter", int(counter))] + ) diff --git a/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/totp.py b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/totp.py new file mode 100644 index 0000000..10c725c --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/totp.py @@ -0,0 +1,56 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography.hazmat.primitives import constant_time +from cryptography.hazmat.primitives.twofactor import InvalidToken +from cryptography.hazmat.primitives.twofactor.hotp import ( + HOTP, + HOTPHashTypes, + _generate_uri, +) +from cryptography.utils import Buffer + + +class TOTP: + def __init__( + self, + key: Buffer, + length: int, + algorithm: HOTPHashTypes, + time_step: int, + backend: typing.Any = None, + enforce_key_length: bool = True, + ): + self._time_step = time_step + self._hotp = HOTP( + key, length, algorithm, enforce_key_length=enforce_key_length + ) + + def generate(self, time: int | float) -> bytes: + if not isinstance(time, (int, float)): + raise TypeError( + "Time parameter must be an integer type or float type." + ) + + counter = int(time / self._time_step) + return self._hotp.generate(counter) + + def verify(self, totp: bytes, time: int) -> None: + if not constant_time.bytes_eq(self.generate(time), totp): + raise InvalidToken("Supplied TOTP value does not match.") + + def get_provisioning_uri( + self, account_name: str, issuer: str | None + ) -> str: + return _generate_uri( + self._hotp, + "totp", + account_name, + issuer, + [("period", int(self._time_step))], + ) diff --git a/venv/lib/python3.12/site-packages/cryptography/py.typed b/venv/lib/python3.12/site-packages/cryptography/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/cryptography/utils.py b/venv/lib/python3.12/site-packages/cryptography/utils.py new file mode 100644 index 0000000..90bd42d --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/utils.py @@ -0,0 +1,136 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import enum +import sys +import types +import typing +import warnings +from collections.abc import Sequence + + +# We use a UserWarning subclass, instead of DeprecationWarning, because CPython +# decided deprecation warnings should be invisible by default. +class CryptographyDeprecationWarning(UserWarning): + pass + + +# Several APIs were deprecated with no specific end-of-life date because of the +# ubiquity of their use. They should not be removed until we agree on when that +# cycle ends. +DeprecatedIn36 = CryptographyDeprecationWarning +DeprecatedIn40 = CryptographyDeprecationWarning +DeprecatedIn41 = CryptographyDeprecationWarning +DeprecatedIn42 = CryptographyDeprecationWarning +DeprecatedIn43 = CryptographyDeprecationWarning +DeprecatedIn47 = CryptographyDeprecationWarning +DeprecatedIn50 = CryptographyDeprecationWarning + + +# If you're wondering why we don't use `Buffer`, it's because `Buffer` would +# be more accurately named: Bufferable. It means something which has an +# `__buffer__`. Which means you can't actually treat the result as a buffer +# (and do things like take a `len()`). +Buffer = typing.Union[bytes, bytearray, memoryview] + + +def _check_bytes(name: str, value: bytes) -> None: + if not isinstance(value, bytes): + raise TypeError(f"{name} must be bytes") + + +def _check_byteslike(name: str, value: Buffer) -> None: + if isinstance(value, (bytes, bytearray, memoryview)): + return + try: + memoryview(value) + except TypeError: + raise TypeError(f"{name} must be bytes-like") + + +def int_to_bytes(integer: int, length: int | None = None) -> bytes: + if length == 0: + raise ValueError("length argument can't be 0") + return integer.to_bytes( + length or (integer.bit_length() + 7) // 8 or 1, "big" + ) + + +class InterfaceNotImplemented(Exception): + pass + + +class _DeprecatedValue: + def __init__(self, value: object, message: str, warning_class): + self.value = value + self.message = message + self.warning_class = warning_class + + +class _ModuleWithDeprecations(types.ModuleType): + def __init__(self, module: types.ModuleType): + super().__init__(module.__name__) + self.__dict__["_module"] = module + + def __getattr__(self, name: str) -> typing.Any: + obj = getattr(self._module, name) + if isinstance(obj, _DeprecatedValue): + warnings.warn(obj.message, obj.warning_class, stacklevel=2) + obj = obj.value + else: + # Cache non-deprecated attributes in our own `__dict__` so that + # subsequent lookups are ordinary module attribute accesses and + # don't pay for this `__getattr__` (which would otherwise defeat + # CPython's LOAD_ATTR module caching for every attribute of the + # module). `__setattr__` and `__delattr__` keep the cache + # coherent. + self.__dict__[name] = obj + return obj + + def __setattr__(self, attr: str, value: object) -> None: + if isinstance(value, _DeprecatedValue): + self.__dict__.pop(attr, None) + else: + self.__dict__[attr] = value + setattr(self._module, attr, value) + + def __delattr__(self, attr: str) -> None: + obj = getattr(self._module, attr) + if isinstance(obj, _DeprecatedValue): + warnings.warn(obj.message, obj.warning_class, stacklevel=2) + + self.__dict__.pop(attr, None) + delattr(self._module, attr) + + def __dir__(self) -> Sequence[str]: + return ["_module", *dir(self._module)] + + +def deprecated( + value: object, + module_name: str, + message: str, + warning_class: type[Warning], + name: str | None = None, +) -> _DeprecatedValue: + module = sys.modules[module_name] + if not isinstance(module, _ModuleWithDeprecations): + sys.modules[module_name] = module = _ModuleWithDeprecations(module) + dv = _DeprecatedValue(value, message, warning_class) + # Maintain backwards compatibility with `name is None` for pyOpenSSL. + if name is not None: + setattr(module, name, dv) + return dv + + +# Python 3.10 changed representation of enums. We use well-defined object +# representation and string representation from Python 3.9. +class Enum(enum.Enum): + def __repr__(self) -> str: + return f"<{self.__class__.__name__}.{self._name_}: {self._value_!r}>" + + def __str__(self) -> str: + return f"{self.__class__.__name__}.{self._name_}" diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/__init__.py b/venv/lib/python3.12/site-packages/cryptography/x509/__init__.py new file mode 100644 index 0000000..6442b00 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/x509/__init__.py @@ -0,0 +1,270 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.x509 import certificate_transparency, oid, verification +from cryptography.x509.base import ( + Attribute, + AttributeNotFound, + Attributes, + Certificate, + CertificateBuilder, + CertificateRevocationList, + CertificateRevocationListBuilder, + CertificateSigningRequest, + CertificateSigningRequestBuilder, + InvalidVersion, + RevokedCertificate, + RevokedCertificateBuilder, + Version, + load_der_x509_certificate, + load_der_x509_crl, + load_der_x509_csr, + load_pem_x509_certificate, + load_pem_x509_certificates, + load_pem_x509_crl, + load_pem_x509_csr, + random_serial_number, +) +from cryptography.x509.extensions import ( + AccessDescription, + Admission, + Admissions, + AuthorityInformationAccess, + AuthorityKeyIdentifier, + BasicConstraints, + CertificateIssuer, + CertificatePolicies, + CRLDistributionPoints, + CRLNumber, + CRLReason, + DeltaCRLIndicator, + DistributionPoint, + DuplicateExtension, + ExtendedKeyUsage, + Extension, + ExtensionNotFound, + Extensions, + ExtensionType, + FreshestCRL, + GeneralNames, + InhibitAnyPolicy, + InvalidityDate, + IssuerAlternativeName, + IssuingDistributionPoint, + KeyUsage, + MSCertificateTemplate, + NameConstraints, + NamingAuthority, + NoticeReference, + OCSPAcceptableResponses, + OCSPNoCheck, + OCSPNonce, + PolicyConstraints, + PolicyInformation, + PrecertificateSignedCertificateTimestamps, + PrecertPoison, + PrivateKeyUsagePeriod, + ProfessionInfo, + ReasonFlags, + SignedCertificateTimestamps, + SubjectAlternativeName, + SubjectInformationAccess, + SubjectKeyIdentifier, + TLSFeature, + TLSFeatureType, + UnrecognizedExtension, + UserNotice, +) +from cryptography.x509.general_name import ( + DirectoryName, + DNSName, + GeneralName, + IPAddress, + OtherName, + RegisteredID, + RFC822Name, + UniformResourceIdentifier, + UnsupportedGeneralNameType, +) +from cryptography.x509.name import ( + Name, + NameAttribute, + RelativeDistinguishedName, +) +from cryptography.x509.oid import ( + AuthorityInformationAccessOID, + CertificatePoliciesOID, + CRLEntryExtensionOID, + ExtendedKeyUsageOID, + ExtensionOID, + NameOID, + ObjectIdentifier, + PublicKeyAlgorithmOID, + SignatureAlgorithmOID, +) + +OID_AUTHORITY_INFORMATION_ACCESS = ExtensionOID.AUTHORITY_INFORMATION_ACCESS +OID_AUTHORITY_KEY_IDENTIFIER = ExtensionOID.AUTHORITY_KEY_IDENTIFIER +OID_BASIC_CONSTRAINTS = ExtensionOID.BASIC_CONSTRAINTS +OID_CERTIFICATE_POLICIES = ExtensionOID.CERTIFICATE_POLICIES +OID_CRL_DISTRIBUTION_POINTS = ExtensionOID.CRL_DISTRIBUTION_POINTS +OID_EXTENDED_KEY_USAGE = ExtensionOID.EXTENDED_KEY_USAGE +OID_FRESHEST_CRL = ExtensionOID.FRESHEST_CRL +OID_INHIBIT_ANY_POLICY = ExtensionOID.INHIBIT_ANY_POLICY +OID_ISSUER_ALTERNATIVE_NAME = ExtensionOID.ISSUER_ALTERNATIVE_NAME +OID_KEY_USAGE = ExtensionOID.KEY_USAGE +OID_PRIVATE_KEY_USAGE_PERIOD = ExtensionOID.PRIVATE_KEY_USAGE_PERIOD +OID_NAME_CONSTRAINTS = ExtensionOID.NAME_CONSTRAINTS +OID_OCSP_NO_CHECK = ExtensionOID.OCSP_NO_CHECK +OID_POLICY_CONSTRAINTS = ExtensionOID.POLICY_CONSTRAINTS +OID_POLICY_MAPPINGS = ExtensionOID.POLICY_MAPPINGS +OID_SUBJECT_ALTERNATIVE_NAME = ExtensionOID.SUBJECT_ALTERNATIVE_NAME +OID_SUBJECT_DIRECTORY_ATTRIBUTES = ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES +OID_SUBJECT_INFORMATION_ACCESS = ExtensionOID.SUBJECT_INFORMATION_ACCESS +OID_SUBJECT_KEY_IDENTIFIER = ExtensionOID.SUBJECT_KEY_IDENTIFIER + +OID_DSA_WITH_SHA1 = SignatureAlgorithmOID.DSA_WITH_SHA1 +OID_DSA_WITH_SHA224 = SignatureAlgorithmOID.DSA_WITH_SHA224 +OID_DSA_WITH_SHA256 = SignatureAlgorithmOID.DSA_WITH_SHA256 +OID_ECDSA_WITH_SHA1 = SignatureAlgorithmOID.ECDSA_WITH_SHA1 +OID_ECDSA_WITH_SHA224 = SignatureAlgorithmOID.ECDSA_WITH_SHA224 +OID_ECDSA_WITH_SHA256 = SignatureAlgorithmOID.ECDSA_WITH_SHA256 +OID_ECDSA_WITH_SHA384 = SignatureAlgorithmOID.ECDSA_WITH_SHA384 +OID_ECDSA_WITH_SHA512 = SignatureAlgorithmOID.ECDSA_WITH_SHA512 +OID_RSA_WITH_MD5 = SignatureAlgorithmOID.RSA_WITH_MD5 +OID_RSA_WITH_SHA1 = SignatureAlgorithmOID.RSA_WITH_SHA1 +OID_RSA_WITH_SHA224 = SignatureAlgorithmOID.RSA_WITH_SHA224 +OID_RSA_WITH_SHA256 = SignatureAlgorithmOID.RSA_WITH_SHA256 +OID_RSA_WITH_SHA384 = SignatureAlgorithmOID.RSA_WITH_SHA384 +OID_RSA_WITH_SHA512 = SignatureAlgorithmOID.RSA_WITH_SHA512 +OID_RSASSA_PSS = SignatureAlgorithmOID.RSASSA_PSS + +OID_COMMON_NAME = NameOID.COMMON_NAME +OID_COUNTRY_NAME = NameOID.COUNTRY_NAME +OID_DOMAIN_COMPONENT = NameOID.DOMAIN_COMPONENT +OID_DN_QUALIFIER = NameOID.DN_QUALIFIER +OID_EMAIL_ADDRESS = NameOID.EMAIL_ADDRESS +OID_GENERATION_QUALIFIER = NameOID.GENERATION_QUALIFIER +OID_GIVEN_NAME = NameOID.GIVEN_NAME +OID_LOCALITY_NAME = NameOID.LOCALITY_NAME +OID_ORGANIZATIONAL_UNIT_NAME = NameOID.ORGANIZATIONAL_UNIT_NAME +OID_ORGANIZATION_NAME = NameOID.ORGANIZATION_NAME +OID_PSEUDONYM = NameOID.PSEUDONYM +OID_SERIAL_NUMBER = NameOID.SERIAL_NUMBER +OID_STATE_OR_PROVINCE_NAME = NameOID.STATE_OR_PROVINCE_NAME +OID_SURNAME = NameOID.SURNAME +OID_TITLE = NameOID.TITLE + +OID_CLIENT_AUTH = ExtendedKeyUsageOID.CLIENT_AUTH +OID_CODE_SIGNING = ExtendedKeyUsageOID.CODE_SIGNING +OID_EMAIL_PROTECTION = ExtendedKeyUsageOID.EMAIL_PROTECTION +OID_OCSP_SIGNING = ExtendedKeyUsageOID.OCSP_SIGNING +OID_SERVER_AUTH = ExtendedKeyUsageOID.SERVER_AUTH +OID_TIME_STAMPING = ExtendedKeyUsageOID.TIME_STAMPING + +OID_ANY_POLICY = CertificatePoliciesOID.ANY_POLICY +OID_CPS_QUALIFIER = CertificatePoliciesOID.CPS_QUALIFIER +OID_CPS_USER_NOTICE = CertificatePoliciesOID.CPS_USER_NOTICE + +OID_CERTIFICATE_ISSUER = CRLEntryExtensionOID.CERTIFICATE_ISSUER +OID_CRL_REASON = CRLEntryExtensionOID.CRL_REASON +OID_INVALIDITY_DATE = CRLEntryExtensionOID.INVALIDITY_DATE + +OID_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS +OID_OCSP = AuthorityInformationAccessOID.OCSP + +__all__ = [ + "OID_CA_ISSUERS", + "OID_OCSP", + "AccessDescription", + "Admission", + "Admissions", + "Attribute", + "AttributeNotFound", + "Attributes", + "AuthorityInformationAccess", + "AuthorityKeyIdentifier", + "BasicConstraints", + "CRLDistributionPoints", + "CRLNumber", + "CRLReason", + "Certificate", + "CertificateBuilder", + "CertificateIssuer", + "CertificatePolicies", + "CertificateRevocationList", + "CertificateRevocationListBuilder", + "CertificateSigningRequest", + "CertificateSigningRequestBuilder", + "DNSName", + "DeltaCRLIndicator", + "DirectoryName", + "DistributionPoint", + "DuplicateExtension", + "ExtendedKeyUsage", + "Extension", + "ExtensionNotFound", + "ExtensionType", + "Extensions", + "FreshestCRL", + "GeneralName", + "GeneralNames", + "IPAddress", + "InhibitAnyPolicy", + "InvalidVersion", + "InvalidityDate", + "IssuerAlternativeName", + "IssuingDistributionPoint", + "KeyUsage", + "MSCertificateTemplate", + "Name", + "NameAttribute", + "NameConstraints", + "NameOID", + "NamingAuthority", + "NoticeReference", + "OCSPAcceptableResponses", + "OCSPNoCheck", + "OCSPNonce", + "ObjectIdentifier", + "OtherName", + "PolicyConstraints", + "PolicyInformation", + "PrecertPoison", + "PrecertificateSignedCertificateTimestamps", + "PrivateKeyUsagePeriod", + "ProfessionInfo", + "PublicKeyAlgorithmOID", + "RFC822Name", + "ReasonFlags", + "RegisteredID", + "RelativeDistinguishedName", + "RevokedCertificate", + "RevokedCertificateBuilder", + "SignatureAlgorithmOID", + "SignedCertificateTimestamps", + "SubjectAlternativeName", + "SubjectInformationAccess", + "SubjectKeyIdentifier", + "TLSFeature", + "TLSFeatureType", + "UniformResourceIdentifier", + "UnrecognizedExtension", + "UnsupportedGeneralNameType", + "UserNotice", + "Version", + "certificate_transparency", + "load_der_x509_certificate", + "load_der_x509_crl", + "load_der_x509_csr", + "load_pem_x509_certificate", + "load_pem_x509_certificates", + "load_pem_x509_crl", + "load_pem_x509_csr", + "oid", + "random_serial_number", + "verification", +] diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..dc9ba7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..7213318 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/certificate_transparency.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/certificate_transparency.cpython-312.pyc new file mode 100644 index 0000000..6d36dac Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/certificate_transparency.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/extensions.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/extensions.cpython-312.pyc new file mode 100644 index 0000000..4d81a1d Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/extensions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/general_name.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/general_name.cpython-312.pyc new file mode 100644 index 0000000..e8cdb40 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/general_name.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/name.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/name.cpython-312.pyc new file mode 100644 index 0000000..20900a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/name.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/ocsp.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/ocsp.cpython-312.pyc new file mode 100644 index 0000000..575d73d Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/ocsp.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/oid.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/oid.cpython-312.pyc new file mode 100644 index 0000000..3f29eeb Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/oid.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/verification.cpython-312.pyc b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/verification.cpython-312.pyc new file mode 100644 index 0000000..f4f16de Binary files /dev/null and b/venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/verification.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/base.py b/venv/lib/python3.12/site-packages/cryptography/x509/base.py new file mode 100644 index 0000000..3ded5dc --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/x509/base.py @@ -0,0 +1,834 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import datetime +import os +import typing +from collections.abc import Iterable + +from cryptography import utils +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import ( + dsa, + ec, + ed448, + ed25519, + mldsa, + mlkem, + padding, + rsa, + x448, + x25519, +) +from cryptography.hazmat.primitives.asymmetric.types import ( + CertificateIssuerPrivateKeyTypes, + CertificatePublicKeyTypes, +) +from cryptography.x509.extensions import ( + Extension, + ExtensionType, + _make_sequence_methods, +) +from cryptography.x509.name import Name, _ASN1Type +from cryptography.x509.oid import ObjectIdentifier + +_EARLIEST_UTC_TIME = datetime.datetime(1950, 1, 1) + +# This must be kept in sync with sign.rs's list of allowable types in +# identify_hash_type +_AllowedHashTypes = typing.Union[ + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + hashes.SHA3_224, + hashes.SHA3_256, + hashes.SHA3_384, + hashes.SHA3_512, +] + + +class AttributeNotFound(Exception): + def __init__(self, msg: str, oid: ObjectIdentifier) -> None: + super().__init__(msg) + self.oid = oid + + +def _reject_duplicate_extension( + extension: Extension[ExtensionType], + extensions: list[Extension[ExtensionType]], +) -> None: + # This is quadratic in the number of extensions + for e in extensions: + if e.oid == extension.oid: + raise ValueError("This extension has already been set.") + + +def _reject_duplicate_attribute( + oid: ObjectIdentifier, + attributes: list[tuple[ObjectIdentifier, bytes, int | None]], +) -> None: + # This is quadratic in the number of attributes + for attr_oid, _, _ in attributes: + if attr_oid == oid: + raise ValueError("This attribute has already been set.") + + +def _convert_to_naive_utc_time(time: datetime.datetime) -> datetime.datetime: + """Normalizes a datetime to a naive datetime in UTC. + + time -- datetime to normalize. Assumed to be in UTC if not timezone + aware. + """ + if time.tzinfo is not None: + offset = time.utcoffset() + offset = offset if offset else datetime.timedelta() + return time.replace(tzinfo=None) - offset + else: + return time + + +class Attribute: + def __init__( + self, + oid: ObjectIdentifier, + value: bytes, + _type: int = _ASN1Type.UTF8String.value, + ) -> None: + self._oid = oid + self._value = value + self._type = _type + + @property + def oid(self) -> ObjectIdentifier: + return self._oid + + @property + def value(self) -> bytes: + return self._value + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Attribute): + return NotImplemented + + return ( + self.oid == other.oid + and self.value == other.value + and self._type == other._type + ) + + def __hash__(self) -> int: + return hash((self.oid, self.value, self._type)) + + +class Attributes: + def __init__( + self, + attributes: Iterable[Attribute], + ) -> None: + self._attributes = list(attributes) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_attributes") + + def __repr__(self) -> str: + return f"" + + def get_attribute_for_oid(self, oid: ObjectIdentifier) -> Attribute: + for attr in self: + if attr.oid == oid: + return attr + + raise AttributeNotFound(f"No {oid} attribute was found", oid) + + +class Version(utils.Enum): + v1 = 0 + v3 = 2 + + +class InvalidVersion(Exception): + def __init__(self, msg: str, parsed_version: int) -> None: + super().__init__(msg) + self.parsed_version = parsed_version + + +Certificate = rust_x509.Certificate +RevokedCertificate = rust_x509.RevokedCertificate + + +CertificateRevocationList = rust_x509.CertificateRevocationList +CertificateSigningRequest = rust_x509.CertificateSigningRequest + + +load_pem_x509_certificate = rust_x509.load_pem_x509_certificate +load_der_x509_certificate = rust_x509.load_der_x509_certificate + +load_pem_x509_certificates = rust_x509.load_pem_x509_certificates + +load_pem_x509_csr = rust_x509.load_pem_x509_csr +load_der_x509_csr = rust_x509.load_der_x509_csr + +load_pem_x509_crl = rust_x509.load_pem_x509_crl +load_der_x509_crl = rust_x509.load_der_x509_crl + + +class CertificateSigningRequestBuilder: + def __init__( + self, + subject_name: Name | None = None, + extensions: list[Extension[ExtensionType]] = [], + attributes: list[tuple[ObjectIdentifier, bytes, int | None]] = [], + ): + """ + Creates an empty X.509 certificate request (v1). + """ + self._subject_name = subject_name + self._extensions = extensions + self._attributes = attributes + + def subject_name(self, name: Name) -> CertificateSigningRequestBuilder: + """ + Sets the certificate requestor's distinguished name. + """ + if not isinstance(name, Name): + raise TypeError("Expecting x509.Name object.") + if self._subject_name is not None: + raise ValueError("The subject name may only be set once.") + return CertificateSigningRequestBuilder( + name, self._extensions, self._attributes + ) + + def add_extension( + self, extval: ExtensionType, critical: bool + ) -> CertificateSigningRequestBuilder: + """ + Adds an X.509 extension to the certificate request. + """ + if not isinstance(extval, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + + return CertificateSigningRequestBuilder( + self._subject_name, + [*self._extensions, extension], + self._attributes, + ) + + def add_attribute( + self, + oid: ObjectIdentifier, + value: bytes, + *, + _tag: _ASN1Type | None = None, + ) -> CertificateSigningRequestBuilder: + """ + Adds an X.509 attribute with an OID and associated value. + """ + if not isinstance(oid, ObjectIdentifier): + raise TypeError("oid must be an ObjectIdentifier") + + if not isinstance(value, bytes): + raise TypeError("value must be bytes") + + if _tag is not None and not isinstance(_tag, _ASN1Type): + raise TypeError("tag must be _ASN1Type") + + _reject_duplicate_attribute(oid, self._attributes) + + if _tag is not None: + tag = _tag.value + else: + tag = None + + return CertificateSigningRequestBuilder( + self._subject_name, + self._extensions, + [*self._attributes, (oid, value, tag)], + ) + + def sign( + self, + private_key: CertificateIssuerPrivateKeyTypes, + algorithm: _AllowedHashTypes | None, + backend: typing.Any = None, + *, + rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, + ecdsa_deterministic: bool | None = None, + ) -> CertificateSigningRequest: + """ + Signs the request using the requestor's private key. + """ + if self._subject_name is None: + raise ValueError("A CertificateSigningRequest must have a subject") + + if rsa_padding is not None: + if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): + raise TypeError("Padding must be PSS or PKCS1v15") + if not isinstance(private_key, rsa.RSAPrivateKey): + raise TypeError("Padding is only supported for RSA keys") + + if ecdsa_deterministic is not None: + if not isinstance(private_key, ec.EllipticCurvePrivateKey): + raise TypeError( + "Deterministic ECDSA is only supported for EC keys" + ) + + return rust_x509.create_x509_csr( + self, + private_key, + algorithm, + rsa_padding, + ecdsa_deterministic, + ) + + +class CertificateBuilder: + _extensions: list[Extension[ExtensionType]] + + def __init__( + self, + issuer_name: Name | None = None, + subject_name: Name | None = None, + public_key: CertificatePublicKeyTypes | None = None, + serial_number: int | None = None, + not_valid_before: datetime.datetime | None = None, + not_valid_after: datetime.datetime | None = None, + extensions: list[Extension[ExtensionType]] = [], + public_key_rsa_padding: type[padding.PSS] | None = None, + ) -> None: + self._version = Version.v3 + self._issuer_name = issuer_name + self._subject_name = subject_name + self._public_key = public_key + self._serial_number = serial_number + self._not_valid_before = not_valid_before + self._not_valid_after = not_valid_after + self._extensions = extensions + self._public_key_rsa_padding = public_key_rsa_padding + + def issuer_name(self, name: Name) -> CertificateBuilder: + """ + Sets the CA's distinguished name. + """ + if not isinstance(name, Name): + raise TypeError("Expecting x509.Name object.") + if self._issuer_name is not None: + raise ValueError("The issuer name may only be set once.") + return CertificateBuilder( + name, + self._subject_name, + self._public_key, + self._serial_number, + self._not_valid_before, + self._not_valid_after, + self._extensions, + self._public_key_rsa_padding, + ) + + def subject_name(self, name: Name) -> CertificateBuilder: + """ + Sets the requestor's distinguished name. + """ + if not isinstance(name, Name): + raise TypeError("Expecting x509.Name object.") + if self._subject_name is not None: + raise ValueError("The subject name may only be set once.") + return CertificateBuilder( + self._issuer_name, + name, + self._public_key, + self._serial_number, + self._not_valid_before, + self._not_valid_after, + self._extensions, + self._public_key_rsa_padding, + ) + + def public_key( + self, + key: CertificatePublicKeyTypes, + *, + rsa_padding: type[padding.PSS] | None = None, + ) -> CertificateBuilder: + """ + Sets the requestor's public key (as found in the signing request). + """ + if not isinstance( + key, + ( + dsa.DSAPublicKey, + rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey, + mldsa.MLDSA44PublicKey, + mldsa.MLDSA65PublicKey, + mldsa.MLDSA87PublicKey, + mlkem.MLKEM768PublicKey, + mlkem.MLKEM1024PublicKey, + x25519.X25519PublicKey, + x448.X448PublicKey, + ), + ): + raise TypeError( + "Expecting one of DSAPublicKey, RSAPublicKey," + " EllipticCurvePublicKey, Ed25519PublicKey," + " Ed448PublicKey, MLDSA44PublicKey, MLDSA65PublicKey," + " MLDSA87PublicKey, MLKEM768PublicKey, MLKEM1024PublicKey," + " X25519PublicKey or X448PublicKey." + ) + if rsa_padding is not None: + if rsa_padding is not padding.PSS: + raise TypeError( + "rsa_padding must be the PSS class, not an instance" + ) + if not isinstance(key, rsa.RSAPublicKey): + raise TypeError( + "rsa_padding is only supported with RSA public keys" + ) + if self._public_key is not None: + raise ValueError("The public key may only be set once.") + return CertificateBuilder( + self._issuer_name, + self._subject_name, + key, + self._serial_number, + self._not_valid_before, + self._not_valid_after, + self._extensions, + rsa_padding, + ) + + def serial_number(self, number: int) -> CertificateBuilder: + """ + Sets the certificate serial number. + """ + if not isinstance(number, int): + raise TypeError("Serial number must be of integral type.") + if self._serial_number is not None: + raise ValueError("The serial number may only be set once.") + if number <= 0: + raise ValueError("The serial number should be positive.") + + # ASN.1 integers are always signed, so most significant bit must be + # zero. + if number.bit_length() >= 160: # As defined in RFC 5280 + raise ValueError( + "The serial number should not be more than 159 bits." + ) + return CertificateBuilder( + self._issuer_name, + self._subject_name, + self._public_key, + number, + self._not_valid_before, + self._not_valid_after, + self._extensions, + self._public_key_rsa_padding, + ) + + def not_valid_before(self, time: datetime.datetime) -> CertificateBuilder: + """ + Sets the certificate activation time. + """ + if not isinstance(time, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._not_valid_before is not None: + raise ValueError("The not valid before may only be set once.") + time = _convert_to_naive_utc_time(time) + if time < _EARLIEST_UTC_TIME: + raise ValueError( + "The not valid before date must be on or after" + " 1950 January 1)." + ) + if self._not_valid_after is not None and time > self._not_valid_after: + raise ValueError( + "The not valid before date must be before the not valid after " + "date." + ) + return CertificateBuilder( + self._issuer_name, + self._subject_name, + self._public_key, + self._serial_number, + time, + self._not_valid_after, + self._extensions, + self._public_key_rsa_padding, + ) + + def not_valid_after(self, time: datetime.datetime) -> CertificateBuilder: + """ + Sets the certificate expiration time. + """ + if not isinstance(time, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._not_valid_after is not None: + raise ValueError("The not valid after may only be set once.") + time = _convert_to_naive_utc_time(time) + if time < _EARLIEST_UTC_TIME: + raise ValueError( + "The not valid after date must be on or after 1950 January 1." + ) + if ( + self._not_valid_before is not None + and time < self._not_valid_before + ): + raise ValueError( + "The not valid after date must be after the not valid before " + "date." + ) + return CertificateBuilder( + self._issuer_name, + self._subject_name, + self._public_key, + self._serial_number, + self._not_valid_before, + time, + self._extensions, + self._public_key_rsa_padding, + ) + + def add_extension( + self, extval: ExtensionType, critical: bool + ) -> CertificateBuilder: + """ + Adds an X.509 extension to the certificate. + """ + if not isinstance(extval, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + + return CertificateBuilder( + self._issuer_name, + self._subject_name, + self._public_key, + self._serial_number, + self._not_valid_before, + self._not_valid_after, + [*self._extensions, extension], + self._public_key_rsa_padding, + ) + + def sign( + self, + private_key: CertificateIssuerPrivateKeyTypes, + algorithm: _AllowedHashTypes | None, + backend: typing.Any = None, + *, + rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, + ecdsa_deterministic: bool | None = None, + ) -> Certificate: + """ + Signs the certificate using the CA's private key. + """ + if self._subject_name is None: + raise ValueError("A certificate must have a subject name") + + if self._issuer_name is None: + raise ValueError("A certificate must have an issuer name") + + if self._serial_number is None: + raise ValueError("A certificate must have a serial number") + + if self._not_valid_before is None: + raise ValueError("A certificate must have a not valid before time") + + if self._not_valid_after is None: + raise ValueError("A certificate must have a not valid after time") + + if self._public_key is None: + raise ValueError("A certificate must have a public key") + + if private_key is None: + raise TypeError("Need private key to sign certificate") + + if rsa_padding is not None: + if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): + raise TypeError("Padding must be PSS or PKCS1v15") + if not isinstance(private_key, rsa.RSAPrivateKey): + raise TypeError("Padding is only supported for RSA keys") + + if ecdsa_deterministic is not None: + if not isinstance(private_key, ec.EllipticCurvePrivateKey): + raise TypeError( + "Deterministic ECDSA is only supported for EC keys" + ) + + return rust_x509.create_x509_certificate( + self, + private_key, + algorithm, + rsa_padding, + ecdsa_deterministic, + ) + + def create_unsigned(self) -> Certificate: + """ + Creates an unsigned certificate, per RFC 9925. + """ + if self._subject_name is None: + raise ValueError("A certificate must have a subject name") + + if self._issuer_name is None: + raise ValueError("A certificate must have an issuer name") + + if self._serial_number is None: + raise ValueError("A certificate must have a serial number") + + if self._not_valid_before is None: + raise ValueError("A certificate must have a not valid before time") + + if self._not_valid_after is None: + raise ValueError("A certificate must have a not valid after time") + + if self._public_key is None: + raise ValueError("A certificate must have a public key") + + return rust_x509.create_x509_certificate( + self, + private_key=None, + hash_algorithm=None, + rsa_padding=None, + ecdsa_deterministic=None, + ) + + +class CertificateRevocationListBuilder: + _extensions: list[Extension[ExtensionType]] + _revoked_certificates: list[RevokedCertificate] + + def __init__( + self, + issuer_name: Name | None = None, + last_update: datetime.datetime | None = None, + next_update: datetime.datetime | None = None, + extensions: list[Extension[ExtensionType]] = [], + revoked_certificates: list[RevokedCertificate] = [], + ): + self._issuer_name = issuer_name + self._last_update = last_update + self._next_update = next_update + self._extensions = extensions + self._revoked_certificates = revoked_certificates + + def issuer_name( + self, issuer_name: Name + ) -> CertificateRevocationListBuilder: + if not isinstance(issuer_name, Name): + raise TypeError("Expecting x509.Name object.") + if self._issuer_name is not None: + raise ValueError("The issuer name may only be set once.") + return CertificateRevocationListBuilder( + issuer_name, + self._last_update, + self._next_update, + self._extensions, + self._revoked_certificates, + ) + + def last_update( + self, last_update: datetime.datetime + ) -> CertificateRevocationListBuilder: + if not isinstance(last_update, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._last_update is not None: + raise ValueError("Last update may only be set once.") + last_update = _convert_to_naive_utc_time(last_update) + if last_update < _EARLIEST_UTC_TIME: + raise ValueError( + "The last update date must be on or after 1950 January 1." + ) + if self._next_update is not None and last_update > self._next_update: + raise ValueError( + "The last update date must be before the next update date." + ) + return CertificateRevocationListBuilder( + self._issuer_name, + last_update, + self._next_update, + self._extensions, + self._revoked_certificates, + ) + + def next_update( + self, next_update: datetime.datetime + ) -> CertificateRevocationListBuilder: + if not isinstance(next_update, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._next_update is not None: + raise ValueError("Last update may only be set once.") + next_update = _convert_to_naive_utc_time(next_update) + if next_update < _EARLIEST_UTC_TIME: + raise ValueError( + "The last update date must be on or after 1950 January 1." + ) + if self._last_update is not None and next_update < self._last_update: + raise ValueError( + "The next update date must be after the last update date." + ) + return CertificateRevocationListBuilder( + self._issuer_name, + self._last_update, + next_update, + self._extensions, + self._revoked_certificates, + ) + + def add_extension( + self, extval: ExtensionType, critical: bool + ) -> CertificateRevocationListBuilder: + """ + Adds an X.509 extension to the certificate revocation list. + """ + if not isinstance(extval, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + return CertificateRevocationListBuilder( + self._issuer_name, + self._last_update, + self._next_update, + [*self._extensions, extension], + self._revoked_certificates, + ) + + def add_revoked_certificate( + self, revoked_certificate: RevokedCertificate + ) -> CertificateRevocationListBuilder: + """ + Adds a revoked certificate to the CRL. + """ + if not isinstance(revoked_certificate, RevokedCertificate): + raise TypeError("Must be an instance of RevokedCertificate") + + return CertificateRevocationListBuilder( + self._issuer_name, + self._last_update, + self._next_update, + self._extensions, + [*self._revoked_certificates, revoked_certificate], + ) + + def sign( + self, + private_key: CertificateIssuerPrivateKeyTypes, + algorithm: _AllowedHashTypes | None, + backend: typing.Any = None, + *, + rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, + ecdsa_deterministic: bool | None = None, + ) -> CertificateRevocationList: + if self._issuer_name is None: + raise ValueError("A CRL must have an issuer name") + + if self._last_update is None: + raise ValueError("A CRL must have a last update time") + + if self._next_update is None: + raise ValueError("A CRL must have a next update time") + + if rsa_padding is not None: + if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): + raise TypeError("Padding must be PSS or PKCS1v15") + if not isinstance(private_key, rsa.RSAPrivateKey): + raise TypeError("Padding is only supported for RSA keys") + + if ecdsa_deterministic is not None: + if not isinstance(private_key, ec.EllipticCurvePrivateKey): + raise TypeError( + "Deterministic ECDSA is only supported for EC keys" + ) + + return rust_x509.create_x509_crl( + self, + private_key, + algorithm, + rsa_padding, + ecdsa_deterministic, + ) + + +class RevokedCertificateBuilder: + def __init__( + self, + serial_number: int | None = None, + revocation_date: datetime.datetime | None = None, + extensions: list[Extension[ExtensionType]] = [], + ): + self._serial_number = serial_number + self._revocation_date = revocation_date + self._extensions = extensions + + def serial_number(self, number: int) -> RevokedCertificateBuilder: + if not isinstance(number, int): + raise TypeError("Serial number must be of integral type.") + if self._serial_number is not None: + raise ValueError("The serial number may only be set once.") + if number <= 0: + raise ValueError("The serial number should be positive") + + # ASN.1 integers are always signed, so most significant bit must be + # zero. + if number.bit_length() >= 160: # As defined in RFC 5280 + raise ValueError( + "The serial number should not be more than 159 bits." + ) + return RevokedCertificateBuilder( + number, self._revocation_date, self._extensions + ) + + def revocation_date( + self, time: datetime.datetime + ) -> RevokedCertificateBuilder: + if not isinstance(time, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._revocation_date is not None: + raise ValueError("The revocation date may only be set once.") + time = _convert_to_naive_utc_time(time) + if time < _EARLIEST_UTC_TIME: + raise ValueError( + "The revocation date must be on or after 1950 January 1." + ) + return RevokedCertificateBuilder( + self._serial_number, time, self._extensions + ) + + def add_extension( + self, extval: ExtensionType, critical: bool + ) -> RevokedCertificateBuilder: + if not isinstance(extval, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + return RevokedCertificateBuilder( + self._serial_number, + self._revocation_date, + [*self._extensions, extension], + ) + + def build(self, backend: typing.Any = None) -> RevokedCertificate: + if self._serial_number is None: + raise ValueError("A revoked certificate must have a serial number") + if self._revocation_date is None: + raise ValueError( + "A revoked certificate must have a revocation date" + ) + return rust_x509.create_revoked_certificate(self) + + +def random_serial_number() -> int: + return int.from_bytes(os.urandom(20), "big") >> 1 diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/certificate_transparency.py b/venv/lib/python3.12/site-packages/cryptography/x509/certificate_transparency.py new file mode 100644 index 0000000..fb66cc6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/x509/certificate_transparency.py @@ -0,0 +1,35 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography import utils +from cryptography.hazmat.bindings._rust import x509 as rust_x509 + + +class LogEntryType(utils.Enum): + X509_CERTIFICATE = 0 + PRE_CERTIFICATE = 1 + + +class Version(utils.Enum): + v1 = 0 + + +class SignatureAlgorithm(utils.Enum): + """ + Signature algorithms that are valid for SCTs. + + These are exactly the same as SignatureAlgorithm in RFC 5246 (TLS 1.2). + + See: + """ + + ANONYMOUS = 0 + RSA = 1 + DSA = 2 + ECDSA = 3 + + +SignedCertificateTimestamp = rust_x509.Sct diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/extensions.py b/venv/lib/python3.12/site-packages/cryptography/x509/extensions.py new file mode 100644 index 0000000..dc7728e --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/x509/extensions.py @@ -0,0 +1,2533 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import datetime +import hashlib +import ipaddress +import typing +from collections.abc import Iterable, Iterator + +from cryptography import utils +from cryptography.hazmat.bindings._rust import asn1 +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.hazmat.primitives import _serialization, constant_time +from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey +from cryptography.hazmat.primitives.asymmetric.types import ( + CertificateIssuerPublicKeyTypes, + CertificatePublicKeyTypes, +) +from cryptography.x509.certificate_transparency import ( + SignedCertificateTimestamp, +) +from cryptography.x509.general_name import ( + DirectoryName, + DNSName, + GeneralName, + IPAddress, + OtherName, + RegisteredID, + RFC822Name, + UniformResourceIdentifier, + _IPAddressTypes, +) +from cryptography.x509.name import Name, RelativeDistinguishedName +from cryptography.x509.oid import ( + CRLEntryExtensionOID, + ExtensionOID, + ObjectIdentifier, + OCSPExtensionOID, +) + +ExtensionTypeVar = typing.TypeVar( + "ExtensionTypeVar", bound="ExtensionType", covariant=True +) + + +def _key_identifier_from_public_key( + public_key: CertificatePublicKeyTypes, +) -> bytes: + if isinstance(public_key, RSAPublicKey): + data = public_key.public_bytes( + _serialization.Encoding.DER, + _serialization.PublicFormat.PKCS1, + ) + elif isinstance(public_key, EllipticCurvePublicKey): + data = public_key.public_bytes( + _serialization.Encoding.X962, + _serialization.PublicFormat.UncompressedPoint, + ) + else: + # This is a very slow way to do this. + serialized = public_key.public_bytes( + _serialization.Encoding.DER, + _serialization.PublicFormat.SubjectPublicKeyInfo, + ) + data = asn1.parse_spki_for_data(serialized) + + return hashlib.sha1(data).digest() + + +def _make_sequence_methods(field_name: str): + def len_method(self) -> int: + return len(getattr(self, field_name)) + + def iter_method(self): + return iter(getattr(self, field_name)) + + def getitem_method(self, idx): + return getattr(self, field_name)[idx] + + return len_method, iter_method, getitem_method + + +class DuplicateExtension(Exception): + def __init__(self, msg: str, oid: ObjectIdentifier) -> None: + super().__init__(msg) + self.oid = oid + + +class ExtensionNotFound(Exception): + def __init__(self, msg: str, oid: ObjectIdentifier) -> None: + super().__init__(msg) + self.oid = oid + + +class ExtensionType(metaclass=abc.ABCMeta): + oid: typing.ClassVar[ObjectIdentifier] + + def public_bytes(self) -> bytes: + """ + Serializes the extension type to DER. + """ + raise NotImplementedError( + f"public_bytes is not implemented for extension type {self!r}" + ) + + +class Extensions: + def __init__(self, extensions: Iterable[Extension[ExtensionType]]) -> None: + self._extensions = list(extensions) + + def get_extension_for_oid( + self, oid: ObjectIdentifier + ) -> Extension[ExtensionType]: + for ext in self: + if ext.oid == oid: + return ext + + raise ExtensionNotFound(f"No {oid} extension was found", oid) + + def get_extension_for_class( + self, extclass: type[ExtensionTypeVar] + ) -> Extension[ExtensionTypeVar]: + if extclass is UnrecognizedExtension: + raise TypeError( + "UnrecognizedExtension can't be used with " + "get_extension_for_class because more than one instance of the" + " class may be present." + ) + + for ext in self: + if isinstance(ext.value, extclass): + return ext + + raise ExtensionNotFound( + f"No {extclass} extension was found", extclass.oid + ) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_extensions") + + def __repr__(self) -> str: + return f"" + + +class CRLNumber(ExtensionType): + oid = ExtensionOID.CRL_NUMBER + + def __init__(self, crl_number: int) -> None: + if not isinstance(crl_number, int): + raise TypeError("crl_number must be an integer") + + self._crl_number = crl_number + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CRLNumber): + return NotImplemented + + return self.crl_number == other.crl_number + + def __hash__(self) -> int: + return hash(self.crl_number) + + def __repr__(self) -> str: + return f"" + + @property + def crl_number(self) -> int: + return self._crl_number + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class AuthorityKeyIdentifier(ExtensionType): + oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER + + def __init__( + self, + key_identifier: bytes | None, + authority_cert_issuer: Iterable[GeneralName] | None, + authority_cert_serial_number: int | None, + ) -> None: + if (authority_cert_issuer is None) != ( + authority_cert_serial_number is None + ): + raise ValueError( + "authority_cert_issuer and authority_cert_serial_number " + "must both be present or both None" + ) + + if authority_cert_issuer is not None: + authority_cert_issuer = list(authority_cert_issuer) + if not all( + isinstance(x, GeneralName) for x in authority_cert_issuer + ): + raise TypeError( + "authority_cert_issuer must be a list of GeneralName " + "objects" + ) + + if authority_cert_serial_number is not None and not isinstance( + authority_cert_serial_number, int + ): + raise TypeError("authority_cert_serial_number must be an integer") + + self._key_identifier = key_identifier + self._authority_cert_issuer = authority_cert_issuer + self._authority_cert_serial_number = authority_cert_serial_number + + # This takes a subset of CertificatePublicKeyTypes because an issuer + # cannot have an X25519/X448 key. This introduces some unfortunate + # asymmetry that requires typing users to explicitly + # narrow their type, but we should make this accurate and not just + # convenient. + @classmethod + def from_issuer_public_key( + cls, public_key: CertificateIssuerPublicKeyTypes + ) -> AuthorityKeyIdentifier: + digest = _key_identifier_from_public_key(public_key) + return cls( + key_identifier=digest, + authority_cert_issuer=None, + authority_cert_serial_number=None, + ) + + @classmethod + def from_issuer_subject_key_identifier( + cls, ski: SubjectKeyIdentifier + ) -> AuthorityKeyIdentifier: + return cls( + key_identifier=ski.digest, + authority_cert_issuer=None, + authority_cert_serial_number=None, + ) + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, AuthorityKeyIdentifier): + return NotImplemented + + return ( + self.key_identifier == other.key_identifier + and self.authority_cert_issuer == other.authority_cert_issuer + and self.authority_cert_serial_number + == other.authority_cert_serial_number + ) + + def __hash__(self) -> int: + if self.authority_cert_issuer is None: + aci = None + else: + aci = tuple(self.authority_cert_issuer) + return hash( + (self.key_identifier, aci, self.authority_cert_serial_number) + ) + + @property + def key_identifier(self) -> bytes | None: + return self._key_identifier + + @property + def authority_cert_issuer( + self, + ) -> list[GeneralName] | None: + return self._authority_cert_issuer + + @property + def authority_cert_serial_number(self) -> int | None: + return self._authority_cert_serial_number + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class SubjectKeyIdentifier(ExtensionType): + oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER + + def __init__(self, digest: bytes) -> None: + self._digest = digest + + @classmethod + def from_public_key( + cls, public_key: CertificatePublicKeyTypes + ) -> SubjectKeyIdentifier: + return cls(_key_identifier_from_public_key(public_key)) + + @property + def digest(self) -> bytes: + return self._digest + + @property + def key_identifier(self) -> bytes: + return self._digest + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SubjectKeyIdentifier): + return NotImplemented + + return constant_time.bytes_eq(self.digest, other.digest) + + def __hash__(self) -> int: + return hash(self.digest) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class AuthorityInformationAccess(ExtensionType): + oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS + + def __init__(self, descriptions: Iterable[AccessDescription]) -> None: + descriptions = list(descriptions) + if not all(isinstance(x, AccessDescription) for x in descriptions): + raise TypeError( + "Every item in the descriptions list must be an " + "AccessDescription" + ) + + self._descriptions = descriptions + + __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, AuthorityInformationAccess): + return NotImplemented + + return self._descriptions == other._descriptions + + def __hash__(self) -> int: + return hash(tuple(self._descriptions)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class SubjectInformationAccess(ExtensionType): + oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS + + def __init__(self, descriptions: Iterable[AccessDescription]) -> None: + descriptions = list(descriptions) + if not all(isinstance(x, AccessDescription) for x in descriptions): + raise TypeError( + "Every item in the descriptions list must be an " + "AccessDescription" + ) + + self._descriptions = descriptions + + __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SubjectInformationAccess): + return NotImplemented + + return self._descriptions == other._descriptions + + def __hash__(self) -> int: + return hash(tuple(self._descriptions)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class AccessDescription: + def __init__( + self, access_method: ObjectIdentifier, access_location: GeneralName + ) -> None: + if not isinstance(access_method, ObjectIdentifier): + raise TypeError("access_method must be an ObjectIdentifier") + + if not isinstance(access_location, GeneralName): + raise TypeError("access_location must be a GeneralName") + + self._access_method = access_method + self._access_location = access_location + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, AccessDescription): + return NotImplemented + + return ( + self.access_method == other.access_method + and self.access_location == other.access_location + ) + + def __hash__(self) -> int: + return hash((self.access_method, self.access_location)) + + @property + def access_method(self) -> ObjectIdentifier: + return self._access_method + + @property + def access_location(self) -> GeneralName: + return self._access_location + + +class BasicConstraints(ExtensionType): + oid = ExtensionOID.BASIC_CONSTRAINTS + + def __init__(self, ca: bool, path_length: int | None) -> None: + if not isinstance(ca, bool): + raise TypeError("ca must be a boolean value") + + if path_length is not None and not ca: + raise ValueError("path_length must be None when ca is False") + + if path_length is not None and ( + not isinstance(path_length, int) or path_length < 0 + ): + raise TypeError( + "path_length must be a non-negative integer or None" + ) + + self._ca = ca + self._path_length = path_length + + @property + def ca(self) -> bool: + return self._ca + + @property + def path_length(self) -> int | None: + return self._path_length + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, BasicConstraints): + return NotImplemented + + return self.ca == other.ca and self.path_length == other.path_length + + def __hash__(self) -> int: + return hash((self.ca, self.path_length)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class DeltaCRLIndicator(ExtensionType): + oid = ExtensionOID.DELTA_CRL_INDICATOR + + def __init__(self, crl_number: int) -> None: + if not isinstance(crl_number, int): + raise TypeError("crl_number must be an integer") + + self._crl_number = crl_number + + @property + def crl_number(self) -> int: + return self._crl_number + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DeltaCRLIndicator): + return NotImplemented + + return self.crl_number == other.crl_number + + def __hash__(self) -> int: + return hash(self.crl_number) + + def __repr__(self) -> str: + return f"" + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class CRLDistributionPoints(ExtensionType): + oid = ExtensionOID.CRL_DISTRIBUTION_POINTS + + def __init__( + self, distribution_points: Iterable[DistributionPoint] + ) -> None: + distribution_points = list(distribution_points) + if not all( + isinstance(x, DistributionPoint) for x in distribution_points + ): + raise TypeError( + "distribution_points must be a list of DistributionPoint " + "objects" + ) + + self._distribution_points = distribution_points + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_distribution_points" + ) + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CRLDistributionPoints): + return NotImplemented + + return self._distribution_points == other._distribution_points + + def __hash__(self) -> int: + return hash(tuple(self._distribution_points)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class FreshestCRL(ExtensionType): + oid = ExtensionOID.FRESHEST_CRL + + def __init__( + self, distribution_points: Iterable[DistributionPoint] + ) -> None: + distribution_points = list(distribution_points) + if not all( + isinstance(x, DistributionPoint) for x in distribution_points + ): + raise TypeError( + "distribution_points must be a list of DistributionPoint " + "objects" + ) + + self._distribution_points = distribution_points + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_distribution_points" + ) + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, FreshestCRL): + return NotImplemented + + return self._distribution_points == other._distribution_points + + def __hash__(self) -> int: + return hash(tuple(self._distribution_points)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class DistributionPoint: + def __init__( + self, + full_name: Iterable[GeneralName] | None, + relative_name: RelativeDistinguishedName | None, + reasons: frozenset[ReasonFlags] | None, + crl_issuer: Iterable[GeneralName] | None, + ) -> None: + if full_name and relative_name: + raise ValueError( + "You cannot provide both full_name and relative_name, at " + "least one must be None." + ) + if not full_name and not relative_name and not crl_issuer: + raise ValueError( + "Either full_name, relative_name or crl_issuer must be " + "provided." + ) + + if full_name is not None: + full_name = list(full_name) + if not all(isinstance(x, GeneralName) for x in full_name): + raise TypeError( + "full_name must be a list of GeneralName objects" + ) + + if relative_name: + if not isinstance(relative_name, RelativeDistinguishedName): + raise TypeError( + "relative_name must be a RelativeDistinguishedName" + ) + + if crl_issuer is not None: + crl_issuer = list(crl_issuer) + if not all(isinstance(x, GeneralName) for x in crl_issuer): + raise TypeError( + "crl_issuer must be None or a list of general names" + ) + + if reasons and ( + not isinstance(reasons, frozenset) + or not all(isinstance(x, ReasonFlags) for x in reasons) + ): + raise TypeError("reasons must be None or frozenset of ReasonFlags") + + if reasons and ( + ReasonFlags.unspecified in reasons + or ReasonFlags.remove_from_crl in reasons + ): + raise ValueError( + "unspecified and remove_from_crl are not valid reasons in a " + "DistributionPoint" + ) + + self._full_name = full_name + self._relative_name = relative_name + self._reasons = reasons + self._crl_issuer = crl_issuer + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DistributionPoint): + return NotImplemented + + return ( + self.full_name == other.full_name + and self.relative_name == other.relative_name + and self.reasons == other.reasons + and self.crl_issuer == other.crl_issuer + ) + + def __hash__(self) -> int: + if self.full_name is not None: + fn: tuple[GeneralName, ...] | None = tuple(self.full_name) + else: + fn = None + + if self.crl_issuer is not None: + crl_issuer: tuple[GeneralName, ...] | None = tuple(self.crl_issuer) + else: + crl_issuer = None + + return hash((fn, self.relative_name, self.reasons, crl_issuer)) + + @property + def full_name(self) -> list[GeneralName] | None: + return self._full_name + + @property + def relative_name(self) -> RelativeDistinguishedName | None: + return self._relative_name + + @property + def reasons(self) -> frozenset[ReasonFlags] | None: + return self._reasons + + @property + def crl_issuer(self) -> list[GeneralName] | None: + return self._crl_issuer + + +class ReasonFlags(utils.Enum): + unspecified = "unspecified" + key_compromise = "keyCompromise" + ca_compromise = "cACompromise" + affiliation_changed = "affiliationChanged" + superseded = "superseded" + cessation_of_operation = "cessationOfOperation" + certificate_hold = "certificateHold" + privilege_withdrawn = "privilegeWithdrawn" + aa_compromise = "aACompromise" + remove_from_crl = "removeFromCRL" + + +# These are distribution point bit string mappings. Not to be confused with +# CRLReason reason flags bit string mappings. +# ReasonFlags ::= BIT STRING { +# unused (0), +# keyCompromise (1), +# cACompromise (2), +# affiliationChanged (3), +# superseded (4), +# cessationOfOperation (5), +# certificateHold (6), +# privilegeWithdrawn (7), +# aACompromise (8) } +_REASON_BIT_MAPPING = { + 1: ReasonFlags.key_compromise, + 2: ReasonFlags.ca_compromise, + 3: ReasonFlags.affiliation_changed, + 4: ReasonFlags.superseded, + 5: ReasonFlags.cessation_of_operation, + 6: ReasonFlags.certificate_hold, + 7: ReasonFlags.privilege_withdrawn, + 8: ReasonFlags.aa_compromise, +} + +_CRLREASONFLAGS = { + ReasonFlags.key_compromise: 1, + ReasonFlags.ca_compromise: 2, + ReasonFlags.affiliation_changed: 3, + ReasonFlags.superseded: 4, + ReasonFlags.cessation_of_operation: 5, + ReasonFlags.certificate_hold: 6, + ReasonFlags.privilege_withdrawn: 7, + ReasonFlags.aa_compromise: 8, +} + +# CRLReason ::= ENUMERATED { +# unspecified (0), +# keyCompromise (1), +# cACompromise (2), +# affiliationChanged (3), +# superseded (4), +# cessationOfOperation (5), +# certificateHold (6), +# -- value 7 is not used +# removeFromCRL (8), +# privilegeWithdrawn (9), +# aACompromise (10) } +_CRL_ENTRY_REASON_ENUM_TO_CODE = { + ReasonFlags.unspecified: 0, + ReasonFlags.key_compromise: 1, + ReasonFlags.ca_compromise: 2, + ReasonFlags.affiliation_changed: 3, + ReasonFlags.superseded: 4, + ReasonFlags.cessation_of_operation: 5, + ReasonFlags.certificate_hold: 6, + ReasonFlags.remove_from_crl: 8, + ReasonFlags.privilege_withdrawn: 9, + ReasonFlags.aa_compromise: 10, +} + + +class PolicyConstraints(ExtensionType): + oid = ExtensionOID.POLICY_CONSTRAINTS + + def __init__( + self, + require_explicit_policy: int | None, + inhibit_policy_mapping: int | None, + ) -> None: + if require_explicit_policy is not None and not isinstance( + require_explicit_policy, int + ): + raise TypeError( + "require_explicit_policy must be a non-negative integer or " + "None" + ) + + if inhibit_policy_mapping is not None and not isinstance( + inhibit_policy_mapping, int + ): + raise TypeError( + "inhibit_policy_mapping must be a non-negative integer or None" + ) + + if inhibit_policy_mapping is None and require_explicit_policy is None: + raise ValueError( + "At least one of require_explicit_policy and " + "inhibit_policy_mapping must not be None" + ) + + self._require_explicit_policy = require_explicit_policy + self._inhibit_policy_mapping = inhibit_policy_mapping + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PolicyConstraints): + return NotImplemented + + return ( + self.require_explicit_policy == other.require_explicit_policy + and self.inhibit_policy_mapping == other.inhibit_policy_mapping + ) + + def __hash__(self) -> int: + return hash( + (self.require_explicit_policy, self.inhibit_policy_mapping) + ) + + @property + def require_explicit_policy(self) -> int | None: + return self._require_explicit_policy + + @property + def inhibit_policy_mapping(self) -> int | None: + return self._inhibit_policy_mapping + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class CertificatePolicies(ExtensionType): + oid = ExtensionOID.CERTIFICATE_POLICIES + + def __init__(self, policies: Iterable[PolicyInformation]) -> None: + policies = list(policies) + if not all(isinstance(x, PolicyInformation) for x in policies): + raise TypeError( + "Every item in the policies list must be a PolicyInformation" + ) + + self._policies = policies + + __len__, __iter__, __getitem__ = _make_sequence_methods("_policies") + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CertificatePolicies): + return NotImplemented + + return self._policies == other._policies + + def __hash__(self) -> int: + return hash(tuple(self._policies)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class PolicyInformation: + def __init__( + self, + policy_identifier: ObjectIdentifier, + policy_qualifiers: Iterable[str | UserNotice] | None, + ) -> None: + if not isinstance(policy_identifier, ObjectIdentifier): + raise TypeError("policy_identifier must be an ObjectIdentifier") + + self._policy_identifier = policy_identifier + + if policy_qualifiers is not None: + policy_qualifiers = list(policy_qualifiers) + if not all( + isinstance(x, (str, UserNotice)) for x in policy_qualifiers + ): + raise TypeError( + "policy_qualifiers must be a list of strings and/or " + "UserNotice objects or None" + ) + + self._policy_qualifiers = policy_qualifiers + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PolicyInformation): + return NotImplemented + + return ( + self.policy_identifier == other.policy_identifier + and self.policy_qualifiers == other.policy_qualifiers + ) + + def __hash__(self) -> int: + if self.policy_qualifiers is not None: + pq = tuple(self.policy_qualifiers) + else: + pq = None + + return hash((self.policy_identifier, pq)) + + @property + def policy_identifier(self) -> ObjectIdentifier: + return self._policy_identifier + + @property + def policy_qualifiers( + self, + ) -> list[str | UserNotice] | None: + return self._policy_qualifiers + + +class UserNotice: + def __init__( + self, + notice_reference: NoticeReference | None, + explicit_text: str | None, + ) -> None: + if notice_reference and not isinstance( + notice_reference, NoticeReference + ): + raise TypeError( + "notice_reference must be None or a NoticeReference" + ) + + self._notice_reference = notice_reference + self._explicit_text = explicit_text + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, UserNotice): + return NotImplemented + + return ( + self.notice_reference == other.notice_reference + and self.explicit_text == other.explicit_text + ) + + def __hash__(self) -> int: + return hash((self.notice_reference, self.explicit_text)) + + @property + def notice_reference(self) -> NoticeReference | None: + return self._notice_reference + + @property + def explicit_text(self) -> str | None: + return self._explicit_text + + +class NoticeReference: + def __init__( + self, + organization: str | None, + notice_numbers: Iterable[int], + ) -> None: + self._organization = organization + notice_numbers = list(notice_numbers) + if not all(isinstance(x, int) for x in notice_numbers): + raise TypeError("notice_numbers must be a list of integers") + + self._notice_numbers = notice_numbers + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, NoticeReference): + return NotImplemented + + return ( + self.organization == other.organization + and self.notice_numbers == other.notice_numbers + ) + + def __hash__(self) -> int: + return hash((self.organization, tuple(self.notice_numbers))) + + @property + def organization(self) -> str | None: + return self._organization + + @property + def notice_numbers(self) -> list[int]: + return self._notice_numbers + + +class ExtendedKeyUsage(ExtensionType): + oid = ExtensionOID.EXTENDED_KEY_USAGE + + def __init__(self, usages: Iterable[ObjectIdentifier]) -> None: + usages = list(usages) + if not all(isinstance(x, ObjectIdentifier) for x in usages): + raise TypeError( + "Every item in the usages list must be an ObjectIdentifier" + ) + + self._usages = usages + + __len__, __iter__, __getitem__ = _make_sequence_methods("_usages") + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ExtendedKeyUsage): + return NotImplemented + + return self._usages == other._usages + + def __hash__(self) -> int: + return hash(tuple(self._usages)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class OCSPNoCheck(ExtensionType): + oid = ExtensionOID.OCSP_NO_CHECK + + def __eq__(self, other: object) -> bool: + if not isinstance(other, OCSPNoCheck): + return NotImplemented + + return True + + def __hash__(self) -> int: + return hash(OCSPNoCheck) + + def __repr__(self) -> str: + return "" + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class PrecertPoison(ExtensionType): + oid = ExtensionOID.PRECERT_POISON + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PrecertPoison): + return NotImplemented + + return True + + def __hash__(self) -> int: + return hash(PrecertPoison) + + def __repr__(self) -> str: + return "" + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class TLSFeature(ExtensionType): + oid = ExtensionOID.TLS_FEATURE + + def __init__(self, features: Iterable[TLSFeatureType]) -> None: + features = list(features) + if ( + not all(isinstance(x, TLSFeatureType) for x in features) + or len(features) == 0 + ): + raise TypeError( + "features must be a list of elements from the TLSFeatureType " + "enum" + ) + + self._features = features + + __len__, __iter__, __getitem__ = _make_sequence_methods("_features") + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, TLSFeature): + return NotImplemented + + return self._features == other._features + + def __hash__(self) -> int: + return hash(tuple(self._features)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class TLSFeatureType(utils.Enum): + # status_request is defined in RFC 6066 and is used for what is commonly + # called OCSP Must-Staple when present in the TLS Feature extension in an + # X.509 certificate. + status_request = 5 + # status_request_v2 is defined in RFC 6961 and allows multiple OCSP + # responses to be provided. It is not currently in use by clients or + # servers. + status_request_v2 = 17 + + +_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType} + + +class InhibitAnyPolicy(ExtensionType): + oid = ExtensionOID.INHIBIT_ANY_POLICY + + def __init__(self, skip_certs: int) -> None: + if not isinstance(skip_certs, int): + raise TypeError("skip_certs must be an integer") + + if skip_certs < 0: + raise ValueError("skip_certs must be a non-negative integer") + + self._skip_certs = skip_certs + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, InhibitAnyPolicy): + return NotImplemented + + return self.skip_certs == other.skip_certs + + def __hash__(self) -> int: + return hash(self.skip_certs) + + @property + def skip_certs(self) -> int: + return self._skip_certs + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class KeyUsage(ExtensionType): + oid = ExtensionOID.KEY_USAGE + + def __init__( + self, + digital_signature: bool, + content_commitment: bool, + key_encipherment: bool, + data_encipherment: bool, + key_agreement: bool, + key_cert_sign: bool, + crl_sign: bool, + encipher_only: bool, + decipher_only: bool, + ) -> None: + if not key_agreement and (encipher_only or decipher_only): + raise ValueError( + "encipher_only and decipher_only can only be true when " + "key_agreement is true" + ) + + self._digital_signature = digital_signature + self._content_commitment = content_commitment + self._key_encipherment = key_encipherment + self._data_encipherment = data_encipherment + self._key_agreement = key_agreement + self._key_cert_sign = key_cert_sign + self._crl_sign = crl_sign + self._encipher_only = encipher_only + self._decipher_only = decipher_only + + @property + def digital_signature(self) -> bool: + return self._digital_signature + + @property + def content_commitment(self) -> bool: + return self._content_commitment + + @property + def key_encipherment(self) -> bool: + return self._key_encipherment + + @property + def data_encipherment(self) -> bool: + return self._data_encipherment + + @property + def key_agreement(self) -> bool: + return self._key_agreement + + @property + def key_cert_sign(self) -> bool: + return self._key_cert_sign + + @property + def crl_sign(self) -> bool: + return self._crl_sign + + @property + def encipher_only(self) -> bool: + if not self.key_agreement: + raise ValueError( + "encipher_only is undefined unless key_agreement is true" + ) + else: + return self._encipher_only + + @property + def decipher_only(self) -> bool: + if not self.key_agreement: + raise ValueError( + "decipher_only is undefined unless key_agreement is true" + ) + else: + return self._decipher_only + + def __repr__(self) -> str: + try: + encipher_only = self.encipher_only + decipher_only = self.decipher_only + except ValueError: + # Users found None confusing because even though encipher/decipher + # have no meaning unless key_agreement is true, to construct an + # instance of the class you still need to pass False. + encipher_only = False + decipher_only = False + + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, KeyUsage): + return NotImplemented + + return ( + self.digital_signature == other.digital_signature + and self.content_commitment == other.content_commitment + and self.key_encipherment == other.key_encipherment + and self.data_encipherment == other.data_encipherment + and self.key_agreement == other.key_agreement + and self.key_cert_sign == other.key_cert_sign + and self.crl_sign == other.crl_sign + and self._encipher_only == other._encipher_only + and self._decipher_only == other._decipher_only + ) + + def __hash__(self) -> int: + return hash( + ( + self.digital_signature, + self.content_commitment, + self.key_encipherment, + self.data_encipherment, + self.key_agreement, + self.key_cert_sign, + self.crl_sign, + self._encipher_only, + self._decipher_only, + ) + ) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class PrivateKeyUsagePeriod(ExtensionType): + oid = ExtensionOID.PRIVATE_KEY_USAGE_PERIOD + + def __init__( + self, + not_before: datetime.datetime | None, + not_after: datetime.datetime | None, + ) -> None: + if ( + not isinstance(not_before, datetime.datetime) + and not_before is not None + ): + raise TypeError("not_before must be a datetime.datetime or None") + + if ( + not isinstance(not_after, datetime.datetime) + and not_after is not None + ): + raise TypeError("not_after must be a datetime.datetime or None") + + if not_before is None and not_after is None: + raise ValueError( + "At least one of not_before and not_after must not be None" + ) + + if ( + not_before is not None + and not_after is not None + and not_before > not_after + ): + raise ValueError("not_before must be before not_after") + + self._not_before = not_before + self._not_after = not_after + + @property + def not_before(self) -> datetime.datetime | None: + return self._not_before + + @property + def not_after(self) -> datetime.datetime | None: + return self._not_after + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PrivateKeyUsagePeriod): + return NotImplemented + + return ( + self.not_before == other.not_before + and self.not_after == other.not_after + ) + + def __hash__(self) -> int: + return hash((self.not_before, self.not_after)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class NameConstraints(ExtensionType): + oid = ExtensionOID.NAME_CONSTRAINTS + + def __init__( + self, + permitted_subtrees: Iterable[GeneralName] | None, + excluded_subtrees: Iterable[GeneralName] | None, + ) -> None: + if permitted_subtrees is not None: + permitted_subtrees = list(permitted_subtrees) + if not permitted_subtrees: + raise ValueError( + "permitted_subtrees must be a non-empty list or None" + ) + if not all(isinstance(x, GeneralName) for x in permitted_subtrees): + raise TypeError( + "permitted_subtrees must be a list of GeneralName objects " + "or None" + ) + + self._validate_tree(permitted_subtrees) + + if excluded_subtrees is not None: + excluded_subtrees = list(excluded_subtrees) + if not excluded_subtrees: + raise ValueError( + "excluded_subtrees must be a non-empty list or None" + ) + if not all(isinstance(x, GeneralName) for x in excluded_subtrees): + raise TypeError( + "excluded_subtrees must be a list of GeneralName objects " + "or None" + ) + + self._validate_tree(excluded_subtrees) + + if permitted_subtrees is None and excluded_subtrees is None: + raise ValueError( + "At least one of permitted_subtrees and excluded_subtrees " + "must not be None" + ) + + self._permitted_subtrees = permitted_subtrees + self._excluded_subtrees = excluded_subtrees + + def __eq__(self, other: object) -> bool: + if not isinstance(other, NameConstraints): + return NotImplemented + + return ( + self.excluded_subtrees == other.excluded_subtrees + and self.permitted_subtrees == other.permitted_subtrees + ) + + def _validate_tree(self, tree: Iterable[GeneralName]) -> None: + self._validate_ip_name(tree) + self._validate_dns_name(tree) + + def _validate_ip_name(self, tree: Iterable[GeneralName]) -> None: + if any( + isinstance(name, IPAddress) + and not isinstance( + name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network) + ) + for name in tree + ): + raise TypeError( + "IPAddress name constraints must be an IPv4Network or" + " IPv6Network object" + ) + + def _validate_dns_name(self, tree: Iterable[GeneralName]) -> None: + if any( + isinstance(name, DNSName) and "*" in name.value for name in tree + ): + raise ValueError( + "DNSName name constraints must not contain the '*' wildcard" + " character" + ) + + def __repr__(self) -> str: + return ( + f"" + ) + + def __hash__(self) -> int: + if self.permitted_subtrees is not None: + ps: tuple[GeneralName, ...] | None = tuple(self.permitted_subtrees) + else: + ps = None + + if self.excluded_subtrees is not None: + es: tuple[GeneralName, ...] | None = tuple(self.excluded_subtrees) + else: + es = None + + return hash((ps, es)) + + @property + def permitted_subtrees( + self, + ) -> list[GeneralName] | None: + return self._permitted_subtrees + + @property + def excluded_subtrees( + self, + ) -> list[GeneralName] | None: + return self._excluded_subtrees + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class Extension(typing.Generic[ExtensionTypeVar]): + def __init__( + self, oid: ObjectIdentifier, critical: bool, value: ExtensionTypeVar + ) -> None: + if not isinstance(oid, ObjectIdentifier): + raise TypeError( + "oid argument must be an ObjectIdentifier instance." + ) + + if not isinstance(critical, bool): + raise TypeError("critical must be a boolean value") + + self._oid = oid + self._critical = critical + self._value = value + + @property + def oid(self) -> ObjectIdentifier: + return self._oid + + @property + def critical(self) -> bool: + return self._critical + + @property + def value(self) -> ExtensionTypeVar: + return self._value + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Extension): + return NotImplemented + + return ( + self.oid == other.oid + and self.critical == other.critical + and self.value == other.value + ) + + def __hash__(self) -> int: + return hash((self.oid, self.critical, self.value)) + + +class GeneralNames: + def __init__(self, general_names: Iterable[GeneralName]) -> None: + general_names = list(general_names) + if not all(isinstance(x, GeneralName) for x in general_names): + raise TypeError( + "Every item in the general_names list must be an " + "object conforming to the GeneralName interface" + ) + + self._general_names = general_names + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + @typing.overload + def get_values_for_type( + self, + type: type[DNSName] + | type[UniformResourceIdentifier] + | type[RFC822Name], + ) -> list[str]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[DirectoryName], + ) -> list[Name]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[RegisteredID], + ) -> list[ObjectIdentifier]: ... + + @typing.overload + def get_values_for_type( + self, type: type[IPAddress] + ) -> list[_IPAddressTypes]: ... + + @typing.overload + def get_values_for_type( + self, type: type[OtherName] + ) -> list[OtherName]: ... + + def get_values_for_type( + self, + type: type[DNSName] + | type[DirectoryName] + | type[IPAddress] + | type[OtherName] + | type[RFC822Name] + | type[RegisteredID] + | type[UniformResourceIdentifier], + ) -> ( + list[_IPAddressTypes] + | list[str] + | list[OtherName] + | list[Name] + | list[ObjectIdentifier] + ): + # Return the value of each GeneralName, except for OtherName instances + # which we return directly because it has two important properties not + # just one value. + objs = (i for i in self if isinstance(i, type)) + if type != OtherName: + return [i.value for i in objs] # type: ignore[return-value,unused-ignore] + return list(objs) # type: ignore[return-value,unused-ignore] + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, GeneralNames): + return NotImplemented + + return self._general_names == other._general_names + + def __hash__(self) -> int: + return hash(tuple(self._general_names)) + + +class SubjectAlternativeName(ExtensionType): + oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME + + def __init__(self, general_names: Iterable[GeneralName]) -> None: + self._general_names = GeneralNames(general_names) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + @typing.overload + def get_values_for_type( + self, + type: type[DNSName] + | type[UniformResourceIdentifier] + | type[RFC822Name], + ) -> list[str]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[DirectoryName], + ) -> list[Name]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[RegisteredID], + ) -> list[ObjectIdentifier]: ... + + @typing.overload + def get_values_for_type( + self, type: type[IPAddress] + ) -> list[_IPAddressTypes]: ... + + @typing.overload + def get_values_for_type( + self, type: type[OtherName] + ) -> list[OtherName]: ... + + def get_values_for_type( + self, + type: type[DNSName] + | type[DirectoryName] + | type[IPAddress] + | type[OtherName] + | type[RFC822Name] + | type[RegisteredID] + | type[UniformResourceIdentifier], + ) -> ( + list[_IPAddressTypes] + | list[str] + | list[OtherName] + | list[Name] + | list[ObjectIdentifier] + ): + return self._general_names.get_values_for_type(type) + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SubjectAlternativeName): + return NotImplemented + + return self._general_names == other._general_names + + def __hash__(self) -> int: + return hash(self._general_names) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class IssuerAlternativeName(ExtensionType): + oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME + + def __init__(self, general_names: Iterable[GeneralName]) -> None: + self._general_names = GeneralNames(general_names) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + @typing.overload + def get_values_for_type( + self, + type: type[DNSName] + | type[UniformResourceIdentifier] + | type[RFC822Name], + ) -> list[str]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[DirectoryName], + ) -> list[Name]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[RegisteredID], + ) -> list[ObjectIdentifier]: ... + + @typing.overload + def get_values_for_type( + self, type: type[IPAddress] + ) -> list[_IPAddressTypes]: ... + + @typing.overload + def get_values_for_type( + self, type: type[OtherName] + ) -> list[OtherName]: ... + + def get_values_for_type( + self, + type: type[DNSName] + | type[DirectoryName] + | type[IPAddress] + | type[OtherName] + | type[RFC822Name] + | type[RegisteredID] + | type[UniformResourceIdentifier], + ) -> ( + list[_IPAddressTypes] + | list[str] + | list[OtherName] + | list[Name] + | list[ObjectIdentifier] + ): + return self._general_names.get_values_for_type(type) + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, IssuerAlternativeName): + return NotImplemented + + return self._general_names == other._general_names + + def __hash__(self) -> int: + return hash(self._general_names) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class CertificateIssuer(ExtensionType): + oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER + + def __init__(self, general_names: Iterable[GeneralName]) -> None: + self._general_names = GeneralNames(general_names) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + @typing.overload + def get_values_for_type( + self, + type: type[DNSName] + | type[UniformResourceIdentifier] + | type[RFC822Name], + ) -> list[str]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[DirectoryName], + ) -> list[Name]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[RegisteredID], + ) -> list[ObjectIdentifier]: ... + + @typing.overload + def get_values_for_type( + self, type: type[IPAddress] + ) -> list[_IPAddressTypes]: ... + + @typing.overload + def get_values_for_type( + self, type: type[OtherName] + ) -> list[OtherName]: ... + + def get_values_for_type( + self, + type: type[DNSName] + | type[DirectoryName] + | type[IPAddress] + | type[OtherName] + | type[RFC822Name] + | type[RegisteredID] + | type[UniformResourceIdentifier], + ) -> ( + list[_IPAddressTypes] + | list[str] + | list[OtherName] + | list[Name] + | list[ObjectIdentifier] + ): + return self._general_names.get_values_for_type(type) + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CertificateIssuer): + return NotImplemented + + return self._general_names == other._general_names + + def __hash__(self) -> int: + return hash(self._general_names) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class CRLReason(ExtensionType): + oid = CRLEntryExtensionOID.CRL_REASON + + def __init__(self, reason: ReasonFlags) -> None: + if not isinstance(reason, ReasonFlags): + raise TypeError("reason must be an element from ReasonFlags") + + self._reason = reason + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CRLReason): + return NotImplemented + + return self.reason == other.reason + + def __hash__(self) -> int: + return hash(self.reason) + + @property + def reason(self) -> ReasonFlags: + return self._reason + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class InvalidityDate(ExtensionType): + oid = CRLEntryExtensionOID.INVALIDITY_DATE + + def __init__(self, invalidity_date: datetime.datetime) -> None: + if not isinstance(invalidity_date, datetime.datetime): + raise TypeError("invalidity_date must be a datetime.datetime") + + self._invalidity_date = invalidity_date + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, InvalidityDate): + return NotImplemented + + return self.invalidity_date == other.invalidity_date + + def __hash__(self) -> int: + return hash(self.invalidity_date) + + @property + def invalidity_date(self) -> datetime.datetime: + return self._invalidity_date + + @property + def invalidity_date_utc(self) -> datetime.datetime: + if self._invalidity_date.tzinfo is None: + return self._invalidity_date.replace(tzinfo=datetime.timezone.utc) + else: + return self._invalidity_date.astimezone(tz=datetime.timezone.utc) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class PrecertificateSignedCertificateTimestamps(ExtensionType): + oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS + + def __init__( + self, + signed_certificate_timestamps: Iterable[SignedCertificateTimestamp], + ) -> None: + signed_certificate_timestamps = list(signed_certificate_timestamps) + if not all( + isinstance(sct, SignedCertificateTimestamp) + for sct in signed_certificate_timestamps + ): + raise TypeError( + "Every item in the signed_certificate_timestamps list must be " + "a SignedCertificateTimestamp" + ) + self._signed_certificate_timestamps = signed_certificate_timestamps + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_signed_certificate_timestamps" + ) + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash(tuple(self._signed_certificate_timestamps)) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PrecertificateSignedCertificateTimestamps): + return NotImplemented + + return ( + self._signed_certificate_timestamps + == other._signed_certificate_timestamps + ) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class SignedCertificateTimestamps(ExtensionType): + oid = ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS + + def __init__( + self, + signed_certificate_timestamps: Iterable[SignedCertificateTimestamp], + ) -> None: + signed_certificate_timestamps = list(signed_certificate_timestamps) + if not all( + isinstance(sct, SignedCertificateTimestamp) + for sct in signed_certificate_timestamps + ): + raise TypeError( + "Every item in the signed_certificate_timestamps list must be " + "a SignedCertificateTimestamp" + ) + self._signed_certificate_timestamps = signed_certificate_timestamps + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_signed_certificate_timestamps" + ) + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash(tuple(self._signed_certificate_timestamps)) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SignedCertificateTimestamps): + return NotImplemented + + return ( + self._signed_certificate_timestamps + == other._signed_certificate_timestamps + ) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class OCSPNonce(ExtensionType): + oid = OCSPExtensionOID.NONCE + + def __init__(self, nonce: bytes) -> None: + if not isinstance(nonce, bytes): + raise TypeError("nonce must be bytes") + + self._nonce = nonce + + def __eq__(self, other: object) -> bool: + if not isinstance(other, OCSPNonce): + return NotImplemented + + return self.nonce == other.nonce + + def __hash__(self) -> int: + return hash(self.nonce) + + def __repr__(self) -> str: + return f"" + + @property + def nonce(self) -> bytes: + return self._nonce + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class OCSPAcceptableResponses(ExtensionType): + oid = OCSPExtensionOID.ACCEPTABLE_RESPONSES + + def __init__(self, responses: Iterable[ObjectIdentifier]) -> None: + responses = list(responses) + if any(not isinstance(r, ObjectIdentifier) for r in responses): + raise TypeError("All responses must be ObjectIdentifiers") + + self._responses = responses + + def __eq__(self, other: object) -> bool: + if not isinstance(other, OCSPAcceptableResponses): + return NotImplemented + + return self._responses == other._responses + + def __hash__(self) -> int: + return hash(tuple(self._responses)) + + def __repr__(self) -> str: + return f"" + + def __iter__(self) -> Iterator[ObjectIdentifier]: + return iter(self._responses) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class IssuingDistributionPoint(ExtensionType): + oid = ExtensionOID.ISSUING_DISTRIBUTION_POINT + + def __init__( + self, + full_name: Iterable[GeneralName] | None, + relative_name: RelativeDistinguishedName | None, + only_contains_user_certs: bool, + only_contains_ca_certs: bool, + only_some_reasons: frozenset[ReasonFlags] | None, + indirect_crl: bool, + only_contains_attribute_certs: bool, + ) -> None: + if full_name is not None: + full_name = list(full_name) + + if only_some_reasons and ( + not isinstance(only_some_reasons, frozenset) + or not all(isinstance(x, ReasonFlags) for x in only_some_reasons) + ): + raise TypeError( + "only_some_reasons must be None or frozenset of ReasonFlags" + ) + + if only_some_reasons and ( + ReasonFlags.unspecified in only_some_reasons + or ReasonFlags.remove_from_crl in only_some_reasons + ): + raise ValueError( + "unspecified and remove_from_crl are not valid reasons in an " + "IssuingDistributionPoint" + ) + + if not ( + isinstance(only_contains_user_certs, bool) + and isinstance(only_contains_ca_certs, bool) + and isinstance(indirect_crl, bool) + and isinstance(only_contains_attribute_certs, bool) + ): + raise TypeError( + "only_contains_user_certs, only_contains_ca_certs, " + "indirect_crl and only_contains_attribute_certs " + "must all be boolean." + ) + + # Per RFC5280 Section 5.2.5, the Issuing Distribution Point extension + # in a CRL can have only one of onlyContainsUserCerts, + # onlyContainsCACerts, onlyContainsAttributeCerts set to TRUE. + crl_constraints = [ + only_contains_user_certs, + only_contains_ca_certs, + only_contains_attribute_certs, + ] + + if len([x for x in crl_constraints if x]) > 1: + raise ValueError( + "Only one of the following can be set to True: " + "only_contains_user_certs, only_contains_ca_certs, " + "only_contains_attribute_certs" + ) + + if not any( + [ + only_contains_user_certs, + only_contains_ca_certs, + indirect_crl, + only_contains_attribute_certs, + full_name, + relative_name, + only_some_reasons, + ] + ): + raise ValueError( + "Cannot create empty extension: " + "if only_contains_user_certs, only_contains_ca_certs, " + "indirect_crl, and only_contains_attribute_certs are all False" + ", then either full_name, relative_name, or only_some_reasons " + "must have a value." + ) + + self._only_contains_user_certs = only_contains_user_certs + self._only_contains_ca_certs = only_contains_ca_certs + self._indirect_crl = indirect_crl + self._only_contains_attribute_certs = only_contains_attribute_certs + self._only_some_reasons = only_some_reasons + self._full_name = full_name + self._relative_name = relative_name + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, IssuingDistributionPoint): + return NotImplemented + + return ( + self.full_name == other.full_name + and self.relative_name == other.relative_name + and self.only_contains_user_certs == other.only_contains_user_certs + and self.only_contains_ca_certs == other.only_contains_ca_certs + and self.only_some_reasons == other.only_some_reasons + and self.indirect_crl == other.indirect_crl + and self.only_contains_attribute_certs + == other.only_contains_attribute_certs + ) + + def __hash__(self) -> int: + if self.full_name is not None: + full_name: tuple[GeneralName, ...] | None = tuple(self.full_name) + else: + full_name = None + + return hash( + ( + full_name, + self.relative_name, + self.only_contains_user_certs, + self.only_contains_ca_certs, + self.only_some_reasons, + self.indirect_crl, + self.only_contains_attribute_certs, + ) + ) + + @property + def full_name(self) -> list[GeneralName] | None: + return self._full_name + + @property + def relative_name(self) -> RelativeDistinguishedName | None: + return self._relative_name + + @property + def only_contains_user_certs(self) -> bool: + return self._only_contains_user_certs + + @property + def only_contains_ca_certs(self) -> bool: + return self._only_contains_ca_certs + + @property + def only_some_reasons( + self, + ) -> frozenset[ReasonFlags] | None: + return self._only_some_reasons + + @property + def indirect_crl(self) -> bool: + return self._indirect_crl + + @property + def only_contains_attribute_certs(self) -> bool: + return self._only_contains_attribute_certs + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class MSCertificateTemplate(ExtensionType): + oid = ExtensionOID.MS_CERTIFICATE_TEMPLATE + + def __init__( + self, + template_id: ObjectIdentifier, + major_version: int | None, + minor_version: int | None, + ) -> None: + if not isinstance(template_id, ObjectIdentifier): + raise TypeError("oid must be an ObjectIdentifier") + self._template_id = template_id + if ( + major_version is not None and not isinstance(major_version, int) + ) or ( + minor_version is not None and not isinstance(minor_version, int) + ): + raise TypeError( + "major_version and minor_version must be integers or None" + ) + self._major_version = major_version + self._minor_version = minor_version + + @property + def template_id(self) -> ObjectIdentifier: + return self._template_id + + @property + def major_version(self) -> int | None: + return self._major_version + + @property + def minor_version(self) -> int | None: + return self._minor_version + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, MSCertificateTemplate): + return NotImplemented + + return ( + self.template_id == other.template_id + and self.major_version == other.major_version + and self.minor_version == other.minor_version + ) + + def __hash__(self) -> int: + return hash((self.template_id, self.major_version, self.minor_version)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class NamingAuthority: + def __init__( + self, + id: ObjectIdentifier | None, + url: str | None, + text: str | None, + ) -> None: + if id is not None and not isinstance(id, ObjectIdentifier): + raise TypeError("id must be an ObjectIdentifier") + + if url is not None and not isinstance(url, str): + raise TypeError("url must be a str") + + if text is not None and not isinstance(text, str): + raise TypeError("text must be a str") + + self._id = id + self._url = url + self._text = text + + @property + def id(self) -> ObjectIdentifier | None: + return self._id + + @property + def url(self) -> str | None: + return self._url + + @property + def text(self) -> str | None: + return self._text + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, NamingAuthority): + return NotImplemented + + return ( + self.id == other.id + and self.url == other.url + and self.text == other.text + ) + + def __hash__(self) -> int: + return hash( + ( + self.id, + self.url, + self.text, + ) + ) + + +class ProfessionInfo: + def __init__( + self, + naming_authority: NamingAuthority | None, + profession_items: Iterable[str], + profession_oids: Iterable[ObjectIdentifier] | None, + registration_number: str | None, + add_profession_info: bytes | None, + ) -> None: + if naming_authority is not None and not isinstance( + naming_authority, NamingAuthority + ): + raise TypeError("naming_authority must be a NamingAuthority") + + profession_items = list(profession_items) + if not all(isinstance(item, str) for item in profession_items): + raise TypeError( + "Every item in the profession_items list must be a str" + ) + + if profession_oids is not None: + profession_oids = list(profession_oids) + if not all( + isinstance(oid, ObjectIdentifier) for oid in profession_oids + ): + raise TypeError( + "Every item in the profession_oids list must be an " + "ObjectIdentifier" + ) + + if registration_number is not None and not isinstance( + registration_number, str + ): + raise TypeError("registration_number must be a str") + + if add_profession_info is not None and not isinstance( + add_profession_info, bytes + ): + raise TypeError("add_profession_info must be bytes") + + self._naming_authority = naming_authority + self._profession_items = profession_items + self._profession_oids = profession_oids + self._registration_number = registration_number + self._add_profession_info = add_profession_info + + @property + def naming_authority(self) -> NamingAuthority | None: + return self._naming_authority + + @property + def profession_items(self) -> list[str]: + return self._profession_items + + @property + def profession_oids(self) -> list[ObjectIdentifier] | None: + return self._profession_oids + + @property + def registration_number(self) -> str | None: + return self._registration_number + + @property + def add_profession_info(self) -> bytes | None: + return self._add_profession_info + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ProfessionInfo): + return NotImplemented + + return ( + self.naming_authority == other.naming_authority + and self.profession_items == other.profession_items + and self.profession_oids == other.profession_oids + and self.registration_number == other.registration_number + and self.add_profession_info == other.add_profession_info + ) + + def __hash__(self) -> int: + if self.profession_oids is not None: + profession_oids = tuple(self.profession_oids) + else: + profession_oids = None + return hash( + ( + self.naming_authority, + tuple(self.profession_items), + profession_oids, + self.registration_number, + self.add_profession_info, + ) + ) + + +class Admission: + def __init__( + self, + admission_authority: GeneralName | None, + naming_authority: NamingAuthority | None, + profession_infos: Iterable[ProfessionInfo], + ) -> None: + if admission_authority is not None and not isinstance( + admission_authority, GeneralName + ): + raise TypeError("admission_authority must be a GeneralName") + + if naming_authority is not None and not isinstance( + naming_authority, NamingAuthority + ): + raise TypeError("naming_authority must be a NamingAuthority") + + profession_infos = list(profession_infos) + if not all( + isinstance(info, ProfessionInfo) for info in profession_infos + ): + raise TypeError( + "Every item in the profession_infos list must be a " + "ProfessionInfo" + ) + + self._admission_authority = admission_authority + self._naming_authority = naming_authority + self._profession_infos = profession_infos + + @property + def admission_authority(self) -> GeneralName | None: + return self._admission_authority + + @property + def naming_authority(self) -> NamingAuthority | None: + return self._naming_authority + + @property + def profession_infos(self) -> list[ProfessionInfo]: + return self._profession_infos + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Admission): + return NotImplemented + + return ( + self.admission_authority == other.admission_authority + and self.naming_authority == other.naming_authority + and self.profession_infos == other.profession_infos + ) + + def __hash__(self) -> int: + return hash( + ( + self.admission_authority, + self.naming_authority, + tuple(self.profession_infos), + ) + ) + + +class Admissions(ExtensionType): + oid = ExtensionOID.ADMISSIONS + + def __init__( + self, + authority: GeneralName | None, + admissions: Iterable[Admission], + ) -> None: + if authority is not None and not isinstance(authority, GeneralName): + raise TypeError("authority must be a GeneralName") + + admissions = list(admissions) + if not all( + isinstance(admission, Admission) for admission in admissions + ): + raise TypeError( + "Every item in the contents_of_admissions list must be an " + "Admission" + ) + + self._authority = authority + self._admissions = admissions + + __len__, __iter__, __getitem__ = _make_sequence_methods("_admissions") + + @property + def authority(self) -> GeneralName | None: + return self._authority + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Admissions): + return NotImplemented + + return ( + self.authority == other.authority + and self._admissions == other._admissions + ) + + def __hash__(self) -> int: + return hash((self.authority, tuple(self._admissions))) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class UnrecognizedExtension(ExtensionType): + def __init__(self, oid: ObjectIdentifier, value: bytes) -> None: + if not isinstance(oid, ObjectIdentifier): + raise TypeError("oid must be an ObjectIdentifier") + self._oid = oid + self._value = value + + @property + def oid(self) -> ObjectIdentifier: # type: ignore[override] + return self._oid + + @property + def value(self) -> bytes: + return self._value + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, UnrecognizedExtension): + return NotImplemented + + return self.oid == other.oid and self.value == other.value + + def __hash__(self) -> int: + return hash((self.oid, self.value)) + + def public_bytes(self) -> bytes: + return self.value diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/general_name.py b/venv/lib/python3.12/site-packages/cryptography/x509/general_name.py new file mode 100644 index 0000000..672f287 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/x509/general_name.py @@ -0,0 +1,281 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import ipaddress +import typing +from email.utils import parseaddr + +from cryptography.x509.name import Name +from cryptography.x509.oid import ObjectIdentifier + +_IPAddressTypes = typing.Union[ + ipaddress.IPv4Address, + ipaddress.IPv6Address, + ipaddress.IPv4Network, + ipaddress.IPv6Network, +] + + +class UnsupportedGeneralNameType(Exception): + pass + + +class GeneralName(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def value(self) -> typing.Any: + """ + Return the value of the object + """ + + +class RFC822Name(GeneralName): + def __init__(self, value: str) -> None: + if isinstance(value, str): + try: + value.encode("ascii") + except UnicodeEncodeError: + raise ValueError( + "RFC822Name values should be passed as an A-label string. " + "This means unicode characters should be encoded via " + "a library like idna." + ) + else: + raise TypeError("value must be string") + + name, address = parseaddr(value) + if name or not address: + # parseaddr has found a name (e.g. Name ) or the entire + # value is an empty string. + raise ValueError("Invalid rfc822name value") + + self._value = value + + @property + def value(self) -> str: + return self._value + + @classmethod + def _init_without_validation(cls, value: str) -> RFC822Name: + instance = cls.__new__(cls) + instance._value = value + return instance + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, RFC822Name): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class DNSName(GeneralName): + def __init__(self, value: str) -> None: + if isinstance(value, str): + try: + value.encode("ascii") + except UnicodeEncodeError: + raise ValueError( + "DNSName values should be passed as an A-label string. " + "This means unicode characters should be encoded via " + "a library like idna." + ) + else: + raise TypeError("value must be string") + + self._value = value + + @property + def value(self) -> str: + return self._value + + @classmethod + def _init_without_validation(cls, value: str) -> DNSName: + instance = cls.__new__(cls) + instance._value = value + return instance + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DNSName): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class UniformResourceIdentifier(GeneralName): + def __init__(self, value: str) -> None: + if isinstance(value, str): + try: + value.encode("ascii") + except UnicodeEncodeError: + raise ValueError( + "URI values should be passed as an A-label string. " + "This means unicode characters should be encoded via " + "a library like idna." + ) + else: + raise TypeError("value must be string") + + self._value = value + + @property + def value(self) -> str: + return self._value + + @classmethod + def _init_without_validation(cls, value: str) -> UniformResourceIdentifier: + instance = cls.__new__(cls) + instance._value = value + return instance + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, UniformResourceIdentifier): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class DirectoryName(GeneralName): + def __init__(self, value: Name) -> None: + if not isinstance(value, Name): + raise TypeError("value must be a Name") + + self._value = value + + @property + def value(self) -> Name: + return self._value + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DirectoryName): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class RegisteredID(GeneralName): + def __init__(self, value: ObjectIdentifier) -> None: + if not isinstance(value, ObjectIdentifier): + raise TypeError("value must be an ObjectIdentifier") + + self._value = value + + @property + def value(self) -> ObjectIdentifier: + return self._value + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, RegisteredID): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class IPAddress(GeneralName): + def __init__(self, value: _IPAddressTypes) -> None: + if not isinstance( + value, + ( + ipaddress.IPv4Address, + ipaddress.IPv6Address, + ipaddress.IPv4Network, + ipaddress.IPv6Network, + ), + ): + raise TypeError( + "value must be an instance of ipaddress.IPv4Address, " + "ipaddress.IPv6Address, ipaddress.IPv4Network, or " + "ipaddress.IPv6Network" + ) + + self._value = value + + @property + def value(self) -> _IPAddressTypes: + return self._value + + def _packed(self) -> bytes: + if isinstance( + self.value, (ipaddress.IPv4Address, ipaddress.IPv6Address) + ): + return self.value.packed + else: + return ( + self.value.network_address.packed + self.value.netmask.packed + ) + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, IPAddress): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class OtherName(GeneralName): + def __init__(self, type_id: ObjectIdentifier, value: bytes) -> None: + if not isinstance(type_id, ObjectIdentifier): + raise TypeError("type_id must be an ObjectIdentifier") + if not isinstance(value, bytes): + raise TypeError("value must be a binary string") + + self._type_id = type_id + self._value = value + + @property + def type_id(self) -> ObjectIdentifier: + return self._type_id + + @property + def value(self) -> bytes: + return self._value + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, OtherName): + return NotImplemented + + return self.type_id == other.type_id and self.value == other.value + + def __hash__(self) -> int: + return hash((self.type_id, self.value)) diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/name.py b/venv/lib/python3.12/site-packages/cryptography/x509/name.py new file mode 100644 index 0000000..2e54edc --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/x509/name.py @@ -0,0 +1,489 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import binascii +import re +import sys +import typing +import warnings +from collections.abc import Iterable, Iterator + +from cryptography import utils +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.x509.oid import NameOID, ObjectIdentifier + + +class _ASN1Type(utils.Enum): + BitString = 3 + OctetString = 4 + UTF8String = 12 + NumericString = 18 + PrintableString = 19 + T61String = 20 + IA5String = 22 + UTCTime = 23 + GeneralizedTime = 24 + VisibleString = 26 + UniversalString = 28 + BMPString = 30 + + +_ASN1_TYPE_TO_ENUM = {i.value: i for i in _ASN1Type} +_NAMEOID_DEFAULT_TYPE: dict[ObjectIdentifier, _ASN1Type] = { + NameOID.COUNTRY_NAME: _ASN1Type.PrintableString, + NameOID.JURISDICTION_COUNTRY_NAME: _ASN1Type.PrintableString, + NameOID.SERIAL_NUMBER: _ASN1Type.PrintableString, + NameOID.DN_QUALIFIER: _ASN1Type.PrintableString, + NameOID.EMAIL_ADDRESS: _ASN1Type.IA5String, + NameOID.DOMAIN_COMPONENT: _ASN1Type.IA5String, +} + +# Type alias +_OidNameMap = typing.Mapping[ObjectIdentifier, str] +_NameOidMap = typing.Mapping[str, ObjectIdentifier] + +#: Short attribute names from RFC 4514: +#: https://tools.ietf.org/html/rfc4514#page-7 +_NAMEOID_TO_NAME: _OidNameMap = { + NameOID.COMMON_NAME: "CN", + NameOID.LOCALITY_NAME: "L", + NameOID.STATE_OR_PROVINCE_NAME: "ST", + NameOID.ORGANIZATION_NAME: "O", + NameOID.ORGANIZATIONAL_UNIT_NAME: "OU", + NameOID.COUNTRY_NAME: "C", + NameOID.STREET_ADDRESS: "STREET", + NameOID.DOMAIN_COMPONENT: "DC", + NameOID.USER_ID: "UID", +} +_NAME_TO_NAMEOID = {v: k for k, v in _NAMEOID_TO_NAME.items()} + +_NAMEOID_LENGTH_LIMIT = { + NameOID.COUNTRY_NAME: (2, 2), + NameOID.JURISDICTION_COUNTRY_NAME: (2, 2), + NameOID.COMMON_NAME: (1, 64), +} + + +def _escape_dn_value(val: str | bytes) -> str: + """Escape special characters in RFC4514 Distinguished Name value.""" + + if not val: + return "" + + # RFC 4514 Section 2.4 defines the value as being the # (U+0023) character + # followed by the hexadecimal encoding of the octets. + if isinstance(val, bytes): + return "#" + binascii.hexlify(val).decode("utf8") + + # See https://tools.ietf.org/html/rfc4514#section-2.4 + val = val.replace("\\", "\\\\") + val = val.replace('"', '\\"') + val = val.replace("+", "\\+") + val = val.replace(",", "\\,") + val = val.replace(";", "\\;") + val = val.replace("<", "\\<") + val = val.replace(">", "\\>") + val = val.replace("\0", "\\00") + + if val[0] == "#" or (val[0] == " " and len(val) > 1): + val = "\\" + val + if val[-1] == " ": + val = val[:-1] + "\\ " + + return val + + +def _unescape_dn_value(val: str) -> str: + if not val: + return "" + + # See https://tools.ietf.org/html/rfc4514#section-3 + + # special = escaped / SPACE / SHARP / EQUALS + # escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE + def sub(m): + val = m.group(0) + # Special character escape + if len(val) == 2: + return val[1:] + + # Unicode string of hex + return binascii.unhexlify(val.replace("\\", "")).decode() + + return _RFC4514NameParser._PAIR_MULTI_RE.sub(sub, val) + + +NameAttributeValueType = typing.TypeVar( + "NameAttributeValueType", + typing.Union[str, bytes], + str, + bytes, + covariant=True, +) + + +class NameAttribute(typing.Generic[NameAttributeValueType]): + def __init__( + self, + oid: ObjectIdentifier, + value: NameAttributeValueType, + _type: _ASN1Type | None = None, + *, + _validate: bool = True, + ) -> None: + if not isinstance(oid, ObjectIdentifier): + raise TypeError( + "oid argument must be an ObjectIdentifier instance." + ) + if _type == _ASN1Type.BitString: + if oid != NameOID.X500_UNIQUE_IDENTIFIER: + raise TypeError( + "oid must be X500_UNIQUE_IDENTIFIER for BitString type." + ) + if not isinstance(value, bytes): + raise TypeError("value must be bytes for BitString") + elif not isinstance(value, str): + raise TypeError("value argument must be a str") + + length_limits = _NAMEOID_LENGTH_LIMIT.get(oid) + if length_limits is not None: + min_length, max_length = length_limits + assert isinstance(value, str) + c_len = len(value.encode("utf8")) + if c_len < min_length or c_len > max_length: + msg = ( + f"Attribute's length must be >= {min_length} and " + f"<= {max_length}, but it was {c_len}" + ) + if _validate is True: + raise ValueError(msg) + else: + warnings.warn(msg, stacklevel=2) + + # The appropriate ASN1 string type varies by OID and is defined across + # multiple RFCs including 2459, 3280, and 5280. In general UTF8String + # is preferred (2459), but 3280 and 5280 specify several OIDs with + # alternate types. This means when we see the sentinel value we need + # to look up whether the OID has a non-UTF8 type. If it does, set it + # to that. Otherwise, UTF8! + if _type is None: + _type = _NAMEOID_DEFAULT_TYPE.get(oid, _ASN1Type.UTF8String) + + if not isinstance(_type, _ASN1Type): + raise TypeError("_type must be from the _ASN1Type enum") + + self._oid = oid + self._value: NameAttributeValueType = value + self._type: _ASN1Type = _type + + @property + def oid(self) -> ObjectIdentifier: + return self._oid + + @property + def value(self) -> NameAttributeValueType: + return self._value + + @property + def rfc4514_attribute_name(self) -> str: + """ + The short attribute name (for example "CN") if available, + otherwise the OID dotted string. + """ + return _NAMEOID_TO_NAME.get(self.oid, self.oid.dotted_string) + + def rfc4514_string( + self, attr_name_overrides: _OidNameMap | None = None + ) -> str: + """ + Format as RFC4514 Distinguished Name string. + + Use short attribute name if available, otherwise fall back to OID + dotted string. + """ + attr_name = ( + attr_name_overrides.get(self.oid) if attr_name_overrides else None + ) + if attr_name is None: + attr_name = self.rfc4514_attribute_name + + return f"{attr_name}={_escape_dn_value(self.value)}" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, NameAttribute): + return NotImplemented + + return self.oid == other.oid and self.value == other.value + + def __hash__(self) -> int: + return hash((self.oid, self.value)) + + def __repr__(self) -> str: + return f"" + + +class RelativeDistinguishedName: + def __init__(self, attributes: Iterable[NameAttribute[str | bytes]]): + attributes = list(attributes) + if not attributes: + raise ValueError("a relative distinguished name cannot be empty") + if not all(isinstance(x, NameAttribute) for x in attributes): + raise TypeError("attributes must be an iterable of NameAttribute") + + # Keep list and frozenset to preserve attribute order where it matters + self._attributes = attributes + self._attribute_set = frozenset(attributes) + + if len(self._attribute_set) != len(attributes): + raise ValueError("duplicate attributes are not allowed") + + def get_attributes_for_oid( + self, + oid: ObjectIdentifier, + ) -> list[NameAttribute[str | bytes]]: + return [i for i in self if i.oid == oid] + + def rfc4514_string( + self, attr_name_overrides: _OidNameMap | None = None + ) -> str: + """ + Format as RFC4514 Distinguished Name string. + + Within each RDN, attributes are joined by '+', although that is rarely + used in certificates. + """ + return "+".join( + attr.rfc4514_string(attr_name_overrides) + for attr in self._attributes + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, RelativeDistinguishedName): + return NotImplemented + + return self._attribute_set == other._attribute_set + + def __hash__(self) -> int: + return hash(self._attribute_set) + + def __iter__(self) -> Iterator[NameAttribute[str | bytes]]: + return iter(self._attributes) + + def __len__(self) -> int: + return len(self._attributes) + + def __repr__(self) -> str: + return f"" + + +class Name: + @typing.overload + def __init__( + self, attributes: Iterable[NameAttribute[str | bytes]] + ) -> None: ... + + @typing.overload + def __init__( + self, attributes: Iterable[RelativeDistinguishedName] + ) -> None: ... + + def __init__( + self, + attributes: Iterable[ + NameAttribute[str | bytes] | RelativeDistinguishedName + ], + ) -> None: + attributes = list(attributes) + if all(isinstance(x, NameAttribute) for x in attributes): + self._attributes = [ + RelativeDistinguishedName([typing.cast(NameAttribute, x)]) + for x in attributes + ] + elif all(isinstance(x, RelativeDistinguishedName) for x in attributes): + self._attributes = typing.cast( + list[RelativeDistinguishedName], attributes + ) + else: + raise TypeError( + "attributes must be a list of NameAttribute" + " or a list RelativeDistinguishedName" + ) + + @classmethod + def from_rfc4514_string( + cls, + data: str, + attr_name_overrides: _NameOidMap | None = None, + ) -> Name: + return _RFC4514NameParser(data, attr_name_overrides or {}).parse() + + @classmethod + def from_bytes(cls, data: bytes) -> Name: + """ + Parse a DER encoded X.509 name (the inverse of ``public_bytes``). + """ + return rust_x509.parse_name_bytes(data) + + def rfc4514_string( + self, attr_name_overrides: _OidNameMap | None = None + ) -> str: + """ + Format as RFC4514 Distinguished Name string. + For example 'CN=foobar.com,O=Foo Corp,C=US' + + An X.509 name is a two-level structure: a list of sets of attributes. + Each list element is separated by ',' and within each list element, set + elements are separated by '+'. The latter is almost never used in + real world certificates. According to RFC4514 section 2.1 the + RDNSequence must be reversed when converting to string representation. + """ + return ",".join( + attr.rfc4514_string(attr_name_overrides) + for attr in reversed(self._attributes) + ) + + def get_attributes_for_oid( + self, + oid: ObjectIdentifier, + ) -> list[NameAttribute[str | bytes]]: + return [i for i in self if i.oid == oid] + + @property + def rdns(self) -> list[RelativeDistinguishedName]: + return self._attributes + + def public_bytes(self, backend: typing.Any = None) -> bytes: + return rust_x509.encode_name_bytes(self) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Name): + return NotImplemented + + return self._attributes == other._attributes + + def __hash__(self) -> int: + # TODO: this is relatively expensive, if this looks like a bottleneck + # for you, consider optimizing! + return hash(tuple(self._attributes)) + + def __iter__(self) -> Iterator[NameAttribute[str | bytes]]: + for rdn in self._attributes: + yield from rdn + + def __len__(self) -> int: + return sum(len(rdn) for rdn in self._attributes) + + def __repr__(self) -> str: + return f"" + + +class _RFC4514NameParser: + _OID_RE = re.compile(r"(0|([1-9]\d*))(\.(0|([1-9]\d*)))+") + _DESCR_RE = re.compile(r"[a-zA-Z][a-zA-Z\d-]*") + + _ESCAPE_SPECIAL = r"[\\ #=\"\+,;<>]" + _ESCAPE_HEX = r"[\da-zA-Z]{2}" + _PAIR = rf"\\({_ESCAPE_SPECIAL}|{_ESCAPE_HEX})" + _PAIR_MULTI_RE = re.compile(rf"(\\{_ESCAPE_SPECIAL})|((\\{_ESCAPE_HEX})+)") + _LUTF1 = r"[\x01-\x1f\x21\x24-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" + _SUTF1 = r"[\x01-\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" + _TUTF1 = r"[\x01-\x1F\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" + _UTFMB = rf"[\x80-{chr(sys.maxunicode)}]" + _LEADCHAR = rf"{_LUTF1}|{_UTFMB}" + _STRINGCHAR = rf"{_SUTF1}|{_UTFMB}" + _TRAILCHAR = rf"{_TUTF1}|{_UTFMB}" + _STRING_RE = re.compile( + rf""" + ( + ({_LEADCHAR}|{_PAIR}) + ( + ({_STRINGCHAR}|{_PAIR})* + ({_TRAILCHAR}|{_PAIR}) + )? + )? + """, + re.VERBOSE, + ) + _HEXSTRING_RE = re.compile(r"#([\da-zA-Z]{2})+") + + def __init__(self, data: str, attr_name_overrides: _NameOidMap) -> None: + self._data = data + self._idx = 0 + + self._attr_name_overrides = attr_name_overrides + + def _has_data(self) -> bool: + return self._idx < len(self._data) + + def _peek(self) -> str | None: + if self._has_data(): + return self._data[self._idx] + return None + + def _read_char(self, ch: str) -> None: + if self._peek() != ch: + raise ValueError + self._idx += 1 + + def _read_re(self, pat) -> str: + match = pat.match(self._data, pos=self._idx) + if match is None: + raise ValueError + val = match.group() + self._idx += len(val) + return val + + def parse(self) -> Name: + """ + Parses the `data` string and converts it to a Name. + + According to RFC4514 section 2.1 the RDNSequence must be + reversed when converting to string representation. So, when + we parse it, we need to reverse again to get the RDNs on the + correct order. + """ + + if not self._has_data(): + return Name([]) + + rdns = [self._parse_rdn()] + + while self._has_data(): + self._read_char(",") + rdns.append(self._parse_rdn()) + + return Name(reversed(rdns)) + + def _parse_rdn(self) -> RelativeDistinguishedName: + nas = [self._parse_na()] + while self._peek() == "+": + self._read_char("+") + nas.append(self._parse_na()) + + return RelativeDistinguishedName(nas) + + def _parse_na(self) -> NameAttribute[str]: + try: + oid_value = self._read_re(self._OID_RE) + except ValueError: + name = self._read_re(self._DESCR_RE) + oid = self._attr_name_overrides.get( + name, _NAME_TO_NAMEOID.get(name) + ) + if oid is None: + raise ValueError + else: + oid = ObjectIdentifier(oid_value) + + self._read_char("=") + if self._peek() == "#": + value = self._read_re(self._HEXSTRING_RE) + value = binascii.unhexlify(value[1:]).decode() + else: + raw_value = self._read_re(self._STRING_RE) + value = _unescape_dn_value(raw_value) + + return NameAttribute(oid, value) diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/ocsp.py b/venv/lib/python3.12/site-packages/cryptography/x509/ocsp.py new file mode 100644 index 0000000..f61ed80 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/x509/ocsp.py @@ -0,0 +1,379 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import datetime +from collections.abc import Iterable + +from cryptography import utils, x509 +from cryptography.hazmat.bindings._rust import ocsp +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric.types import ( + CertificateIssuerPrivateKeyTypes, +) +from cryptography.x509.base import _reject_duplicate_extension + + +class OCSPResponderEncoding(utils.Enum): + HASH = "By Hash" + NAME = "By Name" + + +class OCSPResponseStatus(utils.Enum): + SUCCESSFUL = 0 + MALFORMED_REQUEST = 1 + INTERNAL_ERROR = 2 + TRY_LATER = 3 + SIG_REQUIRED = 5 + UNAUTHORIZED = 6 + + +_ALLOWED_HASHES = ( + hashes.SHA1, + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, +) + + +def _verify_algorithm(algorithm: hashes.HashAlgorithm) -> None: + if not isinstance(algorithm, _ALLOWED_HASHES): + raise ValueError( + "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512" + ) + + +class OCSPCertStatus(utils.Enum): + GOOD = 0 + REVOKED = 1 + UNKNOWN = 2 + + +class _SingleResponse: + def __init__( + self, + resp: tuple[x509.Certificate, x509.Certificate] | None, + resp_hash: tuple[bytes, bytes, int] | None, + algorithm: hashes.HashAlgorithm, + cert_status: OCSPCertStatus, + this_update: datetime.datetime, + next_update: datetime.datetime | None, + revocation_time: datetime.datetime | None, + revocation_reason: x509.ReasonFlags | None, + ): + _verify_algorithm(algorithm) + if not isinstance(this_update, datetime.datetime): + raise TypeError("this_update must be a datetime object") + if next_update is not None and not isinstance( + next_update, datetime.datetime + ): + raise TypeError("next_update must be a datetime object or None") + + self._resp = resp + self._resp_hash = resp_hash + self._algorithm = algorithm + self._this_update = this_update + self._next_update = next_update + + if not isinstance(cert_status, OCSPCertStatus): + raise TypeError( + "cert_status must be an item from the OCSPCertStatus enum" + ) + if cert_status is not OCSPCertStatus.REVOKED: + if revocation_time is not None: + raise ValueError( + "revocation_time can only be provided if the certificate " + "is revoked" + ) + if revocation_reason is not None: + raise ValueError( + "revocation_reason can only be provided if the certificate" + " is revoked" + ) + else: + if not isinstance(revocation_time, datetime.datetime): + raise TypeError("revocation_time must be a datetime object") + + if revocation_reason is not None and not isinstance( + revocation_reason, x509.ReasonFlags + ): + raise TypeError( + "revocation_reason must be an item from the ReasonFlags " + "enum or None" + ) + + self._cert_status = cert_status + self._revocation_time = revocation_time + self._revocation_reason = revocation_reason + + +OCSPRequest = ocsp.OCSPRequest +OCSPResponse = ocsp.OCSPResponse +OCSPSingleResponse = ocsp.OCSPSingleResponse + + +class OCSPRequestBuilder: + def __init__( + self, + request: tuple[ + x509.Certificate, x509.Certificate, hashes.HashAlgorithm + ] + | None = None, + request_hash: tuple[bytes, bytes, int, hashes.HashAlgorithm] + | None = None, + extensions: list[x509.Extension[x509.ExtensionType]] = [], + ) -> None: + self._request = request + self._request_hash = request_hash + self._extensions = extensions + + def add_certificate( + self, + cert: x509.Certificate, + issuer: x509.Certificate, + algorithm: hashes.HashAlgorithm, + ) -> OCSPRequestBuilder: + if self._request is not None or self._request_hash is not None: + raise ValueError("Only one certificate can be added to a request") + + _verify_algorithm(algorithm) + if not isinstance(cert, x509.Certificate) or not isinstance( + issuer, x509.Certificate + ): + raise TypeError("cert and issuer must be a Certificate") + + return OCSPRequestBuilder( + (cert, issuer, algorithm), self._request_hash, self._extensions + ) + + def add_certificate_by_hash( + self, + issuer_name_hash: bytes, + issuer_key_hash: bytes, + serial_number: int, + algorithm: hashes.HashAlgorithm, + ) -> OCSPRequestBuilder: + if self._request is not None or self._request_hash is not None: + raise ValueError("Only one certificate can be added to a request") + + if not isinstance(serial_number, int): + raise TypeError("serial_number must be an integer") + + _verify_algorithm(algorithm) + utils._check_bytes("issuer_name_hash", issuer_name_hash) + utils._check_bytes("issuer_key_hash", issuer_key_hash) + if algorithm.digest_size != len( + issuer_name_hash + ) or algorithm.digest_size != len(issuer_key_hash): + raise ValueError( + "issuer_name_hash and issuer_key_hash must be the same length " + "as the digest size of the algorithm" + ) + + return OCSPRequestBuilder( + self._request, + (issuer_name_hash, issuer_key_hash, serial_number, algorithm), + self._extensions, + ) + + def add_extension( + self, extval: x509.ExtensionType, critical: bool + ) -> OCSPRequestBuilder: + if not isinstance(extval, x509.ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = x509.Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + + return OCSPRequestBuilder( + self._request, self._request_hash, [*self._extensions, extension] + ) + + def build(self) -> OCSPRequest: + if self._request is None and self._request_hash is None: + raise ValueError("You must add a certificate before building") + + return ocsp.create_ocsp_request(self) + + +class OCSPResponseBuilder: + def __init__( + self, + response: _SingleResponse | None = None, + responder_id: tuple[x509.Certificate, OCSPResponderEncoding] + | None = None, + certs: list[x509.Certificate] | None = None, + extensions: list[x509.Extension[x509.ExtensionType]] = [], + ): + self._response = response + self._responder_id = responder_id + self._certs = certs + self._extensions = extensions + + def add_response( + self, + cert: x509.Certificate, + issuer: x509.Certificate, + algorithm: hashes.HashAlgorithm, + cert_status: OCSPCertStatus, + this_update: datetime.datetime, + next_update: datetime.datetime | None, + revocation_time: datetime.datetime | None, + revocation_reason: x509.ReasonFlags | None, + ) -> OCSPResponseBuilder: + if self._response is not None: + raise ValueError("Only one response per OCSPResponse.") + + if not isinstance(cert, x509.Certificate) or not isinstance( + issuer, x509.Certificate + ): + raise TypeError("cert and issuer must be a Certificate") + + singleresp = _SingleResponse( + (cert, issuer), + None, + algorithm, + cert_status, + this_update, + next_update, + revocation_time, + revocation_reason, + ) + return OCSPResponseBuilder( + singleresp, + self._responder_id, + self._certs, + self._extensions, + ) + + def add_response_by_hash( + self, + issuer_name_hash: bytes, + issuer_key_hash: bytes, + serial_number: int, + algorithm: hashes.HashAlgorithm, + cert_status: OCSPCertStatus, + this_update: datetime.datetime, + next_update: datetime.datetime | None, + revocation_time: datetime.datetime | None, + revocation_reason: x509.ReasonFlags | None, + ) -> OCSPResponseBuilder: + if self._response is not None: + raise ValueError("Only one response per OCSPResponse.") + + if not isinstance(serial_number, int): + raise TypeError("serial_number must be an integer") + + utils._check_bytes("issuer_name_hash", issuer_name_hash) + utils._check_bytes("issuer_key_hash", issuer_key_hash) + _verify_algorithm(algorithm) + if algorithm.digest_size != len( + issuer_name_hash + ) or algorithm.digest_size != len(issuer_key_hash): + raise ValueError( + "issuer_name_hash and issuer_key_hash must be the same length " + "as the digest size of the algorithm" + ) + + singleresp = _SingleResponse( + None, + (issuer_name_hash, issuer_key_hash, serial_number), + algorithm, + cert_status, + this_update, + next_update, + revocation_time, + revocation_reason, + ) + return OCSPResponseBuilder( + singleresp, + self._responder_id, + self._certs, + self._extensions, + ) + + def responder_id( + self, encoding: OCSPResponderEncoding, responder_cert: x509.Certificate + ) -> OCSPResponseBuilder: + if self._responder_id is not None: + raise ValueError("responder_id can only be set once") + if not isinstance(responder_cert, x509.Certificate): + raise TypeError("responder_cert must be a Certificate") + if not isinstance(encoding, OCSPResponderEncoding): + raise TypeError( + "encoding must be an element from OCSPResponderEncoding" + ) + + return OCSPResponseBuilder( + self._response, + (responder_cert, encoding), + self._certs, + self._extensions, + ) + + def certificates( + self, certs: Iterable[x509.Certificate] + ) -> OCSPResponseBuilder: + if self._certs is not None: + raise ValueError("certificates may only be set once") + certs = list(certs) + if len(certs) == 0: + raise ValueError("certs must not be an empty list") + if not all(isinstance(x, x509.Certificate) for x in certs): + raise TypeError("certs must be a list of Certificates") + return OCSPResponseBuilder( + self._response, + self._responder_id, + certs, + self._extensions, + ) + + def add_extension( + self, extval: x509.ExtensionType, critical: bool + ) -> OCSPResponseBuilder: + if not isinstance(extval, x509.ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = x509.Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + + return OCSPResponseBuilder( + self._response, + self._responder_id, + self._certs, + [*self._extensions, extension], + ) + + def sign( + self, + private_key: CertificateIssuerPrivateKeyTypes, + algorithm: hashes.HashAlgorithm | None, + ) -> OCSPResponse: + if self._response is None: + raise ValueError("You must add a response before signing") + if self._responder_id is None: + raise ValueError("You must add a responder_id before signing") + + return ocsp.create_ocsp_response( + OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm + ) + + @classmethod + def build_unsuccessful( + cls, response_status: OCSPResponseStatus + ) -> OCSPResponse: + if not isinstance(response_status, OCSPResponseStatus): + raise TypeError( + "response_status must be an item from OCSPResponseStatus" + ) + if response_status is OCSPResponseStatus.SUCCESSFUL: + raise ValueError("response_status cannot be SUCCESSFUL") + + return ocsp.create_ocsp_response(response_status, None, None, None) + + +load_der_ocsp_request = ocsp.load_der_ocsp_request +load_der_ocsp_response = ocsp.load_der_ocsp_response diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/oid.py b/venv/lib/python3.12/site-packages/cryptography/x509/oid.py new file mode 100644 index 0000000..520fc7a --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/x509/oid.py @@ -0,0 +1,37 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat._oid import ( + AttributeOID, + AuthorityInformationAccessOID, + CertificatePoliciesOID, + CRLEntryExtensionOID, + ExtendedKeyUsageOID, + ExtensionOID, + NameOID, + ObjectIdentifier, + OCSPExtensionOID, + OtherNameFormOID, + PublicKeyAlgorithmOID, + SignatureAlgorithmOID, + SubjectInformationAccessOID, +) + +__all__ = [ + "AttributeOID", + "AuthorityInformationAccessOID", + "CRLEntryExtensionOID", + "CertificatePoliciesOID", + "ExtendedKeyUsageOID", + "ExtensionOID", + "NameOID", + "OCSPExtensionOID", + "ObjectIdentifier", + "OtherNameFormOID", + "PublicKeyAlgorithmOID", + "SignatureAlgorithmOID", + "SubjectInformationAccessOID", +] diff --git a/venv/lib/python3.12/site-packages/cryptography/x509/verification.py b/venv/lib/python3.12/site-packages/cryptography/x509/verification.py new file mode 100644 index 0000000..2db4324 --- /dev/null +++ b/venv/lib/python3.12/site-packages/cryptography/x509/verification.py @@ -0,0 +1,34 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.x509.general_name import DNSName, IPAddress + +__all__ = [ + "ClientVerifier", + "Criticality", + "ExtensionPolicy", + "Policy", + "PolicyBuilder", + "ServerVerifier", + "Store", + "Subject", + "VerificationError", + "VerifiedClient", +] + +Store = rust_x509.Store +Subject = typing.Union[DNSName, IPAddress] +VerifiedClient = rust_x509.VerifiedClient +ClientVerifier = rust_x509.ClientVerifier +ServerVerifier = rust_x509.ServerVerifier +PolicyBuilder = rust_x509.PolicyBuilder +Policy = rust_x509.Policy +ExtensionPolicy = rust_x509.ExtensionPolicy +Criticality = rust_x509.Criticality +VerificationError = rust_x509.VerificationError diff --git a/venv/lib/python3.12/site-packages/dateutil/__init__.py b/venv/lib/python3.12/site-packages/dateutil/__init__.py new file mode 100644 index 0000000..a2c19c0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +import sys + +try: + from ._version import version as __version__ +except ImportError: + __version__ = 'unknown' + +__all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz', + 'utils', 'zoneinfo'] + +def __getattr__(name): + import importlib + + if name in __all__: + return importlib.import_module("." + name, __name__) + raise AttributeError( + "module {!r} has not attribute {!r}".format(__name__, name) + ) + + +def __dir__(): + # __dir__ should include all the lazy-importable modules as well. + return [x for x in globals() if x not in sys.modules] + __all__ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7168da5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/_common.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/_common.cpython-312.pyc new file mode 100644 index 0000000..cbe234b Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/_common.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/_version.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/_version.cpython-312.pyc new file mode 100644 index 0000000..a33f79b Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/_version.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/easter.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/easter.cpython-312.pyc new file mode 100644 index 0000000..e77b3e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/easter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/relativedelta.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/relativedelta.cpython-312.pyc new file mode 100644 index 0000000..e17e344 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/relativedelta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/rrule.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/rrule.cpython-312.pyc new file mode 100644 index 0000000..39438a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/rrule.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/tzwin.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/tzwin.cpython-312.pyc new file mode 100644 index 0000000..ebb0f99 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/tzwin.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..0ca25b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/_common.py b/venv/lib/python3.12/site-packages/dateutil/_common.py new file mode 100644 index 0000000..4eb2659 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/_common.py @@ -0,0 +1,43 @@ +""" +Common code used in multiple modules. +""" + + +class weekday(object): + __slots__ = ["weekday", "n"] + + def __init__(self, weekday, n=None): + self.weekday = weekday + self.n = n + + def __call__(self, n): + if n == self.n: + return self + else: + return self.__class__(self.weekday, n) + + def __eq__(self, other): + try: + if self.weekday != other.weekday or self.n != other.n: + return False + except AttributeError: + return False + return True + + def __hash__(self): + return hash(( + self.weekday, + self.n, + )) + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday] + if not self.n: + return s + else: + return "%s(%+d)" % (s, self.n) + +# vim:ts=4:sw=4:et diff --git a/venv/lib/python3.12/site-packages/dateutil/_version.py b/venv/lib/python3.12/site-packages/dateutil/_version.py new file mode 100644 index 0000000..ddda980 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/_version.py @@ -0,0 +1,4 @@ +# file generated by setuptools_scm +# don't change, don't track in version control +__version__ = version = '2.9.0.post0' +__version_tuple__ = version_tuple = (2, 9, 0) diff --git a/venv/lib/python3.12/site-packages/dateutil/easter.py b/venv/lib/python3.12/site-packages/dateutil/easter.py new file mode 100644 index 0000000..f74d1f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/easter.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +""" +This module offers a generic Easter computing method for any given year, using +Western, Orthodox or Julian algorithms. +""" + +import datetime + +__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"] + +EASTER_JULIAN = 1 +EASTER_ORTHODOX = 2 +EASTER_WESTERN = 3 + + +def easter(year, method=EASTER_WESTERN): + """ + This method was ported from the work done by GM Arts, + on top of the algorithm by Claus Tondering, which was + based in part on the algorithm of Ouding (1940), as + quoted in "Explanatory Supplement to the Astronomical + Almanac", P. Kenneth Seidelmann, editor. + + This algorithm implements three different Easter + calculation methods: + + 1. Original calculation in Julian calendar, valid in + dates after 326 AD + 2. Original method, with date converted to Gregorian + calendar, valid in years 1583 to 4099 + 3. Revised method, in Gregorian calendar, valid in + years 1583 to 4099 as well + + These methods are represented by the constants: + + * ``EASTER_JULIAN = 1`` + * ``EASTER_ORTHODOX = 2`` + * ``EASTER_WESTERN = 3`` + + The default method is method 3. + + More about the algorithm may be found at: + + `GM Arts: Easter Algorithms `_ + + and + + `The Calendar FAQ: Easter `_ + + """ + + if not (1 <= method <= 3): + raise ValueError("invalid method") + + # g - Golden year - 1 + # c - Century + # h - (23 - Epact) mod 30 + # i - Number of days from March 21 to Paschal Full Moon + # j - Weekday for PFM (0=Sunday, etc) + # p - Number of days from March 21 to Sunday on or before PFM + # (-6 to 28 methods 1 & 3, to 56 for method 2) + # e - Extra days to add for method 2 (converting Julian + # date to Gregorian date) + + y = year + g = y % 19 + e = 0 + if method < 3: + # Old method + i = (19*g + 15) % 30 + j = (y + y//4 + i) % 7 + if method == 2: + # Extra dates to convert Julian to Gregorian date + e = 10 + if y > 1600: + e = e + y//100 - 16 - (y//100 - 16)//4 + else: + # New method + c = y//100 + h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30 + i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11)) + j = (y + y//4 + i + 2 - c + c//4) % 7 + + # p can be from -6 to 56 corresponding to dates 22 March to 23 May + # (later dates apply to method 2, although 23 May never actually occurs) + p = i - j + e + d = 1 + (p + 27 + (p + 6)//40) % 31 + m = 3 + (p + 26)//30 + return datetime.date(int(y), int(m), int(d)) diff --git a/venv/lib/python3.12/site-packages/dateutil/parser/__init__.py b/venv/lib/python3.12/site-packages/dateutil/parser/__init__.py new file mode 100644 index 0000000..d174b0e --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/parser/__init__.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +from ._parser import parse, parser, parserinfo, ParserError +from ._parser import DEFAULTPARSER, DEFAULTTZPARSER +from ._parser import UnknownTimezoneWarning + +from ._parser import __doc__ + +from .isoparser import isoparser, isoparse + +__all__ = ['parse', 'parser', 'parserinfo', + 'isoparse', 'isoparser', + 'ParserError', + 'UnknownTimezoneWarning'] + + +### +# Deprecate portions of the private interface so that downstream code that +# is improperly relying on it is given *some* notice. + + +def __deprecated_private_func(f): + from functools import wraps + import warnings + + msg = ('{name} is a private function and may break without warning, ' + 'it will be moved and or renamed in future versions.') + msg = msg.format(name=f.__name__) + + @wraps(f) + def deprecated_func(*args, **kwargs): + warnings.warn(msg, DeprecationWarning) + return f(*args, **kwargs) + + return deprecated_func + +def __deprecate_private_class(c): + import warnings + + msg = ('{name} is a private class and may break without warning, ' + 'it will be moved and or renamed in future versions.') + msg = msg.format(name=c.__name__) + + class private_class(c): + __doc__ = c.__doc__ + + def __init__(self, *args, **kwargs): + warnings.warn(msg, DeprecationWarning) + super(private_class, self).__init__(*args, **kwargs) + + private_class.__name__ = c.__name__ + + return private_class + + +from ._parser import _timelex, _resultbase +from ._parser import _tzparser, _parsetz + +_timelex = __deprecate_private_class(_timelex) +_tzparser = __deprecate_private_class(_tzparser) +_resultbase = __deprecate_private_class(_resultbase) +_parsetz = __deprecated_private_func(_parsetz) diff --git a/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..48abc0a Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/_parser.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/_parser.cpython-312.pyc new file mode 100644 index 0000000..0cd4572 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/_parser.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/isoparser.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/isoparser.cpython-312.pyc new file mode 100644 index 0000000..fe19baa Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/isoparser.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/parser/_parser.py b/venv/lib/python3.12/site-packages/dateutil/parser/_parser.py new file mode 100644 index 0000000..37d1663 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/parser/_parser.py @@ -0,0 +1,1613 @@ +# -*- coding: utf-8 -*- +""" +This module offers a generic date/time string parser which is able to parse +most known formats to represent a date and/or time. + +This module attempts to be forgiving with regards to unlikely input formats, +returning a datetime object even for dates which are ambiguous. If an element +of a date/time stamp is omitted, the following rules are applied: + +- If AM or PM is left unspecified, a 24-hour clock is assumed, however, an hour + on a 12-hour clock (``0 <= hour <= 12``) *must* be specified if AM or PM is + specified. +- If a time zone is omitted, a timezone-naive datetime is returned. + +If any other elements are missing, they are taken from the +:class:`datetime.datetime` object passed to the parameter ``default``. If this +results in a day number exceeding the valid number of days per month, the +value falls back to the end of the month. + +Additional resources about date/time string formats can be found below: + +- `A summary of the international standard date and time notation + `_ +- `W3C Date and Time Formats `_ +- `Time Formats (Planetary Rings Node) `_ +- `CPAN ParseDate module + `_ +- `Java SimpleDateFormat Class + `_ +""" +from __future__ import unicode_literals + +import datetime +import re +import string +import time +import warnings + +from calendar import monthrange +from io import StringIO + +import six +from six import integer_types, text_type + +from decimal import Decimal + +from warnings import warn + +from .. import relativedelta +from .. import tz + +__all__ = ["parse", "parserinfo", "ParserError"] + + +# TODO: pandas.core.tools.datetimes imports this explicitly. Might be worth +# making public and/or figuring out if there is something we can +# take off their plate. +class _timelex(object): + # Fractional seconds are sometimes split by a comma + _split_decimal = re.compile("([.,])") + + def __init__(self, instream): + if isinstance(instream, (bytes, bytearray)): + instream = instream.decode() + + if isinstance(instream, text_type): + instream = StringIO(instream) + elif getattr(instream, 'read', None) is None: + raise TypeError('Parser must be a string or character stream, not ' + '{itype}'.format(itype=instream.__class__.__name__)) + + self.instream = instream + self.charstack = [] + self.tokenstack = [] + self.eof = False + + def get_token(self): + """ + This function breaks the time string into lexical units (tokens), which + can be parsed by the parser. Lexical units are demarcated by changes in + the character set, so any continuous string of letters is considered + one unit, any continuous string of numbers is considered one unit. + + The main complication arises from the fact that dots ('.') can be used + both as separators (e.g. "Sep.20.2009") or decimal points (e.g. + "4:30:21.447"). As such, it is necessary to read the full context of + any dot-separated strings before breaking it into tokens; as such, this + function maintains a "token stack", for when the ambiguous context + demands that multiple tokens be parsed at once. + """ + if self.tokenstack: + return self.tokenstack.pop(0) + + seenletters = False + token = None + state = None + + while not self.eof: + # We only realize that we've reached the end of a token when we + # find a character that's not part of the current token - since + # that character may be part of the next token, it's stored in the + # charstack. + if self.charstack: + nextchar = self.charstack.pop(0) + else: + nextchar = self.instream.read(1) + while nextchar == '\x00': + nextchar = self.instream.read(1) + + if not nextchar: + self.eof = True + break + elif not state: + # First character of the token - determines if we're starting + # to parse a word, a number or something else. + token = nextchar + if self.isword(nextchar): + state = 'a' + elif self.isnum(nextchar): + state = '0' + elif self.isspace(nextchar): + token = ' ' + break # emit token + else: + break # emit token + elif state == 'a': + # If we've already started reading a word, we keep reading + # letters until we find something that's not part of a word. + seenletters = True + if self.isword(nextchar): + token += nextchar + elif nextchar == '.': + token += nextchar + state = 'a.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == '0': + # If we've already started reading a number, we keep reading + # numbers until we find something that doesn't fit. + if self.isnum(nextchar): + token += nextchar + elif nextchar == '.' or (nextchar == ',' and len(token) >= 2): + token += nextchar + state = '0.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == 'a.': + # If we've seen some letters and a dot separator, continue + # parsing, and the tokens will be broken up later. + seenletters = True + if nextchar == '.' or self.isword(nextchar): + token += nextchar + elif self.isnum(nextchar) and token[-1] == '.': + token += nextchar + state = '0.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == '0.': + # If we've seen at least one dot separator, keep going, we'll + # break up the tokens later. + if nextchar == '.' or self.isnum(nextchar): + token += nextchar + elif self.isword(nextchar) and token[-1] == '.': + token += nextchar + state = 'a.' + else: + self.charstack.append(nextchar) + break # emit token + + if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or + token[-1] in '.,')): + l = self._split_decimal.split(token) + token = l[0] + for tok in l[1:]: + if tok: + self.tokenstack.append(tok) + + if state == '0.' and token.count('.') == 0: + token = token.replace(',', '.') + + return token + + def __iter__(self): + return self + + def __next__(self): + token = self.get_token() + if token is None: + raise StopIteration + + return token + + def next(self): + return self.__next__() # Python 2.x support + + @classmethod + def split(cls, s): + return list(cls(s)) + + @classmethod + def isword(cls, nextchar): + """ Whether or not the next character is part of a word """ + return nextchar.isalpha() + + @classmethod + def isnum(cls, nextchar): + """ Whether the next character is part of a number """ + return nextchar.isdigit() + + @classmethod + def isspace(cls, nextchar): + """ Whether the next character is whitespace """ + return nextchar.isspace() + + +class _resultbase(object): + + def __init__(self): + for attr in self.__slots__: + setattr(self, attr, None) + + def _repr(self, classname): + l = [] + for attr in self.__slots__: + value = getattr(self, attr) + if value is not None: + l.append("%s=%s" % (attr, repr(value))) + return "%s(%s)" % (classname, ", ".join(l)) + + def __len__(self): + return (sum(getattr(self, attr) is not None + for attr in self.__slots__)) + + def __repr__(self): + return self._repr(self.__class__.__name__) + + +class parserinfo(object): + """ + Class which handles what inputs are accepted. Subclass this to customize + the language and acceptable values for each parameter. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM + and YMD. Default is ``False``. + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken + to be the year, otherwise the last number is taken to be the year. + Default is ``False``. + """ + + # m from a.m/p.m, t from ISO T separator + JUMP = [" ", ".", ",", ";", "-", "/", "'", + "at", "on", "and", "ad", "m", "t", "of", + "st", "nd", "rd", "th"] + + WEEKDAYS = [("Mon", "Monday"), + ("Tue", "Tuesday"), # TODO: "Tues" + ("Wed", "Wednesday"), + ("Thu", "Thursday"), # TODO: "Thurs" + ("Fri", "Friday"), + ("Sat", "Saturday"), + ("Sun", "Sunday")] + MONTHS = [("Jan", "January"), + ("Feb", "February"), # TODO: "Febr" + ("Mar", "March"), + ("Apr", "April"), + ("May", "May"), + ("Jun", "June"), + ("Jul", "July"), + ("Aug", "August"), + ("Sep", "Sept", "September"), + ("Oct", "October"), + ("Nov", "November"), + ("Dec", "December")] + HMS = [("h", "hour", "hours"), + ("m", "minute", "minutes"), + ("s", "second", "seconds")] + AMPM = [("am", "a"), + ("pm", "p")] + UTCZONE = ["UTC", "GMT", "Z", "z"] + PERTAIN = ["of"] + TZOFFSET = {} + # TODO: ERA = ["AD", "BC", "CE", "BCE", "Stardate", + # "Anno Domini", "Year of Our Lord"] + + def __init__(self, dayfirst=False, yearfirst=False): + self._jump = self._convert(self.JUMP) + self._weekdays = self._convert(self.WEEKDAYS) + self._months = self._convert(self.MONTHS) + self._hms = self._convert(self.HMS) + self._ampm = self._convert(self.AMPM) + self._utczone = self._convert(self.UTCZONE) + self._pertain = self._convert(self.PERTAIN) + + self.dayfirst = dayfirst + self.yearfirst = yearfirst + + self._year = time.localtime().tm_year + self._century = self._year // 100 * 100 + + def _convert(self, lst): + dct = {} + for i, v in enumerate(lst): + if isinstance(v, tuple): + for v in v: + dct[v.lower()] = i + else: + dct[v.lower()] = i + return dct + + def jump(self, name): + return name.lower() in self._jump + + def weekday(self, name): + try: + return self._weekdays[name.lower()] + except KeyError: + pass + return None + + def month(self, name): + try: + return self._months[name.lower()] + 1 + except KeyError: + pass + return None + + def hms(self, name): + try: + return self._hms[name.lower()] + except KeyError: + return None + + def ampm(self, name): + try: + return self._ampm[name.lower()] + except KeyError: + return None + + def pertain(self, name): + return name.lower() in self._pertain + + def utczone(self, name): + return name.lower() in self._utczone + + def tzoffset(self, name): + if name in self._utczone: + return 0 + + return self.TZOFFSET.get(name) + + def convertyear(self, year, century_specified=False): + """ + Converts two-digit years to year within [-50, 49] + range of self._year (current local time) + """ + + # Function contract is that the year is always positive + assert year >= 0 + + if year < 100 and not century_specified: + # assume current century to start + year += self._century + + if year >= self._year + 50: # if too far in future + year -= 100 + elif year < self._year - 50: # if too far in past + year += 100 + + return year + + def validate(self, res): + # move to info + if res.year is not None: + res.year = self.convertyear(res.year, res.century_specified) + + if ((res.tzoffset == 0 and not res.tzname) or + (res.tzname == 'Z' or res.tzname == 'z')): + res.tzname = "UTC" + res.tzoffset = 0 + elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname): + res.tzoffset = 0 + return True + + +class _ymd(list): + def __init__(self, *args, **kwargs): + super(self.__class__, self).__init__(*args, **kwargs) + self.century_specified = False + self.dstridx = None + self.mstridx = None + self.ystridx = None + + @property + def has_year(self): + return self.ystridx is not None + + @property + def has_month(self): + return self.mstridx is not None + + @property + def has_day(self): + return self.dstridx is not None + + def could_be_day(self, value): + if self.has_day: + return False + elif not self.has_month: + return 1 <= value <= 31 + elif not self.has_year: + # Be permissive, assume leap year + month = self[self.mstridx] + return 1 <= value <= monthrange(2000, month)[1] + else: + month = self[self.mstridx] + year = self[self.ystridx] + return 1 <= value <= monthrange(year, month)[1] + + def append(self, val, label=None): + if hasattr(val, '__len__'): + if val.isdigit() and len(val) > 2: + self.century_specified = True + if label not in [None, 'Y']: # pragma: no cover + raise ValueError(label) + label = 'Y' + elif val > 100: + self.century_specified = True + if label not in [None, 'Y']: # pragma: no cover + raise ValueError(label) + label = 'Y' + + super(self.__class__, self).append(int(val)) + + if label == 'M': + if self.has_month: + raise ValueError('Month is already set') + self.mstridx = len(self) - 1 + elif label == 'D': + if self.has_day: + raise ValueError('Day is already set') + self.dstridx = len(self) - 1 + elif label == 'Y': + if self.has_year: + raise ValueError('Year is already set') + self.ystridx = len(self) - 1 + + def _resolve_from_stridxs(self, strids): + """ + Try to resolve the identities of year/month/day elements using + ystridx, mstridx, and dstridx, if enough of these are specified. + """ + if len(self) == 3 and len(strids) == 2: + # we can back out the remaining stridx value + missing = [x for x in range(3) if x not in strids.values()] + key = [x for x in ['y', 'm', 'd'] if x not in strids] + assert len(missing) == len(key) == 1 + key = key[0] + val = missing[0] + strids[key] = val + + assert len(self) == len(strids) # otherwise this should not be called + out = {key: self[strids[key]] for key in strids} + return (out.get('y'), out.get('m'), out.get('d')) + + def resolve_ymd(self, yearfirst, dayfirst): + len_ymd = len(self) + year, month, day = (None, None, None) + + strids = (('y', self.ystridx), + ('m', self.mstridx), + ('d', self.dstridx)) + + strids = {key: val for key, val in strids if val is not None} + if (len(self) == len(strids) > 0 or + (len(self) == 3 and len(strids) == 2)): + return self._resolve_from_stridxs(strids) + + mstridx = self.mstridx + + if len_ymd > 3: + raise ValueError("More than three YMD values") + elif len_ymd == 1 or (mstridx is not None and len_ymd == 2): + # One member, or two members with a month string + if mstridx is not None: + month = self[mstridx] + # since mstridx is 0 or 1, self[mstridx-1] always + # looks up the other element + other = self[mstridx - 1] + else: + other = self[0] + + if len_ymd > 1 or mstridx is None: + if other > 31: + year = other + else: + day = other + + elif len_ymd == 2: + # Two members with numbers + if self[0] > 31: + # 99-01 + year, month = self + elif self[1] > 31: + # 01-99 + month, year = self + elif dayfirst and self[1] <= 12: + # 13-01 + day, month = self + else: + # 01-13 + month, day = self + + elif len_ymd == 3: + # Three members + if mstridx == 0: + if self[1] > 31: + # Apr-2003-25 + month, year, day = self + else: + month, day, year = self + elif mstridx == 1: + if self[0] > 31 or (yearfirst and self[2] <= 31): + # 99-Jan-01 + year, month, day = self + else: + # 01-Jan-01 + # Give precedence to day-first, since + # two-digit years is usually hand-written. + day, month, year = self + + elif mstridx == 2: + # WTF!? + if self[1] > 31: + # 01-99-Jan + day, year, month = self + else: + # 99-01-Jan + year, day, month = self + + else: + if (self[0] > 31 or + self.ystridx == 0 or + (yearfirst and self[1] <= 12 and self[2] <= 31)): + # 99-01-01 + if dayfirst and self[2] <= 12: + year, day, month = self + else: + year, month, day = self + elif self[0] > 12 or (dayfirst and self[1] <= 12): + # 13-01-01 + day, month, year = self + else: + # 01-13-01 + month, day, year = self + + return year, month, day + + +class parser(object): + def __init__(self, info=None): + self.info = info or parserinfo() + + def parse(self, timestr, default=None, + ignoretz=False, tzinfos=None, **kwargs): + """ + Parse the date/time string into a :class:`datetime.datetime` object. + + :param timestr: + Any date/time string using the supported formats. + + :param default: + The default datetime object, if this is a datetime object and not + ``None``, elements specified in ``timestr`` replace elements in the + default object. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a + naive :class:`datetime.datetime` object is returned. + + :param tzinfos: + Additional time zone names / aliases which may be present in the + string. This argument maps time zone names (and optionally offsets + from those time zones) to time zones. This parameter can be a + dictionary with timezone aliases mapping time zone names to time + zones or a function taking two parameters (``tzname`` and + ``tzoffset``) and returning a time zone. + + The timezones to which the names are mapped can be an integer + offset from UTC in seconds or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} + >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) + >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, + tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) + + This parameter is ignored if ``ignoretz`` is set. + + :param \\*\\*kwargs: + Keyword arguments as passed to ``_parse()``. + + :return: + Returns a :class:`datetime.datetime` object or, if the + ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the + first element being a :class:`datetime.datetime` object, the second + a tuple containing the fuzzy tokens. + + :raises ParserError: + Raised for invalid or unknown string format, if the provided + :class:`tzinfo` is not in a valid format, or if an invalid date + would be created. + + :raises TypeError: + Raised for non-string or character stream input. + + :raises OverflowError: + Raised if the parsed date exceeds the largest valid C integer on + your system. + """ + + if default is None: + default = datetime.datetime.now().replace(hour=0, minute=0, + second=0, microsecond=0) + + res, skipped_tokens = self._parse(timestr, **kwargs) + + if res is None: + raise ParserError("Unknown string format: %s", timestr) + + if len(res) == 0: + raise ParserError("String does not contain a date: %s", timestr) + + try: + ret = self._build_naive(res, default) + except ValueError as e: + six.raise_from(ParserError(str(e) + ": %s", timestr), e) + + if not ignoretz: + ret = self._build_tzaware(ret, res, tzinfos) + + if kwargs.get('fuzzy_with_tokens', False): + return ret, skipped_tokens + else: + return ret + + class _result(_resultbase): + __slots__ = ["year", "month", "day", "weekday", + "hour", "minute", "second", "microsecond", + "tzname", "tzoffset", "ampm","any_unused_tokens"] + + def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False, + fuzzy_with_tokens=False): + """ + Private method which performs the heavy lifting of parsing, called from + ``parse()``, which passes on its ``kwargs`` to this function. + + :param timestr: + The string to parse. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM + and YMD. If set to ``None``, this value is retrieved from the + current :class:`parserinfo` object (which itself defaults to + ``False``). + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken + to be the year, otherwise the last number is taken to be the year. + If this is set to ``None``, the value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param fuzzy: + Whether to allow fuzzy parsing, allowing for string like "Today is + January 1, 2047 at 8:21:00AM". + + :param fuzzy_with_tokens: + If ``True``, ``fuzzy`` is automatically set to True, and the parser + will return a tuple where the first element is the parsed + :class:`datetime.datetime` datetimestamp and the second element is + a tuple containing the portions of the string which were ignored: + + .. doctest:: + + >>> from dateutil.parser import parse + >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) + (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) + + """ + if fuzzy_with_tokens: + fuzzy = True + + info = self.info + + if dayfirst is None: + dayfirst = info.dayfirst + + if yearfirst is None: + yearfirst = info.yearfirst + + res = self._result() + l = _timelex.split(timestr) # Splits the timestr into tokens + + skipped_idxs = [] + + # year/month/day list + ymd = _ymd() + + len_l = len(l) + i = 0 + try: + while i < len_l: + + # Check if it's a number + value_repr = l[i] + try: + value = float(value_repr) + except ValueError: + value = None + + if value is not None: + # Numeric token + i = self._parse_numeric_token(l, i, info, ymd, res, fuzzy) + + # Check weekday + elif info.weekday(l[i]) is not None: + value = info.weekday(l[i]) + res.weekday = value + + # Check month name + elif info.month(l[i]) is not None: + value = info.month(l[i]) + ymd.append(value, 'M') + + if i + 1 < len_l: + if l[i + 1] in ('-', '/'): + # Jan-01[-99] + sep = l[i + 1] + ymd.append(l[i + 2]) + + if i + 3 < len_l and l[i + 3] == sep: + # Jan-01-99 + ymd.append(l[i + 4]) + i += 2 + + i += 2 + + elif (i + 4 < len_l and l[i + 1] == l[i + 3] == ' ' and + info.pertain(l[i + 2])): + # Jan of 01 + # In this case, 01 is clearly year + if l[i + 4].isdigit(): + # Convert it here to become unambiguous + value = int(l[i + 4]) + year = str(info.convertyear(value)) + ymd.append(year, 'Y') + else: + # Wrong guess + pass + # TODO: not hit in tests + i += 4 + + # Check am/pm + elif info.ampm(l[i]) is not None: + value = info.ampm(l[i]) + val_is_ampm = self._ampm_valid(res.hour, res.ampm, fuzzy) + + if val_is_ampm: + res.hour = self._adjust_ampm(res.hour, value) + res.ampm = value + + elif fuzzy: + skipped_idxs.append(i) + + # Check for a timezone name + elif self._could_be_tzname(res.hour, res.tzname, res.tzoffset, l[i]): + res.tzname = l[i] + res.tzoffset = info.tzoffset(res.tzname) + + # Check for something like GMT+3, or BRST+3. Notice + # that it doesn't mean "I am 3 hours after GMT", but + # "my time +3 is GMT". If found, we reverse the + # logic so that timezone parsing code will get it + # right. + if i + 1 < len_l and l[i + 1] in ('+', '-'): + l[i + 1] = ('+', '-')[l[i + 1] == '+'] + res.tzoffset = None + if info.utczone(res.tzname): + # With something like GMT+3, the timezone + # is *not* GMT. + res.tzname = None + + # Check for a numbered timezone + elif res.hour is not None and l[i] in ('+', '-'): + signal = (-1, 1)[l[i] == '+'] + len_li = len(l[i + 1]) + + # TODO: check that l[i + 1] is integer? + if len_li == 4: + # -0300 + hour_offset = int(l[i + 1][:2]) + min_offset = int(l[i + 1][2:]) + elif i + 2 < len_l and l[i + 2] == ':': + # -03:00 + hour_offset = int(l[i + 1]) + min_offset = int(l[i + 3]) # TODO: Check that l[i+3] is minute-like? + i += 2 + elif len_li <= 2: + # -[0]3 + hour_offset = int(l[i + 1][:2]) + min_offset = 0 + else: + raise ValueError(timestr) + + res.tzoffset = signal * (hour_offset * 3600 + min_offset * 60) + + # Look for a timezone name between parenthesis + if (i + 5 < len_l and + info.jump(l[i + 2]) and l[i + 3] == '(' and + l[i + 5] == ')' and + 3 <= len(l[i + 4]) and + self._could_be_tzname(res.hour, res.tzname, + None, l[i + 4])): + # -0300 (BRST) + res.tzname = l[i + 4] + i += 4 + + i += 1 + + # Check jumps + elif not (info.jump(l[i]) or fuzzy): + raise ValueError(timestr) + + else: + skipped_idxs.append(i) + i += 1 + + # Process year/month/day + year, month, day = ymd.resolve_ymd(yearfirst, dayfirst) + + res.century_specified = ymd.century_specified + res.year = year + res.month = month + res.day = day + + except (IndexError, ValueError): + return None, None + + if not info.validate(res): + return None, None + + if fuzzy_with_tokens: + skipped_tokens = self._recombine_skipped(l, skipped_idxs) + return res, tuple(skipped_tokens) + else: + return res, None + + def _parse_numeric_token(self, tokens, idx, info, ymd, res, fuzzy): + # Token is a number + value_repr = tokens[idx] + try: + value = self._to_decimal(value_repr) + except Exception as e: + six.raise_from(ValueError('Unknown numeric token'), e) + + len_li = len(value_repr) + + len_l = len(tokens) + + if (len(ymd) == 3 and len_li in (2, 4) and + res.hour is None and + (idx + 1 >= len_l or + (tokens[idx + 1] != ':' and + info.hms(tokens[idx + 1]) is None))): + # 19990101T23[59] + s = tokens[idx] + res.hour = int(s[:2]) + + if len_li == 4: + res.minute = int(s[2:]) + + elif len_li == 6 or (len_li > 6 and tokens[idx].find('.') == 6): + # YYMMDD or HHMMSS[.ss] + s = tokens[idx] + + if not ymd and '.' not in tokens[idx]: + ymd.append(s[:2]) + ymd.append(s[2:4]) + ymd.append(s[4:]) + else: + # 19990101T235959[.59] + + # TODO: Check if res attributes already set. + res.hour = int(s[:2]) + res.minute = int(s[2:4]) + res.second, res.microsecond = self._parsems(s[4:]) + + elif len_li in (8, 12, 14): + # YYYYMMDD + s = tokens[idx] + ymd.append(s[:4], 'Y') + ymd.append(s[4:6]) + ymd.append(s[6:8]) + + if len_li > 8: + res.hour = int(s[8:10]) + res.minute = int(s[10:12]) + + if len_li > 12: + res.second = int(s[12:]) + + elif self._find_hms_idx(idx, tokens, info, allow_jump=True) is not None: + # HH[ ]h or MM[ ]m or SS[.ss][ ]s + hms_idx = self._find_hms_idx(idx, tokens, info, allow_jump=True) + (idx, hms) = self._parse_hms(idx, tokens, info, hms_idx) + if hms is not None: + # TODO: checking that hour/minute/second are not + # already set? + self._assign_hms(res, value_repr, hms) + + elif idx + 2 < len_l and tokens[idx + 1] == ':': + # HH:MM[:SS[.ss]] + res.hour = int(value) + value = self._to_decimal(tokens[idx + 2]) # TODO: try/except for this? + (res.minute, res.second) = self._parse_min_sec(value) + + if idx + 4 < len_l and tokens[idx + 3] == ':': + res.second, res.microsecond = self._parsems(tokens[idx + 4]) + + idx += 2 + + idx += 2 + + elif idx + 1 < len_l and tokens[idx + 1] in ('-', '/', '.'): + sep = tokens[idx + 1] + ymd.append(value_repr) + + if idx + 2 < len_l and not info.jump(tokens[idx + 2]): + if tokens[idx + 2].isdigit(): + # 01-01[-01] + ymd.append(tokens[idx + 2]) + else: + # 01-Jan[-01] + value = info.month(tokens[idx + 2]) + + if value is not None: + ymd.append(value, 'M') + else: + raise ValueError() + + if idx + 3 < len_l and tokens[idx + 3] == sep: + # We have three members + value = info.month(tokens[idx + 4]) + + if value is not None: + ymd.append(value, 'M') + else: + ymd.append(tokens[idx + 4]) + idx += 2 + + idx += 1 + idx += 1 + + elif idx + 1 >= len_l or info.jump(tokens[idx + 1]): + if idx + 2 < len_l and info.ampm(tokens[idx + 2]) is not None: + # 12 am + hour = int(value) + res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 2])) + idx += 1 + else: + # Year, month or day + ymd.append(value) + idx += 1 + + elif info.ampm(tokens[idx + 1]) is not None and (0 <= value < 24): + # 12am + hour = int(value) + res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 1])) + idx += 1 + + elif ymd.could_be_day(value): + ymd.append(value) + + elif not fuzzy: + raise ValueError() + + return idx + + def _find_hms_idx(self, idx, tokens, info, allow_jump): + len_l = len(tokens) + + if idx+1 < len_l and info.hms(tokens[idx+1]) is not None: + # There is an "h", "m", or "s" label following this token. We take + # assign the upcoming label to the current token. + # e.g. the "12" in 12h" + hms_idx = idx + 1 + + elif (allow_jump and idx+2 < len_l and tokens[idx+1] == ' ' and + info.hms(tokens[idx+2]) is not None): + # There is a space and then an "h", "m", or "s" label. + # e.g. the "12" in "12 h" + hms_idx = idx + 2 + + elif idx > 0 and info.hms(tokens[idx-1]) is not None: + # There is a "h", "m", or "s" preceding this token. Since neither + # of the previous cases was hit, there is no label following this + # token, so we use the previous label. + # e.g. the "04" in "12h04" + hms_idx = idx-1 + + elif (1 < idx == len_l-1 and tokens[idx-1] == ' ' and + info.hms(tokens[idx-2]) is not None): + # If we are looking at the final token, we allow for a + # backward-looking check to skip over a space. + # TODO: Are we sure this is the right condition here? + hms_idx = idx - 2 + + else: + hms_idx = None + + return hms_idx + + def _assign_hms(self, res, value_repr, hms): + # See GH issue #427, fixing float rounding + value = self._to_decimal(value_repr) + + if hms == 0: + # Hour + res.hour = int(value) + if value % 1: + res.minute = int(60*(value % 1)) + + elif hms == 1: + (res.minute, res.second) = self._parse_min_sec(value) + + elif hms == 2: + (res.second, res.microsecond) = self._parsems(value_repr) + + def _could_be_tzname(self, hour, tzname, tzoffset, token): + return (hour is not None and + tzname is None and + tzoffset is None and + len(token) <= 5 and + (all(x in string.ascii_uppercase for x in token) + or token in self.info.UTCZONE)) + + def _ampm_valid(self, hour, ampm, fuzzy): + """ + For fuzzy parsing, 'a' or 'am' (both valid English words) + may erroneously trigger the AM/PM flag. Deal with that + here. + """ + val_is_ampm = True + + # If there's already an AM/PM flag, this one isn't one. + if fuzzy and ampm is not None: + val_is_ampm = False + + # If AM/PM is found and hour is not, raise a ValueError + if hour is None: + if fuzzy: + val_is_ampm = False + else: + raise ValueError('No hour specified with AM or PM flag.') + elif not 0 <= hour <= 12: + # If AM/PM is found, it's a 12 hour clock, so raise + # an error for invalid range + if fuzzy: + val_is_ampm = False + else: + raise ValueError('Invalid hour specified for 12-hour clock.') + + return val_is_ampm + + def _adjust_ampm(self, hour, ampm): + if hour < 12 and ampm == 1: + hour += 12 + elif hour == 12 and ampm == 0: + hour = 0 + return hour + + def _parse_min_sec(self, value): + # TODO: Every usage of this function sets res.second to the return + # value. Are there any cases where second will be returned as None and + # we *don't* want to set res.second = None? + minute = int(value) + second = None + + sec_remainder = value % 1 + if sec_remainder: + second = int(60 * sec_remainder) + return (minute, second) + + def _parse_hms(self, idx, tokens, info, hms_idx): + # TODO: Is this going to admit a lot of false-positives for when we + # just happen to have digits and "h", "m" or "s" characters in non-date + # text? I guess hex hashes won't have that problem, but there's plenty + # of random junk out there. + if hms_idx is None: + hms = None + new_idx = idx + elif hms_idx > idx: + hms = info.hms(tokens[hms_idx]) + new_idx = hms_idx + else: + # Looking backwards, increment one. + hms = info.hms(tokens[hms_idx]) + 1 + new_idx = idx + + return (new_idx, hms) + + # ------------------------------------------------------------------ + # Handling for individual tokens. These are kept as methods instead + # of functions for the sake of customizability via subclassing. + + def _parsems(self, value): + """Parse a I[.F] seconds value into (seconds, microseconds).""" + if "." not in value: + return int(value), 0 + else: + i, f = value.split(".") + return int(i), int(f.ljust(6, "0")[:6]) + + def _to_decimal(self, val): + try: + decimal_value = Decimal(val) + # See GH 662, edge case, infinite value should not be converted + # via `_to_decimal` + if not decimal_value.is_finite(): + raise ValueError("Converted decimal value is infinite or NaN") + except Exception as e: + msg = "Could not convert %s to decimal" % val + six.raise_from(ValueError(msg), e) + else: + return decimal_value + + # ------------------------------------------------------------------ + # Post-Parsing construction of datetime output. These are kept as + # methods instead of functions for the sake of customizability via + # subclassing. + + def _build_tzinfo(self, tzinfos, tzname, tzoffset): + if callable(tzinfos): + tzdata = tzinfos(tzname, tzoffset) + else: + tzdata = tzinfos.get(tzname) + # handle case where tzinfo is paased an options that returns None + # eg tzinfos = {'BRST' : None} + if isinstance(tzdata, datetime.tzinfo) or tzdata is None: + tzinfo = tzdata + elif isinstance(tzdata, text_type): + tzinfo = tz.tzstr(tzdata) + elif isinstance(tzdata, integer_types): + tzinfo = tz.tzoffset(tzname, tzdata) + else: + raise TypeError("Offset must be tzinfo subclass, tz string, " + "or int offset.") + return tzinfo + + def _build_tzaware(self, naive, res, tzinfos): + if (callable(tzinfos) or (tzinfos and res.tzname in tzinfos)): + tzinfo = self._build_tzinfo(tzinfos, res.tzname, res.tzoffset) + aware = naive.replace(tzinfo=tzinfo) + aware = self._assign_tzname(aware, res.tzname) + + elif res.tzname and res.tzname in time.tzname: + aware = naive.replace(tzinfo=tz.tzlocal()) + + # Handle ambiguous local datetime + aware = self._assign_tzname(aware, res.tzname) + + # This is mostly relevant for winter GMT zones parsed in the UK + if (aware.tzname() != res.tzname and + res.tzname in self.info.UTCZONE): + aware = aware.replace(tzinfo=tz.UTC) + + elif res.tzoffset == 0: + aware = naive.replace(tzinfo=tz.UTC) + + elif res.tzoffset: + aware = naive.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) + + elif not res.tzname and not res.tzoffset: + # i.e. no timezone information was found. + aware = naive + + elif res.tzname: + # tz-like string was parsed but we don't know what to do + # with it + warnings.warn("tzname {tzname} identified but not understood. " + "Pass `tzinfos` argument in order to correctly " + "return a timezone-aware datetime. In a future " + "version, this will raise an " + "exception.".format(tzname=res.tzname), + category=UnknownTimezoneWarning) + aware = naive + + return aware + + def _build_naive(self, res, default): + repl = {} + for attr in ("year", "month", "day", "hour", + "minute", "second", "microsecond"): + value = getattr(res, attr) + if value is not None: + repl[attr] = value + + if 'day' not in repl: + # If the default day exceeds the last day of the month, fall back + # to the end of the month. + cyear = default.year if res.year is None else res.year + cmonth = default.month if res.month is None else res.month + cday = default.day if res.day is None else res.day + + if cday > monthrange(cyear, cmonth)[1]: + repl['day'] = monthrange(cyear, cmonth)[1] + + naive = default.replace(**repl) + + if res.weekday is not None and not res.day: + naive = naive + relativedelta.relativedelta(weekday=res.weekday) + + return naive + + def _assign_tzname(self, dt, tzname): + if dt.tzname() != tzname: + new_dt = tz.enfold(dt, fold=1) + if new_dt.tzname() == tzname: + return new_dt + + return dt + + def _recombine_skipped(self, tokens, skipped_idxs): + """ + >>> tokens = ["foo", " ", "bar", " ", "19June2000", "baz"] + >>> skipped_idxs = [0, 1, 2, 5] + >>> _recombine_skipped(tokens, skipped_idxs) + ["foo bar", "baz"] + """ + skipped_tokens = [] + for i, idx in enumerate(sorted(skipped_idxs)): + if i > 0 and idx - 1 == skipped_idxs[i - 1]: + skipped_tokens[-1] = skipped_tokens[-1] + tokens[idx] + else: + skipped_tokens.append(tokens[idx]) + + return skipped_tokens + + +DEFAULTPARSER = parser() + + +def parse(timestr, parserinfo=None, **kwargs): + """ + + Parse a string in one of the supported formats, using the + ``parserinfo`` parameters. + + :param timestr: + A string containing a date/time stamp. + + :param parserinfo: + A :class:`parserinfo` object containing parameters for the parser. + If ``None``, the default arguments to the :class:`parserinfo` + constructor are used. + + The ``**kwargs`` parameter takes the following keyword arguments: + + :param default: + The default datetime object, if this is a datetime object and not + ``None``, elements specified in ``timestr`` replace elements in the + default object. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a naive + :class:`datetime` object is returned. + + :param tzinfos: + Additional time zone names / aliases which may be present in the + string. This argument maps time zone names (and optionally offsets + from those time zones) to time zones. This parameter can be a + dictionary with timezone aliases mapping time zone names to time + zones or a function taking two parameters (``tzname`` and + ``tzoffset``) and returning a time zone. + + The timezones to which the names are mapped can be an integer + offset from UTC in seconds or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} + >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) + >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, + tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) + + This parameter is ignored if ``ignoretz`` is set. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM and + YMD. If set to ``None``, this value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken to + be the year, otherwise the last number is taken to be the year. If + this is set to ``None``, the value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param fuzzy: + Whether to allow fuzzy parsing, allowing for string like "Today is + January 1, 2047 at 8:21:00AM". + + :param fuzzy_with_tokens: + If ``True``, ``fuzzy`` is automatically set to True, and the parser + will return a tuple where the first element is the parsed + :class:`datetime.datetime` datetimestamp and the second element is + a tuple containing the portions of the string which were ignored: + + .. doctest:: + + >>> from dateutil.parser import parse + >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) + (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) + + :return: + Returns a :class:`datetime.datetime` object or, if the + ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the + first element being a :class:`datetime.datetime` object, the second + a tuple containing the fuzzy tokens. + + :raises ParserError: + Raised for invalid or unknown string formats, if the provided + :class:`tzinfo` is not in a valid format, or if an invalid date would + be created. + + :raises OverflowError: + Raised if the parsed date exceeds the largest valid C integer on + your system. + """ + if parserinfo: + return parser(parserinfo).parse(timestr, **kwargs) + else: + return DEFAULTPARSER.parse(timestr, **kwargs) + + +class _tzparser(object): + + class _result(_resultbase): + + __slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset", + "start", "end"] + + class _attr(_resultbase): + __slots__ = ["month", "week", "weekday", + "yday", "jyday", "day", "time"] + + def __repr__(self): + return self._repr("") + + def __init__(self): + _resultbase.__init__(self) + self.start = self._attr() + self.end = self._attr() + + def parse(self, tzstr): + res = self._result() + l = [x for x in re.split(r'([,:.]|[a-zA-Z]+|[0-9]+)',tzstr) if x] + used_idxs = list() + try: + + len_l = len(l) + + i = 0 + while i < len_l: + # BRST+3[BRDT[+2]] + j = i + while j < len_l and not [x for x in l[j] + if x in "0123456789:,-+"]: + j += 1 + if j != i: + if not res.stdabbr: + offattr = "stdoffset" + res.stdabbr = "".join(l[i:j]) + else: + offattr = "dstoffset" + res.dstabbr = "".join(l[i:j]) + + for ii in range(j): + used_idxs.append(ii) + i = j + if (i < len_l and (l[i] in ('+', '-') or l[i][0] in + "0123456789")): + if l[i] in ('+', '-'): + # Yes, that's right. See the TZ variable + # documentation. + signal = (1, -1)[l[i] == '+'] + used_idxs.append(i) + i += 1 + else: + signal = -1 + len_li = len(l[i]) + if len_li == 4: + # -0300 + setattr(res, offattr, (int(l[i][:2]) * 3600 + + int(l[i][2:]) * 60) * signal) + elif i + 1 < len_l and l[i + 1] == ':': + # -03:00 + setattr(res, offattr, + (int(l[i]) * 3600 + + int(l[i + 2]) * 60) * signal) + used_idxs.append(i) + i += 2 + elif len_li <= 2: + # -[0]3 + setattr(res, offattr, + int(l[i][:2]) * 3600 * signal) + else: + return None + used_idxs.append(i) + i += 1 + if res.dstabbr: + break + else: + break + + + if i < len_l: + for j in range(i, len_l): + if l[j] == ';': + l[j] = ',' + + assert l[i] == ',' + + i += 1 + + if i >= len_l: + pass + elif (8 <= l.count(',') <= 9 and + not [y for x in l[i:] if x != ',' + for y in x if y not in "0123456789+-"]): + # GMT0BST,3,0,30,3600,10,0,26,7200[,3600] + for x in (res.start, res.end): + x.month = int(l[i]) + used_idxs.append(i) + i += 2 + if l[i] == '-': + value = int(l[i + 1]) * -1 + used_idxs.append(i) + i += 1 + else: + value = int(l[i]) + used_idxs.append(i) + i += 2 + if value: + x.week = value + x.weekday = (int(l[i]) - 1) % 7 + else: + x.day = int(l[i]) + used_idxs.append(i) + i += 2 + x.time = int(l[i]) + used_idxs.append(i) + i += 2 + if i < len_l: + if l[i] in ('-', '+'): + signal = (-1, 1)[l[i] == "+"] + used_idxs.append(i) + i += 1 + else: + signal = 1 + used_idxs.append(i) + res.dstoffset = (res.stdoffset + int(l[i]) * signal) + + # This was a made-up format that is not in normal use + warn(('Parsed time zone "%s"' % tzstr) + + 'is in a non-standard dateutil-specific format, which ' + + 'is now deprecated; support for parsing this format ' + + 'will be removed in future versions. It is recommended ' + + 'that you switch to a standard format like the GNU ' + + 'TZ variable format.', tz.DeprecatedTzFormatWarning) + elif (l.count(',') == 2 and l[i:].count('/') <= 2 and + not [y for x in l[i:] if x not in (',', '/', 'J', 'M', + '.', '-', ':') + for y in x if y not in "0123456789"]): + for x in (res.start, res.end): + if l[i] == 'J': + # non-leap year day (1 based) + used_idxs.append(i) + i += 1 + x.jyday = int(l[i]) + elif l[i] == 'M': + # month[-.]week[-.]weekday + used_idxs.append(i) + i += 1 + x.month = int(l[i]) + used_idxs.append(i) + i += 1 + assert l[i] in ('-', '.') + used_idxs.append(i) + i += 1 + x.week = int(l[i]) + if x.week == 5: + x.week = -1 + used_idxs.append(i) + i += 1 + assert l[i] in ('-', '.') + used_idxs.append(i) + i += 1 + x.weekday = (int(l[i]) - 1) % 7 + else: + # year day (zero based) + x.yday = int(l[i]) + 1 + + used_idxs.append(i) + i += 1 + + if i < len_l and l[i] == '/': + used_idxs.append(i) + i += 1 + # start time + len_li = len(l[i]) + if len_li == 4: + # -0300 + x.time = (int(l[i][:2]) * 3600 + + int(l[i][2:]) * 60) + elif i + 1 < len_l and l[i + 1] == ':': + # -03:00 + x.time = int(l[i]) * 3600 + int(l[i + 2]) * 60 + used_idxs.append(i) + i += 2 + if i + 1 < len_l and l[i + 1] == ':': + used_idxs.append(i) + i += 2 + x.time += int(l[i]) + elif len_li <= 2: + # -[0]3 + x.time = (int(l[i][:2]) * 3600) + else: + return None + used_idxs.append(i) + i += 1 + + assert i == len_l or l[i] == ',' + + i += 1 + + assert i >= len_l + + except (IndexError, ValueError, AssertionError): + return None + + unused_idxs = set(range(len_l)).difference(used_idxs) + res.any_unused_tokens = not {l[n] for n in unused_idxs}.issubset({",",":"}) + return res + + +DEFAULTTZPARSER = _tzparser() + + +def _parsetz(tzstr): + return DEFAULTTZPARSER.parse(tzstr) + + +class ParserError(ValueError): + """Exception subclass used for any failure to parse a datetime string. + + This is a subclass of :py:exc:`ValueError`, and should be raised any time + earlier versions of ``dateutil`` would have raised ``ValueError``. + + .. versionadded:: 2.8.1 + """ + def __str__(self): + try: + return self.args[0] % self.args[1:] + except (TypeError, IndexError): + return super(ParserError, self).__str__() + + def __repr__(self): + args = ", ".join("'%s'" % arg for arg in self.args) + return "%s(%s)" % (self.__class__.__name__, args) + + +class UnknownTimezoneWarning(RuntimeWarning): + """Raised when the parser finds a timezone it cannot parse into a tzinfo. + + .. versionadded:: 2.7.0 + """ +# vim:ts=4:sw=4:et diff --git a/venv/lib/python3.12/site-packages/dateutil/parser/isoparser.py b/venv/lib/python3.12/site-packages/dateutil/parser/isoparser.py new file mode 100644 index 0000000..7060087 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/parser/isoparser.py @@ -0,0 +1,416 @@ +# -*- coding: utf-8 -*- +""" +This module offers a parser for ISO-8601 strings + +It is intended to support all valid date, time and datetime formats per the +ISO-8601 specification. + +..versionadded:: 2.7.0 +""" +from datetime import datetime, timedelta, time, date +import calendar +from dateutil import tz + +from functools import wraps + +import re +import six + +__all__ = ["isoparse", "isoparser"] + + +def _takes_ascii(f): + @wraps(f) + def func(self, str_in, *args, **kwargs): + # If it's a stream, read the whole thing + str_in = getattr(str_in, 'read', lambda: str_in)() + + # If it's unicode, turn it into bytes, since ISO-8601 only covers ASCII + if isinstance(str_in, six.text_type): + # ASCII is the same in UTF-8 + try: + str_in = str_in.encode('ascii') + except UnicodeEncodeError as e: + msg = 'ISO-8601 strings should contain only ASCII characters' + six.raise_from(ValueError(msg), e) + + return f(self, str_in, *args, **kwargs) + + return func + + +class isoparser(object): + def __init__(self, sep=None): + """ + :param sep: + A single character that separates date and time portions. If + ``None``, the parser will accept any single character. + For strict ISO-8601 adherence, pass ``'T'``. + """ + if sep is not None: + if (len(sep) != 1 or ord(sep) >= 128 or sep in '0123456789'): + raise ValueError('Separator must be a single, non-numeric ' + + 'ASCII character') + + sep = sep.encode('ascii') + + self._sep = sep + + @_takes_ascii + def isoparse(self, dt_str): + """ + Parse an ISO-8601 datetime string into a :class:`datetime.datetime`. + + An ISO-8601 datetime string consists of a date portion, followed + optionally by a time portion - the date and time portions are separated + by a single character separator, which is ``T`` in the official + standard. Incomplete date formats (such as ``YYYY-MM``) may *not* be + combined with a time portion. + + Supported date formats are: + + Common: + + - ``YYYY`` + - ``YYYY-MM`` + - ``YYYY-MM-DD`` or ``YYYYMMDD`` + + Uncommon: + + - ``YYYY-Www`` or ``YYYYWww`` - ISO week (day defaults to 0) + - ``YYYY-Www-D`` or ``YYYYWwwD`` - ISO week and day + + The ISO week and day numbering follows the same logic as + :func:`datetime.date.isocalendar`. + + Supported time formats are: + + - ``hh`` + - ``hh:mm`` or ``hhmm`` + - ``hh:mm:ss`` or ``hhmmss`` + - ``hh:mm:ss.ssssss`` (Up to 6 sub-second digits) + + Midnight is a special case for `hh`, as the standard supports both + 00:00 and 24:00 as a representation. The decimal separator can be + either a dot or a comma. + + + .. caution:: + + Support for fractional components other than seconds is part of the + ISO-8601 standard, but is not currently implemented in this parser. + + Supported time zone offset formats are: + + - `Z` (UTC) + - `±HH:MM` + - `±HHMM` + - `±HH` + + Offsets will be represented as :class:`dateutil.tz.tzoffset` objects, + with the exception of UTC, which will be represented as + :class:`dateutil.tz.tzutc`. Time zone offsets equivalent to UTC (such + as `+00:00`) will also be represented as :class:`dateutil.tz.tzutc`. + + :param dt_str: + A string or stream containing only an ISO-8601 datetime string + + :return: + Returns a :class:`datetime.datetime` representing the string. + Unspecified components default to their lowest value. + + .. warning:: + + As of version 2.7.0, the strictness of the parser should not be + considered a stable part of the contract. Any valid ISO-8601 string + that parses correctly with the default settings will continue to + parse correctly in future versions, but invalid strings that + currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not + guaranteed to continue failing in future versions if they encode + a valid date. + + .. versionadded:: 2.7.0 + """ + components, pos = self._parse_isodate(dt_str) + + if len(dt_str) > pos: + if self._sep is None or dt_str[pos:pos + 1] == self._sep: + components += self._parse_isotime(dt_str[pos + 1:]) + else: + raise ValueError('String contains unknown ISO components') + + if len(components) > 3 and components[3] == 24: + components[3] = 0 + return datetime(*components) + timedelta(days=1) + + return datetime(*components) + + @_takes_ascii + def parse_isodate(self, datestr): + """ + Parse the date portion of an ISO string. + + :param datestr: + The string portion of an ISO string, without a separator + + :return: + Returns a :class:`datetime.date` object + """ + components, pos = self._parse_isodate(datestr) + if pos < len(datestr): + raise ValueError('String contains unknown ISO ' + + 'components: {!r}'.format(datestr.decode('ascii'))) + return date(*components) + + @_takes_ascii + def parse_isotime(self, timestr): + """ + Parse the time portion of an ISO string. + + :param timestr: + The time portion of an ISO string, without a separator + + :return: + Returns a :class:`datetime.time` object + """ + components = self._parse_isotime(timestr) + if components[0] == 24: + components[0] = 0 + return time(*components) + + @_takes_ascii + def parse_tzstr(self, tzstr, zero_as_utc=True): + """ + Parse a valid ISO time zone string. + + See :func:`isoparser.isoparse` for details on supported formats. + + :param tzstr: + A string representing an ISO time zone offset + + :param zero_as_utc: + Whether to return :class:`dateutil.tz.tzutc` for zero-offset zones + + :return: + Returns :class:`dateutil.tz.tzoffset` for offsets and + :class:`dateutil.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is + specified) offsets equivalent to UTC. + """ + return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc) + + # Constants + _DATE_SEP = b'-' + _TIME_SEP = b':' + _FRACTION_REGEX = re.compile(b'[\\.,]([0-9]+)') + + def _parse_isodate(self, dt_str): + try: + return self._parse_isodate_common(dt_str) + except ValueError: + return self._parse_isodate_uncommon(dt_str) + + def _parse_isodate_common(self, dt_str): + len_str = len(dt_str) + components = [1, 1, 1] + + if len_str < 4: + raise ValueError('ISO string too short') + + # Year + components[0] = int(dt_str[0:4]) + pos = 4 + if pos >= len_str: + return components, pos + + has_sep = dt_str[pos:pos + 1] == self._DATE_SEP + if has_sep: + pos += 1 + + # Month + if len_str - pos < 2: + raise ValueError('Invalid common month') + + components[1] = int(dt_str[pos:pos + 2]) + pos += 2 + + if pos >= len_str: + if has_sep: + return components, pos + else: + raise ValueError('Invalid ISO format') + + if has_sep: + if dt_str[pos:pos + 1] != self._DATE_SEP: + raise ValueError('Invalid separator in ISO string') + pos += 1 + + # Day + if len_str - pos < 2: + raise ValueError('Invalid common day') + components[2] = int(dt_str[pos:pos + 2]) + return components, pos + 2 + + def _parse_isodate_uncommon(self, dt_str): + if len(dt_str) < 4: + raise ValueError('ISO string too short') + + # All ISO formats start with the year + year = int(dt_str[0:4]) + + has_sep = dt_str[4:5] == self._DATE_SEP + + pos = 4 + has_sep # Skip '-' if it's there + if dt_str[pos:pos + 1] == b'W': + # YYYY-?Www-?D? + pos += 1 + weekno = int(dt_str[pos:pos + 2]) + pos += 2 + + dayno = 1 + if len(dt_str) > pos: + if (dt_str[pos:pos + 1] == self._DATE_SEP) != has_sep: + raise ValueError('Inconsistent use of dash separator') + + pos += has_sep + + dayno = int(dt_str[pos:pos + 1]) + pos += 1 + + base_date = self._calculate_weekdate(year, weekno, dayno) + else: + # YYYYDDD or YYYY-DDD + if len(dt_str) - pos < 3: + raise ValueError('Invalid ordinal day') + + ordinal_day = int(dt_str[pos:pos + 3]) + pos += 3 + + if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)): + raise ValueError('Invalid ordinal day' + + ' {} for year {}'.format(ordinal_day, year)) + + base_date = date(year, 1, 1) + timedelta(days=ordinal_day - 1) + + components = [base_date.year, base_date.month, base_date.day] + return components, pos + + def _calculate_weekdate(self, year, week, day): + """ + Calculate the day of corresponding to the ISO year-week-day calendar. + + This function is effectively the inverse of + :func:`datetime.date.isocalendar`. + + :param year: + The year in the ISO calendar + + :param week: + The week in the ISO calendar - range is [1, 53] + + :param day: + The day in the ISO calendar - range is [1 (MON), 7 (SUN)] + + :return: + Returns a :class:`datetime.date` + """ + if not 0 < week < 54: + raise ValueError('Invalid week: {}'.format(week)) + + if not 0 < day < 8: # Range is 1-7 + raise ValueError('Invalid weekday: {}'.format(day)) + + # Get week 1 for the specific year: + jan_4 = date(year, 1, 4) # Week 1 always has January 4th in it + week_1 = jan_4 - timedelta(days=jan_4.isocalendar()[2] - 1) + + # Now add the specific number of weeks and days to get what we want + week_offset = (week - 1) * 7 + (day - 1) + return week_1 + timedelta(days=week_offset) + + def _parse_isotime(self, timestr): + len_str = len(timestr) + components = [0, 0, 0, 0, None] + pos = 0 + comp = -1 + + if len_str < 2: + raise ValueError('ISO time too short') + + has_sep = False + + while pos < len_str and comp < 5: + comp += 1 + + if timestr[pos:pos + 1] in b'-+Zz': + # Detect time zone boundary + components[-1] = self._parse_tzstr(timestr[pos:]) + pos = len_str + break + + if comp == 1 and timestr[pos:pos+1] == self._TIME_SEP: + has_sep = True + pos += 1 + elif comp == 2 and has_sep: + if timestr[pos:pos+1] != self._TIME_SEP: + raise ValueError('Inconsistent use of colon separator') + pos += 1 + + if comp < 3: + # Hour, minute, second + components[comp] = int(timestr[pos:pos + 2]) + pos += 2 + + if comp == 3: + # Fraction of a second + frac = self._FRACTION_REGEX.match(timestr[pos:]) + if not frac: + continue + + us_str = frac.group(1)[:6] # Truncate to microseconds + components[comp] = int(us_str) * 10**(6 - len(us_str)) + pos += len(frac.group()) + + if pos < len_str: + raise ValueError('Unused components in ISO string') + + if components[0] == 24: + # Standard supports 00:00 and 24:00 as representations of midnight + if any(component != 0 for component in components[1:4]): + raise ValueError('Hour may only be 24 at 24:00:00.000') + + return components + + def _parse_tzstr(self, tzstr, zero_as_utc=True): + if tzstr == b'Z' or tzstr == b'z': + return tz.UTC + + if len(tzstr) not in {3, 5, 6}: + raise ValueError('Time zone offset must be 1, 3, 5 or 6 characters') + + if tzstr[0:1] == b'-': + mult = -1 + elif tzstr[0:1] == b'+': + mult = 1 + else: + raise ValueError('Time zone offset requires sign') + + hours = int(tzstr[1:3]) + if len(tzstr) == 3: + minutes = 0 + else: + minutes = int(tzstr[(4 if tzstr[3:4] == self._TIME_SEP else 3):]) + + if zero_as_utc and hours == 0 and minutes == 0: + return tz.UTC + else: + if minutes > 59: + raise ValueError('Invalid minutes in time zone offset') + + if hours > 23: + raise ValueError('Invalid hours in time zone offset') + + return tz.tzoffset(None, mult * (hours * 60 + minutes) * 60) + + +DEFAULT_ISOPARSER = isoparser() +isoparse = DEFAULT_ISOPARSER.isoparse diff --git a/venv/lib/python3.12/site-packages/dateutil/relativedelta.py b/venv/lib/python3.12/site-packages/dateutil/relativedelta.py new file mode 100644 index 0000000..cd323a5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/relativedelta.py @@ -0,0 +1,599 @@ +# -*- coding: utf-8 -*- +import datetime +import calendar + +import operator +from math import copysign + +from six import integer_types +from warnings import warn + +from ._common import weekday + +MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) + +__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] + + +class relativedelta(object): + """ + The relativedelta type is designed to be applied to an existing datetime and + can replace specific components of that datetime, or represents an interval + of time. + + It is based on the specification of the excellent work done by M.-A. Lemburg + in his + `mx.DateTime `_ extension. + However, notice that this type does *NOT* implement the same algorithm as + his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. + + There are two different ways to build a relativedelta instance. The + first one is passing it two date/datetime classes:: + + relativedelta(datetime1, datetime2) + + The second one is passing it any number of the following keyword arguments:: + + relativedelta(arg1=x,arg2=y,arg3=z...) + + year, month, day, hour, minute, second, microsecond: + Absolute information (argument is singular); adding or subtracting a + relativedelta with absolute information does not perform an arithmetic + operation, but rather REPLACES the corresponding value in the + original datetime with the value(s) in relativedelta. + + years, months, weeks, days, hours, minutes, seconds, microseconds: + Relative information, may be negative (argument is plural); adding + or subtracting a relativedelta with relative information performs + the corresponding arithmetic operation on the original datetime value + with the information in the relativedelta. + + weekday: + One of the weekday instances (MO, TU, etc) available in the + relativedelta module. These instances may receive a parameter N, + specifying the Nth weekday, which could be positive or negative + (like MO(+1) or MO(-2)). Not specifying it is the same as specifying + +1. You can also use an integer, where 0=MO. This argument is always + relative e.g. if the calculated date is already Monday, using MO(1) + or MO(-1) won't change the day. To effectively make it absolute, use + it in combination with the day argument (e.g. day=1, MO(1) for first + Monday of the month). + + leapdays: + Will add given days to the date found, if year is a leap + year, and the date found is post 28 of february. + + yearday, nlyearday: + Set the yearday or the non-leap year day (jump leap days). + These are converted to day/month/leapdays information. + + There are relative and absolute forms of the keyword + arguments. The plural is relative, and the singular is + absolute. For each argument in the order below, the absolute form + is applied first (by setting each attribute to that value) and + then the relative form (by adding the value to the attribute). + + The order of attributes considered when this relativedelta is + added to a datetime is: + + 1. Year + 2. Month + 3. Day + 4. Hours + 5. Minutes + 6. Seconds + 7. Microseconds + + Finally, weekday is applied, using the rule described above. + + For example + + >>> from datetime import datetime + >>> from dateutil.relativedelta import relativedelta, MO + >>> dt = datetime(2018, 4, 9, 13, 37, 0) + >>> delta = relativedelta(hours=25, day=1, weekday=MO(1)) + >>> dt + delta + datetime.datetime(2018, 4, 2, 14, 37) + + First, the day is set to 1 (the first of the month), then 25 hours + are added, to get to the 2nd day and 14th hour, finally the + weekday is applied, but since the 2nd is already a Monday there is + no effect. + + """ + + def __init__(self, dt1=None, dt2=None, + years=0, months=0, days=0, leapdays=0, weeks=0, + hours=0, minutes=0, seconds=0, microseconds=0, + year=None, month=None, day=None, weekday=None, + yearday=None, nlyearday=None, + hour=None, minute=None, second=None, microsecond=None): + + if dt1 and dt2: + # datetime is a subclass of date. So both must be date + if not (isinstance(dt1, datetime.date) and + isinstance(dt2, datetime.date)): + raise TypeError("relativedelta only diffs datetime/date") + + # We allow two dates, or two datetimes, so we coerce them to be + # of the same type + if (isinstance(dt1, datetime.datetime) != + isinstance(dt2, datetime.datetime)): + if not isinstance(dt1, datetime.datetime): + dt1 = datetime.datetime.fromordinal(dt1.toordinal()) + elif not isinstance(dt2, datetime.datetime): + dt2 = datetime.datetime.fromordinal(dt2.toordinal()) + + self.years = 0 + self.months = 0 + self.days = 0 + self.leapdays = 0 + self.hours = 0 + self.minutes = 0 + self.seconds = 0 + self.microseconds = 0 + self.year = None + self.month = None + self.day = None + self.weekday = None + self.hour = None + self.minute = None + self.second = None + self.microsecond = None + self._has_time = 0 + + # Get year / month delta between the two + months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month) + self._set_months(months) + + # Remove the year/month delta so the timedelta is just well-defined + # time units (seconds, days and microseconds) + dtm = self.__radd__(dt2) + + # If we've overshot our target, make an adjustment + if dt1 < dt2: + compare = operator.gt + increment = 1 + else: + compare = operator.lt + increment = -1 + + while compare(dt1, dtm): + months += increment + self._set_months(months) + dtm = self.__radd__(dt2) + + # Get the timedelta between the "months-adjusted" date and dt1 + delta = dt1 - dtm + self.seconds = delta.seconds + delta.days * 86400 + self.microseconds = delta.microseconds + else: + # Check for non-integer values in integer-only quantities + if any(x is not None and x != int(x) for x in (years, months)): + raise ValueError("Non-integer years and months are " + "ambiguous and not currently supported.") + + # Relative information + self.years = int(years) + self.months = int(months) + self.days = days + weeks * 7 + self.leapdays = leapdays + self.hours = hours + self.minutes = minutes + self.seconds = seconds + self.microseconds = microseconds + + # Absolute information + self.year = year + self.month = month + self.day = day + self.hour = hour + self.minute = minute + self.second = second + self.microsecond = microsecond + + if any(x is not None and int(x) != x + for x in (year, month, day, hour, + minute, second, microsecond)): + # For now we'll deprecate floats - later it'll be an error. + warn("Non-integer value passed as absolute information. " + + "This is not a well-defined condition and will raise " + + "errors in future versions.", DeprecationWarning) + + if isinstance(weekday, integer_types): + self.weekday = weekdays[weekday] + else: + self.weekday = weekday + + yday = 0 + if nlyearday: + yday = nlyearday + elif yearday: + yday = yearday + if yearday > 59: + self.leapdays = -1 + if yday: + ydayidx = [31, 59, 90, 120, 151, 181, 212, + 243, 273, 304, 334, 366] + for idx, ydays in enumerate(ydayidx): + if yday <= ydays: + self.month = idx+1 + if idx == 0: + self.day = yday + else: + self.day = yday-ydayidx[idx-1] + break + else: + raise ValueError("invalid year day (%d)" % yday) + + self._fix() + + def _fix(self): + if abs(self.microseconds) > 999999: + s = _sign(self.microseconds) + div, mod = divmod(self.microseconds * s, 1000000) + self.microseconds = mod * s + self.seconds += div * s + if abs(self.seconds) > 59: + s = _sign(self.seconds) + div, mod = divmod(self.seconds * s, 60) + self.seconds = mod * s + self.minutes += div * s + if abs(self.minutes) > 59: + s = _sign(self.minutes) + div, mod = divmod(self.minutes * s, 60) + self.minutes = mod * s + self.hours += div * s + if abs(self.hours) > 23: + s = _sign(self.hours) + div, mod = divmod(self.hours * s, 24) + self.hours = mod * s + self.days += div * s + if abs(self.months) > 11: + s = _sign(self.months) + div, mod = divmod(self.months * s, 12) + self.months = mod * s + self.years += div * s + if (self.hours or self.minutes or self.seconds or self.microseconds + or self.hour is not None or self.minute is not None or + self.second is not None or self.microsecond is not None): + self._has_time = 1 + else: + self._has_time = 0 + + @property + def weeks(self): + return int(self.days / 7.0) + + @weeks.setter + def weeks(self, value): + self.days = self.days - (self.weeks * 7) + value * 7 + + def _set_months(self, months): + self.months = months + if abs(self.months) > 11: + s = _sign(self.months) + div, mod = divmod(self.months * s, 12) + self.months = mod * s + self.years = div * s + else: + self.years = 0 + + def normalized(self): + """ + Return a version of this object represented entirely using integer + values for the relative attributes. + + >>> relativedelta(days=1.5, hours=2).normalized() + relativedelta(days=+1, hours=+14) + + :return: + Returns a :class:`dateutil.relativedelta.relativedelta` object. + """ + # Cascade remainders down (rounding each to roughly nearest microsecond) + days = int(self.days) + + hours_f = round(self.hours + 24 * (self.days - days), 11) + hours = int(hours_f) + + minutes_f = round(self.minutes + 60 * (hours_f - hours), 10) + minutes = int(minutes_f) + + seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8) + seconds = int(seconds_f) + + microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds)) + + # Constructor carries overflow back up with call to _fix() + return self.__class__(years=self.years, months=self.months, + days=days, hours=hours, minutes=minutes, + seconds=seconds, microseconds=microseconds, + leapdays=self.leapdays, year=self.year, + month=self.month, day=self.day, + weekday=self.weekday, hour=self.hour, + minute=self.minute, second=self.second, + microsecond=self.microsecond) + + def __add__(self, other): + if isinstance(other, relativedelta): + return self.__class__(years=other.years + self.years, + months=other.months + self.months, + days=other.days + self.days, + hours=other.hours + self.hours, + minutes=other.minutes + self.minutes, + seconds=other.seconds + self.seconds, + microseconds=(other.microseconds + + self.microseconds), + leapdays=other.leapdays or self.leapdays, + year=(other.year if other.year is not None + else self.year), + month=(other.month if other.month is not None + else self.month), + day=(other.day if other.day is not None + else self.day), + weekday=(other.weekday if other.weekday is not None + else self.weekday), + hour=(other.hour if other.hour is not None + else self.hour), + minute=(other.minute if other.minute is not None + else self.minute), + second=(other.second if other.second is not None + else self.second), + microsecond=(other.microsecond if other.microsecond + is not None else + self.microsecond)) + if isinstance(other, datetime.timedelta): + return self.__class__(years=self.years, + months=self.months, + days=self.days + other.days, + hours=self.hours, + minutes=self.minutes, + seconds=self.seconds + other.seconds, + microseconds=self.microseconds + other.microseconds, + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + if not isinstance(other, datetime.date): + return NotImplemented + elif self._has_time and not isinstance(other, datetime.datetime): + other = datetime.datetime.fromordinal(other.toordinal()) + year = (self.year or other.year)+self.years + month = self.month or other.month + if self.months: + assert 1 <= abs(self.months) <= 12 + month += self.months + if month > 12: + year += 1 + month -= 12 + elif month < 1: + year -= 1 + month += 12 + day = min(calendar.monthrange(year, month)[1], + self.day or other.day) + repl = {"year": year, "month": month, "day": day} + for attr in ["hour", "minute", "second", "microsecond"]: + value = getattr(self, attr) + if value is not None: + repl[attr] = value + days = self.days + if self.leapdays and month > 2 and calendar.isleap(year): + days += self.leapdays + ret = (other.replace(**repl) + + datetime.timedelta(days=days, + hours=self.hours, + minutes=self.minutes, + seconds=self.seconds, + microseconds=self.microseconds)) + if self.weekday: + weekday, nth = self.weekday.weekday, self.weekday.n or 1 + jumpdays = (abs(nth) - 1) * 7 + if nth > 0: + jumpdays += (7 - ret.weekday() + weekday) % 7 + else: + jumpdays += (ret.weekday() - weekday) % 7 + jumpdays *= -1 + ret += datetime.timedelta(days=jumpdays) + return ret + + def __radd__(self, other): + return self.__add__(other) + + def __rsub__(self, other): + return self.__neg__().__radd__(other) + + def __sub__(self, other): + if not isinstance(other, relativedelta): + return NotImplemented # In case the other object defines __rsub__ + return self.__class__(years=self.years - other.years, + months=self.months - other.months, + days=self.days - other.days, + hours=self.hours - other.hours, + minutes=self.minutes - other.minutes, + seconds=self.seconds - other.seconds, + microseconds=self.microseconds - other.microseconds, + leapdays=self.leapdays or other.leapdays, + year=(self.year if self.year is not None + else other.year), + month=(self.month if self.month is not None else + other.month), + day=(self.day if self.day is not None else + other.day), + weekday=(self.weekday if self.weekday is not None else + other.weekday), + hour=(self.hour if self.hour is not None else + other.hour), + minute=(self.minute if self.minute is not None else + other.minute), + second=(self.second if self.second is not None else + other.second), + microsecond=(self.microsecond if self.microsecond + is not None else + other.microsecond)) + + def __abs__(self): + return self.__class__(years=abs(self.years), + months=abs(self.months), + days=abs(self.days), + hours=abs(self.hours), + minutes=abs(self.minutes), + seconds=abs(self.seconds), + microseconds=abs(self.microseconds), + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + def __neg__(self): + return self.__class__(years=-self.years, + months=-self.months, + days=-self.days, + hours=-self.hours, + minutes=-self.minutes, + seconds=-self.seconds, + microseconds=-self.microseconds, + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + def __bool__(self): + return not (not self.years and + not self.months and + not self.days and + not self.hours and + not self.minutes and + not self.seconds and + not self.microseconds and + not self.leapdays and + self.year is None and + self.month is None and + self.day is None and + self.weekday is None and + self.hour is None and + self.minute is None and + self.second is None and + self.microsecond is None) + # Compatibility with Python 2.x + __nonzero__ = __bool__ + + def __mul__(self, other): + try: + f = float(other) + except TypeError: + return NotImplemented + + return self.__class__(years=int(self.years * f), + months=int(self.months * f), + days=int(self.days * f), + hours=int(self.hours * f), + minutes=int(self.minutes * f), + seconds=int(self.seconds * f), + microseconds=int(self.microseconds * f), + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + __rmul__ = __mul__ + + def __eq__(self, other): + if not isinstance(other, relativedelta): + return NotImplemented + if self.weekday or other.weekday: + if not self.weekday or not other.weekday: + return False + if self.weekday.weekday != other.weekday.weekday: + return False + n1, n2 = self.weekday.n, other.weekday.n + if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): + return False + return (self.years == other.years and + self.months == other.months and + self.days == other.days and + self.hours == other.hours and + self.minutes == other.minutes and + self.seconds == other.seconds and + self.microseconds == other.microseconds and + self.leapdays == other.leapdays and + self.year == other.year and + self.month == other.month and + self.day == other.day and + self.hour == other.hour and + self.minute == other.minute and + self.second == other.second and + self.microsecond == other.microsecond) + + def __hash__(self): + return hash(( + self.weekday, + self.years, + self.months, + self.days, + self.hours, + self.minutes, + self.seconds, + self.microseconds, + self.leapdays, + self.year, + self.month, + self.day, + self.hour, + self.minute, + self.second, + self.microsecond, + )) + + def __ne__(self, other): + return not self.__eq__(other) + + def __div__(self, other): + try: + reciprocal = 1 / float(other) + except TypeError: + return NotImplemented + + return self.__mul__(reciprocal) + + __truediv__ = __div__ + + def __repr__(self): + l = [] + for attr in ["years", "months", "days", "leapdays", + "hours", "minutes", "seconds", "microseconds"]: + value = getattr(self, attr) + if value: + l.append("{attr}={value:+g}".format(attr=attr, value=value)) + for attr in ["year", "month", "day", "weekday", + "hour", "minute", "second", "microsecond"]: + value = getattr(self, attr) + if value is not None: + l.append("{attr}={value}".format(attr=attr, value=repr(value))) + return "{classname}({attrs})".format(classname=self.__class__.__name__, + attrs=", ".join(l)) + + +def _sign(x): + return int(copysign(1, x)) + +# vim:ts=4:sw=4:et diff --git a/venv/lib/python3.12/site-packages/dateutil/rrule.py b/venv/lib/python3.12/site-packages/dateutil/rrule.py new file mode 100644 index 0000000..571a0d2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/rrule.py @@ -0,0 +1,1737 @@ +# -*- coding: utf-8 -*- +""" +The rrule module offers a small, complete, and very fast, implementation of +the recurrence rules documented in the +`iCalendar RFC `_, +including support for caching of results. +""" +import calendar +import datetime +import heapq +import itertools +import re +import sys +from functools import wraps +# For warning about deprecation of until and count +from warnings import warn + +from six import advance_iterator, integer_types + +from six.moves import _thread, range + +from ._common import weekday as weekdaybase + +try: + from math import gcd +except ImportError: + from fractions import gcd + +__all__ = ["rrule", "rruleset", "rrulestr", + "YEARLY", "MONTHLY", "WEEKLY", "DAILY", + "HOURLY", "MINUTELY", "SECONDLY", + "MO", "TU", "WE", "TH", "FR", "SA", "SU"] + +# Every mask is 7 days longer to handle cross-year weekly periods. +M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30 + + [7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7) +M365MASK = list(M366MASK) +M29, M30, M31 = list(range(1, 30)), list(range(1, 31)), list(range(1, 32)) +MDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) +MDAY365MASK = list(MDAY366MASK) +M29, M30, M31 = list(range(-29, 0)), list(range(-30, 0)), list(range(-31, 0)) +NMDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) +NMDAY365MASK = list(NMDAY366MASK) +M366RANGE = (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366) +M365RANGE = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365) +WDAYMASK = [0, 1, 2, 3, 4, 5, 6]*55 +del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31] +MDAY365MASK = tuple(MDAY365MASK) +M365MASK = tuple(M365MASK) + +FREQNAMES = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY'] + +(YEARLY, + MONTHLY, + WEEKLY, + DAILY, + HOURLY, + MINUTELY, + SECONDLY) = list(range(7)) + +# Imported on demand. +easter = None +parser = None + + +class weekday(weekdaybase): + """ + This version of weekday does not allow n = 0. + """ + def __init__(self, wkday, n=None): + if n == 0: + raise ValueError("Can't create weekday with n==0") + + super(weekday, self).__init__(wkday, n) + + +MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) + + +def _invalidates_cache(f): + """ + Decorator for rruleset methods which may invalidate the + cached length. + """ + @wraps(f) + def inner_func(self, *args, **kwargs): + rv = f(self, *args, **kwargs) + self._invalidate_cache() + return rv + + return inner_func + + +class rrulebase(object): + def __init__(self, cache=False): + if cache: + self._cache = [] + self._cache_lock = _thread.allocate_lock() + self._invalidate_cache() + else: + self._cache = None + self._cache_complete = False + self._len = None + + def __iter__(self): + if self._cache_complete: + return iter(self._cache) + elif self._cache is None: + return self._iter() + else: + return self._iter_cached() + + def _invalidate_cache(self): + if self._cache is not None: + self._cache = [] + self._cache_complete = False + self._cache_gen = self._iter() + + if self._cache_lock.locked(): + self._cache_lock.release() + + self._len = None + + def _iter_cached(self): + i = 0 + gen = self._cache_gen + cache = self._cache + acquire = self._cache_lock.acquire + release = self._cache_lock.release + while gen: + if i == len(cache): + acquire() + if self._cache_complete: + break + try: + for j in range(10): + cache.append(advance_iterator(gen)) + except StopIteration: + self._cache_gen = gen = None + self._cache_complete = True + break + release() + yield cache[i] + i += 1 + while i < self._len: + yield cache[i] + i += 1 + + def __getitem__(self, item): + if self._cache_complete: + return self._cache[item] + elif isinstance(item, slice): + if item.step and item.step < 0: + return list(iter(self))[item] + else: + return list(itertools.islice(self, + item.start or 0, + item.stop or sys.maxsize, + item.step or 1)) + elif item >= 0: + gen = iter(self) + try: + for i in range(item+1): + res = advance_iterator(gen) + except StopIteration: + raise IndexError + return res + else: + return list(iter(self))[item] + + def __contains__(self, item): + if self._cache_complete: + return item in self._cache + else: + for i in self: + if i == item: + return True + elif i > item: + return False + return False + + # __len__() introduces a large performance penalty. + def count(self): + """ Returns the number of recurrences in this set. It will have go + through the whole recurrence, if this hasn't been done before. """ + if self._len is None: + for x in self: + pass + return self._len + + def before(self, dt, inc=False): + """ Returns the last recurrence before the given datetime instance. The + inc keyword defines what happens if dt is an occurrence. With + inc=True, if dt itself is an occurrence, it will be returned. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + last = None + if inc: + for i in gen: + if i > dt: + break + last = i + else: + for i in gen: + if i >= dt: + break + last = i + return last + + def after(self, dt, inc=False): + """ Returns the first recurrence after the given datetime instance. The + inc keyword defines what happens if dt is an occurrence. With + inc=True, if dt itself is an occurrence, it will be returned. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + if inc: + for i in gen: + if i >= dt: + return i + else: + for i in gen: + if i > dt: + return i + return None + + def xafter(self, dt, count=None, inc=False): + """ + Generator which yields up to `count` recurrences after the given + datetime instance, equivalent to `after`. + + :param dt: + The datetime at which to start generating recurrences. + + :param count: + The maximum number of recurrences to generate. If `None` (default), + dates are generated until the recurrence rule is exhausted. + + :param inc: + If `dt` is an instance of the rule and `inc` is `True`, it is + included in the output. + + :yields: Yields a sequence of `datetime` objects. + """ + + if self._cache_complete: + gen = self._cache + else: + gen = self + + # Select the comparison function + if inc: + comp = lambda dc, dtc: dc >= dtc + else: + comp = lambda dc, dtc: dc > dtc + + # Generate dates + n = 0 + for d in gen: + if comp(d, dt): + if count is not None: + n += 1 + if n > count: + break + + yield d + + def between(self, after, before, inc=False, count=1): + """ Returns all the occurrences of the rrule between after and before. + The inc keyword defines what happens if after and/or before are + themselves occurrences. With inc=True, they will be included in the + list, if they are found in the recurrence set. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + started = False + l = [] + if inc: + for i in gen: + if i > before: + break + elif not started: + if i >= after: + started = True + l.append(i) + else: + l.append(i) + else: + for i in gen: + if i >= before: + break + elif not started: + if i > after: + started = True + l.append(i) + else: + l.append(i) + return l + + +class rrule(rrulebase): + """ + That's the base of the rrule operation. It accepts all the keywords + defined in the RFC as its constructor parameters (except byday, + which was renamed to byweekday) and more. The constructor prototype is:: + + rrule(freq) + + Where freq must be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, + or SECONDLY. + + .. note:: + Per RFC section 3.3.10, recurrence instances falling on invalid dates + and times are ignored rather than coerced: + + Recurrence rules may generate recurrence instances with an invalid + date (e.g., February 30) or nonexistent local time (e.g., 1:30 AM + on a day where the local time is moved forward by an hour at 1:00 + AM). Such recurrence instances MUST be ignored and MUST NOT be + counted as part of the recurrence set. + + This can lead to possibly surprising behavior when, for example, the + start date occurs at the end of the month: + + >>> from dateutil.rrule import rrule, MONTHLY + >>> from datetime import datetime + >>> start_date = datetime(2014, 12, 31) + >>> list(rrule(freq=MONTHLY, count=4, dtstart=start_date)) + ... # doctest: +NORMALIZE_WHITESPACE + [datetime.datetime(2014, 12, 31, 0, 0), + datetime.datetime(2015, 1, 31, 0, 0), + datetime.datetime(2015, 3, 31, 0, 0), + datetime.datetime(2015, 5, 31, 0, 0)] + + Additionally, it supports the following keyword arguments: + + :param dtstart: + The recurrence start. Besides being the base for the recurrence, + missing parameters in the final recurrence instances will also be + extracted from this date. If not given, datetime.now() will be used + instead. + :param interval: + The interval between each freq iteration. For example, when using + YEARLY, an interval of 2 means once every two years, but with HOURLY, + it means once every two hours. The default interval is 1. + :param wkst: + The week start day. Must be one of the MO, TU, WE constants, or an + integer, specifying the first day of the week. This will affect + recurrences based on weekly periods. The default week start is got + from calendar.firstweekday(), and may be modified by + calendar.setfirstweekday(). + :param count: + If given, this determines how many occurrences will be generated. + + .. note:: + As of version 2.5.0, the use of the keyword ``until`` in conjunction + with ``count`` is deprecated, to make sure ``dateutil`` is fully + compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` + **must not** occur in the same call to ``rrule``. + :param until: + If given, this must be a datetime instance specifying the upper-bound + limit of the recurrence. The last recurrence in the rule is the greatest + datetime that is less than or equal to the value specified in the + ``until`` parameter. + + .. note:: + As of version 2.5.0, the use of the keyword ``until`` in conjunction + with ``count`` is deprecated, to make sure ``dateutil`` is fully + compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` + **must not** occur in the same call to ``rrule``. + :param bysetpos: + If given, it must be either an integer, or a sequence of integers, + positive or negative. Each given integer will specify an occurrence + number, corresponding to the nth occurrence of the rule inside the + frequency period. For example, a bysetpos of -1 if combined with a + MONTHLY frequency, and a byweekday of (MO, TU, WE, TH, FR), will + result in the last work day of every month. + :param bymonth: + If given, it must be either an integer, or a sequence of integers, + meaning the months to apply the recurrence to. + :param bymonthday: + If given, it must be either an integer, or a sequence of integers, + meaning the month days to apply the recurrence to. + :param byyearday: + If given, it must be either an integer, or a sequence of integers, + meaning the year days to apply the recurrence to. + :param byeaster: + If given, it must be either an integer, or a sequence of integers, + positive or negative. Each integer will define an offset from the + Easter Sunday. Passing the offset 0 to byeaster will yield the Easter + Sunday itself. This is an extension to the RFC specification. + :param byweekno: + If given, it must be either an integer, or a sequence of integers, + meaning the week numbers to apply the recurrence to. Week numbers + have the meaning described in ISO8601, that is, the first week of + the year is that containing at least four days of the new year. + :param byweekday: + If given, it must be either an integer (0 == MO), a sequence of + integers, one of the weekday constants (MO, TU, etc), or a sequence + of these constants. When given, these variables will define the + weekdays where the recurrence will be applied. It's also possible to + use an argument n for the weekday instances, which will mean the nth + occurrence of this weekday in the period. For example, with MONTHLY, + or with YEARLY and BYMONTH, using FR(+1) in byweekday will specify the + first friday of the month where the recurrence happens. Notice that in + the RFC documentation, this is specified as BYDAY, but was renamed to + avoid the ambiguity of that keyword. + :param byhour: + If given, it must be either an integer, or a sequence of integers, + meaning the hours to apply the recurrence to. + :param byminute: + If given, it must be either an integer, or a sequence of integers, + meaning the minutes to apply the recurrence to. + :param bysecond: + If given, it must be either an integer, or a sequence of integers, + meaning the seconds to apply the recurrence to. + :param cache: + If given, it must be a boolean value specifying to enable or disable + caching of results. If you will use the same rrule instance multiple + times, enabling caching will improve the performance considerably. + """ + def __init__(self, freq, dtstart=None, + interval=1, wkst=None, count=None, until=None, bysetpos=None, + bymonth=None, bymonthday=None, byyearday=None, byeaster=None, + byweekno=None, byweekday=None, + byhour=None, byminute=None, bysecond=None, + cache=False): + super(rrule, self).__init__(cache) + global easter + if not dtstart: + if until and until.tzinfo: + dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0) + else: + dtstart = datetime.datetime.now().replace(microsecond=0) + elif not isinstance(dtstart, datetime.datetime): + dtstart = datetime.datetime.fromordinal(dtstart.toordinal()) + else: + dtstart = dtstart.replace(microsecond=0) + self._dtstart = dtstart + self._tzinfo = dtstart.tzinfo + self._freq = freq + self._interval = interval + self._count = count + + # Cache the original byxxx rules, if they are provided, as the _byxxx + # attributes do not necessarily map to the inputs, and this can be + # a problem in generating the strings. Only store things if they've + # been supplied (the string retrieval will just use .get()) + self._original_rule = {} + + if until and not isinstance(until, datetime.datetime): + until = datetime.datetime.fromordinal(until.toordinal()) + self._until = until + + if self._dtstart and self._until: + if (self._dtstart.tzinfo is not None) != (self._until.tzinfo is not None): + # According to RFC5545 Section 3.3.10: + # https://tools.ietf.org/html/rfc5545#section-3.3.10 + # + # > If the "DTSTART" property is specified as a date with UTC + # > time or a date with local time and time zone reference, + # > then the UNTIL rule part MUST be specified as a date with + # > UTC time. + raise ValueError( + 'RRULE UNTIL values must be specified in UTC when DTSTART ' + 'is timezone-aware' + ) + + if count is not None and until: + warn("Using both 'count' and 'until' is inconsistent with RFC 5545" + " and has been deprecated in dateutil. Future versions will " + "raise an error.", DeprecationWarning) + + if wkst is None: + self._wkst = calendar.firstweekday() + elif isinstance(wkst, integer_types): + self._wkst = wkst + else: + self._wkst = wkst.weekday + + if bysetpos is None: + self._bysetpos = None + elif isinstance(bysetpos, integer_types): + if bysetpos == 0 or not (-366 <= bysetpos <= 366): + raise ValueError("bysetpos must be between 1 and 366, " + "or between -366 and -1") + self._bysetpos = (bysetpos,) + else: + self._bysetpos = tuple(bysetpos) + for pos in self._bysetpos: + if pos == 0 or not (-366 <= pos <= 366): + raise ValueError("bysetpos must be between 1 and 366, " + "or between -366 and -1") + + if self._bysetpos: + self._original_rule['bysetpos'] = self._bysetpos + + if (byweekno is None and byyearday is None and bymonthday is None and + byweekday is None and byeaster is None): + if freq == YEARLY: + if bymonth is None: + bymonth = dtstart.month + self._original_rule['bymonth'] = None + bymonthday = dtstart.day + self._original_rule['bymonthday'] = None + elif freq == MONTHLY: + bymonthday = dtstart.day + self._original_rule['bymonthday'] = None + elif freq == WEEKLY: + byweekday = dtstart.weekday() + self._original_rule['byweekday'] = None + + # bymonth + if bymonth is None: + self._bymonth = None + else: + if isinstance(bymonth, integer_types): + bymonth = (bymonth,) + + self._bymonth = tuple(sorted(set(bymonth))) + + if 'bymonth' not in self._original_rule: + self._original_rule['bymonth'] = self._bymonth + + # byyearday + if byyearday is None: + self._byyearday = None + else: + if isinstance(byyearday, integer_types): + byyearday = (byyearday,) + + self._byyearday = tuple(sorted(set(byyearday))) + self._original_rule['byyearday'] = self._byyearday + + # byeaster + if byeaster is not None: + if not easter: + from dateutil import easter + if isinstance(byeaster, integer_types): + self._byeaster = (byeaster,) + else: + self._byeaster = tuple(sorted(byeaster)) + + self._original_rule['byeaster'] = self._byeaster + else: + self._byeaster = None + + # bymonthday + if bymonthday is None: + self._bymonthday = () + self._bynmonthday = () + else: + if isinstance(bymonthday, integer_types): + bymonthday = (bymonthday,) + + bymonthday = set(bymonthday) # Ensure it's unique + + self._bymonthday = tuple(sorted(x for x in bymonthday if x > 0)) + self._bynmonthday = tuple(sorted(x for x in bymonthday if x < 0)) + + # Storing positive numbers first, then negative numbers + if 'bymonthday' not in self._original_rule: + self._original_rule['bymonthday'] = tuple( + itertools.chain(self._bymonthday, self._bynmonthday)) + + # byweekno + if byweekno is None: + self._byweekno = None + else: + if isinstance(byweekno, integer_types): + byweekno = (byweekno,) + + self._byweekno = tuple(sorted(set(byweekno))) + + self._original_rule['byweekno'] = self._byweekno + + # byweekday / bynweekday + if byweekday is None: + self._byweekday = None + self._bynweekday = None + else: + # If it's one of the valid non-sequence types, convert to a + # single-element sequence before the iterator that builds the + # byweekday set. + if isinstance(byweekday, integer_types) or hasattr(byweekday, "n"): + byweekday = (byweekday,) + + self._byweekday = set() + self._bynweekday = set() + for wday in byweekday: + if isinstance(wday, integer_types): + self._byweekday.add(wday) + elif not wday.n or freq > MONTHLY: + self._byweekday.add(wday.weekday) + else: + self._bynweekday.add((wday.weekday, wday.n)) + + if not self._byweekday: + self._byweekday = None + elif not self._bynweekday: + self._bynweekday = None + + if self._byweekday is not None: + self._byweekday = tuple(sorted(self._byweekday)) + orig_byweekday = [weekday(x) for x in self._byweekday] + else: + orig_byweekday = () + + if self._bynweekday is not None: + self._bynweekday = tuple(sorted(self._bynweekday)) + orig_bynweekday = [weekday(*x) for x in self._bynweekday] + else: + orig_bynweekday = () + + if 'byweekday' not in self._original_rule: + self._original_rule['byweekday'] = tuple(itertools.chain( + orig_byweekday, orig_bynweekday)) + + # byhour + if byhour is None: + if freq < HOURLY: + self._byhour = {dtstart.hour} + else: + self._byhour = None + else: + if isinstance(byhour, integer_types): + byhour = (byhour,) + + if freq == HOURLY: + self._byhour = self.__construct_byset(start=dtstart.hour, + byxxx=byhour, + base=24) + else: + self._byhour = set(byhour) + + self._byhour = tuple(sorted(self._byhour)) + self._original_rule['byhour'] = self._byhour + + # byminute + if byminute is None: + if freq < MINUTELY: + self._byminute = {dtstart.minute} + else: + self._byminute = None + else: + if isinstance(byminute, integer_types): + byminute = (byminute,) + + if freq == MINUTELY: + self._byminute = self.__construct_byset(start=dtstart.minute, + byxxx=byminute, + base=60) + else: + self._byminute = set(byminute) + + self._byminute = tuple(sorted(self._byminute)) + self._original_rule['byminute'] = self._byminute + + # bysecond + if bysecond is None: + if freq < SECONDLY: + self._bysecond = ((dtstart.second,)) + else: + self._bysecond = None + else: + if isinstance(bysecond, integer_types): + bysecond = (bysecond,) + + self._bysecond = set(bysecond) + + if freq == SECONDLY: + self._bysecond = self.__construct_byset(start=dtstart.second, + byxxx=bysecond, + base=60) + else: + self._bysecond = set(bysecond) + + self._bysecond = tuple(sorted(self._bysecond)) + self._original_rule['bysecond'] = self._bysecond + + if self._freq >= HOURLY: + self._timeset = None + else: + self._timeset = [] + for hour in self._byhour: + for minute in self._byminute: + for second in self._bysecond: + self._timeset.append( + datetime.time(hour, minute, second, + tzinfo=self._tzinfo)) + self._timeset.sort() + self._timeset = tuple(self._timeset) + + def __str__(self): + """ + Output a string that would generate this RRULE if passed to rrulestr. + This is mostly compatible with RFC5545, except for the + dateutil-specific extension BYEASTER. + """ + + output = [] + h, m, s = [None] * 3 + if self._dtstart: + output.append(self._dtstart.strftime('DTSTART:%Y%m%dT%H%M%S')) + h, m, s = self._dtstart.timetuple()[3:6] + + parts = ['FREQ=' + FREQNAMES[self._freq]] + if self._interval != 1: + parts.append('INTERVAL=' + str(self._interval)) + + if self._wkst: + parts.append('WKST=' + repr(weekday(self._wkst))[0:2]) + + if self._count is not None: + parts.append('COUNT=' + str(self._count)) + + if self._until: + parts.append(self._until.strftime('UNTIL=%Y%m%dT%H%M%S')) + + if self._original_rule.get('byweekday') is not None: + # The str() method on weekday objects doesn't generate + # RFC5545-compliant strings, so we should modify that. + original_rule = dict(self._original_rule) + wday_strings = [] + for wday in original_rule['byweekday']: + if wday.n: + wday_strings.append('{n:+d}{wday}'.format( + n=wday.n, + wday=repr(wday)[0:2])) + else: + wday_strings.append(repr(wday)) + + original_rule['byweekday'] = wday_strings + else: + original_rule = self._original_rule + + partfmt = '{name}={vals}' + for name, key in [('BYSETPOS', 'bysetpos'), + ('BYMONTH', 'bymonth'), + ('BYMONTHDAY', 'bymonthday'), + ('BYYEARDAY', 'byyearday'), + ('BYWEEKNO', 'byweekno'), + ('BYDAY', 'byweekday'), + ('BYHOUR', 'byhour'), + ('BYMINUTE', 'byminute'), + ('BYSECOND', 'bysecond'), + ('BYEASTER', 'byeaster')]: + value = original_rule.get(key) + if value: + parts.append(partfmt.format(name=name, vals=(','.join(str(v) + for v in value)))) + + output.append('RRULE:' + ';'.join(parts)) + return '\n'.join(output) + + def replace(self, **kwargs): + """Return new rrule with same attributes except for those attributes given new + values by whichever keyword arguments are specified.""" + new_kwargs = {"interval": self._interval, + "count": self._count, + "dtstart": self._dtstart, + "freq": self._freq, + "until": self._until, + "wkst": self._wkst, + "cache": False if self._cache is None else True } + new_kwargs.update(self._original_rule) + new_kwargs.update(kwargs) + return rrule(**new_kwargs) + + def _iter(self): + year, month, day, hour, minute, second, weekday, yearday, _ = \ + self._dtstart.timetuple() + + # Some local variables to speed things up a bit + freq = self._freq + interval = self._interval + wkst = self._wkst + until = self._until + bymonth = self._bymonth + byweekno = self._byweekno + byyearday = self._byyearday + byweekday = self._byweekday + byeaster = self._byeaster + bymonthday = self._bymonthday + bynmonthday = self._bynmonthday + bysetpos = self._bysetpos + byhour = self._byhour + byminute = self._byminute + bysecond = self._bysecond + + ii = _iterinfo(self) + ii.rebuild(year, month) + + getdayset = {YEARLY: ii.ydayset, + MONTHLY: ii.mdayset, + WEEKLY: ii.wdayset, + DAILY: ii.ddayset, + HOURLY: ii.ddayset, + MINUTELY: ii.ddayset, + SECONDLY: ii.ddayset}[freq] + + if freq < HOURLY: + timeset = self._timeset + else: + gettimeset = {HOURLY: ii.htimeset, + MINUTELY: ii.mtimeset, + SECONDLY: ii.stimeset}[freq] + if ((freq >= HOURLY and + self._byhour and hour not in self._byhour) or + (freq >= MINUTELY and + self._byminute and minute not in self._byminute) or + (freq >= SECONDLY and + self._bysecond and second not in self._bysecond)): + timeset = () + else: + timeset = gettimeset(hour, minute, second) + + total = 0 + count = self._count + while True: + # Get dayset with the right frequency + dayset, start, end = getdayset(year, month, day) + + # Do the "hard" work ;-) + filtered = False + for i in dayset[start:end]: + if ((bymonth and ii.mmask[i] not in bymonth) or + (byweekno and not ii.wnomask[i]) or + (byweekday and ii.wdaymask[i] not in byweekday) or + (ii.nwdaymask and not ii.nwdaymask[i]) or + (byeaster and not ii.eastermask[i]) or + ((bymonthday or bynmonthday) and + ii.mdaymask[i] not in bymonthday and + ii.nmdaymask[i] not in bynmonthday) or + (byyearday and + ((i < ii.yearlen and i+1 not in byyearday and + -ii.yearlen+i not in byyearday) or + (i >= ii.yearlen and i+1-ii.yearlen not in byyearday and + -ii.nextyearlen+i-ii.yearlen not in byyearday)))): + dayset[i] = None + filtered = True + + # Output results + if bysetpos and timeset: + poslist = [] + for pos in bysetpos: + if pos < 0: + daypos, timepos = divmod(pos, len(timeset)) + else: + daypos, timepos = divmod(pos-1, len(timeset)) + try: + i = [x for x in dayset[start:end] + if x is not None][daypos] + time = timeset[timepos] + except IndexError: + pass + else: + date = datetime.date.fromordinal(ii.yearordinal+i) + res = datetime.datetime.combine(date, time) + if res not in poslist: + poslist.append(res) + poslist.sort() + for res in poslist: + if until and res > until: + self._len = total + return + elif res >= self._dtstart: + if count is not None: + count -= 1 + if count < 0: + self._len = total + return + total += 1 + yield res + else: + for i in dayset[start:end]: + if i is not None: + date = datetime.date.fromordinal(ii.yearordinal + i) + for time in timeset: + res = datetime.datetime.combine(date, time) + if until and res > until: + self._len = total + return + elif res >= self._dtstart: + if count is not None: + count -= 1 + if count < 0: + self._len = total + return + + total += 1 + yield res + + # Handle frequency and interval + fixday = False + if freq == YEARLY: + year += interval + if year > datetime.MAXYEAR: + self._len = total + return + ii.rebuild(year, month) + elif freq == MONTHLY: + month += interval + if month > 12: + div, mod = divmod(month, 12) + month = mod + year += div + if month == 0: + month = 12 + year -= 1 + if year > datetime.MAXYEAR: + self._len = total + return + ii.rebuild(year, month) + elif freq == WEEKLY: + if wkst > weekday: + day += -(weekday+1+(6-wkst))+self._interval*7 + else: + day += -(weekday-wkst)+self._interval*7 + weekday = wkst + fixday = True + elif freq == DAILY: + day += interval + fixday = True + elif freq == HOURLY: + if filtered: + # Jump to one iteration before next day + hour += ((23-hour)//interval)*interval + + if byhour: + ndays, hour = self.__mod_distance(value=hour, + byxxx=self._byhour, + base=24) + else: + ndays, hour = divmod(hour+interval, 24) + + if ndays: + day += ndays + fixday = True + + timeset = gettimeset(hour, minute, second) + elif freq == MINUTELY: + if filtered: + # Jump to one iteration before next day + minute += ((1439-(hour*60+minute))//interval)*interval + + valid = False + rep_rate = (24*60) + for j in range(rep_rate // gcd(interval, rep_rate)): + if byminute: + nhours, minute = \ + self.__mod_distance(value=minute, + byxxx=self._byminute, + base=60) + else: + nhours, minute = divmod(minute+interval, 60) + + div, hour = divmod(hour+nhours, 24) + if div: + day += div + fixday = True + filtered = False + + if not byhour or hour in byhour: + valid = True + break + + if not valid: + raise ValueError('Invalid combination of interval and ' + + 'byhour resulting in empty rule.') + + timeset = gettimeset(hour, minute, second) + elif freq == SECONDLY: + if filtered: + # Jump to one iteration before next day + second += (((86399 - (hour * 3600 + minute * 60 + second)) + // interval) * interval) + + rep_rate = (24 * 3600) + valid = False + for j in range(0, rep_rate // gcd(interval, rep_rate)): + if bysecond: + nminutes, second = \ + self.__mod_distance(value=second, + byxxx=self._bysecond, + base=60) + else: + nminutes, second = divmod(second+interval, 60) + + div, minute = divmod(minute+nminutes, 60) + if div: + hour += div + div, hour = divmod(hour, 24) + if div: + day += div + fixday = True + + if ((not byhour or hour in byhour) and + (not byminute or minute in byminute) and + (not bysecond or second in bysecond)): + valid = True + break + + if not valid: + raise ValueError('Invalid combination of interval, ' + + 'byhour and byminute resulting in empty' + + ' rule.') + + timeset = gettimeset(hour, minute, second) + + if fixday and day > 28: + daysinmonth = calendar.monthrange(year, month)[1] + if day > daysinmonth: + while day > daysinmonth: + day -= daysinmonth + month += 1 + if month == 13: + month = 1 + year += 1 + if year > datetime.MAXYEAR: + self._len = total + return + daysinmonth = calendar.monthrange(year, month)[1] + ii.rebuild(year, month) + + def __construct_byset(self, start, byxxx, base): + """ + If a `BYXXX` sequence is passed to the constructor at the same level as + `FREQ` (e.g. `FREQ=HOURLY,BYHOUR={2,4,7},INTERVAL=3`), there are some + specifications which cannot be reached given some starting conditions. + + This occurs whenever the interval is not coprime with the base of a + given unit and the difference between the starting position and the + ending position is not coprime with the greatest common denominator + between the interval and the base. For example, with a FREQ of hourly + starting at 17:00 and an interval of 4, the only valid values for + BYHOUR would be {21, 1, 5, 9, 13, 17}, because 4 and 24 are not + coprime. + + :param start: + Specifies the starting position. + :param byxxx: + An iterable containing the list of allowed values. + :param base: + The largest allowable value for the specified frequency (e.g. + 24 hours, 60 minutes). + + This does not preserve the type of the iterable, returning a set, since + the values should be unique and the order is irrelevant, this will + speed up later lookups. + + In the event of an empty set, raises a :exception:`ValueError`, as this + results in an empty rrule. + """ + + cset = set() + + # Support a single byxxx value. + if isinstance(byxxx, integer_types): + byxxx = (byxxx, ) + + for num in byxxx: + i_gcd = gcd(self._interval, base) + # Use divmod rather than % because we need to wrap negative nums. + if i_gcd == 1 or divmod(num - start, i_gcd)[1] == 0: + cset.add(num) + + if len(cset) == 0: + raise ValueError("Invalid rrule byxxx generates an empty set.") + + return cset + + def __mod_distance(self, value, byxxx, base): + """ + Calculates the next value in a sequence where the `FREQ` parameter is + specified along with a `BYXXX` parameter at the same "level" + (e.g. `HOURLY` specified with `BYHOUR`). + + :param value: + The old value of the component. + :param byxxx: + The `BYXXX` set, which should have been generated by + `rrule._construct_byset`, or something else which checks that a + valid rule is present. + :param base: + The largest allowable value for the specified frequency (e.g. + 24 hours, 60 minutes). + + If a valid value is not found after `base` iterations (the maximum + number before the sequence would start to repeat), this raises a + :exception:`ValueError`, as no valid values were found. + + This returns a tuple of `divmod(n*interval, base)`, where `n` is the + smallest number of `interval` repetitions until the next specified + value in `byxxx` is found. + """ + accumulator = 0 + for ii in range(1, base + 1): + # Using divmod() over % to account for negative intervals + div, value = divmod(value + self._interval, base) + accumulator += div + if value in byxxx: + return (accumulator, value) + + +class _iterinfo(object): + __slots__ = ["rrule", "lastyear", "lastmonth", + "yearlen", "nextyearlen", "yearordinal", "yearweekday", + "mmask", "mrange", "mdaymask", "nmdaymask", + "wdaymask", "wnomask", "nwdaymask", "eastermask"] + + def __init__(self, rrule): + for attr in self.__slots__: + setattr(self, attr, None) + self.rrule = rrule + + def rebuild(self, year, month): + # Every mask is 7 days longer to handle cross-year weekly periods. + rr = self.rrule + if year != self.lastyear: + self.yearlen = 365 + calendar.isleap(year) + self.nextyearlen = 365 + calendar.isleap(year + 1) + firstyday = datetime.date(year, 1, 1) + self.yearordinal = firstyday.toordinal() + self.yearweekday = firstyday.weekday() + + wday = datetime.date(year, 1, 1).weekday() + if self.yearlen == 365: + self.mmask = M365MASK + self.mdaymask = MDAY365MASK + self.nmdaymask = NMDAY365MASK + self.wdaymask = WDAYMASK[wday:] + self.mrange = M365RANGE + else: + self.mmask = M366MASK + self.mdaymask = MDAY366MASK + self.nmdaymask = NMDAY366MASK + self.wdaymask = WDAYMASK[wday:] + self.mrange = M366RANGE + + if not rr._byweekno: + self.wnomask = None + else: + self.wnomask = [0]*(self.yearlen+7) + # no1wkst = firstwkst = self.wdaymask.index(rr._wkst) + no1wkst = firstwkst = (7-self.yearweekday+rr._wkst) % 7 + if no1wkst >= 4: + no1wkst = 0 + # Number of days in the year, plus the days we got + # from last year. + wyearlen = self.yearlen+(self.yearweekday-rr._wkst) % 7 + else: + # Number of days in the year, minus the days we + # left in last year. + wyearlen = self.yearlen-no1wkst + div, mod = divmod(wyearlen, 7) + numweeks = div+mod//4 + for n in rr._byweekno: + if n < 0: + n += numweeks+1 + if not (0 < n <= numweeks): + continue + if n > 1: + i = no1wkst+(n-1)*7 + if no1wkst != firstwkst: + i -= 7-firstwkst + else: + i = no1wkst + for j in range(7): + self.wnomask[i] = 1 + i += 1 + if self.wdaymask[i] == rr._wkst: + break + if 1 in rr._byweekno: + # Check week number 1 of next year as well + # TODO: Check -numweeks for next year. + i = no1wkst+numweeks*7 + if no1wkst != firstwkst: + i -= 7-firstwkst + if i < self.yearlen: + # If week starts in next year, we + # don't care about it. + for j in range(7): + self.wnomask[i] = 1 + i += 1 + if self.wdaymask[i] == rr._wkst: + break + if no1wkst: + # Check last week number of last year as + # well. If no1wkst is 0, either the year + # started on week start, or week number 1 + # got days from last year, so there are no + # days from last year's last week number in + # this year. + if -1 not in rr._byweekno: + lyearweekday = datetime.date(year-1, 1, 1).weekday() + lno1wkst = (7-lyearweekday+rr._wkst) % 7 + lyearlen = 365+calendar.isleap(year-1) + if lno1wkst >= 4: + lno1wkst = 0 + lnumweeks = 52+(lyearlen + + (lyearweekday-rr._wkst) % 7) % 7//4 + else: + lnumweeks = 52+(self.yearlen-no1wkst) % 7//4 + else: + lnumweeks = -1 + if lnumweeks in rr._byweekno: + for i in range(no1wkst): + self.wnomask[i] = 1 + + if (rr._bynweekday and (month != self.lastmonth or + year != self.lastyear)): + ranges = [] + if rr._freq == YEARLY: + if rr._bymonth: + for month in rr._bymonth: + ranges.append(self.mrange[month-1:month+1]) + else: + ranges = [(0, self.yearlen)] + elif rr._freq == MONTHLY: + ranges = [self.mrange[month-1:month+1]] + if ranges: + # Weekly frequency won't get here, so we may not + # care about cross-year weekly periods. + self.nwdaymask = [0]*self.yearlen + for first, last in ranges: + last -= 1 + for wday, n in rr._bynweekday: + if n < 0: + i = last+(n+1)*7 + i -= (self.wdaymask[i]-wday) % 7 + else: + i = first+(n-1)*7 + i += (7-self.wdaymask[i]+wday) % 7 + if first <= i <= last: + self.nwdaymask[i] = 1 + + if rr._byeaster: + self.eastermask = [0]*(self.yearlen+7) + eyday = easter.easter(year).toordinal()-self.yearordinal + for offset in rr._byeaster: + self.eastermask[eyday+offset] = 1 + + self.lastyear = year + self.lastmonth = month + + def ydayset(self, year, month, day): + return list(range(self.yearlen)), 0, self.yearlen + + def mdayset(self, year, month, day): + dset = [None]*self.yearlen + start, end = self.mrange[month-1:month+1] + for i in range(start, end): + dset[i] = i + return dset, start, end + + def wdayset(self, year, month, day): + # We need to handle cross-year weeks here. + dset = [None]*(self.yearlen+7) + i = datetime.date(year, month, day).toordinal()-self.yearordinal + start = i + for j in range(7): + dset[i] = i + i += 1 + # if (not (0 <= i < self.yearlen) or + # self.wdaymask[i] == self.rrule._wkst): + # This will cross the year boundary, if necessary. + if self.wdaymask[i] == self.rrule._wkst: + break + return dset, start, i + + def ddayset(self, year, month, day): + dset = [None] * self.yearlen + i = datetime.date(year, month, day).toordinal() - self.yearordinal + dset[i] = i + return dset, i, i + 1 + + def htimeset(self, hour, minute, second): + tset = [] + rr = self.rrule + for minute in rr._byminute: + for second in rr._bysecond: + tset.append(datetime.time(hour, minute, second, + tzinfo=rr._tzinfo)) + tset.sort() + return tset + + def mtimeset(self, hour, minute, second): + tset = [] + rr = self.rrule + for second in rr._bysecond: + tset.append(datetime.time(hour, minute, second, tzinfo=rr._tzinfo)) + tset.sort() + return tset + + def stimeset(self, hour, minute, second): + return (datetime.time(hour, minute, second, + tzinfo=self.rrule._tzinfo),) + + +class rruleset(rrulebase): + """ The rruleset type allows more complex recurrence setups, mixing + multiple rules, dates, exclusion rules, and exclusion dates. The type + constructor takes the following keyword arguments: + + :param cache: If True, caching of results will be enabled, improving + performance of multiple queries considerably. """ + + class _genitem(object): + def __init__(self, genlist, gen): + try: + self.dt = advance_iterator(gen) + genlist.append(self) + except StopIteration: + pass + self.genlist = genlist + self.gen = gen + + def __next__(self): + try: + self.dt = advance_iterator(self.gen) + except StopIteration: + if self.genlist[0] is self: + heapq.heappop(self.genlist) + else: + self.genlist.remove(self) + heapq.heapify(self.genlist) + + next = __next__ + + def __lt__(self, other): + return self.dt < other.dt + + def __gt__(self, other): + return self.dt > other.dt + + def __eq__(self, other): + return self.dt == other.dt + + def __ne__(self, other): + return self.dt != other.dt + + def __init__(self, cache=False): + super(rruleset, self).__init__(cache) + self._rrule = [] + self._rdate = [] + self._exrule = [] + self._exdate = [] + + @_invalidates_cache + def rrule(self, rrule): + """ Include the given :py:class:`rrule` instance in the recurrence set + generation. """ + self._rrule.append(rrule) + + @_invalidates_cache + def rdate(self, rdate): + """ Include the given :py:class:`datetime` instance in the recurrence + set generation. """ + self._rdate.append(rdate) + + @_invalidates_cache + def exrule(self, exrule): + """ Include the given rrule instance in the recurrence set exclusion + list. Dates which are part of the given recurrence rules will not + be generated, even if some inclusive rrule or rdate matches them. + """ + self._exrule.append(exrule) + + @_invalidates_cache + def exdate(self, exdate): + """ Include the given datetime instance in the recurrence set + exclusion list. Dates included that way will not be generated, + even if some inclusive rrule or rdate matches them. """ + self._exdate.append(exdate) + + def _iter(self): + rlist = [] + self._rdate.sort() + self._genitem(rlist, iter(self._rdate)) + for gen in [iter(x) for x in self._rrule]: + self._genitem(rlist, gen) + exlist = [] + self._exdate.sort() + self._genitem(exlist, iter(self._exdate)) + for gen in [iter(x) for x in self._exrule]: + self._genitem(exlist, gen) + lastdt = None + total = 0 + heapq.heapify(rlist) + heapq.heapify(exlist) + while rlist: + ritem = rlist[0] + if not lastdt or lastdt != ritem.dt: + while exlist and exlist[0] < ritem: + exitem = exlist[0] + advance_iterator(exitem) + if exlist and exlist[0] is exitem: + heapq.heapreplace(exlist, exitem) + if not exlist or ritem != exlist[0]: + total += 1 + yield ritem.dt + lastdt = ritem.dt + advance_iterator(ritem) + if rlist and rlist[0] is ritem: + heapq.heapreplace(rlist, ritem) + self._len = total + + + + +class _rrulestr(object): + """ Parses a string representation of a recurrence rule or set of + recurrence rules. + + :param s: + Required, a string defining one or more recurrence rules. + + :param dtstart: + If given, used as the default recurrence start if not specified in the + rule string. + + :param cache: + If set ``True`` caching of results will be enabled, improving + performance of multiple queries considerably. + + :param unfold: + If set ``True`` indicates that a rule string is split over more + than one line and should be joined before processing. + + :param forceset: + If set ``True`` forces a :class:`dateutil.rrule.rruleset` to + be returned. + + :param compatible: + If set ``True`` forces ``unfold`` and ``forceset`` to be ``True``. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a naive + :class:`datetime.datetime` object is returned. + + :param tzids: + If given, a callable or mapping used to retrieve a + :class:`datetime.tzinfo` from a string representation. + Defaults to :func:`dateutil.tz.gettz`. + + :param tzinfos: + Additional time zone names / aliases which may be present in a string + representation. See :func:`dateutil.parser.parse` for more + information. + + :return: + Returns a :class:`dateutil.rrule.rruleset` or + :class:`dateutil.rrule.rrule` + """ + + _freq_map = {"YEARLY": YEARLY, + "MONTHLY": MONTHLY, + "WEEKLY": WEEKLY, + "DAILY": DAILY, + "HOURLY": HOURLY, + "MINUTELY": MINUTELY, + "SECONDLY": SECONDLY} + + _weekday_map = {"MO": 0, "TU": 1, "WE": 2, "TH": 3, + "FR": 4, "SA": 5, "SU": 6} + + def _handle_int(self, rrkwargs, name, value, **kwargs): + rrkwargs[name.lower()] = int(value) + + def _handle_int_list(self, rrkwargs, name, value, **kwargs): + rrkwargs[name.lower()] = [int(x) for x in value.split(',')] + + _handle_INTERVAL = _handle_int + _handle_COUNT = _handle_int + _handle_BYSETPOS = _handle_int_list + _handle_BYMONTH = _handle_int_list + _handle_BYMONTHDAY = _handle_int_list + _handle_BYYEARDAY = _handle_int_list + _handle_BYEASTER = _handle_int_list + _handle_BYWEEKNO = _handle_int_list + _handle_BYHOUR = _handle_int_list + _handle_BYMINUTE = _handle_int_list + _handle_BYSECOND = _handle_int_list + + def _handle_FREQ(self, rrkwargs, name, value, **kwargs): + rrkwargs["freq"] = self._freq_map[value] + + def _handle_UNTIL(self, rrkwargs, name, value, **kwargs): + global parser + if not parser: + from dateutil import parser + try: + rrkwargs["until"] = parser.parse(value, + ignoretz=kwargs.get("ignoretz"), + tzinfos=kwargs.get("tzinfos")) + except ValueError: + raise ValueError("invalid until date") + + def _handle_WKST(self, rrkwargs, name, value, **kwargs): + rrkwargs["wkst"] = self._weekday_map[value] + + def _handle_BYWEEKDAY(self, rrkwargs, name, value, **kwargs): + """ + Two ways to specify this: +1MO or MO(+1) + """ + l = [] + for wday in value.split(','): + if '(' in wday: + # If it's of the form TH(+1), etc. + splt = wday.split('(') + w = splt[0] + n = int(splt[1][:-1]) + elif len(wday): + # If it's of the form +1MO + for i in range(len(wday)): + if wday[i] not in '+-0123456789': + break + n = wday[:i] or None + w = wday[i:] + if n: + n = int(n) + else: + raise ValueError("Invalid (empty) BYDAY specification.") + + l.append(weekdays[self._weekday_map[w]](n)) + rrkwargs["byweekday"] = l + + _handle_BYDAY = _handle_BYWEEKDAY + + def _parse_rfc_rrule(self, line, + dtstart=None, + cache=False, + ignoretz=False, + tzinfos=None): + if line.find(':') != -1: + name, value = line.split(':') + if name != "RRULE": + raise ValueError("unknown parameter name") + else: + value = line + rrkwargs = {} + for pair in value.split(';'): + name, value = pair.split('=') + name = name.upper() + value = value.upper() + try: + getattr(self, "_handle_"+name)(rrkwargs, name, value, + ignoretz=ignoretz, + tzinfos=tzinfos) + except AttributeError: + raise ValueError("unknown parameter '%s'" % name) + except (KeyError, ValueError): + raise ValueError("invalid '%s': %s" % (name, value)) + return rrule(dtstart=dtstart, cache=cache, **rrkwargs) + + def _parse_date_value(self, date_value, parms, rule_tzids, + ignoretz, tzids, tzinfos): + global parser + if not parser: + from dateutil import parser + + datevals = [] + value_found = False + TZID = None + + for parm in parms: + if parm.startswith("TZID="): + try: + tzkey = rule_tzids[parm.split('TZID=')[-1]] + except KeyError: + continue + if tzids is None: + from . import tz + tzlookup = tz.gettz + elif callable(tzids): + tzlookup = tzids + else: + tzlookup = getattr(tzids, 'get', None) + if tzlookup is None: + msg = ('tzids must be a callable, mapping, or None, ' + 'not %s' % tzids) + raise ValueError(msg) + + TZID = tzlookup(tzkey) + continue + + # RFC 5445 3.8.2.4: The VALUE parameter is optional, but may be found + # only once. + if parm not in {"VALUE=DATE-TIME", "VALUE=DATE"}: + raise ValueError("unsupported parm: " + parm) + else: + if value_found: + msg = ("Duplicate value parameter found in: " + parm) + raise ValueError(msg) + value_found = True + + for datestr in date_value.split(','): + date = parser.parse(datestr, ignoretz=ignoretz, tzinfos=tzinfos) + if TZID is not None: + if date.tzinfo is None: + date = date.replace(tzinfo=TZID) + else: + raise ValueError('DTSTART/EXDATE specifies multiple timezone') + datevals.append(date) + + return datevals + + def _parse_rfc(self, s, + dtstart=None, + cache=False, + unfold=False, + forceset=False, + compatible=False, + ignoretz=False, + tzids=None, + tzinfos=None): + global parser + if compatible: + forceset = True + unfold = True + + TZID_NAMES = dict(map( + lambda x: (x.upper(), x), + re.findall('TZID=(?P[^:]+):', s) + )) + s = s.upper() + if not s.strip(): + raise ValueError("empty string") + if unfold: + lines = s.splitlines() + i = 0 + while i < len(lines): + line = lines[i].rstrip() + if not line: + del lines[i] + elif i > 0 and line[0] == " ": + lines[i-1] += line[1:] + del lines[i] + else: + i += 1 + else: + lines = s.split() + if (not forceset and len(lines) == 1 and (s.find(':') == -1 or + s.startswith('RRULE:'))): + return self._parse_rfc_rrule(lines[0], cache=cache, + dtstart=dtstart, ignoretz=ignoretz, + tzinfos=tzinfos) + else: + rrulevals = [] + rdatevals = [] + exrulevals = [] + exdatevals = [] + for line in lines: + if not line: + continue + if line.find(':') == -1: + name = "RRULE" + value = line + else: + name, value = line.split(':', 1) + parms = name.split(';') + if not parms: + raise ValueError("empty property name") + name = parms[0] + parms = parms[1:] + if name == "RRULE": + for parm in parms: + raise ValueError("unsupported RRULE parm: "+parm) + rrulevals.append(value) + elif name == "RDATE": + for parm in parms: + if parm != "VALUE=DATE-TIME": + raise ValueError("unsupported RDATE parm: "+parm) + rdatevals.append(value) + elif name == "EXRULE": + for parm in parms: + raise ValueError("unsupported EXRULE parm: "+parm) + exrulevals.append(value) + elif name == "EXDATE": + exdatevals.extend( + self._parse_date_value(value, parms, + TZID_NAMES, ignoretz, + tzids, tzinfos) + ) + elif name == "DTSTART": + dtvals = self._parse_date_value(value, parms, TZID_NAMES, + ignoretz, tzids, tzinfos) + if len(dtvals) != 1: + raise ValueError("Multiple DTSTART values specified:" + + value) + dtstart = dtvals[0] + else: + raise ValueError("unsupported property: "+name) + if (forceset or len(rrulevals) > 1 or rdatevals + or exrulevals or exdatevals): + if not parser and (rdatevals or exdatevals): + from dateutil import parser + rset = rruleset(cache=cache) + for value in rrulevals: + rset.rrule(self._parse_rfc_rrule(value, dtstart=dtstart, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in rdatevals: + for datestr in value.split(','): + rset.rdate(parser.parse(datestr, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in exrulevals: + rset.exrule(self._parse_rfc_rrule(value, dtstart=dtstart, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in exdatevals: + rset.exdate(value) + if compatible and dtstart: + rset.rdate(dtstart) + return rset + else: + return self._parse_rfc_rrule(rrulevals[0], + dtstart=dtstart, + cache=cache, + ignoretz=ignoretz, + tzinfos=tzinfos) + + def __call__(self, s, **kwargs): + return self._parse_rfc(s, **kwargs) + + +rrulestr = _rrulestr() + +# vim:ts=4:sw=4:et diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/__init__.py b/venv/lib/python3.12/site-packages/dateutil/tz/__init__.py new file mode 100644 index 0000000..af1352c --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/tz/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +from .tz import * +from .tz import __doc__ + +__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", + "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz", + "enfold", "datetime_ambiguous", "datetime_exists", + "resolve_imaginary", "UTC", "DeprecatedTzFormatWarning"] + + +class DeprecatedTzFormatWarning(Warning): + """Warning raised when time zones are parsed from deprecated formats.""" diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..884b235 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/_common.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/_common.cpython-312.pyc new file mode 100644 index 0000000..79a906f Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/_common.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/_factories.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/_factories.cpython-312.pyc new file mode 100644 index 0000000..c253887 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/_factories.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/tz.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/tz.cpython-312.pyc new file mode 100644 index 0000000..015d268 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/tz.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/win.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/win.cpython-312.pyc new file mode 100644 index 0000000..b9dfa6e Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/win.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/_common.py b/venv/lib/python3.12/site-packages/dateutil/tz/_common.py new file mode 100644 index 0000000..e6ac118 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/tz/_common.py @@ -0,0 +1,419 @@ +from six import PY2 + +from functools import wraps + +from datetime import datetime, timedelta, tzinfo + + +ZERO = timedelta(0) + +__all__ = ['tzname_in_python2', 'enfold'] + + +def tzname_in_python2(namefunc): + """Change unicode output into bytestrings in Python 2 + + tzname() API changed in Python 3. It used to return bytes, but was changed + to unicode strings + """ + if PY2: + @wraps(namefunc) + def adjust_encoding(*args, **kwargs): + name = namefunc(*args, **kwargs) + if name is not None: + name = name.encode() + + return name + + return adjust_encoding + else: + return namefunc + + +# The following is adapted from Alexander Belopolsky's tz library +# https://github.com/abalkin/tz +if hasattr(datetime, 'fold'): + # This is the pre-python 3.6 fold situation + def enfold(dt, fold=1): + """ + Provides a unified interface for assigning the ``fold`` attribute to + datetimes both before and after the implementation of PEP-495. + + :param fold: + The value for the ``fold`` attribute in the returned datetime. This + should be either 0 or 1. + + :return: + Returns an object for which ``getattr(dt, 'fold', 0)`` returns + ``fold`` for all versions of Python. In versions prior to + Python 3.6, this is a ``_DatetimeWithFold`` object, which is a + subclass of :py:class:`datetime.datetime` with the ``fold`` + attribute added, if ``fold`` is 1. + + .. versionadded:: 2.6.0 + """ + return dt.replace(fold=fold) + +else: + class _DatetimeWithFold(datetime): + """ + This is a class designed to provide a PEP 495-compliant interface for + Python versions before 3.6. It is used only for dates in a fold, so + the ``fold`` attribute is fixed at ``1``. + + .. versionadded:: 2.6.0 + """ + __slots__ = () + + def replace(self, *args, **kwargs): + """ + Return a datetime with the same attributes, except for those + attributes given new values by whichever keyword arguments are + specified. Note that tzinfo=None can be specified to create a naive + datetime from an aware datetime with no conversion of date and time + data. + + This is reimplemented in ``_DatetimeWithFold`` because pypy3 will + return a ``datetime.datetime`` even if ``fold`` is unchanged. + """ + argnames = ( + 'year', 'month', 'day', 'hour', 'minute', 'second', + 'microsecond', 'tzinfo' + ) + + for arg, argname in zip(args, argnames): + if argname in kwargs: + raise TypeError('Duplicate argument: {}'.format(argname)) + + kwargs[argname] = arg + + for argname in argnames: + if argname not in kwargs: + kwargs[argname] = getattr(self, argname) + + dt_class = self.__class__ if kwargs.get('fold', 1) else datetime + + return dt_class(**kwargs) + + @property + def fold(self): + return 1 + + def enfold(dt, fold=1): + """ + Provides a unified interface for assigning the ``fold`` attribute to + datetimes both before and after the implementation of PEP-495. + + :param fold: + The value for the ``fold`` attribute in the returned datetime. This + should be either 0 or 1. + + :return: + Returns an object for which ``getattr(dt, 'fold', 0)`` returns + ``fold`` for all versions of Python. In versions prior to + Python 3.6, this is a ``_DatetimeWithFold`` object, which is a + subclass of :py:class:`datetime.datetime` with the ``fold`` + attribute added, if ``fold`` is 1. + + .. versionadded:: 2.6.0 + """ + if getattr(dt, 'fold', 0) == fold: + return dt + + args = dt.timetuple()[:6] + args += (dt.microsecond, dt.tzinfo) + + if fold: + return _DatetimeWithFold(*args) + else: + return datetime(*args) + + +def _validate_fromutc_inputs(f): + """ + The CPython version of ``fromutc`` checks that the input is a ``datetime`` + object and that ``self`` is attached as its ``tzinfo``. + """ + @wraps(f) + def fromutc(self, dt): + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + return f(self, dt) + + return fromutc + + +class _tzinfo(tzinfo): + """ + Base class for all ``dateutil`` ``tzinfo`` objects. + """ + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + + dt = dt.replace(tzinfo=self) + + wall_0 = enfold(dt, fold=0) + wall_1 = enfold(dt, fold=1) + + same_offset = wall_0.utcoffset() == wall_1.utcoffset() + same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None) + + return same_dt and not same_offset + + def _fold_status(self, dt_utc, dt_wall): + """ + Determine the fold status of a "wall" datetime, given a representation + of the same datetime as a (naive) UTC datetime. This is calculated based + on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all + datetimes, and that this offset is the actual number of hours separating + ``dt_utc`` and ``dt_wall``. + + :param dt_utc: + Representation of the datetime as UTC + + :param dt_wall: + Representation of the datetime as "wall time". This parameter must + either have a `fold` attribute or have a fold-naive + :class:`datetime.tzinfo` attached, otherwise the calculation may + fail. + """ + if self.is_ambiguous(dt_wall): + delta_wall = dt_wall - dt_utc + _fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst())) + else: + _fold = 0 + + return _fold + + def _fold(self, dt): + return getattr(dt, 'fold', 0) + + def _fromutc(self, dt): + """ + Given a timezone-aware datetime in a given timezone, calculates a + timezone-aware datetime in a new timezone. + + Since this is the one time that we *know* we have an unambiguous + datetime object, we take this opportunity to determine whether the + datetime is ambiguous and in a "fold" state (e.g. if it's the first + occurrence, chronologically, of the ambiguous datetime). + + :param dt: + A timezone-aware :class:`datetime.datetime` object. + """ + + # Re-implement the algorithm from Python's datetime.py + dtoff = dt.utcoffset() + if dtoff is None: + raise ValueError("fromutc() requires a non-None utcoffset() " + "result") + + # The original datetime.py code assumes that `dst()` defaults to + # zero during ambiguous times. PEP 495 inverts this presumption, so + # for pre-PEP 495 versions of python, we need to tweak the algorithm. + dtdst = dt.dst() + if dtdst is None: + raise ValueError("fromutc() requires a non-None dst() result") + delta = dtoff - dtdst + + dt += delta + # Set fold=1 so we can default to being in the fold for + # ambiguous dates. + dtdst = enfold(dt, fold=1).dst() + if dtdst is None: + raise ValueError("fromutc(): dt.dst gave inconsistent " + "results; cannot convert") + return dt + dtdst + + @_validate_fromutc_inputs + def fromutc(self, dt): + """ + Given a timezone-aware datetime in a given timezone, calculates a + timezone-aware datetime in a new timezone. + + Since this is the one time that we *know* we have an unambiguous + datetime object, we take this opportunity to determine whether the + datetime is ambiguous and in a "fold" state (e.g. if it's the first + occurrence, chronologically, of the ambiguous datetime). + + :param dt: + A timezone-aware :class:`datetime.datetime` object. + """ + dt_wall = self._fromutc(dt) + + # Calculate the fold status given the two datetimes. + _fold = self._fold_status(dt, dt_wall) + + # Set the default fold value for ambiguous dates + return enfold(dt_wall, fold=_fold) + + +class tzrangebase(_tzinfo): + """ + This is an abstract base class for time zones represented by an annual + transition into and out of DST. Child classes should implement the following + methods: + + * ``__init__(self, *args, **kwargs)`` + * ``transitions(self, year)`` - this is expected to return a tuple of + datetimes representing the DST on and off transitions in standard + time. + + A fully initialized ``tzrangebase`` subclass should also provide the + following attributes: + * ``hasdst``: Boolean whether or not the zone uses DST. + * ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects + representing the respective UTC offsets. + * ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short + abbreviations in DST and STD, respectively. + * ``_hasdst``: Whether or not the zone has DST. + + .. versionadded:: 2.6.0 + """ + def __init__(self): + raise NotImplementedError('tzrangebase is an abstract base class') + + def utcoffset(self, dt): + isdst = self._isdst(dt) + + if isdst is None: + return None + elif isdst: + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + isdst = self._isdst(dt) + + if isdst is None: + return None + elif isdst: + return self._dst_base_offset + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + if self._isdst(dt): + return self._dst_abbr + else: + return self._std_abbr + + def fromutc(self, dt): + """ Given a datetime in UTC, return local time """ + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + # Get transitions - if there are none, fixed offset + transitions = self.transitions(dt.year) + if transitions is None: + return dt + self.utcoffset(dt) + + # Get the transition times in UTC + dston, dstoff = transitions + + dston -= self._std_offset + dstoff -= self._std_offset + + utc_transitions = (dston, dstoff) + dt_utc = dt.replace(tzinfo=None) + + isdst = self._naive_isdst(dt_utc, utc_transitions) + + if isdst: + dt_wall = dt + self._dst_offset + else: + dt_wall = dt + self._std_offset + + _fold = int(not isdst and self.is_ambiguous(dt_wall)) + + return enfold(dt_wall, fold=_fold) + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + if not self.hasdst: + return False + + start, end = self.transitions(dt.year) + + dt = dt.replace(tzinfo=None) + return (end <= dt < end + self._dst_base_offset) + + def _isdst(self, dt): + if not self.hasdst: + return False + elif dt is None: + return None + + transitions = self.transitions(dt.year) + + if transitions is None: + return False + + dt = dt.replace(tzinfo=None) + + isdst = self._naive_isdst(dt, transitions) + + # Handle ambiguous dates + if not isdst and self.is_ambiguous(dt): + return not self._fold(dt) + else: + return isdst + + def _naive_isdst(self, dt, transitions): + dston, dstoff = transitions + + dt = dt.replace(tzinfo=None) + + if dston < dstoff: + isdst = dston <= dt < dstoff + else: + isdst = not dstoff <= dt < dston + + return isdst + + @property + def _dst_base_offset(self): + return self._dst_offset - self._std_offset + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(...)" % self.__class__.__name__ + + __reduce__ = object.__reduce__ diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/_factories.py b/venv/lib/python3.12/site-packages/dateutil/tz/_factories.py new file mode 100644 index 0000000..f8a6589 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/tz/_factories.py @@ -0,0 +1,80 @@ +from datetime import timedelta +import weakref +from collections import OrderedDict + +from six.moves import _thread + + +class _TzSingleton(type): + def __init__(cls, *args, **kwargs): + cls.__instance = None + super(_TzSingleton, cls).__init__(*args, **kwargs) + + def __call__(cls): + if cls.__instance is None: + cls.__instance = super(_TzSingleton, cls).__call__() + return cls.__instance + + +class _TzFactory(type): + def instance(cls, *args, **kwargs): + """Alternate constructor that returns a fresh instance""" + return type.__call__(cls, *args, **kwargs) + + +class _TzOffsetFactory(_TzFactory): + def __init__(cls, *args, **kwargs): + cls.__instances = weakref.WeakValueDictionary() + cls.__strong_cache = OrderedDict() + cls.__strong_cache_size = 8 + + cls._cache_lock = _thread.allocate_lock() + + def __call__(cls, name, offset): + if isinstance(offset, timedelta): + key = (name, offset.total_seconds()) + else: + key = (name, offset) + + instance = cls.__instances.get(key, None) + if instance is None: + instance = cls.__instances.setdefault(key, + cls.instance(name, offset)) + + # This lock may not be necessary in Python 3. See GH issue #901 + with cls._cache_lock: + cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) + + # Remove an item if the strong cache is overpopulated + if len(cls.__strong_cache) > cls.__strong_cache_size: + cls.__strong_cache.popitem(last=False) + + return instance + + +class _TzStrFactory(_TzFactory): + def __init__(cls, *args, **kwargs): + cls.__instances = weakref.WeakValueDictionary() + cls.__strong_cache = OrderedDict() + cls.__strong_cache_size = 8 + + cls.__cache_lock = _thread.allocate_lock() + + def __call__(cls, s, posix_offset=False): + key = (s, posix_offset) + instance = cls.__instances.get(key, None) + + if instance is None: + instance = cls.__instances.setdefault(key, + cls.instance(s, posix_offset)) + + # This lock may not be necessary in Python 3. See GH issue #901 + with cls.__cache_lock: + cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) + + # Remove an item if the strong cache is overpopulated + if len(cls.__strong_cache) > cls.__strong_cache_size: + cls.__strong_cache.popitem(last=False) + + return instance + diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/tz.py b/venv/lib/python3.12/site-packages/dateutil/tz/tz.py new file mode 100644 index 0000000..6175914 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/tz/tz.py @@ -0,0 +1,1849 @@ +# -*- coding: utf-8 -*- +""" +This module offers timezone implementations subclassing the abstract +:py:class:`datetime.tzinfo` type. There are classes to handle tzfile format +files (usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`, +etc), TZ environment string (in all known formats), given ranges (with help +from relative deltas), local machine timezone, fixed offset timezone, and UTC +timezone. +""" +import datetime +import struct +import time +import sys +import os +import bisect +import weakref +from collections import OrderedDict + +import six +from six import string_types +from six.moves import _thread +from ._common import tzname_in_python2, _tzinfo +from ._common import tzrangebase, enfold +from ._common import _validate_fromutc_inputs + +from ._factories import _TzSingleton, _TzOffsetFactory +from ._factories import _TzStrFactory +try: + from .win import tzwin, tzwinlocal +except ImportError: + tzwin = tzwinlocal = None + +# For warning about rounding tzinfo +from warnings import warn + +ZERO = datetime.timedelta(0) +EPOCH = datetime.datetime(1970, 1, 1, 0, 0) +EPOCHORDINAL = EPOCH.toordinal() + + +@six.add_metaclass(_TzSingleton) +class tzutc(datetime.tzinfo): + """ + This is a tzinfo object that represents the UTC time zone. + + **Examples:** + + .. doctest:: + + >>> from datetime import * + >>> from dateutil.tz import * + + >>> datetime.now() + datetime.datetime(2003, 9, 27, 9, 40, 1, 521290) + + >>> datetime.now(tzutc()) + datetime.datetime(2003, 9, 27, 12, 40, 12, 156379, tzinfo=tzutc()) + + >>> datetime.now(tzutc()).tzname() + 'UTC' + + .. versionchanged:: 2.7.0 + ``tzutc()`` is now a singleton, so the result of ``tzutc()`` will + always return the same object. + + .. doctest:: + + >>> from dateutil.tz import tzutc, UTC + >>> tzutc() is tzutc() + True + >>> tzutc() is UTC + True + """ + def utcoffset(self, dt): + return ZERO + + def dst(self, dt): + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return "UTC" + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + return False + + @_validate_fromutc_inputs + def fromutc(self, dt): + """ + Fast track version of fromutc() returns the original ``dt`` object for + any valid :py:class:`datetime.datetime` object. + """ + return dt + + def __eq__(self, other): + if not isinstance(other, (tzutc, tzoffset)): + return NotImplemented + + return (isinstance(other, tzutc) or + (isinstance(other, tzoffset) and other._offset == ZERO)) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + + +#: Convenience constant providing a :class:`tzutc()` instance +#: +#: .. versionadded:: 2.7.0 +UTC = tzutc() + + +@six.add_metaclass(_TzOffsetFactory) +class tzoffset(datetime.tzinfo): + """ + A simple class for representing a fixed offset from UTC. + + :param name: + The timezone name, to be returned when ``tzname()`` is called. + :param offset: + The time zone offset in seconds, or (since version 2.6.0, represented + as a :py:class:`datetime.timedelta` object). + """ + def __init__(self, name, offset): + self._name = name + + try: + # Allow a timedelta + offset = offset.total_seconds() + except (TypeError, AttributeError): + pass + + self._offset = datetime.timedelta(seconds=_get_supported_offset(offset)) + + def utcoffset(self, dt): + return self._offset + + def dst(self, dt): + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._name + + @_validate_fromutc_inputs + def fromutc(self, dt): + return dt + self._offset + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + return False + + def __eq__(self, other): + if not isinstance(other, tzoffset): + return NotImplemented + + return self._offset == other._offset + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(%s, %s)" % (self.__class__.__name__, + repr(self._name), + int(self._offset.total_seconds())) + + __reduce__ = object.__reduce__ + + +class tzlocal(_tzinfo): + """ + A :class:`tzinfo` subclass built around the ``time`` timezone functions. + """ + def __init__(self): + super(tzlocal, self).__init__() + + self._std_offset = datetime.timedelta(seconds=-time.timezone) + if time.daylight: + self._dst_offset = datetime.timedelta(seconds=-time.altzone) + else: + self._dst_offset = self._std_offset + + self._dst_saved = self._dst_offset - self._std_offset + self._hasdst = bool(self._dst_saved) + self._tznames = tuple(time.tzname) + + def utcoffset(self, dt): + if dt is None and self._hasdst: + return None + + if self._isdst(dt): + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + if dt is None and self._hasdst: + return None + + if self._isdst(dt): + return self._dst_offset - self._std_offset + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._tznames[self._isdst(dt)] + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + naive_dst = self._naive_is_dst(dt) + return (not naive_dst and + (naive_dst != self._naive_is_dst(dt - self._dst_saved))) + + def _naive_is_dst(self, dt): + timestamp = _datetime_to_timestamp(dt) + return time.localtime(timestamp + time.timezone).tm_isdst + + def _isdst(self, dt, fold_naive=True): + # We can't use mktime here. It is unstable when deciding if + # the hour near to a change is DST or not. + # + # timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, + # dt.minute, dt.second, dt.weekday(), 0, -1)) + # return time.localtime(timestamp).tm_isdst + # + # The code above yields the following result: + # + # >>> import tz, datetime + # >>> t = tz.tzlocal() + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRDT' + # >>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname() + # 'BRST' + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRST' + # >>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname() + # 'BRDT' + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRDT' + # + # Here is a more stable implementation: + # + if not self._hasdst: + return False + + # Check for ambiguous times: + dstval = self._naive_is_dst(dt) + fold = getattr(dt, 'fold', None) + + if self.is_ambiguous(dt): + if fold is not None: + return not self._fold(dt) + else: + return True + + return dstval + + def __eq__(self, other): + if isinstance(other, tzlocal): + return (self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset) + elif isinstance(other, tzutc): + return (not self._hasdst and + self._tznames[0] in {'UTC', 'GMT'} and + self._std_offset == ZERO) + elif isinstance(other, tzoffset): + return (not self._hasdst and + self._tznames[0] == other._name and + self._std_offset == other._offset) + else: + return NotImplemented + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + + +class _ttinfo(object): + __slots__ = ["offset", "delta", "isdst", "abbr", + "isstd", "isgmt", "dstoffset"] + + def __init__(self): + for attr in self.__slots__: + setattr(self, attr, None) + + def __repr__(self): + l = [] + for attr in self.__slots__: + value = getattr(self, attr) + if value is not None: + l.append("%s=%s" % (attr, repr(value))) + return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) + + def __eq__(self, other): + if not isinstance(other, _ttinfo): + return NotImplemented + + return (self.offset == other.offset and + self.delta == other.delta and + self.isdst == other.isdst and + self.abbr == other.abbr and + self.isstd == other.isstd and + self.isgmt == other.isgmt and + self.dstoffset == other.dstoffset) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __getstate__(self): + state = {} + for name in self.__slots__: + state[name] = getattr(self, name, None) + return state + + def __setstate__(self, state): + for name in self.__slots__: + if name in state: + setattr(self, name, state[name]) + + +class _tzfile(object): + """ + Lightweight class for holding the relevant transition and time zone + information read from binary tzfiles. + """ + attrs = ['trans_list', 'trans_list_utc', 'trans_idx', 'ttinfo_list', + 'ttinfo_std', 'ttinfo_dst', 'ttinfo_before', 'ttinfo_first'] + + def __init__(self, **kwargs): + for attr in self.attrs: + setattr(self, attr, kwargs.get(attr, None)) + + +class tzfile(_tzinfo): + """ + This is a ``tzinfo`` subclass that allows one to use the ``tzfile(5)`` + format timezone files to extract current and historical zone information. + + :param fileobj: + This can be an opened file stream or a file name that the time zone + information can be read from. + + :param filename: + This is an optional parameter specifying the source of the time zone + information in the event that ``fileobj`` is a file object. If omitted + and ``fileobj`` is a file stream, this parameter will be set either to + ``fileobj``'s ``name`` attribute or to ``repr(fileobj)``. + + See `Sources for Time Zone and Daylight Saving Time Data + `_ for more information. + Time zone files can be compiled from the `IANA Time Zone database files + `_ with the `zic time zone compiler + `_ + + .. note:: + + Only construct a ``tzfile`` directly if you have a specific timezone + file on disk that you want to read into a Python ``tzinfo`` object. + If you want to get a ``tzfile`` representing a specific IANA zone, + (e.g. ``'America/New_York'``), you should call + :func:`dateutil.tz.gettz` with the zone identifier. + + + **Examples:** + + Using the US Eastern time zone as an example, we can see that a ``tzfile`` + provides time zone information for the standard Daylight Saving offsets: + + .. testsetup:: tzfile + + from dateutil.tz import gettz + from datetime import datetime + + .. doctest:: tzfile + + >>> NYC = gettz('America/New_York') + >>> NYC + tzfile('/usr/share/zoneinfo/America/New_York') + + >>> print(datetime(2016, 1, 3, tzinfo=NYC)) # EST + 2016-01-03 00:00:00-05:00 + + >>> print(datetime(2016, 7, 7, tzinfo=NYC)) # EDT + 2016-07-07 00:00:00-04:00 + + + The ``tzfile`` structure contains a fully history of the time zone, + so historical dates will also have the right offsets. For example, before + the adoption of the UTC standards, New York used local solar mean time: + + .. doctest:: tzfile + + >>> print(datetime(1901, 4, 12, tzinfo=NYC)) # LMT + 1901-04-12 00:00:00-04:56 + + And during World War II, New York was on "Eastern War Time", which was a + state of permanent daylight saving time: + + .. doctest:: tzfile + + >>> print(datetime(1944, 2, 7, tzinfo=NYC)) # EWT + 1944-02-07 00:00:00-04:00 + + """ + + def __init__(self, fileobj, filename=None): + super(tzfile, self).__init__() + + file_opened_here = False + if isinstance(fileobj, string_types): + self._filename = fileobj + fileobj = open(fileobj, 'rb') + file_opened_here = True + elif filename is not None: + self._filename = filename + elif hasattr(fileobj, "name"): + self._filename = fileobj.name + else: + self._filename = repr(fileobj) + + if fileobj is not None: + if not file_opened_here: + fileobj = _nullcontext(fileobj) + + with fileobj as file_stream: + tzobj = self._read_tzfile(file_stream) + + self._set_tzdata(tzobj) + + def _set_tzdata(self, tzobj): + """ Set the time zone data of this object from a _tzfile object """ + # Copy the relevant attributes over as private attributes + for attr in _tzfile.attrs: + setattr(self, '_' + attr, getattr(tzobj, attr)) + + def _read_tzfile(self, fileobj): + out = _tzfile() + + # From tzfile(5): + # + # The time zone information files used by tzset(3) + # begin with the magic characters "TZif" to identify + # them as time zone information files, followed by + # sixteen bytes reserved for future use, followed by + # six four-byte values of type long, written in a + # ``standard'' byte order (the high-order byte + # of the value is written first). + if fileobj.read(4).decode() != "TZif": + raise ValueError("magic not found") + + fileobj.read(16) + + ( + # The number of UTC/local indicators stored in the file. + ttisgmtcnt, + + # The number of standard/wall indicators stored in the file. + ttisstdcnt, + + # The number of leap seconds for which data is + # stored in the file. + leapcnt, + + # The number of "transition times" for which data + # is stored in the file. + timecnt, + + # The number of "local time types" for which data + # is stored in the file (must not be zero). + typecnt, + + # The number of characters of "time zone + # abbreviation strings" stored in the file. + charcnt, + + ) = struct.unpack(">6l", fileobj.read(24)) + + # The above header is followed by tzh_timecnt four-byte + # values of type long, sorted in ascending order. + # These values are written in ``standard'' byte order. + # Each is used as a transition time (as returned by + # time(2)) at which the rules for computing local time + # change. + + if timecnt: + out.trans_list_utc = list(struct.unpack(">%dl" % timecnt, + fileobj.read(timecnt*4))) + else: + out.trans_list_utc = [] + + # Next come tzh_timecnt one-byte values of type unsigned + # char; each one tells which of the different types of + # ``local time'' types described in the file is associated + # with the same-indexed transition time. These values + # serve as indices into an array of ttinfo structures that + # appears next in the file. + + if timecnt: + out.trans_idx = struct.unpack(">%dB" % timecnt, + fileobj.read(timecnt)) + else: + out.trans_idx = [] + + # Each ttinfo structure is written as a four-byte value + # for tt_gmtoff of type long, in a standard byte + # order, followed by a one-byte value for tt_isdst + # and a one-byte value for tt_abbrind. In each + # structure, tt_gmtoff gives the number of + # seconds to be added to UTC, tt_isdst tells whether + # tm_isdst should be set by localtime(3), and + # tt_abbrind serves as an index into the array of + # time zone abbreviation characters that follow the + # ttinfo structure(s) in the file. + + ttinfo = [] + + for i in range(typecnt): + ttinfo.append(struct.unpack(">lbb", fileobj.read(6))) + + abbr = fileobj.read(charcnt).decode() + + # Then there are tzh_leapcnt pairs of four-byte + # values, written in standard byte order; the + # first value of each pair gives the time (as + # returned by time(2)) at which a leap second + # occurs; the second gives the total number of + # leap seconds to be applied after the given time. + # The pairs of values are sorted in ascending order + # by time. + + # Not used, for now (but seek for correct file position) + if leapcnt: + fileobj.seek(leapcnt * 8, os.SEEK_CUR) + + # Then there are tzh_ttisstdcnt standard/wall + # indicators, each stored as a one-byte value; + # they tell whether the transition times associated + # with local time types were specified as standard + # time or wall clock time, and are used when + # a time zone file is used in handling POSIX-style + # time zone environment variables. + + if ttisstdcnt: + isstd = struct.unpack(">%db" % ttisstdcnt, + fileobj.read(ttisstdcnt)) + + # Finally, there are tzh_ttisgmtcnt UTC/local + # indicators, each stored as a one-byte value; + # they tell whether the transition times associated + # with local time types were specified as UTC or + # local time, and are used when a time zone file + # is used in handling POSIX-style time zone envi- + # ronment variables. + + if ttisgmtcnt: + isgmt = struct.unpack(">%db" % ttisgmtcnt, + fileobj.read(ttisgmtcnt)) + + # Build ttinfo list + out.ttinfo_list = [] + for i in range(typecnt): + gmtoff, isdst, abbrind = ttinfo[i] + gmtoff = _get_supported_offset(gmtoff) + tti = _ttinfo() + tti.offset = gmtoff + tti.dstoffset = datetime.timedelta(0) + tti.delta = datetime.timedelta(seconds=gmtoff) + tti.isdst = isdst + tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)] + tti.isstd = (ttisstdcnt > i and isstd[i] != 0) + tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0) + out.ttinfo_list.append(tti) + + # Replace ttinfo indexes for ttinfo objects. + out.trans_idx = [out.ttinfo_list[idx] for idx in out.trans_idx] + + # Set standard, dst, and before ttinfos. before will be + # used when a given time is before any transitions, + # and will be set to the first non-dst ttinfo, or to + # the first dst, if all of them are dst. + out.ttinfo_std = None + out.ttinfo_dst = None + out.ttinfo_before = None + if out.ttinfo_list: + if not out.trans_list_utc: + out.ttinfo_std = out.ttinfo_first = out.ttinfo_list[0] + else: + for i in range(timecnt-1, -1, -1): + tti = out.trans_idx[i] + if not out.ttinfo_std and not tti.isdst: + out.ttinfo_std = tti + elif not out.ttinfo_dst and tti.isdst: + out.ttinfo_dst = tti + + if out.ttinfo_std and out.ttinfo_dst: + break + else: + if out.ttinfo_dst and not out.ttinfo_std: + out.ttinfo_std = out.ttinfo_dst + + for tti in out.ttinfo_list: + if not tti.isdst: + out.ttinfo_before = tti + break + else: + out.ttinfo_before = out.ttinfo_list[0] + + # Now fix transition times to become relative to wall time. + # + # I'm not sure about this. In my tests, the tz source file + # is setup to wall time, and in the binary file isstd and + # isgmt are off, so it should be in wall time. OTOH, it's + # always in gmt time. Let me know if you have comments + # about this. + lastdst = None + lastoffset = None + lastdstoffset = None + lastbaseoffset = None + out.trans_list = [] + + for i, tti in enumerate(out.trans_idx): + offset = tti.offset + dstoffset = 0 + + if lastdst is not None: + if tti.isdst: + if not lastdst: + dstoffset = offset - lastoffset + + if not dstoffset and lastdstoffset: + dstoffset = lastdstoffset + + tti.dstoffset = datetime.timedelta(seconds=dstoffset) + lastdstoffset = dstoffset + + # If a time zone changes its base offset during a DST transition, + # then you need to adjust by the previous base offset to get the + # transition time in local time. Otherwise you use the current + # base offset. Ideally, I would have some mathematical proof of + # why this is true, but I haven't really thought about it enough. + baseoffset = offset - dstoffset + adjustment = baseoffset + if (lastbaseoffset is not None and baseoffset != lastbaseoffset + and tti.isdst != lastdst): + # The base DST has changed + adjustment = lastbaseoffset + + lastdst = tti.isdst + lastoffset = offset + lastbaseoffset = baseoffset + + out.trans_list.append(out.trans_list_utc[i] + adjustment) + + out.trans_idx = tuple(out.trans_idx) + out.trans_list = tuple(out.trans_list) + out.trans_list_utc = tuple(out.trans_list_utc) + + return out + + def _find_last_transition(self, dt, in_utc=False): + # If there's no list, there are no transitions to find + if not self._trans_list: + return None + + timestamp = _datetime_to_timestamp(dt) + + # Find where the timestamp fits in the transition list - if the + # timestamp is a transition time, it's part of the "after" period. + trans_list = self._trans_list_utc if in_utc else self._trans_list + idx = bisect.bisect_right(trans_list, timestamp) + + # We want to know when the previous transition was, so subtract off 1 + return idx - 1 + + def _get_ttinfo(self, idx): + # For no list or after the last transition, default to _ttinfo_std + if idx is None or (idx + 1) >= len(self._trans_list): + return self._ttinfo_std + + # If there is a list and the time is before it, return _ttinfo_before + if idx < 0: + return self._ttinfo_before + + return self._trans_idx[idx] + + def _find_ttinfo(self, dt): + idx = self._resolve_ambiguous_time(dt) + + return self._get_ttinfo(idx) + + def fromutc(self, dt): + """ + The ``tzfile`` implementation of :py:func:`datetime.tzinfo.fromutc`. + + :param dt: + A :py:class:`datetime.datetime` object. + + :raises TypeError: + Raised if ``dt`` is not a :py:class:`datetime.datetime` object. + + :raises ValueError: + Raised if this is called with a ``dt`` which does not have this + ``tzinfo`` attached. + + :return: + Returns a :py:class:`datetime.datetime` object representing the + wall time in ``self``'s time zone. + """ + # These isinstance checks are in datetime.tzinfo, so we'll preserve + # them, even if we don't care about duck typing. + if not isinstance(dt, datetime.datetime): + raise TypeError("fromutc() requires a datetime argument") + + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + # First treat UTC as wall time and get the transition we're in. + idx = self._find_last_transition(dt, in_utc=True) + tti = self._get_ttinfo(idx) + + dt_out = dt + datetime.timedelta(seconds=tti.offset) + + fold = self.is_ambiguous(dt_out, idx=idx) + + return enfold(dt_out, fold=int(fold)) + + def is_ambiguous(self, dt, idx=None): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + if idx is None: + idx = self._find_last_transition(dt) + + # Calculate the difference in offsets from current to previous + timestamp = _datetime_to_timestamp(dt) + tti = self._get_ttinfo(idx) + + if idx is None or idx <= 0: + return False + + od = self._get_ttinfo(idx - 1).offset - tti.offset + tt = self._trans_list[idx] # Transition time + + return timestamp < tt + od + + def _resolve_ambiguous_time(self, dt): + idx = self._find_last_transition(dt) + + # If we have no transitions, return the index + _fold = self._fold(dt) + if idx is None or idx == 0: + return idx + + # If it's ambiguous and we're in a fold, shift to a different index. + idx_offset = int(not _fold and self.is_ambiguous(dt, idx)) + + return idx - idx_offset + + def utcoffset(self, dt): + if dt is None: + return None + + if not self._ttinfo_std: + return ZERO + + return self._find_ttinfo(dt).delta + + def dst(self, dt): + if dt is None: + return None + + if not self._ttinfo_dst: + return ZERO + + tti = self._find_ttinfo(dt) + + if not tti.isdst: + return ZERO + + # The documentation says that utcoffset()-dst() must + # be constant for every dt. + return tti.dstoffset + + @tzname_in_python2 + def tzname(self, dt): + if not self._ttinfo_std or dt is None: + return None + return self._find_ttinfo(dt).abbr + + def __eq__(self, other): + if not isinstance(other, tzfile): + return NotImplemented + return (self._trans_list == other._trans_list and + self._trans_idx == other._trans_idx and + self._ttinfo_list == other._ttinfo_list) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._filename)) + + def __reduce__(self): + return self.__reduce_ex__(None) + + def __reduce_ex__(self, protocol): + return (self.__class__, (None, self._filename), self.__dict__) + + +class tzrange(tzrangebase): + """ + The ``tzrange`` object is a time zone specified by a set of offsets and + abbreviations, equivalent to the way the ``TZ`` variable can be specified + in POSIX-like systems, but using Python delta objects to specify DST + start, end and offsets. + + :param stdabbr: + The abbreviation for standard time (e.g. ``'EST'``). + + :param stdoffset: + An integer or :class:`datetime.timedelta` object or equivalent + specifying the base offset from UTC. + + If unspecified, +00:00 is used. + + :param dstabbr: + The abbreviation for DST / "Summer" time (e.g. ``'EDT'``). + + If specified, with no other DST information, DST is assumed to occur + and the default behavior or ``dstoffset``, ``start`` and ``end`` is + used. If unspecified and no other DST information is specified, it + is assumed that this zone has no DST. + + If this is unspecified and other DST information is *is* specified, + DST occurs in the zone but the time zone abbreviation is left + unchanged. + + :param dstoffset: + A an integer or :class:`datetime.timedelta` object or equivalent + specifying the UTC offset during DST. If unspecified and any other DST + information is specified, it is assumed to be the STD offset +1 hour. + + :param start: + A :class:`relativedelta.relativedelta` object or equivalent specifying + the time and time of year that daylight savings time starts. To + specify, for example, that DST starts at 2AM on the 2nd Sunday in + March, pass: + + ``relativedelta(hours=2, month=3, day=1, weekday=SU(+2))`` + + If unspecified and any other DST information is specified, the default + value is 2 AM on the first Sunday in April. + + :param end: + A :class:`relativedelta.relativedelta` object or equivalent + representing the time and time of year that daylight savings time + ends, with the same specification method as in ``start``. One note is + that this should point to the first time in the *standard* zone, so if + a transition occurs at 2AM in the DST zone and the clocks are set back + 1 hour to 1AM, set the ``hours`` parameter to +1. + + + **Examples:** + + .. testsetup:: tzrange + + from dateutil.tz import tzrange, tzstr + + .. doctest:: tzrange + + >>> tzstr('EST5EDT') == tzrange("EST", -18000, "EDT") + True + + >>> from dateutil.relativedelta import * + >>> range1 = tzrange("EST", -18000, "EDT") + >>> range2 = tzrange("EST", -18000, "EDT", -14400, + ... relativedelta(hours=+2, month=4, day=1, + ... weekday=SU(+1)), + ... relativedelta(hours=+1, month=10, day=31, + ... weekday=SU(-1))) + >>> tzstr('EST5EDT') == range1 == range2 + True + + """ + def __init__(self, stdabbr, stdoffset=None, + dstabbr=None, dstoffset=None, + start=None, end=None): + + global relativedelta + from dateutil import relativedelta + + self._std_abbr = stdabbr + self._dst_abbr = dstabbr + + try: + stdoffset = stdoffset.total_seconds() + except (TypeError, AttributeError): + pass + + try: + dstoffset = dstoffset.total_seconds() + except (TypeError, AttributeError): + pass + + if stdoffset is not None: + self._std_offset = datetime.timedelta(seconds=stdoffset) + else: + self._std_offset = ZERO + + if dstoffset is not None: + self._dst_offset = datetime.timedelta(seconds=dstoffset) + elif dstabbr and stdoffset is not None: + self._dst_offset = self._std_offset + datetime.timedelta(hours=+1) + else: + self._dst_offset = ZERO + + if dstabbr and start is None: + self._start_delta = relativedelta.relativedelta( + hours=+2, month=4, day=1, weekday=relativedelta.SU(+1)) + else: + self._start_delta = start + + if dstabbr and end is None: + self._end_delta = relativedelta.relativedelta( + hours=+1, month=10, day=31, weekday=relativedelta.SU(-1)) + else: + self._end_delta = end + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = bool(self._start_delta) + + def transitions(self, year): + """ + For a given year, get the DST on and off transition times, expressed + always on the standard time side. For zones with no transitions, this + function returns ``None``. + + :param year: + The year whose transitions you would like to query. + + :return: + Returns a :class:`tuple` of :class:`datetime.datetime` objects, + ``(dston, dstoff)`` for zones with an annual DST transition, or + ``None`` for fixed offset zones. + """ + if not self.hasdst: + return None + + base_year = datetime.datetime(year, 1, 1) + + start = base_year + self._start_delta + end = base_year + self._end_delta + + return (start, end) + + def __eq__(self, other): + if not isinstance(other, tzrange): + return NotImplemented + + return (self._std_abbr == other._std_abbr and + self._dst_abbr == other._dst_abbr and + self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset and + self._start_delta == other._start_delta and + self._end_delta == other._end_delta) + + @property + def _dst_base_offset(self): + return self._dst_base_offset_ + + +@six.add_metaclass(_TzStrFactory) +class tzstr(tzrange): + """ + ``tzstr`` objects are time zone objects specified by a time-zone string as + it would be passed to a ``TZ`` variable on POSIX-style systems (see + the `GNU C Library: TZ Variable`_ for more details). + + There is one notable exception, which is that POSIX-style time zones use an + inverted offset format, so normally ``GMT+3`` would be parsed as an offset + 3 hours *behind* GMT. The ``tzstr`` time zone object will parse this as an + offset 3 hours *ahead* of GMT. If you would like to maintain the POSIX + behavior, pass a ``True`` value to ``posix_offset``. + + The :class:`tzrange` object provides the same functionality, but is + specified using :class:`relativedelta.relativedelta` objects. rather than + strings. + + :param s: + A time zone string in ``TZ`` variable format. This can be a + :class:`bytes` (2.x: :class:`str`), :class:`str` (2.x: + :class:`unicode`) or a stream emitting unicode characters + (e.g. :class:`StringIO`). + + :param posix_offset: + Optional. If set to ``True``, interpret strings such as ``GMT+3`` or + ``UTC+3`` as being 3 hours *behind* UTC rather than ahead, per the + POSIX standard. + + .. caution:: + + Prior to version 2.7.0, this function also supported time zones + in the format: + + * ``EST5EDT,4,0,6,7200,10,0,26,7200,3600`` + * ``EST5EDT,4,1,0,7200,10,-1,0,7200,3600`` + + This format is non-standard and has been deprecated; this function + will raise a :class:`DeprecatedTZFormatWarning` until + support is removed in a future version. + + .. _`GNU C Library: TZ Variable`: + https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + """ + def __init__(self, s, posix_offset=False): + global parser + from dateutil.parser import _parser as parser + + self._s = s + + res = parser._parsetz(s) + if res is None or res.any_unused_tokens: + raise ValueError("unknown string format") + + # Here we break the compatibility with the TZ variable handling. + # GMT-3 actually *means* the timezone -3. + if res.stdabbr in ("GMT", "UTC") and not posix_offset: + res.stdoffset *= -1 + + # We must initialize it first, since _delta() needs + # _std_offset and _dst_offset set. Use False in start/end + # to avoid building it two times. + tzrange.__init__(self, res.stdabbr, res.stdoffset, + res.dstabbr, res.dstoffset, + start=False, end=False) + + if not res.dstabbr: + self._start_delta = None + self._end_delta = None + else: + self._start_delta = self._delta(res.start) + if self._start_delta: + self._end_delta = self._delta(res.end, isend=1) + + self.hasdst = bool(self._start_delta) + + def _delta(self, x, isend=0): + from dateutil import relativedelta + kwargs = {} + if x.month is not None: + kwargs["month"] = x.month + if x.weekday is not None: + kwargs["weekday"] = relativedelta.weekday(x.weekday, x.week) + if x.week > 0: + kwargs["day"] = 1 + else: + kwargs["day"] = 31 + elif x.day: + kwargs["day"] = x.day + elif x.yday is not None: + kwargs["yearday"] = x.yday + elif x.jyday is not None: + kwargs["nlyearday"] = x.jyday + if not kwargs: + # Default is to start on first sunday of april, and end + # on last sunday of october. + if not isend: + kwargs["month"] = 4 + kwargs["day"] = 1 + kwargs["weekday"] = relativedelta.SU(+1) + else: + kwargs["month"] = 10 + kwargs["day"] = 31 + kwargs["weekday"] = relativedelta.SU(-1) + if x.time is not None: + kwargs["seconds"] = x.time + else: + # Default is 2AM. + kwargs["seconds"] = 7200 + if isend: + # Convert to standard time, to follow the documented way + # of working with the extra hour. See the documentation + # of the tzinfo class. + delta = self._dst_offset - self._std_offset + kwargs["seconds"] -= delta.seconds + delta.days * 86400 + return relativedelta.relativedelta(**kwargs) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._s)) + + +class _tzicalvtzcomp(object): + def __init__(self, tzoffsetfrom, tzoffsetto, isdst, + tzname=None, rrule=None): + self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom) + self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto) + self.tzoffsetdiff = self.tzoffsetto - self.tzoffsetfrom + self.isdst = isdst + self.tzname = tzname + self.rrule = rrule + + +class _tzicalvtz(_tzinfo): + def __init__(self, tzid, comps=[]): + super(_tzicalvtz, self).__init__() + + self._tzid = tzid + self._comps = comps + self._cachedate = [] + self._cachecomp = [] + self._cache_lock = _thread.allocate_lock() + + def _find_comp(self, dt): + if len(self._comps) == 1: + return self._comps[0] + + dt = dt.replace(tzinfo=None) + + try: + with self._cache_lock: + return self._cachecomp[self._cachedate.index( + (dt, self._fold(dt)))] + except ValueError: + pass + + lastcompdt = None + lastcomp = None + + for comp in self._comps: + compdt = self._find_compdt(comp, dt) + + if compdt and (not lastcompdt or lastcompdt < compdt): + lastcompdt = compdt + lastcomp = comp + + if not lastcomp: + # RFC says nothing about what to do when a given + # time is before the first onset date. We'll look for the + # first standard component, or the first component, if + # none is found. + for comp in self._comps: + if not comp.isdst: + lastcomp = comp + break + else: + lastcomp = comp[0] + + with self._cache_lock: + self._cachedate.insert(0, (dt, self._fold(dt))) + self._cachecomp.insert(0, lastcomp) + + if len(self._cachedate) > 10: + self._cachedate.pop() + self._cachecomp.pop() + + return lastcomp + + def _find_compdt(self, comp, dt): + if comp.tzoffsetdiff < ZERO and self._fold(dt): + dt -= comp.tzoffsetdiff + + compdt = comp.rrule.before(dt, inc=True) + + return compdt + + def utcoffset(self, dt): + if dt is None: + return None + + return self._find_comp(dt).tzoffsetto + + def dst(self, dt): + comp = self._find_comp(dt) + if comp.isdst: + return comp.tzoffsetdiff + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._find_comp(dt).tzname + + def __repr__(self): + return "" % repr(self._tzid) + + __reduce__ = object.__reduce__ + + +class tzical(object): + """ + This object is designed to parse an iCalendar-style ``VTIMEZONE`` structure + as set out in `RFC 5545`_ Section 4.6.5 into one or more `tzinfo` objects. + + :param `fileobj`: + A file or stream in iCalendar format, which should be UTF-8 encoded + with CRLF endings. + + .. _`RFC 5545`: https://tools.ietf.org/html/rfc5545 + """ + def __init__(self, fileobj): + global rrule + from dateutil import rrule + + if isinstance(fileobj, string_types): + self._s = fileobj + # ical should be encoded in UTF-8 with CRLF + fileobj = open(fileobj, 'r') + else: + self._s = getattr(fileobj, 'name', repr(fileobj)) + fileobj = _nullcontext(fileobj) + + self._vtz = {} + + with fileobj as fobj: + self._parse_rfc(fobj.read()) + + def keys(self): + """ + Retrieves the available time zones as a list. + """ + return list(self._vtz.keys()) + + def get(self, tzid=None): + """ + Retrieve a :py:class:`datetime.tzinfo` object by its ``tzid``. + + :param tzid: + If there is exactly one time zone available, omitting ``tzid`` + or passing :py:const:`None` value returns it. Otherwise a valid + key (which can be retrieved from :func:`keys`) is required. + + :raises ValueError: + Raised if ``tzid`` is not specified but there are either more + or fewer than 1 zone defined. + + :returns: + Returns either a :py:class:`datetime.tzinfo` object representing + the relevant time zone or :py:const:`None` if the ``tzid`` was + not found. + """ + if tzid is None: + if len(self._vtz) == 0: + raise ValueError("no timezones defined") + elif len(self._vtz) > 1: + raise ValueError("more than one timezone available") + tzid = next(iter(self._vtz)) + + return self._vtz.get(tzid) + + def _parse_offset(self, s): + s = s.strip() + if not s: + raise ValueError("empty offset") + if s[0] in ('+', '-'): + signal = (-1, +1)[s[0] == '+'] + s = s[1:] + else: + signal = +1 + if len(s) == 4: + return (int(s[:2]) * 3600 + int(s[2:]) * 60) * signal + elif len(s) == 6: + return (int(s[:2]) * 3600 + int(s[2:4]) * 60 + int(s[4:])) * signal + else: + raise ValueError("invalid offset: " + s) + + def _parse_rfc(self, s): + lines = s.splitlines() + if not lines: + raise ValueError("empty string") + + # Unfold + i = 0 + while i < len(lines): + line = lines[i].rstrip() + if not line: + del lines[i] + elif i > 0 and line[0] == " ": + lines[i-1] += line[1:] + del lines[i] + else: + i += 1 + + tzid = None + comps = [] + invtz = False + comptype = None + for line in lines: + if not line: + continue + name, value = line.split(':', 1) + parms = name.split(';') + if not parms: + raise ValueError("empty property name") + name = parms[0].upper() + parms = parms[1:] + if invtz: + if name == "BEGIN": + if value in ("STANDARD", "DAYLIGHT"): + # Process component + pass + else: + raise ValueError("unknown component: "+value) + comptype = value + founddtstart = False + tzoffsetfrom = None + tzoffsetto = None + rrulelines = [] + tzname = None + elif name == "END": + if value == "VTIMEZONE": + if comptype: + raise ValueError("component not closed: "+comptype) + if not tzid: + raise ValueError("mandatory TZID not found") + if not comps: + raise ValueError( + "at least one component is needed") + # Process vtimezone + self._vtz[tzid] = _tzicalvtz(tzid, comps) + invtz = False + elif value == comptype: + if not founddtstart: + raise ValueError("mandatory DTSTART not found") + if tzoffsetfrom is None: + raise ValueError( + "mandatory TZOFFSETFROM not found") + if tzoffsetto is None: + raise ValueError( + "mandatory TZOFFSETFROM not found") + # Process component + rr = None + if rrulelines: + rr = rrule.rrulestr("\n".join(rrulelines), + compatible=True, + ignoretz=True, + cache=True) + comp = _tzicalvtzcomp(tzoffsetfrom, tzoffsetto, + (comptype == "DAYLIGHT"), + tzname, rr) + comps.append(comp) + comptype = None + else: + raise ValueError("invalid component end: "+value) + elif comptype: + if name == "DTSTART": + # DTSTART in VTIMEZONE takes a subset of valid RRULE + # values under RFC 5545. + for parm in parms: + if parm != 'VALUE=DATE-TIME': + msg = ('Unsupported DTSTART param in ' + + 'VTIMEZONE: ' + parm) + raise ValueError(msg) + rrulelines.append(line) + founddtstart = True + elif name in ("RRULE", "RDATE", "EXRULE", "EXDATE"): + rrulelines.append(line) + elif name == "TZOFFSETFROM": + if parms: + raise ValueError( + "unsupported %s parm: %s " % (name, parms[0])) + tzoffsetfrom = self._parse_offset(value) + elif name == "TZOFFSETTO": + if parms: + raise ValueError( + "unsupported TZOFFSETTO parm: "+parms[0]) + tzoffsetto = self._parse_offset(value) + elif name == "TZNAME": + if parms: + raise ValueError( + "unsupported TZNAME parm: "+parms[0]) + tzname = value + elif name == "COMMENT": + pass + else: + raise ValueError("unsupported property: "+name) + else: + if name == "TZID": + if parms: + raise ValueError( + "unsupported TZID parm: "+parms[0]) + tzid = value + elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"): + pass + else: + raise ValueError("unsupported property: "+name) + elif name == "BEGIN" and value == "VTIMEZONE": + tzid = None + comps = [] + invtz = True + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._s)) + + +if sys.platform != "win32": + TZFILES = ["/etc/localtime", "localtime"] + TZPATHS = ["/usr/share/zoneinfo", + "/usr/lib/zoneinfo", + "/usr/share/lib/zoneinfo", + "/etc/zoneinfo"] +else: + TZFILES = [] + TZPATHS = [] + + +def __get_gettz(): + tzlocal_classes = (tzlocal,) + if tzwinlocal is not None: + tzlocal_classes += (tzwinlocal,) + + class GettzFunc(object): + """ + Retrieve a time zone object from a string representation + + This function is intended to retrieve the :py:class:`tzinfo` subclass + that best represents the time zone that would be used if a POSIX + `TZ variable`_ were set to the same value. + + If no argument or an empty string is passed to ``gettz``, local time + is returned: + + .. code-block:: python3 + + >>> gettz() + tzfile('/etc/localtime') + + This function is also the preferred way to map IANA tz database keys + to :class:`tzfile` objects: + + .. code-block:: python3 + + >>> gettz('Pacific/Kiritimati') + tzfile('/usr/share/zoneinfo/Pacific/Kiritimati') + + On Windows, the standard is extended to include the Windows-specific + zone names provided by the operating system: + + .. code-block:: python3 + + >>> gettz('Egypt Standard Time') + tzwin('Egypt Standard Time') + + Passing a GNU ``TZ`` style string time zone specification returns a + :class:`tzstr` object: + + .. code-block:: python3 + + >>> gettz('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') + tzstr('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') + + :param name: + A time zone name (IANA, or, on Windows, Windows keys), location of + a ``tzfile(5)`` zoneinfo file or ``TZ`` variable style time zone + specifier. An empty string, no argument or ``None`` is interpreted + as local time. + + :return: + Returns an instance of one of ``dateutil``'s :py:class:`tzinfo` + subclasses. + + .. versionchanged:: 2.7.0 + + After version 2.7.0, any two calls to ``gettz`` using the same + input strings will return the same object: + + .. code-block:: python3 + + >>> tz.gettz('America/Chicago') is tz.gettz('America/Chicago') + True + + In addition to improving performance, this ensures that + `"same zone" semantics`_ are used for datetimes in the same zone. + + + .. _`TZ variable`: + https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + + .. _`"same zone" semantics`: + https://blog.ganssle.io/articles/2018/02/aware-datetime-arithmetic.html + """ + def __init__(self): + + self.__instances = weakref.WeakValueDictionary() + self.__strong_cache_size = 8 + self.__strong_cache = OrderedDict() + self._cache_lock = _thread.allocate_lock() + + def __call__(self, name=None): + with self._cache_lock: + rv = self.__instances.get(name, None) + + if rv is None: + rv = self.nocache(name=name) + if not (name is None + or isinstance(rv, tzlocal_classes) + or rv is None): + # tzlocal is slightly more complicated than the other + # time zone providers because it depends on environment + # at construction time, so don't cache that. + # + # We also cannot store weak references to None, so we + # will also not store that. + self.__instances[name] = rv + else: + # No need for strong caching, return immediately + return rv + + self.__strong_cache[name] = self.__strong_cache.pop(name, rv) + + if len(self.__strong_cache) > self.__strong_cache_size: + self.__strong_cache.popitem(last=False) + + return rv + + def set_cache_size(self, size): + with self._cache_lock: + self.__strong_cache_size = size + while len(self.__strong_cache) > size: + self.__strong_cache.popitem(last=False) + + def cache_clear(self): + with self._cache_lock: + self.__instances = weakref.WeakValueDictionary() + self.__strong_cache.clear() + + @staticmethod + def nocache(name=None): + """A non-cached version of gettz""" + tz = None + if not name: + try: + name = os.environ["TZ"] + except KeyError: + pass + if name is None or name in ("", ":"): + for filepath in TZFILES: + if not os.path.isabs(filepath): + filename = filepath + for path in TZPATHS: + filepath = os.path.join(path, filename) + if os.path.isfile(filepath): + break + else: + continue + if os.path.isfile(filepath): + try: + tz = tzfile(filepath) + break + except (IOError, OSError, ValueError): + pass + else: + tz = tzlocal() + else: + try: + if name.startswith(":"): + name = name[1:] + except TypeError as e: + if isinstance(name, bytes): + new_msg = "gettz argument should be str, not bytes" + six.raise_from(TypeError(new_msg), e) + else: + raise + if os.path.isabs(name): + if os.path.isfile(name): + tz = tzfile(name) + else: + tz = None + else: + for path in TZPATHS: + filepath = os.path.join(path, name) + if not os.path.isfile(filepath): + filepath = filepath.replace(' ', '_') + if not os.path.isfile(filepath): + continue + try: + tz = tzfile(filepath) + break + except (IOError, OSError, ValueError): + pass + else: + tz = None + if tzwin is not None: + try: + tz = tzwin(name) + except (WindowsError, UnicodeEncodeError): + # UnicodeEncodeError is for Python 2.7 compat + tz = None + + if not tz: + from dateutil.zoneinfo import get_zonefile_instance + tz = get_zonefile_instance().get(name) + + if not tz: + for c in name: + # name is not a tzstr unless it has at least + # one offset. For short values of "name", an + # explicit for loop seems to be the fastest way + # To determine if a string contains a digit + if c in "0123456789": + try: + tz = tzstr(name) + except ValueError: + pass + break + else: + if name in ("GMT", "UTC"): + tz = UTC + elif name in time.tzname: + tz = tzlocal() + return tz + + return GettzFunc() + + +gettz = __get_gettz() +del __get_gettz + + +def datetime_exists(dt, tz=None): + """ + Given a datetime and a time zone, determine whether or not a given datetime + would fall in a gap. + + :param dt: + A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` + is provided.) + + :param tz: + A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If + ``None`` or not provided, the datetime's own time zone will be used. + + :return: + Returns a boolean value whether or not the "wall time" exists in + ``tz``. + + .. versionadded:: 2.7.0 + """ + if tz is None: + if dt.tzinfo is None: + raise ValueError('Datetime is naive and no time zone provided.') + tz = dt.tzinfo + + dt = dt.replace(tzinfo=None) + + # This is essentially a test of whether or not the datetime can survive + # a round trip to UTC. + dt_rt = dt.replace(tzinfo=tz).astimezone(UTC).astimezone(tz) + dt_rt = dt_rt.replace(tzinfo=None) + + return dt == dt_rt + + +def datetime_ambiguous(dt, tz=None): + """ + Given a datetime and a time zone, determine whether or not a given datetime + is ambiguous (i.e if there are two times differentiated only by their DST + status). + + :param dt: + A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` + is provided.) + + :param tz: + A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If + ``None`` or not provided, the datetime's own time zone will be used. + + :return: + Returns a boolean value whether or not the "wall time" is ambiguous in + ``tz``. + + .. versionadded:: 2.6.0 + """ + if tz is None: + if dt.tzinfo is None: + raise ValueError('Datetime is naive and no time zone provided.') + + tz = dt.tzinfo + + # If a time zone defines its own "is_ambiguous" function, we'll use that. + is_ambiguous_fn = getattr(tz, 'is_ambiguous', None) + if is_ambiguous_fn is not None: + try: + return tz.is_ambiguous(dt) + except Exception: + pass + + # If it doesn't come out and tell us it's ambiguous, we'll just check if + # the fold attribute has any effect on this particular date and time. + dt = dt.replace(tzinfo=tz) + wall_0 = enfold(dt, fold=0) + wall_1 = enfold(dt, fold=1) + + same_offset = wall_0.utcoffset() == wall_1.utcoffset() + same_dst = wall_0.dst() == wall_1.dst() + + return not (same_offset and same_dst) + + +def resolve_imaginary(dt): + """ + Given a datetime that may be imaginary, return an existing datetime. + + This function assumes that an imaginary datetime represents what the + wall time would be in a zone had the offset transition not occurred, so + it will always fall forward by the transition's change in offset. + + .. doctest:: + + >>> from dateutil import tz + >>> from datetime import datetime + >>> NYC = tz.gettz('America/New_York') + >>> print(tz.resolve_imaginary(datetime(2017, 3, 12, 2, 30, tzinfo=NYC))) + 2017-03-12 03:30:00-04:00 + + >>> KIR = tz.gettz('Pacific/Kiritimati') + >>> print(tz.resolve_imaginary(datetime(1995, 1, 1, 12, 30, tzinfo=KIR))) + 1995-01-02 12:30:00+14:00 + + As a note, :func:`datetime.astimezone` is guaranteed to produce a valid, + existing datetime, so a round-trip to and from UTC is sufficient to get + an extant datetime, however, this generally "falls back" to an earlier time + rather than falling forward to the STD side (though no guarantees are made + about this behavior). + + :param dt: + A :class:`datetime.datetime` which may or may not exist. + + :return: + Returns an existing :class:`datetime.datetime`. If ``dt`` was not + imaginary, the datetime returned is guaranteed to be the same object + passed to the function. + + .. versionadded:: 2.7.0 + """ + if dt.tzinfo is not None and not datetime_exists(dt): + + curr_offset = (dt + datetime.timedelta(hours=24)).utcoffset() + old_offset = (dt - datetime.timedelta(hours=24)).utcoffset() + + dt += curr_offset - old_offset + + return dt + + +def _datetime_to_timestamp(dt): + """ + Convert a :class:`datetime.datetime` object to an epoch timestamp in + seconds since January 1, 1970, ignoring the time zone. + """ + return (dt.replace(tzinfo=None) - EPOCH).total_seconds() + + +if sys.version_info >= (3, 6): + def _get_supported_offset(second_offset): + return second_offset +else: + def _get_supported_offset(second_offset): + # For python pre-3.6, round to full-minutes if that's not the case. + # Python's datetime doesn't accept sub-minute timezones. Check + # http://python.org/sf/1447945 or https://bugs.python.org/issue5288 + # for some information. + old_offset = second_offset + calculated_offset = 60 * ((second_offset + 30) // 60) + return calculated_offset + + +try: + # Python 3.7 feature + from contextlib import nullcontext as _nullcontext +except ImportError: + class _nullcontext(object): + """ + Class for wrapping contexts so that they are passed through in a + with statement. + """ + def __init__(self, context): + self.context = context + + def __enter__(self): + return self.context + + def __exit__(*args, **kwargs): + pass + +# vim:ts=4:sw=4:et diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/win.py b/venv/lib/python3.12/site-packages/dateutil/tz/win.py new file mode 100644 index 0000000..cde07ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/tz/win.py @@ -0,0 +1,370 @@ +# -*- coding: utf-8 -*- +""" +This module provides an interface to the native time zone data on Windows, +including :py:class:`datetime.tzinfo` implementations. + +Attempting to import this module on a non-Windows platform will raise an +:py:obj:`ImportError`. +""" +# This code was originally contributed by Jeffrey Harris. +import datetime +import struct + +from six.moves import winreg +from six import text_type + +try: + import ctypes + from ctypes import wintypes +except ValueError: + # ValueError is raised on non-Windows systems for some horrible reason. + raise ImportError("Running tzwin on non-Windows system") + +from ._common import tzrangebase + +__all__ = ["tzwin", "tzwinlocal", "tzres"] + +ONEWEEK = datetime.timedelta(7) + +TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" +TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" +TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" + + +def _settzkeyname(): + handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + try: + winreg.OpenKey(handle, TZKEYNAMENT).Close() + TZKEYNAME = TZKEYNAMENT + except WindowsError: + TZKEYNAME = TZKEYNAME9X + handle.Close() + return TZKEYNAME + + +TZKEYNAME = _settzkeyname() + + +class tzres(object): + """ + Class for accessing ``tzres.dll``, which contains timezone name related + resources. + + .. versionadded:: 2.5.0 + """ + p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char + + def __init__(self, tzres_loc='tzres.dll'): + # Load the user32 DLL so we can load strings from tzres + user32 = ctypes.WinDLL('user32') + + # Specify the LoadStringW function + user32.LoadStringW.argtypes = (wintypes.HINSTANCE, + wintypes.UINT, + wintypes.LPWSTR, + ctypes.c_int) + + self.LoadStringW = user32.LoadStringW + self._tzres = ctypes.WinDLL(tzres_loc) + self.tzres_loc = tzres_loc + + def load_name(self, offset): + """ + Load a timezone name from a DLL offset (integer). + + >>> from dateutil.tzwin import tzres + >>> tzr = tzres() + >>> print(tzr.load_name(112)) + 'Eastern Standard Time' + + :param offset: + A positive integer value referring to a string from the tzres dll. + + .. note:: + + Offsets found in the registry are generally of the form + ``@tzres.dll,-114``. The offset in this case is 114, not -114. + + """ + resource = self.p_wchar() + lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR) + nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0) + return resource[:nchar] + + def name_from_string(self, tzname_str): + """ + Parse strings as returned from the Windows registry into the time zone + name as defined in the registry. + + >>> from dateutil.tzwin import tzres + >>> tzr = tzres() + >>> print(tzr.name_from_string('@tzres.dll,-251')) + 'Dateline Daylight Time' + >>> print(tzr.name_from_string('Eastern Standard Time')) + 'Eastern Standard Time' + + :param tzname_str: + A timezone name string as returned from a Windows registry key. + + :return: + Returns the localized timezone string from tzres.dll if the string + is of the form `@tzres.dll,-offset`, else returns the input string. + """ + if not tzname_str.startswith('@'): + return tzname_str + + name_splt = tzname_str.split(',-') + try: + offset = int(name_splt[1]) + except: + raise ValueError("Malformed timezone string.") + + return self.load_name(offset) + + +class tzwinbase(tzrangebase): + """tzinfo class based on win32's timezones available in the registry.""" + def __init__(self): + raise NotImplementedError('tzwinbase is an abstract base class') + + def __eq__(self, other): + # Compare on all relevant dimensions, including name. + if not isinstance(other, tzwinbase): + return NotImplemented + + return (self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset and + self._stddayofweek == other._stddayofweek and + self._dstdayofweek == other._dstdayofweek and + self._stdweeknumber == other._stdweeknumber and + self._dstweeknumber == other._dstweeknumber and + self._stdhour == other._stdhour and + self._dsthour == other._dsthour and + self._stdminute == other._stdminute and + self._dstminute == other._dstminute and + self._std_abbr == other._std_abbr and + self._dst_abbr == other._dst_abbr) + + @staticmethod + def list(): + """Return a list of all time zones known to the system.""" + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, TZKEYNAME) as tzkey: + result = [winreg.EnumKey(tzkey, i) + for i in range(winreg.QueryInfoKey(tzkey)[0])] + return result + + def display(self): + """ + Return the display name of the time zone. + """ + return self._display + + def transitions(self, year): + """ + For a given year, get the DST on and off transition times, expressed + always on the standard time side. For zones with no transitions, this + function returns ``None``. + + :param year: + The year whose transitions you would like to query. + + :return: + Returns a :class:`tuple` of :class:`datetime.datetime` objects, + ``(dston, dstoff)`` for zones with an annual DST transition, or + ``None`` for fixed offset zones. + """ + + if not self.hasdst: + return None + + dston = picknthweekday(year, self._dstmonth, self._dstdayofweek, + self._dsthour, self._dstminute, + self._dstweeknumber) + + dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek, + self._stdhour, self._stdminute, + self._stdweeknumber) + + # Ambiguous dates default to the STD side + dstoff -= self._dst_base_offset + + return dston, dstoff + + def _get_hasdst(self): + return self._dstmonth != 0 + + @property + def _dst_base_offset(self): + return self._dst_base_offset_ + + +class tzwin(tzwinbase): + """ + Time zone object created from the zone info in the Windows registry + + These are similar to :py:class:`dateutil.tz.tzrange` objects in that + the time zone data is provided in the format of a single offset rule + for either 0 or 2 time zone transitions per year. + + :param: name + The name of a Windows time zone key, e.g. "Eastern Standard Time". + The full list of keys can be retrieved with :func:`tzwin.list`. + """ + + def __init__(self, name): + self._name = name + + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + tzkeyname = text_type("{kn}\\{name}").format(kn=TZKEYNAME, name=name) + with winreg.OpenKey(handle, tzkeyname) as tzkey: + keydict = valuestodict(tzkey) + + self._std_abbr = keydict["Std"] + self._dst_abbr = keydict["Dlt"] + + self._display = keydict["Display"] + + # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm + tup = struct.unpack("=3l16h", keydict["TZI"]) + stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 + dstoffset = stdoffset-tup[2] # + DaylightBias * -1 + self._std_offset = datetime.timedelta(minutes=stdoffset) + self._dst_offset = datetime.timedelta(minutes=dstoffset) + + # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx + (self._stdmonth, + self._stddayofweek, # Sunday = 0 + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[4:9] + + (self._dstmonth, + self._dstdayofweek, # Sunday = 0 + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[12:17] + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = self._get_hasdst() + + def __repr__(self): + return "tzwin(%s)" % repr(self._name) + + def __reduce__(self): + return (self.__class__, (self._name,)) + + +class tzwinlocal(tzwinbase): + """ + Class representing the local time zone information in the Windows registry + + While :class:`dateutil.tz.tzlocal` makes system calls (via the :mod:`time` + module) to retrieve time zone information, ``tzwinlocal`` retrieves the + rules directly from the Windows registry and creates an object like + :class:`dateutil.tz.tzwin`. + + Because Windows does not have an equivalent of :func:`time.tzset`, on + Windows, :class:`dateutil.tz.tzlocal` instances will always reflect the + time zone settings *at the time that the process was started*, meaning + changes to the machine's time zone settings during the run of a program + on Windows will **not** be reflected by :class:`dateutil.tz.tzlocal`. + Because ``tzwinlocal`` reads the registry directly, it is unaffected by + this issue. + """ + def __init__(self): + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: + keydict = valuestodict(tzlocalkey) + + self._std_abbr = keydict["StandardName"] + self._dst_abbr = keydict["DaylightName"] + + try: + tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME, + sn=self._std_abbr) + with winreg.OpenKey(handle, tzkeyname) as tzkey: + _keydict = valuestodict(tzkey) + self._display = _keydict["Display"] + except OSError: + self._display = None + + stdoffset = -keydict["Bias"]-keydict["StandardBias"] + dstoffset = stdoffset-keydict["DaylightBias"] + + self._std_offset = datetime.timedelta(minutes=stdoffset) + self._dst_offset = datetime.timedelta(minutes=dstoffset) + + # For reasons unclear, in this particular key, the day of week has been + # moved to the END of the SYSTEMTIME structure. + tup = struct.unpack("=8h", keydict["StandardStart"]) + + (self._stdmonth, + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[1:5] + + self._stddayofweek = tup[7] + + tup = struct.unpack("=8h", keydict["DaylightStart"]) + + (self._dstmonth, + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[1:5] + + self._dstdayofweek = tup[7] + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = self._get_hasdst() + + def __repr__(self): + return "tzwinlocal()" + + def __str__(self): + # str will return the standard name, not the daylight name. + return "tzwinlocal(%s)" % repr(self._std_abbr) + + def __reduce__(self): + return (self.__class__, ()) + + +def picknthweekday(year, month, dayofweek, hour, minute, whichweek): + """ dayofweek == 0 means Sunday, whichweek 5 means last instance """ + first = datetime.datetime(year, month, 1, hour, minute) + + # This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6), + # Because 7 % 7 = 0 + weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1) + wd = weekdayone + ((whichweek - 1) * ONEWEEK) + if (wd.month != month): + wd -= ONEWEEK + + return wd + + +def valuestodict(key): + """Convert a registry key's values to a dictionary.""" + dout = {} + size = winreg.QueryInfoKey(key)[1] + tz_res = None + + for i in range(size): + key_name, value, dtype = winreg.EnumValue(key, i) + if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN: + # If it's a DWORD (32-bit integer), it's stored as unsigned - convert + # that to a proper signed integer + if value & (1 << 31): + value = value - (1 << 32) + elif dtype == winreg.REG_SZ: + # If it's a reference to the tzres DLL, load the actual string + if value.startswith('@tzres'): + tz_res = tz_res or tzres() + value = tz_res.name_from_string(value) + + value = value.rstrip('\x00') # Remove trailing nulls + + dout[key_name] = value + + return dout diff --git a/venv/lib/python3.12/site-packages/dateutil/tzwin.py b/venv/lib/python3.12/site-packages/dateutil/tzwin.py new file mode 100644 index 0000000..cebc673 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/tzwin.py @@ -0,0 +1,2 @@ +# tzwin has moved to dateutil.tz.win +from .tz.win import * diff --git a/venv/lib/python3.12/site-packages/dateutil/utils.py b/venv/lib/python3.12/site-packages/dateutil/utils.py new file mode 100644 index 0000000..dd2d245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/utils.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +""" +This module offers general convenience and utility functions for dealing with +datetimes. + +.. versionadded:: 2.7.0 +""" +from __future__ import unicode_literals + +from datetime import datetime, time + + +def today(tzinfo=None): + """ + Returns a :py:class:`datetime` representing the current day at midnight + + :param tzinfo: + The time zone to attach (also used to determine the current day). + + :return: + A :py:class:`datetime.datetime` object representing the current day + at midnight. + """ + + dt = datetime.now(tzinfo) + return datetime.combine(dt.date(), time(0, tzinfo=tzinfo)) + + +def default_tzinfo(dt, tzinfo): + """ + Sets the ``tzinfo`` parameter on naive datetimes only + + This is useful for example when you are provided a datetime that may have + either an implicit or explicit time zone, such as when parsing a time zone + string. + + .. doctest:: + + >>> from dateutil.tz import tzoffset + >>> from dateutil.parser import parse + >>> from dateutil.utils import default_tzinfo + >>> dflt_tz = tzoffset("EST", -18000) + >>> print(default_tzinfo(parse('2014-01-01 12:30 UTC'), dflt_tz)) + 2014-01-01 12:30:00+00:00 + >>> print(default_tzinfo(parse('2014-01-01 12:30'), dflt_tz)) + 2014-01-01 12:30:00-05:00 + + :param dt: + The datetime on which to replace the time zone + + :param tzinfo: + The :py:class:`datetime.tzinfo` subclass instance to assign to + ``dt`` if (and only if) it is naive. + + :return: + Returns an aware :py:class:`datetime.datetime`. + """ + if dt.tzinfo is not None: + return dt + else: + return dt.replace(tzinfo=tzinfo) + + +def within_delta(dt1, dt2, delta): + """ + Useful for comparing two datetimes that may have a negligible difference + to be considered equal. + """ + delta = abs(delta) + difference = dt1 - dt2 + return -delta <= difference <= delta diff --git a/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__init__.py b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__init__.py new file mode 100644 index 0000000..34f11ad --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__init__.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +import warnings +import json + +from tarfile import TarFile +from pkgutil import get_data +from io import BytesIO + +from dateutil.tz import tzfile as _tzfile + +__all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata"] + +ZONEFILENAME = "dateutil-zoneinfo.tar.gz" +METADATA_FN = 'METADATA' + + +class tzfile(_tzfile): + def __reduce__(self): + return (gettz, (self._filename,)) + + +def getzoneinfofile_stream(): + try: + return BytesIO(get_data(__name__, ZONEFILENAME)) + except IOError as e: # TODO switch to FileNotFoundError? + warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror)) + return None + + +class ZoneInfoFile(object): + def __init__(self, zonefile_stream=None): + if zonefile_stream is not None: + with TarFile.open(fileobj=zonefile_stream) as tf: + self.zones = {zf.name: tzfile(tf.extractfile(zf), filename=zf.name) + for zf in tf.getmembers() + if zf.isfile() and zf.name != METADATA_FN} + # deal with links: They'll point to their parent object. Less + # waste of memory + links = {zl.name: self.zones[zl.linkname] + for zl in tf.getmembers() if + zl.islnk() or zl.issym()} + self.zones.update(links) + try: + metadata_json = tf.extractfile(tf.getmember(METADATA_FN)) + metadata_str = metadata_json.read().decode('UTF-8') + self.metadata = json.loads(metadata_str) + except KeyError: + # no metadata in tar file + self.metadata = None + else: + self.zones = {} + self.metadata = None + + def get(self, name, default=None): + """ + Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method + for retrieving zones from the zone dictionary. + + :param name: + The name of the zone to retrieve. (Generally IANA zone names) + + :param default: + The value to return in the event of a missing key. + + .. versionadded:: 2.6.0 + + """ + return self.zones.get(name, default) + + +# The current API has gettz as a module function, although in fact it taps into +# a stateful class. So as a workaround for now, without changing the API, we +# will create a new "global" class instance the first time a user requests a +# timezone. Ugly, but adheres to the api. +# +# TODO: Remove after deprecation period. +_CLASS_ZONE_INSTANCE = [] + + +def get_zonefile_instance(new_instance=False): + """ + This is a convenience function which provides a :class:`ZoneInfoFile` + instance using the data provided by the ``dateutil`` package. By default, it + caches a single instance of the ZoneInfoFile object and returns that. + + :param new_instance: + If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and + used as the cached instance for the next call. Otherwise, new instances + are created only as necessary. + + :return: + Returns a :class:`ZoneInfoFile` object. + + .. versionadded:: 2.6 + """ + if new_instance: + zif = None + else: + zif = getattr(get_zonefile_instance, '_cached_instance', None) + + if zif is None: + zif = ZoneInfoFile(getzoneinfofile_stream()) + + get_zonefile_instance._cached_instance = zif + + return zif + + +def gettz(name): + """ + This retrieves a time zone from the local zoneinfo tarball that is packaged + with dateutil. + + :param name: + An IANA-style time zone name, as found in the zoneinfo file. + + :return: + Returns a :class:`dateutil.tz.tzfile` time zone object. + + .. warning:: + It is generally inadvisable to use this function, and it is only + provided for API compatibility with earlier versions. This is *not* + equivalent to ``dateutil.tz.gettz()``, which selects an appropriate + time zone based on the inputs, favoring system zoneinfo. This is ONLY + for accessing the dateutil-specific zoneinfo (which may be out of + date compared to the system zoneinfo). + + .. deprecated:: 2.6 + If you need to use a specific zoneinfofile over the system zoneinfo, + instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call + :func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead. + + Use :func:`get_zonefile_instance` to retrieve an instance of the + dateutil-provided zoneinfo. + """ + warnings.warn("zoneinfo.gettz() will be removed in future versions, " + "to use the dateutil-provided zoneinfo files, instantiate a " + "ZoneInfoFile object and use ZoneInfoFile.zones.get() " + "instead. See the documentation for details.", + DeprecationWarning) + + if len(_CLASS_ZONE_INSTANCE) == 0: + _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) + return _CLASS_ZONE_INSTANCE[0].zones.get(name) + + +def gettz_db_metadata(): + """ Get the zonefile metadata + + See `zonefile_metadata`_ + + :returns: + A dictionary with the database metadata + + .. deprecated:: 2.6 + See deprecation warning in :func:`zoneinfo.gettz`. To get metadata, + query the attribute ``zoneinfo.ZoneInfoFile.metadata``. + """ + warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future " + "versions, to use the dateutil-provided zoneinfo files, " + "ZoneInfoFile object and query the 'metadata' attribute " + "instead. See the documentation for details.", + DeprecationWarning) + + if len(_CLASS_ZONE_INSTANCE) == 0: + _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) + return _CLASS_ZONE_INSTANCE[0].metadata diff --git a/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e626655 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-312.pyc new file mode 100644 index 0000000..9108182 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz new file mode 100644 index 0000000..1461f8c Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz differ diff --git a/venv/lib/python3.12/site-packages/dateutil/zoneinfo/rebuild.py b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/rebuild.py new file mode 100644 index 0000000..684c658 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/rebuild.py @@ -0,0 +1,75 @@ +import logging +import os +import tempfile +import shutil +import json +from subprocess import check_call, check_output +from tarfile import TarFile + +from dateutil.zoneinfo import METADATA_FN, ZONEFILENAME + + +def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None): + """Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar* + + filename is the timezone tarball from ``ftp.iana.org/tz``. + + """ + tmpdir = tempfile.mkdtemp() + zonedir = os.path.join(tmpdir, "zoneinfo") + moduledir = os.path.dirname(__file__) + try: + with TarFile.open(filename) as tf: + for name in zonegroups: + tf.extract(name, tmpdir) + filepaths = [os.path.join(tmpdir, n) for n in zonegroups] + + _run_zic(zonedir, filepaths) + + # write metadata file + with open(os.path.join(zonedir, METADATA_FN), 'w') as f: + json.dump(metadata, f, indent=4, sort_keys=True) + target = os.path.join(moduledir, ZONEFILENAME) + with TarFile.open(target, "w:%s" % format) as tf: + for entry in os.listdir(zonedir): + entrypath = os.path.join(zonedir, entry) + tf.add(entrypath, entry) + finally: + shutil.rmtree(tmpdir) + + +def _run_zic(zonedir, filepaths): + """Calls the ``zic`` compiler in a compatible way to get a "fat" binary. + + Recent versions of ``zic`` default to ``-b slim``, while older versions + don't even have the ``-b`` option (but default to "fat" binaries). The + current version of dateutil does not support Version 2+ TZif files, which + causes problems when used in conjunction with "slim" binaries, so this + function is used to ensure that we always get a "fat" binary. + """ + + try: + help_text = check_output(["zic", "--help"]) + except OSError as e: + _print_on_nosuchfile(e) + raise + + if b"-b " in help_text: + bloat_args = ["-b", "fat"] + else: + bloat_args = [] + + check_call(["zic"] + bloat_args + ["-d", zonedir] + filepaths) + + +def _print_on_nosuchfile(e): + """Print helpful troubleshooting message + + e is an exception raised by subprocess.check_call() + + """ + if e.errno == 2: + logging.error( + "Could not find zic. Perhaps you need to install " + "libc-bin or some other package that provides it, " + "or it's not in your PATH?") diff --git a/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/METADATA new file mode 100644 index 0000000..cd29a4a --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/METADATA @@ -0,0 +1,118 @@ +Metadata-Version: 2.4 +Name: docstring_parser +Version: 0.18.0 +Summary: Parse Python docstrings in reST, Google and Numpydoc format +Project-URL: homepage, https://github.com/rr-/docstring_parser +Project-URL: repository, https://github.com/rr-/docstring_parser +Project-URL: changelog, https://github.com/rr-/docstring_parser/blob/master/CHANGELOG.md +Author-email: Marcin Kurczewski +License: MIT +License-File: LICENSE.md +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Other Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Topic :: Documentation :: Sphinx +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup +Requires-Python: >=3.8 +Provides-Extra: dev +Requires-Dist: pre-commit>=2.16.0; (python_version >= '3.9') and extra == 'dev' +Requires-Dist: pydoctor>=25.4.0; extra == 'dev' +Requires-Dist: pytest; extra == 'dev' +Provides-Extra: docs +Requires-Dist: pydoctor>=25.4.0; extra == 'docs' +Provides-Extra: test +Requires-Dist: pytest; extra == 'test' +Description-Content-Type: text/markdown + +docstring_parser +================ + +[![Build](https://github.com/rr-/docstring_parser/actions/workflows/build.yml/badge.svg)](https://github.com/rr-/docstring_parser/actions/workflows/build.yml) + +Parse Python docstrings. Currently support ReST, Google, Numpydoc-style and +Epydoc docstrings. + +Example usage: + +```python +>>> from docstring_parser import parse +>>> +>>> +>>> docstring = parse( +... ''' +... Short description +... +... Long description spanning multiple lines +... - First line +... - Second line +... - Third line +... +... :param name: description 1 +... :param int priority: description 2 +... :param str sender: description 3 +... :raises ValueError: if name is invalid +... ''') +>>> +>>> docstring.long_description +'Long description spanning multiple lines\n- First line\n- Second line\n- Third line' +>>> docstring.params[1].arg_name +'priority' +>>> docstring.raises[0].type_name +'ValueError' +``` + +Read [API Documentation](https://rr-.github.io/docstring_parser/). + +# Installation + +Installation using pip + +```shell +pip install docstring_parser + +# or if you want to install it in a virtual environment + +python -m venv venv # create environment +source venv/bin/activate # activate environment +python -m pip install docstring_parser +``` + +Installation using conda + + +1. Download and install miniconda or anaconda +2. Install the package from the conda-forge channel via: + - `conda install -c conda-forge docstring_parser` + - or create a new conda environment via `conda create -n my-new-environment -c conda-forge docstring_parser` + + +# Contributing + +To set up the project: +```sh +git clone https://github.com/rr-/docstring_parser.git +cd docstring_parser + +python -m venv venv # create environment +source venv/bin/activate # activate environment + +pip install -e ".[dev]" # install as editable +pre-commit install # make sure pre-commit is setup +``` + +To run tests: +``` +source venv/bin/activate +pytest +``` diff --git a/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/RECORD new file mode 100644 index 0000000..4829749 --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/RECORD @@ -0,0 +1,24 @@ +docstring_parser-0.18.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +docstring_parser-0.18.0.dist-info/METADATA,sha256=y8QgJ5l_1LGUrl_QZKTR5XuSl2lB0MJtNpVhp8AWozM,3550 +docstring_parser-0.18.0.dist-info/RECORD,, +docstring_parser-0.18.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87 +docstring_parser-0.18.0.dist-info/licenses/LICENSE.md,sha256=3-UUozeuhBer0xqK9we71hcrA-VDC7CD4UWJnql6Puo,1084 +docstring_parser/__init__.py,sha256=78WKoStdo7zvCcEU1Sm2zLvU_BRGKyTYjlScFEB14nk,695 +docstring_parser/__pycache__/__init__.cpython-312.pyc,, +docstring_parser/__pycache__/attrdoc.cpython-312.pyc,, +docstring_parser/__pycache__/common.cpython-312.pyc,, +docstring_parser/__pycache__/epydoc.cpython-312.pyc,, +docstring_parser/__pycache__/google.cpython-312.pyc,, +docstring_parser/__pycache__/numpydoc.cpython-312.pyc,, +docstring_parser/__pycache__/parser.cpython-312.pyc,, +docstring_parser/__pycache__/rest.cpython-312.pyc,, +docstring_parser/__pycache__/util.cpython-312.pyc,, +docstring_parser/attrdoc.py,sha256=grazLW9kqFIFmvjP9waYtTjgUEurlrzBZBPMED4yRNk,4126 +docstring_parser/common.py,sha256=Wua16UpvZL8nqmrRMwph2PPNDx774aK3WFnSnjvVPPw,6320 +docstring_parser/epydoc.py,sha256=qOA77cpOAbuqElDA_FAg_k4IRS4KNghg-i6uL_tL1LY,9410 +docstring_parser/google.py,sha256=Bu5Cy1fylpbQLQnchvqnosd7aFZLAQmhjVo14JUZyow,13550 +docstring_parser/numpydoc.py,sha256=VwttH9MIU2JUAPM5D3mU0a2Y5g-9MrNbEI_q8kcGJMY,17643 +docstring_parser/parser.py,sha256=e6xUG9t8Q-o_q_OOkRxw7vs3TG6GxcIHJAF6_udQwXk,2956 +docstring_parser/py.typed,sha256=bWew9mHgMy8LqMu7RuqQXFXLBxh2CRx0dUbSx-3wE48,27 +docstring_parser/rest.py,sha256=XNMfZyEHVqsAH7l4BG1IUB7wgj7qWuysguXJYCikDp8,8301 +docstring_parser/util.py,sha256=8VmGXhuMcWkrvtZSSKIhX4cKakJmWIILffY6JBQAiCg,4506 diff --git a/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/WHEEL new file mode 100644 index 0000000..b1b94fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.29.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/licenses/LICENSE.md b/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/licenses/LICENSE.md new file mode 100644 index 0000000..f75411d --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser-0.18.0.dist-info/licenses/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Marcin Kurczewski + +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. diff --git a/venv/lib/python3.12/site-packages/docstring_parser/__init__.py b/venv/lib/python3.12/site-packages/docstring_parser/__init__.py new file mode 100644 index 0000000..d80ca29 --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser/__init__.py @@ -0,0 +1,34 @@ +"""Parse docstrings as per Sphinx notation.""" + +from .common import ( + Docstring, + DocstringDeprecated, + DocstringMeta, + DocstringParam, + DocstringRaises, + DocstringReturns, + DocstringStyle, + ParseError, + RenderingStyle, +) +from .parser import compose, parse, parse_from_object +from .util import combine_docstrings + +Style = DocstringStyle # backwards compatibility + +__all__ = [ + "parse", + "parse_from_object", + "combine_docstrings", + "compose", + "ParseError", + "Docstring", + "DocstringMeta", + "DocstringParam", + "DocstringRaises", + "DocstringReturns", + "DocstringDeprecated", + "DocstringStyle", + "RenderingStyle", + "Style", +] diff --git a/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a269d88 Binary files /dev/null and b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/attrdoc.cpython-312.pyc b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/attrdoc.cpython-312.pyc new file mode 100644 index 0000000..186fcbc Binary files /dev/null and b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/attrdoc.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/common.cpython-312.pyc b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/common.cpython-312.pyc new file mode 100644 index 0000000..beec4d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/common.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/epydoc.cpython-312.pyc b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/epydoc.cpython-312.pyc new file mode 100644 index 0000000..41fac76 Binary files /dev/null and b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/epydoc.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/google.cpython-312.pyc b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/google.cpython-312.pyc new file mode 100644 index 0000000..b8a746b Binary files /dev/null and b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/google.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/numpydoc.cpython-312.pyc b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/numpydoc.cpython-312.pyc new file mode 100644 index 0000000..e659757 Binary files /dev/null and b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/numpydoc.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/parser.cpython-312.pyc b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/parser.cpython-312.pyc new file mode 100644 index 0000000..e40e5cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/parser.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/rest.cpython-312.pyc b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/rest.cpython-312.pyc new file mode 100644 index 0000000..9701e53 Binary files /dev/null and b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/rest.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/util.cpython-312.pyc b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/util.cpython-312.pyc new file mode 100644 index 0000000..dd84d9b Binary files /dev/null and b/venv/lib/python3.12/site-packages/docstring_parser/__pycache__/util.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/docstring_parser/attrdoc.py b/venv/lib/python3.12/site-packages/docstring_parser/attrdoc.py new file mode 100644 index 0000000..de84a85 --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser/attrdoc.py @@ -0,0 +1,126 @@ +"""Attribute docstrings parsing. + +.. seealso:: https://peps.python.org/pep-0257/#what-is-a-docstring +""" + +import ast +import inspect +import textwrap +import typing as T +from types import ModuleType + +from .common import Docstring, DocstringParam + + +def ast_get_constant_value(node: ast.AST) -> T.Any: + """Return the constant's value if the given node is a constant.""" + return getattr(node, "value") + + +def ast_unparse(node: ast.AST) -> T.Optional[str]: + """Convert the AST node to source code as a string.""" + if hasattr(ast, "unparse"): + return ast.unparse(node) + # Support simple cases in Python < 3.9 + if isinstance(node, ast.Constant): + return str(ast_get_constant_value(node)) + if isinstance(node, ast.Name): + return node.id + return None + + +def ast_is_literal_str(node: ast.AST) -> bool: + """Return True if the given node is a literal string.""" + return ( + isinstance(node, ast.Expr) + and isinstance(node.value, ast.Constant) + and isinstance(ast_get_constant_value(node.value), str) + ) + + +def ast_get_attribute( + node: ast.AST, +) -> T.Optional[T.Tuple[str, T.Optional[str], T.Optional[str]]]: + """Return name, type and default if the given node is an attribute.""" + if isinstance(node, (ast.Assign, ast.AnnAssign)): + target = ( + node.targets[0] if isinstance(node, ast.Assign) else node.target + ) + if isinstance(target, ast.Name): + type_str = None + if isinstance(node, ast.AnnAssign): + type_str = ast_unparse(node.annotation) + default = None + if node.value: + default = ast_unparse(node.value) + return target.id, type_str, default + return None + + +class AttributeDocstrings(ast.NodeVisitor): + """An ast.NodeVisitor that collects attribute docstrings.""" + + attr_docs = None + prev_attr = None + + def visit(self, node): + if self.prev_attr and ast_is_literal_str(node): + attr_name, attr_type, attr_default = self.prev_attr + self.attr_docs[attr_name] = ( + ast_get_constant_value(node.value), + attr_type, + attr_default, + ) + self.prev_attr = ast_get_attribute(node) + if isinstance(node, (ast.ClassDef, ast.Module)): + self.generic_visit(node) + + def get_attr_docs( + self, component: T.Any + ) -> T.Dict[str, T.Tuple[str, T.Optional[str], T.Optional[str]]]: + """Get attribute docstrings from the given component. + + :param component: component to process (class or module) + :returns: for each attribute docstring, a tuple with (description, + type, default) + """ + self.attr_docs = {} + self.prev_attr = None + try: + source = textwrap.dedent(inspect.getsource(component)) + except OSError: + pass + else: + tree = ast.parse(source) + if inspect.ismodule(component): + self.visit(tree) + elif isinstance(tree, ast.Module) and isinstance( + tree.body[0], ast.ClassDef + ): + self.visit(tree.body[0]) + return self.attr_docs + + +def add_attribute_docstrings( + obj: T.Union[type, ModuleType], docstring: Docstring +) -> None: + """Add attribute docstrings found in the object's source code. + + :param obj: object from which to parse attribute docstrings + :param docstring: Docstring object where found attributes are added + :returns: list with names of added attributes + """ + params = set(p.arg_name for p in docstring.params) + for arg_name, (description, type_name, default) in ( + AttributeDocstrings().get_attr_docs(obj).items() + ): + if arg_name not in params: + param = DocstringParam( + args=["attribute", arg_name], + description=description, + arg_name=arg_name, + type_name=type_name, + is_optional=default is not None, + default=default, + ) + docstring.meta.append(param) diff --git a/venv/lib/python3.12/site-packages/docstring_parser/common.py b/venv/lib/python3.12/site-packages/docstring_parser/common.py new file mode 100644 index 0000000..880e378 --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser/common.py @@ -0,0 +1,229 @@ +"""Common methods for parsing.""" + +import enum +import typing as T + +PARAM_KEYWORDS = { + "param", + "parameter", + "arg", + "argument", + "attribute", + "key", + "keyword", +} +RAISES_KEYWORDS = {"raises", "raise", "except", "exception"} +DEPRECATION_KEYWORDS = {"deprecation", "deprecated"} +RETURNS_KEYWORDS = {"return", "returns"} +YIELDS_KEYWORDS = {"yield", "yields"} +EXAMPLES_KEYWORDS = {"example", "examples"} + + +class ParseError(RuntimeError): + """Base class for all parsing related errors.""" + + +class DocstringStyle(enum.Enum): + """Docstring style.""" + + REST = 1 + GOOGLE = 2 + NUMPYDOC = 3 + EPYDOC = 4 + AUTO = 255 + + +class RenderingStyle(enum.Enum): + """Rendering style when unparsing parsed docstrings.""" + + COMPACT = 1 + CLEAN = 2 + EXPANDED = 3 + + +class DocstringMeta: + """Docstring meta information. + + Symbolizes lines in form of + + :param arg: description + :raises ValueError: if something happens + """ + + def __init__( + self, args: T.List[str], description: T.Optional[str] + ) -> None: + """Initialize self. + + :param args: list of arguments. The exact content of this variable is + dependent on the kind of docstring; it's used to distinguish + between custom docstring meta information items. + :param description: associated docstring description. + """ + self.args = args + self.description = description + + +class DocstringParam(DocstringMeta): + """DocstringMeta symbolizing :param metadata.""" + + def __init__( + self, + args: T.List[str], + description: T.Optional[str], + arg_name: str, + type_name: T.Optional[str], + is_optional: T.Optional[bool], + default: T.Optional[str], + ) -> None: + """Initialize self.""" + super().__init__(args, description) + self.arg_name = arg_name + self.type_name = type_name + self.is_optional = is_optional + self.default = default + + +class DocstringReturns(DocstringMeta): + """DocstringMeta symbolizing :returns or :yields metadata.""" + + def __init__( + self, + args: T.List[str], + description: T.Optional[str], + type_name: T.Optional[str], + is_generator: bool, + return_name: T.Optional[str] = None, + ) -> None: + """Initialize self.""" + super().__init__(args, description) + self.type_name = type_name + self.is_generator = is_generator + self.return_name = return_name + + +class DocstringRaises(DocstringMeta): + """DocstringMeta symbolizing :raises metadata.""" + + def __init__( + self, + args: T.List[str], + description: T.Optional[str], + type_name: T.Optional[str], + ) -> None: + """Initialize self.""" + super().__init__(args, description) + self.type_name = type_name + self.description = description + + +class DocstringDeprecated(DocstringMeta): + """DocstringMeta symbolizing deprecation metadata.""" + + def __init__( + self, + args: T.List[str], + description: T.Optional[str], + version: T.Optional[str], + ) -> None: + """Initialize self.""" + super().__init__(args, description) + self.version = version + self.description = description + + +class DocstringExample(DocstringMeta): + """DocstringMeta symbolizing example metadata.""" + + def __init__( + self, + args: T.List[str], + snippet: T.Optional[str], + description: T.Optional[str], + ) -> None: + """Initialize self.""" + super().__init__(args, description) + self.snippet = snippet + self.description = description + + +class Docstring: + """Docstring object representation.""" + + def __init__( + self, + style=None, # type: T.Optional[DocstringStyle] + ) -> None: + """Initialize self.""" + self.short_description = None # type: T.Optional[str] + self.long_description = None # type: T.Optional[str] + self.blank_after_short_description = False + self.blank_after_long_description = False + self.meta = [] # type: T.List[DocstringMeta] + self.style = style # type: T.Optional[DocstringStyle] + + @property + def description(self) -> T.Optional[str]: + """Return the full description of the function + + Returns None if the docstring did not include any description + """ + ret = [] + if self.short_description: + ret.append(self.short_description) + if self.blank_after_short_description: + ret.append("") + if self.long_description: + ret.append(self.long_description) + + if not ret: + return None + + return "\n".join(ret) + + @property + def params(self) -> T.List[DocstringParam]: + """Return a list of information on function params.""" + return [item for item in self.meta if isinstance(item, DocstringParam)] + + @property + def raises(self) -> T.List[DocstringRaises]: + """Return a list of information on the exceptions that the function + may raise. + """ + return [ + item for item in self.meta if isinstance(item, DocstringRaises) + ] + + @property + def returns(self) -> T.Optional[DocstringReturns]: + """Return a single information on function return. + + Takes the first return information. + """ + for item in self.meta: + if isinstance(item, DocstringReturns): + return item + return None + + @property + def many_returns(self) -> T.List[DocstringReturns]: + """Return a list of information on function return.""" + return [ + item for item in self.meta if isinstance(item, DocstringReturns) + ] + + @property + def deprecation(self) -> T.Optional[DocstringDeprecated]: + """Return a single information on function deprecation notes.""" + for item in self.meta: + if isinstance(item, DocstringDeprecated): + return item + return None + + @property + def examples(self) -> T.List[DocstringExample]: + """Return a list of information on function examples.""" + return [ + item for item in self.meta if isinstance(item, DocstringExample) + ] diff --git a/venv/lib/python3.12/site-packages/docstring_parser/epydoc.py b/venv/lib/python3.12/site-packages/docstring_parser/epydoc.py new file mode 100644 index 0000000..f503a05 --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser/epydoc.py @@ -0,0 +1,284 @@ +"""Epyoc-style docstring parsing. + +.. seealso:: http://epydoc.sourceforge.net/manual-fields.html +""" + +import inspect +import re +import typing as T + +from .common import ( + Docstring, + DocstringMeta, + DocstringParam, + DocstringRaises, + DocstringReturns, + DocstringStyle, + ParseError, + RenderingStyle, +) + + +def _clean_str(string: str) -> T.Optional[str]: + string = string.strip() + if len(string) > 0: + return string + return None + + +def parse(text: T.Optional[str]) -> Docstring: + """Parse the epydoc-style docstring into its components. + + :returns: parsed docstring + """ + ret = Docstring(style=DocstringStyle.EPYDOC) + if not text: + return ret + + text = inspect.cleandoc(text) + match = re.search("^@", text, flags=re.M) + if match: + desc_chunk = text[: match.start()] + meta_chunk = text[match.start() :] + else: + desc_chunk = text + meta_chunk = "" + + parts = desc_chunk.split("\n", 1) + ret.short_description = parts[0] or None + if len(parts) > 1: + long_desc_chunk = parts[1] or "" + ret.blank_after_short_description = long_desc_chunk.startswith("\n") + ret.blank_after_long_description = long_desc_chunk.endswith("\n\n") + ret.long_description = long_desc_chunk.strip() or None + + param_pattern = re.compile( + r"(param|keyword|type)(\s+[_A-z][_A-z0-9]*\??):" + ) + attribute_pattern = re.compile(r"(ivar|cvar|var)(\s+[_A-z][_A-z0-9]*\??):") + raise_pattern = re.compile(r"(raise)(\s+[_A-z][_A-z0-9]*\??)?:") + return_pattern = re.compile(r"(return|rtype|yield|ytype):") + meta_pattern = re.compile( + r"([_A-z][_A-z0-9]+)((\s+[_A-z][_A-z0-9]*\??)*):" + ) + + # tokenize + stream: T.List[T.Tuple[str, str, T.List[str], str]] = [] + for match in re.finditer( + r"(^@.*?)(?=^@|\Z)", meta_chunk, flags=re.S | re.M + ): + chunk = match.group(0) + if not chunk: + continue + + param_match = re.search(param_pattern, chunk) + attribute_match = re.search(attribute_pattern, chunk) + raise_match = re.search(raise_pattern, chunk) + return_match = re.search(return_pattern, chunk) + meta_match = re.search(meta_pattern, chunk) + + match = ( + param_match + or attribute_match + or raise_match + or return_match + or meta_match + ) + if not match: + raise ParseError(f'Error parsing meta information near "{chunk}".') + + desc_chunk = chunk[match.end() :] + if param_match: + base = "param" + key: str = match.group(1) + args = [match.group(2).strip()] + elif attribute_match: + base = "attribute" + key: str = match.group(1) + args = [match.group(2).strip()] + elif raise_match: + base = "raise" + key: str = match.group(1) + args = [] if match.group(2) is None else [match.group(2).strip()] + elif return_match: + base = "return" + key: str = match.group(1) + args = [] + else: + base = "meta" + key: str = match.group(1) + token = _clean_str(match.group(2).strip()) + args = [] if token is None else re.split(r"\s+", token) + + # Make sure we didn't match some existing keyword in an incorrect + # way here: + if key in [ + "param", + "ivar", + "cvar", + "var", + "keyword", + "type", + "return", + "rtype", + "yield", + "ytype", + ]: + raise ParseError( + f'Error parsing meta information near "{chunk}".' + ) + + desc = desc_chunk.strip() + if "\n" in desc: + first_line, rest = desc.split("\n", 1) + desc = first_line + "\n" + inspect.cleandoc(rest) + stream.append((base, key, args, desc)) + + # Combine type_name, arg_name, and description information + params: T.Dict[str, T.Dict[str, T.Any]] = {} + for base, key, args, desc in stream: + if base not in ["param", "attribute", "return"]: + continue # nothing to do + + (arg_name,) = args or ("return",) + info = params.setdefault(arg_name, {}) + info_key = "type_name" if "type" in key else "description" + info[info_key] = desc + + if base == "return": + is_generator = key in {"ytype", "yield"} + if info.setdefault("is_generator", is_generator) != is_generator: + raise ParseError( + f'Error parsing meta information for "{arg_name}".' + ) + + is_done: T.Dict[str, bool] = {} + for base, key, args, desc in stream: + if base in ["param", "attribute"] and not is_done.get(args[0], False): + (arg_name,) = args + info = params[arg_name] + type_name = info.get("type_name") + + if type_name and type_name.endswith("?"): + is_optional = True + type_name = type_name[:-1] + else: + is_optional = False + + match = re.match(r".*defaults to (.+)", desc, flags=re.DOTALL) + default = match.group(1).rstrip(".") if match else None + + meta_item = DocstringParam( + args=[key, arg_name], + description=info.get("description"), + arg_name=arg_name, + type_name=type_name, + is_optional=is_optional, + default=default, + ) + is_done[arg_name] = True + elif base == "return" and not is_done.get("return", False): + info = params["return"] + meta_item = DocstringReturns( + args=[key], + description=info.get("description"), + type_name=info.get("type_name"), + is_generator=info.get("is_generator", False), + ) + is_done["return"] = True + elif base == "raise": + (type_name,) = args or (None,) + meta_item = DocstringRaises( + args=[key] + args, + description=desc, + type_name=type_name, + ) + elif base == "meta": + meta_item = DocstringMeta( + args=[key] + args, + description=desc, + ) + else: + (key, *_) = args or ("return",) + assert is_done.get(key, False) + continue # don't append + + ret.meta.append(meta_item) + + return ret + + +def compose( + docstring: Docstring, + rendering_style: RenderingStyle = RenderingStyle.COMPACT, + indent: str = " ", +) -> str: + """Render a parsed docstring into docstring text. + + :param docstring: parsed docstring representation + :param rendering_style: the style to render docstrings + :param indent: the characters used as indentation in the docstring string + :returns: docstring text + """ + + def process_desc(desc: T.Optional[str], is_type: bool) -> str: + if not desc: + return "" + + if rendering_style == RenderingStyle.EXPANDED or ( + rendering_style == RenderingStyle.CLEAN and not is_type + ): + (first, *rest) = desc.splitlines() + return "\n".join( + ["\n" + indent + first] + [indent + line for line in rest] + ) + + (first, *rest) = desc.splitlines() + return "\n".join([" " + first] + [indent + line for line in rest]) + + parts: T.List[str] = [] + if docstring.short_description: + parts.append(docstring.short_description) + if docstring.blank_after_short_description: + parts.append("") + if docstring.long_description: + parts.append(docstring.long_description) + if docstring.blank_after_long_description: + parts.append("") + + for meta in docstring.meta: + if isinstance(meta, DocstringParam): + if meta.type_name: + type_name = ( + f"{meta.type_name}?" + if meta.is_optional + else meta.type_name + ) + text = f"@type {meta.arg_name}:" + text += process_desc(type_name, True) + parts.append(text) + text = f"@param {meta.arg_name}:" + process_desc( + meta.description, False + ) + parts.append(text) + elif isinstance(meta, DocstringReturns): + (arg_key, type_key) = ( + ("yield", "ytype") + if meta.is_generator + else ("return", "rtype") + ) + if meta.type_name: + text = f"@{type_key}:" + process_desc(meta.type_name, True) + parts.append(text) + if meta.description: + text = f"@{arg_key}:" + process_desc(meta.description, False) + parts.append(text) + elif isinstance(meta, DocstringRaises): + text = f"@raise {meta.type_name}:" if meta.type_name else "@raise:" + text += process_desc(meta.description, False) + parts.append(text) + else: + text = f'@{" ".join(meta.args)}:' + text += process_desc(meta.description, False) + parts.append(text) + return "\n".join(parts) diff --git a/venv/lib/python3.12/site-packages/docstring_parser/google.py b/venv/lib/python3.12/site-packages/docstring_parser/google.py new file mode 100644 index 0000000..244e698 --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser/google.py @@ -0,0 +1,414 @@ +"""Google-style docstring parsing.""" + +import inspect +import re +import typing as T +from collections import OrderedDict, namedtuple +from enum import IntEnum + +from .common import ( + EXAMPLES_KEYWORDS, + PARAM_KEYWORDS, + RAISES_KEYWORDS, + RETURNS_KEYWORDS, + YIELDS_KEYWORDS, + Docstring, + DocstringExample, + DocstringMeta, + DocstringParam, + DocstringRaises, + DocstringReturns, + DocstringStyle, + ParseError, + RenderingStyle, +) + + +class SectionType(IntEnum): + """Types of sections.""" + + SINGULAR = 0 + """For sections like examples.""" + + MULTIPLE = 1 + """For sections like params.""" + + SINGULAR_OR_MULTIPLE = 2 + """For sections like returns or yields.""" + + +class Section(namedtuple("SectionBase", "title key type")): + """A docstring section.""" + + +GOOGLE_TYPED_ARG_REGEX = re.compile(r"\s*(.+?)\s*\(\s*(.*[^\s]+)\s*\)") +GOOGLE_ARG_DESC_REGEX = re.compile(r".*\. Defaults to (.+)\.") +MULTIPLE_PATTERN = re.compile(r"(\s*[^:\s]+:)|([^:]*\]:.*)") + +DEFAULT_SECTIONS = [ + Section("Arguments", "param", SectionType.MULTIPLE), + Section("Args", "param", SectionType.MULTIPLE), + Section("Parameters", "param", SectionType.MULTIPLE), + Section("Params", "param", SectionType.MULTIPLE), + Section("Raises", "raises", SectionType.MULTIPLE), + Section("Exceptions", "raises", SectionType.MULTIPLE), + Section("Except", "raises", SectionType.MULTIPLE), + Section("Attributes", "attribute", SectionType.MULTIPLE), + Section("Example", "examples", SectionType.SINGULAR), + Section("Examples", "examples", SectionType.SINGULAR), + Section("Returns", "returns", SectionType.SINGULAR_OR_MULTIPLE), + Section("Yields", "yields", SectionType.SINGULAR_OR_MULTIPLE), +] + + +class GoogleParser: + """Parser for Google-style docstrings.""" + + def __init__( + self, sections: T.Optional[T.List[Section]] = None, title_colon=True + ): + """Setup sections. + + :param sections: Recognized sections or None to defaults. + :param title_colon: require colon after section title. + """ + if not sections: + sections = DEFAULT_SECTIONS + self.sections = {s.title: s for s in sections} + self.title_colon = title_colon + self._setup() + + def _setup(self): + if self.title_colon: + colon = ":" + else: + colon = "" + self.titles_re = re.compile( + "^(" + + "|".join(f"({t})" for t in self.sections) + + ")" + + colon + + "[ \t\r\f\v]*$", + flags=re.M, + ) + + def _build_meta(self, text: str, title: str) -> DocstringMeta: + """Build docstring element. + + :param text: docstring element text + :param title: title of section containing element + :return: + """ + + section = self.sections[title] + + if ( + section.type == SectionType.SINGULAR_OR_MULTIPLE + and not MULTIPLE_PATTERN.match(text) + ) or section.type == SectionType.SINGULAR: + return self._build_single_meta(section, text) + + if ":" not in text: + raise ParseError(f"Expected a colon in {text!r}.") + + # Split spec and description + before, desc = text.split(":", 1) + + if before and "\n" in before: + # If there is a newline in the first line, clean it up + first_line, rest = before.split("\n", 1) + before = first_line + inspect.cleandoc(rest) + + if desc: + desc = desc[1:] if desc[0] == " " else desc + if "\n" in desc: + first_line, rest = desc.split("\n", 1) + desc = first_line + "\n" + inspect.cleandoc(rest) + desc = desc.strip("\n") + + return self._build_multi_meta(section, before, desc) + + @staticmethod + def _build_single_meta(section: Section, desc: str) -> DocstringMeta: + if section.key in RETURNS_KEYWORDS | YIELDS_KEYWORDS: + return DocstringReturns( + args=[section.key], + description=desc, + type_name=None, + is_generator=section.key in YIELDS_KEYWORDS, + ) + if section.key in RAISES_KEYWORDS: + return DocstringRaises( + args=[section.key], description=desc, type_name=None + ) + if section.key in EXAMPLES_KEYWORDS: + return DocstringExample( + args=[section.key], snippet=None, description=desc + ) + if section.key in PARAM_KEYWORDS: + raise ParseError("Expected paramenter name.") + return DocstringMeta(args=[section.key], description=desc) + + @staticmethod + def _build_multi_meta( + section: Section, before: str, desc: str + ) -> DocstringMeta: + if section.key in PARAM_KEYWORDS: + match = GOOGLE_TYPED_ARG_REGEX.match(before) + if match: + arg_name, type_name = match.group(1, 2) + if type_name.endswith(", optional"): + is_optional = True + type_name = type_name[:-10] + elif type_name.endswith("?"): + is_optional = True + type_name = type_name[:-1] + else: + is_optional = False + else: + arg_name, type_name = before, None + is_optional = None + + match = GOOGLE_ARG_DESC_REGEX.match(desc) + default = match.group(1) if match else None + + return DocstringParam( + args=[section.key, before], + description=desc, + arg_name=arg_name, + type_name=type_name, + is_optional=is_optional, + default=default, + ) + if section.key in RETURNS_KEYWORDS | YIELDS_KEYWORDS: + return DocstringReturns( + args=[section.key, before], + description=desc, + type_name=before, + is_generator=section.key in YIELDS_KEYWORDS, + ) + if section.key in RAISES_KEYWORDS: + return DocstringRaises( + args=[section.key, before], description=desc, type_name=before + ) + return DocstringMeta(args=[section.key, before], description=desc) + + def add_section(self, section: Section): + """Add or replace a section. + + :param section: The new section. + """ + + self.sections[section.title] = section + self._setup() + + def parse(self, text: T.Optional[str]) -> Docstring: + """Parse the Google-style docstring into its components. + + :returns: parsed docstring + """ + ret = Docstring(style=DocstringStyle.GOOGLE) + if not text: + return ret + + # Clean according to PEP-0257 + text = inspect.cleandoc(text) + + # Find first title and split on its position + match = self.titles_re.search(text) + if match: + desc_chunk = text[: match.start()] + meta_chunk = text[match.start() :] + else: + desc_chunk = text + meta_chunk = "" + + # Break description into short and long parts + parts = desc_chunk.split("\n", 1) + ret.short_description = parts[0] or None + if len(parts) > 1: + long_desc_chunk = parts[1] or "" + ret.blank_after_short_description = long_desc_chunk.startswith( + "\n" + ) + ret.blank_after_long_description = long_desc_chunk.endswith("\n\n") + ret.long_description = long_desc_chunk.strip() or None + + # Split by sections determined by titles + matches = list(self.titles_re.finditer(meta_chunk)) + if not matches: + return ret + splits = [] + for j in range(len(matches) - 1): + splits.append((matches[j].end(), matches[j + 1].start())) + splits.append((matches[-1].end(), len(meta_chunk))) + + chunks = OrderedDict() # type: T.Mapping[str,str] + for j, (start, end) in enumerate(splits): + title = matches[j].group(1) + if title not in self.sections: + continue + + # Clear Any Unknown Meta + # Ref: https://github.com/rr-/docstring_parser/issues/29 + meta_details = meta_chunk[start:end] + unknown_meta = re.search(r"\n\S", meta_details) + if unknown_meta is not None: + meta_details = meta_details[: unknown_meta.start()] + + chunks[title] = meta_details.strip("\n") + if not chunks: + return ret + + # Add elements from each chunk + for title, chunk in chunks.items(): + # Determine indent + indent_match = re.search(r"^\s*", chunk) + if not indent_match: + raise ParseError(f'Can\'t infer indent from "{chunk}"') + indent = indent_match.group() + + # Check for singular elements + if self.sections[title].type in [ + SectionType.SINGULAR, + SectionType.SINGULAR_OR_MULTIPLE, + ]: + part = inspect.cleandoc(chunk) + ret.meta.append(self._build_meta(part, title)) + continue + + # Split based on lines which have exactly that indent + _re = "^" + indent + r"(?=\S)" + c_matches = list(re.finditer(_re, chunk, flags=re.M)) + if not c_matches: + raise ParseError(f'No specification for "{title}": "{chunk}"') + c_splits = [] + for j in range(len(c_matches) - 1): + c_splits.append((c_matches[j].end(), c_matches[j + 1].start())) + c_splits.append((c_matches[-1].end(), len(chunk))) + for j, (start, end) in enumerate(c_splits): + part = chunk[start:end].strip("\n") + ret.meta.append(self._build_meta(part, title)) + + return ret + + +def parse(text: T.Optional[str]) -> Docstring: + """Parse the Google-style docstring into its components. + + :returns: parsed docstring + """ + return GoogleParser().parse(text) + + +def compose( + docstring: Docstring, + rendering_style: RenderingStyle = RenderingStyle.COMPACT, + indent: str = " ", +) -> str: + """Render a parsed docstring into docstring text. + + :param docstring: parsed docstring representation + :param rendering_style: the style to render docstrings + :param indent: the characters used as indentation in the docstring string + :returns: docstring text + """ + + def process_one( + one: T.Union[DocstringParam, DocstringReturns, DocstringRaises], + ): + head = "" + + if isinstance(one, DocstringParam): + head += one.arg_name or "" + elif isinstance(one, DocstringReturns): + head += one.return_name or "" + + if isinstance(one, DocstringParam) and one.is_optional: + optional = ( + "?" + if rendering_style == RenderingStyle.COMPACT + else ", optional" + ) + else: + optional = "" + + if one.type_name and head: + head += f" ({one.type_name}{optional}):" + elif one.type_name: + head += f"{one.type_name}{optional}:" + else: + head += ":" + head = indent + head + + if one.description and rendering_style == RenderingStyle.EXPANDED: + body = f"\n{indent}{indent}".join( + [head] + one.description.splitlines() + ) + parts.append(body) + elif one.description: + (first, *rest) = one.description.splitlines() + body = f"\n{indent}{indent}".join([head + " " + first] + rest) + parts.append(body) + else: + parts.append(head) + + def process_sect(name: str, args: T.List[T.Any]): + if args: + parts.append(name) + for arg in args: + process_one(arg) + parts.append("") + + parts: T.List[str] = [] + if docstring.short_description: + parts.append(docstring.short_description) + if docstring.blank_after_short_description: + parts.append("") + + if docstring.long_description: + parts.append(docstring.long_description) + if docstring.blank_after_long_description: + parts.append("") + + process_sect( + "Args:", [p for p in docstring.params or [] if p.args[0] == "param"] + ) + + process_sect( + "Attributes:", + [p for p in docstring.params or [] if p.args[0] == "attribute"], + ) + + process_sect( + "Returns:", + [p for p in docstring.many_returns or [] if not p.is_generator], + ) + + process_sect( + "Yields:", [p for p in docstring.many_returns or [] if p.is_generator] + ) + + process_sect("Raises:", docstring.raises or []) + + if docstring.returns and not docstring.many_returns: + ret = docstring.returns + parts.append("Yields:" if ret else "Returns:") + parts.append("-" * len(parts[-1])) + process_one(ret) + + for meta in docstring.meta: + if isinstance( + meta, (DocstringParam, DocstringReturns, DocstringRaises) + ): + continue # Already handled + parts.append(meta.args[0].replace("_", "").title() + ":") + if meta.description: + lines = [indent + l for l in meta.description.splitlines()] + parts.append("\n".join(lines)) + parts.append("") + + while parts and not parts[-1]: + parts.pop() + + return "\n".join(parts) diff --git a/venv/lib/python3.12/site-packages/docstring_parser/numpydoc.py b/venv/lib/python3.12/site-packages/docstring_parser/numpydoc.py new file mode 100644 index 0000000..7965b68 --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser/numpydoc.py @@ -0,0 +1,568 @@ +"""Numpydoc-style docstring parsing. + +:see: https://numpydoc.readthedocs.io/en/latest/format.html +""" + +import inspect +import itertools +import re +import typing as T +from textwrap import dedent + +from .common import ( + Docstring, + DocstringDeprecated, + DocstringExample, + DocstringMeta, + DocstringParam, + DocstringRaises, + DocstringReturns, + DocstringStyle, + RenderingStyle, +) + + +def _pairwise(iterable: T.Iterable, end=None) -> T.Iterable: + left, right = itertools.tee(iterable) + next(right, None) + return itertools.zip_longest(left, right, fillvalue=end) + + +def _clean_str(string: str) -> T.Optional[str]: + string = string.strip() + if len(string) > 0: + return string + return None + + +KV_REGEX = re.compile(r"^[^\s].*$", flags=re.M) +PARAM_KEY_REGEX = re.compile(r"^(?P.*?)(?:\s*:\s*(?P.*?))?$") +PARAM_OPTIONAL_REGEX = re.compile(r"(?P.*?)(?:, optional|\(optional\))$") + +# Ideally, default value will be specified in the type declaration, +# for which the following are supported: +# +# copy : bool, default True +# copy : bool, default=True +# copy : bool, default: True +# +PARAM_DEFAULT_REGEX = re.compile( + r"(?P.*?)(?:, default|\(default\))(?: | |=| = |= |: |)*(?P.*)$" # pylint: disable=C0301 +) + +# If the default value isn't specified in the type declaration, +# it might be in the description. There isn't any formal grammar for this +# in numpydoc, but we can make some educated guesses. +PARAM_DEFAULT_REGEX_IN_DESC = re.compile( + r"(?(?:['\"]).*?(?:['\"])|[\w\-\.]*\w)" # pylint: disable=C0301 +) + +RETURN_KEY_REGEX = re.compile(r"^(?:(?P.*?)\s*:\s*)?(?P.*?)$") + + +class Section: + """Numpydoc section parser. + + :param title: section title. For most sections, this is a heading like + "Parameters" which appears on its own line, underlined by + en-dashes ('-') on the following line. + :param key: meta key string. In the parsed ``DocstringMeta`` instance this + will be the first element of the ``args`` attribute list. + """ + + def __init__(self, title: str, key: str) -> None: + self.title = title + self.key = key + + @property + def title_pattern(self) -> str: + """Regular expression pattern matching this section's header. + + This pattern will match this instance's ``title`` attribute in + an anonymous group. + """ + dashes = "-" * len(self.title) + return rf"^({self.title})\s*?\n{dashes}\s*$" + + def parse(self, text: str) -> T.Iterable[DocstringMeta]: + """Parse ``DocstringMeta`` objects from the body of this section. + + :param text: section body text. Should be cleaned with + ``inspect.cleandoc`` before parsing. + """ + yield DocstringMeta([self.key], description=_clean_str(text)) + + +class _KVSection(Section): + """Base parser for numpydoc sections with key-value syntax. + + E.g. sections that look like this: + key + value + key2 : type + values can also span... + ... multiple lines + """ + + def _parse_item(self, key: str, value: str) -> DocstringMeta: + pass + + def parse(self, text: str) -> T.Iterable[DocstringMeta]: + for match, next_match in _pairwise(KV_REGEX.finditer(text)): + start = match.end() + end = next_match.start() if next_match is not None else None + value = text[start:end] + yield self._parse_item( + key=match.group(), value=inspect.cleandoc(value) + ) + + +class _SphinxSection(Section): + """Base parser for numpydoc sections with sphinx-style syntax. + + E.g. sections that look like this: + .. title:: something + possibly over multiple lines + """ + + @property + def title_pattern(self) -> str: + return rf"^\.\.\s*({self.title})\s*::" + + +class ParamSection(_KVSection): + """Parser for numpydoc parameter sections. + + E.g. any section that looks like this: + arg_name + arg_description + arg_2 : type, optional + descriptions can also span... + ... multiple lines + """ + + def _parse_item(self, key: str, value: str) -> DocstringParam: + match = PARAM_KEY_REGEX.match(key) + arg_name = type_name = is_optional = default = None + if match is not None: + arg_name = match.group("name") + type_name = match.group("type") + if type_name is not None: + optional_match = PARAM_OPTIONAL_REGEX.match(type_name) + if optional_match is not None: + type_name = optional_match.group("type") + is_optional = True + else: + is_optional = False + + default_match = PARAM_DEFAULT_REGEX.match(type_name) + if default_match is not None: + is_optional = True + type_name = default_match.group("type") + default = default_match.group("value") + + # If the default wasn't specifified in the type declaration, + # try and see if we can find it in the description. + if len(value) > 0 and default is None: + default_match = PARAM_DEFAULT_REGEX_IN_DESC.search(value) + if default_match is not None: + default = default_match.group("value") + + return DocstringParam( + args=[self.key, arg_name], + description=_clean_str(value), + arg_name=arg_name, + type_name=type_name, + is_optional=is_optional, + default=default, + ) + + +class RaisesSection(_KVSection): + """Parser for numpydoc raises sections. + + E.g. any section that looks like this: + ValueError + A description of what might raise ValueError + """ + + def _parse_item(self, key: str, value: str) -> DocstringRaises: + return DocstringRaises( + args=[self.key, key], + description=_clean_str(value), + type_name=key if len(key) > 0 else None, + ) + + +class ReturnsSection(_KVSection): + """Parser for numpydoc returns sections. + + E.g. any section that looks like this: + return_name : type + A description of this returned value + another_type + Return names are optional, types are required + """ + + is_generator = False + + def _parse_item(self, key: str, value: str) -> DocstringReturns: + match = RETURN_KEY_REGEX.match(key) + if match is not None: + return_name = match.group("name") + type_name = match.group("type") + else: + return_name = None + type_name = None + + return DocstringReturns( + args=[self.key], + description=_clean_str(value), + type_name=type_name, + is_generator=self.is_generator, + return_name=return_name, + ) + + +class YieldsSection(ReturnsSection): + """Parser for numpydoc generator "yields" sections.""" + + is_generator = True + + +class DeprecationSection(_SphinxSection): + """Parser for numpydoc "deprecation warning" sections.""" + + def parse(self, text: str) -> T.Iterable[DocstringDeprecated]: + version, desc, *_ = text.split(sep="\n", maxsplit=1) + [None, None] + + if desc is not None: + desc = _clean_str(inspect.cleandoc(desc)) + + yield DocstringDeprecated( + args=[self.key], description=desc, version=_clean_str(version) + ) + + +class ExamplesSection(Section): + """Parser for numpydoc examples sections. + + E.g. any section that looks like this: + >>> import numpy.matlib + >>> np.matlib.empty((2, 2)) # filled with random data + matrix([[ 6.76425276e-320, 9.79033856e-307], # random + [ 7.39337286e-309, 3.22135945e-309]]) + >>> np.matlib.empty((2, 2), dtype=int) + matrix([[ 6600475, 0], # random + [ 6586976, 22740995]]) + """ + + def parse(self, text: str) -> T.Iterable[DocstringMeta]: + """Parse ``DocstringExample`` objects from the body of this section. + + :param text: section body text. Should be cleaned with + ``inspect.cleandoc`` before parsing. + """ + lines = dedent(text).strip().splitlines() + while lines: + snippet_lines = [] + description_lines = [] + while lines: + if not lines[0].startswith(">>>"): + break + snippet_lines.append(lines.pop(0)) + while lines: + if lines[0].startswith(">>>"): + break + description_lines.append(lines.pop(0)) + yield DocstringExample( + [self.key], + snippet="\n".join(snippet_lines) if snippet_lines else None, + description="\n".join(description_lines), + ) + + +DEFAULT_SECTIONS = [ + ParamSection("Parameters", "param"), + ParamSection("Params", "param"), + ParamSection("Arguments", "param"), + ParamSection("Args", "param"), + ParamSection("Other Parameters", "other_param"), + ParamSection("Other Params", "other_param"), + ParamSection("Other Arguments", "other_param"), + ParamSection("Other Args", "other_param"), + ParamSection("Receives", "receives"), + ParamSection("Receive", "receives"), + RaisesSection("Raises", "raises"), + RaisesSection("Raise", "raises"), + RaisesSection("Warns", "warns"), + RaisesSection("Warn", "warns"), + ParamSection("Attributes", "attribute"), + ParamSection("Attribute", "attribute"), + ReturnsSection("Returns", "returns"), + ReturnsSection("Return", "returns"), + YieldsSection("Yields", "yields"), + YieldsSection("Yield", "yields"), + ExamplesSection("Examples", "examples"), + ExamplesSection("Example", "examples"), + Section("Warnings", "warnings"), + Section("Warning", "warnings"), + Section("See Also", "see_also"), + Section("Related", "see_also"), + Section("Notes", "notes"), + Section("Note", "notes"), + Section("References", "references"), + Section("Reference", "references"), + DeprecationSection("deprecated", "deprecation"), +] + + +class NumpydocParser: + """Parser for numpydoc-style docstrings.""" + + def __init__(self, sections: T.Optional[T.Dict[str, Section]] = None): + """Setup sections. + + :param sections: Recognized sections or None to defaults. + """ + sections = sections or DEFAULT_SECTIONS + self.sections = {s.title: s for s in sections} + self._setup() + + def _setup(self): + self.titles_re = re.compile( + r"|".join(s.title_pattern for s in self.sections.values()), + flags=re.M, + ) + + def add_section(self, section: Section): + """Add or replace a section. + + :param section: The new section. + """ + + self.sections[section.title] = section + self._setup() + + def parse(self, text: T.Optional[str]) -> Docstring: + """Parse the numpy-style docstring into its components. + + :returns: parsed docstring + """ + ret = Docstring(style=DocstringStyle.NUMPYDOC) + if not text: + return ret + + # Clean according to PEP-0257 + text = inspect.cleandoc(text) + + # Find first title and split on its position + match = self.titles_re.search(text) + if match: + desc_chunk = text[: match.start()] + meta_chunk = text[match.start() :] + else: + desc_chunk = text + meta_chunk = "" + + # Break description into short and long parts + parts = desc_chunk.split("\n", 1) + ret.short_description = parts[0] or None + if len(parts) > 1: + long_desc_chunk = parts[1] or "" + ret.blank_after_short_description = long_desc_chunk.startswith( + "\n" + ) + ret.blank_after_long_description = long_desc_chunk.endswith("\n\n") + ret.long_description = long_desc_chunk.strip() or None + + for match, nextmatch in _pairwise(self.titles_re.finditer(meta_chunk)): + title = next(g for g in match.groups() if g is not None) + factory = self.sections[title] + + # section chunk starts after the header, + # ends at the start of the next header + start = match.end() + end = nextmatch.start() if nextmatch is not None else None + ret.meta.extend(factory.parse(meta_chunk[start:end])) + + return ret + + +def parse(text: T.Optional[str]) -> Docstring: + """Parse the numpy-style docstring into its components. + + :returns: parsed docstring + """ + return NumpydocParser().parse(text) + + +def compose( + # pylint: disable=W0613 + docstring: Docstring, + rendering_style: RenderingStyle = RenderingStyle.COMPACT, + indent: str = " ", +) -> str: + """Render a parsed docstring into docstring text. + + :param docstring: parsed docstring representation + :param rendering_style: the style to render docstrings + :param indent: the characters used as indentation in the docstring string + :returns: docstring text + """ + + def process_one( + one: T.Union[DocstringParam, DocstringReturns, DocstringRaises], + ): + if isinstance(one, DocstringParam): + head = one.arg_name + elif isinstance(one, DocstringReturns): + head = one.return_name + else: + head = None + + if one.type_name and head: + head += f" : {one.type_name}" + elif one.type_name: + head = one.type_name + elif not head: + head = "" + + # If this is a parameter, check if it's optional. + # If it is and there's a not-None default, include that in the type + # declaration, otherwise just mark it as optional. + if isinstance(one, DocstringParam): + if one.default not in [None, "None"]: + head += f", default={one.default}" + elif one.is_optional or one.default == "None": + head += ", optional" + + if one.description: + body = f"\n{indent}".join([head] + one.description.splitlines()) + parts.append(body) + else: + parts.append(head) + + def process_sect(name: str, args: T.List[T.Any]): + if args: + parts.append("") + parts.append(name) + parts.append("-" * len(parts[-1])) + for arg in args: + process_one(arg) + + parts: T.List[str] = [] + if docstring.short_description: + parts.append(docstring.short_description) + if docstring.blank_after_short_description: + parts.append("") + + if docstring.deprecation: + first = ".. deprecated::" + if docstring.deprecation.version: + first += f" {docstring.deprecation.version}" + if docstring.deprecation.description: + rest = docstring.deprecation.description.splitlines() + else: + rest = [] + sep = f"\n{indent}" + parts.append(sep.join([first] + rest)) + + if docstring.long_description: + parts.append(docstring.long_description) + if docstring.blank_after_long_description: + parts.append("") + + process_sect( + "Parameters", + [item for item in docstring.params or [] if item.args[0] == "param"], + ) + + process_sect( + "Attributes", + [ + item + for item in docstring.params or [] + if item.args[0] == "attribute" + ], + ) + + process_sect( + "Returns", + [ + item + for item in docstring.many_returns or [] + if not item.is_generator + ], + ) + + process_sect( + "Yields", + [item for item in docstring.many_returns or [] if item.is_generator], + ) + + if docstring.returns and not docstring.many_returns: + ret = docstring.returns + parts.append("Yields" if ret else "Returns") + parts.append("-" * len(parts[-1])) + process_one(ret) + + process_sect( + "Receives", + [ + item + for item in docstring.params or [] + if item.args[0] == "receives" + ], + ) + + process_sect( + "Other Parameters", + [ + item + for item in docstring.params or [] + if item.args[0] == "other_param" + ], + ) + + process_sect( + "Raises", + [item for item in docstring.raises or [] if item.args[0] == "raises"], + ) + + process_sect( + "Warns", + [item for item in docstring.raises or [] if item.args[0] == "warns"], + ) + + if len(docstring.examples) > 0: + parts.append("") + parts.append("Examples") + parts.append("--------") + for example in docstring.examples: + if example.snippet: + parts.append(example.snippet) + if example.description: + parts.append(example.description) + + for meta in docstring.meta: + if isinstance( + meta, + ( + DocstringDeprecated, + DocstringParam, + DocstringReturns, + DocstringRaises, + DocstringExample, + ), + ): + continue # Already handled + + parts.append("") + parts.append(meta.args[0].replace("_", "").title()) + parts.append("-" * len(meta.args[0])) + + if meta.description: + parts.append(meta.description) + + return "\n".join(parts) diff --git a/venv/lib/python3.12/site-packages/docstring_parser/parser.py b/venv/lib/python3.12/site-packages/docstring_parser/parser.py new file mode 100644 index 0000000..c769a1f --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser/parser.py @@ -0,0 +1,100 @@ +"""The main parsing routine.""" + +import inspect +import typing as T + +from docstring_parser import epydoc, google, numpydoc, rest +from docstring_parser.attrdoc import add_attribute_docstrings +from docstring_parser.common import ( + Docstring, + DocstringStyle, + ParseError, + RenderingStyle, +) + +_STYLE_MAP = { + DocstringStyle.REST: rest, + DocstringStyle.GOOGLE: google, + DocstringStyle.NUMPYDOC: numpydoc, + DocstringStyle.EPYDOC: epydoc, +} + + +def parse( + text: T.Optional[str], style: DocstringStyle = DocstringStyle.AUTO +) -> Docstring: + """Parse the docstring into its components. + + :param text: docstring text to parse + :param style: docstring style + :returns: parsed docstring representation + """ + if style != DocstringStyle.AUTO: + return _STYLE_MAP[style].parse(text) + + exc: T.Optional[Exception] = None + rets = [] + for module in _STYLE_MAP.values(): + try: + ret = module.parse(text) + except ParseError as ex: + exc = ex + else: + rets.append(ret) + + if not rets: + raise exc + + return sorted(rets, key=lambda d: len(d.meta), reverse=True)[0] + + +def parse_from_object( + obj: T.Any, + style: DocstringStyle = DocstringStyle.AUTO, +) -> Docstring: + """Parse the object's docstring(s) into its components. + + The object can be anything that has a ``__doc__`` attribute. In contrast to + the ``parse`` function, ``parse_from_object`` is able to parse attribute + docstrings which are defined in the source code instead of ``__doc__``. + + Currently only attribute docstrings defined at class and module levels are + supported. Attribute docstrings defined in ``__init__`` methods are not + supported. + + When given a class, only the attribute docstrings of that class are parsed, + not its inherited classes. This is a design decision. Separate calls to + this function should be performed to get attribute docstrings of parent + classes. + + :param obj: object from which to parse the docstring(s) + :param style: docstring style + :returns: parsed docstring representation + """ + docstring = parse(obj.__doc__, style=style) + + if inspect.isclass(obj) or inspect.ismodule(obj): + add_attribute_docstrings(obj, docstring) + + return docstring + + +def compose( + docstring: Docstring, + style: DocstringStyle = DocstringStyle.AUTO, + rendering_style: RenderingStyle = RenderingStyle.COMPACT, + indent: str = " ", +) -> str: + """Render a parsed docstring into docstring text. + + :param docstring: parsed docstring representation + :param style: docstring style to render + :param indent: the characters used as indentation in the docstring string + :returns: docstring text + """ + module = _STYLE_MAP[ + docstring.style if style == DocstringStyle.AUTO else style + ] + return module.compose( + docstring, rendering_style=rendering_style, indent=indent + ) diff --git a/venv/lib/python3.12/site-packages/docstring_parser/py.typed b/venv/lib/python3.12/site-packages/docstring_parser/py.typed new file mode 100644 index 0000000..1242d43 --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. diff --git a/venv/lib/python3.12/site-packages/docstring_parser/rest.py b/venv/lib/python3.12/site-packages/docstring_parser/rest.py new file mode 100644 index 0000000..772cf2f --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser/rest.py @@ -0,0 +1,259 @@ +"""ReST-style docstring parsing.""" + +import inspect +import re +import typing as T + +from .common import ( + DEPRECATION_KEYWORDS, + PARAM_KEYWORDS, + RAISES_KEYWORDS, + RETURNS_KEYWORDS, + YIELDS_KEYWORDS, + Docstring, + DocstringDeprecated, + DocstringMeta, + DocstringParam, + DocstringRaises, + DocstringReturns, + DocstringStyle, + ParseError, + RenderingStyle, +) + + +def _build_meta(args: T.List[str], desc: str) -> DocstringMeta: + key = args[0] + + if key in PARAM_KEYWORDS: + if len(args) == 3: + key, type_name, arg_name = args + if type_name.endswith("?"): + is_optional = True + type_name = type_name[:-1] + else: + is_optional = False + elif len(args) == 2: + key, arg_name = args + type_name = None + is_optional = None + else: + raise ParseError( + f"Expected one or two arguments for a {key} keyword." + ) + + match = re.match(r".*defaults to (.+)", desc, flags=re.DOTALL) + default = match.group(1).rstrip(".") if match else None + + return DocstringParam( + args=args, + description=desc, + arg_name=arg_name, + type_name=type_name, + is_optional=is_optional, + default=default, + ) + + if key in RETURNS_KEYWORDS | YIELDS_KEYWORDS: + if len(args) == 2: + type_name = args[1] + elif len(args) == 1: + type_name = None + else: + raise ParseError( + f"Expected one or no arguments for a {key} keyword." + ) + + return DocstringReturns( + args=args, + description=desc, + type_name=type_name, + is_generator=key in YIELDS_KEYWORDS, + ) + + if key in DEPRECATION_KEYWORDS: + match = re.search( + r"^(?Pv?((?:\d+)(?:\.[0-9a-z\.]+))) (?P.+)", + desc, + flags=re.I, + ) + return DocstringDeprecated( + args=args, + version=match.group("version") if match else None, + description=match.group("desc") if match else desc, + ) + + if key in RAISES_KEYWORDS: + if len(args) == 2: + type_name = args[1] + elif len(args) == 1: + type_name = None + else: + raise ParseError( + f"Expected one or no arguments for a {key} keyword." + ) + return DocstringRaises( + args=args, description=desc, type_name=type_name + ) + + return DocstringMeta(args=args, description=desc) + + +def parse(text: T.Optional[str]) -> Docstring: + """Parse the ReST-style docstring into its components. + + :returns: parsed docstring + """ + ret = Docstring(style=DocstringStyle.REST) + if not text: + return ret + + text = inspect.cleandoc(text) + match = re.search("^:", text, flags=re.M) + if match: + desc_chunk = text[: match.start()] + meta_chunk = text[match.start() :] + else: + desc_chunk = text + meta_chunk = "" + + parts = desc_chunk.split("\n", 1) + ret.short_description = parts[0] or None + if len(parts) > 1: + long_desc_chunk = parts[1] or "" + ret.blank_after_short_description = long_desc_chunk.startswith("\n") + ret.blank_after_long_description = long_desc_chunk.endswith("\n\n") + ret.long_description = long_desc_chunk.strip() or None + + types = {} + rtypes = {} + for match in re.finditer( + r"(^:.*?)(?=^:|\Z)", meta_chunk, flags=re.S | re.M + ): + chunk = match.group(0) + if not chunk: + continue + try: + args_chunk, desc_chunk = chunk.lstrip(":").split(":", 1) + except ValueError as ex: + raise ParseError( + f'Error parsing meta information near "{chunk}".' + ) from ex + args = args_chunk.split() + desc = desc_chunk.strip() + + if "\n" in desc: + first_line, rest = desc.split("\n", 1) + desc = first_line + "\n" + inspect.cleandoc(rest) + + # Add special handling for :type a: typename + if len(args) == 2 and args[0] == "type": + types[args[1]] = desc + elif len(args) in [1, 2] and args[0] == "rtype": + rtypes[None if len(args) == 1 else args[1]] = desc + else: + ret.meta.append(_build_meta(args, desc)) + + for meta in ret.meta: + if isinstance(meta, DocstringParam): + meta.type_name = meta.type_name or types.get(meta.arg_name) + elif isinstance(meta, DocstringReturns): + meta.type_name = meta.type_name or rtypes.get(meta.return_name) + + if not any(isinstance(m, DocstringReturns) for m in ret.meta) and rtypes: + for return_name, type_name in rtypes.items(): + ret.meta.append( + DocstringReturns( + args=[], + type_name=type_name, + description=None, + is_generator=False, + return_name=return_name, + ) + ) + + return ret + + +def compose( + docstring: Docstring, + rendering_style: RenderingStyle = RenderingStyle.COMPACT, + indent: str = " ", +) -> str: + """Render a parsed docstring into docstring text. + + :param docstring: parsed docstring representation + :param rendering_style: the style to render docstrings + :param indent: the characters used as indentation in the docstring string + :returns: docstring text + """ + + def process_desc(desc: T.Optional[str]) -> str: + if not desc: + return "" + + if rendering_style == RenderingStyle.CLEAN: + (first, *rest) = desc.splitlines() + return "\n".join([" " + first] + [indent + line for line in rest]) + + if rendering_style == RenderingStyle.EXPANDED: + (first, *rest) = desc.splitlines() + return "\n".join( + ["\n" + indent + first] + [indent + line for line in rest] + ) + + return " " + desc + + parts: T.List[str] = [] + if docstring.short_description: + parts.append(docstring.short_description) + if docstring.blank_after_short_description: + parts.append("") + if docstring.long_description: + parts.append(docstring.long_description) + if docstring.blank_after_long_description: + parts.append("") + + for meta in docstring.meta: + if isinstance(meta, DocstringParam): + if meta.type_name: + type_text = ( + f" {meta.type_name}? " + if meta.is_optional + else f" {meta.type_name} " + ) + else: + type_text = " " + if rendering_style == RenderingStyle.EXPANDED: + text = f":param {meta.arg_name}:" + text += process_desc(meta.description) + parts.append(text) + if type_text[:-1]: + parts.append(f":type {meta.arg_name}:{type_text[:-1]}") + else: + text = f":param{type_text}{meta.arg_name}:" + text += process_desc(meta.description) + parts.append(text) + elif isinstance(meta, DocstringReturns): + type_text = f" {meta.type_name}" if meta.type_name else "" + key = "yields" if meta.is_generator else "returns" + + if rendering_style == RenderingStyle.EXPANDED: + if meta.description: + text = f":{key}:" + text += process_desc(meta.description) + parts.append(text) + if type_text: + parts.append(f":rtype:{type_text}") + else: + text = f":{key}{type_text}:" + text += process_desc(meta.description) + parts.append(text) + elif isinstance(meta, DocstringRaises): + type_text = f" {meta.type_name} " if meta.type_name else "" + text = f":raises{type_text}:" + process_desc(meta.description) + parts.append(text) + else: + text = f':{" ".join(meta.args)}:' + process_desc(meta.description) + parts.append(text) + return "\n".join(parts) diff --git a/venv/lib/python3.12/site-packages/docstring_parser/util.py b/venv/lib/python3.12/site-packages/docstring_parser/util.py new file mode 100644 index 0000000..c5be2f5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/docstring_parser/util.py @@ -0,0 +1,145 @@ +"""Utility functions for working with docstrings.""" + +import typing as T +from collections import ChainMap +from inspect import Signature +from itertools import chain + +from .common import ( + DocstringMeta, + DocstringParam, + DocstringReturns, + DocstringStyle, + RenderingStyle, +) +from .parser import compose, parse + +_Func = T.Callable[..., T.Any] + +assert DocstringReturns # used in docstring + + +def combine_docstrings( + *others: _Func, + exclude: T.Iterable[T.Type[DocstringMeta]] = (), + style: DocstringStyle = DocstringStyle.AUTO, + rendering_style: RenderingStyle = RenderingStyle.COMPACT, +) -> _Func: + """A function decorator that parses the docstrings from `others`, + programmatically combines them with the parsed docstring of the decorated + function, and replaces the docstring of the decorated function with the + composed result. Only parameters that are part of the decorated functions + signature are included in the combined docstring. When multiple sources for + a parameter or docstring metadata exists then the decorator will first + default to the wrapped function's value (when available) and otherwise use + the rightmost definition from ``others``. + + The following example illustrates its usage: + + >>> def fun1(a, b, c, d): + ... '''short_description: fun1 + ... + ... :param a: fun1 + ... :param b: fun1 + ... :return: fun1 + ... ''' + >>> def fun2(b, c, d, e): + ... '''short_description: fun2 + ... + ... long_description: fun2 + ... + ... :param b: fun2 + ... :param c: fun2 + ... :param e: fun2 + ... ''' + >>> @combine_docstrings(fun1, fun2) + >>> def decorated(a, b, c, d, e, f): + ... ''' + ... :param e: decorated + ... :param f: decorated + ... ''' + >>> print(decorated.__doc__) + short_description: fun2 + + long_description: fun2 + + :param a: fun1 + :param b: fun1 + :param c: fun2 + :param e: fun2 + :param f: decorated + :returns: fun1 + >>> @combine_docstrings(fun1, fun2, exclude=[DocstringReturns]) + >>> def decorated(a, b, c, d, e, f): pass + >>> print(decorated.__doc__) + short_description: fun2 + + long_description: fun2 + + :param a: fun1 + :param b: fun1 + :param c: fun2 + :param e: fun2 + + :param others: callables from which to parse docstrings. + :param exclude: an iterable of ``DocstringMeta`` subclasses to exclude when + combining docstrings. + :param style: style composed docstring. The default will infer the style + from the decorated function. + :param rendering_style: The rendering style used to compose a docstring. + :return: the decorated function with a modified docstring. + """ + + def wrapper(func: _Func) -> _Func: + sig = Signature.from_callable(func) + + comb_doc = parse(func.__doc__ or "") + docs = [parse(other.__doc__ or "") for other in others] + [comb_doc] + params = dict( + ChainMap( + *( + {param.arg_name: param for param in doc.params} + for doc in docs + ) + ) + ) + + for doc in reversed(docs): + if not doc.short_description: + continue + comb_doc.short_description = doc.short_description + comb_doc.blank_after_short_description = ( + doc.blank_after_short_description + ) + break + + for doc in reversed(docs): + if not doc.long_description: + continue + comb_doc.long_description = doc.long_description + comb_doc.blank_after_long_description = ( + doc.blank_after_long_description + ) + break + + combined = {} + for doc in docs: + metas = {} + for meta in doc.meta: + meta_type = type(meta) + if meta_type in exclude: + continue + metas.setdefault(meta_type, []).append(meta) + for meta_type, meta in metas.items(): + combined[meta_type] = meta + + combined[DocstringParam] = [ + params[name] for name in sig.parameters if name in params + ] + comb_doc.meta = list(chain(*combined.values())) + func.__doc__ = compose( + comb_doc, style=style, rendering_style=rendering_style + ) + return func + + return wrapper diff --git a/venv/lib/python3.12/site-packages/google/_upb/_message.abi3.so b/venv/lib/python3.12/site-packages/google/_upb/_message.abi3.so new file mode 100755 index 0000000..5e2bcca Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/_upb/_message.abi3.so differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/annotations_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/annotations_pb2.cpython-312.pyc new file mode 100644 index 0000000..19d33a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/annotations_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/auth_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/auth_pb2.cpython-312.pyc new file mode 100644 index 0000000..e21734d Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/auth_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/backend_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/backend_pb2.cpython-312.pyc new file mode 100644 index 0000000..4224ee4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/backend_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/billing_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/billing_pb2.cpython-312.pyc new file mode 100644 index 0000000..87fa6b8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/billing_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/client_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/client_pb2.cpython-312.pyc new file mode 100644 index 0000000..f254bd8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/client_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/config_change_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/config_change_pb2.cpython-312.pyc new file mode 100644 index 0000000..5954bd8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/config_change_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/consumer_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/consumer_pb2.cpython-312.pyc new file mode 100644 index 0000000..ee99c24 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/consumer_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/context_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/context_pb2.cpython-312.pyc new file mode 100644 index 0000000..8875b07 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/context_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/control_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/control_pb2.cpython-312.pyc new file mode 100644 index 0000000..2f59365 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/control_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/distribution_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/distribution_pb2.cpython-312.pyc new file mode 100644 index 0000000..bdbdf21 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/distribution_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/documentation_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/documentation_pb2.cpython-312.pyc new file mode 100644 index 0000000..a0e55c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/documentation_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/endpoint_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/endpoint_pb2.cpython-312.pyc new file mode 100644 index 0000000..06ac8db Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/endpoint_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/error_reason_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/error_reason_pb2.cpython-312.pyc new file mode 100644 index 0000000..3755a75 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/error_reason_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/field_behavior_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/field_behavior_pb2.cpython-312.pyc new file mode 100644 index 0000000..777af1b Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/field_behavior_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/field_info_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/field_info_pb2.cpython-312.pyc new file mode 100644 index 0000000..53a4b18 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/field_info_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/http_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/http_pb2.cpython-312.pyc new file mode 100644 index 0000000..77f2973 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/http_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/httpbody_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/httpbody_pb2.cpython-312.pyc new file mode 100644 index 0000000..e091da2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/httpbody_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/label_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/label_pb2.cpython-312.pyc new file mode 100644 index 0000000..e0d53ba Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/label_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/launch_stage_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/launch_stage_pb2.cpython-312.pyc new file mode 100644 index 0000000..e91a4f9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/launch_stage_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/log_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/log_pb2.cpython-312.pyc new file mode 100644 index 0000000..5eb2f6a Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/log_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/logging_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/logging_pb2.cpython-312.pyc new file mode 100644 index 0000000..5d538b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/logging_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/metric_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/metric_pb2.cpython-312.pyc new file mode 100644 index 0000000..bdb66b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/metric_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/monitored_resource_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/monitored_resource_pb2.cpython-312.pyc new file mode 100644 index 0000000..60069a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/monitored_resource_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/monitoring_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/monitoring_pb2.cpython-312.pyc new file mode 100644 index 0000000..a8f3e09 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/monitoring_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/policy_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/policy_pb2.cpython-312.pyc new file mode 100644 index 0000000..2239450 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/policy_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/quota_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/quota_pb2.cpython-312.pyc new file mode 100644 index 0000000..f835341 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/quota_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/resource_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/resource_pb2.cpython-312.pyc new file mode 100644 index 0000000..b77e58f Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/resource_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/routing_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/routing_pb2.cpython-312.pyc new file mode 100644 index 0000000..dfc7f8b Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/routing_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/service_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/service_pb2.cpython-312.pyc new file mode 100644 index 0000000..5ca907d Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/service_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/source_info_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/source_info_pb2.cpython-312.pyc new file mode 100644 index 0000000..91e8908 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/source_info_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/system_parameter_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/system_parameter_pb2.cpython-312.pyc new file mode 100644 index 0000000..5ba35b6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/system_parameter_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/usage_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/usage_pb2.cpython-312.pyc new file mode 100644 index 0000000..8abc04f Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/usage_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/__pycache__/visibility_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/api/__pycache__/visibility_pb2.cpython-312.pyc new file mode 100644 index 0000000..4abab05 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/api/__pycache__/visibility_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/api/annotations.proto b/venv/lib/python3.12/site-packages/google/api/annotations.proto new file mode 100644 index 0000000..417edd8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} diff --git a/venv/lib/python3.12/site-packages/google/api/annotations_pb2.py b/venv/lib/python3.12/site-packages/google/api/annotations_pb2.py new file mode 100644 index 0000000..5c3dde5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/annotations_pb2.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/annotations.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/annotations.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + +from google.api import http_pb2 as google_dot_api_dot_http__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1cgoogle/api/annotations.proto\x12\ngoogle.api\x1a\x15google/api/http.proto\x1a google/protobuf/descriptor.proto:E\n\x04http\x12\x1e.google.protobuf.MethodOptions\x18\xb0\xca\xbc" \x01(\x0b\x32\x14.google.api.HttpRuleBn\n\x0e\x63om.google.apiB\x10\x41nnotationsProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.api.annotations_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\020AnnotationsProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI" +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/annotations_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/annotations_pb2.pyi new file mode 100644 index 0000000..4c4afeb --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/annotations_pb2.pyi @@ -0,0 +1,24 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pb2 as _descriptor_pb2 + +from google.api import http_pb2 as _http_pb2 + +DESCRIPTOR: _descriptor.FileDescriptor +HTTP_FIELD_NUMBER: _ClassVar[int] +http: _descriptor.FieldDescriptor diff --git a/venv/lib/python3.12/site-packages/google/api/auth.proto b/venv/lib/python3.12/site-packages/google/api/auth.proto new file mode 100644 index 0000000..9b025ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/auth.proto @@ -0,0 +1,238 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "AuthProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// `Authentication` defines the authentication configuration for API methods +// provided by an API service. +// +// Example: +// +// name: calendar.googleapis.com +// authentication: +// providers: +// - id: google_calendar_auth +// jwks_uri: https://www.googleapis.com/oauth2/v1/certs +// issuer: https://securetoken.google.com +// rules: +// - selector: "*" +// requirements: +// provider_id: google_calendar_auth +// - selector: google.calendar.Delegate +// oauth: +// canonical_scopes: https://www.googleapis.com/auth/calendar.read +message Authentication { + // A list of authentication rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated AuthenticationRule rules = 3; + + // Defines a set of authentication providers that a service supports. + repeated AuthProvider providers = 4; +} + +// Authentication rules for the service. +// +// By default, if a method has any authentication requirements, every request +// must include a valid credential matching one of the requirements. +// It's an error to include more than one kind of credential in a single +// request. +// +// If a method doesn't have any auth requirements, request credentials will be +// ignored. +message AuthenticationRule { + // Selects the methods to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // The requirements for OAuth credentials. + OAuthRequirements oauth = 2; + + // If true, the service accepts API keys without any other credential. + // This flag only applies to HTTP and gRPC requests. + bool allow_without_credential = 5; + + // Requirements for additional authentication providers. + repeated AuthRequirement requirements = 7; +} + +// Specifies a location to extract JWT from an API request. +message JwtLocation { + oneof in { + // Specifies HTTP header name to extract JWT token. + string header = 1; + + // Specifies URL query parameter name to extract JWT token. + string query = 2; + + // Specifies cookie name to extract JWT token. + string cookie = 4; + } + + // The value prefix. The value format is "value_prefix{token}" + // Only applies to "in" header type. Must be empty for "in" query type. + // If not empty, the header value has to match (case sensitive) this prefix. + // If not matched, JWT will not be extracted. If matched, JWT will be + // extracted after the prefix is removed. + // + // For example, for "Authorization: Bearer {JWT}", + // value_prefix="Bearer " with a space at the end. + string value_prefix = 3; +} + +// Configuration for an authentication provider, including support for +// [JSON Web Token +// (JWT)](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32). +message AuthProvider { + // The unique identifier of the auth provider. It will be referred to by + // `AuthRequirement.provider_id`. + // + // Example: "bookstore_auth". + string id = 1; + + // Identifies the principal that issued the JWT. See + // https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.1 + // Usually a URL or an email address. + // + // Example: https://securetoken.google.com + // Example: 1234567-compute@developer.gserviceaccount.com + string issuer = 2; + + // URL of the provider's public key set to validate signature of the JWT. See + // [OpenID + // Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + // Optional if the key set document: + // - can be retrieved from + // [OpenID + // Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html) + // of the issuer. + // - can be inferred from the email domain of the issuer (e.g. a Google + // service account). + // + // Example: https://www.googleapis.com/oauth2/v1/certs + string jwks_uri = 3; + + // The list of JWT + // [audiences](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.3). + // that are allowed to access. A JWT containing any of these audiences will + // be accepted. When this setting is absent, JWTs with audiences: + // - "https://[service.name]/[google.protobuf.Api.name]" + // - "https://[service.name]/" + // will be accepted. + // For example, if no audiences are in the setting, LibraryService API will + // accept JWTs with the following audiences: + // - + // https://library-example.googleapis.com/google.example.library.v1.LibraryService + // - https://library-example.googleapis.com/ + // + // Example: + // + // audiences: bookstore_android.apps.googleusercontent.com, + // bookstore_web.apps.googleusercontent.com + string audiences = 4; + + // Redirect URL if JWT token is required but not present or is expired. + // Implement authorizationUrl of securityDefinitions in OpenAPI spec. + string authorization_url = 5; + + // Defines the locations to extract the JWT. For now it is only used by the + // Cloud Endpoints to store the OpenAPI extension [x-google-jwt-locations] + // (https://cloud.google.com/endpoints/docs/openapi/openapi-extensions#x-google-jwt-locations) + // + // JWT locations can be one of HTTP headers, URL query parameters or + // cookies. The rule is that the first match wins. + // + // If not specified, default to use following 3 locations: + // 1) Authorization: Bearer + // 2) x-goog-iap-jwt-assertion + // 3) access_token query parameter + // + // Default locations can be specified as followings: + // jwt_locations: + // - header: Authorization + // value_prefix: "Bearer " + // - header: x-goog-iap-jwt-assertion + // - query: access_token + repeated JwtLocation jwt_locations = 6; +} + +// OAuth scopes are a way to define data and permissions on data. For example, +// there are scopes defined for "Read-only access to Google Calendar" and +// "Access to Cloud Platform". Users can consent to a scope for an application, +// giving it permission to access that data on their behalf. +// +// OAuth scope specifications should be fairly coarse grained; a user will need +// to see and understand the text description of what your scope means. +// +// In most cases: use one or at most two OAuth scopes for an entire family of +// products. If your product has multiple APIs, you should probably be sharing +// the OAuth scope across all of those APIs. +// +// When you need finer grained OAuth consent screens: talk with your product +// management about how developers will use them in practice. +// +// Please note that even though each of the canonical scopes is enough for a +// request to be accepted and passed to the backend, a request can still fail +// due to the backend requiring additional scopes or permissions. +message OAuthRequirements { + // The list of publicly documented OAuth scopes that are allowed access. An + // OAuth token containing any of these scopes will be accepted. + // + // + // Example: + // + // canonical_scopes: https://www.googleapis.com/auth/calendar, + // https://www.googleapis.com/auth/calendar.read + string canonical_scopes = 1; +} + +// User-defined authentication requirements, including support for +// [JSON Web Token +// (JWT)](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32). +message AuthRequirement { + // [id][google.api.AuthProvider.id] from authentication provider. + // + // Example: + // + // provider_id: bookstore_auth + string provider_id = 1; + + // NOTE: This will be deprecated soon, once AuthProvider.audiences is + // implemented and accepted in all the runtime components. + // + // The list of JWT + // [audiences](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.3). + // that are allowed to access. A JWT containing any of these audiences will + // be accepted. When this setting is absent, only JWTs with audience + // "https://[Service_name][google.api.Service.name]/[API_name][google.protobuf.Api.name]" + // will be accepted. For example, if no audiences are in the setting, + // LibraryService API will only accept JWTs with the following audience + // "https://library-example.googleapis.com/google.example.library.v1.LibraryService". + // + // Example: + // + // audiences: bookstore_android.apps.googleusercontent.com, + // bookstore_web.apps.googleusercontent.com + string audiences = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/api/auth_pb2.py b/venv/lib/python3.12/site-packages/google/api/auth_pb2.py new file mode 100644 index 0000000..ec83052 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/auth_pb2.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/auth.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/auth.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x15google/api/auth.proto\x12\ngoogle.api"l\n\x0e\x41uthentication\x12-\n\x05rules\x18\x03 \x03(\x0b\x32\x1e.google.api.AuthenticationRule\x12+\n\tproviders\x18\x04 \x03(\x0b\x32\x18.google.api.AuthProvider"\xa9\x01\n\x12\x41uthenticationRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12,\n\x05oauth\x18\x02 \x01(\x0b\x32\x1d.google.api.OAuthRequirements\x12 \n\x18\x61llow_without_credential\x18\x05 \x01(\x08\x12\x31\n\x0crequirements\x18\x07 \x03(\x0b\x32\x1b.google.api.AuthRequirement"^\n\x0bJwtLocation\x12\x10\n\x06header\x18\x01 \x01(\tH\x00\x12\x0f\n\x05query\x18\x02 \x01(\tH\x00\x12\x10\n\x06\x63ookie\x18\x04 \x01(\tH\x00\x12\x14\n\x0cvalue_prefix\x18\x03 \x01(\tB\x04\n\x02in"\x9a\x01\n\x0c\x41uthProvider\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0e\n\x06issuer\x18\x02 \x01(\t\x12\x10\n\x08jwks_uri\x18\x03 \x01(\t\x12\x11\n\taudiences\x18\x04 \x01(\t\x12\x19\n\x11\x61uthorization_url\x18\x05 \x01(\t\x12.\n\rjwt_locations\x18\x06 \x03(\x0b\x32\x17.google.api.JwtLocation"-\n\x11OAuthRequirements\x12\x18\n\x10\x63\x61nonical_scopes\x18\x01 \x01(\t"9\n\x0f\x41uthRequirement\x12\x13\n\x0bprovider_id\x18\x01 \x01(\t\x12\x11\n\taudiences\x18\x02 \x01(\tBk\n\x0e\x63om.google.apiB\tAuthProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.auth_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\tAuthProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_AUTHENTICATION"]._serialized_start = 37 + _globals["_AUTHENTICATION"]._serialized_end = 145 + _globals["_AUTHENTICATIONRULE"]._serialized_start = 148 + _globals["_AUTHENTICATIONRULE"]._serialized_end = 317 + _globals["_JWTLOCATION"]._serialized_start = 319 + _globals["_JWTLOCATION"]._serialized_end = 413 + _globals["_AUTHPROVIDER"]._serialized_start = 416 + _globals["_AUTHPROVIDER"]._serialized_end = 570 + _globals["_OAUTHREQUIREMENTS"]._serialized_start = 572 + _globals["_OAUTHREQUIREMENTS"]._serialized_end = 617 + _globals["_AUTHREQUIREMENT"]._serialized_start = 619 + _globals["_AUTHREQUIREMENT"]._serialized_end = 676 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/auth_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/auth_pb2.pyi new file mode 100644 index 0000000..421473a --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/auth_pb2.pyi @@ -0,0 +1,120 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class Authentication(_message.Message): + __slots__ = ("rules", "providers") + RULES_FIELD_NUMBER: _ClassVar[int] + PROVIDERS_FIELD_NUMBER: _ClassVar[int] + rules: _containers.RepeatedCompositeFieldContainer[AuthenticationRule] + providers: _containers.RepeatedCompositeFieldContainer[AuthProvider] + def __init__( + self, + rules: _Optional[_Iterable[_Union[AuthenticationRule, _Mapping]]] = ..., + providers: _Optional[_Iterable[_Union[AuthProvider, _Mapping]]] = ..., + ) -> None: ... + +class AuthenticationRule(_message.Message): + __slots__ = ("selector", "oauth", "allow_without_credential", "requirements") + SELECTOR_FIELD_NUMBER: _ClassVar[int] + OAUTH_FIELD_NUMBER: _ClassVar[int] + ALLOW_WITHOUT_CREDENTIAL_FIELD_NUMBER: _ClassVar[int] + REQUIREMENTS_FIELD_NUMBER: _ClassVar[int] + selector: str + oauth: OAuthRequirements + allow_without_credential: bool + requirements: _containers.RepeatedCompositeFieldContainer[AuthRequirement] + def __init__( + self, + selector: _Optional[str] = ..., + oauth: _Optional[_Union[OAuthRequirements, _Mapping]] = ..., + allow_without_credential: _Optional[bool] = ..., + requirements: _Optional[_Iterable[_Union[AuthRequirement, _Mapping]]] = ..., + ) -> None: ... + +class JwtLocation(_message.Message): + __slots__ = ("header", "query", "cookie", "value_prefix") + HEADER_FIELD_NUMBER: _ClassVar[int] + QUERY_FIELD_NUMBER: _ClassVar[int] + COOKIE_FIELD_NUMBER: _ClassVar[int] + VALUE_PREFIX_FIELD_NUMBER: _ClassVar[int] + header: str + query: str + cookie: str + value_prefix: str + def __init__( + self, + header: _Optional[str] = ..., + query: _Optional[str] = ..., + cookie: _Optional[str] = ..., + value_prefix: _Optional[str] = ..., + ) -> None: ... + +class AuthProvider(_message.Message): + __slots__ = ( + "id", + "issuer", + "jwks_uri", + "audiences", + "authorization_url", + "jwt_locations", + ) + ID_FIELD_NUMBER: _ClassVar[int] + ISSUER_FIELD_NUMBER: _ClassVar[int] + JWKS_URI_FIELD_NUMBER: _ClassVar[int] + AUDIENCES_FIELD_NUMBER: _ClassVar[int] + AUTHORIZATION_URL_FIELD_NUMBER: _ClassVar[int] + JWT_LOCATIONS_FIELD_NUMBER: _ClassVar[int] + id: str + issuer: str + jwks_uri: str + audiences: str + authorization_url: str + jwt_locations: _containers.RepeatedCompositeFieldContainer[JwtLocation] + def __init__( + self, + id: _Optional[str] = ..., + issuer: _Optional[str] = ..., + jwks_uri: _Optional[str] = ..., + audiences: _Optional[str] = ..., + authorization_url: _Optional[str] = ..., + jwt_locations: _Optional[_Iterable[_Union[JwtLocation, _Mapping]]] = ..., + ) -> None: ... + +class OAuthRequirements(_message.Message): + __slots__ = ("canonical_scopes",) + CANONICAL_SCOPES_FIELD_NUMBER: _ClassVar[int] + canonical_scopes: str + def __init__(self, canonical_scopes: _Optional[str] = ...) -> None: ... + +class AuthRequirement(_message.Message): + __slots__ = ("provider_id", "audiences") + PROVIDER_ID_FIELD_NUMBER: _ClassVar[int] + AUDIENCES_FIELD_NUMBER: _ClassVar[int] + provider_id: str + audiences: str + def __init__( + self, provider_id: _Optional[str] = ..., audiences: _Optional[str] = ... + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/backend.proto b/venv/lib/python3.12/site-packages/google/api/backend.proto new file mode 100644 index 0000000..7bff445 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/backend.proto @@ -0,0 +1,196 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "BackendProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// `Backend` defines the backend configuration for a service. +message Backend { + // A list of API backend rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated BackendRule rules = 1; +} + +// A backend rule provides configuration for an individual API element. +message BackendRule { + // Path Translation specifies how to combine the backend address with the + // request path in order to produce the appropriate forwarding URL for the + // request. + // + // Path Translation is applicable only to HTTP-based backends. Backends which + // do not accept requests over HTTP/HTTPS should leave `path_translation` + // unspecified. + enum PathTranslation { + PATH_TRANSLATION_UNSPECIFIED = 0; + + // Use the backend address as-is, with no modification to the path. If the + // URL pattern contains variables, the variable names and values will be + // appended to the query string. If a query string parameter and a URL + // pattern variable have the same name, this may result in duplicate keys in + // the query string. + // + // # Examples + // + // Given the following operation config: + // + // Method path: /api/company/{cid}/user/{uid} + // Backend address: https://example.cloudfunctions.net/getUser + // + // Requests to the following request paths will call the backend at the + // translated path: + // + // Request path: /api/company/widgetworks/user/johndoe + // Translated: + // https://example.cloudfunctions.net/getUser?cid=widgetworks&uid=johndoe + // + // Request path: /api/company/widgetworks/user/johndoe?timezone=EST + // Translated: + // https://example.cloudfunctions.net/getUser?timezone=EST&cid=widgetworks&uid=johndoe + CONSTANT_ADDRESS = 1; + + // The request path will be appended to the backend address. + // + // # Examples + // + // Given the following operation config: + // + // Method path: /api/company/{cid}/user/{uid} + // Backend address: https://example.appspot.com + // + // Requests to the following request paths will call the backend at the + // translated path: + // + // Request path: /api/company/widgetworks/user/johndoe + // Translated: + // https://example.appspot.com/api/company/widgetworks/user/johndoe + // + // Request path: /api/company/widgetworks/user/johndoe?timezone=EST + // Translated: + // https://example.appspot.com/api/company/widgetworks/user/johndoe?timezone=EST + APPEND_PATH_TO_ADDRESS = 2; + } + + // Selects the methods to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // The address of the API backend. + // + // The scheme is used to determine the backend protocol and security. + // The following schemes are accepted: + // + // SCHEME PROTOCOL SECURITY + // http:// HTTP None + // https:// HTTP TLS + // grpc:// gRPC None + // grpcs:// gRPC TLS + // + // It is recommended to explicitly include a scheme. Leaving out the scheme + // may cause constrasting behaviors across platforms. + // + // If the port is unspecified, the default is: + // - 80 for schemes without TLS + // - 443 for schemes with TLS + // + // For HTTP backends, use [protocol][google.api.BackendRule.protocol] + // to specify the protocol version. + string address = 2; + + // The number of seconds to wait for a response from a request. The default + // varies based on the request protocol and deployment environment. + double deadline = 3; + + // Deprecated, do not use. + double min_deadline = 4 [deprecated = true]; + + // The number of seconds to wait for the completion of a long running + // operation. The default is no deadline. + double operation_deadline = 5; + + // Path translation specifies how to combine the backend address with the + // request path in order to produce the appropriate forwarding URL for the + // request. See [PathTranslation][google.api.BackendRule.PathTranslation] for + // more details. + PathTranslation path_translation = 6; + + // Authentication settings used by the backend. + // + // These are typically used to provide service management functionality to + // a backend served on a publicly-routable URL. The `authentication` + // details should match the authentication behavior used by the backend. + // + // For example, specifying `jwt_audience` implies that the backend expects + // authentication via a JWT. + // + // When authentication is unspecified, the resulting behavior is the same + // as `disable_auth` set to `true`. + // + // Refer to https://developers.google.com/identity/protocols/OpenIDConnect for + // JWT ID token. + oneof authentication { + // The JWT audience is used when generating a JWT ID token for the backend. + // This ID token will be added in the HTTP "authorization" header, and sent + // to the backend. + string jwt_audience = 7; + + // When disable_auth is true, a JWT ID token won't be generated and the + // original "Authorization" HTTP header will be preserved. If the header is + // used to carry the original token and is expected by the backend, this + // field must be set to true to preserve the header. + bool disable_auth = 8; + } + + // The protocol used for sending a request to the backend. + // The supported values are "http/1.1" and "h2". + // + // The default value is inferred from the scheme in the + // [address][google.api.BackendRule.address] field: + // + // SCHEME PROTOCOL + // http:// http/1.1 + // https:// http/1.1 + // grpc:// h2 + // grpcs:// h2 + // + // For secure HTTP backends (https://) that support HTTP/2, set this field + // to "h2" for improved performance. + // + // Configuring this field to non-default values is only supported for secure + // HTTP backends. This field will be ignored for all other backends. + // + // See + // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids + // for more details on the supported values. + string protocol = 9; + + // The map between request protocol and the backend address. + map overrides_by_request_protocol = 10; + + // The load balancing policy used for connection to the application backend. + // + // Defined as an arbitrary string to accomondate custom load balancing + // policies supported by the underlying channel, but suggest most users use + // one of the standard policies, such as the default, "RoundRobin". + string load_balancing_policy = 11; +} diff --git a/venv/lib/python3.12/site-packages/google/api/backend_pb2.py b/venv/lib/python3.12/site-packages/google/api/backend_pb2.py new file mode 100644 index 0000000..4af4f5a --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/backend_pb2.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/backend.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/backend.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x18google/api/backend.proto\x12\ngoogle.api"1\n\x07\x42\x61\x63kend\x12&\n\x05rules\x18\x01 \x03(\x0b\x32\x17.google.api.BackendRule"\xd1\x04\n\x0b\x42\x61\x63kendRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\x12\x10\n\x08\x64\x65\x61\x64line\x18\x03 \x01(\x01\x12\x18\n\x0cmin_deadline\x18\x04 \x01(\x01\x42\x02\x18\x01\x12\x1a\n\x12operation_deadline\x18\x05 \x01(\x01\x12\x41\n\x10path_translation\x18\x06 \x01(\x0e\x32\'.google.api.BackendRule.PathTranslation\x12\x16\n\x0cjwt_audience\x18\x07 \x01(\tH\x00\x12\x16\n\x0c\x64isable_auth\x18\x08 \x01(\x08H\x00\x12\x10\n\x08protocol\x18\t \x01(\t\x12^\n\x1doverrides_by_request_protocol\x18\n \x03(\x0b\x32\x37.google.api.BackendRule.OverridesByRequestProtocolEntry\x12\x1d\n\x15load_balancing_policy\x18\x0b \x01(\t\x1aZ\n\x1fOverridesByRequestProtocolEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.google.api.BackendRule:\x02\x38\x01"e\n\x0fPathTranslation\x12 \n\x1cPATH_TRANSLATION_UNSPECIFIED\x10\x00\x12\x14\n\x10\x43ONSTANT_ADDRESS\x10\x01\x12\x1a\n\x16\x41PPEND_PATH_TO_ADDRESS\x10\x02\x42\x10\n\x0e\x61uthenticationBn\n\x0e\x63om.google.apiB\x0c\x42\x61\x63kendProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.backend_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\014BackendProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_BACKENDRULE_OVERRIDESBYREQUESTPROTOCOLENTRY"]._loaded_options = None + _globals[ + "_BACKENDRULE_OVERRIDESBYREQUESTPROTOCOLENTRY" + ]._serialized_options = b"8\001" + _globals["_BACKENDRULE"].fields_by_name["min_deadline"]._loaded_options = None + _globals["_BACKENDRULE"].fields_by_name[ + "min_deadline" + ]._serialized_options = b"\030\001" + _globals["_BACKEND"]._serialized_start = 40 + _globals["_BACKEND"]._serialized_end = 89 + _globals["_BACKENDRULE"]._serialized_start = 92 + _globals["_BACKENDRULE"]._serialized_end = 685 + _globals["_BACKENDRULE_OVERRIDESBYREQUESTPROTOCOLENTRY"]._serialized_start = 474 + _globals["_BACKENDRULE_OVERRIDESBYREQUESTPROTOCOLENTRY"]._serialized_end = 564 + _globals["_BACKENDRULE_PATHTRANSLATION"]._serialized_start = 566 + _globals["_BACKENDRULE_PATHTRANSLATION"]._serialized_end = 667 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/backend_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/backend_pb2.pyi new file mode 100644 index 0000000..162a4bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/backend_pb2.pyi @@ -0,0 +1,106 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor + +class Backend(_message.Message): + __slots__ = ("rules",) + RULES_FIELD_NUMBER: _ClassVar[int] + rules: _containers.RepeatedCompositeFieldContainer[BackendRule] + def __init__( + self, rules: _Optional[_Iterable[_Union[BackendRule, _Mapping]]] = ... + ) -> None: ... + +class BackendRule(_message.Message): + __slots__ = ( + "selector", + "address", + "deadline", + "min_deadline", + "operation_deadline", + "path_translation", + "jwt_audience", + "disable_auth", + "protocol", + "overrides_by_request_protocol", + "load_balancing_policy", + ) + class PathTranslation(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + PATH_TRANSLATION_UNSPECIFIED: _ClassVar[BackendRule.PathTranslation] + CONSTANT_ADDRESS: _ClassVar[BackendRule.PathTranslation] + APPEND_PATH_TO_ADDRESS: _ClassVar[BackendRule.PathTranslation] + + PATH_TRANSLATION_UNSPECIFIED: BackendRule.PathTranslation + CONSTANT_ADDRESS: BackendRule.PathTranslation + APPEND_PATH_TO_ADDRESS: BackendRule.PathTranslation + class OverridesByRequestProtocolEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: BackendRule + def __init__( + self, + key: _Optional[str] = ..., + value: _Optional[_Union[BackendRule, _Mapping]] = ..., + ) -> None: ... + + SELECTOR_FIELD_NUMBER: _ClassVar[int] + ADDRESS_FIELD_NUMBER: _ClassVar[int] + DEADLINE_FIELD_NUMBER: _ClassVar[int] + MIN_DEADLINE_FIELD_NUMBER: _ClassVar[int] + OPERATION_DEADLINE_FIELD_NUMBER: _ClassVar[int] + PATH_TRANSLATION_FIELD_NUMBER: _ClassVar[int] + JWT_AUDIENCE_FIELD_NUMBER: _ClassVar[int] + DISABLE_AUTH_FIELD_NUMBER: _ClassVar[int] + PROTOCOL_FIELD_NUMBER: _ClassVar[int] + OVERRIDES_BY_REQUEST_PROTOCOL_FIELD_NUMBER: _ClassVar[int] + LOAD_BALANCING_POLICY_FIELD_NUMBER: _ClassVar[int] + selector: str + address: str + deadline: float + min_deadline: float + operation_deadline: float + path_translation: BackendRule.PathTranslation + jwt_audience: str + disable_auth: bool + protocol: str + overrides_by_request_protocol: _containers.MessageMap[str, BackendRule] + load_balancing_policy: str + def __init__( + self, + selector: _Optional[str] = ..., + address: _Optional[str] = ..., + deadline: _Optional[float] = ..., + min_deadline: _Optional[float] = ..., + operation_deadline: _Optional[float] = ..., + path_translation: _Optional[_Union[BackendRule.PathTranslation, str]] = ..., + jwt_audience: _Optional[str] = ..., + disable_auth: _Optional[bool] = ..., + protocol: _Optional[str] = ..., + overrides_by_request_protocol: _Optional[_Mapping[str, BackendRule]] = ..., + load_balancing_policy: _Optional[str] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/billing.proto b/venv/lib/python3.12/site-packages/google/api/billing.proto new file mode 100644 index 0000000..2b86bb6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/billing.proto @@ -0,0 +1,77 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "BillingProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Billing related configuration of the service. +// +// The following example shows how to configure monitored resources and metrics +// for billing, `consumer_destinations` is the only supported destination and +// the monitored resources need at least one label key +// `cloud.googleapis.com/location` to indicate the location of the billing +// usage, using different monitored resources between monitoring and billing is +// recommended so they can be evolved independently: +// +// +// monitored_resources: +// - type: library.googleapis.com/billing_branch +// labels: +// - key: cloud.googleapis.com/location +// description: | +// Predefined label to support billing location restriction. +// - key: city +// description: | +// Custom label to define the city where the library branch is located +// in. +// - key: name +// description: Custom label to define the name of the library branch. +// metrics: +// - name: library.googleapis.com/book/borrowed_count +// metric_kind: DELTA +// value_type: INT64 +// unit: "1" +// billing: +// consumer_destinations: +// - monitored_resource: library.googleapis.com/billing_branch +// metrics: +// - library.googleapis.com/book/borrowed_count +message Billing { + // Configuration of a specific billing destination (Currently only support + // bill against consumer project). + message BillingDestination { + // The monitored resource type. The type must be defined in + // [Service.monitored_resources][google.api.Service.monitored_resources] + // section. + string monitored_resource = 1; + + // Names of the metrics to report to this billing destination. + // Each name must be defined in + // [Service.metrics][google.api.Service.metrics] section. + repeated string metrics = 2; + } + + // Billing configurations for sending metrics to the consumer project. + // There can be multiple consumer destinations per service, each one must have + // a different monitored resource type. A metric can be used in at most + // one consumer destination. + repeated BillingDestination consumer_destinations = 8; +} diff --git a/venv/lib/python3.12/site-packages/google/api/billing_pb2.py b/venv/lib/python3.12/site-packages/google/api/billing_pb2.py new file mode 100644 index 0000000..37a99bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/billing_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/billing.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/billing.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x18google/api/billing.proto\x12\ngoogle.api"\x93\x01\n\x07\x42illing\x12\x45\n\x15\x63onsumer_destinations\x18\x08 \x03(\x0b\x32&.google.api.Billing.BillingDestination\x1a\x41\n\x12\x42illingDestination\x12\x1a\n\x12monitored_resource\x18\x01 \x01(\t\x12\x0f\n\x07metrics\x18\x02 \x03(\tBn\n\x0e\x63om.google.apiB\x0c\x42illingProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.billing_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\014BillingProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_BILLING"]._serialized_start = 41 + _globals["_BILLING"]._serialized_end = 188 + _globals["_BILLING_BILLINGDESTINATION"]._serialized_start = 123 + _globals["_BILLING_BILLINGDESTINATION"]._serialized_end = 188 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/billing_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/billing_pb2.pyi new file mode 100644 index 0000000..f50e879 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/billing_pb2.pyi @@ -0,0 +1,50 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class Billing(_message.Message): + __slots__ = ("consumer_destinations",) + class BillingDestination(_message.Message): + __slots__ = ("monitored_resource", "metrics") + MONITORED_RESOURCE_FIELD_NUMBER: _ClassVar[int] + METRICS_FIELD_NUMBER: _ClassVar[int] + monitored_resource: str + metrics: _containers.RepeatedScalarFieldContainer[str] + def __init__( + self, + monitored_resource: _Optional[str] = ..., + metrics: _Optional[_Iterable[str]] = ..., + ) -> None: ... + + CONSUMER_DESTINATIONS_FIELD_NUMBER: _ClassVar[int] + consumer_destinations: _containers.RepeatedCompositeFieldContainer[ + Billing.BillingDestination + ] + def __init__( + self, + consumer_destinations: _Optional[ + _Iterable[_Union[Billing.BillingDestination, _Mapping]] + ] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/client.proto b/venv/lib/python3.12/site-packages/google/api/client.proto new file mode 100644 index 0000000..332e0d0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/client.proto @@ -0,0 +1,598 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/launch_stage.proto"; +import "google/protobuf/descriptor.proto"; +import "google/protobuf/duration.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "ClientProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // A definition of a client library method signature. + // + // In client libraries, each proto RPC corresponds to one or more methods + // which the end user is able to call, and calls the underlying RPC. + // Normally, this method receives a single argument (a struct or instance + // corresponding to the RPC request object). Defining this field will + // add one or more overloads providing flattened or simpler method signatures + // in some languages. + // + // The fields on the method signature are provided as a comma-separated + // string. + // + // For example, the proto RPC and annotation: + // + // rpc CreateSubscription(CreateSubscriptionRequest) + // returns (Subscription) { + // option (google.api.method_signature) = "name,topic"; + // } + // + // Would add the following Java overload (in addition to the method accepting + // the request object): + // + // public final Subscription createSubscription(String name, String topic) + // + // The following backwards-compatibility guidelines apply: + // + // * Adding this annotation to an unannotated method is backwards + // compatible. + // * Adding this annotation to a method which already has existing + // method signature annotations is backwards compatible if and only if + // the new method signature annotation is last in the sequence. + // * Modifying or removing an existing method signature annotation is + // a breaking change. + // * Re-ordering existing method signature annotations is a breaking + // change. + repeated string method_signature = 1051; +} + +extend google.protobuf.ServiceOptions { + // The hostname for this service. + // This should be specified with no prefix or protocol. + // + // Example: + // + // service Foo { + // option (google.api.default_host) = "foo.googleapi.com"; + // ... + // } + string default_host = 1049; + + // OAuth scopes needed for the client. + // + // Example: + // + // service Foo { + // option (google.api.oauth_scopes) = \ + // "https://www.googleapis.com/auth/cloud-platform"; + // ... + // } + // + // If there is more than one scope, use a comma-separated string: + // + // Example: + // + // service Foo { + // option (google.api.oauth_scopes) = \ + // "https://www.googleapis.com/auth/cloud-platform," + // "https://www.googleapis.com/auth/monitoring"; + // ... + // } + string oauth_scopes = 1050; + + // The API version of this service, which should be sent by version-aware + // clients to the service. This allows services to abide by the schema and + // behavior of the service at the time this API version was deployed. + // The format of the API version must be treated as opaque by clients. + // Services may use a format with an apparent structure, but clients must + // not rely on this to determine components within an API version, or attempt + // to construct other valid API versions. Note that this is for upcoming + // functionality and may not be implemented for all services. + // + // Example: + // + // service Foo { + // option (google.api.api_version) = "v1_20230821_preview"; + // } + string api_version = 525000001; +} + +// Required information for every language. +message CommonLanguageSettings { + // Link to automatically generated reference documentation. Example: + // https://cloud.google.com/nodejs/docs/reference/asset/latest + string reference_docs_uri = 1 [deprecated = true]; + + // The destination where API teams want this client library to be published. + repeated ClientLibraryDestination destinations = 2; + + // Configuration for which RPCs should be generated in the GAPIC client. + // + // Note: This field should not be used in most cases. + SelectiveGapicGeneration selective_gapic_generation = 3; +} + +// Details about how and where to publish client libraries. +message ClientLibrarySettings { + // Version of the API to apply these settings to. This is the full protobuf + // package for the API, ending in the version element. + // Examples: "google.cloud.speech.v1" and "google.spanner.admin.database.v1". + string version = 1; + + // Launch stage of this version of the API. + LaunchStage launch_stage = 2; + + // When using transport=rest, the client request will encode enums as + // numbers rather than strings. + bool rest_numeric_enums = 3; + + // Settings for legacy Java features, supported in the Service YAML. + JavaSettings java_settings = 21; + + // Settings for C++ client libraries. + CppSettings cpp_settings = 22; + + // Settings for PHP client libraries. + PhpSettings php_settings = 23; + + // Settings for Python client libraries. + PythonSettings python_settings = 24; + + // Settings for Node client libraries. + NodeSettings node_settings = 25; + + // Settings for .NET client libraries. + DotnetSettings dotnet_settings = 26; + + // Settings for Ruby client libraries. + RubySettings ruby_settings = 27; + + // Settings for Go client libraries. + GoSettings go_settings = 28; +} + +// This message configures the settings for publishing [Google Cloud Client +// libraries](https://cloud.google.com/apis/docs/cloud-client-libraries) +// generated from the service config. +message Publishing { + // A list of API method settings, e.g. the behavior for methods that use the + // long-running operation pattern. + repeated MethodSettings method_settings = 2; + + // Link to a *public* URI where users can report issues. Example: + // https://issuetracker.google.com/issues/new?component=190865&template=1161103 + string new_issue_uri = 101; + + // Link to product home page. Example: + // https://cloud.google.com/asset-inventory/docs/overview + string documentation_uri = 102; + + // Used as a tracking tag when collecting data about the APIs developer + // relations artifacts like docs, packages delivered to package managers, + // etc. Example: "speech". + string api_short_name = 103; + + // GitHub label to apply to issues and pull requests opened for this API. + string github_label = 104; + + // GitHub teams to be added to CODEOWNERS in the directory in GitHub + // containing source code for the client libraries for this API. + repeated string codeowner_github_teams = 105; + + // A prefix used in sample code when demarking regions to be included in + // documentation. + string doc_tag_prefix = 106; + + // For whom the client library is being published. + ClientLibraryOrganization organization = 107; + + // Client library settings. If the same version string appears multiple + // times in this list, then the last one wins. Settings from earlier + // settings with the same version string are discarded. + repeated ClientLibrarySettings library_settings = 109; + + // Optional link to proto reference documentation. Example: + // https://cloud.google.com/pubsub/lite/docs/reference/rpc + string proto_reference_documentation_uri = 110; + + // Optional link to REST reference documentation. Example: + // https://cloud.google.com/pubsub/lite/docs/reference/rest + string rest_reference_documentation_uri = 111; +} + +// Settings for Java client libraries. +message JavaSettings { + // The package name to use in Java. Clobbers the java_package option + // set in the protobuf. This should be used **only** by APIs + // who have already set the language_settings.java.package_name" field + // in gapic.yaml. API teams should use the protobuf java_package option + // where possible. + // + // Example of a YAML configuration:: + // + // publishing: + // library_settings: + // java_settings: + // library_package: com.google.cloud.pubsub.v1 + string library_package = 1; + + // Configure the Java class name to use instead of the service's for its + // corresponding generated GAPIC client. Keys are fully-qualified + // service names as they appear in the protobuf (including the full + // the language_settings.java.interface_names" field in gapic.yaml. API + // teams should otherwise use the service name as it appears in the + // protobuf. + // + // Example of a YAML configuration:: + // + // publishing: + // java_settings: + // service_class_names: + // - google.pubsub.v1.Publisher: TopicAdmin + // - google.pubsub.v1.Subscriber: SubscriptionAdmin + map service_class_names = 2; + + // Some settings. + CommonLanguageSettings common = 3; +} + +// Settings for C++ client libraries. +message CppSettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Settings for Php client libraries. +message PhpSettings { + // Some settings. + CommonLanguageSettings common = 1; + + // The package name to use in Php. Clobbers the php_namespace option + // set in the protobuf. This should be used **only** by APIs + // who have already set the language_settings.php.package_name" field + // in gapic.yaml. API teams should use the protobuf php_namespace option + // where possible. + // + // Example of a YAML configuration:: + // + // publishing: + // library_settings: + // php_settings: + // library_package: Google\Cloud\PubSub\V1 + string library_package = 2; +} + +// Settings for Python client libraries. +message PythonSettings { + // Experimental features to be included during client library generation. + // These fields will be deprecated once the feature graduates and is enabled + // by default. + message ExperimentalFeatures { + // Enables generation of asynchronous REST clients if `rest` transport is + // enabled. By default, asynchronous REST clients will not be generated. + // This feature will be enabled by default 1 month after launching the + // feature in preview packages. + bool rest_async_io_enabled = 1; + + // Enables generation of protobuf code using new types that are more + // Pythonic which are included in `protobuf>=5.29.x`. This feature will be + // enabled by default 1 month after launching the feature in preview + // packages. + bool protobuf_pythonic_types_enabled = 2; + + // Disables generation of an unversioned Python package for this client + // library. This means that the module names will need to be versioned in + // import statements. For example `import google.cloud.library_v2` instead + // of `import google.cloud.library`. + bool unversioned_package_disabled = 3; + } + + // Some settings. + CommonLanguageSettings common = 1; + + // Experimental features to be included during client library generation. + ExperimentalFeatures experimental_features = 2; +} + +// Settings for Node client libraries. +message NodeSettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Settings for Dotnet client libraries. +message DotnetSettings { + // Some settings. + CommonLanguageSettings common = 1; + + // Map from original service names to renamed versions. + // This is used when the default generated types + // would cause a naming conflict. (Neither name is + // fully-qualified.) + // Example: Subscriber to SubscriberServiceApi. + map renamed_services = 2; + + // Map from full resource types to the effective short name + // for the resource. This is used when otherwise resource + // named from different services would cause naming collisions. + // Example entry: + // "datalabeling.googleapis.com/Dataset": "DataLabelingDataset" + map renamed_resources = 3; + + // List of full resource types to ignore during generation. + // This is typically used for API-specific Location resources, + // which should be handled by the generator as if they were actually + // the common Location resources. + // Example entry: "documentai.googleapis.com/Location" + repeated string ignored_resources = 4; + + // Namespaces which must be aliased in snippets due to + // a known (but non-generator-predictable) naming collision + repeated string forced_namespace_aliases = 5; + + // Method signatures (in the form "service.method(signature)") + // which are provided separately, so shouldn't be generated. + // Snippets *calling* these methods are still generated, however. + repeated string handwritten_signatures = 6; +} + +// Settings for Ruby client libraries. +message RubySettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Settings for Go client libraries. +message GoSettings { + // Some settings. + CommonLanguageSettings common = 1; + + // Map of service names to renamed services. Keys are the package relative + // service names and values are the name to be used for the service client + // and call options. + // + // Example: + // + // publishing: + // go_settings: + // renamed_services: + // Publisher: TopicAdmin + map renamed_services = 2; +} + +// Describes the generator configuration for a method. +message MethodSettings { + // Describes settings to use when generating API methods that use the + // long-running operation pattern. + // All default values below are from those used in the client library + // generators (e.g. + // [Java](https://github.com/googleapis/gapic-generator-java/blob/04c2faa191a9b5a10b92392fe8482279c4404803/src/main/java/com/google/api/generator/gapic/composer/common/RetrySettingsComposer.java)). + message LongRunning { + // Initial delay after which the first poll request will be made. + // Default value: 5 seconds. + google.protobuf.Duration initial_poll_delay = 1; + + // Multiplier to gradually increase delay between subsequent polls until it + // reaches max_poll_delay. + // Default value: 1.5. + float poll_delay_multiplier = 2; + + // Maximum time between two subsequent poll requests. + // Default value: 45 seconds. + google.protobuf.Duration max_poll_delay = 3; + + // Total polling timeout. + // Default value: 5 minutes. + google.protobuf.Duration total_poll_timeout = 4; + } + + // The fully qualified name of the method, for which the options below apply. + // This is used to find the method to apply the options. + // + // Example: + // + // publishing: + // method_settings: + // - selector: google.storage.control.v2.StorageControl.CreateFolder + // # method settings for CreateFolder... + string selector = 1; + + // Describes settings to use for long-running operations when generating + // API methods for RPCs. Complements RPCs that use the annotations in + // google/longrunning/operations.proto. + // + // Example of a YAML configuration:: + // + // publishing: + // method_settings: + // - selector: google.cloud.speech.v2.Speech.BatchRecognize + // long_running: + // initial_poll_delay: 60s # 1 minute + // poll_delay_multiplier: 1.5 + // max_poll_delay: 360s # 6 minutes + // total_poll_timeout: 54000s # 90 minutes + LongRunning long_running = 2; + + // List of top-level fields of the request message, that should be + // automatically populated by the client libraries based on their + // (google.api.field_info).format. Currently supported format: UUID4. + // + // Example of a YAML configuration: + // + // publishing: + // method_settings: + // - selector: google.example.v1.ExampleService.CreateExample + // auto_populated_fields: + // - request_id + repeated string auto_populated_fields = 3; + + // Batching configuration for an API method in client libraries. + // + // Example of a YAML configuration: + // + // publishing: + // method_settings: + // - selector: google.example.v1.ExampleService.BatchCreateExample + // batching: + // element_count_threshold: 1000 + // request_byte_threshold: 100000000 + // delay_threshold_millis: 10 + BatchingConfigProto batching = 4; +} + +// The organization for which the client libraries are being published. +// Affects the url where generated docs are published, etc. +enum ClientLibraryOrganization { + // Not useful. + CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED = 0; + + // Google Cloud Platform Org. + CLOUD = 1; + + // Ads (Advertising) Org. + ADS = 2; + + // Photos Org. + PHOTOS = 3; + + // Street View Org. + STREET_VIEW = 4; + + // Shopping Org. + SHOPPING = 5; + + // Geo Org. + GEO = 6; + + // Generative AI - https://developers.generativeai.google + GENERATIVE_AI = 7; +} + +// To where should client libraries be published? +enum ClientLibraryDestination { + // Client libraries will neither be generated nor published to package + // managers. + CLIENT_LIBRARY_DESTINATION_UNSPECIFIED = 0; + + // Generate the client library in a repo under github.com/googleapis, + // but don't publish it to package managers. + GITHUB = 10; + + // Publish the library to package managers like nuget.org and npmjs.com. + PACKAGE_MANAGER = 20; +} + +// This message is used to configure the generation of a subset of the RPCs in +// a service for client libraries. +// +// Note: This feature should not be used in most cases. +message SelectiveGapicGeneration { + // An allowlist of the fully qualified names of RPCs that should be included + // on public client surfaces. + repeated string methods = 1; + + // Setting this to true indicates to the client generators that methods + // that would be excluded from the generation should instead be generated + // in a way that indicates these methods should not be consumed by + // end users. How this is expressed is up to individual language + // implementations to decide. Some examples may be: added annotations, + // obfuscated identifiers, or other language idiomatic patterns. + bool generate_omitted_as_internal = 2; +} + +// `BatchingConfigProto` defines the batching configuration for an API method. +message BatchingConfigProto { + // The thresholds which trigger a batched request to be sent. + BatchingSettingsProto thresholds = 1; + + // The request and response fields used in batching. + BatchingDescriptorProto batch_descriptor = 2; +} + +// `BatchingSettingsProto` specifies a set of batching thresholds, each of +// which acts as a trigger to send a batch of messages as a request. At least +// one threshold must be positive nonzero. +message BatchingSettingsProto { + // The number of elements of a field collected into a batch which, if + // exceeded, causes the batch to be sent. + int32 element_count_threshold = 1; + + // The aggregated size of the batched field which, if exceeded, causes the + // batch to be sent. This size is computed by aggregating the sizes of the + // request field to be batched, not of the entire request message. + int64 request_byte_threshold = 2; + + // The duration after which a batch should be sent, starting from the addition + // of the first message to that batch. + google.protobuf.Duration delay_threshold = 3; + + // The maximum number of elements collected in a batch that could be accepted + // by server. + int32 element_count_limit = 4; + + // The maximum size of the request that could be accepted by server. + int32 request_byte_limit = 5; + + // The maximum number of elements allowed by flow control. + int32 flow_control_element_limit = 6; + + // The maximum size of data allowed by flow control. + int32 flow_control_byte_limit = 7; + + // The behavior to take when the flow control limit is exceeded. + FlowControlLimitExceededBehaviorProto flow_control_limit_exceeded_behavior = + 8; +} + +// The behavior to take when the flow control limit is exceeded. +enum FlowControlLimitExceededBehaviorProto { + // Default behavior, system-defined. + UNSET_BEHAVIOR = 0; + + // Stop operation, raise error. + THROW_EXCEPTION = 1; + + // Pause operation until limit clears. + BLOCK = 2; + + // Continue operation, disregard limit. + IGNORE = 3; +} + +// `BatchingDescriptorProto` specifies the fields of the request message to be +// used for batching, and, optionally, the fields of the response message to be +// used for demultiplexing. +message BatchingDescriptorProto { + // The repeated field in the request message to be aggregated by batching. + string batched_field = 1; + + // A list of the fields in the request message. Two requests will be batched + // together only if the values of every field specified in + // `request_discriminator_fields` is equal between the two requests. + repeated string discriminator_fields = 2; + + // Optional. When present, indicates the field in the response message to be + // used to demultiplex the response into multiple response messages, in + // correspondence with the multiple request messages originally batched + // together. + string subresponse_field = 3; +} diff --git a/venv/lib/python3.12/site-packages/google/api/client_pb2.py b/venv/lib/python3.12/site-packages/google/api/client_pb2.py new file mode 100644 index 0000000..7387ce9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/client_pb2.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/client.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/client.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 + +from google.api import launch_stage_pb2 as google_dot_api_dot_launch__stage__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x17google/api/client.proto\x12\ngoogle.api\x1a\x1dgoogle/api/launch_stage.proto\x1a google/protobuf/descriptor.proto\x1a\x1egoogle/protobuf/duration.proto"\xbe\x01\n\x16\x43ommonLanguageSettings\x12\x1e\n\x12reference_docs_uri\x18\x01 \x01(\tB\x02\x18\x01\x12:\n\x0c\x64\x65stinations\x18\x02 \x03(\x0e\x32$.google.api.ClientLibraryDestination\x12H\n\x1aselective_gapic_generation\x18\x03 \x01(\x0b\x32$.google.api.SelectiveGapicGeneration"\xfb\x03\n\x15\x43lientLibrarySettings\x12\x0f\n\x07version\x18\x01 \x01(\t\x12-\n\x0claunch_stage\x18\x02 \x01(\x0e\x32\x17.google.api.LaunchStage\x12\x1a\n\x12rest_numeric_enums\x18\x03 \x01(\x08\x12/\n\rjava_settings\x18\x15 \x01(\x0b\x32\x18.google.api.JavaSettings\x12-\n\x0c\x63pp_settings\x18\x16 \x01(\x0b\x32\x17.google.api.CppSettings\x12-\n\x0cphp_settings\x18\x17 \x01(\x0b\x32\x17.google.api.PhpSettings\x12\x33\n\x0fpython_settings\x18\x18 \x01(\x0b\x32\x1a.google.api.PythonSettings\x12/\n\rnode_settings\x18\x19 \x01(\x0b\x32\x18.google.api.NodeSettings\x12\x33\n\x0f\x64otnet_settings\x18\x1a \x01(\x0b\x32\x1a.google.api.DotnetSettings\x12/\n\rruby_settings\x18\x1b \x01(\x0b\x32\x18.google.api.RubySettings\x12+\n\x0bgo_settings\x18\x1c \x01(\x0b\x32\x16.google.api.GoSettings"\xa8\x03\n\nPublishing\x12\x33\n\x0fmethod_settings\x18\x02 \x03(\x0b\x32\x1a.google.api.MethodSettings\x12\x15\n\rnew_issue_uri\x18\x65 \x01(\t\x12\x19\n\x11\x64ocumentation_uri\x18\x66 \x01(\t\x12\x16\n\x0e\x61pi_short_name\x18g \x01(\t\x12\x14\n\x0cgithub_label\x18h \x01(\t\x12\x1e\n\x16\x63odeowner_github_teams\x18i \x03(\t\x12\x16\n\x0e\x64oc_tag_prefix\x18j \x01(\t\x12;\n\x0corganization\x18k \x01(\x0e\x32%.google.api.ClientLibraryOrganization\x12;\n\x10library_settings\x18m \x03(\x0b\x32!.google.api.ClientLibrarySettings\x12)\n!proto_reference_documentation_uri\x18n \x01(\t\x12(\n rest_reference_documentation_uri\x18o \x01(\t"\xe3\x01\n\x0cJavaSettings\x12\x17\n\x0flibrary_package\x18\x01 \x01(\t\x12L\n\x13service_class_names\x18\x02 \x03(\x0b\x32/.google.api.JavaSettings.ServiceClassNamesEntry\x12\x32\n\x06\x63ommon\x18\x03 \x01(\x0b\x32".google.api.CommonLanguageSettings\x1a\x38\n\x16ServiceClassNamesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"A\n\x0b\x43ppSettings\x12\x32\n\x06\x63ommon\x18\x01 \x01(\x0b\x32".google.api.CommonLanguageSettings"Z\n\x0bPhpSettings\x12\x32\n\x06\x63ommon\x18\x01 \x01(\x0b\x32".google.api.CommonLanguageSettings\x12\x17\n\x0flibrary_package\x18\x02 \x01(\t"\x9b\x02\n\x0ePythonSettings\x12\x32\n\x06\x63ommon\x18\x01 \x01(\x0b\x32".google.api.CommonLanguageSettings\x12N\n\x15\x65xperimental_features\x18\x02 \x01(\x0b\x32/.google.api.PythonSettings.ExperimentalFeatures\x1a\x84\x01\n\x14\x45xperimentalFeatures\x12\x1d\n\x15rest_async_io_enabled\x18\x01 \x01(\x08\x12\'\n\x1fprotobuf_pythonic_types_enabled\x18\x02 \x01(\x08\x12$\n\x1cunversioned_package_disabled\x18\x03 \x01(\x08"B\n\x0cNodeSettings\x12\x32\n\x06\x63ommon\x18\x01 \x01(\x0b\x32".google.api.CommonLanguageSettings"\xaa\x03\n\x0e\x44otnetSettings\x12\x32\n\x06\x63ommon\x18\x01 \x01(\x0b\x32".google.api.CommonLanguageSettings\x12I\n\x10renamed_services\x18\x02 \x03(\x0b\x32/.google.api.DotnetSettings.RenamedServicesEntry\x12K\n\x11renamed_resources\x18\x03 \x03(\x0b\x32\x30.google.api.DotnetSettings.RenamedResourcesEntry\x12\x19\n\x11ignored_resources\x18\x04 \x03(\t\x12 \n\x18\x66orced_namespace_aliases\x18\x05 \x03(\t\x12\x1e\n\x16handwritten_signatures\x18\x06 \x03(\t\x1a\x36\n\x14RenamedServicesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x37\n\x15RenamedResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"B\n\x0cRubySettings\x12\x32\n\x06\x63ommon\x18\x01 \x01(\x0b\x32".google.api.CommonLanguageSettings"\xbf\x01\n\nGoSettings\x12\x32\n\x06\x63ommon\x18\x01 \x01(\x0b\x32".google.api.CommonLanguageSettings\x12\x45\n\x10renamed_services\x18\x02 \x03(\x0b\x32+.google.api.GoSettings.RenamedServicesEntry\x1a\x36\n\x14RenamedServicesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"\x82\x03\n\x0eMethodSettings\x12\x10\n\x08selector\x18\x01 \x01(\t\x12<\n\x0clong_running\x18\x02 \x01(\x0b\x32&.google.api.MethodSettings.LongRunning\x12\x1d\n\x15\x61uto_populated_fields\x18\x03 \x03(\t\x12\x31\n\x08\x62\x61tching\x18\x04 \x01(\x0b\x32\x1f.google.api.BatchingConfigProto\x1a\xcd\x01\n\x0bLongRunning\x12\x35\n\x12initial_poll_delay\x18\x01 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x1d\n\x15poll_delay_multiplier\x18\x02 \x01(\x02\x12\x31\n\x0emax_poll_delay\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x35\n\x12total_poll_timeout\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration"Q\n\x18SelectiveGapicGeneration\x12\x0f\n\x07methods\x18\x01 \x03(\t\x12$\n\x1cgenerate_omitted_as_internal\x18\x02 \x01(\x08"\x8b\x01\n\x13\x42\x61tchingConfigProto\x12\x35\n\nthresholds\x18\x01 \x01(\x0b\x32!.google.api.BatchingSettingsProto\x12=\n\x10\x62\x61tch_descriptor\x18\x02 \x01(\x0b\x32#.google.api.BatchingDescriptorProto"\xeb\x02\n\x15\x42\x61tchingSettingsProto\x12\x1f\n\x17\x65lement_count_threshold\x18\x01 \x01(\x05\x12\x1e\n\x16request_byte_threshold\x18\x02 \x01(\x03\x12\x32\n\x0f\x64\x65lay_threshold\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x1b\n\x13\x65lement_count_limit\x18\x04 \x01(\x05\x12\x1a\n\x12request_byte_limit\x18\x05 \x01(\x05\x12"\n\x1a\x66low_control_element_limit\x18\x06 \x01(\x05\x12\x1f\n\x17\x66low_control_byte_limit\x18\x07 \x01(\x05\x12_\n$flow_control_limit_exceeded_behavior\x18\x08 \x01(\x0e\x32\x31.google.api.FlowControlLimitExceededBehaviorProto"i\n\x17\x42\x61tchingDescriptorProto\x12\x15\n\rbatched_field\x18\x01 \x01(\t\x12\x1c\n\x14\x64iscriminator_fields\x18\x02 \x03(\t\x12\x19\n\x11subresponse_field\x18\x03 \x01(\t*\xa3\x01\n\x19\x43lientLibraryOrganization\x12+\n\'CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED\x10\x00\x12\t\n\x05\x43LOUD\x10\x01\x12\x07\n\x03\x41\x44S\x10\x02\x12\n\n\x06PHOTOS\x10\x03\x12\x0f\n\x0bSTREET_VIEW\x10\x04\x12\x0c\n\x08SHOPPING\x10\x05\x12\x07\n\x03GEO\x10\x06\x12\x11\n\rGENERATIVE_AI\x10\x07*g\n\x18\x43lientLibraryDestination\x12*\n&CLIENT_LIBRARY_DESTINATION_UNSPECIFIED\x10\x00\x12\n\n\x06GITHUB\x10\n\x12\x13\n\x0fPACKAGE_MANAGER\x10\x14*g\n%FlowControlLimitExceededBehaviorProto\x12\x12\n\x0eUNSET_BEHAVIOR\x10\x00\x12\x13\n\x0fTHROW_EXCEPTION\x10\x01\x12\t\n\x05\x42LOCK\x10\x02\x12\n\n\x06IGNORE\x10\x03:9\n\x10method_signature\x12\x1e.google.protobuf.MethodOptions\x18\x9b\x08 \x03(\t:6\n\x0c\x64\x65\x66\x61ult_host\x12\x1f.google.protobuf.ServiceOptions\x18\x99\x08 \x01(\t:6\n\x0coauth_scopes\x12\x1f.google.protobuf.ServiceOptions\x18\x9a\x08 \x01(\t:8\n\x0b\x61pi_version\x12\x1f.google.protobuf.ServiceOptions\x18\xc1\xba\xab\xfa\x01 \x01(\tBi\n\x0e\x63om.google.apiB\x0b\x43lientProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.client_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\013ClientProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI" + _globals["_COMMONLANGUAGESETTINGS"].fields_by_name[ + "reference_docs_uri" + ]._loaded_options = None + _globals["_COMMONLANGUAGESETTINGS"].fields_by_name[ + "reference_docs_uri" + ]._serialized_options = b"\030\001" + _globals["_JAVASETTINGS_SERVICECLASSNAMESENTRY"]._loaded_options = None + _globals["_JAVASETTINGS_SERVICECLASSNAMESENTRY"]._serialized_options = b"8\001" + _globals["_DOTNETSETTINGS_RENAMEDSERVICESENTRY"]._loaded_options = None + _globals["_DOTNETSETTINGS_RENAMEDSERVICESENTRY"]._serialized_options = b"8\001" + _globals["_DOTNETSETTINGS_RENAMEDRESOURCESENTRY"]._loaded_options = None + _globals["_DOTNETSETTINGS_RENAMEDRESOURCESENTRY"]._serialized_options = b"8\001" + _globals["_GOSETTINGS_RENAMEDSERVICESENTRY"]._loaded_options = None + _globals["_GOSETTINGS_RENAMEDSERVICESENTRY"]._serialized_options = b"8\001" + _globals["_CLIENTLIBRARYORGANIZATION"]._serialized_start = 3788 + _globals["_CLIENTLIBRARYORGANIZATION"]._serialized_end = 3951 + _globals["_CLIENTLIBRARYDESTINATION"]._serialized_start = 3953 + _globals["_CLIENTLIBRARYDESTINATION"]._serialized_end = 4056 + _globals["_FLOWCONTROLLIMITEXCEEDEDBEHAVIORPROTO"]._serialized_start = 4058 + _globals["_FLOWCONTROLLIMITEXCEEDEDBEHAVIORPROTO"]._serialized_end = 4161 + _globals["_COMMONLANGUAGESETTINGS"]._serialized_start = 137 + _globals["_COMMONLANGUAGESETTINGS"]._serialized_end = 327 + _globals["_CLIENTLIBRARYSETTINGS"]._serialized_start = 330 + _globals["_CLIENTLIBRARYSETTINGS"]._serialized_end = 837 + _globals["_PUBLISHING"]._serialized_start = 840 + _globals["_PUBLISHING"]._serialized_end = 1264 + _globals["_JAVASETTINGS"]._serialized_start = 1267 + _globals["_JAVASETTINGS"]._serialized_end = 1494 + _globals["_JAVASETTINGS_SERVICECLASSNAMESENTRY"]._serialized_start = 1438 + _globals["_JAVASETTINGS_SERVICECLASSNAMESENTRY"]._serialized_end = 1494 + _globals["_CPPSETTINGS"]._serialized_start = 1496 + _globals["_CPPSETTINGS"]._serialized_end = 1561 + _globals["_PHPSETTINGS"]._serialized_start = 1563 + _globals["_PHPSETTINGS"]._serialized_end = 1653 + _globals["_PYTHONSETTINGS"]._serialized_start = 1656 + _globals["_PYTHONSETTINGS"]._serialized_end = 1939 + _globals["_PYTHONSETTINGS_EXPERIMENTALFEATURES"]._serialized_start = 1807 + _globals["_PYTHONSETTINGS_EXPERIMENTALFEATURES"]._serialized_end = 1939 + _globals["_NODESETTINGS"]._serialized_start = 1941 + _globals["_NODESETTINGS"]._serialized_end = 2007 + _globals["_DOTNETSETTINGS"]._serialized_start = 2010 + _globals["_DOTNETSETTINGS"]._serialized_end = 2436 + _globals["_DOTNETSETTINGS_RENAMEDSERVICESENTRY"]._serialized_start = 2325 + _globals["_DOTNETSETTINGS_RENAMEDSERVICESENTRY"]._serialized_end = 2379 + _globals["_DOTNETSETTINGS_RENAMEDRESOURCESENTRY"]._serialized_start = 2381 + _globals["_DOTNETSETTINGS_RENAMEDRESOURCESENTRY"]._serialized_end = 2436 + _globals["_RUBYSETTINGS"]._serialized_start = 2438 + _globals["_RUBYSETTINGS"]._serialized_end = 2504 + _globals["_GOSETTINGS"]._serialized_start = 2507 + _globals["_GOSETTINGS"]._serialized_end = 2698 + _globals["_GOSETTINGS_RENAMEDSERVICESENTRY"]._serialized_start = 2325 + _globals["_GOSETTINGS_RENAMEDSERVICESENTRY"]._serialized_end = 2379 + _globals["_METHODSETTINGS"]._serialized_start = 2701 + _globals["_METHODSETTINGS"]._serialized_end = 3087 + _globals["_METHODSETTINGS_LONGRUNNING"]._serialized_start = 2882 + _globals["_METHODSETTINGS_LONGRUNNING"]._serialized_end = 3087 + _globals["_SELECTIVEGAPICGENERATION"]._serialized_start = 3089 + _globals["_SELECTIVEGAPICGENERATION"]._serialized_end = 3170 + _globals["_BATCHINGCONFIGPROTO"]._serialized_start = 3173 + _globals["_BATCHINGCONFIGPROTO"]._serialized_end = 3312 + _globals["_BATCHINGSETTINGSPROTO"]._serialized_start = 3315 + _globals["_BATCHINGSETTINGSPROTO"]._serialized_end = 3678 + _globals["_BATCHINGDESCRIPTORPROTO"]._serialized_start = 3680 + _globals["_BATCHINGDESCRIPTORPROTO"]._serialized_end = 3785 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/client_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/client_pb2.pyi new file mode 100644 index 0000000..c25654f --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/client_pb2.pyi @@ -0,0 +1,498 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pb2 as _descriptor_pb2 +from google.protobuf import duration_pb2 as _duration_pb2 +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +from google.api import launch_stage_pb2 as _launch_stage_pb2 + +DESCRIPTOR: _descriptor.FileDescriptor + +class ClientLibraryOrganization(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED: _ClassVar[ClientLibraryOrganization] + CLOUD: _ClassVar[ClientLibraryOrganization] + ADS: _ClassVar[ClientLibraryOrganization] + PHOTOS: _ClassVar[ClientLibraryOrganization] + STREET_VIEW: _ClassVar[ClientLibraryOrganization] + SHOPPING: _ClassVar[ClientLibraryOrganization] + GEO: _ClassVar[ClientLibraryOrganization] + GENERATIVE_AI: _ClassVar[ClientLibraryOrganization] + +class ClientLibraryDestination(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + CLIENT_LIBRARY_DESTINATION_UNSPECIFIED: _ClassVar[ClientLibraryDestination] + GITHUB: _ClassVar[ClientLibraryDestination] + PACKAGE_MANAGER: _ClassVar[ClientLibraryDestination] + +class FlowControlLimitExceededBehaviorProto( + int, metaclass=_enum_type_wrapper.EnumTypeWrapper +): + __slots__ = () + UNSET_BEHAVIOR: _ClassVar[FlowControlLimitExceededBehaviorProto] + THROW_EXCEPTION: _ClassVar[FlowControlLimitExceededBehaviorProto] + BLOCK: _ClassVar[FlowControlLimitExceededBehaviorProto] + IGNORE: _ClassVar[FlowControlLimitExceededBehaviorProto] + +CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED: ClientLibraryOrganization +CLOUD: ClientLibraryOrganization +ADS: ClientLibraryOrganization +PHOTOS: ClientLibraryOrganization +STREET_VIEW: ClientLibraryOrganization +SHOPPING: ClientLibraryOrganization +GEO: ClientLibraryOrganization +GENERATIVE_AI: ClientLibraryOrganization +CLIENT_LIBRARY_DESTINATION_UNSPECIFIED: ClientLibraryDestination +GITHUB: ClientLibraryDestination +PACKAGE_MANAGER: ClientLibraryDestination +UNSET_BEHAVIOR: FlowControlLimitExceededBehaviorProto +THROW_EXCEPTION: FlowControlLimitExceededBehaviorProto +BLOCK: FlowControlLimitExceededBehaviorProto +IGNORE: FlowControlLimitExceededBehaviorProto +METHOD_SIGNATURE_FIELD_NUMBER: _ClassVar[int] +method_signature: _descriptor.FieldDescriptor +DEFAULT_HOST_FIELD_NUMBER: _ClassVar[int] +default_host: _descriptor.FieldDescriptor +OAUTH_SCOPES_FIELD_NUMBER: _ClassVar[int] +oauth_scopes: _descriptor.FieldDescriptor +API_VERSION_FIELD_NUMBER: _ClassVar[int] +api_version: _descriptor.FieldDescriptor + +class CommonLanguageSettings(_message.Message): + __slots__ = ("reference_docs_uri", "destinations", "selective_gapic_generation") + REFERENCE_DOCS_URI_FIELD_NUMBER: _ClassVar[int] + DESTINATIONS_FIELD_NUMBER: _ClassVar[int] + SELECTIVE_GAPIC_GENERATION_FIELD_NUMBER: _ClassVar[int] + reference_docs_uri: str + destinations: _containers.RepeatedScalarFieldContainer[ClientLibraryDestination] + selective_gapic_generation: SelectiveGapicGeneration + def __init__( + self, + reference_docs_uri: _Optional[str] = ..., + destinations: _Optional[_Iterable[_Union[ClientLibraryDestination, str]]] = ..., + selective_gapic_generation: _Optional[ + _Union[SelectiveGapicGeneration, _Mapping] + ] = ..., + ) -> None: ... + +class ClientLibrarySettings(_message.Message): + __slots__ = ( + "version", + "launch_stage", + "rest_numeric_enums", + "java_settings", + "cpp_settings", + "php_settings", + "python_settings", + "node_settings", + "dotnet_settings", + "ruby_settings", + "go_settings", + ) + VERSION_FIELD_NUMBER: _ClassVar[int] + LAUNCH_STAGE_FIELD_NUMBER: _ClassVar[int] + REST_NUMERIC_ENUMS_FIELD_NUMBER: _ClassVar[int] + JAVA_SETTINGS_FIELD_NUMBER: _ClassVar[int] + CPP_SETTINGS_FIELD_NUMBER: _ClassVar[int] + PHP_SETTINGS_FIELD_NUMBER: _ClassVar[int] + PYTHON_SETTINGS_FIELD_NUMBER: _ClassVar[int] + NODE_SETTINGS_FIELD_NUMBER: _ClassVar[int] + DOTNET_SETTINGS_FIELD_NUMBER: _ClassVar[int] + RUBY_SETTINGS_FIELD_NUMBER: _ClassVar[int] + GO_SETTINGS_FIELD_NUMBER: _ClassVar[int] + version: str + launch_stage: _launch_stage_pb2.LaunchStage + rest_numeric_enums: bool + java_settings: JavaSettings + cpp_settings: CppSettings + php_settings: PhpSettings + python_settings: PythonSettings + node_settings: NodeSettings + dotnet_settings: DotnetSettings + ruby_settings: RubySettings + go_settings: GoSettings + def __init__( + self, + version: _Optional[str] = ..., + launch_stage: _Optional[_Union[_launch_stage_pb2.LaunchStage, str]] = ..., + rest_numeric_enums: _Optional[bool] = ..., + java_settings: _Optional[_Union[JavaSettings, _Mapping]] = ..., + cpp_settings: _Optional[_Union[CppSettings, _Mapping]] = ..., + php_settings: _Optional[_Union[PhpSettings, _Mapping]] = ..., + python_settings: _Optional[_Union[PythonSettings, _Mapping]] = ..., + node_settings: _Optional[_Union[NodeSettings, _Mapping]] = ..., + dotnet_settings: _Optional[_Union[DotnetSettings, _Mapping]] = ..., + ruby_settings: _Optional[_Union[RubySettings, _Mapping]] = ..., + go_settings: _Optional[_Union[GoSettings, _Mapping]] = ..., + ) -> None: ... + +class Publishing(_message.Message): + __slots__ = ( + "method_settings", + "new_issue_uri", + "documentation_uri", + "api_short_name", + "github_label", + "codeowner_github_teams", + "doc_tag_prefix", + "organization", + "library_settings", + "proto_reference_documentation_uri", + "rest_reference_documentation_uri", + ) + METHOD_SETTINGS_FIELD_NUMBER: _ClassVar[int] + NEW_ISSUE_URI_FIELD_NUMBER: _ClassVar[int] + DOCUMENTATION_URI_FIELD_NUMBER: _ClassVar[int] + API_SHORT_NAME_FIELD_NUMBER: _ClassVar[int] + GITHUB_LABEL_FIELD_NUMBER: _ClassVar[int] + CODEOWNER_GITHUB_TEAMS_FIELD_NUMBER: _ClassVar[int] + DOC_TAG_PREFIX_FIELD_NUMBER: _ClassVar[int] + ORGANIZATION_FIELD_NUMBER: _ClassVar[int] + LIBRARY_SETTINGS_FIELD_NUMBER: _ClassVar[int] + PROTO_REFERENCE_DOCUMENTATION_URI_FIELD_NUMBER: _ClassVar[int] + REST_REFERENCE_DOCUMENTATION_URI_FIELD_NUMBER: _ClassVar[int] + method_settings: _containers.RepeatedCompositeFieldContainer[MethodSettings] + new_issue_uri: str + documentation_uri: str + api_short_name: str + github_label: str + codeowner_github_teams: _containers.RepeatedScalarFieldContainer[str] + doc_tag_prefix: str + organization: ClientLibraryOrganization + library_settings: _containers.RepeatedCompositeFieldContainer[ClientLibrarySettings] + proto_reference_documentation_uri: str + rest_reference_documentation_uri: str + def __init__( + self, + method_settings: _Optional[_Iterable[_Union[MethodSettings, _Mapping]]] = ..., + new_issue_uri: _Optional[str] = ..., + documentation_uri: _Optional[str] = ..., + api_short_name: _Optional[str] = ..., + github_label: _Optional[str] = ..., + codeowner_github_teams: _Optional[_Iterable[str]] = ..., + doc_tag_prefix: _Optional[str] = ..., + organization: _Optional[_Union[ClientLibraryOrganization, str]] = ..., + library_settings: _Optional[ + _Iterable[_Union[ClientLibrarySettings, _Mapping]] + ] = ..., + proto_reference_documentation_uri: _Optional[str] = ..., + rest_reference_documentation_uri: _Optional[str] = ..., + ) -> None: ... + +class JavaSettings(_message.Message): + __slots__ = ("library_package", "service_class_names", "common") + class ServiceClassNamesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + LIBRARY_PACKAGE_FIELD_NUMBER: _ClassVar[int] + SERVICE_CLASS_NAMES_FIELD_NUMBER: _ClassVar[int] + COMMON_FIELD_NUMBER: _ClassVar[int] + library_package: str + service_class_names: _containers.ScalarMap[str, str] + common: CommonLanguageSettings + def __init__( + self, + library_package: _Optional[str] = ..., + service_class_names: _Optional[_Mapping[str, str]] = ..., + common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ..., + ) -> None: ... + +class CppSettings(_message.Message): + __slots__ = ("common",) + COMMON_FIELD_NUMBER: _ClassVar[int] + common: CommonLanguageSettings + def __init__( + self, common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ... + ) -> None: ... + +class PhpSettings(_message.Message): + __slots__ = ("common", "library_package") + COMMON_FIELD_NUMBER: _ClassVar[int] + LIBRARY_PACKAGE_FIELD_NUMBER: _ClassVar[int] + common: CommonLanguageSettings + library_package: str + def __init__( + self, + common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ..., + library_package: _Optional[str] = ..., + ) -> None: ... + +class PythonSettings(_message.Message): + __slots__ = ("common", "experimental_features") + class ExperimentalFeatures(_message.Message): + __slots__ = ( + "rest_async_io_enabled", + "protobuf_pythonic_types_enabled", + "unversioned_package_disabled", + ) + REST_ASYNC_IO_ENABLED_FIELD_NUMBER: _ClassVar[int] + PROTOBUF_PYTHONIC_TYPES_ENABLED_FIELD_NUMBER: _ClassVar[int] + UNVERSIONED_PACKAGE_DISABLED_FIELD_NUMBER: _ClassVar[int] + rest_async_io_enabled: bool + protobuf_pythonic_types_enabled: bool + unversioned_package_disabled: bool + def __init__( + self, + rest_async_io_enabled: _Optional[bool] = ..., + protobuf_pythonic_types_enabled: _Optional[bool] = ..., + unversioned_package_disabled: _Optional[bool] = ..., + ) -> None: ... + + COMMON_FIELD_NUMBER: _ClassVar[int] + EXPERIMENTAL_FEATURES_FIELD_NUMBER: _ClassVar[int] + common: CommonLanguageSettings + experimental_features: PythonSettings.ExperimentalFeatures + def __init__( + self, + common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ..., + experimental_features: _Optional[ + _Union[PythonSettings.ExperimentalFeatures, _Mapping] + ] = ..., + ) -> None: ... + +class NodeSettings(_message.Message): + __slots__ = ("common",) + COMMON_FIELD_NUMBER: _ClassVar[int] + common: CommonLanguageSettings + def __init__( + self, common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ... + ) -> None: ... + +class DotnetSettings(_message.Message): + __slots__ = ( + "common", + "renamed_services", + "renamed_resources", + "ignored_resources", + "forced_namespace_aliases", + "handwritten_signatures", + ) + class RenamedServicesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + class RenamedResourcesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + COMMON_FIELD_NUMBER: _ClassVar[int] + RENAMED_SERVICES_FIELD_NUMBER: _ClassVar[int] + RENAMED_RESOURCES_FIELD_NUMBER: _ClassVar[int] + IGNORED_RESOURCES_FIELD_NUMBER: _ClassVar[int] + FORCED_NAMESPACE_ALIASES_FIELD_NUMBER: _ClassVar[int] + HANDWRITTEN_SIGNATURES_FIELD_NUMBER: _ClassVar[int] + common: CommonLanguageSettings + renamed_services: _containers.ScalarMap[str, str] + renamed_resources: _containers.ScalarMap[str, str] + ignored_resources: _containers.RepeatedScalarFieldContainer[str] + forced_namespace_aliases: _containers.RepeatedScalarFieldContainer[str] + handwritten_signatures: _containers.RepeatedScalarFieldContainer[str] + def __init__( + self, + common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ..., + renamed_services: _Optional[_Mapping[str, str]] = ..., + renamed_resources: _Optional[_Mapping[str, str]] = ..., + ignored_resources: _Optional[_Iterable[str]] = ..., + forced_namespace_aliases: _Optional[_Iterable[str]] = ..., + handwritten_signatures: _Optional[_Iterable[str]] = ..., + ) -> None: ... + +class RubySettings(_message.Message): + __slots__ = ("common",) + COMMON_FIELD_NUMBER: _ClassVar[int] + common: CommonLanguageSettings + def __init__( + self, common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ... + ) -> None: ... + +class GoSettings(_message.Message): + __slots__ = ("common", "renamed_services") + class RenamedServicesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + COMMON_FIELD_NUMBER: _ClassVar[int] + RENAMED_SERVICES_FIELD_NUMBER: _ClassVar[int] + common: CommonLanguageSettings + renamed_services: _containers.ScalarMap[str, str] + def __init__( + self, + common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ..., + renamed_services: _Optional[_Mapping[str, str]] = ..., + ) -> None: ... + +class MethodSettings(_message.Message): + __slots__ = ("selector", "long_running", "auto_populated_fields", "batching") + class LongRunning(_message.Message): + __slots__ = ( + "initial_poll_delay", + "poll_delay_multiplier", + "max_poll_delay", + "total_poll_timeout", + ) + INITIAL_POLL_DELAY_FIELD_NUMBER: _ClassVar[int] + POLL_DELAY_MULTIPLIER_FIELD_NUMBER: _ClassVar[int] + MAX_POLL_DELAY_FIELD_NUMBER: _ClassVar[int] + TOTAL_POLL_TIMEOUT_FIELD_NUMBER: _ClassVar[int] + initial_poll_delay: _duration_pb2.Duration + poll_delay_multiplier: float + max_poll_delay: _duration_pb2.Duration + total_poll_timeout: _duration_pb2.Duration + def __init__( + self, + initial_poll_delay: _Optional[ + _Union[datetime.timedelta, _duration_pb2.Duration, _Mapping] + ] = ..., + poll_delay_multiplier: _Optional[float] = ..., + max_poll_delay: _Optional[ + _Union[datetime.timedelta, _duration_pb2.Duration, _Mapping] + ] = ..., + total_poll_timeout: _Optional[ + _Union[datetime.timedelta, _duration_pb2.Duration, _Mapping] + ] = ..., + ) -> None: ... + + SELECTOR_FIELD_NUMBER: _ClassVar[int] + LONG_RUNNING_FIELD_NUMBER: _ClassVar[int] + AUTO_POPULATED_FIELDS_FIELD_NUMBER: _ClassVar[int] + BATCHING_FIELD_NUMBER: _ClassVar[int] + selector: str + long_running: MethodSettings.LongRunning + auto_populated_fields: _containers.RepeatedScalarFieldContainer[str] + batching: BatchingConfigProto + def __init__( + self, + selector: _Optional[str] = ..., + long_running: _Optional[_Union[MethodSettings.LongRunning, _Mapping]] = ..., + auto_populated_fields: _Optional[_Iterable[str]] = ..., + batching: _Optional[_Union[BatchingConfigProto, _Mapping]] = ..., + ) -> None: ... + +class SelectiveGapicGeneration(_message.Message): + __slots__ = ("methods", "generate_omitted_as_internal") + METHODS_FIELD_NUMBER: _ClassVar[int] + GENERATE_OMITTED_AS_INTERNAL_FIELD_NUMBER: _ClassVar[int] + methods: _containers.RepeatedScalarFieldContainer[str] + generate_omitted_as_internal: bool + def __init__( + self, + methods: _Optional[_Iterable[str]] = ..., + generate_omitted_as_internal: _Optional[bool] = ..., + ) -> None: ... + +class BatchingConfigProto(_message.Message): + __slots__ = ("thresholds", "batch_descriptor") + THRESHOLDS_FIELD_NUMBER: _ClassVar[int] + BATCH_DESCRIPTOR_FIELD_NUMBER: _ClassVar[int] + thresholds: BatchingSettingsProto + batch_descriptor: BatchingDescriptorProto + def __init__( + self, + thresholds: _Optional[_Union[BatchingSettingsProto, _Mapping]] = ..., + batch_descriptor: _Optional[_Union[BatchingDescriptorProto, _Mapping]] = ..., + ) -> None: ... + +class BatchingSettingsProto(_message.Message): + __slots__ = ( + "element_count_threshold", + "request_byte_threshold", + "delay_threshold", + "element_count_limit", + "request_byte_limit", + "flow_control_element_limit", + "flow_control_byte_limit", + "flow_control_limit_exceeded_behavior", + ) + ELEMENT_COUNT_THRESHOLD_FIELD_NUMBER: _ClassVar[int] + REQUEST_BYTE_THRESHOLD_FIELD_NUMBER: _ClassVar[int] + DELAY_THRESHOLD_FIELD_NUMBER: _ClassVar[int] + ELEMENT_COUNT_LIMIT_FIELD_NUMBER: _ClassVar[int] + REQUEST_BYTE_LIMIT_FIELD_NUMBER: _ClassVar[int] + FLOW_CONTROL_ELEMENT_LIMIT_FIELD_NUMBER: _ClassVar[int] + FLOW_CONTROL_BYTE_LIMIT_FIELD_NUMBER: _ClassVar[int] + FLOW_CONTROL_LIMIT_EXCEEDED_BEHAVIOR_FIELD_NUMBER: _ClassVar[int] + element_count_threshold: int + request_byte_threshold: int + delay_threshold: _duration_pb2.Duration + element_count_limit: int + request_byte_limit: int + flow_control_element_limit: int + flow_control_byte_limit: int + flow_control_limit_exceeded_behavior: FlowControlLimitExceededBehaviorProto + def __init__( + self, + element_count_threshold: _Optional[int] = ..., + request_byte_threshold: _Optional[int] = ..., + delay_threshold: _Optional[ + _Union[datetime.timedelta, _duration_pb2.Duration, _Mapping] + ] = ..., + element_count_limit: _Optional[int] = ..., + request_byte_limit: _Optional[int] = ..., + flow_control_element_limit: _Optional[int] = ..., + flow_control_byte_limit: _Optional[int] = ..., + flow_control_limit_exceeded_behavior: _Optional[ + _Union[FlowControlLimitExceededBehaviorProto, str] + ] = ..., + ) -> None: ... + +class BatchingDescriptorProto(_message.Message): + __slots__ = ("batched_field", "discriminator_fields", "subresponse_field") + BATCHED_FIELD_FIELD_NUMBER: _ClassVar[int] + DISCRIMINATOR_FIELDS_FIELD_NUMBER: _ClassVar[int] + SUBRESPONSE_FIELD_FIELD_NUMBER: _ClassVar[int] + batched_field: str + discriminator_fields: _containers.RepeatedScalarFieldContainer[str] + subresponse_field: str + def __init__( + self, + batched_field: _Optional[str] = ..., + discriminator_fields: _Optional[_Iterable[str]] = ..., + subresponse_field: _Optional[str] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/config_change.proto b/venv/lib/python3.12/site-packages/google/api/config_change.proto new file mode 100644 index 0000000..b6792b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/config_change.proto @@ -0,0 +1,84 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/configchange;configchange"; +option java_multiple_files = true; +option java_outer_classname = "ConfigChangeProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Output generated from semantically comparing two versions of a service +// configuration. +// +// Includes detailed information about a field that have changed with +// applicable advice about potential consequences for the change, such as +// backwards-incompatibility. +message ConfigChange { + // Object hierarchy path to the change, with levels separated by a '.' + // character. For repeated fields, an applicable unique identifier field is + // used for the index (usually selector, name, or id). For maps, the term + // 'key' is used. If the field has no unique identifier, the numeric index + // is used. + // Examples: + // - visibility.rules[selector=="google.LibraryService.ListBooks"].restriction + // - quota.metric_rules[selector=="google"].metric_costs[key=="reads"].value + // - logging.producer_destinations[0] + string element = 1; + + // Value of the changed object in the old Service configuration, + // in JSON format. This field will not be populated if ChangeType == ADDED. + string old_value = 2; + + // Value of the changed object in the new Service configuration, + // in JSON format. This field will not be populated if ChangeType == REMOVED. + string new_value = 3; + + // The type for this change, either ADDED, REMOVED, or MODIFIED. + ChangeType change_type = 4; + + // Collection of advice provided for this change, useful for determining the + // possible impact of this change. + repeated Advice advices = 5; +} + +// Generated advice about this change, used for providing more +// information about how a change will affect the existing service. +message Advice { + // Useful description for why this advice was applied and what actions should + // be taken to mitigate any implied risks. + string description = 2; +} + +// Classifies set of possible modifications to an object in the service +// configuration. +enum ChangeType { + // No value was provided. + CHANGE_TYPE_UNSPECIFIED = 0; + + // The changed object exists in the 'new' service configuration, but not + // in the 'old' service configuration. + ADDED = 1; + + // The changed object exists in the 'old' service configuration, but not + // in the 'new' service configuration. + REMOVED = 2; + + // The changed object exists in both service configurations, but its value + // is different. + MODIFIED = 3; +} diff --git a/venv/lib/python3.12/site-packages/google/api/config_change_pb2.py b/venv/lib/python3.12/site-packages/google/api/config_change_pb2.py new file mode 100644 index 0000000..56754c8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/config_change_pb2.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/config_change.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/config_change.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1egoogle/api/config_change.proto\x12\ngoogle.api"\x97\x01\n\x0c\x43onfigChange\x12\x0f\n\x07\x65lement\x18\x01 \x01(\t\x12\x11\n\told_value\x18\x02 \x01(\t\x12\x11\n\tnew_value\x18\x03 \x01(\t\x12+\n\x0b\x63hange_type\x18\x04 \x01(\x0e\x32\x16.google.api.ChangeType\x12#\n\x07\x61\x64vices\x18\x05 \x03(\x0b\x32\x12.google.api.Advice"\x1d\n\x06\x41\x64vice\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t*O\n\nChangeType\x12\x1b\n\x17\x43HANGE_TYPE_UNSPECIFIED\x10\x00\x12\t\n\x05\x41\x44\x44\x45\x44\x10\x01\x12\x0b\n\x07REMOVED\x10\x02\x12\x0c\n\x08MODIFIED\x10\x03\x42q\n\x0e\x63om.google.apiB\x11\x43onfigChangeProtoP\x01ZCgoogle.golang.org/genproto/googleapis/api/configchange;configchange\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.api.config_change_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\021ConfigChangeProtoP\001ZCgoogle.golang.org/genproto/googleapis/api/configchange;configchange\242\002\004GAPI" + _globals["_CHANGETYPE"]._serialized_start = 231 + _globals["_CHANGETYPE"]._serialized_end = 310 + _globals["_CONFIGCHANGE"]._serialized_start = 47 + _globals["_CONFIGCHANGE"]._serialized_end = 198 + _globals["_ADVICE"]._serialized_start = 200 + _globals["_ADVICE"]._serialized_end = 229 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/config_change_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/config_change_pb2.pyi new file mode 100644 index 0000000..3c7f2c3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/config_change_pb2.pyi @@ -0,0 +1,65 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor + +class ChangeType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + CHANGE_TYPE_UNSPECIFIED: _ClassVar[ChangeType] + ADDED: _ClassVar[ChangeType] + REMOVED: _ClassVar[ChangeType] + MODIFIED: _ClassVar[ChangeType] + +CHANGE_TYPE_UNSPECIFIED: ChangeType +ADDED: ChangeType +REMOVED: ChangeType +MODIFIED: ChangeType + +class ConfigChange(_message.Message): + __slots__ = ("element", "old_value", "new_value", "change_type", "advices") + ELEMENT_FIELD_NUMBER: _ClassVar[int] + OLD_VALUE_FIELD_NUMBER: _ClassVar[int] + NEW_VALUE_FIELD_NUMBER: _ClassVar[int] + CHANGE_TYPE_FIELD_NUMBER: _ClassVar[int] + ADVICES_FIELD_NUMBER: _ClassVar[int] + element: str + old_value: str + new_value: str + change_type: ChangeType + advices: _containers.RepeatedCompositeFieldContainer[Advice] + def __init__( + self, + element: _Optional[str] = ..., + old_value: _Optional[str] = ..., + new_value: _Optional[str] = ..., + change_type: _Optional[_Union[ChangeType, str]] = ..., + advices: _Optional[_Iterable[_Union[Advice, _Mapping]]] = ..., + ) -> None: ... + +class Advice(_message.Message): + __slots__ = ("description",) + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + description: str + def __init__(self, description: _Optional[str] = ...) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/consumer.proto b/venv/lib/python3.12/site-packages/google/api/consumer.proto new file mode 100644 index 0000000..3036049 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/consumer.proto @@ -0,0 +1,82 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "ConsumerProto"; +option java_package = "com.google.api"; + +// A descriptor for defining project properties for a service. One service may +// have many consumer projects, and the service may want to behave differently +// depending on some properties on the project. For example, a project may be +// associated with a school, or a business, or a government agency, a business +// type property on the project may affect how a service responds to the client. +// This descriptor defines which properties are allowed to be set on a project. +// +// Example: +// +// project_properties: +// properties: +// - name: NO_WATERMARK +// type: BOOL +// description: Allows usage of the API without watermarks. +// - name: EXTENDED_TILE_CACHE_PERIOD +// type: INT64 +message ProjectProperties { + // List of per consumer project-specific properties. + repeated Property properties = 1; +} + +// Defines project properties. +// +// API services can define properties that can be assigned to consumer projects +// so that backends can perform response customization without having to make +// additional calls or maintain additional storage. For example, Maps API +// defines properties that controls map tile cache period, or whether to embed a +// watermark in a result. +// +// These values can be set via API producer console. Only API providers can +// define and set these properties. +message Property { + // Supported data type of the property values + enum PropertyType { + // The type is unspecified, and will result in an error. + UNSPECIFIED = 0; + + // The type is `int64`. + INT64 = 1; + + // The type is `bool`. + BOOL = 2; + + // The type is `string`. + STRING = 3; + + // The type is 'double'. + DOUBLE = 4; + } + + // The name of the property (a.k.a key). + string name = 1; + + // The type of this property. + PropertyType type = 2; + + // The description of the property + string description = 3; +} diff --git a/venv/lib/python3.12/site-packages/google/api/consumer_pb2.py b/venv/lib/python3.12/site-packages/google/api/consumer_pb2.py new file mode 100644 index 0000000..8dd1f11 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/consumer_pb2.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/consumer.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/consumer.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x19google/api/consumer.proto\x12\ngoogle.api"=\n\x11ProjectProperties\x12(\n\nproperties\x18\x01 \x03(\x0b\x32\x14.google.api.Property"\xac\x01\n\x08Property\x12\x0c\n\x04name\x18\x01 \x01(\t\x12/\n\x04type\x18\x02 \x01(\x0e\x32!.google.api.Property.PropertyType\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t"L\n\x0cPropertyType\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\t\n\x05INT64\x10\x01\x12\x08\n\x04\x42OOL\x10\x02\x12\n\n\x06STRING\x10\x03\x12\n\n\x06\x44OUBLE\x10\x04\x42h\n\x0e\x63om.google.apiB\rConsumerProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfigb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.consumer_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\rConsumerProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig" + _globals["_PROJECTPROPERTIES"]._serialized_start = 41 + _globals["_PROJECTPROPERTIES"]._serialized_end = 102 + _globals["_PROPERTY"]._serialized_start = 105 + _globals["_PROPERTY"]._serialized_end = 277 + _globals["_PROPERTY_PROPERTYTYPE"]._serialized_start = 201 + _globals["_PROPERTY_PROPERTYTYPE"]._serialized_end = 277 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/consumer_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/consumer_pb2.pyi new file mode 100644 index 0000000..4e990bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/consumer_pb2.pyi @@ -0,0 +1,62 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor + +class ProjectProperties(_message.Message): + __slots__ = ("properties",) + PROPERTIES_FIELD_NUMBER: _ClassVar[int] + properties: _containers.RepeatedCompositeFieldContainer[Property] + def __init__( + self, properties: _Optional[_Iterable[_Union[Property, _Mapping]]] = ... + ) -> None: ... + +class Property(_message.Message): + __slots__ = ("name", "type", "description") + class PropertyType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + UNSPECIFIED: _ClassVar[Property.PropertyType] + INT64: _ClassVar[Property.PropertyType] + BOOL: _ClassVar[Property.PropertyType] + STRING: _ClassVar[Property.PropertyType] + DOUBLE: _ClassVar[Property.PropertyType] + + UNSPECIFIED: Property.PropertyType + INT64: Property.PropertyType + BOOL: Property.PropertyType + STRING: Property.PropertyType + DOUBLE: Property.PropertyType + NAME_FIELD_NUMBER: _ClassVar[int] + TYPE_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + name: str + type: Property.PropertyType + description: str + def __init__( + self, + name: _Optional[str] = ..., + type: _Optional[_Union[Property.PropertyType, str]] = ..., + description: _Optional[str] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/context.proto b/venv/lib/python3.12/site-packages/google/api/context.proto new file mode 100644 index 0000000..c1704f1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/context.proto @@ -0,0 +1,92 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "ContextProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// `Context` defines which contexts an API requests. +// +// Example: +// +// context: +// rules: +// - selector: "*" +// requested: +// - google.rpc.context.ProjectContext +// - google.rpc.context.OriginContext +// +// The above specifies that all methods in the API request +// `google.rpc.context.ProjectContext` and +// `google.rpc.context.OriginContext`. +// +// Available context types are defined in package +// `google.rpc.context`. +// +// This also provides mechanism to allowlist any protobuf message extension that +// can be sent in grpc metadata using “x-goog-ext--bin” and +// “x-goog-ext--jspb” format. For example, list any service +// specific protobuf types that can appear in grpc metadata as follows in your +// yaml file: +// +// Example: +// +// context: +// rules: +// - selector: "google.example.library.v1.LibraryService.CreateBook" +// allowed_request_extensions: +// - google.foo.v1.NewExtension +// allowed_response_extensions: +// - google.foo.v1.NewExtension +// +// You can also specify extension ID instead of fully qualified extension name +// here. +message Context { + // A list of RPC context rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated ContextRule rules = 1; +} + +// A context rule provides information about the context for an individual API +// element. +message ContextRule { + // Selects the methods to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // A list of full type names of requested contexts, only the requested context + // will be made available to the backend. + repeated string requested = 2; + + // A list of full type names of provided contexts. It is used to support + // propagating HTTP headers and ETags from the response extension. + repeated string provided = 3; + + // A list of full type names or extension IDs of extensions allowed in grpc + // side channel from client to backend. + repeated string allowed_request_extensions = 4; + + // A list of full type names or extension IDs of extensions allowed in grpc + // side channel from backend to client. + repeated string allowed_response_extensions = 5; +} diff --git a/venv/lib/python3.12/site-packages/google/api/context_pb2.py b/venv/lib/python3.12/site-packages/google/api/context_pb2.py new file mode 100644 index 0000000..70bb2fb --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/context_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/context.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/context.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x18google/api/context.proto\x12\ngoogle.api"1\n\x07\x43ontext\x12&\n\x05rules\x18\x01 \x03(\x0b\x32\x17.google.api.ContextRule"\x8d\x01\n\x0b\x43ontextRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12\x11\n\trequested\x18\x02 \x03(\t\x12\x10\n\x08provided\x18\x03 \x03(\t\x12"\n\x1a\x61llowed_request_extensions\x18\x04 \x03(\t\x12#\n\x1b\x61llowed_response_extensions\x18\x05 \x03(\tBn\n\x0e\x63om.google.apiB\x0c\x43ontextProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.context_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\014ContextProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_CONTEXT"]._serialized_start = 40 + _globals["_CONTEXT"]._serialized_end = 89 + _globals["_CONTEXTRULE"]._serialized_start = 92 + _globals["_CONTEXTRULE"]._serialized_end = 233 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/context_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/context_pb2.pyi new file mode 100644 index 0000000..65612db --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/context_pb2.pyi @@ -0,0 +1,60 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class Context(_message.Message): + __slots__ = ("rules",) + RULES_FIELD_NUMBER: _ClassVar[int] + rules: _containers.RepeatedCompositeFieldContainer[ContextRule] + def __init__( + self, rules: _Optional[_Iterable[_Union[ContextRule, _Mapping]]] = ... + ) -> None: ... + +class ContextRule(_message.Message): + __slots__ = ( + "selector", + "requested", + "provided", + "allowed_request_extensions", + "allowed_response_extensions", + ) + SELECTOR_FIELD_NUMBER: _ClassVar[int] + REQUESTED_FIELD_NUMBER: _ClassVar[int] + PROVIDED_FIELD_NUMBER: _ClassVar[int] + ALLOWED_REQUEST_EXTENSIONS_FIELD_NUMBER: _ClassVar[int] + ALLOWED_RESPONSE_EXTENSIONS_FIELD_NUMBER: _ClassVar[int] + selector: str + requested: _containers.RepeatedScalarFieldContainer[str] + provided: _containers.RepeatedScalarFieldContainer[str] + allowed_request_extensions: _containers.RepeatedScalarFieldContainer[str] + allowed_response_extensions: _containers.RepeatedScalarFieldContainer[str] + def __init__( + self, + selector: _Optional[str] = ..., + requested: _Optional[_Iterable[str]] = ..., + provided: _Optional[_Iterable[str]] = ..., + allowed_request_extensions: _Optional[_Iterable[str]] = ..., + allowed_response_extensions: _Optional[_Iterable[str]] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/control.proto b/venv/lib/python3.12/site-packages/google/api/control.proto new file mode 100644 index 0000000..c0dc0cd --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/control.proto @@ -0,0 +1,41 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/policy.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "ControlProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Selects and configures the service controller used by the service. +// +// Example: +// +// control: +// environment: servicecontrol.googleapis.com +message Control { + // The service controller environment to use. If empty, no control plane + // features (like quota and billing) will be enabled. The recommended value + // for most services is servicecontrol.googleapis.com. + string environment = 1; + + // Defines policies applying to the API methods of the service. + repeated MethodPolicy method_policies = 4; +} diff --git a/venv/lib/python3.12/site-packages/google/api/control_pb2.py b/venv/lib/python3.12/site-packages/google/api/control_pb2.py new file mode 100644 index 0000000..c25f7bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/control_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/control.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/control.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.api import policy_pb2 as google_dot_api_dot_policy__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x18google/api/control.proto\x12\ngoogle.api\x1a\x17google/api/policy.proto"Q\n\x07\x43ontrol\x12\x13\n\x0b\x65nvironment\x18\x01 \x01(\t\x12\x31\n\x0fmethod_policies\x18\x04 \x03(\x0b\x32\x18.google.api.MethodPolicyBn\n\x0e\x63om.google.apiB\x0c\x43ontrolProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.control_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\014ControlProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_CONTROL"]._serialized_start = 65 + _globals["_CONTROL"]._serialized_end = 146 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/control_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/control_pb2.pyi new file mode 100644 index 0000000..789dc6c --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/control_pb2.pyi @@ -0,0 +1,43 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +from google.api import policy_pb2 as _policy_pb2 + +DESCRIPTOR: _descriptor.FileDescriptor + +class Control(_message.Message): + __slots__ = ("environment", "method_policies") + ENVIRONMENT_FIELD_NUMBER: _ClassVar[int] + METHOD_POLICIES_FIELD_NUMBER: _ClassVar[int] + environment: str + method_policies: _containers.RepeatedCompositeFieldContainer[ + _policy_pb2.MethodPolicy + ] + def __init__( + self, + environment: _Optional[str] = ..., + method_policies: _Optional[ + _Iterable[_Union[_policy_pb2.MethodPolicy, _Mapping]] + ] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/distribution.proto b/venv/lib/python3.12/site-packages/google/api/distribution.proto new file mode 100644 index 0000000..51cf36e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/distribution.proto @@ -0,0 +1,213 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/distribution;distribution"; +option java_multiple_files = true; +option java_outer_classname = "DistributionProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// `Distribution` contains summary statistics for a population of values. It +// optionally contains a histogram representing the distribution of those values +// across a set of buckets. +// +// The summary statistics are the count, mean, sum of the squared deviation from +// the mean, the minimum, and the maximum of the set of population of values. +// The histogram is based on a sequence of buckets and gives a count of values +// that fall into each bucket. The boundaries of the buckets are given either +// explicitly or by formulas for buckets of fixed or exponentially increasing +// widths. +// +// Although it is not forbidden, it is generally a bad idea to include +// non-finite values (infinities or NaNs) in the population of values, as this +// will render the `mean` and `sum_of_squared_deviation` fields meaningless. +message Distribution { + // The range of the population values. + message Range { + // The minimum of the population values. + double min = 1; + + // The maximum of the population values. + double max = 2; + } + + // `BucketOptions` describes the bucket boundaries used to create a histogram + // for the distribution. The buckets can be in a linear sequence, an + // exponential sequence, or each bucket can be specified explicitly. + // `BucketOptions` does not include the number of values in each bucket. + // + // A bucket has an inclusive lower bound and exclusive upper bound for the + // values that are counted for that bucket. The upper bound of a bucket must + // be strictly greater than the lower bound. The sequence of N buckets for a + // distribution consists of an underflow bucket (number 0), zero or more + // finite buckets (number 1 through N - 2) and an overflow bucket (number N - + // 1). The buckets are contiguous: the lower bound of bucket i (i > 0) is the + // same as the upper bound of bucket i - 1. The buckets span the whole range + // of finite values: lower bound of the underflow bucket is -infinity and the + // upper bound of the overflow bucket is +infinity. The finite buckets are + // so-called because both bounds are finite. + message BucketOptions { + // Specifies a linear sequence of buckets that all have the same width + // (except overflow and underflow). Each bucket represents a constant + // absolute uncertainty on the specific value in the bucket. + // + // There are `num_finite_buckets + 2` (= N) buckets. Bucket `i` has the + // following boundaries: + // + // Upper bound (0 <= i < N-1): offset + (width * i). + // + // Lower bound (1 <= i < N): offset + (width * (i - 1)). + message Linear { + // Must be greater than 0. + int32 num_finite_buckets = 1; + + // Must be greater than 0. + double width = 2; + + // Lower bound of the first bucket. + double offset = 3; + } + + // Specifies an exponential sequence of buckets that have a width that is + // proportional to the value of the lower bound. Each bucket represents a + // constant relative uncertainty on a specific value in the bucket. + // + // There are `num_finite_buckets + 2` (= N) buckets. Bucket `i` has the + // following boundaries: + // + // Upper bound (0 <= i < N-1): scale * (growth_factor ^ i). + // + // Lower bound (1 <= i < N): scale * (growth_factor ^ (i - 1)). + message Exponential { + // Must be greater than 0. + int32 num_finite_buckets = 1; + + // Must be greater than 1. + double growth_factor = 2; + + // Must be greater than 0. + double scale = 3; + } + + // Specifies a set of buckets with arbitrary widths. + // + // There are `size(bounds) + 1` (= N) buckets. Bucket `i` has the following + // boundaries: + // + // Upper bound (0 <= i < N-1): bounds[i] + // Lower bound (1 <= i < N); bounds[i - 1] + // + // The `bounds` field must contain at least one element. If `bounds` has + // only one element, then there are no finite buckets, and that single + // element is the common boundary of the overflow and underflow buckets. + message Explicit { + // The values must be monotonically increasing. + repeated double bounds = 1; + } + + // Exactly one of these three fields must be set. + oneof options { + // The linear bucket. + Linear linear_buckets = 1; + + // The exponential buckets. + Exponential exponential_buckets = 2; + + // The explicit buckets. + Explicit explicit_buckets = 3; + } + } + + // Exemplars are example points that may be used to annotate aggregated + // distribution values. They are metadata that gives information about a + // particular value added to a Distribution bucket, such as a trace ID that + // was active when a value was added. They may contain further information, + // such as a example values and timestamps, origin, etc. + message Exemplar { + // Value of the exemplar point. This value determines to which bucket the + // exemplar belongs. + double value = 1; + + // The observation (sampling) time of the above value. + google.protobuf.Timestamp timestamp = 2; + + // Contextual information about the example value. Examples are: + // + // Trace: type.googleapis.com/google.monitoring.v3.SpanContext + // + // Literal string: type.googleapis.com/google.protobuf.StringValue + // + // Labels dropped during aggregation: + // type.googleapis.com/google.monitoring.v3.DroppedLabels + // + // There may be only a single attachment of any given message type in a + // single exemplar, and this is enforced by the system. + repeated google.protobuf.Any attachments = 3; + } + + // The number of values in the population. Must be non-negative. This value + // must equal the sum of the values in `bucket_counts` if a histogram is + // provided. + int64 count = 1; + + // The arithmetic mean of the values in the population. If `count` is zero + // then this field must be zero. + double mean = 2; + + // The sum of squared deviations from the mean of the values in the + // population. For values x_i this is: + // + // Sum[i=1..n]((x_i - mean)^2) + // + // Knuth, "The Art of Computer Programming", Vol. 2, page 232, 3rd edition + // describes Welford's method for accumulating this sum in one pass. + // + // If `count` is zero then this field must be zero. + double sum_of_squared_deviation = 3; + + // If specified, contains the range of the population values. The field + // must not be present if the `count` is zero. + Range range = 4; + + // Defines the histogram bucket boundaries. If the distribution does not + // contain a histogram, then omit this field. + BucketOptions bucket_options = 6; + + // The number of values in each bucket of the histogram, as described in + // `bucket_options`. If the distribution does not have a histogram, then omit + // this field. If there is a histogram, then the sum of the values in + // `bucket_counts` must equal the value in the `count` field of the + // distribution. + // + // If present, `bucket_counts` should contain N values, where N is the number + // of buckets specified in `bucket_options`. If you supply fewer than N + // values, the remaining values are assumed to be 0. + // + // The order of the values in `bucket_counts` follows the bucket numbering + // schemes described for the three bucket types. The first value must be the + // count for the underflow bucket (number 0). The next N-2 values are the + // counts for the finite buckets (number 1 through N-2). The N'th value in + // `bucket_counts` is the count for the overflow bucket (number N-1). + repeated int64 bucket_counts = 7; + + // Must be in increasing order of `value` field. + repeated Exemplar exemplars = 10; +} diff --git a/venv/lib/python3.12/site-packages/google/api/distribution_pb2.py b/venv/lib/python3.12/site-packages/google/api/distribution_pb2.py new file mode 100644 index 0000000..c413513 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/distribution_pb2.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/distribution.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/distribution.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1dgoogle/api/distribution.proto\x12\ngoogle.api\x1a\x19google/protobuf/any.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\xd9\x06\n\x0c\x44istribution\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12\x0c\n\x04mean\x18\x02 \x01(\x01\x12 \n\x18sum_of_squared_deviation\x18\x03 \x01(\x01\x12-\n\x05range\x18\x04 \x01(\x0b\x32\x1e.google.api.Distribution.Range\x12>\n\x0e\x62ucket_options\x18\x06 \x01(\x0b\x32&.google.api.Distribution.BucketOptions\x12\x15\n\rbucket_counts\x18\x07 \x03(\x03\x12\x34\n\texemplars\x18\n \x03(\x0b\x32!.google.api.Distribution.Exemplar\x1a!\n\x05Range\x12\x0b\n\x03min\x18\x01 \x01(\x01\x12\x0b\n\x03max\x18\x02 \x01(\x01\x1a\xb5\x03\n\rBucketOptions\x12G\n\x0elinear_buckets\x18\x01 \x01(\x0b\x32-.google.api.Distribution.BucketOptions.LinearH\x00\x12Q\n\x13\x65xponential_buckets\x18\x02 \x01(\x0b\x32\x32.google.api.Distribution.BucketOptions.ExponentialH\x00\x12K\n\x10\x65xplicit_buckets\x18\x03 \x01(\x0b\x32/.google.api.Distribution.BucketOptions.ExplicitH\x00\x1a\x43\n\x06Linear\x12\x1a\n\x12num_finite_buckets\x18\x01 \x01(\x05\x12\r\n\x05width\x18\x02 \x01(\x01\x12\x0e\n\x06offset\x18\x03 \x01(\x01\x1aO\n\x0b\x45xponential\x12\x1a\n\x12num_finite_buckets\x18\x01 \x01(\x05\x12\x15\n\rgrowth_factor\x18\x02 \x01(\x01\x12\r\n\x05scale\x18\x03 \x01(\x01\x1a\x1a\n\x08\x45xplicit\x12\x0e\n\x06\x62ounds\x18\x01 \x03(\x01\x42\t\n\x07options\x1as\n\x08\x45xemplar\x12\r\n\x05value\x18\x01 \x01(\x01\x12-\n\ttimestamp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12)\n\x0b\x61ttachments\x18\x03 \x03(\x0b\x32\x14.google.protobuf.AnyBq\n\x0e\x63om.google.apiB\x11\x44istributionProtoP\x01ZCgoogle.golang.org/genproto/googleapis/api/distribution;distribution\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.api.distribution_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\021DistributionProtoP\001ZCgoogle.golang.org/genproto/googleapis/api/distribution;distribution\242\002\004GAPI" + _globals["_DISTRIBUTION"]._serialized_start = 106 + _globals["_DISTRIBUTION"]._serialized_end = 963 + _globals["_DISTRIBUTION_RANGE"]._serialized_start = 373 + _globals["_DISTRIBUTION_RANGE"]._serialized_end = 406 + _globals["_DISTRIBUTION_BUCKETOPTIONS"]._serialized_start = 409 + _globals["_DISTRIBUTION_BUCKETOPTIONS"]._serialized_end = 846 + _globals["_DISTRIBUTION_BUCKETOPTIONS_LINEAR"]._serialized_start = 659 + _globals["_DISTRIBUTION_BUCKETOPTIONS_LINEAR"]._serialized_end = 726 + _globals["_DISTRIBUTION_BUCKETOPTIONS_EXPONENTIAL"]._serialized_start = 728 + _globals["_DISTRIBUTION_BUCKETOPTIONS_EXPONENTIAL"]._serialized_end = 807 + _globals["_DISTRIBUTION_BUCKETOPTIONS_EXPLICIT"]._serialized_start = 809 + _globals["_DISTRIBUTION_BUCKETOPTIONS_EXPLICIT"]._serialized_end = 835 + _globals["_DISTRIBUTION_EXEMPLAR"]._serialized_start = 848 + _globals["_DISTRIBUTION_EXEMPLAR"]._serialized_end = 963 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/distribution_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/distribution_pb2.pyi new file mode 100644 index 0000000..5b3ca23 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/distribution_pb2.pyi @@ -0,0 +1,147 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import any_pb2 as _any_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import timestamp_pb2 as _timestamp_pb2 +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class Distribution(_message.Message): + __slots__ = ( + "count", + "mean", + "sum_of_squared_deviation", + "range", + "bucket_options", + "bucket_counts", + "exemplars", + ) + class Range(_message.Message): + __slots__ = ("min", "max") + MIN_FIELD_NUMBER: _ClassVar[int] + MAX_FIELD_NUMBER: _ClassVar[int] + min: float + max: float + def __init__( + self, min: _Optional[float] = ..., max: _Optional[float] = ... + ) -> None: ... + + class BucketOptions(_message.Message): + __slots__ = ("linear_buckets", "exponential_buckets", "explicit_buckets") + class Linear(_message.Message): + __slots__ = ("num_finite_buckets", "width", "offset") + NUM_FINITE_BUCKETS_FIELD_NUMBER: _ClassVar[int] + WIDTH_FIELD_NUMBER: _ClassVar[int] + OFFSET_FIELD_NUMBER: _ClassVar[int] + num_finite_buckets: int + width: float + offset: float + def __init__( + self, + num_finite_buckets: _Optional[int] = ..., + width: _Optional[float] = ..., + offset: _Optional[float] = ..., + ) -> None: ... + + class Exponential(_message.Message): + __slots__ = ("num_finite_buckets", "growth_factor", "scale") + NUM_FINITE_BUCKETS_FIELD_NUMBER: _ClassVar[int] + GROWTH_FACTOR_FIELD_NUMBER: _ClassVar[int] + SCALE_FIELD_NUMBER: _ClassVar[int] + num_finite_buckets: int + growth_factor: float + scale: float + def __init__( + self, + num_finite_buckets: _Optional[int] = ..., + growth_factor: _Optional[float] = ..., + scale: _Optional[float] = ..., + ) -> None: ... + + class Explicit(_message.Message): + __slots__ = ("bounds",) + BOUNDS_FIELD_NUMBER: _ClassVar[int] + bounds: _containers.RepeatedScalarFieldContainer[float] + def __init__(self, bounds: _Optional[_Iterable[float]] = ...) -> None: ... + + LINEAR_BUCKETS_FIELD_NUMBER: _ClassVar[int] + EXPONENTIAL_BUCKETS_FIELD_NUMBER: _ClassVar[int] + EXPLICIT_BUCKETS_FIELD_NUMBER: _ClassVar[int] + linear_buckets: Distribution.BucketOptions.Linear + exponential_buckets: Distribution.BucketOptions.Exponential + explicit_buckets: Distribution.BucketOptions.Explicit + def __init__( + self, + linear_buckets: _Optional[ + _Union[Distribution.BucketOptions.Linear, _Mapping] + ] = ..., + exponential_buckets: _Optional[ + _Union[Distribution.BucketOptions.Exponential, _Mapping] + ] = ..., + explicit_buckets: _Optional[ + _Union[Distribution.BucketOptions.Explicit, _Mapping] + ] = ..., + ) -> None: ... + + class Exemplar(_message.Message): + __slots__ = ("value", "timestamp", "attachments") + VALUE_FIELD_NUMBER: _ClassVar[int] + TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + ATTACHMENTS_FIELD_NUMBER: _ClassVar[int] + value: float + timestamp: _timestamp_pb2.Timestamp + attachments: _containers.RepeatedCompositeFieldContainer[_any_pb2.Any] + def __init__( + self, + value: _Optional[float] = ..., + timestamp: _Optional[ + _Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping] + ] = ..., + attachments: _Optional[_Iterable[_Union[_any_pb2.Any, _Mapping]]] = ..., + ) -> None: ... + + COUNT_FIELD_NUMBER: _ClassVar[int] + MEAN_FIELD_NUMBER: _ClassVar[int] + SUM_OF_SQUARED_DEVIATION_FIELD_NUMBER: _ClassVar[int] + RANGE_FIELD_NUMBER: _ClassVar[int] + BUCKET_OPTIONS_FIELD_NUMBER: _ClassVar[int] + BUCKET_COUNTS_FIELD_NUMBER: _ClassVar[int] + EXEMPLARS_FIELD_NUMBER: _ClassVar[int] + count: int + mean: float + sum_of_squared_deviation: float + range: Distribution.Range + bucket_options: Distribution.BucketOptions + bucket_counts: _containers.RepeatedScalarFieldContainer[int] + exemplars: _containers.RepeatedCompositeFieldContainer[Distribution.Exemplar] + def __init__( + self, + count: _Optional[int] = ..., + mean: _Optional[float] = ..., + sum_of_squared_deviation: _Optional[float] = ..., + range: _Optional[_Union[Distribution.Range, _Mapping]] = ..., + bucket_options: _Optional[_Union[Distribution.BucketOptions, _Mapping]] = ..., + bucket_counts: _Optional[_Iterable[int]] = ..., + exemplars: _Optional[_Iterable[_Union[Distribution.Exemplar, _Mapping]]] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/documentation.proto b/venv/lib/python3.12/site-packages/google/api/documentation.proto new file mode 100644 index 0000000..a730ab8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/documentation.proto @@ -0,0 +1,168 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "DocumentationProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// `Documentation` provides the information for describing a service. +// +// Example: +//
documentation:
+//   summary: >
+//     The Google Calendar API gives access
+//     to most calendar features.
+//   pages:
+//   - name: Overview
+//     content: (== include google/foo/overview.md ==)
+//   - name: Tutorial
+//     content: (== include google/foo/tutorial.md ==)
+//     subpages:
+//     - name: Java
+//       content: (== include google/foo/tutorial_java.md ==)
+//   rules:
+//   - selector: google.calendar.Calendar.Get
+//     description: >
+//       ...
+//   - selector: google.calendar.Calendar.Put
+//     description: >
+//       ...
+// 
+// Documentation is provided in markdown syntax. In addition to +// standard markdown features, definition lists, tables and fenced +// code blocks are supported. Section headers can be provided and are +// interpreted relative to the section nesting of the context where +// a documentation fragment is embedded. +// +// Documentation from the IDL is merged with documentation defined +// via the config at normalization time, where documentation provided +// by config rules overrides IDL provided. +// +// A number of constructs specific to the API platform are supported +// in documentation text. +// +// In order to reference a proto element, the following +// notation can be used: +//
[fully.qualified.proto.name][]
+// To override the display text used for the link, this can be used: +//
[display text][fully.qualified.proto.name]
+// Text can be excluded from doc using the following notation: +//
(-- internal comment --)
+// +// A few directives are available in documentation. Note that +// directives must appear on a single line to be properly +// identified. The `include` directive includes a markdown file from +// an external source: +//
(== include path/to/file ==)
+// The `resource_for` directive marks a message to be the resource of +// a collection in REST view. If it is not specified, tools attempt +// to infer the resource from the operations in a collection: +//
(== resource_for v1.shelves.books ==)
+// The directive `suppress_warning` does not directly affect documentation +// and is documented together with service config validation. +message Documentation { + // A short description of what the service does. The summary must be plain + // text. It becomes the overview of the service displayed in Google Cloud + // Console. + // NOTE: This field is equivalent to the standard field `description`. + string summary = 1; + + // The top level pages for the documentation set. + repeated Page pages = 5; + + // A list of documentation rules that apply to individual API elements. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated DocumentationRule rules = 3; + + // The URL to the root of documentation. + string documentation_root_url = 4; + + // Specifies the service root url if the default one (the service name + // from the yaml file) is not suitable. This can be seen in any fully + // specified service urls as well as sections that show a base that other + // urls are relative to. + string service_root_url = 6; + + // Declares a single overview page. For example: + //
documentation:
+  //   summary: ...
+  //   overview: (== include overview.md ==)
+  // 
+ // This is a shortcut for the following declaration (using pages style): + //
documentation:
+  //   summary: ...
+  //   pages:
+  //   - name: Overview
+  //     content: (== include overview.md ==)
+  // 
+ // Note: you cannot specify both `overview` field and `pages` field. + string overview = 2; +} + +// A documentation rule provides information about individual API elements. +message DocumentationRule { + // The selector is a comma-separated list of patterns for any element such as + // a method, a field, an enum value. Each pattern is a qualified name of the + // element which may end in "*", indicating a wildcard. Wildcards are only + // allowed at the end and for a whole component of the qualified name, + // i.e. "foo.*" is ok, but not "foo.b*" or "foo.*.bar". A wildcard will match + // one or more components. To specify a default for all applicable elements, + // the whole pattern "*" is used. + string selector = 1; + + // Description of the selected proto element (e.g. a message, a method, a + // 'service' definition, or a field). Defaults to leading & trailing comments + // taken from the proto source definition of the proto element. + string description = 2; + + // Deprecation description of the selected element(s). It can be provided if + // an element is marked as `deprecated`. + string deprecation_description = 3; +} + +// Represents a documentation page. A page can contain subpages to represent +// nested documentation set structure. +message Page { + // The name of the page. It will be used as an identity of the page to + // generate URI of the page, text of the link to this page in navigation, + // etc. The full page name (start from the root page name to this page + // concatenated with `.`) can be used as reference to the page in your + // documentation. For example: + //
pages:
+  // - name: Tutorial
+  //   content: (== include tutorial.md ==)
+  //   subpages:
+  //   - name: Java
+  //     content: (== include tutorial_java.md ==)
+  // 
+ // You can reference `Java` page using Markdown reference link syntax: + // `[Java][Tutorial.Java]`. + string name = 1; + + // The Markdown content of the page. You can use ```(== include {path} + // ==)``` to include content from a Markdown file. The content can be used + // to produce the documentation page such as HTML format page. + string content = 2; + + // Subpages of this page. The order of subpages specified here will be + // honored in the generated docset. + repeated Page subpages = 3; +} diff --git a/venv/lib/python3.12/site-packages/google/api/documentation_pb2.py b/venv/lib/python3.12/site-packages/google/api/documentation_pb2.py new file mode 100644 index 0000000..c1c4fb2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/documentation_pb2.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/documentation.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/documentation.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1egoogle/api/documentation.proto\x12\ngoogle.api"\xbb\x01\n\rDocumentation\x12\x0f\n\x07summary\x18\x01 \x01(\t\x12\x1f\n\x05pages\x18\x05 \x03(\x0b\x32\x10.google.api.Page\x12,\n\x05rules\x18\x03 \x03(\x0b\x32\x1d.google.api.DocumentationRule\x12\x1e\n\x16\x64ocumentation_root_url\x18\x04 \x01(\t\x12\x18\n\x10service_root_url\x18\x06 \x01(\t\x12\x10\n\x08overview\x18\x02 \x01(\t"[\n\x11\x44ocumentationRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x1f\n\x17\x64\x65precation_description\x18\x03 \x01(\t"I\n\x04Page\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12"\n\x08subpages\x18\x03 \x03(\x0b\x32\x10.google.api.PageBt\n\x0e\x63om.google.apiB\x12\x44ocumentationProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.api.documentation_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\022DocumentationProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_DOCUMENTATION"]._serialized_start = 47 + _globals["_DOCUMENTATION"]._serialized_end = 234 + _globals["_DOCUMENTATIONRULE"]._serialized_start = 236 + _globals["_DOCUMENTATIONRULE"]._serialized_end = 327 + _globals["_PAGE"]._serialized_start = 329 + _globals["_PAGE"]._serialized_end = 402 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/documentation_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/documentation_pb2.pyi new file mode 100644 index 0000000..f5e066d --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/documentation_pb2.pyi @@ -0,0 +1,86 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class Documentation(_message.Message): + __slots__ = ( + "summary", + "pages", + "rules", + "documentation_root_url", + "service_root_url", + "overview", + ) + SUMMARY_FIELD_NUMBER: _ClassVar[int] + PAGES_FIELD_NUMBER: _ClassVar[int] + RULES_FIELD_NUMBER: _ClassVar[int] + DOCUMENTATION_ROOT_URL_FIELD_NUMBER: _ClassVar[int] + SERVICE_ROOT_URL_FIELD_NUMBER: _ClassVar[int] + OVERVIEW_FIELD_NUMBER: _ClassVar[int] + summary: str + pages: _containers.RepeatedCompositeFieldContainer[Page] + rules: _containers.RepeatedCompositeFieldContainer[DocumentationRule] + documentation_root_url: str + service_root_url: str + overview: str + def __init__( + self, + summary: _Optional[str] = ..., + pages: _Optional[_Iterable[_Union[Page, _Mapping]]] = ..., + rules: _Optional[_Iterable[_Union[DocumentationRule, _Mapping]]] = ..., + documentation_root_url: _Optional[str] = ..., + service_root_url: _Optional[str] = ..., + overview: _Optional[str] = ..., + ) -> None: ... + +class DocumentationRule(_message.Message): + __slots__ = ("selector", "description", "deprecation_description") + SELECTOR_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + DEPRECATION_DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + selector: str + description: str + deprecation_description: str + def __init__( + self, + selector: _Optional[str] = ..., + description: _Optional[str] = ..., + deprecation_description: _Optional[str] = ..., + ) -> None: ... + +class Page(_message.Message): + __slots__ = ("name", "content", "subpages") + NAME_FIELD_NUMBER: _ClassVar[int] + CONTENT_FIELD_NUMBER: _ClassVar[int] + SUBPAGES_FIELD_NUMBER: _ClassVar[int] + name: str + content: str + subpages: _containers.RepeatedCompositeFieldContainer[Page] + def __init__( + self, + name: _Optional[str] = ..., + content: _Optional[str] = ..., + subpages: _Optional[_Iterable[_Union[Page, _Mapping]]] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/endpoint.proto b/venv/lib/python3.12/site-packages/google/api/endpoint.proto new file mode 100644 index 0000000..047cf54 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/endpoint.proto @@ -0,0 +1,69 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "EndpointProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// `Endpoint` describes a network address of a service that serves a set of +// APIs. It is commonly known as a service endpoint. A service may expose +// any number of service endpoints, and all service endpoints share the same +// service definition, such as quota limits and monitoring metrics. +// +// Example: +// +// type: google.api.Service +// name: library-example.googleapis.com +// endpoints: +// # Declares network address `https://library-example.googleapis.com` +// # for service `library-example.googleapis.com`. The `https` scheme +// # is implicit for all service endpoints. Other schemes may be +// # supported in the future. +// - name: library-example.googleapis.com +// allow_cors: false +// - name: content-staging-library-example.googleapis.com +// # Allows HTTP OPTIONS calls to be passed to the API frontend, for it +// # to decide whether the subsequent cross-origin request is allowed +// # to proceed. +// allow_cors: true +message Endpoint { + // The canonical name of this endpoint. + string name = 1; + + // Aliases for this endpoint, these will be served by the same UrlMap as the + // parent endpoint, and will be provisioned in the GCP stack for the Regional + // Endpoints. + repeated string aliases = 2; + + // The specification of an Internet routable address of API frontend that will + // handle requests to this [API + // Endpoint](https://cloud.google.com/apis/design/glossary). It should be + // either a valid IPv4 address or a fully-qualified domain name. For example, + // "8.8.8.8" or "myservice.appspot.com". + string target = 101; + + // Allowing + // [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing), aka + // cross-domain traffic, would allow the backends served from this endpoint to + // receive and respond to HTTP OPTIONS requests. The response will be used by + // the browser to determine whether the subsequent cross-origin request is + // allowed to proceed. + bool allow_cors = 5; +} diff --git a/venv/lib/python3.12/site-packages/google/api/endpoint_pb2.py b/venv/lib/python3.12/site-packages/google/api/endpoint_pb2.py new file mode 100644 index 0000000..8b18f07 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/endpoint_pb2.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/endpoint.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/endpoint.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x19google/api/endpoint.proto\x12\ngoogle.api"M\n\x08\x45ndpoint\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x61liases\x18\x02 \x03(\t\x12\x0e\n\x06target\x18\x65 \x01(\t\x12\x12\n\nallow_cors\x18\x05 \x01(\x08\x42o\n\x0e\x63om.google.apiB\rEndpointProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.endpoint_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\rEndpointProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_ENDPOINT"]._serialized_start = 41 + _globals["_ENDPOINT"]._serialized_end = 118 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/endpoint_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/endpoint_pb2.pyi new file mode 100644 index 0000000..8141e33 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/endpoint_pb2.pyi @@ -0,0 +1,41 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class Endpoint(_message.Message): + __slots__ = ("name", "aliases", "target", "allow_cors") + NAME_FIELD_NUMBER: _ClassVar[int] + ALIASES_FIELD_NUMBER: _ClassVar[int] + TARGET_FIELD_NUMBER: _ClassVar[int] + ALLOW_CORS_FIELD_NUMBER: _ClassVar[int] + name: str + aliases: _containers.RepeatedScalarFieldContainer[str] + target: str + allow_cors: bool + def __init__( + self, + name: _Optional[str] = ..., + aliases: _Optional[_Iterable[str]] = ..., + target: _Optional[str] = ..., + allow_cors: _Optional[bool] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/error_reason.proto b/venv/lib/python3.12/site-packages/google/api/error_reason.proto new file mode 100644 index 0000000..3b4826e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/error_reason.proto @@ -0,0 +1,807 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/error_reason;error_reason"; +option java_multiple_files = true; +option java_outer_classname = "ErrorReasonProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the supported values for `google.rpc.ErrorInfo.reason` for the +// `googleapis.com` error domain. This error domain is reserved for [Service +// Infrastructure](https://cloud.google.com/service-infrastructure/docs/overview). +// For each error info of this domain, the metadata key "service" refers to the +// logical identifier of an API service, such as "pubsub.googleapis.com". The +// "consumer" refers to the entity that consumes an API Service. It typically is +// a Google project that owns the client application or the server resource, +// such as "projects/123". Other metadata keys are specific to each error +// reason. For more information, see the definition of the specific error +// reason. +enum ErrorReason { + // Do not use this default value. + ERROR_REASON_UNSPECIFIED = 0; + + // The request is calling a disabled service for a consumer. + // + // Example of an ErrorInfo when the consumer "projects/123" contacting + // "pubsub.googleapis.com" service which is disabled: + // + // { "reason": "SERVICE_DISABLED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "pubsub.googleapis.com" + // } + // } + // + // This response indicates the "pubsub.googleapis.com" has been disabled in + // "projects/123". + SERVICE_DISABLED = 1; + + // The request whose associated billing account is disabled. + // + // Example of an ErrorInfo when the consumer "projects/123" fails to contact + // "pubsub.googleapis.com" service because the associated billing account is + // disabled: + // + // { "reason": "BILLING_DISABLED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "pubsub.googleapis.com" + // } + // } + // + // This response indicates the billing account associated has been disabled. + BILLING_DISABLED = 2; + + // The request is denied because the provided [API + // key](https://cloud.google.com/docs/authentication/api-keys) is invalid. It + // may be in a bad format, cannot be found, or has been expired). + // + // Example of an ErrorInfo when the request is contacting + // "storage.googleapis.com" service with an invalid API key: + // + // { "reason": "API_KEY_INVALID", + // "domain": "googleapis.com", + // "metadata": { + // "service": "storage.googleapis.com", + // } + // } + API_KEY_INVALID = 3; + + // The request is denied because it violates [API key API + // restrictions](https://cloud.google.com/docs/authentication/api-keys#adding_api_restrictions). + // + // Example of an ErrorInfo when the consumer "projects/123" fails to call the + // "storage.googleapis.com" service because this service is restricted in the + // API key: + // + // { "reason": "API_KEY_SERVICE_BLOCKED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "storage.googleapis.com" + // } + // } + API_KEY_SERVICE_BLOCKED = 4; + + // The request is denied because it violates [API key HTTP + // restrictions](https://cloud.google.com/docs/authentication/api-keys#adding_http_restrictions). + // + // Example of an ErrorInfo when the consumer "projects/123" fails to call + // "storage.googleapis.com" service because the http referrer of the request + // violates API key HTTP restrictions: + // + // { "reason": "API_KEY_HTTP_REFERRER_BLOCKED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "storage.googleapis.com", + // } + // } + API_KEY_HTTP_REFERRER_BLOCKED = 7; + + // The request is denied because it violates [API key IP address + // restrictions](https://cloud.google.com/docs/authentication/api-keys#adding_application_restrictions). + // + // Example of an ErrorInfo when the consumer "projects/123" fails to call + // "storage.googleapis.com" service because the caller IP of the request + // violates API key IP address restrictions: + // + // { "reason": "API_KEY_IP_ADDRESS_BLOCKED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "storage.googleapis.com", + // } + // } + API_KEY_IP_ADDRESS_BLOCKED = 8; + + // The request is denied because it violates [API key Android application + // restrictions](https://cloud.google.com/docs/authentication/api-keys#adding_application_restrictions). + // + // Example of an ErrorInfo when the consumer "projects/123" fails to call + // "storage.googleapis.com" service because the request from the Android apps + // violates the API key Android application restrictions: + // + // { "reason": "API_KEY_ANDROID_APP_BLOCKED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "storage.googleapis.com" + // } + // } + API_KEY_ANDROID_APP_BLOCKED = 9; + + // The request is denied because it violates [API key iOS application + // restrictions](https://cloud.google.com/docs/authentication/api-keys#adding_application_restrictions). + // + // Example of an ErrorInfo when the consumer "projects/123" fails to call + // "storage.googleapis.com" service because the request from the iOS apps + // violates the API key iOS application restrictions: + // + // { "reason": "API_KEY_IOS_APP_BLOCKED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "storage.googleapis.com" + // } + // } + API_KEY_IOS_APP_BLOCKED = 13; + + // The request is denied because there is not enough rate quota for the + // consumer. + // + // Example of an ErrorInfo when the consumer "projects/123" fails to contact + // "pubsub.googleapis.com" service because consumer's rate quota usage has + // reached the maximum value set for the quota limit + // "ReadsPerMinutePerProject" on the quota metric + // "pubsub.googleapis.com/read_requests": + // + // { "reason": "RATE_LIMIT_EXCEEDED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "pubsub.googleapis.com", + // "quota_metric": "pubsub.googleapis.com/read_requests", + // "quota_limit": "ReadsPerMinutePerProject" + // } + // } + // + // Example of an ErrorInfo when the consumer "projects/123" checks quota on + // the service "dataflow.googleapis.com" and hits the organization quota + // limit "DefaultRequestsPerMinutePerOrganization" on the metric + // "dataflow.googleapis.com/default_requests". + // + // { "reason": "RATE_LIMIT_EXCEEDED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "dataflow.googleapis.com", + // "quota_metric": "dataflow.googleapis.com/default_requests", + // "quota_limit": "DefaultRequestsPerMinutePerOrganization" + // } + // } + RATE_LIMIT_EXCEEDED = 5; + + // The request is denied because there is not enough resource quota for the + // consumer. + // + // Example of an ErrorInfo when the consumer "projects/123" fails to contact + // "compute.googleapis.com" service because consumer's resource quota usage + // has reached the maximum value set for the quota limit "VMsPerProject" + // on the quota metric "compute.googleapis.com/vms": + // + // { "reason": "RESOURCE_QUOTA_EXCEEDED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "compute.googleapis.com", + // "quota_metric": "compute.googleapis.com/vms", + // "quota_limit": "VMsPerProject" + // } + // } + // + // Example of an ErrorInfo when the consumer "projects/123" checks resource + // quota on the service "dataflow.googleapis.com" and hits the organization + // quota limit "jobs-per-organization" on the metric + // "dataflow.googleapis.com/job_count". + // + // { "reason": "RESOURCE_QUOTA_EXCEEDED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "dataflow.googleapis.com", + // "quota_metric": "dataflow.googleapis.com/job_count", + // "quota_limit": "jobs-per-organization" + // } + // } + RESOURCE_QUOTA_EXCEEDED = 6; + + // The request whose associated billing account address is in a tax restricted + // location, violates the local tax restrictions when creating resources in + // the restricted region. + // + // Example of an ErrorInfo when creating the Cloud Storage Bucket in the + // container "projects/123" under a tax restricted region + // "locations/asia-northeast3": + // + // { "reason": "LOCATION_TAX_POLICY_VIOLATED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "storage.googleapis.com", + // "location": "locations/asia-northeast3" + // } + // } + // + // This response indicates creating the Cloud Storage Bucket in + // "locations/asia-northeast3" violates the location tax restriction. + LOCATION_TAX_POLICY_VIOLATED = 10; + + // The request is denied because the caller does not have required permission + // on the user project "projects/123" or the user project is invalid. For more + // information, check the [userProject System + // Parameters](https://cloud.google.com/apis/docs/system-parameters). + // + // Example of an ErrorInfo when the caller is calling Cloud Storage service + // with insufficient permissions on the user project: + // + // { "reason": "USER_PROJECT_DENIED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "storage.googleapis.com" + // } + // } + USER_PROJECT_DENIED = 11; + + // The request is denied because the consumer "projects/123" is suspended due + // to Terms of Service(Tos) violations. Check [Project suspension + // guidelines](https://cloud.google.com/resource-manager/docs/project-suspension-guidelines) + // for more information. + // + // Example of an ErrorInfo when calling Cloud Storage service with the + // suspended consumer "projects/123": + // + // { "reason": "CONSUMER_SUSPENDED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "storage.googleapis.com" + // } + // } + CONSUMER_SUSPENDED = 12; + + // The request is denied because the associated consumer is invalid. It may be + // in a bad format, cannot be found, or have been deleted. + // + // Example of an ErrorInfo when calling Cloud Storage service with the + // invalid consumer "projects/123": + // + // { "reason": "CONSUMER_INVALID", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "storage.googleapis.com" + // } + // } + CONSUMER_INVALID = 14; + + // The request is denied because it violates [VPC Service + // Controls](https://cloud.google.com/vpc-service-controls/docs/overview). + // The 'uid' field is a random generated identifier that customer can use it + // to search the audit log for a request rejected by VPC Service Controls. For + // more information, please refer [VPC Service Controls + // Troubleshooting](https://cloud.google.com/vpc-service-controls/docs/troubleshooting#unique-id) + // + // Example of an ErrorInfo when the consumer "projects/123" fails to call + // Cloud Storage service because the request is prohibited by the VPC Service + // Controls. + // + // { "reason": "SECURITY_POLICY_VIOLATED", + // "domain": "googleapis.com", + // "metadata": { + // "uid": "123456789abcde", + // "consumer": "projects/123", + // "service": "storage.googleapis.com" + // } + // } + SECURITY_POLICY_VIOLATED = 15; + + // The request is denied because the provided access token has expired. + // + // Example of an ErrorInfo when the request is calling Cloud Storage service + // with an expired access token: + // + // { "reason": "ACCESS_TOKEN_EXPIRED", + // "domain": "googleapis.com", + // "metadata": { + // "service": "storage.googleapis.com", + // "method": "google.storage.v1.Storage.GetObject" + // } + // } + ACCESS_TOKEN_EXPIRED = 16; + + // The request is denied because the provided access token doesn't have at + // least one of the acceptable scopes required for the API. Please check + // [OAuth 2.0 Scopes for Google + // APIs](https://developers.google.com/identity/protocols/oauth2/scopes) for + // the list of the OAuth 2.0 scopes that you might need to request to access + // the API. + // + // Example of an ErrorInfo when the request is calling Cloud Storage service + // with an access token that is missing required scopes: + // + // { "reason": "ACCESS_TOKEN_SCOPE_INSUFFICIENT", + // "domain": "googleapis.com", + // "metadata": { + // "service": "storage.googleapis.com", + // "method": "google.storage.v1.Storage.GetObject" + // } + // } + ACCESS_TOKEN_SCOPE_INSUFFICIENT = 17; + + // The request is denied because the account associated with the provided + // access token is in an invalid state, such as disabled or deleted. + // For more information, see https://cloud.google.com/docs/authentication. + // + // Warning: For privacy reasons, the server may not be able to disclose the + // email address for some accounts. The client MUST NOT depend on the + // availability of the `email` attribute. + // + // Example of an ErrorInfo when the request is to the Cloud Storage API with + // an access token that is associated with a disabled or deleted [service + // account](http://cloud/iam/docs/service-accounts): + // + // { "reason": "ACCOUNT_STATE_INVALID", + // "domain": "googleapis.com", + // "metadata": { + // "service": "storage.googleapis.com", + // "method": "google.storage.v1.Storage.GetObject", + // "email": "user@123.iam.gserviceaccount.com" + // } + // } + ACCOUNT_STATE_INVALID = 18; + + // The request is denied because the type of the provided access token is not + // supported by the API being called. + // + // Example of an ErrorInfo when the request is to the Cloud Storage API with + // an unsupported token type. + // + // { "reason": "ACCESS_TOKEN_TYPE_UNSUPPORTED", + // "domain": "googleapis.com", + // "metadata": { + // "service": "storage.googleapis.com", + // "method": "google.storage.v1.Storage.GetObject" + // } + // } + ACCESS_TOKEN_TYPE_UNSUPPORTED = 19; + + // The request is denied because the request doesn't have any authentication + // credentials. For more information regarding the supported authentication + // strategies for Google Cloud APIs, see + // https://cloud.google.com/docs/authentication. + // + // Example of an ErrorInfo when the request is to the Cloud Storage API + // without any authentication credentials. + // + // { "reason": "CREDENTIALS_MISSING", + // "domain": "googleapis.com", + // "metadata": { + // "service": "storage.googleapis.com", + // "method": "google.storage.v1.Storage.GetObject" + // } + // } + CREDENTIALS_MISSING = 20; + + // The request is denied because the provided project owning the resource + // which acts as the [API + // consumer](https://cloud.google.com/apis/design/glossary#api_consumer) is + // invalid. It may be in a bad format or empty. + // + // Example of an ErrorInfo when the request is to the Cloud Functions API, + // but the offered resource project in the request in a bad format which can't + // perform the ListFunctions method. + // + // { "reason": "RESOURCE_PROJECT_INVALID", + // "domain": "googleapis.com", + // "metadata": { + // "service": "cloudfunctions.googleapis.com", + // "method": + // "google.cloud.functions.v1.CloudFunctionsService.ListFunctions" + // } + // } + RESOURCE_PROJECT_INVALID = 21; + + // The request is denied because the provided session cookie is missing, + // invalid or failed to decode. + // + // Example of an ErrorInfo when the request is calling Cloud Storage service + // with a SID cookie which can't be decoded. + // + // { "reason": "SESSION_COOKIE_INVALID", + // "domain": "googleapis.com", + // "metadata": { + // "service": "storage.googleapis.com", + // "method": "google.storage.v1.Storage.GetObject", + // "cookie": "SID" + // } + // } + SESSION_COOKIE_INVALID = 23; + + // The request is denied because the user is from a Google Workspace customer + // that blocks their users from accessing a particular service. + // + // Example scenario: https://support.google.com/a/answer/9197205?hl=en + // + // Example of an ErrorInfo when access to Google Cloud Storage service is + // blocked by the Google Workspace administrator: + // + // { "reason": "USER_BLOCKED_BY_ADMIN", + // "domain": "googleapis.com", + // "metadata": { + // "service": "storage.googleapis.com", + // "method": "google.storage.v1.Storage.GetObject", + // } + // } + USER_BLOCKED_BY_ADMIN = 24; + + // The request is denied because the resource service usage is restricted + // by administrators according to the organization policy constraint. + // For more information see + // https://cloud.google.com/resource-manager/docs/organization-policy/restricting-services. + // + // Example of an ErrorInfo when access to Google Cloud Storage service is + // restricted by Resource Usage Restriction policy: + // + // { "reason": "RESOURCE_USAGE_RESTRICTION_VIOLATED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/project-123", + // "service": "storage.googleapis.com" + // } + // } + RESOURCE_USAGE_RESTRICTION_VIOLATED = 25; + + // Unimplemented. Do not use. + // + // The request is denied because it contains unsupported system parameters in + // URL query parameters or HTTP headers. For more information, + // see https://cloud.google.com/apis/docs/system-parameters + // + // Example of an ErrorInfo when access "pubsub.googleapis.com" service with + // a request header of "x-goog-user-ip": + // + // { "reason": "SYSTEM_PARAMETER_UNSUPPORTED", + // "domain": "googleapis.com", + // "metadata": { + // "service": "pubsub.googleapis.com" + // "parameter": "x-goog-user-ip" + // } + // } + SYSTEM_PARAMETER_UNSUPPORTED = 26; + + // The request is denied because it violates Org Restriction: the requested + // resource does not belong to allowed organizations specified in + // "X-Goog-Allowed-Resources" header. + // + // Example of an ErrorInfo when accessing a GCP resource that is restricted by + // Org Restriction for "pubsub.googleapis.com" service. + // + // { + // reason: "ORG_RESTRICTION_VIOLATION" + // domain: "googleapis.com" + // metadata { + // "consumer":"projects/123456" + // "service": "pubsub.googleapis.com" + // } + // } + ORG_RESTRICTION_VIOLATION = 27; + + // The request is denied because "X-Goog-Allowed-Resources" header is in a bad + // format. + // + // Example of an ErrorInfo when + // accessing "pubsub.googleapis.com" service with an invalid + // "X-Goog-Allowed-Resources" request header. + // + // { + // reason: "ORG_RESTRICTION_HEADER_INVALID" + // domain: "googleapis.com" + // metadata { + // "consumer":"projects/123456" + // "service": "pubsub.googleapis.com" + // } + // } + ORG_RESTRICTION_HEADER_INVALID = 28; + + // Unimplemented. Do not use. + // + // The request is calling a service that is not visible to the consumer. + // + // Example of an ErrorInfo when the consumer "projects/123" contacting + // "pubsub.googleapis.com" service which is not visible to the consumer. + // + // { "reason": "SERVICE_NOT_VISIBLE", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "pubsub.googleapis.com" + // } + // } + // + // This response indicates the "pubsub.googleapis.com" is not visible to + // "projects/123" (or it may not exist). + SERVICE_NOT_VISIBLE = 29; + + // The request is related to a project for which GCP access is suspended. + // + // Example of an ErrorInfo when the consumer "projects/123" fails to contact + // "pubsub.googleapis.com" service because GCP access is suspended: + // + // { "reason": "GCP_SUSPENDED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "pubsub.googleapis.com" + // } + // } + // + // This response indicates the associated GCP account has been suspended. + GCP_SUSPENDED = 30; + + // The request violates the location policies when creating resources in + // the restricted region. + // + // Example of an ErrorInfo when creating the Cloud Storage Bucket by + // "projects/123" for service storage.googleapis.com: + // + // { "reason": "LOCATION_POLICY_VIOLATED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "storage.googleapis.com", + // } + // } + // + // This response indicates creating the Cloud Storage Bucket in + // "locations/asia-northeast3" violates at least one location policy. + // The troubleshooting guidance is provided in the Help links. + LOCATION_POLICY_VIOLATED = 31; + + // The request is denied because origin request header is missing. + // + // Example of an ErrorInfo when + // accessing "pubsub.googleapis.com" service with an empty "Origin" request + // header. + // + // { + // reason: "MISSING_ORIGIN" + // domain: "googleapis.com" + // metadata { + // "consumer":"projects/123456" + // "service": "pubsub.googleapis.com" + // } + // } + MISSING_ORIGIN = 33; + + // The request is denied because the request contains more than one credential + // type that are individually acceptable, but not together. The customer + // should retry their request with only one set of credentials. + // + // Example of an ErrorInfo when + // accessing "pubsub.googleapis.com" service with overloaded credentials. + // + // { + // reason: "OVERLOADED_CREDENTIALS" + // domain: "googleapis.com" + // metadata { + // "consumer":"projects/123456" + // "service": "pubsub.googleapis.com" + // } + // } + OVERLOADED_CREDENTIALS = 34; + + // The request whose associated location violates the location org policy + // restrictions when creating resources in the restricted region. + // + // Example of an ErrorInfo when creating the Cloud Storage Bucket in the + // container "projects/123" under a restricted region + // "locations/asia-northeast3": + // + // { + // "reason": "LOCATION_ORG_POLICY_VIOLATED", + // "domain": "googleapis.com", + // "metadata": { + // "resource": "projects/123", + // "location": "locations/asia-northeast3" + // } + // } + // + // This response indicates creating the Cloud Storage Bucket in + // "locations/asia-northeast3" violates the location org policy restriction. + LOCATION_ORG_POLICY_VIOLATED = 35; + + // The request is denied because it access data of regulated customers using + // TLS 1.0 and 1.1. + // + // Example of an ErrorInfo when accessing a GCP resource "projects/123" that + // is restricted by TLS Version Restriction for "pubsub.googleapis.com" + // service. + // + // { + // "reason": "TLS_ORG_POLICY_VIOLATED", + // "domain": "googleapis.com", + // "metadata": { + // "service": "pubsub.googleapis.com" + // "resource": "projects/123", + // "policyName": "constraints/gcp.restrictTLSVersion", + // "tlsVersion": "TLS_VERSION_1" + // } + // } + TLS_ORG_POLICY_VIOLATED = 36; + + // The request is denied because the associated project has exceeded the + // emulator quota limit. + // + // Example of an ErrorInfo when the associated "projects/123" has exceeded the + // emulator quota limit. + // + // { + // "reason": "EMULATOR_QUOTA_EXCEEDED", + // "domain": "googleapis.com", + // "metadata": { + // "service": "pubsub.googleapis.com" + // "consumer": "projects/123" + // } + // } + EMULATOR_QUOTA_EXCEEDED = 38; + + // The request is denied because the associated application credential header + // is invalid for an Android applications. + // + // Example of an ErrorInfo when the request from an Android application to the + // "pubsub.googleapis.com" with an invalid application credential header. + // + // { + // "reason": "CREDENTIAL_ANDROID_APP_INVALID", + // "domain": "googleapis.com", + // "metadata": { + // "service": "pubsub.googleapis.com" + // } + // } + CREDENTIAL_ANDROID_APP_INVALID = 39; + + // The request is denied because IAM permission on resource is denied. + // + // Example of an ErrorInfo when the IAM permission `aiplatform.datasets.list` + // is denied on resource `projects/123`. + // + // { + // "reason": "IAM_PERMISSION_DENIED", + // "domain": "googleapis.com", + // "metadata": { + // "resource": "projects/123" + // "permission": "aiplatform.datasets.list" + // } + // } + IAM_PERMISSION_DENIED = 41; + + // The request is denied because it contains the invalid JWT token. + // + // Example of an ErrorInfo when the request contains an invalid JWT token for + // service `storage.googleapis.com`. + // + // { + // "reason": "JWT_TOKEN_INVALID", + // "domain": "googleapis.com", + // "metadata": { + // "service": "storage.googleapis.com" + // } + // } + JWT_TOKEN_INVALID = 42; + + // The request is denied because it contains credential with type that is + // unsupported. + // + // Example of an ErrorInfo when the request contains an unsupported credential + // type for service `storage.googleapis.com`. + // + // { + // "reason": "CREDENTIAL_TYPE_UNSUPPORTED", + // "domain": "googleapis.com", + // "metadata": { + // "service": "storage.googleapis.com" + // } + // } + CREDENTIAL_TYPE_UNSUPPORTED = 43; + + // The request is denied because it contains unsupported account type. + // + // Example of an ErrorInfo when the request contains an unsupported account + // type for service `storage.googleapis.com`. + // + // { + // "reason": "ACCOUNT_TYPE_UNSUPPORTED", + // "domain": "googleapis.com", + // "metadata": { + // "service": "storage.googleapis.com" + // } + // } + ACCOUNT_TYPE_UNSUPPORTED = 44; + + // The request is denied because the API endpoint is restricted by + // administrators according to the organization policy constraint. + // For more information see + // https://cloud.google.com/assured-workloads/docs/restrict-endpoint-usage. + // + // Example of an ErrorInfo when access to Google Cloud Storage service is + // restricted by Restrict Endpoint Usage policy: + // + // { + // "reason": "ENDPOINT_USAGE_RESTRICTION_VIOLATED", + // "domain": "googleapis.com/policies/endpointUsageRestriction", + // "metadata": { + // "policy_name": "constraints/gcp.restrictEndpointUsage", + // "checked_value": "storage.googleapis.com" + // "consumer": "organization/123" + // "service": "storage.googleapis.com" + // } + // } + ENDPOINT_USAGE_RESTRICTION_VIOLATED = 45; + + // The request is denied because the TLS Cipher Suite is restricted by + // administrators according to the organization policy constraint. + // For more information see + // https://cloud.google.com/assured-workloads/docs/restrict-tls-cipher-suites + // + // Example of an ErrorInfo when access to Google Cloud BigQuery service is + // restricted by Restrict TLS Cipher Suites policy: + // + // { + // "reason": "TLS_CIPHER_RESTRICTION_VIOLATED", + // "domain": "googleapis.com/policies/tlsCipherRestriction", + // "metadata": { + // "policy_name": "constraints/gcp.restrictTLSCipherSuites", + // "checked_value": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + // "consumer": "organization/123" + // "service": "bigquery.googleapis.com" + // } + // } + TLS_CIPHER_RESTRICTION_VIOLATED = 46; + + // The request is denied because the MCP activation check fails. + // + // Example of an ErrorInfo when the container "projects/123" contacting + // "pubsub.googleapis.com" service which is disabled by MCP: + // + // { "reason": "MCP_SERVER_DISABLED", + // "domain": "googleapis.com", + // "metadata": { + // "consumer": "projects/123", + // "service": "pubsub.googleapis.com" + // } + // } + // + // This response indicates the "pubsub.googleapis.com" has been disabled in + // "projects/123" for MCP. + MCP_SERVER_DISABLED = 47; +} diff --git a/venv/lib/python3.12/site-packages/google/api/error_reason_pb2.py b/venv/lib/python3.12/site-packages/google/api/error_reason_pb2.py new file mode 100644 index 0000000..f2d164d --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/error_reason_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/error_reason.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/error_reason.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n\x1dgoogle/api/error_reason.proto\x12\ngoogle.api*\x92\n\n\x0b\x45rrorReason\x12\x1c\n\x18\x45RROR_REASON_UNSPECIFIED\x10\x00\x12\x14\n\x10SERVICE_DISABLED\x10\x01\x12\x14\n\x10\x42ILLING_DISABLED\x10\x02\x12\x13\n\x0f\x41PI_KEY_INVALID\x10\x03\x12\x1b\n\x17\x41PI_KEY_SERVICE_BLOCKED\x10\x04\x12!\n\x1d\x41PI_KEY_HTTP_REFERRER_BLOCKED\x10\x07\x12\x1e\n\x1a\x41PI_KEY_IP_ADDRESS_BLOCKED\x10\x08\x12\x1f\n\x1b\x41PI_KEY_ANDROID_APP_BLOCKED\x10\t\x12\x1b\n\x17\x41PI_KEY_IOS_APP_BLOCKED\x10\r\x12\x17\n\x13RATE_LIMIT_EXCEEDED\x10\x05\x12\x1b\n\x17RESOURCE_QUOTA_EXCEEDED\x10\x06\x12 \n\x1cLOCATION_TAX_POLICY_VIOLATED\x10\n\x12\x17\n\x13USER_PROJECT_DENIED\x10\x0b\x12\x16\n\x12\x43ONSUMER_SUSPENDED\x10\x0c\x12\x14\n\x10\x43ONSUMER_INVALID\x10\x0e\x12\x1c\n\x18SECURITY_POLICY_VIOLATED\x10\x0f\x12\x18\n\x14\x41\x43\x43\x45SS_TOKEN_EXPIRED\x10\x10\x12#\n\x1f\x41\x43\x43\x45SS_TOKEN_SCOPE_INSUFFICIENT\x10\x11\x12\x19\n\x15\x41\x43\x43OUNT_STATE_INVALID\x10\x12\x12!\n\x1d\x41\x43\x43\x45SS_TOKEN_TYPE_UNSUPPORTED\x10\x13\x12\x17\n\x13\x43REDENTIALS_MISSING\x10\x14\x12\x1c\n\x18RESOURCE_PROJECT_INVALID\x10\x15\x12\x1a\n\x16SESSION_COOKIE_INVALID\x10\x17\x12\x19\n\x15USER_BLOCKED_BY_ADMIN\x10\x18\x12'\n#RESOURCE_USAGE_RESTRICTION_VIOLATED\x10\x19\x12 \n\x1cSYSTEM_PARAMETER_UNSUPPORTED\x10\x1a\x12\x1d\n\x19ORG_RESTRICTION_VIOLATION\x10\x1b\x12\"\n\x1eORG_RESTRICTION_HEADER_INVALID\x10\x1c\x12\x17\n\x13SERVICE_NOT_VISIBLE\x10\x1d\x12\x11\n\rGCP_SUSPENDED\x10\x1e\x12\x1c\n\x18LOCATION_POLICY_VIOLATED\x10\x1f\x12\x12\n\x0eMISSING_ORIGIN\x10!\x12\x1a\n\x16OVERLOADED_CREDENTIALS\x10\"\x12 \n\x1cLOCATION_ORG_POLICY_VIOLATED\x10#\x12\x1b\n\x17TLS_ORG_POLICY_VIOLATED\x10$\x12\x1b\n\x17\x45MULATOR_QUOTA_EXCEEDED\x10&\x12\"\n\x1e\x43REDENTIAL_ANDROID_APP_INVALID\x10'\x12\x19\n\x15IAM_PERMISSION_DENIED\x10)\x12\x15\n\x11JWT_TOKEN_INVALID\x10*\x12\x1f\n\x1b\x43REDENTIAL_TYPE_UNSUPPORTED\x10+\x12\x1c\n\x18\x41\x43\x43OUNT_TYPE_UNSUPPORTED\x10,\x12'\n#ENDPOINT_USAGE_RESTRICTION_VIOLATED\x10-\x12#\n\x1fTLS_CIPHER_RESTRICTION_VIOLATED\x10.\x12\x17\n\x13MCP_SERVER_DISABLED\x10/Bp\n\x0e\x63om.google.apiB\x10\x45rrorReasonProtoP\x01ZCgoogle.golang.org/genproto/googleapis/api/error_reason;error_reason\xa2\x02\x04GAPIb\x06proto3" +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.api.error_reason_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\020ErrorReasonProtoP\001ZCgoogle.golang.org/genproto/googleapis/api/error_reason;error_reason\242\002\004GAPI" + _globals["_ERRORREASON"]._serialized_start = 46 + _globals["_ERRORREASON"]._serialized_end = 1344 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/error_reason_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/error_reason_pb2.pyi new file mode 100644 index 0000000..9450370 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/error_reason_pb2.pyi @@ -0,0 +1,112 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar + +from google.protobuf import descriptor as _descriptor +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor + +class ErrorReason(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + ERROR_REASON_UNSPECIFIED: _ClassVar[ErrorReason] + SERVICE_DISABLED: _ClassVar[ErrorReason] + BILLING_DISABLED: _ClassVar[ErrorReason] + API_KEY_INVALID: _ClassVar[ErrorReason] + API_KEY_SERVICE_BLOCKED: _ClassVar[ErrorReason] + API_KEY_HTTP_REFERRER_BLOCKED: _ClassVar[ErrorReason] + API_KEY_IP_ADDRESS_BLOCKED: _ClassVar[ErrorReason] + API_KEY_ANDROID_APP_BLOCKED: _ClassVar[ErrorReason] + API_KEY_IOS_APP_BLOCKED: _ClassVar[ErrorReason] + RATE_LIMIT_EXCEEDED: _ClassVar[ErrorReason] + RESOURCE_QUOTA_EXCEEDED: _ClassVar[ErrorReason] + LOCATION_TAX_POLICY_VIOLATED: _ClassVar[ErrorReason] + USER_PROJECT_DENIED: _ClassVar[ErrorReason] + CONSUMER_SUSPENDED: _ClassVar[ErrorReason] + CONSUMER_INVALID: _ClassVar[ErrorReason] + SECURITY_POLICY_VIOLATED: _ClassVar[ErrorReason] + ACCESS_TOKEN_EXPIRED: _ClassVar[ErrorReason] + ACCESS_TOKEN_SCOPE_INSUFFICIENT: _ClassVar[ErrorReason] + ACCOUNT_STATE_INVALID: _ClassVar[ErrorReason] + ACCESS_TOKEN_TYPE_UNSUPPORTED: _ClassVar[ErrorReason] + CREDENTIALS_MISSING: _ClassVar[ErrorReason] + RESOURCE_PROJECT_INVALID: _ClassVar[ErrorReason] + SESSION_COOKIE_INVALID: _ClassVar[ErrorReason] + USER_BLOCKED_BY_ADMIN: _ClassVar[ErrorReason] + RESOURCE_USAGE_RESTRICTION_VIOLATED: _ClassVar[ErrorReason] + SYSTEM_PARAMETER_UNSUPPORTED: _ClassVar[ErrorReason] + ORG_RESTRICTION_VIOLATION: _ClassVar[ErrorReason] + ORG_RESTRICTION_HEADER_INVALID: _ClassVar[ErrorReason] + SERVICE_NOT_VISIBLE: _ClassVar[ErrorReason] + GCP_SUSPENDED: _ClassVar[ErrorReason] + LOCATION_POLICY_VIOLATED: _ClassVar[ErrorReason] + MISSING_ORIGIN: _ClassVar[ErrorReason] + OVERLOADED_CREDENTIALS: _ClassVar[ErrorReason] + LOCATION_ORG_POLICY_VIOLATED: _ClassVar[ErrorReason] + TLS_ORG_POLICY_VIOLATED: _ClassVar[ErrorReason] + EMULATOR_QUOTA_EXCEEDED: _ClassVar[ErrorReason] + CREDENTIAL_ANDROID_APP_INVALID: _ClassVar[ErrorReason] + IAM_PERMISSION_DENIED: _ClassVar[ErrorReason] + JWT_TOKEN_INVALID: _ClassVar[ErrorReason] + CREDENTIAL_TYPE_UNSUPPORTED: _ClassVar[ErrorReason] + ACCOUNT_TYPE_UNSUPPORTED: _ClassVar[ErrorReason] + ENDPOINT_USAGE_RESTRICTION_VIOLATED: _ClassVar[ErrorReason] + TLS_CIPHER_RESTRICTION_VIOLATED: _ClassVar[ErrorReason] + MCP_SERVER_DISABLED: _ClassVar[ErrorReason] + +ERROR_REASON_UNSPECIFIED: ErrorReason +SERVICE_DISABLED: ErrorReason +BILLING_DISABLED: ErrorReason +API_KEY_INVALID: ErrorReason +API_KEY_SERVICE_BLOCKED: ErrorReason +API_KEY_HTTP_REFERRER_BLOCKED: ErrorReason +API_KEY_IP_ADDRESS_BLOCKED: ErrorReason +API_KEY_ANDROID_APP_BLOCKED: ErrorReason +API_KEY_IOS_APP_BLOCKED: ErrorReason +RATE_LIMIT_EXCEEDED: ErrorReason +RESOURCE_QUOTA_EXCEEDED: ErrorReason +LOCATION_TAX_POLICY_VIOLATED: ErrorReason +USER_PROJECT_DENIED: ErrorReason +CONSUMER_SUSPENDED: ErrorReason +CONSUMER_INVALID: ErrorReason +SECURITY_POLICY_VIOLATED: ErrorReason +ACCESS_TOKEN_EXPIRED: ErrorReason +ACCESS_TOKEN_SCOPE_INSUFFICIENT: ErrorReason +ACCOUNT_STATE_INVALID: ErrorReason +ACCESS_TOKEN_TYPE_UNSUPPORTED: ErrorReason +CREDENTIALS_MISSING: ErrorReason +RESOURCE_PROJECT_INVALID: ErrorReason +SESSION_COOKIE_INVALID: ErrorReason +USER_BLOCKED_BY_ADMIN: ErrorReason +RESOURCE_USAGE_RESTRICTION_VIOLATED: ErrorReason +SYSTEM_PARAMETER_UNSUPPORTED: ErrorReason +ORG_RESTRICTION_VIOLATION: ErrorReason +ORG_RESTRICTION_HEADER_INVALID: ErrorReason +SERVICE_NOT_VISIBLE: ErrorReason +GCP_SUSPENDED: ErrorReason +LOCATION_POLICY_VIOLATED: ErrorReason +MISSING_ORIGIN: ErrorReason +OVERLOADED_CREDENTIALS: ErrorReason +LOCATION_ORG_POLICY_VIOLATED: ErrorReason +TLS_ORG_POLICY_VIOLATED: ErrorReason +EMULATOR_QUOTA_EXCEEDED: ErrorReason +CREDENTIAL_ANDROID_APP_INVALID: ErrorReason +IAM_PERMISSION_DENIED: ErrorReason +JWT_TOKEN_INVALID: ErrorReason +CREDENTIAL_TYPE_UNSUPPORTED: ErrorReason +ACCOUNT_TYPE_UNSUPPORTED: ErrorReason +ENDPOINT_USAGE_RESTRICTION_VIOLATED: ErrorReason +TLS_CIPHER_RESTRICTION_VIOLATED: ErrorReason +MCP_SERVER_DISABLED: ErrorReason diff --git a/venv/lib/python3.12/site-packages/google/api/field_behavior.proto b/venv/lib/python3.12/site-packages/google/api/field_behavior.proto new file mode 100644 index 0000000..861f825 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/field_behavior.proto @@ -0,0 +1,104 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "FieldBehaviorProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.FieldOptions { + // A designation of a specific field behavior (required, output only, etc.) + // in protobuf messages. + // + // Examples: + // + // string name = 1 [(google.api.field_behavior) = REQUIRED]; + // State state = 1 [(google.api.field_behavior) = OUTPUT_ONLY]; + // google.protobuf.Duration ttl = 1 + // [(google.api.field_behavior) = INPUT_ONLY]; + // google.protobuf.Timestamp expire_time = 1 + // [(google.api.field_behavior) = OUTPUT_ONLY, + // (google.api.field_behavior) = IMMUTABLE]; + repeated google.api.FieldBehavior field_behavior = 1052 [packed = false]; +} + +// An indicator of the behavior of a given field (for example, that a field +// is required in requests, or given as output but ignored as input). +// This **does not** change the behavior in protocol buffers itself; it only +// denotes the behavior and may affect how API tooling handles the field. +// +// Note: This enum **may** receive new values in the future. +enum FieldBehavior { + // Conventional default for enums. Do not use this. + FIELD_BEHAVIOR_UNSPECIFIED = 0; + + // Specifically denotes a field as optional. + // While all fields in protocol buffers are optional, this may be specified + // for emphasis if appropriate. + OPTIONAL = 1; + + // Denotes a field as required. + // This indicates that the field **must** be provided as part of the request, + // and failure to do so will cause an error (usually `INVALID_ARGUMENT`). + REQUIRED = 2; + + // Denotes a field as output only. + // This indicates that the field is provided in responses, but including the + // field in a request does nothing (the server *must* ignore it and + // *must not* throw an error as a result of the field's presence). + OUTPUT_ONLY = 3; + + // Denotes a field as input only. + // This indicates that the field is provided in requests, and the + // corresponding field is not included in output. + INPUT_ONLY = 4; + + // Denotes a field as immutable. + // This indicates that the field may be set once in a request to create a + // resource, but may not be changed thereafter. + IMMUTABLE = 5; + + // Denotes that a (repeated) field is an unordered list. + // This indicates that the service may provide the elements of the list + // in any arbitrary order, rather than the order the user originally + // provided. Additionally, the list's order may or may not be stable. + UNORDERED_LIST = 6; + + // Denotes that this field returns a non-empty default value if not set. + // This indicates that if the user provides the empty value in a request, + // a non-empty value will be returned. The user will not be aware of what + // non-empty value to expect. + NON_EMPTY_DEFAULT = 7; + + // Denotes that the field in a resource (a message annotated with + // google.api.resource) is used in the resource name to uniquely identify the + // resource. For AIP-compliant APIs, this should only be applied to the + // `name` field on the resource. + // + // This behavior should not be applied to references to other resources within + // the message. + // + // The identifier field of resources often have different field behavior + // depending on the request it is embedded in (e.g. for Create methods name + // is optional and unused, while for Update methods it is required). Instead + // of method-specific annotations, only `IDENTIFIER` is required. + IDENTIFIER = 8; +} diff --git a/venv/lib/python3.12/site-packages/google/api/field_behavior_pb2.py b/venv/lib/python3.12/site-packages/google/api/field_behavior_pb2.py new file mode 100644 index 0000000..2afc7b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/field_behavior_pb2.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/field_behavior.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/field_behavior.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n\x1fgoogle/api/field_behavior.proto\x12\ngoogle.api\x1a google/protobuf/descriptor.proto*\xb6\x01\n\rFieldBehavior\x12\x1e\n\x1a\x46IELD_BEHAVIOR_UNSPECIFIED\x10\x00\x12\x0c\n\x08OPTIONAL\x10\x01\x12\x0c\n\x08REQUIRED\x10\x02\x12\x0f\n\x0bOUTPUT_ONLY\x10\x03\x12\x0e\n\nINPUT_ONLY\x10\x04\x12\r\n\tIMMUTABLE\x10\x05\x12\x12\n\x0eUNORDERED_LIST\x10\x06\x12\x15\n\x11NON_EMPTY_DEFAULT\x10\x07\x12\x0e\n\nIDENTIFIER\x10\x08:U\n\x0e\x66ield_behavior\x12\x1d.google.protobuf.FieldOptions\x18\x9c\x08 \x03(\x0e\x32\x19.google.api.FieldBehaviorB\x02\x10\x00\x42p\n\x0e\x63om.google.apiB\x12\x46ieldBehaviorProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3" +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.api.field_behavior_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\022FieldBehaviorProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI" + _globals["field_behavior"]._loaded_options = None + _globals["field_behavior"]._serialized_options = b"\020\000" + _globals["_FIELDBEHAVIOR"]._serialized_start = 82 + _globals["_FIELDBEHAVIOR"]._serialized_end = 264 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/field_behavior_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/field_behavior_pb2.pyi new file mode 100644 index 0000000..c67ed03 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/field_behavior_pb2.pyi @@ -0,0 +1,45 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pb2 as _descriptor_pb2 +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor + +class FieldBehavior(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + FIELD_BEHAVIOR_UNSPECIFIED: _ClassVar[FieldBehavior] + OPTIONAL: _ClassVar[FieldBehavior] + REQUIRED: _ClassVar[FieldBehavior] + OUTPUT_ONLY: _ClassVar[FieldBehavior] + INPUT_ONLY: _ClassVar[FieldBehavior] + IMMUTABLE: _ClassVar[FieldBehavior] + UNORDERED_LIST: _ClassVar[FieldBehavior] + NON_EMPTY_DEFAULT: _ClassVar[FieldBehavior] + IDENTIFIER: _ClassVar[FieldBehavior] + +FIELD_BEHAVIOR_UNSPECIFIED: FieldBehavior +OPTIONAL: FieldBehavior +REQUIRED: FieldBehavior +OUTPUT_ONLY: FieldBehavior +INPUT_ONLY: FieldBehavior +IMMUTABLE: FieldBehavior +UNORDERED_LIST: FieldBehavior +NON_EMPTY_DEFAULT: FieldBehavior +IDENTIFIER: FieldBehavior +FIELD_BEHAVIOR_FIELD_NUMBER: _ClassVar[int] +field_behavior: _descriptor.FieldDescriptor diff --git a/venv/lib/python3.12/site-packages/google/api/field_info.proto b/venv/lib/python3.12/site-packages/google/api/field_info.proto new file mode 100644 index 0000000..64387fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/field_info.proto @@ -0,0 +1,106 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "FieldInfoProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.FieldOptions { + // Rich semantic descriptor of an API field beyond the basic typing. + // + // Examples: + // + // string request_id = 1 [(google.api.field_info).format = UUID4]; + // string old_ip_address = 2 [(google.api.field_info).format = IPV4]; + // string new_ip_address = 3 [(google.api.field_info).format = IPV6]; + // string actual_ip_address = 4 [ + // (google.api.field_info).format = IPV4_OR_IPV6 + // ]; + // google.protobuf.Any generic_field = 5 [ + // (google.api.field_info).referenced_types = {type_name: "ActualType"}, + // (google.api.field_info).referenced_types = {type_name: "OtherType"}, + // ]; + // google.protobuf.Any generic_user_input = 5 [ + // (google.api.field_info).referenced_types = {type_name: "*"}, + // ]; + google.api.FieldInfo field_info = 291403980; +} + +// Rich semantic information of an API field beyond basic typing. +message FieldInfo { + // The standard format of a field value. The supported formats are all backed + // by either an RFC defined by the IETF or a Google-defined AIP. + enum Format { + // Default, unspecified value. + FORMAT_UNSPECIFIED = 0; + + // Universally Unique Identifier, version 4, value as defined by + // https://datatracker.ietf.org/doc/html/rfc4122. The value may be + // normalized to entirely lowercase letters. For example, the value + // `F47AC10B-58CC-0372-8567-0E02B2C3D479` would be normalized to + // `f47ac10b-58cc-0372-8567-0e02b2c3d479`. + UUID4 = 1; + + // Internet Protocol v4 value as defined by [RFC + // 791](https://datatracker.ietf.org/doc/html/rfc791). The value may be + // condensed, with leading zeros in each octet stripped. For example, + // `001.022.233.040` would be condensed to `1.22.233.40`. + IPV4 = 2; + + // Internet Protocol v6 value as defined by [RFC + // 2460](https://datatracker.ietf.org/doc/html/rfc2460). The value may be + // normalized to entirely lowercase letters with zeros compressed, following + // [RFC 5952](https://datatracker.ietf.org/doc/html/rfc5952). For example, + // the value `2001:0DB8:0::0` would be normalized to `2001:db8::`. + IPV6 = 3; + + // An IP address in either v4 or v6 format as described by the individual + // values defined herein. See the comments on the IPV4 and IPV6 types for + // allowed normalizations of each. + IPV4_OR_IPV6 = 4; + } + + // The standard format of a field value. This does not explicitly configure + // any API consumer, just documents the API's format for the field it is + // applied to. + Format format = 1; + + // The type(s) that the annotated, generic field may represent. + // + // Currently, this must only be used on fields of type `google.protobuf.Any`. + // Supporting other generic types may be considered in the future. + repeated TypeReference referenced_types = 2; +} + +// A reference to a message type, for use in [FieldInfo][google.api.FieldInfo]. +message TypeReference { + // The name of the type that the annotated, generic field may represent. + // If the type is in the same protobuf package, the value can be the simple + // message name e.g., `"MyMessage"`. Otherwise, the value must be the + // fully-qualified message name e.g., `"google.library.v1.Book"`. + // + // If the type(s) are unknown to the service (e.g. the field accepts generic + // user input), use the wildcard `"*"` to denote this behavior. + // + // See [AIP-202](https://google.aip.dev/202#type-references) for more details. + string type_name = 1; +} diff --git a/venv/lib/python3.12/site-packages/google/api/field_info_pb2.py b/venv/lib/python3.12/site-packages/google/api/field_info_pb2.py new file mode 100644 index 0000000..2afa06a --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/field_info_pb2.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/field_info.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/field_info.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1bgoogle/api/field_info.proto\x12\ngoogle.api\x1a google/protobuf/descriptor.proto"\xc1\x01\n\tFieldInfo\x12,\n\x06\x66ormat\x18\x01 \x01(\x0e\x32\x1c.google.api.FieldInfo.Format\x12\x33\n\x10referenced_types\x18\x02 \x03(\x0b\x32\x19.google.api.TypeReference"Q\n\x06\x46ormat\x12\x16\n\x12\x46ORMAT_UNSPECIFIED\x10\x00\x12\t\n\x05UUID4\x10\x01\x12\x08\n\x04IPV4\x10\x02\x12\x08\n\x04IPV6\x10\x03\x12\x10\n\x0cIPV4_OR_IPV6\x10\x04""\n\rTypeReference\x12\x11\n\ttype_name\x18\x01 \x01(\t:L\n\nfield_info\x12\x1d.google.protobuf.FieldOptions\x18\xcc\xf1\xf9\x8a\x01 \x01(\x0b\x32\x15.google.api.FieldInfoBl\n\x0e\x63om.google.apiB\x0e\x46ieldInfoProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.api.field_info_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\016FieldInfoProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI" + _globals["_FIELDINFO"]._serialized_start = 78 + _globals["_FIELDINFO"]._serialized_end = 271 + _globals["_FIELDINFO_FORMAT"]._serialized_start = 190 + _globals["_FIELDINFO_FORMAT"]._serialized_end = 271 + _globals["_TYPEREFERENCE"]._serialized_start = 273 + _globals["_TYPEREFERENCE"]._serialized_end = 307 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/field_info_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/field_info_pb2.pyi new file mode 100644 index 0000000..4ccc474 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/field_info_pb2.pyi @@ -0,0 +1,60 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pb2 as _descriptor_pb2 +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor +FIELD_INFO_FIELD_NUMBER: _ClassVar[int] +field_info: _descriptor.FieldDescriptor + +class FieldInfo(_message.Message): + __slots__ = ("format", "referenced_types") + class Format(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + FORMAT_UNSPECIFIED: _ClassVar[FieldInfo.Format] + UUID4: _ClassVar[FieldInfo.Format] + IPV4: _ClassVar[FieldInfo.Format] + IPV6: _ClassVar[FieldInfo.Format] + IPV4_OR_IPV6: _ClassVar[FieldInfo.Format] + + FORMAT_UNSPECIFIED: FieldInfo.Format + UUID4: FieldInfo.Format + IPV4: FieldInfo.Format + IPV6: FieldInfo.Format + IPV4_OR_IPV6: FieldInfo.Format + FORMAT_FIELD_NUMBER: _ClassVar[int] + REFERENCED_TYPES_FIELD_NUMBER: _ClassVar[int] + format: FieldInfo.Format + referenced_types: _containers.RepeatedCompositeFieldContainer[TypeReference] + def __init__( + self, + format: _Optional[_Union[FieldInfo.Format, str]] = ..., + referenced_types: _Optional[_Iterable[_Union[TypeReference, _Mapping]]] = ..., + ) -> None: ... + +class TypeReference(_message.Message): + __slots__ = ("type_name",) + TYPE_NAME_FIELD_NUMBER: _ClassVar[int] + type_name: str + def __init__(self, type_name: _Optional[str] = ...) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/http.proto b/venv/lib/python3.12/site-packages/google/api/http.proto new file mode 100644 index 0000000..bb3af8e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/http.proto @@ -0,0 +1,370 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// - HTTP: `GET /v1/messages/123456` +// - gRPC: `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// - HTTP: `GET /v1/messages/123456?revision=2&sub.subfield=foo` +// - gRPC: `GetMessage(message_id: "123456" revision: 2 sub: +// SubMessage(subfield: "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }` +// - gRPC: `UpdateMessage(message_id: "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }` +// - gRPC: `UpdateMessage(message_id: "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// - HTTP: `GET /v1/messages/123456` +// - gRPC: `GetMessage(message_id: "123456")` +// +// - HTTP: `GET /v1/users/me/messages/123456` +// - gRPC: `GetMessage(user_id: "me" message_id: "123456")` +// +// Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They +// are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL +// query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP +// request body, all +// fields are passed via URL path and URL query parameters. +// +// Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// The following example selects a gRPC method and applies an `HttpRule` to it: +// +// http: +// rules: +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/api/http_pb2.py b/venv/lib/python3.12/site-packages/google/api/http_pb2.py new file mode 100644 index 0000000..c5740b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/http_pb2.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/http.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/http.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x15google/api/http.proto\x12\ngoogle.api"T\n\x04Http\x12#\n\x05rules\x18\x01 \x03(\x0b\x32\x14.google.api.HttpRule\x12\'\n\x1f\x66ully_decode_reserved_expansion\x18\x02 \x01(\x08"\x81\x02\n\x08HttpRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12\r\n\x03get\x18\x02 \x01(\tH\x00\x12\r\n\x03put\x18\x03 \x01(\tH\x00\x12\x0e\n\x04post\x18\x04 \x01(\tH\x00\x12\x10\n\x06\x64\x65lete\x18\x05 \x01(\tH\x00\x12\x0f\n\x05patch\x18\x06 \x01(\tH\x00\x12/\n\x06\x63ustom\x18\x08 \x01(\x0b\x32\x1d.google.api.CustomHttpPatternH\x00\x12\x0c\n\x04\x62ody\x18\x07 \x01(\t\x12\x15\n\rresponse_body\x18\x0c \x01(\t\x12\x31\n\x13\x61\x64\x64itional_bindings\x18\x0b \x03(\x0b\x32\x14.google.api.HttpRuleB\t\n\x07pattern"/\n\x11\x43ustomHttpPattern\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x01(\tBg\n\x0e\x63om.google.apiB\tHttpProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.http_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\tHttpProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI" + _globals["_HTTP"]._serialized_start = 37 + _globals["_HTTP"]._serialized_end = 121 + _globals["_HTTPRULE"]._serialized_start = 124 + _globals["_HTTPRULE"]._serialized_end = 381 + _globals["_CUSTOMHTTPPATTERN"]._serialized_start = 383 + _globals["_CUSTOMHTTPPATTERN"]._serialized_end = 430 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/http_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/http_pb2.pyi new file mode 100644 index 0000000..2dafa8f --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/http_pb2.pyi @@ -0,0 +1,94 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class Http(_message.Message): + __slots__ = ("rules", "fully_decode_reserved_expansion") + RULES_FIELD_NUMBER: _ClassVar[int] + FULLY_DECODE_RESERVED_EXPANSION_FIELD_NUMBER: _ClassVar[int] + rules: _containers.RepeatedCompositeFieldContainer[HttpRule] + fully_decode_reserved_expansion: bool + def __init__( + self, + rules: _Optional[_Iterable[_Union[HttpRule, _Mapping]]] = ..., + fully_decode_reserved_expansion: _Optional[bool] = ..., + ) -> None: ... + +class HttpRule(_message.Message): + __slots__ = ( + "selector", + "get", + "put", + "post", + "delete", + "patch", + "custom", + "body", + "response_body", + "additional_bindings", + ) + SELECTOR_FIELD_NUMBER: _ClassVar[int] + GET_FIELD_NUMBER: _ClassVar[int] + PUT_FIELD_NUMBER: _ClassVar[int] + POST_FIELD_NUMBER: _ClassVar[int] + DELETE_FIELD_NUMBER: _ClassVar[int] + PATCH_FIELD_NUMBER: _ClassVar[int] + CUSTOM_FIELD_NUMBER: _ClassVar[int] + BODY_FIELD_NUMBER: _ClassVar[int] + RESPONSE_BODY_FIELD_NUMBER: _ClassVar[int] + ADDITIONAL_BINDINGS_FIELD_NUMBER: _ClassVar[int] + selector: str + get: str + put: str + post: str + delete: str + patch: str + custom: CustomHttpPattern + body: str + response_body: str + additional_bindings: _containers.RepeatedCompositeFieldContainer[HttpRule] + def __init__( + self, + selector: _Optional[str] = ..., + get: _Optional[str] = ..., + put: _Optional[str] = ..., + post: _Optional[str] = ..., + delete: _Optional[str] = ..., + patch: _Optional[str] = ..., + custom: _Optional[_Union[CustomHttpPattern, _Mapping]] = ..., + body: _Optional[str] = ..., + response_body: _Optional[str] = ..., + additional_bindings: _Optional[_Iterable[_Union[HttpRule, _Mapping]]] = ..., + ) -> None: ... + +class CustomHttpPattern(_message.Message): + __slots__ = ("kind", "path") + KIND_FIELD_NUMBER: _ClassVar[int] + PATH_FIELD_NUMBER: _ClassVar[int] + kind: str + path: str + def __init__( + self, kind: _Optional[str] = ..., path: _Optional[str] = ... + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/httpbody.proto b/venv/lib/python3.12/site-packages/google/api/httpbody.proto new file mode 100644 index 0000000..3fd226a --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/httpbody.proto @@ -0,0 +1,80 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/any.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody"; +option java_multiple_files = true; +option java_outer_classname = "HttpBodyProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Message that represents an arbitrary HTTP body. It should only be used for +// payload formats that can't be represented as JSON, such as raw binary or +// an HTML page. +// +// +// This message can be used both in streaming and non-streaming API methods in +// the request as well as the response. +// +// It can be used as a top-level request field, which is convenient if one +// wants to extract parameters from either the URL or HTTP template into the +// request fields and also want access to the raw HTTP body. +// +// Example: +// +// message GetResourceRequest { +// // A unique request id. +// string request_id = 1; +// +// // The raw HTTP body is bound to this field. +// google.api.HttpBody http_body = 2; +// +// } +// +// service ResourceService { +// rpc GetResource(GetResourceRequest) +// returns (google.api.HttpBody); +// rpc UpdateResource(google.api.HttpBody) +// returns (google.protobuf.Empty); +// +// } +// +// Example with streaming methods: +// +// service CaldavService { +// rpc GetCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// rpc UpdateCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// +// } +// +// Use of this type only changes how the request and response bodies are +// handled, all other features will continue to work unchanged. +message HttpBody { + // The HTTP Content-Type header value specifying the content type of the body. + string content_type = 1; + + // The HTTP request/response body as raw binary. + bytes data = 2; + + // Application specific response metadata. Must be set in the first response + // for streaming APIs. + repeated google.protobuf.Any extensions = 3; +} diff --git a/venv/lib/python3.12/site-packages/google/api/httpbody_pb2.py b/venv/lib/python3.12/site-packages/google/api/httpbody_pb2.py new file mode 100644 index 0000000..fa13b38 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/httpbody_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/httpbody.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/httpbody.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x19google/api/httpbody.proto\x12\ngoogle.api\x1a\x19google/protobuf/any.proto"X\n\x08HttpBody\x12\x14\n\x0c\x63ontent_type\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12(\n\nextensions\x18\x03 \x03(\x0b\x32\x14.google.protobuf.AnyBe\n\x0e\x63om.google.apiB\rHttpBodyProtoP\x01Z;google.golang.org/genproto/googleapis/api/httpbody;httpbody\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.httpbody_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\rHttpBodyProtoP\001Z;google.golang.org/genproto/googleapis/api/httpbody;httpbody\242\002\004GAPI" + _globals["_HTTPBODY"]._serialized_start = 68 + _globals["_HTTPBODY"]._serialized_end = 156 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/httpbody_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/httpbody_pb2.pyi new file mode 100644 index 0000000..eed5062 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/httpbody_pb2.pyi @@ -0,0 +1,41 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import any_pb2 as _any_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class HttpBody(_message.Message): + __slots__ = ("content_type", "data", "extensions") + CONTENT_TYPE_FIELD_NUMBER: _ClassVar[int] + DATA_FIELD_NUMBER: _ClassVar[int] + EXTENSIONS_FIELD_NUMBER: _ClassVar[int] + content_type: str + data: bytes + extensions: _containers.RepeatedCompositeFieldContainer[_any_pb2.Any] + def __init__( + self, + content_type: _Optional[str] = ..., + data: _Optional[bytes] = ..., + extensions: _Optional[_Iterable[_Union[_any_pb2.Any, _Mapping]]] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/label.proto b/venv/lib/python3.12/site-packages/google/api/label.proto new file mode 100644 index 0000000..5d77c70 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/label.proto @@ -0,0 +1,47 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/label;label"; +option java_multiple_files = true; +option java_outer_classname = "LabelProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// A description of a label. +message LabelDescriptor { + // Value types that can be used as label values. + enum ValueType { + // A variable-length string. This is the default. + STRING = 0; + + // Boolean; true or false. + BOOL = 1; + + // A 64-bit signed integer. + INT64 = 2; + } + + // The label key. + string key = 1; + + // The type of data that can be assigned to the label. + ValueType value_type = 2; + + // A human-readable description for the label. + string description = 3; +} diff --git a/venv/lib/python3.12/site-packages/google/api/label_pb2.py b/venv/lib/python3.12/site-packages/google/api/label_pb2.py new file mode 100644 index 0000000..4d5d5c6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/label_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/label.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/label.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x16google/api/label.proto\x12\ngoogle.api"\x9c\x01\n\x0fLabelDescriptor\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x39\n\nvalue_type\x18\x02 \x01(\x0e\x32%.google.api.LabelDescriptor.ValueType\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t",\n\tValueType\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x42OOL\x10\x01\x12\t\n\x05INT64\x10\x02\x42\\\n\x0e\x63om.google.apiB\nLabelProtoP\x01Z5google.golang.org/genproto/googleapis/api/label;label\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.label_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\nLabelProtoP\001Z5google.golang.org/genproto/googleapis/api/label;label\242\002\004GAPI" + _globals["_LABELDESCRIPTOR"]._serialized_start = 39 + _globals["_LABELDESCRIPTOR"]._serialized_end = 195 + _globals["_LABELDESCRIPTOR_VALUETYPE"]._serialized_start = 151 + _globals["_LABELDESCRIPTOR_VALUETYPE"]._serialized_end = 195 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/label_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/label_pb2.pyi new file mode 100644 index 0000000..ca9a0b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/label_pb2.pyi @@ -0,0 +1,47 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor + +class LabelDescriptor(_message.Message): + __slots__ = ("key", "value_type", "description") + class ValueType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + STRING: _ClassVar[LabelDescriptor.ValueType] + BOOL: _ClassVar[LabelDescriptor.ValueType] + INT64: _ClassVar[LabelDescriptor.ValueType] + + STRING: LabelDescriptor.ValueType + BOOL: LabelDescriptor.ValueType + INT64: LabelDescriptor.ValueType + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_TYPE_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + key: str + value_type: LabelDescriptor.ValueType + description: str + def __init__( + self, + key: _Optional[str] = ..., + value_type: _Optional[_Union[LabelDescriptor.ValueType, str]] = ..., + description: _Optional[str] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/launch_stage.proto b/venv/lib/python3.12/site-packages/google/api/launch_stage.proto new file mode 100644 index 0000000..0fff247 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/launch_stage.proto @@ -0,0 +1,72 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api;api"; +option java_multiple_files = true; +option java_outer_classname = "LaunchStageProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// The launch stage as defined by [Google Cloud Platform +// Launch Stages](https://cloud.google.com/terms/launch-stages). +enum LaunchStage { + // Do not use this default value. + LAUNCH_STAGE_UNSPECIFIED = 0; + + // The feature is not yet implemented. Users can not use it. + UNIMPLEMENTED = 6; + + // Prelaunch features are hidden from users and are only visible internally. + PRELAUNCH = 7; + + // Early Access features are limited to a closed group of testers. To use + // these features, you must sign up in advance and sign a Trusted Tester + // agreement (which includes confidentiality provisions). These features may + // be unstable, changed in backward-incompatible ways, and are not + // guaranteed to be released. + EARLY_ACCESS = 1; + + // Alpha is a limited availability test for releases before they are cleared + // for widespread use. By Alpha, all significant design issues are resolved + // and we are in the process of verifying functionality. Alpha customers + // need to apply for access, agree to applicable terms, and have their + // projects allowlisted. Alpha releases don't have to be feature complete, + // no SLAs are provided, and there are no technical support obligations, but + // they will be far enough along that customers can actually use them in + // test environments or for limited-use tests -- just like they would in + // normal production cases. + ALPHA = 2; + + // Beta is the point at which we are ready to open a release for any + // customer to use. There are no SLA or technical support obligations in a + // Beta release. Products will be complete from a feature perspective, but + // may have some open outstanding issues. Beta releases are suitable for + // limited production use cases. + BETA = 3; + + // GA features are open to all developers and are considered stable and + // fully qualified for production use. + GA = 4; + + // Deprecated features are scheduled to be shut down and removed. For more + // information, see the "Deprecation Policy" section of our [Terms of + // Service](https://cloud.google.com/terms/) + // and the [Google Cloud Platform Subject to the Deprecation + // Policy](https://cloud.google.com/terms/deprecation) documentation. + DEPRECATED = 5; +} diff --git a/venv/lib/python3.12/site-packages/google/api/launch_stage_pb2.py b/venv/lib/python3.12/site-packages/google/api/launch_stage_pb2.py new file mode 100644 index 0000000..c756f2a --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/launch_stage_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/launch_stage.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/launch_stage.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n\x1dgoogle/api/launch_stage.proto\x12\ngoogle.api*\x8c\x01\n\x0bLaunchStage\x12\x1c\n\x18LAUNCH_STAGE_UNSPECIFIED\x10\x00\x12\x11\n\rUNIMPLEMENTED\x10\x06\x12\r\n\tPRELAUNCH\x10\x07\x12\x10\n\x0c\x45\x41RLY_ACCESS\x10\x01\x12\t\n\x05\x41LPHA\x10\x02\x12\x08\n\x04\x42\x45TA\x10\x03\x12\x06\n\x02GA\x10\x04\x12\x0e\n\nDEPRECATED\x10\x05\x42Z\n\x0e\x63om.google.apiB\x10LaunchStageProtoP\x01Z-google.golang.org/genproto/googleapis/api;api\xa2\x02\x04GAPIb\x06proto3" +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.api.launch_stage_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\020LaunchStageProtoP\001Z-google.golang.org/genproto/googleapis/api;api\242\002\004GAPI" + _globals["_LAUNCHSTAGE"]._serialized_start = 46 + _globals["_LAUNCHSTAGE"]._serialized_end = 186 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/launch_stage_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/launch_stage_pb2.pyi new file mode 100644 index 0000000..bdbe119 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/launch_stage_pb2.pyi @@ -0,0 +1,40 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar + +from google.protobuf import descriptor as _descriptor +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor + +class LaunchStage(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + LAUNCH_STAGE_UNSPECIFIED: _ClassVar[LaunchStage] + UNIMPLEMENTED: _ClassVar[LaunchStage] + PRELAUNCH: _ClassVar[LaunchStage] + EARLY_ACCESS: _ClassVar[LaunchStage] + ALPHA: _ClassVar[LaunchStage] + BETA: _ClassVar[LaunchStage] + GA: _ClassVar[LaunchStage] + DEPRECATED: _ClassVar[LaunchStage] + +LAUNCH_STAGE_UNSPECIFIED: LaunchStage +UNIMPLEMENTED: LaunchStage +PRELAUNCH: LaunchStage +EARLY_ACCESS: LaunchStage +ALPHA: LaunchStage +BETA: LaunchStage +GA: LaunchStage +DEPRECATED: LaunchStage diff --git a/venv/lib/python3.12/site-packages/google/api/log.proto b/venv/lib/python3.12/site-packages/google/api/log.proto new file mode 100644 index 0000000..2f13e72 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/log.proto @@ -0,0 +1,54 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/label.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "LogProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// A description of a log type. Example in YAML format: +// +// - name: library.googleapis.com/activity_history +// description: The history of borrowing and returning library items. +// display_name: Activity +// labels: +// - key: /customer_id +// description: Identifier of a library customer +message LogDescriptor { + // The name of the log. It must be less than 512 characters long and can + // include the following characters: upper- and lower-case alphanumeric + // characters [A-Za-z0-9], and punctuation characters including + // slash, underscore, hyphen, period [/_-.]. + string name = 1; + + // The set of labels that are available to describe a specific log entry. + // Runtime requests that contain labels not specified here are + // considered invalid. + repeated LabelDescriptor labels = 2; + + // A human-readable description of this log. This information appears in + // the documentation and can contain details. + string description = 3; + + // The human-readable name for this log. This information appears on + // the user interface and should be concise. + string display_name = 4; +} diff --git a/venv/lib/python3.12/site-packages/google/api/log_pb2.py b/venv/lib/python3.12/site-packages/google/api/log_pb2.py new file mode 100644 index 0000000..37d4353 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/log_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/log.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/log.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.api import label_pb2 as google_dot_api_dot_label__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x14google/api/log.proto\x12\ngoogle.api\x1a\x16google/api/label.proto"u\n\rLogDescriptor\x12\x0c\n\x04name\x18\x01 \x01(\t\x12+\n\x06labels\x18\x02 \x03(\x0b\x32\x1b.google.api.LabelDescriptor\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x04 \x01(\tBj\n\x0e\x63om.google.apiB\x08LogProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.log_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\010LogProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_LOGDESCRIPTOR"]._serialized_start = 60 + _globals["_LOGDESCRIPTOR"]._serialized_end = 177 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/log_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/log_pb2.pyi new file mode 100644 index 0000000..76948ca --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/log_pb2.pyi @@ -0,0 +1,47 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +from google.api import label_pb2 as _label_pb2 + +DESCRIPTOR: _descriptor.FileDescriptor + +class LogDescriptor(_message.Message): + __slots__ = ("name", "labels", "description", "display_name") + NAME_FIELD_NUMBER: _ClassVar[int] + LABELS_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + DISPLAY_NAME_FIELD_NUMBER: _ClassVar[int] + name: str + labels: _containers.RepeatedCompositeFieldContainer[_label_pb2.LabelDescriptor] + description: str + display_name: str + def __init__( + self, + name: _Optional[str] = ..., + labels: _Optional[ + _Iterable[_Union[_label_pb2.LabelDescriptor, _Mapping]] + ] = ..., + description: _Optional[str] = ..., + display_name: _Optional[str] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/logging.proto b/venv/lib/python3.12/site-packages/google/api/logging.proto new file mode 100644 index 0000000..7a82de9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/logging.proto @@ -0,0 +1,81 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "LoggingProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Logging configuration of the service. +// +// The following example shows how to configure logs to be sent to the +// producer and consumer projects. In the example, the `activity_history` +// log is sent to both the producer and consumer projects, whereas the +// `purchase_history` log is only sent to the producer project. +// +// monitored_resources: +// - type: library.googleapis.com/branch +// labels: +// - key: /city +// description: The city where the library branch is located in. +// - key: /name +// description: The name of the branch. +// logs: +// - name: activity_history +// labels: +// - key: /customer_id +// - name: purchase_history +// logging: +// producer_destinations: +// - monitored_resource: library.googleapis.com/branch +// logs: +// - activity_history +// - purchase_history +// consumer_destinations: +// - monitored_resource: library.googleapis.com/branch +// logs: +// - activity_history +message Logging { + // Configuration of a specific logging destination (the producer project + // or the consumer project). + message LoggingDestination { + // The monitored resource type. The type must be defined in the + // [Service.monitored_resources][google.api.Service.monitored_resources] + // section. + string monitored_resource = 3; + + // Names of the logs to be sent to this destination. Each name must + // be defined in the [Service.logs][google.api.Service.logs] section. If the + // log name is not a domain scoped name, it will be automatically prefixed + // with the service name followed by "/". + repeated string logs = 1; + } + + // Logging configurations for sending logs to the producer project. + // There can be multiple producer destinations, each one must have a + // different monitored resource type. A log can be used in at most + // one producer destination. + repeated LoggingDestination producer_destinations = 1; + + // Logging configurations for sending logs to the consumer project. + // There can be multiple consumer destinations, each one must have a + // different monitored resource type. A log can be used in at most + // one consumer destination. + repeated LoggingDestination consumer_destinations = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/api/logging_pb2.py b/venv/lib/python3.12/site-packages/google/api/logging_pb2.py new file mode 100644 index 0000000..9526e78 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/logging_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/logging.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/logging.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x18google/api/logging.proto\x12\ngoogle.api"\xd7\x01\n\x07Logging\x12\x45\n\x15producer_destinations\x18\x01 \x03(\x0b\x32&.google.api.Logging.LoggingDestination\x12\x45\n\x15\x63onsumer_destinations\x18\x02 \x03(\x0b\x32&.google.api.Logging.LoggingDestination\x1a>\n\x12LoggingDestination\x12\x1a\n\x12monitored_resource\x18\x03 \x01(\t\x12\x0c\n\x04logs\x18\x01 \x03(\tBn\n\x0e\x63om.google.apiB\x0cLoggingProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.logging_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\014LoggingProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_LOGGING"]._serialized_start = 41 + _globals["_LOGGING"]._serialized_end = 256 + _globals["_LOGGING_LOGGINGDESTINATION"]._serialized_start = 194 + _globals["_LOGGING_LOGGINGDESTINATION"]._serialized_end = 256 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/logging_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/logging_pb2.pyi new file mode 100644 index 0000000..aa29c85 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/logging_pb2.pyi @@ -0,0 +1,57 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class Logging(_message.Message): + __slots__ = ("producer_destinations", "consumer_destinations") + class LoggingDestination(_message.Message): + __slots__ = ("monitored_resource", "logs") + MONITORED_RESOURCE_FIELD_NUMBER: _ClassVar[int] + LOGS_FIELD_NUMBER: _ClassVar[int] + monitored_resource: str + logs: _containers.RepeatedScalarFieldContainer[str] + def __init__( + self, + monitored_resource: _Optional[str] = ..., + logs: _Optional[_Iterable[str]] = ..., + ) -> None: ... + + PRODUCER_DESTINATIONS_FIELD_NUMBER: _ClassVar[int] + CONSUMER_DESTINATIONS_FIELD_NUMBER: _ClassVar[int] + producer_destinations: _containers.RepeatedCompositeFieldContainer[ + Logging.LoggingDestination + ] + consumer_destinations: _containers.RepeatedCompositeFieldContainer[ + Logging.LoggingDestination + ] + def __init__( + self, + producer_destinations: _Optional[ + _Iterable[_Union[Logging.LoggingDestination, _Mapping]] + ] = ..., + consumer_destinations: _Optional[ + _Iterable[_Union[Logging.LoggingDestination, _Mapping]] + ] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/metric.proto b/venv/lib/python3.12/site-packages/google/api/metric.proto new file mode 100644 index 0000000..b7fb82a --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/metric.proto @@ -0,0 +1,287 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/label.proto"; +import "google/api/launch_stage.proto"; +import "google/protobuf/duration.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/metric;metric"; +option java_multiple_files = true; +option java_outer_classname = "MetricProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines a metric type and its schema. Once a metric descriptor is created, +// deleting or altering it stops data collection and makes the metric type's +// existing data unusable. +// +message MetricDescriptor { + // The kind of measurement. It describes how the data is reported. + // For information on setting the start time and end time based on + // the MetricKind, see [TimeInterval][google.monitoring.v3.TimeInterval]. + enum MetricKind { + // Do not use this default value. + METRIC_KIND_UNSPECIFIED = 0; + + // An instantaneous measurement of a value. + GAUGE = 1; + + // The change in a value during a time interval. + DELTA = 2; + + // A value accumulated over a time interval. Cumulative + // measurements in a time series should have the same start time + // and increasing end times, until an event resets the cumulative + // value to zero and sets a new start time for the following + // points. + CUMULATIVE = 3; + } + + // The value type of a metric. + enum ValueType { + // Do not use this default value. + VALUE_TYPE_UNSPECIFIED = 0; + + // The value is a boolean. + // This value type can be used only if the metric kind is `GAUGE`. + BOOL = 1; + + // The value is a signed 64-bit integer. + INT64 = 2; + + // The value is a double precision floating point number. + DOUBLE = 3; + + // The value is a text string. + // This value type can be used only if the metric kind is `GAUGE`. + STRING = 4; + + // The value is a [`Distribution`][google.api.Distribution]. + DISTRIBUTION = 5; + + // The value is money. + MONEY = 6; + } + + // Additional annotations that can be used to guide the usage of a metric. + message MetricDescriptorMetadata { + // The resource hierarchy level of the timeseries data of a metric. + enum TimeSeriesResourceHierarchyLevel { + // Do not use this default value. + TIME_SERIES_RESOURCE_HIERARCHY_LEVEL_UNSPECIFIED = 0; + + // Scopes a metric to a project. + PROJECT = 1; + + // Scopes a metric to an organization. + ORGANIZATION = 2; + + // Scopes a metric to a folder. + FOLDER = 3; + } + + // Deprecated. Must use the + // [MetricDescriptor.launch_stage][google.api.MetricDescriptor.launch_stage] + // instead. + LaunchStage launch_stage = 1 [deprecated = true]; + + // The sampling period of metric data points. For metrics which are written + // periodically, consecutive data points are stored at this time interval, + // excluding data loss due to errors. Metrics with a higher granularity have + // a smaller sampling period. + google.protobuf.Duration sample_period = 2; + + // The delay of data points caused by ingestion. Data points older than this + // age are guaranteed to be ingested and available to be read, excluding + // data loss due to errors. + google.protobuf.Duration ingest_delay = 3; + + // The scope of the timeseries data of the metric. + repeated TimeSeriesResourceHierarchyLevel + time_series_resource_hierarchy_level = 4; + } + + // The resource name of the metric descriptor. + string name = 1; + + // The metric type, including its DNS name prefix. The type is not + // URL-encoded. All user-defined metric types have the DNS name + // `custom.googleapis.com` or `external.googleapis.com`. Metric types should + // use a natural hierarchical grouping. For example: + // + // "custom.googleapis.com/invoice/paid/amount" + // "external.googleapis.com/prometheus/up" + // "appengine.googleapis.com/http/server/response_latencies" + string type = 8; + + // The set of labels that can be used to describe a specific + // instance of this metric type. For example, the + // `appengine.googleapis.com/http/server/response_latencies` metric + // type has a label for the HTTP response code, `response_code`, so + // you can look at latencies for successful responses or just + // for responses that failed. + repeated LabelDescriptor labels = 2; + + // Whether the metric records instantaneous values, changes to a value, etc. + // Some combinations of `metric_kind` and `value_type` might not be supported. + MetricKind metric_kind = 3; + + // Whether the measurement is an integer, a floating-point number, etc. + // Some combinations of `metric_kind` and `value_type` might not be supported. + ValueType value_type = 4; + + // The units in which the metric value is reported. It is only applicable + // if the `value_type` is `INT64`, `DOUBLE`, or `DISTRIBUTION`. The `unit` + // defines the representation of the stored metric values. + // + // Different systems might scale the values to be more easily displayed (so a + // value of `0.02kBy` _might_ be displayed as `20By`, and a value of + // `3523kBy` _might_ be displayed as `3.5MBy`). However, if the `unit` is + // `kBy`, then the value of the metric is always in thousands of bytes, no + // matter how it might be displayed. + // + // If you want a custom metric to record the exact number of CPU-seconds used + // by a job, you can create an `INT64 CUMULATIVE` metric whose `unit` is + // `s{CPU}` (or equivalently `1s{CPU}` or just `s`). If the job uses 12,005 + // CPU-seconds, then the value is written as `12005`. + // + // Alternatively, if you want a custom metric to record data in a more + // granular way, you can create a `DOUBLE CUMULATIVE` metric whose `unit` is + // `ks{CPU}`, and then write the value `12.005` (which is `12005/1000`), + // or use `Kis{CPU}` and write `11.723` (which is `12005/1024`). + // + // The supported units are a subset of [The Unified Code for Units of + // Measure](https://unitsofmeasure.org/ucum.html) standard: + // + // **Basic units (UNIT)** + // + // * `bit` bit + // * `By` byte + // * `s` second + // * `min` minute + // * `h` hour + // * `d` day + // * `1` dimensionless + // + // **Prefixes (PREFIX)** + // + // * `k` kilo (10^3) + // * `M` mega (10^6) + // * `G` giga (10^9) + // * `T` tera (10^12) + // * `P` peta (10^15) + // * `E` exa (10^18) + // * `Z` zetta (10^21) + // * `Y` yotta (10^24) + // + // * `m` milli (10^-3) + // * `u` micro (10^-6) + // * `n` nano (10^-9) + // * `p` pico (10^-12) + // * `f` femto (10^-15) + // * `a` atto (10^-18) + // * `z` zepto (10^-21) + // * `y` yocto (10^-24) + // + // * `Ki` kibi (2^10) + // * `Mi` mebi (2^20) + // * `Gi` gibi (2^30) + // * `Ti` tebi (2^40) + // * `Pi` pebi (2^50) + // + // **Grammar** + // + // The grammar also includes these connectors: + // + // * `/` division or ratio (as an infix operator). For examples, + // `kBy/{email}` or `MiBy/10ms` (although you should almost never + // have `/s` in a metric `unit`; rates should always be computed at + // query time from the underlying cumulative or delta value). + // * `.` multiplication or composition (as an infix operator). For + // examples, `GBy.d` or `k{watt}.h`. + // + // The grammar for a unit is as follows: + // + // Expression = Component { "." Component } { "/" Component } ; + // + // Component = ( [ PREFIX ] UNIT | "%" ) [ Annotation ] + // | Annotation + // | "1" + // ; + // + // Annotation = "{" NAME "}" ; + // + // Notes: + // + // * `Annotation` is just a comment if it follows a `UNIT`. If the annotation + // is used alone, then the unit is equivalent to `1`. For examples, + // `{request}/s == 1/s`, `By{transmitted}/s == By/s`. + // * `NAME` is a sequence of non-blank printable ASCII characters not + // containing `{` or `}`. + // * `1` represents a unitary [dimensionless + // unit](https://en.wikipedia.org/wiki/Dimensionless_quantity) of 1, such + // as in `1/s`. It is typically used when none of the basic units are + // appropriate. For example, "new users per day" can be represented as + // `1/d` or `{new-users}/d` (and a metric value `5` would mean "5 new + // users). Alternatively, "thousands of page views per day" would be + // represented as `1000/d` or `k1/d` or `k{page_views}/d` (and a metric + // value of `5.3` would mean "5300 page views per day"). + // * `%` represents dimensionless value of 1/100, and annotates values giving + // a percentage (so the metric values are typically in the range of 0..100, + // and a metric value `3` means "3 percent"). + // * `10^2.%` indicates a metric contains a ratio, typically in the range + // 0..1, that will be multiplied by 100 and displayed as a percentage + // (so a metric value `0.03` means "3 percent"). + string unit = 5; + + // A detailed description of the metric, which can be used in documentation. + string description = 6; + + // A concise name for the metric, which can be displayed in user interfaces. + // Use sentence case without an ending period, for example "Request count". + // This field is optional but it is recommended to be set for any metrics + // associated with user-visible concepts, such as Quota. + string display_name = 7; + + // Optional. Metadata which can be used to guide usage of the metric. + MetricDescriptorMetadata metadata = 10; + + // Optional. The launch stage of the metric definition. + LaunchStage launch_stage = 12; + + // Read-only. If present, then a [time + // series][google.monitoring.v3.TimeSeries], which is identified partially by + // a metric type and a + // [MonitoredResourceDescriptor][google.api.MonitoredResourceDescriptor], that + // is associated with this metric type can only be associated with one of the + // monitored resource types listed here. + repeated string monitored_resource_types = 13; +} + +// A specific metric, identified by specifying values for all of the +// labels of a [`MetricDescriptor`][google.api.MetricDescriptor]. +message Metric { + // An existing metric type, see + // [google.api.MetricDescriptor][google.api.MetricDescriptor]. For example, + // `custom.googleapis.com/invoice/paid/amount`. + string type = 3; + + // The set of label values that uniquely identify this metric. All + // labels listed in the `MetricDescriptor` must be assigned values. + map labels = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/api/metric_pb2.py b/venv/lib/python3.12/site-packages/google/api/metric_pb2.py new file mode 100644 index 0000000..b2935e6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/metric_pb2.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/metric.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/metric.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 + +from google.api import label_pb2 as google_dot_api_dot_label__pb2 +from google.api import launch_stage_pb2 as google_dot_api_dot_launch__stage__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x17google/api/metric.proto\x12\ngoogle.api\x1a\x16google/api/label.proto\x1a\x1dgoogle/api/launch_stage.proto\x1a\x1egoogle/protobuf/duration.proto"\xac\x08\n\x10MetricDescriptor\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x08 \x01(\t\x12+\n\x06labels\x18\x02 \x03(\x0b\x32\x1b.google.api.LabelDescriptor\x12<\n\x0bmetric_kind\x18\x03 \x01(\x0e\x32\'.google.api.MetricDescriptor.MetricKind\x12:\n\nvalue_type\x18\x04 \x01(\x0e\x32&.google.api.MetricDescriptor.ValueType\x12\x0c\n\x04unit\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x07 \x01(\t\x12G\n\x08metadata\x18\n \x01(\x0b\x32\x35.google.api.MetricDescriptor.MetricDescriptorMetadata\x12-\n\x0claunch_stage\x18\x0c \x01(\x0e\x32\x17.google.api.LaunchStage\x12 \n\x18monitored_resource_types\x18\r \x03(\t\x1a\xbd\x03\n\x18MetricDescriptorMetadata\x12\x31\n\x0claunch_stage\x18\x01 \x01(\x0e\x32\x17.google.api.LaunchStageB\x02\x18\x01\x12\x30\n\rsample_period\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration\x12/\n\x0cingest_delay\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x84\x01\n$time_series_resource_hierarchy_level\x18\x04 \x03(\x0e\x32V.google.api.MetricDescriptor.MetricDescriptorMetadata.TimeSeriesResourceHierarchyLevel"\x83\x01\n TimeSeriesResourceHierarchyLevel\x12\x34\n0TIME_SERIES_RESOURCE_HIERARCHY_LEVEL_UNSPECIFIED\x10\x00\x12\x0b\n\x07PROJECT\x10\x01\x12\x10\n\x0cORGANIZATION\x10\x02\x12\n\n\x06\x46OLDER\x10\x03"O\n\nMetricKind\x12\x1b\n\x17METRIC_KIND_UNSPECIFIED\x10\x00\x12\t\n\x05GAUGE\x10\x01\x12\t\n\x05\x44\x45LTA\x10\x02\x12\x0e\n\nCUMULATIVE\x10\x03"q\n\tValueType\x12\x1a\n\x16VALUE_TYPE_UNSPECIFIED\x10\x00\x12\x08\n\x04\x42OOL\x10\x01\x12\t\n\x05INT64\x10\x02\x12\n\n\x06\x44OUBLE\x10\x03\x12\n\n\x06STRING\x10\x04\x12\x10\n\x0c\x44ISTRIBUTION\x10\x05\x12\t\n\x05MONEY\x10\x06"u\n\x06Metric\x12\x0c\n\x04type\x18\x03 \x01(\t\x12.\n\x06labels\x18\x02 \x03(\x0b\x32\x1e.google.api.Metric.LabelsEntry\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42_\n\x0e\x63om.google.apiB\x0bMetricProtoP\x01Z7google.golang.org/genproto/googleapis/api/metric;metric\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.metric_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\013MetricProtoP\001Z7google.golang.org/genproto/googleapis/api/metric;metric\242\002\004GAPI" + _globals["_METRICDESCRIPTOR_METRICDESCRIPTORMETADATA"].fields_by_name[ + "launch_stage" + ]._loaded_options = None + _globals["_METRICDESCRIPTOR_METRICDESCRIPTORMETADATA"].fields_by_name[ + "launch_stage" + ]._serialized_options = b"\030\001" + _globals["_METRIC_LABELSENTRY"]._loaded_options = None + _globals["_METRIC_LABELSENTRY"]._serialized_options = b"8\001" + _globals["_METRICDESCRIPTOR"]._serialized_start = 127 + _globals["_METRICDESCRIPTOR"]._serialized_end = 1195 + _globals["_METRICDESCRIPTOR_METRICDESCRIPTORMETADATA"]._serialized_start = 554 + _globals["_METRICDESCRIPTOR_METRICDESCRIPTORMETADATA"]._serialized_end = 999 + _globals[ + "_METRICDESCRIPTOR_METRICDESCRIPTORMETADATA_TIMESERIESRESOURCEHIERARCHYLEVEL" + ]._serialized_start = 868 + _globals[ + "_METRICDESCRIPTOR_METRICDESCRIPTORMETADATA_TIMESERIESRESOURCEHIERARCHYLEVEL" + ]._serialized_end = 999 + _globals["_METRICDESCRIPTOR_METRICKIND"]._serialized_start = 1001 + _globals["_METRICDESCRIPTOR_METRICKIND"]._serialized_end = 1080 + _globals["_METRICDESCRIPTOR_VALUETYPE"]._serialized_start = 1082 + _globals["_METRICDESCRIPTOR_VALUETYPE"]._serialized_end = 1195 + _globals["_METRIC"]._serialized_start = 1197 + _globals["_METRIC"]._serialized_end = 1314 + _globals["_METRIC_LABELSENTRY"]._serialized_start = 1269 + _globals["_METRIC_LABELSENTRY"]._serialized_end = 1314 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/metric_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/metric_pb2.pyi new file mode 100644 index 0000000..6da6975 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/metric_pb2.pyi @@ -0,0 +1,199 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import duration_pb2 as _duration_pb2 +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +from google.api import label_pb2 as _label_pb2 +from google.api import launch_stage_pb2 as _launch_stage_pb2 + +DESCRIPTOR: _descriptor.FileDescriptor + +class MetricDescriptor(_message.Message): + __slots__ = ( + "name", + "type", + "labels", + "metric_kind", + "value_type", + "unit", + "description", + "display_name", + "metadata", + "launch_stage", + "monitored_resource_types", + ) + class MetricKind(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + METRIC_KIND_UNSPECIFIED: _ClassVar[MetricDescriptor.MetricKind] + GAUGE: _ClassVar[MetricDescriptor.MetricKind] + DELTA: _ClassVar[MetricDescriptor.MetricKind] + CUMULATIVE: _ClassVar[MetricDescriptor.MetricKind] + + METRIC_KIND_UNSPECIFIED: MetricDescriptor.MetricKind + GAUGE: MetricDescriptor.MetricKind + DELTA: MetricDescriptor.MetricKind + CUMULATIVE: MetricDescriptor.MetricKind + class ValueType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + VALUE_TYPE_UNSPECIFIED: _ClassVar[MetricDescriptor.ValueType] + BOOL: _ClassVar[MetricDescriptor.ValueType] + INT64: _ClassVar[MetricDescriptor.ValueType] + DOUBLE: _ClassVar[MetricDescriptor.ValueType] + STRING: _ClassVar[MetricDescriptor.ValueType] + DISTRIBUTION: _ClassVar[MetricDescriptor.ValueType] + MONEY: _ClassVar[MetricDescriptor.ValueType] + + VALUE_TYPE_UNSPECIFIED: MetricDescriptor.ValueType + BOOL: MetricDescriptor.ValueType + INT64: MetricDescriptor.ValueType + DOUBLE: MetricDescriptor.ValueType + STRING: MetricDescriptor.ValueType + DISTRIBUTION: MetricDescriptor.ValueType + MONEY: MetricDescriptor.ValueType + class MetricDescriptorMetadata(_message.Message): + __slots__ = ( + "launch_stage", + "sample_period", + "ingest_delay", + "time_series_resource_hierarchy_level", + ) + class TimeSeriesResourceHierarchyLevel( + int, metaclass=_enum_type_wrapper.EnumTypeWrapper + ): + __slots__ = () + TIME_SERIES_RESOURCE_HIERARCHY_LEVEL_UNSPECIFIED: _ClassVar[ + MetricDescriptor.MetricDescriptorMetadata.TimeSeriesResourceHierarchyLevel + ] + PROJECT: _ClassVar[ + MetricDescriptor.MetricDescriptorMetadata.TimeSeriesResourceHierarchyLevel + ] + ORGANIZATION: _ClassVar[ + MetricDescriptor.MetricDescriptorMetadata.TimeSeriesResourceHierarchyLevel + ] + FOLDER: _ClassVar[ + MetricDescriptor.MetricDescriptorMetadata.TimeSeriesResourceHierarchyLevel + ] + + TIME_SERIES_RESOURCE_HIERARCHY_LEVEL_UNSPECIFIED: ( + MetricDescriptor.MetricDescriptorMetadata.TimeSeriesResourceHierarchyLevel + ) + PROJECT: ( + MetricDescriptor.MetricDescriptorMetadata.TimeSeriesResourceHierarchyLevel + ) + ORGANIZATION: ( + MetricDescriptor.MetricDescriptorMetadata.TimeSeriesResourceHierarchyLevel + ) + FOLDER: ( + MetricDescriptor.MetricDescriptorMetadata.TimeSeriesResourceHierarchyLevel + ) + LAUNCH_STAGE_FIELD_NUMBER: _ClassVar[int] + SAMPLE_PERIOD_FIELD_NUMBER: _ClassVar[int] + INGEST_DELAY_FIELD_NUMBER: _ClassVar[int] + TIME_SERIES_RESOURCE_HIERARCHY_LEVEL_FIELD_NUMBER: _ClassVar[int] + launch_stage: _launch_stage_pb2.LaunchStage + sample_period: _duration_pb2.Duration + ingest_delay: _duration_pb2.Duration + time_series_resource_hierarchy_level: _containers.RepeatedScalarFieldContainer[ + MetricDescriptor.MetricDescriptorMetadata.TimeSeriesResourceHierarchyLevel + ] + def __init__( + self, + launch_stage: _Optional[_Union[_launch_stage_pb2.LaunchStage, str]] = ..., + sample_period: _Optional[ + _Union[datetime.timedelta, _duration_pb2.Duration, _Mapping] + ] = ..., + ingest_delay: _Optional[ + _Union[datetime.timedelta, _duration_pb2.Duration, _Mapping] + ] = ..., + time_series_resource_hierarchy_level: _Optional[ + _Iterable[ + _Union[ + MetricDescriptor.MetricDescriptorMetadata.TimeSeriesResourceHierarchyLevel, + str, + ] + ] + ] = ..., + ) -> None: ... + + NAME_FIELD_NUMBER: _ClassVar[int] + TYPE_FIELD_NUMBER: _ClassVar[int] + LABELS_FIELD_NUMBER: _ClassVar[int] + METRIC_KIND_FIELD_NUMBER: _ClassVar[int] + VALUE_TYPE_FIELD_NUMBER: _ClassVar[int] + UNIT_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + DISPLAY_NAME_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + LAUNCH_STAGE_FIELD_NUMBER: _ClassVar[int] + MONITORED_RESOURCE_TYPES_FIELD_NUMBER: _ClassVar[int] + name: str + type: str + labels: _containers.RepeatedCompositeFieldContainer[_label_pb2.LabelDescriptor] + metric_kind: MetricDescriptor.MetricKind + value_type: MetricDescriptor.ValueType + unit: str + description: str + display_name: str + metadata: MetricDescriptor.MetricDescriptorMetadata + launch_stage: _launch_stage_pb2.LaunchStage + monitored_resource_types: _containers.RepeatedScalarFieldContainer[str] + def __init__( + self, + name: _Optional[str] = ..., + type: _Optional[str] = ..., + labels: _Optional[ + _Iterable[_Union[_label_pb2.LabelDescriptor, _Mapping]] + ] = ..., + metric_kind: _Optional[_Union[MetricDescriptor.MetricKind, str]] = ..., + value_type: _Optional[_Union[MetricDescriptor.ValueType, str]] = ..., + unit: _Optional[str] = ..., + description: _Optional[str] = ..., + display_name: _Optional[str] = ..., + metadata: _Optional[ + _Union[MetricDescriptor.MetricDescriptorMetadata, _Mapping] + ] = ..., + launch_stage: _Optional[_Union[_launch_stage_pb2.LaunchStage, str]] = ..., + monitored_resource_types: _Optional[_Iterable[str]] = ..., + ) -> None: ... + +class Metric(_message.Message): + __slots__ = ("type", "labels") + class LabelsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + TYPE_FIELD_NUMBER: _ClassVar[int] + LABELS_FIELD_NUMBER: _ClassVar[int] + type: str + labels: _containers.ScalarMap[str, str] + def __init__( + self, type: _Optional[str] = ..., labels: _Optional[_Mapping[str, str]] = ... + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/monitored_resource.proto b/venv/lib/python3.12/site-packages/google/api/monitored_resource.proto new file mode 100644 index 0000000..eaf1f12 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/monitored_resource.proto @@ -0,0 +1,129 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/label.proto"; +import "google/api/launch_stage.proto"; +import "google/protobuf/struct.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/monitoredres;monitoredres"; +option java_multiple_files = true; +option java_outer_classname = "MonitoredResourceProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// An object that describes the schema of a +// [MonitoredResource][google.api.MonitoredResource] object using a type name +// and a set of labels. For example, the monitored resource descriptor for +// Google Compute Engine VM instances has a type of +// `"gce_instance"` and specifies the use of the labels `"instance_id"` and +// `"zone"` to identify particular VM instances. +// +// Different APIs can support different monitored resource types. APIs generally +// provide a `list` method that returns the monitored resource descriptors used +// by the API. +// +message MonitoredResourceDescriptor { + // Optional. The resource name of the monitored resource descriptor: + // `"projects/{project_id}/monitoredResourceDescriptors/{type}"` where + // {type} is the value of the `type` field in this object and + // {project_id} is a project ID that provides API-specific context for + // accessing the type. APIs that do not use project information can use the + // resource name format `"monitoredResourceDescriptors/{type}"`. + string name = 5; + + // Required. The monitored resource type. For example, the type + // `"cloudsql_database"` represents databases in Google Cloud SQL. + // For a list of types, see [Monitored resource + // types](https://cloud.google.com/monitoring/api/resources) + // and [Logging resource + // types](https://cloud.google.com/logging/docs/api/v2/resource-list). + string type = 1; + + // Optional. A concise name for the monitored resource type that might be + // displayed in user interfaces. It should be a Title Cased Noun Phrase, + // without any article or other determiners. For example, + // `"Google Cloud SQL Database"`. + string display_name = 2; + + // Optional. A detailed description of the monitored resource type that might + // be used in documentation. + string description = 3; + + // Required. A set of labels used to describe instances of this monitored + // resource type. For example, an individual Google Cloud SQL database is + // identified by values for the labels `"database_id"` and `"zone"`. + repeated LabelDescriptor labels = 4; + + // Optional. The launch stage of the monitored resource definition. + LaunchStage launch_stage = 7; +} + +// An object representing a resource that can be used for monitoring, logging, +// billing, or other purposes. Examples include virtual machine instances, +// databases, and storage devices such as disks. The `type` field identifies a +// [MonitoredResourceDescriptor][google.api.MonitoredResourceDescriptor] object +// that describes the resource's schema. Information in the `labels` field +// identifies the actual resource and its attributes according to the schema. +// For example, a particular Compute Engine VM instance could be represented by +// the following object, because the +// [MonitoredResourceDescriptor][google.api.MonitoredResourceDescriptor] for +// `"gce_instance"` has labels +// `"project_id"`, `"instance_id"` and `"zone"`: +// +// { "type": "gce_instance", +// "labels": { "project_id": "my-project", +// "instance_id": "12345678901234", +// "zone": "us-central1-a" }} +message MonitoredResource { + // Required. The monitored resource type. This field must match + // the `type` field of a + // [MonitoredResourceDescriptor][google.api.MonitoredResourceDescriptor] + // object. For example, the type of a Compute Engine VM instance is + // `gce_instance`. Some descriptors include the service name in the type; for + // example, the type of a Datastream stream is + // `datastream.googleapis.com/Stream`. + string type = 1; + + // Required. Values for all of the labels listed in the associated monitored + // resource descriptor. For example, Compute Engine VM instances use the + // labels `"project_id"`, `"instance_id"`, and `"zone"`. + map labels = 2; +} + +// Auxiliary metadata for a [MonitoredResource][google.api.MonitoredResource] +// object. [MonitoredResource][google.api.MonitoredResource] objects contain the +// minimum set of information to uniquely identify a monitored resource +// instance. There is some other useful auxiliary metadata. Monitoring and +// Logging use an ingestion pipeline to extract metadata for cloud resources of +// all types, and store the metadata in this message. +message MonitoredResourceMetadata { + // Output only. Values for predefined system metadata labels. + // System labels are a kind of metadata extracted by Google, including + // "machine_image", "vpc", "subnet_id", + // "security_group", "name", etc. + // System label values can be only strings, Boolean values, or a list of + // strings. For example: + // + // { "name": "my-test-instance", + // "security_group": ["a", "b", "c"], + // "spot_instance": false } + google.protobuf.Struct system_labels = 1; + + // Output only. A map of user-defined metadata labels. + map user_labels = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/api/monitored_resource_pb2.py b/venv/lib/python3.12/site-packages/google/api/monitored_resource_pb2.py new file mode 100644 index 0000000..c91edf4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/monitored_resource_pb2.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/monitored_resource.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/monitored_resource.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 + +from google.api import label_pb2 as google_dot_api_dot_label__pb2 +from google.api import launch_stage_pb2 as google_dot_api_dot_launch__stage__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n#google/api/monitored_resource.proto\x12\ngoogle.api\x1a\x16google/api/label.proto\x1a\x1dgoogle/api/launch_stage.proto\x1a\x1cgoogle/protobuf/struct.proto"\xc0\x01\n\x1bMonitoredResourceDescriptor\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12+\n\x06labels\x18\x04 \x03(\x0b\x32\x1b.google.api.LabelDescriptor\x12-\n\x0claunch_stage\x18\x07 \x01(\x0e\x32\x17.google.api.LaunchStage"\x8b\x01\n\x11MonitoredResource\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x39\n\x06labels\x18\x02 \x03(\x0b\x32).google.api.MonitoredResource.LabelsEntry\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"\xca\x01\n\x19MonitoredResourceMetadata\x12.\n\rsystem_labels\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\x12J\n\x0buser_labels\x18\x02 \x03(\x0b\x32\x35.google.api.MonitoredResourceMetadata.UserLabelsEntry\x1a\x31\n\x0fUserLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42v\n\x0e\x63om.google.apiB\x16MonitoredResourceProtoP\x01ZCgoogle.golang.org/genproto/googleapis/api/monitoredres;monitoredres\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.api.monitored_resource_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\026MonitoredResourceProtoP\001ZCgoogle.golang.org/genproto/googleapis/api/monitoredres;monitoredres\242\002\004GAPI" + _globals["_MONITOREDRESOURCE_LABELSENTRY"]._loaded_options = None + _globals["_MONITOREDRESOURCE_LABELSENTRY"]._serialized_options = b"8\001" + _globals["_MONITOREDRESOURCEMETADATA_USERLABELSENTRY"]._loaded_options = None + _globals[ + "_MONITOREDRESOURCEMETADATA_USERLABELSENTRY" + ]._serialized_options = b"8\001" + _globals["_MONITOREDRESOURCEDESCRIPTOR"]._serialized_start = 137 + _globals["_MONITOREDRESOURCEDESCRIPTOR"]._serialized_end = 329 + _globals["_MONITOREDRESOURCE"]._serialized_start = 332 + _globals["_MONITOREDRESOURCE"]._serialized_end = 471 + _globals["_MONITOREDRESOURCE_LABELSENTRY"]._serialized_start = 426 + _globals["_MONITOREDRESOURCE_LABELSENTRY"]._serialized_end = 471 + _globals["_MONITOREDRESOURCEMETADATA"]._serialized_start = 474 + _globals["_MONITOREDRESOURCEMETADATA"]._serialized_end = 676 + _globals["_MONITOREDRESOURCEMETADATA_USERLABELSENTRY"]._serialized_start = 627 + _globals["_MONITOREDRESOURCEMETADATA_USERLABELSENTRY"]._serialized_end = 676 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/monitored_resource_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/monitored_resource_pb2.pyi new file mode 100644 index 0000000..5e12501 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/monitored_resource_pb2.pyi @@ -0,0 +1,104 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import struct_pb2 as _struct_pb2 +from google.protobuf.internal import containers as _containers + +from google.api import label_pb2 as _label_pb2 +from google.api import launch_stage_pb2 as _launch_stage_pb2 + +DESCRIPTOR: _descriptor.FileDescriptor + +class MonitoredResourceDescriptor(_message.Message): + __slots__ = ( + "name", + "type", + "display_name", + "description", + "labels", + "launch_stage", + ) + NAME_FIELD_NUMBER: _ClassVar[int] + TYPE_FIELD_NUMBER: _ClassVar[int] + DISPLAY_NAME_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + LABELS_FIELD_NUMBER: _ClassVar[int] + LAUNCH_STAGE_FIELD_NUMBER: _ClassVar[int] + name: str + type: str + display_name: str + description: str + labels: _containers.RepeatedCompositeFieldContainer[_label_pb2.LabelDescriptor] + launch_stage: _launch_stage_pb2.LaunchStage + def __init__( + self, + name: _Optional[str] = ..., + type: _Optional[str] = ..., + display_name: _Optional[str] = ..., + description: _Optional[str] = ..., + labels: _Optional[ + _Iterable[_Union[_label_pb2.LabelDescriptor, _Mapping]] + ] = ..., + launch_stage: _Optional[_Union[_launch_stage_pb2.LaunchStage, str]] = ..., + ) -> None: ... + +class MonitoredResource(_message.Message): + __slots__ = ("type", "labels") + class LabelsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + TYPE_FIELD_NUMBER: _ClassVar[int] + LABELS_FIELD_NUMBER: _ClassVar[int] + type: str + labels: _containers.ScalarMap[str, str] + def __init__( + self, type: _Optional[str] = ..., labels: _Optional[_Mapping[str, str]] = ... + ) -> None: ... + +class MonitoredResourceMetadata(_message.Message): + __slots__ = ("system_labels", "user_labels") + class UserLabelsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + SYSTEM_LABELS_FIELD_NUMBER: _ClassVar[int] + USER_LABELS_FIELD_NUMBER: _ClassVar[int] + system_labels: _struct_pb2.Struct + user_labels: _containers.ScalarMap[str, str] + def __init__( + self, + system_labels: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., + user_labels: _Optional[_Mapping[str, str]] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/monitoring.proto b/venv/lib/python3.12/site-packages/google/api/monitoring.proto new file mode 100644 index 0000000..b939a0c --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/monitoring.proto @@ -0,0 +1,107 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "MonitoringProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Monitoring configuration of the service. +// +// The example below shows how to configure monitored resources and metrics +// for monitoring. In the example, a monitored resource and two metrics are +// defined. The `library.googleapis.com/book/returned_count` metric is sent +// to both producer and consumer projects, whereas the +// `library.googleapis.com/book/num_overdue` metric is only sent to the +// consumer project. +// +// monitored_resources: +// - type: library.googleapis.com/Branch +// display_name: "Library Branch" +// description: "A branch of a library." +// launch_stage: GA +// labels: +// - key: resource_container +// description: "The Cloud container (ie. project id) for the Branch." +// - key: location +// description: "The location of the library branch." +// - key: branch_id +// description: "The id of the branch." +// metrics: +// - name: library.googleapis.com/book/returned_count +// display_name: "Books Returned" +// description: "The count of books that have been returned." +// launch_stage: GA +// metric_kind: DELTA +// value_type: INT64 +// unit: "1" +// labels: +// - key: customer_id +// description: "The id of the customer." +// - name: library.googleapis.com/book/num_overdue +// display_name: "Books Overdue" +// description: "The current number of overdue books." +// launch_stage: GA +// metric_kind: GAUGE +// value_type: INT64 +// unit: "1" +// labels: +// - key: customer_id +// description: "The id of the customer." +// monitoring: +// producer_destinations: +// - monitored_resource: library.googleapis.com/Branch +// metrics: +// - library.googleapis.com/book/returned_count +// consumer_destinations: +// - monitored_resource: library.googleapis.com/Branch +// metrics: +// - library.googleapis.com/book/returned_count +// - library.googleapis.com/book/num_overdue +message Monitoring { + // Configuration of a specific monitoring destination (the producer project + // or the consumer project). + message MonitoringDestination { + // The monitored resource type. The type must be defined in + // [Service.monitored_resources][google.api.Service.monitored_resources] + // section. + string monitored_resource = 1; + + // Types of the metrics to report to this monitoring destination. + // Each type must be defined in + // [Service.metrics][google.api.Service.metrics] section. + repeated string metrics = 2; + } + + // Monitoring configurations for sending metrics to the producer project. + // There can be multiple producer destinations. A monitored resource type may + // appear in multiple monitoring destinations if different aggregations are + // needed for different sets of metrics associated with that monitored + // resource type. A monitored resource and metric pair may only be used once + // in the Monitoring configuration. + repeated MonitoringDestination producer_destinations = 1; + + // Monitoring configurations for sending metrics to the consumer project. + // There can be multiple consumer destinations. A monitored resource type may + // appear in multiple monitoring destinations if different aggregations are + // needed for different sets of metrics associated with that monitored + // resource type. A monitored resource and metric pair may only be used once + // in the Monitoring configuration. + repeated MonitoringDestination consumer_destinations = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/api/monitoring_pb2.py b/venv/lib/python3.12/site-packages/google/api/monitoring_pb2.py new file mode 100644 index 0000000..5fdbe62 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/monitoring_pb2.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/monitoring.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/monitoring.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1bgoogle/api/monitoring.proto\x12\ngoogle.api"\xec\x01\n\nMonitoring\x12K\n\x15producer_destinations\x18\x01 \x03(\x0b\x32,.google.api.Monitoring.MonitoringDestination\x12K\n\x15\x63onsumer_destinations\x18\x02 \x03(\x0b\x32,.google.api.Monitoring.MonitoringDestination\x1a\x44\n\x15MonitoringDestination\x12\x1a\n\x12monitored_resource\x18\x01 \x01(\t\x12\x0f\n\x07metrics\x18\x02 \x03(\tBq\n\x0e\x63om.google.apiB\x0fMonitoringProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.api.monitoring_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\017MonitoringProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_MONITORING"]._serialized_start = 44 + _globals["_MONITORING"]._serialized_end = 280 + _globals["_MONITORING_MONITORINGDESTINATION"]._serialized_start = 212 + _globals["_MONITORING_MONITORINGDESTINATION"]._serialized_end = 280 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/monitoring_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/monitoring_pb2.pyi new file mode 100644 index 0000000..b6eade8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/monitoring_pb2.pyi @@ -0,0 +1,57 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class Monitoring(_message.Message): + __slots__ = ("producer_destinations", "consumer_destinations") + class MonitoringDestination(_message.Message): + __slots__ = ("monitored_resource", "metrics") + MONITORED_RESOURCE_FIELD_NUMBER: _ClassVar[int] + METRICS_FIELD_NUMBER: _ClassVar[int] + monitored_resource: str + metrics: _containers.RepeatedScalarFieldContainer[str] + def __init__( + self, + monitored_resource: _Optional[str] = ..., + metrics: _Optional[_Iterable[str]] = ..., + ) -> None: ... + + PRODUCER_DESTINATIONS_FIELD_NUMBER: _ClassVar[int] + CONSUMER_DESTINATIONS_FIELD_NUMBER: _ClassVar[int] + producer_destinations: _containers.RepeatedCompositeFieldContainer[ + Monitoring.MonitoringDestination + ] + consumer_destinations: _containers.RepeatedCompositeFieldContainer[ + Monitoring.MonitoringDestination + ] + def __init__( + self, + producer_destinations: _Optional[ + _Iterable[_Union[Monitoring.MonitoringDestination, _Mapping]] + ] = ..., + consumer_destinations: _Optional[ + _Iterable[_Union[Monitoring.MonitoringDestination, _Mapping]] + ] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/policy.proto b/venv/lib/python3.12/site-packages/google/api/policy.proto new file mode 100644 index 0000000..c925fdc --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/policy.proto @@ -0,0 +1,82 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "PolicyProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.FieldOptions { + // See [FieldPolicy][]. + google.api.FieldPolicy field_policy = 158361448; +} + +extend google.protobuf.MethodOptions { + // See [MethodPolicy][]. + google.api.MethodPolicy method_policy = 161893301; +} + +// Google API Policy Annotation +// +// This message defines a simple API policy annotation that can be used to +// annotate API request and response message fields with applicable policies. +// One field may have multiple applicable policies that must all be satisfied +// before a request can be processed. This policy annotation is used to +// generate the overall policy that will be used for automatic runtime +// policy enforcement and documentation generation. +message FieldPolicy { + // Selects one or more request or response message fields to apply this + // `FieldPolicy`. + // + // When a `FieldPolicy` is used in proto annotation, the selector must + // be left as empty. The service config generator will automatically fill + // the correct value. + // + // When a `FieldPolicy` is used in service config, the selector must be a + // comma-separated string with valid request or response field paths, + // such as "foo.bar" or "foo.bar,foo.baz". + string selector = 1; + + // Specifies the required permission(s) for the resource referred to by the + // field. It requires the field contains a valid resource reference, and + // the request must pass the permission checks to proceed. For example, + // "resourcemanager.projects.get". + string resource_permission = 2; + + // Specifies the resource type for the resource referred to by the field. + string resource_type = 3; +} + +// Defines policies applying to an RPC method. +message MethodPolicy { + // Selects a method to which these policies should be enforced, for example, + // "google.pubsub.v1.Subscriber.CreateSubscription". + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + // + // NOTE: This field must not be set in the proto annotation. It will be + // automatically filled by the service config compiler . + string selector = 9; + + // Policies that are applicable to the request message. + repeated FieldPolicy request_policies = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/api/policy_pb2.py b/venv/lib/python3.12/site-packages/google/api/policy_pb2.py new file mode 100644 index 0000000..b2ed227 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/policy_pb2.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/policy.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/policy.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x17google/api/policy.proto\x12\ngoogle.api\x1a google/protobuf/descriptor.proto"S\n\x0b\x46ieldPolicy\x12\x10\n\x08selector\x18\x01 \x01(\t\x12\x1b\n\x13resource_permission\x18\x02 \x01(\t\x12\x15\n\rresource_type\x18\x03 \x01(\t"S\n\x0cMethodPolicy\x12\x10\n\x08selector\x18\t \x01(\t\x12\x31\n\x10request_policies\x18\x02 \x03(\x0b\x32\x17.google.api.FieldPolicy:O\n\x0c\x66ield_policy\x12\x1d.google.protobuf.FieldOptions\x18\xe8\xce\xc1K \x01(\x0b\x32\x17.google.api.FieldPolicy:R\n\rmethod_policy\x12\x1e.google.protobuf.MethodOptions\x18\xb5\x97\x99M \x01(\x0b\x32\x18.google.api.MethodPolicyBm\n\x0e\x63om.google.apiB\x0bPolicyProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.policy_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\013PolicyProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_FIELDPOLICY"]._serialized_start = 73 + _globals["_FIELDPOLICY"]._serialized_end = 156 + _globals["_METHODPOLICY"]._serialized_start = 158 + _globals["_METHODPOLICY"]._serialized_end = 241 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/policy_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/policy_pb2.pyi new file mode 100644 index 0000000..2c5fe96 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/policy_pb2.pyi @@ -0,0 +1,57 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pb2 as _descriptor_pb2 +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor +FIELD_POLICY_FIELD_NUMBER: _ClassVar[int] +field_policy: _descriptor.FieldDescriptor +METHOD_POLICY_FIELD_NUMBER: _ClassVar[int] +method_policy: _descriptor.FieldDescriptor + +class FieldPolicy(_message.Message): + __slots__ = ("selector", "resource_permission", "resource_type") + SELECTOR_FIELD_NUMBER: _ClassVar[int] + RESOURCE_PERMISSION_FIELD_NUMBER: _ClassVar[int] + RESOURCE_TYPE_FIELD_NUMBER: _ClassVar[int] + selector: str + resource_permission: str + resource_type: str + def __init__( + self, + selector: _Optional[str] = ..., + resource_permission: _Optional[str] = ..., + resource_type: _Optional[str] = ..., + ) -> None: ... + +class MethodPolicy(_message.Message): + __slots__ = ("selector", "request_policies") + SELECTOR_FIELD_NUMBER: _ClassVar[int] + REQUEST_POLICIES_FIELD_NUMBER: _ClassVar[int] + selector: str + request_policies: _containers.RepeatedCompositeFieldContainer[FieldPolicy] + def __init__( + self, + selector: _Optional[str] = ..., + request_policies: _Optional[_Iterable[_Union[FieldPolicy, _Mapping]]] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/quota.proto b/venv/lib/python3.12/site-packages/google/api/quota.proto new file mode 100644 index 0000000..29ae86e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/quota.proto @@ -0,0 +1,184 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "QuotaProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Quota configuration helps to achieve fairness and budgeting in service +// usage. +// +// The metric based quota configuration works this way: +// - The service configuration defines a set of metrics. +// - For API calls, the quota.metric_rules maps methods to metrics with +// corresponding costs. +// - The quota.limits defines limits on the metrics, which will be used for +// quota checks at runtime. +// +// An example quota configuration in yaml format: +// +// quota: +// limits: +// +// - name: apiWriteQpsPerProject +// metric: library.googleapis.com/write_calls +// unit: "1/min/{project}" # rate limit for consumer projects +// values: +// STANDARD: 10000 +// +// +// (The metric rules bind all methods to the read_calls metric, +// except for the UpdateBook and DeleteBook methods. These two methods +// are mapped to the write_calls metric, with the UpdateBook method +// consuming at twice rate as the DeleteBook method.) +// metric_rules: +// - selector: "*" +// metric_costs: +// library.googleapis.com/read_calls: 1 +// - selector: google.example.library.v1.LibraryService.UpdateBook +// metric_costs: +// library.googleapis.com/write_calls: 2 +// - selector: google.example.library.v1.LibraryService.DeleteBook +// metric_costs: +// library.googleapis.com/write_calls: 1 +// +// Corresponding Metric definition: +// +// metrics: +// - name: library.googleapis.com/read_calls +// display_name: Read requests +// metric_kind: DELTA +// value_type: INT64 +// +// - name: library.googleapis.com/write_calls +// display_name: Write requests +// metric_kind: DELTA +// value_type: INT64 +// +// +message Quota { + // List of QuotaLimit definitions for the service. + repeated QuotaLimit limits = 3; + + // List of MetricRule definitions, each one mapping a selected method to one + // or more metrics. + repeated MetricRule metric_rules = 4; +} + +// Bind API methods to metrics. Binding a method to a metric causes that +// metric's configured quota behaviors to apply to the method call. +message MetricRule { + // Selects the methods to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Metrics to update when the selected methods are called, and the associated + // cost applied to each metric. + // + // The key of the map is the metric name, and the values are the amount + // increased for the metric against which the quota limits are defined. + // The value must not be negative. + map metric_costs = 2; +} + +// `QuotaLimit` defines a specific limit that applies over a specified duration +// for a limit type. There can be at most one limit for a duration and limit +// type combination defined within a `QuotaGroup`. +message QuotaLimit { + // Name of the quota limit. + // + // The name must be provided, and it must be unique within the service. The + // name can only include alphanumeric characters as well as '-'. + // + // The maximum length of the limit name is 64 characters. + string name = 6; + + // Optional. User-visible, extended description for this quota limit. + // Should be used only when more context is needed to understand this limit + // than provided by the limit's display name (see: `display_name`). + string description = 2; + + // Default number of tokens that can be consumed during the specified + // duration. This is the number of tokens assigned when a client + // application developer activates the service for his/her project. + // + // Specifying a value of 0 will block all requests. This can be used if you + // are provisioning quota to selected consumers and blocking others. + // Similarly, a value of -1 will indicate an unlimited quota. No other + // negative values are allowed. + // + // Used by group-based quotas only. + int64 default_limit = 3; + + // Maximum number of tokens that can be consumed during the specified + // duration. Client application developers can override the default limit up + // to this maximum. If specified, this value cannot be set to a value less + // than the default limit. If not specified, it is set to the default limit. + // + // To allow clients to apply overrides with no upper bound, set this to -1, + // indicating unlimited maximum quota. + // + // Used by group-based quotas only. + int64 max_limit = 4; + + // Free tier value displayed in the Developers Console for this limit. + // The free tier is the number of tokens that will be subtracted from the + // billed amount when billing is enabled. + // This field can only be set on a limit with duration "1d", in a billable + // group; it is invalid on any other limit. If this field is not set, it + // defaults to 0, indicating that there is no free tier for this service. + // + // Used by group-based quotas only. + int64 free_tier = 7; + + // Duration of this limit in textual notation. Must be "100s" or "1d". + // + // Used by group-based quotas only. + string duration = 5; + + // The name of the metric this quota limit applies to. The quota limits with + // the same metric will be checked together during runtime. The metric must be + // defined within the service config. + string metric = 8; + + // Specify the unit of the quota limit. It uses the same syntax as + // [MetricDescriptor.unit][google.api.MetricDescriptor.unit]. The supported + // unit kinds are determined by the quota backend system. + // + // Here are some examples: + // * "1/min/{project}" for quota per minute per project. + // + // Note: the order of unit components is insignificant. + // The "1" at the beginning is required to follow the metric unit syntax. + string unit = 9; + + // Tiered limit values. You must specify this as a key:value pair, with an + // integer value that is the maximum number of requests allowed for the + // specified unit. Currently only STANDARD is supported. + map values = 10; + + // User-visible display name for this limit. + // Optional. If not set, the UI will provide a default display name based on + // the quota configuration. This field can be used to override the default + // display name generated from the configuration. + string display_name = 12; +} diff --git a/venv/lib/python3.12/site-packages/google/api/quota_pb2.py b/venv/lib/python3.12/site-packages/google/api/quota_pb2.py new file mode 100644 index 0000000..e61b0f2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/quota_pb2.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/quota.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/quota.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x16google/api/quota.proto\x12\ngoogle.api"]\n\x05Quota\x12&\n\x06limits\x18\x03 \x03(\x0b\x32\x16.google.api.QuotaLimit\x12,\n\x0cmetric_rules\x18\x04 \x03(\x0b\x32\x16.google.api.MetricRule"\x91\x01\n\nMetricRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12=\n\x0cmetric_costs\x18\x02 \x03(\x0b\x32\'.google.api.MetricRule.MetricCostsEntry\x1a\x32\n\x10MetricCostsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x03:\x02\x38\x01"\x95\x02\n\nQuotaLimit\x12\x0c\n\x04name\x18\x06 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x15\n\rdefault_limit\x18\x03 \x01(\x03\x12\x11\n\tmax_limit\x18\x04 \x01(\x03\x12\x11\n\tfree_tier\x18\x07 \x01(\x03\x12\x10\n\x08\x64uration\x18\x05 \x01(\t\x12\x0e\n\x06metric\x18\x08 \x01(\t\x12\x0c\n\x04unit\x18\t \x01(\t\x12\x32\n\x06values\x18\n \x03(\x0b\x32".google.api.QuotaLimit.ValuesEntry\x12\x14\n\x0c\x64isplay_name\x18\x0c \x01(\t\x1a-\n\x0bValuesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x03:\x02\x38\x01\x42l\n\x0e\x63om.google.apiB\nQuotaProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.quota_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\nQuotaProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_METRICRULE_METRICCOSTSENTRY"]._loaded_options = None + _globals["_METRICRULE_METRICCOSTSENTRY"]._serialized_options = b"8\001" + _globals["_QUOTALIMIT_VALUESENTRY"]._loaded_options = None + _globals["_QUOTALIMIT_VALUESENTRY"]._serialized_options = b"8\001" + _globals["_QUOTA"]._serialized_start = 38 + _globals["_QUOTA"]._serialized_end = 131 + _globals["_METRICRULE"]._serialized_start = 134 + _globals["_METRICRULE"]._serialized_end = 279 + _globals["_METRICRULE_METRICCOSTSENTRY"]._serialized_start = 229 + _globals["_METRICRULE_METRICCOSTSENTRY"]._serialized_end = 279 + _globals["_QUOTALIMIT"]._serialized_start = 282 + _globals["_QUOTALIMIT"]._serialized_end = 559 + _globals["_QUOTALIMIT_VALUESENTRY"]._serialized_start = 514 + _globals["_QUOTALIMIT_VALUESENTRY"]._serialized_end = 559 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/quota_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/quota_pb2.pyi new file mode 100644 index 0000000..2c70590 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/quota_pb2.pyi @@ -0,0 +1,116 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class Quota(_message.Message): + __slots__ = ("limits", "metric_rules") + LIMITS_FIELD_NUMBER: _ClassVar[int] + METRIC_RULES_FIELD_NUMBER: _ClassVar[int] + limits: _containers.RepeatedCompositeFieldContainer[QuotaLimit] + metric_rules: _containers.RepeatedCompositeFieldContainer[MetricRule] + def __init__( + self, + limits: _Optional[_Iterable[_Union[QuotaLimit, _Mapping]]] = ..., + metric_rules: _Optional[_Iterable[_Union[MetricRule, _Mapping]]] = ..., + ) -> None: ... + +class MetricRule(_message.Message): + __slots__ = ("selector", "metric_costs") + class MetricCostsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: int + def __init__( + self, key: _Optional[str] = ..., value: _Optional[int] = ... + ) -> None: ... + + SELECTOR_FIELD_NUMBER: _ClassVar[int] + METRIC_COSTS_FIELD_NUMBER: _ClassVar[int] + selector: str + metric_costs: _containers.ScalarMap[str, int] + def __init__( + self, + selector: _Optional[str] = ..., + metric_costs: _Optional[_Mapping[str, int]] = ..., + ) -> None: ... + +class QuotaLimit(_message.Message): + __slots__ = ( + "name", + "description", + "default_limit", + "max_limit", + "free_tier", + "duration", + "metric", + "unit", + "values", + "display_name", + ) + class ValuesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: int + def __init__( + self, key: _Optional[str] = ..., value: _Optional[int] = ... + ) -> None: ... + + NAME_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + DEFAULT_LIMIT_FIELD_NUMBER: _ClassVar[int] + MAX_LIMIT_FIELD_NUMBER: _ClassVar[int] + FREE_TIER_FIELD_NUMBER: _ClassVar[int] + DURATION_FIELD_NUMBER: _ClassVar[int] + METRIC_FIELD_NUMBER: _ClassVar[int] + UNIT_FIELD_NUMBER: _ClassVar[int] + VALUES_FIELD_NUMBER: _ClassVar[int] + DISPLAY_NAME_FIELD_NUMBER: _ClassVar[int] + name: str + description: str + default_limit: int + max_limit: int + free_tier: int + duration: str + metric: str + unit: str + values: _containers.ScalarMap[str, int] + display_name: str + def __init__( + self, + name: _Optional[str] = ..., + description: _Optional[str] = ..., + default_limit: _Optional[int] = ..., + max_limit: _Optional[int] = ..., + free_tier: _Optional[int] = ..., + duration: _Optional[str] = ..., + metric: _Optional[str] = ..., + unit: _Optional[str] = ..., + values: _Optional[_Mapping[str, int]] = ..., + display_name: _Optional[str] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/resource.proto b/venv/lib/python3.12/site-packages/google/api/resource.proto new file mode 100644 index 0000000..2a5213b --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/resource.proto @@ -0,0 +1,242 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "ResourceProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.FieldOptions { + // An annotation that describes a resource reference, see + // [ResourceReference][]. + google.api.ResourceReference resource_reference = 1055; +} + +extend google.protobuf.FileOptions { + // An annotation that describes a resource definition without a corresponding + // message; see [ResourceDescriptor][]. + repeated google.api.ResourceDescriptor resource_definition = 1053; +} + +extend google.protobuf.MessageOptions { + // An annotation that describes a resource definition, see + // [ResourceDescriptor][]. + google.api.ResourceDescriptor resource = 1053; +} + +// A simple descriptor of a resource type. +// +// ResourceDescriptor annotates a resource message (either by means of a +// protobuf annotation or use in the service config), and associates the +// resource's schema, the resource type, and the pattern of the resource name. +// +// Example: +// +// message Topic { +// // Indicates this message defines a resource schema. +// // Declares the resource type in the format of {service}/{kind}. +// // For Kubernetes resources, the format is {api group}/{kind}. +// option (google.api.resource) = { +// type: "pubsub.googleapis.com/Topic" +// pattern: "projects/{project}/topics/{topic}" +// }; +// } +// +// The ResourceDescriptor Yaml config will look like: +// +// resources: +// - type: "pubsub.googleapis.com/Topic" +// pattern: "projects/{project}/topics/{topic}" +// +// Sometimes, resources have multiple patterns, typically because they can +// live under multiple parents. +// +// Example: +// +// message LogEntry { +// option (google.api.resource) = { +// type: "logging.googleapis.com/LogEntry" +// pattern: "projects/{project}/logs/{log}" +// pattern: "folders/{folder}/logs/{log}" +// pattern: "organizations/{organization}/logs/{log}" +// pattern: "billingAccounts/{billing_account}/logs/{log}" +// }; +// } +// +// The ResourceDescriptor Yaml config will look like: +// +// resources: +// - type: 'logging.googleapis.com/LogEntry' +// pattern: "projects/{project}/logs/{log}" +// pattern: "folders/{folder}/logs/{log}" +// pattern: "organizations/{organization}/logs/{log}" +// pattern: "billingAccounts/{billing_account}/logs/{log}" +message ResourceDescriptor { + // A description of the historical or future-looking state of the + // resource pattern. + enum History { + // The "unset" value. + HISTORY_UNSPECIFIED = 0; + + // The resource originally had one pattern and launched as such, and + // additional patterns were added later. + ORIGINALLY_SINGLE_PATTERN = 1; + + // The resource has one pattern, but the API owner expects to add more + // later. (This is the inverse of ORIGINALLY_SINGLE_PATTERN, and prevents + // that from being necessary once there are multiple patterns.) + FUTURE_MULTI_PATTERN = 2; + } + + // A flag representing a specific style that a resource claims to conform to. + enum Style { + // The unspecified value. Do not use. + STYLE_UNSPECIFIED = 0; + + // This resource is intended to be "declarative-friendly". + // + // Declarative-friendly resources must be more strictly consistent, and + // setting this to true communicates to tools that this resource should + // adhere to declarative-friendly expectations. + // + // Note: This is used by the API linter (linter.aip.dev) to enable + // additional checks. + DECLARATIVE_FRIENDLY = 1; + } + + // The resource type. It must be in the format of + // {service_name}/{resource_type_kind}. The `resource_type_kind` must be + // singular and must not include version numbers. + // + // Example: `storage.googleapis.com/Bucket` + // + // The value of the resource_type_kind must follow the regular expression + // /[A-Za-z][a-zA-Z0-9]+/. It should start with an upper case character and + // should use PascalCase (UpperCamelCase). The maximum number of + // characters allowed for the `resource_type_kind` is 100. + string type = 1; + + // Optional. The relative resource name pattern associated with this resource + // type. The DNS prefix of the full resource name shouldn't be specified here. + // + // The path pattern must follow the syntax, which aligns with HTTP binding + // syntax: + // + // Template = Segment { "/" Segment } ; + // Segment = LITERAL | Variable ; + // Variable = "{" LITERAL "}" ; + // + // Examples: + // + // - "projects/{project}/topics/{topic}" + // - "projects/{project}/knowledgeBases/{knowledge_base}" + // + // The components in braces correspond to the IDs for each resource in the + // hierarchy. It is expected that, if multiple patterns are provided, + // the same component name (e.g. "project") refers to IDs of the same + // type of resource. + repeated string pattern = 2; + + // Optional. The field on the resource that designates the resource name + // field. If omitted, this is assumed to be "name". + string name_field = 3; + + // Optional. The historical or future-looking state of the resource pattern. + // + // Example: + // + // // The InspectTemplate message originally only supported resource + // // names with organization, and project was added later. + // message InspectTemplate { + // option (google.api.resource) = { + // type: "dlp.googleapis.com/InspectTemplate" + // pattern: + // "organizations/{organization}/inspectTemplates/{inspect_template}" + // pattern: "projects/{project}/inspectTemplates/{inspect_template}" + // history: ORIGINALLY_SINGLE_PATTERN + // }; + // } + History history = 4; + + // The plural name used in the resource name and permission names, such as + // 'projects' for the resource name of 'projects/{project}' and the permission + // name of 'cloudresourcemanager.googleapis.com/projects.get'. One exception + // to this is for Nested Collections that have stuttering names, as defined + // in [AIP-122](https://google.aip.dev/122#nested-collections), where the + // collection ID in the resource name pattern does not necessarily directly + // match the `plural` value. + // + // It is the same concept of the `plural` field in k8s CRD spec + // https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/ + // + // Note: The plural form is required even for singleton resources. See + // https://aip.dev/156 + string plural = 5; + + // The same concept of the `singular` field in k8s CRD spec + // https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/ + // Such as "project" for the `resourcemanager.googleapis.com/Project` type. + string singular = 6; + + // Style flag(s) for this resource. + // These indicate that a resource is expected to conform to a given + // style. See the specific style flags for additional information. + repeated Style style = 10; +} + +// Defines a proto annotation that describes a string field that refers to +// an API resource. +message ResourceReference { + // The resource type that the annotated field references. + // + // Example: + // + // message Subscription { + // string topic = 2 [(google.api.resource_reference) = { + // type: "pubsub.googleapis.com/Topic" + // }]; + // } + // + // Occasionally, a field may reference an arbitrary resource. In this case, + // APIs use the special value * in their resource reference. + // + // Example: + // + // message GetIamPolicyRequest { + // string resource = 2 [(google.api.resource_reference) = { + // type: "*" + // }]; + // } + string type = 1; + + // The resource type of a child collection that the annotated field + // references. This is useful for annotating the `parent` field that + // doesn't have a fixed resource type. + // + // Example: + // + // message ListLogEntriesRequest { + // string parent = 1 [(google.api.resource_reference) = { + // child_type: "logging.googleapis.com/LogEntry" + // }; + // } + string child_type = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/api/resource_pb2.py b/venv/lib/python3.12/site-packages/google/api/resource_pb2.py new file mode 100644 index 0000000..c97e1f4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/resource_pb2.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/resource.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/resource.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x19google/api/resource.proto\x12\ngoogle.api\x1a google/protobuf/descriptor.proto"\xee\x02\n\x12ResourceDescriptor\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07pattern\x18\x02 \x03(\t\x12\x12\n\nname_field\x18\x03 \x01(\t\x12\x37\n\x07history\x18\x04 \x01(\x0e\x32&.google.api.ResourceDescriptor.History\x12\x0e\n\x06plural\x18\x05 \x01(\t\x12\x10\n\x08singular\x18\x06 \x01(\t\x12\x33\n\x05style\x18\n \x03(\x0e\x32$.google.api.ResourceDescriptor.Style"[\n\x07History\x12\x17\n\x13HISTORY_UNSPECIFIED\x10\x00\x12\x1d\n\x19ORIGINALLY_SINGLE_PATTERN\x10\x01\x12\x18\n\x14\x46UTURE_MULTI_PATTERN\x10\x02"8\n\x05Style\x12\x15\n\x11STYLE_UNSPECIFIED\x10\x00\x12\x18\n\x14\x44\x45\x43LARATIVE_FRIENDLY\x10\x01"5\n\x11ResourceReference\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x12\n\nchild_type\x18\x02 \x01(\t:Y\n\x12resource_reference\x12\x1d.google.protobuf.FieldOptions\x18\x9f\x08 \x01(\x0b\x32\x1d.google.api.ResourceReference:Z\n\x13resource_definition\x12\x1c.google.protobuf.FileOptions\x18\x9d\x08 \x03(\x0b\x32\x1e.google.api.ResourceDescriptor:R\n\x08resource\x12\x1f.google.protobuf.MessageOptions\x18\x9d\x08 \x01(\x0b\x32\x1e.google.api.ResourceDescriptorBk\n\x0e\x63om.google.apiB\rResourceProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.resource_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\rResourceProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI" + _globals["_RESOURCEDESCRIPTOR"]._serialized_start = 76 + _globals["_RESOURCEDESCRIPTOR"]._serialized_end = 442 + _globals["_RESOURCEDESCRIPTOR_HISTORY"]._serialized_start = 293 + _globals["_RESOURCEDESCRIPTOR_HISTORY"]._serialized_end = 384 + _globals["_RESOURCEDESCRIPTOR_STYLE"]._serialized_start = 386 + _globals["_RESOURCEDESCRIPTOR_STYLE"]._serialized_end = 442 + _globals["_RESOURCEREFERENCE"]._serialized_start = 444 + _globals["_RESOURCEREFERENCE"]._serialized_end = 497 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/resource_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/resource_pb2.pyi new file mode 100644 index 0000000..806682c --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/resource_pb2.pyi @@ -0,0 +1,93 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pb2 as _descriptor_pb2 +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor +RESOURCE_REFERENCE_FIELD_NUMBER: _ClassVar[int] +resource_reference: _descriptor.FieldDescriptor +RESOURCE_DEFINITION_FIELD_NUMBER: _ClassVar[int] +resource_definition: _descriptor.FieldDescriptor +RESOURCE_FIELD_NUMBER: _ClassVar[int] +resource: _descriptor.FieldDescriptor + +class ResourceDescriptor(_message.Message): + __slots__ = ( + "type", + "pattern", + "name_field", + "history", + "plural", + "singular", + "style", + ) + class History(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + HISTORY_UNSPECIFIED: _ClassVar[ResourceDescriptor.History] + ORIGINALLY_SINGLE_PATTERN: _ClassVar[ResourceDescriptor.History] + FUTURE_MULTI_PATTERN: _ClassVar[ResourceDescriptor.History] + + HISTORY_UNSPECIFIED: ResourceDescriptor.History + ORIGINALLY_SINGLE_PATTERN: ResourceDescriptor.History + FUTURE_MULTI_PATTERN: ResourceDescriptor.History + class Style(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + STYLE_UNSPECIFIED: _ClassVar[ResourceDescriptor.Style] + DECLARATIVE_FRIENDLY: _ClassVar[ResourceDescriptor.Style] + + STYLE_UNSPECIFIED: ResourceDescriptor.Style + DECLARATIVE_FRIENDLY: ResourceDescriptor.Style + TYPE_FIELD_NUMBER: _ClassVar[int] + PATTERN_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_FIELD_NUMBER: _ClassVar[int] + HISTORY_FIELD_NUMBER: _ClassVar[int] + PLURAL_FIELD_NUMBER: _ClassVar[int] + SINGULAR_FIELD_NUMBER: _ClassVar[int] + STYLE_FIELD_NUMBER: _ClassVar[int] + type: str + pattern: _containers.RepeatedScalarFieldContainer[str] + name_field: str + history: ResourceDescriptor.History + plural: str + singular: str + style: _containers.RepeatedScalarFieldContainer[ResourceDescriptor.Style] + def __init__( + self, + type: _Optional[str] = ..., + pattern: _Optional[_Iterable[str]] = ..., + name_field: _Optional[str] = ..., + history: _Optional[_Union[ResourceDescriptor.History, str]] = ..., + plural: _Optional[str] = ..., + singular: _Optional[str] = ..., + style: _Optional[_Iterable[_Union[ResourceDescriptor.Style, str]]] = ..., + ) -> None: ... + +class ResourceReference(_message.Message): + __slots__ = ("type", "child_type") + TYPE_FIELD_NUMBER: _ClassVar[int] + CHILD_TYPE_FIELD_NUMBER: _ClassVar[int] + type: str + child_type: str + def __init__( + self, type: _Optional[str] = ..., child_type: _Optional[str] = ... + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/routing.proto b/venv/lib/python3.12/site-packages/google/api/routing.proto new file mode 100644 index 0000000..020d32f --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/routing.proto @@ -0,0 +1,465 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "RoutingProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See RoutingRule. + google.api.RoutingRule routing = 72295729; +} + +// Specifies the routing information that should be sent along with the request +// in the form of routing header. +// **NOTE:** All service configuration rules follow the "last one wins" order. +// +// The examples below will apply to an RPC which has the following request type: +// +// Message Definition: +// +// message Request { +// // The name of the Table +// // Values can be of the following formats: +// // - `projects//tables/` +// // - `projects//instances//tables/
` +// // - `region//zones//tables/
` +// string table_name = 1; +// +// // This value specifies routing for replication. +// // It can be in the following formats: +// // - `profiles/` +// // - a legacy `profile_id` that can be any string +// string app_profile_id = 2; +// } +// +// Example message: +// +// { +// table_name: projects/proj_foo/instances/instance_bar/table/table_baz, +// app_profile_id: profiles/prof_qux +// } +// +// The routing header consists of one or multiple key-value pairs. The order of +// the key-value pairs is undefined, the order of the `routing_parameters` in +// the `RoutingRule` only matters for the evaluation order of the path +// templates when `field` is the same. See the examples below for more details. +// +// Every key and value in the routing header must be percent-encoded, +// and joined together in the following format: `key1=value1&key2=value2`. +// The examples below skip the percent-encoding for readability. +// +// Example 1 +// +// Extracting a field from the request to put into the routing header +// unchanged, with the key equal to the field name. +// +// annotation: +// +// option (google.api.routing) = { +// // Take the `app_profile_id`. +// routing_parameters { +// field: "app_profile_id" +// } +// }; +// +// result: +// +// x-goog-request-params: app_profile_id=profiles/prof_qux +// +// Example 2 +// +// Extracting a field from the request to put into the routing header +// unchanged, with the key different from the field name. +// +// annotation: +// +// option (google.api.routing) = { +// // Take the `app_profile_id`, but name it `routing_id` in the header. +// routing_parameters { +// field: "app_profile_id" +// path_template: "{routing_id=**}" +// } +// }; +// +// result: +// +// x-goog-request-params: routing_id=profiles/prof_qux +// +// Example 3 +// +// Extracting a field from the request to put into the routing +// header, while matching a path template syntax on the field's value. +// +// NB: it is more useful to send nothing than to send garbage for the purpose +// of dynamic routing, since garbage pollutes cache. Thus the matching. +// +// Sub-example 3a +// +// The field matches the template. +// +// annotation: +// +// option (google.api.routing) = { +// // Take the `table_name`, if it's well-formed (with project-based +// // syntax). +// routing_parameters { +// field: "table_name" +// path_template: "{table_name=projects/*/instances/*/**}" +// } +// }; +// +// result: +// +// x-goog-request-params: +// table_name=projects/proj_foo/instances/instance_bar/table/table_baz +// +// Sub-example 3b +// +// The field does not match the template. +// +// annotation: +// +// option (google.api.routing) = { +// // Take the `table_name`, if it's well-formed (with region-based +// // syntax). +// routing_parameters { +// field: "table_name" +// path_template: "{table_name=regions/*/zones/*/**}" +// } +// }; +// +// result: +// +// +// +// Sub-example 3c +// +// Multiple alternative conflictingly named path templates are +// specified. The one that matches is used to construct the header. +// +// annotation: +// +// option (google.api.routing) = { +// // Take the `table_name`, if it's well-formed, whether +// // using the region- or projects-based syntax. +// +// routing_parameters { +// field: "table_name" +// path_template: "{table_name=regions/*/zones/*/**}" +// } +// routing_parameters { +// field: "table_name" +// path_template: "{table_name=projects/*/instances/*/**}" +// } +// }; +// +// result: +// +// x-goog-request-params: +// table_name=projects/proj_foo/instances/instance_bar/table/table_baz +// +// Example 4 +// +// Extracting a single routing header key-value pair by matching a +// template syntax on (a part of) a single request field. +// +// annotation: +// +// option (google.api.routing) = { +// // Take just the project id from the `table_name` field. +// routing_parameters { +// field: "table_name" +// path_template: "{routing_id=projects/*}/**" +// } +// }; +// +// result: +// +// x-goog-request-params: routing_id=projects/proj_foo +// +// Example 5 +// +// Extracting a single routing header key-value pair by matching +// several conflictingly named path templates on (parts of) a single request +// field. The last template to match "wins" the conflict. +// +// annotation: +// +// option (google.api.routing) = { +// // If the `table_name` does not have instances information, +// // take just the project id for routing. +// // Otherwise take project + instance. +// +// routing_parameters { +// field: "table_name" +// path_template: "{routing_id=projects/*}/**" +// } +// routing_parameters { +// field: "table_name" +// path_template: "{routing_id=projects/*/instances/*}/**" +// } +// }; +// +// result: +// +// x-goog-request-params: +// routing_id=projects/proj_foo/instances/instance_bar +// +// Example 6 +// +// Extracting multiple routing header key-value pairs by matching +// several non-conflicting path templates on (parts of) a single request field. +// +// Sub-example 6a +// +// Make the templates strict, so that if the `table_name` does not +// have an instance information, nothing is sent. +// +// annotation: +// +// option (google.api.routing) = { +// // The routing code needs two keys instead of one composite +// // but works only for the tables with the "project-instance" name +// // syntax. +// +// routing_parameters { +// field: "table_name" +// path_template: "{project_id=projects/*}/instances/*/**" +// } +// routing_parameters { +// field: "table_name" +// path_template: "projects/*/{instance_id=instances/*}/**" +// } +// }; +// +// result: +// +// x-goog-request-params: +// project_id=projects/proj_foo&instance_id=instances/instance_bar +// +// Sub-example 6b +// +// Make the templates loose, so that if the `table_name` does not +// have an instance information, just the project id part is sent. +// +// annotation: +// +// option (google.api.routing) = { +// // The routing code wants two keys instead of one composite +// // but will work with just the `project_id` for tables without +// // an instance in the `table_name`. +// +// routing_parameters { +// field: "table_name" +// path_template: "{project_id=projects/*}/**" +// } +// routing_parameters { +// field: "table_name" +// path_template: "projects/*/{instance_id=instances/*}/**" +// } +// }; +// +// result (is the same as 6a for our example message because it has the instance +// information): +// +// x-goog-request-params: +// project_id=projects/proj_foo&instance_id=instances/instance_bar +// +// Example 7 +// +// Extracting multiple routing header key-value pairs by matching +// several path templates on multiple request fields. +// +// NB: note that here there is no way to specify sending nothing if one of the +// fields does not match its template. E.g. if the `table_name` is in the wrong +// format, the `project_id` will not be sent, but the `routing_id` will be. +// The backend routing code has to be aware of that and be prepared to not +// receive a full complement of keys if it expects multiple. +// +// annotation: +// +// option (google.api.routing) = { +// // The routing needs both `project_id` and `routing_id` +// // (from the `app_profile_id` field) for routing. +// +// routing_parameters { +// field: "table_name" +// path_template: "{project_id=projects/*}/**" +// } +// routing_parameters { +// field: "app_profile_id" +// path_template: "{routing_id=**}" +// } +// }; +// +// result: +// +// x-goog-request-params: +// project_id=projects/proj_foo&routing_id=profiles/prof_qux +// +// Example 8 +// +// Extracting a single routing header key-value pair by matching +// several conflictingly named path templates on several request fields. The +// last template to match "wins" the conflict. +// +// annotation: +// +// option (google.api.routing) = { +// // The `routing_id` can be a project id or a region id depending on +// // the table name format, but only if the `app_profile_id` is not set. +// // If `app_profile_id` is set it should be used instead. +// +// routing_parameters { +// field: "table_name" +// path_template: "{routing_id=projects/*}/**" +// } +// routing_parameters { +// field: "table_name" +// path_template: "{routing_id=regions/*}/**" +// } +// routing_parameters { +// field: "app_profile_id" +// path_template: "{routing_id=**}" +// } +// }; +// +// result: +// +// x-goog-request-params: routing_id=profiles/prof_qux +// +// Example 9 +// +// Bringing it all together. +// +// annotation: +// +// option (google.api.routing) = { +// // For routing both `table_location` and a `routing_id` are needed. +// // +// // table_location can be either an instance id or a region+zone id. +// // +// // For `routing_id`, take the value of `app_profile_id` +// // - If it's in the format `profiles/`, send +// // just the `` part. +// // - If it's any other literal, send it as is. +// // If the `app_profile_id` is empty, and the `table_name` starts with +// // the project_id, send that instead. +// +// routing_parameters { +// field: "table_name" +// path_template: "projects/*/{table_location=instances/*}/tables/*" +// } +// routing_parameters { +// field: "table_name" +// path_template: "{table_location=regions/*/zones/*}/tables/*" +// } +// routing_parameters { +// field: "table_name" +// path_template: "{routing_id=projects/*}/**" +// } +// routing_parameters { +// field: "app_profile_id" +// path_template: "{routing_id=**}" +// } +// routing_parameters { +// field: "app_profile_id" +// path_template: "profiles/{routing_id=*}" +// } +// }; +// +// result: +// +// x-goog-request-params: +// table_location=instances/instance_bar&routing_id=prof_qux +message RoutingRule { + // A collection of Routing Parameter specifications. + // **NOTE:** If multiple Routing Parameters describe the same key + // (via the `path_template` field or via the `field` field when + // `path_template` is not provided), "last one wins" rule + // determines which Parameter gets used. + // See the examples for more details. + repeated RoutingParameter routing_parameters = 2; +} + +// A projection from an input message to the GRPC or REST header. +message RoutingParameter { + // A request field to extract the header key-value pair from. + string field = 1; + + // A pattern matching the key-value field. Optional. + // If not specified, the whole field specified in the `field` field will be + // taken as value, and its name used as key. If specified, it MUST contain + // exactly one named segment (along with any number of unnamed segments) The + // pattern will be matched over the field specified in the `field` field, then + // if the match is successful: + // - the name of the single named segment will be used as a header name, + // - the match value of the segment will be used as a header value; + // if the match is NOT successful, nothing will be sent. + // + // Example: + // + // -- This is a field in the request message + // | that the header value will be extracted from. + // | + // | -- This is the key name in the + // | | routing header. + // V | + // field: "table_name" v + // path_template: "projects/*/{table_location=instances/*}/tables/*" + // ^ ^ + // | | + // In the {} brackets is the pattern that -- | + // specifies what to extract from the | + // field as a value to be sent. | + // | + // The string in the field must match the whole pattern -- + // before brackets, inside brackets, after brackets. + // + // When looking at this specific example, we can see that: + // - A key-value pair with the key `table_location` + // and the value matching `instances/*` should be added + // to the x-goog-request-params routing header. + // - The value is extracted from the request message's `table_name` field + // if it matches the full pattern specified: + // `projects/*/instances/*/tables/*`. + // + // **NB:** If the `path_template` field is not provided, the key name is + // equal to the field name, and the whole field should be sent as a value. + // This makes the pattern for the field and the value functionally equivalent + // to `**`, and the configuration + // + // { + // field: "table_name" + // } + // + // is a functionally equivalent shorthand to: + // + // { + // field: "table_name" + // path_template: "{table_name=**}" + // } + // + // See Example 1 for more details. + string path_template = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/api/routing_pb2.py b/venv/lib/python3.12/site-packages/google/api/routing_pb2.py new file mode 100644 index 0000000..e05ddcf --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/routing_pb2.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/routing.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/routing.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x18google/api/routing.proto\x12\ngoogle.api\x1a google/protobuf/descriptor.proto"G\n\x0bRoutingRule\x12\x38\n\x12routing_parameters\x18\x02 \x03(\x0b\x32\x1c.google.api.RoutingParameter"8\n\x10RoutingParameter\x12\r\n\x05\x66ield\x18\x01 \x01(\t\x12\x15\n\rpath_template\x18\x02 \x01(\t:K\n\x07routing\x12\x1e.google.protobuf.MethodOptions\x18\xb1\xca\xbc" \x01(\x0b\x32\x17.google.api.RoutingRuleBj\n\x0e\x63om.google.apiB\x0cRoutingProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.routing_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\014RoutingProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI" + _globals["_ROUTINGRULE"]._serialized_start = 74 + _globals["_ROUTINGRULE"]._serialized_end = 145 + _globals["_ROUTINGPARAMETER"]._serialized_start = 147 + _globals["_ROUTINGPARAMETER"]._serialized_end = 203 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/routing_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/routing_pb2.pyi new file mode 100644 index 0000000..39fe16e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/routing_pb2.pyi @@ -0,0 +1,49 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pb2 as _descriptor_pb2 +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor +ROUTING_FIELD_NUMBER: _ClassVar[int] +routing: _descriptor.FieldDescriptor + +class RoutingRule(_message.Message): + __slots__ = ("routing_parameters",) + ROUTING_PARAMETERS_FIELD_NUMBER: _ClassVar[int] + routing_parameters: _containers.RepeatedCompositeFieldContainer[RoutingParameter] + def __init__( + self, + routing_parameters: _Optional[ + _Iterable[_Union[RoutingParameter, _Mapping]] + ] = ..., + ) -> None: ... + +class RoutingParameter(_message.Message): + __slots__ = ("field", "path_template") + FIELD_FIELD_NUMBER: _ClassVar[int] + PATH_TEMPLATE_FIELD_NUMBER: _ClassVar[int] + field: str + path_template: str + def __init__( + self, field: _Optional[str] = ..., path_template: _Optional[str] = ... + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/service.proto b/venv/lib/python3.12/site-packages/google/api/service.proto new file mode 100644 index 0000000..9952ca0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/service.proto @@ -0,0 +1,190 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/auth.proto"; +import "google/api/backend.proto"; +import "google/api/billing.proto"; +import "google/api/client.proto"; +import "google/api/context.proto"; +import "google/api/control.proto"; +import "google/api/documentation.proto"; +import "google/api/endpoint.proto"; +import "google/api/http.proto"; +import "google/api/log.proto"; +import "google/api/logging.proto"; +import "google/api/metric.proto"; +import "google/api/monitored_resource.proto"; +import "google/api/monitoring.proto"; +import "google/api/quota.proto"; +import "google/api/source_info.proto"; +import "google/api/system_parameter.proto"; +import "google/api/usage.proto"; +import "google/protobuf/api.proto"; +import "google/protobuf/type.proto"; +import "google/protobuf/wrappers.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "ServiceProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// `Service` is the root object of Google API service configuration (service +// config). It describes the basic information about a logical service, +// such as the service name and the user-facing title, and delegates other +// aspects to sub-sections. Each sub-section is either a proto message or a +// repeated proto message that configures a specific aspect, such as auth. +// For more information, see each proto message definition. +// +// Example: +// +// type: google.api.Service +// name: calendar.googleapis.com +// title: Google Calendar API +// apis: +// - name: google.calendar.v3.Calendar +// +// visibility: +// rules: +// - selector: "google.calendar.v3.*" +// restriction: PREVIEW +// backend: +// rules: +// - selector: "google.calendar.v3.*" +// address: calendar.example.com +// +// authentication: +// providers: +// - id: google_calendar_auth +// jwks_uri: https://www.googleapis.com/oauth2/v1/certs +// issuer: https://securetoken.google.com +// rules: +// - selector: "*" +// requirements: +// provider_id: google_calendar_auth +message Service { + // The service name, which is a DNS-like logical identifier for the + // service, such as `calendar.googleapis.com`. The service name + // typically goes through DNS verification to make sure the owner + // of the service also owns the DNS name. + string name = 1; + + // The product title for this service, it is the name displayed in Google + // Cloud Console. + string title = 2; + + // The Google project that owns this service. + string producer_project_id = 22; + + // A unique ID for a specific instance of this message, typically assigned + // by the client for tracking purpose. Must be no longer than 63 characters + // and only lower case letters, digits, '.', '_' and '-' are allowed. If + // empty, the server may choose to generate one instead. + string id = 33; + + // A list of API interfaces exported by this service. Only the `name` field + // of the [google.protobuf.Api][google.protobuf.Api] needs to be provided by + // the configuration author, as the remaining fields will be derived from the + // IDL during the normalization process. It is an error to specify an API + // interface here which cannot be resolved against the associated IDL files. + repeated google.protobuf.Api apis = 3; + + // A list of all proto message types included in this API service. + // Types referenced directly or indirectly by the `apis` are automatically + // included. Messages which are not referenced but shall be included, such as + // types used by the `google.protobuf.Any` type, should be listed here by + // name by the configuration author. Example: + // + // types: + // - name: google.protobuf.Int32 + repeated google.protobuf.Type types = 4; + + // A list of all enum types included in this API service. Enums referenced + // directly or indirectly by the `apis` are automatically included. Enums + // which are not referenced but shall be included should be listed here by + // name by the configuration author. Example: + // + // enums: + // - name: google.someapi.v1.SomeEnum + repeated google.protobuf.Enum enums = 5; + + // Additional API documentation. + Documentation documentation = 6; + + // API backend configuration. + Backend backend = 8; + + // HTTP configuration. + Http http = 9; + + // Quota configuration. + Quota quota = 10; + + // Auth configuration. + Authentication authentication = 11; + + // Context configuration. + Context context = 12; + + // Configuration controlling usage of this service. + Usage usage = 15; + + // Configuration for network endpoints. If this is empty, then an endpoint + // with the same name as the service is automatically generated to service all + // defined APIs. + repeated Endpoint endpoints = 18; + + // Configuration for the service control plane. + Control control = 21; + + // Defines the logs used by this service. + repeated LogDescriptor logs = 23; + + // Defines the metrics used by this service. + repeated MetricDescriptor metrics = 24; + + // Defines the monitored resources used by this service. This is required + // by the `Service.monitoring` and `Service.logging` configurations. + repeated MonitoredResourceDescriptor monitored_resources = 25; + + // Billing configuration. + Billing billing = 26; + + // Logging configuration. + Logging logging = 27; + + // Monitoring configuration. + Monitoring monitoring = 28; + + // System parameter configuration. + SystemParameters system_parameters = 29; + + // Output only. The source information for this configuration if available. + SourceInfo source_info = 37; + + // Settings for [Google Cloud Client + // libraries](https://cloud.google.com/apis/docs/cloud-client-libraries) + // generated from APIs defined as protocol buffers. + Publishing publishing = 45; + + // Obsolete. Do not use. + // + // This field has no semantic meaning. The service config compiler always + // sets this field to `3`. + google.protobuf.UInt32Value config_version = 20; +} diff --git a/venv/lib/python3.12/site-packages/google/api/service_pb2.py b/venv/lib/python3.12/site-packages/google/api/service_pb2.py new file mode 100644 index 0000000..5ccd118 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/service_pb2.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/service.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/service.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import api_pb2 as google_dot_protobuf_dot_api__pb2 +from google.protobuf import type_pb2 as google_dot_protobuf_dot_type__pb2 +from google.protobuf import wrappers_pb2 as google_dot_protobuf_dot_wrappers__pb2 + +from google.api import auth_pb2 as google_dot_api_dot_auth__pb2 +from google.api import backend_pb2 as google_dot_api_dot_backend__pb2 +from google.api import billing_pb2 as google_dot_api_dot_billing__pb2 +from google.api import client_pb2 as google_dot_api_dot_client__pb2 +from google.api import context_pb2 as google_dot_api_dot_context__pb2 +from google.api import control_pb2 as google_dot_api_dot_control__pb2 +from google.api import documentation_pb2 as google_dot_api_dot_documentation__pb2 +from google.api import endpoint_pb2 as google_dot_api_dot_endpoint__pb2 +from google.api import http_pb2 as google_dot_api_dot_http__pb2 +from google.api import log_pb2 as google_dot_api_dot_log__pb2 +from google.api import logging_pb2 as google_dot_api_dot_logging__pb2 +from google.api import metric_pb2 as google_dot_api_dot_metric__pb2 +from google.api import ( + monitored_resource_pb2 as google_dot_api_dot_monitored__resource__pb2, +) +from google.api import monitoring_pb2 as google_dot_api_dot_monitoring__pb2 +from google.api import quota_pb2 as google_dot_api_dot_quota__pb2 +from google.api import source_info_pb2 as google_dot_api_dot_source__info__pb2 +from google.api import system_parameter_pb2 as google_dot_api_dot_system__parameter__pb2 +from google.api import usage_pb2 as google_dot_api_dot_usage__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n\x18google/api/service.proto\x12\ngoogle.api\x1a\x15google/api/auth.proto\x1a\x18google/api/backend.proto\x1a\x18google/api/billing.proto\x1a\x17google/api/client.proto\x1a\x18google/api/context.proto\x1a\x18google/api/control.proto\x1a\x1egoogle/api/documentation.proto\x1a\x19google/api/endpoint.proto\x1a\x15google/api/http.proto\x1a\x14google/api/log.proto\x1a\x18google/api/logging.proto\x1a\x17google/api/metric.proto\x1a#google/api/monitored_resource.proto\x1a\x1bgoogle/api/monitoring.proto\x1a\x16google/api/quota.proto\x1a\x1cgoogle/api/source_info.proto\x1a!google/api/system_parameter.proto\x1a\x16google/api/usage.proto\x1a\x19google/protobuf/api.proto\x1a\x1agoogle/protobuf/type.proto\x1a\x1egoogle/protobuf/wrappers.proto\"\x82\x08\n\x07Service\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x1b\n\x13producer_project_id\x18\x16 \x01(\t\x12\n\n\x02id\x18! \x01(\t\x12\"\n\x04\x61pis\x18\x03 \x03(\x0b\x32\x14.google.protobuf.Api\x12$\n\x05types\x18\x04 \x03(\x0b\x32\x15.google.protobuf.Type\x12$\n\x05\x65nums\x18\x05 \x03(\x0b\x32\x15.google.protobuf.Enum\x12\x30\n\rdocumentation\x18\x06 \x01(\x0b\x32\x19.google.api.Documentation\x12$\n\x07\x62\x61\x63kend\x18\x08 \x01(\x0b\x32\x13.google.api.Backend\x12\x1e\n\x04http\x18\t \x01(\x0b\x32\x10.google.api.Http\x12 \n\x05quota\x18\n \x01(\x0b\x32\x11.google.api.Quota\x12\x32\n\x0e\x61uthentication\x18\x0b \x01(\x0b\x32\x1a.google.api.Authentication\x12$\n\x07\x63ontext\x18\x0c \x01(\x0b\x32\x13.google.api.Context\x12 \n\x05usage\x18\x0f \x01(\x0b\x32\x11.google.api.Usage\x12'\n\tendpoints\x18\x12 \x03(\x0b\x32\x14.google.api.Endpoint\x12$\n\x07\x63ontrol\x18\x15 \x01(\x0b\x32\x13.google.api.Control\x12'\n\x04logs\x18\x17 \x03(\x0b\x32\x19.google.api.LogDescriptor\x12-\n\x07metrics\x18\x18 \x03(\x0b\x32\x1c.google.api.MetricDescriptor\x12\x44\n\x13monitored_resources\x18\x19 \x03(\x0b\x32'.google.api.MonitoredResourceDescriptor\x12$\n\x07\x62illing\x18\x1a \x01(\x0b\x32\x13.google.api.Billing\x12$\n\x07logging\x18\x1b \x01(\x0b\x32\x13.google.api.Logging\x12*\n\nmonitoring\x18\x1c \x01(\x0b\x32\x16.google.api.Monitoring\x12\x37\n\x11system_parameters\x18\x1d \x01(\x0b\x32\x1c.google.api.SystemParameters\x12+\n\x0bsource_info\x18% \x01(\x0b\x32\x16.google.api.SourceInfo\x12*\n\npublishing\x18- \x01(\x0b\x32\x16.google.api.Publishing\x12\x34\n\x0e\x63onfig_version\x18\x14 \x01(\x0b\x32\x1c.google.protobuf.UInt32ValueBn\n\x0e\x63om.google.apiB\x0cServiceProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3" +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.service_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\014ServiceProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_SERVICE"]._serialized_start = 614 + _globals["_SERVICE"]._serialized_end = 1640 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/service_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/service_pb2.pyi new file mode 100644 index 0000000..93b393a --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/service_pb2.pyi @@ -0,0 +1,170 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import api_pb2 as _api_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import type_pb2 as _type_pb2 +from google.protobuf import wrappers_pb2 as _wrappers_pb2 +from google.protobuf.internal import containers as _containers + +from google.api import auth_pb2 as _auth_pb2 +from google.api import backend_pb2 as _backend_pb2 +from google.api import billing_pb2 as _billing_pb2 +from google.api import client_pb2 as _client_pb2 +from google.api import context_pb2 as _context_pb2 +from google.api import control_pb2 as _control_pb2 +from google.api import documentation_pb2 as _documentation_pb2 +from google.api import endpoint_pb2 as _endpoint_pb2 +from google.api import http_pb2 as _http_pb2 +from google.api import log_pb2 as _log_pb2 +from google.api import logging_pb2 as _logging_pb2 +from google.api import metric_pb2 as _metric_pb2 +from google.api import monitored_resource_pb2 as _monitored_resource_pb2 +from google.api import monitoring_pb2 as _monitoring_pb2 +from google.api import quota_pb2 as _quota_pb2 +from google.api import source_info_pb2 as _source_info_pb2 +from google.api import system_parameter_pb2 as _system_parameter_pb2 +from google.api import usage_pb2 as _usage_pb2 + +DESCRIPTOR: _descriptor.FileDescriptor + +class Service(_message.Message): + __slots__ = ( + "name", + "title", + "producer_project_id", + "id", + "apis", + "types", + "enums", + "documentation", + "backend", + "http", + "quota", + "authentication", + "context", + "usage", + "endpoints", + "control", + "logs", + "metrics", + "monitored_resources", + "billing", + "logging", + "monitoring", + "system_parameters", + "source_info", + "publishing", + "config_version", + ) + NAME_FIELD_NUMBER: _ClassVar[int] + TITLE_FIELD_NUMBER: _ClassVar[int] + PRODUCER_PROJECT_ID_FIELD_NUMBER: _ClassVar[int] + ID_FIELD_NUMBER: _ClassVar[int] + APIS_FIELD_NUMBER: _ClassVar[int] + TYPES_FIELD_NUMBER: _ClassVar[int] + ENUMS_FIELD_NUMBER: _ClassVar[int] + DOCUMENTATION_FIELD_NUMBER: _ClassVar[int] + BACKEND_FIELD_NUMBER: _ClassVar[int] + HTTP_FIELD_NUMBER: _ClassVar[int] + QUOTA_FIELD_NUMBER: _ClassVar[int] + AUTHENTICATION_FIELD_NUMBER: _ClassVar[int] + CONTEXT_FIELD_NUMBER: _ClassVar[int] + USAGE_FIELD_NUMBER: _ClassVar[int] + ENDPOINTS_FIELD_NUMBER: _ClassVar[int] + CONTROL_FIELD_NUMBER: _ClassVar[int] + LOGS_FIELD_NUMBER: _ClassVar[int] + METRICS_FIELD_NUMBER: _ClassVar[int] + MONITORED_RESOURCES_FIELD_NUMBER: _ClassVar[int] + BILLING_FIELD_NUMBER: _ClassVar[int] + LOGGING_FIELD_NUMBER: _ClassVar[int] + MONITORING_FIELD_NUMBER: _ClassVar[int] + SYSTEM_PARAMETERS_FIELD_NUMBER: _ClassVar[int] + SOURCE_INFO_FIELD_NUMBER: _ClassVar[int] + PUBLISHING_FIELD_NUMBER: _ClassVar[int] + CONFIG_VERSION_FIELD_NUMBER: _ClassVar[int] + name: str + title: str + producer_project_id: str + id: str + apis: _containers.RepeatedCompositeFieldContainer[_api_pb2.Api] + types: _containers.RepeatedCompositeFieldContainer[_type_pb2.Type] + enums: _containers.RepeatedCompositeFieldContainer[_type_pb2.Enum] + documentation: _documentation_pb2.Documentation + backend: _backend_pb2.Backend + http: _http_pb2.Http + quota: _quota_pb2.Quota + authentication: _auth_pb2.Authentication + context: _context_pb2.Context + usage: _usage_pb2.Usage + endpoints: _containers.RepeatedCompositeFieldContainer[_endpoint_pb2.Endpoint] + control: _control_pb2.Control + logs: _containers.RepeatedCompositeFieldContainer[_log_pb2.LogDescriptor] + metrics: _containers.RepeatedCompositeFieldContainer[_metric_pb2.MetricDescriptor] + monitored_resources: _containers.RepeatedCompositeFieldContainer[ + _monitored_resource_pb2.MonitoredResourceDescriptor + ] + billing: _billing_pb2.Billing + logging: _logging_pb2.Logging + monitoring: _monitoring_pb2.Monitoring + system_parameters: _system_parameter_pb2.SystemParameters + source_info: _source_info_pb2.SourceInfo + publishing: _client_pb2.Publishing + config_version: _wrappers_pb2.UInt32Value + def __init__( + self, + name: _Optional[str] = ..., + title: _Optional[str] = ..., + producer_project_id: _Optional[str] = ..., + id: _Optional[str] = ..., + apis: _Optional[_Iterable[_Union[_api_pb2.Api, _Mapping]]] = ..., + types: _Optional[_Iterable[_Union[_type_pb2.Type, _Mapping]]] = ..., + enums: _Optional[_Iterable[_Union[_type_pb2.Enum, _Mapping]]] = ..., + documentation: _Optional[ + _Union[_documentation_pb2.Documentation, _Mapping] + ] = ..., + backend: _Optional[_Union[_backend_pb2.Backend, _Mapping]] = ..., + http: _Optional[_Union[_http_pb2.Http, _Mapping]] = ..., + quota: _Optional[_Union[_quota_pb2.Quota, _Mapping]] = ..., + authentication: _Optional[_Union[_auth_pb2.Authentication, _Mapping]] = ..., + context: _Optional[_Union[_context_pb2.Context, _Mapping]] = ..., + usage: _Optional[_Union[_usage_pb2.Usage, _Mapping]] = ..., + endpoints: _Optional[_Iterable[_Union[_endpoint_pb2.Endpoint, _Mapping]]] = ..., + control: _Optional[_Union[_control_pb2.Control, _Mapping]] = ..., + logs: _Optional[_Iterable[_Union[_log_pb2.LogDescriptor, _Mapping]]] = ..., + metrics: _Optional[ + _Iterable[_Union[_metric_pb2.MetricDescriptor, _Mapping]] + ] = ..., + monitored_resources: _Optional[ + _Iterable[ + _Union[_monitored_resource_pb2.MonitoredResourceDescriptor, _Mapping] + ] + ] = ..., + billing: _Optional[_Union[_billing_pb2.Billing, _Mapping]] = ..., + logging: _Optional[_Union[_logging_pb2.Logging, _Mapping]] = ..., + monitoring: _Optional[_Union[_monitoring_pb2.Monitoring, _Mapping]] = ..., + system_parameters: _Optional[ + _Union[_system_parameter_pb2.SystemParameters, _Mapping] + ] = ..., + source_info: _Optional[_Union[_source_info_pb2.SourceInfo, _Mapping]] = ..., + publishing: _Optional[_Union[_client_pb2.Publishing, _Mapping]] = ..., + config_version: _Optional[_Union[_wrappers_pb2.UInt32Value, _Mapping]] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/source_info.proto b/venv/lib/python3.12/site-packages/google/api/source_info.proto new file mode 100644 index 0000000..1d4094e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/source_info.proto @@ -0,0 +1,31 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/any.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "SourceInfoProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Source information used to create a Service Config +message SourceInfo { + // All files used during config generation. + repeated google.protobuf.Any source_files = 1; +} diff --git a/venv/lib/python3.12/site-packages/google/api/source_info_pb2.py b/venv/lib/python3.12/site-packages/google/api/source_info_pb2.py new file mode 100644 index 0000000..9272ce2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/source_info_pb2.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/source_info.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/source_info.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1cgoogle/api/source_info.proto\x12\ngoogle.api\x1a\x19google/protobuf/any.proto"8\n\nSourceInfo\x12*\n\x0csource_files\x18\x01 \x03(\x0b\x32\x14.google.protobuf.AnyBq\n\x0e\x63om.google.apiB\x0fSourceInfoProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.api.source_info_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\017SourceInfoProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_SOURCEINFO"]._serialized_start = 71 + _globals["_SOURCEINFO"]._serialized_end = 127 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/source_info_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/source_info_pb2.pyi new file mode 100644 index 0000000..f21bfe4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/source_info_pb2.pyi @@ -0,0 +1,34 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import any_pb2 as _any_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class SourceInfo(_message.Message): + __slots__ = ("source_files",) + SOURCE_FILES_FIELD_NUMBER: _ClassVar[int] + source_files: _containers.RepeatedCompositeFieldContainer[_any_pb2.Any] + def __init__( + self, source_files: _Optional[_Iterable[_Union[_any_pb2.Any, _Mapping]]] = ... + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/system_parameter.proto b/venv/lib/python3.12/site-packages/google/api/system_parameter.proto new file mode 100644 index 0000000..5ab1f37 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/system_parameter.proto @@ -0,0 +1,96 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "SystemParameterProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// ### System parameter configuration +// +// A system parameter is a special kind of parameter defined by the API +// system, not by an individual API. It is typically mapped to an HTTP header +// and/or a URL query parameter. This configuration specifies which methods +// change the names of the system parameters. +message SystemParameters { + // Define system parameters. + // + // The parameters defined here will override the default parameters + // implemented by the system. If this field is missing from the service + // config, default system parameters will be used. Default system parameters + // and names is implementation-dependent. + // + // Example: define api key for all methods + // + // system_parameters + // rules: + // - selector: "*" + // parameters: + // - name: api_key + // url_query_parameter: api_key + // + // + // Example: define 2 api key names for a specific method. + // + // system_parameters + // rules: + // - selector: "/ListShelves" + // parameters: + // - name: api_key + // http_header: Api-Key1 + // - name: api_key + // http_header: Api-Key2 + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated SystemParameterRule rules = 1; +} + +// Define a system parameter rule mapping system parameter definitions to +// methods. +message SystemParameterRule { + // Selects the methods to which this rule applies. Use '*' to indicate all + // methods in all APIs. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Define parameters. Multiple names may be defined for a parameter. + // For a given method call, only one of them should be used. If multiple + // names are used the behavior is implementation-dependent. + // If none of the specified names are present the behavior is + // parameter-dependent. + repeated SystemParameter parameters = 2; +} + +// Define a parameter's name and location. The parameter may be passed as either +// an HTTP header or a URL query parameter, and if both are passed the behavior +// is implementation-dependent. +message SystemParameter { + // Define the name of the parameter, such as "api_key" . It is case sensitive. + string name = 1; + + // Define the HTTP header name to use for the parameter. It is case + // insensitive. + string http_header = 2; + + // Define the URL query parameter name to use for the parameter. It is case + // sensitive. + string url_query_parameter = 3; +} diff --git a/venv/lib/python3.12/site-packages/google/api/system_parameter_pb2.py b/venv/lib/python3.12/site-packages/google/api/system_parameter_pb2.py new file mode 100644 index 0000000..822ca3a --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/system_parameter_pb2.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/system_parameter.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/system_parameter.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n!google/api/system_parameter.proto\x12\ngoogle.api"B\n\x10SystemParameters\x12.\n\x05rules\x18\x01 \x03(\x0b\x32\x1f.google.api.SystemParameterRule"X\n\x13SystemParameterRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12/\n\nparameters\x18\x02 \x03(\x0b\x32\x1b.google.api.SystemParameter"Q\n\x0fSystemParameter\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0bhttp_header\x18\x02 \x01(\t\x12\x1b\n\x13url_query_parameter\x18\x03 \x01(\tBv\n\x0e\x63om.google.apiB\x14SystemParameterProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.api.system_parameter_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\024SystemParameterProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_SYSTEMPARAMETERS"]._serialized_start = 49 + _globals["_SYSTEMPARAMETERS"]._serialized_end = 115 + _globals["_SYSTEMPARAMETERRULE"]._serialized_start = 117 + _globals["_SYSTEMPARAMETERRULE"]._serialized_end = 205 + _globals["_SYSTEMPARAMETER"]._serialized_start = 207 + _globals["_SYSTEMPARAMETER"]._serialized_end = 288 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/system_parameter_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/system_parameter_pb2.pyi new file mode 100644 index 0000000..14b7b28 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/system_parameter_pb2.pyi @@ -0,0 +1,60 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class SystemParameters(_message.Message): + __slots__ = ("rules",) + RULES_FIELD_NUMBER: _ClassVar[int] + rules: _containers.RepeatedCompositeFieldContainer[SystemParameterRule] + def __init__( + self, rules: _Optional[_Iterable[_Union[SystemParameterRule, _Mapping]]] = ... + ) -> None: ... + +class SystemParameterRule(_message.Message): + __slots__ = ("selector", "parameters") + SELECTOR_FIELD_NUMBER: _ClassVar[int] + PARAMETERS_FIELD_NUMBER: _ClassVar[int] + selector: str + parameters: _containers.RepeatedCompositeFieldContainer[SystemParameter] + def __init__( + self, + selector: _Optional[str] = ..., + parameters: _Optional[_Iterable[_Union[SystemParameter, _Mapping]]] = ..., + ) -> None: ... + +class SystemParameter(_message.Message): + __slots__ = ("name", "http_header", "url_query_parameter") + NAME_FIELD_NUMBER: _ClassVar[int] + HTTP_HEADER_FIELD_NUMBER: _ClassVar[int] + URL_QUERY_PARAMETER_FIELD_NUMBER: _ClassVar[int] + name: str + http_header: str + url_query_parameter: str + def __init__( + self, + name: _Optional[str] = ..., + http_header: _Optional[str] = ..., + url_query_parameter: _Optional[str] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/usage.proto b/venv/lib/python3.12/site-packages/google/api/usage.proto new file mode 100644 index 0000000..df1b93b --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/usage.proto @@ -0,0 +1,76 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "UsageProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Configuration controlling usage of a service. +message Usage { + // Requirements that must be satisfied before a consumer project can use the + // service. Each requirement is of the form /; + // for example 'serviceusage.googleapis.com/billing-enabled'. + // + // For Google APIs, a Terms of Service requirement must be included here. + // Google Cloud APIs must include "serviceusage.googleapis.com/tos/cloud". + // Other Google APIs should include + // "serviceusage.googleapis.com/tos/universal". Additional ToS can be + // included based on the business needs. + repeated string requirements = 1; + + // A list of usage rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated UsageRule rules = 6; + + // The full resource name of a channel used for sending notifications to the + // service producer. + // + // Google Service Management currently only supports + // [Google Cloud Pub/Sub](https://cloud.google.com/pubsub) as a notification + // channel. To use Google Cloud Pub/Sub as the channel, this must be the name + // of a Cloud Pub/Sub topic that uses the Cloud Pub/Sub topic name format + // documented in https://cloud.google.com/pubsub/docs/overview. + string producer_notification_channel = 7; +} + +// Usage configuration rules for the service. +message UsageRule { + // Selects the methods to which this rule applies. Use '*' to indicate all + // methods in all APIs. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Use this rule to configure unregistered calls for the service. Unregistered + // calls are calls that do not contain consumer project identity. + // (Example: calls that do not contain an API key). + // + // WARNING: By default, API methods do not allow unregistered calls, and each + // method call must be identified by a consumer project identity. + bool allow_unregistered_calls = 2; + + // If true, the selected method should skip service control and the control + // plane features, such as quota and billing, will not be available. + // This flag is used by Google Cloud Endpoints to bypass checks for internal + // methods, such as service health check methods. + bool skip_service_control = 3; +} diff --git a/venv/lib/python3.12/site-packages/google/api/usage_pb2.py b/venv/lib/python3.12/site-packages/google/api/usage_pb2.py new file mode 100644 index 0000000..8dce590 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/usage_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/usage.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/usage.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x16google/api/usage.proto\x12\ngoogle.api"j\n\x05Usage\x12\x14\n\x0crequirements\x18\x01 \x03(\t\x12$\n\x05rules\x18\x06 \x03(\x0b\x32\x15.google.api.UsageRule\x12%\n\x1dproducer_notification_channel\x18\x07 \x01(\t"]\n\tUsageRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12 \n\x18\x61llow_unregistered_calls\x18\x02 \x01(\x08\x12\x1c\n\x14skip_service_control\x18\x03 \x01(\x08\x42l\n\x0e\x63om.google.apiB\nUsageProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.usage_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\nUsageProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI" + _globals["_USAGE"]._serialized_start = 38 + _globals["_USAGE"]._serialized_end = 144 + _globals["_USAGERULE"]._serialized_start = 146 + _globals["_USAGERULE"]._serialized_end = 239 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/usage_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/usage_pb2.pyi new file mode 100644 index 0000000..444b29e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/usage_pb2.pyi @@ -0,0 +1,55 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class Usage(_message.Message): + __slots__ = ("requirements", "rules", "producer_notification_channel") + REQUIREMENTS_FIELD_NUMBER: _ClassVar[int] + RULES_FIELD_NUMBER: _ClassVar[int] + PRODUCER_NOTIFICATION_CHANNEL_FIELD_NUMBER: _ClassVar[int] + requirements: _containers.RepeatedScalarFieldContainer[str] + rules: _containers.RepeatedCompositeFieldContainer[UsageRule] + producer_notification_channel: str + def __init__( + self, + requirements: _Optional[_Iterable[str]] = ..., + rules: _Optional[_Iterable[_Union[UsageRule, _Mapping]]] = ..., + producer_notification_channel: _Optional[str] = ..., + ) -> None: ... + +class UsageRule(_message.Message): + __slots__ = ("selector", "allow_unregistered_calls", "skip_service_control") + SELECTOR_FIELD_NUMBER: _ClassVar[int] + ALLOW_UNREGISTERED_CALLS_FIELD_NUMBER: _ClassVar[int] + SKIP_SERVICE_CONTROL_FIELD_NUMBER: _ClassVar[int] + selector: str + allow_unregistered_calls: bool + skip_service_control: bool + def __init__( + self, + selector: _Optional[str] = ..., + allow_unregistered_calls: _Optional[bool] = ..., + skip_service_control: _Optional[bool] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/api/visibility.proto b/venv/lib/python3.12/site-packages/google/api/visibility.proto new file mode 100644 index 0000000..fb5cac6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/visibility.proto @@ -0,0 +1,112 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/visibility;visibility"; +option java_multiple_files = true; +option java_outer_classname = "VisibilityProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.EnumOptions { + // See `VisibilityRule`. + google.api.VisibilityRule enum_visibility = 72295727; +} + +extend google.protobuf.EnumValueOptions { + // See `VisibilityRule`. + google.api.VisibilityRule value_visibility = 72295727; +} + +extend google.protobuf.FieldOptions { + // See `VisibilityRule`. + google.api.VisibilityRule field_visibility = 72295727; +} + +extend google.protobuf.MessageOptions { + // See `VisibilityRule`. + google.api.VisibilityRule message_visibility = 72295727; +} + +extend google.protobuf.MethodOptions { + // See `VisibilityRule`. + google.api.VisibilityRule method_visibility = 72295727; +} + +extend google.protobuf.ServiceOptions { + // See `VisibilityRule`. + google.api.VisibilityRule api_visibility = 72295727; +} + +// `Visibility` restricts service consumer's access to service elements, +// such as whether an application can call a visibility-restricted method. +// The restriction is expressed by applying visibility labels on service +// elements. The visibility labels are elsewhere linked to service consumers. +// +// A service can define multiple visibility labels, but a service consumer +// should be granted at most one visibility label. Multiple visibility +// labels for a single service consumer are not supported. +// +// If an element and all its parents have no visibility label, its visibility +// is unconditionally granted. +// +// Example: +// +// visibility: +// rules: +// - selector: google.calendar.Calendar.EnhancedSearch +// restriction: PREVIEW +// - selector: google.calendar.Calendar.Delegate +// restriction: INTERNAL +// +// Here, all methods are publicly visible except for the restricted methods +// EnhancedSearch and Delegate. +message Visibility { + // A list of visibility rules that apply to individual API elements. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated VisibilityRule rules = 1; +} + +// A visibility rule provides visibility configuration for an individual API +// element. +message VisibilityRule { + // Selects methods, messages, fields, enums, etc. to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // A comma-separated list of visibility labels that apply to the `selector`. + // Any of the listed labels can be used to grant the visibility. + // + // If a rule has multiple labels, removing one of the labels but not all of + // them can break clients. + // + // Example: + // + // visibility: + // rules: + // - selector: google.calendar.Calendar.EnhancedSearch + // restriction: INTERNAL, PREVIEW + // + // Removing INTERNAL from this restriction will break clients that rely on + // this method and only had access to it through INTERNAL. + string restriction = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/api/visibility_pb2.py b/venv/lib/python3.12/site-packages/google/api/visibility_pb2.py new file mode 100644 index 0000000..4d54b6d --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/visibility_pb2.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/api/visibility.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/api/visibility.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1bgoogle/api/visibility.proto\x12\ngoogle.api\x1a google/protobuf/descriptor.proto"7\n\nVisibility\x12)\n\x05rules\x18\x01 \x03(\x0b\x32\x1a.google.api.VisibilityRule"7\n\x0eVisibilityRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12\x13\n\x0brestriction\x18\x02 \x01(\t:T\n\x0f\x65num_visibility\x12\x1c.google.protobuf.EnumOptions\x18\xaf\xca\xbc" \x01(\x0b\x32\x1a.google.api.VisibilityRule:Z\n\x10value_visibility\x12!.google.protobuf.EnumValueOptions\x18\xaf\xca\xbc" \x01(\x0b\x32\x1a.google.api.VisibilityRule:V\n\x10\x66ield_visibility\x12\x1d.google.protobuf.FieldOptions\x18\xaf\xca\xbc" \x01(\x0b\x32\x1a.google.api.VisibilityRule:Z\n\x12message_visibility\x12\x1f.google.protobuf.MessageOptions\x18\xaf\xca\xbc" \x01(\x0b\x32\x1a.google.api.VisibilityRule:X\n\x11method_visibility\x12\x1e.google.protobuf.MethodOptions\x18\xaf\xca\xbc" \x01(\x0b\x32\x1a.google.api.VisibilityRule:V\n\x0e\x61pi_visibility\x12\x1f.google.protobuf.ServiceOptions\x18\xaf\xca\xbc" \x01(\x0b\x32\x1a.google.api.VisibilityRuleBk\n\x0e\x63om.google.apiB\x0fVisibilityProtoP\x01Z?google.golang.org/genproto/googleapis/api/visibility;visibility\xa2\x02\x04GAPIb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.api.visibility_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.apiB\017VisibilityProtoP\001Z?google.golang.org/genproto/googleapis/api/visibility;visibility\242\002\004GAPI" + _globals["_VISIBILITY"]._serialized_start = 77 + _globals["_VISIBILITY"]._serialized_end = 132 + _globals["_VISIBILITYRULE"]._serialized_start = 134 + _globals["_VISIBILITYRULE"]._serialized_end = 189 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/api/visibility_pb2.pyi b/venv/lib/python3.12/site-packages/google/api/visibility_pb2.pyi new file mode 100644 index 0000000..c4d3277 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/api/visibility_pb2.pyi @@ -0,0 +1,56 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pb2 as _descriptor_pb2 +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor +ENUM_VISIBILITY_FIELD_NUMBER: _ClassVar[int] +enum_visibility: _descriptor.FieldDescriptor +VALUE_VISIBILITY_FIELD_NUMBER: _ClassVar[int] +value_visibility: _descriptor.FieldDescriptor +FIELD_VISIBILITY_FIELD_NUMBER: _ClassVar[int] +field_visibility: _descriptor.FieldDescriptor +MESSAGE_VISIBILITY_FIELD_NUMBER: _ClassVar[int] +message_visibility: _descriptor.FieldDescriptor +METHOD_VISIBILITY_FIELD_NUMBER: _ClassVar[int] +method_visibility: _descriptor.FieldDescriptor +API_VISIBILITY_FIELD_NUMBER: _ClassVar[int] +api_visibility: _descriptor.FieldDescriptor + +class Visibility(_message.Message): + __slots__ = ("rules",) + RULES_FIELD_NUMBER: _ClassVar[int] + rules: _containers.RepeatedCompositeFieldContainer[VisibilityRule] + def __init__( + self, rules: _Optional[_Iterable[_Union[VisibilityRule, _Mapping]]] = ... + ) -> None: ... + +class VisibilityRule(_message.Message): + __slots__ = ("selector", "restriction") + SELECTOR_FIELD_NUMBER: _ClassVar[int] + RESTRICTION_FIELD_NUMBER: _ClassVar[int] + selector: str + restriction: str + def __init__( + self, selector: _Optional[str] = ..., restriction: _Optional[str] = ... + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/cloud/__pycache__/common_resources_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/cloud/__pycache__/common_resources_pb2.cpython-312.pyc new file mode 100644 index 0000000..6eda21d Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/cloud/__pycache__/common_resources_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/cloud/__pycache__/extended_operations_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/cloud/__pycache__/extended_operations_pb2.cpython-312.pyc new file mode 100644 index 0000000..903537f Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/cloud/__pycache__/extended_operations_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/cloud/common_resources.proto b/venv/lib/python3.12/site-packages/google/cloud/common_resources.proto new file mode 100644 index 0000000..cd44c54 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/cloud/common_resources.proto @@ -0,0 +1,52 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains stub messages for common resources in GCP. +// It is not intended to be directly generated, and is instead used by +// other tooling to be able to match common resource patterns. +syntax = "proto3"; + +package google.cloud; + +import "google/api/resource.proto"; + + +option (google.api.resource_definition) = { + type: "cloudresourcemanager.googleapis.com/Project" + pattern: "projects/{project}" +}; + + +option (google.api.resource_definition) = { + type: "cloudresourcemanager.googleapis.com/Organization" + pattern: "organizations/{organization}" +}; + + +option (google.api.resource_definition) = { + type: "cloudresourcemanager.googleapis.com/Folder" + pattern: "folders/{folder}" +}; + + +option (google.api.resource_definition) = { + type: "cloudbilling.googleapis.com/BillingAccount" + pattern: "billingAccounts/{billing_account}" +}; + +option (google.api.resource_definition) = { + type: "locations.googleapis.com/Location" + pattern: "projects/{project}/locations/{location}" +}; + diff --git a/venv/lib/python3.12/site-packages/google/cloud/common_resources_pb2.py b/venv/lib/python3.12/site-packages/google/cloud/common_resources_pb2.py new file mode 100644 index 0000000..4f9cd2d --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/cloud/common_resources_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/cloud/common_resources.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/cloud/common_resources.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.api import resource_pb2 as google_dot_api_dot_resource__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n#google/cloud/common_resources.proto\x12\x0cgoogle.cloud\x1a\x19google/api/resource.protoB\xf9\x02\xea\x41\x41\n+cloudresourcemanager.googleapis.com/Project\x12\x12projects/{project}\xea\x41P\n0cloudresourcemanager.googleapis.com/Organization\x12\x1corganizations/{organization}\xea\x41>\n*cloudresourcemanager.googleapis.com/Folder\x12\x10\x66olders/{folder}\xea\x41O\n*cloudbilling.googleapis.com/BillingAccount\x12!billingAccounts/{billing_account}\xea\x41L\n!locations.googleapis.com/Location\x12'projects/{project}/locations/{location}b\x06proto3" +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.cloud.common_resources_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\352AA\n+cloudresourcemanager.googleapis.com/Project\022\022projects/{project}\352AP\n0cloudresourcemanager.googleapis.com/Organization\022\034organizations/{organization}\352A>\n*cloudresourcemanager.googleapis.com/Folder\022\020folders/{folder}\352AO\n*cloudbilling.googleapis.com/BillingAccount\022!billingAccounts/{billing_account}\352AL\n!locations.googleapis.com/Location\022'projects/{project}/locations/{location}" +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/cloud/common_resources_pb2.pyi b/venv/lib/python3.12/site-packages/google/cloud/common_resources_pb2.pyi new file mode 100644 index 0000000..524a461 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/cloud/common_resources_pb2.pyi @@ -0,0 +1,21 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar + +from google.protobuf import descriptor as _descriptor + +from google.api import resource_pb2 as _resource_pb2 + +DESCRIPTOR: _descriptor.FileDescriptor diff --git a/venv/lib/python3.12/site-packages/google/cloud/extended_operations.proto b/venv/lib/python3.12/site-packages/google/cloud/extended_operations.proto new file mode 100644 index 0000000..69f44dd --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/cloud/extended_operations.proto @@ -0,0 +1,150 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains custom annotations that are used by GAPIC generators to +// handle Long Running Operation methods (LRO) that are NOT compliant with +// https://google.aip.dev/151. These annotations are public for technical +// reasons only. Please DO NOT USE them in your protos. +syntax = "proto3"; + +package google.cloud; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/cloud/extendedops;extendedops"; +option java_multiple_files = true; +option java_outer_classname = "ExtendedOperationsProto"; +option java_package = "com.google.cloud"; +option objc_class_prefix = "GAPI"; + +// FieldOptions to match corresponding fields in the initial request, +// polling request and operation response messages. +// +// Example: +// +// In an API-specific operation message: +// +// message MyOperation { +// string http_error_message = 1 [(operation_field) = ERROR_MESSAGE]; +// int32 http_error_status_code = 2 [(operation_field) = ERROR_CODE]; +// string id = 3 [(operation_field) = NAME]; +// Status status = 4 [(operation_field) = STATUS]; +// } +// +// In a polling request message (the one which is used to poll for an LRO +// status): +// +// message MyPollingRequest { +// string operation = 1 [(operation_response_field) = "id"]; +// string project = 2; +// string region = 3; +// } +// +// In an initial request message (the one which starts an LRO): +// +// message MyInitialRequest { +// string my_project = 2 [(operation_request_field) = "project"]; +// string my_region = 3 [(operation_request_field) = "region"]; +// } +// +extend google.protobuf.FieldOptions { + // A field annotation that maps fields in an API-specific Operation object to + // their standard counterparts in google.longrunning.Operation. See + // OperationResponseMapping enum definition. + OperationResponseMapping operation_field = 1149; + + // A field annotation that maps fields in the initial request message + // (the one which started the LRO) to their counterparts in the polling + // request message. For non-standard LRO, the polling response may be missing + // some of the information needed to make a subsequent polling request. The + // missing information (for example, project or region ID) is contained in the + // fields of the initial request message that this annotation must be applied + // to. The string value of the annotation corresponds to the name of the + // counterpart field in the polling request message that the annotated field's + // value will be copied to. + string operation_request_field = 1150; + + // A field annotation that maps fields in the polling request message to their + // counterparts in the initial and/or polling response message. The initial + // and the polling methods return an API-specific Operation object. Some of + // the fields from that response object must be reused in the subsequent + // request (like operation name/ID) to fully identify the polled operation. + // This annotation must be applied to the fields in the polling request + // message, the string value of the annotation must correspond to the name of + // the counterpart field in the Operation response object whose value will be + // copied to the annotated field. + string operation_response_field = 1151; +} + +// MethodOptions to identify the actual service and method used for operation +// status polling. +// +// Example: +// +// In a method, which starts an LRO: +// +// service MyService { +// rpc Foo(MyInitialRequest) returns (MyOperation) { +// option (operation_service) = "MyPollingService"; +// } +// } +// +// In a polling method: +// +// service MyPollingService { +// rpc Get(MyPollingRequest) returns (MyOperation) { +// option (operation_polling_method) = true; +// } +// } +extend google.protobuf.MethodOptions { + // A method annotation that maps an LRO method (the one which starts an LRO) + // to the service, which will be used to poll for the operation status. The + // annotation must be applied to the method which starts an LRO, the string + // value of the annotation must correspond to the name of the service used to + // poll for the operation status. + string operation_service = 1249; + + // A method annotation that marks methods that can be used for polling + // operation status (e.g. the MyPollingService.Get(MyPollingRequest) method). + bool operation_polling_method = 1250; +} + +// An enum to be used to mark the essential (for polling) fields in an +// API-specific Operation object. A custom Operation object may contain many +// different fields, but only few of them are essential to conduct a successful +// polling process. +enum OperationResponseMapping { + // Do not use. + UNDEFINED = 0; + + // A field in an API-specific (custom) Operation object which carries the same + // meaning as google.longrunning.Operation.name. + NAME = 1; + + // A field in an API-specific (custom) Operation object which carries the same + // meaning as google.longrunning.Operation.done. If the annotated field is of + // an enum type, `annotated_field_name == EnumType.DONE` semantics should be + // equivalent to `Operation.done == true`. If the annotated field is of type + // boolean, then it should follow the same semantics as Operation.done. + // Otherwise, a non-empty value should be treated as `Operation.done == true`. + STATUS = 2; + + // A field in an API-specific (custom) Operation object which carries the same + // meaning as google.longrunning.Operation.error.code. + ERROR_CODE = 3; + + // A field in an API-specific (custom) Operation object which carries the same + // meaning as google.longrunning.Operation.error.message. + ERROR_MESSAGE = 4; +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/google/cloud/extended_operations_pb2.py b/venv/lib/python3.12/site-packages/google/cloud/extended_operations_pb2.py new file mode 100644 index 0000000..b5e8e6b --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/cloud/extended_operations_pb2.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/cloud/extended_operations.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 33, + 5, + "", + "google/cloud/extended_operations.proto", +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n&google/cloud/extended_operations.proto\x12\x0cgoogle.cloud\x1a google/protobuf/descriptor.proto*b\n\x18OperationResponseMapping\x12\r\n\tUNDEFINED\x10\x00\x12\x08\n\x04NAME\x10\x01\x12\n\n\x06STATUS\x10\x02\x12\x0e\n\nERROR_CODE\x10\x03\x12\x11\n\rERROR_MESSAGE\x10\x04:_\n\x0foperation_field\x12\x1d.google.protobuf.FieldOptions\x18\xfd\x08 \x01(\x0e\x32&.google.cloud.OperationResponseMapping:?\n\x17operation_request_field\x12\x1d.google.protobuf.FieldOptions\x18\xfe\x08 \x01(\t:@\n\x18operation_response_field\x12\x1d.google.protobuf.FieldOptions\x18\xff\x08 \x01(\t::\n\x11operation_service\x12\x1e.google.protobuf.MethodOptions\x18\xe1\t \x01(\t:A\n\x18operation_polling_method\x12\x1e.google.protobuf.MethodOptions\x18\xe2\t \x01(\x08\x42y\n\x10\x63om.google.cloudB\x17\x45xtendedOperationsProtoP\x01ZCgoogle.golang.org/genproto/googleapis/cloud/extendedops;extendedops\xa2\x02\x04GAPIb\x06proto3" +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.cloud.extended_operations_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\020com.google.cloudB\027ExtendedOperationsProtoP\001ZCgoogle.golang.org/genproto/googleapis/cloud/extendedops;extendedops\242\002\004GAPI" + _globals["_OPERATIONRESPONSEMAPPING"]._serialized_start = 90 + _globals["_OPERATIONRESPONSEMAPPING"]._serialized_end = 188 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/cloud/extended_operations_pb2.pyi b/venv/lib/python3.12/site-packages/google/cloud/extended_operations_pb2.pyi new file mode 100644 index 0000000..8f29d45 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/cloud/extended_operations_pb2.pyi @@ -0,0 +1,45 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pb2 as _descriptor_pb2 +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor + +class OperationResponseMapping(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + UNDEFINED: _ClassVar[OperationResponseMapping] + NAME: _ClassVar[OperationResponseMapping] + STATUS: _ClassVar[OperationResponseMapping] + ERROR_CODE: _ClassVar[OperationResponseMapping] + ERROR_MESSAGE: _ClassVar[OperationResponseMapping] + +UNDEFINED: OperationResponseMapping +NAME: OperationResponseMapping +STATUS: OperationResponseMapping +ERROR_CODE: OperationResponseMapping +ERROR_MESSAGE: OperationResponseMapping +OPERATION_FIELD_FIELD_NUMBER: _ClassVar[int] +operation_field: _descriptor.FieldDescriptor +OPERATION_REQUEST_FIELD_FIELD_NUMBER: _ClassVar[int] +operation_request_field: _descriptor.FieldDescriptor +OPERATION_RESPONSE_FIELD_FIELD_NUMBER: _ClassVar[int] +operation_response_field: _descriptor.FieldDescriptor +OPERATION_SERVICE_FIELD_NUMBER: _ClassVar[int] +operation_service: _descriptor.FieldDescriptor +OPERATION_POLLING_METHOD_FIELD_NUMBER: _ClassVar[int] +operation_polling_method: _descriptor.FieldDescriptor diff --git a/venv/lib/python3.12/site-packages/google/cloud/location/__pycache__/locations_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/cloud/location/__pycache__/locations_pb2.cpython-312.pyc new file mode 100644 index 0000000..3cc403a Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/cloud/location/__pycache__/locations_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/cloud/location/locations.proto b/venv/lib/python3.12/site-packages/google/cloud/location/locations.proto new file mode 100644 index 0000000..36a0b0e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/cloud/location/locations.proto @@ -0,0 +1,108 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.location; + +import "google/api/annotations.proto"; +import "google/protobuf/any.proto"; +import "google/api/client.proto"; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/cloud/location;location"; +option java_multiple_files = true; +option java_outer_classname = "LocationsProto"; +option java_package = "com.google.cloud.location"; + +// An abstract interface that provides location-related information for +// a service. Service-specific metadata is provided through the +// [Location.metadata][google.cloud.location.Location.metadata] field. +service Locations { + option (google.api.default_host) = "cloud.googleapis.com"; + option (google.api.oauth_scopes) = "https://www.googleapis.com/auth/cloud-platform"; + + // Lists information about the supported locations for this service. + rpc ListLocations(ListLocationsRequest) returns (ListLocationsResponse) { + option (google.api.http) = { + get: "/v1/{name=locations}" + additional_bindings { + get: "/v1/{name=projects/*}/locations" + } + }; + } + + // Gets information about a location. + rpc GetLocation(GetLocationRequest) returns (Location) { + option (google.api.http) = { + get: "/v1/{name=locations/*}" + additional_bindings { + get: "/v1/{name=projects/*/locations/*}" + } + }; + } +} + +// The request message for [Locations.ListLocations][google.cloud.location.Locations.ListLocations]. +message ListLocationsRequest { + // The resource that owns the locations collection, if applicable. + string name = 1; + + // The standard list filter. + string filter = 2; + + // The standard list page size. + int32 page_size = 3; + + // The standard list page token. + string page_token = 4; +} + +// The response message for [Locations.ListLocations][google.cloud.location.Locations.ListLocations]. +message ListLocationsResponse { + // A list of locations that matches the specified filter in the request. + repeated Location locations = 1; + + // The standard List next-page token. + string next_page_token = 2; +} + +// The request message for [Locations.GetLocation][google.cloud.location.Locations.GetLocation]. +message GetLocationRequest { + // Resource name for the location. + string name = 1; +} + +// A resource that represents Google Cloud Platform location. +message Location { + // Resource name for the location, which may vary between implementations. + // For example: `"projects/example-project/locations/us-east1"` + string name = 1; + + // The canonical id for this location. For example: `"us-east1"`. + string location_id = 4; + + // The friendly name for this location, typically a nearby city name. + // For example, "Tokyo". + string display_name = 5; + + // Cross-service attributes for the location. For example + // + // {"cloud.googleapis.com/region": "us-east1"} + map labels = 2; + + // Service-specific metadata. For example the available capacity at the given + // location. + google.protobuf.Any metadata = 3; +} diff --git a/venv/lib/python3.12/site-packages/google/cloud/location/locations_pb2.py b/venv/lib/python3.12/site-packages/google/cloud/location/locations_pb2.py new file mode 100644 index 0000000..6bd7877 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/cloud/location/locations_pb2.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/cloud/location/locations.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 33, + 5, + "", + "google/cloud/location/locations.proto", +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 + +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.api import client_pb2 as google_dot_api_dot_client__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n%google/cloud/location/locations.proto\x12\x15google.cloud.location\x1a\x1cgoogle/api/annotations.proto\x1a\x19google/protobuf/any.proto\x1a\x17google/api/client.proto"[\n\x14ListLocationsRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x66ilter\x18\x02 \x01(\t\x12\x11\n\tpage_size\x18\x03 \x01(\x05\x12\x12\n\npage_token\x18\x04 \x01(\t"d\n\x15ListLocationsResponse\x12\x32\n\tlocations\x18\x01 \x03(\x0b\x32\x1f.google.cloud.location.Location\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t""\n\x12GetLocationRequest\x12\x0c\n\x04name\x18\x01 \x01(\t"\xd7\x01\n\x08Location\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0blocation_id\x18\x04 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x05 \x01(\t\x12;\n\x06labels\x18\x02 \x03(\x0b\x32+.google.cloud.location.Location.LabelsEntry\x12&\n\x08metadata\x18\x03 \x01(\x0b\x32\x14.google.protobuf.Any\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x32\xa4\x03\n\tLocations\x12\xab\x01\n\rListLocations\x12+.google.cloud.location.ListLocationsRequest\x1a,.google.cloud.location.ListLocationsResponse"?\x82\xd3\xe4\x93\x02\x39\x12\x14/v1/{name=locations}Z!\x12\x1f/v1/{name=projects/*}/locations\x12\x9e\x01\n\x0bGetLocation\x12).google.cloud.location.GetLocationRequest\x1a\x1f.google.cloud.location.Location"C\x82\xd3\xe4\x93\x02=\x12\x16/v1/{name=locations/*}Z#\x12!/v1/{name=projects/*/locations/*}\x1aH\xca\x41\x14\x63loud.googleapis.com\xd2\x41.https://www.googleapis.com/auth/cloud-platformBo\n\x19\x63om.google.cloud.locationB\x0eLocationsProtoP\x01Z=google.golang.org/genproto/googleapis/cloud/location;location\xf8\x01\x01\x62\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.cloud.location.locations_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\031com.google.cloud.locationB\016LocationsProtoP\001Z=google.golang.org/genproto/googleapis/cloud/location;location\370\001\001" + _globals["_LOCATION_LABELSENTRY"]._loaded_options = None + _globals["_LOCATION_LABELSENTRY"]._serialized_options = b"8\001" + _globals["_LOCATIONS"]._loaded_options = None + _globals[ + "_LOCATIONS" + ]._serialized_options = b"\312A\024cloud.googleapis.com\322A.https://www.googleapis.com/auth/cloud-platform" + _globals["_LOCATIONS"].methods_by_name["ListLocations"]._loaded_options = None + _globals["_LOCATIONS"].methods_by_name[ + "ListLocations" + ]._serialized_options = b"\202\323\344\223\0029\022\024/v1/{name=locations}Z!\022\037/v1/{name=projects/*}/locations" + _globals["_LOCATIONS"].methods_by_name["GetLocation"]._loaded_options = None + _globals["_LOCATIONS"].methods_by_name[ + "GetLocation" + ]._serialized_options = b"\202\323\344\223\002=\022\026/v1/{name=locations/*}Z#\022!/v1/{name=projects/*/locations/*}" + _globals["_LISTLOCATIONSREQUEST"]._serialized_start = 146 + _globals["_LISTLOCATIONSREQUEST"]._serialized_end = 237 + _globals["_LISTLOCATIONSRESPONSE"]._serialized_start = 239 + _globals["_LISTLOCATIONSRESPONSE"]._serialized_end = 339 + _globals["_GETLOCATIONREQUEST"]._serialized_start = 341 + _globals["_GETLOCATIONREQUEST"]._serialized_end = 375 + _globals["_LOCATION"]._serialized_start = 378 + _globals["_LOCATION"]._serialized_end = 593 + _globals["_LOCATION_LABELSENTRY"]._serialized_start = 548 + _globals["_LOCATION_LABELSENTRY"]._serialized_end = 593 + _globals["_LOCATIONS"]._serialized_start = 596 + _globals["_LOCATIONS"]._serialized_end = 1016 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/cloud/location/locations_pb2.pyi b/venv/lib/python3.12/site-packages/google/cloud/location/locations_pb2.pyi new file mode 100644 index 0000000..7454a9d --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/cloud/location/locations_pb2.pyi @@ -0,0 +1,96 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import any_pb2 as _any_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +from google.api import annotations_pb2 as _annotations_pb2 +from google.api import client_pb2 as _client_pb2 + +DESCRIPTOR: _descriptor.FileDescriptor + +class ListLocationsRequest(_message.Message): + __slots__ = ("name", "filter", "page_size", "page_token") + NAME_FIELD_NUMBER: _ClassVar[int] + FILTER_FIELD_NUMBER: _ClassVar[int] + PAGE_SIZE_FIELD_NUMBER: _ClassVar[int] + PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] + name: str + filter: str + page_size: int + page_token: str + def __init__( + self, + name: _Optional[str] = ..., + filter: _Optional[str] = ..., + page_size: _Optional[int] = ..., + page_token: _Optional[str] = ..., + ) -> None: ... + +class ListLocationsResponse(_message.Message): + __slots__ = ("locations", "next_page_token") + LOCATIONS_FIELD_NUMBER: _ClassVar[int] + NEXT_PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] + locations: _containers.RepeatedCompositeFieldContainer[Location] + next_page_token: str + def __init__( + self, + locations: _Optional[_Iterable[_Union[Location, _Mapping]]] = ..., + next_page_token: _Optional[str] = ..., + ) -> None: ... + +class GetLocationRequest(_message.Message): + __slots__ = ("name",) + NAME_FIELD_NUMBER: _ClassVar[int] + name: str + def __init__(self, name: _Optional[str] = ...) -> None: ... + +class Location(_message.Message): + __slots__ = ("name", "location_id", "display_name", "labels", "metadata") + class LabelsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + NAME_FIELD_NUMBER: _ClassVar[int] + LOCATION_ID_FIELD_NUMBER: _ClassVar[int] + DISPLAY_NAME_FIELD_NUMBER: _ClassVar[int] + LABELS_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + name: str + location_id: str + display_name: str + labels: _containers.ScalarMap[str, str] + metadata: _any_pb2.Any + def __init__( + self, + name: _Optional[str] = ..., + location_id: _Optional[str] = ..., + display_name: _Optional[str] = ..., + labels: _Optional[_Mapping[str, str]] = ..., + metadata: _Optional[_Union[_any_pb2.Any, _Mapping]] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/gapic/metadata/__pycache__/gapic_metadata_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/gapic/metadata/__pycache__/gapic_metadata_pb2.cpython-312.pyc new file mode 100644 index 0000000..2a7ddd6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/gapic/metadata/__pycache__/gapic_metadata_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/gapic/metadata/gapic_metadata.proto b/venv/lib/python3.12/site-packages/google/gapic/metadata/gapic_metadata.proto new file mode 100644 index 0000000..0f2855c --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/gapic/metadata/gapic_metadata.proto @@ -0,0 +1,98 @@ +// Copyright 2020 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +syntax = "proto3"; + +package google.gapic.metadata; + +option csharp_namespace = "Google.Gapic.Metadata"; +option go_package = "google.golang.org/genproto/googleapis/gapic/metadata;metadata"; +option java_multiple_files = true; +option java_outer_classname = "GapicMetadataProto"; +option java_package = "com.google.gapic.metadata"; +option php_namespace = "Google\\Gapic\\Metadata"; +option ruby_package = "Google::Gapic::Metadata"; + +// Metadata about a GAPIC library service for a specific combination of API, +// version, and computer language. +message GapicMetadata { + // Schema version of this proto. Current value: 1.0 + string schema = 1; + + // Any human-readable comments to be included in this file. + string comment = 2; + + // Computer language of this generated language. This must be + // spelled out as it spoken in English, with no capitalization or + // separators (e.g. "csharp", "nodejs"). + string language = 3; + + // The proto package containing the API definition for which this + // GAPIC library was generated. + string proto_package = 4; + + // The language-specific library package for this GAPIC library. + string library_package = 5; + + // A map from each proto-defined service to ServiceForTransports, + // which allows listing information about transport-specific + // implementations of the service. + // + // The key is the name of the service as it appears in the .proto + // file. + map services = 6; + + // A map from a transport name to ServiceAsClient, which allows + // listing information about the client objects that implement the + // parent RPC service for the specified transport. + message ServiceForTransport { + // Map of transport name to ServiceAsClient. The key name is the transport, + // lower-cased with no separators (e.g. "grpc", "rest"). + map clients = 1; + + // The API version for the interface definition found in the + // `google.api.api_version` annotation of this proto-defined service during + // code generation. + // This is the value populated by the client in the API version request + // parameter as per + // [AIP-4236](https://google.aip.dev/client-libraries/4236). + string api_version = 2; + } + + // Information about a specific client implementing a proto-defined service. + message ServiceAsClient { + // The name of the library client formatted as it appears in the source code + string library_client = 1; + + // A mapping from each proto-defined RPC name to the the list of + // methods in library_client that implement it. There can be more + // than one library_client method for each RPC. RPCs with no + // library_client methods need not be included. + // + // The key name is the name of the RPC as defined and formatted in + // the proto file. + map rpcs = 2; + } + + // List of GAPIC client methods implementing the proto-defined RPC + // for the transport and service specified in the containing + // structures. + message MethodList { + // List of methods for a specific proto-service client in the + // GAPIC. These names should be formatted as they appear in the + // source code. + repeated string methods = 1; + } +} diff --git a/venv/lib/python3.12/site-packages/google/gapic/metadata/gapic_metadata_pb2.py b/venv/lib/python3.12/site-packages/google/gapic/metadata/gapic_metadata_pb2.py new file mode 100644 index 0000000..4ed230f --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/gapic/metadata/gapic_metadata_pb2.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/gapic/metadata/gapic_metadata.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 33, + 5, + "", + "google/gapic/metadata/gapic_metadata.proto", +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n*google/gapic/metadata/gapic_metadata.proto\x12\x15google.gapic.metadata"\x85\x06\n\rGapicMetadata\x12\x0e\n\x06schema\x18\x01 \x01(\t\x12\x0f\n\x07\x63omment\x18\x02 \x01(\t\x12\x10\n\x08language\x18\x03 \x01(\t\x12\x15\n\rproto_package\x18\x04 \x01(\t\x12\x17\n\x0flibrary_package\x18\x05 \x01(\t\x12\x44\n\x08services\x18\x06 \x03(\x0b\x32\x32.google.gapic.metadata.GapicMetadata.ServicesEntry\x1ai\n\rServicesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12G\n\x05value\x18\x02 \x01(\x0b\x32\x38.google.gapic.metadata.GapicMetadata.ServiceForTransport:\x02\x38\x01\x1a\xe8\x01\n\x13ServiceForTransport\x12V\n\x07\x63lients\x18\x01 \x03(\x0b\x32\x45.google.gapic.metadata.GapicMetadata.ServiceForTransport.ClientsEntry\x12\x13\n\x0b\x61pi_version\x18\x02 \x01(\t\x1a\x64\n\x0c\x43lientsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x43\n\x05value\x18\x02 \x01(\x0b\x32\x34.google.gapic.metadata.GapicMetadata.ServiceAsClient:\x02\x38\x01\x1a\xd5\x01\n\x0fServiceAsClient\x12\x16\n\x0elibrary_client\x18\x01 \x01(\t\x12L\n\x04rpcs\x18\x02 \x03(\x0b\x32>.google.gapic.metadata.GapicMetadata.ServiceAsClient.RpcsEntry\x1a\\\n\tRpcsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12>\n\x05value\x18\x02 \x01(\x0b\x32/.google.gapic.metadata.GapicMetadata.MethodList:\x02\x38\x01\x1a\x1d\n\nMethodList\x12\x0f\n\x07methods\x18\x01 \x03(\tB\xba\x01\n\x19\x63om.google.gapic.metadataB\x12GapicMetadataProtoP\x01Z=google.golang.org/genproto/googleapis/gapic/metadata;metadata\xaa\x02\x15Google.Gapic.Metadata\xca\x02\x15Google\\Gapic\\Metadata\xea\x02\x17Google::Gapic::Metadatab\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.gapic.metadata.gapic_metadata_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\031com.google.gapic.metadataB\022GapicMetadataProtoP\001Z=google.golang.org/genproto/googleapis/gapic/metadata;metadata\252\002\025Google.Gapic.Metadata\312\002\025Google\\Gapic\\Metadata\352\002\027Google::Gapic::Metadata" + _globals["_GAPICMETADATA_SERVICESENTRY"]._loaded_options = None + _globals["_GAPICMETADATA_SERVICESENTRY"]._serialized_options = b"8\001" + _globals["_GAPICMETADATA_SERVICEFORTRANSPORT_CLIENTSENTRY"]._loaded_options = None + _globals[ + "_GAPICMETADATA_SERVICEFORTRANSPORT_CLIENTSENTRY" + ]._serialized_options = b"8\001" + _globals["_GAPICMETADATA_SERVICEASCLIENT_RPCSENTRY"]._loaded_options = None + _globals["_GAPICMETADATA_SERVICEASCLIENT_RPCSENTRY"]._serialized_options = b"8\001" + _globals["_GAPICMETADATA"]._serialized_start = 70 + _globals["_GAPICMETADATA"]._serialized_end = 843 + _globals["_GAPICMETADATA_SERVICESENTRY"]._serialized_start = 256 + _globals["_GAPICMETADATA_SERVICESENTRY"]._serialized_end = 361 + _globals["_GAPICMETADATA_SERVICEFORTRANSPORT"]._serialized_start = 364 + _globals["_GAPICMETADATA_SERVICEFORTRANSPORT"]._serialized_end = 596 + _globals["_GAPICMETADATA_SERVICEFORTRANSPORT_CLIENTSENTRY"]._serialized_start = 496 + _globals["_GAPICMETADATA_SERVICEFORTRANSPORT_CLIENTSENTRY"]._serialized_end = 596 + _globals["_GAPICMETADATA_SERVICEASCLIENT"]._serialized_start = 599 + _globals["_GAPICMETADATA_SERVICEASCLIENT"]._serialized_end = 812 + _globals["_GAPICMETADATA_SERVICEASCLIENT_RPCSENTRY"]._serialized_start = 720 + _globals["_GAPICMETADATA_SERVICEASCLIENT_RPCSENTRY"]._serialized_end = 812 + _globals["_GAPICMETADATA_METHODLIST"]._serialized_start = 814 + _globals["_GAPICMETADATA_METHODLIST"]._serialized_end = 843 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/gapic/metadata/gapic_metadata_pb2.pyi b/venv/lib/python3.12/site-packages/google/gapic/metadata/gapic_metadata_pb2.pyi new file mode 100644 index 0000000..85b594c --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/gapic/metadata/gapic_metadata_pb2.pyi @@ -0,0 +1,122 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class GapicMetadata(_message.Message): + __slots__ = ( + "schema", + "comment", + "language", + "proto_package", + "library_package", + "services", + ) + class ServicesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: GapicMetadata.ServiceForTransport + def __init__( + self, + key: _Optional[str] = ..., + value: _Optional[_Union[GapicMetadata.ServiceForTransport, _Mapping]] = ..., + ) -> None: ... + + class ServiceForTransport(_message.Message): + __slots__ = ("clients", "api_version") + class ClientsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: GapicMetadata.ServiceAsClient + def __init__( + self, + key: _Optional[str] = ..., + value: _Optional[_Union[GapicMetadata.ServiceAsClient, _Mapping]] = ..., + ) -> None: ... + + CLIENTS_FIELD_NUMBER: _ClassVar[int] + API_VERSION_FIELD_NUMBER: _ClassVar[int] + clients: _containers.MessageMap[str, GapicMetadata.ServiceAsClient] + api_version: str + def __init__( + self, + clients: _Optional[_Mapping[str, GapicMetadata.ServiceAsClient]] = ..., + api_version: _Optional[str] = ..., + ) -> None: ... + + class ServiceAsClient(_message.Message): + __slots__ = ("library_client", "rpcs") + class RpcsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: GapicMetadata.MethodList + def __init__( + self, + key: _Optional[str] = ..., + value: _Optional[_Union[GapicMetadata.MethodList, _Mapping]] = ..., + ) -> None: ... + + LIBRARY_CLIENT_FIELD_NUMBER: _ClassVar[int] + RPCS_FIELD_NUMBER: _ClassVar[int] + library_client: str + rpcs: _containers.MessageMap[str, GapicMetadata.MethodList] + def __init__( + self, + library_client: _Optional[str] = ..., + rpcs: _Optional[_Mapping[str, GapicMetadata.MethodList]] = ..., + ) -> None: ... + + class MethodList(_message.Message): + __slots__ = ("methods",) + METHODS_FIELD_NUMBER: _ClassVar[int] + methods: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, methods: _Optional[_Iterable[str]] = ...) -> None: ... + + SCHEMA_FIELD_NUMBER: _ClassVar[int] + COMMENT_FIELD_NUMBER: _ClassVar[int] + LANGUAGE_FIELD_NUMBER: _ClassVar[int] + PROTO_PACKAGE_FIELD_NUMBER: _ClassVar[int] + LIBRARY_PACKAGE_FIELD_NUMBER: _ClassVar[int] + SERVICES_FIELD_NUMBER: _ClassVar[int] + schema: str + comment: str + language: str + proto_package: str + library_package: str + services: _containers.MessageMap[str, GapicMetadata.ServiceForTransport] + def __init__( + self, + schema: _Optional[str] = ..., + comment: _Optional[str] = ..., + language: _Optional[str] = ..., + proto_package: _Optional[str] = ..., + library_package: _Optional[str] = ..., + services: _Optional[_Mapping[str, GapicMetadata.ServiceForTransport]] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/logging/type/__pycache__/http_request_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/logging/type/__pycache__/http_request_pb2.cpython-312.pyc new file mode 100644 index 0000000..1050a86 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/logging/type/__pycache__/http_request_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/logging/type/__pycache__/log_severity_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/logging/type/__pycache__/log_severity_pb2.cpython-312.pyc new file mode 100644 index 0000000..19028d2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/logging/type/__pycache__/log_severity_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/logging/type/http_request.proto b/venv/lib/python3.12/site-packages/google/logging/type/http_request.proto new file mode 100644 index 0000000..b31522b --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/logging/type/http_request.proto @@ -0,0 +1,95 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.logging.type; + +import "google/protobuf/duration.proto"; + +option csharp_namespace = "Google.Cloud.Logging.Type"; +option go_package = "google.golang.org/genproto/googleapis/logging/type;ltype"; +option java_multiple_files = true; +option java_outer_classname = "HttpRequestProto"; +option java_package = "com.google.logging.type"; +option php_namespace = "Google\\Cloud\\Logging\\Type"; +option ruby_package = "Google::Cloud::Logging::Type"; + +// A common proto for logging HTTP requests. Only contains semantics +// defined by the HTTP specification. Product-specific logging +// information MUST be defined in a separate message. +message HttpRequest { + // The request method. Examples: `"GET"`, `"HEAD"`, `"PUT"`, `"POST"`. + string request_method = 1; + + // The scheme (http, https), the host name, the path and the query + // portion of the URL that was requested. + // Example: `"http://example.com/some/info?color=red"`. + string request_url = 2; + + // The size of the HTTP request message in bytes, including the request + // headers and the request body. + int64 request_size = 3; + + // The response code indicating the status of response. + // Examples: 200, 404. + int32 status = 4; + + // The size of the HTTP response message sent back to the client, in bytes, + // including the response headers and the response body. + int64 response_size = 5; + + // The user agent sent by the client. Example: + // `"Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; Q312461; .NET + // CLR 1.0.3705)"`. + string user_agent = 6; + + // The IP address (IPv4 or IPv6) of the client that issued the HTTP + // request. This field can include port information. Examples: + // `"192.168.1.1"`, `"10.0.0.1:80"`, `"FE80::0202:B3FF:FE1E:8329"`. + string remote_ip = 7; + + // The IP address (IPv4 or IPv6) of the origin server that the request was + // sent to. This field can include port information. Examples: + // `"192.168.1.1"`, `"10.0.0.1:80"`, `"FE80::0202:B3FF:FE1E:8329"`. + string server_ip = 13; + + // The referer URL of the request, as defined in + // [HTTP/1.1 Header Field + // Definitions](https://datatracker.ietf.org/doc/html/rfc2616#section-14.36). + string referer = 8; + + // The request processing latency on the server, from the time the request was + // received until the response was sent. + google.protobuf.Duration latency = 14; + + // Whether or not a cache lookup was attempted. + bool cache_lookup = 11; + + // Whether or not an entity was served from cache + // (with or without validation). + bool cache_hit = 9; + + // Whether or not the response was validated with the origin server before + // being served from cache. This field is only meaningful if `cache_hit` is + // True. + bool cache_validated_with_origin_server = 10; + + // The number of HTTP response bytes inserted into cache. Set only when a + // cache fill was attempted. + int64 cache_fill_bytes = 12; + + // Protocol used for the request. Examples: "HTTP/1.1", "HTTP/2", "websocket" + string protocol = 15; +} diff --git a/venv/lib/python3.12/site-packages/google/logging/type/http_request_pb2.py b/venv/lib/python3.12/site-packages/google/logging/type/http_request_pb2.py new file mode 100644 index 0000000..4be21f6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/logging/type/http_request_pb2.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/logging/type/http_request.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 33, + 5, + "", + "google/logging/type/http_request.proto", +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n&google/logging/type/http_request.proto\x12\x13google.logging.type\x1a\x1egoogle/protobuf/duration.proto"\xef\x02\n\x0bHttpRequest\x12\x16\n\x0erequest_method\x18\x01 \x01(\t\x12\x13\n\x0brequest_url\x18\x02 \x01(\t\x12\x14\n\x0crequest_size\x18\x03 \x01(\x03\x12\x0e\n\x06status\x18\x04 \x01(\x05\x12\x15\n\rresponse_size\x18\x05 \x01(\x03\x12\x12\n\nuser_agent\x18\x06 \x01(\t\x12\x11\n\tremote_ip\x18\x07 \x01(\t\x12\x11\n\tserver_ip\x18\r \x01(\t\x12\x0f\n\x07referer\x18\x08 \x01(\t\x12*\n\x07latency\x18\x0e \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x14\n\x0c\x63\x61\x63he_lookup\x18\x0b \x01(\x08\x12\x11\n\tcache_hit\x18\t \x01(\x08\x12*\n"cache_validated_with_origin_server\x18\n \x01(\x08\x12\x18\n\x10\x63\x61\x63he_fill_bytes\x18\x0c \x01(\x03\x12\x10\n\x08protocol\x18\x0f \x01(\tB\xbe\x01\n\x17\x63om.google.logging.typeB\x10HttpRequestProtoP\x01Z8google.golang.org/genproto/googleapis/logging/type;ltype\xaa\x02\x19Google.Cloud.Logging.Type\xca\x02\x19Google\\Cloud\\Logging\\Type\xea\x02\x1cGoogle::Cloud::Logging::Typeb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.logging.type.http_request_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\027com.google.logging.typeB\020HttpRequestProtoP\001Z8google.golang.org/genproto/googleapis/logging/type;ltype\252\002\031Google.Cloud.Logging.Type\312\002\031Google\\Cloud\\Logging\\Type\352\002\034Google::Cloud::Logging::Type" + _globals["_HTTPREQUEST"]._serialized_start = 96 + _globals["_HTTPREQUEST"]._serialized_end = 463 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/logging/type/http_request_pb2.pyi b/venv/lib/python3.12/site-packages/google/logging/type/http_request_pb2.pyi new file mode 100644 index 0000000..5a05ecf --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/logging/type/http_request_pb2.pyi @@ -0,0 +1,94 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import duration_pb2 as _duration_pb2 +from google.protobuf import message as _message + +DESCRIPTOR: _descriptor.FileDescriptor + +class HttpRequest(_message.Message): + __slots__ = ( + "request_method", + "request_url", + "request_size", + "status", + "response_size", + "user_agent", + "remote_ip", + "server_ip", + "referer", + "latency", + "cache_lookup", + "cache_hit", + "cache_validated_with_origin_server", + "cache_fill_bytes", + "protocol", + ) + REQUEST_METHOD_FIELD_NUMBER: _ClassVar[int] + REQUEST_URL_FIELD_NUMBER: _ClassVar[int] + REQUEST_SIZE_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + RESPONSE_SIZE_FIELD_NUMBER: _ClassVar[int] + USER_AGENT_FIELD_NUMBER: _ClassVar[int] + REMOTE_IP_FIELD_NUMBER: _ClassVar[int] + SERVER_IP_FIELD_NUMBER: _ClassVar[int] + REFERER_FIELD_NUMBER: _ClassVar[int] + LATENCY_FIELD_NUMBER: _ClassVar[int] + CACHE_LOOKUP_FIELD_NUMBER: _ClassVar[int] + CACHE_HIT_FIELD_NUMBER: _ClassVar[int] + CACHE_VALIDATED_WITH_ORIGIN_SERVER_FIELD_NUMBER: _ClassVar[int] + CACHE_FILL_BYTES_FIELD_NUMBER: _ClassVar[int] + PROTOCOL_FIELD_NUMBER: _ClassVar[int] + request_method: str + request_url: str + request_size: int + status: int + response_size: int + user_agent: str + remote_ip: str + server_ip: str + referer: str + latency: _duration_pb2.Duration + cache_lookup: bool + cache_hit: bool + cache_validated_with_origin_server: bool + cache_fill_bytes: int + protocol: str + def __init__( + self, + request_method: _Optional[str] = ..., + request_url: _Optional[str] = ..., + request_size: _Optional[int] = ..., + status: _Optional[int] = ..., + response_size: _Optional[int] = ..., + user_agent: _Optional[str] = ..., + remote_ip: _Optional[str] = ..., + server_ip: _Optional[str] = ..., + referer: _Optional[str] = ..., + latency: _Optional[ + _Union[datetime.timedelta, _duration_pb2.Duration, _Mapping] + ] = ..., + cache_lookup: _Optional[bool] = ..., + cache_hit: _Optional[bool] = ..., + cache_validated_with_origin_server: _Optional[bool] = ..., + cache_fill_bytes: _Optional[int] = ..., + protocol: _Optional[str] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/logging/type/log_severity.proto b/venv/lib/python3.12/site-packages/google/logging/type/log_severity.proto new file mode 100644 index 0000000..406b817 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/logging/type/log_severity.proto @@ -0,0 +1,71 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.logging.type; + +option csharp_namespace = "Google.Cloud.Logging.Type"; +option go_package = "google.golang.org/genproto/googleapis/logging/type;ltype"; +option java_multiple_files = true; +option java_outer_classname = "LogSeverityProto"; +option java_package = "com.google.logging.type"; +option objc_class_prefix = "GLOG"; +option php_namespace = "Google\\Cloud\\Logging\\Type"; +option ruby_package = "Google::Cloud::Logging::Type"; + +// The severity of the event described in a log entry, expressed as one of the +// standard severity levels listed below. For your reference, the levels are +// assigned the listed numeric values. The effect of using numeric values other +// than those listed is undefined. +// +// You can filter for log entries by severity. For example, the following +// filter expression will match log entries with severities `INFO`, `NOTICE`, +// and `WARNING`: +// +// severity > DEBUG AND severity <= WARNING +// +// If you are writing log entries, you should map other severity encodings to +// one of these standard levels. For example, you might map all of Java's FINE, +// FINER, and FINEST levels to `LogSeverity.DEBUG`. You can preserve the +// original severity level in the log entry payload if you wish. +enum LogSeverity { + // (0) The log entry has no assigned severity level. + DEFAULT = 0; + + // (100) Debug or trace information. + DEBUG = 100; + + // (200) Routine information, such as ongoing status or performance. + INFO = 200; + + // (300) Normal but significant events, such as start up, shut down, or + // a configuration change. + NOTICE = 300; + + // (400) Warning events might cause problems. + WARNING = 400; + + // (500) Error events are likely to cause problems. + ERROR = 500; + + // (600) Critical events cause more severe problems or outages. + CRITICAL = 600; + + // (700) A person must take an action immediately. + ALERT = 700; + + // (800) One or more systems are unusable. + EMERGENCY = 800; +} diff --git a/venv/lib/python3.12/site-packages/google/logging/type/log_severity_pb2.py b/venv/lib/python3.12/site-packages/google/logging/type/log_severity_pb2.py new file mode 100644 index 0000000..bcdcc3d --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/logging/type/log_severity_pb2.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/logging/type/log_severity.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 33, + 5, + "", + "google/logging/type/log_severity.proto", +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n&google/logging/type/log_severity.proto\x12\x13google.logging.type*\x82\x01\n\x0bLogSeverity\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\t\n\x05\x44\x45\x42UG\x10\x64\x12\t\n\x04INFO\x10\xc8\x01\x12\x0b\n\x06NOTICE\x10\xac\x02\x12\x0c\n\x07WARNING\x10\x90\x03\x12\n\n\x05\x45RROR\x10\xf4\x03\x12\r\n\x08\x43RITICAL\x10\xd8\x04\x12\n\n\x05\x41LERT\x10\xbc\x05\x12\x0e\n\tEMERGENCY\x10\xa0\x06\x42\xc5\x01\n\x17\x63om.google.logging.typeB\x10LogSeverityProtoP\x01Z8google.golang.org/genproto/googleapis/logging/type;ltype\xa2\x02\x04GLOG\xaa\x02\x19Google.Cloud.Logging.Type\xca\x02\x19Google\\Cloud\\Logging\\Type\xea\x02\x1cGoogle::Cloud::Logging::Typeb\x06proto3" +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.logging.type.log_severity_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\027com.google.logging.typeB\020LogSeverityProtoP\001Z8google.golang.org/genproto/googleapis/logging/type;ltype\242\002\004GLOG\252\002\031Google.Cloud.Logging.Type\312\002\031Google\\Cloud\\Logging\\Type\352\002\034Google::Cloud::Logging::Type" + _globals["_LOGSEVERITY"]._serialized_start = 64 + _globals["_LOGSEVERITY"]._serialized_end = 194 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/logging/type/log_severity_pb2.pyi b/venv/lib/python3.12/site-packages/google/logging/type/log_severity_pb2.pyi new file mode 100644 index 0000000..eeafb8e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/logging/type/log_severity_pb2.pyi @@ -0,0 +1,42 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar + +from google.protobuf import descriptor as _descriptor +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor + +class LogSeverity(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + DEFAULT: _ClassVar[LogSeverity] + DEBUG: _ClassVar[LogSeverity] + INFO: _ClassVar[LogSeverity] + NOTICE: _ClassVar[LogSeverity] + WARNING: _ClassVar[LogSeverity] + ERROR: _ClassVar[LogSeverity] + CRITICAL: _ClassVar[LogSeverity] + ALERT: _ClassVar[LogSeverity] + EMERGENCY: _ClassVar[LogSeverity] + +DEFAULT: LogSeverity +DEBUG: LogSeverity +INFO: LogSeverity +NOTICE: LogSeverity +WARNING: LogSeverity +ERROR: LogSeverity +CRITICAL: LogSeverity +ALERT: LogSeverity +EMERGENCY: LogSeverity diff --git a/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_grpc.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_grpc.cpython-312.pyc new file mode 100644 index 0000000..df2a09d Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_grpc.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_grpc_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_grpc_pb2.cpython-312.pyc new file mode 100644 index 0000000..0fe7ca2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_grpc_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_pb2.cpython-312.pyc new file mode 100644 index 0000000..b1677f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_pb2_grpc.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_pb2_grpc.cpython-312.pyc new file mode 100644 index 0000000..f477089 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_pb2_grpc.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_proto.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_proto.cpython-312.pyc new file mode 100644 index 0000000..92a56c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_proto.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_proto_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_proto_pb2.cpython-312.pyc new file mode 100644 index 0000000..d8c33cb Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/longrunning/__pycache__/operations_proto_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/longrunning/operations_grpc.py b/venv/lib/python3.12/site-packages/google/longrunning/operations_grpc.py new file mode 100644 index 0000000..0545e8c --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/longrunning/operations_grpc.py @@ -0,0 +1,20 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This module is provided for backwards compatibility with +# googleapis-common-protos <= 1.5.0, where this was the import path. + +from __future__ import absolute_import + +from google.longrunning.operations_grpc_pb2 import * # noqa: F401, F403 diff --git a/venv/lib/python3.12/site-packages/google/longrunning/operations_grpc_pb2.py b/venv/lib/python3.12/site-packages/google/longrunning/operations_grpc_pb2.py new file mode 100644 index 0000000..d03fe28 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/longrunning/operations_grpc_pb2.py @@ -0,0 +1,16 @@ +# This module is provided for backwards compatibility with +# googleapis-common-protos <= 1.52.0, where this import path contained +# all of the message and gRPC definitions. + +from google.longrunning.operations_pb2_grpc import * +from google.longrunning.operations_proto_pb2 import * +from google.longrunning.operations_proto_pb2 import ( + _CANCELOPERATIONREQUEST, + _DELETEOPERATIONREQUEST, + _GETOPERATIONREQUEST, + _LISTOPERATIONSREQUEST, + _LISTOPERATIONSRESPONSE, + _OPERATION, + _OPERATIONINFO, + _OPERATIONS, +) diff --git a/venv/lib/python3.12/site-packages/google/longrunning/operations_pb2.py b/venv/lib/python3.12/site-packages/google/longrunning/operations_pb2.py new file mode 100644 index 0000000..928c336 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/longrunning/operations_pb2.py @@ -0,0 +1,45 @@ +# Copyright 2016 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Safe implementation of long-running operations with and without gRPC. + +Multiplexes between versions of long-running operations with and without gRPC. +The former is preferred, but not possible in all environments (such as Google +AppEngine Standard). +""" + +try: + from google.longrunning.operations_grpc_pb2 import * + from google.longrunning.operations_grpc_pb2 import ( + _CANCELOPERATIONREQUEST, + _DELETEOPERATIONREQUEST, + _GETOPERATIONREQUEST, + _LISTOPERATIONSREQUEST, + _LISTOPERATIONSRESPONSE, + _OPERATION, + _OPERATIONINFO, + _OPERATIONS, + ) +except ImportError: + from google.longrunning.operations_proto_pb2 import * + from google.longrunning.operations_proto_pb2 import ( + _CANCELOPERATIONREQUEST, + _DELETEOPERATIONREQUEST, + _GETOPERATIONREQUEST, + _LISTOPERATIONSREQUEST, + _LISTOPERATIONSRESPONSE, + _OPERATION, + _OPERATIONINFO, + _OPERATIONS, + ) diff --git a/venv/lib/python3.12/site-packages/google/longrunning/operations_pb2_grpc.py b/venv/lib/python3.12/site-packages/google/longrunning/operations_pb2_grpc.py new file mode 100644 index 0000000..6bf00bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/longrunning/operations_pb2_grpc.py @@ -0,0 +1,341 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! + +"""Client and server classes corresponding to protobuf-defined services.""" + +import grpc +from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 + +from google.longrunning import ( + operations_proto_pb2 as google_dot_longrunning_dot_operations__pb2, +) + + +class OperationsStub(object): + """Manages long-running operations with an API service. + + When an API method normally takes long time to complete, it can be designed + to return [Operation][google.longrunning.Operation] to the client, and the client can use this + interface to receive the real response asynchronously by polling the + operation resource, or pass the operation resource to another API (such as + Google Cloud Pub/Sub API) to receive the response. Any API service that + returns long-running operations should implement the `Operations` interface + so developers can have a consistent client experience. + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.ListOperations = channel.unary_unary( + "/google.longrunning.Operations/ListOperations", + request_serializer=google_dot_longrunning_dot_operations__pb2.ListOperationsRequest.SerializeToString, + response_deserializer=google_dot_longrunning_dot_operations__pb2.ListOperationsResponse.FromString, + ) + self.GetOperation = channel.unary_unary( + "/google.longrunning.Operations/GetOperation", + request_serializer=google_dot_longrunning_dot_operations__pb2.GetOperationRequest.SerializeToString, + response_deserializer=google_dot_longrunning_dot_operations__pb2.Operation.FromString, + ) + self.DeleteOperation = channel.unary_unary( + "/google.longrunning.Operations/DeleteOperation", + request_serializer=google_dot_longrunning_dot_operations__pb2.DeleteOperationRequest.SerializeToString, + response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, + ) + self.CancelOperation = channel.unary_unary( + "/google.longrunning.Operations/CancelOperation", + request_serializer=google_dot_longrunning_dot_operations__pb2.CancelOperationRequest.SerializeToString, + response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, + ) + self.WaitOperation = channel.unary_unary( + "/google.longrunning.Operations/WaitOperation", + request_serializer=google_dot_longrunning_dot_operations__pb2.WaitOperationRequest.SerializeToString, + response_deserializer=google_dot_longrunning_dot_operations__pb2.Operation.FromString, + ) + + +class OperationsServicer(object): + """Manages long-running operations with an API service. + + When an API method normally takes long time to complete, it can be designed + to return [Operation][google.longrunning.Operation] to the client, and the client can use this + interface to receive the real response asynchronously by polling the + operation resource, or pass the operation resource to another API (such as + Google Cloud Pub/Sub API) to receive the response. Any API service that + returns long-running operations should implement the `Operations` interface + so developers can have a consistent client experience. + """ + + def ListOperations(self, request, context): + """Lists operations that match the specified filter in the request. If the + server doesn't support this method, it returns `UNIMPLEMENTED`. + + NOTE: the `name` binding allows API services to override the binding + to use different resource name schemes, such as `users/*/operations`. To + override the binding, API services can add a binding such as + `"/v1/{name=users/*}/operations"` to their service configuration. + For backwards compatibility, the default name includes the operations + collection id, however overriding users must ensure the name binding + is the parent resource, without the operations collection id. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") + + def GetOperation(self, request, context): + """Gets the latest state of a long-running operation. Clients can use this + method to poll the operation result at intervals as recommended by the API + service. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") + + def DeleteOperation(self, request, context): + """Deletes a long-running operation. This method indicates that the client is + no longer interested in the operation result. It does not cancel the + operation. If the server doesn't support this method, it returns + `google.rpc.Code.UNIMPLEMENTED`. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") + + def CancelOperation(self, request, context): + """Starts asynchronous cancellation on a long-running operation. The server + makes a best effort to cancel the operation, but success is not + guaranteed. If the server doesn't support this method, it returns + `google.rpc.Code.UNIMPLEMENTED`. Clients can use + [Operations.GetOperation][google.longrunning.Operations.GetOperation] or + other methods to check whether the cancellation succeeded or whether the + operation completed despite cancellation. On successful cancellation, + the operation is not deleted; instead, it becomes an operation with + an [Operation.error][google.longrunning.Operation.error] value with a [google.rpc.Status.code][google.rpc.Status.code] of 1, + corresponding to `Code.CANCELLED`. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") + + def WaitOperation(self, request, context): + """Waits until the specified long-running operation is done or reaches at most + a specified timeout, returning the latest state. If the operation is + already done, the latest state is immediately returned. If the timeout + specified is greater than the default HTTP/RPC timeout, the HTTP/RPC + timeout is used. If the server does not support this method, it returns + `google.rpc.Code.UNIMPLEMENTED`. + Note that this method is on a best-effort basis. It may return the latest + state before the specified timeout (including immediately), meaning even an + immediate response is no guarantee that the operation is done. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") + + +def add_OperationsServicer_to_server(servicer, server): + rpc_method_handlers = { + "ListOperations": grpc.unary_unary_rpc_method_handler( + servicer.ListOperations, + request_deserializer=google_dot_longrunning_dot_operations__pb2.ListOperationsRequest.FromString, + response_serializer=google_dot_longrunning_dot_operations__pb2.ListOperationsResponse.SerializeToString, + ), + "GetOperation": grpc.unary_unary_rpc_method_handler( + servicer.GetOperation, + request_deserializer=google_dot_longrunning_dot_operations__pb2.GetOperationRequest.FromString, + response_serializer=google_dot_longrunning_dot_operations__pb2.Operation.SerializeToString, + ), + "DeleteOperation": grpc.unary_unary_rpc_method_handler( + servicer.DeleteOperation, + request_deserializer=google_dot_longrunning_dot_operations__pb2.DeleteOperationRequest.FromString, + response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, + ), + "CancelOperation": grpc.unary_unary_rpc_method_handler( + servicer.CancelOperation, + request_deserializer=google_dot_longrunning_dot_operations__pb2.CancelOperationRequest.FromString, + response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, + ), + "WaitOperation": grpc.unary_unary_rpc_method_handler( + servicer.WaitOperation, + request_deserializer=google_dot_longrunning_dot_operations__pb2.WaitOperationRequest.FromString, + response_serializer=google_dot_longrunning_dot_operations__pb2.Operation.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + "google.longrunning.Operations", rpc_method_handlers + ) + server.add_generic_rpc_handlers((generic_handler,)) + + +# This class is part of an EXPERIMENTAL API. +class Operations(object): + """Manages long-running operations with an API service. + + When an API method normally takes long time to complete, it can be designed + to return [Operation][google.longrunning.Operation] to the client, and the client can use this + interface to receive the real response asynchronously by polling the + operation resource, or pass the operation resource to another API (such as + Google Cloud Pub/Sub API) to receive the response. Any API service that + returns long-running operations should implement the `Operations` interface + so developers can have a consistent client experience. + """ + + @staticmethod + def ListOperations( + request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.unary_unary( + request, + target, + "/google.longrunning.Operations/ListOperations", + google_dot_longrunning_dot_operations__pb2.ListOperationsRequest.SerializeToString, + google_dot_longrunning_dot_operations__pb2.ListOperationsResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + ) + + @staticmethod + def GetOperation( + request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.unary_unary( + request, + target, + "/google.longrunning.Operations/GetOperation", + google_dot_longrunning_dot_operations__pb2.GetOperationRequest.SerializeToString, + google_dot_longrunning_dot_operations__pb2.Operation.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + ) + + @staticmethod + def DeleteOperation( + request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.unary_unary( + request, + target, + "/google.longrunning.Operations/DeleteOperation", + google_dot_longrunning_dot_operations__pb2.DeleteOperationRequest.SerializeToString, + google_dot_protobuf_dot_empty__pb2.Empty.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + ) + + @staticmethod + def CancelOperation( + request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.unary_unary( + request, + target, + "/google.longrunning.Operations/CancelOperation", + google_dot_longrunning_dot_operations__pb2.CancelOperationRequest.SerializeToString, + google_dot_protobuf_dot_empty__pb2.Empty.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + ) + + @staticmethod + def WaitOperation( + request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.unary_unary( + request, + target, + "/google.longrunning.Operations/WaitOperation", + google_dot_longrunning_dot_operations__pb2.WaitOperationRequest.SerializeToString, + google_dot_longrunning_dot_operations__pb2.Operation.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + ) diff --git a/venv/lib/python3.12/site-packages/google/longrunning/operations_proto.proto b/venv/lib/python3.12/site-packages/google/longrunning/operations_proto.proto new file mode 100644 index 0000000..63df207 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/longrunning/operations_proto.proto @@ -0,0 +1,265 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.longrunning; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/protobuf/any.proto"; +import "google/protobuf/descriptor.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/empty.proto"; +import "google/rpc/status.proto"; + +option csharp_namespace = "Google.LongRunning"; +option go_package = "cloud.google.com/go/longrunning/autogen/longrunningpb;longrunningpb"; +option java_multiple_files = true; +option java_outer_classname = "OperationsProto"; +option java_package = "com.google.longrunning"; +option objc_class_prefix = "GLRUN"; +option php_namespace = "Google\\LongRunning"; + +extend google.protobuf.MethodOptions { + // Additional information regarding long-running operations. + // In particular, this specifies the types that are returned from + // long-running operations. + // + // Required for methods that return `google.longrunning.Operation`; invalid + // otherwise. + google.longrunning.OperationInfo operation_info = 1049; +} + +// Manages long-running operations with an API service. +// +// When an API method normally takes long time to complete, it can be designed +// to return [Operation][google.longrunning.Operation] to the client, and the +// client can use this interface to receive the real response asynchronously by +// polling the operation resource, or pass the operation resource to another API +// (such as Pub/Sub API) to receive the response. Any API service that returns +// long-running operations should implement the `Operations` interface so +// developers can have a consistent client experience. +service Operations { + option (google.api.default_host) = "longrunning.googleapis.com"; + + // Lists operations that match the specified filter in the request. If the + // server doesn't support this method, it returns `UNIMPLEMENTED`. + rpc ListOperations(ListOperationsRequest) returns (ListOperationsResponse) { + option (google.api.http) = { + get: "/v1/{name=operations}" + }; + option (google.api.method_signature) = "name,filter"; + } + + // Gets the latest state of a long-running operation. Clients can use this + // method to poll the operation result at intervals as recommended by the API + // service. + rpc GetOperation(GetOperationRequest) returns (Operation) { + option (google.api.http) = { + get: "/v1/{name=operations/**}" + }; + option (google.api.method_signature) = "name"; + } + + // Deletes a long-running operation. This method indicates that the client is + // no longer interested in the operation result. It does not cancel the + // operation. If the server doesn't support this method, it returns + // `google.rpc.Code.UNIMPLEMENTED`. + rpc DeleteOperation(DeleteOperationRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/{name=operations/**}" + }; + option (google.api.method_signature) = "name"; + } + + // Starts asynchronous cancellation on a long-running operation. The server + // makes a best effort to cancel the operation, but success is not + // guaranteed. If the server doesn't support this method, it returns + // `google.rpc.Code.UNIMPLEMENTED`. Clients can use + // [Operations.GetOperation][google.longrunning.Operations.GetOperation] or + // other methods to check whether the cancellation succeeded or whether the + // operation completed despite cancellation. On successful cancellation, + // the operation is not deleted; instead, it becomes an operation with + // an [Operation.error][google.longrunning.Operation.error] value with a + // [google.rpc.Status.code][google.rpc.Status.code] of `1`, corresponding to + // `Code.CANCELLED`. + rpc CancelOperation(CancelOperationRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/v1/{name=operations/**}:cancel" + body: "*" + }; + option (google.api.method_signature) = "name"; + } + + // Waits until the specified long-running operation is done or reaches at most + // a specified timeout, returning the latest state. If the operation is + // already done, the latest state is immediately returned. If the timeout + // specified is greater than the default HTTP/RPC timeout, the HTTP/RPC + // timeout is used. If the server does not support this method, it returns + // `google.rpc.Code.UNIMPLEMENTED`. + // Note that this method is on a best-effort basis. It may return the latest + // state before the specified timeout (including immediately), meaning even an + // immediate response is no guarantee that the operation is done. + rpc WaitOperation(WaitOperationRequest) returns (Operation) {} +} + +// This resource represents a long-running operation that is the result of a +// network API call. +message Operation { + // The server-assigned name, which is only unique within the same service that + // originally returns it. If you use the default HTTP mapping, the + // `name` should be a resource name ending with `operations/{unique_id}`. + string name = 1; + + // Service-specific metadata associated with the operation. It typically + // contains progress information and common metadata such as create time. + // Some services might not provide such metadata. Any method that returns a + // long-running operation should document the metadata type, if any. + google.protobuf.Any metadata = 2; + + // If the value is `false`, it means the operation is still in progress. + // If `true`, the operation is completed, and either `error` or `response` is + // available. + bool done = 3; + + // The operation result, which can be either an `error` or a valid `response`. + // If `done` == `false`, neither `error` nor `response` is set. + // If `done` == `true`, exactly one of `error` or `response` can be set. + // Some services might not provide the result. + oneof result { + // The error result of the operation in case of failure or cancellation. + google.rpc.Status error = 4; + + // The normal, successful response of the operation. If the original + // method returns no data on success, such as `Delete`, the response is + // `google.protobuf.Empty`. If the original method is standard + // `Get`/`Create`/`Update`, the response should be the resource. For other + // methods, the response should have the type `XxxResponse`, where `Xxx` + // is the original method name. For example, if the original method name + // is `TakeSnapshot()`, the inferred response type is + // `TakeSnapshotResponse`. + google.protobuf.Any response = 5; + } +} + +// The request message for +// [Operations.GetOperation][google.longrunning.Operations.GetOperation]. +message GetOperationRequest { + // The name of the operation resource. + string name = 1; +} + +// The request message for +// [Operations.ListOperations][google.longrunning.Operations.ListOperations]. +message ListOperationsRequest { + // The name of the operation's parent resource. + string name = 4; + + // The standard list filter. + string filter = 1; + + // The standard list page size. + int32 page_size = 2; + + // The standard list page token. + string page_token = 3; + + // When set to `true`, operations that are reachable are returned as normal, + // and those that are unreachable are returned in the + // [ListOperationsResponse.unreachable] field. + // + // This can only be `true` when reading across collections e.g. when `parent` + // is set to `"projects/example/locations/-"`. + // + // This field is not by default supported and will result in an + // `UNIMPLEMENTED` error if set unless explicitly documented otherwise in + // service or product specific documentation. + bool return_partial_success = 5; +} + +// The response message for +// [Operations.ListOperations][google.longrunning.Operations.ListOperations]. +message ListOperationsResponse { + // A list of operations that matches the specified filter in the request. + repeated Operation operations = 1; + + // The standard List next-page token. + string next_page_token = 2; + + // Unordered list. Unreachable resources. Populated when the request sets + // `ListOperationsRequest.return_partial_success` and reads across + // collections e.g. when attempting to list all resources across all supported + // locations. + repeated string unreachable = 3 + [(google.api.field_behavior) = UNORDERED_LIST]; +} + +// The request message for +// [Operations.CancelOperation][google.longrunning.Operations.CancelOperation]. +message CancelOperationRequest { + // The name of the operation resource to be cancelled. + string name = 1; +} + +// The request message for +// [Operations.DeleteOperation][google.longrunning.Operations.DeleteOperation]. +message DeleteOperationRequest { + // The name of the operation resource to be deleted. + string name = 1; +} + +// The request message for +// [Operations.WaitOperation][google.longrunning.Operations.WaitOperation]. +message WaitOperationRequest { + // The name of the operation resource to wait on. + string name = 1; + + // The maximum duration to wait before timing out. If left blank, the wait + // will be at most the time permitted by the underlying HTTP/RPC protocol. + // If RPC context deadline is also specified, the shorter one will be used. + google.protobuf.Duration timeout = 2; +} + +// A message representing the message types used by a long-running operation. +// +// Example: +// +// rpc Export(ExportRequest) returns (google.longrunning.Operation) { +// option (google.longrunning.operation_info) = { +// response_type: "ExportResponse" +// metadata_type: "ExportMetadata" +// }; +// } +message OperationInfo { + // Required. The message name of the primary return type for this + // long-running operation. + // This type will be used to deserialize the LRO's response. + // + // If the response is in a different package from the rpc, a fully-qualified + // message name must be used (e.g. `google.protobuf.Struct`). + // + // Note: Altering this value constitutes a breaking change. + string response_type = 1; + + // Required. The message name of the metadata type for this long-running + // operation. + // + // If the response is in a different package from the rpc, a fully-qualified + // message name must be used (e.g. `google.protobuf.Struct`). + // + // Note: Altering this value constitutes a breaking change. + string metadata_type = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/longrunning/operations_proto.py b/venv/lib/python3.12/site-packages/google/longrunning/operations_proto.py new file mode 100644 index 0000000..35d1fac --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/longrunning/operations_proto.py @@ -0,0 +1,6 @@ +# This module is provided for backwards compatibility with +# googleapis-common-protos <= 1.5.0, where this was the import path. + +from __future__ import absolute_import + +from google.longrunning.operations_proto_pb2 import * # noqa: F401, F403 diff --git a/venv/lib/python3.12/site-packages/google/longrunning/operations_proto_pb2.py b/venv/lib/python3.12/site-packages/google/longrunning/operations_proto_pb2.py new file mode 100644 index 0000000..8b12706 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/longrunning/operations_proto_pb2.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/longrunning/operations_proto.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 33, + 5, + "", + "google/longrunning/operations_proto.proto", +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 + +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.api import client_pb2 as google_dot_api_dot_client__pb2 +from google.api import field_behavior_pb2 as google_dot_api_dot_field__behavior__pb2 +from google.rpc import status_pb2 as google_dot_rpc_dot_status__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n)google/longrunning/operations_proto.proto\x12\x12google.longrunning\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/protobuf/any.proto\x1a google/protobuf/descriptor.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x17google/rpc/status.proto"\xa8\x01\n\tOperation\x12\x0c\n\x04name\x18\x01 \x01(\t\x12&\n\x08metadata\x18\x02 \x01(\x0b\x32\x14.google.protobuf.Any\x12\x0c\n\x04\x64one\x18\x03 \x01(\x08\x12#\n\x05\x65rror\x18\x04 \x01(\x0b\x32\x12.google.rpc.StatusH\x00\x12(\n\x08response\x18\x05 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x42\x08\n\x06result"#\n\x13GetOperationRequest\x12\x0c\n\x04name\x18\x01 \x01(\t"|\n\x15ListOperationsRequest\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0e\n\x06\x66ilter\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x12\n\npage_token\x18\x03 \x01(\t\x12\x1e\n\x16return_partial_success\x18\x05 \x01(\x08"~\n\x16ListOperationsResponse\x12\x31\n\noperations\x18\x01 \x03(\x0b\x32\x1d.google.longrunning.Operation\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\x12\x18\n\x0bunreachable\x18\x03 \x03(\tB\x03\xe0\x41\x06"&\n\x16\x43\x61ncelOperationRequest\x12\x0c\n\x04name\x18\x01 \x01(\t"&\n\x16\x44\x65leteOperationRequest\x12\x0c\n\x04name\x18\x01 \x01(\t"P\n\x14WaitOperationRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12*\n\x07timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration"=\n\rOperationInfo\x12\x15\n\rresponse_type\x18\x01 \x01(\t\x12\x15\n\rmetadata_type\x18\x02 \x01(\t2\xaa\x05\n\nOperations\x12\x94\x01\n\x0eListOperations\x12).google.longrunning.ListOperationsRequest\x1a*.google.longrunning.ListOperationsResponse"+\xda\x41\x0bname,filter\x82\xd3\xe4\x93\x02\x17\x12\x15/v1/{name=operations}\x12\x7f\n\x0cGetOperation\x12\'.google.longrunning.GetOperationRequest\x1a\x1d.google.longrunning.Operation"\'\xda\x41\x04name\x82\xd3\xe4\x93\x02\x1a\x12\x18/v1/{name=operations/**}\x12~\n\x0f\x44\x65leteOperation\x12*.google.longrunning.DeleteOperationRequest\x1a\x16.google.protobuf.Empty"\'\xda\x41\x04name\x82\xd3\xe4\x93\x02\x1a*\x18/v1/{name=operations/**}\x12\x88\x01\n\x0f\x43\x61ncelOperation\x12*.google.longrunning.CancelOperationRequest\x1a\x16.google.protobuf.Empty"1\xda\x41\x04name\x82\xd3\xe4\x93\x02$"\x1f/v1/{name=operations/**}:cancel:\x01*\x12Z\n\rWaitOperation\x12(.google.longrunning.WaitOperationRequest\x1a\x1d.google.longrunning.Operation"\x00\x1a\x1d\xca\x41\x1alongrunning.googleapis.com:Z\n\x0eoperation_info\x12\x1e.google.protobuf.MethodOptions\x18\x99\x08 \x01(\x0b\x32!.google.longrunning.OperationInfoB\xa2\x01\n\x16\x63om.google.longrunningB\x0fOperationsProtoP\x01ZCcloud.google.com/go/longrunning/autogen/longrunningpb;longrunningpb\xa2\x02\x05GLRUN\xaa\x02\x12Google.LongRunning\xca\x02\x12Google\\LongRunningb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.longrunning.operations_proto_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\026com.google.longrunningB\017OperationsProtoP\001ZCcloud.google.com/go/longrunning/autogen/longrunningpb;longrunningpb\242\002\005GLRUN\252\002\022Google.LongRunning\312\002\022Google\\LongRunning" + _globals["_LISTOPERATIONSRESPONSE"].fields_by_name[ + "unreachable" + ]._loaded_options = None + _globals["_LISTOPERATIONSRESPONSE"].fields_by_name[ + "unreachable" + ]._serialized_options = b"\340A\006" + _globals["_OPERATIONS"]._loaded_options = None + _globals["_OPERATIONS"]._serialized_options = b"\312A\032longrunning.googleapis.com" + _globals["_OPERATIONS"].methods_by_name["ListOperations"]._loaded_options = None + _globals["_OPERATIONS"].methods_by_name[ + "ListOperations" + ]._serialized_options = ( + b"\332A\013name,filter\202\323\344\223\002\027\022\025/v1/{name=operations}" + ) + _globals["_OPERATIONS"].methods_by_name["GetOperation"]._loaded_options = None + _globals["_OPERATIONS"].methods_by_name[ + "GetOperation" + ]._serialized_options = ( + b"\332A\004name\202\323\344\223\002\032\022\030/v1/{name=operations/**}" + ) + _globals["_OPERATIONS"].methods_by_name["DeleteOperation"]._loaded_options = None + _globals["_OPERATIONS"].methods_by_name[ + "DeleteOperation" + ]._serialized_options = ( + b"\332A\004name\202\323\344\223\002\032*\030/v1/{name=operations/**}" + ) + _globals["_OPERATIONS"].methods_by_name["CancelOperation"]._loaded_options = None + _globals["_OPERATIONS"].methods_by_name[ + "CancelOperation" + ]._serialized_options = ( + b'\332A\004name\202\323\344\223\002$"\037/v1/{name=operations/**}:cancel:\001*' + ) + _globals["_OPERATION"]._serialized_start = 301 + _globals["_OPERATION"]._serialized_end = 469 + _globals["_GETOPERATIONREQUEST"]._serialized_start = 471 + _globals["_GETOPERATIONREQUEST"]._serialized_end = 506 + _globals["_LISTOPERATIONSREQUEST"]._serialized_start = 508 + _globals["_LISTOPERATIONSREQUEST"]._serialized_end = 632 + _globals["_LISTOPERATIONSRESPONSE"]._serialized_start = 634 + _globals["_LISTOPERATIONSRESPONSE"]._serialized_end = 760 + _globals["_CANCELOPERATIONREQUEST"]._serialized_start = 762 + _globals["_CANCELOPERATIONREQUEST"]._serialized_end = 800 + _globals["_DELETEOPERATIONREQUEST"]._serialized_start = 802 + _globals["_DELETEOPERATIONREQUEST"]._serialized_end = 840 + _globals["_WAITOPERATIONREQUEST"]._serialized_start = 842 + _globals["_WAITOPERATIONREQUEST"]._serialized_end = 922 + _globals["_OPERATIONINFO"]._serialized_start = 924 + _globals["_OPERATIONINFO"]._serialized_end = 985 + _globals["_OPERATIONS"]._serialized_start = 988 + _globals["_OPERATIONS"]._serialized_end = 1670 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/longrunning/operations_proto_pb2.pyi b/venv/lib/python3.12/site-packages/google/longrunning/operations_proto_pb2.pyi new file mode 100644 index 0000000..e56b84a --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/longrunning/operations_proto_pb2.pyi @@ -0,0 +1,136 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import any_pb2 as _any_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pb2 as _descriptor_pb2 +from google.protobuf import duration_pb2 as _duration_pb2 +from google.protobuf import empty_pb2 as _empty_pb2 +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +from google.api import annotations_pb2 as _annotations_pb2 +from google.api import client_pb2 as _client_pb2 +from google.api import field_behavior_pb2 as _field_behavior_pb2 +from google.rpc import status_pb2 as _status_pb2 + +DESCRIPTOR: _descriptor.FileDescriptor +OPERATION_INFO_FIELD_NUMBER: _ClassVar[int] +operation_info: _descriptor.FieldDescriptor + +class Operation(_message.Message): + __slots__ = ("name", "metadata", "done", "error", "response") + NAME_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + DONE_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + RESPONSE_FIELD_NUMBER: _ClassVar[int] + name: str + metadata: _any_pb2.Any + done: bool + error: _status_pb2.Status + response: _any_pb2.Any + def __init__( + self, + name: _Optional[str] = ..., + metadata: _Optional[_Union[_any_pb2.Any, _Mapping]] = ..., + done: _Optional[bool] = ..., + error: _Optional[_Union[_status_pb2.Status, _Mapping]] = ..., + response: _Optional[_Union[_any_pb2.Any, _Mapping]] = ..., + ) -> None: ... + +class GetOperationRequest(_message.Message): + __slots__ = ("name",) + NAME_FIELD_NUMBER: _ClassVar[int] + name: str + def __init__(self, name: _Optional[str] = ...) -> None: ... + +class ListOperationsRequest(_message.Message): + __slots__ = ("name", "filter", "page_size", "page_token", "return_partial_success") + NAME_FIELD_NUMBER: _ClassVar[int] + FILTER_FIELD_NUMBER: _ClassVar[int] + PAGE_SIZE_FIELD_NUMBER: _ClassVar[int] + PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] + RETURN_PARTIAL_SUCCESS_FIELD_NUMBER: _ClassVar[int] + name: str + filter: str + page_size: int + page_token: str + return_partial_success: bool + def __init__( + self, + name: _Optional[str] = ..., + filter: _Optional[str] = ..., + page_size: _Optional[int] = ..., + page_token: _Optional[str] = ..., + return_partial_success: _Optional[bool] = ..., + ) -> None: ... + +class ListOperationsResponse(_message.Message): + __slots__ = ("operations", "next_page_token", "unreachable") + OPERATIONS_FIELD_NUMBER: _ClassVar[int] + NEXT_PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] + UNREACHABLE_FIELD_NUMBER: _ClassVar[int] + operations: _containers.RepeatedCompositeFieldContainer[Operation] + next_page_token: str + unreachable: _containers.RepeatedScalarFieldContainer[str] + def __init__( + self, + operations: _Optional[_Iterable[_Union[Operation, _Mapping]]] = ..., + next_page_token: _Optional[str] = ..., + unreachable: _Optional[_Iterable[str]] = ..., + ) -> None: ... + +class CancelOperationRequest(_message.Message): + __slots__ = ("name",) + NAME_FIELD_NUMBER: _ClassVar[int] + name: str + def __init__(self, name: _Optional[str] = ...) -> None: ... + +class DeleteOperationRequest(_message.Message): + __slots__ = ("name",) + NAME_FIELD_NUMBER: _ClassVar[int] + name: str + def __init__(self, name: _Optional[str] = ...) -> None: ... + +class WaitOperationRequest(_message.Message): + __slots__ = ("name", "timeout") + NAME_FIELD_NUMBER: _ClassVar[int] + TIMEOUT_FIELD_NUMBER: _ClassVar[int] + name: str + timeout: _duration_pb2.Duration + def __init__( + self, + name: _Optional[str] = ..., + timeout: _Optional[ + _Union[datetime.timedelta, _duration_pb2.Duration, _Mapping] + ] = ..., + ) -> None: ... + +class OperationInfo(_message.Message): + __slots__ = ("response_type", "metadata_type") + RESPONSE_TYPE_FIELD_NUMBER: _ClassVar[int] + METADATA_TYPE_FIELD_NUMBER: _ClassVar[int] + response_type: str + metadata_type: str + def __init__( + self, response_type: _Optional[str] = ..., metadata_type: _Optional[str] = ... + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__init__.py b/venv/lib/python3.12/site-packages/google/protobuf/__init__.py new file mode 100755 index 0000000..2273ed8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/__init__.py @@ -0,0 +1,10 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +# Copyright 2007 Google Inc. All Rights Reserved. + +__version__ = '7.36.2' diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..56b46d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/any.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/any.cpython-312.pyc new file mode 100644 index 0000000..2f2632b Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/any.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/any_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/any_pb2.cpython-312.pyc new file mode 100644 index 0000000..63551ce Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/any_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/api_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/api_pb2.cpython-312.pyc new file mode 100644 index 0000000..aa854be Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/api_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/descriptor.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/descriptor.cpython-312.pyc new file mode 100644 index 0000000..4c2c757 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/descriptor.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/descriptor_database.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/descriptor_database.cpython-312.pyc new file mode 100644 index 0000000..8b447d9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/descriptor_database.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/descriptor_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/descriptor_pb2.cpython-312.pyc new file mode 100644 index 0000000..660eecc Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/descriptor_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/descriptor_pool.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/descriptor_pool.cpython-312.pyc new file mode 100644 index 0000000..241113c Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/descriptor_pool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/duration.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/duration.cpython-312.pyc new file mode 100644 index 0000000..756ef2e Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/duration.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/duration_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/duration_pb2.cpython-312.pyc new file mode 100644 index 0000000..1f8cc20 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/duration_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/empty_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/empty_pb2.cpython-312.pyc new file mode 100644 index 0000000..c63d954 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/empty_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/field_mask_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/field_mask_pb2.cpython-312.pyc new file mode 100644 index 0000000..c9d1a2e Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/field_mask_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/json_enumvalue_options_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/json_enumvalue_options_pb2.cpython-312.pyc new file mode 100644 index 0000000..bf68416 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/json_enumvalue_options_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/json_format.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/json_format.cpython-312.pyc new file mode 100644 index 0000000..66ee448 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/json_format.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/json_options_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/json_options_pb2.cpython-312.pyc new file mode 100644 index 0000000..5cab818 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/json_options_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/message.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/message.cpython-312.pyc new file mode 100644 index 0000000..6fd93c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/message_factory.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/message_factory.cpython-312.pyc new file mode 100644 index 0000000..cfd5e6c Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/message_factory.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/proto.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/proto.cpython-312.pyc new file mode 100644 index 0000000..670387f Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/proto.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/proto_builder.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/proto_builder.cpython-312.pyc new file mode 100644 index 0000000..c6fd15f Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/proto_builder.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/proto_json.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/proto_json.cpython-312.pyc new file mode 100644 index 0000000..81af084 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/proto_json.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/proto_text.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/proto_text.cpython-312.pyc new file mode 100644 index 0000000..de1294e Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/proto_text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/reflection.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/reflection.cpython-312.pyc new file mode 100644 index 0000000..0b52601 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/reflection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/runtime_version.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/runtime_version.cpython-312.pyc new file mode 100644 index 0000000..c565695 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/runtime_version.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/service_reflection.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/service_reflection.cpython-312.pyc new file mode 100644 index 0000000..2dd9e1d Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/service_reflection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/source_context_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/source_context_pb2.cpython-312.pyc new file mode 100644 index 0000000..31acd2a Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/source_context_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/struct_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/struct_pb2.cpython-312.pyc new file mode 100644 index 0000000..76b0f9a Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/struct_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/symbol_database.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/symbol_database.cpython-312.pyc new file mode 100644 index 0000000..5d491f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/symbol_database.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/text_encoding.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/text_encoding.cpython-312.pyc new file mode 100644 index 0000000..d701b7c Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/text_encoding.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/text_format.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/text_format.cpython-312.pyc new file mode 100644 index 0000000..5f93212 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/text_format.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/timestamp.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/timestamp.cpython-312.pyc new file mode 100644 index 0000000..593beb5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/timestamp.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/timestamp_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/timestamp_pb2.cpython-312.pyc new file mode 100644 index 0000000..22ba04b Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/timestamp_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/type_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/type_pb2.cpython-312.pyc new file mode 100644 index 0000000..bd71f17 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/type_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/unknown_fields.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/unknown_fields.cpython-312.pyc new file mode 100644 index 0000000..7b88430 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/unknown_fields.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/wrappers_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/wrappers_pb2.cpython-312.pyc new file mode 100644 index 0000000..d1c1b7e Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/__pycache__/wrappers_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/any.py b/venv/lib/python3.12/site-packages/google/protobuf/any.py new file mode 100755 index 0000000..4ebedc3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/any.py @@ -0,0 +1,52 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +"""Contains the Any helper APIs.""" + +from typing import Optional, TypeVar + +from google.protobuf import descriptor +from google.protobuf.message import Message + +from google.protobuf.any_pb2 import Any + +_MessageT = TypeVar('_MessageT', bound=Message) + + +def pack( + msg: Message, + type_url_prefix: Optional[str] = 'type.googleapis.com/', + deterministic: Optional[bool] = None, +) -> Any: + any_msg = Any() + any_msg.Pack( + msg=msg, type_url_prefix=type_url_prefix, deterministic=deterministic + ) + return any_msg + + +def unpack(any_msg: Any, msg: Message) -> bool: + return any_msg.Unpack(msg=msg) + + +def unpack_as(any_msg: Any, message_type: type[_MessageT]) -> _MessageT: + unpacked = message_type() + if unpack(any_msg, unpacked): + return unpacked + else: + raise TypeError( + f'Attempted to unpack {type_name(any_msg)} to' + f' {message_type.__qualname__}' + ) + + +def type_name(any_msg: Any) -> str: + return any_msg.TypeName() + + +def is_type(any_msg: Any, des: descriptor.Descriptor) -> bool: + return any_msg.Is(des) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/any_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/any_pb2.py new file mode 100755 index 0000000..5fe7ef3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/any_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/any.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/any.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19google/protobuf/any.proto\x12\x0fgoogle.protobuf\"6\n\x03\x41ny\x12\x19\n\x08type_url\x18\x01 \x01(\tR\x07typeUrl\x12\x14\n\x05value\x18\x02 \x01(\x0cR\x05valueBv\n\x13\x63om.google.protobufB\x08\x41nyProtoP\x01Z,google.golang.org/protobuf/types/known/anypb\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.any_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\023com.google.protobufB\010AnyProtoP\001Z,google.golang.org/protobuf/types/known/anypb\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _globals['_ANY']._serialized_start=46 + _globals['_ANY']._serialized_end=100 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/api_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/api_pb2.py new file mode 100755 index 0000000..806c8e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/api_pb2.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/api.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/api.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import source_context_pb2 as google_dot_protobuf_dot_source__context__pb2 +from google.protobuf import type_pb2 as google_dot_protobuf_dot_type__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19google/protobuf/api.proto\x12\x0fgoogle.protobuf\x1a$google/protobuf/source_context.proto\x1a\x1agoogle/protobuf/type.proto\"\xdb\x02\n\x03\x41pi\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x31\n\x07methods\x18\x02 \x03(\x0b\x32\x17.google.protobuf.MethodR\x07methods\x12\x31\n\x07options\x18\x03 \x03(\x0b\x32\x17.google.protobuf.OptionR\x07options\x12\x18\n\x07version\x18\x04 \x01(\tR\x07version\x12\x45\n\x0esource_context\x18\x05 \x01(\x0b\x32\x1e.google.protobuf.SourceContextR\rsourceContext\x12.\n\x06mixins\x18\x06 \x03(\x0b\x32\x16.google.protobuf.MixinR\x06mixins\x12/\n\x06syntax\x18\x07 \x01(\x0e\x32\x17.google.protobuf.SyntaxR\x06syntax\x12\x18\n\x07\x65\x64ition\x18\x08 \x01(\tR\x07\x65\x64ition\"\xd4\x02\n\x06Method\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12(\n\x10request_type_url\x18\x02 \x01(\tR\x0erequestTypeUrl\x12+\n\x11request_streaming\x18\x03 \x01(\x08R\x10requestStreaming\x12*\n\x11response_type_url\x18\x04 \x01(\tR\x0fresponseTypeUrl\x12-\n\x12response_streaming\x18\x05 \x01(\x08R\x11responseStreaming\x12\x31\n\x07options\x18\x06 \x03(\x0b\x32\x17.google.protobuf.OptionR\x07options\x12\x33\n\x06syntax\x18\x07 \x01(\x0e\x32\x17.google.protobuf.SyntaxB\x02\x18\x01R\x06syntax\x12\x1c\n\x07\x65\x64ition\x18\x08 \x01(\tB\x02\x18\x01R\x07\x65\x64ition\"/\n\x05Mixin\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04root\x18\x02 \x01(\tR\x04rootBv\n\x13\x63om.google.protobufB\x08\x41piProtoP\x01Z,google.golang.org/protobuf/types/known/apipb\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.api_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\023com.google.protobufB\010ApiProtoP\001Z,google.golang.org/protobuf/types/known/apipb\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _globals['_METHOD'].fields_by_name['syntax']._loaded_options = None + _globals['_METHOD'].fields_by_name['syntax']._serialized_options = b'\030\001' + _globals['_METHOD'].fields_by_name['edition']._loaded_options = None + _globals['_METHOD'].fields_by_name['edition']._serialized_options = b'\030\001' + _globals['_API']._serialized_start=113 + _globals['_API']._serialized_end=460 + _globals['_METHOD']._serialized_start=463 + _globals['_METHOD']._serialized_end=803 + _globals['_MIXIN']._serialized_start=805 + _globals['_MIXIN']._serialized_end=852 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/compiler/__init__.py b/venv/lib/python3.12/site-packages/google/protobuf/compiler/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/google/protobuf/compiler/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/compiler/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..366f4a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/compiler/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/compiler/__pycache__/plugin_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/compiler/__pycache__/plugin_pb2.cpython-312.pyc new file mode 100644 index 0000000..3b5c548 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/compiler/__pycache__/plugin_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/compiler/plugin_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/compiler/plugin_pb2.py new file mode 100755 index 0000000..b0d5ca7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/compiler/plugin_pb2.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/compiler/plugin.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/compiler/plugin.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%google/protobuf/compiler/plugin.proto\x12\x18google.protobuf.compiler\x1a google/protobuf/descriptor.proto\"c\n\x07Version\x12\x14\n\x05major\x18\x01 \x01(\x05R\x05major\x12\x14\n\x05minor\x18\x02 \x01(\x05R\x05minor\x12\x14\n\x05patch\x18\x03 \x01(\x05R\x05patch\x12\x16\n\x06suffix\x18\x04 \x01(\tR\x06suffix\"\xcf\x02\n\x14\x43odeGeneratorRequest\x12(\n\x10\x66ile_to_generate\x18\x01 \x03(\tR\x0e\x66ileToGenerate\x12\x1c\n\tparameter\x18\x02 \x01(\tR\tparameter\x12\x43\n\nproto_file\x18\x0f \x03(\x0b\x32$.google.protobuf.FileDescriptorProtoR\tprotoFile\x12\\\n\x17source_file_descriptors\x18\x11 \x03(\x0b\x32$.google.protobuf.FileDescriptorProtoR\x15sourceFileDescriptors\x12L\n\x10\x63ompiler_version\x18\x03 \x01(\x0b\x32!.google.protobuf.compiler.VersionR\x0f\x63ompilerVersion\"\x85\x04\n\x15\x43odeGeneratorResponse\x12\x14\n\x05\x65rror\x18\x01 \x01(\tR\x05\x65rror\x12-\n\x12supported_features\x18\x02 \x01(\x04R\x11supportedFeatures\x12\'\n\x0fminimum_edition\x18\x03 \x01(\x05R\x0eminimumEdition\x12\'\n\x0fmaximum_edition\x18\x04 \x01(\x05R\x0emaximumEdition\x12H\n\x04\x66ile\x18\x0f \x03(\x0b\x32\x34.google.protobuf.compiler.CodeGeneratorResponse.FileR\x04\x66ile\x1a\xb1\x01\n\x04\x46ile\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\'\n\x0finsertion_point\x18\x02 \x01(\tR\x0einsertionPoint\x12\x18\n\x07\x63ontent\x18\x0f \x01(\tR\x07\x63ontent\x12R\n\x13generated_code_info\x18\x10 \x01(\x0b\x32\".google.protobuf.GeneratedCodeInfoR\x11generatedCodeInfo\"W\n\x07\x46\x65\x61ture\x12\x10\n\x0c\x46\x45\x41TURE_NONE\x10\x00\x12\x1b\n\x17\x46\x45\x41TURE_PROTO3_OPTIONAL\x10\x01\x12\x1d\n\x19\x46\x45\x41TURE_SUPPORTS_EDITIONS\x10\x02\x42r\n\x1c\x63om.google.protobuf.compilerB\x0cPluginProtosZ)google.golang.org/protobuf/types/pluginpb\xaa\x02\x18Google.Protobuf.Compiler') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.compiler.plugin_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\034com.google.protobuf.compilerB\014PluginProtosZ)google.golang.org/protobuf/types/pluginpb\252\002\030Google.Protobuf.Compiler' + _globals['_VERSION']._serialized_start=101 + _globals['_VERSION']._serialized_end=200 + _globals['_CODEGENERATORREQUEST']._serialized_start=203 + _globals['_CODEGENERATORREQUEST']._serialized_end=538 + _globals['_CODEGENERATORRESPONSE']._serialized_start=541 + _globals['_CODEGENERATORRESPONSE']._serialized_end=1058 + _globals['_CODEGENERATORRESPONSE_FILE']._serialized_start=792 + _globals['_CODEGENERATORRESPONSE_FILE']._serialized_end=969 + _globals['_CODEGENERATORRESPONSE_FEATURE']._serialized_start=971 + _globals['_CODEGENERATORRESPONSE_FEATURE']._serialized_end=1058 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/descriptor.py b/venv/lib/python3.12/site-packages/google/protobuf/descriptor.py new file mode 100755 index 0000000..6cc8144 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/descriptor.py @@ -0,0 +1,1669 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Descriptors essentially contain exactly the information found in a .proto + +file, in types that make this information accessible in Python. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import abc +import binascii +import os +import threading +import warnings + +from google.protobuf.internal import api_implementation + +_USE_C_DESCRIPTORS = False +if api_implementation.Type() != 'python': + # pylint: disable=protected-access + _message = api_implementation._c_module + # TODO: Remove this import after fix api_implementation + if _message is None: + from google.protobuf.pyext import _message + _USE_C_DESCRIPTORS = True + + +class Error(Exception): + """Base error for this module.""" + + +class TypeTransformationError(Error): + """Error transforming between python proto type and corresponding C++ type.""" + + +if _USE_C_DESCRIPTORS: + # This metaclass allows to override the behavior of code like + # isinstance(my_descriptor, FieldDescriptor) + # and make it return True when the descriptor is an instance of the extension + # type written in C++. + class DescriptorMetaclass(type): + + def __instancecheck__(cls, obj): + if super(DescriptorMetaclass, cls).__instancecheck__(obj): + return True + if isinstance(obj, cls._C_DESCRIPTOR_CLASS): + return True + return False + +else: + # The standard metaclass; nothing changes. + DescriptorMetaclass = abc.ABCMeta + + +class _Lock(object): + """Wrapper class of threading.Lock(), which is allowed by 'with'.""" + + def __new__(cls): + self = object.__new__(cls) + self._lock = threading.Lock() # pylint: disable=protected-access + return self + + def __enter__(self): + self._lock.acquire() + + def __exit__(self, exc_type, exc_value, exc_tb): + self._lock.release() + + +_lock = threading.Lock() + + +def _Deprecated( + name, + alternative='get/find descriptors from generated code or query the descriptor_pool', +): + if _Deprecated.count > 0: + _Deprecated.count -= 1 + warnings.warn( + 'Call to deprecated %s, use %s instead.' % (name, alternative), + category=DeprecationWarning, + stacklevel=3, + ) + + +# These must match the values in descriptor.proto, but we can't use them +# directly because we sometimes need to reference them in feature helpers +# below *during* the build of descriptor.proto. +_FEATURESET_MESSAGE_ENCODING_DELIMITED = 2 +_FEATURESET_FIELD_PRESENCE_IMPLICIT = 2 +_FEATURESET_FIELD_PRESENCE_LEGACY_REQUIRED = 3 +_FEATURESET_REPEATED_FIELD_ENCODING_PACKED = 1 +_FEATURESET_ENUM_TYPE_CLOSED = 2 + +# Deprecated warnings will print 100 times at most which should be enough for +# users to notice and do not cause timeout. +_Deprecated.count = 100 + +_internal_create_key = object() + + +class DescriptorBase(metaclass=DescriptorMetaclass): + """Descriptors base class. + + This class is the base of all descriptor classes. It provides common options + related functionality. + + Attributes: + has_options: True if the descriptor has non-default options. Usually it is + not necessary to read this -- just call GetOptions() which will happily + return the default instance. However, it's sometimes useful for + efficiency, and also useful inside the protobuf implementation to avoid + some bootstrapping issues. + file (FileDescriptor): Reference to file info. + """ + + if _USE_C_DESCRIPTORS: + # The class, or tuple of classes, that are considered as "virtual + # subclasses" of this descriptor class. + _C_DESCRIPTOR_CLASS = () + + def __init__(self, file, options, serialized_options, options_class_name): + """Initialize the descriptor given its options message and the name of the + + class of the options message. The name of the class is required in case + the options message is None and has to be created. + """ + self._features = None + self.file = file + self._original_options = options + # These two fields are duplicated as a compatibility shim for old gencode + # that resets them. In 26.x (cl/580304039) we renamed _options to, + # _loaded_options breaking backwards compatibility. + self._options = self._loaded_options = None + self._options_class_name = options_class_name + self._serialized_options = serialized_options + + # Does this descriptor have non-default options? + self.has_options = (self._original_options is not None) or ( + self._serialized_options is not None + ) + + @property + @abc.abstractmethod + def _parent(self): + pass + + def _InferLegacyFeatures(self, edition, options, features): + """Infers features from proto2/proto3 syntax so that editions logic can be used everywhere. + + Args: + edition: The edition to infer features for. + options: The options for this descriptor that are being processed. + features: The feature set object to modify with inferred features. + """ + pass + + def _GetFeatures(self): + if not self._features: + self._LazyLoadOptions() + return self._features + + def _ResolveFeatures(self, edition, raw_options): + """Resolves features from the raw options of this descriptor. + + Args: + edition: The edition to use for feature defaults. + raw_options: The options for this descriptor that are being processed. + + Returns: + A fully resolved feature set for making runtime decisions. + """ + # pylint: disable=g-import-not-at-top + from google.protobuf import descriptor_pb2 + + if self._parent: + features = descriptor_pb2.FeatureSet() + features.CopyFrom(self._parent._GetFeatures()) + else: + features = self.file.pool._CreateDefaultFeatures(edition) + unresolved = descriptor_pb2.FeatureSet() + unresolved.CopyFrom(raw_options.features) + self._InferLegacyFeatures(edition, raw_options, unresolved) + features.MergeFrom(unresolved) + + # Use the feature cache to reduce memory bloat. + return self.file.pool._InternFeatures(features) + + def _LazyLoadOptions(self): + """Lazily initializes descriptor options towards the end of the build.""" + if self._options and self._loaded_options == self._options: + # If neither has been reset by gencode, use the cache. + return + + # pylint: disable=g-import-not-at-top + from google.protobuf import descriptor_pb2 + + if not hasattr(descriptor_pb2, self._options_class_name): + raise RuntimeError( + 'Unknown options class name %s!' % self._options_class_name + ) + options_class = getattr(descriptor_pb2, self._options_class_name) + features = None + edition = self.file._edition + + if not self.has_options: + if not self._features: + features = self._ResolveFeatures( + descriptor_pb2.Edition.Value(edition), options_class() + ) + with _lock: + self._options = self._loaded_options = options_class() + if not self._features: + self._features = features + else: + if not self._serialized_options: + options = self._original_options + else: + options = _ParseOptions(options_class(), self._serialized_options) + + if not self._features: + features = self._ResolveFeatures( + descriptor_pb2.Edition.Value(edition), options + ) + with _lock: + self._options = self._loaded_options = options + if not self._features: + self._features = features + if options.HasField('features'): + options.ClearField('features') + if not options.SerializeToString(): + self._options = self._loaded_options = options_class() + self.has_options = False + + def GetOptions(self): + """Retrieves descriptor options. + + Returns: + The options set on this descriptor. + """ + # If either has been reset by gencode, reload options. + if not self._options or not self._loaded_options: + self._LazyLoadOptions() + if ( + self._options + and hasattr(self._options, '_SetFrozen') + and not getattr(self._options, '_frozen', False) + ): + self._options._SetFrozen() + return self._options + + +class _NestedDescriptorBase(DescriptorBase): + """Common class for descriptors that can be nested.""" + + def __init__( + self, + options, + options_class_name, + name, + full_name, + file, + containing_type, + serialized_start=None, + serialized_end=None, + serialized_options=None, + ): + """Constructor. + + Args: + options: Protocol message options or None to use default message options. + options_class_name (str): The class name of the above options. + name (str): Name of this protocol message type. + full_name (str): Fully-qualified name of this protocol message type, which + will include protocol "package" name and the name of any enclosing + types. + containing_type: if provided, this is a nested descriptor, with this + descriptor as parent, otherwise None. + serialized_start: The start index (inclusive) in block in the + file.serialized_pb that describes this descriptor. + serialized_end: The end index (exclusive) in block in the + file.serialized_pb that describes this descriptor. + serialized_options: Protocol message serialized options or None. + """ + super(_NestedDescriptorBase, self).__init__( + file, options, serialized_options, options_class_name + ) + + self.name = name + # TODO: Add function to calculate full_name instead of having it in + # memory? + self.full_name = full_name + self.containing_type = containing_type + + self._serialized_start = serialized_start + self._serialized_end = serialized_end + + def CopyToProto(self, proto): + """Copies this to the matching proto in descriptor_pb2. + + Args: + proto: An empty proto instance from descriptor_pb2. + + Raises: + Error: If self couldn't be serialized, due to to few constructor + arguments. + """ + if ( + self.file is not None + and self._serialized_start is not None + and self._serialized_end is not None + ): + proto.ParseFromString( + self.file.serialized_pb[self._serialized_start : self._serialized_end] + ) + else: + raise Error('Descriptor does not contain serialization.') + + +class Descriptor(_NestedDescriptorBase): + """Descriptor for a protocol message type. + + Attributes: + name (str): Name of this protocol message type. + full_name (str): Fully-qualified name of this protocol message type, which + will include protocol "package" name and the name of any enclosing + types. + containing_type (Descriptor): Reference to the descriptor of the type + containing us, or None if this is top-level. + fields (list[FieldDescriptor]): Field descriptors for all fields in this + type. + fields_by_number (dict(int, FieldDescriptor)): Same + :class:`FieldDescriptor` objects as in :attr:`fields`, but indexed by + "number" attribute in each FieldDescriptor. + fields_by_name (dict(str, FieldDescriptor)): Same :class:`FieldDescriptor` + objects as in :attr:`fields`, but indexed by "name" attribute in each + :class:`FieldDescriptor`. + nested_types (list[Descriptor]): Descriptor references for all protocol + message types nested within this one. + nested_types_by_name (dict(str, Descriptor)): Same Descriptor objects as + in :attr:`nested_types`, but indexed by "name" attribute in each + Descriptor. + enum_types (list[EnumDescriptor]): :class:`EnumDescriptor` references for + all enums contained within this type. + enum_types_by_name (dict(str, EnumDescriptor)): Same + :class:`EnumDescriptor` objects as in :attr:`enum_types`, but indexed by + "name" attribute in each EnumDescriptor. + enum_values_by_name (dict(str, EnumValueDescriptor)): Dict mapping from + enum value name to :class:`EnumValueDescriptor` for that value. + extensions (list[FieldDescriptor]): All extensions defined directly within + this message type (NOT within a nested type). + extensions_by_name (dict(str, FieldDescriptor)): Same FieldDescriptor + objects as :attr:`extensions`, but indexed by "name" attribute of each + FieldDescriptor. + is_extendable (bool): Does this type define any extension ranges? + oneofs (list[OneofDescriptor]): The list of descriptors for oneof fields + in this message. + oneofs_by_name (dict(str, OneofDescriptor)): Same objects as in + :attr:`oneofs`, but indexed by "name" attribute. + file (FileDescriptor): Reference to file descriptor. + is_map_entry: If the message type is a map entry. + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.Descriptor + + def __new__( + cls, + name=None, + full_name=None, + filename=None, + containing_type=None, + fields=None, + nested_types=None, + enum_types=None, + extensions=None, + options=None, + serialized_options=None, + is_extendable=True, + extension_ranges=None, + oneofs=None, + file=None, # pylint: disable=redefined-builtin + serialized_start=None, + serialized_end=None, + syntax=None, + is_map_entry=False, + create_key=None, + ): + _message.Message._CheckCalledFromGeneratedFile() + return _message.default_pool.FindMessageTypeByName(full_name) + + # NOTE: The file argument redefining a builtin is nothing we can + # fix right now since we don't know how many clients already rely on the + # name of the argument. + def __init__( + self, + name, + full_name, + filename, + containing_type, + fields, + nested_types, + enum_types, + extensions, + options=None, + serialized_options=None, + is_extendable=True, + extension_ranges=None, + oneofs=None, + file=None, + serialized_start=None, + serialized_end=None, # pylint: disable=redefined-builtin + syntax=None, + is_map_entry=False, + create_key=None, + ): + """Arguments to __init__() are as described in the description + + of Descriptor fields above. + + Note that filename is an obsolete argument, that is not used anymore. + Please use file.name to access this as an attribute. + """ + if create_key is not _internal_create_key: + _Deprecated('create function Descriptor()') + + super(Descriptor, self).__init__( + options, + 'MessageOptions', + name, + full_name, + file, + containing_type, + serialized_start=serialized_start, + serialized_end=serialized_end, + serialized_options=serialized_options, + ) + + # We have fields in addition to fields_by_name and fields_by_number, + # so that: + # 1. Clients can index fields by "order in which they're listed." + # 2. Clients can easily iterate over all fields with the terse + # syntax: for f in descriptor.fields: ... + self.fields = fields + for field in self.fields: + field.containing_type = self + field.file = file + self.fields_by_number = dict((f.number, f) for f in fields) + self.fields_by_name = dict((f.name, f) for f in fields) + self._fields_by_camelcase_name = None + + self.nested_types = nested_types + for nested_type in nested_types: + nested_type.containing_type = self + self.nested_types_by_name = dict((t.name, t) for t in nested_types) + + self.enum_types = enum_types + for enum_type in self.enum_types: + enum_type.containing_type = self + self.enum_types_by_name = dict((t.name, t) for t in enum_types) + self.enum_values_by_name = dict( + (v.name, v) for t in enum_types for v in t.values + ) + + self.extensions = extensions + for extension in self.extensions: + extension.extension_scope = self + self.extensions_by_name = dict((f.name, f) for f in extensions) + self.is_extendable = is_extendable + self.extension_ranges = extension_ranges + self.oneofs = oneofs if oneofs is not None else [] + self.oneofs_by_name = dict((o.name, o) for o in self.oneofs) + for oneof in self.oneofs: + oneof.containing_type = self + oneof.file = file + self._is_map_entry = is_map_entry + + @property + def _parent(self): + return self.containing_type or self.file + + @property + def fields_by_camelcase_name(self): + """Same FieldDescriptor objects as in :attr:`fields`, but indexed by + + :attr:`FieldDescriptor.camelcase_name`. + """ + if self._fields_by_camelcase_name is None: + self._fields_by_camelcase_name = dict( + (f.camelcase_name, f) for f in self.fields + ) + return self._fields_by_camelcase_name + + def EnumValueName(self, enum, value): + """Returns the string name of an enum value. + + This is just a small helper method to simplify a common operation. + + Args: + enum: string name of the Enum. + value: int, value of the enum. + + Returns: + string name of the enum value. + + Raises: + KeyError if either the Enum doesn't exist or the value is not a valid + value for the enum. + """ + return self.enum_types_by_name[enum].values_by_number[value].name + + def CopyToProto(self, proto): + """Copies this to a descriptor_pb2.DescriptorProto. + + Args: + proto: An empty descriptor_pb2.DescriptorProto. + """ + # This function is overridden to give a better doc comment. + super(Descriptor, self).CopyToProto(proto) + + +# TODO: We should have aggressive checking here, +# for example: +# * If you specify a repeated field, you should not be allowed +# to specify a default value. +# * [Other examples here as needed]. +# +# TODO: for this and other *Descriptor classes, we +# might also want to lock things down aggressively (e.g., +# prevent clients from setting the attributes). Having +# stronger invariants here in general will reduce the number +# of runtime checks we must do in reflection.py... +class FieldDescriptor(DescriptorBase): + """Descriptor for a single field in a .proto file. + + Attributes: + name (str): Name of this field, exactly as it appears in .proto. + full_name (str): Name of this field, including containing scope. This is + particularly relevant for extensions. + index (int): Dense, 0-indexed index giving the order that this field + textually appears within its message in the .proto file. + number (int): Tag number declared for this field in the .proto file. + type (int): (One of the TYPE_* constants below) Declared type. + cpp_type (int): (One of the CPPTYPE_* constants below) C++ type used to + represent this field. + label (int): (One of the LABEL_* constants below) Tells whether this field + is optional, required, or repeated. + has_default_value (bool): True if this field has a default value defined, + otherwise false. + default_value (Varies): Default value of this field. Only meaningful for + non-repeated scalar fields. Repeated fields should always set this to [], + and non-repeated composite fields should always set this to None. + containing_type (Descriptor): Descriptor of the protocol message type that + contains this field. Set by the Descriptor constructor if we're passed + into one. Somewhat confusingly, for extension fields, this is the + descriptor of the EXTENDED message, not the descriptor of the message + containing this field. (See is_extension and extension_scope below). + message_type (Descriptor): If a composite field, a descriptor of the message + type contained in this field. Otherwise, this is None. + enum_type (EnumDescriptor): If this field contains an enum, a descriptor of + that enum. Otherwise, this is None. + is_extension: True iff this describes an extension field. + extension_scope (Descriptor): Only meaningful if is_extension is True. Gives + the message that immediately contains this extension field. Will be None + iff we're a top-level (file-level) extension field. + options (descriptor_pb2.FieldOptions): Protocol message field options or + None to use default field options. + containing_oneof (OneofDescriptor): If the field is a member of a oneof + union, contains its descriptor. Otherwise, None. + file (FileDescriptor): Reference to file descriptor. + """ + + # Must be consistent with C++ FieldDescriptor::Type enum in + # descriptor.h. + # + # TODO: Find a way to eliminate this repetition. + TYPE_DOUBLE = 1 + TYPE_FLOAT = 2 + TYPE_INT64 = 3 + TYPE_UINT64 = 4 + TYPE_INT32 = 5 + TYPE_FIXED64 = 6 + TYPE_FIXED32 = 7 + TYPE_BOOL = 8 + TYPE_STRING = 9 + TYPE_GROUP = 10 + TYPE_MESSAGE = 11 + TYPE_BYTES = 12 + TYPE_UINT32 = 13 + TYPE_ENUM = 14 + TYPE_SFIXED32 = 15 + TYPE_SFIXED64 = 16 + TYPE_SINT32 = 17 + TYPE_SINT64 = 18 + MAX_TYPE = 18 + + # Must be consistent with C++ FieldDescriptor::CppType enum in + # descriptor.h. + # + # TODO: Find a way to eliminate this repetition. + CPPTYPE_INT32 = 1 + CPPTYPE_INT64 = 2 + CPPTYPE_UINT32 = 3 + CPPTYPE_UINT64 = 4 + CPPTYPE_DOUBLE = 5 + CPPTYPE_FLOAT = 6 + CPPTYPE_BOOL = 7 + CPPTYPE_ENUM = 8 + CPPTYPE_STRING = 9 + CPPTYPE_MESSAGE = 10 + MAX_CPPTYPE = 10 + + _PYTHON_TO_CPP_PROTO_TYPE_MAP = { + TYPE_DOUBLE: CPPTYPE_DOUBLE, + TYPE_FLOAT: CPPTYPE_FLOAT, + TYPE_ENUM: CPPTYPE_ENUM, + TYPE_INT64: CPPTYPE_INT64, + TYPE_SINT64: CPPTYPE_INT64, + TYPE_SFIXED64: CPPTYPE_INT64, + TYPE_UINT64: CPPTYPE_UINT64, + TYPE_FIXED64: CPPTYPE_UINT64, + TYPE_INT32: CPPTYPE_INT32, + TYPE_SFIXED32: CPPTYPE_INT32, + TYPE_SINT32: CPPTYPE_INT32, + TYPE_UINT32: CPPTYPE_UINT32, + TYPE_FIXED32: CPPTYPE_UINT32, + TYPE_BYTES: CPPTYPE_STRING, + TYPE_STRING: CPPTYPE_STRING, + TYPE_BOOL: CPPTYPE_BOOL, + TYPE_MESSAGE: CPPTYPE_MESSAGE, + TYPE_GROUP: CPPTYPE_MESSAGE, + } + + # Must be consistent with C++ FieldDescriptor::Label enum in + # descriptor.h. + # + # TODO: Find a way to eliminate this repetition. + LABEL_OPTIONAL = 1 + LABEL_REQUIRED = 2 + LABEL_REPEATED = 3 + MAX_LABEL = 3 + + # Must be consistent with C++ constants kMaxNumber, kFirstReservedNumber, + # and kLastReservedNumber in descriptor.h + MAX_FIELD_NUMBER = (1 << 29) - 1 + FIRST_RESERVED_FIELD_NUMBER = 19000 + LAST_RESERVED_FIELD_NUMBER = 19999 + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.FieldDescriptor + + def __new__( + cls, + name, + full_name, + index, + number, + type, + cpp_type, + label, + default_value, + message_type, + enum_type, + containing_type, + is_extension, + extension_scope, + options=None, + serialized_options=None, + has_default_value=True, + containing_oneof=None, + json_name=None, + file=None, + create_key=None, + ): # pylint: disable=redefined-builtin + _message.Message._CheckCalledFromGeneratedFile() + if is_extension: + return _message.default_pool.FindExtensionByName(full_name) + else: + return _message.default_pool.FindFieldByName(full_name) + + def __init__( + self, + name, + full_name, + index, + number, + type, + cpp_type, + label, + default_value, + message_type, + enum_type, + containing_type, + is_extension, + extension_scope, + options=None, + serialized_options=None, + has_default_value=True, + containing_oneof=None, + json_name=None, + file=None, + create_key=None, + ): # pylint: disable=redefined-builtin + """The arguments are as described in the description of FieldDescriptor + + attributes above. + + Note that containing_type may be None, and may be set later if necessary + (to deal with circular references between message types, for example). + Likewise for extension_scope. + """ + if create_key is not _internal_create_key: + _Deprecated('create function FieldDescriptor()') + + super(FieldDescriptor, self).__init__( + file, options, serialized_options, 'FieldOptions' + ) + self.name = name + self.full_name = full_name + self._camelcase_name = None + if json_name is None: + self.json_name = _ToJsonName(name) + else: + self.json_name = json_name + self.index = index + self.number = number + self._type = type + self.cpp_type = cpp_type + self._label = label + self.has_default_value = has_default_value + self.default_value = default_value + self.containing_type = containing_type + self.message_type = message_type + self.enum_type = enum_type + self.is_extension = is_extension + self.extension_scope = extension_scope + self.containing_oneof = containing_oneof + if api_implementation.Type() == 'python': + self._cdescriptor = None + else: + if is_extension: + self._cdescriptor = _message.default_pool.FindExtensionByName(full_name) + else: + self._cdescriptor = _message.default_pool.FindFieldByName(full_name) + + @property + def _parent(self): + if self.containing_oneof: + return self.containing_oneof + if self.is_extension: + return self.extension_scope or self.file + return self.containing_type + + def _InferLegacyFeatures(self, edition, options, features): + # pylint: disable=g-import-not-at-top + from google.protobuf import descriptor_pb2 + + if edition >= descriptor_pb2.Edition.EDITION_2023: + return + + if self._label == FieldDescriptor.LABEL_REQUIRED: + features.field_presence = ( + descriptor_pb2.FeatureSet.FieldPresence.LEGACY_REQUIRED + ) + + if self._type == FieldDescriptor.TYPE_GROUP: + features.message_encoding = ( + descriptor_pb2.FeatureSet.MessageEncoding.DELIMITED + ) + + if options.HasField('packed'): + features.repeated_field_encoding = ( + descriptor_pb2.FeatureSet.RepeatedFieldEncoding.PACKED + if options.packed + else descriptor_pb2.FeatureSet.RepeatedFieldEncoding.EXPANDED + ) + + @property + def type(self): + if ( + self._GetFeatures().message_encoding + == _FEATURESET_MESSAGE_ENCODING_DELIMITED + and self.message_type + and not self.message_type.GetOptions().map_entry + and not self.containing_type.GetOptions().map_entry + ): + return FieldDescriptor.TYPE_GROUP + return self._type + + @type.setter + def type(self, val): + self._type = val + + @property + def is_required(self): + """Returns if the field is required.""" + return ( + self._GetFeatures().field_presence + == _FEATURESET_FIELD_PRESENCE_LEGACY_REQUIRED + ) + + @property + def is_repeated(self): + """Returns if the field is repeated.""" + return self._label == FieldDescriptor.LABEL_REPEATED + + @property + def camelcase_name(self): + """Camelcase name of this field. + + Returns: + str: the name in CamelCase. + """ + if self._camelcase_name is None: + self._camelcase_name = _ToCamelCase(self.name) + return self._camelcase_name + + @property + def has_presence(self): + """Whether the field distinguishes between unpopulated and default values. + + Raises: + RuntimeError: singular field that is not linked with message nor file. + """ + if self.is_repeated: + return False + if ( + self.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE + or self.is_extension + or self.containing_oneof + ): + return True + + return ( + self._GetFeatures().field_presence + != _FEATURESET_FIELD_PRESENCE_IMPLICIT + ) + + @property + def is_packed(self): + """Returns if the field is packed.""" + if not self.is_repeated: + return False + field_type = self.type + if ( + field_type == FieldDescriptor.TYPE_STRING + or field_type == FieldDescriptor.TYPE_GROUP + or field_type == FieldDescriptor.TYPE_MESSAGE + or field_type == FieldDescriptor.TYPE_BYTES + ): + return False + + return ( + self._GetFeatures().repeated_field_encoding + == _FEATURESET_REPEATED_FIELD_ENCODING_PACKED + ) + + @staticmethod + def ProtoTypeToCppProtoType(proto_type): + """Converts from a Python proto type to a C++ Proto Type. + + The Python ProtocolBuffer classes specify both the 'Python' datatype and the + 'C++' datatype - and they're not the same. This helper method should + translate from one to another. + + Args: + proto_type: the Python proto type (descriptor.FieldDescriptor.TYPE_*) + + Returns: + int: descriptor.FieldDescriptor.CPPTYPE_*, the C++ type. + Raises: + TypeTransformationError: when the Python proto type isn't known. + """ + try: + return FieldDescriptor._PYTHON_TO_CPP_PROTO_TYPE_MAP[proto_type] + except KeyError: + raise TypeTransformationError('Unknown proto_type: %s' % proto_type) + + +class EnumDescriptor(_NestedDescriptorBase): + """Descriptor for an enum defined in a .proto file. + + Attributes: + name (str): Name of the enum type. + full_name (str): Full name of the type, including package name and any + enclosing type(s). + values (list[EnumValueDescriptor]): List of the values in this enum. + values_by_name (dict(str, EnumValueDescriptor)): Same as :attr:`values`, but + indexed by the "name" field of each EnumValueDescriptor. + values_by_number (dict(int, EnumValueDescriptor)): Same as :attr:`values`, + but indexed by the "number" field of each EnumValueDescriptor. + containing_type (Descriptor): Descriptor of the immediate containing type of + this enum, or None if this is an enum defined at the top level in a .proto + file. Set by Descriptor's constructor if we're passed into one. + file (FileDescriptor): Reference to file descriptor. + options (descriptor_pb2.EnumOptions): Enum options message or None to use + default enum options. + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.EnumDescriptor + + def __new__( + cls, + name, + full_name, + filename, + values, + containing_type=None, + options=None, + serialized_options=None, + file=None, # pylint: disable=redefined-builtin + serialized_start=None, + serialized_end=None, + create_key=None, + ): + _message.Message._CheckCalledFromGeneratedFile() + return _message.default_pool.FindEnumTypeByName(full_name) + + def __init__( + self, + name, + full_name, + filename, + values, + containing_type=None, + options=None, + serialized_options=None, + file=None, # pylint: disable=redefined-builtin + serialized_start=None, + serialized_end=None, + create_key=None, + ): + """Arguments are as described in the attribute description above. + + Note that filename is an obsolete argument, that is not used anymore. + Please use file.name to access this as an attribute. + """ + if create_key is not _internal_create_key: + _Deprecated('create function EnumDescriptor()') + + super(EnumDescriptor, self).__init__( + options, + 'EnumOptions', + name, + full_name, + file, + containing_type, + serialized_start=serialized_start, + serialized_end=serialized_end, + serialized_options=serialized_options, + ) + + self.values = values + for value in self.values: + value.file = file + value.type = self + self.values_by_name = dict((v.name, v) for v in values) + # Values are reversed to ensure that the first alias is retained. + self.values_by_number = dict((v.number, v) for v in reversed(values)) + + @property + def _parent(self): + return self.containing_type or self.file + + @property + def is_closed(self): + """Returns true whether this is a "closed" enum. + + This means that it: + - Has a fixed set of values, rather than being equivalent to an int32. + - Encountering values not in this set causes them to be treated as unknown + fields. + - The first value (i.e., the default) may be nonzero. + + WARNING: Some runtimes currently have a quirk where non-closed enums are + treated as closed when used as the type of fields defined in a + `syntax = proto2;` file. This quirk is not present in all runtimes; as of + writing, we know that: + + - C++, Java, and C++-based Python share this quirk. + - UPB and UPB-based Python do not. + - PHP and Ruby treat all enums as open regardless of declaration. + + Care should be taken when using this function to respect the target + runtime's enum handling quirks. + """ + return self._GetFeatures().enum_type == _FEATURESET_ENUM_TYPE_CLOSED + + def CopyToProto(self, proto): + """Copies this to a descriptor_pb2.EnumDescriptorProto. + + Args: + proto (descriptor_pb2.EnumDescriptorProto): An empty descriptor proto. + """ + # This function is overridden to give a better doc comment. + super(EnumDescriptor, self).CopyToProto(proto) + + +class EnumValueDescriptor(DescriptorBase): + """Descriptor for a single value within an enum. + + Attributes: + name (str): Name of this value. + index (int): Dense, 0-indexed index giving the order that this value appears + textually within its enum in the .proto file. + number (int): Actual number assigned to this enum value. + type (EnumDescriptor): :class:`EnumDescriptor` to which this value belongs. + Set by :class:`EnumDescriptor`'s constructor if we're passed into one. + options (descriptor_pb2.EnumValueOptions): Enum value options message or + None to use default enum value options options. + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.EnumValueDescriptor + + def __new__( + cls, + name, + index, + number, + type=None, # pylint: disable=redefined-builtin + options=None, + serialized_options=None, + create_key=None, + ): + _message.Message._CheckCalledFromGeneratedFile() + # There is no way we can build a complete EnumValueDescriptor with the + # given parameters (the name of the Enum is not known, for example). + # Fortunately generated files just pass it to the EnumDescriptor() + # constructor, which will ignore it, so returning None is good enough. + return None + + def __init__( + self, + name, + index, + number, + type=None, # pylint: disable=redefined-builtin + options=None, + serialized_options=None, + create_key=None, + ): + """Arguments are as described in the attribute description above.""" + if create_key is not _internal_create_key: + _Deprecated('create function EnumValueDescriptor()') + + super(EnumValueDescriptor, self).__init__( + type.file if type else None, + options, + serialized_options, + 'EnumValueOptions', + ) + self.name = name + self.index = index + self.number = number + self.type = type + + @property + def _parent(self): + return self.type + + +class OneofDescriptor(DescriptorBase): + """Descriptor for a oneof field. + + Attributes: + name (str): Name of the oneof field. + full_name (str): Full name of the oneof field, including package name. + index (int): 0-based index giving the order of the oneof field inside its + containing type. + containing_type (Descriptor): :class:`Descriptor` of the protocol message + type that contains this field. Set by the :class:`Descriptor` constructor + if we're passed into one. + fields (list[FieldDescriptor]): The list of field descriptors this oneof can + contain. + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.OneofDescriptor + + def __new__( + cls, + name, + full_name, + index, + containing_type, + fields, + options=None, + serialized_options=None, + create_key=None, + ): + _message.Message._CheckCalledFromGeneratedFile() + return _message.default_pool.FindOneofByName(full_name) + + def __init__( + self, + name, + full_name, + index, + containing_type, + fields, + options=None, + serialized_options=None, + create_key=None, + ): + """Arguments are as described in the attribute description above.""" + if create_key is not _internal_create_key: + _Deprecated('create function OneofDescriptor()') + + super(OneofDescriptor, self).__init__( + containing_type.file if containing_type else None, + options, + serialized_options, + 'OneofOptions', + ) + self.name = name + self.full_name = full_name + self.index = index + self.containing_type = containing_type + self.fields = fields + + @property + def _parent(self): + return self.containing_type + + +class ServiceDescriptor(_NestedDescriptorBase): + """Descriptor for a service. + + Attributes: + name (str): Name of the service. + full_name (str): Full name of the service, including package name. + index (int): 0-indexed index giving the order that this services definition + appears within the .proto file. + methods (list[MethodDescriptor]): List of methods provided by this service. + methods_by_name (dict(str, MethodDescriptor)): Same + :class:`MethodDescriptor` objects as in :attr:`methods_by_name`, but + indexed by "name" attribute in each :class:`MethodDescriptor`. + options (descriptor_pb2.ServiceOptions): Service options message or None to + use default service options. + file (FileDescriptor): Reference to file info. + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.ServiceDescriptor + + def __new__( + cls, + name=None, + full_name=None, + index=None, + methods=None, + options=None, + serialized_options=None, + file=None, # pylint: disable=redefined-builtin + serialized_start=None, + serialized_end=None, + create_key=None, + ): + _message.Message._CheckCalledFromGeneratedFile() # pylint: disable=protected-access + return _message.default_pool.FindServiceByName(full_name) + + def __init__( + self, + name, + full_name, + index, + methods, + options=None, + serialized_options=None, + file=None, # pylint: disable=redefined-builtin + serialized_start=None, + serialized_end=None, + create_key=None, + ): + if create_key is not _internal_create_key: + _Deprecated('create function ServiceDescriptor()') + + super(ServiceDescriptor, self).__init__( + options, + 'ServiceOptions', + name, + full_name, + file, + None, + serialized_start=serialized_start, + serialized_end=serialized_end, + serialized_options=serialized_options, + ) + self.index = index + self.methods = methods + self.methods_by_name = dict((m.name, m) for m in methods) + # Set the containing service for each method in this service. + for method in self.methods: + method.file = self.file + method.containing_service = self + + @property + def _parent(self): + return self.file + + def FindMethodByName(self, name): + """Searches for the specified method, and returns its descriptor. + + Args: + name (str): Name of the method. + + Returns: + MethodDescriptor: The descriptor for the requested method. + + Raises: + KeyError: if the method cannot be found in the service. + """ + return self.methods_by_name[name] + + def CopyToProto(self, proto): + """Copies this to a descriptor_pb2.ServiceDescriptorProto. + + Args: + proto (descriptor_pb2.ServiceDescriptorProto): An empty descriptor proto. + """ + # This function is overridden to give a better doc comment. + super(ServiceDescriptor, self).CopyToProto(proto) + + +class MethodDescriptor(DescriptorBase): + """Descriptor for a method in a service. + + Attributes: + name (str): Name of the method within the service. + full_name (str): Full name of method. + index (int): 0-indexed index of the method inside the service. + containing_service (ServiceDescriptor): The service that contains this + method. + input_type (Descriptor): The descriptor of the message that this method + accepts. + output_type (Descriptor): The descriptor of the message that this method + returns. + client_streaming (bool): Whether this method uses client streaming. + server_streaming (bool): Whether this method uses server streaming. + options (descriptor_pb2.MethodOptions or None): Method options message, or + None to use default method options. + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.MethodDescriptor + + def __new__( + cls, + name, + full_name, + index, + containing_service, + input_type, + output_type, + client_streaming=False, + server_streaming=False, + options=None, + serialized_options=None, + create_key=None, + ): + _message.Message._CheckCalledFromGeneratedFile() # pylint: disable=protected-access + return _message.default_pool.FindMethodByName(full_name) + + def __init__( + self, + name, + full_name, + index, + containing_service, + input_type, + output_type, + client_streaming=False, + server_streaming=False, + options=None, + serialized_options=None, + create_key=None, + ): + """The arguments are as described in the description of MethodDescriptor + + attributes above. + + Note that containing_service may be None, and may be set later if necessary. + """ + if create_key is not _internal_create_key: + _Deprecated('create function MethodDescriptor()') + + super(MethodDescriptor, self).__init__( + containing_service.file if containing_service else None, + options, + serialized_options, + 'MethodOptions', + ) + self.name = name + self.full_name = full_name + self.index = index + self.containing_service = containing_service + self.input_type = input_type + self.output_type = output_type + self.client_streaming = client_streaming + self.server_streaming = server_streaming + + @property + def _parent(self): + return self.containing_service + + def CopyToProto(self, proto): + """Copies this to a descriptor_pb2.MethodDescriptorProto. + + Args: + proto (descriptor_pb2.MethodDescriptorProto): An empty descriptor proto. + + Raises: + Error: If self couldn't be serialized, due to too few constructor + arguments. + """ + if self.containing_service is not None: + from google.protobuf import descriptor_pb2 + + service_proto = descriptor_pb2.ServiceDescriptorProto() + self.containing_service.CopyToProto(service_proto) + proto.CopyFrom(service_proto.method[self.index]) + else: + raise Error('Descriptor does not contain a service.') + + +class FileDescriptor(DescriptorBase): + """Descriptor for a file. Mimics the descriptor_pb2.FileDescriptorProto. + + Note that :attr:`enum_types_by_name`, :attr:`extensions_by_name`, and + :attr:`dependencies` fields are only set by the + :py:mod:`google.protobuf.message_factory` module, and not by the generated + proto code. + + Attributes: + name (str): Name of file, relative to root of source tree. + package (str): Name of the package + edition (Edition): Enum value indicating edition of the file + serialized_pb (bytes): Byte string of serialized + :class:`descriptor_pb2.FileDescriptorProto`. + dependencies (list[FileDescriptor]): List of other :class:`FileDescriptor` + objects this :class:`FileDescriptor` depends on. + public_dependencies (list[FileDescriptor]): A subset of + :attr:`dependencies`, which were declared as "public". + message_types_by_name (dict(str, Descriptor)): Mapping from message names to + their :class:`Descriptor`. + enum_types_by_name (dict(str, EnumDescriptor)): Mapping from enum names to + their :class:`EnumDescriptor`. + extensions_by_name (dict(str, FieldDescriptor)): Mapping from extension + names declared at file scope to their :class:`FieldDescriptor`. + services_by_name (dict(str, ServiceDescriptor)): Mapping from services' + names to their :class:`ServiceDescriptor`. + pool (DescriptorPool): The pool this descriptor belongs to. When not passed + to the constructor, the global default pool is used. + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.FileDescriptor + + def __new__( + cls, + name, + package, + options=None, + serialized_options=None, + serialized_pb=None, + dependencies=None, + public_dependencies=None, + syntax=None, + edition=None, + pool=None, + create_key=None, + ): + # FileDescriptor() is called from various places, not only from generated + # files, to register dynamic proto files and messages. + # pylint: disable=g-explicit-bool-comparison + if serialized_pb: + return _message.default_pool.AddSerializedFile(serialized_pb) + else: + return super(FileDescriptor, cls).__new__(cls) + + def __init__( + self, + name, + package, + options=None, + serialized_options=None, + serialized_pb=None, + dependencies=None, + public_dependencies=None, + syntax=None, + edition=None, + pool=None, + create_key=None, + ): + """Constructor.""" + if create_key is not _internal_create_key: + _Deprecated('create function FileDescriptor()') + + super(FileDescriptor, self).__init__( + self, options, serialized_options, 'FileOptions' + ) + + if edition and edition != 'EDITION_UNKNOWN': + self._edition = edition + elif syntax == 'proto3': + self._edition = 'EDITION_PROTO3' + else: + self._edition = 'EDITION_PROTO2' + + if pool is None: + from google.protobuf import descriptor_pool + + pool = descriptor_pool.Default() + self.pool = pool + self.message_types_by_name = {} + self.name = name + self.package = package + self.serialized_pb = serialized_pb + + self.enum_types_by_name = {} + self.extensions_by_name = {} + self.services_by_name = {} + self.dependencies = dependencies or [] + self.public_dependencies = public_dependencies or [] + + def CopyToProto(self, proto): + """Copies this to a descriptor_pb2.FileDescriptorProto. + + Args: + proto: An empty descriptor_pb2.FileDescriptorProto. + """ + proto.ParseFromString(self.serialized_pb) + + @property + def _parent(self): + return None + + +def _ParseOptions(message, string): + """Parses serialized options. + + This helper function is used to parse serialized options in generated + proto2 files. It must not be used outside proto2. + """ + message.ParseFromString(string) + return message + + +def _ToCamelCase(name): + """Converts name to camel-case and returns it.""" + capitalize_next = False + result = [] + + for c in name: + if c == '_': + if result: + capitalize_next = True + elif capitalize_next: + result.append(c.upper()) + capitalize_next = False + else: + result += c + + # Lower-case the first letter. + if result and result[0].isupper(): + result[0] = result[0].lower() + return ''.join(result) + + +def _OptionsOrNone(descriptor_proto): + """Returns the value of the field `options`, or None if it is not set.""" + if descriptor_proto.HasField('options'): + return descriptor_proto.options + else: + return None + + +def _ToJsonName(name): + """Converts name to Json name and returns it.""" + capitalize_next = False + result = [] + + for c in name: + if c == '_': + capitalize_next = True + elif capitalize_next: + result.append(c.upper()) + capitalize_next = False + else: + result += c + + return ''.join(result) + + +def MakeDescriptor( + desc_proto, + package='', + build_file_if_cpp=True, + syntax=None, + edition=None, + file_desc=None, +): + """Make a protobuf Descriptor given a DescriptorProto protobuf. + + Handles nested descriptors. Note that this is limited to the scope of defining + a message inside of another message. Composite fields can currently only be + resolved if the message is defined in the same scope as the field. + + Args: + desc_proto: The descriptor_pb2.DescriptorProto protobuf message. + package: Optional package name for the new message Descriptor (string). + build_file_if_cpp: Update the C++ descriptor pool if api matches. Set to + False on recursion, so no duplicates are created. + syntax: The syntax/semantics that should be used. Set to "proto3" to get + proto3 field presence semantics. + edition: The edition that should be used if syntax is "edition". + file_desc: A FileDescriptor to place this descriptor into. + + Returns: + A Descriptor for protobuf messages. + """ + # pylint: disable=g-import-not-at-top + from google.protobuf import descriptor_pb2 + + # Generate a random name for this proto file to prevent conflicts with any + # imported ones. We need to specify a file name so the descriptor pool + # accepts our FileDescriptorProto, but it is not important what that file + # name is actually set to. + proto_name = binascii.hexlify(os.urandom(16)).decode('ascii') + + if package: + file_name = os.path.join(package.replace('.', '/'), proto_name + '.proto') + else: + file_name = proto_name + '.proto' + + if api_implementation.Type() != 'python' and build_file_if_cpp: + # The C++ implementation requires all descriptors to be backed by the same + # definition in the C++ descriptor pool. To do this, we build a + # FileDescriptorProto with the same definition as this descriptor and build + # it into the pool. + file_descriptor_proto = descriptor_pb2.FileDescriptorProto() + file_descriptor_proto.message_type.add().MergeFrom(desc_proto) + + if package: + file_descriptor_proto.package = package + file_descriptor_proto.name = file_name + + _message.default_pool.Add(file_descriptor_proto) + result = _message.default_pool.FindFileByName(file_descriptor_proto.name) + + if _USE_C_DESCRIPTORS: + return result.message_types_by_name[desc_proto.name] + + if file_desc is None: + file_desc = FileDescriptor( + pool=None, + name=file_name, + package=package, + syntax=syntax, + edition=edition, + options=None, + serialized_pb='', + dependencies=[], + public_dependencies=[], + create_key=_internal_create_key, + ) + full_message_name = [desc_proto.name] + if package: + full_message_name.insert(0, package) + + # Create Descriptors for enum types + enum_types = {} + for enum_proto in desc_proto.enum_type: + full_name = '.'.join(full_message_name + [enum_proto.name]) + enum_desc = EnumDescriptor( + enum_proto.name, + full_name, + None, + [ + EnumValueDescriptor( + enum_val.name, + ii, + enum_val.number, + create_key=_internal_create_key, + ) + for ii, enum_val in enumerate(enum_proto.value) + ], + file=file_desc, + create_key=_internal_create_key, + ) + enum_types[full_name] = enum_desc + + # Create Descriptors for nested types + nested_types = {} + for nested_proto in desc_proto.nested_type: + full_name = '.'.join(full_message_name + [nested_proto.name]) + # Nested types are just those defined inside of the message, not all types + # used by fields in the message, so no loops are possible here. + nested_desc = MakeDescriptor( + nested_proto, + package='.'.join(full_message_name), + build_file_if_cpp=False, + syntax=syntax, + edition=edition, + file_desc=file_desc, + ) + nested_types[full_name] = nested_desc + + fields = [] + for field_proto in desc_proto.field: + full_name = '.'.join(full_message_name + [field_proto.name]) + enum_desc = None + nested_desc = None + if field_proto.json_name: + json_name = field_proto.json_name + else: + json_name = None + if field_proto.HasField('type_name'): + type_name = field_proto.type_name + full_type_name = '.'.join( + full_message_name + [type_name[type_name.rfind('.') + 1 :]] + ) + if full_type_name in nested_types: + nested_desc = nested_types[full_type_name] + elif full_type_name in enum_types: + enum_desc = enum_types[full_type_name] + # Else type_name references a non-local type, which isn't implemented + field = FieldDescriptor( + field_proto.name, + full_name, + field_proto.number - 1, + field_proto.number, + field_proto.type, + FieldDescriptor.ProtoTypeToCppProtoType(field_proto.type), + field_proto.label, + None, + nested_desc, + enum_desc, + None, + False, + None, + options=_OptionsOrNone(field_proto), + has_default_value=False, + json_name=json_name, + file=file_desc, + create_key=_internal_create_key, + ) + fields.append(field) + + desc_name = '.'.join(full_message_name) + return Descriptor( + desc_proto.name, + desc_name, + None, + None, + fields, + list(nested_types.values()), + list(enum_types.values()), + [], + options=_OptionsOrNone(desc_proto), + file=file_desc, + create_key=_internal_create_key, + ) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/descriptor_database.py b/venv/lib/python3.12/site-packages/google/protobuf/descriptor_database.py new file mode 100755 index 0000000..ce0033e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/descriptor_database.py @@ -0,0 +1,186 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Provides a container for DescriptorProtos.""" + +__author__ = 'matthewtoia@google.com (Matt Toia)' + +from typing import Dict, Iterator, Optional +import warnings + + +class Error(Exception): + pass + + +class DescriptorDatabaseConflictingDefinitionError(Error): + """Raised when a proto is added with the same name & different descriptor.""" + + +class DescriptorDatabase(object): + """A container accepting FileDescriptorProtos and maps DescriptorProtos.""" + + def __init__(self) -> None: + self._file_desc_protos_by_file: Dict[ + str, 'descriptor_pb2.FileDescriptorProto' + ] = {} + self._file_desc_protos_by_symbol: Dict[ + str, 'descriptor_pb2.FileDescriptorProto' + ] = {} + + def Add(self, file_desc_proto: 'descriptor_pb2.FileDescriptorProto') -> None: + """Adds the FileDescriptorProto and its types to this database. + + Args: + file_desc_proto: The FileDescriptorProto to add. + + Raises: + DescriptorDatabaseConflictingDefinitionError: if an attempt is made to + add a proto with the same name but different definition than an + existing proto in the database. + """ + proto_name = file_desc_proto.name + if proto_name not in self._file_desc_protos_by_file: + self._file_desc_protos_by_file[proto_name] = file_desc_proto + elif self._file_desc_protos_by_file[proto_name] != file_desc_proto: + raise DescriptorDatabaseConflictingDefinitionError( + '%s already added, but with different descriptor.' % proto_name + ) + else: + return + + # Add all the top-level descriptors to the index. + package = file_desc_proto.package + for message in file_desc_proto.message_type: + for name in _ExtractSymbols(message, package): + self._AddSymbol(name, file_desc_proto) + for enum in file_desc_proto.enum_type: + self._AddSymbol( + ('.'.join((package, enum.name)) if package else enum.name), + file_desc_proto, + ) + for enum_value in enum.value: + self._file_desc_protos_by_symbol[ + '.'.join((package, enum_value.name)) if package else enum_value.name + ] = file_desc_proto + for extension in file_desc_proto.extension: + self._AddSymbol( + ('.'.join((package, extension.name)) if package else extension.name), + file_desc_proto, + ) + for service in file_desc_proto.service: + self._AddSymbol( + ('.'.join((package, service.name)) if package else service.name), + file_desc_proto, + ) + + def FindFileByName(self, name: str) -> 'descriptor_pb2.FileDescriptorProto': + """Finds the file descriptor proto by file name. + + Typically the file name is a relative path ending to a .proto file. The + proto with the given name will have to have been added to this database + using the Add method or else an error will be raised. + + Args: + name: The file name to find. + + Returns: + The file descriptor proto matching the name. + + Raises: + KeyError if no file by the given name was added. + """ + + return self._file_desc_protos_by_file[name] + + def FindFileContainingSymbol( + self, symbol: str + ) -> 'descriptor_pb2.FileDescriptorProto': + """Finds the file descriptor proto containing the specified symbol. + + The symbol should be a fully qualified name including the file descriptor's + package and any containing messages. Some examples: + + 'some.package.name.Message' + 'some.package.name.Message.NestedEnum' + 'some.package.name.Message.some_field' + + The file descriptor proto containing the specified symbol must be added to + this database using the Add method or else an error will be raised. + + Args: + symbol: The fully qualified symbol name. + + Returns: + The file descriptor proto containing the symbol. + + Raises: + KeyError if no file contains the specified symbol. + """ + symbol = symbol.lstrip('.') + try: + return self._file_desc_protos_by_symbol[symbol] + except KeyError: + # Fields, enum values, and nested extensions are not in + # _file_desc_protos_by_symbol. Try to find the top level + # descriptor. Non-existent nested symbol under a valid top level + # descriptor can also be found. The behavior is the same with + # protobuf C++. + top_level, _, _ = symbol.rpartition('.') + try: + return self._file_desc_protos_by_symbol[top_level] + except KeyError: + # Raise the original symbol as a KeyError for better diagnostics. + raise KeyError(symbol) + + def FindFileContainingExtension( + self, + extendee_name: str, + extension_number: int, # pylint: disable=unused-argument + ) -> Optional['descriptor_pb2.FileDescriptorProto']: + # TODO: implement this API. + return None + + def FindAllExtensionNumbers(self, extendee_name: str) -> list[int]: # pylint: disable=unused-argument + # TODO: implement this API. + return [] + + def _AddSymbol( + self, name: str, file_desc_proto: 'descriptor_pb2.FileDescriptorProto' + ) -> None: + if name in self._file_desc_protos_by_symbol: + warn_msg = ( + 'Conflict register for file "' + + file_desc_proto.name + + '": ' + + name + + ' is already defined in file "' + + self._file_desc_protos_by_symbol[name].name + + '"' + ) + warnings.warn(warn_msg, RuntimeWarning) + self._file_desc_protos_by_symbol[name] = file_desc_proto + + +def _ExtractSymbols( + desc_proto: 'descriptor_pb2.DescriptorProto', package: str +) -> Iterator[str]: + """Pulls out all the symbols from a descriptor proto. + + Args: + desc_proto: The proto to extract symbols from. + package: The package containing the descriptor type. + + Yields: + The fully qualified name found in the descriptor. + """ + message_name = package + '.' + desc_proto.name if package else desc_proto.name + yield message_name + for nested_type in desc_proto.nested_type: + for symbol in _ExtractSymbols(nested_type, message_name): + yield symbol + for enum_type in desc_proto.enum_type: + yield '.'.join((message_name, enum_type.name)) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/descriptor_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/descriptor_pb2.py new file mode 100755 index 0000000..53ae100 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/descriptor_pb2.py @@ -0,0 +1,3465 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/descriptor.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/descriptor.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR = _descriptor.FileDescriptor( + name='google/protobuf/descriptor.proto', + package='google.protobuf', + syntax='proto2', + edition='EDITION_PROTO2', + serialized_options=b'\n\023com.google.protobufB\020DescriptorProtosH\001Z-google.golang.org/protobuf/types/descriptorpb\370\001\001\242\002\003GPB\252\002\032Google.Protobuf.Reflection', + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n google/protobuf/descriptor.proto\x12\x0fgoogle.protobuf\"[\n\x11\x46ileDescriptorSet\x12\x38\n\x04\x66ile\x18\x01 \x03(\x0b\x32$.google.protobuf.FileDescriptorProtoR\x04\x66ile*\x0c\x08\x80\xec\xca\xff\x01\x10\x81\xec\xca\xff\x01\"\xc5\x05\n\x13\x46ileDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07package\x18\x02 \x01(\tR\x07package\x12\x1e\n\ndependency\x18\x03 \x03(\tR\ndependency\x12+\n\x11public_dependency\x18\n \x03(\x05R\x10publicDependency\x12\'\n\x0fweak_dependency\x18\x0b \x03(\x05R\x0eweakDependency\x12+\n\x11option_dependency\x18\x0f \x03(\tR\x10optionDependency\x12\x43\n\x0cmessage_type\x18\x04 \x03(\x0b\x32 .google.protobuf.DescriptorProtoR\x0bmessageType\x12\x41\n\tenum_type\x18\x05 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProtoR\x08\x65numType\x12\x41\n\x07service\x18\x06 \x03(\x0b\x32\'.google.protobuf.ServiceDescriptorProtoR\x07service\x12\x43\n\textension\x18\x07 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProtoR\textension\x12\x36\n\x07options\x18\x08 \x01(\x0b\x32\x1c.google.protobuf.FileOptionsR\x07options\x12I\n\x10source_code_info\x18\t \x01(\x0b\x32\x1f.google.protobuf.SourceCodeInfoR\x0esourceCodeInfo\x12\x16\n\x06syntax\x18\x0c \x01(\tR\x06syntax\x12\x32\n\x07\x65\x64ition\x18\x0e \x01(\x0e\x32\x18.google.protobuf.EditionR\x07\x65\x64ition\"\xfc\x06\n\x0f\x44\x65scriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12;\n\x05\x66ield\x18\x02 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProtoR\x05\x66ield\x12\x43\n\textension\x18\x06 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProtoR\textension\x12\x41\n\x0bnested_type\x18\x03 \x03(\x0b\x32 .google.protobuf.DescriptorProtoR\nnestedType\x12\x41\n\tenum_type\x18\x04 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProtoR\x08\x65numType\x12X\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32/.google.protobuf.DescriptorProto.ExtensionRangeR\x0e\x65xtensionRange\x12\x44\n\noneof_decl\x18\x08 \x03(\x0b\x32%.google.protobuf.OneofDescriptorProtoR\toneofDecl\x12\x39\n\x07options\x18\x07 \x01(\x0b\x32\x1f.google.protobuf.MessageOptionsR\x07options\x12U\n\x0ereserved_range\x18\t \x03(\x0b\x32..google.protobuf.DescriptorProto.ReservedRangeR\rreservedRange\x12#\n\rreserved_name\x18\n \x03(\tR\x0creservedName\x12\x41\n\nvisibility\x18\x0b \x01(\x0e\x32!.google.protobuf.SymbolVisibilityR\nvisibility\x1az\n\x0e\x45xtensionRange\x12\x14\n\x05start\x18\x01 \x01(\x05R\x05start\x12\x10\n\x03\x65nd\x18\x02 \x01(\x05R\x03\x65nd\x12@\n\x07options\x18\x03 \x01(\x0b\x32&.google.protobuf.ExtensionRangeOptionsR\x07options\x1a\x37\n\rReservedRange\x12\x14\n\x05start\x18\x01 \x01(\x05R\x05start\x12\x10\n\x03\x65nd\x18\x02 \x01(\x05R\x03\x65nd\"\xd4\x04\n\x15\x45xtensionRangeOptions\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\x12Y\n\x0b\x64\x65\x63laration\x18\x02 \x03(\x0b\x32\x32.google.protobuf.ExtensionRangeOptions.DeclarationB\x03\x88\x01\x02R\x0b\x64\x65\x63laration\x12\x37\n\x08\x66\x65\x61tures\x18\x32 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12m\n\x0cverification\x18\x03 \x01(\x0e\x32\x38.google.protobuf.ExtensionRangeOptions.VerificationState:\nUNVERIFIEDB\x03\x88\x01\x02R\x0cverification\x1a\x94\x01\n\x0b\x44\x65\x63laration\x12\x16\n\x06number\x18\x01 \x01(\x05R\x06number\x12\x1b\n\tfull_name\x18\x02 \x01(\tR\x08\x66ullName\x12\x12\n\x04type\x18\x03 \x01(\tR\x04type\x12\x1a\n\x08reserved\x18\x05 \x01(\x08R\x08reserved\x12\x1a\n\x08repeated\x18\x06 \x01(\x08R\x08repeatedJ\x04\x08\x04\x10\x05\"4\n\x11VerificationState\x12\x0f\n\x0b\x44\x45\x43LARATION\x10\x00\x12\x0e\n\nUNVERIFIED\x10\x01*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xc1\x06\n\x14\x46ieldDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n\x06number\x18\x03 \x01(\x05R\x06number\x12\x41\n\x05label\x18\x04 \x01(\x0e\x32+.google.protobuf.FieldDescriptorProto.LabelR\x05label\x12>\n\x04type\x18\x05 \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.TypeR\x04type\x12\x1b\n\ttype_name\x18\x06 \x01(\tR\x08typeName\x12\x1a\n\x08\x65xtendee\x18\x02 \x01(\tR\x08\x65xtendee\x12#\n\rdefault_value\x18\x07 \x01(\tR\x0c\x64\x65\x66\x61ultValue\x12\x1f\n\x0boneof_index\x18\t \x01(\x05R\noneofIndex\x12\x1b\n\tjson_name\x18\n \x01(\tR\x08jsonName\x12\x37\n\x07options\x18\x08 \x01(\x0b\x32\x1d.google.protobuf.FieldOptionsR\x07options\x12\'\n\x0fproto3_optional\x18\x11 \x01(\x08R\x0eproto3Optional\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REPEATED\x10\x03\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\"c\n\x14OneofDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x37\n\x07options\x18\x02 \x01(\x0b\x32\x1d.google.protobuf.OneofOptionsR\x07options\"\xa6\x03\n\x13\x45numDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12?\n\x05value\x18\x02 \x03(\x0b\x32).google.protobuf.EnumValueDescriptorProtoR\x05value\x12\x36\n\x07options\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.EnumOptionsR\x07options\x12]\n\x0ereserved_range\x18\x04 \x03(\x0b\x32\x36.google.protobuf.EnumDescriptorProto.EnumReservedRangeR\rreservedRange\x12#\n\rreserved_name\x18\x05 \x03(\tR\x0creservedName\x12\x41\n\nvisibility\x18\x06 \x01(\x0e\x32!.google.protobuf.SymbolVisibilityR\nvisibility\x1a;\n\x11\x45numReservedRange\x12\x14\n\x05start\x18\x01 \x01(\x05R\x05start\x12\x10\n\x03\x65nd\x18\x02 \x01(\x05R\x03\x65nd\"\x83\x01\n\x18\x45numValueDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n\x06number\x18\x02 \x01(\x05R\x06number\x12;\n\x07options\x18\x03 \x01(\x0b\x32!.google.protobuf.EnumValueOptionsR\x07options\"\xb5\x01\n\x16ServiceDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12>\n\x06method\x18\x02 \x03(\x0b\x32&.google.protobuf.MethodDescriptorProtoR\x06method\x12\x39\n\x07options\x18\x03 \x01(\x0b\x32\x1f.google.protobuf.ServiceOptionsR\x07optionsJ\x04\x08\x04\x10\x05R\x06stream\"\x89\x02\n\x15MethodDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n\ninput_type\x18\x02 \x01(\tR\tinputType\x12\x1f\n\x0boutput_type\x18\x03 \x01(\tR\noutputType\x12\x38\n\x07options\x18\x04 \x01(\x0b\x32\x1e.google.protobuf.MethodOptionsR\x07options\x12\x30\n\x10\x63lient_streaming\x18\x05 \x01(\x08:\x05\x66\x61lseR\x0f\x63lientStreaming\x12\x30\n\x10server_streaming\x18\x06 \x01(\x08:\x05\x66\x61lseR\x0fserverStreaming\"\x8c\x0c\n\x0b\x46ileOptions\x12!\n\x0cjava_package\x18\x01 \x01(\tR\x0bjavaPackage\x12\x30\n\x14java_outer_classname\x18\x08 \x01(\tR\x12javaOuterClassname\x12\xf9\x01\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lseB\xc1\x01\xb2\x01\xbd\x01\x08\xe6\x07 \xe9\x07*\xb4\x01This behavior is enabled by default in editions 2024 and above. To disable it, you can set `features.(pb.java).nest_in_file_class = YES` on individual messages, enums, or services.R\x11javaMultipleFiles\x12\x44\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08\x42\x02\x18\x01R\x19javaGenerateEqualsAndHash\x12:\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lseR\x13javaStringCheckUtf8\x12S\n\x0coptimize_for\x18\t \x01(\x0e\x32).google.protobuf.FileOptions.OptimizeMode:\x05SPEEDR\x0boptimizeFor\x12\x1d\n\ngo_package\x18\x0b \x01(\tR\tgoPackage\x12\x35\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lseR\x11\x63\x63GenericServices\x12\x39\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lseR\x13javaGenericServices\x12\x35\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lseR\x11pyGenericServices\x12%\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12\xbf\x01\n\x10\x63\x63_enable_arenas\x18\x1f \x01(\x08:\x04trueB\x8e\x01\xb2\x01\x8a\x01 \xea\x07*\x84\x01\x63\x63_enable_arenas is enabled by default in every edition and overrides are ignored.This option is removed in editions 2026 and above.R\x0e\x63\x63\x45nableArenas\x12*\n\x11objc_class_prefix\x18$ \x01(\tR\x0fobjcClassPrefix\x12)\n\x10\x63sharp_namespace\x18% \x01(\tR\x0f\x63sharpNamespace\x12!\n\x0cswift_prefix\x18\' \x01(\tR\x0bswiftPrefix\x12(\n\x10php_class_prefix\x18( \x01(\tR\x0ephpClassPrefix\x12#\n\rphp_namespace\x18) \x01(\tR\x0cphpNamespace\x12\x34\n\x16php_metadata_namespace\x18, \x01(\tR\x14phpMetadataNamespace\x12!\n\x0cruby_package\x18- \x01(\tR\x0brubyPackage\x12\x37\n\x08\x66\x65\x61tures\x18\x32 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08*\x10+J\x04\x08&\x10\'R\x14php_generic_services\"\xfc\x03\n\x0eMessageOptions\x12<\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lseR\x14messageSetWireFormat\x12L\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lseR\x1cnoStandardDescriptorAccessor\x12%\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12\x1b\n\tmap_entry\x18\x07 \x01(\x08R\x08mapEntry\x12V\n&deprecated_legacy_json_field_conflicts\x18\x0b \x01(\x08\x42\x02\x18\x01R\"deprecatedLegacyJsonFieldConflicts\x12\x37\n\x08\x66\x65\x61tures\x18\x0c \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x04\x10\x05J\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07J\x04\x08\x08\x10\tJ\x04\x08\t\x10\n\"\xce\r\n\x0c\x46ieldOptions\x12\x41\n\x05\x63type\x18\x01 \x01(\x0e\x32#.google.protobuf.FieldOptions.CType:\x06STRINGR\x05\x63type\x12\x16\n\x06packed\x18\x02 \x01(\x08R\x06packed\x12G\n\x06jstype\x18\x06 \x01(\x0e\x32$.google.protobuf.FieldOptions.JSType:\tJS_NORMALR\x06jstype\x12\x19\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lseR\x04lazy\x12.\n\x0funverified_lazy\x18\x0f \x01(\x08:\x05\x66\x61lseR\x0eunverifiedLazy\x12%\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12\x1d\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lseB\x02\x18\x01R\x04weak\x12(\n\x0c\x64\x65\x62ug_redact\x18\x10 \x01(\x08:\x05\x66\x61lseR\x0b\x64\x65\x62ugRedact\x12K\n\tretention\x18\x11 \x01(\x0e\x32-.google.protobuf.FieldOptions.OptionRetentionR\tretention\x12H\n\x07targets\x18\x13 \x03(\x0e\x32..google.protobuf.FieldOptions.OptionTargetTypeR\x07targets\x12W\n\x10\x65\x64ition_defaults\x18\x14 \x03(\x0b\x32,.google.protobuf.FieldOptions.EditionDefaultR\x0f\x65\x64itionDefaults\x12\x37\n\x08\x66\x65\x61tures\x18\x15 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12U\n\x0f\x66\x65\x61ture_support\x18\x16 \x01(\x0b\x32,.google.protobuf.FieldOptions.FeatureSupportR\x0e\x66\x65\x61tureSupport\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\x1aZ\n\x0e\x45\x64itionDefault\x12\x32\n\x07\x65\x64ition\x18\x03 \x01(\x0e\x32\x18.google.protobuf.EditionR\x07\x65\x64ition\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value\x1a\xbb\x02\n\x0e\x46\x65\x61tureSupport\x12G\n\x12\x65\x64ition_introduced\x18\x01 \x01(\x0e\x32\x18.google.protobuf.EditionR\x11\x65\x64itionIntroduced\x12G\n\x12\x65\x64ition_deprecated\x18\x02 \x01(\x0e\x32\x18.google.protobuf.EditionR\x11\x65\x64itionDeprecated\x12/\n\x13\x64\x65precation_warning\x18\x03 \x01(\tR\x12\x64\x65precationWarning\x12\x41\n\x0f\x65\x64ition_removed\x18\x04 \x01(\x0e\x32\x18.google.protobuf.EditionR\x0e\x65\x64itionRemoved\x12#\n\rremoval_error\x18\x05 \x01(\tR\x0cremovalError\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02\"5\n\x06JSType\x12\r\n\tJS_NORMAL\x10\x00\x12\r\n\tJS_STRING\x10\x01\x12\r\n\tJS_NUMBER\x10\x02\"U\n\x0fOptionRetention\x12\x15\n\x11RETENTION_UNKNOWN\x10\x00\x12\x15\n\x11RETENTION_RUNTIME\x10\x01\x12\x14\n\x10RETENTION_SOURCE\x10\x02\"\x8c\x02\n\x10OptionTargetType\x12\x17\n\x13TARGET_TYPE_UNKNOWN\x10\x00\x12\x14\n\x10TARGET_TYPE_FILE\x10\x01\x12\x1f\n\x1bTARGET_TYPE_EXTENSION_RANGE\x10\x02\x12\x17\n\x13TARGET_TYPE_MESSAGE\x10\x03\x12\x15\n\x11TARGET_TYPE_FIELD\x10\x04\x12\x15\n\x11TARGET_TYPE_ONEOF\x10\x05\x12\x14\n\x10TARGET_TYPE_ENUM\x10\x06\x12\x1a\n\x16TARGET_TYPE_ENUM_ENTRY\x10\x07\x12\x17\n\x13TARGET_TYPE_SERVICE\x10\x08\x12\x16\n\x12TARGET_TYPE_METHOD\x10\t*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x04\x10\x05J\x04\x08\x12\x10\x13\"\xb4\x01\n\x0cOneofOptions\x12\x37\n\x08\x66\x65\x61tures\x18\x01 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd9\x02\n\x0b\x45numOptions\x12\x1f\n\x0b\x61llow_alias\x18\x02 \x01(\x08R\nallowAlias\x12%\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12V\n&deprecated_legacy_json_field_conflicts\x18\x06 \x01(\x08\x42\x02\x18\x01R\"deprecatedLegacyJsonFieldConflicts\x12\x37\n\x08\x66\x65\x61tures\x18\x07 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x05\x10\x06\"\xe0\x02\n\x10\x45numValueOptions\x12%\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12\x37\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12(\n\x0c\x64\x65\x62ug_redact\x18\x03 \x01(\x08:\x05\x66\x61lseR\x0b\x64\x65\x62ugRedact\x12U\n\x0f\x66\x65\x61ture_support\x18\x04 \x01(\x0b\x32,.google.protobuf.FieldOptions.FeatureSupportR\x0e\x66\x65\x61tureSupport\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xdd\x01\n\x0eServiceOptions\x12\x37\n\x08\x66\x65\x61tures\x18\" \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12%\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xa1\x03\n\rMethodOptions\x12%\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12q\n\x11idempotency_level\x18\" \x01(\x0e\x32/.google.protobuf.MethodOptions.IdempotencyLevel:\x13IDEMPOTENCY_UNKNOWNR\x10idempotencyLevel\x12\x37\n\x08\x66\x65\x61tures\x18# \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\"P\n\x10IdempotencyLevel\x12\x17\n\x13IDEMPOTENCY_UNKNOWN\x10\x00\x12\x13\n\x0fNO_SIDE_EFFECTS\x10\x01\x12\x0e\n\nIDEMPOTENT\x10\x02*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x9a\x03\n\x13UninterpretedOption\x12\x41\n\x04name\x18\x02 \x03(\x0b\x32-.google.protobuf.UninterpretedOption.NamePartR\x04name\x12)\n\x10identifier_value\x18\x03 \x01(\tR\x0fidentifierValue\x12,\n\x12positive_int_value\x18\x04 \x01(\x04R\x10positiveIntValue\x12,\n\x12negative_int_value\x18\x05 \x01(\x03R\x10negativeIntValue\x12!\n\x0c\x64ouble_value\x18\x06 \x01(\x01R\x0b\x64oubleValue\x12!\n\x0cstring_value\x18\x07 \x01(\x0cR\x0bstringValue\x12\'\n\x0f\x61ggregate_value\x18\x08 \x01(\tR\x0e\x61ggregateValue\x1aJ\n\x08NamePart\x12\x1b\n\tname_part\x18\x01 \x02(\tR\x08namePart\x12!\n\x0cis_extension\x18\x02 \x02(\x08R\x0bisExtension\"\xfd\x11\n\nFeatureSet\x12\x91\x01\n\x0e\x66ield_presence\x18\x01 \x01(\x0e\x32).google.protobuf.FeatureSet.FieldPresenceB?\x88\x01\x01\x98\x01\x04\x98\x01\x01\xa2\x01\r\x12\x08\x45XPLICIT\x18\x84\x07\xa2\x01\r\x12\x08IMPLICIT\x18\xe7\x07\xa2\x01\r\x12\x08\x45XPLICIT\x18\xe8\x07\xb2\x01\x03\x08\xe8\x07R\rfieldPresence\x12l\n\tenum_type\x18\x02 \x01(\x0e\x32$.google.protobuf.FeatureSet.EnumTypeB)\x88\x01\x01\x98\x01\x06\x98\x01\x01\xa2\x01\x0b\x12\x06\x43LOSED\x18\x84\x07\xa2\x01\t\x12\x04OPEN\x18\xe7\x07\xb2\x01\x03\x08\xe8\x07R\x08\x65numType\x12\x98\x01\n\x17repeated_field_encoding\x18\x03 \x01(\x0e\x32\x31.google.protobuf.FeatureSet.RepeatedFieldEncodingB-\x88\x01\x01\x98\x01\x04\x98\x01\x01\xa2\x01\r\x12\x08\x45XPANDED\x18\x84\x07\xa2\x01\x0b\x12\x06PACKED\x18\xe7\x07\xb2\x01\x03\x08\xe8\x07R\x15repeatedFieldEncoding\x12~\n\x0futf8_validation\x18\x04 \x01(\x0e\x32*.google.protobuf.FeatureSet.Utf8ValidationB)\x88\x01\x01\x98\x01\x04\x98\x01\x01\xa2\x01\t\x12\x04NONE\x18\x84\x07\xa2\x01\x0b\x12\x06VERIFY\x18\xe7\x07\xb2\x01\x03\x08\xe8\x07R\x0eutf8Validation\x12~\n\x10message_encoding\x18\x05 \x01(\x0e\x32+.google.protobuf.FeatureSet.MessageEncodingB&\x88\x01\x01\x98\x01\x04\x98\x01\x01\xa2\x01\x14\x12\x0fLENGTH_PREFIXED\x18\x84\x07\xb2\x01\x03\x08\xe8\x07R\x0fmessageEncoding\x12\x82\x01\n\x0bjson_format\x18\x06 \x01(\x0e\x32&.google.protobuf.FeatureSet.JsonFormatB9\x88\x01\x01\x98\x01\x03\x98\x01\x06\x98\x01\x01\xa2\x01\x17\x12\x12LEGACY_BEST_EFFORT\x18\x84\x07\xa2\x01\n\x12\x05\x41LLOW\x18\xe7\x07\xb2\x01\x03\x08\xe8\x07R\njsonFormat\x12\xbc\x01\n\x14\x65nforce_naming_style\x18\x07 \x01(\x0e\x32..google.protobuf.FeatureSet.EnforceNamingStyleBZ\x88\x01\x02\x98\x01\x01\x98\x01\x02\x98\x01\x03\x98\x01\x04\x98\x01\x05\x98\x01\x06\x98\x01\x07\x98\x01\x08\x98\x01\t\xa2\x01\x11\x12\x0cSTYLE_LEGACY\x18\x84\x07\xa2\x01\x0e\x12\tSTYLE2024\x18\xe9\x07\xa2\x01\x0e\x12\tSTYLE2026\x18\xea\x07\xb2\x01\x03\x08\xe9\x07R\x12\x65nforceNamingStyle\x12\xc7\x01\n\x19\x64\x65\x66\x61ult_symbol_visibility\x18\x08 \x01(\x0e\x32\x45.google.protobuf.FeatureSet.VisibilityFeature.DefaultSymbolVisibilityBD\x88\x01\x02\x98\x01\x01\xa2\x01\x0f\x12\nEXPORT_ALL\x18\x84\x07\xa2\x01\x15\x12\x10\x45XPORT_TOP_LEVEL\x18\xe9\x07\xa2\x01\x0b\x12\x06STRICT\x18\xea\x07\xb2\x01\x03\x08\xe9\x07R\x17\x64\x65\x66\x61ultSymbolVisibility\x12\xc3\x01\n\x14\x65nforce_proto_limits\x18\t \x01(\x0e\x32\x41.google.protobuf.FeatureSet.ProtoLimitsFeature.EnforceProtoLimitsBN\x88\x01\x02\x98\x01\x06\x98\x01\x03\x98\x01\x04\x98\x01\x05\xa2\x01\x1e\x12\x19LEGACY_NO_EXPLICIT_LIMITS\x18\x84\x07\xa2\x01\x15\x12\x10PROTO_LIMITS2026\x18\xea\x07\xb2\x01\x03\x08\xea\x07R\x12\x65nforceProtoLimits\x1a\xa1\x01\n\x11VisibilityFeature\"\x81\x01\n\x17\x44\x65\x66\x61ultSymbolVisibility\x12%\n!DEFAULT_SYMBOL_VISIBILITY_UNKNOWN\x10\x00\x12\x0e\n\nEXPORT_ALL\x10\x01\x12\x14\n\x10\x45XPORT_TOP_LEVEL\x10\x02\x12\r\n\tLOCAL_ALL\x10\x03\x12\n\n\x06STRICT\x10\x04J\x08\x08\x01\x10\x80\x80\x80\x80\x02\x1ay\n\x12ProtoLimitsFeature\"c\n\x12\x45nforceProtoLimits\x12\x18\n\x14PROTO_LIMITS_UNKNOWN\x10\x00\x12\x1d\n\x19LEGACY_NO_EXPLICIT_LIMITS\x10\x01\x12\x14\n\x10PROTO_LIMITS2026\x10\x02\"\\\n\rFieldPresence\x12\x1a\n\x16\x46IELD_PRESENCE_UNKNOWN\x10\x00\x12\x0c\n\x08\x45XPLICIT\x10\x01\x12\x0c\n\x08IMPLICIT\x10\x02\x12\x13\n\x0fLEGACY_REQUIRED\x10\x03\"7\n\x08\x45numType\x12\x15\n\x11\x45NUM_TYPE_UNKNOWN\x10\x00\x12\x08\n\x04OPEN\x10\x01\x12\n\n\x06\x43LOSED\x10\x02\"V\n\x15RepeatedFieldEncoding\x12#\n\x1fREPEATED_FIELD_ENCODING_UNKNOWN\x10\x00\x12\n\n\x06PACKED\x10\x01\x12\x0c\n\x08\x45XPANDED\x10\x02\"I\n\x0eUtf8Validation\x12\x1b\n\x17UTF8_VALIDATION_UNKNOWN\x10\x00\x12\n\n\x06VERIFY\x10\x02\x12\x08\n\x04NONE\x10\x03\"\x04\x08\x01\x10\x01\"S\n\x0fMessageEncoding\x12\x1c\n\x18MESSAGE_ENCODING_UNKNOWN\x10\x00\x12\x13\n\x0fLENGTH_PREFIXED\x10\x01\x12\r\n\tDELIMITED\x10\x02\"H\n\nJsonFormat\x12\x17\n\x13JSON_FORMAT_UNKNOWN\x10\x00\x12\t\n\x05\x41LLOW\x10\x01\x12\x16\n\x12LEGACY_BEST_EFFORT\x10\x02\"f\n\x12\x45nforceNamingStyle\x12 \n\x1c\x45NFORCE_NAMING_STYLE_UNKNOWN\x10\x00\x12\r\n\tSTYLE2024\x10\x01\x12\x10\n\x0cSTYLE_LEGACY\x10\x02\x12\r\n\tSTYLE2026\x10\x03*\x06\x08\xe8\x07\x10\x8bN*\x06\x08\x8bN\x10\x90N*\x06\x08\x90N\x10\x91NJ\x06\x08\xe7\x07\x10\xe8\x07\"\xef\x03\n\x12\x46\x65\x61tureSetDefaults\x12X\n\x08\x64\x65\x66\x61ults\x18\x01 \x03(\x0b\x32<.google.protobuf.FeatureSetDefaults.FeatureSetEditionDefaultR\x08\x64\x65\x66\x61ults\x12\x41\n\x0fminimum_edition\x18\x04 \x01(\x0e\x32\x18.google.protobuf.EditionR\x0eminimumEdition\x12\x41\n\x0fmaximum_edition\x18\x05 \x01(\x0e\x32\x18.google.protobuf.EditionR\x0emaximumEdition\x1a\xf8\x01\n\x18\x46\x65\x61tureSetEditionDefault\x12\x32\n\x07\x65\x64ition\x18\x03 \x01(\x0e\x32\x18.google.protobuf.EditionR\x07\x65\x64ition\x12N\n\x14overridable_features\x18\x04 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x13overridableFeatures\x12\x42\n\x0e\x66ixed_features\x18\x05 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\rfixedFeaturesJ\x04\x08\x01\x10\x02J\x04\x08\x02\x10\x03R\x08\x66\x65\x61tures\"\xb5\x02\n\x0eSourceCodeInfo\x12\x44\n\x08location\x18\x01 \x03(\x0b\x32(.google.protobuf.SourceCodeInfo.LocationR\x08location\x1a\xce\x01\n\x08Location\x12\x16\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01R\x04path\x12\x16\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01R\x04span\x12)\n\x10leading_comments\x18\x03 \x01(\tR\x0fleadingComments\x12+\n\x11trailing_comments\x18\x04 \x01(\tR\x10trailingComments\x12:\n\x19leading_detached_comments\x18\x06 \x03(\tR\x17leadingDetachedComments*\x0c\x08\x80\xec\xca\xff\x01\x10\x81\xec\xca\xff\x01\"\xd0\x02\n\x11GeneratedCodeInfo\x12M\n\nannotation\x18\x01 \x03(\x0b\x32-.google.protobuf.GeneratedCodeInfo.AnnotationR\nannotation\x1a\xeb\x01\n\nAnnotation\x12\x16\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01R\x04path\x12\x1f\n\x0bsource_file\x18\x02 \x01(\tR\nsourceFile\x12\x14\n\x05\x62\x65gin\x18\x03 \x01(\x05R\x05\x62\x65gin\x12\x10\n\x03\x65nd\x18\x04 \x01(\x05R\x03\x65nd\x12R\n\x08semantic\x18\x05 \x01(\x0e\x32\x36.google.protobuf.GeneratedCodeInfo.Annotation.SemanticR\x08semantic\"(\n\x08Semantic\x12\x08\n\x04NONE\x10\x00\x12\x07\n\x03SET\x10\x01\x12\t\n\x05\x41LIAS\x10\x02*\xd1\x02\n\x07\x45\x64ition\x12\x13\n\x0f\x45\x44ITION_UNKNOWN\x10\x00\x12\x13\n\x0e\x45\x44ITION_LEGACY\x10\x84\x07\x12\x13\n\x0e\x45\x44ITION_PROTO2\x10\xe6\x07\x12\x13\n\x0e\x45\x44ITION_PROTO3\x10\xe7\x07\x12\x11\n\x0c\x45\x44ITION_2023\x10\xe8\x07\x12\x11\n\x0c\x45\x44ITION_2024\x10\xe9\x07\x12\x11\n\x0c\x45\x44ITION_2026\x10\xea\x07\x12\x15\n\x10\x45\x44ITION_UNSTABLE\x10\x8fN\x12\x17\n\x13\x45\x44ITION_1_TEST_ONLY\x10\x01\x12\x17\n\x13\x45\x44ITION_2_TEST_ONLY\x10\x02\x12\x1d\n\x17\x45\x44ITION_99997_TEST_ONLY\x10\x9d\x8d\x06\x12\x1d\n\x17\x45\x44ITION_99998_TEST_ONLY\x10\x9e\x8d\x06\x12\x1d\n\x17\x45\x44ITION_99999_TEST_ONLY\x10\x9f\x8d\x06\x12\x13\n\x0b\x45\x44ITION_MAX\x10\xff\xff\xff\xff\x07*U\n\x10SymbolVisibility\x12\x14\n\x10VISIBILITY_UNSET\x10\x00\x12\x14\n\x10VISIBILITY_LOCAL\x10\x01\x12\x15\n\x11VISIBILITY_EXPORT\x10\x02\x42~\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01Z-google.golang.org/protobuf/types/descriptorpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1aGoogle.Protobuf.Reflection' + ) +else: + DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n google/protobuf/descriptor.proto\x12\x0fgoogle.protobuf\"[\n\x11\x46ileDescriptorSet\x12\x38\n\x04\x66ile\x18\x01 \x03(\x0b\x32$.google.protobuf.FileDescriptorProtoR\x04\x66ile*\x0c\x08\x80\xec\xca\xff\x01\x10\x81\xec\xca\xff\x01\"\xc5\x05\n\x13\x46ileDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07package\x18\x02 \x01(\tR\x07package\x12\x1e\n\ndependency\x18\x03 \x03(\tR\ndependency\x12+\n\x11public_dependency\x18\n \x03(\x05R\x10publicDependency\x12\'\n\x0fweak_dependency\x18\x0b \x03(\x05R\x0eweakDependency\x12+\n\x11option_dependency\x18\x0f \x03(\tR\x10optionDependency\x12\x43\n\x0cmessage_type\x18\x04 \x03(\x0b\x32 .google.protobuf.DescriptorProtoR\x0bmessageType\x12\x41\n\tenum_type\x18\x05 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProtoR\x08\x65numType\x12\x41\n\x07service\x18\x06 \x03(\x0b\x32\'.google.protobuf.ServiceDescriptorProtoR\x07service\x12\x43\n\textension\x18\x07 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProtoR\textension\x12\x36\n\x07options\x18\x08 \x01(\x0b\x32\x1c.google.protobuf.FileOptionsR\x07options\x12I\n\x10source_code_info\x18\t \x01(\x0b\x32\x1f.google.protobuf.SourceCodeInfoR\x0esourceCodeInfo\x12\x16\n\x06syntax\x18\x0c \x01(\tR\x06syntax\x12\x32\n\x07\x65\x64ition\x18\x0e \x01(\x0e\x32\x18.google.protobuf.EditionR\x07\x65\x64ition\"\xfc\x06\n\x0f\x44\x65scriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12;\n\x05\x66ield\x18\x02 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProtoR\x05\x66ield\x12\x43\n\textension\x18\x06 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProtoR\textension\x12\x41\n\x0bnested_type\x18\x03 \x03(\x0b\x32 .google.protobuf.DescriptorProtoR\nnestedType\x12\x41\n\tenum_type\x18\x04 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProtoR\x08\x65numType\x12X\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32/.google.protobuf.DescriptorProto.ExtensionRangeR\x0e\x65xtensionRange\x12\x44\n\noneof_decl\x18\x08 \x03(\x0b\x32%.google.protobuf.OneofDescriptorProtoR\toneofDecl\x12\x39\n\x07options\x18\x07 \x01(\x0b\x32\x1f.google.protobuf.MessageOptionsR\x07options\x12U\n\x0ereserved_range\x18\t \x03(\x0b\x32..google.protobuf.DescriptorProto.ReservedRangeR\rreservedRange\x12#\n\rreserved_name\x18\n \x03(\tR\x0creservedName\x12\x41\n\nvisibility\x18\x0b \x01(\x0e\x32!.google.protobuf.SymbolVisibilityR\nvisibility\x1az\n\x0e\x45xtensionRange\x12\x14\n\x05start\x18\x01 \x01(\x05R\x05start\x12\x10\n\x03\x65nd\x18\x02 \x01(\x05R\x03\x65nd\x12@\n\x07options\x18\x03 \x01(\x0b\x32&.google.protobuf.ExtensionRangeOptionsR\x07options\x1a\x37\n\rReservedRange\x12\x14\n\x05start\x18\x01 \x01(\x05R\x05start\x12\x10\n\x03\x65nd\x18\x02 \x01(\x05R\x03\x65nd\"\xd4\x04\n\x15\x45xtensionRangeOptions\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\x12Y\n\x0b\x64\x65\x63laration\x18\x02 \x03(\x0b\x32\x32.google.protobuf.ExtensionRangeOptions.DeclarationB\x03\x88\x01\x02R\x0b\x64\x65\x63laration\x12\x37\n\x08\x66\x65\x61tures\x18\x32 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12m\n\x0cverification\x18\x03 \x01(\x0e\x32\x38.google.protobuf.ExtensionRangeOptions.VerificationState:\nUNVERIFIEDB\x03\x88\x01\x02R\x0cverification\x1a\x94\x01\n\x0b\x44\x65\x63laration\x12\x16\n\x06number\x18\x01 \x01(\x05R\x06number\x12\x1b\n\tfull_name\x18\x02 \x01(\tR\x08\x66ullName\x12\x12\n\x04type\x18\x03 \x01(\tR\x04type\x12\x1a\n\x08reserved\x18\x05 \x01(\x08R\x08reserved\x12\x1a\n\x08repeated\x18\x06 \x01(\x08R\x08repeatedJ\x04\x08\x04\x10\x05\"4\n\x11VerificationState\x12\x0f\n\x0b\x44\x45\x43LARATION\x10\x00\x12\x0e\n\nUNVERIFIED\x10\x01*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xc1\x06\n\x14\x46ieldDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n\x06number\x18\x03 \x01(\x05R\x06number\x12\x41\n\x05label\x18\x04 \x01(\x0e\x32+.google.protobuf.FieldDescriptorProto.LabelR\x05label\x12>\n\x04type\x18\x05 \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.TypeR\x04type\x12\x1b\n\ttype_name\x18\x06 \x01(\tR\x08typeName\x12\x1a\n\x08\x65xtendee\x18\x02 \x01(\tR\x08\x65xtendee\x12#\n\rdefault_value\x18\x07 \x01(\tR\x0c\x64\x65\x66\x61ultValue\x12\x1f\n\x0boneof_index\x18\t \x01(\x05R\noneofIndex\x12\x1b\n\tjson_name\x18\n \x01(\tR\x08jsonName\x12\x37\n\x07options\x18\x08 \x01(\x0b\x32\x1d.google.protobuf.FieldOptionsR\x07options\x12\'\n\x0fproto3_optional\x18\x11 \x01(\x08R\x0eproto3Optional\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REPEATED\x10\x03\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\"c\n\x14OneofDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x37\n\x07options\x18\x02 \x01(\x0b\x32\x1d.google.protobuf.OneofOptionsR\x07options\"\xa6\x03\n\x13\x45numDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12?\n\x05value\x18\x02 \x03(\x0b\x32).google.protobuf.EnumValueDescriptorProtoR\x05value\x12\x36\n\x07options\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.EnumOptionsR\x07options\x12]\n\x0ereserved_range\x18\x04 \x03(\x0b\x32\x36.google.protobuf.EnumDescriptorProto.EnumReservedRangeR\rreservedRange\x12#\n\rreserved_name\x18\x05 \x03(\tR\x0creservedName\x12\x41\n\nvisibility\x18\x06 \x01(\x0e\x32!.google.protobuf.SymbolVisibilityR\nvisibility\x1a;\n\x11\x45numReservedRange\x12\x14\n\x05start\x18\x01 \x01(\x05R\x05start\x12\x10\n\x03\x65nd\x18\x02 \x01(\x05R\x03\x65nd\"\x83\x01\n\x18\x45numValueDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n\x06number\x18\x02 \x01(\x05R\x06number\x12;\n\x07options\x18\x03 \x01(\x0b\x32!.google.protobuf.EnumValueOptionsR\x07options\"\xb5\x01\n\x16ServiceDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12>\n\x06method\x18\x02 \x03(\x0b\x32&.google.protobuf.MethodDescriptorProtoR\x06method\x12\x39\n\x07options\x18\x03 \x01(\x0b\x32\x1f.google.protobuf.ServiceOptionsR\x07optionsJ\x04\x08\x04\x10\x05R\x06stream\"\x89\x02\n\x15MethodDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n\ninput_type\x18\x02 \x01(\tR\tinputType\x12\x1f\n\x0boutput_type\x18\x03 \x01(\tR\noutputType\x12\x38\n\x07options\x18\x04 \x01(\x0b\x32\x1e.google.protobuf.MethodOptionsR\x07options\x12\x30\n\x10\x63lient_streaming\x18\x05 \x01(\x08:\x05\x66\x61lseR\x0f\x63lientStreaming\x12\x30\n\x10server_streaming\x18\x06 \x01(\x08:\x05\x66\x61lseR\x0fserverStreaming\"\x8c\x0c\n\x0b\x46ileOptions\x12!\n\x0cjava_package\x18\x01 \x01(\tR\x0bjavaPackage\x12\x30\n\x14java_outer_classname\x18\x08 \x01(\tR\x12javaOuterClassname\x12\xf9\x01\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lseB\xc1\x01\xb2\x01\xbd\x01\x08\xe6\x07 \xe9\x07*\xb4\x01This behavior is enabled by default in editions 2024 and above. To disable it, you can set `features.(pb.java).nest_in_file_class = YES` on individual messages, enums, or services.R\x11javaMultipleFiles\x12\x44\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08\x42\x02\x18\x01R\x19javaGenerateEqualsAndHash\x12:\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lseR\x13javaStringCheckUtf8\x12S\n\x0coptimize_for\x18\t \x01(\x0e\x32).google.protobuf.FileOptions.OptimizeMode:\x05SPEEDR\x0boptimizeFor\x12\x1d\n\ngo_package\x18\x0b \x01(\tR\tgoPackage\x12\x35\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lseR\x11\x63\x63GenericServices\x12\x39\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lseR\x13javaGenericServices\x12\x35\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lseR\x11pyGenericServices\x12%\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12\xbf\x01\n\x10\x63\x63_enable_arenas\x18\x1f \x01(\x08:\x04trueB\x8e\x01\xb2\x01\x8a\x01 \xea\x07*\x84\x01\x63\x63_enable_arenas is enabled by default in every edition and overrides are ignored.This option is removed in editions 2026 and above.R\x0e\x63\x63\x45nableArenas\x12*\n\x11objc_class_prefix\x18$ \x01(\tR\x0fobjcClassPrefix\x12)\n\x10\x63sharp_namespace\x18% \x01(\tR\x0f\x63sharpNamespace\x12!\n\x0cswift_prefix\x18\' \x01(\tR\x0bswiftPrefix\x12(\n\x10php_class_prefix\x18( \x01(\tR\x0ephpClassPrefix\x12#\n\rphp_namespace\x18) \x01(\tR\x0cphpNamespace\x12\x34\n\x16php_metadata_namespace\x18, \x01(\tR\x14phpMetadataNamespace\x12!\n\x0cruby_package\x18- \x01(\tR\x0brubyPackage\x12\x37\n\x08\x66\x65\x61tures\x18\x32 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08*\x10+J\x04\x08&\x10\'R\x14php_generic_services\"\xfc\x03\n\x0eMessageOptions\x12<\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lseR\x14messageSetWireFormat\x12L\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lseR\x1cnoStandardDescriptorAccessor\x12%\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12\x1b\n\tmap_entry\x18\x07 \x01(\x08R\x08mapEntry\x12V\n&deprecated_legacy_json_field_conflicts\x18\x0b \x01(\x08\x42\x02\x18\x01R\"deprecatedLegacyJsonFieldConflicts\x12\x37\n\x08\x66\x65\x61tures\x18\x0c \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x04\x10\x05J\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07J\x04\x08\x08\x10\tJ\x04\x08\t\x10\n\"\xce\r\n\x0c\x46ieldOptions\x12\x41\n\x05\x63type\x18\x01 \x01(\x0e\x32#.google.protobuf.FieldOptions.CType:\x06STRINGR\x05\x63type\x12\x16\n\x06packed\x18\x02 \x01(\x08R\x06packed\x12G\n\x06jstype\x18\x06 \x01(\x0e\x32$.google.protobuf.FieldOptions.JSType:\tJS_NORMALR\x06jstype\x12\x19\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lseR\x04lazy\x12.\n\x0funverified_lazy\x18\x0f \x01(\x08:\x05\x66\x61lseR\x0eunverifiedLazy\x12%\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12\x1d\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lseB\x02\x18\x01R\x04weak\x12(\n\x0c\x64\x65\x62ug_redact\x18\x10 \x01(\x08:\x05\x66\x61lseR\x0b\x64\x65\x62ugRedact\x12K\n\tretention\x18\x11 \x01(\x0e\x32-.google.protobuf.FieldOptions.OptionRetentionR\tretention\x12H\n\x07targets\x18\x13 \x03(\x0e\x32..google.protobuf.FieldOptions.OptionTargetTypeR\x07targets\x12W\n\x10\x65\x64ition_defaults\x18\x14 \x03(\x0b\x32,.google.protobuf.FieldOptions.EditionDefaultR\x0f\x65\x64itionDefaults\x12\x37\n\x08\x66\x65\x61tures\x18\x15 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12U\n\x0f\x66\x65\x61ture_support\x18\x16 \x01(\x0b\x32,.google.protobuf.FieldOptions.FeatureSupportR\x0e\x66\x65\x61tureSupport\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\x1aZ\n\x0e\x45\x64itionDefault\x12\x32\n\x07\x65\x64ition\x18\x03 \x01(\x0e\x32\x18.google.protobuf.EditionR\x07\x65\x64ition\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value\x1a\xbb\x02\n\x0e\x46\x65\x61tureSupport\x12G\n\x12\x65\x64ition_introduced\x18\x01 \x01(\x0e\x32\x18.google.protobuf.EditionR\x11\x65\x64itionIntroduced\x12G\n\x12\x65\x64ition_deprecated\x18\x02 \x01(\x0e\x32\x18.google.protobuf.EditionR\x11\x65\x64itionDeprecated\x12/\n\x13\x64\x65precation_warning\x18\x03 \x01(\tR\x12\x64\x65precationWarning\x12\x41\n\x0f\x65\x64ition_removed\x18\x04 \x01(\x0e\x32\x18.google.protobuf.EditionR\x0e\x65\x64itionRemoved\x12#\n\rremoval_error\x18\x05 \x01(\tR\x0cremovalError\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02\"5\n\x06JSType\x12\r\n\tJS_NORMAL\x10\x00\x12\r\n\tJS_STRING\x10\x01\x12\r\n\tJS_NUMBER\x10\x02\"U\n\x0fOptionRetention\x12\x15\n\x11RETENTION_UNKNOWN\x10\x00\x12\x15\n\x11RETENTION_RUNTIME\x10\x01\x12\x14\n\x10RETENTION_SOURCE\x10\x02\"\x8c\x02\n\x10OptionTargetType\x12\x17\n\x13TARGET_TYPE_UNKNOWN\x10\x00\x12\x14\n\x10TARGET_TYPE_FILE\x10\x01\x12\x1f\n\x1bTARGET_TYPE_EXTENSION_RANGE\x10\x02\x12\x17\n\x13TARGET_TYPE_MESSAGE\x10\x03\x12\x15\n\x11TARGET_TYPE_FIELD\x10\x04\x12\x15\n\x11TARGET_TYPE_ONEOF\x10\x05\x12\x14\n\x10TARGET_TYPE_ENUM\x10\x06\x12\x1a\n\x16TARGET_TYPE_ENUM_ENTRY\x10\x07\x12\x17\n\x13TARGET_TYPE_SERVICE\x10\x08\x12\x16\n\x12TARGET_TYPE_METHOD\x10\t*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x04\x10\x05J\x04\x08\x12\x10\x13\"\xb4\x01\n\x0cOneofOptions\x12\x37\n\x08\x66\x65\x61tures\x18\x01 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd9\x02\n\x0b\x45numOptions\x12\x1f\n\x0b\x61llow_alias\x18\x02 \x01(\x08R\nallowAlias\x12%\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12V\n&deprecated_legacy_json_field_conflicts\x18\x06 \x01(\x08\x42\x02\x18\x01R\"deprecatedLegacyJsonFieldConflicts\x12\x37\n\x08\x66\x65\x61tures\x18\x07 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x05\x10\x06\"\xe0\x02\n\x10\x45numValueOptions\x12%\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12\x37\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12(\n\x0c\x64\x65\x62ug_redact\x18\x03 \x01(\x08:\x05\x66\x61lseR\x0b\x64\x65\x62ugRedact\x12U\n\x0f\x66\x65\x61ture_support\x18\x04 \x01(\x0b\x32,.google.protobuf.FieldOptions.FeatureSupportR\x0e\x66\x65\x61tureSupport\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xdd\x01\n\x0eServiceOptions\x12\x37\n\x08\x66\x65\x61tures\x18\" \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12%\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xa1\x03\n\rMethodOptions\x12%\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12q\n\x11idempotency_level\x18\" \x01(\x0e\x32/.google.protobuf.MethodOptions.IdempotencyLevel:\x13IDEMPOTENCY_UNKNOWNR\x10idempotencyLevel\x12\x37\n\x08\x66\x65\x61tures\x18# \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x08\x66\x65\x61tures\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\"P\n\x10IdempotencyLevel\x12\x17\n\x13IDEMPOTENCY_UNKNOWN\x10\x00\x12\x13\n\x0fNO_SIDE_EFFECTS\x10\x01\x12\x0e\n\nIDEMPOTENT\x10\x02*\x06\x08\xde\x07\x10\xe7\x07*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x9a\x03\n\x13UninterpretedOption\x12\x41\n\x04name\x18\x02 \x03(\x0b\x32-.google.protobuf.UninterpretedOption.NamePartR\x04name\x12)\n\x10identifier_value\x18\x03 \x01(\tR\x0fidentifierValue\x12,\n\x12positive_int_value\x18\x04 \x01(\x04R\x10positiveIntValue\x12,\n\x12negative_int_value\x18\x05 \x01(\x03R\x10negativeIntValue\x12!\n\x0c\x64ouble_value\x18\x06 \x01(\x01R\x0b\x64oubleValue\x12!\n\x0cstring_value\x18\x07 \x01(\x0cR\x0bstringValue\x12\'\n\x0f\x61ggregate_value\x18\x08 \x01(\tR\x0e\x61ggregateValue\x1aJ\n\x08NamePart\x12\x1b\n\tname_part\x18\x01 \x02(\tR\x08namePart\x12!\n\x0cis_extension\x18\x02 \x02(\x08R\x0bisExtension\"\xfd\x11\n\nFeatureSet\x12\x91\x01\n\x0e\x66ield_presence\x18\x01 \x01(\x0e\x32).google.protobuf.FeatureSet.FieldPresenceB?\x88\x01\x01\x98\x01\x04\x98\x01\x01\xa2\x01\r\x12\x08\x45XPLICIT\x18\x84\x07\xa2\x01\r\x12\x08IMPLICIT\x18\xe7\x07\xa2\x01\r\x12\x08\x45XPLICIT\x18\xe8\x07\xb2\x01\x03\x08\xe8\x07R\rfieldPresence\x12l\n\tenum_type\x18\x02 \x01(\x0e\x32$.google.protobuf.FeatureSet.EnumTypeB)\x88\x01\x01\x98\x01\x06\x98\x01\x01\xa2\x01\x0b\x12\x06\x43LOSED\x18\x84\x07\xa2\x01\t\x12\x04OPEN\x18\xe7\x07\xb2\x01\x03\x08\xe8\x07R\x08\x65numType\x12\x98\x01\n\x17repeated_field_encoding\x18\x03 \x01(\x0e\x32\x31.google.protobuf.FeatureSet.RepeatedFieldEncodingB-\x88\x01\x01\x98\x01\x04\x98\x01\x01\xa2\x01\r\x12\x08\x45XPANDED\x18\x84\x07\xa2\x01\x0b\x12\x06PACKED\x18\xe7\x07\xb2\x01\x03\x08\xe8\x07R\x15repeatedFieldEncoding\x12~\n\x0futf8_validation\x18\x04 \x01(\x0e\x32*.google.protobuf.FeatureSet.Utf8ValidationB)\x88\x01\x01\x98\x01\x04\x98\x01\x01\xa2\x01\t\x12\x04NONE\x18\x84\x07\xa2\x01\x0b\x12\x06VERIFY\x18\xe7\x07\xb2\x01\x03\x08\xe8\x07R\x0eutf8Validation\x12~\n\x10message_encoding\x18\x05 \x01(\x0e\x32+.google.protobuf.FeatureSet.MessageEncodingB&\x88\x01\x01\x98\x01\x04\x98\x01\x01\xa2\x01\x14\x12\x0fLENGTH_PREFIXED\x18\x84\x07\xb2\x01\x03\x08\xe8\x07R\x0fmessageEncoding\x12\x82\x01\n\x0bjson_format\x18\x06 \x01(\x0e\x32&.google.protobuf.FeatureSet.JsonFormatB9\x88\x01\x01\x98\x01\x03\x98\x01\x06\x98\x01\x01\xa2\x01\x17\x12\x12LEGACY_BEST_EFFORT\x18\x84\x07\xa2\x01\n\x12\x05\x41LLOW\x18\xe7\x07\xb2\x01\x03\x08\xe8\x07R\njsonFormat\x12\xbc\x01\n\x14\x65nforce_naming_style\x18\x07 \x01(\x0e\x32..google.protobuf.FeatureSet.EnforceNamingStyleBZ\x88\x01\x02\x98\x01\x01\x98\x01\x02\x98\x01\x03\x98\x01\x04\x98\x01\x05\x98\x01\x06\x98\x01\x07\x98\x01\x08\x98\x01\t\xa2\x01\x11\x12\x0cSTYLE_LEGACY\x18\x84\x07\xa2\x01\x0e\x12\tSTYLE2024\x18\xe9\x07\xa2\x01\x0e\x12\tSTYLE2026\x18\xea\x07\xb2\x01\x03\x08\xe9\x07R\x12\x65nforceNamingStyle\x12\xc7\x01\n\x19\x64\x65\x66\x61ult_symbol_visibility\x18\x08 \x01(\x0e\x32\x45.google.protobuf.FeatureSet.VisibilityFeature.DefaultSymbolVisibilityBD\x88\x01\x02\x98\x01\x01\xa2\x01\x0f\x12\nEXPORT_ALL\x18\x84\x07\xa2\x01\x15\x12\x10\x45XPORT_TOP_LEVEL\x18\xe9\x07\xa2\x01\x0b\x12\x06STRICT\x18\xea\x07\xb2\x01\x03\x08\xe9\x07R\x17\x64\x65\x66\x61ultSymbolVisibility\x12\xc3\x01\n\x14\x65nforce_proto_limits\x18\t \x01(\x0e\x32\x41.google.protobuf.FeatureSet.ProtoLimitsFeature.EnforceProtoLimitsBN\x88\x01\x02\x98\x01\x06\x98\x01\x03\x98\x01\x04\x98\x01\x05\xa2\x01\x1e\x12\x19LEGACY_NO_EXPLICIT_LIMITS\x18\x84\x07\xa2\x01\x15\x12\x10PROTO_LIMITS2026\x18\xea\x07\xb2\x01\x03\x08\xea\x07R\x12\x65nforceProtoLimits\x1a\xa1\x01\n\x11VisibilityFeature\"\x81\x01\n\x17\x44\x65\x66\x61ultSymbolVisibility\x12%\n!DEFAULT_SYMBOL_VISIBILITY_UNKNOWN\x10\x00\x12\x0e\n\nEXPORT_ALL\x10\x01\x12\x14\n\x10\x45XPORT_TOP_LEVEL\x10\x02\x12\r\n\tLOCAL_ALL\x10\x03\x12\n\n\x06STRICT\x10\x04J\x08\x08\x01\x10\x80\x80\x80\x80\x02\x1ay\n\x12ProtoLimitsFeature\"c\n\x12\x45nforceProtoLimits\x12\x18\n\x14PROTO_LIMITS_UNKNOWN\x10\x00\x12\x1d\n\x19LEGACY_NO_EXPLICIT_LIMITS\x10\x01\x12\x14\n\x10PROTO_LIMITS2026\x10\x02\"\\\n\rFieldPresence\x12\x1a\n\x16\x46IELD_PRESENCE_UNKNOWN\x10\x00\x12\x0c\n\x08\x45XPLICIT\x10\x01\x12\x0c\n\x08IMPLICIT\x10\x02\x12\x13\n\x0fLEGACY_REQUIRED\x10\x03\"7\n\x08\x45numType\x12\x15\n\x11\x45NUM_TYPE_UNKNOWN\x10\x00\x12\x08\n\x04OPEN\x10\x01\x12\n\n\x06\x43LOSED\x10\x02\"V\n\x15RepeatedFieldEncoding\x12#\n\x1fREPEATED_FIELD_ENCODING_UNKNOWN\x10\x00\x12\n\n\x06PACKED\x10\x01\x12\x0c\n\x08\x45XPANDED\x10\x02\"I\n\x0eUtf8Validation\x12\x1b\n\x17UTF8_VALIDATION_UNKNOWN\x10\x00\x12\n\n\x06VERIFY\x10\x02\x12\x08\n\x04NONE\x10\x03\"\x04\x08\x01\x10\x01\"S\n\x0fMessageEncoding\x12\x1c\n\x18MESSAGE_ENCODING_UNKNOWN\x10\x00\x12\x13\n\x0fLENGTH_PREFIXED\x10\x01\x12\r\n\tDELIMITED\x10\x02\"H\n\nJsonFormat\x12\x17\n\x13JSON_FORMAT_UNKNOWN\x10\x00\x12\t\n\x05\x41LLOW\x10\x01\x12\x16\n\x12LEGACY_BEST_EFFORT\x10\x02\"f\n\x12\x45nforceNamingStyle\x12 \n\x1c\x45NFORCE_NAMING_STYLE_UNKNOWN\x10\x00\x12\r\n\tSTYLE2024\x10\x01\x12\x10\n\x0cSTYLE_LEGACY\x10\x02\x12\r\n\tSTYLE2026\x10\x03*\x06\x08\xe8\x07\x10\x8bN*\x06\x08\x8bN\x10\x90N*\x06\x08\x90N\x10\x91NJ\x06\x08\xe7\x07\x10\xe8\x07\"\xef\x03\n\x12\x46\x65\x61tureSetDefaults\x12X\n\x08\x64\x65\x66\x61ults\x18\x01 \x03(\x0b\x32<.google.protobuf.FeatureSetDefaults.FeatureSetEditionDefaultR\x08\x64\x65\x66\x61ults\x12\x41\n\x0fminimum_edition\x18\x04 \x01(\x0e\x32\x18.google.protobuf.EditionR\x0eminimumEdition\x12\x41\n\x0fmaximum_edition\x18\x05 \x01(\x0e\x32\x18.google.protobuf.EditionR\x0emaximumEdition\x1a\xf8\x01\n\x18\x46\x65\x61tureSetEditionDefault\x12\x32\n\x07\x65\x64ition\x18\x03 \x01(\x0e\x32\x18.google.protobuf.EditionR\x07\x65\x64ition\x12N\n\x14overridable_features\x18\x04 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\x13overridableFeatures\x12\x42\n\x0e\x66ixed_features\x18\x05 \x01(\x0b\x32\x1b.google.protobuf.FeatureSetR\rfixedFeaturesJ\x04\x08\x01\x10\x02J\x04\x08\x02\x10\x03R\x08\x66\x65\x61tures\"\xb5\x02\n\x0eSourceCodeInfo\x12\x44\n\x08location\x18\x01 \x03(\x0b\x32(.google.protobuf.SourceCodeInfo.LocationR\x08location\x1a\xce\x01\n\x08Location\x12\x16\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01R\x04path\x12\x16\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01R\x04span\x12)\n\x10leading_comments\x18\x03 \x01(\tR\x0fleadingComments\x12+\n\x11trailing_comments\x18\x04 \x01(\tR\x10trailingComments\x12:\n\x19leading_detached_comments\x18\x06 \x03(\tR\x17leadingDetachedComments*\x0c\x08\x80\xec\xca\xff\x01\x10\x81\xec\xca\xff\x01\"\xd0\x02\n\x11GeneratedCodeInfo\x12M\n\nannotation\x18\x01 \x03(\x0b\x32-.google.protobuf.GeneratedCodeInfo.AnnotationR\nannotation\x1a\xeb\x01\n\nAnnotation\x12\x16\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01R\x04path\x12\x1f\n\x0bsource_file\x18\x02 \x01(\tR\nsourceFile\x12\x14\n\x05\x62\x65gin\x18\x03 \x01(\x05R\x05\x62\x65gin\x12\x10\n\x03\x65nd\x18\x04 \x01(\x05R\x03\x65nd\x12R\n\x08semantic\x18\x05 \x01(\x0e\x32\x36.google.protobuf.GeneratedCodeInfo.Annotation.SemanticR\x08semantic\"(\n\x08Semantic\x12\x08\n\x04NONE\x10\x00\x12\x07\n\x03SET\x10\x01\x12\t\n\x05\x41LIAS\x10\x02*\xd1\x02\n\x07\x45\x64ition\x12\x13\n\x0f\x45\x44ITION_UNKNOWN\x10\x00\x12\x13\n\x0e\x45\x44ITION_LEGACY\x10\x84\x07\x12\x13\n\x0e\x45\x44ITION_PROTO2\x10\xe6\x07\x12\x13\n\x0e\x45\x44ITION_PROTO3\x10\xe7\x07\x12\x11\n\x0c\x45\x44ITION_2023\x10\xe8\x07\x12\x11\n\x0c\x45\x44ITION_2024\x10\xe9\x07\x12\x11\n\x0c\x45\x44ITION_2026\x10\xea\x07\x12\x15\n\x10\x45\x44ITION_UNSTABLE\x10\x8fN\x12\x17\n\x13\x45\x44ITION_1_TEST_ONLY\x10\x01\x12\x17\n\x13\x45\x44ITION_2_TEST_ONLY\x10\x02\x12\x1d\n\x17\x45\x44ITION_99997_TEST_ONLY\x10\x9d\x8d\x06\x12\x1d\n\x17\x45\x44ITION_99998_TEST_ONLY\x10\x9e\x8d\x06\x12\x1d\n\x17\x45\x44ITION_99999_TEST_ONLY\x10\x9f\x8d\x06\x12\x13\n\x0b\x45\x44ITION_MAX\x10\xff\xff\xff\xff\x07*U\n\x10SymbolVisibility\x12\x14\n\x10VISIBILITY_UNSET\x10\x00\x12\x14\n\x10VISIBILITY_LOCAL\x10\x01\x12\x15\n\x11VISIBILITY_EXPORT\x10\x02\x42~\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01Z-google.golang.org/protobuf/types/descriptorpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1aGoogle.Protobuf.Reflection') + +_globals = globals() +if not _descriptor._USE_C_DESCRIPTORS: + _EDITION = _descriptor.EnumDescriptor( + name='Edition', + full_name='google.protobuf.Edition', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='EDITION_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EDITION_LEGACY', index=1, number=900, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EDITION_PROTO2', index=2, number=998, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EDITION_PROTO3', index=3, number=999, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EDITION_2023', index=4, number=1000, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EDITION_2024', index=5, number=1001, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EDITION_2026', index=6, number=1002, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EDITION_UNSTABLE', index=7, number=9999, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EDITION_1_TEST_ONLY', index=8, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EDITION_2_TEST_ONLY', index=9, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EDITION_99997_TEST_ONLY', index=10, number=99997, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EDITION_99998_TEST_ONLY', index=11, number=99998, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EDITION_99999_TEST_ONLY', index=12, number=99999, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EDITION_MAX', index=13, number=2147483647, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_EDITION) + + _SYMBOLVISIBILITY = _descriptor.EnumDescriptor( + name='SymbolVisibility', + full_name='google.protobuf.SymbolVisibility', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='VISIBILITY_UNSET', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='VISIBILITY_LOCAL', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='VISIBILITY_EXPORT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_SYMBOLVISIBILITY) + + _EXTENSIONRANGEOPTIONS_VERIFICATIONSTATE = _descriptor.EnumDescriptor( + name='VerificationState', + full_name='google.protobuf.ExtensionRangeOptions.VerificationState', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DECLARATION', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='UNVERIFIED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_EXTENSIONRANGEOPTIONS_VERIFICATIONSTATE) + + _FIELDDESCRIPTORPROTO_TYPE = _descriptor.EnumDescriptor( + name='Type', + full_name='google.protobuf.FieldDescriptorProto.Type', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='TYPE_DOUBLE', index=0, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_FLOAT', index=1, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_INT64', index=2, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_UINT64', index=3, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_INT32', index=4, number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_FIXED64', index=5, number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_FIXED32', index=6, number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_BOOL', index=7, number=8, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_STRING', index=8, number=9, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_GROUP', index=9, number=10, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_MESSAGE', index=10, number=11, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_BYTES', index=11, number=12, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_UINT32', index=12, number=13, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_ENUM', index=13, number=14, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_SFIXED32', index=14, number=15, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_SFIXED64', index=15, number=16, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_SINT32', index=16, number=17, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_SINT64', index=17, number=18, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDDESCRIPTORPROTO_TYPE) + + _FIELDDESCRIPTORPROTO_LABEL = _descriptor.EnumDescriptor( + name='Label', + full_name='google.protobuf.FieldDescriptorProto.Label', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='LABEL_OPTIONAL', index=0, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LABEL_REPEATED', index=1, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LABEL_REQUIRED', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDDESCRIPTORPROTO_LABEL) + + _FILEOPTIONS_OPTIMIZEMODE = _descriptor.EnumDescriptor( + name='OptimizeMode', + full_name='google.protobuf.FileOptions.OptimizeMode', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SPEED', index=0, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CODE_SIZE', index=1, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LITE_RUNTIME', index=2, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FILEOPTIONS_OPTIMIZEMODE) + + _FIELDOPTIONS_CTYPE = _descriptor.EnumDescriptor( + name='CType', + full_name='google.protobuf.FieldOptions.CType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='STRING', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CORD', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STRING_PIECE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDOPTIONS_CTYPE) + + _FIELDOPTIONS_JSTYPE = _descriptor.EnumDescriptor( + name='JSType', + full_name='google.protobuf.FieldOptions.JSType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='JS_NORMAL', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='JS_STRING', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='JS_NUMBER', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDOPTIONS_JSTYPE) + + _FIELDOPTIONS_OPTIONRETENTION = _descriptor.EnumDescriptor( + name='OptionRetention', + full_name='google.protobuf.FieldOptions.OptionRetention', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='RETENTION_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='RETENTION_RUNTIME', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='RETENTION_SOURCE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDOPTIONS_OPTIONRETENTION) + + _FIELDOPTIONS_OPTIONTARGETTYPE = _descriptor.EnumDescriptor( + name='OptionTargetType', + full_name='google.protobuf.FieldOptions.OptionTargetType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='TARGET_TYPE_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TARGET_TYPE_FILE', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TARGET_TYPE_EXTENSION_RANGE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TARGET_TYPE_MESSAGE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TARGET_TYPE_FIELD', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TARGET_TYPE_ONEOF', index=5, number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TARGET_TYPE_ENUM', index=6, number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TARGET_TYPE_ENUM_ENTRY', index=7, number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TARGET_TYPE_SERVICE', index=8, number=8, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TARGET_TYPE_METHOD', index=9, number=9, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDOPTIONS_OPTIONTARGETTYPE) + + _METHODOPTIONS_IDEMPOTENCYLEVEL = _descriptor.EnumDescriptor( + name='IdempotencyLevel', + full_name='google.protobuf.MethodOptions.IdempotencyLevel', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='IDEMPOTENCY_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='NO_SIDE_EFFECTS', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='IDEMPOTENT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_METHODOPTIONS_IDEMPOTENCYLEVEL) + + _FEATURESET_VISIBILITYFEATURE_DEFAULTSYMBOLVISIBILITY = _descriptor.EnumDescriptor( + name='DefaultSymbolVisibility', + full_name='google.protobuf.FeatureSet.VisibilityFeature.DefaultSymbolVisibility', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DEFAULT_SYMBOL_VISIBILITY_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EXPORT_ALL', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EXPORT_TOP_LEVEL', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LOCAL_ALL', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STRICT', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FEATURESET_VISIBILITYFEATURE_DEFAULTSYMBOLVISIBILITY) + + _FEATURESET_PROTOLIMITSFEATURE_ENFORCEPROTOLIMITS = _descriptor.EnumDescriptor( + name='EnforceProtoLimits', + full_name='google.protobuf.FeatureSet.ProtoLimitsFeature.EnforceProtoLimits', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='PROTO_LIMITS_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LEGACY_NO_EXPLICIT_LIMITS', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PROTO_LIMITS2026', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FEATURESET_PROTOLIMITSFEATURE_ENFORCEPROTOLIMITS) + + _FEATURESET_FIELDPRESENCE = _descriptor.EnumDescriptor( + name='FieldPresence', + full_name='google.protobuf.FeatureSet.FieldPresence', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='FIELD_PRESENCE_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EXPLICIT', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='IMPLICIT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LEGACY_REQUIRED', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FEATURESET_FIELDPRESENCE) + + _FEATURESET_ENUMTYPE = _descriptor.EnumDescriptor( + name='EnumType', + full_name='google.protobuf.FeatureSet.EnumType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='ENUM_TYPE_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OPEN', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CLOSED', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FEATURESET_ENUMTYPE) + + _FEATURESET_REPEATEDFIELDENCODING = _descriptor.EnumDescriptor( + name='RepeatedFieldEncoding', + full_name='google.protobuf.FeatureSet.RepeatedFieldEncoding', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='REPEATED_FIELD_ENCODING_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PACKED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EXPANDED', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FEATURESET_REPEATEDFIELDENCODING) + + _FEATURESET_UTF8VALIDATION = _descriptor.EnumDescriptor( + name='Utf8Validation', + full_name='google.protobuf.FeatureSet.Utf8Validation', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='UTF8_VALIDATION_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='VERIFY', index=1, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='NONE', index=2, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FEATURESET_UTF8VALIDATION) + + _FEATURESET_MESSAGEENCODING = _descriptor.EnumDescriptor( + name='MessageEncoding', + full_name='google.protobuf.FeatureSet.MessageEncoding', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='MESSAGE_ENCODING_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LENGTH_PREFIXED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DELIMITED', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FEATURESET_MESSAGEENCODING) + + _FEATURESET_JSONFORMAT = _descriptor.EnumDescriptor( + name='JsonFormat', + full_name='google.protobuf.FeatureSet.JsonFormat', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='JSON_FORMAT_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='ALLOW', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LEGACY_BEST_EFFORT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FEATURESET_JSONFORMAT) + + _FEATURESET_ENFORCENAMINGSTYLE = _descriptor.EnumDescriptor( + name='EnforceNamingStyle', + full_name='google.protobuf.FeatureSet.EnforceNamingStyle', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='ENFORCE_NAMING_STYLE_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STYLE2024', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STYLE_LEGACY', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STYLE2026', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FEATURESET_ENFORCENAMINGSTYLE) + + _GENERATEDCODEINFO_ANNOTATION_SEMANTIC = _descriptor.EnumDescriptor( + name='Semantic', + full_name='google.protobuf.GeneratedCodeInfo.Annotation.Semantic', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='NONE', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SET', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='ALIAS', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_GENERATEDCODEINFO_ANNOTATION_SEMANTIC) + + + _FILEDESCRIPTORSET = _descriptor.Descriptor( + name='FileDescriptorSet', + full_name='google.protobuf.FileDescriptorSet', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='file', full_name='google.protobuf.FileDescriptorSet.file', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='file', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + extension_ranges=[(536000000, 536000001), ], + oneofs=[ + ], + ) + + + _FILEDESCRIPTORPROTO = _descriptor.Descriptor( + name='FileDescriptorProto', + full_name='google.protobuf.FileDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.FileDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='package', full_name='google.protobuf.FileDescriptorProto.package', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='package', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='dependency', full_name='google.protobuf.FileDescriptorProto.dependency', index=2, + number=3, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='dependency', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='public_dependency', full_name='google.protobuf.FileDescriptorProto.public_dependency', index=3, + number=10, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='publicDependency', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='weak_dependency', full_name='google.protobuf.FileDescriptorProto.weak_dependency', index=4, + number=11, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='weakDependency', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='option_dependency', full_name='google.protobuf.FileDescriptorProto.option_dependency', index=5, + number=15, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='optionDependency', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='message_type', full_name='google.protobuf.FileDescriptorProto.message_type', index=6, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='messageType', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='enum_type', full_name='google.protobuf.FileDescriptorProto.enum_type', index=7, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='enumType', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='service', full_name='google.protobuf.FileDescriptorProto.service', index=8, + number=6, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='service', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension', full_name='google.protobuf.FileDescriptorProto.extension', index=9, + number=7, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='extension', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.FileDescriptorProto.options', index=10, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='source_code_info', full_name='google.protobuf.FileDescriptorProto.source_code_info', index=11, + number=9, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='sourceCodeInfo', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='syntax', full_name='google.protobuf.FileDescriptorProto.syntax', index=12, + number=12, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='syntax', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='edition', full_name='google.protobuf.FileDescriptorProto.edition', index=13, + number=14, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='edition', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + + _DESCRIPTORPROTO_EXTENSIONRANGE = _descriptor.Descriptor( + name='ExtensionRange', + full_name='google.protobuf.DescriptorProto.ExtensionRange', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='start', full_name='google.protobuf.DescriptorProto.ExtensionRange.start', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='start', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', full_name='google.protobuf.DescriptorProto.ExtensionRange.end', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='end', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.DescriptorProto.ExtensionRange.options', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + _DESCRIPTORPROTO_RESERVEDRANGE = _descriptor.Descriptor( + name='ReservedRange', + full_name='google.protobuf.DescriptorProto.ReservedRange', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='start', full_name='google.protobuf.DescriptorProto.ReservedRange.start', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='start', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', full_name='google.protobuf.DescriptorProto.ReservedRange.end', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='end', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + _DESCRIPTORPROTO = _descriptor.Descriptor( + name='DescriptorProto', + full_name='google.protobuf.DescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.DescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='field', full_name='google.protobuf.DescriptorProto.field', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='field', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension', full_name='google.protobuf.DescriptorProto.extension', index=2, + number=6, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='extension', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='nested_type', full_name='google.protobuf.DescriptorProto.nested_type', index=3, + number=3, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='nestedType', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='enum_type', full_name='google.protobuf.DescriptorProto.enum_type', index=4, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='enumType', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension_range', full_name='google.protobuf.DescriptorProto.extension_range', index=5, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='extensionRange', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='oneof_decl', full_name='google.protobuf.DescriptorProto.oneof_decl', index=6, + number=8, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='oneofDecl', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.DescriptorProto.options', index=7, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reserved_range', full_name='google.protobuf.DescriptorProto.reserved_range', index=8, + number=9, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='reservedRange', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reserved_name', full_name='google.protobuf.DescriptorProto.reserved_name', index=9, + number=10, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='reservedName', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='visibility', full_name='google.protobuf.DescriptorProto.visibility', index=10, + number=11, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='visibility', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_DESCRIPTORPROTO_EXTENSIONRANGE, _DESCRIPTORPROTO_RESERVEDRANGE, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + + _EXTENSIONRANGEOPTIONS_DECLARATION = _descriptor.Descriptor( + name='Declaration', + full_name='google.protobuf.ExtensionRangeOptions.Declaration', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='number', full_name='google.protobuf.ExtensionRangeOptions.Declaration.number', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='number', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='full_name', full_name='google.protobuf.ExtensionRangeOptions.Declaration.full_name', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='fullName', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type', full_name='google.protobuf.ExtensionRangeOptions.Declaration.type', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='type', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reserved', full_name='google.protobuf.ExtensionRangeOptions.Declaration.reserved', index=3, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='reserved', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='repeated', full_name='google.protobuf.ExtensionRangeOptions.Declaration.repeated', index=4, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='repeated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + _EXTENSIONRANGEOPTIONS = _descriptor.Descriptor( + name='ExtensionRangeOptions', + full_name='google.protobuf.ExtensionRangeOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.ExtensionRangeOptions.uninterpreted_option', index=0, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='declaration', full_name='google.protobuf.ExtensionRangeOptions.declaration', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\210\001\002', json_name='declaration', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='features', full_name='google.protobuf.ExtensionRangeOptions.features', index=2, + number=50, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='features', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='verification', full_name='google.protobuf.ExtensionRangeOptions.verification', index=3, + number=3, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=1, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\210\001\002', json_name='verification', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_EXTENSIONRANGEOPTIONS_DECLARATION, ], + enum_types=[ + _EXTENSIONRANGEOPTIONS_VERIFICATIONSTATE, + ], + serialized_options=None, + is_extendable=True, + extension_ranges=[(990, 999), (1000, 536870912), ], + oneofs=[ + ], + ) + + + _FIELDDESCRIPTORPROTO = _descriptor.Descriptor( + name='FieldDescriptorProto', + full_name='google.protobuf.FieldDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.FieldDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='number', full_name='google.protobuf.FieldDescriptorProto.number', index=1, + number=3, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='number', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='label', full_name='google.protobuf.FieldDescriptorProto.label', index=2, + number=4, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=1, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='label', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type', full_name='google.protobuf.FieldDescriptorProto.type', index=3, + number=5, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=1, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='type', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type_name', full_name='google.protobuf.FieldDescriptorProto.type_name', index=4, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='typeName', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extendee', full_name='google.protobuf.FieldDescriptorProto.extendee', index=5, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='extendee', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='default_value', full_name='google.protobuf.FieldDescriptorProto.default_value', index=6, + number=7, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='defaultValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='oneof_index', full_name='google.protobuf.FieldDescriptorProto.oneof_index', index=7, + number=9, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='oneofIndex', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='json_name', full_name='google.protobuf.FieldDescriptorProto.json_name', index=8, + number=10, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='jsonName', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.FieldDescriptorProto.options', index=9, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='proto3_optional', full_name='google.protobuf.FieldDescriptorProto.proto3_optional', index=10, + number=17, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='proto3Optional', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _FIELDDESCRIPTORPROTO_TYPE, + _FIELDDESCRIPTORPROTO_LABEL, + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + + _ONEOFDESCRIPTORPROTO = _descriptor.Descriptor( + name='OneofDescriptorProto', + full_name='google.protobuf.OneofDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.OneofDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.OneofDescriptorProto.options', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + + _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE = _descriptor.Descriptor( + name='EnumReservedRange', + full_name='google.protobuf.EnumDescriptorProto.EnumReservedRange', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='start', full_name='google.protobuf.EnumDescriptorProto.EnumReservedRange.start', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='start', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', full_name='google.protobuf.EnumDescriptorProto.EnumReservedRange.end', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='end', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + _ENUMDESCRIPTORPROTO = _descriptor.Descriptor( + name='EnumDescriptorProto', + full_name='google.protobuf.EnumDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.EnumDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', full_name='google.protobuf.EnumDescriptorProto.value', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='value', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.EnumDescriptorProto.options', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reserved_range', full_name='google.protobuf.EnumDescriptorProto.reserved_range', index=3, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='reservedRange', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reserved_name', full_name='google.protobuf.EnumDescriptorProto.reserved_name', index=4, + number=5, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='reservedName', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='visibility', full_name='google.protobuf.EnumDescriptorProto.visibility', index=5, + number=6, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='visibility', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + + _ENUMVALUEDESCRIPTORPROTO = _descriptor.Descriptor( + name='EnumValueDescriptorProto', + full_name='google.protobuf.EnumValueDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.EnumValueDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='number', full_name='google.protobuf.EnumValueDescriptorProto.number', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='number', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.EnumValueDescriptorProto.options', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + + _SERVICEDESCRIPTORPROTO = _descriptor.Descriptor( + name='ServiceDescriptorProto', + full_name='google.protobuf.ServiceDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.ServiceDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='method', full_name='google.protobuf.ServiceDescriptorProto.method', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='method', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.ServiceDescriptorProto.options', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + + _METHODDESCRIPTORPROTO = _descriptor.Descriptor( + name='MethodDescriptorProto', + full_name='google.protobuf.MethodDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.MethodDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='input_type', full_name='google.protobuf.MethodDescriptorProto.input_type', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='inputType', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='output_type', full_name='google.protobuf.MethodDescriptorProto.output_type', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='outputType', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.MethodDescriptorProto.options', index=3, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='client_streaming', full_name='google.protobuf.MethodDescriptorProto.client_streaming', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='clientStreaming', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='server_streaming', full_name='google.protobuf.MethodDescriptorProto.server_streaming', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='serverStreaming', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + + _FILEOPTIONS = _descriptor.Descriptor( + name='FileOptions', + full_name='google.protobuf.FileOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='java_package', full_name='google.protobuf.FileOptions.java_package', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='javaPackage', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_outer_classname', full_name='google.protobuf.FileOptions.java_outer_classname', index=1, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='javaOuterClassname', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_multiple_files', full_name='google.protobuf.FileOptions.java_multiple_files', index=2, + number=10, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\262\001\275\001\010\346\007 \351\007*\264\001This behavior is enabled by default in editions 2024 and above. To disable it, you can set `features.(pb.java).nest_in_file_class = YES` on individual messages, enums, or services.', json_name='javaMultipleFiles', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_generate_equals_and_hash', full_name='google.protobuf.FileOptions.java_generate_equals_and_hash', index=3, + number=20, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\030\001', json_name='javaGenerateEqualsAndHash', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_string_check_utf8', full_name='google.protobuf.FileOptions.java_string_check_utf8', index=4, + number=27, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='javaStringCheckUtf8', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='optimize_for', full_name='google.protobuf.FileOptions.optimize_for', index=5, + number=9, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=1, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='optimizeFor', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='go_package', full_name='google.protobuf.FileOptions.go_package', index=6, + number=11, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='goPackage', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cc_generic_services', full_name='google.protobuf.FileOptions.cc_generic_services', index=7, + number=16, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='ccGenericServices', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_generic_services', full_name='google.protobuf.FileOptions.java_generic_services', index=8, + number=17, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='javaGenericServices', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='py_generic_services', full_name='google.protobuf.FileOptions.py_generic_services', index=9, + number=18, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='pyGenericServices', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.FileOptions.deprecated', index=10, + number=23, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cc_enable_arenas', full_name='google.protobuf.FileOptions.cc_enable_arenas', index=11, + number=31, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=True, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\262\001\212\001 \352\007*\204\001cc_enable_arenas is enabled by default in every edition and overrides are ignored.This option is removed in editions 2026 and above.', json_name='ccEnableArenas', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='objc_class_prefix', full_name='google.protobuf.FileOptions.objc_class_prefix', index=12, + number=36, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='objcClassPrefix', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='csharp_namespace', full_name='google.protobuf.FileOptions.csharp_namespace', index=13, + number=37, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='csharpNamespace', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='swift_prefix', full_name='google.protobuf.FileOptions.swift_prefix', index=14, + number=39, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='swiftPrefix', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='php_class_prefix', full_name='google.protobuf.FileOptions.php_class_prefix', index=15, + number=40, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='phpClassPrefix', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='php_namespace', full_name='google.protobuf.FileOptions.php_namespace', index=16, + number=41, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='phpNamespace', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='php_metadata_namespace', full_name='google.protobuf.FileOptions.php_metadata_namespace', index=17, + number=44, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='phpMetadataNamespace', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ruby_package', full_name='google.protobuf.FileOptions.ruby_package', index=18, + number=45, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='rubyPackage', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='features', full_name='google.protobuf.FileOptions.features', index=19, + number=50, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='features', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.FileOptions.uninterpreted_option', index=20, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _FILEOPTIONS_OPTIMIZEMODE, + ], + serialized_options=None, + is_extendable=True, + extension_ranges=[(990, 999), (1000, 536870912), ], + oneofs=[ + ], + ) + + + _MESSAGEOPTIONS = _descriptor.Descriptor( + name='MessageOptions', + full_name='google.protobuf.MessageOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='message_set_wire_format', full_name='google.protobuf.MessageOptions.message_set_wire_format', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='messageSetWireFormat', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='no_standard_descriptor_accessor', full_name='google.protobuf.MessageOptions.no_standard_descriptor_accessor', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='noStandardDescriptorAccessor', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.MessageOptions.deprecated', index=2, + number=3, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='map_entry', full_name='google.protobuf.MessageOptions.map_entry', index=3, + number=7, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='mapEntry', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated_legacy_json_field_conflicts', full_name='google.protobuf.MessageOptions.deprecated_legacy_json_field_conflicts', index=4, + number=11, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\030\001', json_name='deprecatedLegacyJsonFieldConflicts', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='features', full_name='google.protobuf.MessageOptions.features', index=5, + number=12, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='features', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.MessageOptions.uninterpreted_option', index=6, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + extension_ranges=[(990, 999), (1000, 536870912), ], + oneofs=[ + ], + ) + + + _FIELDOPTIONS_EDITIONDEFAULT = _descriptor.Descriptor( + name='EditionDefault', + full_name='google.protobuf.FieldOptions.EditionDefault', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='edition', full_name='google.protobuf.FieldOptions.EditionDefault.edition', index=0, + number=3, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='edition', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', full_name='google.protobuf.FieldOptions.EditionDefault.value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='value', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + _FIELDOPTIONS_FEATURESUPPORT = _descriptor.Descriptor( + name='FeatureSupport', + full_name='google.protobuf.FieldOptions.FeatureSupport', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='edition_introduced', full_name='google.protobuf.FieldOptions.FeatureSupport.edition_introduced', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='editionIntroduced', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='edition_deprecated', full_name='google.protobuf.FieldOptions.FeatureSupport.edition_deprecated', index=1, + number=2, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='editionDeprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecation_warning', full_name='google.protobuf.FieldOptions.FeatureSupport.deprecation_warning', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecationWarning', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='edition_removed', full_name='google.protobuf.FieldOptions.FeatureSupport.edition_removed', index=3, + number=4, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='editionRemoved', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='removal_error', full_name='google.protobuf.FieldOptions.FeatureSupport.removal_error', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='removalError', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + _FIELDOPTIONS = _descriptor.Descriptor( + name='FieldOptions', + full_name='google.protobuf.FieldOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='ctype', full_name='google.protobuf.FieldOptions.ctype', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='ctype', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='packed', full_name='google.protobuf.FieldOptions.packed', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='packed', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='jstype', full_name='google.protobuf.FieldOptions.jstype', index=2, + number=6, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='jstype', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='lazy', full_name='google.protobuf.FieldOptions.lazy', index=3, + number=5, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='lazy', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='unverified_lazy', full_name='google.protobuf.FieldOptions.unverified_lazy', index=4, + number=15, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='unverifiedLazy', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.FieldOptions.deprecated', index=5, + number=3, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='weak', full_name='google.protobuf.FieldOptions.weak', index=6, + number=10, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\030\001', json_name='weak', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='debug_redact', full_name='google.protobuf.FieldOptions.debug_redact', index=7, + number=16, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='debugRedact', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='retention', full_name='google.protobuf.FieldOptions.retention', index=8, + number=17, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='retention', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='targets', full_name='google.protobuf.FieldOptions.targets', index=9, + number=19, type=14, cpp_type=8, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='targets', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='edition_defaults', full_name='google.protobuf.FieldOptions.edition_defaults', index=10, + number=20, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='editionDefaults', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='features', full_name='google.protobuf.FieldOptions.features', index=11, + number=21, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='features', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='feature_support', full_name='google.protobuf.FieldOptions.feature_support', index=12, + number=22, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='featureSupport', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.FieldOptions.uninterpreted_option', index=13, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_FIELDOPTIONS_EDITIONDEFAULT, _FIELDOPTIONS_FEATURESUPPORT, ], + enum_types=[ + _FIELDOPTIONS_CTYPE, + _FIELDOPTIONS_JSTYPE, + _FIELDOPTIONS_OPTIONRETENTION, + _FIELDOPTIONS_OPTIONTARGETTYPE, + ], + serialized_options=None, + is_extendable=True, + extension_ranges=[(990, 999), (1000, 536870912), ], + oneofs=[ + ], + ) + + + _ONEOFOPTIONS = _descriptor.Descriptor( + name='OneofOptions', + full_name='google.protobuf.OneofOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='features', full_name='google.protobuf.OneofOptions.features', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='features', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.OneofOptions.uninterpreted_option', index=1, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + extension_ranges=[(990, 999), (1000, 536870912), ], + oneofs=[ + ], + ) + + + _ENUMOPTIONS = _descriptor.Descriptor( + name='EnumOptions', + full_name='google.protobuf.EnumOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='allow_alias', full_name='google.protobuf.EnumOptions.allow_alias', index=0, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='allowAlias', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.EnumOptions.deprecated', index=1, + number=3, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated_legacy_json_field_conflicts', full_name='google.protobuf.EnumOptions.deprecated_legacy_json_field_conflicts', index=2, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\030\001', json_name='deprecatedLegacyJsonFieldConflicts', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='features', full_name='google.protobuf.EnumOptions.features', index=3, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='features', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.EnumOptions.uninterpreted_option', index=4, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + extension_ranges=[(990, 999), (1000, 536870912), ], + oneofs=[ + ], + ) + + + _ENUMVALUEOPTIONS = _descriptor.Descriptor( + name='EnumValueOptions', + full_name='google.protobuf.EnumValueOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.EnumValueOptions.deprecated', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='features', full_name='google.protobuf.EnumValueOptions.features', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='features', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='debug_redact', full_name='google.protobuf.EnumValueOptions.debug_redact', index=2, + number=3, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='debugRedact', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='feature_support', full_name='google.protobuf.EnumValueOptions.feature_support', index=3, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='featureSupport', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.EnumValueOptions.uninterpreted_option', index=4, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + extension_ranges=[(990, 999), (1000, 536870912), ], + oneofs=[ + ], + ) + + + _SERVICEOPTIONS = _descriptor.Descriptor( + name='ServiceOptions', + full_name='google.protobuf.ServiceOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='features', full_name='google.protobuf.ServiceOptions.features', index=0, + number=34, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='features', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.ServiceOptions.deprecated', index=1, + number=33, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.ServiceOptions.uninterpreted_option', index=2, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + extension_ranges=[(990, 999), (1000, 536870912), ], + oneofs=[ + ], + ) + + + _METHODOPTIONS = _descriptor.Descriptor( + name='MethodOptions', + full_name='google.protobuf.MethodOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.MethodOptions.deprecated', index=0, + number=33, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='idempotency_level', full_name='google.protobuf.MethodOptions.idempotency_level', index=1, + number=34, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='idempotencyLevel', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='features', full_name='google.protobuf.MethodOptions.features', index=2, + number=35, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='features', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.MethodOptions.uninterpreted_option', index=3, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _METHODOPTIONS_IDEMPOTENCYLEVEL, + ], + serialized_options=None, + is_extendable=True, + extension_ranges=[(990, 999), (1000, 536870912), ], + oneofs=[ + ], + ) + + + _UNINTERPRETEDOPTION_NAMEPART = _descriptor.Descriptor( + name='NamePart', + full_name='google.protobuf.UninterpretedOption.NamePart', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name_part', full_name='google.protobuf.UninterpretedOption.NamePart.name_part', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='namePart', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_extension', full_name='google.protobuf.UninterpretedOption.NamePart.is_extension', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='isExtension', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + _UNINTERPRETEDOPTION = _descriptor.Descriptor( + name='UninterpretedOption', + full_name='google.protobuf.UninterpretedOption', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.UninterpretedOption.name', index=0, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='identifier_value', full_name='google.protobuf.UninterpretedOption.identifier_value', index=1, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='identifierValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='positive_int_value', full_name='google.protobuf.UninterpretedOption.positive_int_value', index=2, + number=4, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='positiveIntValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='negative_int_value', full_name='google.protobuf.UninterpretedOption.negative_int_value', index=3, + number=5, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='negativeIntValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='double_value', full_name='google.protobuf.UninterpretedOption.double_value', index=4, + number=6, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='doubleValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='string_value', full_name='google.protobuf.UninterpretedOption.string_value', index=5, + number=7, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='stringValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='aggregate_value', full_name='google.protobuf.UninterpretedOption.aggregate_value', index=6, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='aggregateValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_UNINTERPRETEDOPTION_NAMEPART, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + + _FEATURESET_VISIBILITYFEATURE = _descriptor.Descriptor( + name='VisibilityFeature', + full_name='google.protobuf.FeatureSet.VisibilityFeature', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _FEATURESET_VISIBILITYFEATURE_DEFAULTSYMBOLVISIBILITY, + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + _FEATURESET_PROTOLIMITSFEATURE = _descriptor.Descriptor( + name='ProtoLimitsFeature', + full_name='google.protobuf.FeatureSet.ProtoLimitsFeature', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _FEATURESET_PROTOLIMITSFEATURE_ENFORCEPROTOLIMITS, + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + _FEATURESET = _descriptor.Descriptor( + name='FeatureSet', + full_name='google.protobuf.FeatureSet', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='field_presence', full_name='google.protobuf.FeatureSet.field_presence', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\210\001\001\230\001\004\230\001\001\242\001\r\022\010EXPLICIT\030\204\007\242\001\r\022\010IMPLICIT\030\347\007\242\001\r\022\010EXPLICIT\030\350\007\262\001\003\010\350\007', json_name='fieldPresence', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='enum_type', full_name='google.protobuf.FeatureSet.enum_type', index=1, + number=2, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\210\001\001\230\001\006\230\001\001\242\001\013\022\006CLOSED\030\204\007\242\001\t\022\004OPEN\030\347\007\262\001\003\010\350\007', json_name='enumType', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='repeated_field_encoding', full_name='google.protobuf.FeatureSet.repeated_field_encoding', index=2, + number=3, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\210\001\001\230\001\004\230\001\001\242\001\r\022\010EXPANDED\030\204\007\242\001\013\022\006PACKED\030\347\007\262\001\003\010\350\007', json_name='repeatedFieldEncoding', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='utf8_validation', full_name='google.protobuf.FeatureSet.utf8_validation', index=3, + number=4, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\210\001\001\230\001\004\230\001\001\242\001\t\022\004NONE\030\204\007\242\001\013\022\006VERIFY\030\347\007\262\001\003\010\350\007', json_name='utf8Validation', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='message_encoding', full_name='google.protobuf.FeatureSet.message_encoding', index=4, + number=5, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\210\001\001\230\001\004\230\001\001\242\001\024\022\017LENGTH_PREFIXED\030\204\007\262\001\003\010\350\007', json_name='messageEncoding', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='json_format', full_name='google.protobuf.FeatureSet.json_format', index=5, + number=6, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\210\001\001\230\001\003\230\001\006\230\001\001\242\001\027\022\022LEGACY_BEST_EFFORT\030\204\007\242\001\n\022\005ALLOW\030\347\007\262\001\003\010\350\007', json_name='jsonFormat', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='enforce_naming_style', full_name='google.protobuf.FeatureSet.enforce_naming_style', index=6, + number=7, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\210\001\002\230\001\001\230\001\002\230\001\003\230\001\004\230\001\005\230\001\006\230\001\007\230\001\010\230\001\t\242\001\021\022\014STYLE_LEGACY\030\204\007\242\001\016\022\tSTYLE2024\030\351\007\242\001\016\022\tSTYLE2026\030\352\007\262\001\003\010\351\007', json_name='enforceNamingStyle', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='default_symbol_visibility', full_name='google.protobuf.FeatureSet.default_symbol_visibility', index=7, + number=8, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\210\001\002\230\001\001\242\001\017\022\nEXPORT_ALL\030\204\007\242\001\025\022\020EXPORT_TOP_LEVEL\030\351\007\242\001\013\022\006STRICT\030\352\007\262\001\003\010\351\007', json_name='defaultSymbolVisibility', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='enforce_proto_limits', full_name='google.protobuf.FeatureSet.enforce_proto_limits', index=8, + number=9, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\210\001\002\230\001\006\230\001\003\230\001\004\230\001\005\242\001\036\022\031LEGACY_NO_EXPLICIT_LIMITS\030\204\007\242\001\025\022\020PROTO_LIMITS2026\030\352\007\262\001\003\010\352\007', json_name='enforceProtoLimits', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_FEATURESET_VISIBILITYFEATURE, _FEATURESET_PROTOLIMITSFEATURE, ], + enum_types=[ + _FEATURESET_FIELDPRESENCE, + _FEATURESET_ENUMTYPE, + _FEATURESET_REPEATEDFIELDENCODING, + _FEATURESET_UTF8VALIDATION, + _FEATURESET_MESSAGEENCODING, + _FEATURESET_JSONFORMAT, + _FEATURESET_ENFORCENAMINGSTYLE, + ], + serialized_options=None, + is_extendable=True, + extension_ranges=[(1000, 9995), (9995, 10000), (10000, 10001), ], + oneofs=[ + ], + ) + + + _FEATURESETDEFAULTS_FEATURESETEDITIONDEFAULT = _descriptor.Descriptor( + name='FeatureSetEditionDefault', + full_name='google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='edition', full_name='google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.edition', index=0, + number=3, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='edition', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='overridable_features', full_name='google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.overridable_features', index=1, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='overridableFeatures', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='fixed_features', full_name='google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.fixed_features', index=2, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='fixedFeatures', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + _FEATURESETDEFAULTS = _descriptor.Descriptor( + name='FeatureSetDefaults', + full_name='google.protobuf.FeatureSetDefaults', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='defaults', full_name='google.protobuf.FeatureSetDefaults.defaults', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='defaults', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='minimum_edition', full_name='google.protobuf.FeatureSetDefaults.minimum_edition', index=1, + number=4, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='minimumEdition', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='maximum_edition', full_name='google.protobuf.FeatureSetDefaults.maximum_edition', index=2, + number=5, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='maximumEdition', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_FEATURESETDEFAULTS_FEATURESETEDITIONDEFAULT, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + + _SOURCECODEINFO_LOCATION = _descriptor.Descriptor( + name='Location', + full_name='google.protobuf.SourceCodeInfo.Location', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='path', full_name='google.protobuf.SourceCodeInfo.Location.path', index=0, + number=1, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\020\001', json_name='path', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='span', full_name='google.protobuf.SourceCodeInfo.Location.span', index=1, + number=2, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\020\001', json_name='span', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='leading_comments', full_name='google.protobuf.SourceCodeInfo.Location.leading_comments', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='leadingComments', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='trailing_comments', full_name='google.protobuf.SourceCodeInfo.Location.trailing_comments', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='trailingComments', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='leading_detached_comments', full_name='google.protobuf.SourceCodeInfo.Location.leading_detached_comments', index=4, + number=6, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='leadingDetachedComments', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + _SOURCECODEINFO = _descriptor.Descriptor( + name='SourceCodeInfo', + full_name='google.protobuf.SourceCodeInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='location', full_name='google.protobuf.SourceCodeInfo.location', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='location', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_SOURCECODEINFO_LOCATION, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + extension_ranges=[(536000000, 536000001), ], + oneofs=[ + ], + ) + + + _GENERATEDCODEINFO_ANNOTATION = _descriptor.Descriptor( + name='Annotation', + full_name='google.protobuf.GeneratedCodeInfo.Annotation', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='path', full_name='google.protobuf.GeneratedCodeInfo.Annotation.path', index=0, + number=1, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=b'\020\001', json_name='path', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='source_file', full_name='google.protobuf.GeneratedCodeInfo.Annotation.source_file', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='sourceFile', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='begin', full_name='google.protobuf.GeneratedCodeInfo.Annotation.begin', index=2, + number=3, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='begin', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', full_name='google.protobuf.GeneratedCodeInfo.Annotation.end', index=3, + number=4, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='end', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='semantic', full_name='google.protobuf.GeneratedCodeInfo.Annotation.semantic', index=4, + number=5, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='semantic', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _GENERATEDCODEINFO_ANNOTATION_SEMANTIC, + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + _GENERATEDCODEINFO = _descriptor.Descriptor( + name='GeneratedCodeInfo', + full_name='google.protobuf.GeneratedCodeInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='annotation', full_name='google.protobuf.GeneratedCodeInfo.annotation', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='annotation', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_GENERATEDCODEINFO_ANNOTATION, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + ) + + _FILEDESCRIPTORSET.fields_by_name['file'].message_type = _FILEDESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['message_type'].message_type = _DESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['enum_type'].message_type = _ENUMDESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['service'].message_type = _SERVICEDESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['extension'].message_type = _FIELDDESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['options'].message_type = _FILEOPTIONS + _FILEDESCRIPTORPROTO.fields_by_name['source_code_info'].message_type = _SOURCECODEINFO + _FILEDESCRIPTORPROTO.fields_by_name['edition'].enum_type = _EDITION + _DESCRIPTORPROTO_EXTENSIONRANGE.fields_by_name['options'].message_type = _EXTENSIONRANGEOPTIONS + _DESCRIPTORPROTO_EXTENSIONRANGE.containing_type = _DESCRIPTORPROTO + _DESCRIPTORPROTO_RESERVEDRANGE.containing_type = _DESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['field'].message_type = _FIELDDESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['extension'].message_type = _FIELDDESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['nested_type'].message_type = _DESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['enum_type'].message_type = _ENUMDESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['extension_range'].message_type = _DESCRIPTORPROTO_EXTENSIONRANGE + _DESCRIPTORPROTO.fields_by_name['oneof_decl'].message_type = _ONEOFDESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['options'].message_type = _MESSAGEOPTIONS + _DESCRIPTORPROTO.fields_by_name['reserved_range'].message_type = _DESCRIPTORPROTO_RESERVEDRANGE + _DESCRIPTORPROTO.fields_by_name['visibility'].enum_type = _SYMBOLVISIBILITY + _EXTENSIONRANGEOPTIONS_DECLARATION.containing_type = _EXTENSIONRANGEOPTIONS + _EXTENSIONRANGEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _EXTENSIONRANGEOPTIONS.fields_by_name['declaration'].message_type = _EXTENSIONRANGEOPTIONS_DECLARATION + _EXTENSIONRANGEOPTIONS.fields_by_name['features'].message_type = _FEATURESET + _EXTENSIONRANGEOPTIONS.fields_by_name['verification'].enum_type = _EXTENSIONRANGEOPTIONS_VERIFICATIONSTATE + _EXTENSIONRANGEOPTIONS_VERIFICATIONSTATE.containing_type = _EXTENSIONRANGEOPTIONS + _FIELDDESCRIPTORPROTO.fields_by_name['label'].enum_type = _FIELDDESCRIPTORPROTO_LABEL + _FIELDDESCRIPTORPROTO.fields_by_name['type'].enum_type = _FIELDDESCRIPTORPROTO_TYPE + _FIELDDESCRIPTORPROTO.fields_by_name['options'].message_type = _FIELDOPTIONS + _FIELDDESCRIPTORPROTO_TYPE.containing_type = _FIELDDESCRIPTORPROTO + _FIELDDESCRIPTORPROTO_LABEL.containing_type = _FIELDDESCRIPTORPROTO + _ONEOFDESCRIPTORPROTO.fields_by_name['options'].message_type = _ONEOFOPTIONS + _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE.containing_type = _ENUMDESCRIPTORPROTO + _ENUMDESCRIPTORPROTO.fields_by_name['value'].message_type = _ENUMVALUEDESCRIPTORPROTO + _ENUMDESCRIPTORPROTO.fields_by_name['options'].message_type = _ENUMOPTIONS + _ENUMDESCRIPTORPROTO.fields_by_name['reserved_range'].message_type = _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE + _ENUMDESCRIPTORPROTO.fields_by_name['visibility'].enum_type = _SYMBOLVISIBILITY + _ENUMVALUEDESCRIPTORPROTO.fields_by_name['options'].message_type = _ENUMVALUEOPTIONS + _SERVICEDESCRIPTORPROTO.fields_by_name['method'].message_type = _METHODDESCRIPTORPROTO + _SERVICEDESCRIPTORPROTO.fields_by_name['options'].message_type = _SERVICEOPTIONS + _METHODDESCRIPTORPROTO.fields_by_name['options'].message_type = _METHODOPTIONS + _FILEOPTIONS.fields_by_name['optimize_for'].enum_type = _FILEOPTIONS_OPTIMIZEMODE + _FILEOPTIONS.fields_by_name['features'].message_type = _FEATURESET + _FILEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _FILEOPTIONS_OPTIMIZEMODE.containing_type = _FILEOPTIONS + _MESSAGEOPTIONS.fields_by_name['features'].message_type = _FEATURESET + _MESSAGEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _FIELDOPTIONS_EDITIONDEFAULT.fields_by_name['edition'].enum_type = _EDITION + _FIELDOPTIONS_EDITIONDEFAULT.containing_type = _FIELDOPTIONS + _FIELDOPTIONS_FEATURESUPPORT.fields_by_name['edition_introduced'].enum_type = _EDITION + _FIELDOPTIONS_FEATURESUPPORT.fields_by_name['edition_deprecated'].enum_type = _EDITION + _FIELDOPTIONS_FEATURESUPPORT.fields_by_name['edition_removed'].enum_type = _EDITION + _FIELDOPTIONS_FEATURESUPPORT.containing_type = _FIELDOPTIONS + _FIELDOPTIONS.fields_by_name['ctype'].enum_type = _FIELDOPTIONS_CTYPE + _FIELDOPTIONS.fields_by_name['jstype'].enum_type = _FIELDOPTIONS_JSTYPE + _FIELDOPTIONS.fields_by_name['retention'].enum_type = _FIELDOPTIONS_OPTIONRETENTION + _FIELDOPTIONS.fields_by_name['targets'].enum_type = _FIELDOPTIONS_OPTIONTARGETTYPE + _FIELDOPTIONS.fields_by_name['edition_defaults'].message_type = _FIELDOPTIONS_EDITIONDEFAULT + _FIELDOPTIONS.fields_by_name['features'].message_type = _FEATURESET + _FIELDOPTIONS.fields_by_name['feature_support'].message_type = _FIELDOPTIONS_FEATURESUPPORT + _FIELDOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _FIELDOPTIONS_CTYPE.containing_type = _FIELDOPTIONS + _FIELDOPTIONS_JSTYPE.containing_type = _FIELDOPTIONS + _FIELDOPTIONS_OPTIONRETENTION.containing_type = _FIELDOPTIONS + _FIELDOPTIONS_OPTIONTARGETTYPE.containing_type = _FIELDOPTIONS + _ONEOFOPTIONS.fields_by_name['features'].message_type = _FEATURESET + _ONEOFOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _ENUMOPTIONS.fields_by_name['features'].message_type = _FEATURESET + _ENUMOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _ENUMVALUEOPTIONS.fields_by_name['features'].message_type = _FEATURESET + _ENUMVALUEOPTIONS.fields_by_name['feature_support'].message_type = _FIELDOPTIONS_FEATURESUPPORT + _ENUMVALUEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _SERVICEOPTIONS.fields_by_name['features'].message_type = _FEATURESET + _SERVICEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _METHODOPTIONS.fields_by_name['idempotency_level'].enum_type = _METHODOPTIONS_IDEMPOTENCYLEVEL + _METHODOPTIONS.fields_by_name['features'].message_type = _FEATURESET + _METHODOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _METHODOPTIONS_IDEMPOTENCYLEVEL.containing_type = _METHODOPTIONS + _UNINTERPRETEDOPTION_NAMEPART.containing_type = _UNINTERPRETEDOPTION + _UNINTERPRETEDOPTION.fields_by_name['name'].message_type = _UNINTERPRETEDOPTION_NAMEPART + _FEATURESET_VISIBILITYFEATURE.containing_type = _FEATURESET + _FEATURESET_VISIBILITYFEATURE_DEFAULTSYMBOLVISIBILITY.containing_type = _FEATURESET_VISIBILITYFEATURE + _FEATURESET_PROTOLIMITSFEATURE.containing_type = _FEATURESET + _FEATURESET_PROTOLIMITSFEATURE_ENFORCEPROTOLIMITS.containing_type = _FEATURESET_PROTOLIMITSFEATURE + _FEATURESET.fields_by_name['field_presence'].enum_type = _FEATURESET_FIELDPRESENCE + _FEATURESET.fields_by_name['enum_type'].enum_type = _FEATURESET_ENUMTYPE + _FEATURESET.fields_by_name['repeated_field_encoding'].enum_type = _FEATURESET_REPEATEDFIELDENCODING + _FEATURESET.fields_by_name['utf8_validation'].enum_type = _FEATURESET_UTF8VALIDATION + _FEATURESET.fields_by_name['message_encoding'].enum_type = _FEATURESET_MESSAGEENCODING + _FEATURESET.fields_by_name['json_format'].enum_type = _FEATURESET_JSONFORMAT + _FEATURESET.fields_by_name['enforce_naming_style'].enum_type = _FEATURESET_ENFORCENAMINGSTYLE + _FEATURESET.fields_by_name['default_symbol_visibility'].enum_type = _FEATURESET_VISIBILITYFEATURE_DEFAULTSYMBOLVISIBILITY + _FEATURESET.fields_by_name['enforce_proto_limits'].enum_type = _FEATURESET_PROTOLIMITSFEATURE_ENFORCEPROTOLIMITS + _FEATURESET_FIELDPRESENCE.containing_type = _FEATURESET + _FEATURESET_ENUMTYPE.containing_type = _FEATURESET + _FEATURESET_REPEATEDFIELDENCODING.containing_type = _FEATURESET + _FEATURESET_UTF8VALIDATION.containing_type = _FEATURESET + _FEATURESET_MESSAGEENCODING.containing_type = _FEATURESET + _FEATURESET_JSONFORMAT.containing_type = _FEATURESET + _FEATURESET_ENFORCENAMINGSTYLE.containing_type = _FEATURESET + _FEATURESETDEFAULTS_FEATURESETEDITIONDEFAULT.fields_by_name['edition'].enum_type = _EDITION + _FEATURESETDEFAULTS_FEATURESETEDITIONDEFAULT.fields_by_name['overridable_features'].message_type = _FEATURESET + _FEATURESETDEFAULTS_FEATURESETEDITIONDEFAULT.fields_by_name['fixed_features'].message_type = _FEATURESET + _FEATURESETDEFAULTS_FEATURESETEDITIONDEFAULT.containing_type = _FEATURESETDEFAULTS + _FEATURESETDEFAULTS.fields_by_name['defaults'].message_type = _FEATURESETDEFAULTS_FEATURESETEDITIONDEFAULT + _FEATURESETDEFAULTS.fields_by_name['minimum_edition'].enum_type = _EDITION + _FEATURESETDEFAULTS.fields_by_name['maximum_edition'].enum_type = _EDITION + _SOURCECODEINFO_LOCATION.containing_type = _SOURCECODEINFO + _SOURCECODEINFO.fields_by_name['location'].message_type = _SOURCECODEINFO_LOCATION + _GENERATEDCODEINFO_ANNOTATION.fields_by_name['semantic'].enum_type = _GENERATEDCODEINFO_ANNOTATION_SEMANTIC + _GENERATEDCODEINFO_ANNOTATION.containing_type = _GENERATEDCODEINFO + _GENERATEDCODEINFO_ANNOTATION_SEMANTIC.containing_type = _GENERATEDCODEINFO_ANNOTATION + _GENERATEDCODEINFO.fields_by_name['annotation'].message_type = _GENERATEDCODEINFO_ANNOTATION + DESCRIPTOR.message_types_by_name['FileDescriptorSet'] = _FILEDESCRIPTORSET + DESCRIPTOR.message_types_by_name['FileDescriptorProto'] = _FILEDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['DescriptorProto'] = _DESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['ExtensionRangeOptions'] = _EXTENSIONRANGEOPTIONS + DESCRIPTOR.message_types_by_name['FieldDescriptorProto'] = _FIELDDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['OneofDescriptorProto'] = _ONEOFDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['EnumDescriptorProto'] = _ENUMDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['EnumValueDescriptorProto'] = _ENUMVALUEDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['ServiceDescriptorProto'] = _SERVICEDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['MethodDescriptorProto'] = _METHODDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['FileOptions'] = _FILEOPTIONS + DESCRIPTOR.message_types_by_name['MessageOptions'] = _MESSAGEOPTIONS + DESCRIPTOR.message_types_by_name['FieldOptions'] = _FIELDOPTIONS + DESCRIPTOR.message_types_by_name['OneofOptions'] = _ONEOFOPTIONS + DESCRIPTOR.message_types_by_name['EnumOptions'] = _ENUMOPTIONS + DESCRIPTOR.message_types_by_name['EnumValueOptions'] = _ENUMVALUEOPTIONS + DESCRIPTOR.message_types_by_name['ServiceOptions'] = _SERVICEOPTIONS + DESCRIPTOR.message_types_by_name['MethodOptions'] = _METHODOPTIONS + DESCRIPTOR.message_types_by_name['UninterpretedOption'] = _UNINTERPRETEDOPTION + DESCRIPTOR.message_types_by_name['FeatureSet'] = _FEATURESET + DESCRIPTOR.message_types_by_name['FeatureSetDefaults'] = _FEATURESETDEFAULTS + DESCRIPTOR.message_types_by_name['SourceCodeInfo'] = _SOURCECODEINFO + DESCRIPTOR.message_types_by_name['GeneratedCodeInfo'] = _GENERATEDCODEINFO + DESCRIPTOR.enum_types_by_name['Edition'] = _EDITION + DESCRIPTOR.enum_types_by_name['SymbolVisibility'] = _SYMBOLVISIBILITY + _sym_db.RegisterFileDescriptor(DESCRIPTOR) + + class _ResolvedFeatures: + def __init__(self, features = None, **kwargs): + if features: + for k, v in features.FIELDS.items(): + setattr(self, k, getattr(features, k)) + else: + for k, v in kwargs.items(): + setattr(self, k, v) + DESCRIPTOR._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORSET._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORSET.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[5]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[6]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[7]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[8]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[9]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[10]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[11]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[12]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEDESCRIPTORPROTO.fields[13]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO.fields[5]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO.fields[6]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO.fields[7]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO.fields[8]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO.fields[9]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO.fields[10]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO_EXTENSIONRANGE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO_EXTENSIONRANGE.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO_EXTENSIONRANGE.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO_EXTENSIONRANGE.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO_RESERVEDRANGE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO_RESERVEDRANGE.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _DESCRIPTORPROTO_RESERVEDRANGE.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS_DECLARATION._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS_DECLARATION.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS_DECLARATION.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS_DECLARATION.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS_DECLARATION.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS_DECLARATION.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO.fields[5]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO.fields[6]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO.fields[7]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO.fields[8]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO.fields[9]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO.fields[10]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ONEOFDESCRIPTORPROTO._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ONEOFDESCRIPTORPROTO.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ONEOFDESCRIPTORPROTO.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMDESCRIPTORPROTO._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMDESCRIPTORPROTO.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMDESCRIPTORPROTO.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMDESCRIPTORPROTO.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMDESCRIPTORPROTO.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMDESCRIPTORPROTO.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMDESCRIPTORPROTO.fields[5]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMVALUEDESCRIPTORPROTO._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMVALUEDESCRIPTORPROTO.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMVALUEDESCRIPTORPROTO.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMVALUEDESCRIPTORPROTO.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SERVICEDESCRIPTORPROTO._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SERVICEDESCRIPTORPROTO.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SERVICEDESCRIPTORPROTO.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SERVICEDESCRIPTORPROTO.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODDESCRIPTORPROTO._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODDESCRIPTORPROTO.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODDESCRIPTORPROTO.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODDESCRIPTORPROTO.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODDESCRIPTORPROTO.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODDESCRIPTORPROTO.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODDESCRIPTORPROTO.fields[5]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[5]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[6]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[7]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[8]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[9]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[10]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[11]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[12]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[13]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[14]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[15]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[16]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[17]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[18]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[19]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS.fields[20]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _MESSAGEOPTIONS._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _MESSAGEOPTIONS.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _MESSAGEOPTIONS.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _MESSAGEOPTIONS.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _MESSAGEOPTIONS.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _MESSAGEOPTIONS.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _MESSAGEOPTIONS.fields[5]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _MESSAGEOPTIONS.fields[6]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[5]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[6]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[7]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[8]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[9]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[10]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[11]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[12]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS.fields[13]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_EDITIONDEFAULT._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_EDITIONDEFAULT.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_EDITIONDEFAULT.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_FEATURESUPPORT._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_FEATURESUPPORT.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_FEATURESUPPORT.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_FEATURESUPPORT.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_FEATURESUPPORT.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_FEATURESUPPORT.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ONEOFOPTIONS._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ONEOFOPTIONS.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ONEOFOPTIONS.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMOPTIONS._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMOPTIONS.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMOPTIONS.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMOPTIONS.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMOPTIONS.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMOPTIONS.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMVALUEOPTIONS._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMVALUEOPTIONS.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMVALUEOPTIONS.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMVALUEOPTIONS.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMVALUEOPTIONS.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _ENUMVALUEOPTIONS.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SERVICEOPTIONS._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SERVICEOPTIONS.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SERVICEOPTIONS.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SERVICEOPTIONS.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODOPTIONS._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODOPTIONS.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODOPTIONS.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODOPTIONS.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODOPTIONS.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _UNINTERPRETEDOPTION._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _UNINTERPRETEDOPTION.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _UNINTERPRETEDOPTION.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _UNINTERPRETEDOPTION.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _UNINTERPRETEDOPTION.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _UNINTERPRETEDOPTION.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _UNINTERPRETEDOPTION.fields[5]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _UNINTERPRETEDOPTION.fields[6]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _UNINTERPRETEDOPTION_NAMEPART._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _UNINTERPRETEDOPTION_NAMEPART.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["LEGACY_REQUIRED"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _UNINTERPRETEDOPTION_NAMEPART.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["LEGACY_REQUIRED"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET.fields[5]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET.fields[6]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET.fields[7]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET.fields[8]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_VISIBILITYFEATURE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_PROTOLIMITSFEATURE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESETDEFAULTS._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESETDEFAULTS.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESETDEFAULTS.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESETDEFAULTS.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESETDEFAULTS_FEATURESETEDITIONDEFAULT._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESETDEFAULTS_FEATURESETEDITIONDEFAULT.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESETDEFAULTS_FEATURESETEDITIONDEFAULT.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESETDEFAULTS_FEATURESETEDITIONDEFAULT.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SOURCECODEINFO._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SOURCECODEINFO.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SOURCECODEINFO_LOCATION._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SOURCECODEINFO_LOCATION.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["PACKED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SOURCECODEINFO_LOCATION.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["PACKED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SOURCECODEINFO_LOCATION.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SOURCECODEINFO_LOCATION.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SOURCECODEINFO_LOCATION.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _GENERATEDCODEINFO._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _GENERATEDCODEINFO.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _GENERATEDCODEINFO_ANNOTATION._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _GENERATEDCODEINFO_ANNOTATION.fields[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["PACKED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _GENERATEDCODEINFO_ANNOTATION.fields[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _GENERATEDCODEINFO_ANNOTATION.fields[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _GENERATEDCODEINFO_ANNOTATION.fields[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _GENERATEDCODEINFO_ANNOTATION.fields[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS_VERIFICATIONSTATE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS_VERIFICATIONSTATE.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EXTENSIONRANGEOPTIONS_VERIFICATIONSTATE.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[5]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[6]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[7]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[8]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[9]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[10]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[11]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[12]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[13]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[14]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[15]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[16]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_TYPE.values[17]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_LABEL._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_LABEL.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_LABEL.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDDESCRIPTORPROTO_LABEL.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS_OPTIMIZEMODE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS_OPTIMIZEMODE.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS_OPTIMIZEMODE.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FILEOPTIONS_OPTIMIZEMODE.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_CTYPE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_CTYPE.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_CTYPE.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_CTYPE.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_JSTYPE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_JSTYPE.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_JSTYPE.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_JSTYPE.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONRETENTION._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONRETENTION.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONRETENTION.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONRETENTION.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONTARGETTYPE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONTARGETTYPE.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONTARGETTYPE.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONTARGETTYPE.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONTARGETTYPE.values[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONTARGETTYPE.values[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONTARGETTYPE.values[5]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONTARGETTYPE.values[6]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONTARGETTYPE.values[7]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONTARGETTYPE.values[8]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FIELDOPTIONS_OPTIONTARGETTYPE.values[9]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODOPTIONS_IDEMPOTENCYLEVEL._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODOPTIONS_IDEMPOTENCYLEVEL.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODOPTIONS_IDEMPOTENCYLEVEL.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _METHODOPTIONS_IDEMPOTENCYLEVEL.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_FIELDPRESENCE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_FIELDPRESENCE.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_FIELDPRESENCE.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_FIELDPRESENCE.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_FIELDPRESENCE.values[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_ENUMTYPE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_ENUMTYPE.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_ENUMTYPE.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_ENUMTYPE.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_REPEATEDFIELDENCODING._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_REPEATEDFIELDENCODING.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_REPEATEDFIELDENCODING.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_REPEATEDFIELDENCODING.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_UTF8VALIDATION._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_UTF8VALIDATION.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_UTF8VALIDATION.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_UTF8VALIDATION.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_MESSAGEENCODING._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_MESSAGEENCODING.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_MESSAGEENCODING.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_MESSAGEENCODING.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_JSONFORMAT._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_JSONFORMAT.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_JSONFORMAT.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_JSONFORMAT.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_ENFORCENAMINGSTYLE._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_ENFORCENAMINGSTYLE.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_ENFORCENAMINGSTYLE.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_ENFORCENAMINGSTYLE.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_ENFORCENAMINGSTYLE.values[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_VISIBILITYFEATURE_DEFAULTSYMBOLVISIBILITY._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_VISIBILITYFEATURE_DEFAULTSYMBOLVISIBILITY.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_VISIBILITYFEATURE_DEFAULTSYMBOLVISIBILITY.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_VISIBILITYFEATURE_DEFAULTSYMBOLVISIBILITY.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_VISIBILITYFEATURE_DEFAULTSYMBOLVISIBILITY.values[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_VISIBILITYFEATURE_DEFAULTSYMBOLVISIBILITY.values[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_PROTOLIMITSFEATURE_ENFORCEPROTOLIMITS._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_PROTOLIMITSFEATURE_ENFORCEPROTOLIMITS.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_PROTOLIMITSFEATURE_ENFORCEPROTOLIMITS.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _FEATURESET_PROTOLIMITSFEATURE_ENFORCEPROTOLIMITS.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _GENERATEDCODEINFO_ANNOTATION_SEMANTIC._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _GENERATEDCODEINFO_ANNOTATION_SEMANTIC.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _GENERATEDCODEINFO_ANNOTATION_SEMANTIC.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _GENERATEDCODEINFO_ANNOTATION_SEMANTIC.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[3]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[4]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[5]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[6]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[7]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[8]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[9]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[10]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[11]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[12]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _EDITION.values[13]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SYMBOLVISIBILITY._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SYMBOLVISIBILITY.values[0]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SYMBOLVISIBILITY.values[1]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) + _SYMBOLVISIBILITY.values[2]._features = _ResolvedFeatures(field_presence=_FEATURESET_FIELDPRESENCE.values_by_name["EXPLICIT"].number,enum_type=_FEATURESET_ENUMTYPE.values_by_name["CLOSED"].number,repeated_field_encoding=_FEATURESET_REPEATEDFIELDENCODING.values_by_name["EXPANDED"].number,utf8_validation=_FEATURESET_UTF8VALIDATION.values_by_name["NONE"].number,message_encoding=_FEATURESET_MESSAGEENCODING.values_by_name["LENGTH_PREFIXED"].number,json_format=_FEATURESET_JSONFORMAT.values_by_name["LEGACY_BEST_EFFORT"].number) +else: + _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.descriptor_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\023com.google.protobufB\020DescriptorProtosH\001Z-google.golang.org/protobuf/types/descriptorpb\370\001\001\242\002\003GPB\252\002\032Google.Protobuf.Reflection' + _globals['_EXTENSIONRANGEOPTIONS'].fields_by_name['declaration']._loaded_options = None + _globals['_EXTENSIONRANGEOPTIONS'].fields_by_name['declaration']._serialized_options = b'\210\001\002' + _globals['_EXTENSIONRANGEOPTIONS'].fields_by_name['verification']._loaded_options = None + _globals['_EXTENSIONRANGEOPTIONS'].fields_by_name['verification']._serialized_options = b'\210\001\002' + _globals['_FILEOPTIONS'].fields_by_name['java_multiple_files']._loaded_options = None + _globals['_FILEOPTIONS'].fields_by_name['java_multiple_files']._serialized_options = b'\262\001\275\001\010\346\007 \351\007*\264\001This behavior is enabled by default in editions 2024 and above. To disable it, you can set `features.(pb.java).nest_in_file_class = YES` on individual messages, enums, or services.' + _globals['_FILEOPTIONS'].fields_by_name['java_generate_equals_and_hash']._loaded_options = None + _globals['_FILEOPTIONS'].fields_by_name['java_generate_equals_and_hash']._serialized_options = b'\030\001' + _globals['_FILEOPTIONS'].fields_by_name['cc_enable_arenas']._loaded_options = None + _globals['_FILEOPTIONS'].fields_by_name['cc_enable_arenas']._serialized_options = b'\262\001\212\001 \352\007*\204\001cc_enable_arenas is enabled by default in every edition and overrides are ignored.This option is removed in editions 2026 and above.' + _globals['_MESSAGEOPTIONS'].fields_by_name['deprecated_legacy_json_field_conflicts']._loaded_options = None + _globals['_MESSAGEOPTIONS'].fields_by_name['deprecated_legacy_json_field_conflicts']._serialized_options = b'\030\001' + _globals['_FIELDOPTIONS'].fields_by_name['weak']._loaded_options = None + _globals['_FIELDOPTIONS'].fields_by_name['weak']._serialized_options = b'\030\001' + _globals['_ENUMOPTIONS'].fields_by_name['deprecated_legacy_json_field_conflicts']._loaded_options = None + _globals['_ENUMOPTIONS'].fields_by_name['deprecated_legacy_json_field_conflicts']._serialized_options = b'\030\001' + _globals['_FEATURESET'].fields_by_name['field_presence']._loaded_options = None + _globals['_FEATURESET'].fields_by_name['field_presence']._serialized_options = b'\210\001\001\230\001\004\230\001\001\242\001\r\022\010EXPLICIT\030\204\007\242\001\r\022\010IMPLICIT\030\347\007\242\001\r\022\010EXPLICIT\030\350\007\262\001\003\010\350\007' + _globals['_FEATURESET'].fields_by_name['enum_type']._loaded_options = None + _globals['_FEATURESET'].fields_by_name['enum_type']._serialized_options = b'\210\001\001\230\001\006\230\001\001\242\001\013\022\006CLOSED\030\204\007\242\001\t\022\004OPEN\030\347\007\262\001\003\010\350\007' + _globals['_FEATURESET'].fields_by_name['repeated_field_encoding']._loaded_options = None + _globals['_FEATURESET'].fields_by_name['repeated_field_encoding']._serialized_options = b'\210\001\001\230\001\004\230\001\001\242\001\r\022\010EXPANDED\030\204\007\242\001\013\022\006PACKED\030\347\007\262\001\003\010\350\007' + _globals['_FEATURESET'].fields_by_name['utf8_validation']._loaded_options = None + _globals['_FEATURESET'].fields_by_name['utf8_validation']._serialized_options = b'\210\001\001\230\001\004\230\001\001\242\001\t\022\004NONE\030\204\007\242\001\013\022\006VERIFY\030\347\007\262\001\003\010\350\007' + _globals['_FEATURESET'].fields_by_name['message_encoding']._loaded_options = None + _globals['_FEATURESET'].fields_by_name['message_encoding']._serialized_options = b'\210\001\001\230\001\004\230\001\001\242\001\024\022\017LENGTH_PREFIXED\030\204\007\262\001\003\010\350\007' + _globals['_FEATURESET'].fields_by_name['json_format']._loaded_options = None + _globals['_FEATURESET'].fields_by_name['json_format']._serialized_options = b'\210\001\001\230\001\003\230\001\006\230\001\001\242\001\027\022\022LEGACY_BEST_EFFORT\030\204\007\242\001\n\022\005ALLOW\030\347\007\262\001\003\010\350\007' + _globals['_FEATURESET'].fields_by_name['enforce_naming_style']._loaded_options = None + _globals['_FEATURESET'].fields_by_name['enforce_naming_style']._serialized_options = b'\210\001\002\230\001\001\230\001\002\230\001\003\230\001\004\230\001\005\230\001\006\230\001\007\230\001\010\230\001\t\242\001\021\022\014STYLE_LEGACY\030\204\007\242\001\016\022\tSTYLE2024\030\351\007\242\001\016\022\tSTYLE2026\030\352\007\262\001\003\010\351\007' + _globals['_FEATURESET'].fields_by_name['default_symbol_visibility']._loaded_options = None + _globals['_FEATURESET'].fields_by_name['default_symbol_visibility']._serialized_options = b'\210\001\002\230\001\001\242\001\017\022\nEXPORT_ALL\030\204\007\242\001\025\022\020EXPORT_TOP_LEVEL\030\351\007\242\001\013\022\006STRICT\030\352\007\262\001\003\010\351\007' + _globals['_FEATURESET'].fields_by_name['enforce_proto_limits']._loaded_options = None + _globals['_FEATURESET'].fields_by_name['enforce_proto_limits']._serialized_options = b'\210\001\002\230\001\006\230\001\003\230\001\004\230\001\005\242\001\036\022\031LEGACY_NO_EXPLICIT_LIMITS\030\204\007\242\001\025\022\020PROTO_LIMITS2026\030\352\007\262\001\003\010\352\007' + _globals['_SOURCECODEINFO_LOCATION'].fields_by_name['path']._loaded_options = None + _globals['_SOURCECODEINFO_LOCATION'].fields_by_name['path']._serialized_options = b'\020\001' + _globals['_SOURCECODEINFO_LOCATION'].fields_by_name['span']._loaded_options = None + _globals['_SOURCECODEINFO_LOCATION'].fields_by_name['span']._serialized_options = b'\020\001' + _globals['_GENERATEDCODEINFO_ANNOTATION'].fields_by_name['path']._loaded_options = None + _globals['_GENERATEDCODEINFO_ANNOTATION'].fields_by_name['path']._serialized_options = b'\020\001' + _globals['_EDITION']._serialized_start=13504 + _globals['_EDITION']._serialized_end=13841 + _globals['_SYMBOLVISIBILITY']._serialized_start=13843 + _globals['_SYMBOLVISIBILITY']._serialized_end=13928 + _globals['_FILEDESCRIPTORSET']._serialized_start=53 + _globals['_FILEDESCRIPTORSET']._serialized_end=144 + _globals['_FILEDESCRIPTORPROTO']._serialized_start=147 + _globals['_FILEDESCRIPTORPROTO']._serialized_end=856 + _globals['_DESCRIPTORPROTO']._serialized_start=859 + _globals['_DESCRIPTORPROTO']._serialized_end=1751 + _globals['_DESCRIPTORPROTO_EXTENSIONRANGE']._serialized_start=1572 + _globals['_DESCRIPTORPROTO_EXTENSIONRANGE']._serialized_end=1694 + _globals['_DESCRIPTORPROTO_RESERVEDRANGE']._serialized_start=1696 + _globals['_DESCRIPTORPROTO_RESERVEDRANGE']._serialized_end=1751 + _globals['_EXTENSIONRANGEOPTIONS']._serialized_start=1754 + _globals['_EXTENSIONRANGEOPTIONS']._serialized_end=2350 + _globals['_EXTENSIONRANGEOPTIONS_DECLARATION']._serialized_start=2129 + _globals['_EXTENSIONRANGEOPTIONS_DECLARATION']._serialized_end=2277 + _globals['_EXTENSIONRANGEOPTIONS_VERIFICATIONSTATE']._serialized_start=2279 + _globals['_EXTENSIONRANGEOPTIONS_VERIFICATIONSTATE']._serialized_end=2331 + _globals['_FIELDDESCRIPTORPROTO']._serialized_start=2353 + _globals['_FIELDDESCRIPTORPROTO']._serialized_end=3186 + _globals['_FIELDDESCRIPTORPROTO_TYPE']._serialized_start=2807 + _globals['_FIELDDESCRIPTORPROTO_TYPE']._serialized_end=3117 + _globals['_FIELDDESCRIPTORPROTO_LABEL']._serialized_start=3119 + _globals['_FIELDDESCRIPTORPROTO_LABEL']._serialized_end=3186 + _globals['_ONEOFDESCRIPTORPROTO']._serialized_start=3188 + _globals['_ONEOFDESCRIPTORPROTO']._serialized_end=3287 + _globals['_ENUMDESCRIPTORPROTO']._serialized_start=3290 + _globals['_ENUMDESCRIPTORPROTO']._serialized_end=3712 + _globals['_ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE']._serialized_start=3653 + _globals['_ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE']._serialized_end=3712 + _globals['_ENUMVALUEDESCRIPTORPROTO']._serialized_start=3715 + _globals['_ENUMVALUEDESCRIPTORPROTO']._serialized_end=3846 + _globals['_SERVICEDESCRIPTORPROTO']._serialized_start=3849 + _globals['_SERVICEDESCRIPTORPROTO']._serialized_end=4030 + _globals['_METHODDESCRIPTORPROTO']._serialized_start=4033 + _globals['_METHODDESCRIPTORPROTO']._serialized_end=4298 + _globals['_FILEOPTIONS']._serialized_start=4301 + _globals['_FILEOPTIONS']._serialized_end=5849 + _globals['_FILEOPTIONS_OPTIMIZEMODE']._serialized_start=5738 + _globals['_FILEOPTIONS_OPTIMIZEMODE']._serialized_end=5796 + _globals['_MESSAGEOPTIONS']._serialized_start=5852 + _globals['_MESSAGEOPTIONS']._serialized_end=6360 + _globals['_FIELDOPTIONS']._serialized_start=6363 + _globals['_FIELDOPTIONS']._serialized_end=8105 + _globals['_FIELDOPTIONS_EDITIONDEFAULT']._serialized_start=7204 + _globals['_FIELDOPTIONS_EDITIONDEFAULT']._serialized_end=7294 + _globals['_FIELDOPTIONS_FEATURESUPPORT']._serialized_start=7297 + _globals['_FIELDOPTIONS_FEATURESUPPORT']._serialized_end=7612 + _globals['_FIELDOPTIONS_CTYPE']._serialized_start=7614 + _globals['_FIELDOPTIONS_CTYPE']._serialized_end=7661 + _globals['_FIELDOPTIONS_JSTYPE']._serialized_start=7663 + _globals['_FIELDOPTIONS_JSTYPE']._serialized_end=7716 + _globals['_FIELDOPTIONS_OPTIONRETENTION']._serialized_start=7718 + _globals['_FIELDOPTIONS_OPTIONRETENTION']._serialized_end=7803 + _globals['_FIELDOPTIONS_OPTIONTARGETTYPE']._serialized_start=7806 + _globals['_FIELDOPTIONS_OPTIONTARGETTYPE']._serialized_end=8074 + _globals['_ONEOFOPTIONS']._serialized_start=8108 + _globals['_ONEOFOPTIONS']._serialized_end=8288 + _globals['_ENUMOPTIONS']._serialized_start=8291 + _globals['_ENUMOPTIONS']._serialized_end=8636 + _globals['_ENUMVALUEOPTIONS']._serialized_start=8639 + _globals['_ENUMVALUEOPTIONS']._serialized_end=8991 + _globals['_SERVICEOPTIONS']._serialized_start=8994 + _globals['_SERVICEOPTIONS']._serialized_end=9215 + _globals['_METHODOPTIONS']._serialized_start=9218 + _globals['_METHODOPTIONS']._serialized_end=9635 + _globals['_METHODOPTIONS_IDEMPOTENCYLEVEL']._serialized_start=9536 + _globals['_METHODOPTIONS_IDEMPOTENCYLEVEL']._serialized_end=9616 + _globals['_UNINTERPRETEDOPTION']._serialized_start=9638 + _globals['_UNINTERPRETEDOPTION']._serialized_end=10048 + _globals['_UNINTERPRETEDOPTION_NAMEPART']._serialized_start=9974 + _globals['_UNINTERPRETEDOPTION_NAMEPART']._serialized_end=10048 + _globals['_FEATURESET']._serialized_start=10051 + _globals['_FEATURESET']._serialized_end=12352 + _globals['_FEATURESET_VISIBILITYFEATURE']._serialized_start=11459 + _globals['_FEATURESET_VISIBILITYFEATURE']._serialized_end=11620 + _globals['_FEATURESET_VISIBILITYFEATURE_DEFAULTSYMBOLVISIBILITY']._serialized_start=11481 + _globals['_FEATURESET_VISIBILITYFEATURE_DEFAULTSYMBOLVISIBILITY']._serialized_end=11610 + _globals['_FEATURESET_PROTOLIMITSFEATURE']._serialized_start=11622 + _globals['_FEATURESET_PROTOLIMITSFEATURE']._serialized_end=11743 + _globals['_FEATURESET_PROTOLIMITSFEATURE_ENFORCEPROTOLIMITS']._serialized_start=11644 + _globals['_FEATURESET_PROTOLIMITSFEATURE_ENFORCEPROTOLIMITS']._serialized_end=11743 + _globals['_FEATURESET_FIELDPRESENCE']._serialized_start=11745 + _globals['_FEATURESET_FIELDPRESENCE']._serialized_end=11837 + _globals['_FEATURESET_ENUMTYPE']._serialized_start=11839 + _globals['_FEATURESET_ENUMTYPE']._serialized_end=11894 + _globals['_FEATURESET_REPEATEDFIELDENCODING']._serialized_start=11896 + _globals['_FEATURESET_REPEATEDFIELDENCODING']._serialized_end=11982 + _globals['_FEATURESET_UTF8VALIDATION']._serialized_start=11984 + _globals['_FEATURESET_UTF8VALIDATION']._serialized_end=12057 + _globals['_FEATURESET_MESSAGEENCODING']._serialized_start=12059 + _globals['_FEATURESET_MESSAGEENCODING']._serialized_end=12142 + _globals['_FEATURESET_JSONFORMAT']._serialized_start=12144 + _globals['_FEATURESET_JSONFORMAT']._serialized_end=12216 + _globals['_FEATURESET_ENFORCENAMINGSTYLE']._serialized_start=12218 + _globals['_FEATURESET_ENFORCENAMINGSTYLE']._serialized_end=12320 + _globals['_FEATURESETDEFAULTS']._serialized_start=12355 + _globals['_FEATURESETDEFAULTS']._serialized_end=12850 + _globals['_FEATURESETDEFAULTS_FEATURESETEDITIONDEFAULT']._serialized_start=12602 + _globals['_FEATURESETDEFAULTS_FEATURESETEDITIONDEFAULT']._serialized_end=12850 + _globals['_SOURCECODEINFO']._serialized_start=12853 + _globals['_SOURCECODEINFO']._serialized_end=13162 + _globals['_SOURCECODEINFO_LOCATION']._serialized_start=12942 + _globals['_SOURCECODEINFO_LOCATION']._serialized_end=13148 + _globals['_GENERATEDCODEINFO']._serialized_start=13165 + _globals['_GENERATEDCODEINFO']._serialized_end=13501 + _globals['_GENERATEDCODEINFO_ANNOTATION']._serialized_start=13266 + _globals['_GENERATEDCODEINFO_ANNOTATION']._serialized_end=13501 + _globals['_GENERATEDCODEINFO_ANNOTATION_SEMANTIC']._serialized_start=13461 + _globals['_GENERATEDCODEINFO_ANNOTATION_SEMANTIC']._serialized_end=13501 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/descriptor_pool.py b/venv/lib/python3.12/site-packages/google/protobuf/descriptor_pool.py new file mode 100755 index 0000000..cd441e7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/descriptor_pool.py @@ -0,0 +1,1468 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Provides DescriptorPool to use as a container for proto2 descriptors. + +The DescriptorPool is used in conjection with a DescriptorDatabase to maintain +a collection of protocol buffer descriptors for use when dynamically creating +message types at runtime. + +For most applications protocol buffers should be used via modules generated by +the protocol buffer compiler tool. This should only be used when the type of +protocol buffers used in an application or library cannot be predetermined. + +Below is a straightforward example on how to use this class:: + + pool = DescriptorPool() + file_descriptor_protos = [ ... ] + for file_descriptor_proto in file_descriptor_protos: + pool.Add(file_descriptor_proto) + my_message_descriptor = pool.FindMessageTypeByName('some.package.MessageType') + +The message descriptor can be used in conjunction with the message_factory +module in order to create a protocol buffer class that can be encoded and +decoded. + +If you want to get a Python class for the specified proto, use the +helper functions inside google.protobuf.message_factory +directly instead of this class. +""" + +__author__ = 'matthewtoia@google.com (Matt Toia)' + +import collections +import threading +import warnings + +from google.protobuf import descriptor +from google.protobuf import descriptor_database +from google.protobuf import text_encoding +from google.protobuf.internal import python_edition_defaults +from google.protobuf.internal import python_message + +_USE_C_DESCRIPTORS = descriptor._USE_C_DESCRIPTORS # pylint: disable=protected-access + + +def _NormalizeFullyQualifiedName(name): + """Remove leading period from fully-qualified type name. + + Due to b/13860351 in descriptor_database.py, types in the root namespace are + generated with a leading period. This function removes that prefix. + + Args: + name (str): The fully-qualified symbol name. + + Returns: + str: The normalized fully-qualified symbol name. + """ + return name.lstrip('.') + + +def _OptionsOrNone(descriptor_proto): + """Returns the value of the field `options`, or None if it is not set.""" + if descriptor_proto.HasField('options'): + return descriptor_proto.options + else: + return None + + +def _IsMessageSetExtension(field): + return ( + field.is_extension + and field.containing_type.has_options + and field.containing_type.GetOptions().message_set_wire_format + and field.type == descriptor.FieldDescriptor.TYPE_MESSAGE + and not field.is_required + and not field.is_repeated + ) + + +_edition_defaults_lock = threading.Lock() + + +class DescriptorPool(object): + """A collection of protobufs dynamically constructed by descriptor protos.""" + + if _USE_C_DESCRIPTORS: + + def __new__(cls, descriptor_db=None): + # pylint: disable=protected-access + return descriptor._message.DescriptorPool(descriptor_db) + + def __init__( + self, descriptor_db=None, use_deprecated_legacy_json_field_conflicts=False + ): + """Initializes a Pool of proto buffs. + + The descriptor_db argument to the constructor is provided to allow + specialized file descriptor proto lookup code to be triggered on demand. An + example would be an implementation which will read and compile a file + specified in a call to FindFileByName() and not require the call to Add() + at all. Results from this database will be cached internally here as well. + + Args: + descriptor_db: A secondary source of file descriptors. + use_deprecated_legacy_json_field_conflicts: Unused, for compatibility with + C++. + """ + + self._internal_db = descriptor_database.DescriptorDatabase() + self._descriptor_db = descriptor_db + self._descriptors = {} + self._enum_descriptors = {} + self._service_descriptors = {} + self._file_descriptors = {} + self._toplevel_extensions = {} + self._top_enum_values = {} + # We store extensions in two two-level mappings: The first key is the + # descriptor of the message being extended, the second key is the extension + # full name or its tag number. + self._extensions_by_name = collections.defaultdict(dict) + self._extensions_by_number = collections.defaultdict(dict) + self._serialized_edition_defaults = ( + python_edition_defaults._PROTOBUF_INTERNAL_PYTHON_EDITION_DEFAULTS + ) + self._edition_defaults = None + self._feature_cache = dict() + + def _CheckConflictRegister(self, desc, desc_name, file_name): + """Check if the descriptor name conflicts with another of the same name. + + Args: + desc: Descriptor of a message, enum, service, extension or enum value. + desc_name (str): the full name of desc. + file_name (str): The file name of descriptor. + """ + for register, descriptor_type in [ + (self._descriptors, descriptor.Descriptor), + (self._enum_descriptors, descriptor.EnumDescriptor), + (self._service_descriptors, descriptor.ServiceDescriptor), + (self._toplevel_extensions, descriptor.FieldDescriptor), + (self._top_enum_values, descriptor.EnumValueDescriptor), + ]: + if desc_name in register: + old_desc = register[desc_name] + if isinstance(old_desc, descriptor.EnumValueDescriptor): + old_file = old_desc.type.file.name + else: + old_file = old_desc.file.name + + if not isinstance(desc, descriptor_type) or (old_file != file_name): + error_msg = ( + 'Conflict register for file "' + + file_name + + '": ' + + desc_name + + ' is already defined in file "' + + old_file + + '". Please fix the conflict by adding ' + 'package name on the proto file, or use different ' + 'name for the duplication.' + ) + if isinstance(desc, descriptor.EnumValueDescriptor): + error_msg += ( + '\nNote: enum values appear as ' + 'siblings of the enum type instead of ' + 'children of it.' + ) + + raise TypeError(error_msg) + + return + + def Add(self, file_desc_proto): + """Adds the FileDescriptorProto and its types to this pool. + + Args: + file_desc_proto (FileDescriptorProto): The file descriptor to add. + """ + + self._internal_db.Add(file_desc_proto) + + def AddSerializedFile(self, serialized_file_desc_proto): + """Adds the FileDescriptorProto and its types to this pool. + + Args: + serialized_file_desc_proto (bytes): A bytes string, serialization of the + :class:`FileDescriptorProto` to add. + + Returns: + FileDescriptor: Descriptor for the added file. + """ + + # pylint: disable=g-import-not-at-top + from google.protobuf import descriptor_pb2 + + file_desc_proto = descriptor_pb2.FileDescriptorProto.FromString( + serialized_file_desc_proto + ) + file_desc = self._ConvertFileProtoToFileDescriptor(file_desc_proto) + file_desc.serialized_pb = serialized_file_desc_proto + return file_desc + + # Never call this method. It is for internal usage only. + def _AddDescriptor(self, desc): + """Adds a Descriptor to the pool, non-recursively. + + If the Descriptor contains nested messages or enums, the caller must + explicitly register them. This method also registers the FileDescriptor + associated with the message. + + Args: + desc: A Descriptor. + """ + if not isinstance(desc, descriptor.Descriptor): + raise TypeError('Expected instance of descriptor.Descriptor.') + + self._CheckConflictRegister(desc, desc.full_name, desc.file.name) + + self._descriptors[desc.full_name] = desc + self._AddFileDescriptor(desc.file) + + # Never call this method. It is for internal usage only. + def _AddEnumDescriptor(self, enum_desc): + """Adds an EnumDescriptor to the pool. + + This method also registers the FileDescriptor associated with the enum. + + Args: + enum_desc: An EnumDescriptor. + """ + + if not isinstance(enum_desc, descriptor.EnumDescriptor): + raise TypeError('Expected instance of descriptor.EnumDescriptor.') + + file_name = enum_desc.file.name + self._CheckConflictRegister(enum_desc, enum_desc.full_name, file_name) + self._enum_descriptors[enum_desc.full_name] = enum_desc + + # Top enum values need to be indexed. + # Count the number of dots to see whether the enum is toplevel or nested + # in a message. We cannot use enum_desc.containing_type at this stage. + if enum_desc.file.package: + top_level = ( + enum_desc.full_name.count('.') - enum_desc.file.package.count('.') + == 1 + ) + else: + top_level = enum_desc.full_name.count('.') == 0 + if top_level: + file_name = enum_desc.file.name + package = enum_desc.file.package + for enum_value in enum_desc.values: + full_name = _NormalizeFullyQualifiedName( + '.'.join((package, enum_value.name)) + ) + self._CheckConflictRegister(enum_value, full_name, file_name) + self._top_enum_values[full_name] = enum_value + self._AddFileDescriptor(enum_desc.file) + + # Never call this method. It is for internal usage only. + def _AddServiceDescriptor(self, service_desc): + """Adds a ServiceDescriptor to the pool. + + Args: + service_desc: A ServiceDescriptor. + """ + + if not isinstance(service_desc, descriptor.ServiceDescriptor): + raise TypeError('Expected instance of descriptor.ServiceDescriptor.') + + self._CheckConflictRegister( + service_desc, service_desc.full_name, service_desc.file.name + ) + self._service_descriptors[service_desc.full_name] = service_desc + + # Never call this method. It is for internal usage only. + def _AddExtensionDescriptor(self, extension): + """Adds a FieldDescriptor describing an extension to the pool. + + Args: + extension: A FieldDescriptor. + + Raises: + AssertionError: when another extension with the same number extends the + same message. + TypeError: when the specified extension is not a + descriptor.FieldDescriptor. + """ + if not ( + isinstance(extension, descriptor.FieldDescriptor) + and extension.is_extension + ): + raise TypeError('Expected an extension descriptor.') + + if extension.extension_scope is None: + self._CheckConflictRegister( + extension, extension.full_name, extension.file.name + ) + self._toplevel_extensions[extension.full_name] = extension + + try: + existing_desc = self._extensions_by_number[extension.containing_type][ + extension.number + ] + except KeyError: + pass + else: + if extension is not existing_desc: + raise AssertionError( + 'Extensions "%s" and "%s" both try to extend message type "%s" ' + 'with field number %d.' + % ( + extension.full_name, + existing_desc.full_name, + extension.containing_type.full_name, + extension.number, + ) + ) + + self._extensions_by_number[extension.containing_type][ + extension.number + ] = extension + self._extensions_by_name[extension.containing_type][ + extension.full_name + ] = extension + + # Also register MessageSet extensions with the type name. + if _IsMessageSetExtension(extension): + self._extensions_by_name[extension.containing_type][ + extension.message_type.full_name + ] = extension + + if hasattr(extension.containing_type, '_concrete_class'): + python_message._AttachFieldHelpers( + extension.containing_type._concrete_class, extension + ) + + # Never call this method. It is for internal usage only. + def _InternalAddFileDescriptor(self, file_desc): + """Adds a FileDescriptor to the pool, non-recursively. + + If the FileDescriptor contains messages or enums, the caller must explicitly + register them. + + Args: + file_desc: A FileDescriptor. + """ + + self._AddFileDescriptor(file_desc) + + def _AddFileDescriptor(self, file_desc): + """Adds a FileDescriptor to the pool, non-recursively. + + If the FileDescriptor contains messages or enums, the caller must explicitly + register them. + + Args: + file_desc: A FileDescriptor. + """ + + if not isinstance(file_desc, descriptor.FileDescriptor): + raise TypeError('Expected instance of descriptor.FileDescriptor.') + self._file_descriptors[file_desc.name] = file_desc + + def FindFileByName(self, file_name): + """Gets a FileDescriptor by file name. + + Args: + file_name (str): The path to the file to get a descriptor for. + + Returns: + FileDescriptor: The descriptor for the named file. + + Raises: + KeyError: if the file cannot be found in the pool. + """ + + try: + return self._file_descriptors[file_name] + except KeyError: + pass + + try: + file_proto = self._internal_db.FindFileByName(file_name) + except KeyError as error: + if self._descriptor_db: + file_proto = self._descriptor_db.FindFileByName(file_name) + else: + raise error + if not file_proto: + raise KeyError('Cannot find a file named %s' % file_name) + return self._ConvertFileProtoToFileDescriptor(file_proto) + + def FindFileContainingSymbol(self, symbol): + """Gets the FileDescriptor for the file containing the specified symbol. + + Args: + symbol (str): The name of the symbol to search for. + + Returns: + FileDescriptor: Descriptor for the file that contains the specified + symbol. + + Raises: + KeyError: if the file cannot be found in the pool. + """ + + symbol = _NormalizeFullyQualifiedName(symbol) + try: + return self._InternalFindFileContainingSymbol(symbol) + except KeyError: + pass + + try: + # Try fallback database. Build and find again if possible. + self._FindFileContainingSymbolInDb(symbol) + return self._InternalFindFileContainingSymbol(symbol) + except KeyError: + raise KeyError('Cannot find a file containing %s' % symbol) + + def _InternalFindFileContainingSymbol(self, symbol): + """Gets the already built FileDescriptor containing the specified symbol. + + Args: + symbol (str): The name of the symbol to search for. + + Returns: + FileDescriptor: Descriptor for the file that contains the specified + symbol. + + Raises: + KeyError: if the file cannot be found in the pool. + """ + try: + return self._descriptors[symbol].file + except KeyError: + pass + + try: + return self._enum_descriptors[symbol].file + except KeyError: + pass + + try: + return self._service_descriptors[symbol].file + except KeyError: + pass + + try: + return self._top_enum_values[symbol].type.file + except KeyError: + pass + + try: + return self._toplevel_extensions[symbol].file + except KeyError: + pass + + # Try fields, enum values and nested extensions inside a message. + top_name, _, sub_name = symbol.rpartition('.') + try: + message = self.FindMessageTypeByName(top_name) + assert ( + sub_name in message.extensions_by_name + or sub_name in message.fields_by_name + or sub_name in message.enum_values_by_name + ) + return message.file + except (KeyError, AssertionError): + raise KeyError('Cannot find a file containing %s' % symbol) + + def FindMessageTypeByName(self, full_name): + """Loads the named descriptor from the pool. + + Args: + full_name (str): The full name of the descriptor to load. + + Returns: + Descriptor: The descriptor for the named type. + + Raises: + KeyError: if the message cannot be found in the pool. + """ + + full_name = _NormalizeFullyQualifiedName(full_name) + if full_name not in self._descriptors: + self._FindFileContainingSymbolInDb(full_name) + return self._descriptors[full_name] + + def FindEnumTypeByName(self, full_name): + """Loads the named enum descriptor from the pool. + + Args: + full_name (str): The full name of the enum descriptor to load. + + Returns: + EnumDescriptor: The enum descriptor for the named type. + + Raises: + KeyError: if the enum cannot be found in the pool. + """ + + full_name = _NormalizeFullyQualifiedName(full_name) + if full_name not in self._enum_descriptors: + self._FindFileContainingSymbolInDb(full_name) + return self._enum_descriptors[full_name] + + def FindFieldByName(self, full_name): + """Loads the named field descriptor from the pool. + + Args: + full_name (str): The full name of the field descriptor to load. + + Returns: + FieldDescriptor: The field descriptor for the named field. + + Raises: + KeyError: if the field cannot be found in the pool. + """ + full_name = _NormalizeFullyQualifiedName(full_name) + message_name, _, field_name = full_name.rpartition('.') + message_descriptor = self.FindMessageTypeByName(message_name) + return message_descriptor.fields_by_name[field_name] + + def FindOneofByName(self, full_name): + """Loads the named oneof descriptor from the pool. + + Args: + full_name (str): The full name of the oneof descriptor to load. + + Returns: + OneofDescriptor: The oneof descriptor for the named oneof. + + Raises: + KeyError: if the oneof cannot be found in the pool. + """ + full_name = _NormalizeFullyQualifiedName(full_name) + message_name, _, oneof_name = full_name.rpartition('.') + message_descriptor = self.FindMessageTypeByName(message_name) + return message_descriptor.oneofs_by_name[oneof_name] + + def FindExtensionByName(self, full_name): + """Loads the named extension descriptor from the pool. + + Args: + full_name (str): The full name of the extension descriptor to load. + + Returns: + FieldDescriptor: The field descriptor for the named extension. + + Raises: + KeyError: if the extension cannot be found in the pool. + """ + full_name = _NormalizeFullyQualifiedName(full_name) + try: + # The proto compiler does not give any link between the FileDescriptor + # and top-level extensions unless the FileDescriptorProto is added to + # the DescriptorDatabase, but this can impact memory usage. + # So we registered these extensions by name explicitly. + return self._toplevel_extensions[full_name] + except KeyError: + pass + message_name, _, extension_name = full_name.rpartition('.') + try: + # Most extensions are nested inside a message. + scope = self.FindMessageTypeByName(message_name) + except KeyError: + # Some extensions are defined at file scope. + scope = self._FindFileContainingSymbolInDb(full_name) + return scope.extensions_by_name[extension_name] + + def FindExtensionByNumber(self, message_descriptor, number): + """Gets the extension of the specified message with the specified number. + + Extensions have to be registered to this pool by calling :func:`Add` or + :func:`AddExtensionDescriptor`. + + Args: + message_descriptor (Descriptor): descriptor of the extended message. + number (int): Number of the extension field. + + Returns: + FieldDescriptor: The descriptor for the extension. + + Raises: + KeyError: when no extension with the given number is known for the + specified message. + """ + try: + return self._extensions_by_number[message_descriptor][number] + except KeyError: + self._TryLoadExtensionFromDB(message_descriptor, number) + return self._extensions_by_number[message_descriptor][number] + + def FindAllExtensions(self, message_descriptor): + """Gets all the known extensions of a given message. + + Extensions have to be registered to this pool by build related + :func:`Add` or :func:`AddExtensionDescriptor`. + + Args: + message_descriptor (Descriptor): Descriptor of the extended message. + + Returns: + list[FieldDescriptor]: Field descriptors describing the extensions. + """ + # Fallback to descriptor db if FindAllExtensionNumbers is provided. + if self._descriptor_db and hasattr( + self._descriptor_db, 'FindAllExtensionNumbers' + ): + full_name = message_descriptor.full_name + try: + all_numbers = self._descriptor_db.FindAllExtensionNumbers(full_name) + except: + pass + else: + if isinstance(all_numbers, list): + for number in all_numbers: + if number in self._extensions_by_number[message_descriptor]: + continue + self._TryLoadExtensionFromDB(message_descriptor, number) + else: + warnings.warn( + 'FindAllExtensionNumbers() on fall back DB must return a list,' + ' not {0}'.format(type(all_numbers)) + ) + + return list(self._extensions_by_number[message_descriptor].values()) + + def _TryLoadExtensionFromDB(self, message_descriptor, number): + """Try to Load extensions from descriptor db. + + Args: + message_descriptor: descriptor of the extended message. + number: the extension number that needs to be loaded. + """ + if not self._descriptor_db: + return + # Only supported when FindFileContainingExtension is provided. + if not hasattr(self._descriptor_db, 'FindFileContainingExtension'): + return + + full_name = message_descriptor.full_name + file_proto = None + try: + file_proto = self._descriptor_db.FindFileContainingExtension( + full_name, number + ) + except: + return + + if file_proto is None: + return + + try: + self._ConvertFileProtoToFileDescriptor(file_proto) + except: + warn_msg = 'Unable to load proto file %s for extension number %d.' % ( + file_proto.name, + number, + ) + warnings.warn(warn_msg, RuntimeWarning) + + def FindServiceByName(self, full_name): + """Loads the named service descriptor from the pool. + + Args: + full_name (str): The full name of the service descriptor to load. + + Returns: + ServiceDescriptor: The service descriptor for the named service. + + Raises: + KeyError: if the service cannot be found in the pool. + """ + full_name = _NormalizeFullyQualifiedName(full_name) + if full_name not in self._service_descriptors: + self._FindFileContainingSymbolInDb(full_name) + return self._service_descriptors[full_name] + + def FindMethodByName(self, full_name): + """Loads the named service method descriptor from the pool. + + Args: + full_name (str): The full name of the method descriptor to load. + + Returns: + MethodDescriptor: The method descriptor for the service method. + + Raises: + KeyError: if the method cannot be found in the pool. + """ + full_name = _NormalizeFullyQualifiedName(full_name) + service_name, _, method_name = full_name.rpartition('.') + service_descriptor = self.FindServiceByName(service_name) + return service_descriptor.methods_by_name[method_name] + + def SetFeatureSetDefaults(self, defaults): + """Sets the default feature mappings used during the build. + + Args: + defaults: a FeatureSetDefaults message containing the new mappings. + """ + if self._edition_defaults is not None: + raise ValueError( + "Feature set defaults can't be changed once the pool has started" + ' building!' + ) + + # pylint: disable=g-import-not-at-top + from google.protobuf import descriptor_pb2 + + if not isinstance(defaults, descriptor_pb2.FeatureSetDefaults): + raise TypeError('SetFeatureSetDefaults called with invalid type') + + if defaults.minimum_edition > defaults.maximum_edition: + raise ValueError( + 'Invalid edition range %s to %s' + % ( + descriptor_pb2.Edition.Name(defaults.minimum_edition), + descriptor_pb2.Edition.Name(defaults.maximum_edition), + ) + ) + + prev_edition = descriptor_pb2.Edition.EDITION_UNKNOWN + for d in defaults.defaults: + if d.edition == descriptor_pb2.Edition.EDITION_UNKNOWN: + raise ValueError('Invalid edition EDITION_UNKNOWN specified') + if prev_edition >= d.edition: + raise ValueError( + 'Feature set defaults are not strictly increasing. %s is greater' + ' than or equal to %s' + % ( + descriptor_pb2.Edition.Name(prev_edition), + descriptor_pb2.Edition.Name(d.edition), + ) + ) + prev_edition = d.edition + self._edition_defaults = defaults + + def _CreateDefaultFeatures(self, edition): + """Creates a FeatureSet message with defaults for a specific edition. + + Args: + edition: the edition to generate defaults for. + + Returns: + A FeatureSet message with defaults for a specific edition. + """ + # pylint: disable=g-import-not-at-top + from google.protobuf import descriptor_pb2 + + with _edition_defaults_lock: + if not self._edition_defaults: + self._edition_defaults = descriptor_pb2.FeatureSetDefaults() + self._edition_defaults.ParseFromString( + self._serialized_edition_defaults + ) + + if edition < self._edition_defaults.minimum_edition: + raise TypeError( + 'Edition %s is earlier than the minimum supported edition %s!' + % ( + descriptor_pb2.Edition.Name(edition), + descriptor_pb2.Edition.Name( + self._edition_defaults.minimum_edition + ), + ) + ) + if ( + edition > self._edition_defaults.maximum_edition + and edition != descriptor_pb2.EDITION_UNSTABLE + ): + raise TypeError( + 'Edition %s is later than the maximum supported edition %s!' + % ( + descriptor_pb2.Edition.Name(edition), + descriptor_pb2.Edition.Name( + self._edition_defaults.maximum_edition + ), + ) + ) + found = None + for d in self._edition_defaults.defaults: + if d.edition > edition: + break + found = d + if found is None: + raise TypeError( + 'No valid default found for edition %s!' + % descriptor_pb2.Edition.Name(edition) + ) + + defaults = descriptor_pb2.FeatureSet() + defaults.CopyFrom(found.fixed_features) + defaults.MergeFrom(found.overridable_features) + return defaults + + def _InternFeatures(self, features): + serialized = features.SerializeToString() + with _edition_defaults_lock: + cached = self._feature_cache.get(serialized) + if cached is None: + self._feature_cache[serialized] = features + cached = features + return cached + + def _FindFileContainingSymbolInDb(self, symbol): + """Finds the file in descriptor DB containing the specified symbol. + + Args: + symbol (str): The name of the symbol to search for. + + Returns: + FileDescriptor: The file that contains the specified symbol. + + Raises: + KeyError: if the file cannot be found in the descriptor database. + """ + try: + file_proto = self._internal_db.FindFileContainingSymbol(symbol) + except KeyError as error: + if self._descriptor_db: + file_proto = self._descriptor_db.FindFileContainingSymbol(symbol) + else: + raise error + if not file_proto: + raise KeyError('Cannot find a file containing %s' % symbol) + return self._ConvertFileProtoToFileDescriptor(file_proto) + + def _ConvertFileProtoToFileDescriptor(self, file_proto): + """Creates a FileDescriptor from a proto or returns a cached copy. + + This method also has the side effect of loading all the symbols found in + the file into the appropriate dictionaries in the pool. + + Args: + file_proto: The proto to convert. + + Returns: + A FileDescriptor matching the passed in proto. + """ + if file_proto.name not in self._file_descriptors: + built_deps = list(self._GetDeps(file_proto.dependency)) + direct_deps = [self.FindFileByName(n) for n in file_proto.dependency] + public_deps = [direct_deps[i] for i in file_proto.public_dependency] + + # pylint: disable=g-import-not-at-top + from google.protobuf import descriptor_pb2 + + file_descriptor = descriptor.FileDescriptor( + pool=self, + name=file_proto.name, + package=file_proto.package, + syntax=file_proto.syntax, + edition=descriptor_pb2.Edition.Name(file_proto.edition), + options=_OptionsOrNone(file_proto), + serialized_pb=file_proto.SerializeToString(), + dependencies=direct_deps, + public_dependencies=public_deps, + # pylint: disable=protected-access + create_key=descriptor._internal_create_key, + ) + scope = {} + + # This loop extracts all the message and enum types from all the + # dependencies of the file_proto. This is necessary to create the + # scope of available message types when defining the passed in + # file proto. + for dependency in built_deps: + scope.update( + self._ExtractSymbols(dependency.message_types_by_name.values()) + ) + scope.update( + (_PrefixWithDot(enum.full_name), enum) + for enum in dependency.enum_types_by_name.values() + ) + + for message_type in file_proto.message_type: + message_desc = self._ConvertMessageDescriptor( + message_type, + file_proto.package, + file_descriptor, + scope, + file_proto.syntax, + ) + file_descriptor.message_types_by_name[message_desc.name] = message_desc + + for enum_type in file_proto.enum_type: + file_descriptor.enum_types_by_name[enum_type.name] = ( + self._ConvertEnumDescriptor( + enum_type, + file_proto.package, + file_descriptor, + None, + scope, + True, + ) + ) + + for index, extension_proto in enumerate(file_proto.extension): + extension_desc = self._MakeFieldDescriptor( + extension_proto, + file_proto.package, + index, + file_descriptor, + is_extension=True, + ) + extension_desc.containing_type = self._GetTypeFromScope( + file_descriptor.package, extension_proto.extendee, scope + ) + self._SetFieldType( + extension_proto, extension_desc, file_descriptor.package, scope + ) + file_descriptor.extensions_by_name[extension_desc.name] = extension_desc + + for desc_proto in file_proto.message_type: + self._SetAllFieldTypes(file_proto.package, desc_proto, scope) + + if file_proto.package: + desc_proto_prefix = _PrefixWithDot(file_proto.package) + else: + desc_proto_prefix = '' + + for desc_proto in file_proto.message_type: + desc = self._GetTypeFromScope(desc_proto_prefix, desc_proto.name, scope) + file_descriptor.message_types_by_name[desc_proto.name] = desc + + for index, service_proto in enumerate(file_proto.service): + file_descriptor.services_by_name[service_proto.name] = ( + self._MakeServiceDescriptor( + service_proto, index, scope, file_proto.package, file_descriptor + ) + ) + + self._file_descriptors[file_proto.name] = file_descriptor + + # Add extensions to the pool + def AddExtensionForNested(message_type): + for nested in message_type.nested_types: + AddExtensionForNested(nested) + for extension in message_type.extensions: + self._AddExtensionDescriptor(extension) + + file_desc = self._file_descriptors[file_proto.name] + for extension in file_desc.extensions_by_name.values(): + self._AddExtensionDescriptor(extension) + for message_type in file_desc.message_types_by_name.values(): + AddExtensionForNested(message_type) + + return file_desc + + def _ConvertMessageDescriptor( + self, desc_proto, package=None, file_desc=None, scope=None, syntax=None + ): + """Adds the proto to the pool in the specified package. + + Args: + desc_proto: The descriptor_pb2.DescriptorProto protobuf message. + package: The package the proto should be located in. + file_desc: The file containing this message. + scope: Dict mapping short and full symbols to message and enum types. + syntax: string indicating syntax of the file ("proto2" or "proto3") + + Returns: + The added descriptor. + """ + + if package: + desc_name = '.'.join((package, desc_proto.name)) + else: + desc_name = desc_proto.name + + if file_desc is None: + file_name = None + else: + file_name = file_desc.name + + if scope is None: + scope = {} + + nested = [ + self._ConvertMessageDescriptor( + nested, desc_name, file_desc, scope, syntax + ) + for nested in desc_proto.nested_type + ] + enums = [ + self._ConvertEnumDescriptor( + enum, desc_name, file_desc, None, scope, False + ) + for enum in desc_proto.enum_type + ] + fields = [ + self._MakeFieldDescriptor(field, desc_name, index, file_desc) + for index, field in enumerate(desc_proto.field) + ] + extensions = [ + self._MakeFieldDescriptor( + extension, desc_name, index, file_desc, is_extension=True + ) + for index, extension in enumerate(desc_proto.extension) + ] + oneofs = [ + # pylint: disable=g-complex-comprehension + descriptor.OneofDescriptor( + desc.name, + '.'.join((desc_name, desc.name)), + index, + None, + [], + _OptionsOrNone(desc), + # pylint: disable=protected-access + create_key=descriptor._internal_create_key, + ) + for index, desc in enumerate(desc_proto.oneof_decl) + ] + extension_ranges = [(r.start, r.end) for r in desc_proto.extension_range] + if extension_ranges: + is_extendable = True + else: + is_extendable = False + desc = descriptor.Descriptor( + name=desc_proto.name, + full_name=desc_name, + filename=file_name, + containing_type=None, + fields=fields, + oneofs=oneofs, + nested_types=nested, + enum_types=enums, + extensions=extensions, + options=_OptionsOrNone(desc_proto), + is_extendable=is_extendable, + extension_ranges=extension_ranges, + file=file_desc, + serialized_start=None, + serialized_end=None, + is_map_entry=desc_proto.options.map_entry, + # pylint: disable=protected-access + create_key=descriptor._internal_create_key, + ) + for nested in desc.nested_types: + nested.containing_type = desc + for enum in desc.enum_types: + enum.containing_type = desc + for field_index, field_desc in enumerate(desc_proto.field): + if field_desc.HasField('oneof_index'): + oneof_index = field_desc.oneof_index + oneofs[oneof_index].fields.append(fields[field_index]) + fields[field_index].containing_oneof = oneofs[oneof_index] + + scope[_PrefixWithDot(desc_name)] = desc + self._CheckConflictRegister(desc, desc.full_name, desc.file.name) + self._descriptors[desc_name] = desc + return desc + + def _ConvertEnumDescriptor( + self, + enum_proto, + package=None, + file_desc=None, + containing_type=None, + scope=None, + top_level=False, + ): + """Make a protobuf EnumDescriptor given an EnumDescriptorProto protobuf. + + Args: + enum_proto: The descriptor_pb2.EnumDescriptorProto protobuf message. + package: Optional package name for the new message EnumDescriptor. + file_desc: The file containing the enum descriptor. + containing_type: The type containing this enum. + scope: Scope containing available types. + top_level: If True, the enum is a top level symbol. If False, the enum is + defined inside a message. + + Returns: + The added descriptor + """ + + if package: + enum_name = '.'.join((package, enum_proto.name)) + else: + enum_name = enum_proto.name + + if file_desc is None: + file_name = None + else: + file_name = file_desc.name + + values = [ + self._MakeEnumValueDescriptor(value, index) + for index, value in enumerate(enum_proto.value) + ] + desc = descriptor.EnumDescriptor( + name=enum_proto.name, + full_name=enum_name, + filename=file_name, + file=file_desc, + values=values, + containing_type=containing_type, + options=_OptionsOrNone(enum_proto), + # pylint: disable=protected-access + create_key=descriptor._internal_create_key, + ) + scope['.%s' % enum_name] = desc + self._CheckConflictRegister(desc, desc.full_name, desc.file.name) + self._enum_descriptors[enum_name] = desc + + # Add top level enum values. + if top_level: + for value in values: + full_name = _NormalizeFullyQualifiedName( + '.'.join((package, value.name)) + ) + self._CheckConflictRegister(value, full_name, file_name) + self._top_enum_values[full_name] = value + + return desc + + def _MakeFieldDescriptor( + self, field_proto, message_name, index, file_desc, is_extension=False + ): + """Creates a field descriptor from a FieldDescriptorProto. + + For message and enum type fields, this method will do a look up + in the pool for the appropriate descriptor for that type. If it + is unavailable, it will fall back to the _source function to + create it. If this type is still unavailable, construction will + fail. + + Args: + field_proto: The proto describing the field. + message_name: The name of the containing message. + index: Index of the field + file_desc: The file containing the field descriptor. + is_extension: Indication that this field is for an extension. + + Returns: + An initialized FieldDescriptor object + """ + + if message_name: + full_name = '.'.join((message_name, field_proto.name)) + else: + full_name = field_proto.name + + if field_proto.json_name: + json_name = field_proto.json_name + else: + json_name = None + + return descriptor.FieldDescriptor( + name=field_proto.name, + full_name=full_name, + index=index, + number=field_proto.number, + type=field_proto.type, + cpp_type=None, + message_type=None, + enum_type=None, + containing_type=None, + label=field_proto.label, + has_default_value=False, + default_value=None, + is_extension=is_extension, + extension_scope=None, + options=_OptionsOrNone(field_proto), + json_name=json_name, + file=file_desc, + # pylint: disable=protected-access + create_key=descriptor._internal_create_key, + ) + + def _SetAllFieldTypes(self, package, desc_proto, scope): + """Sets all the descriptor's fields's types. + + This method also sets the containing types on any extensions. + + Args: + package: The current package of desc_proto. + desc_proto: The message descriptor to update. + scope: Enclosing scope of available types. + """ + + package = _PrefixWithDot(package) + + main_desc = self._GetTypeFromScope(package, desc_proto.name, scope) + + if package == '.': + nested_package = _PrefixWithDot(desc_proto.name) + else: + nested_package = '.'.join([package, desc_proto.name]) + + for field_proto, field_desc in zip(desc_proto.field, main_desc.fields): + self._SetFieldType(field_proto, field_desc, nested_package, scope) + + for extension_proto, extension_desc in zip( + desc_proto.extension, main_desc.extensions + ): + extension_desc.containing_type = self._GetTypeFromScope( + nested_package, extension_proto.extendee, scope + ) + self._SetFieldType(extension_proto, extension_desc, nested_package, scope) + + for nested_type in desc_proto.nested_type: + self._SetAllFieldTypes(nested_package, nested_type, scope) + + def _SetFieldType(self, field_proto, field_desc, package, scope): + """Sets the field's type, cpp_type, message_type and enum_type. + + Args: + field_proto: Data about the field in proto format. + field_desc: The descriptor to modify. + package: The package the field's container is in. + scope: Enclosing scope of available types. + """ + if field_proto.type_name: + desc = self._GetTypeFromScope(package, field_proto.type_name, scope) + else: + desc = None + + if not field_proto.HasField('type'): + if isinstance(desc, descriptor.Descriptor): + field_proto.type = descriptor.FieldDescriptor.TYPE_MESSAGE + else: + field_proto.type = descriptor.FieldDescriptor.TYPE_ENUM + + field_desc.cpp_type = descriptor.FieldDescriptor.ProtoTypeToCppProtoType( + field_proto.type + ) + + if ( + field_proto.type == descriptor.FieldDescriptor.TYPE_MESSAGE + or field_proto.type == descriptor.FieldDescriptor.TYPE_GROUP + ): + field_desc.message_type = desc + + if field_proto.type == descriptor.FieldDescriptor.TYPE_ENUM: + field_desc.enum_type = desc + + if field_proto.label == descriptor.FieldDescriptor.LABEL_REPEATED: + field_desc.has_default_value = False + field_desc.default_value = [] + elif field_proto.HasField('default_value'): + field_desc.has_default_value = True + if ( + field_proto.type == descriptor.FieldDescriptor.TYPE_DOUBLE + or field_proto.type == descriptor.FieldDescriptor.TYPE_FLOAT + ): + field_desc.default_value = float(field_proto.default_value) + elif field_proto.type == descriptor.FieldDescriptor.TYPE_STRING: + field_desc.default_value = field_proto.default_value + elif field_proto.type == descriptor.FieldDescriptor.TYPE_BOOL: + field_desc.default_value = field_proto.default_value.lower() == 'true' + elif field_proto.type == descriptor.FieldDescriptor.TYPE_ENUM: + field_desc.default_value = field_desc.enum_type.values_by_name[ + field_proto.default_value + ].number + elif field_proto.type == descriptor.FieldDescriptor.TYPE_BYTES: + field_desc.default_value = text_encoding.CUnescape( + field_proto.default_value + ) + elif field_proto.type == descriptor.FieldDescriptor.TYPE_MESSAGE: + field_desc.default_value = None + else: + # All other types are of the "int" type. + field_desc.default_value = int(field_proto.default_value) + else: + field_desc.has_default_value = False + if ( + field_proto.type == descriptor.FieldDescriptor.TYPE_DOUBLE + or field_proto.type == descriptor.FieldDescriptor.TYPE_FLOAT + ): + field_desc.default_value = 0.0 + elif field_proto.type == descriptor.FieldDescriptor.TYPE_STRING: + field_desc.default_value = '' + elif field_proto.type == descriptor.FieldDescriptor.TYPE_BOOL: + field_desc.default_value = False + elif field_proto.type == descriptor.FieldDescriptor.TYPE_ENUM: + field_desc.default_value = field_desc.enum_type.values[0].number + elif field_proto.type == descriptor.FieldDescriptor.TYPE_BYTES: + field_desc.default_value = b'' + elif field_proto.type == descriptor.FieldDescriptor.TYPE_MESSAGE: + field_desc.default_value = None + elif field_proto.type == descriptor.FieldDescriptor.TYPE_GROUP: + field_desc.default_value = None + else: + # All other types are of the "int" type. + field_desc.default_value = 0 + + field_desc.type = field_proto.type + + def _MakeEnumValueDescriptor(self, value_proto, index): + """Creates a enum value descriptor object from a enum value proto. + + Args: + value_proto: The proto describing the enum value. + index: The index of the enum value. + + Returns: + An initialized EnumValueDescriptor object. + """ + + return descriptor.EnumValueDescriptor( + name=value_proto.name, + index=index, + number=value_proto.number, + options=_OptionsOrNone(value_proto), + type=None, + # pylint: disable=protected-access + create_key=descriptor._internal_create_key, + ) + + def _MakeServiceDescriptor( + self, service_proto, service_index, scope, package, file_desc + ): + """Make a protobuf ServiceDescriptor given a ServiceDescriptorProto. + + Args: + service_proto: The descriptor_pb2.ServiceDescriptorProto protobuf message. + service_index: The index of the service in the File. + scope: Dict mapping short and full symbols to message and enum types. + package: Optional package name for the new message EnumDescriptor. + file_desc: The file containing the service descriptor. + + Returns: + The added descriptor. + """ + + if package: + service_name = '.'.join((package, service_proto.name)) + else: + service_name = service_proto.name + + methods = [ + self._MakeMethodDescriptor( + method_proto, service_name, package, scope, index + ) + for index, method_proto in enumerate(service_proto.method) + ] + desc = descriptor.ServiceDescriptor( + name=service_proto.name, + full_name=service_name, + index=service_index, + methods=methods, + options=_OptionsOrNone(service_proto), + file=file_desc, + # pylint: disable=protected-access + create_key=descriptor._internal_create_key, + ) + self._CheckConflictRegister(desc, desc.full_name, desc.file.name) + self._service_descriptors[service_name] = desc + return desc + + def _MakeMethodDescriptor( + self, method_proto, service_name, package, scope, index + ): + """Creates a method descriptor from a MethodDescriptorProto. + + Args: + method_proto: The proto describing the method. + service_name: The name of the containing service. + package: Optional package name to look up for types. + scope: Scope containing available types. + index: Index of the method in the service. + + Returns: + An initialized MethodDescriptor object. + """ + full_name = '.'.join((service_name, method_proto.name)) + input_type = self._GetTypeFromScope(package, method_proto.input_type, scope) + output_type = self._GetTypeFromScope( + package, method_proto.output_type, scope + ) + return descriptor.MethodDescriptor( + name=method_proto.name, + full_name=full_name, + index=index, + containing_service=None, + input_type=input_type, + output_type=output_type, + client_streaming=method_proto.client_streaming, + server_streaming=method_proto.server_streaming, + options=_OptionsOrNone(method_proto), + # pylint: disable=protected-access + create_key=descriptor._internal_create_key, + ) + + def _ExtractSymbols(self, descriptors): + """Pulls out all the symbols from descriptor protos. + + Args: + descriptors: The messages to extract descriptors from. + + Yields: + A two element tuple of the type name and descriptor object. + """ + + for desc in descriptors: + yield (_PrefixWithDot(desc.full_name), desc) + for symbol in self._ExtractSymbols(desc.nested_types): + yield symbol + for enum in desc.enum_types: + yield (_PrefixWithDot(enum.full_name), enum) + + def _GetDeps(self, dependencies, visited=None): + """Recursively finds dependencies for file protos. + + Args: + dependencies: The names of the files being depended on. + visited: The names of files already found. + + Yields: + Each direct and indirect dependency. + """ + + visited = visited or set() + for dependency in dependencies: + if dependency not in visited: + visited.add(dependency) + dep_desc = self.FindFileByName(dependency) + yield dep_desc + public_files = [d.name for d in dep_desc.public_dependencies] + yield from self._GetDeps(public_files, visited) + + def _GetTypeFromScope(self, package, type_name, scope): + """Finds a given type name in the current scope. + + Args: + package: The package the proto should be located in. + type_name: The name of the type to be found in the scope. + scope: Dict mapping short and full symbols to message and enum types. + + Returns: + The descriptor for the requested type. + """ + if type_name not in scope: + components = _PrefixWithDot(package).split('.') + while components: + possible_match = '.'.join(components + [type_name]) + if possible_match in scope: + type_name = possible_match + break + else: + components.pop(-1) + return scope[type_name] + + +def _PrefixWithDot(name): + return name if name.startswith('.') else '.%s' % name + + +if _USE_C_DESCRIPTORS: + # TODO: This pool could be constructed from Python code, when we + # support a flag like 'use_cpp_generated_pool=True'. + # pylint: disable=protected-access + _DEFAULT = descriptor._message.default_pool +else: + _DEFAULT = DescriptorPool() + + +def Default(): + return _DEFAULT diff --git a/venv/lib/python3.12/site-packages/google/protobuf/duration.py b/venv/lib/python3.12/site-packages/google/protobuf/duration.py new file mode 100755 index 0000000..21f17bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/duration.py @@ -0,0 +1,100 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +"""Contains the Duration helper APIs.""" + +import datetime + +from google.protobuf.duration_pb2 import Duration + + +def from_json_string(value: str) -> Duration: + """Converts a string to Duration. + + Args: + value: A string to be converted. The string must end with 's'. Any + fractional digits (or none) are accepted as long as they fit into + precision. For example: "1s", "1.01s", "1.0000001s", "-3.100s" + + Raises: + ValueError: On parsing problems. + """ + duration = Duration() + duration.FromJsonString(value) + return duration + + +def from_microseconds(micros: float) -> Duration: + """Converts microseconds to Duration.""" + duration = Duration() + duration.FromMicroseconds(micros) + return duration + + +def from_milliseconds(millis: float) -> Duration: + """Converts milliseconds to Duration.""" + duration = Duration() + duration.FromMilliseconds(millis) + return duration + + +def from_nanoseconds(nanos: float) -> Duration: + """Converts nanoseconds to Duration.""" + duration = Duration() + duration.FromNanoseconds(nanos) + return duration + + +def from_seconds(seconds: float) -> Duration: + """Converts seconds to Duration.""" + duration = Duration() + duration.FromSeconds(seconds) + return duration + + +def from_timedelta(td: datetime.timedelta) -> Duration: + """Converts timedelta to Duration.""" + duration = Duration() + duration.FromTimedelta(td) + return duration + + +def to_json_string(duration: Duration) -> str: + """Converts Duration to string format. + + Returns: + A string converted from self. The string format will contains + 3, 6, or 9 fractional digits depending on the precision required to + represent the exact Duration value. For example: "1s", "1.010s", + "1.000000100s", "-3.100s" + """ + return duration.ToJsonString() + + +def to_microseconds(duration: Duration) -> int: + """Converts a Duration to microseconds.""" + return duration.ToMicroseconds() + + +def to_milliseconds(duration: Duration) -> int: + """Converts a Duration to milliseconds.""" + return duration.ToMilliseconds() + + +def to_nanoseconds(duration: Duration) -> int: + """Converts a Duration to nanoseconds.""" + return duration.ToNanoseconds() + + +def to_seconds(duration: Duration) -> int: + """Converts a Duration to seconds.""" + return duration.ToSeconds() + + +def to_timedelta(duration: Duration) -> datetime.timedelta: + """Converts Duration to timedelta.""" + return duration.ToTimedelta() diff --git a/venv/lib/python3.12/site-packages/google/protobuf/duration_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/duration_pb2.py new file mode 100755 index 0000000..604490b --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/duration_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/duration.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/duration.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1egoogle/protobuf/duration.proto\x12\x0fgoogle.protobuf\":\n\x08\x44uration\x12\x18\n\x07seconds\x18\x01 \x01(\x03R\x07seconds\x12\x14\n\x05nanos\x18\x02 \x01(\x05R\x05nanosB\x83\x01\n\x13\x63om.google.protobufB\rDurationProtoP\x01Z1google.golang.org/protobuf/types/known/durationpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.duration_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\023com.google.protobufB\rDurationProtoP\001Z1google.golang.org/protobuf/types/known/durationpb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _globals['_DURATION']._serialized_start=51 + _globals['_DURATION']._serialized_end=109 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/empty_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/empty_pb2.py new file mode 100755 index 0000000..e46bb23 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/empty_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/empty.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/empty.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bgoogle/protobuf/empty.proto\x12\x0fgoogle.protobuf\"\x07\n\x05\x45mptyB}\n\x13\x63om.google.protobufB\nEmptyProtoP\x01Z.google.golang.org/protobuf/types/known/emptypb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.empty_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\023com.google.protobufB\nEmptyProtoP\001Z.google.golang.org/protobuf/types/known/emptypb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _globals['_EMPTY']._serialized_start=48 + _globals['_EMPTY']._serialized_end=55 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/field_mask_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/field_mask_pb2.py new file mode 100755 index 0000000..35e9334 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/field_mask_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/field_mask.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/field_mask.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n google/protobuf/field_mask.proto\x12\x0fgoogle.protobuf\"!\n\tFieldMask\x12\x14\n\x05paths\x18\x01 \x03(\tR\x05pathsB\x85\x01\n\x13\x63om.google.protobufB\x0e\x46ieldMaskProtoP\x01Z2google.golang.org/protobuf/types/known/fieldmaskpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.field_mask_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\023com.google.protobufB\016FieldMaskProtoP\001Z2google.golang.org/protobuf/types/known/fieldmaskpb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _globals['_FIELDMASK']._serialized_start=53 + _globals['_FIELDMASK']._serialized_end=86 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__init__.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/__init__.py new file mode 100755 index 0000000..80d99fe --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/__init__.py @@ -0,0 +1,6 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ed1e626 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/api_implementation.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/api_implementation.cpython-312.pyc new file mode 100644 index 0000000..55d54b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/api_implementation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/builder.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/builder.cpython-312.pyc new file mode 100644 index 0000000..a365404 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/builder.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/containers.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/containers.cpython-312.pyc new file mode 100644 index 0000000..f123450 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/containers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/decoder.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/decoder.cpython-312.pyc new file mode 100644 index 0000000..c830390 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/decoder.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/encoder.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/encoder.cpython-312.pyc new file mode 100644 index 0000000..e362468 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/encoder.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/enum_type_wrapper.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/enum_type_wrapper.cpython-312.pyc new file mode 100644 index 0000000..f46506f Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/enum_type_wrapper.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/extension_dict.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/extension_dict.cpython-312.pyc new file mode 100644 index 0000000..6e71b81 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/extension_dict.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/field_mask.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/field_mask.cpython-312.pyc new file mode 100644 index 0000000..eedc8b3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/field_mask.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/json_format_benchmark.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/json_format_benchmark.cpython-312.pyc new file mode 100644 index 0000000..e5b7d3b Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/json_format_benchmark.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/message_benchmark.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/message_benchmark.cpython-312.pyc new file mode 100644 index 0000000..d8a3582 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/message_benchmark.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/message_listener.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/message_listener.cpython-312.pyc new file mode 100644 index 0000000..8934db9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/message_listener.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/python_edition_defaults.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/python_edition_defaults.cpython-312.pyc new file mode 100644 index 0000000..4659bd1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/python_edition_defaults.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/python_message.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/python_message.cpython-312.pyc new file mode 100644 index 0000000..c161fe2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/python_message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/testing_refleaks.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/testing_refleaks.cpython-312.pyc new file mode 100644 index 0000000..373002d Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/testing_refleaks.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/type_checkers.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/type_checkers.cpython-312.pyc new file mode 100644 index 0000000..5c912f3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/type_checkers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/well_known_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/well_known_types.cpython-312.pyc new file mode 100644 index 0000000..3f4398b Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/well_known_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/wire_format.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/wire_format.cpython-312.pyc new file mode 100644 index 0000000..598e856 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/internal/__pycache__/wire_format.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/api_implementation.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/api_implementation.py new file mode 100755 index 0000000..2cf62f2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/api_implementation.py @@ -0,0 +1,143 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Determine which implementation of the protobuf API is used in this process.""" + +import importlib +import os +import sys +import warnings + +_GOOGLE3_PYTHON_UPB_DEFAULT = True + + +def _ApiVersionToImplementationType(api_version): + if api_version == 2: + return 'cpp' + if api_version == 1: + raise ValueError('api_version=1 is no longer supported.') + if api_version == 0: + return 'python' + return None + + +_implementation_type = None +try: + # pylint: disable=g-import-not-at-top + from google.protobuf.internal import _api_implementation + # The compile-time constants in the _api_implementation module can be used to + # switch to a certain implementation of the Python API at build time. + _implementation_type = _ApiVersionToImplementationType( + _api_implementation.api_version + ) +except ImportError: + pass # Unspecified by compiler flags. + + +def _CanImport(mod_name): + try: + mod = importlib.import_module(mod_name) + # Work around a known issue in the classic bootstrap .par import hook. + if not mod: + raise ImportError(mod_name + ' import succeeded but was None') + return True + except ImportError: + return False + + +if _implementation_type is None: + if _CanImport('google._upb._message'): + _implementation_type = 'upb' + elif _CanImport('google.protobuf.pyext._message'): + _implementation_type = 'cpp' + else: + _implementation_type = 'python' + +# This environment variable can be used to switch to a certain implementation +# of the Python API, overriding the compile-time constants in the +# _api_implementation module. Right now only 'python', 'cpp' and 'upb' are +# valid values. Any other value will raise error. +_implementation_type = os.getenv( + 'PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION', _implementation_type +) + +if _implementation_type not in ('python', 'cpp', 'upb'): + raise ValueError( + 'PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION {0} is not ' + "supported. Please set to 'python', 'cpp' or " + "'upb'.".format(_implementation_type) + ) + +if 'PyPy' in sys.version and _implementation_type == 'cpp': + warnings.warn( + 'PyPy does not work yet with cpp protocol buffers. ' + 'Falling back to the python implementation.' + ) + _implementation_type = 'python' + +_c_module = None + +if _implementation_type == 'cpp': + try: + # pylint: disable=g-import-not-at-top + from google.protobuf.pyext import _message + + sys.modules['google3.net.proto2.python.internal.cpp._message'] = _message + _c_module = _message + del _message + except ImportError: + # TODO: fail back to python + warnings.warn('Selected implementation cpp is not available.') + pass + +if _implementation_type == 'upb': + try: + # pylint: disable=g-import-not-at-top + from google._upb import _message + + _c_module = _message + del _message + except ImportError: + warnings.warn( + 'Selected implementation upb is not available. ' + 'Falling back to the python implementation.' + ) + _implementation_type = 'python' + pass + +# Detect if serialization should be deterministic by default +try: + # The presence of this module in a build allows the proto implementation to + # be upgraded merely via build deps. + # + # NOTE: Merely importing this automatically enables deterministic proto + # serialization for C++ code, but we still need to export it as a boolean so + # that we can do the same for `_implementation_type == 'python'`. + # + # NOTE2: It is possible for C++ code to enable deterministic serialization by + # default _without_ affecting Python code, if the C++ implementation is not in + # use by this module. That is intended behavior, so we don't actually expose + # this boolean outside of this module. + # + # pylint: disable=g-import-not-at-top,unused-import + from google.protobuf import enable_deterministic_proto_serialization + + _python_deterministic_proto_serialization = True +except ImportError: + _python_deterministic_proto_serialization = False + + +# Usage of this function is discouraged. Clients shouldn't care which +# implementation of the API is in use. Note that there is no guarantee +# that differences between APIs will be maintained. +# Please don't use this function if possible. +def Type(): + return _implementation_type + + +# For internal use only +def IsPythonDefaultSerializationDeterministic(): + return _python_deterministic_proto_serialization diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/builder.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/builder.py new file mode 100755 index 0000000..1f7e862 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/builder.py @@ -0,0 +1,123 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Builds descriptors, message classes and services for generated _pb2.py. + +This file is only called in python generated _pb2.py files. It builds +descriptors, message classes and services that users can directly use +in generated code. +""" + +__author__ = 'jieluo@google.com (Jie Luo)' + +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import enum_type_wrapper +from google.protobuf.internal import python_message + +_sym_db = _symbol_database.Default() + + +def _BuildNestedDescriptors(module, msg_des, prefix): + for name, nested_msg in msg_des.nested_types_by_name.items(): + module_name = prefix + name.upper() + module[module_name] = nested_msg + _BuildNestedDescriptors(module, nested_msg, module_name + '_') + for enum_des in msg_des.enum_types: + module[prefix + enum_des.name.upper()] = enum_des + + +def BuildMessageAndEnumDescriptors(file_des, module): + """Builds message and enum descriptors. + + Args: + file_des: FileDescriptor of the .proto file + module: Generated _pb2 module + """ + for name, msg_des in file_des.message_types_by_name.items(): + module_name = '_' + name.upper() + module[module_name] = msg_des + _BuildNestedDescriptors(module, msg_des, module_name + '_') + + +def _BuildMessage(module_name, msg_des, prefix): + create_dict = {} + for name, nested_msg in msg_des.nested_types_by_name.items(): + create_dict[name] = _BuildMessage( + module_name, nested_msg, prefix + msg_des.name + '.' + ) + create_dict['DESCRIPTOR'] = msg_des + create_dict['__module__'] = module_name + create_dict['__qualname__'] = prefix + msg_des.name + message_class = _reflection.GeneratedProtocolMessageType( + msg_des.name, (_message.Message,), create_dict + ) + _sym_db.RegisterMessage(message_class) + return message_class + + +def BuildTopDescriptorsAndMessages(file_des, module_name, module): + """Builds top level descriptors and message classes. + + Args: + file_des: FileDescriptor of the .proto file + module_name: str, the name of generated _pb2 module + module: Generated _pb2 module + """ + + # top level enums + for name, enum_des in file_des.enum_types_by_name.items(): + module['_' + name.upper()] = enum_des + module[name] = enum_type_wrapper.EnumTypeWrapper(enum_des) + for enum_value in enum_des.values: + module[enum_value.name] = enum_value.number + + # top level extensions + for name, extension_des in file_des.extensions_by_name.items(): + module[name.upper() + '_FIELD_NUMBER'] = extension_des.number + module[name] = extension_des + + # services + for name, service in file_des.services_by_name.items(): + module['_' + name.upper()] = service + + # Build messages. + for name, msg_des in file_des.message_types_by_name.items(): + module[name] = _BuildMessage(module_name, msg_des, '') + + +def AddHelpersToExtensions(file_des): + """no-op to keep old generated code work with new runtime. + + Args: + file_des: FileDescriptor of the .proto file + """ + # TODO: Remove this on-op + return + + +def BuildServices(file_des, module_name, module): + """Builds services classes and services stub class. + + Args: + file_des: FileDescriptor of the .proto file + module_name: str, the name of generated _pb2 module + module: Generated _pb2 module + """ + # pylint: disable=g-import-not-at-top + from google.protobuf import service_reflection + # pylint: enable=g-import-not-at-top + for name, service in file_des.services_by_name.items(): + module[name] = service_reflection.GeneratedServiceType( + name, (), dict(DESCRIPTOR=service, __module__=module_name) + ) + stub_name = name + '_Stub' + module[stub_name] = service_reflection.GeneratedServiceStubType( + stub_name, + (module[name],), + dict(DESCRIPTOR=service, __module__=module_name), + ) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/containers.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/containers.py new file mode 100755 index 0000000..896faee --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/containers.py @@ -0,0 +1,826 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Contains container classes to represent different protocol buffer types. + +This file defines container classes which represent categories of protocol +buffer field types which need extra maintenance. Currently these categories +are: + +- Repeated scalar fields - These are all repeated fields which aren't + composite (e.g. they are of simple types like int32, string, etc). +- Repeated composite fields - Repeated fields which are composite. This + includes groups and nested messages. +""" + +import collections.abc +import copy +import pickle +import warnings +from typing import ( + Any, + Iterable, + Iterator, + List, + MutableMapping, + MutableSequence, + NoReturn, + Optional, + Sequence, + TypeVar, + Union, + overload, +) + +_T = TypeVar('_T') +_K = TypeVar('_K') +_V = TypeVar('_V') + +from google.protobuf.descriptor import FieldDescriptor +from google.protobuf import message + + +def _CheckFrozen(is_frozen: bool, msg: str) -> None: + if is_frozen: + warnings.warn( + 'Mutating messages or containers returned by GetOptions() is' + ' deprecated and will raise an exception in a future release.', + category=FutureWarning, + stacklevel=3, + ) + + +class BaseContainer(Sequence[_T]): + """Base container class.""" + + # Minimizes memory usage and disallows assignment to other attributes. + __slots__ = ['_message_listener', '_values', '_frozen'] + + def __init__(self, message_listener: Any) -> None: + """Args: + + message_listener: A MessageListener implementation. + The RepeatedScalarFieldContainer will call this object's + Modified() method when it is modified. + """ + self._message_listener = message_listener + self._values = [] + self._frozen = False + + @overload + def __getitem__(self, key: int) -> _T: + ... + + @overload + def __getitem__(self, key: slice) -> List[_T]: + ... + + def __getitem__(self, key): + """Retrieves item by the specified key.""" + return self._values[key] + + def __len__(self) -> int: + """Returns the number of elements in the container.""" + return len(self._values) + + def __ne__(self, other: Any) -> bool: + """Checks if another instance isn't equal to this one.""" + # The concrete classes should define __eq__. + return not self == other + + __hash__ = None + + def __repr__(self) -> str: + return repr(self._values) + + def _SetFrozen(self) -> None: + self._frozen = True + + def _AssureWritable(self) -> 'BaseContainer[_T]': + _CheckFrozen(self._frozen, 'Container is immutable') + return self + + def sort(self, *args, **kwargs) -> None: + self._AssureWritable() + # Continue to support the old sort_function keyword argument. + # This is expected to be a rare occurrence, so use LBYL to avoid + # the overhead of actually catching KeyError. + if 'sort_function' in kwargs: + kwargs['cmp'] = kwargs.pop('sort_function') + self._values.sort(*args, **kwargs) + + def reverse(self) -> None: + self._AssureWritable() + self._values.reverse() + + +# TODO: Remove this. BaseContainer does *not* conform to +# MutableSequence, only its subclasses do. +collections.abc.MutableSequence.register(BaseContainer) + + +class RepeatedScalarFieldContainer(BaseContainer[_T], MutableSequence[_T]): + """Simple, type-checked, list-like container for holding repeated scalars.""" + + # Disallows assignment to other attributes. + __slots__ = ['_type_checker', '_field'] + + def __init__( + self, + message_listener: Any, + type_checker: Any, + field: Any = None, + ) -> None: + """Args: + + message_listener: A MessageListener implementation. The + RepeatedScalarFieldContainer will call this object's Modified() method + when it is modified. + type_checker: A type_checkers.ValueChecker instance to run on elements + inserted into this container. + """ + super().__init__(message_listener) + self._type_checker = type_checker + self._field = field + + def append(self, value: _T) -> None: + """Appends an item to the list. Similar to list.append().""" + self._AssureWritable() + self._values.append(self._type_checker.CheckValue(value)) + if not self._message_listener.dirty: + self._message_listener.Modified() + + def insert(self, key: int, value: _T) -> None: + """Inserts the item at the specified position. Similar to list.insert().""" + self._AssureWritable() + self._values.insert(key, self._type_checker.CheckValue(value)) + if not self._message_listener.dirty: + self._message_listener.Modified() + + def extend(self, elem_seq: Iterable[_T]) -> None: + """Extends by appending the given iterable. Similar to list.extend().""" + self._AssureWritable() + elem_seq_iter = iter(elem_seq) + new_values = [self._type_checker.CheckValue(elem) for elem in elem_seq_iter] + if new_values: + self._values.extend(new_values) + self._message_listener.Modified() + + def MergeFrom( + self, + other: Union['RepeatedScalarFieldContainer[_T]', Iterable[_T]], + ) -> None: + """Appends the contents of another repeated field of the same type to this + + one. We do not check the types of the individual fields. + """ + self._AssureWritable() + self._values.extend(other) + self._message_listener.Modified() + + def remove(self, elem: _T): + """Removes an item from the list. Similar to list.remove().""" + self._AssureWritable() + self._values.remove(elem) + self._message_listener.Modified() + + def pop(self, key: Optional[int] = -1) -> _T: + """Removes and returns an item at a given index. Similar to list.pop().""" + self._AssureWritable() + value = self._values[key] + self.__delitem__(key) + return value + + @overload + def __setitem__(self, key: int, value: _T) -> None: + ... + + @overload + def __setitem__(self, key: slice, value: Iterable[_T]) -> None: + ... + + def __setitem__(self, key, value) -> None: + """Sets the item on the specified position.""" + self._AssureWritable() + if isinstance(key, slice): + if key.step is not None: + raise ValueError('Extended slices not supported') + self._values[key] = map(self._type_checker.CheckValue, value) + self._message_listener.Modified() + else: + self._values[key] = self._type_checker.CheckValue(value) + self._message_listener.Modified() + + def __delitem__(self, key: Union[int, slice]) -> None: + """Deletes the item at the specified position.""" + self._AssureWritable() + del self._values[key] + self._message_listener.Modified() + + def __eq__(self, other: Any) -> bool: + """Compares the current instance with another one.""" + if self is other: + return True + # Special case for the same type which should be common and fast. + if isinstance(other, self.__class__): + return other._values == self._values + # We are presumably comparing against some other sequence type. + return other == self._values + + def __deepcopy__( + self, + unused_memo: Any = None, + ) -> 'RepeatedScalarFieldContainer[_T]': + clone = RepeatedScalarFieldContainer( + copy.deepcopy(self._message_listener), self._type_checker, self._field + ) + clone.MergeFrom(self) + return clone + + def __reduce__(self, **kwargs) -> NoReturn: + raise pickle.PickleError( + "Can't pickle repeated scalar fields, convert to list first" + ) + + def __array__(self, dtype=None, copy=None): + import numpy as np + + if dtype is None: + cpp_type = self._field.cpp_type + if cpp_type == FieldDescriptor.CPPTYPE_INT32: + dtype = np.int32 + elif cpp_type == FieldDescriptor.CPPTYPE_INT64: + dtype = np.int64 + elif cpp_type == FieldDescriptor.CPPTYPE_UINT32: + dtype = np.uint32 + elif cpp_type == FieldDescriptor.CPPTYPE_UINT64: + dtype = np.uint64 + elif cpp_type == FieldDescriptor.CPPTYPE_DOUBLE: + dtype = np.float64 + elif cpp_type == FieldDescriptor.CPPTYPE_FLOAT: + dtype = np.float32 + elif cpp_type == FieldDescriptor.CPPTYPE_BOOL: + dtype = np.bool + elif cpp_type == FieldDescriptor.CPPTYPE_ENUM: + dtype = np.int32 + elif self._field.type == FieldDescriptor.TYPE_BYTES: + dtype = 'S' + elif self._field.type == FieldDescriptor.TYPE_STRING: + dtype = str + else: + raise SystemError( + 'Code should never reach here: message type detected in' + ' RepeatedScalarFieldContainer' + ) + return np.array(self._values, dtype=dtype, copy=True) + + +# TODO: Constrain T to be a subtype of Message. +class RepeatedCompositeFieldContainer(BaseContainer[_T], MutableSequence[_T]): + """Simple, list-like container for holding repeated composite fields.""" + + # Disallows assignment to other attributes. + __slots__ = ['_message_descriptor'] + + def __init__(self, message_listener: Any, message_descriptor: Any) -> None: + """Note that we pass in a descriptor instead of the generated directly, + + since at the time we construct a _RepeatedCompositeFieldContainer we + haven't yet necessarily initialized the type that will be contained in the + container. + + Args: + message_listener: A MessageListener implementation. The + RepeatedCompositeFieldContainer will call this object's Modified() + method when it is modified. + message_descriptor: A Descriptor instance describing the protocol type + that should be present in this container. We'll use the _concrete_class + field of this descriptor when the client calls add(). + """ + super().__init__(message_listener) + self._message_descriptor = message_descriptor + + def _SetFrozen(self) -> None: + super()._SetFrozen() + for val in self._values: + val._SetFrozen() + + def add(self, **kwargs: Any) -> _T: + """Adds a new element at the end of the list and returns it. + + Keyword arguments may be used to initialize the element. + """ + self._AssureWritable() + new_element = self._message_descriptor._concrete_class(**kwargs) + new_element._SetListener(self._message_listener) + self._values.append(new_element) + if not self._message_listener.dirty: + self._message_listener.Modified() + return new_element + + def append(self, value: _T) -> None: + """Appends one element by copying the message.""" + self._AssureWritable() + new_element = self._message_descriptor._concrete_class() + new_element._SetListener(self._message_listener) + new_element.CopyFrom(value) + self._values.append(new_element) + if not self._message_listener.dirty: + self._message_listener.Modified() + + def insert(self, key: int, value: _T) -> None: + """Inserts the item at the specified position by copying.""" + self._AssureWritable() + new_element = self._message_descriptor._concrete_class() + new_element._SetListener(self._message_listener) + new_element.CopyFrom(value) + self._values.insert(key, new_element) + if not self._message_listener.dirty: + self._message_listener.Modified() + + def extend(self, elem_seq: Iterable[_T]) -> None: + """Extends by appending the given sequence of elements of the same type + + as this one, copying each individual message. + """ + self._AssureWritable() + message_class = self._message_descriptor._concrete_class + listener = self._message_listener + values = self._values + for message in elem_seq: + new_element = message_class() + new_element._SetListener(listener) + new_element.MergeFrom(message) + values.append(new_element) + listener.Modified() + + def MergeFrom( + self, + other: Union['RepeatedCompositeFieldContainer[_T]', Iterable[_T]], + ) -> None: + """Appends the contents of another repeated field of the same type to this + + one, copying each individual message. + """ + self._AssureWritable() + self.extend(other) + + def remove(self, elem: _T) -> None: + """Removes an item from the list. Similar to list.remove().""" + self._AssureWritable() + self._values.remove(elem) + self._message_listener.Modified() + + def pop(self, key: Optional[int] = -1) -> _T: + """Removes and returns an item at a given index. Similar to list.pop().""" + self._AssureWritable() + value = self._values[key] + self.__delitem__(key) + return value + + @overload + def __setitem__(self, key: int, value: _T) -> None: + ... + + @overload + def __setitem__(self, key: slice, value: Iterable[_T]) -> None: + ... + + def __setitem__(self, key, value): + # This method is implemented to make RepeatedCompositeFieldContainer + # structurally compatible with typing.MutableSequence. It is + # otherwise unsupported and will always raise an error. + raise TypeError( + f'{self.__class__.__name__} object does not support item assignment' + ) + + def __delitem__(self, key: Union[int, slice]) -> None: + """Deletes the item at the specified position.""" + self._AssureWritable() + del self._values[key] + self._message_listener.Modified() + + def __eq__(self, other: Any) -> bool: + """Compares the current instance with another one.""" + if self is other: + return True + if not isinstance(other, self.__class__): + raise TypeError( + 'Can only compare repeated composite fields against ' + 'other repeated composite fields.' + ) + return self._values == other._values + + +class ScalarMap(MutableMapping[_K, _V]): + """Simple, type-checked, dict-like container for holding repeated scalars.""" + + # Disallows assignment to other attributes. + __slots__ = [ + '_key_checker', + '_value_checker', + '_values', + '_message_listener', + '_entry_descriptor', + '_frozen', + ] + + def __init__( + self, + message_listener: Any, + key_checker: Any, + value_checker: Any, + entry_descriptor: Any, + ) -> None: + """Args: + + message_listener: A MessageListener implementation. + The ScalarMap will call this object's Modified() method when it + is modified. + key_checker: A type_checkers.ValueChecker instance to run on keys + inserted into this container. + value_checker: A type_checkers.ValueChecker instance to run on values + inserted into this container. + entry_descriptor: The MessageDescriptor of a map entry: key and value. + """ + self._message_listener = message_listener + self._key_checker = key_checker + self._value_checker = value_checker + self._entry_descriptor = entry_descriptor + self._values = {} + self._frozen = False + + def _SetFrozen(self) -> None: + self._frozen = True + + def _AssureWritable(self) -> 'ScalarMap[_K, _V]': + _CheckFrozen(self._frozen, 'Map is immutable') + return self + + def __getitem__(self, key: _K) -> _V: + key = self._key_checker.CheckValue(key) + try: + return self._values[key] + except KeyError: + self._AssureWritable() + val = self._value_checker.DefaultValue() + self._values[key] = val + return val + + def __contains__(self, item: _K) -> bool: + # We check the key's type to match the strong-typing flavor of the API. + # Also this makes it easier to match the behavior of the C++ implementation. + item = self._key_checker.CheckValue(item) + return item in self._values + + @overload + def get(self, key: _K) -> Optional[_V]: + ... + + @overload + def get(self, key: _K, default: _T) -> Union[_V, _T]: + ... + + # We need to override this explicitly, because our defaultdict-like behavior + # will make the default implementation (from our base class) always insert + # the key. + def get(self, key, default=None): + checked_key = self._key_checker.CheckValue(key) + if checked_key in self._values: + return self[checked_key] + else: + return default + + def __setitem__(self, key: _K, value: _V) -> _T: + self._AssureWritable() + checked_key = self._key_checker.CheckValue(key) + checked_value = self._value_checker.CheckValue(value) + self._values[checked_key] = checked_value + self._message_listener.Modified() + + def __delitem__(self, key: _K) -> None: + self._AssureWritable() + checked_key = self._key_checker.CheckValue(key) + del self._values[checked_key] + self._message_listener.Modified() + + def __len__(self) -> int: + return len(self._values) + + def __iter__(self) -> Iterator[_K]: + return iter(self._values) + + def __repr__(self) -> str: + return repr(self._values) + + def setdefault(self, key: _K, value: Optional[_V] = None) -> _V: + self._AssureWritable() + checked_key = self._key_checker.CheckValue(key) + if value == None: + raise ValueError('The value for scalar map setdefault must be set.') + if checked_key not in self._values: + self.__setitem__(checked_key, value) + return self[key] + + def MergeFrom(self, other: 'ScalarMap[_K, _V]') -> None: + self._AssureWritable() + self._values.update(other._values) + self._message_listener.Modified() + + def InvalidateIterators(self) -> None: + # It appears that the only way to reliably invalidate iterators to + # self._values is to ensure that its size changes. + original = self._values + self._values = original.copy() + original[None] = None + + # This is defined in the abstract base, but we can do it much more cheaply. + def clear(self) -> None: + self._AssureWritable() + self._values.clear() + self._message_listener.Modified() + + def GetEntryClass(self) -> Any: + return self._entry_descriptor._concrete_class + + +class MessageMap(MutableMapping[_K, _V]): + """Simple, type-checked, dict-like container for with submessage values.""" + + # Disallows assignment to other attributes. + __slots__ = [ + '_key_checker', + '_values', + '_message_listener', + '_message_descriptor', + '_entry_descriptor', + '_frozen', + ] + + def __init__( + self, + message_listener: Any, + message_descriptor: Any, + key_checker: Any, + entry_descriptor: Any, + ) -> None: + """Args: + + message_listener: A MessageListener implementation. + The ScalarMap will call this object's Modified() method when it + is modified. + key_checker: A type_checkers.ValueChecker instance to run on keys + inserted into this container. + value_checker: A type_checkers.ValueChecker instance to run on values + inserted into this container. + entry_descriptor: The MessageDescriptor of a map entry: key and value. + """ + self._message_listener = message_listener + self._message_descriptor = message_descriptor + self._key_checker = key_checker + self._entry_descriptor = entry_descriptor + self._values = {} + self._frozen = False + + def _SetFrozen(self) -> None: + self._frozen = True + for val in self._values.values(): + val._SetFrozen() + + def _AssureWritable(self) -> 'MessageMap[_K, _V]': + _CheckFrozen(self._frozen, 'Map is immutable') + return self + + def __getitem__(self, key: _K) -> _V: + key = self._key_checker.CheckValue(key) + try: + return self._values[key] + except KeyError: + self._AssureWritable() + new_element = self._message_descriptor._concrete_class() + new_element._SetListener(self._message_listener) + self._values[key] = new_element + self._message_listener.Modified() + return new_element + + def get_or_create(self, key: _K) -> _V: + """get_or_create() is an alias for getitem (ie. map[key]). + + Args: + key: The key to get or create in the map. + + This is useful in cases where you want to be explicit that the call is + mutating the map. This can avoid lint errors for statements like this + that otherwise would appear to be pointless statements: + + msg.my_map[key] + """ + return self[key] + + @overload + def get(self, key: _K) -> Optional[_V]: + ... + + @overload + def get(self, key: _K, default: _T) -> Union[_V, _T]: + ... + + # We need to override this explicitly, because our defaultdict-like behavior + # will make the default implementation (from our base class) always insert + # the key. + def get(self, key, default=None): + if key in self: + return self[key] + else: + return default + + def __contains__(self, item: _K) -> bool: + item = self._key_checker.CheckValue(item) + return item in self._values + + def __setitem__(self, key: _K, value: _V) -> NoReturn: + self._AssureWritable() + raise ValueError('May not set values directly, call my_map[key].foo = 5') + + def __delitem__(self, key: _K) -> None: + self._AssureWritable() + key = self._key_checker.CheckValue(key) + del self._values[key] + self._message_listener.Modified() + + def __len__(self) -> int: + return len(self._values) + + def __iter__(self) -> Iterator[_K]: + return iter(self._values) + + def __repr__(self) -> str: + return repr(self._values) + + def setdefault(self, key: _K, value: Optional[_V] = None) -> _V: + self._AssureWritable() + raise NotImplementedError( + 'Set message map value directly is not supported, call' + ' my_map[key].foo = 5' + ) + + def MergeFrom(self, other: 'MessageMap[_K, _V]') -> None: + self._AssureWritable() + # pylint: disable=protected-access + for key in other._values: + # According to documentation: "When parsing from the wire or when merging, + # if there are duplicate map keys the last key seen is used". + if key in self: + del self[key] + self[key].CopyFrom(other[key]) + # self._message_listener.Modified() not required here, because + # mutations to submessages already propagate. + + def InvalidateIterators(self) -> None: + # It appears that the only way to reliably invalidate iterators to + # self._values is to ensure that its size changes. + original = self._values + self._values = original.copy() + original[None] = None + + # This is defined in the abstract base, but we can do it much more cheaply. + def clear(self) -> None: + self._AssureWritable() + self._values.clear() + self._message_listener.Modified() + + def GetEntryClass(self) -> Any: + return self._entry_descriptor._concrete_class + + +class _UnknownField: + """A parsed unknown field.""" + + # Disallows assignment to other attributes. + __slots__ = ['_field_number', '_wire_type', '_data'] + + def __init__(self, field_number, wire_type, data): + self._field_number = field_number + self._wire_type = wire_type + self._data = data + return + + def __lt__(self, other): + # pylint: disable=protected-access + return self._field_number < other._field_number + + def __eq__(self, other): + if self is other: + return True + # pylint: disable=protected-access + return ( + self._field_number == other._field_number + and self._wire_type == other._wire_type + and self._data == other._data + ) + + +class UnknownFieldRef: # pylint: disable=missing-class-docstring + + def __init__(self, parent, index): + self._parent = parent + self._index = index + + def _check_valid(self): + if not self._parent: + raise ValueError( + 'UnknownField does not exist. The parent message might be cleared.' + ) + if self._index >= len(self._parent): + raise ValueError( + 'UnknownField does not exist. The parent message might be cleared.' + ) + + @property + def field_number(self): + self._check_valid() + # pylint: disable=protected-access + return self._parent._internal_get(self._index)._field_number + + @property + def wire_type(self): + self._check_valid() + # pylint: disable=protected-access + return self._parent._internal_get(self._index)._wire_type + + @property + def data(self): + self._check_valid() + # pylint: disable=protected-access + return self._parent._internal_get(self._index)._data + + +class UnknownFieldSet: + """UnknownField container""" + + # Disallows assignment to other attributes. + __slots__ = ['_values'] + + def __init__(self): + self._values = [] + + def __getitem__(self, index): + if self._values is None: + raise ValueError( + 'UnknownFields does not exist. The parent message might be cleared.' + ) + size = len(self._values) + if index < 0: + index += size + if index < 0 or index >= size: + raise IndexError('index %d out of range'.index) + + return UnknownFieldRef(self, index) + + def _internal_get(self, index): + return self._values[index] + + def __len__(self): + if self._values is None: + raise ValueError( + 'UnknownFields does not exist. The parent message might be cleared.' + ) + return len(self._values) + + def _add(self, field_number, wire_type, data): + unknown_field = _UnknownField(field_number, wire_type, data) + self._values.append(unknown_field) + return unknown_field + + def __iter__(self): + for i in range(len(self)): + yield UnknownFieldRef(self, i) + + def _extend(self, other): + if other is None: + return + # pylint: disable=protected-access + self._values.extend(other._values) + + def __eq__(self, other): + if self is other: + return True + # Sort unknown fields because their order shouldn't + # affect equality test. + values = list(self._values) + if other is None: + return not values + values.sort() + # pylint: disable=protected-access + other_values = sorted(other._values) + return values == other_values + + def _clear(self): + for value in self._values: + # pylint: disable=protected-access + if isinstance(value._data, UnknownFieldSet): + value._data._clear() # pylint: disable=protected-access + self._values = None diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/decoder.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/decoder.py new file mode 100755 index 0000000..ae9b1d7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/decoder.py @@ -0,0 +1,1127 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Code for decoding protocol buffer primitives. + +This code is very similar to encoder.py -- read the docs for that module first. + +A "decoder" is a function with the signature: + Decode(buffer, pos, end, message, field_dict) +The arguments are: + buffer: The string containing the encoded message. + pos: The current position in the string. + end: The position in the string where the current message ends. May be + less than len(buffer) if we're reading a sub-message. + message: The message object into which we're parsing. + field_dict: message._fields (avoids a hashtable lookup). +The decoder reads the field and stores it into field_dict, returning the new +buffer position. A decoder for a repeated field may proactively decode all of +the elements of that field, if they appear consecutively. + +Note that decoders may throw any of the following: + IndexError: Indicates a truncated message. + struct.error: Unpacking of a fixed-width field failed. + message.DecodeError: Other errors. + +Decoders are expected to raise an exception if they are called with pos > end. +This allows callers to be lax about bounds checking: it's fineto read past +"end" as long as you are sure that someone else will notice and throw an +exception later on. + +Something up the call stack is expected to catch IndexError and struct.error +and convert them to message.DecodeError. + +Decoders are constructed using decoder constructors with the signature: + MakeDecoder(field_number, is_repeated, is_packed, key, new_default) +The arguments are: + field_number: The field number of the field we want to decode. + is_repeated: Is the field a repeated field? (bool) + is_packed: Is the field a packed field? (bool) + key: The key to use when looking up the field within field_dict. + (This is actually the FieldDescriptor but nothing in this + file should depend on that.) + new_default: A function which takes a message object as a parameter and + returns a new instance of the default value for this field. + (This is called for repeated fields and sub-messages, when an + instance does not already exist.) + +As with encoders, we define a decoder constructor for every type of field. +Then, for every field of every message class we construct an actual decoder. +That decoder goes into a dict indexed by tag, so when we decode a message +we repeatedly read a tag, look up the corresponding decoder, and invoke it. +""" + +__author__ = 'kenton@google.com (Kenton Varda)' + +import math +import numbers +import struct + +from google.protobuf import message +from google.protobuf.internal import containers +from google.protobuf.internal import encoder +from google.protobuf.internal import wire_format + +# This is not for optimization, but rather to avoid conflicts with local +# variables named "message". +_DecodeError = message.DecodeError + + +def IsDefaultScalarValue(value): + """Returns whether or not a scalar value is the default value of its type. + + Specifically, this should be used to determine presence of implicit-presence + fields, where we disallow custom defaults. + + Args: + value: A scalar value to check. + + Returns: + True if the value is equivalent to a default value, False otherwise. + """ + if isinstance(value, numbers.Number) and math.copysign(1.0, value) < 0: + # Special case for negative zero, where "truthiness" fails to give the right + # answer. + return False + + # Normally, we can just use Python's boolean conversion. + return not value + + +def _VarintDecoder(mask, result_type): + """Return an encoder for a basic varint value (does not include tag). + + Decoded values will be bitwise-anded with the given mask before being + returned, e.g. to limit them to 32 bits. The returned decoder does not + take the usual "end" parameter -- the caller is expected to do bounds checking + after the fact (often the caller can defer such checking until later). The + decoder returns a (value, new_pos) pair. + """ + + def DecodeVarint(buffer, pos: int = None): + result = 0 + shift = 0 + while 1: + if pos is None: + # Read from BytesIO + try: + b = buffer.read(1)[0] + except IndexError as e: + if shift == 0: + # End of BytesIO. + return None + else: + raise ValueError('Fail to read varint %s' % str(e)) + else: + b = buffer[pos] + pos += 1 + result |= (b & 0x7F) << shift + if not (b & 0x80): + result &= mask + result = result_type(result) + return result if pos is None else (result, pos) + shift += 7 + if shift >= 64: + raise _DecodeError('Too many bytes when decoding varint.') + + return DecodeVarint + + +def _SignedVarintDecoder(bits, result_type): + """Like _VarintDecoder() but decodes signed values.""" + + signbit = 1 << (bits - 1) + mask = (1 << bits) - 1 + + def DecodeVarint(buffer, pos): + result = 0 + shift = 0 + while 1: + b = buffer[pos] + result |= (b & 0x7F) << shift + pos += 1 + if not (b & 0x80): + result &= mask + result = (result ^ signbit) - signbit + result = result_type(result) + return (result, pos) + shift += 7 + if shift >= 64: + raise _DecodeError('Too many bytes when decoding varint.') + + return DecodeVarint + + +# All 32-bit and 64-bit values are represented as int. +_DecodeVarint = _VarintDecoder((1 << 64) - 1, int) +_DecodeSignedVarint = _SignedVarintDecoder(64, int) + +# Use these versions for values which must be limited to 32 bits. +_DecodeVarint32 = _VarintDecoder((1 << 32) - 1, int) +_DecodeSignedVarint32 = _SignedVarintDecoder(32, int) + + +def ReadTag(buffer, pos): + """Read a tag from the memoryview, and return a (tag_bytes, new_pos) tuple. + + We return the raw bytes of the tag rather than decoding them. The raw + bytes can then be used to look up the proper decoder. This effectively allows + us to trade some work that would be done in pure-python (decoding a varint) + for work that is done in C (searching for a byte string in a hash table). + In a low-level language it would be much cheaper to decode the varint and + use that, but not in Python. + + Args: + buffer: memoryview object of the encoded bytes + pos: int of the current position to start from + + Returns: + Tuple[bytes, int] of the tag data and new position. + """ + start = pos + while buffer[pos] & 0x80: + pos += 1 + pos += 1 + + tag_bytes = buffer[start:pos].tobytes() + return tag_bytes, pos + + +def DecodeTag(tag_bytes): + """Decode a tag from the bytes. + + Args: + tag_bytes: the bytes of the tag + + Returns: + Tuple[int, int] of the tag field number and wire type. + """ + tag, _ = _DecodeVarint(tag_bytes, 0) + return wire_format.UnpackTag(tag) + + +# -------------------------------------------------------------------- + + +def _SimpleDecoder(wire_type, decode_value): + """Return a constructor for a decoder for fields of a particular type. + + Args: + wire_type: The field's wire type. + decode_value: A function which decodes an individual value, e.g. + _DecodeVarint() + """ + + def SpecificDecoder( + field_number, + is_repeated, + is_packed, + key, + new_default, + clear_if_default=False, + ): + if is_packed: + local_DecodeVarint = _DecodeVarint + + def DecodePackedField( + buffer, pos, end, message, field_dict, current_depth=0 + ): + del current_depth # unused + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + endpoint, pos = local_DecodeVarint(buffer, pos) + endpoint += pos + if endpoint > end: + raise _DecodeError('Truncated message.') + while pos < endpoint: + element, pos = decode_value(buffer, pos) + value.append(element) + if pos > endpoint: + del value[-1] # Discard corrupt value. + raise _DecodeError('Packed element was truncated.') + return pos + + return DecodePackedField + elif is_repeated: + tag_bytes = encoder.TagBytes(field_number, wire_type) + tag_len = len(tag_bytes) + + def DecodeRepeatedField( + buffer, pos, end, message, field_dict, current_depth=0 + ): + del current_depth # unused + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + element, new_pos = decode_value(buffer, pos) + value.append(element) + # Predict that the next tag is another copy of the same repeated + # field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos >= end: + # Prediction failed. Return. + if new_pos > end: + raise _DecodeError('Truncated message.') + return new_pos + + return DecodeRepeatedField + else: + + def DecodeField(buffer, pos, end, message, field_dict, current_depth=0): + del current_depth # unused + new_value, pos = decode_value(buffer, pos) + if pos > end: + raise _DecodeError('Truncated message.') + if clear_if_default and IsDefaultScalarValue(new_value): + field_dict.pop(key, None) + else: + field_dict[key] = new_value + return pos + + return DecodeField + + return SpecificDecoder + + +def _ModifiedDecoder(wire_type, decode_value, modify_value): + """Like SimpleDecoder but additionally invokes modify_value on every value + + before storing it. Usually modify_value is ZigZagDecode. + """ + + # Reusing _SimpleDecoder is slightly slower than copying a bunch of code, but + # not enough to make a significant difference. + + def InnerDecode(buffer, pos): + result, new_pos = decode_value(buffer, pos) + return (modify_value(result), new_pos) + + return _SimpleDecoder(wire_type, InnerDecode) + + +def _StructPackDecoder(wire_type, format): + """Return a constructor for a decoder for a fixed-width field. + + Args: + wire_type: The field's wire type. + format: The format string to pass to struct.unpack(). + """ + + value_size = struct.calcsize(format) + local_unpack = struct.unpack + + # Reusing _SimpleDecoder is slightly slower than copying a bunch of code, but + # not enough to make a significant difference. + + # Note that we expect someone up-stack to catch struct.error and convert + # it to _DecodeError -- this way we don't have to set up exception- + # handling blocks every time we parse one value. + + def InnerDecode(buffer, pos): + new_pos = pos + value_size + result = local_unpack(format, buffer[pos:new_pos])[0] + return (result, new_pos) + + return _SimpleDecoder(wire_type, InnerDecode) + + +def _FloatDecoder(): + """Returns a decoder for a float field. + + This code works around a bug in struct.unpack for non-finite 32-bit + floating-point values. + """ + + local_unpack = struct.unpack + + def InnerDecode(buffer, pos): + """Decode serialized float to a float and new position. + + Args: + buffer: memoryview of the serialized bytes + pos: int, position in the memory view to start at. + + Returns: + Tuple[float, int] of the deserialized float value and new position + in the serialized data. + """ + # We expect a 32-bit value in little-endian byte order. Bit 1 is the sign + # bit, bits 2-9 represent the exponent, and bits 10-32 are the significand. + new_pos = pos + 4 + float_bytes = buffer[pos:new_pos].tobytes() + + # If this value has all its exponent bits set, then it's non-finite. + # In Python 2.4, struct.unpack will convert it to a finite 64-bit value. + # To avoid that, we parse it specially. + if float_bytes[3:4] in b'\x7F\xFF' and float_bytes[2:3] >= b'\x80': + # If at least one significand bit is set... + if float_bytes[0:3] != b'\x00\x00\x80': + return (math.nan, new_pos) + # If sign bit is set... + if float_bytes[3:4] == b'\xFF': + return (-math.inf, new_pos) + return (math.inf, new_pos) + + # Note that we expect someone up-stack to catch struct.error and convert + # it to _DecodeError -- this way we don't have to set up exception- + # handling blocks every time we parse one value. + result = local_unpack('= b'\xF0') + and (double_bytes[0:7] != b'\x00\x00\x00\x00\x00\x00\xF0') + ): + return (math.nan, new_pos) + + # Note that we expect someone up-stack to catch struct.error and convert + # it to _DecodeError -- this way we don't have to set up exception- + # handling blocks every time we parse one value. + result = local_unpack(' end: + raise _DecodeError('Truncated message.') + while pos < endpoint: + value_start_pos = pos + element, pos = _DecodeSignedVarint32(buffer, pos) + # pylint: disable=protected-access + if element in enum_type.values_by_number: + value.append(element) + else: + if not message._unknown_fields: + message._unknown_fields = [] + tag_bytes = encoder.TagBytes( + field_number, wire_format.WIRETYPE_VARINT + ) + + message._unknown_fields.append( + (tag_bytes, buffer[value_start_pos:pos].tobytes()) + ) + # pylint: enable=protected-access + if pos > endpoint: + if element in enum_type.values_by_number: + del value[-1] # Discard corrupt value. + else: + del message._unknown_fields[-1] + # pylint: enable=protected-access + raise _DecodeError('Packed element was truncated.') + return pos + + return DecodePackedField + elif is_repeated: + tag_bytes = encoder.TagBytes(field_number, wire_format.WIRETYPE_VARINT) + tag_len = len(tag_bytes) + + def DecodeRepeatedField( + buffer, pos, end, message, field_dict, current_depth=0 + ): + """Decode serialized repeated enum to its value and a new position. + + Args: + buffer: memoryview of the serialized bytes. + pos: int, position in the memory view to start at. + end: int, end position of serialized data + message: Message object to store unknown fields in + field_dict: Map[Descriptor, Any] to store decoded values in. + + Returns: + int, new position in serialized data. + """ + del current_depth # unused + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + element, new_pos = _DecodeSignedVarint32(buffer, pos) + # pylint: disable=protected-access + if element in enum_type.values_by_number: + value.append(element) + else: + if not message._unknown_fields: + message._unknown_fields = [] + message._unknown_fields.append( + (tag_bytes, buffer[pos:new_pos].tobytes()) + ) + # pylint: enable=protected-access + # Predict that the next tag is another copy of the same repeated + # field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos >= end: + # Prediction failed. Return. + if new_pos > end: + raise _DecodeError('Truncated message.') + return new_pos + + return DecodeRepeatedField + else: + + def DecodeField(buffer, pos, end, message, field_dict, current_depth=0): + """Decode serialized repeated enum to its value and a new position. + + Args: + buffer: memoryview of the serialized bytes. + pos: int, position in the memory view to start at. + end: int, end position of serialized data + message: Message object to store unknown fields in + field_dict: Map[Descriptor, Any] to store decoded values in. + + Returns: + int, new position in serialized data. + """ + del current_depth # unused + value_start_pos = pos + enum_value, pos = _DecodeSignedVarint32(buffer, pos) + if pos > end: + raise _DecodeError('Truncated message.') + if clear_if_default and IsDefaultScalarValue(enum_value): + field_dict.pop(key, None) + return pos + # pylint: disable=protected-access + if enum_value in enum_type.values_by_number: + field_dict[key] = enum_value + else: + if not message._unknown_fields: + message._unknown_fields = [] + tag_bytes = encoder.TagBytes(field_number, wire_format.WIRETYPE_VARINT) + message._unknown_fields.append( + (tag_bytes, buffer[value_start_pos:pos].tobytes()) + ) + # pylint: enable=protected-access + return pos + + return DecodeField + + +# -------------------------------------------------------------------- + +Int32Decoder = _SimpleDecoder( + wire_format.WIRETYPE_VARINT, _DecodeSignedVarint32 +) + +Int64Decoder = _SimpleDecoder(wire_format.WIRETYPE_VARINT, _DecodeSignedVarint) + +UInt32Decoder = _SimpleDecoder(wire_format.WIRETYPE_VARINT, _DecodeVarint32) +UInt64Decoder = _SimpleDecoder(wire_format.WIRETYPE_VARINT, _DecodeVarint) + +SInt32Decoder = _ModifiedDecoder( + wire_format.WIRETYPE_VARINT, _DecodeVarint32, wire_format.ZigZagDecode +) +SInt64Decoder = _ModifiedDecoder( + wire_format.WIRETYPE_VARINT, _DecodeVarint, wire_format.ZigZagDecode +) + +# Note that Python conveniently guarantees that when using the '<' prefix on +# formats, they will also have the same size across all platforms (as opposed +# to without the prefix, where their sizes depend on the C compiler's basic +# type sizes). +Fixed32Decoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED32, ' end: + raise _DecodeError('Truncated string.') + value.append(_ConvertToUnicode(buffer[pos:new_pos])) + # Predict that the next tag is another copy of the same repeated field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos == end: + # Prediction failed. Return. + return new_pos + + return DecodeRepeatedField + else: + + def DecodeField(buffer, pos, end, message, field_dict, current_depth=0): + del current_depth # unused + size, pos = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated string.') + if clear_if_default and IsDefaultScalarValue(size): + field_dict.pop(key, None) + else: + field_dict[key] = _ConvertToUnicode(buffer[pos:new_pos]) + return new_pos + + return DecodeField + + +def BytesDecoder( + field_number, + is_repeated, + is_packed, + key, + new_default, + clear_if_default=False, +): + """Returns a decoder for a bytes field.""" + + local_DecodeVarint = _DecodeVarint + + assert not is_packed + if is_repeated: + tag_bytes = encoder.TagBytes( + field_number, wire_format.WIRETYPE_LENGTH_DELIMITED + ) + tag_len = len(tag_bytes) + + def DecodeRepeatedField( + buffer, pos, end, message, field_dict, current_depth=0 + ): + del current_depth # unused + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + size, pos = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated string.') + value.append(buffer[pos:new_pos].tobytes()) + # Predict that the next tag is another copy of the same repeated field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos == end: + # Prediction failed. Return. + return new_pos + + return DecodeRepeatedField + else: + + def DecodeField(buffer, pos, end, message, field_dict, current_depth=0): + del current_depth # unused + size, pos = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated string.') + if clear_if_default and IsDefaultScalarValue(size): + field_dict.pop(key, None) + else: + field_dict[key] = buffer[pos:new_pos].tobytes() + return new_pos + + return DecodeField + + +def GroupDecoder(field_number, is_repeated, is_packed, key, new_default): + """Returns a decoder for a group field.""" + + end_tag_bytes = encoder.TagBytes(field_number, wire_format.WIRETYPE_END_GROUP) + end_tag_len = len(end_tag_bytes) + + assert not is_packed + if is_repeated: + tag_bytes = encoder.TagBytes(field_number, wire_format.WIRETYPE_START_GROUP) + tag_len = len(tag_bytes) + + def DecodeRepeatedField( + buffer, pos, end, message, field_dict, current_depth=0 + ): + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + # Read sub-message. + current_depth += 1 + if current_depth > _recursion_limit: + raise _DecodeError( + 'Error parsing message: too many levels of nesting.' + ) + pos = value.add()._InternalParse(buffer, pos, end, current_depth) + current_depth -= 1 + # Read end tag. + new_pos = pos + end_tag_len + if buffer[pos:new_pos] != end_tag_bytes or new_pos > end: + raise _DecodeError('Missing group end tag.') + # Predict that the next tag is another copy of the same repeated field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos == end: + # Prediction failed. Return. + return new_pos + + return DecodeRepeatedField + else: + + def DecodeField(buffer, pos, end, message, field_dict, current_depth=0): + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + # Read sub-message. + current_depth += 1 + if current_depth > _recursion_limit: + raise _DecodeError('Error parsing message: too many levels of nesting.') + pos = value._InternalParse(buffer, pos, end, current_depth) + current_depth -= 1 + # Read end tag. + new_pos = pos + end_tag_len + if buffer[pos:new_pos] != end_tag_bytes or new_pos > end: + raise _DecodeError('Missing group end tag.') + return new_pos + + return DecodeField + + +def MessageDecoder(field_number, is_repeated, is_packed, key, new_default): + """Returns a decoder for a message field.""" + + local_DecodeVarint = _DecodeVarint + + assert not is_packed + if is_repeated: + tag_bytes = encoder.TagBytes( + field_number, wire_format.WIRETYPE_LENGTH_DELIMITED + ) + tag_len = len(tag_bytes) + + def DecodeRepeatedField( + buffer, pos, end, message, field_dict, current_depth=0 + ): + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + # Read length. + size, pos = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated message.') + # Read sub-message. + current_depth += 1 + if current_depth > _recursion_limit: + raise _DecodeError( + 'Error parsing message: too many levels of nesting.' + ) + if ( + value.add()._InternalParse(buffer, pos, new_pos, current_depth) + != new_pos + ): + # The only reason _InternalParse would return early is if it + # encountered an end-group tag. + raise _DecodeError('Unexpected end-group tag.') + current_depth -= 1 + # Predict that the next tag is another copy of the same repeated field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos == end: + # Prediction failed. Return. + return new_pos + + return DecodeRepeatedField + else: + + def DecodeField(buffer, pos, end, message, field_dict, current_depth=0): + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + # Read length. + size, pos = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated message.') + # Read sub-message. + current_depth += 1 + if current_depth > _recursion_limit: + raise _DecodeError('Error parsing message: too many levels of nesting.') + if value._InternalParse(buffer, pos, new_pos, current_depth) != new_pos: + # The only reason _InternalParse would return early is if it encountered + # an end-group tag. + raise _DecodeError('Unexpected end-group tag.') + current_depth -= 1 + return new_pos + + return DecodeField + + +# -------------------------------------------------------------------- + +MESSAGE_SET_ITEM_TAG = encoder.TagBytes(1, wire_format.WIRETYPE_START_GROUP) + + +def MessageSetItemDecoder(descriptor): + """Returns a decoder for a MessageSet item. + + The parameter is the message Descriptor. + + The message set message looks like this: + message MessageSet { + repeated group Item = 1 { + required int32 type_id = 2; + required string message = 3; + } + } + """ + + type_id_tag_bytes = encoder.TagBytes(2, wire_format.WIRETYPE_VARINT) + message_tag_bytes = encoder.TagBytes(3, wire_format.WIRETYPE_LENGTH_DELIMITED) + item_end_tag_bytes = encoder.TagBytes(1, wire_format.WIRETYPE_END_GROUP) + + local_ReadTag = ReadTag + local_DecodeVarint = _DecodeVarint + + def DecodeItem(buffer, pos, end, message, field_dict, current_depth=0): + """Decode serialized message set to its value and new position. + + Args: + buffer: memoryview of the serialized bytes. + pos: int, position in the memory view to start at. + end: int, end position of serialized data + message: Message object to store unknown fields in + field_dict: Map[Descriptor, Any] to store decoded values in. + + Returns: + int, new position in serialized data. + """ + message_set_item_start = pos + type_id = -1 + message_start = -1 + message_end = -1 + + # Technically, type_id and message can appear in any order, so we need + # a little loop here. + while 1: + tag_bytes, pos = local_ReadTag(buffer, pos) + if tag_bytes == type_id_tag_bytes: + type_id, pos = local_DecodeVarint(buffer, pos) + elif tag_bytes == message_tag_bytes: + size, message_start = local_DecodeVarint(buffer, pos) + pos = message_end = message_start + size + elif tag_bytes == item_end_tag_bytes: + break + else: + field_number, wire_type = DecodeTag(tag_bytes) + _, pos = _DecodeUnknownField(buffer, pos, end, field_number, wire_type) + if pos == -1: + raise _DecodeError('Unexpected end-group tag.') + + if pos > end: + raise _DecodeError('Truncated message.') + + if type_id == -1: + raise _DecodeError('MessageSet item missing type_id.') + if message_start == -1: + raise _DecodeError('MessageSet item missing message.') + + extension = message.Extensions._FindExtensionByNumber(type_id) + # pylint: disable=protected-access + if extension is not None: + value = field_dict.get(extension) + if value is None: + message_type = extension.message_type + if not hasattr(message_type, '_concrete_class'): + message_factory.GetMessageClass(message_type) + value = field_dict.setdefault(extension, message_type._concrete_class()) + current_depth += 1 + if current_depth > _recursion_limit: + raise _DecodeError('Error parsing message: too many levels of nesting.') + if ( + value._InternalParse( + buffer, message_start, message_end, current_depth + ) + != message_end + ): + # The only reason _InternalParse would return early is if it encountered + # an end-group tag. + raise _DecodeError('Unexpected end-group tag.') + current_depth -= 1 + else: + if not message._unknown_fields: + message._unknown_fields = [] + message._unknown_fields.append( + (MESSAGE_SET_ITEM_TAG, buffer[message_set_item_start:pos].tobytes()) + ) + # pylint: enable=protected-access + + return pos + + return DecodeItem + + +def UnknownMessageSetItemDecoder(): + """Returns a decoder for a Unknown MessageSet item.""" + + type_id_tag_bytes = encoder.TagBytes(2, wire_format.WIRETYPE_VARINT) + message_tag_bytes = encoder.TagBytes(3, wire_format.WIRETYPE_LENGTH_DELIMITED) + item_end_tag_bytes = encoder.TagBytes(1, wire_format.WIRETYPE_END_GROUP) + + def DecodeUnknownItem(buffer): + pos = 0 + end = len(buffer) + message_start = -1 + message_end = -1 + while 1: + tag_bytes, pos = ReadTag(buffer, pos) + if tag_bytes == type_id_tag_bytes: + type_id, pos = _DecodeVarint(buffer, pos) + elif tag_bytes == message_tag_bytes: + size, message_start = _DecodeVarint(buffer, pos) + pos = message_end = message_start + size + elif tag_bytes == item_end_tag_bytes: + break + else: + field_number, wire_type = DecodeTag(tag_bytes) + _, pos = _DecodeUnknownField(buffer, pos, end, field_number, wire_type) + if pos == -1: + raise _DecodeError('Unexpected end-group tag.') + + if pos > end: + raise _DecodeError('Truncated message.') + + if type_id == -1: + raise _DecodeError('MessageSet item missing type_id.') + if message_start == -1: + raise _DecodeError('MessageSet item missing message.') + + return (type_id, buffer[message_start:message_end].tobytes()) + + return DecodeUnknownItem + + +# -------------------------------------------------------------------- + + +def MapDecoder(field_descriptor, new_default, is_message_map): + """Returns a decoder for a map field.""" + + key = field_descriptor + tag_bytes = encoder.TagBytes( + field_descriptor.number, wire_format.WIRETYPE_LENGTH_DELIMITED + ) + tag_len = len(tag_bytes) + local_DecodeVarint = _DecodeVarint + # Can't read _concrete_class yet; might not be initialized. + message_type = field_descriptor.message_type + + def DecodeMap(buffer, pos, end, message, field_dict, current_depth=0): + submsg = message_type._concrete_class() + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + # Read length. + size, pos = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated message.') + # Read sub-message. + submsg.Clear() + current_depth += 1 + if current_depth > _recursion_limit: + raise _DecodeError('Error parsing message: too many levels of nesting.') + if submsg._InternalParse(buffer, pos, new_pos, current_depth) != new_pos: + # The only reason _InternalParse would return early is if it + # encountered an end-group tag. + raise _DecodeError('Unexpected end-group tag.') + current_depth -= 1 + + if is_message_map: + value[submsg.key].CopyFrom(submsg.value) + else: + value[submsg.key] = submsg.value + + # Predict that the next tag is another copy of the same repeated field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos == end: + # Prediction failed. Return. + return new_pos + + return DecodeMap + + +def _DecodeFixed64(buffer, pos): + """Decode a fixed64.""" + new_pos = pos + 8 + return (struct.unpack('= _recursion_limit: + raise _DecodeError('Error parsing message: too many levels of nesting.') + data, pos = _DecodeUnknownFieldSet(buffer, pos, end_pos, current_depth) + current_depth -= 1 + # Check end tag. + if buffer[pos - len(end_tag_bytes) : pos] != end_tag_bytes: + raise _DecodeError('Missing group end tag.') + elif wire_type == wire_format.WIRETYPE_END_GROUP: + return (0, -1) + else: + raise _DecodeError('Wrong wire type in tag.') + + if pos > end_pos: + raise _DecodeError('Truncated message.') + + return (data, pos) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/encoder.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/encoder.py new file mode 100755 index 0000000..f02b486 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/encoder.py @@ -0,0 +1,931 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Code for encoding protocol message primitives. + +Contains the logic for encoding every logical protocol field type +into one of the 5 physical wire types. + +This code is designed to push the Python interpreter's performance to the +limits. + +The basic idea is that at startup time, for every field (i.e. every +FieldDescriptor) we construct two functions: a "sizer" and an "encoder". The +sizer takes a value of this field's type and computes its byte size. The +encoder takes a writer function and a value. It encodes the value into byte +strings and invokes the writer function to write those strings. Typically the +writer function is the write() method of a BytesIO. + +We try to do as much work as possible when constructing the writer and the +sizer rather than when calling them. In particular: +* We copy any needed global functions to local variables, so that we do not need + to do costly global table lookups at runtime. +* Similarly, we try to do any attribute lookups at startup time if possible. +* Every field's tag is encoded to bytes at startup, since it can't change at + runtime. +* Whatever component of the field size we can compute at startup, we do. +* We *avoid* sharing code if doing so would make the code slower and not sharing + does not burden us too much. For example, encoders for repeated fields do + not just call the encoders for singular fields in a loop because this would + add an extra function call overhead for every loop iteration; instead, we + manually inline the single-value encoder into the loop. +* If a Python function lacks a return statement, Python actually generates + instructions to pop the result of the last statement off the stack, push + None onto the stack, and then return that. If we really don't care what + value is returned, then we can save two instructions by returning the + result of the last statement. It looks funny but it helps. +* We assume that type and bounds checking has happened at a higher level. +""" + +__author__ = 'kenton@google.com (Kenton Varda)' + +import struct + +from google.protobuf.internal import wire_format + +# This will overflow and thus become IEEE-754 "infinity". We would use +# "float('inf')" but it doesn't work on Windows pre-Python-2.6. +_POS_INF = 1e10000 +_NEG_INF = -_POS_INF + + +def _VarintSize(value): + """Compute the size of a varint value.""" + if value <= 0x7F: + return 1 + if value <= 0x3FFF: + return 2 + if value <= 0x1FFFFF: + return 3 + if value <= 0xFFFFFFF: + return 4 + if value <= 0x7FFFFFFFF: + return 5 + if value <= 0x3FFFFFFFFFF: + return 6 + if value <= 0x1FFFFFFFFFFFF: + return 7 + if value <= 0xFFFFFFFFFFFFFF: + return 8 + if value <= 0x7FFFFFFFFFFFFFFF: + return 9 + return 10 + + +def _SignedVarintSize(value): + """Compute the size of a signed varint value.""" + if value < 0: + return 10 + if value <= 0x7F: + return 1 + if value <= 0x3FFF: + return 2 + if value <= 0x1FFFFF: + return 3 + if value <= 0xFFFFFFF: + return 4 + if value <= 0x7FFFFFFFF: + return 5 + if value <= 0x3FFFFFFFFFF: + return 6 + if value <= 0x1FFFFFFFFFFFF: + return 7 + if value <= 0xFFFFFFFFFFFFFF: + return 8 + if value <= 0x7FFFFFFFFFFFFFFF: + return 9 + return 10 + + +def _TagSize(field_number): + """Returns the number of bytes required to serialize a tag with this field + + number. + """ + # Just pass in type 0, since the type won't affect the tag+type size. + return _VarintSize(wire_format.PackTag(field_number, 0)) + + +# -------------------------------------------------------------------- +# In this section we define some generic sizers. Each of these functions +# takes parameters specific to a particular field type, e.g. int32 or fixed64. +# It returns another function which in turn takes parameters specific to a +# particular field, e.g. the field number and whether it is repeated or packed. +# Look at the next section to see how these are used. + + +def _SimpleSizer(compute_value_size): + """A sizer which uses the function compute_value_size to compute the size of + + each value. Typically compute_value_size is _VarintSize. + """ + + def SpecificSizer(field_number, is_repeated, is_packed): + tag_size = _TagSize(field_number) + if is_packed: + local_VarintSize = _VarintSize + + def PackedFieldSize(value): + result = 0 + for element in value: + result += compute_value_size(element) + return result + local_VarintSize(result) + tag_size + + return PackedFieldSize + elif is_repeated: + + def RepeatedFieldSize(value): + result = tag_size * len(value) + for element in value: + result += compute_value_size(element) + return result + + return RepeatedFieldSize + else: + + def FieldSize(value): + return tag_size + compute_value_size(value) + + return FieldSize + + return SpecificSizer + + +def _ModifiedSizer(compute_value_size, modify_value): + """Like SimpleSizer, but modify_value is invoked on each value before it is + + passed to compute_value_size. modify_value is typically ZigZagEncode. + """ + + def SpecificSizer(field_number, is_repeated, is_packed): + tag_size = _TagSize(field_number) + if is_packed: + local_VarintSize = _VarintSize + + def PackedFieldSize(value): + result = 0 + for element in value: + result += compute_value_size(modify_value(element)) + return result + local_VarintSize(result) + tag_size + + return PackedFieldSize + elif is_repeated: + + def RepeatedFieldSize(value): + result = tag_size * len(value) + for element in value: + result += compute_value_size(modify_value(element)) + return result + + return RepeatedFieldSize + else: + + def FieldSize(value): + return tag_size + compute_value_size(modify_value(value)) + + return FieldSize + + return SpecificSizer + + +def _FixedSizer(value_size): + """Like _SimpleSizer except for a fixed-size field. + + The input is the size of one value. + """ + + def SpecificSizer(field_number, is_repeated, is_packed): + tag_size = _TagSize(field_number) + if is_packed: + local_VarintSize = _VarintSize + + def PackedFieldSize(value): + result = len(value) * value_size + return result + local_VarintSize(result) + tag_size + + return PackedFieldSize + elif is_repeated: + element_size = value_size + tag_size + + def RepeatedFieldSize(value): + return len(value) * element_size + + return RepeatedFieldSize + else: + field_size = value_size + tag_size + + def FieldSize(value): + return field_size + + return FieldSize + + return SpecificSizer + + +# ==================================================================== +# Here we declare a sizer constructor for each field type. Each "sizer +# constructor" is a function that takes (field_number, is_repeated, is_packed) +# as parameters and returns a sizer, which in turn takes a field value as +# a parameter and returns its encoded size. + +Int32Sizer = Int64Sizer = EnumSizer = _SimpleSizer(_SignedVarintSize) + +UInt32Sizer = UInt64Sizer = _SimpleSizer(_VarintSize) + +SInt32Sizer = SInt64Sizer = _ModifiedSizer( + _SignedVarintSize, wire_format.ZigZagEncode +) + +Fixed32Sizer = SFixed32Sizer = FloatSizer = _FixedSizer(4) +Fixed64Sizer = SFixed64Sizer = DoubleSizer = _FixedSizer(8) + +BoolSizer = _FixedSizer(1) + + +def StringSizer(field_number, is_repeated, is_packed): + """Returns a sizer for a string field.""" + + tag_size = _TagSize(field_number) + local_VarintSize = _VarintSize + local_len = len + assert not is_packed + if is_repeated: + + def RepeatedFieldSize(value): + result = tag_size * len(value) + for element in value: + l = local_len(element.encode('utf-8')) + result += local_VarintSize(l) + l + return result + + return RepeatedFieldSize + else: + + def FieldSize(value): + l = local_len(value.encode('utf-8')) + return tag_size + local_VarintSize(l) + l + + return FieldSize + + +def BytesSizer(field_number, is_repeated, is_packed): + """Returns a sizer for a bytes field.""" + + tag_size = _TagSize(field_number) + local_VarintSize = _VarintSize + local_len = len + assert not is_packed + if is_repeated: + + def RepeatedFieldSize(value): + result = tag_size * len(value) + for element in value: + l = local_len(element) + result += local_VarintSize(l) + l + return result + + return RepeatedFieldSize + else: + + def FieldSize(value): + l = local_len(value) + return tag_size + local_VarintSize(l) + l + + return FieldSize + + +def GroupSizer(field_number, is_repeated, is_packed): + """Returns a sizer for a group field.""" + + tag_size = _TagSize(field_number) * 2 + assert not is_packed + if is_repeated: + + def RepeatedFieldSize(value): + result = tag_size * len(value) + for element in value: + result += element.ByteSize() + return result + + return RepeatedFieldSize + else: + + def FieldSize(value): + return tag_size + value.ByteSize() + + return FieldSize + + +def MessageSizer(field_number, is_repeated, is_packed): + """Returns a sizer for a message field.""" + + tag_size = _TagSize(field_number) + local_VarintSize = _VarintSize + assert not is_packed + if is_repeated: + + def RepeatedFieldSize(value): + result = tag_size * len(value) + for element in value: + l = element.ByteSize() + result += local_VarintSize(l) + l + return result + + return RepeatedFieldSize + else: + + def FieldSize(value): + l = value.ByteSize() + return tag_size + local_VarintSize(l) + l + + return FieldSize + + +# -------------------------------------------------------------------- +# MessageSet is special: it needs custom logic to compute its size properly. + + +def MessageSetItemSizer(field_number): + """Returns a sizer for extensions of MessageSet. + + The message set message looks like this: + message MessageSet { + repeated group Item = 1 { + required int32 type_id = 2; + required string message = 3; + } + } + """ + static_size = ( + _TagSize(1) * 2 + _TagSize(2) + _VarintSize(field_number) + _TagSize(3) + ) + local_VarintSize = _VarintSize + + def FieldSize(value): + l = value.ByteSize() + return static_size + local_VarintSize(l) + l + + return FieldSize + + +# -------------------------------------------------------------------- +# Map is special: it needs custom logic to compute its size properly. + + +def MapSizer(field_descriptor, key_sizer, value_sizer): + """Returns a sizer for a map field.""" + + def FieldSize(map_value): + tag_size = _TagSize(field_descriptor.number) + local_VarintSize = _VarintSize + + total = 0 + for key in map_value: + val = map_value[key] + entry_size = key_sizer(key) + value_sizer(val) + total += tag_size + local_VarintSize(entry_size) + entry_size + + return total + + return FieldSize + + +# ==================================================================== +# Encoders! + + +def _VarintEncoder(): + """Return an encoder for a basic varint value (does not include tag).""" + + local_int2byte = struct.Struct('>B').pack + + def EncodeVarint(write, value, unused_deterministic=None): + bits = value & 0x7F + value >>= 7 + while value: + write(local_int2byte(0x80 | bits)) + bits = value & 0x7F + value >>= 7 + return write(local_int2byte(bits)) + + return EncodeVarint + + +def _SignedVarintEncoder(): + """Return an encoder for a basic signed varint value (does not include + + tag). + """ + + local_int2byte = struct.Struct('>B').pack + + def EncodeSignedVarint(write, value, unused_deterministic=None): + if value < 0: + value += 1 << 64 + bits = value & 0x7F + value >>= 7 + while value: + write(local_int2byte(0x80 | bits)) + bits = value & 0x7F + value >>= 7 + return write(local_int2byte(bits)) + + return EncodeSignedVarint + + +_EncodeVarint = _VarintEncoder() +_EncodeSignedVarint = _SignedVarintEncoder() + + +def _VarintBytes(value): + """Encode the given integer as a varint and return the bytes. + + This is only called at startup time so it doesn't need to be fast. + """ + + pieces = [] + _EncodeVarint(pieces.append, value, True) + return b''.join(pieces) + + +def TagBytes(field_number, wire_type): + """Encode the given tag and return the bytes. Only called at startup.""" + + return bytes(_VarintBytes(wire_format.PackTag(field_number, wire_type))) + + +# -------------------------------------------------------------------- +# As with sizers (see above), we have a number of common encoder +# implementations. + + +def _SimpleEncoder(wire_type, encode_value, compute_value_size): + """Return a constructor for an encoder for fields of a particular type. + + Args: + wire_type: The field's wire type, for encoding tags. + encode_value: A function which encodes an individual value, e.g. + _EncodeVarint(). + compute_value_size: A function which computes the size of an individual + value, e.g. _VarintSize(). + """ + + def SpecificEncoder(field_number, is_repeated, is_packed): + if is_packed: + tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) + local_EncodeVarint = _EncodeVarint + + def EncodePackedField(write, value, deterministic): + write(tag_bytes) + size = 0 + for element in value: + size += compute_value_size(element) + local_EncodeVarint(write, size, deterministic) + for element in value: + encode_value(write, element, deterministic) + + return EncodePackedField + elif is_repeated: + tag_bytes = TagBytes(field_number, wire_type) + + def EncodeRepeatedField(write, value, deterministic): + for element in value: + write(tag_bytes) + encode_value(write, element, deterministic) + + return EncodeRepeatedField + else: + tag_bytes = TagBytes(field_number, wire_type) + + def EncodeField(write, value, deterministic): + write(tag_bytes) + return encode_value(write, value, deterministic) + + return EncodeField + + return SpecificEncoder + + +def _ModifiedEncoder(wire_type, encode_value, compute_value_size, modify_value): + """Like SimpleEncoder but additionally invokes modify_value on every value + + before passing it to encode_value. Usually modify_value is ZigZagEncode. + """ + + def SpecificEncoder(field_number, is_repeated, is_packed): + if is_packed: + tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) + local_EncodeVarint = _EncodeVarint + + def EncodePackedField(write, value, deterministic): + write(tag_bytes) + size = 0 + for element in value: + size += compute_value_size(modify_value(element)) + local_EncodeVarint(write, size, deterministic) + for element in value: + encode_value(write, modify_value(element), deterministic) + + return EncodePackedField + elif is_repeated: + tag_bytes = TagBytes(field_number, wire_type) + + def EncodeRepeatedField(write, value, deterministic): + for element in value: + write(tag_bytes) + encode_value(write, modify_value(element), deterministic) + + return EncodeRepeatedField + else: + tag_bytes = TagBytes(field_number, wire_type) + + def EncodeField(write, value, deterministic): + write(tag_bytes) + return encode_value(write, modify_value(value), deterministic) + + return EncodeField + + return SpecificEncoder + + +def _StructPackEncoder(wire_type, format): + """Return a constructor for an encoder for a fixed-width field. + + Args: + wire_type: The field's wire type, for encoding tags. + format: The format string to pass to struct.pack(). + """ + + value_size = struct.calcsize(format) + + def SpecificEncoder(field_number, is_repeated, is_packed): + local_struct_pack = struct.pack + if is_packed: + tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) + local_EncodeVarint = _EncodeVarint + + def EncodePackedField(write, value, deterministic): + write(tag_bytes) + local_EncodeVarint(write, len(value) * value_size, deterministic) + for element in value: + write(local_struct_pack(format, element)) + + return EncodePackedField + elif is_repeated: + tag_bytes = TagBytes(field_number, wire_type) + + def EncodeRepeatedField(write, value, unused_deterministic=None): + for element in value: + write(tag_bytes) + write(local_struct_pack(format, element)) + + return EncodeRepeatedField + else: + tag_bytes = TagBytes(field_number, wire_type) + + def EncodeField(write, value, unused_deterministic=None): + write(tag_bytes) + return write(local_struct_pack(format, value)) + + return EncodeField + + return SpecificEncoder + + +def _FloatingPointEncoder(wire_type, format): + """Return a constructor for an encoder for float fields. + + This is like StructPackEncoder, but catches errors that may be due to + passing non-finite floating-point values to struct.pack, and makes a + second attempt to encode those values. + + Args: + wire_type: The field's wire type, for encoding tags. + format: The format string to pass to struct.pack(). + """ + + value_size = struct.calcsize(format) + if value_size == 4: + + def EncodeNonFiniteOrRaise(write, value): + # Remember that the serialized form uses little-endian byte order. + if value == _POS_INF: + write(b'\x00\x00\x80\x7F') + elif value == _NEG_INF: + write(b'\x00\x00\x80\xFF') + elif value != value: # NaN + write(b'\x00\x00\xC0\x7F') + else: + raise + + elif value_size == 8: + + def EncodeNonFiniteOrRaise(write, value): + if value == _POS_INF: + write(b'\x00\x00\x00\x00\x00\x00\xF0\x7F') + elif value == _NEG_INF: + write(b'\x00\x00\x00\x00\x00\x00\xF0\xFF') + elif value != value: # NaN + write(b'\x00\x00\x00\x00\x00\x00\xF8\x7F') + else: + raise + + else: + raise ValueError( + "Can't encode floating-point values that are " + '%d bytes long (only 4 or 8)' % value_size + ) + + def SpecificEncoder(field_number, is_repeated, is_packed): + local_struct_pack = struct.pack + if is_packed: + tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) + local_EncodeVarint = _EncodeVarint + + def EncodePackedField(write, value, deterministic): + write(tag_bytes) + local_EncodeVarint(write, len(value) * value_size, deterministic) + for element in value: + # This try/except block is going to be faster than any code that + # we could write to check whether element is finite. + try: + write(local_struct_pack(format, element)) + except SystemError: + EncodeNonFiniteOrRaise(write, element) + + return EncodePackedField + elif is_repeated: + tag_bytes = TagBytes(field_number, wire_type) + + def EncodeRepeatedField(write, value, unused_deterministic=None): + for element in value: + write(tag_bytes) + try: + write(local_struct_pack(format, element)) + except SystemError: + EncodeNonFiniteOrRaise(write, element) + + return EncodeRepeatedField + else: + tag_bytes = TagBytes(field_number, wire_type) + + def EncodeField(write, value, unused_deterministic=None): + write(tag_bytes) + try: + write(local_struct_pack(format, value)) + except SystemError: + EncodeNonFiniteOrRaise(write, value) + + return EncodeField + + return SpecificEncoder + + +# ==================================================================== +# Here we declare an encoder constructor for each field type. These work +# very similarly to sizer constructors, described earlier. + +Int32Encoder = Int64Encoder = EnumEncoder = _SimpleEncoder( + wire_format.WIRETYPE_VARINT, _EncodeSignedVarint, _SignedVarintSize +) + +UInt32Encoder = UInt64Encoder = _SimpleEncoder( + wire_format.WIRETYPE_VARINT, _EncodeVarint, _VarintSize +) + +SInt32Encoder = SInt64Encoder = _ModifiedEncoder( + wire_format.WIRETYPE_VARINT, + _EncodeVarint, + _VarintSize, + wire_format.ZigZagEncode, +) + +# Note that Python conveniently guarantees that when using the '<' prefix on +# formats, they will also have the same size across all platforms (as opposed +# to without the prefix, where their sizes depend on the C compiler's basic +# type sizes). +Fixed32Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED32, ' str + ValueType = int + + def __init__(self, enum_type): + """Inits EnumTypeWrapper with an EnumDescriptor.""" + self._enum_type = enum_type + self.DESCRIPTOR = enum_type # pylint: disable=invalid-name + + def Name(self, number): # pylint: disable=invalid-name + """Returns a string containing the name of an enum value.""" + try: + return self._enum_type.values_by_number[number].name + except KeyError: + pass # fall out to break exception chaining + + if not isinstance(number, int): + raise TypeError( + 'Enum value for {} must be an int, but got {} {!r}.'.format( + self._enum_type.name, type(number), number + ) + ) + else: + # repr here to handle the odd case when you pass in a boolean. + raise ValueError( + 'Enum {} has no name defined for value {!r}'.format( + self._enum_type.name, number + ) + ) + + def Value(self, name): # pylint: disable=invalid-name + """Returns the value corresponding to the given enum name.""" + try: + return self._enum_type.values_by_name[name].number + except KeyError: + pass # fall out to break exception chaining + raise ValueError( + 'Enum {} has no value defined for name {!r}'.format( + self._enum_type.name, name + ) + ) + + def keys(self): + """Return a list of the string names in the enum. + + Returns: + A list of strs, in the order they were defined in the .proto file. + """ + + return [ + value_descriptor.name for value_descriptor in self._enum_type.values + ] + + def values(self): + """Return a list of the integer values in the enum. + + Returns: + A list of ints, in the order they were defined in the .proto file. + """ + + return [ + value_descriptor.number for value_descriptor in self._enum_type.values + ] + + def items(self): + """Return a list of the (name, value) pairs of the enum. + + Returns: + A list of (str, int) pairs, in the order they were defined + in the .proto file. + """ + return [ + (value_descriptor.name, value_descriptor.number) + for value_descriptor in self._enum_type.values + ] + + def __getattr__(self, name): + """Returns the value corresponding to the given enum name.""" + try: + return ( + super(EnumTypeWrapper, self) + .__getattribute__('_enum_type') + .values_by_name[name] + .number + ) + except KeyError: + pass # fall out to break exception chaining + raise AttributeError( + 'Enum {} has no value defined for name {!r}'.format( + self._enum_type.name, name + ) + ) + + def __or__(self, other): + """Returns the union type of self and other.""" + if sys.version_info >= (3, 10): + return type(self) | other + else: + raise NotImplementedError( + 'You may not use | on EnumTypes (or classes) below python 3.10' + ) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/extension_dict.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/extension_dict.py new file mode 100755 index 0000000..5e6a132 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/extension_dict.py @@ -0,0 +1,209 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Contains _ExtensionDict class to represent extensions.""" + +from google.protobuf.descriptor import FieldDescriptor +from google.protobuf.internal import type_checkers + + +def _VerifyExtensionHandle(message, extension_handle): + """Verify that the given extension handle is valid.""" + + if not isinstance(extension_handle, FieldDescriptor): + raise KeyError( + 'HasExtension() expects an extension handle, got: %s' % extension_handle + ) + + if not extension_handle.is_extension: + raise KeyError('"%s" is not an extension.' % extension_handle.full_name) + + if not extension_handle.containing_type: + raise KeyError( + '"%s" is missing a containing_type.' % extension_handle.full_name + ) + + if extension_handle.containing_type is not message.DESCRIPTOR: + raise KeyError( + 'Extension "%s" extends message type "%s", but this ' + 'message is of type "%s".' + % ( + extension_handle.full_name, + extension_handle.containing_type.full_name, + message.DESCRIPTOR.full_name, + ) + ) + + +# TODO: Unify error handling of "unknown extension" crap. +# TODO: Support iteritems()-style iteration over all +# extensions with the "has" bits turned on? +class _ExtensionDict(object): + """Dict-like container for Extension fields on proto instances. + + Note that in all cases we expect extension handles to be + FieldDescriptors. + """ + + def __init__(self, extended_message): + """Args: + + extended_message: Message instance for which we are the Extensions dict. + """ + self._extended_message = extended_message + + def __getitem__(self, extension_handle): + """Returns the current value of the given extension handle.""" + + _VerifyExtensionHandle(self._extended_message, extension_handle) + + result = self._extended_message._fields.get(extension_handle) + if result is not None: + return result + + if extension_handle.is_repeated: + result = extension_handle._default_constructor(self._extended_message) + elif extension_handle.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE: + message_type = extension_handle.message_type + if not hasattr(message_type, '_concrete_class'): + # pylint: disable=g-import-not-at-top + from google.protobuf import message_factory + + message_factory.GetMessageClass(message_type) + if not hasattr(extension_handle.message_type, '_concrete_class'): + from google.protobuf import message_factory + + message_factory.GetMessageClass(extension_handle.message_type) + result = extension_handle.message_type._concrete_class() + try: + result._SetListener(self._extended_message._listener_for_children) + except ReferenceError: + pass + else: + # Singular scalar -- just return the default without inserting into the + # dict. + return extension_handle.default_value + + # Atomically check if another thread has preempted us and, if not, swap + # in the new object we just created. If someone has preempted us, we + # take that object and discard ours. + # WARNING: We are relying on setdefault() being atomic. This is true + # in CPython but we haven't investigated others. This warning appears + # in several other locations in this file. + if self._extended_message._frozen: + result._SetFrozen() + result = self._extended_message._fields.setdefault(extension_handle, result) + + return result + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return False + + my_fields = self._extended_message.ListFields() + other_fields = other._extended_message.ListFields() + + # Get rid of non-extension fields. + my_fields = [field for field in my_fields if field.is_extension] + other_fields = [field for field in other_fields if field.is_extension] + + return my_fields == other_fields + + def __ne__(self, other): + return not self == other + + def __len__(self): + fields = self._extended_message.ListFields() + # Get rid of non-extension fields. + extension_fields = [field for field in fields if field[0].is_extension] + return len(extension_fields) + + def __hash__(self): + raise TypeError('unhashable object') + + # Note that this is only meaningful for non-repeated, scalar extension + # fields. Note also that we may have to call _Modified() when we do + # successfully set a field this way, to set any necessary "has" bits in the + # ancestors of the extended message. + def __setitem__(self, extension_handle, value): + """If extension_handle specifies a non-repeated, scalar extension + + field, sets the value of that field. + """ + + _VerifyExtensionHandle(self._extended_message, extension_handle) + + self._extended_message._AssureWritable() + + if ( + extension_handle.is_repeated + or extension_handle.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE + ): + raise TypeError( + 'Cannot assign to extension "%s" because it is a repeated or ' + 'composite type.' + % extension_handle.full_name + ) + + # It's slightly wasteful to lookup the type checker each time, + # but we expect this to be a vanishingly uncommon case anyway. + type_checker = type_checkers.GetTypeChecker(extension_handle) + # pylint: disable=protected-access + self._extended_message._fields[extension_handle] = type_checker.CheckValue( + value + ) + self._extended_message._Modified() + + def __delitem__(self, extension_handle): + self._extended_message.ClearExtension(extension_handle) + + def _FindExtensionByName(self, name): + """Tries to find a known extension with the specified name. + + Args: + name: Extension full name. + + Returns: + Extension field descriptor. + """ + descriptor = self._extended_message.DESCRIPTOR + extensions = descriptor.file.pool._extensions_by_name[descriptor] + return extensions.get(name, None) + + def _FindExtensionByNumber(self, number): + """Tries to find a known extension with the field number. + + Args: + number: Extension field number. + + Returns: + Extension field descriptor. + """ + descriptor = self._extended_message.DESCRIPTOR + extensions = descriptor.file.pool._extensions_by_number[descriptor] + return extensions.get(number, None) + + def __iter__(self): + # Return a generator over the populated extension fields + return ( + f[0] for f in self._extended_message.ListFields() if f[0].is_extension + ) + + def __contains__(self, extension_handle): + _VerifyExtensionHandle(self._extended_message, extension_handle) + + if extension_handle not in self._extended_message._fields: + return False + + if extension_handle.is_repeated: + return bool(self._extended_message._fields.get(extension_handle)) + + if extension_handle.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE: + value = self._extended_message._fields.get(extension_handle) + # pylint: disable=protected-access + return value is not None and value._is_present_in_parent + + return True diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/field_mask.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/field_mask.py new file mode 100755 index 0000000..a8f01dc --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/field_mask.py @@ -0,0 +1,352 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Contains FieldMask class.""" + +from google.protobuf.descriptor import FieldDescriptor + + +class FieldMask(object): + """Class for FieldMask message type.""" + + __slots__ = () + + def ToJsonString(self): + """Converts FieldMask to string according to ProtoJSON spec.""" + camelcase_paths = [] + for path in self.paths: + camelcase_paths.append(_SnakeCaseToCamelCase(path)) + return ','.join(camelcase_paths) + + def FromJsonString(self, value): + """Converts string to FieldMask according to ProtoJSON spec.""" + if not isinstance(value, str): + raise ValueError('FieldMask JSON value not a string: {!r}'.format(value)) + self.Clear() + if value: + for path in value.split(','): + self.paths.append(_CamelCaseToSnakeCase(path)) + + def IsValidForDescriptor(self, message_descriptor): + """Checks whether the FieldMask is valid for Message Descriptor.""" + for path in self.paths: + if not _IsValidPath(message_descriptor, path): + return False + return True + + def AllFieldsFromDescriptor(self, message_descriptor): + """Gets all direct fields of Message Descriptor to FieldMask.""" + self.Clear() + for field in message_descriptor.fields: + self.paths.append(field.name) + + def CanonicalFormFromMask(self, mask): + """Converts a FieldMask to the canonical form. + + Removes paths that are covered by another path. For example, + "foo.bar" is covered by "foo" and will be removed if "foo" + is also in the FieldMask. Then sorts all paths in alphabetical order. + + Args: + mask: The original FieldMask to be converted. + """ + tree = _FieldMaskTree(mask) + tree.ToFieldMask(self) + + def Union(self, mask1, mask2): + """Merges mask1 and mask2 into this FieldMask.""" + _CheckFieldMaskMessage(mask1) + _CheckFieldMaskMessage(mask2) + tree = _FieldMaskTree(mask1) + tree.MergeFromFieldMask(mask2) + tree.ToFieldMask(self) + + def Intersect(self, mask1, mask2): + """Intersects mask1 and mask2 into this FieldMask.""" + _CheckFieldMaskMessage(mask1) + _CheckFieldMaskMessage(mask2) + tree = _FieldMaskTree(mask1) + intersection = _FieldMaskTree() + for path in mask2.paths: + tree.IntersectPath(path, intersection) + intersection.ToFieldMask(self) + + def MergeMessage( + self, + source, + destination, + replace_message_field=False, + replace_repeated_field=False, + ): + """Merges fields specified in FieldMask from source to destination. + + Args: + source: Source message. + destination: The destination message to be merged into. + replace_message_field: Replace message field if True. Merge message field + if False. + replace_repeated_field: Replace repeated field if True. Append elements of + repeated field if False. + """ + tree = _FieldMaskTree(self) + tree.MergeMessage( + source, destination, replace_message_field, replace_repeated_field + ) + + +def _IsValidPath(message_descriptor, path): + """Checks whether the path is valid for Message Descriptor.""" + parts = path.split('.') + last = parts.pop() + for name in parts: + field = message_descriptor.fields_by_name.get(name) + if ( + field is None + or field.is_repeated + or field.type != FieldDescriptor.TYPE_MESSAGE + ): + return False + message_descriptor = field.message_type + return last in message_descriptor.fields_by_name + + +def _CheckFieldMaskMessage(message): + """Raises ValueError if message is not a FieldMask.""" + message_descriptor = message.DESCRIPTOR + if ( + message_descriptor.name != 'FieldMask' + or message_descriptor.file.name != 'google/protobuf/field_mask.proto' + ): + raise ValueError( + 'Message {0} is not a FieldMask.'.format(message_descriptor.full_name) + ) + + +def _SnakeCaseToCamelCase(path_name): + """Converts a path name from snake_case to camelCase.""" + result = [] + after_underscore = False + for c in path_name: + if c.isupper(): + raise ValueError( + 'Fail to print FieldMask to Json string: Path name ' + '{0} must not contain uppercase letters.'.format(path_name) + ) + if after_underscore: + if c.islower(): + result.append(c.upper()) + after_underscore = False + else: + raise ValueError( + 'Fail to print FieldMask to Json string: The ' + 'character after a "_" must be a lowercase letter ' + 'in path name {0}.'.format(path_name) + ) + elif c == '_': + after_underscore = True + else: + result += c + + if after_underscore: + raise ValueError( + 'Fail to print FieldMask to Json string: Trailing "_" ' + 'in path name {0}.'.format(path_name) + ) + return ''.join(result) + + +def _CamelCaseToSnakeCase(path_name): + """Converts a field name from camelCase to snake_case.""" + result = [] + for c in path_name: + if c == '_': + raise ValueError( + 'Fail to parse FieldMask: Path name ' + '{0} must not contain "_"s.'.format(path_name) + ) + if c.isupper(): + result += '_' + result += c.lower() + else: + result += c + return ''.join(result) + + +class _FieldMaskTree(object): + """Represents a FieldMask in a tree structure. + + For example, given a FieldMask "foo.bar,foo.baz,bar.baz", + the FieldMaskTree will be: + [_root] -+- foo -+- bar + | | + | +- baz + | + +- bar --- baz + In the tree, each leaf node represents a field path. + """ + + __slots__ = ('_root',) + + def __init__(self, field_mask=None): + """Initializes the tree by FieldMask.""" + self._root = {} + if field_mask: + self.MergeFromFieldMask(field_mask) + + def MergeFromFieldMask(self, field_mask): + """Merges a FieldMask to the tree.""" + for path in field_mask.paths: + self.AddPath(path) + + def AddPath(self, path): + """Adds a field path into the tree. + + If the field path to add is a sub-path of an existing field path + in the tree (i.e., a leaf node), it means the tree already matches + the given path so nothing will be added to the tree. If the path + matches an existing non-leaf node in the tree, that non-leaf node + will be turned into a leaf node with all its children removed because + the path matches all the node's children. Otherwise, a new path will + be added. + + Args: + path: The field path to add. + """ + node = self._root + for name in path.split('.'): + if name not in node: + node[name] = {} + elif not node[name]: + # Pre-existing empty node implies we already have this entire tree. + return + node = node[name] + # Remove any sub-trees we might have had. + node.clear() + + def ToFieldMask(self, field_mask): + """Converts the tree to a FieldMask.""" + field_mask.Clear() + _AddFieldPaths(self._root, '', field_mask) + + def IntersectPath(self, path, intersection): + """Calculates the intersection part of a field path with this tree. + + Args: + path: The field path to calculates. + intersection: The out tree to record the intersection part. + """ + node = self._root + for name in path.split('.'): + if name not in node: + return + elif not node[name]: + intersection.AddPath(path) + return + node = node[name] + intersection.AddLeafNodes(path, node) + + def AddLeafNodes(self, prefix, node): + """Adds leaf nodes begin with prefix to this tree.""" + if not node: + self.AddPath(prefix) + return + stack = [(prefix, node)] + while stack: + current_prefix, current_node = stack.pop() + if not current_node: + self.AddPath(current_prefix) + continue + for name in current_node: + child_path = current_prefix + '.' + name + stack.append((child_path, current_node[name])) + + def MergeMessage( + self, source, destination, replace_message, replace_repeated + ): + """Merge all fields specified by this tree from source to destination.""" + _MergeMessage( + self._root, source, destination, replace_message, replace_repeated + ) + + +def _StrConvert(value): + """Converts value to str if it is not.""" + # This file is imported by c extension and some methods like ClearField + # requires string for the field name. py2/py3 has different text + # type and may use unicode. + if not isinstance(value, str): + return value.encode('utf-8') + return value + + +def _MergeMessage(node, source, destination, replace_message, replace_repeated): + """Merge all fields specified by a sub-tree from source to destination.""" + stack = [(node, source, destination)] + while stack: + current_node, current_source, current_destination = stack.pop() + source_descriptor = current_source.DESCRIPTOR + for name in current_node: + child = current_node[name] + field = source_descriptor.fields_by_name[name] + if field is None: + raise ValueError( + "Error: Can't find field {0} in message {1}.".format( + name, source_descriptor.full_name + ) + ) + if child: + # Sub-paths are only allowed for singular message fields. + if ( + field.is_repeated + or field.cpp_type != FieldDescriptor.CPPTYPE_MESSAGE + ): + raise ValueError( + 'Error: Field {0} in message {1} is not a singular ' + 'message field and cannot have sub-fields.'.format( + name, source_descriptor.full_name + ) + ) + if current_source.HasField(name): + stack.append(( + child, + getattr(current_source, name), + getattr(current_destination, name), + )) + continue + if field.is_repeated: + if replace_repeated: + current_destination.ClearField(_StrConvert(name)) + repeated_source = getattr(current_source, name) + repeated_destination = getattr(current_destination, name) + repeated_destination.MergeFrom(repeated_source) + else: + if field.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE: + if replace_message: + current_destination.ClearField(_StrConvert(name)) + if current_source.HasField(name): + getattr(current_destination, name).MergeFrom( + getattr(current_source, name) + ) + elif not field.has_presence or current_source.HasField(name): + setattr(current_destination, name, getattr(current_source, name)) + else: + current_destination.ClearField(_StrConvert(name)) + + +def _AddFieldPaths(node, prefix, field_mask): + """Adds the field paths descended from node to field_mask.""" + stack = [(node, prefix)] + while stack: + current_node, current_prefix = stack.pop() + if not current_node and current_prefix: + field_mask.paths.append(current_prefix) + continue + for name in sorted(current_node, reverse=True): + if current_prefix: + child_path = current_prefix + '.' + name + else: + child_path = name + stack.append((current_node[name], child_path)) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/json_format_benchmark.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/json_format_benchmark.py new file mode 100755 index 0000000..be2458d --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/json_format_benchmark.py @@ -0,0 +1,250 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2026 Google LLC. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +"""Microbenchmarks for JSON format parsing and custom enum names.""" + +import statistics +import timeit +import unittest + +from google.protobuf import json_format + +from google.protobuf.json import json_enumval_custom_string_pb2 + + +class JsonFormatBenchmark(unittest.TestCase): + """Microbenchmarks for JSON format parsing.""" + + def test_benchmark_parse_json(self): + """Benchmarks parsing of default, custom, and unknown enum names in JSON.""" + default_payloads = [ + '{"armor":"ARMOR_GREAT_HELM"}', + '{"armor":"ARMOR_GAUNTLET"}', + '{"armor":"ARMOR_PLATE"}', + '{"armor":"ARMOR_COIF"}', + '{"armor":"ARMOR_PAULDRON"}', + '{"armor":"ARMOR_SABATON"}', + '{"armor":"ARMOR_HACHI_MAI_DO"}', + ] + custom_payloads = [ + '{"armor":"gr8 helm"}', + '{"armor":"a\\"b"}', + '{"armor":"\\"plate\\""}', + '{"armor":""}', + '{"armor":"p\\taul\\ndron"}', + '{"armor":"sabaton"}', + '{"armor":"8"}', + ] + unknown_payloads = [ + '{"armor":"UNKNOWN_1"}', + '{"armor":"UNKNOWN_2"}', + '{"armor":"UNKNOWN_3"}', + '{"armor":"UNKNOWN_4"}', + '{"armor":"UNKNOWN_5"}', + '{"armor":"UNKNOWN_6"}', + '{"armor":"UNKNOWN_7"}', + ] + + repeated_default_payload = ( + '{"armors":[' + + ','.join( + [ + '"ARMOR_GREAT_HELM"', + '"ARMOR_GAUNTLET"', + '"ARMOR_PLATE"', + '"ARMOR_COIF"', + '"ARMOR_PAULDRON"', + '"ARMOR_SABATON"', + '"ARMOR_HACHI_MAI_DO"', + ] + * 14 + ) + + ']}' + ) + repeated_custom_payload = ( + '{"armors":[' + + ','.join( + [ + '"gr8 helm"', + '"a\\"b"', + '"\\"plate\\""', + '""', + '"p\\taul\\ndron"', + '"sabaton"', + '"8"', + ] + * 14 + ) + + ']}' + ) + repeated_unknown_payload = ( + '{"armors":[' + + ','.join( + [ + '"UNKNOWN_1"', + '"UNKNOWN_2"', + '"UNKNOWN_3"', + '"UNKNOWN_4"', + '"UNKNOWN_5"', + '"UNKNOWN_6"', + '"UNKNOWN_7"', + ] + * 14 + ) + + ']}' + ) + + iterations = 10 + trials = 100 + num_parses = 10 + num_items = 98 + + msg = json_enumval_custom_string_pb2.Knight() + + # Warmup phase to eliminate initial compilation/import overhead + for _ in range(200): + for p in custom_payloads: + msg.Clear() + json_format.Parse(p, msg) + + def run_def(): + for p in default_payloads: + for _ in range(num_parses): + msg.Clear() + json_format.Parse(p, msg) + + def run_cust(): + for p in custom_payloads: + for _ in range(num_parses): + msg.Clear() + json_format.Parse(p, msg) + + def run_unk(): + for p in unknown_payloads: + for _ in range(num_parses): + msg.Clear() + json_format.Parse(p, msg, ignore_unknown_fields=True) + + def run_rep_def(): + for _ in range(num_parses): + msg.Clear() + json_format.Parse(repeated_default_payload, msg) + + def run_rep_cust(): + for _ in range(num_parses): + msg.Clear() + json_format.Parse(repeated_custom_payload, msg) + + def run_rep_unk(): + for _ in range(num_parses): + msg.Clear() + json_format.Parse( + repeated_unknown_payload, msg, ignore_unknown_fields=True + ) + + benchmarks = [ + ( + 'run_def', + timeit.Timer(run_def, setup=msg.Clear), + len(default_payloads) * num_parses, + ), + ( + 'run_cust', + timeit.Timer(run_cust, setup=msg.Clear), + len(custom_payloads) * num_parses, + ), + ( + 'run_unk', + timeit.Timer(run_unk, setup=msg.Clear), + len(unknown_payloads) * num_parses, + ), + ( + 'run_rep_def', + timeit.Timer(run_rep_def, setup=msg.Clear), + num_items * num_parses, + ), + ( + 'run_rep_cust', + timeit.Timer(run_rep_cust, setup=msg.Clear), + num_items * num_parses, + ), + ( + 'run_rep_unk', + timeit.Timer(run_rep_unk, setup=msg.Clear), + num_items * num_parses, + ), + ] + + # Interleaved trials to minimize CPU frequency scaling / ordering bias + benchmark_samples = {name: [] for name, _, _ in benchmarks} + for _ in range(trials): + for name, timer, num_ops_per_batch in benchmarks: + total_ops = iterations * num_ops_per_batch + elapsed = timer.timeit(number=iterations) + benchmark_samples[name].append(elapsed * 1e6 / total_ops) + + def stats(samples): + if len(samples) < 100: + raise ValueError( + f'Insufficient samples ({len(samples)}) for statistical' + ' calculations. At least 100 samples are required.' + ) + med_val = statistics.median(samples) + mean_val = statistics.mean(samples) + stdev_val = statistics.stdev(samples) + p99_val = statistics.quantiles(samples, n=100)[98] + return p99_val, med_val, mean_val, stdev_val + + p99_def, med_def, mean_def, std_def = stats(benchmark_samples['run_def']) + p99_cust, med_cust, mean_cust, std_cust = stats( + benchmark_samples['run_cust'] + ) + p99_unk, med_unk, mean_unk, std_unk = stats(benchmark_samples['run_unk']) + p99_rep_def, med_rep_def, mean_rep_def, std_rep_def = stats( + benchmark_samples['run_rep_def'] + ) + p99_rep_cust, med_rep_cust, mean_rep_cust, std_rep_cust = stats( + benchmark_samples['run_rep_cust'] + ) + p99_rep_unk, med_rep_unk, mean_rep_unk, std_rep_unk = stats( + benchmark_samples['run_rep_unk'] + ) + + print( + f'\n[BENCHMARK] ParseJsonDefault: med {med_def:.2f}' + f' us/op | p99 {p99_def:.2f} us/op | mean {mean_def:.2f} ±' + f' {std_def:.2f} us/op' + ) + print( + f'[BENCHMARK] ParseJsonCustom: med {med_cust:.2f} us/op' + f' | p99 {p99_cust:.2f} us/op | mean {mean_cust:.2f} ± {std_cust:.2f}' + ' us/op' + ) + print( + '[BENCHMARK] ParseJsonUnknownIgnored: ' + f' med {med_unk:.2f} us/op | p99 {p99_unk:.2f} us/op | mean' + f' {mean_unk:.2f} ± {std_unk:.2f} us/op' + ) + print( + '[BENCHMARK] ParseRepeatedJsonDefault: ' + f' med {med_rep_def:.2f} us/item | p99 {p99_rep_def:.2f} us/item |' + f' mean {mean_rep_def:.2f} ± {std_rep_def:.2f} us/item' + ) + print( + '[BENCHMARK] ParseRepeatedJsonCustom: ' + f' med {med_rep_cust:.2f} us/item | p99 {p99_rep_cust:.2f} us/item |' + f' mean {mean_rep_cust:.2f} ± {std_rep_cust:.2f} us/item' + ) + print( + '[BENCHMARK] ParseRepeatedJsonUnknownIgnored:' + f' med {med_rep_unk:.2f} us/item | p99 {p99_rep_unk:.2f} us/item |' + f' mean {mean_rep_unk:.2f} ± {std_rep_unk:.2f} us/item' + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/message_benchmark.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/message_benchmark.py new file mode 100755 index 0000000..6413648 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/message_benchmark.py @@ -0,0 +1,300 @@ +"""Benchmarks for assigning large NumPy data to protobuf fields.""" + +from collections.abc import Callable +import functools +import sys + +import google_benchmark +import numpy as np + +from google.protobuf import unittest_pb2 + + +@functools.cache +def make_array(num_bytes: int): + return np.zeros(shape=(num_bytes,), dtype=np.uint8) + + +def benchmark( + func: Callable[[google_benchmark.State], None], +) -> Callable[[google_benchmark.State], None]: + """Decorates a function for benchmarking.""" + + @google_benchmark.register + @google_benchmark.option.unit(google_benchmark.kMillisecond) + @google_benchmark.option.arg_names(['num_bytes']) + @google_benchmark.option.arg(1024 * 1024 * 20) + @google_benchmark.option.arg(1024 * 1024 * 100) + @functools.wraps(func) + def wrapper(state: google_benchmark.State) -> None: + func(state) + state.bytes_processed = state.iterations * state.range(0) + + return wrapper + + +@benchmark +def bench_build_message_via_slice(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.int32) + while state: + msg = unittest_pb2.TestAllTypes() + msg.repeated_int32[:] = arr + + +@benchmark +def bench_build_message(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.int32) + while state: + _ = unittest_pb2.TestAllTypes(repeated_int32=arr) + + +@benchmark +def bench_build_message_nested_via_slice(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.int32) + while state: + msg = unittest_pb2.NestedTestAllTypes() + msg.payload.repeated_int32[:] = arr + + +@benchmark +def bench_build_nested_message_dict(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.int32) + while state: + _ = unittest_pb2.NestedTestAllTypes(payload=dict(repeated_int32=arr)) + + +@benchmark +def bench_build_nested_message_int32(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.int32) + while state: + _ = unittest_pb2.NestedTestAllTypes( + payload=unittest_pb2.TestAllTypes(repeated_int32=arr) + ) + + +@benchmark +def bench_build_nested_message_cord(state: google_benchmark.State): + chunk_size = state.range(0) // 1000 + strings = ['a' * chunk_size] * 1000 + while state: + _ = unittest_pb2.NestedTestAllTypes( + payload=unittest_pb2.TestAllTypes(repeated_cord=strings) + ) + + +@benchmark +def bench_build_nested_message_string_piece(state: google_benchmark.State): + chunk_size = state.range(0) // 1000 + strings = ['a' * chunk_size] * 1000 + while state: + _ = unittest_pb2.NestedTestAllTypes( + payload=unittest_pb2.TestAllTypes(repeated_string_piece=strings) + ) + + +@benchmark +def bench_build_nested_message_nested_message(state: google_benchmark.State): + subs = [unittest_pb2.TestAllTypes.NestedMessage(bb=123)] * ( + state.range(0) // 8 + ) + while state: + _ = unittest_pb2.NestedTestAllTypes( + payload=unittest_pb2.TestAllTypes(repeated_nested_message=subs) + ) + + +@benchmark +def bench_assign_repeated_float(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.float32) + msg = unittest_pb2.TestAllTypes() + msg_source = unittest_pb2.TestAllTypes() + msg_source.repeated_float.extend(arr) + while state: + state.pause_timing() + msg.Clear() + state.resume_timing() + msg.repeated_float[:] = msg_source.repeated_float + + +@benchmark +def bench_assign_repeated_int64_to_int32(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.int64) + msg = unittest_pb2.TestAllTypes() + msg_source = unittest_pb2.TestAllTypes() + msg_source.repeated_int64.extend(arr) + while state: + state.pause_timing() + msg.Clear() + state.resume_timing() + msg.repeated_int32[:] = msg_source.repeated_int64 + + +@benchmark +def bench_assign_repeated_double_to_float(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.float64) + msg = unittest_pb2.TestAllTypes() + msg_source = unittest_pb2.TestAllTypes() + msg_source.repeated_double.extend(arr) + while state: + state.pause_timing() + msg.Clear() + state.resume_timing() + msg.repeated_float[:] = msg_source.repeated_double + + +@benchmark +def bench_assign_numpy_int64_to_int32(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.int64) + msg = unittest_pb2.TestAllTypes() + while state: + state.pause_timing() + msg.Clear() + state.resume_timing() + msg.repeated_int32[:] = arr + + +@benchmark +def bench_assign_numpy_double_to_float(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.float64) + msg = unittest_pb2.TestAllTypes() + while state: + state.pause_timing() + msg.Clear() + state.resume_timing() + msg.repeated_float[:] = arr + + +@benchmark +def bench_extend_int32(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.int32) + msg = unittest_pb2.TestAllTypes() + while state: + state.pause_timing() + msg.Clear() + state.resume_timing() + msg.repeated_int32.extend(arr) + + +@benchmark +def bench_decode_from_bytes(state: google_benchmark.State): + arr = make_array(state.range(0)) + msg = unittest_pb2.TestAllTypes() + msg.optional_bytes = memoryview(arr) + msg_bytes = msg.SerializeToString() + while state: + _ = unittest_pb2.TestAllTypes.FromString(msg_bytes) + + +@benchmark +def bench_decode_from_memoryview(state: google_benchmark.State): + arr = make_array(state.range(0)) + msg = unittest_pb2.TestAllTypes() + msg.optional_bytes = memoryview(arr) + msg_bytes = memoryview(msg.SerializeToString()) + while state: + _ = unittest_pb2.TestAllTypes.FromString(msg_bytes) + + +@benchmark +def bench_encode_into_bytes(state: google_benchmark.State): + arr = make_array(state.range(0)) + msg = unittest_pb2.TestAllTypes() + msg.optional_bytes = memoryview(arr) + while state: + _ = msg.SerializeToString() + + +@benchmark +def bench_assign_int32(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.int32) + msg = unittest_pb2.TestAllTypes() + while state: + msg.repeated_int32[:] = arr + state.pause_timing() + msg.Clear() + state.resume_timing() + + +@benchmark +def bench_assign_extend_int32(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.int32) + msg = unittest_pb2.TestAllTypes() + msg.repeated_int32[:] = arr + while state: + msg.repeated_int32[len(arr) :] = arr + state.pause_timing() + msg.repeated_int32[:] = arr + state.resume_timing() + + +@benchmark +def bench_assign_slice_int32(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.int32) + half_arr = arr[: len(arr) // 2] + msg = unittest_pb2.TestAllTypes() + msg.repeated_int32.extend(arr) + while state: + msg.repeated_int32[::2] = half_arr + + +@benchmark +def bench_extend_int64(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.int64) + msg = unittest_pb2.TestAllTypes() + while state: + state.pause_timing() + msg.Clear() + state.resume_timing() + msg.repeated_int64.extend(arr) + + +@benchmark +def bench_extend_float(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.float32) + msg = unittest_pb2.TestAllTypes() + while state: + state.pause_timing() + msg.Clear() + state.resume_timing() + msg.repeated_float.extend(arr) + + +@benchmark +def bench_extend_double(state: google_benchmark.State): + arr = make_array(state.range(0)).view(dtype=np.float64) + msg = unittest_pb2.TestAllTypes() + while state: + state.pause_timing() + msg.Clear() + state.resume_timing() + msg.repeated_double.extend(arr) + + +@benchmark +def bench_assign_bytes(state: google_benchmark.State): + arr = make_array(state.range(0)) + msg = unittest_pb2.TestAllTypes() + arr_bytes = arr.tobytes() + while state: + state.pause_timing() + msg.Clear() + state.resume_timing() + msg.optional_bytes = arr_bytes + + +@benchmark +def bench_assign_bytes_with_conversion(state: google_benchmark.State): + arr = make_array(state.range(0)) + msg = unittest_pb2.TestAllTypes() + while state: + state.pause_timing() + msg.Clear() + state.resume_timing() + msg.optional_bytes = memoryview(arr) + + +if __name__ == '__main__': + if any(arg.startswith('--benchmark_filter') for arg in sys.argv): + google_benchmark.main() + else: + print('No benchmark filter specified. Skipping benchmarks.') diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/message_listener.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/message_listener.py new file mode 100755 index 0000000..7abb851 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/message_listener.py @@ -0,0 +1,56 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +"""Defines a listener interface for observing certain + +state transitions on Message objects. + +Also defines a null implementation of this interface. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + + +class MessageListener(object): + """Listens for modifications made to a message. + + Meant to be registered via Message._SetListener(). + + Attributes: + dirty: If True, then calling Modified() would be a no-op. This can be used + to avoid these calls entirely in the common case. + """ + + def Modified(self): + """Called every time the message is modified in such a way that the parent + + message may need to be updated. This currently means either: + (a) The message was modified for the first time, so the parent message + should henceforth mark the message as present. + (b) The message's cached byte size became dirty -- i.e. the message was + modified for the first time after a previous call to ByteSize(). + Therefore the parent should also mark its byte size as dirty. + Note that (a) implies (b), since new objects start out with a client cached + size (zero). However, we document (a) explicitly because it is important. + + Modified() will *only* be called in response to one of these two events -- + not every time the sub-message is modified. + + Note that if the listener's |dirty| attribute is true, then calling + Modified at the moment would be a no-op, so it can be skipped. Performance- + sensitive callers should check this attribute directly before calling since + it will be true most of the time. + """ + + raise NotImplementedError + + +class NullMessageListener(object): + """No-op MessageListener implementation.""" + + def Modified(self): + pass diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/python_edition_defaults.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/python_edition_defaults.py new file mode 100755 index 0000000..a6b7741 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/python_edition_defaults.py @@ -0,0 +1,5 @@ +""" +This file contains the serialized FeatureSetDefaults object corresponding to +the Pure Python runtime. This is used for feature resolution under Editions. +""" +_PROTOBUF_INTERNAL_PYTHON_EDITION_DEFAULTS = b"\n\031\030\204\007\"\000*\022\010\001\020\002\030\002 \003(\0010\0028\002@\001H\001\n\031\030\347\007\"\000*\022\010\002\020\001\030\001 \002(\0010\0018\002@\001H\001\n\031\030\350\007\"\014\010\001\020\001\030\001 \002(\0010\001*\0068\002@\001H\001\n\031\030\351\007\"\020\010\001\020\001\030\001 \002(\0010\0018\001@\002*\002H\001\n\031\030\352\007\"\022\010\001\020\001\030\001 \002(\0010\0018\003@\004H\002*\000 \346\007(\352\007" diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/python_message.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/python_message.py new file mode 100755 index 0000000..53bd94f --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/python_message.py @@ -0,0 +1,1778 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +# This code is meant to work on Python 2.4 and above only. +# +# TODO: Helpers for verbose, common checks like seeing if a +# descriptor's cpp_type is CPPTYPE_MESSAGE. +"""Contains a metaclass and helper functions used to create + +protocol message classes from Descriptor objects at runtime. + +Recall that a metaclass is the "type" of a class. +(A class is to a metaclass what an instance is to a class.) + +In this case, we use the GeneratedProtocolMessageType metaclass +to inject all the useful functionality into the classes +output by the protocol compiler at compile-time. + +The upshot of all this is that the real implementation +details for ALL pure-Python protocol buffers are *here in +this file*. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import datetime +from io import BytesIO +import math +import struct +import sys +import warnings +import weakref + +from google.protobuf import descriptor as descriptor_mod +from google.protobuf import message as message_mod +from google.protobuf import text_format + +# We use "as" to avoid name collisions with variables. +from google.protobuf.internal import api_implementation +from google.protobuf.internal import containers +from google.protobuf.internal import decoder +from google.protobuf.internal import encoder +from google.protobuf.internal import enum_type_wrapper +from google.protobuf.internal import extension_dict +from google.protobuf.internal import message_listener as message_listener_mod +from google.protobuf.internal import type_checkers +from google.protobuf.internal import well_known_types +from google.protobuf.internal import wire_format + +_FieldDescriptor = descriptor_mod.FieldDescriptor +_AnyFullTypeName = 'google.protobuf.Any' +_StructFullTypeName = 'google.protobuf.Struct' +_ListValueFullTypeName = 'google.protobuf.ListValue' +_ExtensionDict = extension_dict._ExtensionDict + + +class GeneratedProtocolMessageType(type): + """Metaclass for protocol message classes created at runtime from Descriptors. + + We add implementations for all methods described in the Message class. We + also create properties to allow getting/setting all fields in the protocol + message. Finally, we create slots to prevent users from accidentally + "setting" nonexistent fields in the protocol message, which then wouldn't get + serialized / deserialized properly. + + The protocol compiler currently uses this metaclass to create protocol + message classes at runtime. Clients can also manually create their own + classes at runtime, as in this example: + + mydescriptor = Descriptor(.....) + factory = symbol_database.Default() + factory.pool.AddDescriptor(mydescriptor) + MyProtoClass = message_factory.GetMessageClass(mydescriptor) + myproto_instance = MyProtoClass() + myproto.foo_field = 23 + ... + """ + + # Must be consistent with the protocol-compiler code in + # proto2/compiler/internal/generator.*. + _DESCRIPTOR_KEY = 'DESCRIPTOR' + + def __new__(cls, name, bases, dictionary): + """Custom allocation for runtime-generated class types. + + We override __new__ because this is apparently the only place + where we can meaningfully set __slots__ on the class we're creating(?). + (The interplay between metaclasses and slots is not very well-documented). + + Args: + name: Name of the class (ignored, but required by the metaclass protocol). + bases: Base classes of the class we're constructing. (Should be + message.Message). We ignore this field, but it's required by the + metaclass protocol + dictionary: The class dictionary of the class we're constructing. + dictionary[_DESCRIPTOR_KEY] must contain a Descriptor object describing + this protocol message type. + + Returns: + Newly-allocated class. + + Raises: + RuntimeError: Generated code only work with python cpp extension. + """ + descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY] + + if isinstance(descriptor, str): + raise RuntimeError( + 'The generated code only work with python cpp ' + 'extension, but it is using pure python runtime.' + ) + + # If a concrete class already exists for this descriptor, don't try to + # create another. Doing so will break any messages that already exist with + # the existing class. + # + # The C++ implementation appears to have its own internal `PyMessageFactory` + # to achieve similar results. + # + # This most commonly happens in `text_format.py` when using descriptors from + # a custom pool; it calls message_factory.GetMessageClass() on a + # descriptor which already has an existing concrete class. + new_class = getattr(descriptor, '_concrete_class', None) + if new_class: + return new_class + + if descriptor.full_name in well_known_types.WKTBASES: + bases += (well_known_types.WKTBASES[descriptor.full_name],) + _AddClassAttributesForNestedExtensions(descriptor, dictionary) + _AddSlots(descriptor, dictionary) + + superclass = super(GeneratedProtocolMessageType, cls) + new_class = superclass.__new__(cls, name, bases, dictionary) + return new_class + + def __init__(cls, name, bases, dictionary): + """Here we perform the majority of our work on the class. + + We add enum getters, an __init__ method, implementations of all Message + methods, and properties for all fields in the protocol type. + + Args: + name: Name of the class (ignored, but required by the metaclass protocol). + bases: Base classes of the class we're constructing. (Should be + message.Message). We ignore this field, but it's required by the + metaclass protocol + dictionary: The class dictionary of the class we're constructing. + dictionary[_DESCRIPTOR_KEY] must contain a Descriptor object describing + this protocol message type. + """ + descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY] + + # If this is an _existing_ class looked up via `_concrete_class` in the + # __new__ method above, then we don't need to re-initialize anything. + existing_class = getattr(descriptor, '_concrete_class', None) + if existing_class: + assert existing_class is cls, ( + 'Duplicate `GeneratedProtocolMessageType` created for descriptor %r' + % (descriptor.full_name) + ) + return + + cls._message_set_decoders_by_tag = {} + cls._fields_by_tag = {} + if ( + descriptor.has_options + and descriptor.GetOptions().message_set_wire_format + ): + cls._message_set_decoders_by_tag[decoder.MESSAGE_SET_ITEM_TAG] = ( + decoder.MessageSetItemDecoder(descriptor), + None, + ) + + # Attach stuff to each FieldDescriptor for quick lookup later on. + for field in descriptor.fields: + _AttachFieldHelpers(cls, field) + + if descriptor.is_extendable and hasattr(descriptor.file, 'pool'): + extensions = descriptor.file.pool.FindAllExtensions(descriptor) + for ext in extensions: + _AttachFieldHelpers(cls, ext) + + descriptor._concrete_class = cls # pylint: disable=protected-access + _AddEnumValues(descriptor, cls) + _AddInitMethod(descriptor, cls) + _AddPropertiesForFields(descriptor, cls) + _AddPropertiesForExtensions(descriptor, cls) + _AddStaticMethods(cls) + _AddMessageMethods(descriptor, cls) + _AddPrivateHelperMethods(descriptor, cls) + + superclass = super(GeneratedProtocolMessageType, cls) + superclass.__init__(name, bases, dictionary) + + +# Stateless helpers for GeneratedProtocolMessageType below. +# Outside clients should not access these directly. +# +# I opted not to make any of these methods on the metaclass, to make it more +# clear that I'm not really using any state there and to keep clients from +# thinking that they have direct access to these construction helpers. + + +def _PropertyName(proto_field_name): + """Returns the name of the public property attribute which + + clients can use to get and (in some cases) set the value + of a protocol message field. + + Args: + proto_field_name: The protocol message field name, exactly as it appears (or + would appear) in a .proto file. + """ + # TODO: Escape Python keywords (e.g., yield), and test this support. + # nnorwitz makes my day by writing: + # """ + # FYI. See the keyword module in the stdlib. This could be as simple as: + # + # if keyword.iskeyword(proto_field_name): + # return proto_field_name + "_" + # return proto_field_name + # """ + # Kenton says: The above is a BAD IDEA. People rely on being able to use + # getattr() and setattr() to reflectively manipulate field values. If we + # rename the properties, then every such user has to also make sure to apply + # the same transformation. Note that currently if you name a field "yield", + # you can still access it just fine using getattr/setattr -- it's not even + # that cumbersome to do so. + # TODO: Remove this method entirely if/when everyone agrees with my + # position. + return proto_field_name + + +def _AddSlots(message_descriptor, dictionary): + """Adds a __slots__ entry to dictionary, containing the names of all valid + + attributes for this message type. + + Args: + message_descriptor: A Descriptor instance describing this message type. + dictionary: Class dictionary to which we'll add a '__slots__' entry. + """ + dictionary['__slots__'] = [ + '_cached_byte_size', + '_cached_byte_size_dirty', + '_fields', + '_unknown_fields', + '_is_present_in_parent', + '_listener', + '_listener_for_children', + '__weakref__', + '_oneofs', + '_frozen', + ] + + +def _IsMessageSetExtension(field): + return ( + field.is_extension + and field.containing_type.has_options + and field.containing_type.GetOptions().message_set_wire_format + and field.type == _FieldDescriptor.TYPE_MESSAGE + and not field.is_required + and not field.is_repeated + ) + + +def _IsMapField(field): + return ( + field.type == _FieldDescriptor.TYPE_MESSAGE + and field.message_type._is_map_entry + ) + + +def _IsMessageMapField(field): + value_type = field.message_type.fields_by_name['value'] + return value_type.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE + + +def _AttachFieldHelpers(cls, field_descriptor): + field_descriptor._default_constructor = _DefaultValueConstructorForField( + field_descriptor + ) + + def AddFieldByTag(wiretype, is_packed): + tag_bytes = encoder.TagBytes(field_descriptor.number, wiretype) + cls._fields_by_tag[tag_bytes] = (field_descriptor, is_packed) + + AddFieldByTag( + type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_descriptor.type], False + ) + + if field_descriptor.is_repeated and wire_format.IsTypePackable( + field_descriptor.type + ): + # To support wire compatibility of adding packed = true, add a decoder for + # packed values regardless of the field's options. + AddFieldByTag(wire_format.WIRETYPE_LENGTH_DELIMITED, True) + + +def _MaybeAddEncoder(cls, field_descriptor): + if hasattr(field_descriptor, '_encoder'): + return + is_repeated = field_descriptor.is_repeated + is_map_entry = _IsMapField(field_descriptor) + is_packed = field_descriptor.is_packed + + if is_map_entry: + key_descriptor = field_descriptor.message_type.fields_by_name['key'] + value_descriptor = field_descriptor.message_type.fields_by_name['value'] + + key_sizer = type_checkers.TYPE_TO_SIZER[key_descriptor.type]( + key_descriptor.number, False, False + ) + value_sizer = type_checkers.TYPE_TO_SIZER[value_descriptor.type]( + value_descriptor.number, False, False + ) + + key_encoder = type_checkers.TYPE_TO_ENCODER[key_descriptor.type]( + key_descriptor.number, False, False + ) + value_encoder = type_checkers.TYPE_TO_ENCODER[value_descriptor.type]( + value_descriptor.number, False, False + ) + + field_encoder = encoder.MapEncoder( + field_descriptor, key_encoder, value_encoder, key_sizer, value_sizer + ) + sizer = encoder.MapSizer(field_descriptor, key_sizer, value_sizer) + elif _IsMessageSetExtension(field_descriptor): + field_encoder = encoder.MessageSetItemEncoder(field_descriptor.number) + sizer = encoder.MessageSetItemSizer(field_descriptor.number) + else: + field_encoder = type_checkers.TYPE_TO_ENCODER[field_descriptor.type]( + field_descriptor.number, is_repeated, is_packed + ) + sizer = type_checkers.TYPE_TO_SIZER[field_descriptor.type]( + field_descriptor.number, is_repeated, is_packed + ) + + field_descriptor._sizer = sizer + field_descriptor._encoder = field_encoder + + +def _MaybeAddDecoder(cls, field_descriptor): + if hasattr(field_descriptor, '_decoders'): + return + + is_repeated = field_descriptor.is_repeated + is_map_entry = _IsMapField(field_descriptor) + helper_decoders = {} + + def AddDecoder(is_packed): + decode_type = field_descriptor.type + if ( + decode_type == _FieldDescriptor.TYPE_ENUM + and not field_descriptor.enum_type.is_closed + ): + decode_type = _FieldDescriptor.TYPE_INT32 + + oneof_descriptor = None + if field_descriptor.containing_oneof is not None: + oneof_descriptor = field_descriptor + + if is_map_entry: + is_message_map = _IsMessageMapField(field_descriptor) + + field_decoder = decoder.MapDecoder( + field_descriptor, + _GetInitializeDefaultForMap(field_descriptor), + is_message_map, + ) + elif decode_type == _FieldDescriptor.TYPE_STRING: + field_decoder = decoder.StringDecoder( + field_descriptor.number, + is_repeated, + is_packed, + field_descriptor, + field_descriptor._default_constructor, + not field_descriptor.has_presence, + ) + elif field_descriptor.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + field_decoder = type_checkers.TYPE_TO_DECODER[decode_type]( + field_descriptor.number, + is_repeated, + is_packed, + field_descriptor, + field_descriptor._default_constructor, + ) + else: + field_decoder = type_checkers.TYPE_TO_DECODER[decode_type]( + field_descriptor.number, + is_repeated, + is_packed, + # pylint: disable=protected-access + field_descriptor, + field_descriptor._default_constructor, + not field_descriptor.has_presence, + ) + + helper_decoders[is_packed] = field_decoder + + AddDecoder(False) + + if is_repeated and wire_format.IsTypePackable(field_descriptor.type): + # To support wire compatibility of adding packed = true, add a decoder for + # packed values regardless of the field's options. + AddDecoder(True) + + field_descriptor._decoders = helper_decoders + + +def _AddClassAttributesForNestedExtensions(descriptor, dictionary): + extensions = descriptor.extensions_by_name + for extension_name, extension_field in extensions.items(): + assert extension_name not in dictionary + dictionary[extension_name] = extension_field + + +def _AddEnumValues(descriptor, cls): + """Sets class-level attributes for all enum fields defined in this message. + + Also exporting a class-level object that can name enum values. + + Args: + descriptor: Descriptor object for this message type. + cls: Class we're constructing for this message type. + """ + for enum_type in descriptor.enum_types: + setattr(cls, enum_type.name, enum_type_wrapper.EnumTypeWrapper(enum_type)) + for enum_value in enum_type.values: + setattr(cls, enum_value.name, enum_value.number) + + +def _GetInitializeDefaultForMap(field): + if not field.is_repeated: + raise ValueError('map_entry set on non-repeated field %s' % (field.name)) + fields_by_name = field.message_type.fields_by_name + key_checker = type_checkers.GetTypeChecker(fields_by_name['key']) + + value_field = fields_by_name['value'] + if _IsMessageMapField(field): + + def MakeMessageMapDefault(message): + return containers.MessageMap( + message._listener_for_children, + value_field.message_type, + key_checker, + field.message_type, + ) + + return MakeMessageMapDefault + else: + value_checker = type_checkers.GetTypeChecker(value_field) + + def MakePrimitiveMapDefault(message): + return containers.ScalarMap( + message._listener_for_children, + key_checker, + value_checker, + field.message_type, + ) + + return MakePrimitiveMapDefault + + +def _DefaultValueConstructorForField(field): + """Returns a function which returns a default value for a field. + + Args: + field: FieldDescriptor object for this field. + + The returned function has one argument: + message: Message instance containing this field, or a weakref proxy + of same. + + That function in turn returns a default value for this field. The default + value may refer back to |message| via a weak reference. + """ + + if _IsMapField(field): + return _GetInitializeDefaultForMap(field) + + if field.is_repeated: + if field.has_default_value and field.default_value != []: + raise ValueError( + 'Repeated field default value not empty list: %s' + % (field.default_value) + ) + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + # We can't look at _concrete_class yet since it might not have + # been set. (Depends on order in which we initialize the classes). + message_type = field.message_type + + def MakeRepeatedMessageDefault(message): + return containers.RepeatedCompositeFieldContainer( + message._listener_for_children, field.message_type + ) + + return MakeRepeatedMessageDefault + else: + type_checker = type_checkers.GetTypeChecker(field) + + def MakeRepeatedScalarDefault(message): + return containers.RepeatedScalarFieldContainer( + message._listener_for_children, type_checker, field + ) + + return MakeRepeatedScalarDefault + + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + message_type = field.message_type + + def MakeSubMessageDefault(message): + # _concrete_class may not yet be initialized. + if not hasattr(message_type, '_concrete_class'): + from google.protobuf import message_factory + + message_factory.GetMessageClass(message_type) + result = message_type._concrete_class() + result._SetListener( + _OneofListener(message, field) + if field.containing_oneof is not None + else message._listener_for_children + ) + return result + + return MakeSubMessageDefault + + def MakeScalarDefault(message): + # TODO: This may be broken since there may not be + # default_value. Combine with has_default_value somehow. + return field.default_value + + return MakeScalarDefault + + +def _ReraiseTypeErrorWithFieldName(message_name, field_name): + """Re-raise the currently-handled TypeError with the field name added.""" + exc = sys.exc_info()[1] + if len(exc.args) == 1 and type(exc) is TypeError: + # simple TypeError; add field name to exception message + exc = TypeError('%s for field %s.%s' % (str(exc), message_name, field_name)) + + # re-raise possibly-amended exception with original traceback: + raise exc.with_traceback(sys.exc_info()[2]) + + +def _AddInitMethod(message_descriptor, cls): + """Adds an __init__ method to cls.""" + + def _GetIntegerEnumValue(enum_type, value): + """Convert a string or integer enum value to an integer. + + If the value is a string, it is converted to the enum value in + enum_type with the same name. If the value is not a string, it's + returned as-is. (No conversion or bounds-checking is done.) + """ + if isinstance(value, str): + try: + return enum_type.values_by_name[value].number + except KeyError: + raise ValueError( + 'Enum type %s: unknown label "%s"' % (enum_type.full_name, value) + ) + return value + + def init(self, **kwargs): + + def init_wkt_or_merge(field, msg, value): + if isinstance(value, message_mod.Message): + msg.MergeFrom(value) + elif ( + isinstance(value, dict) + and field.message_type.full_name == _StructFullTypeName + ): + msg.Clear() + if len(value) == 1 and 'fields' in value: + try: + msg.update(value) + except: + msg.Clear() + msg.__init__(**value) + else: + msg.update(value) + elif hasattr(msg, '_internal_assign'): + msg._internal_assign(value) + else: + raise TypeError( + 'Message field {0}.{1} must be initialized with a ' + 'dict or instance of same class, got {2}.'.format( + message_descriptor.name, + field.name, + type(value).__name__, + ) + ) + + self._cached_byte_size = 0 + self._cached_byte_size_dirty = len(kwargs) > 0 + self._fields = {} + # Contains a mapping from oneof field descriptors to the descriptor + # of the currently set field in that oneof field. + self._oneofs = {} + + # _unknown_fields is () when empty for efficiency, and will be turned into + # a list if fields are added. + self._unknown_fields = () + self._is_present_in_parent = False + self._listener = message_listener_mod.NullMessageListener() + self._listener_for_children = _Listener(self) + self._frozen = False + for field_name, field_value in kwargs.items(): + field = _GetFieldByName(message_descriptor, field_name) + if field is None: + raise TypeError( + '%s() got an unexpected keyword argument "%s"' + % (message_descriptor.name, field_name) + ) + if field_value is None: + # field=None is the same as no field at all. + continue + if field.is_repeated: + field_copy = field._default_constructor(self) + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: # Composite + if _IsMapField(field): + if _IsMessageMapField(field): + for key in field_value: + item_value = field_value[key] + if isinstance(item_value, dict): + field_copy[key].__init__(**item_value) + else: + field_copy[key].MergeFrom(item_value) + else: + field_copy.update(field_value) + else: + for val in field_value: + if isinstance(val, dict) and ( + field.message_type.full_name != _StructFullTypeName + ): + field_copy.add(**val) + else: + new_msg = field_copy.add() + init_wkt_or_merge(field, new_msg, val) + else: # Scalar + if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM: + field_value = [ + _GetIntegerEnumValue(field.enum_type, val) + for val in field_value + ] + field_copy.extend(field_value) + self._fields[field] = field_copy + elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + field_copy = field._default_constructor(self) + if isinstance(field_value, dict) and ( + field.message_type.full_name != _StructFullTypeName + ): + new_val = field.message_type._concrete_class(**field_value) + field_copy.MergeFrom(new_val) + else: + try: + init_wkt_or_merge(field, field_copy, field_value) + except TypeError: + _ReraiseTypeErrorWithFieldName(message_descriptor.name, field_name) + self._fields[field] = field_copy + else: + if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM: + field_value = _GetIntegerEnumValue(field.enum_type, field_value) + try: + setattr(self, field_name, field_value) + except TypeError: + _ReraiseTypeErrorWithFieldName(message_descriptor.name, field_name) + + init.__module__ = None + init.__doc__ = None + cls.__init__ = init + + +def _GetFieldByName(message_descriptor, field_name): + """Returns a field descriptor by field name. + + Args: + message_descriptor: A Descriptor describing all fields in message. + field_name: The name of the field to retrieve. + + Returns: + The field descriptor associated with the field name. + """ + try: + return message_descriptor.fields_by_name[field_name] + except KeyError: + raise ValueError( + 'Protocol message %s has no "%s" field.' + % (message_descriptor.name, field_name) + ) + + +def _AddPropertiesForFields(descriptor, cls): + """Adds properties for all fields in this protocol message type.""" + for field in descriptor.fields: + _AddPropertiesForField(field, cls) + + if descriptor.is_extendable: + # _ExtensionDict is just an adaptor with no state so we allocate a new one + # every time it is accessed. + cls.Extensions = property(lambda self: _ExtensionDict(self)) + + +def _AddPropertiesForField(field, cls): + """Adds a public property for a protocol message field. + + Clients can use this property to get and (in the case of non-repeated scalar + fields) directly set the value of a protocol message field. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + # Catch it if we add other types that we should + # handle specially here. + assert _FieldDescriptor.MAX_CPPTYPE == 10 + + constant_name = field.name.upper() + '_FIELD_NUMBER' + setattr(cls, constant_name, field.number) + + if field.is_repeated: + _AddPropertiesForRepeatedField(field, cls) + elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + _AddPropertiesForNonRepeatedCompositeField(field, cls) + else: + _AddPropertiesForNonRepeatedScalarField(field, cls) + + +class _FieldProperty(property): + __slots__ = ('DESCRIPTOR',) + + def __init__(self, descriptor, getter, setter, doc): + property.__init__(self, getter, setter, doc=doc) + self.DESCRIPTOR = descriptor + + +def _AddPropertiesForRepeatedField(field, cls): + """Adds a public property for a "repeated" protocol message field. + + Clients can use this property to get the value of the field, which will be + either a RepeatedScalarFieldContainer or RepeatedCompositeFieldContainer (see + below). + + Note that when clients add values to these containers, we perform + type-checking in the case of repeated scalar fields, and we also set any + necessary "has" bits as a side-effect. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + proto_field_name = field.name + property_name = _PropertyName(proto_field_name) + + def getter(self): + field_value = self._fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + if self._frozen: + field_value._SetFrozen() + + # Atomically check if another thread has preempted us and, if not, swap + # in the new object we just created. If someone has preempted us, we + # take that object and discard ours. + # WARNING: We are relying on setdefault() being atomic. This is true + # in CPython but we haven't investigated others. This warning appears + # in several other locations in this file. + field_value = self._fields.setdefault(field, field_value) + return field_value + + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + # We define a setter just so we can throw an exception with a more + # helpful error message. + def setter(self, new_value): + raise AttributeError( + 'Assignment not allowed to repeated field ' + '"%s" in protocol message object.' % proto_field_name + ) + + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, _FieldProperty(field, getter, setter, doc=doc)) + + +def _AddPropertiesForNonRepeatedScalarField(field, cls): + """Adds a public property for a nonrepeated, scalar protocol message field. + + Clients can use this property to get and directly set the value of the field. + Note that when the client sets the value of a field by using this property, + all necessary "has" bits are set as a side-effect, and we also perform + type-checking. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + proto_field_name = field.name + property_name = _PropertyName(proto_field_name) + type_checker = type_checkers.GetTypeChecker(field) + default_value = field.default_value + + def getter(self): + # TODO: This may be broken since there may not be + # default_value. Combine with has_default_value somehow. + return self._fields.get(field, default_value) + + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + def field_setter(self, new_value): + self._AssureWritable() + # pylint: disable=protected-access + # Testing the value for truthiness captures all of the implicit presence + # defaults (0, 0.0, enum 0, and False), except for -0.0. + try: + new_value = type_checker.CheckValue(new_value) + except TypeError as e: + raise TypeError( + 'Cannot set %s to %.1024r: %s' % (field.full_name, new_value, e) + ) + if not field.has_presence and decoder.IsDefaultScalarValue(new_value): + self._fields.pop(field, None) + else: + self._fields[field] = new_value + # Check _cached_byte_size_dirty inline to improve performance, since scalar + # setters are called frequently. + if not self._cached_byte_size_dirty: + self._Modified() + + if field.containing_oneof: + + def setter(self, new_value): + field_setter(self, new_value) + self._UpdateOneofState(field) + + else: + setter = field_setter + + setter.__module__ = None + setter.__doc__ = 'Setter for %s.' % proto_field_name + + # Add a property to encapsulate the getter/setter. + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, _FieldProperty(field, getter, setter, doc=doc)) + + +def _AddPropertiesForNonRepeatedCompositeField(field, cls): + """Adds a public property for a nonrepeated, composite protocol message field. + + A composite field is a "group" or "message" field. + + Clients can use this property to get the value of the field, but cannot + assign to the property directly. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + # TODO: Remove duplication with similar method + # for non-repeated scalars. + proto_field_name = field.name + property_name = _PropertyName(proto_field_name) + + def getter(self): + field_value = self._fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + if self._frozen: + field_value._SetFrozen() + + # Atomically check if another thread has preempted us and, if not, swap + # in the new object we just created. If someone has preempted us, we + # take that object and discard ours. + # WARNING: We are relying on setdefault() being atomic. This is true + # in CPython but we haven't investigated others. This warning appears + # in several other locations in this file. + field_value = self._fields.setdefault(field, field_value) + return field_value + + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + # We define a setter just so we can throw an exception with a more + # helpful error message. + def setter(self, new_value): + self._AssureWritable() + if field.message_type.full_name == 'google.protobuf.Timestamp': + getter(self) + self._fields[field].FromDatetime(new_value) + elif field.message_type.full_name == 'google.protobuf.Duration': + getter(self) + self._fields[field].FromTimedelta(new_value) + elif field.message_type.full_name == _StructFullTypeName: + getter(self) + self._fields[field].Clear() + self._fields[field].update(new_value) + elif field.message_type.full_name == _ListValueFullTypeName: + getter(self) + self._fields[field].Clear() + self._fields[field].extend(new_value) + else: + raise AttributeError( + 'Assignment not allowed to composite field ' + '"%s" in protocol message object.' % proto_field_name + ) + + # Add a property to encapsulate the getter. + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, _FieldProperty(field, getter, setter, doc=doc)) + + +def _AddPropertiesForExtensions(descriptor, cls): + """Adds properties for all fields in this protocol message type.""" + extensions = descriptor.extensions_by_name + for extension_name, extension_field in extensions.items(): + constant_name = extension_name.upper() + '_FIELD_NUMBER' + setattr(cls, constant_name, extension_field.number) + + # TODO: Migrate all users of these attributes to functions like + # pool.FindExtensionByNumber(descriptor). + if descriptor.file is not None: + # TODO: Use cls.MESSAGE_FACTORY.pool when available. + pool = descriptor.file.pool + + +def _AddStaticMethods(cls): + + def RegisterExtension(_): + """no-op to keep generated code <=4.23 working with new runtimes.""" + # This was originally removed in 5.26 (cl/595989309). + pass + + cls.RegisterExtension = staticmethod(RegisterExtension) + + def FromString(s): + message = cls() + message.MergeFromString(s) + return message + + cls.FromString = staticmethod(FromString) + + +def _IsPresent(item): + """Given a (FieldDescriptor, value) tuple from _fields, return true if the + + value should be included in the list returned by ListFields(). + """ + + if item[0].is_repeated: + return bool(item[1]) + elif item[0].cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + return item[1]._is_present_in_parent + else: + return True + + +def _AddListFieldsMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def ListFields(self): + all_fields = [item for item in self._fields.items() if _IsPresent(item)] + all_fields.sort(key=lambda item: item[0].number) + return all_fields + + cls.ListFields = ListFields + + +def _AddHasFieldMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + hassable_fields = {} + for field in message_descriptor.fields: + if field.is_repeated: + continue + # For proto3, only submessages and fields inside a oneof have presence. + if not field.has_presence: + continue + hassable_fields[field.name] = field + + # Has methods are supported for oneof descriptors. + for oneof in message_descriptor.oneofs: + hassable_fields[oneof.name] = oneof + + def HasField(self, field_name): + try: + field = hassable_fields[field_name] + except KeyError as exc: + raise ValueError( + 'Protocol message %s has no non-repeated field "%s" ' + 'nor has presence is not available for this field.' + % (message_descriptor.full_name, field_name) + ) from exc + + if isinstance(field, descriptor_mod.OneofDescriptor): + try: + return HasField(self, self._oneofs[field].name) + except KeyError: + return False + else: + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + value = self._fields.get(field) + return value is not None and value._is_present_in_parent + else: + return field in self._fields + + cls.HasField = HasField + + +def _AddClearFieldMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def ClearField(self, field_name): + self._AssureWritable() + try: + field = message_descriptor.fields_by_name[field_name] + except KeyError: + try: + field = message_descriptor.oneofs_by_name[field_name] + if field in self._oneofs: + field = self._oneofs[field] + else: + return + except KeyError: + raise ValueError( + 'Protocol message %s has no "%s" field.' + % (message_descriptor.name, field_name) + ) + + if field in self._fields: + # To match the C++ implementation, we need to invalidate iterators + # for map fields when ClearField() happens. + if hasattr(self._fields[field], 'InvalidateIterators'): + self._fields[field].InvalidateIterators() + + # Note: If the field is a sub-message, its listener will still point + # at us. That's fine, because the worst than can happen is that it + # will call _Modified() and invalidate our byte size. Big deal. + del self._fields[field] + + if self._oneofs.get(field.containing_oneof, None) is field: + del self._oneofs[field.containing_oneof] + + # Always call _Modified() -- even if nothing was changed, this is + # a mutating method, and thus calling it should cause the field to become + # present in the parent message. + self._Modified() + + cls.ClearField = ClearField + + +def _AddClearExtensionMethod(cls): + """Helper for _AddMessageMethods().""" + + def ClearExtension(self, field_descriptor): + self._AssureWritable() + extension_dict._VerifyExtensionHandle(self, field_descriptor) + + # Similar to ClearField(), above. + if field_descriptor in self._fields: + del self._fields[field_descriptor] + self._Modified() + + cls.ClearExtension = ClearExtension + + +def _AddHasExtensionMethod(cls): + """Helper for _AddMessageMethods().""" + + def HasExtension(self, field_descriptor): + extension_dict._VerifyExtensionHandle(self, field_descriptor) + if field_descriptor.is_repeated: + raise KeyError('"%s" is repeated.' % field_descriptor.full_name) + + if field_descriptor.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + value = self._fields.get(field_descriptor) + return value is not None and value._is_present_in_parent + else: + return field_descriptor in self._fields + + cls.HasExtension = HasExtension + + +def _InternalUnpackAny(msg): + """Unpacks Any message and returns the unpacked message. + + This internal method is different from public Any Unpack method which takes + the target message as argument. _InternalUnpackAny method does not have + target message type and need to find the message type in descriptor pool. + + Args: + msg: An Any message to be unpacked. + + Returns: + The unpacked message. + """ + # TODO: Don't use the factory of generated messages. + # To make Any work with custom factories, use the message factory of the + # parent message. + # pylint: disable=g-import-not-at-top + from google.protobuf import symbol_database + + factory = symbol_database.Default() + + type_url = msg.type_url + + if not type_url: + return None + + # TODO: For now we just strip the hostname. Better logic will be + # required. + type_name = type_url.split('/')[-1] + descriptor = factory.pool.FindMessageTypeByName(type_name) + + if descriptor is None: + return None + + # Unable to import message_factory at top because of circular import. + # pylint: disable=g-import-not-at-top + from google.protobuf import message_factory + + message_class = message_factory.GetMessageClass(descriptor) + message = message_class() + + message.ParseFromString(msg.value) + return message + + +def _AddEqualsMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def __eq__(self, other): + if self.DESCRIPTOR.full_name == _ListValueFullTypeName and isinstance( + other, list + ): + return self._internal_compare(other) + if self.DESCRIPTOR.full_name == _StructFullTypeName and isinstance( + other, dict + ): + return self._internal_compare(other) + + if ( + not isinstance(other, message_mod.Message) + or other.DESCRIPTOR != self.DESCRIPTOR + ): + return NotImplemented + + if self is other: + return True + + if self.DESCRIPTOR.full_name == _AnyFullTypeName: + any_a = _InternalUnpackAny(self) + any_b = _InternalUnpackAny(other) + if any_a and any_b: + return any_a == any_b + + if not self.ListFields() == other.ListFields(): + return False + + # TODO: Fix UnknownFieldSet to consider MessageSet extensions, + # then use it for the comparison. + unknown_fields = list(self._unknown_fields) + unknown_fields.sort() + other_unknown_fields = list(other._unknown_fields) + other_unknown_fields.sort() + return unknown_fields == other_unknown_fields + + cls.__eq__ = __eq__ + + +def _AddStrMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def __str__(self): + return text_format.MessageToString(self) + + cls.__str__ = __str__ + + +def _AddReprMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def __repr__(self): + return text_format.MessageToString(self) + + cls.__repr__ = __repr__ + + +def _AddUnicodeMethod(unused_message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def __unicode__(self): + return text_format.MessageToString(self, as_utf8=True).decode('utf-8') + + cls.__unicode__ = __unicode__ + + +def _AddContainsMethod(message_descriptor, cls): + + if message_descriptor.full_name == 'google.protobuf.Struct': + + def __contains__(self, key): + return key in self.fields + + elif message_descriptor.full_name == 'google.protobuf.ListValue': + + def __contains__(self, value): + return value in self.items() + + else: + + def __contains__(self, field): + return self.HasField(field) + + cls.__contains__ = __contains__ + + +def _BytesForNonRepeatedElement(value, field_number, field_type): + """Returns the number of bytes needed to serialize a non-repeated element. + + The returned byte count includes space for tag information and any other + additional space associated with serializing value. + + Args: + value: Value we're serializing. + field_number: Field number of this value. (Since the field number is stored + as part of a varint-encoded tag, this has an impact on the total bytes + required to serialize the value). + field_type: The type of the field. One of the TYPE_* constants within + FieldDescriptor. + """ + try: + fn = type_checkers.TYPE_TO_BYTE_SIZE_FN[field_type] + return fn(field_number, value) + except KeyError: + raise message_mod.EncodeError('Unrecognized field type: %d' % field_type) + + +def _AddByteSizeMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def ByteSize(self): + if not self._cached_byte_size_dirty: + return self._cached_byte_size + + size = 0 + descriptor = self.DESCRIPTOR + if descriptor._is_map_entry: + # Fields of map entry should always be serialized. + key_field = descriptor.fields_by_name['key'] + _MaybeAddEncoder(cls, key_field) + size = key_field._sizer(self.key) + value_field = descriptor.fields_by_name['value'] + _MaybeAddEncoder(cls, value_field) + size += value_field._sizer(self.value) + else: + for field_descriptor, field_value in self.ListFields(): + _MaybeAddEncoder(cls, field_descriptor) + size += field_descriptor._sizer(field_value) + for tag_bytes, value_bytes in self._unknown_fields: + size += len(tag_bytes) + len(value_bytes) + + self._cached_byte_size = size + self._cached_byte_size_dirty = False + self._listener_for_children.dirty = False + return size + + cls.ByteSize = ByteSize + + +def _AddSerializeToStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def SerializeToString(self, **kwargs): + # Check if the message has all of its required fields set. + if not self.IsInitialized(): + raise message_mod.EncodeError( + 'Message %s is missing required fields: %s' + % ( + self.DESCRIPTOR.full_name, + ','.join(self.FindInitializationErrors()), + ) + ) + return self.SerializePartialToString(**kwargs) + + cls.SerializeToString = SerializeToString + + +def _AddSerializePartialToStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def SerializePartialToString(self, **kwargs): + out = BytesIO() + self._InternalSerialize(out.write, **kwargs) + return out.getvalue() + + cls.SerializePartialToString = SerializePartialToString + + def InternalSerialize(self, write_bytes, deterministic=None): + if deterministic is None: + deterministic = ( + api_implementation.IsPythonDefaultSerializationDeterministic() + ) + else: + deterministic = bool(deterministic) + + descriptor = self.DESCRIPTOR + if descriptor._is_map_entry: + # Fields of map entry should always be serialized. + key_field = descriptor.fields_by_name['key'] + _MaybeAddEncoder(cls, key_field) + key_field._encoder(write_bytes, self.key, deterministic) + value_field = descriptor.fields_by_name['value'] + _MaybeAddEncoder(cls, value_field) + value_field._encoder(write_bytes, self.value, deterministic) + else: + for field_descriptor, field_value in self.ListFields(): + _MaybeAddEncoder(cls, field_descriptor) + field_descriptor._encoder(write_bytes, field_value, deterministic) + for tag_bytes, value_bytes in self._unknown_fields: + write_bytes(tag_bytes) + write_bytes(value_bytes) + + cls._InternalSerialize = InternalSerialize + + +def _AddMergeFromStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def MergeFromString(self, serialized): + self._AssureWritable() + serialized = memoryview(serialized) + length = len(serialized) + try: + if self._InternalParse(serialized, 0, length) != length: + # The only reason _InternalParse would return early is if it + # encountered an end-group tag. + raise message_mod.DecodeError('Unexpected end-group tag.') + except (IndexError, TypeError): + # Now ord(buf[p:p+1]) == ord('') gets TypeError. + raise message_mod.DecodeError('Truncated message.') + except struct.error as e: + raise message_mod.DecodeError(e) + return length # Return this for legacy reasons. + + cls.MergeFromString = MergeFromString + + fields_by_tag = cls._fields_by_tag + message_set_decoders_by_tag = cls._message_set_decoders_by_tag + + def InternalParse(self, buffer, pos, end, current_depth=0): + """Create a message from serialized bytes. + + Args: + self: Message, instance of the proto message object. + buffer: memoryview of the serialized data. + pos: int, position to start in the serialized data. + end: int, end position of the serialized data. + + Returns: + Message object. + """ + # Guard against internal misuse, since this function is called internally + # quite extensively, and its easy to accidentally pass bytes. + assert isinstance(buffer, memoryview) + self._Modified() + field_dict = self._fields + while pos != end: + tag_bytes, new_pos = decoder.ReadTag(buffer, pos) + field_decoder, field_des = message_set_decoders_by_tag.get( + tag_bytes, (None, None) + ) + if field_decoder: + pos = field_decoder( + buffer, new_pos, end, self, field_dict, current_depth + ) + continue + field_des, is_packed = fields_by_tag.get(tag_bytes, (None, None)) + if field_des is None: + if not self._unknown_fields: # pylint: disable=protected-access + self._unknown_fields = [] # pylint: disable=protected-access + field_number, wire_type = decoder.DecodeTag(tag_bytes) + if field_number == 0: + raise message_mod.DecodeError('Field number 0 is illegal.') + data, new_pos = decoder._DecodeUnknownField( + buffer, new_pos, end, field_number, wire_type + ) # pylint: disable=protected-access + if new_pos == -1: + return pos + self._unknown_fields.append( + (tag_bytes, buffer[pos + len(tag_bytes) : new_pos].tobytes()) + ) + pos = new_pos + else: + _MaybeAddDecoder(cls, field_des) + field_decoder = field_des._decoders[is_packed] + pos = field_decoder( + buffer, new_pos, end, self, field_dict, current_depth + ) + if field_des.containing_oneof: + self._UpdateOneofState(field_des) + return pos + + cls._InternalParse = InternalParse + + +def _AddIsInitializedMethod(message_descriptor, cls): + """Adds the IsInitialized and FindInitializationError methods to the + + protocol message class. + """ + + required_fields = [ + field for field in message_descriptor.fields if field.is_required + ] + + def IsInitialized(self, errors=None): + """Checks if all required fields of a message are set. + + Args: + errors: A list which, if provided, will be populated with the field paths + of all missing required fields. + + Returns: + True iff the specified message has all required fields set. + """ + + # Performance is critical so we avoid HasField() and ListFields(). + + for field in required_fields: + if field not in self._fields or ( + field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE + and not self._fields[field]._is_present_in_parent + ): + if errors is not None: + errors.extend(self.FindInitializationErrors()) + return False + + for field, value in list(self._fields.items()): # dict can change size! + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if field.is_repeated: + if field.message_type._is_map_entry: + continue + for element in value: + if not element.IsInitialized(): + if errors is not None: + errors.extend(self.FindInitializationErrors()) + return False + elif value._is_present_in_parent and not value.IsInitialized(): + if errors is not None: + errors.extend(self.FindInitializationErrors()) + return False + + return True + + cls.IsInitialized = IsInitialized + + def FindInitializationErrors(self): + """Finds required fields which are not initialized. + + Returns: + A list of strings. Each string is a path to an uninitialized field from + the top-level message, e.g. "foo.bar[5].baz". + """ + + errors = [] # simplify things + + for field in required_fields: + if not self.HasField(field.name): + errors.append(field.name) + + for field, value in self.ListFields(): + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if field.is_extension: + name = '(%s)' % field.full_name + else: + name = field.name + + if _IsMapField(field): + if _IsMessageMapField(field): + for key in value: + element = value[key] + prefix = '%s[%s].' % (name, key) + sub_errors = element.FindInitializationErrors() + errors += [prefix + error for error in sub_errors] + else: + # ScalarMaps can't have any initialization errors. + pass + elif field.is_repeated: + for i in range(len(value)): + element = value[i] + prefix = '%s[%d].' % (name, i) + sub_errors = element.FindInitializationErrors() + errors += [prefix + error for error in sub_errors] + else: + prefix = name + '.' + sub_errors = value.FindInitializationErrors() + errors += [prefix + error for error in sub_errors] + + return errors + + cls.FindInitializationErrors = FindInitializationErrors + + +def _FullyQualifiedClassName(klass): + module = klass.__module__ + name = getattr(klass, '__qualname__', klass.__name__) + if module in (None, 'builtins', '__builtin__'): + return name + return module + '.' + name + + +def _AddMergeFromMethod(cls): + CPPTYPE_MESSAGE = _FieldDescriptor.CPPTYPE_MESSAGE + + def MergeFrom(self, msg): + self._AssureWritable() + if not isinstance(msg, cls): + raise TypeError( + 'Parameter to MergeFrom() must be instance of same class: ' + 'expected %s got %s.' + % ( + _FullyQualifiedClassName(cls), + _FullyQualifiedClassName(msg.__class__), + ) + ) + + assert msg is not self + self._Modified() + + fields = self._fields + + for field, value in msg._fields.items(): + if field.is_repeated: + field_value = fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + fields[field] = field_value + field_value.MergeFrom(value) + elif field.cpp_type == CPPTYPE_MESSAGE: + if value._is_present_in_parent: + field_value = fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + fields[field] = field_value + field_value.MergeFrom(value) + else: + self._fields[field] = value + if field.containing_oneof: + self._UpdateOneofState(field) + + if msg._unknown_fields: + if not self._unknown_fields: + self._unknown_fields = [] + self._unknown_fields.extend(msg._unknown_fields) + + cls.MergeFrom = MergeFrom + + +def _AddWhichOneofMethod(message_descriptor, cls): + + def WhichOneof(self, oneof_name): + """Returns the name of the currently set field inside a oneof, or None.""" + try: + field = message_descriptor.oneofs_by_name[oneof_name] + except KeyError: + raise ValueError('Protocol message has no oneof "%s" field.' % oneof_name) + + nested_field = self._oneofs.get(field, None) + if nested_field is not None and self.HasField(nested_field.name): + return nested_field.name + else: + return None + + cls.WhichOneof = WhichOneof + + +def _Clear(self): + self._AssureWritable() + # Clear fields. + self._fields = {} + self._unknown_fields = () + + self._oneofs = {} + self._Modified() + + +def _SetFrozen(self): + self._frozen = True + for value in self._fields.values(): + if hasattr(value, '_SetFrozen'): + value._SetFrozen() + + +def _AssureWritable(self): + if self._frozen: + warnings.warn( + 'Mutating messages or containers returned by GetOptions() is' + ' deprecated and will raise an exception in a future release.', + category=FutureWarning, + stacklevel=3, + ) + return self + + +def _UnknownFields(self): + raise NotImplementedError( + 'Please use the add-on feaure ' + 'unknown_fields.UnknownFieldSet(message) in ' + 'unknown_fields.py instead.' + ) + + +def _DiscardUnknownFields(self): + self._AssureWritable() + self._unknown_fields = [] + for field, value in self.ListFields(): + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if _IsMapField(field): + if _IsMessageMapField(field): + for key in value: + value[key].DiscardUnknownFields() + elif field.is_repeated: + for sub_message in value: + sub_message.DiscardUnknownFields() + else: + value.DiscardUnknownFields() + + +def _SetListener(self, listener): + if listener is None: + self._listener = message_listener_mod.NullMessageListener() + else: + self._listener = listener + + +def _AddMessageMethods(message_descriptor, cls): + """Adds implementations of all Message methods to cls.""" + _AddListFieldsMethod(message_descriptor, cls) + _AddHasFieldMethod(message_descriptor, cls) + _AddClearFieldMethod(message_descriptor, cls) + if message_descriptor.is_extendable: + _AddClearExtensionMethod(cls) + _AddHasExtensionMethod(cls) + _AddEqualsMethod(message_descriptor, cls) + _AddStrMethod(message_descriptor, cls) + _AddReprMethod(message_descriptor, cls) + _AddUnicodeMethod(message_descriptor, cls) + _AddContainsMethod(message_descriptor, cls) + _AddByteSizeMethod(message_descriptor, cls) + _AddSerializeToStringMethod(message_descriptor, cls) + _AddSerializePartialToStringMethod(message_descriptor, cls) + _AddMergeFromStringMethod(message_descriptor, cls) + _AddIsInitializedMethod(message_descriptor, cls) + _AddMergeFromMethod(cls) + _AddWhichOneofMethod(message_descriptor, cls) + # Adds methods which do not depend on cls. + cls.Clear = _Clear + cls.DiscardUnknownFields = _DiscardUnknownFields + cls._SetListener = _SetListener + cls._SetFrozen = _SetFrozen + cls._AssureWritable = _AssureWritable + + +def _AddPrivateHelperMethods(message_descriptor, cls): + """Adds implementation of private helper methods to cls.""" + + def Modified(self): + """Sets the _cached_byte_size_dirty bit to true, + + and propagates this to our listener iff this was a state change. + """ + + # Note: Some callers check _cached_byte_size_dirty before calling + # _Modified() as an extra optimization. So, if this method is ever + # changed such that it does stuff even when _cached_byte_size_dirty is + # already true, the callers need to be updated. + if not self._cached_byte_size_dirty: + self._cached_byte_size_dirty = True + self._listener_for_children.dirty = True + self._is_present_in_parent = True + self._listener.Modified() + + def _UpdateOneofState(self, field): + """Sets field as the active field in its containing oneof. + + Will also delete currently active field in the oneof, if it is different + from the argument. Does not mark the message as modified. + """ + other_field = self._oneofs.setdefault(field.containing_oneof, field) + if other_field is not field: + del self._fields[other_field] + self._oneofs[field.containing_oneof] = field + + cls._Modified = Modified + cls.SetInParent = Modified + cls._UpdateOneofState = _UpdateOneofState + + +class _Listener(object): + """MessageListener implementation that a parent message registers with its + + child message. + + In order to support semantics like: + + foo.bar.baz.moo = 23 + assert foo.HasField('bar') + + ...child objects must have back references to their parents. + This helper class is at the heart of this support. + """ + + def __init__(self, parent_message): + """Args: + + parent_message: The message whose _Modified() method we should call when + we receive Modified() messages. + """ + # This listener establishes a back reference from a child (contained) object + # to its parent (containing) object. We make this a weak reference to avoid + # creating cyclic garbage when the client finishes with the 'parent' object + # in the tree. + if isinstance(parent_message, weakref.ProxyType): + self._parent_message_weakref = parent_message + else: + self._parent_message_weakref = weakref.proxy(parent_message) + + # As an optimization, we also indicate directly on the listener whether + # or not the parent message is dirty. This way we can avoid traversing + # up the tree in the common case. + self.dirty = False + + def Modified(self): + if self.dirty: + return + try: + # Propagate the signal to our parents iff this is the first field set. + self._parent_message_weakref._Modified() + except ReferenceError: + # We can get here if a client has kept a reference to a child object, + # and is now setting a field on it, but the child's parent has been + # garbage-collected. This is not an error. + pass + + +class _OneofListener(_Listener): + """Special listener implementation for setting composite oneof fields.""" + + def __init__(self, parent_message, field): + """Args: + + parent_message: The message whose _Modified() method we should call when + we receive Modified() messages. + field: The descriptor of the field being set in the parent message. + """ + super(_OneofListener, self).__init__(parent_message) + self._field = field + + def Modified(self): + """Also updates the state of the containing oneof in the parent message.""" + try: + self._parent_message_weakref._UpdateOneofState(self._field) + super(_OneofListener, self).Modified() + except ReferenceError: + pass diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/testing_refleaks.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/testing_refleaks.py new file mode 100755 index 0000000..53728ed --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/testing_refleaks.py @@ -0,0 +1,136 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +"""A subclass of unittest.TestCase which checks for reference leaks. + +To use: +- Use testing_refleak.BaseTestCase instead of unittest.TestCase +- Configure and compile Python with --with-pydebug + +If sys.gettotalrefcount() is not available (because Python was built without +the Py_DEBUG option), then this module is a no-op and tests will run normally. +""" + +import copyreg +import gc +import sys +import unittest + + +class LocalTestResult(unittest.TestResult): + """A TestResult which forwards events to a parent object, except for Skips.""" + + def __init__(self, parent_result): + unittest.TestResult.__init__(self) + self.parent_result = parent_result + + def addError(self, test, error): + self.parent_result.addError(test, error) + + def addFailure(self, test, error): + self.parent_result.addFailure(test, error) + + def addSkip(self, test, reason): + pass + + def addDuration(self, test, duration): + pass + + +class ReferenceLeakCheckerMixin(object): + """A mixin class for TestCase, which checks reference counts.""" + + NB_RUNS = 3 + + def run(self, result=None): + testMethod = getattr(self, self._testMethodName) + expecting_failure_method = getattr( + testMethod, "__unittest_expecting_failure__", False + ) + expecting_failure_class = getattr( + self, "__unittest_expecting_failure__", False + ) + if expecting_failure_class or expecting_failure_method: + return + + # python_message.py registers all Message classes to some pickle global + # registry, which makes the classes immortal. + # We save a copy of this registry, and reset it before we could references. + self._saved_pickle_registry = copyreg.dispatch_table.copy() + + # Run the test twice, to warm up the instance attributes. + super(ReferenceLeakCheckerMixin, self).run(result=result) + super(ReferenceLeakCheckerMixin, self).run(result=result) + + local_result = LocalTestResult(result) + num_flakes = 0 + refcount_deltas = [] + + # Observe the refcount, then create oldrefcount which actually makes the + # refcount 1 higher than the recorded value immediately + oldrefcount = self._getRefcounts() + while len(refcount_deltas) < self.NB_RUNS: + oldrefcount = self._getRefcounts() + super(ReferenceLeakCheckerMixin, self).run(result=local_result) + newrefcount = self._getRefcounts() + # If the GC was able to collect some objects after the call to run() that + # it could not collect before the call, then the counts won't match. + if newrefcount < oldrefcount and num_flakes < 2: + # This result is (probably) a flake -- garbage collectors aren't very + # predictable, but a lower ending refcount is the opposite of the + # failure we are testing for. If the result is repeatable, then we will + # eventually report it, but not after trying to eliminate it. + num_flakes += 1 + continue + num_flakes = 0 + refcount_deltas.append(newrefcount - oldrefcount) + print(refcount_deltas, self) + + try: + self.assertEqual(refcount_deltas, [0] * self.NB_RUNS) + except Exception: # pylint: disable=broad-except + result.addError(self, sys.exc_info()) + + def _getRefcounts(self): + if hasattr(sys, "_clear_internal_caches"): # Since 3.13 + sys._clear_internal_caches() # pylint: disable=protected-access + else: + sys._clear_type_cache() # pylint: disable=protected-access + copyreg.dispatch_table.clear() + copyreg.dispatch_table.update(self._saved_pickle_registry) + # It is sometimes necessary to gc.collect() multiple times, to ensure + # that all objects can be collected. + gc.collect() + gc.collect() + gc.collect() + return sys.gettotalrefcount() + + +if hasattr(sys, "gettotalrefcount"): + + def TestCase(test_class): + new_bases = (ReferenceLeakCheckerMixin,) + test_class.__bases__ + new_class = type(test_class)( + test_class.__name__, new_bases, dict(test_class.__dict__) + ) + return new_class + + SkipReferenceLeakChecker = unittest.skip + +else: + # When PyDEBUG is not enabled, run the tests normally. + + def TestCase(test_class): + return test_class + + def SkipReferenceLeakChecker(reason): + del reason # Don't skip, so don't need a reason. + + def Same(func): + return func + + return Same diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/type_checkers.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/type_checkers.py new file mode 100755 index 0000000..e959688 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/type_checkers.py @@ -0,0 +1,465 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Provides type checking routines. + +This module defines type checking utilities in the forms of dictionaries: + +VALUE_CHECKERS: A dictionary of field types and a value validation object. +TYPE_TO_BYTE_SIZE_FN: A dictionary with field types and a size computing + function. +TYPE_TO_SERIALIZE_METHOD: A dictionary with field types and serialization + function. +FIELD_TYPE_TO_WIRE_TYPE: A dictionary with field typed and their + corresponding wire types. +TYPE_TO_DESERIALIZE_METHOD: A dictionary with field types and deserialization + function. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import numbers +import struct +import warnings + +from google.protobuf import descriptor +from google.protobuf.internal import decoder +from google.protobuf.internal import encoder +from google.protobuf.internal import wire_format + +_FieldDescriptor = descriptor.FieldDescriptor + + +def TruncateToFourByteFloat(original): + return struct.unpack(' _FLOAT_MAX: + if converted_value <= _MAX_FLOAT_AS_DOUBLE_ROUNDED: + return _FLOAT_MAX + return _INF + if converted_value < _FLOAT_MIN: + if converted_value >= -_MAX_FLOAT_AS_DOUBLE_ROUNDED: + return _FLOAT_MIN + return _NEG_INF + + return TruncateToFourByteFloat(converted_value) + + +# Type-checkers for all scalar CPPTYPEs. +_VALUE_CHECKERS = { + _FieldDescriptor.CPPTYPE_INT32: Int32ValueChecker(), + _FieldDescriptor.CPPTYPE_INT64: Int64ValueChecker(), + _FieldDescriptor.CPPTYPE_UINT32: Uint32ValueChecker(), + _FieldDescriptor.CPPTYPE_UINT64: Uint64ValueChecker(), + _FieldDescriptor.CPPTYPE_DOUBLE: DoubleValueChecker(), + _FieldDescriptor.CPPTYPE_FLOAT: FloatValueChecker(), + _FieldDescriptor.CPPTYPE_BOOL: BoolValueChecker(), + _FieldDescriptor.CPPTYPE_STRING: TypeCheckerWithDefault(b'', bytes), +} + +# Map from field type to a function F, such that F(field_num, value) +# gives the total byte size for a value of the given type. This +# byte size includes tag information and any other additional space +# associated with serializing "value". +TYPE_TO_BYTE_SIZE_FN = { + _FieldDescriptor.TYPE_DOUBLE: wire_format.DoubleByteSize, + _FieldDescriptor.TYPE_FLOAT: wire_format.FloatByteSize, + _FieldDescriptor.TYPE_INT64: wire_format.Int64ByteSize, + _FieldDescriptor.TYPE_UINT64: wire_format.UInt64ByteSize, + _FieldDescriptor.TYPE_INT32: wire_format.Int32ByteSize, + _FieldDescriptor.TYPE_FIXED64: wire_format.Fixed64ByteSize, + _FieldDescriptor.TYPE_FIXED32: wire_format.Fixed32ByteSize, + _FieldDescriptor.TYPE_BOOL: wire_format.BoolByteSize, + _FieldDescriptor.TYPE_STRING: wire_format.StringByteSize, + _FieldDescriptor.TYPE_GROUP: wire_format.GroupByteSize, + _FieldDescriptor.TYPE_MESSAGE: wire_format.MessageByteSize, + _FieldDescriptor.TYPE_BYTES: wire_format.BytesByteSize, + _FieldDescriptor.TYPE_UINT32: wire_format.UInt32ByteSize, + _FieldDescriptor.TYPE_ENUM: wire_format.EnumByteSize, + _FieldDescriptor.TYPE_SFIXED32: wire_format.SFixed32ByteSize, + _FieldDescriptor.TYPE_SFIXED64: wire_format.SFixed64ByteSize, + _FieldDescriptor.TYPE_SINT32: wire_format.SInt32ByteSize, + _FieldDescriptor.TYPE_SINT64: wire_format.SInt64ByteSize, +} + +# Maps from field types to encoder constructors. +TYPE_TO_ENCODER = { + _FieldDescriptor.TYPE_DOUBLE: encoder.DoubleEncoder, + _FieldDescriptor.TYPE_FLOAT: encoder.FloatEncoder, + _FieldDescriptor.TYPE_INT64: encoder.Int64Encoder, + _FieldDescriptor.TYPE_UINT64: encoder.UInt64Encoder, + _FieldDescriptor.TYPE_INT32: encoder.Int32Encoder, + _FieldDescriptor.TYPE_FIXED64: encoder.Fixed64Encoder, + _FieldDescriptor.TYPE_FIXED32: encoder.Fixed32Encoder, + _FieldDescriptor.TYPE_BOOL: encoder.BoolEncoder, + _FieldDescriptor.TYPE_STRING: encoder.StringEncoder, + _FieldDescriptor.TYPE_GROUP: encoder.GroupEncoder, + _FieldDescriptor.TYPE_MESSAGE: encoder.MessageEncoder, + _FieldDescriptor.TYPE_BYTES: encoder.BytesEncoder, + _FieldDescriptor.TYPE_UINT32: encoder.UInt32Encoder, + _FieldDescriptor.TYPE_ENUM: encoder.EnumEncoder, + _FieldDescriptor.TYPE_SFIXED32: encoder.SFixed32Encoder, + _FieldDescriptor.TYPE_SFIXED64: encoder.SFixed64Encoder, + _FieldDescriptor.TYPE_SINT32: encoder.SInt32Encoder, + _FieldDescriptor.TYPE_SINT64: encoder.SInt64Encoder, +} + +# Maps from field types to sizer constructors. +TYPE_TO_SIZER = { + _FieldDescriptor.TYPE_DOUBLE: encoder.DoubleSizer, + _FieldDescriptor.TYPE_FLOAT: encoder.FloatSizer, + _FieldDescriptor.TYPE_INT64: encoder.Int64Sizer, + _FieldDescriptor.TYPE_UINT64: encoder.UInt64Sizer, + _FieldDescriptor.TYPE_INT32: encoder.Int32Sizer, + _FieldDescriptor.TYPE_FIXED64: encoder.Fixed64Sizer, + _FieldDescriptor.TYPE_FIXED32: encoder.Fixed32Sizer, + _FieldDescriptor.TYPE_BOOL: encoder.BoolSizer, + _FieldDescriptor.TYPE_STRING: encoder.StringSizer, + _FieldDescriptor.TYPE_GROUP: encoder.GroupSizer, + _FieldDescriptor.TYPE_MESSAGE: encoder.MessageSizer, + _FieldDescriptor.TYPE_BYTES: encoder.BytesSizer, + _FieldDescriptor.TYPE_UINT32: encoder.UInt32Sizer, + _FieldDescriptor.TYPE_ENUM: encoder.EnumSizer, + _FieldDescriptor.TYPE_SFIXED32: encoder.SFixed32Sizer, + _FieldDescriptor.TYPE_SFIXED64: encoder.SFixed64Sizer, + _FieldDescriptor.TYPE_SINT32: encoder.SInt32Sizer, + _FieldDescriptor.TYPE_SINT64: encoder.SInt64Sizer, +} + +# Maps from field type to a decoder constructor. +TYPE_TO_DECODER = { + _FieldDescriptor.TYPE_DOUBLE: decoder.DoubleDecoder, + _FieldDescriptor.TYPE_FLOAT: decoder.FloatDecoder, + _FieldDescriptor.TYPE_INT64: decoder.Int64Decoder, + _FieldDescriptor.TYPE_UINT64: decoder.UInt64Decoder, + _FieldDescriptor.TYPE_INT32: decoder.Int32Decoder, + _FieldDescriptor.TYPE_FIXED64: decoder.Fixed64Decoder, + _FieldDescriptor.TYPE_FIXED32: decoder.Fixed32Decoder, + _FieldDescriptor.TYPE_BOOL: decoder.BoolDecoder, + _FieldDescriptor.TYPE_STRING: decoder.StringDecoder, + _FieldDescriptor.TYPE_GROUP: decoder.GroupDecoder, + _FieldDescriptor.TYPE_MESSAGE: decoder.MessageDecoder, + _FieldDescriptor.TYPE_BYTES: decoder.BytesDecoder, + _FieldDescriptor.TYPE_UINT32: decoder.UInt32Decoder, + _FieldDescriptor.TYPE_ENUM: decoder.EnumDecoder, + _FieldDescriptor.TYPE_SFIXED32: decoder.SFixed32Decoder, + _FieldDescriptor.TYPE_SFIXED64: decoder.SFixed64Decoder, + _FieldDescriptor.TYPE_SINT32: decoder.SInt32Decoder, + _FieldDescriptor.TYPE_SINT64: decoder.SInt64Decoder, +} + +# Maps from field type to expected wiretype. +FIELD_TYPE_TO_WIRE_TYPE = { + _FieldDescriptor.TYPE_DOUBLE: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_FLOAT: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_INT64: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_UINT64: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_INT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_FIXED64: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_FIXED32: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_BOOL: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_STRING: wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_GROUP: wire_format.WIRETYPE_START_GROUP, + _FieldDescriptor.TYPE_MESSAGE: wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_BYTES: wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_UINT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_ENUM: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_SFIXED32: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_SFIXED64: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_SINT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_SINT64: wire_format.WIRETYPE_VARINT, +} diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/well_known_types.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/well_known_types.py new file mode 100755 index 0000000..76f274d --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/well_known_types.py @@ -0,0 +1,707 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Contains well known classes. + +This files defines well known classes which need extra maintenance including: + - Any + - Duration + - FieldMask + - Struct + - Timestamp +""" + +__author__ = 'jieluo@google.com (Jie Luo)' + +import calendar +import collections.abc +import datetime +from typing import Union +import warnings +from google.protobuf.internal import field_mask + +FieldMask = field_mask.FieldMask + +_TIMESTAMPFORMAT = '%Y-%m-%dT%H:%M:%S' +_NANOS_PER_SECOND = 1000000000 +_NANOS_PER_MILLISECOND = 1000000 +_NANOS_PER_MICROSECOND = 1000 +_MILLIS_PER_SECOND = 1000 +_MICROS_PER_SECOND = 1000000 +_SECONDS_PER_DAY = 24 * 3600 +_DURATION_SECONDS_MAX = 315576000000 +_TIMESTAMP_SECONDS_MIN = -62135596800 +_TIMESTAMP_SECONDS_MAX = 253402300799 + +_EPOCH_DATETIME_NAIVE = datetime.datetime(1970, 1, 1, tzinfo=None) +_EPOCH_DATETIME_AWARE = _EPOCH_DATETIME_NAIVE.replace( + tzinfo=datetime.timezone.utc +) + + +class Any(object): + """Class for Any Message type.""" + + __slots__ = () + + def Pack( + self, msg, type_url_prefix='type.googleapis.com/', deterministic=None + ): + """Packs the specified message into current Any message.""" + if len(type_url_prefix) < 1 or type_url_prefix[-1] != '/': + self.type_url = '%s/%s' % (type_url_prefix, msg.DESCRIPTOR.full_name) + else: + self.type_url = '%s%s' % (type_url_prefix, msg.DESCRIPTOR.full_name) + self.value = msg.SerializeToString(deterministic=deterministic) + + def Unpack(self, msg): + """Unpacks the current Any message into specified message.""" + descriptor = msg.DESCRIPTOR + if not self.Is(descriptor): + return False + msg.ParseFromString(self.value) + return True + + def TypeName(self): + """Returns the protobuf type name of the inner message.""" + # Only last part is to be used: b/25630112 + return self.type_url.rpartition('/')[2] + + def Is(self, descriptor): + """Checks if this Any represents the given protobuf type.""" + return '/' in self.type_url and self.TypeName() == descriptor.full_name + + +class Timestamp(object): + """Class for Timestamp message type.""" + + __slots__ = () + + def ToJsonString(self): + """Converts Timestamp to RFC 3339 date string format. + + Returns: + A string converted from timestamp. The string is always Z-normalized + and uses 3, 6 or 9 fractional digits as required to represent the + exact time. Example of the return format: '1972-01-01T10:00:20.021Z' + """ + _CheckTimestampValid(self.seconds, self.nanos) + nanos = self.nanos + seconds = self.seconds % _SECONDS_PER_DAY + days = (self.seconds - seconds) // _SECONDS_PER_DAY + dt = datetime.datetime(1970, 1, 1) + datetime.timedelta(days, seconds) + + result = dt.isoformat() + if (nanos % 1e9) == 0: + # If there are 0 fractional digits, the fractional + # point '.' should be omitted when serializing. + return result + 'Z' + if (nanos % 1e6) == 0: + # Serialize 3 fractional digits. + return result + '.%03dZ' % (nanos / 1e6) + if (nanos % 1e3) == 0: + # Serialize 6 fractional digits. + return result + '.%06dZ' % (nanos / 1e3) + # Serialize 9 fractional digits. + return result + '.%09dZ' % nanos + + def FromJsonString(self, value): + """Parse a RFC 3339 date string format to Timestamp. + + Args: + value: A date string. Any fractional digits (or none) and any offset are + accepted as long as they fit into nano-seconds precision. Example of + accepted format: '1972-01-01T10:00:20.021-05:00' + + Raises: + ValueError: On parsing problems. + """ + if not isinstance(value, str): + raise ValueError('Timestamp JSON value not a string: {!r}'.format(value)) + timezone_offset = value.find('Z') + if timezone_offset == -1: + timezone_offset = value.find('+') + if timezone_offset == -1: + timezone_offset = value.rfind('-') + if timezone_offset == -1: + raise ValueError( + 'Failed to parse timestamp: missing valid timezone offset.' + ) + time_value = value[0:timezone_offset] + # Parse datetime and nanos. + point_position = time_value.find('.') + if point_position == -1: + second_value = time_value + nano_value = '' + else: + second_value = time_value[:point_position] + nano_value = time_value[point_position + 1 :] + if 't' in second_value: + raise ValueError( + "time data '{0}' does not match format '%Y-%m-%dT%H:%M:%S', " + "lowercase 't' is not accepted".format(second_value) + ) + date_object = datetime.datetime.strptime(second_value, _TIMESTAMPFORMAT) + td = date_object - datetime.datetime(1970, 1, 1) + seconds = td.seconds + td.days * _SECONDS_PER_DAY + if len(nano_value) > 9: + raise ValueError( + 'Failed to parse Timestamp: nanos {0} more than ' + '9 fractional digits.'.format(nano_value) + ) + if nano_value: + nanos = round(float('0.' + nano_value) * 1e9) + else: + nanos = 0 + # Parse timezone offsets. + if value[timezone_offset] == 'Z': + if len(value) != timezone_offset + 1: + raise ValueError( + 'Failed to parse timestamp: invalid trailing data {0}.'.format( + value + ) + ) + else: + timezone = value[timezone_offset:] + pos = timezone.find(':') + if pos == -1: + raise ValueError('Invalid timezone offset value: {0}.'.format(timezone)) + if timezone[0] == '+': + seconds -= (int(timezone[1:pos]) * 60 + int(timezone[pos + 1 :])) * 60 + else: + seconds += (int(timezone[1:pos]) * 60 + int(timezone[pos + 1 :])) * 60 + # Set seconds and nanos + _CheckTimestampValid(seconds, nanos) + self.seconds = int(seconds) + self.nanos = int(nanos) + + def GetCurrentTime(self): + """Get the current UTC into Timestamp.""" + self.FromDatetime(datetime.datetime.now(tz=datetime.timezone.utc)) + + def ToNanoseconds(self): + """Converts Timestamp to nanoseconds since epoch.""" + _CheckTimestampValid(self.seconds, self.nanos) + return self.seconds * _NANOS_PER_SECOND + self.nanos + + def ToMicroseconds(self): + """Converts Timestamp to microseconds since epoch.""" + _CheckTimestampValid(self.seconds, self.nanos) + return ( + self.seconds * _MICROS_PER_SECOND + self.nanos // _NANOS_PER_MICROSECOND + ) + + def ToMilliseconds(self): + """Converts Timestamp to milliseconds since epoch.""" + _CheckTimestampValid(self.seconds, self.nanos) + return ( + self.seconds * _MILLIS_PER_SECOND + self.nanos // _NANOS_PER_MILLISECOND + ) + + def ToSeconds(self): + """Converts Timestamp to seconds since epoch.""" + _CheckTimestampValid(self.seconds, self.nanos) + return self.seconds + + def FromNanoseconds(self, nanos): + """Converts nanoseconds since epoch to Timestamp.""" + seconds = nanos // _NANOS_PER_SECOND + nanos = nanos % _NANOS_PER_SECOND + _CheckTimestampValid(seconds, nanos) + self.seconds = seconds + self.nanos = nanos + + def FromMicroseconds(self, micros): + """Converts microseconds since epoch to Timestamp.""" + seconds = micros // _MICROS_PER_SECOND + nanos = (micros % _MICROS_PER_SECOND) * _NANOS_PER_MICROSECOND + _CheckTimestampValid(seconds, nanos) + self.seconds = seconds + self.nanos = nanos + + def FromMilliseconds(self, millis): + """Converts milliseconds since epoch to Timestamp.""" + seconds = millis // _MILLIS_PER_SECOND + nanos = (millis % _MILLIS_PER_SECOND) * _NANOS_PER_MILLISECOND + _CheckTimestampValid(seconds, nanos) + self.seconds = seconds + self.nanos = nanos + + def FromSeconds(self, seconds): + """Converts seconds since epoch to Timestamp.""" + _CheckTimestampValid(seconds, 0) + self.seconds = seconds + self.nanos = 0 + + def ToDatetime(self, tzinfo=None): + """Converts Timestamp to a datetime. + + Args: + tzinfo: A datetime.tzinfo subclass; defaults to None. + + Returns: + If tzinfo is None, returns a timezone-naive UTC datetime (with no timezone + information, i.e. not aware that it's UTC). + + Otherwise, returns a timezone-aware datetime in the input timezone. + """ + # Using datetime.fromtimestamp for this would avoid constructing an extra + # timedelta object and possibly an extra datetime. Unfortunately, that has + # the disadvantage of not handling the full precision (on all platforms, see + # https://github.com/python/cpython/issues/109849) or full range (on some + # platforms, see https://github.com/python/cpython/issues/110042) of + # datetime. + _CheckTimestampValid(self.seconds, self.nanos) + delta = datetime.timedelta( + seconds=self.seconds, + microseconds=_RoundTowardZero(self.nanos, _NANOS_PER_MICROSECOND), + ) + if tzinfo is None: + return _EPOCH_DATETIME_NAIVE + delta + else: + # Note the tz conversion has to come after the timedelta arithmetic. + return (_EPOCH_DATETIME_AWARE + delta).astimezone(tzinfo) + + def FromDatetime(self, dt): + """Converts datetime to Timestamp. + + Args: + dt: A datetime. If it's timezone-naive, it's assumed to be in UTC. + """ + # Using this guide: http://wiki.python.org/moin/WorkingWithTime + # And this conversion guide: http://docs.python.org/library/time.html + + # Turn the date parameter into a tuple (struct_time) that can then be + # manipulated into a long value of seconds. During the conversion from + # struct_time to long, the source date in UTC, and so it follows that the + # correct transformation is calendar.timegm() + if type(dt).__name__ != 'datetime' and not isinstance( + dt, datetime.datetime + ): + raise TypeError( + 'Fail to convert to Timestamp. Expected a datetime object ' + 'got {0}'.format(type(dt).__name__) + ) + try: + seconds = calendar.timegm(dt.utctimetuple()) + nanos = dt.microsecond * _NANOS_PER_MICROSECOND + except AttributeError as e: + raise AttributeError( + 'Fail to convert to Timestamp. Expected a datetime like ' + 'object got {0} : {1}'.format(type(dt).__name__, e) + ) from e + _CheckTimestampValid(seconds, nanos) + self.seconds = seconds + self.nanos = nanos + + def _internal_assign(self, dt): + self.FromDatetime(dt) + + def __add__(self, value) -> datetime.datetime: + if isinstance(value, Duration): + return self.ToDatetime() + value.ToTimedelta() + return self.ToDatetime() + value + + __radd__ = __add__ + + def __sub__(self, value) -> Union[datetime.datetime, datetime.timedelta]: + if isinstance(value, Timestamp): + return self.ToDatetime() - value.ToDatetime() + elif isinstance(value, Duration): + return self.ToDatetime() - value.ToTimedelta() + return self.ToDatetime() - value + + def __rsub__(self, dt) -> datetime.timedelta: + return dt - self.ToDatetime() + + +def _CheckTimestampValid(seconds, nanos): + if seconds < _TIMESTAMP_SECONDS_MIN or seconds > _TIMESTAMP_SECONDS_MAX: + raise ValueError( + 'Timestamp is not valid: Seconds {0} must be in range ' + '[-62135596800, 253402300799].'.format(seconds)) + if nanos < 0 or nanos >= _NANOS_PER_SECOND: + raise ValueError( + 'Timestamp is not valid: Nanos {} must be in a range ' + '[0, 999999].'.format(nanos) + ) + + +class Duration(object): + """Class for Duration message type.""" + + __slots__ = () + + def ToJsonString(self): + """Converts Duration to string format. + + Returns: + A string converted from self. The string format will contains + 3, 6, or 9 fractional digits depending on the precision required to + represent the exact Duration value. For example: "1s", "1.010s", + "1.000000100s", "-3.100s" + """ + _CheckDurationValid(self.seconds, self.nanos) + if self.seconds < 0 or self.nanos < 0: + result = '-' + seconds = -self.seconds + int((0 - self.nanos) // 1e9) + nanos = (0 - self.nanos) % 1e9 + else: + result = '' + seconds = self.seconds + int(self.nanos // 1e9) + nanos = self.nanos % 1e9 + result += '%d' % seconds + if (nanos % 1e9) == 0: + # If there are 0 fractional digits, the fractional + # point '.' should be omitted when serializing. + return result + 's' + if (nanos % 1e6) == 0: + # Serialize 3 fractional digits. + return result + '.%03ds' % (nanos / 1e6) + if (nanos % 1e3) == 0: + # Serialize 6 fractional digits. + return result + '.%06ds' % (nanos / 1e3) + # Serialize 9 fractional digits. + return result + '.%09ds' % nanos + + def FromJsonString(self, value): + """Converts a string to Duration. + + Args: + value: A string to be converted. The string must end with 's'. Any + fractional digits (or none) are accepted as long as they fit into + precision. For example: "1s", "1.01s", "1.0000001s", "-3.100s + + Raises: + ValueError: On parsing problems. + """ + if not isinstance(value, str): + raise ValueError('Duration JSON value not a string: {!r}'.format(value)) + if len(value) < 1 or value[-1] != 's': + raise ValueError('Duration must end with letter "s": {0}.'.format(value)) + try: + pos = value.find('.') + if pos == -1: + seconds = int(value[:-1]) + nanos = 0 + else: + seconds = int(value[:pos]) + if value[0] == '-': + nanos = int(round(float('-0{0}'.format(value[pos:-1])) * 1e9)) + else: + nanos = int(round(float('0{0}'.format(value[pos:-1])) * 1e9)) + _CheckDurationValid(seconds, nanos) + self.seconds = seconds + self.nanos = nanos + except ValueError as e: + raise ValueError("Couldn't parse duration: {0} : {1}.".format(value, e)) + + def ToNanoseconds(self): + """Converts a Duration to nanoseconds.""" + return self.seconds * _NANOS_PER_SECOND + self.nanos + + def ToMicroseconds(self): + """Converts a Duration to microseconds.""" + micros = _RoundTowardZero(self.nanos, _NANOS_PER_MICROSECOND) + return self.seconds * _MICROS_PER_SECOND + micros + + def ToMilliseconds(self): + """Converts a Duration to milliseconds.""" + millis = _RoundTowardZero(self.nanos, _NANOS_PER_MILLISECOND) + return self.seconds * _MILLIS_PER_SECOND + millis + + def ToSeconds(self): + """Converts a Duration to seconds.""" + return self.seconds + + def FromNanoseconds(self, nanos): + """Converts nanoseconds to Duration.""" + self._NormalizeDuration( + nanos // _NANOS_PER_SECOND, nanos % _NANOS_PER_SECOND + ) + + def FromMicroseconds(self, micros): + """Converts microseconds to Duration.""" + self._NormalizeDuration( + micros // _MICROS_PER_SECOND, + (micros % _MICROS_PER_SECOND) * _NANOS_PER_MICROSECOND, + ) + + def FromMilliseconds(self, millis): + """Converts milliseconds to Duration.""" + self._NormalizeDuration( + millis // _MILLIS_PER_SECOND, + (millis % _MILLIS_PER_SECOND) * _NANOS_PER_MILLISECOND, + ) + + def FromSeconds(self, seconds): + """Converts seconds to Duration.""" + self.seconds = seconds + self.nanos = 0 + + def ToTimedelta(self) -> datetime.timedelta: + """Converts Duration to timedelta.""" + return datetime.timedelta( + seconds=self.seconds, + microseconds=_RoundTowardZero(self.nanos, _NANOS_PER_MICROSECOND), + ) + + def FromTimedelta(self, td): + """Converts timedelta to Duration.""" + if type(td).__name__ != 'timedelta' and not isinstance( + td, datetime.timedelta + ): + raise TypeError( + 'Fail to convert to Duration. Expected a timedelta object ' + 'got {0}'.format(type(td).__name__) + ) + try: + self._NormalizeDuration( + td.seconds + td.days * _SECONDS_PER_DAY, + td.microseconds * _NANOS_PER_MICROSECOND, + ) + except AttributeError as e: + raise AttributeError( + 'Fail to convert to Duration. Expected a timedelta like ' + 'object got {0}: {1}'.format(type(td).__name__, e) + ) from e + + def _internal_assign(self, td): + self.FromTimedelta(td) + + def _NormalizeDuration(self, seconds, nanos): + """Set Duration by seconds and nanos.""" + # Force nanos to be negative if the duration is negative. + if seconds < 0 and nanos > 0: + seconds += 1 + nanos -= _NANOS_PER_SECOND + self.seconds = seconds + self.nanos = nanos + + def __add__(self, value) -> Union[datetime.datetime, datetime.timedelta]: + if isinstance(value, Timestamp): + return self.ToTimedelta() + value.ToDatetime() + return self.ToTimedelta() + value + + __radd__ = __add__ + + def __sub__(self, value) -> datetime.timedelta: + return self.ToTimedelta() - value + + def __rsub__(self, value) -> Union[datetime.datetime, datetime.timedelta]: + return value - self.ToTimedelta() + + +def _CheckDurationValid(seconds, nanos): + if seconds < -_DURATION_SECONDS_MAX or seconds > _DURATION_SECONDS_MAX: + raise ValueError( + 'Duration is not valid: Seconds {0} must be in range ' + '[-315576000000, 315576000000].'.format(seconds) + ) + if nanos <= -_NANOS_PER_SECOND or nanos >= _NANOS_PER_SECOND: + raise ValueError( + 'Duration is not valid: Nanos {0} must be in range ' + '[-999999999, 999999999].'.format(nanos) + ) + if (nanos < 0 and seconds > 0) or (nanos > 0 and seconds < 0): + raise ValueError('Duration is not valid: Sign mismatch.') + + +def _RoundTowardZero(value, divider): + """Truncates the remainder part after division.""" + # For some languages, the sign of the remainder is implementation + # dependent if any of the operands is negative. Here we enforce + # "rounded toward zero" semantics. For example, for (-5) / 2 an + # implementation may give -3 as the result with the remainder being + # 1. This function ensures we always return -2 (closer to zero). + result = value // divider + remainder = value % divider + if result < 0 and remainder > 0: + return result + 1 + else: + return result + + +def _SetStructValue(struct_value, value): + if value is None: + struct_value.null_value = 0 + elif isinstance(value, bool): + # Note: this check must come before the number check because in Python + # True and False are also considered numbers. + struct_value.bool_value = value + elif isinstance(value, str): + struct_value.string_value = value + elif isinstance(value, (int, float)): + struct_value.number_value = value + elif isinstance(value, (dict, Struct)): + struct_value.struct_value.Clear() + struct_value.struct_value.update(value) + elif isinstance(value, (list, tuple, ListValue)): + struct_value.list_value.Clear() + struct_value.list_value.extend(value) + else: + raise ValueError('Unexpected type') + + +def _GetStructValue(struct_value): + which = struct_value.WhichOneof('kind') + if which == 'struct_value': + return struct_value.struct_value + elif which == 'null_value': + return None + elif which == 'number_value': + return struct_value.number_value + elif which == 'string_value': + return struct_value.string_value + elif which == 'bool_value': + return struct_value.bool_value + elif which == 'list_value': + return struct_value.list_value + elif which is None: + raise ValueError('Value not set') + + +class Struct(object): + """Class for Struct message type.""" + + __slots__ = () + + def __getitem__(self, key): + return _GetStructValue(self.fields[key]) + + def __setitem__(self, key, value): + _SetStructValue(self.fields[key], value) + + def __delitem__(self, key): + del self.fields[key] + + def __len__(self): + return len(self.fields) + + def __iter__(self): + return iter(self.fields) + + def _internal_assign(self, dictionary): + self.Clear() + self.update(dictionary) + + def _internal_compare(self, other): + size = len(self) + if size != len(other): + return False + for key, value in self.items(): + if key not in other: + return False + if isinstance(other[key], (dict, list)): + if not value._internal_compare(other[key]): + return False + elif value != other[key]: + return False + return True + + def keys(self): # pylint: disable=invalid-name + return self.fields.keys() + + def values(self): # pylint: disable=invalid-name + return [self[key] for key in self] + + def items(self): # pylint: disable=invalid-name + return [(key, self[key]) for key in self] + + def get_or_create_list(self, key): + """Returns a list for this key, creating if it didn't exist already.""" + if not self.fields[key].HasField('list_value'): + # Clear will mark list_value modified which will indeed create a list. + self.fields[key].list_value.Clear() + return self.fields[key].list_value + + def get_or_create_struct(self, key): + """Returns a struct for this key, creating if it didn't exist already.""" + if not self.fields[key].HasField('struct_value'): + # Clear will mark struct_value modified which will indeed create a struct. + self.fields[key].struct_value.Clear() + return self.fields[key].struct_value + + def update(self, dictionary): # pylint: disable=invalid-name + for key, value in dictionary.items(): + _SetStructValue(self.fields[key], value) + + +collections.abc.MutableMapping.register(Struct) + + +class ListValue(object): + """Class for ListValue message type.""" + + __slots__ = () + + def __len__(self): + return len(self.values) + + def append(self, value): + _SetStructValue(self.values.add(), value) + + def extend(self, elem_seq): + for value in elem_seq: + self.append(value) + + def __getitem__(self, index): + """Retrieves item by the specified index.""" + return _GetStructValue(self.values.__getitem__(index)) + + def __setitem__(self, index, value): + _SetStructValue(self.values.__getitem__(index), value) + + def __delitem__(self, key): + del self.values[key] + + def _internal_assign(self, elem_seq): + self.Clear() + self.extend(elem_seq) + + def _internal_compare(self, other): + size = len(self) + if size != len(other): + return False + for i in range(size): + if isinstance(other[i], (dict, list)): + if not self[i]._internal_compare(other[i]): + return False + elif self[i] != other[i]: + return False + return True + + def items(self): + for i in range(len(self)): + yield self[i] + + def add_struct(self): + """Appends and returns a struct value as the next value in the list.""" + struct_value = self.values.add().struct_value + # Clear will mark struct_value modified which will indeed create a struct. + struct_value.Clear() + return struct_value + + def add_list(self): + """Appends and returns a list value as the next value in the list.""" + list_value = self.values.add().list_value + # Clear will mark list_value modified which will indeed create a list. + list_value.Clear() + return list_value + + +collections.abc.MutableSequence.register(ListValue) + +# LINT.IfChange(wktbases) +WKTBASES = { + 'google.protobuf.Any': Any, + 'google.protobuf.Duration': Duration, + 'google.protobuf.FieldMask': FieldMask, + 'google.protobuf.ListValue': ListValue, + 'google.protobuf.Struct': Struct, + 'google.protobuf.Timestamp': Timestamp, +} +# LINT.ThenChange(//depot/google.protobuf/compiler/python/pyi_generator.cc:wktbases) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/internal/wire_format.py b/venv/lib/python3.12/site-packages/google/protobuf/internal/wire_format.py new file mode 100755 index 0000000..fb7a4cd --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/internal/wire_format.py @@ -0,0 +1,255 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Constants and static functions to support protocol buffer wire format.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +from google.protobuf import descriptor +from google.protobuf import message + +TAG_TYPE_BITS = 3 # Number of bits used to hold type info in a proto tag. +TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1 # 0x7 + +# These numbers identify the wire type of a protocol buffer value. +# We use the least-significant TAG_TYPE_BITS bits of the varint-encoded +# tag-and-type to store one of these WIRETYPE_* constants. +# These values must match WireType enum in //google/protobuf/wire_format.h. +WIRETYPE_VARINT = 0 +WIRETYPE_FIXED64 = 1 +WIRETYPE_LENGTH_DELIMITED = 2 +WIRETYPE_START_GROUP = 3 +WIRETYPE_END_GROUP = 4 +WIRETYPE_FIXED32 = 5 +_WIRETYPE_MAX = 5 + +# Bounds for various integer types. +INT32_MAX = int((1 << 31) - 1) +INT32_MIN = int(-(1 << 31)) +UINT32_MAX = (1 << 32) - 1 + +INT64_MAX = (1 << 63) - 1 +INT64_MIN = -(1 << 63) +UINT64_MAX = (1 << 64) - 1 + +# "struct" format strings that will encode/decode the specified formats. +FORMAT_UINT32_LITTLE_ENDIAN = '> TAG_TYPE_BITS), (tag & TAG_TYPE_MASK) + + +def ZigZagEncode(value): + """ZigZag Transform: Encodes signed integers so that they can be + + effectively used with varint encoding. See wire_format.h for + more details. + """ + if value >= 0: + return value << 1 + return (value << 1) ^ (~0) + + +def ZigZagDecode(value): + """Inverse of ZigZagEncode().""" + if not value & 0x1: + return value >> 1 + return (value >> 1) ^ (~0) + + +# The *ByteSize() functions below return the number of bytes required to +# serialize "field number + type" information and then serialize the value. + + +def Int32ByteSize(field_number, int32): + return Int64ByteSize(field_number, int32) + + +def Int32ByteSizeNoTag(int32): + return _VarUInt64ByteSizeNoTag(0xFFFFFFFFFFFFFFFF & int32) + + +def Int64ByteSize(field_number, int64): + # Have to convert to uint before calling UInt64ByteSize(). + return UInt64ByteSize(field_number, 0xFFFFFFFFFFFFFFFF & int64) + + +def UInt32ByteSize(field_number, uint32): + return UInt64ByteSize(field_number, uint32) + + +def UInt64ByteSize(field_number, uint64): + return TagByteSize(field_number) + _VarUInt64ByteSizeNoTag(uint64) + + +def SInt32ByteSize(field_number, int32): + return UInt32ByteSize(field_number, ZigZagEncode(int32)) + + +def SInt64ByteSize(field_number, int64): + return UInt64ByteSize(field_number, ZigZagEncode(int64)) + + +def Fixed32ByteSize(field_number, fixed32): + return TagByteSize(field_number) + 4 + + +def Fixed64ByteSize(field_number, fixed64): + return TagByteSize(field_number) + 8 + + +def SFixed32ByteSize(field_number, sfixed32): + return TagByteSize(field_number) + 4 + + +def SFixed64ByteSize(field_number, sfixed64): + return TagByteSize(field_number) + 8 + + +def FloatByteSize(field_number, flt): + return TagByteSize(field_number) + 4 + + +def DoubleByteSize(field_number, double): + return TagByteSize(field_number) + 8 + + +def BoolByteSize(field_number, b): + return TagByteSize(field_number) + 1 + + +def EnumByteSize(field_number, enum): + return UInt32ByteSize(field_number, enum) + + +def StringByteSize(field_number, string): + return BytesByteSize(field_number, string.encode('utf-8')) + + +def BytesByteSize(field_number, b): + return TagByteSize(field_number) + _VarUInt64ByteSizeNoTag(len(b)) + len(b) + + +def GroupByteSize(field_number, message): + return ( + 2 * TagByteSize(field_number) + message.ByteSize() # START and END group. + ) + + +def MessageByteSize(field_number, message): + return ( + TagByteSize(field_number) + + _VarUInt64ByteSizeNoTag(message.ByteSize()) + + message.ByteSize() + ) + + +def MessageSetItemByteSize(field_number, msg): + # First compute the sizes of the tags. + # There are 2 tags for the beginning and ending of the repeated group, that + # is field number 1, one with field number 2 (type_id) and one with field + # number 3 (message). + total_size = 2 * TagByteSize(1) + TagByteSize(2) + TagByteSize(3) + + # Add the number of bytes for type_id. + total_size += _VarUInt64ByteSizeNoTag(field_number) + + message_size = msg.ByteSize() + + # The number of bytes for encoding the length of the message. + total_size += _VarUInt64ByteSizeNoTag(message_size) + + # The size of the message. + total_size += message_size + return total_size + + +def TagByteSize(field_number): + """Returns the bytes required to serialize a tag with this field number.""" + # Just pass in type 0, since the type won't affect the tag+type size. + return _VarUInt64ByteSizeNoTag(PackTag(field_number, 0)) + + +# Private helper function for the *ByteSize() functions above. + + +def _VarUInt64ByteSizeNoTag(uint64): + """Returns the number of bytes required to serialize a single varint + + using boundary value comparisons. (unrolled loop optimization -WPierce) + uint64 must be unsigned. + """ + if uint64 <= 0x7F: + return 1 + if uint64 <= 0x3FFF: + return 2 + if uint64 <= 0x1FFFFF: + return 3 + if uint64 <= 0xFFFFFFF: + return 4 + if uint64 <= 0x7FFFFFFFF: + return 5 + if uint64 <= 0x3FFFFFFFFFF: + return 6 + if uint64 <= 0x1FFFFFFFFFFFF: + return 7 + if uint64 <= 0xFFFFFFFFFFFFFF: + return 8 + if uint64 <= 0x7FFFFFFFFFFFFFFF: + return 9 + if uint64 > UINT64_MAX: + raise message.EncodeError('Value out of range: %d' % uint64) + return 10 + + +NON_PACKABLE_TYPES = ( + descriptor.FieldDescriptor.TYPE_STRING, + descriptor.FieldDescriptor.TYPE_GROUP, + descriptor.FieldDescriptor.TYPE_MESSAGE, + descriptor.FieldDescriptor.TYPE_BYTES, +) + + +def IsTypePackable(field_type): + """Return true iff packable = true is valid for fields of this type. + + Args: + field_type: a FieldDescriptor::Type value. + + Returns: + True iff fields of this type are packable. + """ + return field_type not in NON_PACKABLE_TYPES diff --git a/venv/lib/python3.12/site-packages/google/protobuf/json_enumvalue_options_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/json_enumvalue_options_pb2.py new file mode 100755 index 0000000..27e71cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/json_enumvalue_options_pb2.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/json_enumvalue_options.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/json_enumvalue_options.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,google/protobuf/json_enumvalue_options.proto\x12\x0cpb.enumvalue\x1a google/protobuf/descriptor.proto\"0\n\x14JsonEnumValueOptions\x12\x16\n\x06string\x18\x01 \x01(\tR\x06stringX\x01:b\n\x04json\x12!.google.protobuf.EnumValueOptions\x18\xe6\x07 \x01(\x0b\x32\".pb.enumvalue.JsonEnumValueOptionsB\x06\xb2\x01\x03\x08\xea\x07R\x04jsonB\x1a\n\x18\x63om.google.protobuf.utilb\x08\x65\x64itionsp\xe9\x07') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.json_enumvalue_options_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\030com.google.protobuf.util' + _globals['json']._loaded_options = None + _globals['json']._serialized_options = b'\262\001\003\010\352\007' + _globals['_JSONENUMVALUEOPTIONS']._serialized_start=96 + _globals['_JSONENUMVALUEOPTIONS']._serialized_end=144 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/json_format.py b/venv/lib/python3.12/site-packages/google/protobuf/json_format.py new file mode 100755 index 0000000..90a0b3e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/json_format.py @@ -0,0 +1,1247 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Contains routines for printing protocol messages in JSON format. + +Simple usage example: + + # Create a proto object and serialize it to a json format string. + message = my_proto_pb2.MyMessage(foo='bar') + json_string = json_format.MessageToJson(message) + + # Parse a json format string to proto object. + message = json_format.Parse(json_string, my_proto_pb2.MyMessage()) +""" + +__author__ = 'jieluo@google.com (Jie Luo)' + +import base64 +from collections import OrderedDict +import json +import math +from operator import methodcaller +import re + +from google.protobuf import descriptor +from google.protobuf import descriptor_pool +from google.protobuf import message_factory +from google.protobuf import symbol_database +from google.protobuf.internal import type_checkers + +_INT_TYPES = frozenset([ + descriptor.FieldDescriptor.CPPTYPE_INT32, + descriptor.FieldDescriptor.CPPTYPE_UINT32, + descriptor.FieldDescriptor.CPPTYPE_INT64, + descriptor.FieldDescriptor.CPPTYPE_UINT64, +]) +_INT64_TYPES = frozenset([ + descriptor.FieldDescriptor.CPPTYPE_INT64, + descriptor.FieldDescriptor.CPPTYPE_UINT64, +]) +_FLOAT_TYPES = frozenset([ + descriptor.FieldDescriptor.CPPTYPE_FLOAT, + descriptor.FieldDescriptor.CPPTYPE_DOUBLE, +]) +_INFINITY = 'Infinity' +_NEG_INFINITY = '-Infinity' +_NAN = 'NaN' + +_UNPAIRED_SURROGATE_PATTERN = re.compile( + '[\ud800-\udbff](?![\udc00-\udfff])|(? self.max_recursion_depth: + raise ParseError( + 'Message too deep. Max recursion depth is {0}'.format( + self.max_recursion_depth + ) + ) + message_descriptor = message.DESCRIPTOR + full_name = message_descriptor.full_name + if not path: + path = message_descriptor.name + if _IsWrapperMessage(message_descriptor): + self._ConvertWrapperMessage(value, message, path) + elif full_name in _WKTJSONMETHODS: + methodcaller(_WKTJSONMETHODS[full_name][1], value, message, path)(self) + else: + self._ConvertFieldValuePair(value, message, path) + self.recursion_depth -= 1 + + def _ConvertFieldValuePair(self, js, message, path): + """Convert field value pairs into regular message. + + Args: + js: A JSON object to convert the field value pairs. + message: A regular protocol message to record the data. + path: parent path to log parse error info. + + Raises: + ParseError: In case of problems converting. + """ + names = [] + message_descriptor = message.DESCRIPTOR + fields_by_json_name = dict( + (f.json_name, f) for f in message_descriptor.fields + ) + + def _ClearFieldOrExtension(message, field): + if field.is_extension: + message.ClearExtension(field) + else: + message.ClearField(field.name) + + def _GetFieldOrExtension(message, field): + if field.is_extension: + return message.Extensions[field] + else: + return getattr(message, field.name) + + def _SetFieldOrExtension(message, field, value): + if field.is_extension: + message.Extensions[field] = value + else: + setattr(message, field.name, value) + + for name in js: + try: + field = fields_by_json_name.get(name, None) + if not field: + field = message_descriptor.fields_by_name.get(name, None) + if not field and _VALID_EXTENSION_NAME.match(name): + if not message_descriptor.is_extendable: + raise ParseError( + 'Message type {0} does not have extensions at {1}'.format( + message_descriptor.full_name, path + ) + ) + identifier = name[1:-1] # strip [] brackets + # pylint: disable=protected-access + field = message.Extensions._FindExtensionByName(identifier) + # pylint: enable=protected-access + if not field: + # Try looking for extension by the message type name, dropping the + # field name following the final . separator in full_name. + identifier = '.'.join(identifier.split('.')[:-1]) + # pylint: disable=protected-access + field = message.Extensions._FindExtensionByName(identifier) + # pylint: enable=protected-access + if not field: + if self.ignore_unknown_fields: + continue + raise ParseError( + ( + 'Message type "{0}" has no field named "{1}" at "{2}".\n' + ' Available Fields(except extensions): "{3}"' + ).format( + message_descriptor.full_name, + name, + path, + [f.json_name for f in message_descriptor.fields], + ) + ) + if name in names: + raise ParseError( + 'Message type "{0}" should not have multiple ' + '"{1}" fields at "{2}".'.format( + message.DESCRIPTOR.full_name, name, path + ) + ) + names.append(name) + value = js[name] + # Check no other oneof field is parsed. + if field.containing_oneof is not None and value is not None: + oneof_name = field.containing_oneof.name + if oneof_name in names: + raise ParseError( + 'Message type "{0}" should not have multiple ' + '"{1}" oneof fields at "{2}".'.format( + message.DESCRIPTOR.full_name, oneof_name, path + ) + ) + names.append(oneof_name) + + if value is None: + if ( + field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE + and field.message_type.full_name == 'google.protobuf.Value' + ): + sub_message = _GetFieldOrExtension(message, field) + sub_message.null_value = 0 + elif ( + field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM + and field.enum_type.full_name == 'google.protobuf.NullValue' + ): + _SetFieldOrExtension(message, field, 0) + else: + _ClearFieldOrExtension(message, field) + continue + + # Parse field value. + if _IsMapEntry(field): + _ClearFieldOrExtension(message, field) + self._ConvertMapFieldValue( + value, message, field, '{0}.{1}'.format(path, name) + ) + elif field.is_repeated: + _ClearFieldOrExtension(message, field) + if not isinstance(value, _LIST_LIKE): + raise ParseError( + 'repeated field {0} must be in [] which is {1} at {2}'.format( + name, value, path + ) + ) + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + # Repeated message field. + for index, item in enumerate(value): + sub_message = _GetFieldOrExtension(message, field).add() + # None is a null_value in Value. + if ( + item is None + and sub_message.DESCRIPTOR.full_name + != 'google.protobuf.Value' + ): + raise ParseError( + 'null is not allowed to be used as an element' + ' in a repeated field at {0}.{1}[{2}]'.format( + path, name, index + ) + ) + self.ConvertMessage( + item, sub_message, '{0}.{1}[{2}]'.format(path, name, index) + ) + else: + # Repeated scalar field. + for index, item in enumerate(value): + if item is None: + raise ParseError( + 'null is not allowed to be used as an element' + ' in a repeated field at {0}.{1}[{2}]'.format( + path, name, index + ) + ) + self._ConvertAndAppendScalar( + message, field, item, '{0}.{1}[{2}]'.format(path, name, index) + ) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + sub_message = _GetFieldOrExtension(message, field) + sub_message.SetInParent() + self.ConvertMessage(value, sub_message, '{0}.{1}'.format(path, name)) + else: + self._ConvertAndSetScalar( + message, field, value, '{0}.{1}'.format(path, name) + ) + except ParseError as e: + if field and field.containing_oneof is None: + raise ParseError( + 'Failed to parse {0} field: {1}.'.format(name, e) + ) from e + else: + raise ParseError(str(e)) from e + except ValueError as e: + raise ParseError( + 'Failed to parse {0} field: {1}.'.format(name, e) + ) from e + except TypeError as e: + raise ParseError( + 'Failed to parse {0} field: {1}.'.format(name, e) + ) from e + + def _ConvertAnyMessage(self, value, message, path): + """Convert a JSON representation into Any message.""" + if isinstance(value, dict) and not value: + return + try: + type_url = value['@type'] + except KeyError as e: + raise ParseError( + '@type is missing when parsing any message at {0}'.format(path) + ) from e + + try: + sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) + except TypeError as e: + raise ParseError('{0} at {1}'.format(e, path)) from e + message_descriptor = sub_message.DESCRIPTOR + full_name = message_descriptor.full_name + if _IsWrapperMessage(message_descriptor): + self._ConvertWrapperMessage( + value['value'], sub_message, '{0}.value'.format(path) + ) + elif full_name in _WKTJSONMETHODS: + # For well-known types (including nested Any), use ConvertMessage + # to ensure recursion depth is properly tracked + self.ConvertMessage(value['value'], sub_message, '{0}.value'.format(path)) + else: + del value['@type'] + try: + self._ConvertFieldValuePair(value, sub_message, path) + finally: + value['@type'] = type_url + # Sets Any message + message.value = sub_message.SerializeToString() + message.type_url = type_url + + def _ConvertGenericMessage(self, value, message, path): + """Convert a JSON representation into message with FromJsonString.""" + # Duration, Timestamp, FieldMask have a FromJsonString method to do the + # conversion. Users can also call the method directly. + try: + message.FromJsonString(value) + except ValueError as e: + raise ParseError('{0} at {1}'.format(e, path)) from e + + def _ConvertValueMessage(self, value, message, path): + """Convert a JSON representation into Value message.""" + if isinstance(value, dict): + self.ConvertMessage(value, message.struct_value, path) + elif isinstance(value, _LIST_LIKE): + self.ConvertMessage(value, message.list_value, path) + elif value is None: + message.null_value = 0 + elif isinstance(value, bool): + message.bool_value = value + elif isinstance(value, str): + message.string_value = value + elif isinstance(value, _INT_OR_FLOAT): + message.number_value = value + else: + raise ParseError( + 'Value {0} has unexpected type {1} at {2}'.format( + value, type(value), path + ) + ) + + def _ConvertListOrTupleValueMessage(self, value, message, path): + """Convert a JSON representation into ListValue message.""" + if not isinstance(value, _LIST_LIKE): + raise ParseError( + 'ListValue must be in [] which is {0} at {1}'.format(value, path) + ) + message.ClearField('values') + for index, item in enumerate(value): + self.ConvertMessage( + item, message.values.add(), '{0}[{1}]'.format(path, index) + ) + + def _ConvertStructMessage(self, value, message, path): + """Convert a JSON representation into Struct message.""" + if not isinstance(value, dict): + raise ParseError( + 'Struct must be in a dict which is {0} at {1}'.format(value, path) + ) + # Clear will mark the struct as modified so it will be created even if + # there are no values. + message.Clear() + for key in value: + self.ConvertMessage( + value[key], message.fields[key], '{0}.{1}'.format(path, key) + ) + return + + def _ConvertWrapperMessage(self, value, message, path): + """Convert a JSON representation into Wrapper message.""" + field = message.DESCRIPTOR.fields_by_name['value'] + self._ConvertAndSetScalar( + message, field, value, path='{0}.value'.format(path) + ) + + def _ConvertMapFieldValue(self, value, message, field, path): + """Convert map field value for a message map field. + + Args: + value: A JSON object to convert the map field value. + message: A protocol message to record the converted data. + field: The descriptor of the map field to be converted. + path: parent path to log parse error info. + + Raises: + ParseError: In case of convert problems. + """ + if not isinstance(value, dict): + raise ParseError( + 'Map field {0} must be in a dict which is {1} at {2}'.format( + field.name, value, path + ) + ) + key_field = field.message_type.fields_by_name['key'] + value_field = field.message_type.fields_by_name['value'] + for key in value: + key_value = _ConvertScalarFieldValue( + key, + key_field, + '{0}.key'.format(path), + self._custom_enum_names_cache, + self._GetEnumValueJsonExtension(), + require_str=True, + ) + if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + self.ConvertMessage( + value[key], + getattr(message, field.name)[key_value], + '{0}[{1}]'.format(path, key_value), + ) + else: + self._ConvertAndSetScalarToMapKey( + message, + field, + key_value, + value[key], + path='{0}[{1}]'.format(path, key_value), + ) + + def _ConvertAndSetScalar(self, message, field, js_value, path): + """Convert scalar from js_value and assign it to message.field.""" + try: + value = _ConvertScalarFieldValue( + js_value, + field, + path, + self._custom_enum_names_cache, + self._GetEnumValueJsonExtension(), + ) + if field.is_extension: + message.Extensions[field] = value + else: + setattr(message, field.name, value) + except EnumStringValueParseError: + if not self.ignore_unknown_fields: + raise + + def _ConvertAndAppendScalar(self, message, repeated_field, js_value, path): + """Convert scalar from js_value and append it to message.repeated_field.""" + try: + if repeated_field.is_extension: + repeated = message.Extensions[repeated_field] + else: + repeated = getattr(message, repeated_field.name) + value = _ConvertScalarFieldValue( + js_value, + repeated_field, + path, + self._custom_enum_names_cache, + self._GetEnumValueJsonExtension(), + ) + repeated.append(value) + except EnumStringValueParseError: + if not self.ignore_unknown_fields: + raise + + def _ConvertAndSetScalarToMapKey( + self, message, map_field, converted_key, js_value, path + ): + """Convert scalar from 'js_value' and add it to message.map_field[converted_key].""" + try: + getattr(message, map_field.name)[converted_key] = ( + _ConvertScalarFieldValue( + js_value, + map_field.message_type.fields_by_name['value'], + path, + self._custom_enum_names_cache, + self._GetEnumValueJsonExtension(), + ) + ) + except EnumStringValueParseError: + if not self.ignore_unknown_fields: + raise + + +def _GetJsonEnumValueOption(ev, extension_descriptor): + """Helper to get the JsonEnumValueOptions for an enum value. + + Args: + ev: The EnumValueDescriptor. + extension_descriptor: The extension descriptor for 'pb.enumvalue.json'. + + Returns: + The JsonEnumValueOptions message if the extension is present, + otherwise None. + """ + if ev.GetOptions().HasExtension(extension_descriptor): + return ev.GetOptions().Extensions[extension_descriptor] + return None + + +def _GetCustomJsonEnumNames( + enum_type, custom_enum_names_cache, enumvalue_json_extension=None +): + """Helper to get a mapping from custom JSON name to EnumValueDescriptor. + + Args: + enum_type: The EnumDescriptor. + custom_enum_names_cache: A dict to store/lookup the cached map. + enumvalue_json_extension: The extension descriptor for 'pb.enumvalue.json', + or None to look it up in the default descriptor pool. + + Returns: + A dict mapping custom JSON name strings to EnumValueDescriptors. + """ + if enum_type in custom_enum_names_cache: + return custom_enum_names_cache[enum_type] + + custom_names = {} + if enumvalue_json_extension is None: + # Options are always put on the default pool, so we only search the default pool. + try: + enumvalue_json_extension = descriptor_pool.Default().FindExtensionByName( + 'pb.enumvalue.json' + ) + except KeyError: + enumvalue_json_extension = None + + if enumvalue_json_extension is not None: + for ev in enum_type.values: + options = ev.GetOptions() + if options.HasExtension(enumvalue_json_extension): + option = options.Extensions[enumvalue_json_extension] + if option.HasField('string'): + custom_names[option.string] = ev + + custom_enum_names_cache[enum_type] = custom_names + return custom_names + + +def _ConvertScalarFieldValue( + value, + field, + path, + custom_enum_names_cache, + enumvalue_json_extension=None, + require_str=False, +): + """Convert a single scalar field value. + + Args: + value: A scalar value to convert the scalar field value. + field: The descriptor of the field to convert. + path: parent path to log parse error info. + custom_enum_names_cache: A dict to store/lookup custom enum names. + enumvalue_json_extension: The extension descriptor for 'pb.enumvalue.json', + or None if not loaded. + require_str: If True, the field value must be a str. + + Returns: + The converted scalar field value + + Raises: + ParseError: In case of convert problems. + EnumStringValueParseError: In case of unknown enum string value. + """ + try: + if field.cpp_type in _INT_TYPES: + return _ConvertInteger(value) + elif field.cpp_type in _FLOAT_TYPES: + return _ConvertFloat(value, field) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: + return _ConvertBool(value, require_str) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: + if field.type == descriptor.FieldDescriptor.TYPE_BYTES: + if isinstance(value, str): + encoded = value.encode('utf-8') + else: + encoded = value + # Add extra padding '=' + padded_value = encoded + b'=' * (4 - len(encoded) % 4) + return base64.urlsafe_b64decode(padded_value) + else: + # Checking for unpaired surrogates appears to be unreliable, + # depending on the specific Python version, so we check manually. + if _UNPAIRED_SURROGATE_PATTERN.search(value): + raise ParseError('Unpaired surrogate') + return value + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: + # Convert an enum value. + enum_value = field.enum_type.values_by_name.get(value, None) + # First check to see if we have a custom enum string. + if ( + enum_value is None + and isinstance(value, str) + and enumvalue_json_extension is not None + ): + custom_names = _GetCustomJsonEnumNames( + field.enum_type, + custom_enum_names_cache, + enumvalue_json_extension, + ) + enum_value = custom_names.get(value, None) + # If not, try parsing it as an integer. + if enum_value is None: + try: + number = int(value) + enum_value = field.enum_type.values_by_number.get(number, None) + except ValueError as e: + # Since parsing to integer failed and lookup in values_by_name didn't + # find this name, we have an enum string value which is unknown. + raise EnumStringValueParseError( + 'Invalid enum value {0} for enum type {1}'.format( + value, field.enum_type.full_name + ) + ) from e + if enum_value is None: + if field.enum_type.is_closed: + raise ParseError( + 'Invalid enum value {0} for enum type {1}'.format( + value, field.enum_type.full_name + ) + ) + else: + return number + return enum_value.number + except EnumStringValueParseError as e: + raise EnumStringValueParseError('{0} at {1}'.format(e, path)) from e + except ParseError as e: + raise ParseError('{0} at {1}'.format(e, path)) from e + + +def _ConvertInteger(value): + """Convert an integer. + + Args: + value: A scalar value to convert. + + Returns: + The integer value. + + Raises: + ParseError: If an integer couldn't be consumed. + """ + if isinstance(value, float) and not value.is_integer(): + raise ParseError("Couldn't parse integer: {0}".format(value)) + + if isinstance(value, str) and value.find(' ') != -1: + raise ParseError('Couldn\'t parse integer: "{0}"'.format(value)) + + if isinstance(value, bool): + raise ParseError( + 'Bool value {0} is not acceptable for integer field'.format(value) + ) + + try: + return int(value) + except ValueError as e: + # Attempt to parse as an integer-valued float. + try: + f = float(value) + except ValueError: + # Raise the original exception for the int parse. + raise e # pylint: disable=raise-missing-from + if not f.is_integer(): + raise ParseError( + 'Couldn\'t parse non-integer string: "{0}"'.format(value) + ) from e + return int(f) + + +def _ConvertFloat(value, field): + """Convert an floating point number.""" + if isinstance(value, float): + if math.isnan(value): + raise ParseError('Couldn\'t parse NaN, use quoted "NaN" instead') + if math.isinf(value): + if value > 0: + raise ParseError( + "Couldn't parse Infinity or value too large, " + 'use quoted "Infinity" instead' + ) + else: + raise ParseError( + "Couldn't parse -Infinity or value too small, " + 'use quoted "-Infinity" instead' + ) + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: + # pylint: disable=protected-access + if value > type_checkers._FLOAT_MAX: + raise ParseError('Float value too large') + # pylint: disable=protected-access + if value < type_checkers._FLOAT_MIN: + raise ParseError('Float value too small') + if value == 'nan': + raise ParseError('Couldn\'t parse float "nan", use "NaN" instead') + try: + # Assume Python compatible syntax. + return float(value) + except ValueError as e: + # Check alternative spellings. + if value == _NEG_INFINITY: + return float('-inf') + elif value == _INFINITY: + return float('inf') + elif value == _NAN: + return float('nan') + else: + raise ParseError("Couldn't parse float: {0}".format(value)) from e + + +def _ConvertBool(value, require_str): + """Convert a boolean value. + + Args: + value: A scalar value to convert. + require_str: If True, value must be a str. + + Returns: + The bool parsed. + + Raises: + ParseError: If a boolean value couldn't be consumed. + """ + if require_str: + if value == 'true': + return True + elif value == 'false': + return False + else: + raise ParseError('Expected "true" or "false", not {0}'.format(value)) + + if not isinstance(value, bool): + raise ParseError('Expected true or false without quotes') + return value + + +_WKTJSONMETHODS = { + 'google.protobuf.Any': ['_AnyMessageToJsonObject', '_ConvertAnyMessage'], + 'google.protobuf.Duration': [ + '_GenericMessageToJsonObject', + '_ConvertGenericMessage', + ], + 'google.protobuf.FieldMask': [ + '_GenericMessageToJsonObject', + '_ConvertGenericMessage', + ], + 'google.protobuf.ListValue': [ + '_ListValueMessageToJsonObject', + '_ConvertListOrTupleValueMessage', + ], + 'google.protobuf.Struct': [ + '_StructMessageToJsonObject', + '_ConvertStructMessage', + ], + 'google.protobuf.Timestamp': [ + '_GenericMessageToJsonObject', + '_ConvertGenericMessage', + ], + 'google.protobuf.Value': [ + '_ValueMessageToJsonObject', + '_ConvertValueMessage', + ], +} diff --git a/venv/lib/python3.12/site-packages/google/protobuf/json_options_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/json_options_pb2.py new file mode 100755 index 0000000..15cdb6b --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/json_options_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/json_options.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/json_options.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import json_enumvalue_options_pb2 as google_dot_protobuf_dot_json__enumvalue__options__pb2 + +from google.protobuf.json_enumvalue_options_pb2 import * + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"google/protobuf/json_options.proto\x12\x02pb\x1a,google/protobuf/json_enumvalue_options.protoB\x1a\n\x18\x63om.google.protobuf.utilP\x00\x62\x08\x65\x64itionsp\xe9\x07') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.json_options_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\030com.google.protobuf.util' +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/message.py b/venv/lib/python3.12/site-packages/google/protobuf/message.py new file mode 100755 index 0000000..bfab0f9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/message.py @@ -0,0 +1,467 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +# TODO: We should just make these methods all "pure-virtual" and move +# all implementation out, into reflection.py for now. +"""Contains an abstract base class for protocol messages.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +_INCONSISTENT_MESSAGE_ATTRIBUTES = ('Extensions',) + + +class Error(Exception): + """Base error type for this module.""" + + pass + + +class DecodeError(Error): + """Exception raised when deserializing messages.""" + + pass + + +class EncodeError(Error): + """Exception raised when serializing messages.""" + + pass + + +class FrozenInstanceError(AttributeError): + """Exception raised when mutating a frozen message.""" + + pass + + +class Message(object): + """Abstract base class for protocol messages. + + Protocol message classes are almost always generated by the protocol + compiler. These generated types subclass Message and implement the methods + shown below. + """ + + # TODO: Link to an HTML document here. + + # TODO: Document that instances of this class will also + # have an Extensions attribute with __getitem__ and __setitem__. + # Again, not sure how to best convey this. + + # TODO: Document these fields and methods. + + __slots__ = [] + + #: The :class:`google.protobuf.Descriptor` + # for this message type. + DESCRIPTOR = None + + def __deepcopy__(self, memo=None): + clone = type(self)() + clone.MergeFrom(self) + return clone + + def __dir__(self): + """Provides the list of all accessible Message attributes.""" + message_attributes = set(super().__dir__()) + + # TODO: Remove this once the UPB implementation is improved. + # The UPB proto implementation currently doesn't provide proto fields as + # attributes and they have to added. + if self.DESCRIPTOR is not None: + for field in self.DESCRIPTOR.fields: + message_attributes.add(field.name) + + # The Fast C++ proto implementation provides inaccessible attributes that + # have to be removed. + for attribute in _INCONSISTENT_MESSAGE_ATTRIBUTES: + if attribute not in message_attributes: + continue + try: + getattr(self, attribute) + except AttributeError: + message_attributes.remove(attribute) + + return sorted(message_attributes) + + def __eq__(self, other_msg): + """Recursively compares two messages by value and structure.""" + raise NotImplementedError + + def __ne__(self, other_msg): + # Can't just say self != other_msg, since that would infinitely recurse. :) + return not self == other_msg + + def __hash__(self): + raise TypeError('unhashable object') + + def __str__(self): + """Outputs a human-readable representation of the message.""" + raise NotImplementedError + + def __unicode__(self): + """Outputs a human-readable representation of the message.""" + raise NotImplementedError + + def __contains__(self, field_name_or_key): + """Checks if a certain field is set for the message. + + Has presence fields return true if the field is set, false if the field is + not set. Fields without presence do raise `ValueError` (this includes + repeated fields, map fields, and implicit presence fields). + + If field_name is not defined in the message descriptor, `ValueError` will + be raised. + Note: WKT Struct checks if the key is contained in fields. ListValue checks + if the item is contained in the list. + + Args: + field_name_or_key: For Struct, the key (str) of the fields map. For + ListValue, any type that may be contained in the list. For other + messages, name of the field (str) to check for presence. + + Returns: + bool: For Struct, whether the item is contained in fields. For ListValue, + whether the item is contained in the list. For other message, + whether a value has been set for the named field. + + Raises: + ValueError: For normal messages, if the `field_name_or_key` is not a + member of this message or `field_name_or_key` is not a string. + """ + raise NotImplementedError + + def MergeFrom(self, other_msg): + """Merges the contents of the specified message into current message. + + This method merges the contents of the specified message into the current + message. Singular fields that are set in the specified message overwrite + the corresponding fields in the current message. Repeated fields are + appended. Singular sub-messages and groups are recursively merged. + + Args: + other_msg (Message): A message to merge into the current message. + """ + raise NotImplementedError + + def CopyFrom(self, other_msg): + """Copies the content of the specified message into the current message. + + The method clears the current message and then merges the specified + message using MergeFrom. + + Args: + other_msg (Message): A message to copy into the current one. + """ + if self is other_msg: + return + self.Clear() + self.MergeFrom(other_msg) + + def Clear(self): + """Clears all data that was set in the message.""" + raise NotImplementedError + + def SetInParent(self): + """Mark this as present in the parent. + + This normally happens automatically when you assign a field of a + sub-message, but sometimes you want to make the sub-message + present while keeping it empty. If you find yourself using this, + you may want to reconsider your design. + """ + raise NotImplementedError + + def IsInitialized(self): + """Checks if the message is initialized. + + Returns: + bool: The method returns True if the message is initialized (i.e. all of + its required fields are set). + """ + raise NotImplementedError + + # TODO: MergeFromString() should probably return None and be + # implemented in terms of a helper that returns the # of bytes read. Our + # deserialization routines would use the helper when recursively + # deserializing, but the end user would almost always just want the no-return + # MergeFromString(). + + def MergeFromString(self, serialized): + """Merges serialized protocol buffer data into this message. + + When we find a field in `serialized` that is already present + in this message: + + - If it's a "repeated" field, we append to the end of our list. + - Else, if it's a scalar, we overwrite our field. + - Else, (it's a nonrepeated composite), we recursively merge + into the existing composite. + + Args: + serialized (bytes): Any object that allows us to call + ``memoryview(serialized)`` to access a string of bytes using the buffer + interface. + + Returns: + int: The number of bytes read from `serialized`. + For non-group messages, this will always be `len(serialized)`, + but for messages which are actually groups, this will + generally be less than `len(serialized)`, since we must + stop when we reach an ``END_GROUP`` tag. Note that if + we *do* stop because of an ``END_GROUP`` tag, the number + of bytes returned does not include the bytes + for the ``END_GROUP`` tag information. + + Raises: + DecodeError: if the input cannot be parsed. + """ + # TODO: Document handling of unknown fields. + # TODO: When we switch to a helper, this will return None. + raise NotImplementedError + + def ParseFromString(self, serialized): + """Parse serialized protocol buffer data in binary form into this message. + + Like :func:`MergeFromString()`, except we clear the object first. + + Raises: + message.DecodeError if the input cannot be parsed. + """ + self.Clear() + return self.MergeFromString(serialized) + + def SerializeToString(self, **kwargs): + """Serializes the protocol message to a binary string. + + Keyword Args: + deterministic (bool): If true, requests deterministic serialization + of the protobuf. Note that there is no canonical representation of + protobuf messages: deterministic serialization only means 'consistent + for current build, but not stable between rebuilds, and may not match + decisions made by other languages'. + See + https://protobuf.dev/programming-guides/serialization-not-canonical/. + + Returns: + A binary string representation of the message if all of the required + fields in the message are set (i.e. the message is initialized). + + Raises: + EncodeError: if the message isn't initialized (see :func:`IsInitialized`). + """ + raise NotImplementedError + + def SerializePartialToString(self, **kwargs): + """Serializes the protocol message to a binary string. + + This method is similar to SerializeToString but doesn't check if the + message is initialized. + + Keyword Args: + deterministic (bool): If true, requests deterministic serialization + of the protobuf. Note that 'deterministic' serialization only means + 'Consistent for current build, but still arbitary and not stable over + time'. There is no canonical representation of protobuf messages, + See + https://protobuf.dev/programming-guides/serialization-not-canonical/. + + Returns: + bytes: A serialized representation of the partial message. + """ + raise NotImplementedError + + # TODO: Decide whether we like these better + # than auto-generated has_foo() and clear_foo() methods + # on the instances themselves. This way is less consistent + # with C++, but it makes reflection-type access easier and + # reduces the number of magically autogenerated things. + # + # TODO: Be sure to document (and test) exactly + # which field names are accepted here. Are we case-sensitive? + # What do we do with fields that share names with Python keywords + # like 'lambda' and 'yield'? + # + # nnorwitz says: + # """ + # Typically (in python), an underscore is appended to names that are + # keywords. So they would become lambda_ or yield_. + # """ + def ListFields(self): + """Returns a list of (FieldDescriptor, value) tuples for present fields. + + A message field is non-empty if HasField() would return true. A singular + primitive field is non-empty if HasField() would return true in proto2 or it + is non zero in proto3. A repeated field is non-empty if it contains at least + one element. The fields are ordered by field number. + + Returns: + list[tuple(FieldDescriptor, value)]: field descriptors and values + for all fields in the message which are not empty. The values vary by + field type. + """ + raise NotImplementedError + + def HasField(self, field_name): + """Checks if a certain field is set for the message. + + For a oneof group, checks if any field inside is set. Note that if the + field_name is not defined in the message descriptor, :exc:`ValueError` will + be raised. + + Args: + field_name (str): The name of the field to check for presence. + + Returns: + bool: Whether a value has been set for the named field. + + Raises: + ValueError: if the `field_name` is not a member of this message. + """ + raise NotImplementedError + + def ClearField(self, field_name): + """Clears the contents of a given field. + + Inside a oneof group, clears the field set. If the name neither refers to a + defined field or oneof group, :exc:`ValueError` is raised. + + Args: + field_name (str): The name of the field to check for presence. + + Raises: + ValueError: if the `field_name` is not a member of this message. + """ + raise NotImplementedError + + def WhichOneof(self, oneof_group): + """Returns the name of the field that is set inside a oneof group. + + If no field is set, returns None. + + Args: + oneof_group (str): the name of the oneof group to check. + + Returns: + str or None: The name of the group that is set, or None. + + Raises: + ValueError: no group with the given name exists + """ + raise NotImplementedError + + def HasExtension(self, field_descriptor): + """Checks if a certain extension is present for this message. + + Extensions are retrieved using the :attr:`Extensions` mapping (if present). + + Args: + field_descriptor: The field descriptor for the extension to check. + + Returns: + bool: Whether the extension is present for this message. + + Raises: + KeyError: if the extension is repeated. Similar to repeated fields, + there is no separate notion of presence: a "not present" repeated + extension is an empty list. + """ + raise NotImplementedError + + def ClearExtension(self, field_descriptor): + """Clears the contents of a given extension. + + Args: + field_descriptor: The field descriptor for the extension to clear. + """ + raise NotImplementedError + + def UnknownFields(self): + """Returns the UnknownFieldSet. + + Returns: + UnknownFieldSet: The unknown fields stored in this message. + """ + raise NotImplementedError + + def DiscardUnknownFields(self): + """Clears all fields in the :class:`UnknownFieldSet`. + + This operation is recursive for nested message. + """ + raise NotImplementedError + + def ByteSize(self): + """Returns the serialized size of this message. + + Recursively calls ByteSize() on all contained messages. + + Returns: + int: The number of bytes required to serialize this message. + """ + raise NotImplementedError + + @classmethod + def FromString(cls, s): + raise NotImplementedError + + def _SetListener(self, message_listener): + """Internal method used by the protocol message implementation. + + Clients should not call this directly. + + Sets a listener that this message will call on certain state transitions. + + The purpose of this method is to register back-edges from children to + parents at runtime, for the purpose of setting "has" bits and + byte-size-dirty bits in the parent and ancestor objects whenever a child or + descendant object is modified. + + If the client wants to disconnect this Message from the object tree, she + explicitly sets callback to None. + + If message_listener is None, unregisters any existing listener. Otherwise, + message_listener must implement the MessageListener interface in + internal/message_listener.py, and we discard any listener registered + via a previous _SetListener() call. + """ + raise NotImplementedError + + def __getstate__(self): + """Support the pickle protocol.""" + return dict(serialized=self.SerializePartialToString()) + + def __setstate__(self, state): + """Support the pickle protocol.""" + self.__init__() + serialized = state['serialized'] + # On Python 3, using encoding='latin1' is required for unpickling + # protos pickled by Python 2. + if not isinstance(serialized, bytes): + serialized = serialized.encode('latin1') + self.ParseFromString(serialized) + + def __reduce__(self): + message_descriptor = self.DESCRIPTOR + if message_descriptor.containing_type is None: + return type(self), (), self.__getstate__() + # the message type must be nested. + # Python does not pickle nested classes; use the symbol_database on the + # receiving end. + container = message_descriptor + return ( + _InternalConstructMessage, + (container.full_name,), + self.__getstate__(), + ) + + +def _InternalConstructMessage(full_name): + """Constructs a nested message.""" + from google.protobuf import symbol_database # pylint:disable=g-import-not-at-top + + return symbol_database.Default().GetSymbol(full_name)() diff --git a/venv/lib/python3.12/site-packages/google/protobuf/message_factory.py b/venv/lib/python3.12/site-packages/google/protobuf/message_factory.py new file mode 100755 index 0000000..1dca055 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/message_factory.py @@ -0,0 +1,188 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Provides a factory class for generating dynamic messages. + +The easiest way to use this class is if you have access to the FileDescriptor +protos containing the messages you want to create you can just do the following: + +message_classes = message_factory.GetMessages(iterable_of_file_descriptors) +my_proto_instance = message_classes['some.proto.package.MessageName']() +""" + +__author__ = 'matthewtoia@google.com (Matt Toia)' + +import warnings + +from google.protobuf import descriptor_pool +from google.protobuf import message +from google.protobuf.internal import api_implementation + +if api_implementation.Type() == 'python': + from google.protobuf.internal import python_message as message_impl +else: + from google.protobuf.pyext import cpp_message as message_impl # pylint: disable=g-import-not-at-top + +# The type of all Message classes. +_GENERATED_PROTOCOL_MESSAGE_TYPE = message_impl.GeneratedProtocolMessageType + + +def GetMessageClass(descriptor): + """Obtains a proto2 message class based on the passed in descriptor. + + Passing a descriptor with a fully qualified name matching a previous + invocation will cause the same class to be returned. + + Args: + descriptor: The descriptor to build from. + + Returns: + A class describing the passed in descriptor. + """ + concrete_class = getattr(descriptor, '_concrete_class', None) + if concrete_class: + return concrete_class + return _InternalCreateMessageClass(descriptor) + + +def GetMessageClassesForFiles(files, pool): + """Gets all the messages from specified files. + + This will find and resolve dependencies, failing if the descriptor + pool cannot satisfy them. + + This will not return the classes for nested types within those classes, for + those, use GetMessageClass() on the nested types within their containing + messages. + + For example, for the message: + + message NestedTypeMessage { + message NestedType { + string data = 1; + } + NestedType nested = 1; + } + + NestedTypeMessage will be in the result, but not + NestedTypeMessage.NestedType. + + Args: + files: The file names to extract messages from. + pool: The descriptor pool to find the files including the dependent files. + + Returns: + A dictionary mapping proto names to the message classes. + """ + result = {} + for file_name in files: + file_desc = pool.FindFileByName(file_name) + for desc in file_desc.message_types_by_name.values(): + result[desc.full_name] = GetMessageClass(desc) + + # While the extension FieldDescriptors are created by the descriptor pool, + # the python classes created in the factory need them to be registered + # explicitly, which is done below. + # + # The call to RegisterExtension will specifically check if the + # extension was already registered on the object and either + # ignore the registration if the original was the same, or raise + # an error if they were different. + + for extension in file_desc.extensions_by_name.values(): + _ = GetMessageClass(extension.containing_type) + if api_implementation.Type() != 'python': + # TODO: Remove this check here. Duplicate extension + # register check should be in descriptor_pool. + if extension is not pool.FindExtensionByNumber( + extension.containing_type, extension.number + ): + raise ValueError('Double registration of Extensions') + # Recursively load protos for extension field, in order to be able to + # fully represent the extension. This matches the behavior for regular + # fields too. + if extension.message_type: + GetMessageClass(extension.message_type) + return result + + +def _InternalCreateMessageClass(descriptor): + """Builds a proto2 message class based on the passed in descriptor. + + Args: + descriptor: The descriptor to build from. + + Returns: + A class describing the passed in descriptor. + """ + descriptor_name = descriptor.name + result_class = _GENERATED_PROTOCOL_MESSAGE_TYPE( + descriptor_name, + (message.Message,), + { + 'DESCRIPTOR': descriptor, + # If module not set, it wrongly points to message_factory module. + '__module__': None, + }, + ) + for field in descriptor.fields: + if field.message_type: + GetMessageClass(field.message_type) + + for extension in result_class.DESCRIPTOR.extensions: + extended_class = GetMessageClass(extension.containing_type) + if api_implementation.Type() != 'python': + # TODO: Remove this check here. Duplicate extension + # register check should be in descriptor_pool. + pool = extension.containing_type.file.pool + if extension is not pool.FindExtensionByNumber( + extension.containing_type, extension.number + ): + raise ValueError('Double registration of Extensions') + if extension.message_type: + GetMessageClass(extension.message_type) + return result_class + + +# Deprecated. Please use GetMessageClass() or GetMessageClassesForFiles() +# method above instead. +class MessageFactory(object): + """Factory for creating Proto2 messages from descriptors in a pool.""" + + def __init__(self, pool=None): + """Initializes a new factory.""" + self.pool = pool or descriptor_pool.DescriptorPool() + + +def GetMessages(file_protos, pool=None): + """Builds a dictionary of all the messages available in a set of files. + + Args: + file_protos: Iterable of FileDescriptorProto to build messages out of. + pool: The descriptor pool to add the file protos. + + Returns: + A dictionary mapping proto names to the message classes. This will include + any dependent messages as well as any messages defined in the same file as + a specified message. + """ + # The cpp implementation of the protocol buffer library requires to add the + # message in topological order of the dependency graph. + des_pool = pool or descriptor_pool.DescriptorPool() + file_by_name = {file_proto.name: file_proto for file_proto in file_protos} + + def _AddFile(file_proto): + for dependency in file_proto.dependency: + if dependency in file_by_name: + # Remove from elements to be visited, in order to cut cycles. + _AddFile(file_by_name.pop(dependency)) + des_pool.Add(file_proto) + + while file_by_name: + _AddFile(file_by_name.popitem()[1]) + return GetMessageClassesForFiles( + [file_proto.name for file_proto in file_protos], des_pool + ) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/proto.py b/venv/lib/python3.12/site-packages/google/protobuf/proto.py new file mode 100755 index 0000000..ddaac26 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/proto.py @@ -0,0 +1,153 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Contains the Nextgen Pythonic protobuf APIs.""" + +import io +from typing import Text, Type, TypeVar + +from google.protobuf.internal import decoder +from google.protobuf.internal import encoder +from google.protobuf.message import Message + +_MESSAGE = TypeVar('_MESSAGE', bound='Message') + + +def serialize(message: _MESSAGE, deterministic: bool = None) -> bytes: + """Return the serialized proto. + + Args: + message: The proto message to be serialized. + deterministic: If true, requests deterministic serialization of the + protobuf, with predictable ordering of map keys. + + Returns: + A binary bytes representation of the message. + """ + return message.SerializeToString(deterministic=deterministic) + + +def parse(message_class: Type[_MESSAGE], payload: bytes) -> _MESSAGE: + """Given a serialized data in binary form, deserialize it into a Message. + + Args: + message_class: The message meta class. + payload: A serialized bytes in binary form. + + Returns: + A new message deserialized from payload. + """ + new_message = message_class() + new_message.ParseFromString(payload) + return new_message + + +def serialize_length_prefixed(message: _MESSAGE, output: io.BytesIO) -> None: + """Writes the size of the message as a varint and the serialized message. + + Writes the size of the message as a varint and then the serialized message. + This allows more data to be written to the output after the message. Use + parse_length_prefixed to parse messages written by this method. + + The output stream must be buffered, e.g. using + https://docs.python.org/3/library/io.html#buffered-streams. + + Example usage: + out = io.BytesIO() + for msg in message_list: + proto.serialize_length_prefixed(msg, out) + + Args: + message: The protocol buffer message that should be serialized. + output: BytesIO or custom buffered IO that data should be written to. + """ + payload = serialize(message) + size = len(payload) + encoder._VarintEncoder()(output.write, size) + out_size = output.write(payload) + + if out_size != size: + raise TypeError( + 'Failed to write complete message (wrote: %d, expected: %d)' + '. Ensure output is using buffered IO.' % (out_size, size) + ) + + +def parse_length_prefixed( + message_class: Type[_MESSAGE], input_bytes: io.BytesIO +) -> _MESSAGE: + """Parse a message from input_bytes. + + Args: + message_class: The protocol buffer message class that parser should parse. + input_bytes: A buffered input. + + Example usage: + while True: + msg = proto.parse_length_prefixed(message_class, input_bytes) + if msg is None: + break + ... + + Returns: + A parsed message if successful. None if input_bytes is at EOF. + """ + size = decoder._DecodeVarint(input_bytes) + if size is None: + # It is the end of buffered input. See example usage in the + # API description. + return None + + message = message_class() + + if size == 0: + return message + + parsed_size = message.ParseFromString(input_bytes.read(size)) + if parsed_size != size: + raise ValueError( + 'Truncated message or non-buffered input_bytes: ' + 'Expected {0} bytes but only {1} bytes parsed for ' + '{2}.'.format(size, parsed_size, message.DESCRIPTOR.name) + ) + return message + + +def byte_size(message: Message) -> int: + """Returns the serialized size of this message. + + Args: + message: A proto message. + + Returns: + int: The number of bytes required to serialize this message. + """ + return message.ByteSize() + + +def clear_message(message: Message) -> None: + """Clears all data that was set in the message. + + Args: + message: The proto message to be cleared. + """ + message.Clear() + + +def clear_field(message: Message, field_name: Text) -> None: + """Clears the contents of a given field. + + Inside a oneof group, clears the field set. If the name neither refers to a + defined field or oneof group, :exc:`ValueError` is raised. + + Args: + message: The proto message. + field_name (str): The name of the field to be cleared. + + Raises: + ValueError: if the `field_name` is not a member of this message. + """ + message.ClearField(field_name) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/proto_builder.py b/venv/lib/python3.12/site-packages/google/protobuf/proto_builder.py new file mode 100755 index 0000000..732cffd --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/proto_builder.py @@ -0,0 +1,117 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Dynamic Protobuf class creator.""" + +from collections import OrderedDict +import hashlib +import os + +from google.protobuf import descriptor +from google.protobuf import descriptor_pb2 +from google.protobuf import descriptor_pool +from google.protobuf import message_factory + + +def _GetMessageFromFactory(pool, full_name): + """Get a proto class from the MessageFactory by name. + + Args: + pool: a descriptor pool. + full_name: str, the fully qualified name of the proto type. + + Returns: + A class, for the type identified by full_name. + Raises: + KeyError, if the proto is not found in the factory's descriptor pool. + """ + proto_descriptor = pool.FindMessageTypeByName(full_name) + proto_cls = message_factory.GetMessageClass(proto_descriptor) + return proto_cls + + +def MakeSimpleProtoClass(fields, full_name=None, pool=None): + """Create a Protobuf class whose fields are basic types. + + Note: this doesn't validate field names! + + Args: + fields: dict of {name: field_type} mappings for each field in the proto. If + this is an OrderedDict the order will be maintained, otherwise the fields + will be sorted by name. + full_name: optional str, the fully-qualified name of the proto type. + pool: optional DescriptorPool instance. + + Returns: + a class, the new protobuf class with a FileDescriptor. + """ + pool_instance = pool or descriptor_pool.DescriptorPool() + if full_name is not None: + try: + proto_cls = _GetMessageFromFactory(pool_instance, full_name) + return proto_cls + except KeyError: + # The factory's DescriptorPool doesn't know about this class yet. + pass + + # Get a list of (name, field_type) tuples from the fields dict. If fields was + # an OrderedDict we keep the order, but otherwise we sort the field to ensure + # consistent ordering. + field_items = fields.items() + if not isinstance(fields, OrderedDict): + field_items = sorted(field_items) + + # Use a consistent file name that is unlikely to conflict with any imported + # proto files. + fields_hash = hashlib.sha1(usedforsecurity=False) + for f_name, f_type in field_items: + fields_hash.update(f_name.encode('utf-8')) + fields_hash.update(str(f_type).encode('utf-8')) + proto_file_name = fields_hash.hexdigest() + '.proto' + + # If the proto is anonymous, use the same hash to name it. + if full_name is None: + full_name = ( + 'net.proto2.python.public.proto_builder.AnonymousProto_' + + fields_hash.hexdigest() + ) + try: + proto_cls = _GetMessageFromFactory(pool_instance, full_name) + return proto_cls + except KeyError: + # The factory's DescriptorPool doesn't know about this class yet. + pass + + # This is the first time we see this proto: add a new descriptor to the pool. + pool_instance.Add( + _MakeFileDescriptorProto(proto_file_name, full_name, field_items) + ) + return _GetMessageFromFactory(pool_instance, full_name) + + +def _MakeFileDescriptorProto(proto_file_name, full_name, field_items): + """Populate FileDescriptorProto for MessageFactory's DescriptorPool.""" + package, name = full_name.rsplit('.', 1) + file_proto = descriptor_pb2.FileDescriptorProto() + file_proto.name = os.path.join(package.replace('.', '/'), proto_file_name) + file_proto.package = package + desc_proto = file_proto.message_type.add() + desc_proto.name = name + for f_number, (f_name, f_type) in enumerate(field_items, 1): + field_proto = desc_proto.field.add() + field_proto.name = f_name + # # If the number falls in the reserved range, reassign it to the correct + # # number after the range. + if f_number >= descriptor.FieldDescriptor.FIRST_RESERVED_FIELD_NUMBER: + f_number += ( + descriptor.FieldDescriptor.LAST_RESERVED_FIELD_NUMBER + - descriptor.FieldDescriptor.FIRST_RESERVED_FIELD_NUMBER + + 1 + ) + field_proto.number = f_number + field_proto.label = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL + field_proto.type = f_type + return file_proto diff --git a/venv/lib/python3.12/site-packages/google/protobuf/proto_json.py b/venv/lib/python3.12/site-packages/google/protobuf/proto_json.py new file mode 100755 index 0000000..dbdba5e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/proto_json.py @@ -0,0 +1,81 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Contains the Nextgen Pythonic Protobuf JSON APIs.""" + +from typing import Optional, Type + +from google.protobuf import json_format +from google.protobuf.descriptor_pool import DescriptorPool +from google.protobuf.message import Message + + +def serialize( + message: Message, + always_print_fields_with_no_presence: bool = False, + preserving_proto_field_name: bool = False, + use_integers_for_enums: bool = False, + descriptor_pool: Optional[DescriptorPool] = None, +) -> dict: + """Converts protobuf message to a dictionary. + + When the dictionary is encoded to JSON, it conforms to ProtoJSON spec. + + Args: + message: The protocol buffers message instance to serialize. + always_print_fields_with_no_presence: If True, fields without presence + (implicit presence scalars, repeated fields, and map fields) will always + be serialized. Any field that supports presence is not affected by this + option (including singular message fields and oneof fields). + preserving_proto_field_name: If True, use the original proto field names as + defined in the .proto file. If False, convert the field names to + lowerCamelCase. + use_integers_for_enums: If true, print integers instead of enum names. + descriptor_pool: A Descriptor Pool for resolving types. If None use the + default. + + Returns: + A dict representation of the protocol buffer message. + """ + return json_format.MessageToDict( + message, + always_print_fields_with_no_presence=always_print_fields_with_no_presence, + preserving_proto_field_name=preserving_proto_field_name, + use_integers_for_enums=use_integers_for_enums, + ) + + +def parse( + message_class: Type[Message], + js_dict: dict, + ignore_unknown_fields: bool = False, + descriptor_pool: Optional[DescriptorPool] = None, + max_recursion_depth: int = 100, +) -> Message: + """Parses a JSON dictionary representation into a message. + + Args: + message_class: The message meta class. + js_dict: Dict representation of a JSON message. + ignore_unknown_fields: If True, do not raise errors for unknown fields. + descriptor_pool: A Descriptor Pool for resolving types. If None use the + default. + max_recursion_depth: max recursion depth of JSON message to be deserialized. + JSON messages over this depth will fail to be deserialized. Default value + is 100. + + Returns: + A new message passed from json_dict. + """ + new_message = message_class() + json_format.ParseDict( + js_dict=js_dict, + message=new_message, + ignore_unknown_fields=ignore_unknown_fields, + descriptor_pool=descriptor_pool, + max_recursion_depth=max_recursion_depth, + ) + return new_message diff --git a/venv/lib/python3.12/site-packages/google/protobuf/proto_text.py b/venv/lib/python3.12/site-packages/google/protobuf/proto_text.py new file mode 100755 index 0000000..0533f97 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/proto_text.py @@ -0,0 +1,118 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2025 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Contains the Nextgen Pythonic Protobuf Text Format APIs.""" + +from typing import AnyStr, Callable, Optional, Text, Type, Union + +from google.protobuf import text_format +from google.protobuf.descriptor_pool import DescriptorPool +from google.protobuf.message import Message + +_MsgFormatter = Callable[[Message, Union[int, bool], bool], Optional[Text]] + + +def serialize( + message: Message, + as_utf8: bool = True, + as_one_line: bool = False, + use_short_repeated_primitives: bool = False, + pointy_brackets: bool = False, + use_index_order: bool = False, + use_field_number: bool = False, + descriptor_pool: Optional[DescriptorPool] = None, + indent: int = 0, + message_formatter: Optional[_MsgFormatter] = None, + print_unknown_fields: bool = False, + force_colon: bool = False, +) -> str: + """Convert protobuf message to text format. + + Double values can be formatted compactly with 15 digits of + precision (which is the most that IEEE 754 "double" can guarantee) + using double_format='.15g'. To ensure that converting to text and back to a + proto will result in an identical value, double_format='.17g' should be used. + + Args: + message: The protocol buffers message. + as_utf8: Return unescaped Unicode for non-ASCII characters. + as_one_line: Don't introduce newlines between fields. + use_short_repeated_primitives: Use short repeated format for primitives. + pointy_brackets: If True, use angle brackets instead of curly braces for + nesting. + use_index_order: If True, fields of a proto message will be printed using + the order defined in source code instead of the field number, extensions + will be printed at the end of the message and their relative order is + determined by the extension number. By default, use the field number + order. + use_field_number: If True, print field numbers instead of names. + descriptor_pool (DescriptorPool): Descriptor pool used to resolve Any types. + indent (int): The initial indent level, in terms of spaces, for pretty + print. + message_formatter (function(message, indent, as_one_line) -> unicode|None): + Custom formatter for selected sub-messages (usually based on message + type). Use to pretty print parts of the protobuf for easier diffing. + print_unknown_fields: If True, unknown fields will be printed. + force_colon: If set, a colon will be added after the field name even if the + field is a proto message. + + Returns: + str: A string of the text formatted protocol buffer message. + """ + return text_format.MessageToString( + message=message, + as_utf8=as_utf8, + as_one_line=as_one_line, + use_short_repeated_primitives=use_short_repeated_primitives, + pointy_brackets=pointy_brackets, + use_index_order=use_index_order, + use_field_number=use_field_number, + descriptor_pool=descriptor_pool, + indent=indent, + message_formatter=message_formatter, + print_unknown_fields=print_unknown_fields, + force_colon=force_colon, + ) + + +def parse( + message_class: Type[Message], + text: AnyStr, + allow_unknown_extension: bool = False, + allow_field_number: bool = False, + descriptor_pool: Optional[DescriptorPool] = None, + allow_unknown_field: bool = False, +) -> Message: + """Parses a text representation of a protocol message into a message. + + Args: + message_class: The message meta class. + text (str): Message text representation. + message (Message): A protocol buffer message to merge into. + allow_unknown_extension: if True, skip over missing extensions and keep + parsing + allow_field_number: if True, both field number and field name are allowed. + descriptor_pool (DescriptorPool): Descriptor pool used to resolve Any types. + allow_unknown_field: if True, skip over unknown field and keep parsing. + Avoid to use this option if possible. It may hide some errors (e.g. + spelling error on field name) + + Returns: + Message: A new message passed from text. + + Raises: + ParseError: On text parsing problems. + """ + new_message = message_class() + text_format.Parse( + text=text, + message=new_message, + allow_unknown_extension=allow_unknown_extension, + allow_field_number=allow_field_number, + descriptor_pool=descriptor_pool, + allow_unknown_field=allow_unknown_field, + ) + return new_message diff --git a/venv/lib/python3.12/site-packages/google/protobuf/pyext/__init__.py b/venv/lib/python3.12/site-packages/google/protobuf/pyext/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/google/protobuf/pyext/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/pyext/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..9687708 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/pyext/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/pyext/__pycache__/cpp_message.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/pyext/__pycache__/cpp_message.cpython-312.pyc new file mode 100644 index 0000000..170929e Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/pyext/__pycache__/cpp_message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/pyext/cpp_message.py b/venv/lib/python3.12/site-packages/google/protobuf/pyext/cpp_message.py new file mode 100755 index 0000000..be67be2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/pyext/cpp_message.py @@ -0,0 +1,46 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Protocol message implementation hooks for C++ implementation. + +Contains helper functions used to create protocol message classes from +Descriptor objects at runtime backed by the protocol buffer C++ API. +""" + +__author__ = 'tibell@google.com (Johan Tibell)' + +from google.protobuf.internal import api_implementation + +# pylint: disable=protected-access +_message = api_implementation._c_module +# TODO: Remove this import after fix api_implementation +if _message is None: + from google.protobuf.pyext import _message + + +class GeneratedProtocolMessageType(_message.MessageMeta): + """Metaclass for protocol message classes created at runtime from Descriptors. + + The protocol compiler currently uses this metaclass to create protocol + message classes at runtime. Clients can also manually create their own + classes at runtime, as in this example: + + mydescriptor = Descriptor(.....) + factory = symbol_database.Default() + factory.pool.AddDescriptor(mydescriptor) + MyProtoClass = message_factory.GetMessageClass(mydescriptor) + myproto_instance = MyProtoClass() + myproto.foo_field = 23 + ... + + The above example will not work for nested types. If you wish to include them, + use reflection.MakeClass() instead of manually instantiating the class in + order to create the appropriate class structure. + """ + + # Must be consistent with the protocol-compiler code in + # proto2/compiler/internal/generator.*. + _DESCRIPTOR_KEY = 'DESCRIPTOR' diff --git a/venv/lib/python3.12/site-packages/google/protobuf/reflection.py b/venv/lib/python3.12/site-packages/google/protobuf/reflection.py new file mode 100755 index 0000000..84c1115 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/reflection.py @@ -0,0 +1,36 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +# This code is meant to work on Python 2.4 and above only. +"""Contains a metaclass and helper functions used to create + +protocol message classes from Descriptor objects at runtime. + +Recall that a metaclass is the "type" of a class. +(A class is to a metaclass what an instance is to a class.) + +In this case, we use the GeneratedProtocolMessageType metaclass +to inject all the useful functionality into the classes +output by the protocol compiler at compile-time. + +The upshot of all this is that the real implementation +details for ALL pure-Python protocol buffers are *here in +this file*. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import warnings + +from google.protobuf import message_factory +from google.protobuf import symbol_database + +# The type of all Message classes. +# Part of the public interface, but normally only used by message factories. +GeneratedProtocolMessageType = message_factory._GENERATED_PROTOCOL_MESSAGE_TYPE + +MESSAGE_CLASS_CACHE = {} diff --git a/venv/lib/python3.12/site-packages/google/protobuf/runtime_version.py b/venv/lib/python3.12/site-packages/google/protobuf/runtime_version.py new file mode 100755 index 0000000..857f373 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/runtime_version.py @@ -0,0 +1,105 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Protobuf Runtime versions and validators. + +It should only be accessed by Protobuf gencodes and tests. DO NOT USE it +elsewhere. +""" + +__author__ = 'shaod@google.com (Dennis Shao)' + +from enum import Enum + +import os +import warnings + + +class Domain(Enum): + GOOGLE_INTERNAL = 1 + PUBLIC = 2 + + +# The versions of this Python Protobuf runtime to be changed automatically by +# the Protobuf release process. Do not edit them manually. +# These OSS versions are not stripped to avoid merging conflicts. +OSS_DOMAIN = Domain.PUBLIC +OSS_MAJOR = 7 +OSS_MINOR = 36 +OSS_PATCH = 2 +OSS_SUFFIX = '' + +DOMAIN = OSS_DOMAIN +MAJOR = OSS_MAJOR +MINOR = OSS_MINOR +PATCH = OSS_PATCH +SUFFIX = OSS_SUFFIX + +# Avoid flooding of warnings. +_MAX_WARNING_COUNT = 20 +_warning_count = 0 + + +class VersionError(Exception): + """Exception class for version violation.""" + + +def _ReportVersionError(msg): + raise VersionError(msg) + + +def ValidateProtobufRuntimeVersion( + gen_domain, gen_major, gen_minor, gen_patch, gen_suffix, location +): + """Function to validate versions. + + Args: + gen_domain: The domain where the code was generated from. + gen_major: The major version number of the gencode. + gen_minor: The minor version number of the gencode. + gen_patch: The patch version number of the gencode. + gen_suffix: The version suffix e.g. '-dev', '-rc1' of the gencode. + location: The proto location that causes the version violation. + + Raises: + VersionError: if gencode version is invalid or incompatible with the + runtime. + """ + + disable_flag = os.getenv('TEMPORARILY_DISABLE_PROTOBUF_VERSION_CHECK') + if disable_flag is not None and disable_flag.lower() == 'true': + return + + global _warning_count + + version = f'{MAJOR}.{MINOR}.{PATCH}{SUFFIX}' + gen_version = f'{gen_major}.{gen_minor}.{gen_patch}{gen_suffix}' + + if gen_major < 0 or gen_minor < 0 or gen_patch < 0: + raise VersionError(f'Invalid gencode version: {gen_version}') + + error_prompt = ( + 'See Protobuf version guarantees at' + ' https://protobuf.dev/support/cross-version-runtime-guarantee.' + ) + + if gen_domain != DOMAIN: + _ReportVersionError( + 'Detected mismatched Protobuf Gencode/Runtime domains when loading' + f' {location}: gencode {gen_domain.name} runtime {DOMAIN.name}.' + ' Cross-domain usage of Protobuf is not supported.' + ) + + if ( + MAJOR < gen_major + or (MAJOR == gen_major and MINOR < gen_minor) + or (MAJOR == gen_major and MINOR == gen_minor and PATCH < gen_patch) + ): + _ReportVersionError( + 'Detected incompatible Protobuf Gencode/Runtime versions when loading' + f' {location}: gencode {gen_version} runtime {version}. Runtime version' + f' cannot be older than the linked gencode version. {error_prompt}' + ) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/service_reflection.py b/venv/lib/python3.12/site-packages/google/protobuf/service_reflection.py new file mode 100755 index 0000000..8760be5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/service_reflection.py @@ -0,0 +1,284 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +"""Contains metaclasses used to create protocol service and service stub + +classes from ServiceDescriptor objects at runtime. + +The GeneratedServiceType and GeneratedServiceStubType metaclasses are used to +inject all useful functionality into the classes output by the protocol +compiler at compile-time. +""" + +__author__ = 'petar@google.com (Petar Petrov)' + + +class GeneratedServiceType(type): + """Metaclass for service classes created at runtime from ServiceDescriptors. + + Implementations for all methods described in the Service class are added here + by this class. We also create properties to allow getting/setting all fields + in the protocol message. + + The protocol compiler currently uses this metaclass to create protocol service + classes at runtime. Clients can also manually create their own classes at + runtime, as in this example:: + + mydescriptor = ServiceDescriptor(.....) + class MyProtoService(service.Service): + __metaclass__ = GeneratedServiceType + DESCRIPTOR = mydescriptor + myservice_instance = MyProtoService() + # ... + """ + + _DESCRIPTOR_KEY = 'DESCRIPTOR' + + def __init__(cls, name, bases, dictionary): + """Creates a message service class. + + Args: + name: Name of the class (ignored, but required by the metaclass protocol). + bases: Base classes of the class being constructed. + dictionary: The class dictionary of the class being constructed. + dictionary[_DESCRIPTOR_KEY] must contain a ServiceDescriptor object + describing this protocol service type. + """ + # Don't do anything if this class doesn't have a descriptor. This happens + # when a service class is subclassed. + if GeneratedServiceType._DESCRIPTOR_KEY not in dictionary: + return + + descriptor = dictionary[GeneratedServiceType._DESCRIPTOR_KEY] + service_builder = _ServiceBuilder(descriptor) + service_builder.BuildService(cls) + cls.DESCRIPTOR = descriptor + + +class GeneratedServiceStubType(GeneratedServiceType): + """Metaclass for service stubs created at runtime from ServiceDescriptors. + + This class has similar responsibilities as GeneratedServiceType, except that + it creates the service stub classes. + """ + + _DESCRIPTOR_KEY = 'DESCRIPTOR' + + def __init__(cls, name, bases, dictionary): + """Creates a message service stub class. + + Args: + name: Name of the class (ignored, here). + bases: Base classes of the class being constructed. + dictionary: The class dictionary of the class being constructed. + dictionary[_DESCRIPTOR_KEY] must contain a ServiceDescriptor object + describing this protocol service type. + """ + super(GeneratedServiceStubType, cls).__init__(name, bases, dictionary) + # Don't do anything if this class doesn't have a descriptor. This happens + # when a service stub is subclassed. + if GeneratedServiceStubType._DESCRIPTOR_KEY not in dictionary: + return + + descriptor = dictionary[GeneratedServiceStubType._DESCRIPTOR_KEY] + service_stub_builder = _ServiceStubBuilder(descriptor) + service_stub_builder.BuildServiceStub(cls) + + +class _ServiceBuilder(object): + """This class constructs a protocol service class using a service descriptor. + + Given a service descriptor, this class constructs a class that represents + the specified service descriptor. One service builder instance constructs + exactly one service class. That means all instances of that class share the + same builder. + """ + + def __init__(self, service_descriptor): + """Initializes an instance of the service class builder. + + Args: + service_descriptor: ServiceDescriptor to use when constructing the service + class. + """ + self.descriptor = service_descriptor + + def BuildService(builder, cls): + """Constructs the service class. + + Args: + cls: The class that will be constructed. + """ + + # CallMethod needs to operate with an instance of the Service class. This + # internal wrapper function exists only to be able to pass the service + # instance to the method that does the real CallMethod work. + # Making sure to use exact argument names from the abstract interface in + # service.py to match the type signature + def _WrapCallMethod(self, method_descriptor, rpc_controller, request, done): + return builder._CallMethod( + self, method_descriptor, rpc_controller, request, done + ) + + def _WrapGetRequestClass(self, method_descriptor): + return builder._GetRequestClass(method_descriptor) + + def _WrapGetResponseClass(self, method_descriptor): + return builder._GetResponseClass(method_descriptor) + + builder.cls = cls + cls.CallMethod = _WrapCallMethod + cls.GetDescriptor = staticmethod(lambda: builder.descriptor) + cls.GetDescriptor.__doc__ = 'Returns the service descriptor.' + cls.GetRequestClass = _WrapGetRequestClass + cls.GetResponseClass = _WrapGetResponseClass + for method in builder.descriptor.methods: + setattr(cls, method.name, builder._GenerateNonImplementedMethod(method)) + + def _CallMethod( + self, srvc, method_descriptor, rpc_controller, request, callback + ): + """Calls the method described by a given method descriptor. + + Args: + srvc: Instance of the service for which this method is called. + method_descriptor: Descriptor that represent the method to call. + rpc_controller: RPC controller to use for this method's execution. + request: Request protocol message. + callback: A callback to invoke after the method has completed. + """ + if method_descriptor.containing_service != self.descriptor: + raise RuntimeError( + 'CallMethod() given method descriptor for wrong service type.' + ) + method = getattr(srvc, method_descriptor.name) + return method(rpc_controller, request, callback) + + def _GetRequestClass(self, method_descriptor): + """Returns the class of the request protocol message. + + Args: + method_descriptor: Descriptor of the method for which to return the + request protocol message class. + + Returns: + A class that represents the input protocol message of the specified + method. + """ + if method_descriptor.containing_service != self.descriptor: + raise RuntimeError( + 'GetRequestClass() given method descriptor for wrong service type.' + ) + return method_descriptor.input_type._concrete_class + + def _GetResponseClass(self, method_descriptor): + """Returns the class of the response protocol message. + + Args: + method_descriptor: Descriptor of the method for which to return the + response protocol message class. + + Returns: + A class that represents the output protocol message of the specified + method. + """ + if method_descriptor.containing_service != self.descriptor: + raise RuntimeError( + 'GetResponseClass() given method descriptor for wrong service type.' + ) + return method_descriptor.output_type._concrete_class + + def _GenerateNonImplementedMethod(self, method): + """Generates and returns a method that can be set for a service methods. + + Args: + method: Descriptor of the service method for which a method is to be + generated. + + Returns: + A method that can be added to the service class. + """ + return lambda inst, rpc_controller, request, callback: ( + self._NonImplementedMethod(method.name, rpc_controller, callback) + ) + + def _NonImplementedMethod(self, method_name, rpc_controller, callback): + """The body of all methods in the generated service class. + + Args: + method_name: Name of the method being executed. + rpc_controller: RPC controller used to execute this method. + callback: A callback which will be invoked when the method finishes. + """ + rpc_controller.SetFailed('Method %s not implemented.' % method_name) + callback(None) + + +class _ServiceStubBuilder(object): + """Constructs a protocol service stub class using a service descriptor. + + Given a service descriptor, this class constructs a suitable stub class. + A stub is just a type-safe wrapper around an RpcChannel which emulates a + local implementation of the service. + + One service stub builder instance constructs exactly one class. It means all + instances of that class share the same service stub builder. + """ + + def __init__(self, service_descriptor): + """Initializes an instance of the service stub class builder. + + Args: + service_descriptor: ServiceDescriptor to use when constructing the stub + class. + """ + self.descriptor = service_descriptor + + def BuildServiceStub(self, cls): + """Constructs the stub class. + + Args: + cls: The class that will be constructed. + """ + + def _ServiceStubInit(stub, rpc_channel): + stub.rpc_channel = rpc_channel + + self.cls = cls + cls.__init__ = _ServiceStubInit + for method in self.descriptor.methods: + setattr(cls, method.name, self._GenerateStubMethod(method)) + + def _GenerateStubMethod(self, method): + return ( + lambda inst, rpc_controller, request, callback=None: self._StubMethod( + inst, method, rpc_controller, request, callback + ) + ) + + def _StubMethod( + self, stub, method_descriptor, rpc_controller, request, callback + ): + """The body of all service methods in the generated stub class. + + Args: + stub: Stub instance. + method_descriptor: Descriptor of the invoked method. + rpc_controller: Rpc controller to execute the method. + request: Request protocol message. + callback: A callback to execute when the method finishes. + + Returns: + Response message (in case of blocking call). + """ + return stub.rpc_channel.CallMethod( + method_descriptor, + rpc_controller, + request, + method_descriptor.output_type._concrete_class, + callback, + ) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/source_context_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/source_context_pb2.py new file mode 100755 index 0000000..f1ac797 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/source_context_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/source_context.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/source_context.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$google/protobuf/source_context.proto\x12\x0fgoogle.protobuf\",\n\rSourceContext\x12\x1b\n\tfile_name\x18\x01 \x01(\tR\x08\x66ileNameB\x8a\x01\n\x13\x63om.google.protobufB\x12SourceContextProtoP\x01Z6google.golang.org/protobuf/types/known/sourcecontextpb\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.source_context_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\023com.google.protobufB\022SourceContextProtoP\001Z6google.golang.org/protobuf/types/known/sourcecontextpb\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _globals['_SOURCECONTEXT']._serialized_start=57 + _globals['_SOURCECONTEXT']._serialized_end=101 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/struct_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/struct_pb2.py new file mode 100755 index 0000000..210af98 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/struct_pb2.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/struct.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/struct.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cgoogle/protobuf/struct.proto\x12\x0fgoogle.protobuf\"\x98\x01\n\x06Struct\x12;\n\x06\x66ields\x18\x01 \x03(\x0b\x32#.google.protobuf.Struct.FieldsEntryR\x06\x66ields\x1aQ\n\x0b\x46ieldsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12,\n\x05value\x18\x02 \x01(\x0b\x32\x16.google.protobuf.ValueR\x05value:\x02\x38\x01\"\xb2\x02\n\x05Value\x12;\n\nnull_value\x18\x01 \x01(\x0e\x32\x1a.google.protobuf.NullValueH\x00R\tnullValue\x12#\n\x0cnumber_value\x18\x02 \x01(\x01H\x00R\x0bnumberValue\x12#\n\x0cstring_value\x18\x03 \x01(\tH\x00R\x0bstringValue\x12\x1f\n\nbool_value\x18\x04 \x01(\x08H\x00R\tboolValue\x12<\n\x0cstruct_value\x18\x05 \x01(\x0b\x32\x17.google.protobuf.StructH\x00R\x0bstructValue\x12;\n\nlist_value\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.ListValueH\x00R\tlistValueB\x06\n\x04kind\";\n\tListValue\x12.\n\x06values\x18\x01 \x03(\x0b\x32\x16.google.protobuf.ValueR\x06values*\x1b\n\tNullValue\x12\x0e\n\nNULL_VALUE\x10\x00\x42\x7f\n\x13\x63om.google.protobufB\x0bStructProtoP\x01Z/google.golang.org/protobuf/types/known/structpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.struct_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\023com.google.protobufB\013StructProtoP\001Z/google.golang.org/protobuf/types/known/structpb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _globals['_STRUCT_FIELDSENTRY']._loaded_options = None + _globals['_STRUCT_FIELDSENTRY']._serialized_options = b'8\001' + _globals['_NULLVALUE']._serialized_start=574 + _globals['_NULLVALUE']._serialized_end=601 + _globals['_STRUCT']._serialized_start=50 + _globals['_STRUCT']._serialized_end=202 + _globals['_STRUCT_FIELDSENTRY']._serialized_start=121 + _globals['_STRUCT_FIELDSENTRY']._serialized_end=202 + _globals['_VALUE']._serialized_start=205 + _globals['_VALUE']._serialized_end=511 + _globals['_LISTVALUE']._serialized_start=513 + _globals['_LISTVALUE']._serialized_end=572 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/symbol_database.py b/venv/lib/python3.12/site-packages/google/protobuf/symbol_database.py new file mode 100755 index 0000000..6f02346 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/symbol_database.py @@ -0,0 +1,178 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""A database of Python protocol buffer generated symbols. + +SymbolDatabase is the MessageFactory for messages generated at compile time, +and makes it easy to create new instances of a registered type, given only the +type's protocol buffer symbol name. + +Example usage:: + + db = symbol_database.SymbolDatabase() + + # Register symbols of interest, from one or multiple files. + db.RegisterFileDescriptor(my_proto_pb2.DESCRIPTOR) + db.RegisterMessage(my_proto_pb2.MyMessage) + db.RegisterEnumDescriptor(my_proto_pb2.MyEnum.DESCRIPTOR) + + # The database can be used as a MessageFactory, to generate types based on + # their name: + types = db.GetMessages(['my_proto.proto']) + my_message_instance = types['MyMessage']() + + # The database's underlying descriptor pool can be queried, so it's not + # necessary to know a type's filename to be able to generate it: + filename = db.pool.FindFileContainingSymbol('MyMessage') + my_message_instance = db.GetMessages([filename])['MyMessage']() + + # This functionality is also provided directly via a convenience method: + my_message_instance = db.GetSymbol('MyMessage')() +""" + +import warnings + +from google.protobuf import descriptor_pool +from google.protobuf import message_factory +from google.protobuf.internal import api_implementation + + +class SymbolDatabase: + """A database of Python generated symbols.""" + + # local cache of registered classes. + _classes = {} + + def __init__(self, pool=None): + """Initializes a new SymbolDatabase.""" + self.pool = pool or descriptor_pool.DescriptorPool() + + def RegisterMessage(self, message): + """Registers the given message type in the local database. + + Calls to GetSymbol() and GetMessages() will return messages registered here. + + Args: + message: A :class:`google.protobuf.message.Message` subclass (or + instance); its descriptor will be registered. + + Returns: + The provided message. + """ + + desc = message.DESCRIPTOR + self._classes[desc] = message + self.RegisterMessageDescriptor(desc) + return message + + def RegisterMessageDescriptor(self, message_descriptor): + """Registers the given message descriptor in the local database. + + Args: + message_descriptor (Descriptor): the message descriptor to add. + """ + if api_implementation.Type() == 'python': + # pylint: disable=protected-access + self.pool._AddDescriptor(message_descriptor) + + def RegisterEnumDescriptor(self, enum_descriptor): + """Registers the given enum descriptor in the local database. + + Args: + enum_descriptor (EnumDescriptor): The enum descriptor to register. + + Returns: + EnumDescriptor: The provided descriptor. + """ + if api_implementation.Type() == 'python': + # pylint: disable=protected-access + self.pool._AddEnumDescriptor(enum_descriptor) + return enum_descriptor + + def RegisterServiceDescriptor(self, service_descriptor): + """Registers the given service descriptor in the local database. + + Args: + service_descriptor (ServiceDescriptor): the service descriptor to + register. + """ + if api_implementation.Type() == 'python': + # pylint: disable=protected-access + self.pool._AddServiceDescriptor(service_descriptor) + + def RegisterFileDescriptor(self, file_descriptor): + """Registers the given file descriptor in the local database. + + Args: + file_descriptor (FileDescriptor): The file descriptor to register. + """ + if api_implementation.Type() == 'python': + # pylint: disable=protected-access + self.pool._InternalAddFileDescriptor(file_descriptor) + + def GetSymbol(self, symbol): + """Tries to find a symbol in the local database. + + Currently, this method only returns message.Message instances, however, if + may be extended in future to support other symbol types. + + Args: + symbol (str): a protocol buffer symbol. + + Returns: + A Python class corresponding to the symbol. + + Raises: + KeyError: if the symbol could not be found. + """ + + return self._classes[self.pool.FindMessageTypeByName(symbol)] + + def GetMessages(self, files): + # TODO: Fix the differences with MessageFactory. + """Gets all registered messages from a specified file. + + Only messages already created and registered will be returned; (this is the + case for imported _pb2 modules) + But unlike MessageFactory, this version also returns already defined nested + messages, but does not register any message extensions. + + Args: + files (list[str]): The file names to extract messages from. + + Returns: + A dictionary mapping proto names to the message classes. + + Raises: + KeyError: if a file could not be found. + """ + + def _GetAllMessages(desc): + """Walk a message Descriptor and recursively yields all message names.""" + yield desc + for msg_desc in desc.nested_types: + for nested_desc in _GetAllMessages(msg_desc): + yield nested_desc + + result = {} + for file_name in files: + file_desc = self.pool.FindFileByName(file_name) + for msg_desc in file_desc.message_types_by_name.values(): + for desc in _GetAllMessages(msg_desc): + try: + result[desc.full_name] = self._classes[desc] + except KeyError: + # This descriptor has no registered class, skip it. + pass + return result + + +_DEFAULT = SymbolDatabase(pool=descriptor_pool.Default()) + + +def Default(): + """Returns the default SymbolDatabase.""" + return _DEFAULT diff --git a/venv/lib/python3.12/site-packages/google/protobuf/testdata/__init__.py b/venv/lib/python3.12/site-packages/google/protobuf/testdata/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/google/protobuf/testdata/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/testdata/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..14191cf Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/testdata/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/text_encoding.py b/venv/lib/python3.12/site-packages/google/protobuf/text_encoding.py new file mode 100755 index 0000000..294a462 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/text_encoding.py @@ -0,0 +1,111 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Encoding related utilities.""" + +import re + + +def _AsciiIsPrint(i): + return i >= 32 and i < 127 + + +def _MakeStrEscapes(): + ret = {} + for i in range(0, 128): + if not _AsciiIsPrint(i): + ret[i] = r'\%03o' % i + ret[ord('\t')] = r'\t' # optional escape + ret[ord('\n')] = r'\n' # optional escape + ret[ord('\r')] = r'\r' # optional escape + ret[ord('"')] = r'\"' # necessary escape + ret[ord("'")] = r'\'' # optional escape + ret[ord('\\')] = r'\\' # necessary escape + return ret + + +# Maps int -> char, performing string escapes. +_str_escapes = _MakeStrEscapes() + +# Maps int -> char, performing byte escaping and string escapes +_byte_escapes = {i: chr(i) for i in range(0, 256)} +_byte_escapes.update(_str_escapes) +_byte_escapes.update({i: r'\%03o' % i for i in range(128, 256)}) + + +def _DecodeUtf8EscapeErrors(text_bytes): + ret = '' + while text_bytes: + try: + ret += text_bytes.decode('utf-8').translate(_str_escapes) + text_bytes = '' + except UnicodeDecodeError as e: + ret += text_bytes[: e.start].decode('utf-8').translate(_str_escapes) + ret += _byte_escapes[text_bytes[e.start]] + text_bytes = text_bytes[e.start + 1 :] + return ret + + +def CEscape(text, as_utf8) -> str: + """Escape a bytes string for use in an text protocol buffer. + + Args: + text: A byte string to be escaped. + as_utf8: Specifies if result may contain non-ASCII characters. In Python 3 + this allows unescaped non-ASCII Unicode characters. In Python 2 the return + value will be valid UTF-8 rather than only ASCII. + + Returns: + Escaped string (str). + """ + # Python's text.encode() 'string_escape' or 'unicode_escape' codecs do not + # satisfy our needs; they encodes unprintable characters using two-digit hex + # escapes whereas our C++ unescaping function allows hex escapes to be any + # length. So, "\0011".encode('string_escape') ends up being "\\x011", which + # will be decoded in C++ as a single-character string with char code 0x11. + text_is_unicode = isinstance(text, str) + if as_utf8: + if text_is_unicode: + return text.translate(_str_escapes) + else: + return _DecodeUtf8EscapeErrors(text) + else: + if text_is_unicode: + text = text.encode('utf-8') + return ''.join([_byte_escapes[c] for c in text]) + + +_CUNESCAPE_HEX = re.compile(r'(\\+)x([0-9a-fA-F])(?![0-9a-fA-F])') + + +def CUnescape(text: str) -> bytes: + """Unescape a text string with C-style escape sequences to UTF-8 bytes. + + Args: + text: The data to parse in a str. + + Returns: + A byte string. + """ + + def ReplaceHex(m): + # Only replace the match if the number of leading back slashes is odd. i.e. + # the slash itself is not escaped. + if len(m.group(1)) & 1: + return m.group(1) + 'x0' + m.group(2) + return m.group(0) + + # This is required because the 'string_escape' encoding doesn't + # allow single-digit hex escapes (like '\xf'). + result = _CUNESCAPE_HEX.sub(ReplaceHex, text) + + # Replaces Unicode escape sequences with their character equivalents. + result = result.encode('raw_unicode_escape').decode('raw_unicode_escape') + # Encode Unicode characters as UTF-8, then decode to Latin-1 escaping + # unprintable characters. + result = result.encode('utf-8').decode('unicode_escape') + # Convert Latin-1 text back to a byte string (latin-1 codec also works here). + return result.encode('latin-1') diff --git a/venv/lib/python3.12/site-packages/google/protobuf/text_format.py b/venv/lib/python3.12/site-packages/google/protobuf/text_format.py new file mode 100755 index 0000000..ad7f50c --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/text_format.py @@ -0,0 +1,2089 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Contains routines for printing messages in Protobuf Text Format. + +Printing and parsing messages in Text Format is useful for debugging +and human editing of messages. + +Unlike the Binary and ProtoJSON formats, Text Format is not designed to be +used as a wire format; instead it is intended for human-in-the-loop +configuration use-cases. + +Systems processing untrusted inputs should strongly prefer to use Binary format +instead. If a textual format of untrusted inputs is required, consider using +ProtoJSON format instead. + +Simple usage example:: + + # Create a proto object and serialize it to a text proto string. + message = my_proto_pb2.MyMessage(foo='bar') + text_proto = text_format.MessageToString(message) + + # Parse a text proto string. + message = text_format.Parse(text_proto, my_proto_pb2.MyMessage()) +""" + +__author__ = 'kenton@google.com (Kenton Varda)' + +# TODO Import thread contention leads to test failures. +import encodings.raw_unicode_escape # pylint: disable=unused-import +import encodings.unicode_escape # pylint: disable=unused-import +import io +import math +import re +import warnings + +from google.protobuf.internal import decoder +from google.protobuf.internal import type_checkers +from google.protobuf import descriptor + +from google.protobuf import text_encoding +from google.protobuf import unknown_fields + +# pylint: disable=g-import-not-at-top +__all__ = [ + 'MessageToString', + 'Parse', + 'PrintMessage', + 'PrintField', + 'PrintFieldValue', + 'Merge', + 'MessageToBytes', +] + +_INTEGER_CHECKERS = ( + type_checkers.Uint32ValueChecker(), + type_checkers.Int32ValueChecker(), + type_checkers.Uint64ValueChecker(), + type_checkers.Int64ValueChecker(), +) +_FLOAT_INFINITY = re.compile('-?inf(?:inity)?f?$', re.IGNORECASE) +_FLOAT_NAN = re.compile('nanf?$', re.IGNORECASE) +_FLOAT_OCTAL_PREFIX = re.compile('-?0[0-9]+') +_PERCENT_ENCODING = re.compile(r'^%[\da-fA-F][\da-fA-F]$') +_TYPE_NAME = re.compile(r'^[^\d\W]\w*(\.[^\d\W]\w*)*$') +_QUOTES = frozenset(("'", '"')) +_ANY_FULL_TYPE_NAME = 'google.protobuf.Any' +_DEBUG_STRING_SILENT_MARKER = '\t ' + +_as_utf8_default = True + + +class Error(Exception): + """Top-level module error for text_format.""" + + +class ParseError(Error): + """Thrown in case of text parsing or tokenizing error.""" + + def __init__(self, message=None, line=None, column=None): + if message is not None and line is not None: + loc = str(line) + if column is not None: + loc += ':{0}'.format(column) + message = '{0} : {1}'.format(loc, message) + if message is not None: + super(ParseError, self).__init__(message) + else: + super(ParseError, self).__init__() + self._line = line + self._column = column + + def GetLine(self): + return self._line + + def GetColumn(self): + return self._column + + +class TextWriter(object): + + def __init__(self, as_utf8): + self._writer = io.StringIO() + + def write(self, val): + return self._writer.write(val) + + def close(self): + return self._writer.close() + + def getvalue(self): + return self._writer.getvalue() + + +def MessageToString( + message, + as_utf8=_as_utf8_default, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + use_field_number=False, + descriptor_pool=None, + indent=0, + message_formatter=None, + print_unknown_fields=False, + force_colon=False, +) -> str: + """Convert protobuf message to text format. + + Args: + message: The protocol buffers message. + as_utf8: Return unescaped Unicode for non-ASCII characters. + as_one_line: Don't introduce newlines between fields. + use_short_repeated_primitives: Use short repeated format for primitives. + pointy_brackets: If True, use angle brackets instead of curly braces for + nesting. + use_index_order: If True, fields of a proto message will be printed using + the order defined in source code instead of the field number, extensions + will be printed at the end of the message and their relative order is + determined by the extension number. By default, use the field number + order. + use_field_number: If True, print field numbers instead of names. + descriptor_pool (DescriptorPool): Descriptor pool used to resolve Any types. + indent (int): The initial indent level, in terms of spaces, for pretty + print. + message_formatter (function(message, indent, as_one_line) -> unicode|None): + Custom formatter for selected sub-messages (usually based on message + type). Use to pretty print parts of the protobuf for easier diffing. + print_unknown_fields: If True, unknown fields will be printed. + force_colon: If set, a colon will be added after the field name even if the + field is a proto message. + + Returns: + str: A string of the text formatted protocol buffer message. + """ + out = TextWriter(as_utf8) + printer = _Printer( + out=out, + indent=indent, + as_utf8=as_utf8, + as_one_line=as_one_line, + use_short_repeated_primitives=use_short_repeated_primitives, + pointy_brackets=pointy_brackets, + use_index_order=use_index_order, + use_field_number=use_field_number, + descriptor_pool=descriptor_pool, + message_formatter=message_formatter, + print_unknown_fields=print_unknown_fields, + force_colon=force_colon, + ) + printer.PrintMessage(message) + result = out.getvalue() + out.close() + if as_one_line: + return result.rstrip() + return result + + +def MessageToBytes(message, **kwargs) -> bytes: + """Convert protobuf message to encoded text format. See MessageToString.""" + text = MessageToString(message, **kwargs) + if isinstance(text, bytes): + return text + codec = 'utf-8' if kwargs.get('as_utf8') else 'ascii' + return text.encode(codec) + + +def _IsMapEntry(field): + return ( + field.type == descriptor.FieldDescriptor.TYPE_MESSAGE + and field.message_type.has_options + and field.message_type.GetOptions().map_entry + ) + + +def _IsGroupLike(field): + """Determines if a field is consistent with a proto2 group. + + Args: + field: The field descriptor. + + Returns: + True if this field is group-like, false otherwise. + """ + # Groups are always tag-delimited. + if field.type != descriptor.FieldDescriptor.TYPE_GROUP: + return False + + # Group fields always are always the lowercase type name. + if field.name != field.message_type.name.lower(): + return False + + if field.message_type.file != field.file: + return False + + # Group messages are always defined in the same scope as the field. File + # level extensions will compare NULL == NULL here, which is why the file + # comparison above is necessary to ensure both come from the same file. + return ( + field.message_type.containing_type == field.extension_scope + if field.is_extension + else field.message_type.containing_type == field.containing_type + ) + + +def PrintMessage( + message, + out, + indent=0, + as_utf8=_as_utf8_default, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + use_field_number=False, + descriptor_pool=None, + message_formatter=None, + print_unknown_fields=False, + force_colon=False, +): + """Convert the message to text format and write it to the out stream. + + Args: + message: The Message object to convert to text format. + out: A file handle to write the message to. + indent: The initial indent level for pretty print. + as_utf8: Return unescaped Unicode for non-ASCII characters. + as_one_line: Don't introduce newlines between fields. + use_short_repeated_primitives: Use short repeated format for primitives. + pointy_brackets: If True, use angle brackets instead of curly braces for + nesting. + use_index_order: If True, print fields of a proto message using the order + defined in source code instead of the field number. By default, use the + field number order. + use_field_number: If True, print field numbers instead of names. + descriptor_pool: A DescriptorPool used to resolve Any types. + message_formatter: A function(message, indent, as_one_line): unicode|None to + custom format selected sub-messages (usually based on message type). Use + to pretty print parts of the protobuf for easier diffing. + print_unknown_fields: If True, unknown fields will be printed. + force_colon: If set, a colon will be added after the field name even if the + field is a proto message. + """ + printer = _Printer( + out=out, + indent=indent, + as_utf8=as_utf8, + as_one_line=as_one_line, + use_short_repeated_primitives=use_short_repeated_primitives, + pointy_brackets=pointy_brackets, + use_index_order=use_index_order, + use_field_number=use_field_number, + descriptor_pool=descriptor_pool, + message_formatter=message_formatter, + print_unknown_fields=print_unknown_fields, + force_colon=force_colon, + ) + printer.PrintMessage(message) + + +def PrintField( + field, + value, + out, + indent=0, + as_utf8=_as_utf8_default, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + message_formatter=None, + print_unknown_fields=False, + force_colon=False, +): + """Print a single field name/value pair.""" + printer = _Printer( + out, + indent, + as_utf8, + as_one_line, + use_short_repeated_primitives, + pointy_brackets, + use_index_order, + message_formatter=message_formatter, + print_unknown_fields=print_unknown_fields, + force_colon=force_colon, + ) + printer.PrintField(field, value) + + +def PrintFieldValue( + field, + value, + out, + indent=0, + as_utf8=_as_utf8_default, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + message_formatter=None, + print_unknown_fields=False, + force_colon=False, +): + """Print a single field value (not including name).""" + printer = _Printer( + out, + indent, + as_utf8, + as_one_line, + use_short_repeated_primitives, + pointy_brackets, + use_index_order, + message_formatter=message_formatter, + print_unknown_fields=print_unknown_fields, + force_colon=force_colon, + ) + printer.PrintFieldValue(field, value) + + +def _BuildMessageFromTypeName(type_name, descriptor_pool): + """Returns a protobuf message instance. + + Args: + type_name: Fully-qualified protobuf message type name string. + descriptor_pool: DescriptorPool instance. + + Returns: + A Message instance of type matching type_name, or None if the a Descriptor + wasn't found matching type_name. + """ + # pylint: disable=g-import-not-at-top + if descriptor_pool is None: + from google.protobuf import descriptor_pool as pool_mod + + descriptor_pool = pool_mod.Default() + from google.protobuf import message_factory + + try: + message_descriptor = descriptor_pool.FindMessageTypeByName(type_name) + except KeyError: + return None + message_type = message_factory.GetMessageClass(message_descriptor) + return message_type() + + +# These values must match WireType enum in //google/protobuf/wire_format.h. +WIRETYPE_LENGTH_DELIMITED = 2 +WIRETYPE_START_GROUP = 3 + + +class _Printer(object): + """Text format printer for protocol message.""" + + def __init__( + self, + out, + indent=0, + as_utf8=_as_utf8_default, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + use_field_number=False, + descriptor_pool=None, + message_formatter=None, + print_unknown_fields=False, + force_colon=False, + ): + """Initialize the Printer. + + Args: + out: To record the text format result. + indent: The initial indent level for pretty print. + as_utf8: Return unescaped Unicode for non-ASCII characters. + as_one_line: Don't introduce newlines between fields. + use_short_repeated_primitives: Use short repeated format for primitives. + pointy_brackets: If True, use angle brackets instead of curly braces for + nesting. + use_index_order: If True, print fields of a proto message using the order + defined in source code instead of the field number. By default, use the + field number order. + use_field_number: If True, print field numbers instead of names. + descriptor_pool: A DescriptorPool used to resolve Any types. + message_formatter: A function(message, indent, as_one_line): unicode|None + to custom format selected sub-messages (usually based on message type). + Use to pretty print parts of the protobuf for easier diffing. + print_unknown_fields: If True, unknown fields will be printed. + force_colon: If set, a colon will be added after the field name even if + the field is a proto message. + """ + self.out = out + self.indent = indent + self.as_utf8 = as_utf8 + self.as_one_line = as_one_line + self.use_short_repeated_primitives = use_short_repeated_primitives + self.pointy_brackets = pointy_brackets + self.use_index_order = use_index_order + self.use_field_number = use_field_number + self.descriptor_pool = descriptor_pool + self.message_formatter = message_formatter + self.print_unknown_fields = print_unknown_fields + self.force_colon = force_colon + + def _TryPrintAsAnyMessage(self, message): + """Serializes if message is a google.protobuf.Any field.""" + if '/' not in message.type_url: + return False + packed_message = _BuildMessageFromTypeName( + message.TypeName(), self.descriptor_pool + ) + if packed_message is not None: + packed_message.MergeFromString(message.value) + colon = ':' if self.force_colon else '' + self.out.write('%s[%s]%s ' % (self.indent * ' ', message.type_url, colon)) + self._PrintMessageFieldValue(packed_message) + self.out.write(' ' if self.as_one_line else '\n') + return True + else: + return False + + def _TryCustomFormatMessage(self, message): + formatted = self.message_formatter(message, self.indent, self.as_one_line) + if formatted is None: + return False + + out = self.out + out.write(' ' * self.indent) + out.write(formatted) + out.write(' ' if self.as_one_line else '\n') + return True + + def PrintMessage(self, message): + """Convert protobuf message to text format. + + Args: + message: The protocol buffers message. + """ + if self.message_formatter and self._TryCustomFormatMessage(message): + return + if ( + message.DESCRIPTOR.full_name == _ANY_FULL_TYPE_NAME + and self._TryPrintAsAnyMessage(message) + ): + return + fields = message.ListFields() + if self.use_index_order: + fields.sort( + key=lambda x: x[0].number if x[0].is_extension else x[0].index + ) + for field, value in fields: + if _IsMapEntry(field): + for key in sorted(value): + # This is slow for maps with submessage entries because it copies the + # entire tree. Unfortunately this would take significant refactoring + # of this file to work around. + # + # TODO: refactor and optimize if this becomes an issue. + entry_submsg = value.GetEntryClass()(key=key, value=value[key]) + self.PrintField(field, entry_submsg) + elif field.is_repeated: + if ( + self.use_short_repeated_primitives + and field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_MESSAGE + and field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_STRING + ): + self._PrintShortRepeatedPrimitivesValue(field, value) + else: + for element in value: + self.PrintField(field, element) + else: + self.PrintField(field, value) + + if self.print_unknown_fields: + self._PrintUnknownFields(unknown_fields.UnknownFieldSet(message)) + + def _PrintUnknownFields(self, unknown_field_set): + """Print unknown fields.""" + out = self.out + for field in unknown_field_set: + out.write(' ' * self.indent) + out.write(str(field.field_number)) + if field.wire_type == WIRETYPE_START_GROUP: + if self.as_one_line: + out.write(' { ') + else: + out.write(' {\n') + self.indent += 2 + + self._PrintUnknownFields(field.data) + + if self.as_one_line: + out.write('} ') + else: + self.indent -= 2 + out.write(' ' * self.indent + '}\n') + elif field.wire_type == WIRETYPE_LENGTH_DELIMITED: + try: + # If this field is parseable as a Message, it is probably + # an embedded message. + # pylint: disable=protected-access + embedded_unknown_message, pos = decoder._DecodeUnknownFieldSet( + memoryview(field.data), 0, len(field.data) + ) + except Exception: # pylint: disable=broad-except + pos = 0 + + if pos == len(field.data): + if self.as_one_line: + out.write(' { ') + else: + out.write(' {\n') + self.indent += 2 + + self._PrintUnknownFields(embedded_unknown_message) + + if self.as_one_line: + out.write('} ') + else: + self.indent -= 2 + out.write(' ' * self.indent + '}\n') + else: + # A string or bytes field. self.as_utf8 may not work. + out.write(': "') + out.write(text_encoding.CEscape(field.data, False)) + out.write('" ' if self.as_one_line else '"\n') + else: + # varint, fixed32, fixed64 + out.write(': ') + out.write(str(field.data)) + out.write(' ' if self.as_one_line else '\n') + + def _PrintFieldName(self, field): + """Print field name.""" + out = self.out + out.write(' ' * self.indent) + if self.use_field_number: + out.write(str(field.number)) + else: + if field.is_extension: + out.write('[') + if ( + field.containing_type.GetOptions().message_set_wire_format + and field.type == descriptor.FieldDescriptor.TYPE_MESSAGE + and not field.is_required + and not field.is_repeated + ): + out.write(field.message_type.full_name) + else: + out.write(field.full_name) + out.write(']') + elif _IsGroupLike(field): + # For groups, use the capitalized name. + out.write(field.message_type.name) + else: + out.write(field.name) + + if ( + self.force_colon + or field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_MESSAGE + ): + # The colon is optional in this case, but our cross-language golden files + # don't include it. Here, the colon is only included if force_colon is + # set to True + out.write(':') + + def PrintField(self, field, value): + """Print a single field name/value pair.""" + self._PrintFieldName(field) + self.out.write(' ') + self.PrintFieldValue(field, value) + self.out.write(' ' if self.as_one_line else '\n') + + def _PrintShortRepeatedPrimitivesValue(self, field, value): + """ "Prints short repeated primitives value.""" + # Note: this is called only when value has at least one element. + self._PrintFieldName(field) + self.out.write(' [') + for i in range(len(value) - 1): + self.PrintFieldValue(field, value[i]) + self.out.write(', ') + self.PrintFieldValue(field, value[-1]) + self.out.write(']') + self.out.write(' ' if self.as_one_line else '\n') + + def _PrintMessageFieldValue(self, value): + if self.pointy_brackets: + openb = '<' + closeb = '>' + else: + openb = '{' + closeb = '}' + + if self.as_one_line: + self.out.write('%s ' % openb) + self.PrintMessage(value) + self.out.write(closeb) + else: + self.out.write('%s\n' % openb) + self.indent += 2 + self.PrintMessage(value) + self.indent -= 2 + self.out.write(' ' * self.indent + closeb) + + def PrintFieldValue(self, field, value): + """Print a single field value (not including name). + + For repeated fields, the value should be a single element. + + Args: + field: The descriptor of the field to be printed. + value: The value of the field. + """ + out = self.out + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + self._PrintMessageFieldValue(value) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: + enum_value = field.enum_type.values_by_number.get(value, None) + if enum_value is not None: + out.write(enum_value.name) + else: + out.write(str(value)) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: + out.write('"') + if isinstance(value, str) and not self.as_utf8: + out_value = value.encode('utf-8') + else: + out_value = value + if field.type == descriptor.FieldDescriptor.TYPE_BYTES: + # We always need to escape all binary data in TYPE_BYTES fields. + out_as_utf8 = False + else: + out_as_utf8 = self.as_utf8 + out.write(text_encoding.CEscape(out_value, out_as_utf8)) + out.write('"') + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: + if value: + out.write('true') + else: + out.write('false') + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: + if math.isnan(value): + out.write(str(value)) + else: + out.write(str(type_checkers.ToShortestFloat(value))) + else: + out.write(str(value)) + + +def Parse( + text, + message, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False, + max_recursion_depth=None, +): + """Parses a text representation of a protocol message into a message. + + NOTE: for historical reasons this function does not clear the input + message. This is different from what the binary msg.ParseFrom(...) does. + If text contains a field already set in message, the value is appended if the + field is repeated. Otherwise, an error is raised. + + Example:: + + a = MyProto() + a.repeated_field.append('test') + b = MyProto() + + # Repeated fields are combined + text_format.Parse(repr(a), b) + text_format.Parse(repr(a), b) # repeated_field contains ["test", "test"] + + # Non-repeated fields cannot be overwritten + a.singular_field = 1 + b.singular_field = 2 + text_format.Parse(repr(a), b) # ParseError + + # Binary version: + b.ParseFromString(a.SerializeToString()) # repeated_field is now "test" + + Caller is responsible for clearing the message as needed. + + Args: + text (str): Message text representation. + message (Message): A protocol buffer message to merge into. + allow_unknown_extension: if True, skip over missing extensions and keep + parsing + allow_field_number: if True, both field number and field name are allowed. + descriptor_pool (DescriptorPool): Descriptor pool used to resolve Any types. + allow_unknown_field: if True, skip over unknown field and keep parsing. + Avoid to use this option if possible. It may hide some errors (e.g. + spelling error on field name) + max_recursion_depth: Optional maximum recursion depth of the message to be + parsed: Text Format inputs over this depth will fail to parse. ``None`` + means no additional limit (the Python runtime will enforce some limit + due to call stack limits). As Text Format is primarily intended to be + used on trusted configuration inputs, and to maintain backwards + compatibility, the default of ``None`` (unbounded) is intentional. For + better consistency with what messages will successfully round trip + through binary wire format, or for the discouraged case of processing + untrusted Text Format inputs, setting a limit of 100 is recommended. + + Returns: + Message: The same message passed as argument. + + Raises: + ParseError: On text parsing problems. + """ + return ParseLines( + text.split(b'\n' if isinstance(text, bytes) else '\n'), + message, + allow_unknown_extension, + allow_field_number, + descriptor_pool=descriptor_pool, + allow_unknown_field=allow_unknown_field, + max_recursion_depth=max_recursion_depth, + ) + + +def Merge( + text, + message, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False, + max_recursion_depth=None, +): + """Parses a text representation of a protocol message into a message. + + Like Parse(), but allows repeated values for a non-repeated field, and uses + the last one. This means any non-repeated, top-level fields specified in text + replace those in the message. + + Args: + text (str): Message text representation. + message (Message): A protocol buffer message to merge into. + allow_unknown_extension: if True, skip over missing extensions and keep + parsing + allow_field_number: if True, both field number and field name are allowed. + descriptor_pool (DescriptorPool): Descriptor pool used to resolve Any types. + allow_unknown_field: if True, skip over unknown field and keep parsing. + Avoid to use this option if possible. It may hide some errors (e.g. + spelling error on field name) + max_recursion_depth: Optional maximum recursion depth of the message to be + parsed: Text Format inputs over this depth will fail to parse. ``None`` + means no additional limit (the Python runtime will enforce some limit + due to call stack limits). As Text Format is primarily intended to be + used on trusted configuration inputs, and to maintain backwards + compatibility, the default of ``None`` (unbounded) is intentional. For + better consistency with what messages will successfully round trip + through binary wire format, or for the discouraged case of processing + untrusted Text Format inputs, setting a limit of 100 is recommended. + + Returns: + Message: The same message passed as argument. + + Raises: + ParseError: On text parsing problems. + """ + return MergeLines( + text.split(b'\n' if isinstance(text, bytes) else '\n'), + message, + allow_unknown_extension, + allow_field_number, + descriptor_pool=descriptor_pool, + allow_unknown_field=allow_unknown_field, + max_recursion_depth=max_recursion_depth, + ) + + +def ParseLines( + lines, + message, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False, + max_recursion_depth=None, +): + """Parses a text representation of a protocol message into a message. + + See Parse() for caveats. + + Args: + lines: An iterable of lines of a message's text representation. + message: A protocol buffer message to merge into. + allow_unknown_extension: if True, skip over missing extensions and keep + parsing + allow_field_number: if True, both field number and field name are allowed. + descriptor_pool: A DescriptorPool used to resolve Any types. + allow_unknown_field: if True, skip over unknown field and keep parsing. + Avoid to use this option if possible. It may hide some errors (e.g. + spelling error on field name) + max_recursion_depth: Optional maximum recursion depth of the message to be + parsed: Text Format inputs over this depth will fail to parse. ``None`` + means no additional limit (the Python runtime will enforce some limit + due to call stack limits). As Text Format is primarily intended to be + used on trusted configuration inputs, and to maintain backwards + compatibility, the default of ``None`` (unbounded) is intentional. For + better consistency with what messages will successfully round trip + through binary wire format, or for the discouraged case of processing + untrusted Text Format inputs, setting a limit of 100 is recommended. + + Returns: + The same message passed as argument. + + Raises: + ParseError: On text parsing problems. + """ + parser = _Parser( + allow_unknown_extension, + allow_field_number, + descriptor_pool=descriptor_pool, + allow_unknown_field=allow_unknown_field, + max_recursion_depth=max_recursion_depth, + ) + return parser.ParseLines(lines, message) + + +def MergeLines( + lines, + message, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False, + max_recursion_depth=None, +): + """Parses a text representation of a protocol message into a message. + + See Merge() for more details. + + Args: + lines: An iterable of lines of a message's text representation. + message: A protocol buffer message to merge into. + allow_unknown_extension: if True, skip over missing extensions and keep + parsing + allow_field_number: if True, both field number and field name are allowed. + descriptor_pool: A DescriptorPool used to resolve Any types. + allow_unknown_field: if True, skip over unknown field and keep parsing. + Avoid to use this option if possible. It may hide some errors (e.g. + spelling error on field name) + max_recursion_depth: Optional maximum recursion depth of the message to be + parsed: Text Format inputs over this depth will fail to parse. ``None`` + means no additional limit (the Python runtime will enforce some limit + due to call stack limits). As Text Format is primarily intended to be + used on trusted configuration inputs, and to maintain backwards + compatibility, the default of ``None`` (unbounded) is intentional. For + better consistency with what messages will successfully round trip + through binary wire format, or for the discouraged case of processing + untrusted Text Format inputs, setting a limit of 100 is recommended. + + Returns: + The same message passed as argument. + + Raises: + ParseError: On text parsing problems. + """ + parser = _Parser( + allow_unknown_extension, + allow_field_number, + descriptor_pool=descriptor_pool, + allow_unknown_field=allow_unknown_field, + max_recursion_depth=max_recursion_depth, + ) + return parser.MergeLines(lines, message) + + +class _Parser(object): + """Text format parser for protocol message.""" + + def __init__( + self, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False, + max_recursion_depth=None, + ): + self.allow_unknown_extension = allow_unknown_extension + self.allow_field_number = allow_field_number + self.descriptor_pool = descriptor_pool + self.allow_unknown_field = allow_unknown_field + self.max_recursion_depth = max_recursion_depth + self.recursion_depth = 0 + + def ParseLines(self, lines, message): + """Parses a text representation of a protocol message into a message.""" + self._allow_multiple_scalars = False + self._ParseOrMerge(lines, message) + return message + + def MergeLines(self, lines, message): + """Merges a text representation of a protocol message into a message.""" + self._allow_multiple_scalars = True + self._ParseOrMerge(lines, message) + return message + + def _ParseOrMerge(self, lines, message): + """Converts a text representation of a protocol message into a message. + + Args: + lines: Lines of a message's text representation. + message: A protocol buffer message to merge into. + + Raises: + ParseError: On text parsing problems. + """ + # Tokenize expects native str lines. + try: + str_lines = ( + line if isinstance(line, str) else line.decode('utf-8') + for line in lines + ) + tokenizer = Tokenizer(str_lines) + except UnicodeDecodeError as e: + raise ParseError from e + if message: + self.root_type = message.DESCRIPTOR.full_name + self.recursion_depth += 1 + if ( + self.max_recursion_depth is not None + and self.recursion_depth > self.max_recursion_depth + ): + raise ParseError( + 'Message too deep. Max recursion depth is {0}'.format( + self.max_recursion_depth + ) + ) + while not tokenizer.AtEnd(): + self._MergeField(tokenizer, message) + self.recursion_depth -= 1 + + def _MergeMessage(self, tokenizer, message, end_token): + self.recursion_depth += 1 + if ( + self.max_recursion_depth is not None + and self.recursion_depth > self.max_recursion_depth + ): + raise ParseError( + 'Message too deep. Max recursion depth is {0}'.format( + self.max_recursion_depth + ) + ) + while not tokenizer.TryConsume(end_token): + if tokenizer.AtEnd(): + raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % (end_token,)) + self._MergeField(tokenizer, message) + self.recursion_depth -= 1 + + def _MergeField(self, tokenizer, message): + """Merges a single protocol message field into a message. + + Args: + tokenizer: A tokenizer to parse the field name and values. + message: A protocol message to record the data. + + Raises: + ParseError: In case of text parsing problems. + """ + message_descriptor = message.DESCRIPTOR + if ( + message_descriptor.full_name == _ANY_FULL_TYPE_NAME + and tokenizer.TryConsume('[') + ): + type_url_prefix, packed_type_name = self._ConsumeAnyTypeUrl(tokenizer) + tokenizer.TryConsume(':') + self._DetectSilentMarker( + tokenizer, + message_descriptor.full_name, + type_url_prefix + '/' + packed_type_name, + ) + if tokenizer.TryConsume('<'): + expanded_any_end_token = '>' + else: + tokenizer.Consume('{') + expanded_any_end_token = '}' + expanded_any_sub_message = _BuildMessageFromTypeName( + packed_type_name, self.descriptor_pool + ) + # Direct comparison with None is used instead of implicit bool conversion + # to avoid false positives with falsy initial values, e.g. for + # google.protobuf.ListValue. + if expanded_any_sub_message is None: + raise ParseError( + 'Type %s not found in descriptor pool' % packed_type_name + ) + self._MergeMessage( + tokenizer, expanded_any_sub_message, expanded_any_end_token + ) + deterministic = False + + message.Pack( + expanded_any_sub_message, + type_url_prefix=type_url_prefix + '/', + deterministic=deterministic, + ) + return + + if tokenizer.TryConsume('['): + name = [tokenizer.ConsumeIdentifier()] + while tokenizer.TryConsume('.'): + name.append(tokenizer.ConsumeIdentifier()) + name = '.'.join(name) + + if not message_descriptor.is_extendable: + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" does not have extensions.' + % message_descriptor.full_name + ) + # pylint: disable=protected-access + field = message.Extensions._FindExtensionByName(name) + # pylint: enable=protected-access + if not field: + if self.allow_unknown_extension: + field = None + else: + raise tokenizer.ParseErrorPreviousToken( + 'Extension "%s" not registered. ' + 'Did you import the _pb2 module which defines it? ' + 'If you are trying to place the extension in the MessageSet ' + 'field of another message that is in an Any or MessageSet field, ' + "that message's _pb2 module must be imported as well" % name + ) + elif message_descriptor != field.containing_type: + raise tokenizer.ParseErrorPreviousToken( + 'Extension "%s" does not extend message type "%s".' + % (name, message_descriptor.full_name) + ) + + tokenizer.Consume(']') + + else: + name = tokenizer.ConsumeIdentifierOrNumber() + if self.allow_field_number and name.isdigit(): + number = ParseInteger(name, True, True) + field = message_descriptor.fields_by_number.get(number, None) + if not field and message_descriptor.is_extendable: + field = message.Extensions._FindExtensionByNumber(number) + else: + field = message_descriptor.fields_by_name.get(name, None) + + # Group names are expected to be capitalized as they appear in the + # .proto file, which actually matches their type names, not their field + # names. + if not field: + field = message_descriptor.fields_by_name.get(name.lower(), None) + if field and not _IsGroupLike(field): + field = None + if field and field.message_type.name != name: + field = None + + if not field and not self.allow_unknown_field: + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" has no field named "%s".' + % (message_descriptor.full_name, name) + ) + + if field: + if not self._allow_multiple_scalars and field.containing_oneof: + # Check if there's a different field set in this oneof. + # Note that we ignore the case if the same field was set before, and we + # apply _allow_multiple_scalars to non-scalar fields as well. + which_oneof = message.WhichOneof(field.containing_oneof.name) + if which_oneof is not None and which_oneof != field.name: + raise tokenizer.ParseErrorPreviousToken( + 'Field "%s" is specified along with field "%s", another member ' + 'of oneof "%s" for message type "%s".' + % ( + field.name, + which_oneof, + field.containing_oneof.name, + message_descriptor.full_name, + ) + ) + + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + tokenizer.TryConsume(':') + self._DetectSilentMarker( + tokenizer, message_descriptor.full_name, field.full_name + ) + merger = self._MergeMessageField + else: + tokenizer.Consume(':') + self._DetectSilentMarker( + tokenizer, message_descriptor.full_name, field.full_name + ) + merger = self._MergeScalarField + + if field.is_repeated and tokenizer.TryConsume('['): + # Short repeated format, e.g. "foo: [1, 2, 3]" + if not tokenizer.TryConsume(']'): + while True: + merger(tokenizer, message, field) + if tokenizer.TryConsume(']'): + break + tokenizer.Consume(',') + + else: + merger(tokenizer, message, field) + + else: # Proto field is unknown. + assert self.allow_unknown_extension or self.allow_unknown_field + self._SkipFieldContents(tokenizer, name, message_descriptor.full_name) + + # For historical reasons, fields may optionally be separated by commas or + # semicolons. + if not tokenizer.TryConsume(','): + tokenizer.TryConsume(';') + + def _LogSilentMarker(self, immediate_message_type, field_name): + pass + + def _DetectSilentMarker(self, tokenizer, immediate_message_type, field_name): + if tokenizer.contains_silent_marker_before_current_token: + self._LogSilentMarker(immediate_message_type, field_name) + + def _ConsumeAnyTypeUrl(self, tokenizer): + """Consumes a google.protobuf.Any type URL. + + Assumes the caller has already consumed the opening [ and consumes up to the + closing ]. + + Args: + tokenizer: A tokenizer to parse the type URL. + + Returns: + A tuple of type URL prefix (without trailing slash) and type name. + """ + # Consume all tokens with valid URL characters until ]. Whitespace and + # comments are ignored/skipped by the Tokenizer. + tokens = [] + last_slash = -1 + while True: + try: + tokens.append(tokenizer.ConsumeUrlChars()) + continue + except ParseError: + pass + if tokenizer.TryConsume('/'): + last_slash = len(tokens) + tokens.append('/') + else: + tokenizer.Consume(']') + break + + if last_slash == -1: + raise tokenizer.ParseError('Type URL does not contain "/".') + + prefix = ''.join(tokens[:last_slash]) + name = ''.join(tokens[last_slash + 1 :]) + + if not prefix: + raise tokenizer.ParseError('Type URL prefix is empty.') + if prefix.startswith('/'): + raise tokenizer.ParseError('Type URL prefix starts with "/".') + + # Check for invalid percent encodings. '%' needs to be followed by exactly + # two valid hexadecimal digits. + for i, char in enumerate(prefix): + if char == '%' and not _PERCENT_ENCODING.match(prefix[i : i + 3]): + raise tokenizer.ParseError( + f'Invalid percent escape, got "{prefix[i : i + 3]}".' + ) + + # After the last slash we expect a valid type name, not just any sequence of + # URL characters. + if not _TYPE_NAME.match(name): + raise tokenizer.ParseError('Expected type name, got "%s".' % name) + + return prefix, name + + def _MergeMessageField(self, tokenizer, message, field): + """Merges a single scalar field into a message. + + Args: + tokenizer: A tokenizer to parse the field value. + message: The message of which field is a member. + field: The descriptor of the field to be merged. + + Raises: + ParseError: In case of text parsing problems. + """ + is_map_entry = _IsMapEntry(field) + + if tokenizer.TryConsume('<'): + end_token = '>' + else: + tokenizer.Consume('{') + end_token = '}' + + if field.is_repeated: + if field.is_extension: + sub_message = message.Extensions[field].add() + elif is_map_entry: + sub_message = getattr(message, field.name).GetEntryClass()() + else: + sub_message = getattr(message, field.name).add() + else: + if field.is_extension: + if not self._allow_multiple_scalars and message.HasExtension(field): + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" extensions.' + % (message.DESCRIPTOR.full_name, field.full_name) + ) + sub_message = message.Extensions[field] + else: + # Also apply _allow_multiple_scalars to message field. + # TODO: Change to _allow_singular_overwrites. + if not self._allow_multiple_scalars and message.HasField(field.name): + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" fields.' + % (message.DESCRIPTOR.full_name, field.name) + ) + sub_message = getattr(message, field.name) + sub_message.SetInParent() + + self._MergeMessage(tokenizer, sub_message, end_token) + + if is_map_entry: + value_cpptype = field.message_type.fields_by_name['value'].cpp_type + if value_cpptype == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + value = getattr(message, field.name)[sub_message.key] + value.CopyFrom(sub_message.value) + else: + getattr(message, field.name)[sub_message.key] = sub_message.value + + def _MergeScalarField(self, tokenizer, message, field): + """Merges a single scalar field into a message. + + Args: + tokenizer: A tokenizer to parse the field value. + message: A protocol message to record the data. + field: The descriptor of the field to be merged. + + Raises: + ParseError: In case of text parsing problems. + RuntimeError: On runtime errors. + """ + _ = self.allow_unknown_extension + value = None + + if field.type in ( + descriptor.FieldDescriptor.TYPE_INT32, + descriptor.FieldDescriptor.TYPE_SINT32, + descriptor.FieldDescriptor.TYPE_SFIXED32, + ): + value = _ConsumeInt32(tokenizer) + elif field.type in ( + descriptor.FieldDescriptor.TYPE_INT64, + descriptor.FieldDescriptor.TYPE_SINT64, + descriptor.FieldDescriptor.TYPE_SFIXED64, + ): + value = _ConsumeInt64(tokenizer) + elif field.type in ( + descriptor.FieldDescriptor.TYPE_UINT32, + descriptor.FieldDescriptor.TYPE_FIXED32, + ): + value = _ConsumeUint32(tokenizer) + elif field.type in ( + descriptor.FieldDescriptor.TYPE_UINT64, + descriptor.FieldDescriptor.TYPE_FIXED64, + ): + value = _ConsumeUint64(tokenizer) + elif field.type in ( + descriptor.FieldDescriptor.TYPE_FLOAT, + descriptor.FieldDescriptor.TYPE_DOUBLE, + ): + value = tokenizer.ConsumeFloat() + elif field.type == descriptor.FieldDescriptor.TYPE_BOOL: + value = tokenizer.ConsumeBool() + elif field.type == descriptor.FieldDescriptor.TYPE_STRING: + value = tokenizer.ConsumeString() + elif field.type == descriptor.FieldDescriptor.TYPE_BYTES: + value = tokenizer.ConsumeByteString() + elif field.type == descriptor.FieldDescriptor.TYPE_ENUM: + value = tokenizer.ConsumeEnum(field) + else: + raise RuntimeError('Unknown field type %d' % field.type) + + if field.is_repeated: + if field.is_extension: + message.Extensions[field].append(value) + else: + getattr(message, field.name).append(value) + else: + if field.is_extension: + if ( + not self._allow_multiple_scalars + and field.has_presence + and message.HasExtension(field) + ): + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" extensions.' + % (message.DESCRIPTOR.full_name, field.full_name) + ) + else: + message.Extensions[field] = value + else: + duplicate_error = False + if not self._allow_multiple_scalars: + if field.has_presence: + duplicate_error = message.HasField(field.name) + else: + # For field that doesn't represent presence, try best effort to + # check multiple scalars by compare to default values. + duplicate_error = not decoder.IsDefaultScalarValue( + getattr(message, field.name) + ) + + if duplicate_error: + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" fields.' + % (message.DESCRIPTOR.full_name, field.name) + ) + else: + setattr(message, field.name, value) + + def _SkipFieldContents(self, tokenizer, field_name, immediate_message_type): + """Skips over contents (value or message) of a field. + + Args: + tokenizer: A tokenizer to parse the field name and values. + field_name: The field name currently being parsed. + immediate_message_type: The type of the message immediately containing the + silent marker. + """ + # Try to guess the type of this field. + # If this field is not a message, there should be a ":" between the + # field name and the field value and also the field value should not + # start with "{" or "<" which indicates the beginning of a message body. + # If there is no ":" or there is a "{" or "<" after ":", this field has + # to be a message or the input is ill-formed. + if ( + tokenizer.TryConsume(':') + and not tokenizer.LookingAt('{') + and not tokenizer.LookingAt('<') + ): + self._DetectSilentMarker(tokenizer, immediate_message_type, field_name) + if tokenizer.LookingAt('['): + self._SkipRepeatedFieldValue(tokenizer, immediate_message_type) + else: + self._SkipFieldValue(tokenizer) + else: + self._DetectSilentMarker(tokenizer, immediate_message_type, field_name) + self._SkipFieldMessage(tokenizer, immediate_message_type) + + def _SkipField(self, tokenizer, immediate_message_type): + """Skips over a complete field (name and value/message). + + Args: + tokenizer: A tokenizer to parse the field name and values. + immediate_message_type: The type of the message immediately containing the + silent marker. + """ + field_name = '' + if tokenizer.TryConsume('['): + # Consume extension or google.protobuf.Any type URL + field_name += '[' + tokenizer.ConsumeIdentifier() + num_identifiers = 1 + while tokenizer.TryConsume('.'): + field_name += '.' + tokenizer.ConsumeIdentifier() + num_identifiers += 1 + # This is possibly a type URL for an Any message. + if num_identifiers == 3 and tokenizer.TryConsume('/'): + field_name += '/' + tokenizer.ConsumeIdentifier() + while tokenizer.TryConsume('.'): + field_name += '.' + tokenizer.ConsumeIdentifier() + tokenizer.Consume(']') + field_name += ']' + else: + field_name += tokenizer.ConsumeIdentifierOrNumber() + + self._SkipFieldContents(tokenizer, field_name, immediate_message_type) + + # For historical reasons, fields may optionally be separated by commas or + # semicolons. + if not tokenizer.TryConsume(','): + tokenizer.TryConsume(';') + + def _SkipFieldMessage(self, tokenizer, immediate_message_type): + """Skips over a field message. + + Args: + tokenizer: A tokenizer to parse the field name and values. + immediate_message_type: The type of the message immediately containing the + silent marker + """ + if tokenizer.TryConsume('<'): + delimiter = '>' + else: + tokenizer.Consume('{') + delimiter = '}' + + while not tokenizer.LookingAt('>') and not tokenizer.LookingAt('}'): + self._SkipField(tokenizer, immediate_message_type) + + tokenizer.Consume(delimiter) + + def _SkipFieldValue(self, tokenizer): + """Skips over a field value. + + Args: + tokenizer: A tokenizer to parse the field name and values. + + Raises: + ParseError: In case an invalid field value is found. + """ + if ( + not tokenizer.TryConsumeByteString() + and not tokenizer.TryConsumeIdentifier() + and not _TryConsumeInt64(tokenizer) + and not _TryConsumeUint64(tokenizer) + and not tokenizer.TryConsumeFloat() + ): + raise ParseError('Invalid field value: ' + tokenizer.token) + + def _SkipRepeatedFieldValue(self, tokenizer, immediate_message_type): + """Skips over a repeated field value. + + Args: + tokenizer: A tokenizer to parse the field value. + """ + tokenizer.Consume('[') + if not tokenizer.TryConsume(']'): + while True: + if tokenizer.LookingAt('<') or tokenizer.LookingAt('{'): + self._SkipFieldMessage(tokenizer, immediate_message_type) + else: + self._SkipFieldValue(tokenizer) + if tokenizer.TryConsume(']'): + break + tokenizer.Consume(',') + + +class Tokenizer(object): + """Protocol buffer text representation tokenizer. + + This class handles the lower level string parsing by splitting it into + meaningful tokens. + + It was directly ported from the Java protocol buffer API. + """ + + _WHITESPACE = re.compile(r'\s+') + _COMMENT = re.compile(r'(\s*#.*$)', re.MULTILINE) + _WHITESPACE_OR_COMMENT = re.compile(r'(\s|(#.*$))+', re.MULTILINE) + _TOKEN = re.compile( + '|'.join( + [ + r'[a-zA-Z_][0-9a-zA-Z_+-]*', # an identifier + r'([0-9+-]|(\.[0-9]))[0-9a-zA-Z_.+-]*', # a number + ] + + [ # quoted str for each quote mark + # Avoid backtracking! https://stackoverflow.com/a/844267 + r'{qt}[^{qt}\n\\]*((\\.)+[^{qt}\n\\]*)*({qt}|\\?$)'.format( + qt=mark + ) + for mark in _QUOTES + ] + ) + ) + + _IDENTIFIER = re.compile(r'[^\d\W]\w*') + _IDENTIFIER_OR_NUMBER = re.compile(r'\w+') + # Accepted URL characters (excluding "/") + _URL_CHARS = re.compile(r'^[0-9a-zA-Z-.~_ !$&()*+,;=%]+$') + + def __init__(self, lines, skip_comments=True): + self._position = 0 + self._line = -1 + self._column = 0 + self._token_start = None + self.token = '' + self._lines = iter(lines) + self._current_line = '' + self._previous_line = 0 + self._previous_column = 0 + self._more_lines = True + self._skip_comments = skip_comments + self._whitespace_pattern = ( + skip_comments and self._WHITESPACE_OR_COMMENT or self._WHITESPACE + ) + self.contains_silent_marker_before_current_token = False + + self._SkipWhitespace() + self.NextToken() + + def LookingAt(self, token): + return self.token == token + + def AtEnd(self): + """Checks the end of the text was reached. + + Returns: + True iff the end was reached. + """ + return not self.token + + def _PopLine(self): + while len(self._current_line) <= self._column: + try: + self._current_line = next(self._lines) + except StopIteration: + self._current_line = '' + self._more_lines = False + return + else: + self._line += 1 + self._column = 0 + + def _SkipWhitespace(self): + while True: + self._PopLine() + match = self._whitespace_pattern.match(self._current_line, self._column) + if not match: + break + self.contains_silent_marker_before_current_token = match.group(0) == ( + ' ' + _DEBUG_STRING_SILENT_MARKER + ) + length = len(match.group(0)) + self._column += length + + def TryConsume(self, token): + """Tries to consume a given piece of text. + + Args: + token: Text to consume. + + Returns: + True iff the text was consumed. + """ + if self.token == token: + self.NextToken() + return True + return False + + def Consume(self, token): + """Consumes a piece of text. + + Args: + token: Text to consume. + + Raises: + ParseError: If the text couldn't be consumed. + """ + if not self.TryConsume(token): + raise self.ParseError('Expected "%s".' % token) + + def ConsumeComment(self): + result = self.token + if not self._COMMENT.match(result): + raise self.ParseError('Expected comment.') + self.NextToken() + return result + + def ConsumeCommentOrTrailingComment(self): + """Consumes a comment, returns a 2-tuple (trailing bool, comment str).""" + + # Tokenizer initializes _previous_line and _previous_column to 0. As the + # tokenizer starts, it looks like there is a previous token on the line. + just_started = self._line == 0 and self._column == 0 + + before_parsing = self._previous_line + comment = self.ConsumeComment() + + # A trailing comment is a comment on the same line than the previous token. + trailing = self._previous_line == before_parsing and not just_started + + return trailing, comment + + def TryConsumeIdentifier(self): + try: + self.ConsumeIdentifier() + return True + except ParseError: + return False + + def ConsumeIdentifier(self): + """Consumes protocol message field identifier. + + Returns: + Identifier string. + + Raises: + ParseError: If an identifier couldn't be consumed. + """ + result = self.token + if not self._IDENTIFIER.match(result): + raise self.ParseError('Expected identifier.') + self.NextToken() + return result + + def TryConsumeIdentifierOrNumber(self): + try: + self.ConsumeIdentifierOrNumber() + return True + except ParseError: + return False + + def ConsumeIdentifierOrNumber(self): + """Consumes protocol message field identifier. + + Returns: + Identifier string. + + Raises: + ParseError: If an identifier couldn't be consumed. + """ + result = self.token + if not self._IDENTIFIER_OR_NUMBER.match(result): + raise self.ParseError('Expected identifier or number, got %s.' % result) + self.NextToken() + return result + + def TryConsumeInteger(self): + try: + self.ConsumeInteger() + return True + except ParseError: + return False + + def ConsumeInteger(self): + """Consumes an integer number. + + Returns: + The integer parsed. + + Raises: + ParseError: If an integer couldn't be consumed. + """ + try: + result = _ParseAbstractInteger(self.token) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def TryConsumeFloat(self): + try: + self.ConsumeFloat() + return True + except ParseError: + return False + + def ConsumeFloat(self): + """Consumes an floating point number. + + Returns: + The number parsed. + + Raises: + ParseError: If a floating point number couldn't be consumed. + """ + try: + result = ParseFloat(self.token) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def ConsumeBool(self): + """Consumes a boolean value. + + Returns: + The bool parsed. + + Raises: + ParseError: If a boolean value couldn't be consumed. + """ + try: + result = ParseBool(self.token) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def TryConsumeByteString(self): + try: + self.ConsumeByteString() + return True + except ParseError: + return False + + def ConsumeString(self): + """Consumes a string value. + + Returns: + The string parsed. + + Raises: + ParseError: If a string value couldn't be consumed. + """ + the_bytes = self.ConsumeByteString() + try: + return str(the_bytes, 'utf-8') + except UnicodeDecodeError as e: + raise self._StringParseError(e) + + def ConsumeByteString(self): + """Consumes a byte array value. + + Returns: + The array parsed (as a string). + + Raises: + ParseError: If a byte array value couldn't be consumed. + """ + the_list = [self._ConsumeSingleByteString()] + while self.token and self.token[0] in _QUOTES: + the_list.append(self._ConsumeSingleByteString()) + return b''.join(the_list) + + def _ConsumeSingleByteString(self): + """Consume one token of a string literal. + + String literals (whether bytes or text) can come in multiple adjacent + tokens which are automatically concatenated, like in C or Python. This + method only consumes one token. + + Returns: + The token parsed. + Raises: + ParseError: When the wrong format data is found. + """ + text = self.token + if len(text) < 1 or text[0] not in _QUOTES: + raise self.ParseError('Expected string but found: %r' % (text,)) + + if len(text) < 2 or text[-1] != text[0]: + raise self.ParseError('String missing ending quote: %r' % (text,)) + + try: + result = text_encoding.CUnescape(text[1:-1]) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def ConsumeEnum(self, field): + try: + result = ParseEnum(field, self.token) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def ConsumeUrlChars(self): + """Consumes a token containing valid URL characters. + + Excludes '/' so that it can be treated specially as a delimiter. + + Returns: + The next token containing one or more URL characters. + + Raises: + ParseError: If the next token contains unaccepted URL characters. + """ + if not self._URL_CHARS.match(self.token): + raise self.ParseError('Expected URL character(s), got "%s"' % self.token) + + result = self.token + self.NextToken() + return result + + def TryConsumeUrlChars(self): + try: + self.ConsumeUrlChars() + return True + except ParseError: + return False + + def ParseErrorPreviousToken(self, message): + """Creates and *returns* a ParseError for the previously read token. + + Args: + message: A message to set for the exception. + + Returns: + A ParseError instance. + """ + return ParseError( + message, self._previous_line + 1, self._previous_column + 1 + ) + + def ParseError(self, message): + """Creates and *returns* a ParseError for the current token.""" + return ParseError( + "'" + self._current_line + "': " + message, + self._line + 1, + self._column + 1, + ) + + def _StringParseError(self, e): + return self.ParseError("Couldn't parse string: " + str(e)) + + def NextToken(self): + """Reads the next meaningful token.""" + self._previous_line = self._line + self._previous_column = self._column + self.contains_silent_marker_before_current_token = False + + self._column += len(self.token) + self._SkipWhitespace() + + if not self._more_lines: + self.token = '' + return + + match = self._TOKEN.match(self._current_line, self._column) + if not match and not self._skip_comments: + match = self._COMMENT.match(self._current_line, self._column) + if match: + token = match.group(0) + self.token = token + else: + self.token = self._current_line[self._column] + + +# Aliased so it can still be accessed by current visibility violators. +# TODO: Migrate violators to textformat_tokenizer. +_Tokenizer = Tokenizer # pylint: disable=invalid-name + + +def _ConsumeInt32(tokenizer): + """Consumes a signed 32bit integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + + Returns: + The integer parsed. + + Raises: + ParseError: If a signed 32bit integer couldn't be consumed. + """ + return _ConsumeInteger(tokenizer, is_signed=True, is_long=False) + + +def _ConsumeUint32(tokenizer): + """Consumes an unsigned 32bit integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + + Returns: + The integer parsed. + + Raises: + ParseError: If an unsigned 32bit integer couldn't be consumed. + """ + return _ConsumeInteger(tokenizer, is_signed=False, is_long=False) + + +def _TryConsumeInt64(tokenizer): + try: + _ConsumeInt64(tokenizer) + return True + except ParseError: + return False + + +def _ConsumeInt64(tokenizer): + """Consumes a signed 32bit integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + + Returns: + The integer parsed. + + Raises: + ParseError: If a signed 32bit integer couldn't be consumed. + """ + return _ConsumeInteger(tokenizer, is_signed=True, is_long=True) + + +def _TryConsumeUint64(tokenizer): + try: + _ConsumeUint64(tokenizer) + return True + except ParseError: + return False + + +def _ConsumeUint64(tokenizer): + """Consumes an unsigned 64bit integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + + Returns: + The integer parsed. + + Raises: + ParseError: If an unsigned 64bit integer couldn't be consumed. + """ + return _ConsumeInteger(tokenizer, is_signed=False, is_long=True) + + +def _ConsumeInteger(tokenizer, is_signed=False, is_long=False): + """Consumes an integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + is_signed: True if a signed integer must be parsed. + is_long: True if a long integer must be parsed. + + Returns: + The integer parsed. + + Raises: + ParseError: If an integer with given characteristics couldn't be consumed. + """ + try: + result = ParseInteger(tokenizer.token, is_signed=is_signed, is_long=is_long) + except ValueError as e: + raise tokenizer.ParseError(str(e)) + tokenizer.NextToken() + return result + + +def ParseInteger(text, is_signed=False, is_long=False): + """Parses an integer. + + Args: + text: The text to parse. + is_signed: True if a signed integer must be parsed. + is_long: True if a long integer must be parsed. + + Returns: + The integer value. + + Raises: + ValueError: Thrown Iff the text is not a valid integer. + """ + # Do the actual parsing. Exception handling is propagated to caller. + result = _ParseAbstractInteger(text) + + # Check if the integer is sane. Exceptions handled by callers. + checker = _INTEGER_CHECKERS[2 * int(is_long) + int(is_signed)] + checker.CheckValue(result) + return result + + +def _ParseAbstractInteger(text): + """Parses an integer without checking size/signedness. + + Args: + text: The text to parse. + + Returns: + The integer value. + + Raises: + ValueError: Thrown Iff the text is not a valid integer. + """ + # Do the actual parsing. Exception handling is propagated to caller. + orig_text = text + c_octal_match = re.match(r'(-?)0(\d+)$', text) + if c_octal_match: + # Python 3 no longer supports 0755 octal syntax without the 'o', so + # we always use the '0o' prefix for multi-digit numbers starting with 0. + text = c_octal_match.group(1) + '0o' + c_octal_match.group(2) + try: + return int(text, 0) + except ValueError: + raise ValueError("Couldn't parse integer: %s" % orig_text) + + +def ParseFloat(text): + """Parse a floating point number. + + Args: + text: Text to parse. + + Returns: + The number parsed. + + Raises: + ValueError: If a floating point number couldn't be parsed. + """ + if _FLOAT_OCTAL_PREFIX.match(text): + raise ValueError('Invalid octal float: %s' % text) + try: + # Assume Python compatible syntax. + return float(text) + except ValueError: + # Check alternative spellings. + if _FLOAT_INFINITY.match(text): + if text[0] == '-': + return float('-inf') + else: + return float('inf') + elif _FLOAT_NAN.match(text): + return float('nan') + else: + # assume '1.0f' format + try: + return float(text.rstrip('fF')) + except ValueError: + raise ValueError("Couldn't parse float: %s" % text) + + +def ParseBool(text): + """Parse a boolean value. + + Args: + text: Text to parse. + + Returns: + Boolean values parsed + + Raises: + ValueError: If text is not a valid boolean. + """ + if text in ('true', 't', '1', 'True'): + return True + elif text in ('false', 'f', '0', 'False'): + return False + else: + raise ValueError('Expected "true" or "false".') + + +def ParseEnum(field, value): + """Parse an enum value. + + The value can be specified by a number (the enum value), or by + a string literal (the enum name). + + Args: + field: Enum field descriptor. + value: String value. + + Returns: + Enum value number. + + Raises: + ValueError: If the enum value could not be parsed. + """ + enum_descriptor = field.enum_type + try: + number = int(value, 0) + except ValueError: + # Identifier. + enum_value = enum_descriptor.values_by_name.get(value, None) + if enum_value is None: + raise ValueError( + 'Enum type "%s" has no value named %s.' + % (enum_descriptor.full_name, value) + ) + else: + if not field.enum_type.is_closed: + return number + enum_value = enum_descriptor.values_by_number.get(number, None) + if enum_value is None: + raise ValueError( + 'Enum type "%s" has no value with number %d.' + % (enum_descriptor.full_name, number) + ) + return enum_value.number diff --git a/venv/lib/python3.12/site-packages/google/protobuf/timestamp.py b/venv/lib/python3.12/site-packages/google/protobuf/timestamp.py new file mode 100755 index 0000000..b23b9f1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/timestamp.py @@ -0,0 +1,112 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +"""Contains the Timestamp helper APIs.""" + +import datetime +from typing import Optional + +from google.protobuf.timestamp_pb2 import Timestamp + + +def from_json_string(value: str) -> Timestamp: + """Parse a RFC 3339 date string format to Timestamp. + + Args: + value: A date string. Any fractional digits (or none) and any offset are + accepted as long as they fit into nano-seconds precision. Example of + accepted format: '1972-01-01T10:00:20.021-05:00' + + Raises: + ValueError: On parsing problems. + """ + timestamp = Timestamp() + timestamp.FromJsonString(value) + return timestamp + + +def from_microseconds(micros: float) -> Timestamp: + """Converts microseconds since epoch to Timestamp.""" + timestamp = Timestamp() + timestamp.FromMicroseconds(micros) + return timestamp + + +def from_milliseconds(millis: float) -> Timestamp: + """Converts milliseconds since epoch to Timestamp.""" + timestamp = Timestamp() + timestamp.FromMilliseconds(millis) + return timestamp + + +def from_nanoseconds(nanos: float) -> Timestamp: + """Converts nanoseconds since epoch to Timestamp.""" + timestamp = Timestamp() + timestamp.FromNanoseconds(nanos) + return timestamp + + +def from_seconds(seconds: float) -> Timestamp: + """Converts seconds since epoch to Timestamp.""" + timestamp = Timestamp() + timestamp.FromSeconds(seconds) + return timestamp + + +def from_current_time() -> Timestamp: + """Converts the current UTC to Timestamp.""" + timestamp = Timestamp() + timestamp.FromDatetime(datetime.datetime.now(tz=datetime.timezone.utc)) + return timestamp + + +def to_json_string(ts: Timestamp) -> str: + """Converts Timestamp to RFC 3339 date string format. + + Returns: + A string converted from timestamp. The string is always Z-normalized + and uses 3, 6 or 9 fractional digits as required to represent the + exact time. Example of the return format: '1972-01-01T10:00:20.021Z' + """ + return ts.ToJsonString() + + +def to_microseconds(ts: Timestamp) -> int: + """Converts Timestamp to microseconds since epoch.""" + return ts.ToMicroseconds() + + +def to_milliseconds(ts: Timestamp) -> int: + """Converts Timestamp to milliseconds since epoch.""" + return ts.ToMilliseconds() + + +def to_nanoseconds(ts: Timestamp) -> int: + """Converts Timestamp to nanoseconds since epoch.""" + return ts.ToNanoseconds() + + +def to_seconds(ts: Timestamp) -> int: + """Converts Timestamp to seconds since epoch.""" + return ts.ToSeconds() + + +def to_datetime( + ts: Timestamp, tz: Optional[datetime.tzinfo] = None +) -> datetime.datetime: + """Converts Timestamp to a datetime. + + Args: + tz: A datetime.tzinfo subclass; defaults to None. + + Returns: + If tzinfo is None, returns a timezone-naive UTC datetime (with no timezone + information, i.e. not aware that it's UTC). + + Otherwise, returns a timezone-aware datetime in the input timezone. + """ + return ts.ToDatetime(tzinfo=tz) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/timestamp_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/timestamp_pb2.py new file mode 100755 index 0000000..74a38ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/timestamp_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/timestamp.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/timestamp.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fgoogle/protobuf/timestamp.proto\x12\x0fgoogle.protobuf\";\n\tTimestamp\x12\x18\n\x07seconds\x18\x01 \x01(\x03R\x07seconds\x12\x14\n\x05nanos\x18\x02 \x01(\x05R\x05nanosB\x85\x01\n\x13\x63om.google.protobufB\x0eTimestampProtoP\x01Z2google.golang.org/protobuf/types/known/timestamppb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.timestamp_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\023com.google.protobufB\016TimestampProtoP\001Z2google.golang.org/protobuf/types/known/timestamppb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _globals['_TIMESTAMP']._serialized_start=52 + _globals['_TIMESTAMP']._serialized_end=111 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/type_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/type_pb2.py new file mode 100755 index 0000000..127b72e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/type_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/type.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/type.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 +from google.protobuf import source_context_pb2 as google_dot_protobuf_dot_source__context__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1agoogle/protobuf/type.proto\x12\x0fgoogle.protobuf\x1a\x19google/protobuf/any.proto\x1a$google/protobuf/source_context.proto\"\xa7\x02\n\x04Type\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12.\n\x06\x66ields\x18\x02 \x03(\x0b\x32\x16.google.protobuf.FieldR\x06\x66ields\x12\x16\n\x06oneofs\x18\x03 \x03(\tR\x06oneofs\x12\x31\n\x07options\x18\x04 \x03(\x0b\x32\x17.google.protobuf.OptionR\x07options\x12\x45\n\x0esource_context\x18\x05 \x01(\x0b\x32\x1e.google.protobuf.SourceContextR\rsourceContext\x12/\n\x06syntax\x18\x06 \x01(\x0e\x32\x17.google.protobuf.SyntaxR\x06syntax\x12\x18\n\x07\x65\x64ition\x18\x07 \x01(\tR\x07\x65\x64ition\"\xb4\x06\n\x05\x46ield\x12/\n\x04kind\x18\x01 \x01(\x0e\x32\x1b.google.protobuf.Field.KindR\x04kind\x12\x44\n\x0b\x63\x61rdinality\x18\x02 \x01(\x0e\x32\".google.protobuf.Field.CardinalityR\x0b\x63\x61rdinality\x12\x16\n\x06number\x18\x03 \x01(\x05R\x06number\x12\x12\n\x04name\x18\x04 \x01(\tR\x04name\x12\x19\n\x08type_url\x18\x06 \x01(\tR\x07typeUrl\x12\x1f\n\x0boneof_index\x18\x07 \x01(\x05R\noneofIndex\x12\x16\n\x06packed\x18\x08 \x01(\x08R\x06packed\x12\x31\n\x07options\x18\t \x03(\x0b\x32\x17.google.protobuf.OptionR\x07options\x12\x1b\n\tjson_name\x18\n \x01(\tR\x08jsonName\x12#\n\rdefault_value\x18\x0b \x01(\tR\x0c\x64\x65\x66\x61ultValue\"\xc8\x02\n\x04Kind\x12\x10\n\x0cTYPE_UNKNOWN\x10\x00\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"t\n\x0b\x43\x61rdinality\x12\x17\n\x13\x43\x41RDINALITY_UNKNOWN\x10\x00\x12\x18\n\x14\x43\x41RDINALITY_OPTIONAL\x10\x01\x12\x18\n\x14\x43\x41RDINALITY_REQUIRED\x10\x02\x12\x18\n\x14\x43\x41RDINALITY_REPEATED\x10\x03\"\x99\x02\n\x04\x45num\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x38\n\tenumvalue\x18\x02 \x03(\x0b\x32\x1a.google.protobuf.EnumValueR\tenumvalue\x12\x31\n\x07options\x18\x03 \x03(\x0b\x32\x17.google.protobuf.OptionR\x07options\x12\x45\n\x0esource_context\x18\x04 \x01(\x0b\x32\x1e.google.protobuf.SourceContextR\rsourceContext\x12/\n\x06syntax\x18\x05 \x01(\x0e\x32\x17.google.protobuf.SyntaxR\x06syntax\x12\x18\n\x07\x65\x64ition\x18\x06 \x01(\tR\x07\x65\x64ition\"j\n\tEnumValue\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n\x06number\x18\x02 \x01(\x05R\x06number\x12\x31\n\x07options\x18\x03 \x03(\x0b\x32\x17.google.protobuf.OptionR\x07options\"H\n\x06Option\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12*\n\x05value\x18\x02 \x01(\x0b\x32\x14.google.protobuf.AnyR\x05value*C\n\x06Syntax\x12\x11\n\rSYNTAX_PROTO2\x10\x00\x12\x11\n\rSYNTAX_PROTO3\x10\x01\x12\x13\n\x0fSYNTAX_EDITIONS\x10\x02\x42{\n\x13\x63om.google.protobufB\tTypeProtoP\x01Z-google.golang.org/protobuf/types/known/typepb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.type_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\023com.google.protobufB\tTypeProtoP\001Z-google.golang.org/protobuf/types/known/typepb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _globals['_SYNTAX']._serialized_start=1699 + _globals['_SYNTAX']._serialized_end=1766 + _globals['_TYPE']._serialized_start=113 + _globals['_TYPE']._serialized_end=408 + _globals['_FIELD']._serialized_start=411 + _globals['_FIELD']._serialized_end=1231 + _globals['_FIELD_KIND']._serialized_start=785 + _globals['_FIELD_KIND']._serialized_end=1113 + _globals['_FIELD_CARDINALITY']._serialized_start=1115 + _globals['_FIELD_CARDINALITY']._serialized_end=1231 + _globals['_ENUM']._serialized_start=1234 + _globals['_ENUM']._serialized_end=1515 + _globals['_ENUMVALUE']._serialized_start=1517 + _globals['_ENUMVALUE']._serialized_end=1623 + _globals['_OPTION']._serialized_start=1625 + _globals['_OPTION']._serialized_end=1697 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/unknown_fields.py b/venv/lib/python3.12/site-packages/google/protobuf/unknown_fields.py new file mode 100755 index 0000000..0e33f21 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/unknown_fields.py @@ -0,0 +1,93 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd +"""Contains Unknown Fields APIs. + +Simple usage example: + unknown_field_set = UnknownFieldSet(message) + for unknown_field in unknown_field_set: + wire_type = unknown_field.wire_type + field_number = unknown_field.field_number + data = unknown_field.data +""" + +from google.protobuf.internal import api_implementation + +if api_implementation._c_module is not None: # pylint: disable=protected-access + UnknownFieldSet = api_implementation._c_module.UnknownFieldSet # pylint: disable=protected-access +else: + from google.protobuf.internal import decoder # pylint: disable=g-import-not-at-top + from google.protobuf.internal import wire_format # pylint: disable=g-import-not-at-top + + class UnknownField: + """A parsed unknown field.""" + + # Disallows assignment to other attributes. + __slots__ = ['_field_number', '_wire_type', '_data'] + + def __init__(self, field_number, wire_type, data): + self._field_number = field_number + self._wire_type = wire_type + self._data = data + return + + @property + def field_number(self): + return self._field_number + + @property + def wire_type(self): + return self._wire_type + + @property + def data(self): + return self._data + + class UnknownFieldSet: + """UnknownField container.""" + + # Disallows assignment to other attributes. + __slots__ = ['_values'] + + def __init__(self, msg): + + def InternalAdd(field_number, wire_type, data): + unknown_field = UnknownField(field_number, wire_type, data) + self._values.append(unknown_field) + + self._values = [] + msg_des = msg.DESCRIPTOR + # pylint: disable=protected-access + unknown_fields = msg._unknown_fields + if msg_des.has_options and msg_des.GetOptions().message_set_wire_format: + local_decoder = decoder.UnknownMessageSetItemDecoder() + for _, buffer in unknown_fields: + field_number, data = local_decoder(memoryview(buffer)) + InternalAdd(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED, data) + else: + for tag_bytes, buffer in unknown_fields: + field_number, wire_type = decoder.DecodeTag(tag_bytes) + if field_number == 0: + raise RuntimeError('Field number 0 is illegal.') + data, _ = decoder._DecodeUnknownField( + memoryview(buffer), 0, len(buffer), field_number, wire_type + ) + InternalAdd(field_number, wire_type, data) + + def __getitem__(self, index): + size = len(self._values) + if index < 0: + index += size + if index < 0 or index >= size: + raise IndexError('index %d out of range'.index) + + return self._values[index] + + def __len__(self): + return len(self._values) + + def __iter__(self): + return iter(self._values) diff --git a/venv/lib/python3.12/site-packages/google/protobuf/util/__init__.py b/venv/lib/python3.12/site-packages/google/protobuf/util/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/google/protobuf/util/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/protobuf/util/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ce37156 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/protobuf/util/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/protobuf/wrappers_pb2.py b/venv/lib/python3.12/site-packages/google/protobuf/wrappers_pb2.py new file mode 100755 index 0000000..32b117e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/protobuf/wrappers_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/protobuf/wrappers.proto +# Protobuf Python Version: 7.36.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 36, + 2, + '', + 'google/protobuf/wrappers.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1egoogle/protobuf/wrappers.proto\x12\x0fgoogle.protobuf\"#\n\x0b\x44oubleValue\x12\x14\n\x05value\x18\x01 \x01(\x01R\x05value\"\"\n\nFloatValue\x12\x14\n\x05value\x18\x01 \x01(\x02R\x05value\"\"\n\nInt64Value\x12\x14\n\x05value\x18\x01 \x01(\x03R\x05value\"#\n\x0bUInt64Value\x12\x14\n\x05value\x18\x01 \x01(\x04R\x05value\"\"\n\nInt32Value\x12\x14\n\x05value\x18\x01 \x01(\x05R\x05value\"#\n\x0bUInt32Value\x12\x14\n\x05value\x18\x01 \x01(\rR\x05value\"!\n\tBoolValue\x12\x14\n\x05value\x18\x01 \x01(\x08R\x05value\"#\n\x0bStringValue\x12\x14\n\x05value\x18\x01 \x01(\tR\x05value\"\"\n\nBytesValue\x12\x14\n\x05value\x18\x01 \x01(\x0cR\x05valueB\x83\x01\n\x13\x63om.google.protobufB\rWrappersProtoP\x01Z1google.golang.org/protobuf/types/known/wrapperspb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.wrappers_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\023com.google.protobufB\rWrappersProtoP\001Z1google.golang.org/protobuf/types/known/wrapperspb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _globals['_DOUBLEVALUE']._serialized_start=51 + _globals['_DOUBLEVALUE']._serialized_end=86 + _globals['_FLOATVALUE']._serialized_start=88 + _globals['_FLOATVALUE']._serialized_end=122 + _globals['_INT64VALUE']._serialized_start=124 + _globals['_INT64VALUE']._serialized_end=158 + _globals['_UINT64VALUE']._serialized_start=160 + _globals['_UINT64VALUE']._serialized_end=195 + _globals['_INT32VALUE']._serialized_start=197 + _globals['_INT32VALUE']._serialized_end=231 + _globals['_UINT32VALUE']._serialized_start=233 + _globals['_UINT32VALUE']._serialized_end=268 + _globals['_BOOLVALUE']._serialized_start=270 + _globals['_BOOLVALUE']._serialized_end=303 + _globals['_STRINGVALUE']._serialized_start=305 + _globals['_STRINGVALUE']._serialized_end=340 + _globals['_BYTESVALUE']._serialized_start=342 + _globals['_BYTESVALUE']._serialized_end=376 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/rpc/__pycache__/code_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/rpc/__pycache__/code_pb2.cpython-312.pyc new file mode 100644 index 0000000..151666f Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/rpc/__pycache__/code_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/rpc/__pycache__/error_details_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/rpc/__pycache__/error_details_pb2.cpython-312.pyc new file mode 100644 index 0000000..91b86f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/rpc/__pycache__/error_details_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/rpc/__pycache__/http_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/rpc/__pycache__/http_pb2.cpython-312.pyc new file mode 100644 index 0000000..4369660 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/rpc/__pycache__/http_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/rpc/__pycache__/status_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/rpc/__pycache__/status_pb2.cpython-312.pyc new file mode 100644 index 0000000..9c33c89 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/rpc/__pycache__/status_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/rpc/code.proto b/venv/lib/python3.12/site-packages/google/rpc/code.proto new file mode 100644 index 0000000..46b5d75 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/code.proto @@ -0,0 +1,186 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +option go_package = "google.golang.org/genproto/googleapis/rpc/code;code"; +option java_multiple_files = true; +option java_outer_classname = "CodeProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// The canonical error codes for gRPC APIs. +// +// +// Sometimes multiple error codes may apply. Services should return +// the most specific error code that applies. For example, prefer +// `OUT_OF_RANGE` over `FAILED_PRECONDITION` if both codes apply. +// Similarly prefer `NOT_FOUND` or `ALREADY_EXISTS` over `FAILED_PRECONDITION`. +enum Code { + // Not an error; returned on success. + // + // HTTP Mapping: 200 OK + OK = 0; + + // The operation was cancelled, typically by the caller. + // + // HTTP Mapping: 499 Client Closed Request + CANCELLED = 1; + + // Unknown error. For example, this error may be returned when + // a `Status` value received from another address space belongs to + // an error space that is not known in this address space. Also + // errors raised by APIs that do not return enough error information + // may be converted to this error. + // + // HTTP Mapping: 500 Internal Server Error + UNKNOWN = 2; + + // The client specified an invalid argument. Note that this differs + // from `FAILED_PRECONDITION`. `INVALID_ARGUMENT` indicates arguments + // that are problematic regardless of the state of the system + // (e.g., a malformed file name). + // + // HTTP Mapping: 400 Bad Request + INVALID_ARGUMENT = 3; + + // The deadline expired before the operation could complete. For operations + // that change the state of the system, this error may be returned + // even if the operation has completed successfully. For example, a + // successful response from a server could have been delayed long + // enough for the deadline to expire. + // + // HTTP Mapping: 504 Gateway Timeout + DEADLINE_EXCEEDED = 4; + + // Some requested entity (e.g., file or directory) was not found. + // + // Note to server developers: if a request is denied for an entire class + // of users, such as gradual feature rollout or undocumented allowlist, + // `NOT_FOUND` may be used. If a request is denied for some users within + // a class of users, such as user-based access control, `PERMISSION_DENIED` + // must be used. + // + // HTTP Mapping: 404 Not Found + NOT_FOUND = 5; + + // The entity that a client attempted to create (e.g., file or directory) + // already exists. + // + // HTTP Mapping: 409 Conflict + ALREADY_EXISTS = 6; + + // The caller does not have permission to execute the specified + // operation. `PERMISSION_DENIED` must not be used for rejections + // caused by exhausting some resource (use `RESOURCE_EXHAUSTED` + // instead for those errors). `PERMISSION_DENIED` must not be + // used if the caller can not be identified (use `UNAUTHENTICATED` + // instead for those errors). This error code does not imply the + // request is valid or the requested entity exists or satisfies + // other pre-conditions. + // + // HTTP Mapping: 403 Forbidden + PERMISSION_DENIED = 7; + + // The request does not have valid authentication credentials for the + // operation. + // + // HTTP Mapping: 401 Unauthorized + UNAUTHENTICATED = 16; + + // Some resource has been exhausted, perhaps a per-user quota, or + // perhaps the entire file system is out of space. + // + // HTTP Mapping: 429 Too Many Requests + RESOURCE_EXHAUSTED = 8; + + // The operation was rejected because the system is not in a state + // required for the operation's execution. For example, the directory + // to be deleted is non-empty, an rmdir operation is applied to + // a non-directory, etc. + // + // Service implementors can use the following guidelines to decide + // between `FAILED_PRECONDITION`, `ABORTED`, and `UNAVAILABLE`: + // (a) Use `UNAVAILABLE` if the client can retry just the failing call. + // (b) Use `ABORTED` if the client should retry at a higher level. For + // example, when a client-specified test-and-set fails, indicating the + // client should restart a read-modify-write sequence. + // (c) Use `FAILED_PRECONDITION` if the client should not retry until + // the system state has been explicitly fixed. For example, if an "rmdir" + // fails because the directory is non-empty, `FAILED_PRECONDITION` + // should be returned since the client should not retry unless + // the files are deleted from the directory. + // + // HTTP Mapping: 400 Bad Request + FAILED_PRECONDITION = 9; + + // The operation was aborted, typically due to a concurrency issue such as + // a sequencer check failure or transaction abort. + // + // See the guidelines above for deciding between `FAILED_PRECONDITION`, + // `ABORTED`, and `UNAVAILABLE`. + // + // HTTP Mapping: 409 Conflict + ABORTED = 10; + + // The operation was attempted past the valid range. E.g., seeking or + // reading past end-of-file. + // + // Unlike `INVALID_ARGUMENT`, this error indicates a problem that may + // be fixed if the system state changes. For example, a 32-bit file + // system will generate `INVALID_ARGUMENT` if asked to read at an + // offset that is not in the range [0,2^32-1], but it will generate + // `OUT_OF_RANGE` if asked to read from an offset past the current + // file size. + // + // There is a fair bit of overlap between `FAILED_PRECONDITION` and + // `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific + // error) when it applies so that callers who are iterating through + // a space can easily look for an `OUT_OF_RANGE` error to detect when + // they are done. + // + // HTTP Mapping: 400 Bad Request + OUT_OF_RANGE = 11; + + // The operation is not implemented or is not supported/enabled in this + // service. + // + // HTTP Mapping: 501 Not Implemented + UNIMPLEMENTED = 12; + + // Internal errors. This means that some invariants expected by the + // underlying system have been broken. This error code is reserved + // for serious errors. + // + // HTTP Mapping: 500 Internal Server Error + INTERNAL = 13; + + // The service is currently unavailable. This is most likely a + // transient condition, which can be corrected by retrying with + // a backoff. Note that it is not always safe to retry + // non-idempotent operations. + // + // See the guidelines above for deciding between `FAILED_PRECONDITION`, + // `ABORTED`, and `UNAVAILABLE`. + // + // HTTP Mapping: 503 Service Unavailable + UNAVAILABLE = 14; + + // Unrecoverable data loss or corruption. + // + // HTTP Mapping: 500 Internal Server Error + DATA_LOSS = 15; +} diff --git a/venv/lib/python3.12/site-packages/google/rpc/code_pb2.py b/venv/lib/python3.12/site-packages/google/rpc/code_pb2.py new file mode 100644 index 0000000..d4dd6f3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/code_pb2.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/rpc/code.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/rpc/code.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n\x15google/rpc/code.proto\x12\ngoogle.rpc*\xb7\x02\n\x04\x43ode\x12\x06\n\x02OK\x10\x00\x12\r\n\tCANCELLED\x10\x01\x12\x0b\n\x07UNKNOWN\x10\x02\x12\x14\n\x10INVALID_ARGUMENT\x10\x03\x12\x15\n\x11\x44\x45\x41\x44LINE_EXCEEDED\x10\x04\x12\r\n\tNOT_FOUND\x10\x05\x12\x12\n\x0e\x41LREADY_EXISTS\x10\x06\x12\x15\n\x11PERMISSION_DENIED\x10\x07\x12\x13\n\x0fUNAUTHENTICATED\x10\x10\x12\x16\n\x12RESOURCE_EXHAUSTED\x10\x08\x12\x17\n\x13\x46\x41ILED_PRECONDITION\x10\t\x12\x0b\n\x07\x41\x42ORTED\x10\n\x12\x10\n\x0cOUT_OF_RANGE\x10\x0b\x12\x11\n\rUNIMPLEMENTED\x10\x0c\x12\x0c\n\x08INTERNAL\x10\r\x12\x0f\n\x0bUNAVAILABLE\x10\x0e\x12\r\n\tDATA_LOSS\x10\x0f\x42X\n\x0e\x63om.google.rpcB\tCodeProtoP\x01Z3google.golang.org/genproto/googleapis/rpc/code;code\xa2\x02\x03RPCb\x06proto3" +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.rpc.code_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.rpcB\tCodeProtoP\001Z3google.golang.org/genproto/googleapis/rpc/code;code\242\002\003RPC" + _globals["_CODE"]._serialized_start = 38 + _globals["_CODE"]._serialized_end = 349 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/rpc/code_pb2.pyi b/venv/lib/python3.12/site-packages/google/rpc/code_pb2.pyi new file mode 100644 index 0000000..17acb5a --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/code_pb2.pyi @@ -0,0 +1,58 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar + +from google.protobuf import descriptor as _descriptor +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor + +class Code(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + OK: _ClassVar[Code] + CANCELLED: _ClassVar[Code] + UNKNOWN: _ClassVar[Code] + INVALID_ARGUMENT: _ClassVar[Code] + DEADLINE_EXCEEDED: _ClassVar[Code] + NOT_FOUND: _ClassVar[Code] + ALREADY_EXISTS: _ClassVar[Code] + PERMISSION_DENIED: _ClassVar[Code] + UNAUTHENTICATED: _ClassVar[Code] + RESOURCE_EXHAUSTED: _ClassVar[Code] + FAILED_PRECONDITION: _ClassVar[Code] + ABORTED: _ClassVar[Code] + OUT_OF_RANGE: _ClassVar[Code] + UNIMPLEMENTED: _ClassVar[Code] + INTERNAL: _ClassVar[Code] + UNAVAILABLE: _ClassVar[Code] + DATA_LOSS: _ClassVar[Code] + +OK: Code +CANCELLED: Code +UNKNOWN: Code +INVALID_ARGUMENT: Code +DEADLINE_EXCEEDED: Code +NOT_FOUND: Code +ALREADY_EXISTS: Code +PERMISSION_DENIED: Code +UNAUTHENTICATED: Code +RESOURCE_EXHAUSTED: Code +FAILED_PRECONDITION: Code +ABORTED: Code +OUT_OF_RANGE: Code +UNIMPLEMENTED: Code +INTERNAL: Code +UNAVAILABLE: Code +DATA_LOSS: Code diff --git a/venv/lib/python3.12/site-packages/google/rpc/context/__pycache__/attribute_context_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/rpc/context/__pycache__/attribute_context_pb2.cpython-312.pyc new file mode 100644 index 0000000..4f650ca Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/rpc/context/__pycache__/attribute_context_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/rpc/context/__pycache__/audit_context_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/rpc/context/__pycache__/audit_context_pb2.cpython-312.pyc new file mode 100644 index 0000000..e83eced Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/rpc/context/__pycache__/audit_context_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/rpc/context/attribute_context.proto b/venv/lib/python3.12/site-packages/google/rpc/context/attribute_context.proto new file mode 100644 index 0000000..55f95f8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/context/attribute_context.proto @@ -0,0 +1,350 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc.context; + +import "google/protobuf/any.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "google.golang.org/genproto/googleapis/rpc/context/attribute_context;attribute_context"; +option java_multiple_files = true; +option java_outer_classname = "AttributeContextProto"; +option java_package = "com.google.rpc.context"; + +// This message defines the standard attribute vocabulary for Google APIs. +// +// An attribute is a piece of metadata that describes an activity on a network +// service. For example, the size of an HTTP request, or the status code of +// an HTTP response. +// +// Each attribute has a type and a name, which is logically defined as +// a proto message field in `AttributeContext`. The field type becomes the +// attribute type, and the field path becomes the attribute name. For example, +// the attribute `source.ip` maps to field `AttributeContext.source.ip`. +// +// This message definition is guaranteed not to have any wire breaking change. +// So you can use it directly for passing attributes across different systems. +// +// NOTE: Different system may generate different subset of attributes. Please +// verify the system specification before relying on an attribute generated +// a system. +message AttributeContext { + // This message defines attributes for a node that handles a network request. + // The node can be either a service or an application that sends, forwards, + // or receives the request. Service peers should fill in + // `principal` and `labels` as appropriate. + message Peer { + // The IP address of the peer. + string ip = 1; + + // The network port of the peer. + int64 port = 2; + + // The labels associated with the peer. + map labels = 6; + + // The identity of this peer. Similar to `Request.auth.principal`, but + // relative to the peer instead of the request. For example, the + // identity associated with a load balancer that forwarded the request. + string principal = 7; + + // The CLDR country/region code associated with the above IP address. + // If the IP address is private, the `region_code` should reflect the + // physical location where this peer is running. + string region_code = 8; + } + + // This message defines attributes associated with API operations, such as + // a network API request. The terminology is based on the conventions used + // by Google APIs, Istio, and OpenAPI. + message Api { + // The API service name. It is a logical identifier for a networked API, + // such as "pubsub.googleapis.com". The naming syntax depends on the + // API management system being used for handling the request. + string service = 1; + + // The API operation name. For gRPC requests, it is the fully qualified API + // method name, such as "google.pubsub.v1.Publisher.Publish". For OpenAPI + // requests, it is the `operationId`, such as "getPet". + string operation = 2; + + // The API protocol used for sending the request, such as "http", "https", + // "grpc", or "internal". + string protocol = 3; + + // The API version associated with the API operation above, such as "v1" or + // "v1alpha1". + string version = 4; + } + + // This message defines request authentication attributes. Terminology is + // based on the JSON Web Token (JWT) standard, but the terms also + // correlate to concepts in other standards. + message Auth { + // The authenticated principal. Reflects the issuer (`iss`) and subject + // (`sub`) claims within a JWT. The issuer and subject should be `/` + // delimited, with `/` percent-encoded within the subject fragment. For + // Google accounts, the principal format is: + // "https://accounts.google.com/{id}" + string principal = 1; + + // The intended audience(s) for this authentication information. Reflects + // the audience (`aud`) claim within a JWT. The audience + // value(s) depends on the `issuer`, but typically include one or more of + // the following pieces of information: + // + // * The services intended to receive the credential. For example, + // ["https://pubsub.googleapis.com/", "https://storage.googleapis.com/"]. + // * A set of service-based scopes. For example, + // ["https://www.googleapis.com/auth/cloud-platform"]. + // * The client id of an app, such as the Firebase project id for JWTs + // from Firebase Auth. + // + // Consult the documentation for the credential issuer to determine the + // information provided. + repeated string audiences = 2; + + // The authorized presenter of the credential. Reflects the optional + // Authorized Presenter (`azp`) claim within a JWT or the + // OAuth client id. For example, a Google Cloud Platform client id looks + // as follows: "123456789012.apps.googleusercontent.com". + string presenter = 3; + + // Structured claims presented with the credential. JWTs include + // `{key: value}` pairs for standard and private claims. The following + // is a subset of the standard required and optional claims that would + // typically be presented for a Google-based JWT: + // + // {'iss': 'accounts.google.com', + // 'sub': '113289723416554971153', + // 'aud': ['123456789012', 'pubsub.googleapis.com'], + // 'azp': '123456789012.apps.googleusercontent.com', + // 'email': 'jsmith@example.com', + // 'iat': 1353601026, + // 'exp': 1353604926} + // + // SAML assertions are similarly specified, but with an identity provider + // dependent structure. + google.protobuf.Struct claims = 4; + + // A list of access level resource names that allow resources to be + // accessed by authenticated requester. It is part of Secure GCP processing + // for the incoming request. An access level string has the format: + // "//{api_service_name}/accessPolicies/{policy_id}/accessLevels/{short_name}" + // + // Example: + // "//accesscontextmanager.googleapis.com/accessPolicies/MY_POLICY_ID/accessLevels/MY_LEVEL" + repeated string access_levels = 5; + } + + // This message defines attributes for an HTTP request. If the actual + // request is not an HTTP request, the runtime system should try to map + // the actual request to an equivalent HTTP request. + message Request { + // The unique ID for a request, which can be propagated to downstream + // systems. The ID should have low probability of collision + // within a single day for a specific service. + string id = 1; + + // The HTTP request method, such as `GET`, `POST`. + string method = 2; + + // The HTTP request headers. If multiple headers share the same key, they + // must be merged according to the HTTP spec. All header keys must be + // lowercased, because HTTP header keys are case-insensitive. + map headers = 3; + + // The HTTP URL path, excluding the query parameters. + string path = 4; + + // The HTTP request `Host` header value. + string host = 5; + + // The HTTP URL scheme, such as `http` and `https`. + string scheme = 6; + + // The HTTP URL query in the format of `name1=value1&name2=value2`, as it + // appears in the first line of the HTTP request. No decoding is performed. + string query = 7; + + // The timestamp when the `destination` service receives the last byte of + // the request. + google.protobuf.Timestamp time = 9; + + // The HTTP request size in bytes. If unknown, it must be -1. + int64 size = 10; + + // The network protocol used with the request, such as "http/1.1", + // "spdy/3", "h2", "h2c", "webrtc", "tcp", "udp", "quic". See + // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids + // for details. + string protocol = 11; + + // A special parameter for request reason. It is used by security systems + // to associate auditing information with a request. + string reason = 12; + + // The request authentication. May be absent for unauthenticated requests. + // Derived from the HTTP request `Authorization` header or equivalent. + Auth auth = 13; + + // The values from Origin header from the HTTP request, such as + // "https://console.cloud.google.com". Modern browsers can only have one + // origin. Special browsers and/or HTTP clients may require multiple + // origins. + string origin = 14; + } + + // This message defines attributes for a typical network response. It + // generally models semantics of an HTTP response. + message Response { + // The HTTP response status code, such as `200` and `404`. + int64 code = 1; + + // The HTTP response size in bytes. If unknown, it must be -1. + int64 size = 2; + + // The HTTP response headers. If multiple headers share the same key, they + // must be merged according to HTTP spec. All header keys must be + // lowercased, because HTTP header keys are case-insensitive. + map headers = 3; + + // The timestamp when the `destination` service sends the last byte of + // the response. + google.protobuf.Timestamp time = 4; + + // The amount of time it takes the backend service to fully respond to a + // request. Measured from when the destination service starts to send the + // request to the backend until when the destination service receives the + // complete response from the backend. + google.protobuf.Duration backend_latency = 5; + } + + // This message defines core attributes for a resource. A resource is an + // addressable (named) entity provided by the destination service. For + // example, a file stored on a network storage service. + message Resource { + // The name of the service that this resource belongs to, such as + // `pubsub.googleapis.com`. The service may be different from the DNS + // hostname that actually serves the request. + string service = 1; + + // The stable identifier (name) of a resource on the `service`. A resource + // can be logically identified as "//{resource.service}/{resource.name}". + // The differences between a resource name and a URI are: + // + // * Resource name is a logical identifier, independent of network + // protocol and API version. For example, + // `//pubsub.googleapis.com/projects/123/topics/news-feed`. + // * URI often includes protocol and version information, so it can + // be used directly by applications. For example, + // `https://pubsub.googleapis.com/v1/projects/123/topics/news-feed`. + // + // See https://cloud.google.com/apis/design/resource_names for details. + string name = 2; + + // The type of the resource. The syntax is platform-specific because + // different platforms define their resources differently. + // + // For Google APIs, the type format must be "{service}/{kind}", such as + // "pubsub.googleapis.com/Topic". + string type = 3; + + // The labels or tags on the resource, such as AWS resource tags and + // Kubernetes resource labels. + map labels = 4; + + // The unique identifier of the resource. UID is unique in the time + // and space for this resource within the scope of the service. It is + // typically generated by the server on successful creation of a resource + // and must not be changed. UID is used to uniquely identify resources + // with resource name reuses. This should be a UUID4. + string uid = 5; + + // Annotations is an unstructured key-value map stored with a resource that + // may be set by external tools to store and retrieve arbitrary metadata. + // They are not queryable and should be preserved when modifying objects. + // + // More info: + // https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + map annotations = 6; + + // Mutable. The display name set by clients. Must be <= 63 characters. + string display_name = 7; + + // Output only. The timestamp when the resource was created. This may + // be either the time creation was initiated or when it was completed. + google.protobuf.Timestamp create_time = 8; + + // Output only. The timestamp when the resource was last updated. Any + // change to the resource made by users must refresh this value. + // Changes to a resource made by the service should refresh this value. + google.protobuf.Timestamp update_time = 9; + + // Output only. The timestamp when the resource was deleted. + // If the resource is not deleted, this must be empty. + google.protobuf.Timestamp delete_time = 10; + + // Output only. An opaque value that uniquely identifies a version or + // generation of a resource. It can be used to confirm that the client + // and server agree on the ordering of a resource being written. + string etag = 11; + + // Immutable. The location of the resource. The location encoding is + // specific to the service provider, and new encoding may be introduced + // as the service evolves. + // + // For Google Cloud products, the encoding is what is used by Google Cloud + // APIs, such as `us-east1`, `aws-us-east-1`, and `azure-eastus2`. The + // semantics of `location` is identical to the + // `cloud.googleapis.com/location` label used by some Google Cloud APIs. + string location = 12; + } + + // The origin of a network activity. In a multi hop network activity, + // the origin represents the sender of the first hop. For the first hop, + // the `source` and the `origin` must have the same content. + Peer origin = 7; + + // The source of a network activity, such as starting a TCP connection. + // In a multi hop network activity, the source represents the sender of the + // last hop. + Peer source = 1; + + // The destination of a network activity, such as accepting a TCP connection. + // In a multi hop network activity, the destination represents the receiver of + // the last hop. + Peer destination = 2; + + // Represents a network request, such as an HTTP request. + Request request = 3; + + // Represents a network response, such as an HTTP response. + Response response = 4; + + // Represents a target resource that is involved with a network activity. + // If multiple resources are involved with an activity, this must be the + // primary one. + Resource resource = 5; + + // Represents an API operation that is involved to a network activity. + Api api = 6; + + // Supports extensions for advanced use cases, such as logs and metrics. + repeated google.protobuf.Any extensions = 8; +} diff --git a/venv/lib/python3.12/site-packages/google/rpc/context/attribute_context_pb2.py b/venv/lib/python3.12/site-packages/google/rpc/context/attribute_context_pb2.py new file mode 100644 index 0000000..d20d962 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/context/attribute_context_pb2.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/rpc/context/attribute_context.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 33, + 5, + "", + "google/rpc/context/attribute_context.proto", +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n*google/rpc/context/attribute_context.proto\x12\x12google.rpc.context\x1a\x19google/protobuf/any.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x93\x10\n\x10\x41ttributeContext\x12\x39\n\x06origin\x18\x07 \x01(\x0b\x32).google.rpc.context.AttributeContext.Peer\x12\x39\n\x06source\x18\x01 \x01(\x0b\x32).google.rpc.context.AttributeContext.Peer\x12>\n\x0b\x64\x65stination\x18\x02 \x01(\x0b\x32).google.rpc.context.AttributeContext.Peer\x12=\n\x07request\x18\x03 \x01(\x0b\x32,.google.rpc.context.AttributeContext.Request\x12?\n\x08response\x18\x04 \x01(\x0b\x32-.google.rpc.context.AttributeContext.Response\x12?\n\x08resource\x18\x05 \x01(\x0b\x32-.google.rpc.context.AttributeContext.Resource\x12\x35\n\x03\x61pi\x18\x06 \x01(\x0b\x32(.google.rpc.context.AttributeContext.Api\x12(\n\nextensions\x18\x08 \x03(\x0b\x32\x14.google.protobuf.Any\x1a\xbe\x01\n\x04Peer\x12\n\n\x02ip\x18\x01 \x01(\t\x12\x0c\n\x04port\x18\x02 \x01(\x03\x12\x45\n\x06labels\x18\x06 \x03(\x0b\x32\x35.google.rpc.context.AttributeContext.Peer.LabelsEntry\x12\x11\n\tprincipal\x18\x07 \x01(\t\x12\x13\n\x0bregion_code\x18\x08 \x01(\t\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1aL\n\x03\x41pi\x12\x0f\n\x07service\x18\x01 \x01(\t\x12\x11\n\toperation\x18\x02 \x01(\t\x12\x10\n\x08protocol\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\x1a\x7f\n\x04\x41uth\x12\x11\n\tprincipal\x18\x01 \x01(\t\x12\x11\n\taudiences\x18\x02 \x03(\t\x12\x11\n\tpresenter\x18\x03 \x01(\t\x12'\n\x06\x63laims\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x15\n\raccess_levels\x18\x05 \x03(\t\x1a\xff\x02\n\x07Request\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0e\n\x06method\x18\x02 \x01(\t\x12J\n\x07headers\x18\x03 \x03(\x0b\x32\x39.google.rpc.context.AttributeContext.Request.HeadersEntry\x12\x0c\n\x04path\x18\x04 \x01(\t\x12\x0c\n\x04host\x18\x05 \x01(\t\x12\x0e\n\x06scheme\x18\x06 \x01(\t\x12\r\n\x05query\x18\x07 \x01(\t\x12(\n\x04time\x18\t \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0c\n\x04size\x18\n \x01(\x03\x12\x10\n\x08protocol\x18\x0b \x01(\t\x12\x0e\n\x06reason\x18\x0c \x01(\t\x12\x37\n\x04\x61uth\x18\r \x01(\x0b\x32).google.rpc.context.AttributeContext.Auth\x12\x0e\n\x06origin\x18\x0e \x01(\t\x1a.\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x81\x02\n\x08Response\x12\x0c\n\x04\x63ode\x18\x01 \x01(\x03\x12\x0c\n\x04size\x18\x02 \x01(\x03\x12K\n\x07headers\x18\x03 \x03(\x0b\x32:.google.rpc.context.AttributeContext.Response.HeadersEntry\x12(\n\x04time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x32\n\x0f\x62\x61\x63kend_latency\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x1a.\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x90\x04\n\x08Resource\x12\x0f\n\x07service\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0c\n\x04type\x18\x03 \x01(\t\x12I\n\x06labels\x18\x04 \x03(\x0b\x32\x39.google.rpc.context.AttributeContext.Resource.LabelsEntry\x12\x0b\n\x03uid\x18\x05 \x01(\t\x12S\n\x0b\x61nnotations\x18\x06 \x03(\x0b\x32>.google.rpc.context.AttributeContext.Resource.AnnotationsEntry\x12\x14\n\x0c\x64isplay_name\x18\x07 \x01(\t\x12/\n\x0b\x63reate_time\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12/\n\x0bupdate_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12/\n\x0b\x64\x65lete_time\x18\n \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0c\n\x04\x65tag\x18\x0b \x01(\t\x12\x10\n\x08location\x18\x0c \x01(\t\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x32\n\x10\x41nnotationsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x88\x01\n\x16\x63om.google.rpc.contextB\x15\x41ttributeContextProtoP\x01ZUgoogle.golang.org/genproto/googleapis/rpc/context/attribute_context;attribute_contextb\x06proto3" +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.rpc.context.attribute_context_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\026com.google.rpc.contextB\025AttributeContextProtoP\001ZUgoogle.golang.org/genproto/googleapis/rpc/context/attribute_context;attribute_context" + _globals["_ATTRIBUTECONTEXT_PEER_LABELSENTRY"]._loaded_options = None + _globals["_ATTRIBUTECONTEXT_PEER_LABELSENTRY"]._serialized_options = b"8\001" + _globals["_ATTRIBUTECONTEXT_REQUEST_HEADERSENTRY"]._loaded_options = None + _globals["_ATTRIBUTECONTEXT_REQUEST_HEADERSENTRY"]._serialized_options = b"8\001" + _globals["_ATTRIBUTECONTEXT_RESPONSE_HEADERSENTRY"]._loaded_options = None + _globals["_ATTRIBUTECONTEXT_RESPONSE_HEADERSENTRY"]._serialized_options = b"8\001" + _globals["_ATTRIBUTECONTEXT_RESOURCE_LABELSENTRY"]._loaded_options = None + _globals["_ATTRIBUTECONTEXT_RESOURCE_LABELSENTRY"]._serialized_options = b"8\001" + _globals["_ATTRIBUTECONTEXT_RESOURCE_ANNOTATIONSENTRY"]._loaded_options = None + _globals[ + "_ATTRIBUTECONTEXT_RESOURCE_ANNOTATIONSENTRY" + ]._serialized_options = b"8\001" + _globals["_ATTRIBUTECONTEXT"]._serialized_start = 189 + _globals["_ATTRIBUTECONTEXT"]._serialized_end = 2256 + _globals["_ATTRIBUTECONTEXT_PEER"]._serialized_start = 682 + _globals["_ATTRIBUTECONTEXT_PEER"]._serialized_end = 872 + _globals["_ATTRIBUTECONTEXT_PEER_LABELSENTRY"]._serialized_start = 827 + _globals["_ATTRIBUTECONTEXT_PEER_LABELSENTRY"]._serialized_end = 872 + _globals["_ATTRIBUTECONTEXT_API"]._serialized_start = 874 + _globals["_ATTRIBUTECONTEXT_API"]._serialized_end = 950 + _globals["_ATTRIBUTECONTEXT_AUTH"]._serialized_start = 952 + _globals["_ATTRIBUTECONTEXT_AUTH"]._serialized_end = 1079 + _globals["_ATTRIBUTECONTEXT_REQUEST"]._serialized_start = 1082 + _globals["_ATTRIBUTECONTEXT_REQUEST"]._serialized_end = 1465 + _globals["_ATTRIBUTECONTEXT_REQUEST_HEADERSENTRY"]._serialized_start = 1419 + _globals["_ATTRIBUTECONTEXT_REQUEST_HEADERSENTRY"]._serialized_end = 1465 + _globals["_ATTRIBUTECONTEXT_RESPONSE"]._serialized_start = 1468 + _globals["_ATTRIBUTECONTEXT_RESPONSE"]._serialized_end = 1725 + _globals["_ATTRIBUTECONTEXT_RESPONSE_HEADERSENTRY"]._serialized_start = 1419 + _globals["_ATTRIBUTECONTEXT_RESPONSE_HEADERSENTRY"]._serialized_end = 1465 + _globals["_ATTRIBUTECONTEXT_RESOURCE"]._serialized_start = 1728 + _globals["_ATTRIBUTECONTEXT_RESOURCE"]._serialized_end = 2256 + _globals["_ATTRIBUTECONTEXT_RESOURCE_LABELSENTRY"]._serialized_start = 827 + _globals["_ATTRIBUTECONTEXT_RESOURCE_LABELSENTRY"]._serialized_end = 872 + _globals["_ATTRIBUTECONTEXT_RESOURCE_ANNOTATIONSENTRY"]._serialized_start = 2206 + _globals["_ATTRIBUTECONTEXT_RESOURCE_ANNOTATIONSENTRY"]._serialized_end = 2256 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/rpc/context/attribute_context_pb2.pyi b/venv/lib/python3.12/site-packages/google/rpc/context/attribute_context_pb2.pyi new file mode 100644 index 0000000..feab1f6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/context/attribute_context_pb2.pyi @@ -0,0 +1,326 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import any_pb2 as _any_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import duration_pb2 as _duration_pb2 +from google.protobuf import message as _message +from google.protobuf import struct_pb2 as _struct_pb2 +from google.protobuf import timestamp_pb2 as _timestamp_pb2 +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class AttributeContext(_message.Message): + __slots__ = ( + "origin", + "source", + "destination", + "request", + "response", + "resource", + "api", + "extensions", + ) + class Peer(_message.Message): + __slots__ = ("ip", "port", "labels", "principal", "region_code") + class LabelsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + IP_FIELD_NUMBER: _ClassVar[int] + PORT_FIELD_NUMBER: _ClassVar[int] + LABELS_FIELD_NUMBER: _ClassVar[int] + PRINCIPAL_FIELD_NUMBER: _ClassVar[int] + REGION_CODE_FIELD_NUMBER: _ClassVar[int] + ip: str + port: int + labels: _containers.ScalarMap[str, str] + principal: str + region_code: str + def __init__( + self, + ip: _Optional[str] = ..., + port: _Optional[int] = ..., + labels: _Optional[_Mapping[str, str]] = ..., + principal: _Optional[str] = ..., + region_code: _Optional[str] = ..., + ) -> None: ... + + class Api(_message.Message): + __slots__ = ("service", "operation", "protocol", "version") + SERVICE_FIELD_NUMBER: _ClassVar[int] + OPERATION_FIELD_NUMBER: _ClassVar[int] + PROTOCOL_FIELD_NUMBER: _ClassVar[int] + VERSION_FIELD_NUMBER: _ClassVar[int] + service: str + operation: str + protocol: str + version: str + def __init__( + self, + service: _Optional[str] = ..., + operation: _Optional[str] = ..., + protocol: _Optional[str] = ..., + version: _Optional[str] = ..., + ) -> None: ... + + class Auth(_message.Message): + __slots__ = ("principal", "audiences", "presenter", "claims", "access_levels") + PRINCIPAL_FIELD_NUMBER: _ClassVar[int] + AUDIENCES_FIELD_NUMBER: _ClassVar[int] + PRESENTER_FIELD_NUMBER: _ClassVar[int] + CLAIMS_FIELD_NUMBER: _ClassVar[int] + ACCESS_LEVELS_FIELD_NUMBER: _ClassVar[int] + principal: str + audiences: _containers.RepeatedScalarFieldContainer[str] + presenter: str + claims: _struct_pb2.Struct + access_levels: _containers.RepeatedScalarFieldContainer[str] + def __init__( + self, + principal: _Optional[str] = ..., + audiences: _Optional[_Iterable[str]] = ..., + presenter: _Optional[str] = ..., + claims: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., + access_levels: _Optional[_Iterable[str]] = ..., + ) -> None: ... + + class Request(_message.Message): + __slots__ = ( + "id", + "method", + "headers", + "path", + "host", + "scheme", + "query", + "time", + "size", + "protocol", + "reason", + "auth", + "origin", + ) + class HeadersEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + ID_FIELD_NUMBER: _ClassVar[int] + METHOD_FIELD_NUMBER: _ClassVar[int] + HEADERS_FIELD_NUMBER: _ClassVar[int] + PATH_FIELD_NUMBER: _ClassVar[int] + HOST_FIELD_NUMBER: _ClassVar[int] + SCHEME_FIELD_NUMBER: _ClassVar[int] + QUERY_FIELD_NUMBER: _ClassVar[int] + TIME_FIELD_NUMBER: _ClassVar[int] + SIZE_FIELD_NUMBER: _ClassVar[int] + PROTOCOL_FIELD_NUMBER: _ClassVar[int] + REASON_FIELD_NUMBER: _ClassVar[int] + AUTH_FIELD_NUMBER: _ClassVar[int] + ORIGIN_FIELD_NUMBER: _ClassVar[int] + id: str + method: str + headers: _containers.ScalarMap[str, str] + path: str + host: str + scheme: str + query: str + time: _timestamp_pb2.Timestamp + size: int + protocol: str + reason: str + auth: AttributeContext.Auth + origin: str + def __init__( + self, + id: _Optional[str] = ..., + method: _Optional[str] = ..., + headers: _Optional[_Mapping[str, str]] = ..., + path: _Optional[str] = ..., + host: _Optional[str] = ..., + scheme: _Optional[str] = ..., + query: _Optional[str] = ..., + time: _Optional[ + _Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping] + ] = ..., + size: _Optional[int] = ..., + protocol: _Optional[str] = ..., + reason: _Optional[str] = ..., + auth: _Optional[_Union[AttributeContext.Auth, _Mapping]] = ..., + origin: _Optional[str] = ..., + ) -> None: ... + + class Response(_message.Message): + __slots__ = ("code", "size", "headers", "time", "backend_latency") + class HeadersEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + CODE_FIELD_NUMBER: _ClassVar[int] + SIZE_FIELD_NUMBER: _ClassVar[int] + HEADERS_FIELD_NUMBER: _ClassVar[int] + TIME_FIELD_NUMBER: _ClassVar[int] + BACKEND_LATENCY_FIELD_NUMBER: _ClassVar[int] + code: int + size: int + headers: _containers.ScalarMap[str, str] + time: _timestamp_pb2.Timestamp + backend_latency: _duration_pb2.Duration + def __init__( + self, + code: _Optional[int] = ..., + size: _Optional[int] = ..., + headers: _Optional[_Mapping[str, str]] = ..., + time: _Optional[ + _Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping] + ] = ..., + backend_latency: _Optional[ + _Union[datetime.timedelta, _duration_pb2.Duration, _Mapping] + ] = ..., + ) -> None: ... + + class Resource(_message.Message): + __slots__ = ( + "service", + "name", + "type", + "labels", + "uid", + "annotations", + "display_name", + "create_time", + "update_time", + "delete_time", + "etag", + "location", + ) + class LabelsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + class AnnotationsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + SERVICE_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + TYPE_FIELD_NUMBER: _ClassVar[int] + LABELS_FIELD_NUMBER: _ClassVar[int] + UID_FIELD_NUMBER: _ClassVar[int] + ANNOTATIONS_FIELD_NUMBER: _ClassVar[int] + DISPLAY_NAME_FIELD_NUMBER: _ClassVar[int] + CREATE_TIME_FIELD_NUMBER: _ClassVar[int] + UPDATE_TIME_FIELD_NUMBER: _ClassVar[int] + DELETE_TIME_FIELD_NUMBER: _ClassVar[int] + ETAG_FIELD_NUMBER: _ClassVar[int] + LOCATION_FIELD_NUMBER: _ClassVar[int] + service: str + name: str + type: str + labels: _containers.ScalarMap[str, str] + uid: str + annotations: _containers.ScalarMap[str, str] + display_name: str + create_time: _timestamp_pb2.Timestamp + update_time: _timestamp_pb2.Timestamp + delete_time: _timestamp_pb2.Timestamp + etag: str + location: str + def __init__( + self, + service: _Optional[str] = ..., + name: _Optional[str] = ..., + type: _Optional[str] = ..., + labels: _Optional[_Mapping[str, str]] = ..., + uid: _Optional[str] = ..., + annotations: _Optional[_Mapping[str, str]] = ..., + display_name: _Optional[str] = ..., + create_time: _Optional[ + _Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping] + ] = ..., + update_time: _Optional[ + _Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping] + ] = ..., + delete_time: _Optional[ + _Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping] + ] = ..., + etag: _Optional[str] = ..., + location: _Optional[str] = ..., + ) -> None: ... + + ORIGIN_FIELD_NUMBER: _ClassVar[int] + SOURCE_FIELD_NUMBER: _ClassVar[int] + DESTINATION_FIELD_NUMBER: _ClassVar[int] + REQUEST_FIELD_NUMBER: _ClassVar[int] + RESPONSE_FIELD_NUMBER: _ClassVar[int] + RESOURCE_FIELD_NUMBER: _ClassVar[int] + API_FIELD_NUMBER: _ClassVar[int] + EXTENSIONS_FIELD_NUMBER: _ClassVar[int] + origin: AttributeContext.Peer + source: AttributeContext.Peer + destination: AttributeContext.Peer + request: AttributeContext.Request + response: AttributeContext.Response + resource: AttributeContext.Resource + api: AttributeContext.Api + extensions: _containers.RepeatedCompositeFieldContainer[_any_pb2.Any] + def __init__( + self, + origin: _Optional[_Union[AttributeContext.Peer, _Mapping]] = ..., + source: _Optional[_Union[AttributeContext.Peer, _Mapping]] = ..., + destination: _Optional[_Union[AttributeContext.Peer, _Mapping]] = ..., + request: _Optional[_Union[AttributeContext.Request, _Mapping]] = ..., + response: _Optional[_Union[AttributeContext.Response, _Mapping]] = ..., + resource: _Optional[_Union[AttributeContext.Resource, _Mapping]] = ..., + api: _Optional[_Union[AttributeContext.Api, _Mapping]] = ..., + extensions: _Optional[_Iterable[_Union[_any_pb2.Any, _Mapping]]] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/rpc/context/audit_context.proto b/venv/lib/python3.12/site-packages/google/rpc/context/audit_context.proto new file mode 100644 index 0000000..03c5ab9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/context/audit_context.proto @@ -0,0 +1,48 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc.context; + +import "google/protobuf/struct.proto"; + +option go_package = "google.golang.org/genproto/googleapis/rpc/context;context"; +option java_multiple_files = true; +option java_outer_classname = "AuditContextProto"; +option java_package = "com.google.rpc.context"; + +// `AuditContext` provides information that is needed for audit logging. +message AuditContext { + // Serialized audit log. + bytes audit_log = 1; + + // An API request message that is scrubbed based on the method annotation. + // This field should only be filled if audit_log field is present. + // Service Control will use this to assemble a complete log for Cloud Audit + // Logs and Google internal audit logs. + google.protobuf.Struct scrubbed_request = 2; + + // An API response message that is scrubbed based on the method annotation. + // This field should only be filled if audit_log field is present. + // Service Control will use this to assemble a complete log for Cloud Audit + // Logs and Google internal audit logs. + google.protobuf.Struct scrubbed_response = 3; + + // Number of scrubbed response items. + int32 scrubbed_response_item_count = 4; + + // Audit resource name which is scrubbed. + string target_resource = 5; +} diff --git a/venv/lib/python3.12/site-packages/google/rpc/context/audit_context_pb2.py b/venv/lib/python3.12/site-packages/google/rpc/context/audit_context_pb2.py new file mode 100644 index 0000000..99bcf60 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/context/audit_context_pb2.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/rpc/context/audit_context.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 33, + 5, + "", + "google/rpc/context/audit_context.proto", +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n&google/rpc/context/audit_context.proto\x12\x12google.rpc.context\x1a\x1cgoogle/protobuf/struct.proto"\xc7\x01\n\x0c\x41uditContext\x12\x11\n\taudit_log\x18\x01 \x01(\x0c\x12\x31\n\x10scrubbed_request\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x32\n\x11scrubbed_response\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12$\n\x1cscrubbed_response_item_count\x18\x04 \x01(\x05\x12\x17\n\x0ftarget_resource\x18\x05 \x01(\tBh\n\x16\x63om.google.rpc.contextB\x11\x41uditContextProtoP\x01Z9google.golang.org/genproto/googleapis/rpc/context;contextb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.rpc.context.audit_context_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\026com.google.rpc.contextB\021AuditContextProtoP\001Z9google.golang.org/genproto/googleapis/rpc/context;context" + _globals["_AUDITCONTEXT"]._serialized_start = 93 + _globals["_AUDITCONTEXT"]._serialized_end = 292 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/rpc/context/audit_context_pb2.pyi b/venv/lib/python3.12/site-packages/google/rpc/context/audit_context_pb2.pyi new file mode 100644 index 0000000..720be02 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/context/audit_context_pb2.pyi @@ -0,0 +1,51 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import struct_pb2 as _struct_pb2 + +DESCRIPTOR: _descriptor.FileDescriptor + +class AuditContext(_message.Message): + __slots__ = ( + "audit_log", + "scrubbed_request", + "scrubbed_response", + "scrubbed_response_item_count", + "target_resource", + ) + AUDIT_LOG_FIELD_NUMBER: _ClassVar[int] + SCRUBBED_REQUEST_FIELD_NUMBER: _ClassVar[int] + SCRUBBED_RESPONSE_FIELD_NUMBER: _ClassVar[int] + SCRUBBED_RESPONSE_ITEM_COUNT_FIELD_NUMBER: _ClassVar[int] + TARGET_RESOURCE_FIELD_NUMBER: _ClassVar[int] + audit_log: bytes + scrubbed_request: _struct_pb2.Struct + scrubbed_response: _struct_pb2.Struct + scrubbed_response_item_count: int + target_resource: str + def __init__( + self, + audit_log: _Optional[bytes] = ..., + scrubbed_request: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., + scrubbed_response: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., + scrubbed_response_item_count: _Optional[int] = ..., + target_resource: _Optional[str] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/rpc/error_details.proto b/venv/lib/python3.12/site-packages/google/rpc/error_details.proto new file mode 100644 index 0000000..43b7d04 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/error_details.proto @@ -0,0 +1,363 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +import "google/protobuf/duration.proto"; + +option go_package = "google.golang.org/genproto/googleapis/rpc/errdetails;errdetails"; +option java_multiple_files = true; +option java_outer_classname = "ErrorDetailsProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// Describes the cause of the error with structured details. +// +// Example of an error when contacting the "pubsub.googleapis.com" API when it +// is not enabled: +// +// { "reason": "API_DISABLED" +// "domain": "googleapis.com" +// "metadata": { +// "resource": "projects/123", +// "service": "pubsub.googleapis.com" +// } +// } +// +// This response indicates that the pubsub.googleapis.com API is not enabled. +// +// Example of an error that is returned when attempting to create a Spanner +// instance in a region that is out of stock: +// +// { "reason": "STOCKOUT" +// "domain": "spanner.googleapis.com", +// "metadata": { +// "availableRegions": "us-central1,us-east2" +// } +// } +message ErrorInfo { + // The reason of the error. This is a constant value that identifies the + // proximate cause of the error. Error reasons are unique within a particular + // domain of errors. This should be at most 63 characters and match a + // regular expression of `[A-Z][A-Z0-9_]+[A-Z0-9]`, which represents + // UPPER_SNAKE_CASE. + string reason = 1; + + // The logical grouping to which the "reason" belongs. The error domain + // is typically the registered service name of the tool or product that + // generates the error. Example: "pubsub.googleapis.com". If the error is + // generated by some common infrastructure, the error domain must be a + // globally unique value that identifies the infrastructure. For Google API + // infrastructure, the error domain is "googleapis.com". + string domain = 2; + + // Additional structured details about this error. + // + // Keys must match a regular expression of `[a-z][a-zA-Z0-9-_]+` but should + // ideally be lowerCamelCase. Also, they must be limited to 64 characters in + // length. When identifying the current value of an exceeded limit, the units + // should be contained in the key, not the value. For example, rather than + // `{"instanceLimit": "100/request"}`, should be returned as, + // `{"instanceLimitPerRequest": "100"}`, if the client exceeds the number of + // instances that can be created in a single (batch) request. + map metadata = 3; +} + +// Describes when the clients can retry a failed request. Clients could ignore +// the recommendation here or retry when this information is missing from error +// responses. +// +// It's always recommended that clients should use exponential backoff when +// retrying. +// +// Clients should wait until `retry_delay` amount of time has passed since +// receiving the error response before retrying. If retrying requests also +// fail, clients should use an exponential backoff scheme to gradually increase +// the delay between retries based on `retry_delay`, until either a maximum +// number of retries have been reached or a maximum retry delay cap has been +// reached. +message RetryInfo { + // Clients should wait at least this long between retrying the same request. + google.protobuf.Duration retry_delay = 1; +} + +// Describes additional debugging info. +message DebugInfo { + // The stack trace entries indicating where the error occurred. + repeated string stack_entries = 1; + + // Additional debugging information provided by the server. + string detail = 2; +} + +// Describes how a quota check failed. +// +// For example if a daily limit was exceeded for the calling project, +// a service could respond with a QuotaFailure detail containing the project +// id and the description of the quota limit that was exceeded. If the +// calling project hasn't enabled the service in the developer console, then +// a service could respond with the project id and set `service_disabled` +// to true. +// +// Also see RetryInfo and Help types for other details about handling a +// quota failure. +message QuotaFailure { + // A message type used to describe a single quota violation. For example, a + // daily quota or a custom quota that was exceeded. + message Violation { + // The subject on which the quota check failed. + // For example, "clientip:" or "project:". + string subject = 1; + + // A description of how the quota check failed. Clients can use this + // description to find more about the quota configuration in the service's + // public documentation, or find the relevant quota limit to adjust through + // developer console. + // + // For example: "Service disabled" or "Daily Limit for read operations + // exceeded". + string description = 2; + + // The API Service from which the `QuotaFailure.Violation` orginates. In + // some cases, Quota issues originate from an API Service other than the one + // that was called. In other words, a dependency of the called API Service + // could be the cause of the `QuotaFailure`, and this field would have the + // dependency API service name. + // + // For example, if the called API is Kubernetes Engine API + // (container.googleapis.com), and a quota violation occurs in the + // Kubernetes Engine API itself, this field would be + // "container.googleapis.com". On the other hand, if the quota violation + // occurs when the Kubernetes Engine API creates VMs in the Compute Engine + // API (compute.googleapis.com), this field would be + // "compute.googleapis.com". + string api_service = 3; + + // The metric of the violated quota. A quota metric is a named counter to + // measure usage, such as API requests or CPUs. When an activity occurs in a + // service, such as Virtual Machine allocation, one or more quota metrics + // may be affected. + // + // For example, "compute.googleapis.com/cpus_per_vm_family", + // "storage.googleapis.com/internet_egress_bandwidth". + string quota_metric = 4; + + // The id of the violated quota. Also know as "limit name", this is the + // unique identifier of a quota in the context of an API service. + // + // For example, "CPUS-PER-VM-FAMILY-per-project-region". + string quota_id = 5; + + // The dimensions of the violated quota. Every non-global quota is enforced + // on a set of dimensions. While quota metric defines what to count, the + // dimensions specify for what aspects the counter should be increased. + // + // For example, the quota "CPUs per region per VM family" enforces a limit + // on the metric "compute.googleapis.com/cpus_per_vm_family" on dimensions + // "region" and "vm_family". And if the violation occurred in region + // "us-central1" and for VM family "n1", the quota_dimensions would be, + // + // { + // "region": "us-central1", + // "vm_family": "n1", + // } + // + // When a quota is enforced globally, the quota_dimensions would always be + // empty. + map quota_dimensions = 6; + + // The enforced quota value at the time of the `QuotaFailure`. + // + // For example, if the enforced quota value at the time of the + // `QuotaFailure` on the number of CPUs is "10", then the value of this + // field would reflect this quantity. + int64 quota_value = 7; + + // The new quota value being rolled out at the time of the violation. At the + // completion of the rollout, this value will be enforced in place of + // quota_value. If no rollout is in progress at the time of the violation, + // this field is not set. + // + // For example, if at the time of the violation a rollout is in progress + // changing the number of CPUs quota from 10 to 20, 20 would be the value of + // this field. + optional int64 future_quota_value = 8; + } + + // Describes all quota violations. + repeated Violation violations = 1; +} + +// Describes what preconditions have failed. +// +// For example, if an RPC failed because it required the Terms of Service to be +// acknowledged, it could list the terms of service violation in the +// PreconditionFailure message. +message PreconditionFailure { + // A message type used to describe a single precondition failure. + message Violation { + // The type of PreconditionFailure. We recommend using a service-specific + // enum type to define the supported precondition violation subjects. For + // example, "TOS" for "Terms of Service violation". + string type = 1; + + // The subject, relative to the type, that failed. + // For example, "google.com/cloud" relative to the "TOS" type would indicate + // which terms of service is being referenced. + string subject = 2; + + // A description of how the precondition failed. Developers can use this + // description to understand how to fix the failure. + // + // For example: "Terms of service not accepted". + string description = 3; + } + + // Describes all precondition violations. + repeated Violation violations = 1; +} + +// Describes violations in a client request. This error type focuses on the +// syntactic aspects of the request. +message BadRequest { + // A message type used to describe a single bad request field. + message FieldViolation { + // A path that leads to a field in the request body. The value will be a + // sequence of dot-separated identifiers that identify a protocol buffer + // field. + // + // Consider the following: + // + // message CreateContactRequest { + // message EmailAddress { + // enum Type { + // TYPE_UNSPECIFIED = 0; + // HOME = 1; + // WORK = 2; + // } + // + // optional string email = 1; + // repeated EmailType type = 2; + // } + // + // string full_name = 1; + // repeated EmailAddress email_addresses = 2; + // } + // + // In this example, in proto `field` could take one of the following values: + // + // * `full_name` for a violation in the `full_name` value + // * `email_addresses[0].email` for a violation in the `email` field of the + // first `email_addresses` message + // * `email_addresses[2].type[1]` for a violation in the second `type` + // value in the third `email_addresses` message. + // + // In JSON, the same values are represented as: + // + // * `fullName` for a violation in the `fullName` value + // * `emailAddresses[0].email` for a violation in the `email` field of the + // first `emailAddresses` message + // * `emailAddresses[2].type[1]` for a violation in the second `type` + // value in the third `emailAddresses` message. + string field = 1; + + // A description of why the request element is bad. + string description = 2; + + // The reason of the field-level error. This is a constant value that + // identifies the proximate cause of the field-level error. It should + // uniquely identify the type of the FieldViolation within the scope of the + // google.rpc.ErrorInfo.domain. This should be at most 63 + // characters and match a regular expression of `[A-Z][A-Z0-9_]+[A-Z0-9]`, + // which represents UPPER_SNAKE_CASE. + string reason = 3; + + // Provides a localized error message for field-level errors that is safe to + // return to the API consumer. + LocalizedMessage localized_message = 4; + } + + // Describes all violations in a client request. + repeated FieldViolation field_violations = 1; +} + +// Contains metadata about the request that clients can attach when filing a bug +// or providing other forms of feedback. +message RequestInfo { + // An opaque string that should only be interpreted by the service generating + // it. For example, it can be used to identify requests in the service's logs. + string request_id = 1; + + // Any data that was used to serve this request. For example, an encrypted + // stack trace that can be sent back to the service provider for debugging. + string serving_data = 2; +} + +// Describes the resource that is being accessed. +message ResourceInfo { + // A name for the type of resource being accessed, e.g. "sql table", + // "cloud storage bucket", "file", "Google calendar"; or the type URL + // of the resource: e.g. "type.googleapis.com/google.pubsub.v1.Topic". + string resource_type = 1; + + // The name of the resource being accessed. For example, a shared calendar + // name: "example.com_4fghdhgsrgh@group.calendar.google.com", if the current + // error is + // [google.rpc.Code.PERMISSION_DENIED][google.rpc.Code.PERMISSION_DENIED]. + string resource_name = 2; + + // The owner of the resource (optional). + // For example, "user:" or "project:". + string owner = 3; + + // Describes what error is encountered when accessing this resource. + // For example, updating a cloud project may require the `writer` permission + // on the developer console project. + string description = 4; +} + +// Provides links to documentation or for performing an out of band action. +// +// For example, if a quota check failed with an error indicating the calling +// project hasn't enabled the accessed service, this can contain a URL pointing +// directly to the right place in the developer console to flip the bit. +message Help { + // Describes a URL link. + message Link { + // Describes what the link offers. + string description = 1; + + // The URL of the link. + string url = 2; + } + + // URL(s) pointing to additional information on handling the current error. + repeated Link links = 1; +} + +// Provides a localized error message that is safe to return to the user +// which can be attached to an RPC error. +message LocalizedMessage { + // The locale used following the specification defined at + // https://www.rfc-editor.org/rfc/bcp/bcp47.txt. + // Examples are: "en-US", "fr-CH", "es-MX" + string locale = 1; + + // The localized error message in the above locale. + string message = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/rpc/error_details_pb2.py b/venv/lib/python3.12/site-packages/google/rpc/error_details_pb2.py new file mode 100644 index 0000000..1038e8c --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/error_details_pb2.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/rpc/error_details.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/rpc/error_details.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1egoogle/rpc/error_details.proto\x12\ngoogle.rpc\x1a\x1egoogle/protobuf/duration.proto"\x93\x01\n\tErrorInfo\x12\x0e\n\x06reason\x18\x01 \x01(\t\x12\x0e\n\x06\x64omain\x18\x02 \x01(\t\x12\x35\n\x08metadata\x18\x03 \x03(\x0b\x32#.google.rpc.ErrorInfo.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01";\n\tRetryInfo\x12.\n\x0bretry_delay\x18\x01 \x01(\x0b\x32\x19.google.protobuf.Duration"2\n\tDebugInfo\x12\x15\n\rstack_entries\x18\x01 \x03(\t\x12\x0e\n\x06\x64\x65tail\x18\x02 \x01(\t"\x8f\x03\n\x0cQuotaFailure\x12\x36\n\nviolations\x18\x01 \x03(\x0b\x32".google.rpc.QuotaFailure.Violation\x1a\xc6\x02\n\tViolation\x12\x0f\n\x07subject\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x13\n\x0b\x61pi_service\x18\x03 \x01(\t\x12\x14\n\x0cquota_metric\x18\x04 \x01(\t\x12\x10\n\x08quota_id\x18\x05 \x01(\t\x12Q\n\x10quota_dimensions\x18\x06 \x03(\x0b\x32\x37.google.rpc.QuotaFailure.Violation.QuotaDimensionsEntry\x12\x13\n\x0bquota_value\x18\x07 \x01(\x03\x12\x1f\n\x12\x66uture_quota_value\x18\x08 \x01(\x03H\x00\x88\x01\x01\x1a\x36\n\x14QuotaDimensionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x15\n\x13_future_quota_value"\x95\x01\n\x13PreconditionFailure\x12=\n\nviolations\x18\x01 \x03(\x0b\x32).google.rpc.PreconditionFailure.Violation\x1a?\n\tViolation\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07subject\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t"\xcc\x01\n\nBadRequest\x12?\n\x10\x66ield_violations\x18\x01 \x03(\x0b\x32%.google.rpc.BadRequest.FieldViolation\x1a}\n\x0e\x46ieldViolation\x12\r\n\x05\x66ield\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x37\n\x11localized_message\x18\x04 \x01(\x0b\x32\x1c.google.rpc.LocalizedMessage"7\n\x0bRequestInfo\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x14\n\x0cserving_data\x18\x02 \x01(\t"`\n\x0cResourceInfo\x12\x15\n\rresource_type\x18\x01 \x01(\t\x12\x15\n\rresource_name\x18\x02 \x01(\t\x12\r\n\x05owner\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t"V\n\x04Help\x12$\n\x05links\x18\x01 \x03(\x0b\x32\x15.google.rpc.Help.Link\x1a(\n\x04Link\x12\x13\n\x0b\x64\x65scription\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t"3\n\x10LocalizedMessage\x12\x0e\n\x06locale\x18\x01 \x01(\t\x12\x0f\n\x07message\x18\x02 \x01(\tBl\n\x0e\x63om.google.rpcB\x11\x45rrorDetailsProtoP\x01Z?google.golang.org/genproto/googleapis/rpc/errdetails;errdetails\xa2\x02\x03RPCb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.rpc.error_details_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.rpcB\021ErrorDetailsProtoP\001Z?google.golang.org/genproto/googleapis/rpc/errdetails;errdetails\242\002\003RPC" + _globals["_ERRORINFO_METADATAENTRY"]._loaded_options = None + _globals["_ERRORINFO_METADATAENTRY"]._serialized_options = b"8\001" + _globals["_QUOTAFAILURE_VIOLATION_QUOTADIMENSIONSENTRY"]._loaded_options = None + _globals[ + "_QUOTAFAILURE_VIOLATION_QUOTADIMENSIONSENTRY" + ]._serialized_options = b"8\001" + _globals["_ERRORINFO"]._serialized_start = 79 + _globals["_ERRORINFO"]._serialized_end = 226 + _globals["_ERRORINFO_METADATAENTRY"]._serialized_start = 179 + _globals["_ERRORINFO_METADATAENTRY"]._serialized_end = 226 + _globals["_RETRYINFO"]._serialized_start = 228 + _globals["_RETRYINFO"]._serialized_end = 287 + _globals["_DEBUGINFO"]._serialized_start = 289 + _globals["_DEBUGINFO"]._serialized_end = 339 + _globals["_QUOTAFAILURE"]._serialized_start = 342 + _globals["_QUOTAFAILURE"]._serialized_end = 741 + _globals["_QUOTAFAILURE_VIOLATION"]._serialized_start = 415 + _globals["_QUOTAFAILURE_VIOLATION"]._serialized_end = 741 + _globals["_QUOTAFAILURE_VIOLATION_QUOTADIMENSIONSENTRY"]._serialized_start = 664 + _globals["_QUOTAFAILURE_VIOLATION_QUOTADIMENSIONSENTRY"]._serialized_end = 718 + _globals["_PRECONDITIONFAILURE"]._serialized_start = 744 + _globals["_PRECONDITIONFAILURE"]._serialized_end = 893 + _globals["_PRECONDITIONFAILURE_VIOLATION"]._serialized_start = 830 + _globals["_PRECONDITIONFAILURE_VIOLATION"]._serialized_end = 893 + _globals["_BADREQUEST"]._serialized_start = 896 + _globals["_BADREQUEST"]._serialized_end = 1100 + _globals["_BADREQUEST_FIELDVIOLATION"]._serialized_start = 975 + _globals["_BADREQUEST_FIELDVIOLATION"]._serialized_end = 1100 + _globals["_REQUESTINFO"]._serialized_start = 1102 + _globals["_REQUESTINFO"]._serialized_end = 1157 + _globals["_RESOURCEINFO"]._serialized_start = 1159 + _globals["_RESOURCEINFO"]._serialized_end = 1255 + _globals["_HELP"]._serialized_start = 1257 + _globals["_HELP"]._serialized_end = 1343 + _globals["_HELP_LINK"]._serialized_start = 1303 + _globals["_HELP_LINK"]._serialized_end = 1343 + _globals["_LOCALIZEDMESSAGE"]._serialized_start = 1345 + _globals["_LOCALIZEDMESSAGE"]._serialized_end = 1396 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/rpc/error_details_pb2.pyi b/venv/lib/python3.12/site-packages/google/rpc/error_details_pb2.pyi new file mode 100644 index 0000000..d581ac7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/error_details_pb2.pyi @@ -0,0 +1,250 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import duration_pb2 as _duration_pb2 +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class ErrorInfo(_message.Message): + __slots__ = ("reason", "domain", "metadata") + class MetadataEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + REASON_FIELD_NUMBER: _ClassVar[int] + DOMAIN_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + reason: str + domain: str + metadata: _containers.ScalarMap[str, str] + def __init__( + self, + reason: _Optional[str] = ..., + domain: _Optional[str] = ..., + metadata: _Optional[_Mapping[str, str]] = ..., + ) -> None: ... + +class RetryInfo(_message.Message): + __slots__ = ("retry_delay",) + RETRY_DELAY_FIELD_NUMBER: _ClassVar[int] + retry_delay: _duration_pb2.Duration + def __init__( + self, + retry_delay: _Optional[ + _Union[datetime.timedelta, _duration_pb2.Duration, _Mapping] + ] = ..., + ) -> None: ... + +class DebugInfo(_message.Message): + __slots__ = ("stack_entries", "detail") + STACK_ENTRIES_FIELD_NUMBER: _ClassVar[int] + DETAIL_FIELD_NUMBER: _ClassVar[int] + stack_entries: _containers.RepeatedScalarFieldContainer[str] + detail: str + def __init__( + self, + stack_entries: _Optional[_Iterable[str]] = ..., + detail: _Optional[str] = ..., + ) -> None: ... + +class QuotaFailure(_message.Message): + __slots__ = ("violations",) + class Violation(_message.Message): + __slots__ = ( + "subject", + "description", + "api_service", + "quota_metric", + "quota_id", + "quota_dimensions", + "quota_value", + "future_quota_value", + ) + class QuotaDimensionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... + + SUBJECT_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + API_SERVICE_FIELD_NUMBER: _ClassVar[int] + QUOTA_METRIC_FIELD_NUMBER: _ClassVar[int] + QUOTA_ID_FIELD_NUMBER: _ClassVar[int] + QUOTA_DIMENSIONS_FIELD_NUMBER: _ClassVar[int] + QUOTA_VALUE_FIELD_NUMBER: _ClassVar[int] + FUTURE_QUOTA_VALUE_FIELD_NUMBER: _ClassVar[int] + subject: str + description: str + api_service: str + quota_metric: str + quota_id: str + quota_dimensions: _containers.ScalarMap[str, str] + quota_value: int + future_quota_value: int + def __init__( + self, + subject: _Optional[str] = ..., + description: _Optional[str] = ..., + api_service: _Optional[str] = ..., + quota_metric: _Optional[str] = ..., + quota_id: _Optional[str] = ..., + quota_dimensions: _Optional[_Mapping[str, str]] = ..., + quota_value: _Optional[int] = ..., + future_quota_value: _Optional[int] = ..., + ) -> None: ... + + VIOLATIONS_FIELD_NUMBER: _ClassVar[int] + violations: _containers.RepeatedCompositeFieldContainer[QuotaFailure.Violation] + def __init__( + self, + violations: _Optional[ + _Iterable[_Union[QuotaFailure.Violation, _Mapping]] + ] = ..., + ) -> None: ... + +class PreconditionFailure(_message.Message): + __slots__ = ("violations",) + class Violation(_message.Message): + __slots__ = ("type", "subject", "description") + TYPE_FIELD_NUMBER: _ClassVar[int] + SUBJECT_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + type: str + subject: str + description: str + def __init__( + self, + type: _Optional[str] = ..., + subject: _Optional[str] = ..., + description: _Optional[str] = ..., + ) -> None: ... + + VIOLATIONS_FIELD_NUMBER: _ClassVar[int] + violations: _containers.RepeatedCompositeFieldContainer[ + PreconditionFailure.Violation + ] + def __init__( + self, + violations: _Optional[ + _Iterable[_Union[PreconditionFailure.Violation, _Mapping]] + ] = ..., + ) -> None: ... + +class BadRequest(_message.Message): + __slots__ = ("field_violations",) + class FieldViolation(_message.Message): + __slots__ = ("field", "description", "reason", "localized_message") + FIELD_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + REASON_FIELD_NUMBER: _ClassVar[int] + LOCALIZED_MESSAGE_FIELD_NUMBER: _ClassVar[int] + field: str + description: str + reason: str + localized_message: LocalizedMessage + def __init__( + self, + field: _Optional[str] = ..., + description: _Optional[str] = ..., + reason: _Optional[str] = ..., + localized_message: _Optional[_Union[LocalizedMessage, _Mapping]] = ..., + ) -> None: ... + + FIELD_VIOLATIONS_FIELD_NUMBER: _ClassVar[int] + field_violations: _containers.RepeatedCompositeFieldContainer[ + BadRequest.FieldViolation + ] + def __init__( + self, + field_violations: _Optional[ + _Iterable[_Union[BadRequest.FieldViolation, _Mapping]] + ] = ..., + ) -> None: ... + +class RequestInfo(_message.Message): + __slots__ = ("request_id", "serving_data") + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + SERVING_DATA_FIELD_NUMBER: _ClassVar[int] + request_id: str + serving_data: str + def __init__( + self, request_id: _Optional[str] = ..., serving_data: _Optional[str] = ... + ) -> None: ... + +class ResourceInfo(_message.Message): + __slots__ = ("resource_type", "resource_name", "owner", "description") + RESOURCE_TYPE_FIELD_NUMBER: _ClassVar[int] + RESOURCE_NAME_FIELD_NUMBER: _ClassVar[int] + OWNER_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + resource_type: str + resource_name: str + owner: str + description: str + def __init__( + self, + resource_type: _Optional[str] = ..., + resource_name: _Optional[str] = ..., + owner: _Optional[str] = ..., + description: _Optional[str] = ..., + ) -> None: ... + +class Help(_message.Message): + __slots__ = ("links",) + class Link(_message.Message): + __slots__ = ("description", "url") + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + URL_FIELD_NUMBER: _ClassVar[int] + description: str + url: str + def __init__( + self, description: _Optional[str] = ..., url: _Optional[str] = ... + ) -> None: ... + + LINKS_FIELD_NUMBER: _ClassVar[int] + links: _containers.RepeatedCompositeFieldContainer[Help.Link] + def __init__( + self, links: _Optional[_Iterable[_Union[Help.Link, _Mapping]]] = ... + ) -> None: ... + +class LocalizedMessage(_message.Message): + __slots__ = ("locale", "message") + LOCALE_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + locale: str + message: str + def __init__( + self, locale: _Optional[str] = ..., message: _Optional[str] = ... + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/rpc/http.proto b/venv/lib/python3.12/site-packages/google/rpc/http.proto new file mode 100644 index 0000000..9d97b60 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/http.proto @@ -0,0 +1,64 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +option go_package = "google.golang.org/genproto/googleapis/rpc/http;http"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// Represents an HTTP request. +message HttpRequest { + // The HTTP request method. + string method = 1; + + // The HTTP request URI. + string uri = 2; + + // The HTTP request headers. The ordering of the headers is significant. + // Multiple headers with the same key may present for the request. + repeated HttpHeader headers = 3; + + // The HTTP request body. If the body is not expected, it should be empty. + bytes body = 4; +} + +// Represents an HTTP response. +message HttpResponse { + // The HTTP status code, such as 200 or 404. + int32 status = 1; + + // The HTTP reason phrase, such as "OK" or "Not Found". + string reason = 2; + + // The HTTP response headers. The ordering of the headers is significant. + // Multiple headers with the same key may present for the response. + repeated HttpHeader headers = 3; + + // The HTTP response body. If the body is not expected, it should be empty. + bytes body = 4; +} + +// Represents an HTTP header. +message HttpHeader { + // The HTTP header key. It is case insensitive. + string key = 1; + + // The HTTP header value. + string value = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/rpc/http_pb2.py b/venv/lib/python3.12/site-packages/google/rpc/http_pb2.py new file mode 100644 index 0000000..3aa1fa8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/http_pb2.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/rpc/http.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/rpc/http.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x15google/rpc/http.proto\x12\ngoogle.rpc"a\n\x0bHttpRequest\x12\x0e\n\x06method\x18\x01 \x01(\t\x12\x0b\n\x03uri\x18\x02 \x01(\t\x12\'\n\x07headers\x18\x03 \x03(\x0b\x32\x16.google.rpc.HttpHeader\x12\x0c\n\x04\x62ody\x18\x04 \x01(\x0c"e\n\x0cHttpResponse\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\'\n\x07headers\x18\x03 \x03(\x0b\x32\x16.google.rpc.HttpHeader\x12\x0c\n\x04\x62ody\x18\x04 \x01(\x0c"(\n\nHttpHeader\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\tBX\n\x0e\x63om.google.rpcB\tHttpProtoP\x01Z3google.golang.org/genproto/googleapis/rpc/http;http\xa2\x02\x03RPCb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.rpc.http_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.rpcB\tHttpProtoP\001Z3google.golang.org/genproto/googleapis/rpc/http;http\242\002\003RPC" + _globals["_HTTPREQUEST"]._serialized_start = 37 + _globals["_HTTPREQUEST"]._serialized_end = 134 + _globals["_HTTPRESPONSE"]._serialized_start = 136 + _globals["_HTTPRESPONSE"]._serialized_end = 237 + _globals["_HTTPHEADER"]._serialized_start = 239 + _globals["_HTTPHEADER"]._serialized_end = 279 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/rpc/http_pb2.pyi b/venv/lib/python3.12/site-packages/google/rpc/http_pb2.pyi new file mode 100644 index 0000000..69f680b --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/http_pb2.pyi @@ -0,0 +1,71 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class HttpRequest(_message.Message): + __slots__ = ("method", "uri", "headers", "body") + METHOD_FIELD_NUMBER: _ClassVar[int] + URI_FIELD_NUMBER: _ClassVar[int] + HEADERS_FIELD_NUMBER: _ClassVar[int] + BODY_FIELD_NUMBER: _ClassVar[int] + method: str + uri: str + headers: _containers.RepeatedCompositeFieldContainer[HttpHeader] + body: bytes + def __init__( + self, + method: _Optional[str] = ..., + uri: _Optional[str] = ..., + headers: _Optional[_Iterable[_Union[HttpHeader, _Mapping]]] = ..., + body: _Optional[bytes] = ..., + ) -> None: ... + +class HttpResponse(_message.Message): + __slots__ = ("status", "reason", "headers", "body") + STATUS_FIELD_NUMBER: _ClassVar[int] + REASON_FIELD_NUMBER: _ClassVar[int] + HEADERS_FIELD_NUMBER: _ClassVar[int] + BODY_FIELD_NUMBER: _ClassVar[int] + status: int + reason: str + headers: _containers.RepeatedCompositeFieldContainer[HttpHeader] + body: bytes + def __init__( + self, + status: _Optional[int] = ..., + reason: _Optional[str] = ..., + headers: _Optional[_Iterable[_Union[HttpHeader, _Mapping]]] = ..., + body: _Optional[bytes] = ..., + ) -> None: ... + +class HttpHeader(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__( + self, key: _Optional[str] = ..., value: _Optional[str] = ... + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/rpc/status.proto b/venv/lib/python3.12/site-packages/google/rpc/status.proto new file mode 100644 index 0000000..97f50b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/status.proto @@ -0,0 +1,48 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +import "google/protobuf/any.proto"; + +option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; +option java_multiple_files = true; +option java_outer_classname = "StatusProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// The `Status` type defines a logical error model that is suitable for +// different programming environments, including REST APIs and RPC APIs. It is +// used by [gRPC](https://github.com/grpc). Each `Status` message contains +// three pieces of data: error code, error message, and error details. +// +// You can find out more about this error model and how to work with it in the +// [API Design Guide](https://cloud.google.com/apis/design/errors). +message Status { + // The status code, which should be an enum value of + // [google.rpc.Code][google.rpc.Code]. + int32 code = 1; + + // A developer-facing error message, which should be in English. Any + // user-facing error message should be localized and sent in the + // [google.rpc.Status.details][google.rpc.Status.details] field, or localized + // by the client. + string message = 2; + + // A list of messages that carry the error details. There is a common set of + // message types for APIs to use. + repeated google.protobuf.Any details = 3; +} diff --git a/venv/lib/python3.12/site-packages/google/rpc/status_pb2.py b/venv/lib/python3.12/site-packages/google/rpc/status_pb2.py new file mode 100644 index 0000000..9b3b782 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/status_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/rpc/status.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/rpc/status.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x17google/rpc/status.proto\x12\ngoogle.rpc\x1a\x19google/protobuf/any.proto"N\n\x06Status\x12\x0c\n\x04\x63ode\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\x12%\n\x07\x64\x65tails\x18\x03 \x03(\x0b\x32\x14.google.protobuf.AnyB^\n\x0e\x63om.google.rpcB\x0bStatusProtoP\x01Z7google.golang.org/genproto/googleapis/rpc/status;status\xa2\x02\x03RPCb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.rpc.status_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\016com.google.rpcB\013StatusProtoP\001Z7google.golang.org/genproto/googleapis/rpc/status;status\242\002\003RPC" + _globals["_STATUS"]._serialized_start = 66 + _globals["_STATUS"]._serialized_end = 144 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/rpc/status_pb2.pyi b/venv/lib/python3.12/site-packages/google/rpc/status_pb2.pyi new file mode 100644 index 0000000..c24374a --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/rpc/status_pb2.pyi @@ -0,0 +1,41 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import any_pb2 as _any_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class Status(_message.Message): + __slots__ = ("code", "message", "details") + CODE_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + DETAILS_FIELD_NUMBER: _ClassVar[int] + code: int + message: str + details: _containers.RepeatedCompositeFieldContainer[_any_pb2.Any] + def __init__( + self, + code: _Optional[int] = ..., + message: _Optional[str] = ..., + details: _Optional[_Iterable[_Union[_any_pb2.Any, _Mapping]]] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/calendar_period_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/calendar_period_pb2.cpython-312.pyc new file mode 100644 index 0000000..03fce4c Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/calendar_period_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/color_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/color_pb2.cpython-312.pyc new file mode 100644 index 0000000..f3d9475 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/color_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/date_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/date_pb2.cpython-312.pyc new file mode 100644 index 0000000..72bdd08 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/date_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/datetime_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/datetime_pb2.cpython-312.pyc new file mode 100644 index 0000000..65e8819 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/datetime_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/dayofweek_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/dayofweek_pb2.cpython-312.pyc new file mode 100644 index 0000000..f6cdcd6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/dayofweek_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/decimal_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/decimal_pb2.cpython-312.pyc new file mode 100644 index 0000000..845d78f Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/decimal_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/expr_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/expr_pb2.cpython-312.pyc new file mode 100644 index 0000000..2a5b879 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/expr_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/fraction_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/fraction_pb2.cpython-312.pyc new file mode 100644 index 0000000..d3c1dd4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/fraction_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/interval_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/interval_pb2.cpython-312.pyc new file mode 100644 index 0000000..4ef3a46 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/interval_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/latlng_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/latlng_pb2.cpython-312.pyc new file mode 100644 index 0000000..8d802a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/latlng_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/localized_text_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/localized_text_pb2.cpython-312.pyc new file mode 100644 index 0000000..65474e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/localized_text_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/money_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/money_pb2.cpython-312.pyc new file mode 100644 index 0000000..e5cc6bb Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/money_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/month_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/month_pb2.cpython-312.pyc new file mode 100644 index 0000000..c15f747 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/month_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/phone_number_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/phone_number_pb2.cpython-312.pyc new file mode 100644 index 0000000..bcbc367 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/phone_number_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/postal_address_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/postal_address_pb2.cpython-312.pyc new file mode 100644 index 0000000..651b1ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/postal_address_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/quaternion_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/quaternion_pb2.cpython-312.pyc new file mode 100644 index 0000000..aceb647 Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/quaternion_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/__pycache__/timeofday_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/google/type/__pycache__/timeofday_pb2.cpython-312.pyc new file mode 100644 index 0000000..1c4304c Binary files /dev/null and b/venv/lib/python3.12/site-packages/google/type/__pycache__/timeofday_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/google/type/calendar_period.proto b/venv/lib/python3.12/site-packages/google/type/calendar_period.proto new file mode 100644 index 0000000..ae08aeb --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/calendar_period.proto @@ -0,0 +1,56 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/calendarperiod;calendarperiod"; +option java_multiple_files = true; +option java_outer_classname = "CalendarPeriodProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// A `CalendarPeriod` represents the abstract concept of a time period that has +// a canonical start. Grammatically, "the start of the current +// `CalendarPeriod`." All calendar times begin at midnight UTC. +enum CalendarPeriod { + // Undefined period, raises an error. + CALENDAR_PERIOD_UNSPECIFIED = 0; + + // A day. + DAY = 1; + + // A week. Weeks begin on Monday, following + // [ISO 8601](https://en.wikipedia.org/wiki/ISO_week_date). + WEEK = 2; + + // A fortnight. The first calendar fortnight of the year begins at the start + // of week 1 according to + // [ISO 8601](https://en.wikipedia.org/wiki/ISO_week_date). + FORTNIGHT = 3; + + // A month. + MONTH = 4; + + // A quarter. Quarters start on dates 1-Jan, 1-Apr, 1-Jul, and 1-Oct of each + // year. + QUARTER = 5; + + // A half-year. Half-years start on dates 1-Jan and 1-Jul. + HALF = 6; + + // A year. + YEAR = 7; +} diff --git a/venv/lib/python3.12/site-packages/google/type/calendar_period_pb2.py b/venv/lib/python3.12/site-packages/google/type/calendar_period_pb2.py new file mode 100644 index 0000000..02abe1b --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/calendar_period_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/calendar_period.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/calendar_period.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n!google/type/calendar_period.proto\x12\x0bgoogle.type*\x7f\n\x0e\x43\x61lendarPeriod\x12\x1f\n\x1b\x43\x41LENDAR_PERIOD_UNSPECIFIED\x10\x00\x12\x07\n\x03\x44\x41Y\x10\x01\x12\x08\n\x04WEEK\x10\x02\x12\r\n\tFORTNIGHT\x10\x03\x12\t\n\x05MONTH\x10\x04\x12\x0b\n\x07QUARTER\x10\x05\x12\x08\n\x04HALF\x10\x06\x12\x08\n\x04YEAR\x10\x07\x42x\n\x0f\x63om.google.typeB\x13\x43\x61lendarPeriodProtoP\x01ZHgoogle.golang.org/genproto/googleapis/type/calendarperiod;calendarperiod\xa2\x02\x03GTPb\x06proto3" +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.type.calendar_period_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\023CalendarPeriodProtoP\001ZHgoogle.golang.org/genproto/googleapis/type/calendarperiod;calendarperiod\242\002\003GTP" + _globals["_CALENDARPERIOD"]._serialized_start = 50 + _globals["_CALENDARPERIOD"]._serialized_end = 177 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/calendar_period_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/calendar_period_pb2.pyi new file mode 100644 index 0000000..2e230b4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/calendar_period_pb2.pyi @@ -0,0 +1,40 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar + +from google.protobuf import descriptor as _descriptor +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor + +class CalendarPeriod(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + CALENDAR_PERIOD_UNSPECIFIED: _ClassVar[CalendarPeriod] + DAY: _ClassVar[CalendarPeriod] + WEEK: _ClassVar[CalendarPeriod] + FORTNIGHT: _ClassVar[CalendarPeriod] + MONTH: _ClassVar[CalendarPeriod] + QUARTER: _ClassVar[CalendarPeriod] + HALF: _ClassVar[CalendarPeriod] + YEAR: _ClassVar[CalendarPeriod] + +CALENDAR_PERIOD_UNSPECIFIED: CalendarPeriod +DAY: CalendarPeriod +WEEK: CalendarPeriod +FORTNIGHT: CalendarPeriod +MONTH: CalendarPeriod +QUARTER: CalendarPeriod +HALF: CalendarPeriod +YEAR: CalendarPeriod diff --git a/venv/lib/python3.12/site-packages/google/type/color.proto b/venv/lib/python3.12/site-packages/google/type/color.proto new file mode 100644 index 0000000..c48d064 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/color.proto @@ -0,0 +1,173 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +import "google/protobuf/wrappers.proto"; + +option go_package = "google.golang.org/genproto/googleapis/type/color;color"; +option java_multiple_files = true; +option java_outer_classname = "ColorProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents a color in the RGBA color space. This representation is designed +// for simplicity of conversion to and from color representations in various +// languages over compactness. For example, the fields of this representation +// can be trivially provided to the constructor of `java.awt.Color` in Java; it +// can also be trivially provided to UIColor's `+colorWithRed:green:blue:alpha` +// method in iOS; and, with just a little work, it can be easily formatted into +// a CSS `rgba()` string in JavaScript. +// +// This reference page doesn't have information about the absolute color +// space that should be used to interpret the RGB value—for example, sRGB, +// Adobe RGB, +// DCI-P3, and BT.2020. By default, applications should assume the sRGB color +// space. +// +// When color equality needs to be decided, implementations, unless documented +// otherwise, treat two colors as equal if all their red, green, blue, and alpha +// values each differ by at most `1e-5`. +// +// Example (Java): +// +// import com.google.type.Color; +// +// // ... +// public static java.awt.Color fromProto(Color protocolor) { +// float alpha = protocolor.hasAlpha() +// ? protocolor.getAlpha().getValue() +// : 1.0; +// +// return new java.awt.Color( +// protocolor.getRed(), +// protocolor.getGreen(), +// protocolor.getBlue(), +// alpha); +// } +// +// public static Color toProto(java.awt.Color color) { +// float red = (float) color.getRed(); +// float green = (float) color.getGreen(); +// float blue = (float) color.getBlue(); +// float denominator = 255.0; +// Color.Builder resultBuilder = +// Color +// .newBuilder() +// .setRed(red / denominator) +// .setGreen(green / denominator) +// .setBlue(blue / denominator); +// int alpha = color.getAlpha(); +// if (alpha != 255) { +// result.setAlpha( +// FloatValue +// .newBuilder() +// .setValue(((float) alpha) / denominator) +// .build()); +// } +// return resultBuilder.build(); +// } +// // ... +// +// Example (iOS / Obj-C): +// +// // ... +// static UIColor* fromProto(Color* protocolor) { +// float red = [protocolor red]; +// float green = [protocolor green]; +// float blue = [protocolor blue]; +// FloatValue* alpha_wrapper = [protocolor alpha]; +// float alpha = 1.0; +// if (alpha_wrapper != nil) { +// alpha = [alpha_wrapper value]; +// } +// return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; +// } +// +// static Color* toProto(UIColor* color) { +// CGFloat red, green, blue, alpha; +// if (![color getRed:&red green:&green blue:&blue alpha:&alpha]) { +// return nil; +// } +// Color* result = [[Color alloc] init]; +// [result setRed:red]; +// [result setGreen:green]; +// [result setBlue:blue]; +// if (alpha <= 0.9999) { +// [result setAlpha:floatWrapperWithValue(alpha)]; +// } +// [result autorelease]; +// return result; +// } +// // ... +// +// Example (JavaScript): +// +// // ... +// +// var protoToCssColor = function(rgb_color) { +// var redFrac = rgb_color.red || 0.0; +// var greenFrac = rgb_color.green || 0.0; +// var blueFrac = rgb_color.blue || 0.0; +// var red = Math.floor(redFrac * 255); +// var green = Math.floor(greenFrac * 255); +// var blue = Math.floor(blueFrac * 255); +// +// if (!('alpha' in rgb_color)) { +// return rgbToCssColor(red, green, blue); +// } +// +// var alphaFrac = rgb_color.alpha.value || 0.0; +// var rgbParams = [red, green, blue].join(','); +// return ['rgba(', rgbParams, ',', alphaFrac, ')'].join(''); +// }; +// +// var rgbToCssColor = function(red, green, blue) { +// var rgbNumber = new Number((red << 16) | (green << 8) | blue); +// var hexString = rgbNumber.toString(16); +// var missingZeros = 6 - hexString.length; +// var resultBuilder = ['#']; +// for (var i = 0; i < missingZeros; i++) { +// resultBuilder.push('0'); +// } +// resultBuilder.push(hexString); +// return resultBuilder.join(''); +// }; +// +// // ... +message Color { + // The amount of red in the color as a value in the interval [0, 1]. + float red = 1; + + // The amount of green in the color as a value in the interval [0, 1]. + float green = 2; + + // The amount of blue in the color as a value in the interval [0, 1]. + float blue = 3; + + // The fraction of this color that should be applied to the pixel. That is, + // the final pixel color is defined by the equation: + // + // `pixel color = alpha * (this color) + (1.0 - alpha) * (background color)` + // + // This means that a value of 1.0 corresponds to a solid color, whereas + // a value of 0.0 corresponds to a completely transparent color. This + // uses a wrapper message rather than a simple float scalar so that it is + // possible to distinguish between a default value and the value being unset. + // If omitted, this color object is rendered as a solid color + // (as if the alpha value had been explicitly given a value of 1.0). + google.protobuf.FloatValue alpha = 4; +} diff --git a/venv/lib/python3.12/site-packages/google/type/color_pb2.py b/venv/lib/python3.12/site-packages/google/type/color_pb2.py new file mode 100644 index 0000000..d73d21c --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/color_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/color.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/color.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import wrappers_pb2 as google_dot_protobuf_dot_wrappers__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x17google/type/color.proto\x12\x0bgoogle.type\x1a\x1egoogle/protobuf/wrappers.proto"]\n\x05\x43olor\x12\x0b\n\x03red\x18\x01 \x01(\x02\x12\r\n\x05green\x18\x02 \x01(\x02\x12\x0c\n\x04\x62lue\x18\x03 \x01(\x02\x12*\n\x05\x61lpha\x18\x04 \x01(\x0b\x32\x1b.google.protobuf.FloatValueB]\n\x0f\x63om.google.typeB\nColorProtoP\x01Z6google.golang.org/genproto/googleapis/type/color;color\xa2\x02\x03GTPb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.type.color_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\nColorProtoP\001Z6google.golang.org/genproto/googleapis/type/color;color\242\002\003GTP" + _globals["_COLOR"]._serialized_start = 72 + _globals["_COLOR"]._serialized_end = 165 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/color_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/color_pb2.pyi new file mode 100644 index 0000000..c80242e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/color_pb2.pyi @@ -0,0 +1,42 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import wrappers_pb2 as _wrappers_pb2 + +DESCRIPTOR: _descriptor.FileDescriptor + +class Color(_message.Message): + __slots__ = ("red", "green", "blue", "alpha") + RED_FIELD_NUMBER: _ClassVar[int] + GREEN_FIELD_NUMBER: _ClassVar[int] + BLUE_FIELD_NUMBER: _ClassVar[int] + ALPHA_FIELD_NUMBER: _ClassVar[int] + red: float + green: float + blue: float + alpha: _wrappers_pb2.FloatValue + def __init__( + self, + red: _Optional[float] = ..., + green: _Optional[float] = ..., + blue: _Optional[float] = ..., + alpha: _Optional[_Union[_wrappers_pb2.FloatValue, _Mapping]] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/date.proto b/venv/lib/python3.12/site-packages/google/type/date.proto new file mode 100644 index 0000000..12be7e1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/date.proto @@ -0,0 +1,54 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/date;date"; +option java_multiple_files = true; +option java_outer_classname = "DateProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents a whole or partial calendar date, such as a birthday. The time of +// day and time zone are either specified elsewhere or are insignificant. The +// date is relative to the Gregorian Calendar. This can represent one of the +// following: +// +// * A full date, with non-zero year, month, and day values. +// * A month and day, with a zero year (for example, an anniversary). +// * A year on its own, with a zero month and a zero day. +// * A year and month, with a zero day (for example, a credit card expiration +// date). +// +// Related types: +// +// * [google.type.TimeOfDay][google.type.TimeOfDay] +// * [google.type.DateTime][google.type.DateTime] +// * [google.protobuf.Timestamp][google.protobuf.Timestamp] +message Date { + // Year of the date. Must be from 1 to 9999, or 0 to specify a date without + // a year. + int32 year = 1; + + // Month of a year. Must be from 1 to 12, or 0 to specify a year without a + // month and day. + int32 month = 2; + + // Day of a month. Must be from 1 to 31 and valid for the year and month, or 0 + // to specify a year by itself or a year and month where the day isn't + // significant. + int32 day = 3; +} diff --git a/venv/lib/python3.12/site-packages/google/type/date_pb2.py b/venv/lib/python3.12/site-packages/google/type/date_pb2.py new file mode 100644 index 0000000..04c118e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/date_pb2.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/date.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/date.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x16google/type/date.proto\x12\x0bgoogle.type"0\n\x04\x44\x61te\x12\x0c\n\x04year\x18\x01 \x01(\x05\x12\r\n\x05month\x18\x02 \x01(\x05\x12\x0b\n\x03\x64\x61y\x18\x03 \x01(\x05\x42Z\n\x0f\x63om.google.typeB\tDateProtoP\x01Z4google.golang.org/genproto/googleapis/type/date;date\xa2\x02\x03GTPb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.type.date_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\tDateProtoP\001Z4google.golang.org/genproto/googleapis/type/date;date\242\002\003GTP" + _globals["_DATE"]._serialized_start = 39 + _globals["_DATE"]._serialized_end = 87 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/date_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/date_pb2.pyi new file mode 100644 index 0000000..fcaf2f9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/date_pb2.pyi @@ -0,0 +1,36 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message + +DESCRIPTOR: _descriptor.FileDescriptor + +class Date(_message.Message): + __slots__ = ("year", "month", "day") + YEAR_FIELD_NUMBER: _ClassVar[int] + MONTH_FIELD_NUMBER: _ClassVar[int] + DAY_FIELD_NUMBER: _ClassVar[int] + year: int + month: int + day: int + def __init__( + self, + year: _Optional[int] = ..., + month: _Optional[int] = ..., + day: _Optional[int] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/datetime.proto b/venv/lib/python3.12/site-packages/google/type/datetime.proto new file mode 100644 index 0000000..b81e4f4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/datetime.proto @@ -0,0 +1,104 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +import "google/protobuf/duration.proto"; + +option go_package = "google.golang.org/genproto/googleapis/type/datetime;datetime"; +option java_multiple_files = true; +option java_outer_classname = "DateTimeProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents civil time (or occasionally physical time). +// +// This type can represent a civil time in one of a few possible ways: +// +// * When utc_offset is set and time_zone is unset: a civil time on a calendar +// day with a particular offset from UTC. +// * When time_zone is set and utc_offset is unset: a civil time on a calendar +// day in a particular time zone. +// * When neither time_zone nor utc_offset is set: a civil time on a calendar +// day in local time. +// +// The date is relative to the Proleptic Gregorian Calendar. +// +// If year, month, or day are 0, the DateTime is considered not to have a +// specific year, month, or day respectively. +// +// This type may also be used to represent a physical time if all the date and +// time fields are set and either case of the `time_offset` oneof is set. +// Consider using `Timestamp` message for physical time instead. If your use +// case also would like to store the user's timezone, that can be done in +// another field. +// +// This type is more flexible than some applications may want. Make sure to +// document and validate your application's limitations. +message DateTime { + // Optional. Year of date. Must be from 1 to 9999, or 0 if specifying a + // datetime without a year. + int32 year = 1; + + // Optional. Month of year. Must be from 1 to 12, or 0 if specifying a + // datetime without a month. + int32 month = 2; + + // Optional. Day of month. Must be from 1 to 31 and valid for the year and + // month, or 0 if specifying a datetime without a day. + int32 day = 3; + + // Optional. Hours of day in 24 hour format. Should be from 0 to 23, defaults + // to 0 (midnight). An API may choose to allow the value "24:00:00" for + // scenarios like business closing time. + int32 hours = 4; + + // Optional. Minutes of hour of day. Must be from 0 to 59, defaults to 0. + int32 minutes = 5; + + // Optional. Seconds of minutes of the time. Must normally be from 0 to 59, + // defaults to 0. An API may allow the value 60 if it allows leap-seconds. + int32 seconds = 6; + + // Optional. Fractions of seconds in nanoseconds. Must be from 0 to + // 999,999,999, defaults to 0. + int32 nanos = 7; + + // Optional. Specifies either the UTC offset or the time zone of the DateTime. + // Choose carefully between them, considering that time zone data may change + // in the future (for example, a country modifies their DST start/end dates, + // and future DateTimes in the affected range had already been stored). + // If omitted, the DateTime is considered to be in local time. + oneof time_offset { + // UTC offset. Must be whole seconds, between -18 hours and +18 hours. + // For example, a UTC offset of -4:00 would be represented as + // { seconds: -14400 }. + google.protobuf.Duration utc_offset = 8; + + // Time zone. + TimeZone time_zone = 9; + } +} + +// Represents a time zone from the +// [IANA Time Zone Database](https://www.iana.org/time-zones). +message TimeZone { + // IANA Time Zone Database time zone. For example "America/New_York". + string id = 1; + + // Optional. IANA Time Zone Database version number. For example "2019a". + string version = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/type/datetime_pb2.py b/venv/lib/python3.12/site-packages/google/type/datetime_pb2.py new file mode 100644 index 0000000..4de61cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/datetime_pb2.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/datetime.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/datetime.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1agoogle/type/datetime.proto\x12\x0bgoogle.type\x1a\x1egoogle/protobuf/duration.proto"\xe0\x01\n\x08\x44\x61teTime\x12\x0c\n\x04year\x18\x01 \x01(\x05\x12\r\n\x05month\x18\x02 \x01(\x05\x12\x0b\n\x03\x64\x61y\x18\x03 \x01(\x05\x12\r\n\x05hours\x18\x04 \x01(\x05\x12\x0f\n\x07minutes\x18\x05 \x01(\x05\x12\x0f\n\x07seconds\x18\x06 \x01(\x05\x12\r\n\x05nanos\x18\x07 \x01(\x05\x12/\n\nutc_offset\x18\x08 \x01(\x0b\x32\x19.google.protobuf.DurationH\x00\x12*\n\ttime_zone\x18\t \x01(\x0b\x32\x15.google.type.TimeZoneH\x00\x42\r\n\x0btime_offset"\'\n\x08TimeZone\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\tBf\n\x0f\x63om.google.typeB\rDateTimeProtoP\x01Z None: ... + +class TimeZone(_message.Message): + __slots__ = ("id", "version") + ID_FIELD_NUMBER: _ClassVar[int] + VERSION_FIELD_NUMBER: _ClassVar[int] + id: str + version: str + def __init__( + self, id: _Optional[str] = ..., version: _Optional[str] = ... + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/dayofweek.proto b/venv/lib/python3.12/site-packages/google/type/dayofweek.proto new file mode 100644 index 0000000..669d894 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/dayofweek.proto @@ -0,0 +1,50 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/dayofweek;dayofweek"; +option java_multiple_files = true; +option java_outer_classname = "DayOfWeekProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents a day of the week. +enum DayOfWeek { + // The day of the week is unspecified. + DAY_OF_WEEK_UNSPECIFIED = 0; + + // Monday + MONDAY = 1; + + // Tuesday + TUESDAY = 2; + + // Wednesday + WEDNESDAY = 3; + + // Thursday + THURSDAY = 4; + + // Friday + FRIDAY = 5; + + // Saturday + SATURDAY = 6; + + // Sunday + SUNDAY = 7; +} diff --git a/venv/lib/python3.12/site-packages/google/type/dayofweek_pb2.py b/venv/lib/python3.12/site-packages/google/type/dayofweek_pb2.py new file mode 100644 index 0000000..aa23df4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/dayofweek_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/dayofweek.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/dayofweek.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n\x1bgoogle/type/dayofweek.proto\x12\x0bgoogle.type*\x84\x01\n\tDayOfWeek\x12\x1b\n\x17\x44\x41Y_OF_WEEK_UNSPECIFIED\x10\x00\x12\n\n\x06MONDAY\x10\x01\x12\x0b\n\x07TUESDAY\x10\x02\x12\r\n\tWEDNESDAY\x10\x03\x12\x0c\n\x08THURSDAY\x10\x04\x12\n\n\x06\x46RIDAY\x10\x05\x12\x0c\n\x08SATURDAY\x10\x06\x12\n\n\x06SUNDAY\x10\x07\x42i\n\x0f\x63om.google.typeB\x0e\x44\x61yOfWeekProtoP\x01Z>google.golang.org/genproto/googleapis/type/dayofweek;dayofweek\xa2\x02\x03GTPb\x06proto3" +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.type.dayofweek_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\016DayOfWeekProtoP\001Z>google.golang.org/genproto/googleapis/type/dayofweek;dayofweek\242\002\003GTP" + _globals["_DAYOFWEEK"]._serialized_start = 45 + _globals["_DAYOFWEEK"]._serialized_end = 177 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/dayofweek_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/dayofweek_pb2.pyi new file mode 100644 index 0000000..34c6770 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/dayofweek_pb2.pyi @@ -0,0 +1,40 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar + +from google.protobuf import descriptor as _descriptor +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor + +class DayOfWeek(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + DAY_OF_WEEK_UNSPECIFIED: _ClassVar[DayOfWeek] + MONDAY: _ClassVar[DayOfWeek] + TUESDAY: _ClassVar[DayOfWeek] + WEDNESDAY: _ClassVar[DayOfWeek] + THURSDAY: _ClassVar[DayOfWeek] + FRIDAY: _ClassVar[DayOfWeek] + SATURDAY: _ClassVar[DayOfWeek] + SUNDAY: _ClassVar[DayOfWeek] + +DAY_OF_WEEK_UNSPECIFIED: DayOfWeek +MONDAY: DayOfWeek +TUESDAY: DayOfWeek +WEDNESDAY: DayOfWeek +THURSDAY: DayOfWeek +FRIDAY: DayOfWeek +SATURDAY: DayOfWeek +SUNDAY: DayOfWeek diff --git a/venv/lib/python3.12/site-packages/google/type/decimal.proto b/venv/lib/python3.12/site-packages/google/type/decimal.proto new file mode 100644 index 0000000..353e14b --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/decimal.proto @@ -0,0 +1,93 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/decimal;decimal"; +option java_multiple_files = true; +option java_outer_classname = "DecimalProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// A representation of a decimal value, such as 2.5. Clients may convert values +// into language-native decimal formats, such as Java's +// [BigDecimal](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/math/BigDecimal.html) +// or Python's +// [decimal.Decimal](https://docs.python.org/3/library/decimal.html). +message Decimal { + // The decimal value, as a string. + // + // The string representation consists of an optional sign, `+` (`U+002B`) + // or `-` (`U+002D`), followed by a sequence of zero or more decimal digits + // ("the integer"), optionally followed by a fraction, optionally followed + // by an exponent. An empty string **should** be interpreted as `0`. + // + // The fraction consists of a decimal point followed by zero or more decimal + // digits. The string must contain at least one digit in either the integer + // or the fraction. The number formed by the sign, the integer and the + // fraction is referred to as the significand. + // + // The exponent consists of the character `e` (`U+0065`) or `E` (`U+0045`) + // followed by one or more decimal digits. + // + // Services **should** normalize decimal values before storing them by: + // + // - Removing an explicitly-provided `+` sign (`+2.5` -> `2.5`). + // - Replacing a zero-length integer value with `0` (`.5` -> `0.5`). + // - Coercing the exponent character to upper-case, with explicit sign + // (`2.5e8` -> `2.5E+8`). + // - Removing an explicitly-provided zero exponent (`2.5E0` -> `2.5`). + // + // Services **may** perform additional normalization based on its own needs + // and the internal decimal implementation selected, such as shifting the + // decimal point and exponent value together (example: `2.5E-1` <-> `0.25`). + // Additionally, services **may** preserve trailing zeroes in the fraction + // to indicate increased precision, but are not required to do so. + // + // Note that only the `.` character is supported to divide the integer + // and the fraction; `,` **should not** be supported regardless of locale. + // Additionally, thousand separators **should not** be supported. If a + // service does support them, values **must** be normalized. + // + // The ENBF grammar is: + // + // DecimalString = + // '' | [Sign] Significand [Exponent]; + // + // Sign = '+' | '-'; + // + // Significand = + // Digits ['.'] [Digits] | [Digits] '.' Digits; + // + // Exponent = ('e' | 'E') [Sign] Digits; + // + // Digits = { '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' }; + // + // Services **should** clearly document the range of supported values, the + // maximum supported precision (total number of digits), and, if applicable, + // the scale (number of digits after the decimal point), as well as how it + // behaves when receiving out-of-bounds values. + // + // Services **may** choose to accept values passed as input even when the + // value has a higher precision or scale than the service supports, and + // **should** round the value to fit the supported scale. Alternatively, the + // service **may** error with `400 Bad Request` (`INVALID_ARGUMENT` in gRPC) + // if precision would be lost. + // + // Services **should** error with `400 Bad Request` (`INVALID_ARGUMENT` in + // gRPC) if the service receives a value outside of the supported range. + string value = 1; +} diff --git a/venv/lib/python3.12/site-packages/google/type/decimal_pb2.py b/venv/lib/python3.12/site-packages/google/type/decimal_pb2.py new file mode 100644 index 0000000..581c05b --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/decimal_pb2.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/decimal.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/decimal.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x19google/type/decimal.proto\x12\x0bgoogle.type"\x18\n\x07\x44\x65\x63imal\x12\r\n\x05value\x18\x01 \x01(\tBc\n\x0f\x63om.google.typeB\x0c\x44\x65\x63imalProtoP\x01Z:google.golang.org/genproto/googleapis/type/decimal;decimal\xa2\x02\x03GTPb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.type.decimal_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\014DecimalProtoP\001Z:google.golang.org/genproto/googleapis/type/decimal;decimal\242\002\003GTP" + _globals["_DECIMAL"]._serialized_start = 42 + _globals["_DECIMAL"]._serialized_end = 66 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/decimal_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/decimal_pb2.pyi new file mode 100644 index 0000000..95f43e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/decimal_pb2.pyi @@ -0,0 +1,27 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message + +DESCRIPTOR: _descriptor.FileDescriptor + +class Decimal(_message.Message): + __slots__ = ("value",) + VALUE_FIELD_NUMBER: _ClassVar[int] + value: str + def __init__(self, value: _Optional[str] = ...) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/expr.proto b/venv/lib/python3.12/site-packages/google/type/expr.proto new file mode 100644 index 0000000..2fca5cd --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/expr.proto @@ -0,0 +1,73 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/expr;expr"; +option java_multiple_files = true; +option java_outer_classname = "ExprProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents a textual expression in the Common Expression Language (CEL) +// syntax. CEL is a C-like expression language. The syntax and semantics of CEL +// are documented at https://github.com/google/cel-spec. +// +// Example (Comparison): +// +// title: "Summary size limit" +// description: "Determines if a summary is less than 100 chars" +// expression: "document.summary.size() < 100" +// +// Example (Equality): +// +// title: "Requestor is owner" +// description: "Determines if requestor is the document owner" +// expression: "document.owner == request.auth.claims.email" +// +// Example (Logic): +// +// title: "Public documents" +// description: "Determine whether the document should be publicly visible" +// expression: "document.type != 'private' && document.type != 'internal'" +// +// Example (Data Manipulation): +// +// title: "Notification string" +// description: "Create a notification string with a timestamp." +// expression: "'New message received at ' + string(document.create_time)" +// +// The exact variables and functions that may be referenced within an expression +// are determined by the service that evaluates it. See the service +// documentation for additional information. +message Expr { + // Textual representation of an expression in Common Expression Language + // syntax. + string expression = 1; + + // Optional. Title for the expression, i.e. a short string describing + // its purpose. This can be used e.g. in UIs which allow to enter the + // expression. + string title = 2; + + // Optional. Description of the expression. This is a longer text which + // describes the expression, e.g. when hovered over it in a UI. + string description = 3; + + // Optional. String indicating the location of the expression for error + // reporting, e.g. a file name and a position in the file. + string location = 4; +} diff --git a/venv/lib/python3.12/site-packages/google/type/expr_pb2.py b/venv/lib/python3.12/site-packages/google/type/expr_pb2.py new file mode 100644 index 0000000..0c4ec73 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/expr_pb2.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/expr.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/expr.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x16google/type/expr.proto\x12\x0bgoogle.type"P\n\x04\x45xpr\x12\x12\n\nexpression\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x10\n\x08location\x18\x04 \x01(\tBZ\n\x0f\x63om.google.typeB\tExprProtoP\x01Z4google.golang.org/genproto/googleapis/type/expr;expr\xa2\x02\x03GTPb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.type.expr_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\tExprProtoP\001Z4google.golang.org/genproto/googleapis/type/expr;expr\242\002\003GTP" + _globals["_EXPR"]._serialized_start = 39 + _globals["_EXPR"]._serialized_end = 119 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/expr_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/expr_pb2.pyi new file mode 100644 index 0000000..a1a5566 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/expr_pb2.pyi @@ -0,0 +1,39 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message + +DESCRIPTOR: _descriptor.FileDescriptor + +class Expr(_message.Message): + __slots__ = ("expression", "title", "description", "location") + EXPRESSION_FIELD_NUMBER: _ClassVar[int] + TITLE_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + LOCATION_FIELD_NUMBER: _ClassVar[int] + expression: str + title: str + description: str + location: str + def __init__( + self, + expression: _Optional[str] = ..., + title: _Optional[str] = ..., + description: _Optional[str] = ..., + location: _Optional[str] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/fraction.proto b/venv/lib/python3.12/site-packages/google/type/fraction.proto new file mode 100644 index 0000000..72f90b3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/fraction.proto @@ -0,0 +1,33 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/fraction;fraction"; +option java_multiple_files = true; +option java_outer_classname = "FractionProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents a fraction in terms of a numerator divided by a denominator. +message Fraction { + // The numerator in the fraction, e.g. 2 in 2/3. + int64 numerator = 1; + + // The value by which the numerator is divided, e.g. 3 in 2/3. Must be + // positive. + int64 denominator = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/type/fraction_pb2.py b/venv/lib/python3.12/site-packages/google/type/fraction_pb2.py new file mode 100644 index 0000000..2d20334 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/fraction_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/fraction.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/fraction.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1agoogle/type/fraction.proto\x12\x0bgoogle.type"2\n\x08\x46raction\x12\x11\n\tnumerator\x18\x01 \x01(\x03\x12\x13\n\x0b\x64\x65nominator\x18\x02 \x01(\x03\x42\x66\n\x0f\x63om.google.typeB\rFractionProtoP\x01Z None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/interval.proto b/venv/lib/python3.12/site-packages/google/type/interval.proto new file mode 100644 index 0000000..8e00128 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/interval.proto @@ -0,0 +1,45 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +import "google/protobuf/timestamp.proto"; + +option go_package = "google.golang.org/genproto/googleapis/type/interval;interval"; +option java_multiple_files = true; +option java_outer_classname = "IntervalProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents a time interval, encoded as a Timestamp start (inclusive) and a +// Timestamp end (exclusive). +// +// The start must be less than or equal to the end. +// When the start equals the end, the interval is empty (matches no time). +// When both start and end are unspecified, the interval matches any time. +message Interval { + // Optional. Inclusive start of the interval. + // + // If specified, a Timestamp matching this interval will have to be the same + // or after the start. + google.protobuf.Timestamp start_time = 1; + + // Optional. Exclusive end of the interval. + // + // If specified, a Timestamp matching this interval will have to be before the + // end. + google.protobuf.Timestamp end_time = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/type/interval_pb2.py b/venv/lib/python3.12/site-packages/google/type/interval_pb2.py new file mode 100644 index 0000000..3ea37fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/interval_pb2.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/interval.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/interval.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1agoogle/type/interval.proto\x12\x0bgoogle.type\x1a\x1fgoogle/protobuf/timestamp.proto"h\n\x08Interval\x12.\n\nstart_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08\x65nd_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.TimestampBf\n\x0f\x63om.google.typeB\rIntervalProtoP\x01Z None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/latlng.proto b/venv/lib/python3.12/site-packages/google/type/latlng.proto new file mode 100644 index 0000000..2866058 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/latlng.proto @@ -0,0 +1,36 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/latlng;latlng"; +option java_multiple_files = true; +option java_outer_classname = "LatLngProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// An object that represents a latitude/longitude pair. This is expressed as a +// pair of doubles to represent degrees latitude and degrees longitude. Unless +// specified otherwise, this object must conform to the +// +// WGS84 standard. Values must be within normalized ranges. +message LatLng { + // The latitude in degrees. It must be in the range [-90.0, +90.0]. + double latitude = 1; + + // The longitude in degrees. It must be in the range [-180.0, +180.0]. + double longitude = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/type/latlng_pb2.py b/venv/lib/python3.12/site-packages/google/type/latlng_pb2.py new file mode 100644 index 0000000..2af4e41 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/latlng_pb2.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/latlng.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/latlng.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x18google/type/latlng.proto\x12\x0bgoogle.type"-\n\x06LatLng\x12\x10\n\x08latitude\x18\x01 \x01(\x01\x12\x11\n\tlongitude\x18\x02 \x01(\x01\x42`\n\x0f\x63om.google.typeB\x0bLatLngProtoP\x01Z8google.golang.org/genproto/googleapis/type/latlng;latlng\xa2\x02\x03GTPb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.type.latlng_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\013LatLngProtoP\001Z8google.golang.org/genproto/googleapis/type/latlng;latlng\242\002\003GTP" + _globals["_LATLNG"]._serialized_start = 41 + _globals["_LATLNG"]._serialized_end = 86 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/latlng_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/latlng_pb2.pyi new file mode 100644 index 0000000..033dd88 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/latlng_pb2.pyi @@ -0,0 +1,31 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message + +DESCRIPTOR: _descriptor.FileDescriptor + +class LatLng(_message.Message): + __slots__ = ("latitude", "longitude") + LATITUDE_FIELD_NUMBER: _ClassVar[int] + LONGITUDE_FIELD_NUMBER: _ClassVar[int] + latitude: float + longitude: float + def __init__( + self, latitude: _Optional[float] = ..., longitude: _Optional[float] = ... + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/localized_text.proto b/venv/lib/python3.12/site-packages/google/type/localized_text.proto new file mode 100644 index 0000000..d3f2c2c --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/localized_text.proto @@ -0,0 +1,36 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/localized_text;localized_text"; +option java_multiple_files = true; +option java_outer_classname = "LocalizedTextProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Localized variant of a text in a particular language. +message LocalizedText { + // Localized string in the language corresponding to + // [language_code][google.type.LocalizedText.language_code] below. + string text = 1; + + // The text's BCP-47 language code, such as "en-US" or "sr-Latn". + // + // For more information, see + // http://www.unicode.org/reports/tr35/#Unicode_locale_identifier. + string language_code = 2; +} diff --git a/venv/lib/python3.12/site-packages/google/type/localized_text_pb2.py b/venv/lib/python3.12/site-packages/google/type/localized_text_pb2.py new file mode 100644 index 0000000..00361d3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/localized_text_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/localized_text.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/localized_text.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n google/type/localized_text.proto\x12\x0bgoogle.type"4\n\rLocalizedText\x12\x0c\n\x04text\x18\x01 \x01(\t\x12\x15\n\rlanguage_code\x18\x02 \x01(\tBw\n\x0f\x63om.google.typeB\x12LocalizedTextProtoP\x01ZHgoogle.golang.org/genproto/googleapis/type/localized_text;localized_text\xa2\x02\x03GTPb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.type.localized_text_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\022LocalizedTextProtoP\001ZHgoogle.golang.org/genproto/googleapis/type/localized_text;localized_text\242\002\003GTP" + _globals["_LOCALIZEDTEXT"]._serialized_start = 49 + _globals["_LOCALIZEDTEXT"]._serialized_end = 101 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/localized_text_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/localized_text_pb2.pyi new file mode 100644 index 0000000..f67aa6d --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/localized_text_pb2.pyi @@ -0,0 +1,31 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message + +DESCRIPTOR: _descriptor.FileDescriptor + +class LocalizedText(_message.Message): + __slots__ = ("text", "language_code") + TEXT_FIELD_NUMBER: _ClassVar[int] + LANGUAGE_CODE_FIELD_NUMBER: _ClassVar[int] + text: str + language_code: str + def __init__( + self, text: _Optional[str] = ..., language_code: _Optional[str] = ... + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/money.proto b/venv/lib/python3.12/site-packages/google/type/money.proto new file mode 100644 index 0000000..ed0d790 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/money.proto @@ -0,0 +1,41 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/money;money"; +option java_multiple_files = true; +option java_outer_classname = "MoneyProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents an amount of money with its currency type. +message Money { + // The three-letter currency code defined in ISO 4217. + string currency_code = 1; + + // The whole units of the amount. + // For example if `currencyCode` is `"USD"`, then 1 unit is one US dollar. + int64 units = 2; + + // Number of nano (10^-9) units of the amount. + // The value must be between -999,999,999 and +999,999,999 inclusive. + // If `units` is positive, `nanos` must be positive or zero. + // If `units` is zero, `nanos` can be positive, zero, or negative. + // If `units` is negative, `nanos` must be negative or zero. + // For example $-1.75 is represented as `units`=-1 and `nanos`=-750,000,000. + int32 nanos = 3; +} diff --git a/venv/lib/python3.12/site-packages/google/type/money_pb2.py b/venv/lib/python3.12/site-packages/google/type/money_pb2.py new file mode 100644 index 0000000..4256b3a --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/money_pb2.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/money.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/money.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x17google/type/money.proto\x12\x0bgoogle.type"<\n\x05Money\x12\x15\n\rcurrency_code\x18\x01 \x01(\t\x12\r\n\x05units\x18\x02 \x01(\x03\x12\r\n\x05nanos\x18\x03 \x01(\x05\x42]\n\x0f\x63om.google.typeB\nMoneyProtoP\x01Z6google.golang.org/genproto/googleapis/type/money;money\xa2\x02\x03GTPb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.type.money_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\nMoneyProtoP\001Z6google.golang.org/genproto/googleapis/type/money;money\242\002\003GTP" + _globals["_MONEY"]._serialized_start = 40 + _globals["_MONEY"]._serialized_end = 100 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/money_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/money_pb2.pyi new file mode 100644 index 0000000..dd1c02b --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/money_pb2.pyi @@ -0,0 +1,36 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message + +DESCRIPTOR: _descriptor.FileDescriptor + +class Money(_message.Message): + __slots__ = ("currency_code", "units", "nanos") + CURRENCY_CODE_FIELD_NUMBER: _ClassVar[int] + UNITS_FIELD_NUMBER: _ClassVar[int] + NANOS_FIELD_NUMBER: _ClassVar[int] + currency_code: str + units: int + nanos: int + def __init__( + self, + currency_code: _Optional[str] = ..., + units: _Optional[int] = ..., + nanos: _Optional[int] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/month.proto b/venv/lib/python3.12/site-packages/google/type/month.proto new file mode 100644 index 0000000..f8a6630 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/month.proto @@ -0,0 +1,65 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/month;month"; +option java_multiple_files = true; +option java_outer_classname = "MonthProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents a month in the Gregorian calendar. +enum Month { + // The unspecified month. + MONTH_UNSPECIFIED = 0; + + // The month of January. + JANUARY = 1; + + // The month of February. + FEBRUARY = 2; + + // The month of March. + MARCH = 3; + + // The month of April. + APRIL = 4; + + // The month of May. + MAY = 5; + + // The month of June. + JUNE = 6; + + // The month of July. + JULY = 7; + + // The month of August. + AUGUST = 8; + + // The month of September. + SEPTEMBER = 9; + + // The month of October. + OCTOBER = 10; + + // The month of November. + NOVEMBER = 11; + + // The month of December. + DECEMBER = 12; +} diff --git a/venv/lib/python3.12/site-packages/google/type/month_pb2.py b/venv/lib/python3.12/site-packages/google/type/month_pb2.py new file mode 100644 index 0000000..b3d7ebc --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/month_pb2.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/month.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/month.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n\x17google/type/month.proto\x12\x0bgoogle.type*\xb0\x01\n\x05Month\x12\x15\n\x11MONTH_UNSPECIFIED\x10\x00\x12\x0b\n\x07JANUARY\x10\x01\x12\x0c\n\x08\x46\x45\x42RUARY\x10\x02\x12\t\n\x05MARCH\x10\x03\x12\t\n\x05\x41PRIL\x10\x04\x12\x07\n\x03MAY\x10\x05\x12\x08\n\x04JUNE\x10\x06\x12\x08\n\x04JULY\x10\x07\x12\n\n\x06\x41UGUST\x10\x08\x12\r\n\tSEPTEMBER\x10\t\x12\x0b\n\x07OCTOBER\x10\n\x12\x0c\n\x08NOVEMBER\x10\x0b\x12\x0c\n\x08\x44\x45\x43\x45MBER\x10\x0c\x42]\n\x0f\x63om.google.typeB\nMonthProtoP\x01Z6google.golang.org/genproto/googleapis/type/month;month\xa2\x02\x03GTPb\x06proto3" +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.type.month_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\nMonthProtoP\001Z6google.golang.org/genproto/googleapis/type/month;month\242\002\003GTP" + _globals["_MONTH"]._serialized_start = 41 + _globals["_MONTH"]._serialized_end = 217 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/month_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/month_pb2.pyi new file mode 100644 index 0000000..a5634ce --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/month_pb2.pyi @@ -0,0 +1,50 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar + +from google.protobuf import descriptor as _descriptor +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper + +DESCRIPTOR: _descriptor.FileDescriptor + +class Month(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + MONTH_UNSPECIFIED: _ClassVar[Month] + JANUARY: _ClassVar[Month] + FEBRUARY: _ClassVar[Month] + MARCH: _ClassVar[Month] + APRIL: _ClassVar[Month] + MAY: _ClassVar[Month] + JUNE: _ClassVar[Month] + JULY: _ClassVar[Month] + AUGUST: _ClassVar[Month] + SEPTEMBER: _ClassVar[Month] + OCTOBER: _ClassVar[Month] + NOVEMBER: _ClassVar[Month] + DECEMBER: _ClassVar[Month] + +MONTH_UNSPECIFIED: Month +JANUARY: Month +FEBRUARY: Month +MARCH: Month +APRIL: Month +MAY: Month +JUNE: Month +JULY: Month +AUGUST: Month +SEPTEMBER: Month +OCTOBER: Month +NOVEMBER: Month +DECEMBER: Month diff --git a/venv/lib/python3.12/site-packages/google/type/phone_number.proto b/venv/lib/python3.12/site-packages/google/type/phone_number.proto new file mode 100644 index 0000000..7c55be0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/phone_number.proto @@ -0,0 +1,118 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/phone_number;phone_number"; +option java_multiple_files = true; +option java_outer_classname = "PhoneNumberProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// An object representing a phone number, suitable as an API wire format. +// +// This representation: +// +// - should not be used for locale-specific formatting of a phone number, such +// as "+1 (650) 253-0000 ext. 123" +// +// - is not designed for efficient storage +// - may not be suitable for dialing - specialized libraries (see references) +// should be used to parse the number for that purpose +// +// To do something meaningful with this number, such as format it for various +// use-cases, convert it to an `i18n.phonenumbers.PhoneNumber` object first. +// +// For instance, in Java this would be: +// +// com.google.type.PhoneNumber wireProto = +// com.google.type.PhoneNumber.newBuilder().build(); +// com.google.i18n.phonenumbers.Phonenumber.PhoneNumber phoneNumber = +// PhoneNumberUtil.getInstance().parse(wireProto.getE164Number(), "ZZ"); +// if (!wireProto.getExtension().isEmpty()) { +// phoneNumber.setExtension(wireProto.getExtension()); +// } +// +// Reference(s): +// - https://github.com/google/libphonenumber +message PhoneNumber { + // An object representing a short code, which is a phone number that is + // typically much shorter than regular phone numbers and can be used to + // address messages in MMS and SMS systems, as well as for abbreviated dialing + // (For example "Text 611 to see how many minutes you have remaining on your + // plan."). + // + // Short codes are restricted to a region and are not internationally + // dialable, which means the same short code can exist in different regions, + // with different usage and pricing, even if those regions share the same + // country calling code (For example: US and CA). + message ShortCode { + // Required. The BCP-47 region code of the location where calls to this + // short code can be made, such as "US" and "BB". + // + // Reference(s): + // - http://www.unicode.org/reports/tr35/#unicode_region_subtag + string region_code = 1; + + // Required. The short code digits, without a leading plus ('+') or country + // calling code. For example "611". + string number = 2; + } + + // Required. Either a regular number, or a short code. New fields may be + // added to the oneof below in the future, so clients should ignore phone + // numbers for which none of the fields they coded against are set. + oneof kind { + // The phone number, represented as a leading plus sign ('+'), followed by a + // phone number that uses a relaxed ITU E.164 format consisting of the + // country calling code (1 to 3 digits) and the subscriber number, with no + // additional spaces or formatting. For example: + // + // - correct: "+15552220123" + // + // - incorrect: "+1 (555) 222-01234 x123" + // + // The ITU E.164 format limits the latter to 12 digits, but in practice not + // all countries respect that, so we relax that restriction here. + // National-only numbers are not allowed. + // + // References: + // + // - https://www.itu.int/rec/T-REC-E.164-201011-I + // + // - https://en.wikipedia.org/wiki/E.164. + // + // - https://en.wikipedia.org/wiki/List_of_country_calling_codes + string e164_number = 1; + + // A short code. + // + // Reference(s): + // - https://wikipedia.org/wiki/Short_code + ShortCode short_code = 2; + } + + // The phone number's extension. The extension is not standardized in ITU + // recommendations, except for being defined as a series of numbers with a + // maximum length of 40 digits. Other than digits, some other dialing + // characters such as ',' (indicating a wait) or '#' may be stored here. + // + // Note that no regions currently use extensions with short codes, so this + // field is normally only set in conjunction with an E.164 number. It is held + // separately from the E.164 number to allow for short code extensions in the + // future. + string extension = 3; +} diff --git a/venv/lib/python3.12/site-packages/google/type/phone_number_pb2.py b/venv/lib/python3.12/site-packages/google/type/phone_number_pb2.py new file mode 100644 index 0000000..6ead14d --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/phone_number_pb2.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/phone_number.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/phone_number.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1egoogle/type/phone_number.proto\x12\x0bgoogle.type"\xab\x01\n\x0bPhoneNumber\x12\x15\n\x0b\x65\x31\x36\x34_number\x18\x01 \x01(\tH\x00\x12\x38\n\nshort_code\x18\x02 \x01(\x0b\x32".google.type.PhoneNumber.ShortCodeH\x00\x12\x11\n\textension\x18\x03 \x01(\t\x1a\x30\n\tShortCode\x12\x13\n\x0bregion_code\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\tB\x06\n\x04kindBq\n\x0f\x63om.google.typeB\x10PhoneNumberProtoP\x01ZDgoogle.golang.org/genproto/googleapis/type/phone_number;phone_number\xa2\x02\x03GTPb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.type.phone_number_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\020PhoneNumberProtoP\001ZDgoogle.golang.org/genproto/googleapis/type/phone_number;phone_number\242\002\003GTP" + _globals["_PHONENUMBER"]._serialized_start = 48 + _globals["_PHONENUMBER"]._serialized_end = 219 + _globals["_PHONENUMBER_SHORTCODE"]._serialized_start = 163 + _globals["_PHONENUMBER_SHORTCODE"]._serialized_end = 211 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/phone_number_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/phone_number_pb2.pyi new file mode 100644 index 0000000..aae75ce --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/phone_number_pb2.pyi @@ -0,0 +1,48 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message + +DESCRIPTOR: _descriptor.FileDescriptor + +class PhoneNumber(_message.Message): + __slots__ = ("e164_number", "short_code", "extension") + class ShortCode(_message.Message): + __slots__ = ("region_code", "number") + REGION_CODE_FIELD_NUMBER: _ClassVar[int] + NUMBER_FIELD_NUMBER: _ClassVar[int] + region_code: str + number: str + def __init__( + self, region_code: _Optional[str] = ..., number: _Optional[str] = ... + ) -> None: ... + + E164_NUMBER_FIELD_NUMBER: _ClassVar[int] + SHORT_CODE_FIELD_NUMBER: _ClassVar[int] + EXTENSION_FIELD_NUMBER: _ClassVar[int] + e164_number: str + short_code: PhoneNumber.ShortCode + extension: str + def __init__( + self, + e164_number: _Optional[str] = ..., + short_code: _Optional[_Union[PhoneNumber.ShortCode, _Mapping]] = ..., + extension: _Optional[str] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/postal_address.proto b/venv/lib/python3.12/site-packages/google/type/postal_address.proto new file mode 100644 index 0000000..5705962 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/postal_address.proto @@ -0,0 +1,134 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/postaladdress;postaladdress"; +option java_multiple_files = true; +option java_outer_classname = "PostalAddressProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents a postal address, such as for postal delivery or payments +// addresses. With a postal address, a postal service can deliver items to a +// premise, P.O. box, or similar. A postal address is not intended to model +// geographical locations like roads, towns, or mountains. +// +// In typical usage, an address would be created by user input or from importing +// existing data, depending on the type of process. +// +// Advice on address input or editing: +// +// - Use an internationalization-ready address widget such as +// https://github.com/google/libaddressinput. +// - Users should not be presented with UI elements for input or editing of +// fields outside countries where that field is used. +// +// For more guidance on how to use this schema, see: +// https://support.google.com/business/answer/6397478. +message PostalAddress { + // The schema revision of the `PostalAddress`. This must be set to 0, which is + // the latest revision. + // + // All new revisions **must** be backward compatible with old revisions. + int32 revision = 1; + + // Required. CLDR region code of the country/region of the address. This + // is never inferred and it is up to the user to ensure the value is + // correct. See https://cldr.unicode.org/ and + // https://www.unicode.org/cldr/charts/30/supplemental/territory_information.html + // for details. Example: "CH" for Switzerland. + string region_code = 2; + + // Optional. BCP-47 language code of the contents of this address (if + // known). This is often the UI language of the input form or is expected + // to match one of the languages used in the address' country/region, or their + // transliterated equivalents. + // This can affect formatting in certain countries, but is not critical + // to the correctness of the data and will never affect any validation or + // other non-formatting related operations. + // + // If this value is not known, it should be omitted (rather than specifying a + // possibly incorrect default). + // + // Examples: "zh-Hant", "ja", "ja-Latn", "en". + string language_code = 3; + + // Optional. Postal code of the address. Not all countries use or require + // postal codes to be present, but where they are used, they may trigger + // additional validation with other parts of the address (for example, + // state or zip code validation in the United States). + string postal_code = 4; + + // Optional. Additional, country-specific, sorting code. This is not used + // in most regions. Where it is used, the value is either a string like + // "CEDEX", optionally followed by a number (for example, "CEDEX 7"), or just + // a number alone, representing the "sector code" (Jamaica), "delivery area + // indicator" (Malawi) or "post office indicator" (Côte d'Ivoire). + string sorting_code = 5; + + // Optional. Highest administrative subdivision which is used for postal + // addresses of a country or region. + // For example, this can be a state, a province, an oblast, or a prefecture. + // For Spain, this is the province and not the autonomous + // community (for example, "Barcelona" and not "Catalonia"). + // Many countries don't use an administrative area in postal addresses. For + // example, in Switzerland, this should be left unpopulated. + string administrative_area = 6; + + // Optional. Generally refers to the city or town portion of the address. + // Examples: US city, IT comune, UK post town. + // In regions of the world where localities are not well defined or do not fit + // into this structure well, leave `locality` empty and use `address_lines`. + string locality = 7; + + // Optional. Sublocality of the address. + // For example, this can be a neighborhood, borough, or district. + string sublocality = 8; + + // Unstructured address lines describing the lower levels of an address. + // + // Because values in `address_lines` do not have type information and may + // sometimes contain multiple values in a single field (for example, + // "Austin, TX"), it is important that the line order is clear. The order of + // address lines should be "envelope order" for the country or region of the + // address. In places where this can vary (for example, Japan), + // `address_language` is used to make it explicit (for example, "ja" for + // large-to-small ordering and "ja-Latn" or "en" for small-to-large). In this + // way, the most specific line of an address can be selected based on the + // language. + // + // The minimum permitted structural representation of an address consists + // of a `region_code` with all remaining information placed in the + // `address_lines`. It would be possible to format such an address very + // approximately without geocoding, but no semantic reasoning could be + // made about any of the address components until it was at least + // partially resolved. + // + // Creating an address only containing a `region_code` and `address_lines` and + // then geocoding is the recommended way to handle completely unstructured + // addresses (as opposed to guessing which parts of the address should be + // localities or administrative areas). + repeated string address_lines = 9; + + // Optional. The recipient at the address. + // This field may, under certain circumstances, contain multiline information. + // For example, it might contain "care of" information. + repeated string recipients = 10; + + // Optional. The name of the organization at the address. + string organization = 11; +} diff --git a/venv/lib/python3.12/site-packages/google/type/postal_address_pb2.py b/venv/lib/python3.12/site-packages/google/type/postal_address_pb2.py new file mode 100644 index 0000000..d3cdc8e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/postal_address_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/postal_address.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/postal_address.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n google/type/postal_address.proto\x12\x0bgoogle.type"\xfd\x01\n\rPostalAddress\x12\x10\n\x08revision\x18\x01 \x01(\x05\x12\x13\n\x0bregion_code\x18\x02 \x01(\t\x12\x15\n\rlanguage_code\x18\x03 \x01(\t\x12\x13\n\x0bpostal_code\x18\x04 \x01(\t\x12\x14\n\x0csorting_code\x18\x05 \x01(\t\x12\x1b\n\x13\x61\x64ministrative_area\x18\x06 \x01(\t\x12\x10\n\x08locality\x18\x07 \x01(\t\x12\x13\n\x0bsublocality\x18\x08 \x01(\t\x12\x15\n\raddress_lines\x18\t \x03(\t\x12\x12\n\nrecipients\x18\n \x03(\t\x12\x14\n\x0corganization\x18\x0b \x01(\tBu\n\x0f\x63om.google.typeB\x12PostalAddressProtoP\x01ZFgoogle.golang.org/genproto/googleapis/type/postaladdress;postaladdress\xa2\x02\x03GTPb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.type.postal_address_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\022PostalAddressProtoP\001ZFgoogle.golang.org/genproto/googleapis/type/postaladdress;postaladdress\242\002\003GTP" + _globals["_POSTALADDRESS"]._serialized_start = 50 + _globals["_POSTALADDRESS"]._serialized_end = 303 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/postal_address_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/postal_address_pb2.pyi new file mode 100644 index 0000000..946cbf7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/postal_address_pb2.pyi @@ -0,0 +1,74 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable as _Iterable +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class PostalAddress(_message.Message): + __slots__ = ( + "revision", + "region_code", + "language_code", + "postal_code", + "sorting_code", + "administrative_area", + "locality", + "sublocality", + "address_lines", + "recipients", + "organization", + ) + REVISION_FIELD_NUMBER: _ClassVar[int] + REGION_CODE_FIELD_NUMBER: _ClassVar[int] + LANGUAGE_CODE_FIELD_NUMBER: _ClassVar[int] + POSTAL_CODE_FIELD_NUMBER: _ClassVar[int] + SORTING_CODE_FIELD_NUMBER: _ClassVar[int] + ADMINISTRATIVE_AREA_FIELD_NUMBER: _ClassVar[int] + LOCALITY_FIELD_NUMBER: _ClassVar[int] + SUBLOCALITY_FIELD_NUMBER: _ClassVar[int] + ADDRESS_LINES_FIELD_NUMBER: _ClassVar[int] + RECIPIENTS_FIELD_NUMBER: _ClassVar[int] + ORGANIZATION_FIELD_NUMBER: _ClassVar[int] + revision: int + region_code: str + language_code: str + postal_code: str + sorting_code: str + administrative_area: str + locality: str + sublocality: str + address_lines: _containers.RepeatedScalarFieldContainer[str] + recipients: _containers.RepeatedScalarFieldContainer[str] + organization: str + def __init__( + self, + revision: _Optional[int] = ..., + region_code: _Optional[str] = ..., + language_code: _Optional[str] = ..., + postal_code: _Optional[str] = ..., + sorting_code: _Optional[str] = ..., + administrative_area: _Optional[str] = ..., + locality: _Optional[str] = ..., + sublocality: _Optional[str] = ..., + address_lines: _Optional[_Iterable[str]] = ..., + recipients: _Optional[_Iterable[str]] = ..., + organization: _Optional[str] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/quaternion.proto b/venv/lib/python3.12/site-packages/google/type/quaternion.proto new file mode 100644 index 0000000..d246298 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/quaternion.proto @@ -0,0 +1,94 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/quaternion;quaternion"; +option java_multiple_files = true; +option java_outer_classname = "QuaternionProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// A quaternion, represented by four 64-bit floating point values. +// +// A quaternion is defined as the quotient of two directed lines in a +// three-dimensional space or equivalently as the quotient of two Euclidean +// vectors (https://en.wikipedia.org/wiki/Quaternion). +// +// Quaternions are often used in calculations involving three-dimensional +// rotations (https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation), +// as they provide greater mathematical robustness by avoiding the gimbal lock +// problems that can be encountered when using Euler angles +// (https://en.wikipedia.org/wiki/Gimbal_lock). +// +// Quaternions are generally represented in this form: +// +// w + xi + yj + zk +// +// where x, y, z, and w are real numbers, and i, j, and k are three imaginary +// numbers. +// +// The naming choice `(x, y, z, w)` comes from the desire to avoid confusion for +// those interested in the geometric properties of the quaternion in the 3D +// Cartesian space. Other texts often use alternative names or subscripts, such +// as `(a, b, c, d)`, `(1, i, j, k)`, or `(0, 1, 2, 3)`, which are perhaps +// better suited for mathematical interpretations. +// +// To avoid any confusion, as well as to maintain compatibility with a large +// number of software libraries, the quaternions represented using the protocol +// buffer below *must* follow the Hamilton convention, which defines `ij = k` +// (i.e. a right-handed algebra), and therefore: +// +// i^2 = j^2 = k^2 = ijk = −1 +// ij = −ji = k +// jk = −kj = i +// ki = −ik = j +// +// Please DO NOT use this to represent quaternions that follow the JPL +// convention, or any of the other quaternion flavors out there. +// +// Definitions: +// +// - Quaternion norm (or magnitude): `sqrt(x^2 + y^2 + z^2 + w^2)`. +// - Unit (or normalized) quaternion: a quaternion whose norm is 1. +// - Pure quaternion: a quaternion whose scalar component (`w`) is 0. +// - Rotation quaternion: a unit quaternion used to represent rotation. +// - Orientation quaternion: a unit quaternion used to represent orientation. +// +// A quaternion can be normalized by dividing it by its norm. The resulting +// quaternion maintains the same direction, but has a norm of 1, i.e. it moves +// on the unit sphere. This is generally necessary for rotation and orientation +// quaternions, to avoid rounding errors: +// https://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions +// +// Note that `(x, y, z, w)` and `(-x, -y, -z, -w)` represent the same rotation, +// but normalization would be even more useful, e.g. for comparison purposes, if +// it would produce a unique representation. It is thus recommended that `w` be +// kept positive, which can be achieved by changing all the signs when `w` is +// negative. +message Quaternion { + // The x component. + double x = 1; + + // The y component. + double y = 2; + + // The z component. + double z = 3; + + // The scalar component. + double w = 4; +} diff --git a/venv/lib/python3.12/site-packages/google/type/quaternion_pb2.py b/venv/lib/python3.12/site-packages/google/type/quaternion_pb2.py new file mode 100644 index 0000000..8ab2d1f --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/quaternion_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/quaternion.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/quaternion.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1cgoogle/type/quaternion.proto\x12\x0bgoogle.type"8\n\nQuaternion\x12\t\n\x01x\x18\x01 \x01(\x01\x12\t\n\x01y\x18\x02 \x01(\x01\x12\t\n\x01z\x18\x03 \x01(\x01\x12\t\n\x01w\x18\x04 \x01(\x01\x42l\n\x0f\x63om.google.typeB\x0fQuaternionProtoP\x01Z@google.golang.org/genproto/googleapis/type/quaternion;quaternion\xa2\x02\x03GTPb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.type.quaternion_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\017QuaternionProtoP\001Z@google.golang.org/genproto/googleapis/type/quaternion;quaternion\242\002\003GTP" + _globals["_QUATERNION"]._serialized_start = 45 + _globals["_QUATERNION"]._serialized_end = 101 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/quaternion_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/quaternion_pb2.pyi new file mode 100644 index 0000000..8a7bfbe --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/quaternion_pb2.pyi @@ -0,0 +1,39 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message + +DESCRIPTOR: _descriptor.FileDescriptor + +class Quaternion(_message.Message): + __slots__ = ("x", "y", "z", "w") + X_FIELD_NUMBER: _ClassVar[int] + Y_FIELD_NUMBER: _ClassVar[int] + Z_FIELD_NUMBER: _ClassVar[int] + W_FIELD_NUMBER: _ClassVar[int] + x: float + y: float + z: float + w: float + def __init__( + self, + x: _Optional[float] = ..., + y: _Optional[float] = ..., + z: _Optional[float] = ..., + w: _Optional[float] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/google/type/timeofday.proto b/venv/lib/python3.12/site-packages/google/type/timeofday.proto new file mode 100644 index 0000000..94090bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/timeofday.proto @@ -0,0 +1,47 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.type; + +option go_package = "google.golang.org/genproto/googleapis/type/timeofday;timeofday"; +option java_multiple_files = true; +option java_outer_classname = "TimeOfDayProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents a time of day. The date and time zone are either not significant +// or are specified elsewhere. An API may choose to allow leap seconds. Related +// types are [google.type.Date][google.type.Date] and +// `google.protobuf.Timestamp`. +message TimeOfDay { + // Hours of a day in 24 hour format. Must be greater than or equal to 0 and + // typically must be less than or equal to 23. An API may choose to allow the + // value "24:00:00" for scenarios like business closing time. + int32 hours = 1; + + // Minutes of an hour. Must be greater than or equal to 0 and less than or + // equal to 59. + int32 minutes = 2; + + // Seconds of a minute. Must be greater than or equal to 0 and typically must + // be less than or equal to 59. An API may allow the value 60 if it allows + // leap-seconds. + int32 seconds = 3; + + // Fractions of seconds, in nanoseconds. Must be greater than or equal to 0 + // and less than or equal to 999,999,999. + int32 nanos = 4; +} diff --git a/venv/lib/python3.12/site-packages/google/type/timeofday_pb2.py b/venv/lib/python3.12/site-packages/google/type/timeofday_pb2.py new file mode 100644 index 0000000..7dd268e --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/timeofday_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/type/timeofday.proto +# Protobuf Python Version: 6.33.5 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 33, 5, "", "google/type/timeofday.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1bgoogle/type/timeofday.proto\x12\x0bgoogle.type"K\n\tTimeOfDay\x12\r\n\x05hours\x18\x01 \x01(\x05\x12\x0f\n\x07minutes\x18\x02 \x01(\x05\x12\x0f\n\x07seconds\x18\x03 \x01(\x05\x12\r\n\x05nanos\x18\x04 \x01(\x05\x42i\n\x0f\x63om.google.typeB\x0eTimeOfDayProtoP\x01Z>google.golang.org/genproto/googleapis/type/timeofday;timeofday\xa2\x02\x03GTPb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "google.type.timeofday_pb2", _globals +) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\017com.google.typeB\016TimeOfDayProtoP\001Z>google.golang.org/genproto/googleapis/type/timeofday;timeofday\242\002\003GTP" + _globals["_TIMEOFDAY"]._serialized_start = 44 + _globals["_TIMEOFDAY"]._serialized_end = 119 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/google/type/timeofday_pb2.pyi b/venv/lib/python3.12/site-packages/google/type/timeofday_pb2.pyi new file mode 100644 index 0000000..1a5e261 --- /dev/null +++ b/venv/lib/python3.12/site-packages/google/type/timeofday_pb2.pyi @@ -0,0 +1,39 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message + +DESCRIPTOR: _descriptor.FileDescriptor + +class TimeOfDay(_message.Message): + __slots__ = ("hours", "minutes", "seconds", "nanos") + HOURS_FIELD_NUMBER: _ClassVar[int] + MINUTES_FIELD_NUMBER: _ClassVar[int] + SECONDS_FIELD_NUMBER: _ClassVar[int] + NANOS_FIELD_NUMBER: _ClassVar[int] + hours: int + minutes: int + seconds: int + nanos: int + def __init__( + self, + hours: _Optional[int] = ..., + minutes: _Optional[int] = ..., + seconds: _Optional[int] = ..., + nanos: _Optional[int] = ..., + ) -> None: ... diff --git a/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/METADATA b/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/METADATA new file mode 100644 index 0000000..0b68420 --- /dev/null +++ b/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/METADATA @@ -0,0 +1,214 @@ +Metadata-Version: 2.4 +Name: googleapis-common-protos +Version: 1.75.4 +Summary: Common protobufs used in Google APIs +Author-email: Google LLC +License-Expression: Apache-2.0 +Project-URL: Repository, https://github.com/googleapis/google-cloud-python/tree/main/packages/googleapis-common-protos +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Operating System :: OS Independent +Classifier: Topic :: Internet +Requires-Python: >=3.10 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: protobuf<8.0.0,>=6.33.5 +Provides-Extra: grpc +Requires-Dist: grpcio<2.0.0,>=1.59.0; extra == "grpc" +Dynamic: license-file + +Python Client for Service Config +================================ + +|stable| |pypi| |versions| + +- `Client Library Documentation`_ + +.. |stable| image:: https://img.shields.io/badge/support-stable-gold.svg + :target: https://github.com/googleapis/google-cloud-python/blob/main/README.rst#stability-levels +.. |pypi| image:: https://img.shields.io/pypi/v/googleapis-common-protos.svg + :target: https://pypi.org/project/googleapis-common-protos/ +.. |versions| image:: https://img.shields.io/pypi/pyversions/googleapis-common-protos.svg + :target: https://pypi.org/project/googleapis-common-protos/ +.. _Client Library Documentation: https://github.com/googleapis/google-cloud-python/tree/main/packages/googleapis-common-protos/summary_overview + +Quick Start +----------- + +In order to use this library, you first need to go through the following steps: + +1. `Select or create a Cloud Platform project.`_ +2. `Enable billing for your project.`_ +3. `Set up Authentication.`_ + +.. _Select or create a Cloud Platform project.: https://console.cloud.google.com/project +.. _Enable billing for your project.: https://cloud.google.com/billing/docs/how-to/modify-project#enable_billing_for_a_project +.. _Set up Authentication.: https://googleapis.dev/python/google-api-core/latest/auth.html + +Installation +~~~~~~~~~~~~ + +Install this library in a virtual environment using `venv`_. `venv`_ is a tool that +creates isolated Python environments. These isolated environments can have separate +versions of Python packages, which allows you to isolate one project's dependencies +from the dependencies of other projects. + +With `venv`_, it's possible to install this library without needing system +install permissions, and without clashing with the installed system +dependencies. + +.. _`venv`: https://docs.python.org/3/library/venv.html + + +Code samples and snippets +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Code samples and snippets live in the `samples/`_ folder. + +.. _samples/: https://github.com/googleapis/google-cloud-python/tree/main/packages/googleapis-common-protos/samples + + +Supported Python Versions +^^^^^^^^^^^^^^^^^^^^^^^^^ +Our client libraries are compatible with all current `active`_ and `maintenance`_ versions of +Python. + +Python >= 3.10, including 3.14 + +.. _active: https://devguide.python.org/devcycle/#in-development-main-branch +.. _maintenance: https://devguide.python.org/devcycle/#maintenance-branches + +Unsupported Python Versions +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Python <= 3.9 + + +If you are using an `end-of-life`_ +version of Python, we recommend that you update as soon as possible to an actively supported version. + +.. _end-of-life: https://devguide.python.org/devcycle/#end-of-life-branches + +Mac/Linux +^^^^^^^^^ + +.. code-block:: console + + python3 -m venv + source /bin/activate + pip install googleapis-common-protos + + +Windows +^^^^^^^ + +.. code-block:: console + + py -m venv + .\\Scripts\activate + pip install googleapis-common-protos + +Next Steps +~~~~~~~~~~ + +- Read the `Client Library Documentation`_ for Service Config + to see other available methods on the client. +- View this `README`_ to see the full list of Cloud + APIs that we cover. + +.. _README: https://github.com/googleapis/google-cloud-python/blob/main/README.rst + +Logging +------- + +This library uses the standard Python :code:`logging` functionality to log some RPC events that could be of interest for debugging and monitoring purposes. +Note the following: + +#. Logs may contain sensitive information. Take care to **restrict access to the logs** if they are saved, whether it be on local storage or on Google Cloud Logging. +#. Google may refine the occurrence, level, and content of various log messages in this library without flagging such changes as breaking. **Do not depend on immutability of the logging events**. +#. By default, the logging events from this library are not handled. You must **explicitly configure log handling** using one of the mechanisms below. + +Simple, environment-based configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To enable logging for this library without any changes in your code, set the :code:`GOOGLE_SDK_PYTHON_LOGGING_SCOPE` environment variable to a valid Google +logging scope. This configures handling of logging events (at level :code:`logging.DEBUG` or higher) from this library in a default manner, emitting the logged +messages in a structured format. It does not currently allow customizing the logging levels captured nor the handlers, formatters, etc. used for any logging +event. + +A logging scope is a period-separated namespace that begins with :code:`google`, identifying the Python module or package to log. + +- Valid logging scopes: :code:`google`, :code:`google.cloud.asset.v1`, :code:`google.api`, :code:`google.auth`, etc. +- Invalid logging scopes: :code:`foo`, :code:`123`, etc. + +**NOTE**: If the logging scope is invalid, the library does not set up any logging handlers. + +Environment-Based Examples +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Enabling the default handler for all Google-based loggers + +.. code-block:: console + + export GOOGLE_SDK_PYTHON_LOGGING_SCOPE=google + +- Enabling the default handler for a specific Google module (for a client library called :code:`library_v1`): + +.. code-block:: console + + export GOOGLE_SDK_PYTHON_LOGGING_SCOPE=google.cloud.library_v1 + + +Advanced, code-based configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can also configure a valid logging scope using Python's standard `logging` mechanism. + +Code-Based Examples +^^^^^^^^^^^^^^^^^^^ + +- Configuring a handler for all Google-based loggers + +.. code-block:: python + + import logging + + from google.cloud import library_v1 + + base_logger = logging.getLogger("google") + base_logger.addHandler(logging.StreamHandler()) + base_logger.setLevel(logging.DEBUG) + +- Configuring a handler for a specific Google module (for a client library called :code:`library_v1`): + +.. code-block:: python + + import logging + + from google.cloud import library_v1 + + base_logger = logging.getLogger("google.cloud.library_v1") + base_logger.addHandler(logging.StreamHandler()) + base_logger.setLevel(logging.DEBUG) + +Logging details +~~~~~~~~~~~~~~~ + +#. Regardless of which of the mechanisms above you use to configure logging for this library, by default logging events are not propagated up to the root + logger from the `google`-level logger. If you need the events to be propagated to the root logger, you must explicitly set + :code:`logging.getLogger("google").propagate = True` in your code. +#. You can mix the different logging configurations above for different Google modules. For example, you may want use a code-based logging configuration for + one library, but decide you need to also set up environment-based logging configuration for another library. + + #. If you attempt to use both code-based and environment-based configuration for the same module, the environment-based configuration will be ineffectual + if the code -based configuration gets applied first. + +#. The Google-specific logging configurations (default handlers for environment-based configuration; not propagating logging events to the root logger) get + executed the first time *any* client library is instantiated in your application, and only if the affected loggers have not been previously configured. + (This is the reason for 2.i. above.) diff --git a/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/RECORD b/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/RECORD new file mode 100644 index 0000000..be423fc --- /dev/null +++ b/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/RECORD @@ -0,0 +1,268 @@ +google/api/__pycache__/annotations_pb2.cpython-312.pyc,, +google/api/__pycache__/auth_pb2.cpython-312.pyc,, +google/api/__pycache__/backend_pb2.cpython-312.pyc,, +google/api/__pycache__/billing_pb2.cpython-312.pyc,, +google/api/__pycache__/client_pb2.cpython-312.pyc,, +google/api/__pycache__/config_change_pb2.cpython-312.pyc,, +google/api/__pycache__/consumer_pb2.cpython-312.pyc,, +google/api/__pycache__/context_pb2.cpython-312.pyc,, +google/api/__pycache__/control_pb2.cpython-312.pyc,, +google/api/__pycache__/distribution_pb2.cpython-312.pyc,, +google/api/__pycache__/documentation_pb2.cpython-312.pyc,, +google/api/__pycache__/endpoint_pb2.cpython-312.pyc,, +google/api/__pycache__/error_reason_pb2.cpython-312.pyc,, +google/api/__pycache__/field_behavior_pb2.cpython-312.pyc,, +google/api/__pycache__/field_info_pb2.cpython-312.pyc,, +google/api/__pycache__/http_pb2.cpython-312.pyc,, +google/api/__pycache__/httpbody_pb2.cpython-312.pyc,, +google/api/__pycache__/label_pb2.cpython-312.pyc,, +google/api/__pycache__/launch_stage_pb2.cpython-312.pyc,, +google/api/__pycache__/log_pb2.cpython-312.pyc,, +google/api/__pycache__/logging_pb2.cpython-312.pyc,, +google/api/__pycache__/metric_pb2.cpython-312.pyc,, +google/api/__pycache__/monitored_resource_pb2.cpython-312.pyc,, +google/api/__pycache__/monitoring_pb2.cpython-312.pyc,, +google/api/__pycache__/policy_pb2.cpython-312.pyc,, +google/api/__pycache__/quota_pb2.cpython-312.pyc,, +google/api/__pycache__/resource_pb2.cpython-312.pyc,, +google/api/__pycache__/routing_pb2.cpython-312.pyc,, +google/api/__pycache__/service_pb2.cpython-312.pyc,, +google/api/__pycache__/source_info_pb2.cpython-312.pyc,, +google/api/__pycache__/system_parameter_pb2.cpython-312.pyc,, +google/api/__pycache__/usage_pb2.cpython-312.pyc,, +google/api/__pycache__/visibility_pb2.cpython-312.pyc,, +google/api/annotations.proto,sha256=556nQctgWmXnjKMiF0dkpK-f3hliwWMeErhMSTS6mmw,1045 +google/api/annotations_pb2.py,sha256=0P05f7yyEyJDyeuEF9bPczq_Fni_tg5laGmMu_Kz6fs,2414 +google/api/annotations_pb2.pyi,sha256=yAsaRnQFljG5TZeFT1y3gcvYhelrdgf0CBVQ7FymxEg,887 +google/api/auth.proto,sha256=ASZOTluO4gOI3DUAZnBMrTgWv1FDC1gPLM29urNqwzY,9262 +google/api/auth_pb2.py,sha256=i_d39CU9CnsUkIz1GdxMuM7rqG8jbSlTc1W9Rgx-IBQ,3826 +google/api/auth_pb2.pyi,sha256=njj0wdSRHvOpbP3geN2k-3Dp-jBpKlCmTW_yiupkUy8,4353 +google/api/backend.proto,sha256=jVY4DMkKfL6Hdm5TuyKLr5BTkFhCGnikh7getvvFpcw,7609 +google/api/backend_pb2.py,sha256=gtpp5S87tNV3xVgvDopRhw-zuSEu7zL3jksTHI7m-WA,4067 +google/api/backend_pb2.pyi,sha256=tLuPxpZwWvxiokA70S37FnBi_xHiQdR7l-W7lqGraCk,4056 +google/api/billing.proto,sha256=CeStm0L5GBYLyC8U3oCrk2AxWGACRuTNWqiMe_cVbyY,3062 +google/api/billing_pb2.py,sha256=0VCTXxM_lpyWUw_I_O8ZiexjeHMf-79d3U-By-JfEmM,2542 +google/api/billing_pb2.pyi,sha256=IbZqiR_iRAKBmWzNHJfy-jWGkhwg8f-Q8KC0x4sVuKs,1889 +google/api/client.proto,sha256=Xp_BFykP59NHJ5FUPJeb7Hz0SX8_5v-vnemumcxPGAQ,21460 +google/api/client_pb2.py,sha256=Og4Fs4eZKbLN9OOl0F49BlrDXeMDh7jVzjz0WHERNaE,13096 +google/api/client_pb2.pyi,sha256=SMRPfiSwi5p6iKOJqy28s-nZLxJIZjyIBshaL_rxxig,19974 +google/api/config_change.proto,sha256=Vp4BOt2QggBfBspllzjPKcBrhqA9avts2VRJYDvFotU,3166 +google/api/config_change_pb2.py,sha256=OOlvZfMbFwax4MlunWvJ1TXrslBk3tyvA1mFbJZvgW8,2927 +google/api/config_change_pb2.pyi,sha256=GpSpGbqgdX4eAc_h5fBRYaOG3P_AzjKDbIHtOnXiMwI,2402 +google/api/consumer.proto,sha256=mZrLMRYo2cKUHQz9qr3xgEe-LuBh3aLZVD15qm3v7IQ,2717 +google/api/consumer_pb2.py,sha256=Iz6ntVobtUC35e3d07sgfOgC8g9BGU9zcnRD771zyXA,2803 +google/api/consumer_pb2.pyi,sha256=KByNLqdoVPkp73BRiCnmbr1NnGEnjqAlxBiIBOmwijE,2367 +google/api/context.proto,sha256=mmb7KqO6Rt834703e6i4QgJaRBivePOL65mK9ar5cYU,3229 +google/api/context_pb2.py,sha256=Q5ikAUYbrySmgg9dOJY34hPnaSz3fhEyBjg0ioIamZM,2604 +google/api/context_pb2.pyi,sha256=_9q7zdlFKMKzqli-OL69vFSYTO4J45Y2HYkLjsWl5B8,2357 +google/api/control.proto,sha256=WB77VK6jSnzRLMBuDdLjFVpxNkUmns7yFWKXIzOJZf4,1438 +google/api/control_pb2.py,sha256=xMqkYeUiTaz6BXYTo2w1ZOiZWUemY21gE1IJ5XMTmI0,2405 +google/api/control_pb2.pyi,sha256=1wZCYS0R-LSO2gVPOqvbVbrdT-o3_VPQSdwCRmTicHI,1564 +google/api/distribution.proto,sha256=lL0aF9viC8-5rRThYGR7PrLgmpXtq6NDe4OxnRGPwoY,8660 +google/api/distribution_pb2.py,sha256=zMxspeg0zsl8UNnnxXKFj82brdbiswvHE7eIpfwCy1E,4717 +google/api/distribution_pb2.pyi,sha256=qf3hrNe54ONSr6OQ8-ITwtW_Vq4VOo9jXACjfDPWQIo,5851 +google/api/documentation.proto,sha256=pjKEaypqBt5Ks4gNhC8xQqOFEFVToX_8n_43GLjFh0c,6925 +google/api/documentation_pb2.py,sha256=gKJWWHu41T0q0mCM9o47hHquNl9tUAL4uON8oVB0Oko,3077 +google/api/documentation_pb2.pyi,sha256=IZx5THyorv-X7ASekQyTu089T5WaEJMbogjCxY1ns_w,3080 +google/api/endpoint.proto,sha256=pXxcRg9sjsPcoZXGvQSiS-pnrqN5rJWssAlz3maNw40,2891 +google/api/endpoint_pb2.py,sha256=S8IuozDpJgju98_qF0gZzAKjEi0MExhgTh699fWtvrg,2337 +google/api/endpoint_pb2.pyi,sha256=Lp9AVraMiyD-7sFB9upNlwgYfC5R_T8Xfn-0LCHwrOo,1499 +google/api/error_reason.proto,sha256=xp28eBE118qUSF7GdOgszpRYsBrnBpBqW3PrJ1y1Ew4,30153 +google/api/error_reason_pb2.py,sha256=LNSDe9plRNBjhjOYz-jpt98Lj4y2Ie0Vs4LsB_b4icI,4232 +google/api/error_reason_pb2.pyi,sha256=bY7ceN6hELYkOkaSvub28Iy0Zwq8eMF4jf77ZMESRF0,4813 +google/api/field_behavior.proto,sha256=u37XdyXJX3UewsR82SNZ0BN8OTg7imKu9BureqdHjaU,4306 +google/api/field_behavior_pb2.py,sha256=nTt0VzPOixgj7taRRZ5UaEIppZwybcNMDzAYzF21LjA,2922 +google/api/field_behavior_pb2.pyi,sha256=HKVTIpHfvh2KJaaJnyqqzVBgmN_8oyputJVIcil5nSA,1680 +google/api/field_info.proto,sha256=U7sKZzZCtO__LLSPbrnbsGk7GYrIN9poSuVPdR6M854,4456 +google/api/field_info_pb2.py,sha256=WN08vMDH4CiEJn-4GmLCPeOgug6JINLGGABOrOSIxJI,3048 +google/api/field_info_pb2.pyi,sha256=ciWt8d7nbzkyJykhd7LD-BkUyrIbpLDa9BATtnewDnw,2386 +google/api/http.proto,sha256=6tmRKaoV3V9iM5QgMMcu7DO8DXscfCYNwUMpPvZsW3g,15059 +google/api/http_pb2.py,sha256=oXQ0mwZeuNqSFNyMfRF_icYNA_9dI9Q_Qrljh6-quDk,3094 +google/api/http_pb2.pyi,sha256=TOKHCWnWWC0kWuU-Wi0KXc_eXnXxDEaSh5NCw170bRo,3176 +google/api/httpbody.proto,sha256=RUpRAjluAw7bDa5cCQVsiVMEGif2ZeSx48EigPbtJCE,2661 +google/api/httpbody_pb2.py,sha256=m5eorvPrtUnjW8HOtOIKm05a-m6Db_03KhffhymsTQc,2419 +google/api/httpbody_pb2.pyi,sha256=RjXvqiGgtDIsW0uxRjFpRogig6jve2BB-kMWcLQmWvQ,1587 +google/api/label.proto,sha256=2t_ebV7M42t-MjLiQ5oNWsgzV1AHMqK3dg6JzGYRGAY,1357 +google/api/label_pb2.py,sha256=Wu08Pi4yHpUD-NQq0TEnS_tGnFAxI4hTb6Ucx4OOlNU,2552 +google/api/label_pb2.pyi,sha256=e9xbiTc7WWwXo9nudkr_YgpyiNuZeOCXTjWWhkWzReQ,1778 +google/api/launch_stage.proto,sha256=aOIsmCEie06ExwKVafzhBVwh5syrmR4Jhrx18S703YI,3083 +google/api/launch_stage_pb2.py,sha256=kxz6-FOJwqBu8ab6_khWsTK0Y9Sp89zbOUkoPMlcW2Y,2444 +google/api/launch_stage_pb2.pyi,sha256=EtU16uO1pQYSj4cm1CJAzgA-NTBFLnd2q2YC1otGpfc,1381 +google/api/log.proto,sha256=SYnp8Z9tunTeauKRWgRvu-yPeqYbVVUvRTF996yP7Cc,2043 +google/api/log_pb2.py,sha256=cJlWltNHeEl0yFvit93j7RPZRJ7mwbAAqrMWyo6jroo,2461 +google/api/log_pb2.pyi,sha256=HKpNvn6GeGXGb5SlsGeeLH2xS4fVeIau4qhzlPdpuTQ,1746 +google/api/logging.proto,sha256=gDlYNjtkEu12Xsrk4qqymGtBFjfFy0kVgZZXXmH561g,3174 +google/api/logging_pb2.py,sha256=yzEHJfhsMXPQps94WFUeFdvzBxPBSyzyJRXoKXvpsLQ,2623 +google/api/logging_pb2.pyi,sha256=YH1pnk_EQPsdstBJzTUVzf_D-u2q9VpBBHu8kuyrYMk,2196 +google/api/metric.proto,sha256=Ah8O9VDbI9mPi6-3SspBib7RZZEciJC_h2mtBSx61s0,11166 +google/api/metric_pb2.py,sha256=ULKcAKmdgZHEVIpKG9g0o670VsVYA1LkfZ7usHLJ_-g,5708 +google/api/metric_pb2.pyi,sha256=pmw4xNZl-q-D3pkcHUECS-RtELi06HAmv4jQnl_Z_PE,7959 +google/api/monitored_resource.proto,sha256=z31kMz9bYoetsOuL2-VSvDiBOoXOgTTDnL4cVr5-nNI,5888 +google/api/monitored_resource_pb2.py,sha256=jdr2wG-GEWr8HhDuLQgiVqgcLM0TL0IQplGXMTlGL-s,4399 +google/api/monitored_resource_pb2.pyi,sha256=E32ewkjg9FHt9gGcW3r6VZbMjtu8cpmRig6AHg6zq7k,3662 +google/api/monitoring.proto,sha256=BlY9xUnnVtxVm6cxNsp8XpSuUvmoj0T9M9XZnoP8zs8,4457 +google/api/monitoring_pb2.py,sha256=kl0Oj2ZMzuepQpMwOrJ_AEK8evjz_LBg-Jr9i1xdMsA,2681 +google/api/monitoring_pb2.pyi,sha256=HJh_4rPh0jWvn4stWNpxFAXatAnPkGmEtufWJ8MU0Qs,2238 +google/api/policy.proto,sha256=aVzA-bYS5DyVha7GjHD-enQQJq3oKrKIzXv7OQXHloo,3110 +google/api/policy_pb2.py,sha256=C77opM8-tb68TK9F7WtYZMgPXHeP8NqIeNKAe1dup5c,2912 +google/api/policy_pb2.pyi,sha256=FVHD1PgDFwKLPMbdRY4QaMTIlZPc0645u0eKW0dv7gI,2187 +google/api/quota.proto,sha256=rV6KUwN_0YC_ZGiU9ONnBayYTyBNwtXZjY_pne23JdM,7180 +google/api/quota_pb2.py,sha256=YVTawzeFW6H5XuwTcBVSEgOLKdLa5-6YR95rSlZVDTo,3852 +google/api/quota_pb2.pyi,sha256=AKmz6M3rvw0ZxMLFRopNestsUel63DiSB09mQtcbnTA,3894 +google/api/resource.proto,sha256=gV1R6tpDQb8BZytHS17Aq76hkOjfn0qiagyW29rSmTs,8994 +google/api/resource_pb2.py,sha256=U0fGJ7YnQRbWkE82T1AzzZYb0V32JnLQGnz47p05TyY,3763 +google/api/resource_pb2.pyi,sha256=X6NPOoM3TvZKOhQ4AxK0u5Wd2V_DIojdGZFV0tcLG2A,3597 +google/api/routing.proto,sha256=8xy0PD7-B6gM2BTu3lh_Gji02siAUwadz-qshnAFEb4,15193 +google/api/routing_pb2.py,sha256=8YvyQBxTeOvyCu0gi5IlUILsKXJwsrHDcVaCLJ-jTKc,2716 +google/api/routing_pb2.pyi,sha256=2iyz1IFZQzyeyeuDuPSyFq04LclkhK2HdhAMMZMFo2I,1839 +google/api/service.proto,sha256=kcRxkGafszlDfwn5EdLqQwfG6Z9dZqtQTEjRo9InTaM,6698 +google/api/service_pb2.py,sha256=bKsEQaAT1thCgWXw-VagMdmlu3d-sapKDc6FHJT4nfw,6085 +google/api/service_pb2.pyi,sha256=DyH3iVuvUO6onjjhoVCKMmENj_8PJNexDUnkBrDgWY0,7182 +google/api/source_info.proto,sha256=hpjvyJjXFLIG7cyK8Ly8-A1Tjn3QBf4gbSuSLDFLq2M,1091 +google/api/source_info_pb2.py,sha256=XqdRnMkP88lsmvaREgCRNhIkJbSQHWOc2OsdxcSc8F4,2386 +google/api/source_info_pb2.pyi,sha256=p6rDnpQvT0N4IY4gdj52JAKduZXm7R2Lyyo96jQlKhw,1361 +google/api/system_parameter.proto,sha256=tFFRmHZ3rCeJkSvIu7JwcJtPZKhTODxloTMBPgl095M,3475 +google/api/system_parameter_pb2.py,sha256=ivfkknsaj8Lap0mKJTQFVXOla-5wvG298a05CQBHpww,2847 +google/api/system_parameter_pb2.pyi,sha256=rkDrNyeJPRsfNY4cI3dVYi-dmxjdYgNK9weH_3Z5WMY,2235 +google/api/usage.proto,sha256=JsOCA34-fGyuum0YMpttfd83wWMuxbzVfFhU1b4Ae60,3198 +google/api/usage_pb2.py,sha256=ujbrKqQkdplkiFwlAwk2OATQHrPkpgUEd_WZTvc3O3c,2579 +google/api/usage_pb2.pyi,sha256=OAe42yXVlbyMo0T3AmsUpRJfiUoUgukGyHYG8-bR43k,2215 +google/api/visibility.proto,sha256=gPC1xRdPDHM4Dg6R6GXhQhM5MMXzWzwHwqUuxmEKGBA,3767 +google/api/visibility_pb2.py,sha256=enJ6Q3if8cWYwy9xPPmO9BPvemODZTrN1CH03Io0Si0,3344 +google/api/visibility_pb2.pyi,sha256=Cnkc-umYhbvsOj1T1sc2VZ0TbIN6nD4UggS91P-bk-c,2231 +google/cloud/__pycache__/common_resources_pb2.cpython-312.pyc,, +google/cloud/__pycache__/extended_operations_pb2.cpython-312.pyc,, +google/cloud/common_resources.proto,sha256=rTSsmDKoThiospOtRHLZX27W7cBPCEJS5rpVK1bP9Y0,1578 +google/cloud/common_resources_pb2.py,sha256=mWI9fl6Uqpndlzv_eujjmsLC7MFryzPpa5oO0JmK9ic,2832 +google/cloud/common_resources_pb2.pyi,sha256=auOAyE8-AWvsY9qu5xhX0TcaGWfP9vPOUcvEWkkngBc,765 +google/cloud/extended_operations.proto,sha256=gI4lL58QZw4JOL01_j5Rirk5A00f6MG0P0m5eX9ytSM,6307 +google/cloud/extended_operations_pb2.py,sha256=PPR3P-Ps1XcHHvIPmvoFn6dbRV86cKeF6pG_nafDd_k,3078 +google/cloud/extended_operations_pb2.pyi,sha256=7OLGIjrs7vyd0YZRbbF1bml4JF9hSdolMCEnR3XSE90,1889 +google/cloud/location/__pycache__/locations_pb2.cpython-312.pyc,, +google/cloud/location/locations.proto,sha256=QPoDwHP-htlXfOl9mtm2XzWOmXMHzbxX1fYIvfAeAs4,3604 +google/cloud/location/locations_pb2.py,sha256=Ustqzf0fjmc6lgc4nGWzfG8BLkKytBECh9kJ9xsMAaY,5366 +google/cloud/location/locations_pb2.pyi,sha256=ngMuwwf_0DvScpKM02aToCsUStDnz4gGixYQEGOssuA,3410 +google/gapic/metadata/__pycache__/gapic_metadata_pb2.cpython-312.pyc,, +google/gapic/metadata/gapic_metadata.proto,sha256=_VxjjqKXoL4XYFOBBQCwIbIQ724gD6SOR-AJBTwPk4Y,3805 +google/gapic/metadata/gapic_metadata_pb2.py,sha256=SJw3fFm9dqrwc2mEL-vqUUfwxa6VaTa7dB7TWzxlR5I,5123 +google/gapic/metadata/gapic_metadata_pb2.pyi,sha256=e1qpGP7qJgP0lfGCvawhQlt2Jd4pYZ2xAxtOd7DH-Yk,4516 +google/logging/type/__pycache__/http_request_pb2.cpython-312.pyc,, +google/logging/type/__pycache__/log_severity_pb2.cpython-312.pyc,, +google/logging/type/http_request.proto,sha256=zxYFInPIcyFDrWQYwN4FmqSaOy9AL2Lmp8Y0kUNsxGU,3601 +google/logging/type/http_request_pb2.py,sha256=IBd8GHO8m1hQ8AYK-K9HrqlJZg1GNOeowcE3ZNF0DCw,3285 +google/logging/type/http_request_pb2.pyi,sha256=V2IHFBJES8xU8fqiu5xbGTBV38LHuxsmDTbgYq3aS2Y,3202 +google/logging/type/log_severity.proto,sha256=zqnBXSkSV3PGG0sBjdvJs9fwiKL3N9anTjjxtgfdpR4,2555 +google/logging/type/log_severity_pb2.py,sha256=FFkbjmM7qlTP6PxXEocNHiXX9hq5V7OxZIYjr6r83ys,2830 +google/logging/type/log_severity_pb2.pyi,sha256=47i081SvX-t7hqlZzmzy8NZj7OK3myeoNeupVCHWi_0,1378 +google/longrunning/__pycache__/operations_grpc.cpython-312.pyc,, +google/longrunning/__pycache__/operations_grpc_pb2.cpython-312.pyc,, +google/longrunning/__pycache__/operations_pb2.cpython-312.pyc,, +google/longrunning/__pycache__/operations_pb2_grpc.cpython-312.pyc,, +google/longrunning/__pycache__/operations_proto.cpython-312.pyc,, +google/longrunning/__pycache__/operations_proto_pb2.cpython-312.pyc,, +google/longrunning/operations_grpc.py,sha256=ZtTe_7HJsDWG4OZol8Qi0qoV7HkK4BAxt8Vh0Vh_-xs,818 +google/longrunning/operations_grpc_pb2.py,sha256=iglg37RTLIXqkyQofLiC4yJrzywRhXlPE0apvd0jTKM,531 +google/longrunning/operations_pb2.py,sha256=IREpzTVW90oSeGiWq0sRogtXgiBzEL8kMuwL8XC0Cqc,1569 +google/longrunning/operations_pb2_grpc.py,sha256=DFQ7x25xPEr9VFT9zSVcZ2gqPsvG-5xhQGfLm4mRu3s,14464 +google/longrunning/operations_proto.proto,sha256=XS5_nutVuEF7UOClNqCSur0y0m5PvrcJnGzc2m4W25w,10931 +google/longrunning/operations_proto.py,sha256=vIe7RoVZfjsS7MSZB487GGMIB_5_7--sT_cyt8coGCk,243 +google/longrunning/operations_proto_pb2.py,sha256=5gQo1xfK_8yFENrAO6FPQWXFOGMcyxBwLQFT6YLvIlE,7987 +google/longrunning/operations_proto_pb2.pyi,sha256=ahHlnn6fCISd9EcYBe5vt2v2bURbwieZ2WGSnL8zMvs,4989 +google/rpc/__pycache__/code_pb2.cpython-312.pyc,, +google/rpc/__pycache__/error_details_pb2.cpython-312.pyc,, +google/rpc/__pycache__/http_pb2.cpython-312.pyc,, +google/rpc/__pycache__/status_pb2.cpython-312.pyc,, +google/rpc/code.proto,sha256=g6Yxhbg89zMhlvxerQ7JG5OsIH--kbPVvD1y9GGwuzE,7138 +google/rpc/code_pb2.py,sha256=LteShTb-LkEeKWJ-83Er9DaH5ZP1EP7RTJCcWQD5LB8,2704 +google/rpc/code_pb2.pyi,sha256=S0tVsFZr7_QXpLUhRkRCqDj3EoaNAO0Rn5FV7SXIaqs,1771 +google/rpc/context/__pycache__/attribute_context_pb2.cpython-312.pyc,, +google/rpc/context/__pycache__/audit_context_pb2.cpython-312.pyc,, +google/rpc/context/attribute_context.proto,sha256=iqPw_p9Icr4KMqGtT2IDsYpnt6S7afwApGUlULRVqT4,15115 +google/rpc/context/attribute_context_pb2.py,sha256=kfU8ZcQF_YkP_VOMIUrVVraY4vqxyg1zMH5rvPS8uLk,8787 +google/rpc/context/attribute_context_pb2.pyi,sha256=qcLFZeuz-3NOAYcKvHJ1593G6LGLnV2MfiOtqcF8VJU,11924 +google/rpc/context/audit_context.proto,sha256=zRpTxlPL1iautgoibxmRO8l1J3FT4bXbrlo7pDvaBF4,1829 +google/rpc/context/audit_context_pb2.py,sha256=auq4s9wZyO7EETpeeHblMgR5tVT3hVuhlrjXczvNyUg,2679 +google/rpc/context/audit_context_pb2.pyi,sha256=GfJtDQTdR6qThds41jVggjpiivZY6IFmba7SZcFvUvs,1931 +google/rpc/error_details.proto,sha256=XBjULmkk9EdiJTS0t824tTwGBsjOcpuRLuXt-I0cVSA,14599 +google/rpc/error_details_pb2.py,sha256=A1ZjNd6XULdouKWnMZTWzQqxkU3rvpoCHyUopjRlzHY,6705 +google/rpc/error_details_pb2.pyi,sha256=Se7lEnseavX9Tfn2uXV4oE0kc7VQGRPs3v1Y2Wm_m4U,8521 +google/rpc/http.proto,sha256=-SSZX6rlLrksCdfqJdmyF_6aZilUwGOY511ctOWsA4Y,1940 +google/rpc/http_pb2.py,sha256=RzTiT8P5XWSJm8tgDmhsf1Psu8wusFp26tmLkD8pmgA,2793 +google/rpc/http_pb2.pyi,sha256=76ggIoAbQa26zFM-MojMGw6XrsBRp6MmZL-WNr44u3k,2485 +google/rpc/status.proto,sha256=9b_SYuZwXHrnPzLgrY7iDOjAoleN-MT3br92tXLyle0,1902 +google/rpc/status_pb2.py,sha256=DaEmXnXsnFLZleXEiTpfQ_MOeiM_d6VZaHrvCikxjO8,2389 +google/rpc/status_pb2.pyi,sha256=kJGUe-6A1nGSRLzzJtqjUXGpR-eo3xD70AVAERkbjgI,1549 +google/type/__pycache__/calendar_period_pb2.cpython-312.pyc,, +google/type/__pycache__/color_pb2.cpython-312.pyc,, +google/type/__pycache__/date_pb2.cpython-312.pyc,, +google/type/__pycache__/datetime_pb2.cpython-312.pyc,, +google/type/__pycache__/dayofweek_pb2.cpython-312.pyc,, +google/type/__pycache__/decimal_pb2.cpython-312.pyc,, +google/type/__pycache__/expr_pb2.cpython-312.pyc,, +google/type/__pycache__/fraction_pb2.cpython-312.pyc,, +google/type/__pycache__/interval_pb2.cpython-312.pyc,, +google/type/__pycache__/latlng_pb2.cpython-312.pyc,, +google/type/__pycache__/localized_text_pb2.cpython-312.pyc,, +google/type/__pycache__/money_pb2.cpython-312.pyc,, +google/type/__pycache__/month_pb2.cpython-312.pyc,, +google/type/__pycache__/phone_number_pb2.cpython-312.pyc,, +google/type/__pycache__/postal_address_pb2.cpython-312.pyc,, +google/type/__pycache__/quaternion_pb2.cpython-312.pyc,, +google/type/__pycache__/timeofday_pb2.cpython-312.pyc,, +google/type/calendar_period.proto,sha256=9yF5iMSypeW2F5wgg_UhvZxnCedksCG-qLeFRRa47sM,1762 +google/type/calendar_period_pb2.py,sha256=kegBBUrgUx-59dQQCkBF-OwwP4vExNxLtyKJaHK0a5c,2522 +google/type/calendar_period_pb2.pyi,sha256=sVo812wLSg28VeC_oZHNSNbvxWgbbw-FWyWDA1IK_xg,1400 +google/type/color.proto,sha256=VudYYPezFTiqaZcdWXAe8EJiTX8aW5x44B5k3VJJ1oE,6355 +google/type/color_pb2.py,sha256=2XNmZFrsKo2JOGjNwNq3MAXTLRXhEqLl2E1nHbkwqGg,2436 +google/type/color_pb2.pyi,sha256=fMHTxhcFpN1oQm77uyl4qmpImRkVoVVdBORy2S_i0ek,1501 +google/type/date.proto,sha256=llrtlNwElkcRdr1gf33JQThU8HyBJTkz2gdd7iZpqdo,2010 +google/type/date_pb2.py,sha256=31Mgde-5aUgI2lkRb35HQrNofsYNJSKuPe0XWpBlu3Y,2238 +google/type/date_pb2.pyi,sha256=bhco_u3qTskpSxPthxrNYoafvupno3P7D0PDOfiw8KU,1187 +google/type/datetime.proto,sha256=jT6duaGNV58LGFd0RsbrifYnL_jSl-nhZCyDVFNLZJ8,4051 +google/type/datetime_pb2.py,sha256=kK2Lce-EjzhqUn7OX2zg2ZZyqYImWOEKkvPmL5LmCEo,2918 +google/type/datetime_pb2.pyi,sha256=2Q2q0K_gkILk6KYWbjqtg8JbB_Th_qQK7JrHk79CUHQ,2487 +google/type/dayofweek.proto,sha256=ymgfkFAq2BggcQXwflDA9eJnbArwqH_u-gcbKZxDIFs,1204 +google/type/dayofweek_pb2.py,sha256=TdcdKi3JUj30XOwjG7735euq8cvU-DBb81fpUjMnr84,2455 +google/type/dayofweek_pb2.pyi,sha256=RbF1ibri8xloSPPHR25zNuzBTzpwbzn6u_FGsT_P4iE,1335 +google/type/decimal.proto,sha256=B7G4uISyUOxdeiR7A2EFQmXvm0toahP8vW6TZ54tdmA,4225 +google/type/decimal_pb2.py,sha256=eX5pLEQ4wkxjThN9ogeoDbMaj7dWFxu7E95nsWZauJ4,2214 +google/type/decimal_pb2.pyi,sha256=ETMNgbDSMkwbwVzF1dy1iYv5F1vfSZVrZTacBiqw4MQ,980 +google/type/expr.proto,sha256=0dPcT7ZAFuoY0h1S7Zf_Z-ZVbWm__JT_SwL22A-9CmM,2730 +google/type/expr_pb2.py,sha256=raylzWdlPU-uAoqz0xlOL-HNI7UHmrKvBjiG_BUTUhY,2277 +google/type/expr_pb2.pyi,sha256=8pRxBNy6-l60abONgar3wsRFxhNO17AXXXbZkv1-aHc,1355 +google/type/fraction.proto,sha256=kmiYI3Uq-eiUFnd1fcroK8q8K02nArLn16IDNU8Ojp4,1156 +google/type/fraction_pb2.py,sha256=lnY0zghgdZrQLXgfY6b5fOVQg-lZiN3lF6cil6eHp-4,2272 +google/type/fraction_pb2.pyi,sha256=xxowlWmKi9jiqpTxJPJVuUW4-OqpmT_7Jp-D1liGaSY,1126 +google/type/interval.proto,sha256=Ka64BsFKpTgvG8vZW9cyNd6h7K42DVdXulyeG_FuZmo,1635 +google/type/interval_pb2.py,sha256=dDzPG2oxyctP7jkcbhmURK61hbMYGw2rji_vaX_v7rA,2445 +google/type/interval_pb2.pyi,sha256=ThM515bgzGYfEXWS1Ty3V8qZsvETeGBKEsL05AAstkw,1496 +google/type/latlng.proto,sha256=OWZlYbbCy0XUiKVq2Qm47JtaCGzPYuKMZiBZFw89HL0,1433 +google/type/latlng_pb2.py,sha256=0NCr8Vo1ANpjR7hPi0oIcBUS15rjkGhyG-QN2hRjcNY,2229 +google/type/latlng_pb2.pyi,sha256=H1_dd3GUfw-M3Cro2VoyUQTN44XDXKTWaYrp_Kt_nek,1120 +google/type/localized_text.proto,sha256=QiVYjqGjTBNBZjb__yoIyEmy-rS4LLwt4QXUJKvxcQY,1317 +google/type/localized_text_pb2.py,sha256=t2im8JSY3Z6wD2VCkCGYy2J8T0N6Jg_tZBOMYpmv3BE,2323 +google/type/localized_text_pb2.pyi,sha256=lHUdsojwSWDYMNBDzSy4_9OKyflCBdA91wZ0TAR5d-A,1119 +google/type/money.proto,sha256=ShOe9R4kNSVmisHd0FkSJUUtiWM--w7UJjfoe3wY80k,1571 +google/type/money_pb2.py,sha256=yUx-xtNB2BM0Oc4cgUCK_Rp1yqHbf2yLPcZKPBpJEzM,2245 +google/type/money_pb2.pyi,sha256=dTUleurCYBGl3UsFgEL-AIGerrPd6-HUAX02kLmXZ4Y,1232 +google/type/month.proto,sha256=fbmvxTp3igZAnEqGxF0ec_8YkxtkNE6OcGNjBYvEmXQ,1479 +google/type/month_pb2.py,sha256=-DFAs-UzbZ4DVLwArhnuJQ6eOBg67pOXtuuXGiSqpZ8,2531 +google/type/month_pb2.pyi,sha256=CP_EBA3rCkhfHRqOQDQMKl8sv3gvQjpW69kQ7T5Mdss,1458 +google/type/phone_number.proto,sha256=wVm4OCBqdU11zLn2h84CE1G1bb3-722o_sXHyKINSG0,4784 +google/type/phone_number_pb2.py,sha256=2rTS_E9882EdCHrbrZxFZWte_zCxcL_AGaI5vizMbO0,2650 +google/type/phone_number_pb2.pyi,sha256=8rQfvHsk6uYeiFfHMuvjkZXr2KIZRNccIBuMozM6GsU,1754 +google/type/postal_address.proto,sha256=ZCChFjQ75f3xr8Vx7Z0qfzlycj1m4NJjOKDKsDqAA_o,6322 +google/type/postal_address_pb2.py,sha256=YVaBXvpTDSExH5C8pUy73_GQLuWTG5qygFmyHMOTIzA,2707 +google/type/postal_address_pb2.pyi,sha256=kM9HkMKUm5Kipj89hR1Dtye-ke6aPWNSvYK50Il1r-g,2614 +google/type/quaternion.proto,sha256=4zvM4Lkl5UcCREiip9gyFn9rKMaschknkdsjY7Q4o3U,3826 +google/type/quaternion_pb2.py,sha256=CH-SRbcs5RaSjYF67qLrKWPHDIhNU_RQl0xLKCrtHfw,2331 +google/type/quaternion_pb2.pyi,sha256=78kj_z0GXrulbyoCcbon2tzlWsYHwwoTPVnWOWl1aOk,1257 +google/type/timeofday.proto,sha256=leDlsFSJ88fhexFf-Nuf5ba59RYugzgeNNggjPZZco4,1840 +google/type/timeofday_pb2.py,sha256=fB1HRp6lePMhKt7wHMDKR9Gl-Vq_YVeg1Fvzd6N8yTE,2342 +google/type/timeofday_pb2.pyi,sha256=B9mzMCS_LEgqUGrvb_0fNDY0AnKcduAuN5mJe7xjn1s,1320 +googleapis_common_protos-1.75.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +googleapis_common_protos-1.75.4.dist-info/METADATA,sha256=TwIBTnzUAhtae5EZGuIfCX33uuSb0Hm2b00CwyaLvrY,8460 +googleapis_common_protos-1.75.4.dist-info/RECORD,, +googleapis_common_protos-1.75.4.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91 +googleapis_common_protos-1.75.4.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358 +googleapis_common_protos-1.75.4.dist-info/top_level.txt,sha256=_1QvSJIhFAGfxb79D6DhB7SUw2X6T4rwnz_LLrbcD3c,7 diff --git a/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/WHEEL b/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/WHEEL new file mode 100644 index 0000000..13013d7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (84.0.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/licenses/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/licenses/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/top_level.txt new file mode 100644 index 0000000..cb42911 --- /dev/null +++ b/venv/lib/python3.12/site-packages/googleapis_common_protos-1.75.4.dist-info/top_level.txt @@ -0,0 +1 @@ +google diff --git a/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/METADATA new file mode 100644 index 0000000..8a2f639 --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/METADATA @@ -0,0 +1,202 @@ +Metadata-Version: 2.4 +Name: h11 +Version: 0.16.0 +Summary: A pure-Python, bring-your-own-I/O implementation of HTTP/1.1 +Home-page: https://github.com/python-hyper/h11 +Author: Nathaniel J. Smith +Author-email: njs@pobox.com +License: MIT +Classifier: Development Status :: 3 - Alpha +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: System :: Networking +Requires-Python: >=3.8 +License-File: LICENSE.txt +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: requires-python +Dynamic: summary + +h11 +=== + +.. image:: https://travis-ci.org/python-hyper/h11.svg?branch=master + :target: https://travis-ci.org/python-hyper/h11 + :alt: Automated test status + +.. image:: https://codecov.io/gh/python-hyper/h11/branch/master/graph/badge.svg + :target: https://codecov.io/gh/python-hyper/h11 + :alt: Test coverage + +.. image:: https://readthedocs.org/projects/h11/badge/?version=latest + :target: http://h11.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +This is a little HTTP/1.1 library written from scratch in Python, +heavily inspired by `hyper-h2 `_. + +It's a "bring-your-own-I/O" library; h11 contains no IO code +whatsoever. This means you can hook h11 up to your favorite network +API, and that could be anything you want: synchronous, threaded, +asynchronous, or your own implementation of `RFC 6214 +`_ -- h11 won't judge you. +(Compare this to the current state of the art, where every time a `new +network API `_ comes along then someone +gets to start over reimplementing the entire HTTP protocol from +scratch.) Cory Benfield made an `excellent blog post describing the +benefits of this approach +`_, or if you like video +then here's his `PyCon 2016 talk on the same theme +`_. + +This also means that h11 is not immediately useful out of the box: +it's a toolkit for building programs that speak HTTP, not something +that could directly replace ``requests`` or ``twisted.web`` or +whatever. But h11 makes it much easier to implement something like +``requests`` or ``twisted.web``. + +At a high level, working with h11 goes like this: + +1) First, create an ``h11.Connection`` object to track the state of a + single HTTP/1.1 connection. + +2) When you read data off the network, pass it to + ``conn.receive_data(...)``; you'll get back a list of objects + representing high-level HTTP "events". + +3) When you want to send a high-level HTTP event, create the + corresponding "event" object and pass it to ``conn.send(...)``; + this will give you back some bytes that you can then push out + through the network. + +For example, a client might instantiate and then send a +``h11.Request`` object, then zero or more ``h11.Data`` objects for the +request body (e.g., if this is a POST), and then a +``h11.EndOfMessage`` to indicate the end of the message. Then the +server would then send back a ``h11.Response``, some ``h11.Data``, and +its own ``h11.EndOfMessage``. If either side violates the protocol, +you'll get a ``h11.ProtocolError`` exception. + +h11 is suitable for implementing both servers and clients, and has a +pleasantly symmetric API: the events you send as a client are exactly +the ones that you receive as a server and vice-versa. + +`Here's an example of a tiny HTTP client +`_ + +It also has `a fine manual `_. + +FAQ +--- + +*Whyyyyy?* + +I wanted to play with HTTP in `Curio +`__ and `Trio +`__, which at the time didn't have any +HTTP libraries. So I thought, no big deal, Python has, like, a dozen +different implementations of HTTP, surely I can find one that's +reusable. I didn't find one, but I did find Cory's call-to-arms +blog-post. So I figured, well, fine, if I have to implement HTTP from +scratch, at least I can make sure no-one *else* has to ever again. + +*Should I use it?* + +Maybe. You should be aware that it's a very young project. But, it's +feature complete and has an exhaustive test-suite and complete docs, +so the next step is for people to try using it and see how it goes +:-). If you do then please let us know -- if nothing else we'll want +to talk to you before making any incompatible changes! + +*What are the features/limitations?* + +Roughly speaking, it's trying to be a robust, complete, and non-hacky +implementation of the first "chapter" of the HTTP/1.1 spec: `RFC 7230: +HTTP/1.1 Message Syntax and Routing +`_. That is, it mostly focuses on +implementing HTTP at the level of taking bytes on and off the wire, +and the headers related to that, and tries to be anal about spec +conformance. It doesn't know about higher-level concerns like URL +routing, conditional GETs, cross-origin cookie policies, or content +negotiation. But it does know how to take care of framing, +cross-version differences in keep-alive handling, and the "obsolete +line folding" rule, so you can focus your energies on the hard / +interesting parts for your application, and it tries to support the +full specification in the sense that any useful HTTP/1.1 conformant +application should be able to use h11. + +It's pure Python, and has no dependencies outside of the standard +library. + +It has a test suite with 100.0% coverage for both statements and +branches. + +Currently it supports Python 3 (testing on 3.8-3.12) and PyPy 3. +The last Python 2-compatible version was h11 0.11.x. +(Originally it had a Cython wrapper for `http-parser +`_ and a beautiful nested state +machine implemented with ``yield from`` to postprocess the output. But +I had to take these out -- the new *parser* needs fewer lines-of-code +than the old *parser wrapper*, is written in pure Python, uses no +exotic language syntax, and has more features. It's sad, really; that +old state machine was really slick. I just need a few sentences here +to mourn that.) + +I don't know how fast it is. I haven't benchmarked or profiled it yet, +so it's probably got a few pointless hot spots, and I've been trying +to err on the side of simplicity and robustness instead of +micro-optimization. But at the architectural level I tried hard to +avoid fundamentally bad decisions, e.g., I believe that all the +parsing algorithms remain linear-time even in the face of pathological +input like slowloris, and there are no byte-by-byte loops. (I also +believe that it maintains bounded memory usage in the face of +arbitrary/pathological input.) + +The whole library is ~800 lines-of-code. You can read and understand +the whole thing in less than an hour. Most of the energy invested in +this so far has been spent on trying to keep things simple by +minimizing special-cases and ad hoc state manipulation; even though it +is now quite small and simple, I'm still annoyed that I haven't +figured out how to make it even smaller and simpler. (Unfortunately, +HTTP does not lend itself to simplicity.) + +The API is ~feature complete and I don't expect the general outlines +to change much, but you can't judge an API's ergonomics until you +actually document and use it, so I'd expect some changes in the +details. + +*How do I try it?* + +.. code-block:: sh + + $ pip install h11 + $ git clone git@github.com:python-hyper/h11 + $ cd h11/examples + $ python basic-client.py + +and go from there. + +*License?* + +MIT + +*Code of conduct?* + +Contributors are requested to follow our `code of conduct +`_ in +all project spaces. diff --git a/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/RECORD new file mode 100644 index 0000000..a8f8e63 --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/RECORD @@ -0,0 +1,29 @@ +h11-0.16.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +h11-0.16.0.dist-info/METADATA,sha256=KPMmCYrAn8unm48YD5YIfIQf4kViFct7hyqcfVzRnWQ,8348 +h11-0.16.0.dist-info/RECORD,, +h11-0.16.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91 +h11-0.16.0.dist-info/licenses/LICENSE.txt,sha256=N9tbuFkm2yikJ6JYZ_ELEjIAOuob5pzLhRE4rbjm82E,1124 +h11-0.16.0.dist-info/top_level.txt,sha256=F7dC4jl3zeh8TGHEPaWJrMbeuoWbS379Gwdi-Yvdcis,4 +h11/__init__.py,sha256=iO1KzkSO42yZ6ffg-VMgbx_ZVTWGUY00nRYEWn-s3kY,1507 +h11/__pycache__/__init__.cpython-312.pyc,, +h11/__pycache__/_abnf.cpython-312.pyc,, +h11/__pycache__/_connection.cpython-312.pyc,, +h11/__pycache__/_events.cpython-312.pyc,, +h11/__pycache__/_headers.cpython-312.pyc,, +h11/__pycache__/_readers.cpython-312.pyc,, +h11/__pycache__/_receivebuffer.cpython-312.pyc,, +h11/__pycache__/_state.cpython-312.pyc,, +h11/__pycache__/_util.cpython-312.pyc,, +h11/__pycache__/_version.cpython-312.pyc,, +h11/__pycache__/_writers.cpython-312.pyc,, +h11/_abnf.py,sha256=ybixr0xsupnkA6GFAyMubuXF6Tc1lb_hF890NgCsfNc,4815 +h11/_connection.py,sha256=k9YRVf6koZqbttBW36xSWaJpWdZwa-xQVU9AHEo9DuI,26863 +h11/_events.py,sha256=I97aXoal1Wu7dkL548BANBUCkOIbe-x5CioYA9IBY14,11792 +h11/_headers.py,sha256=P7D-lBNxHwdLZPLimmYwrPG-9ZkjElvvJZJdZAgSP-4,10412 +h11/_readers.py,sha256=a4RypORUCC3d0q_kxPuBIM7jTD8iLt5X91TH0FsduN4,8590 +h11/_receivebuffer.py,sha256=xrspsdsNgWFxRfQcTXxR8RrdjRXXTK0Io5cQYWpJ1Ws,5252 +h11/_state.py,sha256=_5LG_BGR8FCcFQeBPH-TMHgm_-B-EUcWCnQof_9XjFE,13231 +h11/_util.py,sha256=LWkkjXyJaFlAy6Lt39w73UStklFT5ovcvo0TkY7RYuk,4888 +h11/_version.py,sha256=GVSsbPSPDcOuF6ptfIiXnVJoaEm3ygXbMnqlr_Giahw,686 +h11/_writers.py,sha256=oFKm6PtjeHfbj4RLX7VB7KDc1gIY53gXG3_HR9ltmTA,5081 +h11/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 diff --git a/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/WHEEL new file mode 100644 index 0000000..1eb3c49 --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (78.1.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt b/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..8f080ea --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 Nathaniel J. Smith and other contributors + +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. diff --git a/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/top_level.txt new file mode 100644 index 0000000..0d24def --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/top_level.txt @@ -0,0 +1 @@ +h11 diff --git a/venv/lib/python3.12/site-packages/h11/__init__.py b/venv/lib/python3.12/site-packages/h11/__init__.py new file mode 100644 index 0000000..989e92c --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11/__init__.py @@ -0,0 +1,62 @@ +# A highish-level implementation of the HTTP/1.1 wire protocol (RFC 7230), +# containing no networking code at all, loosely modelled on hyper-h2's generic +# implementation of HTTP/2 (and in particular the h2.connection.H2Connection +# class). There's still a bunch of subtle details you need to get right if you +# want to make this actually useful, because it doesn't implement all the +# semantics to check that what you're asking to write to the wire is sensible, +# but at least it gets you out of dealing with the wire itself. + +from h11._connection import Connection, NEED_DATA, PAUSED +from h11._events import ( + ConnectionClosed, + Data, + EndOfMessage, + Event, + InformationalResponse, + Request, + Response, +) +from h11._state import ( + CLIENT, + CLOSED, + DONE, + ERROR, + IDLE, + MIGHT_SWITCH_PROTOCOL, + MUST_CLOSE, + SEND_BODY, + SEND_RESPONSE, + SERVER, + SWITCHED_PROTOCOL, +) +from h11._util import LocalProtocolError, ProtocolError, RemoteProtocolError +from h11._version import __version__ + +PRODUCT_ID = "python-h11/" + __version__ + + +__all__ = ( + "Connection", + "NEED_DATA", + "PAUSED", + "ConnectionClosed", + "Data", + "EndOfMessage", + "Event", + "InformationalResponse", + "Request", + "Response", + "CLIENT", + "CLOSED", + "DONE", + "ERROR", + "IDLE", + "MUST_CLOSE", + "SEND_BODY", + "SEND_RESPONSE", + "SERVER", + "SWITCHED_PROTOCOL", + "ProtocolError", + "LocalProtocolError", + "RemoteProtocolError", +) diff --git a/venv/lib/python3.12/site-packages/h11/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/h11/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..feb9bc6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/h11/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/h11/__pycache__/_abnf.cpython-312.pyc b/venv/lib/python3.12/site-packages/h11/__pycache__/_abnf.cpython-312.pyc new file mode 100644 index 0000000..9217659 Binary files /dev/null and b/venv/lib/python3.12/site-packages/h11/__pycache__/_abnf.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/h11/__pycache__/_connection.cpython-312.pyc b/venv/lib/python3.12/site-packages/h11/__pycache__/_connection.cpython-312.pyc new file mode 100644 index 0000000..d7027d6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/h11/__pycache__/_connection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/h11/__pycache__/_events.cpython-312.pyc b/venv/lib/python3.12/site-packages/h11/__pycache__/_events.cpython-312.pyc new file mode 100644 index 0000000..e1182ef Binary files /dev/null and b/venv/lib/python3.12/site-packages/h11/__pycache__/_events.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/h11/__pycache__/_headers.cpython-312.pyc b/venv/lib/python3.12/site-packages/h11/__pycache__/_headers.cpython-312.pyc new file mode 100644 index 0000000..0a618ca Binary files /dev/null and b/venv/lib/python3.12/site-packages/h11/__pycache__/_headers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/h11/__pycache__/_readers.cpython-312.pyc b/venv/lib/python3.12/site-packages/h11/__pycache__/_readers.cpython-312.pyc new file mode 100644 index 0000000..913e55e Binary files /dev/null and b/venv/lib/python3.12/site-packages/h11/__pycache__/_readers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/h11/__pycache__/_receivebuffer.cpython-312.pyc b/venv/lib/python3.12/site-packages/h11/__pycache__/_receivebuffer.cpython-312.pyc new file mode 100644 index 0000000..dac836e Binary files /dev/null and b/venv/lib/python3.12/site-packages/h11/__pycache__/_receivebuffer.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/h11/__pycache__/_state.cpython-312.pyc b/venv/lib/python3.12/site-packages/h11/__pycache__/_state.cpython-312.pyc new file mode 100644 index 0000000..ac93db0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/h11/__pycache__/_state.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/h11/__pycache__/_util.cpython-312.pyc b/venv/lib/python3.12/site-packages/h11/__pycache__/_util.cpython-312.pyc new file mode 100644 index 0000000..b348747 Binary files /dev/null and b/venv/lib/python3.12/site-packages/h11/__pycache__/_util.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/h11/__pycache__/_version.cpython-312.pyc b/venv/lib/python3.12/site-packages/h11/__pycache__/_version.cpython-312.pyc new file mode 100644 index 0000000..49eabf4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/h11/__pycache__/_version.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/h11/__pycache__/_writers.cpython-312.pyc b/venv/lib/python3.12/site-packages/h11/__pycache__/_writers.cpython-312.pyc new file mode 100644 index 0000000..8bc7b1c Binary files /dev/null and b/venv/lib/python3.12/site-packages/h11/__pycache__/_writers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/h11/_abnf.py b/venv/lib/python3.12/site-packages/h11/_abnf.py new file mode 100644 index 0000000..933587f --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11/_abnf.py @@ -0,0 +1,132 @@ +# We use native strings for all the re patterns, to take advantage of string +# formatting, and then convert to bytestrings when compiling the final re +# objects. + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#whitespace +# OWS = *( SP / HTAB ) +# ; optional whitespace +OWS = r"[ \t]*" + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.token.separators +# token = 1*tchar +# +# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" +# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" +# / DIGIT / ALPHA +# ; any VCHAR, except delimiters +token = r"[-!#$%&'*+.^_`|~0-9a-zA-Z]+" + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#header.fields +# field-name = token +field_name = token + +# The standard says: +# +# field-value = *( field-content / obs-fold ) +# field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] +# field-vchar = VCHAR / obs-text +# obs-fold = CRLF 1*( SP / HTAB ) +# ; obsolete line folding +# ; see Section 3.2.4 +# +# https://tools.ietf.org/html/rfc5234#appendix-B.1 +# +# VCHAR = %x21-7E +# ; visible (printing) characters +# +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.quoted-string +# obs-text = %x80-FF +# +# However, the standard definition of field-content is WRONG! It disallows +# fields containing a single visible character surrounded by whitespace, +# e.g. "foo a bar". +# +# See: https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189 +# +# So our definition of field_content attempts to fix it up... +# +# Also, we allow lots of control characters, because apparently people assume +# that they're legal in practice (e.g., google analytics makes cookies with +# \x01 in them!): +# https://github.com/python-hyper/h11/issues/57 +# We still don't allow NUL or whitespace, because those are often treated as +# meta-characters and letting them through can lead to nasty issues like SSRF. +vchar = r"[\x21-\x7e]" +vchar_or_obs_text = r"[^\x00\s]" +field_vchar = vchar_or_obs_text +field_content = r"{field_vchar}+(?:[ \t]+{field_vchar}+)*".format(**globals()) + +# We handle obs-fold at a different level, and our fixed-up field_content +# already grows to swallow the whole value, so ? instead of * +field_value = r"({field_content})?".format(**globals()) + +# header-field = field-name ":" OWS field-value OWS +header_field = ( + r"(?P{field_name})" + r":" + r"{OWS}" + r"(?P{field_value})" + r"{OWS}".format(**globals()) +) + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#request.line +# +# request-line = method SP request-target SP HTTP-version CRLF +# method = token +# HTTP-version = HTTP-name "/" DIGIT "." DIGIT +# HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive +# +# request-target is complicated (see RFC 7230 sec 5.3) -- could be path, full +# URL, host+port (for connect), or even "*", but in any case we are guaranteed +# that it contists of the visible printing characters. +method = token +request_target = r"{vchar}+".format(**globals()) +http_version = r"HTTP/(?P[0-9]\.[0-9])" +request_line = ( + r"(?P{method})" + r" " + r"(?P{request_target})" + r" " + r"{http_version}".format(**globals()) +) + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#status.line +# +# status-line = HTTP-version SP status-code SP reason-phrase CRLF +# status-code = 3DIGIT +# reason-phrase = *( HTAB / SP / VCHAR / obs-text ) +status_code = r"[0-9]{3}" +reason_phrase = r"([ \t]|{vchar_or_obs_text})*".format(**globals()) +status_line = ( + r"{http_version}" + r" " + r"(?P{status_code})" + # However, there are apparently a few too many servers out there that just + # leave out the reason phrase: + # https://github.com/scrapy/scrapy/issues/345#issuecomment-281756036 + # https://github.com/seanmonstar/httparse/issues/29 + # so make it optional. ?: is a non-capturing group. + r"(?: (?P{reason_phrase}))?".format(**globals()) +) + +HEXDIG = r"[0-9A-Fa-f]" +# Actually +# +# chunk-size = 1*HEXDIG +# +# but we impose an upper-limit to avoid ridiculosity. len(str(2**64)) == 20 +chunk_size = r"({HEXDIG}){{1,20}}".format(**globals()) +# Actually +# +# chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) +# +# but we aren't parsing the things so we don't really care. +chunk_ext = r";.*" +chunk_header = ( + r"(?P{chunk_size})" + r"(?P{chunk_ext})?" + r"{OWS}\r\n".format( + **globals() + ) # Even though the specification does not allow for extra whitespaces, + # we are lenient with trailing whitespaces because some servers on the wild use it. +) diff --git a/venv/lib/python3.12/site-packages/h11/_connection.py b/venv/lib/python3.12/site-packages/h11/_connection.py new file mode 100644 index 0000000..e37d82a --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11/_connection.py @@ -0,0 +1,659 @@ +# This contains the main Connection class. Everything in h11 revolves around +# this. +from typing import ( + Any, + Callable, + cast, + Dict, + List, + Optional, + overload, + Tuple, + Type, + Union, +) + +from ._events import ( + ConnectionClosed, + Data, + EndOfMessage, + Event, + InformationalResponse, + Request, + Response, +) +from ._headers import get_comma_header, has_expect_100_continue, set_comma_header +from ._readers import READERS, ReadersType +from ._receivebuffer import ReceiveBuffer +from ._state import ( + _SWITCH_CONNECT, + _SWITCH_UPGRADE, + CLIENT, + ConnectionState, + DONE, + ERROR, + MIGHT_SWITCH_PROTOCOL, + SEND_BODY, + SERVER, + SWITCHED_PROTOCOL, +) +from ._util import ( # Import the internal things we need + LocalProtocolError, + RemoteProtocolError, + Sentinel, +) +from ._writers import WRITERS, WritersType + +# Everything in __all__ gets re-exported as part of the h11 public API. +__all__ = ["Connection", "NEED_DATA", "PAUSED"] + + +class NEED_DATA(Sentinel, metaclass=Sentinel): + pass + + +class PAUSED(Sentinel, metaclass=Sentinel): + pass + + +# If we ever have this much buffered without it making a complete parseable +# event, we error out. The only time we really buffer is when reading the +# request/response line + headers together, so this is effectively the limit on +# the size of that. +# +# Some precedents for defaults: +# - node.js: 80 * 1024 +# - tomcat: 8 * 1024 +# - IIS: 16 * 1024 +# - Apache: <8 KiB per line> +DEFAULT_MAX_INCOMPLETE_EVENT_SIZE = 16 * 1024 + + +# RFC 7230's rules for connection lifecycles: +# - If either side says they want to close the connection, then the connection +# must close. +# - HTTP/1.1 defaults to keep-alive unless someone says Connection: close +# - HTTP/1.0 defaults to close unless both sides say Connection: keep-alive +# (and even this is a mess -- e.g. if you're implementing a proxy then +# sending Connection: keep-alive is forbidden). +# +# We simplify life by simply not supporting keep-alive with HTTP/1.0 peers. So +# our rule is: +# - If someone says Connection: close, we will close +# - If someone uses HTTP/1.0, we will close. +def _keep_alive(event: Union[Request, Response]) -> bool: + connection = get_comma_header(event.headers, b"connection") + if b"close" in connection: + return False + if getattr(event, "http_version", b"1.1") < b"1.1": + return False + return True + + +def _body_framing( + request_method: bytes, event: Union[Request, Response] +) -> Tuple[str, Union[Tuple[()], Tuple[int]]]: + # Called when we enter SEND_BODY to figure out framing information for + # this body. + # + # These are the only two events that can trigger a SEND_BODY state: + assert type(event) in (Request, Response) + # Returns one of: + # + # ("content-length", count) + # ("chunked", ()) + # ("http/1.0", ()) + # + # which are (lookup key, *args) for constructing body reader/writer + # objects. + # + # Reference: https://tools.ietf.org/html/rfc7230#section-3.3.3 + # + # Step 1: some responses always have an empty body, regardless of what the + # headers say. + if type(event) is Response: + if ( + event.status_code in (204, 304) + or request_method == b"HEAD" + or (request_method == b"CONNECT" and 200 <= event.status_code < 300) + ): + return ("content-length", (0,)) + # Section 3.3.3 also lists another case -- responses with status_code + # < 200. For us these are InformationalResponses, not Responses, so + # they can't get into this function in the first place. + assert event.status_code >= 200 + + # Step 2: check for Transfer-Encoding (T-E beats C-L): + transfer_encodings = get_comma_header(event.headers, b"transfer-encoding") + if transfer_encodings: + assert transfer_encodings == [b"chunked"] + return ("chunked", ()) + + # Step 3: check for Content-Length + content_lengths = get_comma_header(event.headers, b"content-length") + if content_lengths: + return ("content-length", (int(content_lengths[0]),)) + + # Step 4: no applicable headers; fallback/default depends on type + if type(event) is Request: + return ("content-length", (0,)) + else: + return ("http/1.0", ()) + + +################################################################ +# +# The main Connection class +# +################################################################ + + +class Connection: + """An object encapsulating the state of an HTTP connection. + + Args: + our_role: If you're implementing a client, pass :data:`h11.CLIENT`. If + you're implementing a server, pass :data:`h11.SERVER`. + + max_incomplete_event_size (int): + The maximum number of bytes we're willing to buffer of an + incomplete event. In practice this mostly sets a limit on the + maximum size of the request/response line + headers. If this is + exceeded, then :meth:`next_event` will raise + :exc:`RemoteProtocolError`. + + """ + + def __init__( + self, + our_role: Type[Sentinel], + max_incomplete_event_size: int = DEFAULT_MAX_INCOMPLETE_EVENT_SIZE, + ) -> None: + self._max_incomplete_event_size = max_incomplete_event_size + # State and role tracking + if our_role not in (CLIENT, SERVER): + raise ValueError(f"expected CLIENT or SERVER, not {our_role!r}") + self.our_role = our_role + self.their_role: Type[Sentinel] + if our_role is CLIENT: + self.their_role = SERVER + else: + self.their_role = CLIENT + self._cstate = ConnectionState() + + # Callables for converting data->events or vice-versa given the + # current state + self._writer = self._get_io_object(self.our_role, None, WRITERS) + self._reader = self._get_io_object(self.their_role, None, READERS) + + # Holds any unprocessed received data + self._receive_buffer = ReceiveBuffer() + # If this is true, then it indicates that the incoming connection was + # closed *after* the end of whatever's in self._receive_buffer: + self._receive_buffer_closed = False + + # Extra bits of state that don't fit into the state machine. + # + # These two are only used to interpret framing headers for figuring + # out how to read/write response bodies. their_http_version is also + # made available as a convenient public API. + self.their_http_version: Optional[bytes] = None + self._request_method: Optional[bytes] = None + # This is pure flow-control and doesn't at all affect the set of legal + # transitions, so no need to bother ConnectionState with it: + self.client_is_waiting_for_100_continue = False + + @property + def states(self) -> Dict[Type[Sentinel], Type[Sentinel]]: + """A dictionary like:: + + {CLIENT: , SERVER: } + + See :ref:`state-machine` for details. + + """ + return dict(self._cstate.states) + + @property + def our_state(self) -> Type[Sentinel]: + """The current state of whichever role we are playing. See + :ref:`state-machine` for details. + """ + return self._cstate.states[self.our_role] + + @property + def their_state(self) -> Type[Sentinel]: + """The current state of whichever role we are NOT playing. See + :ref:`state-machine` for details. + """ + return self._cstate.states[self.their_role] + + @property + def they_are_waiting_for_100_continue(self) -> bool: + return self.their_role is CLIENT and self.client_is_waiting_for_100_continue + + def start_next_cycle(self) -> None: + """Attempt to reset our connection state for a new request/response + cycle. + + If both client and server are in :data:`DONE` state, then resets them + both to :data:`IDLE` state in preparation for a new request/response + cycle on this same connection. Otherwise, raises a + :exc:`LocalProtocolError`. + + See :ref:`keepalive-and-pipelining`. + + """ + old_states = dict(self._cstate.states) + self._cstate.start_next_cycle() + self._request_method = None + # self.their_http_version gets left alone, since it presumably lasts + # beyond a single request/response cycle + assert not self.client_is_waiting_for_100_continue + self._respond_to_state_changes(old_states) + + def _process_error(self, role: Type[Sentinel]) -> None: + old_states = dict(self._cstate.states) + self._cstate.process_error(role) + self._respond_to_state_changes(old_states) + + def _server_switch_event(self, event: Event) -> Optional[Type[Sentinel]]: + if type(event) is InformationalResponse and event.status_code == 101: + return _SWITCH_UPGRADE + if type(event) is Response: + if ( + _SWITCH_CONNECT in self._cstate.pending_switch_proposals + and 200 <= event.status_code < 300 + ): + return _SWITCH_CONNECT + return None + + # All events go through here + def _process_event(self, role: Type[Sentinel], event: Event) -> None: + # First, pass the event through the state machine to make sure it + # succeeds. + old_states = dict(self._cstate.states) + if role is CLIENT and type(event) is Request: + if event.method == b"CONNECT": + self._cstate.process_client_switch_proposal(_SWITCH_CONNECT) + if get_comma_header(event.headers, b"upgrade"): + self._cstate.process_client_switch_proposal(_SWITCH_UPGRADE) + server_switch_event = None + if role is SERVER: + server_switch_event = self._server_switch_event(event) + self._cstate.process_event(role, type(event), server_switch_event) + + # Then perform the updates triggered by it. + + if type(event) is Request: + self._request_method = event.method + + if role is self.their_role and type(event) in ( + Request, + Response, + InformationalResponse, + ): + event = cast(Union[Request, Response, InformationalResponse], event) + self.their_http_version = event.http_version + + # Keep alive handling + # + # RFC 7230 doesn't really say what one should do if Connection: close + # shows up on a 1xx InformationalResponse. I think the idea is that + # this is not supposed to happen. In any case, if it does happen, we + # ignore it. + if type(event) in (Request, Response) and not _keep_alive( + cast(Union[Request, Response], event) + ): + self._cstate.process_keep_alive_disabled() + + # 100-continue + if type(event) is Request and has_expect_100_continue(event): + self.client_is_waiting_for_100_continue = True + if type(event) in (InformationalResponse, Response): + self.client_is_waiting_for_100_continue = False + if role is CLIENT and type(event) in (Data, EndOfMessage): + self.client_is_waiting_for_100_continue = False + + self._respond_to_state_changes(old_states, event) + + def _get_io_object( + self, + role: Type[Sentinel], + event: Optional[Event], + io_dict: Union[ReadersType, WritersType], + ) -> Optional[Callable[..., Any]]: + # event may be None; it's only used when entering SEND_BODY + state = self._cstate.states[role] + if state is SEND_BODY: + # Special case: the io_dict has a dict of reader/writer factories + # that depend on the request/response framing. + framing_type, args = _body_framing( + cast(bytes, self._request_method), cast(Union[Request, Response], event) + ) + return io_dict[SEND_BODY][framing_type](*args) # type: ignore[index] + else: + # General case: the io_dict just has the appropriate reader/writer + # for this state + return io_dict.get((role, state)) # type: ignore[return-value] + + # This must be called after any action that might have caused + # self._cstate.states to change. + def _respond_to_state_changes( + self, + old_states: Dict[Type[Sentinel], Type[Sentinel]], + event: Optional[Event] = None, + ) -> None: + # Update reader/writer + if self.our_state != old_states[self.our_role]: + self._writer = self._get_io_object(self.our_role, event, WRITERS) + if self.their_state != old_states[self.their_role]: + self._reader = self._get_io_object(self.their_role, event, READERS) + + @property + def trailing_data(self) -> Tuple[bytes, bool]: + """Data that has been received, but not yet processed, represented as + a tuple with two elements, where the first is a byte-string containing + the unprocessed data itself, and the second is a bool that is True if + the receive connection was closed. + + See :ref:`switching-protocols` for discussion of why you'd want this. + """ + return (bytes(self._receive_buffer), self._receive_buffer_closed) + + def receive_data(self, data: bytes) -> None: + """Add data to our internal receive buffer. + + This does not actually do any processing on the data, just stores + it. To trigger processing, you have to call :meth:`next_event`. + + Args: + data (:term:`bytes-like object`): + The new data that was just received. + + Special case: If *data* is an empty byte-string like ``b""``, + then this indicates that the remote side has closed the + connection (end of file). Normally this is convenient, because + standard Python APIs like :meth:`file.read` or + :meth:`socket.recv` use ``b""`` to indicate end-of-file, while + other failures to read are indicated using other mechanisms + like raising :exc:`TimeoutError`. When using such an API you + can just blindly pass through whatever you get from ``read`` + to :meth:`receive_data`, and everything will work. + + But, if you have an API where reading an empty string is a + valid non-EOF condition, then you need to be aware of this and + make sure to check for such strings and avoid passing them to + :meth:`receive_data`. + + Returns: + Nothing, but after calling this you should call :meth:`next_event` + to parse the newly received data. + + Raises: + RuntimeError: + Raised if you pass an empty *data*, indicating EOF, and then + pass a non-empty *data*, indicating more data that somehow + arrived after the EOF. + + (Calling ``receive_data(b"")`` multiple times is fine, + and equivalent to calling it once.) + + """ + if data: + if self._receive_buffer_closed: + raise RuntimeError("received close, then received more data?") + self._receive_buffer += data + else: + self._receive_buffer_closed = True + + def _extract_next_receive_event( + self, + ) -> Union[Event, Type[NEED_DATA], Type[PAUSED]]: + state = self.their_state + # We don't pause immediately when they enter DONE, because even in + # DONE state we can still process a ConnectionClosed() event. But + # if we have data in our buffer, then we definitely aren't getting + # a ConnectionClosed() immediately and we need to pause. + if state is DONE and self._receive_buffer: + return PAUSED + if state is MIGHT_SWITCH_PROTOCOL or state is SWITCHED_PROTOCOL: + return PAUSED + assert self._reader is not None + event = self._reader(self._receive_buffer) + if event is None: + if not self._receive_buffer and self._receive_buffer_closed: + # In some unusual cases (basically just HTTP/1.0 bodies), EOF + # triggers an actual protocol event; in that case, we want to + # return that event, and then the state will change and we'll + # get called again to generate the actual ConnectionClosed(). + if hasattr(self._reader, "read_eof"): + event = self._reader.read_eof() + else: + event = ConnectionClosed() + if event is None: + event = NEED_DATA + return event # type: ignore[no-any-return] + + def next_event(self) -> Union[Event, Type[NEED_DATA], Type[PAUSED]]: + """Parse the next event out of our receive buffer, update our internal + state, and return it. + + This is a mutating operation -- think of it like calling :func:`next` + on an iterator. + + Returns: + : One of three things: + + 1) An event object -- see :ref:`events`. + + 2) The special constant :data:`NEED_DATA`, which indicates that + you need to read more data from your socket and pass it to + :meth:`receive_data` before this method will be able to return + any more events. + + 3) The special constant :data:`PAUSED`, which indicates that we + are not in a state where we can process incoming data (usually + because the peer has finished their part of the current + request/response cycle, and you have not yet called + :meth:`start_next_cycle`). See :ref:`flow-control` for details. + + Raises: + RemoteProtocolError: + The peer has misbehaved. You should close the connection + (possibly after sending some kind of 4xx response). + + Once this method returns :class:`ConnectionClosed` once, then all + subsequent calls will also return :class:`ConnectionClosed`. + + If this method raises any exception besides :exc:`RemoteProtocolError` + then that's a bug -- if it happens please file a bug report! + + If this method raises any exception then it also sets + :attr:`Connection.their_state` to :data:`ERROR` -- see + :ref:`error-handling` for discussion. + + """ + + if self.their_state is ERROR: + raise RemoteProtocolError("Can't receive data when peer state is ERROR") + try: + event = self._extract_next_receive_event() + if event not in [NEED_DATA, PAUSED]: + self._process_event(self.their_role, cast(Event, event)) + if event is NEED_DATA: + if len(self._receive_buffer) > self._max_incomplete_event_size: + # 431 is "Request header fields too large" which is pretty + # much the only situation where we can get here + raise RemoteProtocolError( + "Receive buffer too long", error_status_hint=431 + ) + if self._receive_buffer_closed: + # We're still trying to complete some event, but that's + # never going to happen because no more data is coming + raise RemoteProtocolError("peer unexpectedly closed connection") + return event + except BaseException as exc: + self._process_error(self.their_role) + if isinstance(exc, LocalProtocolError): + exc._reraise_as_remote_protocol_error() + else: + raise + + @overload + def send(self, event: ConnectionClosed) -> None: + ... + + @overload + def send( + self, event: Union[Request, InformationalResponse, Response, Data, EndOfMessage] + ) -> bytes: + ... + + @overload + def send(self, event: Event) -> Optional[bytes]: + ... + + def send(self, event: Event) -> Optional[bytes]: + """Convert a high-level event into bytes that can be sent to the peer, + while updating our internal state machine. + + Args: + event: The :ref:`event ` to send. + + Returns: + If ``type(event) is ConnectionClosed``, then returns + ``None``. Otherwise, returns a :term:`bytes-like object`. + + Raises: + LocalProtocolError: + Sending this event at this time would violate our + understanding of the HTTP/1.1 protocol. + + If this method raises any exception then it also sets + :attr:`Connection.our_state` to :data:`ERROR` -- see + :ref:`error-handling` for discussion. + + """ + data_list = self.send_with_data_passthrough(event) + if data_list is None: + return None + else: + return b"".join(data_list) + + def send_with_data_passthrough(self, event: Event) -> Optional[List[bytes]]: + """Identical to :meth:`send`, except that in situations where + :meth:`send` returns a single :term:`bytes-like object`, this instead + returns a list of them -- and when sending a :class:`Data` event, this + list is guaranteed to contain the exact object you passed in as + :attr:`Data.data`. See :ref:`sendfile` for discussion. + + """ + if self.our_state is ERROR: + raise LocalProtocolError("Can't send data when our state is ERROR") + try: + if type(event) is Response: + event = self._clean_up_response_headers_for_sending(event) + # We want to call _process_event before calling the writer, + # because if someone tries to do something invalid then this will + # give a sensible error message, while our writers all just assume + # they will only receive valid events. But, _process_event might + # change self._writer. So we have to do a little dance: + writer = self._writer + self._process_event(self.our_role, event) + if type(event) is ConnectionClosed: + return None + else: + # In any situation where writer is None, process_event should + # have raised ProtocolError + assert writer is not None + data_list: List[bytes] = [] + writer(event, data_list.append) + return data_list + except: + self._process_error(self.our_role) + raise + + def send_failed(self) -> None: + """Notify the state machine that we failed to send the data it gave + us. + + This causes :attr:`Connection.our_state` to immediately become + :data:`ERROR` -- see :ref:`error-handling` for discussion. + + """ + self._process_error(self.our_role) + + # When sending a Response, we take responsibility for a few things: + # + # - Sometimes you MUST set Connection: close. We take care of those + # times. (You can also set it yourself if you want, and if you do then + # we'll respect that and close the connection at the right time. But you + # don't have to worry about that unless you want to.) + # + # - The user has to set Content-Length if they want it. Otherwise, for + # responses that have bodies (e.g. not HEAD), then we will automatically + # select the right mechanism for streaming a body of unknown length, + # which depends on depending on the peer's HTTP version. + # + # This function's *only* responsibility is making sure headers are set up + # right -- everything downstream just looks at the headers. There are no + # side channels. + def _clean_up_response_headers_for_sending(self, response: Response) -> Response: + assert type(response) is Response + + headers = response.headers + need_close = False + + # HEAD requests need some special handling: they always act like they + # have Content-Length: 0, and that's how _body_framing treats + # them. But their headers are supposed to match what we would send if + # the request was a GET. (Technically there is one deviation allowed: + # we're allowed to leave out the framing headers -- see + # https://tools.ietf.org/html/rfc7231#section-4.3.2 . But it's just as + # easy to get them right.) + method_for_choosing_headers = cast(bytes, self._request_method) + if method_for_choosing_headers == b"HEAD": + method_for_choosing_headers = b"GET" + framing_type, _ = _body_framing(method_for_choosing_headers, response) + if framing_type in ("chunked", "http/1.0"): + # This response has a body of unknown length. + # If our peer is HTTP/1.1, we use Transfer-Encoding: chunked + # If our peer is HTTP/1.0, we use no framing headers, and close the + # connection afterwards. + # + # Make sure to clear Content-Length (in principle user could have + # set both and then we ignored Content-Length b/c + # Transfer-Encoding overwrote it -- this would be naughty of them, + # but the HTTP spec says that if our peer does this then we have + # to fix it instead of erroring out, so we'll accord the user the + # same respect). + headers = set_comma_header(headers, b"content-length", []) + if self.their_http_version is None or self.their_http_version < b"1.1": + # Either we never got a valid request and are sending back an + # error (their_http_version is None), so we assume the worst; + # or else we did get a valid HTTP/1.0 request, so we know that + # they don't understand chunked encoding. + headers = set_comma_header(headers, b"transfer-encoding", []) + # This is actually redundant ATM, since currently we + # unconditionally disable keep-alive when talking to HTTP/1.0 + # peers. But let's be defensive just in case we add + # Connection: keep-alive support later: + if self._request_method != b"HEAD": + need_close = True + else: + headers = set_comma_header(headers, b"transfer-encoding", [b"chunked"]) + + if not self._cstate.keep_alive or need_close: + # Make sure Connection: close is set + connection = set(get_comma_header(headers, b"connection")) + connection.discard(b"keep-alive") + connection.add(b"close") + headers = set_comma_header(headers, b"connection", sorted(connection)) + + return Response( + headers=headers, + status_code=response.status_code, + http_version=response.http_version, + reason=response.reason, + ) diff --git a/venv/lib/python3.12/site-packages/h11/_events.py b/venv/lib/python3.12/site-packages/h11/_events.py new file mode 100644 index 0000000..ca1c3ad --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11/_events.py @@ -0,0 +1,369 @@ +# High level events that make up HTTP/1.1 conversations. Loosely inspired by +# the corresponding events in hyper-h2: +# +# http://python-hyper.org/h2/en/stable/api.html#events +# +# Don't subclass these. Stuff will break. + +import re +from abc import ABC +from dataclasses import dataclass +from typing import List, Tuple, Union + +from ._abnf import method, request_target +from ._headers import Headers, normalize_and_validate +from ._util import bytesify, LocalProtocolError, validate + +# Everything in __all__ gets re-exported as part of the h11 public API. +__all__ = [ + "Event", + "Request", + "InformationalResponse", + "Response", + "Data", + "EndOfMessage", + "ConnectionClosed", +] + +method_re = re.compile(method.encode("ascii")) +request_target_re = re.compile(request_target.encode("ascii")) + + +class Event(ABC): + """ + Base class for h11 events. + """ + + __slots__ = () + + +@dataclass(init=False, frozen=True) +class Request(Event): + """The beginning of an HTTP request. + + Fields: + + .. attribute:: method + + An HTTP method, e.g. ``b"GET"`` or ``b"POST"``. Always a byte + string. :term:`Bytes-like objects ` and native + strings containing only ascii characters will be automatically + converted to byte strings. + + .. attribute:: target + + The target of an HTTP request, e.g. ``b"/index.html"``, or one of the + more exotic formats described in `RFC 7320, section 5.3 + `_. Always a byte + string. :term:`Bytes-like objects ` and native + strings containing only ascii characters will be automatically + converted to byte strings. + + .. attribute:: headers + + Request headers, represented as a list of (name, value) pairs. See + :ref:`the header normalization rules ` for details. + + .. attribute:: http_version + + The HTTP protocol version, represented as a byte string like + ``b"1.1"``. See :ref:`the HTTP version normalization rules + ` for details. + + """ + + __slots__ = ("method", "headers", "target", "http_version") + + method: bytes + headers: Headers + target: bytes + http_version: bytes + + def __init__( + self, + *, + method: Union[bytes, str], + headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]], + target: Union[bytes, str], + http_version: Union[bytes, str] = b"1.1", + _parsed: bool = False, + ) -> None: + super().__init__() + if isinstance(headers, Headers): + object.__setattr__(self, "headers", headers) + else: + object.__setattr__( + self, "headers", normalize_and_validate(headers, _parsed=_parsed) + ) + if not _parsed: + object.__setattr__(self, "method", bytesify(method)) + object.__setattr__(self, "target", bytesify(target)) + object.__setattr__(self, "http_version", bytesify(http_version)) + else: + object.__setattr__(self, "method", method) + object.__setattr__(self, "target", target) + object.__setattr__(self, "http_version", http_version) + + # "A server MUST respond with a 400 (Bad Request) status code to any + # HTTP/1.1 request message that lacks a Host header field and to any + # request message that contains more than one Host header field or a + # Host header field with an invalid field-value." + # -- https://tools.ietf.org/html/rfc7230#section-5.4 + host_count = 0 + for name, value in self.headers: + if name == b"host": + host_count += 1 + if self.http_version == b"1.1" and host_count == 0: + raise LocalProtocolError("Missing mandatory Host: header") + if host_count > 1: + raise LocalProtocolError("Found multiple Host: headers") + + validate(method_re, self.method, "Illegal method characters") + validate(request_target_re, self.target, "Illegal target characters") + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(init=False, frozen=True) +class _ResponseBase(Event): + __slots__ = ("headers", "http_version", "reason", "status_code") + + headers: Headers + http_version: bytes + reason: bytes + status_code: int + + def __init__( + self, + *, + headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]], + status_code: int, + http_version: Union[bytes, str] = b"1.1", + reason: Union[bytes, str] = b"", + _parsed: bool = False, + ) -> None: + super().__init__() + if isinstance(headers, Headers): + object.__setattr__(self, "headers", headers) + else: + object.__setattr__( + self, "headers", normalize_and_validate(headers, _parsed=_parsed) + ) + if not _parsed: + object.__setattr__(self, "reason", bytesify(reason)) + object.__setattr__(self, "http_version", bytesify(http_version)) + if not isinstance(status_code, int): + raise LocalProtocolError("status code must be integer") + # Because IntEnum objects are instances of int, but aren't + # duck-compatible (sigh), see gh-72. + object.__setattr__(self, "status_code", int(status_code)) + else: + object.__setattr__(self, "reason", reason) + object.__setattr__(self, "http_version", http_version) + object.__setattr__(self, "status_code", status_code) + + self.__post_init__() + + def __post_init__(self) -> None: + pass + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(init=False, frozen=True) +class InformationalResponse(_ResponseBase): + """An HTTP informational response. + + Fields: + + .. attribute:: status_code + + The status code of this response, as an integer. For an + :class:`InformationalResponse`, this is always in the range [100, + 200). + + .. attribute:: headers + + Request headers, represented as a list of (name, value) pairs. See + :ref:`the header normalization rules ` for + details. + + .. attribute:: http_version + + The HTTP protocol version, represented as a byte string like + ``b"1.1"``. See :ref:`the HTTP version normalization rules + ` for details. + + .. attribute:: reason + + The reason phrase of this response, as a byte string. For example: + ``b"OK"``, or ``b"Not Found"``. + + """ + + def __post_init__(self) -> None: + if not (100 <= self.status_code < 200): + raise LocalProtocolError( + "InformationalResponse status_code should be in range " + "[100, 200), not {}".format(self.status_code) + ) + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(init=False, frozen=True) +class Response(_ResponseBase): + """The beginning of an HTTP response. + + Fields: + + .. attribute:: status_code + + The status code of this response, as an integer. For an + :class:`Response`, this is always in the range [200, + 1000). + + .. attribute:: headers + + Request headers, represented as a list of (name, value) pairs. See + :ref:`the header normalization rules ` for details. + + .. attribute:: http_version + + The HTTP protocol version, represented as a byte string like + ``b"1.1"``. See :ref:`the HTTP version normalization rules + ` for details. + + .. attribute:: reason + + The reason phrase of this response, as a byte string. For example: + ``b"OK"``, or ``b"Not Found"``. + + """ + + def __post_init__(self) -> None: + if not (200 <= self.status_code < 1000): + raise LocalProtocolError( + "Response status_code should be in range [200, 1000), not {}".format( + self.status_code + ) + ) + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(init=False, frozen=True) +class Data(Event): + """Part of an HTTP message body. + + Fields: + + .. attribute:: data + + A :term:`bytes-like object` containing part of a message body. Or, if + using the ``combine=False`` argument to :meth:`Connection.send`, then + any object that your socket writing code knows what to do with, and for + which calling :func:`len` returns the number of bytes that will be + written -- see :ref:`sendfile` for details. + + .. attribute:: chunk_start + + A marker that indicates whether this data object is from the start of a + chunked transfer encoding chunk. This field is ignored when when a Data + event is provided to :meth:`Connection.send`: it is only valid on + events emitted from :meth:`Connection.next_event`. You probably + shouldn't use this attribute at all; see + :ref:`chunk-delimiters-are-bad` for details. + + .. attribute:: chunk_end + + A marker that indicates whether this data object is the last for a + given chunked transfer encoding chunk. This field is ignored when when + a Data event is provided to :meth:`Connection.send`: it is only valid + on events emitted from :meth:`Connection.next_event`. You probably + shouldn't use this attribute at all; see + :ref:`chunk-delimiters-are-bad` for details. + + """ + + __slots__ = ("data", "chunk_start", "chunk_end") + + data: bytes + chunk_start: bool + chunk_end: bool + + def __init__( + self, data: bytes, chunk_start: bool = False, chunk_end: bool = False + ) -> None: + object.__setattr__(self, "data", data) + object.__setattr__(self, "chunk_start", chunk_start) + object.__setattr__(self, "chunk_end", chunk_end) + + # This is an unhashable type. + __hash__ = None # type: ignore + + +# XX FIXME: "A recipient MUST ignore (or consider as an error) any fields that +# are forbidden to be sent in a trailer, since processing them as if they were +# present in the header section might bypass external security filters." +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#chunked.trailer.part +# Unfortunately, the list of forbidden fields is long and vague :-/ +@dataclass(init=False, frozen=True) +class EndOfMessage(Event): + """The end of an HTTP message. + + Fields: + + .. attribute:: headers + + Default value: ``[]`` + + Any trailing headers attached to this message, represented as a list of + (name, value) pairs. See :ref:`the header normalization rules + ` for details. + + Must be empty unless ``Transfer-Encoding: chunked`` is in use. + + """ + + __slots__ = ("headers",) + + headers: Headers + + def __init__( + self, + *, + headers: Union[ + Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]], None + ] = None, + _parsed: bool = False, + ) -> None: + super().__init__() + if headers is None: + headers = Headers([]) + elif not isinstance(headers, Headers): + headers = normalize_and_validate(headers, _parsed=_parsed) + + object.__setattr__(self, "headers", headers) + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(frozen=True) +class ConnectionClosed(Event): + """This event indicates that the sender has closed their outgoing + connection. + + Note that this does not necessarily mean that they can't *receive* further + data, because TCP connections are composed to two one-way channels which + can be closed independently. See :ref:`closing` for details. + + No fields. + """ + + pass diff --git a/venv/lib/python3.12/site-packages/h11/_headers.py b/venv/lib/python3.12/site-packages/h11/_headers.py new file mode 100644 index 0000000..31da3e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11/_headers.py @@ -0,0 +1,282 @@ +import re +from typing import AnyStr, cast, List, overload, Sequence, Tuple, TYPE_CHECKING, Union + +from ._abnf import field_name, field_value +from ._util import bytesify, LocalProtocolError, validate + +if TYPE_CHECKING: + from ._events import Request + +try: + from typing import Literal +except ImportError: + from typing_extensions import Literal # type: ignore + +CONTENT_LENGTH_MAX_DIGITS = 20 # allow up to 1 billion TB - 1 + + +# Facts +# ----- +# +# Headers are: +# keys: case-insensitive ascii +# values: mixture of ascii and raw bytes +# +# "Historically, HTTP has allowed field content with text in the ISO-8859-1 +# charset [ISO-8859-1], supporting other charsets only through use of +# [RFC2047] encoding. In practice, most HTTP header field values use only a +# subset of the US-ASCII charset [USASCII]. Newly defined header fields SHOULD +# limit their field values to US-ASCII octets. A recipient SHOULD treat other +# octets in field content (obs-text) as opaque data." +# And it deprecates all non-ascii values +# +# Leading/trailing whitespace in header names is forbidden +# +# Values get leading/trailing whitespace stripped +# +# Content-Disposition actually needs to contain unicode semantically; to +# accomplish this it has a terrifically weird way of encoding the filename +# itself as ascii (and even this still has lots of cross-browser +# incompatibilities) +# +# Order is important: +# "a proxy MUST NOT change the order of these field values when forwarding a +# message" +# (and there are several headers where the order indicates a preference) +# +# Multiple occurences of the same header: +# "A sender MUST NOT generate multiple header fields with the same field name +# in a message unless either the entire field value for that header field is +# defined as a comma-separated list [or the header is Set-Cookie which gets a +# special exception]" - RFC 7230. (cookies are in RFC 6265) +# +# So every header aside from Set-Cookie can be merged by b", ".join if it +# occurs repeatedly. But, of course, they can't necessarily be split by +# .split(b","), because quoting. +# +# Given all this mess (case insensitive, duplicates allowed, order is +# important, ...), there doesn't appear to be any standard way to handle +# headers in Python -- they're almost like dicts, but... actually just +# aren't. For now we punt and just use a super simple representation: headers +# are a list of pairs +# +# [(name1, value1), (name2, value2), ...] +# +# where all entries are bytestrings, names are lowercase and have no +# leading/trailing whitespace, and values are bytestrings with no +# leading/trailing whitespace. Searching and updating are done via naive O(n) +# methods. +# +# Maybe a dict-of-lists would be better? + +_content_length_re = re.compile(rb"[0-9]+") +_field_name_re = re.compile(field_name.encode("ascii")) +_field_value_re = re.compile(field_value.encode("ascii")) + + +class Headers(Sequence[Tuple[bytes, bytes]]): + """ + A list-like interface that allows iterating over headers as byte-pairs + of (lowercased-name, value). + + Internally we actually store the representation as three-tuples, + including both the raw original casing, in order to preserve casing + over-the-wire, and the lowercased name, for case-insensitive comparisions. + + r = Request( + method="GET", + target="/", + headers=[("Host", "example.org"), ("Connection", "keep-alive")], + http_version="1.1", + ) + assert r.headers == [ + (b"host", b"example.org"), + (b"connection", b"keep-alive") + ] + assert r.headers.raw_items() == [ + (b"Host", b"example.org"), + (b"Connection", b"keep-alive") + ] + """ + + __slots__ = "_full_items" + + def __init__(self, full_items: List[Tuple[bytes, bytes, bytes]]) -> None: + self._full_items = full_items + + def __bool__(self) -> bool: + return bool(self._full_items) + + def __eq__(self, other: object) -> bool: + return list(self) == list(other) # type: ignore + + def __len__(self) -> int: + return len(self._full_items) + + def __repr__(self) -> str: + return "" % repr(list(self)) + + def __getitem__(self, idx: int) -> Tuple[bytes, bytes]: # type: ignore[override] + _, name, value = self._full_items[idx] + return (name, value) + + def raw_items(self) -> List[Tuple[bytes, bytes]]: + return [(raw_name, value) for raw_name, _, value in self._full_items] + + +HeaderTypes = Union[ + List[Tuple[bytes, bytes]], + List[Tuple[bytes, str]], + List[Tuple[str, bytes]], + List[Tuple[str, str]], +] + + +@overload +def normalize_and_validate(headers: Headers, _parsed: Literal[True]) -> Headers: + ... + + +@overload +def normalize_and_validate(headers: HeaderTypes, _parsed: Literal[False]) -> Headers: + ... + + +@overload +def normalize_and_validate( + headers: Union[Headers, HeaderTypes], _parsed: bool = False +) -> Headers: + ... + + +def normalize_and_validate( + headers: Union[Headers, HeaderTypes], _parsed: bool = False +) -> Headers: + new_headers = [] + seen_content_length = None + saw_transfer_encoding = False + for name, value in headers: + # For headers coming out of the parser, we can safely skip some steps, + # because it always returns bytes and has already run these regexes + # over the data: + if not _parsed: + name = bytesify(name) + value = bytesify(value) + validate(_field_name_re, name, "Illegal header name {!r}", name) + validate(_field_value_re, value, "Illegal header value {!r}", value) + assert isinstance(name, bytes) + assert isinstance(value, bytes) + + raw_name = name + name = name.lower() + if name == b"content-length": + lengths = {length.strip() for length in value.split(b",")} + if len(lengths) != 1: + raise LocalProtocolError("conflicting Content-Length headers") + value = lengths.pop() + validate(_content_length_re, value, "bad Content-Length") + if len(value) > CONTENT_LENGTH_MAX_DIGITS: + raise LocalProtocolError("bad Content-Length") + if seen_content_length is None: + seen_content_length = value + new_headers.append((raw_name, name, value)) + elif seen_content_length != value: + raise LocalProtocolError("conflicting Content-Length headers") + elif name == b"transfer-encoding": + # "A server that receives a request message with a transfer coding + # it does not understand SHOULD respond with 501 (Not + # Implemented)." + # https://tools.ietf.org/html/rfc7230#section-3.3.1 + if saw_transfer_encoding: + raise LocalProtocolError( + "multiple Transfer-Encoding headers", error_status_hint=501 + ) + # "All transfer-coding names are case-insensitive" + # -- https://tools.ietf.org/html/rfc7230#section-4 + value = value.lower() + if value != b"chunked": + raise LocalProtocolError( + "Only Transfer-Encoding: chunked is supported", + error_status_hint=501, + ) + saw_transfer_encoding = True + new_headers.append((raw_name, name, value)) + else: + new_headers.append((raw_name, name, value)) + return Headers(new_headers) + + +def get_comma_header(headers: Headers, name: bytes) -> List[bytes]: + # Should only be used for headers whose value is a list of + # comma-separated, case-insensitive values. + # + # The header name `name` is expected to be lower-case bytes. + # + # Connection: meets these criteria (including cast insensitivity). + # + # Content-Length: technically is just a single value (1*DIGIT), but the + # standard makes reference to implementations that do multiple values, and + # using this doesn't hurt. Ditto, case insensitivity doesn't things either + # way. + # + # Transfer-Encoding: is more complex (allows for quoted strings), so + # splitting on , is actually wrong. For example, this is legal: + # + # Transfer-Encoding: foo; options="1,2", chunked + # + # and should be parsed as + # + # foo; options="1,2" + # chunked + # + # but this naive function will parse it as + # + # foo; options="1 + # 2" + # chunked + # + # However, this is okay because the only thing we are going to do with + # any Transfer-Encoding is reject ones that aren't just "chunked", so + # both of these will be treated the same anyway. + # + # Expect: the only legal value is the literal string + # "100-continue". Splitting on commas is harmless. Case insensitive. + # + out: List[bytes] = [] + for _, found_name, found_raw_value in headers._full_items: + if found_name == name: + found_raw_value = found_raw_value.lower() + for found_split_value in found_raw_value.split(b","): + found_split_value = found_split_value.strip() + if found_split_value: + out.append(found_split_value) + return out + + +def set_comma_header(headers: Headers, name: bytes, new_values: List[bytes]) -> Headers: + # The header name `name` is expected to be lower-case bytes. + # + # Note that when we store the header we use title casing for the header + # names, in order to match the conventional HTTP header style. + # + # Simply calling `.title()` is a blunt approach, but it's correct + # here given the cases where we're using `set_comma_header`... + # + # Connection, Content-Length, Transfer-Encoding. + new_headers: List[Tuple[bytes, bytes]] = [] + for found_raw_name, found_name, found_raw_value in headers._full_items: + if found_name != name: + new_headers.append((found_raw_name, found_raw_value)) + for new_value in new_values: + new_headers.append((name.title(), new_value)) + return normalize_and_validate(new_headers) + + +def has_expect_100_continue(request: "Request") -> bool: + # https://tools.ietf.org/html/rfc7231#section-5.1.1 + # "A server that receives a 100-continue expectation in an HTTP/1.0 request + # MUST ignore that expectation." + if request.http_version < b"1.1": + return False + expect = get_comma_header(request.headers, b"expect") + return b"100-continue" in expect diff --git a/venv/lib/python3.12/site-packages/h11/_readers.py b/venv/lib/python3.12/site-packages/h11/_readers.py new file mode 100644 index 0000000..576804c --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11/_readers.py @@ -0,0 +1,250 @@ +# Code to read HTTP data +# +# Strategy: each reader is a callable which takes a ReceiveBuffer object, and +# either: +# 1) consumes some of it and returns an Event +# 2) raises a LocalProtocolError (for consistency -- e.g. we call validate() +# and it might raise a LocalProtocolError, so simpler just to always use +# this) +# 3) returns None, meaning "I need more data" +# +# If they have a .read_eof attribute, then this will be called if an EOF is +# received -- but this is optional. Either way, the actual ConnectionClosed +# event will be generated afterwards. +# +# READERS is a dict describing how to pick a reader. It maps states to either: +# - a reader +# - or, for body readers, a dict of per-framing reader factories + +import re +from typing import Any, Callable, Dict, Iterable, NoReturn, Optional, Tuple, Type, Union + +from ._abnf import chunk_header, header_field, request_line, status_line +from ._events import Data, EndOfMessage, InformationalResponse, Request, Response +from ._receivebuffer import ReceiveBuffer +from ._state import ( + CLIENT, + CLOSED, + DONE, + IDLE, + MUST_CLOSE, + SEND_BODY, + SEND_RESPONSE, + SERVER, +) +from ._util import LocalProtocolError, RemoteProtocolError, Sentinel, validate + +__all__ = ["READERS"] + +header_field_re = re.compile(header_field.encode("ascii")) +obs_fold_re = re.compile(rb"[ \t]+") + + +def _obsolete_line_fold(lines: Iterable[bytes]) -> Iterable[bytes]: + it = iter(lines) + last: Optional[bytes] = None + for line in it: + match = obs_fold_re.match(line) + if match: + if last is None: + raise LocalProtocolError("continuation line at start of headers") + if not isinstance(last, bytearray): + # Cast to a mutable type, avoiding copy on append to ensure O(n) time + last = bytearray(last) + last += b" " + last += line[match.end() :] + else: + if last is not None: + yield last + last = line + if last is not None: + yield last + + +def _decode_header_lines( + lines: Iterable[bytes], +) -> Iterable[Tuple[bytes, bytes]]: + for line in _obsolete_line_fold(lines): + matches = validate(header_field_re, line, "illegal header line: {!r}", line) + yield (matches["field_name"], matches["field_value"]) + + +request_line_re = re.compile(request_line.encode("ascii")) + + +def maybe_read_from_IDLE_client(buf: ReceiveBuffer) -> Optional[Request]: + lines = buf.maybe_extract_lines() + if lines is None: + if buf.is_next_line_obviously_invalid_request_line(): + raise LocalProtocolError("illegal request line") + return None + if not lines: + raise LocalProtocolError("no request line received") + matches = validate( + request_line_re, lines[0], "illegal request line: {!r}", lines[0] + ) + return Request( + headers=list(_decode_header_lines(lines[1:])), _parsed=True, **matches + ) + + +status_line_re = re.compile(status_line.encode("ascii")) + + +def maybe_read_from_SEND_RESPONSE_server( + buf: ReceiveBuffer, +) -> Union[InformationalResponse, Response, None]: + lines = buf.maybe_extract_lines() + if lines is None: + if buf.is_next_line_obviously_invalid_request_line(): + raise LocalProtocolError("illegal request line") + return None + if not lines: + raise LocalProtocolError("no response line received") + matches = validate(status_line_re, lines[0], "illegal status line: {!r}", lines[0]) + http_version = ( + b"1.1" if matches["http_version"] is None else matches["http_version"] + ) + reason = b"" if matches["reason"] is None else matches["reason"] + status_code = int(matches["status_code"]) + class_: Union[Type[InformationalResponse], Type[Response]] = ( + InformationalResponse if status_code < 200 else Response + ) + return class_( + headers=list(_decode_header_lines(lines[1:])), + _parsed=True, + status_code=status_code, + reason=reason, + http_version=http_version, + ) + + +class ContentLengthReader: + def __init__(self, length: int) -> None: + self._length = length + self._remaining = length + + def __call__(self, buf: ReceiveBuffer) -> Union[Data, EndOfMessage, None]: + if self._remaining == 0: + return EndOfMessage() + data = buf.maybe_extract_at_most(self._remaining) + if data is None: + return None + self._remaining -= len(data) + return Data(data=data) + + def read_eof(self) -> NoReturn: + raise RemoteProtocolError( + "peer closed connection without sending complete message body " + "(received {} bytes, expected {})".format( + self._length - self._remaining, self._length + ) + ) + + +chunk_header_re = re.compile(chunk_header.encode("ascii")) + + +class ChunkedReader: + def __init__(self) -> None: + self._bytes_in_chunk = 0 + # After reading a chunk, we have to throw away the trailing \r\n. + # This tracks the bytes that we need to match and throw away. + self._bytes_to_discard = b"" + self._reading_trailer = False + + def __call__(self, buf: ReceiveBuffer) -> Union[Data, EndOfMessage, None]: + if self._reading_trailer: + lines = buf.maybe_extract_lines() + if lines is None: + return None + return EndOfMessage(headers=list(_decode_header_lines(lines))) + if self._bytes_to_discard: + data = buf.maybe_extract_at_most(len(self._bytes_to_discard)) + if data is None: + return None + if data != self._bytes_to_discard[: len(data)]: + raise LocalProtocolError( + f"malformed chunk footer: {data!r} (expected {self._bytes_to_discard!r})" + ) + self._bytes_to_discard = self._bytes_to_discard[len(data) :] + if self._bytes_to_discard: + return None + # else, fall through and read some more + assert self._bytes_to_discard == b"" + if self._bytes_in_chunk == 0: + # We need to refill our chunk count + chunk_header = buf.maybe_extract_next_line() + if chunk_header is None: + return None + matches = validate( + chunk_header_re, + chunk_header, + "illegal chunk header: {!r}", + chunk_header, + ) + # XX FIXME: we discard chunk extensions. Does anyone care? + self._bytes_in_chunk = int(matches["chunk_size"], base=16) + if self._bytes_in_chunk == 0: + self._reading_trailer = True + return self(buf) + chunk_start = True + else: + chunk_start = False + assert self._bytes_in_chunk > 0 + data = buf.maybe_extract_at_most(self._bytes_in_chunk) + if data is None: + return None + self._bytes_in_chunk -= len(data) + if self._bytes_in_chunk == 0: + self._bytes_to_discard = b"\r\n" + chunk_end = True + else: + chunk_end = False + return Data(data=data, chunk_start=chunk_start, chunk_end=chunk_end) + + def read_eof(self) -> NoReturn: + raise RemoteProtocolError( + "peer closed connection without sending complete message body " + "(incomplete chunked read)" + ) + + +class Http10Reader: + def __call__(self, buf: ReceiveBuffer) -> Optional[Data]: + data = buf.maybe_extract_at_most(999999999) + if data is None: + return None + return Data(data=data) + + def read_eof(self) -> EndOfMessage: + return EndOfMessage() + + +def expect_nothing(buf: ReceiveBuffer) -> None: + if buf: + raise LocalProtocolError("Got data when expecting EOF") + return None + + +ReadersType = Dict[ + Union[Type[Sentinel], Tuple[Type[Sentinel], Type[Sentinel]]], + Union[Callable[..., Any], Dict[str, Callable[..., Any]]], +] + +READERS: ReadersType = { + (CLIENT, IDLE): maybe_read_from_IDLE_client, + (SERVER, IDLE): maybe_read_from_SEND_RESPONSE_server, + (SERVER, SEND_RESPONSE): maybe_read_from_SEND_RESPONSE_server, + (CLIENT, DONE): expect_nothing, + (CLIENT, MUST_CLOSE): expect_nothing, + (CLIENT, CLOSED): expect_nothing, + (SERVER, DONE): expect_nothing, + (SERVER, MUST_CLOSE): expect_nothing, + (SERVER, CLOSED): expect_nothing, + SEND_BODY: { + "chunked": ChunkedReader, + "content-length": ContentLengthReader, + "http/1.0": Http10Reader, + }, +} diff --git a/venv/lib/python3.12/site-packages/h11/_receivebuffer.py b/venv/lib/python3.12/site-packages/h11/_receivebuffer.py new file mode 100644 index 0000000..e5c4e08 --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11/_receivebuffer.py @@ -0,0 +1,153 @@ +import re +import sys +from typing import List, Optional, Union + +__all__ = ["ReceiveBuffer"] + + +# Operations we want to support: +# - find next \r\n or \r\n\r\n (\n or \n\n are also acceptable), +# or wait until there is one +# - read at-most-N bytes +# Goals: +# - on average, do this fast +# - worst case, do this in O(n) where n is the number of bytes processed +# Plan: +# - store bytearray, offset, how far we've searched for a separator token +# - use the how-far-we've-searched data to avoid rescanning +# - while doing a stream of uninterrupted processing, advance offset instead +# of constantly copying +# WARNING: +# - I haven't benchmarked or profiled any of this yet. +# +# Note that starting in Python 3.4, deleting the initial n bytes from a +# bytearray is amortized O(n), thanks to some excellent work by Antoine +# Martin: +# +# https://bugs.python.org/issue19087 +# +# This means that if we only supported 3.4+, we could get rid of the code here +# involving self._start and self.compress, because it's doing exactly the same +# thing that bytearray now does internally. +# +# BUT unfortunately, we still support 2.7, and reading short segments out of a +# long buffer MUST be O(bytes read) to avoid DoS issues, so we can't actually +# delete this code. Yet: +# +# https://pythonclock.org/ +# +# (Two things to double-check first though: make sure PyPy also has the +# optimization, and benchmark to make sure it's a win, since we do have a +# slightly clever thing where we delay calling compress() until we've +# processed a whole event, which could in theory be slightly more efficient +# than the internal bytearray support.) +blank_line_regex = re.compile(b"\n\r?\n", re.MULTILINE) + + +class ReceiveBuffer: + def __init__(self) -> None: + self._data = bytearray() + self._next_line_search = 0 + self._multiple_lines_search = 0 + + def __iadd__(self, byteslike: Union[bytes, bytearray]) -> "ReceiveBuffer": + self._data += byteslike + return self + + def __bool__(self) -> bool: + return bool(len(self)) + + def __len__(self) -> int: + return len(self._data) + + # for @property unprocessed_data + def __bytes__(self) -> bytes: + return bytes(self._data) + + def _extract(self, count: int) -> bytearray: + # extracting an initial slice of the data buffer and return it + out = self._data[:count] + del self._data[:count] + + self._next_line_search = 0 + self._multiple_lines_search = 0 + + return out + + def maybe_extract_at_most(self, count: int) -> Optional[bytearray]: + """ + Extract a fixed number of bytes from the buffer. + """ + out = self._data[:count] + if not out: + return None + + return self._extract(count) + + def maybe_extract_next_line(self) -> Optional[bytearray]: + """ + Extract the first line, if it is completed in the buffer. + """ + # Only search in buffer space that we've not already looked at. + search_start_index = max(0, self._next_line_search - 1) + partial_idx = self._data.find(b"\r\n", search_start_index) + + if partial_idx == -1: + self._next_line_search = len(self._data) + return None + + # + 2 is to compensate len(b"\r\n") + idx = partial_idx + 2 + + return self._extract(idx) + + def maybe_extract_lines(self) -> Optional[List[bytearray]]: + """ + Extract everything up to the first blank line, and return a list of lines. + """ + # Handle the case where we have an immediate empty line. + if self._data[:1] == b"\n": + self._extract(1) + return [] + + if self._data[:2] == b"\r\n": + self._extract(2) + return [] + + # Only search in buffer space that we've not already looked at. + match = blank_line_regex.search(self._data, self._multiple_lines_search) + if match is None: + self._multiple_lines_search = max(0, len(self._data) - 2) + return None + + # Truncate the buffer and return it. + idx = match.span(0)[-1] + out = self._extract(idx) + lines = out.split(b"\n") + + for line in lines: + if line.endswith(b"\r"): + del line[-1] + + assert lines[-2] == lines[-1] == b"" + + del lines[-2:] + + return lines + + # In theory we should wait until `\r\n` before starting to validate + # incoming data. However it's interesting to detect (very) invalid data + # early given they might not even contain `\r\n` at all (hence only + # timeout will get rid of them). + # This is not a 100% effective detection but more of a cheap sanity check + # allowing for early abort in some useful cases. + # This is especially interesting when peer is messing up with HTTPS and + # sent us a TLS stream where we were expecting plain HTTP given all + # versions of TLS so far start handshake with a 0x16 message type code. + def is_next_line_obviously_invalid_request_line(self) -> bool: + try: + # HTTP header line must not contain non-printable characters + # and should not start with a space + return self._data[0] < 0x21 + except IndexError: + return False diff --git a/venv/lib/python3.12/site-packages/h11/_state.py b/venv/lib/python3.12/site-packages/h11/_state.py new file mode 100644 index 0000000..3ad444b --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11/_state.py @@ -0,0 +1,365 @@ +################################################################ +# The core state machine +################################################################ +# +# Rule 1: everything that affects the state machine and state transitions must +# live here in this file. As much as possible goes into the table-based +# representation, but for the bits that don't quite fit, the actual code and +# state must nonetheless live here. +# +# Rule 2: this file does not know about what role we're playing; it only knows +# about HTTP request/response cycles in the abstract. This ensures that we +# don't cheat and apply different rules to local and remote parties. +# +# +# Theory of operation +# =================== +# +# Possibly the simplest way to think about this is that we actually have 5 +# different state machines here. Yes, 5. These are: +# +# 1) The client state, with its complicated automaton (see the docs) +# 2) The server state, with its complicated automaton (see the docs) +# 3) The keep-alive state, with possible states {True, False} +# 4) The SWITCH_CONNECT state, with possible states {False, True} +# 5) The SWITCH_UPGRADE state, with possible states {False, True} +# +# For (3)-(5), the first state listed is the initial state. +# +# (1)-(3) are stored explicitly in member variables. The last +# two are stored implicitly in the pending_switch_proposals set as: +# (state of 4) == (_SWITCH_CONNECT in pending_switch_proposals) +# (state of 5) == (_SWITCH_UPGRADE in pending_switch_proposals) +# +# And each of these machines has two different kinds of transitions: +# +# a) Event-triggered +# b) State-triggered +# +# Event triggered is the obvious thing that you'd think it is: some event +# happens, and if it's the right event at the right time then a transition +# happens. But there are somewhat complicated rules for which machines can +# "see" which events. (As a rule of thumb, if a machine "sees" an event, this +# means two things: the event can affect the machine, and if the machine is +# not in a state where it expects that event then it's an error.) These rules +# are: +# +# 1) The client machine sees all h11.events objects emitted by the client. +# +# 2) The server machine sees all h11.events objects emitted by the server. +# +# It also sees the client's Request event. +# +# And sometimes, server events are annotated with a _SWITCH_* event. For +# example, we can have a (Response, _SWITCH_CONNECT) event, which is +# different from a regular Response event. +# +# 3) The keep-alive machine sees the process_keep_alive_disabled() event +# (which is derived from Request/Response events), and this event +# transitions it from True -> False, or from False -> False. There's no way +# to transition back. +# +# 4&5) The _SWITCH_* machines transition from False->True when we get a +# Request that proposes the relevant type of switch (via +# process_client_switch_proposals), and they go from True->False when we +# get a Response that has no _SWITCH_* annotation. +# +# So that's event-triggered transitions. +# +# State-triggered transitions are less standard. What they do here is couple +# the machines together. The way this works is, when certain *joint* +# configurations of states are achieved, then we automatically transition to a +# new *joint* state. So, for example, if we're ever in a joint state with +# +# client: DONE +# keep-alive: False +# +# then the client state immediately transitions to: +# +# client: MUST_CLOSE +# +# This is fundamentally different from an event-based transition, because it +# doesn't matter how we arrived at the {client: DONE, keep-alive: False} state +# -- maybe the client transitioned SEND_BODY -> DONE, or keep-alive +# transitioned True -> False. Either way, once this precondition is satisfied, +# this transition is immediately triggered. +# +# What if two conflicting state-based transitions get enabled at the same +# time? In practice there's only one case where this arises (client DONE -> +# MIGHT_SWITCH_PROTOCOL versus DONE -> MUST_CLOSE), and we resolve it by +# explicitly prioritizing the DONE -> MIGHT_SWITCH_PROTOCOL transition. +# +# Implementation +# -------------- +# +# The event-triggered transitions for the server and client machines are all +# stored explicitly in a table. Ditto for the state-triggered transitions that +# involve just the server and client state. +# +# The transitions for the other machines, and the state-triggered transitions +# that involve the other machines, are written out as explicit Python code. +# +# It'd be nice if there were some cleaner way to do all this. This isn't +# *too* terrible, but I feel like it could probably be better. +# +# WARNING +# ------- +# +# The script that generates the state machine diagrams for the docs knows how +# to read out the EVENT_TRIGGERED_TRANSITIONS and STATE_TRIGGERED_TRANSITIONS +# tables. But it can't automatically read the transitions that are written +# directly in Python code. So if you touch those, you need to also update the +# script to keep it in sync! +from typing import cast, Dict, Optional, Set, Tuple, Type, Union + +from ._events import * +from ._util import LocalProtocolError, Sentinel + +# Everything in __all__ gets re-exported as part of the h11 public API. +__all__ = [ + "CLIENT", + "SERVER", + "IDLE", + "SEND_RESPONSE", + "SEND_BODY", + "DONE", + "MUST_CLOSE", + "CLOSED", + "MIGHT_SWITCH_PROTOCOL", + "SWITCHED_PROTOCOL", + "ERROR", +] + + +class CLIENT(Sentinel, metaclass=Sentinel): + pass + + +class SERVER(Sentinel, metaclass=Sentinel): + pass + + +# States +class IDLE(Sentinel, metaclass=Sentinel): + pass + + +class SEND_RESPONSE(Sentinel, metaclass=Sentinel): + pass + + +class SEND_BODY(Sentinel, metaclass=Sentinel): + pass + + +class DONE(Sentinel, metaclass=Sentinel): + pass + + +class MUST_CLOSE(Sentinel, metaclass=Sentinel): + pass + + +class CLOSED(Sentinel, metaclass=Sentinel): + pass + + +class ERROR(Sentinel, metaclass=Sentinel): + pass + + +# Switch types +class MIGHT_SWITCH_PROTOCOL(Sentinel, metaclass=Sentinel): + pass + + +class SWITCHED_PROTOCOL(Sentinel, metaclass=Sentinel): + pass + + +class _SWITCH_UPGRADE(Sentinel, metaclass=Sentinel): + pass + + +class _SWITCH_CONNECT(Sentinel, metaclass=Sentinel): + pass + + +EventTransitionType = Dict[ + Type[Sentinel], + Dict[ + Type[Sentinel], + Dict[Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]], Type[Sentinel]], + ], +] + +EVENT_TRIGGERED_TRANSITIONS: EventTransitionType = { + CLIENT: { + IDLE: {Request: SEND_BODY, ConnectionClosed: CLOSED}, + SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE}, + DONE: {ConnectionClosed: CLOSED}, + MUST_CLOSE: {ConnectionClosed: CLOSED}, + CLOSED: {ConnectionClosed: CLOSED}, + MIGHT_SWITCH_PROTOCOL: {}, + SWITCHED_PROTOCOL: {}, + ERROR: {}, + }, + SERVER: { + IDLE: { + ConnectionClosed: CLOSED, + Response: SEND_BODY, + # Special case: server sees client Request events, in this form + (Request, CLIENT): SEND_RESPONSE, + }, + SEND_RESPONSE: { + InformationalResponse: SEND_RESPONSE, + Response: SEND_BODY, + (InformationalResponse, _SWITCH_UPGRADE): SWITCHED_PROTOCOL, + (Response, _SWITCH_CONNECT): SWITCHED_PROTOCOL, + }, + SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE}, + DONE: {ConnectionClosed: CLOSED}, + MUST_CLOSE: {ConnectionClosed: CLOSED}, + CLOSED: {ConnectionClosed: CLOSED}, + SWITCHED_PROTOCOL: {}, + ERROR: {}, + }, +} + +StateTransitionType = Dict[ + Tuple[Type[Sentinel], Type[Sentinel]], Dict[Type[Sentinel], Type[Sentinel]] +] + +# NB: there are also some special-case state-triggered transitions hard-coded +# into _fire_state_triggered_transitions below. +STATE_TRIGGERED_TRANSITIONS: StateTransitionType = { + # (Client state, Server state) -> new states + # Protocol negotiation + (MIGHT_SWITCH_PROTOCOL, SWITCHED_PROTOCOL): {CLIENT: SWITCHED_PROTOCOL}, + # Socket shutdown + (CLOSED, DONE): {SERVER: MUST_CLOSE}, + (CLOSED, IDLE): {SERVER: MUST_CLOSE}, + (ERROR, DONE): {SERVER: MUST_CLOSE}, + (DONE, CLOSED): {CLIENT: MUST_CLOSE}, + (IDLE, CLOSED): {CLIENT: MUST_CLOSE}, + (DONE, ERROR): {CLIENT: MUST_CLOSE}, +} + + +class ConnectionState: + def __init__(self) -> None: + # Extra bits of state that don't quite fit into the state model. + + # If this is False then it enables the automatic DONE -> MUST_CLOSE + # transition. Don't set this directly; call .keep_alive_disabled() + self.keep_alive = True + + # This is a subset of {UPGRADE, CONNECT}, containing the proposals + # made by the client for switching protocols. + self.pending_switch_proposals: Set[Type[Sentinel]] = set() + + self.states: Dict[Type[Sentinel], Type[Sentinel]] = {CLIENT: IDLE, SERVER: IDLE} + + def process_error(self, role: Type[Sentinel]) -> None: + self.states[role] = ERROR + self._fire_state_triggered_transitions() + + def process_keep_alive_disabled(self) -> None: + self.keep_alive = False + self._fire_state_triggered_transitions() + + def process_client_switch_proposal(self, switch_event: Type[Sentinel]) -> None: + self.pending_switch_proposals.add(switch_event) + self._fire_state_triggered_transitions() + + def process_event( + self, + role: Type[Sentinel], + event_type: Type[Event], + server_switch_event: Optional[Type[Sentinel]] = None, + ) -> None: + _event_type: Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]] = event_type + if server_switch_event is not None: + assert role is SERVER + if server_switch_event not in self.pending_switch_proposals: + raise LocalProtocolError( + "Received server _SWITCH_UPGRADE event without a pending proposal" + ) + _event_type = (event_type, server_switch_event) + if server_switch_event is None and _event_type is Response: + self.pending_switch_proposals = set() + self._fire_event_triggered_transitions(role, _event_type) + # Special case: the server state does get to see Request + # events. + if _event_type is Request: + assert role is CLIENT + self._fire_event_triggered_transitions(SERVER, (Request, CLIENT)) + self._fire_state_triggered_transitions() + + def _fire_event_triggered_transitions( + self, + role: Type[Sentinel], + event_type: Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]], + ) -> None: + state = self.states[role] + try: + new_state = EVENT_TRIGGERED_TRANSITIONS[role][state][event_type] + except KeyError: + event_type = cast(Type[Event], event_type) + raise LocalProtocolError( + "can't handle event type {} when role={} and state={}".format( + event_type.__name__, role, self.states[role] + ) + ) from None + self.states[role] = new_state + + def _fire_state_triggered_transitions(self) -> None: + # We apply these rules repeatedly until converging on a fixed point + while True: + start_states = dict(self.states) + + # It could happen that both these special-case transitions are + # enabled at the same time: + # + # DONE -> MIGHT_SWITCH_PROTOCOL + # DONE -> MUST_CLOSE + # + # For example, this will always be true of a HTTP/1.0 client + # requesting CONNECT. If this happens, the protocol switch takes + # priority. From there the client will either go to + # SWITCHED_PROTOCOL, in which case it's none of our business when + # they close the connection, or else the server will deny the + # request, in which case the client will go back to DONE and then + # from there to MUST_CLOSE. + if self.pending_switch_proposals: + if self.states[CLIENT] is DONE: + self.states[CLIENT] = MIGHT_SWITCH_PROTOCOL + + if not self.pending_switch_proposals: + if self.states[CLIENT] is MIGHT_SWITCH_PROTOCOL: + self.states[CLIENT] = DONE + + if not self.keep_alive: + for role in (CLIENT, SERVER): + if self.states[role] is DONE: + self.states[role] = MUST_CLOSE + + # Tabular state-triggered transitions + joint_state = (self.states[CLIENT], self.states[SERVER]) + changes = STATE_TRIGGERED_TRANSITIONS.get(joint_state, {}) + self.states.update(changes) + + if self.states == start_states: + # Fixed point reached + return + + def start_next_cycle(self) -> None: + if self.states != {CLIENT: DONE, SERVER: DONE}: + raise LocalProtocolError( + f"not in a reusable state. self.states={self.states}" + ) + # Can't reach DONE/DONE with any of these active, but still, let's be + # sure. + assert self.keep_alive + assert not self.pending_switch_proposals + self.states = {CLIENT: IDLE, SERVER: IDLE} diff --git a/venv/lib/python3.12/site-packages/h11/_util.py b/venv/lib/python3.12/site-packages/h11/_util.py new file mode 100644 index 0000000..6718445 --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11/_util.py @@ -0,0 +1,135 @@ +from typing import Any, Dict, NoReturn, Pattern, Tuple, Type, TypeVar, Union + +__all__ = [ + "ProtocolError", + "LocalProtocolError", + "RemoteProtocolError", + "validate", + "bytesify", +] + + +class ProtocolError(Exception): + """Exception indicating a violation of the HTTP/1.1 protocol. + + This as an abstract base class, with two concrete base classes: + :exc:`LocalProtocolError`, which indicates that you tried to do something + that HTTP/1.1 says is illegal, and :exc:`RemoteProtocolError`, which + indicates that the remote peer tried to do something that HTTP/1.1 says is + illegal. See :ref:`error-handling` for details. + + In addition to the normal :exc:`Exception` features, it has one attribute: + + .. attribute:: error_status_hint + + This gives a suggestion as to what status code a server might use if + this error occurred as part of a request. + + For a :exc:`RemoteProtocolError`, this is useful as a suggestion for + how you might want to respond to a misbehaving peer, if you're + implementing a server. + + For a :exc:`LocalProtocolError`, this can be taken as a suggestion for + how your peer might have responded to *you* if h11 had allowed you to + continue. + + The default is 400 Bad Request, a generic catch-all for protocol + violations. + + """ + + def __init__(self, msg: str, error_status_hint: int = 400) -> None: + if type(self) is ProtocolError: + raise TypeError("tried to directly instantiate ProtocolError") + Exception.__init__(self, msg) + self.error_status_hint = error_status_hint + + +# Strategy: there are a number of public APIs where a LocalProtocolError can +# be raised (send(), all the different event constructors, ...), and only one +# public API where RemoteProtocolError can be raised +# (receive_data()). Therefore we always raise LocalProtocolError internally, +# and then receive_data will translate this into a RemoteProtocolError. +# +# Internally: +# LocalProtocolError is the generic "ProtocolError". +# Externally: +# LocalProtocolError is for local errors and RemoteProtocolError is for +# remote errors. +class LocalProtocolError(ProtocolError): + def _reraise_as_remote_protocol_error(self) -> NoReturn: + # After catching a LocalProtocolError, use this method to re-raise it + # as a RemoteProtocolError. This method must be called from inside an + # except: block. + # + # An easy way to get an equivalent RemoteProtocolError is just to + # modify 'self' in place. + self.__class__ = RemoteProtocolError # type: ignore + # But the re-raising is somewhat non-trivial -- you might think that + # now that we've modified the in-flight exception object, that just + # doing 'raise' to re-raise it would be enough. But it turns out that + # this doesn't work, because Python tracks the exception type + # (exc_info[0]) separately from the exception object (exc_info[1]), + # and we only modified the latter. So we really do need to re-raise + # the new type explicitly. + # On py3, the traceback is part of the exception object, so our + # in-place modification preserved it and we can just re-raise: + raise self + + +class RemoteProtocolError(ProtocolError): + pass + + +def validate( + regex: Pattern[bytes], data: bytes, msg: str = "malformed data", *format_args: Any +) -> Dict[str, bytes]: + match = regex.fullmatch(data) + if not match: + if format_args: + msg = msg.format(*format_args) + raise LocalProtocolError(msg) + return match.groupdict() + + +# Sentinel values +# +# - Inherit identity-based comparison and hashing from object +# - Have a nice repr +# - Have a *bonus property*: type(sentinel) is sentinel +# +# The bonus property is useful if you want to take the return value from +# next_event() and do some sort of dispatch based on type(event). + +_T_Sentinel = TypeVar("_T_Sentinel", bound="Sentinel") + + +class Sentinel(type): + def __new__( + cls: Type[_T_Sentinel], + name: str, + bases: Tuple[type, ...], + namespace: Dict[str, Any], + **kwds: Any + ) -> _T_Sentinel: + assert bases == (Sentinel,) + v = super().__new__(cls, name, bases, namespace, **kwds) + v.__class__ = v # type: ignore + return v + + def __repr__(self) -> str: + return self.__name__ + + +# Used for methods, request targets, HTTP versions, header names, and header +# values. Accepts ascii-strings, or bytes/bytearray/memoryview/..., and always +# returns bytes. +def bytesify(s: Union[bytes, bytearray, memoryview, int, str]) -> bytes: + # Fast-path: + if type(s) is bytes: + return s + if isinstance(s, str): + s = s.encode("ascii") + if isinstance(s, int): + raise TypeError("expected bytes-like object, not int") + return bytes(s) diff --git a/venv/lib/python3.12/site-packages/h11/_version.py b/venv/lib/python3.12/site-packages/h11/_version.py new file mode 100644 index 0000000..76e7327 --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11/_version.py @@ -0,0 +1,16 @@ +# This file must be kept very simple, because it is consumed from several +# places -- it is imported by h11/__init__.py, execfile'd by setup.py, etc. + +# We use a simple scheme: +# 1.0.0 -> 1.0.0+dev -> 1.1.0 -> 1.1.0+dev +# where the +dev versions are never released into the wild, they're just what +# we stick into the VCS in between releases. +# +# This is compatible with PEP 440: +# http://legacy.python.org/dev/peps/pep-0440/ +# via the use of the "local suffix" "+dev", which is disallowed on index +# servers and causes 1.0.0+dev to sort after plain 1.0.0, which is what we +# want. (Contrast with the special suffix 1.0.0.dev, which sorts *before* +# 1.0.0.) + +__version__ = "0.16.0" diff --git a/venv/lib/python3.12/site-packages/h11/_writers.py b/venv/lib/python3.12/site-packages/h11/_writers.py new file mode 100644 index 0000000..939cdb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11/_writers.py @@ -0,0 +1,145 @@ +# Code to read HTTP data +# +# Strategy: each writer takes an event + a write-some-bytes function, which is +# calls. +# +# WRITERS is a dict describing how to pick a reader. It maps states to either: +# - a writer +# - or, for body writers, a dict of framin-dependent writer factories + +from typing import Any, Callable, Dict, List, Tuple, Type, Union + +from ._events import Data, EndOfMessage, Event, InformationalResponse, Request, Response +from ._headers import Headers +from ._state import CLIENT, IDLE, SEND_BODY, SEND_RESPONSE, SERVER +from ._util import LocalProtocolError, Sentinel + +__all__ = ["WRITERS"] + +Writer = Callable[[bytes], Any] + + +def write_headers(headers: Headers, write: Writer) -> None: + # "Since the Host field-value is critical information for handling a + # request, a user agent SHOULD generate Host as the first header field + # following the request-line." - RFC 7230 + raw_items = headers._full_items + for raw_name, name, value in raw_items: + if name == b"host": + write(b"%s: %s\r\n" % (raw_name, value)) + for raw_name, name, value in raw_items: + if name != b"host": + write(b"%s: %s\r\n" % (raw_name, value)) + write(b"\r\n") + + +def write_request(request: Request, write: Writer) -> None: + if request.http_version != b"1.1": + raise LocalProtocolError("I only send HTTP/1.1") + write(b"%s %s HTTP/1.1\r\n" % (request.method, request.target)) + write_headers(request.headers, write) + + +# Shared between InformationalResponse and Response +def write_any_response( + response: Union[InformationalResponse, Response], write: Writer +) -> None: + if response.http_version != b"1.1": + raise LocalProtocolError("I only send HTTP/1.1") + status_bytes = str(response.status_code).encode("ascii") + # We don't bother sending ascii status messages like "OK"; they're + # optional and ignored by the protocol. (But the space after the numeric + # status code is mandatory.) + # + # XX FIXME: could at least make an effort to pull out the status message + # from stdlib's http.HTTPStatus table. Or maybe just steal their enums + # (either by import or copy/paste). We already accept them as status codes + # since they're of type IntEnum < int. + write(b"HTTP/1.1 %s %s\r\n" % (status_bytes, response.reason)) + write_headers(response.headers, write) + + +class BodyWriter: + def __call__(self, event: Event, write: Writer) -> None: + if type(event) is Data: + self.send_data(event.data, write) + elif type(event) is EndOfMessage: + self.send_eom(event.headers, write) + else: # pragma: no cover + assert False + + def send_data(self, data: bytes, write: Writer) -> None: + pass + + def send_eom(self, headers: Headers, write: Writer) -> None: + pass + + +# +# These are all careful not to do anything to 'data' except call len(data) and +# write(data). This allows us to transparently pass-through funny objects, +# like placeholder objects referring to files on disk that will be sent via +# sendfile(2). +# +class ContentLengthWriter(BodyWriter): + def __init__(self, length: int) -> None: + self._length = length + + def send_data(self, data: bytes, write: Writer) -> None: + self._length -= len(data) + if self._length < 0: + raise LocalProtocolError("Too much data for declared Content-Length") + write(data) + + def send_eom(self, headers: Headers, write: Writer) -> None: + if self._length != 0: + raise LocalProtocolError("Too little data for declared Content-Length") + if headers: + raise LocalProtocolError("Content-Length and trailers don't mix") + + +class ChunkedWriter(BodyWriter): + def send_data(self, data: bytes, write: Writer) -> None: + # if we encoded 0-length data in the naive way, it would look like an + # end-of-message. + if not data: + return + write(b"%x\r\n" % len(data)) + write(data) + write(b"\r\n") + + def send_eom(self, headers: Headers, write: Writer) -> None: + write(b"0\r\n") + write_headers(headers, write) + + +class Http10Writer(BodyWriter): + def send_data(self, data: bytes, write: Writer) -> None: + write(data) + + def send_eom(self, headers: Headers, write: Writer) -> None: + if headers: + raise LocalProtocolError("can't send trailers to HTTP/1.0 client") + # no need to close the socket ourselves, that will be taken care of by + # Connection: close machinery + + +WritersType = Dict[ + Union[Tuple[Type[Sentinel], Type[Sentinel]], Type[Sentinel]], + Union[ + Dict[str, Type[BodyWriter]], + Callable[[Union[InformationalResponse, Response], Writer], None], + Callable[[Request, Writer], None], + ], +] + +WRITERS: WritersType = { + (CLIENT, IDLE): write_request, + (SERVER, IDLE): write_any_response, + (SERVER, SEND_RESPONSE): write_any_response, + SEND_BODY: { + "chunked": ChunkedWriter, + "content-length": ContentLengthWriter, + "http/1.0": Http10Writer, + }, +} diff --git a/venv/lib/python3.12/site-packages/h11/py.typed b/venv/lib/python3.12/site-packages/h11/py.typed new file mode 100644 index 0000000..f5642f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/h11/py.typed @@ -0,0 +1 @@ +Marker diff --git a/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/METADATA b/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/METADATA new file mode 100644 index 0000000..8056834 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/METADATA @@ -0,0 +1,625 @@ +Metadata-Version: 2.4 +Name: httpcore +Version: 1.0.9 +Summary: A minimal low-level HTTP client. +Project-URL: Documentation, https://www.encode.io/httpcore +Project-URL: Homepage, https://www.encode.io/httpcore/ +Project-URL: Source, https://github.com/encode/httpcore +Author-email: Tom Christie +License-Expression: BSD-3-Clause +License-File: LICENSE.md +Classifier: Development Status :: 3 - Alpha +Classifier: Environment :: Web Environment +Classifier: Framework :: AsyncIO +Classifier: Framework :: Trio +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.8 +Requires-Dist: certifi +Requires-Dist: h11>=0.16 +Provides-Extra: asyncio +Requires-Dist: anyio<5.0,>=4.0; extra == 'asyncio' +Provides-Extra: http2 +Requires-Dist: h2<5,>=3; extra == 'http2' +Provides-Extra: socks +Requires-Dist: socksio==1.*; extra == 'socks' +Provides-Extra: trio +Requires-Dist: trio<1.0,>=0.22.0; extra == 'trio' +Description-Content-Type: text/markdown + +# HTTP Core + +[![Test Suite](https://github.com/encode/httpcore/workflows/Test%20Suite/badge.svg)](https://github.com/encode/httpcore/actions) +[![Package version](https://badge.fury.io/py/httpcore.svg)](https://pypi.org/project/httpcore/) + +> *Do one thing, and do it well.* + +The HTTP Core package provides a minimal low-level HTTP client, which does +one thing only. Sending HTTP requests. + +It does not provide any high level model abstractions over the API, +does not handle redirects, multipart uploads, building authentication headers, +transparent HTTP caching, URL parsing, session cookie handling, +content or charset decoding, handling JSON, environment based configuration +defaults, or any of that Jazz. + +Some things HTTP Core does do: + +* Sending HTTP requests. +* Thread-safe / task-safe connection pooling. +* HTTP(S) proxy & SOCKS proxy support. +* Supports HTTP/1.1 and HTTP/2. +* Provides both sync and async interfaces. +* Async backend support for `asyncio` and `trio`. + +## Requirements + +Python 3.8+ + +## Installation + +For HTTP/1.1 only support, install with: + +```shell +$ pip install httpcore +``` + +There are also a number of optional extras available... + +```shell +$ pip install httpcore['asyncio,trio,http2,socks'] +``` + +## Sending requests + +Send an HTTP request: + +```python +import httpcore + +response = httpcore.request("GET", "https://www.example.com/") + +print(response) +# +print(response.status) +# 200 +print(response.headers) +# [(b'Accept-Ranges', b'bytes'), (b'Age', b'557328'), (b'Cache-Control', b'max-age=604800'), ...] +print(response.content) +# b'\n\n\nExample Domain\n\n\n ...' +``` + +The top-level `httpcore.request()` function is provided for convenience. In practice whenever you're working with `httpcore` you'll want to use the connection pooling functionality that it provides. + +```python +import httpcore + +http = httpcore.ConnectionPool() +response = http.request("GET", "https://www.example.com/") +``` + +Once you're ready to get going, [head over to the documentation](https://www.encode.io/httpcore/). + +## Motivation + +You *probably* don't want to be using HTTP Core directly. It might make sense if +you're writing something like a proxy service in Python, and you just want +something at the lowest possible level, but more typically you'll want to use +a higher level client library, such as `httpx`. + +The motivation for `httpcore` is: + +* To provide a reusable low-level client library, that other packages can then build on top of. +* To provide a *really clear interface split* between the networking code and client logic, + so that each is easier to understand and reason about in isolation. + +## Dependencies + +The `httpcore` package has the following dependencies... + +* `h11` +* `certifi` + +And the following optional extras... + +* `anyio` - Required by `pip install httpcore['asyncio']`. +* `trio` - Required by `pip install httpcore['trio']`. +* `h2` - Required by `pip install httpcore['http2']`. +* `socksio` - Required by `pip install httpcore['socks']`. + +## Versioning + +We use [SEMVER for our versioning policy](https://semver.org/). + +For changes between package versions please see our [project changelog](CHANGELOG.md). + +We recommend pinning your requirements either the most current major version, or a more specific version range: + +```python +pip install 'httpcore==1.*' +``` +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## Version 1.0.9 (April 24th, 2025) + +- Resolve https://github.com/advisories/GHSA-vqfr-h8mv-ghfj with h11 dependency update. (#1008) + +## Version 1.0.8 (April 11th, 2025) + +- Fix `AttributeError` when importing on Python 3.14. (#1005) + +## Version 1.0.7 (November 15th, 2024) + +- Support `proxy=…` configuration on `ConnectionPool()`. (#974) + +## Version 1.0.6 (October 1st, 2024) + +- Relax `trio` dependency pinning. (#956) +- Handle `trio` raising `NotImplementedError` on unsupported platforms. (#955) +- Handle mapping `ssl.SSLError` to `httpcore.ConnectError`. (#918) + +## 1.0.5 (March 27th, 2024) + +- Handle `EndOfStream` exception for anyio backend. (#899) +- Allow trio `0.25.*` series in package dependancies. (#903) + +## 1.0.4 (February 21st, 2024) + +- Add `target` request extension. (#888) +- Fix support for connection `Upgrade` and `CONNECT` when some data in the stream has been read. (#882) + +## 1.0.3 (February 13th, 2024) + +- Fix support for async cancellations. (#880) +- Fix trace extension when used with socks proxy. (#849) +- Fix SSL context for connections using the "wss" scheme (#869) + +## 1.0.2 (November 10th, 2023) + +- Fix `float("inf")` timeouts in `Event.wait` function. (#846) + +## 1.0.1 (November 3rd, 2023) + +- Fix pool timeout to account for the total time spent retrying. (#823) +- Raise a neater RuntimeError when the correct async deps are not installed. (#826) +- Add support for synchronous TLS-in-TLS streams. (#840) + +## 1.0.0 (October 6th, 2023) + +From version 1.0 our async support is now optional, as the package has minimal dependencies by default. + +For async support use either `pip install 'httpcore[asyncio]'` or `pip install 'httpcore[trio]'`. + +The project versioning policy is now explicitly governed by SEMVER. See https://semver.org/. + +- Async support becomes fully optional. (#809) +- Add support for Python 3.12. (#807) + +## 0.18.0 (September 8th, 2023) + +- Add support for HTTPS proxies. (#745, #786) +- Drop Python 3.7 support. (#727) +- Handle `sni_hostname` extension with SOCKS proxy. (#774) +- Handle HTTP/1.1 half-closed connections gracefully. (#641) +- Change the type of `Extensions` from `Mapping[Str, Any]` to `MutableMapping[Str, Any]`. (#762) + +## 0.17.3 (July 5th, 2023) + +- Support async cancellations, ensuring that the connection pool is left in a clean state when cancellations occur. (#726) +- The networking backend interface has [been added to the public API](https://www.encode.io/httpcore/network-backends). Some classes which were previously private implementation detail are now part of the top-level public API. (#699) +- Graceful handling of HTTP/2 GoAway frames, with requests being transparently retried on a new connection. (#730) +- Add exceptions when a synchronous `trace callback` is passed to an asynchronous request or an asynchronous `trace callback` is passed to a synchronous request. (#717) +- Drop Python 3.7 support. (#727) + +## 0.17.2 (May 23th, 2023) + +- Add `socket_options` argument to `ConnectionPool` and `HTTProxy` classes. (#668) +- Improve logging with per-module logger names. (#690) +- Add `sni_hostname` request extension. (#696) +- Resolve race condition during import of `anyio` package. (#692) +- Enable TCP_NODELAY for all synchronous sockets. (#651) + +## 0.17.1 (May 17th, 2023) + +- If 'retries' is set, then allow retries if an SSL handshake error occurs. (#669) +- Improve correctness of tracebacks on network exceptions, by raising properly chained exceptions. (#678) +- Prevent connection-hanging behaviour when HTTP/2 connections are closed by a server-sent 'GoAway' frame. (#679) +- Fix edge-case exception when removing requests from the connection pool. (#680) +- Fix pool timeout edge-case. (#688) + +## 0.17.0 (March 16th, 2023) + +- Add DEBUG level logging. (#648) +- Respect HTTP/2 max concurrent streams when settings updates are sent by server. (#652) +- Increase the allowable HTTP header size to 100kB. (#647) +- Add `retries` option to SOCKS proxy classes. (#643) + +## 0.16.3 (December 20th, 2022) + +- Allow `ws` and `wss` schemes. Allows us to properly support websocket upgrade connections. (#625) +- Forwarding HTTP proxies use a connection-per-remote-host. Required by some proxy implementations. (#637) +- Don't raise `RuntimeError` when closing a connection pool with active connections. Removes some error cases when cancellations are used. (#631) +- Lazy import `anyio`, so that it's no longer a hard dependancy, and isn't imported if unused. (#639) + +## 0.16.2 (November 25th, 2022) + +- Revert 'Fix async cancellation behaviour', which introduced race conditions. (#627) +- Raise `RuntimeError` if attempting to us UNIX domain sockets on Windows. (#619) + +## 0.16.1 (November 17th, 2022) + +- Fix HTTP/1.1 interim informational responses, such as "100 Continue". (#605) + +## 0.16.0 (October 11th, 2022) + +- Support HTTP/1.1 informational responses. (#581) +- Fix async cancellation behaviour. (#580) +- Support `h11` 0.14. (#579) + +## 0.15.0 (May 17th, 2022) + +- Drop Python 3.6 support (#535) +- Ensure HTTP proxy CONNECT requests include `timeout` configuration. (#506) +- Switch to explicit `typing.Optional` for type hints. (#513) +- For `trio` map OSError exceptions to `ConnectError`. (#543) + +## 0.14.7 (February 4th, 2022) + +- Requests which raise a PoolTimeout need to be removed from the pool queue. (#502) +- Fix AttributeError that happened when Socks5Connection were terminated. (#501) + +## 0.14.6 (February 1st, 2022) + +- Fix SOCKS support for `http://` URLs. (#492) +- Resolve race condition around exceptions during streaming a response. (#491) + +## 0.14.5 (January 18th, 2022) + +- SOCKS proxy support. (#478) +- Add proxy_auth argument to HTTPProxy. (#481) +- Improve error message on 'RemoteProtocolError' exception when server disconnects without sending a response. (#479) + +## 0.14.4 (January 5th, 2022) + +- Support HTTP/2 on HTTPS tunnelling proxies. (#468) +- Fix proxy headers missing on HTTP forwarding. (#456) +- Only instantiate SSL context if required. (#457) +- More robust HTTP/2 handling. (#253, #439, #440, #441) + +## 0.14.3 (November 17th, 2021) + +- Fix race condition when removing closed connections from the pool. (#437) + +## 0.14.2 (November 16th, 2021) + +- Failed connections no longer remain in the pool. (Pull #433) + +## 0.14.1 (November 12th, 2021) + +- `max_connections` becomes optional. (Pull #429) +- `certifi` is now included in the install dependancies. (Pull #428) +- `h2` is now strictly optional. (Pull #428) + +## 0.14.0 (November 11th, 2021) + +The 0.14 release is a complete reworking of `httpcore`, comprehensively addressing some underlying issues in the connection pooling, as well as substantially redesigning the API to be more user friendly. + +Some of the lower-level API design also makes the components more easily testable in isolation, and the package now has 100% test coverage. + +See [discussion #419](https://github.com/encode/httpcore/discussions/419) for a little more background. + +There's some other neat bits in there too, such as the "trace" extension, which gives a hook into inspecting the internal events that occur during the request/response cycle. This extension is needed for the HTTPX cli, in order to... + +* Log the point at which the connection is established, and the IP/port on which it is made. +* Determine if the outgoing request should log as HTTP/1.1 or HTTP/2, rather than having to assume it's HTTP/2 if the --http2 flag was passed. (Which may not actually be true.) +* Log SSL version info / certificate info. + +Note that `curio` support is not currently available in 0.14.0. If you're using `httpcore` with `curio` please get in touch, so we can assess if we ought to prioritize it as a feature or not. + +## 0.13.7 (September 13th, 2021) + +- Fix broken error messaging when URL scheme is missing, or a non HTTP(S) scheme is used. (Pull #403) + +## 0.13.6 (June 15th, 2021) + +### Fixed + +- Close sockets when read or write timeouts occur. (Pull #365) + +## 0.13.5 (June 14th, 2021) + +### Fixed + +- Resolved niggles with AnyIO EOF behaviours. (Pull #358, #362) + +## 0.13.4 (June 9th, 2021) + +### Added + +- Improved error messaging when URL scheme is missing, or a non HTTP(S) scheme is used. (Pull #354) + +### Fixed + +- Switched to `anyio` as the default backend implementation when running with `asyncio`. Resolves some awkward [TLS timeout issues](https://github.com/encode/httpx/discussions/1511). + +## 0.13.3 (May 6th, 2021) + +### Added + +- Support HTTP/2 prior knowledge, using `httpcore.SyncConnectionPool(http1=False)`. (Pull #333) + +### Fixed + +- Handle cases where environment does not provide `select.poll` support. (Pull #331) + +## 0.13.2 (April 29th, 2021) + +### Added + +- Improve error message for specific case of `RemoteProtocolError` where server disconnects without sending a response. (Pull #313) + +## 0.13.1 (April 28th, 2021) + +### Fixed + +- More resiliant testing for closed connections. (Pull #311) +- Don't raise exceptions on ungraceful connection closes. (Pull #310) + +## 0.13.0 (April 21st, 2021) + +The 0.13 release updates the core API in order to match the HTTPX Transport API, +introduced in HTTPX 0.18 onwards. + +An example of making requests with the new interface is: + +```python +with httpcore.SyncConnectionPool() as http: + status_code, headers, stream, extensions = http.handle_request( + method=b'GET', + url=(b'https', b'example.org', 443, b'/'), + headers=[(b'host', b'example.org'), (b'user-agent', b'httpcore')] + stream=httpcore.ByteStream(b''), + extensions={} + ) + body = stream.read() + print(status_code, body) +``` + +### Changed + +- The `.request()` method is now `handle_request()`. (Pull #296) +- The `.arequest()` method is now `.handle_async_request()`. (Pull #296) +- The `headers` argument is no longer optional. (Pull #296) +- The `stream` argument is no longer optional. (Pull #296) +- The `ext` argument is now named `extensions`, and is no longer optional. (Pull #296) +- The `"reason"` extension keyword is now named `"reason_phrase"`. (Pull #296) +- The `"reason_phrase"` and `"http_version"` extensions now use byte strings for their values. (Pull #296) +- The `httpcore.PlainByteStream()` class becomes `httpcore.ByteStream()`. (Pull #296) + +### Added + +- Streams now support a `.read()` interface. (Pull #296) + +### Fixed + +- Task cancellation no longer leaks connections from the connection pool. (Pull #305) + +## 0.12.3 (December 7th, 2020) + +### Fixed + +- Abort SSL connections on close rather than waiting for remote EOF when using `asyncio`. (Pull #167) +- Fix exception raised in case of connect timeouts when using the `anyio` backend. (Pull #236) +- Fix `Host` header precedence for `:authority` in HTTP/2. (Pull #241, #243) +- Handle extra edge case when detecting for socket readability when using `asyncio`. (Pull #242, #244) +- Fix `asyncio` SSL warning when using proxy tunneling. (Pull #249) + +## 0.12.2 (November 20th, 2020) + +### Fixed + +- Properly wrap connect errors on the asyncio backend. (Pull #235) +- Fix `ImportError` occurring on Python 3.9 when using the HTTP/1.1 sync client in a multithreaded context. (Pull #237) + +## 0.12.1 (November 7th, 2020) + +### Added + +- Add connect retries. (Pull #221) + +### Fixed + +- Tweak detection of dropped connections, resolving an issue with open files limits on Linux. (Pull #185) +- Avoid leaking connections when establishing an HTTP tunnel to a proxy has failed. (Pull #223) +- Properly wrap OS errors when using `trio`. (Pull #225) + +## 0.12.0 (October 6th, 2020) + +### Changed + +- HTTP header casing is now preserved, rather than always sent in lowercase. (#216 and python-hyper/h11#104) + +### Added + +- Add Python 3.9 to officially supported versions. + +### Fixed + +- Gracefully handle a stdlib asyncio bug when a connection is closed while it is in a paused-for-reading state. (#201) + +## 0.11.1 (September 28nd, 2020) + +### Fixed + +- Add await to async semaphore release() coroutine (#197) +- Drop incorrect curio classifier (#192) + +## 0.11.0 (September 22nd, 2020) + +The Transport API with 0.11.0 has a couple of significant changes. + +Firstly we've moved changed the request interface in order to allow extensions, which will later enable us to support features +such as trailing headers, HTTP/2 server push, and CONNECT/Upgrade connections. + +The interface changes from: + +```python +def request(method, url, headers, stream, timeout): + return (http_version, status_code, reason, headers, stream) +``` + +To instead including an optional dictionary of extensions on the request and response: + +```python +def request(method, url, headers, stream, ext): + return (status_code, headers, stream, ext) +``` + +Having an open-ended extensions point will allow us to add later support for various optional features, that wouldn't otherwise be supported without these API changes. + +In particular: + +* Trailing headers support. +* HTTP/2 Server Push +* sendfile. +* Exposing raw connection on CONNECT, Upgrade, HTTP/2 bi-di streaming. +* Exposing debug information out of the API, including template name, template context. + +Currently extensions are limited to: + +* request: `timeout` - Optional. Timeout dictionary. +* response: `http_version` - Optional. Include the HTTP version used on the response. +* response: `reason` - Optional. Include the reason phrase used on the response. Only valid with HTTP/1.*. + +See https://github.com/encode/httpx/issues/1274#issuecomment-694884553 for the history behind this. + +Secondly, the async version of `request` is now namespaced as `arequest`. + +This allows concrete transports to support both sync and async implementations on the same class. + +### Added + +- Add curio support. (Pull #168) +- Add anyio support, with `backend="anyio"`. (Pull #169) + +### Changed + +- Update the Transport API to use 'ext' for optional extensions. (Pull #190) +- Update the Transport API to use `.request` and `.arequest` so implementations can support both sync and async. (Pull #189) + +## 0.10.2 (August 20th, 2020) + +### Added + +- Added Unix Domain Socket support. (Pull #139) + +### Fixed + +- Always include the port on proxy CONNECT requests. (Pull #154) +- Fix `max_keepalive_connections` configuration. (Pull #153) +- Fixes behaviour in HTTP/1.1 where server disconnects can be used to signal the end of the response body. (Pull #164) + +## 0.10.1 (August 7th, 2020) + +- Include `max_keepalive_connections` on `AsyncHTTPProxy`/`SyncHTTPProxy` classes. + +## 0.10.0 (August 7th, 2020) + +The most notable change in the 0.10.0 release is that HTTP/2 support is now fully optional. + +Use either `pip install httpcore` for HTTP/1.1 support only, or `pip install httpcore[http2]` for HTTP/1.1 and HTTP/2 support. + +### Added + +- HTTP/2 support becomes optional. (Pull #121, #130) +- Add `local_address=...` support. (Pull #100, #134) +- Add `PlainByteStream`, `IteratorByteStream`, `AsyncIteratorByteStream`. The `AsyncByteSteam` and `SyncByteStream` classes are now pure interface classes. (#133) +- Add `LocalProtocolError`, `RemoteProtocolError` exceptions. (Pull #129) +- Add `UnsupportedProtocol` exception. (Pull #128) +- Add `.get_connection_info()` method. (Pull #102, #137) +- Add better TRACE logs. (Pull #101) + +### Changed + +- `max_keepalive` is deprecated in favour of `max_keepalive_connections`. (Pull #140) + +### Fixed + +- Improve handling of server disconnects. (Pull #112) + +## 0.9.1 (May 27th, 2020) + +### Fixed + +- Proper host resolution for sync case, including IPv6 support. (Pull #97) +- Close outstanding connections when connection pool is closed. (Pull #98) + +## 0.9.0 (May 21th, 2020) + +### Changed + +- URL port becomes an `Optional[int]` instead of `int`. (Pull #92) + +### Fixed + +- Honor HTTP/2 max concurrent streams settings. (Pull #89, #90) +- Remove incorrect debug log. (Pull #83) + +## 0.8.4 (May 11th, 2020) + +### Added + +- Logging via HTTPCORE_LOG_LEVEL and HTTPX_LOG_LEVEL environment variables +and TRACE level logging. (Pull #79) + +### Fixed + +- Reuse of connections on HTTP/2 in close concurrency situations. (Pull #81) + +## 0.8.3 (May 6rd, 2020) + +### Fixed + +- Include `Host` and `Accept` headers on proxy "CONNECT" requests. +- De-duplicate any headers also contained in proxy_headers. +- HTTP/2 flag not being passed down to proxy connections. + +## 0.8.2 (May 3rd, 2020) + +### Fixed + +- Fix connections using proxy forwarding requests not being added to the +connection pool properly. (Pull #70) + +## 0.8.1 (April 30th, 2020) + +### Changed + +- Allow inherintance of both `httpcore.AsyncByteStream`, `httpcore.SyncByteStream` without type conflicts. + +## 0.8.0 (April 30th, 2020) + +### Fixed + +- Fixed tunnel proxy support. + +### Added + +- New `TimeoutException` base class. + +## 0.7.0 (March 5th, 2020) + +- First integration with HTTPX. diff --git a/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/RECORD b/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/RECORD new file mode 100644 index 0000000..44d4769 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/RECORD @@ -0,0 +1,68 @@ +httpcore-1.0.9.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +httpcore-1.0.9.dist-info/METADATA,sha256=_i1P2mGZEol4d54M8n88BFxTGGP83Zh-rMdPOhjUHCE,21529 +httpcore-1.0.9.dist-info/RECORD,, +httpcore-1.0.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +httpcore-1.0.9.dist-info/licenses/LICENSE.md,sha256=_ctZFUx0y6uhahEkL3dAvqnyPW_rVUeRfYxflKgDkqU,1518 +httpcore/__init__.py,sha256=9kT_kqChCCJUTHww24ZmR_ezcdbpRYWksD-gYNzkZP8,3445 +httpcore/__pycache__/__init__.cpython-312.pyc,, +httpcore/__pycache__/_api.cpython-312.pyc,, +httpcore/__pycache__/_exceptions.cpython-312.pyc,, +httpcore/__pycache__/_models.cpython-312.pyc,, +httpcore/__pycache__/_ssl.cpython-312.pyc,, +httpcore/__pycache__/_synchronization.cpython-312.pyc,, +httpcore/__pycache__/_trace.cpython-312.pyc,, +httpcore/__pycache__/_utils.cpython-312.pyc,, +httpcore/_api.py,sha256=unZmeDschBWCGCPCwkS3Wot9euK6bg_kKxLtGTxw214,3146 +httpcore/_async/__init__.py,sha256=EWdl2v4thnAHzJpqjU4h2a8DUiGAvNiWrkii9pfhTf0,1221 +httpcore/_async/__pycache__/__init__.cpython-312.pyc,, +httpcore/_async/__pycache__/connection.cpython-312.pyc,, +httpcore/_async/__pycache__/connection_pool.cpython-312.pyc,, +httpcore/_async/__pycache__/http11.cpython-312.pyc,, +httpcore/_async/__pycache__/http2.cpython-312.pyc,, +httpcore/_async/__pycache__/http_proxy.cpython-312.pyc,, +httpcore/_async/__pycache__/interfaces.cpython-312.pyc,, +httpcore/_async/__pycache__/socks_proxy.cpython-312.pyc,, +httpcore/_async/connection.py,sha256=6OcPXqMEfc0BU38_-iHUNDd1vKSTc2UVT09XqNb_BOk,8449 +httpcore/_async/connection_pool.py,sha256=DOIQ2s2ZCf9qfwxhzMprTPLqCL8OxGXiKF6qRHxvVyY,17307 +httpcore/_async/http11.py,sha256=-qM9bV7PjSQF5vxs37-eUXOIFwbIjPcZbNliuX9TtBw,13880 +httpcore/_async/http2.py,sha256=azX1fcmtXaIwjputFlZ4vd92J8xwjGOa9ax9QIv4394,23936 +httpcore/_async/http_proxy.py,sha256=2zVkrlv-Ds-rWGaqaXlrhEJiAQFPo23BT3Gq_sWoBXU,14701 +httpcore/_async/interfaces.py,sha256=jTiaWL83pgpGC9ziv90ZfwaKNMmHwmOalzaKiuTxATo,4455 +httpcore/_async/socks_proxy.py,sha256=lLKgLlggPfhFlqi0ODeBkOWvt9CghBBUyqsnsU1tx6Q,13841 +httpcore/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +httpcore/_backends/__pycache__/__init__.cpython-312.pyc,, +httpcore/_backends/__pycache__/anyio.cpython-312.pyc,, +httpcore/_backends/__pycache__/auto.cpython-312.pyc,, +httpcore/_backends/__pycache__/base.cpython-312.pyc,, +httpcore/_backends/__pycache__/mock.cpython-312.pyc,, +httpcore/_backends/__pycache__/sync.cpython-312.pyc,, +httpcore/_backends/__pycache__/trio.cpython-312.pyc,, +httpcore/_backends/anyio.py,sha256=x8PgEhXRC8bVqsdzk_YJx8Y6d9Tub06CuUSwnbmtqoY,5252 +httpcore/_backends/auto.py,sha256=zO136PKZmsaTDK-HRk84eA-MUg8_2wJf4NvmK432Aio,1662 +httpcore/_backends/base.py,sha256=aShgRdZnMmRhFWHetjumlM73f8Kz1YOAyCUP_4kHslA,3042 +httpcore/_backends/mock.py,sha256=er9T436uSe7NLrfiLa4x6Nuqg5ivQ693CxWYCWsgbH4,4077 +httpcore/_backends/sync.py,sha256=bhE4d9iK9Umxdsdsgm2EfKnXaBms2WggGYU-7jmUujU,7977 +httpcore/_backends/trio.py,sha256=LHu4_Mr5MswQmmT3yE4oLgf9b_JJfeVS4BjDxeJc7Ro,5996 +httpcore/_exceptions.py,sha256=looCKga3_YVYu3s-d3L9RMPRJyhsY7fiuuGxvkOD0c0,1184 +httpcore/_models.py,sha256=IO2CcXcdpovRcLTdGFGB6RyBZdEm2h_TOmoCc4rEKho,17623 +httpcore/_ssl.py,sha256=srqmSNU4iOUvWF-SrJvb8G_YEbHFELOXQOwdDIBTS9c,187 +httpcore/_sync/__init__.py,sha256=JBDIgXt5la1LCJ1sLQeKhjKFpLnpNr8Svs6z2ni3fgg,1141 +httpcore/_sync/__pycache__/__init__.cpython-312.pyc,, +httpcore/_sync/__pycache__/connection.cpython-312.pyc,, +httpcore/_sync/__pycache__/connection_pool.cpython-312.pyc,, +httpcore/_sync/__pycache__/http11.cpython-312.pyc,, +httpcore/_sync/__pycache__/http2.cpython-312.pyc,, +httpcore/_sync/__pycache__/http_proxy.cpython-312.pyc,, +httpcore/_sync/__pycache__/interfaces.cpython-312.pyc,, +httpcore/_sync/__pycache__/socks_proxy.cpython-312.pyc,, +httpcore/_sync/connection.py,sha256=9exGOb3PB-Mp2T1-sckSeL2t-tJ_9-NXomV8ihmWCgU,8238 +httpcore/_sync/connection_pool.py,sha256=a-T8LTsUxc7r0Ww1atfHSDoWPjQ0fA8Ul7S3-F0Mj70,16955 +httpcore/_sync/http11.py,sha256=IFobD1Md5JFlJGKWnh1_Q3epikUryI8qo09v8MiJIEA,13476 +httpcore/_sync/http2.py,sha256=AxU4yhcq68Bn5vqdJYtiXKYUj7nvhYbxz3v4rT4xnvA,23400 +httpcore/_sync/http_proxy.py,sha256=_al_6crKuEZu2wyvu493RZImJdBJnj5oGKNjLOJL2Zo,14463 +httpcore/_sync/interfaces.py,sha256=snXON42vUDHO5JBJvo8D4VWk2Wat44z2OXXHDrjbl94,4344 +httpcore/_sync/socks_proxy.py,sha256=zegZW9Snqj2_992DFJa8_CppOVBkVL4AgwduRkStakQ,13614 +httpcore/_synchronization.py,sha256=zSi13mAColBnknjZBknUC6hKNDQT4C6ijnezZ-r0T2s,9434 +httpcore/_trace.py,sha256=ck6ZoIzYTkdNAIfq5MGeKqBXDtqjOX-qfYwmZFbrGco,3952 +httpcore/_utils.py,sha256=_RLgXYOAYC350ikALV59GZ68IJrdocRZxPs9PjmzdFY,1537 +httpcore/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/WHEEL b/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/WHEEL new file mode 100644 index 0000000..12228d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/licenses/LICENSE.md b/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/licenses/LICENSE.md new file mode 100644 index 0000000..311b2b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/licenses/LICENSE.md @@ -0,0 +1,27 @@ +Copyright © 2020, [Encode OSS Ltd](https://www.encode.io/). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/httpcore/__init__.py b/venv/lib/python3.12/site-packages/httpcore/__init__.py new file mode 100644 index 0000000..9a92dc4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/__init__.py @@ -0,0 +1,141 @@ +from ._api import request, stream +from ._async import ( + AsyncConnectionInterface, + AsyncConnectionPool, + AsyncHTTP2Connection, + AsyncHTTP11Connection, + AsyncHTTPConnection, + AsyncHTTPProxy, + AsyncSOCKSProxy, +) +from ._backends.base import ( + SOCKET_OPTION, + AsyncNetworkBackend, + AsyncNetworkStream, + NetworkBackend, + NetworkStream, +) +from ._backends.mock import AsyncMockBackend, AsyncMockStream, MockBackend, MockStream +from ._backends.sync import SyncBackend +from ._exceptions import ( + ConnectError, + ConnectionNotAvailable, + ConnectTimeout, + LocalProtocolError, + NetworkError, + PoolTimeout, + ProtocolError, + ProxyError, + ReadError, + ReadTimeout, + RemoteProtocolError, + TimeoutException, + UnsupportedProtocol, + WriteError, + WriteTimeout, +) +from ._models import URL, Origin, Proxy, Request, Response +from ._ssl import default_ssl_context +from ._sync import ( + ConnectionInterface, + ConnectionPool, + HTTP2Connection, + HTTP11Connection, + HTTPConnection, + HTTPProxy, + SOCKSProxy, +) + +# The 'httpcore.AnyIOBackend' class is conditional on 'anyio' being installed. +try: + from ._backends.anyio import AnyIOBackend +except ImportError: # pragma: nocover + + class AnyIOBackend: # type: ignore + def __init__(self, *args, **kwargs): # type: ignore + msg = ( + "Attempted to use 'httpcore.AnyIOBackend' but 'anyio' is not installed." + ) + raise RuntimeError(msg) + + +# The 'httpcore.TrioBackend' class is conditional on 'trio' being installed. +try: + from ._backends.trio import TrioBackend +except ImportError: # pragma: nocover + + class TrioBackend: # type: ignore + def __init__(self, *args, **kwargs): # type: ignore + msg = "Attempted to use 'httpcore.TrioBackend' but 'trio' is not installed." + raise RuntimeError(msg) + + +__all__ = [ + # top-level requests + "request", + "stream", + # models + "Origin", + "URL", + "Request", + "Response", + "Proxy", + # async + "AsyncHTTPConnection", + "AsyncConnectionPool", + "AsyncHTTPProxy", + "AsyncHTTP11Connection", + "AsyncHTTP2Connection", + "AsyncConnectionInterface", + "AsyncSOCKSProxy", + # sync + "HTTPConnection", + "ConnectionPool", + "HTTPProxy", + "HTTP11Connection", + "HTTP2Connection", + "ConnectionInterface", + "SOCKSProxy", + # network backends, implementations + "SyncBackend", + "AnyIOBackend", + "TrioBackend", + # network backends, mock implementations + "AsyncMockBackend", + "AsyncMockStream", + "MockBackend", + "MockStream", + # network backends, interface + "AsyncNetworkStream", + "AsyncNetworkBackend", + "NetworkStream", + "NetworkBackend", + # util + "default_ssl_context", + "SOCKET_OPTION", + # exceptions + "ConnectionNotAvailable", + "ProxyError", + "ProtocolError", + "LocalProtocolError", + "RemoteProtocolError", + "UnsupportedProtocol", + "TimeoutException", + "PoolTimeout", + "ConnectTimeout", + "ReadTimeout", + "WriteTimeout", + "NetworkError", + "ConnectError", + "ReadError", + "WriteError", +] + +__version__ = "1.0.9" + + +__locals = locals() +for __name in __all__: + # Exclude SOCKET_OPTION, it causes AttributeError on Python 3.14 + if not __name.startswith(("__", "SOCKET_OPTION")): + setattr(__locals[__name], "__module__", "httpcore") # noqa diff --git a/venv/lib/python3.12/site-packages/httpcore/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a8d18cf Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/__pycache__/_api.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_api.cpython-312.pyc new file mode 100644 index 0000000..2877055 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_api.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/__pycache__/_exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_exceptions.cpython-312.pyc new file mode 100644 index 0000000..4162c05 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/__pycache__/_models.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_models.cpython-312.pyc new file mode 100644 index 0000000..d54765d Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_models.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/__pycache__/_ssl.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_ssl.cpython-312.pyc new file mode 100644 index 0000000..fbe3735 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_ssl.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/__pycache__/_synchronization.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_synchronization.cpython-312.pyc new file mode 100644 index 0000000..32fda57 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_synchronization.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/__pycache__/_trace.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_trace.cpython-312.pyc new file mode 100644 index 0000000..132216b Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_trace.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/__pycache__/_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_utils.cpython-312.pyc new file mode 100644 index 0000000..eef668d Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/__pycache__/_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_api.py b/venv/lib/python3.12/site-packages/httpcore/_api.py new file mode 100644 index 0000000..38b961d --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_api.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +import contextlib +import typing + +from ._models import URL, Extensions, HeaderTypes, Response +from ._sync.connection_pool import ConnectionPool + + +def request( + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterator[bytes] | None = None, + extensions: Extensions | None = None, +) -> Response: + """ + Sends an HTTP request, returning the response. + + ``` + response = httpcore.request("GET", "https://www.example.com/") + ``` + + Arguments: + method: The HTTP method for the request. Typically one of `"GET"`, + `"OPTIONS"`, `"HEAD"`, `"POST"`, `"PUT"`, `"PATCH"`, or `"DELETE"`. + url: The URL of the HTTP request. Either as an instance of `httpcore.URL`, + or as str/bytes. + headers: The HTTP request headers. Either as a dictionary of str/bytes, + or as a list of two-tuples of str/bytes. + content: The content of the request body. Either as bytes, + or as a bytes iterator. + extensions: A dictionary of optional extra information included on the request. + Possible keys include `"timeout"`. + + Returns: + An instance of `httpcore.Response`. + """ + with ConnectionPool() as pool: + return pool.request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + + +@contextlib.contextmanager +def stream( + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterator[bytes] | None = None, + extensions: Extensions | None = None, +) -> typing.Iterator[Response]: + """ + Sends an HTTP request, returning the response within a content manager. + + ``` + with httpcore.stream("GET", "https://www.example.com/") as response: + ... + ``` + + When using the `stream()` function, the body of the response will not be + automatically read. If you want to access the response body you should + either use `content = response.read()`, or `for chunk in response.iter_content()`. + + Arguments: + method: The HTTP method for the request. Typically one of `"GET"`, + `"OPTIONS"`, `"HEAD"`, `"POST"`, `"PUT"`, `"PATCH"`, or `"DELETE"`. + url: The URL of the HTTP request. Either as an instance of `httpcore.URL`, + or as str/bytes. + headers: The HTTP request headers. Either as a dictionary of str/bytes, + or as a list of two-tuples of str/bytes. + content: The content of the request body. Either as bytes, + or as a bytes iterator. + extensions: A dictionary of optional extra information included on the request. + Possible keys include `"timeout"`. + + Returns: + An instance of `httpcore.Response`. + """ + with ConnectionPool() as pool: + with pool.stream( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) as response: + yield response diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/__init__.py b/venv/lib/python3.12/site-packages/httpcore/_async/__init__.py new file mode 100644 index 0000000..88dc7f0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_async/__init__.py @@ -0,0 +1,39 @@ +from .connection import AsyncHTTPConnection +from .connection_pool import AsyncConnectionPool +from .http11 import AsyncHTTP11Connection +from .http_proxy import AsyncHTTPProxy +from .interfaces import AsyncConnectionInterface + +try: + from .http2 import AsyncHTTP2Connection +except ImportError: # pragma: nocover + + class AsyncHTTP2Connection: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use http2 support, but the `h2` package is not " + "installed. Use 'pip install httpcore[http2]'." + ) + + +try: + from .socks_proxy import AsyncSOCKSProxy +except ImportError: # pragma: nocover + + class AsyncSOCKSProxy: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use SOCKS support, but the `socksio` package is not " + "installed. Use 'pip install httpcore[socks]'." + ) + + +__all__ = [ + "AsyncHTTPConnection", + "AsyncConnectionPool", + "AsyncHTTPProxy", + "AsyncHTTP11Connection", + "AsyncHTTP2Connection", + "AsyncConnectionInterface", + "AsyncSOCKSProxy", +] diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4add372 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/connection.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/connection.cpython-312.pyc new file mode 100644 index 0000000..6daa160 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/connection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-312.pyc new file mode 100644 index 0000000..e25a5cc Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/http11.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/http11.cpython-312.pyc new file mode 100644 index 0000000..90be6bc Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/http11.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/http2.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/http2.cpython-312.pyc new file mode 100644 index 0000000..3a7bcf5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/http2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-312.pyc new file mode 100644 index 0000000..0d8f3fa Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/interfaces.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/interfaces.cpython-312.pyc new file mode 100644 index 0000000..1310d35 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/interfaces.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/socks_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/socks_proxy.cpython-312.pyc new file mode 100644 index 0000000..1f6883d Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_async/__pycache__/socks_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/connection.py b/venv/lib/python3.12/site-packages/httpcore/_async/connection.py new file mode 100644 index 0000000..b42581d --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_async/connection.py @@ -0,0 +1,222 @@ +from __future__ import annotations + +import itertools +import logging +import ssl +import types +import typing + +from .._backends.auto import AutoBackend +from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream +from .._exceptions import ConnectError, ConnectTimeout +from .._models import Origin, Request, Response +from .._ssl import default_ssl_context +from .._synchronization import AsyncLock +from .._trace import Trace +from .http11 import AsyncHTTP11Connection +from .interfaces import AsyncConnectionInterface + +RETRIES_BACKOFF_FACTOR = 0.5 # 0s, 0.5s, 1s, 2s, 4s, etc. + + +logger = logging.getLogger("httpcore.connection") + + +def exponential_backoff(factor: float) -> typing.Iterator[float]: + """ + Generate a geometric sequence that has a ratio of 2 and starts with 0. + + For example: + - `factor = 2`: `0, 2, 4, 8, 16, 32, 64, ...` + - `factor = 3`: `0, 3, 6, 12, 24, 48, 96, ...` + """ + yield 0 + for n in itertools.count(): + yield factor * 2**n + + +class AsyncHTTPConnection(AsyncConnectionInterface): + def __init__( + self, + origin: Origin, + ssl_context: ssl.SSLContext | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + self._origin = origin + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend: AsyncNetworkBackend = ( + AutoBackend() if network_backend is None else network_backend + ) + self._connection: AsyncConnectionInterface | None = None + self._connect_failed: bool = False + self._request_lock = AsyncLock() + self._socket_options = socket_options + + async def handle_async_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection to {self._origin}" + ) + + try: + async with self._request_lock: + if self._connection is None: + stream = await self._connect(request) + + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import AsyncHTTP2Connection + + self._connection = AsyncHTTP2Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = AsyncHTTP11Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except BaseException as exc: + self._connect_failed = True + raise exc + + return await self._connection.handle_async_request(request) + + async def _connect(self, request: Request) -> AsyncNetworkStream: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + retries_left = self._retries + delays = exponential_backoff(factor=RETRIES_BACKOFF_FACTOR) + + while True: + try: + if self._uds is None: + kwargs = { + "host": self._origin.host.decode("ascii"), + "port": self._origin.port, + "local_address": self._local_address, + "timeout": timeout, + "socket_options": self._socket_options, + } + async with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = await self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + else: + kwargs = { + "path": self._uds, + "timeout": timeout, + "socket_options": self._socket_options, + } + async with Trace( + "connect_unix_socket", logger, request, kwargs + ) as trace: + stream = await self._network_backend.connect_unix_socket( + **kwargs + ) + trace.return_value = stream + + if self._origin.scheme in (b"https", b"wss"): + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname + or self._origin.host.decode("ascii"), + "timeout": timeout, + } + async with Trace("start_tls", logger, request, kwargs) as trace: + stream = await stream.start_tls(**kwargs) + trace.return_value = stream + return stream + except (ConnectError, ConnectTimeout): + if retries_left <= 0: + raise + retries_left -= 1 + delay = next(delays) + async with Trace("retry", logger, request, kwargs) as trace: + await self._network_backend.sleep(delay) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + async def aclose(self) -> None: + if self._connection is not None: + async with Trace("close", logger, None, {}): + await self._connection.aclose() + + def is_available(self) -> bool: + if self._connection is None: + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 + and (self._origin.scheme == b"https" or not self._http1) + and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + async def __aenter__(self) -> AsyncHTTPConnection: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + await self.aclose() diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/connection_pool.py b/venv/lib/python3.12/site-packages/httpcore/_async/connection_pool.py new file mode 100644 index 0000000..96e973d --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_async/connection_pool.py @@ -0,0 +1,420 @@ +from __future__ import annotations + +import ssl +import sys +import types +import typing + +from .._backends.auto import AutoBackend +from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend +from .._exceptions import ConnectionNotAvailable, UnsupportedProtocol +from .._models import Origin, Proxy, Request, Response +from .._synchronization import AsyncEvent, AsyncShieldCancellation, AsyncThreadLock +from .connection import AsyncHTTPConnection +from .interfaces import AsyncConnectionInterface, AsyncRequestInterface + + +class AsyncPoolRequest: + def __init__(self, request: Request) -> None: + self.request = request + self.connection: AsyncConnectionInterface | None = None + self._connection_acquired = AsyncEvent() + + def assign_to_connection(self, connection: AsyncConnectionInterface | None) -> None: + self.connection = connection + self._connection_acquired.set() + + def clear_connection(self) -> None: + self.connection = None + self._connection_acquired = AsyncEvent() + + async def wait_for_connection( + self, timeout: float | None = None + ) -> AsyncConnectionInterface: + if self.connection is None: + await self._connection_acquired.wait(timeout=timeout) + assert self.connection is not None + return self.connection + + def is_queued(self) -> bool: + return self.connection is None + + +class AsyncConnectionPool(AsyncRequestInterface): + """ + A connection pool for making HTTP requests. + """ + + def __init__( + self, + ssl_context: ssl.SSLContext | None = None, + proxy: Proxy | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish a + connection. + local_address: Local address to connect from. Can also be used to connect + using a particular address family. Using `local_address="0.0.0.0"` + will connect using an `AF_INET` address (IPv4), while using + `local_address="::"` will connect using an `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + socket_options: Socket options that have to be included + in the TCP socket when the connection was established. + """ + self._ssl_context = ssl_context + self._proxy = proxy + self._max_connections = ( + sys.maxsize if max_connections is None else max_connections + ) + self._max_keepalive_connections = ( + sys.maxsize + if max_keepalive_connections is None + else max_keepalive_connections + ) + self._max_keepalive_connections = min( + self._max_connections, self._max_keepalive_connections + ) + + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend = ( + AutoBackend() if network_backend is None else network_backend + ) + self._socket_options = socket_options + + # The mutable state on a connection pool is the queue of incoming requests, + # and the set of connections that are servicing those requests. + self._connections: list[AsyncConnectionInterface] = [] + self._requests: list[AsyncPoolRequest] = [] + + # We only mutate the state of the connection pool within an 'optional_thread_lock' + # context. This holds a threading lock unless we're running in async mode, + # in which case it is a no-op. + self._optional_thread_lock = AsyncThreadLock() + + def create_connection(self, origin: Origin) -> AsyncConnectionInterface: + if self._proxy is not None: + if self._proxy.url.scheme in (b"socks5", b"socks5h"): + from .socks_proxy import AsyncSocks5Connection + + return AsyncSocks5Connection( + proxy_origin=self._proxy.url.origin, + proxy_auth=self._proxy.auth, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + elif origin.scheme == b"http": + from .http_proxy import AsyncForwardHTTPConnection + + return AsyncForwardHTTPConnection( + proxy_origin=self._proxy.url.origin, + proxy_headers=self._proxy.headers, + proxy_ssl_context=self._proxy.ssl_context, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + ) + from .http_proxy import AsyncTunnelHTTPConnection + + return AsyncTunnelHTTPConnection( + proxy_origin=self._proxy.url.origin, + proxy_headers=self._proxy.headers, + proxy_ssl_context=self._proxy.ssl_context, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + return AsyncHTTPConnection( + origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + retries=self._retries, + local_address=self._local_address, + uds=self._uds, + network_backend=self._network_backend, + socket_options=self._socket_options, + ) + + @property + def connections(self) -> list[AsyncConnectionInterface]: + """ + Return a list of the connections currently in the pool. + + For example: + + ```python + >>> pool.connections + [ + , + , + , + ] + ``` + """ + return list(self._connections) + + async def handle_async_request(self, request: Request) -> Response: + """ + Send an HTTP request, and return an HTTP response. + + This is the core implementation that is called into by `.request()` or `.stream()`. + """ + scheme = request.url.scheme.decode() + if scheme == "": + raise UnsupportedProtocol( + "Request URL is missing an 'http://' or 'https://' protocol." + ) + if scheme not in ("http", "https", "ws", "wss"): + raise UnsupportedProtocol( + f"Request URL has an unsupported protocol '{scheme}://'." + ) + + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("pool", None) + + with self._optional_thread_lock: + # Add the incoming request to our request queue. + pool_request = AsyncPoolRequest(request) + self._requests.append(pool_request) + + try: + while True: + with self._optional_thread_lock: + # Assign incoming requests to available connections, + # closing or creating new connections as required. + closing = self._assign_requests_to_connections() + await self._close_connections(closing) + + # Wait until this request has an assigned connection. + connection = await pool_request.wait_for_connection(timeout=timeout) + + try: + # Send the request on the assigned connection. + response = await connection.handle_async_request( + pool_request.request + ) + except ConnectionNotAvailable: + # In some cases a connection may initially be available to + # handle a request, but then become unavailable. + # + # In this case we clear the connection and try again. + pool_request.clear_connection() + else: + break # pragma: nocover + + except BaseException as exc: + with self._optional_thread_lock: + # For any exception or cancellation we remove the request from + # the queue, and then re-assign requests to connections. + self._requests.remove(pool_request) + closing = self._assign_requests_to_connections() + + await self._close_connections(closing) + raise exc from None + + # Return the response. Note that in this case we still have to manage + # the point at which the response is closed. + assert isinstance(response.stream, typing.AsyncIterable) + return Response( + status=response.status, + headers=response.headers, + content=PoolByteStream( + stream=response.stream, pool_request=pool_request, pool=self + ), + extensions=response.extensions, + ) + + def _assign_requests_to_connections(self) -> list[AsyncConnectionInterface]: + """ + Manage the state of the connection pool, assigning incoming + requests to connections as available. + + Called whenever a new request is added or removed from the pool. + + Any closing connections are returned, allowing the I/O for closing + those connections to be handled seperately. + """ + closing_connections = [] + + # First we handle cleaning up any connections that are closed, + # have expired their keep-alive, or surplus idle connections. + for connection in list(self._connections): + if connection.is_closed(): + # log: "removing closed connection" + self._connections.remove(connection) + elif connection.has_expired(): + # log: "closing expired connection" + self._connections.remove(connection) + closing_connections.append(connection) + elif ( + connection.is_idle() + and len([connection.is_idle() for connection in self._connections]) + > self._max_keepalive_connections + ): + # log: "closing idle connection" + self._connections.remove(connection) + closing_connections.append(connection) + + # Assign queued requests to connections. + queued_requests = [request for request in self._requests if request.is_queued()] + for pool_request in queued_requests: + origin = pool_request.request.url.origin + available_connections = [ + connection + for connection in self._connections + if connection.can_handle_request(origin) and connection.is_available() + ] + idle_connections = [ + connection for connection in self._connections if connection.is_idle() + ] + + # There are three cases for how we may be able to handle the request: + # + # 1. There is an existing connection that can handle the request. + # 2. We can create a new connection to handle the request. + # 3. We can close an idle connection and then create a new connection + # to handle the request. + if available_connections: + # log: "reusing existing connection" + connection = available_connections[0] + pool_request.assign_to_connection(connection) + elif len(self._connections) < self._max_connections: + # log: "creating new connection" + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + elif idle_connections: + # log: "closing idle connection" + connection = idle_connections[0] + self._connections.remove(connection) + closing_connections.append(connection) + # log: "creating new connection" + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + + return closing_connections + + async def _close_connections(self, closing: list[AsyncConnectionInterface]) -> None: + # Close connections which have been removed from the pool. + with AsyncShieldCancellation(): + for connection in closing: + await connection.aclose() + + async def aclose(self) -> None: + # Explicitly close the connection pool. + # Clears all existing requests and connections. + with self._optional_thread_lock: + closing_connections = list(self._connections) + self._connections = [] + await self._close_connections(closing_connections) + + async def __aenter__(self) -> AsyncConnectionPool: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + await self.aclose() + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + with self._optional_thread_lock: + request_is_queued = [request.is_queued() for request in self._requests] + connection_is_idle = [ + connection.is_idle() for connection in self._connections + ] + + num_active_requests = request_is_queued.count(False) + num_queued_requests = request_is_queued.count(True) + num_active_connections = connection_is_idle.count(False) + num_idle_connections = connection_is_idle.count(True) + + requests_info = ( + f"Requests: {num_active_requests} active, {num_queued_requests} queued" + ) + connection_info = ( + f"Connections: {num_active_connections} active, {num_idle_connections} idle" + ) + + return f"<{class_name} [{requests_info} | {connection_info}]>" + + +class PoolByteStream: + def __init__( + self, + stream: typing.AsyncIterable[bytes], + pool_request: AsyncPoolRequest, + pool: AsyncConnectionPool, + ) -> None: + self._stream = stream + self._pool_request = pool_request + self._pool = pool + self._closed = False + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + try: + async for part in self._stream: + yield part + except BaseException as exc: + await self.aclose() + raise exc from None + + async def aclose(self) -> None: + if not self._closed: + self._closed = True + with AsyncShieldCancellation(): + if hasattr(self._stream, "aclose"): + await self._stream.aclose() + + with self._pool._optional_thread_lock: + self._pool._requests.remove(self._pool_request) + closing = self._pool._assign_requests_to_connections() + + await self._pool._close_connections(closing) diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/http11.py b/venv/lib/python3.12/site-packages/httpcore/_async/http11.py new file mode 100644 index 0000000..e6d6d70 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_async/http11.py @@ -0,0 +1,379 @@ +from __future__ import annotations + +import enum +import logging +import ssl +import time +import types +import typing + +import h11 + +from .._backends.base import AsyncNetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, + WriteError, + map_exceptions, +) +from .._models import Origin, Request, Response +from .._synchronization import AsyncLock, AsyncShieldCancellation +from .._trace import Trace +from .interfaces import AsyncConnectionInterface + +logger = logging.getLogger("httpcore.http11") + + +# A subset of `h11.Event` types supported by `_send_event` +H11SendEvent = typing.Union[ + h11.Request, + h11.Data, + h11.EndOfMessage, +] + + +class HTTPConnectionState(enum.IntEnum): + NEW = 0 + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class AsyncHTTP11Connection(AsyncConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + MAX_INCOMPLETE_EVENT_SIZE = 100 * 1024 + + def __init__( + self, + origin: Origin, + stream: AsyncNetworkStream, + keepalive_expiry: float | None = None, + ) -> None: + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: float | None = keepalive_expiry + self._expire_at: float | None = None + self._state = HTTPConnectionState.NEW + self._state_lock = AsyncLock() + self._request_count = 0 + self._h11_state = h11.Connection( + our_role=h11.CLIENT, + max_incomplete_event_size=self.MAX_INCOMPLETE_EVENT_SIZE, + ) + + async def handle_async_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection " + f"to {self._origin}" + ) + + async with self._state_lock: + if self._state in (HTTPConnectionState.NEW, HTTPConnectionState.IDLE): + self._request_count += 1 + self._state = HTTPConnectionState.ACTIVE + self._expire_at = None + else: + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request} + try: + async with Trace( + "send_request_headers", logger, request, kwargs + ) as trace: + await self._send_request_headers(**kwargs) + async with Trace("send_request_body", logger, request, kwargs) as trace: + await self._send_request_body(**kwargs) + except WriteError: + # If we get a write error while we're writing the request, + # then we supress this error and move on to attempting to + # read the response. Servers can sometimes close the request + # pre-emptively and then respond with a well formed HTTP + # error response. + pass + + async with Trace( + "receive_response_headers", logger, request, kwargs + ) as trace: + ( + http_version, + status, + reason_phrase, + headers, + trailing_data, + ) = await self._receive_response_headers(**kwargs) + trace.return_value = ( + http_version, + status, + reason_phrase, + headers, + ) + + network_stream = self._network_stream + + # CONNECT or Upgrade request + if (status == 101) or ( + (request.method == b"CONNECT") and (200 <= status < 300) + ): + network_stream = AsyncHTTP11UpgradeStream(network_stream, trailing_data) + + return Response( + status=status, + headers=headers, + content=HTTP11ConnectionByteStream(self, request), + extensions={ + "http_version": http_version, + "reason_phrase": reason_phrase, + "network_stream": network_stream, + }, + ) + except BaseException as exc: + with AsyncShieldCancellation(): + async with Trace("response_closed", logger, request) as trace: + await self._response_closed() + raise exc + + # Sending the request... + + async def _send_request_headers(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + with map_exceptions({h11.LocalProtocolError: LocalProtocolError}): + event = h11.Request( + method=request.method, + target=request.url.target, + headers=request.headers, + ) + await self._send_event(event, timeout=timeout) + + async def _send_request_body(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + assert isinstance(request.stream, typing.AsyncIterable) + async for chunk in request.stream: + event = h11.Data(data=chunk) + await self._send_event(event, timeout=timeout) + + await self._send_event(h11.EndOfMessage(), timeout=timeout) + + async def _send_event(self, event: h11.Event, timeout: float | None = None) -> None: + bytes_to_send = self._h11_state.send(event) + if bytes_to_send is not None: + await self._network_stream.write(bytes_to_send, timeout=timeout) + + # Receiving the response... + + async def _receive_response_headers( + self, request: Request + ) -> tuple[bytes, int, bytes, list[tuple[bytes, bytes]], bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = await self._receive_event(timeout=timeout) + if isinstance(event, h11.Response): + break + if ( + isinstance(event, h11.InformationalResponse) + and event.status_code == 101 + ): + break + + http_version = b"HTTP/" + event.http_version + + # h11 version 0.11+ supports a `raw_items` interface to get the + # raw header casing, rather than the enforced lowercase headers. + headers = event.headers.raw_items() + + trailing_data, _ = self._h11_state.trailing_data + + return http_version, event.status_code, event.reason, headers, trailing_data + + async def _receive_response_body( + self, request: Request + ) -> typing.AsyncIterator[bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = await self._receive_event(timeout=timeout) + if isinstance(event, h11.Data): + yield bytes(event.data) + elif isinstance(event, (h11.EndOfMessage, h11.PAUSED)): + break + + async def _receive_event( + self, timeout: float | None = None + ) -> h11.Event | type[h11.PAUSED]: + while True: + with map_exceptions({h11.RemoteProtocolError: RemoteProtocolError}): + event = self._h11_state.next_event() + + if event is h11.NEED_DATA: + data = await self._network_stream.read( + self.READ_NUM_BYTES, timeout=timeout + ) + + # If we feed this case through h11 we'll raise an exception like: + # + # httpcore.RemoteProtocolError: can't handle event type + # ConnectionClosed when role=SERVER and state=SEND_RESPONSE + # + # Which is accurate, but not very informative from an end-user + # perspective. Instead we handle this case distinctly and treat + # it as a ConnectError. + if data == b"" and self._h11_state.their_state == h11.SEND_RESPONSE: + msg = "Server disconnected without sending a response." + raise RemoteProtocolError(msg) + + self._h11_state.receive_data(data) + else: + # mypy fails to narrow the type in the above if statement above + return event # type: ignore[return-value] + + async def _response_closed(self) -> None: + async with self._state_lock: + if ( + self._h11_state.our_state is h11.DONE + and self._h11_state.their_state is h11.DONE + ): + self._state = HTTPConnectionState.IDLE + self._h11_state.start_next_cycle() + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + else: + await self.aclose() + + # Once the connection is no longer required... + + async def aclose(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._state = HTTPConnectionState.CLOSED + await self._network_stream.aclose() + + # The AsyncConnectionInterface methods provide information about the state of + # the connection, allowing for a connection pooling implementation to + # determine when to reuse and when to close the connection... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_available(self) -> bool: + # Note that HTTP/1.1 connections in the "NEW" state are not treated as + # being "available". The control flow which created the connection will + # be able to send an outgoing request, but the connection will not be + # acquired from the connection pool for any other request. + return self._state == HTTPConnectionState.IDLE + + def has_expired(self) -> bool: + now = time.monotonic() + keepalive_expired = self._expire_at is not None and now > self._expire_at + + # If the HTTP connection is idle but the socket is readable, then the + # only valid state is that the socket is about to return b"", indicating + # a server-initiated disconnect. + server_disconnected = ( + self._state == HTTPConnectionState.IDLE + and self._network_stream.get_extra_info("is_readable") + ) + + return keepalive_expired or server_disconnected + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return ( + f"{origin!r}, HTTP/1.1, {self._state.name}, " + f"Request Count: {self._request_count}" + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return ( + f"<{class_name} [{origin!r}, {self._state.name}, " + f"Request Count: {self._request_count}]>" + ) + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + async def __aenter__(self) -> AsyncHTTP11Connection: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + await self.aclose() + + +class HTTP11ConnectionByteStream: + def __init__(self, connection: AsyncHTTP11Connection, request: Request) -> None: + self._connection = connection + self._request = request + self._closed = False + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + kwargs = {"request": self._request} + try: + async with Trace("receive_response_body", logger, self._request, kwargs): + async for chunk in self._connection._receive_response_body(**kwargs): + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with AsyncShieldCancellation(): + await self.aclose() + raise exc + + async def aclose(self) -> None: + if not self._closed: + self._closed = True + async with Trace("response_closed", logger, self._request): + await self._connection._response_closed() + + +class AsyncHTTP11UpgradeStream(AsyncNetworkStream): + def __init__(self, stream: AsyncNetworkStream, leading_data: bytes) -> None: + self._stream = stream + self._leading_data = leading_data + + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + if self._leading_data: + buffer = self._leading_data[:max_bytes] + self._leading_data = self._leading_data[max_bytes:] + return buffer + else: + return await self._stream.read(max_bytes, timeout) + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + await self._stream.write(buffer, timeout) + + async def aclose(self) -> None: + await self._stream.aclose() + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + return await self._stream.start_tls(ssl_context, server_hostname, timeout) + + def get_extra_info(self, info: str) -> typing.Any: + return self._stream.get_extra_info(info) diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/http2.py b/venv/lib/python3.12/site-packages/httpcore/_async/http2.py new file mode 100644 index 0000000..dbd0bee --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_async/http2.py @@ -0,0 +1,592 @@ +from __future__ import annotations + +import enum +import logging +import time +import types +import typing + +import h2.config +import h2.connection +import h2.events +import h2.exceptions +import h2.settings + +from .._backends.base import AsyncNetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, +) +from .._models import Origin, Request, Response +from .._synchronization import AsyncLock, AsyncSemaphore, AsyncShieldCancellation +from .._trace import Trace +from .interfaces import AsyncConnectionInterface + +logger = logging.getLogger("httpcore.http2") + + +def has_body_headers(request: Request) -> bool: + return any( + k.lower() == b"content-length" or k.lower() == b"transfer-encoding" + for k, v in request.headers + ) + + +class HTTPConnectionState(enum.IntEnum): + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class AsyncHTTP2Connection(AsyncConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + CONFIG = h2.config.H2Configuration(validate_inbound_headers=False) + + def __init__( + self, + origin: Origin, + stream: AsyncNetworkStream, + keepalive_expiry: float | None = None, + ): + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: float | None = keepalive_expiry + self._h2_state = h2.connection.H2Connection(config=self.CONFIG) + self._state = HTTPConnectionState.IDLE + self._expire_at: float | None = None + self._request_count = 0 + self._init_lock = AsyncLock() + self._state_lock = AsyncLock() + self._read_lock = AsyncLock() + self._write_lock = AsyncLock() + self._sent_connection_init = False + self._used_all_stream_ids = False + self._connection_error = False + + # Mapping from stream ID to response stream events. + self._events: dict[ + int, + list[ + h2.events.ResponseReceived + | h2.events.DataReceived + | h2.events.StreamEnded + | h2.events.StreamReset, + ], + ] = {} + + # Connection terminated events are stored as state since + # we need to handle them for all streams. + self._connection_terminated: h2.events.ConnectionTerminated | None = None + + self._read_exception: Exception | None = None + self._write_exception: Exception | None = None + + async def handle_async_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + # This cannot occur in normal operation, since the connection pool + # will only send requests on connections that handle them. + # It's in place simply for resilience as a guard against incorrect + # usage, for anyone working directly with httpcore connections. + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection " + f"to {self._origin}" + ) + + async with self._state_lock: + if self._state in (HTTPConnectionState.ACTIVE, HTTPConnectionState.IDLE): + self._request_count += 1 + self._expire_at = None + self._state = HTTPConnectionState.ACTIVE + else: + raise ConnectionNotAvailable() + + async with self._init_lock: + if not self._sent_connection_init: + try: + sci_kwargs = {"request": request} + async with Trace( + "send_connection_init", logger, request, sci_kwargs + ): + await self._send_connection_init(**sci_kwargs) + except BaseException as exc: + with AsyncShieldCancellation(): + await self.aclose() + raise exc + + self._sent_connection_init = True + + # Initially start with just 1 until the remote server provides + # its max_concurrent_streams value + self._max_streams = 1 + + local_settings_max_streams = ( + self._h2_state.local_settings.max_concurrent_streams + ) + self._max_streams_semaphore = AsyncSemaphore(local_settings_max_streams) + + for _ in range(local_settings_max_streams - self._max_streams): + await self._max_streams_semaphore.acquire() + + await self._max_streams_semaphore.acquire() + + try: + stream_id = self._h2_state.get_next_available_stream_id() + self._events[stream_id] = [] + except h2.exceptions.NoAvailableStreamIDError: # pragma: nocover + self._used_all_stream_ids = True + self._request_count -= 1 + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request, "stream_id": stream_id} + async with Trace("send_request_headers", logger, request, kwargs): + await self._send_request_headers(request=request, stream_id=stream_id) + async with Trace("send_request_body", logger, request, kwargs): + await self._send_request_body(request=request, stream_id=stream_id) + async with Trace( + "receive_response_headers", logger, request, kwargs + ) as trace: + status, headers = await self._receive_response( + request=request, stream_id=stream_id + ) + trace.return_value = (status, headers) + + return Response( + status=status, + headers=headers, + content=HTTP2ConnectionByteStream(self, request, stream_id=stream_id), + extensions={ + "http_version": b"HTTP/2", + "network_stream": self._network_stream, + "stream_id": stream_id, + }, + ) + except BaseException as exc: # noqa: PIE786 + with AsyncShieldCancellation(): + kwargs = {"stream_id": stream_id} + async with Trace("response_closed", logger, request, kwargs): + await self._response_closed(stream_id=stream_id) + + if isinstance(exc, h2.exceptions.ProtocolError): + # One case where h2 can raise a protocol error is when a + # closed frame has been seen by the state machine. + # + # This happens when one stream is reading, and encounters + # a GOAWAY event. Other flows of control may then raise + # a protocol error at any point they interact with the 'h2_state'. + # + # In this case we'll have stored the event, and should raise + # it as a RemoteProtocolError. + if self._connection_terminated: # pragma: nocover + raise RemoteProtocolError(self._connection_terminated) + # If h2 raises a protocol error in some other state then we + # must somehow have made a protocol violation. + raise LocalProtocolError(exc) # pragma: nocover + + raise exc + + async def _send_connection_init(self, request: Request) -> None: + """ + The HTTP/2 connection requires some initial setup before we can start + using individual request/response streams on it. + """ + # Need to set these manually here instead of manipulating via + # __setitem__() otherwise the H2Connection will emit SettingsUpdate + # frames in addition to sending the undesired defaults. + self._h2_state.local_settings = h2.settings.Settings( + client=True, + initial_values={ + # Disable PUSH_PROMISE frames from the server since we don't do anything + # with them for now. Maybe when we support caching? + h2.settings.SettingCodes.ENABLE_PUSH: 0, + # These two are taken from h2 for safe defaults + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS: 100, + h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE: 65536, + }, + ) + + # Some websites (*cough* Yahoo *cough*) balk at this setting being + # present in the initial handshake since it's not defined in the original + # RFC despite the RFC mandating ignoring settings you don't know about. + del self._h2_state.local_settings[ + h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL + ] + + self._h2_state.initiate_connection() + self._h2_state.increment_flow_control_window(2**24) + await self._write_outgoing_data(request) + + # Sending the request... + + async def _send_request_headers(self, request: Request, stream_id: int) -> None: + """ + Send the request headers to a given stream ID. + """ + end_stream = not has_body_headers(request) + + # In HTTP/2 the ':authority' pseudo-header is used instead of 'Host'. + # In order to gracefully handle HTTP/1.1 and HTTP/2 we always require + # HTTP/1.1 style headers, and map them appropriately if we end up on + # an HTTP/2 connection. + authority = [v for k, v in request.headers if k.lower() == b"host"][0] + + headers = [ + (b":method", request.method), + (b":authority", authority), + (b":scheme", request.url.scheme), + (b":path", request.url.target), + ] + [ + (k.lower(), v) + for k, v in request.headers + if k.lower() + not in ( + b"host", + b"transfer-encoding", + ) + ] + + self._h2_state.send_headers(stream_id, headers, end_stream=end_stream) + self._h2_state.increment_flow_control_window(2**24, stream_id=stream_id) + await self._write_outgoing_data(request) + + async def _send_request_body(self, request: Request, stream_id: int) -> None: + """ + Iterate over the request body sending it to a given stream ID. + """ + if not has_body_headers(request): + return + + assert isinstance(request.stream, typing.AsyncIterable) + async for data in request.stream: + await self._send_stream_data(request, stream_id, data) + await self._send_end_stream(request, stream_id) + + async def _send_stream_data( + self, request: Request, stream_id: int, data: bytes + ) -> None: + """ + Send a single chunk of data in one or more data frames. + """ + while data: + max_flow = await self._wait_for_outgoing_flow(request, stream_id) + chunk_size = min(len(data), max_flow) + chunk, data = data[:chunk_size], data[chunk_size:] + self._h2_state.send_data(stream_id, chunk) + await self._write_outgoing_data(request) + + async def _send_end_stream(self, request: Request, stream_id: int) -> None: + """ + Send an empty data frame on on a given stream ID with the END_STREAM flag set. + """ + self._h2_state.end_stream(stream_id) + await self._write_outgoing_data(request) + + # Receiving the response... + + async def _receive_response( + self, request: Request, stream_id: int + ) -> tuple[int, list[tuple[bytes, bytes]]]: + """ + Return the response status code and headers for a given stream ID. + """ + while True: + event = await self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.ResponseReceived): + break + + status_code = 200 + headers = [] + assert event.headers is not None + for k, v in event.headers: + if k == b":status": + status_code = int(v.decode("ascii", errors="ignore")) + elif not k.startswith(b":"): + headers.append((k, v)) + + return (status_code, headers) + + async def _receive_response_body( + self, request: Request, stream_id: int + ) -> typing.AsyncIterator[bytes]: + """ + Iterator that returns the bytes of the response body for a given stream ID. + """ + while True: + event = await self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.DataReceived): + assert event.flow_controlled_length is not None + assert event.data is not None + amount = event.flow_controlled_length + self._h2_state.acknowledge_received_data(amount, stream_id) + await self._write_outgoing_data(request) + yield event.data + elif isinstance(event, h2.events.StreamEnded): + break + + async def _receive_stream_event( + self, request: Request, stream_id: int + ) -> h2.events.ResponseReceived | h2.events.DataReceived | h2.events.StreamEnded: + """ + Return the next available event for a given stream ID. + + Will read more data from the network if required. + """ + while not self._events.get(stream_id): + await self._receive_events(request, stream_id) + event = self._events[stream_id].pop(0) + if isinstance(event, h2.events.StreamReset): + raise RemoteProtocolError(event) + return event + + async def _receive_events( + self, request: Request, stream_id: int | None = None + ) -> None: + """ + Read some data from the network until we see one or more events + for a given stream ID. + """ + async with self._read_lock: + if self._connection_terminated is not None: + last_stream_id = self._connection_terminated.last_stream_id + if stream_id and last_stream_id and stream_id > last_stream_id: + self._request_count -= 1 + raise ConnectionNotAvailable() + raise RemoteProtocolError(self._connection_terminated) + + # This conditional is a bit icky. We don't want to block reading if we've + # actually got an event to return for a given stream. We need to do that + # check *within* the atomic read lock. Though it also need to be optional, + # because when we call it from `_wait_for_outgoing_flow` we *do* want to + # block until we've available flow control, event when we have events + # pending for the stream ID we're attempting to send on. + if stream_id is None or not self._events.get(stream_id): + events = await self._read_incoming_data(request) + for event in events: + if isinstance(event, h2.events.RemoteSettingsChanged): + async with Trace( + "receive_remote_settings", logger, request + ) as trace: + await self._receive_remote_settings_change(event) + trace.return_value = event + + elif isinstance( + event, + ( + h2.events.ResponseReceived, + h2.events.DataReceived, + h2.events.StreamEnded, + h2.events.StreamReset, + ), + ): + if event.stream_id in self._events: + self._events[event.stream_id].append(event) + + elif isinstance(event, h2.events.ConnectionTerminated): + self._connection_terminated = event + + await self._write_outgoing_data(request) + + async def _receive_remote_settings_change( + self, event: h2.events.RemoteSettingsChanged + ) -> None: + max_concurrent_streams = event.changed_settings.get( + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS + ) + if max_concurrent_streams: + new_max_streams = min( + max_concurrent_streams.new_value, + self._h2_state.local_settings.max_concurrent_streams, + ) + if new_max_streams and new_max_streams != self._max_streams: + while new_max_streams > self._max_streams: + await self._max_streams_semaphore.release() + self._max_streams += 1 + while new_max_streams < self._max_streams: + await self._max_streams_semaphore.acquire() + self._max_streams -= 1 + + async def _response_closed(self, stream_id: int) -> None: + await self._max_streams_semaphore.release() + del self._events[stream_id] + async with self._state_lock: + if self._connection_terminated and not self._events: + await self.aclose() + + elif self._state == HTTPConnectionState.ACTIVE and not self._events: + self._state = HTTPConnectionState.IDLE + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + if self._used_all_stream_ids: # pragma: nocover + await self.aclose() + + async def aclose(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._h2_state.close_connection() + self._state = HTTPConnectionState.CLOSED + await self._network_stream.aclose() + + # Wrappers around network read/write operations... + + async def _read_incoming_data(self, request: Request) -> list[h2.events.Event]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + if self._read_exception is not None: + raise self._read_exception # pragma: nocover + + try: + data = await self._network_stream.read(self.READ_NUM_BYTES, timeout) + if data == b"": + raise RemoteProtocolError("Server disconnected") + except Exception as exc: + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future reads. + # (For example, this means that a single read timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._read_exception = exc + self._connection_error = True + raise exc + + events: list[h2.events.Event] = self._h2_state.receive_data(data) + + return events + + async def _write_outgoing_data(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + async with self._write_lock: + data_to_send = self._h2_state.data_to_send() + + if self._write_exception is not None: + raise self._write_exception # pragma: nocover + + try: + await self._network_stream.write(data_to_send, timeout) + except Exception as exc: # pragma: nocover + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future write. + # (For example, this means that a single write timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._write_exception = exc + self._connection_error = True + raise exc + + # Flow control... + + async def _wait_for_outgoing_flow(self, request: Request, stream_id: int) -> int: + """ + Returns the maximum allowable outgoing flow for a given stream. + + If the allowable flow is zero, then waits on the network until + WindowUpdated frames have increased the flow rate. + https://tools.ietf.org/html/rfc7540#section-6.9 + """ + local_flow: int = self._h2_state.local_flow_control_window(stream_id) + max_frame_size: int = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + while flow == 0: + await self._receive_events(request) + local_flow = self._h2_state.local_flow_control_window(stream_id) + max_frame_size = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + return flow + + # Interface for connection pooling... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_available(self) -> bool: + return ( + self._state != HTTPConnectionState.CLOSED + and not self._connection_error + and not self._used_all_stream_ids + and not ( + self._h2_state.state_machine.state + == h2.connection.ConnectionState.CLOSED + ) + ) + + def has_expired(self) -> bool: + now = time.monotonic() + return self._expire_at is not None and now > self._expire_at + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return ( + f"{origin!r}, HTTP/2, {self._state.name}, " + f"Request Count: {self._request_count}" + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return ( + f"<{class_name} [{origin!r}, {self._state.name}, " + f"Request Count: {self._request_count}]>" + ) + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + async def __aenter__(self) -> AsyncHTTP2Connection: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + await self.aclose() + + +class HTTP2ConnectionByteStream: + def __init__( + self, connection: AsyncHTTP2Connection, request: Request, stream_id: int + ) -> None: + self._connection = connection + self._request = request + self._stream_id = stream_id + self._closed = False + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + kwargs = {"request": self._request, "stream_id": self._stream_id} + try: + async with Trace("receive_response_body", logger, self._request, kwargs): + async for chunk in self._connection._receive_response_body( + request=self._request, stream_id=self._stream_id + ): + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with AsyncShieldCancellation(): + await self.aclose() + raise exc + + async def aclose(self) -> None: + if not self._closed: + self._closed = True + kwargs = {"stream_id": self._stream_id} + async with Trace("response_closed", logger, self._request, kwargs): + await self._connection._response_closed(stream_id=self._stream_id) diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/http_proxy.py b/venv/lib/python3.12/site-packages/httpcore/_async/http_proxy.py new file mode 100644 index 0000000..cc9d920 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_async/http_proxy.py @@ -0,0 +1,367 @@ +from __future__ import annotations + +import base64 +import logging +import ssl +import typing + +from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend +from .._exceptions import ProxyError +from .._models import ( + URL, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, +) +from .._ssl import default_ssl_context +from .._synchronization import AsyncLock +from .._trace import Trace +from .connection import AsyncHTTPConnection +from .connection_pool import AsyncConnectionPool +from .http11 import AsyncHTTP11Connection +from .interfaces import AsyncConnectionInterface + +ByteOrStr = typing.Union[bytes, str] +HeadersAsSequence = typing.Sequence[typing.Tuple[ByteOrStr, ByteOrStr]] +HeadersAsMapping = typing.Mapping[ByteOrStr, ByteOrStr] + + +logger = logging.getLogger("httpcore.proxy") + + +def merge_headers( + default_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, + override_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, +) -> list[tuple[bytes, bytes]]: + """ + Append default_headers and override_headers, de-duplicating if a key exists + in both cases. + """ + default_headers = [] if default_headers is None else list(default_headers) + override_headers = [] if override_headers is None else list(override_headers) + has_override = set(key.lower() for key, value in override_headers) + default_headers = [ + (key, value) + for key, value in default_headers + if key.lower() not in has_override + ] + return default_headers + override_headers + + +class AsyncHTTPProxy(AsyncConnectionPool): # pragma: nocover + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: URL | bytes | str, + proxy_auth: tuple[bytes | str, bytes | str] | None = None, + proxy_headers: HeadersAsMapping | HeadersAsSequence | None = None, + ssl_context: ssl.SSLContext | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + proxy_auth: Any proxy authentication as a two-tuple of + (username, password). May be either bytes or ascii-only str. + proxy_headers: Any HTTP headers to use for the proxy requests. + For example `{"Proxy-Authorization": "Basic :"}`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + proxy_ssl_context: The same as `ssl_context`, but for a proxy server rather than a remote origin. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + local_address=local_address, + uds=uds, + socket_options=socket_options, + ) + + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if ( + self._proxy_url.scheme == b"http" and proxy_ssl_context is not None + ): # pragma: no cover + raise RuntimeError( + "The `proxy_ssl_context` argument is not allowed for the http scheme" + ) + + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + if proxy_auth is not None: + username = enforce_bytes(proxy_auth[0], name="proxy_auth") + password = enforce_bytes(proxy_auth[1], name="proxy_auth") + userpass = username + b":" + password + authorization = b"Basic " + base64.b64encode(userpass) + self._proxy_headers = [ + (b"Proxy-Authorization", authorization) + ] + self._proxy_headers + + def create_connection(self, origin: Origin) -> AsyncConnectionInterface: + if origin.scheme == b"http": + return AsyncForwardHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + proxy_ssl_context=self._proxy_ssl_context, + ) + return AsyncTunnelHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + ssl_context=self._ssl_context, + proxy_ssl_context=self._proxy_ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class AsyncForwardHTTPConnection(AsyncConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_headers: HeadersAsMapping | HeadersAsSequence | None = None, + keepalive_expiry: float | None = None, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + ) -> None: + self._connection = AsyncHTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._remote_origin = remote_origin + + async def handle_async_request(self, request: Request) -> Response: + headers = merge_headers(self._proxy_headers, request.headers) + url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=bytes(request.url), + ) + proxy_request = Request( + method=request.method, + url=url, + headers=headers, + content=request.stream, + extensions=request.extensions, + ) + return await self._connection.handle_async_request(proxy_request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + async def aclose(self) -> None: + await self._connection.aclose() + + def info(self) -> str: + return self._connection.info() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + +class AsyncTunnelHTTPConnection(AsyncConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + ssl_context: ssl.SSLContext | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + proxy_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + self._connection: AsyncConnectionInterface = AsyncHTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._connect_lock = AsyncLock() + self._connected = False + + async def handle_async_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("connect", None) + + async with self._connect_lock: + if not self._connected: + target = b"%b:%d" % (self._remote_origin.host, self._remote_origin.port) + + connect_url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=target, + ) + connect_headers = merge_headers( + [(b"Host", target), (b"Accept", b"*/*")], self._proxy_headers + ) + connect_request = Request( + method=b"CONNECT", + url=connect_url, + headers=connect_headers, + extensions=request.extensions, + ) + connect_response = await self._connection.handle_async_request( + connect_request + ) + + if connect_response.status < 200 or connect_response.status > 299: + reason_bytes = connect_response.extensions.get("reason_phrase", b"") + reason_str = reason_bytes.decode("ascii", errors="ignore") + msg = "%d %s" % (connect_response.status, reason_str) + await self._connection.aclose() + raise ProxyError(msg) + + stream = connect_response.extensions["network_stream"] + + # Upgrade the stream to SSL + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + async with Trace("start_tls", logger, request, kwargs) as trace: + stream = await stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import AsyncHTTP2Connection + + self._connection = AsyncHTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = AsyncHTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + + self._connected = True + return await self._connection.handle_async_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + async def aclose(self) -> None: + await self._connection.aclose() + + def info(self) -> str: + return self._connection.info() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/interfaces.py b/venv/lib/python3.12/site-packages/httpcore/_async/interfaces.py new file mode 100644 index 0000000..361583b --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_async/interfaces.py @@ -0,0 +1,137 @@ +from __future__ import annotations + +import contextlib +import typing + +from .._models import ( + URL, + Extensions, + HeaderTypes, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, + include_request_headers, +) + + +class AsyncRequestInterface: + async def request( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.AsyncIterator[bytes] | None = None, + extensions: Extensions | None = None, + ) -> Response: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = await self.handle_async_request(request) + try: + await response.aread() + finally: + await response.aclose() + return response + + @contextlib.asynccontextmanager + async def stream( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.AsyncIterator[bytes] | None = None, + extensions: Extensions | None = None, + ) -> typing.AsyncIterator[Response]: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = await self.handle_async_request(request) + try: + yield response + finally: + await response.aclose() + + async def handle_async_request(self, request: Request) -> Response: + raise NotImplementedError() # pragma: nocover + + +class AsyncConnectionInterface(AsyncRequestInterface): + async def aclose(self) -> None: + raise NotImplementedError() # pragma: nocover + + def info(self) -> str: + raise NotImplementedError() # pragma: nocover + + def can_handle_request(self, origin: Origin) -> bool: + raise NotImplementedError() # pragma: nocover + + def is_available(self) -> bool: + """ + Return `True` if the connection is currently able to accept an + outgoing request. + + An HTTP/1.1 connection will only be available if it is currently idle. + + An HTTP/2 connection will be available so long as the stream ID space is + not yet exhausted, and the connection is not in an error state. + + While the connection is being established we may not yet know if it is going + to result in an HTTP/1.1 or HTTP/2 connection. The connection should be + treated as being available, but might ultimately raise `NewConnectionRequired` + required exceptions if multiple requests are attempted over a connection + that ends up being established as HTTP/1.1. + """ + raise NotImplementedError() # pragma: nocover + + def has_expired(self) -> bool: + """ + Return `True` if the connection is in a state where it should be closed. + + This either means that the connection is idle and it has passed the + expiry time on its keep-alive, or that server has sent an EOF. + """ + raise NotImplementedError() # pragma: nocover + + def is_idle(self) -> bool: + """ + Return `True` if the connection is currently idle. + """ + raise NotImplementedError() # pragma: nocover + + def is_closed(self) -> bool: + """ + Return `True` if the connection has been closed. + + Used when a response is closed to determine if the connection may be + returned to the connection pool or not. + """ + raise NotImplementedError() # pragma: nocover diff --git a/venv/lib/python3.12/site-packages/httpcore/_async/socks_proxy.py b/venv/lib/python3.12/site-packages/httpcore/_async/socks_proxy.py new file mode 100644 index 0000000..b363f55 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_async/socks_proxy.py @@ -0,0 +1,341 @@ +from __future__ import annotations + +import logging +import ssl + +import socksio + +from .._backends.auto import AutoBackend +from .._backends.base import AsyncNetworkBackend, AsyncNetworkStream +from .._exceptions import ConnectionNotAvailable, ProxyError +from .._models import URL, Origin, Request, Response, enforce_bytes, enforce_url +from .._ssl import default_ssl_context +from .._synchronization import AsyncLock +from .._trace import Trace +from .connection_pool import AsyncConnectionPool +from .http11 import AsyncHTTP11Connection +from .interfaces import AsyncConnectionInterface + +logger = logging.getLogger("httpcore.socks") + + +AUTH_METHODS = { + b"\x00": "NO AUTHENTICATION REQUIRED", + b"\x01": "GSSAPI", + b"\x02": "USERNAME/PASSWORD", + b"\xff": "NO ACCEPTABLE METHODS", +} + +REPLY_CODES = { + b"\x00": "Succeeded", + b"\x01": "General SOCKS server failure", + b"\x02": "Connection not allowed by ruleset", + b"\x03": "Network unreachable", + b"\x04": "Host unreachable", + b"\x05": "Connection refused", + b"\x06": "TTL expired", + b"\x07": "Command not supported", + b"\x08": "Address type not supported", +} + + +async def _init_socks5_connection( + stream: AsyncNetworkStream, + *, + host: bytes, + port: int, + auth: tuple[bytes, bytes] | None = None, +) -> None: + conn = socksio.socks5.SOCKS5Connection() + + # Auth method request + auth_method = ( + socksio.socks5.SOCKS5AuthMethod.NO_AUTH_REQUIRED + if auth is None + else socksio.socks5.SOCKS5AuthMethod.USERNAME_PASSWORD + ) + conn.send(socksio.socks5.SOCKS5AuthMethodsRequest([auth_method])) + outgoing_bytes = conn.data_to_send() + await stream.write(outgoing_bytes) + + # Auth method response + incoming_bytes = await stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5AuthReply) + if response.method != auth_method: + requested = AUTH_METHODS.get(auth_method, "UNKNOWN") + responded = AUTH_METHODS.get(response.method, "UNKNOWN") + raise ProxyError( + f"Requested {requested} from proxy server, but got {responded}." + ) + + if response.method == socksio.socks5.SOCKS5AuthMethod.USERNAME_PASSWORD: + # Username/password request + assert auth is not None + username, password = auth + conn.send(socksio.socks5.SOCKS5UsernamePasswordRequest(username, password)) + outgoing_bytes = conn.data_to_send() + await stream.write(outgoing_bytes) + + # Username/password response + incoming_bytes = await stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5UsernamePasswordReply) + if not response.success: + raise ProxyError("Invalid username/password") + + # Connect request + conn.send( + socksio.socks5.SOCKS5CommandRequest.from_address( + socksio.socks5.SOCKS5Command.CONNECT, (host, port) + ) + ) + outgoing_bytes = conn.data_to_send() + await stream.write(outgoing_bytes) + + # Connect response + incoming_bytes = await stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5Reply) + if response.reply_code != socksio.socks5.SOCKS5ReplyCode.SUCCEEDED: + reply_code = REPLY_CODES.get(response.reply_code, "UNKOWN") + raise ProxyError(f"Proxy Server could not connect: {reply_code}.") + + +class AsyncSOCKSProxy(AsyncConnectionPool): # pragma: nocover + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: URL | bytes | str, + proxy_auth: tuple[bytes | str, bytes | str] | None = None, + ssl_context: ssl.SSLContext | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + network_backend: AsyncNetworkBackend | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + ) + self._ssl_context = ssl_context + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if proxy_auth is not None: + username, password = proxy_auth + username_bytes = enforce_bytes(username, name="proxy_auth") + password_bytes = enforce_bytes(password, name="proxy_auth") + self._proxy_auth: tuple[bytes, bytes] | None = ( + username_bytes, + password_bytes, + ) + else: + self._proxy_auth = None + + def create_connection(self, origin: Origin) -> AsyncConnectionInterface: + return AsyncSocks5Connection( + proxy_origin=self._proxy_url.origin, + remote_origin=origin, + proxy_auth=self._proxy_auth, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class AsyncSocks5Connection(AsyncConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_auth: tuple[bytes, bytes] | None = None, + ssl_context: ssl.SSLContext | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + network_backend: AsyncNetworkBackend | None = None, + ) -> None: + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._proxy_auth = proxy_auth + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + + self._network_backend: AsyncNetworkBackend = ( + AutoBackend() if network_backend is None else network_backend + ) + self._connect_lock = AsyncLock() + self._connection: AsyncConnectionInterface | None = None + self._connect_failed = False + + async def handle_async_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + async with self._connect_lock: + if self._connection is None: + try: + # Connect to the proxy + kwargs = { + "host": self._proxy_origin.host.decode("ascii"), + "port": self._proxy_origin.port, + "timeout": timeout, + } + async with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = await self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + + # Connect to the remote host using socks5 + kwargs = { + "stream": stream, + "host": self._remote_origin.host.decode("ascii"), + "port": self._remote_origin.port, + "auth": self._proxy_auth, + } + async with Trace( + "setup_socks5_connection", logger, request, kwargs + ) as trace: + await _init_socks5_connection(**kwargs) + trace.return_value = stream + + # Upgrade the stream to SSL + if self._remote_origin.scheme == b"https": + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ( + ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ) + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname + or self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + async with Trace("start_tls", logger, request, kwargs) as trace: + stream = await stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or ( + self._http2 and not self._http1 + ): # pragma: nocover + from .http2 import AsyncHTTP2Connection + + self._connection = AsyncHTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = AsyncHTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except Exception as exc: + self._connect_failed = True + raise exc + elif not self._connection.is_available(): # pragma: nocover + raise ConnectionNotAvailable() + + return await self._connection.handle_async_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + async def aclose(self) -> None: + if self._connection is not None: + await self._connection.aclose() + + def is_available(self) -> bool: + if self._connection is None: # pragma: nocover + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 + and (self._remote_origin.scheme == b"https" or not self._http1) + and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: # pragma: nocover + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/__init__.py b/venv/lib/python3.12/site-packages/httpcore/_backends/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0fecdd7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/anyio.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/anyio.cpython-312.pyc new file mode 100644 index 0000000..1c5d692 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/anyio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/auto.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/auto.cpython-312.pyc new file mode 100644 index 0000000..5fbebf6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/auto.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..0cdf2a8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/mock.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/mock.cpython-312.pyc new file mode 100644 index 0000000..6e53a78 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/mock.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/sync.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/sync.cpython-312.pyc new file mode 100644 index 0000000..0243583 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/sync.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/trio.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/trio.cpython-312.pyc new file mode 100644 index 0000000..c0a2b48 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_backends/__pycache__/trio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/anyio.py b/venv/lib/python3.12/site-packages/httpcore/_backends/anyio.py new file mode 100644 index 0000000..a140095 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_backends/anyio.py @@ -0,0 +1,146 @@ +from __future__ import annotations + +import ssl +import typing + +import anyio + +from .._exceptions import ( + ConnectError, + ConnectTimeout, + ReadError, + ReadTimeout, + WriteError, + WriteTimeout, + map_exceptions, +) +from .._utils import is_socket_readable +from .base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream + + +class AnyIOStream(AsyncNetworkStream): + def __init__(self, stream: anyio.abc.ByteStream) -> None: + self._stream = stream + + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + exc_map = { + TimeoutError: ReadTimeout, + anyio.BrokenResourceError: ReadError, + anyio.ClosedResourceError: ReadError, + anyio.EndOfStream: ReadError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + try: + return await self._stream.receive(max_bytes=max_bytes) + except anyio.EndOfStream: # pragma: nocover + return b"" + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + if not buffer: + return + + exc_map = { + TimeoutError: WriteTimeout, + anyio.BrokenResourceError: WriteError, + anyio.ClosedResourceError: WriteError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + await self._stream.send(item=buffer) + + async def aclose(self) -> None: + await self._stream.aclose() + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + exc_map = { + TimeoutError: ConnectTimeout, + anyio.BrokenResourceError: ConnectError, + anyio.EndOfStream: ConnectError, + ssl.SSLError: ConnectError, + } + with map_exceptions(exc_map): + try: + with anyio.fail_after(timeout): + ssl_stream = await anyio.streams.tls.TLSStream.wrap( + self._stream, + ssl_context=ssl_context, + hostname=server_hostname, + standard_compatible=False, + server_side=False, + ) + except Exception as exc: # pragma: nocover + await self.aclose() + raise exc + return AnyIOStream(ssl_stream) + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object": + return self._stream.extra(anyio.streams.tls.TLSAttribute.ssl_object, None) + if info == "client_addr": + return self._stream.extra(anyio.abc.SocketAttribute.local_address, None) + if info == "server_addr": + return self._stream.extra(anyio.abc.SocketAttribute.remote_address, None) + if info == "socket": + return self._stream.extra(anyio.abc.SocketAttribute.raw_socket, None) + if info == "is_readable": + sock = self._stream.extra(anyio.abc.SocketAttribute.raw_socket, None) + return is_socket_readable(sock) + return None + + +class AnyIOBackend(AsyncNetworkBackend): + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: # pragma: nocover + if socket_options is None: + socket_options = [] + exc_map = { + TimeoutError: ConnectTimeout, + OSError: ConnectError, + anyio.BrokenResourceError: ConnectError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + stream: anyio.abc.ByteStream = await anyio.connect_tcp( + remote_host=host, + remote_port=port, + local_host=local_address, + ) + # By default TCP sockets opened in `asyncio` include TCP_NODELAY. + for option in socket_options: + stream._raw_socket.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return AnyIOStream(stream) + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: # pragma: nocover + if socket_options is None: + socket_options = [] + exc_map = { + TimeoutError: ConnectTimeout, + OSError: ConnectError, + anyio.BrokenResourceError: ConnectError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + stream: anyio.abc.ByteStream = await anyio.connect_unix(path) + for option in socket_options: + stream._raw_socket.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return AnyIOStream(stream) + + async def sleep(self, seconds: float) -> None: + await anyio.sleep(seconds) # pragma: nocover diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/auto.py b/venv/lib/python3.12/site-packages/httpcore/_backends/auto.py new file mode 100644 index 0000000..49f0e69 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_backends/auto.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +import typing + +from .._synchronization import current_async_library +from .base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream + + +class AutoBackend(AsyncNetworkBackend): + async def _init_backend(self) -> None: + if not (hasattr(self, "_backend")): + backend = current_async_library() + if backend == "trio": + from .trio import TrioBackend + + self._backend: AsyncNetworkBackend = TrioBackend() + else: + from .anyio import AnyIOBackend + + self._backend = AnyIOBackend() + + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + await self._init_backend() + return await self._backend.connect_tcp( + host, + port, + timeout=timeout, + local_address=local_address, + socket_options=socket_options, + ) + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: # pragma: nocover + await self._init_backend() + return await self._backend.connect_unix_socket( + path, timeout=timeout, socket_options=socket_options + ) + + async def sleep(self, seconds: float) -> None: # pragma: nocover + await self._init_backend() + return await self._backend.sleep(seconds) diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/base.py b/venv/lib/python3.12/site-packages/httpcore/_backends/base.py new file mode 100644 index 0000000..cf55c8b --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_backends/base.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +import ssl +import time +import typing + +SOCKET_OPTION = typing.Union[ + typing.Tuple[int, int, int], + typing.Tuple[int, int, typing.Union[bytes, bytearray]], + typing.Tuple[int, int, None, int], +] + + +class NetworkStream: + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + raise NotImplementedError() # pragma: nocover + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + raise NotImplementedError() # pragma: nocover + + def close(self) -> None: + raise NotImplementedError() # pragma: nocover + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + raise NotImplementedError() # pragma: nocover + + def get_extra_info(self, info: str) -> typing.Any: + return None # pragma: nocover + + +class NetworkBackend: + def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + raise NotImplementedError() # pragma: nocover + + def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + raise NotImplementedError() # pragma: nocover + + def sleep(self, seconds: float) -> None: + time.sleep(seconds) # pragma: nocover + + +class AsyncNetworkStream: + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + raise NotImplementedError() # pragma: nocover + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + raise NotImplementedError() # pragma: nocover + + async def aclose(self) -> None: + raise NotImplementedError() # pragma: nocover + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + raise NotImplementedError() # pragma: nocover + + def get_extra_info(self, info: str) -> typing.Any: + return None # pragma: nocover + + +class AsyncNetworkBackend: + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + raise NotImplementedError() # pragma: nocover + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + raise NotImplementedError() # pragma: nocover + + async def sleep(self, seconds: float) -> None: + raise NotImplementedError() # pragma: nocover diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/mock.py b/venv/lib/python3.12/site-packages/httpcore/_backends/mock.py new file mode 100644 index 0000000..9b6edca --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_backends/mock.py @@ -0,0 +1,143 @@ +from __future__ import annotations + +import ssl +import typing + +from .._exceptions import ReadError +from .base import ( + SOCKET_OPTION, + AsyncNetworkBackend, + AsyncNetworkStream, + NetworkBackend, + NetworkStream, +) + + +class MockSSLObject: + def __init__(self, http2: bool): + self._http2 = http2 + + def selected_alpn_protocol(self) -> str: + return "h2" if self._http2 else "http/1.1" + + +class MockStream(NetworkStream): + def __init__(self, buffer: list[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + self._closed = False + + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + if self._closed: + raise ReadError("Connection closed") + if not self._buffer: + return b"" + return self._buffer.pop(0) + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + pass + + def close(self) -> None: + self._closed = True + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + return self + + def get_extra_info(self, info: str) -> typing.Any: + return MockSSLObject(http2=self._http2) if info == "ssl_object" else None + + def __repr__(self) -> str: + return "" + + +class MockBackend(NetworkBackend): + def __init__(self, buffer: list[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + + def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + return MockStream(list(self._buffer), http2=self._http2) + + def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + return MockStream(list(self._buffer), http2=self._http2) + + def sleep(self, seconds: float) -> None: + pass + + +class AsyncMockStream(AsyncNetworkStream): + def __init__(self, buffer: list[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + self._closed = False + + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + if self._closed: + raise ReadError("Connection closed") + if not self._buffer: + return b"" + return self._buffer.pop(0) + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + pass + + async def aclose(self) -> None: + self._closed = True + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + return self + + def get_extra_info(self, info: str) -> typing.Any: + return MockSSLObject(http2=self._http2) if info == "ssl_object" else None + + def __repr__(self) -> str: + return "" + + +class AsyncMockBackend(AsyncNetworkBackend): + def __init__(self, buffer: list[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + return AsyncMockStream(list(self._buffer), http2=self._http2) + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + return AsyncMockStream(list(self._buffer), http2=self._http2) + + async def sleep(self, seconds: float) -> None: + pass diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/sync.py b/venv/lib/python3.12/site-packages/httpcore/_backends/sync.py new file mode 100644 index 0000000..4018a09 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_backends/sync.py @@ -0,0 +1,241 @@ +from __future__ import annotations + +import functools +import socket +import ssl +import sys +import typing + +from .._exceptions import ( + ConnectError, + ConnectTimeout, + ExceptionMapping, + ReadError, + ReadTimeout, + WriteError, + WriteTimeout, + map_exceptions, +) +from .._utils import is_socket_readable +from .base import SOCKET_OPTION, NetworkBackend, NetworkStream + + +class TLSinTLSStream(NetworkStream): # pragma: no cover + """ + Because the standard `SSLContext.wrap_socket` method does + not work for `SSLSocket` objects, we need this class + to implement TLS stream using an underlying `SSLObject` + instance in order to support TLS on top of TLS. + """ + + # Defined in RFC 8449 + TLS_RECORD_SIZE = 16384 + + def __init__( + self, + sock: socket.socket, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ): + self._sock = sock + self._incoming = ssl.MemoryBIO() + self._outgoing = ssl.MemoryBIO() + + self.ssl_obj = ssl_context.wrap_bio( + incoming=self._incoming, + outgoing=self._outgoing, + server_hostname=server_hostname, + ) + + self._sock.settimeout(timeout) + self._perform_io(self.ssl_obj.do_handshake) + + def _perform_io( + self, + func: typing.Callable[..., typing.Any], + ) -> typing.Any: + ret = None + + while True: + errno = None + try: + ret = func() + except (ssl.SSLWantReadError, ssl.SSLWantWriteError) as e: + errno = e.errno + + self._sock.sendall(self._outgoing.read()) + + if errno == ssl.SSL_ERROR_WANT_READ: + buf = self._sock.recv(self.TLS_RECORD_SIZE) + + if buf: + self._incoming.write(buf) + else: + self._incoming.write_eof() + if errno is None: + return ret + + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + exc_map: ExceptionMapping = {socket.timeout: ReadTimeout, OSError: ReadError} + with map_exceptions(exc_map): + self._sock.settimeout(timeout) + return typing.cast( + bytes, self._perform_io(functools.partial(self.ssl_obj.read, max_bytes)) + ) + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + exc_map: ExceptionMapping = {socket.timeout: WriteTimeout, OSError: WriteError} + with map_exceptions(exc_map): + self._sock.settimeout(timeout) + while buffer: + nsent = self._perform_io(functools.partial(self.ssl_obj.write, buffer)) + buffer = buffer[nsent:] + + def close(self) -> None: + self._sock.close() + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + raise NotImplementedError() + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object": + return self.ssl_obj + if info == "client_addr": + return self._sock.getsockname() + if info == "server_addr": + return self._sock.getpeername() + if info == "socket": + return self._sock + if info == "is_readable": + return is_socket_readable(self._sock) + return None + + +class SyncStream(NetworkStream): + def __init__(self, sock: socket.socket) -> None: + self._sock = sock + + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + exc_map: ExceptionMapping = {socket.timeout: ReadTimeout, OSError: ReadError} + with map_exceptions(exc_map): + self._sock.settimeout(timeout) + return self._sock.recv(max_bytes) + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + if not buffer: + return + + exc_map: ExceptionMapping = {socket.timeout: WriteTimeout, OSError: WriteError} + with map_exceptions(exc_map): + while buffer: + self._sock.settimeout(timeout) + n = self._sock.send(buffer) + buffer = buffer[n:] + + def close(self) -> None: + self._sock.close() + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + exc_map: ExceptionMapping = { + socket.timeout: ConnectTimeout, + OSError: ConnectError, + } + with map_exceptions(exc_map): + try: + if isinstance(self._sock, ssl.SSLSocket): # pragma: no cover + # If the underlying socket has already been upgraded + # to the TLS layer (i.e. is an instance of SSLSocket), + # we need some additional smarts to support TLS-in-TLS. + return TLSinTLSStream( + self._sock, ssl_context, server_hostname, timeout + ) + else: + self._sock.settimeout(timeout) + sock = ssl_context.wrap_socket( + self._sock, server_hostname=server_hostname + ) + except Exception as exc: # pragma: nocover + self.close() + raise exc + return SyncStream(sock) + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object" and isinstance(self._sock, ssl.SSLSocket): + return self._sock._sslobj # type: ignore + if info == "client_addr": + return self._sock.getsockname() + if info == "server_addr": + return self._sock.getpeername() + if info == "socket": + return self._sock + if info == "is_readable": + return is_socket_readable(self._sock) + return None + + +class SyncBackend(NetworkBackend): + def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + # Note that we automatically include `TCP_NODELAY` + # in addition to any other custom socket options. + if socket_options is None: + socket_options = [] # pragma: no cover + address = (host, port) + source_address = None if local_address is None else (local_address, 0) + exc_map: ExceptionMapping = { + socket.timeout: ConnectTimeout, + OSError: ConnectError, + } + + with map_exceptions(exc_map): + sock = socket.create_connection( + address, + timeout, + source_address=source_address, + ) + for option in socket_options: + sock.setsockopt(*option) # pragma: no cover + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + return SyncStream(sock) + + def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: # pragma: nocover + if sys.platform == "win32": + raise RuntimeError( + "Attempted to connect to a UNIX socket on a Windows system." + ) + if socket_options is None: + socket_options = [] + + exc_map: ExceptionMapping = { + socket.timeout: ConnectTimeout, + OSError: ConnectError, + } + with map_exceptions(exc_map): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + for option in socket_options: + sock.setsockopt(*option) + sock.settimeout(timeout) + sock.connect(path) + return SyncStream(sock) diff --git a/venv/lib/python3.12/site-packages/httpcore/_backends/trio.py b/venv/lib/python3.12/site-packages/httpcore/_backends/trio.py new file mode 100644 index 0000000..6f53f5f --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_backends/trio.py @@ -0,0 +1,159 @@ +from __future__ import annotations + +import ssl +import typing + +import trio + +from .._exceptions import ( + ConnectError, + ConnectTimeout, + ExceptionMapping, + ReadError, + ReadTimeout, + WriteError, + WriteTimeout, + map_exceptions, +) +from .base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream + + +class TrioStream(AsyncNetworkStream): + def __init__(self, stream: trio.abc.Stream) -> None: + self._stream = stream + + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ReadTimeout, + trio.BrokenResourceError: ReadError, + trio.ClosedResourceError: ReadError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + data: bytes = await self._stream.receive_some(max_bytes=max_bytes) + return data + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + if not buffer: + return + + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: WriteTimeout, + trio.BrokenResourceError: WriteError, + trio.ClosedResourceError: WriteError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + await self._stream.send_all(data=buffer) + + async def aclose(self) -> None: + await self._stream.aclose() + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ConnectTimeout, + trio.BrokenResourceError: ConnectError, + } + ssl_stream = trio.SSLStream( + self._stream, + ssl_context=ssl_context, + server_hostname=server_hostname, + https_compatible=True, + server_side=False, + ) + with map_exceptions(exc_map): + try: + with trio.fail_after(timeout_or_inf): + await ssl_stream.do_handshake() + except Exception as exc: # pragma: nocover + await self.aclose() + raise exc + return TrioStream(ssl_stream) + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object" and isinstance(self._stream, trio.SSLStream): + # Type checkers cannot see `_ssl_object` attribute because trio._ssl.SSLStream uses __getattr__/__setattr__. + # Tracked at https://github.com/python-trio/trio/issues/542 + return self._stream._ssl_object # type: ignore[attr-defined] + if info == "client_addr": + return self._get_socket_stream().socket.getsockname() + if info == "server_addr": + return self._get_socket_stream().socket.getpeername() + if info == "socket": + stream = self._stream + while isinstance(stream, trio.SSLStream): + stream = stream.transport_stream + assert isinstance(stream, trio.SocketStream) + return stream.socket + if info == "is_readable": + socket = self.get_extra_info("socket") + return socket.is_readable() + return None + + def _get_socket_stream(self) -> trio.SocketStream: + stream = self._stream + while isinstance(stream, trio.SSLStream): + stream = stream.transport_stream + assert isinstance(stream, trio.SocketStream) + return stream + + +class TrioBackend(AsyncNetworkBackend): + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + # By default for TCP sockets, trio enables TCP_NODELAY. + # https://trio.readthedocs.io/en/stable/reference-io.html#trio.SocketStream + if socket_options is None: + socket_options = [] # pragma: no cover + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ConnectTimeout, + trio.BrokenResourceError: ConnectError, + OSError: ConnectError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + stream: trio.abc.Stream = await trio.open_tcp_stream( + host=host, port=port, local_address=local_address + ) + for option in socket_options: + stream.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return TrioStream(stream) + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: # pragma: nocover + if socket_options is None: + socket_options = [] + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ConnectTimeout, + trio.BrokenResourceError: ConnectError, + OSError: ConnectError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + stream: trio.abc.Stream = await trio.open_unix_socket(path) + for option in socket_options: + stream.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return TrioStream(stream) + + async def sleep(self, seconds: float) -> None: + await trio.sleep(seconds) # pragma: nocover diff --git a/venv/lib/python3.12/site-packages/httpcore/_exceptions.py b/venv/lib/python3.12/site-packages/httpcore/_exceptions.py new file mode 100644 index 0000000..bc28d44 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_exceptions.py @@ -0,0 +1,81 @@ +import contextlib +import typing + +ExceptionMapping = typing.Mapping[typing.Type[Exception], typing.Type[Exception]] + + +@contextlib.contextmanager +def map_exceptions(map: ExceptionMapping) -> typing.Iterator[None]: + try: + yield + except Exception as exc: # noqa: PIE786 + for from_exc, to_exc in map.items(): + if isinstance(exc, from_exc): + raise to_exc(exc) from exc + raise # pragma: nocover + + +class ConnectionNotAvailable(Exception): + pass + + +class ProxyError(Exception): + pass + + +class UnsupportedProtocol(Exception): + pass + + +class ProtocolError(Exception): + pass + + +class RemoteProtocolError(ProtocolError): + pass + + +class LocalProtocolError(ProtocolError): + pass + + +# Timeout errors + + +class TimeoutException(Exception): + pass + + +class PoolTimeout(TimeoutException): + pass + + +class ConnectTimeout(TimeoutException): + pass + + +class ReadTimeout(TimeoutException): + pass + + +class WriteTimeout(TimeoutException): + pass + + +# Network errors + + +class NetworkError(Exception): + pass + + +class ConnectError(NetworkError): + pass + + +class ReadError(NetworkError): + pass + + +class WriteError(NetworkError): + pass diff --git a/venv/lib/python3.12/site-packages/httpcore/_models.py b/venv/lib/python3.12/site-packages/httpcore/_models.py new file mode 100644 index 0000000..8a65f13 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_models.py @@ -0,0 +1,516 @@ +from __future__ import annotations + +import base64 +import ssl +import typing +import urllib.parse + +# Functions for typechecking... + + +ByteOrStr = typing.Union[bytes, str] +HeadersAsSequence = typing.Sequence[typing.Tuple[ByteOrStr, ByteOrStr]] +HeadersAsMapping = typing.Mapping[ByteOrStr, ByteOrStr] +HeaderTypes = typing.Union[HeadersAsSequence, HeadersAsMapping, None] + +Extensions = typing.MutableMapping[str, typing.Any] + + +def enforce_bytes(value: bytes | str, *, name: str) -> bytes: + """ + Any arguments that are ultimately represented as bytes can be specified + either as bytes or as strings. + + However we enforce that any string arguments must only contain characters in + the plain ASCII range. chr(0)...chr(127). If you need to use characters + outside that range then be precise, and use a byte-wise argument. + """ + if isinstance(value, str): + try: + return value.encode("ascii") + except UnicodeEncodeError: + raise TypeError(f"{name} strings may not include unicode characters.") + elif isinstance(value, bytes): + return value + + seen_type = type(value).__name__ + raise TypeError(f"{name} must be bytes or str, but got {seen_type}.") + + +def enforce_url(value: URL | bytes | str, *, name: str) -> URL: + """ + Type check for URL parameters. + """ + if isinstance(value, (bytes, str)): + return URL(value) + elif isinstance(value, URL): + return value + + seen_type = type(value).__name__ + raise TypeError(f"{name} must be a URL, bytes, or str, but got {seen_type}.") + + +def enforce_headers( + value: HeadersAsMapping | HeadersAsSequence | None = None, *, name: str +) -> list[tuple[bytes, bytes]]: + """ + Convienence function that ensure all items in request or response headers + are either bytes or strings in the plain ASCII range. + """ + if value is None: + return [] + elif isinstance(value, typing.Mapping): + return [ + ( + enforce_bytes(k, name="header name"), + enforce_bytes(v, name="header value"), + ) + for k, v in value.items() + ] + elif isinstance(value, typing.Sequence): + return [ + ( + enforce_bytes(k, name="header name"), + enforce_bytes(v, name="header value"), + ) + for k, v in value + ] + + seen_type = type(value).__name__ + raise TypeError( + f"{name} must be a mapping or sequence of two-tuples, but got {seen_type}." + ) + + +def enforce_stream( + value: bytes | typing.Iterable[bytes] | typing.AsyncIterable[bytes] | None, + *, + name: str, +) -> typing.Iterable[bytes] | typing.AsyncIterable[bytes]: + if value is None: + return ByteStream(b"") + elif isinstance(value, bytes): + return ByteStream(value) + return value + + +# * https://tools.ietf.org/html/rfc3986#section-3.2.3 +# * https://url.spec.whatwg.org/#url-miscellaneous +# * https://url.spec.whatwg.org/#scheme-state +DEFAULT_PORTS = { + b"ftp": 21, + b"http": 80, + b"https": 443, + b"ws": 80, + b"wss": 443, +} + + +def include_request_headers( + headers: list[tuple[bytes, bytes]], + *, + url: "URL", + content: None | bytes | typing.Iterable[bytes] | typing.AsyncIterable[bytes], +) -> list[tuple[bytes, bytes]]: + headers_set = set(k.lower() for k, v in headers) + + if b"host" not in headers_set: + default_port = DEFAULT_PORTS.get(url.scheme) + if url.port is None or url.port == default_port: + header_value = url.host + else: + header_value = b"%b:%d" % (url.host, url.port) + headers = [(b"Host", header_value)] + headers + + if ( + content is not None + and b"content-length" not in headers_set + and b"transfer-encoding" not in headers_set + ): + if isinstance(content, bytes): + content_length = str(len(content)).encode("ascii") + headers += [(b"Content-Length", content_length)] + else: + headers += [(b"Transfer-Encoding", b"chunked")] # pragma: nocover + + return headers + + +# Interfaces for byte streams... + + +class ByteStream: + """ + A container for non-streaming content, and that supports both sync and async + stream iteration. + """ + + def __init__(self, content: bytes) -> None: + self._content = content + + def __iter__(self) -> typing.Iterator[bytes]: + yield self._content + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + yield self._content + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{len(self._content)} bytes]>" + + +class Origin: + def __init__(self, scheme: bytes, host: bytes, port: int) -> None: + self.scheme = scheme + self.host = host + self.port = port + + def __eq__(self, other: typing.Any) -> bool: + return ( + isinstance(other, Origin) + and self.scheme == other.scheme + and self.host == other.host + and self.port == other.port + ) + + def __str__(self) -> str: + scheme = self.scheme.decode("ascii") + host = self.host.decode("ascii") + port = str(self.port) + return f"{scheme}://{host}:{port}" + + +class URL: + """ + Represents the URL against which an HTTP request may be made. + + The URL may either be specified as a plain string, for convienence: + + ```python + url = httpcore.URL("https://www.example.com/") + ``` + + Or be constructed with explicitily pre-parsed components: + + ```python + url = httpcore.URL(scheme=b'https', host=b'www.example.com', port=None, target=b'/') + ``` + + Using this second more explicit style allows integrations that are using + `httpcore` to pass through URLs that have already been parsed in order to use + libraries such as `rfc-3986` rather than relying on the stdlib. It also ensures + that URL parsing is treated identically at both the networking level and at any + higher layers of abstraction. + + The four components are important here, as they allow the URL to be precisely + specified in a pre-parsed format. They also allow certain types of request to + be created that could not otherwise be expressed. + + For example, an HTTP request to `http://www.example.com/` forwarded via a proxy + at `http://localhost:8080`... + + ```python + # Constructs an HTTP request with a complete URL as the target: + # GET https://www.example.com/ HTTP/1.1 + url = httpcore.URL( + scheme=b'http', + host=b'localhost', + port=8080, + target=b'https://www.example.com/' + ) + request = httpcore.Request( + method="GET", + url=url + ) + ``` + + Another example is constructing an `OPTIONS *` request... + + ```python + # Constructs an 'OPTIONS *' HTTP request: + # OPTIONS * HTTP/1.1 + url = httpcore.URL(scheme=b'https', host=b'www.example.com', target=b'*') + request = httpcore.Request(method="OPTIONS", url=url) + ``` + + This kind of request is not possible to formulate with a URL string, + because the `/` delimiter is always used to demark the target from the + host/port portion of the URL. + + For convenience, string-like arguments may be specified either as strings or + as bytes. However, once a request is being issue over-the-wire, the URL + components are always ultimately required to be a bytewise representation. + + In order to avoid any ambiguity over character encodings, when strings are used + as arguments, they must be strictly limited to the ASCII range `chr(0)`-`chr(127)`. + If you require a bytewise representation that is outside this range you must + handle the character encoding directly, and pass a bytes instance. + """ + + def __init__( + self, + url: bytes | str = "", + *, + scheme: bytes | str = b"", + host: bytes | str = b"", + port: int | None = None, + target: bytes | str = b"", + ) -> None: + """ + Parameters: + url: The complete URL as a string or bytes. + scheme: The URL scheme as a string or bytes. + Typically either `"http"` or `"https"`. + host: The URL host as a string or bytes. Such as `"www.example.com"`. + port: The port to connect to. Either an integer or `None`. + target: The target of the HTTP request. Such as `"/items?search=red"`. + """ + if url: + parsed = urllib.parse.urlparse(enforce_bytes(url, name="url")) + self.scheme = parsed.scheme + self.host = parsed.hostname or b"" + self.port = parsed.port + self.target = (parsed.path or b"/") + ( + b"?" + parsed.query if parsed.query else b"" + ) + else: + self.scheme = enforce_bytes(scheme, name="scheme") + self.host = enforce_bytes(host, name="host") + self.port = port + self.target = enforce_bytes(target, name="target") + + @property + def origin(self) -> Origin: + default_port = { + b"http": 80, + b"https": 443, + b"ws": 80, + b"wss": 443, + b"socks5": 1080, + b"socks5h": 1080, + }[self.scheme] + return Origin( + scheme=self.scheme, host=self.host, port=self.port or default_port + ) + + def __eq__(self, other: typing.Any) -> bool: + return ( + isinstance(other, URL) + and other.scheme == self.scheme + and other.host == self.host + and other.port == self.port + and other.target == self.target + ) + + def __bytes__(self) -> bytes: + if self.port is None: + return b"%b://%b%b" % (self.scheme, self.host, self.target) + return b"%b://%b:%d%b" % (self.scheme, self.host, self.port, self.target) + + def __repr__(self) -> str: + return ( + f"{self.__class__.__name__}(scheme={self.scheme!r}, " + f"host={self.host!r}, port={self.port!r}, target={self.target!r})" + ) + + +class Request: + """ + An HTTP request. + """ + + def __init__( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes + | typing.Iterable[bytes] + | typing.AsyncIterable[bytes] + | None = None, + extensions: Extensions | None = None, + ) -> None: + """ + Parameters: + method: The HTTP request method, either as a string or bytes. + For example: `GET`. + url: The request URL, either as a `URL` instance, or as a string or bytes. + For example: `"https://www.example.com".` + headers: The HTTP request headers. + content: The content of the request body. + extensions: A dictionary of optional extra information included on + the request. Possible keys include `"timeout"`, and `"trace"`. + """ + self.method: bytes = enforce_bytes(method, name="method") + self.url: URL = enforce_url(url, name="url") + self.headers: list[tuple[bytes, bytes]] = enforce_headers( + headers, name="headers" + ) + self.stream: typing.Iterable[bytes] | typing.AsyncIterable[bytes] = ( + enforce_stream(content, name="content") + ) + self.extensions = {} if extensions is None else extensions + + if "target" in self.extensions: + self.url = URL( + scheme=self.url.scheme, + host=self.url.host, + port=self.url.port, + target=self.extensions["target"], + ) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.method!r}]>" + + +class Response: + """ + An HTTP response. + """ + + def __init__( + self, + status: int, + *, + headers: HeaderTypes = None, + content: bytes + | typing.Iterable[bytes] + | typing.AsyncIterable[bytes] + | None = None, + extensions: Extensions | None = None, + ) -> None: + """ + Parameters: + status: The HTTP status code of the response. For example `200`. + headers: The HTTP response headers. + content: The content of the response body. + extensions: A dictionary of optional extra information included on + the responseself.Possible keys include `"http_version"`, + `"reason_phrase"`, and `"network_stream"`. + """ + self.status: int = status + self.headers: list[tuple[bytes, bytes]] = enforce_headers( + headers, name="headers" + ) + self.stream: typing.Iterable[bytes] | typing.AsyncIterable[bytes] = ( + enforce_stream(content, name="content") + ) + self.extensions = {} if extensions is None else extensions + + self._stream_consumed = False + + @property + def content(self) -> bytes: + if not hasattr(self, "_content"): + if isinstance(self.stream, typing.Iterable): + raise RuntimeError( + "Attempted to access 'response.content' on a streaming response. " + "Call 'response.read()' first." + ) + else: + raise RuntimeError( + "Attempted to access 'response.content' on a streaming response. " + "Call 'await response.aread()' first." + ) + return self._content + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.status}]>" + + # Sync interface... + + def read(self) -> bytes: + if not isinstance(self.stream, typing.Iterable): # pragma: nocover + raise RuntimeError( + "Attempted to read an asynchronous response using 'response.read()'. " + "You should use 'await response.aread()' instead." + ) + if not hasattr(self, "_content"): + self._content = b"".join([part for part in self.iter_stream()]) + return self._content + + def iter_stream(self) -> typing.Iterator[bytes]: + if not isinstance(self.stream, typing.Iterable): # pragma: nocover + raise RuntimeError( + "Attempted to stream an asynchronous response using 'for ... in " + "response.iter_stream()'. " + "You should use 'async for ... in response.aiter_stream()' instead." + ) + if self._stream_consumed: + raise RuntimeError( + "Attempted to call 'for ... in response.iter_stream()' more than once." + ) + self._stream_consumed = True + for chunk in self.stream: + yield chunk + + def close(self) -> None: + if not isinstance(self.stream, typing.Iterable): # pragma: nocover + raise RuntimeError( + "Attempted to close an asynchronous response using 'response.close()'. " + "You should use 'await response.aclose()' instead." + ) + if hasattr(self.stream, "close"): + self.stream.close() + + # Async interface... + + async def aread(self) -> bytes: + if not isinstance(self.stream, typing.AsyncIterable): # pragma: nocover + raise RuntimeError( + "Attempted to read an synchronous response using " + "'await response.aread()'. " + "You should use 'response.read()' instead." + ) + if not hasattr(self, "_content"): + self._content = b"".join([part async for part in self.aiter_stream()]) + return self._content + + async def aiter_stream(self) -> typing.AsyncIterator[bytes]: + if not isinstance(self.stream, typing.AsyncIterable): # pragma: nocover + raise RuntimeError( + "Attempted to stream an synchronous response using 'async for ... in " + "response.aiter_stream()'. " + "You should use 'for ... in response.iter_stream()' instead." + ) + if self._stream_consumed: + raise RuntimeError( + "Attempted to call 'async for ... in response.aiter_stream()' " + "more than once." + ) + self._stream_consumed = True + async for chunk in self.stream: + yield chunk + + async def aclose(self) -> None: + if not isinstance(self.stream, typing.AsyncIterable): # pragma: nocover + raise RuntimeError( + "Attempted to close a synchronous response using " + "'await response.aclose()'. " + "You should use 'response.close()' instead." + ) + if hasattr(self.stream, "aclose"): + await self.stream.aclose() + + +class Proxy: + def __init__( + self, + url: URL | bytes | str, + auth: tuple[bytes | str, bytes | str] | None = None, + headers: HeadersAsMapping | HeadersAsSequence | None = None, + ssl_context: ssl.SSLContext | None = None, + ): + self.url = enforce_url(url, name="url") + self.headers = enforce_headers(headers, name="headers") + self.ssl_context = ssl_context + + if auth is not None: + username = enforce_bytes(auth[0], name="auth") + password = enforce_bytes(auth[1], name="auth") + userpass = username + b":" + password + authorization = b"Basic " + base64.b64encode(userpass) + self.auth: tuple[bytes, bytes] | None = (username, password) + self.headers = [(b"Proxy-Authorization", authorization)] + self.headers + else: + self.auth = None diff --git a/venv/lib/python3.12/site-packages/httpcore/_ssl.py b/venv/lib/python3.12/site-packages/httpcore/_ssl.py new file mode 100644 index 0000000..c99c5a6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_ssl.py @@ -0,0 +1,9 @@ +import ssl + +import certifi + + +def default_ssl_context() -> ssl.SSLContext: + context = ssl.create_default_context() + context.load_verify_locations(certifi.where()) + return context diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/__init__.py b/venv/lib/python3.12/site-packages/httpcore/_sync/__init__.py new file mode 100644 index 0000000..b476d76 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_sync/__init__.py @@ -0,0 +1,39 @@ +from .connection import HTTPConnection +from .connection_pool import ConnectionPool +from .http11 import HTTP11Connection +from .http_proxy import HTTPProxy +from .interfaces import ConnectionInterface + +try: + from .http2 import HTTP2Connection +except ImportError: # pragma: nocover + + class HTTP2Connection: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use http2 support, but the `h2` package is not " + "installed. Use 'pip install httpcore[http2]'." + ) + + +try: + from .socks_proxy import SOCKSProxy +except ImportError: # pragma: nocover + + class SOCKSProxy: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use SOCKS support, but the `socksio` package is not " + "installed. Use 'pip install httpcore[socks]'." + ) + + +__all__ = [ + "HTTPConnection", + "ConnectionPool", + "HTTPProxy", + "HTTP11Connection", + "HTTP2Connection", + "ConnectionInterface", + "SOCKSProxy", +] diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..47a1ef4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/connection.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/connection.cpython-312.pyc new file mode 100644 index 0000000..b795599 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/connection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-312.pyc new file mode 100644 index 0000000..c002681 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http11.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http11.cpython-312.pyc new file mode 100644 index 0000000..fdd1962 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http11.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http2.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http2.cpython-312.pyc new file mode 100644 index 0000000..7c49e8d Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-312.pyc new file mode 100644 index 0000000..41f2f86 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/interfaces.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/interfaces.cpython-312.pyc new file mode 100644 index 0000000..a970b9d Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/interfaces.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/socks_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/socks_proxy.cpython-312.pyc new file mode 100644 index 0000000..52d3294 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore/_sync/__pycache__/socks_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/connection.py b/venv/lib/python3.12/site-packages/httpcore/_sync/connection.py new file mode 100644 index 0000000..363f8be --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_sync/connection.py @@ -0,0 +1,222 @@ +from __future__ import annotations + +import itertools +import logging +import ssl +import types +import typing + +from .._backends.sync import SyncBackend +from .._backends.base import SOCKET_OPTION, NetworkBackend, NetworkStream +from .._exceptions import ConnectError, ConnectTimeout +from .._models import Origin, Request, Response +from .._ssl import default_ssl_context +from .._synchronization import Lock +from .._trace import Trace +from .http11 import HTTP11Connection +from .interfaces import ConnectionInterface + +RETRIES_BACKOFF_FACTOR = 0.5 # 0s, 0.5s, 1s, 2s, 4s, etc. + + +logger = logging.getLogger("httpcore.connection") + + +def exponential_backoff(factor: float) -> typing.Iterator[float]: + """ + Generate a geometric sequence that has a ratio of 2 and starts with 0. + + For example: + - `factor = 2`: `0, 2, 4, 8, 16, 32, 64, ...` + - `factor = 3`: `0, 3, 6, 12, 24, 48, 96, ...` + """ + yield 0 + for n in itertools.count(): + yield factor * 2**n + + +class HTTPConnection(ConnectionInterface): + def __init__( + self, + origin: Origin, + ssl_context: ssl.SSLContext | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + self._origin = origin + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend: NetworkBackend = ( + SyncBackend() if network_backend is None else network_backend + ) + self._connection: ConnectionInterface | None = None + self._connect_failed: bool = False + self._request_lock = Lock() + self._socket_options = socket_options + + def handle_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection to {self._origin}" + ) + + try: + with self._request_lock: + if self._connection is None: + stream = self._connect(request) + + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import HTTP2Connection + + self._connection = HTTP2Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = HTTP11Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except BaseException as exc: + self._connect_failed = True + raise exc + + return self._connection.handle_request(request) + + def _connect(self, request: Request) -> NetworkStream: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + retries_left = self._retries + delays = exponential_backoff(factor=RETRIES_BACKOFF_FACTOR) + + while True: + try: + if self._uds is None: + kwargs = { + "host": self._origin.host.decode("ascii"), + "port": self._origin.port, + "local_address": self._local_address, + "timeout": timeout, + "socket_options": self._socket_options, + } + with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + else: + kwargs = { + "path": self._uds, + "timeout": timeout, + "socket_options": self._socket_options, + } + with Trace( + "connect_unix_socket", logger, request, kwargs + ) as trace: + stream = self._network_backend.connect_unix_socket( + **kwargs + ) + trace.return_value = stream + + if self._origin.scheme in (b"https", b"wss"): + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname + or self._origin.host.decode("ascii"), + "timeout": timeout, + } + with Trace("start_tls", logger, request, kwargs) as trace: + stream = stream.start_tls(**kwargs) + trace.return_value = stream + return stream + except (ConnectError, ConnectTimeout): + if retries_left <= 0: + raise + retries_left -= 1 + delay = next(delays) + with Trace("retry", logger, request, kwargs) as trace: + self._network_backend.sleep(delay) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def close(self) -> None: + if self._connection is not None: + with Trace("close", logger, None, {}): + self._connection.close() + + def is_available(self) -> bool: + if self._connection is None: + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 + and (self._origin.scheme == b"https" or not self._http1) + and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + def __enter__(self) -> HTTPConnection: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self.close() diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py b/venv/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py new file mode 100644 index 0000000..9ccfa53 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py @@ -0,0 +1,420 @@ +from __future__ import annotations + +import ssl +import sys +import types +import typing + +from .._backends.sync import SyncBackend +from .._backends.base import SOCKET_OPTION, NetworkBackend +from .._exceptions import ConnectionNotAvailable, UnsupportedProtocol +from .._models import Origin, Proxy, Request, Response +from .._synchronization import Event, ShieldCancellation, ThreadLock +from .connection import HTTPConnection +from .interfaces import ConnectionInterface, RequestInterface + + +class PoolRequest: + def __init__(self, request: Request) -> None: + self.request = request + self.connection: ConnectionInterface | None = None + self._connection_acquired = Event() + + def assign_to_connection(self, connection: ConnectionInterface | None) -> None: + self.connection = connection + self._connection_acquired.set() + + def clear_connection(self) -> None: + self.connection = None + self._connection_acquired = Event() + + def wait_for_connection( + self, timeout: float | None = None + ) -> ConnectionInterface: + if self.connection is None: + self._connection_acquired.wait(timeout=timeout) + assert self.connection is not None + return self.connection + + def is_queued(self) -> bool: + return self.connection is None + + +class ConnectionPool(RequestInterface): + """ + A connection pool for making HTTP requests. + """ + + def __init__( + self, + ssl_context: ssl.SSLContext | None = None, + proxy: Proxy | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish a + connection. + local_address: Local address to connect from. Can also be used to connect + using a particular address family. Using `local_address="0.0.0.0"` + will connect using an `AF_INET` address (IPv4), while using + `local_address="::"` will connect using an `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + socket_options: Socket options that have to be included + in the TCP socket when the connection was established. + """ + self._ssl_context = ssl_context + self._proxy = proxy + self._max_connections = ( + sys.maxsize if max_connections is None else max_connections + ) + self._max_keepalive_connections = ( + sys.maxsize + if max_keepalive_connections is None + else max_keepalive_connections + ) + self._max_keepalive_connections = min( + self._max_connections, self._max_keepalive_connections + ) + + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend = ( + SyncBackend() if network_backend is None else network_backend + ) + self._socket_options = socket_options + + # The mutable state on a connection pool is the queue of incoming requests, + # and the set of connections that are servicing those requests. + self._connections: list[ConnectionInterface] = [] + self._requests: list[PoolRequest] = [] + + # We only mutate the state of the connection pool within an 'optional_thread_lock' + # context. This holds a threading lock unless we're running in async mode, + # in which case it is a no-op. + self._optional_thread_lock = ThreadLock() + + def create_connection(self, origin: Origin) -> ConnectionInterface: + if self._proxy is not None: + if self._proxy.url.scheme in (b"socks5", b"socks5h"): + from .socks_proxy import Socks5Connection + + return Socks5Connection( + proxy_origin=self._proxy.url.origin, + proxy_auth=self._proxy.auth, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + elif origin.scheme == b"http": + from .http_proxy import ForwardHTTPConnection + + return ForwardHTTPConnection( + proxy_origin=self._proxy.url.origin, + proxy_headers=self._proxy.headers, + proxy_ssl_context=self._proxy.ssl_context, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + ) + from .http_proxy import TunnelHTTPConnection + + return TunnelHTTPConnection( + proxy_origin=self._proxy.url.origin, + proxy_headers=self._proxy.headers, + proxy_ssl_context=self._proxy.ssl_context, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + return HTTPConnection( + origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + retries=self._retries, + local_address=self._local_address, + uds=self._uds, + network_backend=self._network_backend, + socket_options=self._socket_options, + ) + + @property + def connections(self) -> list[ConnectionInterface]: + """ + Return a list of the connections currently in the pool. + + For example: + + ```python + >>> pool.connections + [ + , + , + , + ] + ``` + """ + return list(self._connections) + + def handle_request(self, request: Request) -> Response: + """ + Send an HTTP request, and return an HTTP response. + + This is the core implementation that is called into by `.request()` or `.stream()`. + """ + scheme = request.url.scheme.decode() + if scheme == "": + raise UnsupportedProtocol( + "Request URL is missing an 'http://' or 'https://' protocol." + ) + if scheme not in ("http", "https", "ws", "wss"): + raise UnsupportedProtocol( + f"Request URL has an unsupported protocol '{scheme}://'." + ) + + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("pool", None) + + with self._optional_thread_lock: + # Add the incoming request to our request queue. + pool_request = PoolRequest(request) + self._requests.append(pool_request) + + try: + while True: + with self._optional_thread_lock: + # Assign incoming requests to available connections, + # closing or creating new connections as required. + closing = self._assign_requests_to_connections() + self._close_connections(closing) + + # Wait until this request has an assigned connection. + connection = pool_request.wait_for_connection(timeout=timeout) + + try: + # Send the request on the assigned connection. + response = connection.handle_request( + pool_request.request + ) + except ConnectionNotAvailable: + # In some cases a connection may initially be available to + # handle a request, but then become unavailable. + # + # In this case we clear the connection and try again. + pool_request.clear_connection() + else: + break # pragma: nocover + + except BaseException as exc: + with self._optional_thread_lock: + # For any exception or cancellation we remove the request from + # the queue, and then re-assign requests to connections. + self._requests.remove(pool_request) + closing = self._assign_requests_to_connections() + + self._close_connections(closing) + raise exc from None + + # Return the response. Note that in this case we still have to manage + # the point at which the response is closed. + assert isinstance(response.stream, typing.Iterable) + return Response( + status=response.status, + headers=response.headers, + content=PoolByteStream( + stream=response.stream, pool_request=pool_request, pool=self + ), + extensions=response.extensions, + ) + + def _assign_requests_to_connections(self) -> list[ConnectionInterface]: + """ + Manage the state of the connection pool, assigning incoming + requests to connections as available. + + Called whenever a new request is added or removed from the pool. + + Any closing connections are returned, allowing the I/O for closing + those connections to be handled seperately. + """ + closing_connections = [] + + # First we handle cleaning up any connections that are closed, + # have expired their keep-alive, or surplus idle connections. + for connection in list(self._connections): + if connection.is_closed(): + # log: "removing closed connection" + self._connections.remove(connection) + elif connection.has_expired(): + # log: "closing expired connection" + self._connections.remove(connection) + closing_connections.append(connection) + elif ( + connection.is_idle() + and len([connection.is_idle() for connection in self._connections]) + > self._max_keepalive_connections + ): + # log: "closing idle connection" + self._connections.remove(connection) + closing_connections.append(connection) + + # Assign queued requests to connections. + queued_requests = [request for request in self._requests if request.is_queued()] + for pool_request in queued_requests: + origin = pool_request.request.url.origin + available_connections = [ + connection + for connection in self._connections + if connection.can_handle_request(origin) and connection.is_available() + ] + idle_connections = [ + connection for connection in self._connections if connection.is_idle() + ] + + # There are three cases for how we may be able to handle the request: + # + # 1. There is an existing connection that can handle the request. + # 2. We can create a new connection to handle the request. + # 3. We can close an idle connection and then create a new connection + # to handle the request. + if available_connections: + # log: "reusing existing connection" + connection = available_connections[0] + pool_request.assign_to_connection(connection) + elif len(self._connections) < self._max_connections: + # log: "creating new connection" + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + elif idle_connections: + # log: "closing idle connection" + connection = idle_connections[0] + self._connections.remove(connection) + closing_connections.append(connection) + # log: "creating new connection" + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + + return closing_connections + + def _close_connections(self, closing: list[ConnectionInterface]) -> None: + # Close connections which have been removed from the pool. + with ShieldCancellation(): + for connection in closing: + connection.close() + + def close(self) -> None: + # Explicitly close the connection pool. + # Clears all existing requests and connections. + with self._optional_thread_lock: + closing_connections = list(self._connections) + self._connections = [] + self._close_connections(closing_connections) + + def __enter__(self) -> ConnectionPool: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self.close() + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + with self._optional_thread_lock: + request_is_queued = [request.is_queued() for request in self._requests] + connection_is_idle = [ + connection.is_idle() for connection in self._connections + ] + + num_active_requests = request_is_queued.count(False) + num_queued_requests = request_is_queued.count(True) + num_active_connections = connection_is_idle.count(False) + num_idle_connections = connection_is_idle.count(True) + + requests_info = ( + f"Requests: {num_active_requests} active, {num_queued_requests} queued" + ) + connection_info = ( + f"Connections: {num_active_connections} active, {num_idle_connections} idle" + ) + + return f"<{class_name} [{requests_info} | {connection_info}]>" + + +class PoolByteStream: + def __init__( + self, + stream: typing.Iterable[bytes], + pool_request: PoolRequest, + pool: ConnectionPool, + ) -> None: + self._stream = stream + self._pool_request = pool_request + self._pool = pool + self._closed = False + + def __iter__(self) -> typing.Iterator[bytes]: + try: + for part in self._stream: + yield part + except BaseException as exc: + self.close() + raise exc from None + + def close(self) -> None: + if not self._closed: + self._closed = True + with ShieldCancellation(): + if hasattr(self._stream, "close"): + self._stream.close() + + with self._pool._optional_thread_lock: + self._pool._requests.remove(self._pool_request) + closing = self._pool._assign_requests_to_connections() + + self._pool._close_connections(closing) diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/http11.py b/venv/lib/python3.12/site-packages/httpcore/_sync/http11.py new file mode 100644 index 0000000..ebd3a97 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_sync/http11.py @@ -0,0 +1,379 @@ +from __future__ import annotations + +import enum +import logging +import ssl +import time +import types +import typing + +import h11 + +from .._backends.base import NetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, + WriteError, + map_exceptions, +) +from .._models import Origin, Request, Response +from .._synchronization import Lock, ShieldCancellation +from .._trace import Trace +from .interfaces import ConnectionInterface + +logger = logging.getLogger("httpcore.http11") + + +# A subset of `h11.Event` types supported by `_send_event` +H11SendEvent = typing.Union[ + h11.Request, + h11.Data, + h11.EndOfMessage, +] + + +class HTTPConnectionState(enum.IntEnum): + NEW = 0 + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class HTTP11Connection(ConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + MAX_INCOMPLETE_EVENT_SIZE = 100 * 1024 + + def __init__( + self, + origin: Origin, + stream: NetworkStream, + keepalive_expiry: float | None = None, + ) -> None: + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: float | None = keepalive_expiry + self._expire_at: float | None = None + self._state = HTTPConnectionState.NEW + self._state_lock = Lock() + self._request_count = 0 + self._h11_state = h11.Connection( + our_role=h11.CLIENT, + max_incomplete_event_size=self.MAX_INCOMPLETE_EVENT_SIZE, + ) + + def handle_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection " + f"to {self._origin}" + ) + + with self._state_lock: + if self._state in (HTTPConnectionState.NEW, HTTPConnectionState.IDLE): + self._request_count += 1 + self._state = HTTPConnectionState.ACTIVE + self._expire_at = None + else: + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request} + try: + with Trace( + "send_request_headers", logger, request, kwargs + ) as trace: + self._send_request_headers(**kwargs) + with Trace("send_request_body", logger, request, kwargs) as trace: + self._send_request_body(**kwargs) + except WriteError: + # If we get a write error while we're writing the request, + # then we supress this error and move on to attempting to + # read the response. Servers can sometimes close the request + # pre-emptively and then respond with a well formed HTTP + # error response. + pass + + with Trace( + "receive_response_headers", logger, request, kwargs + ) as trace: + ( + http_version, + status, + reason_phrase, + headers, + trailing_data, + ) = self._receive_response_headers(**kwargs) + trace.return_value = ( + http_version, + status, + reason_phrase, + headers, + ) + + network_stream = self._network_stream + + # CONNECT or Upgrade request + if (status == 101) or ( + (request.method == b"CONNECT") and (200 <= status < 300) + ): + network_stream = HTTP11UpgradeStream(network_stream, trailing_data) + + return Response( + status=status, + headers=headers, + content=HTTP11ConnectionByteStream(self, request), + extensions={ + "http_version": http_version, + "reason_phrase": reason_phrase, + "network_stream": network_stream, + }, + ) + except BaseException as exc: + with ShieldCancellation(): + with Trace("response_closed", logger, request) as trace: + self._response_closed() + raise exc + + # Sending the request... + + def _send_request_headers(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + with map_exceptions({h11.LocalProtocolError: LocalProtocolError}): + event = h11.Request( + method=request.method, + target=request.url.target, + headers=request.headers, + ) + self._send_event(event, timeout=timeout) + + def _send_request_body(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + assert isinstance(request.stream, typing.Iterable) + for chunk in request.stream: + event = h11.Data(data=chunk) + self._send_event(event, timeout=timeout) + + self._send_event(h11.EndOfMessage(), timeout=timeout) + + def _send_event(self, event: h11.Event, timeout: float | None = None) -> None: + bytes_to_send = self._h11_state.send(event) + if bytes_to_send is not None: + self._network_stream.write(bytes_to_send, timeout=timeout) + + # Receiving the response... + + def _receive_response_headers( + self, request: Request + ) -> tuple[bytes, int, bytes, list[tuple[bytes, bytes]], bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = self._receive_event(timeout=timeout) + if isinstance(event, h11.Response): + break + if ( + isinstance(event, h11.InformationalResponse) + and event.status_code == 101 + ): + break + + http_version = b"HTTP/" + event.http_version + + # h11 version 0.11+ supports a `raw_items` interface to get the + # raw header casing, rather than the enforced lowercase headers. + headers = event.headers.raw_items() + + trailing_data, _ = self._h11_state.trailing_data + + return http_version, event.status_code, event.reason, headers, trailing_data + + def _receive_response_body( + self, request: Request + ) -> typing.Iterator[bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = self._receive_event(timeout=timeout) + if isinstance(event, h11.Data): + yield bytes(event.data) + elif isinstance(event, (h11.EndOfMessage, h11.PAUSED)): + break + + def _receive_event( + self, timeout: float | None = None + ) -> h11.Event | type[h11.PAUSED]: + while True: + with map_exceptions({h11.RemoteProtocolError: RemoteProtocolError}): + event = self._h11_state.next_event() + + if event is h11.NEED_DATA: + data = self._network_stream.read( + self.READ_NUM_BYTES, timeout=timeout + ) + + # If we feed this case through h11 we'll raise an exception like: + # + # httpcore.RemoteProtocolError: can't handle event type + # ConnectionClosed when role=SERVER and state=SEND_RESPONSE + # + # Which is accurate, but not very informative from an end-user + # perspective. Instead we handle this case distinctly and treat + # it as a ConnectError. + if data == b"" and self._h11_state.their_state == h11.SEND_RESPONSE: + msg = "Server disconnected without sending a response." + raise RemoteProtocolError(msg) + + self._h11_state.receive_data(data) + else: + # mypy fails to narrow the type in the above if statement above + return event # type: ignore[return-value] + + def _response_closed(self) -> None: + with self._state_lock: + if ( + self._h11_state.our_state is h11.DONE + and self._h11_state.their_state is h11.DONE + ): + self._state = HTTPConnectionState.IDLE + self._h11_state.start_next_cycle() + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + else: + self.close() + + # Once the connection is no longer required... + + def close(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._state = HTTPConnectionState.CLOSED + self._network_stream.close() + + # The ConnectionInterface methods provide information about the state of + # the connection, allowing for a connection pooling implementation to + # determine when to reuse and when to close the connection... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_available(self) -> bool: + # Note that HTTP/1.1 connections in the "NEW" state are not treated as + # being "available". The control flow which created the connection will + # be able to send an outgoing request, but the connection will not be + # acquired from the connection pool for any other request. + return self._state == HTTPConnectionState.IDLE + + def has_expired(self) -> bool: + now = time.monotonic() + keepalive_expired = self._expire_at is not None and now > self._expire_at + + # If the HTTP connection is idle but the socket is readable, then the + # only valid state is that the socket is about to return b"", indicating + # a server-initiated disconnect. + server_disconnected = ( + self._state == HTTPConnectionState.IDLE + and self._network_stream.get_extra_info("is_readable") + ) + + return keepalive_expired or server_disconnected + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return ( + f"{origin!r}, HTTP/1.1, {self._state.name}, " + f"Request Count: {self._request_count}" + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return ( + f"<{class_name} [{origin!r}, {self._state.name}, " + f"Request Count: {self._request_count}]>" + ) + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + def __enter__(self) -> HTTP11Connection: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self.close() + + +class HTTP11ConnectionByteStream: + def __init__(self, connection: HTTP11Connection, request: Request) -> None: + self._connection = connection + self._request = request + self._closed = False + + def __iter__(self) -> typing.Iterator[bytes]: + kwargs = {"request": self._request} + try: + with Trace("receive_response_body", logger, self._request, kwargs): + for chunk in self._connection._receive_response_body(**kwargs): + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with ShieldCancellation(): + self.close() + raise exc + + def close(self) -> None: + if not self._closed: + self._closed = True + with Trace("response_closed", logger, self._request): + self._connection._response_closed() + + +class HTTP11UpgradeStream(NetworkStream): + def __init__(self, stream: NetworkStream, leading_data: bytes) -> None: + self._stream = stream + self._leading_data = leading_data + + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + if self._leading_data: + buffer = self._leading_data[:max_bytes] + self._leading_data = self._leading_data[max_bytes:] + return buffer + else: + return self._stream.read(max_bytes, timeout) + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + self._stream.write(buffer, timeout) + + def close(self) -> None: + self._stream.close() + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + return self._stream.start_tls(ssl_context, server_hostname, timeout) + + def get_extra_info(self, info: str) -> typing.Any: + return self._stream.get_extra_info(info) diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/http2.py b/venv/lib/python3.12/site-packages/httpcore/_sync/http2.py new file mode 100644 index 0000000..ddcc189 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_sync/http2.py @@ -0,0 +1,592 @@ +from __future__ import annotations + +import enum +import logging +import time +import types +import typing + +import h2.config +import h2.connection +import h2.events +import h2.exceptions +import h2.settings + +from .._backends.base import NetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, +) +from .._models import Origin, Request, Response +from .._synchronization import Lock, Semaphore, ShieldCancellation +from .._trace import Trace +from .interfaces import ConnectionInterface + +logger = logging.getLogger("httpcore.http2") + + +def has_body_headers(request: Request) -> bool: + return any( + k.lower() == b"content-length" or k.lower() == b"transfer-encoding" + for k, v in request.headers + ) + + +class HTTPConnectionState(enum.IntEnum): + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class HTTP2Connection(ConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + CONFIG = h2.config.H2Configuration(validate_inbound_headers=False) + + def __init__( + self, + origin: Origin, + stream: NetworkStream, + keepalive_expiry: float | None = None, + ): + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: float | None = keepalive_expiry + self._h2_state = h2.connection.H2Connection(config=self.CONFIG) + self._state = HTTPConnectionState.IDLE + self._expire_at: float | None = None + self._request_count = 0 + self._init_lock = Lock() + self._state_lock = Lock() + self._read_lock = Lock() + self._write_lock = Lock() + self._sent_connection_init = False + self._used_all_stream_ids = False + self._connection_error = False + + # Mapping from stream ID to response stream events. + self._events: dict[ + int, + list[ + h2.events.ResponseReceived + | h2.events.DataReceived + | h2.events.StreamEnded + | h2.events.StreamReset, + ], + ] = {} + + # Connection terminated events are stored as state since + # we need to handle them for all streams. + self._connection_terminated: h2.events.ConnectionTerminated | None = None + + self._read_exception: Exception | None = None + self._write_exception: Exception | None = None + + def handle_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + # This cannot occur in normal operation, since the connection pool + # will only send requests on connections that handle them. + # It's in place simply for resilience as a guard against incorrect + # usage, for anyone working directly with httpcore connections. + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection " + f"to {self._origin}" + ) + + with self._state_lock: + if self._state in (HTTPConnectionState.ACTIVE, HTTPConnectionState.IDLE): + self._request_count += 1 + self._expire_at = None + self._state = HTTPConnectionState.ACTIVE + else: + raise ConnectionNotAvailable() + + with self._init_lock: + if not self._sent_connection_init: + try: + sci_kwargs = {"request": request} + with Trace( + "send_connection_init", logger, request, sci_kwargs + ): + self._send_connection_init(**sci_kwargs) + except BaseException as exc: + with ShieldCancellation(): + self.close() + raise exc + + self._sent_connection_init = True + + # Initially start with just 1 until the remote server provides + # its max_concurrent_streams value + self._max_streams = 1 + + local_settings_max_streams = ( + self._h2_state.local_settings.max_concurrent_streams + ) + self._max_streams_semaphore = Semaphore(local_settings_max_streams) + + for _ in range(local_settings_max_streams - self._max_streams): + self._max_streams_semaphore.acquire() + + self._max_streams_semaphore.acquire() + + try: + stream_id = self._h2_state.get_next_available_stream_id() + self._events[stream_id] = [] + except h2.exceptions.NoAvailableStreamIDError: # pragma: nocover + self._used_all_stream_ids = True + self._request_count -= 1 + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request, "stream_id": stream_id} + with Trace("send_request_headers", logger, request, kwargs): + self._send_request_headers(request=request, stream_id=stream_id) + with Trace("send_request_body", logger, request, kwargs): + self._send_request_body(request=request, stream_id=stream_id) + with Trace( + "receive_response_headers", logger, request, kwargs + ) as trace: + status, headers = self._receive_response( + request=request, stream_id=stream_id + ) + trace.return_value = (status, headers) + + return Response( + status=status, + headers=headers, + content=HTTP2ConnectionByteStream(self, request, stream_id=stream_id), + extensions={ + "http_version": b"HTTP/2", + "network_stream": self._network_stream, + "stream_id": stream_id, + }, + ) + except BaseException as exc: # noqa: PIE786 + with ShieldCancellation(): + kwargs = {"stream_id": stream_id} + with Trace("response_closed", logger, request, kwargs): + self._response_closed(stream_id=stream_id) + + if isinstance(exc, h2.exceptions.ProtocolError): + # One case where h2 can raise a protocol error is when a + # closed frame has been seen by the state machine. + # + # This happens when one stream is reading, and encounters + # a GOAWAY event. Other flows of control may then raise + # a protocol error at any point they interact with the 'h2_state'. + # + # In this case we'll have stored the event, and should raise + # it as a RemoteProtocolError. + if self._connection_terminated: # pragma: nocover + raise RemoteProtocolError(self._connection_terminated) + # If h2 raises a protocol error in some other state then we + # must somehow have made a protocol violation. + raise LocalProtocolError(exc) # pragma: nocover + + raise exc + + def _send_connection_init(self, request: Request) -> None: + """ + The HTTP/2 connection requires some initial setup before we can start + using individual request/response streams on it. + """ + # Need to set these manually here instead of manipulating via + # __setitem__() otherwise the H2Connection will emit SettingsUpdate + # frames in addition to sending the undesired defaults. + self._h2_state.local_settings = h2.settings.Settings( + client=True, + initial_values={ + # Disable PUSH_PROMISE frames from the server since we don't do anything + # with them for now. Maybe when we support caching? + h2.settings.SettingCodes.ENABLE_PUSH: 0, + # These two are taken from h2 for safe defaults + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS: 100, + h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE: 65536, + }, + ) + + # Some websites (*cough* Yahoo *cough*) balk at this setting being + # present in the initial handshake since it's not defined in the original + # RFC despite the RFC mandating ignoring settings you don't know about. + del self._h2_state.local_settings[ + h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL + ] + + self._h2_state.initiate_connection() + self._h2_state.increment_flow_control_window(2**24) + self._write_outgoing_data(request) + + # Sending the request... + + def _send_request_headers(self, request: Request, stream_id: int) -> None: + """ + Send the request headers to a given stream ID. + """ + end_stream = not has_body_headers(request) + + # In HTTP/2 the ':authority' pseudo-header is used instead of 'Host'. + # In order to gracefully handle HTTP/1.1 and HTTP/2 we always require + # HTTP/1.1 style headers, and map them appropriately if we end up on + # an HTTP/2 connection. + authority = [v for k, v in request.headers if k.lower() == b"host"][0] + + headers = [ + (b":method", request.method), + (b":authority", authority), + (b":scheme", request.url.scheme), + (b":path", request.url.target), + ] + [ + (k.lower(), v) + for k, v in request.headers + if k.lower() + not in ( + b"host", + b"transfer-encoding", + ) + ] + + self._h2_state.send_headers(stream_id, headers, end_stream=end_stream) + self._h2_state.increment_flow_control_window(2**24, stream_id=stream_id) + self._write_outgoing_data(request) + + def _send_request_body(self, request: Request, stream_id: int) -> None: + """ + Iterate over the request body sending it to a given stream ID. + """ + if not has_body_headers(request): + return + + assert isinstance(request.stream, typing.Iterable) + for data in request.stream: + self._send_stream_data(request, stream_id, data) + self._send_end_stream(request, stream_id) + + def _send_stream_data( + self, request: Request, stream_id: int, data: bytes + ) -> None: + """ + Send a single chunk of data in one or more data frames. + """ + while data: + max_flow = self._wait_for_outgoing_flow(request, stream_id) + chunk_size = min(len(data), max_flow) + chunk, data = data[:chunk_size], data[chunk_size:] + self._h2_state.send_data(stream_id, chunk) + self._write_outgoing_data(request) + + def _send_end_stream(self, request: Request, stream_id: int) -> None: + """ + Send an empty data frame on on a given stream ID with the END_STREAM flag set. + """ + self._h2_state.end_stream(stream_id) + self._write_outgoing_data(request) + + # Receiving the response... + + def _receive_response( + self, request: Request, stream_id: int + ) -> tuple[int, list[tuple[bytes, bytes]]]: + """ + Return the response status code and headers for a given stream ID. + """ + while True: + event = self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.ResponseReceived): + break + + status_code = 200 + headers = [] + assert event.headers is not None + for k, v in event.headers: + if k == b":status": + status_code = int(v.decode("ascii", errors="ignore")) + elif not k.startswith(b":"): + headers.append((k, v)) + + return (status_code, headers) + + def _receive_response_body( + self, request: Request, stream_id: int + ) -> typing.Iterator[bytes]: + """ + Iterator that returns the bytes of the response body for a given stream ID. + """ + while True: + event = self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.DataReceived): + assert event.flow_controlled_length is not None + assert event.data is not None + amount = event.flow_controlled_length + self._h2_state.acknowledge_received_data(amount, stream_id) + self._write_outgoing_data(request) + yield event.data + elif isinstance(event, h2.events.StreamEnded): + break + + def _receive_stream_event( + self, request: Request, stream_id: int + ) -> h2.events.ResponseReceived | h2.events.DataReceived | h2.events.StreamEnded: + """ + Return the next available event for a given stream ID. + + Will read more data from the network if required. + """ + while not self._events.get(stream_id): + self._receive_events(request, stream_id) + event = self._events[stream_id].pop(0) + if isinstance(event, h2.events.StreamReset): + raise RemoteProtocolError(event) + return event + + def _receive_events( + self, request: Request, stream_id: int | None = None + ) -> None: + """ + Read some data from the network until we see one or more events + for a given stream ID. + """ + with self._read_lock: + if self._connection_terminated is not None: + last_stream_id = self._connection_terminated.last_stream_id + if stream_id and last_stream_id and stream_id > last_stream_id: + self._request_count -= 1 + raise ConnectionNotAvailable() + raise RemoteProtocolError(self._connection_terminated) + + # This conditional is a bit icky. We don't want to block reading if we've + # actually got an event to return for a given stream. We need to do that + # check *within* the atomic read lock. Though it also need to be optional, + # because when we call it from `_wait_for_outgoing_flow` we *do* want to + # block until we've available flow control, event when we have events + # pending for the stream ID we're attempting to send on. + if stream_id is None or not self._events.get(stream_id): + events = self._read_incoming_data(request) + for event in events: + if isinstance(event, h2.events.RemoteSettingsChanged): + with Trace( + "receive_remote_settings", logger, request + ) as trace: + self._receive_remote_settings_change(event) + trace.return_value = event + + elif isinstance( + event, + ( + h2.events.ResponseReceived, + h2.events.DataReceived, + h2.events.StreamEnded, + h2.events.StreamReset, + ), + ): + if event.stream_id in self._events: + self._events[event.stream_id].append(event) + + elif isinstance(event, h2.events.ConnectionTerminated): + self._connection_terminated = event + + self._write_outgoing_data(request) + + def _receive_remote_settings_change( + self, event: h2.events.RemoteSettingsChanged + ) -> None: + max_concurrent_streams = event.changed_settings.get( + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS + ) + if max_concurrent_streams: + new_max_streams = min( + max_concurrent_streams.new_value, + self._h2_state.local_settings.max_concurrent_streams, + ) + if new_max_streams and new_max_streams != self._max_streams: + while new_max_streams > self._max_streams: + self._max_streams_semaphore.release() + self._max_streams += 1 + while new_max_streams < self._max_streams: + self._max_streams_semaphore.acquire() + self._max_streams -= 1 + + def _response_closed(self, stream_id: int) -> None: + self._max_streams_semaphore.release() + del self._events[stream_id] + with self._state_lock: + if self._connection_terminated and not self._events: + self.close() + + elif self._state == HTTPConnectionState.ACTIVE and not self._events: + self._state = HTTPConnectionState.IDLE + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + if self._used_all_stream_ids: # pragma: nocover + self.close() + + def close(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._h2_state.close_connection() + self._state = HTTPConnectionState.CLOSED + self._network_stream.close() + + # Wrappers around network read/write operations... + + def _read_incoming_data(self, request: Request) -> list[h2.events.Event]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + if self._read_exception is not None: + raise self._read_exception # pragma: nocover + + try: + data = self._network_stream.read(self.READ_NUM_BYTES, timeout) + if data == b"": + raise RemoteProtocolError("Server disconnected") + except Exception as exc: + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future reads. + # (For example, this means that a single read timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._read_exception = exc + self._connection_error = True + raise exc + + events: list[h2.events.Event] = self._h2_state.receive_data(data) + + return events + + def _write_outgoing_data(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + with self._write_lock: + data_to_send = self._h2_state.data_to_send() + + if self._write_exception is not None: + raise self._write_exception # pragma: nocover + + try: + self._network_stream.write(data_to_send, timeout) + except Exception as exc: # pragma: nocover + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future write. + # (For example, this means that a single write timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._write_exception = exc + self._connection_error = True + raise exc + + # Flow control... + + def _wait_for_outgoing_flow(self, request: Request, stream_id: int) -> int: + """ + Returns the maximum allowable outgoing flow for a given stream. + + If the allowable flow is zero, then waits on the network until + WindowUpdated frames have increased the flow rate. + https://tools.ietf.org/html/rfc7540#section-6.9 + """ + local_flow: int = self._h2_state.local_flow_control_window(stream_id) + max_frame_size: int = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + while flow == 0: + self._receive_events(request) + local_flow = self._h2_state.local_flow_control_window(stream_id) + max_frame_size = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + return flow + + # Interface for connection pooling... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_available(self) -> bool: + return ( + self._state != HTTPConnectionState.CLOSED + and not self._connection_error + and not self._used_all_stream_ids + and not ( + self._h2_state.state_machine.state + == h2.connection.ConnectionState.CLOSED + ) + ) + + def has_expired(self) -> bool: + now = time.monotonic() + return self._expire_at is not None and now > self._expire_at + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return ( + f"{origin!r}, HTTP/2, {self._state.name}, " + f"Request Count: {self._request_count}" + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return ( + f"<{class_name} [{origin!r}, {self._state.name}, " + f"Request Count: {self._request_count}]>" + ) + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + def __enter__(self) -> HTTP2Connection: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self.close() + + +class HTTP2ConnectionByteStream: + def __init__( + self, connection: HTTP2Connection, request: Request, stream_id: int + ) -> None: + self._connection = connection + self._request = request + self._stream_id = stream_id + self._closed = False + + def __iter__(self) -> typing.Iterator[bytes]: + kwargs = {"request": self._request, "stream_id": self._stream_id} + try: + with Trace("receive_response_body", logger, self._request, kwargs): + for chunk in self._connection._receive_response_body( + request=self._request, stream_id=self._stream_id + ): + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with ShieldCancellation(): + self.close() + raise exc + + def close(self) -> None: + if not self._closed: + self._closed = True + kwargs = {"stream_id": self._stream_id} + with Trace("response_closed", logger, self._request, kwargs): + self._connection._response_closed(stream_id=self._stream_id) diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/http_proxy.py b/venv/lib/python3.12/site-packages/httpcore/_sync/http_proxy.py new file mode 100644 index 0000000..ecca88f --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_sync/http_proxy.py @@ -0,0 +1,367 @@ +from __future__ import annotations + +import base64 +import logging +import ssl +import typing + +from .._backends.base import SOCKET_OPTION, NetworkBackend +from .._exceptions import ProxyError +from .._models import ( + URL, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, +) +from .._ssl import default_ssl_context +from .._synchronization import Lock +from .._trace import Trace +from .connection import HTTPConnection +from .connection_pool import ConnectionPool +from .http11 import HTTP11Connection +from .interfaces import ConnectionInterface + +ByteOrStr = typing.Union[bytes, str] +HeadersAsSequence = typing.Sequence[typing.Tuple[ByteOrStr, ByteOrStr]] +HeadersAsMapping = typing.Mapping[ByteOrStr, ByteOrStr] + + +logger = logging.getLogger("httpcore.proxy") + + +def merge_headers( + default_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, + override_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, +) -> list[tuple[bytes, bytes]]: + """ + Append default_headers and override_headers, de-duplicating if a key exists + in both cases. + """ + default_headers = [] if default_headers is None else list(default_headers) + override_headers = [] if override_headers is None else list(override_headers) + has_override = set(key.lower() for key, value in override_headers) + default_headers = [ + (key, value) + for key, value in default_headers + if key.lower() not in has_override + ] + return default_headers + override_headers + + +class HTTPProxy(ConnectionPool): # pragma: nocover + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: URL | bytes | str, + proxy_auth: tuple[bytes | str, bytes | str] | None = None, + proxy_headers: HeadersAsMapping | HeadersAsSequence | None = None, + ssl_context: ssl.SSLContext | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + proxy_auth: Any proxy authentication as a two-tuple of + (username, password). May be either bytes or ascii-only str. + proxy_headers: Any HTTP headers to use for the proxy requests. + For example `{"Proxy-Authorization": "Basic :"}`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + proxy_ssl_context: The same as `ssl_context`, but for a proxy server rather than a remote origin. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + local_address=local_address, + uds=uds, + socket_options=socket_options, + ) + + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if ( + self._proxy_url.scheme == b"http" and proxy_ssl_context is not None + ): # pragma: no cover + raise RuntimeError( + "The `proxy_ssl_context` argument is not allowed for the http scheme" + ) + + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + if proxy_auth is not None: + username = enforce_bytes(proxy_auth[0], name="proxy_auth") + password = enforce_bytes(proxy_auth[1], name="proxy_auth") + userpass = username + b":" + password + authorization = b"Basic " + base64.b64encode(userpass) + self._proxy_headers = [ + (b"Proxy-Authorization", authorization) + ] + self._proxy_headers + + def create_connection(self, origin: Origin) -> ConnectionInterface: + if origin.scheme == b"http": + return ForwardHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + proxy_ssl_context=self._proxy_ssl_context, + ) + return TunnelHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + ssl_context=self._ssl_context, + proxy_ssl_context=self._proxy_ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class ForwardHTTPConnection(ConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_headers: HeadersAsMapping | HeadersAsSequence | None = None, + keepalive_expiry: float | None = None, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + ) -> None: + self._connection = HTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._remote_origin = remote_origin + + def handle_request(self, request: Request) -> Response: + headers = merge_headers(self._proxy_headers, request.headers) + url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=bytes(request.url), + ) + proxy_request = Request( + method=request.method, + url=url, + headers=headers, + content=request.stream, + extensions=request.extensions, + ) + return self._connection.handle_request(proxy_request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def close(self) -> None: + self._connection.close() + + def info(self) -> str: + return self._connection.info() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + +class TunnelHTTPConnection(ConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + ssl_context: ssl.SSLContext | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + proxy_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + self._connection: ConnectionInterface = HTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._connect_lock = Lock() + self._connected = False + + def handle_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("connect", None) + + with self._connect_lock: + if not self._connected: + target = b"%b:%d" % (self._remote_origin.host, self._remote_origin.port) + + connect_url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=target, + ) + connect_headers = merge_headers( + [(b"Host", target), (b"Accept", b"*/*")], self._proxy_headers + ) + connect_request = Request( + method=b"CONNECT", + url=connect_url, + headers=connect_headers, + extensions=request.extensions, + ) + connect_response = self._connection.handle_request( + connect_request + ) + + if connect_response.status < 200 or connect_response.status > 299: + reason_bytes = connect_response.extensions.get("reason_phrase", b"") + reason_str = reason_bytes.decode("ascii", errors="ignore") + msg = "%d %s" % (connect_response.status, reason_str) + self._connection.close() + raise ProxyError(msg) + + stream = connect_response.extensions["network_stream"] + + # Upgrade the stream to SSL + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + with Trace("start_tls", logger, request, kwargs) as trace: + stream = stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import HTTP2Connection + + self._connection = HTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = HTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + + self._connected = True + return self._connection.handle_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def close(self) -> None: + self._connection.close() + + def info(self) -> str: + return self._connection.info() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/interfaces.py b/venv/lib/python3.12/site-packages/httpcore/_sync/interfaces.py new file mode 100644 index 0000000..e673d4c --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_sync/interfaces.py @@ -0,0 +1,137 @@ +from __future__ import annotations + +import contextlib +import typing + +from .._models import ( + URL, + Extensions, + HeaderTypes, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, + include_request_headers, +) + + +class RequestInterface: + def request( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterator[bytes] | None = None, + extensions: Extensions | None = None, + ) -> Response: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = self.handle_request(request) + try: + response.read() + finally: + response.close() + return response + + @contextlib.contextmanager + def stream( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterator[bytes] | None = None, + extensions: Extensions | None = None, + ) -> typing.Iterator[Response]: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = self.handle_request(request) + try: + yield response + finally: + response.close() + + def handle_request(self, request: Request) -> Response: + raise NotImplementedError() # pragma: nocover + + +class ConnectionInterface(RequestInterface): + def close(self) -> None: + raise NotImplementedError() # pragma: nocover + + def info(self) -> str: + raise NotImplementedError() # pragma: nocover + + def can_handle_request(self, origin: Origin) -> bool: + raise NotImplementedError() # pragma: nocover + + def is_available(self) -> bool: + """ + Return `True` if the connection is currently able to accept an + outgoing request. + + An HTTP/1.1 connection will only be available if it is currently idle. + + An HTTP/2 connection will be available so long as the stream ID space is + not yet exhausted, and the connection is not in an error state. + + While the connection is being established we may not yet know if it is going + to result in an HTTP/1.1 or HTTP/2 connection. The connection should be + treated as being available, but might ultimately raise `NewConnectionRequired` + required exceptions if multiple requests are attempted over a connection + that ends up being established as HTTP/1.1. + """ + raise NotImplementedError() # pragma: nocover + + def has_expired(self) -> bool: + """ + Return `True` if the connection is in a state where it should be closed. + + This either means that the connection is idle and it has passed the + expiry time on its keep-alive, or that server has sent an EOF. + """ + raise NotImplementedError() # pragma: nocover + + def is_idle(self) -> bool: + """ + Return `True` if the connection is currently idle. + """ + raise NotImplementedError() # pragma: nocover + + def is_closed(self) -> bool: + """ + Return `True` if the connection has been closed. + + Used when a response is closed to determine if the connection may be + returned to the connection pool or not. + """ + raise NotImplementedError() # pragma: nocover diff --git a/venv/lib/python3.12/site-packages/httpcore/_sync/socks_proxy.py b/venv/lib/python3.12/site-packages/httpcore/_sync/socks_proxy.py new file mode 100644 index 0000000..0ca96dd --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_sync/socks_proxy.py @@ -0,0 +1,341 @@ +from __future__ import annotations + +import logging +import ssl + +import socksio + +from .._backends.sync import SyncBackend +from .._backends.base import NetworkBackend, NetworkStream +from .._exceptions import ConnectionNotAvailable, ProxyError +from .._models import URL, Origin, Request, Response, enforce_bytes, enforce_url +from .._ssl import default_ssl_context +from .._synchronization import Lock +from .._trace import Trace +from .connection_pool import ConnectionPool +from .http11 import HTTP11Connection +from .interfaces import ConnectionInterface + +logger = logging.getLogger("httpcore.socks") + + +AUTH_METHODS = { + b"\x00": "NO AUTHENTICATION REQUIRED", + b"\x01": "GSSAPI", + b"\x02": "USERNAME/PASSWORD", + b"\xff": "NO ACCEPTABLE METHODS", +} + +REPLY_CODES = { + b"\x00": "Succeeded", + b"\x01": "General SOCKS server failure", + b"\x02": "Connection not allowed by ruleset", + b"\x03": "Network unreachable", + b"\x04": "Host unreachable", + b"\x05": "Connection refused", + b"\x06": "TTL expired", + b"\x07": "Command not supported", + b"\x08": "Address type not supported", +} + + +def _init_socks5_connection( + stream: NetworkStream, + *, + host: bytes, + port: int, + auth: tuple[bytes, bytes] | None = None, +) -> None: + conn = socksio.socks5.SOCKS5Connection() + + # Auth method request + auth_method = ( + socksio.socks5.SOCKS5AuthMethod.NO_AUTH_REQUIRED + if auth is None + else socksio.socks5.SOCKS5AuthMethod.USERNAME_PASSWORD + ) + conn.send(socksio.socks5.SOCKS5AuthMethodsRequest([auth_method])) + outgoing_bytes = conn.data_to_send() + stream.write(outgoing_bytes) + + # Auth method response + incoming_bytes = stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5AuthReply) + if response.method != auth_method: + requested = AUTH_METHODS.get(auth_method, "UNKNOWN") + responded = AUTH_METHODS.get(response.method, "UNKNOWN") + raise ProxyError( + f"Requested {requested} from proxy server, but got {responded}." + ) + + if response.method == socksio.socks5.SOCKS5AuthMethod.USERNAME_PASSWORD: + # Username/password request + assert auth is not None + username, password = auth + conn.send(socksio.socks5.SOCKS5UsernamePasswordRequest(username, password)) + outgoing_bytes = conn.data_to_send() + stream.write(outgoing_bytes) + + # Username/password response + incoming_bytes = stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5UsernamePasswordReply) + if not response.success: + raise ProxyError("Invalid username/password") + + # Connect request + conn.send( + socksio.socks5.SOCKS5CommandRequest.from_address( + socksio.socks5.SOCKS5Command.CONNECT, (host, port) + ) + ) + outgoing_bytes = conn.data_to_send() + stream.write(outgoing_bytes) + + # Connect response + incoming_bytes = stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5Reply) + if response.reply_code != socksio.socks5.SOCKS5ReplyCode.SUCCEEDED: + reply_code = REPLY_CODES.get(response.reply_code, "UNKOWN") + raise ProxyError(f"Proxy Server could not connect: {reply_code}.") + + +class SOCKSProxy(ConnectionPool): # pragma: nocover + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: URL | bytes | str, + proxy_auth: tuple[bytes | str, bytes | str] | None = None, + ssl_context: ssl.SSLContext | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + network_backend: NetworkBackend | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + ) + self._ssl_context = ssl_context + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if proxy_auth is not None: + username, password = proxy_auth + username_bytes = enforce_bytes(username, name="proxy_auth") + password_bytes = enforce_bytes(password, name="proxy_auth") + self._proxy_auth: tuple[bytes, bytes] | None = ( + username_bytes, + password_bytes, + ) + else: + self._proxy_auth = None + + def create_connection(self, origin: Origin) -> ConnectionInterface: + return Socks5Connection( + proxy_origin=self._proxy_url.origin, + remote_origin=origin, + proxy_auth=self._proxy_auth, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class Socks5Connection(ConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_auth: tuple[bytes, bytes] | None = None, + ssl_context: ssl.SSLContext | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + network_backend: NetworkBackend | None = None, + ) -> None: + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._proxy_auth = proxy_auth + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + + self._network_backend: NetworkBackend = ( + SyncBackend() if network_backend is None else network_backend + ) + self._connect_lock = Lock() + self._connection: ConnectionInterface | None = None + self._connect_failed = False + + def handle_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + with self._connect_lock: + if self._connection is None: + try: + # Connect to the proxy + kwargs = { + "host": self._proxy_origin.host.decode("ascii"), + "port": self._proxy_origin.port, + "timeout": timeout, + } + with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + + # Connect to the remote host using socks5 + kwargs = { + "stream": stream, + "host": self._remote_origin.host.decode("ascii"), + "port": self._remote_origin.port, + "auth": self._proxy_auth, + } + with Trace( + "setup_socks5_connection", logger, request, kwargs + ) as trace: + _init_socks5_connection(**kwargs) + trace.return_value = stream + + # Upgrade the stream to SSL + if self._remote_origin.scheme == b"https": + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ( + ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ) + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname + or self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + with Trace("start_tls", logger, request, kwargs) as trace: + stream = stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or ( + self._http2 and not self._http1 + ): # pragma: nocover + from .http2 import HTTP2Connection + + self._connection = HTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = HTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except Exception as exc: + self._connect_failed = True + raise exc + elif not self._connection.is_available(): # pragma: nocover + raise ConnectionNotAvailable() + + return self._connection.handle_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def close(self) -> None: + if self._connection is not None: + self._connection.close() + + def is_available(self) -> bool: + if self._connection is None: # pragma: nocover + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 + and (self._remote_origin.scheme == b"https" or not self._http1) + and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: # pragma: nocover + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/venv/lib/python3.12/site-packages/httpcore/_synchronization.py b/venv/lib/python3.12/site-packages/httpcore/_synchronization.py new file mode 100644 index 0000000..2ecc9e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_synchronization.py @@ -0,0 +1,318 @@ +from __future__ import annotations + +import threading +import types + +from ._exceptions import ExceptionMapping, PoolTimeout, map_exceptions + +# Our async synchronization primatives use either 'anyio' or 'trio' depending +# on if they're running under asyncio or trio. + +try: + import trio +except (ImportError, NotImplementedError): # pragma: nocover + trio = None # type: ignore + +try: + import anyio +except ImportError: # pragma: nocover + anyio = None # type: ignore + + +def current_async_library() -> str: + # Determine if we're running under trio or asyncio. + # See https://sniffio.readthedocs.io/en/latest/ + try: + import sniffio + except ImportError: # pragma: nocover + environment = "asyncio" + else: + environment = sniffio.current_async_library() + + if environment not in ("asyncio", "trio"): # pragma: nocover + raise RuntimeError("Running under an unsupported async environment.") + + if environment == "asyncio" and anyio is None: # pragma: nocover + raise RuntimeError( + "Running with asyncio requires installation of 'httpcore[asyncio]'." + ) + + if environment == "trio" and trio is None: # pragma: nocover + raise RuntimeError( + "Running with trio requires installation of 'httpcore[trio]'." + ) + + return environment + + +class AsyncLock: + """ + This is a standard lock. + + In the sync case `Lock` provides thread locking. + In the async case `AsyncLock` provides async locking. + """ + + def __init__(self) -> None: + self._backend = "" + + def setup(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a lock with the correct implementation. + """ + self._backend = current_async_library() + if self._backend == "trio": + self._trio_lock = trio.Lock() + elif self._backend == "asyncio": + self._anyio_lock = anyio.Lock() + + async def __aenter__(self) -> AsyncLock: + if not self._backend: + self.setup() + + if self._backend == "trio": + await self._trio_lock.acquire() + elif self._backend == "asyncio": + await self._anyio_lock.acquire() + + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + if self._backend == "trio": + self._trio_lock.release() + elif self._backend == "asyncio": + self._anyio_lock.release() + + +class AsyncThreadLock: + """ + This is a threading-only lock for no-I/O contexts. + + In the sync case `ThreadLock` provides thread locking. + In the async case `AsyncThreadLock` is a no-op. + """ + + def __enter__(self) -> AsyncThreadLock: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + pass + + +class AsyncEvent: + def __init__(self) -> None: + self._backend = "" + + def setup(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a lock with the correct implementation. + """ + self._backend = current_async_library() + if self._backend == "trio": + self._trio_event = trio.Event() + elif self._backend == "asyncio": + self._anyio_event = anyio.Event() + + def set(self) -> None: + if not self._backend: + self.setup() + + if self._backend == "trio": + self._trio_event.set() + elif self._backend == "asyncio": + self._anyio_event.set() + + async def wait(self, timeout: float | None = None) -> None: + if not self._backend: + self.setup() + + if self._backend == "trio": + trio_exc_map: ExceptionMapping = {trio.TooSlowError: PoolTimeout} + timeout_or_inf = float("inf") if timeout is None else timeout + with map_exceptions(trio_exc_map): + with trio.fail_after(timeout_or_inf): + await self._trio_event.wait() + elif self._backend == "asyncio": + anyio_exc_map: ExceptionMapping = {TimeoutError: PoolTimeout} + with map_exceptions(anyio_exc_map): + with anyio.fail_after(timeout): + await self._anyio_event.wait() + + +class AsyncSemaphore: + def __init__(self, bound: int) -> None: + self._bound = bound + self._backend = "" + + def setup(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a semaphore with the correct implementation. + """ + self._backend = current_async_library() + if self._backend == "trio": + self._trio_semaphore = trio.Semaphore( + initial_value=self._bound, max_value=self._bound + ) + elif self._backend == "asyncio": + self._anyio_semaphore = anyio.Semaphore( + initial_value=self._bound, max_value=self._bound + ) + + async def acquire(self) -> None: + if not self._backend: + self.setup() + + if self._backend == "trio": + await self._trio_semaphore.acquire() + elif self._backend == "asyncio": + await self._anyio_semaphore.acquire() + + async def release(self) -> None: + if self._backend == "trio": + self._trio_semaphore.release() + elif self._backend == "asyncio": + self._anyio_semaphore.release() + + +class AsyncShieldCancellation: + # For certain portions of our codebase where we're dealing with + # closing connections during exception handling we want to shield + # the operation from being cancelled. + # + # with AsyncShieldCancellation(): + # ... # clean-up operations, shielded from cancellation. + + def __init__(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a shielded scope with the correct implementation. + """ + self._backend = current_async_library() + + if self._backend == "trio": + self._trio_shield = trio.CancelScope(shield=True) + elif self._backend == "asyncio": + self._anyio_shield = anyio.CancelScope(shield=True) + + def __enter__(self) -> AsyncShieldCancellation: + if self._backend == "trio": + self._trio_shield.__enter__() + elif self._backend == "asyncio": + self._anyio_shield.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + if self._backend == "trio": + self._trio_shield.__exit__(exc_type, exc_value, traceback) + elif self._backend == "asyncio": + self._anyio_shield.__exit__(exc_type, exc_value, traceback) + + +# Our thread-based synchronization primitives... + + +class Lock: + """ + This is a standard lock. + + In the sync case `Lock` provides thread locking. + In the async case `AsyncLock` provides async locking. + """ + + def __init__(self) -> None: + self._lock = threading.Lock() + + def __enter__(self) -> Lock: + self._lock.acquire() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self._lock.release() + + +class ThreadLock: + """ + This is a threading-only lock for no-I/O contexts. + + In the sync case `ThreadLock` provides thread locking. + In the async case `AsyncThreadLock` is a no-op. + """ + + def __init__(self) -> None: + self._lock = threading.Lock() + + def __enter__(self) -> ThreadLock: + self._lock.acquire() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self._lock.release() + + +class Event: + def __init__(self) -> None: + self._event = threading.Event() + + def set(self) -> None: + self._event.set() + + def wait(self, timeout: float | None = None) -> None: + if timeout == float("inf"): # pragma: no cover + timeout = None + if not self._event.wait(timeout=timeout): + raise PoolTimeout() # pragma: nocover + + +class Semaphore: + def __init__(self, bound: int) -> None: + self._semaphore = threading.Semaphore(value=bound) + + def acquire(self) -> None: + self._semaphore.acquire() + + def release(self) -> None: + self._semaphore.release() + + +class ShieldCancellation: + # Thread-synchronous codebases don't support cancellation semantics. + # We have this class because we need to mirror the async and sync + # cases within our package, but it's just a no-op. + def __enter__(self) -> ShieldCancellation: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + pass diff --git a/venv/lib/python3.12/site-packages/httpcore/_trace.py b/venv/lib/python3.12/site-packages/httpcore/_trace.py new file mode 100644 index 0000000..5f1cd7c --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_trace.py @@ -0,0 +1,107 @@ +from __future__ import annotations + +import inspect +import logging +import types +import typing + +from ._models import Request + + +class Trace: + def __init__( + self, + name: str, + logger: logging.Logger, + request: Request | None = None, + kwargs: dict[str, typing.Any] | None = None, + ) -> None: + self.name = name + self.logger = logger + self.trace_extension = ( + None if request is None else request.extensions.get("trace") + ) + self.debug = self.logger.isEnabledFor(logging.DEBUG) + self.kwargs = kwargs or {} + self.return_value: typing.Any = None + self.should_trace = self.debug or self.trace_extension is not None + self.prefix = self.logger.name.split(".")[-1] + + def trace(self, name: str, info: dict[str, typing.Any]) -> None: + if self.trace_extension is not None: + prefix_and_name = f"{self.prefix}.{name}" + ret = self.trace_extension(prefix_and_name, info) + if inspect.iscoroutine(ret): # pragma: no cover + raise TypeError( + "If you are using a synchronous interface, " + "the callback of the `trace` extension should " + "be a normal function instead of an asynchronous function." + ) + + if self.debug: + if not info or "return_value" in info and info["return_value"] is None: + message = name + else: + args = " ".join([f"{key}={value!r}" for key, value in info.items()]) + message = f"{name} {args}" + self.logger.debug(message) + + def __enter__(self) -> Trace: + if self.should_trace: + info = self.kwargs + self.trace(f"{self.name}.started", info) + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + if self.should_trace: + if exc_value is None: + info = {"return_value": self.return_value} + self.trace(f"{self.name}.complete", info) + else: + info = {"exception": exc_value} + self.trace(f"{self.name}.failed", info) + + async def atrace(self, name: str, info: dict[str, typing.Any]) -> None: + if self.trace_extension is not None: + prefix_and_name = f"{self.prefix}.{name}" + coro = self.trace_extension(prefix_and_name, info) + if not inspect.iscoroutine(coro): # pragma: no cover + raise TypeError( + "If you're using an asynchronous interface, " + "the callback of the `trace` extension should " + "be an asynchronous function rather than a normal function." + ) + await coro + + if self.debug: + if not info or "return_value" in info and info["return_value"] is None: + message = name + else: + args = " ".join([f"{key}={value!r}" for key, value in info.items()]) + message = f"{name} {args}" + self.logger.debug(message) + + async def __aenter__(self) -> Trace: + if self.should_trace: + info = self.kwargs + await self.atrace(f"{self.name}.started", info) + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + if self.should_trace: + if exc_value is None: + info = {"return_value": self.return_value} + await self.atrace(f"{self.name}.complete", info) + else: + info = {"exception": exc_value} + await self.atrace(f"{self.name}.failed", info) diff --git a/venv/lib/python3.12/site-packages/httpcore/_utils.py b/venv/lib/python3.12/site-packages/httpcore/_utils.py new file mode 100644 index 0000000..c44ff93 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore/_utils.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +import select +import socket +import sys + + +def is_socket_readable(sock: socket.socket | None) -> bool: + """ + Return whether a socket, as identifed by its file descriptor, is readable. + "A socket is readable" means that the read buffer isn't empty, i.e. that calling + .recv() on it would immediately return some data. + """ + # NOTE: we want check for readability without actually attempting to read, because + # we don't want to block forever if it's not readable. + + # In the case that the socket no longer exists, or cannot return a file + # descriptor, we treat it as being readable, as if it the next read operation + # on it is ready to return the terminating `b""`. + sock_fd = None if sock is None else sock.fileno() + if sock_fd is None or sock_fd < 0: # pragma: nocover + return True + + # The implementation below was stolen from: + # https://github.com/python-trio/trio/blob/20ee2b1b7376db637435d80e266212a35837ddcc/trio/_socket.py#L471-L478 + # See also: https://github.com/encode/httpcore/pull/193#issuecomment-703129316 + + # Use select.select on Windows, and when poll is unavailable and select.poll + # everywhere else. (E.g. When eventlet is in use. See #327) + if ( + sys.platform == "win32" or getattr(select, "poll", None) is None + ): # pragma: nocover + rready, _, _ = select.select([sock_fd], [], [], 0) + return bool(rready) + p = select.poll() + p.register(sock_fd, select.POLLIN) + return bool(p.poll(0)) diff --git a/venv/lib/python3.12/site-packages/httpcore/py.typed b/venv/lib/python3.12/site-packages/httpcore/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/METADATA b/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/METADATA new file mode 100644 index 0000000..0efc06e --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/METADATA @@ -0,0 +1,758 @@ +Metadata-Version: 2.5 +Name: httpcore2 +Version: 2.13.1 +Summary: A minimal low-level HTTP client. +Project-URL: Changelog, https://github.com/pydantic/httpx2/blob/main/src/httpcore2/CHANGELOG.md +Project-URL: Homepage, https://github.com/pydantic/httpx2 +Project-URL: Source, https://github.com/pydantic/httpx2/blob/main/src/httpcore2 +Author-email: Tom Christie +Maintainer-email: "Pydantic Services Inc." +License-Expression: BSD-3-Clause +License-File: LICENSE.md +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: AnyIO +Classifier: Framework :: AsyncIO +Classifier: Framework :: Trio +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: 3.15 +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.10 +Requires-Dist: h11>=0.16 +Requires-Dist: truststore>=0.10 +Provides-Extra: asyncio +Requires-Dist: anyio<5.0,>=4.5.0; extra == 'asyncio' +Provides-Extra: http2 +Requires-Dist: h2<5,>=3; extra == 'http2' +Provides-Extra: socks +Requires-Dist: socksio==1.*; extra == 'socks' +Provides-Extra: trio +Requires-Dist: trio<1.0,>=0.34.0; extra == 'trio' +Description-Content-Type: text/markdown + +# HTTPCore 2 + +[![Test Suite](https://github.com/pydantic/httpx2/workflows/Test%20Suite/badge.svg)](https://github.com/pydantic/httpx2/actions) +[![Package version](https://badge.fury.io/py/httpcore2.svg)](https://pypi.org/project/httpcore2/) + +> *Do one thing, and do it well.* + +The HTTPCore 2 package provides a minimal low-level HTTP client, which does +one thing only. Sending HTTP requests. + +It does not provide any high level model abstractions over the API, +does not handle redirects, multipart uploads, building authentication headers, +transparent HTTP caching, URL parsing, session cookie handling, +content or charset decoding, handling JSON, environment based configuration +defaults, or any of that Jazz. + +Some things HTTPCore 2 does do: + +* Sending HTTP requests. +* Thread-safe / task-safe connection pooling. +* HTTP(S) proxy & SOCKS proxy support. +* Supports HTTP/1.1 and HTTP/2. +* Provides both sync and async interfaces. +* Async backend support for `asyncio` and `trio`. + +## Installation + +For HTTP/1.1 only support, install with: + +```shell +pip install httpcore2 +``` + +There are also a number of optional extras available... + +```shell +pip install 'httpcore2[asyncio,trio,http2,socks]' +``` + +## Sending requests + +Send an HTTP request: + +```python +import httpcore2 + +response = httpcore2.request("GET", "https://www.example.com/") + +print(response) +# +print(response.status) +# 200 +print(response.headers) +# [(b'Accept-Ranges', b'bytes'), (b'Age', b'557328'), (b'Cache-Control', b'max-age=604800'), ...] +print(response.content) +# b'\n\n\nExample Domain\n\n\n ...' +``` + +The top-level `httpcore2.request()` function is provided for convenience. In practice whenever you're working with `httpcore2` you'll want to use the connection pooling functionality that it provides. + +```python +import httpcore2 + +http = httpcore2.ConnectionPool() +response = http.request("GET", "https://www.example.com/") +``` + +Once you're ready to get going, [head over to the documentation](https://github.com/pydantic/httpx2/tree/main/src/httpcore2/docs). + +## Motivation + +You *probably* don't want to be using HTTPCore 2 directly. It might make sense if +you're writing something like a proxy service in Python, and you just want +something at the lowest possible level, but more typically you'll want to use +a higher level client library, such as `httpx2`. + +The motivation for `httpcore2` is: + +* To provide a reusable low-level client library, that other packages can then build on top of. +* To provide a *really clear interface split* between the networking code and client logic, + so that each is easier to understand and reason about in isolation. + +## Dependencies + +The `httpcore2` package has the following dependencies... + +* `h11` +* `truststore` + +And the following optional extras... + +* `anyio` - Required by `pip install 'httpcore2[asyncio]'`. +* `trio` - Required by `pip install 'httpcore2[trio]'`. +* `h2` - Required by `pip install 'httpcore2[http2]'`. +* `socksio` - Required by `pip install 'httpcore2[socks]'`. + +## Versioning + +We use [SEMVER for our versioning policy](https://semver.org/). + +For changes between package versions please see our [project changelog](CHANGELOG.md). + +We recommend pinning your requirements either the most current major version, or a more specific version range: + +```shell +pip install 'httpcore2==2.*' +``` +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## 2.13.1 (September 23rd, 2026) + +### Fixed + +* Honor the `sni_hostname` extension for TLS inside HTTP proxy tunnels without applying it to the proxy's TLS connection. + ([#1223](https://github.com/pydantic/httpx2/pull/1223)) +* Prefer HTTP/2 during TLS protocol negotiation when enabled, and stop advertising HTTP/1.1 when it is disabled. + ([#1155](https://github.com/pydantic/httpx2/pull/1155)) + +## 2.13.0 (September 14th, 2026) + +### Changed + +* Require Trio 0.34.0 or later for the `trio` extra. + ([#1179](https://github.com/pydantic/httpx2/pull/1179)) + +### Fixed + +* Avoid nested async generator finalization errors when streamed responses are abandoned early. + ([#1204](https://github.com/pydantic/httpx2/pull/1204)) + +## 2.12.0 (August 18th, 2026) + +No changes since `2.11.0`. Version bumped to stay in lockstep with `httpx2`. + +## 2.11.0 (August 18th, 2026) + +### Changed + +* Cache sniffio availability instead of importing it on every synchronization call. + ([#1132](https://github.com/pydantic/httpx2/pull/1132)) + +## 2.10.0 (August 9th, 2026) + +### Added + +* Add support for Python 3.15. ([#1090](https://github.com/pydantic/httpx2/pull/1090)) + +### Changed + +* Avoid quadratic copying when sending large HTTP/2 request bodies. ([#1127](https://github.com/pydantic/httpx2/pull/1127)) + +### Fixed + +* Propagate the original exception instead of raising `KeyError` when an HTTP/2 stream fails. ([#1093](https://github.com/pydantic/httpx2/pull/1093)) +* Start TLS for the `wss` scheme in SOCKS5 proxy connections. ([#1104](https://github.com/pydantic/httpx2/pull/1104)) + +## 2.9.1 (July 24th, 2026) + +No changes since `2.9.0`. Version bumped to stay in lockstep with `httpx2`. + +## 2.9.0 (July 23rd, 2026) + +No changes since `2.8.0`. Version bumped to stay in lockstep with `httpx2`. + +## 2.8.0 (July 23rd, 2026) + +### Changed + +* Assign each released connection to a single queued request, eliminating multi-assignment churn in the connection pool. ([#1075](https://github.com/pydantic/httpx2/pull/1075)) + +## 2.7.0 (July 14th, 2026) + +No changes since `2.6.0`. Version bumped to stay in lockstep with `httpx2`. + +## 2.6.0 (July 14th, 2026) + +### Fixed + +* Clean up garbage connections on cancellation in `AsyncConnectionPool`. ([#983](https://github.com/pydantic/httpx2/pull/983)) +* Read `_expire_at` only once in `has_expired()`. ([#1045](https://github.com/pydantic/httpx2/pull/1045)) + +## 2.5.0 (June 25th, 2026) + +### Fixed + +* Propagate the timeout through the SOCKS5 handshake. ([#1009](https://github.com/pydantic/httpx2/pull/1009)) + +## 2.4.0 (June 11th, 2026) + +### Fixed + +* Move HTTP/2 stream events cleanup inside `_state_lock`. ([#1013](https://github.com/pydantic/httpx2/pull/1013)) +* Release the HTTP/2 semaphore permit on `NoAvailableStreamIDError`. ([#1012](https://github.com/pydantic/httpx2/pull/1012)) +* Use `RLock` instead of `Lock` to prevent a thread deadlock. ([#1008](https://github.com/pydantic/httpx2/pull/1008)) + +## 2.3.0 (June 1st, 2026) + +### Changed + +* Use `truststore` instead of `certifi` for default SSL verification, loading the operating system's trust store. ([#1002](https://github.com/pydantic/httpx2/pull/1002)) +* Rewrite `_assign_requests_to_connections` as a single-pass loop. ([#974](https://github.com/pydantic/httpx2/pull/974)) +* Use `anyio`'s `fast_acquire` for `Lock` and `Semaphore`. ([#970](https://github.com/pydantic/httpx2/pull/970)) +* Use `memoryview` in `write()` to avoid copies. ([#954](https://github.com/pydantic/httpx2/pull/954)) + +## 2.2.0 (May 16th, 2026) + +No changes since `2.1.0`. Version bumped to stay in lockstep with `httpx2`. + +## 2.1.0 (May 15th, 2026) + +### Removed + +* Drop support for Python 3.8 and 3.9. ([#208](https://github.com/pydantic/httpx2/pull/208)) + +### Added + +* Add support for Python 3.14. ([#208](https://github.com/pydantic/httpx2/pull/208)) +* Bundle `LICENSE.md` in the sdist. ([#938](https://github.com/pydantic/httpx2/pull/938)) + +### Fixed + +* Wait for positive flow-control credit when a peer's `SETTINGS` frame drives a stream's send window negative, instead of letting h2 raise `LocalProtocolError`. ([#935](https://github.com/pydantic/httpx2/pull/935)) + +## 2.0.0 + +Official first release of `httpcore2`. No changes since `2.0.0b1`. + +## 2.0.0b1 + +First release of `httpcore2`, a fork of [`httpcore`](https://github.com/encode/httpcore) maintained by Pydantic. Forked from `httpcore 1.0.9`. + +### Breaking changes + +* **Renamed package**: `httpcore` -> `httpcore2`. `import httpcore` becomes `import httpcore2`. No other public API changed. + +### Fixed + +* Fix `max_keepalive_connections` not being properly handled. (Inherited from upstream PR [encode/httpcore#1000](https://github.com/encode/httpcore/pull/1000).) + +--- + +Historical entries below are from upstream `encode/httpcore`. + +## Version 1.0.9 (April 24th, 2025) + +- Resolve https://github.com/advisories/GHSA-vqfr-h8mv-ghfj with h11 dependency update. (#1008) + +## Version 1.0.8 (April 11th, 2025) + +- Fix `AttributeError` when importing on Python 3.14. (#1005) + +## Version 1.0.7 (November 15th, 2024) + +- Support `proxy=…` configuration on `ConnectionPool()`. (#974) + +## Version 1.0.6 (October 1st, 2024) + +- Relax `trio` dependency pinning. (#956) +- Handle `trio` raising `NotImplementedError` on unsupported platforms. (#955) +- Handle mapping `ssl.SSLError` to `httpcore.ConnectError`. (#918) + +## 1.0.5 (March 27th, 2024) + +- Handle `EndOfStream` exception for anyio backend. (#899) +- Allow trio `0.25.*` series in package dependencies. (#903) + +## 1.0.4 (February 21st, 2024) + +- Add `target` request extension. (#888) +- Fix support for connection `Upgrade` and `CONNECT` when some data in the stream has been read. (#882) + +## 1.0.3 (February 13th, 2024) + +- Fix support for async cancellations. (#880) +- Fix trace extension when used with socks proxy. (#849) +- Fix SSL context for connections using the "wss" scheme (#869) + +## 1.0.2 (November 10th, 2023) + +- Fix `float("inf")` timeouts in `Event.wait` function. (#846) + +## 1.0.1 (November 3rd, 2023) + +- Fix pool timeout to account for the total time spent retrying. (#823) +- Raise a neater RuntimeError when the correct async deps are not installed. (#826) +- Add support for synchronous TLS-in-TLS streams. (#840) + +## 1.0.0 (October 6th, 2023) + +From version 1.0 our async support is now optional, as the package has minimal dependencies by default. + +For async support use either `pip install 'httpcore[asyncio]'` or `pip install 'httpcore[trio]'`. + +The project versioning policy is now explicitly governed by SEMVER. See https://semver.org/. + +- Async support becomes fully optional. (#809) +- Add support for Python 3.12. (#807) + +## 0.18.0 (September 8th, 2023) + +- Add support for HTTPS proxies. (#745, #786) +- Drop Python 3.7 support. (#727) +- Handle `sni_hostname` extension with SOCKS proxy. (#774) +- Handle HTTP/1.1 half-closed connections gracefully. (#641) +- Change the type of `Extensions` from `Mapping[Str, Any]` to `MutableMapping[Str, Any]`. (#762) + +## 0.17.3 (July 5th, 2023) + +- Support async cancellations, ensuring that the connection pool is left in a clean state when cancellations occur. (#726) +- The networking backend interface has [been added to the public API](https://www.encode.io/httpcore/network-backends). Some classes which were previously private implementation detail are now part of the top-level public API. (#699) +- Graceful handling of HTTP/2 GoAway frames, with requests being transparently retried on a new connection. (#730) +- Add exceptions when a synchronous `trace callback` is passed to an asynchronous request or an asynchronous `trace callback` is passed to a synchronous request. (#717) +- Drop Python 3.7 support. (#727) + +## 0.17.2 (May 23th, 2023) + +- Add `socket_options` argument to `ConnectionPool` and `HTTProxy` classes. (#668) +- Improve logging with per-module logger names. (#690) +- Add `sni_hostname` request extension. (#696) +- Resolve race condition during import of `anyio` package. (#692) +- Enable TCP_NODELAY for all synchronous sockets. (#651) + +## 0.17.1 (May 17th, 2023) + +- If 'retries' is set, then allow retries if an SSL handshake error occurs. (#669) +- Improve correctness of tracebacks on network exceptions, by raising properly chained exceptions. (#678) +- Prevent connection-hanging behaviour when HTTP/2 connections are closed by a server-sent 'GoAway' frame. (#679) +- Fix edge-case exception when removing requests from the connection pool. (#680) +- Fix pool timeout edge-case. (#688) + +## 0.17.0 (March 16th, 2023) + +- Add DEBUG level logging. (#648) +- Respect HTTP/2 max concurrent streams when settings updates are sent by server. (#652) +- Increase the allowable HTTP header size to 100kB. (#647) +- Add `retries` option to SOCKS proxy classes. (#643) + +## 0.16.3 (December 20th, 2022) + +- Allow `ws` and `wss` schemes. Allows us to properly support websocket upgrade connections. (#625) +- Forwarding HTTP proxies use a connection-per-remote-host. Required by some proxy implementations. (#637) +- Don't raise `RuntimeError` when closing a connection pool with active connections. Removes some error cases when cancellations are used. (#631) +- Lazy import `anyio`, so that it's no longer a hard dependency, and isn't imported if unused. (#639) + +## 0.16.2 (November 25th, 2022) + +- Revert 'Fix async cancellation behaviour', which introduced race conditions. (#627) +- Raise `RuntimeError` if attempting to us UNIX domain sockets on Windows. (#619) + +## 0.16.1 (November 17th, 2022) + +- Fix HTTP/1.1 interim informational responses, such as "100 Continue". (#605) + +## 0.16.0 (October 11th, 2022) + +- Support HTTP/1.1 informational responses. (#581) +- Fix async cancellation behaviour. (#580) +- Support `h11` 0.14. (#579) + +## 0.15.0 (May 17th, 2022) + +- Drop Python 3.6 support (#535) +- Ensure HTTP proxy CONNECT requests include `timeout` configuration. (#506) +- Switch to explicit `typing.Optional` for type hints. (#513) +- For `trio` map OSError exceptions to `ConnectError`. (#543) + +## 0.14.7 (February 4th, 2022) + +- Requests which raise a PoolTimeout need to be removed from the pool queue. (#502) +- Fix AttributeError that happened when Socks5Connection were terminated. (#501) + +## 0.14.6 (February 1st, 2022) + +- Fix SOCKS support for `http://` URLs. (#492) +- Resolve race condition around exceptions during streaming a response. (#491) + +## 0.14.5 (January 18th, 2022) + +- SOCKS proxy support. (#478) +- Add proxy_auth argument to HTTPProxy. (#481) +- Improve error message on 'RemoteProtocolError' exception when server disconnects without sending a response. (#479) + +## 0.14.4 (January 5th, 2022) + +- Support HTTP/2 on HTTPS tunnelling proxies. (#468) +- Fix proxy headers missing on HTTP forwarding. (#456) +- Only instantiate SSL context if required. (#457) +- More robust HTTP/2 handling. (#253, #439, #440, #441) + +## 0.14.3 (November 17th, 2021) + +- Fix race condition when removing closed connections from the pool. (#437) + +## 0.14.2 (November 16th, 2021) + +- Failed connections no longer remain in the pool. (Pull #433) + +## 0.14.1 (November 12th, 2021) + +- `max_connections` becomes optional. (Pull #429) +- `certifi` is now included in the install dependencies. (Pull #428) +- `h2` is now strictly optional. (Pull #428) + +## 0.14.0 (November 11th, 2021) + +The 0.14 release is a complete reworking of `httpcore`, comprehensively addressing some underlying issues in the connection pooling, as well as substantially redesigning the API to be more user friendly. + +Some of the lower-level API design also makes the components more easily testable in isolation, and the package now has 100% test coverage. + +See [discussion #419](https://github.com/encode/httpcore/discussions/419) for a little more background. + +There's some other neat bits in there too, such as the "trace" extension, which gives a hook into inspecting the internal events that occur during the request/response cycle. This extension is needed for the HTTPX cli, in order to... + +* Log the point at which the connection is established, and the IP/port on which it is made. +* Determine if the outgoing request should log as HTTP/1.1 or HTTP/2, rather than having to assume it's HTTP/2 if the --http2 flag was passed. (Which may not actually be true.) +* Log SSL version info / certificate info. + +Note that `curio` support is not currently available in 0.14.0. If you're using `httpcore` with `curio` please get in touch, so we can assess if we ought to prioritize it as a feature or not. + +## 0.13.7 (September 13th, 2021) + +- Fix broken error messaging when URL scheme is missing, or a non HTTP(S) scheme is used. (Pull #403) + +## 0.13.6 (June 15th, 2021) + +### Fixed + +- Close sockets when read or write timeouts occur. (Pull #365) + +## 0.13.5 (June 14th, 2021) + +### Fixed + +- Resolved niggles with AnyIO EOF behaviours. (Pull #358, #362) + +## 0.13.4 (June 9th, 2021) + +### Added + +- Improved error messaging when URL scheme is missing, or a non HTTP(S) scheme is used. (Pull #354) + +### Fixed + +- Switched to `anyio` as the default backend implementation when running with `asyncio`. Resolves some awkward [TLS timeout issues](https://github.com/encode/httpx/discussions/1511). + +## 0.13.3 (May 6th, 2021) + +### Added + +- Support HTTP/2 prior knowledge, using `httpcore.SyncConnectionPool(http1=False)`. (Pull #333) + +### Fixed + +- Handle cases where environment does not provide `select.poll` support. (Pull #331) + +## 0.13.2 (April 29th, 2021) + +### Added + +- Improve error message for specific case of `RemoteProtocolError` where server disconnects without sending a response. (Pull #313) + +## 0.13.1 (April 28th, 2021) + +### Fixed + +- More resilient testing for closed connections. (Pull #311) +- Don't raise exceptions on ungraceful connection closes. (Pull #310) + +## 0.13.0 (April 21st, 2021) + +The 0.13 release updates the core API in order to match the HTTPX Transport API, +introduced in HTTPX 0.18 onwards. + +An example of making requests with the new interface is: + +```python +with httpcore.SyncConnectionPool() as http: + status_code, headers, stream, extensions = http.handle_request( + method=b'GET', + url=(b'https', b'example.org', 443, b'/'), + headers=[(b'host', b'example.org'), (b'user-agent', b'httpcore')] + stream=httpcore.ByteStream(b''), + extensions={} + ) + body = stream.read() + print(status_code, body) +``` + +### Changed + +- The `.request()` method is now `handle_request()`. (Pull #296) +- The `.arequest()` method is now `.handle_async_request()`. (Pull #296) +- The `headers` argument is no longer optional. (Pull #296) +- The `stream` argument is no longer optional. (Pull #296) +- The `ext` argument is now named `extensions`, and is no longer optional. (Pull #296) +- The `"reason"` extension keyword is now named `"reason_phrase"`. (Pull #296) +- The `"reason_phrase"` and `"http_version"` extensions now use byte strings for their values. (Pull #296) +- The `httpcore.PlainByteStream()` class becomes `httpcore.ByteStream()`. (Pull #296) + +### Added + +- Streams now support a `.read()` interface. (Pull #296) + +### Fixed + +- Task cancellation no longer leaks connections from the connection pool. (Pull #305) + +## 0.12.3 (December 7th, 2020) + +### Fixed + +- Abort SSL connections on close rather than waiting for remote EOF when using `asyncio`. (Pull #167) +- Fix exception raised in case of connect timeouts when using the `anyio` backend. (Pull #236) +- Fix `Host` header precedence for `:authority` in HTTP/2. (Pull #241, #243) +- Handle extra edge case when detecting for socket readability when using `asyncio`. (Pull #242, #244) +- Fix `asyncio` SSL warning when using proxy tunneling. (Pull #249) + +## 0.12.2 (November 20th, 2020) + +### Fixed + +- Properly wrap connect errors on the asyncio backend. (Pull #235) +- Fix `ImportError` occurring on Python 3.9 when using the HTTP/1.1 sync client in a multithreaded context. (Pull #237) + +## 0.12.1 (November 7th, 2020) + +### Added + +- Add connect retries. (Pull #221) + +### Fixed + +- Tweak detection of dropped connections, resolving an issue with open files limits on Linux. (Pull #185) +- Avoid leaking connections when establishing an HTTP tunnel to a proxy has failed. (Pull #223) +- Properly wrap OS errors when using `trio`. (Pull #225) + +## 0.12.0 (October 6th, 2020) + +### Changed + +- HTTP header casing is now preserved, rather than always sent in lowercase. (#216 and python-hyper/h11#104) + +### Added + +- Add Python 3.9 to officially supported versions. + +### Fixed + +- Gracefully handle a stdlib asyncio bug when a connection is closed while it is in a paused-for-reading state. (#201) + +## 0.11.1 (September 28nd, 2020) + +### Fixed + +- Add await to async semaphore release() coroutine (#197) +- Drop incorrect curio classifier (#192) + +## 0.11.0 (September 22nd, 2020) + +The Transport API with 0.11.0 has a couple of significant changes. + +Firstly we've moved changed the request interface in order to allow extensions, which will later enable us to support features +such as trailing headers, HTTP/2 server push, and CONNECT/Upgrade connections. + +The interface changes from: + +```python +def request(method, url, headers, stream, timeout): + return (http_version, status_code, reason, headers, stream) +``` + +To instead including an optional dictionary of extensions on the request and response: + +```python +def request(method, url, headers, stream, ext): + return (status_code, headers, stream, ext) +``` + +Having an open-ended extensions point will allow us to add later support for various optional features, that wouldn't otherwise be supported without these API changes. + +In particular: + +* Trailing headers support. +* HTTP/2 Server Push +* sendfile. +* Exposing raw connection on CONNECT, Upgrade, HTTP/2 bi-di streaming. +* Exposing debug information out of the API, including template name, template context. + +Currently extensions are limited to: + +* request: `timeout` - Optional. Timeout dictionary. +* response: `http_version` - Optional. Include the HTTP version used on the response. +* response: `reason` - Optional. Include the reason phrase used on the response. Only valid with HTTP/1.*. + +See https://github.com/encode/httpx/issues/1274#issuecomment-694884553 for the history behind this. + +Secondly, the async version of `request` is now namespaced as `arequest`. + +This allows concrete transports to support both sync and async implementations on the same class. + +### Added + +- Add curio support. (Pull #168) +- Add anyio support, with `backend="anyio"`. (Pull #169) + +### Changed + +- Update the Transport API to use 'ext' for optional extensions. (Pull #190) +- Update the Transport API to use `.request` and `.arequest` so implementations can support both sync and async. (Pull #189) + +## 0.10.2 (August 20th, 2020) + +### Added + +- Added Unix Domain Socket support. (Pull #139) + +### Fixed + +- Always include the port on proxy CONNECT requests. (Pull #154) +- Fix `max_keepalive_connections` configuration. (Pull #153) +- Fixes behaviour in HTTP/1.1 where server disconnects can be used to signal the end of the response body. (Pull #164) + +## 0.10.1 (August 7th, 2020) + +- Include `max_keepalive_connections` on `AsyncHTTPProxy`/`SyncHTTPProxy` classes. + +## 0.10.0 (August 7th, 2020) + +The most notable change in the 0.10.0 release is that HTTP/2 support is now fully optional. + +Use either `pip install httpcore` for HTTP/1.1 support only, or `pip install httpcore[http2]` for HTTP/1.1 and HTTP/2 support. + +### Added + +- HTTP/2 support becomes optional. (Pull #121, #130) +- Add `local_address=...` support. (Pull #100, #134) +- Add `PlainByteStream`, `IteratorByteStream`, `AsyncIteratorByteStream`. The `AsyncByteSteam` and `SyncByteStream` classes are now pure interface classes. (#133) +- Add `LocalProtocolError`, `RemoteProtocolError` exceptions. (Pull #129) +- Add `UnsupportedProtocol` exception. (Pull #128) +- Add `.get_connection_info()` method. (Pull #102, #137) +- Add better TRACE logs. (Pull #101) + +### Changed + +- `max_keepalive` is deprecated in favour of `max_keepalive_connections`. (Pull #140) + +### Fixed + +- Improve handling of server disconnects. (Pull #112) + +## 0.9.1 (May 27th, 2020) + +### Fixed + +- Proper host resolution for sync case, including IPv6 support. (Pull #97) +- Close outstanding connections when connection pool is closed. (Pull #98) + +## 0.9.0 (May 21th, 2020) + +### Changed + +- URL port becomes an `Optional[int]` instead of `int`. (Pull #92) + +### Fixed + +- Honor HTTP/2 max concurrent streams settings. (Pull #89, #90) +- Remove incorrect debug log. (Pull #83) + +## 0.8.4 (May 11th, 2020) + +### Added + +- Logging via HTTPCORE_LOG_LEVEL and HTTPX_LOG_LEVEL environment variables +and TRACE level logging. (Pull #79) + +### Fixed + +- Reuse of connections on HTTP/2 in close concurrency situations. (Pull #81) + +## 0.8.3 (May 6rd, 2020) + +### Fixed + +- Include `Host` and `Accept` headers on proxy "CONNECT" requests. +- De-duplicate any headers also contained in proxy_headers. +- HTTP/2 flag not being passed down to proxy connections. + +## 0.8.2 (May 3rd, 2020) + +### Fixed + +- Fix connections using proxy forwarding requests not being added to the +connection pool properly. (Pull #70) + +## 0.8.1 (April 30th, 2020) + +### Changed + +- Allow inherintance of both `httpcore.AsyncByteStream`, `httpcore.SyncByteStream` without type conflicts. + +## 0.8.0 (April 30th, 2020) + +### Fixed + +- Fixed tunnel proxy support. + +### Added + +- New `TimeoutException` base class. + +## 0.7.0 (March 5th, 2020) + +- First integration with HTTPX. diff --git a/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/RECORD new file mode 100644 index 0000000..905d219 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/RECORD @@ -0,0 +1,68 @@ +httpcore2-2.13.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +httpcore2-2.13.1.dist-info/METADATA,sha256=N6DqBnTpi3w6H5nBfZu1RX3ieg-TyLZWDJ0X1SUTmrs,26537 +httpcore2-2.13.1.dist-info/RECORD,, +httpcore2-2.13.1.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87 +httpcore2-2.13.1.dist-info/licenses/LICENSE.md,sha256=xN8SXIB7BhNQHpQ6XJJJbdEdLBZgTOOWCXqN3jljBNk,1599 +httpcore2/__init__.py,sha256=jAhvhEBbwqLtHC3PKzTQ3MZFjwnxzSTCETuB9CC5wdg,3473 +httpcore2/__pycache__/__init__.cpython-312.pyc,, +httpcore2/__pycache__/_api.cpython-312.pyc,, +httpcore2/__pycache__/_exceptions.cpython-312.pyc,, +httpcore2/__pycache__/_models.cpython-312.pyc,, +httpcore2/__pycache__/_ssl.cpython-312.pyc,, +httpcore2/__pycache__/_synchronization.cpython-312.pyc,, +httpcore2/__pycache__/_trace.cpython-312.pyc,, +httpcore2/__pycache__/_utils.cpython-312.pyc,, +httpcore2/_api.py,sha256=3xEGeZ6U2rrQsFu-9aHhBNPvdrMU7FLb3cya2ovihQs,3184 +httpcore2/_async/__init__.py,sha256=3FmsS1b2TK-nAlgpKmVGLEFigUYTtoV1CgyJq-Cl5xc,1223 +httpcore2/_async/__pycache__/__init__.cpython-312.pyc,, +httpcore2/_async/__pycache__/connection.cpython-312.pyc,, +httpcore2/_async/__pycache__/connection_pool.cpython-312.pyc,, +httpcore2/_async/__pycache__/http11.cpython-312.pyc,, +httpcore2/_async/__pycache__/http2.cpython-312.pyc,, +httpcore2/_async/__pycache__/http_proxy.cpython-312.pyc,, +httpcore2/_async/__pycache__/interfaces.cpython-312.pyc,, +httpcore2/_async/__pycache__/socks_proxy.cpython-312.pyc,, +httpcore2/_async/connection.py,sha256=nDJx7nUkp9HXgOFDqHUUxI_tPyl9BtYufFQqG74-uYA,8305 +httpcore2/_async/connection_pool.py,sha256=KiYnYlJE9oOn-TutTbL9T06Q0YwX61IMHBzVl9YWhRI,19629 +httpcore2/_async/http11.py,sha256=m8OisIV3ixcPvSK20NYfiERIqdgwHkSNEUb3JOu0KwA,13926 +httpcore2/_async/http2.py,sha256=3E-XPqLMdr9qsxIp5DfdjL4JKM5IQkyYMEfVwYkdEQ8,24005 +httpcore2/_async/http_proxy.py,sha256=ujdPYbzvNPM8vpxR1N2YoTOwU9hPSgNLdVC-b-4atBI,14766 +httpcore2/_async/interfaces.py,sha256=EJ3o4y61Q82cmm7H2IinHxJDqwO18BKyEAA5b3DLuys,5363 +httpcore2/_async/socks_proxy.py,sha256=4JJvWUsCdOmw7-Lay0O-IoVJza9g4J16vvtjYkGE7-0,13947 +httpcore2/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +httpcore2/_backends/__pycache__/__init__.cpython-312.pyc,, +httpcore2/_backends/__pycache__/anyio.cpython-312.pyc,, +httpcore2/_backends/__pycache__/auto.cpython-312.pyc,, +httpcore2/_backends/__pycache__/base.cpython-312.pyc,, +httpcore2/_backends/__pycache__/mock.cpython-312.pyc,, +httpcore2/_backends/__pycache__/sync.cpython-312.pyc,, +httpcore2/_backends/__pycache__/trio.cpython-312.pyc,, +httpcore2/_backends/anyio.py,sha256=QlM55m7iLJYTNf0ICa3VbFjKe-EqMpa_Di9b5dE3oSg,5499 +httpcore2/_backends/auto.py,sha256=NI_UX8sCgRbuxrbK-6tpaV7rgFBgOVfo6d6itTKqA3o,1642 +httpcore2/_backends/base.py,sha256=bGgfuJlHOjPZgQAlGpC4UmTCaYtrblWo3fL12HJKGTU,2997 +httpcore2/_backends/mock.py,sha256=ahbwYl2mEu5K075jTtG6jwLCVHYdVqV2mS0G4-KNQIE,4079 +httpcore2/_backends/sync.py,sha256=sFp-UsYnx7i4qLQVnPp1IPvjn3ZeNO6O5tAJ9Ot2WNM,7959 +httpcore2/_backends/trio.py,sha256=hNOqRRdRbAWmsezO8PGIC6qwvgX4Bg2SSZ7zOht14E4,5961 +httpcore2/_exceptions.py,sha256=ffKPbxfqN_qezmQ2f1B_zrYOw67kEj1K-PCNiCVTEiA,1239 +httpcore2/_models.py,sha256=gELd8jvTk-nkXzLvWIAodlG7Ep6cE0IbLj2D8BcMEGc,17422 +httpcore2/_ssl.py,sha256=ye9EHkxTUjSCwVvoRzIdqzsp6qpck5rMVxKtleqNBuM,398 +httpcore2/_sync/__init__.py,sha256=AQ-tXy0gVolW_kNYfBxzR86v7C3GBSXf1TnGCtC6GhA,1143 +httpcore2/_sync/__pycache__/__init__.cpython-312.pyc,, +httpcore2/_sync/__pycache__/connection.cpython-312.pyc,, +httpcore2/_sync/__pycache__/connection_pool.cpython-312.pyc,, +httpcore2/_sync/__pycache__/http11.cpython-312.pyc,, +httpcore2/_sync/__pycache__/http2.cpython-312.pyc,, +httpcore2/_sync/__pycache__/http_proxy.cpython-312.pyc,, +httpcore2/_sync/__pycache__/interfaces.cpython-312.pyc,, +httpcore2/_sync/__pycache__/socks_proxy.cpython-312.pyc,, +httpcore2/_sync/connection.py,sha256=eX04TfDvqgSOjCB5YQTuydO-btvMBPrAxrUjcbmPyEE,8094 +httpcore2/_sync/connection_pool.py,sha256=6meZmb5dXO3OY7jVJTsos5btmmBuOpZC5Ubnp43zU_A,19246 +httpcore2/_sync/http11.py,sha256=2ZmbOOjU89HLNNtZKVI-9Q98ChXvteqqT86_5fkLI8c,13487 +httpcore2/_sync/http2.py,sha256=2i7RAc5LNZils7O6swdUZKODEcqQl8mNqyfpdgN31Ak,23428 +httpcore2/_sync/http_proxy.py,sha256=IC6IBzY1yCO9TDWn5vq0-_CCU3WtZYdQ05HV71DQSx0,14528 +httpcore2/_sync/interfaces.py,sha256=a6n964OVWiQeSk01nfS6hp63EgGXibxEPhIk-Bm2eKc,5247 +httpcore2/_sync/socks_proxy.py,sha256=3rK9Bkhj7nMcq_LFzpr54pw9Gj9aWjXuHpdOEe6eeAk,13720 +httpcore2/_synchronization.py,sha256=JYjm3eZ9KFm-wcTZBTIV87KcQSMs1AZeRTP2amb4IoE,9442 +httpcore2/_trace.py,sha256=FUYsjUcH1GK1eeWRqXaCuSYy6eA2GcwQUphLbJRQL9o,3928 +httpcore2/_utils.py,sha256=EqJIsFuBA0lXogge1JEhkdskZ7YpUeoS4SSrFtmQD0M,2533 +httpcore2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/WHEEL b/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/WHEEL new file mode 100644 index 0000000..51abe1f --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.32.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/licenses/LICENSE.md b/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/licenses/LICENSE.md new file mode 100644 index 0000000..88e61a1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2-2.13.1.dist-info/licenses/LICENSE.md @@ -0,0 +1,28 @@ +Copyright © 2026 to present Pydantic Services Inc. and individual contributors. +Copyright © 2020, [Encode OSS Ltd](https://www.encode.io/). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/httpcore2/__init__.py b/venv/lib/python3.12/site-packages/httpcore2/__init__.py new file mode 100644 index 0000000..708eea9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/__init__.py @@ -0,0 +1,141 @@ +from importlib.metadata import version + +from ._api import request, stream +from ._async import ( + AsyncConnectionInterface, + AsyncConnectionPool, + AsyncHTTP2Connection, + AsyncHTTP11Connection, + AsyncHTTPConnection, + AsyncHTTPProxy, + AsyncSOCKSProxy, +) +from ._backends.base import ( + SOCKET_OPTION, + AsyncNetworkBackend, + AsyncNetworkStream, + NetworkBackend, + NetworkStream, +) +from ._backends.mock import AsyncMockBackend, AsyncMockStream, MockBackend, MockStream +from ._backends.sync import SyncBackend +from ._exceptions import ( + ConnectError, + ConnectionNotAvailable, + ConnectTimeout, + LocalProtocolError, + NetworkError, + PoolTimeout, + ProtocolError, + ProxyError, + ReadError, + ReadTimeout, + RemoteProtocolError, + TimeoutException, + UnsupportedProtocol, + WriteError, + WriteTimeout, +) +from ._models import URL, Origin, Proxy, Request, Response +from ._ssl import default_ssl_context +from ._sync import ( + ConnectionInterface, + ConnectionPool, + HTTP2Connection, + HTTP11Connection, + HTTPConnection, + HTTPProxy, + SOCKSProxy, +) + +# The 'httpcore2.AnyIOBackend' class is conditional on 'anyio' being installed. +try: + from ._backends.anyio import AnyIOBackend +except ImportError: # pragma: no cover + + class AnyIOBackend: # type: ignore + def __init__(self, *args, **kwargs): # type: ignore + msg = "Attempted to use 'httpcore2.AnyIOBackend' but 'anyio' is not installed." + raise RuntimeError(msg) + + +# The 'httpcore2.TrioBackend' class is conditional on 'trio' being installed. +try: + from ._backends.trio import TrioBackend +except ImportError: # pragma: no cover + + class TrioBackend: # type: ignore + def __init__(self, *args, **kwargs): # type: ignore + msg = "Attempted to use 'httpcore2.TrioBackend' but 'trio' is not installed." + raise RuntimeError(msg) + + +__all__ = [ + # top-level requests + "request", + "stream", + # models + "Origin", + "URL", + "Request", + "Response", + "Proxy", + # async + "AsyncHTTPConnection", + "AsyncConnectionPool", + "AsyncHTTPProxy", + "AsyncHTTP11Connection", + "AsyncHTTP2Connection", + "AsyncConnectionInterface", + "AsyncSOCKSProxy", + # sync + "HTTPConnection", + "ConnectionPool", + "HTTPProxy", + "HTTP11Connection", + "HTTP2Connection", + "ConnectionInterface", + "SOCKSProxy", + # network backends, implementations + "SyncBackend", + "AnyIOBackend", + "TrioBackend", + # network backends, mock implementations + "AsyncMockBackend", + "AsyncMockStream", + "MockBackend", + "MockStream", + # network backends, interface + "AsyncNetworkStream", + "AsyncNetworkBackend", + "NetworkStream", + "NetworkBackend", + # util + "default_ssl_context", + "SOCKET_OPTION", + # exceptions + "ConnectionNotAvailable", + "ProxyError", + "ProtocolError", + "LocalProtocolError", + "RemoteProtocolError", + "UnsupportedProtocol", + "TimeoutException", + "PoolTimeout", + "ConnectTimeout", + "ReadTimeout", + "WriteTimeout", + "NetworkError", + "ConnectError", + "ReadError", + "WriteError", +] + +__version__ = version("httpcore2") + + +__locals = locals() +for __name in __all__: + # Exclude SOCKET_OPTION, it causes AttributeError on Python 3.14 + if not __name.startswith(("__", "SOCKET_OPTION")): + setattr(__locals[__name], "__module__", "httpcore2") # noqa diff --git a/venv/lib/python3.12/site-packages/httpcore2/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6bc6095 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_api.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_api.cpython-312.pyc new file mode 100644 index 0000000..7d31bf1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_api.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_exceptions.cpython-312.pyc new file mode 100644 index 0000000..2150183 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_models.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_models.cpython-312.pyc new file mode 100644 index 0000000..c3ed9fd Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_models.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_ssl.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_ssl.cpython-312.pyc new file mode 100644 index 0000000..e3e9493 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_ssl.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_synchronization.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_synchronization.cpython-312.pyc new file mode 100644 index 0000000..79ffd1f Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_synchronization.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_trace.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_trace.cpython-312.pyc new file mode 100644 index 0000000..514bb31 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_trace.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_utils.cpython-312.pyc new file mode 100644 index 0000000..45b6168 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/__pycache__/_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_api.py b/venv/lib/python3.12/site-packages/httpcore2/_api.py new file mode 100644 index 0000000..c7f4f22 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_api.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +import contextlib +import typing +from collections.abc import Generator + +from ._models import URL, Extensions, HeaderTypes, Response +from ._sync.connection_pool import ConnectionPool + + +def request( + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterator[bytes] | None = None, + extensions: Extensions | None = None, +) -> Response: + """ + Sends an HTTP request, returning the response. + + ``` + response = httpcore2.request("GET", "https://www.example.com/") + ``` + + Arguments: + method: The HTTP method for the request. Typically one of `"GET"`, + `"OPTIONS"`, `"HEAD"`, `"POST"`, `"PUT"`, `"PATCH"`, or `"DELETE"`. + url: The URL of the HTTP request. Either as an instance of `httpcore2.URL`, + or as str/bytes. + headers: The HTTP request headers. Either as a dictionary of str/bytes, + or as a list of two-tuples of str/bytes. + content: The content of the request body. Either as bytes, + or as a bytes iterator. + extensions: A dictionary of optional extra information included on the request. + Possible keys include `"timeout"`. + + Returns: + An instance of `httpcore2.Response`. + """ + with ConnectionPool() as pool: + return pool.request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + + +@contextlib.contextmanager +def stream( + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterator[bytes] | None = None, + extensions: Extensions | None = None, +) -> Generator[Response]: + """ + Sends an HTTP request, returning the response within a content manager. + + ``` + with httpcore2.stream("GET", "https://www.example.com/") as response: + ... + ``` + + When using the `stream()` function, the body of the response will not be + automatically read. If you want to access the response body you should + either use `content = response.read()`, or `for chunk in response.iter_content()`. + + Arguments: + method: The HTTP method for the request. Typically one of `"GET"`, + `"OPTIONS"`, `"HEAD"`, `"POST"`, `"PUT"`, `"PATCH"`, or `"DELETE"`. + url: The URL of the HTTP request. Either as an instance of `httpcore2.URL`, + or as str/bytes. + headers: The HTTP request headers. Either as a dictionary of str/bytes, + or as a list of two-tuples of str/bytes. + content: The content of the request body. Either as bytes, + or as a bytes iterator. + extensions: A dictionary of optional extra information included on the request. + Possible keys include `"timeout"`. + + Returns: + An instance of `httpcore2.Response`. + """ + with ConnectionPool() as pool: + with pool.stream( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) as response: + yield response diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/__init__.py b/venv/lib/python3.12/site-packages/httpcore2/_async/__init__.py new file mode 100644 index 0000000..96b4d76 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_async/__init__.py @@ -0,0 +1,39 @@ +from .connection import AsyncHTTPConnection +from .connection_pool import AsyncConnectionPool +from .http11 import AsyncHTTP11Connection +from .http_proxy import AsyncHTTPProxy +from .interfaces import AsyncConnectionInterface + +try: + from .http2 import AsyncHTTP2Connection +except ImportError: # pragma: no cover + + class AsyncHTTP2Connection: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use http2 support, but the `h2` package is not " + "installed. Use 'pip install httpcore[http2]'." + ) + + +try: + from .socks_proxy import AsyncSOCKSProxy +except ImportError: # pragma: no cover + + class AsyncSOCKSProxy: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use SOCKS support, but the `socksio` package is not " + "installed. Use 'pip install httpcore[socks]'." + ) + + +__all__ = [ + "AsyncHTTPConnection", + "AsyncConnectionPool", + "AsyncHTTPProxy", + "AsyncHTTP11Connection", + "AsyncHTTP2Connection", + "AsyncConnectionInterface", + "AsyncSOCKSProxy", +] diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6bab925 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/connection.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/connection.cpython-312.pyc new file mode 100644 index 0000000..234edb5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/connection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/connection_pool.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/connection_pool.cpython-312.pyc new file mode 100644 index 0000000..f5ca370 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/connection_pool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/http11.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/http11.cpython-312.pyc new file mode 100644 index 0000000..69ca158 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/http11.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/http2.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/http2.cpython-312.pyc new file mode 100644 index 0000000..627100c Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/http2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/http_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/http_proxy.cpython-312.pyc new file mode 100644 index 0000000..8a5bfad Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/http_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/interfaces.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/interfaces.cpython-312.pyc new file mode 100644 index 0000000..b9388b1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/interfaces.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/socks_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/socks_proxy.cpython-312.pyc new file mode 100644 index 0000000..c49b741 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_async/__pycache__/socks_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/connection.py b/venv/lib/python3.12/site-packages/httpcore2/_async/connection.py new file mode 100644 index 0000000..f0b445e --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_async/connection.py @@ -0,0 +1,208 @@ +from __future__ import annotations + +import itertools +import logging +import ssl +import types +import typing + +from .._backends.auto import AutoBackend +from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream +from .._exceptions import ConnectError, ConnectTimeout +from .._models import Origin, Request, Response +from .._ssl import default_ssl_context +from .._synchronization import AsyncLock +from .._trace import Trace +from .http11 import AsyncHTTP11Connection +from .interfaces import AsyncConnectionInterface + +RETRIES_BACKOFF_FACTOR = 0.5 # 0s, 0.5s, 1s, 2s, 4s, etc. + + +logger = logging.getLogger("httpcore2.connection") + + +def exponential_backoff(factor: float) -> typing.Iterator[float]: + """ + Generate a geometric sequence that has a ratio of 2 and starts with 0. + + For example: + - `factor = 2`: `0, 2, 4, 8, 16, 32, 64, ...` + - `factor = 3`: `0, 3, 6, 12, 24, 48, 96, ...` + """ + yield 0 + for n in itertools.count(): + yield factor * 2**n + + +class AsyncHTTPConnection(AsyncConnectionInterface): + def __init__( + self, + origin: Origin, + ssl_context: ssl.SSLContext | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + self._origin = origin + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend: AsyncNetworkBackend = AutoBackend() if network_backend is None else network_backend + self._connection: AsyncConnectionInterface | None = None + self._connect_failed: bool = False + self._request_lock = AsyncLock() + self._socket_options = socket_options + + async def handle_async_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError(f"Attempted to send request to {request.url.origin} on connection to {self._origin}") + + try: + async with self._request_lock: + if self._connection is None: + stream = await self._connect(request) + + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ssl_object is not None and ssl_object.selected_alpn_protocol() == "h2" + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import AsyncHTTP2Connection + + self._connection = AsyncHTTP2Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = AsyncHTTP11Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except BaseException as exc: + self._connect_failed = True + raise exc + + return await self._connection.handle_async_request(request) + + async def _connect(self, request: Request) -> AsyncNetworkStream: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + retries_left = self._retries + delays = exponential_backoff(factor=RETRIES_BACKOFF_FACTOR) + + while True: + try: + if self._uds is None: + kwargs = { + "host": self._origin.host.decode("ascii"), + "port": self._origin.port, + "local_address": self._local_address, + "timeout": timeout, + "socket_options": self._socket_options, + } + async with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = await self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + else: + kwargs = { + "path": self._uds, + "timeout": timeout, + "socket_options": self._socket_options, + } + async with Trace("connect_unix_socket", logger, request, kwargs) as trace: + stream = await self._network_backend.connect_unix_socket(**kwargs) + trace.return_value = stream + + if self._origin.scheme in (b"https", b"wss"): + ssl_context = default_ssl_context() if self._ssl_context is None else self._ssl_context + alpn_protocols = (["h2", "http/1.1"] if self._http1 else ["h2"]) if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname or self._origin.host.decode("ascii"), + "timeout": timeout, + } + async with Trace("start_tls", logger, request, kwargs) as trace: + stream = await stream.start_tls(**kwargs) + trace.return_value = stream + return stream + except (ConnectError, ConnectTimeout): + if retries_left <= 0: + raise + retries_left -= 1 + delay = next(delays) + async with Trace("retry", logger, request, kwargs) as trace: + await self._network_backend.sleep(delay) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + async def aclose(self) -> None: + if self._connection is not None: + async with Trace("close", logger, None, {}): + await self._connection.aclose() + + def is_connected(self) -> bool: + return self._connection is not None and self._connection.is_connected() + + def is_available(self) -> bool: + if self._connection is None: + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return self._http2 and (self._origin.scheme == b"https" or not self._http1) and not self._connect_failed + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_idle() + + def can_multiplex(self) -> bool: + return self._connection is not None and self._connection.can_multiplex() + + def is_closed(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + async def __aenter__(self) -> AsyncHTTPConnection: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + await self.aclose() diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/connection_pool.py b/venv/lib/python3.12/site-packages/httpcore2/_async/connection_pool.py new file mode 100644 index 0000000..2c5e398 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_async/connection_pool.py @@ -0,0 +1,440 @@ +from __future__ import annotations + +import ssl +import sys +import types +import typing +from collections.abc import AsyncGenerator + +from .._backends.auto import AutoBackend +from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend +from .._exceptions import ConnectionNotAvailable, UnsupportedProtocol +from .._models import Origin, Proxy, Request, Response +from .._synchronization import AsyncEvent, AsyncShieldCancellation, AsyncThreadLock +from .._utils import safe_async_iterate +from .connection import AsyncHTTPConnection +from .interfaces import AsyncConnectionInterface, AsyncRequestInterface + + +class AsyncPoolRequest: + def __init__(self, request: Request) -> None: + self.request = request + self.connection: AsyncConnectionInterface | None = None + self._connection_acquired = AsyncEvent() + + def assign_to_connection(self, connection: AsyncConnectionInterface | None) -> None: + self.connection = connection + self._connection_acquired.set() + + def clear_connection(self) -> None: + self.connection = None + self._connection_acquired = AsyncEvent() + + async def wait_for_connection(self, timeout: float | None = None) -> AsyncConnectionInterface: + if self.connection is None: + await self._connection_acquired.wait(timeout=timeout) + assert self.connection is not None + return self.connection + + def is_queued(self) -> bool: + return self.connection is None + + +class AsyncConnectionPool(AsyncRequestInterface): + """ + A connection pool for making HTTP requests. + """ + + def __init__( + self, + ssl_context: ssl.SSLContext | None = None, + proxy: Proxy | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore2.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish a + connection. + local_address: Local address to connect from. Can also be used to connect + using a particular address family. Using `local_address="0.0.0.0"` + will connect using an `AF_INET` address (IPv4), while using + `local_address="::"` will connect using an `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + socket_options: Socket options that have to be included + in the TCP socket when the connection was established. + """ + self._ssl_context = ssl_context + self._proxy = proxy + self._max_connections = sys.maxsize if max_connections is None else max_connections + self._max_keepalive_connections = ( + sys.maxsize if max_keepalive_connections is None else max_keepalive_connections + ) + self._max_keepalive_connections = min(self._max_connections, self._max_keepalive_connections) + + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend = AutoBackend() if network_backend is None else network_backend + self._socket_options = socket_options + + # The mutable state on a connection pool is the queue of incoming requests, + # and the set of connections that are servicing those requests. + self._connections: list[AsyncConnectionInterface] = [] + self._requests: list[AsyncPoolRequest] = [] + + # We only mutate the state of the connection pool within an 'optional_thread_lock' + # context. This holds a threading lock unless we're running in async mode, + # in which case it is a no-op. + self._optional_thread_lock = AsyncThreadLock() + + def create_connection(self, origin: Origin) -> AsyncConnectionInterface: + if self._proxy is not None: + if self._proxy.url.scheme in (b"socks5", b"socks5h"): + from .socks_proxy import AsyncSocks5Connection + + return AsyncSocks5Connection( + proxy_origin=self._proxy.url.origin, + proxy_auth=self._proxy.auth, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + elif origin.scheme == b"http": + from .http_proxy import AsyncForwardHTTPConnection + + return AsyncForwardHTTPConnection( + proxy_origin=self._proxy.url.origin, + proxy_headers=self._proxy.headers, + proxy_ssl_context=self._proxy.ssl_context, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + ) + from .http_proxy import AsyncTunnelHTTPConnection + + return AsyncTunnelHTTPConnection( + proxy_origin=self._proxy.url.origin, + proxy_headers=self._proxy.headers, + proxy_ssl_context=self._proxy.ssl_context, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + return AsyncHTTPConnection( + origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + retries=self._retries, + local_address=self._local_address, + uds=self._uds, + network_backend=self._network_backend, + socket_options=self._socket_options, + ) + + @property + def connections(self) -> list[AsyncConnectionInterface]: + """ + Return a list of the connections currently in the pool. + + For example: + + ```python + >>> pool.connections + [ + , + , + , + ] + ``` + """ + return list(self._connections) + + async def handle_async_request(self, request: Request) -> Response: + """ + Send an HTTP request, and return an HTTP response. + + This is the core implementation that is called into by `.request()` or `.stream()`. + """ + scheme = request.url.scheme.decode() + if scheme == "": + raise UnsupportedProtocol("Request URL is missing an 'http://' or 'https://' protocol.") + if scheme not in ("http", "https", "ws", "wss"): + raise UnsupportedProtocol(f"Request URL has an unsupported protocol '{scheme}://'.") + + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("pool", None) + + with self._optional_thread_lock: + # Add the incoming request to our request queue. + pool_request = AsyncPoolRequest(request) + self._requests.append(pool_request) + + try: + while True: + with self._optional_thread_lock: + # Assign incoming requests to available connections, + # closing or creating new connections as required. + closing = self._assign_requests_to_connections() + await self._close_connections(closing) + + # Wait until this request has an assigned connection. + connection = await pool_request.wait_for_connection(timeout=timeout) + + try: + # Send the request on the assigned connection. + response = await connection.handle_async_request(pool_request.request) + except ConnectionNotAvailable: + # In some cases a connection may initially be available to + # handle a request, but then become unavailable. + # + # In this case we clear the connection and try again. + pool_request.clear_connection() + else: + break # pragma: no cover + + except BaseException as exc: + with self._optional_thread_lock: + # For any exception or cancellation we remove the request from + # the queue, and then re-assign requests to connections. + self._requests.remove(pool_request) + closing = self._assign_requests_to_connections() + + await self._close_connections(closing) + raise exc from None + + # Return the response. Note that in this case we still have to manage + # the point at which the response is closed. + assert isinstance(response.stream, typing.AsyncIterable) + return Response( + status=response.status, + headers=response.headers, + content=PoolByteStream(stream=response.stream, pool_request=pool_request, pool=self), + extensions=response.extensions, + ) + + def _assign_requests_to_connections(self) -> list[AsyncConnectionInterface]: + """ + Manage the state of the connection pool, assigning incoming + requests to connections as available. + + Called whenever a new request is added or removed from the pool. + + Any closing connections are returned, allowing the I/O for closing + those connections to be handled separately. + """ + closing_connections: list[AsyncConnectionInterface] = [] + retained_connections: list[AsyncConnectionInterface] = [] + + # Connections currently referenced by an in-flight request, including + # connections that are in the process of being established and idle + # connections reserved by an assigned-but-not-yet-sent request. + request_connections = {r.connection for r in self._requests} + + # First we handle cleaning up any connections that are closed + # or have expired their keep-alive, in a single pass. Reserved + # connections skip the expiry check: they were checked when assigned, + # and `has_expired()` on an idle connection probes the socket. + for connection in self._connections: + reserved = connection in request_connections + if connection.is_closed(): + continue + elif not (connection.is_connected() or reserved): + # Garbage: a NEW-state connection whose request was cancelled + # before the TCP handshake completed. Drop it without closing + # (there is no socket to close yet). + continue + elif not reserved and connection.has_expired(): + closing_connections.append(connection) + else: + retained_connections.append(connection) + + # Then we close any surplus idle connections, to enforce the + # max_keepalive_connections setting. Reserved connections are not + # surplus: a request is about to be sent on them. + idle_surplus = ( + sum(connection.is_idle() and connection not in request_connections for connection in retained_connections) + - self._max_keepalive_connections + ) + if idle_surplus > 0: + kept: list[AsyncConnectionInterface] = [] + for connection in retained_connections: + if idle_surplus > 0 and connection.is_idle() and connection not in request_connections: + closing_connections.append(connection) + idle_surplus -= 1 + else: + kept.append(connection) + retained_connections = kept + + self._connections = retained_connections + + # Snapshot the set of reusable connections once, rather than rebuilding + # it per queued request — this is what brings the loop from O(N*M) to + # O(N+M) in the common case. + # + # An idle connection already assigned to an in-flight request is + # reserved: it stays IDLE until the winning task sends on it, so + # without this exclusion the next pass would assign it again and the + # loser would churn through `ConnectionNotAvailable`. Multiplexing + # connections are exempt: they can take further requests while idle. + available_connections = [ + connection + for connection in self._connections + if connection.is_available() + and not (connection.is_idle() and connection in request_connections and not connection.can_multiplex()) + ] + new_connection_budget = self._max_connections - len(self._connections) + + # Assign queued requests to connections. Once no connection is + # available and no new connection may be created, no queued request + # can be assigned, so the scan stops early: this keeps a pass on a + # saturated pool O(connections) rather than O(in-flight requests). + for pool_request in self._requests: + if not available_connections and new_connection_budget <= 0: + break + if not pool_request.is_queued(): + continue + origin = pool_request.request.url.origin + + # There are three cases for how we may be able to handle the request: + # + # 1. There is an existing connection that can handle the request. + # 2. We can create a new connection to handle the request. + # 3. We can close an idle connection and then create a new connection + # to handle the request. + for idx, connection in enumerate(available_connections): + if connection.can_handle_request(origin): + pool_request.assign_to_connection(connection) + if connection.is_idle() and not connection.can_multiplex(): + # An idle HTTP/1.1 connection can only take this + # single request until it is released. + del available_connections[idx] + break + else: + if new_connection_budget > 0: + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + new_connection_budget -= 1 + continue + for idx, connection in enumerate(available_connections): + if connection.is_idle(): + del available_connections[idx] + self._connections.remove(connection) + closing_connections.append(connection) + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + break + + return closing_connections + + async def _close_connections(self, closing: list[AsyncConnectionInterface]) -> None: + # Close connections which have been removed from the pool. + with AsyncShieldCancellation(): + for connection in closing: + await connection.aclose() + + async def aclose(self) -> None: + # Explicitly close the connection pool. + # Clears all existing requests and connections. + with self._optional_thread_lock: + closing_connections = list(self._connections) + self._connections = [] + await self._close_connections(closing_connections) + + async def __aenter__(self) -> AsyncConnectionPool: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + await self.aclose() + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + with self._optional_thread_lock: + request_is_queued = [request.is_queued() for request in self._requests] + connection_is_idle = [connection.is_idle() for connection in self._connections] + + num_active_requests = request_is_queued.count(False) + num_queued_requests = request_is_queued.count(True) + num_active_connections = connection_is_idle.count(False) + num_idle_connections = connection_is_idle.count(True) + + requests_info = f"Requests: {num_active_requests} active, {num_queued_requests} queued" + connection_info = f"Connections: {num_active_connections} active, {num_idle_connections} idle" + + return f"<{class_name} [{requests_info} | {connection_info}]>" + + +class PoolByteStream: + def __init__( + self, + stream: typing.AsyncIterable[bytes], + pool_request: AsyncPoolRequest, + pool: AsyncConnectionPool, + ) -> None: + self._stream = stream + self._pool_request = pool_request + self._pool = pool + self._closed = False + + async def __aiter__(self) -> AsyncGenerator[bytes]: + async with safe_async_iterate(self._stream) as iterator: + async for chunk in iterator: + yield chunk + + async def aclose(self) -> None: + if not self._closed: + self._closed = True + with AsyncShieldCancellation(): + if hasattr(self._stream, "aclose"): + await self._stream.aclose() + + with self._pool._optional_thread_lock: + self._pool._requests.remove(self._pool_request) + closing = self._pool._assign_requests_to_connections() + + await self._pool._close_connections(closing) diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/http11.py b/venv/lib/python3.12/site-packages/httpcore2/_async/http11.py new file mode 100644 index 0000000..2fc9445 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_async/http11.py @@ -0,0 +1,357 @@ +from __future__ import annotations + +import enum +import logging +import ssl +import time +import types +import typing +from collections.abc import AsyncGenerator + +import h11 + +from .._backends.base import AsyncNetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, + WriteError, + map_exceptions, +) +from .._models import Origin, Request, Response +from .._synchronization import AsyncLock, AsyncShieldCancellation +from .._trace import Trace +from .._utils import safe_async_iterate +from .interfaces import AsyncConnectionInterface + +logger = logging.getLogger("httpcore2.http11") + + +# A subset of `h11.Event` types supported by `_send_event` +H11SendEvent = h11.Request | h11.Data | h11.EndOfMessage + + +class HTTPConnectionState(enum.IntEnum): + NEW = 0 + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class AsyncHTTP11Connection(AsyncConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + MAX_INCOMPLETE_EVENT_SIZE = 100 * 1024 + + def __init__( + self, + origin: Origin, + stream: AsyncNetworkStream, + keepalive_expiry: float | None = None, + ) -> None: + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: float | None = keepalive_expiry + self._expire_at: float | None = None + self._state = HTTPConnectionState.NEW + self._state_lock = AsyncLock() + self._request_count = 0 + self._h11_state = h11.Connection( + our_role=h11.CLIENT, + max_incomplete_event_size=self.MAX_INCOMPLETE_EVENT_SIZE, + ) + + async def handle_async_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError(f"Attempted to send request to {request.url.origin} on connection to {self._origin}") + + async with self._state_lock: + if self._state in (HTTPConnectionState.NEW, HTTPConnectionState.IDLE): + self._request_count += 1 + self._state = HTTPConnectionState.ACTIVE + self._expire_at = None + else: + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request} + try: + async with Trace("send_request_headers", logger, request, kwargs) as trace: + await self._send_request_headers(**kwargs) + async with Trace("send_request_body", logger, request, kwargs) as trace: + await self._send_request_body(**kwargs) + except WriteError: + # If we get a write error while we're writing the request, + # then we suppress this error and move on to attempting to + # read the response. Servers can sometimes close the request + # preemptively and then respond with a well formed HTTP + # error response. + pass + + async with Trace("receive_response_headers", logger, request, kwargs) as trace: + ( + http_version, + status, + reason_phrase, + headers, + trailing_data, + ) = await self._receive_response_headers(**kwargs) + trace.return_value = ( + http_version, + status, + reason_phrase, + headers, + ) + + network_stream = self._network_stream + + # CONNECT or Upgrade request + if (status == 101) or ((request.method == b"CONNECT") and (200 <= status < 300)): + network_stream = AsyncHTTP11UpgradeStream(network_stream, trailing_data) + + return Response( + status=status, + headers=headers, + content=HTTP11ConnectionByteStream(self, request), + extensions={ + "http_version": http_version, + "reason_phrase": reason_phrase, + "network_stream": network_stream, + }, + ) + except BaseException as exc: + with AsyncShieldCancellation(): + async with Trace("response_closed", logger, request) as trace: + await self._response_closed() + raise exc + + # Sending the request... + + async def _send_request_headers(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + with map_exceptions({h11.LocalProtocolError: LocalProtocolError}): + event = h11.Request( + method=request.method, + target=request.url.target, + headers=request.headers, + ) + await self._send_event(event, timeout=timeout) + + async def _send_request_body(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + assert isinstance(request.stream, typing.AsyncIterable) + async with safe_async_iterate(request.stream) as iterator: + async for chunk in iterator: + event = h11.Data(data=chunk) + await self._send_event(event, timeout=timeout) + + await self._send_event(h11.EndOfMessage(), timeout=timeout) + + async def _send_event(self, event: h11.Event, timeout: float | None = None) -> None: + bytes_to_send = self._h11_state.send(event) + if bytes_to_send is not None: + await self._network_stream.write(bytes_to_send, timeout=timeout) + + # Receiving the response... + + async def _receive_response_headers( + self, request: Request + ) -> tuple[bytes, int, bytes, list[tuple[bytes, bytes]], bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = await self._receive_event(timeout=timeout) + if isinstance(event, h11.Response): + break + if isinstance(event, h11.InformationalResponse) and event.status_code == 101: + break + + http_version = b"HTTP/" + event.http_version + + # h11 version 0.11+ supports a `raw_items` interface to get the + # raw header casing, rather than the enforced lowercase headers. + headers = event.headers.raw_items() + + trailing_data, _ = self._h11_state.trailing_data + + return http_version, event.status_code, event.reason, headers, trailing_data + + async def _receive_response_body(self, request: Request) -> AsyncGenerator[bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = await self._receive_event(timeout=timeout) + if isinstance(event, h11.Data): + yield bytes(event.data) + elif isinstance(event, (h11.EndOfMessage, h11.PAUSED)): + break + + async def _receive_event(self, timeout: float | None = None) -> h11.Event | type[h11.PAUSED]: + while True: + with map_exceptions({h11.RemoteProtocolError: RemoteProtocolError}): + event = self._h11_state.next_event() + + if event is h11.NEED_DATA: + data = await self._network_stream.read(self.READ_NUM_BYTES, timeout=timeout) + + # If we feed this case through h11 we'll raise an exception like: + # + # httpcore2.RemoteProtocolError: can't handle event type + # ConnectionClosed when role=SERVER and state=SEND_RESPONSE + # + # Which is accurate, but not very informative from an end-user + # perspective. Instead we handle this case distinctly and treat + # it as a ConnectError. + if data == b"" and self._h11_state.their_state == h11.SEND_RESPONSE: + msg = "Server disconnected without sending a response." + raise RemoteProtocolError(msg) + + self._h11_state.receive_data(data) + else: + # mypy fails to narrow the type in the above if statement above + return event # type: ignore[return-value] + + async def _response_closed(self) -> None: + async with self._state_lock: + if self._h11_state.our_state is h11.DONE and self._h11_state.their_state is h11.DONE: + self._state = HTTPConnectionState.IDLE + self._h11_state.start_next_cycle() + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + else: + await self.aclose() + + # Once the connection is no longer required... + + async def aclose(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._state = HTTPConnectionState.CLOSED + await self._network_stream.aclose() + + # The AsyncConnectionInterface methods provide information about the state of + # the connection, allowing for a connection pooling implementation to + # determine when to reuse and when to close the connection... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_connected(self) -> bool: + return not self.is_closed() + + def is_available(self) -> bool: + # Note that HTTP/1.1 connections in the "NEW" state are not treated as + # being "available". The control flow which created the connection will + # be able to send an outgoing request, but the connection will not be + # acquired from the connection pool for any other request. + return self._state == HTTPConnectionState.IDLE + + def has_expired(self) -> bool: + now = time.monotonic() + # Read `_expire_at` once into a local: on free-threaded builds another + # thread may reset it to `None` between the check and the comparison. + expire_at = self._expire_at + keepalive_expired = expire_at is not None and now > expire_at + + # If the HTTP connection is idle but the socket is readable, then the + # only valid state is that the socket is about to return b"", indicating + # a server-initiated disconnect. + server_disconnected = self._state == HTTPConnectionState.IDLE and self._network_stream.get_extra_info( + "is_readable" + ) + + return keepalive_expired or server_disconnected + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return f"{origin!r}, HTTP/1.1, {self._state.name}, Request Count: {self._request_count}" + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return f"<{class_name} [{origin!r}, {self._state.name}, Request Count: {self._request_count}]>" + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + async def __aenter__(self) -> AsyncHTTP11Connection: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + await self.aclose() + + +class HTTP11ConnectionByteStream: + def __init__(self, connection: AsyncHTTP11Connection, request: Request) -> None: + self._connection = connection + self._request = request + self._closed = False + + async def __aiter__(self) -> AsyncGenerator[bytes]: + kwargs = {"request": self._request} + try: + async with Trace("receive_response_body", logger, self._request, kwargs): + async with safe_async_iterate(self._connection._receive_response_body(**kwargs)) as iterator: + async for chunk in iterator: + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with AsyncShieldCancellation(): + await self.aclose() + raise exc + + async def aclose(self) -> None: + if not self._closed: + self._closed = True + async with Trace("response_closed", logger, self._request): + await self._connection._response_closed() + + +class AsyncHTTP11UpgradeStream(AsyncNetworkStream): + def __init__(self, stream: AsyncNetworkStream, leading_data: bytes) -> None: + self._stream = stream + self._leading_data = leading_data + + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + if self._leading_data: + buffer = self._leading_data[:max_bytes] + self._leading_data = self._leading_data[max_bytes:] + return buffer + else: + return await self._stream.read(max_bytes, timeout) + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + await self._stream.write(buffer, timeout) + + async def aclose(self) -> None: + await self._stream.aclose() + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + return await self._stream.start_tls(ssl_context, server_hostname, timeout) + + def get_extra_info(self, info: str) -> typing.Any: + return self._stream.get_extra_info(info) diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/http2.py b/venv/lib/python3.12/site-packages/httpcore2/_async/http2.py new file mode 100644 index 0000000..6f009c1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_async/http2.py @@ -0,0 +1,559 @@ +from __future__ import annotations + +import enum +import logging +import time +import types +import typing +from collections.abc import AsyncGenerator + +import h2.config +import h2.connection +import h2.events +import h2.exceptions +import h2.settings + +from .._backends.base import AsyncNetworkStream +from .._exceptions import ConnectionNotAvailable, LocalProtocolError, RemoteProtocolError +from .._models import Origin, Request, Response +from .._synchronization import AsyncLock, AsyncSemaphore, AsyncShieldCancellation +from .._trace import Trace +from .._utils import safe_async_iterate +from .interfaces import AsyncConnectionInterface + +logger = logging.getLogger("httpcore2.http2") + + +def has_body_headers(request: Request) -> bool: + return any(k.lower() == b"content-length" or k.lower() == b"transfer-encoding" for k, _v in request.headers) + + +class HTTPConnectionState(enum.IntEnum): + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class AsyncHTTP2Connection(AsyncConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + CONFIG = h2.config.H2Configuration(validate_inbound_headers=False) + + def __init__( + self, + origin: Origin, + stream: AsyncNetworkStream, + keepalive_expiry: float | None = None, + ): + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: float | None = keepalive_expiry + self._h2_state = h2.connection.H2Connection(config=self.CONFIG) + self._state = HTTPConnectionState.IDLE + self._expire_at: float | None = None + self._request_count = 0 + self._init_lock = AsyncLock() + self._state_lock = AsyncLock() + self._read_lock = AsyncLock() + self._write_lock = AsyncLock() + self._sent_connection_init = False + self._used_all_stream_ids = False + self._connection_error = False + + # Mapping from stream ID to response stream events. + self._events: dict[ + int, + list[h2.events.ResponseReceived | h2.events.DataReceived | h2.events.StreamEnded | h2.events.StreamReset,], + ] = {} + + # Connection terminated events are stored as state since + # we need to handle them for all streams. + self._connection_terminated: h2.events.ConnectionTerminated | None = None + + self._read_exception: Exception | None = None + self._write_exception: Exception | None = None + + async def handle_async_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + # This cannot occur in normal operation, since the connection pool + # will only send requests on connections that handle them. + # It's in place simply for resilience as a guard against incorrect + # usage, for anyone working directly with httpcore connections. + raise RuntimeError(f"Attempted to send request to {request.url.origin} on connection to {self._origin}") + + async with self._state_lock: + if self._state in (HTTPConnectionState.ACTIVE, HTTPConnectionState.IDLE): + self._request_count += 1 + self._expire_at = None + self._state = HTTPConnectionState.ACTIVE + else: + raise ConnectionNotAvailable() + + async with self._init_lock: + if not self._sent_connection_init: + try: + sci_kwargs = {"request": request} + async with Trace("send_connection_init", logger, request, sci_kwargs): + await self._send_connection_init(**sci_kwargs) + except BaseException as exc: + with AsyncShieldCancellation(): + await self.aclose() + raise exc + + self._sent_connection_init = True + + # Initially start with just 1 until the remote server provides + # its max_concurrent_streams value + self._max_streams = 1 + + local_settings_max_streams = self._h2_state.local_settings.max_concurrent_streams + self._max_streams_semaphore = AsyncSemaphore(local_settings_max_streams) + + for _ in range(local_settings_max_streams - self._max_streams): + await self._max_streams_semaphore.acquire() + + await self._max_streams_semaphore.acquire() + + try: + stream_id = self._h2_state.get_next_available_stream_id() + self._events[stream_id] = [] + except h2.exceptions.NoAvailableStreamIDError: # pragma: no cover + self._used_all_stream_ids = True + self._request_count -= 1 + await self._max_streams_semaphore.release() + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request, "stream_id": stream_id} + async with Trace("send_request_headers", logger, request, kwargs): + await self._send_request_headers(request=request, stream_id=stream_id) + async with Trace("send_request_body", logger, request, kwargs): + await self._send_request_body(request=request, stream_id=stream_id) + async with Trace("receive_response_headers", logger, request, kwargs) as trace: + status, headers = await self._receive_response(request=request, stream_id=stream_id) + trace.return_value = (status, headers) + + return Response( + status=status, + headers=headers, + content=HTTP2ConnectionByteStream(self, request, stream_id=stream_id), + extensions={ + "http_version": b"HTTP/2", + "network_stream": self._network_stream, + "stream_id": stream_id, + }, + ) + except BaseException as exc: # noqa: PIE786 + with AsyncShieldCancellation(): + kwargs = {"stream_id": stream_id} + async with Trace("response_closed", logger, request, kwargs): + await self._response_closed(stream_id=stream_id) + + if isinstance(exc, h2.exceptions.ProtocolError): + # One case where h2 can raise a protocol error is when a + # closed frame has been seen by the state machine. + # + # This happens when one stream is reading, and encounters + # a GOAWAY event. Other flows of control may then raise + # a protocol error at any point they interact with the 'h2_state'. + # + # In this case we'll have stored the event, and should raise + # it as a RemoteProtocolError. + if self._connection_terminated: # pragma: no cover + raise RemoteProtocolError(self._connection_terminated) + # If h2 raises a protocol error in some other state then we + # must somehow have made a protocol violation. + raise LocalProtocolError(exc) # pragma: no cover + + raise exc + + async def _send_connection_init(self, request: Request) -> None: + """ + The HTTP/2 connection requires some initial setup before we can start + using individual request/response streams on it. + """ + # Need to set these manually here instead of manipulating via + # __setitem__() otherwise the H2Connection will emit SettingsUpdate + # frames in addition to sending the undesired defaults. + self._h2_state.local_settings = h2.settings.Settings( + client=True, + initial_values={ + # Disable PUSH_PROMISE frames from the server since we don't do anything + # with them for now. Maybe when we support caching? + h2.settings.SettingCodes.ENABLE_PUSH: 0, + # These two are taken from h2 for safe defaults + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS: 100, + h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE: 65536, + }, + ) + + # Some websites (*cough* Yahoo *cough*) balk at this setting being + # present in the initial handshake since it's not defined in the original + # RFC despite the RFC mandating ignoring settings you don't know about. + del self._h2_state.local_settings[h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL] + + self._h2_state.initiate_connection() + self._h2_state.increment_flow_control_window(2**24) + await self._write_outgoing_data(request) + + # Sending the request... + + async def _send_request_headers(self, request: Request, stream_id: int) -> None: + """ + Send the request headers to a given stream ID. + """ + end_stream = not has_body_headers(request) + + # In HTTP/2 the ':authority' pseudo-header is used instead of 'Host'. + # In order to gracefully handle HTTP/1.1 and HTTP/2 we always require + # HTTP/1.1 style headers, and map them appropriately if we end up on + # an HTTP/2 connection. + authority = [v for k, v in request.headers if k.lower() == b"host"][0] + + headers = [ + (b":method", request.method), + (b":authority", authority), + (b":scheme", request.url.scheme), + (b":path", request.url.target), + ] + [ + (k.lower(), v) + for k, v in request.headers + if k.lower() + not in ( + b"host", + b"transfer-encoding", + ) + ] + + self._h2_state.send_headers(stream_id, headers, end_stream=end_stream) + self._h2_state.increment_flow_control_window(2**24, stream_id=stream_id) + await self._write_outgoing_data(request) + + async def _send_request_body(self, request: Request, stream_id: int) -> None: + """ + Iterate over the request body sending it to a given stream ID. + """ + if not has_body_headers(request): + return + + assert isinstance(request.stream, typing.AsyncIterable) + async with safe_async_iterate(request.stream) as iterator: + async for chunk in iterator: + await self._send_stream_data(request, stream_id, chunk) + + await self._send_end_stream(request, stream_id) + + async def _send_stream_data(self, request: Request, stream_id: int, data: bytes) -> None: + """ + Send a single chunk of data in one or more data frames. + """ + position = 0 + while position < len(data): + max_flow = await self._wait_for_outgoing_flow(request, stream_id) + chunk = data[position : position + max_flow] + position += len(chunk) + self._h2_state.send_data(stream_id, chunk) + await self._write_outgoing_data(request) + + async def _send_end_stream(self, request: Request, stream_id: int) -> None: + """ + Send an empty data frame on on a given stream ID with the END_STREAM flag set. + """ + self._h2_state.end_stream(stream_id) + await self._write_outgoing_data(request) + + # Receiving the response... + + async def _receive_response(self, request: Request, stream_id: int) -> tuple[int, list[tuple[bytes, bytes]]]: + """ + Return the response status code and headers for a given stream ID. + """ + while True: + event = await self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.ResponseReceived): + break + + status_code = 200 + headers: list[tuple[bytes, bytes]] = [] + assert event.headers is not None + for k, v in event.headers: + if k == b":status": + status_code = int(v.decode("ascii", errors="ignore")) + elif not k.startswith(b":"): + headers.append((k, v)) + + return (status_code, headers) + + async def _receive_response_body(self, request: Request, stream_id: int) -> AsyncGenerator[bytes]: + """ + Iterator that returns the bytes of the response body for a given stream ID. + """ + while True: + event = await self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.DataReceived): + assert event.flow_controlled_length is not None + assert event.data is not None + amount = event.flow_controlled_length + self._h2_state.acknowledge_received_data(amount, stream_id) + await self._write_outgoing_data(request) + yield event.data + elif isinstance(event, h2.events.StreamEnded): + break + + async def _receive_stream_event( + self, request: Request, stream_id: int + ) -> h2.events.ResponseReceived | h2.events.DataReceived | h2.events.StreamEnded: + """ + Return the next available event for a given stream ID. + + Will read more data from the network if required. + """ + while not self._events.get(stream_id): + await self._receive_events(request, stream_id) + event = self._events[stream_id].pop(0) + if isinstance(event, h2.events.StreamReset): + raise RemoteProtocolError(event) + return event + + async def _receive_events(self, request: Request, stream_id: int | None = None) -> None: + """ + Read some data from the network until we see one or more events + for a given stream ID. + """ + async with self._read_lock: + if self._connection_terminated is not None: + last_stream_id = self._connection_terminated.last_stream_id + if stream_id and last_stream_id and stream_id > last_stream_id: + self._request_count -= 1 + raise ConnectionNotAvailable() + raise RemoteProtocolError(self._connection_terminated) + + # This conditional is a bit icky. We don't want to block reading if we've + # actually got an event to return for a given stream. We need to do that + # check *within* the atomic read lock. Though it also need to be optional, + # because when we call it from `_wait_for_outgoing_flow` we *do* want to + # block until we've available flow control, event when we have events + # pending for the stream ID we're attempting to send on. + if stream_id is None or not self._events.get(stream_id): + events = await self._read_incoming_data(request) + for event in events: + if isinstance(event, h2.events.RemoteSettingsChanged): + async with Trace("receive_remote_settings", logger, request) as trace: + await self._receive_remote_settings_change(event) + trace.return_value = event + + elif isinstance( + event, + ( + h2.events.ResponseReceived, + h2.events.DataReceived, + h2.events.StreamEnded, + h2.events.StreamReset, + ), + ): + if event.stream_id in self._events: + self._events[event.stream_id].append(event) + + elif isinstance(event, h2.events.ConnectionTerminated): + self._connection_terminated = event + + await self._write_outgoing_data(request) + + async def _receive_remote_settings_change(self, event: h2.events.RemoteSettingsChanged) -> None: + max_concurrent_streams = event.changed_settings.get(h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS) + if max_concurrent_streams: + new_max_streams = min( + max_concurrent_streams.new_value, + self._h2_state.local_settings.max_concurrent_streams, + ) + if new_max_streams and new_max_streams != self._max_streams: + while new_max_streams > self._max_streams: + await self._max_streams_semaphore.release() + self._max_streams += 1 + while new_max_streams < self._max_streams: + await self._max_streams_semaphore.acquire() + self._max_streams -= 1 + + async def _response_closed(self, stream_id: int) -> None: + async with self._state_lock: + if stream_id in self._events: + await self._max_streams_semaphore.release() + del self._events[stream_id] + if self._connection_terminated and not self._events: + await self.aclose() + + elif self._state == HTTPConnectionState.ACTIVE and not self._events: + self._state = HTTPConnectionState.IDLE + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + if self._used_all_stream_ids: # pragma: no cover + await self.aclose() + + async def aclose(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._h2_state.close_connection() + self._state = HTTPConnectionState.CLOSED + await self._network_stream.aclose() + + # Wrappers around network read/write operations... + + async def _read_incoming_data(self, request: Request) -> list[h2.events.Event]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + if self._read_exception is not None: + raise self._read_exception # pragma: no cover + + try: + data = await self._network_stream.read(self.READ_NUM_BYTES, timeout) + if data == b"": + raise RemoteProtocolError("Server disconnected") + except Exception as exc: + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future reads. + # (For example, this means that a single read timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._read_exception = exc + self._connection_error = True + raise exc + + events: list[h2.events.Event] = self._h2_state.receive_data(data) + + return events + + async def _write_outgoing_data(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + async with self._write_lock: + data_to_send = self._h2_state.data_to_send() + + if self._write_exception is not None: + raise self._write_exception # pragma: no cover + + try: + await self._network_stream.write(data_to_send, timeout) + except Exception as exc: # pragma: no cover + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future write. + # (For example, this means that a single write timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._write_exception = exc + self._connection_error = True + raise exc + + # Flow control... + + async def _wait_for_outgoing_flow(self, request: Request, stream_id: int) -> int: + """ + Returns the maximum allowable outgoing flow for a given stream. + + If the allowable flow is zero, then waits on the network until + WindowUpdated frames have increased the flow rate. + https://tools.ietf.org/html/rfc7540#section-6.9 + """ + local_flow: int = self._h2_state.local_flow_control_window(stream_id) + max_frame_size: int = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + while flow <= 0: + await self._receive_events(request) + local_flow = self._h2_state.local_flow_control_window(stream_id) + max_frame_size = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + return flow + + # Interface for connection pooling... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_connected(self) -> bool: + return not self.is_closed() + + def is_available(self) -> bool: + return ( + self._state != HTTPConnectionState.CLOSED + and not self._connection_error + and not self._used_all_stream_ids + and not (self._h2_state.state_machine.state == h2.connection.ConnectionState.CLOSED) + ) + + def has_expired(self) -> bool: + now = time.monotonic() + # Read `_expire_at` once into a local: on free-threaded builds another + # thread may reset it to `None` between the check and the comparison. + expire_at = self._expire_at + return expire_at is not None and now > expire_at + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def can_multiplex(self) -> bool: + return True + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return f"{origin!r}, HTTP/2, {self._state.name}, Request Count: {self._request_count}" + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return f"<{class_name} [{origin!r}, {self._state.name}, Request Count: {self._request_count}]>" + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + async def __aenter__(self) -> AsyncHTTP2Connection: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + await self.aclose() + + +class HTTP2ConnectionByteStream: + def __init__(self, connection: AsyncHTTP2Connection, request: Request, stream_id: int) -> None: + self._connection = connection + self._request = request + self._stream_id = stream_id + self._closed = False + + async def __aiter__(self) -> AsyncGenerator[bytes]: + kwargs = {"request": self._request, "stream_id": self._stream_id} + try: + async with Trace("receive_response_body", logger, self._request, kwargs): + async with safe_async_iterate( + self._connection._receive_response_body(request=self._request, stream_id=self._stream_id) + ) as iterator: + async for chunk in iterator: + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with AsyncShieldCancellation(): + await self.aclose() + raise exc + + async def aclose(self) -> None: + if not self._closed: + self._closed = True + kwargs = {"stream_id": self._stream_id} + async with Trace("response_closed", logger, self._request, kwargs): + await self._connection._response_closed(stream_id=self._stream_id) diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/http_proxy.py b/venv/lib/python3.12/site-packages/httpcore2/_async/http_proxy.py new file mode 100644 index 0000000..b4aa31c --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_async/http_proxy.py @@ -0,0 +1,353 @@ +from __future__ import annotations + +import base64 +import logging +import ssl +import typing + +from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend +from .._exceptions import ProxyError +from .._models import ( + URL, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, +) +from .._ssl import default_ssl_context +from .._synchronization import AsyncLock +from .._trace import Trace +from .connection import AsyncHTTPConnection +from .connection_pool import AsyncConnectionPool +from .http11 import AsyncHTTP11Connection +from .interfaces import AsyncConnectionInterface + +ByteOrStr = bytes | str +HeadersAsSequence = typing.Sequence[tuple[ByteOrStr, ByteOrStr]] +HeadersAsMapping = typing.Mapping[ByteOrStr, ByteOrStr] + + +logger = logging.getLogger("httpcore2.proxy") + + +def merge_headers( + default_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, + override_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, +) -> list[tuple[bytes, bytes]]: + """ + Append default_headers and override_headers, de-duplicating if a key exists + in both cases. + """ + default_headers = [] if default_headers is None else list(default_headers) + override_headers = [] if override_headers is None else list(override_headers) + has_override = set(key.lower() for key, _value in override_headers) + default_headers = [(key, value) for key, value in default_headers if key.lower() not in has_override] + return default_headers + override_headers + + +class AsyncHTTPProxy(AsyncConnectionPool): # pragma: no cover + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: URL | bytes | str, + proxy_auth: tuple[bytes | str, bytes | str] | None = None, + proxy_headers: HeadersAsMapping | HeadersAsSequence | None = None, + ssl_context: ssl.SSLContext | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + proxy_auth: Any proxy authentication as a two-tuple of + (username, password). May be either bytes or ascii-only str. + proxy_headers: Any HTTP headers to use for the proxy requests. + For example `{"Proxy-Authorization": "Basic :"}`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore2.default_ssl_context()` + will be used. + proxy_ssl_context: The same as `ssl_context`, but for a proxy server rather than a remote origin. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + local_address=local_address, + uds=uds, + socket_options=socket_options, + ) + + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if self._proxy_url.scheme == b"http" and proxy_ssl_context is not None: # pragma: no cover + raise RuntimeError("The `proxy_ssl_context` argument is not allowed for the http scheme") + + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + if proxy_auth is not None: + username = enforce_bytes(proxy_auth[0], name="proxy_auth") + password = enforce_bytes(proxy_auth[1], name="proxy_auth") + userpass = username + b":" + password + authorization = b"Basic " + base64.b64encode(userpass) + self._proxy_headers = [(b"Proxy-Authorization", authorization)] + self._proxy_headers + + def create_connection(self, origin: Origin) -> AsyncConnectionInterface: + if origin.scheme == b"http": + return AsyncForwardHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + proxy_ssl_context=self._proxy_ssl_context, + ) + return AsyncTunnelHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + ssl_context=self._ssl_context, + proxy_ssl_context=self._proxy_ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class AsyncForwardHTTPConnection(AsyncConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_headers: HeadersAsMapping | HeadersAsSequence | None = None, + keepalive_expiry: float | None = None, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + ) -> None: + self._connection = AsyncHTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._remote_origin = remote_origin + + async def handle_async_request(self, request: Request) -> Response: + headers = merge_headers(self._proxy_headers, request.headers) + url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=bytes(request.url), + ) + proxy_request = Request( + method=request.method, + url=url, + headers=headers, + content=request.stream, + extensions={key: value for key, value in request.extensions.items() if key != "sni_hostname"}, + ) + return await self._connection.handle_async_request(proxy_request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + async def aclose(self) -> None: + await self._connection.aclose() + + def info(self) -> str: + return self._connection.info() + + def is_connected(self) -> bool: + return self._connection.is_connected() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + +class AsyncTunnelHTTPConnection(AsyncConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + ssl_context: ssl.SSLContext | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + proxy_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + self._connection: AsyncConnectionInterface = AsyncHTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._connect_lock = AsyncLock() + self._connected = False + + async def handle_async_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("connect", None) + + async with self._connect_lock: + if not self._connected: + target = b"%b:%d" % (self._remote_origin.host, self._remote_origin.port) + + connect_url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=target, + ) + connect_headers = merge_headers([(b"Host", target), (b"Accept", b"*/*")], self._proxy_headers) + connect_request = Request( + method=b"CONNECT", + url=connect_url, + headers=connect_headers, + extensions={key: value for key, value in request.extensions.items() if key != "sni_hostname"}, + ) + connect_response = await self._connection.handle_async_request(connect_request) + + if connect_response.status < 200 or connect_response.status > 299: + reason_bytes = connect_response.extensions.get("reason_phrase", b"") + reason_str = reason_bytes.decode("ascii", errors="ignore") + msg = f"{connect_response.status} {reason_str}" + await self._connection.aclose() + raise ProxyError(msg) + + stream = connect_response.extensions["network_stream"] + + # Upgrade the stream to SSL + ssl_context = default_ssl_context() if self._ssl_context is None else self._ssl_context + alpn_protocols = (["h2", "http/1.1"] if self._http1 else ["h2"]) if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + server_hostname = request.extensions.get("sni_hostname") or self._remote_origin.host.decode("ascii") + kwargs = { + "ssl_context": ssl_context, + "server_hostname": server_hostname, + "timeout": timeout, + } + async with Trace("start_tls", logger, request, kwargs) as trace: + stream = await stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ssl_object is not None and ssl_object.selected_alpn_protocol() == "h2" + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import AsyncHTTP2Connection + + self._connection = AsyncHTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = AsyncHTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + + self._connected = True + return await self._connection.handle_async_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + async def aclose(self) -> None: + await self._connection.aclose() + + def info(self) -> str: + return self._connection.info() + + def is_connected(self) -> bool: + return self._connection.is_connected() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/interfaces.py b/venv/lib/python3.12/site-packages/httpcore2/_async/interfaces.py new file mode 100644 index 0000000..008d295 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_async/interfaces.py @@ -0,0 +1,160 @@ +from __future__ import annotations + +import contextlib +import typing +from collections.abc import AsyncGenerator + +from .._models import ( + URL, + Extensions, + HeaderTypes, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, + include_request_headers, +) + + +class AsyncRequestInterface: + async def request( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.AsyncIterator[bytes] | None = None, + extensions: Extensions | None = None, + ) -> Response: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = await self.handle_async_request(request) + try: + await response.aread() + finally: + await response.aclose() + return response + + @contextlib.asynccontextmanager + async def stream( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.AsyncIterator[bytes] | None = None, + extensions: Extensions | None = None, + ) -> AsyncGenerator[Response]: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = await self.handle_async_request(request) + try: + yield response + finally: + await response.aclose() + + async def handle_async_request(self, request: Request) -> Response: + raise NotImplementedError() # pragma: no cover + + +class AsyncConnectionInterface(AsyncRequestInterface): + async def aclose(self) -> None: + raise NotImplementedError() # pragma: no cover + + def info(self) -> str: + raise NotImplementedError() # pragma: no cover + + def can_handle_request(self, origin: Origin) -> bool: + raise NotImplementedError() # pragma: no cover + + def is_connected(self) -> bool: + """ + Return `True` if the connection is open (the underlying socket has been + established). A connection in the NEW state (just created but not yet + connected) returns `False`. + + Note: for some implementations `is_connected() != not is_closed()`. + The default implementation returns `not self.is_closed()`, which is + correct for connections that are never in the NEW (pre-TCP) state. + """ + return not self.is_closed() # pragma: no cover + + def is_available(self) -> bool: + """ + Return `True` if the connection is currently able to accept an + outgoing request. + + An HTTP/1.1 connection will only be available if it is currently idle. + + An HTTP/2 connection will be available so long as the stream ID space is + not yet exhausted, and the connection is not in an error state. + + While the connection is being established we may not yet know if it is going + to result in an HTTP/1.1 or HTTP/2 connection. The connection should be + treated as being available, but might ultimately raise `NewConnectionRequired` + required exceptions if multiple requests are attempted over a connection + that ends up being established as HTTP/1.1. + """ + raise NotImplementedError() # pragma: no cover + + def has_expired(self) -> bool: + """ + Return `True` if the connection is in a state where it should be closed. + + This either means that the connection is idle and it has passed the + expiry time on its keep-alive, or that server has sent an EOF. + """ + raise NotImplementedError() # pragma: no cover + + def is_idle(self) -> bool: + """ + Return `True` if the connection is currently idle. + """ + raise NotImplementedError() # pragma: no cover + + def can_multiplex(self) -> bool: + """ + Return `True` if the connection can serve multiple requests + concurrently, such as an established HTTP/2 connection. + + The default covers HTTP/1.1-style implementations, which serve a + single request at a time. + """ + return False + + def is_closed(self) -> bool: + """ + Return `True` if the connection has been closed. + + Used when a response is closed to determine if the connection may be + returned to the connection pool or not. + """ + raise NotImplementedError() # pragma: no cover diff --git a/venv/lib/python3.12/site-packages/httpcore2/_async/socks_proxy.py b/venv/lib/python3.12/site-packages/httpcore2/_async/socks_proxy.py new file mode 100644 index 0000000..2bc347b --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_async/socks_proxy.py @@ -0,0 +1,327 @@ +from __future__ import annotations + +import logging +import ssl + +import socksio + +from .._backends.auto import AutoBackend +from .._backends.base import AsyncNetworkBackend, AsyncNetworkStream +from .._exceptions import ConnectionNotAvailable, ProxyError +from .._models import URL, Origin, Request, Response, enforce_bytes, enforce_url +from .._ssl import default_ssl_context +from .._synchronization import AsyncLock +from .._trace import Trace +from .connection_pool import AsyncConnectionPool +from .http11 import AsyncHTTP11Connection +from .interfaces import AsyncConnectionInterface + +logger = logging.getLogger("httpcore2.socks") + + +AUTH_METHODS = { + b"\x00": "NO AUTHENTICATION REQUIRED", + b"\x01": "GSSAPI", + b"\x02": "USERNAME/PASSWORD", + b"\xff": "NO ACCEPTABLE METHODS", +} + +REPLY_CODES = { + b"\x00": "Succeeded", + b"\x01": "General SOCKS server failure", + b"\x02": "Connection not allowed by ruleset", + b"\x03": "Network unreachable", + b"\x04": "Host unreachable", + b"\x05": "Connection refused", + b"\x06": "TTL expired", + b"\x07": "Command not supported", + b"\x08": "Address type not supported", +} + + +async def _init_socks5_connection( + stream: AsyncNetworkStream, + *, + host: bytes, + port: int, + auth: tuple[bytes, bytes] | None = None, + timeouts: dict[str, float | None] | None = None, +) -> None: + timeouts = timeouts or {} + write_timeout = timeouts.get("write", None) + read_timeout = timeouts.get("read", None) + conn = socksio.socks5.SOCKS5Connection() + + # Auth method request + auth_method = ( + socksio.socks5.SOCKS5AuthMethod.NO_AUTH_REQUIRED + if auth is None + else socksio.socks5.SOCKS5AuthMethod.USERNAME_PASSWORD + ) + conn.send(socksio.socks5.SOCKS5AuthMethodsRequest([auth_method])) + outgoing_bytes = conn.data_to_send() + await stream.write(outgoing_bytes, timeout=write_timeout) + + # Auth method response + incoming_bytes = await stream.read(max_bytes=4096, timeout=read_timeout) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5AuthReply) + if response.method != auth_method: + requested = AUTH_METHODS.get(auth_method, "UNKNOWN") + responded = AUTH_METHODS.get(response.method, "UNKNOWN") + raise ProxyError(f"Requested {requested} from proxy server, but got {responded}.") + + if response.method == socksio.socks5.SOCKS5AuthMethod.USERNAME_PASSWORD: + # Username/password request + assert auth is not None + username, password = auth + conn.send(socksio.socks5.SOCKS5UsernamePasswordRequest(username, password)) + outgoing_bytes = conn.data_to_send() + await stream.write(outgoing_bytes, timeout=write_timeout) + + # Username/password response + incoming_bytes = await stream.read(max_bytes=4096, timeout=read_timeout) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5UsernamePasswordReply) + if not response.success: + raise ProxyError("Invalid username/password") + + # Connect request + conn.send(socksio.socks5.SOCKS5CommandRequest.from_address(socksio.socks5.SOCKS5Command.CONNECT, (host, port))) + outgoing_bytes = conn.data_to_send() + await stream.write(outgoing_bytes, timeout=write_timeout) + + # Connect response + incoming_bytes = await stream.read(max_bytes=4096, timeout=read_timeout) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5Reply) + if response.reply_code != socksio.socks5.SOCKS5ReplyCode.SUCCEEDED: + reply_code = REPLY_CODES.get(response.reply_code, "UNKOWN") + raise ProxyError(f"Proxy Server could not connect: {reply_code}.") + + +class AsyncSOCKSProxy(AsyncConnectionPool): # pragma: no cover + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: URL | bytes | str, + proxy_auth: tuple[bytes | str, bytes | str] | None = None, + ssl_context: ssl.SSLContext | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + network_backend: AsyncNetworkBackend | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore2.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + ) + self._ssl_context = ssl_context + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if proxy_auth is not None: + username, password = proxy_auth + username_bytes = enforce_bytes(username, name="proxy_auth") + password_bytes = enforce_bytes(password, name="proxy_auth") + self._proxy_auth: tuple[bytes, bytes] | None = ( + username_bytes, + password_bytes, + ) + else: + self._proxy_auth = None + + def create_connection(self, origin: Origin) -> AsyncConnectionInterface: + return AsyncSocks5Connection( + proxy_origin=self._proxy_url.origin, + remote_origin=origin, + proxy_auth=self._proxy_auth, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class AsyncSocks5Connection(AsyncConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_auth: tuple[bytes, bytes] | None = None, + ssl_context: ssl.SSLContext | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + network_backend: AsyncNetworkBackend | None = None, + ) -> None: + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._proxy_auth = proxy_auth + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + + self._network_backend: AsyncNetworkBackend = AutoBackend() if network_backend is None else network_backend + self._connect_lock = AsyncLock() + self._connection: AsyncConnectionInterface | None = None + self._connect_failed = False + + async def handle_async_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + async with self._connect_lock: + if self._connection is None: + try: + # Connect to the proxy + kwargs = { + "host": self._proxy_origin.host.decode("ascii"), + "port": self._proxy_origin.port, + "timeout": timeout, + } + async with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = await self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + + # Connect to the remote host using socks5 + kwargs = { + "stream": stream, + "host": self._remote_origin.host.decode("ascii"), + "port": self._remote_origin.port, + "auth": self._proxy_auth, + "timeouts": timeouts, + } + async with Trace("setup_socks5_connection", logger, request, kwargs) as trace: + await _init_socks5_connection(**kwargs) + trace.return_value = stream + + # Upgrade the stream to SSL + if self._remote_origin.scheme in (b"https", b"wss"): + ssl_context = default_ssl_context() if self._ssl_context is None else self._ssl_context + alpn_protocols = ( + (["h2", "http/1.1"] if self._http1 else ["h2"]) if self._http2 else ["http/1.1"] + ) + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname or self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + async with Trace("start_tls", logger, request, kwargs) as trace: + stream = await stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ssl_object is not None and ssl_object.selected_alpn_protocol() == "h2" + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or (self._http2 and not self._http1): # pragma: no cover + from .http2 import AsyncHTTP2Connection + + self._connection = AsyncHTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = AsyncHTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except Exception as exc: + self._connect_failed = True + raise exc + elif not self._connection.is_available(): # pragma: no cover + raise ConnectionNotAvailable() + + return await self._connection.handle_async_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + async def aclose(self) -> None: + if self._connection is not None: + await self._connection.aclose() + + def is_connected(self) -> bool: + return self._connection is not None and self._connection.is_connected() + + def is_available(self) -> bool: + if self._connection is None: # pragma: no cover + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 and (self._remote_origin.scheme == b"https" or not self._http1) and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: # pragma: no cover + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: # pragma: no cover + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: # pragma: no cover + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: # pragma: no cover + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/__init__.py b/venv/lib/python3.12/site-packages/httpcore2/_backends/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4dbd9eb Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/anyio.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/anyio.cpython-312.pyc new file mode 100644 index 0000000..2b3096e Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/anyio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/auto.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/auto.cpython-312.pyc new file mode 100644 index 0000000..71bf8fa Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/auto.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..f8ca710 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/mock.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/mock.cpython-312.pyc new file mode 100644 index 0000000..d79478f Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/mock.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/sync.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/sync.cpython-312.pyc new file mode 100644 index 0000000..654ddb4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/sync.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/trio.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/trio.cpython-312.pyc new file mode 100644 index 0000000..1c914ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_backends/__pycache__/trio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/anyio.py b/venv/lib/python3.12/site-packages/httpcore2/_backends/anyio.py new file mode 100644 index 0000000..16c270e --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_backends/anyio.py @@ -0,0 +1,148 @@ +from __future__ import annotations + +import ssl +import typing + +import anyio +import anyio.abc +import anyio.streams.tls + +from .._exceptions import ( + ConnectError, + ConnectTimeout, + ReadError, + ReadTimeout, + WriteError, + WriteTimeout, + map_exceptions, +) +from .._utils import is_socket_readable +from .base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream + + +class AnyIOStream(AsyncNetworkStream): + def __init__(self, stream: anyio.abc.ByteStream) -> None: + self._stream = stream + + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + exc_map: dict[type[Exception], type[Exception]] = { + TimeoutError: ReadTimeout, + anyio.BrokenResourceError: ReadError, + anyio.ClosedResourceError: ReadError, + anyio.EndOfStream: ReadError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + try: + return await self._stream.receive(max_bytes=max_bytes) + except anyio.EndOfStream: # pragma: no cover + return b"" + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + if not buffer: + return + + exc_map: dict[type[Exception], type[Exception]] = { + TimeoutError: WriteTimeout, + anyio.BrokenResourceError: WriteError, + anyio.ClosedResourceError: WriteError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + await self._stream.send(item=buffer) + + async def aclose(self) -> None: + await self._stream.aclose() + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + exc_map: dict[type[Exception], type[Exception]] = { + TimeoutError: ConnectTimeout, + anyio.BrokenResourceError: ConnectError, + anyio.EndOfStream: ConnectError, + ssl.SSLError: ConnectError, + } + with map_exceptions(exc_map): + try: + with anyio.fail_after(timeout): + ssl_stream = await anyio.streams.tls.TLSStream.wrap( + self._stream, + ssl_context=ssl_context, + hostname=server_hostname, + standard_compatible=False, + server_side=False, + ) + except Exception as exc: # pragma: no cover + await self.aclose() + raise exc + return AnyIOStream(ssl_stream) + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object": + return self._stream.extra(anyio.streams.tls.TLSAttribute.ssl_object, None) + if info == "client_addr": + return self._stream.extra(anyio.abc.SocketAttribute.local_address, None) + if info == "server_addr": + return self._stream.extra(anyio.abc.SocketAttribute.remote_address, None) + if info == "socket": + return self._stream.extra(anyio.abc.SocketAttribute.raw_socket, None) + if info == "is_readable": + sock = self._stream.extra(anyio.abc.SocketAttribute.raw_socket, None) + return is_socket_readable(sock) + return None + + +class AnyIOBackend(AsyncNetworkBackend): + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: # pragma: no cover + if socket_options is None: + socket_options = [] + exc_map: dict[type[Exception], type[Exception]] = { + TimeoutError: ConnectTimeout, + OSError: ConnectError, + anyio.BrokenResourceError: ConnectError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + stream: anyio.abc.ByteStream = await anyio.connect_tcp( + remote_host=host, + remote_port=port, + local_host=local_address, + ) + # By default TCP sockets opened in `asyncio` include TCP_NODELAY. + for option in socket_options: + stream._raw_socket.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return AnyIOStream(stream) + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: # pragma: no cover + if socket_options is None: + socket_options = [] + exc_map: dict[type[Exception], type[Exception]] = { + TimeoutError: ConnectTimeout, + OSError: ConnectError, + anyio.BrokenResourceError: ConnectError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + stream: anyio.abc.ByteStream = await anyio.connect_unix(path) + for option in socket_options: + stream._raw_socket.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return AnyIOStream(stream) + + async def sleep(self, seconds: float) -> None: + await anyio.sleep(seconds) # pragma: no cover diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/auto.py b/venv/lib/python3.12/site-packages/httpcore2/_backends/auto.py new file mode 100644 index 0000000..c946c9e --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_backends/auto.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +import typing + +from .._synchronization import current_async_library +from .base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream + + +class AutoBackend(AsyncNetworkBackend): + async def _init_backend(self) -> None: + if not (hasattr(self, "_backend")): + backend = current_async_library() + if backend == "trio": + from .trio import TrioBackend + + self._backend: AsyncNetworkBackend = TrioBackend() + else: + from .anyio import AnyIOBackend + + self._backend = AnyIOBackend() + + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + await self._init_backend() + return await self._backend.connect_tcp( + host, + port, + timeout=timeout, + local_address=local_address, + socket_options=socket_options, + ) + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: # pragma: no cover + await self._init_backend() + return await self._backend.connect_unix_socket(path, timeout=timeout, socket_options=socket_options) + + async def sleep(self, seconds: float) -> None: # pragma: no cover + await self._init_backend() + return await self._backend.sleep(seconds) diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/base.py b/venv/lib/python3.12/site-packages/httpcore2/_backends/base.py new file mode 100644 index 0000000..da0d72d --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_backends/base.py @@ -0,0 +1,97 @@ +from __future__ import annotations + +import ssl +import time +import typing + +SOCKET_OPTION = tuple[int, int, int] | tuple[int, int, bytes | bytearray] | tuple[int, int, None, int] + + +class NetworkStream: + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + raise NotImplementedError() # pragma: no cover + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + raise NotImplementedError() # pragma: no cover + + def close(self) -> None: + raise NotImplementedError() # pragma: no cover + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + raise NotImplementedError() # pragma: no cover + + def get_extra_info(self, info: str) -> typing.Any: + return None # pragma: no cover + + +class NetworkBackend: + def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + raise NotImplementedError() # pragma: no cover + + def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + raise NotImplementedError() # pragma: no cover + + def sleep(self, seconds: float) -> None: + time.sleep(seconds) # pragma: no cover + + +class AsyncNetworkStream: + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + raise NotImplementedError() # pragma: no cover + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + raise NotImplementedError() # pragma: no cover + + async def aclose(self) -> None: + raise NotImplementedError() # pragma: no cover + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + raise NotImplementedError() # pragma: no cover + + def get_extra_info(self, info: str) -> typing.Any: + return None # pragma: no cover + + +class AsyncNetworkBackend: + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + raise NotImplementedError() # pragma: no cover + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + raise NotImplementedError() # pragma: no cover + + async def sleep(self, seconds: float) -> None: + raise NotImplementedError() # pragma: no cover diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/mock.py b/venv/lib/python3.12/site-packages/httpcore2/_backends/mock.py new file mode 100644 index 0000000..d710782 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_backends/mock.py @@ -0,0 +1,143 @@ +from __future__ import annotations + +import ssl +import typing + +from .._exceptions import ReadError +from .base import ( + SOCKET_OPTION, + AsyncNetworkBackend, + AsyncNetworkStream, + NetworkBackend, + NetworkStream, +) + + +class MockSSLObject: + def __init__(self, http2: bool): + self._http2 = http2 + + def selected_alpn_protocol(self) -> str: + return "h2" if self._http2 else "http/1.1" + + +class MockStream(NetworkStream): + def __init__(self, buffer: list[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + self._closed = False + + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + if self._closed: + raise ReadError("Connection closed") + if not self._buffer: + return b"" + return self._buffer.pop(0) + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + pass + + def close(self) -> None: + self._closed = True + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + return self + + def get_extra_info(self, info: str) -> typing.Any: + return MockSSLObject(http2=self._http2) if info == "ssl_object" else None + + def __repr__(self) -> str: + return "" + + +class MockBackend(NetworkBackend): + def __init__(self, buffer: list[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + + def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + return MockStream(list(self._buffer), http2=self._http2) + + def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + return MockStream(list(self._buffer), http2=self._http2) + + def sleep(self, seconds: float) -> None: + pass + + +class AsyncMockStream(AsyncNetworkStream): + def __init__(self, buffer: list[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + self._closed = False + + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + if self._closed: + raise ReadError("Connection closed") + if not self._buffer: + return b"" + return self._buffer.pop(0) + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + pass + + async def aclose(self) -> None: + self._closed = True + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + return self + + def get_extra_info(self, info: str) -> typing.Any: + return MockSSLObject(http2=self._http2) if info == "ssl_object" else None + + def __repr__(self) -> str: + return "" + + +class AsyncMockBackend(AsyncNetworkBackend): + def __init__(self, buffer: list[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + return AsyncMockStream(list(self._buffer), http2=self._http2) + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + return AsyncMockStream(list(self._buffer), http2=self._http2) + + async def sleep(self, seconds: float) -> None: + pass diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/sync.py b/venv/lib/python3.12/site-packages/httpcore2/_backends/sync.py new file mode 100644 index 0000000..54ce542 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_backends/sync.py @@ -0,0 +1,235 @@ +from __future__ import annotations + +import functools +import socket +import ssl +import sys +import typing + +from .._exceptions import ( + ConnectError, + ConnectTimeout, + ExceptionMapping, + ReadError, + ReadTimeout, + WriteError, + WriteTimeout, + map_exceptions, +) +from .._utils import is_socket_readable +from .base import SOCKET_OPTION, NetworkBackend, NetworkStream + + +class TLSinTLSStream(NetworkStream): # pragma: no cover + """ + Because the standard `SSLContext.wrap_socket` method does + not work for `SSLSocket` objects, we need this class + to implement TLS stream using an underlying `SSLObject` + instance in order to support TLS on top of TLS. + """ + + # Defined in RFC 8449 + TLS_RECORD_SIZE = 16384 + + def __init__( + self, + sock: socket.socket, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ): + self._sock = sock + self._incoming = ssl.MemoryBIO() + self._outgoing = ssl.MemoryBIO() + + self.ssl_obj = ssl_context.wrap_bio( + incoming=self._incoming, + outgoing=self._outgoing, + server_hostname=server_hostname, + ) + + self._sock.settimeout(timeout) + self._perform_io(self.ssl_obj.do_handshake) + + def _perform_io( + self, + func: typing.Callable[..., typing.Any], + ) -> typing.Any: + ret = None + + while True: + errno = None + try: + ret = func() + except (ssl.SSLWantReadError, ssl.SSLWantWriteError) as e: + errno = e.errno + + self._sock.sendall(self._outgoing.read()) + + if errno == ssl.SSL_ERROR_WANT_READ: + buf = self._sock.recv(self.TLS_RECORD_SIZE) + + if buf: + self._incoming.write(buf) + else: + self._incoming.write_eof() + if errno is None: + return ret + + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + exc_map: ExceptionMapping = {socket.timeout: ReadTimeout, OSError: ReadError} + with map_exceptions(exc_map): + self._sock.settimeout(timeout) + return typing.cast(bytes, self._perform_io(functools.partial(self.ssl_obj.read, max_bytes))) + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + exc_map: ExceptionMapping = {socket.timeout: WriteTimeout, OSError: WriteError} + with map_exceptions(exc_map): + self._sock.settimeout(timeout) + view = memoryview(buffer) # zero-copy slicing; avoids copies + while view: + nsent = self._perform_io(functools.partial(self.ssl_obj.write, view)) + view = view[nsent:] + + def close(self) -> None: + self._sock.close() + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + raise NotImplementedError() + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object": + return self.ssl_obj + if info == "client_addr": + return self._sock.getsockname() + if info == "server_addr": + return self._sock.getpeername() + if info == "socket": + return self._sock + if info == "is_readable": + return is_socket_readable(self._sock) + return None + + +class SyncStream(NetworkStream): + def __init__(self, sock: socket.socket) -> None: + self._sock = sock + + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + exc_map: ExceptionMapping = {socket.timeout: ReadTimeout, OSError: ReadError} + with map_exceptions(exc_map): + self._sock.settimeout(timeout) + return self._sock.recv(max_bytes) + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + if not buffer: + return + + exc_map: ExceptionMapping = {socket.timeout: WriteTimeout, OSError: WriteError} + with map_exceptions(exc_map): + view = memoryview(buffer) # zero-copy slicing; avoids copies + while view: + self._sock.settimeout(timeout) + n = self._sock.send(view) + view = view[n:] + + def close(self) -> None: + self._sock.close() + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + exc_map: ExceptionMapping = { + socket.timeout: ConnectTimeout, + OSError: ConnectError, + } + with map_exceptions(exc_map): + try: + if isinstance(self._sock, ssl.SSLSocket): # pragma: no cover + # If the underlying socket has already been upgraded + # to the TLS layer (i.e. is an instance of SSLSocket), + # we need some additional smarts to support TLS-in-TLS. + return TLSinTLSStream(self._sock, ssl_context, server_hostname, timeout) + else: + self._sock.settimeout(timeout) + sock = ssl_context.wrap_socket(self._sock, server_hostname=server_hostname) + except Exception as exc: # pragma: no cover + self.close() + raise exc + return SyncStream(sock) + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object" and isinstance(self._sock, ssl.SSLSocket): + return self._sock._sslobj # type: ignore + if info == "client_addr": + return self._sock.getsockname() + if info == "server_addr": + return self._sock.getpeername() + if info == "socket": + return self._sock + if info == "is_readable": + return is_socket_readable(self._sock) + return None + + +class SyncBackend(NetworkBackend): + def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + # Note that we automatically include `TCP_NODELAY` + # in addition to any other custom socket options. + if socket_options is None: + socket_options = [] # pragma: no cover + address = (host, port) + source_address = None if local_address is None else (local_address, 0) + exc_map: ExceptionMapping = { + socket.timeout: ConnectTimeout, + OSError: ConnectError, + } + + with map_exceptions(exc_map): + sock = socket.create_connection( + address, + timeout, + source_address=source_address, + ) + for option in socket_options: + sock.setsockopt(*option) # pragma: no cover + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + return SyncStream(sock) + + def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: # pragma: no cover + if sys.platform == "win32": + raise RuntimeError("Attempted to connect to a UNIX socket on a Windows system.") + if socket_options is None: + socket_options = [] + + exc_map: ExceptionMapping = { + socket.timeout: ConnectTimeout, + OSError: ConnectError, + } + with map_exceptions(exc_map): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + for option in socket_options: + sock.setsockopt(*option) + sock.settimeout(timeout) + sock.connect(path) + return SyncStream(sock) diff --git a/venv/lib/python3.12/site-packages/httpcore2/_backends/trio.py b/venv/lib/python3.12/site-packages/httpcore2/_backends/trio.py new file mode 100644 index 0000000..742985b --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_backends/trio.py @@ -0,0 +1,157 @@ +from __future__ import annotations + +import ssl +import typing + +import trio + +from .._exceptions import ( + ConnectError, + ConnectTimeout, + ExceptionMapping, + ReadError, + ReadTimeout, + WriteError, + WriteTimeout, + map_exceptions, +) +from .base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream + + +class TrioStream(AsyncNetworkStream): + def __init__(self, stream: trio.abc.Stream) -> None: + self._stream = stream + + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ReadTimeout, + trio.BrokenResourceError: ReadError, + trio.ClosedResourceError: ReadError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + data: bytes = await self._stream.receive_some(max_bytes=max_bytes) + return data + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + if not buffer: + return + + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: WriteTimeout, + trio.BrokenResourceError: WriteError, + trio.ClosedResourceError: WriteError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + await self._stream.send_all(data=buffer) + + async def aclose(self) -> None: + await self._stream.aclose() + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ConnectTimeout, + trio.BrokenResourceError: ConnectError, + } + ssl_stream = trio.SSLStream( + self._stream, + ssl_context=ssl_context, + server_hostname=server_hostname, + https_compatible=True, + server_side=False, + ) + with map_exceptions(exc_map): + try: + with trio.fail_after(timeout_or_inf): + await ssl_stream.do_handshake() + except Exception as exc: # pragma: no cover + await self.aclose() + raise exc + return TrioStream(ssl_stream) + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object" and isinstance(self._stream, trio.SSLStream): + # Type checkers cannot see `_ssl_object` attribute because trio._ssl.SSLStream uses __getattr__/__setattr__. + # Tracked at https://github.com/python-trio/trio/issues/542 + return self._stream._ssl_object # type: ignore[attr-defined] + if info == "client_addr": + return self._get_socket_stream().socket.getsockname() + if info == "server_addr": + return self._get_socket_stream().socket.getpeername() + if info == "socket": + stream = self._stream + while isinstance(stream, trio.SSLStream): + stream = stream.transport_stream + assert isinstance(stream, trio.SocketStream) + return stream.socket + if info == "is_readable": + socket = self.get_extra_info("socket") + return socket.is_readable() + return None + + def _get_socket_stream(self) -> trio.SocketStream: + stream = self._stream + while isinstance(stream, trio.SSLStream): + stream = stream.transport_stream + assert isinstance(stream, trio.SocketStream) + return stream + + +class TrioBackend(AsyncNetworkBackend): + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + # By default for TCP sockets, trio enables TCP_NODELAY. + # https://trio.readthedocs.io/en/stable/reference-io.html#trio.SocketStream + if socket_options is None: + socket_options = [] # pragma: no cover + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ConnectTimeout, + trio.BrokenResourceError: ConnectError, + OSError: ConnectError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + stream: trio.abc.Stream = await trio.open_tcp_stream(host=host, port=port, local_address=local_address) + for option in socket_options: + stream.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return TrioStream(stream) + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: # pragma: no cover + if socket_options is None: + socket_options = [] + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ConnectTimeout, + trio.BrokenResourceError: ConnectError, + OSError: ConnectError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + stream: trio.abc.Stream = await trio.open_unix_socket(path) + for option in socket_options: + stream.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return TrioStream(stream) + + async def sleep(self, seconds: float) -> None: + await trio.sleep(seconds) # pragma: no cover diff --git a/venv/lib/python3.12/site-packages/httpcore2/_exceptions.py b/venv/lib/python3.12/site-packages/httpcore2/_exceptions.py new file mode 100644 index 0000000..a54d43b --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_exceptions.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +import contextlib +import typing +from collections.abc import Generator + +ExceptionMapping = typing.Mapping[type[Exception], type[Exception]] + + +@contextlib.contextmanager +def map_exceptions(map: ExceptionMapping) -> Generator[None]: + try: + yield + except Exception as exc: # noqa: PIE786 + for from_exc, to_exc in map.items(): + if isinstance(exc, from_exc): + raise to_exc(exc) from exc + raise # pragma: no cover + + +class ConnectionNotAvailable(Exception): + pass + + +class ProxyError(Exception): + pass + + +class UnsupportedProtocol(Exception): + pass + + +class ProtocolError(Exception): + pass + + +class RemoteProtocolError(ProtocolError): + pass + + +class LocalProtocolError(ProtocolError): + pass + + +# Timeout errors + + +class TimeoutException(Exception): + pass + + +class PoolTimeout(TimeoutException): + pass + + +class ConnectTimeout(TimeoutException): + pass + + +class ReadTimeout(TimeoutException): + pass + + +class WriteTimeout(TimeoutException): + pass + + +# Network errors + + +class NetworkError(Exception): + pass + + +class ConnectError(NetworkError): + pass + + +class ReadError(NetworkError): + pass + + +class WriteError(NetworkError): + pass diff --git a/venv/lib/python3.12/site-packages/httpcore2/_models.py b/venv/lib/python3.12/site-packages/httpcore2/_models.py new file mode 100644 index 0000000..c8e4f0c --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_models.py @@ -0,0 +1,490 @@ +from __future__ import annotations + +import base64 +import ssl +import typing +import urllib.parse +from collections.abc import AsyncGenerator + +from ._utils import safe_async_iterate + +# Functions for typechecking... + + +ByteOrStr = bytes | str +HeadersAsSequence = typing.Sequence[tuple[ByteOrStr, ByteOrStr]] +HeadersAsMapping = typing.Mapping[ByteOrStr, ByteOrStr] +HeaderTypes = HeadersAsSequence | HeadersAsMapping | None + +Extensions = typing.MutableMapping[str, typing.Any] + + +def enforce_bytes(value: bytes | str, *, name: str) -> bytes: + """ + Any arguments that are ultimately represented as bytes can be specified + either as bytes or as strings. + + However we enforce that any string arguments must only contain characters in + the plain ASCII range. chr(0)...chr(127). If you need to use characters + outside that range then be precise, and use a byte-wise argument. + """ + if isinstance(value, str): + try: + return value.encode("ascii") + except UnicodeEncodeError: + raise TypeError(f"{name} strings may not include unicode characters.") + elif isinstance(value, bytes): + return value + + seen_type = type(value).__name__ + raise TypeError(f"{name} must be bytes or str, but got {seen_type}.") + + +def enforce_url(value: URL | bytes | str, *, name: str) -> URL: + """ + Type check for URL parameters. + """ + if isinstance(value, (bytes, str)): + return URL(value) + elif isinstance(value, URL): + return value + + seen_type = type(value).__name__ + raise TypeError(f"{name} must be a URL, bytes, or str, but got {seen_type}.") + + +def enforce_headers( + value: HeadersAsMapping | HeadersAsSequence | None = None, *, name: str +) -> list[tuple[bytes, bytes]]: + """ + Convenience function that ensure all items in request or response headers + are either bytes or strings in the plain ASCII range. + """ + if value is None: + return [] + elif isinstance(value, typing.Mapping): + return [ + ( + enforce_bytes(k, name="header name"), + enforce_bytes(v, name="header value"), + ) + for k, v in value.items() + ] + elif isinstance(value, typing.Sequence): + return [ + ( + enforce_bytes(k, name="header name"), + enforce_bytes(v, name="header value"), + ) + for k, v in value + ] + + seen_type = type(value).__name__ + raise TypeError(f"{name} must be a mapping or sequence of two-tuples, but got {seen_type}.") + + +def enforce_stream( + value: bytes | typing.Iterable[bytes] | typing.AsyncIterable[bytes] | None, + *, + name: str, +) -> typing.Iterable[bytes] | typing.AsyncIterable[bytes]: + if value is None: + return ByteStream(b"") + elif isinstance(value, bytes): + return ByteStream(value) + return value + + +# * https://tools.ietf.org/html/rfc3986#section-3.2.3 +# * https://url.spec.whatwg.org/#url-miscellaneous +# * https://url.spec.whatwg.org/#scheme-state +DEFAULT_PORTS = { + b"ftp": 21, + b"http": 80, + b"https": 443, + b"ws": 80, + b"wss": 443, +} + + +def include_request_headers( + headers: list[tuple[bytes, bytes]], + *, + url: URL, + content: None | bytes | typing.Iterable[bytes] | typing.AsyncIterable[bytes], +) -> list[tuple[bytes, bytes]]: + headers_set = {k.lower() for k, _v in headers} + + if b"host" not in headers_set: + default_port = DEFAULT_PORTS.get(url.scheme) + if url.port is None or url.port == default_port: + header_value = url.host + else: + header_value = b"%b:%d" % (url.host, url.port) + headers = [(b"Host", header_value)] + headers + + if content is not None and b"content-length" not in headers_set and b"transfer-encoding" not in headers_set: + if isinstance(content, bytes): + content_length = str(len(content)).encode("ascii") + headers += [(b"Content-Length", content_length)] + else: + headers += [(b"Transfer-Encoding", b"chunked")] # pragma: no cover + + return headers + + +# Interfaces for byte streams... + + +class ByteStream: + """ + A container for non-streaming content, and that supports both sync and async + stream iteration. + """ + + def __init__(self, content: bytes) -> None: + self._content = content + + def __iter__(self) -> typing.Iterator[bytes]: + yield self._content + + async def __aiter__(self) -> AsyncGenerator[bytes]: + yield self._content + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{len(self._content)} bytes]>" + + +class Origin: + def __init__(self, scheme: bytes, host: bytes, port: int) -> None: + self.scheme = scheme + self.host = host + self.port = port + + def __eq__(self, other: typing.Any) -> bool: + return ( + isinstance(other, Origin) + and self.scheme == other.scheme + and self.host == other.host + and self.port == other.port + ) + + def __str__(self) -> str: + scheme = self.scheme.decode("ascii") + host = self.host.decode("ascii") + port = str(self.port) + return f"{scheme}://{host}:{port}" + + +class URL: + """ + Represents the URL against which an HTTP request may be made. + + The URL may either be specified as a plain string, for convenience: + + ```python + url = httpcore2.URL("https://www.example.com/") + ``` + + Or be constructed with explicitly pre-parsed components: + + ```python + url = httpcore2.URL(scheme=b'https', host=b'www.example.com', port=None, target=b'/') + ``` + + Using this second more explicit style allows integrations that are using + `httpcore` to pass through URLs that have already been parsed in order to use + libraries such as `rfc-3986` rather than relying on the stdlib. It also ensures + that URL parsing is treated identically at both the networking level and at any + higher layers of abstraction. + + The four components are important here, as they allow the URL to be precisely + specified in a pre-parsed format. They also allow certain types of request to + be created that could not otherwise be expressed. + + For example, an HTTP request to `http://www.example.com/` forwarded via a proxy + at `http://localhost:8080`... + + ```python + # Constructs an HTTP request with a complete URL as the target: + # GET https://www.example.com/ HTTP/1.1 + url = httpcore2.URL( + scheme=b'http', + host=b'localhost', + port=8080, + target=b'https://www.example.com/' + ) + request = httpcore2.Request( + method="GET", + url=url + ) + ``` + + Another example is constructing an `OPTIONS *` request... + + ```python + # Constructs an 'OPTIONS *' HTTP request: + # OPTIONS * HTTP/1.1 + url = httpcore2.URL(scheme=b'https', host=b'www.example.com', target=b'*') + request = httpcore2.Request(method="OPTIONS", url=url) + ``` + + This kind of request is not possible to formulate with a URL string, + because the `/` delimiter is always used to demark the target from the + host/port portion of the URL. + + For convenience, string-like arguments may be specified either as strings or + as bytes. However, once a request is being issue over-the-wire, the URL + components are always ultimately required to be a bytewise representation. + + In order to avoid any ambiguity over character encodings, when strings are used + as arguments, they must be strictly limited to the ASCII range `chr(0)`-`chr(127)`. + If you require a bytewise representation that is outside this range you must + handle the character encoding directly, and pass a bytes instance. + """ + + def __init__( + self, + url: bytes | str = "", + *, + scheme: bytes | str = b"", + host: bytes | str = b"", + port: int | None = None, + target: bytes | str = b"", + ) -> None: + """ + Parameters: + url: The complete URL as a string or bytes. + scheme: The URL scheme as a string or bytes. + Typically either `"http"` or `"https"`. + host: The URL host as a string or bytes. Such as `"www.example.com"`. + port: The port to connect to. Either an integer or `None`. + target: The target of the HTTP request. Such as `"/items?search=red"`. + """ + if url: + parsed = urllib.parse.urlparse(enforce_bytes(url, name="url")) + self.scheme = parsed.scheme + self.host = parsed.hostname or b"" + self.port = parsed.port + self.target = (parsed.path or b"/") + (b"?" + parsed.query if parsed.query else b"") + else: + self.scheme = enforce_bytes(scheme, name="scheme") + self.host = enforce_bytes(host, name="host") + self.port = port + self.target = enforce_bytes(target, name="target") + + @property + def origin(self) -> Origin: + default_port = { + b"http": 80, + b"https": 443, + b"ws": 80, + b"wss": 443, + b"socks5": 1080, + b"socks5h": 1080, + }[self.scheme] + return Origin(scheme=self.scheme, host=self.host, port=self.port or default_port) + + def __eq__(self, other: typing.Any) -> bool: + return ( + isinstance(other, URL) + and other.scheme == self.scheme + and other.host == self.host + and other.port == self.port + and other.target == self.target + ) + + def __bytes__(self) -> bytes: + if self.port is None: + return b"%b://%b%b" % (self.scheme, self.host, self.target) + return b"%b://%b:%d%b" % (self.scheme, self.host, self.port, self.target) + + def __repr__(self) -> str: + return ( + f"{self.__class__.__name__}(scheme={self.scheme!r}, " + f"host={self.host!r}, port={self.port!r}, target={self.target!r})" + ) + + +class Request: + """ + An HTTP request. + """ + + def __init__( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterable[bytes] | typing.AsyncIterable[bytes] | None = None, + extensions: Extensions | None = None, + ) -> None: + """ + Parameters: + method: The HTTP request method, either as a string or bytes. + For example: `GET`. + url: The request URL, either as a `URL` instance, or as a string or bytes. + For example: `"https://www.example.com".` + headers: The HTTP request headers. + content: The content of the request body. + extensions: A dictionary of optional extra information included on + the request. Possible keys include `"timeout"`, and `"trace"`. + """ + self.method: bytes = enforce_bytes(method, name="method") + self.url: URL = enforce_url(url, name="url") + self.headers: list[tuple[bytes, bytes]] = enforce_headers(headers, name="headers") + self.stream: typing.Iterable[bytes] | typing.AsyncIterable[bytes] = enforce_stream(content, name="content") + self.extensions = {} if extensions is None else extensions + + if "target" in self.extensions: + self.url = URL( + scheme=self.url.scheme, + host=self.url.host, + port=self.url.port, + target=self.extensions["target"], + ) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.method!r}]>" + + +class Response: + """ + An HTTP response. + """ + + def __init__( + self, + status: int, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterable[bytes] | typing.AsyncIterable[bytes] | None = None, + extensions: Extensions | None = None, + ) -> None: + """ + Parameters: + status: The HTTP status code of the response. For example `200`. + headers: The HTTP response headers. + content: The content of the response body. + extensions: A dictionary of optional extra information included on + the responseself.Possible keys include `"http_version"`, + `"reason_phrase"`, and `"network_stream"`. + """ + self.status: int = status + self.headers: list[tuple[bytes, bytes]] = enforce_headers(headers, name="headers") + self.stream: typing.Iterable[bytes] | typing.AsyncIterable[bytes] = enforce_stream(content, name="content") + self.extensions = {} if extensions is None else extensions + + self._stream_consumed = False + + @property + def content(self) -> bytes: + if not hasattr(self, "_content"): + if isinstance(self.stream, typing.Iterable): + raise RuntimeError( + "Attempted to access 'response.content' on a streaming response. Call 'response.read()' first." + ) + else: + raise RuntimeError( + "Attempted to access 'response.content' on a streaming response. " + "Call 'await response.aread()' first." + ) + return self._content + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.status}]>" + + # Sync interface... + + def read(self) -> bytes: + if not isinstance(self.stream, typing.Iterable): # pragma: no cover + raise RuntimeError( + "Attempted to read an asynchronous response using 'response.read()'. " + "You should use 'await response.aread()' instead." + ) + if not hasattr(self, "_content"): + self._content = b"".join(list(self.iter_stream())) + return self._content + + def iter_stream(self) -> typing.Iterator[bytes]: + if not isinstance(self.stream, typing.Iterable): # pragma: no cover + raise RuntimeError( + "Attempted to stream an asynchronous response using 'for ... in " + "response.iter_stream()'. " + "You should use 'async for ... in response.aiter_stream()' instead." + ) + if self._stream_consumed: + raise RuntimeError("Attempted to call 'for ... in response.iter_stream()' more than once.") + self._stream_consumed = True + yield from self.stream + + def close(self) -> None: + if not isinstance(self.stream, typing.Iterable): # pragma: no cover + raise RuntimeError( + "Attempted to close an asynchronous response using 'response.close()'. " + "You should use 'await response.aclose()' instead." + ) + if hasattr(self.stream, "close"): + self.stream.close() + + # Async interface... + + async def aread(self) -> bytes: + if not isinstance(self.stream, typing.AsyncIterable): # pragma: no cover + raise RuntimeError( + "Attempted to read an synchronous response using " + "'await response.aread()'. " + "You should use 'response.read()' instead." + ) + if not hasattr(self, "_content"): + async with safe_async_iterate(self.aiter_stream()) as parts: + self._content = b"".join([part async for part in parts]) + return self._content + + async def aiter_stream(self) -> AsyncGenerator[bytes]: + if not isinstance(self.stream, typing.AsyncIterable): # pragma: no cover + raise RuntimeError( + "Attempted to stream an synchronous response using 'async for ... in " + "response.aiter_stream()'. " + "You should use 'for ... in response.iter_stream()' instead." + ) + if self._stream_consumed: + raise RuntimeError("Attempted to call 'async for ... in response.aiter_stream()' more than once.") + self._stream_consumed = True + async with safe_async_iterate(self.stream) as iterator: + async for chunk in iterator: + yield chunk + + async def aclose(self) -> None: + if not isinstance(self.stream, typing.AsyncIterable): # pragma: no cover + raise RuntimeError( + "Attempted to close a synchronous response using " + "'await response.aclose()'. " + "You should use 'response.close()' instead." + ) + if hasattr(self.stream, "aclose"): + await self.stream.aclose() + + +class Proxy: + def __init__( + self, + url: URL | bytes | str, + auth: tuple[bytes | str, bytes | str] | None = None, + headers: HeadersAsMapping | HeadersAsSequence | None = None, + ssl_context: ssl.SSLContext | None = None, + ): + self.url = enforce_url(url, name="url") + self.headers = enforce_headers(headers, name="headers") + self.ssl_context = ssl_context + + if auth is not None: + username = enforce_bytes(auth[0], name="auth") + password = enforce_bytes(auth[1], name="auth") + userpass = username + b":" + password + authorization = b"Basic " + base64.b64encode(userpass) + self.auth: tuple[bytes, bytes] | None = (username, password) + self.headers = [(b"Proxy-Authorization", authorization)] + self.headers + else: + self.auth = None diff --git a/venv/lib/python3.12/site-packages/httpcore2/_ssl.py b/venv/lib/python3.12/site-packages/httpcore2/_ssl.py new file mode 100644 index 0000000..c937023 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_ssl.py @@ -0,0 +1,12 @@ +import os +import ssl + +import truststore + + +def default_ssl_context() -> ssl.SSLContext: + if cafile := os.environ.get("SSL_CERT_FILE"): # pragma: no cover + return ssl.create_default_context(cafile=cafile) + if capath := os.environ.get("SSL_CERT_DIR"): # pragma: no cover + return ssl.create_default_context(capath=capath) + return truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/__init__.py b/venv/lib/python3.12/site-packages/httpcore2/_sync/__init__.py new file mode 100644 index 0000000..0ae2720 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_sync/__init__.py @@ -0,0 +1,39 @@ +from .connection import HTTPConnection +from .connection_pool import ConnectionPool +from .http11 import HTTP11Connection +from .http_proxy import HTTPProxy +from .interfaces import ConnectionInterface + +try: + from .http2 import HTTP2Connection +except ImportError: # pragma: no cover + + class HTTP2Connection: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use http2 support, but the `h2` package is not " + "installed. Use 'pip install httpcore[http2]'." + ) + + +try: + from .socks_proxy import SOCKSProxy +except ImportError: # pragma: no cover + + class SOCKSProxy: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use SOCKS support, but the `socksio` package is not " + "installed. Use 'pip install httpcore[socks]'." + ) + + +__all__ = [ + "HTTPConnection", + "ConnectionPool", + "HTTPProxy", + "HTTP11Connection", + "HTTP2Connection", + "ConnectionInterface", + "SOCKSProxy", +] diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0cb57a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/connection.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/connection.cpython-312.pyc new file mode 100644 index 0000000..aed0549 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/connection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/connection_pool.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/connection_pool.cpython-312.pyc new file mode 100644 index 0000000..91b07aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/connection_pool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/http11.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/http11.cpython-312.pyc new file mode 100644 index 0000000..a199dc6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/http11.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/http2.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/http2.cpython-312.pyc new file mode 100644 index 0000000..3cbaddf Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/http2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/http_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/http_proxy.cpython-312.pyc new file mode 100644 index 0000000..1bf5a31 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/http_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/interfaces.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/interfaces.cpython-312.pyc new file mode 100644 index 0000000..e92fbc9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/interfaces.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/socks_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/socks_proxy.cpython-312.pyc new file mode 100644 index 0000000..4aac1f2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpcore2/_sync/__pycache__/socks_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/connection.py b/venv/lib/python3.12/site-packages/httpcore2/_sync/connection.py new file mode 100644 index 0000000..6691a9f --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_sync/connection.py @@ -0,0 +1,208 @@ +from __future__ import annotations + +import itertools +import logging +import ssl +import types +import typing + +from .._backends.sync import SyncBackend +from .._backends.base import SOCKET_OPTION, NetworkBackend, NetworkStream +from .._exceptions import ConnectError, ConnectTimeout +from .._models import Origin, Request, Response +from .._ssl import default_ssl_context +from .._synchronization import Lock +from .._trace import Trace +from .http11 import HTTP11Connection +from .interfaces import ConnectionInterface + +RETRIES_BACKOFF_FACTOR = 0.5 # 0s, 0.5s, 1s, 2s, 4s, etc. + + +logger = logging.getLogger("httpcore2.connection") + + +def exponential_backoff(factor: float) -> typing.Iterator[float]: + """ + Generate a geometric sequence that has a ratio of 2 and starts with 0. + + For example: + - `factor = 2`: `0, 2, 4, 8, 16, 32, 64, ...` + - `factor = 3`: `0, 3, 6, 12, 24, 48, 96, ...` + """ + yield 0 + for n in itertools.count(): + yield factor * 2**n + + +class HTTPConnection(ConnectionInterface): + def __init__( + self, + origin: Origin, + ssl_context: ssl.SSLContext | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + self._origin = origin + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend: NetworkBackend = SyncBackend() if network_backend is None else network_backend + self._connection: ConnectionInterface | None = None + self._connect_failed: bool = False + self._request_lock = Lock() + self._socket_options = socket_options + + def handle_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError(f"Attempted to send request to {request.url.origin} on connection to {self._origin}") + + try: + with self._request_lock: + if self._connection is None: + stream = self._connect(request) + + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ssl_object is not None and ssl_object.selected_alpn_protocol() == "h2" + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import HTTP2Connection + + self._connection = HTTP2Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = HTTP11Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except BaseException as exc: + self._connect_failed = True + raise exc + + return self._connection.handle_request(request) + + def _connect(self, request: Request) -> NetworkStream: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + retries_left = self._retries + delays = exponential_backoff(factor=RETRIES_BACKOFF_FACTOR) + + while True: + try: + if self._uds is None: + kwargs = { + "host": self._origin.host.decode("ascii"), + "port": self._origin.port, + "local_address": self._local_address, + "timeout": timeout, + "socket_options": self._socket_options, + } + with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + else: + kwargs = { + "path": self._uds, + "timeout": timeout, + "socket_options": self._socket_options, + } + with Trace("connect_unix_socket", logger, request, kwargs) as trace: + stream = self._network_backend.connect_unix_socket(**kwargs) + trace.return_value = stream + + if self._origin.scheme in (b"https", b"wss"): + ssl_context = default_ssl_context() if self._ssl_context is None else self._ssl_context + alpn_protocols = (["h2", "http/1.1"] if self._http1 else ["h2"]) if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname or self._origin.host.decode("ascii"), + "timeout": timeout, + } + with Trace("start_tls", logger, request, kwargs) as trace: + stream = stream.start_tls(**kwargs) + trace.return_value = stream + return stream + except (ConnectError, ConnectTimeout): + if retries_left <= 0: + raise + retries_left -= 1 + delay = next(delays) + with Trace("retry", logger, request, kwargs) as trace: + self._network_backend.sleep(delay) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def close(self) -> None: + if self._connection is not None: + with Trace("close", logger, None, {}): + self._connection.close() + + def is_connected(self) -> bool: + return self._connection is not None and self._connection.is_connected() + + def is_available(self) -> bool: + if self._connection is None: + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return self._http2 and (self._origin.scheme == b"https" or not self._http1) and not self._connect_failed + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_idle() + + def can_multiplex(self) -> bool: + return self._connection is not None and self._connection.can_multiplex() + + def is_closed(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + def __enter__(self) -> HTTPConnection: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self.close() diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/connection_pool.py b/venv/lib/python3.12/site-packages/httpcore2/_sync/connection_pool.py new file mode 100644 index 0000000..a6051ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_sync/connection_pool.py @@ -0,0 +1,440 @@ +from __future__ import annotations + +import ssl +import sys +import types +import typing +from collections.abc import Generator + +from .._backends.sync import SyncBackend +from .._backends.base import SOCKET_OPTION, NetworkBackend +from .._exceptions import ConnectionNotAvailable, UnsupportedProtocol +from .._models import Origin, Proxy, Request, Response +from .._synchronization import Event, ShieldCancellation, ThreadLock +from .._utils import safe_iterate +from .connection import HTTPConnection +from .interfaces import ConnectionInterface, RequestInterface + + +class PoolRequest: + def __init__(self, request: Request) -> None: + self.request = request + self.connection: ConnectionInterface | None = None + self._connection_acquired = Event() + + def assign_to_connection(self, connection: ConnectionInterface | None) -> None: + self.connection = connection + self._connection_acquired.set() + + def clear_connection(self) -> None: + self.connection = None + self._connection_acquired = Event() + + def wait_for_connection(self, timeout: float | None = None) -> ConnectionInterface: + if self.connection is None: + self._connection_acquired.wait(timeout=timeout) + assert self.connection is not None + return self.connection + + def is_queued(self) -> bool: + return self.connection is None + + +class ConnectionPool(RequestInterface): + """ + A connection pool for making HTTP requests. + """ + + def __init__( + self, + ssl_context: ssl.SSLContext | None = None, + proxy: Proxy | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore2.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish a + connection. + local_address: Local address to connect from. Can also be used to connect + using a particular address family. Using `local_address="0.0.0.0"` + will connect using an `AF_INET` address (IPv4), while using + `local_address="::"` will connect using an `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + socket_options: Socket options that have to be included + in the TCP socket when the connection was established. + """ + self._ssl_context = ssl_context + self._proxy = proxy + self._max_connections = sys.maxsize if max_connections is None else max_connections + self._max_keepalive_connections = ( + sys.maxsize if max_keepalive_connections is None else max_keepalive_connections + ) + self._max_keepalive_connections = min(self._max_connections, self._max_keepalive_connections) + + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend = SyncBackend() if network_backend is None else network_backend + self._socket_options = socket_options + + # The mutable state on a connection pool is the queue of incoming requests, + # and the set of connections that are servicing those requests. + self._connections: list[ConnectionInterface] = [] + self._requests: list[PoolRequest] = [] + + # We only mutate the state of the connection pool within an 'optional_thread_lock' + # context. This holds a threading lock unless we're running in async mode, + # in which case it is a no-op. + self._optional_thread_lock = ThreadLock() + + def create_connection(self, origin: Origin) -> ConnectionInterface: + if self._proxy is not None: + if self._proxy.url.scheme in (b"socks5", b"socks5h"): + from .socks_proxy import Socks5Connection + + return Socks5Connection( + proxy_origin=self._proxy.url.origin, + proxy_auth=self._proxy.auth, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + elif origin.scheme == b"http": + from .http_proxy import ForwardHTTPConnection + + return ForwardHTTPConnection( + proxy_origin=self._proxy.url.origin, + proxy_headers=self._proxy.headers, + proxy_ssl_context=self._proxy.ssl_context, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + ) + from .http_proxy import TunnelHTTPConnection + + return TunnelHTTPConnection( + proxy_origin=self._proxy.url.origin, + proxy_headers=self._proxy.headers, + proxy_ssl_context=self._proxy.ssl_context, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + return HTTPConnection( + origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + retries=self._retries, + local_address=self._local_address, + uds=self._uds, + network_backend=self._network_backend, + socket_options=self._socket_options, + ) + + @property + def connections(self) -> list[ConnectionInterface]: + """ + Return a list of the connections currently in the pool. + + For example: + + ```python + >>> pool.connections + [ + , + , + , + ] + ``` + """ + return list(self._connections) + + def handle_request(self, request: Request) -> Response: + """ + Send an HTTP request, and return an HTTP response. + + This is the core implementation that is called into by `.request()` or `.stream()`. + """ + scheme = request.url.scheme.decode() + if scheme == "": + raise UnsupportedProtocol("Request URL is missing an 'http://' or 'https://' protocol.") + if scheme not in ("http", "https", "ws", "wss"): + raise UnsupportedProtocol(f"Request URL has an unsupported protocol '{scheme}://'.") + + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("pool", None) + + with self._optional_thread_lock: + # Add the incoming request to our request queue. + pool_request = PoolRequest(request) + self._requests.append(pool_request) + + try: + while True: + with self._optional_thread_lock: + # Assign incoming requests to available connections, + # closing or creating new connections as required. + closing = self._assign_requests_to_connections() + self._close_connections(closing) + + # Wait until this request has an assigned connection. + connection = pool_request.wait_for_connection(timeout=timeout) + + try: + # Send the request on the assigned connection. + response = connection.handle_request(pool_request.request) + except ConnectionNotAvailable: + # In some cases a connection may initially be available to + # handle a request, but then become unavailable. + # + # In this case we clear the connection and try again. + pool_request.clear_connection() + else: + break # pragma: no cover + + except BaseException as exc: + with self._optional_thread_lock: + # For any exception or cancellation we remove the request from + # the queue, and then re-assign requests to connections. + self._requests.remove(pool_request) + closing = self._assign_requests_to_connections() + + self._close_connections(closing) + raise exc from None + + # Return the response. Note that in this case we still have to manage + # the point at which the response is closed. + assert isinstance(response.stream, typing.Iterable) + return Response( + status=response.status, + headers=response.headers, + content=PoolByteStream(stream=response.stream, pool_request=pool_request, pool=self), + extensions=response.extensions, + ) + + def _assign_requests_to_connections(self) -> list[ConnectionInterface]: + """ + Manage the state of the connection pool, assigning incoming + requests to connections as available. + + Called whenever a new request is added or removed from the pool. + + Any closing connections are returned, allowing the I/O for closing + those connections to be handled separately. + """ + closing_connections: list[ConnectionInterface] = [] + retained_connections: list[ConnectionInterface] = [] + + # Connections currently referenced by an in-flight request, including + # connections that are in the process of being established and idle + # connections reserved by an assigned-but-not-yet-sent request. + request_connections = {r.connection for r in self._requests} + + # First we handle cleaning up any connections that are closed + # or have expired their keep-alive, in a single pass. Reserved + # connections skip the expiry check: they were checked when assigned, + # and `has_expired()` on an idle connection probes the socket. + for connection in self._connections: + reserved = connection in request_connections + if connection.is_closed(): + continue + elif not (connection.is_connected() or reserved): + # Garbage: a NEW-state connection whose request was cancelled + # before the TCP handshake completed. Drop it without closing + # (there is no socket to close yet). + continue + elif not reserved and connection.has_expired(): + closing_connections.append(connection) + else: + retained_connections.append(connection) + + # Then we close any surplus idle connections, to enforce the + # max_keepalive_connections setting. Reserved connections are not + # surplus: a request is about to be sent on them. + idle_surplus = ( + sum(connection.is_idle() and connection not in request_connections for connection in retained_connections) + - self._max_keepalive_connections + ) + if idle_surplus > 0: + kept: list[ConnectionInterface] = [] + for connection in retained_connections: + if idle_surplus > 0 and connection.is_idle() and connection not in request_connections: + closing_connections.append(connection) + idle_surplus -= 1 + else: + kept.append(connection) + retained_connections = kept + + self._connections = retained_connections + + # Snapshot the set of reusable connections once, rather than rebuilding + # it per queued request — this is what brings the loop from O(N*M) to + # O(N+M) in the common case. + # + # An idle connection already assigned to an in-flight request is + # reserved: it stays IDLE until the winning task sends on it, so + # without this exclusion the next pass would assign it again and the + # loser would churn through `ConnectionNotAvailable`. Multiplexing + # connections are exempt: they can take further requests while idle. + available_connections = [ + connection + for connection in self._connections + if connection.is_available() + and not (connection.is_idle() and connection in request_connections and not connection.can_multiplex()) + ] + new_connection_budget = self._max_connections - len(self._connections) + + # Assign queued requests to connections. Once no connection is + # available and no new connection may be created, no queued request + # can be assigned, so the scan stops early: this keeps a pass on a + # saturated pool O(connections) rather than O(in-flight requests). + for pool_request in self._requests: + if not available_connections and new_connection_budget <= 0: + break + if not pool_request.is_queued(): + continue + origin = pool_request.request.url.origin + + # There are three cases for how we may be able to handle the request: + # + # 1. There is an existing connection that can handle the request. + # 2. We can create a new connection to handle the request. + # 3. We can close an idle connection and then create a new connection + # to handle the request. + for idx, connection in enumerate(available_connections): + if connection.can_handle_request(origin): + pool_request.assign_to_connection(connection) + if connection.is_idle() and not connection.can_multiplex(): + # An idle HTTP/1.1 connection can only take this + # single request until it is released. + del available_connections[idx] + break + else: + if new_connection_budget > 0: + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + new_connection_budget -= 1 + continue + for idx, connection in enumerate(available_connections): + if connection.is_idle(): + del available_connections[idx] + self._connections.remove(connection) + closing_connections.append(connection) + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + break + + return closing_connections + + def _close_connections(self, closing: list[ConnectionInterface]) -> None: + # Close connections which have been removed from the pool. + with ShieldCancellation(): + for connection in closing: + connection.close() + + def close(self) -> None: + # Explicitly close the connection pool. + # Clears all existing requests and connections. + with self._optional_thread_lock: + closing_connections = list(self._connections) + self._connections = [] + self._close_connections(closing_connections) + + def __enter__(self) -> ConnectionPool: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self.close() + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + with self._optional_thread_lock: + request_is_queued = [request.is_queued() for request in self._requests] + connection_is_idle = [connection.is_idle() for connection in self._connections] + + num_active_requests = request_is_queued.count(False) + num_queued_requests = request_is_queued.count(True) + num_active_connections = connection_is_idle.count(False) + num_idle_connections = connection_is_idle.count(True) + + requests_info = f"Requests: {num_active_requests} active, {num_queued_requests} queued" + connection_info = f"Connections: {num_active_connections} active, {num_idle_connections} idle" + + return f"<{class_name} [{requests_info} | {connection_info}]>" + + +class PoolByteStream: + def __init__( + self, + stream: typing.Iterable[bytes], + pool_request: PoolRequest, + pool: ConnectionPool, + ) -> None: + self._stream = stream + self._pool_request = pool_request + self._pool = pool + self._closed = False + + def __iter__(self) -> Generator[bytes]: + with safe_iterate(self._stream) as iterator: + for chunk in iterator: + yield chunk + + def close(self) -> None: + if not self._closed: + self._closed = True + with ShieldCancellation(): + if hasattr(self._stream, "close"): + self._stream.close() + + with self._pool._optional_thread_lock: + self._pool._requests.remove(self._pool_request) + closing = self._pool._assign_requests_to_connections() + + self._pool._close_connections(closing) diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/http11.py b/venv/lib/python3.12/site-packages/httpcore2/_sync/http11.py new file mode 100644 index 0000000..50bce83 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_sync/http11.py @@ -0,0 +1,357 @@ +from __future__ import annotations + +import enum +import logging +import ssl +import time +import types +import typing +from collections.abc import Generator + +import h11 + +from .._backends.base import NetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, + WriteError, + map_exceptions, +) +from .._models import Origin, Request, Response +from .._synchronization import Lock, ShieldCancellation +from .._trace import Trace +from .._utils import safe_iterate +from .interfaces import ConnectionInterface + +logger = logging.getLogger("httpcore2.http11") + + +# A subset of `h11.Event` types supported by `_send_event` +H11SendEvent = h11.Request | h11.Data | h11.EndOfMessage + + +class HTTPConnectionState(enum.IntEnum): + NEW = 0 + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class HTTP11Connection(ConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + MAX_INCOMPLETE_EVENT_SIZE = 100 * 1024 + + def __init__( + self, + origin: Origin, + stream: NetworkStream, + keepalive_expiry: float | None = None, + ) -> None: + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: float | None = keepalive_expiry + self._expire_at: float | None = None + self._state = HTTPConnectionState.NEW + self._state_lock = Lock() + self._request_count = 0 + self._h11_state = h11.Connection( + our_role=h11.CLIENT, + max_incomplete_event_size=self.MAX_INCOMPLETE_EVENT_SIZE, + ) + + def handle_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError(f"Attempted to send request to {request.url.origin} on connection to {self._origin}") + + with self._state_lock: + if self._state in (HTTPConnectionState.NEW, HTTPConnectionState.IDLE): + self._request_count += 1 + self._state = HTTPConnectionState.ACTIVE + self._expire_at = None + else: + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request} + try: + with Trace("send_request_headers", logger, request, kwargs) as trace: + self._send_request_headers(**kwargs) + with Trace("send_request_body", logger, request, kwargs) as trace: + self._send_request_body(**kwargs) + except WriteError: + # If we get a write error while we're writing the request, + # then we suppress this error and move on to attempting to + # read the response. Servers can sometimes close the request + # preemptively and then respond with a well formed HTTP + # error response. + pass + + with Trace("receive_response_headers", logger, request, kwargs) as trace: + ( + http_version, + status, + reason_phrase, + headers, + trailing_data, + ) = self._receive_response_headers(**kwargs) + trace.return_value = ( + http_version, + status, + reason_phrase, + headers, + ) + + network_stream = self._network_stream + + # CONNECT or Upgrade request + if (status == 101) or ((request.method == b"CONNECT") and (200 <= status < 300)): + network_stream = HTTP11UpgradeStream(network_stream, trailing_data) + + return Response( + status=status, + headers=headers, + content=HTTP11ConnectionByteStream(self, request), + extensions={ + "http_version": http_version, + "reason_phrase": reason_phrase, + "network_stream": network_stream, + }, + ) + except BaseException as exc: + with ShieldCancellation(): + with Trace("response_closed", logger, request) as trace: + self._response_closed() + raise exc + + # Sending the request... + + def _send_request_headers(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + with map_exceptions({h11.LocalProtocolError: LocalProtocolError}): + event = h11.Request( + method=request.method, + target=request.url.target, + headers=request.headers, + ) + self._send_event(event, timeout=timeout) + + def _send_request_body(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + assert isinstance(request.stream, typing.Iterable) + with safe_iterate(request.stream) as iterator: + for chunk in iterator: + event = h11.Data(data=chunk) + self._send_event(event, timeout=timeout) + + self._send_event(h11.EndOfMessage(), timeout=timeout) + + def _send_event(self, event: h11.Event, timeout: float | None = None) -> None: + bytes_to_send = self._h11_state.send(event) + if bytes_to_send is not None: + self._network_stream.write(bytes_to_send, timeout=timeout) + + # Receiving the response... + + def _receive_response_headers( + self, request: Request + ) -> tuple[bytes, int, bytes, list[tuple[bytes, bytes]], bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = self._receive_event(timeout=timeout) + if isinstance(event, h11.Response): + break + if isinstance(event, h11.InformationalResponse) and event.status_code == 101: + break + + http_version = b"HTTP/" + event.http_version + + # h11 version 0.11+ supports a `raw_items` interface to get the + # raw header casing, rather than the enforced lowercase headers. + headers = event.headers.raw_items() + + trailing_data, _ = self._h11_state.trailing_data + + return http_version, event.status_code, event.reason, headers, trailing_data + + def _receive_response_body(self, request: Request) -> Generator[bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = self._receive_event(timeout=timeout) + if isinstance(event, h11.Data): + yield bytes(event.data) + elif isinstance(event, (h11.EndOfMessage, h11.PAUSED)): + break + + def _receive_event(self, timeout: float | None = None) -> h11.Event | type[h11.PAUSED]: + while True: + with map_exceptions({h11.RemoteProtocolError: RemoteProtocolError}): + event = self._h11_state.next_event() + + if event is h11.NEED_DATA: + data = self._network_stream.read(self.READ_NUM_BYTES, timeout=timeout) + + # If we feed this case through h11 we'll raise an exception like: + # + # httpcore2.RemoteProtocolError: can't handle event type + # ConnectionClosed when role=SERVER and state=SEND_RESPONSE + # + # Which is accurate, but not very informative from an end-user + # perspective. Instead we handle this case distinctly and treat + # it as a ConnectError. + if data == b"" and self._h11_state.their_state == h11.SEND_RESPONSE: + msg = "Server disconnected without sending a response." + raise RemoteProtocolError(msg) + + self._h11_state.receive_data(data) + else: + # mypy fails to narrow the type in the above if statement above + return event # type: ignore[return-value] + + def _response_closed(self) -> None: + with self._state_lock: + if self._h11_state.our_state is h11.DONE and self._h11_state.their_state is h11.DONE: + self._state = HTTPConnectionState.IDLE + self._h11_state.start_next_cycle() + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + else: + self.close() + + # Once the connection is no longer required... + + def close(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._state = HTTPConnectionState.CLOSED + self._network_stream.close() + + # The ConnectionInterface methods provide information about the state of + # the connection, allowing for a connection pooling implementation to + # determine when to reuse and when to close the connection... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_connected(self) -> bool: + return not self.is_closed() + + def is_available(self) -> bool: + # Note that HTTP/1.1 connections in the "NEW" state are not treated as + # being "available". The control flow which created the connection will + # be able to send an outgoing request, but the connection will not be + # acquired from the connection pool for any other request. + return self._state == HTTPConnectionState.IDLE + + def has_expired(self) -> bool: + now = time.monotonic() + # Read `_expire_at` once into a local: on free-threaded builds another + # thread may reset it to `None` between the check and the comparison. + expire_at = self._expire_at + keepalive_expired = expire_at is not None and now > expire_at + + # If the HTTP connection is idle but the socket is readable, then the + # only valid state is that the socket is about to return b"", indicating + # a server-initiated disconnect. + server_disconnected = self._state == HTTPConnectionState.IDLE and self._network_stream.get_extra_info( + "is_readable" + ) + + return keepalive_expired or server_disconnected + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return f"{origin!r}, HTTP/1.1, {self._state.name}, Request Count: {self._request_count}" + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return f"<{class_name} [{origin!r}, {self._state.name}, Request Count: {self._request_count}]>" + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + def __enter__(self) -> HTTP11Connection: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self.close() + + +class HTTP11ConnectionByteStream: + def __init__(self, connection: HTTP11Connection, request: Request) -> None: + self._connection = connection + self._request = request + self._closed = False + + def __iter__(self) -> Generator[bytes]: + kwargs = {"request": self._request} + try: + with Trace("receive_response_body", logger, self._request, kwargs): + with safe_iterate(self._connection._receive_response_body(**kwargs)) as iterator: + for chunk in iterator: + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with ShieldCancellation(): + self.close() + raise exc + + def close(self) -> None: + if not self._closed: + self._closed = True + with Trace("response_closed", logger, self._request): + self._connection._response_closed() + + +class HTTP11UpgradeStream(NetworkStream): + def __init__(self, stream: NetworkStream, leading_data: bytes) -> None: + self._stream = stream + self._leading_data = leading_data + + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + if self._leading_data: + buffer = self._leading_data[:max_bytes] + self._leading_data = self._leading_data[max_bytes:] + return buffer + else: + return self._stream.read(max_bytes, timeout) + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + self._stream.write(buffer, timeout) + + def close(self) -> None: + self._stream.close() + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + return self._stream.start_tls(ssl_context, server_hostname, timeout) + + def get_extra_info(self, info: str) -> typing.Any: + return self._stream.get_extra_info(info) diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/http2.py b/venv/lib/python3.12/site-packages/httpcore2/_sync/http2.py new file mode 100644 index 0000000..ee314c8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_sync/http2.py @@ -0,0 +1,559 @@ +from __future__ import annotations + +import enum +import logging +import time +import types +import typing +from collections.abc import Generator + +import h2.config +import h2.connection +import h2.events +import h2.exceptions +import h2.settings + +from .._backends.base import NetworkStream +from .._exceptions import ConnectionNotAvailable, LocalProtocolError, RemoteProtocolError +from .._models import Origin, Request, Response +from .._synchronization import Lock, Semaphore, ShieldCancellation +from .._trace import Trace +from .._utils import safe_iterate +from .interfaces import ConnectionInterface + +logger = logging.getLogger("httpcore2.http2") + + +def has_body_headers(request: Request) -> bool: + return any(k.lower() == b"content-length" or k.lower() == b"transfer-encoding" for k, _v in request.headers) + + +class HTTPConnectionState(enum.IntEnum): + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class HTTP2Connection(ConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + CONFIG = h2.config.H2Configuration(validate_inbound_headers=False) + + def __init__( + self, + origin: Origin, + stream: NetworkStream, + keepalive_expiry: float | None = None, + ): + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: float | None = keepalive_expiry + self._h2_state = h2.connection.H2Connection(config=self.CONFIG) + self._state = HTTPConnectionState.IDLE + self._expire_at: float | None = None + self._request_count = 0 + self._init_lock = Lock() + self._state_lock = Lock() + self._read_lock = Lock() + self._write_lock = Lock() + self._sent_connection_init = False + self._used_all_stream_ids = False + self._connection_error = False + + # Mapping from stream ID to response stream events. + self._events: dict[ + int, + list[h2.events.ResponseReceived | h2.events.DataReceived | h2.events.StreamEnded | h2.events.StreamReset,], + ] = {} + + # Connection terminated events are stored as state since + # we need to handle them for all streams. + self._connection_terminated: h2.events.ConnectionTerminated | None = None + + self._read_exception: Exception | None = None + self._write_exception: Exception | None = None + + def handle_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + # This cannot occur in normal operation, since the connection pool + # will only send requests on connections that handle them. + # It's in place simply for resilience as a guard against incorrect + # usage, for anyone working directly with httpcore connections. + raise RuntimeError(f"Attempted to send request to {request.url.origin} on connection to {self._origin}") + + with self._state_lock: + if self._state in (HTTPConnectionState.ACTIVE, HTTPConnectionState.IDLE): + self._request_count += 1 + self._expire_at = None + self._state = HTTPConnectionState.ACTIVE + else: + raise ConnectionNotAvailable() + + with self._init_lock: + if not self._sent_connection_init: + try: + sci_kwargs = {"request": request} + with Trace("send_connection_init", logger, request, sci_kwargs): + self._send_connection_init(**sci_kwargs) + except BaseException as exc: + with ShieldCancellation(): + self.close() + raise exc + + self._sent_connection_init = True + + # Initially start with just 1 until the remote server provides + # its max_concurrent_streams value + self._max_streams = 1 + + local_settings_max_streams = self._h2_state.local_settings.max_concurrent_streams + self._max_streams_semaphore = Semaphore(local_settings_max_streams) + + for _ in range(local_settings_max_streams - self._max_streams): + self._max_streams_semaphore.acquire() + + self._max_streams_semaphore.acquire() + + try: + stream_id = self._h2_state.get_next_available_stream_id() + self._events[stream_id] = [] + except h2.exceptions.NoAvailableStreamIDError: # pragma: no cover + self._used_all_stream_ids = True + self._request_count -= 1 + self._max_streams_semaphore.release() + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request, "stream_id": stream_id} + with Trace("send_request_headers", logger, request, kwargs): + self._send_request_headers(request=request, stream_id=stream_id) + with Trace("send_request_body", logger, request, kwargs): + self._send_request_body(request=request, stream_id=stream_id) + with Trace("receive_response_headers", logger, request, kwargs) as trace: + status, headers = self._receive_response(request=request, stream_id=stream_id) + trace.return_value = (status, headers) + + return Response( + status=status, + headers=headers, + content=HTTP2ConnectionByteStream(self, request, stream_id=stream_id), + extensions={ + "http_version": b"HTTP/2", + "network_stream": self._network_stream, + "stream_id": stream_id, + }, + ) + except BaseException as exc: # noqa: PIE786 + with ShieldCancellation(): + kwargs = {"stream_id": stream_id} + with Trace("response_closed", logger, request, kwargs): + self._response_closed(stream_id=stream_id) + + if isinstance(exc, h2.exceptions.ProtocolError): + # One case where h2 can raise a protocol error is when a + # closed frame has been seen by the state machine. + # + # This happens when one stream is reading, and encounters + # a GOAWAY event. Other flows of control may then raise + # a protocol error at any point they interact with the 'h2_state'. + # + # In this case we'll have stored the event, and should raise + # it as a RemoteProtocolError. + if self._connection_terminated: # pragma: no cover + raise RemoteProtocolError(self._connection_terminated) + # If h2 raises a protocol error in some other state then we + # must somehow have made a protocol violation. + raise LocalProtocolError(exc) # pragma: no cover + + raise exc + + def _send_connection_init(self, request: Request) -> None: + """ + The HTTP/2 connection requires some initial setup before we can start + using individual request/response streams on it. + """ + # Need to set these manually here instead of manipulating via + # __setitem__() otherwise the H2Connection will emit SettingsUpdate + # frames in addition to sending the undesired defaults. + self._h2_state.local_settings = h2.settings.Settings( + client=True, + initial_values={ + # Disable PUSH_PROMISE frames from the server since we don't do anything + # with them for now. Maybe when we support caching? + h2.settings.SettingCodes.ENABLE_PUSH: 0, + # These two are taken from h2 for safe defaults + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS: 100, + h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE: 65536, + }, + ) + + # Some websites (*cough* Yahoo *cough*) balk at this setting being + # present in the initial handshake since it's not defined in the original + # RFC despite the RFC mandating ignoring settings you don't know about. + del self._h2_state.local_settings[h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL] + + self._h2_state.initiate_connection() + self._h2_state.increment_flow_control_window(2**24) + self._write_outgoing_data(request) + + # Sending the request... + + def _send_request_headers(self, request: Request, stream_id: int) -> None: + """ + Send the request headers to a given stream ID. + """ + end_stream = not has_body_headers(request) + + # In HTTP/2 the ':authority' pseudo-header is used instead of 'Host'. + # In order to gracefully handle HTTP/1.1 and HTTP/2 we always require + # HTTP/1.1 style headers, and map them appropriately if we end up on + # an HTTP/2 connection. + authority = [v for k, v in request.headers if k.lower() == b"host"][0] + + headers = [ + (b":method", request.method), + (b":authority", authority), + (b":scheme", request.url.scheme), + (b":path", request.url.target), + ] + [ + (k.lower(), v) + for k, v in request.headers + if k.lower() + not in ( + b"host", + b"transfer-encoding", + ) + ] + + self._h2_state.send_headers(stream_id, headers, end_stream=end_stream) + self._h2_state.increment_flow_control_window(2**24, stream_id=stream_id) + self._write_outgoing_data(request) + + def _send_request_body(self, request: Request, stream_id: int) -> None: + """ + Iterate over the request body sending it to a given stream ID. + """ + if not has_body_headers(request): + return + + assert isinstance(request.stream, typing.Iterable) + with safe_iterate(request.stream) as iterator: + for chunk in iterator: + self._send_stream_data(request, stream_id, chunk) + + self._send_end_stream(request, stream_id) + + def _send_stream_data(self, request: Request, stream_id: int, data: bytes) -> None: + """ + Send a single chunk of data in one or more data frames. + """ + position = 0 + while position < len(data): + max_flow = self._wait_for_outgoing_flow(request, stream_id) + chunk = data[position : position + max_flow] + position += len(chunk) + self._h2_state.send_data(stream_id, chunk) + self._write_outgoing_data(request) + + def _send_end_stream(self, request: Request, stream_id: int) -> None: + """ + Send an empty data frame on on a given stream ID with the END_STREAM flag set. + """ + self._h2_state.end_stream(stream_id) + self._write_outgoing_data(request) + + # Receiving the response... + + def _receive_response(self, request: Request, stream_id: int) -> tuple[int, list[tuple[bytes, bytes]]]: + """ + Return the response status code and headers for a given stream ID. + """ + while True: + event = self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.ResponseReceived): + break + + status_code = 200 + headers: list[tuple[bytes, bytes]] = [] + assert event.headers is not None + for k, v in event.headers: + if k == b":status": + status_code = int(v.decode("ascii", errors="ignore")) + elif not k.startswith(b":"): + headers.append((k, v)) + + return (status_code, headers) + + def _receive_response_body(self, request: Request, stream_id: int) -> Generator[bytes]: + """ + Iterator that returns the bytes of the response body for a given stream ID. + """ + while True: + event = self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.DataReceived): + assert event.flow_controlled_length is not None + assert event.data is not None + amount = event.flow_controlled_length + self._h2_state.acknowledge_received_data(amount, stream_id) + self._write_outgoing_data(request) + yield event.data + elif isinstance(event, h2.events.StreamEnded): + break + + def _receive_stream_event( + self, request: Request, stream_id: int + ) -> h2.events.ResponseReceived | h2.events.DataReceived | h2.events.StreamEnded: + """ + Return the next available event for a given stream ID. + + Will read more data from the network if required. + """ + while not self._events.get(stream_id): + self._receive_events(request, stream_id) + event = self._events[stream_id].pop(0) + if isinstance(event, h2.events.StreamReset): + raise RemoteProtocolError(event) + return event + + def _receive_events(self, request: Request, stream_id: int | None = None) -> None: + """ + Read some data from the network until we see one or more events + for a given stream ID. + """ + with self._read_lock: + if self._connection_terminated is not None: + last_stream_id = self._connection_terminated.last_stream_id + if stream_id and last_stream_id and stream_id > last_stream_id: + self._request_count -= 1 + raise ConnectionNotAvailable() + raise RemoteProtocolError(self._connection_terminated) + + # This conditional is a bit icky. We don't want to block reading if we've + # actually got an event to return for a given stream. We need to do that + # check *within* the atomic read lock. Though it also need to be optional, + # because when we call it from `_wait_for_outgoing_flow` we *do* want to + # block until we've available flow control, event when we have events + # pending for the stream ID we're attempting to send on. + if stream_id is None or not self._events.get(stream_id): + events = self._read_incoming_data(request) + for event in events: + if isinstance(event, h2.events.RemoteSettingsChanged): + with Trace("receive_remote_settings", logger, request) as trace: + self._receive_remote_settings_change(event) + trace.return_value = event + + elif isinstance( + event, + ( + h2.events.ResponseReceived, + h2.events.DataReceived, + h2.events.StreamEnded, + h2.events.StreamReset, + ), + ): + if event.stream_id in self._events: + self._events[event.stream_id].append(event) + + elif isinstance(event, h2.events.ConnectionTerminated): + self._connection_terminated = event + + self._write_outgoing_data(request) + + def _receive_remote_settings_change(self, event: h2.events.RemoteSettingsChanged) -> None: + max_concurrent_streams = event.changed_settings.get(h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS) + if max_concurrent_streams: + new_max_streams = min( + max_concurrent_streams.new_value, + self._h2_state.local_settings.max_concurrent_streams, + ) + if new_max_streams and new_max_streams != self._max_streams: + while new_max_streams > self._max_streams: + self._max_streams_semaphore.release() + self._max_streams += 1 + while new_max_streams < self._max_streams: + self._max_streams_semaphore.acquire() + self._max_streams -= 1 + + def _response_closed(self, stream_id: int) -> None: + with self._state_lock: + if stream_id in self._events: + self._max_streams_semaphore.release() + del self._events[stream_id] + if self._connection_terminated and not self._events: + self.close() + + elif self._state == HTTPConnectionState.ACTIVE and not self._events: + self._state = HTTPConnectionState.IDLE + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + if self._used_all_stream_ids: # pragma: no cover + self.close() + + def close(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._h2_state.close_connection() + self._state = HTTPConnectionState.CLOSED + self._network_stream.close() + + # Wrappers around network read/write operations... + + def _read_incoming_data(self, request: Request) -> list[h2.events.Event]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + if self._read_exception is not None: + raise self._read_exception # pragma: no cover + + try: + data = self._network_stream.read(self.READ_NUM_BYTES, timeout) + if data == b"": + raise RemoteProtocolError("Server disconnected") + except Exception as exc: + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future reads. + # (For example, this means that a single read timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._read_exception = exc + self._connection_error = True + raise exc + + events: list[h2.events.Event] = self._h2_state.receive_data(data) + + return events + + def _write_outgoing_data(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + with self._write_lock: + data_to_send = self._h2_state.data_to_send() + + if self._write_exception is not None: + raise self._write_exception # pragma: no cover + + try: + self._network_stream.write(data_to_send, timeout) + except Exception as exc: # pragma: no cover + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future write. + # (For example, this means that a single write timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._write_exception = exc + self._connection_error = True + raise exc + + # Flow control... + + def _wait_for_outgoing_flow(self, request: Request, stream_id: int) -> int: + """ + Returns the maximum allowable outgoing flow for a given stream. + + If the allowable flow is zero, then waits on the network until + WindowUpdated frames have increased the flow rate. + https://tools.ietf.org/html/rfc7540#section-6.9 + """ + local_flow: int = self._h2_state.local_flow_control_window(stream_id) + max_frame_size: int = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + while flow <= 0: + self._receive_events(request) + local_flow = self._h2_state.local_flow_control_window(stream_id) + max_frame_size = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + return flow + + # Interface for connection pooling... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_connected(self) -> bool: + return not self.is_closed() + + def is_available(self) -> bool: + return ( + self._state != HTTPConnectionState.CLOSED + and not self._connection_error + and not self._used_all_stream_ids + and not (self._h2_state.state_machine.state == h2.connection.ConnectionState.CLOSED) + ) + + def has_expired(self) -> bool: + now = time.monotonic() + # Read `_expire_at` once into a local: on free-threaded builds another + # thread may reset it to `None` between the check and the comparison. + expire_at = self._expire_at + return expire_at is not None and now > expire_at + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def can_multiplex(self) -> bool: + return True + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return f"{origin!r}, HTTP/2, {self._state.name}, Request Count: {self._request_count}" + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return f"<{class_name} [{origin!r}, {self._state.name}, Request Count: {self._request_count}]>" + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + def __enter__(self) -> HTTP2Connection: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self.close() + + +class HTTP2ConnectionByteStream: + def __init__(self, connection: HTTP2Connection, request: Request, stream_id: int) -> None: + self._connection = connection + self._request = request + self._stream_id = stream_id + self._closed = False + + def __iter__(self) -> Generator[bytes]: + kwargs = {"request": self._request, "stream_id": self._stream_id} + try: + with Trace("receive_response_body", logger, self._request, kwargs): + with safe_iterate( + self._connection._receive_response_body(request=self._request, stream_id=self._stream_id) + ) as iterator: + for chunk in iterator: + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with ShieldCancellation(): + self.close() + raise exc + + def close(self) -> None: + if not self._closed: + self._closed = True + kwargs = {"stream_id": self._stream_id} + with Trace("response_closed", logger, self._request, kwargs): + self._connection._response_closed(stream_id=self._stream_id) diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/http_proxy.py b/venv/lib/python3.12/site-packages/httpcore2/_sync/http_proxy.py new file mode 100644 index 0000000..f7f6677 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_sync/http_proxy.py @@ -0,0 +1,353 @@ +from __future__ import annotations + +import base64 +import logging +import ssl +import typing + +from .._backends.base import SOCKET_OPTION, NetworkBackend +from .._exceptions import ProxyError +from .._models import ( + URL, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, +) +from .._ssl import default_ssl_context +from .._synchronization import Lock +from .._trace import Trace +from .connection import HTTPConnection +from .connection_pool import ConnectionPool +from .http11 import HTTP11Connection +from .interfaces import ConnectionInterface + +ByteOrStr = bytes | str +HeadersAsSequence = typing.Sequence[tuple[ByteOrStr, ByteOrStr]] +HeadersAsMapping = typing.Mapping[ByteOrStr, ByteOrStr] + + +logger = logging.getLogger("httpcore2.proxy") + + +def merge_headers( + default_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, + override_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, +) -> list[tuple[bytes, bytes]]: + """ + Append default_headers and override_headers, de-duplicating if a key exists + in both cases. + """ + default_headers = [] if default_headers is None else list(default_headers) + override_headers = [] if override_headers is None else list(override_headers) + has_override = set(key.lower() for key, _value in override_headers) + default_headers = [(key, value) for key, value in default_headers if key.lower() not in has_override] + return default_headers + override_headers + + +class HTTPProxy(ConnectionPool): # pragma: no cover + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: URL | bytes | str, + proxy_auth: tuple[bytes | str, bytes | str] | None = None, + proxy_headers: HeadersAsMapping | HeadersAsSequence | None = None, + ssl_context: ssl.SSLContext | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + proxy_auth: Any proxy authentication as a two-tuple of + (username, password). May be either bytes or ascii-only str. + proxy_headers: Any HTTP headers to use for the proxy requests. + For example `{"Proxy-Authorization": "Basic :"}`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore2.default_ssl_context()` + will be used. + proxy_ssl_context: The same as `ssl_context`, but for a proxy server rather than a remote origin. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + local_address=local_address, + uds=uds, + socket_options=socket_options, + ) + + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if self._proxy_url.scheme == b"http" and proxy_ssl_context is not None: # pragma: no cover + raise RuntimeError("The `proxy_ssl_context` argument is not allowed for the http scheme") + + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + if proxy_auth is not None: + username = enforce_bytes(proxy_auth[0], name="proxy_auth") + password = enforce_bytes(proxy_auth[1], name="proxy_auth") + userpass = username + b":" + password + authorization = b"Basic " + base64.b64encode(userpass) + self._proxy_headers = [(b"Proxy-Authorization", authorization)] + self._proxy_headers + + def create_connection(self, origin: Origin) -> ConnectionInterface: + if origin.scheme == b"http": + return ForwardHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + proxy_ssl_context=self._proxy_ssl_context, + ) + return TunnelHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + ssl_context=self._ssl_context, + proxy_ssl_context=self._proxy_ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class ForwardHTTPConnection(ConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_headers: HeadersAsMapping | HeadersAsSequence | None = None, + keepalive_expiry: float | None = None, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + ) -> None: + self._connection = HTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._remote_origin = remote_origin + + def handle_request(self, request: Request) -> Response: + headers = merge_headers(self._proxy_headers, request.headers) + url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=bytes(request.url), + ) + proxy_request = Request( + method=request.method, + url=url, + headers=headers, + content=request.stream, + extensions={key: value for key, value in request.extensions.items() if key != "sni_hostname"}, + ) + return self._connection.handle_request(proxy_request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def close(self) -> None: + self._connection.close() + + def info(self) -> str: + return self._connection.info() + + def is_connected(self) -> bool: + return self._connection.is_connected() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + +class TunnelHTTPConnection(ConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + ssl_context: ssl.SSLContext | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + proxy_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + self._connection: ConnectionInterface = HTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._connect_lock = Lock() + self._connected = False + + def handle_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("connect", None) + + with self._connect_lock: + if not self._connected: + target = b"%b:%d" % (self._remote_origin.host, self._remote_origin.port) + + connect_url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=target, + ) + connect_headers = merge_headers([(b"Host", target), (b"Accept", b"*/*")], self._proxy_headers) + connect_request = Request( + method=b"CONNECT", + url=connect_url, + headers=connect_headers, + extensions={key: value for key, value in request.extensions.items() if key != "sni_hostname"}, + ) + connect_response = self._connection.handle_request(connect_request) + + if connect_response.status < 200 or connect_response.status > 299: + reason_bytes = connect_response.extensions.get("reason_phrase", b"") + reason_str = reason_bytes.decode("ascii", errors="ignore") + msg = f"{connect_response.status} {reason_str}" + self._connection.close() + raise ProxyError(msg) + + stream = connect_response.extensions["network_stream"] + + # Upgrade the stream to SSL + ssl_context = default_ssl_context() if self._ssl_context is None else self._ssl_context + alpn_protocols = (["h2", "http/1.1"] if self._http1 else ["h2"]) if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + server_hostname = request.extensions.get("sni_hostname") or self._remote_origin.host.decode("ascii") + kwargs = { + "ssl_context": ssl_context, + "server_hostname": server_hostname, + "timeout": timeout, + } + with Trace("start_tls", logger, request, kwargs) as trace: + stream = stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ssl_object is not None and ssl_object.selected_alpn_protocol() == "h2" + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import HTTP2Connection + + self._connection = HTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = HTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + + self._connected = True + return self._connection.handle_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def close(self) -> None: + self._connection.close() + + def info(self) -> str: + return self._connection.info() + + def is_connected(self) -> bool: + return self._connection.is_connected() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/interfaces.py b/venv/lib/python3.12/site-packages/httpcore2/_sync/interfaces.py new file mode 100644 index 0000000..0e66bd1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_sync/interfaces.py @@ -0,0 +1,160 @@ +from __future__ import annotations + +import contextlib +import typing +from collections.abc import Generator + +from .._models import ( + URL, + Extensions, + HeaderTypes, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, + include_request_headers, +) + + +class RequestInterface: + def request( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterator[bytes] | None = None, + extensions: Extensions | None = None, + ) -> Response: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = self.handle_request(request) + try: + response.read() + finally: + response.close() + return response + + @contextlib.contextmanager + def stream( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterator[bytes] | None = None, + extensions: Extensions | None = None, + ) -> Generator[Response]: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = self.handle_request(request) + try: + yield response + finally: + response.close() + + def handle_request(self, request: Request) -> Response: + raise NotImplementedError() # pragma: no cover + + +class ConnectionInterface(RequestInterface): + def close(self) -> None: + raise NotImplementedError() # pragma: no cover + + def info(self) -> str: + raise NotImplementedError() # pragma: no cover + + def can_handle_request(self, origin: Origin) -> bool: + raise NotImplementedError() # pragma: no cover + + def is_connected(self) -> bool: + """ + Return `True` if the connection is open (the underlying socket has been + established). A connection in the NEW state (just created but not yet + connected) returns `False`. + + Note: for some implementations `is_connected() != not is_closed()`. + The default implementation returns `not self.is_closed()`, which is + correct for connections that are never in the NEW (pre-TCP) state. + """ + return not self.is_closed() # pragma: no cover + + def is_available(self) -> bool: + """ + Return `True` if the connection is currently able to accept an + outgoing request. + + An HTTP/1.1 connection will only be available if it is currently idle. + + An HTTP/2 connection will be available so long as the stream ID space is + not yet exhausted, and the connection is not in an error state. + + While the connection is being established we may not yet know if it is going + to result in an HTTP/1.1 or HTTP/2 connection. The connection should be + treated as being available, but might ultimately raise `NewConnectionRequired` + required exceptions if multiple requests are attempted over a connection + that ends up being established as HTTP/1.1. + """ + raise NotImplementedError() # pragma: no cover + + def has_expired(self) -> bool: + """ + Return `True` if the connection is in a state where it should be closed. + + This either means that the connection is idle and it has passed the + expiry time on its keep-alive, or that server has sent an EOF. + """ + raise NotImplementedError() # pragma: no cover + + def is_idle(self) -> bool: + """ + Return `True` if the connection is currently idle. + """ + raise NotImplementedError() # pragma: no cover + + def can_multiplex(self) -> bool: + """ + Return `True` if the connection can serve multiple requests + concurrently, such as an established HTTP/2 connection. + + The default covers HTTP/1.1-style implementations, which serve a + single request at a time. + """ + return False + + def is_closed(self) -> bool: + """ + Return `True` if the connection has been closed. + + Used when a response is closed to determine if the connection may be + returned to the connection pool or not. + """ + raise NotImplementedError() # pragma: no cover diff --git a/venv/lib/python3.12/site-packages/httpcore2/_sync/socks_proxy.py b/venv/lib/python3.12/site-packages/httpcore2/_sync/socks_proxy.py new file mode 100644 index 0000000..b49a576 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_sync/socks_proxy.py @@ -0,0 +1,327 @@ +from __future__ import annotations + +import logging +import ssl + +import socksio + +from .._backends.sync import SyncBackend +from .._backends.base import NetworkBackend, NetworkStream +from .._exceptions import ConnectionNotAvailable, ProxyError +from .._models import URL, Origin, Request, Response, enforce_bytes, enforce_url +from .._ssl import default_ssl_context +from .._synchronization import Lock +from .._trace import Trace +from .connection_pool import ConnectionPool +from .http11 import HTTP11Connection +from .interfaces import ConnectionInterface + +logger = logging.getLogger("httpcore2.socks") + + +AUTH_METHODS = { + b"\x00": "NO AUTHENTICATION REQUIRED", + b"\x01": "GSSAPI", + b"\x02": "USERNAME/PASSWORD", + b"\xff": "NO ACCEPTABLE METHODS", +} + +REPLY_CODES = { + b"\x00": "Succeeded", + b"\x01": "General SOCKS server failure", + b"\x02": "Connection not allowed by ruleset", + b"\x03": "Network unreachable", + b"\x04": "Host unreachable", + b"\x05": "Connection refused", + b"\x06": "TTL expired", + b"\x07": "Command not supported", + b"\x08": "Address type not supported", +} + + +def _init_socks5_connection( + stream: NetworkStream, + *, + host: bytes, + port: int, + auth: tuple[bytes, bytes] | None = None, + timeouts: dict[str, float | None] | None = None, +) -> None: + timeouts = timeouts or {} + write_timeout = timeouts.get("write", None) + read_timeout = timeouts.get("read", None) + conn = socksio.socks5.SOCKS5Connection() + + # Auth method request + auth_method = ( + socksio.socks5.SOCKS5AuthMethod.NO_AUTH_REQUIRED + if auth is None + else socksio.socks5.SOCKS5AuthMethod.USERNAME_PASSWORD + ) + conn.send(socksio.socks5.SOCKS5AuthMethodsRequest([auth_method])) + outgoing_bytes = conn.data_to_send() + stream.write(outgoing_bytes, timeout=write_timeout) + + # Auth method response + incoming_bytes = stream.read(max_bytes=4096, timeout=read_timeout) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5AuthReply) + if response.method != auth_method: + requested = AUTH_METHODS.get(auth_method, "UNKNOWN") + responded = AUTH_METHODS.get(response.method, "UNKNOWN") + raise ProxyError(f"Requested {requested} from proxy server, but got {responded}.") + + if response.method == socksio.socks5.SOCKS5AuthMethod.USERNAME_PASSWORD: + # Username/password request + assert auth is not None + username, password = auth + conn.send(socksio.socks5.SOCKS5UsernamePasswordRequest(username, password)) + outgoing_bytes = conn.data_to_send() + stream.write(outgoing_bytes, timeout=write_timeout) + + # Username/password response + incoming_bytes = stream.read(max_bytes=4096, timeout=read_timeout) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5UsernamePasswordReply) + if not response.success: + raise ProxyError("Invalid username/password") + + # Connect request + conn.send(socksio.socks5.SOCKS5CommandRequest.from_address(socksio.socks5.SOCKS5Command.CONNECT, (host, port))) + outgoing_bytes = conn.data_to_send() + stream.write(outgoing_bytes, timeout=write_timeout) + + # Connect response + incoming_bytes = stream.read(max_bytes=4096, timeout=read_timeout) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5Reply) + if response.reply_code != socksio.socks5.SOCKS5ReplyCode.SUCCEEDED: + reply_code = REPLY_CODES.get(response.reply_code, "UNKOWN") + raise ProxyError(f"Proxy Server could not connect: {reply_code}.") + + +class SOCKSProxy(ConnectionPool): # pragma: no cover + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: URL | bytes | str, + proxy_auth: tuple[bytes | str, bytes | str] | None = None, + ssl_context: ssl.SSLContext | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + network_backend: NetworkBackend | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore2.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + ) + self._ssl_context = ssl_context + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if proxy_auth is not None: + username, password = proxy_auth + username_bytes = enforce_bytes(username, name="proxy_auth") + password_bytes = enforce_bytes(password, name="proxy_auth") + self._proxy_auth: tuple[bytes, bytes] | None = ( + username_bytes, + password_bytes, + ) + else: + self._proxy_auth = None + + def create_connection(self, origin: Origin) -> ConnectionInterface: + return Socks5Connection( + proxy_origin=self._proxy_url.origin, + remote_origin=origin, + proxy_auth=self._proxy_auth, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class Socks5Connection(ConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_auth: tuple[bytes, bytes] | None = None, + ssl_context: ssl.SSLContext | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + network_backend: NetworkBackend | None = None, + ) -> None: + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._proxy_auth = proxy_auth + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + + self._network_backend: NetworkBackend = SyncBackend() if network_backend is None else network_backend + self._connect_lock = Lock() + self._connection: ConnectionInterface | None = None + self._connect_failed = False + + def handle_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + with self._connect_lock: + if self._connection is None: + try: + # Connect to the proxy + kwargs = { + "host": self._proxy_origin.host.decode("ascii"), + "port": self._proxy_origin.port, + "timeout": timeout, + } + with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + + # Connect to the remote host using socks5 + kwargs = { + "stream": stream, + "host": self._remote_origin.host.decode("ascii"), + "port": self._remote_origin.port, + "auth": self._proxy_auth, + "timeouts": timeouts, + } + with Trace("setup_socks5_connection", logger, request, kwargs) as trace: + _init_socks5_connection(**kwargs) + trace.return_value = stream + + # Upgrade the stream to SSL + if self._remote_origin.scheme in (b"https", b"wss"): + ssl_context = default_ssl_context() if self._ssl_context is None else self._ssl_context + alpn_protocols = ( + (["h2", "http/1.1"] if self._http1 else ["h2"]) if self._http2 else ["http/1.1"] + ) + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname or self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + with Trace("start_tls", logger, request, kwargs) as trace: + stream = stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ssl_object is not None and ssl_object.selected_alpn_protocol() == "h2" + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or (self._http2 and not self._http1): # pragma: no cover + from .http2 import HTTP2Connection + + self._connection = HTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = HTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except Exception as exc: + self._connect_failed = True + raise exc + elif not self._connection.is_available(): # pragma: no cover + raise ConnectionNotAvailable() + + return self._connection.handle_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def close(self) -> None: + if self._connection is not None: + self._connection.close() + + def is_connected(self) -> bool: + return self._connection is not None and self._connection.is_connected() + + def is_available(self) -> bool: + if self._connection is None: # pragma: no cover + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 and (self._remote_origin.scheme == b"https" or not self._http1) and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: # pragma: no cover + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: # pragma: no cover + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: # pragma: no cover + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: # pragma: no cover + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/venv/lib/python3.12/site-packages/httpcore2/_synchronization.py b/venv/lib/python3.12/site-packages/httpcore2/_synchronization.py new file mode 100644 index 0000000..b3f8ade --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_synchronization.py @@ -0,0 +1,313 @@ +from __future__ import annotations + +import threading +import types + +from ._exceptions import ExceptionMapping, PoolTimeout, map_exceptions + +# Our async synchronization primitives use either 'anyio' or 'trio' depending +# on if they're running under asyncio or trio. + +try: + import trio +except (ImportError, NotImplementedError): # pragma: no cover + trio = None # type: ignore + +try: + import anyio +except ImportError: # pragma: no cover + anyio = None # type: ignore + +try: + import sniffio +except ImportError: # pragma: no cover + sniffio = None # type: ignore + + +def current_async_library() -> str: + # Determine if we're running under trio or asyncio. + # See https://sniffio.readthedocs.io/en/latest/ + if sniffio is None: # pragma: no cover + environment = "asyncio" + else: + environment = sniffio.current_async_library() + + if environment not in ("asyncio", "trio"): # pragma: no cover + raise RuntimeError("Running under an unsupported async environment.") + + if environment == "asyncio" and anyio is None: # pragma: no cover + raise RuntimeError("Running with asyncio requires installation of 'httpcore[asyncio]'.") + + if environment == "trio" and trio is None: # pragma: no cover + raise RuntimeError("Running with trio requires installation of 'httpcore[trio]'.") + + return environment + + +class AsyncLock: + """ + This is a standard lock. + + In the sync case `Lock` provides thread locking. + In the async case `AsyncLock` provides async locking. + """ + + def __init__(self) -> None: + self._backend = "" + + def setup(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a lock with the correct implementation. + """ + self._backend = current_async_library() + if self._backend == "trio": + self._trio_lock = trio.Lock() + elif self._backend == "asyncio": + self._anyio_lock = anyio.Lock(fast_acquire=True) + + async def __aenter__(self) -> AsyncLock: + if not self._backend: + self.setup() + + if self._backend == "trio": + await self._trio_lock.acquire() + elif self._backend == "asyncio": + await self._anyio_lock.acquire() + + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + if self._backend == "trio": + self._trio_lock.release() + elif self._backend == "asyncio": + self._anyio_lock.release() + + +class AsyncThreadLock: + """ + This is a threading-only lock for no-I/O contexts. + + In the sync case `ThreadLock` provides thread locking. + In the async case `AsyncThreadLock` is a no-op. + """ + + def __enter__(self) -> AsyncThreadLock: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + pass + + +class AsyncEvent: + def __init__(self) -> None: + self._backend = "" + + def setup(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a lock with the correct implementation. + """ + self._backend = current_async_library() + if self._backend == "trio": + self._trio_event = trio.Event() + elif self._backend == "asyncio": + self._anyio_event = anyio.Event() + + def set(self) -> None: + if not self._backend: + self.setup() + + if self._backend == "trio": + self._trio_event.set() + elif self._backend == "asyncio": + self._anyio_event.set() + + async def wait(self, timeout: float | None = None) -> None: + if not self._backend: + self.setup() + + if self._backend == "trio": + trio_exc_map: ExceptionMapping = {trio.TooSlowError: PoolTimeout} + timeout_or_inf = float("inf") if timeout is None else timeout + with map_exceptions(trio_exc_map): + with trio.fail_after(timeout_or_inf): + await self._trio_event.wait() + elif self._backend == "asyncio": + anyio_exc_map: ExceptionMapping = {TimeoutError: PoolTimeout} + with map_exceptions(anyio_exc_map): + with anyio.fail_after(timeout): + await self._anyio_event.wait() + + +class AsyncSemaphore: + def __init__(self, bound: int) -> None: + self._bound = bound + self._backend = "" + + def setup(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a semaphore with the correct implementation. + """ + self._backend = current_async_library() + if self._backend == "trio": + self._trio_semaphore = trio.Semaphore(initial_value=self._bound, max_value=self._bound) + elif self._backend == "asyncio": + self._anyio_semaphore = anyio.Semaphore(initial_value=self._bound, max_value=self._bound, fast_acquire=True) + + async def acquire(self) -> None: + if not self._backend: + self.setup() + + if self._backend == "trio": + await self._trio_semaphore.acquire() + elif self._backend == "asyncio": + await self._anyio_semaphore.acquire() + + async def release(self) -> None: + if self._backend == "trio": + self._trio_semaphore.release() + elif self._backend == "asyncio": + self._anyio_semaphore.release() + + +class AsyncShieldCancellation: + # For certain portions of our codebase where we're dealing with + # closing connections during exception handling we want to shield + # the operation from being cancelled. + # + # with AsyncShieldCancellation(): + # ... # clean-up operations, shielded from cancellation. + + def __init__(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a shielded scope with the correct implementation. + """ + self._backend = current_async_library() + + if self._backend == "trio": + self._trio_shield = trio.CancelScope(shield=True) + elif self._backend == "asyncio": + self._anyio_shield = anyio.CancelScope(shield=True) + + def __enter__(self) -> AsyncShieldCancellation: + if self._backend == "trio": + self._trio_shield.__enter__() + elif self._backend == "asyncio": + self._anyio_shield.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + if self._backend == "trio": + self._trio_shield.__exit__(exc_type, exc_value, traceback) + elif self._backend == "asyncio": + self._anyio_shield.__exit__(exc_type, exc_value, traceback) + + +# Our thread-based synchronization primitives... + + +class Lock: + """ + This is a standard lock. + + In the sync case `Lock` provides thread locking. + In the async case `AsyncLock` provides async locking. + """ + + def __init__(self) -> None: + self._lock = threading.Lock() + + def __enter__(self) -> Lock: + self._lock.acquire() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self._lock.release() + + +class ThreadLock: + """ + This is a threading-only lock for no-I/O contexts. + + In the sync case `ThreadLock` provides thread locking. + In the async case `AsyncThreadLock` is a no-op. + """ + + def __init__(self) -> None: + self._lock = threading.RLock() + + def __enter__(self) -> ThreadLock: + self._lock.acquire() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self._lock.release() + + +class Event: + def __init__(self) -> None: + self._event = threading.Event() + + def set(self) -> None: + self._event.set() + + def wait(self, timeout: float | None = None) -> None: + if timeout == float("inf"): # pragma: no cover + timeout = None + if not self._event.wait(timeout=timeout): + raise PoolTimeout() # pragma: no cover + + +class Semaphore: + def __init__(self, bound: int) -> None: + self._semaphore = threading.Semaphore(value=bound) + + def acquire(self) -> None: + self._semaphore.acquire() + + def release(self) -> None: + self._semaphore.release() + + +class ShieldCancellation: + # Thread-synchronous codebases don't support cancellation semantics. + # We have this class because we need to mirror the async and sync + # cases within our package, but it's just a no-op. + def __enter__(self) -> ShieldCancellation: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + pass diff --git a/venv/lib/python3.12/site-packages/httpcore2/_trace.py b/venv/lib/python3.12/site-packages/httpcore2/_trace.py new file mode 100644 index 0000000..8e33ec7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_trace.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +import inspect +import logging +import types +import typing + +from ._models import Request + + +class Trace: + def __init__( + self, + name: str, + logger: logging.Logger, + request: Request | None = None, + kwargs: dict[str, typing.Any] | None = None, + ) -> None: + self.name = name + self.logger = logger + self.trace_extension = None if request is None else request.extensions.get("trace") + self.debug = self.logger.isEnabledFor(logging.DEBUG) + self.kwargs = kwargs or {} + self.return_value: typing.Any = None + self.should_trace = self.debug or self.trace_extension is not None + self.prefix = self.logger.name.split(".")[-1] + + def trace(self, name: str, info: dict[str, typing.Any]) -> None: + if self.trace_extension is not None: + prefix_and_name = f"{self.prefix}.{name}" + ret = self.trace_extension(prefix_and_name, info) + if inspect.iscoroutine(ret): # pragma: no cover + raise TypeError( + "If you are using a synchronous interface, " + "the callback of the `trace` extension should " + "be a normal function instead of an asynchronous function." + ) + + if self.debug: + if not info or "return_value" in info and info["return_value"] is None: + message = name + else: + args = " ".join([f"{key}={value!r}" for key, value in info.items()]) + message = f"{name} {args}" + self.logger.debug(message) + + def __enter__(self) -> Trace: + if self.should_trace: + info = self.kwargs + self.trace(f"{self.name}.started", info) + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + if self.should_trace: + if exc_value is None: + info = {"return_value": self.return_value} + self.trace(f"{self.name}.complete", info) + else: + info = {"exception": exc_value} + self.trace(f"{self.name}.failed", info) + + async def atrace(self, name: str, info: dict[str, typing.Any]) -> None: + if self.trace_extension is not None: + prefix_and_name = f"{self.prefix}.{name}" + coro = self.trace_extension(prefix_and_name, info) + if not inspect.iscoroutine(coro): # pragma: no cover + raise TypeError( + "If you're using an asynchronous interface, " + "the callback of the `trace` extension should " + "be an asynchronous function rather than a normal function." + ) + await coro + + if self.debug: + if not info or "return_value" in info and info["return_value"] is None: + message = name + else: + args = " ".join([f"{key}={value!r}" for key, value in info.items()]) + message = f"{name} {args}" + self.logger.debug(message) + + async def __aenter__(self) -> Trace: + if self.should_trace: + info = self.kwargs + await self.atrace(f"{self.name}.started", info) + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + if self.should_trace: + if exc_value is None: + info = {"return_value": self.return_value} + await self.atrace(f"{self.name}.complete", info) + else: + info = {"exception": exc_value} + await self.atrace(f"{self.name}.failed", info) diff --git a/venv/lib/python3.12/site-packages/httpcore2/_utils.py b/venv/lib/python3.12/site-packages/httpcore2/_utils.py new file mode 100644 index 0000000..4816b8d --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpcore2/_utils.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import select +import socket +import sys +import typing +from collections.abc import ( + AsyncIterable, + AsyncIterator, + Generator, + Iterable, + Iterator, +) +from contextlib import AbstractAsyncContextManager, aclosing, contextmanager, nullcontext +from types import AsyncGeneratorType + +T = typing.TypeVar("T") + + +def is_socket_readable(sock: socket.socket | None) -> bool: + """ + Return whether a socket, as identified by its file descriptor, is readable. + "A socket is readable" means that the read buffer isn't empty, i.e. that calling + .recv() on it would immediately return some data. + """ + # NOTE: we want check for readability without actually attempting to read, because + # we don't want to block forever if it's not readable. + + # In the case that the socket no longer exists, or cannot return a file + # descriptor, we treat it as being readable, as if it the next read operation + # on it is ready to return the terminating `b""`. + sock_fd = None if sock is None else sock.fileno() + if sock_fd is None or sock_fd < 0: # pragma: no cover + return True + + # The implementation below was stolen from: + # https://github.com/python-trio/trio/blob/20ee2b1b7376db637435d80e266212a35837ddcc/trio/_socket.py#L471-L478 + # See also: https://github.com/encode/httpcore/pull/193#issuecomment-703129316 + + # Use select.select on Windows, and when poll is unavailable and select.poll + # everywhere else. (E.g. When eventlet is in use. See #327) + if sys.platform == "win32" or getattr(select, "poll", None) is None: # pragma: no cover + rready, _, _ = select.select([sock_fd], [], [], 0) + return bool(rready) + p = select.poll() + p.register(sock_fd, select.POLLIN) + return bool(p.poll(0)) + + +def safe_async_iterate( + iterable_or_iterator: AsyncIterable[T] | AsyncIterator[T], / +) -> AbstractAsyncContextManager[AsyncIterator[T]]: + iterator = ( + iterable_or_iterator if isinstance(iterable_or_iterator, AsyncIterator) else iterable_or_iterator.__aiter__() + ) + if isinstance(iterator, AsyncGeneratorType): + return aclosing(iterator) + return nullcontext(iterator) + + +@contextmanager +def safe_iterate(iterable_or_iterator: Iterable[T] | Iterator[T], /) -> Generator[Iterator[T], None, None]: + # This is boilerplate code, only needed to make unasync happy + iterator = iterable_or_iterator if isinstance(iterable_or_iterator, Iterator) else iterable_or_iterator.__iter__() + yield iterator diff --git a/venv/lib/python3.12/site-packages/httpcore2/py.typed b/venv/lib/python3.12/site-packages/httpcore2/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/METADATA b/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/METADATA new file mode 100644 index 0000000..b0d2b19 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/METADATA @@ -0,0 +1,203 @@ +Metadata-Version: 2.3 +Name: httpx +Version: 0.28.1 +Summary: The next generation HTTP client. +Project-URL: Changelog, https://github.com/encode/httpx/blob/master/CHANGELOG.md +Project-URL: Documentation, https://www.python-httpx.org +Project-URL: Homepage, https://github.com/encode/httpx +Project-URL: Source, https://github.com/encode/httpx +Author-email: Tom Christie +License: BSD-3-Clause +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Web Environment +Classifier: Framework :: AsyncIO +Classifier: Framework :: Trio +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.8 +Requires-Dist: anyio +Requires-Dist: certifi +Requires-Dist: httpcore==1.* +Requires-Dist: idna +Provides-Extra: brotli +Requires-Dist: brotli; (platform_python_implementation == 'CPython') and extra == 'brotli' +Requires-Dist: brotlicffi; (platform_python_implementation != 'CPython') and extra == 'brotli' +Provides-Extra: cli +Requires-Dist: click==8.*; extra == 'cli' +Requires-Dist: pygments==2.*; extra == 'cli' +Requires-Dist: rich<14,>=10; extra == 'cli' +Provides-Extra: http2 +Requires-Dist: h2<5,>=3; extra == 'http2' +Provides-Extra: socks +Requires-Dist: socksio==1.*; extra == 'socks' +Provides-Extra: zstd +Requires-Dist: zstandard>=0.18.0; extra == 'zstd' +Description-Content-Type: text/markdown + +

+ HTTPX +

+ +

HTTPX - A next-generation HTTP client for Python.

+ +

+ + Test Suite + + + Package version + +

+ +HTTPX is a fully featured HTTP client library for Python 3. It includes **an integrated command line client**, has support for both **HTTP/1.1 and HTTP/2**, and provides both **sync and async APIs**. + +--- + +Install HTTPX using pip: + +```shell +$ pip install httpx +``` + +Now, let's get started: + +```pycon +>>> import httpx +>>> r = httpx.get('https://www.example.org/') +>>> r + +>>> r.status_code +200 +>>> r.headers['content-type'] +'text/html; charset=UTF-8' +>>> r.text +'\n\n\nExample Domain...' +``` + +Or, using the command-line client. + +```shell +$ pip install 'httpx[cli]' # The command line client is an optional dependency. +``` + +Which now allows us to use HTTPX directly from the command-line... + +

+ httpx --help +

+ +Sending a request... + +

+ httpx http://httpbin.org/json +

+ +## Features + +HTTPX builds on the well-established usability of `requests`, and gives you: + +* A broadly [requests-compatible API](https://www.python-httpx.org/compatibility/). +* An integrated command-line client. +* HTTP/1.1 [and HTTP/2 support](https://www.python-httpx.org/http2/). +* Standard synchronous interface, but with [async support if you need it](https://www.python-httpx.org/async/). +* Ability to make requests directly to [WSGI applications](https://www.python-httpx.org/advanced/transports/#wsgi-transport) or [ASGI applications](https://www.python-httpx.org/advanced/transports/#asgi-transport). +* Strict timeouts everywhere. +* Fully type annotated. +* 100% test coverage. + +Plus all the standard features of `requests`... + +* International Domains and URLs +* Keep-Alive & Connection Pooling +* Sessions with Cookie Persistence +* Browser-style SSL Verification +* Basic/Digest Authentication +* Elegant Key/Value Cookies +* Automatic Decompression +* Automatic Content Decoding +* Unicode Response Bodies +* Multipart File Uploads +* HTTP(S) Proxy Support +* Connection Timeouts +* Streaming Downloads +* .netrc Support +* Chunked Requests + +## Installation + +Install with pip: + +```shell +$ pip install httpx +``` + +Or, to include the optional HTTP/2 support, use: + +```shell +$ pip install httpx[http2] +``` + +HTTPX requires Python 3.8+. + +## Documentation + +Project documentation is available at [https://www.python-httpx.org/](https://www.python-httpx.org/). + +For a run-through of all the basics, head over to the [QuickStart](https://www.python-httpx.org/quickstart/). + +For more advanced topics, see the [Advanced Usage](https://www.python-httpx.org/advanced/) section, the [async support](https://www.python-httpx.org/async/) section, or the [HTTP/2](https://www.python-httpx.org/http2/) section. + +The [Developer Interface](https://www.python-httpx.org/api/) provides a comprehensive API reference. + +To find out about tools that integrate with HTTPX, see [Third Party Packages](https://www.python-httpx.org/third_party_packages/). + +## Contribute + +If you want to contribute with HTTPX check out the [Contributing Guide](https://www.python-httpx.org/contributing/) to learn how to start. + +## Dependencies + +The HTTPX project relies on these excellent libraries: + +* `httpcore` - The underlying transport implementation for `httpx`. + * `h11` - HTTP/1.1 support. +* `certifi` - SSL certificates. +* `idna` - Internationalized domain name support. +* `sniffio` - Async library autodetection. + +As well as these optional installs: + +* `h2` - HTTP/2 support. *(Optional, with `httpx[http2]`)* +* `socksio` - SOCKS proxy support. *(Optional, with `httpx[socks]`)* +* `rich` - Rich terminal support. *(Optional, with `httpx[cli]`)* +* `click` - Command line client support. *(Optional, with `httpx[cli]`)* +* `brotli` or `brotlicffi` - Decoding for "brotli" compressed responses. *(Optional, with `httpx[brotli]`)* +* `zstandard` - Decoding for "zstd" compressed responses. *(Optional, with `httpx[zstd]`)* + +A huge amount of credit is due to `requests` for the API layout that +much of this work follows, as well as to `urllib3` for plenty of design +inspiration around the lower-level networking details. + +--- + +

HTTPX is BSD licensed code.
Designed & crafted with care.

— 🦋 —

+ +## Release Information + +### Fixed + +* Reintroduced supposedly-private `URLTypes` shortcut. (#2673) + + +--- + +[Full changelog](https://github.com/encode/httpx/blob/master/CHANGELOG.md) diff --git a/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/RECORD new file mode 100644 index 0000000..2c34e6b --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/RECORD @@ -0,0 +1,54 @@ +../../../bin/httpx,sha256=UtbesLLfXuuTppd2kLeNHZmzn3UXH2qTIPVJ02m7akY,177 +httpx-0.28.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +httpx-0.28.1.dist-info/METADATA,sha256=_rubD48-gNV8gZnDBPNcQzboWB0dGNeYPJJ2a4J5OyU,7052 +httpx-0.28.1.dist-info/RECORD,, +httpx-0.28.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87 +httpx-0.28.1.dist-info/entry_points.txt,sha256=2lVkdQmxLA1pNMgSN2eV89o90HCZezhmNwsy6ryKDSA,37 +httpx-0.28.1.dist-info/licenses/LICENSE.md,sha256=TsWdVE8StfU5o6cW_TIaxYzNgDC0ZSIfLIgCAM3yjY0,1508 +httpx/__init__.py,sha256=CsaZe6yZj0rHg6322AWKWHGTMVr9txgEfD5P3_Rrz60,2171 +httpx/__pycache__/__init__.cpython-312.pyc,, +httpx/__pycache__/__version__.cpython-312.pyc,, +httpx/__pycache__/_api.cpython-312.pyc,, +httpx/__pycache__/_auth.cpython-312.pyc,, +httpx/__pycache__/_client.cpython-312.pyc,, +httpx/__pycache__/_config.cpython-312.pyc,, +httpx/__pycache__/_content.cpython-312.pyc,, +httpx/__pycache__/_decoders.cpython-312.pyc,, +httpx/__pycache__/_exceptions.cpython-312.pyc,, +httpx/__pycache__/_main.cpython-312.pyc,, +httpx/__pycache__/_models.cpython-312.pyc,, +httpx/__pycache__/_multipart.cpython-312.pyc,, +httpx/__pycache__/_status_codes.cpython-312.pyc,, +httpx/__pycache__/_types.cpython-312.pyc,, +httpx/__pycache__/_urlparse.cpython-312.pyc,, +httpx/__pycache__/_urls.cpython-312.pyc,, +httpx/__pycache__/_utils.cpython-312.pyc,, +httpx/__version__.py,sha256=LoUyYeOXTieGzuP_64UL0wxdtxjuu_QbOvE7NOg-IqU,108 +httpx/_api.py,sha256=r_Zgs4jIpcPJLqK5dbbSayqo_iVMKFaxZCd-oOHxLEs,11743 +httpx/_auth.py,sha256=Yr3QwaUSK17rGYx-7j-FdicFIzz4Y9FFV-1F4-7RXX4,11891 +httpx/_client.py,sha256=xD-UG67-WMkeltAAOeGGj-cZ2RRTAm19sWRxlFY7_40,65714 +httpx/_config.py,sha256=pPp2U-wicfcKsF-KYRE1LYdt3e6ERGeIoXZ8Gjo3LWc,8547 +httpx/_content.py,sha256=LGGzrJTR3OvN4Mb1GVVNLXkXJH-6oKlwAttO9p5w_yg,8161 +httpx/_decoders.py,sha256=p0dX8I0NEHexs3UGp4SsZutiMhsXrrWl6-GnqVb0iKM,12041 +httpx/_exceptions.py,sha256=bxW7fxzgVMAdNTbwT0Vnq04gJDW1_gI_GFiQPuMyjL0,8527 +httpx/_main.py,sha256=Cg9GMabiTT_swaDfUgIRitSwxLRMSwUDOm7LdSGqlA4,15626 +httpx/_models.py,sha256=4__Guyv1gLxuZChwim8kfQNiIOcJ9acreFOSurvZfms,44700 +httpx/_multipart.py,sha256=KOHEZZl6oohg9mPaKyyu345qq1rJLg35TUG3YAzXB3Y,9843 +httpx/_status_codes.py,sha256=DYn-2ufBgMeXy5s8x3_TB7wjAuAAMewTakPrm5rXEsc,5639 +httpx/_transports/__init__.py,sha256=GbUoBSAOp7z-l-9j5YhMhR3DMIcn6FVLhj072O3Nnno,275 +httpx/_transports/__pycache__/__init__.cpython-312.pyc,, +httpx/_transports/__pycache__/asgi.cpython-312.pyc,, +httpx/_transports/__pycache__/base.cpython-312.pyc,, +httpx/_transports/__pycache__/default.cpython-312.pyc,, +httpx/_transports/__pycache__/mock.cpython-312.pyc,, +httpx/_transports/__pycache__/wsgi.cpython-312.pyc,, +httpx/_transports/asgi.py,sha256=HRfiDYMPt4wQH2gFgHZg4c-i3sblo6bL5GTqcET-xz8,5501 +httpx/_transports/base.py,sha256=kZS_VMbViYfF570pogUCJ1bulz-ybfL51Pqs9yktebU,2523 +httpx/_transports/default.py,sha256=AzeaRUyVwCccTyyNJexDf0n1dFfzzydpdIQgvw7PLnk,13983 +httpx/_transports/mock.py,sha256=PTo0d567RITXxGrki6kN7_67wwAxfwiMDcuXJiZCjEo,1232 +httpx/_transports/wsgi.py,sha256=NcPX3Xap_EwCFZWO_OaSyQNuInCYx1QMNbO8GAei6jY,4825 +httpx/_types.py,sha256=Jyh41GQq7AOev8IOWKDAg7zCbvHAfufmW5g_PiTtErY,2965 +httpx/_urlparse.py,sha256=ZAmH47ONfkxrrj-PPYhGeiHjb6AjKCS-ANWIN4OL_KY,18546 +httpx/_urls.py,sha256=dX99VR1DSOHpgo9Aq7PzYO4FKdxqKjwyNp8grf8dHN0,21550 +httpx/_utils.py,sha256=_TVeqAKvxJkKHdz7dFeb4s0LZqQXgeFkXSgfiHBK_1o,8285 +httpx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/WHEEL b/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/WHEEL new file mode 100644 index 0000000..21aaa72 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.26.3 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/entry_points.txt new file mode 100644 index 0000000..8ae9600 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +httpx = httpx:main diff --git a/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/licenses/LICENSE.md b/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/licenses/LICENSE.md new file mode 100644 index 0000000..ab79d16 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/licenses/LICENSE.md @@ -0,0 +1,12 @@ +Copyright © 2019, [Encode OSS Ltd](https://www.encode.io/). +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/httpx/__init__.py b/venv/lib/python3.12/site-packages/httpx/__init__.py new file mode 100644 index 0000000..e9addde --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/__init__.py @@ -0,0 +1,105 @@ +from .__version__ import __description__, __title__, __version__ +from ._api import * +from ._auth import * +from ._client import * +from ._config import * +from ._content import * +from ._exceptions import * +from ._models import * +from ._status_codes import * +from ._transports import * +from ._types import * +from ._urls import * + +try: + from ._main import main +except ImportError: # pragma: no cover + + def main() -> None: # type: ignore + import sys + + print( + "The httpx command line client could not run because the required " + "dependencies were not installed.\nMake sure you've installed " + "everything with: pip install 'httpx[cli]'" + ) + sys.exit(1) + + +__all__ = [ + "__description__", + "__title__", + "__version__", + "ASGITransport", + "AsyncBaseTransport", + "AsyncByteStream", + "AsyncClient", + "AsyncHTTPTransport", + "Auth", + "BaseTransport", + "BasicAuth", + "ByteStream", + "Client", + "CloseError", + "codes", + "ConnectError", + "ConnectTimeout", + "CookieConflict", + "Cookies", + "create_ssl_context", + "DecodingError", + "delete", + "DigestAuth", + "get", + "head", + "Headers", + "HTTPError", + "HTTPStatusError", + "HTTPTransport", + "InvalidURL", + "Limits", + "LocalProtocolError", + "main", + "MockTransport", + "NetRCAuth", + "NetworkError", + "options", + "patch", + "PoolTimeout", + "post", + "ProtocolError", + "Proxy", + "ProxyError", + "put", + "QueryParams", + "ReadError", + "ReadTimeout", + "RemoteProtocolError", + "request", + "Request", + "RequestError", + "RequestNotRead", + "Response", + "ResponseNotRead", + "stream", + "StreamClosed", + "StreamConsumed", + "StreamError", + "SyncByteStream", + "Timeout", + "TimeoutException", + "TooManyRedirects", + "TransportError", + "UnsupportedProtocol", + "URL", + "USE_CLIENT_DEFAULT", + "WriteError", + "WriteTimeout", + "WSGITransport", +] + + +__locals = locals() +for __name in __all__: + if not __name.startswith("__"): + setattr(__locals[__name], "__module__", "httpx") # noqa diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..42a4ef4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/__version__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/__version__.cpython-312.pyc new file mode 100644 index 0000000..9abd687 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/__version__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_api.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_api.cpython-312.pyc new file mode 100644 index 0000000..c815115 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_api.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_auth.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_auth.cpython-312.pyc new file mode 100644 index 0000000..86e57e0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_auth.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_client.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_client.cpython-312.pyc new file mode 100644 index 0000000..5ae8b9c Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_client.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_config.cpython-312.pyc new file mode 100644 index 0000000..c53adfd Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_content.cpython-312.pyc new file mode 100644 index 0000000..7e783cb Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_decoders.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_decoders.cpython-312.pyc new file mode 100644 index 0000000..ecd2074 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_decoders.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_exceptions.cpython-312.pyc new file mode 100644 index 0000000..ca3cf37 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_main.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_main.cpython-312.pyc new file mode 100644 index 0000000..0453add Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_main.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_models.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_models.cpython-312.pyc new file mode 100644 index 0000000..8831d1f Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_models.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_multipart.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_multipart.cpython-312.pyc new file mode 100644 index 0000000..012c5ef Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_multipart.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_status_codes.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_status_codes.cpython-312.pyc new file mode 100644 index 0000000..b8414d1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_status_codes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_types.cpython-312.pyc new file mode 100644 index 0000000..857e920 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_urlparse.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_urlparse.cpython-312.pyc new file mode 100644 index 0000000..38fbb7c Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_urlparse.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_urls.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_urls.cpython-312.pyc new file mode 100644 index 0000000..9930c30 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_urls.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__pycache__/_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/__pycache__/_utils.cpython-312.pyc new file mode 100644 index 0000000..33838bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/__pycache__/_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/__version__.py b/venv/lib/python3.12/site-packages/httpx/__version__.py new file mode 100644 index 0000000..801bfac --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/__version__.py @@ -0,0 +1,3 @@ +__title__ = "httpx" +__description__ = "A next generation HTTP client, for Python 3." +__version__ = "0.28.1" diff --git a/venv/lib/python3.12/site-packages/httpx/_api.py b/venv/lib/python3.12/site-packages/httpx/_api.py new file mode 100644 index 0000000..c3cda1e --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_api.py @@ -0,0 +1,438 @@ +from __future__ import annotations + +import typing +from contextlib import contextmanager + +from ._client import Client +from ._config import DEFAULT_TIMEOUT_CONFIG +from ._models import Response +from ._types import ( + AuthTypes, + CookieTypes, + HeaderTypes, + ProxyTypes, + QueryParamTypes, + RequestContent, + RequestData, + RequestFiles, + TimeoutTypes, +) +from ._urls import URL + +if typing.TYPE_CHECKING: + import ssl # pragma: no cover + + +__all__ = [ + "delete", + "get", + "head", + "options", + "patch", + "post", + "put", + "request", + "stream", +] + + +def request( + method: str, + url: URL | str, + *, + params: QueryParamTypes | None = None, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + trust_env: bool = True, +) -> Response: + """ + Sends an HTTP request. + + **Parameters:** + + * **method** - HTTP method for the new `Request` object: `GET`, `OPTIONS`, + `HEAD`, `POST`, `PUT`, `PATCH`, or `DELETE`. + * **url** - URL for the new `Request` object. + * **params** - *(optional)* Query parameters to include in the URL, as a + string, dictionary, or sequence of two-tuples. + * **content** - *(optional)* Binary content to include in the body of the + request, as bytes or a byte iterator. + * **data** - *(optional)* Form data to include in the body of the request, + as a dictionary. + * **files** - *(optional)* A dictionary of upload files to include in the + body of the request. + * **json** - *(optional)* A JSON serializable object to include in the body + of the request. + * **headers** - *(optional)* Dictionary of HTTP headers to include in the + request. + * **cookies** - *(optional)* Dictionary of Cookie items to include in the + request. + * **auth** - *(optional)* An authentication class to use when sending the + request. + * **proxy** - *(optional)* A proxy URL where all the traffic should be routed. + * **timeout** - *(optional)* The timeout configuration to use when sending + the request. + * **follow_redirects** - *(optional)* Enables or disables HTTP redirects. + * **verify** - *(optional)* Either `True` to use an SSL context with the + default CA bundle, `False` to disable verification, or an instance of + `ssl.SSLContext` to use a custom context. + * **trust_env** - *(optional)* Enables or disables usage of environment + variables for configuration. + + **Returns:** `Response` + + Usage: + + ``` + >>> import httpx + >>> response = httpx.request('GET', 'https://httpbin.org/get') + >>> response + + ``` + """ + with Client( + cookies=cookies, + proxy=proxy, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) as client: + return client.request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + auth=auth, + follow_redirects=follow_redirects, + ) + + +@contextmanager +def stream( + method: str, + url: URL | str, + *, + params: QueryParamTypes | None = None, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + trust_env: bool = True, +) -> typing.Iterator[Response]: + """ + Alternative to `httpx.request()` that streams the response body + instead of loading it into memory at once. + + **Parameters**: See `httpx.request`. + + See also: [Streaming Responses][0] + + [0]: /quickstart#streaming-responses + """ + with Client( + cookies=cookies, + proxy=proxy, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) as client: + with client.stream( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + auth=auth, + follow_redirects=follow_redirects, + ) as response: + yield response + + +def get( + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `GET` request. + + **Parameters**: See `httpx.request`. + + Note that the `data`, `files`, `json` and `content` parameters are not available + on this function, as `GET` requests should not include a request body. + """ + return request( + "GET", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def options( + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends an `OPTIONS` request. + + **Parameters**: See `httpx.request`. + + Note that the `data`, `files`, `json` and `content` parameters are not available + on this function, as `OPTIONS` requests should not include a request body. + """ + return request( + "OPTIONS", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def head( + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `HEAD` request. + + **Parameters**: See `httpx.request`. + + Note that the `data`, `files`, `json` and `content` parameters are not available + on this function, as `HEAD` requests should not include a request body. + """ + return request( + "HEAD", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def post( + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `POST` request. + + **Parameters**: See `httpx.request`. + """ + return request( + "POST", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def put( + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `PUT` request. + + **Parameters**: See `httpx.request`. + """ + return request( + "PUT", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def patch( + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `PATCH` request. + + **Parameters**: See `httpx.request`. + """ + return request( + "PATCH", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def delete( + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + verify: ssl.SSLContext | str | bool = True, + trust_env: bool = True, +) -> Response: + """ + Sends a `DELETE` request. + + **Parameters**: See `httpx.request`. + + Note that the `data`, `files`, `json` and `content` parameters are not available + on this function, as `DELETE` requests should not include a request body. + """ + return request( + "DELETE", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) diff --git a/venv/lib/python3.12/site-packages/httpx/_auth.py b/venv/lib/python3.12/site-packages/httpx/_auth.py new file mode 100644 index 0000000..b03971a --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_auth.py @@ -0,0 +1,348 @@ +from __future__ import annotations + +import hashlib +import os +import re +import time +import typing +from base64 import b64encode +from urllib.request import parse_http_list + +from ._exceptions import ProtocolError +from ._models import Cookies, Request, Response +from ._utils import to_bytes, to_str, unquote + +if typing.TYPE_CHECKING: # pragma: no cover + from hashlib import _Hash + + +__all__ = ["Auth", "BasicAuth", "DigestAuth", "NetRCAuth"] + + +class Auth: + """ + Base class for all authentication schemes. + + To implement a custom authentication scheme, subclass `Auth` and override + the `.auth_flow()` method. + + If the authentication scheme does I/O such as disk access or network calls, or uses + synchronization primitives such as locks, you should override `.sync_auth_flow()` + and/or `.async_auth_flow()` instead of `.auth_flow()` to provide specialized + implementations that will be used by `Client` and `AsyncClient` respectively. + """ + + requires_request_body = False + requires_response_body = False + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + """ + Execute the authentication flow. + + To dispatch a request, `yield` it: + + ``` + yield request + ``` + + The client will `.send()` the response back into the flow generator. You can + access it like so: + + ``` + response = yield request + ``` + + A `return` (or reaching the end of the generator) will result in the + client returning the last response obtained from the server. + + You can dispatch as many requests as is necessary. + """ + yield request + + def sync_auth_flow( + self, request: Request + ) -> typing.Generator[Request, Response, None]: + """ + Execute the authentication flow synchronously. + + By default, this defers to `.auth_flow()`. You should override this method + when the authentication scheme does I/O and/or uses concurrency primitives. + """ + if self.requires_request_body: + request.read() + + flow = self.auth_flow(request) + request = next(flow) + + while True: + response = yield request + if self.requires_response_body: + response.read() + + try: + request = flow.send(response) + except StopIteration: + break + + async def async_auth_flow( + self, request: Request + ) -> typing.AsyncGenerator[Request, Response]: + """ + Execute the authentication flow asynchronously. + + By default, this defers to `.auth_flow()`. You should override this method + when the authentication scheme does I/O and/or uses concurrency primitives. + """ + if self.requires_request_body: + await request.aread() + + flow = self.auth_flow(request) + request = next(flow) + + while True: + response = yield request + if self.requires_response_body: + await response.aread() + + try: + request = flow.send(response) + except StopIteration: + break + + +class FunctionAuth(Auth): + """ + Allows the 'auth' argument to be passed as a simple callable function, + that takes the request, and returns a new, modified request. + """ + + def __init__(self, func: typing.Callable[[Request], Request]) -> None: + self._func = func + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + yield self._func(request) + + +class BasicAuth(Auth): + """ + Allows the 'auth' argument to be passed as a (username, password) pair, + and uses HTTP Basic authentication. + """ + + def __init__(self, username: str | bytes, password: str | bytes) -> None: + self._auth_header = self._build_auth_header(username, password) + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + request.headers["Authorization"] = self._auth_header + yield request + + def _build_auth_header(self, username: str | bytes, password: str | bytes) -> str: + userpass = b":".join((to_bytes(username), to_bytes(password))) + token = b64encode(userpass).decode() + return f"Basic {token}" + + +class NetRCAuth(Auth): + """ + Use a 'netrc' file to lookup basic auth credentials based on the url host. + """ + + def __init__(self, file: str | None = None) -> None: + # Lazily import 'netrc'. + # There's no need for us to load this module unless 'NetRCAuth' is being used. + import netrc + + self._netrc_info = netrc.netrc(file) + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + auth_info = self._netrc_info.authenticators(request.url.host) + if auth_info is None or not auth_info[2]: + # The netrc file did not have authentication credentials for this host. + yield request + else: + # Build a basic auth header with credentials from the netrc file. + request.headers["Authorization"] = self._build_auth_header( + username=auth_info[0], password=auth_info[2] + ) + yield request + + def _build_auth_header(self, username: str | bytes, password: str | bytes) -> str: + userpass = b":".join((to_bytes(username), to_bytes(password))) + token = b64encode(userpass).decode() + return f"Basic {token}" + + +class DigestAuth(Auth): + _ALGORITHM_TO_HASH_FUNCTION: dict[str, typing.Callable[[bytes], _Hash]] = { + "MD5": hashlib.md5, + "MD5-SESS": hashlib.md5, + "SHA": hashlib.sha1, + "SHA-SESS": hashlib.sha1, + "SHA-256": hashlib.sha256, + "SHA-256-SESS": hashlib.sha256, + "SHA-512": hashlib.sha512, + "SHA-512-SESS": hashlib.sha512, + } + + def __init__(self, username: str | bytes, password: str | bytes) -> None: + self._username = to_bytes(username) + self._password = to_bytes(password) + self._last_challenge: _DigestAuthChallenge | None = None + self._nonce_count = 1 + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + if self._last_challenge: + request.headers["Authorization"] = self._build_auth_header( + request, self._last_challenge + ) + + response = yield request + + if response.status_code != 401 or "www-authenticate" not in response.headers: + # If the response is not a 401 then we don't + # need to build an authenticated request. + return + + for auth_header in response.headers.get_list("www-authenticate"): + if auth_header.lower().startswith("digest "): + break + else: + # If the response does not include a 'WWW-Authenticate: Digest ...' + # header, then we don't need to build an authenticated request. + return + + self._last_challenge = self._parse_challenge(request, response, auth_header) + self._nonce_count = 1 + + request.headers["Authorization"] = self._build_auth_header( + request, self._last_challenge + ) + if response.cookies: + Cookies(response.cookies).set_cookie_header(request=request) + yield request + + def _parse_challenge( + self, request: Request, response: Response, auth_header: str + ) -> _DigestAuthChallenge: + """ + Returns a challenge from a Digest WWW-Authenticate header. + These take the form of: + `Digest realm="realm@host.com",qop="auth,auth-int",nonce="abc",opaque="xyz"` + """ + scheme, _, fields = auth_header.partition(" ") + + # This method should only ever have been called with a Digest auth header. + assert scheme.lower() == "digest" + + header_dict: dict[str, str] = {} + for field in parse_http_list(fields): + key, value = field.strip().split("=", 1) + header_dict[key] = unquote(value) + + try: + realm = header_dict["realm"].encode() + nonce = header_dict["nonce"].encode() + algorithm = header_dict.get("algorithm", "MD5") + opaque = header_dict["opaque"].encode() if "opaque" in header_dict else None + qop = header_dict["qop"].encode() if "qop" in header_dict else None + return _DigestAuthChallenge( + realm=realm, nonce=nonce, algorithm=algorithm, opaque=opaque, qop=qop + ) + except KeyError as exc: + message = "Malformed Digest WWW-Authenticate header" + raise ProtocolError(message, request=request) from exc + + def _build_auth_header( + self, request: Request, challenge: _DigestAuthChallenge + ) -> str: + hash_func = self._ALGORITHM_TO_HASH_FUNCTION[challenge.algorithm.upper()] + + def digest(data: bytes) -> bytes: + return hash_func(data).hexdigest().encode() + + A1 = b":".join((self._username, challenge.realm, self._password)) + + path = request.url.raw_path + A2 = b":".join((request.method.encode(), path)) + # TODO: implement auth-int + HA2 = digest(A2) + + nc_value = b"%08x" % self._nonce_count + cnonce = self._get_client_nonce(self._nonce_count, challenge.nonce) + self._nonce_count += 1 + + HA1 = digest(A1) + if challenge.algorithm.lower().endswith("-sess"): + HA1 = digest(b":".join((HA1, challenge.nonce, cnonce))) + + qop = self._resolve_qop(challenge.qop, request=request) + if qop is None: + # Following RFC 2069 + digest_data = [HA1, challenge.nonce, HA2] + else: + # Following RFC 2617/7616 + digest_data = [HA1, challenge.nonce, nc_value, cnonce, qop, HA2] + + format_args = { + "username": self._username, + "realm": challenge.realm, + "nonce": challenge.nonce, + "uri": path, + "response": digest(b":".join(digest_data)), + "algorithm": challenge.algorithm.encode(), + } + if challenge.opaque: + format_args["opaque"] = challenge.opaque + if qop: + format_args["qop"] = b"auth" + format_args["nc"] = nc_value + format_args["cnonce"] = cnonce + + return "Digest " + self._get_header_value(format_args) + + def _get_client_nonce(self, nonce_count: int, nonce: bytes) -> bytes: + s = str(nonce_count).encode() + s += nonce + s += time.ctime().encode() + s += os.urandom(8) + + return hashlib.sha1(s).hexdigest()[:16].encode() + + def _get_header_value(self, header_fields: dict[str, bytes]) -> str: + NON_QUOTED_FIELDS = ("algorithm", "qop", "nc") + QUOTED_TEMPLATE = '{}="{}"' + NON_QUOTED_TEMPLATE = "{}={}" + + header_value = "" + for i, (field, value) in enumerate(header_fields.items()): + if i > 0: + header_value += ", " + template = ( + QUOTED_TEMPLATE + if field not in NON_QUOTED_FIELDS + else NON_QUOTED_TEMPLATE + ) + header_value += template.format(field, to_str(value)) + + return header_value + + def _resolve_qop(self, qop: bytes | None, request: Request) -> bytes | None: + if qop is None: + return None + qops = re.split(b", ?", qop) + if b"auth" in qops: + return b"auth" + + if qops == [b"auth-int"]: + raise NotImplementedError("Digest auth-int support is not yet implemented") + + message = f'Unexpected qop value "{qop!r}" in digest auth' + raise ProtocolError(message, request=request) + + +class _DigestAuthChallenge(typing.NamedTuple): + realm: bytes + nonce: bytes + algorithm: str + opaque: bytes | None + qop: bytes | None diff --git a/venv/lib/python3.12/site-packages/httpx/_client.py b/venv/lib/python3.12/site-packages/httpx/_client.py new file mode 100644 index 0000000..2249231 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_client.py @@ -0,0 +1,2019 @@ +from __future__ import annotations + +import datetime +import enum +import logging +import time +import typing +import warnings +from contextlib import asynccontextmanager, contextmanager +from types import TracebackType + +from .__version__ import __version__ +from ._auth import Auth, BasicAuth, FunctionAuth +from ._config import ( + DEFAULT_LIMITS, + DEFAULT_MAX_REDIRECTS, + DEFAULT_TIMEOUT_CONFIG, + Limits, + Proxy, + Timeout, +) +from ._decoders import SUPPORTED_DECODERS +from ._exceptions import ( + InvalidURL, + RemoteProtocolError, + TooManyRedirects, + request_context, +) +from ._models import Cookies, Headers, Request, Response +from ._status_codes import codes +from ._transports.base import AsyncBaseTransport, BaseTransport +from ._transports.default import AsyncHTTPTransport, HTTPTransport +from ._types import ( + AsyncByteStream, + AuthTypes, + CertTypes, + CookieTypes, + HeaderTypes, + ProxyTypes, + QueryParamTypes, + RequestContent, + RequestData, + RequestExtensions, + RequestFiles, + SyncByteStream, + TimeoutTypes, +) +from ._urls import URL, QueryParams +from ._utils import URLPattern, get_environment_proxies + +if typing.TYPE_CHECKING: + import ssl # pragma: no cover + +__all__ = ["USE_CLIENT_DEFAULT", "AsyncClient", "Client"] + +# The type annotation for @classmethod and context managers here follows PEP 484 +# https://www.python.org/dev/peps/pep-0484/#annotating-instance-and-class-methods +T = typing.TypeVar("T", bound="Client") +U = typing.TypeVar("U", bound="AsyncClient") + + +def _is_https_redirect(url: URL, location: URL) -> bool: + """ + Return 'True' if 'location' is a HTTPS upgrade of 'url' + """ + if url.host != location.host: + return False + + return ( + url.scheme == "http" + and _port_or_default(url) == 80 + and location.scheme == "https" + and _port_or_default(location) == 443 + ) + + +def _port_or_default(url: URL) -> int | None: + if url.port is not None: + return url.port + return {"http": 80, "https": 443}.get(url.scheme) + + +def _same_origin(url: URL, other: URL) -> bool: + """ + Return 'True' if the given URLs share the same origin. + """ + return ( + url.scheme == other.scheme + and url.host == other.host + and _port_or_default(url) == _port_or_default(other) + ) + + +class UseClientDefault: + """ + For some parameters such as `auth=...` and `timeout=...` we need to be able + to indicate the default "unset" state, in a way that is distinctly different + to using `None`. + + The default "unset" state indicates that whatever default is set on the + client should be used. This is different to setting `None`, which + explicitly disables the parameter, possibly overriding a client default. + + For example we use `timeout=USE_CLIENT_DEFAULT` in the `request()` signature. + Omitting the `timeout` parameter will send a request using whatever default + timeout has been configured on the client. Including `timeout=None` will + ensure no timeout is used. + + Note that user code shouldn't need to use the `USE_CLIENT_DEFAULT` constant, + but it is used internally when a parameter is not included. + """ + + +USE_CLIENT_DEFAULT = UseClientDefault() + + +logger = logging.getLogger("httpx") + +USER_AGENT = f"python-httpx/{__version__}" +ACCEPT_ENCODING = ", ".join( + [key for key in SUPPORTED_DECODERS.keys() if key != "identity"] +) + + +class ClientState(enum.Enum): + # UNOPENED: + # The client has been instantiated, but has not been used to send a request, + # or been opened by entering the context of a `with` block. + UNOPENED = 1 + # OPENED: + # The client has either sent a request, or is within a `with` block. + OPENED = 2 + # CLOSED: + # The client has either exited the `with` block, or `close()` has + # been called explicitly. + CLOSED = 3 + + +class BoundSyncStream(SyncByteStream): + """ + A byte stream that is bound to a given response instance, and that + ensures the `response.elapsed` is set once the response is closed. + """ + + def __init__( + self, stream: SyncByteStream, response: Response, start: float + ) -> None: + self._stream = stream + self._response = response + self._start = start + + def __iter__(self) -> typing.Iterator[bytes]: + for chunk in self._stream: + yield chunk + + def close(self) -> None: + elapsed = time.perf_counter() - self._start + self._response.elapsed = datetime.timedelta(seconds=elapsed) + self._stream.close() + + +class BoundAsyncStream(AsyncByteStream): + """ + An async byte stream that is bound to a given response instance, and that + ensures the `response.elapsed` is set once the response is closed. + """ + + def __init__( + self, stream: AsyncByteStream, response: Response, start: float + ) -> None: + self._stream = stream + self._response = response + self._start = start + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + async for chunk in self._stream: + yield chunk + + async def aclose(self) -> None: + elapsed = time.perf_counter() - self._start + self._response.elapsed = datetime.timedelta(seconds=elapsed) + await self._stream.aclose() + + +EventHook = typing.Callable[..., typing.Any] + + +class BaseClient: + def __init__( + self, + *, + auth: AuthTypes | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + max_redirects: int = DEFAULT_MAX_REDIRECTS, + event_hooks: None | (typing.Mapping[str, list[EventHook]]) = None, + base_url: URL | str = "", + trust_env: bool = True, + default_encoding: str | typing.Callable[[bytes], str] = "utf-8", + ) -> None: + event_hooks = {} if event_hooks is None else event_hooks + + self._base_url = self._enforce_trailing_slash(URL(base_url)) + + self._auth = self._build_auth(auth) + self._params = QueryParams(params) + self.headers = Headers(headers) + self._cookies = Cookies(cookies) + self._timeout = Timeout(timeout) + self.follow_redirects = follow_redirects + self.max_redirects = max_redirects + self._event_hooks = { + "request": list(event_hooks.get("request", [])), + "response": list(event_hooks.get("response", [])), + } + self._trust_env = trust_env + self._default_encoding = default_encoding + self._state = ClientState.UNOPENED + + @property + def is_closed(self) -> bool: + """ + Check if the client being closed + """ + return self._state == ClientState.CLOSED + + @property + def trust_env(self) -> bool: + return self._trust_env + + def _enforce_trailing_slash(self, url: URL) -> URL: + if url.raw_path.endswith(b"/"): + return url + return url.copy_with(raw_path=url.raw_path + b"/") + + def _get_proxy_map( + self, proxy: ProxyTypes | None, allow_env_proxies: bool + ) -> dict[str, Proxy | None]: + if proxy is None: + if allow_env_proxies: + return { + key: None if url is None else Proxy(url=url) + for key, url in get_environment_proxies().items() + } + return {} + else: + proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy + return {"all://": proxy} + + @property + def timeout(self) -> Timeout: + return self._timeout + + @timeout.setter + def timeout(self, timeout: TimeoutTypes) -> None: + self._timeout = Timeout(timeout) + + @property + def event_hooks(self) -> dict[str, list[EventHook]]: + return self._event_hooks + + @event_hooks.setter + def event_hooks(self, event_hooks: dict[str, list[EventHook]]) -> None: + self._event_hooks = { + "request": list(event_hooks.get("request", [])), + "response": list(event_hooks.get("response", [])), + } + + @property + def auth(self) -> Auth | None: + """ + Authentication class used when none is passed at the request-level. + + See also [Authentication][0]. + + [0]: /quickstart/#authentication + """ + return self._auth + + @auth.setter + def auth(self, auth: AuthTypes) -> None: + self._auth = self._build_auth(auth) + + @property + def base_url(self) -> URL: + """ + Base URL to use when sending requests with relative URLs. + """ + return self._base_url + + @base_url.setter + def base_url(self, url: URL | str) -> None: + self._base_url = self._enforce_trailing_slash(URL(url)) + + @property + def headers(self) -> Headers: + """ + HTTP headers to include when sending requests. + """ + return self._headers + + @headers.setter + def headers(self, headers: HeaderTypes) -> None: + client_headers = Headers( + { + b"Accept": b"*/*", + b"Accept-Encoding": ACCEPT_ENCODING.encode("ascii"), + b"Connection": b"keep-alive", + b"User-Agent": USER_AGENT.encode("ascii"), + } + ) + client_headers.update(headers) + self._headers = client_headers + + @property + def cookies(self) -> Cookies: + """ + Cookie values to include when sending requests. + """ + return self._cookies + + @cookies.setter + def cookies(self, cookies: CookieTypes) -> None: + self._cookies = Cookies(cookies) + + @property + def params(self) -> QueryParams: + """ + Query parameters to include in the URL when sending requests. + """ + return self._params + + @params.setter + def params(self, params: QueryParamTypes) -> None: + self._params = QueryParams(params) + + def build_request( + self, + method: str, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Request: + """ + Build and return a request instance. + + * The `params`, `headers` and `cookies` arguments + are merged with any values set on the client. + * The `url` argument is merged with any `base_url` set on the client. + + See also: [Request instances][0] + + [0]: /advanced/clients/#request-instances + """ + url = self._merge_url(url) + headers = self._merge_headers(headers) + cookies = self._merge_cookies(cookies) + params = self._merge_queryparams(params) + extensions = {} if extensions is None else extensions + if "timeout" not in extensions: + timeout = ( + self.timeout + if isinstance(timeout, UseClientDefault) + else Timeout(timeout) + ) + extensions = dict(**extensions, timeout=timeout.as_dict()) + return Request( + method, + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + extensions=extensions, + ) + + def _merge_url(self, url: URL | str) -> URL: + """ + Merge a URL argument together with any 'base_url' on the client, + to create the URL used for the outgoing request. + """ + merge_url = URL(url) + if merge_url.is_relative_url: + # To merge URLs we always append to the base URL. To get this + # behaviour correct we always ensure the base URL ends in a '/' + # separator, and strip any leading '/' from the merge URL. + # + # So, eg... + # + # >>> client = Client(base_url="https://www.example.com/subpath") + # >>> client.base_url + # URL('https://www.example.com/subpath/') + # >>> client.build_request("GET", "/path").url + # URL('https://www.example.com/subpath/path') + merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/") + return self.base_url.copy_with(raw_path=merge_raw_path) + return merge_url + + def _merge_cookies(self, cookies: CookieTypes | None = None) -> CookieTypes | None: + """ + Merge a cookies argument together with any cookies on the client, + to create the cookies used for the outgoing request. + """ + if cookies or self.cookies: + merged_cookies = Cookies(self.cookies) + merged_cookies.update(cookies) + return merged_cookies + return cookies + + def _merge_headers(self, headers: HeaderTypes | None = None) -> HeaderTypes | None: + """ + Merge a headers argument together with any headers on the client, + to create the headers used for the outgoing request. + """ + merged_headers = Headers(self.headers) + merged_headers.update(headers) + return merged_headers + + def _merge_queryparams( + self, params: QueryParamTypes | None = None + ) -> QueryParamTypes | None: + """ + Merge a queryparams argument together with any queryparams on the client, + to create the queryparams used for the outgoing request. + """ + if params or self.params: + merged_queryparams = QueryParams(self.params) + return merged_queryparams.merge(params) + return params + + def _build_auth(self, auth: AuthTypes | None) -> Auth | None: + if auth is None: + return None + elif isinstance(auth, tuple): + return BasicAuth(username=auth[0], password=auth[1]) + elif isinstance(auth, Auth): + return auth + elif callable(auth): + return FunctionAuth(func=auth) + else: + raise TypeError(f'Invalid "auth" argument: {auth!r}') + + def _build_request_auth( + self, + request: Request, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + ) -> Auth: + auth = ( + self._auth if isinstance(auth, UseClientDefault) else self._build_auth(auth) + ) + + if auth is not None: + return auth + + username, password = request.url.username, request.url.password + if username or password: + return BasicAuth(username=username, password=password) + + return Auth() + + def _build_redirect_request(self, request: Request, response: Response) -> Request: + """ + Given a request and a redirect response, return a new request that + should be used to effect the redirect. + """ + method = self._redirect_method(request, response) + url = self._redirect_url(request, response) + headers = self._redirect_headers(request, url, method) + stream = self._redirect_stream(request, method) + cookies = Cookies(self.cookies) + return Request( + method=method, + url=url, + headers=headers, + cookies=cookies, + stream=stream, + extensions=request.extensions, + ) + + def _redirect_method(self, request: Request, response: Response) -> str: + """ + When being redirected we may want to change the method of the request + based on certain specs or browser behavior. + """ + method = request.method + + # https://tools.ietf.org/html/rfc7231#section-6.4.4 + if response.status_code == codes.SEE_OTHER and method != "HEAD": + method = "GET" + + # Do what the browsers do, despite standards... + # Turn 302s into GETs. + if response.status_code == codes.FOUND and method != "HEAD": + method = "GET" + + # If a POST is responded to with a 301, turn it into a GET. + # This bizarre behaviour is explained in 'requests' issue 1704. + if response.status_code == codes.MOVED_PERMANENTLY and method == "POST": + method = "GET" + + return method + + def _redirect_url(self, request: Request, response: Response) -> URL: + """ + Return the URL for the redirect to follow. + """ + location = response.headers["Location"] + + try: + url = URL(location) + except InvalidURL as exc: + raise RemoteProtocolError( + f"Invalid URL in location header: {exc}.", request=request + ) from None + + # Handle malformed 'Location' headers that are "absolute" form, have no host. + # See: https://github.com/encode/httpx/issues/771 + if url.scheme and not url.host: + url = url.copy_with(host=request.url.host) + + # Facilitate relative 'Location' headers, as allowed by RFC 7231. + # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') + if url.is_relative_url: + url = request.url.join(url) + + # Attach previous fragment if needed (RFC 7231 7.1.2) + if request.url.fragment and not url.fragment: + url = url.copy_with(fragment=request.url.fragment) + + return url + + def _redirect_headers(self, request: Request, url: URL, method: str) -> Headers: + """ + Return the headers that should be used for the redirect request. + """ + headers = Headers(request.headers) + + if not _same_origin(url, request.url): + if not _is_https_redirect(request.url, url): + # Strip Authorization headers when responses are redirected + # away from the origin. (Except for direct HTTP to HTTPS redirects.) + headers.pop("Authorization", None) + + # Update the Host header. + headers["Host"] = url.netloc.decode("ascii") + + if method != request.method and method == "GET": + # If we've switch to a 'GET' request, then strip any headers which + # are only relevant to the request body. + headers.pop("Content-Length", None) + headers.pop("Transfer-Encoding", None) + + # We should use the client cookie store to determine any cookie header, + # rather than whatever was on the original outgoing request. + headers.pop("Cookie", None) + + return headers + + def _redirect_stream( + self, request: Request, method: str + ) -> SyncByteStream | AsyncByteStream | None: + """ + Return the body that should be used for the redirect request. + """ + if method != request.method and method == "GET": + return None + + return request.stream + + def _set_timeout(self, request: Request) -> None: + if "timeout" not in request.extensions: + timeout = ( + self.timeout + if isinstance(self.timeout, UseClientDefault) + else Timeout(self.timeout) + ) + request.extensions = dict(**request.extensions, timeout=timeout.as_dict()) + + +class Client(BaseClient): + """ + An HTTP client, with connection pooling, HTTP/2, redirects, cookie persistence, etc. + + It can be shared between threads. + + Usage: + + ```python + >>> client = httpx.Client() + >>> response = client.get('https://example.org') + ``` + + **Parameters:** + + * **auth** - *(optional)* An authentication class to use when sending + requests. + * **params** - *(optional)* Query parameters to include in request URLs, as + a string, dictionary, or sequence of two-tuples. + * **headers** - *(optional)* Dictionary of HTTP headers to include when + sending requests. + * **cookies** - *(optional)* Dictionary of Cookie items to include when + sending requests. + * **verify** - *(optional)* Either `True` to use an SSL context with the + default CA bundle, `False` to disable verification, or an instance of + `ssl.SSLContext` to use a custom context. + * **http2** - *(optional)* A boolean indicating if HTTP/2 support should be + enabled. Defaults to `False`. + * **proxy** - *(optional)* A proxy URL where all the traffic should be routed. + * **timeout** - *(optional)* The timeout configuration to use when sending + requests. + * **limits** - *(optional)* The limits configuration to use. + * **max_redirects** - *(optional)* The maximum number of redirect responses + that should be followed. + * **base_url** - *(optional)* A URL to use as the base when building + request URLs. + * **transport** - *(optional)* A transport class to use for sending requests + over the network. + * **trust_env** - *(optional)* Enables or disables usage of environment + variables for configuration. + * **default_encoding** - *(optional)* The default encoding to use for decoding + response text, if no charset information is included in a response Content-Type + header. Set to a callable for automatic character set detection. Default: "utf-8". + """ + + def __init__( + self, + *, + auth: AuthTypes | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + proxy: ProxyTypes | None = None, + mounts: None | (typing.Mapping[str, BaseTransport | None]) = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + limits: Limits = DEFAULT_LIMITS, + max_redirects: int = DEFAULT_MAX_REDIRECTS, + event_hooks: None | (typing.Mapping[str, list[EventHook]]) = None, + base_url: URL | str = "", + transport: BaseTransport | None = None, + default_encoding: str | typing.Callable[[bytes], str] = "utf-8", + ) -> None: + super().__init__( + auth=auth, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + follow_redirects=follow_redirects, + max_redirects=max_redirects, + event_hooks=event_hooks, + base_url=base_url, + trust_env=trust_env, + default_encoding=default_encoding, + ) + + if http2: + try: + import h2 # noqa + except ImportError: # pragma: no cover + raise ImportError( + "Using http2=True, but the 'h2' package is not installed. " + "Make sure to install httpx using `pip install httpx[http2]`." + ) from None + + allow_env_proxies = trust_env and transport is None + proxy_map = self._get_proxy_map(proxy, allow_env_proxies) + + self._transport = self._init_transport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + transport=transport, + ) + self._mounts: dict[URLPattern, BaseTransport | None] = { + URLPattern(key): None + if proxy is None + else self._init_proxy_transport( + proxy, + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + ) + for key, proxy in proxy_map.items() + } + if mounts is not None: + self._mounts.update( + {URLPattern(key): transport for key, transport in mounts.items()} + ) + + self._mounts = dict(sorted(self._mounts.items())) + + def _init_transport( + self, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + transport: BaseTransport | None = None, + ) -> BaseTransport: + if transport is not None: + return transport + + return HTTPTransport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + ) + + def _init_proxy_transport( + self, + proxy: Proxy, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + ) -> BaseTransport: + return HTTPTransport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + proxy=proxy, + ) + + def _transport_for_url(self, url: URL) -> BaseTransport: + """ + Returns the transport instance that should be used for a given URL. + This will either be the standard connection pool, or a proxy. + """ + for pattern, transport in self._mounts.items(): + if pattern.matches(url): + return self._transport if transport is None else transport + + return self._transport + + def request( + self, + method: str, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Build and send a request. + + Equivalent to: + + ```python + request = client.build_request(...) + response = client.send(request, ...) + ``` + + See `Client.build_request()`, `Client.send()` and + [Merging of configuration][0] for how the various parameters + are merged with client-level configuration. + + [0]: /advanced/clients/#merging-of-configuration + """ + if cookies is not None: + message = ( + "Setting per-request cookies=<...> is being deprecated, because " + "the expected behaviour on cookie persistence is ambiguous. Set " + "cookies directly on the client instance instead." + ) + warnings.warn(message, DeprecationWarning, stacklevel=2) + + request = self.build_request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + extensions=extensions, + ) + return self.send(request, auth=auth, follow_redirects=follow_redirects) + + @contextmanager + def stream( + self, + method: str, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> typing.Iterator[Response]: + """ + Alternative to `httpx.request()` that streams the response body + instead of loading it into memory at once. + + **Parameters**: See `httpx.request`. + + See also: [Streaming Responses][0] + + [0]: /quickstart#streaming-responses + """ + request = self.build_request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + extensions=extensions, + ) + response = self.send( + request=request, + auth=auth, + follow_redirects=follow_redirects, + stream=True, + ) + try: + yield response + finally: + response.close() + + def send( + self, + request: Request, + *, + stream: bool = False, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + ) -> Response: + """ + Send a request. + + The request is sent as-is, unmodified. + + Typically you'll want to build one with `Client.build_request()` + so that any client-level configuration is merged into the request, + but passing an explicit `httpx.Request()` is supported as well. + + See also: [Request instances][0] + + [0]: /advanced/clients/#request-instances + """ + if self._state == ClientState.CLOSED: + raise RuntimeError("Cannot send a request, as the client has been closed.") + + self._state = ClientState.OPENED + follow_redirects = ( + self.follow_redirects + if isinstance(follow_redirects, UseClientDefault) + else follow_redirects + ) + + self._set_timeout(request) + + auth = self._build_request_auth(request, auth) + + response = self._send_handling_auth( + request, + auth=auth, + follow_redirects=follow_redirects, + history=[], + ) + try: + if not stream: + response.read() + + return response + + except BaseException as exc: + response.close() + raise exc + + def _send_handling_auth( + self, + request: Request, + auth: Auth, + follow_redirects: bool, + history: list[Response], + ) -> Response: + auth_flow = auth.sync_auth_flow(request) + try: + request = next(auth_flow) + + while True: + response = self._send_handling_redirects( + request, + follow_redirects=follow_redirects, + history=history, + ) + try: + try: + next_request = auth_flow.send(response) + except StopIteration: + return response + + response.history = list(history) + response.read() + request = next_request + history.append(response) + + except BaseException as exc: + response.close() + raise exc + finally: + auth_flow.close() + + def _send_handling_redirects( + self, + request: Request, + follow_redirects: bool, + history: list[Response], + ) -> Response: + while True: + if len(history) > self.max_redirects: + raise TooManyRedirects( + "Exceeded maximum allowed redirects.", request=request + ) + + for hook in self._event_hooks["request"]: + hook(request) + + response = self._send_single_request(request) + try: + for hook in self._event_hooks["response"]: + hook(response) + response.history = list(history) + + if not response.has_redirect_location: + return response + + request = self._build_redirect_request(request, response) + history = history + [response] + + if follow_redirects: + response.read() + else: + response.next_request = request + return response + + except BaseException as exc: + response.close() + raise exc + + def _send_single_request(self, request: Request) -> Response: + """ + Sends a single request, without handling any redirections. + """ + transport = self._transport_for_url(request.url) + start = time.perf_counter() + + if not isinstance(request.stream, SyncByteStream): + raise RuntimeError( + "Attempted to send an async request with a sync Client instance." + ) + + with request_context(request=request): + response = transport.handle_request(request) + + assert isinstance(response.stream, SyncByteStream) + + response.request = request + response.stream = BoundSyncStream( + response.stream, response=response, start=start + ) + self.cookies.extract_cookies(response) + response.default_encoding = self._default_encoding + + logger.info( + 'HTTP Request: %s %s "%s %d %s"', + request.method, + request.url, + response.http_version, + response.status_code, + response.reason_phrase, + ) + + return response + + def get( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `GET` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "GET", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def options( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send an `OPTIONS` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "OPTIONS", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def head( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `HEAD` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "HEAD", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def post( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `POST` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "POST", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def put( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `PUT` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "PUT", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def patch( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `PATCH` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "PATCH", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def delete( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `DELETE` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "DELETE", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def close(self) -> None: + """ + Close transport and proxies. + """ + if self._state != ClientState.CLOSED: + self._state = ClientState.CLOSED + + self._transport.close() + for transport in self._mounts.values(): + if transport is not None: + transport.close() + + def __enter__(self: T) -> T: + if self._state != ClientState.UNOPENED: + msg = { + ClientState.OPENED: "Cannot open a client instance more than once.", + ClientState.CLOSED: ( + "Cannot reopen a client instance, once it has been closed." + ), + }[self._state] + raise RuntimeError(msg) + + self._state = ClientState.OPENED + + self._transport.__enter__() + for transport in self._mounts.values(): + if transport is not None: + transport.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + self._state = ClientState.CLOSED + + self._transport.__exit__(exc_type, exc_value, traceback) + for transport in self._mounts.values(): + if transport is not None: + transport.__exit__(exc_type, exc_value, traceback) + + +class AsyncClient(BaseClient): + """ + An asynchronous HTTP client, with connection pooling, HTTP/2, redirects, + cookie persistence, etc. + + It can be shared between tasks. + + Usage: + + ```python + >>> async with httpx.AsyncClient() as client: + >>> response = await client.get('https://example.org') + ``` + + **Parameters:** + + * **auth** - *(optional)* An authentication class to use when sending + requests. + * **params** - *(optional)* Query parameters to include in request URLs, as + a string, dictionary, or sequence of two-tuples. + * **headers** - *(optional)* Dictionary of HTTP headers to include when + sending requests. + * **cookies** - *(optional)* Dictionary of Cookie items to include when + sending requests. + * **verify** - *(optional)* Either `True` to use an SSL context with the + default CA bundle, `False` to disable verification, or an instance of + `ssl.SSLContext` to use a custom context. + * **http2** - *(optional)* A boolean indicating if HTTP/2 support should be + enabled. Defaults to `False`. + * **proxy** - *(optional)* A proxy URL where all the traffic should be routed. + * **timeout** - *(optional)* The timeout configuration to use when sending + requests. + * **limits** - *(optional)* The limits configuration to use. + * **max_redirects** - *(optional)* The maximum number of redirect responses + that should be followed. + * **base_url** - *(optional)* A URL to use as the base when building + request URLs. + * **transport** - *(optional)* A transport class to use for sending requests + over the network. + * **trust_env** - *(optional)* Enables or disables usage of environment + variables for configuration. + * **default_encoding** - *(optional)* The default encoding to use for decoding + response text, if no charset information is included in a response Content-Type + header. Set to a callable for automatic character set detection. Default: "utf-8". + """ + + def __init__( + self, + *, + auth: AuthTypes | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + http1: bool = True, + http2: bool = False, + proxy: ProxyTypes | None = None, + mounts: None | (typing.Mapping[str, AsyncBaseTransport | None]) = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + limits: Limits = DEFAULT_LIMITS, + max_redirects: int = DEFAULT_MAX_REDIRECTS, + event_hooks: None | (typing.Mapping[str, list[EventHook]]) = None, + base_url: URL | str = "", + transport: AsyncBaseTransport | None = None, + trust_env: bool = True, + default_encoding: str | typing.Callable[[bytes], str] = "utf-8", + ) -> None: + super().__init__( + auth=auth, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + follow_redirects=follow_redirects, + max_redirects=max_redirects, + event_hooks=event_hooks, + base_url=base_url, + trust_env=trust_env, + default_encoding=default_encoding, + ) + + if http2: + try: + import h2 # noqa + except ImportError: # pragma: no cover + raise ImportError( + "Using http2=True, but the 'h2' package is not installed. " + "Make sure to install httpx using `pip install httpx[http2]`." + ) from None + + allow_env_proxies = trust_env and transport is None + proxy_map = self._get_proxy_map(proxy, allow_env_proxies) + + self._transport = self._init_transport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + transport=transport, + ) + + self._mounts: dict[URLPattern, AsyncBaseTransport | None] = { + URLPattern(key): None + if proxy is None + else self._init_proxy_transport( + proxy, + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + ) + for key, proxy in proxy_map.items() + } + if mounts is not None: + self._mounts.update( + {URLPattern(key): transport for key, transport in mounts.items()} + ) + self._mounts = dict(sorted(self._mounts.items())) + + def _init_transport( + self, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + transport: AsyncBaseTransport | None = None, + ) -> AsyncBaseTransport: + if transport is not None: + return transport + + return AsyncHTTPTransport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + ) + + def _init_proxy_transport( + self, + proxy: Proxy, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + ) -> AsyncBaseTransport: + return AsyncHTTPTransport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + proxy=proxy, + ) + + def _transport_for_url(self, url: URL) -> AsyncBaseTransport: + """ + Returns the transport instance that should be used for a given URL. + This will either be the standard connection pool, or a proxy. + """ + for pattern, transport in self._mounts.items(): + if pattern.matches(url): + return self._transport if transport is None else transport + + return self._transport + + async def request( + self, + method: str, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Build and send a request. + + Equivalent to: + + ```python + request = client.build_request(...) + response = await client.send(request, ...) + ``` + + See `AsyncClient.build_request()`, `AsyncClient.send()` + and [Merging of configuration][0] for how the various parameters + are merged with client-level configuration. + + [0]: /advanced/clients/#merging-of-configuration + """ + + if cookies is not None: # pragma: no cover + message = ( + "Setting per-request cookies=<...> is being deprecated, because " + "the expected behaviour on cookie persistence is ambiguous. Set " + "cookies directly on the client instance instead." + ) + warnings.warn(message, DeprecationWarning, stacklevel=2) + + request = self.build_request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + extensions=extensions, + ) + return await self.send(request, auth=auth, follow_redirects=follow_redirects) + + @asynccontextmanager + async def stream( + self, + method: str, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> typing.AsyncIterator[Response]: + """ + Alternative to `httpx.request()` that streams the response body + instead of loading it into memory at once. + + **Parameters**: See `httpx.request`. + + See also: [Streaming Responses][0] + + [0]: /quickstart#streaming-responses + """ + request = self.build_request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + extensions=extensions, + ) + response = await self.send( + request=request, + auth=auth, + follow_redirects=follow_redirects, + stream=True, + ) + try: + yield response + finally: + await response.aclose() + + async def send( + self, + request: Request, + *, + stream: bool = False, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + ) -> Response: + """ + Send a request. + + The request is sent as-is, unmodified. + + Typically you'll want to build one with `AsyncClient.build_request()` + so that any client-level configuration is merged into the request, + but passing an explicit `httpx.Request()` is supported as well. + + See also: [Request instances][0] + + [0]: /advanced/clients/#request-instances + """ + if self._state == ClientState.CLOSED: + raise RuntimeError("Cannot send a request, as the client has been closed.") + + self._state = ClientState.OPENED + follow_redirects = ( + self.follow_redirects + if isinstance(follow_redirects, UseClientDefault) + else follow_redirects + ) + + self._set_timeout(request) + + auth = self._build_request_auth(request, auth) + + response = await self._send_handling_auth( + request, + auth=auth, + follow_redirects=follow_redirects, + history=[], + ) + try: + if not stream: + await response.aread() + + return response + + except BaseException as exc: + await response.aclose() + raise exc + + async def _send_handling_auth( + self, + request: Request, + auth: Auth, + follow_redirects: bool, + history: list[Response], + ) -> Response: + auth_flow = auth.async_auth_flow(request) + try: + request = await auth_flow.__anext__() + + while True: + response = await self._send_handling_redirects( + request, + follow_redirects=follow_redirects, + history=history, + ) + try: + try: + next_request = await auth_flow.asend(response) + except StopAsyncIteration: + return response + + response.history = list(history) + await response.aread() + request = next_request + history.append(response) + + except BaseException as exc: + await response.aclose() + raise exc + finally: + await auth_flow.aclose() + + async def _send_handling_redirects( + self, + request: Request, + follow_redirects: bool, + history: list[Response], + ) -> Response: + while True: + if len(history) > self.max_redirects: + raise TooManyRedirects( + "Exceeded maximum allowed redirects.", request=request + ) + + for hook in self._event_hooks["request"]: + await hook(request) + + response = await self._send_single_request(request) + try: + for hook in self._event_hooks["response"]: + await hook(response) + + response.history = list(history) + + if not response.has_redirect_location: + return response + + request = self._build_redirect_request(request, response) + history = history + [response] + + if follow_redirects: + await response.aread() + else: + response.next_request = request + return response + + except BaseException as exc: + await response.aclose() + raise exc + + async def _send_single_request(self, request: Request) -> Response: + """ + Sends a single request, without handling any redirections. + """ + transport = self._transport_for_url(request.url) + start = time.perf_counter() + + if not isinstance(request.stream, AsyncByteStream): + raise RuntimeError( + "Attempted to send an sync request with an AsyncClient instance." + ) + + with request_context(request=request): + response = await transport.handle_async_request(request) + + assert isinstance(response.stream, AsyncByteStream) + response.request = request + response.stream = BoundAsyncStream( + response.stream, response=response, start=start + ) + self.cookies.extract_cookies(response) + response.default_encoding = self._default_encoding + + logger.info( + 'HTTP Request: %s %s "%s %d %s"', + request.method, + request.url, + response.http_version, + response.status_code, + response.reason_phrase, + ) + + return response + + async def get( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `GET` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "GET", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def options( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send an `OPTIONS` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "OPTIONS", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def head( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `HEAD` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "HEAD", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def post( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `POST` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "POST", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def put( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `PUT` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "PUT", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def patch( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `PATCH` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "PATCH", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def delete( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `DELETE` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "DELETE", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def aclose(self) -> None: + """ + Close transport and proxies. + """ + if self._state != ClientState.CLOSED: + self._state = ClientState.CLOSED + + await self._transport.aclose() + for proxy in self._mounts.values(): + if proxy is not None: + await proxy.aclose() + + async def __aenter__(self: U) -> U: + if self._state != ClientState.UNOPENED: + msg = { + ClientState.OPENED: "Cannot open a client instance more than once.", + ClientState.CLOSED: ( + "Cannot reopen a client instance, once it has been closed." + ), + }[self._state] + raise RuntimeError(msg) + + self._state = ClientState.OPENED + + await self._transport.__aenter__() + for proxy in self._mounts.values(): + if proxy is not None: + await proxy.__aenter__() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + self._state = ClientState.CLOSED + + await self._transport.__aexit__(exc_type, exc_value, traceback) + for proxy in self._mounts.values(): + if proxy is not None: + await proxy.__aexit__(exc_type, exc_value, traceback) diff --git a/venv/lib/python3.12/site-packages/httpx/_config.py b/venv/lib/python3.12/site-packages/httpx/_config.py new file mode 100644 index 0000000..467a6c9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_config.py @@ -0,0 +1,248 @@ +from __future__ import annotations + +import os +import typing + +from ._models import Headers +from ._types import CertTypes, HeaderTypes, TimeoutTypes +from ._urls import URL + +if typing.TYPE_CHECKING: + import ssl # pragma: no cover + +__all__ = ["Limits", "Proxy", "Timeout", "create_ssl_context"] + + +class UnsetType: + pass # pragma: no cover + + +UNSET = UnsetType() + + +def create_ssl_context( + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, +) -> ssl.SSLContext: + import ssl + import warnings + + import certifi + + if verify is True: + if trust_env and os.environ.get("SSL_CERT_FILE"): # pragma: nocover + ctx = ssl.create_default_context(cafile=os.environ["SSL_CERT_FILE"]) + elif trust_env and os.environ.get("SSL_CERT_DIR"): # pragma: nocover + ctx = ssl.create_default_context(capath=os.environ["SSL_CERT_DIR"]) + else: + # Default case... + ctx = ssl.create_default_context(cafile=certifi.where()) + elif verify is False: + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + elif isinstance(verify, str): # pragma: nocover + message = ( + "`verify=` is deprecated. " + "Use `verify=ssl.create_default_context(cafile=...)` " + "or `verify=ssl.create_default_context(capath=...)` instead." + ) + warnings.warn(message, DeprecationWarning) + if os.path.isdir(verify): + return ssl.create_default_context(capath=verify) + return ssl.create_default_context(cafile=verify) + else: + ctx = verify + + if cert: # pragma: nocover + message = ( + "`cert=...` is deprecated. Use `verify=` instead," + "with `.load_cert_chain()` to configure the certificate chain." + ) + warnings.warn(message, DeprecationWarning) + if isinstance(cert, str): + ctx.load_cert_chain(cert) + else: + ctx.load_cert_chain(*cert) + + return ctx + + +class Timeout: + """ + Timeout configuration. + + **Usage**: + + Timeout(None) # No timeouts. + Timeout(5.0) # 5s timeout on all operations. + Timeout(None, connect=5.0) # 5s timeout on connect, no other timeouts. + Timeout(5.0, connect=10.0) # 10s timeout on connect. 5s timeout elsewhere. + Timeout(5.0, pool=None) # No timeout on acquiring connection from pool. + # 5s timeout elsewhere. + """ + + def __init__( + self, + timeout: TimeoutTypes | UnsetType = UNSET, + *, + connect: None | float | UnsetType = UNSET, + read: None | float | UnsetType = UNSET, + write: None | float | UnsetType = UNSET, + pool: None | float | UnsetType = UNSET, + ) -> None: + if isinstance(timeout, Timeout): + # Passed as a single explicit Timeout. + assert connect is UNSET + assert read is UNSET + assert write is UNSET + assert pool is UNSET + self.connect = timeout.connect # type: typing.Optional[float] + self.read = timeout.read # type: typing.Optional[float] + self.write = timeout.write # type: typing.Optional[float] + self.pool = timeout.pool # type: typing.Optional[float] + elif isinstance(timeout, tuple): + # Passed as a tuple. + self.connect = timeout[0] + self.read = timeout[1] + self.write = None if len(timeout) < 3 else timeout[2] + self.pool = None if len(timeout) < 4 else timeout[3] + elif not ( + isinstance(connect, UnsetType) + or isinstance(read, UnsetType) + or isinstance(write, UnsetType) + or isinstance(pool, UnsetType) + ): + self.connect = connect + self.read = read + self.write = write + self.pool = pool + else: + if isinstance(timeout, UnsetType): + raise ValueError( + "httpx.Timeout must either include a default, or set all " + "four parameters explicitly." + ) + self.connect = timeout if isinstance(connect, UnsetType) else connect + self.read = timeout if isinstance(read, UnsetType) else read + self.write = timeout if isinstance(write, UnsetType) else write + self.pool = timeout if isinstance(pool, UnsetType) else pool + + def as_dict(self) -> dict[str, float | None]: + return { + "connect": self.connect, + "read": self.read, + "write": self.write, + "pool": self.pool, + } + + def __eq__(self, other: typing.Any) -> bool: + return ( + isinstance(other, self.__class__) + and self.connect == other.connect + and self.read == other.read + and self.write == other.write + and self.pool == other.pool + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + if len({self.connect, self.read, self.write, self.pool}) == 1: + return f"{class_name}(timeout={self.connect})" + return ( + f"{class_name}(connect={self.connect}, " + f"read={self.read}, write={self.write}, pool={self.pool})" + ) + + +class Limits: + """ + Configuration for limits to various client behaviors. + + **Parameters:** + + * **max_connections** - The maximum number of concurrent connections that may be + established. + * **max_keepalive_connections** - Allow the connection pool to maintain + keep-alive connections below this point. Should be less than or equal + to `max_connections`. + * **keepalive_expiry** - Time limit on idle keep-alive connections in seconds. + """ + + def __init__( + self, + *, + max_connections: int | None = None, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = 5.0, + ) -> None: + self.max_connections = max_connections + self.max_keepalive_connections = max_keepalive_connections + self.keepalive_expiry = keepalive_expiry + + def __eq__(self, other: typing.Any) -> bool: + return ( + isinstance(other, self.__class__) + and self.max_connections == other.max_connections + and self.max_keepalive_connections == other.max_keepalive_connections + and self.keepalive_expiry == other.keepalive_expiry + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + return ( + f"{class_name}(max_connections={self.max_connections}, " + f"max_keepalive_connections={self.max_keepalive_connections}, " + f"keepalive_expiry={self.keepalive_expiry})" + ) + + +class Proxy: + def __init__( + self, + url: URL | str, + *, + ssl_context: ssl.SSLContext | None = None, + auth: tuple[str, str] | None = None, + headers: HeaderTypes | None = None, + ) -> None: + url = URL(url) + headers = Headers(headers) + + if url.scheme not in ("http", "https", "socks5", "socks5h"): + raise ValueError(f"Unknown scheme for proxy URL {url!r}") + + if url.username or url.password: + # Remove any auth credentials from the URL. + auth = (url.username, url.password) + url = url.copy_with(username=None, password=None) + + self.url = url + self.auth = auth + self.headers = headers + self.ssl_context = ssl_context + + @property + def raw_auth(self) -> tuple[bytes, bytes] | None: + # The proxy authentication as raw bytes. + return ( + None + if self.auth is None + else (self.auth[0].encode("utf-8"), self.auth[1].encode("utf-8")) + ) + + def __repr__(self) -> str: + # The authentication is represented with the password component masked. + auth = (self.auth[0], "********") if self.auth else None + + # Build a nice concise representation. + url_str = f"{str(self.url)!r}" + auth_str = f", auth={auth!r}" if auth else "" + headers_str = f", headers={dict(self.headers)!r}" if self.headers else "" + return f"Proxy({url_str}{auth_str}{headers_str})" + + +DEFAULT_TIMEOUT_CONFIG = Timeout(timeout=5.0) +DEFAULT_LIMITS = Limits(max_connections=100, max_keepalive_connections=20) +DEFAULT_MAX_REDIRECTS = 20 diff --git a/venv/lib/python3.12/site-packages/httpx/_content.py b/venv/lib/python3.12/site-packages/httpx/_content.py new file mode 100644 index 0000000..6f479a0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_content.py @@ -0,0 +1,240 @@ +from __future__ import annotations + +import inspect +import warnings +from json import dumps as json_dumps +from typing import ( + Any, + AsyncIterable, + AsyncIterator, + Iterable, + Iterator, + Mapping, +) +from urllib.parse import urlencode + +from ._exceptions import StreamClosed, StreamConsumed +from ._multipart import MultipartStream +from ._types import ( + AsyncByteStream, + RequestContent, + RequestData, + RequestFiles, + ResponseContent, + SyncByteStream, +) +from ._utils import peek_filelike_length, primitive_value_to_str + +__all__ = ["ByteStream"] + + +class ByteStream(AsyncByteStream, SyncByteStream): + def __init__(self, stream: bytes) -> None: + self._stream = stream + + def __iter__(self) -> Iterator[bytes]: + yield self._stream + + async def __aiter__(self) -> AsyncIterator[bytes]: + yield self._stream + + +class IteratorByteStream(SyncByteStream): + CHUNK_SIZE = 65_536 + + def __init__(self, stream: Iterable[bytes]) -> None: + self._stream = stream + self._is_stream_consumed = False + self._is_generator = inspect.isgenerator(stream) + + def __iter__(self) -> Iterator[bytes]: + if self._is_stream_consumed and self._is_generator: + raise StreamConsumed() + + self._is_stream_consumed = True + if hasattr(self._stream, "read"): + # File-like interfaces should use 'read' directly. + chunk = self._stream.read(self.CHUNK_SIZE) + while chunk: + yield chunk + chunk = self._stream.read(self.CHUNK_SIZE) + else: + # Otherwise iterate. + for part in self._stream: + yield part + + +class AsyncIteratorByteStream(AsyncByteStream): + CHUNK_SIZE = 65_536 + + def __init__(self, stream: AsyncIterable[bytes]) -> None: + self._stream = stream + self._is_stream_consumed = False + self._is_generator = inspect.isasyncgen(stream) + + async def __aiter__(self) -> AsyncIterator[bytes]: + if self._is_stream_consumed and self._is_generator: + raise StreamConsumed() + + self._is_stream_consumed = True + if hasattr(self._stream, "aread"): + # File-like interfaces should use 'aread' directly. + chunk = await self._stream.aread(self.CHUNK_SIZE) + while chunk: + yield chunk + chunk = await self._stream.aread(self.CHUNK_SIZE) + else: + # Otherwise iterate. + async for part in self._stream: + yield part + + +class UnattachedStream(AsyncByteStream, SyncByteStream): + """ + If a request or response is serialized using pickle, then it is no longer + attached to a stream for I/O purposes. Any stream operations should result + in `httpx.StreamClosed`. + """ + + def __iter__(self) -> Iterator[bytes]: + raise StreamClosed() + + async def __aiter__(self) -> AsyncIterator[bytes]: + raise StreamClosed() + yield b"" # pragma: no cover + + +def encode_content( + content: str | bytes | Iterable[bytes] | AsyncIterable[bytes], +) -> tuple[dict[str, str], SyncByteStream | AsyncByteStream]: + if isinstance(content, (bytes, str)): + body = content.encode("utf-8") if isinstance(content, str) else content + content_length = len(body) + headers = {"Content-Length": str(content_length)} if body else {} + return headers, ByteStream(body) + + elif isinstance(content, Iterable) and not isinstance(content, dict): + # `not isinstance(content, dict)` is a bit oddly specific, but it + # catches a case that's easy for users to make in error, and would + # otherwise pass through here, like any other bytes-iterable, + # because `dict` happens to be iterable. See issue #2491. + content_length_or_none = peek_filelike_length(content) + + if content_length_or_none is None: + headers = {"Transfer-Encoding": "chunked"} + else: + headers = {"Content-Length": str(content_length_or_none)} + return headers, IteratorByteStream(content) # type: ignore + + elif isinstance(content, AsyncIterable): + headers = {"Transfer-Encoding": "chunked"} + return headers, AsyncIteratorByteStream(content) + + raise TypeError(f"Unexpected type for 'content', {type(content)!r}") + + +def encode_urlencoded_data( + data: RequestData, +) -> tuple[dict[str, str], ByteStream]: + plain_data = [] + for key, value in data.items(): + if isinstance(value, (list, tuple)): + plain_data.extend([(key, primitive_value_to_str(item)) for item in value]) + else: + plain_data.append((key, primitive_value_to_str(value))) + body = urlencode(plain_data, doseq=True).encode("utf-8") + content_length = str(len(body)) + content_type = "application/x-www-form-urlencoded" + headers = {"Content-Length": content_length, "Content-Type": content_type} + return headers, ByteStream(body) + + +def encode_multipart_data( + data: RequestData, files: RequestFiles, boundary: bytes | None +) -> tuple[dict[str, str], MultipartStream]: + multipart = MultipartStream(data=data, files=files, boundary=boundary) + headers = multipart.get_headers() + return headers, multipart + + +def encode_text(text: str) -> tuple[dict[str, str], ByteStream]: + body = text.encode("utf-8") + content_length = str(len(body)) + content_type = "text/plain; charset=utf-8" + headers = {"Content-Length": content_length, "Content-Type": content_type} + return headers, ByteStream(body) + + +def encode_html(html: str) -> tuple[dict[str, str], ByteStream]: + body = html.encode("utf-8") + content_length = str(len(body)) + content_type = "text/html; charset=utf-8" + headers = {"Content-Length": content_length, "Content-Type": content_type} + return headers, ByteStream(body) + + +def encode_json(json: Any) -> tuple[dict[str, str], ByteStream]: + body = json_dumps( + json, ensure_ascii=False, separators=(",", ":"), allow_nan=False + ).encode("utf-8") + content_length = str(len(body)) + content_type = "application/json" + headers = {"Content-Length": content_length, "Content-Type": content_type} + return headers, ByteStream(body) + + +def encode_request( + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: Any | None = None, + boundary: bytes | None = None, +) -> tuple[dict[str, str], SyncByteStream | AsyncByteStream]: + """ + Handles encoding the given `content`, `data`, `files`, and `json`, + returning a two-tuple of (, ). + """ + if data is not None and not isinstance(data, Mapping): + # We prefer to separate `content=` + # for raw request content, and `data=
` for url encoded or + # multipart form content. + # + # However for compat with requests, we *do* still support + # `data=` usages. We deal with that case here, treating it + # as if `content=<...>` had been supplied instead. + message = "Use 'content=<...>' to upload raw bytes/text content." + warnings.warn(message, DeprecationWarning, stacklevel=2) + return encode_content(data) + + if content is not None: + return encode_content(content) + elif files: + return encode_multipart_data(data or {}, files, boundary) + elif data: + return encode_urlencoded_data(data) + elif json is not None: + return encode_json(json) + + return {}, ByteStream(b"") + + +def encode_response( + content: ResponseContent | None = None, + text: str | None = None, + html: str | None = None, + json: Any | None = None, +) -> tuple[dict[str, str], SyncByteStream | AsyncByteStream]: + """ + Handles encoding the given `content`, returning a two-tuple of + (, ). + """ + if content is not None: + return encode_content(content) + elif text is not None: + return encode_text(text) + elif html is not None: + return encode_html(html) + elif json is not None: + return encode_json(json) + + return {}, ByteStream(b"") diff --git a/venv/lib/python3.12/site-packages/httpx/_decoders.py b/venv/lib/python3.12/site-packages/httpx/_decoders.py new file mode 100644 index 0000000..899dfad --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_decoders.py @@ -0,0 +1,393 @@ +""" +Handlers for Content-Encoding. + +See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding +""" + +from __future__ import annotations + +import codecs +import io +import typing +import zlib + +from ._exceptions import DecodingError + +# Brotli support is optional +try: + # The C bindings in `brotli` are recommended for CPython. + import brotli +except ImportError: # pragma: no cover + try: + # The CFFI bindings in `brotlicffi` are recommended for PyPy + # and other environments. + import brotlicffi as brotli + except ImportError: + brotli = None + + +# Zstandard support is optional +try: + import zstandard +except ImportError: # pragma: no cover + zstandard = None # type: ignore + + +class ContentDecoder: + def decode(self, data: bytes) -> bytes: + raise NotImplementedError() # pragma: no cover + + def flush(self) -> bytes: + raise NotImplementedError() # pragma: no cover + + +class IdentityDecoder(ContentDecoder): + """ + Handle unencoded data. + """ + + def decode(self, data: bytes) -> bytes: + return data + + def flush(self) -> bytes: + return b"" + + +class DeflateDecoder(ContentDecoder): + """ + Handle 'deflate' decoding. + + See: https://stackoverflow.com/questions/1838699 + """ + + def __init__(self) -> None: + self.first_attempt = True + self.decompressor = zlib.decompressobj() + + def decode(self, data: bytes) -> bytes: + was_first_attempt = self.first_attempt + self.first_attempt = False + try: + return self.decompressor.decompress(data) + except zlib.error as exc: + if was_first_attempt: + self.decompressor = zlib.decompressobj(-zlib.MAX_WBITS) + return self.decode(data) + raise DecodingError(str(exc)) from exc + + def flush(self) -> bytes: + try: + return self.decompressor.flush() + except zlib.error as exc: # pragma: no cover + raise DecodingError(str(exc)) from exc + + +class GZipDecoder(ContentDecoder): + """ + Handle 'gzip' decoding. + + See: https://stackoverflow.com/questions/1838699 + """ + + def __init__(self) -> None: + self.decompressor = zlib.decompressobj(zlib.MAX_WBITS | 16) + + def decode(self, data: bytes) -> bytes: + try: + return self.decompressor.decompress(data) + except zlib.error as exc: + raise DecodingError(str(exc)) from exc + + def flush(self) -> bytes: + try: + return self.decompressor.flush() + except zlib.error as exc: # pragma: no cover + raise DecodingError(str(exc)) from exc + + +class BrotliDecoder(ContentDecoder): + """ + Handle 'brotli' decoding. + + Requires `pip install brotlipy`. See: https://brotlipy.readthedocs.io/ + or `pip install brotli`. See https://github.com/google/brotli + Supports both 'brotlipy' and 'Brotli' packages since they share an import + name. The top branches are for 'brotlipy' and bottom branches for 'Brotli' + """ + + def __init__(self) -> None: + if brotli is None: # pragma: no cover + raise ImportError( + "Using 'BrotliDecoder', but neither of the 'brotlicffi' or 'brotli' " + "packages have been installed. " + "Make sure to install httpx using `pip install httpx[brotli]`." + ) from None + + self.decompressor = brotli.Decompressor() + self.seen_data = False + self._decompress: typing.Callable[[bytes], bytes] + if hasattr(self.decompressor, "decompress"): + # The 'brotlicffi' package. + self._decompress = self.decompressor.decompress # pragma: no cover + else: + # The 'brotli' package. + self._decompress = self.decompressor.process # pragma: no cover + + def decode(self, data: bytes) -> bytes: + if not data: + return b"" + self.seen_data = True + try: + return self._decompress(data) + except brotli.error as exc: + raise DecodingError(str(exc)) from exc + + def flush(self) -> bytes: + if not self.seen_data: + return b"" + try: + if hasattr(self.decompressor, "finish"): + # Only available in the 'brotlicffi' package. + + # As the decompressor decompresses eagerly, this + # will never actually emit any data. However, it will potentially throw + # errors if a truncated or damaged data stream has been used. + self.decompressor.finish() # pragma: no cover + return b"" + except brotli.error as exc: # pragma: no cover + raise DecodingError(str(exc)) from exc + + +class ZStandardDecoder(ContentDecoder): + """ + Handle 'zstd' RFC 8878 decoding. + + Requires `pip install zstandard`. + Can be installed as a dependency of httpx using `pip install httpx[zstd]`. + """ + + # inspired by the ZstdDecoder implementation in urllib3 + def __init__(self) -> None: + if zstandard is None: # pragma: no cover + raise ImportError( + "Using 'ZStandardDecoder', ..." + "Make sure to install httpx using `pip install httpx[zstd]`." + ) from None + + self.decompressor = zstandard.ZstdDecompressor().decompressobj() + self.seen_data = False + + def decode(self, data: bytes) -> bytes: + assert zstandard is not None + self.seen_data = True + output = io.BytesIO() + try: + output.write(self.decompressor.decompress(data)) + while self.decompressor.eof and self.decompressor.unused_data: + unused_data = self.decompressor.unused_data + self.decompressor = zstandard.ZstdDecompressor().decompressobj() + output.write(self.decompressor.decompress(unused_data)) + except zstandard.ZstdError as exc: + raise DecodingError(str(exc)) from exc + return output.getvalue() + + def flush(self) -> bytes: + if not self.seen_data: + return b"" + ret = self.decompressor.flush() # note: this is a no-op + if not self.decompressor.eof: + raise DecodingError("Zstandard data is incomplete") # pragma: no cover + return bytes(ret) + + +class MultiDecoder(ContentDecoder): + """ + Handle the case where multiple encodings have been applied. + """ + + def __init__(self, children: typing.Sequence[ContentDecoder]) -> None: + """ + 'children' should be a sequence of decoders in the order in which + each was applied. + """ + # Note that we reverse the order for decoding. + self.children = list(reversed(children)) + + def decode(self, data: bytes) -> bytes: + for child in self.children: + data = child.decode(data) + return data + + def flush(self) -> bytes: + data = b"" + for child in self.children: + data = child.decode(data) + child.flush() + return data + + +class ByteChunker: + """ + Handles returning byte content in fixed-size chunks. + """ + + def __init__(self, chunk_size: int | None = None) -> None: + self._buffer = io.BytesIO() + self._chunk_size = chunk_size + + def decode(self, content: bytes) -> list[bytes]: + if self._chunk_size is None: + return [content] if content else [] + + self._buffer.write(content) + if self._buffer.tell() >= self._chunk_size: + value = self._buffer.getvalue() + chunks = [ + value[i : i + self._chunk_size] + for i in range(0, len(value), self._chunk_size) + ] + if len(chunks[-1]) == self._chunk_size: + self._buffer.seek(0) + self._buffer.truncate() + return chunks + else: + self._buffer.seek(0) + self._buffer.write(chunks[-1]) + self._buffer.truncate() + return chunks[:-1] + else: + return [] + + def flush(self) -> list[bytes]: + value = self._buffer.getvalue() + self._buffer.seek(0) + self._buffer.truncate() + return [value] if value else [] + + +class TextChunker: + """ + Handles returning text content in fixed-size chunks. + """ + + def __init__(self, chunk_size: int | None = None) -> None: + self._buffer = io.StringIO() + self._chunk_size = chunk_size + + def decode(self, content: str) -> list[str]: + if self._chunk_size is None: + return [content] if content else [] + + self._buffer.write(content) + if self._buffer.tell() >= self._chunk_size: + value = self._buffer.getvalue() + chunks = [ + value[i : i + self._chunk_size] + for i in range(0, len(value), self._chunk_size) + ] + if len(chunks[-1]) == self._chunk_size: + self._buffer.seek(0) + self._buffer.truncate() + return chunks + else: + self._buffer.seek(0) + self._buffer.write(chunks[-1]) + self._buffer.truncate() + return chunks[:-1] + else: + return [] + + def flush(self) -> list[str]: + value = self._buffer.getvalue() + self._buffer.seek(0) + self._buffer.truncate() + return [value] if value else [] + + +class TextDecoder: + """ + Handles incrementally decoding bytes into text + """ + + def __init__(self, encoding: str = "utf-8") -> None: + self.decoder = codecs.getincrementaldecoder(encoding)(errors="replace") + + def decode(self, data: bytes) -> str: + return self.decoder.decode(data) + + def flush(self) -> str: + return self.decoder.decode(b"", True) + + +class LineDecoder: + """ + Handles incrementally reading lines from text. + + Has the same behaviour as the stdllib splitlines, + but handling the input iteratively. + """ + + def __init__(self) -> None: + self.buffer: list[str] = [] + self.trailing_cr: bool = False + + def decode(self, text: str) -> list[str]: + # See https://docs.python.org/3/library/stdtypes.html#str.splitlines + NEWLINE_CHARS = "\n\r\x0b\x0c\x1c\x1d\x1e\x85\u2028\u2029" + + # We always push a trailing `\r` into the next decode iteration. + if self.trailing_cr: + text = "\r" + text + self.trailing_cr = False + if text.endswith("\r"): + self.trailing_cr = True + text = text[:-1] + + if not text: + # NOTE: the edge case input of empty text doesn't occur in practice, + # because other httpx internals filter out this value + return [] # pragma: no cover + + trailing_newline = text[-1] in NEWLINE_CHARS + lines = text.splitlines() + + if len(lines) == 1 and not trailing_newline: + # No new lines, buffer the input and continue. + self.buffer.append(lines[0]) + return [] + + if self.buffer: + # Include any existing buffer in the first portion of the + # splitlines result. + lines = ["".join(self.buffer) + lines[0]] + lines[1:] + self.buffer = [] + + if not trailing_newline: + # If the last segment of splitlines is not newline terminated, + # then drop it from our output and start a new buffer. + self.buffer = [lines.pop()] + + return lines + + def flush(self) -> list[str]: + if not self.buffer and not self.trailing_cr: + return [] + + lines = ["".join(self.buffer)] + self.buffer = [] + self.trailing_cr = False + return lines + + +SUPPORTED_DECODERS = { + "identity": IdentityDecoder, + "gzip": GZipDecoder, + "deflate": DeflateDecoder, + "br": BrotliDecoder, + "zstd": ZStandardDecoder, +} + + +if brotli is None: + SUPPORTED_DECODERS.pop("br") # pragma: no cover +if zstandard is None: + SUPPORTED_DECODERS.pop("zstd") # pragma: no cover diff --git a/venv/lib/python3.12/site-packages/httpx/_exceptions.py b/venv/lib/python3.12/site-packages/httpx/_exceptions.py new file mode 100644 index 0000000..77f45a6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_exceptions.py @@ -0,0 +1,379 @@ +""" +Our exception hierarchy: + +* HTTPError + x RequestError + + TransportError + - TimeoutException + · ConnectTimeout + · ReadTimeout + · WriteTimeout + · PoolTimeout + - NetworkError + · ConnectError + · ReadError + · WriteError + · CloseError + - ProtocolError + · LocalProtocolError + · RemoteProtocolError + - ProxyError + - UnsupportedProtocol + + DecodingError + + TooManyRedirects + x HTTPStatusError +* InvalidURL +* CookieConflict +* StreamError + x StreamConsumed + x StreamClosed + x ResponseNotRead + x RequestNotRead +""" + +from __future__ import annotations + +import contextlib +import typing + +if typing.TYPE_CHECKING: + from ._models import Request, Response # pragma: no cover + +__all__ = [ + "CloseError", + "ConnectError", + "ConnectTimeout", + "CookieConflict", + "DecodingError", + "HTTPError", + "HTTPStatusError", + "InvalidURL", + "LocalProtocolError", + "NetworkError", + "PoolTimeout", + "ProtocolError", + "ProxyError", + "ReadError", + "ReadTimeout", + "RemoteProtocolError", + "RequestError", + "RequestNotRead", + "ResponseNotRead", + "StreamClosed", + "StreamConsumed", + "StreamError", + "TimeoutException", + "TooManyRedirects", + "TransportError", + "UnsupportedProtocol", + "WriteError", + "WriteTimeout", +] + + +class HTTPError(Exception): + """ + Base class for `RequestError` and `HTTPStatusError`. + + Useful for `try...except` blocks when issuing a request, + and then calling `.raise_for_status()`. + + For example: + + ``` + try: + response = httpx.get("https://www.example.com") + response.raise_for_status() + except httpx.HTTPError as exc: + print(f"HTTP Exception for {exc.request.url} - {exc}") + ``` + """ + + def __init__(self, message: str) -> None: + super().__init__(message) + self._request: Request | None = None + + @property + def request(self) -> Request: + if self._request is None: + raise RuntimeError("The .request property has not been set.") + return self._request + + @request.setter + def request(self, request: Request) -> None: + self._request = request + + +class RequestError(HTTPError): + """ + Base class for all exceptions that may occur when issuing a `.request()`. + """ + + def __init__(self, message: str, *, request: Request | None = None) -> None: + super().__init__(message) + # At the point an exception is raised we won't typically have a request + # instance to associate it with. + # + # The 'request_context' context manager is used within the Client and + # Response methods in order to ensure that any raised exceptions + # have a `.request` property set on them. + self._request = request + + +class TransportError(RequestError): + """ + Base class for all exceptions that occur at the level of the Transport API. + """ + + +# Timeout exceptions... + + +class TimeoutException(TransportError): + """ + The base class for timeout errors. + + An operation has timed out. + """ + + +class ConnectTimeout(TimeoutException): + """ + Timed out while connecting to the host. + """ + + +class ReadTimeout(TimeoutException): + """ + Timed out while receiving data from the host. + """ + + +class WriteTimeout(TimeoutException): + """ + Timed out while sending data to the host. + """ + + +class PoolTimeout(TimeoutException): + """ + Timed out waiting to acquire a connection from the pool. + """ + + +# Core networking exceptions... + + +class NetworkError(TransportError): + """ + The base class for network-related errors. + + An error occurred while interacting with the network. + """ + + +class ReadError(NetworkError): + """ + Failed to receive data from the network. + """ + + +class WriteError(NetworkError): + """ + Failed to send data through the network. + """ + + +class ConnectError(NetworkError): + """ + Failed to establish a connection. + """ + + +class CloseError(NetworkError): + """ + Failed to close a connection. + """ + + +# Other transport exceptions... + + +class ProxyError(TransportError): + """ + An error occurred while establishing a proxy connection. + """ + + +class UnsupportedProtocol(TransportError): + """ + Attempted to make a request to an unsupported protocol. + + For example issuing a request to `ftp://www.example.com`. + """ + + +class ProtocolError(TransportError): + """ + The protocol was violated. + """ + + +class LocalProtocolError(ProtocolError): + """ + A protocol was violated by the client. + + For example if the user instantiated a `Request` instance explicitly, + failed to include the mandatory `Host:` header, and then issued it directly + using `client.send()`. + """ + + +class RemoteProtocolError(ProtocolError): + """ + The protocol was violated by the server. + + For example, returning malformed HTTP. + """ + + +# Other request exceptions... + + +class DecodingError(RequestError): + """ + Decoding of the response failed, due to a malformed encoding. + """ + + +class TooManyRedirects(RequestError): + """ + Too many redirects. + """ + + +# Client errors + + +class HTTPStatusError(HTTPError): + """ + The response had an error HTTP status of 4xx or 5xx. + + May be raised when calling `response.raise_for_status()` + """ + + def __init__(self, message: str, *, request: Request, response: Response) -> None: + super().__init__(message) + self.request = request + self.response = response + + +class InvalidURL(Exception): + """ + URL is improperly formed or cannot be parsed. + """ + + def __init__(self, message: str) -> None: + super().__init__(message) + + +class CookieConflict(Exception): + """ + Attempted to lookup a cookie by name, but multiple cookies existed. + + Can occur when calling `response.cookies.get(...)`. + """ + + def __init__(self, message: str) -> None: + super().__init__(message) + + +# Stream exceptions... + +# These may occur as the result of a programming error, by accessing +# the request/response stream in an invalid manner. + + +class StreamError(RuntimeError): + """ + The base class for stream exceptions. + + The developer made an error in accessing the request stream in + an invalid way. + """ + + def __init__(self, message: str) -> None: + super().__init__(message) + + +class StreamConsumed(StreamError): + """ + Attempted to read or stream content, but the content has already + been streamed. + """ + + def __init__(self) -> None: + message = ( + "Attempted to read or stream some content, but the content has " + "already been streamed. For requests, this could be due to passing " + "a generator as request content, and then receiving a redirect " + "response or a secondary request as part of an authentication flow." + "For responses, this could be due to attempting to stream the response " + "content more than once." + ) + super().__init__(message) + + +class StreamClosed(StreamError): + """ + Attempted to read or stream response content, but the request has been + closed. + """ + + def __init__(self) -> None: + message = ( + "Attempted to read or stream content, but the stream has " "been closed." + ) + super().__init__(message) + + +class ResponseNotRead(StreamError): + """ + Attempted to access streaming response content, without having called `read()`. + """ + + def __init__(self) -> None: + message = ( + "Attempted to access streaming response content," + " without having called `read()`." + ) + super().__init__(message) + + +class RequestNotRead(StreamError): + """ + Attempted to access streaming request content, without having called `read()`. + """ + + def __init__(self) -> None: + message = ( + "Attempted to access streaming request content," + " without having called `read()`." + ) + super().__init__(message) + + +@contextlib.contextmanager +def request_context( + request: Request | None = None, +) -> typing.Iterator[None]: + """ + A context manager that can be used to attach the given request context + to any `RequestError` exceptions that are raised within the block. + """ + try: + yield + except RequestError as exc: + if request is not None: + exc.request = request + raise exc diff --git a/venv/lib/python3.12/site-packages/httpx/_main.py b/venv/lib/python3.12/site-packages/httpx/_main.py new file mode 100644 index 0000000..cffa4bb --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_main.py @@ -0,0 +1,506 @@ +from __future__ import annotations + +import functools +import json +import sys +import typing + +import click +import pygments.lexers +import pygments.util +import rich.console +import rich.markup +import rich.progress +import rich.syntax +import rich.table + +from ._client import Client +from ._exceptions import RequestError +from ._models import Response +from ._status_codes import codes + +if typing.TYPE_CHECKING: + import httpcore # pragma: no cover + + +def print_help() -> None: + console = rich.console.Console() + + console.print("[bold]HTTPX :butterfly:", justify="center") + console.print() + console.print("A next generation HTTP client.", justify="center") + console.print() + console.print( + "Usage: [bold]httpx[/bold] [cyan] [OPTIONS][/cyan] ", justify="left" + ) + console.print() + + table = rich.table.Table.grid(padding=1, pad_edge=True) + table.add_column("Parameter", no_wrap=True, justify="left", style="bold") + table.add_column("Description") + table.add_row( + "-m, --method [cyan]METHOD", + "Request method, such as GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD.\n" + "[Default: GET, or POST if a request body is included]", + ) + table.add_row( + "-p, --params [cyan] ...", + "Query parameters to include in the request URL.", + ) + table.add_row( + "-c, --content [cyan]TEXT", "Byte content to include in the request body." + ) + table.add_row( + "-d, --data [cyan] ...", "Form data to include in the request body." + ) + table.add_row( + "-f, --files [cyan] ...", + "Form files to include in the request body.", + ) + table.add_row("-j, --json [cyan]TEXT", "JSON data to include in the request body.") + table.add_row( + "-h, --headers [cyan] ...", + "Include additional HTTP headers in the request.", + ) + table.add_row( + "--cookies [cyan] ...", "Cookies to include in the request." + ) + table.add_row( + "--auth [cyan]", + "Username and password to include in the request. Specify '-' for the password" + " to use a password prompt. Note that using --verbose/-v will expose" + " the Authorization header, including the password encoding" + " in a trivially reversible format.", + ) + + table.add_row( + "--proxy [cyan]URL", + "Send the request via a proxy. Should be the URL giving the proxy address.", + ) + + table.add_row( + "--timeout [cyan]FLOAT", + "Timeout value to use for network operations, such as establishing the" + " connection, reading some data, etc... [Default: 5.0]", + ) + + table.add_row("--follow-redirects", "Automatically follow redirects.") + table.add_row("--no-verify", "Disable SSL verification.") + table.add_row( + "--http2", "Send the request using HTTP/2, if the remote server supports it." + ) + + table.add_row( + "--download [cyan]FILE", + "Save the response content as a file, rather than displaying it.", + ) + + table.add_row("-v, --verbose", "Verbose output. Show request as well as response.") + table.add_row("--help", "Show this message and exit.") + console.print(table) + + +def get_lexer_for_response(response: Response) -> str: + content_type = response.headers.get("Content-Type") + if content_type is not None: + mime_type, _, _ = content_type.partition(";") + try: + return typing.cast( + str, pygments.lexers.get_lexer_for_mimetype(mime_type.strip()).name + ) + except pygments.util.ClassNotFound: # pragma: no cover + pass + return "" # pragma: no cover + + +def format_request_headers(request: httpcore.Request, http2: bool = False) -> str: + version = "HTTP/2" if http2 else "HTTP/1.1" + headers = [ + (name.lower() if http2 else name, value) for name, value in request.headers + ] + method = request.method.decode("ascii") + target = request.url.target.decode("ascii") + lines = [f"{method} {target} {version}"] + [ + f"{name.decode('ascii')}: {value.decode('ascii')}" for name, value in headers + ] + return "\n".join(lines) + + +def format_response_headers( + http_version: bytes, + status: int, + reason_phrase: bytes | None, + headers: list[tuple[bytes, bytes]], +) -> str: + version = http_version.decode("ascii") + reason = ( + codes.get_reason_phrase(status) + if reason_phrase is None + else reason_phrase.decode("ascii") + ) + lines = [f"{version} {status} {reason}"] + [ + f"{name.decode('ascii')}: {value.decode('ascii')}" for name, value in headers + ] + return "\n".join(lines) + + +def print_request_headers(request: httpcore.Request, http2: bool = False) -> None: + console = rich.console.Console() + http_text = format_request_headers(request, http2=http2) + syntax = rich.syntax.Syntax(http_text, "http", theme="ansi_dark", word_wrap=True) + console.print(syntax) + syntax = rich.syntax.Syntax("", "http", theme="ansi_dark", word_wrap=True) + console.print(syntax) + + +def print_response_headers( + http_version: bytes, + status: int, + reason_phrase: bytes | None, + headers: list[tuple[bytes, bytes]], +) -> None: + console = rich.console.Console() + http_text = format_response_headers(http_version, status, reason_phrase, headers) + syntax = rich.syntax.Syntax(http_text, "http", theme="ansi_dark", word_wrap=True) + console.print(syntax) + syntax = rich.syntax.Syntax("", "http", theme="ansi_dark", word_wrap=True) + console.print(syntax) + + +def print_response(response: Response) -> None: + console = rich.console.Console() + lexer_name = get_lexer_for_response(response) + if lexer_name: + if lexer_name.lower() == "json": + try: + data = response.json() + text = json.dumps(data, indent=4) + except ValueError: # pragma: no cover + text = response.text + else: + text = response.text + + syntax = rich.syntax.Syntax(text, lexer_name, theme="ansi_dark", word_wrap=True) + console.print(syntax) + else: + console.print(f"<{len(response.content)} bytes of binary data>") + + +_PCTRTT = typing.Tuple[typing.Tuple[str, str], ...] +_PCTRTTT = typing.Tuple[_PCTRTT, ...] +_PeerCertRetDictType = typing.Dict[str, typing.Union[str, _PCTRTTT, _PCTRTT]] + + +def format_certificate(cert: _PeerCertRetDictType) -> str: # pragma: no cover + lines = [] + for key, value in cert.items(): + if isinstance(value, (list, tuple)): + lines.append(f"* {key}:") + for item in value: + if key in ("subject", "issuer"): + for sub_item in item: + lines.append(f"* {sub_item[0]}: {sub_item[1]!r}") + elif isinstance(item, tuple) and len(item) == 2: + lines.append(f"* {item[0]}: {item[1]!r}") + else: + lines.append(f"* {item!r}") + else: + lines.append(f"* {key}: {value!r}") + return "\n".join(lines) + + +def trace( + name: str, info: typing.Mapping[str, typing.Any], verbose: bool = False +) -> None: + console = rich.console.Console() + if name == "connection.connect_tcp.started" and verbose: + host = info["host"] + console.print(f"* Connecting to {host!r}") + elif name == "connection.connect_tcp.complete" and verbose: + stream = info["return_value"] + server_addr = stream.get_extra_info("server_addr") + console.print(f"* Connected to {server_addr[0]!r} on port {server_addr[1]}") + elif name == "connection.start_tls.complete" and verbose: # pragma: no cover + stream = info["return_value"] + ssl_object = stream.get_extra_info("ssl_object") + version = ssl_object.version() + cipher = ssl_object.cipher() + server_cert = ssl_object.getpeercert() + alpn = ssl_object.selected_alpn_protocol() + console.print(f"* SSL established using {version!r} / {cipher[0]!r}") + console.print(f"* Selected ALPN protocol: {alpn!r}") + if server_cert: + console.print("* Server certificate:") + console.print(format_certificate(server_cert)) + elif name == "http11.send_request_headers.started" and verbose: + request = info["request"] + print_request_headers(request, http2=False) + elif name == "http2.send_request_headers.started" and verbose: # pragma: no cover + request = info["request"] + print_request_headers(request, http2=True) + elif name == "http11.receive_response_headers.complete": + http_version, status, reason_phrase, headers = info["return_value"] + print_response_headers(http_version, status, reason_phrase, headers) + elif name == "http2.receive_response_headers.complete": # pragma: no cover + status, headers = info["return_value"] + http_version = b"HTTP/2" + reason_phrase = None + print_response_headers(http_version, status, reason_phrase, headers) + + +def download_response(response: Response, download: typing.BinaryIO) -> None: + console = rich.console.Console() + console.print() + content_length = response.headers.get("Content-Length") + with rich.progress.Progress( + "[progress.description]{task.description}", + "[progress.percentage]{task.percentage:>3.0f}%", + rich.progress.BarColumn(bar_width=None), + rich.progress.DownloadColumn(), + rich.progress.TransferSpeedColumn(), + ) as progress: + description = f"Downloading [bold]{rich.markup.escape(download.name)}" + download_task = progress.add_task( + description, + total=int(content_length or 0), + start=content_length is not None, + ) + for chunk in response.iter_bytes(): + download.write(chunk) + progress.update(download_task, completed=response.num_bytes_downloaded) + + +def validate_json( + ctx: click.Context, + param: click.Option | click.Parameter, + value: typing.Any, +) -> typing.Any: + if value is None: + return None + + try: + return json.loads(value) + except json.JSONDecodeError: # pragma: no cover + raise click.BadParameter("Not valid JSON") + + +def validate_auth( + ctx: click.Context, + param: click.Option | click.Parameter, + value: typing.Any, +) -> typing.Any: + if value == (None, None): + return None + + username, password = value + if password == "-": # pragma: no cover + password = click.prompt("Password", hide_input=True) + return (username, password) + + +def handle_help( + ctx: click.Context, + param: click.Option | click.Parameter, + value: typing.Any, +) -> None: + if not value or ctx.resilient_parsing: + return + + print_help() + ctx.exit() + + +@click.command(add_help_option=False) +@click.argument("url", type=str) +@click.option( + "--method", + "-m", + "method", + type=str, + help=( + "Request method, such as GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD. " + "[Default: GET, or POST if a request body is included]" + ), +) +@click.option( + "--params", + "-p", + "params", + type=(str, str), + multiple=True, + help="Query parameters to include in the request URL.", +) +@click.option( + "--content", + "-c", + "content", + type=str, + help="Byte content to include in the request body.", +) +@click.option( + "--data", + "-d", + "data", + type=(str, str), + multiple=True, + help="Form data to include in the request body.", +) +@click.option( + "--files", + "-f", + "files", + type=(str, click.File(mode="rb")), + multiple=True, + help="Form files to include in the request body.", +) +@click.option( + "--json", + "-j", + "json", + type=str, + callback=validate_json, + help="JSON data to include in the request body.", +) +@click.option( + "--headers", + "-h", + "headers", + type=(str, str), + multiple=True, + help="Include additional HTTP headers in the request.", +) +@click.option( + "--cookies", + "cookies", + type=(str, str), + multiple=True, + help="Cookies to include in the request.", +) +@click.option( + "--auth", + "auth", + type=(str, str), + default=(None, None), + callback=validate_auth, + help=( + "Username and password to include in the request. " + "Specify '-' for the password to use a password prompt. " + "Note that using --verbose/-v will expose the Authorization header, " + "including the password encoding in a trivially reversible format." + ), +) +@click.option( + "--proxy", + "proxy", + type=str, + default=None, + help="Send the request via a proxy. Should be the URL giving the proxy address.", +) +@click.option( + "--timeout", + "timeout", + type=float, + default=5.0, + help=( + "Timeout value to use for network operations, such as establishing the " + "connection, reading some data, etc... [Default: 5.0]" + ), +) +@click.option( + "--follow-redirects", + "follow_redirects", + is_flag=True, + default=False, + help="Automatically follow redirects.", +) +@click.option( + "--no-verify", + "verify", + is_flag=True, + default=True, + help="Disable SSL verification.", +) +@click.option( + "--http2", + "http2", + type=bool, + is_flag=True, + default=False, + help="Send the request using HTTP/2, if the remote server supports it.", +) +@click.option( + "--download", + type=click.File("wb"), + help="Save the response content as a file, rather than displaying it.", +) +@click.option( + "--verbose", + "-v", + type=bool, + is_flag=True, + default=False, + help="Verbose. Show request as well as response.", +) +@click.option( + "--help", + is_flag=True, + is_eager=True, + expose_value=False, + callback=handle_help, + help="Show this message and exit.", +) +def main( + url: str, + method: str, + params: list[tuple[str, str]], + content: str, + data: list[tuple[str, str]], + files: list[tuple[str, click.File]], + json: str, + headers: list[tuple[str, str]], + cookies: list[tuple[str, str]], + auth: tuple[str, str] | None, + proxy: str, + timeout: float, + follow_redirects: bool, + verify: bool, + http2: bool, + download: typing.BinaryIO | None, + verbose: bool, +) -> None: + """ + An HTTP command line client. + Sends a request and displays the response. + """ + if not method: + method = "POST" if content or data or files or json else "GET" + + try: + with Client(proxy=proxy, timeout=timeout, http2=http2, verify=verify) as client: + with client.stream( + method, + url, + params=list(params), + content=content, + data=dict(data), + files=files, # type: ignore + json=json, + headers=headers, + cookies=dict(cookies), + auth=auth, + follow_redirects=follow_redirects, + extensions={"trace": functools.partial(trace, verbose=verbose)}, + ) as response: + if download is not None: + download_response(response, download) + else: + response.read() + if response.content: + print_response(response) + + except RequestError as exc: + console = rich.console.Console() + console.print(f"[red]{type(exc).__name__}[/red]: {exc}") + sys.exit(1) + + sys.exit(0 if response.is_success else 1) diff --git a/venv/lib/python3.12/site-packages/httpx/_models.py b/venv/lib/python3.12/site-packages/httpx/_models.py new file mode 100644 index 0000000..67d74bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_models.py @@ -0,0 +1,1277 @@ +from __future__ import annotations + +import codecs +import datetime +import email.message +import json as jsonlib +import re +import typing +import urllib.request +from collections.abc import Mapping +from http.cookiejar import Cookie, CookieJar + +from ._content import ByteStream, UnattachedStream, encode_request, encode_response +from ._decoders import ( + SUPPORTED_DECODERS, + ByteChunker, + ContentDecoder, + IdentityDecoder, + LineDecoder, + MultiDecoder, + TextChunker, + TextDecoder, +) +from ._exceptions import ( + CookieConflict, + HTTPStatusError, + RequestNotRead, + ResponseNotRead, + StreamClosed, + StreamConsumed, + request_context, +) +from ._multipart import get_multipart_boundary_from_content_type +from ._status_codes import codes +from ._types import ( + AsyncByteStream, + CookieTypes, + HeaderTypes, + QueryParamTypes, + RequestContent, + RequestData, + RequestExtensions, + RequestFiles, + ResponseContent, + ResponseExtensions, + SyncByteStream, +) +from ._urls import URL +from ._utils import to_bytes_or_str, to_str + +__all__ = ["Cookies", "Headers", "Request", "Response"] + +SENSITIVE_HEADERS = {"authorization", "proxy-authorization"} + + +def _is_known_encoding(encoding: str) -> bool: + """ + Return `True` if `encoding` is a known codec. + """ + try: + codecs.lookup(encoding) + except LookupError: + return False + return True + + +def _normalize_header_key(key: str | bytes, encoding: str | None = None) -> bytes: + """ + Coerce str/bytes into a strictly byte-wise HTTP header key. + """ + return key if isinstance(key, bytes) else key.encode(encoding or "ascii") + + +def _normalize_header_value(value: str | bytes, encoding: str | None = None) -> bytes: + """ + Coerce str/bytes into a strictly byte-wise HTTP header value. + """ + if isinstance(value, bytes): + return value + if not isinstance(value, str): + raise TypeError(f"Header value must be str or bytes, not {type(value)}") + return value.encode(encoding or "ascii") + + +def _parse_content_type_charset(content_type: str) -> str | None: + # We used to use `cgi.parse_header()` here, but `cgi` became a dead battery. + # See: https://peps.python.org/pep-0594/#cgi + msg = email.message.Message() + msg["content-type"] = content_type + return msg.get_content_charset(failobj=None) + + +def _parse_header_links(value: str) -> list[dict[str, str]]: + """ + Returns a list of parsed link headers, for more info see: + https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link + The generic syntax of those is: + Link: < uri-reference >; param1=value1; param2="value2" + So for instance: + Link; '; type="image/jpeg",;' + would return + [ + {"url": "http:/.../front.jpeg", "type": "image/jpeg"}, + {"url": "http://.../back.jpeg"}, + ] + :param value: HTTP Link entity-header field + :return: list of parsed link headers + """ + links: list[dict[str, str]] = [] + replace_chars = " '\"" + value = value.strip(replace_chars) + if not value: + return links + for val in re.split(", *<", value): + try: + url, params = val.split(";", 1) + except ValueError: + url, params = val, "" + link = {"url": url.strip("<> '\"")} + for param in params.split(";"): + try: + key, value = param.split("=") + except ValueError: + break + link[key.strip(replace_chars)] = value.strip(replace_chars) + links.append(link) + return links + + +def _obfuscate_sensitive_headers( + items: typing.Iterable[tuple[typing.AnyStr, typing.AnyStr]], +) -> typing.Iterator[tuple[typing.AnyStr, typing.AnyStr]]: + for k, v in items: + if to_str(k.lower()) in SENSITIVE_HEADERS: + v = to_bytes_or_str("[secure]", match_type_of=v) + yield k, v + + +class Headers(typing.MutableMapping[str, str]): + """ + HTTP headers, as a case-insensitive multi-dict. + """ + + def __init__( + self, + headers: HeaderTypes | None = None, + encoding: str | None = None, + ) -> None: + self._list = [] # type: typing.List[typing.Tuple[bytes, bytes, bytes]] + + if isinstance(headers, Headers): + self._list = list(headers._list) + elif isinstance(headers, Mapping): + for k, v in headers.items(): + bytes_key = _normalize_header_key(k, encoding) + bytes_value = _normalize_header_value(v, encoding) + self._list.append((bytes_key, bytes_key.lower(), bytes_value)) + elif headers is not None: + for k, v in headers: + bytes_key = _normalize_header_key(k, encoding) + bytes_value = _normalize_header_value(v, encoding) + self._list.append((bytes_key, bytes_key.lower(), bytes_value)) + + self._encoding = encoding + + @property + def encoding(self) -> str: + """ + Header encoding is mandated as ascii, but we allow fallbacks to utf-8 + or iso-8859-1. + """ + if self._encoding is None: + for encoding in ["ascii", "utf-8"]: + for key, value in self.raw: + try: + key.decode(encoding) + value.decode(encoding) + except UnicodeDecodeError: + break + else: + # The else block runs if 'break' did not occur, meaning + # all values fitted the encoding. + self._encoding = encoding + break + else: + # The ISO-8859-1 encoding covers all 256 code points in a byte, + # so will never raise decode errors. + self._encoding = "iso-8859-1" + return self._encoding + + @encoding.setter + def encoding(self, value: str) -> None: + self._encoding = value + + @property + def raw(self) -> list[tuple[bytes, bytes]]: + """ + Returns a list of the raw header items, as byte pairs. + """ + return [(raw_key, value) for raw_key, _, value in self._list] + + def keys(self) -> typing.KeysView[str]: + return {key.decode(self.encoding): None for _, key, value in self._list}.keys() + + def values(self) -> typing.ValuesView[str]: + values_dict: dict[str, str] = {} + for _, key, value in self._list: + str_key = key.decode(self.encoding) + str_value = value.decode(self.encoding) + if str_key in values_dict: + values_dict[str_key] += f", {str_value}" + else: + values_dict[str_key] = str_value + return values_dict.values() + + def items(self) -> typing.ItemsView[str, str]: + """ + Return `(key, value)` items of headers. Concatenate headers + into a single comma separated value when a key occurs multiple times. + """ + values_dict: dict[str, str] = {} + for _, key, value in self._list: + str_key = key.decode(self.encoding) + str_value = value.decode(self.encoding) + if str_key in values_dict: + values_dict[str_key] += f", {str_value}" + else: + values_dict[str_key] = str_value + return values_dict.items() + + def multi_items(self) -> list[tuple[str, str]]: + """ + Return a list of `(key, value)` pairs of headers. Allow multiple + occurrences of the same key without concatenating into a single + comma separated value. + """ + return [ + (key.decode(self.encoding), value.decode(self.encoding)) + for _, key, value in self._list + ] + + def get(self, key: str, default: typing.Any = None) -> typing.Any: + """ + Return a header value. If multiple occurrences of the header occur + then concatenate them together with commas. + """ + try: + return self[key] + except KeyError: + return default + + def get_list(self, key: str, split_commas: bool = False) -> list[str]: + """ + Return a list of all header values for a given key. + If `split_commas=True` is passed, then any comma separated header + values are split into multiple return strings. + """ + get_header_key = key.lower().encode(self.encoding) + + values = [ + item_value.decode(self.encoding) + for _, item_key, item_value in self._list + if item_key.lower() == get_header_key + ] + + if not split_commas: + return values + + split_values = [] + for value in values: + split_values.extend([item.strip() for item in value.split(",")]) + return split_values + + def update(self, headers: HeaderTypes | None = None) -> None: # type: ignore + headers = Headers(headers) + for key in headers.keys(): + if key in self: + self.pop(key) + self._list.extend(headers._list) + + def copy(self) -> Headers: + return Headers(self, encoding=self.encoding) + + def __getitem__(self, key: str) -> str: + """ + Return a single header value. + + If there are multiple headers with the same key, then we concatenate + them with commas. See: https://tools.ietf.org/html/rfc7230#section-3.2.2 + """ + normalized_key = key.lower().encode(self.encoding) + + items = [ + header_value.decode(self.encoding) + for _, header_key, header_value in self._list + if header_key == normalized_key + ] + + if items: + return ", ".join(items) + + raise KeyError(key) + + def __setitem__(self, key: str, value: str) -> None: + """ + Set the header `key` to `value`, removing any duplicate entries. + Retains insertion order. + """ + set_key = key.encode(self._encoding or "utf-8") + set_value = value.encode(self._encoding or "utf-8") + lookup_key = set_key.lower() + + found_indexes = [ + idx + for idx, (_, item_key, _) in enumerate(self._list) + if item_key == lookup_key + ] + + for idx in reversed(found_indexes[1:]): + del self._list[idx] + + if found_indexes: + idx = found_indexes[0] + self._list[idx] = (set_key, lookup_key, set_value) + else: + self._list.append((set_key, lookup_key, set_value)) + + def __delitem__(self, key: str) -> None: + """ + Remove the header `key`. + """ + del_key = key.lower().encode(self.encoding) + + pop_indexes = [ + idx + for idx, (_, item_key, _) in enumerate(self._list) + if item_key.lower() == del_key + ] + + if not pop_indexes: + raise KeyError(key) + + for idx in reversed(pop_indexes): + del self._list[idx] + + def __contains__(self, key: typing.Any) -> bool: + header_key = key.lower().encode(self.encoding) + return header_key in [key for _, key, _ in self._list] + + def __iter__(self) -> typing.Iterator[typing.Any]: + return iter(self.keys()) + + def __len__(self) -> int: + return len(self._list) + + def __eq__(self, other: typing.Any) -> bool: + try: + other_headers = Headers(other) + except ValueError: + return False + + self_list = [(key, value) for _, key, value in self._list] + other_list = [(key, value) for _, key, value in other_headers._list] + return sorted(self_list) == sorted(other_list) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + + encoding_str = "" + if self.encoding != "ascii": + encoding_str = f", encoding={self.encoding!r}" + + as_list = list(_obfuscate_sensitive_headers(self.multi_items())) + as_dict = dict(as_list) + + no_duplicate_keys = len(as_dict) == len(as_list) + if no_duplicate_keys: + return f"{class_name}({as_dict!r}{encoding_str})" + return f"{class_name}({as_list!r}{encoding_str})" + + +class Request: + def __init__( + self, + method: str, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + stream: SyncByteStream | AsyncByteStream | None = None, + extensions: RequestExtensions | None = None, + ) -> None: + self.method = method.upper() + self.url = URL(url) if params is None else URL(url, params=params) + self.headers = Headers(headers) + self.extensions = {} if extensions is None else dict(extensions) + + if cookies: + Cookies(cookies).set_cookie_header(self) + + if stream is None: + content_type: str | None = self.headers.get("content-type") + headers, stream = encode_request( + content=content, + data=data, + files=files, + json=json, + boundary=get_multipart_boundary_from_content_type( + content_type=content_type.encode(self.headers.encoding) + if content_type + else None + ), + ) + self._prepare(headers) + self.stream = stream + # Load the request body, except for streaming content. + if isinstance(stream, ByteStream): + self.read() + else: + # There's an important distinction between `Request(content=...)`, + # and `Request(stream=...)`. + # + # Using `content=...` implies automatically populated `Host` and content + # headers, of either `Content-Length: ...` or `Transfer-Encoding: chunked`. + # + # Using `stream=...` will not automatically include *any* + # auto-populated headers. + # + # As an end-user you don't really need `stream=...`. It's only + # useful when: + # + # * Preserving the request stream when copying requests, eg for redirects. + # * Creating request instances on the *server-side* of the transport API. + self.stream = stream + + def _prepare(self, default_headers: dict[str, str]) -> None: + for key, value in default_headers.items(): + # Ignore Transfer-Encoding if the Content-Length has been set explicitly. + if key.lower() == "transfer-encoding" and "Content-Length" in self.headers: + continue + self.headers.setdefault(key, value) + + auto_headers: list[tuple[bytes, bytes]] = [] + + has_host = "Host" in self.headers + has_content_length = ( + "Content-Length" in self.headers or "Transfer-Encoding" in self.headers + ) + + if not has_host and self.url.host: + auto_headers.append((b"Host", self.url.netloc)) + if not has_content_length and self.method in ("POST", "PUT", "PATCH"): + auto_headers.append((b"Content-Length", b"0")) + + self.headers = Headers(auto_headers + self.headers.raw) + + @property + def content(self) -> bytes: + if not hasattr(self, "_content"): + raise RequestNotRead() + return self._content + + def read(self) -> bytes: + """ + Read and return the request content. + """ + if not hasattr(self, "_content"): + assert isinstance(self.stream, typing.Iterable) + self._content = b"".join(self.stream) + if not isinstance(self.stream, ByteStream): + # If a streaming request has been read entirely into memory, then + # we can replace the stream with a raw bytes implementation, + # to ensure that any non-replayable streams can still be used. + self.stream = ByteStream(self._content) + return self._content + + async def aread(self) -> bytes: + """ + Read and return the request content. + """ + if not hasattr(self, "_content"): + assert isinstance(self.stream, typing.AsyncIterable) + self._content = b"".join([part async for part in self.stream]) + if not isinstance(self.stream, ByteStream): + # If a streaming request has been read entirely into memory, then + # we can replace the stream with a raw bytes implementation, + # to ensure that any non-replayable streams can still be used. + self.stream = ByteStream(self._content) + return self._content + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + url = str(self.url) + return f"<{class_name}({self.method!r}, {url!r})>" + + def __getstate__(self) -> dict[str, typing.Any]: + return { + name: value + for name, value in self.__dict__.items() + if name not in ["extensions", "stream"] + } + + def __setstate__(self, state: dict[str, typing.Any]) -> None: + for name, value in state.items(): + setattr(self, name, value) + self.extensions = {} + self.stream = UnattachedStream() + + +class Response: + def __init__( + self, + status_code: int, + *, + headers: HeaderTypes | None = None, + content: ResponseContent | None = None, + text: str | None = None, + html: str | None = None, + json: typing.Any = None, + stream: SyncByteStream | AsyncByteStream | None = None, + request: Request | None = None, + extensions: ResponseExtensions | None = None, + history: list[Response] | None = None, + default_encoding: str | typing.Callable[[bytes], str] = "utf-8", + ) -> None: + self.status_code = status_code + self.headers = Headers(headers) + + self._request: Request | None = request + + # When follow_redirects=False and a redirect is received, + # the client will set `response.next_request`. + self.next_request: Request | None = None + + self.extensions = {} if extensions is None else dict(extensions) + self.history = [] if history is None else list(history) + + self.is_closed = False + self.is_stream_consumed = False + + self.default_encoding = default_encoding + + if stream is None: + headers, stream = encode_response(content, text, html, json) + self._prepare(headers) + self.stream = stream + if isinstance(stream, ByteStream): + # Load the response body, except for streaming content. + self.read() + else: + # There's an important distinction between `Response(content=...)`, + # and `Response(stream=...)`. + # + # Using `content=...` implies automatically populated content headers, + # of either `Content-Length: ...` or `Transfer-Encoding: chunked`. + # + # Using `stream=...` will not automatically include any content headers. + # + # As an end-user you don't really need `stream=...`. It's only + # useful when creating response instances having received a stream + # from the transport API. + self.stream = stream + + self._num_bytes_downloaded = 0 + + def _prepare(self, default_headers: dict[str, str]) -> None: + for key, value in default_headers.items(): + # Ignore Transfer-Encoding if the Content-Length has been set explicitly. + if key.lower() == "transfer-encoding" and "content-length" in self.headers: + continue + self.headers.setdefault(key, value) + + @property + def elapsed(self) -> datetime.timedelta: + """ + Returns the time taken for the complete request/response + cycle to complete. + """ + if not hasattr(self, "_elapsed"): + raise RuntimeError( + "'.elapsed' may only be accessed after the response " + "has been read or closed." + ) + return self._elapsed + + @elapsed.setter + def elapsed(self, elapsed: datetime.timedelta) -> None: + self._elapsed = elapsed + + @property + def request(self) -> Request: + """ + Returns the request instance associated to the current response. + """ + if self._request is None: + raise RuntimeError( + "The request instance has not been set on this response." + ) + return self._request + + @request.setter + def request(self, value: Request) -> None: + self._request = value + + @property + def http_version(self) -> str: + try: + http_version: bytes = self.extensions["http_version"] + except KeyError: + return "HTTP/1.1" + else: + return http_version.decode("ascii", errors="ignore") + + @property + def reason_phrase(self) -> str: + try: + reason_phrase: bytes = self.extensions["reason_phrase"] + except KeyError: + return codes.get_reason_phrase(self.status_code) + else: + return reason_phrase.decode("ascii", errors="ignore") + + @property + def url(self) -> URL: + """ + Returns the URL for which the request was made. + """ + return self.request.url + + @property + def content(self) -> bytes: + if not hasattr(self, "_content"): + raise ResponseNotRead() + return self._content + + @property + def text(self) -> str: + if not hasattr(self, "_text"): + content = self.content + if not content: + self._text = "" + else: + decoder = TextDecoder(encoding=self.encoding or "utf-8") + self._text = "".join([decoder.decode(self.content), decoder.flush()]) + return self._text + + @property + def encoding(self) -> str | None: + """ + Return an encoding to use for decoding the byte content into text. + The priority for determining this is given by... + + * `.encoding = <>` has been set explicitly. + * The encoding as specified by the charset parameter in the Content-Type header. + * The encoding as determined by `default_encoding`, which may either be + a string like "utf-8" indicating the encoding to use, or may be a callable + which enables charset autodetection. + """ + if not hasattr(self, "_encoding"): + encoding = self.charset_encoding + if encoding is None or not _is_known_encoding(encoding): + if isinstance(self.default_encoding, str): + encoding = self.default_encoding + elif hasattr(self, "_content"): + encoding = self.default_encoding(self._content) + self._encoding = encoding or "utf-8" + return self._encoding + + @encoding.setter + def encoding(self, value: str) -> None: + """ + Set the encoding to use for decoding the byte content into text. + + If the `text` attribute has been accessed, attempting to set the + encoding will throw a ValueError. + """ + if hasattr(self, "_text"): + raise ValueError( + "Setting encoding after `text` has been accessed is not allowed." + ) + self._encoding = value + + @property + def charset_encoding(self) -> str | None: + """ + Return the encoding, as specified by the Content-Type header. + """ + content_type = self.headers.get("Content-Type") + if content_type is None: + return None + + return _parse_content_type_charset(content_type) + + def _get_content_decoder(self) -> ContentDecoder: + """ + Returns a decoder instance which can be used to decode the raw byte + content, depending on the Content-Encoding used in the response. + """ + if not hasattr(self, "_decoder"): + decoders: list[ContentDecoder] = [] + values = self.headers.get_list("content-encoding", split_commas=True) + for value in values: + value = value.strip().lower() + try: + decoder_cls = SUPPORTED_DECODERS[value] + decoders.append(decoder_cls()) + except KeyError: + continue + + if len(decoders) == 1: + self._decoder = decoders[0] + elif len(decoders) > 1: + self._decoder = MultiDecoder(children=decoders) + else: + self._decoder = IdentityDecoder() + + return self._decoder + + @property + def is_informational(self) -> bool: + """ + A property which is `True` for 1xx status codes, `False` otherwise. + """ + return codes.is_informational(self.status_code) + + @property + def is_success(self) -> bool: + """ + A property which is `True` for 2xx status codes, `False` otherwise. + """ + return codes.is_success(self.status_code) + + @property + def is_redirect(self) -> bool: + """ + A property which is `True` for 3xx status codes, `False` otherwise. + + Note that not all responses with a 3xx status code indicate a URL redirect. + + Use `response.has_redirect_location` to determine responses with a properly + formed URL redirection. + """ + return codes.is_redirect(self.status_code) + + @property + def is_client_error(self) -> bool: + """ + A property which is `True` for 4xx status codes, `False` otherwise. + """ + return codes.is_client_error(self.status_code) + + @property + def is_server_error(self) -> bool: + """ + A property which is `True` for 5xx status codes, `False` otherwise. + """ + return codes.is_server_error(self.status_code) + + @property + def is_error(self) -> bool: + """ + A property which is `True` for 4xx and 5xx status codes, `False` otherwise. + """ + return codes.is_error(self.status_code) + + @property + def has_redirect_location(self) -> bool: + """ + Returns True for 3xx responses with a properly formed URL redirection, + `False` otherwise. + """ + return ( + self.status_code + in ( + # 301 (Cacheable redirect. Method may change to GET.) + codes.MOVED_PERMANENTLY, + # 302 (Uncacheable redirect. Method may change to GET.) + codes.FOUND, + # 303 (Client should make a GET or HEAD request.) + codes.SEE_OTHER, + # 307 (Equiv. 302, but retain method) + codes.TEMPORARY_REDIRECT, + # 308 (Equiv. 301, but retain method) + codes.PERMANENT_REDIRECT, + ) + and "Location" in self.headers + ) + + def raise_for_status(self) -> Response: + """ + Raise the `HTTPStatusError` if one occurred. + """ + request = self._request + if request is None: + raise RuntimeError( + "Cannot call `raise_for_status` as the request " + "instance has not been set on this response." + ) + + if self.is_success: + return self + + if self.has_redirect_location: + message = ( + "{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n" + "Redirect location: '{0.headers[location]}'\n" + "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}" + ) + else: + message = ( + "{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n" + "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}" + ) + + status_class = self.status_code // 100 + error_types = { + 1: "Informational response", + 3: "Redirect response", + 4: "Client error", + 5: "Server error", + } + error_type = error_types.get(status_class, "Invalid status code") + message = message.format(self, error_type=error_type) + raise HTTPStatusError(message, request=request, response=self) + + def json(self, **kwargs: typing.Any) -> typing.Any: + return jsonlib.loads(self.content, **kwargs) + + @property + def cookies(self) -> Cookies: + if not hasattr(self, "_cookies"): + self._cookies = Cookies() + self._cookies.extract_cookies(self) + return self._cookies + + @property + def links(self) -> dict[str | None, dict[str, str]]: + """ + Returns the parsed header links of the response, if any + """ + header = self.headers.get("link") + if header is None: + return {} + + return { + (link.get("rel") or link.get("url")): link + for link in _parse_header_links(header) + } + + @property + def num_bytes_downloaded(self) -> int: + return self._num_bytes_downloaded + + def __repr__(self) -> str: + return f"" + + def __getstate__(self) -> dict[str, typing.Any]: + return { + name: value + for name, value in self.__dict__.items() + if name not in ["extensions", "stream", "is_closed", "_decoder"] + } + + def __setstate__(self, state: dict[str, typing.Any]) -> None: + for name, value in state.items(): + setattr(self, name, value) + self.is_closed = True + self.extensions = {} + self.stream = UnattachedStream() + + def read(self) -> bytes: + """ + Read and return the response content. + """ + if not hasattr(self, "_content"): + self._content = b"".join(self.iter_bytes()) + return self._content + + def iter_bytes(self, chunk_size: int | None = None) -> typing.Iterator[bytes]: + """ + A byte-iterator over the decoded response content. + This allows us to handle gzip, deflate, brotli, and zstd encoded responses. + """ + if hasattr(self, "_content"): + chunk_size = len(self._content) if chunk_size is None else chunk_size + for i in range(0, len(self._content), max(chunk_size, 1)): + yield self._content[i : i + chunk_size] + else: + decoder = self._get_content_decoder() + chunker = ByteChunker(chunk_size=chunk_size) + with request_context(request=self._request): + for raw_bytes in self.iter_raw(): + decoded = decoder.decode(raw_bytes) + for chunk in chunker.decode(decoded): + yield chunk + decoded = decoder.flush() + for chunk in chunker.decode(decoded): + yield chunk # pragma: no cover + for chunk in chunker.flush(): + yield chunk + + def iter_text(self, chunk_size: int | None = None) -> typing.Iterator[str]: + """ + A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + decoder = TextDecoder(encoding=self.encoding or "utf-8") + chunker = TextChunker(chunk_size=chunk_size) + with request_context(request=self._request): + for byte_content in self.iter_bytes(): + text_content = decoder.decode(byte_content) + for chunk in chunker.decode(text_content): + yield chunk + text_content = decoder.flush() + for chunk in chunker.decode(text_content): + yield chunk # pragma: no cover + for chunk in chunker.flush(): + yield chunk + + def iter_lines(self) -> typing.Iterator[str]: + decoder = LineDecoder() + with request_context(request=self._request): + for text in self.iter_text(): + for line in decoder.decode(text): + yield line + for line in decoder.flush(): + yield line + + def iter_raw(self, chunk_size: int | None = None) -> typing.Iterator[bytes]: + """ + A byte-iterator over the raw response content. + """ + if self.is_stream_consumed: + raise StreamConsumed() + if self.is_closed: + raise StreamClosed() + if not isinstance(self.stream, SyncByteStream): + raise RuntimeError("Attempted to call a sync iterator on an async stream.") + + self.is_stream_consumed = True + self._num_bytes_downloaded = 0 + chunker = ByteChunker(chunk_size=chunk_size) + + with request_context(request=self._request): + for raw_stream_bytes in self.stream: + self._num_bytes_downloaded += len(raw_stream_bytes) + for chunk in chunker.decode(raw_stream_bytes): + yield chunk + + for chunk in chunker.flush(): + yield chunk + + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + Automatically called if the response body is read to completion. + """ + if not isinstance(self.stream, SyncByteStream): + raise RuntimeError("Attempted to call an sync close on an async stream.") + + if not self.is_closed: + self.is_closed = True + with request_context(request=self._request): + self.stream.close() + + async def aread(self) -> bytes: + """ + Read and return the response content. + """ + if not hasattr(self, "_content"): + self._content = b"".join([part async for part in self.aiter_bytes()]) + return self._content + + async def aiter_bytes( + self, chunk_size: int | None = None + ) -> typing.AsyncIterator[bytes]: + """ + A byte-iterator over the decoded response content. + This allows us to handle gzip, deflate, brotli, and zstd encoded responses. + """ + if hasattr(self, "_content"): + chunk_size = len(self._content) if chunk_size is None else chunk_size + for i in range(0, len(self._content), max(chunk_size, 1)): + yield self._content[i : i + chunk_size] + else: + decoder = self._get_content_decoder() + chunker = ByteChunker(chunk_size=chunk_size) + with request_context(request=self._request): + async for raw_bytes in self.aiter_raw(): + decoded = decoder.decode(raw_bytes) + for chunk in chunker.decode(decoded): + yield chunk + decoded = decoder.flush() + for chunk in chunker.decode(decoded): + yield chunk # pragma: no cover + for chunk in chunker.flush(): + yield chunk + + async def aiter_text( + self, chunk_size: int | None = None + ) -> typing.AsyncIterator[str]: + """ + A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + decoder = TextDecoder(encoding=self.encoding or "utf-8") + chunker = TextChunker(chunk_size=chunk_size) + with request_context(request=self._request): + async for byte_content in self.aiter_bytes(): + text_content = decoder.decode(byte_content) + for chunk in chunker.decode(text_content): + yield chunk + text_content = decoder.flush() + for chunk in chunker.decode(text_content): + yield chunk # pragma: no cover + for chunk in chunker.flush(): + yield chunk + + async def aiter_lines(self) -> typing.AsyncIterator[str]: + decoder = LineDecoder() + with request_context(request=self._request): + async for text in self.aiter_text(): + for line in decoder.decode(text): + yield line + for line in decoder.flush(): + yield line + + async def aiter_raw( + self, chunk_size: int | None = None + ) -> typing.AsyncIterator[bytes]: + """ + A byte-iterator over the raw response content. + """ + if self.is_stream_consumed: + raise StreamConsumed() + if self.is_closed: + raise StreamClosed() + if not isinstance(self.stream, AsyncByteStream): + raise RuntimeError("Attempted to call an async iterator on an sync stream.") + + self.is_stream_consumed = True + self._num_bytes_downloaded = 0 + chunker = ByteChunker(chunk_size=chunk_size) + + with request_context(request=self._request): + async for raw_stream_bytes in self.stream: + self._num_bytes_downloaded += len(raw_stream_bytes) + for chunk in chunker.decode(raw_stream_bytes): + yield chunk + + for chunk in chunker.flush(): + yield chunk + + await self.aclose() + + async def aclose(self) -> None: + """ + Close the response and release the connection. + Automatically called if the response body is read to completion. + """ + if not isinstance(self.stream, AsyncByteStream): + raise RuntimeError("Attempted to call an async close on an sync stream.") + + if not self.is_closed: + self.is_closed = True + with request_context(request=self._request): + await self.stream.aclose() + + +class Cookies(typing.MutableMapping[str, str]): + """ + HTTP Cookies, as a mutable mapping. + """ + + def __init__(self, cookies: CookieTypes | None = None) -> None: + if cookies is None or isinstance(cookies, dict): + self.jar = CookieJar() + if isinstance(cookies, dict): + for key, value in cookies.items(): + self.set(key, value) + elif isinstance(cookies, list): + self.jar = CookieJar() + for key, value in cookies: + self.set(key, value) + elif isinstance(cookies, Cookies): + self.jar = CookieJar() + for cookie in cookies.jar: + self.jar.set_cookie(cookie) + else: + self.jar = cookies + + def extract_cookies(self, response: Response) -> None: + """ + Loads any cookies based on the response `Set-Cookie` headers. + """ + urllib_response = self._CookieCompatResponse(response) + urllib_request = self._CookieCompatRequest(response.request) + + self.jar.extract_cookies(urllib_response, urllib_request) # type: ignore + + def set_cookie_header(self, request: Request) -> None: + """ + Sets an appropriate 'Cookie:' HTTP header on the `Request`. + """ + urllib_request = self._CookieCompatRequest(request) + self.jar.add_cookie_header(urllib_request) + + def set(self, name: str, value: str, domain: str = "", path: str = "/") -> None: + """ + Set a cookie value by name. May optionally include domain and path. + """ + kwargs = { + "version": 0, + "name": name, + "value": value, + "port": None, + "port_specified": False, + "domain": domain, + "domain_specified": bool(domain), + "domain_initial_dot": domain.startswith("."), + "path": path, + "path_specified": bool(path), + "secure": False, + "expires": None, + "discard": True, + "comment": None, + "comment_url": None, + "rest": {"HttpOnly": None}, + "rfc2109": False, + } + cookie = Cookie(**kwargs) # type: ignore + self.jar.set_cookie(cookie) + + def get( # type: ignore + self, + name: str, + default: str | None = None, + domain: str | None = None, + path: str | None = None, + ) -> str | None: + """ + Get a cookie by name. May optionally include domain and path + in order to specify exactly which cookie to retrieve. + """ + value = None + for cookie in self.jar: + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + if value is not None: + message = f"Multiple cookies exist with name={name}" + raise CookieConflict(message) + value = cookie.value + + if value is None: + return default + return value + + def delete( + self, + name: str, + domain: str | None = None, + path: str | None = None, + ) -> None: + """ + Delete a cookie by name. May optionally include domain and path + in order to specify exactly which cookie to delete. + """ + if domain is not None and path is not None: + return self.jar.clear(domain, path, name) + + remove = [ + cookie + for cookie in self.jar + if cookie.name == name + and (domain is None or cookie.domain == domain) + and (path is None or cookie.path == path) + ] + + for cookie in remove: + self.jar.clear(cookie.domain, cookie.path, cookie.name) + + def clear(self, domain: str | None = None, path: str | None = None) -> None: + """ + Delete all cookies. Optionally include a domain and path in + order to only delete a subset of all the cookies. + """ + args = [] + if domain is not None: + args.append(domain) + if path is not None: + assert domain is not None + args.append(path) + self.jar.clear(*args) + + def update(self, cookies: CookieTypes | None = None) -> None: # type: ignore + cookies = Cookies(cookies) + for cookie in cookies.jar: + self.jar.set_cookie(cookie) + + def __setitem__(self, name: str, value: str) -> None: + return self.set(name, value) + + def __getitem__(self, name: str) -> str: + value = self.get(name) + if value is None: + raise KeyError(name) + return value + + def __delitem__(self, name: str) -> None: + return self.delete(name) + + def __len__(self) -> int: + return len(self.jar) + + def __iter__(self) -> typing.Iterator[str]: + return (cookie.name for cookie in self.jar) + + def __bool__(self) -> bool: + for _ in self.jar: + return True + return False + + def __repr__(self) -> str: + cookies_repr = ", ".join( + [ + f"" + for cookie in self.jar + ] + ) + + return f"" + + class _CookieCompatRequest(urllib.request.Request): + """ + Wraps a `Request` instance up in a compatibility interface suitable + for use with `CookieJar` operations. + """ + + def __init__(self, request: Request) -> None: + super().__init__( + url=str(request.url), + headers=dict(request.headers), + method=request.method, + ) + self.request = request + + def add_unredirected_header(self, key: str, value: str) -> None: + super().add_unredirected_header(key, value) + self.request.headers[key] = value + + class _CookieCompatResponse: + """ + Wraps a `Request` instance up in a compatibility interface suitable + for use with `CookieJar` operations. + """ + + def __init__(self, response: Response) -> None: + self.response = response + + def info(self) -> email.message.Message: + info = email.message.Message() + for key, value in self.response.headers.multi_items(): + # Note that setting `info[key]` here is an "append" operation, + # not a "replace" operation. + # https://docs.python.org/3/library/email.compat32-message.html#email.message.Message.__setitem__ + info[key] = value + return info diff --git a/venv/lib/python3.12/site-packages/httpx/_multipart.py b/venv/lib/python3.12/site-packages/httpx/_multipart.py new file mode 100644 index 0000000..b4761af --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_multipart.py @@ -0,0 +1,300 @@ +from __future__ import annotations + +import io +import mimetypes +import os +import re +import typing +from pathlib import Path + +from ._types import ( + AsyncByteStream, + FileContent, + FileTypes, + RequestData, + RequestFiles, + SyncByteStream, +) +from ._utils import ( + peek_filelike_length, + primitive_value_to_str, + to_bytes, +) + +_HTML5_FORM_ENCODING_REPLACEMENTS = {'"': "%22", "\\": "\\\\"} +_HTML5_FORM_ENCODING_REPLACEMENTS.update( + {chr(c): "%{:02X}".format(c) for c in range(0x1F + 1) if c != 0x1B} +) +_HTML5_FORM_ENCODING_RE = re.compile( + r"|".join([re.escape(c) for c in _HTML5_FORM_ENCODING_REPLACEMENTS.keys()]) +) + + +def _format_form_param(name: str, value: str) -> bytes: + """ + Encode a name/value pair within a multipart form. + """ + + def replacer(match: typing.Match[str]) -> str: + return _HTML5_FORM_ENCODING_REPLACEMENTS[match.group(0)] + + value = _HTML5_FORM_ENCODING_RE.sub(replacer, value) + return f'{name}="{value}"'.encode() + + +def _guess_content_type(filename: str | None) -> str | None: + """ + Guesses the mimetype based on a filename. Defaults to `application/octet-stream`. + + Returns `None` if `filename` is `None` or empty. + """ + if filename: + return mimetypes.guess_type(filename)[0] or "application/octet-stream" + return None + + +def get_multipart_boundary_from_content_type( + content_type: bytes | None, +) -> bytes | None: + if not content_type or not content_type.startswith(b"multipart/form-data"): + return None + # parse boundary according to + # https://www.rfc-editor.org/rfc/rfc2046#section-5.1.1 + if b";" in content_type: + for section in content_type.split(b";"): + if section.strip().lower().startswith(b"boundary="): + return section.strip()[len(b"boundary=") :].strip(b'"') + return None + + +class DataField: + """ + A single form field item, within a multipart form field. + """ + + def __init__(self, name: str, value: str | bytes | int | float | None) -> None: + if not isinstance(name, str): + raise TypeError( + f"Invalid type for name. Expected str, got {type(name)}: {name!r}" + ) + if value is not None and not isinstance(value, (str, bytes, int, float)): + raise TypeError( + "Invalid type for value. Expected primitive type," + f" got {type(value)}: {value!r}" + ) + self.name = name + self.value: str | bytes = ( + value if isinstance(value, bytes) else primitive_value_to_str(value) + ) + + def render_headers(self) -> bytes: + if not hasattr(self, "_headers"): + name = _format_form_param("name", self.name) + self._headers = b"".join( + [b"Content-Disposition: form-data; ", name, b"\r\n\r\n"] + ) + + return self._headers + + def render_data(self) -> bytes: + if not hasattr(self, "_data"): + self._data = to_bytes(self.value) + + return self._data + + def get_length(self) -> int: + headers = self.render_headers() + data = self.render_data() + return len(headers) + len(data) + + def render(self) -> typing.Iterator[bytes]: + yield self.render_headers() + yield self.render_data() + + +class FileField: + """ + A single file field item, within a multipart form field. + """ + + CHUNK_SIZE = 64 * 1024 + + def __init__(self, name: str, value: FileTypes) -> None: + self.name = name + + fileobj: FileContent + + headers: dict[str, str] = {} + content_type: str | None = None + + # This large tuple based API largely mirror's requests' API + # It would be good to think of better APIs for this that we could + # include in httpx 2.0 since variable length tuples(especially of 4 elements) + # are quite unwieldly + if isinstance(value, tuple): + if len(value) == 2: + # neither the 3rd parameter (content_type) nor the 4th (headers) + # was included + filename, fileobj = value + elif len(value) == 3: + filename, fileobj, content_type = value + else: + # all 4 parameters included + filename, fileobj, content_type, headers = value # type: ignore + else: + filename = Path(str(getattr(value, "name", "upload"))).name + fileobj = value + + if content_type is None: + content_type = _guess_content_type(filename) + + has_content_type_header = any("content-type" in key.lower() for key in headers) + if content_type is not None and not has_content_type_header: + # note that unlike requests, we ignore the content_type provided in the 3rd + # tuple element if it is also included in the headers requests does + # the opposite (it overwrites the headerwith the 3rd tuple element) + headers["Content-Type"] = content_type + + if isinstance(fileobj, io.StringIO): + raise TypeError( + "Multipart file uploads require 'io.BytesIO', not 'io.StringIO'." + ) + if isinstance(fileobj, io.TextIOBase): + raise TypeError( + "Multipart file uploads must be opened in binary mode, not text mode." + ) + + self.filename = filename + self.file = fileobj + self.headers = headers + + def get_length(self) -> int | None: + headers = self.render_headers() + + if isinstance(self.file, (str, bytes)): + return len(headers) + len(to_bytes(self.file)) + + file_length = peek_filelike_length(self.file) + + # If we can't determine the filesize without reading it into memory, + # then return `None` here, to indicate an unknown file length. + if file_length is None: + return None + + return len(headers) + file_length + + def render_headers(self) -> bytes: + if not hasattr(self, "_headers"): + parts = [ + b"Content-Disposition: form-data; ", + _format_form_param("name", self.name), + ] + if self.filename: + filename = _format_form_param("filename", self.filename) + parts.extend([b"; ", filename]) + for header_name, header_value in self.headers.items(): + key, val = f"\r\n{header_name}: ".encode(), header_value.encode() + parts.extend([key, val]) + parts.append(b"\r\n\r\n") + self._headers = b"".join(parts) + + return self._headers + + def render_data(self) -> typing.Iterator[bytes]: + if isinstance(self.file, (str, bytes)): + yield to_bytes(self.file) + return + + if hasattr(self.file, "seek"): + try: + self.file.seek(0) + except io.UnsupportedOperation: + pass + + chunk = self.file.read(self.CHUNK_SIZE) + while chunk: + yield to_bytes(chunk) + chunk = self.file.read(self.CHUNK_SIZE) + + def render(self) -> typing.Iterator[bytes]: + yield self.render_headers() + yield from self.render_data() + + +class MultipartStream(SyncByteStream, AsyncByteStream): + """ + Request content as streaming multipart encoded form data. + """ + + def __init__( + self, + data: RequestData, + files: RequestFiles, + boundary: bytes | None = None, + ) -> None: + if boundary is None: + boundary = os.urandom(16).hex().encode("ascii") + + self.boundary = boundary + self.content_type = "multipart/form-data; boundary=%s" % boundary.decode( + "ascii" + ) + self.fields = list(self._iter_fields(data, files)) + + def _iter_fields( + self, data: RequestData, files: RequestFiles + ) -> typing.Iterator[FileField | DataField]: + for name, value in data.items(): + if isinstance(value, (tuple, list)): + for item in value: + yield DataField(name=name, value=item) + else: + yield DataField(name=name, value=value) + + file_items = files.items() if isinstance(files, typing.Mapping) else files + for name, value in file_items: + yield FileField(name=name, value=value) + + def iter_chunks(self) -> typing.Iterator[bytes]: + for field in self.fields: + yield b"--%s\r\n" % self.boundary + yield from field.render() + yield b"\r\n" + yield b"--%s--\r\n" % self.boundary + + def get_content_length(self) -> int | None: + """ + Return the length of the multipart encoded content, or `None` if + any of the files have a length that cannot be determined upfront. + """ + boundary_length = len(self.boundary) + length = 0 + + for field in self.fields: + field_length = field.get_length() + if field_length is None: + return None + + length += 2 + boundary_length + 2 # b"--{boundary}\r\n" + length += field_length + length += 2 # b"\r\n" + + length += 2 + boundary_length + 4 # b"--{boundary}--\r\n" + return length + + # Content stream interface. + + def get_headers(self) -> dict[str, str]: + content_length = self.get_content_length() + content_type = self.content_type + if content_length is None: + return {"Transfer-Encoding": "chunked", "Content-Type": content_type} + return {"Content-Length": str(content_length), "Content-Type": content_type} + + def __iter__(self) -> typing.Iterator[bytes]: + for chunk in self.iter_chunks(): + yield chunk + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + for chunk in self.iter_chunks(): + yield chunk diff --git a/venv/lib/python3.12/site-packages/httpx/_status_codes.py b/venv/lib/python3.12/site-packages/httpx/_status_codes.py new file mode 100644 index 0000000..133a623 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_status_codes.py @@ -0,0 +1,162 @@ +from __future__ import annotations + +from enum import IntEnum + +__all__ = ["codes"] + + +class codes(IntEnum): + """HTTP status codes and reason phrases + + Status codes from the following RFCs are all observed: + + * RFC 7231: Hypertext Transfer Protocol (HTTP/1.1), obsoletes 2616 + * RFC 6585: Additional HTTP Status Codes + * RFC 3229: Delta encoding in HTTP + * RFC 4918: HTTP Extensions for WebDAV, obsoletes 2518 + * RFC 5842: Binding Extensions to WebDAV + * RFC 7238: Permanent Redirect + * RFC 2295: Transparent Content Negotiation in HTTP + * RFC 2774: An HTTP Extension Framework + * RFC 7540: Hypertext Transfer Protocol Version 2 (HTTP/2) + * RFC 2324: Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0) + * RFC 7725: An HTTP Status Code to Report Legal Obstacles + * RFC 8297: An HTTP Status Code for Indicating Hints + * RFC 8470: Using Early Data in HTTP + """ + + def __new__(cls, value: int, phrase: str = "") -> codes: + obj = int.__new__(cls, value) + obj._value_ = value + + obj.phrase = phrase # type: ignore[attr-defined] + return obj + + def __str__(self) -> str: + return str(self.value) + + @classmethod + def get_reason_phrase(cls, value: int) -> str: + try: + return codes(value).phrase # type: ignore + except ValueError: + return "" + + @classmethod + def is_informational(cls, value: int) -> bool: + """ + Returns `True` for 1xx status codes, `False` otherwise. + """ + return 100 <= value <= 199 + + @classmethod + def is_success(cls, value: int) -> bool: + """ + Returns `True` for 2xx status codes, `False` otherwise. + """ + return 200 <= value <= 299 + + @classmethod + def is_redirect(cls, value: int) -> bool: + """ + Returns `True` for 3xx status codes, `False` otherwise. + """ + return 300 <= value <= 399 + + @classmethod + def is_client_error(cls, value: int) -> bool: + """ + Returns `True` for 4xx status codes, `False` otherwise. + """ + return 400 <= value <= 499 + + @classmethod + def is_server_error(cls, value: int) -> bool: + """ + Returns `True` for 5xx status codes, `False` otherwise. + """ + return 500 <= value <= 599 + + @classmethod + def is_error(cls, value: int) -> bool: + """ + Returns `True` for 4xx or 5xx status codes, `False` otherwise. + """ + return 400 <= value <= 599 + + # informational + CONTINUE = 100, "Continue" + SWITCHING_PROTOCOLS = 101, "Switching Protocols" + PROCESSING = 102, "Processing" + EARLY_HINTS = 103, "Early Hints" + + # success + OK = 200, "OK" + CREATED = 201, "Created" + ACCEPTED = 202, "Accepted" + NON_AUTHORITATIVE_INFORMATION = 203, "Non-Authoritative Information" + NO_CONTENT = 204, "No Content" + RESET_CONTENT = 205, "Reset Content" + PARTIAL_CONTENT = 206, "Partial Content" + MULTI_STATUS = 207, "Multi-Status" + ALREADY_REPORTED = 208, "Already Reported" + IM_USED = 226, "IM Used" + + # redirection + MULTIPLE_CHOICES = 300, "Multiple Choices" + MOVED_PERMANENTLY = 301, "Moved Permanently" + FOUND = 302, "Found" + SEE_OTHER = 303, "See Other" + NOT_MODIFIED = 304, "Not Modified" + USE_PROXY = 305, "Use Proxy" + TEMPORARY_REDIRECT = 307, "Temporary Redirect" + PERMANENT_REDIRECT = 308, "Permanent Redirect" + + # client error + BAD_REQUEST = 400, "Bad Request" + UNAUTHORIZED = 401, "Unauthorized" + PAYMENT_REQUIRED = 402, "Payment Required" + FORBIDDEN = 403, "Forbidden" + NOT_FOUND = 404, "Not Found" + METHOD_NOT_ALLOWED = 405, "Method Not Allowed" + NOT_ACCEPTABLE = 406, "Not Acceptable" + PROXY_AUTHENTICATION_REQUIRED = 407, "Proxy Authentication Required" + REQUEST_TIMEOUT = 408, "Request Timeout" + CONFLICT = 409, "Conflict" + GONE = 410, "Gone" + LENGTH_REQUIRED = 411, "Length Required" + PRECONDITION_FAILED = 412, "Precondition Failed" + REQUEST_ENTITY_TOO_LARGE = 413, "Request Entity Too Large" + REQUEST_URI_TOO_LONG = 414, "Request-URI Too Long" + UNSUPPORTED_MEDIA_TYPE = 415, "Unsupported Media Type" + REQUESTED_RANGE_NOT_SATISFIABLE = 416, "Requested Range Not Satisfiable" + EXPECTATION_FAILED = 417, "Expectation Failed" + IM_A_TEAPOT = 418, "I'm a teapot" + MISDIRECTED_REQUEST = 421, "Misdirected Request" + UNPROCESSABLE_ENTITY = 422, "Unprocessable Entity" + LOCKED = 423, "Locked" + FAILED_DEPENDENCY = 424, "Failed Dependency" + TOO_EARLY = 425, "Too Early" + UPGRADE_REQUIRED = 426, "Upgrade Required" + PRECONDITION_REQUIRED = 428, "Precondition Required" + TOO_MANY_REQUESTS = 429, "Too Many Requests" + REQUEST_HEADER_FIELDS_TOO_LARGE = 431, "Request Header Fields Too Large" + UNAVAILABLE_FOR_LEGAL_REASONS = 451, "Unavailable For Legal Reasons" + + # server errors + INTERNAL_SERVER_ERROR = 500, "Internal Server Error" + NOT_IMPLEMENTED = 501, "Not Implemented" + BAD_GATEWAY = 502, "Bad Gateway" + SERVICE_UNAVAILABLE = 503, "Service Unavailable" + GATEWAY_TIMEOUT = 504, "Gateway Timeout" + HTTP_VERSION_NOT_SUPPORTED = 505, "HTTP Version Not Supported" + VARIANT_ALSO_NEGOTIATES = 506, "Variant Also Negotiates" + INSUFFICIENT_STORAGE = 507, "Insufficient Storage" + LOOP_DETECTED = 508, "Loop Detected" + NOT_EXTENDED = 510, "Not Extended" + NETWORK_AUTHENTICATION_REQUIRED = 511, "Network Authentication Required" + + +# Include lower-case styles for `requests` compatibility. +for code in codes: + setattr(codes, code._name_.lower(), int(code)) diff --git a/venv/lib/python3.12/site-packages/httpx/_transports/__init__.py b/venv/lib/python3.12/site-packages/httpx/_transports/__init__.py new file mode 100644 index 0000000..7a32105 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_transports/__init__.py @@ -0,0 +1,15 @@ +from .asgi import * +from .base import * +from .default import * +from .mock import * +from .wsgi import * + +__all__ = [ + "ASGITransport", + "AsyncBaseTransport", + "BaseTransport", + "AsyncHTTPTransport", + "HTTPTransport", + "MockTransport", + "WSGITransport", +] diff --git a/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b7aad06 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/asgi.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/asgi.cpython-312.pyc new file mode 100644 index 0000000..d3cefa0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/asgi.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..c8d296f Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/default.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/default.cpython-312.pyc new file mode 100644 index 0000000..71b42dc Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/default.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/mock.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/mock.cpython-312.pyc new file mode 100644 index 0000000..18652c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/mock.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/wsgi.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/wsgi.cpython-312.pyc new file mode 100644 index 0000000..5aa024a Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx/_transports/__pycache__/wsgi.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx/_transports/asgi.py b/venv/lib/python3.12/site-packages/httpx/_transports/asgi.py new file mode 100644 index 0000000..2bc4efa --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_transports/asgi.py @@ -0,0 +1,187 @@ +from __future__ import annotations + +import typing + +from .._models import Request, Response +from .._types import AsyncByteStream +from .base import AsyncBaseTransport + +if typing.TYPE_CHECKING: # pragma: no cover + import asyncio + + import trio + + Event = typing.Union[asyncio.Event, trio.Event] + + +_Message = typing.MutableMapping[str, typing.Any] +_Receive = typing.Callable[[], typing.Awaitable[_Message]] +_Send = typing.Callable[ + [typing.MutableMapping[str, typing.Any]], typing.Awaitable[None] +] +_ASGIApp = typing.Callable[ + [typing.MutableMapping[str, typing.Any], _Receive, _Send], typing.Awaitable[None] +] + +__all__ = ["ASGITransport"] + + +def is_running_trio() -> bool: + try: + # sniffio is a dependency of trio. + + # See https://github.com/python-trio/trio/issues/2802 + import sniffio + + if sniffio.current_async_library() == "trio": + return True + except ImportError: # pragma: nocover + pass + + return False + + +def create_event() -> Event: + if is_running_trio(): + import trio + + return trio.Event() + + import asyncio + + return asyncio.Event() + + +class ASGIResponseStream(AsyncByteStream): + def __init__(self, body: list[bytes]) -> None: + self._body = body + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + yield b"".join(self._body) + + +class ASGITransport(AsyncBaseTransport): + """ + A custom AsyncTransport that handles sending requests directly to an ASGI app. + + ```python + transport = httpx.ASGITransport( + app=app, + root_path="/submount", + client=("1.2.3.4", 123) + ) + client = httpx.AsyncClient(transport=transport) + ``` + + Arguments: + + * `app` - The ASGI application. + * `raise_app_exceptions` - Boolean indicating if exceptions in the application + should be raised. Default to `True`. Can be set to `False` for use cases + such as testing the content of a client 500 response. + * `root_path` - The root path on which the ASGI application should be mounted. + * `client` - A two-tuple indicating the client IP and port of incoming requests. + ``` + """ + + def __init__( + self, + app: _ASGIApp, + raise_app_exceptions: bool = True, + root_path: str = "", + client: tuple[str, int] = ("127.0.0.1", 123), + ) -> None: + self.app = app + self.raise_app_exceptions = raise_app_exceptions + self.root_path = root_path + self.client = client + + async def handle_async_request( + self, + request: Request, + ) -> Response: + assert isinstance(request.stream, AsyncByteStream) + + # ASGI scope. + scope = { + "type": "http", + "asgi": {"version": "3.0"}, + "http_version": "1.1", + "method": request.method, + "headers": [(k.lower(), v) for (k, v) in request.headers.raw], + "scheme": request.url.scheme, + "path": request.url.path, + "raw_path": request.url.raw_path.split(b"?")[0], + "query_string": request.url.query, + "server": (request.url.host, request.url.port), + "client": self.client, + "root_path": self.root_path, + } + + # Request. + request_body_chunks = request.stream.__aiter__() + request_complete = False + + # Response. + status_code = None + response_headers = None + body_parts = [] + response_started = False + response_complete = create_event() + + # ASGI callables. + + async def receive() -> dict[str, typing.Any]: + nonlocal request_complete + + if request_complete: + await response_complete.wait() + return {"type": "http.disconnect"} + + try: + body = await request_body_chunks.__anext__() + except StopAsyncIteration: + request_complete = True + return {"type": "http.request", "body": b"", "more_body": False} + return {"type": "http.request", "body": body, "more_body": True} + + async def send(message: typing.MutableMapping[str, typing.Any]) -> None: + nonlocal status_code, response_headers, response_started + + if message["type"] == "http.response.start": + assert not response_started + + status_code = message["status"] + response_headers = message.get("headers", []) + response_started = True + + elif message["type"] == "http.response.body": + assert not response_complete.is_set() + body = message.get("body", b"") + more_body = message.get("more_body", False) + + if body and request.method != "HEAD": + body_parts.append(body) + + if not more_body: + response_complete.set() + + try: + await self.app(scope, receive, send) + except Exception: # noqa: PIE-786 + if self.raise_app_exceptions: + raise + + response_complete.set() + if status_code is None: + status_code = 500 + if response_headers is None: + response_headers = {} + + assert response_complete.is_set() + assert status_code is not None + assert response_headers is not None + + stream = ASGIResponseStream(body_parts) + + return Response(status_code, headers=response_headers, stream=stream) diff --git a/venv/lib/python3.12/site-packages/httpx/_transports/base.py b/venv/lib/python3.12/site-packages/httpx/_transports/base.py new file mode 100644 index 0000000..66fd99d --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_transports/base.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import typing +from types import TracebackType + +from .._models import Request, Response + +T = typing.TypeVar("T", bound="BaseTransport") +A = typing.TypeVar("A", bound="AsyncBaseTransport") + +__all__ = ["AsyncBaseTransport", "BaseTransport"] + + +class BaseTransport: + def __enter__(self: T) -> T: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + self.close() + + def handle_request(self, request: Request) -> Response: + """ + Send a single HTTP request and return a response. + + Developers shouldn't typically ever need to call into this API directly, + since the Client class provides all the higher level user-facing API + niceties. + + In order to properly release any network resources, the response + stream should *either* be consumed immediately, with a call to + `response.stream.read()`, or else the `handle_request` call should + be followed with a try/finally block to ensuring the stream is + always closed. + + Example usage: + + with httpx.HTTPTransport() as transport: + req = httpx.Request( + method=b"GET", + url=(b"https", b"www.example.com", 443, b"/"), + headers=[(b"Host", b"www.example.com")], + ) + resp = transport.handle_request(req) + body = resp.stream.read() + print(resp.status_code, resp.headers, body) + + + Takes a `Request` instance as the only argument. + + Returns a `Response` instance. + """ + raise NotImplementedError( + "The 'handle_request' method must be implemented." + ) # pragma: no cover + + def close(self) -> None: + pass + + +class AsyncBaseTransport: + async def __aenter__(self: A) -> A: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + await self.aclose() + + async def handle_async_request( + self, + request: Request, + ) -> Response: + raise NotImplementedError( + "The 'handle_async_request' method must be implemented." + ) # pragma: no cover + + async def aclose(self) -> None: + pass diff --git a/venv/lib/python3.12/site-packages/httpx/_transports/default.py b/venv/lib/python3.12/site-packages/httpx/_transports/default.py new file mode 100644 index 0000000..d5aa05f --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_transports/default.py @@ -0,0 +1,406 @@ +""" +Custom transports, with nicely configured defaults. + +The following additional keyword arguments are currently supported by httpcore... + +* uds: str +* local_address: str +* retries: int + +Example usages... + +# Disable HTTP/2 on a single specific domain. +mounts = { + "all://": httpx.HTTPTransport(http2=True), + "all://*example.org": httpx.HTTPTransport() +} + +# Using advanced httpcore configuration, with connection retries. +transport = httpx.HTTPTransport(retries=1) +client = httpx.Client(transport=transport) + +# Using advanced httpcore configuration, with unix domain sockets. +transport = httpx.HTTPTransport(uds="socket.uds") +client = httpx.Client(transport=transport) +""" + +from __future__ import annotations + +import contextlib +import typing +from types import TracebackType + +if typing.TYPE_CHECKING: + import ssl # pragma: no cover + + import httpx # pragma: no cover + +from .._config import DEFAULT_LIMITS, Limits, Proxy, create_ssl_context +from .._exceptions import ( + ConnectError, + ConnectTimeout, + LocalProtocolError, + NetworkError, + PoolTimeout, + ProtocolError, + ProxyError, + ReadError, + ReadTimeout, + RemoteProtocolError, + TimeoutException, + UnsupportedProtocol, + WriteError, + WriteTimeout, +) +from .._models import Request, Response +from .._types import AsyncByteStream, CertTypes, ProxyTypes, SyncByteStream +from .._urls import URL +from .base import AsyncBaseTransport, BaseTransport + +T = typing.TypeVar("T", bound="HTTPTransport") +A = typing.TypeVar("A", bound="AsyncHTTPTransport") + +SOCKET_OPTION = typing.Union[ + typing.Tuple[int, int, int], + typing.Tuple[int, int, typing.Union[bytes, bytearray]], + typing.Tuple[int, int, None, int], +] + +__all__ = ["AsyncHTTPTransport", "HTTPTransport"] + +HTTPCORE_EXC_MAP: dict[type[Exception], type[httpx.HTTPError]] = {} + + +def _load_httpcore_exceptions() -> dict[type[Exception], type[httpx.HTTPError]]: + import httpcore + + return { + httpcore.TimeoutException: TimeoutException, + httpcore.ConnectTimeout: ConnectTimeout, + httpcore.ReadTimeout: ReadTimeout, + httpcore.WriteTimeout: WriteTimeout, + httpcore.PoolTimeout: PoolTimeout, + httpcore.NetworkError: NetworkError, + httpcore.ConnectError: ConnectError, + httpcore.ReadError: ReadError, + httpcore.WriteError: WriteError, + httpcore.ProxyError: ProxyError, + httpcore.UnsupportedProtocol: UnsupportedProtocol, + httpcore.ProtocolError: ProtocolError, + httpcore.LocalProtocolError: LocalProtocolError, + httpcore.RemoteProtocolError: RemoteProtocolError, + } + + +@contextlib.contextmanager +def map_httpcore_exceptions() -> typing.Iterator[None]: + global HTTPCORE_EXC_MAP + if len(HTTPCORE_EXC_MAP) == 0: + HTTPCORE_EXC_MAP = _load_httpcore_exceptions() + try: + yield + except Exception as exc: + mapped_exc = None + + for from_exc, to_exc in HTTPCORE_EXC_MAP.items(): + if not isinstance(exc, from_exc): + continue + # We want to map to the most specific exception we can find. + # Eg if `exc` is an `httpcore.ReadTimeout`, we want to map to + # `httpx.ReadTimeout`, not just `httpx.TimeoutException`. + if mapped_exc is None or issubclass(to_exc, mapped_exc): + mapped_exc = to_exc + + if mapped_exc is None: # pragma: no cover + raise + + message = str(exc) + raise mapped_exc(message) from exc + + +class ResponseStream(SyncByteStream): + def __init__(self, httpcore_stream: typing.Iterable[bytes]) -> None: + self._httpcore_stream = httpcore_stream + + def __iter__(self) -> typing.Iterator[bytes]: + with map_httpcore_exceptions(): + for part in self._httpcore_stream: + yield part + + def close(self) -> None: + if hasattr(self._httpcore_stream, "close"): + self._httpcore_stream.close() + + +class HTTPTransport(BaseTransport): + def __init__( + self, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + proxy: ProxyTypes | None = None, + uds: str | None = None, + local_address: str | None = None, + retries: int = 0, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + import httpcore + + proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy + ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env) + + if proxy is None: + self._pool = httpcore.ConnectionPool( + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + uds=uds, + local_address=local_address, + retries=retries, + socket_options=socket_options, + ) + elif proxy.url.scheme in ("http", "https"): + self._pool = httpcore.HTTPProxy( + proxy_url=httpcore.URL( + scheme=proxy.url.raw_scheme, + host=proxy.url.raw_host, + port=proxy.url.port, + target=proxy.url.raw_path, + ), + proxy_auth=proxy.raw_auth, + proxy_headers=proxy.headers.raw, + ssl_context=ssl_context, + proxy_ssl_context=proxy.ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + socket_options=socket_options, + ) + elif proxy.url.scheme in ("socks5", "socks5h"): + try: + import socksio # noqa + except ImportError: # pragma: no cover + raise ImportError( + "Using SOCKS proxy, but the 'socksio' package is not installed. " + "Make sure to install httpx using `pip install httpx[socks]`." + ) from None + + self._pool = httpcore.SOCKSProxy( + proxy_url=httpcore.URL( + scheme=proxy.url.raw_scheme, + host=proxy.url.raw_host, + port=proxy.url.port, + target=proxy.url.raw_path, + ), + proxy_auth=proxy.raw_auth, + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + ) + else: # pragma: no cover + raise ValueError( + "Proxy protocol must be either 'http', 'https', 'socks5', or 'socks5h'," + f" but got {proxy.url.scheme!r}." + ) + + def __enter__(self: T) -> T: # Use generics for subclass support. + self._pool.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + with map_httpcore_exceptions(): + self._pool.__exit__(exc_type, exc_value, traceback) + + def handle_request( + self, + request: Request, + ) -> Response: + assert isinstance(request.stream, SyncByteStream) + import httpcore + + req = httpcore.Request( + method=request.method, + url=httpcore.URL( + scheme=request.url.raw_scheme, + host=request.url.raw_host, + port=request.url.port, + target=request.url.raw_path, + ), + headers=request.headers.raw, + content=request.stream, + extensions=request.extensions, + ) + with map_httpcore_exceptions(): + resp = self._pool.handle_request(req) + + assert isinstance(resp.stream, typing.Iterable) + + return Response( + status_code=resp.status, + headers=resp.headers, + stream=ResponseStream(resp.stream), + extensions=resp.extensions, + ) + + def close(self) -> None: + self._pool.close() + + +class AsyncResponseStream(AsyncByteStream): + def __init__(self, httpcore_stream: typing.AsyncIterable[bytes]) -> None: + self._httpcore_stream = httpcore_stream + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + with map_httpcore_exceptions(): + async for part in self._httpcore_stream: + yield part + + async def aclose(self) -> None: + if hasattr(self._httpcore_stream, "aclose"): + await self._httpcore_stream.aclose() + + +class AsyncHTTPTransport(AsyncBaseTransport): + def __init__( + self, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + proxy: ProxyTypes | None = None, + uds: str | None = None, + local_address: str | None = None, + retries: int = 0, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + import httpcore + + proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy + ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env) + + if proxy is None: + self._pool = httpcore.AsyncConnectionPool( + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + uds=uds, + local_address=local_address, + retries=retries, + socket_options=socket_options, + ) + elif proxy.url.scheme in ("http", "https"): + self._pool = httpcore.AsyncHTTPProxy( + proxy_url=httpcore.URL( + scheme=proxy.url.raw_scheme, + host=proxy.url.raw_host, + port=proxy.url.port, + target=proxy.url.raw_path, + ), + proxy_auth=proxy.raw_auth, + proxy_headers=proxy.headers.raw, + proxy_ssl_context=proxy.ssl_context, + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + socket_options=socket_options, + ) + elif proxy.url.scheme in ("socks5", "socks5h"): + try: + import socksio # noqa + except ImportError: # pragma: no cover + raise ImportError( + "Using SOCKS proxy, but the 'socksio' package is not installed. " + "Make sure to install httpx using `pip install httpx[socks]`." + ) from None + + self._pool = httpcore.AsyncSOCKSProxy( + proxy_url=httpcore.URL( + scheme=proxy.url.raw_scheme, + host=proxy.url.raw_host, + port=proxy.url.port, + target=proxy.url.raw_path, + ), + proxy_auth=proxy.raw_auth, + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + ) + else: # pragma: no cover + raise ValueError( + "Proxy protocol must be either 'http', 'https', 'socks5', or 'socks5h'," + " but got {proxy.url.scheme!r}." + ) + + async def __aenter__(self: A) -> A: # Use generics for subclass support. + await self._pool.__aenter__() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + with map_httpcore_exceptions(): + await self._pool.__aexit__(exc_type, exc_value, traceback) + + async def handle_async_request( + self, + request: Request, + ) -> Response: + assert isinstance(request.stream, AsyncByteStream) + import httpcore + + req = httpcore.Request( + method=request.method, + url=httpcore.URL( + scheme=request.url.raw_scheme, + host=request.url.raw_host, + port=request.url.port, + target=request.url.raw_path, + ), + headers=request.headers.raw, + content=request.stream, + extensions=request.extensions, + ) + with map_httpcore_exceptions(): + resp = await self._pool.handle_async_request(req) + + assert isinstance(resp.stream, typing.AsyncIterable) + + return Response( + status_code=resp.status, + headers=resp.headers, + stream=AsyncResponseStream(resp.stream), + extensions=resp.extensions, + ) + + async def aclose(self) -> None: + await self._pool.aclose() diff --git a/venv/lib/python3.12/site-packages/httpx/_transports/mock.py b/venv/lib/python3.12/site-packages/httpx/_transports/mock.py new file mode 100644 index 0000000..8c418f5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_transports/mock.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +import typing + +from .._models import Request, Response +from .base import AsyncBaseTransport, BaseTransport + +SyncHandler = typing.Callable[[Request], Response] +AsyncHandler = typing.Callable[[Request], typing.Coroutine[None, None, Response]] + + +__all__ = ["MockTransport"] + + +class MockTransport(AsyncBaseTransport, BaseTransport): + def __init__(self, handler: SyncHandler | AsyncHandler) -> None: + self.handler = handler + + def handle_request( + self, + request: Request, + ) -> Response: + request.read() + response = self.handler(request) + if not isinstance(response, Response): # pragma: no cover + raise TypeError("Cannot use an async handler in a sync Client") + return response + + async def handle_async_request( + self, + request: Request, + ) -> Response: + await request.aread() + response = self.handler(request) + + # Allow handler to *optionally* be an `async` function. + # If it is, then the `response` variable need to be awaited to actually + # return the result. + + if not isinstance(response, Response): + response = await response + + return response diff --git a/venv/lib/python3.12/site-packages/httpx/_transports/wsgi.py b/venv/lib/python3.12/site-packages/httpx/_transports/wsgi.py new file mode 100644 index 0000000..8592ffe --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_transports/wsgi.py @@ -0,0 +1,149 @@ +from __future__ import annotations + +import io +import itertools +import sys +import typing + +from .._models import Request, Response +from .._types import SyncByteStream +from .base import BaseTransport + +if typing.TYPE_CHECKING: + from _typeshed import OptExcInfo # pragma: no cover + from _typeshed.wsgi import WSGIApplication # pragma: no cover + +_T = typing.TypeVar("_T") + + +__all__ = ["WSGITransport"] + + +def _skip_leading_empty_chunks(body: typing.Iterable[_T]) -> typing.Iterable[_T]: + body = iter(body) + for chunk in body: + if chunk: + return itertools.chain([chunk], body) + return [] + + +class WSGIByteStream(SyncByteStream): + def __init__(self, result: typing.Iterable[bytes]) -> None: + self._close = getattr(result, "close", None) + self._result = _skip_leading_empty_chunks(result) + + def __iter__(self) -> typing.Iterator[bytes]: + for part in self._result: + yield part + + def close(self) -> None: + if self._close is not None: + self._close() + + +class WSGITransport(BaseTransport): + """ + A custom transport that handles sending requests directly to an WSGI app. + The simplest way to use this functionality is to use the `app` argument. + + ``` + client = httpx.Client(app=app) + ``` + + Alternatively, you can setup the transport instance explicitly. + This allows you to include any additional configuration arguments specific + to the WSGITransport class: + + ``` + transport = httpx.WSGITransport( + app=app, + script_name="/submount", + remote_addr="1.2.3.4" + ) + client = httpx.Client(transport=transport) + ``` + + Arguments: + + * `app` - The WSGI application. + * `raise_app_exceptions` - Boolean indicating if exceptions in the application + should be raised. Default to `True`. Can be set to `False` for use cases + such as testing the content of a client 500 response. + * `script_name` - The root path on which the WSGI application should be mounted. + * `remote_addr` - A string indicating the client IP of incoming requests. + ``` + """ + + def __init__( + self, + app: WSGIApplication, + raise_app_exceptions: bool = True, + script_name: str = "", + remote_addr: str = "127.0.0.1", + wsgi_errors: typing.TextIO | None = None, + ) -> None: + self.app = app + self.raise_app_exceptions = raise_app_exceptions + self.script_name = script_name + self.remote_addr = remote_addr + self.wsgi_errors = wsgi_errors + + def handle_request(self, request: Request) -> Response: + request.read() + wsgi_input = io.BytesIO(request.content) + + port = request.url.port or {"http": 80, "https": 443}[request.url.scheme] + environ = { + "wsgi.version": (1, 0), + "wsgi.url_scheme": request.url.scheme, + "wsgi.input": wsgi_input, + "wsgi.errors": self.wsgi_errors or sys.stderr, + "wsgi.multithread": True, + "wsgi.multiprocess": False, + "wsgi.run_once": False, + "REQUEST_METHOD": request.method, + "SCRIPT_NAME": self.script_name, + "PATH_INFO": request.url.path, + "QUERY_STRING": request.url.query.decode("ascii"), + "SERVER_NAME": request.url.host, + "SERVER_PORT": str(port), + "SERVER_PROTOCOL": "HTTP/1.1", + "REMOTE_ADDR": self.remote_addr, + } + for header_key, header_value in request.headers.raw: + key = header_key.decode("ascii").upper().replace("-", "_") + if key not in ("CONTENT_TYPE", "CONTENT_LENGTH"): + key = "HTTP_" + key + environ[key] = header_value.decode("ascii") + + seen_status = None + seen_response_headers = None + seen_exc_info = None + + def start_response( + status: str, + response_headers: list[tuple[str, str]], + exc_info: OptExcInfo | None = None, + ) -> typing.Callable[[bytes], typing.Any]: + nonlocal seen_status, seen_response_headers, seen_exc_info + seen_status = status + seen_response_headers = response_headers + seen_exc_info = exc_info + return lambda _: None + + result = self.app(environ, start_response) + + stream = WSGIByteStream(result) + + assert seen_status is not None + assert seen_response_headers is not None + if seen_exc_info and seen_exc_info[0] and self.raise_app_exceptions: + raise seen_exc_info[1] + + status_code = int(seen_status.split()[0]) + headers = [ + (key.encode("ascii"), value.encode("ascii")) + for key, value in seen_response_headers + ] + + return Response(status_code, headers=headers, stream=stream) diff --git a/venv/lib/python3.12/site-packages/httpx/_types.py b/venv/lib/python3.12/site-packages/httpx/_types.py new file mode 100644 index 0000000..704dfdf --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_types.py @@ -0,0 +1,114 @@ +""" +Type definitions for type checking purposes. +""" + +from http.cookiejar import CookieJar +from typing import ( + IO, + TYPE_CHECKING, + Any, + AsyncIterable, + AsyncIterator, + Callable, + Dict, + Iterable, + Iterator, + List, + Mapping, + Optional, + Sequence, + Tuple, + Union, +) + +if TYPE_CHECKING: # pragma: no cover + from ._auth import Auth # noqa: F401 + from ._config import Proxy, Timeout # noqa: F401 + from ._models import Cookies, Headers, Request # noqa: F401 + from ._urls import URL, QueryParams # noqa: F401 + + +PrimitiveData = Optional[Union[str, int, float, bool]] + +URLTypes = Union["URL", str] + +QueryParamTypes = Union[ + "QueryParams", + Mapping[str, Union[PrimitiveData, Sequence[PrimitiveData]]], + List[Tuple[str, PrimitiveData]], + Tuple[Tuple[str, PrimitiveData], ...], + str, + bytes, +] + +HeaderTypes = Union[ + "Headers", + Mapping[str, str], + Mapping[bytes, bytes], + Sequence[Tuple[str, str]], + Sequence[Tuple[bytes, bytes]], +] + +CookieTypes = Union["Cookies", CookieJar, Dict[str, str], List[Tuple[str, str]]] + +TimeoutTypes = Union[ + Optional[float], + Tuple[Optional[float], Optional[float], Optional[float], Optional[float]], + "Timeout", +] +ProxyTypes = Union["URL", str, "Proxy"] +CertTypes = Union[str, Tuple[str, str], Tuple[str, str, str]] + +AuthTypes = Union[ + Tuple[Union[str, bytes], Union[str, bytes]], + Callable[["Request"], "Request"], + "Auth", +] + +RequestContent = Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]] +ResponseContent = Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]] +ResponseExtensions = Mapping[str, Any] + +RequestData = Mapping[str, Any] + +FileContent = Union[IO[bytes], bytes, str] +FileTypes = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], +] +RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] + +RequestExtensions = Mapping[str, Any] + +__all__ = ["AsyncByteStream", "SyncByteStream"] + + +class SyncByteStream: + def __iter__(self) -> Iterator[bytes]: + raise NotImplementedError( + "The '__iter__' method must be implemented." + ) # pragma: no cover + yield b"" # pragma: no cover + + def close(self) -> None: + """ + Subclasses can override this method to release any network resources + after a request/response cycle is complete. + """ + + +class AsyncByteStream: + async def __aiter__(self) -> AsyncIterator[bytes]: + raise NotImplementedError( + "The '__aiter__' method must be implemented." + ) # pragma: no cover + yield b"" # pragma: no cover + + async def aclose(self) -> None: + pass diff --git a/venv/lib/python3.12/site-packages/httpx/_urlparse.py b/venv/lib/python3.12/site-packages/httpx/_urlparse.py new file mode 100644 index 0000000..bf190fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_urlparse.py @@ -0,0 +1,527 @@ +""" +An implementation of `urlparse` that provides URL validation and normalization +as described by RFC3986. + +We rely on this implementation rather than the one in Python's stdlib, because: + +* It provides more complete URL validation. +* It properly differentiates between an empty querystring and an absent querystring, + to distinguish URLs with a trailing '?'. +* It handles scheme, hostname, port, and path normalization. +* It supports IDNA hostnames, normalizing them to their encoded form. +* The API supports passing individual components, as well as the complete URL string. + +Previously we relied on the excellent `rfc3986` package to handle URL parsing and +validation, but this module provides a simpler alternative, with less indirection +required. +""" + +from __future__ import annotations + +import ipaddress +import re +import typing + +import idna + +from ._exceptions import InvalidURL + +MAX_URL_LENGTH = 65536 + +# https://datatracker.ietf.org/doc/html/rfc3986.html#section-2.3 +UNRESERVED_CHARACTERS = ( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~" +) +SUB_DELIMS = "!$&'()*+,;=" + +PERCENT_ENCODED_REGEX = re.compile("%[A-Fa-f0-9]{2}") + +# https://url.spec.whatwg.org/#percent-encoded-bytes + +# The fragment percent-encode set is the C0 control percent-encode set +# and U+0020 SPACE, U+0022 ("), U+003C (<), U+003E (>), and U+0060 (`). +FRAG_SAFE = "".join( + [chr(i) for i in range(0x20, 0x7F) if i not in (0x20, 0x22, 0x3C, 0x3E, 0x60)] +) + +# The query percent-encode set is the C0 control percent-encode set +# and U+0020 SPACE, U+0022 ("), U+0023 (#), U+003C (<), and U+003E (>). +QUERY_SAFE = "".join( + [chr(i) for i in range(0x20, 0x7F) if i not in (0x20, 0x22, 0x23, 0x3C, 0x3E)] +) + +# The path percent-encode set is the query percent-encode set +# and U+003F (?), U+0060 (`), U+007B ({), and U+007D (}). +PATH_SAFE = "".join( + [ + chr(i) + for i in range(0x20, 0x7F) + if i not in (0x20, 0x22, 0x23, 0x3C, 0x3E) + (0x3F, 0x60, 0x7B, 0x7D) + ] +) + +# The userinfo percent-encode set is the path percent-encode set +# and U+002F (/), U+003A (:), U+003B (;), U+003D (=), U+0040 (@), +# U+005B ([) to U+005E (^), inclusive, and U+007C (|). +USERNAME_SAFE = "".join( + [ + chr(i) + for i in range(0x20, 0x7F) + if i + not in (0x20, 0x22, 0x23, 0x3C, 0x3E) + + (0x3F, 0x60, 0x7B, 0x7D) + + (0x2F, 0x3A, 0x3B, 0x3D, 0x40, 0x5B, 0x5C, 0x5D, 0x5E, 0x7C) + ] +) +PASSWORD_SAFE = "".join( + [ + chr(i) + for i in range(0x20, 0x7F) + if i + not in (0x20, 0x22, 0x23, 0x3C, 0x3E) + + (0x3F, 0x60, 0x7B, 0x7D) + + (0x2F, 0x3A, 0x3B, 0x3D, 0x40, 0x5B, 0x5C, 0x5D, 0x5E, 0x7C) + ] +) +# Note... The terminology 'userinfo' percent-encode set in the WHATWG document +# is used for the username and password quoting. For the joint userinfo component +# we remove U+003A (:) from the safe set. +USERINFO_SAFE = "".join( + [ + chr(i) + for i in range(0x20, 0x7F) + if i + not in (0x20, 0x22, 0x23, 0x3C, 0x3E) + + (0x3F, 0x60, 0x7B, 0x7D) + + (0x2F, 0x3B, 0x3D, 0x40, 0x5B, 0x5C, 0x5D, 0x5E, 0x7C) + ] +) + + +# {scheme}: (optional) +# //{authority} (optional) +# {path} +# ?{query} (optional) +# #{fragment} (optional) +URL_REGEX = re.compile( + ( + r"(?:(?P{scheme}):)?" + r"(?://(?P{authority}))?" + r"(?P{path})" + r"(?:\?(?P{query}))?" + r"(?:#(?P{fragment}))?" + ).format( + scheme="([a-zA-Z][a-zA-Z0-9+.-]*)?", + authority="[^/?#]*", + path="[^?#]*", + query="[^#]*", + fragment=".*", + ) +) + +# {userinfo}@ (optional) +# {host} +# :{port} (optional) +AUTHORITY_REGEX = re.compile( + ( + r"(?:(?P{userinfo})@)?" r"(?P{host})" r":?(?P{port})?" + ).format( + userinfo=".*", # Any character sequence. + host="(\\[.*\\]|[^:@]*)", # Either any character sequence excluding ':' or '@', + # or an IPv6 address enclosed within square brackets. + port=".*", # Any character sequence. + ) +) + + +# If we call urlparse with an individual component, then we need to regex +# validate that component individually. +# Note that we're duplicating the same strings as above. Shock! Horror!! +COMPONENT_REGEX = { + "scheme": re.compile("([a-zA-Z][a-zA-Z0-9+.-]*)?"), + "authority": re.compile("[^/?#]*"), + "path": re.compile("[^?#]*"), + "query": re.compile("[^#]*"), + "fragment": re.compile(".*"), + "userinfo": re.compile("[^@]*"), + "host": re.compile("(\\[.*\\]|[^:]*)"), + "port": re.compile(".*"), +} + + +# We use these simple regexs as a first pass before handing off to +# the stdlib 'ipaddress' module for IP address validation. +IPv4_STYLE_HOSTNAME = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$") +IPv6_STYLE_HOSTNAME = re.compile(r"^\[.*\]$") + + +class ParseResult(typing.NamedTuple): + scheme: str + userinfo: str + host: str + port: int | None + path: str + query: str | None + fragment: str | None + + @property + def authority(self) -> str: + return "".join( + [ + f"{self.userinfo}@" if self.userinfo else "", + f"[{self.host}]" if ":" in self.host else self.host, + f":{self.port}" if self.port is not None else "", + ] + ) + + @property + def netloc(self) -> str: + return "".join( + [ + f"[{self.host}]" if ":" in self.host else self.host, + f":{self.port}" if self.port is not None else "", + ] + ) + + def copy_with(self, **kwargs: str | None) -> ParseResult: + if not kwargs: + return self + + defaults = { + "scheme": self.scheme, + "authority": self.authority, + "path": self.path, + "query": self.query, + "fragment": self.fragment, + } + defaults.update(kwargs) + return urlparse("", **defaults) + + def __str__(self) -> str: + authority = self.authority + return "".join( + [ + f"{self.scheme}:" if self.scheme else "", + f"//{authority}" if authority else "", + self.path, + f"?{self.query}" if self.query is not None else "", + f"#{self.fragment}" if self.fragment is not None else "", + ] + ) + + +def urlparse(url: str = "", **kwargs: str | None) -> ParseResult: + # Initial basic checks on allowable URLs. + # --------------------------------------- + + # Hard limit the maximum allowable URL length. + if len(url) > MAX_URL_LENGTH: + raise InvalidURL("URL too long") + + # If a URL includes any ASCII control characters including \t, \r, \n, + # then treat it as invalid. + if any(char.isascii() and not char.isprintable() for char in url): + char = next(char for char in url if char.isascii() and not char.isprintable()) + idx = url.find(char) + error = ( + f"Invalid non-printable ASCII character in URL, {char!r} at position {idx}." + ) + raise InvalidURL(error) + + # Some keyword arguments require special handling. + # ------------------------------------------------ + + # Coerce "port" to a string, if it is provided as an integer. + if "port" in kwargs: + port = kwargs["port"] + kwargs["port"] = str(port) if isinstance(port, int) else port + + # Replace "netloc" with "host and "port". + if "netloc" in kwargs: + netloc = kwargs.pop("netloc") or "" + kwargs["host"], _, kwargs["port"] = netloc.partition(":") + + # Replace "username" and/or "password" with "userinfo". + if "username" in kwargs or "password" in kwargs: + username = quote(kwargs.pop("username", "") or "", safe=USERNAME_SAFE) + password = quote(kwargs.pop("password", "") or "", safe=PASSWORD_SAFE) + kwargs["userinfo"] = f"{username}:{password}" if password else username + + # Replace "raw_path" with "path" and "query". + if "raw_path" in kwargs: + raw_path = kwargs.pop("raw_path") or "" + kwargs["path"], seperator, kwargs["query"] = raw_path.partition("?") + if not seperator: + kwargs["query"] = None + + # Ensure that IPv6 "host" addresses are always escaped with "[...]". + if "host" in kwargs: + host = kwargs.get("host") or "" + if ":" in host and not (host.startswith("[") and host.endswith("]")): + kwargs["host"] = f"[{host}]" + + # If any keyword arguments are provided, ensure they are valid. + # ------------------------------------------------------------- + + for key, value in kwargs.items(): + if value is not None: + if len(value) > MAX_URL_LENGTH: + raise InvalidURL(f"URL component '{key}' too long") + + # If a component includes any ASCII control characters including \t, \r, \n, + # then treat it as invalid. + if any(char.isascii() and not char.isprintable() for char in value): + char = next( + char for char in value if char.isascii() and not char.isprintable() + ) + idx = value.find(char) + error = ( + f"Invalid non-printable ASCII character in URL {key} component, " + f"{char!r} at position {idx}." + ) + raise InvalidURL(error) + + # Ensure that keyword arguments match as a valid regex. + if not COMPONENT_REGEX[key].fullmatch(value): + raise InvalidURL(f"Invalid URL component '{key}'") + + # The URL_REGEX will always match, but may have empty components. + url_match = URL_REGEX.match(url) + assert url_match is not None + url_dict = url_match.groupdict() + + # * 'scheme', 'authority', and 'path' may be empty strings. + # * 'query' may be 'None', indicating no trailing "?" portion. + # Any string including the empty string, indicates a trailing "?". + # * 'fragment' may be 'None', indicating no trailing "#" portion. + # Any string including the empty string, indicates a trailing "#". + scheme = kwargs.get("scheme", url_dict["scheme"]) or "" + authority = kwargs.get("authority", url_dict["authority"]) or "" + path = kwargs.get("path", url_dict["path"]) or "" + query = kwargs.get("query", url_dict["query"]) + frag = kwargs.get("fragment", url_dict["fragment"]) + + # The AUTHORITY_REGEX will always match, but may have empty components. + authority_match = AUTHORITY_REGEX.match(authority) + assert authority_match is not None + authority_dict = authority_match.groupdict() + + # * 'userinfo' and 'host' may be empty strings. + # * 'port' may be 'None'. + userinfo = kwargs.get("userinfo", authority_dict["userinfo"]) or "" + host = kwargs.get("host", authority_dict["host"]) or "" + port = kwargs.get("port", authority_dict["port"]) + + # Normalize and validate each component. + # We end up with a parsed representation of the URL, + # with components that are plain ASCII bytestrings. + parsed_scheme: str = scheme.lower() + parsed_userinfo: str = quote(userinfo, safe=USERINFO_SAFE) + parsed_host: str = encode_host(host) + parsed_port: int | None = normalize_port(port, scheme) + + has_scheme = parsed_scheme != "" + has_authority = ( + parsed_userinfo != "" or parsed_host != "" or parsed_port is not None + ) + validate_path(path, has_scheme=has_scheme, has_authority=has_authority) + if has_scheme or has_authority: + path = normalize_path(path) + + parsed_path: str = quote(path, safe=PATH_SAFE) + parsed_query: str | None = None if query is None else quote(query, safe=QUERY_SAFE) + parsed_frag: str | None = None if frag is None else quote(frag, safe=FRAG_SAFE) + + # The parsed ASCII bytestrings are our canonical form. + # All properties of the URL are derived from these. + return ParseResult( + parsed_scheme, + parsed_userinfo, + parsed_host, + parsed_port, + parsed_path, + parsed_query, + parsed_frag, + ) + + +def encode_host(host: str) -> str: + if not host: + return "" + + elif IPv4_STYLE_HOSTNAME.match(host): + # Validate IPv4 hostnames like #.#.#.# + # + # From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2 + # + # IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + try: + ipaddress.IPv4Address(host) + except ipaddress.AddressValueError: + raise InvalidURL(f"Invalid IPv4 address: {host!r}") + return host + + elif IPv6_STYLE_HOSTNAME.match(host): + # Validate IPv6 hostnames like [...] + # + # From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2 + # + # "A host identified by an Internet Protocol literal address, version 6 + # [RFC3513] or later, is distinguished by enclosing the IP literal + # within square brackets ("[" and "]"). This is the only place where + # square bracket characters are allowed in the URI syntax." + try: + ipaddress.IPv6Address(host[1:-1]) + except ipaddress.AddressValueError: + raise InvalidURL(f"Invalid IPv6 address: {host!r}") + return host[1:-1] + + elif host.isascii(): + # Regular ASCII hostnames + # + # From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2 + # + # reg-name = *( unreserved / pct-encoded / sub-delims ) + WHATWG_SAFE = '"`{}%|\\' + return quote(host.lower(), safe=SUB_DELIMS + WHATWG_SAFE) + + # IDNA hostnames + try: + return idna.encode(host.lower()).decode("ascii") + except idna.IDNAError: + raise InvalidURL(f"Invalid IDNA hostname: {host!r}") + + +def normalize_port(port: str | int | None, scheme: str) -> int | None: + # From https://tools.ietf.org/html/rfc3986#section-3.2.3 + # + # "A scheme may define a default port. For example, the "http" scheme + # defines a default port of "80", corresponding to its reserved TCP + # port number. The type of port designated by the port number (e.g., + # TCP, UDP, SCTP) is defined by the URI scheme. URI producers and + # normalizers should omit the port component and its ":" delimiter if + # port is empty or if its value would be the same as that of the + # scheme's default." + if port is None or port == "": + return None + + try: + port_as_int = int(port) + except ValueError: + raise InvalidURL(f"Invalid port: {port!r}") + + # See https://url.spec.whatwg.org/#url-miscellaneous + default_port = {"ftp": 21, "http": 80, "https": 443, "ws": 80, "wss": 443}.get( + scheme + ) + if port_as_int == default_port: + return None + return port_as_int + + +def validate_path(path: str, has_scheme: bool, has_authority: bool) -> None: + """ + Path validation rules that depend on if the URL contains + a scheme or authority component. + + See https://datatracker.ietf.org/doc/html/rfc3986.html#section-3.3 + """ + if has_authority: + # If a URI contains an authority component, then the path component + # must either be empty or begin with a slash ("/") character." + if path and not path.startswith("/"): + raise InvalidURL("For absolute URLs, path must be empty or begin with '/'") + + if not has_scheme and not has_authority: + # If a URI does not contain an authority component, then the path cannot begin + # with two slash characters ("//"). + if path.startswith("//"): + raise InvalidURL("Relative URLs cannot have a path starting with '//'") + + # In addition, a URI reference (Section 4.1) may be a relative-path reference, + # in which case the first path segment cannot contain a colon (":") character. + if path.startswith(":"): + raise InvalidURL("Relative URLs cannot have a path starting with ':'") + + +def normalize_path(path: str) -> str: + """ + Drop "." and ".." segments from a URL path. + + For example: + + normalize_path("/path/./to/somewhere/..") == "/path/to" + """ + # Fast return when no '.' characters in the path. + if "." not in path: + return path + + components = path.split("/") + + # Fast return when no '.' or '..' components in the path. + if "." not in components and ".." not in components: + return path + + # https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4 + output: list[str] = [] + for component in components: + if component == ".": + pass + elif component == "..": + if output and output != [""]: + output.pop() + else: + output.append(component) + return "/".join(output) + + +def PERCENT(string: str) -> str: + return "".join([f"%{byte:02X}" for byte in string.encode("utf-8")]) + + +def percent_encoded(string: str, safe: str) -> str: + """ + Use percent-encoding to quote a string. + """ + NON_ESCAPED_CHARS = UNRESERVED_CHARACTERS + safe + + # Fast path for strings that don't need escaping. + if not string.rstrip(NON_ESCAPED_CHARS): + return string + + return "".join( + [char if char in NON_ESCAPED_CHARS else PERCENT(char) for char in string] + ) + + +def quote(string: str, safe: str) -> str: + """ + Use percent-encoding to quote a string, omitting existing '%xx' escape sequences. + + See: https://www.rfc-editor.org/rfc/rfc3986#section-2.1 + + * `string`: The string to be percent-escaped. + * `safe`: A string containing characters that may be treated as safe, and do not + need to be escaped. Unreserved characters are always treated as safe. + See: https://www.rfc-editor.org/rfc/rfc3986#section-2.3 + """ + parts = [] + current_position = 0 + for match in re.finditer(PERCENT_ENCODED_REGEX, string): + start_position, end_position = match.start(), match.end() + matched_text = match.group(0) + # Add any text up to the '%xx' escape sequence. + if start_position != current_position: + leading_text = string[current_position:start_position] + parts.append(percent_encoded(leading_text, safe=safe)) + + # Add the '%xx' escape sequence. + parts.append(matched_text) + current_position = end_position + + # Add any text after the final '%xx' escape sequence. + if current_position != len(string): + trailing_text = string[current_position:] + parts.append(percent_encoded(trailing_text, safe=safe)) + + return "".join(parts) diff --git a/venv/lib/python3.12/site-packages/httpx/_urls.py b/venv/lib/python3.12/site-packages/httpx/_urls.py new file mode 100644 index 0000000..147a8fa --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_urls.py @@ -0,0 +1,641 @@ +from __future__ import annotations + +import typing +from urllib.parse import parse_qs, unquote, urlencode + +import idna + +from ._types import QueryParamTypes +from ._urlparse import urlparse +from ._utils import primitive_value_to_str + +__all__ = ["URL", "QueryParams"] + + +class URL: + """ + url = httpx.URL("HTTPS://jo%40email.com:a%20secret@müller.de:1234/pa%20th?search=ab#anchorlink") + + assert url.scheme == "https" + assert url.username == "jo@email.com" + assert url.password == "a secret" + assert url.userinfo == b"jo%40email.com:a%20secret" + assert url.host == "müller.de" + assert url.raw_host == b"xn--mller-kva.de" + assert url.port == 1234 + assert url.netloc == b"xn--mller-kva.de:1234" + assert url.path == "/pa th" + assert url.query == b"?search=ab" + assert url.raw_path == b"/pa%20th?search=ab" + assert url.fragment == "anchorlink" + + The components of a URL are broken down like this: + + https://jo%40email.com:a%20secret@müller.de:1234/pa%20th?search=ab#anchorlink + [scheme] [ username ] [password] [ host ][port][ path ] [ query ] [fragment] + [ userinfo ] [ netloc ][ raw_path ] + + Note that: + + * `url.scheme` is normalized to always be lowercased. + + * `url.host` is normalized to always be lowercased. Internationalized domain + names are represented in unicode, without IDNA encoding applied. For instance: + + url = httpx.URL("http://中国.icom.museum") + assert url.host == "中国.icom.museum" + url = httpx.URL("http://xn--fiqs8s.icom.museum") + assert url.host == "中国.icom.museum" + + * `url.raw_host` is normalized to always be lowercased, and is IDNA encoded. + + url = httpx.URL("http://中国.icom.museum") + assert url.raw_host == b"xn--fiqs8s.icom.museum" + url = httpx.URL("http://xn--fiqs8s.icom.museum") + assert url.raw_host == b"xn--fiqs8s.icom.museum" + + * `url.port` is either None or an integer. URLs that include the default port for + "http", "https", "ws", "wss", and "ftp" schemes have their port + normalized to `None`. + + assert httpx.URL("http://example.com") == httpx.URL("http://example.com:80") + assert httpx.URL("http://example.com").port is None + assert httpx.URL("http://example.com:80").port is None + + * `url.userinfo` is raw bytes, without URL escaping. Usually you'll want to work + with `url.username` and `url.password` instead, which handle the URL escaping. + + * `url.raw_path` is raw bytes of both the path and query, without URL escaping. + This portion is used as the target when constructing HTTP requests. Usually you'll + want to work with `url.path` instead. + + * `url.query` is raw bytes, without URL escaping. A URL query string portion can + only be properly URL escaped when decoding the parameter names and values + themselves. + """ + + def __init__(self, url: URL | str = "", **kwargs: typing.Any) -> None: + if kwargs: + allowed = { + "scheme": str, + "username": str, + "password": str, + "userinfo": bytes, + "host": str, + "port": int, + "netloc": bytes, + "path": str, + "query": bytes, + "raw_path": bytes, + "fragment": str, + "params": object, + } + + # Perform type checking for all supported keyword arguments. + for key, value in kwargs.items(): + if key not in allowed: + message = f"{key!r} is an invalid keyword argument for URL()" + raise TypeError(message) + if value is not None and not isinstance(value, allowed[key]): + expected = allowed[key].__name__ + seen = type(value).__name__ + message = f"Argument {key!r} must be {expected} but got {seen}" + raise TypeError(message) + if isinstance(value, bytes): + kwargs[key] = value.decode("ascii") + + if "params" in kwargs: + # Replace any "params" keyword with the raw "query" instead. + # + # Ensure that empty params use `kwargs["query"] = None` rather + # than `kwargs["query"] = ""`, so that generated URLs do not + # include an empty trailing "?". + params = kwargs.pop("params") + kwargs["query"] = None if not params else str(QueryParams(params)) + + if isinstance(url, str): + self._uri_reference = urlparse(url, **kwargs) + elif isinstance(url, URL): + self._uri_reference = url._uri_reference.copy_with(**kwargs) + else: + raise TypeError( + "Invalid type for url. Expected str or httpx.URL," + f" got {type(url)}: {url!r}" + ) + + @property + def scheme(self) -> str: + """ + The URL scheme, such as "http", "https". + Always normalised to lowercase. + """ + return self._uri_reference.scheme + + @property + def raw_scheme(self) -> bytes: + """ + The raw bytes representation of the URL scheme, such as b"http", b"https". + Always normalised to lowercase. + """ + return self._uri_reference.scheme.encode("ascii") + + @property + def userinfo(self) -> bytes: + """ + The URL userinfo as a raw bytestring. + For example: b"jo%40email.com:a%20secret". + """ + return self._uri_reference.userinfo.encode("ascii") + + @property + def username(self) -> str: + """ + The URL username as a string, with URL decoding applied. + For example: "jo@email.com" + """ + userinfo = self._uri_reference.userinfo + return unquote(userinfo.partition(":")[0]) + + @property + def password(self) -> str: + """ + The URL password as a string, with URL decoding applied. + For example: "a secret" + """ + userinfo = self._uri_reference.userinfo + return unquote(userinfo.partition(":")[2]) + + @property + def host(self) -> str: + """ + The URL host as a string. + Always normalized to lowercase, with IDNA hosts decoded into unicode. + + Examples: + + url = httpx.URL("http://www.EXAMPLE.org") + assert url.host == "www.example.org" + + url = httpx.URL("http://中国.icom.museum") + assert url.host == "中国.icom.museum" + + url = httpx.URL("http://xn--fiqs8s.icom.museum") + assert url.host == "中国.icom.museum" + + url = httpx.URL("https://[::ffff:192.168.0.1]") + assert url.host == "::ffff:192.168.0.1" + """ + host: str = self._uri_reference.host + + if host.startswith("xn--"): + host = idna.decode(host) + + return host + + @property + def raw_host(self) -> bytes: + """ + The raw bytes representation of the URL host. + Always normalized to lowercase, and IDNA encoded. + + Examples: + + url = httpx.URL("http://www.EXAMPLE.org") + assert url.raw_host == b"www.example.org" + + url = httpx.URL("http://中国.icom.museum") + assert url.raw_host == b"xn--fiqs8s.icom.museum" + + url = httpx.URL("http://xn--fiqs8s.icom.museum") + assert url.raw_host == b"xn--fiqs8s.icom.museum" + + url = httpx.URL("https://[::ffff:192.168.0.1]") + assert url.raw_host == b"::ffff:192.168.0.1" + """ + return self._uri_reference.host.encode("ascii") + + @property + def port(self) -> int | None: + """ + The URL port as an integer. + + Note that the URL class performs port normalization as per the WHATWG spec. + Default ports for "http", "https", "ws", "wss", and "ftp" schemes are always + treated as `None`. + + For example: + + assert httpx.URL("http://www.example.com") == httpx.URL("http://www.example.com:80") + assert httpx.URL("http://www.example.com:80").port is None + """ + return self._uri_reference.port + + @property + def netloc(self) -> bytes: + """ + Either `` or `:` as bytes. + Always normalized to lowercase, and IDNA encoded. + + This property may be used for generating the value of a request + "Host" header. + """ + return self._uri_reference.netloc.encode("ascii") + + @property + def path(self) -> str: + """ + The URL path as a string. Excluding the query string, and URL decoded. + + For example: + + url = httpx.URL("https://example.com/pa%20th") + assert url.path == "/pa th" + """ + path = self._uri_reference.path or "/" + return unquote(path) + + @property + def query(self) -> bytes: + """ + The URL query string, as raw bytes, excluding the leading b"?". + + This is necessarily a bytewise interface, because we cannot + perform URL decoding of this representation until we've parsed + the keys and values into a QueryParams instance. + + For example: + + url = httpx.URL("https://example.com/?filter=some%20search%20terms") + assert url.query == b"filter=some%20search%20terms" + """ + query = self._uri_reference.query or "" + return query.encode("ascii") + + @property + def params(self) -> QueryParams: + """ + The URL query parameters, neatly parsed and packaged into an immutable + multidict representation. + """ + return QueryParams(self._uri_reference.query) + + @property + def raw_path(self) -> bytes: + """ + The complete URL path and query string as raw bytes. + Used as the target when constructing HTTP requests. + + For example: + + GET /users?search=some%20text HTTP/1.1 + Host: www.example.org + Connection: close + """ + path = self._uri_reference.path or "/" + if self._uri_reference.query is not None: + path += "?" + self._uri_reference.query + return path.encode("ascii") + + @property + def fragment(self) -> str: + """ + The URL fragments, as used in HTML anchors. + As a string, without the leading '#'. + """ + return unquote(self._uri_reference.fragment or "") + + @property + def is_absolute_url(self) -> bool: + """ + Return `True` for absolute URLs such as 'http://example.com/path', + and `False` for relative URLs such as '/path'. + """ + # We don't use `.is_absolute` from `rfc3986` because it treats + # URLs with a fragment portion as not absolute. + # What we actually care about is if the URL provides + # a scheme and hostname to which connections should be made. + return bool(self._uri_reference.scheme and self._uri_reference.host) + + @property + def is_relative_url(self) -> bool: + """ + Return `False` for absolute URLs such as 'http://example.com/path', + and `True` for relative URLs such as '/path'. + """ + return not self.is_absolute_url + + def copy_with(self, **kwargs: typing.Any) -> URL: + """ + Copy this URL, returning a new URL with some components altered. + Accepts the same set of parameters as the components that are made + available via properties on the `URL` class. + + For example: + + url = httpx.URL("https://www.example.com").copy_with( + username="jo@gmail.com", password="a secret" + ) + assert url == "https://jo%40email.com:a%20secret@www.example.com" + """ + return URL(self, **kwargs) + + def copy_set_param(self, key: str, value: typing.Any = None) -> URL: + return self.copy_with(params=self.params.set(key, value)) + + def copy_add_param(self, key: str, value: typing.Any = None) -> URL: + return self.copy_with(params=self.params.add(key, value)) + + def copy_remove_param(self, key: str) -> URL: + return self.copy_with(params=self.params.remove(key)) + + def copy_merge_params(self, params: QueryParamTypes) -> URL: + return self.copy_with(params=self.params.merge(params)) + + def join(self, url: URL | str) -> URL: + """ + Return an absolute URL, using this URL as the base. + + Eg. + + url = httpx.URL("https://www.example.com/test") + url = url.join("/new/path") + assert url == "https://www.example.com/new/path" + """ + from urllib.parse import urljoin + + return URL(urljoin(str(self), str(URL(url)))) + + def __hash__(self) -> int: + return hash(str(self)) + + def __eq__(self, other: typing.Any) -> bool: + return isinstance(other, (URL, str)) and str(self) == str(URL(other)) + + def __str__(self) -> str: + return str(self._uri_reference) + + def __repr__(self) -> str: + scheme, userinfo, host, port, path, query, fragment = self._uri_reference + + if ":" in userinfo: + # Mask any password component. + userinfo = f'{userinfo.split(":")[0]}:[secure]' + + authority = "".join( + [ + f"{userinfo}@" if userinfo else "", + f"[{host}]" if ":" in host else host, + f":{port}" if port is not None else "", + ] + ) + url = "".join( + [ + f"{self.scheme}:" if scheme else "", + f"//{authority}" if authority else "", + path, + f"?{query}" if query is not None else "", + f"#{fragment}" if fragment is not None else "", + ] + ) + + return f"{self.__class__.__name__}({url!r})" + + @property + def raw(self) -> tuple[bytes, bytes, int, bytes]: # pragma: nocover + import collections + import warnings + + warnings.warn("URL.raw is deprecated.") + RawURL = collections.namedtuple( + "RawURL", ["raw_scheme", "raw_host", "port", "raw_path"] + ) + return RawURL( + raw_scheme=self.raw_scheme, + raw_host=self.raw_host, + port=self.port, + raw_path=self.raw_path, + ) + + +class QueryParams(typing.Mapping[str, str]): + """ + URL query parameters, as a multi-dict. + """ + + def __init__(self, *args: QueryParamTypes | None, **kwargs: typing.Any) -> None: + assert len(args) < 2, "Too many arguments." + assert not (args and kwargs), "Cannot mix named and unnamed arguments." + + value = args[0] if args else kwargs + + if value is None or isinstance(value, (str, bytes)): + value = value.decode("ascii") if isinstance(value, bytes) else value + self._dict = parse_qs(value, keep_blank_values=True) + elif isinstance(value, QueryParams): + self._dict = {k: list(v) for k, v in value._dict.items()} + else: + dict_value: dict[typing.Any, list[typing.Any]] = {} + if isinstance(value, (list, tuple)): + # Convert list inputs like: + # [("a", "123"), ("a", "456"), ("b", "789")] + # To a dict representation, like: + # {"a": ["123", "456"], "b": ["789"]} + for item in value: + dict_value.setdefault(item[0], []).append(item[1]) + else: + # Convert dict inputs like: + # {"a": "123", "b": ["456", "789"]} + # To dict inputs where values are always lists, like: + # {"a": ["123"], "b": ["456", "789"]} + dict_value = { + k: list(v) if isinstance(v, (list, tuple)) else [v] + for k, v in value.items() + } + + # Ensure that keys and values are neatly coerced to strings. + # We coerce values `True` and `False` to JSON-like "true" and "false" + # representations, and coerce `None` values to the empty string. + self._dict = { + str(k): [primitive_value_to_str(item) for item in v] + for k, v in dict_value.items() + } + + def keys(self) -> typing.KeysView[str]: + """ + Return all the keys in the query params. + + Usage: + + q = httpx.QueryParams("a=123&a=456&b=789") + assert list(q.keys()) == ["a", "b"] + """ + return self._dict.keys() + + def values(self) -> typing.ValuesView[str]: + """ + Return all the values in the query params. If a key occurs more than once + only the first item for that key is returned. + + Usage: + + q = httpx.QueryParams("a=123&a=456&b=789") + assert list(q.values()) == ["123", "789"] + """ + return {k: v[0] for k, v in self._dict.items()}.values() + + def items(self) -> typing.ItemsView[str, str]: + """ + Return all items in the query params. If a key occurs more than once + only the first item for that key is returned. + + Usage: + + q = httpx.QueryParams("a=123&a=456&b=789") + assert list(q.items()) == [("a", "123"), ("b", "789")] + """ + return {k: v[0] for k, v in self._dict.items()}.items() + + def multi_items(self) -> list[tuple[str, str]]: + """ + Return all items in the query params. Allow duplicate keys to occur. + + Usage: + + q = httpx.QueryParams("a=123&a=456&b=789") + assert list(q.multi_items()) == [("a", "123"), ("a", "456"), ("b", "789")] + """ + multi_items: list[tuple[str, str]] = [] + for k, v in self._dict.items(): + multi_items.extend([(k, i) for i in v]) + return multi_items + + def get(self, key: typing.Any, default: typing.Any = None) -> typing.Any: + """ + Get a value from the query param for a given key. If the key occurs + more than once, then only the first value is returned. + + Usage: + + q = httpx.QueryParams("a=123&a=456&b=789") + assert q.get("a") == "123" + """ + if key in self._dict: + return self._dict[str(key)][0] + return default + + def get_list(self, key: str) -> list[str]: + """ + Get all values from the query param for a given key. + + Usage: + + q = httpx.QueryParams("a=123&a=456&b=789") + assert q.get_list("a") == ["123", "456"] + """ + return list(self._dict.get(str(key), [])) + + def set(self, key: str, value: typing.Any = None) -> QueryParams: + """ + Return a new QueryParams instance, setting the value of a key. + + Usage: + + q = httpx.QueryParams("a=123") + q = q.set("a", "456") + assert q == httpx.QueryParams("a=456") + """ + q = QueryParams() + q._dict = dict(self._dict) + q._dict[str(key)] = [primitive_value_to_str(value)] + return q + + def add(self, key: str, value: typing.Any = None) -> QueryParams: + """ + Return a new QueryParams instance, setting or appending the value of a key. + + Usage: + + q = httpx.QueryParams("a=123") + q = q.add("a", "456") + assert q == httpx.QueryParams("a=123&a=456") + """ + q = QueryParams() + q._dict = dict(self._dict) + q._dict[str(key)] = q.get_list(key) + [primitive_value_to_str(value)] + return q + + def remove(self, key: str) -> QueryParams: + """ + Return a new QueryParams instance, removing the value of a key. + + Usage: + + q = httpx.QueryParams("a=123") + q = q.remove("a") + assert q == httpx.QueryParams("") + """ + q = QueryParams() + q._dict = dict(self._dict) + q._dict.pop(str(key), None) + return q + + def merge(self, params: QueryParamTypes | None = None) -> QueryParams: + """ + Return a new QueryParams instance, updated with. + + Usage: + + q = httpx.QueryParams("a=123") + q = q.merge({"b": "456"}) + assert q == httpx.QueryParams("a=123&b=456") + + q = httpx.QueryParams("a=123") + q = q.merge({"a": "456", "b": "789"}) + assert q == httpx.QueryParams("a=456&b=789") + """ + q = QueryParams(params) + q._dict = {**self._dict, **q._dict} + return q + + def __getitem__(self, key: typing.Any) -> str: + return self._dict[key][0] + + def __contains__(self, key: typing.Any) -> bool: + return key in self._dict + + def __iter__(self) -> typing.Iterator[typing.Any]: + return iter(self.keys()) + + def __len__(self) -> int: + return len(self._dict) + + def __bool__(self) -> bool: + return bool(self._dict) + + def __hash__(self) -> int: + return hash(str(self)) + + def __eq__(self, other: typing.Any) -> bool: + if not isinstance(other, self.__class__): + return False + return sorted(self.multi_items()) == sorted(other.multi_items()) + + def __str__(self) -> str: + return urlencode(self.multi_items()) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + query_string = str(self) + return f"{class_name}({query_string!r})" + + def update(self, params: QueryParamTypes | None = None) -> None: + raise RuntimeError( + "QueryParams are immutable since 0.18.0. " + "Use `q = q.merge(...)` to create an updated copy." + ) + + def __setitem__(self, key: str, value: str) -> None: + raise RuntimeError( + "QueryParams are immutable since 0.18.0. " + "Use `q = q.set(key, value)` to create an updated copy." + ) diff --git a/venv/lib/python3.12/site-packages/httpx/_utils.py b/venv/lib/python3.12/site-packages/httpx/_utils.py new file mode 100644 index 0000000..7fe827d --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx/_utils.py @@ -0,0 +1,242 @@ +from __future__ import annotations + +import ipaddress +import os +import re +import typing +from urllib.request import getproxies + +from ._types import PrimitiveData + +if typing.TYPE_CHECKING: # pragma: no cover + from ._urls import URL + + +def primitive_value_to_str(value: PrimitiveData) -> str: + """ + Coerce a primitive data type into a string value. + + Note that we prefer JSON-style 'true'/'false' for boolean values here. + """ + if value is True: + return "true" + elif value is False: + return "false" + elif value is None: + return "" + return str(value) + + +def get_environment_proxies() -> dict[str, str | None]: + """Gets proxy information from the environment""" + + # urllib.request.getproxies() falls back on System + # Registry and Config for proxies on Windows and macOS. + # We don't want to propagate non-HTTP proxies into + # our configuration such as 'TRAVIS_APT_PROXY'. + proxy_info = getproxies() + mounts: dict[str, str | None] = {} + + for scheme in ("http", "https", "all"): + if proxy_info.get(scheme): + hostname = proxy_info[scheme] + mounts[f"{scheme}://"] = ( + hostname if "://" in hostname else f"http://{hostname}" + ) + + no_proxy_hosts = [host.strip() for host in proxy_info.get("no", "").split(",")] + for hostname in no_proxy_hosts: + # See https://curl.haxx.se/libcurl/c/CURLOPT_NOPROXY.html for details + # on how names in `NO_PROXY` are handled. + if hostname == "*": + # If NO_PROXY=* is used or if "*" occurs as any one of the comma + # separated hostnames, then we should just bypass any information + # from HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, and always ignore + # proxies. + return {} + elif hostname: + # NO_PROXY=.google.com is marked as "all://*.google.com, + # which disables "www.google.com" but not "google.com" + # NO_PROXY=google.com is marked as "all://*google.com, + # which disables "www.google.com" and "google.com". + # (But not "wwwgoogle.com") + # NO_PROXY can include domains, IPv6, IPv4 addresses and "localhost" + # NO_PROXY=example.com,::1,localhost,192.168.0.0/16 + if "://" in hostname: + mounts[hostname] = None + elif is_ipv4_hostname(hostname): + mounts[f"all://{hostname}"] = None + elif is_ipv6_hostname(hostname): + mounts[f"all://[{hostname}]"] = None + elif hostname.lower() == "localhost": + mounts[f"all://{hostname}"] = None + else: + mounts[f"all://*{hostname}"] = None + + return mounts + + +def to_bytes(value: str | bytes, encoding: str = "utf-8") -> bytes: + return value.encode(encoding) if isinstance(value, str) else value + + +def to_str(value: str | bytes, encoding: str = "utf-8") -> str: + return value if isinstance(value, str) else value.decode(encoding) + + +def to_bytes_or_str(value: str, match_type_of: typing.AnyStr) -> typing.AnyStr: + return value if isinstance(match_type_of, str) else value.encode() + + +def unquote(value: str) -> str: + return value[1:-1] if value[0] == value[-1] == '"' else value + + +def peek_filelike_length(stream: typing.Any) -> int | None: + """ + Given a file-like stream object, return its length in number of bytes + without reading it into memory. + """ + try: + # Is it an actual file? + fd = stream.fileno() + # Yup, seems to be an actual file. + length = os.fstat(fd).st_size + except (AttributeError, OSError): + # No... Maybe it's something that supports random access, like `io.BytesIO`? + try: + # Assuming so, go to end of stream to figure out its length, + # then put it back in place. + offset = stream.tell() + length = stream.seek(0, os.SEEK_END) + stream.seek(offset) + except (AttributeError, OSError): + # Not even that? Sorry, we're doomed... + return None + + return length + + +class URLPattern: + """ + A utility class currently used for making lookups against proxy keys... + + # Wildcard matching... + >>> pattern = URLPattern("all://") + >>> pattern.matches(httpx.URL("http://example.com")) + True + + # Witch scheme matching... + >>> pattern = URLPattern("https://") + >>> pattern.matches(httpx.URL("https://example.com")) + True + >>> pattern.matches(httpx.URL("http://example.com")) + False + + # With domain matching... + >>> pattern = URLPattern("https://example.com") + >>> pattern.matches(httpx.URL("https://example.com")) + True + >>> pattern.matches(httpx.URL("http://example.com")) + False + >>> pattern.matches(httpx.URL("https://other.com")) + False + + # Wildcard scheme, with domain matching... + >>> pattern = URLPattern("all://example.com") + >>> pattern.matches(httpx.URL("https://example.com")) + True + >>> pattern.matches(httpx.URL("http://example.com")) + True + >>> pattern.matches(httpx.URL("https://other.com")) + False + + # With port matching... + >>> pattern = URLPattern("https://example.com:1234") + >>> pattern.matches(httpx.URL("https://example.com:1234")) + True + >>> pattern.matches(httpx.URL("https://example.com")) + False + """ + + def __init__(self, pattern: str) -> None: + from ._urls import URL + + if pattern and ":" not in pattern: + raise ValueError( + f"Proxy keys should use proper URL forms rather " + f"than plain scheme strings. " + f'Instead of "{pattern}", use "{pattern}://"' + ) + + url = URL(pattern) + self.pattern = pattern + self.scheme = "" if url.scheme == "all" else url.scheme + self.host = "" if url.host == "*" else url.host + self.port = url.port + if not url.host or url.host == "*": + self.host_regex: typing.Pattern[str] | None = None + elif url.host.startswith("*."): + # *.example.com should match "www.example.com", but not "example.com" + domain = re.escape(url.host[2:]) + self.host_regex = re.compile(f"^.+\\.{domain}$") + elif url.host.startswith("*"): + # *example.com should match "www.example.com" and "example.com" + domain = re.escape(url.host[1:]) + self.host_regex = re.compile(f"^(.+\\.)?{domain}$") + else: + # example.com should match "example.com" but not "www.example.com" + domain = re.escape(url.host) + self.host_regex = re.compile(f"^{domain}$") + + def matches(self, other: URL) -> bool: + if self.scheme and self.scheme != other.scheme: + return False + if ( + self.host + and self.host_regex is not None + and not self.host_regex.match(other.host) + ): + return False + if self.port is not None and self.port != other.port: + return False + return True + + @property + def priority(self) -> tuple[int, int, int]: + """ + The priority allows URLPattern instances to be sortable, so that + we can match from most specific to least specific. + """ + # URLs with a port should take priority over URLs without a port. + port_priority = 0 if self.port is not None else 1 + # Longer hostnames should match first. + host_priority = -len(self.host) + # Longer schemes should match first. + scheme_priority = -len(self.scheme) + return (port_priority, host_priority, scheme_priority) + + def __hash__(self) -> int: + return hash(self.pattern) + + def __lt__(self, other: URLPattern) -> bool: + return self.priority < other.priority + + def __eq__(self, other: typing.Any) -> bool: + return isinstance(other, URLPattern) and self.pattern == other.pattern + + +def is_ipv4_hostname(hostname: str) -> bool: + try: + ipaddress.IPv4Address(hostname.split("/")[0]) + except Exception: + return False + return True + + +def is_ipv6_hostname(hostname: str) -> bool: + try: + ipaddress.IPv6Address(hostname.split("/")[0]) + except Exception: + return False + return True diff --git a/venv/lib/python3.12/site-packages/httpx/py.typed b/venv/lib/python3.12/site-packages/httpx/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/METADATA b/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/METADATA new file mode 100644 index 0000000..f238977 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/METADATA @@ -0,0 +1,228 @@ +Metadata-Version: 2.5 +Name: httpx2 +Version: 2.13.1 +Summary: The next generation HTTP client. +Project-URL: Changelog, https://github.com/pydantic/httpx2/blob/main/src/httpx2/CHANGELOG.md +Project-URL: Homepage, https://github.com/pydantic/httpx2 +Project-URL: Source, https://github.com/pydantic/httpx2 +Author-email: Tom Christie +Maintainer-email: "Pydantic Services Inc." +License-Expression: BSD-3-Clause +License-File: LICENSE.md +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: AnyIO +Classifier: Framework :: AsyncIO +Classifier: Framework :: Trio +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: 3.15 +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.10 +Requires-Dist: anyio>=4.10; sys_platform != 'emscripten' +Requires-Dist: httpcore2==2.13.1; sys_platform != 'emscripten' +Requires-Dist: httpx2-jsfetch; sys_platform == 'emscripten' and python_version >= '3.12' +Requires-Dist: idna>=3.18 +Requires-Dist: truststore>=0.10; sys_platform != 'emscripten' +Requires-Dist: typing-extensions>=4.5.0; python_version < '3.13' +Provides-Extra: brotli +Requires-Dist: brotli>=1.2.0; (platform_python_implementation == 'CPython') and extra == 'brotli' +Requires-Dist: brotlicffi>=1.2.0.2; (platform_python_implementation != 'CPython') and extra == 'brotli' +Provides-Extra: cli +Requires-Dist: click>=8.4.2; extra == 'cli' +Requires-Dist: pygments==2.*; extra == 'cli' +Requires-Dist: rich<16,>=10; extra == 'cli' +Provides-Extra: http2 +Requires-Dist: h2<5,>=3; extra == 'http2' +Provides-Extra: socks +Requires-Dist: socksio==1.*; extra == 'socks' +Provides-Extra: ws +Requires-Dist: wsproto>=1.2; extra == 'ws' +Provides-Extra: zstd +Requires-Dist: backports-zstd>=1.0.0; (python_version <= '3.13') and extra == 'zstd' +Description-Content-Type: text/markdown + +

HTTPX2

+ +

A next-generation HTTP client for Python.

+ +

+Test Suite +Package version +Join Slack +

+ +HTTPX2 is a fully featured HTTP client library for Python. It includes **an integrated command line client**, has support for both **HTTP/1.1 and HTTP/2**, and provides both **sync and async APIs**. + +> [!NOTE] +> HTTPX2 is a continuation of the wonderful work started by [@lovelydinosaur](https://github.com/lovelydinosaur) and the broader HTTPX community. We're enormously grateful for everything that has gone into HTTPX over the years - it has been a foundational piece of the modern Python ecosystem, and this project would not exist without it. +> +> With HTTPX itself seeing limited activity recently, Pydantic is picking up stewardship under the HTTPX2 name so that users have a reliably maintained path forward - including timely security updates for a library that sits in the critical path of so many production systems. Our aim is to honour the original project's design, keep it stable for everyone relying on it, and continue evolving it carefully. Thank you to [@lovelydinosaur](https://github.com/lovelydinosaur) and every past contributor for laying such a strong foundation. 💙 + +--- + +Install HTTPX2 using pip: + +```shell +pip install httpx2 +``` + +Now, let's get started: + +```pycon +>>> import httpx2 +>>> r = httpx2.get('https://www.example.org/') +>>> r + +>>> r.status_code +200 +>>> r.headers['content-type'] +'text/html; charset=UTF-8' +>>> r.text +'\n\n\nExample Domain...' +``` + +Or, using the command-line client. + +```shell +pip install 'httpx2[cli]' # The command line client is an optional dependency. +``` + +Which now allows us to use HTTPX2 directly from the command-line: + +```shell +httpx2 --help +``` + +## Features + +HTTPX2 builds on the well-established usability of `requests`, and gives you: + +* A broadly [requests-compatible API](https://pydantic.dev/docs/httpx2/guides/compatibility/). +* An integrated command-line client. +* HTTP/1.1 [and HTTP/2 support](https://pydantic.dev/docs/httpx2/guides/http2/). +* Standard synchronous interface, but with [async support if you need it](https://pydantic.dev/docs/httpx2/guides/async/). +* Ability to make requests directly to [WSGI applications](https://pydantic.dev/docs/httpx2/advanced/transports/#wsgi-transport) or [ASGI applications](https://pydantic.dev/docs/httpx2/advanced/transports/#asgi-transport). +* Strict timeouts everywhere. +* Fully type annotated. +* 100% test coverage. + +Plus all the standard features of `requests`... + +* International Domains and URLs +* Keep-Alive & Connection Pooling +* Sessions with Cookie Persistence +* Browser-style SSL Verification +* Basic/Digest Authentication +* Elegant Key/Value Cookies +* Automatic Decompression +* Automatic Content Decoding +* Unicode Response Bodies +* Multipart File Uploads +* HTTP(S) Proxy Support +* Connection Timeouts +* Streaming Downloads +* .netrc Support +* Chunked Requests + +## Installation + +Install with pip: + +```shell +pip install httpx2 +``` + +Or, to include the optional HTTP/2 support, use: + +```shell +pip install httpx2[http2] +``` + +## Documentation + +Project documentation is available at [https://pydantic.dev/docs/httpx2/](https://pydantic.dev/docs/httpx2/). + +For a run-through of all the basics, head over to the [QuickStart](https://pydantic.dev/docs/httpx2/get-started/quickstart/). + +For more advanced topics, see the [Advanced Usage](https://pydantic.dev/docs/httpx2/advanced/clients/) section, the [async support](https://pydantic.dev/docs/httpx2/guides/async/) section, or the [HTTP/2](https://pydantic.dev/docs/httpx2/guides/http2/) section. + +The [Developer Interface](https://pydantic.dev/docs/httpx2/api/api/) provides a comprehensive API reference. + +To find out about tools that integrate with HTTPX, see [Third Party Packages](https://pydantic.dev/docs/httpx2/community/third_party_packages/). + +## Observability with Pydantic Logfire + +Install Logfire with HTTPX instrumentation support: + +```shell +pip install 'logfire[httpx]' +``` + +HTTPX2 works out of the box with [Pydantic Logfire](https://pydantic.dev/logfire), our observability platform built on OpenTelemetry. One line instruments every request, giving you traces, timings, and status codes with no other changes: + +```python +import logfire +import httpx2 + +logfire.configure() +logfire.instrument_httpx() + +httpx2.get("https://pydantic.dev/") +``` + +This works the same way for explicit `httpx2.Client()` and `httpx2.AsyncClient()` instances, and you can scope instrumentation to a single client by passing it in: `logfire.instrument_httpx(client)`. Pass `capture_all=True` to also record headers and bodies. See the [Logfire HTTPX docs](https://logfire.pydantic.dev/docs/integrations/http-clients/httpx/) for the full set of options. + +## Contribute + +If you want to contribute with HTTPX2 check out the [Contributing Guide](https://pydantic.dev/docs/httpx2/community/contributing/) to learn how to start. + +## Dependencies + +The HTTPX2 project relies on these excellent libraries: + +* `httpcore2` - The underlying transport implementation for `httpx2`. + * `h11` - HTTP/1.1 support. +* `anyio` - Structured concurrency primitives, used to support both `asyncio` and `trio`. +* `truststore` - SSL verification using the operating system's trust store. +* `idna` - Internationalized domain name support. + +As well as these optional installs: + +* `h2` - HTTP/2 support. *(Optional, with `httpx2[http2]`)* +* `socksio` - SOCKS proxy support. *(Optional, with `httpx2[socks]`)* +* `rich` - Rich terminal support. *(Optional, with `httpx2[cli]`)* +* `click` - Command line client support. *(Optional, with `httpx2[cli]`)* +* `brotli` or `brotlicffi` - Decoding for "brotli" compressed responses. *(Optional, with `httpx2[brotli]`)* +* `backports.zstd` - Decoding for "zstd" compressed responses on Python 3.13 and below. *(Optional, with `httpx2[zstd]`. On Python 3.14+, `zstd` is supported via the stdlib [`compression.zstd`](https://docs.python.org/3/library/compression.zstd.html) module when it is available, and the `httpx2[zstd]` extra installs nothing.)* + +A huge amount of credit is due to `requests` for the API layout that +much of this work follows, as well as to `urllib3` for plenty of design +inspiration around the lower-level networking details. + +--- + +

HTTPX2 is BSD licensed code.
Designed & crafted with care.

— 🦋 —

+ +## Release Information + +## 2.13.1 (September 23rd, 2026) + +### Fixed + +* Calculate `Content-Length` from the remaining bytes when a file is passed as `content=`, respecting its current position. + ([#1214](https://github.com/pydantic/httpx2/pull/1214)) +* Stop the sync WebSocket keepalive thread cleanly when a ping races with connection shutdown. + ([#1228](https://github.com/pydantic/httpx2/pull/1228)) + +--- + +[Full changelog](https://github.com/pydantic/httpx2/blob/main/src/httpx2/CHANGELOG.md) diff --git a/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/RECORD new file mode 100644 index 0000000..fa58bcd --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/RECORD @@ -0,0 +1,68 @@ +../../../bin/httpx2,sha256=Ox37qnLW4uVifK8jdaUmn7KvWATb1y_qFmJ8zOp8lkk,184 +httpx2-2.13.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +httpx2-2.13.1.dist-info/METADATA,sha256=t1sDoAO1WT4ACQfPcjn8w_aIuPF9tOz_iD0RIh6piPw,9799 +httpx2-2.13.1.dist-info/RECORD,, +httpx2-2.13.1.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87 +httpx2-2.13.1.dist-info/entry_points.txt,sha256=3uiZorRQlQAFNoHaDxuMsmkdjt3rUTmuBz81tLXFdQY,45 +httpx2-2.13.1.dist-info/licenses/LICENSE.md,sha256=fn1tuvf-Fg05_W6E_QLDPKuWvL6jTTaZjVRbzrShnDA,1589 +httpx2/__init__.py,sha256=BfjyqGaJJhHp5NsWDrQ7GJUjqr1zGXNoqV3vb5eUozo,2661 +httpx2/__pycache__/__init__.cpython-312.pyc,, +httpx2/__pycache__/__version__.cpython-312.pyc,, +httpx2/__pycache__/_alias.cpython-312.pyc,, +httpx2/__pycache__/_api.cpython-312.pyc,, +httpx2/__pycache__/_auth.cpython-312.pyc,, +httpx2/__pycache__/_client.cpython-312.pyc,, +httpx2/__pycache__/_config.cpython-312.pyc,, +httpx2/__pycache__/_content.cpython-312.pyc,, +httpx2/__pycache__/_decoders.cpython-312.pyc,, +httpx2/__pycache__/_exceptions.cpython-312.pyc,, +httpx2/__pycache__/_main.cpython-312.pyc,, +httpx2/__pycache__/_models.cpython-312.pyc,, +httpx2/__pycache__/_multipart.cpython-312.pyc,, +httpx2/__pycache__/_sse.cpython-312.pyc,, +httpx2/__pycache__/_status_codes.cpython-312.pyc,, +httpx2/__pycache__/_types.cpython-312.pyc,, +httpx2/__pycache__/_urlparse.cpython-312.pyc,, +httpx2/__pycache__/_urls.cpython-312.pyc,, +httpx2/__pycache__/_utils.cpython-312.pyc,, +httpx2/__version__.py,sha256=XWKDNx4TZNJGkuO6vv16RvfV8lhdgdMIbQ8Bt0cR4UQ,158 +httpx2/_alias.py,sha256=Evn6S3BWugzd5XgDAYOsQf45_tAQpgae4JJ6U6n02Bw,2431 +httpx2/_api.py,sha256=IhvMLaySB7-pEER2l_qCbSDjTmG28hVPXIJJMdiHze0,14794 +httpx2/_auth.py,sha256=qBNZ6I_DrWPLLZRa0oZT6cD3NP5NrG6f6KREUdhKugo,11648 +httpx2/_client.py,sha256=-jlIqmap79Xt8JfG0T2SdhLGFzKLQ87vxL3V41CqqTs,75410 +httpx2/_config.py,sha256=C1P3VPG7cI8upIW8y7SrH_oHJZz4wAikLYlZucsePbA,7847 +httpx2/_content.py,sha256=sNp9IVai11gXO_4P5Uka8XqrT_A8Ab5xkgRlKD4xeY0,8490 +httpx2/_decoders.py,sha256=C479GSHULT-xFz3Rr5TwZ0uUn2ih_afezn7QcgoIfAA,15046 +httpx2/_exceptions.py,sha256=69TPZgHvTkkjV1P0y5DSVOmDkxwCRsKhCfhd_qTTaVc,8857 +httpx2/_main.py,sha256=JT_VdROzHpKs8DNBIvXZXF2jYr6jxo0NVyk1FK6ff5A,15434 +httpx2/_models.py,sha256=0TnQxUoazWWZX2k9h2RB5V_kGO8nmfZH7GNh9pWw1NY,45738 +httpx2/_multipart.py,sha256=ksgIJarALvO7EcgDmc40GX6GyVO6XG0faszehXdRaTg,10018 +httpx2/_sse.py,sha256=IZ1Lf6NrgoZa7FP5-n_VhKhk0eEYFkPZtOC8g5JpQCk,7552 +httpx2/_status_codes.py,sha256=0CWpuD1dw8PYrt9f7epZ0bZZQEnmqe-z6FK_IzIeh14,6625 +httpx2/_transports/__init__.py,sha256=e65w9X4RMK9O_0Q6kPqpXr1ijeEf6b9xbl4KfrAYDCI,711 +httpx2/_transports/__pycache__/__init__.cpython-312.pyc,, +httpx2/_transports/__pycache__/asgi.cpython-312.pyc,, +httpx2/_transports/__pycache__/base.cpython-312.pyc,, +httpx2/_transports/__pycache__/default.cpython-312.pyc,, +httpx2/_transports/__pycache__/mock.cpython-312.pyc,, +httpx2/_transports/__pycache__/wsgi.cpython-312.pyc,, +httpx2/_transports/asgi.py,sha256=PWe4gtw8l2NqK5VusB4uprs6l46fJ4ewLX8FUEOozuk,5429 +httpx2/_transports/base.py,sha256=KY5zBVbdG6ge2sE-j0ZKyJ1C1LWBZVHQnOg0BdQ6idE,2584 +httpx2/_transports/default.py,sha256=j8Q6KILUca_3UTZqfyjpHF4xgxpLKao7tOZpMaIUjpc,13913 +httpx2/_transports/mock.py,sha256=3LZowN_YXbXezA2P4D4g29bCemTiT5Gr8U_vBvxtgxs,1209 +httpx2/_transports/wsgi.py,sha256=R5UApE_xXvl3BwKMgokN5irXiXGRFbOrtTdiKXFEBkI,4904 +httpx2/_types.py,sha256=pVETNjBR2OoWs9-sY7tRTAQM8sZx8-kNwZ4DjZcEYZU,2671 +httpx2/_urlparse.py,sha256=X-_rLDsC9QMIGc1FXr9DVRTYsLLB_SOdaCOtF2-DRIA,18323 +httpx2/_urls.py,sha256=t2wZOCuWtmP8vHnwq2hLhLZ_g2Y36K3SMpU0u_3J8CY,23184 +httpx2/_utils.py,sha256=BjXxmeYO9mtQPvCEBIoljbpy4GM8Oi0RCZuZRCIKQRk,8652 +httpx2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +httpx2/websockets/__init__.py,sha256=K66d2Zczl0Ewa0ey0Q2ETx5utzVH6n5G6J22mR3GbV0,1826 +httpx2/websockets/__pycache__/__init__.cpython-312.pyc,, +httpx2/websockets/__pycache__/_api.cpython-312.pyc,, +httpx2/websockets/__pycache__/_exceptions.cpython-312.pyc,, +httpx2/websockets/__pycache__/_ping.cpython-312.pyc,, +httpx2/websockets/__pycache__/_transport.cpython-312.pyc,, +httpx2/websockets/_api.py,sha256=u6eZNC28if6cl2K-yXqWXC_kD0Lrp1GKyGMjNjksyMk,62288 +httpx2/websockets/_exceptions.py,sha256=bpnBX9fDdkWh9jKWqqXAcQ_jsIPDRSuzUexxz_RPzqA,1290 +httpx2/websockets/_ping.py,sha256=M6Xmbap01-ldl1ejejmkjJbBgK1ulBj-_oaUoKciqrM,1258 +httpx2/websockets/_transport.py,sha256=Sb8G6vgI8jy4Ky91Zrb14Ri01qsTx9OcqK-YzHpQxik,10831 diff --git a/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/WHEEL b/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/WHEEL new file mode 100644 index 0000000..51abe1f --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.32.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/entry_points.txt new file mode 100644 index 0000000..38598b6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +httpx2 = httpx2._main:main diff --git a/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/licenses/LICENSE.md b/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/licenses/LICENSE.md new file mode 100644 index 0000000..23b0ee0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2-2.13.1.dist-info/licenses/LICENSE.md @@ -0,0 +1,13 @@ +Copyright © 2026 to present Pydantic Services Inc. and individual contributors. +Copyright © 2019, [Encode OSS Ltd](https://www.encode.io/). +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/httpx2/__init__.py b/venv/lib/python3.12/site-packages/httpx2/__init__.py new file mode 100644 index 0000000..5a57e03 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/__init__.py @@ -0,0 +1,130 @@ +from .__version__ import __description__, __title__, __version__ +from ._alias import * +from ._api import * +from ._auth import * +from ._client import * +from ._config import * +from ._content import * +from ._exceptions import * +from ._models import * +from ._sse import * +from ._status_codes import * +from ._transports import * +from ._types import * +from ._urls import * + +__all__ = [ + # __version__.py + "__description__", + "__title__", + "__version__", + # _alias.py + "alias_httpx", + # _api.py + "delete", + "get", + "head", + "options", + "patch", + "post", + "put", + "query", + "request", + "stream", + "websocket", + # _auth.py + "Auth", + "BasicAuth", + "DigestAuth", + "FunctionAuth", + "NetRCAuth", + # _client.py + "AsyncClient", + "Client", + "USE_CLIENT_DEFAULT", + # _config.py + "create_ssl_context", + "Limits", + "Proxy", + "Timeout", + # _content.py + "ByteStream", + # _exceptions.py + "CloseError", + "ConnectError", + "ConnectTimeout", + "CookieConflict", + "DecodingError", + "HTTPError", + "HTTPStatusError", + "InvalidURL", + "LocalProtocolError", + "NetworkError", + "PoolTimeout", + "ProtocolError", + "ProxyError", + "ReadError", + "ReadTimeout", + "RemoteProtocolError", + "RequestError", + "RequestNotRead", + "ResponseNotRead", + "StreamClosed", + "StreamConsumed", + "StreamError", + "TimeoutException", + "TooManyRedirects", + "TransportError", + "UnsupportedProtocol", + "WriteError", + "WriteTimeout", + # _models.py + "Cookies", + "Headers", + "Request", + "Response", + # _sse.py + "EventSource", + "ServerSentEvent", + "SSEError", + # _status_codes.py + "codes", + # _transports + "ASGITransport", + "AsyncBaseTransport", + "AsyncHTTPTransport", + "BaseTransport", + "HTTPTransport", + "MockTransport", + "WSGITransport", + # _types.py + "AsyncByteStream", + "SyncByteStream", + # _urls.py + "Origin", + "QueryParams", + "URL", +] + + +__locals = locals() +for __name in __all__: + if not __name.startswith("__"): + setattr(__locals[__name], "__module__", "httpx2") # noqa + + +def __getattr__(name: str) -> object: # pragma: no cover + if name == "main": + import warnings + + warnings.warn( + "`httpx2.main` is deprecated and will be removed in a future release. " + "Use the `httpx2` CLI entry point instead.", + DeprecationWarning, + stacklevel=2, + ) + from ._main import main + + return main + + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1e9f705 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/__version__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/__version__.cpython-312.pyc new file mode 100644 index 0000000..9ce617c Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/__version__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_alias.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_alias.cpython-312.pyc new file mode 100644 index 0000000..724a106 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_alias.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_api.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_api.cpython-312.pyc new file mode 100644 index 0000000..c632e8c Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_api.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_auth.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_auth.cpython-312.pyc new file mode 100644 index 0000000..fe11107 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_auth.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_client.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_client.cpython-312.pyc new file mode 100644 index 0000000..a3757e7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_client.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_config.cpython-312.pyc new file mode 100644 index 0000000..dc5a47c Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_content.cpython-312.pyc new file mode 100644 index 0000000..1989e17 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_decoders.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_decoders.cpython-312.pyc new file mode 100644 index 0000000..0325859 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_decoders.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_exceptions.cpython-312.pyc new file mode 100644 index 0000000..f4f7496 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_main.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_main.cpython-312.pyc new file mode 100644 index 0000000..e369fc1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_main.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_models.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_models.cpython-312.pyc new file mode 100644 index 0000000..7791307 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_models.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_multipart.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_multipart.cpython-312.pyc new file mode 100644 index 0000000..468f50b Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_multipart.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_sse.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_sse.cpython-312.pyc new file mode 100644 index 0000000..c7aa0d9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_sse.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_status_codes.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_status_codes.cpython-312.pyc new file mode 100644 index 0000000..576ab69 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_status_codes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_types.cpython-312.pyc new file mode 100644 index 0000000..5c9488d Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_urlparse.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_urlparse.cpython-312.pyc new file mode 100644 index 0000000..0767966 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_urlparse.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_urls.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_urls.cpython-312.pyc new file mode 100644 index 0000000..86621de Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_urls.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__pycache__/_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_utils.cpython-312.pyc new file mode 100644 index 0000000..ac0c08d Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/__pycache__/_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/__version__.py b/venv/lib/python3.12/site-packages/httpx2/__version__.py new file mode 100644 index 0000000..5353a4b --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/__version__.py @@ -0,0 +1,5 @@ +from importlib.metadata import version + +__title__ = "httpx2" +__description__ = "A next generation HTTP client, for Python 3." +__version__ = version("httpx2") diff --git a/venv/lib/python3.12/site-packages/httpx2/_alias.py b/venv/lib/python3.12/site-packages/httpx2/_alias.py new file mode 100644 index 0000000..111e2cf --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_alias.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +import importlib +import importlib.abc +import importlib.machinery +import importlib.util +import sys +from collections.abc import Sequence +from types import ModuleType + +__all__ = ["alias_httpx"] + + +class _AliasLoader(importlib.abc.Loader): + _original_spec: importlib.machinery.ModuleSpec | None + + def __init__(self, real_name: str) -> None: + self._real_name = real_name + + def create_module(self, spec: importlib.machinery.ModuleSpec) -> ModuleType: + module = importlib.import_module(self._real_name) + self._original_spec = module.__spec__ + return module + + def exec_module(self, module: ModuleType) -> None: + module.__spec__ = self._original_spec + + +class _AliasFinder(importlib.abc.MetaPathFinder): + def __init__(self, alias: str, real: str) -> None: + self._alias = alias + self._real = real + + def find_spec( + self, + fullname: str, + path: Sequence[str] | None = None, + target: ModuleType | None = None, + ) -> importlib.machinery.ModuleSpec | None: + if fullname != self._alias and not fullname.startswith(self._alias + "."): + return None + real_name = self._real + fullname.removeprefix(self._alias) + if real_name not in sys.modules and importlib.util.find_spec(real_name) is None: + return None + return importlib.machinery.ModuleSpec(fullname, _AliasLoader(real_name)) + + +def _alias(alias: str, module: ModuleType) -> None: + existing = sys.modules.get(alias) + if existing is not None and existing is not module: + raise RuntimeError(f"{alias} was already imported; call `alias_httpx()` before any `import {alias}`.") + + if not any(isinstance(finder, _AliasFinder) and finder._alias == alias for finder in sys.meta_path): + sys.meta_path.insert(0, _AliasFinder(alias, module.__name__)) + sys.modules[alias] = module + + +def alias_httpx() -> None: + """ + Make `import httpx` resolve to `httpx2`, and `import httpcore` to `httpcore2`, process-wide. + + Intended for applications migrating from `httpx`, so that dependencies still + importing `httpx` or `httpcore` share the `httpx2` classes. Libraries should never call this. + + Must be called before anything imports `httpx` or `httpcore`. Calling it again is a no-op. + """ + import httpcore2 + import httpx2 + + _alias("httpx", httpx2) + _alias("httpcore", httpcore2) diff --git a/venv/lib/python3.12/site-packages/httpx2/_api.py b/venv/lib/python3.12/site-packages/httpx2/_api.py new file mode 100644 index 0000000..a7a4ddc --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_api.py @@ -0,0 +1,532 @@ +from __future__ import annotations + +import typing +from collections.abc import Generator +from contextlib import contextmanager + +from ._client import Client +from ._config import ( + DEFAULT_KEEPALIVE_PING_INTERVAL_SECONDS, + DEFAULT_KEEPALIVE_PING_TIMEOUT_SECONDS, + DEFAULT_MAX_MESSAGE_SIZE_BYTES, + DEFAULT_QUEUE_SIZE, + DEFAULT_TIMEOUT_CONFIG, +) +from ._models import Response +from ._types import ( + AuthTypes, + CookieTypes, + HeaderTypes, + ProxyTypes, + QueryParamTypes, + RequestContent, + RequestData, + RequestFiles, + TimeoutTypes, +) +from ._urls import URL + +if typing.TYPE_CHECKING: + import ssl # pragma: no cover + + from .websockets._api import WebSocketSession + + +__all__ = [ + "delete", + "get", + "head", + "options", + "patch", + "post", + "put", + "query", + "request", + "stream", + "websocket", +] + + +def request( + method: str, + url: URL | str, + *, + params: QueryParamTypes | None = None, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + trust_env: bool = True, +) -> Response: + """Sends an HTTP request. + + Parameters: + method: HTTP method for the new `Request` object: `GET`, `OPTIONS`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE`, + or `QUERY`. + url: URL for the new `Request` object. + params: *(optional)* Query parameters to include in the URL, as a string, dictionary, or sequence of two-tuples. + content: *(optional)* Binary content to include in the body of the request, as bytes or a byte iterator. + data: *(optional)* Form data to include in the body of the request, as a dictionary. + files: *(optional)* A dictionary of upload files to include in the body of the request. + json: *(optional)* A JSON serializable object to include in the body of the request. + headers: *(optional)* Dictionary of HTTP headers to include in the request. + cookies: *(optional)* Dictionary of Cookie items to include in the request. + auth: *(optional)* An authentication class to use when sending the request. + proxy: *(optional)* A proxy URL where all the traffic should be routed. + timeout: *(optional)* The timeout configuration to use when sending the request. + follow_redirects: *(optional)* Enables or disables HTTP redirects. + verify: *(optional)* Either `True` to use an SSL context with the default CA bundle, `False` to disable + verification, or an instance of `ssl.SSLContext` to use a custom context. + trust_env: *(optional)* Enables or disables usage of environment variables for configuration. + + Returns: + The `Response` object. + + Usage: + + ``` + >>> import httpx2 + >>> response = httpx2.request('GET', 'https://httpbin.org/get') + >>> response + + ``` + """ + with Client( + cookies=cookies, + proxy=proxy, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) as client: + return client.request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + auth=auth, + follow_redirects=follow_redirects, + ) + + +@contextmanager +def stream( + method: str, + url: URL | str, + *, + params: QueryParamTypes | None = None, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + trust_env: bool = True, +) -> Generator[Response]: + """ + Alternative to `httpx2.request()` that streams the response body + instead of loading it into memory at once. + + **Parameters**: See `httpx2.request`. + + See also: [Streaming Responses][0] + + [0]: quickstart.md#streaming-responses + """ + with Client( + cookies=cookies, + proxy=proxy, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) as client: + with client.stream( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + auth=auth, + follow_redirects=follow_redirects, + ) as response: + yield response + + +def get( + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `GET` request. + + **Parameters**: See `httpx2.request`. + + Note that the `data`, `files`, `json` and `content` parameters are not available + on this function, as `GET` requests should not include a request body. + """ + return request( + "GET", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def options( + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends an `OPTIONS` request. + + **Parameters**: See `httpx2.request`. + + Note that the `data`, `files`, `json` and `content` parameters are not available + on this function, as `OPTIONS` requests should not include a request body. + """ + return request( + "OPTIONS", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def head( + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `HEAD` request. + + **Parameters**: See `httpx2.request`. + + Note that the `data`, `files`, `json` and `content` parameters are not available + on this function, as `HEAD` requests should not include a request body. + """ + return request( + "HEAD", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def post( + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `POST` request. + + **Parameters**: See `httpx2.request`. + """ + return request( + "POST", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def put( + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `PUT` request. + + **Parameters**: See `httpx2.request`. + """ + return request( + "PUT", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def patch( + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `PATCH` request. + + **Parameters**: See `httpx2.request`. + """ + return request( + "PATCH", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def delete( + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + verify: ssl.SSLContext | str | bool = True, + trust_env: bool = True, +) -> Response: + """ + Sends a `DELETE` request. + + **Parameters**: See `httpx2.request`. + + Note that the `data`, `files`, `json` and `content` parameters are not available + on this function, as `DELETE` requests should not include a request body. + """ + return request( + "DELETE", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def query( + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + verify: ssl.SSLContext | str | bool = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `QUERY` request. + + **Parameters**: See `httpx2.request`. + """ + return request( + "QUERY", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + follow_redirects=follow_redirects, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +@contextmanager +def websocket( + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + follow_redirects: bool = False, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + verify: ssl.SSLContext | str | bool = True, + trust_env: bool = True, + subprotocols: list[str] | None = None, + max_message_size_bytes: int = DEFAULT_MAX_MESSAGE_SIZE_BYTES, + queue_size: int = DEFAULT_QUEUE_SIZE, + keepalive_ping_interval_seconds: float | None = DEFAULT_KEEPALIVE_PING_INTERVAL_SECONDS, + keepalive_ping_timeout_seconds: float | None = DEFAULT_KEEPALIVE_PING_TIMEOUT_SECONDS, +) -> Generator[WebSocketSession]: + """ + Open a WebSocket session. + + The session is closed automatically when exiting the context manager. + + ```python + with httpx2.websocket("ws://localhost:8000/ws") as ws: + ws.send_text("Hello!") + message = ws.receive_text() + ``` + + **Parameters**: See `httpx2.request` and `httpx2.Client.websocket`. + """ + with Client( + cookies=cookies, + proxy=proxy, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) as client: + with client.websocket( + url, + params=params, + headers=headers, + auth=auth, + follow_redirects=follow_redirects, + subprotocols=subprotocols, + max_message_size_bytes=max_message_size_bytes, + queue_size=queue_size, + keepalive_ping_interval_seconds=keepalive_ping_interval_seconds, + keepalive_ping_timeout_seconds=keepalive_ping_timeout_seconds, + ) as session: + yield session diff --git a/venv/lib/python3.12/site-packages/httpx2/_auth.py b/venv/lib/python3.12/site-packages/httpx2/_auth.py new file mode 100644 index 0000000..7e2f29c --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_auth.py @@ -0,0 +1,328 @@ +from __future__ import annotations + +import hashlib +import os +import re +import time +import typing +from base64 import b64encode +from urllib.request import parse_http_list + +from ._exceptions import ProtocolError +from ._models import Cookies, Request, Response +from ._utils import to_bytes, to_str + +if typing.TYPE_CHECKING: + from hashlib import _Hash + + +__all__ = ["Auth", "BasicAuth", "DigestAuth", "FunctionAuth", "NetRCAuth"] + + +class Auth: + """ + Base class for all authentication schemes. + + To implement a custom authentication scheme, subclass `Auth` and override + the `.auth_flow()` method. + + If the authentication scheme does I/O such as disk access or network calls, or uses + synchronization primitives such as locks, you should override `.sync_auth_flow()` + and/or `.async_auth_flow()` instead of `.auth_flow()` to provide specialized + implementations that will be used by `Client` and `AsyncClient` respectively. + """ + + requires_request_body = False + requires_response_body = False + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + """ + Execute the authentication flow. + + To dispatch a request, `yield` it: + + ``` + yield request + ``` + + The client will `.send()` the response back into the flow generator. You can + access it like so: + + ``` + response = yield request + ``` + + A `return` (or reaching the end of the generator) will result in the + client returning the last response obtained from the server. + + You can dispatch as many requests as is necessary. + """ + yield request + + def sync_auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + """ + Execute the authentication flow synchronously. + + By default, this defers to `.auth_flow()`. You should override this method + when the authentication scheme does I/O and/or uses concurrency primitives. + """ + if self.requires_request_body: + request.read() + + flow = self.auth_flow(request) + request = next(flow) + + while True: + response = yield request + if self.requires_response_body: + response.read() + + try: + request = flow.send(response) + except StopIteration: + break + + async def async_auth_flow(self, request: Request) -> typing.AsyncGenerator[Request, Response]: + """ + Execute the authentication flow asynchronously. + + By default, this defers to `.auth_flow()`. You should override this method + when the authentication scheme does I/O and/or uses concurrency primitives. + """ + if self.requires_request_body: + await request.aread() + + flow = self.auth_flow(request) + request = next(flow) + + while True: + response = yield request + if self.requires_response_body: + await response.aread() + + try: + request = flow.send(response) + except StopIteration: + break + + +class FunctionAuth(Auth): + """ + Allows the 'auth' argument to be passed as a simple callable function, + that takes the request, and returns a new, modified request. + """ + + def __init__(self, func: typing.Callable[[Request], Request]) -> None: + self._func = func + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + yield self._func(request) + + +class BasicAuth(Auth): + """ + Allows the 'auth' argument to be passed as a (username, password) pair, + and uses HTTP Basic authentication. + """ + + def __init__(self, username: str | bytes, password: str | bytes) -> None: + self._auth_header = self._build_auth_header(username, password) + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + request.headers["Authorization"] = self._auth_header + yield request + + def _build_auth_header(self, username: str | bytes, password: str | bytes) -> str: + userpass = b":".join((to_bytes(username), to_bytes(password))) + token = b64encode(userpass).decode() + return f"Basic {token}" + + +class NetRCAuth(Auth): + """ + Use a 'netrc' file to lookup basic auth credentials based on the url host. + """ + + def __init__(self, file: str | None = None) -> None: + # Lazily import 'netrc'. + # There's no need for us to load this module unless 'NetRCAuth' is being used. + import netrc + + self._netrc_info = netrc.netrc(file) + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + auth_info = self._netrc_info.authenticators(request.url.host) + if auth_info is None or not auth_info[2]: + # The netrc file did not have authentication credentials for this host. + yield request + else: + # Build a basic auth header with credentials from the netrc file. + request.headers["Authorization"] = self._build_auth_header(username=auth_info[0], password=auth_info[2]) + yield request + + def _build_auth_header(self, username: str | bytes, password: str | bytes) -> str: + userpass = b":".join((to_bytes(username), to_bytes(password))) + token = b64encode(userpass).decode() + return f"Basic {token}" + + +class DigestAuth(Auth): + _ALGORITHM_TO_HASH_FUNCTION: dict[str, typing.Callable[[bytes], _Hash]] = { + "MD5": hashlib.md5, + "MD5-SESS": hashlib.md5, + "SHA": hashlib.sha1, + "SHA-SESS": hashlib.sha1, + "SHA-256": hashlib.sha256, + "SHA-256-SESS": hashlib.sha256, + "SHA-512": hashlib.sha512, + "SHA-512-SESS": hashlib.sha512, + } + + def __init__(self, username: str | bytes, password: str | bytes) -> None: + self._username = to_bytes(username) + self._password = to_bytes(password) + self._last_challenge: _DigestAuthChallenge | None = None + self._nonce_count = 1 + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + if self._last_challenge: + request.headers["Authorization"] = self._build_auth_header(request, self._last_challenge) + + response = yield request + + if response.status_code != 401 or "www-authenticate" not in response.headers: + # If the response is not a 401 then we don't + # need to build an authenticated request. + return + + for auth_header in response.headers.get_list("www-authenticate"): + if auth_header.lower().startswith("digest "): + break + else: + # If the response does not include a 'WWW-Authenticate: Digest ...' + # header, then we don't need to build an authenticated request. + return + + self._last_challenge = self._parse_challenge(request, response, auth_header) + self._nonce_count = 1 + + request.headers["Authorization"] = self._build_auth_header(request, self._last_challenge) + if response.cookies: + Cookies(response.cookies).set_cookie_header(request=request) + yield request + + def _parse_challenge(self, request: Request, response: Response, auth_header: str) -> _DigestAuthChallenge: + """ + Returns a challenge from a Digest WWW-Authenticate header. + These take the form of: + `Digest realm="realm@host.com",qop="auth,auth-int",nonce="abc",opaque="xyz"` + """ + scheme, _, fields = auth_header.partition(" ") + + # This method should only ever have been called with a Digest auth header. + assert scheme.lower() == "digest" + + header_dict: dict[str, str] = {} + for field in parse_http_list(fields): + key, value = field.strip().split("=", 1) + header_dict[key] = value.strip('"') + + try: + realm = header_dict["realm"].encode() + nonce = header_dict["nonce"].encode() + algorithm = header_dict.get("algorithm", "MD5") + opaque = header_dict["opaque"].encode() if "opaque" in header_dict else None + qop = header_dict["qop"].encode() if "qop" in header_dict else None + return _DigestAuthChallenge(realm=realm, nonce=nonce, algorithm=algorithm, opaque=opaque, qop=qop) + except KeyError as exc: + message = "Malformed Digest WWW-Authenticate header" + raise ProtocolError(message, request=request) from exc + + def _build_auth_header(self, request: Request, challenge: _DigestAuthChallenge) -> str: + hash_func = self._ALGORITHM_TO_HASH_FUNCTION[challenge.algorithm.upper()] + + def digest(data: bytes) -> bytes: + return hash_func(data).hexdigest().encode() + + A1 = b":".join((self._username, challenge.realm, self._password)) + + path = request.url.raw_path + A2 = b":".join((request.method.encode(), path)) + # TODO: implement auth-int + HA2 = digest(A2) + + nc_value = b"%08x" % self._nonce_count + cnonce = self._get_client_nonce(self._nonce_count, challenge.nonce) + self._nonce_count += 1 + + HA1 = digest(A1) + if challenge.algorithm.lower().endswith("-sess"): + HA1 = digest(b":".join((HA1, challenge.nonce, cnonce))) + + qop = self._resolve_qop(challenge.qop, request=request) + if qop is None: + # Following RFC 2069 + digest_data = [HA1, challenge.nonce, HA2] + else: + # Following RFC 2617/7616 + digest_data = [HA1, challenge.nonce, nc_value, cnonce, qop, HA2] + + format_args = { + "username": self._username, + "realm": challenge.realm, + "nonce": challenge.nonce, + "uri": path, + "response": digest(b":".join(digest_data)), + "algorithm": challenge.algorithm.encode(), + } + if challenge.opaque: + format_args["opaque"] = challenge.opaque + if qop: + format_args["qop"] = b"auth" + format_args["nc"] = nc_value + format_args["cnonce"] = cnonce + + return "Digest " + self._get_header_value(format_args) + + def _get_client_nonce(self, nonce_count: int, nonce: bytes) -> bytes: + s = str(nonce_count).encode() + s += nonce + s += time.ctime().encode() + s += os.urandom(8) + + return hashlib.sha1(s).hexdigest()[:16].encode() + + def _get_header_value(self, header_fields: dict[str, bytes]) -> str: + NON_QUOTED_FIELDS = ("algorithm", "qop", "nc") + QUOTED_TEMPLATE = '{}="{}"' + NON_QUOTED_TEMPLATE = "{}={}" + + header_value = "" + for i, (field, value) in enumerate(header_fields.items()): + if i > 0: + header_value += ", " + template = QUOTED_TEMPLATE if field not in NON_QUOTED_FIELDS else NON_QUOTED_TEMPLATE + header_value += template.format(field, to_str(value)) + + return header_value + + def _resolve_qop(self, qop: bytes | None, request: Request) -> bytes | None: + if qop is None: + return None + qops = re.split(b", ?", qop) + if b"auth" in qops: + return b"auth" + + if qops == [b"auth-int"]: + raise NotImplementedError("Digest auth-int support is not yet implemented") + + message = f'Unexpected qop value "{qop!r}" in digest auth' + raise ProtocolError(message, request=request) + + +class _DigestAuthChallenge(typing.NamedTuple): + realm: bytes + nonce: bytes + algorithm: str + opaque: bytes | None + qop: bytes | None diff --git a/venv/lib/python3.12/site-packages/httpx2/_client.py b/venv/lib/python3.12/site-packages/httpx2/_client.py new file mode 100644 index 0000000..2103d36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_client.py @@ -0,0 +1,2237 @@ +from __future__ import annotations + +import datetime +import enum +import logging +import time +import typing +import warnings +from collections.abc import AsyncGenerator, Generator +from contextlib import asynccontextmanager, contextmanager +from types import TracebackType + +from .__version__ import __version__ +from ._auth import Auth, BasicAuth, FunctionAuth +from ._config import ( + DEFAULT_KEEPALIVE_PING_INTERVAL_SECONDS, + DEFAULT_KEEPALIVE_PING_TIMEOUT_SECONDS, + DEFAULT_LIMITS, + DEFAULT_MAX_EVENT_SIZE_BYTES, + DEFAULT_MAX_MESSAGE_SIZE_BYTES, + DEFAULT_MAX_REDIRECTS, + DEFAULT_QUEUE_SIZE, + DEFAULT_TIMEOUT_CONFIG, + Limits, + Proxy, + Timeout, +) +from ._decoders import SUPPORTED_DECODERS +from ._exceptions import ( + InvalidURL, + RemoteProtocolError, + TooManyRedirects, + request_context, +) +from ._models import Cookies, Headers, Request, Response +from ._sse import EventSource +from ._status_codes import codes +from ._transports import AsyncHTTPTransport, HTTPTransport +from ._transports.base import AsyncBaseTransport, BaseTransport +from ._types import ( + AsyncByteStream, + AuthTypes, + CertTypes, + CookieTypes, + HeaderTypes, + ProxyTypes, + QueryParamTypes, + RequestContent, + RequestData, + RequestExtensions, + RequestFiles, + SyncByteStream, + TimeoutTypes, +) +from ._urls import URL, QueryParams +from ._utils import URLPattern, get_environment_proxies + +if typing.TYPE_CHECKING: + import ssl # pragma: no cover + + from .websockets._api import AsyncWebSocketSession, WebSocketSession + +__all__ = ["USE_CLIENT_DEFAULT", "AsyncClient", "Client"] + +# The type annotation for @classmethod and context managers here follows PEP 484 +# https://www.python.org/dev/peps/pep-0484/#annotating-instance-and-class-methods +T = typing.TypeVar("T", bound="Client") +U = typing.TypeVar("U", bound="AsyncClient") + + +def _is_https_redirect(url: URL, location: URL) -> bool: + """ + Return 'True' if 'location' is a HTTPS upgrade of 'url' + """ + origin = url.origin + location_origin = location.origin + + return ( + origin.host == location_origin.host + and origin.scheme == "http" + and origin.port == 80 + and location_origin.scheme == "https" + and location_origin.port == 443 + ) + + +class UseClientDefault: + """ + For some parameters such as `auth=...` and `timeout=...` we need to be able + to indicate the default "unset" state, in a way that is distinctly different + to using `None`. + + The default "unset" state indicates that whatever default is set on the + client should be used. This is different to setting `None`, which + explicitly disables the parameter, possibly overriding a client default. + + For example we use `timeout=USE_CLIENT_DEFAULT` in the `request()` signature. + Omitting the `timeout` parameter will send a request using whatever default + timeout has been configured on the client. Including `timeout=None` will + ensure no timeout is used. + + Note that user code shouldn't need to use the `USE_CLIENT_DEFAULT` constant, + but it is used internally when a parameter is not included. + """ + + +USE_CLIENT_DEFAULT = UseClientDefault() + + +logger = logging.getLogger("httpx2") + +USER_AGENT = f"python-httpx2/{__version__}" +ACCEPT_ENCODING = ", ".join([key for key in SUPPORTED_DECODERS.keys() if key != "identity"]) + + +class ClientState(enum.Enum): + # UNOPENED: + # The client has been instantiated, but has not been used to send a request, + # or been opened by entering the context of a `with` block. + UNOPENED = 1 + # OPENED: + # The client has either sent a request, or is within a `with` block. + OPENED = 2 + # CLOSED: + # The client has either exited the `with` block, or `close()` has + # been called explicitly. + CLOSED = 3 + + +class BoundSyncStream(SyncByteStream): + """ + A byte stream that tracks elapsed time for a response. Once closed, the + elapsed time is available via the `elapsed` attribute, and the response + can read it back from `response.stream.elapsed`. + """ + + def __init__(self, stream: SyncByteStream, start: float) -> None: + self._stream = stream + self._start = start + self.elapsed: datetime.timedelta | None = None + + def __iter__(self) -> typing.Iterator[bytes]: + yield from self._stream + + def close(self) -> None: + self.elapsed = datetime.timedelta(seconds=time.perf_counter() - self._start) + self._stream.close() + + +class BoundAsyncStream(AsyncByteStream): + """ + An async byte stream that tracks elapsed time for a response. Once closed, + the elapsed time is available via the `elapsed` attribute, and the response + can read it back from `response.stream.elapsed`. + """ + + def __init__(self, stream: AsyncByteStream, start: float) -> None: + self._stream = stream + self._start = start + self.elapsed: datetime.timedelta | None = None + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + stream = self._stream.__aiter__() + try: + async for chunk in stream: + yield chunk + finally: + if isinstance(stream, AsyncGenerator): + await stream.aclose() + + async def aclose(self) -> None: + self.elapsed = datetime.timedelta(seconds=time.perf_counter() - self._start) + await self._stream.aclose() + + +EventHook = typing.Callable[..., typing.Any] + + +class BaseClient: + def __init__( + self, + *, + auth: AuthTypes | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + max_redirects: int = DEFAULT_MAX_REDIRECTS, + event_hooks: None | (typing.Mapping[str, list[EventHook]]) = None, + base_url: URL | str = "", + trust_env: bool = True, + default_encoding: str | typing.Callable[[bytes], str | None] = "utf-8", + ) -> None: + event_hooks = {} if event_hooks is None else event_hooks + + self._base_url = self._enforce_trailing_slash(URL(base_url)) + + self._auth = self._build_auth(auth) + self._params = QueryParams(params) + self.headers = Headers(headers) + self._cookies = Cookies(cookies) + self._timeout = Timeout(timeout) + self.follow_redirects = follow_redirects + self.max_redirects = max_redirects + self._event_hooks = { + "request": list(event_hooks.get("request", [])), + "response": list(event_hooks.get("response", [])), + } + self._trust_env = trust_env + self._default_encoding = default_encoding + self._state = ClientState.UNOPENED + + @property + def is_closed(self) -> bool: + """ + Check if the client being closed + """ + return self._state == ClientState.CLOSED + + @property + def trust_env(self) -> bool: + return self._trust_env + + def _enforce_trailing_slash(self, url: URL) -> URL: + if url.raw_path.endswith(b"/"): + return url + return url.copy_with(raw_path=url.raw_path + b"/") + + def _get_proxy_map(self, proxy: ProxyTypes | None, allow_env_proxies: bool) -> dict[str, Proxy | None]: + if proxy is None: + if allow_env_proxies: + return {key: None if url is None else Proxy(url=url) for key, url in get_environment_proxies().items()} + return {} + else: + proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy + return {"all://": proxy} + + @property + def timeout(self) -> Timeout: + return self._timeout + + @timeout.setter + def timeout(self, timeout: TimeoutTypes) -> None: + self._timeout = Timeout(timeout) + + @property + def event_hooks(self) -> dict[str, list[EventHook]]: + return self._event_hooks + + @event_hooks.setter + def event_hooks(self, event_hooks: dict[str, list[EventHook]]) -> None: + self._event_hooks = { + "request": list(event_hooks.get("request", [])), + "response": list(event_hooks.get("response", [])), + } + + @property + def auth(self) -> Auth | None: + """ + Authentication class used when none is passed at the request-level. + + See also [Authentication][0]. + + [0]: quickstart.md#authentication + """ + return self._auth + + @auth.setter + def auth(self, auth: AuthTypes) -> None: + self._auth = self._build_auth(auth) + + @property + def base_url(self) -> URL: + """ + Base URL to use when sending requests with relative URLs. + """ + return self._base_url + + @base_url.setter + def base_url(self, url: URL | str) -> None: + self._base_url = self._enforce_trailing_slash(URL(url)) + + @property + def headers(self) -> Headers: + """ + HTTP headers to include when sending requests. + """ + return self._headers + + @headers.setter + def headers(self, headers: HeaderTypes) -> None: + client_headers = Headers( + { + b"Accept": b"*/*", + b"Accept-Encoding": ACCEPT_ENCODING.encode("ascii"), + b"Connection": b"keep-alive", + b"User-Agent": USER_AGENT.encode("ascii"), + } + ) + client_headers.update(headers) + self._headers = client_headers + + @property + def cookies(self) -> Cookies: + """ + Cookie values to include when sending requests. + """ + return self._cookies + + @cookies.setter + def cookies(self, cookies: CookieTypes) -> None: + self._cookies = Cookies(cookies) + + @property + def params(self) -> QueryParams: + """ + Query parameters to include in the URL when sending requests. + """ + return self._params + + @params.setter + def params(self, params: QueryParamTypes) -> None: + self._params = QueryParams(params) + + def build_request( + self, + method: str, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Request: + """ + Build and return a request instance. + + * The `params`, `headers` and `cookies` arguments + are merged with any values set on the client. + * The `url` argument is merged with any `base_url` set on the client. + + See also: [Request instances][0] + + [0]: advanced/clients.md#request-instances + """ + url = self._merge_url(url) + headers = self._merge_headers(headers) + cookies = self._merge_cookies(cookies) + params = self._merge_queryparams(params) + extensions = {} if extensions is None else extensions + if "timeout" not in extensions: + timeout = self.timeout if isinstance(timeout, UseClientDefault) else Timeout(timeout) + extensions = dict(**extensions, timeout=timeout.as_dict()) + return Request( + method, + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + extensions=extensions, + ) + + def _merge_url(self, url: URL | str) -> URL: + """ + Merge a URL argument together with any 'base_url' on the client, + to create the URL used for the outgoing request. + """ + merge_url = URL(url) + if merge_url.is_relative_url: + # To merge URLs we always append to the base URL. To get this + # behaviour correct we always ensure the base URL ends in a '/' + # separator, and strip any leading '/' from the merge URL. + # + # So, eg... + # + # >>> client = Client(base_url="https://www.example.com/subpath") + # >>> client.base_url + # URL('https://www.example.com/subpath/') + # >>> client.build_request("GET", "/path").url + # URL('https://www.example.com/subpath/path') + merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/") + return self.base_url.copy_with(raw_path=merge_raw_path) + return merge_url + + def _merge_cookies(self, cookies: CookieTypes | None = None) -> CookieTypes | None: + """ + Merge a cookies argument together with any cookies on the client, + to create the cookies used for the outgoing request. + """ + if cookies or self.cookies: + merged_cookies = Cookies(self.cookies) + merged_cookies.update(cookies) + return merged_cookies + return cookies + + def _merge_headers(self, headers: HeaderTypes | None = None) -> HeaderTypes | None: + """ + Merge a headers argument together with any headers on the client, + to create the headers used for the outgoing request. + """ + merged_headers = Headers(self.headers) + merged_headers.update(headers) + return merged_headers + + def _merge_queryparams(self, params: QueryParamTypes | None = None) -> QueryParamTypes | None: + """ + Merge a queryparams argument together with any queryparams on the client, + to create the queryparams used for the outgoing request. + """ + if params or self.params: + merged_queryparams = QueryParams(self.params) + return merged_queryparams.merge(params) + return params + + def _build_auth(self, auth: AuthTypes | None) -> Auth | None: + if auth is None: + return None + elif isinstance(auth, tuple): + return BasicAuth(username=auth[0], password=auth[1]) + elif isinstance(auth, Auth): + return auth + elif callable(auth): + return FunctionAuth(func=auth) + else: + raise TypeError(f'Invalid "auth" argument: {auth!r}') + + def _build_request_auth( + self, + request: Request, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + ) -> Auth: + auth = self._auth if isinstance(auth, UseClientDefault) else self._build_auth(auth) + + if auth is not None: + return auth + + username, password = request.url.username, request.url.password + if username or password: + return BasicAuth(username=username, password=password) + + return Auth() + + def _build_redirect_request(self, request: Request, response: Response) -> Request: + """ + Given a request and a redirect response, return a new request that + should be used to effect the redirect. + """ + method = self._redirect_method(request, response) + url = self._redirect_url(request, response) + headers = self._redirect_headers(request, url, method) + stream = self._redirect_stream(request, method) + cookies = Cookies(self.cookies) + return Request( + method=method, + url=url, + headers=headers, + cookies=cookies, + stream=stream, + extensions=request.extensions, + ) + + def _redirect_method(self, request: Request, response: Response) -> str: + """ + When being redirected we may want to change the method of the request + based on certain specs or browser behavior. + """ + method = request.method + + # https://tools.ietf.org/html/rfc7231#section-6.4.4 + if response.status_code == codes.SEE_OTHER and method != "HEAD": + method = "GET" + + # Do what the browsers do, despite standards... + # Turn 302s into GETs. + # QUERY is excluded, per RFC 10008 Section 2.5. + # https://datatracker.ietf.org/doc/html/rfc10008#section-2.5 + if response.status_code == codes.FOUND and method not in ("HEAD", "QUERY"): + method = "GET" + + # If a POST is responded to with a 301, turn it into a GET. + # This bizarre behaviour is explained in 'requests' issue 1704. + if response.status_code == codes.MOVED_PERMANENTLY and method == "POST": + method = "GET" + + return method + + def _redirect_url(self, request: Request, response: Response) -> URL: + """ + Return the URL for the redirect to follow. + """ + location = response.headers["Location"] + + try: + url = URL(location) + except InvalidURL as exc: + raise RemoteProtocolError(f"Invalid URL in location header: {exc}.", request=request) from None + + # Handle malformed 'Location' headers that are "absolute" form, have no host. + # See: https://github.com/encode/httpx/issues/771 + if url.scheme and not url.host: + url = url.copy_with(host=request.url.host) + + # Facilitate relative 'Location' headers, as allowed by RFC 7231. + # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') + if url.is_relative_url: + url = request.url.join(url) + + # Attach previous fragment if needed (RFC 7231 7.1.2) + if request.url.fragment and not url.fragment: + url = url.copy_with(fragment=request.url.fragment) + + return url + + def _redirect_headers(self, request: Request, url: URL, method: str) -> Headers: + """ + Return the headers that should be used for the redirect request. + """ + headers = Headers(request.headers) + + if url.origin != request.url.origin: + if not _is_https_redirect(request.url, url): + # Strip Authorization headers when responses are redirected + # away from the origin. (Except for direct HTTP to HTTPS redirects.) + headers.pop("Authorization", None) + + # Update the Host header. + headers["Host"] = url.netloc.decode("ascii") + + if method != request.method and method == "GET": + # If we've switch to a 'GET' request, then strip any headers which + # are only relevant to the request body. + headers.pop("Content-Length", None) + headers.pop("Transfer-Encoding", None) + + # We should use the client cookie store to determine any cookie header, + # rather than whatever was on the original outgoing request. + headers.pop("Cookie", None) + + return headers + + def _redirect_stream(self, request: Request, method: str) -> SyncByteStream | AsyncByteStream | None: + """ + Return the body that should be used for the redirect request. + """ + if method != request.method and method == "GET": + return None + + return request.stream + + def _set_timeout(self, request: Request) -> None: + if "timeout" not in request.extensions: + timeout = self.timeout if isinstance(self.timeout, UseClientDefault) else Timeout(self.timeout) + request.extensions = dict(**request.extensions, timeout=timeout.as_dict()) + + +class Client(BaseClient): + """ + An HTTP client, with connection pooling, HTTP/2, redirects, cookie persistence, etc. + + It can be shared between threads. + + Usage: + + ```python + >>> client = httpx2.Client() + >>> response = client.get('https://example.org') + ``` + + **Parameters:** + + * **auth** - *(optional)* An authentication class to use when sending + requests. + * **params** - *(optional)* Query parameters to include in request URLs, as + a string, dictionary, or sequence of two-tuples. + * **headers** - *(optional)* Dictionary of HTTP headers to include when + sending requests. + * **cookies** - *(optional)* Dictionary of Cookie items to include when + sending requests. + * **verify** - *(optional)* Either `True` to use an SSL context with the + default CA bundle, `False` to disable verification, or an instance of + `ssl.SSLContext` to use a custom context. + * **http2** - *(optional)* A boolean indicating if HTTP/2 support should be + enabled. Defaults to `False`. + * **proxy** - *(optional)* A proxy URL where all the traffic should be routed. + * **mounts** - *(optional)* A dictionary mapping URL patterns to transports, + used to route requests through specific transports based on the URL. + * **timeout** - *(optional)* The timeout configuration to use when sending + requests. + * **limits** - *(optional)* The limits configuration to use. + * **max_redirects** - *(optional)* The maximum number of redirect responses + that should be followed. + * **base_url** - *(optional)* A URL to use as the base when building + request URLs. + * **transport** - *(optional)* A transport class to use for sending requests + over the network. + * **trust_env** - *(optional)* Enables or disables usage of environment + variables for configuration. + * **default_encoding** - *(optional)* The default encoding to use for decoding + response text, if no charset information is included in a response Content-Type + header. Set to a callable for automatic character set detection. Default: "utf-8". + """ + + def __init__( + self, + *, + auth: AuthTypes | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + proxy: ProxyTypes | None = None, + mounts: None | (typing.Mapping[str, BaseTransport | None]) = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + limits: Limits = DEFAULT_LIMITS, + max_redirects: int = DEFAULT_MAX_REDIRECTS, + event_hooks: None | (typing.Mapping[str, list[EventHook]]) = None, + base_url: URL | str = "", + transport: BaseTransport | None = None, + default_encoding: str | typing.Callable[[bytes], str | None] = "utf-8", + ) -> None: + super().__init__( + auth=auth, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + follow_redirects=follow_redirects, + max_redirects=max_redirects, + event_hooks=event_hooks, + base_url=base_url, + trust_env=trust_env, + default_encoding=default_encoding, + ) + + if http2: + try: + import h2 # noqa + except ImportError: # pragma: no cover + raise ImportError( + "Using http2=True, but the 'h2' package is not installed. " + "Make sure to install httpx using `pip install httpx[http2]`." + ) from None + + allow_env_proxies = trust_env and transport is None + proxy_map = self._get_proxy_map(proxy, allow_env_proxies) + + self._transport = self._init_transport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + transport=transport, + ) + self._mounts: dict[URLPattern, BaseTransport | None] = { + URLPattern(key): None + if proxy is None + else self._init_proxy_transport( + proxy, + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + ) + for key, proxy in proxy_map.items() + } + if mounts is not None: + self._mounts.update({URLPattern(key): transport for key, transport in mounts.items()}) + + self._mounts = dict(sorted(self._mounts.items())) + + def _init_transport( + self, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + transport: BaseTransport | None = None, + ) -> BaseTransport: + if transport is not None: + return transport + + return HTTPTransport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + ) + + def _init_proxy_transport( + self, + proxy: Proxy, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + ) -> BaseTransport: + return HTTPTransport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + proxy=proxy, + ) + + def _transport_for_url(self, url: URL) -> BaseTransport: + """ + Returns the transport instance that should be used for a given URL. + This will either be the standard connection pool, or a proxy. + """ + for pattern, transport in self._mounts.items(): + if pattern.matches(url): + return self._transport if transport is None else transport + + return self._transport + + def request( + self, + method: str, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Build and send a request. + + Equivalent to: + + ```python + request = client.build_request(...) + response = client.send(request, ...) + ``` + + See `Client.build_request()`, `Client.send()` and + [Merging of configuration][0] for how the various parameters + are merged with client-level configuration. + + [0]: advanced/clients.md#merging-of-configuration + """ + if cookies is not None: + message = ( + "Setting per-request cookies=<...> is being deprecated, because " + "the expected behaviour on cookie persistence is ambiguous. Set " + "cookies directly on the client instance instead." + ) + warnings.warn(message, DeprecationWarning, stacklevel=2) + + request = self.build_request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + extensions=extensions, + ) + return self.send(request, auth=auth, follow_redirects=follow_redirects) + + @contextmanager + def stream( + self, + method: str, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Generator[Response]: + """ + Alternative to `httpx2.request()` that streams the response body + instead of loading it into memory at once. + + **Parameters**: See `httpx2.request`. + + See also: [Streaming Responses][0] + + [0]: quickstart.md#streaming-responses + """ + request = self.build_request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + extensions=extensions, + ) + response = self.send( + request=request, + auth=auth, + follow_redirects=follow_redirects, + stream=True, + ) + try: + yield response + finally: + response.close() + + @contextmanager + def sse( + self, + url: URL | str, + *, + method: str = "GET", + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + max_event_size: int | None = DEFAULT_MAX_EVENT_SIZE_BYTES, + ) -> Generator[EventSource]: + """ + Connect to a server-sent events endpoint and yield an `EventSource`. + + Iterating the `EventSource` yields `ServerSentEvent` instances. + + **Parameters**: See `httpx2.request`. + """ + with self.stream( + method, + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers={"Accept": "text/event-stream", "Cache-Control": "no-store"} | Headers(headers), + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) as response: + yield EventSource(response, max_event_size=max_event_size) + + @contextmanager + def websocket( + self, + url: URL | str, + *, + max_message_size_bytes: int = DEFAULT_MAX_MESSAGE_SIZE_BYTES, + queue_size: int = DEFAULT_QUEUE_SIZE, + keepalive_ping_interval_seconds: float | None = DEFAULT_KEEPALIVE_PING_INTERVAL_SECONDS, + keepalive_ping_timeout_seconds: float | None = DEFAULT_KEEPALIVE_PING_TIMEOUT_SECONDS, + subprotocols: list[str] | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Generator[WebSocketSession]: + """ + Open a WebSocket session, using this client's configuration. + + The session is closed automatically when exiting the context manager. + + ```python + with httpx2.Client() as client: + with client.websocket("ws://localhost:8000/ws") as ws: + ws.send_text("Hello!") + message = ws.receive_text() + ``` + """ + try: + from .websockets._api import connect_ws + except ImportError: # pragma: no cover + raise ImportError( + "WebSocket support requires the `wsproto` package. Install it with `pip install httpx2[ws]`." + ) + + with connect_ws( + str(url), + self, + max_message_size_bytes=max_message_size_bytes, + queue_size=queue_size, + keepalive_ping_interval_seconds=keepalive_ping_interval_seconds, + keepalive_ping_timeout_seconds=keepalive_ping_timeout_seconds, + subprotocols=subprotocols, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) as session: + yield session + + def send( + self, + request: Request, + *, + stream: bool = False, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + ) -> Response: + """ + Send a request. + + The request is sent as-is, unmodified. + + Typically you'll want to build one with `Client.build_request()` + so that any client-level configuration is merged into the request, + but passing an explicit `httpx2.Request()` is supported as well. + + See also: [Request instances][0] + + [0]: advanced/clients.md#request-instances + """ + if self._state == ClientState.CLOSED: + raise RuntimeError("Cannot send a request, as the client has been closed.") + + self._state = ClientState.OPENED + follow_redirects = self.follow_redirects if isinstance(follow_redirects, UseClientDefault) else follow_redirects + + self._set_timeout(request) + + auth = self._build_request_auth(request, auth) + + response = self._send_handling_auth( + request, + auth=auth, + follow_redirects=follow_redirects, + history=[], + ) + try: + if not stream: + response.read() + + return response + + except BaseException as exc: + response.close() + raise exc + + def _send_handling_auth( + self, + request: Request, + auth: Auth, + follow_redirects: bool, + history: list[Response], + ) -> Response: + auth_flow = auth.sync_auth_flow(request) + try: + request = next(auth_flow) + + while True: + response = self._send_handling_redirects( + request, + follow_redirects=follow_redirects, + history=history, + ) + try: + try: + next_request = auth_flow.send(response) + except StopIteration: + return response + + response.history = list(history) + response.read() + request = next_request + history.append(response) + + except BaseException as exc: + response.close() + raise exc + finally: + auth_flow.close() + + def _send_handling_redirects( + self, + request: Request, + follow_redirects: bool, + history: list[Response], + ) -> Response: + while True: + if len(history) > self.max_redirects: + raise TooManyRedirects("Exceeded maximum allowed redirects.", request=request) + + for hook in self._event_hooks["request"]: + hook(request) + + response = self._send_single_request(request) + try: + for hook in self._event_hooks["response"]: + hook(response) + response.history = list(history) + + if not response.has_redirect_location: + return response + + request = self._build_redirect_request(request, response) + history = history + [response] + + if follow_redirects: + response.read() + else: + response.next_request = request + return response + + except BaseException as exc: + response.close() + raise exc + + def _send_single_request(self, request: Request) -> Response: + """ + Sends a single request, without handling any redirections. + """ + transport = self._transport_for_url(request.url) + start = time.perf_counter() + + if not isinstance(request.stream, SyncByteStream): + raise RuntimeError("Attempted to send an async request with a sync Client instance.") + + with request_context(request=request): + response = transport.handle_request(request) + + assert isinstance(response.stream, SyncByteStream) + + response.request = request + response.stream = BoundSyncStream(response.stream, start=start) + self.cookies.extract_cookies(response) + response.default_encoding = self._default_encoding + + logger.info( + 'HTTP Request: %s %s "%s %d %s"', + request.method, + request.url, + response.http_version, + response.status_code, + response.reason_phrase, + ) + + return response + + def get( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `GET` request. + + **Parameters**: See `httpx2.request`. + """ + return self.request( + "GET", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def options( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send an `OPTIONS` request. + + **Parameters**: See `httpx2.request`. + """ + return self.request( + "OPTIONS", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def head( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `HEAD` request. + + **Parameters**: See `httpx2.request`. + """ + return self.request( + "HEAD", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def post( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `POST` request. + + **Parameters**: See `httpx2.request`. + """ + return self.request( + "POST", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def put( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `PUT` request. + + **Parameters**: See `httpx2.request`. + """ + return self.request( + "PUT", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def patch( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `PATCH` request. + + **Parameters**: See `httpx2.request`. + """ + return self.request( + "PATCH", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def delete( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `DELETE` request. + + **Parameters**: See `httpx2.request`. + """ + return self.request( + "DELETE", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def query( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `QUERY` request. + + **Parameters**: See `httpx2.request`. + """ + return self.request( + "QUERY", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def close(self) -> None: + """ + Close transport and proxies. + """ + if self._state != ClientState.CLOSED: + self._state = ClientState.CLOSED + + self._transport.close() + for transport in self._mounts.values(): + if transport is not None: + transport.close() + + def __enter__(self: T) -> T: + if self._state != ClientState.UNOPENED: + msg = { + ClientState.OPENED: "Cannot open a client instance more than once.", + ClientState.CLOSED: ("Cannot reopen a client instance, once it has been closed."), + }[self._state] + raise RuntimeError(msg) + + self._state = ClientState.OPENED + + self._transport.__enter__() + for transport in self._mounts.values(): + if transport is not None: + transport.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + self._state = ClientState.CLOSED + + self._transport.__exit__(exc_type, exc_value, traceback) + for transport in self._mounts.values(): + if transport is not None: + transport.__exit__(exc_type, exc_value, traceback) + + +class AsyncClient(BaseClient): + """ + An asynchronous HTTP client, with connection pooling, HTTP/2, redirects, + cookie persistence, etc. + + It can be shared between tasks. + + Usage: + + ```python + >>> async with httpx2.AsyncClient() as client: + >>> response = await client.get('https://example.org') + ``` + + **Parameters:** + + * **auth** - *(optional)* An authentication class to use when sending + requests. + * **params** - *(optional)* Query parameters to include in request URLs, as + a string, dictionary, or sequence of two-tuples. + * **headers** - *(optional)* Dictionary of HTTP headers to include when + sending requests. + * **cookies** - *(optional)* Dictionary of Cookie items to include when + sending requests. + * **verify** - *(optional)* Either `True` to use an SSL context with the + default CA bundle, `False` to disable verification, or an instance of + `ssl.SSLContext` to use a custom context. + * **http2** - *(optional)* A boolean indicating if HTTP/2 support should be + enabled. Defaults to `False`. + * **proxy** - *(optional)* A proxy URL where all the traffic should be routed. + * **mounts** - *(optional)* A dictionary mapping URL patterns to transports, + used to route requests through specific transports based on the URL. + * **timeout** - *(optional)* The timeout configuration to use when sending + requests. + * **limits** - *(optional)* The limits configuration to use. + * **max_redirects** - *(optional)* The maximum number of redirect responses + that should be followed. + * **base_url** - *(optional)* A URL to use as the base when building + request URLs. + * **transport** - *(optional)* A transport class to use for sending requests + over the network. + * **trust_env** - *(optional)* Enables or disables usage of environment + variables for configuration. + * **default_encoding** - *(optional)* The default encoding to use for decoding + response text, if no charset information is included in a response Content-Type + header. Set to a callable for automatic character set detection. Default: "utf-8". + """ + + def __init__( + self, + *, + auth: AuthTypes | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + http1: bool = True, + http2: bool = False, + proxy: ProxyTypes | None = None, + mounts: None | (typing.Mapping[str, AsyncBaseTransport | None]) = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + limits: Limits = DEFAULT_LIMITS, + max_redirects: int = DEFAULT_MAX_REDIRECTS, + event_hooks: None | (typing.Mapping[str, list[EventHook]]) = None, + base_url: URL | str = "", + transport: AsyncBaseTransport | None = None, + trust_env: bool = True, + default_encoding: str | typing.Callable[[bytes], str | None] = "utf-8", + ) -> None: + super().__init__( + auth=auth, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + follow_redirects=follow_redirects, + max_redirects=max_redirects, + event_hooks=event_hooks, + base_url=base_url, + trust_env=trust_env, + default_encoding=default_encoding, + ) + + if http2: + try: + import h2 # noqa + except ImportError: # pragma: no cover + raise ImportError( + "Using http2=True, but the 'h2' package is not installed. " + "Make sure to install httpx using `pip install httpx[http2]`." + ) from None + + allow_env_proxies = trust_env and transport is None + proxy_map = self._get_proxy_map(proxy, allow_env_proxies) + + self._transport = self._init_transport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + transport=transport, + ) + + self._mounts: dict[URLPattern, AsyncBaseTransport | None] = { + URLPattern(key): None + if proxy is None + else self._init_proxy_transport( + proxy, + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + ) + for key, proxy in proxy_map.items() + } + if mounts is not None: + self._mounts.update({URLPattern(key): transport for key, transport in mounts.items()}) + self._mounts = dict(sorted(self._mounts.items())) + + def _init_transport( + self, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + transport: AsyncBaseTransport | None = None, + ) -> AsyncBaseTransport: + if transport is not None: + return transport + + return AsyncHTTPTransport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + ) + + def _init_proxy_transport( + self, + proxy: Proxy, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + ) -> AsyncBaseTransport: + return AsyncHTTPTransport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + proxy=proxy, + ) + + def _transport_for_url(self, url: URL) -> AsyncBaseTransport: + """ + Returns the transport instance that should be used for a given URL. + This will either be the standard connection pool, or a proxy. + """ + for pattern, transport in self._mounts.items(): + if pattern.matches(url): + return self._transport if transport is None else transport + + return self._transport + + async def request( + self, + method: str, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Build and send a request. + + Equivalent to: + + ```python + request = client.build_request(...) + response = await client.send(request, ...) + ``` + + See `AsyncClient.build_request()`, `AsyncClient.send()` + and [Merging of configuration][0] for how the various parameters + are merged with client-level configuration. + + [0]: advanced/clients.md#merging-of-configuration + """ + + if cookies is not None: # pragma: no cover + message = ( + "Setting per-request cookies=<...> is being deprecated, because " + "the expected behaviour on cookie persistence is ambiguous. Set " + "cookies directly on the client instance instead." + ) + warnings.warn(message, DeprecationWarning, stacklevel=2) + + request = self.build_request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + extensions=extensions, + ) + return await self.send(request, auth=auth, follow_redirects=follow_redirects) + + @asynccontextmanager + async def stream( + self, + method: str, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> AsyncGenerator[Response]: + """ + Alternative to `httpx2.request()` that streams the response body + instead of loading it into memory at once. + + **Parameters**: See `httpx2.request`. + + See also: [Streaming Responses][0] + + [0]: quickstart.md#streaming-responses + """ + request = self.build_request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + extensions=extensions, + ) + response = await self.send( + request=request, + auth=auth, + follow_redirects=follow_redirects, + stream=True, + ) + try: + yield response + finally: + await response.aclose() + + @asynccontextmanager + async def sse( + self, + url: URL | str, + *, + method: str = "GET", + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + max_event_size: int | None = DEFAULT_MAX_EVENT_SIZE_BYTES, + ) -> AsyncGenerator[EventSource]: + """ + Connect to a server-sent events endpoint and yield an `EventSource`. + + Iterating the `EventSource` yields `ServerSentEvent` instances. + + **Parameters**: See `httpx2.request`. + """ + async with self.stream( + method, + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers={"Accept": "text/event-stream", "Cache-Control": "no-store"} | Headers(headers), + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) as response: + yield EventSource(response, max_event_size=max_event_size) + + @asynccontextmanager + async def websocket( + self, + url: URL | str, + *, + max_message_size_bytes: int = DEFAULT_MAX_MESSAGE_SIZE_BYTES, + queue_size: int = DEFAULT_QUEUE_SIZE, + keepalive_ping_interval_seconds: float | None = DEFAULT_KEEPALIVE_PING_INTERVAL_SECONDS, + keepalive_ping_timeout_seconds: float | None = DEFAULT_KEEPALIVE_PING_TIMEOUT_SECONDS, + subprotocols: list[str] | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> AsyncGenerator[AsyncWebSocketSession]: + """ + Open a WebSocket session, using this client's configuration. + + The session is closed automatically when exiting the context manager. + + ```python + async with httpx2.AsyncClient() as client: + async with client.websocket("ws://localhost:8000/ws") as ws: + await ws.send_text("Hello!") + message = await ws.receive_text() + ``` + """ + try: + from .websockets._api import aconnect_ws + except ImportError: # pragma: no cover + raise ImportError( + "WebSocket support requires the `wsproto` package. Install it with `pip install httpx2[ws]`." + ) + + async with aconnect_ws( + str(url), + self, + max_message_size_bytes=max_message_size_bytes, + queue_size=queue_size, + keepalive_ping_interval_seconds=keepalive_ping_interval_seconds, + keepalive_ping_timeout_seconds=keepalive_ping_timeout_seconds, + subprotocols=subprotocols, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) as session: + yield session + + async def send( + self, + request: Request, + *, + stream: bool = False, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + ) -> Response: + """ + Send a request. + + The request is sent as-is, unmodified. + + Typically you'll want to build one with `AsyncClient.build_request()` + so that any client-level configuration is merged into the request, + but passing an explicit `httpx2.Request()` is supported as well. + + See also: [Request instances][0] + + [0]: advanced/clients.md#request-instances + """ + if self._state == ClientState.CLOSED: + raise RuntimeError("Cannot send a request, as the client has been closed.") + + self._state = ClientState.OPENED + follow_redirects = self.follow_redirects if isinstance(follow_redirects, UseClientDefault) else follow_redirects + + self._set_timeout(request) + + auth = self._build_request_auth(request, auth) + + response = await self._send_handling_auth( + request, + auth=auth, + follow_redirects=follow_redirects, + history=[], + ) + try: + if not stream: + await response.aread() + + return response + + except BaseException as exc: + await response.aclose() + raise exc + + async def _send_handling_auth( + self, + request: Request, + auth: Auth, + follow_redirects: bool, + history: list[Response], + ) -> Response: + auth_flow = auth.async_auth_flow(request) + try: + request = await auth_flow.__anext__() + + while True: + response = await self._send_handling_redirects( + request, + follow_redirects=follow_redirects, + history=history, + ) + try: + try: + next_request = await auth_flow.asend(response) + except StopAsyncIteration: + return response + + response.history = list(history) + await response.aread() + request = next_request + history.append(response) + + except BaseException as exc: + await response.aclose() + raise exc + finally: + await auth_flow.aclose() + + async def _send_handling_redirects( + self, + request: Request, + follow_redirects: bool, + history: list[Response], + ) -> Response: + while True: + if len(history) > self.max_redirects: + raise TooManyRedirects("Exceeded maximum allowed redirects.", request=request) + + for hook in self._event_hooks["request"]: + await hook(request) + + response = await self._send_single_request(request) + try: + for hook in self._event_hooks["response"]: + await hook(response) + + response.history = list(history) + + if not response.has_redirect_location: + return response + + request = self._build_redirect_request(request, response) + history = history + [response] + + if follow_redirects: + await response.aread() + else: + response.next_request = request + return response + + except BaseException as exc: + await response.aclose() + raise exc + + async def _send_single_request(self, request: Request) -> Response: + """ + Sends a single request, without handling any redirections. + """ + transport = self._transport_for_url(request.url) + start = time.perf_counter() + + if not isinstance(request.stream, AsyncByteStream): + raise RuntimeError("Attempted to send a sync request with an AsyncClient instance.") + + with request_context(request=request): + response = await transport.handle_async_request(request) + + assert isinstance(response.stream, AsyncByteStream) + response.request = request + response.stream = BoundAsyncStream(response.stream, start=start) + self.cookies.extract_cookies(response) + response.default_encoding = self._default_encoding + + logger.info( + 'HTTP Request: %s %s "%s %d %s"', + request.method, + request.url, + response.http_version, + response.status_code, + response.reason_phrase, + ) + + return response + + async def get( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `GET` request. + + **Parameters**: See `httpx2.request`. + """ + return await self.request( + "GET", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def options( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send an `OPTIONS` request. + + **Parameters**: See `httpx2.request`. + """ + return await self.request( + "OPTIONS", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def head( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `HEAD` request. + + **Parameters**: See `httpx2.request`. + """ + return await self.request( + "HEAD", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def post( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `POST` request. + + **Parameters**: See `httpx2.request`. + """ + return await self.request( + "POST", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def put( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `PUT` request. + + **Parameters**: See `httpx2.request`. + """ + return await self.request( + "PUT", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def patch( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `PATCH` request. + + **Parameters**: See `httpx2.request`. + """ + return await self.request( + "PATCH", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def delete( + self, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `DELETE` request. + + **Parameters**: See `httpx2.request`. + """ + return await self.request( + "DELETE", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def query( + self, + url: URL | str, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `QUERY` request. + + **Parameters**: See `httpx2.request`. + """ + return await self.request( + "QUERY", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def aclose(self) -> None: + """ + Close transport and proxies. + """ + if self._state != ClientState.CLOSED: + self._state = ClientState.CLOSED + + await self._transport.aclose() + for proxy in self._mounts.values(): + if proxy is not None: + await proxy.aclose() + + async def __aenter__(self: U) -> U: + if self._state != ClientState.UNOPENED: + msg = { + ClientState.OPENED: "Cannot open a client instance more than once.", + ClientState.CLOSED: ("Cannot reopen a client instance, once it has been closed."), + }[self._state] + raise RuntimeError(msg) + + self._state = ClientState.OPENED + + await self._transport.__aenter__() + for proxy in self._mounts.values(): + if proxy is not None: + await proxy.__aenter__() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + self._state = ClientState.CLOSED + + await self._transport.__aexit__(exc_type, exc_value, traceback) + for proxy in self._mounts.values(): + if proxy is not None: + await proxy.__aexit__(exc_type, exc_value, traceback) diff --git a/venv/lib/python3.12/site-packages/httpx2/_config.py b/venv/lib/python3.12/site-packages/httpx2/_config.py new file mode 100644 index 0000000..ed3e29f --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_config.py @@ -0,0 +1,227 @@ +from __future__ import annotations + +import dataclasses +import os +import typing + +from ._models import Headers +from ._types import CertTypes, HeaderTypes, TimeoutTypes +from ._urls import URL + +if typing.TYPE_CHECKING: + import ssl # pragma: no cover + +__all__ = ["Limits", "Proxy", "Timeout", "create_ssl_context"] + + +class UnsetType: + pass # pragma: no cover + + +UNSET = UnsetType() + + +def create_ssl_context( + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, +) -> ssl.SSLContext: + import ssl + import warnings + + import truststore + + if verify is True: + if trust_env and os.environ.get("SSL_CERT_FILE"): # pragma: no cover + ctx = ssl.create_default_context(cafile=os.environ["SSL_CERT_FILE"]) + elif trust_env and os.environ.get("SSL_CERT_DIR"): # pragma: no cover + ctx = ssl.create_default_context(capath=os.environ["SSL_CERT_DIR"]) + else: + # Default case: rely on the system trust store via `truststore`. + ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + elif verify is False: + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + elif isinstance(verify, str): + if cert: + raise TypeError( + "`verify=` cannot be combined with `cert=...`. " + "Build an `ssl.SSLContext` and pass it as `verify=`, " + "using `.load_cert_chain()` to configure the certificate chain." + ) + message = ( + "`verify=` is deprecated. " + "Use `verify=ssl.create_default_context(cafile=...)` " + "or `verify=ssl.create_default_context(capath=...)` instead." + ) + warnings.warn(message, DeprecationWarning) + if os.path.isdir(verify): # pragma: no cover + return ssl.create_default_context(capath=verify) + return ssl.create_default_context(cafile=verify) + else: + ctx = verify + + if cert: # pragma: no cover + message = ( + "`cert=...` is deprecated. Use `verify=` instead," + "with `.load_cert_chain()` to configure the certificate chain." + ) + warnings.warn(message, DeprecationWarning) + if isinstance(cert, str): + ctx.load_cert_chain(cert) + else: + ctx.load_cert_chain(*cert) + + return ctx + + +@dataclasses.dataclass(init=False, repr=False) +class Timeout: + """ + Timeout configuration. + + **Usage**: + + Timeout(None) # No timeouts. + Timeout(5.0) # 5s timeout on all operations. + Timeout(None, connect=5.0) # 5s timeout on connect, no other timeouts. + Timeout(5.0, connect=10.0) # 10s timeout on connect. 5s timeout elsewhere. + Timeout(5.0, pool=None) # No timeout on acquiring connection from pool. + # 5s timeout elsewhere. + """ + + connect: float | None + read: float | None + write: float | None + pool: float | None + + def __init__( + self, + timeout: TimeoutTypes | UnsetType = UNSET, + *, + connect: None | float | UnsetType = UNSET, + read: None | float | UnsetType = UNSET, + write: None | float | UnsetType = UNSET, + pool: None | float | UnsetType = UNSET, + ) -> None: + if isinstance(timeout, Timeout): + # Passed as a single explicit Timeout. + assert connect is UNSET + assert read is UNSET + assert write is UNSET + assert pool is UNSET + self.connect = timeout.connect + self.read = timeout.read + self.write = timeout.write + self.pool = timeout.pool + elif isinstance(timeout, tuple): + # Passed as a tuple. + self.connect = timeout[0] + self.read = timeout[1] + self.write = None if len(timeout) < 3 else timeout[2] + self.pool = None if len(timeout) < 4 else timeout[3] + elif not ( + isinstance(connect, UnsetType) + or isinstance(read, UnsetType) + or isinstance(write, UnsetType) + or isinstance(pool, UnsetType) + ): + self.connect = connect + self.read = read + self.write = write + self.pool = pool + else: + if isinstance(timeout, UnsetType): + raise ValueError("httpx2.Timeout must either include a default, or set all four parameters explicitly.") + self.connect = timeout if isinstance(connect, UnsetType) else connect + self.read = timeout if isinstance(read, UnsetType) else read + self.write = timeout if isinstance(write, UnsetType) else write + self.pool = timeout if isinstance(pool, UnsetType) else pool + + def as_dict(self) -> dict[str, float | None]: + return { + "connect": self.connect, + "read": self.read, + "write": self.write, + "pool": self.pool, + } + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + if len({self.connect, self.read, self.write, self.pool}) == 1: + return f"{class_name}(timeout={self.connect})" + return f"{class_name}(connect={self.connect}, read={self.read}, write={self.write}, pool={self.pool})" + + +@dataclasses.dataclass(kw_only=True) +class Limits: + """ + Configuration for limits to various client behaviors. + + **Parameters:** + + * **max_connections** - The maximum number of concurrent connections that may be + established. + * **max_keepalive_connections** - Allow the connection pool to maintain + keep-alive connections below this point. Should be less than or equal + to `max_connections`. + * **keepalive_expiry** - Time limit on idle keep-alive connections in seconds. + """ + + max_connections: int | None = None + max_keepalive_connections: int | None = None + keepalive_expiry: float | None = 5.0 + + +class Proxy: + def __init__( + self, + url: URL | str, + *, + ssl_context: ssl.SSLContext | None = None, + auth: tuple[str, str] | None = None, + headers: HeaderTypes | None = None, + ) -> None: + url = URL(url) + headers = Headers(headers) + + if url.scheme not in ("http", "https", "socks5", "socks5h"): + raise ValueError(f"Unknown scheme for proxy URL {url!r}") + + if url.username or url.password: + # Remove any auth credentials from the URL. + auth = (url.username, url.password) + url = url.copy_with(username=None, password=None) + + self.url = url + self.auth = auth + self.headers = headers + self.ssl_context = ssl_context + + @property + def raw_auth(self) -> tuple[bytes, bytes] | None: + # The proxy authentication as raw bytes. + return None if self.auth is None else (self.auth[0].encode("utf-8"), self.auth[1].encode("utf-8")) + + def __repr__(self) -> str: + # The authentication is represented with the password component masked. + auth = (self.auth[0], "********") if self.auth else None + + # Build a nice concise representation. + url_str = f"{str(self.url)!r}" + auth_str = f", auth={auth!r}" if auth else "" + headers_str = f", headers={dict(self.headers)!r}" if self.headers else "" + return f"Proxy({url_str}{auth_str}{headers_str})" + + +DEFAULT_TIMEOUT_CONFIG = Timeout(timeout=5.0) +DEFAULT_LIMITS = Limits(max_connections=100, max_keepalive_connections=20) +DEFAULT_MAX_REDIRECTS = 20 + +DEFAULT_MAX_EVENT_SIZE_BYTES = 1024 * 1024 + +DEFAULT_MAX_MESSAGE_SIZE_BYTES = 65_536 +DEFAULT_QUEUE_SIZE = 512 +DEFAULT_KEEPALIVE_PING_INTERVAL_SECONDS = 20.0 +DEFAULT_KEEPALIVE_PING_TIMEOUT_SECONDS = 20.0 diff --git a/venv/lib/python3.12/site-packages/httpx2/_content.py b/venv/lib/python3.12/site-packages/httpx2/_content.py new file mode 100644 index 0000000..3acb257 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_content.py @@ -0,0 +1,237 @@ +from __future__ import annotations + +import inspect +import warnings +from collections.abc import AsyncGenerator, AsyncIterable, AsyncIterator, Iterable, Iterator, Mapping +from json import dumps as json_dumps +from typing import ( + Any, +) +from urllib.parse import urlencode + +from ._exceptions import StreamClosed, StreamConsumed +from ._multipart import MultipartStream +from ._types import ( + AsyncByteStream, + RequestContent, + RequestData, + RequestFiles, + ResponseContent, + SyncByteStream, +) +from ._utils import peek_filelike_remaining_length, primitive_value_to_str + +__all__ = ["ByteStream"] + + +class ByteStream(AsyncByteStream, SyncByteStream): + def __init__(self, stream: bytes) -> None: + self._stream = stream + + def __iter__(self) -> Iterator[bytes]: + yield self._stream + + async def __aiter__(self) -> AsyncIterator[bytes]: + yield self._stream + + +class IteratorByteStream(SyncByteStream): + CHUNK_SIZE = 65_536 + + def __init__(self, stream: Iterable[bytes]) -> None: + self._stream = stream + self._is_stream_consumed = False + self._is_generator = inspect.isgenerator(stream) + + def __iter__(self) -> Iterator[bytes]: + if self._is_stream_consumed and self._is_generator: + raise StreamConsumed() + + self._is_stream_consumed = True + if hasattr(self._stream, "read"): + # File-like interfaces should use 'read' directly. + chunk = self._stream.read(self.CHUNK_SIZE) + while chunk: + yield chunk + chunk = self._stream.read(self.CHUNK_SIZE) + else: + # Otherwise iterate. + yield from self._stream + + +class AsyncIteratorByteStream(AsyncByteStream): + CHUNK_SIZE = 65_536 + + def __init__(self, stream: AsyncIterable[bytes]) -> None: + self._stream = stream + self._is_stream_consumed = False + self._is_generator = inspect.isasyncgen(stream) + + async def __aiter__(self) -> AsyncIterator[bytes]: + if self._is_stream_consumed and self._is_generator: + raise StreamConsumed() + + self._is_stream_consumed = True + if hasattr(self._stream, "aread"): + # File-like interfaces should use 'aread' directly. + chunk = await self._stream.aread(self.CHUNK_SIZE) + while chunk: + yield chunk + chunk = await self._stream.aread(self.CHUNK_SIZE) + else: + # Otherwise iterate, making sure the wrapped stream is closed even if the + # consumer stops early (e.g. an exception is raised part-way through decoding). + stream = self._stream.__aiter__() + try: + async for part in stream: + yield part + finally: + if isinstance(stream, AsyncGenerator): + await stream.aclose() + + +class UnattachedStream(AsyncByteStream, SyncByteStream): + """ + If a request or response is serialized using pickle, then it is no longer + attached to a stream for I/O purposes. Any stream operations should result + in `httpx2.StreamClosed`. + """ + + def __iter__(self) -> Iterator[bytes]: + raise StreamClosed() + + async def __aiter__(self) -> AsyncIterator[bytes]: + raise StreamClosed() + yield b"" # pragma: no cover + + +def encode_content( + content: str | bytes | Iterable[bytes] | AsyncIterable[bytes], +) -> tuple[dict[str, str], SyncByteStream | AsyncByteStream]: + if isinstance(content, (bytes, str)): + body = content.encode("utf-8") if isinstance(content, str) else content + content_length = len(body) + headers = {"Content-Length": str(content_length)} if body else {} + return headers, ByteStream(body) + + elif isinstance(content, Iterable) and not isinstance(content, dict): + # `not isinstance(content, dict)` is a bit oddly specific, but it + # catches a case that's easy for users to make in error, and would + # otherwise pass through here, like any other bytes-iterable, + # because `dict` happens to be iterable. See issue #2491. + content_length_or_none = peek_filelike_remaining_length(content) + + if content_length_or_none is None: + headers = {"Transfer-Encoding": "chunked"} + else: + headers = {"Content-Length": str(content_length_or_none)} + return headers, IteratorByteStream(content) + + elif isinstance(content, AsyncIterable): + headers = {"Transfer-Encoding": "chunked"} + return headers, AsyncIteratorByteStream(content) + + raise TypeError(f"Unexpected type for 'content', {type(content)!r}") + + +def encode_urlencoded_data(data: RequestData) -> tuple[dict[str, str], ByteStream]: + plain_data: list[tuple[str, str]] = [] + for key, value in data.items(): + if isinstance(value, (list, tuple)): + plain_data.extend([(key, primitive_value_to_str(item)) for item in value]) + else: + plain_data.append((key, primitive_value_to_str(value))) + body = urlencode(plain_data, doseq=True).encode("utf-8") + content_length = str(len(body)) + content_type = "application/x-www-form-urlencoded" + headers = {"Content-Length": content_length, "Content-Type": content_type} + return headers, ByteStream(body) + + +def encode_multipart_data( + data: RequestData, files: RequestFiles, boundary: bytes | None +) -> tuple[dict[str, str], MultipartStream]: + multipart = MultipartStream(data=data, files=files, boundary=boundary) + headers = multipart.get_headers() + return headers, multipart + + +def encode_text(text: str) -> tuple[dict[str, str], ByteStream]: + body = text.encode("utf-8") + content_length = str(len(body)) + content_type = "text/plain; charset=utf-8" + headers = {"Content-Length": content_length, "Content-Type": content_type} + return headers, ByteStream(body) + + +def encode_html(html: str) -> tuple[dict[str, str], ByteStream]: + body = html.encode("utf-8") + content_length = str(len(body)) + content_type = "text/html; charset=utf-8" + headers = {"Content-Length": content_length, "Content-Type": content_type} + return headers, ByteStream(body) + + +def encode_json(json: Any) -> tuple[dict[str, str], ByteStream]: + body = json_dumps(json, ensure_ascii=False, separators=(",", ":"), allow_nan=False).encode("utf-8") + content_length = str(len(body)) + content_type = "application/json" + headers = {"Content-Length": content_length, "Content-Type": content_type} + return headers, ByteStream(body) + + +def encode_request( + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: Any | None = None, + boundary: bytes | None = None, +) -> tuple[dict[str, str], SyncByteStream | AsyncByteStream]: + """ + Handles encoding the given `content`, `data`, `files`, and `json`, + returning a two-tuple of (, ). + """ + if data is not None and not isinstance(data, Mapping): + # We prefer to separate `content=` + # for raw request content, and `data=` for url encoded or + # multipart form content. + # + # However for compat with requests, we *do* still support + # `data=` usages. We deal with that case here, treating it + # as if `content=<...>` had been supplied instead. + message = "Use 'content=<...>' to upload raw bytes/text content." + warnings.warn(message, DeprecationWarning, stacklevel=2) + return encode_content(data) + + if content is not None: + return encode_content(content) + elif files: + return encode_multipart_data(data or {}, files, boundary) + elif data: + return encode_urlencoded_data(data) + elif json is not None: + return encode_json(json) + + return {}, ByteStream(b"") + + +def encode_response( + content: ResponseContent | None = None, + text: str | None = None, + html: str | None = None, + json: Any | None = None, +) -> tuple[dict[str, str], SyncByteStream | AsyncByteStream]: + """ + Handles encoding the given `content`, returning a two-tuple of + (, ). + """ + if content is not None: + return encode_content(content) + elif text is not None: + return encode_text(text) + elif html is not None: + return encode_html(html) + elif json is not None: + return encode_json(json) + + return {}, ByteStream(b"") diff --git a/venv/lib/python3.12/site-packages/httpx2/_decoders.py b/venv/lib/python3.12/site-packages/httpx2/_decoders.py new file mode 100644 index 0000000..73b302d --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_decoders.py @@ -0,0 +1,458 @@ +""" +Handlers for Content-Encoding. + +See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding +""" + +from __future__ import annotations + +import codecs +import io +import itertools +import sys +import typing +import zlib + +from ._exceptions import DecodingError + +# Brotli support is optional +try: + # The C bindings in `brotli` are recommended for CPython. + import brotli +except ImportError: # pragma: no cover + try: + # The CFFI bindings in `brotlicffi` are recommended for PyPy + # and other environments. + import brotlicffi as brotli + except ImportError: + brotli = None + + +# Zstandard support is optional on Python <= 3.13. +# On Python 3.14+, the stdlib includes an optional built-in zstd implementation. +if typing.TYPE_CHECKING: + if sys.version_info >= (3, 14): + from compression.zstd import ZstdDecompressor, ZstdError + else: + from backports.zstd import ZstdDecompressor, ZstdError + + _zstandard_installed: bool +else: # pragma: no cover + try: + if sys.version_info >= (3, 14): + from compression.zstd import ZstdDecompressor, ZstdError + else: + from backports.zstd import ZstdDecompressor, ZstdError + + _zstandard_installed = True + except ImportError: + _zstandard_installed = False + + +MAX_DECODE_CHUNK_SIZE = 2**20 # 1 MiB + + +class Decompressor(typing.Protocol): + @property + def unconsumed_tail(self) -> bytes: ... + + def decompress(self, data: bytes, max_length: int) -> bytes: ... + + def flush(self) -> bytes: ... + + +class ZlibDecompressor: + """ + Drain a `zlib`/`gzip` decompressor in bounded pieces so a small compressed + input cannot inflate to an unbounded buffer in a single call. + """ + + def __init__(self, decompressor: Decompressor) -> None: + self.decompressor = decompressor + + def decompress(self, data: bytes) -> typing.Iterator[bytes]: + decompressed = self.decompressor.decompress(data, MAX_DECODE_CHUNK_SIZE) + while decompressed: + yield decompressed + decompressed = self.decompressor.decompress(self.decompressor.unconsumed_tail, MAX_DECODE_CHUNK_SIZE) + + def flush(self) -> bytes: + return self.decompressor.flush() + + +class ContentDecoder: + def decode(self, data: bytes) -> typing.Iterator[bytes]: + raise NotImplementedError() # pragma: no cover + + def flush(self) -> typing.Iterator[bytes]: + raise NotImplementedError() # pragma: no cover + + +class IdentityDecoder(ContentDecoder): + """ + Handle unencoded data. + """ + + def decode(self, data: bytes) -> typing.Iterator[bytes]: + yield data + + def flush(self) -> typing.Iterator[bytes]: + yield from () + + +class DeflateDecoder(ContentDecoder): + """ + Handle 'deflate' decoding. + + See: https://stackoverflow.com/questions/1838699 + """ + + def __init__(self) -> None: + self.first_attempt = True + self.decompressor = ZlibDecompressor(zlib.decompressobj()) + + def decode(self, data: bytes) -> typing.Iterator[bytes]: + was_first_attempt = self.first_attempt + self.first_attempt = False + try: + yield from self.decompressor.decompress(data) + except zlib.error as exc: + if was_first_attempt: + self.decompressor = ZlibDecompressor(zlib.decompressobj(-zlib.MAX_WBITS)) + yield from self.decode(data) + else: + raise DecodingError(str(exc)) from exc + + def flush(self) -> typing.Iterator[bytes]: + try: + yield self.decompressor.flush() + except zlib.error as exc: # pragma: no cover + raise DecodingError(str(exc)) from exc + + +class GZipDecoder(ContentDecoder): + """ + Handle 'gzip' decoding. + + See: https://stackoverflow.com/questions/1838699 + """ + + def __init__(self) -> None: + self.decompressor = ZlibDecompressor(zlib.decompressobj(zlib.MAX_WBITS | 16)) + + def decode(self, data: bytes) -> typing.Iterator[bytes]: + try: + yield from self.decompressor.decompress(data) + except zlib.error as exc: + raise DecodingError(str(exc)) from exc + + def flush(self) -> typing.Iterator[bytes]: + try: + yield self.decompressor.flush() + except zlib.error as exc: # pragma: no cover + raise DecodingError(str(exc)) from exc + + +class BrotliDecoder(ContentDecoder): + """ + Handle 'brotli' decoding. + + Requires `pip install brotlipy`. See: https://brotlipy.readthedocs.io/ + or `pip install brotli`. See https://github.com/google/brotli + Supports both 'brotlipy' and 'Brotli' packages since they share an import + name. The top branches are for 'brotlipy' and bottom branches for 'Brotli' + """ + + def __init__(self) -> None: + if brotli is None: # pragma: no cover + raise ImportError( + "Using 'BrotliDecoder', but neither of the 'brotlicffi' or 'brotli' " + "packages have been installed. " + "Make sure to install httpx using `pip install httpx[brotli]`." + ) from None + + self.decompressor = brotli.Decompressor() + self.seen_data = False + self._decompress: typing.Callable[..., bytes] + if hasattr(self.decompressor, "decompress"): + # The 'brotlicffi' package. + self._decompress = self.decompressor.decompress # pragma: no cover + else: + # The 'brotli' package. + self._decompress = self.decompressor.process + + def decode(self, data: bytes) -> typing.Iterator[bytes]: + if not data: + return + self.seen_data = True + try: + # The C backend may allocate nearly twice the requested threshold. + output_buffer_limit = MAX_DECODE_CHUNK_SIZE // 2 + decompressed = self._decompress(data, output_buffer_limit=output_buffer_limit) + while decompressed: + yield decompressed + decompressed = self._decompress(b"", output_buffer_limit=output_buffer_limit) + except brotli.error as exc: + raise DecodingError(str(exc)) from exc + + def flush(self) -> typing.Iterator[bytes]: + if not self.seen_data: + return + try: + if hasattr(self.decompressor, "finish"): + # Only available in the 'brotlicffi' package. + + # As the decompressor decompresses eagerly, this + # will never actually emit any data. However, it will potentially throw + # errors if a truncated or damaged data stream has been used. + self.decompressor.finish() # pragma: no cover + except brotli.error as exc: # pragma: no cover + raise DecodingError(str(exc)) from exc + yield from () + + +class ZStandardDecoder(ContentDecoder): + """Handle 'zstd' RFC 8878 decoding. + + On Python 3.13 and below, this requires `pip install httpx2[zstd]`. + """ + + # inspired by the ZstdDecoder implementation in urllib3 + def __init__(self) -> None: + if not _zstandard_installed: # pragma: no cover + raise ImportError( + "Using 'ZStandardDecoder', ...Make sure to install httpx using `pip install httpx[zstd]`." + ) from None + + self.decompressor = ZstdDecompressor() + self.seen_data = False + + def decode(self, data: bytes) -> typing.Iterator[bytes]: + if not data: + return + self.seen_data = True + try: + if self.decompressor.eof: + data = self.decompressor.unused_data + data + self.decompressor = ZstdDecompressor() + while True: + decompressed = self.decompressor.decompress(data, MAX_DECODE_CHUNK_SIZE) + while decompressed: + yield decompressed + if self.decompressor.needs_input or self.decompressor.eof: + break + decompressed = self.decompressor.decompress(b"", MAX_DECODE_CHUNK_SIZE) + if not (self.decompressor.eof and self.decompressor.unused_data): + break + data = self.decompressor.unused_data + self.decompressor = ZstdDecompressor() + except ZstdError as exc: + raise DecodingError(str(exc)) from exc + + def flush(self) -> typing.Iterator[bytes]: + if not self.seen_data: + return + if not self.decompressor.eof: + raise DecodingError("Zstandard data is incomplete") # pragma: no cover + yield from () + + +class MultiDecoder(ContentDecoder): + """ + Handle the case where multiple encodings have been applied. + """ + + max_decode_links: typing.ClassVar[int] = 5 + + def __init__(self, encodings: typing.Sequence[str]) -> None: + """ + 'encodings' should be the content codings in the order in which + each was applied. + """ + codings = [encoding for encoding in encodings if encoding in SUPPORTED_DECODERS] + if len(codings) > self.max_decode_links: + raise DecodingError(f"Cannot apply more than {self.max_decode_links} content encodings.") + # Note that we reverse the order for decoding. + self.children: list[ContentDecoder] = [SUPPORTED_DECODERS[coding]() for coding in reversed(codings)] + + def decode(self, data: bytes) -> typing.Iterator[bytes]: + streams: typing.Iterator[bytes] = iter((data,)) + for child in self.children: + streams = self._pipe(child.decode, streams) + yield from streams + + def flush(self) -> typing.Iterator[bytes]: + streams: typing.Iterator[bytes] = iter(()) + for child in self.children: + streams = itertools.chain(self._pipe(child.decode, streams), child.flush()) + yield from streams + + @staticmethod + def _pipe( + decode: typing.Callable[[bytes], typing.Iterator[bytes]], + upstream: typing.Iterator[bytes], + ) -> typing.Iterator[bytes]: + for chunk in upstream: + yield from decode(chunk) + + +class ByteChunker: + """ + Handles returning byte content in fixed-size chunks. + """ + + def __init__(self, chunk_size: int | None = None) -> None: + self._buffer = io.BytesIO() + self._chunk_size = chunk_size + + def decode(self, content: bytes) -> list[bytes]: + if self._chunk_size is None: + return [content] if content else [] + + self._buffer.write(content) + if self._buffer.tell() >= self._chunk_size: + value = self._buffer.getvalue() + chunks = [value[i : i + self._chunk_size] for i in range(0, len(value), self._chunk_size)] + if len(chunks[-1]) == self._chunk_size: + self._buffer.seek(0) + self._buffer.truncate() + return chunks + else: + self._buffer.seek(0) + self._buffer.write(chunks[-1]) + self._buffer.truncate() + return chunks[:-1] + else: + return [] + + def flush(self) -> list[bytes]: + value = self._buffer.getvalue() + self._buffer.seek(0) + self._buffer.truncate() + return [value] if value else [] + + +class TextChunker: + """ + Handles returning text content in fixed-size chunks. + """ + + def __init__(self, chunk_size: int | None = None) -> None: + self._buffer = io.StringIO() + self._chunk_size = chunk_size + + def decode(self, content: str) -> list[str]: + if self._chunk_size is None: + return [content] if content else [] + + self._buffer.write(content) + if self._buffer.tell() >= self._chunk_size: + value = self._buffer.getvalue() + chunks = [value[i : i + self._chunk_size] for i in range(0, len(value), self._chunk_size)] + if len(chunks[-1]) == self._chunk_size: + self._buffer.seek(0) + self._buffer.truncate() + return chunks + else: + self._buffer.seek(0) + self._buffer.write(chunks[-1]) + self._buffer.truncate() + return chunks[:-1] + else: + return [] + + def flush(self) -> list[str]: + value = self._buffer.getvalue() + self._buffer.seek(0) + self._buffer.truncate() + return [value] if value else [] + + +class TextDecoder: + """ + Handles incrementally decoding bytes into text + """ + + def __init__(self, encoding: str = "utf-8") -> None: + self.decoder = codecs.getincrementaldecoder(encoding)(errors="replace") + + def decode(self, data: bytes) -> str: + return self.decoder.decode(data) + + def flush(self) -> str: + return self.decoder.decode(b"", True) + + +class LineDecoder: + """ + Handles incrementally reading lines from text. + + Has the same behaviour as the stdllib splitlines, + but handling the input iteratively. + """ + + def __init__(self) -> None: + self.buffer: list[str] = [] + self.trailing_cr: bool = False + + def decode(self, text: str) -> list[str]: + # See https://docs.python.org/3/library/stdtypes.html#str.splitlines + NEWLINE_CHARS = "\n\r\x0b\x0c\x1c\x1d\x1e\x85\u2028\u2029" + + # We always push a trailing `\r` into the next decode iteration. + if self.trailing_cr: + text = "\r" + text + self.trailing_cr = False + if text.endswith("\r"): + self.trailing_cr = True + text = text[:-1] + + if not text: + # NOTE: the edge case input of empty text doesn't occur in practice, + # because other httpx internals filter out this value + return [] # pragma: no cover + + trailing_newline = text[-1] in NEWLINE_CHARS + lines = text.splitlines() + + if len(lines) == 1 and not trailing_newline: + # No new lines, buffer the input and continue. + self.buffer.append(lines[0]) + return [] + + if self.buffer: + # Include any existing buffer in the first portion of the + # splitlines result. + lines = ["".join(self.buffer) + lines[0]] + lines[1:] + self.buffer = [] + + if not trailing_newline: + # If the last segment of splitlines is not newline terminated, + # then drop it from our output and start a new buffer. + self.buffer = [lines.pop()] + + return lines + + def flush(self) -> list[str]: + if not self.buffer and not self.trailing_cr: + return [] + + lines = ["".join(self.buffer)] + self.buffer = [] + self.trailing_cr = False + return lines + + +SUPPORTED_DECODERS: dict[str, type[ContentDecoder]] = { + "identity": IdentityDecoder, + "gzip": GZipDecoder, + "deflate": DeflateDecoder, + "br": BrotliDecoder, + "zstd": ZStandardDecoder, +} + + +if brotli is None: + SUPPORTED_DECODERS.pop("br") # pragma: no cover +if not _zstandard_installed: + SUPPORTED_DECODERS.pop("zstd") # pragma: no cover diff --git a/venv/lib/python3.12/site-packages/httpx2/_exceptions.py b/venv/lib/python3.12/site-packages/httpx2/_exceptions.py new file mode 100644 index 0000000..2b74151 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_exceptions.py @@ -0,0 +1,380 @@ +""" +Our exception hierarchy: + +* HTTPError + x RequestError + + TransportError + - TimeoutException + · ConnectTimeout + · ReadTimeout + · WriteTimeout + · PoolTimeout + - NetworkError + · ConnectError + · ReadError + · WriteError + · CloseError + - ProtocolError + · LocalProtocolError + · RemoteProtocolError + - ProxyError + - UnsupportedProtocol + + DecodingError + + TooManyRedirects + x HTTPStatusError +* InvalidURL +* CookieConflict +* StreamError + x StreamConsumed + x StreamClosed + x ResponseNotRead + x RequestNotRead +""" + +from __future__ import annotations + +import contextlib +import typing +from collections.abc import Generator + +if typing.TYPE_CHECKING: + from ._models import Request, Response # pragma: no cover + +__all__ = [ + "CloseError", + "ConnectError", + "ConnectTimeout", + "CookieConflict", + "DecodingError", + "HTTPError", + "HTTPStatusError", + "InvalidURL", + "LocalProtocolError", + "NetworkError", + "PoolTimeout", + "ProtocolError", + "ProxyError", + "ReadError", + "ReadTimeout", + "RemoteProtocolError", + "RequestError", + "RequestNotRead", + "ResponseNotRead", + "StreamClosed", + "StreamConsumed", + "StreamError", + "TimeoutException", + "TooManyRedirects", + "TransportError", + "UnsupportedProtocol", + "WriteError", + "WriteTimeout", +] + + +class HTTPXDeprecationWarning(UserWarning): + """A custom deprecation warning for HTTPX. + + Unlike the built-in `DeprecationWarning`, this inherits from `UserWarning` to ensure it is visible by default, + helping users discover deprecated features without needing to enable warnings explicitly. + + Reference: https://sethmlarson.dev/deprecations-via-warnings-dont-work-for-python-libraries + """ + + +class HTTPError(Exception): + """ + Base class for `RequestError` and `HTTPStatusError`. + + Useful for `try...except` blocks when issuing a request, + and then calling `.raise_for_status()`. + + For example: + + ``` + try: + response = httpx2.get("https://www.example.com") + response.raise_for_status() + except httpx2.HTTPError as exc: + print(f"HTTP Exception for {exc.request.url} - {exc}") + ``` + """ + + def __init__(self, message: str) -> None: + super().__init__(message) + self._request: Request | None = None + + @property + def request(self) -> Request: + if self._request is None: + raise RuntimeError("The .request property has not been set.") + return self._request + + @request.setter + def request(self, request: Request) -> None: + self._request = request + + +class RequestError(HTTPError): + """ + Base class for all exceptions that may occur when issuing a `.request()`. + """ + + def __init__(self, message: str, *, request: Request | None = None) -> None: + super().__init__(message) + # At the point an exception is raised we won't typically have a request + # instance to associate it with. + # + # The 'request_context' context manager is used within the Client and + # Response methods in order to ensure that any raised exceptions + # have a `.request` property set on them. + self._request = request + + +class TransportError(RequestError): + """ + Base class for all exceptions that occur at the level of the Transport API. + """ + + +# Timeout exceptions... + + +class TimeoutException(TransportError): + """ + The base class for timeout errors. + + An operation has timed out. + """ + + +class ConnectTimeout(TimeoutException): + """ + Timed out while connecting to the host. + """ + + +class ReadTimeout(TimeoutException): + """ + Timed out while receiving data from the host. + """ + + +class WriteTimeout(TimeoutException): + """ + Timed out while sending data to the host. + """ + + +class PoolTimeout(TimeoutException): + """ + Timed out waiting to acquire a connection from the pool. + """ + + +# Core networking exceptions... + + +class NetworkError(TransportError): + """ + The base class for network-related errors. + + An error occurred while interacting with the network. + """ + + +class ReadError(NetworkError): + """ + Failed to receive data from the network. + """ + + +class WriteError(NetworkError): + """ + Failed to send data through the network. + """ + + +class ConnectError(NetworkError): + """ + Failed to establish a connection. + """ + + +class CloseError(NetworkError): + """ + Failed to close a connection. + """ + + +# Other transport exceptions... + + +class ProxyError(TransportError): + """ + An error occurred while establishing a proxy connection. + """ + + +class UnsupportedProtocol(TransportError): + """ + Attempted to make a request to an unsupported protocol. + + For example issuing a request to `ftp://www.example.com`. + """ + + +class ProtocolError(TransportError): + """ + The protocol was violated. + """ + + +class LocalProtocolError(ProtocolError): + """ + A protocol was violated by the client. + + For example if the user instantiated a `Request` instance explicitly, + failed to include the mandatory `Host:` header, and then issued it directly + using `client.send()`. + """ + + +class RemoteProtocolError(ProtocolError): + """ + The protocol was violated by the server. + + For example, returning malformed HTTP. + """ + + +# Other request exceptions... + + +class DecodingError(RequestError): + """ + Decoding of the response failed, due to a malformed encoding. + """ + + +class TooManyRedirects(RequestError): + """ + Too many redirects. + """ + + +# Client errors + + +class HTTPStatusError(HTTPError): + """ + The response had an error HTTP status of 4xx or 5xx. + + May be raised when calling `response.raise_for_status()` + """ + + def __init__(self, message: str, *, request: Request, response: Response) -> None: + super().__init__(message) + self.request = request + self.response = response + + +class InvalidURL(Exception): + """ + URL is improperly formed or cannot be parsed. + """ + + def __init__(self, message: str) -> None: + super().__init__(message) + + +class CookieConflict(Exception): + """ + Attempted to lookup a cookie by name, but multiple cookies existed. + + Can occur when calling `response.cookies.get(...)`. + """ + + def __init__(self, message: str) -> None: + super().__init__(message) + + +# Stream exceptions... + +# These may occur as the result of a programming error, by accessing +# the request/response stream in an invalid manner. + + +class StreamError(RuntimeError): + """ + The base class for stream exceptions. + + The developer made an error in accessing the request stream in + an invalid way. + """ + + def __init__(self, message: str) -> None: + super().__init__(message) + + +class StreamConsumed(StreamError): + """ + Attempted to read or stream content, but the content has already + been streamed. + """ + + def __init__(self) -> None: + message = ( + "Attempted to read or stream some content, but the content has " + "already been streamed. For requests, this could be due to passing " + "a generator as request content, and then receiving a redirect " + "response or a secondary request as part of an authentication flow." + "For responses, this could be due to attempting to stream the response " + "content more than once." + ) + super().__init__(message) + + +class StreamClosed(StreamError): + """ + Attempted to read or stream response content, but the request has been + closed. + """ + + def __init__(self) -> None: + message = "Attempted to read or stream content, but the stream has been closed." + super().__init__(message) + + +class ResponseNotRead(StreamError): + """ + Attempted to access streaming response content, without having called `read()`. + """ + + def __init__(self) -> None: + message = "Attempted to access streaming response content, without having called `read()`." + super().__init__(message) + + +class RequestNotRead(StreamError): + """ + Attempted to access streaming request content, without having called `read()`. + """ + + def __init__(self) -> None: + message = "Attempted to access streaming request content, without having called `read()`." + super().__init__(message) + + +@contextlib.contextmanager +def request_context(request: Request | None = None) -> Generator[None]: + """ + A context manager that can be used to attach the given request context + to any `RequestError` exceptions that are raised within the block. + """ + try: + yield + except RequestError as exc: + if request is not None: + exc.request = request + raise exc diff --git a/venv/lib/python3.12/site-packages/httpx2/_main.py b/venv/lib/python3.12/site-packages/httpx2/_main.py new file mode 100644 index 0000000..e53f394 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_main.py @@ -0,0 +1,483 @@ +from __future__ import annotations + +import functools +import json +import sys +import typing + +import click +import pygments.lexers +import pygments.util +import rich.console +import rich.markup +import rich.progress +import rich.syntax +import rich.table + +from ._client import Client +from ._exceptions import RequestError +from ._models import Response +from ._status_codes import codes + +if typing.TYPE_CHECKING: + import httpcore2 # pragma: no cover + + +def print_help() -> None: + console = rich.console.Console() + + console.print("[bold]HTTPX :butterfly:", justify="center") + console.print() + console.print("A next generation HTTP client.", justify="center") + console.print() + console.print("Usage: [bold]httpx2[/bold] [cyan] [OPTIONS][/cyan] ", justify="left") + console.print() + + table = rich.table.Table.grid(padding=1, pad_edge=True) + table.add_column("Parameter", no_wrap=True, justify="left", style="bold") + table.add_column("Description") + table.add_row( + "-m, --method [cyan]METHOD", + "Request method, such as GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD, QUERY.\n" + "[Default: GET, or POST if a request body is included]", + ) + table.add_row( + "-p, --params [cyan] ...", + "Query parameters to include in the request URL.", + ) + table.add_row("-c, --content [cyan]TEXT", "Byte content to include in the request body.") + table.add_row("-d, --data [cyan] ...", "Form data to include in the request body.") + table.add_row( + "-f, --files [cyan] ...", + "Form files to include in the request body.", + ) + table.add_row("-j, --json [cyan]TEXT", "JSON data to include in the request body.") + table.add_row( + "-h, --headers [cyan] ...", + "Include additional HTTP headers in the request.", + ) + table.add_row("--cookies [cyan] ...", "Cookies to include in the request.") + table.add_row( + "--auth [cyan]", + "Username and password to include in the request. Specify '-' for the password" + " to use a password prompt. Note that using --verbose/-v will expose" + " the Authorization header, including the password encoding" + " in a trivially reversible format.", + ) + + table.add_row( + "--proxy [cyan]URL", + "Send the request via a proxy. Should be the URL giving the proxy address.", + ) + + table.add_row( + "--timeout [cyan]FLOAT", + "Timeout value to use for network operations, such as establishing the" + " connection, reading some data, etc... [Default: 5.0]", + ) + + table.add_row("--follow-redirects", "Automatically follow redirects.") + table.add_row("--verify / --no-verify", "Enable SSL verification.") + table.add_row("--http2", "Send the request using HTTP/2, if the remote server supports it.") + + table.add_row( + "--download [cyan]FILE", + "Save the response content as a file, rather than displaying it.", + ) + + table.add_row("-v, --verbose", "Verbose output. Show request as well as response.") + table.add_row("--help", "Show this message and exit.") + console.print(table) + + +def get_lexer_for_response(response: Response) -> str: + content_type = response.headers.get("Content-Type") + if content_type is not None: + mime_type, _, _ = content_type.partition(";") + try: + return typing.cast(str, pygments.lexers.get_lexer_for_mimetype(mime_type.strip()).name) + except pygments.util.ClassNotFound: # pragma: no cover + pass + return "" # pragma: no cover + + +def format_request_headers(request: httpcore2.Request, http2: bool = False) -> str: + version = "HTTP/2" if http2 else "HTTP/1.1" + headers = [(name.lower() if http2 else name, value) for name, value in request.headers] + method = request.method.decode("ascii") + target = request.url.target.decode("ascii") + lines = [f"{method} {target} {version}"] + [ + f"{name.decode('ascii')}: {value.decode('ascii')}" for name, value in headers + ] + return "\n".join(lines) + + +def format_response_headers( + http_version: bytes, + status: int, + reason_phrase: bytes | None, + headers: list[tuple[bytes, bytes]], +) -> str: + version = http_version.decode("ascii") + reason = codes.get_reason_phrase(status) if reason_phrase is None else reason_phrase.decode("ascii") + lines = [f"{version} {status} {reason}"] + [ + f"{name.decode('ascii')}: {value.decode('ascii')}" for name, value in headers + ] + return "\n".join(lines) + + +def print_request_headers(request: httpcore2.Request, http2: bool = False) -> None: + console = rich.console.Console() + http_text = format_request_headers(request, http2=http2) + syntax = rich.syntax.Syntax(http_text, "http", theme="ansi_dark", word_wrap=True) + console.print(syntax) + syntax = rich.syntax.Syntax("", "http", theme="ansi_dark", word_wrap=True) + console.print(syntax) + + +def print_response_headers( + http_version: bytes, + status: int, + reason_phrase: bytes | None, + headers: list[tuple[bytes, bytes]], +) -> None: + console = rich.console.Console() + http_text = format_response_headers(http_version, status, reason_phrase, headers) + syntax = rich.syntax.Syntax(http_text, "http", theme="ansi_dark", word_wrap=True) + console.print(syntax) + syntax = rich.syntax.Syntax("", "http", theme="ansi_dark", word_wrap=True) + console.print(syntax) + + +def print_response(response: Response) -> None: + console = rich.console.Console() + lexer_name = get_lexer_for_response(response) + if lexer_name: + if lexer_name.lower() == "json": + try: + data = response.json() + text = json.dumps(data, indent=4) + except ValueError: # pragma: no cover + text = response.text + else: + text = response.text + + syntax = rich.syntax.Syntax(text, lexer_name, theme="ansi_dark", word_wrap=True) + console.print(syntax) + else: + console.print(f"<{len(response.content)} bytes of binary data>") + + +_PCTRTT = tuple[tuple[str, str], ...] +_PCTRTTT = tuple[_PCTRTT, ...] +_PeerCertRetDictType = dict[str, str | _PCTRTTT | _PCTRTT] + + +def format_certificate(cert: _PeerCertRetDictType) -> str: # pragma: no cover + lines: list[str] = [] + for key, value in cert.items(): + if isinstance(value, (list, tuple)): + lines.append(f"* {key}:") + for item in value: + if key in ("subject", "issuer"): + lines.extend(f"* {sub_item[0]}: {sub_item[1]!r}" for sub_item in item) + elif isinstance(item, tuple) and len(item) == 2: + lines.append(f"* {item[0]}: {item[1]!r}") + else: + lines.append(f"* {item!r}") + else: + lines.append(f"* {key}: {value!r}") + return "\n".join(lines) + + +def trace(name: str, info: typing.Mapping[str, typing.Any], verbose: bool = False) -> None: + console = rich.console.Console() + if name == "connection.connect_tcp.started" and verbose: + host = info["host"] + console.print(f"* Connecting to {host!r}") + elif name == "connection.connect_tcp.complete" and verbose: + stream = info["return_value"] + server_addr = stream.get_extra_info("server_addr") + console.print(f"* Connected to {server_addr[0]!r} on port {server_addr[1]}") + elif name == "connection.start_tls.complete" and verbose: # pragma: no cover + stream = info["return_value"] + ssl_object = stream.get_extra_info("ssl_object") + version = ssl_object.version() + cipher = ssl_object.cipher() + server_cert = ssl_object.getpeercert() + alpn = ssl_object.selected_alpn_protocol() + console.print(f"* SSL established using {version!r} / {cipher[0]!r}") + console.print(f"* Selected ALPN protocol: {alpn!r}") + if server_cert: + console.print("* Server certificate:") + console.print(format_certificate(server_cert)) + elif name == "http11.send_request_headers.started" and verbose: + request = info["request"] + print_request_headers(request, http2=False) + elif name == "http2.send_request_headers.started" and verbose: # pragma: no cover + request = info["request"] + print_request_headers(request, http2=True) + elif name == "http11.receive_response_headers.complete": + http_version, status, reason_phrase, headers = info["return_value"] + print_response_headers(http_version, status, reason_phrase, headers) + elif name == "http2.receive_response_headers.complete": # pragma: no cover + status, headers = info["return_value"] + http_version = b"HTTP/2" + reason_phrase = None + print_response_headers(http_version, status, reason_phrase, headers) + + +def download_response(response: Response, download: typing.BinaryIO) -> None: + console = rich.console.Console() + console.print() + content_length = response.headers.get("Content-Length") + with rich.progress.Progress( + "[progress.description]{task.description}", + "[progress.percentage]{task.percentage:>3.0f}%", + rich.progress.BarColumn(bar_width=None), + rich.progress.DownloadColumn(), + rich.progress.TransferSpeedColumn(), + ) as progress: + description = f"Downloading [bold]{rich.markup.escape(download.name)}" + download_task = progress.add_task( + description, + total=int(content_length or 0), + start=content_length is not None, + ) + for chunk in response.iter_bytes(): + download.write(chunk) + progress.update(download_task, completed=response.num_bytes_downloaded) + + +def validate_json( + ctx: click.Context, + param: click.Option | click.Parameter, + value: typing.Any, +) -> typing.Any: + if value is None: + return None + + try: + return json.loads(value) + except json.JSONDecodeError: # pragma: no cover + raise click.BadParameter("Not valid JSON") + + +def validate_auth( + ctx: click.Context, + param: click.Option | click.Parameter, + value: typing.Any, +) -> typing.Any: + if value == (None, None): + return None + + username, password = value + if password == "-": # pragma: no cover + password = click.prompt("Password", hide_input=True) + return (username, password) + + +def handle_help( + ctx: click.Context, + param: click.Option | click.Parameter, + value: typing.Any, +) -> None: + if not value or ctx.resilient_parsing: + return + + print_help() + ctx.exit() + + +@click.command(add_help_option=False) +@click.argument("url", type=str) +@click.option( + "--method", + "-m", + "method", + type=str, + help=( + "Request method, such as GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD, QUERY. " + "[Default: GET, or POST if a request body is included]" + ), +) +@click.option( + "--params", + "-p", + "params", + type=(str, str), + multiple=True, + help="Query parameters to include in the request URL.", +) +@click.option( + "--content", + "-c", + "content", + type=str, + help="Byte content to include in the request body.", +) +@click.option( + "--data", + "-d", + "data", + type=(str, str), + multiple=True, + help="Form data to include in the request body.", +) +@click.option( + "--files", + "-f", + "files", + type=(str, click.File(mode="rb")), + multiple=True, + help="Form files to include in the request body.", +) +@click.option( + "--json", + "-j", + "json", + type=str, + callback=validate_json, + help="JSON data to include in the request body.", +) +@click.option( + "--headers", + "-h", + "headers", + type=(str, str), + multiple=True, + help="Include additional HTTP headers in the request.", +) +@click.option( + "--cookies", + "cookies", + type=(str, str), + multiple=True, + help="Cookies to include in the request.", +) +@click.option( + "--auth", + "auth", + type=(str, str), + default=(None, None), + callback=validate_auth, + help=( + "Username and password to include in the request. " + "Specify '-' for the password to use a password prompt. " + "Note that using --verbose/-v will expose the Authorization header, " + "including the password encoding in a trivially reversible format." + ), +) +@click.option( + "--proxy", + "proxy", + type=str, + default=None, + help="Send the request via a proxy. Should be the URL giving the proxy address.", +) +@click.option( + "--timeout", + "timeout", + type=float, + default=5.0, + help=( + "Timeout value to use for network operations, such as establishing the " + "connection, reading some data, etc... [Default: 5.0]" + ), +) +@click.option( + "--follow-redirects", + "follow_redirects", + is_flag=True, + default=False, + help="Automatically follow redirects.", +) +@click.option( + "--verify/--no-verify", + default=True, + help="Enable SSL verification.", +) +@click.option( + "--http2", + "http2", + type=bool, + is_flag=True, + default=False, + help="Send the request using HTTP/2, if the remote server supports it.", +) +@click.option( + "--download", + type=click.File("wb"), + help="Save the response content as a file, rather than displaying it.", +) +@click.option( + "--verbose", + "-v", + type=bool, + is_flag=True, + default=False, + help="Verbose. Show request as well as response.", +) +@click.option( + "--help", + is_flag=True, + is_eager=True, + expose_value=False, + callback=handle_help, + help="Show this message and exit.", +) +def main( + url: str, + method: str, + params: list[tuple[str, str]], + content: str, + data: list[tuple[str, str]], + files: list[tuple[str, click.File]], + json: str, + headers: list[tuple[str, str]], + cookies: list[tuple[str, str]], + auth: tuple[str, str] | None, + proxy: str, + timeout: float, + follow_redirects: bool, + verify: bool, + http2: bool, + download: typing.BinaryIO | None, + verbose: bool, +) -> None: + """ + An HTTP command line client. + Sends a request and displays the response. + """ + if not method: + method = "POST" if content or data or files or json else "GET" + + try: + with Client(proxy=proxy, timeout=timeout, http2=http2, verify=verify) as client: + with client.stream( + method, + url, + params=list(params), + content=content, + data=dict(data), + files=files, # type: ignore[arg-type] + json=json, + headers=headers, + cookies=dict(cookies), + auth=auth, + follow_redirects=follow_redirects, + extensions={"trace": functools.partial(trace, verbose=verbose)}, + ) as response: + if download is not None: + download_response(response, download) + else: + response.read() + if response.content: + print_response(response) + + except RequestError as exc: + console = rich.console.Console() + console.print(f"[red]{type(exc).__name__}[/red]: {exc}") + sys.exit(1) + + sys.exit(0 if response.is_success else 1) diff --git a/venv/lib/python3.12/site-packages/httpx2/_models.py b/venv/lib/python3.12/site-packages/httpx2/_models.py new file mode 100644 index 0000000..0272f36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_models.py @@ -0,0 +1,1273 @@ +from __future__ import annotations + +import codecs +import contextlib +import datetime +import email.message +import json as jsonlib +import re +import typing +import urllib.request +from collections.abc import AsyncGenerator, Mapping +from http.cookiejar import Cookie, CookieJar + +from ._content import ByteStream, UnattachedStream, encode_request, encode_response +from ._decoders import ( + ByteChunker, + ContentDecoder, + IdentityDecoder, + LineDecoder, + MultiDecoder, + TextChunker, + TextDecoder, +) +from ._exceptions import ( + CookieConflict, + HTTPStatusError, + RequestNotRead, + ResponseNotRead, + StreamClosed, + StreamConsumed, + request_context, +) +from ._multipart import get_multipart_boundary_from_content_type +from ._status_codes import codes +from ._types import ( + AsyncByteStream, + CookieTypes, + HeaderTypes, + QueryParamTypes, + RequestContent, + RequestData, + RequestExtensions, + RequestFiles, + ResponseContent, + ResponseExtensions, + SyncByteStream, +) +from ._urls import URL +from ._utils import to_bytes_or_str, to_str + +__all__ = ["Cookies", "Headers", "Request", "Response"] + +SENSITIVE_HEADERS = {"authorization", "proxy-authorization"} + +_T = typing.TypeVar("_T") + + +def _is_known_encoding(encoding: str) -> bool: + """ + Return `True` if `encoding` is a known codec. + """ + try: + codecs.lookup(encoding) + except LookupError: + return False + return True + + +def _normalize_header_key(key: str | bytes, encoding: str | None = None) -> bytes: + """ + Coerce str/bytes into a strictly byte-wise HTTP header key. + """ + return key if isinstance(key, bytes) else key.encode(encoding or "ascii") + + +def _normalize_header_value(value: str | bytes, encoding: str | None = None) -> bytes: + """ + Coerce str/bytes into a strictly byte-wise HTTP header value. + """ + if isinstance(value, bytes): + return value + if not isinstance(value, str): + raise TypeError(f"Header value must be str or bytes, not {type(value)}") + return value.encode(encoding or "ascii") + + +def _parse_content_type_charset(content_type: str) -> str | None: + # We used to use `cgi.parse_header()` here, but `cgi` became a dead battery. + # See: https://peps.python.org/pep-0594/#cgi + msg = email.message.Message() + msg["content-type"] = content_type + return msg.get_content_charset(failobj=None) + + +def _parse_header_links(value: str) -> list[dict[str, str]]: + """ + Returns a list of parsed link headers, for more info see: + https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link + The generic syntax of those is: + Link: < uri-reference >; param1=value1; param2="value2" + So for instance: + Link; '; type="image/jpeg",;' + would return + [ + {"url": "http:/.../front.jpeg", "type": "image/jpeg"}, + {"url": "http://.../back.jpeg"}, + ] + :param value: HTTP Link entity-header field + :return: list of parsed link headers + """ + links: list[dict[str, str]] = [] + replace_chars = " '\"" + value = value.strip(replace_chars) + if not value: + return links + for val in re.split(", *<", value): + try: + url, params = val.split(";", 1) + except ValueError: + url, params = val, "" + link = {"url": url.strip("<> '\"")} + for param in params.split(";"): + try: + key, value = param.split("=") + except ValueError: + break + link[key.strip(replace_chars)] = value.strip(replace_chars) + links.append(link) + return links + + +def _obfuscate_sensitive_headers( + items: typing.Iterable[tuple[typing.AnyStr, typing.AnyStr]], +) -> typing.Iterator[tuple[typing.AnyStr, typing.AnyStr]]: + for k, v in items: + if to_str(k.lower()) in SENSITIVE_HEADERS: + v = to_bytes_or_str("[secure]", match_type_of=v) + yield k, v + + +class Headers(typing.MutableMapping[str, str]): + """ + HTTP headers, as a case-insensitive multi-dict. + """ + + def __init__( + self, + headers: HeaderTypes | None = None, + encoding: str | None = None, + ) -> None: + self._list: list[tuple[bytes, bytes, bytes]] = [] + + if isinstance(headers, Headers): + self._list = list(headers._list) + elif isinstance(headers, Mapping): + for k, v in headers.items(): + bytes_key = _normalize_header_key(k, encoding) + bytes_value = _normalize_header_value(v, encoding) + self._list.append((bytes_key, bytes_key.lower(), bytes_value)) + elif headers is not None: + for k, v in headers: + bytes_key = _normalize_header_key(k, encoding) + bytes_value = _normalize_header_value(v, encoding) + self._list.append((bytes_key, bytes_key.lower(), bytes_value)) + + self._encoding = encoding + + @property + def encoding(self) -> str: + """ + Header encoding is mandated as ascii, but we allow fallbacks to utf-8 + or iso-8859-1. + """ + if self._encoding is None: + for encoding in ["ascii", "utf-8"]: + for key, value in self.raw: + try: + key.decode(encoding) + value.decode(encoding) + except UnicodeDecodeError: + break + else: + # The else block runs if 'break' did not occur, meaning + # all values fitted the encoding. + self._encoding = encoding + break + else: + # The ISO-8859-1 encoding covers all 256 code points in a byte, + # so will never raise decode errors. + self._encoding = "iso-8859-1" + return self._encoding + + @encoding.setter + def encoding(self, value: str) -> None: + self._encoding = value + + @property + def raw(self) -> list[tuple[bytes, bytes]]: + """ + Returns a list of the raw header items, as byte pairs. + """ + return [(raw_key, value) for raw_key, _, value in self._list] + + def keys(self) -> typing.KeysView[str]: + return {key.decode(self.encoding): None for _, key, _value in self._list}.keys() + + def values(self) -> typing.ValuesView[str]: + values_dict: dict[str, str] = {} + for _, key, value in self._list: + str_key = key.decode(self.encoding) + str_value = value.decode(self.encoding) + if str_key in values_dict: + values_dict[str_key] += f", {str_value}" + else: + values_dict[str_key] = str_value + return values_dict.values() + + def items(self) -> typing.ItemsView[str, str]: + """ + Return `(key, value)` items of headers. Concatenate headers + into a single comma separated value when a key occurs multiple times. + """ + values_dict: dict[str, str] = {} + for _, key, value in self._list: + str_key = key.decode(self.encoding) + str_value = value.decode(self.encoding) + if str_key in values_dict: + values_dict[str_key] += f", {str_value}" + else: + values_dict[str_key] = str_value + return values_dict.items() + + def multi_items(self) -> list[tuple[str, str]]: + """ + Return a list of `(key, value)` pairs of headers. Allow multiple + occurrences of the same key without concatenating into a single + comma separated value. + """ + return [(key.decode(self.encoding), value.decode(self.encoding)) for _, key, value in self._list] + + @typing.overload + def get(self, key: str, /) -> str | None: ... + + @typing.overload + def get(self, key: str, default: _T) -> str | _T: ... + + def get(self, key: str, default: _T | None = None) -> str | _T | None: + """ + Return a header value. If multiple occurrences of the header occur + then concatenate them together with commas. + """ + try: + return self[key] + except KeyError: + return default + + def get_list(self, key: str, split_commas: bool = False) -> list[str]: + """ + Return a list of all header values for a given key. + If `split_commas=True` is passed, then any comma separated header + values are split into multiple return strings. + """ + get_header_key = key.lower().encode(self.encoding) + + values = [ + item_value.decode(self.encoding) + for _, item_key, item_value in self._list + if item_key.lower() == get_header_key + ] + + if not split_commas: + return values + + split_values: list[str] = [] + for value in values: + split_values.extend([item.strip() for item in value.split(",")]) + return split_values + + def update(self, headers: HeaderTypes | None = None) -> None: # type: ignore + headers = Headers(headers) + for key in headers.keys(): + if key in self: + self.pop(key) + self._list.extend(headers._list) + + def copy(self) -> Headers: + return Headers(self, encoding=self.encoding) + + def __or__(self, other: Mapping[str, str]) -> Headers: + if not isinstance(other, Mapping): + return NotImplemented + merged = self.copy() + merged.update(other) + return merged + + def __ror__(self, other: Mapping[str, str]) -> Headers: + if not isinstance(other, Mapping): + return NotImplemented + merged = Headers(other) + merged.update(self) + return merged + + def __ior__(self, other: HeaderTypes) -> Headers: + self.update(other) + return self + + def __getitem__(self, key: str) -> str: + """ + Return a single header value. + + If there are multiple headers with the same key, then we concatenate + them with commas. See: https://tools.ietf.org/html/rfc7230#section-3.2.2 + """ + normalized_key = key.lower().encode(self.encoding) + + items = [ + header_value.decode(self.encoding) + for _, header_key, header_value in self._list + if header_key == normalized_key + ] + + if items: + return ", ".join(items) + + raise KeyError(key) + + def __setitem__(self, key: str, value: str) -> None: + """ + Set the header `key` to `value`, removing any duplicate entries. + Retains insertion order. + """ + set_key = key.encode(self._encoding or "utf-8") + set_value = value.encode(self._encoding or "utf-8") + lookup_key = set_key.lower() + + found_indexes = [idx for idx, (_, item_key, _) in enumerate(self._list) if item_key == lookup_key] + + for idx in reversed(found_indexes[1:]): + del self._list[idx] + + if found_indexes: + idx = found_indexes[0] + self._list[idx] = (set_key, lookup_key, set_value) + else: + self._list.append((set_key, lookup_key, set_value)) + + def __delitem__(self, key: str) -> None: + """ + Remove the header `key`. + """ + del_key = key.lower().encode(self.encoding) + + pop_indexes = [idx for idx, (_, item_key, _) in enumerate(self._list) if item_key.lower() == del_key] + + if not pop_indexes: + raise KeyError(key) + + for idx in reversed(pop_indexes): + del self._list[idx] + + def __contains__(self, key: typing.Any) -> bool: + header_key = key.lower().encode(self.encoding) + return header_key in [key for _, key, _ in self._list] + + def __iter__(self) -> typing.Iterator[typing.Any]: + return iter(self.keys()) + + def __len__(self) -> int: + return len(self._list) + + def __eq__(self, other: typing.Any) -> bool: + try: + other_headers = Headers(other) + except ValueError: + return False + + self_list = [(key, value) for _, key, value in self._list] + other_list = [(key, value) for _, key, value in other_headers._list] + return sorted(self_list) == sorted(other_list) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + + encoding_str = "" + if self.encoding != "ascii": + encoding_str = f", encoding={self.encoding!r}" + + as_list = list(_obfuscate_sensitive_headers(self.multi_items())) + as_dict = dict(as_list) + + no_duplicate_keys = len(as_dict) == len(as_list) + if no_duplicate_keys: + return f"{class_name}({as_dict!r}{encoding_str})" + return f"{class_name}({as_list!r}{encoding_str})" + + +class Request: + def __init__( + self, + method: str, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + stream: SyncByteStream | AsyncByteStream | None = None, + extensions: RequestExtensions | None = None, + ) -> None: + self.method = method.upper() + self.url = URL(url) if params is None else URL(url, params=params) + self.headers = Headers(headers) + self.extensions = {} if extensions is None else dict(extensions) + + if cookies: + Cookies(cookies).set_cookie_header(self) + + if stream is None: + content_type: str | None = self.headers.get("content-type") + headers, stream = encode_request( + content=content, + data=data, + files=files, + json=json, + boundary=get_multipart_boundary_from_content_type( + content_type=content_type.encode(self.headers.encoding) if content_type else None + ), + ) + self._prepare(headers) + self.stream = stream + # Load the request body, except for streaming content. + if isinstance(stream, ByteStream): + self.read() + else: + # There's an important distinction between `Request(content=...)`, + # and `Request(stream=...)`. + # + # Using `content=...` implies automatically populated `Host` and content + # headers, of either `Content-Length: ...` or `Transfer-Encoding: chunked`. + # + # Using `stream=...` will not automatically include *any* + # auto-populated headers. + # + # As an end-user you don't really need `stream=...`. It's only + # useful when: + # + # * Preserving the request stream when copying requests, eg for redirects. + # * Creating request instances on the *server-side* of the transport API. + self.stream = stream + + def _prepare(self, default_headers: dict[str, str]) -> None: + for key, value in default_headers.items(): + # Ignore Content-Length if Transfer-Encoding has been set explicitly. + if key.lower() == "content-length" and "Transfer-Encoding" in self.headers: + continue + # Ignore Transfer-Encoding if Content-Length has been set explicitly. + if key.lower() == "transfer-encoding" and "Content-Length" in self.headers: + continue + self.headers.setdefault(key, value) + + auto_headers: list[tuple[bytes, bytes]] = [] + + has_host = "Host" in self.headers + has_content_length = "Content-Length" in self.headers or "Transfer-Encoding" in self.headers + + if not has_host and self.url.host: + auto_headers.append((b"Host", self.url.netloc)) + if not has_content_length and self.method in ("POST", "PUT", "PATCH", "QUERY"): + auto_headers.append((b"Content-Length", b"0")) + + self.headers = Headers(auto_headers + self.headers.raw) + + @property + def content(self) -> bytes: + if not hasattr(self, "_content"): + raise RequestNotRead() + return self._content + + def read(self) -> bytes: + """ + Read and return the request content. + """ + if not hasattr(self, "_content"): + assert isinstance(self.stream, typing.Iterable) + self._content = b"".join(self.stream) + if not isinstance(self.stream, ByteStream): + # If a streaming request has been read entirely into memory, then + # we can replace the stream with a raw bytes implementation, + # to ensure that any non-replayable streams can still be used. + self.stream = ByteStream(self._content) + return self._content + + async def aread(self) -> bytes: + """ + Read and return the request content. + """ + if not hasattr(self, "_content"): + assert isinstance(self.stream, typing.AsyncIterable) + self._content = b"".join([part async for part in self.stream]) + if not isinstance(self.stream, ByteStream): + # If a streaming request has been read entirely into memory, then + # we can replace the stream with a raw bytes implementation, + # to ensure that any non-replayable streams can still be used. + self.stream = ByteStream(self._content) + return self._content + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + url = str(self.url) + return f"<{class_name}({self.method!r}, {url!r})>" + + def __getstate__(self) -> dict[str, typing.Any]: + return {name: value for name, value in self.__dict__.items() if name not in ["extensions", "stream"]} + + def __setstate__(self, state: dict[str, typing.Any]) -> None: + for name, value in state.items(): + setattr(self, name, value) + self.extensions = {} + self.stream = UnattachedStream() + + +class Response: + def __init__( + self, + status_code: int, + *, + headers: HeaderTypes | None = None, + content: ResponseContent | None = None, + text: str | None = None, + html: str | None = None, + json: typing.Any = None, + stream: SyncByteStream | AsyncByteStream | None = None, + request: Request | None = None, + extensions: ResponseExtensions | None = None, + history: list[Response] | None = None, + default_encoding: str | typing.Callable[[bytes], str | None] = "utf-8", + ) -> None: + self.status_code = status_code + self.headers = Headers(headers) + + self._request: Request | None = request + + # When follow_redirects=False and a redirect is received, + # the client will set `response.next_request`. + self.next_request: Request | None = None + + self.extensions = {} if extensions is None else dict(extensions) + self.history = [] if history is None else list(history) + + self.is_closed = False + self.is_stream_consumed = False + + self.default_encoding = default_encoding + + if stream is None: + headers, stream = encode_response(content, text, html, json) + self._prepare(headers) + self.stream = stream + if isinstance(stream, ByteStream): + # Load the response body, except for streaming content. + self.read() + else: + # There's an important distinction between `Response(content=...)`, + # and `Response(stream=...)`. + # + # Using `content=...` implies automatically populated content headers, + # of either `Content-Length: ...` or `Transfer-Encoding: chunked`. + # + # Using `stream=...` will not automatically include any content headers. + # + # As an end-user you don't really need `stream=...`. It's only + # useful when creating response instances having received a stream + # from the transport API. + self.stream = stream + + self._num_bytes_downloaded = 0 + + def _prepare(self, default_headers: dict[str, str]) -> None: + for key, value in default_headers.items(): + # Ignore Transfer-Encoding if the Content-Length has been set explicitly. + if key.lower() == "transfer-encoding" and "content-length" in self.headers: + continue + self.headers.setdefault(key, value) + + @property + def elapsed(self) -> datetime.timedelta: + """ + Returns the time taken for the complete request/response + cycle to complete. + """ + if not hasattr(self, "_elapsed"): + stream_elapsed: datetime.timedelta | None = getattr(self.stream, "elapsed", None) + if stream_elapsed is not None: + return stream_elapsed + raise RuntimeError("'.elapsed' may only be accessed after the response has been read or closed.") + return self._elapsed + + @elapsed.setter + def elapsed(self, elapsed: datetime.timedelta) -> None: + self._elapsed = elapsed + + @property + def request(self) -> Request: + """ + Returns the request instance associated to the current response. + """ + if self._request is None: + raise RuntimeError("The request instance has not been set on this response.") + return self._request + + @request.setter + def request(self, value: Request) -> None: + self._request = value + + @property + def http_version(self) -> str: + try: + http_version: bytes = self.extensions["http_version"] + except KeyError: + return "HTTP/1.1" + else: + return http_version.decode("ascii", errors="ignore") + + @property + def reason_phrase(self) -> str: + try: + reason_phrase: bytes = self.extensions["reason_phrase"] + except KeyError: + return codes.get_reason_phrase(self.status_code) + else: + return reason_phrase.decode("ascii", errors="ignore") + + @property + def url(self) -> URL: + """ + Returns the URL for which the request was made. + """ + return self.request.url + + @property + def content(self) -> bytes: + if not hasattr(self, "_content"): + raise ResponseNotRead() + return self._content + + @property + def text(self) -> str: + if not hasattr(self, "_text"): + content = self.content + if not content: + self._text = "" + else: + decoder = TextDecoder(encoding=self.encoding or "utf-8") + self._text = "".join([decoder.decode(self.content), decoder.flush()]) + return self._text + + @property + def encoding(self) -> str | None: + """ + Return an encoding to use for decoding the byte content into text. + The priority for determining this is given by... + + * `.encoding = <>` has been set explicitly. + * The encoding as specified by the charset parameter in the Content-Type header. + * The encoding as determined by `default_encoding`, which may either be + a string like "utf-8" indicating the encoding to use, or may be a callable + which enables charset autodetection. + """ + if not hasattr(self, "_encoding"): + encoding = self.charset_encoding + if encoding is None or not _is_known_encoding(encoding): + if isinstance(self.default_encoding, str): + encoding = self.default_encoding + elif hasattr(self, "_content"): + encoding = self.default_encoding(self._content) + self._encoding = encoding or "utf-8" + return self._encoding + + @encoding.setter + def encoding(self, value: str) -> None: + """ + Set the encoding to use for decoding the byte content into text. + + If the `text` attribute has been accessed, attempting to set the + encoding will throw a ValueError. + """ + if hasattr(self, "_text"): + raise ValueError("Setting encoding after `text` has been accessed is not allowed.") + self._encoding = value + + @property + def charset_encoding(self) -> str | None: + """ + Return the encoding, as specified by the Content-Type header. + """ + content_type = self.headers.get("Content-Type") + if content_type is None: + return None + + return _parse_content_type_charset(content_type) + + def _get_content_decoder(self) -> ContentDecoder: + """ + Returns a decoder instance which can be used to decode the raw byte + content, depending on the Content-Encoding used in the response. + """ + if not hasattr(self, "_decoder"): + values = self.headers.get_list("content-encoding", split_commas=True) + encodings = [value.strip().lower() for value in values] + decoder = MultiDecoder([encoding for encoding in encodings if encoding != "identity"]) + if len(decoder.children) == 1: + self._decoder = decoder.children[0] + elif decoder.children: + self._decoder = decoder + else: + self._decoder = IdentityDecoder() + + return self._decoder + + @property + def is_informational(self) -> bool: + """ + A property which is `True` for 1xx status codes, `False` otherwise. + """ + return codes.is_informational(self.status_code) + + @property + def is_success(self) -> bool: + """ + A property which is `True` for 2xx status codes, `False` otherwise. + """ + return codes.is_success(self.status_code) + + @property + def is_redirect(self) -> bool: + """ + A property which is `True` for 3xx status codes, `False` otherwise. + + Note that not all responses with a 3xx status code indicate a URL redirect. + + Use `response.has_redirect_location` to determine responses with a properly + formed URL redirection. + """ + return codes.is_redirect(self.status_code) + + @property + def is_client_error(self) -> bool: + """ + A property which is `True` for 4xx status codes, `False` otherwise. + """ + return codes.is_client_error(self.status_code) + + @property + def is_server_error(self) -> bool: + """ + A property which is `True` for 5xx status codes, `False` otherwise. + """ + return codes.is_server_error(self.status_code) + + @property + def is_error(self) -> bool: + """ + A property which is `True` for 4xx and 5xx status codes, `False` otherwise. + """ + return codes.is_error(self.status_code) + + @property + def has_redirect_location(self) -> bool: + """ + Returns True for 3xx responses with a properly formed URL redirection, + `False` otherwise. + """ + return ( + self.status_code + in ( + # 301 (Cacheable redirect. Method may change to GET.) + codes.MOVED_PERMANENTLY, + # 302 (Uncacheable redirect. Method may change to GET.) + codes.FOUND, + # 303 (Client should make a GET or HEAD request.) + codes.SEE_OTHER, + # 307 (Equiv. 302, but retain method) + codes.TEMPORARY_REDIRECT, + # 308 (Equiv. 301, but retain method) + codes.PERMANENT_REDIRECT, + ) + and "Location" in self.headers + ) + + def raise_for_status(self) -> Response: + """ + Raise the `HTTPStatusError` if one occurred. + """ + request = self._request + if request is None: + raise RuntimeError( + "Cannot call `raise_for_status` as the request instance has not been set on this response." + ) + + if self.is_success: + return self + + if self.has_redirect_location: + message = ( + "{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n" + "Redirect location: '{0.headers[location]}'\n" + "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}" + ) + else: + message = ( + "{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n" + "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}" + ) + + status_class = self.status_code // 100 + error_types = { + 1: "Informational response", + 3: "Redirect response", + 4: "Client error", + 5: "Server error", + } + error_type = error_types.get(status_class, "Invalid status code") + message = message.format(self, error_type=error_type) + raise HTTPStatusError(message, request=request, response=self) + + def json(self, **kwargs: typing.Any) -> typing.Any: + return jsonlib.loads(self.content, **kwargs) + + @property + def cookies(self) -> Cookies: + if not hasattr(self, "_cookies"): + self._cookies = Cookies() + self._cookies.extract_cookies(self) + return self._cookies + + @property + def links(self) -> dict[str | None, dict[str, str]]: + """ + Returns the parsed header links of the response, if any + """ + header = self.headers.get("link") + if header is None: + return {} + + return {(link.get("rel") or link.get("url")): link for link in _parse_header_links(header)} + + @property + def num_bytes_downloaded(self) -> int: + return self._num_bytes_downloaded + + def __repr__(self) -> str: + return f"" + + def __getstate__(self) -> dict[str, typing.Any]: + return { + name: value + for name, value in self.__dict__.items() + if name not in ["extensions", "stream", "is_closed", "_decoder"] + } + + def __setstate__(self, state: dict[str, typing.Any]) -> None: + for name, value in state.items(): + setattr(self, name, value) + self.is_closed = True + self.extensions = {} + self.stream = UnattachedStream() + + def read(self) -> bytes: + """ + Read and return the response content. + """ + if not hasattr(self, "_content"): + self._content = b"".join(self.iter_bytes()) + return self._content + + def iter_bytes(self, chunk_size: int | None = None) -> typing.Iterator[bytes]: + """ + A byte-iterator over the decoded response content. + This allows us to handle gzip, deflate, brotli, and zstd encoded responses. + """ + if hasattr(self, "_content"): + chunk_size = len(self._content) if chunk_size is None else chunk_size + for i in range(0, len(self._content), max(chunk_size, 1)): + yield self._content[i : i + chunk_size] + else: + decoder = self._get_content_decoder() + chunker = ByteChunker(chunk_size=chunk_size) + with request_context(request=self._request): + for raw_bytes in self.iter_raw(): + for decoded in decoder.decode(raw_bytes): + for chunk in chunker.decode(decoded): + yield chunk + for decoded in decoder.flush(): + for chunk in chunker.decode(decoded): + yield chunk # pragma: no cover + for chunk in chunker.flush(): + yield chunk + + def iter_text(self, chunk_size: int | None = None) -> typing.Iterator[str]: + """ + A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + decoder = TextDecoder(encoding=self.encoding or "utf-8") + chunker = TextChunker(chunk_size=chunk_size) + with request_context(request=self._request): + for byte_content in self.iter_bytes(): + text_content = decoder.decode(byte_content) + for chunk in chunker.decode(text_content): + yield chunk + text_content = decoder.flush() + for chunk in chunker.decode(text_content): + yield chunk # pragma: no cover + for chunk in chunker.flush(): + yield chunk + + def iter_lines(self) -> typing.Iterator[str]: + decoder = LineDecoder() + with request_context(request=self._request): + for text in self.iter_text(): + for line in decoder.decode(text): + yield line + for line in decoder.flush(): + yield line + + def iter_raw(self, chunk_size: int | None = None) -> typing.Iterator[bytes]: + """ + A byte-iterator over the raw response content. + """ + if self.is_stream_consumed: + raise StreamConsumed() + if self.is_closed: + raise StreamClosed() + if not isinstance(self.stream, SyncByteStream): + raise RuntimeError("Attempted to call a sync iterator on an async stream.") + + self.is_stream_consumed = True + self._num_bytes_downloaded = 0 + chunker = ByteChunker(chunk_size=chunk_size) + + try: + with request_context(request=self._request): + for raw_stream_bytes in self.stream: + self._num_bytes_downloaded += len(raw_stream_bytes) + for chunk in chunker.decode(raw_stream_bytes): + yield chunk + + for chunk in chunker.flush(): + yield chunk + finally: + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + Automatically called if the response body is read to completion. + """ + if not isinstance(self.stream, SyncByteStream): + raise RuntimeError("Attempted to call a sync close on an async stream.") + + if not self.is_closed: + self.is_closed = True + with request_context(request=self._request): + self.stream.close() + + async def aread(self) -> bytes: + """ + Read and return the response content. + """ + if not hasattr(self, "_content"): + async with contextlib.aclosing(self.aiter_bytes()) as parts: + self._content = b"".join([part async for part in parts]) + return self._content + + async def aiter_bytes(self, chunk_size: int | None = None) -> typing.AsyncGenerator[bytes, None]: + """ + A byte-iterator over the decoded response content. + This allows us to handle gzip, deflate, brotli, and zstd encoded responses. + """ + if hasattr(self, "_content"): + chunk_size = len(self._content) if chunk_size is None else chunk_size + for i in range(0, len(self._content), max(chunk_size, 1)): + yield self._content[i : i + chunk_size] + else: + decoder = self._get_content_decoder() + chunker = ByteChunker(chunk_size=chunk_size) + with request_context(request=self._request): + async with contextlib.aclosing(self.aiter_raw()) as raw_stream: + async for raw_bytes in raw_stream: + for decoded in decoder.decode(raw_bytes): + for chunk in chunker.decode(decoded): + yield chunk + for decoded in decoder.flush(): + for chunk in chunker.decode(decoded): + yield chunk # pragma: no cover + for chunk in chunker.flush(): + yield chunk + + async def aiter_text(self, chunk_size: int | None = None) -> typing.AsyncIterator[str]: + """ + A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + decoder = TextDecoder(encoding=self.encoding or "utf-8") + chunker = TextChunker(chunk_size=chunk_size) + with request_context(request=self._request): + async for byte_content in self.aiter_bytes(): + text_content = decoder.decode(byte_content) + for chunk in chunker.decode(text_content): + yield chunk + text_content = decoder.flush() + for chunk in chunker.decode(text_content): + yield chunk # pragma: no cover + for chunk in chunker.flush(): + yield chunk + + async def aiter_lines(self) -> typing.AsyncIterator[str]: + decoder = LineDecoder() + with request_context(request=self._request): + async for text in self.aiter_text(): + for line in decoder.decode(text): + yield line + for line in decoder.flush(): + yield line + + async def aiter_raw(self, chunk_size: int | None = None) -> typing.AsyncGenerator[bytes, None]: + """ + A byte-iterator over the raw response content. + """ + if self.is_stream_consumed: + raise StreamConsumed() + if self.is_closed: + raise StreamClosed() + if not isinstance(self.stream, AsyncByteStream): + raise RuntimeError("Attempted to call an async iterator on a sync stream.") + + self.is_stream_consumed = True + self._num_bytes_downloaded = 0 + chunker = ByteChunker(chunk_size=chunk_size) + + stream = self.stream.__aiter__() + try: + with request_context(request=self._request): + async for raw_stream_bytes in stream: + self._num_bytes_downloaded += len(raw_stream_bytes) + for chunk in chunker.decode(raw_stream_bytes): + yield chunk + + for chunk in chunker.flush(): + yield chunk + finally: + if isinstance(stream, AsyncGenerator): + await stream.aclose() + await self.aclose() + + async def aclose(self) -> None: + """ + Close the response and release the connection. + Automatically called if the response body is read to completion. + """ + if not isinstance(self.stream, AsyncByteStream): + raise RuntimeError("Attempted to call an async close on a sync stream.") + + if not self.is_closed: + self.is_closed = True + with request_context(request=self._request): + await self.stream.aclose() + + +class Cookies(typing.MutableMapping[str, str]): + """ + HTTP Cookies, as a mutable mapping. + """ + + def __init__(self, cookies: CookieTypes | None = None) -> None: + if cookies is None or isinstance(cookies, dict): + self.jar = CookieJar() + if isinstance(cookies, dict): + for key, value in cookies.items(): + self.set(key, value) + elif isinstance(cookies, list): + self.jar = CookieJar() + for key, value in cookies: + self.set(key, value) + elif isinstance(cookies, Cookies): + self.jar = CookieJar() + for cookie in cookies.jar: + self.jar.set_cookie(cookie) + else: + self.jar = cookies + + def extract_cookies(self, response: Response) -> None: + """ + Loads any cookies based on the response `Set-Cookie` headers. + """ + if "set-cookie" not in response.headers and "set-cookie2" not in response.headers: + return + + urllib_response = self._CookieCompatResponse(response) + urllib_request = self._CookieCompatRequest(response.request) + + self.jar.extract_cookies(urllib_response, urllib_request) # type: ignore + + def set_cookie_header(self, request: Request) -> None: + """ + Sets an appropriate 'Cookie:' HTTP header on the `Request`. + """ + urllib_request = self._CookieCompatRequest(request) + self.jar.add_cookie_header(urllib_request) + + def set(self, name: str, value: str, domain: str = "", path: str = "/") -> None: + """ + Set a cookie value by name. May optionally include domain and path. + """ + kwargs = { + "version": 0, + "name": name, + "value": value, + "port": None, + "port_specified": False, + "domain": domain, + "domain_specified": bool(domain), + "domain_initial_dot": domain.startswith("."), + "path": path, + "path_specified": bool(path), + "secure": False, + "expires": None, + "discard": True, + "comment": None, + "comment_url": None, + "rest": {"HttpOnly": None}, + "rfc2109": False, + } + cookie = Cookie(**kwargs) # type: ignore + self.jar.set_cookie(cookie) + + def get( # type: ignore + self, + name: str, + default: str | None = None, + domain: str | None = None, + path: str | None = None, + ) -> str | None: + """ + Get a cookie by name. May optionally include domain and path + in order to specify exactly which cookie to retrieve. + """ + value = None + for cookie in self.jar: + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + if value is not None: + message = f"Multiple cookies exist with name={name}" + raise CookieConflict(message) + value = cookie.value + + if value is None: + return default + return value + + def delete( + self, + name: str, + domain: str | None = None, + path: str | None = None, + ) -> None: + """ + Delete a cookie by name. May optionally include domain and path + in order to specify exactly which cookie to delete. + """ + if domain is not None and path is not None: + return self.jar.clear(domain, path, name) + + remove = [ + cookie + for cookie in self.jar + if cookie.name == name + and (domain is None or cookie.domain == domain) + and (path is None or cookie.path == path) + ] + + for cookie in remove: + self.jar.clear(cookie.domain, cookie.path, cookie.name) + + def clear(self, domain: str | None = None, path: str | None = None) -> None: + """ + Delete all cookies. Optionally include a domain and path in + order to only delete a subset of all the cookies. + """ + args: list[str] = [] + if domain is not None: + args.append(domain) + if path is not None: + assert domain is not None + args.append(path) + self.jar.clear(*args) + + def update(self, cookies: CookieTypes | None = None) -> None: # type: ignore + cookies = Cookies(cookies) + for cookie in cookies.jar: + self.jar.set_cookie(cookie) + + def __setitem__(self, name: str, value: str) -> None: + return self.set(name, value) + + def __getitem__(self, name: str) -> str: + value = self.get(name) + if value is None: + raise KeyError(name) + return value + + def __delitem__(self, name: str) -> None: + return self.delete(name) + + def __len__(self) -> int: + return len(self.jar) + + def __iter__(self) -> typing.Iterator[str]: + return (cookie.name for cookie in self.jar) + + def __bool__(self) -> bool: + for _ in self.jar: + return True + return False + + def __repr__(self) -> str: + cookies_repr = ", ".join( + [f"" for cookie in self.jar] + ) + + return f"" + + class _CookieCompatRequest(urllib.request.Request): + """ + Wraps a `Request` instance up in a compatibility interface suitable + for use with `CookieJar` operations. + """ + + def __init__(self, request: Request) -> None: + super().__init__( + url=str(request.url), + headers=dict(request.headers), + method=request.method, + ) + self.request = request + + def add_unredirected_header(self, key: str, val: str) -> None: + super().add_unredirected_header(key, val) + self.request.headers[key] = val + + class _CookieCompatResponse: + """ + Wraps a `Request` instance up in a compatibility interface suitable + for use with `CookieJar` operations. + """ + + def __init__(self, response: Response) -> None: + self.response = response + + def info(self) -> email.message.Message: + info = email.message.Message() + for key, value in self.response.headers.multi_items(): + # Note that setting `info[key]` here is an "append" operation, + # not a "replace" operation. + # https://docs.python.org/3/library/email.compat32-message.html#email.message.Message.__setitem__ + info[key] = value + return info diff --git a/venv/lib/python3.12/site-packages/httpx2/_multipart.py b/venv/lib/python3.12/site-packages/httpx2/_multipart.py new file mode 100644 index 0000000..2c65610 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_multipart.py @@ -0,0 +1,284 @@ +from __future__ import annotations + +import io +import mimetypes +import os +import re +import typing +from pathlib import Path + +from ._types import ( + AsyncByteStream, + FileContent, + FileTypes, + RequestData, + RequestFiles, + SyncByteStream, +) +from ._utils import ( + peek_filelike_length, + primitive_value_to_str, + to_bytes, +) + +_HTML5_FORM_ENCODING_REPLACEMENTS = {'"': "%22", "\\": "\\\\"} +_HTML5_FORM_ENCODING_REPLACEMENTS.update({chr(c): f"%{c:02X}" for c in range(0x1F + 1) if c != 0x1B}) +_HTML5_FORM_ENCODING_RE = re.compile(r"|".join([re.escape(c) for c in _HTML5_FORM_ENCODING_REPLACEMENTS.keys()])) +_HEADER_NAME_RE = re.compile(r"[!#$%&'*+\-.^_`|~0-9A-Za-z]+") +_FORBIDDEN_HEADER_VALUE_CHARS_RE = re.compile(r"[\x00-\x08\x0a-\x1f\x7f]") + + +def _format_form_param(name: str, value: str) -> bytes: + """ + Encode a name/value pair within a multipart form. + """ + + def replacer(match: typing.Match[str]) -> str: + return _HTML5_FORM_ENCODING_REPLACEMENTS[match.group(0)] + + value = _HTML5_FORM_ENCODING_RE.sub(replacer, value) + return f'{name}="{value}"'.encode() + + +def _guess_content_type(filename: str | None) -> str | None: + """ + Guesses the mimetype based on a filename. Defaults to `application/octet-stream`. + + Returns `None` if `filename` is `None` or empty. + """ + if filename: + return mimetypes.guess_type(filename)[0] or "application/octet-stream" + return None + + +def get_multipart_boundary_from_content_type( + content_type: bytes | None, +) -> bytes | None: + if not content_type or not content_type.startswith(b"multipart/form-data"): + return None + # parse boundary according to + # https://www.rfc-editor.org/rfc/rfc2046#section-5.1.1 + if b";" in content_type: + for section in content_type.split(b";"): + if section.strip().lower().startswith(b"boundary="): + return section.strip()[len(b"boundary=") :].strip(b'"') + return None + + +class DataField: + """ + A single form field item, within a multipart form field. + """ + + def __init__(self, name: str, value: str | bytes | int | float | None) -> None: + if not isinstance(name, str): + raise TypeError(f"Invalid type for name. Expected str, got {type(name)}: {name!r}") + if value is not None and not isinstance(value, (str, bytes, int, float)): + raise TypeError(f"Invalid type for value. Expected primitive type, got {type(value)}: {value!r}") + self.name = name + self.value: str | bytes = value if isinstance(value, bytes) else primitive_value_to_str(value) + + def render_headers(self) -> bytes: + if not hasattr(self, "_headers"): + name = _format_form_param("name", self.name) + self._headers = b"".join([b"Content-Disposition: form-data; ", name, b"\r\n\r\n"]) + + return self._headers + + def render_data(self) -> bytes: + if not hasattr(self, "_data"): + self._data = to_bytes(self.value) + + return self._data + + def get_length(self) -> int: + headers = self.render_headers() + data = self.render_data() + return len(headers) + len(data) + + def render(self) -> typing.Iterator[bytes]: + yield self.render_headers() + yield self.render_data() + + +class FileField: + """ + A single file field item, within a multipart form field. + """ + + CHUNK_SIZE = 64 * 1024 + + def __init__(self, name: str, value: FileTypes) -> None: + self.name = name + + fileobj: FileContent + + headers: dict[str, str] = {} + content_type: str | None = None + + # This large tuple based API largely mirror's requests' API + # It would be good to think of better APIs for this that we could + # include in httpx 2.0 since variable length tuples(especially of 4 elements) + # are quite unwieldy + if isinstance(value, tuple): + if len(value) == 2: + # neither the 3rd parameter (content_type) nor the 4th (headers) + # was included + filename, fileobj = value + elif len(value) == 3: + filename, fileobj, content_type = value + else: + # all 4 parameters included + filename, fileobj, content_type, headers = value # type: ignore + else: + filename = Path(str(getattr(value, "name", "upload"))).name + fileobj = value + + if content_type is None: + content_type = _guess_content_type(filename) + + has_content_type_header = any("content-type" in key.lower() for key in headers) + if content_type is not None and not has_content_type_header: + # note that unlike requests, we ignore the content_type provided in the 3rd + # tuple element if it is also included in the headers requests does + # the opposite (it overwrites the headerwith the 3rd tuple element) + headers["Content-Type"] = content_type + + if isinstance(fileobj, io.StringIO): + raise TypeError("Multipart file uploads require 'io.BytesIO', not 'io.StringIO'.") + if isinstance(fileobj, io.TextIOBase): + raise TypeError("Multipart file uploads must be opened in binary mode, not text mode.") + + self.filename = filename + self.file = fileobj + self.headers = headers + + def get_length(self) -> int | None: + headers = self.render_headers() + + if isinstance(self.file, (str, bytes)): + return len(headers) + len(to_bytes(self.file)) + + file_length = peek_filelike_length(self.file) + + # If we can't determine the filesize without reading it into memory, + # then return `None` here, to indicate an unknown file length. + if file_length is None: + return None + + return len(headers) + file_length + + def render_headers(self) -> bytes: + if not hasattr(self, "_headers"): + parts = [ + b"Content-Disposition: form-data; ", + _format_form_param("name", self.name), + ] + if self.filename: + filename = _format_form_param("filename", self.filename) + parts.extend([b"; ", filename]) + for header_name, header_value in self.headers.items(): + if _HEADER_NAME_RE.fullmatch(header_name) is None: + raise ValueError("Invalid multipart header name.") + if _FORBIDDEN_HEADER_VALUE_CHARS_RE.search(header_value) is not None: + raise ValueError("Invalid control character in multipart header value.") + key, val = f"\r\n{header_name}: ".encode(), header_value.encode() + parts.extend([key, val]) + parts.append(b"\r\n\r\n") + self._headers = b"".join(parts) + + return self._headers + + def render_data(self) -> typing.Iterator[bytes]: + if isinstance(self.file, (str, bytes)): + yield to_bytes(self.file) + return + + if hasattr(self.file, "seek"): + try: + self.file.seek(0) + except io.UnsupportedOperation: + pass + + chunk = self.file.read(self.CHUNK_SIZE) + while chunk: + yield to_bytes(chunk) + chunk = self.file.read(self.CHUNK_SIZE) + + def render(self) -> typing.Iterator[bytes]: + yield self.render_headers() + yield from self.render_data() + + +class MultipartStream(SyncByteStream, AsyncByteStream): + """ + Request content as streaming multipart encoded form data. + """ + + def __init__( + self, + data: RequestData, + files: RequestFiles, + boundary: bytes | None = None, + ) -> None: + if boundary is None: + boundary = os.urandom(16).hex().encode("ascii") + + self.boundary = boundary + self.content_type = f"multipart/form-data; boundary={boundary.decode('ascii')}" + self.fields = list(self._iter_fields(data, files)) + + def _iter_fields(self, data: RequestData, files: RequestFiles) -> typing.Iterator[FileField | DataField]: + for name, value in data.items(): + if isinstance(value, (tuple, list)): + for item in value: + yield DataField(name=name, value=item) + else: + yield DataField(name=name, value=value) + + file_items = files.items() if isinstance(files, typing.Mapping) else files + for name, value in file_items: + yield FileField(name=name, value=value) + + def iter_chunks(self) -> typing.Iterator[bytes]: + for field in self.fields: + yield b"--%s\r\n" % self.boundary + yield from field.render() + yield b"\r\n" + yield b"--%s--\r\n" % self.boundary + + def get_content_length(self) -> int | None: + """ + Return the length of the multipart encoded content, or `None` if + any of the files have a length that cannot be determined upfront. + """ + boundary_length = len(self.boundary) + length = 0 + + for field in self.fields: + field_length = field.get_length() + if field_length is None: + return None + + length += 2 + boundary_length + 2 # b"--{boundary}\r\n" + length += field_length + length += 2 # b"\r\n" + + length += 2 + boundary_length + 4 # b"--{boundary}--\r\n" + return length + + # Content stream interface. + + def get_headers(self) -> dict[str, str]: + content_length = self.get_content_length() + content_type = self.content_type + if content_length is None: + return {"Transfer-Encoding": "chunked", "Content-Type": content_type} + return {"Content-Length": str(content_length), "Content-Type": content_type} + + def __iter__(self) -> typing.Iterator[bytes]: + yield from self.iter_chunks() + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + for chunk in self.iter_chunks(): + yield chunk diff --git a/venv/lib/python3.12/site-packages/httpx2/_sse.py b/venv/lib/python3.12/site-packages/httpx2/_sse.py new file mode 100644 index 0000000..df2be6a --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_sse.py @@ -0,0 +1,226 @@ +""" +Server-sent events support, derived from httpx-sse (https://github.com/florimondmanca/httpx-sse). + +``` +MIT License + +Copyright (c) 2022 Florimond Manca + +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. +``` +""" + +from __future__ import annotations + +import json as jsonlib +from collections.abc import AsyncIterator, Iterator +from dataclasses import dataclass + +from ._config import DEFAULT_MAX_EVENT_SIZE_BYTES +from ._exceptions import TransportError, request_context +from ._models import Response + +__all__ = ["EventSource", "SSEError", "ServerSentEvent"] + + +class SSEError(TransportError): + """ + An error that occurred while connecting to a server-sent events endpoint. + """ + + +@dataclass(frozen=True) +class ServerSentEvent: + event: str = "message" + data: str = "" + id: str = "" + retry: int | None = None + + def json(self) -> object: + return jsonlib.loads(self.data) + + +class _SSEEventDecoder: + def __init__(self, max_event_size: int | None = None) -> None: + self._max_event_size = max_event_size + self._event = "" + self._data: list[str] = [] + self._event_size = 0 + self._last_event_id = "" + self._retry: int | None = None + self._pending = False + + def decode(self, line: str) -> ServerSentEvent | None: + if not line: + self._event_size = 0 + if not self._pending: + return None + + sse = ServerSentEvent( + event=self._event or "message", + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + self._event = "" + self._data = [] + self._retry = None + self._pending = False + return sse + + if line.startswith(":"): + return None + + self._event_size += len(line.encode("utf-8")) + self._check_size() + + fieldname, _, value = line.partition(":") + value = value[1:] if value.startswith(" ") else value + + if fieldname == "event": + self._event = value + self._pending = True + elif fieldname == "data": + self._data.append(value) + self._pending = True + elif fieldname == "id": + if "\0" not in value: + self._last_event_id = value + self._pending = True + elif fieldname == "retry": + try: + self._retry = int(value) + self._pending = True + except ValueError: + pass + + return None + + def check_pending(self, pending_size: int) -> None: + """ + Bound the total bytes buffered for the in-progress event, including + a trailing line that has not been terminated by a newline yet. + """ + self._check_size(pending_size) + + def _check_size(self, pending_size: int = 0) -> None: + if self._max_event_size is not None and self._event_size + pending_size > self._max_event_size: + raise SSEError(f"Server-sent event exceeded the {self._max_event_size} byte limit.") + + +class _SSELineDecoder: + def __init__(self) -> None: + self._parts: list[str] = [] + self._pending_size = 0 + self._trailing_cr = False + + @property + def pending_size(self) -> int: + return self._pending_size + + def decode(self, text: str) -> list[str]: + if self._trailing_cr: + text = "\r" + text + self._trailing_cr = False + if text.endswith("\r"): + self._trailing_cr = True + text = text[:-1] + + text = text.replace("\r\n", "\n").replace("\r", "\n") + if "\n" not in text: + self._append(text) + return [] + + lines = text.split("\n") + self._append(lines[0]) + lines[0] = self._consume_pending() + self._append(lines.pop()) + return lines + + def flush(self) -> list[str]: + if self._trailing_cr: + self._append("\n") + self._trailing_cr = False + buffer = self._consume_pending() + if not buffer: + return [] + return buffer.split("\n") + + def _append(self, text: str) -> None: + if text: + self._parts.append(text) + self._pending_size += len(text.encode("utf-8")) + + def _consume_pending(self) -> str: + pending = "".join(self._parts) + self._parts = [] + self._pending_size = 0 + return pending + + +class _SSEParser: + def __init__(self, max_event_size: int | None = None) -> None: + self._event_decoder = _SSEEventDecoder(max_event_size) + self._line_decoder = _SSELineDecoder() + + def decode(self, text: str) -> Iterator[ServerSentEvent]: + yield from self._decode_lines(self._line_decoder.decode(text)) + self._event_decoder.check_pending(self._line_decoder.pending_size) + + def flush(self) -> Iterator[ServerSentEvent]: + yield from self._decode_lines(self._line_decoder.flush()) + + def _decode_lines(self, lines: list[str]) -> Iterator[ServerSentEvent]: + for line in lines: + sse = self._event_decoder.decode(line) + if sse is not None: + yield sse + + +class EventSource: + def __init__(self, response: Response, max_event_size: int | None = DEFAULT_MAX_EVENT_SIZE_BYTES) -> None: + self._response = response + self._max_event_size = max_event_size + + @property + def response(self) -> Response: + return self._response + + def _check_content_type(self) -> None: + content_type, _, _ = self._response.headers.get("content-type", "").partition(";") + if content_type.strip().lower() != "text/event-stream": + raise SSEError(f"Expected response with content type 'text/event-stream', got {content_type.strip()!r}.") + + def __iter__(self) -> Iterator[ServerSentEvent]: + with request_context(request=self._response.request): + self._check_content_type() + parser = _SSEParser(self._max_event_size) + for chunk in self._response.iter_text(): + yield from parser.decode(chunk) + yield from parser.flush() + + async def __aiter__(self) -> AsyncIterator[ServerSentEvent]: + with request_context(request=self._response.request): + self._check_content_type() + parser = _SSEParser(self._max_event_size) + async for chunk in self._response.aiter_text(): + for sse in parser.decode(chunk): + yield sse + for sse in parser.flush(): + yield sse diff --git a/venv/lib/python3.12/site-packages/httpx2/_status_codes.py b/venv/lib/python3.12/site-packages/httpx2/_status_codes.py new file mode 100644 index 0000000..8afc6a9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_status_codes.py @@ -0,0 +1,189 @@ +from __future__ import annotations + +import warnings +from enum import EnumMeta, IntEnum + +__all__ = ["codes"] + +# Constant names that were renamed by RFC 9110, kept as deprecated aliases +# mapping each old name to its canonical replacement. +_DEPRECATED_CODES = { + "REQUEST_ENTITY_TOO_LARGE": "CONTENT_TOO_LARGE", + "REQUEST_URI_TOO_LONG": "URI_TOO_LONG", + "REQUESTED_RANGE_NOT_SATISFIABLE": "RANGE_NOT_SATISFIABLE", + "UNPROCESSABLE_ENTITY": "UNPROCESSABLE_CONTENT", +} + + +class _CodesMeta(EnumMeta): + def __getattribute__(cls, name: str) -> object: + if name in _DEPRECATED_CODES: + new_name = _DEPRECATED_CODES[name] + warnings.warn( + f"'{name}' is deprecated. Use '{new_name}' instead.", + category=DeprecationWarning, + stacklevel=2, + ) + return super().__getattribute__(name) + + +class codes(IntEnum, metaclass=_CodesMeta): + """HTTP status codes and reason phrases + + Status codes from the following RFCs are all observed: + + * RFC 9110: HTTP Semantics, obsoletes 7231 & 7238 + * RFC 6585: Additional HTTP Status Codes + * RFC 3229: Delta encoding in HTTP + * RFC 4918: HTTP Extensions for WebDAV, obsoletes 2518 + * RFC 5842: Binding Extensions to WebDAV + * RFC 2295: Transparent Content Negotiation in HTTP + * RFC 2774: An HTTP Extension Framework + * RFC 7540: Hypertext Transfer Protocol Version 2 (HTTP/2) + * RFC 2324: Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0) + * RFC 7725: An HTTP Status Code to Report Legal Obstacles + * RFC 8297: An HTTP Status Code for Indicating Hints + * RFC 8470: Using Early Data in HTTP + """ + + def __new__(cls, value: int, phrase: str = "") -> codes: + obj = int.__new__(cls, value) + obj._value_ = value + + obj.phrase = phrase # type: ignore[attr-defined] + return obj + + def __str__(self) -> str: + return str(self.value) + + @classmethod + def get_reason_phrase(cls, value: int) -> str: + try: + return codes(value).phrase # type: ignore + except ValueError: + return "" + + @classmethod + def is_informational(cls, value: int) -> bool: + """ + Returns `True` for 1xx status codes, `False` otherwise. + """ + return 100 <= value <= 199 + + @classmethod + def is_success(cls, value: int) -> bool: + """ + Returns `True` for 2xx status codes, `False` otherwise. + """ + return 200 <= value <= 299 + + @classmethod + def is_redirect(cls, value: int) -> bool: + """ + Returns `True` for 3xx status codes, `False` otherwise. + """ + return 300 <= value <= 399 + + @classmethod + def is_client_error(cls, value: int) -> bool: + """ + Returns `True` for 4xx status codes, `False` otherwise. + """ + return 400 <= value <= 499 + + @classmethod + def is_server_error(cls, value: int) -> bool: + """ + Returns `True` for 5xx status codes, `False` otherwise. + """ + return 500 <= value <= 599 + + @classmethod + def is_error(cls, value: int) -> bool: + """ + Returns `True` for 4xx or 5xx status codes, `False` otherwise. + """ + return 400 <= value <= 599 + + # informational + CONTINUE = 100, "Continue" + SWITCHING_PROTOCOLS = 101, "Switching Protocols" + PROCESSING = 102, "Processing" + EARLY_HINTS = 103, "Early Hints" + + # success + OK = 200, "OK" + CREATED = 201, "Created" + ACCEPTED = 202, "Accepted" + NON_AUTHORITATIVE_INFORMATION = 203, "Non-Authoritative Information" + NO_CONTENT = 204, "No Content" + RESET_CONTENT = 205, "Reset Content" + PARTIAL_CONTENT = 206, "Partial Content" + MULTI_STATUS = 207, "Multi-Status" + ALREADY_REPORTED = 208, "Already Reported" + IM_USED = 226, "IM Used" + + # redirection + MULTIPLE_CHOICES = 300, "Multiple Choices" + MOVED_PERMANENTLY = 301, "Moved Permanently" + FOUND = 302, "Found" + SEE_OTHER = 303, "See Other" + NOT_MODIFIED = 304, "Not Modified" + USE_PROXY = 305, "Use Proxy" + TEMPORARY_REDIRECT = 307, "Temporary Redirect" + PERMANENT_REDIRECT = 308, "Permanent Redirect" + + # client error + BAD_REQUEST = 400, "Bad Request" + UNAUTHORIZED = 401, "Unauthorized" + PAYMENT_REQUIRED = 402, "Payment Required" + FORBIDDEN = 403, "Forbidden" + NOT_FOUND = 404, "Not Found" + METHOD_NOT_ALLOWED = 405, "Method Not Allowed" + NOT_ACCEPTABLE = 406, "Not Acceptable" + PROXY_AUTHENTICATION_REQUIRED = 407, "Proxy Authentication Required" + REQUEST_TIMEOUT = 408, "Request Timeout" + CONFLICT = 409, "Conflict" + GONE = 410, "Gone" + LENGTH_REQUIRED = 411, "Length Required" + PRECONDITION_FAILED = 412, "Precondition Failed" + CONTENT_TOO_LARGE = 413, "Content Too Large" + URI_TOO_LONG = 414, "URI Too Long" + UNSUPPORTED_MEDIA_TYPE = 415, "Unsupported Media Type" + RANGE_NOT_SATISFIABLE = 416, "Range Not Satisfiable" + EXPECTATION_FAILED = 417, "Expectation Failed" + IM_A_TEAPOT = 418, "I'm a teapot" + MISDIRECTED_REQUEST = 421, "Misdirected Request" + UNPROCESSABLE_CONTENT = 422, "Unprocessable Content" + LOCKED = 423, "Locked" + FAILED_DEPENDENCY = 424, "Failed Dependency" + TOO_EARLY = 425, "Too Early" + UPGRADE_REQUIRED = 426, "Upgrade Required" + PRECONDITION_REQUIRED = 428, "Precondition Required" + TOO_MANY_REQUESTS = 429, "Too Many Requests" + REQUEST_HEADER_FIELDS_TOO_LARGE = 431, "Request Header Fields Too Large" + UNAVAILABLE_FOR_LEGAL_REASONS = 451, "Unavailable For Legal Reasons" + + # server errors + INTERNAL_SERVER_ERROR = 500, "Internal Server Error" + NOT_IMPLEMENTED = 501, "Not Implemented" + BAD_GATEWAY = 502, "Bad Gateway" + SERVICE_UNAVAILABLE = 503, "Service Unavailable" + GATEWAY_TIMEOUT = 504, "Gateway Timeout" + HTTP_VERSION_NOT_SUPPORTED = 505, "HTTP Version Not Supported" + VARIANT_ALSO_NEGOTIATES = 506, "Variant Also Negotiates" + INSUFFICIENT_STORAGE = 507, "Insufficient Storage" + LOOP_DETECTED = 508, "Loop Detected" + NOT_EXTENDED = 510, "Not Extended" + NETWORK_AUTHENTICATION_REQUIRED = 511, "Network Authentication Required" + + # Deprecated aliases for names replaced by RFC 9110. + REQUEST_ENTITY_TOO_LARGE = CONTENT_TOO_LARGE + REQUEST_URI_TOO_LONG = URI_TOO_LONG + REQUESTED_RANGE_NOT_SATISFIABLE = RANGE_NOT_SATISFIABLE + UNPROCESSABLE_ENTITY = UNPROCESSABLE_CONTENT + + +# Include lower-case styles for `requests` compatibility. +for name, code in codes.__members__.items(): + setattr(codes, name.lower(), int(code)) diff --git a/venv/lib/python3.12/site-packages/httpx2/_transports/__init__.py b/venv/lib/python3.12/site-packages/httpx2/_transports/__init__.py new file mode 100644 index 0000000..eb822b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_transports/__init__.py @@ -0,0 +1,27 @@ +import sys + +from .asgi import ASGITransport +from .base import AsyncBaseTransport, BaseTransport +from .mock import MockTransport +from .wsgi import WSGITransport + +if sys.platform == "emscripten": # pragma: nocover + if sys.version_info < (3, 12): + raise RuntimeError("httpx2 requires Python 3.12+ on Emscripten.") + + from httpx2_jsfetch import ( + AsyncJavascriptFetchTransport as AsyncHTTPTransport, + JavascriptFetchTransport as HTTPTransport, + ) +else: + from .default import AsyncHTTPTransport, HTTPTransport + +__all__ = [ + "ASGITransport", + "AsyncBaseTransport", + "BaseTransport", + "AsyncHTTPTransport", + "HTTPTransport", + "MockTransport", + "WSGITransport", +] diff --git a/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..9812ecf Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/asgi.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/asgi.cpython-312.pyc new file mode 100644 index 0000000..8a9e473 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/asgi.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..b1ab0c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/default.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/default.cpython-312.pyc new file mode 100644 index 0000000..f77c6ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/default.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/mock.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/mock.cpython-312.pyc new file mode 100644 index 0000000..73b37b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/mock.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/wsgi.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/wsgi.cpython-312.pyc new file mode 100644 index 0000000..12622a9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/_transports/__pycache__/wsgi.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/_transports/asgi.py b/venv/lib/python3.12/site-packages/httpx2/_transports/asgi.py new file mode 100644 index 0000000..33f25db --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_transports/asgi.py @@ -0,0 +1,178 @@ +from __future__ import annotations + +import typing + +from .._models import Request, Response +from .._types import AsyncByteStream +from .base import AsyncBaseTransport + +if typing.TYPE_CHECKING: + import asyncio + + import trio + + Event = asyncio.Event | trio.Event + + +_Message = typing.MutableMapping[str, typing.Any] +_Receive = typing.Callable[[], typing.Awaitable[_Message]] +_Send = typing.Callable[[typing.MutableMapping[str, typing.Any]], typing.Awaitable[None]] +_ASGIApp = typing.Callable[[typing.MutableMapping[str, typing.Any], _Receive, _Send], typing.Awaitable[None]] + +__all__ = ["ASGITransport"] + + +def is_running_trio() -> bool: + try: + # sniffio is a dependency of trio. + + # See https://github.com/python-trio/trio/issues/2802 + import sniffio + + if sniffio.current_async_library() == "trio": + return True + except ImportError: # pragma: no cover + pass + + return False + + +def create_event() -> Event: + if is_running_trio(): + import trio + + return trio.Event() + + import asyncio + + return asyncio.Event() + + +class ASGIResponseStream(AsyncByteStream): + def __init__(self, body: list[bytes]) -> None: + self._body = body + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + yield b"".join(self._body) + + +class ASGITransport(AsyncBaseTransport): + """ + A custom AsyncTransport that handles sending requests directly to an ASGI app. + + ```python + transport = httpx2.ASGITransport( + app=app, + root_path="/submount", + client=("1.2.3.4", 123) + ) + client = httpx2.AsyncClient(transport=transport) + ``` + + Arguments: + app: The ASGI application. + raise_app_exceptions: Boolean indicating if exceptions in the application + should be raised. Default to `True`. Can be set to `False` for use cases + such as testing the content of a client 500 response. + root_path: The root path on which the ASGI application should be mounted. + client: A two-tuple indicating the client IP and port of incoming requests. + """ + + def __init__( + self, + app: _ASGIApp, + raise_app_exceptions: bool = True, + root_path: str = "", + client: tuple[str, int] = ("127.0.0.1", 123), + ) -> None: + self.app = app + self.raise_app_exceptions = raise_app_exceptions + self.root_path = root_path + self.client = client + + async def handle_async_request(self, request: Request) -> Response: + assert isinstance(request.stream, AsyncByteStream) + + # ASGI scope. + scope = { + "type": "http", + "asgi": {"version": "3.0"}, + "http_version": "1.1", + "method": request.method, + "headers": [(k.lower(), v) for (k, v) in request.headers.raw], + "scheme": request.url.scheme, + "path": request.url.path, + "raw_path": request.url.raw_path.split(b"?")[0], + "query_string": request.url.query, + "server": (request.url.host, request.url.port), + "client": self.client, + "root_path": self.root_path, + } + + # Request. + request_body_chunks = request.stream.__aiter__() + request_complete = False + + # Response. + status_code = None + response_headers = None + body_parts: list[bytes] = [] + response_started = False + response_complete = create_event() + + # ASGI callables. + + async def receive() -> dict[str, typing.Any]: + nonlocal request_complete + + if request_complete: + await response_complete.wait() + return {"type": "http.disconnect"} + + try: + body = await request_body_chunks.__anext__() + except StopAsyncIteration: + request_complete = True + return {"type": "http.request", "body": b"", "more_body": False} + return {"type": "http.request", "body": body, "more_body": True} + + async def send(message: typing.MutableMapping[str, typing.Any]) -> None: + nonlocal status_code, response_headers, response_started + + if message["type"] == "http.response.start": + assert not response_started + + status_code = message["status"] + response_headers = message.get("headers", []) + response_started = True + + elif message["type"] == "http.response.body": + assert not response_complete.is_set() + body = message.get("body", b"") + more_body = message.get("more_body", False) + + if body and request.method != "HEAD": + body_parts.append(body) + + if not more_body: + response_complete.set() + + try: + await self.app(scope, receive, send) + except Exception: + if self.raise_app_exceptions: + raise + + response_complete.set() + if status_code is None: + status_code = 500 + if response_headers is None: + response_headers = {} + + assert response_complete.is_set() + assert status_code is not None + assert response_headers is not None + + stream = ASGIResponseStream(body_parts) + + return Response(status_code, headers=response_headers, stream=stream) diff --git a/venv/lib/python3.12/site-packages/httpx2/_transports/base.py b/venv/lib/python3.12/site-packages/httpx2/_transports/base.py new file mode 100644 index 0000000..30e9090 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_transports/base.py @@ -0,0 +1,83 @@ +from __future__ import annotations + +import typing +from types import TracebackType + +from .._models import Request, Response + +# TODO(Marcelo): When Python 3.10 reaches EOF, we can use `typing.Self` instead of defining those two. +T = typing.TypeVar("T", bound="BaseTransport") +A = typing.TypeVar("A", bound="AsyncBaseTransport") + +__all__ = ["AsyncBaseTransport", "BaseTransport"] + + +class BaseTransport: + def __enter__(self: T) -> T: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + self.close() + + def handle_request(self, request: Request) -> Response: + """ + Send a single HTTP request and return a response. + + Developers shouldn't typically ever need to call into this API directly, + since the Client class provides all the higher level user-facing API + niceties. + + In order to properly release any network resources, the response + stream should *either* be consumed immediately, with a call to + `response.stream.read()`, or else the `handle_request` call should + be followed with a try/finally block to ensuring the stream is + always closed. + + Example usage: + + with httpx2.HTTPTransport() as transport: + req = httpx2.Request( + method=b"GET", + url=(b"https", b"www.example.com", 443, b"/"), + headers=[(b"Host", b"www.example.com")], + ) + resp = transport.handle_request(req) + body = resp.stream.read() + print(resp.status_code, resp.headers, body) + + + Takes a `Request` instance as the only argument. + + Returns a `Response` instance. + """ + raise NotImplementedError("The 'handle_request' method must be implemented.") # pragma: no cover + + def close(self) -> None: + pass + + +class AsyncBaseTransport: + async def __aenter__(self: A) -> A: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + await self.aclose() + + async def handle_async_request( + self, + request: Request, + ) -> Response: + raise NotImplementedError("The 'handle_async_request' method must be implemented.") # pragma: no cover + + async def aclose(self) -> None: + pass diff --git a/venv/lib/python3.12/site-packages/httpx2/_transports/default.py b/venv/lib/python3.12/site-packages/httpx2/_transports/default.py new file mode 100644 index 0000000..17d48c4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_transports/default.py @@ -0,0 +1,400 @@ +""" +Custom transports, with nicely configured defaults. + +The following additional keyword arguments are currently supported by httpcore... + +* uds: str +* local_address: str +* retries: int + +Example usages... + +# Disable HTTP/2 on a single specific domain. +mounts = { + "all://": httpx2.HTTPTransport(http2=True), + "all://*example.org": httpx2.HTTPTransport() +} + +# Using advanced httpcore configuration, with connection retries. +transport = httpx2.HTTPTransport(retries=1) +client = httpx2.Client(transport=transport) + +# Using advanced httpcore configuration, with unix domain sockets. +transport = httpx2.HTTPTransport(uds="socket.uds") +client = httpx2.Client(transport=transport) +""" + +from __future__ import annotations + +import contextlib +import typing +from collections.abc import Generator +from types import TracebackType + +if typing.TYPE_CHECKING: # pragma: no cover + import ssl + + import httpx2 + +from .._config import DEFAULT_LIMITS, Limits, Proxy, create_ssl_context +from .._exceptions import ( + ConnectError, + ConnectTimeout, + LocalProtocolError, + NetworkError, + PoolTimeout, + ProtocolError, + ProxyError, + ReadError, + ReadTimeout, + RemoteProtocolError, + TimeoutException, + UnsupportedProtocol, + WriteError, + WriteTimeout, +) +from .._models import Request, Response +from .._types import AsyncByteStream, CertTypes, ProxyTypes, SyncByteStream +from .._urls import URL +from .base import AsyncBaseTransport, BaseTransport + +T = typing.TypeVar("T", bound="HTTPTransport") +A = typing.TypeVar("A", bound="AsyncHTTPTransport") + +SOCKET_OPTION = tuple[int, int, int] | tuple[int, int, bytes | bytearray] | tuple[int, int, None, int] + +__all__ = ["AsyncHTTPTransport", "HTTPTransport"] + +HTTPCORE_EXC_MAP: dict[type[Exception], type[httpx2.HTTPError]] = {} + + +def _load_httpcore_exceptions() -> dict[type[Exception], type[httpx2.HTTPError]]: + import httpcore2 + + return { + httpcore2.TimeoutException: TimeoutException, + httpcore2.ConnectTimeout: ConnectTimeout, + httpcore2.ReadTimeout: ReadTimeout, + httpcore2.WriteTimeout: WriteTimeout, + httpcore2.PoolTimeout: PoolTimeout, + httpcore2.NetworkError: NetworkError, + httpcore2.ConnectError: ConnectError, + httpcore2.ReadError: ReadError, + httpcore2.WriteError: WriteError, + httpcore2.ProxyError: ProxyError, + httpcore2.UnsupportedProtocol: UnsupportedProtocol, + httpcore2.ProtocolError: ProtocolError, + httpcore2.LocalProtocolError: LocalProtocolError, + httpcore2.RemoteProtocolError: RemoteProtocolError, + } + + +@contextlib.contextmanager +def map_httpcore_exceptions() -> Generator[None]: + global HTTPCORE_EXC_MAP + if len(HTTPCORE_EXC_MAP) == 0: + HTTPCORE_EXC_MAP = _load_httpcore_exceptions() + try: + yield + except Exception as exc: + mapped_exc = None + + for from_exc, to_exc in HTTPCORE_EXC_MAP.items(): + if not isinstance(exc, from_exc): + continue + # We want to map to the most specific exception we can find. + # Eg if `exc` is an `httpcore2.ReadTimeout`, we want to map to + # `httpx2.ReadTimeout`, not just `httpx2.TimeoutException`. + if mapped_exc is None or issubclass(to_exc, mapped_exc): + mapped_exc = to_exc + + if mapped_exc is None: # pragma: no cover + raise + + message = str(exc) + raise mapped_exc(message) from exc + + +class ResponseStream(SyncByteStream): + def __init__(self, httpcore_stream: typing.Iterable[bytes]) -> None: + self._httpcore_stream = httpcore_stream + + def __iter__(self) -> typing.Iterator[bytes]: + with map_httpcore_exceptions(): + yield from self._httpcore_stream + + def close(self) -> None: + if hasattr(self._httpcore_stream, "close"): + self._httpcore_stream.close() + + +class HTTPTransport(BaseTransport): + def __init__( + self, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + proxy: ProxyTypes | None = None, + uds: str | None = None, + local_address: str | None = None, + retries: int = 0, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + import httpcore2 + + proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy + ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env) + + if proxy is None: + self._pool = httpcore2.ConnectionPool( + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + uds=uds, + local_address=local_address, + retries=retries, + socket_options=socket_options, + ) + elif proxy.url.scheme in ("http", "https"): + self._pool = httpcore2.HTTPProxy( + proxy_url=httpcore2.URL( + scheme=proxy.url.raw_scheme, + host=proxy.url.raw_host, + port=proxy.url.port, + target=proxy.url.raw_path, + ), + proxy_auth=proxy.raw_auth, + proxy_headers=proxy.headers.raw, + ssl_context=ssl_context, + proxy_ssl_context=proxy.ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + socket_options=socket_options, + ) + elif proxy.url.scheme in ("socks5", "socks5h"): + try: + import socksio # noqa + except ImportError: # pragma: no cover + raise ImportError( + "Using SOCKS proxy, but the 'socksio' package is not installed. " + "Make sure to install httpx using `pip install httpx[socks]`." + ) from None + + self._pool = httpcore2.SOCKSProxy( + proxy_url=httpcore2.URL( + scheme=proxy.url.raw_scheme, + host=proxy.url.raw_host, + port=proxy.url.port, + target=proxy.url.raw_path, + ), + proxy_auth=proxy.raw_auth, + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + ) + else: # pragma: no cover + raise ValueError( + f"Proxy protocol must be either 'http', 'https', 'socks5', or 'socks5h', but got {proxy.url.scheme!r}." + ) + + def __enter__(self: T) -> T: # Use generics for subclass support. + self._pool.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + with map_httpcore_exceptions(): + self._pool.__exit__(exc_type, exc_value, traceback) + + def handle_request( + self, + request: Request, + ) -> Response: + assert isinstance(request.stream, SyncByteStream) + import httpcore2 + + req = httpcore2.Request( + method=request.method, + url=httpcore2.URL( + scheme=request.url.raw_scheme, + host=request.url.raw_host, + port=request.url.port, + target=request.url.raw_path, + ), + headers=request.headers.raw, + content=request.stream, + extensions=request.extensions, + ) + with map_httpcore_exceptions(): + resp = self._pool.handle_request(req) + + assert isinstance(resp.stream, typing.Iterable) + + return Response( + status_code=resp.status, + headers=resp.headers, + stream=ResponseStream(resp.stream), + extensions=resp.extensions, + ) + + def close(self) -> None: + self._pool.close() + + +class AsyncResponseStream(AsyncByteStream): + def __init__(self, httpcore_stream: typing.AsyncIterable[bytes]) -> None: + self._httpcore_stream = httpcore_stream + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + with map_httpcore_exceptions(): + async for part in self._httpcore_stream: + yield part + + async def aclose(self) -> None: + if hasattr(self._httpcore_stream, "aclose"): + await self._httpcore_stream.aclose() + + +class AsyncHTTPTransport(AsyncBaseTransport): + def __init__( + self, + verify: ssl.SSLContext | str | bool = True, + cert: CertTypes | None = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + proxy: ProxyTypes | None = None, + uds: str | None = None, + local_address: str | None = None, + retries: int = 0, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + import httpcore2 + + proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy + ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env) + + if proxy is None: + self._pool = httpcore2.AsyncConnectionPool( + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + uds=uds, + local_address=local_address, + retries=retries, + socket_options=socket_options, + ) + elif proxy.url.scheme in ("http", "https"): + self._pool = httpcore2.AsyncHTTPProxy( + proxy_url=httpcore2.URL( + scheme=proxy.url.raw_scheme, + host=proxy.url.raw_host, + port=proxy.url.port, + target=proxy.url.raw_path, + ), + proxy_auth=proxy.raw_auth, + proxy_headers=proxy.headers.raw, + proxy_ssl_context=proxy.ssl_context, + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + socket_options=socket_options, + ) + elif proxy.url.scheme in ("socks5", "socks5h"): + try: + import socksio # noqa + except ImportError: # pragma: no cover + raise ImportError( + "Using SOCKS proxy, but the 'socksio' package is not installed. " + "Make sure to install httpx using `pip install httpx[socks]`." + ) from None + + self._pool = httpcore2.AsyncSOCKSProxy( + proxy_url=httpcore2.URL( + scheme=proxy.url.raw_scheme, + host=proxy.url.raw_host, + port=proxy.url.port, + target=proxy.url.raw_path, + ), + proxy_auth=proxy.raw_auth, + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + ) + else: # pragma: no cover + raise ValueError( + f"Proxy protocol must be either 'http', 'https', 'socks5', or 'socks5h', but got {proxy.url.scheme!r}." + ) + + async def __aenter__(self: A) -> A: # Use generics for subclass support. + await self._pool.__aenter__() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + with map_httpcore_exceptions(): + await self._pool.__aexit__(exc_type, exc_value, traceback) + + async def handle_async_request( + self, + request: Request, + ) -> Response: + assert isinstance(request.stream, AsyncByteStream) + import httpcore2 + + req = httpcore2.Request( + method=request.method, + url=httpcore2.URL( + scheme=request.url.raw_scheme, + host=request.url.raw_host, + port=request.url.port, + target=request.url.raw_path, + ), + headers=request.headers.raw, + content=request.stream, + extensions=request.extensions, + ) + with map_httpcore_exceptions(): + resp = await self._pool.handle_async_request(req) + + assert isinstance(resp.stream, typing.AsyncIterable) + + return Response( + status_code=resp.status, + headers=resp.headers, + stream=AsyncResponseStream(resp.stream), + extensions=resp.extensions, + ) + + async def aclose(self) -> None: + await self._pool.aclose() diff --git a/venv/lib/python3.12/site-packages/httpx2/_transports/mock.py b/venv/lib/python3.12/site-packages/httpx2/_transports/mock.py new file mode 100644 index 0000000..7db82d1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_transports/mock.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +import typing + +from .._models import Request, Response +from .base import AsyncBaseTransport, BaseTransport + +SyncHandler = typing.Callable[[Request], Response] +AsyncHandler = typing.Callable[[Request], typing.Coroutine[None, None, Response]] + + +__all__ = ["MockTransport"] + + +class MockTransport(AsyncBaseTransport, BaseTransport): + def __init__(self, handler: SyncHandler | AsyncHandler) -> None: + self.handler = handler + + def handle_request( + self, + request: Request, + ) -> Response: + request.read() + response = self.handler(request) + if not isinstance(response, Response): # pragma: no cover + raise TypeError("Cannot use an async handler in a sync Client") + return response + + async def handle_async_request(self, request: Request) -> Response: + await request.aread() + response = self.handler(request) + + # Allow handler to *optionally* be an `async` function. + # If it is, then the `response` variable need to be awaited to actually + # return the result. + + if not isinstance(response, Response): + response = await response + + return response diff --git a/venv/lib/python3.12/site-packages/httpx2/_transports/wsgi.py b/venv/lib/python3.12/site-packages/httpx2/_transports/wsgi.py new file mode 100644 index 0000000..f15ec38 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_transports/wsgi.py @@ -0,0 +1,145 @@ +from __future__ import annotations + +import io +import itertools +import sys +import typing + +from .._models import Request, Response +from .._types import SyncByteStream +from .base import BaseTransport + +if typing.TYPE_CHECKING: + from _typeshed import OptExcInfo # pragma: no cover + from _typeshed.wsgi import WSGIApplication # pragma: no cover + +_T = typing.TypeVar("_T") + + +__all__ = ["WSGITransport"] + + +def _skip_leading_empty_chunks(body: typing.Iterable[_T]) -> typing.Iterable[_T]: + body = iter(body) + for chunk in body: + if chunk: + return itertools.chain([chunk], body) + return [] + + +class WSGIByteStream(SyncByteStream): + def __init__(self, result: typing.Iterable[bytes]) -> None: + self._close = getattr(result, "close", None) + self._result = _skip_leading_empty_chunks(result) + + def __iter__(self) -> typing.Iterator[bytes]: + yield from self._result + + def close(self) -> None: + if self._close is not None: + self._close() + + +class WSGITransport(BaseTransport): + """ + A custom transport that handles sending requests directly to an WSGI app. + The simplest way to use this functionality is to use the `app` argument. + + ``` + client = httpx2.Client(app=app) + ``` + + Alternatively, you can setup the transport instance explicitly. + This allows you to include any additional configuration arguments specific + to the WSGITransport class: + + ``` + transport = httpx2.WSGITransport( + app=app, + script_name="/submount", + remote_addr="1.2.3.4" + ) + client = httpx2.Client(transport=transport) + ``` + + Arguments: + app: The WSGI application. + raise_app_exceptions: Boolean indicating if exceptions in the application + should be raised. Default to `True`. Can be set to `False` for use cases + such as testing the content of a client 500 response. + script_name: The root path on which the WSGI application should be mounted. + remote_addr: A string indicating the client IP of incoming requests. + """ + + def __init__( + self, + app: WSGIApplication, + raise_app_exceptions: bool = True, + script_name: str = "", + remote_addr: str = "127.0.0.1", + wsgi_errors: typing.TextIO | None = None, + ) -> None: + self.app = app + self.raise_app_exceptions = raise_app_exceptions + self.script_name = script_name + self.remote_addr = remote_addr + self.wsgi_errors = wsgi_errors + + def handle_request(self, request: Request) -> Response: + request.read() + wsgi_input = io.BytesIO(request.content) + + port = request.url.port or {"http": 80, "https": 443}[request.url.scheme] + environ = { + "wsgi.version": (1, 0), + "wsgi.url_scheme": request.url.scheme, + "wsgi.input": wsgi_input, + "wsgi.errors": self.wsgi_errors or sys.stderr, + "wsgi.multithread": True, + "wsgi.multiprocess": False, + "wsgi.run_once": False, + "REQUEST_METHOD": request.method, + "SCRIPT_NAME": self.script_name, + "PATH_INFO": request.url.path, + "QUERY_STRING": request.url.query.decode("ascii"), + "SERVER_NAME": request.url.host, + "SERVER_PORT": str(port), + "SERVER_PROTOCOL": "HTTP/1.1", + "REMOTE_ADDR": self.remote_addr, + } + if request.content and "Content-Length" not in request.headers: + environ["CONTENT_LENGTH"] = str(len(request.content)) + for header_key, header_value in request.headers.raw: + key = header_key.decode("ascii").upper().replace("-", "_") + if key not in ("CONTENT_TYPE", "CONTENT_LENGTH"): + key = "HTTP_" + key + environ[key] = header_value.decode("ascii") + + seen_status = None + seen_response_headers = None + seen_exc_info = None + + def start_response( + status: str, + response_headers: list[tuple[str, str]], + exc_info: OptExcInfo | None = None, + ) -> typing.Callable[[bytes], typing.Any]: + nonlocal seen_status, seen_response_headers, seen_exc_info + seen_status = status + seen_response_headers = response_headers + seen_exc_info = exc_info + return lambda _: None + + result = self.app(environ, start_response) + + stream = WSGIByteStream(result) + + assert seen_status is not None + assert seen_response_headers is not None + if seen_exc_info and seen_exc_info[0] and self.raise_app_exceptions: + raise seen_exc_info[1] + + status_code = int(seen_status.split()[0]) + headers = [(key.encode("ascii"), value.encode("ascii")) for key, value in seen_response_headers] + + return Response(status_code, headers=headers, stream=stream) diff --git a/venv/lib/python3.12/site-packages/httpx2/_types.py b/venv/lib/python3.12/site-packages/httpx2/_types.py new file mode 100644 index 0000000..3635da6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_types.py @@ -0,0 +1,86 @@ +""" +Type definitions for type checking purposes. +""" + +from collections.abc import AsyncIterable, AsyncIterator, Callable, Iterable, Iterator, Mapping, Sequence +from http.cookiejar import CookieJar +from typing import IO, TYPE_CHECKING, Any, Union + +if TYPE_CHECKING: + from ._auth import Auth # noqa: F401 + from ._config import Proxy, Timeout # noqa: F401 + from ._models import Cookies, Headers, Request # noqa: F401 + from ._urls import URL, QueryParams # noqa: F401 + + +PrimitiveData = str | int | float | bool | None + +URLTypes = Union["URL", str] + +QueryParamTypes = Union[ + "QueryParams", + Mapping[str, PrimitiveData | Sequence[PrimitiveData]], + list[tuple[str, PrimitiveData]], + tuple[tuple[str, PrimitiveData], ...], + str, + bytes, +] + +HeaderTypes = Union[ + "Headers", + Mapping[str, str], + Mapping[bytes, bytes], + Sequence[tuple[str, str]], + Sequence[tuple[bytes, bytes]], +] + +CookieTypes = Union["Cookies", CookieJar, dict[str, str], list[tuple[str, str]]] + +TimeoutTypes = Union[float | None, tuple[float | None, float | None, float | None, float | None], "Timeout"] +ProxyTypes = Union["URL", str, "Proxy"] +CertTypes = str | tuple[str, str] | tuple[str, str, str] + +AuthTypes = Union[tuple[str | bytes, str | bytes], Callable[["Request"], "Request"], "Auth"] + +RequestContent = str | bytes | Iterable[bytes] | AsyncIterable[bytes] +ResponseContent = str | bytes | Iterable[bytes] | AsyncIterable[bytes] +ResponseExtensions = Mapping[str, Any] + +RequestData = Mapping[str, Any] + +FileContent = IO[bytes] | bytes | str +FileTypes = ( + # # file (or bytes) + FileContent + # # (filename, file (or bytes)) + | tuple[str | None, FileContent] + # # (filename, file (or bytes), content_type) + | tuple[str | None, FileContent, str | None] + | tuple[str | None, FileContent, str | None, Mapping[str, str]] +) +RequestFiles = Mapping[str, FileTypes] | Sequence[tuple[str, FileTypes]] + +RequestExtensions = Mapping[str, Any] + +__all__ = ["AsyncByteStream", "SyncByteStream"] + + +class SyncByteStream: + def __iter__(self) -> Iterator[bytes]: + raise NotImplementedError("The '__iter__' method must be implemented.") # pragma: no cover + yield b"" # pragma: no cover + + def close(self) -> None: + """ + Subclasses can override this method to release any network resources + after a request/response cycle is complete. + """ + + +class AsyncByteStream: + async def __aiter__(self) -> AsyncIterator[bytes]: + raise NotImplementedError("The '__aiter__' method must be implemented.") # pragma: no cover + yield b"" # pragma: no cover + + async def aclose(self) -> None: + pass diff --git a/venv/lib/python3.12/site-packages/httpx2/_urlparse.py b/venv/lib/python3.12/site-packages/httpx2/_urlparse.py new file mode 100644 index 0000000..4a9d9bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_urlparse.py @@ -0,0 +1,502 @@ +""" +An implementation of `urlparse` that provides URL validation and normalization +as described by RFC3986. + +We rely on this implementation rather than the one in Python's stdlib, because: + +* It provides more complete URL validation. +* It properly differentiates between an empty querystring and an absent querystring, + to distinguish URLs with a trailing '?'. +* It handles scheme, hostname, port, and path normalization. +* It supports IDNA hostnames, normalizing them to their encoded form. +* The API supports passing individual components, as well as the complete URL string. + +Previously we relied on the excellent `rfc3986` package to handle URL parsing and +validation, but this module provides a simpler alternative, with less indirection +required. +""" + +from __future__ import annotations + +import ipaddress +import re +import typing + +import idna + +from ._exceptions import InvalidURL + +MAX_URL_LENGTH = 65536 + +# https://datatracker.ietf.org/doc/html/rfc3986.html#section-2.3 +UNRESERVED_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~" +SUB_DELIMS = "!$&'()*+,;=" + +PERCENT_ENCODED_REGEX = re.compile("%[A-Fa-f0-9]{2}") + +# https://url.spec.whatwg.org/#percent-encoded-bytes + +# The fragment percent-encode set is the C0 control percent-encode set +# and U+0020 SPACE, U+0022 ("), U+003C (<), U+003E (>), and U+0060 (`). +FRAG_SAFE = "".join([chr(i) for i in range(0x20, 0x7F) if i not in (0x20, 0x22, 0x3C, 0x3E, 0x60)]) + +# The query percent-encode set is the C0 control percent-encode set +# and U+0020 SPACE, U+0022 ("), U+0023 (#), U+003C (<), and U+003E (>). +QUERY_SAFE = "".join([chr(i) for i in range(0x20, 0x7F) if i not in (0x20, 0x22, 0x23, 0x3C, 0x3E)]) + +# The path percent-encode set is the query percent-encode set +# and U+003F (?), U+0060 (`), U+007B ({), and U+007D (}). +PATH_SAFE = "".join( + [chr(i) for i in range(0x20, 0x7F) if i not in (0x20, 0x22, 0x23, 0x3C, 0x3E) + (0x3F, 0x60, 0x7B, 0x7D)] +) + +# The userinfo percent-encode set is the path percent-encode set +# and U+002F (/), U+003A (:), U+003B (;), U+003D (=), U+0040 (@), +# U+005B ([) to U+005E (^), inclusive, and U+007C (|). +USERNAME_SAFE = "".join( + [ + chr(i) + for i in range(0x20, 0x7F) + if i + not in (0x20, 0x22, 0x23, 0x3C, 0x3E) + + (0x3F, 0x60, 0x7B, 0x7D) + + (0x2F, 0x3A, 0x3B, 0x3D, 0x40, 0x5B, 0x5C, 0x5D, 0x5E, 0x7C) + ] +) +PASSWORD_SAFE = "".join( + [ + chr(i) + for i in range(0x20, 0x7F) + if i + not in (0x20, 0x22, 0x23, 0x3C, 0x3E) + + (0x3F, 0x60, 0x7B, 0x7D) + + (0x2F, 0x3A, 0x3B, 0x3D, 0x40, 0x5B, 0x5C, 0x5D, 0x5E, 0x7C) + ] +) +# Note... The terminology 'userinfo' percent-encode set in the WHATWG document +# is used for the username and password quoting. For the joint userinfo component +# we remove U+003A (:) from the safe set. +USERINFO_SAFE = "".join( + [ + chr(i) + for i in range(0x20, 0x7F) + if i + not in (0x20, 0x22, 0x23, 0x3C, 0x3E) + + (0x3F, 0x60, 0x7B, 0x7D) + + (0x2F, 0x3B, 0x3D, 0x40, 0x5B, 0x5C, 0x5D, 0x5E, 0x7C) + ] +) + + +# {scheme}: (optional) +# //{authority} (optional) +# {path} +# ?{query} (optional) +# #{fragment} (optional) +URL_REGEX = re.compile( + ( + r"(?:(?P{scheme}):)?" + r"(?://(?P{authority}))?" + r"(?P{path})" + r"(?:\?(?P{query}))?" + r"(?:#(?P{fragment}))?" + ).format( + scheme="([a-zA-Z][a-zA-Z0-9+.-]*)?", + authority="[^/?#]*", + path="[^?#]*", + query="[^#]*", + fragment=".*", + ) +) + +# {userinfo}@ (optional) +# {host} +# :{port} (optional) +AUTHORITY_REGEX = re.compile( + (r"(?:(?P{userinfo})@)?" r"(?P{host})" r":?(?P{port})?").format( + userinfo=".*", # Any character sequence. + host="(\\[.*\\]|[^:@]*)", # Either any character sequence excluding ':' or '@', + # or an IPv6 address enclosed within square brackets. + port=".*", # Any character sequence. + ) +) + + +# If we call urlparse with an individual component, then we need to regex +# validate that component individually. +# Note that we're duplicating the same strings as above. Shock! Horror!! +COMPONENT_REGEX = { + "scheme": re.compile("([a-zA-Z][a-zA-Z0-9+.-]*)?"), + "authority": re.compile("[^/?#]*"), + "path": re.compile("[^?#]*"), + "query": re.compile("[^#]*"), + "fragment": re.compile(".*"), + "userinfo": re.compile("[^@]*"), + "host": re.compile("(\\[.*\\]|[^:]*)"), + "port": re.compile(".*"), +} + + +# We use these simple regexs as a first pass before handing off to +# the stdlib 'ipaddress' module for IP address validation. +IPv4_STYLE_HOSTNAME = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$") +IPv6_STYLE_HOSTNAME = re.compile(r"^\[.*\]$") + + +class ParseResult(typing.NamedTuple): + scheme: str + userinfo: str + host: str + port: int | None + path: str + query: str | None + fragment: str | None + + @property + def authority(self) -> str: + return "".join( + [ + f"{self.userinfo}@" if self.userinfo else "", + f"[{self.host}]" if ":" in self.host else self.host, + f":{self.port}" if self.port is not None else "", + ] + ) + + @property + def netloc(self) -> str: + return "".join( + [ + f"[{self.host}]" if ":" in self.host else self.host, + f":{self.port}" if self.port is not None else "", + ] + ) + + def copy_with(self, **kwargs: str | None) -> ParseResult: + if not kwargs: + return self + + defaults = { + "scheme": self.scheme, + "authority": self.authority, + "path": self.path, + "query": self.query, + "fragment": self.fragment, + } + defaults.update(kwargs) + return urlparse("", **defaults) + + def __str__(self) -> str: + authority = self.authority + return "".join( + [ + f"{self.scheme}:" if self.scheme else "", + f"//{authority}" if authority else "", + self.path, + f"?{self.query}" if self.query is not None else "", + f"#{self.fragment}" if self.fragment is not None else "", + ] + ) + + +def urlparse(url: str = "", **kwargs: str | None) -> ParseResult: + # Initial basic checks on allowable URLs. + # --------------------------------------- + + # Hard limit the maximum allowable URL length. + if len(url) > MAX_URL_LENGTH: + raise InvalidURL("URL too long") + + # If a URL includes any ASCII control characters including \t, \r, \n, + # then treat it as invalid. + if any(char.isascii() and not char.isprintable() for char in url): + char = next(char for char in url if char.isascii() and not char.isprintable()) + idx = url.find(char) + error = f"Invalid non-printable ASCII character in URL, {char!r} at position {idx}." + raise InvalidURL(error) + + # Some keyword arguments require special handling. + # ------------------------------------------------ + + # Coerce "port" to a string, if it is provided as an integer. + if "port" in kwargs: + port = kwargs["port"] + kwargs["port"] = str(port) if isinstance(port, int) else port + + # Replace "netloc" with "host and "port". + if "netloc" in kwargs: + netloc = kwargs.pop("netloc") or "" + kwargs["host"], _, kwargs["port"] = netloc.partition(":") + + # Replace "username" and/or "password" with "userinfo". + if "username" in kwargs or "password" in kwargs: + username = quote(kwargs.pop("username", "") or "", safe=USERNAME_SAFE) + password = quote(kwargs.pop("password", "") or "", safe=PASSWORD_SAFE) + kwargs["userinfo"] = f"{username}:{password}" if password else username + + # Replace "raw_path" with "path" and "query". + if "raw_path" in kwargs: + raw_path = kwargs.pop("raw_path") or "" + kwargs["path"], separator, kwargs["query"] = raw_path.partition("?") + if not separator: + kwargs["query"] = None + + # Ensure that IPv6 "host" addresses are always escaped with "[...]". + if "host" in kwargs: + host = kwargs.get("host") or "" + if ":" in host and not (host.startswith("[") and host.endswith("]")): + kwargs["host"] = f"[{host}]" + + # If any keyword arguments are provided, ensure they are valid. + # ------------------------------------------------------------- + + for key, value in kwargs.items(): + if value is not None: + if len(value) > MAX_URL_LENGTH: + raise InvalidURL(f"URL component '{key}' too long") + + # If a component includes any ASCII control characters including \t, \r, \n, + # then treat it as invalid. + if any(char.isascii() and not char.isprintable() for char in value): + char = next(char for char in value if char.isascii() and not char.isprintable()) + idx = value.find(char) + error = f"Invalid non-printable ASCII character in URL {key} component, {char!r} at position {idx}." + raise InvalidURL(error) + + # Ensure that keyword arguments match as a valid regex. + if not COMPONENT_REGEX[key].fullmatch(value): + raise InvalidURL(f"Invalid URL component '{key}'") + + # The URL_REGEX will always match, but may have empty components. + url_match = URL_REGEX.match(url) + assert url_match is not None + url_dict = url_match.groupdict() + + # * 'scheme', 'authority', and 'path' may be empty strings. + # * 'query' may be 'None', indicating no trailing "?" portion. + # Any string including the empty string, indicates a trailing "?". + # * 'fragment' may be 'None', indicating no trailing "#" portion. + # Any string including the empty string, indicates a trailing "#". + scheme = kwargs.get("scheme", url_dict["scheme"]) or "" + authority = kwargs.get("authority", url_dict["authority"]) or "" + path = kwargs.get("path", url_dict["path"]) or "" + query = kwargs.get("query", url_dict["query"]) + frag = kwargs.get("fragment", url_dict["fragment"]) + + # The AUTHORITY_REGEX will always match, but may have empty components. + authority_match = AUTHORITY_REGEX.match(authority) + assert authority_match is not None + authority_dict = authority_match.groupdict() + + # * 'userinfo' and 'host' may be empty strings. + # * 'port' may be 'None'. + userinfo = kwargs.get("userinfo", authority_dict["userinfo"]) or "" + host = kwargs.get("host", authority_dict["host"]) or "" + port = kwargs.get("port", authority_dict["port"]) + + # Normalize and validate each component. + # We end up with a parsed representation of the URL, + # with components that are plain ASCII bytestrings. + parsed_scheme: str = scheme.lower() + parsed_userinfo: str = quote(userinfo, safe=USERINFO_SAFE) + parsed_host: str = encode_host(host) + parsed_port: int | None = normalize_port(port, scheme) + + has_scheme = parsed_scheme != "" + has_authority = parsed_userinfo != "" or parsed_host != "" or parsed_port is not None + validate_path(path, has_scheme=has_scheme, has_authority=has_authority) + if has_scheme or has_authority: + path = normalize_path(path) + + parsed_path: str = quote(path, safe=PATH_SAFE) + parsed_query: str | None = None if query is None else quote(query, safe=QUERY_SAFE) + parsed_frag: str | None = None if frag is None else quote(frag, safe=FRAG_SAFE) + + # The parsed ASCII bytestrings are our canonical form. + # All properties of the URL are derived from these. + return ParseResult( + parsed_scheme, + parsed_userinfo, + parsed_host, + parsed_port, + parsed_path, + parsed_query, + parsed_frag, + ) + + +def encode_host(host: str) -> str: + if not host: + return "" + + elif IPv4_STYLE_HOSTNAME.match(host): + # Validate IPv4 hostnames like #.#.#.# + # + # From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2 + # + # IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + try: + ipaddress.IPv4Address(host) + except ipaddress.AddressValueError: + raise InvalidURL(f"Invalid IPv4 address: {host!r}") + return host + + elif IPv6_STYLE_HOSTNAME.match(host): + # Validate IPv6 hostnames like [...] + # + # From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2 + # + # "A host identified by an Internet Protocol literal address, version 6 + # [RFC3513] or later, is distinguished by enclosing the IP literal + # within square brackets ("[" and "]"). This is the only place where + # square bracket characters are allowed in the URI syntax." + try: + ipaddress.IPv6Address(host[1:-1]) + except ipaddress.AddressValueError: + raise InvalidURL(f"Invalid IPv6 address: {host!r}") + return host[1:-1] + + elif host.isascii(): + # Regular ASCII hostnames + # + # From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2 + # + # reg-name = *( unreserved / pct-encoded / sub-delims ) + WHATWG_SAFE = '"`{}%|\\' + return quote(host.lower(), safe=SUB_DELIMS + WHATWG_SAFE) + + # IDNA hostnames + try: + return idna.encode(host.lower()).decode("ascii") + except idna.IDNAError: + raise InvalidURL(f"Invalid IDNA hostname: {host!r}") + + +def normalize_port(port: str | int | None, scheme: str) -> int | None: + # From https://tools.ietf.org/html/rfc3986#section-3.2.3 + # + # "A scheme may define a default port. For example, the "http" scheme + # defines a default port of "80", corresponding to its reserved TCP + # port number. The type of port designated by the port number (e.g., + # TCP, UDP, SCTP) is defined by the URI scheme. URI producers and + # normalizers should omit the port component and its ":" delimiter if + # port is empty or if its value would be the same as that of the + # scheme's default." + if port is None or port == "": + return None + + try: + port_as_int = int(port) + except ValueError: + raise InvalidURL(f"Invalid port: {port!r}") + + # See https://url.spec.whatwg.org/#url-miscellaneous + default_port = {"ftp": 21, "http": 80, "https": 443, "ws": 80, "wss": 443}.get(scheme) + if port_as_int == default_port: + return None + return port_as_int + + +def validate_path(path: str, has_scheme: bool, has_authority: bool) -> None: + """ + Path validation rules that depend on if the URL contains + a scheme or authority component. + + See https://datatracker.ietf.org/doc/html/rfc3986.html#section-3.3 + """ + if has_authority: + # If a URI contains an authority component, then the path component + # must either be empty or begin with a slash ("/") character." + if path and not path.startswith("/"): + raise InvalidURL("For absolute URLs, path must be empty or begin with '/'") + + if not has_scheme and not has_authority: + # If a URI does not contain an authority component, then the path cannot begin + # with two slash characters ("//"). + if path.startswith("//"): + raise InvalidURL("Relative URLs cannot have a path starting with '//'") + + # In addition, a URI reference (Section 4.1) may be a relative-path reference, + # in which case the first path segment cannot contain a colon (":") character. + if path.startswith(":"): + raise InvalidURL("Relative URLs cannot have a path starting with ':'") + + +def normalize_path(path: str) -> str: + """ + Drop "." and ".." segments from a URL path. + + For example: + + normalize_path("/path/./to/somewhere/..") == "/path/to" + """ + # Fast return when no '.' characters in the path. + if "." not in path: + return path + + components = path.split("/") + + # Fast return when no '.' or '..' components in the path. + if "." not in components and ".." not in components: + return path + + # https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4 + output: list[str] = [] + for component in components: + if component == ".": + pass + elif component == "..": + if output and output != [""]: + output.pop() + else: + output.append(component) + return "/".join(output) + + +def PERCENT(string: str) -> str: + return "".join([f"%{byte:02X}" for byte in string.encode("utf-8")]) + + +def percent_encoded(string: str, safe: str) -> str: + """ + Use percent-encoding to quote a string. + """ + NON_ESCAPED_CHARS = UNRESERVED_CHARACTERS + safe + + # Fast path for strings that don't need escaping. + if not string.rstrip(NON_ESCAPED_CHARS): + return string + + return "".join([char if char in NON_ESCAPED_CHARS else PERCENT(char) for char in string]) + + +def quote(string: str, safe: str) -> str: + """ + Use percent-encoding to quote a string, omitting existing '%xx' escape sequences. + + See: https://www.rfc-editor.org/rfc/rfc3986#section-2.1 + + * `string`: The string to be percent-escaped. + * `safe`: A string containing characters that may be treated as safe, and do not + need to be escaped. Unreserved characters are always treated as safe. + See: https://www.rfc-editor.org/rfc/rfc3986#section-2.3 + """ + parts: list[str] = [] + current_position = 0 + for match in re.finditer(PERCENT_ENCODED_REGEX, string): + start_position, end_position = match.start(), match.end() + matched_text = match.group(0) + # Add any text up to the '%xx' escape sequence. + if start_position != current_position: + leading_text = string[current_position:start_position] + parts.append(percent_encoded(leading_text, safe=safe)) + + # Add the '%xx' escape sequence. + parts.append(matched_text) + current_position = end_position + + # Add any text after the final '%xx' escape sequence. + if current_position != len(string): + trailing_text = string[current_position:] + parts.append(percent_encoded(trailing_text, safe=safe)) + + return "".join(parts) diff --git a/venv/lib/python3.12/site-packages/httpx2/_urls.py b/venv/lib/python3.12/site-packages/httpx2/_urls.py new file mode 100644 index 0000000..2abc2bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_urls.py @@ -0,0 +1,689 @@ +from __future__ import annotations + +import ipaddress +import sys +import typing +from dataclasses import dataclass +from urllib.parse import parse_qs, unquote, urlencode + +import idna + +if sys.version_info >= (3, 13): + from warnings import deprecated # pragma: no cover +else: + from typing_extensions import deprecated # pragma: no cover + +from ._exceptions import HTTPXDeprecationWarning +from ._types import QueryParamTypes +from ._urlparse import urlparse +from ._utils import primitive_value_to_str + +__all__ = ["URL", "Origin", "QueryParams"] + + +class URL: + """ + url = httpx2.URL("HTTPS://jo%40email.com:a%20secret@müller.de:1234/pa%20th?search=ab#anchorlink") + + assert url.scheme == "https" + assert url.username == "jo@email.com" + assert url.password == "a secret" + assert url.userinfo == b"jo%40email.com:a%20secret" + assert url.host == "müller.de" + assert url.raw_host == b"xn--mller-kva.de" + assert url.port == 1234 + assert url.netloc == b"xn--mller-kva.de:1234" + assert url.path == "/pa th" + assert url.query == b"search=ab" + assert url.raw_path == b"/pa%20th?search=ab" + assert url.fragment == "anchorlink" + + The components of a URL are broken down like this: + + https://jo%40email.com:a%20secret@müller.de:1234/pa%20th?search=ab#anchorlink + [scheme] [ username ] [password] [ host ][port][ path ] [ query ] [fragment] + [ userinfo ] [ netloc ][ raw_path ] + + Note that: + + * `url.scheme` is normalized to always be lowercased. + + * `url.host` is normalized to always be lowercased. Internationalized domain + names are represented in unicode, without IDNA encoding applied. For instance: + + url = httpx2.URL("http://中国.icom.museum") + assert url.host == "中国.icom.museum" + url = httpx2.URL("http://xn--fiqs8s.icom.museum") + assert url.host == "中国.icom.museum" + + * `url.raw_host` is normalized to always be lowercased, and is IDNA encoded. + + url = httpx2.URL("http://中国.icom.museum") + assert url.raw_host == b"xn--fiqs8s.icom.museum" + url = httpx2.URL("http://xn--fiqs8s.icom.museum") + assert url.raw_host == b"xn--fiqs8s.icom.museum" + + * `url.port` is either None or an integer. URLs that include the default port for + "http", "https", "ws", "wss", and "ftp" schemes have their port + normalized to `None`. + + assert httpx2.URL("http://example.com") == httpx2.URL("http://example.com:80") + assert httpx2.URL("http://example.com").port is None + assert httpx2.URL("http://example.com:80").port is None + + * `url.userinfo` is raw bytes, without URL escaping. Usually you'll want to work + with `url.username` and `url.password` instead, which handle the URL escaping. + + * `url.raw_path` is raw bytes of both the path and query, without URL escaping. + This portion is used as the target when constructing HTTP requests. Usually you'll + want to work with `url.path` instead. + + * `url.query` is raw bytes, without URL escaping. A URL query string portion can + only be properly URL escaped when decoding the parameter names and values + themselves. + """ + + def __init__(self, url: URL | str = "", **kwargs: typing.Any) -> None: + if kwargs: + allowed = { + "scheme": str, + "username": str, + "password": str, + "userinfo": bytes, + "host": str, + "port": int, + "netloc": bytes, + "path": str, + "query": bytes, + "raw_path": bytes, + "fragment": str, + "params": object, + } + + # Perform type checking for all supported keyword arguments. + for key, value in kwargs.items(): + if key not in allowed: + message = f"{key!r} is an invalid keyword argument for URL()" + raise TypeError(message) + if value is not None and not isinstance(value, allowed[key]): + expected = allowed[key].__name__ + seen = type(value).__name__ + message = f"Argument {key!r} must be {expected} but got {seen}" + raise TypeError(message) + if isinstance(value, bytes): + kwargs[key] = value.decode("ascii") + + if "params" in kwargs: + # Replace any "params" keyword with the raw "query" instead. + # + # Ensure that empty params use `kwargs["query"] = None` rather + # than `kwargs["query"] = ""`, so that generated URLs do not + # include an empty trailing "?". + params = kwargs.pop("params") + kwargs["query"] = None if not params else str(QueryParams(params)) + + if isinstance(url, str): + self._uri_reference = urlparse(url, **kwargs) + elif isinstance(url, URL): + self._uri_reference = url._uri_reference.copy_with(**kwargs) + else: + raise TypeError(f"Invalid type for url. Expected str or httpx2.URL, got {type(url)}: {url!r}") + + @property + def scheme(self) -> str: + """ + The URL scheme, such as "http", "https". + Always normalised to lowercase. + """ + return self._uri_reference.scheme + + @property + def raw_scheme(self) -> bytes: + """ + The raw bytes representation of the URL scheme, such as b"http", b"https". + Always normalised to lowercase. + """ + return self._uri_reference.scheme.encode("ascii") + + @property + def userinfo(self) -> bytes: + """ + The URL userinfo as a raw bytestring. + For example: b"jo%40email.com:a%20secret". + """ + return self._uri_reference.userinfo.encode("ascii") + + @property + def username(self) -> str: + """ + The URL username as a string, with URL decoding applied. + For example: "jo@email.com" + """ + userinfo = self._uri_reference.userinfo + return unquote(userinfo.partition(":")[0]) + + @property + def password(self) -> str: + """ + The URL password as a string, with URL decoding applied. + For example: "a secret" + """ + userinfo = self._uri_reference.userinfo + return unquote(userinfo.partition(":")[2]) + + @property + def host(self) -> str: + """ + The URL host as a string. + Always normalized to lowercase, with IDNA hosts decoded into unicode. + + Examples: + + url = httpx2.URL("http://www.EXAMPLE.org") + assert url.host == "www.example.org" + + url = httpx2.URL("http://中国.icom.museum") + assert url.host == "中国.icom.museum" + + url = httpx2.URL("http://xn--fiqs8s.icom.museum") + assert url.host == "中国.icom.museum" + + url = httpx2.URL("https://[::ffff:192.168.0.1]") + assert url.host == "::ffff:192.168.0.1" + """ + host: str = self._uri_reference.host + + if "xn--" in host: + host = idna.decode(host, display=True) + + return host + + @property + def raw_host(self) -> bytes: + """ + The raw bytes representation of the URL host. + Always normalized to lowercase, and IDNA encoded. + + Examples: + + url = httpx2.URL("http://www.EXAMPLE.org") + assert url.raw_host == b"www.example.org" + + url = httpx2.URL("http://中国.icom.museum") + assert url.raw_host == b"xn--fiqs8s.icom.museum" + + url = httpx2.URL("http://xn--fiqs8s.icom.museum") + assert url.raw_host == b"xn--fiqs8s.icom.museum" + + url = httpx2.URL("https://[::ffff:192.168.0.1]") + assert url.raw_host == b"::ffff:192.168.0.1" + """ + return self._uri_reference.host.encode("ascii") + + @property + def port(self) -> int | None: + """ + The URL port as an integer. + + Note that the URL class performs port normalization as per the WHATWG spec. + Default ports for "http", "https", "ws", "wss", and "ftp" schemes are always + treated as `None`. + + For example: + + assert httpx2.URL("http://www.example.com") == httpx2.URL("http://www.example.com:80") + assert httpx2.URL("http://www.example.com:80").port is None + """ + return self._uri_reference.port + + @property + def netloc(self) -> bytes: + """ + Either `` or `:` as bytes. + Always normalized to lowercase, and IDNA encoded. + + This property may be used for generating the value of a request + "Host" header. + """ + return self._uri_reference.netloc.encode("ascii") + + @property + def path(self) -> str: + """ + The URL path as a string. Excluding the query string, and URL decoded. + + For example: + + url = httpx2.URL("https://example.com/pa%20th") + assert url.path == "/pa th" + """ + path = self._uri_reference.path or "/" + return unquote(path) + + @property + def query(self) -> bytes: + """ + The URL query string, as raw bytes, excluding the leading b"?". + + This is necessarily a bytewise interface, because we cannot + perform URL decoding of this representation until we've parsed + the keys and values into a QueryParams instance. + + For example: + + url = httpx2.URL("https://example.com/?filter=some%20search%20terms") + assert url.query == b"filter=some%20search%20terms" + """ + query = self._uri_reference.query or "" + return query.encode("ascii") + + @property + def params(self) -> QueryParams: + """ + The URL query parameters, neatly parsed and packaged into an immutable + multidict representation. + """ + return QueryParams(self._uri_reference.query) + + @property + def raw_path(self) -> bytes: + """ + The complete URL path and query string as raw bytes. + Used as the target when constructing HTTP requests. + + For example: + + GET /users?search=some%20text HTTP/1.1 + Host: www.example.org + Connection: close + """ + path = self._uri_reference.path or "/" + if self._uri_reference.query is not None: + path += "?" + self._uri_reference.query + return path.encode("ascii") + + @property + def fragment(self) -> str: + """ + The URL fragments, as used in HTML anchors. + As a string, without the leading '#'. + """ + return unquote(self._uri_reference.fragment or "") + + @property + def is_absolute_url(self) -> bool: + """ + Return `True` for absolute URLs such as 'http://example.com/path', + and `False` for relative URLs such as '/path'. + """ + # We don't use `.is_absolute` from `rfc3986` because it treats + # URLs with a fragment portion as not absolute. + # What we actually care about is if the URL provides + # a scheme and hostname to which connections should be made. + return bool(self._uri_reference.scheme and self._uri_reference.host) + + @property + def is_relative_url(self) -> bool: + """ + Return `False` for absolute URLs such as 'http://example.com/path', + and `True` for relative URLs such as '/path'. + """ + return not self.is_absolute_url + + @property + def origin(self) -> Origin: + """ + The URL origin as an immutable, hashable `Origin` value. + + The origin consists of the URL scheme, host, and effective port. + User information, path, query, and fragment components are excluded. + """ + return Origin(self) + + def copy_with(self, **kwargs: typing.Any) -> URL: + """ + Copy this URL, returning a new URL with some components altered. + Accepts the same set of parameters as the components that are made + available via properties on the `URL` class. + + For example: + + url = httpx2.URL("https://www.example.com").copy_with( + username="jo@gmail.com", password="a secret" + ) + assert url == "https://jo%40email.com:a%20secret@www.example.com" + """ + return URL(self, **kwargs) + + def copy_set_param(self, key: str, value: typing.Any = None) -> URL: + return self.copy_with(params=self.params.set(key, value)) + + def copy_add_param(self, key: str, value: typing.Any = None) -> URL: + return self.copy_with(params=self.params.add(key, value)) + + def copy_remove_param(self, key: str) -> URL: + return self.copy_with(params=self.params.remove(key)) + + def copy_merge_params(self, params: QueryParamTypes) -> URL: + return self.copy_with(params=self.params.merge(params)) + + def join(self, url: URL | str) -> URL: + """ + Return an absolute URL, using this URL as the base. + + Eg. + + url = httpx2.URL("https://www.example.com/test") + url = url.join("/new/path") + assert url == "https://www.example.com/new/path" + """ + from urllib.parse import urljoin + + return URL(urljoin(str(self), str(URL(url)))) + + def __hash__(self) -> int: + return hash(str(self)) + + def __eq__(self, other: typing.Any) -> bool: + return isinstance(other, (URL, str)) and str(self) == str(URL(other)) + + def __str__(self) -> str: + return str(self._uri_reference) + + def __repr__(self) -> str: + scheme, userinfo, host, port, path, query, fragment = self._uri_reference + + if ":" in userinfo: + # Mask any password component. + userinfo = f"{userinfo.split(':')[0]}:[secure]" + + authority = "".join( + [ + f"{userinfo}@" if userinfo else "", + f"[{host}]" if ":" in host else host, + f":{port}" if port is not None else "", + ] + ) + url = "".join( + [ + f"{self.scheme}:" if scheme else "", + f"//{authority}" if authority else "", + path, + f"?{query}" if query is not None else "", + f"#{fragment}" if fragment is not None else "", + ] + ) + + return f"{self.__class__.__name__}({url!r})" + + @property + @deprecated("URL.raw is deprecated.", category=HTTPXDeprecationWarning) + def raw(self) -> tuple[bytes, bytes, int, bytes]: # pragma: no cover + import collections + + RawURL = collections.namedtuple("RawURL", ["raw_scheme", "raw_host", "port", "raw_path"]) + return RawURL( + raw_scheme=self.raw_scheme, + raw_host=self.raw_host, + port=self.port, + raw_path=self.raw_path, + ) + + +_ORIGIN_DEFAULT_PORTS = { + "ftp": 21, + "http": 80, + "https": 443, + "ws": 80, + "wss": 443, +} + + +@dataclass(frozen=True, slots=True, init=False) +class Origin: + """ + The scheme, host, and effective port of a URL. + + Origins are normalized, immutable, comparable, and hashable. + + See RFC 9110, Section 4.3.1: https://www.rfc-editor.org/rfc/rfc9110.html#name-uri-origin + """ + + scheme: str + host: str + port: int | None + + def __init__(self, url: URL | str) -> None: + if not isinstance(url, URL): + url = URL(url) + + if url.is_relative_url: + raise ValueError("URL must be absolute to have an origin") + + host = url.host + if b":" in url.raw_host: + # IPv6 addresses may have multiple equivalent string forms. Store + # their canonical compressed form so origin equality is numeric. + host = str(ipaddress.IPv6Address(url.raw_host.decode("ascii"))) + + port = url.port + if port is None: + port = _ORIGIN_DEFAULT_PORTS.get(url.scheme) + + object.__setattr__(self, "scheme", url.scheme) + object.__setattr__(self, "host", host) + object.__setattr__(self, "port", port) + + +class QueryParams(typing.Mapping[str, str]): + """ + URL query parameters, as a multi-dict. + """ + + def __init__(self, *args: QueryParamTypes | None, **kwargs: typing.Any) -> None: + assert len(args) < 2, "Too many arguments." + assert not (args and kwargs), "Cannot mix named and unnamed arguments." + + value = args[0] if args else kwargs + + if value is None or isinstance(value, (str, bytes)): + value = value.decode("ascii") if isinstance(value, bytes) else value + self._dict = parse_qs(value, keep_blank_values=True) + elif isinstance(value, QueryParams): + self._dict = {k: list(v) for k, v in value._dict.items()} + else: + dict_value: dict[typing.Any, list[typing.Any]] = {} + if isinstance(value, (list, tuple)): + # Convert list inputs like: + # [("a", "123"), ("a", "456"), ("b", "789")] + # To a dict representation, like: + # {"a": ["123", "456"], "b": ["789"]} + for item in value: + dict_value.setdefault(item[0], []).append(item[1]) + else: + # Convert dict inputs like: + # {"a": "123", "b": ["456", "789"]} + # To dict inputs where values are always lists, like: + # {"a": ["123"], "b": ["456", "789"]} + dict_value = {k: list(v) if isinstance(v, (list, tuple)) else [v] for k, v in value.items()} + + # Ensure that keys and values are neatly coerced to strings. + # We coerce values `True` and `False` to JSON-like "true" and "false" + # representations, and coerce `None` values to the empty string. + self._dict = {str(k): [primitive_value_to_str(item) for item in v] for k, v in dict_value.items()} + + def keys(self) -> typing.KeysView[str]: + """ + Return all the keys in the query params. + + Usage: + + q = httpx2.QueryParams("a=123&a=456&b=789") + assert list(q.keys()) == ["a", "b"] + """ + return self._dict.keys() + + def values(self) -> typing.ValuesView[str]: + """ + Return all the values in the query params. If a key occurs more than once + only the first item for that key is returned. + + Usage: + + q = httpx2.QueryParams("a=123&a=456&b=789") + assert list(q.values()) == ["123", "789"] + """ + return {k: v[0] for k, v in self._dict.items()}.values() + + def items(self) -> typing.ItemsView[str, str]: + """ + Return all items in the query params. If a key occurs more than once + only the first item for that key is returned. + + Usage: + + q = httpx2.QueryParams("a=123&a=456&b=789") + assert list(q.items()) == [("a", "123"), ("b", "789")] + """ + return {k: v[0] for k, v in self._dict.items()}.items() + + def multi_items(self) -> list[tuple[str, str]]: + """ + Return all items in the query params. Allow duplicate keys to occur. + + Usage: + + q = httpx2.QueryParams("a=123&a=456&b=789") + assert list(q.multi_items()) == [("a", "123"), ("a", "456"), ("b", "789")] + """ + multi_items: list[tuple[str, str]] = [] + for k, v in self._dict.items(): + multi_items.extend([(k, i) for i in v]) + return multi_items + + def get(self, key: typing.Any, default: typing.Any = None) -> typing.Any: + """ + Get a value from the query param for a given key. If the key occurs + more than once, then only the first value is returned. + + Usage: + + q = httpx2.QueryParams("a=123&a=456&b=789") + assert q.get("a") == "123" + """ + if key in self._dict: + return self._dict[str(key)][0] + return default + + def get_list(self, key: str) -> list[str]: + """ + Get all values from the query param for a given key. + + Usage: + + q = httpx2.QueryParams("a=123&a=456&b=789") + assert q.get_list("a") == ["123", "456"] + """ + return list(self._dict.get(str(key), [])) + + def set(self, key: str, value: typing.Any = None) -> QueryParams: + """ + Return a new QueryParams instance, setting the value of a key. + + Usage: + + q = httpx2.QueryParams("a=123") + q = q.set("a", "456") + assert q == httpx2.QueryParams("a=456") + """ + q = QueryParams() + q._dict = dict(self._dict) + q._dict[str(key)] = [primitive_value_to_str(value)] + return q + + def add(self, key: str, value: typing.Any = None) -> QueryParams: + """ + Return a new QueryParams instance, setting or appending the value of a key. + + Usage: + + q = httpx2.QueryParams("a=123") + q = q.add("a", "456") + assert q == httpx2.QueryParams("a=123&a=456") + """ + q = QueryParams() + q._dict = dict(self._dict) + q._dict[str(key)] = q.get_list(key) + [primitive_value_to_str(value)] + return q + + def remove(self, key: str) -> QueryParams: + """ + Return a new QueryParams instance, removing the value of a key. + + Usage: + + q = httpx2.QueryParams("a=123") + q = q.remove("a") + assert q == httpx2.QueryParams("") + """ + q = QueryParams() + q._dict = dict(self._dict) + q._dict.pop(str(key), None) + return q + + def merge(self, params: QueryParamTypes | None = None) -> QueryParams: + """ + Return a new QueryParams instance, updated with. + + Usage: + + q = httpx2.QueryParams("a=123") + q = q.merge({"b": "456"}) + assert q == httpx2.QueryParams("a=123&b=456") + + q = httpx2.QueryParams("a=123") + q = q.merge({"a": "456", "b": "789"}) + assert q == httpx2.QueryParams("a=456&b=789") + """ + q = QueryParams(params) + q._dict = {**self._dict, **q._dict} + return q + + def __getitem__(self, key: typing.Any) -> str: + return self._dict[key][0] + + def __contains__(self, key: typing.Any) -> bool: + return key in self._dict + + def __iter__(self) -> typing.Iterator[typing.Any]: + return iter(self.keys()) + + def __len__(self) -> int: + return len(self._dict) + + def __bool__(self) -> bool: + return bool(self._dict) + + def __hash__(self) -> int: + return hash(str(self)) + + def __eq__(self, other: typing.Any) -> bool: + if not isinstance(other, self.__class__): + return False + return sorted(self.multi_items()) == sorted(other.multi_items()) + + def __str__(self) -> str: + return urlencode(self.multi_items()) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + query_string = str(self) + return f"{class_name}({query_string!r})" + + def update(self, params: QueryParamTypes | None = None) -> None: + raise RuntimeError("QueryParams are immutable since 0.18.0. Use `q = q.merge(...)` to create an updated copy.") + + def __setitem__(self, key: str, value: str) -> None: + raise RuntimeError( + "QueryParams are immutable since 0.18.0. Use `q = q.set(key, value)` to create an updated copy." + ) diff --git a/venv/lib/python3.12/site-packages/httpx2/_utils.py b/venv/lib/python3.12/site-packages/httpx2/_utils.py new file mode 100644 index 0000000..6112f3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/_utils.py @@ -0,0 +1,248 @@ +from __future__ import annotations + +import ipaddress +import os +import re +import typing +from urllib.request import getproxies + +from ._types import PrimitiveData + +if typing.TYPE_CHECKING: + from ._urls import URL + + +def primitive_value_to_str(value: PrimitiveData) -> str: + """ + Coerce a primitive data type into a string value. + + Note that we prefer JSON-style 'true'/'false' for boolean values here. + """ + if value is True: + return "true" + elif value is False: + return "false" + elif value is None: + return "" + return str(value) + + +def get_environment_proxies() -> dict[str, str | None]: + """Gets proxy information from the environment""" + + # urllib.request.getproxies() falls back on System + # Registry and Config for proxies on Windows and macOS. + # We don't want to propagate non-HTTP proxies into + # our configuration such as 'TRAVIS_APT_PROXY'. + proxy_info = getproxies() + mounts: dict[str, str | None] = {} + + for scheme in ("http", "https", "all"): + if proxy_info.get(scheme): + hostname = proxy_info[scheme] + mounts[f"{scheme}://"] = hostname if "://" in hostname else f"http://{hostname}" + + no_proxy_hosts = [host.strip() for host in proxy_info.get("no", "").split(",")] + for hostname in no_proxy_hosts: + # See https://curl.haxx.se/libcurl/c/CURLOPT_NOPROXY.html for details + # on how names in `NO_PROXY` are handled. + if hostname == "*": + # If NO_PROXY=* is used or if "*" occurs as any one of the comma + # separated hostnames, then we should just bypass any information + # from HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, and always ignore + # proxies. + return {} + elif hostname: + # NO_PROXY=.google.com is marked as "all://*.google.com, + # which disables "www.google.com" but not "google.com" + # NO_PROXY=google.com is marked as "all://*google.com, + # which disables "www.google.com" and "google.com". + # (But not "wwwgoogle.com") + # NO_PROXY can include domains, IPv6, IPv4 addresses and "localhost" + # NO_PROXY=example.com,::1,localhost,192.168.0.0/16 + if "://" in hostname: + mounts[hostname] = None + elif is_ipv4_hostname(hostname): + mounts[f"all://{hostname}"] = None + elif is_ipv6_hostname(hostname): + if "/" in hostname: + addr, _, subnet = hostname.partition("/") + mounts[f"all://[{addr}]/{subnet}"] = None + else: + mounts[f"all://[{hostname}]"] = None + elif hostname.lower() == "localhost": + mounts[f"all://{hostname}"] = None + else: + mounts[f"all://*{hostname}"] = None + + return mounts + + +def to_bytes(value: str | bytes, encoding: str = "utf-8") -> bytes: + return value.encode(encoding) if isinstance(value, str) else value + + +def to_str(value: str | bytes, encoding: str = "utf-8") -> str: + return value if isinstance(value, str) else value.decode(encoding) + + +def to_bytes_or_str(value: str, match_type_of: typing.AnyStr) -> typing.AnyStr: + return value if isinstance(match_type_of, str) else value.encode() + + +def peek_filelike_length(stream: typing.Any) -> int | None: + """ + Given a file-like stream object, return its length in number of bytes + without reading it into memory. + """ + try: + # Is it an actual file? + fd = stream.fileno() + # Yup, seems to be an actual file. + length = os.fstat(fd).st_size + except (AttributeError, OSError): + # No... Maybe it's something that supports random access, like `io.BytesIO`? + try: + # Assuming so, go to end of stream to figure out its length, + # then put it back in place. + offset = stream.tell() + length = stream.seek(0, os.SEEK_END) + stream.seek(offset) + except (AttributeError, OSError): + # Not even that? Sorry, we're doomed... + return None + + return length + + +def peek_filelike_remaining_length(stream: typing.Any) -> int | None: + """Return the remaining byte length without changing the stream position.""" + try: + offset: int = stream.tell() + length: int = stream.seek(0, os.SEEK_END) + stream.seek(offset) + except (AttributeError, OSError): + return None + + return max(0, length - offset) + + +class URLPattern: + """ + A utility class currently used for making lookups against proxy keys... + + # Wildcard matching... + >>> pattern = URLPattern("all://") + >>> pattern.matches(httpx2.URL("http://example.com")) + True + + # Witch scheme matching... + >>> pattern = URLPattern("https://") + >>> pattern.matches(httpx2.URL("https://example.com")) + True + >>> pattern.matches(httpx2.URL("http://example.com")) + False + + # With domain matching... + >>> pattern = URLPattern("https://example.com") + >>> pattern.matches(httpx2.URL("https://example.com")) + True + >>> pattern.matches(httpx2.URL("http://example.com")) + False + >>> pattern.matches(httpx2.URL("https://other.com")) + False + + # Wildcard scheme, with domain matching... + >>> pattern = URLPattern("all://example.com") + >>> pattern.matches(httpx2.URL("https://example.com")) + True + >>> pattern.matches(httpx2.URL("http://example.com")) + True + >>> pattern.matches(httpx2.URL("https://other.com")) + False + + # With port matching... + >>> pattern = URLPattern("https://example.com:1234") + >>> pattern.matches(httpx2.URL("https://example.com:1234")) + True + >>> pattern.matches(httpx2.URL("https://example.com")) + False + """ + + def __init__(self, pattern: str) -> None: + from ._urls import URL + + if pattern and ":" not in pattern: + raise ValueError( + f"Proxy keys should use proper URL forms rather " + f"than plain scheme strings. " + f'Instead of "{pattern}", use "{pattern}://"' + ) + + url = URL(pattern) + self.pattern = pattern + self.scheme = "" if url.scheme == "all" else url.scheme + self.host = "" if url.host == "*" else url.host + self.port = url.port + if not url.host or url.host == "*": + self.host_regex: typing.Pattern[str] | None = None + elif url.host.startswith("*."): + # *.example.com should match "www.example.com", but not "example.com" + domain = re.escape(url.host[2:]) + self.host_regex = re.compile(f"^.+\\.{domain}$") + elif url.host.startswith("*"): + # *example.com should match "www.example.com" and "example.com" + domain = re.escape(url.host[1:]) + self.host_regex = re.compile(f"^(.+\\.)?{domain}$") + else: + # example.com should match "example.com" but not "www.example.com" + domain = re.escape(url.host) + self.host_regex = re.compile(f"^{domain}$") + + def matches(self, other: URL) -> bool: + if self.scheme and self.scheme != other.scheme: + return False + if self.host and self.host_regex is not None and not self.host_regex.match(other.host): + return False + if self.port is not None and self.port != other.port: + return False + return True + + @property + def priority(self) -> tuple[int, int, int]: + """ + The priority allows URLPattern instances to be sortable, so that + we can match from most specific to least specific. + """ + # URLs with a port should take priority over URLs without a port. + port_priority = 0 if self.port is not None else 1 + # Longer hostnames should match first. + host_priority = -len(self.host) + # Longer schemes should match first. + scheme_priority = -len(self.scheme) + return (port_priority, host_priority, scheme_priority) + + def __hash__(self) -> int: + return hash(self.pattern) + + def __lt__(self, other: URLPattern) -> bool: + return self.priority < other.priority + + def __eq__(self, other: typing.Any) -> bool: + return isinstance(other, URLPattern) and self.pattern == other.pattern + + +def is_ipv4_hostname(hostname: str) -> bool: + try: + ipaddress.IPv4Address(hostname.split("/")[0]) + except Exception: + return False + return True + + +def is_ipv6_hostname(hostname: str) -> bool: + try: + ipaddress.IPv6Address(hostname.split("/")[0]) + except Exception: + return False + return True diff --git a/venv/lib/python3.12/site-packages/httpx2/py.typed b/venv/lib/python3.12/site-packages/httpx2/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/httpx2/websockets/__init__.py b/venv/lib/python3.12/site-packages/httpx2/websockets/__init__.py new file mode 100644 index 0000000..8545b09 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/websockets/__init__.py @@ -0,0 +1,57 @@ +""" +WebSocket support, derived from httpx-ws (https://github.com/frankie567/httpx-ws). + +``` +MIT License + +Copyright (c) 2021 François Voron + +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. +``` +""" + +from ._api import ( + AsyncWebSocketClient, + AsyncWebSocketSession, + JSONMode, + WebSocketClient, + WebSocketSession, +) +from ._exceptions import ( + HTTPXWSException, + WebSocketDisconnect, + WebSocketInvalidTypeReceived, + WebSocketNetworkError, + WebSocketUpgradeError, +) +from ._transport import ASGIWebSocketTransport + +__all__ = [ + "ASGIWebSocketTransport", + "AsyncWebSocketClient", + "AsyncWebSocketSession", + "HTTPXWSException", + "JSONMode", + "WebSocketClient", + "WebSocketDisconnect", + "WebSocketInvalidTypeReceived", + "WebSocketNetworkError", + "WebSocketSession", + "WebSocketUpgradeError", +] diff --git a/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d13026a Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/_api.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/_api.cpython-312.pyc new file mode 100644 index 0000000..5f6a34a Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/_api.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/_exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/_exceptions.cpython-312.pyc new file mode 100644 index 0000000..cbcaee8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/_exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/_ping.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/_ping.cpython-312.pyc new file mode 100644 index 0000000..4ad365f Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/_ping.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/_transport.cpython-312.pyc b/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/_transport.cpython-312.pyc new file mode 100644 index 0000000..bdb91eb Binary files /dev/null and b/venv/lib/python3.12/site-packages/httpx2/websockets/__pycache__/_transport.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/httpx2/websockets/_api.py b/venv/lib/python3.12/site-packages/httpx2/websockets/_api.py new file mode 100644 index 0000000..1ce9cb0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/websockets/_api.py @@ -0,0 +1,1660 @@ +from __future__ import annotations + +import base64 +import concurrent.futures +import contextlib +import json +import queue +import secrets +import sys +import threading +import typing +from types import TracebackType + +if sys.version_info >= (3, 13): + from typing import TypeVar # pragma: no cover +else: + from typing_extensions import TypeVar # pragma: no cover + +import anyio +import wsproto +import wsproto.utilities +from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream +from wsproto.frame_protocol import CloseReason + +from .._client import USE_CLIENT_DEFAULT +from .._config import ( + DEFAULT_KEEPALIVE_PING_INTERVAL_SECONDS, + DEFAULT_KEEPALIVE_PING_TIMEOUT_SECONDS, + DEFAULT_MAX_MESSAGE_SIZE_BYTES, + DEFAULT_QUEUE_SIZE, +) +from .._models import Headers +from ._exceptions import ( + HTTPXWSException, + WebSocketDisconnect, + WebSocketInvalidTypeReceived, + WebSocketNetworkError, + WebSocketUpgradeError, +) +from ._ping import AsyncPingManager, PingManager +from ._transport import ASGIWebSocketAsyncNetworkStream + +if typing.TYPE_CHECKING: + from httpcore2 import AsyncNetworkStream, NetworkStream + + from .._client import AsyncClient, Client, UseClientDefault + from .._models import Response + from .._types import ( + AuthTypes, + CookieTypes, + HeaderTypes, + QueryParamTypes, + RequestExtensions, + TimeoutTypes, + ) + +JSONMode = typing.Literal["text", "binary"] +TaskFunction = typing.TypeVar("TaskFunction") +TaskResult = typing.TypeVar("TaskResult") +SyncSession = TypeVar("SyncSession", bound="WebSocketSession", default="WebSocketSession") +AsyncSession = TypeVar("AsyncSession", bound="AsyncWebSocketSession", default="AsyncWebSocketSession") + + +class ShouldClose(Exception): + pass + + +class EndOfStream(Exception): + pass + + +class WebSocketSession: + """ + Sync context manager representing an opened WebSocket session. + + Attributes: + subprotocol (typing.Optional[str]): + Optional protocol that has been accepted by the server. + response (Response | None): + The webSocket handshake response. + """ + + subprotocol: str | None + response: Response | None + + def __init__( + self, + stream: NetworkStream, + *, + max_message_size_bytes: int = DEFAULT_MAX_MESSAGE_SIZE_BYTES, + queue_size: int = DEFAULT_QUEUE_SIZE, + keepalive_ping_interval_seconds: float | None = DEFAULT_KEEPALIVE_PING_INTERVAL_SECONDS, + keepalive_ping_timeout_seconds: float | None = DEFAULT_KEEPALIVE_PING_TIMEOUT_SECONDS, + response: Response | None = None, + ) -> None: + self.stream = stream + self.connection = wsproto.connection.Connection(wsproto.ConnectionType.CLIENT) + self.response = response + if self.response is not None: + self.subprotocol = self.response.headers.get("sec-websocket-protocol") + else: + self.subprotocol = None + + self._events: queue.Queue[wsproto.events.Event | HTTPXWSException] = queue.Queue(queue_size) + + self._ping_manager = PingManager() + self._should_close = threading.Event() + self._write_lock = threading.Lock() + self._should_close_task: concurrent.futures.Future[bool] | None = None + self._executor: concurrent.futures.ThreadPoolExecutor | None = None + + self._max_message_size_bytes = max_message_size_bytes + self._queue_size = queue_size + self._keepalive_ping_interval_seconds = keepalive_ping_interval_seconds + self._keepalive_ping_timeout_seconds = keepalive_ping_timeout_seconds + + def _get_executor_should_close_task( + self, + ) -> tuple[concurrent.futures.ThreadPoolExecutor, concurrent.futures.Future[bool]]: + if self._should_close_task is None: + self._executor = concurrent.futures.ThreadPoolExecutor() + self._should_close_task = self._executor.submit(self._should_close.wait) + assert self._executor is not None + return self._executor, self._should_close_task + + def __enter__(self) -> WebSocketSession: + self._background_receive_task = threading.Thread( + target=self._background_receive, args=(self._max_message_size_bytes,) + ) + self._background_receive_task.start() + + self._background_keepalive_ping_task: threading.Thread | None = None + if self._keepalive_ping_interval_seconds is not None: + self._background_keepalive_ping_task = threading.Thread( + target=self._background_keepalive_ping, + args=( + self._keepalive_ping_interval_seconds, + self._keepalive_ping_timeout_seconds, + ), + ) + self._background_keepalive_ping_task.start() + + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.close() + self._background_receive_task.join() + if self._background_keepalive_ping_task is not None: + self._background_keepalive_ping_task.join() + + def ping(self, payload: bytes = b"") -> threading.Event: + """ + Send a Ping message. + + Args: + payload: + Payload to attach to the Ping event. + Internally, it's used to track this specific event. + If left empty, a random one will be generated. + + Returns: + An event that can be used to wait for the corresponding Pong response. + + Examples: + Send a Ping and wait for the Pong + + pong_callback = ws.ping() + # Will block until the corresponding Pong is received. + pong_callback.wait() + """ + ping_id, callback = self._ping_manager.create(payload) + event = wsproto.events.Ping(ping_id) + self.send(event) + return callback + + def send(self, event: wsproto.events.Event) -> None: + """ + Send an Event message. + + Mainly useful to send events that are not supported by the library. + Most of the time, [ping()][httpx_ws.WebSocketSession.ping], + [send_text()][httpx_ws.WebSocketSession.send_text], + [send_bytes()][httpx_ws.WebSocketSession.send_bytes] + and [send_json()][httpx_ws.WebSocketSession.send_json] are preferred. + + Args: + event: The event to send. + + Raises: + WebSocketNetworkError: A network error occured. + + Examples: + Send an event. + + event = wsproto.events.Message(b"Hello!") + ws.send(event) + """ + import httpcore2 + + try: + data = self.connection.send(event) + with self._write_lock: + self.stream.write(data) + except httpcore2.WriteError as e: + self.close(CloseReason.INTERNAL_ERROR, "Stream write error") + raise WebSocketNetworkError() from e + + def send_text(self, data: str) -> None: + """ + Send a text message. + + Args: + data: The text to send. + + Raises: + WebSocketNetworkError: A network error occured. + + Examples: + Send a text message. + + ws.send_text("Hello!") + """ + event = wsproto.events.TextMessage(data=data) + self.send(event) + + def send_bytes(self, data: bytes) -> None: + """ + Send a bytes message. + + Args: + data: The data to send. + + Raises: + WebSocketNetworkError: A network error occured. + + Examples: + Send a bytes message. + + ws.send_bytes(b"Hello!") + """ + event = wsproto.events.BytesMessage(data=data) + self.send(event) + + def send_json(self, data: typing.Any, mode: JSONMode = "text") -> None: + """ + Send JSON data. + + Args: + data: + The data to send. Must be serializable by [json.dumps][json.dumps]. + mode: + The sending mode. Should either be `'text'` or `'bytes'`. + + Raises: + WebSocketNetworkError: A network error occured. + + Examples: + Send JSON data. + + data = {"message": "Hello!"} + ws.send_json(data) + """ + assert mode in ["text", "binary"] + serialized_data = json.dumps(data) + if mode == "text": + self.send_text(serialized_data) + else: + self.send_bytes(serialized_data.encode("utf-8")) + + def receive(self, timeout: float | None = None) -> wsproto.events.Event: + """ + Receive an event from the server. + + Mainly useful to receive raw [wsproto.events.Event][wsproto.events.Event]. + Most of the time, [receive_text()][httpx_ws.WebSocketSession.receive_text], + [receive_bytes()][httpx_ws.WebSocketSession.receive_bytes], + and [receive_json()][httpx_ws.WebSocketSession.receive_json] are preferred. + + Args: + timeout: + Number of seconds to wait for an event. + If `None`, will block until an event is available. + + Returns: + A raw [wsproto.events.Event][wsproto.events.Event]. + + Raises: + TimeoutError: No event was received before the timeout delay. + WebSocketDisconnect: The server closed the websocket. + WebSocketNetworkError: A network error occured. + + Examples: + Wait for an event until one is available. + + try: + event = ws.receive() + except WebSocketDisconnect: + print("Connection closed") + + Wait for an event for 2 seconds. + + try: + event = ws.receive(timeout=2.) + except TimeoutError: + print("No event received.") + except WebSocketDisconnect: + print("Connection closed") + """ + try: + event = self._events.get(block=True, timeout=timeout) + except queue.Empty as e: + raise TimeoutError from e + if isinstance(event, HTTPXWSException): + raise event + if isinstance(event, wsproto.events.CloseConnection): + raise WebSocketDisconnect(event.code, event.reason) + return event + + def receive_text(self, timeout: float | None = None) -> str: + """ + Receive text from the server. + + Args: + timeout: + Number of seconds to wait for an event. + If `None`, will block until an event is available. + + Returns: + Text data. + + Raises: + TimeoutError: No event was received before the timeout delay. + WebSocketDisconnect: The server closed the websocket. + WebSocketNetworkError: A network error occured. + WebSocketInvalidTypeReceived: The received event was not a text message. + + Examples: + Wait for text until available. + + try: + text = ws.receive_text() + except WebSocketDisconnect: + print("Connection closed") + + Wait for text for 2 seconds. + + try: + event = ws.receive_text(timeout=2.) + except TimeoutError: + print("No text received.") + except WebSocketDisconnect: + print("Connection closed") + """ + event = self.receive(timeout) + if isinstance(event, wsproto.events.TextMessage): + return event.data + raise WebSocketInvalidTypeReceived(event) + + def receive_bytes(self, timeout: float | None = None) -> bytes: + """ + Receive bytes from the server. + + Args: + timeout: + Number of seconds to wait for an event. + If `None`, will block until an event is available. + + Returns: + Bytes data. + + Raises: + TimeoutError: No event was received before the timeout delay. + WebSocketDisconnect: The server closed the websocket. + WebSocketNetworkError: A network error occured. + WebSocketInvalidTypeReceived: The received event was not a bytes message. + + Examples: + Wait for bytes until available. + + try: + data = ws.receive_bytes() + except WebSocketDisconnect: + print("Connection closed") + + Wait for bytes for 2 seconds. + + try: + data = ws.receive_bytes(timeout=2.) + except TimeoutError: + print("No data received.") + except WebSocketDisconnect: + print("Connection closed") + """ + event = self.receive(timeout) + if isinstance(event, wsproto.events.BytesMessage): + return bytes(event.data) + raise WebSocketInvalidTypeReceived(event) + + def receive_json(self, timeout: float | None = None, mode: JSONMode = "text") -> typing.Any: + """ + Receive JSON data from the server. + + The received data should be parseable by [json.loads][json.loads]. + + Args: + timeout: + Number of seconds to wait for an event. + If `None`, will block until an event is available. + mode: + Receive mode. Should either be `'text'` or `'bytes'`. + + Returns: + Parsed JSON data. + + Raises: + TimeoutError: No event was received before the timeout delay. + WebSocketDisconnect: The server closed the websocket. + WebSocketNetworkError: A network error occured. + WebSocketInvalidTypeReceived: The received event + didn't correspond to the specified mode. + + Examples: + Wait for data until available. + + try: + data = ws.receive_json() + except WebSocketDisconnect: + print("Connection closed") + + Wait for data for 2 seconds. + + try: + data = ws.receive_json(timeout=2.) + except TimeoutError: + print("No data received.") + except WebSocketDisconnect: + print("Connection closed") + """ + assert mode in ["text", "binary"] + data: str | bytes + if mode == "text": + data = self.receive_text(timeout) + elif mode == "binary": + data = self.receive_bytes(timeout) + return json.loads(data) + + def close(self, code: int = 1000, reason: str | None = None) -> None: + """ + Close the WebSocket session. + + Internally, it'll send the + [CloseConnection][wsproto.events.CloseConnection] event. + + *This method is automatically called when exiting the context manager.* + + Args: + code: + The integer close code to indicate why the connection has closed. + reason: + Additional reasoning for why the connection has closed. + + Examples: + Close the WebSocket session. + + ws.close() + """ + import httpcore2 + + self._should_close.set() + if self._executor is not None: + self._executor.shutdown(False) + if self.connection.state not in { + wsproto.connection.ConnectionState.LOCAL_CLOSING, + wsproto.connection.ConnectionState.CLOSED, + }: + event = wsproto.events.CloseConnection(code, reason) + data = self.connection.send(event) + try: + with self._write_lock: + self.stream.write(data) + except httpcore2.WriteError: + pass + self.stream.close() + + def _background_receive(self, max_bytes: int) -> None: + """ + Background thread listening for data from the server. + + Internally, it'll: + + * Answer to Ping events. + * Acknowledge Pong events. + * Put other events in the [_events][_events] + queue that'll eventually be consumed by the user. + + Args: + max_bytes: The maximum chunk size to read at each iteration. + """ + import httpcore2 + + partial_message_buffer: str | bytes | None = None + partial_message_size = 0 + try: + while not self._should_close.is_set(): + data = self._wait_until_closed(self._read_stream, max_bytes) + self.connection.receive_data(data) + for event in self.connection.events(): + if isinstance(event, wsproto.events.Ping): + data = self.connection.send(event.response()) + with self._write_lock: + self.stream.write(data) + continue + if isinstance(event, wsproto.events.Pong): + self._ping_manager.ack(event.payload) + continue + if isinstance(event, wsproto.events.CloseConnection): + self._should_close.set() + if isinstance(event, wsproto.events.Message): + partial_message_size += len(event.data.encode() if isinstance(event.data, str) else event.data) + if partial_message_size > max_bytes: + self.close(CloseReason.MESSAGE_TOO_BIG, "Message too big") + self._events.put(WebSocketDisconnect(CloseReason.MESSAGE_TOO_BIG, "Message too big")) + break + # Unfinished message: bufferize + if not event.message_finished: + if partial_message_buffer is None: + partial_message_buffer = event.data + else: + partial_message_buffer += event.data + # Finished message but no buffer: just emit the event + elif partial_message_buffer is None: + partial_message_size = 0 + self._events.put(event) + # Finished message with buffer: emit the full event + else: + event_type = type(event) + full_message_event = event_type(partial_message_buffer + event.data) + partial_message_buffer = None + partial_message_size = 0 + self._events.put(full_message_event) + continue + self._events.put(event) + except (httpcore2.ReadError, httpcore2.WriteError, EndOfStream): + self.close(CloseReason.INTERNAL_ERROR, "Stream error") + self._events.put(WebSocketNetworkError()) + except ShouldClose: + pass + + def _background_keepalive_ping(self, interval_seconds: float, timeout_seconds: float | None = None) -> None: + try: + while not self._should_close.is_set(): + should_close = self._wait_until_closed(self._should_close.wait, interval_seconds) + if should_close: # pragma: no cover + raise ShouldClose() + try: + pong_callback = self.ping() + except wsproto.utilities.LocalProtocolError: + return + if timeout_seconds is not None: + acknowledged = self._wait_until_closed(pong_callback.wait, timeout_seconds) + if not acknowledged: + self.close(CloseReason.INTERNAL_ERROR, "Keepalive ping timeout") + self._events.put(WebSocketNetworkError()) + except ShouldClose: + pass + + def _wait_until_closed( + self, callable: typing.Callable[..., TaskResult], *args: typing.Any, **kwargs: typing.Any + ) -> TaskResult: + try: + executor, should_close_task = self._get_executor_should_close_task() + todo_task = executor.submit(callable, *args, **kwargs) + except RuntimeError as e: + raise ShouldClose() from e + else: + done, _ = concurrent.futures.wait( + (todo_task, should_close_task), # type: ignore[misc] + return_when=concurrent.futures.FIRST_COMPLETED, + ) + if should_close_task in done: + raise ShouldClose() + assert todo_task in done + result = todo_task.result() + return result + + def _read_stream(self, max_bytes: int) -> bytes: + data = self.stream.read(max_bytes) + if data == b"": + raise EndOfStream() + return data + + +class AsyncWebSocketSession(anyio.AsyncContextManagerMixin): + """ + Async context manager representing an opened WebSocket session. + + Internally, this session uses an anyio task group to manage background tasks. + As a result, exceptions that are not caught inside the context manager + and propagate out of the `async with` block will be wrapped + in an [ExceptionGroup][ExceptionGroup]. + + To handle them, use the `except*` syntax: + + async with AsyncWebSocketSession(stream) as ws: + try: + data = await ws.receive_text() + except WebSocketDisconnect: + # Caught inside the context manager: plain exception. + print("Connection closed") + + # If not caught inside: + try: + async with AsyncWebSocketSession(stream) as ws: + data = await ws.receive_text() + except* WebSocketDisconnect: + # Propagated out of the context manager: wrapped in ExceptionGroup. + print("Connection closed") + + Attributes: + subprotocol (typing.Optional[str]): + Optional protocol that has been accepted by the server. + response (Response | None): + The webSocket handshake response. + """ + + subprotocol: str | None + response: Response | None + _send_event: MemoryObjectSendStream[wsproto.events.Event | HTTPXWSException] + _receive_event: MemoryObjectReceiveStream[wsproto.events.Event | HTTPXWSException] + + def __init__( + self, + stream: AsyncNetworkStream, + *, + max_message_size_bytes: int = DEFAULT_MAX_MESSAGE_SIZE_BYTES, + queue_size: int = DEFAULT_QUEUE_SIZE, + keepalive_ping_interval_seconds: float | None = DEFAULT_KEEPALIVE_PING_INTERVAL_SECONDS, + keepalive_ping_timeout_seconds: float | None = DEFAULT_KEEPALIVE_PING_TIMEOUT_SECONDS, + response: Response | None = None, + ) -> None: + self.stream = stream + self.connection = wsproto.connection.Connection(wsproto.ConnectionType.CLIENT) + self.response = response + if self.response is not None: + self.subprotocol = self.response.headers.get("sec-websocket-protocol") + else: + self.subprotocol = None + + self._ping_manager = AsyncPingManager() + self._should_close = anyio.Event() + self._write_lock = anyio.Lock() + + self._max_message_size_bytes = max_message_size_bytes + self._queue_size = queue_size + + # Always disable keepalive ping when emulating ASGI + if isinstance(stream, ASGIWebSocketAsyncNetworkStream): + self._keepalive_ping_interval_seconds = None + self._keepalive_ping_timeout_seconds = None + else: + self._keepalive_ping_interval_seconds = keepalive_ping_interval_seconds + self._keepalive_ping_timeout_seconds = keepalive_ping_timeout_seconds + + @contextlib.asynccontextmanager + async def __asynccontextmanager__(self) -> typing.AsyncGenerator[AsyncWebSocketSession, None]: + self._send_event, self._receive_event = anyio.create_memory_object_stream[ + wsproto.events.Event | HTTPXWSException + ]() + self._background_task_group = anyio.create_task_group() + + async with self._send_event, self._receive_event, self._background_task_group: + self._background_task_group.start_soon(self._background_receive, self._max_message_size_bytes) + if self._keepalive_ping_interval_seconds is not None: + self._background_task_group.start_soon( + self._background_keepalive_ping, + self._keepalive_ping_interval_seconds, + self._keepalive_ping_timeout_seconds, + ) + + try: + yield self + finally: + self._background_task_group.cancel_scope.cancel() + with anyio.CancelScope(shield=True): + await self.close() + + async def ping(self, payload: bytes = b"") -> anyio.Event: + """ + Send a Ping message. + + Args: + payload: + Payload to attach to the Ping event. + Internally, it's used to track this specific event. + If left empty, a random one will be generated. + + Returns: + An event that can be used to wait for the corresponding Pong response. + + Examples: + Send a Ping and wait for the Pong + + pong_callback = await ws.ping() + # Will block until the corresponding Pong is received. + await pong_callback.wait() + """ + ping_id, callback = self._ping_manager.create(payload) + event = wsproto.events.Ping(ping_id) + await self.send(event) + return callback + + async def send(self, event: wsproto.events.Event) -> None: + """ + Send an Event message. + + Mainly useful to send events that are not supported by the library. + Most of the time, [ping()][httpx_ws.AsyncWebSocketSession.ping], + [send_text()][httpx_ws.AsyncWebSocketSession.send_text], + [send_bytes()][httpx_ws.AsyncWebSocketSession.send_bytes] + and [send_json()][httpx_ws.AsyncWebSocketSession.send_json] are preferred. + + Args: + event: The event to send. + + Raises: + WebSocketNetworkError: A network error occured. + + Note: + Exceptions not caught inside the context manager will be + wrapped in an [ExceptionGroup][ExceptionGroup]. Use `except*` to catch them + outside the `async with` block. + + Examples: + Send an event. + + event = await wsproto.events.Message(b"Hello!") + ws.send(event) + """ + import httpcore2 + + try: + data = self.connection.send(event) + async with self._write_lock: + await self.stream.write(data) + except httpcore2.WriteError as e: + await self.close(CloseReason.INTERNAL_ERROR, "Stream write error") + raise WebSocketNetworkError() from e + + async def send_text(self, data: str) -> None: + """ + Send a text message. + + Args: + data: The text to send. + + Raises: + WebSocketNetworkError: A network error occured. + + Note: + Exceptions not caught inside the context manager will be + wrapped in an [ExceptionGroup][ExceptionGroup]. Use `except*` to catch them + outside the `async with` block. + + Examples: + Send a text message. + + await ws.send_text("Hello!") + """ + event = wsproto.events.TextMessage(data=data) + await self.send(event) + + async def send_bytes(self, data: bytes) -> None: + """ + Send a bytes message. + + Args: + data: The data to send. + + Raises: + WebSocketNetworkError: A network error occured. + + Note: + Exceptions not caught inside the context manager will be + wrapped in an [ExceptionGroup][ExceptionGroup]. Use `except*` to catch them + outside the `async with` block. + + Examples: + Send a bytes message. + + await ws.send_bytes(b"Hello!") + """ + event = wsproto.events.BytesMessage(data=data) + await self.send(event) + + async def send_json(self, data: typing.Any, mode: JSONMode = "text") -> None: + """ + Send JSON data. + + Args: + data: + The data to send. Must be serializable by [json.dumps][json.dumps]. + mode: + The sending mode. Should either be `'text'` or `'bytes'`. + + Raises: + WebSocketNetworkError: A network error occured. + + Note: + Exceptions not caught inside the context manager will be + wrapped in an [ExceptionGroup][ExceptionGroup]. Use `except*` to catch them + outside the `async with` block. + + Examples: + Send JSON data. + + data = {"message": "Hello!"} + await ws.send_json(data) + """ + assert mode in ["text", "binary"] + serialized_data = json.dumps(data) + if mode == "text": + await self.send_text(serialized_data) + else: + await self.send_bytes(serialized_data.encode("utf-8")) + + async def receive(self, timeout: float | None = None) -> wsproto.events.Event: + """ + Receive an event from the server. + + Mainly useful to receive raw [wsproto.events.Event][wsproto.events.Event]. + Most of the time, [receive_text()][httpx_ws.AsyncWebSocketSession.receive_text], + [receive_bytes()][httpx_ws.AsyncWebSocketSession.receive_bytes], + and [receive_json()][httpx_ws.AsyncWebSocketSession.receive_json] are preferred. + + Args: + timeout: + Number of seconds to wait for an event. + If `None`, will block until an event is available. + + Returns: + A raw [wsproto.events.Event][wsproto.events.Event]. + + Raises: + TimeoutError: No event was received before the timeout delay. + WebSocketDisconnect: The server closed the websocket. + WebSocketNetworkError: A network error occured. + + Note: + Exceptions not caught inside the context manager will be + wrapped in an [ExceptionGroup][ExceptionGroup]. Use `except*` to catch them + outside the `async with` block. + + Examples: + Wait for an event until one is available. + + try: + event = await ws.receive() + except WebSocketDisconnect: + print("Connection closed") + + Wait for an event for 2 seconds. + + try: + event = await ws.receive(timeout=2.) + except TimeoutError: + print("No event received.") + except WebSocketDisconnect: + print("Connection closed") + """ + with anyio.fail_after(timeout): + event = await self._receive_event.receive() + if isinstance(event, HTTPXWSException): + raise event + if isinstance(event, wsproto.events.CloseConnection): + raise WebSocketDisconnect(event.code, event.reason) + return event + + async def receive_text(self, timeout: float | None = None) -> str: + """ + Receive text from the server. + + Args: + timeout: + Number of seconds to wait for an event. + If `None`, will block until an event is available. + + Returns: + Text data. + + Raises: + TimeoutError: No event was received before the timeout delay. + WebSocketDisconnect: The server closed the websocket. + WebSocketNetworkError: A network error occured. + WebSocketInvalidTypeReceived: The received event was not a text message. + + Note: + Exceptions not caught inside the context manager will be + wrapped in an [ExceptionGroup][ExceptionGroup]. Use `except*` to catch them + outside the `async with` block. + + Examples: + Wait for text until available. + + try: + text = await ws.receive_text() + except WebSocketDisconnect: + print("Connection closed") + + Wait for text for 2 seconds. + + try: + event = await ws.receive_text(timeout=2.) + except TimeoutError: + print("No text received.") + except WebSocketDisconnect: + print("Connection closed") + """ + event = await self.receive(timeout) + if isinstance(event, wsproto.events.TextMessage): + return event.data + raise WebSocketInvalidTypeReceived(event) + + async def receive_bytes(self, timeout: float | None = None) -> bytes: + """ + Receive bytes from the server. + + Args: + timeout: + Number of seconds to wait for an event. + If `None`, will block until an event is available. + + Returns: + Bytes data. + + Raises: + TimeoutError: No event was received before the timeout delay. + WebSocketDisconnect: The server closed the websocket. + WebSocketNetworkError: A network error occured. + WebSocketInvalidTypeReceived: The received event was not a bytes message. + + Note: + Exceptions not caught inside the context manager will be + wrapped in an [ExceptionGroup][ExceptionGroup]. Use `except*` to catch them + outside the `async with` block. + + Examples: + Wait for bytes until available. + + try: + data = await ws.receive_bytes() + except WebSocketDisconnect: + print("Connection closed") + + Wait for bytes for 2 seconds. + + try: + data = await ws.receive_bytes(timeout=2.) + except TimeoutError: + print("No data received.") + except WebSocketDisconnect: + print("Connection closed") + """ + event = await self.receive(timeout) + if isinstance(event, wsproto.events.BytesMessage): + return bytes(event.data) + raise WebSocketInvalidTypeReceived(event) + + async def receive_json(self, timeout: float | None = None, mode: JSONMode = "text") -> typing.Any: + """ + Receive JSON data from the server. + + The received data should be parseable by [json.loads][json.loads]. + + Args: + timeout: + Number of seconds to wait for an event. + If `None`, will block until an event is available. + mode: + Receive mode. Should either be `'text'` or `'bytes'`. + + Returns: + Parsed JSON data. + + Raises: + TimeoutError: No event was received before the timeout delay. + WebSocketDisconnect: The server closed the websocket. + WebSocketNetworkError: A network error occured. + WebSocketInvalidTypeReceived: The received event + didn't correspond to the specified mode. + + Note: + Exceptions not caught inside the context manager will be + wrapped in an [ExceptionGroup][ExceptionGroup]. Use `except*` to catch them + outside the `async with` block. + + Examples: + Wait for data until available. + + try: + data = await ws.receive_json() + except WebSocketDisconnect: + print("Connection closed") + + Wait for data for 2 seconds. + + try: + data = await ws.receive_json(timeout=2.) + except TimeoutError: + print("No data received.") + except WebSocketDisconnect: + print("Connection closed") + """ + assert mode in ["text", "binary"] + data: str | bytes + if mode == "text": + data = await self.receive_text(timeout) + elif mode == "binary": + data = await self.receive_bytes(timeout) + return json.loads(data) + + async def close(self, code: int = 1000, reason: str | None = None) -> None: + """ + Close the WebSocket session. + + Internally, it'll send the + [CloseConnection][wsproto.events.CloseConnection] event. + + *This method is automatically called when exiting the context manager.* + + Args: + code: + The integer close code to indicate why the connection has closed. + reason: + Additional reasoning for why the connection has closed. + + Examples: + Close the WebSocket session. + + await ws.close() + """ + import httpcore2 + + self._should_close.set() + if self.connection.state not in { + wsproto.connection.ConnectionState.LOCAL_CLOSING, + wsproto.connection.ConnectionState.CLOSED, + }: + event = wsproto.events.CloseConnection(code, reason) + data = self.connection.send(event) + try: + async with self._write_lock: + await self.stream.write(data) + except httpcore2.WriteError: + pass + await self.stream.aclose() + + async def _background_receive(self, max_bytes: int) -> None: + """ + Background task listening for data from the server. + + Internally, it'll: + + * Answer to Ping events. + * Acknowledge Pong events. + * Put other events in the [_events][_events] + queue that'll eventually be consumed by the user. + + Args: + max_bytes: The maximum chunk size to read at each iteration. + """ + import httpcore2 + + partial_message_buffer: str | bytes | None = None + partial_message_size = 0 + try: + while not self._should_close.is_set(): + data = await self._read_stream(max_bytes) + self.connection.receive_data(data) + for event in self.connection.events(): + if isinstance(event, wsproto.events.Ping): + data = self.connection.send(event.response()) + async with self._write_lock: + await self.stream.write(data) + continue + if isinstance(event, wsproto.events.Pong): + self._ping_manager.ack(event.payload) + continue + if isinstance(event, wsproto.events.CloseConnection): + self._should_close.set() + if isinstance(event, wsproto.events.Message): + partial_message_size += len(event.data.encode() if isinstance(event.data, str) else event.data) + if partial_message_size > max_bytes: + await self.close(CloseReason.MESSAGE_TOO_BIG, "Message too big") + await self._send_event.send( + WebSocketDisconnect(CloseReason.MESSAGE_TOO_BIG, "Message too big") + ) + break + # Unfinished message: bufferize + if not event.message_finished: + if partial_message_buffer is None: + partial_message_buffer = event.data + else: + partial_message_buffer += event.data + # Finished message but no buffer: just emit the event + elif partial_message_buffer is None: + partial_message_size = 0 + await self._send_event.send(event) + # Finished message with buffer: emit the full event + else: + event_type = type(event) + full_message_event = event_type(partial_message_buffer + event.data) + partial_message_buffer = None + partial_message_size = 0 + await self._send_event.send(full_message_event) + continue + await self._send_event.send(event) + except (httpcore2.ReadError, httpcore2.WriteError, EndOfStream): + await self.close(CloseReason.INTERNAL_ERROR, "Stream error") + await self._send_event.send(WebSocketNetworkError()) + + async def _background_keepalive_ping(self, interval_seconds: float, timeout_seconds: float | None = None) -> None: + while not self._should_close.is_set(): + await anyio.sleep(interval_seconds) + + try: + pong_callback = await self.ping() + # Connection is closing, exit the task + except wsproto.utilities.LocalProtocolError: + return + + if timeout_seconds is not None: + try: + with anyio.fail_after(timeout_seconds): + await pong_callback.wait() + except TimeoutError: + await self.close(CloseReason.INTERNAL_ERROR, "Keepalive ping timeout") + await self._send_event.send(WebSocketNetworkError()) + + async def _read_stream(self, max_bytes: int) -> bytes: + data = await self.stream.read(max_bytes) + if data == b"": + raise EndOfStream() + return data + + +def _get_headers( + subprotocols: list[str] | None, +) -> dict[str, typing.Any]: + headers = { + "connection": "upgrade", + "upgrade": "websocket", + "sec-websocket-key": base64.b64encode(secrets.token_bytes(16)).decode("utf-8"), + "sec-websocket-version": "13", + } + if subprotocols is not None: + headers["sec-websocket-protocol"] = ", ".join(subprotocols) + return headers + + +class WebSocketClient(typing.Generic[SyncSession]): + """ + A sync WebSocket client. + + This class provides an API for connecting to WebSocket. + + Attributes: + client: + HTTPX client to use. + max_message_size_bytes: + Maximum incoming message size in bytes. + Larger messages, including fragmented messages whose + cumulative size exceeds the limit, close the connection + with `MESSAGE_TOO_BIG`. + Defaults to 65 KiB. + queue_size: + Size of the queue where the received messages will be held + until they are consumed. + If the queue is full, the client will stop receive messages + from the server until the queue has room available. + Defaults to 512. + keepalive_ping_interval_seconds: + Interval at which the client will automatically send a Ping event + to keep the connection alive. Set it to `None` to disable this mechanism. + Defaults to 20 seconds. + keepalive_ping_timeout_seconds: + Maximum delay the client will wait for an answer to its Ping event. + If the delay is exceeded, + [WebSocketNetworkError][httpx_ws.WebSocketNetworkError] + will be raised and the connection closed. + Defaults to 20 seconds. + session_class: + The session class to use. + Defaults to [WebSocketSession][httpx_ws.WebSocketSession]. + """ + + def __init__( + self, + client: Client, + *, + max_message_size_bytes: int = DEFAULT_MAX_MESSAGE_SIZE_BYTES, + queue_size: int = DEFAULT_QUEUE_SIZE, + keepalive_ping_interval_seconds: float | None = DEFAULT_KEEPALIVE_PING_INTERVAL_SECONDS, + keepalive_ping_timeout_seconds: float | None = DEFAULT_KEEPALIVE_PING_TIMEOUT_SECONDS, + session_class: type[SyncSession] = WebSocketSession, # type: ignore[assignment] + ) -> None: + self.client = client + self.max_message_size_bytes = max_message_size_bytes + self.queue_size = queue_size + self.keepalive_ping_interval_seconds = keepalive_ping_interval_seconds + self.keepalive_ping_timeout_seconds = keepalive_ping_timeout_seconds + self.session_class = session_class + + @contextlib.contextmanager + def connect( + self, + url: str, + *, + subprotocols: list[str] | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> typing.Generator[SyncSession, None, None]: + """ + Start a sync WebSocket session. + + It returns a context manager that'll automatically + call [close()][httpx_ws.WebSocketSession.close] when exiting. + + Args: + url: The WebSocket URL. + subprotocols: + Optional list of subprotocols to negotiate with the server. + params: + Query parameters to include in the handshake request. + headers: + Headers to include in the handshake request. + cookies: + Cookies to include in the handshake request. + auth: + Authentication to use for the handshake request. + follow_redirects: + Whether to follow redirects on the handshake request. + timeout: + Timeout configuration for the handshake request. + extensions: + Request extensions for the handshake request. + + Returns: + A [context manager][contextlib.AbstractContextManager] + for [WebSocketSession][httpx_ws.WebSocketSession]. + + Examples: + Initialize the client and connect to a WebSocket. + + with httpx2.Client() as client: + ws_client = WebSocketClient(client) + with ws_client.connect("http://localhost:8000/ws") as ws: + message = ws.receive_text() + print(message) + ws.send_text("Hello!") + """ + with self.client.stream( + "GET", + url, + params=params, + headers=Headers(headers) | _get_headers(subprotocols), + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) as response: + if response.status_code != 101: + raise WebSocketUpgradeError(response) + + session = self.session_class( + response.extensions["network_stream"], + max_message_size_bytes=self.max_message_size_bytes, + queue_size=self.queue_size, + keepalive_ping_interval_seconds=self.keepalive_ping_interval_seconds, + keepalive_ping_timeout_seconds=self.keepalive_ping_timeout_seconds, + response=response, + ) + with session: + yield session + + +@contextlib.contextmanager +def connect_ws( + url: str, + client: Client | None = None, + *, + max_message_size_bytes: int = DEFAULT_MAX_MESSAGE_SIZE_BYTES, + queue_size: int = DEFAULT_QUEUE_SIZE, + keepalive_ping_interval_seconds: float | None = DEFAULT_KEEPALIVE_PING_INTERVAL_SECONDS, + keepalive_ping_timeout_seconds: float | None = DEFAULT_KEEPALIVE_PING_TIMEOUT_SECONDS, + subprotocols: list[str] | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, +) -> typing.Generator[WebSocketSession, None, None]: + """ + Start a sync WebSocket session. + + It returns a context manager that'll automatically + call [close()][httpx_ws.WebSocketSession.close] when exiting. + + Args: + url: The WebSocket URL. + client: + HTTPX client to use. + If not provided, a default one will be initialized. + max_message_size_bytes: + Maximum incoming message size in bytes. + Larger messages, including fragmented messages whose + cumulative size exceeds the limit, close the connection + with `MESSAGE_TOO_BIG`. + Defaults to 65 KiB. + queue_size: + Size of the queue where the received messages will be held + until they are consumed. + If the queue is full, the client will stop receive messages + from the server until the queue has room available. + Defaults to 512. + keepalive_ping_interval_seconds: + Interval at which the client will automatically send a Ping event + to keep the connection alive. Set it to `None` to disable this mechanism. + Defaults to 20 seconds. + keepalive_ping_timeout_seconds: + Maximum delay the client will wait for an answer to its Ping event. + If the delay is exceeded, + [WebSocketNetworkError][httpx_ws.WebSocketNetworkError] + will be raised and the connection closed. + Defaults to 20 seconds. + subprotocols: + Optional list of subprotocols to negotiate with the server. + params: + Query parameters to include in the handshake request. + headers: + Headers to include in the handshake request. + cookies: + Cookies to include in the handshake request. + auth: + Authentication to use for the handshake request. + follow_redirects: + Whether to follow redirects on the handshake request. + timeout: + Timeout configuration for the handshake request. + extensions: + Request extensions for the handshake request. + + Returns: + A [context manager][contextlib.AbstractContextManager] + for [WebSocketSession][httpx_ws.WebSocketSession]. + + Examples: + Without explicit HTTPX client. + + with connect_ws("http://localhost:8000/ws") as ws: + message = ws.receive_text() + print(message) + ws.send_text("Hello!") + + With explicit HTTPX client. + + with httpx2.Client() as client: + with connect_ws("http://localhost:8000/ws", client) as ws: + message = ws.receive_text() + print(message) + ws.send_text("Hello!") + """ + if client is None: + from .._client import Client + + owned_client: contextlib.AbstractContextManager[Client] = Client() + else: + owned_client = contextlib.nullcontext(client) + + with owned_client as client: + ws_client = WebSocketClient( + client=client, + max_message_size_bytes=max_message_size_bytes, + queue_size=queue_size, + keepalive_ping_interval_seconds=keepalive_ping_interval_seconds, + keepalive_ping_timeout_seconds=keepalive_ping_timeout_seconds, + ) + with ws_client.connect( + url, + subprotocols=subprotocols, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) as websocket: + yield websocket + + +class AsyncWebSocketClient(typing.Generic[AsyncSession]): + """ + An async WebSocket client. + + This class provides an API for connecting to WebSocket. + + Attributes: + client: + HTTPX client to use. + max_message_size_bytes: + Maximum incoming message size in bytes. + Larger messages, including fragmented messages whose + cumulative size exceeds the limit, close the connection + with `MESSAGE_TOO_BIG`. + Defaults to 65 KiB. + queue_size: + Size of the queue where the received messages will be held + until they are consumed. + If the queue is full, the client will stop receive messages + from the server until the queue has room available. + Defaults to 512. + keepalive_ping_interval_seconds: + Interval at which the client will automatically send a Ping event + to keep the connection alive. Set it to `None` to disable this mechanism. + Defaults to 20 seconds. + keepalive_ping_timeout_seconds: + Maximum delay the client will wait for an answer to its Ping event. + If the delay is exceeded, + [WebSocketNetworkError][httpx_ws.WebSocketNetworkError] + will be raised in an [ExceptionGroup][ExceptionGroup] and the connection closed. + Defaults to 20 seconds. + session_class: + The session class to use. + Defaults to [AsyncWebSocketSession][httpx_ws.AsyncWebSocketSession]. + """ + + def __init__( + self, + client: AsyncClient, + *, + max_message_size_bytes: int = DEFAULT_MAX_MESSAGE_SIZE_BYTES, + queue_size: int = DEFAULT_QUEUE_SIZE, + keepalive_ping_interval_seconds: float | None = DEFAULT_KEEPALIVE_PING_INTERVAL_SECONDS, + keepalive_ping_timeout_seconds: float | None = DEFAULT_KEEPALIVE_PING_TIMEOUT_SECONDS, + session_class: type[AsyncSession] = AsyncWebSocketSession, # type: ignore[assignment] + ) -> None: + self.client = client + self.max_message_size_bytes = max_message_size_bytes + self.queue_size = queue_size + self.keepalive_ping_interval_seconds = keepalive_ping_interval_seconds + self.keepalive_ping_timeout_seconds = keepalive_ping_timeout_seconds + self.session_class = session_class + + @contextlib.asynccontextmanager + async def connect( + self, + url: str, + *, + subprotocols: list[str] | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> typing.AsyncGenerator[AsyncSession, None]: + """ + Start an async WebSocket session. + + It returns an async context manager that'll automatically + call [close()][httpx_ws.AsyncWebSocketSession.close] when exiting. + + Args: + url: The WebSocket URL. + subprotocols: + Optional list of subprotocols to negotiate with the server. + params: + Query parameters to include in the handshake request. + headers: + Headers to include in the handshake request. + cookies: + Cookies to include in the handshake request. + auth: + Authentication to use for the handshake request. + follow_redirects: + Whether to follow redirects on the handshake request. + timeout: + Timeout configuration for the handshake request. + extensions: + Request extensions for the handshake request. + + Returns: + An [async context manager][contextlib.AbstractAsyncContextManager] + for [AsyncWebSocketSession][httpx_ws.AsyncWebSocketSession]. + + Examples: + Initialize the client and connect to a WebSocket. + + async with httpx2.AsyncClient() as client: + ws_client = AsyncWebSocketClient(client) + async with ws_client.connect("http://localhost:8000/ws") as ws: + message = await ws.receive_text() + print(message) + await ws.send_text("Hello!") + """ + async with self.client.stream( + "GET", + url, + params=params, + headers=Headers(headers) | _get_headers(subprotocols), + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) as response: + if response.status_code != 101: + raise WebSocketUpgradeError(response) + + session = self.session_class( + response.extensions["network_stream"], + max_message_size_bytes=self.max_message_size_bytes, + queue_size=self.queue_size, + keepalive_ping_interval_seconds=self.keepalive_ping_interval_seconds, + keepalive_ping_timeout_seconds=self.keepalive_ping_timeout_seconds, + response=response, + ) + async with session: + yield session + + +@contextlib.asynccontextmanager +async def aconnect_ws( + url: str, + client: AsyncClient | None = None, + *, + max_message_size_bytes: int = DEFAULT_MAX_MESSAGE_SIZE_BYTES, + queue_size: int = DEFAULT_QUEUE_SIZE, + keepalive_ping_interval_seconds: float | None = DEFAULT_KEEPALIVE_PING_INTERVAL_SECONDS, + keepalive_ping_timeout_seconds: float | None = DEFAULT_KEEPALIVE_PING_TIMEOUT_SECONDS, + subprotocols: list[str] | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, +) -> typing.AsyncGenerator[AsyncWebSocketSession, None]: + """ + Start an async WebSocket session. + + It returns an async context manager that'll automatically + call [close()][httpx_ws.AsyncWebSocketSession.close] when exiting. + + Args: + url: The WebSocket URL. + client: + HTTPX client to use. + If not provided, a default one will be initialized. + max_message_size_bytes: + Maximum incoming message size in bytes. + Larger messages, including fragmented messages whose + cumulative size exceeds the limit, close the connection + with `MESSAGE_TOO_BIG`. + Defaults to 65 KiB. + queue_size: + Size of the queue where the received messages will be held + until they are consumed. + If the queue is full, the client will stop receive messages + from the server until the queue has room available. + Defaults to 512. + keepalive_ping_interval_seconds: + Interval at which the client will automatically send a Ping event + to keep the connection alive. Set it to `None` to disable this mechanism. + Defaults to 20 seconds. + keepalive_ping_timeout_seconds: + Maximum delay the client will wait for an answer to its Ping event. + If the delay is exceeded, + [WebSocketNetworkError][httpx_ws.WebSocketNetworkError] + will be raised in an [ExceptionGroup][ExceptionGroup] and the connection closed. + Defaults to 20 seconds. + subprotocols: + Optional list of subprotocols to negotiate with the server. + params: + Query parameters to include in the handshake request. + headers: + Headers to include in the handshake request. + cookies: + Cookies to include in the handshake request. + auth: + Authentication to use for the handshake request. + follow_redirects: + Whether to follow redirects on the handshake request. + timeout: + Timeout configuration for the handshake request. + extensions: + Request extensions for the handshake request. + + Returns: + An [async context manager][contextlib.AbstractAsyncContextManager] + for [AsyncWebSocketSession][httpx_ws.AsyncWebSocketSession]. + + Examples: + Without explicit HTTPX client. + + async with aconnect_ws("http://localhost:8000/ws") as ws: + message = await ws.receive_text() + print(message) + await ws.send_text("Hello!") + + With explicit HTTPX client. + + async with httpx2.AsyncClient() as client: + async with aconnect_ws("http://localhost:8000/ws", client) as ws: + message = await ws.receive_text() + print(message) + await ws.send_text("Hello!") + """ + if client is None: + from .._client import AsyncClient + + owned_client: contextlib.AbstractAsyncContextManager[AsyncClient] = AsyncClient() + else: + owned_client = contextlib.nullcontext(client) + + async with owned_client as client: + ws_client = AsyncWebSocketClient( + client=client, + max_message_size_bytes=max_message_size_bytes, + queue_size=queue_size, + keepalive_ping_interval_seconds=keepalive_ping_interval_seconds, + keepalive_ping_timeout_seconds=keepalive_ping_timeout_seconds, + ) + async with ws_client.connect( + url, + subprotocols=subprotocols, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) as websocket: + yield websocket diff --git a/venv/lib/python3.12/site-packages/httpx2/websockets/_exceptions.py b/venv/lib/python3.12/site-packages/httpx2/websockets/_exceptions.py new file mode 100644 index 0000000..df3a500 --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/websockets/_exceptions.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +import typing + +if typing.TYPE_CHECKING: + import wsproto + + from .._models import Response + + +class HTTPXWSException(Exception): + """ + Base exception class for HTTPX WS. + """ + + +class WebSocketUpgradeError(HTTPXWSException): + """ + Raised when the initial connection didn't correctly upgrade to a WebSocket session. + """ + + def __init__(self, response: Response) -> None: + self.response = response + + +class WebSocketDisconnect(HTTPXWSException): + """ + Raised when the server closed the WebSocket session. + + Args: + code: + The integer close code to indicate why the connection has closed. + reason: + Additional reasoning for why the connection has closed. + """ + + def __init__(self, code: int = 1000, reason: str | None = None) -> None: + self.code = code + self.reason = reason or "" + + +class WebSocketInvalidTypeReceived(HTTPXWSException): + """ + Raised when an event is not of the expected type. + """ + + def __init__(self, event: wsproto.events.Event) -> None: + self.event = event + + +class WebSocketNetworkError(HTTPXWSException): + """ + Raised when a network error occured, + typically if the underlying stream has closed or timeout. + """ diff --git a/venv/lib/python3.12/site-packages/httpx2/websockets/_ping.py b/venv/lib/python3.12/site-packages/httpx2/websockets/_ping.py new file mode 100644 index 0000000..73aff9c --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/websockets/_ping.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +import secrets +import threading + +import anyio + + +class PingManagerBase: + def _generate_id(self) -> bytes: + return secrets.token_bytes() + + +class PingManager(PingManagerBase): + def __init__(self) -> None: + self._pings: dict[bytes, threading.Event] = {} + + def create(self, ping_id: bytes | None = None) -> tuple[bytes, threading.Event]: + ping_id = self._generate_id() if not ping_id else ping_id + event = threading.Event() + self._pings[ping_id] = event + return ping_id, event + + def ack(self, ping_id: bytes | bytearray) -> None: + event = self._pings.pop(bytes(ping_id), None) + if event is not None: + event.set() + + +class AsyncPingManager(PingManagerBase): + def __init__(self) -> None: + self._pings: dict[bytes, anyio.Event] = {} + + def create(self, ping_id: bytes | None = None) -> tuple[bytes, anyio.Event]: + ping_id = self._generate_id() if not ping_id else ping_id + event = anyio.Event() + self._pings[ping_id] = event + return ping_id, event + + def ack(self, ping_id: bytes | bytearray) -> None: + event = self._pings.pop(bytes(ping_id), None) + if event is not None: + event.set() diff --git a/venv/lib/python3.12/site-packages/httpx2/websockets/_transport.py b/venv/lib/python3.12/site-packages/httpx2/websockets/_transport.py new file mode 100644 index 0000000..1a1df5b --- /dev/null +++ b/venv/lib/python3.12/site-packages/httpx2/websockets/_transport.py @@ -0,0 +1,282 @@ +from __future__ import annotations + +import contextlib +import math +import typing +from types import TracebackType + +import anyio +import wsproto +from anyio.streams.stapled import StapledObjectStream +from wsproto.frame_protocol import CloseReason + +from .._models import Request, Response +from .._transports.asgi import ASGITransport, _ASGIApp +from .._types import AsyncByteStream +from ._exceptions import WebSocketDisconnect, WebSocketUpgradeError + +Scope = dict[str, typing.Any] +Message = dict[str, typing.Any] +Receive = typing.Callable[[], typing.Awaitable[Message]] +Send = typing.Callable[[Scope], typing.Coroutine[None, None, None]] +ASGIApp = typing.Callable[[Scope, Receive, Send], typing.Coroutine[None, None, None]] + + +class ASGIWebSocketTransportError(Exception): + pass + + +class UnhandledASGIMessageType(ASGIWebSocketTransportError): + def __init__(self, message: Message) -> None: + self.message = message + + +class UnhandledWebSocketEvent(ASGIWebSocketTransportError): + def __init__(self, event: wsproto.events.Event) -> None: + self.event = event + + +class ASGIWebSocketAsyncNetworkStream: + def __init__( + self, + app: ASGIApp, + scope: Scope, + task_group: anyio.abc.TaskGroup, + initial_receive_timeout: float = 1.0, + ) -> None: + self.app = app + self.scope = scope + self._receive_queue = StapledObjectStream(*anyio.create_memory_object_stream[Message](max_buffer_size=math.inf)) + self._send_queue = StapledObjectStream(*anyio.create_memory_object_stream[Message](max_buffer_size=math.inf)) + self._task_group = task_group + self._initial_receive_timeout = initial_receive_timeout + self.connection = wsproto.WSConnection(wsproto.ConnectionType.SERVER) + self.connection.initiate_upgrade_connection(scope["headers"], scope["path"]) + self._aentered = False + + async def __aenter__( + self, + ) -> tuple[ASGIWebSocketAsyncNetworkStream, bytes]: + if self._aentered: + raise RuntimeError("Cannot use ASGIWebSocketAsyncNetworkStream in a context manager twice") + self._aentered = True + self._task_group.start_soon(self._run) + async with contextlib.AsyncExitStack() as stack: + stack.push_async_callback(self.aclose) + await self.send({"type": "websocket.connect"}) + + try: + message = await self.receive(self._initial_receive_timeout) + except TimeoutError as e: + raise RuntimeError( + "WebSocket didn't accept the connection in time. Did you forget to call accept()?" + ) from e + + if message["type"] == "websocket.close": + await stack.aclose() + raise WebSocketDisconnect(message["code"], message.get("reason")) + + # Websocket Denial Response extension + # Ref: https://asgi.readthedocs.io/en/latest/extensions.html#websocket-denial-response + if message["type"] == "websocket.http.response.start": + status_code: int = message["status"] + headers: list[tuple[bytes, bytes]] = message["headers"] + body: list[bytes] = [] + while True: + message = await self.receive() + assert message["type"] == "websocket.http.response.body" + body.append(message["body"]) + if not message.get("more_body", False): + break + + await stack.aclose() + raise WebSocketUpgradeError(Response(status_code, headers=headers, content=b"".join(body))) + + assert message["type"] == "websocket.accept" + retval = self, self._build_accept_response(message) + self._exit_stack = stack.pop_all() + return retval + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + return await self._exit_stack.__aexit__(exc_type, exc_val, exc_tb) + + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + message: Message = await self.receive(timeout=timeout) + type = message["type"] + + if type not in {"websocket.send", "websocket.close"}: + raise UnhandledASGIMessageType(message) + + event: wsproto.events.Event + if type == "websocket.send": + data_str: str | None = message.get("text") + if data_str is not None: + event = wsproto.events.TextMessage(data_str) + data_bytes: bytes | None = message.get("bytes") + if data_bytes is not None: + event = wsproto.events.BytesMessage(data_bytes) + elif type == "websocket.close": + event = wsproto.events.CloseConnection(message["code"], message["reason"]) + + return self.connection.send(event) + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + self.connection.receive_data(buffer) + for event in self.connection.events(): + if isinstance(event, wsproto.events.Request): + pass + elif isinstance(event, wsproto.events.CloseConnection): + await self.send( + { + "type": "websocket.disconnect", + "code": event.code, + "reason": event.reason, + } + ) + elif isinstance(event, wsproto.events.TextMessage): + await self.send({"type": "websocket.receive", "text": event.data}) + elif isinstance(event, wsproto.events.BytesMessage): + await self.send({"type": "websocket.receive", "bytes": event.data}) + else: + raise UnhandledWebSocketEvent(event) + + async def aclose(self) -> None: + with contextlib.suppress(anyio.ClosedResourceError): + await self.send({"type": "websocket.disconnect"}) + await self._receive_queue.aclose() + await self._send_queue.aclose() + + async def send(self, message: Message) -> None: + await self._receive_queue.send(message) + + async def receive(self, timeout: float | None = None) -> Message: + if timeout is None: + timeout = math.inf + with anyio.fail_after(timeout): + return await self._send_queue.receive() + + async def _run(self) -> None: + """ + The sub-thread in which the websocket session runs. + """ + scope = self.scope + receive = self._receive_queue.receive + send = self._send_queue.send + try: + await self.app(scope, receive, send) + except Exception as e: + message = { + "type": "websocket.close", + "code": CloseReason.INTERNAL_ERROR, + "reason": str(e), + } + with contextlib.suppress(anyio.ClosedResourceError): + await send(message) + + def _build_accept_response(self, message: Message) -> bytes: + subprotocol = message.get("subprotocol", None) + headers = message.get("headers", []) + return self.connection.send( + wsproto.events.AcceptConnection( + subprotocol=subprotocol, + extra_headers=headers, + ) + ) + + +class ASGIWebSocketTransport(ASGITransport): + def __init__( + self, + app: _ASGIApp, + raise_app_exceptions: bool = True, + root_path: str = "", + client: tuple[str, int] = ("127.0.0.1", 123), + initial_receive_timeout: float = 1.0, + ) -> None: + super().__init__(app, raise_app_exceptions, root_path, client) + self._exit_stack: contextlib.AsyncExitStack | None = None + self._initial_receive_timeout = initial_receive_timeout + + async def __aenter__(self) -> ASGIWebSocketTransport: + async with contextlib.AsyncExitStack() as stack: + self._task_group = await stack.enter_async_context(anyio.create_task_group()) + self._exit_stack = stack.pop_all() + + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_val: BaseException | None = None, + exc_tb: TracebackType | None = None, + ) -> None: + await super().__aexit__(exc_type, exc_val, exc_tb) + assert self._exit_stack is not None + await self._exit_stack.__aexit__(exc_type, exc_val, exc_tb) + + async def handle_async_request(self, request: Request) -> Response: + scheme = request.url.scheme + headers = request.headers + + if scheme in {"ws", "wss"} or headers.get("upgrade") == "websocket": + subprotocols: list[str] = [] + if (subprotocols_header := headers.get("sec-websocket-protocol")) is not None: + subprotocols = subprotocols_header.split(",") + + scope = { + "type": "websocket", + "path": request.url.path, + "raw_path": request.url.raw_path, + "root_path": self.root_path, + "scheme": scheme, + "query_string": request.url.query, + "headers": [(k.lower(), v) for (k, v) in request.headers.raw], + "client": self.client, + "server": (request.url.host, request.url.port), + "subprotocols": subprotocols, + } + return await self._handle_ws_request(request, scope) + + return await super().handle_async_request(request) + + async def _create_asgi_websocket_async_network_stream( + self, + *, + task_status: anyio.abc.TaskStatus[tuple[ASGIWebSocketAsyncNetworkStream, bytes]], + ) -> None: + stream = ASGIWebSocketAsyncNetworkStream( + self.app, # type: ignore[arg-type] + self.scope, + self._task_group, + self._initial_receive_timeout, + ) + assert self._exit_stack is not None + result = await self._exit_stack.enter_async_context(stream) + task_status.started(result) + + async def _handle_ws_request( + self, + request: Request, + scope: Scope, + ) -> Response: + assert isinstance(request.stream, AsyncByteStream) + + self.scope = scope + stream, accept_response = await self._task_group.start(self._create_asgi_websocket_async_network_stream) + accept_response_lines = accept_response.decode("utf-8").splitlines() + headers = [ + typing.cast(tuple[str, str], line.split(": ", 1)) + for line in accept_response_lines[1:] + if line.strip() != "" + ] + + return Response( + status_code=101, + headers=headers, + extensions={"network_stream": stream}, + ) diff --git a/venv/lib/python3.12/site-packages/idna-3.20.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/idna-3.20.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna-3.20.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/idna-3.20.dist-info/METADATA b/venv/lib/python3.12/site-packages/idna-3.20.dist-info/METADATA new file mode 100644 index 0000000..edbc446 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna-3.20.dist-info/METADATA @@ -0,0 +1,176 @@ +Metadata-Version: 2.5 +Name: idna +Version: 3.20 +Summary: Internationalized Domain Names in Applications (IDNA) +Author-email: Kim Davies +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +License-Expression: BSD-3-Clause +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: 3.15 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: Name Service (DNS) +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Utilities +License-File: LICENSE.md +Requires-Dist: ruff >= 0.16.0 ; extra == "all" +Requires-Dist: mypy >= 1.11.2 ; extra == "all" +Requires-Dist: ty >= 0.0.37 ; extra == "all" +Requires-Dist: pytest >= 8.3.2 ; extra == "all" +Requires-Dist: hypothesis >= 6.141.1 ; extra == "all" +Requires-Dist: coverage >= 7.10.0 ; extra == "all" +Project-URL: Changelog, https://github.com/kjd/idna/blob/master/HISTORY.md +Project-URL: Issue tracker, https://github.com/kjd/idna/issues +Project-URL: Source, https://github.com/kjd/idna +Provides-Extra: all +Import-Name: idna + +# Internationalized Domain Names in Applications (IDNA) + +Support for [Internationalized Domain Names in Applications +(IDNA)](https://tools.ietf.org/html/rfc5891) and [Unicode IDNA +Compatibility Processing](https://unicode.org/reports/tr46/). It +supersedes the standard library's `encodings.idna`, which only +implements the 2003 specification, offering broader script coverage and +limiting domains with known security vulnerabilities. + +## Usage + +Package may be installed from [PyPI](https://pypi.org/project/idna/) via +the typical methods (e.g. `python3 -m pip install idna`) + +For typical usage, the `encode` and `decode` functions will take a +domain name argument and perform a conversion to ASCII-compatible encoding +(known as A-labels), or to Unicode strings (known as U-labels) +respectively. + +```pycon +>>> import idna +>>> idna.encode('ドメイン.テスト') +b'xn--eckwd4c7c.xn--zckzah' +>>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) +ドメイン.テスト +``` + +Conversions can be applied at a per-label basis using the `ulabel` or +`alabel` functions for specialized use cases. + + +### Compatibility Mapping (UTS #46) + +This library provides support for [Unicode IDNA Compatibility +Processing](https://unicode.org/reports/tr46/) which normalizes input from +different potential ways a user may input a domain prior to performing the IDNA +conversion operations. This functionality, known as a +[mapping](https://tools.ietf.org/html/rfc5895), is considered by the +specification to be a local user-interface issue distinct from IDNA +conversion functionality. + +For example, "Königsgäßchen" is not a permissible label as capital letters +are not allowed. UTS #46 will convert this into lower case prior to applying +the IDNA conversion. + +```pycon +>>> import idna +>>> idna.encode('Königsgäßchen') +... +idna.core.InvalidCodepoint: Codepoint U+004B at position 1 of 'Königsgäßchen' not allowed +>>> idna.encode('Königsgäßchen', uts46=True) +b'xn--knigsgchen-b4a3dun' +>>> idna.decode('xn--knigsgchen-b4a3dun') +'königsgäßchen' +``` + +When performing a decode operation for display purposes, `decode()` +accepts a `display=True` argument that leaves any `xn--` label that +fails to decode unchanged. This is useful for user interface display +where a domain is in use, the A-label form can be presented when it +is not a valid IDN. + + +## Exceptions + +All errors raised during conversion derive from the `idna.IDNAError` +base class. The more specific exceptions are: + +* `idna.IDNABidiError` — raised when a label contains an illegal + combination of left-to-right and right-to-left characters. +* `idna.InvalidCodepoint` — raised when a label contains a codepoint + that is INVALID for IDNA. +* `idna.InvalidCodepointContext` — raised when a CONTEXTO or CONTEXTJ + codepoint appears in a position whose contextual requirements are + not satisfied. + +Exceptions carry machine-readable attributes so that applications do +not need to parse the message: `code` is a short, stable identifier +for the rule that failed (such as `disallowed_codepoint` or +`label_too_long`); and, when the failure can be attributed to a +particular character, `text` (the label or domain being validated), +`codepoint` (the offending codepoint as an integer) and `position` +are set. + + +## Command-line tool + +The package supports command-line usage to convert domain names +between their Unicode and ASCII-compatible forms. It can be run either +as a module (`python3 -m idna`) or, once installed (such as with `uv +tool` or `pipx`), via the `idna` script: + +```bash +$ uv tool install idna +$ idna xn--e1afmkfd.xn--p1ai +пример.рф +$ idna пример.рф +xn--e1afmkfd.xn--p1ai +``` + +Mode can be specified with `-e`/`--encode` or `-d`/`--decode`, otherwise +it will be chosen automatically based on the first input. Multiple +domains can be supplied either as arguments or through standard input. +UTS #46 mapping is applied by default, which lets the tool accept +inputs that aren't strictly valid IDNA 2008 by normalising them first, +pass `--strict` to disable UTS #46. + +Conversion failures are reported on stderr together with the +offending input; processing continues with the remaining domains and +the tool exits with a non-zero status if any conversion failed. + + +## Additional Notes + +* **Python version support**. This library supports Python 3.9 and higher. + As this library serves as a low-level toolkit for a variety of + applications, we strive to support all versions of Python that are + not beyond end-of-life. Free-threaded Python is also supported, + as the library holds no mutable global state the functions can be + called concurrently from multiple threads. + +* **Unicode version**. The IDNA and UTS #46 lookup tables are generated + from a specific Unicode release. Some Unicode data depends on the + running Python's `unicodedata` module, so on an older Python a + character new to Unicode may be rejected as unknown even if this + library knows about it. + +* **Emoji**. It is an occasional request to support emoji domains in + this library. Encoding of symbols like emoji is expressly prohibited by + the IDNA technical standard, and emoji domains are broadly phased + out across the domain industry due to associated security risks. + +* **Regenerating lookup tables**. The IDNA and UTS #46 functionality + relies upon pre-calculated lookup tables, generated using the + `idna-data` script in [`tools/`](tools/README.md). + diff --git a/venv/lib/python3.12/site-packages/idna-3.20.dist-info/RECORD b/venv/lib/python3.12/site-packages/idna-3.20.dist-info/RECORD new file mode 100644 index 0000000..5894328 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna-3.20.dist-info/RECORD @@ -0,0 +1,28 @@ +../../../bin/idna,sha256=NGQUFIXBsVozHrDo16PLDFJSRda4HoG69_47xxFtSWg,180 +idna-3.20.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +idna-3.20.dist-info/METADATA,sha256=29jBTB5MoeDJgkptvHy794iE8461LZEUjNMCX2ceS4U,7207 +idna-3.20.dist-info/RECORD,, +idna-3.20.dist-info/WHEEL,sha256=lqN8jXt_QloFOGcx9kStvWbryhfEXoI2F2z-YURm350,81 +idna-3.20.dist-info/entry_points.txt,sha256=7H3nGOHap3jnLE5e7q7Ywr9Vq8axB7WIj5-C_4N2vhw,38 +idna-3.20.dist-info/licenses/LICENSE.md,sha256=GppPDj1HmickDd1ZqRN6ZqtKD539yMphiMwL_YUYfwQ,1541 +idna/__init__.py,sha256=hRTD7VMTajWW699RL6SHu919pama3K7YLgNj0sMG068,944 +idna/__main__.py,sha256=4JMK66Wj4uLZTKbF-sT3LAxOsr6buig77PmOkJCRRxw,83 +idna/__pycache__/__init__.cpython-312.pyc,, +idna/__pycache__/__main__.cpython-312.pyc,, +idna/__pycache__/cli.cpython-312.pyc,, +idna/__pycache__/codec.cpython-312.pyc,, +idna/__pycache__/compat.cpython-312.pyc,, +idna/__pycache__/core.cpython-312.pyc,, +idna/__pycache__/idnadata.cpython-312.pyc,, +idna/__pycache__/intranges.cpython-312.pyc,, +idna/__pycache__/package_data.cpython-312.pyc,, +idna/__pycache__/uts46data.cpython-312.pyc,, +idna/cli.py,sha256=LgGXKiXmev37pdgkATwB6AiJORZTnA2zP3LzfEK4iWo,4245 +idna/codec.py,sha256=ff9CR9debiZcvqEmHxDRIrqSmaNxQpz6AY4JHsC3YC0,8002 +idna/compat.py,sha256=T_eZXOUt0b5Tga56cPybQu9CCYK0FTK1mdiPb96na8A,1376 +idna/core.py,sha256=PdTD_-5P8iMgf6xl84EqHmiVrorR56btYBFXaPNVFSA,32580 +idna/idnadata.py,sha256=I6mowdFd6cY00L8hPXeYEPSFCD_hsPfgkEStBKacx-s,45763 +idna/intranges.py,sha256=g49scLSkqJtAhLmOODa7hVHriSjmb60tiTsEoocJdBI,1851 +idna/package_data.py,sha256=rnWVNKPFgqAKyFbPykZKOe6CUdOTII2K8GtgW8IOAqk,21 +idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +idna/uts46data.py,sha256=dw6Em_oVbHGCiolEClf-mupwc18QOuRhbuT2guE9bAA,237174 diff --git a/venv/lib/python3.12/site-packages/idna-3.20.dist-info/WHEEL b/venv/lib/python3.12/site-packages/idna-3.20.dist-info/WHEEL new file mode 100644 index 0000000..1ab9a08 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna-3.20.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 4.1.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/idna-3.20.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/idna-3.20.dist-info/entry_points.txt new file mode 100644 index 0000000..59ca7ac --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna-3.20.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +idna=idna.cli:main + diff --git a/venv/lib/python3.12/site-packages/idna-3.20.dist-info/licenses/LICENSE.md b/venv/lib/python3.12/site-packages/idna-3.20.dist-info/licenses/LICENSE.md new file mode 100644 index 0000000..f706835 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna-3.20.dist-info/licenses/LICENSE.md @@ -0,0 +1,31 @@ +BSD 3-Clause License + +Copyright (c) 2013-2026, Kim Davies and contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/idna/__init__.py b/venv/lib/python3.12/site-packages/idna/__init__.py new file mode 100644 index 0000000..d6c890a --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/__init__.py @@ -0,0 +1,47 @@ +from .core import ( + IDNABidiError, + IDNAError, + InvalidCodepoint, + InvalidCodepointContext, + alabel, + check_bidi, + check_hyphen_ok, + check_initial_combiner, + check_label, + check_nfc, + decode, + encode, + ulabel, + uts46_remap, + valid_contextj, + valid_contexto, + valid_label_length, + valid_string_length, +) +from .idnadata import __version__ as unicode_version +from .intranges import intranges_contain +from .package_data import __version__ + +__all__ = [ + "__version__", + "unicode_version", + "IDNABidiError", + "IDNAError", + "InvalidCodepoint", + "InvalidCodepointContext", + "alabel", + "check_bidi", + "check_hyphen_ok", + "check_initial_combiner", + "check_label", + "check_nfc", + "decode", + "encode", + "intranges_contain", + "ulabel", + "uts46_remap", + "valid_contextj", + "valid_contexto", + "valid_label_length", + "valid_string_length", +] diff --git a/venv/lib/python3.12/site-packages/idna/__main__.py b/venv/lib/python3.12/site-packages/idna/__main__.py new file mode 100644 index 0000000..dbdd066 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/__main__.py @@ -0,0 +1,6 @@ +import sys + +from .cli import main + +if __name__ == "__main__": + sys.exit(main()) diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..69ef460 Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/__main__.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000..eba3e7c Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/__main__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/cli.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/cli.cpython-312.pyc new file mode 100644 index 0000000..6ff574f Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/cli.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/codec.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/codec.cpython-312.pyc new file mode 100644 index 0000000..97e8eed Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/codec.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/compat.cpython-312.pyc new file mode 100644 index 0000000..d31bc0b Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/compat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/core.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/core.cpython-312.pyc new file mode 100644 index 0000000..bba99c8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/core.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/idnadata.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/idnadata.cpython-312.pyc new file mode 100644 index 0000000..b80c448 Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/idnadata.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/intranges.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/intranges.cpython-312.pyc new file mode 100644 index 0000000..d3d5e4c Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/intranges.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/package_data.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/package_data.cpython-312.pyc new file mode 100644 index 0000000..29d8783 Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/package_data.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/uts46data.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/uts46data.cpython-312.pyc new file mode 100644 index 0000000..93af471 Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/uts46data.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/cli.py b/venv/lib/python3.12/site-packages/idna/cli.py new file mode 100644 index 0000000..894d8a0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/cli.py @@ -0,0 +1,132 @@ +"""Command-line interface for the :mod:`idna` package. + +Invoked via ``python -m idna``. See :func:`main` for the entry point. +""" + +from __future__ import annotations + +import argparse +import sys +from itertools import chain +from typing import IO, TYPE_CHECKING + +from . import IDNAError, decode, encode, unicode_version +from .core import _alabel_prefix, _unicode_dots_re +from .package_data import __version__ + +if TYPE_CHECKING: + from collections.abc import Iterable + + +def _looks_like_alabel(s: str) -> bool: + """Return True if any label in ``s`` carries the ``xn--`` ACE prefix.""" + prefix = _alabel_prefix.decode("ascii") + return any(label.lower().startswith(prefix) for label in _unicode_dots_re.split(s)) + + +def _build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + prog="python -m idna", + description=( + "Convert a domain name between its Unicode (U-label) and " + "ASCII-compatible (A-label) forms. With no mode flag, the " + "direction is chosen from the first input — if it contains " + "an xn-- label the stream is decoded, otherwise it is " + "encoded — and the same mode is applied to every remaining " + "input. UTS #46 mapping is applied by default; pass " + "--strict to disable it. When no domains are given on the " + "command line and stdin is piped, one domain per line is " + "read from stdin." + ), + ) + mode = parser.add_mutually_exclusive_group() + mode.add_argument( + "-e", + "--encode", + dest="mode", + action="store_const", + const="encode", + help="Encode the input to its ASCII A-label form.", + ) + mode.add_argument( + "-d", + "--decode", + dest="mode", + action="store_const", + const="decode", + help="Decode the input from its ASCII A-label form.", + ) + parser.add_argument( + "--strict", + action="store_true", + help="Disable the default UTS #46 mapping and apply IDNA 2008 rules verbatim.", + ) + parser.add_argument( + "--version", + action="version", + version=f"idna {__version__} (Unicode {unicode_version})", + ) + parser.add_argument( + "domain", + nargs="*", + help="One or more domain names to convert. Omit to read from stdin.", + ) + return parser + + +def _iter_stdin(stream: IO[str]) -> Iterable[str]: + """Yield non-empty stripped lines from ``stream``, ignoring blanks.""" + for line in stream: + stripped = line.strip() + if stripped: + yield stripped + + +def _convert_one(domain: str, mode: str, uts46: bool) -> bool: + """Convert ``domain`` and write the result; return ``False`` on failure.""" + try: + if mode == "decode": + print(decode(domain, uts46=uts46)) + else: + print(encode(domain, uts46=uts46).decode("ascii")) + except IDNAError as err: + print(f"idna: {mode} failed for {domain!r}: {err}", file=sys.stderr) + return False + return True + + +def main(argv: list[str] | None = None) -> int: + """Entry point for ``python -m idna``. + + When more than one domain is supplied (via positional arguments or + piped stdin) and no mode flag is given, the first input determines + the direction and that mode is applied uniformly to the rest. + + :param argv: Argument list excluding the program name. Defaults to + :data:`sys.argv` when ``None``. + :returns: ``0`` on success, ``1`` if any conversion fails. + """ + parser = _build_parser() + args = parser.parse_args(argv) + uts46 = not args.strict + + if args.domain: + domains: Iterable[str] = args.domain + elif not sys.stdin.isatty(): + domains = _iter_stdin(sys.stdin) + else: + parser.error("a domain argument is required when stdin is a terminal") + + iterator = iter(domains) + first = next(iterator, None) + if first is None: + return 0 + + mode = args.mode or ("decode" if _looks_like_alabel(first) else "encode") + + results = [_convert_one(domain, mode, uts46) for domain in chain([first], iterator)] + return 0 if all(results) else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/venv/lib/python3.12/site-packages/idna/codec.py b/venv/lib/python3.12/site-packages/idna/codec.py new file mode 100644 index 0000000..8efc1ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/codec.py @@ -0,0 +1,225 @@ +from __future__ import annotations + +import codecs +from typing import Any + +from .core import IDNAError, _max_domain_length, _unicode_dots_re, alabel, decode, encode, ulabel + + +class Codec(codecs.Codec): + """Stateless IDNA 2008 codec. + + Implements the :class:`codecs.Codec` protocol so that the whole-domain + encoder (:func:`idna.encode`) and decoder (:func:`idna.decode`) are + accessible through the standard codec machinery as ``"idna2008"``. + + Only the ``"strict"`` error handler is supported; any other handler + raises :exc:`~idna.IDNAError`. + """ + + def encode(self, data: str, errors: str = "strict") -> tuple[bytes, int]: # ty: ignore[invalid-method-override] + if errors != "strict": + raise IDNAError(f'Unsupported error handling "{errors}"', code="unsupported_errors") + + if not data: + return b"", 0 + + return encode(data), len(data) + + def decode(self, data: bytes, errors: str = "strict") -> tuple[str, int]: # ty: ignore[invalid-method-override] + if errors != "strict": + raise IDNAError(f'Unsupported error handling "{errors}"', code="unsupported_errors") + + if not data: + return "", 0 + + return decode(data), len(data) + + +class IncrementalEncoder(codecs.BufferedIncrementalEncoder): + """Incremental IDNA 2008 encoder. + + Buffers a partial trailing label across calls until either the next + label separator is seen or ``final=True``, so that streamed input is + encoded one whole label at a time. Any of the four Unicode label + separators (``U+002E``, ``U+3002``, ``U+FF0E``, ``U+FF61``) ends a + label; the result always uses ``U+002E`` as the separator. + + The 253-octet domain length limit (254 with a trailing dot) that + :func:`idna.encode` enforces is applied to the accumulated output, so + that streaming a name and encoding it in one shot either both succeed + with the same result or both raise :exc:`~idna.IDNAError`. + + Only the ``"strict"`` error handler is supported. + """ + + def __init__(self, errors: str = "strict") -> None: + super().__init__(errors) + self._emitted = 0 # octets returned so far + self._trailing_dot = False # whether the output so far ends with "." + + def reset(self) -> None: + super().reset() + self._emitted = 0 + self._trailing_dot = False + + def getstate(self) -> Any: + if not self.buffer and not self._emitted: + return 0 + return (self.buffer, self._emitted, self._trailing_dot) + + def setstate(self, state: Any) -> None: + if state: + self.buffer, self._emitted, self._trailing_dot = state + else: + self.reset() + + def _buffer_encode(self, data: str, errors: str, final: bool) -> tuple[bytes, int]: # ty: ignore[invalid-method-override] + if errors != "strict": + raise IDNAError(f'Unsupported error handling "{errors}"', code="unsupported_errors") + + result_bytes = b"" + size = 0 + if data: + labels = _unicode_dots_re.split(data) + trailing_dot = b"" + if labels: + if not labels[-1]: + trailing_dot = b"." + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = b"." + + result = [] + for label in labels: + result.append(alabel(label)) + if size: + size += 1 + size += len(label) + + result_bytes = b".".join(result) + trailing_dot + size += len(trailing_dot) + + # Mirror encode(): the whole name may not exceed 253 octets, or 254 + # when it ends with a dot. Until the input is final a trailing dot + # may still arrive, so only the 254-octet ceiling applies before then. + self._emitted += len(result_bytes) + if result_bytes: + self._trailing_dot = result_bytes.endswith(b".") + may_end_with_dot = self._trailing_dot or not final + if self._emitted > _max_domain_length + may_end_with_dot: + raise IDNAError("Domain too long", code="domain_too_long") + return result_bytes, size + + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + """Incremental IDNA 2008 decoder. + + Buffers a partial trailing label across calls until either the next + label separator is seen or ``final=True``, so that streamed input is + decoded one whole label at a time. + + The 254-octet input length limit that :func:`idna.decode` enforces is + applied to the accumulated input, so that streaming a name and decoding + it in one shot either both succeed with the same result or both raise + :exc:`~idna.IDNAError`. + + Only the ``"strict"`` error handler is supported. + """ + + def __init__(self, errors: str = "strict") -> None: + super().__init__(errors) + self._consumed = 0 # input octets consumed so far + + def reset(self) -> None: + super().reset() + self._consumed = 0 + + def getstate(self) -> tuple[bytes, int]: + return (self.buffer, self._consumed) + + def setstate(self, state: tuple[bytes, int]) -> None: + self.buffer, self._consumed = state + + def _buffer_decode(self, data: Any, errors: str, final: bool) -> tuple[str, int]: # ty: ignore[invalid-method-override] + if errors != "strict": + raise IDNAError(f'Unsupported error handling "{errors}"', code="unsupported_errors") + + if not data: + return ("", 0) + + if not isinstance(data, str): + try: + data = str(data, "ascii") + except UnicodeDecodeError as err: + raise IDNAError("Invalid ASCII in A-label", code="invalid_ascii") from err + + # Mirror decode(), which rejects input longer than 254 characters + # (253 plus a possible trailing dot) before looking at any label. + # ``data`` is the unconsumed buffer plus the new input, so this is + # the total seen so far. + if self._consumed + len(data) > _max_domain_length + 1: + raise IDNAError("Domain too long", code="domain_too_long") + + labels = _unicode_dots_re.split(data) + trailing_dot = "" + if labels: + if not labels[-1]: + trailing_dot = "." + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = "." + + result = [] + size = 0 + for label in labels: + result.append(ulabel(label)) + if size: + size += 1 + size += len(label) + + result_str = ".".join(result) + trailing_dot + size += len(trailing_dot) + self._consumed += size + return (result_str, size) + + +class StreamWriter(Codec, codecs.StreamWriter): + pass + + +class StreamReader(Codec, codecs.StreamReader): + pass + + +def search_function(name: str) -> codecs.CodecInfo | None: + """Codec search function registered with :mod:`codecs`. + + Returns a :class:`codecs.CodecInfo` for the ``"idna2008"`` codec name + so that ``str.encode("idna2008")`` and ``bytes.decode("idna2008")`` + invoke the IDNA 2008 codec defined in this module. + + :param name: The codec name being looked up. + :returns: A :class:`codecs.CodecInfo` instance if ``name`` is + ``"idna2008"``, otherwise ``None``. + """ + if name != "idna2008": + return None + return codecs.CodecInfo( + name=name, + encode=Codec().encode, + decode=Codec().decode, # type: ignore + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) + + +codecs.register(search_function) diff --git a/venv/lib/python3.12/site-packages/idna/compat.py b/venv/lib/python3.12/site-packages/idna/compat.py new file mode 100644 index 0000000..5bedee9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/compat.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +from typing import Any + +from .core import decode, encode + + +def ToASCII(label: str) -> bytes: + """Compatibility shim for :rfc:`3490` ``ToASCII``. + + Delegates to :func:`idna.encode` (IDNA 2008). Provided to ease porting + of code written against the legacy :mod:`encodings.idna` API; new code + should call :func:`idna.encode` directly. + + :param label: The label or domain to encode. + :returns: The encoded form as ASCII :class:`bytes`. + """ + return encode(label) + + +def ToUnicode(label: bytes | bytearray) -> str: + """Compatibility shim for :rfc:`3490` ``ToUnicode``. + + Delegates to :func:`idna.decode` (IDNA 2008). Provided to ease porting + of code written against the legacy :mod:`encodings.idna` API; new code + should call :func:`idna.decode` directly. + + :param label: The label or domain to decode. + :returns: The decoded Unicode form. + """ + return decode(label) + + +def nameprep(s: Any) -> None: + """Stub for :rfc:`3491` Nameprep, which is not used by IDNA 2008. + + IDNA 2008 (:rfc:`5891`) replaces Nameprep with the per-codepoint + validity classes from :rfc:`5892`; this function exists only to + return a clear error if legacy code attempts to call it. + + :raises NotImplementedError: Always. + """ + raise NotImplementedError("IDNA 2008 does not utilise nameprep protocol") diff --git a/venv/lib/python3.12/site-packages/idna/core.py b/venv/lib/python3.12/site-packages/idna/core.py new file mode 100644 index 0000000..d4a6f0e --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/core.py @@ -0,0 +1,863 @@ +from __future__ import annotations + +import bisect +import re +import unicodedata +import warnings +from typing import Literal + +from . import idnadata +from .intranges import intranges_contain + +_virama_combining_class = 9 +_alabel_prefix = b"xn--" +_max_input_length = 1024 +_max_domain_length = 253 # RFC 1035 octets, excluding any trailing dot +_STATUS_VALID, _STATUS_MAPPED, _STATUS_DEVIATION, _STATUS_IGNORED = b"VMDI" +_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]") +_std3_disallowed_re = re.compile("[\x00-\x2c\x2f\x3a-\x40A-Z\x5b-\x60\x7b-\x7f]") +_bidi_rtl_first = frozenset({"R", "AL"}) +_bidi_rtl_categories = frozenset({"R", "AL", "AN"}) +_bidi_rtl_allowed = frozenset({"R", "AL", "AN", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"}) +_bidi_rtl_valid_ending = frozenset({"R", "AL", "EN", "AN"}) +_bidi_rtl_numeric = frozenset({"AN", "EN"}) +_bidi_ltr_allowed = frozenset({"L", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"}) +_bidi_ltr_valid_ending = frozenset({"L", "EN"}) +_bidi_joiner_l_or_d = frozenset({"L", "D"}) +_bidi_joiner_r_or_d = frozenset({"R", "D"}) + + +def _joining_type(cp: int) -> str | None: + for jt, ranges in idnadata.joining_types.items(): + if intranges_contain(cp, ranges): + return jt + return None + + +# Machine-readable identifiers for the rule an :class:`IDNAError` reports. +# These strings are stable and documented; exception message wording is not. +_ErrorCode = Literal[ + "input_too_long", + "label_too_long", + "domain_too_long", + "empty_label", + "empty_domain", + "not_nfc", + "hyphen_3_4", + "hyphen_start_end", + "leading_combiner", + "disallowed_codepoint", + "contextj", + "contexto", + "unknown_codepoint", + "bidi_rule_1", + "bidi_rule_2", + "bidi_rule_3", + "bidi_rule_4", + "bidi_rule_5", + "bidi_rule_6", + "bidi_unknown_direction", + "invalid_alabel", + "non_canonical_alabel", + "invalid_ascii", + "invalid_utf8", + "uts46_disallowed", + "uts46_std3", + "unsupported_errors", +] + + +class IDNAError(UnicodeError): + """Base exception for all IDNA-encoding related problems. + + ``str(err)`` is a human-readable description of the failure. The + exception also carries machine-readable attributes so callers do not + need to parse the message: + + * ``code`` -- a short, stable identifier for the rule that failed, such + as ``"disallowed_codepoint"`` or ``"bidi_rule_2"``; the full list is + documented in the README. Message wording, by contrast, may change + between releases. + * ``text`` -- the label (or, for UTS #46 processing, the domain) that + was being validated; + * ``codepoint`` -- the offending codepoint, as an ``int``; + * ``position`` -- the 1-based index of the offending character within + ``text``, matching the position quoted in the message. + + Each is ``None`` when it does not apply. + """ + + code: str | None + text: str | None + codepoint: int | None + position: int | None + + def __init__( + self, + *args: object, + code: _ErrorCode | None = None, + text: str | None = None, + codepoint: int | None = None, + position: int | None = None, + ) -> None: + super().__init__(*args) + self.code = code + self.text = text + self.codepoint = codepoint + self.position = position + + +class IDNABidiError(IDNAError): + """Exception when bidirectional requirements are not satisfied""" + + +class InvalidCodepoint(IDNAError): + """Exception when a disallowed or unallocated codepoint is used""" + + +class InvalidCodepointContext(IDNAError): + """Exception when the codepoint is not valid in the context it is used""" + + +def _combining_class(cp: int) -> int: + v = unicodedata.combining(chr(cp)) + if v == 0 and not unicodedata.name(chr(cp)): + raise ValueError("Unknown character in unicodedata") + return v + + +def _is_script(cp: str, script: str) -> bool: + return intranges_contain(ord(cp), idnadata.scripts[script]) + + +def _punycode(s: str) -> bytes: + return s.encode("punycode") + + +def _unot(s: int) -> str: + return f"U+{s:04X}" + + +def valid_label_length(label: bytes | str) -> bool: + """Check that a label does not exceed the maximum permitted length. + + Per :rfc:`1035` (and :rfc:`5891` §4.2.4) a DNS label must not exceed + 63 octets. The argument may be either a :class:`str` (a U-label, where + length is measured in characters) or :class:`bytes` (an A-label, where + length is measured in octets). + + :param label: The label to check. + :returns: ``True`` if the label is within the length limit, otherwise + ``False``. + """ + return len(label) <= 63 + + +def valid_string_length(domain: bytes | str, trailing_dot: bool) -> bool: + """Check that a full domain name does not exceed the maximum length. + + Per :rfc:`1035`, a domain name is limited to 253 octets when no trailing + dot is present, or 254 octets when one is included. + + :param domain: The full (possibly multi-label) domain name. + :param trailing_dot: ``True`` if ``domain`` includes a trailing ``.``. + :returns: ``True`` if the domain is within the length limit, otherwise + ``False``. + """ + return len(domain) <= _max_domain_length + trailing_dot + + +def check_bidi(label: str, check_ltr: bool = False) -> bool: + """Validate the Bidi Rule from :rfc:`5893` for a single label. + + The Bidi Rule constrains how bidirectional characters (Hebrew, Arabic, + etc.) may appear within a label. By default the check is only applied + when the label contains at least one right-to-left character (Unicode + bidirectional categories ``R``, ``AL``, or ``AN``); set ``check_ltr`` + to ``True`` to apply it to LTR-only labels as well. + + :param label: The label to validate, as a Unicode string. + :param check_ltr: If ``True``, apply the rules even when the label + contains no RTL characters. + :returns: ``True`` if the label satisfies the Bidi Rule. + :raises IDNABidiError: If any of Bidi Rule conditions 1-6 are violated, + or if the directional category of a codepoint cannot be determined. + """ + if len(label) > _max_input_length: + raise IDNAError("Label too long", code="input_too_long") + # Bidi rules should only be applied if string contains RTL characters + bidi_label = False + for idx, cp in enumerate(label, 1): + direction = unicodedata.bidirectional(cp) + if direction == "": + # String likely comes from a newer version of Unicode + raise IDNABidiError( + f"Unknown directionality in label {label!r} at position {idx}", + code="bidi_unknown_direction", + text=label, + codepoint=ord(cp), + position=idx, + ) + if direction in _bidi_rtl_categories: + bidi_label = True + if not bidi_label and not check_ltr: + return True + + # Bidi rule 1 + direction = unicodedata.bidirectional(label[0]) + if direction in _bidi_rtl_first: + rtl = True + elif direction == "L": + rtl = False + else: + raise IDNABidiError( + f"First codepoint in label {label!r} must be directionality L, R or AL", + code="bidi_rule_1", + text=label, + codepoint=ord(label[0]), + position=1, + ) + + valid_ending = False + ending_idx = 1 + number_type: str | None = None + for idx, cp in enumerate(label, 1): + direction = unicodedata.bidirectional(cp) + + if rtl: + # Bidi rule 2 + if direction not in _bidi_rtl_allowed: + raise IDNABidiError( + f"Invalid direction for codepoint at position {idx} in a right-to-left label", + code="bidi_rule_2", + text=label, + codepoint=ord(cp), + position=idx, + ) + # Bidi rule 3 + if direction in _bidi_rtl_valid_ending: + valid_ending = True + ending_idx = idx + elif direction != "NSM": + valid_ending = False + ending_idx = idx + # Bidi rule 4 + if direction in _bidi_rtl_numeric: + if not number_type: + number_type = direction + elif number_type != direction: + raise IDNABidiError( + "Can not mix numeral types in a right-to-left label", + code="bidi_rule_4", + text=label, + codepoint=ord(cp), + position=idx, + ) + else: + # Bidi rule 5 + if direction not in _bidi_ltr_allowed: + raise IDNABidiError( + f"Invalid direction for codepoint at position {idx} in a left-to-right label", + code="bidi_rule_5", + text=label, + codepoint=ord(cp), + position=idx, + ) + # Bidi rule 6 + if direction in _bidi_ltr_valid_ending: + valid_ending = True + ending_idx = idx + elif direction != "NSM": + valid_ending = False + ending_idx = idx + + if not valid_ending: + # Rules 3 and 6 concern the last character that is not a + # non-spacing mark, which is what ``ending_idx`` tracks. + raise IDNABidiError( + "Label ends with illegal codepoint directionality", + code="bidi_rule_3" if rtl else "bidi_rule_6", + text=label, + codepoint=ord(label[ending_idx - 1]), + position=ending_idx, + ) + + return True + + +def check_initial_combiner(label: str) -> bool: + """Reject labels that begin with a combining mark. + + Per :rfc:`5891` §4.2.3.2 a label must not start with a character of + Unicode general category ``M`` (Mark). + + :param label: The label to check. + :returns: ``True`` if the first character is not a combining mark. + :raises IDNAError: If the label begins with a combining character. + """ + if label and unicodedata.category(label[0])[0] == "M": + raise IDNAError( + "Label begins with an illegal combining character", + code="leading_combiner", + text=label, + codepoint=ord(label[0]), + position=1, + ) + return True + + +def check_hyphen_ok(label: str) -> bool: + """Validate the hyphen restrictions for a label. + + Per :rfc:`5891` §4.2.3.1 a label must not start or end with a hyphen + (``U+002D``), and must not have hyphens in both the third and fourth + positions (the prefix reserved for A-labels). + + :param label: The label to check. + :returns: ``True`` if the hyphen restrictions are satisfied. + :raises IDNAError: If any of the hyphen restrictions are violated. + """ + if label[2:4] == "--": + raise IDNAError("Label has disallowed hyphens in 3rd and 4th position", code="hyphen_3_4") + if label.startswith("-") or label.endswith("-"): + raise IDNAError("Label must not start or end with a hyphen", code="hyphen_start_end") + return True + + +def check_nfc(label: str) -> None: + """Require that a label is in Unicode Normalization Form C. + + :param label: The label to check. + :raises IDNAError: If ``label`` differs from its NFC normalisation. + """ + if len(label) > _max_input_length: + raise IDNAError("Label too long", code="input_too_long") + if unicodedata.normalize("NFC", label) != label: + raise IDNAError("Label must be in Normalization Form C", code="not_nfc") + + +def valid_contextj(label: str, pos: int) -> bool: + """Validate the CONTEXTJ rules from :rfc:`5892` Appendix A. + + These rules govern the contextual use of the joiner codepoints + ``U+200C`` (ZERO WIDTH NON-JOINER, Appendix A.1) and ``U+200D`` + (ZERO WIDTH JOINER, Appendix A.2) within a label. + + :param label: The label containing the codepoint. + :param pos: Index of the joiner codepoint within ``label``. + :returns: ``True`` if the codepoint at ``pos`` satisfies its CONTEXTJ + rule, ``False`` otherwise (including when the codepoint at + ``pos`` is not a recognised joiner). + :raises ValueError: If an adjacent codepoint has no Unicode name when + determining its combining class. + :raises IDNAError: If ``label`` exceeds the defensive input length limit. + """ + if len(label) > _max_input_length: + raise IDNAError("Label too long", code="input_too_long") + cp_value = ord(label[pos]) + + if cp_value == 0x200C: + if pos > 0 and _combining_class(ord(label[pos - 1])) == _virama_combining_class: + return True + + ok = False + for i in range(pos - 1, -1, -1): + joining_type = _joining_type(ord(label[i])) + if joining_type == "T": + continue + if joining_type in _bidi_joiner_l_or_d: + ok = True + break + break + + if not ok: + return False + + ok = False + for i in range(pos + 1, len(label)): + joining_type = _joining_type(ord(label[i])) + if joining_type == "T": + continue + if joining_type in _bidi_joiner_r_or_d: + ok = True + break + break + return ok + + if cp_value == 0x200D: + return pos > 0 and _combining_class(ord(label[pos - 1])) == _virama_combining_class + + return False + + +def valid_contexto(label: str, pos: int, exception: bool = False) -> bool: + """Validate the CONTEXTO rules from :rfc:`5892` Appendix A. + + Covers the contextual rules for codepoints such as MIDDLE DOT + (``U+00B7``), Greek lower numeral sign, Hebrew punctuation, Katakana + middle dot, and the Arabic-Indic / Extended Arabic-Indic digit ranges. + + :param label: The label containing the codepoint. + :param pos: Index of the codepoint within ``label``. + :param exception: Reserved for forward compatibility; currently unused. + :returns: ``True`` if the codepoint at ``pos`` satisfies its CONTEXTO + rule, ``False`` otherwise (including when the codepoint is not a + recognised CONTEXTO codepoint). + :raises IDNAError: If ``label`` exceeds the defensive input length limit. + """ + if len(label) > _max_input_length: + raise IDNAError("Label too long", code="input_too_long") + cp_value = ord(label[pos]) + + if cp_value == 0x00B7: + return 0 < pos < len(label) - 1 and ord(label[pos - 1]) == 0x006C and ord(label[pos + 1]) == 0x006C + + if cp_value == 0x0375: + if pos < len(label) - 1 and len(label) > 1: + return _is_script(label[pos + 1], "Greek") + return False + + if cp_value in {0x05F3, 0x05F4}: + if pos > 0: + return _is_script(label[pos - 1], "Hebrew") + return False + + if cp_value == 0x30FB: + for cp in label: + if cp == "\u30fb": + continue + if _is_script(cp, "Hiragana") or _is_script(cp, "Katakana") or _is_script(cp, "Han"): + return True + return False + + if 0x660 <= cp_value <= 0x669: + return not any(0x6F0 <= ord(cp) <= 0x06F9 for cp in label) + + if 0x6F0 <= cp_value <= 0x6F9: + return not any(0x660 <= ord(cp) <= 0x0669 for cp in label) + + return False + + +def check_label(label: str | bytes | bytearray) -> None: + """Run the full set of IDNA 2008 validity checks on a single label. + + Applies, in order: NFC normalisation (:func:`check_nfc`), hyphen + restrictions (:func:`check_hyphen_ok`), the no-leading-combiner rule + (:func:`check_initial_combiner`), per-codepoint validity (PVALID, + CONTEXTJ, CONTEXTO classes from :rfc:`5892`), and the Bidi Rule + (:func:`check_bidi`). + + :param label: The label to validate. ``bytes`` or ``bytearray`` input + is decoded as UTF-8 first. + :raises IDNAError: If the label is empty or fails a structural rule. + :raises InvalidCodepoint: If the label contains a DISALLOWED or + UNASSIGNED codepoint. + :raises InvalidCodepointContext: If a CONTEXTJ or CONTEXTO codepoint + is not valid in its context. + :raises IDNABidiError: If the Bidi Rule is violated. + """ + if len(label) > _max_input_length: + raise IDNAError("Label too long", code="input_too_long") + if isinstance(label, (bytes, bytearray)): + try: + label = label.decode("utf-8") + except UnicodeDecodeError as err: + raise IDNAError("Invalid UTF-8 in label", code="invalid_utf8") from err + if len(label) == 0: + raise IDNAError("Empty Label", code="empty_label") + + # Check against the domain length rather than the label length to + # support some UTS #46 use cases, while still bounding the work done + # by the label contextual rules below. + if not valid_string_length(label, trailing_dot=True): + raise IDNAError("Label too long", code="label_too_long") + + check_nfc(label) + check_hyphen_ok(label) + check_initial_combiner(label) + + for pos, cp in enumerate(label): + cp_value = ord(cp) + if intranges_contain(cp_value, idnadata.codepoint_classes["PVALID"]): + continue + if intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTJ"]): + try: + contextj_ok = valid_contextj(label, pos) + except ValueError as err: + raise IDNAError( + f"Unknown codepoint adjacent to joiner {_unot(cp_value)} at position {pos + 1} in {label!r}", + code="unknown_codepoint", + text=label, + codepoint=cp_value, + position=pos + 1, + ) from err + if not contextj_ok: + raise InvalidCodepointContext( + f"Joiner {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}", + code="contextj", + text=label, + codepoint=cp_value, + position=pos + 1, + ) + elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTO"]): + if not valid_contexto(label, pos): + raise InvalidCodepointContext( + f"Codepoint {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}", + code="contexto", + text=label, + codepoint=cp_value, + position=pos + 1, + ) + else: + raise InvalidCodepoint( + f"Codepoint {_unot(cp_value)} at position {pos + 1} of {label!r} not allowed", + code="disallowed_codepoint", + text=label, + codepoint=cp_value, + position=pos + 1, + ) + + check_bidi(label) + + +def alabel(label: str) -> bytes: + """Convert a single U-label into its A-label form. + + The result is the ASCII-Compatible Encoding (ACE) form per :rfc:`5891` + §4: the label is validated, Punycode-encoded, and prefixed with + ``xn--``. Pure ASCII labels that are already valid IDNA labels are + returned unchanged (as :class:`bytes`). + + :param label: The label to convert, as a Unicode string. + :returns: The A-label as ASCII-encoded :class:`bytes`. + :raises IDNAError: If the label is invalid or the resulting A-label + exceeds 63 octets. + """ + if len(label) > _max_input_length: + raise IDNAError("Label too long", code="input_too_long") + try: + label_bytes = label.encode("ascii") + except UnicodeEncodeError: + pass + else: + ulabel(label_bytes) + if not valid_label_length(label_bytes): + raise IDNAError("Label too long", code="label_too_long") + return label_bytes + + check_label(label) + label_bytes = _alabel_prefix + _punycode(label) + + if not valid_label_length(label_bytes): + raise IDNAError("Label too long", code="label_too_long") + + return label_bytes + + +def ulabel(label: str | bytes | bytearray) -> str: + """Convert a single A-label into its U-label form. + + Performs the inverse of :func:`alabel`: an ``xn--``-prefixed label is + Punycode-decoded and validated, and is rejected unless it is the + canonical A-label for the decoded U-label (:rfc:`5891` §5.3). Labels + that are already Unicode (or plain ASCII without the ACE prefix) are + validated and returned as a Unicode string. + + :param label: The label to convert. ``bytes`` or ``bytearray`` input + is treated as ASCII. + :returns: The U-label as a Unicode string. + :raises IDNAError: If the label is malformed or fails validation. + """ + if len(label) > _max_input_length: + raise IDNAError("Label too long", code="input_too_long") + if not isinstance(label, (bytes, bytearray)): + try: + label_bytes = label.encode("ascii") + except UnicodeEncodeError: + check_label(label) + return label + else: + label_bytes = bytes(label) + if not label_bytes.isascii(): + raise IDNAError("Invalid ASCII in A-label", code="invalid_ascii") + + label_bytes = label_bytes.lower() + if label_bytes.startswith(_alabel_prefix): + label_bytes = label_bytes[len(_alabel_prefix) :] + if not label_bytes: + raise IDNAError("Malformed A-label, no Punycode eligible content found", code="invalid_alabel") + if label_bytes.endswith(b"-"): + raise IDNAError("A-label must not end with a hyphen", code="invalid_alabel") + else: + check_label(label_bytes) + return label_bytes.decode("ascii") + + try: + label = label_bytes.decode("punycode") + except UnicodeError as err: + raise IDNAError("Invalid A-label", code="invalid_alabel") from err + # RFC 5891 §5.3: the label is rejected unless re-encoding the decoded + # form reproduces the (lowercased) input. This catches "fake A-labels" + # (RFC 5890 §2.3.2.1) such as ``xn---bbk``, a non-canonical Punycode + # spelling of ``xn--bbk`` that would otherwise decode to the same + # U-label and so display identically to a different wire-format name. + if _punycode(label) != label_bytes: + raise IDNAError("A-label is not the canonical Punycode encoding of its U-label", code="non_canonical_alabel") + check_label(label) + return label + + +def _check_std3(text: str, domain: str, offset: int) -> None: + """Raise if ``text``, a slice of ``domain`` starting at ``offset`` that + UTS #46 mapping left unchanged, contains an ASCII character disallowed + under ``UseSTD3ASCIIRules``.""" + match = _std3_disallowed_re.search(text) + if match: + codepoint = ord(match.group()) + position = offset + match.start() + 1 + raise InvalidCodepoint( + f"Codepoint {_unot(codepoint)} not allowed at position {position} in {domain!r}", + code="uts46_std3", + text=domain, + codepoint=codepoint, + position=position, + ) + + +def _warn_transitional() -> None: + warnings.warn( + "Transitional processing is deprecated in UTS #46 and has no effect. " + "The transitional argument will be removed in a future version.", + DeprecationWarning, + stacklevel=3, + ) + + +def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str: + """Apply the UTS #46 character mapping to a domain string. + + Implements the mapping table from `UTS #46 §4 + `_: each character is kept, + replaced, or rejected based on its status (``V``, ``M``, ``D``, + ``I``, ``X``). The result is returned in Normalisation Form C. + + :param domain: The full domain name to remap. + :param std3_rules: If ``True``, apply UTS #46's ``UseSTD3ASCIIRules``: + after mapping, any ASCII character other than a lowercase letter, + digit, hyphen or the label separator ``.`` is rejected, whether it + appeared in the input or was produced by a mapping (e.g. U+FF01 + FULLWIDTH EXCLAMATION MARK maps to ``!``). If ``False``, such + characters are passed through. + :param transitional: Deprecated and ignored. UTS #46 deprecated + transitional processing in Unicode 15.1 and deviation (status + ``D``) codepoints are now always kept, so this has no effect + beyond emitting a :class:`DeprecationWarning`. It will be removed + in a future version. + :returns: The remapped domain, in Normalisation Form C. + :raises InvalidCodepoint: If the domain contains a disallowed + codepoint under the chosen rules. + :raises IDNAError: If ``domain`` exceeds the defensive input length limit. + """ + if transitional: + _warn_transitional() + if len(domain) > _max_input_length: + raise IDNAError("Domain too long", code="input_too_long") + if domain.isascii(): + # The only ASCII mapping in UTS #46 is upper- to lowercase, and + # ASCII is invariant under NFC, so lowercasing is the whole job. + result = domain.lower() + if std3_rules: + _check_std3(result, domain, 0) + return result + + from .uts46data import uts46_replacements, uts46_starts, uts46_statuses + + # ``start`` marks the run of unchanged input not yet copied; a run is + # only sliced out when a character must be replaced or dropped, so the + # common no-change case makes no copy. STD3 is checked per output piece + # to report a violation at its input position. + output: list[str] = [] + start = 0 + for pos, char in enumerate(domain): + code_point = ord(char) + i = code_point if code_point < 256 else bisect.bisect_right(uts46_starts, code_point) - 1 + status = uts46_statuses[i] + # UTS #46 §4: V valid, D deviation (kept), M mapped, I ignored, + # anything else disallowed. + if status == _STATUS_VALID: + continue + if status == _STATUS_MAPPED: + replacement = uts46_replacements[i] + elif status == _STATUS_DEVIATION: + continue + elif status == _STATUS_IGNORED: + replacement = None + else: + raise InvalidCodepoint( + f"Codepoint {_unot(code_point)} not allowed at position {pos + 1} in {domain!r}", + code="uts46_disallowed", + text=domain, + codepoint=code_point, + position=pos + 1, + ) + if start < pos: + run = domain[start:pos] + if std3_rules: + _check_std3(run, domain, start) + output.append(run) + if replacement: + if std3_rules and _std3_disallowed_re.search(replacement): + raise InvalidCodepoint( + f"Codepoint {_unot(code_point)} not allowed at position {pos + 1} in {domain!r}", + code="uts46_std3", + text=domain, + codepoint=code_point, + position=pos + 1, + ) + output.append(replacement) + start = pos + 1 + + if start == 0: + if std3_rules: + _check_std3(domain, domain, 0) + return unicodedata.normalize("NFC", domain) + tail = domain[start:] + if std3_rules: + _check_std3(tail, domain, start) + output.append(tail) + return unicodedata.normalize("NFC", "".join(output)) + + +def encode( + s: str | bytes | bytearray, + strict: bool = False, + uts46: bool = False, + std3_rules: bool = False, + transitional: bool = False, +) -> bytes: + """Encode a Unicode domain name into its ASCII (A-label) form. + + Splits the input on label separators (only ``U+002E`` if ``strict`` is + set; otherwise also IDEOGRAPHIC FULL STOP ``U+3002``, FULLWIDTH FULL + STOP ``U+FF0E``, and HALFWIDTH IDEOGRAPHIC FULL STOP ``U+FF61``), + encodes each label with :func:`alabel`, and rejoins them with ``.``. + Optionally pre-processes the input through :func:`uts46_remap`. + + :param s: The domain name to encode. + :param strict: If ``True``, only ``U+002E`` is recognised as a label + separator. + :param uts46: If ``True``, apply UTS #46 mapping before encoding. + :param std3_rules: Forwarded to :func:`uts46_remap` when ``uts46`` is + ``True``. + :param transitional: Deprecated and ignored (see :func:`uts46_remap`): + emits a :class:`DeprecationWarning` and will be removed in a + future version. + :returns: The encoded domain as ASCII :class:`bytes`. + :raises IDNAError: If the domain is empty, contains an invalid label, + or exceeds the maximum domain length. + """ + if transitional: + _warn_transitional() + if not isinstance(s, str): + try: + s = str(s, "ascii") + except (UnicodeDecodeError, TypeError) as err: + raise IDNAError( + "should pass a unicode string to the function rather than a byte string.", code="invalid_ascii" + ) from err + if len(s) > _max_input_length: + raise IDNAError("Domain too long", code="input_too_long") + if uts46: + s = uts46_remap(s, std3_rules) + + if not valid_string_length(s, trailing_dot=True): + raise IDNAError("Domain too long", code="domain_too_long") + + trailing_dot = False + result = [] + labels = s.split(".") if strict else _unicode_dots_re.split(s) + if not labels or labels == [""]: + raise IDNAError("Empty domain", code="empty_domain") + if labels[-1] == "": + del labels[-1] + trailing_dot = True + for label in labels: + s = alabel(label) + if s: + result.append(s) + else: + raise IDNAError("Empty label", code="empty_label") + if trailing_dot: + result.append(b"") + s = b".".join(result) + if not valid_string_length(s, trailing_dot): + raise IDNAError("Domain too long", code="domain_too_long") + return s + + +def decode( + s: str | bytes | bytearray, + strict: bool = False, + uts46: bool = False, + std3_rules: bool = False, + display: bool = False, +) -> str: + """Decode an A-label-encoded domain name back to Unicode. + + Splits the input on label separators (see :func:`encode` for the + rules), decodes each label with :func:`ulabel`, and rejoins them + with ``.``. Optionally pre-processes the input through + :func:`uts46_remap`. + + :param s: The domain name to decode. + :param strict: If ``True``, only ``U+002E`` is recognised as a label + separator. + :param uts46: If ``True``, apply UTS #46 mapping before decoding. + :param std3_rules: Forwarded to :func:`uts46_remap` when ``uts46`` is + ``True``. + :param display: If ``True``, any ``xn--`` label that fails IDNA + validation is passed through unchanged (lowercased) rather than + aborting the whole call. Intended for "decode for display" + consumers (e.g. URL libraries, HTTP clients) that want to show + the user the label as it appears on the wire when it cannot be + rendered as Unicode. Matches the per-label recovery prescribed + by UTS #46 §4 and the WHATWG URL "domain to Unicode" algorithm. + :returns: The decoded domain as a Unicode string. + :raises IDNAError: If the input is not valid ASCII, contains an + invalid label, or is empty. + """ + if not isinstance(s, str): + try: + s = str(s, "ascii") + except (UnicodeDecodeError, TypeError) as err: + raise IDNAError("Invalid ASCII in A-label", code="invalid_ascii") from err + if len(s) > _max_input_length: + raise IDNAError("Domain too long", code="input_too_long") + if uts46: + s = uts46_remap(s, std3_rules, False) + if not valid_string_length(s, trailing_dot=True): + raise IDNAError("Domain too long", code="domain_too_long") + trailing_dot = False + result = [] + labels = s.split(".") if strict else _unicode_dots_re.split(s) + if not labels or labels == [""]: + raise IDNAError("Empty domain", code="empty_domain") + if not labels[-1]: + del labels[-1] + trailing_dot = True + for label in labels: + try: + u = ulabel(label) + except IDNAError: + if display and label[:4].lower() == "xn--": + u = label.lower() + else: + raise + if u: + result.append(u) + else: + raise IDNAError("Empty label", code="empty_label") + if trailing_dot: + result.append("") + return ".".join(result) diff --git a/venv/lib/python3.12/site-packages/idna/idnadata.py b/venv/lib/python3.12/site-packages/idna/idnadata.py new file mode 100644 index 0000000..471c047 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/idnadata.py @@ -0,0 +1,1933 @@ +# This file is automatically generated by tools/idna-data + +__version__ = "18.0.0" + +scripts = { + "Greek": ( + 0x37000000374, + 0x37500000378, + 0x37A0000037E, + 0x37F00000380, + 0x38400000385, + 0x38600000387, + 0x3880000038B, + 0x38C0000038D, + 0x38E000003A2, + 0x3A3000003E2, + 0x3F000000400, + 0x1D2600001D2B, + 0x1D5D00001D62, + 0x1D6600001D6B, + 0x1DBF00001DC0, + 0x1F0000001F16, + 0x1F1800001F1E, + 0x1F2000001F46, + 0x1F4800001F4E, + 0x1F5000001F58, + 0x1F5900001F5A, + 0x1F5B00001F5C, + 0x1F5D00001F5E, + 0x1F5F00001F7E, + 0x1F8000001FB5, + 0x1FB600001FC5, + 0x1FC600001FD4, + 0x1FD600001FDC, + 0x1FDD00001FF0, + 0x1FF200001FF5, + 0x1FF600001FFF, + 0x212600002127, + 0xAB650000AB66, + 0x101400001018F, + 0x101A0000101A1, + 0x1D2000001D246, + 0x1DFF30001DFF5, + ), + "Han": ( + 0x2E8000002E9A, + 0x2E9B00002EF4, + 0x2F0000002FD6, + 0x300500003006, + 0x300700003008, + 0x30210000302A, + 0x30380000303C, + 0x340000004DC0, + 0x4E000000A000, + 0xF9000000FA6E, + 0xFA700000FADA, + 0x16FE200016FE4, + 0x16FF000016FF7, + 0x200000002A6E0, + 0x2A7000002B81F, + 0x2B8200002CEAE, + 0x2CEB00002EBE1, + 0x2EBF00002EE5E, + 0x2F8000002FA1E, + 0x300000003134B, + 0x313500003347A, + ), + "Hebrew": ( + 0x591000005CA, + 0x5D0000005EB, + 0x5EF000005F5, + 0xFB1D0000FB37, + 0xFB380000FB3D, + 0xFB3E0000FB3F, + 0xFB400000FB42, + 0xFB430000FB45, + 0xFB460000FB50, + ), + "Hiragana": ( + 0x304100003097, + 0x309D000030A0, + 0x1B0010001B120, + 0x1B1230001B124, + 0x1B1320001B133, + 0x1B1500001B153, + 0x1F2000001F201, + ), + "Katakana": ( + 0x30A1000030FB, + 0x30FD00003100, + 0x31F000003200, + 0x32D0000032FF, + 0x330000003358, + 0xFF660000FF70, + 0xFF710000FF9E, + 0x1AFF00001AFF4, + 0x1AFF50001AFFC, + 0x1AFFD0001AFFF, + 0x1B0000001B001, + 0x1B1200001B123, + 0x1B1240001B129, + 0x1B1550001B156, + 0x1B1640001B169, + ), +} + + +joining_types = { + "C": ( + 0x64000000641, + 0x7FA000007FB, + 0x88300000886, + 0x180A0000180B, + 0x200D0000200E, + ), + "D": ( + 0x62000000621, + 0x62600000627, + 0x62800000629, + 0x62A0000062F, + 0x63300000640, + 0x64100000648, + 0x6490000064B, + 0x66E00000670, + 0x67800000688, + 0x69A000006C0, + 0x6C1000006C3, + 0x6CC000006CD, + 0x6CE000006CF, + 0x6D0000006D2, + 0x6FA000006FD, + 0x6FF00000700, + 0x71200000715, + 0x71A0000071E, + 0x71F00000728, + 0x7290000072A, + 0x72B0000072C, + 0x72D0000072F, + 0x74E00000759, + 0x75C0000076B, + 0x76D00000771, + 0x77200000773, + 0x77500000778, + 0x77A00000780, + 0x7CA000007EB, + 0x84100000846, + 0x84800000849, + 0x84A00000854, + 0x85500000856, + 0x86000000861, + 0x86200000866, + 0x86800000869, + 0x88600000887, + 0x8890000088E, + 0x88F00000890, + 0x8A0000008AA, + 0x8AF000008B1, + 0x8B3000008B9, + 0x8BA000008C9, + 0x180700001808, + 0x182000001879, + 0x1887000018A9, + 0x18AA000018AB, + 0xA8400000A872, + 0x10AC000010AC5, + 0x10AD300010AD7, + 0x10AD800010ADD, + 0x10ADE00010AE1, + 0x10AEB00010AEF, + 0x10B8000010B81, + 0x10B8200010B83, + 0x10B8600010B89, + 0x10B8A00010B8C, + 0x10B8D00010B8E, + 0x10B9000010B91, + 0x10BAD00010BAF, + 0x10D0100010D22, + 0x10D2300010D24, + 0x10EC300010EC5, + 0x10EC600010EC8, + 0x10F3000010F33, + 0x10F3400010F45, + 0x10F5100010F54, + 0x10F7000010F74, + 0x10F7600010F82, + 0x10FB000010FB1, + 0x10FB200010FB4, + 0x10FB800010FB9, + 0x10FBB00010FBD, + 0x10FBE00010FC0, + 0x10FC100010FC2, + 0x10FC400010FC5, + 0x10FCA00010FCB, + 0x1E9000001E944, + ), + "L": ( + 0xA8720000A873, + 0x10ACD00010ACE, + 0x10AD700010AD8, + 0x10D0000010D01, + 0x10ED900010EEF, + 0x10FCB00010FCC, + ), + "R": ( + 0x62200000626, + 0x62700000628, + 0x6290000062A, + 0x62F00000633, + 0x64800000649, + 0x67100000674, + 0x67500000678, + 0x6880000069A, + 0x6C0000006C1, + 0x6C3000006CC, + 0x6CD000006CE, + 0x6CF000006D0, + 0x6D2000006D4, + 0x6D5000006D6, + 0x6EE000006F0, + 0x71000000711, + 0x7150000071A, + 0x71E0000071F, + 0x72800000729, + 0x72A0000072B, + 0x72C0000072D, + 0x72F00000730, + 0x74D0000074E, + 0x7590000075C, + 0x76B0000076D, + 0x77100000772, + 0x77300000775, + 0x7780000077A, + 0x84000000841, + 0x84600000848, + 0x8490000084A, + 0x85400000855, + 0x85600000859, + 0x86700000868, + 0x8690000086B, + 0x87000000883, + 0x88E0000088F, + 0x8AA000008AD, + 0x8AE000008AF, + 0x8B1000008B3, + 0x8B9000008BA, + 0x10AC500010AC6, + 0x10AC700010AC8, + 0x10AC900010ACB, + 0x10ACE00010AD3, + 0x10ADD00010ADE, + 0x10AE100010AE2, + 0x10AE400010AE5, + 0x10AEF00010AF0, + 0x10B8100010B82, + 0x10B8300010B86, + 0x10B8900010B8A, + 0x10B8C00010B8D, + 0x10B8E00010B90, + 0x10B9100010B92, + 0x10BA900010BAD, + 0x10D2200010D23, + 0x10EC200010EC3, + 0x10F3300010F34, + 0x10F5400010F55, + 0x10F7400010F76, + 0x10FB400010FB7, + 0x10FB900010FBB, + 0x10FBD00010FBE, + 0x10FC200010FC4, + 0x10FC900010FCA, + ), + "T": ( + 0xAD000000AE, + 0x30000000370, + 0x4830000048A, + 0x591000005BE, + 0x5BF000005C0, + 0x5C1000005C3, + 0x5C4000005C6, + 0x5C7000005CA, + 0x6100000061B, + 0x61C0000061D, + 0x64B00000660, + 0x67000000671, + 0x6D6000006DD, + 0x6DF000006E5, + 0x6E7000006E9, + 0x6EA000006EE, + 0x70F00000710, + 0x71100000712, + 0x7300000074B, + 0x7A6000007B1, + 0x7EB000007F4, + 0x7FD000007FE, + 0x8160000081A, + 0x81B00000824, + 0x82500000828, + 0x8290000082E, + 0x8590000085C, + 0x897000008A0, + 0x8CA000008E2, + 0x8E300000903, + 0x93A0000093B, + 0x93C0000093D, + 0x94100000949, + 0x94D0000094E, + 0x95100000958, + 0x96200000964, + 0x98100000982, + 0x9BC000009BD, + 0x9C1000009C5, + 0x9CD000009CE, + 0x9E2000009E4, + 0x9FE000009FF, + 0xA0100000A03, + 0xA3C00000A3D, + 0xA4100000A43, + 0xA4700000A49, + 0xA4B00000A4E, + 0xA5100000A52, + 0xA7000000A72, + 0xA7500000A76, + 0xA8100000A83, + 0xABC00000ABD, + 0xAC100000AC6, + 0xAC700000AC9, + 0xACD00000ACE, + 0xAE200000AE4, + 0xAFA00000B00, + 0xB0100000B02, + 0xB3C00000B3D, + 0xB3F00000B40, + 0xB4100000B45, + 0xB4D00000B4E, + 0xB5300000B57, + 0xB6200000B64, + 0xB8200000B83, + 0xBC000000BC1, + 0xBCD00000BCE, + 0xC0000000C01, + 0xC0400000C05, + 0xC3C00000C3D, + 0xC3E00000C41, + 0xC4600000C49, + 0xC4A00000C4E, + 0xC5500000C57, + 0xC6200000C64, + 0xC8100000C82, + 0xCBC00000CBD, + 0xCBF00000CC0, + 0xCC600000CC7, + 0xCCC00000CCE, + 0xCE200000CE4, + 0xD0000000D02, + 0xD3B00000D3D, + 0xD4100000D45, + 0xD4D00000D4E, + 0xD6200000D64, + 0xD8100000D82, + 0xDCA00000DCB, + 0xDD200000DD5, + 0xDD600000DD7, + 0xE3100000E32, + 0xE3400000E3B, + 0xE4700000E4F, + 0xEB100000EB2, + 0xEB400000EBD, + 0xEC800000ECF, + 0xF1800000F1A, + 0xF3500000F36, + 0xF3700000F38, + 0xF3900000F3A, + 0xF7100000F7F, + 0xF8000000F85, + 0xF8600000F88, + 0xF8D00000F98, + 0xF9900000FBD, + 0xFC600000FC7, + 0x102D00001031, + 0x103200001038, + 0x10390000103B, + 0x103D0000103F, + 0x10580000105A, + 0x105E00001061, + 0x107100001075, + 0x108200001083, + 0x108500001087, + 0x108D0000108E, + 0x109D0000109E, + 0x135D00001360, + 0x171200001715, + 0x173200001734, + 0x175200001754, + 0x177200001774, + 0x17B4000017B6, + 0x17B7000017BE, + 0x17C6000017C7, + 0x17C9000017D4, + 0x17DD000017DE, + 0x180B0000180E, + 0x180F00001810, + 0x188500001887, + 0x18A9000018AA, + 0x192000001923, + 0x192700001929, + 0x193200001933, + 0x19390000193C, + 0x1A1700001A19, + 0x1A1B00001A1C, + 0x1A5600001A57, + 0x1A5800001A5F, + 0x1A6000001A61, + 0x1A6200001A63, + 0x1A6500001A6D, + 0x1A7300001A7D, + 0x1A7F00001A80, + 0x1AB000001AF1, + 0x1B0000001B04, + 0x1B3400001B35, + 0x1B3600001B3B, + 0x1B3C00001B3D, + 0x1B4200001B43, + 0x1B6B00001B74, + 0x1B8000001B82, + 0x1BA200001BA6, + 0x1BA800001BAA, + 0x1BAB00001BAE, + 0x1BE600001BE7, + 0x1BE800001BEA, + 0x1BED00001BEE, + 0x1BEF00001BF2, + 0x1C2C00001C34, + 0x1C3600001C38, + 0x1CD000001CD3, + 0x1CD400001CE1, + 0x1CE200001CE9, + 0x1CED00001CEE, + 0x1CF400001CF5, + 0x1CF800001CFA, + 0x1DC000001E00, + 0x200B0000200C, + 0x200E00002010, + 0x202A0000202F, + 0x206000002065, + 0x206A00002070, + 0x20D0000020F1, + 0x2CEF00002CF2, + 0x2D7F00002D80, + 0x2DE000002E00, + 0x302A0000302E, + 0x30990000309B, + 0xA66F0000A673, + 0xA6740000A67E, + 0xA69E0000A6A0, + 0xA6F00000A6F2, + 0xA8020000A803, + 0xA8060000A807, + 0xA80B0000A80C, + 0xA8250000A827, + 0xA82C0000A82D, + 0xA8C40000A8C6, + 0xA8E00000A8F2, + 0xA8FF0000A900, + 0xA9260000A92E, + 0xA9470000A952, + 0xA9800000A983, + 0xA9B30000A9B4, + 0xA9B60000A9BA, + 0xA9BC0000A9BE, + 0xA9E50000A9E6, + 0xAA290000AA2F, + 0xAA310000AA33, + 0xAA350000AA37, + 0xAA430000AA44, + 0xAA4C0000AA4D, + 0xAA7C0000AA7D, + 0xAAB00000AAB1, + 0xAAB20000AAB5, + 0xAAB70000AAB9, + 0xAABE0000AAC0, + 0xAAC10000AAC2, + 0xAAEC0000AAEE, + 0xAAF60000AAF7, + 0xABE50000ABE6, + 0xABE80000ABE9, + 0xABED0000ABEE, + 0xFB1E0000FB1F, + 0xFE000000FE10, + 0xFE200000FE30, + 0xFEFF0000FF00, + 0xFFF90000FFFC, + 0x101FD000101FE, + 0x102E0000102E1, + 0x103760001037B, + 0x10A0100010A04, + 0x10A0500010A07, + 0x10A0C00010A10, + 0x10A3800010A3B, + 0x10A3F00010A40, + 0x10AE500010AE7, + 0x10D2400010D28, + 0x10D6900010D6E, + 0x10EAB00010EAD, + 0x10ECB00010ED0, + 0x10EF000010F00, + 0x10F4600010F51, + 0x10F8200010F86, + 0x1100100011002, + 0x1103800011047, + 0x1107000011071, + 0x1107300011075, + 0x1107F00011082, + 0x110B3000110B7, + 0x110B9000110BB, + 0x110C2000110C3, + 0x1110000011103, + 0x111270001112C, + 0x1112D00011135, + 0x1117300011174, + 0x1118000011182, + 0x111B6000111BF, + 0x111C9000111CD, + 0x111CF000111D0, + 0x1122F00011232, + 0x1123400011235, + 0x1123600011238, + 0x1123E0001123F, + 0x1124100011242, + 0x112DF000112E0, + 0x112E3000112EB, + 0x1130000011302, + 0x1133B0001133D, + 0x1134000011341, + 0x113660001136D, + 0x1137000011375, + 0x113BB000113C1, + 0x113CE000113CF, + 0x113D0000113D1, + 0x113D2000113D3, + 0x113E1000113E3, + 0x1143800011440, + 0x1144200011445, + 0x1144600011447, + 0x1145E0001145F, + 0x114B3000114B9, + 0x114BA000114BB, + 0x114BF000114C1, + 0x114C2000114C4, + 0x115B2000115B6, + 0x115BC000115BE, + 0x115BF000115C1, + 0x115DC000115DE, + 0x116330001163B, + 0x1163D0001163E, + 0x1163F00011641, + 0x116AB000116AC, + 0x116AD000116AE, + 0x116B0000116B6, + 0x116B7000116B8, + 0x1171D0001171E, + 0x1171F00011720, + 0x1172200011726, + 0x117270001172C, + 0x1182F00011838, + 0x118390001183B, + 0x1193B0001193D, + 0x1193E0001193F, + 0x1194300011944, + 0x119D4000119D8, + 0x119DA000119DC, + 0x119E0000119E1, + 0x11A0100011A0B, + 0x11A3300011A39, + 0x11A3B00011A3F, + 0x11A4700011A48, + 0x11A5100011A57, + 0x11A5900011A5C, + 0x11A8A00011A97, + 0x11A9800011A9A, + 0x11B6000011B61, + 0x11B6200011B65, + 0x11B6600011B67, + 0x11C3000011C37, + 0x11C3800011C3E, + 0x11C3F00011C40, + 0x11C9200011CA8, + 0x11CAA00011CB1, + 0x11CB200011CB4, + 0x11CB500011CB7, + 0x11D3100011D37, + 0x11D3A00011D3B, + 0x11D3C00011D3E, + 0x11D3F00011D46, + 0x11D4700011D48, + 0x11D9000011D92, + 0x11D9500011D96, + 0x11D9700011D98, + 0x11DF000011DF1, + 0x11EF300011EF5, + 0x11F0000011F02, + 0x11F3600011F3B, + 0x11F4000011F41, + 0x11F4200011F43, + 0x11F5A00011F5B, + 0x1343000013441, + 0x1344700013456, + 0x1611E0001612A, + 0x1612D00016130, + 0x16AF000016AF5, + 0x16B3000016B37, + 0x16F4F00016F50, + 0x16F8F00016F93, + 0x16FE400016FE5, + 0x1BC9D0001BC9F, + 0x1BCA00001BCA4, + 0x1CF000001CF2E, + 0x1CF300001CF47, + 0x1D1270001D129, + 0x1D1670001D16A, + 0x1D1730001D183, + 0x1D1850001D18C, + 0x1D1AA0001D1AE, + 0x1D2420001D245, + 0x1D25B0001D25D, + 0x1DA000001DA37, + 0x1DA3B0001DA6D, + 0x1DA750001DA76, + 0x1DA840001DA85, + 0x1DA9B0001DAA0, + 0x1DAA10001DAB0, + 0x1E0000001E007, + 0x1E0080001E019, + 0x1E01B0001E022, + 0x1E0230001E025, + 0x1E0260001E02B, + 0x1E08F0001E090, + 0x1E1300001E137, + 0x1E2AE0001E2AF, + 0x1E2EC0001E2F0, + 0x1E4EC0001E4F0, + 0x1E5EE0001E5F0, + 0x1E6E30001E6E4, + 0x1E6E60001E6E7, + 0x1E6EE0001E6F0, + 0x1E6F50001E6F6, + 0x1E8D00001E8D7, + 0x1E9440001E94C, + 0xE0001000E0002, + 0xE0020000E0080, + 0xE0100000E01F0, + ), +} + + +codepoint_classes = { + "PVALID": ( + 0x2D0000002E, + 0x300000003A, + 0x610000007B, + 0xDF000000F7, + 0xF800000100, + 0x10100000102, + 0x10300000104, + 0x10500000106, + 0x10700000108, + 0x1090000010A, + 0x10B0000010C, + 0x10D0000010E, + 0x10F00000110, + 0x11100000112, + 0x11300000114, + 0x11500000116, + 0x11700000118, + 0x1190000011A, + 0x11B0000011C, + 0x11D0000011E, + 0x11F00000120, + 0x12100000122, + 0x12300000124, + 0x12500000126, + 0x12700000128, + 0x1290000012A, + 0x12B0000012C, + 0x12D0000012E, + 0x12F00000130, + 0x13100000132, + 0x13500000136, + 0x13700000139, + 0x13A0000013B, + 0x13C0000013D, + 0x13E0000013F, + 0x14200000143, + 0x14400000145, + 0x14600000147, + 0x14800000149, + 0x14B0000014C, + 0x14D0000014E, + 0x14F00000150, + 0x15100000152, + 0x15300000154, + 0x15500000156, + 0x15700000158, + 0x1590000015A, + 0x15B0000015C, + 0x15D0000015E, + 0x15F00000160, + 0x16100000162, + 0x16300000164, + 0x16500000166, + 0x16700000168, + 0x1690000016A, + 0x16B0000016C, + 0x16D0000016E, + 0x16F00000170, + 0x17100000172, + 0x17300000174, + 0x17500000176, + 0x17700000178, + 0x17A0000017B, + 0x17C0000017D, + 0x17E0000017F, + 0x18000000181, + 0x18300000184, + 0x18500000186, + 0x18800000189, + 0x18C0000018E, + 0x19200000193, + 0x19500000196, + 0x1990000019C, + 0x19E0000019F, + 0x1A1000001A2, + 0x1A3000001A4, + 0x1A5000001A6, + 0x1A8000001A9, + 0x1AA000001AC, + 0x1AD000001AE, + 0x1B0000001B1, + 0x1B4000001B5, + 0x1B6000001B7, + 0x1B9000001BC, + 0x1BD000001C4, + 0x1CE000001CF, + 0x1D0000001D1, + 0x1D2000001D3, + 0x1D4000001D5, + 0x1D6000001D7, + 0x1D8000001D9, + 0x1DA000001DB, + 0x1DC000001DE, + 0x1DF000001E0, + 0x1E1000001E2, + 0x1E3000001E4, + 0x1E5000001E6, + 0x1E7000001E8, + 0x1E9000001EA, + 0x1EB000001EC, + 0x1ED000001EE, + 0x1EF000001F1, + 0x1F5000001F6, + 0x1F9000001FA, + 0x1FB000001FC, + 0x1FD000001FE, + 0x1FF00000200, + 0x20100000202, + 0x20300000204, + 0x20500000206, + 0x20700000208, + 0x2090000020A, + 0x20B0000020C, + 0x20D0000020E, + 0x20F00000210, + 0x21100000212, + 0x21300000214, + 0x21500000216, + 0x21700000218, + 0x2190000021A, + 0x21B0000021C, + 0x21D0000021E, + 0x21F00000220, + 0x22100000222, + 0x22300000224, + 0x22500000226, + 0x22700000228, + 0x2290000022A, + 0x22B0000022C, + 0x22D0000022E, + 0x22F00000230, + 0x23100000232, + 0x2330000023A, + 0x23C0000023D, + 0x23F00000241, + 0x24200000243, + 0x24700000248, + 0x2490000024A, + 0x24B0000024C, + 0x24D0000024E, + 0x24F000002B0, + 0x2B9000002C2, + 0x2C6000002D2, + 0x2EC000002ED, + 0x2EE000002EF, + 0x30000000340, + 0x34200000343, + 0x3460000034F, + 0x35000000370, + 0x37100000372, + 0x37300000374, + 0x37700000378, + 0x37B0000037E, + 0x39000000391, + 0x3AC000003CF, + 0x3D7000003D8, + 0x3D9000003DA, + 0x3DB000003DC, + 0x3DD000003DE, + 0x3DF000003E0, + 0x3E1000003E2, + 0x3E3000003E4, + 0x3E5000003E6, + 0x3E7000003E8, + 0x3E9000003EA, + 0x3EB000003EC, + 0x3ED000003EE, + 0x3EF000003F0, + 0x3F3000003F4, + 0x3F8000003F9, + 0x3FB000003FD, + 0x43000000460, + 0x46100000462, + 0x46300000464, + 0x46500000466, + 0x46700000468, + 0x4690000046A, + 0x46B0000046C, + 0x46D0000046E, + 0x46F00000470, + 0x47100000472, + 0x47300000474, + 0x47500000476, + 0x47700000478, + 0x4790000047A, + 0x47B0000047C, + 0x47D0000047E, + 0x47F00000480, + 0x48100000482, + 0x48300000488, + 0x48B0000048C, + 0x48D0000048E, + 0x48F00000490, + 0x49100000492, + 0x49300000494, + 0x49500000496, + 0x49700000498, + 0x4990000049A, + 0x49B0000049C, + 0x49D0000049E, + 0x49F000004A0, + 0x4A1000004A2, + 0x4A3000004A4, + 0x4A5000004A6, + 0x4A7000004A8, + 0x4A9000004AA, + 0x4AB000004AC, + 0x4AD000004AE, + 0x4AF000004B0, + 0x4B1000004B2, + 0x4B3000004B4, + 0x4B5000004B6, + 0x4B7000004B8, + 0x4B9000004BA, + 0x4BB000004BC, + 0x4BD000004BE, + 0x4BF000004C0, + 0x4C2000004C3, + 0x4C4000004C5, + 0x4C6000004C7, + 0x4C8000004C9, + 0x4CA000004CB, + 0x4CC000004CD, + 0x4CE000004D0, + 0x4D1000004D2, + 0x4D3000004D4, + 0x4D5000004D6, + 0x4D7000004D8, + 0x4D9000004DA, + 0x4DB000004DC, + 0x4DD000004DE, + 0x4DF000004E0, + 0x4E1000004E2, + 0x4E3000004E4, + 0x4E5000004E6, + 0x4E7000004E8, + 0x4E9000004EA, + 0x4EB000004EC, + 0x4ED000004EE, + 0x4EF000004F0, + 0x4F1000004F2, + 0x4F3000004F4, + 0x4F5000004F6, + 0x4F7000004F8, + 0x4F9000004FA, + 0x4FB000004FC, + 0x4FD000004FE, + 0x4FF00000500, + 0x50100000502, + 0x50300000504, + 0x50500000506, + 0x50700000508, + 0x5090000050A, + 0x50B0000050C, + 0x50D0000050E, + 0x50F00000510, + 0x51100000512, + 0x51300000514, + 0x51500000516, + 0x51700000518, + 0x5190000051A, + 0x51B0000051C, + 0x51D0000051E, + 0x51F00000520, + 0x52100000522, + 0x52300000524, + 0x52500000526, + 0x52700000528, + 0x5290000052A, + 0x52B0000052C, + 0x52D0000052E, + 0x52F00000530, + 0x5590000055A, + 0x56000000587, + 0x58800000589, + 0x591000005BE, + 0x5BF000005C0, + 0x5C1000005C3, + 0x5C4000005C6, + 0x5C7000005CA, + 0x5D0000005EB, + 0x5EF000005F3, + 0x6100000061B, + 0x62000000640, + 0x64100000660, + 0x66E00000675, + 0x679000006D4, + 0x6D5000006DD, + 0x6DF000006E9, + 0x6EA000006F0, + 0x6FA00000700, + 0x7100000074B, + 0x74D000007B2, + 0x7C0000007F6, + 0x7FD000007FE, + 0x8000000082E, + 0x8400000085C, + 0x8600000086B, + 0x87000000888, + 0x88900000890, + 0x897000008E2, + 0x8E300000958, + 0x96000000964, + 0x96600000970, + 0x97100000984, + 0x9850000098D, + 0x98F00000991, + 0x993000009A9, + 0x9AA000009B1, + 0x9B2000009B3, + 0x9B6000009BA, + 0x9BC000009C5, + 0x9C7000009C9, + 0x9CB000009CF, + 0x9D7000009D8, + 0x9E0000009E4, + 0x9E6000009F2, + 0x9FC000009FD, + 0x9FE000009FF, + 0xA0100000A04, + 0xA0500000A0B, + 0xA0F00000A11, + 0xA1300000A29, + 0xA2A00000A31, + 0xA3200000A33, + 0xA3500000A36, + 0xA3800000A3A, + 0xA3C00000A3D, + 0xA3E00000A43, + 0xA4700000A49, + 0xA4B00000A4E, + 0xA5100000A52, + 0xA5C00000A5D, + 0xA6600000A76, + 0xA8100000A84, + 0xA8500000A8E, + 0xA8F00000A92, + 0xA9300000AA9, + 0xAAA00000AB1, + 0xAB200000AB4, + 0xAB500000ABA, + 0xABC00000AC6, + 0xAC700000ACA, + 0xACB00000ACE, + 0xAD000000AD1, + 0xAE000000AE4, + 0xAE600000AF0, + 0xAF900000B00, + 0xB0100000B04, + 0xB0500000B0D, + 0xB0F00000B11, + 0xB1300000B29, + 0xB2A00000B31, + 0xB3200000B34, + 0xB3500000B3A, + 0xB3C00000B45, + 0xB4700000B49, + 0xB4B00000B4E, + 0xB5300000B58, + 0xB5F00000B64, + 0xB6600000B70, + 0xB7100000B72, + 0xB8200000B84, + 0xB8500000B8B, + 0xB8E00000B91, + 0xB9200000B96, + 0xB9900000B9B, + 0xB9C00000B9D, + 0xB9E00000BA0, + 0xBA300000BA5, + 0xBA800000BAB, + 0xBAE00000BBA, + 0xBBE00000BC3, + 0xBC600000BC9, + 0xBCA00000BCE, + 0xBD000000BD1, + 0xBD700000BD8, + 0xBE600000BF0, + 0xC0000000C0D, + 0xC0E00000C11, + 0xC1200000C29, + 0xC2A00000C3A, + 0xC3C00000C45, + 0xC4600000C49, + 0xC4A00000C4E, + 0xC5500000C57, + 0xC5800000C5B, + 0xC5C00000C5E, + 0xC6000000C64, + 0xC6600000C70, + 0xC8000000C84, + 0xC8500000C8D, + 0xC8E00000C91, + 0xC9200000CA9, + 0xCAA00000CB4, + 0xCB500000CBA, + 0xCBC00000CC5, + 0xCC600000CC9, + 0xCCA00000CCE, + 0xCD500000CD7, + 0xCDC00000CDF, + 0xCE000000CE4, + 0xCE600000CF0, + 0xCF100000CF4, + 0xD0000000D0D, + 0xD0E00000D11, + 0xD1200000D45, + 0xD4600000D49, + 0xD4A00000D4F, + 0xD5400000D58, + 0xD5F00000D64, + 0xD6600000D70, + 0xD7A00000D80, + 0xD8100000D84, + 0xD8500000D97, + 0xD9A00000DB2, + 0xDB300000DBC, + 0xDBD00000DBE, + 0xDC000000DC7, + 0xDCA00000DCB, + 0xDCF00000DD5, + 0xDD600000DD7, + 0xDD800000DE0, + 0xDE600000DF0, + 0xDF200000DF4, + 0xE0100000E33, + 0xE3400000E3B, + 0xE4000000E4F, + 0xE5000000E5A, + 0xE8100000E83, + 0xE8400000E85, + 0xE8600000E8B, + 0xE8C00000EA4, + 0xEA500000EA6, + 0xEA700000EB3, + 0xEB400000EBE, + 0xEC000000EC5, + 0xEC600000EC7, + 0xEC800000ECF, + 0xED000000EDA, + 0xEDE00000EE0, + 0xF0000000F01, + 0xF0B00000F0C, + 0xF1800000F1A, + 0xF2000000F2A, + 0xF3500000F36, + 0xF3700000F38, + 0xF3900000F3A, + 0xF3E00000F43, + 0xF4400000F48, + 0xF4900000F4D, + 0xF4E00000F52, + 0xF5300000F57, + 0xF5800000F5C, + 0xF5D00000F69, + 0xF6A00000F6D, + 0xF7100000F73, + 0xF7400000F75, + 0xF7A00000F81, + 0xF8200000F85, + 0xF8600000F93, + 0xF9400000F98, + 0xF9900000F9D, + 0xF9E00000FA2, + 0xFA300000FA7, + 0xFA800000FAC, + 0xFAD00000FB9, + 0xFBA00000FBD, + 0xFC600000FC7, + 0x10000000104A, + 0x10500000109E, + 0x10D0000010FB, + 0x10FD00001100, + 0x120000001249, + 0x124A0000124E, + 0x125000001257, + 0x125800001259, + 0x125A0000125E, + 0x126000001289, + 0x128A0000128E, + 0x1290000012B1, + 0x12B2000012B6, + 0x12B8000012BF, + 0x12C0000012C1, + 0x12C2000012C6, + 0x12C8000012D7, + 0x12D800001311, + 0x131200001316, + 0x13180000135B, + 0x135D00001360, + 0x138000001390, + 0x13A0000013F6, + 0x14010000166D, + 0x166F00001680, + 0x16810000169B, + 0x16A0000016EB, + 0x16F1000016F9, + 0x170000001716, + 0x171F00001735, + 0x174000001754, + 0x17600000176D, + 0x176E00001771, + 0x177200001774, + 0x1780000017B4, + 0x17B6000017D4, + 0x17D7000017D8, + 0x17DC000017DE, + 0x17E0000017EA, + 0x18100000181A, + 0x182000001879, + 0x1880000018AB, + 0x18B0000018F6, + 0x19000000191F, + 0x19200000192C, + 0x19300000193C, + 0x19460000196E, + 0x197000001975, + 0x1980000019AC, + 0x19B0000019CA, + 0x19D0000019DA, + 0x1A0000001A1C, + 0x1A2000001A5F, + 0x1A6000001A7D, + 0x1A7F00001A8A, + 0x1A9000001A9A, + 0x1AA700001AA8, + 0x1AB000001ABE, + 0x1ABF00001AF1, + 0x1B0000001B4D, + 0x1B5000001B5A, + 0x1B6B00001B74, + 0x1B8000001BF4, + 0x1C0000001C38, + 0x1C4000001C4A, + 0x1C4D00001C7E, + 0x1C8A00001C8B, + 0x1CD000001CD3, + 0x1CD400001CFB, + 0x1D0000001D2C, + 0x1D2F00001D30, + 0x1D3B00001D3C, + 0x1D4E00001D4F, + 0x1D6B00001D78, + 0x1D7900001D9B, + 0x1DC000001E00, + 0x1E0100001E02, + 0x1E0300001E04, + 0x1E0500001E06, + 0x1E0700001E08, + 0x1E0900001E0A, + 0x1E0B00001E0C, + 0x1E0D00001E0E, + 0x1E0F00001E10, + 0x1E1100001E12, + 0x1E1300001E14, + 0x1E1500001E16, + 0x1E1700001E18, + 0x1E1900001E1A, + 0x1E1B00001E1C, + 0x1E1D00001E1E, + 0x1E1F00001E20, + 0x1E2100001E22, + 0x1E2300001E24, + 0x1E2500001E26, + 0x1E2700001E28, + 0x1E2900001E2A, + 0x1E2B00001E2C, + 0x1E2D00001E2E, + 0x1E2F00001E30, + 0x1E3100001E32, + 0x1E3300001E34, + 0x1E3500001E36, + 0x1E3700001E38, + 0x1E3900001E3A, + 0x1E3B00001E3C, + 0x1E3D00001E3E, + 0x1E3F00001E40, + 0x1E4100001E42, + 0x1E4300001E44, + 0x1E4500001E46, + 0x1E4700001E48, + 0x1E4900001E4A, + 0x1E4B00001E4C, + 0x1E4D00001E4E, + 0x1E4F00001E50, + 0x1E5100001E52, + 0x1E5300001E54, + 0x1E5500001E56, + 0x1E5700001E58, + 0x1E5900001E5A, + 0x1E5B00001E5C, + 0x1E5D00001E5E, + 0x1E5F00001E60, + 0x1E6100001E62, + 0x1E6300001E64, + 0x1E6500001E66, + 0x1E6700001E68, + 0x1E6900001E6A, + 0x1E6B00001E6C, + 0x1E6D00001E6E, + 0x1E6F00001E70, + 0x1E7100001E72, + 0x1E7300001E74, + 0x1E7500001E76, + 0x1E7700001E78, + 0x1E7900001E7A, + 0x1E7B00001E7C, + 0x1E7D00001E7E, + 0x1E7F00001E80, + 0x1E8100001E82, + 0x1E8300001E84, + 0x1E8500001E86, + 0x1E8700001E88, + 0x1E8900001E8A, + 0x1E8B00001E8C, + 0x1E8D00001E8E, + 0x1E8F00001E90, + 0x1E9100001E92, + 0x1E9300001E94, + 0x1E9500001E9A, + 0x1E9C00001E9E, + 0x1E9F00001EA0, + 0x1EA100001EA2, + 0x1EA300001EA4, + 0x1EA500001EA6, + 0x1EA700001EA8, + 0x1EA900001EAA, + 0x1EAB00001EAC, + 0x1EAD00001EAE, + 0x1EAF00001EB0, + 0x1EB100001EB2, + 0x1EB300001EB4, + 0x1EB500001EB6, + 0x1EB700001EB8, + 0x1EB900001EBA, + 0x1EBB00001EBC, + 0x1EBD00001EBE, + 0x1EBF00001EC0, + 0x1EC100001EC2, + 0x1EC300001EC4, + 0x1EC500001EC6, + 0x1EC700001EC8, + 0x1EC900001ECA, + 0x1ECB00001ECC, + 0x1ECD00001ECE, + 0x1ECF00001ED0, + 0x1ED100001ED2, + 0x1ED300001ED4, + 0x1ED500001ED6, + 0x1ED700001ED8, + 0x1ED900001EDA, + 0x1EDB00001EDC, + 0x1EDD00001EDE, + 0x1EDF00001EE0, + 0x1EE100001EE2, + 0x1EE300001EE4, + 0x1EE500001EE6, + 0x1EE700001EE8, + 0x1EE900001EEA, + 0x1EEB00001EEC, + 0x1EED00001EEE, + 0x1EEF00001EF0, + 0x1EF100001EF2, + 0x1EF300001EF4, + 0x1EF500001EF6, + 0x1EF700001EF8, + 0x1EF900001EFA, + 0x1EFB00001EFC, + 0x1EFD00001EFE, + 0x1EFF00001F08, + 0x1F1000001F16, + 0x1F2000001F28, + 0x1F3000001F38, + 0x1F4000001F46, + 0x1F5000001F58, + 0x1F6000001F68, + 0x1F7000001F71, + 0x1F7200001F73, + 0x1F7400001F75, + 0x1F7600001F77, + 0x1F7800001F79, + 0x1F7A00001F7B, + 0x1F7C00001F7D, + 0x1FB000001FB2, + 0x1FB600001FB7, + 0x1FC600001FC7, + 0x1FD000001FD3, + 0x1FD600001FD8, + 0x1FE000001FE3, + 0x1FE400001FE8, + 0x1FF600001FF7, + 0x208F00002090, + 0x214E0000214F, + 0x218400002185, + 0x2C3000002C60, + 0x2C6100002C62, + 0x2C6500002C67, + 0x2C6800002C69, + 0x2C6A00002C6B, + 0x2C6C00002C6D, + 0x2C7100002C72, + 0x2C7300002C75, + 0x2C7600002C7C, + 0x2C8100002C82, + 0x2C8300002C84, + 0x2C8500002C86, + 0x2C8700002C88, + 0x2C8900002C8A, + 0x2C8B00002C8C, + 0x2C8D00002C8E, + 0x2C8F00002C90, + 0x2C9100002C92, + 0x2C9300002C94, + 0x2C9500002C96, + 0x2C9700002C98, + 0x2C9900002C9A, + 0x2C9B00002C9C, + 0x2C9D00002C9E, + 0x2C9F00002CA0, + 0x2CA100002CA2, + 0x2CA300002CA4, + 0x2CA500002CA6, + 0x2CA700002CA8, + 0x2CA900002CAA, + 0x2CAB00002CAC, + 0x2CAD00002CAE, + 0x2CAF00002CB0, + 0x2CB100002CB2, + 0x2CB300002CB4, + 0x2CB500002CB6, + 0x2CB700002CB8, + 0x2CB900002CBA, + 0x2CBB00002CBC, + 0x2CBD00002CBE, + 0x2CBF00002CC0, + 0x2CC100002CC2, + 0x2CC300002CC4, + 0x2CC500002CC6, + 0x2CC700002CC8, + 0x2CC900002CCA, + 0x2CCB00002CCC, + 0x2CCD00002CCE, + 0x2CCF00002CD0, + 0x2CD100002CD2, + 0x2CD300002CD4, + 0x2CD500002CD6, + 0x2CD700002CD8, + 0x2CD900002CDA, + 0x2CDB00002CDC, + 0x2CDD00002CDE, + 0x2CDF00002CE0, + 0x2CE100002CE2, + 0x2CE300002CE5, + 0x2CEC00002CED, + 0x2CEE00002CF2, + 0x2CF300002CF4, + 0x2D0000002D26, + 0x2D2700002D28, + 0x2D2D00002D2E, + 0x2D3000002D68, + 0x2D7F00002D97, + 0x2DA000002DA7, + 0x2DA800002DAF, + 0x2DB000002DB7, + 0x2DB800002DBF, + 0x2DC000002DC7, + 0x2DC800002DCF, + 0x2DD000002DD7, + 0x2DD800002DDF, + 0x2DE000002E00, + 0x2E2F00002E30, + 0x300500003008, + 0x302A0000302E, + 0x303C0000303D, + 0x304100003097, + 0x30990000309B, + 0x309D0000309F, + 0x30A1000030FB, + 0x30FC000030FF, + 0x310500003130, + 0x31A0000031C0, + 0x31F000003200, + 0x340000004DC0, + 0x4E000000A48D, + 0xA4D00000A4FE, + 0xA5000000A60D, + 0xA6100000A62C, + 0xA6410000A642, + 0xA6430000A644, + 0xA6450000A646, + 0xA6470000A648, + 0xA6490000A64A, + 0xA64B0000A64C, + 0xA64D0000A64E, + 0xA64F0000A650, + 0xA6510000A652, + 0xA6530000A654, + 0xA6550000A656, + 0xA6570000A658, + 0xA6590000A65A, + 0xA65B0000A65C, + 0xA65D0000A65E, + 0xA65F0000A660, + 0xA6610000A662, + 0xA6630000A664, + 0xA6650000A666, + 0xA6670000A668, + 0xA6690000A66A, + 0xA66B0000A66C, + 0xA66D0000A670, + 0xA6740000A67E, + 0xA67F0000A680, + 0xA6810000A682, + 0xA6830000A684, + 0xA6850000A686, + 0xA6870000A688, + 0xA6890000A68A, + 0xA68B0000A68C, + 0xA68D0000A68E, + 0xA68F0000A690, + 0xA6910000A692, + 0xA6930000A694, + 0xA6950000A696, + 0xA6970000A698, + 0xA6990000A69A, + 0xA69B0000A69C, + 0xA69E0000A6E6, + 0xA6F00000A6F2, + 0xA7170000A720, + 0xA7230000A724, + 0xA7250000A726, + 0xA7270000A728, + 0xA7290000A72A, + 0xA72B0000A72C, + 0xA72D0000A72E, + 0xA72F0000A732, + 0xA7330000A734, + 0xA7350000A736, + 0xA7370000A738, + 0xA7390000A73A, + 0xA73B0000A73C, + 0xA73D0000A73E, + 0xA73F0000A740, + 0xA7410000A742, + 0xA7430000A744, + 0xA7450000A746, + 0xA7470000A748, + 0xA7490000A74A, + 0xA74B0000A74C, + 0xA74D0000A74E, + 0xA74F0000A750, + 0xA7510000A752, + 0xA7530000A754, + 0xA7550000A756, + 0xA7570000A758, + 0xA7590000A75A, + 0xA75B0000A75C, + 0xA75D0000A75E, + 0xA75F0000A760, + 0xA7610000A762, + 0xA7630000A764, + 0xA7650000A766, + 0xA7670000A768, + 0xA7690000A76A, + 0xA76B0000A76C, + 0xA76D0000A76E, + 0xA76F0000A770, + 0xA7710000A779, + 0xA77A0000A77B, + 0xA77C0000A77D, + 0xA77F0000A780, + 0xA7810000A782, + 0xA7830000A784, + 0xA7850000A786, + 0xA7870000A789, + 0xA78C0000A78D, + 0xA78E0000A790, + 0xA7910000A792, + 0xA7930000A796, + 0xA7970000A798, + 0xA7990000A79A, + 0xA79B0000A79C, + 0xA79D0000A79E, + 0xA79F0000A7A0, + 0xA7A10000A7A2, + 0xA7A30000A7A4, + 0xA7A50000A7A6, + 0xA7A70000A7A8, + 0xA7A90000A7AA, + 0xA7AF0000A7B0, + 0xA7B50000A7B6, + 0xA7B70000A7B8, + 0xA7B90000A7BA, + 0xA7BB0000A7BC, + 0xA7BD0000A7BE, + 0xA7BF0000A7C0, + 0xA7C10000A7C2, + 0xA7C30000A7C4, + 0xA7C80000A7C9, + 0xA7CA0000A7CB, + 0xA7CD0000A7CE, + 0xA7CF0000A7D0, + 0xA7D10000A7D2, + 0xA7D30000A7D4, + 0xA7D50000A7D6, + 0xA7D70000A7D8, + 0xA7D90000A7DA, + 0xA7DB0000A7DC, + 0xA7F60000A7F8, + 0xA7FA0000A828, + 0xA82C0000A82D, + 0xA8400000A874, + 0xA8800000A8C6, + 0xA8D00000A8DA, + 0xA8E00000A8F8, + 0xA8FB0000A8FC, + 0xA8FD0000A92E, + 0xA9300000A954, + 0xA9800000A9C1, + 0xA9CF0000A9DA, + 0xA9E00000A9FF, + 0xAA000000AA37, + 0xAA400000AA4E, + 0xAA500000AA5A, + 0xAA600000AA77, + 0xAA7A0000AAC3, + 0xAADB0000AADE, + 0xAAE00000AAF0, + 0xAAF20000AAF7, + 0xAB010000AB07, + 0xAB090000AB0F, + 0xAB110000AB17, + 0xAB200000AB27, + 0xAB280000AB2F, + 0xAB300000AB5B, + 0xAB600000AB69, + 0xABC00000ABEB, + 0xABEC0000ABEE, + 0xABF00000ABFA, + 0xAC000000D7A4, + 0xFA0E0000FA10, + 0xFA110000FA12, + 0xFA130000FA15, + 0xFA1F0000FA20, + 0xFA210000FA22, + 0xFA230000FA25, + 0xFA270000FA2A, + 0xFB1E0000FB1F, + 0xFE200000FE30, + 0xFE730000FE74, + 0x100000001000C, + 0x1000D00010027, + 0x100280001003B, + 0x1003C0001003E, + 0x1003F0001004E, + 0x100500001005E, + 0x10080000100FB, + 0x101FD000101FE, + 0x102800001029D, + 0x102A0000102D1, + 0x102E0000102E1, + 0x1030000010320, + 0x1032D00010341, + 0x103420001034A, + 0x103500001037B, + 0x103800001039E, + 0x103A0000103C4, + 0x103C8000103D0, + 0x104280001049E, + 0x104A0000104AA, + 0x104D8000104FC, + 0x1050000010528, + 0x1053000010564, + 0x10597000105A2, + 0x105A3000105B2, + 0x105B3000105BA, + 0x105BB000105BD, + 0x105C0000105F4, + 0x1060000010737, + 0x1074000010756, + 0x1076000010768, + 0x1078000010781, + 0x1080000010806, + 0x1080800010809, + 0x1080A00010836, + 0x1083700010839, + 0x1083C0001083D, + 0x1083F00010856, + 0x1086000010877, + 0x108800001089F, + 0x108E0000108F3, + 0x108F4000108F6, + 0x1090000010916, + 0x109200001093A, + 0x109400001095A, + 0x10980000109B8, + 0x109BE000109C0, + 0x10A0000010A04, + 0x10A0500010A07, + 0x10A0C00010A14, + 0x10A1500010A18, + 0x10A1900010A36, + 0x10A3800010A3B, + 0x10A3F00010A40, + 0x10A6000010A7D, + 0x10A8000010A9D, + 0x10AC000010AC8, + 0x10AC900010AE7, + 0x10B0000010B36, + 0x10B4000010B56, + 0x10B6000010B73, + 0x10B8000010B92, + 0x10C0000010C49, + 0x10CC000010CF3, + 0x10D0000010D28, + 0x10D3000010D3A, + 0x10D4000010D50, + 0x10D6900010D6E, + 0x10D6F00010D86, + 0x10E8000010EAA, + 0x10EAB00010EAD, + 0x10EB000010EB2, + 0x10EC200010EC8, + 0x10ECB00010ED0, + 0x10ED900010EEF, + 0x10EF000010F1D, + 0x10F2700010F28, + 0x10F3000010F51, + 0x10F7000010F86, + 0x10FB000010FC5, + 0x10FE000010FF7, + 0x1100000011047, + 0x1106600011076, + 0x1107F000110BB, + 0x110C2000110C3, + 0x110D0000110E9, + 0x110F0000110FA, + 0x1110000011135, + 0x1113600011140, + 0x1114400011148, + 0x1115000011174, + 0x1117600011177, + 0x11180000111C5, + 0x111C9000111CD, + 0x111CE000111DB, + 0x111DC000111DD, + 0x1120000011212, + 0x1121300011238, + 0x1123E00011242, + 0x1128000011287, + 0x1128800011289, + 0x1128A0001128E, + 0x1128F0001129E, + 0x1129F000112A9, + 0x112B0000112EB, + 0x112F0000112FA, + 0x1130000011304, + 0x113050001130D, + 0x1130F00011311, + 0x1131300011329, + 0x1132A00011331, + 0x1133200011334, + 0x113350001133A, + 0x1133B00011345, + 0x1134700011349, + 0x1134B0001134E, + 0x1135000011351, + 0x1135700011358, + 0x1135D00011364, + 0x113660001136D, + 0x1137000011375, + 0x113800001138A, + 0x1138B0001138C, + 0x1138E0001138F, + 0x11390000113B6, + 0x113B7000113C1, + 0x113C2000113C3, + 0x113C5000113C6, + 0x113C7000113CB, + 0x113CC000113D4, + 0x113E1000113E3, + 0x114000001144B, + 0x114500001145A, + 0x1145E00011462, + 0x11480000114C6, + 0x114C7000114C8, + 0x114D0000114DA, + 0x11580000115B6, + 0x115B8000115C1, + 0x115D8000115DE, + 0x1160000011641, + 0x1164400011645, + 0x116500001165A, + 0x11680000116B9, + 0x116C0000116CA, + 0x116D0000116E4, + 0x117000001171B, + 0x1171D0001172C, + 0x117300001173A, + 0x1174000011747, + 0x118000001183B, + 0x118C0000118EA, + 0x118FF00011907, + 0x119090001190A, + 0x1190C00011914, + 0x1191500011917, + 0x1191800011936, + 0x1193700011939, + 0x1193B00011944, + 0x119500001195A, + 0x119A0000119A8, + 0x119AA000119D8, + 0x119DA000119E2, + 0x119E3000119E5, + 0x11A0000011A3F, + 0x11A4700011A48, + 0x11A5000011A9A, + 0x11A9D00011A9E, + 0x11AB000011AF9, + 0x11B0A00011B0B, + 0x11B6000011B68, + 0x11BC000011BE1, + 0x11BF000011BFA, + 0x11C0000011C09, + 0x11C0A00011C37, + 0x11C3800011C41, + 0x11C5000011C5A, + 0x11C7200011C90, + 0x11C9200011CA8, + 0x11CA900011CB7, + 0x11D0000011D07, + 0x11D0800011D0A, + 0x11D0B00011D37, + 0x11D3A00011D3B, + 0x11D3C00011D3E, + 0x11D3F00011D48, + 0x11D5000011D5A, + 0x11D6000011D66, + 0x11D6700011D69, + 0x11D6A00011D8F, + 0x11D9000011D92, + 0x11D9300011D99, + 0x11DA000011DAA, + 0x11DB000011DDC, + 0x11DE000011DEA, + 0x11DF000011DF2, + 0x11EE000011EF7, + 0x11F0000011F11, + 0x11F1200011F3B, + 0x11F3E00011F43, + 0x11F5000011F5B, + 0x11FB000011FB1, + 0x120000001239A, + 0x1248000012544, + 0x12F9000012FF1, + 0x1300000013430, + 0x1344000013456, + 0x13460000143FB, + 0x1440000014647, + 0x161000001613A, + 0x1680000016A39, + 0x16A4000016A5F, + 0x16A6000016A6A, + 0x16A7000016ABF, + 0x16AC000016ACA, + 0x16AD000016AEE, + 0x16AF000016AF5, + 0x16B0000016B37, + 0x16B4000016B44, + 0x16B5000016B5A, + 0x16B6300016B78, + 0x16B7D00016B90, + 0x16D4000016D6D, + 0x16D7000016D7A, + 0x16E6000016E80, + 0x16EBB00016ED4, + 0x16F0000016F4B, + 0x16F4F00016F88, + 0x16F8F00016FA0, + 0x16FE000016FE2, + 0x16FE300016FE5, + 0x16FF000016FF4, + 0x1700000018CDB, + 0x18CFF00018D21, + 0x18D8000018DF3, + 0x18E0000019192, + 0x191A0000191D3, + 0x1AFF00001AFF4, + 0x1AFF50001AFFC, + 0x1AFFD0001AFFF, + 0x1B0000001B123, + 0x1B1270001B129, + 0x1B1320001B133, + 0x1B1500001B153, + 0x1B1550001B156, + 0x1B1640001B169, + 0x1B1700001B2FC, + 0x1BC000001BC6B, + 0x1BC700001BC7D, + 0x1BC800001BC89, + 0x1BC900001BC9A, + 0x1BC9D0001BC9F, + 0x1CF000001CF2E, + 0x1CF300001CF47, + 0x1D2500001D253, + 0x1D25B0001D25D, + 0x1D25F0001D260, + 0x1D2800001D282, + 0x1DA000001DA37, + 0x1DA3B0001DA6D, + 0x1DA750001DA76, + 0x1DA840001DA85, + 0x1DA9B0001DAA0, + 0x1DAA10001DAB0, + 0x1DF000001DF40, + 0x1DF410001DF48, + 0x1DF490001DF4A, + 0x1DF4B0001DF4D, + 0x1DF4E0001DF51, + 0x1DF520001DF68, + 0x1DF690001DF6A, + 0x1DF6B0001DF6C, + 0x1DF6D0001DF6E, + 0x1DF6F0001DF72, + 0x1DF730001DF74, + 0x1DF750001DF76, + 0x1DF770001DF78, + 0x1DF790001DF7A, + 0x1DF7B0001DF7C, + 0x1DF7D0001DF7E, + 0x1DF7F0001DF82, + 0x1DF900001DF95, + 0x1DF960001DF97, + 0x1E0000001E007, + 0x1E0080001E019, + 0x1E01B0001E022, + 0x1E0230001E025, + 0x1E0260001E02B, + 0x1E08F0001E090, + 0x1E1000001E12D, + 0x1E1300001E13E, + 0x1E1400001E14A, + 0x1E14E0001E14F, + 0x1E2900001E2AF, + 0x1E2C00001E2FA, + 0x1E4D00001E4FA, + 0x1E5D00001E5FB, + 0x1E6C00001E6DF, + 0x1E6E00001E6F6, + 0x1E6FE0001E700, + 0x1E7E00001E7E7, + 0x1E7E80001E7EC, + 0x1E7ED0001E7EF, + 0x1E7F00001E7FF, + 0x1E8000001E8C5, + 0x1E8D00001E8D7, + 0x1E9220001E94C, + 0x1E9500001E95A, + 0x200000002A6E0, + 0x2A7000002B81F, + 0x2B8200002CEAE, + 0x2CEB00002EBE1, + 0x2EBF00002EE5E, + 0x300000003134B, + 0x313500003347A, + 0x3D0000003FC40, + ), + "CONTEXTJ": (0x200C0000200E,), + "CONTEXTO": ( + 0xB7000000B8, + 0x37500000376, + 0x5F3000005F5, + 0x6600000066A, + 0x6F0000006FA, + 0x30FB000030FC, + ), +} diff --git a/venv/lib/python3.12/site-packages/idna/intranges.py b/venv/lib/python3.12/site-packages/idna/intranges.py new file mode 100644 index 0000000..19d7781 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/intranges.py @@ -0,0 +1,55 @@ +""" +Given a list of integers, made up of (hopefully) a small number of long runs +of consecutive integers, compute a representation of the form +((start1, end1), (start2, end2) ...). Then answer the question "was x present +in the original list?" in time O(log(# runs)). +""" + +import bisect + + +def intranges_from_list(list_: list[int]) -> tuple[int, ...]: + """Represent a list of integers as a sequence of ranges: + ((start_0, end_0), (start_1, end_1), ...), such that the original + integers are exactly those x such that start_i <= x < end_i for some i. + + Ranges are encoded as single integers (start << 32 | end), not as tuples. + """ + + sorted_list = sorted(list_) + ranges = [] + last_write = -1 + for i in range(len(sorted_list)): + if i + 1 < len(sorted_list) and sorted_list[i] == sorted_list[i + 1] - 1: + continue + current_range = sorted_list[last_write + 1 : i + 1] + ranges.append(_encode_range(current_range[0], current_range[-1] + 1)) + last_write = i + + return tuple(ranges) + + +def _encode_range(start: int, end: int) -> int: + return (start << 32) | end + + +def _decode_range(r: int) -> tuple[int, int]: + return (r >> 32), (r & ((1 << 32) - 1)) + + +def intranges_contain(int_: int, ranges: tuple[int, ...]) -> bool: + """Determine if `int_` falls into one of the ranges in `ranges`.""" + tuple_ = _encode_range(int_, 0) + pos = bisect.bisect_left(ranges, tuple_) + # we could be immediately ahead of a tuple (start, end) + # with start < int_ <= end + if pos > 0: + left, right = _decode_range(ranges[pos - 1]) + if left <= int_ < right: + return True + # or we could be immediately behind a tuple (int_, end) + if pos < len(ranges): + left, _ = _decode_range(ranges[pos]) + if left == int_: + return True + return False diff --git a/venv/lib/python3.12/site-packages/idna/package_data.py b/venv/lib/python3.12/site-packages/idna/package_data.py new file mode 100644 index 0000000..2f9ff7e --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/package_data.py @@ -0,0 +1 @@ +__version__ = "3.20" diff --git a/venv/lib/python3.12/site-packages/idna/py.typed b/venv/lib/python3.12/site-packages/idna/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/idna/uts46data.py b/venv/lib/python3.12/site-packages/idna/uts46data.py new file mode 100644 index 0000000..dbfb60f --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/uts46data.py @@ -0,0 +1,17117 @@ +# This file is automatically generated by tools/idna-data + +from __future__ import annotations + +from array import array + +"""IDNA Mapping Table from UTS46.""" + + +__version__ = "18.0.0" + +uts46_starts: array[int] = array( + "I", + ( + 0x0, + 0x1, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x7, + 0x8, + 0x9, + 0xA, + 0xB, + 0xC, + 0xD, + 0xE, + 0xF, + 0x10, + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x16, + 0x17, + 0x18, + 0x19, + 0x1A, + 0x1B, + 0x1C, + 0x1D, + 0x1E, + 0x1F, + 0x20, + 0x21, + 0x22, + 0x23, + 0x24, + 0x25, + 0x26, + 0x27, + 0x28, + 0x29, + 0x2A, + 0x2B, + 0x2C, + 0x2D, + 0x2E, + 0x2F, + 0x30, + 0x31, + 0x32, + 0x33, + 0x34, + 0x35, + 0x36, + 0x37, + 0x38, + 0x39, + 0x3A, + 0x3B, + 0x3C, + 0x3D, + 0x3E, + 0x3F, + 0x40, + 0x41, + 0x42, + 0x43, + 0x44, + 0x45, + 0x46, + 0x47, + 0x48, + 0x49, + 0x4A, + 0x4B, + 0x4C, + 0x4D, + 0x4E, + 0x4F, + 0x50, + 0x51, + 0x52, + 0x53, + 0x54, + 0x55, + 0x56, + 0x57, + 0x58, + 0x59, + 0x5A, + 0x5B, + 0x5C, + 0x5D, + 0x5E, + 0x5F, + 0x60, + 0x61, + 0x62, + 0x63, + 0x64, + 0x65, + 0x66, + 0x67, + 0x68, + 0x69, + 0x6A, + 0x6B, + 0x6C, + 0x6D, + 0x6E, + 0x6F, + 0x70, + 0x71, + 0x72, + 0x73, + 0x74, + 0x75, + 0x76, + 0x77, + 0x78, + 0x79, + 0x7A, + 0x7B, + 0x7C, + 0x7D, + 0x7E, + 0x7F, + 0x80, + 0x81, + 0x82, + 0x83, + 0x84, + 0x85, + 0x86, + 0x87, + 0x88, + 0x89, + 0x8A, + 0x8B, + 0x8C, + 0x8D, + 0x8E, + 0x8F, + 0x90, + 0x91, + 0x92, + 0x93, + 0x94, + 0x95, + 0x96, + 0x97, + 0x98, + 0x99, + 0x9A, + 0x9B, + 0x9C, + 0x9D, + 0x9E, + 0x9F, + 0xA0, + 0xA1, + 0xA2, + 0xA3, + 0xA4, + 0xA5, + 0xA6, + 0xA7, + 0xA8, + 0xA9, + 0xAA, + 0xAB, + 0xAC, + 0xAD, + 0xAE, + 0xAF, + 0xB0, + 0xB1, + 0xB2, + 0xB3, + 0xB4, + 0xB5, + 0xB6, + 0xB7, + 0xB8, + 0xB9, + 0xBA, + 0xBB, + 0xBC, + 0xBD, + 0xBE, + 0xBF, + 0xC0, + 0xC1, + 0xC2, + 0xC3, + 0xC4, + 0xC5, + 0xC6, + 0xC7, + 0xC8, + 0xC9, + 0xCA, + 0xCB, + 0xCC, + 0xCD, + 0xCE, + 0xCF, + 0xD0, + 0xD1, + 0xD2, + 0xD3, + 0xD4, + 0xD5, + 0xD6, + 0xD7, + 0xD8, + 0xD9, + 0xDA, + 0xDB, + 0xDC, + 0xDD, + 0xDE, + 0xDF, + 0xE0, + 0xE1, + 0xE2, + 0xE3, + 0xE4, + 0xE5, + 0xE6, + 0xE7, + 0xE8, + 0xE9, + 0xEA, + 0xEB, + 0xEC, + 0xED, + 0xEE, + 0xEF, + 0xF0, + 0xF1, + 0xF2, + 0xF3, + 0xF4, + 0xF5, + 0xF6, + 0xF7, + 0xF8, + 0xF9, + 0xFA, + 0xFB, + 0xFC, + 0xFD, + 0xFE, + 0xFF, + 0x100, + 0x101, + 0x102, + 0x103, + 0x104, + 0x105, + 0x106, + 0x107, + 0x108, + 0x109, + 0x10A, + 0x10B, + 0x10C, + 0x10D, + 0x10E, + 0x10F, + 0x110, + 0x111, + 0x112, + 0x113, + 0x114, + 0x115, + 0x116, + 0x117, + 0x118, + 0x119, + 0x11A, + 0x11B, + 0x11C, + 0x11D, + 0x11E, + 0x11F, + 0x120, + 0x121, + 0x122, + 0x123, + 0x124, + 0x125, + 0x126, + 0x127, + 0x128, + 0x129, + 0x12A, + 0x12B, + 0x12C, + 0x12D, + 0x12E, + 0x12F, + 0x130, + 0x131, + 0x132, + 0x134, + 0x135, + 0x136, + 0x137, + 0x139, + 0x13A, + 0x13B, + 0x13C, + 0x13D, + 0x13E, + 0x13F, + 0x141, + 0x142, + 0x143, + 0x144, + 0x145, + 0x146, + 0x147, + 0x148, + 0x149, + 0x14A, + 0x14B, + 0x14C, + 0x14D, + 0x14E, + 0x14F, + 0x150, + 0x151, + 0x152, + 0x153, + 0x154, + 0x155, + 0x156, + 0x157, + 0x158, + 0x159, + 0x15A, + 0x15B, + 0x15C, + 0x15D, + 0x15E, + 0x15F, + 0x160, + 0x161, + 0x162, + 0x163, + 0x164, + 0x165, + 0x166, + 0x167, + 0x168, + 0x169, + 0x16A, + 0x16B, + 0x16C, + 0x16D, + 0x16E, + 0x16F, + 0x170, + 0x171, + 0x172, + 0x173, + 0x174, + 0x175, + 0x176, + 0x177, + 0x178, + 0x179, + 0x17A, + 0x17B, + 0x17C, + 0x17D, + 0x17E, + 0x17F, + 0x180, + 0x181, + 0x182, + 0x183, + 0x184, + 0x185, + 0x186, + 0x187, + 0x188, + 0x189, + 0x18A, + 0x18B, + 0x18C, + 0x18E, + 0x18F, + 0x190, + 0x191, + 0x192, + 0x193, + 0x194, + 0x195, + 0x196, + 0x197, + 0x198, + 0x199, + 0x19C, + 0x19D, + 0x19E, + 0x19F, + 0x1A0, + 0x1A1, + 0x1A2, + 0x1A3, + 0x1A4, + 0x1A5, + 0x1A6, + 0x1A7, + 0x1A8, + 0x1A9, + 0x1AA, + 0x1AC, + 0x1AD, + 0x1AE, + 0x1AF, + 0x1B0, + 0x1B1, + 0x1B2, + 0x1B3, + 0x1B4, + 0x1B5, + 0x1B6, + 0x1B7, + 0x1B8, + 0x1B9, + 0x1BC, + 0x1BD, + 0x1C4, + 0x1C7, + 0x1CA, + 0x1CD, + 0x1CE, + 0x1CF, + 0x1D0, + 0x1D1, + 0x1D2, + 0x1D3, + 0x1D4, + 0x1D5, + 0x1D6, + 0x1D7, + 0x1D8, + 0x1D9, + 0x1DA, + 0x1DB, + 0x1DC, + 0x1DE, + 0x1DF, + 0x1E0, + 0x1E1, + 0x1E2, + 0x1E3, + 0x1E4, + 0x1E5, + 0x1E6, + 0x1E7, + 0x1E8, + 0x1E9, + 0x1EA, + 0x1EB, + 0x1EC, + 0x1ED, + 0x1EE, + 0x1EF, + 0x1F1, + 0x1F4, + 0x1F5, + 0x1F6, + 0x1F7, + 0x1F8, + 0x1F9, + 0x1FA, + 0x1FB, + 0x1FC, + 0x1FD, + 0x1FE, + 0x1FF, + 0x200, + 0x201, + 0x202, + 0x203, + 0x204, + 0x205, + 0x206, + 0x207, + 0x208, + 0x209, + 0x20A, + 0x20B, + 0x20C, + 0x20D, + 0x20E, + 0x20F, + 0x210, + 0x211, + 0x212, + 0x213, + 0x214, + 0x215, + 0x216, + 0x217, + 0x218, + 0x219, + 0x21A, + 0x21B, + 0x21C, + 0x21D, + 0x21E, + 0x21F, + 0x220, + 0x221, + 0x222, + 0x223, + 0x224, + 0x225, + 0x226, + 0x227, + 0x228, + 0x229, + 0x22A, + 0x22B, + 0x22C, + 0x22D, + 0x22E, + 0x22F, + 0x230, + 0x231, + 0x232, + 0x233, + 0x23A, + 0x23B, + 0x23C, + 0x23D, + 0x23E, + 0x23F, + 0x241, + 0x242, + 0x243, + 0x244, + 0x245, + 0x246, + 0x247, + 0x248, + 0x249, + 0x24A, + 0x24B, + 0x24C, + 0x24D, + 0x24E, + 0x24F, + 0x2B0, + 0x2B1, + 0x2B2, + 0x2B3, + 0x2B4, + 0x2B5, + 0x2B6, + 0x2B7, + 0x2B8, + 0x2B9, + 0x2D8, + 0x2D9, + 0x2DA, + 0x2DB, + 0x2DC, + 0x2DD, + 0x2DE, + 0x2E0, + 0x2E1, + 0x2E2, + 0x2E3, + 0x2E4, + 0x2E5, + 0x340, + 0x341, + 0x342, + 0x343, + 0x344, + 0x345, + 0x346, + 0x34F, + 0x350, + 0x370, + 0x371, + 0x372, + 0x373, + 0x374, + 0x375, + 0x376, + 0x377, + 0x378, + 0x37A, + 0x37B, + 0x37E, + 0x37F, + 0x380, + 0x384, + 0x385, + 0x386, + 0x387, + 0x388, + 0x389, + 0x38A, + 0x38B, + 0x38C, + 0x38D, + 0x38E, + 0x38F, + 0x390, + 0x391, + 0x392, + 0x393, + 0x394, + 0x395, + 0x396, + 0x397, + 0x398, + 0x399, + 0x39A, + 0x39B, + 0x39C, + 0x39D, + 0x39E, + 0x39F, + 0x3A0, + 0x3A1, + 0x3A2, + 0x3A3, + 0x3A4, + 0x3A5, + 0x3A6, + 0x3A7, + 0x3A8, + 0x3A9, + 0x3AA, + 0x3AB, + 0x3AC, + 0x3C2, + 0x3C3, + 0x3CF, + 0x3D0, + 0x3D1, + 0x3D2, + 0x3D3, + 0x3D4, + 0x3D5, + 0x3D6, + 0x3D7, + 0x3D8, + 0x3D9, + 0x3DA, + 0x3DB, + 0x3DC, + 0x3DD, + 0x3DE, + 0x3DF, + 0x3E0, + 0x3E1, + 0x3E2, + 0x3E3, + 0x3E4, + 0x3E5, + 0x3E6, + 0x3E7, + 0x3E8, + 0x3E9, + 0x3EA, + 0x3EB, + 0x3EC, + 0x3ED, + 0x3EE, + 0x3EF, + 0x3F0, + 0x3F1, + 0x3F2, + 0x3F3, + 0x3F4, + 0x3F5, + 0x3F6, + 0x3F7, + 0x3F8, + 0x3F9, + 0x3FA, + 0x3FB, + 0x3FD, + 0x3FE, + 0x3FF, + 0x400, + 0x401, + 0x402, + 0x403, + 0x404, + 0x405, + 0x406, + 0x407, + 0x408, + 0x409, + 0x40A, + 0x40B, + 0x40C, + 0x40D, + 0x40E, + 0x40F, + 0x410, + 0x411, + 0x412, + 0x413, + 0x414, + 0x415, + 0x416, + 0x417, + 0x418, + 0x419, + 0x41A, + 0x41B, + 0x41C, + 0x41D, + 0x41E, + 0x41F, + 0x420, + 0x421, + 0x422, + 0x423, + 0x424, + 0x425, + 0x426, + 0x427, + 0x428, + 0x429, + 0x42A, + 0x42B, + 0x42C, + 0x42D, + 0x42E, + 0x42F, + 0x430, + 0x460, + 0x461, + 0x462, + 0x463, + 0x464, + 0x465, + 0x466, + 0x467, + 0x468, + 0x469, + 0x46A, + 0x46B, + 0x46C, + 0x46D, + 0x46E, + 0x46F, + 0x470, + 0x471, + 0x472, + 0x473, + 0x474, + 0x475, + 0x476, + 0x477, + 0x478, + 0x479, + 0x47A, + 0x47B, + 0x47C, + 0x47D, + 0x47E, + 0x47F, + 0x480, + 0x481, + 0x48A, + 0x48B, + 0x48C, + 0x48D, + 0x48E, + 0x48F, + 0x490, + 0x491, + 0x492, + 0x493, + 0x494, + 0x495, + 0x496, + 0x497, + 0x498, + 0x499, + 0x49A, + 0x49B, + 0x49C, + 0x49D, + 0x49E, + 0x49F, + 0x4A0, + 0x4A1, + 0x4A2, + 0x4A3, + 0x4A4, + 0x4A5, + 0x4A6, + 0x4A7, + 0x4A8, + 0x4A9, + 0x4AA, + 0x4AB, + 0x4AC, + 0x4AD, + 0x4AE, + 0x4AF, + 0x4B0, + 0x4B1, + 0x4B2, + 0x4B3, + 0x4B4, + 0x4B5, + 0x4B6, + 0x4B7, + 0x4B8, + 0x4B9, + 0x4BA, + 0x4BB, + 0x4BC, + 0x4BD, + 0x4BE, + 0x4BF, + 0x4C0, + 0x4C1, + 0x4C2, + 0x4C3, + 0x4C4, + 0x4C5, + 0x4C6, + 0x4C7, + 0x4C8, + 0x4C9, + 0x4CA, + 0x4CB, + 0x4CC, + 0x4CD, + 0x4CE, + 0x4D0, + 0x4D1, + 0x4D2, + 0x4D3, + 0x4D4, + 0x4D5, + 0x4D6, + 0x4D7, + 0x4D8, + 0x4D9, + 0x4DA, + 0x4DB, + 0x4DC, + 0x4DD, + 0x4DE, + 0x4DF, + 0x4E0, + 0x4E1, + 0x4E2, + 0x4E3, + 0x4E4, + 0x4E5, + 0x4E6, + 0x4E7, + 0x4E8, + 0x4E9, + 0x4EA, + 0x4EB, + 0x4EC, + 0x4ED, + 0x4EE, + 0x4EF, + 0x4F0, + 0x4F1, + 0x4F2, + 0x4F3, + 0x4F4, + 0x4F5, + 0x4F6, + 0x4F7, + 0x4F8, + 0x4F9, + 0x4FA, + 0x4FB, + 0x4FC, + 0x4FD, + 0x4FE, + 0x4FF, + 0x500, + 0x501, + 0x502, + 0x503, + 0x504, + 0x505, + 0x506, + 0x507, + 0x508, + 0x509, + 0x50A, + 0x50B, + 0x50C, + 0x50D, + 0x50E, + 0x50F, + 0x510, + 0x511, + 0x512, + 0x513, + 0x514, + 0x515, + 0x516, + 0x517, + 0x518, + 0x519, + 0x51A, + 0x51B, + 0x51C, + 0x51D, + 0x51E, + 0x51F, + 0x520, + 0x521, + 0x522, + 0x523, + 0x524, + 0x525, + 0x526, + 0x527, + 0x528, + 0x529, + 0x52A, + 0x52B, + 0x52C, + 0x52D, + 0x52E, + 0x52F, + 0x530, + 0x531, + 0x532, + 0x533, + 0x534, + 0x535, + 0x536, + 0x537, + 0x538, + 0x539, + 0x53A, + 0x53B, + 0x53C, + 0x53D, + 0x53E, + 0x53F, + 0x540, + 0x541, + 0x542, + 0x543, + 0x544, + 0x545, + 0x546, + 0x547, + 0x548, + 0x549, + 0x54A, + 0x54B, + 0x54C, + 0x54D, + 0x54E, + 0x54F, + 0x550, + 0x551, + 0x552, + 0x553, + 0x554, + 0x555, + 0x556, + 0x557, + 0x558, + 0x559, + 0x587, + 0x588, + 0x58B, + 0x58C, + 0x58D, + 0x590, + 0x591, + 0x5CA, + 0x5D0, + 0x5EB, + 0x5EF, + 0x5F5, + 0x606, + 0x61C, + 0x61D, + 0x675, + 0x676, + 0x677, + 0x678, + 0x679, + 0x6DD, + 0x6DE, + 0x70E, + 0x710, + 0x74B, + 0x74D, + 0x7B2, + 0x7C0, + 0x7FB, + 0x7FD, + 0x82E, + 0x830, + 0x83F, + 0x840, + 0x85C, + 0x85E, + 0x85F, + 0x860, + 0x86B, + 0x870, + 0x890, + 0x897, + 0x8E2, + 0x8E3, + 0x958, + 0x959, + 0x95A, + 0x95B, + 0x95C, + 0x95D, + 0x95E, + 0x95F, + 0x960, + 0x984, + 0x985, + 0x98D, + 0x98F, + 0x991, + 0x993, + 0x9A9, + 0x9AA, + 0x9B1, + 0x9B2, + 0x9B3, + 0x9B6, + 0x9BA, + 0x9BC, + 0x9C5, + 0x9C7, + 0x9C9, + 0x9CB, + 0x9CF, + 0x9D7, + 0x9D8, + 0x9DC, + 0x9DD, + 0x9DE, + 0x9DF, + 0x9E0, + 0x9E4, + 0x9E6, + 0x9FF, + 0xA01, + 0xA04, + 0xA05, + 0xA0B, + 0xA0F, + 0xA11, + 0xA13, + 0xA29, + 0xA2A, + 0xA31, + 0xA32, + 0xA33, + 0xA34, + 0xA35, + 0xA36, + 0xA37, + 0xA38, + 0xA3A, + 0xA3C, + 0xA3D, + 0xA3E, + 0xA43, + 0xA47, + 0xA49, + 0xA4B, + 0xA4E, + 0xA51, + 0xA52, + 0xA59, + 0xA5A, + 0xA5B, + 0xA5C, + 0xA5D, + 0xA5E, + 0xA5F, + 0xA66, + 0xA77, + 0xA81, + 0xA84, + 0xA85, + 0xA8E, + 0xA8F, + 0xA92, + 0xA93, + 0xAA9, + 0xAAA, + 0xAB1, + 0xAB2, + 0xAB4, + 0xAB5, + 0xABA, + 0xABC, + 0xAC6, + 0xAC7, + 0xACA, + 0xACB, + 0xACE, + 0xAD0, + 0xAD1, + 0xAE0, + 0xAE4, + 0xAE6, + 0xAF2, + 0xAF9, + 0xB00, + 0xB01, + 0xB04, + 0xB05, + 0xB0D, + 0xB0F, + 0xB11, + 0xB13, + 0xB29, + 0xB2A, + 0xB31, + 0xB32, + 0xB34, + 0xB35, + 0xB3A, + 0xB3C, + 0xB45, + 0xB47, + 0xB49, + 0xB4B, + 0xB4E, + 0xB53, + 0xB58, + 0xB5C, + 0xB5D, + 0xB5E, + 0xB5F, + 0xB64, + 0xB66, + 0xB78, + 0xB82, + 0xB84, + 0xB85, + 0xB8B, + 0xB8E, + 0xB91, + 0xB92, + 0xB96, + 0xB99, + 0xB9B, + 0xB9C, + 0xB9D, + 0xB9E, + 0xBA0, + 0xBA3, + 0xBA5, + 0xBA8, + 0xBAB, + 0xBAE, + 0xBBA, + 0xBBE, + 0xBC3, + 0xBC6, + 0xBC9, + 0xBCA, + 0xBCE, + 0xBD0, + 0xBD1, + 0xBD7, + 0xBD8, + 0xBE6, + 0xBFB, + 0xC00, + 0xC0D, + 0xC0E, + 0xC11, + 0xC12, + 0xC29, + 0xC2A, + 0xC3A, + 0xC3C, + 0xC45, + 0xC46, + 0xC49, + 0xC4A, + 0xC4E, + 0xC55, + 0xC57, + 0xC58, + 0xC5B, + 0xC5C, + 0xC5E, + 0xC60, + 0xC64, + 0xC66, + 0xC70, + 0xC77, + 0xC8D, + 0xC8E, + 0xC91, + 0xC92, + 0xCA9, + 0xCAA, + 0xCB4, + 0xCB5, + 0xCBA, + 0xCBC, + 0xCC5, + 0xCC6, + 0xCC9, + 0xCCA, + 0xCCE, + 0xCD5, + 0xCD7, + 0xCDC, + 0xCDF, + 0xCE0, + 0xCE4, + 0xCE6, + 0xCF0, + 0xCF1, + 0xCF4, + 0xD00, + 0xD0D, + 0xD0E, + 0xD11, + 0xD12, + 0xD45, + 0xD46, + 0xD49, + 0xD4A, + 0xD50, + 0xD54, + 0xD64, + 0xD66, + 0xD80, + 0xD81, + 0xD84, + 0xD85, + 0xD97, + 0xD9A, + 0xDB2, + 0xDB3, + 0xDBC, + 0xDBD, + 0xDBE, + 0xDC0, + 0xDC7, + 0xDCA, + 0xDCB, + 0xDCF, + 0xDD5, + 0xDD6, + 0xDD7, + 0xDD8, + 0xDE0, + 0xDE6, + 0xDF0, + 0xDF2, + 0xDF5, + 0xE01, + 0xE33, + 0xE34, + 0xE3B, + 0xE3F, + 0xE5C, + 0xE81, + 0xE83, + 0xE84, + 0xE85, + 0xE86, + 0xE8B, + 0xE8C, + 0xEA4, + 0xEA5, + 0xEA6, + 0xEA7, + 0xEB3, + 0xEB4, + 0xEBE, + 0xEC0, + 0xEC5, + 0xEC6, + 0xEC7, + 0xEC8, + 0xECF, + 0xED0, + 0xEDA, + 0xEDC, + 0xEDD, + 0xEDE, + 0xEE0, + 0xF00, + 0xF0C, + 0xF0D, + 0xF43, + 0xF44, + 0xF48, + 0xF49, + 0xF4D, + 0xF4E, + 0xF52, + 0xF53, + 0xF57, + 0xF58, + 0xF5C, + 0xF5D, + 0xF69, + 0xF6A, + 0xF6D, + 0xF71, + 0xF73, + 0xF74, + 0xF75, + 0xF76, + 0xF77, + 0xF78, + 0xF79, + 0xF7A, + 0xF81, + 0xF82, + 0xF93, + 0xF94, + 0xF98, + 0xF99, + 0xF9D, + 0xF9E, + 0xFA2, + 0xFA3, + 0xFA7, + 0xFA8, + 0xFAC, + 0xFAD, + 0xFB9, + 0xFBA, + 0xFBD, + 0xFBE, + 0xFCD, + 0xFCE, + 0xFDB, + 0x1000, + 0x10A0, + 0x10A1, + 0x10A2, + 0x10A3, + 0x10A4, + 0x10A5, + 0x10A6, + 0x10A7, + 0x10A8, + 0x10A9, + 0x10AA, + 0x10AB, + 0x10AC, + 0x10AD, + 0x10AE, + 0x10AF, + 0x10B0, + 0x10B1, + 0x10B2, + 0x10B3, + 0x10B4, + 0x10B5, + 0x10B6, + 0x10B7, + 0x10B8, + 0x10B9, + 0x10BA, + 0x10BB, + 0x10BC, + 0x10BD, + 0x10BE, + 0x10BF, + 0x10C0, + 0x10C1, + 0x10C2, + 0x10C3, + 0x10C4, + 0x10C5, + 0x10C6, + 0x10C7, + 0x10C8, + 0x10CD, + 0x10CE, + 0x10D0, + 0x10FC, + 0x10FD, + 0x115F, + 0x1161, + 0x1249, + 0x124A, + 0x124E, + 0x1250, + 0x1257, + 0x1258, + 0x1259, + 0x125A, + 0x125E, + 0x1260, + 0x1289, + 0x128A, + 0x128E, + 0x1290, + 0x12B1, + 0x12B2, + 0x12B6, + 0x12B8, + 0x12BF, + 0x12C0, + 0x12C1, + 0x12C2, + 0x12C6, + 0x12C8, + 0x12D7, + 0x12D8, + 0x1311, + 0x1312, + 0x1316, + 0x1318, + 0x135B, + 0x135D, + 0x137D, + 0x1380, + 0x139A, + 0x13A0, + 0x13F6, + 0x13F8, + 0x13F9, + 0x13FA, + 0x13FB, + 0x13FC, + 0x13FD, + 0x13FE, + 0x1400, + 0x1680, + 0x1681, + 0x169D, + 0x16A0, + 0x16F9, + 0x1700, + 0x1716, + 0x171F, + 0x1737, + 0x1740, + 0x1754, + 0x1760, + 0x176D, + 0x176E, + 0x1771, + 0x1772, + 0x1774, + 0x1780, + 0x17B4, + 0x17B6, + 0x17DE, + 0x17E0, + 0x17EA, + 0x17F0, + 0x17FA, + 0x1800, + 0x180B, + 0x1810, + 0x181A, + 0x1820, + 0x1879, + 0x1880, + 0x18AB, + 0x18B0, + 0x18F6, + 0x1900, + 0x191F, + 0x1920, + 0x192C, + 0x1930, + 0x193C, + 0x1940, + 0x1941, + 0x1944, + 0x196E, + 0x1970, + 0x1975, + 0x1980, + 0x19AC, + 0x19B0, + 0x19CA, + 0x19D0, + 0x19DB, + 0x19DE, + 0x1A1C, + 0x1A1E, + 0x1A5F, + 0x1A60, + 0x1A7D, + 0x1A7F, + 0x1A8A, + 0x1A90, + 0x1A9A, + 0x1AA0, + 0x1AAE, + 0x1AB0, + 0x1AF1, + 0x1B00, + 0x1B4D, + 0x1B4E, + 0x1BF4, + 0x1BFC, + 0x1C38, + 0x1C3B, + 0x1C4A, + 0x1C4D, + 0x1C80, + 0x1C81, + 0x1C82, + 0x1C83, + 0x1C84, + 0x1C86, + 0x1C87, + 0x1C88, + 0x1C89, + 0x1C8A, + 0x1C8B, + 0x1C90, + 0x1C91, + 0x1C92, + 0x1C93, + 0x1C94, + 0x1C95, + 0x1C96, + 0x1C97, + 0x1C98, + 0x1C99, + 0x1C9A, + 0x1C9B, + 0x1C9C, + 0x1C9D, + 0x1C9E, + 0x1C9F, + 0x1CA0, + 0x1CA1, + 0x1CA2, + 0x1CA3, + 0x1CA4, + 0x1CA5, + 0x1CA6, + 0x1CA7, + 0x1CA8, + 0x1CA9, + 0x1CAA, + 0x1CAB, + 0x1CAC, + 0x1CAD, + 0x1CAE, + 0x1CAF, + 0x1CB0, + 0x1CB1, + 0x1CB2, + 0x1CB3, + 0x1CB4, + 0x1CB5, + 0x1CB6, + 0x1CB7, + 0x1CB8, + 0x1CB9, + 0x1CBA, + 0x1CBB, + 0x1CBD, + 0x1CBE, + 0x1CBF, + 0x1CC0, + 0x1CC8, + 0x1CD0, + 0x1CFB, + 0x1D00, + 0x1D2C, + 0x1D2D, + 0x1D2E, + 0x1D2F, + 0x1D30, + 0x1D31, + 0x1D32, + 0x1D33, + 0x1D34, + 0x1D35, + 0x1D36, + 0x1D37, + 0x1D38, + 0x1D39, + 0x1D3A, + 0x1D3B, + 0x1D3C, + 0x1D3D, + 0x1D3E, + 0x1D3F, + 0x1D40, + 0x1D41, + 0x1D42, + 0x1D43, + 0x1D44, + 0x1D45, + 0x1D46, + 0x1D47, + 0x1D48, + 0x1D49, + 0x1D4A, + 0x1D4B, + 0x1D4C, + 0x1D4D, + 0x1D4E, + 0x1D4F, + 0x1D50, + 0x1D51, + 0x1D52, + 0x1D53, + 0x1D54, + 0x1D55, + 0x1D56, + 0x1D57, + 0x1D58, + 0x1D59, + 0x1D5A, + 0x1D5B, + 0x1D5C, + 0x1D5D, + 0x1D5E, + 0x1D5F, + 0x1D60, + 0x1D61, + 0x1D62, + 0x1D63, + 0x1D64, + 0x1D65, + 0x1D66, + 0x1D67, + 0x1D68, + 0x1D69, + 0x1D6A, + 0x1D6B, + 0x1D78, + 0x1D79, + 0x1D9B, + 0x1D9C, + 0x1D9D, + 0x1D9E, + 0x1D9F, + 0x1DA0, + 0x1DA1, + 0x1DA2, + 0x1DA3, + 0x1DA4, + 0x1DA5, + 0x1DA6, + 0x1DA7, + 0x1DA8, + 0x1DA9, + 0x1DAA, + 0x1DAB, + 0x1DAC, + 0x1DAD, + 0x1DAE, + 0x1DAF, + 0x1DB0, + 0x1DB1, + 0x1DB2, + 0x1DB3, + 0x1DB4, + 0x1DB5, + 0x1DB6, + 0x1DB7, + 0x1DB8, + 0x1DB9, + 0x1DBA, + 0x1DBB, + 0x1DBC, + 0x1DBD, + 0x1DBE, + 0x1DBF, + 0x1DC0, + 0x1E00, + 0x1E01, + 0x1E02, + 0x1E03, + 0x1E04, + 0x1E05, + 0x1E06, + 0x1E07, + 0x1E08, + 0x1E09, + 0x1E0A, + 0x1E0B, + 0x1E0C, + 0x1E0D, + 0x1E0E, + 0x1E0F, + 0x1E10, + 0x1E11, + 0x1E12, + 0x1E13, + 0x1E14, + 0x1E15, + 0x1E16, + 0x1E17, + 0x1E18, + 0x1E19, + 0x1E1A, + 0x1E1B, + 0x1E1C, + 0x1E1D, + 0x1E1E, + 0x1E1F, + 0x1E20, + 0x1E21, + 0x1E22, + 0x1E23, + 0x1E24, + 0x1E25, + 0x1E26, + 0x1E27, + 0x1E28, + 0x1E29, + 0x1E2A, + 0x1E2B, + 0x1E2C, + 0x1E2D, + 0x1E2E, + 0x1E2F, + 0x1E30, + 0x1E31, + 0x1E32, + 0x1E33, + 0x1E34, + 0x1E35, + 0x1E36, + 0x1E37, + 0x1E38, + 0x1E39, + 0x1E3A, + 0x1E3B, + 0x1E3C, + 0x1E3D, + 0x1E3E, + 0x1E3F, + 0x1E40, + 0x1E41, + 0x1E42, + 0x1E43, + 0x1E44, + 0x1E45, + 0x1E46, + 0x1E47, + 0x1E48, + 0x1E49, + 0x1E4A, + 0x1E4B, + 0x1E4C, + 0x1E4D, + 0x1E4E, + 0x1E4F, + 0x1E50, + 0x1E51, + 0x1E52, + 0x1E53, + 0x1E54, + 0x1E55, + 0x1E56, + 0x1E57, + 0x1E58, + 0x1E59, + 0x1E5A, + 0x1E5B, + 0x1E5C, + 0x1E5D, + 0x1E5E, + 0x1E5F, + 0x1E60, + 0x1E61, + 0x1E62, + 0x1E63, + 0x1E64, + 0x1E65, + 0x1E66, + 0x1E67, + 0x1E68, + 0x1E69, + 0x1E6A, + 0x1E6B, + 0x1E6C, + 0x1E6D, + 0x1E6E, + 0x1E6F, + 0x1E70, + 0x1E71, + 0x1E72, + 0x1E73, + 0x1E74, + 0x1E75, + 0x1E76, + 0x1E77, + 0x1E78, + 0x1E79, + 0x1E7A, + 0x1E7B, + 0x1E7C, + 0x1E7D, + 0x1E7E, + 0x1E7F, + 0x1E80, + 0x1E81, + 0x1E82, + 0x1E83, + 0x1E84, + 0x1E85, + 0x1E86, + 0x1E87, + 0x1E88, + 0x1E89, + 0x1E8A, + 0x1E8B, + 0x1E8C, + 0x1E8D, + 0x1E8E, + 0x1E8F, + 0x1E90, + 0x1E91, + 0x1E92, + 0x1E93, + 0x1E94, + 0x1E95, + 0x1E9A, + 0x1E9B, + 0x1E9C, + 0x1E9E, + 0x1E9F, + 0x1EA0, + 0x1EA1, + 0x1EA2, + 0x1EA3, + 0x1EA4, + 0x1EA5, + 0x1EA6, + 0x1EA7, + 0x1EA8, + 0x1EA9, + 0x1EAA, + 0x1EAB, + 0x1EAC, + 0x1EAD, + 0x1EAE, + 0x1EAF, + 0x1EB0, + 0x1EB1, + 0x1EB2, + 0x1EB3, + 0x1EB4, + 0x1EB5, + 0x1EB6, + 0x1EB7, + 0x1EB8, + 0x1EB9, + 0x1EBA, + 0x1EBB, + 0x1EBC, + 0x1EBD, + 0x1EBE, + 0x1EBF, + 0x1EC0, + 0x1EC1, + 0x1EC2, + 0x1EC3, + 0x1EC4, + 0x1EC5, + 0x1EC6, + 0x1EC7, + 0x1EC8, + 0x1EC9, + 0x1ECA, + 0x1ECB, + 0x1ECC, + 0x1ECD, + 0x1ECE, + 0x1ECF, + 0x1ED0, + 0x1ED1, + 0x1ED2, + 0x1ED3, + 0x1ED4, + 0x1ED5, + 0x1ED6, + 0x1ED7, + 0x1ED8, + 0x1ED9, + 0x1EDA, + 0x1EDB, + 0x1EDC, + 0x1EDD, + 0x1EDE, + 0x1EDF, + 0x1EE0, + 0x1EE1, + 0x1EE2, + 0x1EE3, + 0x1EE4, + 0x1EE5, + 0x1EE6, + 0x1EE7, + 0x1EE8, + 0x1EE9, + 0x1EEA, + 0x1EEB, + 0x1EEC, + 0x1EED, + 0x1EEE, + 0x1EEF, + 0x1EF0, + 0x1EF1, + 0x1EF2, + 0x1EF3, + 0x1EF4, + 0x1EF5, + 0x1EF6, + 0x1EF7, + 0x1EF8, + 0x1EF9, + 0x1EFA, + 0x1EFB, + 0x1EFC, + 0x1EFD, + 0x1EFE, + 0x1EFF, + 0x1F08, + 0x1F09, + 0x1F0A, + 0x1F0B, + 0x1F0C, + 0x1F0D, + 0x1F0E, + 0x1F0F, + 0x1F10, + 0x1F16, + 0x1F18, + 0x1F19, + 0x1F1A, + 0x1F1B, + 0x1F1C, + 0x1F1D, + 0x1F1E, + 0x1F20, + 0x1F28, + 0x1F29, + 0x1F2A, + 0x1F2B, + 0x1F2C, + 0x1F2D, + 0x1F2E, + 0x1F2F, + 0x1F30, + 0x1F38, + 0x1F39, + 0x1F3A, + 0x1F3B, + 0x1F3C, + 0x1F3D, + 0x1F3E, + 0x1F3F, + 0x1F40, + 0x1F46, + 0x1F48, + 0x1F49, + 0x1F4A, + 0x1F4B, + 0x1F4C, + 0x1F4D, + 0x1F4E, + 0x1F50, + 0x1F58, + 0x1F59, + 0x1F5A, + 0x1F5B, + 0x1F5C, + 0x1F5D, + 0x1F5E, + 0x1F5F, + 0x1F60, + 0x1F68, + 0x1F69, + 0x1F6A, + 0x1F6B, + 0x1F6C, + 0x1F6D, + 0x1F6E, + 0x1F6F, + 0x1F70, + 0x1F71, + 0x1F72, + 0x1F73, + 0x1F74, + 0x1F75, + 0x1F76, + 0x1F77, + 0x1F78, + 0x1F79, + 0x1F7A, + 0x1F7B, + 0x1F7C, + 0x1F7D, + 0x1F7E, + 0x1F80, + 0x1F81, + 0x1F82, + 0x1F83, + 0x1F84, + 0x1F85, + 0x1F86, + 0x1F87, + 0x1F88, + 0x1F89, + 0x1F8A, + 0x1F8B, + 0x1F8C, + 0x1F8D, + 0x1F8E, + 0x1F8F, + 0x1F90, + 0x1F91, + 0x1F92, + 0x1F93, + 0x1F94, + 0x1F95, + 0x1F96, + 0x1F97, + 0x1F98, + 0x1F99, + 0x1F9A, + 0x1F9B, + 0x1F9C, + 0x1F9D, + 0x1F9E, + 0x1F9F, + 0x1FA0, + 0x1FA1, + 0x1FA2, + 0x1FA3, + 0x1FA4, + 0x1FA5, + 0x1FA6, + 0x1FA7, + 0x1FA8, + 0x1FA9, + 0x1FAA, + 0x1FAB, + 0x1FAC, + 0x1FAD, + 0x1FAE, + 0x1FAF, + 0x1FB0, + 0x1FB2, + 0x1FB3, + 0x1FB4, + 0x1FB5, + 0x1FB6, + 0x1FB7, + 0x1FB8, + 0x1FB9, + 0x1FBA, + 0x1FBB, + 0x1FBC, + 0x1FBD, + 0x1FBE, + 0x1FBF, + 0x1FC0, + 0x1FC1, + 0x1FC2, + 0x1FC3, + 0x1FC4, + 0x1FC5, + 0x1FC6, + 0x1FC7, + 0x1FC8, + 0x1FC9, + 0x1FCA, + 0x1FCB, + 0x1FCC, + 0x1FCD, + 0x1FCE, + 0x1FCF, + 0x1FD0, + 0x1FD3, + 0x1FD4, + 0x1FD6, + 0x1FD8, + 0x1FD9, + 0x1FDA, + 0x1FDB, + 0x1FDC, + 0x1FDD, + 0x1FDE, + 0x1FDF, + 0x1FE0, + 0x1FE3, + 0x1FE4, + 0x1FE8, + 0x1FE9, + 0x1FEA, + 0x1FEB, + 0x1FEC, + 0x1FED, + 0x1FEE, + 0x1FEF, + 0x1FF0, + 0x1FF2, + 0x1FF3, + 0x1FF4, + 0x1FF5, + 0x1FF6, + 0x1FF7, + 0x1FF8, + 0x1FF9, + 0x1FFA, + 0x1FFB, + 0x1FFC, + 0x1FFD, + 0x1FFE, + 0x1FFF, + 0x2000, + 0x200B, + 0x200C, + 0x200E, + 0x2010, + 0x2011, + 0x2012, + 0x2017, + 0x2018, + 0x2024, + 0x2027, + 0x2028, + 0x202F, + 0x2030, + 0x2033, + 0x2034, + 0x2035, + 0x2036, + 0x2037, + 0x2038, + 0x203C, + 0x203D, + 0x203E, + 0x203F, + 0x2047, + 0x2048, + 0x2049, + 0x204A, + 0x2057, + 0x2058, + 0x205F, + 0x2060, + 0x2065, + 0x206A, + 0x2070, + 0x2071, + 0x2072, + 0x2074, + 0x2075, + 0x2076, + 0x2077, + 0x2078, + 0x2079, + 0x207A, + 0x207B, + 0x207C, + 0x207D, + 0x207E, + 0x207F, + 0x2080, + 0x2081, + 0x2082, + 0x2083, + 0x2084, + 0x2085, + 0x2086, + 0x2087, + 0x2088, + 0x2089, + 0x208A, + 0x208B, + 0x208C, + 0x208D, + 0x208E, + 0x208F, + 0x2090, + 0x2091, + 0x2092, + 0x2093, + 0x2094, + 0x2095, + 0x2096, + 0x2097, + 0x2098, + 0x2099, + 0x209A, + 0x209B, + 0x209C, + 0x209D, + 0x209E, + 0x209F, + 0x20A0, + 0x20A8, + 0x20A9, + 0x20C5, + 0x20D0, + 0x20F1, + 0x2100, + 0x2101, + 0x2102, + 0x2103, + 0x2104, + 0x2105, + 0x2106, + 0x2107, + 0x2108, + 0x2109, + 0x210A, + 0x210B, + 0x210F, + 0x2110, + 0x2112, + 0x2114, + 0x2115, + 0x2116, + 0x2117, + 0x2119, + 0x211A, + 0x211B, + 0x211E, + 0x2120, + 0x2121, + 0x2122, + 0x2123, + 0x2124, + 0x2125, + 0x2126, + 0x2127, + 0x2128, + 0x2129, + 0x212A, + 0x212B, + 0x212C, + 0x212D, + 0x212E, + 0x212F, + 0x2131, + 0x2132, + 0x2133, + 0x2134, + 0x2135, + 0x2136, + 0x2137, + 0x2138, + 0x2139, + 0x213A, + 0x213B, + 0x213C, + 0x213D, + 0x213F, + 0x2140, + 0x2141, + 0x2145, + 0x2147, + 0x2148, + 0x2149, + 0x214A, + 0x2150, + 0x2151, + 0x2152, + 0x2153, + 0x2154, + 0x2155, + 0x2156, + 0x2157, + 0x2158, + 0x2159, + 0x215A, + 0x215B, + 0x215C, + 0x215D, + 0x215E, + 0x215F, + 0x2160, + 0x2161, + 0x2162, + 0x2163, + 0x2164, + 0x2165, + 0x2166, + 0x2167, + 0x2168, + 0x2169, + 0x216A, + 0x216B, + 0x216C, + 0x216D, + 0x216E, + 0x216F, + 0x2170, + 0x2171, + 0x2172, + 0x2173, + 0x2174, + 0x2175, + 0x2176, + 0x2177, + 0x2178, + 0x2179, + 0x217A, + 0x217B, + 0x217C, + 0x217D, + 0x217E, + 0x217F, + 0x2180, + 0x2183, + 0x2184, + 0x2189, + 0x218A, + 0x218C, + 0x2190, + 0x222C, + 0x222D, + 0x222E, + 0x222F, + 0x2230, + 0x2231, + 0x2329, + 0x232A, + 0x232B, + 0x242A, + 0x2440, + 0x244B, + 0x2460, + 0x2461, + 0x2462, + 0x2463, + 0x2464, + 0x2465, + 0x2466, + 0x2467, + 0x2468, + 0x2469, + 0x246A, + 0x246B, + 0x246C, + 0x246D, + 0x246E, + 0x246F, + 0x2470, + 0x2471, + 0x2472, + 0x2473, + 0x2474, + 0x2475, + 0x2476, + 0x2477, + 0x2478, + 0x2479, + 0x247A, + 0x247B, + 0x247C, + 0x247D, + 0x247E, + 0x247F, + 0x2480, + 0x2481, + 0x2482, + 0x2483, + 0x2484, + 0x2485, + 0x2486, + 0x2487, + 0x2488, + 0x249C, + 0x249D, + 0x249E, + 0x249F, + 0x24A0, + 0x24A1, + 0x24A2, + 0x24A3, + 0x24A4, + 0x24A5, + 0x24A6, + 0x24A7, + 0x24A8, + 0x24A9, + 0x24AA, + 0x24AB, + 0x24AC, + 0x24AD, + 0x24AE, + 0x24AF, + 0x24B0, + 0x24B1, + 0x24B2, + 0x24B3, + 0x24B4, + 0x24B5, + 0x24B6, + 0x24B7, + 0x24B8, + 0x24B9, + 0x24BA, + 0x24BB, + 0x24BC, + 0x24BD, + 0x24BE, + 0x24BF, + 0x24C0, + 0x24C1, + 0x24C2, + 0x24C3, + 0x24C4, + 0x24C5, + 0x24C6, + 0x24C7, + 0x24C8, + 0x24C9, + 0x24CA, + 0x24CB, + 0x24CC, + 0x24CD, + 0x24CE, + 0x24CF, + 0x24D0, + 0x24D1, + 0x24D2, + 0x24D3, + 0x24D4, + 0x24D5, + 0x24D6, + 0x24D7, + 0x24D8, + 0x24D9, + 0x24DA, + 0x24DB, + 0x24DC, + 0x24DD, + 0x24DE, + 0x24DF, + 0x24E0, + 0x24E1, + 0x24E2, + 0x24E3, + 0x24E4, + 0x24E5, + 0x24E6, + 0x24E7, + 0x24E8, + 0x24E9, + 0x24EA, + 0x24EB, + 0x2A0C, + 0x2A0D, + 0x2A74, + 0x2A75, + 0x2A76, + 0x2A77, + 0x2ADC, + 0x2ADD, + 0x2B74, + 0x2B76, + 0x2C00, + 0x2C01, + 0x2C02, + 0x2C03, + 0x2C04, + 0x2C05, + 0x2C06, + 0x2C07, + 0x2C08, + 0x2C09, + 0x2C0A, + 0x2C0B, + 0x2C0C, + 0x2C0D, + 0x2C0E, + 0x2C0F, + 0x2C10, + 0x2C11, + 0x2C12, + 0x2C13, + 0x2C14, + 0x2C15, + 0x2C16, + 0x2C17, + 0x2C18, + 0x2C19, + 0x2C1A, + 0x2C1B, + 0x2C1C, + 0x2C1D, + 0x2C1E, + 0x2C1F, + 0x2C20, + 0x2C21, + 0x2C22, + 0x2C23, + 0x2C24, + 0x2C25, + 0x2C26, + 0x2C27, + 0x2C28, + 0x2C29, + 0x2C2A, + 0x2C2B, + 0x2C2C, + 0x2C2D, + 0x2C2E, + 0x2C2F, + 0x2C30, + 0x2C60, + 0x2C61, + 0x2C62, + 0x2C63, + 0x2C64, + 0x2C65, + 0x2C67, + 0x2C68, + 0x2C69, + 0x2C6A, + 0x2C6B, + 0x2C6C, + 0x2C6D, + 0x2C6E, + 0x2C6F, + 0x2C70, + 0x2C71, + 0x2C72, + 0x2C73, + 0x2C75, + 0x2C76, + 0x2C7C, + 0x2C7D, + 0x2C7E, + 0x2C7F, + 0x2C80, + 0x2C81, + 0x2C82, + 0x2C83, + 0x2C84, + 0x2C85, + 0x2C86, + 0x2C87, + 0x2C88, + 0x2C89, + 0x2C8A, + 0x2C8B, + 0x2C8C, + 0x2C8D, + 0x2C8E, + 0x2C8F, + 0x2C90, + 0x2C91, + 0x2C92, + 0x2C93, + 0x2C94, + 0x2C95, + 0x2C96, + 0x2C97, + 0x2C98, + 0x2C99, + 0x2C9A, + 0x2C9B, + 0x2C9C, + 0x2C9D, + 0x2C9E, + 0x2C9F, + 0x2CA0, + 0x2CA1, + 0x2CA2, + 0x2CA3, + 0x2CA4, + 0x2CA5, + 0x2CA6, + 0x2CA7, + 0x2CA8, + 0x2CA9, + 0x2CAA, + 0x2CAB, + 0x2CAC, + 0x2CAD, + 0x2CAE, + 0x2CAF, + 0x2CB0, + 0x2CB1, + 0x2CB2, + 0x2CB3, + 0x2CB4, + 0x2CB5, + 0x2CB6, + 0x2CB7, + 0x2CB8, + 0x2CB9, + 0x2CBA, + 0x2CBB, + 0x2CBC, + 0x2CBD, + 0x2CBE, + 0x2CBF, + 0x2CC0, + 0x2CC1, + 0x2CC2, + 0x2CC3, + 0x2CC4, + 0x2CC5, + 0x2CC6, + 0x2CC7, + 0x2CC8, + 0x2CC9, + 0x2CCA, + 0x2CCB, + 0x2CCC, + 0x2CCD, + 0x2CCE, + 0x2CCF, + 0x2CD0, + 0x2CD1, + 0x2CD2, + 0x2CD3, + 0x2CD4, + 0x2CD5, + 0x2CD6, + 0x2CD7, + 0x2CD8, + 0x2CD9, + 0x2CDA, + 0x2CDB, + 0x2CDC, + 0x2CDD, + 0x2CDE, + 0x2CDF, + 0x2CE0, + 0x2CE1, + 0x2CE2, + 0x2CE3, + 0x2CEB, + 0x2CEC, + 0x2CED, + 0x2CEE, + 0x2CF2, + 0x2CF3, + 0x2CF4, + 0x2CF9, + 0x2D26, + 0x2D27, + 0x2D28, + 0x2D2D, + 0x2D2E, + 0x2D30, + 0x2D68, + 0x2D6F, + 0x2D70, + 0x2D71, + 0x2D7F, + 0x2D97, + 0x2DA0, + 0x2DA7, + 0x2DA8, + 0x2DAF, + 0x2DB0, + 0x2DB7, + 0x2DB8, + 0x2DBF, + 0x2DC0, + 0x2DC7, + 0x2DC8, + 0x2DCF, + 0x2DD0, + 0x2DD7, + 0x2DD8, + 0x2DDF, + 0x2DE0, + 0x2E5E, + 0x2E60, + 0x2E64, + 0x2E80, + 0x2E9A, + 0x2E9B, + 0x2E9F, + 0x2EA0, + 0x2EF3, + 0x2EF4, + 0x2F00, + 0x2F01, + 0x2F02, + 0x2F03, + 0x2F04, + 0x2F05, + 0x2F06, + 0x2F07, + 0x2F08, + 0x2F09, + 0x2F0A, + 0x2F0B, + 0x2F0C, + 0x2F0D, + 0x2F0E, + 0x2F0F, + 0x2F10, + 0x2F11, + 0x2F12, + 0x2F13, + 0x2F14, + 0x2F15, + 0x2F16, + 0x2F17, + 0x2F18, + 0x2F19, + 0x2F1A, + 0x2F1B, + 0x2F1C, + 0x2F1D, + 0x2F1E, + 0x2F1F, + 0x2F20, + 0x2F21, + 0x2F22, + 0x2F23, + 0x2F24, + 0x2F25, + 0x2F26, + 0x2F27, + 0x2F28, + 0x2F29, + 0x2F2A, + 0x2F2B, + 0x2F2C, + 0x2F2D, + 0x2F2E, + 0x2F2F, + 0x2F30, + 0x2F31, + 0x2F32, + 0x2F33, + 0x2F34, + 0x2F35, + 0x2F36, + 0x2F37, + 0x2F38, + 0x2F39, + 0x2F3A, + 0x2F3B, + 0x2F3C, + 0x2F3D, + 0x2F3E, + 0x2F3F, + 0x2F40, + 0x2F41, + 0x2F42, + 0x2F43, + 0x2F44, + 0x2F45, + 0x2F46, + 0x2F47, + 0x2F48, + 0x2F49, + 0x2F4A, + 0x2F4B, + 0x2F4C, + 0x2F4D, + 0x2F4E, + 0x2F4F, + 0x2F50, + 0x2F51, + 0x2F52, + 0x2F53, + 0x2F54, + 0x2F55, + 0x2F56, + 0x2F57, + 0x2F58, + 0x2F59, + 0x2F5A, + 0x2F5B, + 0x2F5C, + 0x2F5D, + 0x2F5E, + 0x2F5F, + 0x2F60, + 0x2F61, + 0x2F62, + 0x2F63, + 0x2F64, + 0x2F65, + 0x2F66, + 0x2F67, + 0x2F68, + 0x2F69, + 0x2F6A, + 0x2F6B, + 0x2F6C, + 0x2F6D, + 0x2F6E, + 0x2F6F, + 0x2F70, + 0x2F71, + 0x2F72, + 0x2F73, + 0x2F74, + 0x2F75, + 0x2F76, + 0x2F77, + 0x2F78, + 0x2F79, + 0x2F7A, + 0x2F7B, + 0x2F7C, + 0x2F7D, + 0x2F7E, + 0x2F7F, + 0x2F80, + 0x2F81, + 0x2F82, + 0x2F83, + 0x2F84, + 0x2F85, + 0x2F86, + 0x2F87, + 0x2F88, + 0x2F89, + 0x2F8A, + 0x2F8B, + 0x2F8C, + 0x2F8D, + 0x2F8E, + 0x2F8F, + 0x2F90, + 0x2F91, + 0x2F92, + 0x2F93, + 0x2F94, + 0x2F95, + 0x2F96, + 0x2F97, + 0x2F98, + 0x2F99, + 0x2F9A, + 0x2F9B, + 0x2F9C, + 0x2F9D, + 0x2F9E, + 0x2F9F, + 0x2FA0, + 0x2FA1, + 0x2FA2, + 0x2FA3, + 0x2FA4, + 0x2FA5, + 0x2FA6, + 0x2FA7, + 0x2FA8, + 0x2FA9, + 0x2FAA, + 0x2FAB, + 0x2FAC, + 0x2FAD, + 0x2FAE, + 0x2FAF, + 0x2FB0, + 0x2FB1, + 0x2FB2, + 0x2FB3, + 0x2FB4, + 0x2FB5, + 0x2FB6, + 0x2FB7, + 0x2FB8, + 0x2FB9, + 0x2FBA, + 0x2FBB, + 0x2FBC, + 0x2FBD, + 0x2FBE, + 0x2FBF, + 0x2FC0, + 0x2FC1, + 0x2FC2, + 0x2FC3, + 0x2FC4, + 0x2FC5, + 0x2FC6, + 0x2FC7, + 0x2FC8, + 0x2FC9, + 0x2FCA, + 0x2FCB, + 0x2FCC, + 0x2FCD, + 0x2FCE, + 0x2FCF, + 0x2FD0, + 0x2FD1, + 0x2FD2, + 0x2FD3, + 0x2FD4, + 0x2FD5, + 0x2FD6, + 0x3000, + 0x3001, + 0x3002, + 0x3003, + 0x3036, + 0x3037, + 0x3038, + 0x3039, + 0x303A, + 0x303B, + 0x3040, + 0x3041, + 0x3097, + 0x3099, + 0x309B, + 0x309C, + 0x309D, + 0x309F, + 0x30A0, + 0x30FF, + 0x3100, + 0x3105, + 0x3130, + 0x3131, + 0x3132, + 0x3133, + 0x3134, + 0x3135, + 0x3136, + 0x3137, + 0x3138, + 0x3139, + 0x313A, + 0x313B, + 0x313C, + 0x313D, + 0x313E, + 0x313F, + 0x3140, + 0x3141, + 0x3142, + 0x3143, + 0x3144, + 0x3145, + 0x3146, + 0x3147, + 0x3148, + 0x3149, + 0x314A, + 0x314B, + 0x314C, + 0x314D, + 0x314E, + 0x314F, + 0x3150, + 0x3151, + 0x3152, + 0x3153, + 0x3154, + 0x3155, + 0x3156, + 0x3157, + 0x3158, + 0x3159, + 0x315A, + 0x315B, + 0x315C, + 0x315D, + 0x315E, + 0x315F, + 0x3160, + 0x3161, + 0x3162, + 0x3163, + 0x3164, + 0x3165, + 0x3166, + 0x3167, + 0x3168, + 0x3169, + 0x316A, + 0x316B, + 0x316C, + 0x316D, + 0x316E, + 0x316F, + 0x3170, + 0x3171, + 0x3172, + 0x3173, + 0x3174, + 0x3175, + 0x3176, + 0x3177, + 0x3178, + 0x3179, + 0x317A, + 0x317B, + 0x317C, + 0x317D, + 0x317E, + 0x317F, + 0x3180, + 0x3181, + 0x3182, + 0x3183, + 0x3184, + 0x3185, + 0x3186, + 0x3187, + 0x3188, + 0x3189, + 0x318A, + 0x318B, + 0x318C, + 0x318D, + 0x318E, + 0x318F, + 0x3190, + 0x3192, + 0x3193, + 0x3194, + 0x3195, + 0x3196, + 0x3197, + 0x3198, + 0x3199, + 0x319A, + 0x319B, + 0x319C, + 0x319D, + 0x319E, + 0x319F, + 0x31A0, + 0x31E6, + 0x31F0, + 0x3200, + 0x3201, + 0x3202, + 0x3203, + 0x3204, + 0x3205, + 0x3206, + 0x3207, + 0x3208, + 0x3209, + 0x320A, + 0x320B, + 0x320C, + 0x320D, + 0x320E, + 0x320F, + 0x3210, + 0x3211, + 0x3212, + 0x3213, + 0x3214, + 0x3215, + 0x3216, + 0x3217, + 0x3218, + 0x3219, + 0x321A, + 0x321B, + 0x321C, + 0x321D, + 0x321E, + 0x321F, + 0x3220, + 0x3221, + 0x3222, + 0x3223, + 0x3224, + 0x3225, + 0x3226, + 0x3227, + 0x3228, + 0x3229, + 0x322A, + 0x322B, + 0x322C, + 0x322D, + 0x322E, + 0x322F, + 0x3230, + 0x3231, + 0x3232, + 0x3233, + 0x3234, + 0x3235, + 0x3236, + 0x3237, + 0x3238, + 0x3239, + 0x323A, + 0x323B, + 0x323C, + 0x323D, + 0x323E, + 0x323F, + 0x3240, + 0x3241, + 0x3242, + 0x3243, + 0x3244, + 0x3245, + 0x3246, + 0x3247, + 0x3248, + 0x3250, + 0x3251, + 0x3252, + 0x3253, + 0x3254, + 0x3255, + 0x3256, + 0x3257, + 0x3258, + 0x3259, + 0x325A, + 0x325B, + 0x325C, + 0x325D, + 0x325E, + 0x325F, + 0x3260, + 0x3261, + 0x3262, + 0x3263, + 0x3264, + 0x3265, + 0x3266, + 0x3267, + 0x3268, + 0x3269, + 0x326A, + 0x326B, + 0x326C, + 0x326D, + 0x326E, + 0x326F, + 0x3270, + 0x3271, + 0x3272, + 0x3273, + 0x3274, + 0x3275, + 0x3276, + 0x3277, + 0x3278, + 0x3279, + 0x327A, + 0x327B, + 0x327C, + 0x327D, + 0x327E, + 0x327F, + 0x3280, + 0x3281, + 0x3282, + 0x3283, + 0x3284, + 0x3285, + 0x3286, + 0x3287, + 0x3288, + 0x3289, + 0x328A, + 0x328B, + 0x328C, + 0x328D, + 0x328E, + 0x328F, + 0x3290, + 0x3291, + 0x3292, + 0x3293, + 0x3294, + 0x3295, + 0x3296, + 0x3297, + 0x3298, + 0x3299, + 0x329A, + 0x329B, + 0x329C, + 0x329D, + 0x329E, + 0x329F, + 0x32A0, + 0x32A1, + 0x32A2, + 0x32A3, + 0x32A4, + 0x32A5, + 0x32A6, + 0x32A7, + 0x32A8, + 0x32A9, + 0x32AA, + 0x32AB, + 0x32AC, + 0x32AD, + 0x32AE, + 0x32AF, + 0x32B0, + 0x32B1, + 0x32B2, + 0x32B3, + 0x32B4, + 0x32B5, + 0x32B6, + 0x32B7, + 0x32B8, + 0x32B9, + 0x32BA, + 0x32BB, + 0x32BC, + 0x32BD, + 0x32BE, + 0x32BF, + 0x32C0, + 0x32C1, + 0x32C2, + 0x32C3, + 0x32C4, + 0x32C5, + 0x32C6, + 0x32C7, + 0x32C8, + 0x32C9, + 0x32CA, + 0x32CB, + 0x32CC, + 0x32CD, + 0x32CE, + 0x32CF, + 0x32D0, + 0x32D1, + 0x32D2, + 0x32D3, + 0x32D4, + 0x32D5, + 0x32D6, + 0x32D7, + 0x32D8, + 0x32D9, + 0x32DA, + 0x32DB, + 0x32DC, + 0x32DD, + 0x32DE, + 0x32DF, + 0x32E0, + 0x32E1, + 0x32E2, + 0x32E3, + 0x32E4, + 0x32E5, + 0x32E6, + 0x32E7, + 0x32E8, + 0x32E9, + 0x32EA, + 0x32EB, + 0x32EC, + 0x32ED, + 0x32EE, + 0x32EF, + 0x32F0, + 0x32F1, + 0x32F2, + 0x32F3, + 0x32F4, + 0x32F5, + 0x32F6, + 0x32F7, + 0x32F8, + 0x32F9, + 0x32FA, + 0x32FB, + 0x32FC, + 0x32FD, + 0x32FE, + 0x32FF, + 0x3300, + 0x3301, + 0x3302, + 0x3303, + 0x3304, + 0x3305, + 0x3306, + 0x3307, + 0x3308, + 0x3309, + 0x330A, + 0x330B, + 0x330C, + 0x330D, + 0x330E, + 0x330F, + 0x3310, + 0x3311, + 0x3312, + 0x3313, + 0x3314, + 0x3315, + 0x3316, + 0x3317, + 0x3318, + 0x3319, + 0x331A, + 0x331B, + 0x331C, + 0x331D, + 0x331E, + 0x331F, + 0x3320, + 0x3321, + 0x3322, + 0x3323, + 0x3324, + 0x3325, + 0x3326, + 0x3327, + 0x3328, + 0x3329, + 0x332A, + 0x332B, + 0x332C, + 0x332D, + 0x332E, + 0x332F, + 0x3330, + 0x3331, + 0x3332, + 0x3333, + 0x3334, + 0x3335, + 0x3336, + 0x3337, + 0x3338, + 0x3339, + 0x333A, + 0x333B, + 0x333C, + 0x333D, + 0x333E, + 0x333F, + 0x3340, + 0x3341, + 0x3342, + 0x3343, + 0x3344, + 0x3345, + 0x3346, + 0x3347, + 0x3348, + 0x3349, + 0x334A, + 0x334B, + 0x334C, + 0x334D, + 0x334E, + 0x334F, + 0x3350, + 0x3351, + 0x3352, + 0x3353, + 0x3354, + 0x3355, + 0x3356, + 0x3357, + 0x3358, + 0x3359, + 0x335A, + 0x335B, + 0x335C, + 0x335D, + 0x335E, + 0x335F, + 0x3360, + 0x3361, + 0x3362, + 0x3363, + 0x3364, + 0x3365, + 0x3366, + 0x3367, + 0x3368, + 0x3369, + 0x336A, + 0x336B, + 0x336C, + 0x336D, + 0x336E, + 0x336F, + 0x3370, + 0x3371, + 0x3372, + 0x3373, + 0x3374, + 0x3375, + 0x3376, + 0x3377, + 0x3378, + 0x3379, + 0x337A, + 0x337B, + 0x337C, + 0x337D, + 0x337E, + 0x337F, + 0x3380, + 0x3381, + 0x3382, + 0x3383, + 0x3384, + 0x3385, + 0x3386, + 0x3387, + 0x3388, + 0x3389, + 0x338A, + 0x338B, + 0x338C, + 0x338D, + 0x338E, + 0x338F, + 0x3390, + 0x3391, + 0x3392, + 0x3393, + 0x3394, + 0x3395, + 0x3396, + 0x3397, + 0x3398, + 0x3399, + 0x339A, + 0x339B, + 0x339C, + 0x339D, + 0x339E, + 0x339F, + 0x33A0, + 0x33A1, + 0x33A2, + 0x33A3, + 0x33A4, + 0x33A5, + 0x33A6, + 0x33A7, + 0x33A8, + 0x33A9, + 0x33AA, + 0x33AB, + 0x33AC, + 0x33AD, + 0x33AE, + 0x33AF, + 0x33B0, + 0x33B1, + 0x33B2, + 0x33B3, + 0x33B4, + 0x33B5, + 0x33B6, + 0x33B7, + 0x33B8, + 0x33B9, + 0x33BA, + 0x33BB, + 0x33BC, + 0x33BD, + 0x33BE, + 0x33BF, + 0x33C0, + 0x33C1, + 0x33C2, + 0x33C3, + 0x33C4, + 0x33C5, + 0x33C6, + 0x33C7, + 0x33C8, + 0x33C9, + 0x33CA, + 0x33CB, + 0x33CC, + 0x33CD, + 0x33CE, + 0x33CF, + 0x33D0, + 0x33D1, + 0x33D2, + 0x33D3, + 0x33D4, + 0x33D5, + 0x33D6, + 0x33D7, + 0x33D8, + 0x33D9, + 0x33DA, + 0x33DB, + 0x33DC, + 0x33DD, + 0x33DE, + 0x33DF, + 0x33E0, + 0x33E1, + 0x33E2, + 0x33E3, + 0x33E4, + 0x33E5, + 0x33E6, + 0x33E7, + 0x33E8, + 0x33E9, + 0x33EA, + 0x33EB, + 0x33EC, + 0x33ED, + 0x33EE, + 0x33EF, + 0x33F0, + 0x33F1, + 0x33F2, + 0x33F3, + 0x33F4, + 0x33F5, + 0x33F6, + 0x33F7, + 0x33F8, + 0x33F9, + 0x33FA, + 0x33FB, + 0x33FC, + 0x33FD, + 0x33FE, + 0x33FF, + 0x3400, + 0xA48D, + 0xA490, + 0xA4C7, + 0xA4D0, + 0xA62C, + 0xA640, + 0xA641, + 0xA642, + 0xA643, + 0xA644, + 0xA645, + 0xA646, + 0xA647, + 0xA648, + 0xA649, + 0xA64A, + 0xA64B, + 0xA64C, + 0xA64D, + 0xA64E, + 0xA64F, + 0xA650, + 0xA651, + 0xA652, + 0xA653, + 0xA654, + 0xA655, + 0xA656, + 0xA657, + 0xA658, + 0xA659, + 0xA65A, + 0xA65B, + 0xA65C, + 0xA65D, + 0xA65E, + 0xA65F, + 0xA660, + 0xA661, + 0xA662, + 0xA663, + 0xA664, + 0xA665, + 0xA666, + 0xA667, + 0xA668, + 0xA669, + 0xA66A, + 0xA66B, + 0xA66C, + 0xA66D, + 0xA680, + 0xA681, + 0xA682, + 0xA683, + 0xA684, + 0xA685, + 0xA686, + 0xA687, + 0xA688, + 0xA689, + 0xA68A, + 0xA68B, + 0xA68C, + 0xA68D, + 0xA68E, + 0xA68F, + 0xA690, + 0xA691, + 0xA692, + 0xA693, + 0xA694, + 0xA695, + 0xA696, + 0xA697, + 0xA698, + 0xA699, + 0xA69A, + 0xA69B, + 0xA69C, + 0xA69D, + 0xA69E, + 0xA6F8, + 0xA700, + 0xA722, + 0xA723, + 0xA724, + 0xA725, + 0xA726, + 0xA727, + 0xA728, + 0xA729, + 0xA72A, + 0xA72B, + 0xA72C, + 0xA72D, + 0xA72E, + 0xA72F, + 0xA732, + 0xA733, + 0xA734, + 0xA735, + 0xA736, + 0xA737, + 0xA738, + 0xA739, + 0xA73A, + 0xA73B, + 0xA73C, + 0xA73D, + 0xA73E, + 0xA73F, + 0xA740, + 0xA741, + 0xA742, + 0xA743, + 0xA744, + 0xA745, + 0xA746, + 0xA747, + 0xA748, + 0xA749, + 0xA74A, + 0xA74B, + 0xA74C, + 0xA74D, + 0xA74E, + 0xA74F, + 0xA750, + 0xA751, + 0xA752, + 0xA753, + 0xA754, + 0xA755, + 0xA756, + 0xA757, + 0xA758, + 0xA759, + 0xA75A, + 0xA75B, + 0xA75C, + 0xA75D, + 0xA75E, + 0xA75F, + 0xA760, + 0xA761, + 0xA762, + 0xA763, + 0xA764, + 0xA765, + 0xA766, + 0xA767, + 0xA768, + 0xA769, + 0xA76A, + 0xA76B, + 0xA76C, + 0xA76D, + 0xA76E, + 0xA76F, + 0xA770, + 0xA771, + 0xA779, + 0xA77A, + 0xA77B, + 0xA77C, + 0xA77D, + 0xA77E, + 0xA77F, + 0xA780, + 0xA781, + 0xA782, + 0xA783, + 0xA784, + 0xA785, + 0xA786, + 0xA787, + 0xA78B, + 0xA78C, + 0xA78D, + 0xA78E, + 0xA790, + 0xA791, + 0xA792, + 0xA793, + 0xA796, + 0xA797, + 0xA798, + 0xA799, + 0xA79A, + 0xA79B, + 0xA79C, + 0xA79D, + 0xA79E, + 0xA79F, + 0xA7A0, + 0xA7A1, + 0xA7A2, + 0xA7A3, + 0xA7A4, + 0xA7A5, + 0xA7A6, + 0xA7A7, + 0xA7A8, + 0xA7A9, + 0xA7AA, + 0xA7AB, + 0xA7AC, + 0xA7AD, + 0xA7AE, + 0xA7AF, + 0xA7B0, + 0xA7B1, + 0xA7B2, + 0xA7B3, + 0xA7B4, + 0xA7B5, + 0xA7B6, + 0xA7B7, + 0xA7B8, + 0xA7B9, + 0xA7BA, + 0xA7BB, + 0xA7BC, + 0xA7BD, + 0xA7BE, + 0xA7BF, + 0xA7C0, + 0xA7C1, + 0xA7C2, + 0xA7C3, + 0xA7C4, + 0xA7C5, + 0xA7C6, + 0xA7C7, + 0xA7C8, + 0xA7C9, + 0xA7CA, + 0xA7CB, + 0xA7CC, + 0xA7CD, + 0xA7CE, + 0xA7CF, + 0xA7D0, + 0xA7D1, + 0xA7D2, + 0xA7D3, + 0xA7D4, + 0xA7D5, + 0xA7D6, + 0xA7D7, + 0xA7D8, + 0xA7D9, + 0xA7DA, + 0xA7DB, + 0xA7DC, + 0xA7DD, + 0xA7DE, + 0xA7E2, + 0xA7E3, + 0xA7F1, + 0xA7F2, + 0xA7F3, + 0xA7F4, + 0xA7F5, + 0xA7F6, + 0xA7F8, + 0xA7F9, + 0xA7FA, + 0xA82D, + 0xA830, + 0xA83A, + 0xA840, + 0xA878, + 0xA880, + 0xA8C6, + 0xA8CE, + 0xA8DA, + 0xA8E0, + 0xA954, + 0xA95F, + 0xA97D, + 0xA980, + 0xA9CE, + 0xA9CF, + 0xA9DA, + 0xA9DE, + 0xA9FF, + 0xAA00, + 0xAA37, + 0xAA40, + 0xAA4E, + 0xAA50, + 0xAA5A, + 0xAA5C, + 0xAAC3, + 0xAADB, + 0xAAF7, + 0xAB01, + 0xAB07, + 0xAB09, + 0xAB0F, + 0xAB11, + 0xAB17, + 0xAB20, + 0xAB27, + 0xAB28, + 0xAB2F, + 0xAB30, + 0xAB5C, + 0xAB5D, + 0xAB5E, + 0xAB5F, + 0xAB60, + 0xAB69, + 0xAB6A, + 0xAB6C, + 0xAB6D, + 0xAB6E, + 0xAB70, + 0xAB71, + 0xAB72, + 0xAB73, + 0xAB74, + 0xAB75, + 0xAB76, + 0xAB77, + 0xAB78, + 0xAB79, + 0xAB7A, + 0xAB7B, + 0xAB7C, + 0xAB7D, + 0xAB7E, + 0xAB7F, + 0xAB80, + 0xAB81, + 0xAB82, + 0xAB83, + 0xAB84, + 0xAB85, + 0xAB86, + 0xAB87, + 0xAB88, + 0xAB89, + 0xAB8A, + 0xAB8B, + 0xAB8C, + 0xAB8D, + 0xAB8E, + 0xAB8F, + 0xAB90, + 0xAB91, + 0xAB92, + 0xAB93, + 0xAB94, + 0xAB95, + 0xAB96, + 0xAB97, + 0xAB98, + 0xAB99, + 0xAB9A, + 0xAB9B, + 0xAB9C, + 0xAB9D, + 0xAB9E, + 0xAB9F, + 0xABA0, + 0xABA1, + 0xABA2, + 0xABA3, + 0xABA4, + 0xABA5, + 0xABA6, + 0xABA7, + 0xABA8, + 0xABA9, + 0xABAA, + 0xABAB, + 0xABAC, + 0xABAD, + 0xABAE, + 0xABAF, + 0xABB0, + 0xABB1, + 0xABB2, + 0xABB3, + 0xABB4, + 0xABB5, + 0xABB6, + 0xABB7, + 0xABB8, + 0xABB9, + 0xABBA, + 0xABBB, + 0xABBC, + 0xABBD, + 0xABBE, + 0xABBF, + 0xABC0, + 0xABEE, + 0xABF0, + 0xABFA, + 0xAC00, + 0xD7A4, + 0xD7B0, + 0xD7C7, + 0xD7CB, + 0xD7FC, + 0xF900, + 0xF901, + 0xF902, + 0xF903, + 0xF904, + 0xF905, + 0xF906, + 0xF907, + 0xF909, + 0xF90A, + 0xF90B, + 0xF90C, + 0xF90D, + 0xF90E, + 0xF90F, + 0xF910, + 0xF911, + 0xF912, + 0xF913, + 0xF914, + 0xF915, + 0xF916, + 0xF917, + 0xF918, + 0xF919, + 0xF91A, + 0xF91B, + 0xF91C, + 0xF91D, + 0xF91E, + 0xF91F, + 0xF920, + 0xF921, + 0xF922, + 0xF923, + 0xF924, + 0xF925, + 0xF926, + 0xF927, + 0xF928, + 0xF929, + 0xF92A, + 0xF92B, + 0xF92C, + 0xF92D, + 0xF92E, + 0xF92F, + 0xF930, + 0xF931, + 0xF932, + 0xF933, + 0xF934, + 0xF935, + 0xF936, + 0xF937, + 0xF938, + 0xF939, + 0xF93A, + 0xF93B, + 0xF93C, + 0xF93D, + 0xF93E, + 0xF93F, + 0xF940, + 0xF941, + 0xF942, + 0xF943, + 0xF944, + 0xF945, + 0xF946, + 0xF947, + 0xF948, + 0xF949, + 0xF94A, + 0xF94B, + 0xF94C, + 0xF94D, + 0xF94E, + 0xF94F, + 0xF950, + 0xF951, + 0xF952, + 0xF953, + 0xF954, + 0xF955, + 0xF956, + 0xF957, + 0xF958, + 0xF959, + 0xF95A, + 0xF95B, + 0xF95C, + 0xF95D, + 0xF95E, + 0xF95F, + 0xF960, + 0xF961, + 0xF962, + 0xF963, + 0xF964, + 0xF965, + 0xF966, + 0xF967, + 0xF968, + 0xF969, + 0xF96A, + 0xF96B, + 0xF96C, + 0xF96D, + 0xF96E, + 0xF96F, + 0xF970, + 0xF971, + 0xF972, + 0xF973, + 0xF974, + 0xF975, + 0xF976, + 0xF977, + 0xF978, + 0xF979, + 0xF97A, + 0xF97B, + 0xF97C, + 0xF97D, + 0xF97E, + 0xF97F, + 0xF980, + 0xF981, + 0xF982, + 0xF983, + 0xF984, + 0xF985, + 0xF986, + 0xF987, + 0xF988, + 0xF989, + 0xF98A, + 0xF98B, + 0xF98C, + 0xF98D, + 0xF98E, + 0xF98F, + 0xF990, + 0xF991, + 0xF992, + 0xF993, + 0xF994, + 0xF995, + 0xF996, + 0xF997, + 0xF998, + 0xF999, + 0xF99A, + 0xF99B, + 0xF99C, + 0xF99D, + 0xF99E, + 0xF99F, + 0xF9A0, + 0xF9A1, + 0xF9A2, + 0xF9A3, + 0xF9A4, + 0xF9A5, + 0xF9A6, + 0xF9A7, + 0xF9A8, + 0xF9A9, + 0xF9AA, + 0xF9AB, + 0xF9AC, + 0xF9AD, + 0xF9AE, + 0xF9AF, + 0xF9B0, + 0xF9B1, + 0xF9B2, + 0xF9B3, + 0xF9B4, + 0xF9B5, + 0xF9B6, + 0xF9B7, + 0xF9B8, + 0xF9B9, + 0xF9BA, + 0xF9BB, + 0xF9BC, + 0xF9BD, + 0xF9BE, + 0xF9BF, + 0xF9C0, + 0xF9C1, + 0xF9C2, + 0xF9C3, + 0xF9C4, + 0xF9C5, + 0xF9C6, + 0xF9C7, + 0xF9C8, + 0xF9C9, + 0xF9CA, + 0xF9CB, + 0xF9CC, + 0xF9CD, + 0xF9CE, + 0xF9CF, + 0xF9D0, + 0xF9D1, + 0xF9D2, + 0xF9D3, + 0xF9D4, + 0xF9D5, + 0xF9D6, + 0xF9D7, + 0xF9D8, + 0xF9D9, + 0xF9DA, + 0xF9DB, + 0xF9DC, + 0xF9DD, + 0xF9DE, + 0xF9DF, + 0xF9E0, + 0xF9E1, + 0xF9E2, + 0xF9E3, + 0xF9E4, + 0xF9E5, + 0xF9E6, + 0xF9E7, + 0xF9E8, + 0xF9E9, + 0xF9EA, + 0xF9EB, + 0xF9EC, + 0xF9ED, + 0xF9EE, + 0xF9EF, + 0xF9F0, + 0xF9F1, + 0xF9F2, + 0xF9F3, + 0xF9F4, + 0xF9F5, + 0xF9F6, + 0xF9F7, + 0xF9F8, + 0xF9F9, + 0xF9FA, + 0xF9FB, + 0xF9FC, + 0xF9FD, + 0xF9FE, + 0xF9FF, + 0xFA00, + 0xFA01, + 0xFA02, + 0xFA03, + 0xFA04, + 0xFA05, + 0xFA06, + 0xFA07, + 0xFA08, + 0xFA09, + 0xFA0A, + 0xFA0B, + 0xFA0C, + 0xFA0D, + 0xFA0E, + 0xFA10, + 0xFA11, + 0xFA12, + 0xFA13, + 0xFA15, + 0xFA16, + 0xFA17, + 0xFA18, + 0xFA19, + 0xFA1A, + 0xFA1B, + 0xFA1C, + 0xFA1D, + 0xFA1E, + 0xFA1F, + 0xFA20, + 0xFA21, + 0xFA22, + 0xFA23, + 0xFA25, + 0xFA26, + 0xFA27, + 0xFA2A, + 0xFA2B, + 0xFA2C, + 0xFA2D, + 0xFA2E, + 0xFA2F, + 0xFA30, + 0xFA31, + 0xFA32, + 0xFA33, + 0xFA34, + 0xFA35, + 0xFA36, + 0xFA37, + 0xFA38, + 0xFA39, + 0xFA3A, + 0xFA3B, + 0xFA3C, + 0xFA3D, + 0xFA3E, + 0xFA3F, + 0xFA40, + 0xFA41, + 0xFA42, + 0xFA43, + 0xFA44, + 0xFA45, + 0xFA46, + 0xFA47, + 0xFA48, + 0xFA49, + 0xFA4A, + 0xFA4B, + 0xFA4C, + 0xFA4D, + 0xFA4E, + 0xFA4F, + 0xFA50, + 0xFA51, + 0xFA52, + 0xFA53, + 0xFA54, + 0xFA55, + 0xFA56, + 0xFA57, + 0xFA58, + 0xFA59, + 0xFA5A, + 0xFA5B, + 0xFA5C, + 0xFA5D, + 0xFA5F, + 0xFA60, + 0xFA61, + 0xFA62, + 0xFA63, + 0xFA64, + 0xFA65, + 0xFA66, + 0xFA67, + 0xFA68, + 0xFA69, + 0xFA6A, + 0xFA6B, + 0xFA6C, + 0xFA6D, + 0xFA6E, + 0xFA70, + 0xFA71, + 0xFA72, + 0xFA73, + 0xFA74, + 0xFA75, + 0xFA76, + 0xFA77, + 0xFA78, + 0xFA79, + 0xFA7A, + 0xFA7B, + 0xFA7C, + 0xFA7D, + 0xFA7E, + 0xFA7F, + 0xFA80, + 0xFA81, + 0xFA82, + 0xFA83, + 0xFA84, + 0xFA85, + 0xFA86, + 0xFA87, + 0xFA88, + 0xFA89, + 0xFA8A, + 0xFA8B, + 0xFA8C, + 0xFA8D, + 0xFA8E, + 0xFA8F, + 0xFA90, + 0xFA91, + 0xFA92, + 0xFA93, + 0xFA94, + 0xFA95, + 0xFA96, + 0xFA97, + 0xFA98, + 0xFA99, + 0xFA9A, + 0xFA9B, + 0xFA9C, + 0xFA9D, + 0xFA9E, + 0xFA9F, + 0xFAA0, + 0xFAA1, + 0xFAA2, + 0xFAA3, + 0xFAA4, + 0xFAA5, + 0xFAA6, + 0xFAA7, + 0xFAA8, + 0xFAA9, + 0xFAAA, + 0xFAAB, + 0xFAAC, + 0xFAAD, + 0xFAAE, + 0xFAAF, + 0xFAB0, + 0xFAB1, + 0xFAB2, + 0xFAB3, + 0xFAB4, + 0xFAB5, + 0xFAB6, + 0xFAB7, + 0xFAB8, + 0xFAB9, + 0xFABA, + 0xFABB, + 0xFABC, + 0xFABD, + 0xFABE, + 0xFABF, + 0xFAC0, + 0xFAC1, + 0xFAC2, + 0xFAC3, + 0xFAC4, + 0xFAC5, + 0xFAC6, + 0xFAC7, + 0xFAC8, + 0xFAC9, + 0xFACA, + 0xFACB, + 0xFACC, + 0xFACD, + 0xFACE, + 0xFACF, + 0xFAD0, + 0xFAD1, + 0xFAD2, + 0xFAD3, + 0xFAD4, + 0xFAD5, + 0xFAD6, + 0xFAD7, + 0xFAD8, + 0xFAD9, + 0xFADA, + 0xFB00, + 0xFB01, + 0xFB02, + 0xFB03, + 0xFB04, + 0xFB05, + 0xFB07, + 0xFB13, + 0xFB14, + 0xFB15, + 0xFB16, + 0xFB17, + 0xFB18, + 0xFB1D, + 0xFB1E, + 0xFB1F, + 0xFB20, + 0xFB21, + 0xFB22, + 0xFB23, + 0xFB24, + 0xFB25, + 0xFB26, + 0xFB27, + 0xFB28, + 0xFB29, + 0xFB2A, + 0xFB2B, + 0xFB2C, + 0xFB2D, + 0xFB2E, + 0xFB2F, + 0xFB30, + 0xFB31, + 0xFB32, + 0xFB33, + 0xFB34, + 0xFB35, + 0xFB36, + 0xFB37, + 0xFB38, + 0xFB39, + 0xFB3A, + 0xFB3B, + 0xFB3C, + 0xFB3D, + 0xFB3E, + 0xFB3F, + 0xFB40, + 0xFB41, + 0xFB42, + 0xFB43, + 0xFB44, + 0xFB45, + 0xFB46, + 0xFB47, + 0xFB48, + 0xFB49, + 0xFB4A, + 0xFB4B, + 0xFB4C, + 0xFB4D, + 0xFB4E, + 0xFB4F, + 0xFB50, + 0xFB52, + 0xFB56, + 0xFB5A, + 0xFB5E, + 0xFB62, + 0xFB66, + 0xFB6A, + 0xFB6E, + 0xFB72, + 0xFB76, + 0xFB7A, + 0xFB7E, + 0xFB82, + 0xFB84, + 0xFB86, + 0xFB88, + 0xFB8A, + 0xFB8C, + 0xFB8E, + 0xFB92, + 0xFB96, + 0xFB9A, + 0xFB9E, + 0xFBA0, + 0xFBA4, + 0xFBA6, + 0xFBAA, + 0xFBAE, + 0xFBB0, + 0xFBB2, + 0xFBD3, + 0xFBD7, + 0xFBD9, + 0xFBDB, + 0xFBDD, + 0xFBDE, + 0xFBE0, + 0xFBE2, + 0xFBE4, + 0xFBE8, + 0xFBEA, + 0xFBEC, + 0xFBEE, + 0xFBF0, + 0xFBF2, + 0xFBF4, + 0xFBF6, + 0xFBF9, + 0xFBFC, + 0xFC00, + 0xFC01, + 0xFC02, + 0xFC03, + 0xFC04, + 0xFC05, + 0xFC06, + 0xFC07, + 0xFC08, + 0xFC09, + 0xFC0A, + 0xFC0B, + 0xFC0C, + 0xFC0D, + 0xFC0E, + 0xFC0F, + 0xFC10, + 0xFC11, + 0xFC12, + 0xFC13, + 0xFC14, + 0xFC15, + 0xFC16, + 0xFC17, + 0xFC18, + 0xFC19, + 0xFC1A, + 0xFC1B, + 0xFC1C, + 0xFC1D, + 0xFC1E, + 0xFC1F, + 0xFC20, + 0xFC21, + 0xFC22, + 0xFC23, + 0xFC24, + 0xFC25, + 0xFC26, + 0xFC27, + 0xFC28, + 0xFC29, + 0xFC2A, + 0xFC2B, + 0xFC2C, + 0xFC2D, + 0xFC2E, + 0xFC2F, + 0xFC30, + 0xFC31, + 0xFC32, + 0xFC33, + 0xFC34, + 0xFC35, + 0xFC36, + 0xFC37, + 0xFC38, + 0xFC39, + 0xFC3A, + 0xFC3B, + 0xFC3C, + 0xFC3D, + 0xFC3E, + 0xFC3F, + 0xFC40, + 0xFC41, + 0xFC42, + 0xFC43, + 0xFC44, + 0xFC45, + 0xFC46, + 0xFC47, + 0xFC48, + 0xFC49, + 0xFC4A, + 0xFC4B, + 0xFC4C, + 0xFC4D, + 0xFC4E, + 0xFC4F, + 0xFC50, + 0xFC51, + 0xFC52, + 0xFC53, + 0xFC54, + 0xFC55, + 0xFC56, + 0xFC57, + 0xFC58, + 0xFC59, + 0xFC5A, + 0xFC5B, + 0xFC5C, + 0xFC5D, + 0xFC5E, + 0xFC5F, + 0xFC60, + 0xFC61, + 0xFC62, + 0xFC63, + 0xFC64, + 0xFC65, + 0xFC66, + 0xFC67, + 0xFC68, + 0xFC69, + 0xFC6A, + 0xFC6B, + 0xFC6C, + 0xFC6D, + 0xFC6E, + 0xFC6F, + 0xFC70, + 0xFC71, + 0xFC72, + 0xFC73, + 0xFC74, + 0xFC75, + 0xFC76, + 0xFC77, + 0xFC78, + 0xFC79, + 0xFC7A, + 0xFC7B, + 0xFC7C, + 0xFC7D, + 0xFC7E, + 0xFC7F, + 0xFC80, + 0xFC81, + 0xFC82, + 0xFC83, + 0xFC84, + 0xFC85, + 0xFC86, + 0xFC87, + 0xFC88, + 0xFC89, + 0xFC8A, + 0xFC8B, + 0xFC8C, + 0xFC8D, + 0xFC8E, + 0xFC8F, + 0xFC90, + 0xFC91, + 0xFC92, + 0xFC93, + 0xFC94, + 0xFC95, + 0xFC96, + 0xFC97, + 0xFC98, + 0xFC99, + 0xFC9A, + 0xFC9B, + 0xFC9C, + 0xFC9D, + 0xFC9E, + 0xFC9F, + 0xFCA0, + 0xFCA1, + 0xFCA2, + 0xFCA3, + 0xFCA4, + 0xFCA5, + 0xFCA6, + 0xFCA7, + 0xFCA8, + 0xFCA9, + 0xFCAA, + 0xFCAB, + 0xFCAC, + 0xFCAD, + 0xFCAE, + 0xFCAF, + 0xFCB0, + 0xFCB1, + 0xFCB2, + 0xFCB3, + 0xFCB4, + 0xFCB5, + 0xFCB6, + 0xFCB7, + 0xFCB8, + 0xFCB9, + 0xFCBA, + 0xFCBB, + 0xFCBC, + 0xFCBD, + 0xFCBE, + 0xFCBF, + 0xFCC0, + 0xFCC1, + 0xFCC2, + 0xFCC3, + 0xFCC4, + 0xFCC5, + 0xFCC6, + 0xFCC7, + 0xFCC8, + 0xFCC9, + 0xFCCA, + 0xFCCB, + 0xFCCC, + 0xFCCD, + 0xFCCE, + 0xFCCF, + 0xFCD0, + 0xFCD1, + 0xFCD2, + 0xFCD3, + 0xFCD4, + 0xFCD5, + 0xFCD6, + 0xFCD7, + 0xFCD8, + 0xFCD9, + 0xFCDA, + 0xFCDB, + 0xFCDC, + 0xFCDD, + 0xFCDE, + 0xFCDF, + 0xFCE0, + 0xFCE1, + 0xFCE2, + 0xFCE3, + 0xFCE4, + 0xFCE5, + 0xFCE6, + 0xFCE7, + 0xFCE8, + 0xFCE9, + 0xFCEA, + 0xFCEB, + 0xFCEC, + 0xFCED, + 0xFCEE, + 0xFCEF, + 0xFCF0, + 0xFCF1, + 0xFCF2, + 0xFCF3, + 0xFCF4, + 0xFCF5, + 0xFCF6, + 0xFCF7, + 0xFCF8, + 0xFCF9, + 0xFCFA, + 0xFCFB, + 0xFCFC, + 0xFCFD, + 0xFCFE, + 0xFCFF, + 0xFD00, + 0xFD01, + 0xFD02, + 0xFD03, + 0xFD04, + 0xFD05, + 0xFD06, + 0xFD07, + 0xFD08, + 0xFD09, + 0xFD0A, + 0xFD0B, + 0xFD0C, + 0xFD0D, + 0xFD0E, + 0xFD0F, + 0xFD10, + 0xFD11, + 0xFD12, + 0xFD13, + 0xFD14, + 0xFD15, + 0xFD16, + 0xFD17, + 0xFD18, + 0xFD19, + 0xFD1A, + 0xFD1B, + 0xFD1C, + 0xFD1D, + 0xFD1E, + 0xFD1F, + 0xFD20, + 0xFD21, + 0xFD22, + 0xFD23, + 0xFD24, + 0xFD25, + 0xFD26, + 0xFD27, + 0xFD28, + 0xFD29, + 0xFD2A, + 0xFD2B, + 0xFD2C, + 0xFD2D, + 0xFD2E, + 0xFD2F, + 0xFD30, + 0xFD31, + 0xFD32, + 0xFD33, + 0xFD34, + 0xFD35, + 0xFD36, + 0xFD37, + 0xFD38, + 0xFD39, + 0xFD3A, + 0xFD3B, + 0xFD3C, + 0xFD3E, + 0xFD50, + 0xFD51, + 0xFD53, + 0xFD54, + 0xFD55, + 0xFD56, + 0xFD57, + 0xFD58, + 0xFD5A, + 0xFD5B, + 0xFD5C, + 0xFD5D, + 0xFD5E, + 0xFD5F, + 0xFD61, + 0xFD62, + 0xFD64, + 0xFD66, + 0xFD67, + 0xFD69, + 0xFD6A, + 0xFD6C, + 0xFD6E, + 0xFD6F, + 0xFD71, + 0xFD73, + 0xFD74, + 0xFD75, + 0xFD76, + 0xFD78, + 0xFD79, + 0xFD7A, + 0xFD7B, + 0xFD7C, + 0xFD7E, + 0xFD7F, + 0xFD80, + 0xFD81, + 0xFD82, + 0xFD83, + 0xFD85, + 0xFD87, + 0xFD89, + 0xFD8A, + 0xFD8B, + 0xFD8C, + 0xFD8D, + 0xFD8E, + 0xFD8F, + 0xFD90, + 0xFD92, + 0xFD93, + 0xFD94, + 0xFD95, + 0xFD96, + 0xFD97, + 0xFD99, + 0xFD9A, + 0xFD9B, + 0xFD9C, + 0xFD9E, + 0xFD9F, + 0xFDA0, + 0xFDA1, + 0xFDA2, + 0xFDA3, + 0xFDA4, + 0xFDA5, + 0xFDA6, + 0xFDA7, + 0xFDA8, + 0xFDA9, + 0xFDAA, + 0xFDAB, + 0xFDAC, + 0xFDAD, + 0xFDAE, + 0xFDAF, + 0xFDB0, + 0xFDB1, + 0xFDB2, + 0xFDB3, + 0xFDB4, + 0xFDB5, + 0xFDB6, + 0xFDB7, + 0xFDB8, + 0xFDB9, + 0xFDBA, + 0xFDBB, + 0xFDBC, + 0xFDBD, + 0xFDBE, + 0xFDBF, + 0xFDC0, + 0xFDC1, + 0xFDC2, + 0xFDC3, + 0xFDC4, + 0xFDC5, + 0xFDC6, + 0xFDC7, + 0xFDC8, + 0xFDD0, + 0xFDF0, + 0xFDF1, + 0xFDF2, + 0xFDF3, + 0xFDF4, + 0xFDF5, + 0xFDF6, + 0xFDF7, + 0xFDF8, + 0xFDF9, + 0xFDFA, + 0xFDFB, + 0xFDFC, + 0xFDFD, + 0xFE00, + 0xFE10, + 0xFE11, + 0xFE12, + 0xFE13, + 0xFE14, + 0xFE15, + 0xFE16, + 0xFE17, + 0xFE18, + 0xFE19, + 0xFE20, + 0xFE30, + 0xFE31, + 0xFE32, + 0xFE33, + 0xFE35, + 0xFE36, + 0xFE37, + 0xFE38, + 0xFE39, + 0xFE3A, + 0xFE3B, + 0xFE3C, + 0xFE3D, + 0xFE3E, + 0xFE3F, + 0xFE40, + 0xFE41, + 0xFE42, + 0xFE43, + 0xFE44, + 0xFE45, + 0xFE47, + 0xFE48, + 0xFE49, + 0xFE4D, + 0xFE50, + 0xFE51, + 0xFE52, + 0xFE54, + 0xFE55, + 0xFE56, + 0xFE57, + 0xFE58, + 0xFE59, + 0xFE5A, + 0xFE5B, + 0xFE5C, + 0xFE5D, + 0xFE5E, + 0xFE5F, + 0xFE60, + 0xFE61, + 0xFE62, + 0xFE63, + 0xFE64, + 0xFE65, + 0xFE66, + 0xFE67, + 0xFE68, + 0xFE69, + 0xFE6A, + 0xFE6B, + 0xFE6C, + 0xFE70, + 0xFE71, + 0xFE72, + 0xFE73, + 0xFE74, + 0xFE75, + 0xFE76, + 0xFE77, + 0xFE78, + 0xFE79, + 0xFE7A, + 0xFE7B, + 0xFE7C, + 0xFE7D, + 0xFE7E, + 0xFE7F, + 0xFE80, + 0xFE81, + 0xFE83, + 0xFE85, + 0xFE87, + 0xFE89, + 0xFE8D, + 0xFE8F, + 0xFE93, + 0xFE95, + 0xFE99, + 0xFE9D, + 0xFEA1, + 0xFEA5, + 0xFEA9, + 0xFEAB, + 0xFEAD, + 0xFEAF, + 0xFEB1, + 0xFEB5, + 0xFEB9, + 0xFEBD, + 0xFEC1, + 0xFEC5, + 0xFEC9, + 0xFECD, + 0xFED1, + 0xFED5, + 0xFED9, + 0xFEDD, + 0xFEE1, + 0xFEE5, + 0xFEE9, + 0xFEED, + 0xFEEF, + 0xFEF1, + 0xFEF5, + 0xFEF7, + 0xFEF9, + 0xFEFB, + 0xFEFD, + 0xFEFF, + 0xFF00, + 0xFF01, + 0xFF02, + 0xFF03, + 0xFF04, + 0xFF05, + 0xFF06, + 0xFF07, + 0xFF08, + 0xFF09, + 0xFF0A, + 0xFF0B, + 0xFF0C, + 0xFF0D, + 0xFF0E, + 0xFF0F, + 0xFF10, + 0xFF11, + 0xFF12, + 0xFF13, + 0xFF14, + 0xFF15, + 0xFF16, + 0xFF17, + 0xFF18, + 0xFF19, + 0xFF1A, + 0xFF1B, + 0xFF1C, + 0xFF1D, + 0xFF1E, + 0xFF1F, + 0xFF20, + 0xFF21, + 0xFF22, + 0xFF23, + 0xFF24, + 0xFF25, + 0xFF26, + 0xFF27, + 0xFF28, + 0xFF29, + 0xFF2A, + 0xFF2B, + 0xFF2C, + 0xFF2D, + 0xFF2E, + 0xFF2F, + 0xFF30, + 0xFF31, + 0xFF32, + 0xFF33, + 0xFF34, + 0xFF35, + 0xFF36, + 0xFF37, + 0xFF38, + 0xFF39, + 0xFF3A, + 0xFF3B, + 0xFF3C, + 0xFF3D, + 0xFF3E, + 0xFF3F, + 0xFF40, + 0xFF41, + 0xFF42, + 0xFF43, + 0xFF44, + 0xFF45, + 0xFF46, + 0xFF47, + 0xFF48, + 0xFF49, + 0xFF4A, + 0xFF4B, + 0xFF4C, + 0xFF4D, + 0xFF4E, + 0xFF4F, + 0xFF50, + 0xFF51, + 0xFF52, + 0xFF53, + 0xFF54, + 0xFF55, + 0xFF56, + 0xFF57, + 0xFF58, + 0xFF59, + 0xFF5A, + 0xFF5B, + 0xFF5C, + 0xFF5D, + 0xFF5E, + 0xFF5F, + 0xFF60, + 0xFF61, + 0xFF62, + 0xFF63, + 0xFF64, + 0xFF65, + 0xFF66, + 0xFF67, + 0xFF68, + 0xFF69, + 0xFF6A, + 0xFF6B, + 0xFF6C, + 0xFF6D, + 0xFF6E, + 0xFF6F, + 0xFF70, + 0xFF71, + 0xFF72, + 0xFF73, + 0xFF74, + 0xFF75, + 0xFF76, + 0xFF77, + 0xFF78, + 0xFF79, + 0xFF7A, + 0xFF7B, + 0xFF7C, + 0xFF7D, + 0xFF7E, + 0xFF7F, + 0xFF80, + 0xFF81, + 0xFF82, + 0xFF83, + 0xFF84, + 0xFF85, + 0xFF86, + 0xFF87, + 0xFF88, + 0xFF89, + 0xFF8A, + 0xFF8B, + 0xFF8C, + 0xFF8D, + 0xFF8E, + 0xFF8F, + 0xFF90, + 0xFF91, + 0xFF92, + 0xFF93, + 0xFF94, + 0xFF95, + 0xFF96, + 0xFF97, + 0xFF98, + 0xFF99, + 0xFF9A, + 0xFF9B, + 0xFF9C, + 0xFF9D, + 0xFF9E, + 0xFF9F, + 0xFFA0, + 0xFFA1, + 0xFFA2, + 0xFFA3, + 0xFFA4, + 0xFFA5, + 0xFFA6, + 0xFFA7, + 0xFFA8, + 0xFFA9, + 0xFFAA, + 0xFFAB, + 0xFFAC, + 0xFFAD, + 0xFFAE, + 0xFFAF, + 0xFFB0, + 0xFFB1, + 0xFFB2, + 0xFFB3, + 0xFFB4, + 0xFFB5, + 0xFFB6, + 0xFFB7, + 0xFFB8, + 0xFFB9, + 0xFFBA, + 0xFFBB, + 0xFFBC, + 0xFFBD, + 0xFFBE, + 0xFFBF, + 0xFFC2, + 0xFFC3, + 0xFFC4, + 0xFFC5, + 0xFFC6, + 0xFFC7, + 0xFFC8, + 0xFFCA, + 0xFFCB, + 0xFFCC, + 0xFFCD, + 0xFFCE, + 0xFFCF, + 0xFFD0, + 0xFFD2, + 0xFFD3, + 0xFFD4, + 0xFFD5, + 0xFFD6, + 0xFFD7, + 0xFFD8, + 0xFFDA, + 0xFFDB, + 0xFFDC, + 0xFFDD, + 0xFFE0, + 0xFFE1, + 0xFFE2, + 0xFFE3, + 0xFFE4, + 0xFFE5, + 0xFFE6, + 0xFFE7, + 0xFFE8, + 0xFFE9, + 0xFFEA, + 0xFFEB, + 0xFFEC, + 0xFFED, + 0xFFEE, + 0xFFEF, + 0x10000, + 0x1000C, + 0x1000D, + 0x10027, + 0x10028, + 0x1003B, + 0x1003C, + 0x1003E, + 0x1003F, + 0x1004E, + 0x10050, + 0x1005E, + 0x10080, + 0x100FB, + 0x10100, + 0x10103, + 0x10107, + 0x10134, + 0x10137, + 0x1018F, + 0x10190, + 0x1019D, + 0x101A0, + 0x101A1, + 0x101D0, + 0x101FE, + 0x10280, + 0x1029D, + 0x102A0, + 0x102D1, + 0x102E0, + 0x102FC, + 0x10300, + 0x10324, + 0x1032D, + 0x1034B, + 0x10350, + 0x1037B, + 0x10380, + 0x1039E, + 0x1039F, + 0x103C4, + 0x103C8, + 0x103D6, + 0x10400, + 0x10401, + 0x10402, + 0x10403, + 0x10404, + 0x10405, + 0x10406, + 0x10407, + 0x10408, + 0x10409, + 0x1040A, + 0x1040B, + 0x1040C, + 0x1040D, + 0x1040E, + 0x1040F, + 0x10410, + 0x10411, + 0x10412, + 0x10413, + 0x10414, + 0x10415, + 0x10416, + 0x10417, + 0x10418, + 0x10419, + 0x1041A, + 0x1041B, + 0x1041C, + 0x1041D, + 0x1041E, + 0x1041F, + 0x10420, + 0x10421, + 0x10422, + 0x10423, + 0x10424, + 0x10425, + 0x10426, + 0x10427, + 0x10428, + 0x1049E, + 0x104A0, + 0x104AA, + 0x104B0, + 0x104B1, + 0x104B2, + 0x104B3, + 0x104B4, + 0x104B5, + 0x104B6, + 0x104B7, + 0x104B8, + 0x104B9, + 0x104BA, + 0x104BB, + 0x104BC, + 0x104BD, + 0x104BE, + 0x104BF, + 0x104C0, + 0x104C1, + 0x104C2, + 0x104C3, + 0x104C4, + 0x104C5, + 0x104C6, + 0x104C7, + 0x104C8, + 0x104C9, + 0x104CA, + 0x104CB, + 0x104CC, + 0x104CD, + 0x104CE, + 0x104CF, + 0x104D0, + 0x104D1, + 0x104D2, + 0x104D3, + 0x104D4, + 0x104D8, + 0x104FC, + 0x10500, + 0x10528, + 0x10530, + 0x10564, + 0x1056F, + 0x10570, + 0x10571, + 0x10572, + 0x10573, + 0x10574, + 0x10575, + 0x10576, + 0x10577, + 0x10578, + 0x10579, + 0x1057A, + 0x1057B, + 0x1057C, + 0x1057D, + 0x1057E, + 0x1057F, + 0x10580, + 0x10581, + 0x10582, + 0x10583, + 0x10584, + 0x10585, + 0x10586, + 0x10587, + 0x10588, + 0x10589, + 0x1058A, + 0x1058B, + 0x1058C, + 0x1058D, + 0x1058E, + 0x1058F, + 0x10590, + 0x10591, + 0x10592, + 0x10593, + 0x10594, + 0x10595, + 0x10596, + 0x10597, + 0x105A2, + 0x105A3, + 0x105B2, + 0x105B3, + 0x105BA, + 0x105BB, + 0x105BD, + 0x105C0, + 0x105F4, + 0x10600, + 0x10737, + 0x10740, + 0x10756, + 0x10760, + 0x10768, + 0x10780, + 0x10781, + 0x10782, + 0x10783, + 0x10784, + 0x10785, + 0x10786, + 0x10787, + 0x10788, + 0x10789, + 0x1078A, + 0x1078B, + 0x1078C, + 0x1078D, + 0x1078E, + 0x1078F, + 0x10790, + 0x10791, + 0x10792, + 0x10793, + 0x10794, + 0x10795, + 0x10796, + 0x10797, + 0x10798, + 0x10799, + 0x1079A, + 0x1079B, + 0x1079C, + 0x1079D, + 0x1079E, + 0x1079F, + 0x107A0, + 0x107A1, + 0x107A2, + 0x107A3, + 0x107A4, + 0x107A5, + 0x107A6, + 0x107A7, + 0x107A8, + 0x107A9, + 0x107AA, + 0x107AB, + 0x107AC, + 0x107AD, + 0x107AE, + 0x107AF, + 0x107B0, + 0x107B1, + 0x107B2, + 0x107B3, + 0x107B4, + 0x107B5, + 0x107B6, + 0x107B7, + 0x107B8, + 0x107B9, + 0x107BA, + 0x107BB, + 0x107BC, + 0x107BD, + 0x107BE, + 0x107BF, + 0x107C0, + 0x10800, + 0x10806, + 0x10808, + 0x10809, + 0x1080A, + 0x10836, + 0x10837, + 0x10839, + 0x1083C, + 0x1083D, + 0x1083F, + 0x10856, + 0x10857, + 0x1089F, + 0x108A7, + 0x108B0, + 0x108E0, + 0x108F3, + 0x108F4, + 0x108F6, + 0x108FB, + 0x1091C, + 0x1091F, + 0x1093A, + 0x1093F, + 0x1095A, + 0x10980, + 0x109B8, + 0x109BC, + 0x109D0, + 0x109D2, + 0x10A04, + 0x10A05, + 0x10A07, + 0x10A0C, + 0x10A14, + 0x10A15, + 0x10A18, + 0x10A19, + 0x10A36, + 0x10A38, + 0x10A3B, + 0x10A3F, + 0x10A49, + 0x10A50, + 0x10A59, + 0x10A60, + 0x10AA0, + 0x10AC0, + 0x10AE7, + 0x10AEB, + 0x10AF7, + 0x10B00, + 0x10B36, + 0x10B39, + 0x10B56, + 0x10B58, + 0x10B73, + 0x10B78, + 0x10B92, + 0x10B99, + 0x10B9D, + 0x10BA9, + 0x10BB0, + 0x10C00, + 0x10C49, + 0x10C80, + 0x10C81, + 0x10C82, + 0x10C83, + 0x10C84, + 0x10C85, + 0x10C86, + 0x10C87, + 0x10C88, + 0x10C89, + 0x10C8A, + 0x10C8B, + 0x10C8C, + 0x10C8D, + 0x10C8E, + 0x10C8F, + 0x10C90, + 0x10C91, + 0x10C92, + 0x10C93, + 0x10C94, + 0x10C95, + 0x10C96, + 0x10C97, + 0x10C98, + 0x10C99, + 0x10C9A, + 0x10C9B, + 0x10C9C, + 0x10C9D, + 0x10C9E, + 0x10C9F, + 0x10CA0, + 0x10CA1, + 0x10CA2, + 0x10CA3, + 0x10CA4, + 0x10CA5, + 0x10CA6, + 0x10CA7, + 0x10CA8, + 0x10CA9, + 0x10CAA, + 0x10CAB, + 0x10CAC, + 0x10CAD, + 0x10CAE, + 0x10CAF, + 0x10CB0, + 0x10CB1, + 0x10CB2, + 0x10CB3, + 0x10CC0, + 0x10CF3, + 0x10CFA, + 0x10D28, + 0x10D30, + 0x10D3A, + 0x10D40, + 0x10D50, + 0x10D51, + 0x10D52, + 0x10D53, + 0x10D54, + 0x10D55, + 0x10D56, + 0x10D57, + 0x10D58, + 0x10D59, + 0x10D5A, + 0x10D5B, + 0x10D5C, + 0x10D5D, + 0x10D5E, + 0x10D5F, + 0x10D60, + 0x10D61, + 0x10D62, + 0x10D63, + 0x10D64, + 0x10D65, + 0x10D66, + 0x10D69, + 0x10D86, + 0x10D8E, + 0x10D90, + 0x10E60, + 0x10E7F, + 0x10E80, + 0x10EAA, + 0x10EAB, + 0x10EAE, + 0x10EB0, + 0x10EB2, + 0x10EC2, + 0x10EC8, + 0x10EC9, + 0x10EEF, + 0x10EF0, + 0x10F28, + 0x10F30, + 0x10F5A, + 0x10F70, + 0x10F8A, + 0x10FB0, + 0x10FCC, + 0x10FE0, + 0x10FF7, + 0x11000, + 0x1104E, + 0x11052, + 0x11076, + 0x1107F, + 0x110BD, + 0x110BE, + 0x110C3, + 0x110D0, + 0x110E9, + 0x110F0, + 0x110FA, + 0x11100, + 0x11135, + 0x11136, + 0x11148, + 0x11150, + 0x11177, + 0x11180, + 0x111E0, + 0x111E1, + 0x111F5, + 0x11200, + 0x11212, + 0x11213, + 0x11242, + 0x11280, + 0x11287, + 0x11288, + 0x11289, + 0x1128A, + 0x1128E, + 0x1128F, + 0x1129E, + 0x1129F, + 0x112AA, + 0x112B0, + 0x112EB, + 0x112F0, + 0x112FA, + 0x11300, + 0x11304, + 0x11305, + 0x1130D, + 0x1130F, + 0x11311, + 0x11313, + 0x11329, + 0x1132A, + 0x11331, + 0x11332, + 0x11334, + 0x11335, + 0x1133A, + 0x1133B, + 0x11345, + 0x11347, + 0x11349, + 0x1134B, + 0x1134E, + 0x11350, + 0x11351, + 0x11357, + 0x11358, + 0x1135D, + 0x11364, + 0x11366, + 0x1136D, + 0x11370, + 0x11375, + 0x11380, + 0x1138A, + 0x1138B, + 0x1138C, + 0x1138E, + 0x1138F, + 0x11390, + 0x113B6, + 0x113B7, + 0x113C1, + 0x113C2, + 0x113C3, + 0x113C5, + 0x113C6, + 0x113C7, + 0x113CB, + 0x113CC, + 0x113D6, + 0x113D7, + 0x113D9, + 0x113E1, + 0x113E3, + 0x11400, + 0x1145C, + 0x1145D, + 0x11462, + 0x11480, + 0x114C8, + 0x114D0, + 0x114DA, + 0x11580, + 0x115B6, + 0x115B8, + 0x115DE, + 0x11600, + 0x11645, + 0x11650, + 0x1165A, + 0x11660, + 0x1166D, + 0x11680, + 0x116BA, + 0x116C0, + 0x116CA, + 0x116D0, + 0x116E4, + 0x11700, + 0x1171B, + 0x1171D, + 0x1172C, + 0x11730, + 0x11747, + 0x11800, + 0x1183C, + 0x118A0, + 0x118A1, + 0x118A2, + 0x118A3, + 0x118A4, + 0x118A5, + 0x118A6, + 0x118A7, + 0x118A8, + 0x118A9, + 0x118AA, + 0x118AB, + 0x118AC, + 0x118AD, + 0x118AE, + 0x118AF, + 0x118B0, + 0x118B1, + 0x118B2, + 0x118B3, + 0x118B4, + 0x118B5, + 0x118B6, + 0x118B7, + 0x118B8, + 0x118B9, + 0x118BA, + 0x118BB, + 0x118BC, + 0x118BD, + 0x118BE, + 0x118BF, + 0x118C0, + 0x118F3, + 0x118FF, + 0x11907, + 0x11909, + 0x1190A, + 0x1190C, + 0x11914, + 0x11915, + 0x11917, + 0x11918, + 0x11936, + 0x11937, + 0x11939, + 0x1193B, + 0x11947, + 0x11950, + 0x1195A, + 0x119A0, + 0x119A8, + 0x119AA, + 0x119D8, + 0x119DA, + 0x119E5, + 0x11A00, + 0x11A48, + 0x11A50, + 0x11AA3, + 0x11AB0, + 0x11AF9, + 0x11B00, + 0x11B0B, + 0x11B60, + 0x11B68, + 0x11BC0, + 0x11BE2, + 0x11BF0, + 0x11BFA, + 0x11C00, + 0x11C09, + 0x11C0A, + 0x11C37, + 0x11C38, + 0x11C46, + 0x11C50, + 0x11C6D, + 0x11C70, + 0x11C90, + 0x11C92, + 0x11CA8, + 0x11CA9, + 0x11CB7, + 0x11D00, + 0x11D07, + 0x11D08, + 0x11D0A, + 0x11D0B, + 0x11D37, + 0x11D3A, + 0x11D3B, + 0x11D3C, + 0x11D3E, + 0x11D3F, + 0x11D48, + 0x11D50, + 0x11D5A, + 0x11D60, + 0x11D66, + 0x11D67, + 0x11D69, + 0x11D6A, + 0x11D8F, + 0x11D90, + 0x11D92, + 0x11D93, + 0x11D99, + 0x11DA0, + 0x11DAA, + 0x11DB0, + 0x11DDC, + 0x11DE0, + 0x11DEA, + 0x11DF0, + 0x11DF2, + 0x11EE0, + 0x11EF9, + 0x11F00, + 0x11F11, + 0x11F12, + 0x11F3B, + 0x11F3E, + 0x11F5B, + 0x11FB0, + 0x11FB1, + 0x11FC0, + 0x11FF2, + 0x11FFF, + 0x1239A, + 0x12400, + 0x12544, + 0x12550, + 0x12687, + 0x12F90, + 0x12FF3, + 0x13000, + 0x13430, + 0x13440, + 0x13456, + 0x13460, + 0x143FB, + 0x14400, + 0x14647, + 0x16100, + 0x1613A, + 0x16800, + 0x16A39, + 0x16A40, + 0x16A5F, + 0x16A60, + 0x16A6A, + 0x16A6E, + 0x16ABF, + 0x16AC0, + 0x16ACA, + 0x16AD0, + 0x16AEE, + 0x16AF0, + 0x16AF6, + 0x16B00, + 0x16B46, + 0x16B50, + 0x16B5A, + 0x16B5B, + 0x16B62, + 0x16B63, + 0x16B78, + 0x16B7D, + 0x16B90, + 0x16D40, + 0x16D7A, + 0x16E40, + 0x16E41, + 0x16E42, + 0x16E43, + 0x16E44, + 0x16E45, + 0x16E46, + 0x16E47, + 0x16E48, + 0x16E49, + 0x16E4A, + 0x16E4B, + 0x16E4C, + 0x16E4D, + 0x16E4E, + 0x16E4F, + 0x16E50, + 0x16E51, + 0x16E52, + 0x16E53, + 0x16E54, + 0x16E55, + 0x16E56, + 0x16E57, + 0x16E58, + 0x16E59, + 0x16E5A, + 0x16E5B, + 0x16E5C, + 0x16E5D, + 0x16E5E, + 0x16E5F, + 0x16E60, + 0x16E9B, + 0x16EA0, + 0x16EA1, + 0x16EA2, + 0x16EA3, + 0x16EA4, + 0x16EA5, + 0x16EA6, + 0x16EA7, + 0x16EA8, + 0x16EA9, + 0x16EAA, + 0x16EAB, + 0x16EAC, + 0x16EAD, + 0x16EAE, + 0x16EAF, + 0x16EB0, + 0x16EB1, + 0x16EB2, + 0x16EB3, + 0x16EB4, + 0x16EB5, + 0x16EB6, + 0x16EB7, + 0x16EB8, + 0x16EB9, + 0x16EBB, + 0x16ED4, + 0x16F00, + 0x16F4B, + 0x16F4F, + 0x16F88, + 0x16F8F, + 0x16FA0, + 0x16FE0, + 0x16FE5, + 0x16FF0, + 0x16FF7, + 0x17000, + 0x18CDB, + 0x18CFF, + 0x18D21, + 0x18D80, + 0x18DF3, + 0x18E00, + 0x19192, + 0x191A0, + 0x191D3, + 0x1AFF0, + 0x1AFF4, + 0x1AFF5, + 0x1AFFC, + 0x1AFFD, + 0x1AFFF, + 0x1B000, + 0x1B123, + 0x1B124, + 0x1B125, + 0x1B126, + 0x1B127, + 0x1B129, + 0x1B132, + 0x1B133, + 0x1B150, + 0x1B153, + 0x1B155, + 0x1B156, + 0x1B164, + 0x1B169, + 0x1B170, + 0x1B2FC, + 0x1BC00, + 0x1BC6B, + 0x1BC70, + 0x1BC7D, + 0x1BC80, + 0x1BC89, + 0x1BC90, + 0x1BC9A, + 0x1BC9C, + 0x1BCA0, + 0x1BCA4, + 0x1CC00, + 0x1CCD6, + 0x1CCD7, + 0x1CCD8, + 0x1CCD9, + 0x1CCDA, + 0x1CCDB, + 0x1CCDC, + 0x1CCDD, + 0x1CCDE, + 0x1CCDF, + 0x1CCE0, + 0x1CCE1, + 0x1CCE2, + 0x1CCE3, + 0x1CCE4, + 0x1CCE5, + 0x1CCE6, + 0x1CCE7, + 0x1CCE8, + 0x1CCE9, + 0x1CCEA, + 0x1CCEB, + 0x1CCEC, + 0x1CCED, + 0x1CCEE, + 0x1CCEF, + 0x1CCF0, + 0x1CCF1, + 0x1CCF2, + 0x1CCF3, + 0x1CCF4, + 0x1CCF5, + 0x1CCF6, + 0x1CCF7, + 0x1CCF8, + 0x1CCF9, + 0x1CCFA, + 0x1CCFD, + 0x1CD00, + 0x1CEB4, + 0x1CEBA, + 0x1CED1, + 0x1CED2, + 0x1CED5, + 0x1CEDD, + 0x1CEFE, + 0x1CF00, + 0x1CF2E, + 0x1CF30, + 0x1CF47, + 0x1CF50, + 0x1CFC4, + 0x1D000, + 0x1D0F6, + 0x1D100, + 0x1D15E, + 0x1D15F, + 0x1D160, + 0x1D161, + 0x1D162, + 0x1D163, + 0x1D164, + 0x1D165, + 0x1D173, + 0x1D17B, + 0x1D1BB, + 0x1D1BC, + 0x1D1BD, + 0x1D1BE, + 0x1D1BF, + 0x1D1C0, + 0x1D1C1, + 0x1D246, + 0x1D250, + 0x1D282, + 0x1D2C0, + 0x1D2D4, + 0x1D2E0, + 0x1D2F4, + 0x1D300, + 0x1D357, + 0x1D360, + 0x1D379, + 0x1D400, + 0x1D401, + 0x1D402, + 0x1D403, + 0x1D404, + 0x1D405, + 0x1D406, + 0x1D407, + 0x1D408, + 0x1D409, + 0x1D40A, + 0x1D40B, + 0x1D40C, + 0x1D40D, + 0x1D40E, + 0x1D40F, + 0x1D410, + 0x1D411, + 0x1D412, + 0x1D413, + 0x1D414, + 0x1D415, + 0x1D416, + 0x1D417, + 0x1D418, + 0x1D419, + 0x1D41A, + 0x1D41B, + 0x1D41C, + 0x1D41D, + 0x1D41E, + 0x1D41F, + 0x1D420, + 0x1D421, + 0x1D422, + 0x1D423, + 0x1D424, + 0x1D425, + 0x1D426, + 0x1D427, + 0x1D428, + 0x1D429, + 0x1D42A, + 0x1D42B, + 0x1D42C, + 0x1D42D, + 0x1D42E, + 0x1D42F, + 0x1D430, + 0x1D431, + 0x1D432, + 0x1D433, + 0x1D434, + 0x1D435, + 0x1D436, + 0x1D437, + 0x1D438, + 0x1D439, + 0x1D43A, + 0x1D43B, + 0x1D43C, + 0x1D43D, + 0x1D43E, + 0x1D43F, + 0x1D440, + 0x1D441, + 0x1D442, + 0x1D443, + 0x1D444, + 0x1D445, + 0x1D446, + 0x1D447, + 0x1D448, + 0x1D449, + 0x1D44A, + 0x1D44B, + 0x1D44C, + 0x1D44D, + 0x1D44E, + 0x1D44F, + 0x1D450, + 0x1D451, + 0x1D452, + 0x1D453, + 0x1D454, + 0x1D455, + 0x1D456, + 0x1D457, + 0x1D458, + 0x1D459, + 0x1D45A, + 0x1D45B, + 0x1D45C, + 0x1D45D, + 0x1D45E, + 0x1D45F, + 0x1D460, + 0x1D461, + 0x1D462, + 0x1D463, + 0x1D464, + 0x1D465, + 0x1D466, + 0x1D467, + 0x1D468, + 0x1D469, + 0x1D46A, + 0x1D46B, + 0x1D46C, + 0x1D46D, + 0x1D46E, + 0x1D46F, + 0x1D470, + 0x1D471, + 0x1D472, + 0x1D473, + 0x1D474, + 0x1D475, + 0x1D476, + 0x1D477, + 0x1D478, + 0x1D479, + 0x1D47A, + 0x1D47B, + 0x1D47C, + 0x1D47D, + 0x1D47E, + 0x1D47F, + 0x1D480, + 0x1D481, + 0x1D482, + 0x1D483, + 0x1D484, + 0x1D485, + 0x1D486, + 0x1D487, + 0x1D488, + 0x1D489, + 0x1D48A, + 0x1D48B, + 0x1D48C, + 0x1D48D, + 0x1D48E, + 0x1D48F, + 0x1D490, + 0x1D491, + 0x1D492, + 0x1D493, + 0x1D494, + 0x1D495, + 0x1D496, + 0x1D497, + 0x1D498, + 0x1D499, + 0x1D49A, + 0x1D49B, + 0x1D49C, + 0x1D49D, + 0x1D49E, + 0x1D49F, + 0x1D4A0, + 0x1D4A2, + 0x1D4A3, + 0x1D4A5, + 0x1D4A6, + 0x1D4A7, + 0x1D4A9, + 0x1D4AA, + 0x1D4AB, + 0x1D4AC, + 0x1D4AD, + 0x1D4AE, + 0x1D4AF, + 0x1D4B0, + 0x1D4B1, + 0x1D4B2, + 0x1D4B3, + 0x1D4B4, + 0x1D4B5, + 0x1D4B6, + 0x1D4B7, + 0x1D4B8, + 0x1D4B9, + 0x1D4BA, + 0x1D4BB, + 0x1D4BC, + 0x1D4BD, + 0x1D4BE, + 0x1D4BF, + 0x1D4C0, + 0x1D4C1, + 0x1D4C2, + 0x1D4C3, + 0x1D4C4, + 0x1D4C5, + 0x1D4C6, + 0x1D4C7, + 0x1D4C8, + 0x1D4C9, + 0x1D4CA, + 0x1D4CB, + 0x1D4CC, + 0x1D4CD, + 0x1D4CE, + 0x1D4CF, + 0x1D4D0, + 0x1D4D1, + 0x1D4D2, + 0x1D4D3, + 0x1D4D4, + 0x1D4D5, + 0x1D4D6, + 0x1D4D7, + 0x1D4D8, + 0x1D4D9, + 0x1D4DA, + 0x1D4DB, + 0x1D4DC, + 0x1D4DD, + 0x1D4DE, + 0x1D4DF, + 0x1D4E0, + 0x1D4E1, + 0x1D4E2, + 0x1D4E3, + 0x1D4E4, + 0x1D4E5, + 0x1D4E6, + 0x1D4E7, + 0x1D4E8, + 0x1D4E9, + 0x1D4EA, + 0x1D4EB, + 0x1D4EC, + 0x1D4ED, + 0x1D4EE, + 0x1D4EF, + 0x1D4F0, + 0x1D4F1, + 0x1D4F2, + 0x1D4F3, + 0x1D4F4, + 0x1D4F5, + 0x1D4F6, + 0x1D4F7, + 0x1D4F8, + 0x1D4F9, + 0x1D4FA, + 0x1D4FB, + 0x1D4FC, + 0x1D4FD, + 0x1D4FE, + 0x1D4FF, + 0x1D500, + 0x1D501, + 0x1D502, + 0x1D503, + 0x1D504, + 0x1D505, + 0x1D506, + 0x1D507, + 0x1D508, + 0x1D509, + 0x1D50A, + 0x1D50B, + 0x1D50D, + 0x1D50E, + 0x1D50F, + 0x1D510, + 0x1D511, + 0x1D512, + 0x1D513, + 0x1D514, + 0x1D515, + 0x1D516, + 0x1D517, + 0x1D518, + 0x1D519, + 0x1D51A, + 0x1D51B, + 0x1D51C, + 0x1D51D, + 0x1D51E, + 0x1D51F, + 0x1D520, + 0x1D521, + 0x1D522, + 0x1D523, + 0x1D524, + 0x1D525, + 0x1D526, + 0x1D527, + 0x1D528, + 0x1D529, + 0x1D52A, + 0x1D52B, + 0x1D52C, + 0x1D52D, + 0x1D52E, + 0x1D52F, + 0x1D530, + 0x1D531, + 0x1D532, + 0x1D533, + 0x1D534, + 0x1D535, + 0x1D536, + 0x1D537, + 0x1D538, + 0x1D539, + 0x1D53A, + 0x1D53B, + 0x1D53C, + 0x1D53D, + 0x1D53E, + 0x1D53F, + 0x1D540, + 0x1D541, + 0x1D542, + 0x1D543, + 0x1D544, + 0x1D545, + 0x1D546, + 0x1D547, + 0x1D54A, + 0x1D54B, + 0x1D54C, + 0x1D54D, + 0x1D54E, + 0x1D54F, + 0x1D550, + 0x1D551, + 0x1D552, + 0x1D553, + 0x1D554, + 0x1D555, + 0x1D556, + 0x1D557, + 0x1D558, + 0x1D559, + 0x1D55A, + 0x1D55B, + 0x1D55C, + 0x1D55D, + 0x1D55E, + 0x1D55F, + 0x1D560, + 0x1D561, + 0x1D562, + 0x1D563, + 0x1D564, + 0x1D565, + 0x1D566, + 0x1D567, + 0x1D568, + 0x1D569, + 0x1D56A, + 0x1D56B, + 0x1D56C, + 0x1D56D, + 0x1D56E, + 0x1D56F, + 0x1D570, + 0x1D571, + 0x1D572, + 0x1D573, + 0x1D574, + 0x1D575, + 0x1D576, + 0x1D577, + 0x1D578, + 0x1D579, + 0x1D57A, + 0x1D57B, + 0x1D57C, + 0x1D57D, + 0x1D57E, + 0x1D57F, + 0x1D580, + 0x1D581, + 0x1D582, + 0x1D583, + 0x1D584, + 0x1D585, + 0x1D586, + 0x1D587, + 0x1D588, + 0x1D589, + 0x1D58A, + 0x1D58B, + 0x1D58C, + 0x1D58D, + 0x1D58E, + 0x1D58F, + 0x1D590, + 0x1D591, + 0x1D592, + 0x1D593, + 0x1D594, + 0x1D595, + 0x1D596, + 0x1D597, + 0x1D598, + 0x1D599, + 0x1D59A, + 0x1D59B, + 0x1D59C, + 0x1D59D, + 0x1D59E, + 0x1D59F, + 0x1D5A0, + 0x1D5A1, + 0x1D5A2, + 0x1D5A3, + 0x1D5A4, + 0x1D5A5, + 0x1D5A6, + 0x1D5A7, + 0x1D5A8, + 0x1D5A9, + 0x1D5AA, + 0x1D5AB, + 0x1D5AC, + 0x1D5AD, + 0x1D5AE, + 0x1D5AF, + 0x1D5B0, + 0x1D5B1, + 0x1D5B2, + 0x1D5B3, + 0x1D5B4, + 0x1D5B5, + 0x1D5B6, + 0x1D5B7, + 0x1D5B8, + 0x1D5B9, + 0x1D5BA, + 0x1D5BB, + 0x1D5BC, + 0x1D5BD, + 0x1D5BE, + 0x1D5BF, + 0x1D5C0, + 0x1D5C1, + 0x1D5C2, + 0x1D5C3, + 0x1D5C4, + 0x1D5C5, + 0x1D5C6, + 0x1D5C7, + 0x1D5C8, + 0x1D5C9, + 0x1D5CA, + 0x1D5CB, + 0x1D5CC, + 0x1D5CD, + 0x1D5CE, + 0x1D5CF, + 0x1D5D0, + 0x1D5D1, + 0x1D5D2, + 0x1D5D3, + 0x1D5D4, + 0x1D5D5, + 0x1D5D6, + 0x1D5D7, + 0x1D5D8, + 0x1D5D9, + 0x1D5DA, + 0x1D5DB, + 0x1D5DC, + 0x1D5DD, + 0x1D5DE, + 0x1D5DF, + 0x1D5E0, + 0x1D5E1, + 0x1D5E2, + 0x1D5E3, + 0x1D5E4, + 0x1D5E5, + 0x1D5E6, + 0x1D5E7, + 0x1D5E8, + 0x1D5E9, + 0x1D5EA, + 0x1D5EB, + 0x1D5EC, + 0x1D5ED, + 0x1D5EE, + 0x1D5EF, + 0x1D5F0, + 0x1D5F1, + 0x1D5F2, + 0x1D5F3, + 0x1D5F4, + 0x1D5F5, + 0x1D5F6, + 0x1D5F7, + 0x1D5F8, + 0x1D5F9, + 0x1D5FA, + 0x1D5FB, + 0x1D5FC, + 0x1D5FD, + 0x1D5FE, + 0x1D5FF, + 0x1D600, + 0x1D601, + 0x1D602, + 0x1D603, + 0x1D604, + 0x1D605, + 0x1D606, + 0x1D607, + 0x1D608, + 0x1D609, + 0x1D60A, + 0x1D60B, + 0x1D60C, + 0x1D60D, + 0x1D60E, + 0x1D60F, + 0x1D610, + 0x1D611, + 0x1D612, + 0x1D613, + 0x1D614, + 0x1D615, + 0x1D616, + 0x1D617, + 0x1D618, + 0x1D619, + 0x1D61A, + 0x1D61B, + 0x1D61C, + 0x1D61D, + 0x1D61E, + 0x1D61F, + 0x1D620, + 0x1D621, + 0x1D622, + 0x1D623, + 0x1D624, + 0x1D625, + 0x1D626, + 0x1D627, + 0x1D628, + 0x1D629, + 0x1D62A, + 0x1D62B, + 0x1D62C, + 0x1D62D, + 0x1D62E, + 0x1D62F, + 0x1D630, + 0x1D631, + 0x1D632, + 0x1D633, + 0x1D634, + 0x1D635, + 0x1D636, + 0x1D637, + 0x1D638, + 0x1D639, + 0x1D63A, + 0x1D63B, + 0x1D63C, + 0x1D63D, + 0x1D63E, + 0x1D63F, + 0x1D640, + 0x1D641, + 0x1D642, + 0x1D643, + 0x1D644, + 0x1D645, + 0x1D646, + 0x1D647, + 0x1D648, + 0x1D649, + 0x1D64A, + 0x1D64B, + 0x1D64C, + 0x1D64D, + 0x1D64E, + 0x1D64F, + 0x1D650, + 0x1D651, + 0x1D652, + 0x1D653, + 0x1D654, + 0x1D655, + 0x1D656, + 0x1D657, + 0x1D658, + 0x1D659, + 0x1D65A, + 0x1D65B, + 0x1D65C, + 0x1D65D, + 0x1D65E, + 0x1D65F, + 0x1D660, + 0x1D661, + 0x1D662, + 0x1D663, + 0x1D664, + 0x1D665, + 0x1D666, + 0x1D667, + 0x1D668, + 0x1D669, + 0x1D66A, + 0x1D66B, + 0x1D66C, + 0x1D66D, + 0x1D66E, + 0x1D66F, + 0x1D670, + 0x1D671, + 0x1D672, + 0x1D673, + 0x1D674, + 0x1D675, + 0x1D676, + 0x1D677, + 0x1D678, + 0x1D679, + 0x1D67A, + 0x1D67B, + 0x1D67C, + 0x1D67D, + 0x1D67E, + 0x1D67F, + 0x1D680, + 0x1D681, + 0x1D682, + 0x1D683, + 0x1D684, + 0x1D685, + 0x1D686, + 0x1D687, + 0x1D688, + 0x1D689, + 0x1D68A, + 0x1D68B, + 0x1D68C, + 0x1D68D, + 0x1D68E, + 0x1D68F, + 0x1D690, + 0x1D691, + 0x1D692, + 0x1D693, + 0x1D694, + 0x1D695, + 0x1D696, + 0x1D697, + 0x1D698, + 0x1D699, + 0x1D69A, + 0x1D69B, + 0x1D69C, + 0x1D69D, + 0x1D69E, + 0x1D69F, + 0x1D6A0, + 0x1D6A1, + 0x1D6A2, + 0x1D6A3, + 0x1D6A4, + 0x1D6A5, + 0x1D6A6, + 0x1D6A7, + 0x1D6A8, + 0x1D6A9, + 0x1D6AA, + 0x1D6AB, + 0x1D6AC, + 0x1D6AD, + 0x1D6AE, + 0x1D6AF, + 0x1D6B0, + 0x1D6B1, + 0x1D6B2, + 0x1D6B3, + 0x1D6B4, + 0x1D6B5, + 0x1D6B6, + 0x1D6B7, + 0x1D6B8, + 0x1D6B9, + 0x1D6BA, + 0x1D6BB, + 0x1D6BC, + 0x1D6BD, + 0x1D6BE, + 0x1D6BF, + 0x1D6C0, + 0x1D6C1, + 0x1D6C2, + 0x1D6C3, + 0x1D6C4, + 0x1D6C5, + 0x1D6C6, + 0x1D6C7, + 0x1D6C8, + 0x1D6C9, + 0x1D6CA, + 0x1D6CB, + 0x1D6CC, + 0x1D6CD, + 0x1D6CE, + 0x1D6CF, + 0x1D6D0, + 0x1D6D1, + 0x1D6D2, + 0x1D6D3, + 0x1D6D5, + 0x1D6D6, + 0x1D6D7, + 0x1D6D8, + 0x1D6D9, + 0x1D6DA, + 0x1D6DB, + 0x1D6DC, + 0x1D6DD, + 0x1D6DE, + 0x1D6DF, + 0x1D6E0, + 0x1D6E1, + 0x1D6E2, + 0x1D6E3, + 0x1D6E4, + 0x1D6E5, + 0x1D6E6, + 0x1D6E7, + 0x1D6E8, + 0x1D6E9, + 0x1D6EA, + 0x1D6EB, + 0x1D6EC, + 0x1D6ED, + 0x1D6EE, + 0x1D6EF, + 0x1D6F0, + 0x1D6F1, + 0x1D6F2, + 0x1D6F3, + 0x1D6F4, + 0x1D6F5, + 0x1D6F6, + 0x1D6F7, + 0x1D6F8, + 0x1D6F9, + 0x1D6FA, + 0x1D6FB, + 0x1D6FC, + 0x1D6FD, + 0x1D6FE, + 0x1D6FF, + 0x1D700, + 0x1D701, + 0x1D702, + 0x1D703, + 0x1D704, + 0x1D705, + 0x1D706, + 0x1D707, + 0x1D708, + 0x1D709, + 0x1D70A, + 0x1D70B, + 0x1D70C, + 0x1D70D, + 0x1D70F, + 0x1D710, + 0x1D711, + 0x1D712, + 0x1D713, + 0x1D714, + 0x1D715, + 0x1D716, + 0x1D717, + 0x1D718, + 0x1D719, + 0x1D71A, + 0x1D71B, + 0x1D71C, + 0x1D71D, + 0x1D71E, + 0x1D71F, + 0x1D720, + 0x1D721, + 0x1D722, + 0x1D723, + 0x1D724, + 0x1D725, + 0x1D726, + 0x1D727, + 0x1D728, + 0x1D729, + 0x1D72A, + 0x1D72B, + 0x1D72C, + 0x1D72D, + 0x1D72E, + 0x1D72F, + 0x1D730, + 0x1D731, + 0x1D732, + 0x1D733, + 0x1D734, + 0x1D735, + 0x1D736, + 0x1D737, + 0x1D738, + 0x1D739, + 0x1D73A, + 0x1D73B, + 0x1D73C, + 0x1D73D, + 0x1D73E, + 0x1D73F, + 0x1D740, + 0x1D741, + 0x1D742, + 0x1D743, + 0x1D744, + 0x1D745, + 0x1D746, + 0x1D747, + 0x1D749, + 0x1D74A, + 0x1D74B, + 0x1D74C, + 0x1D74D, + 0x1D74E, + 0x1D74F, + 0x1D750, + 0x1D751, + 0x1D752, + 0x1D753, + 0x1D754, + 0x1D755, + 0x1D756, + 0x1D757, + 0x1D758, + 0x1D759, + 0x1D75A, + 0x1D75B, + 0x1D75C, + 0x1D75D, + 0x1D75E, + 0x1D75F, + 0x1D760, + 0x1D761, + 0x1D762, + 0x1D763, + 0x1D764, + 0x1D765, + 0x1D766, + 0x1D767, + 0x1D768, + 0x1D769, + 0x1D76A, + 0x1D76B, + 0x1D76C, + 0x1D76D, + 0x1D76E, + 0x1D76F, + 0x1D770, + 0x1D771, + 0x1D772, + 0x1D773, + 0x1D774, + 0x1D775, + 0x1D776, + 0x1D777, + 0x1D778, + 0x1D779, + 0x1D77A, + 0x1D77B, + 0x1D77C, + 0x1D77D, + 0x1D77E, + 0x1D77F, + 0x1D780, + 0x1D781, + 0x1D783, + 0x1D784, + 0x1D785, + 0x1D786, + 0x1D787, + 0x1D788, + 0x1D789, + 0x1D78A, + 0x1D78B, + 0x1D78C, + 0x1D78D, + 0x1D78E, + 0x1D78F, + 0x1D790, + 0x1D791, + 0x1D792, + 0x1D793, + 0x1D794, + 0x1D795, + 0x1D796, + 0x1D797, + 0x1D798, + 0x1D799, + 0x1D79A, + 0x1D79B, + 0x1D79C, + 0x1D79D, + 0x1D79E, + 0x1D79F, + 0x1D7A0, + 0x1D7A1, + 0x1D7A2, + 0x1D7A3, + 0x1D7A4, + 0x1D7A5, + 0x1D7A6, + 0x1D7A7, + 0x1D7A8, + 0x1D7A9, + 0x1D7AA, + 0x1D7AB, + 0x1D7AC, + 0x1D7AD, + 0x1D7AE, + 0x1D7AF, + 0x1D7B0, + 0x1D7B1, + 0x1D7B2, + 0x1D7B3, + 0x1D7B4, + 0x1D7B5, + 0x1D7B6, + 0x1D7B7, + 0x1D7B8, + 0x1D7B9, + 0x1D7BA, + 0x1D7BB, + 0x1D7BD, + 0x1D7BE, + 0x1D7BF, + 0x1D7C0, + 0x1D7C1, + 0x1D7C2, + 0x1D7C3, + 0x1D7C4, + 0x1D7C5, + 0x1D7C6, + 0x1D7C7, + 0x1D7C8, + 0x1D7C9, + 0x1D7CA, + 0x1D7CC, + 0x1D7CE, + 0x1D7CF, + 0x1D7D0, + 0x1D7D1, + 0x1D7D2, + 0x1D7D3, + 0x1D7D4, + 0x1D7D5, + 0x1D7D6, + 0x1D7D7, + 0x1D7D8, + 0x1D7D9, + 0x1D7DA, + 0x1D7DB, + 0x1D7DC, + 0x1D7DD, + 0x1D7DE, + 0x1D7DF, + 0x1D7E0, + 0x1D7E1, + 0x1D7E2, + 0x1D7E3, + 0x1D7E4, + 0x1D7E5, + 0x1D7E6, + 0x1D7E7, + 0x1D7E8, + 0x1D7E9, + 0x1D7EA, + 0x1D7EB, + 0x1D7EC, + 0x1D7ED, + 0x1D7EE, + 0x1D7EF, + 0x1D7F0, + 0x1D7F1, + 0x1D7F2, + 0x1D7F3, + 0x1D7F4, + 0x1D7F5, + 0x1D7F6, + 0x1D7F7, + 0x1D7F8, + 0x1D7F9, + 0x1D7FA, + 0x1D7FB, + 0x1D7FC, + 0x1D7FD, + 0x1D7FE, + 0x1D7FF, + 0x1D800, + 0x1DA8C, + 0x1DA9B, + 0x1DAA0, + 0x1DAA1, + 0x1DAB0, + 0x1DB00, + 0x1DB1D, + 0x1DF00, + 0x1DF40, + 0x1DF41, + 0x1DF48, + 0x1DF49, + 0x1DF4A, + 0x1DF4B, + 0x1DF4D, + 0x1DF4E, + 0x1DF51, + 0x1DF52, + 0x1DF68, + 0x1DF69, + 0x1DF6A, + 0x1DF6B, + 0x1DF6C, + 0x1DF6D, + 0x1DF6E, + 0x1DF6F, + 0x1DF72, + 0x1DF73, + 0x1DF74, + 0x1DF75, + 0x1DF76, + 0x1DF77, + 0x1DF78, + 0x1DF79, + 0x1DF7A, + 0x1DF7B, + 0x1DF7C, + 0x1DF7D, + 0x1DF7E, + 0x1DF7F, + 0x1DF82, + 0x1DF90, + 0x1DF95, + 0x1DF96, + 0x1DF97, + 0x1DFCD, + 0x1DFCE, + 0x1DFCF, + 0x1DFD0, + 0x1DFD1, + 0x1DFD2, + 0x1DFD3, + 0x1DFD4, + 0x1DFD5, + 0x1DFD6, + 0x1DFD7, + 0x1DFD8, + 0x1DFD9, + 0x1DFDA, + 0x1DFDB, + 0x1DFDC, + 0x1DFDD, + 0x1DFDE, + 0x1DFDF, + 0x1DFE0, + 0x1DFE1, + 0x1DFE2, + 0x1DFE3, + 0x1DFE4, + 0x1DFE5, + 0x1DFE6, + 0x1DFE7, + 0x1DFE8, + 0x1DFE9, + 0x1DFEA, + 0x1DFEB, + 0x1DFEC, + 0x1DFED, + 0x1DFEE, + 0x1DFEF, + 0x1DFF0, + 0x1DFF1, + 0x1DFF2, + 0x1DFF3, + 0x1DFF4, + 0x1DFF5, + 0x1DFF6, + 0x1DFF7, + 0x1DFF8, + 0x1DFF9, + 0x1DFFA, + 0x1DFFB, + 0x1DFFC, + 0x1DFFD, + 0x1DFFE, + 0x1DFFF, + 0x1E000, + 0x1E007, + 0x1E008, + 0x1E019, + 0x1E01B, + 0x1E022, + 0x1E023, + 0x1E025, + 0x1E026, + 0x1E02B, + 0x1E030, + 0x1E031, + 0x1E032, + 0x1E033, + 0x1E034, + 0x1E035, + 0x1E036, + 0x1E037, + 0x1E038, + 0x1E039, + 0x1E03A, + 0x1E03B, + 0x1E03C, + 0x1E03D, + 0x1E03E, + 0x1E03F, + 0x1E040, + 0x1E041, + 0x1E042, + 0x1E043, + 0x1E044, + 0x1E045, + 0x1E046, + 0x1E047, + 0x1E048, + 0x1E049, + 0x1E04A, + 0x1E04B, + 0x1E04C, + 0x1E04D, + 0x1E04E, + 0x1E04F, + 0x1E050, + 0x1E051, + 0x1E052, + 0x1E053, + 0x1E054, + 0x1E055, + 0x1E056, + 0x1E057, + 0x1E058, + 0x1E059, + 0x1E05A, + 0x1E05B, + 0x1E05C, + 0x1E05D, + 0x1E05E, + 0x1E05F, + 0x1E060, + 0x1E061, + 0x1E062, + 0x1E063, + 0x1E064, + 0x1E065, + 0x1E066, + 0x1E067, + 0x1E068, + 0x1E069, + 0x1E06A, + 0x1E06B, + 0x1E06C, + 0x1E06D, + 0x1E06E, + 0x1E08F, + 0x1E090, + 0x1E100, + 0x1E12D, + 0x1E130, + 0x1E13E, + 0x1E140, + 0x1E14A, + 0x1E14E, + 0x1E150, + 0x1E290, + 0x1E2AF, + 0x1E2C0, + 0x1E2FA, + 0x1E2FF, + 0x1E300, + 0x1E4D0, + 0x1E4FA, + 0x1E5D0, + 0x1E5FB, + 0x1E5FF, + 0x1E600, + 0x1E6C0, + 0x1E6DF, + 0x1E6E0, + 0x1E6F6, + 0x1E6FE, + 0x1E700, + 0x1E7E0, + 0x1E7E7, + 0x1E7E8, + 0x1E7EC, + 0x1E7ED, + 0x1E7EF, + 0x1E7F0, + 0x1E7FF, + 0x1E800, + 0x1E8C5, + 0x1E8C7, + 0x1E8D7, + 0x1E900, + 0x1E901, + 0x1E902, + 0x1E903, + 0x1E904, + 0x1E905, + 0x1E906, + 0x1E907, + 0x1E908, + 0x1E909, + 0x1E90A, + 0x1E90B, + 0x1E90C, + 0x1E90D, + 0x1E90E, + 0x1E90F, + 0x1E910, + 0x1E911, + 0x1E912, + 0x1E913, + 0x1E914, + 0x1E915, + 0x1E916, + 0x1E917, + 0x1E918, + 0x1E919, + 0x1E91A, + 0x1E91B, + 0x1E91C, + 0x1E91D, + 0x1E91E, + 0x1E91F, + 0x1E920, + 0x1E921, + 0x1E922, + 0x1E94C, + 0x1E950, + 0x1E95A, + 0x1E95E, + 0x1E960, + 0x1EC71, + 0x1ECB5, + 0x1ED01, + 0x1ED3E, + 0x1EE00, + 0x1EE01, + 0x1EE02, + 0x1EE03, + 0x1EE04, + 0x1EE05, + 0x1EE06, + 0x1EE07, + 0x1EE08, + 0x1EE09, + 0x1EE0A, + 0x1EE0B, + 0x1EE0C, + 0x1EE0D, + 0x1EE0E, + 0x1EE0F, + 0x1EE10, + 0x1EE11, + 0x1EE12, + 0x1EE13, + 0x1EE14, + 0x1EE15, + 0x1EE16, + 0x1EE17, + 0x1EE18, + 0x1EE19, + 0x1EE1A, + 0x1EE1B, + 0x1EE1C, + 0x1EE1D, + 0x1EE1E, + 0x1EE1F, + 0x1EE20, + 0x1EE21, + 0x1EE22, + 0x1EE23, + 0x1EE24, + 0x1EE25, + 0x1EE27, + 0x1EE28, + 0x1EE29, + 0x1EE2A, + 0x1EE2B, + 0x1EE2C, + 0x1EE2D, + 0x1EE2E, + 0x1EE2F, + 0x1EE30, + 0x1EE31, + 0x1EE32, + 0x1EE33, + 0x1EE34, + 0x1EE35, + 0x1EE36, + 0x1EE37, + 0x1EE38, + 0x1EE39, + 0x1EE3A, + 0x1EE3B, + 0x1EE3C, + 0x1EE42, + 0x1EE43, + 0x1EE47, + 0x1EE48, + 0x1EE49, + 0x1EE4A, + 0x1EE4B, + 0x1EE4C, + 0x1EE4D, + 0x1EE4E, + 0x1EE4F, + 0x1EE50, + 0x1EE51, + 0x1EE52, + 0x1EE53, + 0x1EE54, + 0x1EE55, + 0x1EE57, + 0x1EE58, + 0x1EE59, + 0x1EE5A, + 0x1EE5B, + 0x1EE5C, + 0x1EE5D, + 0x1EE5E, + 0x1EE5F, + 0x1EE60, + 0x1EE61, + 0x1EE62, + 0x1EE63, + 0x1EE64, + 0x1EE65, + 0x1EE67, + 0x1EE68, + 0x1EE69, + 0x1EE6A, + 0x1EE6B, + 0x1EE6C, + 0x1EE6D, + 0x1EE6E, + 0x1EE6F, + 0x1EE70, + 0x1EE71, + 0x1EE72, + 0x1EE73, + 0x1EE74, + 0x1EE75, + 0x1EE76, + 0x1EE77, + 0x1EE78, + 0x1EE79, + 0x1EE7A, + 0x1EE7B, + 0x1EE7C, + 0x1EE7D, + 0x1EE7E, + 0x1EE7F, + 0x1EE80, + 0x1EE81, + 0x1EE82, + 0x1EE83, + 0x1EE84, + 0x1EE85, + 0x1EE86, + 0x1EE87, + 0x1EE88, + 0x1EE89, + 0x1EE8A, + 0x1EE8B, + 0x1EE8C, + 0x1EE8D, + 0x1EE8E, + 0x1EE8F, + 0x1EE90, + 0x1EE91, + 0x1EE92, + 0x1EE93, + 0x1EE94, + 0x1EE95, + 0x1EE96, + 0x1EE97, + 0x1EE98, + 0x1EE99, + 0x1EE9A, + 0x1EE9B, + 0x1EE9C, + 0x1EEA1, + 0x1EEA2, + 0x1EEA3, + 0x1EEA4, + 0x1EEA5, + 0x1EEA6, + 0x1EEA7, + 0x1EEA8, + 0x1EEA9, + 0x1EEAA, + 0x1EEAB, + 0x1EEAC, + 0x1EEAD, + 0x1EEAE, + 0x1EEAF, + 0x1EEB0, + 0x1EEB1, + 0x1EEB2, + 0x1EEB3, + 0x1EEB4, + 0x1EEB5, + 0x1EEB6, + 0x1EEB7, + 0x1EEB8, + 0x1EEB9, + 0x1EEBA, + 0x1EEBB, + 0x1EEBC, + 0x1EEF0, + 0x1EEF2, + 0x1F000, + 0x1F02C, + 0x1F030, + 0x1F094, + 0x1F0A0, + 0x1F0AF, + 0x1F0B1, + 0x1F0C0, + 0x1F0C1, + 0x1F0D0, + 0x1F0D1, + 0x1F0F6, + 0x1F101, + 0x1F102, + 0x1F103, + 0x1F104, + 0x1F105, + 0x1F106, + 0x1F107, + 0x1F108, + 0x1F109, + 0x1F10A, + 0x1F10B, + 0x1F110, + 0x1F111, + 0x1F112, + 0x1F113, + 0x1F114, + 0x1F115, + 0x1F116, + 0x1F117, + 0x1F118, + 0x1F119, + 0x1F11A, + 0x1F11B, + 0x1F11C, + 0x1F11D, + 0x1F11E, + 0x1F11F, + 0x1F120, + 0x1F121, + 0x1F122, + 0x1F123, + 0x1F124, + 0x1F125, + 0x1F126, + 0x1F127, + 0x1F128, + 0x1F129, + 0x1F12A, + 0x1F12B, + 0x1F12C, + 0x1F12D, + 0x1F12E, + 0x1F12F, + 0x1F130, + 0x1F131, + 0x1F132, + 0x1F133, + 0x1F134, + 0x1F135, + 0x1F136, + 0x1F137, + 0x1F138, + 0x1F139, + 0x1F13A, + 0x1F13B, + 0x1F13C, + 0x1F13D, + 0x1F13E, + 0x1F13F, + 0x1F140, + 0x1F141, + 0x1F142, + 0x1F143, + 0x1F144, + 0x1F145, + 0x1F146, + 0x1F147, + 0x1F148, + 0x1F149, + 0x1F14A, + 0x1F14B, + 0x1F14C, + 0x1F14D, + 0x1F14E, + 0x1F14F, + 0x1F150, + 0x1F16A, + 0x1F16B, + 0x1F16C, + 0x1F16D, + 0x1F190, + 0x1F191, + 0x1F1AF, + 0x1F1E6, + 0x1F200, + 0x1F201, + 0x1F202, + 0x1F203, + 0x1F210, + 0x1F211, + 0x1F212, + 0x1F213, + 0x1F214, + 0x1F215, + 0x1F216, + 0x1F217, + 0x1F218, + 0x1F219, + 0x1F21A, + 0x1F21B, + 0x1F21C, + 0x1F21D, + 0x1F21E, + 0x1F21F, + 0x1F220, + 0x1F221, + 0x1F222, + 0x1F223, + 0x1F224, + 0x1F225, + 0x1F226, + 0x1F227, + 0x1F228, + 0x1F229, + 0x1F22A, + 0x1F22B, + 0x1F22C, + 0x1F22D, + 0x1F22E, + 0x1F22F, + 0x1F230, + 0x1F231, + 0x1F232, + 0x1F233, + 0x1F234, + 0x1F235, + 0x1F236, + 0x1F237, + 0x1F238, + 0x1F239, + 0x1F23A, + 0x1F23B, + 0x1F23C, + 0x1F240, + 0x1F241, + 0x1F242, + 0x1F243, + 0x1F244, + 0x1F245, + 0x1F246, + 0x1F247, + 0x1F248, + 0x1F249, + 0x1F250, + 0x1F251, + 0x1F252, + 0x1F260, + 0x1F266, + 0x1F300, + 0x1F6DA, + 0x1F6DC, + 0x1F6ED, + 0x1F6F0, + 0x1F6FD, + 0x1F700, + 0x1F7DC, + 0x1F7E0, + 0x1F7EC, + 0x1F7F0, + 0x1F80C, + 0x1F810, + 0x1F848, + 0x1F850, + 0x1F85A, + 0x1F860, + 0x1F888, + 0x1F890, + 0x1F8AE, + 0x1F8B0, + 0x1F8BC, + 0x1F8C0, + 0x1F8C2, + 0x1F8D0, + 0x1F8D9, + 0x1F900, + 0x1FA58, + 0x1FA60, + 0x1FA6E, + 0x1FA70, + 0x1FA7D, + 0x1FA80, + 0x1FAC7, + 0x1FAC8, + 0x1FAC9, + 0x1FACC, + 0x1FADE, + 0x1FADF, + 0x1FAEC, + 0x1FAEF, + 0x1FAFB, + 0x1FB00, + 0x1FB93, + 0x1FB94, + 0x1FBF0, + 0x1FBF1, + 0x1FBF2, + 0x1FBF3, + 0x1FBF4, + 0x1FBF5, + 0x1FBF6, + 0x1FBF7, + 0x1FBF8, + 0x1FBF9, + 0x1FBFA, + 0x1FBFB, + 0x20000, + 0x2A6E0, + 0x2A700, + 0x2B81F, + 0x2B820, + 0x2CEAE, + 0x2CEB0, + 0x2EBE1, + 0x2EBF0, + 0x2EE5E, + 0x2F800, + 0x2F801, + 0x2F802, + 0x2F803, + 0x2F804, + 0x2F805, + 0x2F806, + 0x2F807, + 0x2F808, + 0x2F809, + 0x2F80A, + 0x2F80B, + 0x2F80C, + 0x2F80D, + 0x2F80E, + 0x2F80F, + 0x2F810, + 0x2F811, + 0x2F812, + 0x2F813, + 0x2F814, + 0x2F815, + 0x2F816, + 0x2F817, + 0x2F818, + 0x2F819, + 0x2F81A, + 0x2F81B, + 0x2F81C, + 0x2F81D, + 0x2F81E, + 0x2F81F, + 0x2F820, + 0x2F821, + 0x2F822, + 0x2F823, + 0x2F824, + 0x2F825, + 0x2F826, + 0x2F827, + 0x2F828, + 0x2F829, + 0x2F82A, + 0x2F82B, + 0x2F82C, + 0x2F82D, + 0x2F82E, + 0x2F82F, + 0x2F830, + 0x2F831, + 0x2F834, + 0x2F835, + 0x2F836, + 0x2F837, + 0x2F838, + 0x2F839, + 0x2F83A, + 0x2F83B, + 0x2F83C, + 0x2F83D, + 0x2F83E, + 0x2F83F, + 0x2F840, + 0x2F841, + 0x2F842, + 0x2F843, + 0x2F844, + 0x2F845, + 0x2F847, + 0x2F848, + 0x2F849, + 0x2F84A, + 0x2F84B, + 0x2F84C, + 0x2F84D, + 0x2F84E, + 0x2F84F, + 0x2F850, + 0x2F851, + 0x2F852, + 0x2F853, + 0x2F854, + 0x2F855, + 0x2F856, + 0x2F857, + 0x2F858, + 0x2F859, + 0x2F85A, + 0x2F85B, + 0x2F85C, + 0x2F85D, + 0x2F85E, + 0x2F85F, + 0x2F860, + 0x2F861, + 0x2F862, + 0x2F863, + 0x2F864, + 0x2F865, + 0x2F866, + 0x2F867, + 0x2F868, + 0x2F869, + 0x2F86A, + 0x2F86C, + 0x2F86D, + 0x2F86E, + 0x2F86F, + 0x2F870, + 0x2F871, + 0x2F872, + 0x2F873, + 0x2F874, + 0x2F875, + 0x2F876, + 0x2F877, + 0x2F878, + 0x2F879, + 0x2F87A, + 0x2F87B, + 0x2F87C, + 0x2F87D, + 0x2F87E, + 0x2F87F, + 0x2F880, + 0x2F881, + 0x2F882, + 0x2F883, + 0x2F884, + 0x2F885, + 0x2F886, + 0x2F887, + 0x2F888, + 0x2F889, + 0x2F88A, + 0x2F88B, + 0x2F88C, + 0x2F88D, + 0x2F88E, + 0x2F88F, + 0x2F890, + 0x2F891, + 0x2F893, + 0x2F894, + 0x2F896, + 0x2F897, + 0x2F898, + 0x2F899, + 0x2F89A, + 0x2F89B, + 0x2F89C, + 0x2F89D, + 0x2F89E, + 0x2F89F, + 0x2F8A0, + 0x2F8A1, + 0x2F8A2, + 0x2F8A3, + 0x2F8A4, + 0x2F8A5, + 0x2F8A6, + 0x2F8A7, + 0x2F8A8, + 0x2F8A9, + 0x2F8AA, + 0x2F8AB, + 0x2F8AC, + 0x2F8AD, + 0x2F8AE, + 0x2F8AF, + 0x2F8B0, + 0x2F8B1, + 0x2F8B2, + 0x2F8B3, + 0x2F8B4, + 0x2F8B5, + 0x2F8B6, + 0x2F8B7, + 0x2F8B8, + 0x2F8B9, + 0x2F8BA, + 0x2F8BB, + 0x2F8BC, + 0x2F8BD, + 0x2F8BE, + 0x2F8BF, + 0x2F8C0, + 0x2F8C1, + 0x2F8C2, + 0x2F8C3, + 0x2F8C4, + 0x2F8C5, + 0x2F8C6, + 0x2F8C7, + 0x2F8C8, + 0x2F8C9, + 0x2F8CA, + 0x2F8CB, + 0x2F8CC, + 0x2F8CD, + 0x2F8CE, + 0x2F8CF, + 0x2F8D0, + 0x2F8D1, + 0x2F8D2, + 0x2F8D3, + 0x2F8D4, + 0x2F8D5, + 0x2F8D6, + 0x2F8D7, + 0x2F8D8, + 0x2F8D9, + 0x2F8DA, + 0x2F8DB, + 0x2F8DC, + 0x2F8DD, + 0x2F8DE, + 0x2F8DF, + 0x2F8E0, + 0x2F8E1, + 0x2F8E2, + 0x2F8E3, + 0x2F8E4, + 0x2F8E5, + 0x2F8E6, + 0x2F8E7, + 0x2F8E8, + 0x2F8E9, + 0x2F8EA, + 0x2F8EB, + 0x2F8EC, + 0x2F8ED, + 0x2F8EE, + 0x2F8EF, + 0x2F8F0, + 0x2F8F1, + 0x2F8F2, + 0x2F8F3, + 0x2F8F4, + 0x2F8F5, + 0x2F8F6, + 0x2F8F7, + 0x2F8F8, + 0x2F8F9, + 0x2F8FA, + 0x2F8FB, + 0x2F8FC, + 0x2F8FD, + 0x2F8FE, + 0x2F8FF, + 0x2F900, + 0x2F901, + 0x2F902, + 0x2F903, + 0x2F904, + 0x2F905, + 0x2F906, + 0x2F907, + 0x2F908, + 0x2F909, + 0x2F90A, + 0x2F90B, + 0x2F90C, + 0x2F90D, + 0x2F90E, + 0x2F90F, + 0x2F910, + 0x2F911, + 0x2F912, + 0x2F913, + 0x2F914, + 0x2F915, + 0x2F916, + 0x2F917, + 0x2F918, + 0x2F919, + 0x2F91A, + 0x2F91B, + 0x2F91C, + 0x2F91D, + 0x2F91E, + 0x2F91F, + 0x2F920, + 0x2F921, + 0x2F922, + 0x2F923, + 0x2F924, + 0x2F925, + 0x2F926, + 0x2F927, + 0x2F928, + 0x2F929, + 0x2F92A, + 0x2F92B, + 0x2F92C, + 0x2F92E, + 0x2F92F, + 0x2F930, + 0x2F931, + 0x2F932, + 0x2F933, + 0x2F934, + 0x2F935, + 0x2F936, + 0x2F937, + 0x2F938, + 0x2F939, + 0x2F93A, + 0x2F93B, + 0x2F93C, + 0x2F93D, + 0x2F93E, + 0x2F93F, + 0x2F940, + 0x2F941, + 0x2F942, + 0x2F943, + 0x2F944, + 0x2F945, + 0x2F946, + 0x2F948, + 0x2F949, + 0x2F94A, + 0x2F94B, + 0x2F94C, + 0x2F94D, + 0x2F94E, + 0x2F94F, + 0x2F950, + 0x2F951, + 0x2F952, + 0x2F953, + 0x2F954, + 0x2F955, + 0x2F956, + 0x2F957, + 0x2F958, + 0x2F959, + 0x2F95A, + 0x2F95B, + 0x2F95C, + 0x2F95D, + 0x2F95F, + 0x2F960, + 0x2F961, + 0x2F962, + 0x2F963, + 0x2F964, + 0x2F965, + 0x2F966, + 0x2F967, + 0x2F968, + 0x2F969, + 0x2F96A, + 0x2F96B, + 0x2F96C, + 0x2F96D, + 0x2F96E, + 0x2F96F, + 0x2F970, + 0x2F971, + 0x2F972, + 0x2F973, + 0x2F974, + 0x2F975, + 0x2F976, + 0x2F977, + 0x2F978, + 0x2F979, + 0x2F97A, + 0x2F97B, + 0x2F97C, + 0x2F97D, + 0x2F97E, + 0x2F97F, + 0x2F980, + 0x2F981, + 0x2F982, + 0x2F983, + 0x2F984, + 0x2F985, + 0x2F986, + 0x2F987, + 0x2F988, + 0x2F989, + 0x2F98A, + 0x2F98B, + 0x2F98C, + 0x2F98D, + 0x2F98E, + 0x2F98F, + 0x2F990, + 0x2F991, + 0x2F992, + 0x2F993, + 0x2F994, + 0x2F995, + 0x2F996, + 0x2F997, + 0x2F998, + 0x2F999, + 0x2F99A, + 0x2F99B, + 0x2F99C, + 0x2F99D, + 0x2F99E, + 0x2F99F, + 0x2F9A0, + 0x2F9A1, + 0x2F9A2, + 0x2F9A3, + 0x2F9A4, + 0x2F9A5, + 0x2F9A6, + 0x2F9A7, + 0x2F9A8, + 0x2F9A9, + 0x2F9AA, + 0x2F9AB, + 0x2F9AC, + 0x2F9AD, + 0x2F9AE, + 0x2F9AF, + 0x2F9B0, + 0x2F9B1, + 0x2F9B2, + 0x2F9B3, + 0x2F9B4, + 0x2F9B5, + 0x2F9B6, + 0x2F9B7, + 0x2F9B8, + 0x2F9B9, + 0x2F9BA, + 0x2F9BB, + 0x2F9BC, + 0x2F9BD, + 0x2F9BE, + 0x2F9BF, + 0x2F9C0, + 0x2F9C1, + 0x2F9C2, + 0x2F9C3, + 0x2F9C4, + 0x2F9C5, + 0x2F9C6, + 0x2F9C7, + 0x2F9C8, + 0x2F9C9, + 0x2F9CA, + 0x2F9CB, + 0x2F9CC, + 0x2F9CD, + 0x2F9CE, + 0x2F9CF, + 0x2F9D0, + 0x2F9D1, + 0x2F9D2, + 0x2F9D3, + 0x2F9D4, + 0x2F9D5, + 0x2F9D6, + 0x2F9D7, + 0x2F9D8, + 0x2F9D9, + 0x2F9DA, + 0x2F9DB, + 0x2F9DC, + 0x2F9DD, + 0x2F9DE, + 0x2F9DF, + 0x2F9E0, + 0x2F9E1, + 0x2F9E2, + 0x2F9E3, + 0x2F9E4, + 0x2F9E5, + 0x2F9E6, + 0x2F9E7, + 0x2F9E8, + 0x2F9E9, + 0x2F9EA, + 0x2F9EB, + 0x2F9EC, + 0x2F9ED, + 0x2F9EE, + 0x2F9EF, + 0x2F9F0, + 0x2F9F1, + 0x2F9F2, + 0x2F9F3, + 0x2F9F4, + 0x2F9F5, + 0x2F9F6, + 0x2F9F7, + 0x2F9F8, + 0x2F9F9, + 0x2F9FA, + 0x2F9FB, + 0x2F9FC, + 0x2F9FD, + 0x2F9FE, + 0x2FA00, + 0x2FA01, + 0x2FA02, + 0x2FA03, + 0x2FA04, + 0x2FA05, + 0x2FA06, + 0x2FA07, + 0x2FA08, + 0x2FA09, + 0x2FA0A, + 0x2FA0B, + 0x2FA0C, + 0x2FA0D, + 0x2FA0E, + 0x2FA0F, + 0x2FA10, + 0x2FA11, + 0x2FA12, + 0x2FA13, + 0x2FA14, + 0x2FA15, + 0x2FA16, + 0x2FA17, + 0x2FA18, + 0x2FA19, + 0x2FA1A, + 0x2FA1B, + 0x2FA1C, + 0x2FA1D, + 0x2FA1E, + 0x30000, + 0x3134B, + 0x31350, + 0x3347A, + 0x3D000, + 0x3FC40, + 0xE0100, + 0xE01F0, + ), +) + +uts46_statuses: bytes = ( + b"VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV" + b"VMMMMMMMMMMMMMMMMMMMMMMMMMMVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV" + b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXMVVVVVVVMVMVVIVMVVMMMMVVMMMVMMMV" + b"MMMMMMMMMMMMMMMMMMMMMMMVMMMMMMMDVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV" + b"MVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMMVMVMVMVMVMMV" + b"MVMVMVMMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMMVMVMVMVMM" + b"VMVMMVMMMVMMMMVMMVMMMVMMVMMVMVMVMMVMVMVMMVMMMVMVMMVMVMMMMVMVMVMV" + b"MVMVMVMVMVMVMVMVMVMVMVMVMVMMVMMMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVM" + b"VMVMVMVMVMVMVMVMVMVMVMVMVMVMMVMMVMVMMMMVMVMVMVMVMMMMMMMMMVMMMMMM" + b"VMMMMMVMMVMMMVIVMVMVMVMVXMVMMXMMMMMMMXMXMMVMMMMMMMMMMMMMMMMMXMMM" + b"MMMMMMVDVMMMMMMMMVMVMVMVMVMVMVMVMVMVMVMVMVMMMVMMVMVMMVMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMVMVMVMVMVMVMVMVMVMVMV" + b"MVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMV" + b"MVMMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVM" + b"VMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVXMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMXMVMVMMVXVXVXVXVXVMMMMVXVXVXVXVXVXVXVXVX" + b"VXVXVXVMMMMMMMMVXVXVXVXVXVXVXVXVXVXVXMMXMVXVXVXVXVXVXVXVMXVMXVXV" + b"XVXVXVXVXMMMVXMXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVX" + b"VXVXMMXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXV" + b"XVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXV" + b"XVXVMVXVXVXVXVXVXVXVMVXVXVXVXVXMMVXVMVMVXVMVMVMVMVMVXVMVMMMMMVMV" + b"MVXVMVMVMVMVMVXVXVXVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMXMXV" + b"MVIVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXMMMMMMXVXVXVXVXVXVXVXVX" + b"VXVIVXVXVXVIVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVMMM" + b"MMMMMMVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMVXVXVMMMV" + b"MMMMMMMMMMMVMMMMMMMMMMMMMMMMMMVMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMVMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMVMVMVMVMVMVMVMVMVMVMVMVMVMV" + b"MVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMV" + b"MVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMMVMVM" + b"VMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVM" + b"VMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMMMMMMMMVXMMMMMMXVMMMMMMMMVMMMMMM" + b"MMVXMMMMMMXVXMXMXMXMVMMMMMMMMVMVMVMVMVMVMVMXMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMVMMMXVMMMMMMMMMMMMMMXVMMMMMMMMMVMXVM" + b"MMMXMMMVMVMMMMMMMMXMMMXVMMMMMMMMXMIDXVMVMVXVXMVMMVMMVMVMVMMMVMVM" + b"IXIMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMVMMMMMMMMMMMMMMMMVMVXVXMMMMVMMM" + b"VMMMMMMVMMVMMMVMMMVMVMVMVMMMMVMMMMMMMMMMVMMMMMVMMMMVMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMVMVXVMMVMMVMMVXVXMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMVMMMVMVXVMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMVMMMVMVMVMVMMMMVMVMV" + b"MMMMMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMV" + b"MVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVXVXVXVXVXMVXVXVXVX" + b"VXVXVXVXVXVXVXVXVXVMVMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMVMVMVMMMVXVXVMMVM" + b"VMXVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMIMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXVMMMMMMMMMMMMMMVXVMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMXMMMMMMMMM" + b"MMMMMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVXVXVXMVMVMVMVMVM" + b"VMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVM" + b"MVXVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMV" + b"MVMVMVMVMVMVMVMVMVMVMVMMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMMM" + b"MMVMMMMMVMVMVMVMVMVMVMVMMMMVMVMMVMVMVMVMVMVMVMVMMXMXMMMMMVMMVXVX" + b"VXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVMMMMVMVMMXMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMV" + b"XVXVXVXVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMVMVMVMMMMMMMMMMVMVMVMMVMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMMMXMMMMMXMVMMMMMMMMMMMMMMMMMMMMM" + b"MMMXMMMMMXMXMMXMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVXMMMMMMMMMMMM" + b"MVIMMXMMMMMMXVXMMMMMMMMMMMMMMMMMMMVMMMMMMXMMMMMMMMMMMMMMMMMMMXMM" + b"MMXMMMVMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXIXMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMIMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMM" + b"MMMXMMMMMMXMMMMMMXMMMXMMMMMMMXMMMMMMMXVXVXVXVXVXVXVXVXVXVXVXVXVX" + b"VXVXVXVXVXVXVXVXVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVXVXMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXVXVXVXVMMMMMMMMMMMXMMMMMMMMMM" + b"MMMMMXMMMMMMMXMMXVXVXVXVXVXVXVXVXVMMMMMXMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMXMMMMMMMMMMMMMMXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVX" + b"VXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMXVXVXVXVMMMMMMMMMMMMMMMMMMMMMMXVXVXVXVXVX" + b"VXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVX" + b"VXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVX" + b"VXVXVXVXVXVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVXVXVXVXVXVXVXVXVXVX" + b"VXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVX" + b"VXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMVXMMMMMMMMMMMMMMMMMMMMMMMMMXVXVXVXVXVXVX" + b"VXVXVXVXVXVXVXVXVMMMMVXVXVXVXVXVXVXVXVXVXVIXVMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMVXVXVXVXVXVXVXVXVXVMMMMMMMVIVMMMMMMVXVXVXVXVXVX" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMXMXMMXMMMMXMMMMMMMMMMMMXMXMMMMMM" + b"MXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMXMMMMXMMMMMMMMXMMMMMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMXMMMM" + b"MXMXMMMMMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"XMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVXVXVXVXVMVMV" + b"MVMVMVMVMVMVMVMVMVMVMVMVMVMVXVMVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMVXVXVXVXVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXV" + b"XVXVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVXVXVXVXVXMMMMXMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMXMMXMXMXMMMMMMMMMMXMMMMXMXMXMXMXMXMXMMMXMMXMXMX" + b"MXMXMXMXMMXMXMMMMXMMMMMMMXMMMMXMMMMXMXMMMMMMMMMMXMMMMMMMMMMMMMMM" + b"MMXMMMXMMMMMXMMMMMMMMMMMMMMMMMXVXVXVXVXVXVXVXMMMMMMMMMMVMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMMMVMVX" + b"VMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMMMMMMXMMXV" + b"XVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVMMMMMMMMMMVXVXVXVX" + b"VXVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + b"MMMMMMMMMMMMMMMMMMMMMMMMXVXVXVXIX" +) + +uts46_replacements: tuple[str | None, ...] = ( + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + " ", + None, + None, + None, + None, + None, + None, + None, + " ̈", + None, + "a", + None, + None, + None, + None, + " ̄", + None, + None, + "2", + "3", + " ́", + "μ", + None, + None, + " ̧", + "1", + "o", + None, + "1⁄4", + "1⁄2", + "3⁄4", + None, + "à", + "á", + "â", + "ã", + "ä", + "å", + "æ", + "ç", + "è", + "é", + "ê", + "ë", + "ì", + "í", + "î", + "ï", + "ð", + "ñ", + "ò", + "ó", + "ô", + "õ", + "ö", + None, + "ø", + "ù", + "ú", + "û", + "ü", + "ý", + "þ", + "ss", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "ā", + None, + "ă", + None, + "ą", + None, + "ć", + None, + "ĉ", + None, + "ċ", + None, + "č", + None, + "ď", + None, + "đ", + None, + "ē", + None, + "ĕ", + None, + "ė", + None, + "ę", + None, + "ě", + None, + "ĝ", + None, + "ğ", + None, + "ġ", + None, + "ģ", + None, + "ĥ", + None, + "ħ", + None, + "ĩ", + None, + "ī", + None, + "ĭ", + None, + "į", + None, + "i̇", + None, + "ij", + "ĵ", + None, + "ķ", + None, + "ĺ", + None, + "ļ", + None, + "ľ", + None, + "l·", + "ł", + None, + "ń", + None, + "ņ", + None, + "ň", + None, + "ʼn", + "ŋ", + None, + "ō", + None, + "ŏ", + None, + "ő", + None, + "œ", + None, + "ŕ", + None, + "ŗ", + None, + "ř", + None, + "ś", + None, + "ŝ", + None, + "ş", + None, + "š", + None, + "ţ", + None, + "ť", + None, + "ŧ", + None, + "ũ", + None, + "ū", + None, + "ŭ", + None, + "ů", + None, + "ű", + None, + "ų", + None, + "ŵ", + None, + "ŷ", + None, + "ÿ", + "ź", + None, + "ż", + None, + "ž", + None, + "s", + None, + "ɓ", + "ƃ", + None, + "ƅ", + None, + "ɔ", + "ƈ", + None, + "ɖ", + "ɗ", + "ƌ", + None, + "ǝ", + "ə", + "ɛ", + "ƒ", + None, + "ɠ", + "ɣ", + None, + "ɩ", + "ɨ", + "ƙ", + None, + "ɯ", + "ɲ", + None, + "ɵ", + "ơ", + None, + "ƣ", + None, + "ƥ", + None, + "ʀ", + "ƨ", + None, + "ʃ", + None, + "ƭ", + None, + "ʈ", + "ư", + None, + "ʊ", + "ʋ", + "ƴ", + None, + "ƶ", + None, + "ʒ", + "ƹ", + None, + "ƽ", + None, + "dž", + "lj", + "nj", + "ǎ", + None, + "ǐ", + None, + "ǒ", + None, + "ǔ", + None, + "ǖ", + None, + "ǘ", + None, + "ǚ", + None, + "ǜ", + None, + "ǟ", + None, + "ǡ", + None, + "ǣ", + None, + "ǥ", + None, + "ǧ", + None, + "ǩ", + None, + "ǫ", + None, + "ǭ", + None, + "ǯ", + None, + "dz", + "ǵ", + None, + "ƕ", + "ƿ", + "ǹ", + None, + "ǻ", + None, + "ǽ", + None, + "ǿ", + None, + "ȁ", + None, + "ȃ", + None, + "ȅ", + None, + "ȇ", + None, + "ȉ", + None, + "ȋ", + None, + "ȍ", + None, + "ȏ", + None, + "ȑ", + None, + "ȓ", + None, + "ȕ", + None, + "ȗ", + None, + "ș", + None, + "ț", + None, + "ȝ", + None, + "ȟ", + None, + "ƞ", + None, + "ȣ", + None, + "ȥ", + None, + "ȧ", + None, + "ȩ", + None, + "ȫ", + None, + "ȭ", + None, + "ȯ", + None, + "ȱ", + None, + "ȳ", + None, + "ⱥ", + "ȼ", + None, + "ƚ", + "ⱦ", + None, + "ɂ", + None, + "ƀ", + "ʉ", + "ʌ", + "ɇ", + None, + "ɉ", + None, + "ɋ", + None, + "ɍ", + None, + "ɏ", + None, + "h", + "ɦ", + "j", + "r", + "ɹ", + "ɻ", + "ʁ", + "w", + "y", + None, + " ̆", + " ̇", + " ̊", + " ̨", + " ̃", + " ̋", + None, + "ɣ", + "l", + "s", + "x", + "ʕ", + None, + "̀", + "́", + None, + "̓", + "̈́", + "ι", + None, + None, + None, + "ͱ", + None, + "ͳ", + None, + "ʹ", + None, + "ͷ", + None, + None, + " ι", + None, + ";", + "ϳ", + None, + " ́", + " ̈́", + "ά", + "·", + "έ", + "ή", + "ί", + None, + "ό", + None, + "ύ", + "ώ", + None, + "α", + "β", + "γ", + "δ", + "ε", + "ζ", + "η", + "θ", + "ι", + "κ", + "λ", + "μ", + "ν", + "ξ", + "ο", + "π", + "ρ", + None, + "σ", + "τ", + "υ", + "φ", + "χ", + "ψ", + "ω", + "ϊ", + "ϋ", + None, + "σ", + None, + "ϗ", + "β", + "θ", + "υ", + "ύ", + "ϋ", + "φ", + "π", + None, + "ϙ", + None, + "ϛ", + None, + "ϝ", + None, + "ϟ", + None, + "ϡ", + None, + "ϣ", + None, + "ϥ", + None, + "ϧ", + None, + "ϩ", + None, + "ϫ", + None, + "ϭ", + None, + "ϯ", + None, + "κ", + "ρ", + "σ", + None, + "θ", + "ε", + None, + "ϸ", + None, + "σ", + "ϻ", + None, + "ͻ", + "ͼ", + "ͽ", + "ѐ", + "ё", + "ђ", + "ѓ", + "є", + "ѕ", + "і", + "ї", + "ј", + "љ", + "њ", + "ћ", + "ќ", + "ѝ", + "ў", + "џ", + "а", + "б", + "в", + "г", + "д", + "е", + "ж", + "з", + "и", + "й", + "к", + "л", + "м", + "н", + "о", + "п", + "р", + "с", + "т", + "у", + "ф", + "х", + "ц", + "ч", + "ш", + "щ", + "ъ", + "ы", + "ь", + "э", + "ю", + "я", + None, + "ѡ", + None, + "ѣ", + None, + "ѥ", + None, + "ѧ", + None, + "ѩ", + None, + "ѫ", + None, + "ѭ", + None, + "ѯ", + None, + "ѱ", + None, + "ѳ", + None, + "ѵ", + None, + "ѷ", + None, + "ѹ", + None, + "ѻ", + None, + "ѽ", + None, + "ѿ", + None, + "ҁ", + None, + "ҋ", + None, + "ҍ", + None, + "ҏ", + None, + "ґ", + None, + "ғ", + None, + "ҕ", + None, + "җ", + None, + "ҙ", + None, + "қ", + None, + "ҝ", + None, + "ҟ", + None, + "ҡ", + None, + "ң", + None, + "ҥ", + None, + "ҧ", + None, + "ҩ", + None, + "ҫ", + None, + "ҭ", + None, + "ү", + None, + "ұ", + None, + "ҳ", + None, + "ҵ", + None, + "ҷ", + None, + "ҹ", + None, + "һ", + None, + "ҽ", + None, + "ҿ", + None, + "ӏ", + "ӂ", + None, + "ӄ", + None, + "ӆ", + None, + "ӈ", + None, + "ӊ", + None, + "ӌ", + None, + "ӎ", + None, + "ӑ", + None, + "ӓ", + None, + "ӕ", + None, + "ӗ", + None, + "ә", + None, + "ӛ", + None, + "ӝ", + None, + "ӟ", + None, + "ӡ", + None, + "ӣ", + None, + "ӥ", + None, + "ӧ", + None, + "ө", + None, + "ӫ", + None, + "ӭ", + None, + "ӯ", + None, + "ӱ", + None, + "ӳ", + None, + "ӵ", + None, + "ӷ", + None, + "ӹ", + None, + "ӻ", + None, + "ӽ", + None, + "ӿ", + None, + "ԁ", + None, + "ԃ", + None, + "ԅ", + None, + "ԇ", + None, + "ԉ", + None, + "ԋ", + None, + "ԍ", + None, + "ԏ", + None, + "ԑ", + None, + "ԓ", + None, + "ԕ", + None, + "ԗ", + None, + "ԙ", + None, + "ԛ", + None, + "ԝ", + None, + "ԟ", + None, + "ԡ", + None, + "ԣ", + None, + "ԥ", + None, + "ԧ", + None, + "ԩ", + None, + "ԫ", + None, + "ԭ", + None, + "ԯ", + None, + None, + "ա", + "բ", + "գ", + "դ", + "ե", + "զ", + "է", + "ը", + "թ", + "ժ", + "ի", + "լ", + "խ", + "ծ", + "կ", + "հ", + "ձ", + "ղ", + "ճ", + "մ", + "յ", + "ն", + "շ", + "ո", + "չ", + "պ", + "ջ", + "ռ", + "ս", + "վ", + "տ", + "ր", + "ց", + "ւ", + "փ", + "ք", + "օ", + "ֆ", + None, + "է", + None, + "եւ", + None, + "ի", + "յ", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "اٴ", + "وٴ", + "ۇٴ", + "يٴ", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "क़", + "ख़", + "ग़", + "ज़", + "ड़", + "ढ़", + "फ़", + "य़", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "ড়", + "ঢ়", + None, + "য়", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "ਲ਼", + None, + None, + "ਸ਼", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "ਖ਼", + "ਗ਼", + "ਜ਼", + None, + None, + "ਫ਼", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "ଡ଼", + "ଢ଼", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "ํา", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "ໍາ", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "ຫນ", + "ຫມ", + None, + None, + None, + "་", + None, + "གྷ", + None, + None, + None, + "ཌྷ", + None, + "དྷ", + None, + "བྷ", + None, + "ཛྷ", + None, + "ཀྵ", + None, + None, + None, + "ཱི", + None, + "ཱུ", + "ྲྀ", + "ྲཱྀ", + "ླྀ", + "ླཱྀ", + None, + "ཱྀ", + None, + "ྒྷ", + None, + None, + None, + "ྜྷ", + None, + "ྡྷ", + None, + "ྦྷ", + None, + "ྫྷ", + None, + "ྐྵ", + None, + None, + None, + None, + None, + None, + None, + "ⴀ", + "ⴁ", + "ⴂ", + "ⴃ", + "ⴄ", + "ⴅ", + "ⴆ", + "ⴇ", + "ⴈ", + "ⴉ", + "ⴊ", + "ⴋ", + "ⴌ", + "ⴍ", + "ⴎ", + "ⴏ", + "ⴐ", + "ⴑ", + "ⴒ", + "ⴓ", + "ⴔ", + "ⴕ", + "ⴖ", + "ⴗ", + "ⴘ", + "ⴙ", + "ⴚ", + "ⴛ", + "ⴜ", + "ⴝ", + "ⴞ", + "ⴟ", + "ⴠ", + "ⴡ", + "ⴢ", + "ⴣ", + "ⴤ", + "ⴥ", + None, + "ⴧ", + None, + "ⴭ", + None, + None, + "ნ", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "Ᏸ", + "Ᏹ", + "Ᏺ", + "Ᏻ", + "Ᏼ", + "Ᏽ", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "в", + "д", + "о", + "с", + "т", + "ъ", + "ѣ", + "ꙋ", + "ᲊ", + None, + None, + "ა", + "ბ", + "გ", + "დ", + "ე", + "ვ", + "ზ", + "თ", + "ი", + "კ", + "ლ", + "მ", + "ნ", + "ო", + "პ", + "ჟ", + "რ", + "ს", + "ტ", + "უ", + "ფ", + "ქ", + "ღ", + "ყ", + "შ", + "ჩ", + "ც", + "ძ", + "წ", + "ჭ", + "ხ", + "ჯ", + "ჰ", + "ჱ", + "ჲ", + "ჳ", + "ჴ", + "ჵ", + "ჶ", + "ჷ", + "ჸ", + "ჹ", + "ჺ", + None, + "ჽ", + "ჾ", + "ჿ", + None, + None, + None, + None, + None, + "a", + "æ", + "b", + None, + "d", + "e", + "ǝ", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + None, + "o", + "ȣ", + "p", + "r", + "t", + "u", + "w", + "a", + "ɐ", + "ɑ", + "ᴂ", + "b", + "d", + "e", + "ə", + "ɛ", + "ɜ", + "g", + None, + "k", + "m", + "ŋ", + "o", + "ɔ", + "ᴖ", + "ᴗ", + "p", + "t", + "u", + "ᴝ", + "ɯ", + "v", + "ᴥ", + "β", + "γ", + "δ", + "φ", + "χ", + "i", + "r", + "u", + "v", + "β", + "γ", + "ρ", + "φ", + "χ", + None, + "н", + None, + "ɒ", + "c", + "ɕ", + "ð", + "ɜ", + "f", + "ɟ", + "ɡ", + "ɥ", + "ɨ", + "ɩ", + "ɪ", + "ᵻ", + "ʝ", + "ɭ", + "ᶅ", + "ʟ", + "ɱ", + "ɰ", + "ɲ", + "ɳ", + "ɴ", + "ɵ", + "ɸ", + "ʂ", + "ʃ", + "ƫ", + "ʉ", + "ʊ", + "ᴜ", + "ʋ", + "ʌ", + "z", + "ʐ", + "ʑ", + "ʒ", + "θ", + None, + "ḁ", + None, + "ḃ", + None, + "ḅ", + None, + "ḇ", + None, + "ḉ", + None, + "ḋ", + None, + "ḍ", + None, + "ḏ", + None, + "ḑ", + None, + "ḓ", + None, + "ḕ", + None, + "ḗ", + None, + "ḙ", + None, + "ḛ", + None, + "ḝ", + None, + "ḟ", + None, + "ḡ", + None, + "ḣ", + None, + "ḥ", + None, + "ḧ", + None, + "ḩ", + None, + "ḫ", + None, + "ḭ", + None, + "ḯ", + None, + "ḱ", + None, + "ḳ", + None, + "ḵ", + None, + "ḷ", + None, + "ḹ", + None, + "ḻ", + None, + "ḽ", + None, + "ḿ", + None, + "ṁ", + None, + "ṃ", + None, + "ṅ", + None, + "ṇ", + None, + "ṉ", + None, + "ṋ", + None, + "ṍ", + None, + "ṏ", + None, + "ṑ", + None, + "ṓ", + None, + "ṕ", + None, + "ṗ", + None, + "ṙ", + None, + "ṛ", + None, + "ṝ", + None, + "ṟ", + None, + "ṡ", + None, + "ṣ", + None, + "ṥ", + None, + "ṧ", + None, + "ṩ", + None, + "ṫ", + None, + "ṭ", + None, + "ṯ", + None, + "ṱ", + None, + "ṳ", + None, + "ṵ", + None, + "ṷ", + None, + "ṹ", + None, + "ṻ", + None, + "ṽ", + None, + "ṿ", + None, + "ẁ", + None, + "ẃ", + None, + "ẅ", + None, + "ẇ", + None, + "ẉ", + None, + "ẋ", + None, + "ẍ", + None, + "ẏ", + None, + "ẑ", + None, + "ẓ", + None, + "ẕ", + None, + "aʾ", + "ṡ", + None, + "ß", + None, + "ạ", + None, + "ả", + None, + "ấ", + None, + "ầ", + None, + "ẩ", + None, + "ẫ", + None, + "ậ", + None, + "ắ", + None, + "ằ", + None, + "ẳ", + None, + "ẵ", + None, + "ặ", + None, + "ẹ", + None, + "ẻ", + None, + "ẽ", + None, + "ế", + None, + "ề", + None, + "ể", + None, + "ễ", + None, + "ệ", + None, + "ỉ", + None, + "ị", + None, + "ọ", + None, + "ỏ", + None, + "ố", + None, + "ồ", + None, + "ổ", + None, + "ỗ", + None, + "ộ", + None, + "ớ", + None, + "ờ", + None, + "ở", + None, + "ỡ", + None, + "ợ", + None, + "ụ", + None, + "ủ", + None, + "ứ", + None, + "ừ", + None, + "ử", + None, + "ữ", + None, + "ự", + None, + "ỳ", + None, + "ỵ", + None, + "ỷ", + None, + "ỹ", + None, + "ỻ", + None, + "ỽ", + None, + "ỿ", + None, + "ἀ", + "ἁ", + "ἂ", + "ἃ", + "ἄ", + "ἅ", + "ἆ", + "ἇ", + None, + None, + "ἐ", + "ἑ", + "ἒ", + "ἓ", + "ἔ", + "ἕ", + None, + None, + "ἠ", + "ἡ", + "ἢ", + "ἣ", + "ἤ", + "ἥ", + "ἦ", + "ἧ", + None, + "ἰ", + "ἱ", + "ἲ", + "ἳ", + "ἴ", + "ἵ", + "ἶ", + "ἷ", + None, + None, + "ὀ", + "ὁ", + "ὂ", + "ὃ", + "ὄ", + "ὅ", + None, + None, + None, + "ὑ", + None, + "ὓ", + None, + "ὕ", + None, + "ὗ", + None, + "ὠ", + "ὡ", + "ὢ", + "ὣ", + "ὤ", + "ὥ", + "ὦ", + "ὧ", + None, + "ά", + None, + "έ", + None, + "ή", + None, + "ί", + None, + "ό", + None, + "ύ", + None, + "ώ", + None, + "ἀι", + "ἁι", + "ἂι", + "ἃι", + "ἄι", + "ἅι", + "ἆι", + "ἇι", + "ἀι", + "ἁι", + "ἂι", + "ἃι", + "ἄι", + "ἅι", + "ἆι", + "ἇι", + "ἠι", + "ἡι", + "ἢι", + "ἣι", + "ἤι", + "ἥι", + "ἦι", + "ἧι", + "ἠι", + "ἡι", + "ἢι", + "ἣι", + "ἤι", + "ἥι", + "ἦι", + "ἧι", + "ὠι", + "ὡι", + "ὢι", + "ὣι", + "ὤι", + "ὥι", + "ὦι", + "ὧι", + "ὠι", + "ὡι", + "ὢι", + "ὣι", + "ὤι", + "ὥι", + "ὦι", + "ὧι", + None, + "ὰι", + "αι", + "άι", + None, + None, + "ᾶι", + "ᾰ", + "ᾱ", + "ὰ", + "ά", + "αι", + " ̓", + "ι", + " ̓", + " ͂", + " ̈͂", + "ὴι", + "ηι", + "ήι", + None, + None, + "ῆι", + "ὲ", + "έ", + "ὴ", + "ή", + "ηι", + " ̓̀", + " ̓́", + " ̓͂", + None, + "ΐ", + None, + None, + "ῐ", + "ῑ", + "ὶ", + "ί", + None, + " ̔̀", + " ̔́", + " ̔͂", + None, + "ΰ", + None, + "ῠ", + "ῡ", + "ὺ", + "ύ", + "ῥ", + " ̈̀", + " ̈́", + "`", + None, + "ὼι", + "ωι", + "ώι", + None, + None, + "ῶι", + "ὸ", + "ό", + "ὼ", + "ώ", + "ωι", + " ́", + " ̔", + None, + " ", + None, + "", + None, + None, + "‐", + None, + " ̳", + None, + None, + None, + None, + " ", + None, + "′′", + "′′′", + None, + "‵‵", + "‵‵‵", + None, + "!!", + None, + " ̅", + None, + "??", + "?!", + "!?", + None, + "′′′′", + None, + " ", + None, + None, + None, + "0", + "i", + None, + "4", + "5", + "6", + "7", + "8", + "9", + "+", + "−", + "=", + "(", + ")", + "n", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "+", + "−", + "=", + "(", + ")", + None, + "a", + "e", + "o", + "x", + "ə", + "h", + "k", + "l", + "m", + "n", + "p", + "s", + "t", + "w", + "y", + "z", + None, + "rs", + None, + None, + None, + None, + "a/c", + "a/s", + "c", + "°c", + None, + "c/o", + "c/u", + "ɛ", + None, + "°f", + "g", + "h", + "ħ", + "i", + "l", + None, + "n", + "no", + None, + "p", + "q", + "r", + None, + "sm", + "tel", + "tm", + None, + "z", + None, + "ω", + None, + "z", + None, + "k", + "å", + "b", + "c", + None, + "e", + "f", + "ⅎ", + "m", + "o", + "א", + "ב", + "ג", + "ד", + "i", + None, + "fax", + "π", + "γ", + "π", + "∑", + None, + "d", + "e", + "i", + "j", + None, + "1⁄7", + "1⁄9", + "1⁄10", + "1⁄3", + "2⁄3", + "1⁄5", + "2⁄5", + "3⁄5", + "4⁄5", + "1⁄6", + "5⁄6", + "1⁄8", + "3⁄8", + "5⁄8", + "7⁄8", + "1⁄", + "i", + "ii", + "iii", + "iv", + "v", + "vi", + "vii", + "viii", + "ix", + "x", + "xi", + "xii", + "l", + "c", + "d", + "m", + "i", + "ii", + "iii", + "iv", + "v", + "vi", + "vii", + "viii", + "ix", + "x", + "xi", + "xii", + "l", + "c", + "d", + "m", + None, + "ↄ", + None, + "0⁄3", + None, + None, + None, + "∫∫", + "∫∫∫", + None, + "∮∮", + "∮∮∮", + None, + "〈", + "〉", + None, + None, + None, + None, + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "(1)", + "(2)", + "(3)", + "(4)", + "(5)", + "(6)", + "(7)", + "(8)", + "(9)", + "(10)", + "(11)", + "(12)", + "(13)", + "(14)", + "(15)", + "(16)", + "(17)", + "(18)", + "(19)", + "(20)", + None, + "(a)", + "(b)", + "(c)", + "(d)", + "(e)", + "(f)", + "(g)", + "(h)", + "(i)", + "(j)", + "(k)", + "(l)", + "(m)", + "(n)", + "(o)", + "(p)", + "(q)", + "(r)", + "(s)", + "(t)", + "(u)", + "(v)", + "(w)", + "(x)", + "(y)", + "(z)", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "0", + None, + "∫∫∫∫", + None, + "::=", + "==", + "===", + None, + "⫝̸", + None, + None, + None, + "ⰰ", + "ⰱ", + "ⰲ", + "ⰳ", + "ⰴ", + "ⰵ", + "ⰶ", + "ⰷ", + "ⰸ", + "ⰹ", + "ⰺ", + "ⰻ", + "ⰼ", + "ⰽ", + "ⰾ", + "ⰿ", + "ⱀ", + "ⱁ", + "ⱂ", + "ⱃ", + "ⱄ", + "ⱅ", + "ⱆ", + "ⱇ", + "ⱈ", + "ⱉ", + "ⱊ", + "ⱋ", + "ⱌ", + "ⱍ", + "ⱎ", + "ⱏ", + "ⱐ", + "ⱑ", + "ⱒ", + "ⱓ", + "ⱔ", + "ⱕ", + "ⱖ", + "ⱗ", + "ⱘ", + "ⱙ", + "ⱚ", + "ⱛ", + "ⱜ", + "ⱝ", + "ⱞ", + "ⱟ", + None, + "ⱡ", + None, + "ɫ", + "ᵽ", + "ɽ", + None, + "ⱨ", + None, + "ⱪ", + None, + "ⱬ", + None, + "ɑ", + "ɱ", + "ɐ", + "ɒ", + None, + "ⱳ", + None, + "ⱶ", + None, + "j", + "v", + "ȿ", + "ɀ", + "ⲁ", + None, + "ⲃ", + None, + "ⲅ", + None, + "ⲇ", + None, + "ⲉ", + None, + "ⲋ", + None, + "ⲍ", + None, + "ⲏ", + None, + "ⲑ", + None, + "ⲓ", + None, + "ⲕ", + None, + "ⲗ", + None, + "ⲙ", + None, + "ⲛ", + None, + "ⲝ", + None, + "ⲟ", + None, + "ⲡ", + None, + "ⲣ", + None, + "ⲥ", + None, + "ⲧ", + None, + "ⲩ", + None, + "ⲫ", + None, + "ⲭ", + None, + "ⲯ", + None, + "ⲱ", + None, + "ⲳ", + None, + "ⲵ", + None, + "ⲷ", + None, + "ⲹ", + None, + "ⲻ", + None, + "ⲽ", + None, + "ⲿ", + None, + "ⳁ", + None, + "ⳃ", + None, + "ⳅ", + None, + "ⳇ", + None, + "ⳉ", + None, + "ⳋ", + None, + "ⳍ", + None, + "ⳏ", + None, + "ⳑ", + None, + "ⳓ", + None, + "ⳕ", + None, + "ⳗ", + None, + "ⳙ", + None, + "ⳛ", + None, + "ⳝ", + None, + "ⳟ", + None, + "ⳡ", + None, + "ⳣ", + None, + "ⳬ", + None, + "ⳮ", + None, + "ⳳ", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "ⵡ", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "母", + None, + "龟", + None, + "一", + "丨", + "丶", + "丿", + "乙", + "亅", + "二", + "亠", + "人", + "儿", + "入", + "八", + "冂", + "冖", + "冫", + "几", + "凵", + "刀", + "力", + "勹", + "匕", + "匚", + "匸", + "十", + "卜", + "卩", + "厂", + "厶", + "又", + "口", + "囗", + "土", + "士", + "夂", + "夊", + "夕", + "大", + "女", + "子", + "宀", + "寸", + "小", + "尢", + "尸", + "屮", + "山", + "巛", + "工", + "己", + "巾", + "干", + "幺", + "广", + "廴", + "廾", + "弋", + "弓", + "彐", + "彡", + "彳", + "心", + "戈", + "戶", + "手", + "支", + "攴", + "文", + "斗", + "斤", + "方", + "无", + "日", + "曰", + "月", + "木", + "欠", + "止", + "歹", + "殳", + "毋", + "比", + "毛", + "氏", + "气", + "水", + "火", + "爪", + "父", + "爻", + "爿", + "片", + "牙", + "牛", + "犬", + "玄", + "玉", + "瓜", + "瓦", + "甘", + "生", + "用", + "田", + "疋", + "疒", + "癶", + "白", + "皮", + "皿", + "目", + "矛", + "矢", + "石", + "示", + "禸", + "禾", + "穴", + "立", + "竹", + "米", + "糸", + "缶", + "网", + "羊", + "羽", + "老", + "而", + "耒", + "耳", + "聿", + "肉", + "臣", + "自", + "至", + "臼", + "舌", + "舛", + "舟", + "艮", + "色", + "艸", + "虍", + "虫", + "血", + "行", + "衣", + "襾", + "見", + "角", + "言", + "谷", + "豆", + "豕", + "豸", + "貝", + "赤", + "走", + "足", + "身", + "車", + "辛", + "辰", + "辵", + "邑", + "酉", + "釆", + "里", + "金", + "長", + "門", + "阜", + "隶", + "隹", + "雨", + "靑", + "非", + "面", + "革", + "韋", + "韭", + "音", + "頁", + "風", + "飛", + "食", + "首", + "香", + "馬", + "骨", + "高", + "髟", + "鬥", + "鬯", + "鬲", + "鬼", + "魚", + "鳥", + "鹵", + "鹿", + "麥", + "麻", + "黃", + "黍", + "黑", + "黹", + "黽", + "鼎", + "鼓", + "鼠", + "鼻", + "齊", + "齒", + "龍", + "龜", + "龠", + None, + " ", + None, + ".", + None, + "〒", + None, + "十", + "卄", + "卅", + None, + None, + None, + None, + None, + " ゙", + " ゚", + None, + "より", + None, + "コト", + None, + None, + None, + "ᄀ", + "ᄁ", + "ᆪ", + "ᄂ", + "ᆬ", + "ᆭ", + "ᄃ", + "ᄄ", + "ᄅ", + "ᆰ", + "ᆱ", + "ᆲ", + "ᆳ", + "ᆴ", + "ᆵ", + "ᄚ", + "ᄆ", + "ᄇ", + "ᄈ", + "ᄡ", + "ᄉ", + "ᄊ", + "ᄋ", + "ᄌ", + "ᄍ", + "ᄎ", + "ᄏ", + "ᄐ", + "ᄑ", + "ᄒ", + "ᅡ", + "ᅢ", + "ᅣ", + "ᅤ", + "ᅥ", + "ᅦ", + "ᅧ", + "ᅨ", + "ᅩ", + "ᅪ", + "ᅫ", + "ᅬ", + "ᅭ", + "ᅮ", + "ᅯ", + "ᅰ", + "ᅱ", + "ᅲ", + "ᅳ", + "ᅴ", + "ᅵ", + None, + "ᄔ", + "ᄕ", + "ᇇ", + "ᇈ", + "ᇌ", + "ᇎ", + "ᇓ", + "ᇗ", + "ᇙ", + "ᄜ", + "ᇝ", + "ᇟ", + "ᄝ", + "ᄞ", + "ᄠ", + "ᄢ", + "ᄣ", + "ᄧ", + "ᄩ", + "ᄫ", + "ᄬ", + "ᄭ", + "ᄮ", + "ᄯ", + "ᄲ", + "ᄶ", + "ᅀ", + "ᅇ", + "ᅌ", + "ᇱ", + "ᇲ", + "ᅗ", + "ᅘ", + "ᅙ", + "ᆄ", + "ᆅ", + "ᆈ", + "ᆑ", + "ᆒ", + "ᆔ", + "ᆞ", + "ᆡ", + None, + None, + "一", + "二", + "三", + "四", + "上", + "中", + "下", + "甲", + "乙", + "丙", + "丁", + "天", + "地", + "人", + None, + None, + None, + "(ᄀ)", + "(ᄂ)", + "(ᄃ)", + "(ᄅ)", + "(ᄆ)", + "(ᄇ)", + "(ᄉ)", + "(ᄋ)", + "(ᄌ)", + "(ᄎ)", + "(ᄏ)", + "(ᄐ)", + "(ᄑ)", + "(ᄒ)", + "(가)", + "(나)", + "(다)", + "(라)", + "(마)", + "(바)", + "(사)", + "(아)", + "(자)", + "(차)", + "(카)", + "(타)", + "(파)", + "(하)", + "(주)", + "(오전)", + "(오후)", + None, + "(一)", + "(二)", + "(三)", + "(四)", + "(五)", + "(六)", + "(七)", + "(八)", + "(九)", + "(十)", + "(月)", + "(火)", + "(水)", + "(木)", + "(金)", + "(土)", + "(日)", + "(株)", + "(有)", + "(社)", + "(名)", + "(特)", + "(財)", + "(祝)", + "(労)", + "(代)", + "(呼)", + "(学)", + "(監)", + "(企)", + "(資)", + "(協)", + "(祭)", + "(休)", + "(自)", + "(至)", + "問", + "幼", + "文", + "箏", + None, + "pte", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "ᄀ", + "ᄂ", + "ᄃ", + "ᄅ", + "ᄆ", + "ᄇ", + "ᄉ", + "ᄋ", + "ᄌ", + "ᄎ", + "ᄏ", + "ᄐ", + "ᄑ", + "ᄒ", + "가", + "나", + "다", + "라", + "마", + "바", + "사", + "아", + "자", + "차", + "카", + "타", + "파", + "하", + "참고", + "주의", + "우", + None, + "一", + "二", + "三", + "四", + "五", + "六", + "七", + "八", + "九", + "十", + "月", + "火", + "水", + "木", + "金", + "土", + "日", + "株", + "有", + "社", + "名", + "特", + "財", + "祝", + "労", + "秘", + "男", + "女", + "適", + "優", + "印", + "注", + "項", + "休", + "写", + "正", + "上", + "中", + "下", + "左", + "右", + "医", + "宗", + "学", + "監", + "企", + "資", + "協", + "夜", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "43", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "1月", + "2月", + "3月", + "4月", + "5月", + "6月", + "7月", + "8月", + "9月", + "10月", + "11月", + "12月", + "hg", + "erg", + "ev", + "ltd", + "ア", + "イ", + "ウ", + "エ", + "オ", + "カ", + "キ", + "ク", + "ケ", + "コ", + "サ", + "シ", + "ス", + "セ", + "ソ", + "タ", + "チ", + "ツ", + "テ", + "ト", + "ナ", + "ニ", + "ヌ", + "ネ", + "ノ", + "ハ", + "ヒ", + "フ", + "ヘ", + "ホ", + "マ", + "ミ", + "ム", + "メ", + "モ", + "ヤ", + "ユ", + "ヨ", + "ラ", + "リ", + "ル", + "レ", + "ロ", + "ワ", + "ヰ", + "ヱ", + "ヲ", + "令和", + "アパート", + "アルファ", + "アンペア", + "アール", + "イニング", + "インチ", + "ウォン", + "エスクード", + "エーカー", + "オンス", + "オーム", + "カイリ", + "カラット", + "カロリー", + "ガロン", + "ガンマ", + "ギガ", + "ギニー", + "キュリー", + "ギルダー", + "キロ", + "キログラム", + "キロメートル", + "キロワット", + "グラム", + "グラムトン", + "クルゼイロ", + "クローネ", + "ケース", + "コルナ", + "コーポ", + "サイクル", + "サンチーム", + "シリング", + "センチ", + "セント", + "ダース", + "デシ", + "ドル", + "トン", + "ナノ", + "ノット", + "ハイツ", + "パーセント", + "パーツ", + "バーレル", + "ピアストル", + "ピクル", + "ピコ", + "ビル", + "ファラッド", + "フィート", + "ブッシェル", + "フラン", + "ヘクタール", + "ペソ", + "ペニヒ", + "ヘルツ", + "ペンス", + "ページ", + "ベータ", + "ポイント", + "ボルト", + "ホン", + "ポンド", + "ホール", + "ホーン", + "マイクロ", + "マイル", + "マッハ", + "マルク", + "マンション", + "ミクロン", + "ミリ", + "ミリバール", + "メガ", + "メガトン", + "メートル", + "ヤード", + "ヤール", + "ユアン", + "リットル", + "リラ", + "ルピー", + "ルーブル", + "レム", + "レントゲン", + "ワット", + "0点", + "1点", + "2点", + "3点", + "4点", + "5点", + "6点", + "7点", + "8点", + "9点", + "10点", + "11点", + "12点", + "13点", + "14点", + "15点", + "16点", + "17点", + "18点", + "19点", + "20点", + "21点", + "22点", + "23点", + "24点", + "hpa", + "da", + "au", + "bar", + "ov", + "pc", + "dm", + "dm2", + "dm3", + "iu", + "平成", + "昭和", + "大正", + "明治", + "株式会社", + "pa", + "na", + "μa", + "ma", + "ka", + "kb", + "mb", + "gb", + "cal", + "kcal", + "pf", + "nf", + "μf", + "μg", + "mg", + "kg", + "hz", + "khz", + "mhz", + "ghz", + "thz", + "μl", + "ml", + "dl", + "kl", + "fm", + "nm", + "μm", + "mm", + "cm", + "km", + "mm2", + "cm2", + "m2", + "km2", + "mm3", + "cm3", + "m3", + "km3", + "m∕s", + "m∕s2", + "pa", + "kpa", + "mpa", + "gpa", + "rad", + "rad∕s", + "rad∕s2", + "ps", + "ns", + "μs", + "ms", + "pv", + "nv", + "μv", + "mv", + "kv", + "mv", + "pw", + "nw", + "μw", + "mw", + "kw", + "mw", + "kω", + "mω", + None, + "bq", + "cc", + "cd", + "c∕kg", + None, + "db", + "gy", + "ha", + "hp", + "in", + "kk", + "km", + "kt", + "lm", + "ln", + "log", + "lx", + "mb", + "mil", + "mol", + "ph", + None, + "ppm", + "pr", + "sr", + "sv", + "wb", + "v∕m", + "a∕m", + "1日", + "2日", + "3日", + "4日", + "5日", + "6日", + "7日", + "8日", + "9日", + "10日", + "11日", + "12日", + "13日", + "14日", + "15日", + "16日", + "17日", + "18日", + "19日", + "20日", + "21日", + "22日", + "23日", + "24日", + "25日", + "26日", + "27日", + "28日", + "29日", + "30日", + "31日", + "gal", + None, + None, + None, + None, + None, + None, + "ꙁ", + None, + "ꙃ", + None, + "ꙅ", + None, + "ꙇ", + None, + "ꙉ", + None, + "ꙋ", + None, + "ꙍ", + None, + "ꙏ", + None, + "ꙑ", + None, + "ꙓ", + None, + "ꙕ", + None, + "ꙗ", + None, + "ꙙ", + None, + "ꙛ", + None, + "ꙝ", + None, + "ꙟ", + None, + "ꙡ", + None, + "ꙣ", + None, + "ꙥ", + None, + "ꙧ", + None, + "ꙩ", + None, + "ꙫ", + None, + "ꙭ", + None, + "ꚁ", + None, + "ꚃ", + None, + "ꚅ", + None, + "ꚇ", + None, + "ꚉ", + None, + "ꚋ", + None, + "ꚍ", + None, + "ꚏ", + None, + "ꚑ", + None, + "ꚓ", + None, + "ꚕ", + None, + "ꚗ", + None, + "ꚙ", + None, + "ꚛ", + None, + "ъ", + "ь", + None, + None, + None, + "ꜣ", + None, + "ꜥ", + None, + "ꜧ", + None, + "ꜩ", + None, + "ꜫ", + None, + "ꜭ", + None, + "ꜯ", + None, + "ꜳ", + None, + "ꜵ", + None, + "ꜷ", + None, + "ꜹ", + None, + "ꜻ", + None, + "ꜽ", + None, + "ꜿ", + None, + "ꝁ", + None, + "ꝃ", + None, + "ꝅ", + None, + "ꝇ", + None, + "ꝉ", + None, + "ꝋ", + None, + "ꝍ", + None, + "ꝏ", + None, + "ꝑ", + None, + "ꝓ", + None, + "ꝕ", + None, + "ꝗ", + None, + "ꝙ", + None, + "ꝛ", + None, + "ꝝ", + None, + "ꝟ", + None, + "ꝡ", + None, + "ꝣ", + None, + "ꝥ", + None, + "ꝧ", + None, + "ꝩ", + None, + "ꝫ", + None, + "ꝭ", + None, + "ꝯ", + None, + "ꝯ", + None, + "ꝺ", + None, + "ꝼ", + None, + "ᵹ", + "ꝿ", + None, + "ꞁ", + None, + "ꞃ", + None, + "ꞅ", + None, + "ꞇ", + None, + "ꞌ", + None, + "ɥ", + None, + "ꞑ", + None, + "ꞓ", + None, + "ꞗ", + None, + "ꞙ", + None, + "ꞛ", + None, + "ꞝ", + None, + "ꞟ", + None, + "ꞡ", + None, + "ꞣ", + None, + "ꞥ", + None, + "ꞧ", + None, + "ꞩ", + None, + "ɦ", + "ɜ", + "ɡ", + "ɬ", + "ɪ", + None, + "ʞ", + "ʇ", + "ʝ", + "ꭓ", + "ꞵ", + None, + "ꞷ", + None, + "ꞹ", + None, + "ꞻ", + None, + "ꞽ", + None, + "ꞿ", + None, + "ꟁ", + None, + "ꟃ", + None, + "ꞔ", + "ʂ", + "ᶎ", + "ꟈ", + None, + "ꟊ", + None, + "ɤ", + "ꟍ", + None, + "꟏", + None, + "ꟑ", + None, + "ꟓ", + None, + "ꟕ", + None, + "ꟗ", + None, + "ꟙ", + None, + "ꟛ", + None, + "ƛ", + "ɷ", + None, + "ɼ", + None, + "s", + "c", + "f", + "q", + "ꟶ", + None, + "ħ", + "œ", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "ꜧ", + "ꬷ", + "ɫ", + "ꭒ", + None, + "ʍ", + None, + "ꭋ", + "ꭌ", + None, + "Ꭰ", + "Ꭱ", + "Ꭲ", + "Ꭳ", + "Ꭴ", + "Ꭵ", + "Ꭶ", + "Ꭷ", + "Ꭸ", + "Ꭹ", + "Ꭺ", + "Ꭻ", + "Ꭼ", + "Ꭽ", + "Ꭾ", + "Ꭿ", + "Ꮀ", + "Ꮁ", + "Ꮂ", + "Ꮃ", + "Ꮄ", + "Ꮅ", + "Ꮆ", + "Ꮇ", + "Ꮈ", + "Ꮉ", + "Ꮊ", + "Ꮋ", + "Ꮌ", + "Ꮍ", + "Ꮎ", + "Ꮏ", + "Ꮐ", + "Ꮑ", + "Ꮒ", + "Ꮓ", + "Ꮔ", + "Ꮕ", + "Ꮖ", + "Ꮗ", + "Ꮘ", + "Ꮙ", + "Ꮚ", + "Ꮛ", + "Ꮜ", + "Ꮝ", + "Ꮞ", + "Ꮟ", + "Ꮠ", + "Ꮡ", + "Ꮢ", + "Ꮣ", + "Ꮤ", + "Ꮥ", + "Ꮦ", + "Ꮧ", + "Ꮨ", + "Ꮩ", + "Ꮪ", + "Ꮫ", + "Ꮬ", + "Ꮭ", + "Ꮮ", + "Ꮯ", + "Ꮰ", + "Ꮱ", + "Ꮲ", + "Ꮳ", + "Ꮴ", + "Ꮵ", + "Ꮶ", + "Ꮷ", + "Ꮸ", + "Ꮹ", + "Ꮺ", + "Ꮻ", + "Ꮼ", + "Ꮽ", + "Ꮾ", + "Ꮿ", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "豈", + "更", + "車", + "賈", + "滑", + "串", + "句", + "龜", + "契", + "金", + "喇", + "奈", + "懶", + "癩", + "羅", + "蘿", + "螺", + "裸", + "邏", + "樂", + "洛", + "烙", + "珞", + "落", + "酪", + "駱", + "亂", + "卵", + "欄", + "爛", + "蘭", + "鸞", + "嵐", + "濫", + "藍", + "襤", + "拉", + "臘", + "蠟", + "廊", + "朗", + "浪", + "狼", + "郎", + "來", + "冷", + "勞", + "擄", + "櫓", + "爐", + "盧", + "老", + "蘆", + "虜", + "路", + "露", + "魯", + "鷺", + "碌", + "祿", + "綠", + "菉", + "錄", + "鹿", + "論", + "壟", + "弄", + "籠", + "聾", + "牢", + "磊", + "賂", + "雷", + "壘", + "屢", + "樓", + "淚", + "漏", + "累", + "縷", + "陋", + "勒", + "肋", + "凜", + "凌", + "稜", + "綾", + "菱", + "陵", + "讀", + "拏", + "樂", + "諾", + "丹", + "寧", + "怒", + "率", + "異", + "北", + "磻", + "便", + "復", + "不", + "泌", + "數", + "索", + "參", + "塞", + "省", + "葉", + "說", + "殺", + "辰", + "沈", + "拾", + "若", + "掠", + "略", + "亮", + "兩", + "凉", + "梁", + "糧", + "良", + "諒", + "量", + "勵", + "呂", + "女", + "廬", + "旅", + "濾", + "礪", + "閭", + "驪", + "麗", + "黎", + "力", + "曆", + "歷", + "轢", + "年", + "憐", + "戀", + "撚", + "漣", + "煉", + "璉", + "秊", + "練", + "聯", + "輦", + "蓮", + "連", + "鍊", + "列", + "劣", + "咽", + "烈", + "裂", + "說", + "廉", + "念", + "捻", + "殮", + "簾", + "獵", + "令", + "囹", + "寧", + "嶺", + "怜", + "玲", + "瑩", + "羚", + "聆", + "鈴", + "零", + "靈", + "領", + "例", + "禮", + "醴", + "隸", + "惡", + "了", + "僚", + "寮", + "尿", + "料", + "樂", + "燎", + "療", + "蓼", + "遼", + "龍", + "暈", + "阮", + "劉", + "杻", + "柳", + "流", + "溜", + "琉", + "留", + "硫", + "紐", + "類", + "六", + "戮", + "陸", + "倫", + "崙", + "淪", + "輪", + "律", + "慄", + "栗", + "率", + "隆", + "利", + "吏", + "履", + "易", + "李", + "梨", + "泥", + "理", + "痢", + "罹", + "裏", + "裡", + "里", + "離", + "匿", + "溺", + "吝", + "燐", + "璘", + "藺", + "隣", + "鱗", + "麟", + "林", + "淋", + "臨", + "立", + "笠", + "粒", + "狀", + "炙", + "識", + "什", + "茶", + "刺", + "切", + "度", + "拓", + "糖", + "宅", + "洞", + "暴", + "輻", + "行", + "降", + "見", + "廓", + "兀", + "嗀", + None, + "塚", + None, + "晴", + None, + "凞", + "猪", + "益", + "礼", + "神", + "祥", + "福", + "靖", + "精", + "羽", + None, + "蘒", + None, + "諸", + None, + "逸", + "都", + None, + "飯", + "飼", + "館", + "鶴", + "郞", + "隷", + "侮", + "僧", + "免", + "勉", + "勤", + "卑", + "喝", + "嘆", + "器", + "塀", + "墨", + "層", + "屮", + "悔", + "慨", + "憎", + "懲", + "敏", + "既", + "暑", + "梅", + "海", + "渚", + "漢", + "煮", + "爫", + "琢", + "碑", + "社", + "祉", + "祈", + "祐", + "祖", + "祝", + "禍", + "禎", + "穀", + "突", + "節", + "練", + "縉", + "繁", + "署", + "者", + "臭", + "艹", + "著", + "褐", + "視", + "謁", + "謹", + "賓", + "贈", + "辶", + "逸", + "難", + "響", + "頻", + "恵", + "𤋮", + "舘", + None, + "並", + "况", + "全", + "侀", + "充", + "冀", + "勇", + "勺", + "喝", + "啕", + "喙", + "嗢", + "塚", + "墳", + "奄", + "奔", + "婢", + "嬨", + "廒", + "廙", + "彩", + "徭", + "惘", + "慎", + "愈", + "憎", + "慠", + "懲", + "戴", + "揄", + "搜", + "摒", + "敖", + "晴", + "朗", + "望", + "杖", + "歹", + "殺", + "流", + "滛", + "滋", + "漢", + "瀞", + "煮", + "瞧", + "爵", + "犯", + "猪", + "瑱", + "甆", + "画", + "瘝", + "瘟", + "益", + "盛", + "直", + "睊", + "着", + "磌", + "窱", + "節", + "类", + "絛", + "練", + "缾", + "者", + "荒", + "華", + "蝹", + "襁", + "覆", + "視", + "調", + "諸", + "請", + "謁", + "諾", + "諭", + "謹", + "變", + "贈", + "輸", + "遲", + "醙", + "鉶", + "陼", + "難", + "靖", + "韛", + "響", + "頋", + "頻", + "鬒", + "龜", + "𢡊", + "𢡄", + "𣏕", + "㮝", + "䀘", + "䀹", + "𥉉", + "𥳐", + "𧻓", + "齃", + "龎", + None, + "ff", + "fi", + "fl", + "ffi", + "ffl", + "st", + None, + "մն", + "մե", + "մի", + "վն", + "մխ", + None, + "יִ", + None, + "ײַ", + "ע", + "א", + "ד", + "ה", + "כ", + "ל", + "ם", + "ר", + "ת", + "+", + "שׁ", + "שׂ", + "שּׁ", + "שּׂ", + "אַ", + "אָ", + "אּ", + "בּ", + "גּ", + "דּ", + "הּ", + "וּ", + "זּ", + None, + "טּ", + "יּ", + "ךּ", + "כּ", + "לּ", + None, + "מּ", + None, + "נּ", + "סּ", + None, + "ףּ", + "פּ", + None, + "צּ", + "קּ", + "רּ", + "שּ", + "תּ", + "וֹ", + "בֿ", + "כֿ", + "פֿ", + "אל", + "ٱ", + "ٻ", + "پ", + "ڀ", + "ٺ", + "ٿ", + "ٹ", + "ڤ", + "ڦ", + "ڄ", + "ڃ", + "چ", + "ڇ", + "ڍ", + "ڌ", + "ڎ", + "ڈ", + "ژ", + "ڑ", + "ک", + "گ", + "ڳ", + "ڱ", + "ں", + "ڻ", + "ۀ", + "ہ", + "ھ", + "ے", + "ۓ", + None, + "ڭ", + "ۇ", + "ۆ", + "ۈ", + "ۇٴ", + "ۋ", + "ۅ", + "ۉ", + "ې", + "ى", + "ئا", + "ئە", + "ئو", + "ئۇ", + "ئۆ", + "ئۈ", + "ئې", + "ئى", + "ی", + "ئج", + "ئح", + "ئم", + "ئى", + "ئي", + "بج", + "بح", + "بخ", + "بم", + "بى", + "بي", + "تج", + "تح", + "تخ", + "تم", + "تى", + "تي", + "ثج", + "ثم", + "ثى", + "ثي", + "جح", + "جم", + "حج", + "حم", + "خج", + "خح", + "خم", + "سج", + "سح", + "سخ", + "سم", + "صح", + "صم", + "ضج", + "ضح", + "ضخ", + "ضم", + "طح", + "طم", + "ظم", + "عج", + "عم", + "غج", + "غم", + "فج", + "فح", + "فخ", + "فم", + "فى", + "في", + "قح", + "قم", + "قى", + "قي", + "كا", + "كج", + "كح", + "كخ", + "كل", + "كم", + "كى", + "كي", + "لج", + "لح", + "لخ", + "لم", + "لى", + "لي", + "مج", + "مح", + "مخ", + "مم", + "مى", + "مي", + "نج", + "نح", + "نخ", + "نم", + "نى", + "ني", + "هج", + "هم", + "هى", + "هي", + "يج", + "يح", + "يخ", + "يم", + "يى", + "يي", + "ذٰ", + "رٰ", + "ىٰ", + " ٌّ", + " ٍّ", + " َّ", + " ُّ", + " ِّ", + " ّٰ", + "ئر", + "ئز", + "ئم", + "ئن", + "ئى", + "ئي", + "بر", + "بز", + "بم", + "بن", + "بى", + "بي", + "تر", + "تز", + "تم", + "تن", + "تى", + "تي", + "ثر", + "ثز", + "ثم", + "ثن", + "ثى", + "ثي", + "فى", + "في", + "قى", + "قي", + "كا", + "كل", + "كم", + "كى", + "كي", + "لم", + "لى", + "لي", + "ما", + "مم", + "نر", + "نز", + "نم", + "نن", + "نى", + "ني", + "ىٰ", + "ير", + "يز", + "يم", + "ين", + "يى", + "يي", + "ئج", + "ئح", + "ئخ", + "ئم", + "ئه", + "بج", + "بح", + "بخ", + "بم", + "به", + "تج", + "تح", + "تخ", + "تم", + "ته", + "ثم", + "جح", + "جم", + "حج", + "حم", + "خج", + "خم", + "سج", + "سح", + "سخ", + "سم", + "صح", + "صخ", + "صم", + "ضج", + "ضح", + "ضخ", + "ضم", + "طح", + "ظم", + "عج", + "عم", + "غج", + "غم", + "فج", + "فح", + "فخ", + "فم", + "قح", + "قم", + "كج", + "كح", + "كخ", + "كل", + "كم", + "لج", + "لح", + "لخ", + "لم", + "له", + "مج", + "مح", + "مخ", + "مم", + "نج", + "نح", + "نخ", + "نم", + "نه", + "هج", + "هم", + "هٰ", + "يج", + "يح", + "يخ", + "يم", + "يه", + "ئم", + "ئه", + "بم", + "به", + "تم", + "ته", + "ثم", + "ثه", + "سم", + "سه", + "شم", + "شه", + "كل", + "كم", + "لم", + "نم", + "نه", + "يم", + "يه", + "ـَّ", + "ـُّ", + "ـِّ", + "طى", + "طي", + "عى", + "عي", + "غى", + "غي", + "سى", + "سي", + "شى", + "شي", + "حى", + "حي", + "جى", + "جي", + "خى", + "خي", + "صى", + "صي", + "ضى", + "ضي", + "شج", + "شح", + "شخ", + "شم", + "شر", + "سر", + "صر", + "ضر", + "طى", + "طي", + "عى", + "عي", + "غى", + "غي", + "سى", + "سي", + "شى", + "شي", + "حى", + "حي", + "جى", + "جي", + "خى", + "خي", + "صى", + "صي", + "ضى", + "ضي", + "شج", + "شح", + "شخ", + "شم", + "شر", + "سر", + "صر", + "ضر", + "شج", + "شح", + "شخ", + "شم", + "سه", + "شه", + "طم", + "سج", + "سح", + "سخ", + "شج", + "شح", + "شخ", + "طم", + "ظم", + "اً", + None, + "تجم", + "تحج", + "تحم", + "تخم", + "تمج", + "تمح", + "تمخ", + "جمح", + "حمي", + "حمى", + "سحج", + "سجح", + "سجى", + "سمح", + "سمج", + "سمم", + "صحح", + "صمم", + "شحم", + "شجي", + "شمخ", + "شمم", + "ضحى", + "ضخم", + "طمح", + "طمم", + "طمي", + "عجم", + "عمم", + "عمى", + "غمم", + "غمي", + "غمى", + "فخم", + "قمح", + "قمم", + "لحم", + "لحي", + "لحى", + "لجج", + "لخم", + "لمح", + "محج", + "محم", + "محي", + "مجح", + "مجم", + "مخج", + "مخم", + None, + "مجخ", + "همج", + "همم", + "نحم", + "نحى", + "نجم", + "نجى", + "نمي", + "نمى", + "يمم", + "بخي", + "تجي", + "تجى", + "تخي", + "تخى", + "تمي", + "تمى", + "جمي", + "جحى", + "جمى", + "سخى", + "صحي", + "شحي", + "ضحي", + "لجي", + "لمي", + "يحي", + "يجي", + "يمي", + "ممي", + "قمي", + "نحي", + "قمح", + "لحم", + "عمي", + "كمي", + "نجح", + "مخي", + "لجم", + "كمم", + "لجم", + "نجح", + "جحي", + "حجي", + "مجي", + "فمي", + "بحي", + "كمم", + "عجم", + "صمم", + "سخي", + "نجي", + None, + None, + "صلے", + "قلے", + "الله", + "اكبر", + "محمد", + "صلعم", + "رسول", + "عليه", + "وسلم", + "صلى", + "صلى الله عليه وسلم", + "جل جلاله", + "ریال", + None, + None, + ",", + "、", + None, + ":", + ";", + "!", + "?", + "〖", + "〗", + None, + None, + None, + "—", + "–", + "_", + "(", + ")", + "{", + "}", + "〔", + "〕", + "【", + "】", + "《", + "》", + "〈", + "〉", + "「", + "」", + "『", + "』", + None, + "[", + "]", + " ̅", + "_", + ",", + "、", + None, + ";", + ":", + "?", + "!", + "—", + "(", + ")", + "{", + "}", + "〔", + "〕", + "#", + "&", + "*", + "+", + "-", + "<", + ">", + "=", + None, + "\\", + "$", + "%", + "@", + None, + " ً", + "ـً", + " ٌ", + None, + " ٍ", + None, + " َ", + "ـَ", + " ُ", + "ـُ", + " ِ", + "ـِ", + " ّ", + "ـّ", + " ْ", + "ـْ", + "ء", + "آ", + "أ", + "ؤ", + "إ", + "ئ", + "ا", + "ب", + "ة", + "ت", + "ث", + "ج", + "ح", + "خ", + "د", + "ذ", + "ر", + "ز", + "س", + "ش", + "ص", + "ض", + "ط", + "ظ", + "ع", + "غ", + "ف", + "ق", + "ك", + "ل", + "م", + "ن", + "ه", + "و", + "ى", + "ي", + "لآ", + "لأ", + "لإ", + "لا", + None, + None, + None, + "!", + '"', + "#", + "$", + "%", + "&", + "'", + "(", + ")", + "*", + "+", + ",", + "-", + ".", + "/", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + ":", + ";", + "<", + "=", + ">", + "?", + "@", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "[", + "\\", + "]", + "^", + "_", + "`", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "{", + "|", + "}", + "~", + "⦅", + "⦆", + ".", + "「", + "」", + "、", + "・", + "ヲ", + "ァ", + "ィ", + "ゥ", + "ェ", + "ォ", + "ャ", + "ュ", + "ョ", + "ッ", + "ー", + "ア", + "イ", + "ウ", + "エ", + "オ", + "カ", + "キ", + "ク", + "ケ", + "コ", + "サ", + "シ", + "ス", + "セ", + "ソ", + "タ", + "チ", + "ツ", + "テ", + "ト", + "ナ", + "ニ", + "ヌ", + "ネ", + "ノ", + "ハ", + "ヒ", + "フ", + "ヘ", + "ホ", + "マ", + "ミ", + "ム", + "メ", + "モ", + "ヤ", + "ユ", + "ヨ", + "ラ", + "リ", + "ル", + "レ", + "ロ", + "ワ", + "ン", + "゙", + "゚", + None, + "ᄀ", + "ᄁ", + "ᆪ", + "ᄂ", + "ᆬ", + "ᆭ", + "ᄃ", + "ᄄ", + "ᄅ", + "ᆰ", + "ᆱ", + "ᆲ", + "ᆳ", + "ᆴ", + "ᆵ", + "ᄚ", + "ᄆ", + "ᄇ", + "ᄈ", + "ᄡ", + "ᄉ", + "ᄊ", + "ᄋ", + "ᄌ", + "ᄍ", + "ᄎ", + "ᄏ", + "ᄐ", + "ᄑ", + "ᄒ", + None, + "ᅡ", + "ᅢ", + "ᅣ", + "ᅤ", + "ᅥ", + "ᅦ", + None, + "ᅧ", + "ᅨ", + "ᅩ", + "ᅪ", + "ᅫ", + "ᅬ", + None, + "ᅭ", + "ᅮ", + "ᅯ", + "ᅰ", + "ᅱ", + "ᅲ", + None, + "ᅳ", + "ᅴ", + "ᅵ", + None, + "¢", + "£", + "¬", + " ̄", + "¦", + "¥", + "₩", + None, + "│", + "←", + "↑", + "→", + "↓", + "■", + "○", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "𐐨", + "𐐩", + "𐐪", + "𐐫", + "𐐬", + "𐐭", + "𐐮", + "𐐯", + "𐐰", + "𐐱", + "𐐲", + "𐐳", + "𐐴", + "𐐵", + "𐐶", + "𐐷", + "𐐸", + "𐐹", + "𐐺", + "𐐻", + "𐐼", + "𐐽", + "𐐾", + "𐐿", + "𐑀", + "𐑁", + "𐑂", + "𐑃", + "𐑄", + "𐑅", + "𐑆", + "𐑇", + "𐑈", + "𐑉", + "𐑊", + "𐑋", + "𐑌", + "𐑍", + "𐑎", + "𐑏", + None, + None, + None, + None, + "𐓘", + "𐓙", + "𐓚", + "𐓛", + "𐓜", + "𐓝", + "𐓞", + "𐓟", + "𐓠", + "𐓡", + "𐓢", + "𐓣", + "𐓤", + "𐓥", + "𐓦", + "𐓧", + "𐓨", + "𐓩", + "𐓪", + "𐓫", + "𐓬", + "𐓭", + "𐓮", + "𐓯", + "𐓰", + "𐓱", + "𐓲", + "𐓳", + "𐓴", + "𐓵", + "𐓶", + "𐓷", + "𐓸", + "𐓹", + "𐓺", + "𐓻", + None, + None, + None, + None, + None, + None, + None, + None, + "𐖗", + "𐖘", + "𐖙", + "𐖚", + "𐖛", + "𐖜", + "𐖝", + "𐖞", + "𐖟", + "𐖠", + "𐖡", + None, + "𐖣", + "𐖤", + "𐖥", + "𐖦", + "𐖧", + "𐖨", + "𐖩", + "𐖪", + "𐖫", + "𐖬", + "𐖭", + "𐖮", + "𐖯", + "𐖰", + "𐖱", + None, + "𐖳", + "𐖴", + "𐖵", + "𐖶", + "𐖷", + "𐖸", + "𐖹", + None, + "𐖻", + "𐖼", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "ː", + "ˑ", + "æ", + "ʙ", + "ɓ", + None, + "ʣ", + "ꭦ", + "ʥ", + "ʤ", + "ɖ", + "ɗ", + "ᶑ", + "ɘ", + "ɞ", + "ʩ", + "ɤ", + "ɢ", + "ɠ", + "ʛ", + "ħ", + "ʜ", + "ɧ", + "ʄ", + "ʪ", + "ʫ", + "ɬ", + "𝼄", + "ꞎ", + "ɮ", + "𝼅", + "ʎ", + "𝼆", + "ø", + "ɶ", + "ɷ", + "q", + "ɺ", + "𝼈", + "ɽ", + "ɾ", + "ʀ", + "ʨ", + "ʦ", + "ꭧ", + "ʧ", + "ʈ", + "ⱱ", + None, + "ʏ", + "ʡ", + "ʢ", + "ʘ", + "ǀ", + "ǁ", + "ǂ", + "𝼊", + "𝼞", + "ʇ", + "ʖ", + "ʗ", + "ʞ", + "𝼋", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "𐳀", + "𐳁", + "𐳂", + "𐳃", + "𐳄", + "𐳅", + "𐳆", + "𐳇", + "𐳈", + "𐳉", + "𐳊", + "𐳋", + "𐳌", + "𐳍", + "𐳎", + "𐳏", + "𐳐", + "𐳑", + "𐳒", + "𐳓", + "𐳔", + "𐳕", + "𐳖", + "𐳗", + "𐳘", + "𐳙", + "𐳚", + "𐳛", + "𐳜", + "𐳝", + "𐳞", + "𐳟", + "𐳠", + "𐳡", + "𐳢", + "𐳣", + "𐳤", + "𐳥", + "𐳦", + "𐳧", + "𐳨", + "𐳩", + "𐳪", + "𐳫", + "𐳬", + "𐳭", + "𐳮", + "𐳯", + "𐳰", + "𐳱", + "𐳲", + None, + None, + None, + None, + None, + None, + None, + None, + "𐵰", + "𐵱", + "𐵲", + "𐵳", + "𐵴", + "𐵵", + "𐵶", + "𐵷", + "𐵸", + "𐵹", + "𐵺", + "𐵻", + "𐵼", + "𐵽", + "𐵾", + "𐵿", + "𐶀", + "𐶁", + "𐶂", + "𐶃", + "𐶄", + "𐶅", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "𑣀", + "𑣁", + "𑣂", + "𑣃", + "𑣄", + "𑣅", + "𑣆", + "𑣇", + "𑣈", + "𑣉", + "𑣊", + "𑣋", + "𑣌", + "𑣍", + "𑣎", + "𑣏", + "𑣐", + "𑣑", + "𑣒", + "𑣓", + "𑣔", + "𑣕", + "𑣖", + "𑣗", + "𑣘", + "𑣙", + "𑣚", + "𑣛", + "𑣜", + "𑣝", + "𑣞", + "𑣟", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "𖹠", + "𖹡", + "𖹢", + "𖹣", + "𖹤", + "𖹥", + "𖹦", + "𖹧", + "𖹨", + "𖹩", + "𖹪", + "𖹫", + "𖹬", + "𖹭", + "𖹮", + "𖹯", + "𖹰", + "𖹱", + "𖹲", + "𖹳", + "𖹴", + "𖹵", + "𖹶", + "𖹷", + "𖹸", + "𖹹", + "𖹺", + "𖹻", + "𖹼", + "𖹽", + "𖹾", + "𖹿", + None, + None, + "𖺻", + "𖺼", + "𖺽", + "𖺾", + "𖺿", + "𖻀", + "𖻁", + "𖻂", + "𖻃", + "𖻄", + "𖻅", + "𖻆", + "𖻇", + "𖻈", + "𖻉", + "𖻊", + "𖻋", + "𖻌", + "𖻍", + "𖻎", + "𖻏", + "𖻐", + "𖻑", + "𖻒", + "𖻓", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "こと", + "トキ", + "トテ", + "ヨリ", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "𝅗𝅥", + "𝅘𝅥", + "𝅘𝅥𝅮", + "𝅘𝅥𝅯", + "𝅘𝅥𝅰", + "𝅘𝅥𝅱", + "𝅘𝅥𝅲", + None, + None, + None, + "𝆹𝅥", + "𝆺𝅥", + "𝆹𝅥𝅮", + "𝆺𝅥𝅮", + "𝆹𝅥𝅯", + "𝆺𝅥𝅯", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + None, + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + None, + "c", + "d", + None, + "g", + None, + "j", + "k", + None, + "n", + "o", + "p", + "q", + None, + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + None, + "f", + None, + "h", + "i", + "j", + "k", + "l", + "m", + "n", + None, + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + None, + "d", + "e", + "f", + "g", + None, + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + None, + "s", + "t", + "u", + "v", + "w", + "x", + "y", + None, + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + None, + "d", + "e", + "f", + "g", + None, + "i", + "j", + "k", + "l", + "m", + None, + "o", + None, + "s", + "t", + "u", + "v", + "w", + "x", + "y", + None, + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "ı", + "ȷ", + "ss", + None, + "α", + "β", + "γ", + "δ", + "ε", + "ζ", + "η", + "θ", + "ι", + "κ", + "λ", + "μ", + "ν", + "ξ", + "ο", + "π", + "ρ", + "θ", + "σ", + "τ", + "υ", + "φ", + "χ", + "ψ", + "ω", + "∇", + "α", + "β", + "γ", + "δ", + "ε", + "ζ", + "η", + "θ", + "ι", + "κ", + "λ", + "μ", + "ν", + "ξ", + "ο", + "π", + "ρ", + "σ", + "τ", + "υ", + "φ", + "χ", + "ψ", + "ω", + "∂", + "ε", + "θ", + "κ", + "φ", + "ρ", + "π", + "α", + "β", + "γ", + "δ", + "ε", + "ζ", + "η", + "θ", + "ι", + "κ", + "λ", + "μ", + "ν", + "ξ", + "ο", + "π", + "ρ", + "θ", + "σ", + "τ", + "υ", + "φ", + "χ", + "ψ", + "ω", + "∇", + "α", + "β", + "γ", + "δ", + "ε", + "ζ", + "η", + "θ", + "ι", + "κ", + "λ", + "μ", + "ν", + "ξ", + "ο", + "π", + "ρ", + "σ", + "τ", + "υ", + "φ", + "χ", + "ψ", + "ω", + "∂", + "ε", + "θ", + "κ", + "φ", + "ρ", + "π", + "α", + "β", + "γ", + "δ", + "ε", + "ζ", + "η", + "θ", + "ι", + "κ", + "λ", + "μ", + "ν", + "ξ", + "ο", + "π", + "ρ", + "θ", + "σ", + "τ", + "υ", + "φ", + "χ", + "ψ", + "ω", + "∇", + "α", + "β", + "γ", + "δ", + "ε", + "ζ", + "η", + "θ", + "ι", + "κ", + "λ", + "μ", + "ν", + "ξ", + "ο", + "π", + "ρ", + "σ", + "τ", + "υ", + "φ", + "χ", + "ψ", + "ω", + "∂", + "ε", + "θ", + "κ", + "φ", + "ρ", + "π", + "α", + "β", + "γ", + "δ", + "ε", + "ζ", + "η", + "θ", + "ι", + "κ", + "λ", + "μ", + "ν", + "ξ", + "ο", + "π", + "ρ", + "θ", + "σ", + "τ", + "υ", + "φ", + "χ", + "ψ", + "ω", + "∇", + "α", + "β", + "γ", + "δ", + "ε", + "ζ", + "η", + "θ", + "ι", + "κ", + "λ", + "μ", + "ν", + "ξ", + "ο", + "π", + "ρ", + "σ", + "τ", + "υ", + "φ", + "χ", + "ψ", + "ω", + "∂", + "ε", + "θ", + "κ", + "φ", + "ρ", + "π", + "α", + "β", + "γ", + "δ", + "ε", + "ζ", + "η", + "θ", + "ι", + "κ", + "λ", + "μ", + "ν", + "ξ", + "ο", + "π", + "ρ", + "θ", + "σ", + "τ", + "υ", + "φ", + "χ", + "ψ", + "ω", + "∇", + "α", + "β", + "γ", + "δ", + "ε", + "ζ", + "η", + "θ", + "ι", + "κ", + "λ", + "μ", + "ν", + "ξ", + "ο", + "π", + "ρ", + "σ", + "τ", + "υ", + "φ", + "χ", + "ψ", + "ω", + "∂", + "ε", + "θ", + "κ", + "φ", + "ρ", + "π", + "ϝ", + None, + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + None, + None, + None, + None, + None, + None, + None, + None, + None, + "𝽁", + None, + "𝽉", + None, + "𝽋", + None, + "𝽎", + None, + "𝽒", + None, + "𝽩", + None, + "𝽫", + None, + "𝽭", + None, + "𝽯", + None, + "𝽳", + None, + "𝽵", + None, + "𝽷", + None, + "𝽹", + None, + "𝽻", + None, + "𝽽", + None, + "𝽿", + None, + None, + None, + "ss", + None, + None, + "𝽗", + "𝽘", + "𝽙", + "ɣ", + "ᴘ", + "ƀ", + "đ", + "ǥ", + "ł", + "ƚ", + "𝽐", + "𝼾", + "ɉ", + "ᵿ", + "𝼿", + "ƞ", + "ɼ", + "ȸ", + "ȹ", + "𝼫", + "𝼙", + "ʆ", + "ᶘ", + "𝼬", + "𝼜", + "ʓ", + "ᶚ", + "ᴅ", + "ᴀ", + "ᴇ", + "ɿ", + "ʅ", + "ʮ", + "ʯ", + "ȡ", + "ȴ", + "ȵ", + "ȶ", + "ψ", + "ω", + "ᶁ", + "ꞕ", + "ᶇ", + "ᶊ", + "ᶎ", + "ƈ", + "ƙ", + "ƥ", + "ʠ", + "ƭ", + "𝼉", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "а", + "б", + "в", + "г", + "д", + "е", + "ж", + "з", + "и", + "к", + "л", + "м", + "о", + "п", + "р", + "с", + "т", + "у", + "ф", + "х", + "ц", + "ч", + "ш", + "ы", + "э", + "ю", + "ꚉ", + "ә", + "і", + "ј", + "ө", + "ү", + "ӏ", + "а", + "б", + "в", + "г", + "д", + "е", + "ж", + "з", + "и", + "к", + "л", + "о", + "п", + "с", + "у", + "ф", + "х", + "ц", + "ч", + "ш", + "ъ", + "ы", + "ґ", + "і", + "ѕ", + "џ", + "ҫ", + "ꙑ", + "ұ", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "𞤢", + "𞤣", + "𞤤", + "𞤥", + "𞤦", + "𞤧", + "𞤨", + "𞤩", + "𞤪", + "𞤫", + "𞤬", + "𞤭", + "𞤮", + "𞤯", + "𞤰", + "𞤱", + "𞤲", + "𞤳", + "𞤴", + "𞤵", + "𞤶", + "𞤷", + "𞤸", + "𞤹", + "𞤺", + "𞤻", + "𞤼", + "𞤽", + "𞤾", + "𞤿", + "𞥀", + "𞥁", + "𞥂", + "𞥃", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "ا", + "ب", + "ج", + "د", + None, + "و", + "ز", + "ح", + "ط", + "ي", + "ك", + "ل", + "م", + "ن", + "س", + "ع", + "ف", + "ص", + "ق", + "ر", + "ش", + "ت", + "ث", + "خ", + "ذ", + "ض", + "ظ", + "غ", + "ٮ", + "ں", + "ڡ", + "ٯ", + None, + "ب", + "ج", + None, + "ه", + None, + "ح", + None, + "ي", + "ك", + "ل", + "م", + "ن", + "س", + "ع", + "ف", + "ص", + "ق", + None, + "ش", + "ت", + "ث", + "خ", + None, + "ض", + None, + "غ", + None, + "ج", + None, + "ح", + None, + "ي", + None, + "ل", + None, + "ن", + "س", + "ع", + None, + "ص", + "ق", + None, + "ش", + None, + "خ", + None, + "ض", + None, + "غ", + None, + "ں", + None, + "ٯ", + None, + "ب", + "ج", + None, + "ه", + None, + "ح", + "ط", + "ي", + "ك", + None, + "م", + "ن", + "س", + "ع", + "ف", + "ص", + "ق", + None, + "ش", + "ت", + "ث", + "خ", + None, + "ض", + "ظ", + "غ", + "ٮ", + None, + "ڡ", + None, + "ا", + "ب", + "ج", + "د", + "ه", + "و", + "ز", + "ح", + "ط", + "ي", + None, + "ل", + "م", + "ن", + "س", + "ع", + "ف", + "ص", + "ق", + "ر", + "ش", + "ت", + "ث", + "خ", + "ذ", + "ض", + "ظ", + "غ", + None, + "ب", + "ج", + "د", + None, + "و", + "ز", + "ح", + "ط", + "ي", + None, + "ل", + "م", + "ن", + "س", + "ع", + "ف", + "ص", + "ق", + "ر", + "ش", + "ت", + "ث", + "خ", + "ذ", + "ض", + "ظ", + "غ", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "0,", + "1,", + "2,", + "3,", + "4,", + "5,", + "6,", + "7,", + "8,", + "9,", + None, + "(a)", + "(b)", + "(c)", + "(d)", + "(e)", + "(f)", + "(g)", + "(h)", + "(i)", + "(j)", + "(k)", + "(l)", + "(m)", + "(n)", + "(o)", + "(p)", + "(q)", + "(r)", + "(s)", + "(t)", + "(u)", + "(v)", + "(w)", + "(x)", + "(y)", + "(z)", + "〔s〕", + "c", + "r", + "cd", + "wz", + None, + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "hv", + "mv", + "sd", + "ss", + "ppv", + "wc", + None, + "mc", + "md", + "mr", + None, + "dj", + None, + None, + None, + "ほか", + "ココ", + "サ", + None, + "手", + "字", + "双", + "デ", + "二", + "多", + "解", + "天", + "交", + "映", + "無", + "料", + "前", + "後", + "再", + "新", + "初", + "終", + "生", + "販", + "声", + "吹", + "演", + "投", + "捕", + "一", + "三", + "遊", + "左", + "中", + "右", + "指", + "走", + "打", + "禁", + "空", + "合", + "満", + "有", + "月", + "申", + "割", + "営", + "配", + None, + "〔本〕", + "〔三〕", + "〔二〕", + "〔安〕", + "〔点〕", + "〔打〕", + "〔盗〕", + "〔勝〕", + "〔敗〕", + None, + "得", + "可", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + "丽", + "丸", + "乁", + "𠄢", + "你", + "侮", + "侻", + "倂", + "偺", + "備", + "僧", + "像", + "㒞", + "𠘺", + "免", + "兔", + "兤", + "具", + "𠔜", + "㒹", + "內", + "再", + "𠕋", + "冗", + "冤", + "仌", + "冬", + "况", + "𩇟", + "凵", + "刃", + "㓟", + "刻", + "剆", + "割", + "剷", + "㔕", + "勇", + "勉", + "勤", + "勺", + "包", + "匆", + "北", + "卉", + "卑", + "博", + "即", + "卽", + "卿", + "𠨬", + "灰", + "及", + "叟", + "𠭣", + "叫", + "叱", + "吆", + "咞", + "吸", + "呈", + "周", + "咢", + "哶", + "唐", + "啓", + "啣", + "善", + "喙", + "喫", + "喳", + "嗂", + "圖", + "嘆", + "圗", + "噑", + "噴", + "切", + "壮", + "城", + "埴", + "堍", + "型", + "堲", + "報", + "墬", + "𡓤", + "売", + "壷", + "夆", + "多", + "夢", + "奢", + "𡚨", + "𡛪", + "姬", + "娛", + "娧", + "姘", + "婦", + "㛮", + "㛼", + "嬈", + "嬾", + "𡧈", + "寃", + "寘", + "寧", + "寳", + "𡬘", + "寿", + "将", + "当", + "尢", + "㞁", + "屠", + "屮", + "峀", + "岍", + "𡷤", + "嵃", + "𡷦", + "嵮", + "嵫", + "嵼", + "巡", + "巢", + "㠯", + "巽", + "帨", + "帽", + "幩", + "㡢", + "𢆃", + "㡼", + "庰", + "庳", + "庶", + "廊", + "𪎒", + "廾", + "𢌱", + "舁", + "弢", + "㣇", + "𣊸", + "𦇚", + "形", + "彫", + "㣣", + "徚", + "忍", + "志", + "忹", + "悁", + "㤺", + "㤜", + "悔", + "𢛔", + "惇", + "慈", + "慌", + "慎", + "慌", + "慺", + "憎", + "憲", + "憤", + "憯", + "懞", + "懲", + "懶", + "成", + "戛", + "扝", + "抱", + "拔", + "捐", + "𢬌", + "挽", + "拼", + "捨", + "掃", + "揤", + "𢯱", + "搢", + "揅", + "掩", + "㨮", + "摩", + "摾", + "撝", + "摷", + "㩬", + "敏", + "敬", + "𣀊", + "旣", + "書", + "晉", + "㬙", + "暑", + "㬈", + "㫤", + "冒", + "冕", + "最", + "暜", + "肭", + "䏙", + "朗", + "望", + "朡", + "杞", + "杓", + "𣏃", + "㭉", + "柺", + "枅", + "桒", + "梅", + "𣑭", + "梎", + "栟", + "椔", + "㮝", + "楂", + "榣", + "槪", + "檨", + "𣚣", + "櫛", + "㰘", + "次", + "𣢧", + "歔", + "㱎", + "歲", + "殟", + "殺", + "殻", + "𣪍", + "𡴋", + "𣫺", + "汎", + "𣲼", + "沿", + "泍", + "汧", + "洖", + "派", + "海", + "流", + "浩", + "浸", + "涅", + "𣴞", + "洴", + "港", + "湮", + "㴳", + "滋", + "滇", + "𣻑", + "淹", + "潮", + "𣽞", + "𣾎", + "濆", + "瀹", + "瀞", + "瀛", + "㶖", + "灊", + "災", + "灷", + "炭", + "𠔥", + "煅", + "𤉣", + "熜", + "𤎫", + "爨", + "爵", + "牐", + "𤘈", + "犀", + "犕", + "𤜵", + "𤠔", + "獺", + "王", + "㺬", + "玥", + "㺸", + "瑇", + "瑜", + "瑱", + "璅", + "瓊", + "㼛", + "甤", + "𤰶", + "甾", + "𤲒", + "異", + "𢆟", + "瘐", + "𤾡", + "𤾸", + "𥁄", + "㿼", + "䀈", + "直", + "𥃳", + "𥃲", + "𥄙", + "𥄳", + "眞", + "真", + "睊", + "䀹", + "瞋", + "䁆", + "䂖", + "𥐝", + "硎", + "碌", + "磌", + "䃣", + "𥘦", + "祖", + "𥚚", + "𥛅", + "福", + "秫", + "䄯", + "穀", + "穊", + "穏", + "𥥼", + "𥪧", + "竮", + "䈂", + "𥮫", + "篆", + "築", + "䈧", + "𥲀", + "糒", + "䊠", + "糨", + "糣", + "紀", + "𥾆", + "絣", + "䌁", + "緇", + "縂", + "繅", + "䌴", + "𦈨", + "𦉇", + "䍙", + "𦋙", + "罺", + "𦌾", + "羕", + "翺", + "者", + "𦓚", + "𦔣", + "聠", + "𦖨", + "聰", + "𣍟", + "䏕", + "育", + "脃", + "䐋", + "脾", + "媵", + "𦞧", + "𦞵", + "𣎓", + "𣎜", + "舁", + "舄", + "辞", + "䑫", + "芑", + "芋", + "芝", + "劳", + "花", + "芳", + "芽", + "苦", + "𦬼", + "若", + "茝", + "荣", + "莭", + "茣", + "莽", + "菧", + "著", + "荓", + "菊", + "菌", + "菜", + "𦰶", + "𦵫", + "𦳕", + "䔫", + "蓱", + "蓳", + "蔖", + "𧏊", + "蕤", + "𦼬", + "䕝", + "䕡", + "𦾱", + "𧃒", + "䕫", + "虐", + "虜", + "虧", + "虩", + "蚩", + "蚈", + "蜎", + "蛢", + "蝹", + "蜨", + "蝫", + "螆", + "䗗", + "蟡", + "蠁", + "䗹", + "衠", + "衣", + "𧙧", + "裗", + "裞", + "䘵", + "裺", + "㒻", + "𧢮", + "𧥦", + "䚾", + "䛇", + "誠", + "諭", + "變", + "豕", + "𧲨", + "貫", + "賁", + "贛", + "起", + "𧼯", + "𠠄", + "跋", + "趼", + "跰", + "𠣞", + "軔", + "輸", + "𨗒", + "𨗭", + "邔", + "郱", + "鄑", + "𨜮", + "鄛", + "鈸", + "鋗", + "鋘", + "鉼", + "鏹", + "鐕", + "𨯺", + "開", + "䦕", + "閷", + "𨵷", + "䧦", + "雃", + "嶲", + "霣", + "𩅅", + "𩈚", + "䩮", + "䩶", + "韠", + "𩐊", + "䪲", + "𩒖", + "頋", + "頩", + "𩖶", + "飢", + "䬳", + "餩", + "馧", + "駂", + "駾", + "䯎", + "𩬰", + "鬒", + "鱀", + "鳽", + "䳎", + "䳭", + "鵧", + "𪃎", + "䳸", + "𪄅", + "𪈎", + "𪊑", + "麻", + "䵖", + "黹", + "黾", + "鼅", + "鼏", + "鼖", + "鼻", + "𪘀", + None, + None, + None, + None, + None, + None, + None, + None, + None, +) diff --git a/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/METADATA new file mode 100644 index 0000000..e3d5dba --- /dev/null +++ b/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/METADATA @@ -0,0 +1,149 @@ +Metadata-Version: 2.4 +Name: jiter +Version: 0.17.0 +Classifier: Development Status :: 4 - Beta +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: 3.15 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: GraalPy +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: Intended Audience :: System Administrators +Classifier: Operating System :: Unix +Classifier: Operating System :: POSIX :: Linux +Classifier: Environment :: Console +Classifier: Environment :: MacOS X +Classifier: Topic :: File Formats :: JSON +Classifier: Framework :: Pydantic :: 2 +License-File: LICENSE +Summary: Fast iterable JSON parser. +Home-Page: https://github.com/pydantic/jiter/ +Author-email: Samuel Colvin +License-Expression: MIT +Requires-Python: >=3.10 +Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM + +# jiter + +[![CI](https://github.com/pydantic/jiter/workflows/CI/badge.svg?event=push)](https://github.com/pydantic/jiter/actions?query=event%3Apush+branch%3Amain+workflow%3ACI) +[![pypi](https://img.shields.io/pypi/v/jiter.svg)](https://pypi.python.org/pypi/jiter) +[![versions](https://img.shields.io/pypi/pyversions/jiter.svg)](https://github.com/pydantic/jiter) +[![license](https://img.shields.io/github/license/pydantic/jiter.svg)](https://github.com/pydantic/jiter/blob/main/LICENSE) + +This is a standalone version of the JSON parser used in `pydantic-core`. The recommendation is to only use this package directly if you do not use `pydantic`. + +The API is extremely minimal: + +```python +def from_json( + json_data: bytes, + /, + *, + allow_inf_nan: bool = True, + cache_mode: Literal[True, False, 'all', 'keys', 'none'] = 'all', + partial_mode: Literal[True, False, 'off', 'on', 'trailing-strings'] = False, + catch_duplicate_keys: bool = False, + float_mode: Literal['float', 'decimal', 'lossless-float'] = 'float', +) -> Any: + """ + Parse input bytes into a JSON object. + + Arguments: + json_data: The JSON data to parse + allow_inf_nan: Whether to allow infinity (`Infinity` an `-Infinity`) and `NaN` values to float fields. + Defaults to True. + cache_mode: cache Python strings to improve performance at the cost of some memory usage + - True / 'all' - cache all strings + - 'keys' - cache only object keys + - False / 'none' - cache nothing + partial_mode: How to handle incomplete strings: + - False / 'off' - raise an exception if the input is incomplete + - True / 'on' - allow incomplete JSON but discard the last string if it is incomplete + - 'trailing-strings' - allow incomplete JSON, and include the last incomplete string in the output + catch_duplicate_keys: if True, raise an exception if objects contain the same key multiple times + float_mode: How to return floats: as a `float`, `Decimal` or `LosslessFloat` + + Returns: + Python object built from the JSON input. + """ + + +def cache_clear() -> None: + """ + Reset the string cache. + """ + + +def cache_usage() -> int: + """ + get the size of the string cache. + + Returns: + Size of the string cache in bytes. + """ + +``` + +## Examples + +The main function provided by Jiter is `from_json()`, which accepts a bytes object containing JSON and returns a Python dictionary, list or other value. + +```python +import jiter + +json_data = b'{"name": "John", "age": 30}' +parsed_data = jiter.from_json(json_data) +print(parsed_data) # Output: {'name': 'John', 'age': 30} +``` + +### Handling Partial JSON + +Incomplete JSON objects can be parsed using the `partial_mode=` parameter. + +```python +import jiter + +partial_json = b'{"name": "John", "age": 30, "city": "New Yor' + +# Raise error on incomplete JSON +try: + jiter.from_json(partial_json, partial_mode=False) +except ValueError as e: + print(f'Error: {e}') + +# Parse incomplete JSON, discarding incomplete last field +result = jiter.from_json(partial_json, partial_mode=True) +print(result) # Output: {'name': 'John', 'age': 30} + +# Parse incomplete JSON, including incomplete last field +result = jiter.from_json(partial_json, partial_mode='trailing-strings') +print(result) # Output: {'name': 'John', 'age': 30, 'city': 'New Yor'} +``` + +### Catching Duplicate Keys + +The `catch_duplicate_keys=True` option can be used to raise a `ValueError` if an object contains duplicate keys. + +```python +import jiter + +json_with_dupes = b'{"foo": 1, "foo": 2}' + +# Default behavior (last value wins) +result = jiter.from_json(json_with_dupes) +print(result) # Output: {'foo': 2} + +# Catch duplicate keys +try: + jiter.from_json(json_with_dupes, catch_duplicate_keys=True) +except ValueError as e: + print(f'Error: {e}') +``` + diff --git a/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/RECORD new file mode 100644 index 0000000..80396cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/RECORD @@ -0,0 +1,11 @@ +jiter-0.17.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +jiter-0.17.0.dist-info/METADATA,sha256=c4_Fslx8uvgTkohqlWYTIoiMVpEgW1egl1odofYuPqc,5215 +jiter-0.17.0.dist-info/RECORD,, +jiter-0.17.0.dist-info/WHEEL,sha256=cdZhYY2bcH9hrDcqDVEEdpL0M5tjmmAr-lwUq15CEdY,147 +jiter-0.17.0.dist-info/licenses/LICENSE,sha256=fHE0ufe5eMA_yodVF885jbkfGbu4EJtmhedCqj9XRo4,1091 +jiter-0.17.0.dist-info/sboms/jiter-python.cyclonedx.json,sha256=XW4ObCRkaj3EDr5AruHtBGk5OFpz_xBzofuJE6J8oM0,43425 +jiter/__init__.py,sha256=Fp9HkOixiYYDSiC_80vmiJ_sCoCGT8OAh48yltm0lP0,103 +jiter/__init__.pyi,sha256=A_KrIA3Jfuu-S8kptMexo7DDq6kUwpTIM8KrrfwZhcg,2319 +jiter/__pycache__/__init__.cpython-312.pyc,, +jiter/jiter.cpython-312-x86_64-linux-gnu.so,sha256=hz8zKHftEUhCMeAF4rB45vCjklOgE4KQ7vNLmnuskYg,747976 +jiter/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/WHEEL new file mode 100644 index 0000000..8cb0d6d --- /dev/null +++ b/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: maturin (1.15.0) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 diff --git a/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..47de40d --- /dev/null +++ b/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 to present Samuel Colvin + +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. diff --git a/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/sboms/jiter-python.cyclonedx.json b/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/sboms/jiter-python.cyclonedx.json new file mode 100644 index 0000000..4754f50 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jiter-0.17.0.dist-info/sboms/jiter-python.cyclonedx.json @@ -0,0 +1,1420 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "version": 1, + "serialNumber": "urn:uuid:5bd3e09e-7557-4b64-8cf2-b6ad12bf0feb", + "metadata": { + "timestamp": "2026-09-12T14:52:35.406994479Z", + "tools": [ + { + "vendor": "CycloneDX", + "name": "cargo-cyclonedx", + "version": "0.5.9" + } + ], + "authors": [ + { + "name": "Samuel Colvin", + "email": "samuel@pydantic.dev" + } + ], + "component": { + "type": "library", + "bom-ref": "path+file:///home/runner/work/jiter/jiter/crates/jiter-python#0.17.0", + "author": "Samuel Colvin ", + "name": "jiter-python", + "version": "0.17.0", + "scope": "required", + "licenses": [ + { + "expression": "MIT" + } + ], + "purl": "pkg:cargo/jiter-python@0.17.0?download_url=file://.", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pydantic/jiter/" + }, + { + "type": "vcs", + "url": "https://github.com/pydantic/jiter/" + } + ], + "components": [ + { + "type": "library", + "bom-ref": "path+file:///home/runner/work/jiter/jiter/crates/jiter-python#0.17.0 bin-target-0", + "name": "jiter_python", + "version": "0.17.0", + "purl": "pkg:cargo/jiter-python@0.17.0?download_url=file://.#src/lib.rs" + } + ] + }, + "properties": [ + { + "name": "cdx:rustc:sbom:target:all_targets", + "value": "true" + } + ] + }, + "components": [ + { + "type": "library", + "bom-ref": "path+file:///home/runner/work/jiter/jiter/crates/jiter#0.17.0", + "author": "Samuel Colvin ", + "name": "jiter", + "version": "0.17.0", + "description": "Fast Iterable JSON parser", + "scope": "required", + "licenses": [ + { + "expression": "MIT" + } + ], + "purl": "pkg:cargo/jiter@0.17.0?download_url=file://../jiter", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pydantic/jiter/" + }, + { + "type": "vcs", + "url": "https://github.com/pydantic/jiter/" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#ahash@0.8.12", + "author": "Tom Kaitchuck ", + "name": "ahash", + "version": "0.8.12", + "description": "A non-cryptographic hash function using AES-NI for high performance", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/ahash@0.8.12", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/ahash" + }, + { + "type": "vcs", + "url": "https://github.com/tkaitchuck/ahash" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#autocfg@1.5.1", + "author": "Josh Stone ", + "name": "autocfg", + "version": "1.5.1", + "description": "Automatic cfg for Rust compiler features", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "purl": "pkg:cargo/autocfg@1.5.1", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/autocfg/" + }, + { + "type": "vcs", + "url": "https://github.com/cuviper/autocfg" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#bitvec@1.1.1", + "name": "bitvec", + "version": "1.1.1", + "description": "Addresses memory by bits, for packed collections and bitfields", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" + } + ], + "licenses": [ + { + "expression": "MIT" + } + ], + "purl": "pkg:cargo/bitvec@1.1.1", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/bitvec/latest/bitvec" + }, + { + "type": "website", + "url": "https://bitvecto-rs.github.io/bitvec" + }, + { + "type": "vcs", + "url": "https://github.com/bitvecto-rs/bitvec" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", + "author": "Alex Crichton ", + "name": "cfg-if", + "version": "1.0.4", + "description": "A macro to ergonomically define an item depending on a large number of #[cfg] parameters. Structured like an if-else chain, the first matching branch is the item that gets emitted. ", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/cfg-if@1.0.4", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/rust-lang/cfg-if" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#funty@2.0.0", + "author": "myrrlyn ", + "name": "funty", + "version": "2.0.0", + "description": "Trait generalization over the primitive types", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + } + ], + "licenses": [ + { + "expression": "MIT" + } + ], + "purl": "pkg:cargo/funty@2.0.0", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/funty" + }, + { + "type": "vcs", + "url": "https://github.com/myrrlyn/funty" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.4", + "author": "The Rand Project Developers", + "name": "getrandom", + "version": "0.3.4", + "description": "A small cross-platform library for retrieving random data from system source", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/getrandom@0.3.4", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/getrandom" + }, + { + "type": "vcs", + "url": "https://github.com/rust-random/getrandom" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0", + "name": "heck", + "version": "0.5.0", + "description": "heck is a case conversion library.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/heck@0.5.0", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/withoutboats/heck" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#lexical-parse-float@1.0.6", + "author": "Alex Huszagh ", + "name": "lexical-parse-float", + "version": "1.0.6", + "description": "Efficient parsing of floats from strings.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "52a9f232fbd6f550bc0137dcb5f99ab674071ac2d690ac69704593cb4abbea56" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/lexical-parse-float@1.0.6", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/Alexhuszagh/rust-lexical" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#lexical-parse-integer@1.0.6", + "author": "Alex Huszagh ", + "name": "lexical-parse-integer", + "version": "1.0.6", + "description": "Efficient parsing of integers from strings.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "9a7a039f8fb9c19c996cd7b2fcce303c1b2874fe1aca544edc85c4a5f8489b34" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/lexical-parse-integer@1.0.6", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/Alexhuszagh/rust-lexical" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#lexical-util@1.0.7", + "author": "Alex Huszagh ", + "name": "lexical-util", + "version": "1.0.7", + "description": "Shared utilities for lexical creates.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "2604dd126bb14f13fb5d1bd6a66155079cb9fa655b37f875b3a742c705dbed17" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/lexical-util@1.0.7", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/Alexhuszagh/rust-lexical" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189", + "name": "libc", + "version": "0.2.189", + "description": "Raw FFI bindings to platform libraries like libc.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/libc@0.2.189", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/rust-lang/libc" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#num-bigint@0.4.8", + "author": "The Rust Project Developers", + "name": "num-bigint", + "version": "0.4.8", + "description": "Big integer implementation for Rust", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/num-bigint@0.4.8", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/num-bigint" + }, + { + "type": "website", + "url": "https://github.com/rust-num/num-bigint" + }, + { + "type": "vcs", + "url": "https://github.com/rust-num/num-bigint" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#num-integer@0.1.47", + "author": "The Rust Project Developers", + "name": "num-integer", + "version": "0.1.47", + "description": "Integer traits and functions", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/num-integer@0.1.47", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/num-integer" + }, + { + "type": "website", + "url": "https://github.com/rust-num/num-integer" + }, + { + "type": "vcs", + "url": "https://github.com/rust-num/num-integer" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19", + "author": "The Rust Project Developers", + "name": "num-traits", + "version": "0.2.19", + "description": "Numeric traits for generic mathematics", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/num-traits@0.2.19", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/num-traits" + }, + { + "type": "website", + "url": "https://github.com/rust-num/num-traits" + }, + { + "type": "vcs", + "url": "https://github.com/rust-num/num-traits" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4", + "author": "Aleksey Kladov ", + "name": "once_cell", + "version": "1.21.4", + "description": "Single assignment cells and lazy values.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/once_cell@1.21.4", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/once_cell" + }, + { + "type": "vcs", + "url": "https://github.com/matklad/once_cell" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.15.0", + "name": "portable-atomic", + "version": "1.15.0", + "description": "Portable atomic types including support for 128-bit atomics, atomic float, etc. ", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "purl": "pkg:cargo/portable-atomic@1.15.0", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/taiki-e/portable-atomic" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107", + "author": "David Tolnay , Alex Crichton ", + "name": "proc-macro2", + "version": "1.0.107", + "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/proc-macro2@1.0.107", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/proc-macro2" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/proc-macro2" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.2", + "author": "PyO3 Project and Contributors ", + "name": "pyo3-build-config", + "version": "0.29.2", + "description": "Build configuration for the PyO3 ecosystem", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3-build-config@0.29.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.2", + "author": "PyO3 Project and Contributors ", + "name": "pyo3-ffi", + "version": "0.29.2", + "description": "Python-API bindings for the PyO3 ecosystem", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3-ffi@0.29.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "other", + "url": "python" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.2", + "author": "PyO3 Project and Contributors ", + "name": "pyo3-macros-backend", + "version": "0.29.2", + "description": "Code generation for PyO3 package", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3-macros-backend@0.29.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.2", + "author": "PyO3 Project and Contributors ", + "name": "pyo3-macros", + "version": "0.29.2", + "description": "Proc macros for PyO3 package", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3-macros@0.29.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.2", + "author": "PyO3 Project and Contributors ", + "name": "pyo3", + "version": "0.29.2", + "description": "Bindings to Python interpreter", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3@0.29.2", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/crate/pyo3/" + }, + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "other", + "url": "pyo3-python" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47", + "author": "David Tolnay ", + "name": "quote", + "version": "1.0.47", + "description": "Quasi-quoting macro quote!(...)", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/quote@1.0.47", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/quote/" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/quote" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#r-efi@5.3.0", + "name": "r-efi", + "version": "5.3.0", + "description": "UEFI Reference Specification Protocol Constants and Definitions", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0 OR LGPL-2.1-or-later" + } + ], + "purl": "pkg:cargo/r-efi@5.3.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/r-efi/r-efi/wiki" + }, + { + "type": "vcs", + "url": "https://github.com/r-efi/r-efi" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#radium@0.7.0", + "author": "Nika Layzell , myrrlyn ", + "name": "radium", + "version": "0.7.0", + "description": "Portable interfaces for maybe-atomic types", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + } + ], + "licenses": [ + { + "expression": "MIT" + } + ], + "purl": "pkg:cargo/radium@0.7.0", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/radium" + }, + { + "type": "website", + "url": "https://github.com/bitvecto-rs/radium" + }, + { + "type": "vcs", + "url": "https://github.com/bitvecto-rs/radium" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2", + "author": "The Servo Project Developers", + "name": "smallvec", + "version": "1.15.2", + "description": "'Small vector' optimization: store up to a small number of items on the stack", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/smallvec@1.15.2", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/smallvec/" + }, + { + "type": "vcs", + "url": "https://github.com/servo/rust-smallvec" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119", + "author": "David Tolnay ", + "name": "syn", + "version": "2.0.119", + "description": "Parser for Rust source code", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/syn@2.0.119", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/syn" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/syn" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#tap@1.0.1", + "author": "Elliott Linder , myrrlyn ", + "name": "tap", + "version": "1.0.1", + "description": "Generic extensions for tapping values in Rust", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + } + ], + "licenses": [ + { + "expression": "MIT" + } + ], + "purl": "pkg:cargo/tap@1.0.1", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/tap" + }, + { + "type": "website", + "url": "https://github.com/myrrlyn/tap" + }, + { + "type": "vcs", + "url": "https://github.com/myrrlyn/tap" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5", + "author": "Dan Gohman ", + "name": "target-lexicon", + "version": "0.13.5", + "description": "LLVM target triple types", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 WITH LLVM-exception" + } + ], + "purl": "pkg:cargo/target-lexicon@0.13.5", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/target-lexicon/" + }, + { + "type": "vcs", + "url": "https://github.com/bytecodealliance/target-lexicon" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24", + "author": "David Tolnay ", + "name": "unicode-ident", + "version": "1.0.24", + "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + } + ], + "licenses": [ + { + "expression": "(MIT OR Apache-2.0) AND Unicode-3.0" + } + ], + "purl": "pkg:cargo/unicode-ident@1.0.24", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/unicode-ident" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/unicode-ident" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5", + "author": "Sergio Benitez ", + "name": "version_check", + "version": "0.9.5", + "description": "Tiny crate to check the version of the installed/running rustc.", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/version_check@0.9.5", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/version_check/" + }, + { + "type": "vcs", + "url": "https://github.com/SergioBenitez/version_check" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#wasip2@1.0.4+wasi-0.2.12", + "name": "wasip2", + "version": "1.0.4+wasi-0.2.12", + "description": "WASIp2 API bindings for Rust", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT" + } + ], + "purl": "pkg:cargo/wasip2@1.0.4+wasi-0.2.12", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/wasip2" + }, + { + "type": "vcs", + "url": "https://github.com/bytecodealliance/wasi-rs" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#wit-bindgen@0.57.1", + "author": "Alex Crichton ", + "name": "wit-bindgen", + "version": "0.57.1", + "description": "Rust bindings generator and runtime support for WIT and the component model. Used when compiling Rust programs to the component model. ", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT" + } + ], + "purl": "pkg:cargo/wit-bindgen@0.57.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/bytecodealliance/wit-bindgen" + }, + { + "type": "vcs", + "url": "https://github.com/bytecodealliance/wit-bindgen" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#wyz@0.5.1", + "author": "myrrlyn ", + "name": "wyz", + "version": "0.5.1", + "description": "myrrlyn’s utility collection", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" + } + ], + "licenses": [ + { + "expression": "MIT" + } + ], + "purl": "pkg:cargo/wyz@0.5.1", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/wyz" + }, + { + "type": "website", + "url": "https://myrrlyn.net/crates/wyz" + }, + { + "type": "vcs", + "url": "https://github.com/myrrlyn/wyz" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#zerocopy-derive@0.8.56", + "name": "zerocopy-derive", + "version": "0.8.56", + "description": "Custom derive for traits from the zerocopy crate", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" + } + ], + "licenses": [ + { + "expression": "BSD-2-Clause OR Apache-2.0 OR MIT" + } + ], + "purl": "pkg:cargo/zerocopy-derive@0.8.56", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/google/zerocopy" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#zerocopy@0.8.56", + "name": "zerocopy", + "version": "0.8.56", + "description": "Zerocopy makes zero-cost memory manipulation effortless. We write \"unsafe\" so you don't have to.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" + } + ], + "licenses": [ + { + "expression": "BSD-2-Clause OR Apache-2.0 OR MIT" + } + ], + "purl": "pkg:cargo/zerocopy@0.8.56", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/google/zerocopy" + } + ] + } + ], + "dependencies": [ + { + "ref": "path+file:///home/runner/work/jiter/jiter/crates/jiter#0.17.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#ahash@0.8.12", + "registry+https://github.com/rust-lang/crates.io-index#bitvec@1.1.1", + "registry+https://github.com/rust-lang/crates.io-index#lexical-parse-float@1.0.6", + "registry+https://github.com/rust-lang/crates.io-index#num-bigint@0.4.8", + "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19", + "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.2", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.2", + "registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2" + ] + }, + { + "ref": "path+file:///home/runner/work/jiter/jiter/crates/jiter-python#0.17.0", + "dependsOn": [ + "path+file:///home/runner/work/jiter/jiter/crates/jiter#0.17.0", + "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.2" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#ahash@0.8.12", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", + "registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.4", + "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4", + "registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5", + "registry+https://github.com/rust-lang/crates.io-index#zerocopy@0.8.56" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#autocfg@1.5.1" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#bitvec@1.1.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#funty@2.0.0", + "registry+https://github.com/rust-lang/crates.io-index#radium@0.7.0", + "registry+https://github.com/rust-lang/crates.io-index#tap@1.0.1", + "registry+https://github.com/rust-lang/crates.io-index#wyz@0.5.1" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#funty@2.0.0" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.4", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", + "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189", + "registry+https://github.com/rust-lang/crates.io-index#r-efi@5.3.0", + "registry+https://github.com/rust-lang/crates.io-index#wasip2@1.0.4+wasi-0.2.12" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#lexical-parse-float@1.0.6", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#lexical-parse-integer@1.0.6", + "registry+https://github.com/rust-lang/crates.io-index#lexical-util@1.0.7" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#lexical-parse-integer@1.0.6", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#lexical-util@1.0.7" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#lexical-util@1.0.7" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#num-bigint@0.4.8", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#num-integer@0.1.47", + "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#num-integer@0.1.47", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#autocfg@1.5.1" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.15.0" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.2", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.2", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.2" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.2", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0", + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47", + "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.2", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.2", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47", + "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.2", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.189", + "registry+https://github.com/rust-lang/crates.io-index#num-bigint@0.4.8", + "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19", + "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4", + "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.15.0", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.2", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.2", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.2" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#r-efi@5.3.0" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#radium@0.7.0" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.2" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47", + "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#tap@1.0.1" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#wasip2@1.0.4+wasi-0.2.12", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#wit-bindgen@0.57.1" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#wit-bindgen@0.57.1" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#wyz@0.5.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#tap@1.0.1" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#zerocopy-derive@0.8.56", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.107", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.47", + "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.119" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#zerocopy@0.8.56", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#zerocopy-derive@0.8.56" + ] + } + ] +} \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/jiter/__init__.py b/venv/lib/python3.12/site-packages/jiter/__init__.py new file mode 100644 index 0000000..3d17192 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jiter/__init__.py @@ -0,0 +1,5 @@ +from .jiter import * + +__doc__ = jiter.__doc__ +if hasattr(jiter, "__all__"): + __all__ = jiter.__all__ \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/jiter/__init__.pyi b/venv/lib/python3.12/site-packages/jiter/__init__.pyi new file mode 100644 index 0000000..faa6377 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jiter/__init__.pyi @@ -0,0 +1,63 @@ +import decimal +from typing import Any, Literal + +def from_json( + json_data: bytes, + /, + *, + allow_inf_nan: bool = True, + cache_mode: Literal[True, False, 'all', 'keys', 'none'] = 'all', + partial_mode: Literal[True, False, 'off', 'on', 'trailing-strings'] = False, + catch_duplicate_keys: bool = False, + float_mode: Literal['float', 'decimal', 'lossless-float'] = 'float', +) -> Any: + """ + Parse input bytes into a JSON object. + + Arguments: + json_data: The JSON data to parse + allow_inf_nan: Whether to allow infinity (`Infinity` an `-Infinity`) and `NaN` values to float fields. + Defaults to True. + cache_mode: cache Python strings to improve performance at the cost of some memory usage + - True / 'all' - cache all strings + - 'keys' - cache only object keys + - False / 'none' - cache nothing + partial_mode: How to handle incomplete strings: + - False / 'off' - raise an exception if the input is incomplete + - True / 'on' - allow incomplete JSON but discard the last string if it is incomplete + - 'trailing-strings' - allow incomplete JSON, and include the last incomplete string in the output + catch_duplicate_keys: if True, raise an exception if objects contain the same key multiple times + float_mode: How to return floats: as a `float`, `Decimal` or `LosslessFloat` + + Returns: + Python object built from the JSON input. + """ + +def cache_clear() -> None: + """ + Reset the string cache. A cache in use by a parse on another thread is left as it is. + """ + +def cache_usage() -> int: + """ + get the size of the string cache. + + Returns: + Size of the string cache in bytes. + """ + +class LosslessFloat: + """ + Represents a float from JSON, by holding the underlying bytes representing a float from JSON. + """ + def __init__(self, json_float: bytes) -> None: + """Construct a LosslessFloat object from a JSON bytes slice""" + + def as_decimal(self) -> decimal.Decimal: + """Construct a Python Decimal from the JSON bytes slice""" + + def __float__(self) -> float: + """Construct a Python float from the JSON bytes slice""" + + def __bytes__(self) -> bytes: + """Return the JSON bytes slice as bytes""" diff --git a/venv/lib/python3.12/site-packages/jiter/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/jiter/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2ecfe8a Binary files /dev/null and b/venv/lib/python3.12/site-packages/jiter/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jiter/jiter.cpython-312-x86_64-linux-gnu.so b/venv/lib/python3.12/site-packages/jiter/jiter.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000..3e6ccf2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jiter/jiter.cpython-312-x86_64-linux-gnu.so differ diff --git a/venv/lib/python3.12/site-packages/jiter/py.typed b/venv/lib/python3.12/site-packages/jiter/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/LICENSE b/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/LICENSE new file mode 100644 index 0000000..9c520c6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved + +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. diff --git a/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/METADATA new file mode 100644 index 0000000..465f77a --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/METADATA @@ -0,0 +1,239 @@ +Metadata-Version: 2.1 +Name: jmespath +Version: 1.1.0 +Summary: JSON Matching Expressions +Home-page: https://github.com/jmespath/jmespath.py +Author: James Saryerwinnie +Author-email: js@jamesls.com +License: MIT +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=3.9 +License-File: LICENSE + +JMESPath +======== + + +.. image:: https://badges.gitter.im/Join Chat.svg + :target: https://gitter.im/jmespath/chat + + +JMESPath (pronounced "james path") allows you to declaratively specify how to +extract elements from a JSON document. + +For example, given this document:: + + {"foo": {"bar": "baz"}} + +The jmespath expression ``foo.bar`` will return "baz". + +JMESPath also supports: + +Referencing elements in a list. Given the data:: + + {"foo": {"bar": ["one", "two"]}} + +The expression: ``foo.bar[0]`` will return "one". +You can also reference all the items in a list using the ``*`` +syntax:: + + {"foo": {"bar": [{"name": "one"}, {"name": "two"}]}} + +The expression: ``foo.bar[*].name`` will return ["one", "two"]. +Negative indexing is also supported (-1 refers to the last element +in the list). Given the data above, the expression +``foo.bar[-1].name`` will return "two". + +The ``*`` can also be used for hash types:: + + {"foo": {"bar": {"name": "one"}, "baz": {"name": "two"}}} + +The expression: ``foo.*.name`` will return ["one", "two"]. + + +Installation +============ + +You can install JMESPath from pypi with: + +.. code:: bash + + pip install jmespath + + +API +=== + +The ``jmespath.py`` library has two functions +that operate on python data structures. You can use ``search`` +and give it the jmespath expression and the data: + +.. code:: python + + >>> import jmespath + >>> path = jmespath.search('foo.bar', {'foo': {'bar': 'baz'}}) + 'baz' + +Similar to the ``re`` module, you can use the ``compile`` function +to compile the JMESPath expression and use this parsed expression +to perform repeated searches: + +.. code:: python + + >>> import jmespath + >>> expression = jmespath.compile('foo.bar') + >>> expression.search({'foo': {'bar': 'baz'}}) + 'baz' + >>> expression.search({'foo': {'bar': 'other'}}) + 'other' + +This is useful if you're going to use the same jmespath expression to +search multiple documents. This avoids having to reparse the +JMESPath expression each time you search a new document. + +Options +------- + +You can provide an instance of ``jmespath.Options`` to control how +a JMESPath expression is evaluated. The most common scenario for +using an ``Options`` instance is if you want to have ordered output +of your dict keys. To do this you can use either of these options: + +.. code:: python + + >>> import jmespath + >>> jmespath.search('{a: a, b: b}', + ... mydata, + ... jmespath.Options(dict_cls=collections.OrderedDict)) + + + >>> import jmespath + >>> parsed = jmespath.compile('{a: a, b: b}') + >>> parsed.search(mydata, + ... jmespath.Options(dict_cls=collections.OrderedDict)) + + +Custom Functions +~~~~~~~~~~~~~~~~ + +The JMESPath language has numerous +`built-in functions +`__, but it is +also possible to add your own custom functions. Keep in mind that +custom function support in jmespath.py is experimental and the API may +change based on feedback. + +**If you have a custom function that you've found useful, consider submitting +it to jmespath.site and propose that it be added to the JMESPath language.** +You can submit proposals +`here `__. + +To create custom functions: + +* Create a subclass of ``jmespath.functions.Functions``. +* Create a method with the name ``_func_``. +* Apply the ``jmespath.functions.signature`` decorator that indicates + the expected types of the function arguments. +* Provide an instance of your subclass in a ``jmespath.Options`` object. + +Below are a few examples: + +.. code:: python + + import jmespath + from jmespath import functions + + # 1. Create a subclass of functions.Functions. + # The function.Functions base class has logic + # that introspects all of its methods and automatically + # registers your custom functions in its function table. + class CustomFunctions(functions.Functions): + + # 2 and 3. Create a function that starts with _func_ + # and decorate it with @signature which indicates its + # expected types. + # In this example, we're creating a jmespath function + # called "unique_letters" that accepts a single argument + # with an expected type "string". + @functions.signature({'types': ['string']}) + def _func_unique_letters(self, s): + # Given a string s, return a sorted + # string of unique letters: 'ccbbadd' -> 'abcd' + return ''.join(sorted(set(s))) + + # Here's another example. This is creating + # a jmespath function called "my_add" that expects + # two arguments, both of which should be of type number. + @functions.signature({'types': ['number']}, {'types': ['number']}) + def _func_my_add(self, x, y): + return x + y + + # 4. Provide an instance of your subclass in a Options object. + options = jmespath.Options(custom_functions=CustomFunctions()) + + # Provide this value to jmespath.search: + # This will print 3 + print( + jmespath.search( + 'my_add(`1`, `2`)', {}, options=options) + ) + + # This will print "abcd" + print( + jmespath.search( + 'foo.bar | unique_letters(@)', + {'foo': {'bar': 'ccbbadd'}}, + options=options) + ) + +Again, if you come up with useful functions that you think make +sense in the JMESPath language (and make sense to implement in all +JMESPath libraries, not just python), please let us know at +`jmespath.site `__. + + +Specification +============= + +If you'd like to learn more about the JMESPath language, you can check out +the `JMESPath tutorial `__. Also check +out the `JMESPath examples page `__ for +examples of more complex jmespath queries. + +The grammar is specified using ABNF, as described in +`RFC4234 `_. +You can find the most up to date +`grammar for JMESPath here `__. + +You can read the full +`JMESPath specification here `__. + + +Testing +======= + +In addition to the unit tests for the jmespath modules, +there is a ``tests/compliance`` directory that contains +.json files with test cases. This allows other implementations +to verify they are producing the correct output. Each json +file is grouped by feature. + + +Discuss +======= + +Join us on our `Gitter channel `__ +if you want to chat or if you have any questions. diff --git a/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/RECORD new file mode 100644 index 0000000..ea2dce9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/RECORD @@ -0,0 +1,24 @@ +../../../bin/__pycache__/jp.cpython-312.pyc,, +../../../bin/jp.py,sha256=Q1G2OdMFSvwTL1nEaySfw_KWnmQNhIrOeuhRGNuzgek,1720 +jmespath-1.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +jmespath-1.1.0.dist-info/LICENSE,sha256=bu-s-k1xuC0IQIx1FHCsjZhUU42iFCyye-Aof7E9Crk,1113 +jmespath-1.1.0.dist-info/METADATA,sha256=gRgemjEvG4flvnyDdsV7DoyWK6UkoloxPUPcLi05hPM,7610 +jmespath-1.1.0.dist-info/RECORD,, +jmespath-1.1.0.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91 +jmespath-1.1.0.dist-info/top_level.txt,sha256=vuy_oZ1ckpeSNrAi8JK8-yIGO6bduO3qvW2cCMQmPH8,9 +jmespath/__init__.py,sha256=G97-NCdEGv5d0XOBK5XeGNsWcxKUpMEU45Lh75Sc2KM,281 +jmespath/__pycache__/__init__.cpython-312.pyc,, +jmespath/__pycache__/ast.cpython-312.pyc,, +jmespath/__pycache__/compat.cpython-312.pyc,, +jmespath/__pycache__/exceptions.cpython-312.pyc,, +jmespath/__pycache__/functions.cpython-312.pyc,, +jmespath/__pycache__/lexer.cpython-312.pyc,, +jmespath/__pycache__/parser.cpython-312.pyc,, +jmespath/__pycache__/visitor.cpython-312.pyc,, +jmespath/ast.py,sha256=SiHRM1mdQPRt12R2qkQu_Ezbd-ghk5HLy1Hl1jHAyv8,2130 +jmespath/compat.py,sha256=rMHpvmG3JAxX_lQ0yPK1dhLz-yOuEybMqdrEq6RRAr4,381 +jmespath/exceptions.py,sha256=5HRot4Bv3SCTdvh5lAQdQBsqSdaKjuTPSyGmk08zSvA,4128 +jmespath/functions.py,sha256=c9KRMTFNm6KkV6I9QvgjhiZGc7sxILs-L3mvxHBV_kw,12729 +jmespath/lexer.py,sha256=LE3-dM75ny9RMR8mXmWlKKRTIlz8njvWz-M-2y4t5WA,8574 +jmespath/parser.py,sha256=VjDQN5yjSe5Gf5c9IncA6E4YxVRzYhJUaXBZraDsUTc,19571 +jmespath/visitor.py,sha256=w4pEz62C7l9Qs30AP3NmOMEr55nuJWVV-kb-QhEYKRI,10834 diff --git a/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/WHEEL new file mode 100644 index 0000000..ecaf39f --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (71.1.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/top_level.txt new file mode 100644 index 0000000..45c1e03 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath-1.1.0.dist-info/top_level.txt @@ -0,0 +1 @@ +jmespath diff --git a/venv/lib/python3.12/site-packages/jmespath/__init__.py b/venv/lib/python3.12/site-packages/jmespath/__init__.py new file mode 100644 index 0000000..baf7382 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath/__init__.py @@ -0,0 +1,12 @@ +from jmespath import parser +from jmespath.visitor import Options + +__version__ = '1.1.0' + + +def compile(expression): + return parser.Parser().parse(expression) + + +def search(expression, data, options=None): + return parser.Parser().parse(expression).search(data, options=options) diff --git a/venv/lib/python3.12/site-packages/jmespath/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/jmespath/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..601369d Binary files /dev/null and b/venv/lib/python3.12/site-packages/jmespath/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jmespath/__pycache__/ast.cpython-312.pyc b/venv/lib/python3.12/site-packages/jmespath/__pycache__/ast.cpython-312.pyc new file mode 100644 index 0000000..c54bf74 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jmespath/__pycache__/ast.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jmespath/__pycache__/compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/jmespath/__pycache__/compat.cpython-312.pyc new file mode 100644 index 0000000..8a59414 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jmespath/__pycache__/compat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jmespath/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/jmespath/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..63e8556 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jmespath/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jmespath/__pycache__/functions.cpython-312.pyc b/venv/lib/python3.12/site-packages/jmespath/__pycache__/functions.cpython-312.pyc new file mode 100644 index 0000000..44952ee Binary files /dev/null and b/venv/lib/python3.12/site-packages/jmespath/__pycache__/functions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jmespath/__pycache__/lexer.cpython-312.pyc b/venv/lib/python3.12/site-packages/jmespath/__pycache__/lexer.cpython-312.pyc new file mode 100644 index 0000000..8a6c2db Binary files /dev/null and b/venv/lib/python3.12/site-packages/jmespath/__pycache__/lexer.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jmespath/__pycache__/parser.cpython-312.pyc b/venv/lib/python3.12/site-packages/jmespath/__pycache__/parser.cpython-312.pyc new file mode 100644 index 0000000..497b4a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jmespath/__pycache__/parser.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jmespath/__pycache__/visitor.cpython-312.pyc b/venv/lib/python3.12/site-packages/jmespath/__pycache__/visitor.cpython-312.pyc new file mode 100644 index 0000000..fc81580 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jmespath/__pycache__/visitor.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jmespath/ast.py b/venv/lib/python3.12/site-packages/jmespath/ast.py new file mode 100644 index 0000000..dd56c6e --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath/ast.py @@ -0,0 +1,90 @@ +# AST nodes have this structure: +# {"type": ", children: [], "value": ""} + + +def comparator(name, first, second): + return {'type': 'comparator', 'children': [first, second], 'value': name} + + +def current_node(): + return {'type': 'current', 'children': []} + + +def expref(expression): + return {'type': 'expref', 'children': [expression]} + + +def function_expression(name, args): + return {'type': 'function_expression', 'children': args, 'value': name} + + +def field(name): + return {"type": "field", "children": [], "value": name} + + +def filter_projection(left, right, comparator): + return {'type': 'filter_projection', 'children': [left, right, comparator]} + + +def flatten(node): + return {'type': 'flatten', 'children': [node]} + + +def identity(): + return {"type": "identity", 'children': []} + + +def index(index): + return {"type": "index", "value": index, "children": []} + + +def index_expression(children): + return {"type": "index_expression", 'children': children} + + +def key_val_pair(key_name, node): + return {"type": "key_val_pair", 'children': [node], "value": key_name} + + +def literal(literal_value): + return {'type': 'literal', 'value': literal_value, 'children': []} + + +def multi_select_dict(nodes): + return {"type": "multi_select_dict", "children": nodes} + + +def multi_select_list(nodes): + return {"type": "multi_select_list", "children": nodes} + + +def or_expression(left, right): + return {"type": "or_expression", "children": [left, right]} + + +def and_expression(left, right): + return {"type": "and_expression", "children": [left, right]} + + +def not_expression(expr): + return {"type": "not_expression", "children": [expr]} + + +def pipe(left, right): + return {'type': 'pipe', 'children': [left, right]} + + +def projection(left, right): + return {'type': 'projection', 'children': [left, right]} + + +def subexpression(children): + return {"type": "subexpression", 'children': children} + + +def slice(start, end, step): + return {"type": "slice", "children": [start, end, step]} + + +def value_projection(left, right): + return {'type': 'value_projection', 'children': [left, right]} diff --git a/venv/lib/python3.12/site-packages/jmespath/compat.py b/venv/lib/python3.12/site-packages/jmespath/compat.py new file mode 100644 index 0000000..50f8f27 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath/compat.py @@ -0,0 +1,19 @@ +import sys +import inspect +from itertools import zip_longest + + +text_type = str +string_type = str + + +def with_str_method(cls): + # In python3, we don't need to do anything, we return a str type. + return cls + +def with_repr_method(cls): + return cls + +def get_methods(cls): + for name, method in inspect.getmembers(cls, predicate=inspect.isfunction): + yield name, method diff --git a/venv/lib/python3.12/site-packages/jmespath/exceptions.py b/venv/lib/python3.12/site-packages/jmespath/exceptions.py new file mode 100644 index 0000000..0156015 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath/exceptions.py @@ -0,0 +1,122 @@ +from jmespath.compat import with_str_method + + +class JMESPathError(ValueError): + pass + + +@with_str_method +class ParseError(JMESPathError): + _ERROR_MESSAGE = 'Invalid jmespath expression' + def __init__(self, lex_position, token_value, token_type, + msg=_ERROR_MESSAGE): + super(ParseError, self).__init__(lex_position, token_value, token_type) + self.lex_position = lex_position + self.token_value = token_value + self.token_type = token_type.upper() + self.msg = msg + # Whatever catches the ParseError can fill in the full expression + self.expression = None + + def __str__(self): + # self.lex_position +1 to account for the starting double quote char. + underline = ' ' * (self.lex_position + 1) + '^' + return ( + '%s: Parse error at column %s, ' + 'token "%s" (%s), for expression:\n"%s"\n%s' % ( + self.msg, self.lex_position, self.token_value, self.token_type, + self.expression, underline)) + + +@with_str_method +class IncompleteExpressionError(ParseError): + def set_expression(self, expression): + self.expression = expression + self.lex_position = len(expression) + self.token_type = None + self.token_value = None + + def __str__(self): + # self.lex_position +1 to account for the starting double quote char. + underline = ' ' * (self.lex_position + 1) + '^' + return ( + 'Invalid jmespath expression: Incomplete expression:\n' + '"%s"\n%s' % (self.expression, underline)) + + +@with_str_method +class LexerError(ParseError): + def __init__(self, lexer_position, lexer_value, message, expression=None): + self.lexer_position = lexer_position + self.lexer_value = lexer_value + self.message = message + super(LexerError, self).__init__(lexer_position, + lexer_value, + message) + # Whatever catches LexerError can set this. + self.expression = expression + + def __str__(self): + underline = ' ' * self.lexer_position + '^' + return 'Bad jmespath expression: %s:\n%s\n%s' % ( + self.message, self.expression, underline) + + +@with_str_method +class ArityError(ParseError): + def __init__(self, expected, actual, name): + self.expected_arity = expected + self.actual_arity = actual + self.function_name = name + self.expression = None + + def __str__(self): + return ("Expected %s %s for function %s(), " + "received %s" % ( + self.expected_arity, + self._pluralize('argument', self.expected_arity), + self.function_name, + self.actual_arity)) + + def _pluralize(self, word, count): + if count == 1: + return word + else: + return word + 's' + + +@with_str_method +class VariadictArityError(ArityError): + def __str__(self): + return ("Expected at least %s %s for function %s(), " + "received %s" % ( + self.expected_arity, + self._pluralize('argument', self.expected_arity), + self.function_name, + self.actual_arity)) + + +@with_str_method +class JMESPathTypeError(JMESPathError): + def __init__(self, function_name, current_value, actual_type, + expected_types): + self.function_name = function_name + self.current_value = current_value + self.actual_type = actual_type + self.expected_types = expected_types + + def __str__(self): + return ('In function %s(), invalid type for value: %s, ' + 'expected one of: %s, received: "%s"' % ( + self.function_name, self.current_value, + self.expected_types, self.actual_type)) + + +class EmptyExpressionError(JMESPathError): + def __init__(self): + super(EmptyExpressionError, self).__init__( + "Invalid JMESPath expression: cannot be empty.") + + +class UnknownFunctionError(JMESPathError): + pass diff --git a/venv/lib/python3.12/site-packages/jmespath/functions.py b/venv/lib/python3.12/site-packages/jmespath/functions.py new file mode 100644 index 0000000..627b569 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath/functions.py @@ -0,0 +1,362 @@ +import math +import json + +from jmespath import exceptions +from jmespath.compat import string_type as STRING_TYPE +from jmespath.compat import get_methods + + +# python types -> jmespath types +TYPES_MAP = { + 'bool': 'boolean', + 'list': 'array', + 'dict': 'object', + 'NoneType': 'null', + 'unicode': 'string', + 'str': 'string', + 'float': 'number', + 'int': 'number', + 'long': 'number', + 'OrderedDict': 'object', + '_Projection': 'array', + '_Expression': 'expref', +} + + +# jmespath types -> python types +REVERSE_TYPES_MAP = { + 'boolean': ('bool',), + 'array': ('list', '_Projection'), + 'object': ('dict', 'OrderedDict',), + 'null': ('NoneType',), + 'string': ('unicode', 'str'), + 'number': ('float', 'int', 'long'), + 'expref': ('_Expression',), +} + + +def signature(*arguments): + def _record_signature(func): + func.signature = arguments + return func + return _record_signature + + +class FunctionRegistry(type): + def __init__(cls, name, bases, attrs): + cls._populate_function_table() + super(FunctionRegistry, cls).__init__(name, bases, attrs) + + def _populate_function_table(cls): + function_table = {} + # Any method with a @signature decorator that also + # starts with "_func_" is registered as a function. + # _func_max_by -> max_by function. + for name, method in get_methods(cls): + if not name.startswith('_func_'): + continue + signature = getattr(method, 'signature', None) + if signature is not None: + function_table[name[6:]] = { + 'function': method, + 'signature': signature, + } + cls.FUNCTION_TABLE = function_table + + +class Functions(metaclass=FunctionRegistry): + + FUNCTION_TABLE = { + } + + def call_function(self, function_name, resolved_args): + try: + spec = self.FUNCTION_TABLE[function_name] + except KeyError: + raise exceptions.UnknownFunctionError( + "Unknown function: %s()" % function_name) + function = spec['function'] + signature = spec['signature'] + self._validate_arguments(resolved_args, signature, function_name) + return function(self, *resolved_args) + + def _validate_arguments(self, args, signature, function_name): + if signature and signature[-1].get('variadic'): + if len(args) < len(signature): + raise exceptions.VariadictArityError( + len(signature), len(args), function_name) + elif len(args) != len(signature): + raise exceptions.ArityError( + len(signature), len(args), function_name) + return self._type_check(args, signature, function_name) + + def _type_check(self, actual, signature, function_name): + for i in range(len(signature)): + allowed_types = signature[i]['types'] + if allowed_types: + self._type_check_single(actual[i], allowed_types, + function_name) + + def _type_check_single(self, current, types, function_name): + # Type checking involves checking the top level type, + # and in the case of arrays, potentially checking the types + # of each element. + allowed_types, allowed_subtypes = self._get_allowed_pytypes(types) + # We're not using isinstance() on purpose. + # The type model for jmespath does not map + # 1-1 with python types (booleans are considered + # integers in python for example). + actual_typename = type(current).__name__ + if actual_typename not in allowed_types: + raise exceptions.JMESPathTypeError( + function_name, current, + self._convert_to_jmespath_type(actual_typename), types) + # If we're dealing with a list type, we can have + # additional restrictions on the type of the list + # elements (for example a function can require a + # list of numbers or a list of strings). + # Arrays are the only types that can have subtypes. + if allowed_subtypes: + self._subtype_check(current, allowed_subtypes, + types, function_name) + + def _get_allowed_pytypes(self, types): + allowed_types = [] + allowed_subtypes = [] + for t in types: + type_ = t.split('-', 1) + if len(type_) == 2: + type_, subtype = type_ + allowed_subtypes.append(REVERSE_TYPES_MAP[subtype]) + else: + type_ = type_[0] + allowed_types.extend(REVERSE_TYPES_MAP[type_]) + return allowed_types, allowed_subtypes + + def _subtype_check(self, current, allowed_subtypes, types, function_name): + if len(allowed_subtypes) == 1: + # The easy case, we know up front what type + # we need to validate. + allowed_subtypes = allowed_subtypes[0] + for element in current: + actual_typename = type(element).__name__ + if actual_typename not in allowed_subtypes: + raise exceptions.JMESPathTypeError( + function_name, element, actual_typename, types) + elif len(allowed_subtypes) > 1 and current: + # Dynamic type validation. Based on the first + # type we see, we validate that the remaining types + # match. + first = type(current[0]).__name__ + for subtypes in allowed_subtypes: + if first in subtypes: + allowed = subtypes + break + else: + raise exceptions.JMESPathTypeError( + function_name, current[0], first, types) + for element in current: + actual_typename = type(element).__name__ + if actual_typename not in allowed: + raise exceptions.JMESPathTypeError( + function_name, element, actual_typename, types) + + @signature({'types': ['number']}) + def _func_abs(self, arg): + return abs(arg) + + @signature({'types': ['array-number']}) + def _func_avg(self, arg): + if arg: + return sum(arg) / len(arg) + else: + return None + + @signature({'types': [], 'variadic': True}) + def _func_not_null(self, *arguments): + for argument in arguments: + if argument is not None: + return argument + + @signature({'types': []}) + def _func_to_array(self, arg): + if isinstance(arg, list): + return arg + else: + return [arg] + + @signature({'types': []}) + def _func_to_string(self, arg): + if isinstance(arg, STRING_TYPE): + return arg + else: + return json.dumps(arg, separators=(',', ':'), + default=str) + + @signature({'types': []}) + def _func_to_number(self, arg): + if isinstance(arg, (list, dict, bool)): + return None + elif arg is None: + return None + elif isinstance(arg, (int, float)): + return arg + else: + try: + return int(arg) + except ValueError: + try: + return float(arg) + except ValueError: + return None + + @signature({'types': ['array', 'string']}, {'types': []}) + def _func_contains(self, subject, search): + return search in subject + + @signature({'types': ['string', 'array', 'object']}) + def _func_length(self, arg): + return len(arg) + + @signature({'types': ['string']}, {'types': ['string']}) + def _func_ends_with(self, search, suffix): + return search.endswith(suffix) + + @signature({'types': ['string']}, {'types': ['string']}) + def _func_starts_with(self, search, suffix): + return search.startswith(suffix) + + @signature({'types': ['array', 'string']}) + def _func_reverse(self, arg): + if isinstance(arg, STRING_TYPE): + return arg[::-1] + else: + return list(reversed(arg)) + + @signature({"types": ['number']}) + def _func_ceil(self, arg): + return math.ceil(arg) + + @signature({"types": ['number']}) + def _func_floor(self, arg): + return math.floor(arg) + + @signature({"types": ['string']}, {"types": ['array-string']}) + def _func_join(self, separator, array): + return separator.join(array) + + @signature({'types': ['expref']}, {'types': ['array']}) + def _func_map(self, expref, arg): + result = [] + for element in arg: + result.append(expref.visit(expref.expression, element)) + return result + + @signature({"types": ['array-number', 'array-string']}) + def _func_max(self, arg): + if arg: + return max(arg) + else: + return None + + @signature({"types": ["object"], "variadic": True}) + def _func_merge(self, *arguments): + merged = {} + for arg in arguments: + merged.update(arg) + return merged + + @signature({"types": ['array-number', 'array-string']}) + def _func_min(self, arg): + if arg: + return min(arg) + else: + return None + + @signature({"types": ['array-string', 'array-number']}) + def _func_sort(self, arg): + return list(sorted(arg)) + + @signature({"types": ['array-number']}) + def _func_sum(self, arg): + return sum(arg) + + @signature({"types": ['object']}) + def _func_keys(self, arg): + # To be consistent with .values() + # should we also return the indices of a list? + return list(arg.keys()) + + @signature({"types": ['object']}) + def _func_values(self, arg): + return list(arg.values()) + + @signature({'types': []}) + def _func_type(self, arg): + if isinstance(arg, STRING_TYPE): + return "string" + elif isinstance(arg, bool): + return "boolean" + elif isinstance(arg, list): + return "array" + elif isinstance(arg, dict): + return "object" + elif isinstance(arg, (float, int)): + return "number" + elif arg is None: + return "null" + + @signature({'types': ['array']}, {'types': ['expref']}) + def _func_sort_by(self, array, expref): + if not array: + return array + # sort_by allows for the expref to be either a number of + # a string, so we have some special logic to handle this. + # We evaluate the first array element and verify that it's + # either a string of a number. We then create a key function + # that validates that type, which requires that remaining array + # elements resolve to the same type as the first element. + required_type = self._convert_to_jmespath_type( + type(expref.visit(expref.expression, array[0])).__name__) + if required_type not in ['number', 'string']: + raise exceptions.JMESPathTypeError( + 'sort_by', array[0], required_type, ['string', 'number']) + keyfunc = self._create_key_func(expref, + [required_type], + 'sort_by') + return list(sorted(array, key=keyfunc)) + + @signature({'types': ['array']}, {'types': ['expref']}) + def _func_min_by(self, array, expref): + keyfunc = self._create_key_func(expref, + ['number', 'string'], + 'min_by') + if array: + return min(array, key=keyfunc) + else: + return None + + @signature({'types': ['array']}, {'types': ['expref']}) + def _func_max_by(self, array, expref): + keyfunc = self._create_key_func(expref, + ['number', 'string'], + 'max_by') + if array: + return max(array, key=keyfunc) + else: + return None + + def _create_key_func(self, expref, allowed_types, function_name): + def keyfunc(x): + result = expref.visit(expref.expression, x) + actual_typename = type(result).__name__ + jmespath_type = self._convert_to_jmespath_type(actual_typename) + # allowed_types is in term of jmespath types, not python types. + if jmespath_type not in allowed_types: + raise exceptions.JMESPathTypeError( + function_name, result, jmespath_type, allowed_types) + return result + return keyfunc + + def _convert_to_jmespath_type(self, pyobject): + return TYPES_MAP.get(pyobject, 'unknown') diff --git a/venv/lib/python3.12/site-packages/jmespath/lexer.py b/venv/lib/python3.12/site-packages/jmespath/lexer.py new file mode 100644 index 0000000..8db05e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath/lexer.py @@ -0,0 +1,208 @@ +import string +import warnings +from json import loads + +from jmespath.exceptions import LexerError, EmptyExpressionError + + +class Lexer(object): + START_IDENTIFIER = set(string.ascii_letters + '_') + VALID_IDENTIFIER = set(string.ascii_letters + string.digits + '_') + VALID_NUMBER = set(string.digits) + WHITESPACE = set(" \t\n\r") + SIMPLE_TOKENS = { + '.': 'dot', + '*': 'star', + ']': 'rbracket', + ',': 'comma', + ':': 'colon', + '@': 'current', + '(': 'lparen', + ')': 'rparen', + '{': 'lbrace', + '}': 'rbrace', + } + + def tokenize(self, expression): + self._initialize_for_expression(expression) + while self._current is not None: + if self._current in self.SIMPLE_TOKENS: + yield {'type': self.SIMPLE_TOKENS[self._current], + 'value': self._current, + 'start': self._position, 'end': self._position + 1} + self._next() + elif self._current in self.START_IDENTIFIER: + start = self._position + buff = self._current + while self._next() in self.VALID_IDENTIFIER: + buff += self._current + yield {'type': 'unquoted_identifier', 'value': buff, + 'start': start, 'end': start + len(buff)} + elif self._current in self.WHITESPACE: + self._next() + elif self._current == '[': + start = self._position + next_char = self._next() + if next_char == ']': + self._next() + yield {'type': 'flatten', 'value': '[]', + 'start': start, 'end': start + 2} + elif next_char == '?': + self._next() + yield {'type': 'filter', 'value': '[?', + 'start': start, 'end': start + 2} + else: + yield {'type': 'lbracket', 'value': '[', + 'start': start, 'end': start + 1} + elif self._current == "'": + yield self._consume_raw_string_literal() + elif self._current == '|': + yield self._match_or_else('|', 'or', 'pipe') + elif self._current == '&': + yield self._match_or_else('&', 'and', 'expref') + elif self._current == '`': + yield self._consume_literal() + elif self._current in self.VALID_NUMBER: + start = self._position + buff = self._consume_number() + yield {'type': 'number', 'value': int(buff), + 'start': start, 'end': start + len(buff)} + elif self._current == '-': + # Negative number. + start = self._position + buff = self._consume_number() + if len(buff) > 1: + yield {'type': 'number', 'value': int(buff), + 'start': start, 'end': start + len(buff)} + else: + raise LexerError(lexer_position=start, + lexer_value=buff, + message="Unknown token '%s'" % buff) + elif self._current == '"': + yield self._consume_quoted_identifier() + elif self._current == '<': + yield self._match_or_else('=', 'lte', 'lt') + elif self._current == '>': + yield self._match_or_else('=', 'gte', 'gt') + elif self._current == '!': + yield self._match_or_else('=', 'ne', 'not') + elif self._current == '=': + if self._next() == '=': + yield {'type': 'eq', 'value': '==', + 'start': self._position - 1, 'end': self._position} + self._next() + else: + if self._current is None: + # If we're at the EOF, we never advanced + # the position so we don't need to rewind + # it back one location. + position = self._position + else: + position = self._position - 1 + raise LexerError( + lexer_position=position, + lexer_value='=', + message="Unknown token '='") + else: + raise LexerError(lexer_position=self._position, + lexer_value=self._current, + message="Unknown token %s" % self._current) + yield {'type': 'eof', 'value': '', + 'start': self._length, 'end': self._length} + + def _consume_number(self): + start = self._position + buff = self._current + while self._next() in self.VALID_NUMBER: + buff += self._current + return buff + + def _initialize_for_expression(self, expression): + if not expression: + raise EmptyExpressionError() + self._position = 0 + self._expression = expression + self._chars = list(self._expression) + self._current = self._chars[self._position] + self._length = len(self._expression) + + def _next(self): + if self._position == self._length - 1: + self._current = None + else: + self._position += 1 + self._current = self._chars[self._position] + return self._current + + def _consume_until(self, delimiter): + # Consume until the delimiter is reached, + # allowing for the delimiter to be escaped with "\". + start = self._position + buff = '' + self._next() + while self._current != delimiter: + if self._current == '\\': + buff += '\\' + self._next() + if self._current is None: + # We're at the EOF. + raise LexerError(lexer_position=start, + lexer_value=self._expression[start:], + message="Unclosed %s delimiter" % delimiter) + buff += self._current + self._next() + # Skip the closing delimiter. + self._next() + return buff + + def _consume_literal(self): + start = self._position + lexeme = self._consume_until('`').replace('\\`', '`') + try: + # Assume it is valid JSON and attempt to parse. + parsed_json = loads(lexeme) + except ValueError: + try: + # Invalid JSON values should be converted to quoted + # JSON strings during the JEP-12 deprecation period. + parsed_json = loads('"%s"' % lexeme.lstrip()) + warnings.warn("deprecated string literal syntax", + PendingDeprecationWarning) + except ValueError: + raise LexerError(lexer_position=start, + lexer_value=self._expression[start:], + message="Bad token %s" % lexeme) + token_len = self._position - start + return {'type': 'literal', 'value': parsed_json, + 'start': start, 'end': token_len} + + def _consume_quoted_identifier(self): + start = self._position + lexeme = '"' + self._consume_until('"') + '"' + try: + token_len = self._position - start + return {'type': 'quoted_identifier', 'value': loads(lexeme), + 'start': start, 'end': token_len} + except ValueError as e: + error_message = str(e).split(':')[0] + raise LexerError(lexer_position=start, + lexer_value=lexeme, + message=error_message) + + def _consume_raw_string_literal(self): + start = self._position + lexeme = self._consume_until("'").replace("\\'", "'") + token_len = self._position - start + return {'type': 'literal', 'value': lexeme, + 'start': start, 'end': token_len} + + def _match_or_else(self, expected, match_type, else_type): + start = self._position + current = self._current + next_char = self._next() + if next_char == expected: + self._next() + return {'type': match_type, 'value': current + next_char, + 'start': start, 'end': start + 1} + return {'type': else_type, 'value': current, + 'start': start, 'end': start} diff --git a/venv/lib/python3.12/site-packages/jmespath/parser.py b/venv/lib/python3.12/site-packages/jmespath/parser.py new file mode 100644 index 0000000..cc8e804 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath/parser.py @@ -0,0 +1,534 @@ +"""Top down operator precedence parser. + +This is an implementation of Vaughan R. Pratt's +"Top Down Operator Precedence" parser. +(http://dl.acm.org/citation.cfm?doid=512927.512931). + +These are some additional resources that help explain the +general idea behind a Pratt parser: + +* http://effbot.org/zone/simple-top-down-parsing.htm +* http://javascript.crockford.com/tdop/tdop.html + +A few notes on the implementation. + +* All the nud/led tokens are on the Parser class itself, and are dispatched + using getattr(). This keeps all the parsing logic contained to a single + class. +* We use two passes through the data. One to create a list of token, + then one pass through the tokens to create the AST. While the lexer actually + yields tokens, we convert it to a list so we can easily implement two tokens + of lookahead. A previous implementation used a fixed circular buffer, but it + was significantly slower. Also, the average jmespath expression typically + does not have a large amount of token so this is not an issue. And + interestingly enough, creating a token list first is actually faster than + consuming from the token iterator one token at a time. + +""" +from jmespath import lexer +from jmespath.compat import with_repr_method +from jmespath import ast +from jmespath import exceptions +from jmespath import visitor + + +class Parser(object): + BINDING_POWER = { + 'eof': 0, + 'unquoted_identifier': 0, + 'quoted_identifier': 0, + 'literal': 0, + 'rbracket': 0, + 'rparen': 0, + 'comma': 0, + 'rbrace': 0, + 'number': 0, + 'current': 0, + 'expref': 0, + 'colon': 0, + 'pipe': 1, + 'or': 2, + 'and': 3, + 'eq': 5, + 'gt': 5, + 'lt': 5, + 'gte': 5, + 'lte': 5, + 'ne': 5, + 'flatten': 9, + # Everything above stops a projection. + 'star': 20, + 'filter': 21, + 'dot': 40, + 'not': 45, + 'lbrace': 50, + 'lbracket': 55, + 'lparen': 60, + } + # The maximum binding power for a token that can stop + # a projection. + _PROJECTION_STOP = 10 + # The _MAX_SIZE most recent expressions are cached in + # _CACHE dict. + _CACHE = {} + _MAX_SIZE = 512 + + def __init__(self, lookahead=2): + self.tokenizer = None + self._tokens = [None] * lookahead + self._buffer_size = lookahead + self._index = 0 + + def parse(self, expression): + try: + return self._CACHE[expression] + except KeyError: + pass + parsed_result = self._do_parse(expression) + if len(self._CACHE) >= self._MAX_SIZE: + try: + del self._CACHE[next(iter(self._CACHE))] + except (KeyError, StopIteration, RuntimeError): + # KeyError - Another thread else already deleted the key. + # RuntimeError - Another modified the cache. + # StopIteration - (Unlikely) Cache is empty. + # + # If we encounter an error we should NOT be adding to the + # cache. To ensure we do not exceed self._MAX_SIZE, we + # can only add to the cache if we successfully removed + # an element from the cache, otherwise this can grow + # unbounded. + return parsed_result + self._CACHE[expression] = parsed_result + return parsed_result + + def _do_parse(self, expression): + try: + return self._parse(expression) + except exceptions.LexerError as e: + e.expression = expression + raise + except exceptions.IncompleteExpressionError as e: + e.set_expression(expression) + raise + except exceptions.ParseError as e: + e.expression = expression + raise + + def _parse(self, expression): + self.tokenizer = lexer.Lexer().tokenize(expression) + self._tokens = list(self.tokenizer) + self._index = 0 + parsed = self._expression(binding_power=0) + if not self._current_token() == 'eof': + t = self._lookahead_token(0) + raise exceptions.ParseError(t['start'], t['value'], t['type'], + "Unexpected token: %s" % t['value']) + return ParsedResult(expression, parsed) + + def _expression(self, binding_power=0): + left_token = self._lookahead_token(0) + self._advance() + nud_function = getattr( + self, '_token_nud_%s' % left_token['type'], + self._error_nud_token) + left = nud_function(left_token) + current_token = self._current_token() + while binding_power < self.BINDING_POWER[current_token]: + led = getattr(self, '_token_led_%s' % current_token, None) + if led is None: + error_token = self._lookahead_token(0) + self._error_led_token(error_token) + else: + self._advance() + left = led(left) + current_token = self._current_token() + return left + + def _token_nud_literal(self, token): + return ast.literal(token['value']) + + def _token_nud_unquoted_identifier(self, token): + return ast.field(token['value']) + + def _token_nud_quoted_identifier(self, token): + field = ast.field(token['value']) + # You can't have a quoted identifier as a function + # name. + if self._current_token() == 'lparen': + t = self._lookahead_token(0) + raise exceptions.ParseError( + 0, t['value'], t['type'], + 'Quoted identifier not allowed for function names.') + return field + + def _token_nud_star(self, token): + left = ast.identity() + if self._current_token() == 'rbracket': + right = ast.identity() + else: + right = self._parse_projection_rhs(self.BINDING_POWER['star']) + return ast.value_projection(left, right) + + def _token_nud_filter(self, token): + return self._token_led_filter(ast.identity()) + + def _token_nud_lbrace(self, token): + return self._parse_multi_select_hash() + + def _token_nud_lparen(self, token): + expression = self._expression() + self._match('rparen') + return expression + + def _token_nud_flatten(self, token): + left = ast.flatten(ast.identity()) + right = self._parse_projection_rhs( + self.BINDING_POWER['flatten']) + return ast.projection(left, right) + + def _token_nud_not(self, token): + expr = self._expression(self.BINDING_POWER['not']) + return ast.not_expression(expr) + + def _token_nud_lbracket(self, token): + if self._current_token() in ['number', 'colon']: + right = self._parse_index_expression() + # We could optimize this and remove the identity() node. + # We don't really need an index_expression node, we can + # just use emit an index node here if we're not dealing + # with a slice. + return self._project_if_slice(ast.identity(), right) + elif self._current_token() == 'star' and \ + self._lookahead(1) == 'rbracket': + self._advance() + self._advance() + right = self._parse_projection_rhs(self.BINDING_POWER['star']) + return ast.projection(ast.identity(), right) + else: + return self._parse_multi_select_list() + + def _parse_index_expression(self): + # We're here: + # [ + # ^ + # | current token + if (self._lookahead(0) == 'colon' or + self._lookahead(1) == 'colon'): + return self._parse_slice_expression() + else: + # Parse the syntax [number] + node = ast.index(self._lookahead_token(0)['value']) + self._advance() + self._match('rbracket') + return node + + def _parse_slice_expression(self): + # [start:end:step] + # Where start, end, and step are optional. + # The last colon is optional as well. + parts = [None, None, None] + index = 0 + current_token = self._current_token() + while not current_token == 'rbracket' and index < 3: + if current_token == 'colon': + index += 1 + if index == 3: + self._raise_parse_error_for_token( + self._lookahead_token(0), 'syntax error') + self._advance() + elif current_token == 'number': + parts[index] = self._lookahead_token(0)['value'] + self._advance() + else: + self._raise_parse_error_for_token( + self._lookahead_token(0), 'syntax error') + current_token = self._current_token() + self._match('rbracket') + return ast.slice(*parts) + + def _token_nud_current(self, token): + return ast.current_node() + + def _token_nud_expref(self, token): + expression = self._expression(self.BINDING_POWER['expref']) + return ast.expref(expression) + + def _token_led_dot(self, left): + if not self._current_token() == 'star': + right = self._parse_dot_rhs(self.BINDING_POWER['dot']) + if left['type'] == 'subexpression': + left['children'].append(right) + return left + else: + return ast.subexpression([left, right]) + else: + # We're creating a projection. + self._advance() + right = self._parse_projection_rhs( + self.BINDING_POWER['dot']) + return ast.value_projection(left, right) + + def _token_led_pipe(self, left): + right = self._expression(self.BINDING_POWER['pipe']) + return ast.pipe(left, right) + + def _token_led_or(self, left): + right = self._expression(self.BINDING_POWER['or']) + return ast.or_expression(left, right) + + def _token_led_and(self, left): + right = self._expression(self.BINDING_POWER['and']) + return ast.and_expression(left, right) + + def _token_led_lparen(self, left): + if left['type'] != 'field': + # 0 - first func arg or closing paren. + # -1 - '(' token + # -2 - invalid function "name". + prev_t = self._lookahead_token(-2) + raise exceptions.ParseError( + prev_t['start'], prev_t['value'], prev_t['type'], + "Invalid function name '%s'" % prev_t['value']) + name = left['value'] + args = [] + while not self._current_token() == 'rparen': + expression = self._expression() + if self._current_token() == 'comma': + self._match('comma') + args.append(expression) + self._match('rparen') + function_node = ast.function_expression(name, args) + return function_node + + def _token_led_filter(self, left): + # Filters are projections. + condition = self._expression(0) + self._match('rbracket') + if self._current_token() == 'flatten': + right = ast.identity() + else: + right = self._parse_projection_rhs(self.BINDING_POWER['filter']) + return ast.filter_projection(left, right, condition) + + def _token_led_eq(self, left): + return self._parse_comparator(left, 'eq') + + def _token_led_ne(self, left): + return self._parse_comparator(left, 'ne') + + def _token_led_gt(self, left): + return self._parse_comparator(left, 'gt') + + def _token_led_gte(self, left): + return self._parse_comparator(left, 'gte') + + def _token_led_lt(self, left): + return self._parse_comparator(left, 'lt') + + def _token_led_lte(self, left): + return self._parse_comparator(left, 'lte') + + def _token_led_flatten(self, left): + left = ast.flatten(left) + right = self._parse_projection_rhs( + self.BINDING_POWER['flatten']) + return ast.projection(left, right) + + def _token_led_lbracket(self, left): + token = self._lookahead_token(0) + if token['type'] in ['number', 'colon']: + right = self._parse_index_expression() + if left['type'] == 'index_expression': + # Optimization: if the left node is an index expr, + # we can avoid creating another node and instead just add + # the right node as a child of the left. + left['children'].append(right) + return left + else: + return self._project_if_slice(left, right) + else: + # We have a projection + self._match('star') + self._match('rbracket') + right = self._parse_projection_rhs(self.BINDING_POWER['star']) + return ast.projection(left, right) + + def _project_if_slice(self, left, right): + index_expr = ast.index_expression([left, right]) + if right['type'] == 'slice': + return ast.projection( + index_expr, + self._parse_projection_rhs(self.BINDING_POWER['star'])) + else: + return index_expr + + def _parse_comparator(self, left, comparator): + right = self._expression(self.BINDING_POWER[comparator]) + return ast.comparator(comparator, left, right) + + def _parse_multi_select_list(self): + expressions = [] + while True: + expression = self._expression() + expressions.append(expression) + if self._current_token() == 'rbracket': + break + else: + self._match('comma') + self._match('rbracket') + return ast.multi_select_list(expressions) + + def _parse_multi_select_hash(self): + pairs = [] + while True: + key_token = self._lookahead_token(0) + # Before getting the token value, verify it's + # an identifier. + self._match_multiple_tokens( + token_types=['quoted_identifier', 'unquoted_identifier']) + key_name = key_token['value'] + self._match('colon') + value = self._expression(0) + node = ast.key_val_pair(key_name=key_name, node=value) + pairs.append(node) + if self._current_token() == 'comma': + self._match('comma') + elif self._current_token() == 'rbrace': + self._match('rbrace') + break + return ast.multi_select_dict(nodes=pairs) + + def _parse_projection_rhs(self, binding_power): + # Parse the right hand side of the projection. + if self.BINDING_POWER[self._current_token()] < self._PROJECTION_STOP: + # BP of 10 are all the tokens that stop a projection. + right = ast.identity() + elif self._current_token() == 'lbracket': + right = self._expression(binding_power) + elif self._current_token() == 'filter': + right = self._expression(binding_power) + elif self._current_token() == 'dot': + self._match('dot') + right = self._parse_dot_rhs(binding_power) + else: + self._raise_parse_error_for_token(self._lookahead_token(0), + 'syntax error') + return right + + def _parse_dot_rhs(self, binding_power): + # From the grammar: + # expression '.' ( identifier / + # multi-select-list / + # multi-select-hash / + # function-expression / + # * + # In terms of tokens that means that after a '.', + # you can have: + lookahead = self._current_token() + # Common case "foo.bar", so first check for an identifier. + if lookahead in ['quoted_identifier', 'unquoted_identifier', 'star']: + return self._expression(binding_power) + elif lookahead == 'lbracket': + self._match('lbracket') + return self._parse_multi_select_list() + elif lookahead == 'lbrace': + self._match('lbrace') + return self._parse_multi_select_hash() + else: + t = self._lookahead_token(0) + allowed = ['quoted_identifier', 'unquoted_identifier', + 'lbracket', 'lbrace'] + msg = ( + "Expecting: %s, got: %s" % (allowed, t['type']) + ) + self._raise_parse_error_for_token(t, msg) + + def _error_nud_token(self, token): + if token['type'] == 'eof': + raise exceptions.IncompleteExpressionError( + token['start'], token['value'], token['type']) + self._raise_parse_error_for_token(token, 'invalid token') + + def _error_led_token(self, token): + self._raise_parse_error_for_token(token, 'invalid token') + + def _match(self, token_type=None): + # inline'd self._current_token() + if self._current_token() == token_type: + # inline'd self._advance() + self._advance() + else: + self._raise_parse_error_maybe_eof( + token_type, self._lookahead_token(0)) + + def _match_multiple_tokens(self, token_types): + if self._current_token() not in token_types: + self._raise_parse_error_maybe_eof( + token_types, self._lookahead_token(0)) + self._advance() + + def _advance(self): + self._index += 1 + + def _current_token(self): + return self._tokens[self._index]['type'] + + def _lookahead(self, number): + return self._tokens[self._index + number]['type'] + + def _lookahead_token(self, number): + return self._tokens[self._index + number] + + def _raise_parse_error_for_token(self, token, reason): + lex_position = token['start'] + actual_value = token['value'] + actual_type = token['type'] + raise exceptions.ParseError(lex_position, actual_value, + actual_type, reason) + + def _raise_parse_error_maybe_eof(self, expected_type, token): + lex_position = token['start'] + actual_value = token['value'] + actual_type = token['type'] + if actual_type == 'eof': + raise exceptions.IncompleteExpressionError( + lex_position, actual_value, actual_type) + message = 'Expecting: %s, got: %s' % (expected_type, + actual_type) + raise exceptions.ParseError( + lex_position, actual_value, actual_type, message) + + @classmethod + def purge(cls): + """Clear the expression compilation cache.""" + cls._CACHE.clear() + + +@with_repr_method +class ParsedResult(object): + def __init__(self, expression, parsed): + self.expression = expression + self.parsed = parsed + + def search(self, value, options=None): + interpreter = visitor.TreeInterpreter(options) + result = interpreter.visit(self.parsed, value) + return result + + def _render_dot_file(self): + """Render the parsed AST as a dot file. + + Note that this is marked as an internal method because + the AST is an implementation detail and is subject + to change. This method can be used to help troubleshoot + or for development purposes, but is not considered part + of the public supported API. Use at your own risk. + + """ + renderer = visitor.GraphvizVisitor() + contents = renderer.visit(self.parsed) + return contents + + def __repr__(self): + return repr(self.parsed) diff --git a/venv/lib/python3.12/site-packages/jmespath/visitor.py b/venv/lib/python3.12/site-packages/jmespath/visitor.py new file mode 100644 index 0000000..15fb177 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jmespath/visitor.py @@ -0,0 +1,328 @@ +import operator + +from jmespath import functions +from jmespath.compat import string_type +from numbers import Number + + +def _equals(x, y): + if _is_special_number_case(x, y): + return False + else: + return x == y + + +def _is_special_number_case(x, y): + # We need to special case comparing 0 or 1 to + # True/False. While normally comparing any + # integer other than 0/1 to True/False will always + # return False. However 0/1 have this: + # >>> 0 == True + # False + # >>> 0 == False + # True + # >>> 1 == True + # True + # >>> 1 == False + # False + # + # Also need to consider that: + # >>> 0 in [True, False] + # True + if _is_actual_number(x) and x in (0, 1): + return isinstance(y, bool) + elif _is_actual_number(y) and y in (0, 1): + return isinstance(x, bool) + + +def _is_comparable(x): + # The spec doesn't officially support string types yet, + # but enough people are relying on this behavior that + # it's been added back. This should eventually become + # part of the official spec. + return _is_actual_number(x) or isinstance(x, string_type) + + +def _is_actual_number(x): + # We need to handle python's quirkiness with booleans, + # specifically: + # + # >>> isinstance(False, int) + # True + # >>> isinstance(True, int) + # True + if isinstance(x, bool): + return False + return isinstance(x, Number) + + +class Options(object): + """Options to control how a JMESPath function is evaluated.""" + def __init__(self, dict_cls=None, custom_functions=None): + #: The class to use when creating a dict. The interpreter + # may create dictionaries during the evaluation of a JMESPath + # expression. For example, a multi-select hash will + # create a dictionary. By default we use a dict() type. + # You can set this value to change what dict type is used. + # The most common reason you would change this is if you + # want to set a collections.OrderedDict so that you can + # have predictable key ordering. + self.dict_cls = dict_cls + self.custom_functions = custom_functions + + +class _Expression(object): + def __init__(self, expression, interpreter): + self.expression = expression + self.interpreter = interpreter + + def visit(self, node, *args, **kwargs): + return self.interpreter.visit(node, *args, **kwargs) + + +class Visitor(object): + def __init__(self): + self._method_cache = {} + + def visit(self, node, *args, **kwargs): + node_type = node['type'] + method = self._method_cache.get(node_type) + if method is None: + method = getattr( + self, 'visit_%s' % node['type'], self.default_visit) + self._method_cache[node_type] = method + return method(node, *args, **kwargs) + + def default_visit(self, node, *args, **kwargs): + raise NotImplementedError("default_visit") + + +class TreeInterpreter(Visitor): + COMPARATOR_FUNC = { + 'eq': _equals, + 'ne': lambda x, y: not _equals(x, y), + 'lt': operator.lt, + 'gt': operator.gt, + 'lte': operator.le, + 'gte': operator.ge + } + _EQUALITY_OPS = ['eq', 'ne'] + MAP_TYPE = dict + + def __init__(self, options=None): + super(TreeInterpreter, self).__init__() + self._dict_cls = self.MAP_TYPE + if options is None: + options = Options() + self._options = options + if options.dict_cls is not None: + self._dict_cls = self._options.dict_cls + if options.custom_functions is not None: + self._functions = self._options.custom_functions + else: + self._functions = functions.Functions() + + def default_visit(self, node, *args, **kwargs): + raise NotImplementedError(node['type']) + + def visit_subexpression(self, node, value): + result = value + for node in node['children']: + result = self.visit(node, result) + return result + + def visit_field(self, node, value): + try: + return value.get(node['value']) + except AttributeError: + return None + + def visit_comparator(self, node, value): + # Common case: comparator is == or != + comparator_func = self.COMPARATOR_FUNC[node['value']] + if node['value'] in self._EQUALITY_OPS: + return comparator_func( + self.visit(node['children'][0], value), + self.visit(node['children'][1], value) + ) + else: + # Ordering operators are only valid for numbers. + # Evaluating any other type with a comparison operator + # will yield a None value. + left = self.visit(node['children'][0], value) + right = self.visit(node['children'][1], value) + num_types = (int, float) + if not (_is_comparable(left) and + _is_comparable(right)): + return None + return comparator_func(left, right) + + def visit_current(self, node, value): + return value + + def visit_expref(self, node, value): + return _Expression(node['children'][0], self) + + def visit_function_expression(self, node, value): + resolved_args = [] + for child in node['children']: + current = self.visit(child, value) + resolved_args.append(current) + return self._functions.call_function(node['value'], resolved_args) + + def visit_filter_projection(self, node, value): + base = self.visit(node['children'][0], value) + if not isinstance(base, list): + return None + comparator_node = node['children'][2] + collected = [] + for element in base: + if self._is_true(self.visit(comparator_node, element)): + current = self.visit(node['children'][1], element) + if current is not None: + collected.append(current) + return collected + + def visit_flatten(self, node, value): + base = self.visit(node['children'][0], value) + if not isinstance(base, list): + # Can't flatten the object if it's not a list. + return None + merged_list = [] + for element in base: + if isinstance(element, list): + merged_list.extend(element) + else: + merged_list.append(element) + return merged_list + + def visit_identity(self, node, value): + return value + + def visit_index(self, node, value): + # Even though we can index strings, we don't + # want to support that. + if not isinstance(value, list): + return None + try: + return value[node['value']] + except IndexError: + return None + + def visit_index_expression(self, node, value): + result = value + for node in node['children']: + result = self.visit(node, result) + return result + + def visit_slice(self, node, value): + if not isinstance(value, list): + return None + s = slice(*node['children']) + return value[s] + + def visit_key_val_pair(self, node, value): + return self.visit(node['children'][0], value) + + def visit_literal(self, node, value): + return node['value'] + + def visit_multi_select_dict(self, node, value): + if value is None: + return None + collected = self._dict_cls() + for child in node['children']: + collected[child['value']] = self.visit(child, value) + return collected + + def visit_multi_select_list(self, node, value): + if value is None: + return None + collected = [] + for child in node['children']: + collected.append(self.visit(child, value)) + return collected + + def visit_or_expression(self, node, value): + matched = self.visit(node['children'][0], value) + if self._is_false(matched): + matched = self.visit(node['children'][1], value) + return matched + + def visit_and_expression(self, node, value): + matched = self.visit(node['children'][0], value) + if self._is_false(matched): + return matched + return self.visit(node['children'][1], value) + + def visit_not_expression(self, node, value): + original_result = self.visit(node['children'][0], value) + if _is_actual_number(original_result) and original_result == 0: + # Special case for 0, !0 should be false, not true. + # 0 is not a special cased integer in jmespath. + return False + return not original_result + + def visit_pipe(self, node, value): + result = value + for node in node['children']: + result = self.visit(node, result) + return result + + def visit_projection(self, node, value): + base = self.visit(node['children'][0], value) + if not isinstance(base, list): + return None + collected = [] + for element in base: + current = self.visit(node['children'][1], element) + if current is not None: + collected.append(current) + return collected + + def visit_value_projection(self, node, value): + base = self.visit(node['children'][0], value) + try: + base = base.values() + except AttributeError: + return None + collected = [] + for element in base: + current = self.visit(node['children'][1], element) + if current is not None: + collected.append(current) + return collected + + def _is_false(self, value): + # This looks weird, but we're explicitly using equality checks + # because the truth/false values are different between + # python and jmespath. + return (value == '' or value == [] or value == {} or value is None or + value is False) + + def _is_true(self, value): + return not self._is_false(value) + + +class GraphvizVisitor(Visitor): + def __init__(self): + super(GraphvizVisitor, self).__init__() + self._lines = [] + self._count = 1 + + def visit(self, node, *args, **kwargs): + self._lines.append('digraph AST {') + current = '%s%s' % (node['type'], self._count) + self._count += 1 + self._visit(node, current) + self._lines.append('}') + return '\n'.join(self._lines) + + def _visit(self, node, current): + self._lines.append('%s [label="%s(%s)"]' % ( + current, node['type'], node.get('value', ''))) + for child in node.get('children', []): + child_name = '%s%s' % (child['type'], self._count) + self._count += 1 + self._lines.append(' %s -> %s' % (current, child_name)) + self._visit(child, child_name) diff --git a/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/METADATA new file mode 100644 index 0000000..ba828d2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/METADATA @@ -0,0 +1,170 @@ +Metadata-Version: 2.4 +Name: jsonschema +Version: 4.26.0 +Summary: An implementation of JSON Schema validation for Python +Project-URL: Homepage, https://github.com/python-jsonschema/jsonschema +Project-URL: Documentation, https://python-jsonschema.readthedocs.io/ +Project-URL: Issues, https://github.com/python-jsonschema/jsonschema/issues/ +Project-URL: Funding, https://github.com/sponsors/Julian +Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-jsonschema?utm_source=pypi-jsonschema&utm_medium=referral&utm_campaign=pypi-link +Project-URL: Changelog, https://github.com/python-jsonschema/jsonschema/blob/main/CHANGELOG.rst +Project-URL: Source, https://github.com/python-jsonschema/jsonschema +Author-email: Julian Berman +License-Expression: MIT +License-File: COPYING +Keywords: data validation,json,json schema,jsonschema,validation +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: File Formats :: JSON +Classifier: Topic :: File Formats :: JSON :: JSON Schema +Requires-Python: >=3.10 +Requires-Dist: attrs>=22.2.0 +Requires-Dist: jsonschema-specifications>=2023.03.6 +Requires-Dist: referencing>=0.28.4 +Requires-Dist: rpds-py>=0.25.0 +Provides-Extra: format +Requires-Dist: fqdn; extra == 'format' +Requires-Dist: idna; extra == 'format' +Requires-Dist: isoduration; extra == 'format' +Requires-Dist: jsonpointer>1.13; extra == 'format' +Requires-Dist: rfc3339-validator; extra == 'format' +Requires-Dist: rfc3987; extra == 'format' +Requires-Dist: uri-template; extra == 'format' +Requires-Dist: webcolors>=1.11; extra == 'format' +Provides-Extra: format-nongpl +Requires-Dist: fqdn; extra == 'format-nongpl' +Requires-Dist: idna; extra == 'format-nongpl' +Requires-Dist: isoduration; extra == 'format-nongpl' +Requires-Dist: jsonpointer>1.13; extra == 'format-nongpl' +Requires-Dist: rfc3339-validator; extra == 'format-nongpl' +Requires-Dist: rfc3986-validator>0.1.0; extra == 'format-nongpl' +Requires-Dist: rfc3987-syntax>=1.1.0; extra == 'format-nongpl' +Requires-Dist: uri-template; extra == 'format-nongpl' +Requires-Dist: webcolors>=24.6.0; extra == 'format-nongpl' +Description-Content-Type: text/x-rst + +========== +jsonschema +========== + +|PyPI| |Pythons| |CI| |ReadTheDocs| |Precommit| |Zenodo| + +.. |PyPI| image:: https://img.shields.io/pypi/v/jsonschema.svg + :alt: PyPI version + :target: https://pypi.org/project/jsonschema/ + +.. |Pythons| image:: https://img.shields.io/pypi/pyversions/jsonschema.svg + :alt: Supported Python versions + :target: https://pypi.org/project/jsonschema/ + +.. |CI| image:: https://github.com/python-jsonschema/jsonschema/workflows/CI/badge.svg + :alt: Build status + :target: https://github.com/python-jsonschema/jsonschema/actions?query=workflow%3ACI + +.. |ReadTheDocs| image:: https://readthedocs.org/projects/python-jsonschema/badge/?version=stable&style=flat + :alt: ReadTheDocs status + :target: https://python-jsonschema.readthedocs.io/en/stable/ + +.. |Precommit| image:: https://results.pre-commit.ci/badge/github/python-jsonschema/jsonschema/main.svg + :alt: pre-commit.ci status + :target: https://results.pre-commit.ci/latest/github/python-jsonschema/jsonschema/main + +.. |Zenodo| image:: https://zenodo.org/badge/3072629.svg + :alt: Zenodo DOI + :target: https://zenodo.org/badge/latestdoi/3072629 + + +``jsonschema`` is an implementation of the `JSON Schema `_ specification for Python. + +.. code:: python + + >>> from jsonschema import validate + + >>> # A sample schema, like what we'd get from json.load() + >>> schema = { + ... "type" : "object", + ... "properties" : { + ... "price" : {"type" : "number"}, + ... "name" : {"type" : "string"}, + ... }, + ... } + + >>> # If no exception is raised by validate(), the instance is valid. + >>> validate(instance={"name" : "Eggs", "price" : 34.99}, schema=schema) + + >>> validate( + ... instance={"name" : "Eggs", "price" : "Invalid"}, schema=schema, + ... ) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValidationError: 'Invalid' is not of type 'number' + +It can also be used from the command line by installing `check-jsonschema `_. + +Features +-------- + +* Full support for `Draft 2020-12 `_, `Draft 2019-09 `_, `Draft 7 `_, `Draft 6 `_, `Draft 4 `_ and `Draft 3 `_ + +* `Lazy validation `_ that can iteratively report *all* validation errors. + +* `Programmatic querying `_ of which properties or items failed validation. + + +Installation +------------ + +``jsonschema`` is available on `PyPI `_. You can install using `pip `_: + +.. code:: bash + + $ pip install jsonschema + + +Extras +====== + +Two extras are available when installing the package, both currently related to ``format`` validation: + + * ``format`` + * ``format-nongpl`` + +They can be used when installing in order to include additional dependencies, e.g.: + +.. code:: bash + + $ pip install jsonschema'[format]' + +Be aware that the mere presence of these dependencies – or even the specification of ``format`` checks in a schema – do *not* activate format checks (as per the specification). +Please read the `format validation documentation `_ for further details. + +About +----- + +I'm Julian Berman. + +``jsonschema`` is on `GitHub `_. + +Get in touch, via GitHub or otherwise, if you've got something to contribute, it'd be most welcome! + +If you feel overwhelmingly grateful, you can also `sponsor me `_. + +And for companies who appreciate ``jsonschema`` and its continued support and growth, ``jsonschema`` is also now supportable via `TideLift `_. + + +Release Information +------------------- + +v4.26.0 +======= + +* Decrease import time by delaying importing of ``urllib.request`` (#1416). diff --git a/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/RECORD new file mode 100644 index 0000000..9353494 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/RECORD @@ -0,0 +1,82 @@ +../../../bin/jsonschema,sha256=MTxTC3xlxOm5IfHm30o7BRDoGLjfOe86HPExLSvTrv0,186 +jsonschema-4.26.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +jsonschema-4.26.0.dist-info/METADATA,sha256=BbGJhmypipbaOTS_a4NofO0_-FMlJzt34WtwX22LJtI,7592 +jsonschema-4.26.0.dist-info/RECORD,, +jsonschema-4.26.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87 +jsonschema-4.26.0.dist-info/entry_points.txt,sha256=vO7rX4Fs_xIVJy2pnAtKgTSxfpnozAVQ0DjCmpMxnWE,51 +jsonschema-4.26.0.dist-info/licenses/COPYING,sha256=T5KgFaE8TRoEC-8BiqE0MLTxvHO0Gxa7hGw0Z2bedDk,1057 +jsonschema/__init__.py,sha256=p-Rw4TS_0OPHZIJyImDWsdWgmd6CPWHMXLq7BuQxTGc,3941 +jsonschema/__main__.py,sha256=iLsZf2upUB3ilBKTlMnyK-HHt2Cnnfkwwxi_c6gLvSA,115 +jsonschema/__pycache__/__init__.cpython-312.pyc,, +jsonschema/__pycache__/__main__.cpython-312.pyc,, +jsonschema/__pycache__/_format.cpython-312.pyc,, +jsonschema/__pycache__/_keywords.cpython-312.pyc,, +jsonschema/__pycache__/_legacy_keywords.cpython-312.pyc,, +jsonschema/__pycache__/_types.cpython-312.pyc,, +jsonschema/__pycache__/_typing.cpython-312.pyc,, +jsonschema/__pycache__/_utils.cpython-312.pyc,, +jsonschema/__pycache__/cli.cpython-312.pyc,, +jsonschema/__pycache__/exceptions.cpython-312.pyc,, +jsonschema/__pycache__/protocols.cpython-312.pyc,, +jsonschema/__pycache__/validators.cpython-312.pyc,, +jsonschema/_format.py,sha256=lrs1U7iSB2ZX9Z2EalsROYeQm1d9kefw2CLo08G8P4s,15568 +jsonschema/_keywords.py,sha256=r8_DrqAfn6QLwQnmXEggveiSU-UaIL2p2nuPINelfFc,14949 +jsonschema/_legacy_keywords.py,sha256=2tWuwRPWbYS7EAl8wBIC_rabGuv1J4dfYLqNEPpShhA,15191 +jsonschema/_types.py,sha256=f_nkTDbrja3E3fsINTC73y_gMXTU210I_H7qK0NN30c,5456 +jsonschema/_typing.py,sha256=pXLnGy3jNtIUTq037kUfq0we17085eHtJwDJ2t--mQE,617 +jsonschema/_utils.py,sha256=Xv6_wKKslBJlwyj9-j2c8JDFw-4z4aWFnVe2pX8h7U4,10659 +jsonschema/benchmarks/__init__.py,sha256=A0sQrxDBVHSyQ-8ru3L11hMXf3q9gVuB9x_YgHb4R9M,70 +jsonschema/benchmarks/__pycache__/__init__.cpython-312.pyc,, +jsonschema/benchmarks/__pycache__/const_vs_enum.cpython-312.pyc,, +jsonschema/benchmarks/__pycache__/contains.cpython-312.pyc,, +jsonschema/benchmarks/__pycache__/import_benchmark.cpython-312.pyc,, +jsonschema/benchmarks/__pycache__/issue232.cpython-312.pyc,, +jsonschema/benchmarks/__pycache__/json_schema_test_suite.cpython-312.pyc,, +jsonschema/benchmarks/__pycache__/nested_schemas.cpython-312.pyc,, +jsonschema/benchmarks/__pycache__/subcomponents.cpython-312.pyc,, +jsonschema/benchmarks/__pycache__/unused_registry.cpython-312.pyc,, +jsonschema/benchmarks/__pycache__/useless_applicator_schemas.cpython-312.pyc,, +jsonschema/benchmarks/__pycache__/useless_keywords.cpython-312.pyc,, +jsonschema/benchmarks/__pycache__/validator_creation.cpython-312.pyc,, +jsonschema/benchmarks/const_vs_enum.py,sha256=DVFi3WDqBalZFOibnjpX1uTSr3Rxa2cPgFcowd7Ukrs,830 +jsonschema/benchmarks/contains.py,sha256=gexQoUrCOwECofbt19BeosQZ7WFL6PDdkX49DWwBlOg,786 +jsonschema/benchmarks/import_benchmark.py,sha256=2DRIOVS6p-bqLn_822B_X5FkIRvsJ2V1CYnV2druzDs,779 +jsonschema/benchmarks/issue232.py,sha256=3LLYLIlBGQnVuyyo2iAv-xky5P6PRFHANx4-zIIQOoE,521 +jsonschema/benchmarks/issue232/issue.json,sha256=eaPOZjMRu5u8RpKrsA9uk7ucPZS5tkKG4D_hkOTQ3Hk,117105 +jsonschema/benchmarks/json_schema_test_suite.py,sha256=PvfabpUYcF4_7csYDTcTauED8rnFEGYbdY5RqTXD08s,320 +jsonschema/benchmarks/nested_schemas.py,sha256=mo07dx-CIgmSOI62CNs4g5xu1FzHklLBpkQoDxWYcKs,1892 +jsonschema/benchmarks/subcomponents.py,sha256=fEyiMzsWeK2pd7DEGCuuY-vzGunwhHczRBWEnBRLKIo,1113 +jsonschema/benchmarks/unused_registry.py,sha256=hwRwONc9cefPtYzkoX_TYRO3GyUojriv0-YQaK3vnj0,940 +jsonschema/benchmarks/useless_applicator_schemas.py,sha256=EVm5-EtOEFoLP_Vt2j4SrCwlx05NhPqNuZQ6LIMP1Dc,3342 +jsonschema/benchmarks/useless_keywords.py,sha256=bj_zKr1oVctFlqyZaObCsYTgFjiiNgPzC0hr1Y868mE,867 +jsonschema/benchmarks/validator_creation.py,sha256=UkUQlLAnussnr_KdCIdad6xx2pXxQLmYtsXoiirKeWQ,285 +jsonschema/cli.py,sha256=av90OtpSxuiko3FAyEHtxpI-NSuX3WMtoQpIx09obJY,8445 +jsonschema/exceptions.py,sha256=b42hUDOfPFcprI4ZlNpjDeLKv8k9vOhgWct2jyDlxzk,15256 +jsonschema/protocols.py,sha256=lgyryVHqFijSFRAz95ch3VN2Fz-oTFyhj9Obcy5P_CI,7198 +jsonschema/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jsonschema/tests/__pycache__/__init__.cpython-312.pyc,, +jsonschema/tests/__pycache__/_suite.cpython-312.pyc,, +jsonschema/tests/__pycache__/fuzz_validate.cpython-312.pyc,, +jsonschema/tests/__pycache__/test_cli.cpython-312.pyc,, +jsonschema/tests/__pycache__/test_deprecations.cpython-312.pyc,, +jsonschema/tests/__pycache__/test_exceptions.cpython-312.pyc,, +jsonschema/tests/__pycache__/test_format.cpython-312.pyc,, +jsonschema/tests/__pycache__/test_jsonschema_test_suite.cpython-312.pyc,, +jsonschema/tests/__pycache__/test_types.cpython-312.pyc,, +jsonschema/tests/__pycache__/test_utils.cpython-312.pyc,, +jsonschema/tests/__pycache__/test_validators.cpython-312.pyc,, +jsonschema/tests/_suite.py,sha256=2k0X91N7dOHhQc5mrYv40OKf1weioj6RMBqWgLT6-PI,8374 +jsonschema/tests/fuzz_validate.py,sha256=fUA7yTJIihaCwJplkUehZeyB84HcXEcqtY5oPJXIO7I,1114 +jsonschema/tests/test_cli.py,sha256=A89r5LOHy-peLPZA5YDkOaMTWqzQO_w2Tu8WFz_vphM,28544 +jsonschema/tests/test_deprecations.py,sha256=yG6mkRJHpTHbWoxpLC5y5H7fk8erGOs8f_9V4tCBEh8,15754 +jsonschema/tests/test_exceptions.py,sha256=rsEqdjknUz3dEyJb6_DiModPHXsJTEWzDrxUscg8G5o,24301 +jsonschema/tests/test_format.py,sha256=eVm5SMaWF2lOPO28bPAwNvkiQvHCQKy-MnuAgEchfEc,3188 +jsonschema/tests/test_jsonschema_test_suite.py,sha256=tAfxknM65OR9LyDPHu1pkEaombLgjRLnJ6FPiWPdxjg,8461 +jsonschema/tests/test_types.py,sha256=cF51KTDmdsx06MrIc4fXKt0X9fIsVgw5uhT8CamVa8U,6977 +jsonschema/tests/test_utils.py,sha256=sao74o1PyYMxBfqweokQN48CFSS6yhJk5FkCfMJ5PsI,4163 +jsonschema/tests/test_validators.py,sha256=uzl-agyLoZPTufouIHSxFPfPYrdIdjNwjHcD_5NyU4E,87949 +jsonschema/tests/typing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jsonschema/tests/typing/__pycache__/__init__.cpython-312.pyc,, +jsonschema/tests/typing/__pycache__/test_all_concrete_validators_match_protocol.cpython-312.pyc,, +jsonschema/tests/typing/test_all_concrete_validators_match_protocol.py,sha256=I5XUl5ZYUSZo3APkF10l8Mnfk09oXKqvXWttGGd3sDk,1238 +jsonschema/validators.py,sha256=l_FxuZW0sMoFUwqTZqElL7F1P5nzy3sQaLbZqrEJb6U,47159 diff --git a/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/WHEEL new file mode 100644 index 0000000..ae8ec1b --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.28.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/entry_points.txt new file mode 100644 index 0000000..eecef9d --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +jsonschema = jsonschema.cli:main diff --git a/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/licenses/COPYING b/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/licenses/COPYING new file mode 100644 index 0000000..af9cfbd --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema-4.26.0.dist-info/licenses/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2013 Julian Berman + +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. diff --git a/venv/lib/python3.12/site-packages/jsonschema/__init__.py b/venv/lib/python3.12/site-packages/jsonschema/__init__.py new file mode 100644 index 0000000..d8dec8c --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/__init__.py @@ -0,0 +1,120 @@ +""" +An implementation of JSON Schema for Python. + +The main functionality is provided by the validator classes for each of the +supported JSON Schema versions. + +Most commonly, `jsonschema.validators.validate` is the quickest way to simply +validate a given instance under a schema, and will create a validator +for you. +""" +import warnings + +from jsonschema._format import FormatChecker +from jsonschema._types import TypeChecker +from jsonschema.exceptions import SchemaError, ValidationError +from jsonschema.validators import ( + Draft3Validator, + Draft4Validator, + Draft6Validator, + Draft7Validator, + Draft201909Validator, + Draft202012Validator, + validate, +) + + +def __getattr__(name): + if name == "__version__": + warnings.warn( + "Accessing jsonschema.__version__ is deprecated and will be " + "removed in a future release. Use importlib.metadata directly " + "to query for jsonschema's version.", + DeprecationWarning, + stacklevel=2, + ) + + from importlib import metadata + return metadata.version("jsonschema") + elif name == "RefResolver": + from jsonschema.validators import _RefResolver + warnings.warn( + _RefResolver._DEPRECATION_MESSAGE, + DeprecationWarning, + stacklevel=2, + ) + return _RefResolver + elif name == "ErrorTree": + warnings.warn( + "Importing ErrorTree directly from the jsonschema package " + "is deprecated and will become an ImportError. Import it from " + "jsonschema.exceptions instead.", + DeprecationWarning, + stacklevel=2, + ) + from jsonschema.exceptions import ErrorTree + return ErrorTree + elif name == "FormatError": + warnings.warn( + "Importing FormatError directly from the jsonschema package " + "is deprecated and will become an ImportError. Import it from " + "jsonschema.exceptions instead.", + DeprecationWarning, + stacklevel=2, + ) + from jsonschema.exceptions import FormatError + return FormatError + elif name == "Validator": + warnings.warn( + "Importing Validator directly from the jsonschema package " + "is deprecated and will become an ImportError. Import it from " + "jsonschema.protocols instead.", + DeprecationWarning, + stacklevel=2, + ) + from jsonschema.protocols import Validator + return Validator + elif name == "RefResolutionError": + from jsonschema.exceptions import _RefResolutionError + warnings.warn( + _RefResolutionError._DEPRECATION_MESSAGE, + DeprecationWarning, + stacklevel=2, + ) + return _RefResolutionError + + format_checkers = { + "draft3_format_checker": Draft3Validator, + "draft4_format_checker": Draft4Validator, + "draft6_format_checker": Draft6Validator, + "draft7_format_checker": Draft7Validator, + "draft201909_format_checker": Draft201909Validator, + "draft202012_format_checker": Draft202012Validator, + } + ValidatorForFormat = format_checkers.get(name) + if ValidatorForFormat is not None: + warnings.warn( + f"Accessing jsonschema.{name} is deprecated and will be " + "removed in a future release. Instead, use the FORMAT_CHECKER " + "attribute on the corresponding Validator.", + DeprecationWarning, + stacklevel=2, + ) + return ValidatorForFormat.FORMAT_CHECKER + + raise AttributeError(f"module {__name__} has no attribute {name}") + + +__all__ = [ + "Draft3Validator", + "Draft4Validator", + "Draft6Validator", + "Draft7Validator", + "Draft201909Validator", + "Draft202012Validator", + "FormatChecker", + "SchemaError", + "TypeChecker", + "ValidationError", + "validate", +] diff --git a/venv/lib/python3.12/site-packages/jsonschema/__main__.py b/venv/lib/python3.12/site-packages/jsonschema/__main__.py new file mode 100644 index 0000000..fb260ae --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/__main__.py @@ -0,0 +1,6 @@ +""" +The jsonschema CLI is now deprecated in favor of check-jsonschema. +""" +from jsonschema.cli import main + +main() diff --git a/venv/lib/python3.12/site-packages/jsonschema/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a78765b Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/__pycache__/__main__.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000..3bbf623 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/__main__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_format.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_format.cpython-312.pyc new file mode 100644 index 0000000..ba27eca Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_format.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_keywords.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_keywords.cpython-312.pyc new file mode 100644 index 0000000..36ccd4d Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_keywords.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_legacy_keywords.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_legacy_keywords.cpython-312.pyc new file mode 100644 index 0000000..709e826 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_legacy_keywords.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_types.cpython-312.pyc new file mode 100644 index 0000000..677573e Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_typing.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_typing.cpython-312.pyc new file mode 100644 index 0000000..d475b3e Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_typing.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_utils.cpython-312.pyc new file mode 100644 index 0000000..673bd12 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/__pycache__/cli.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/cli.cpython-312.pyc new file mode 100644 index 0000000..d9fc5d1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/cli.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..ece305d Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/__pycache__/protocols.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/protocols.cpython-312.pyc new file mode 100644 index 0000000..33ac03c Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/protocols.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/__pycache__/validators.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/validators.cpython-312.pyc new file mode 100644 index 0000000..0843fa7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/__pycache__/validators.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/_format.py b/venv/lib/python3.12/site-packages/jsonschema/_format.py new file mode 100644 index 0000000..5efda86 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/_format.py @@ -0,0 +1,546 @@ +from __future__ import annotations + +from contextlib import suppress +from datetime import date, datetime +from uuid import UUID +import ipaddress +import re +import typing +import warnings + +from jsonschema.exceptions import FormatError + +_FormatCheckCallable = typing.Callable[[object], bool] +#: A format checker callable. +_F = typing.TypeVar("_F", bound=_FormatCheckCallable) +_RaisesType = type[Exception] | tuple[type[Exception], ...] + +_RE_DATE = re.compile(r"^\d{4}-\d{2}-\d{2}$", re.ASCII) + + +class FormatChecker: + """ + A ``format`` property checker. + + JSON Schema does not mandate that the ``format`` property actually do any + validation. If validation is desired however, instances of this class can + be hooked into validators to enable format validation. + + `FormatChecker` objects always return ``True`` when asked about + formats that they do not know how to validate. + + To add a check for a custom format use the `FormatChecker.checks` + decorator. + + Arguments: + + formats: + + The known formats to validate. This argument can be used to + limit which formats will be used during validation. + + """ + + checkers: dict[ + str, + tuple[_FormatCheckCallable, _RaisesType], + ] = {} # noqa: RUF012 + + def __init__(self, formats: typing.Iterable[str] | None = None): + if formats is None: + formats = self.checkers.keys() + self.checkers = {k: self.checkers[k] for k in formats} + + def __repr__(self): + return f"" + + def checks( + self, format: str, raises: _RaisesType = (), + ) -> typing.Callable[[_F], _F]: + """ + Register a decorated function as validating a new format. + + Arguments: + + format: + + The format that the decorated function will check. + + raises: + + The exception(s) raised by the decorated function when an + invalid instance is found. + + The exception object will be accessible as the + `jsonschema.exceptions.ValidationError.cause` attribute of the + resulting validation error. + + """ + + def _checks(func: _F) -> _F: + self.checkers[format] = (func, raises) + return func + + return _checks + + @classmethod + def cls_checks( + cls, format: str, raises: _RaisesType = (), + ) -> typing.Callable[[_F], _F]: + warnings.warn( + ( + "FormatChecker.cls_checks is deprecated. Call " + "FormatChecker.checks on a specific FormatChecker instance " + "instead." + ), + DeprecationWarning, + stacklevel=2, + ) + return cls._cls_checks(format=format, raises=raises) + + @classmethod + def _cls_checks( + cls, format: str, raises: _RaisesType = (), + ) -> typing.Callable[[_F], _F]: + def _checks(func: _F) -> _F: + cls.checkers[format] = (func, raises) + return func + + return _checks + + def check(self, instance: object, format: str) -> None: + """ + Check whether the instance conforms to the given format. + + Arguments: + + instance (*any primitive type*, i.e. str, number, bool): + + The instance to check + + format: + + The format that instance should conform to + + Raises: + + FormatError: + + if the instance does not conform to ``format`` + + """ + if format not in self.checkers: + return + + func, raises = self.checkers[format] + result, cause = None, None + try: + result = func(instance) + except raises as e: + cause = e + if not result: + raise FormatError(f"{instance!r} is not a {format!r}", cause=cause) + + def conforms(self, instance: object, format: str) -> bool: + """ + Check whether the instance conforms to the given format. + + Arguments: + + instance (*any primitive type*, i.e. str, number, bool): + + The instance to check + + format: + + The format that instance should conform to + + Returns: + + bool: whether it conformed + + """ + try: + self.check(instance, format) + except FormatError: + return False + else: + return True + + +draft3_format_checker = FormatChecker() +draft4_format_checker = FormatChecker() +draft6_format_checker = FormatChecker() +draft7_format_checker = FormatChecker() +draft201909_format_checker = FormatChecker() +draft202012_format_checker = FormatChecker() + +_draft_checkers: dict[str, FormatChecker] = dict( + draft3=draft3_format_checker, + draft4=draft4_format_checker, + draft6=draft6_format_checker, + draft7=draft7_format_checker, + draft201909=draft201909_format_checker, + draft202012=draft202012_format_checker, +) + + +def _checks_drafts( + name=None, + draft3=None, + draft4=None, + draft6=None, + draft7=None, + draft201909=None, + draft202012=None, + raises=(), +) -> typing.Callable[[_F], _F]: + draft3 = draft3 or name + draft4 = draft4 or name + draft6 = draft6 or name + draft7 = draft7 or name + draft201909 = draft201909 or name + draft202012 = draft202012 or name + + def wrap(func: _F) -> _F: + if draft3: + func = _draft_checkers["draft3"].checks(draft3, raises)(func) + if draft4: + func = _draft_checkers["draft4"].checks(draft4, raises)(func) + if draft6: + func = _draft_checkers["draft6"].checks(draft6, raises)(func) + if draft7: + func = _draft_checkers["draft7"].checks(draft7, raises)(func) + if draft201909: + func = _draft_checkers["draft201909"].checks(draft201909, raises)( + func, + ) + if draft202012: + func = _draft_checkers["draft202012"].checks(draft202012, raises)( + func, + ) + + # Oy. This is bad global state, but relied upon for now, until + # deprecation. See #519 and test_format_checkers_come_with_defaults + FormatChecker._cls_checks( + draft202012 or draft201909 or draft7 or draft6 or draft4 or draft3, + raises, + )(func) + return func + + return wrap + + +@_checks_drafts(name="idn-email") +@_checks_drafts(name="email") +def is_email(instance: object) -> bool: + if not isinstance(instance, str): + return True + return "@" in instance + + +@_checks_drafts( + draft3="ip-address", + draft4="ipv4", + draft6="ipv4", + draft7="ipv4", + draft201909="ipv4", + draft202012="ipv4", + raises=ipaddress.AddressValueError, +) +def is_ipv4(instance: object) -> bool: + if not isinstance(instance, str): + return True + return bool(ipaddress.IPv4Address(instance)) + + +@_checks_drafts(name="ipv6", raises=ipaddress.AddressValueError) +def is_ipv6(instance: object) -> bool: + if not isinstance(instance, str): + return True + address = ipaddress.IPv6Address(instance) + return not getattr(address, "scope_id", "") + + +with suppress(ImportError): + from fqdn import FQDN + + @_checks_drafts( + draft3="host-name", + draft4="hostname", + draft6="hostname", + draft7="hostname", + draft201909="hostname", + draft202012="hostname", + # fqdn.FQDN("") raises a ValueError due to a bug + # however, it's not clear when or if that will be fixed, so catch it + # here for now + raises=ValueError, + ) + def is_host_name(instance: object) -> bool: + if not isinstance(instance, str): + return True + return FQDN(instance, min_labels=1).is_valid + + +with suppress(ImportError): + # The built-in `idna` codec only implements RFC 3890, so we go elsewhere. + import idna + + @_checks_drafts( + draft7="idn-hostname", + draft201909="idn-hostname", + draft202012="idn-hostname", + raises=(idna.IDNAError, UnicodeError), + ) + def is_idn_host_name(instance: object) -> bool: + if not isinstance(instance, str): + return True + idna.encode(instance) + return True + + +try: + import rfc3987 +except ImportError: + with suppress(ImportError): + from rfc3986_validator import validate_rfc3986 + + @_checks_drafts(name="uri") + def is_uri(instance: object) -> bool: + if not isinstance(instance, str): + return True + return validate_rfc3986(instance, rule="URI") + + @_checks_drafts( + draft6="uri-reference", + draft7="uri-reference", + draft201909="uri-reference", + draft202012="uri-reference", + raises=ValueError, + ) + def is_uri_reference(instance: object) -> bool: + if not isinstance(instance, str): + return True + return validate_rfc3986(instance, rule="URI_reference") + + with suppress(ImportError): + from rfc3987_syntax import is_valid_syntax as _rfc3987_is_valid_syntax + + @_checks_drafts( + draft7="iri", + draft201909="iri", + draft202012="iri", + raises=ValueError, + ) + def is_iri(instance: object) -> bool: + if not isinstance(instance, str): + return True + return _rfc3987_is_valid_syntax("iri", instance) + + @_checks_drafts( + draft7="iri-reference", + draft201909="iri-reference", + draft202012="iri-reference", + raises=ValueError, + ) + def is_iri_reference(instance: object) -> bool: + if not isinstance(instance, str): + return True + return _rfc3987_is_valid_syntax("iri_reference", instance) + +else: + + @_checks_drafts( + draft7="iri", + draft201909="iri", + draft202012="iri", + raises=ValueError, + ) + def is_iri(instance: object) -> bool: + if not isinstance(instance, str): + return True + return rfc3987.parse(instance, rule="IRI") + + @_checks_drafts( + draft7="iri-reference", + draft201909="iri-reference", + draft202012="iri-reference", + raises=ValueError, + ) + def is_iri_reference(instance: object) -> bool: + if not isinstance(instance, str): + return True + return rfc3987.parse(instance, rule="IRI_reference") + + @_checks_drafts(name="uri", raises=ValueError) + def is_uri(instance: object) -> bool: + if not isinstance(instance, str): + return True + return rfc3987.parse(instance, rule="URI") + + @_checks_drafts( + draft6="uri-reference", + draft7="uri-reference", + draft201909="uri-reference", + draft202012="uri-reference", + raises=ValueError, + ) + def is_uri_reference(instance: object) -> bool: + if not isinstance(instance, str): + return True + return rfc3987.parse(instance, rule="URI_reference") + + +with suppress(ImportError): + from rfc3339_validator import validate_rfc3339 + + @_checks_drafts(name="date-time") + def is_datetime(instance: object) -> bool: + if not isinstance(instance, str): + return True + return validate_rfc3339(instance.upper()) + + @_checks_drafts( + draft7="time", + draft201909="time", + draft202012="time", + ) + def is_time(instance: object) -> bool: + if not isinstance(instance, str): + return True + return is_datetime("1970-01-01T" + instance) + + +@_checks_drafts(name="regex", raises=re.error) +def is_regex(instance: object) -> bool: + if not isinstance(instance, str): + return True + return bool(re.compile(instance)) + + +@_checks_drafts( + draft3="date", + draft7="date", + draft201909="date", + draft202012="date", + raises=ValueError, +) +def is_date(instance: object) -> bool: + if not isinstance(instance, str): + return True + return bool(_RE_DATE.fullmatch(instance) and date.fromisoformat(instance)) + + +@_checks_drafts(draft3="time", raises=ValueError) +def is_draft3_time(instance: object) -> bool: + if not isinstance(instance, str): + return True + return bool(datetime.strptime(instance, "%H:%M:%S")) # noqa: DTZ007 + + +with suppress(ImportError): + import webcolors + + @_checks_drafts(draft3="color", raises=(ValueError, TypeError)) + def is_css21_color(instance: object) -> bool: + if isinstance(instance, str): + try: + webcolors.name_to_hex(instance) + except ValueError: + webcolors.normalize_hex(instance.lower()) + return True + + +with suppress(ImportError): + import jsonpointer + + @_checks_drafts( + draft6="json-pointer", + draft7="json-pointer", + draft201909="json-pointer", + draft202012="json-pointer", + raises=jsonpointer.JsonPointerException, + ) + def is_json_pointer(instance: object) -> bool: + if not isinstance(instance, str): + return True + return bool(jsonpointer.JsonPointer(instance)) + + # TODO: I don't want to maintain this, so it + # needs to go either into jsonpointer (pending + # https://github.com/stefankoegl/python-json-pointer/issues/34) or + # into a new external library. + @_checks_drafts( + draft7="relative-json-pointer", + draft201909="relative-json-pointer", + draft202012="relative-json-pointer", + raises=jsonpointer.JsonPointerException, + ) + def is_relative_json_pointer(instance: object) -> bool: + # Definition taken from: + # https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01#section-3 + if not isinstance(instance, str): + return True + if not instance: + return False + + non_negative_integer, rest = [], "" + for i, character in enumerate(instance): + if character.isdigit(): + # digits with a leading "0" are not allowed + if i > 0 and int(instance[i - 1]) == 0: + return False + + non_negative_integer.append(character) + continue + + if not non_negative_integer: + return False + + rest = instance[i:] + break + return (rest == "#") or bool(jsonpointer.JsonPointer(rest)) + + +with suppress(ImportError): + import uri_template + + @_checks_drafts( + draft6="uri-template", + draft7="uri-template", + draft201909="uri-template", + draft202012="uri-template", + ) + def is_uri_template(instance: object) -> bool: + if not isinstance(instance, str): + return True + return uri_template.validate(instance) + + +with suppress(ImportError): + import isoduration + + @_checks_drafts( + draft201909="duration", + draft202012="duration", + raises=isoduration.DurationParsingException, + ) + def is_duration(instance: object) -> bool: + if not isinstance(instance, str): + return True + isoduration.parse_duration(instance) + # FIXME: See bolsote/isoduration#25 and bolsote/isoduration#21 + return instance.endswith(tuple("DMYWHMS")) + + +@_checks_drafts( + draft201909="uuid", + draft202012="uuid", + raises=ValueError, +) +def is_uuid(instance: object) -> bool: + if not isinstance(instance, str): + return True + UUID(instance) + return all(instance[position] == "-" for position in (8, 13, 18, 23)) diff --git a/venv/lib/python3.12/site-packages/jsonschema/_keywords.py b/venv/lib/python3.12/site-packages/jsonschema/_keywords.py new file mode 100644 index 0000000..f30f954 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/_keywords.py @@ -0,0 +1,449 @@ +from fractions import Fraction +import re + +from jsonschema._utils import ( + ensure_list, + equal, + extras_msg, + find_additional_properties, + find_evaluated_item_indexes_by_schema, + find_evaluated_property_keys_by_schema, + uniq, +) +from jsonschema.exceptions import FormatError, ValidationError + + +def patternProperties(validator, patternProperties, instance, schema): + if not validator.is_type(instance, "object"): + return + + for pattern, subschema in patternProperties.items(): + for k, v in instance.items(): + if re.search(pattern, k): + yield from validator.descend( + v, subschema, path=k, schema_path=pattern, + ) + + +def propertyNames(validator, propertyNames, instance, schema): + if not validator.is_type(instance, "object"): + return + + for property in instance: + yield from validator.descend(instance=property, schema=propertyNames) + + +def additionalProperties(validator, aP, instance, schema): + if not validator.is_type(instance, "object"): + return + + extras = set(find_additional_properties(instance, schema)) + + if validator.is_type(aP, "object"): + for extra in extras: + yield from validator.descend(instance[extra], aP, path=extra) + elif not aP and extras: + if "patternProperties" in schema: + verb = "does" if len(extras) == 1 else "do" + joined = ", ".join(repr(each) for each in sorted(extras)) + patterns = ", ".join( + repr(each) for each in sorted(schema["patternProperties"]) + ) + error = f"{joined} {verb} not match any of the regexes: {patterns}" + yield ValidationError(error) + else: + error = "Additional properties are not allowed (%s %s unexpected)" + yield ValidationError(error % extras_msg(sorted(extras, key=str))) + + +def items(validator, items, instance, schema): + if not validator.is_type(instance, "array"): + return + + prefix = len(schema.get("prefixItems", [])) + total = len(instance) + extra = total - prefix + if extra <= 0: + return + + if items is False: + rest = instance[prefix:] if extra != 1 else instance[prefix] + item = "items" if prefix != 1 else "item" + yield ValidationError( + f"Expected at most {prefix} {item} but found {extra} " + f"extra: {rest!r}", + ) + else: + for index in range(prefix, total): + yield from validator.descend( + instance=instance[index], + schema=items, + path=index, + ) + + +def const(validator, const, instance, schema): + if not equal(instance, const): + yield ValidationError(f"{const!r} was expected") + + +def contains(validator, contains, instance, schema): + if not validator.is_type(instance, "array"): + return + + matches = 0 + min_contains = schema.get("minContains", 1) + max_contains = schema.get("maxContains", len(instance)) + + contains_validator = validator.evolve(schema=contains) + + for each in instance: + if contains_validator.is_valid(each): + matches += 1 + if matches > max_contains: + yield ValidationError( + "Too many items match the given schema " + f"(expected at most {max_contains})", + validator="maxContains", + validator_value=max_contains, + ) + return + + if matches < min_contains: + if not matches: + yield ValidationError( + f"{instance!r} does not contain items " + "matching the given schema", + ) + else: + yield ValidationError( + "Too few items match the given schema (expected at least " + f"{min_contains} but only {matches} matched)", + validator="minContains", + validator_value=min_contains, + ) + + +def exclusiveMinimum(validator, minimum, instance, schema): + if not validator.is_type(instance, "number"): + return + + if instance <= minimum: + yield ValidationError( + f"{instance!r} is less than or equal to " + f"the minimum of {minimum!r}", + ) + + +def exclusiveMaximum(validator, maximum, instance, schema): + if not validator.is_type(instance, "number"): + return + + if instance >= maximum: + yield ValidationError( + f"{instance!r} is greater than or equal " + f"to the maximum of {maximum!r}", + ) + + +def minimum(validator, minimum, instance, schema): + if not validator.is_type(instance, "number"): + return + + if instance < minimum: + message = f"{instance!r} is less than the minimum of {minimum!r}" + yield ValidationError(message) + + +def maximum(validator, maximum, instance, schema): + if not validator.is_type(instance, "number"): + return + + if instance > maximum: + message = f"{instance!r} is greater than the maximum of {maximum!r}" + yield ValidationError(message) + + +def multipleOf(validator, dB, instance, schema): + if not validator.is_type(instance, "number"): + return + + if isinstance(dB, float): + quotient = instance / dB + try: + failed = int(quotient) != quotient + except OverflowError: + # When `instance` is large and `dB` is less than one, + # quotient can overflow to infinity; and then casting to int + # raises an error. + # + # In this case we fall back to Fraction logic, which is + # exact and cannot overflow. The performance is also + # acceptable: we try the fast all-float option first, and + # we know that fraction(dB) can have at most a few hundred + # digits in each part. The worst-case slowdown is therefore + # for already-slow enormous integers or Decimals. + failed = (Fraction(instance) / Fraction(dB)).denominator != 1 + else: + failed = instance % dB + + if failed: + yield ValidationError(f"{instance!r} is not a multiple of {dB}") + + +def minItems(validator, mI, instance, schema): + if validator.is_type(instance, "array") and len(instance) < mI: + message = "should be non-empty" if mI == 1 else "is too short" + yield ValidationError(f"{instance!r} {message}") + + +def maxItems(validator, mI, instance, schema): + if validator.is_type(instance, "array") and len(instance) > mI: + message = "is expected to be empty" if mI == 0 else "is too long" + yield ValidationError(f"{instance!r} {message}") + + +def uniqueItems(validator, uI, instance, schema): + if ( + uI + and validator.is_type(instance, "array") + and not uniq(instance) + ): + yield ValidationError(f"{instance!r} has non-unique elements") + + +def pattern(validator, patrn, instance, schema): + if ( + validator.is_type(instance, "string") + and not re.search(patrn, instance) + ): + yield ValidationError(f"{instance!r} does not match {patrn!r}") + + +def format(validator, format, instance, schema): + if validator.format_checker is not None: + try: + validator.format_checker.check(instance, format) + except FormatError as error: + yield ValidationError(error.message, cause=error.cause) + + +def minLength(validator, mL, instance, schema): + if validator.is_type(instance, "string") and len(instance) < mL: + message = "should be non-empty" if mL == 1 else "is too short" + yield ValidationError(f"{instance!r} {message}") + + +def maxLength(validator, mL, instance, schema): + if validator.is_type(instance, "string") and len(instance) > mL: + message = "is expected to be empty" if mL == 0 else "is too long" + yield ValidationError(f"{instance!r} {message}") + + +def dependentRequired(validator, dependentRequired, instance, schema): + if not validator.is_type(instance, "object"): + return + + for property, dependency in dependentRequired.items(): + if property not in instance: + continue + + for each in dependency: + if each not in instance: + message = f"{each!r} is a dependency of {property!r}" + yield ValidationError(message) + + +def dependentSchemas(validator, dependentSchemas, instance, schema): + if not validator.is_type(instance, "object"): + return + + for property, dependency in dependentSchemas.items(): + if property not in instance: + continue + yield from validator.descend( + instance, dependency, schema_path=property, + ) + + +def enum(validator, enums, instance, schema): + if all(not equal(each, instance) for each in enums): + yield ValidationError(f"{instance!r} is not one of {enums!r}") + + +def ref(validator, ref, instance, schema): + yield from validator._validate_reference(ref=ref, instance=instance) + + +def dynamicRef(validator, dynamicRef, instance, schema): + yield from validator._validate_reference(ref=dynamicRef, instance=instance) + + +def type(validator, types, instance, schema): + types = ensure_list(types) + + if not any(validator.is_type(instance, type) for type in types): + reprs = ", ".join(repr(type) for type in types) + yield ValidationError(f"{instance!r} is not of type {reprs}") + + +def properties(validator, properties, instance, schema): + if not validator.is_type(instance, "object"): + return + + for property, subschema in properties.items(): + if property in instance: + yield from validator.descend( + instance[property], + subschema, + path=property, + schema_path=property, + ) + + +def required(validator, required, instance, schema): + if not validator.is_type(instance, "object"): + return + for property in required: + if property not in instance: + yield ValidationError(f"{property!r} is a required property") + + +def minProperties(validator, mP, instance, schema): + if validator.is_type(instance, "object") and len(instance) < mP: + message = ( + "should be non-empty" if mP == 1 + else "does not have enough properties" + ) + yield ValidationError(f"{instance!r} {message}") + + +def maxProperties(validator, mP, instance, schema): + if not validator.is_type(instance, "object"): + return + if validator.is_type(instance, "object") and len(instance) > mP: + message = ( + "is expected to be empty" if mP == 0 + else "has too many properties" + ) + yield ValidationError(f"{instance!r} {message}") + + +def allOf(validator, allOf, instance, schema): + for index, subschema in enumerate(allOf): + yield from validator.descend(instance, subschema, schema_path=index) + + +def anyOf(validator, anyOf, instance, schema): + all_errors = [] + for index, subschema in enumerate(anyOf): + errs = list(validator.descend(instance, subschema, schema_path=index)) + if not errs: + break + all_errors.extend(errs) + else: + yield ValidationError( + f"{instance!r} is not valid under any of the given schemas", + context=all_errors, + ) + + +def oneOf(validator, oneOf, instance, schema): + subschemas = enumerate(oneOf) + all_errors = [] + for index, subschema in subschemas: + errs = list(validator.descend(instance, subschema, schema_path=index)) + if not errs: + first_valid = subschema + break + all_errors.extend(errs) + else: + yield ValidationError( + f"{instance!r} is not valid under any of the given schemas", + context=all_errors, + ) + + more_valid = [ + each for _, each in subschemas + if validator.evolve(schema=each).is_valid(instance) + ] + if more_valid: + more_valid.append(first_valid) + reprs = ", ".join(repr(schema) for schema in more_valid) + yield ValidationError(f"{instance!r} is valid under each of {reprs}") + + +def not_(validator, not_schema, instance, schema): + if validator.evolve(schema=not_schema).is_valid(instance): + message = f"{instance!r} should not be valid under {not_schema!r}" + yield ValidationError(message) + + +def if_(validator, if_schema, instance, schema): + if validator.evolve(schema=if_schema).is_valid(instance): + if "then" in schema: + then = schema["then"] + yield from validator.descend(instance, then, schema_path="then") + elif "else" in schema: + else_ = schema["else"] + yield from validator.descend(instance, else_, schema_path="else") + + +def unevaluatedItems(validator, unevaluatedItems, instance, schema): + if not validator.is_type(instance, "array"): + return + evaluated_item_indexes = find_evaluated_item_indexes_by_schema( + validator, instance, schema, + ) + unevaluated_items = [ + item for index, item in enumerate(instance) + if index not in evaluated_item_indexes + ] + if unevaluated_items: + error = "Unevaluated items are not allowed (%s %s unexpected)" + yield ValidationError(error % extras_msg(unevaluated_items)) + + +def unevaluatedProperties(validator, unevaluatedProperties, instance, schema): + if not validator.is_type(instance, "object"): + return + evaluated_keys = find_evaluated_property_keys_by_schema( + validator, instance, schema, + ) + unevaluated_keys = [] + for property in instance: + if property not in evaluated_keys: + for _ in validator.descend( + instance[property], + unevaluatedProperties, + path=property, + schema_path=property, + ): + # FIXME: Include context for each unevaluated property + # indicating why it's invalid under the subschema. + unevaluated_keys.append(property) # noqa: PERF401 + + if unevaluated_keys: + if unevaluatedProperties is False: + error = "Unevaluated properties are not allowed (%s %s unexpected)" + extras = sorted(unevaluated_keys, key=str) + yield ValidationError(error % extras_msg(extras)) + else: + error = ( + "Unevaluated properties are not valid under " + "the given schema (%s %s unevaluated and invalid)" + ) + yield ValidationError(error % extras_msg(unevaluated_keys)) + + +def prefixItems(validator, prefixItems, instance, schema): + if not validator.is_type(instance, "array"): + return + + for (index, item), subschema in zip(enumerate(instance), prefixItems): + yield from validator.descend( + instance=item, + schema=subschema, + schema_path=index, + path=index, + ) diff --git a/venv/lib/python3.12/site-packages/jsonschema/_legacy_keywords.py b/venv/lib/python3.12/site-packages/jsonschema/_legacy_keywords.py new file mode 100644 index 0000000..c691589 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/_legacy_keywords.py @@ -0,0 +1,449 @@ +import re + +from referencing.jsonschema import lookup_recursive_ref + +from jsonschema import _utils +from jsonschema.exceptions import ValidationError + + +def ignore_ref_siblings(schema): + """ + Ignore siblings of ``$ref`` if it is present. + + Otherwise, return all keywords. + + Suitable for use with `create`'s ``applicable_validators`` argument. + """ + ref = schema.get("$ref") + if ref is not None: + return [("$ref", ref)] + else: + return schema.items() + + +def dependencies_draft3(validator, dependencies, instance, schema): + if not validator.is_type(instance, "object"): + return + + for property, dependency in dependencies.items(): + if property not in instance: + continue + + if validator.is_type(dependency, "object"): + yield from validator.descend( + instance, dependency, schema_path=property, + ) + elif validator.is_type(dependency, "string"): + if dependency not in instance: + message = f"{dependency!r} is a dependency of {property!r}" + yield ValidationError(message) + else: + for each in dependency: + if each not in instance: + message = f"{each!r} is a dependency of {property!r}" + yield ValidationError(message) + + +def dependencies_draft4_draft6_draft7( + validator, + dependencies, + instance, + schema, +): + """ + Support for the ``dependencies`` keyword from pre-draft 2019-09. + + In later drafts, the keyword was split into separate + ``dependentRequired`` and ``dependentSchemas`` validators. + """ + if not validator.is_type(instance, "object"): + return + + for property, dependency in dependencies.items(): + if property not in instance: + continue + + if validator.is_type(dependency, "array"): + for each in dependency: + if each not in instance: + message = f"{each!r} is a dependency of {property!r}" + yield ValidationError(message) + else: + yield from validator.descend( + instance, dependency, schema_path=property, + ) + + +def disallow_draft3(validator, disallow, instance, schema): + for disallowed in _utils.ensure_list(disallow): + if validator.evolve(schema={"type": [disallowed]}).is_valid(instance): + message = f"{disallowed!r} is disallowed for {instance!r}" + yield ValidationError(message) + + +def extends_draft3(validator, extends, instance, schema): + if validator.is_type(extends, "object"): + yield from validator.descend(instance, extends) + return + for index, subschema in enumerate(extends): + yield from validator.descend(instance, subschema, schema_path=index) + + +def items_draft3_draft4(validator, items, instance, schema): + if not validator.is_type(instance, "array"): + return + + if validator.is_type(items, "object"): + for index, item in enumerate(instance): + yield from validator.descend(item, items, path=index) + else: + for (index, item), subschema in zip(enumerate(instance), items): + yield from validator.descend( + item, subschema, path=index, schema_path=index, + ) + + +def additionalItems(validator, aI, instance, schema): + if ( + not validator.is_type(instance, "array") + or validator.is_type(schema.get("items", {}), "object") + ): + return + + len_items = len(schema.get("items", [])) + if validator.is_type(aI, "object"): + for index, item in enumerate(instance[len_items:], start=len_items): + yield from validator.descend(item, aI, path=index) + elif not aI and len(instance) > len(schema.get("items", [])): + error = "Additional items are not allowed (%s %s unexpected)" + yield ValidationError( + error % _utils.extras_msg(instance[len(schema.get("items", [])):]), + ) + + +def items_draft6_draft7_draft201909(validator, items, instance, schema): + if not validator.is_type(instance, "array"): + return + + if validator.is_type(items, "array"): + for (index, item), subschema in zip(enumerate(instance), items): + yield from validator.descend( + item, subschema, path=index, schema_path=index, + ) + else: + for index, item in enumerate(instance): + yield from validator.descend(item, items, path=index) + + +def minimum_draft3_draft4(validator, minimum, instance, schema): + if not validator.is_type(instance, "number"): + return + + if schema.get("exclusiveMinimum", False): + failed = instance <= minimum + cmp = "less than or equal to" + else: + failed = instance < minimum + cmp = "less than" + + if failed: + message = f"{instance!r} is {cmp} the minimum of {minimum!r}" + yield ValidationError(message) + + +def maximum_draft3_draft4(validator, maximum, instance, schema): + if not validator.is_type(instance, "number"): + return + + if schema.get("exclusiveMaximum", False): + failed = instance >= maximum + cmp = "greater than or equal to" + else: + failed = instance > maximum + cmp = "greater than" + + if failed: + message = f"{instance!r} is {cmp} the maximum of {maximum!r}" + yield ValidationError(message) + + +def properties_draft3(validator, properties, instance, schema): + if not validator.is_type(instance, "object"): + return + + for property, subschema in properties.items(): + if property in instance: + yield from validator.descend( + instance[property], + subschema, + path=property, + schema_path=property, + ) + elif subschema.get("required", False): + error = ValidationError(f"{property!r} is a required property") + error._set( + validator="required", + validator_value=subschema["required"], + instance=instance, + schema=schema, + ) + error.path.appendleft(property) + error.schema_path.extend([property, "required"]) + yield error + + +def type_draft3(validator, types, instance, schema): + types = _utils.ensure_list(types) + + all_errors = [] + for index, type in enumerate(types): + if validator.is_type(type, "object"): + errors = list(validator.descend(instance, type, schema_path=index)) + if not errors: + return + all_errors.extend(errors) + elif validator.is_type(instance, type): + return + + reprs = [] + for type in types: + try: + reprs.append(repr(type["name"])) + except Exception: # noqa: BLE001 + reprs.append(repr(type)) + yield ValidationError( + f"{instance!r} is not of type {', '.join(reprs)}", + context=all_errors, + ) + + +def contains_draft6_draft7(validator, contains, instance, schema): + if not validator.is_type(instance, "array"): + return + + if not any( + validator.evolve(schema=contains).is_valid(element) + for element in instance + ): + yield ValidationError( + f"None of {instance!r} are valid under the given schema", + ) + + +def recursiveRef(validator, recursiveRef, instance, schema): + resolved = lookup_recursive_ref(validator._resolver) + yield from validator.descend( + instance, + resolved.contents, + resolver=resolved.resolver, + ) + + +def find_evaluated_item_indexes_by_schema(validator, instance, schema): + """ + Get all indexes of items that get evaluated under the current schema. + + Covers all keywords related to unevaluatedItems: items, prefixItems, if, + then, else, contains, unevaluatedItems, allOf, oneOf, anyOf + """ + if validator.is_type(schema, "boolean"): + return [] + evaluated_indexes = [] + + ref = schema.get("$ref") + if ref is not None: + resolved = validator._resolver.lookup(ref) + evaluated_indexes.extend( + find_evaluated_item_indexes_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + if "$recursiveRef" in schema: + resolved = lookup_recursive_ref(validator._resolver) + evaluated_indexes.extend( + find_evaluated_item_indexes_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + if "items" in schema: + if "additionalItems" in schema: + return list(range(len(instance))) + + if validator.is_type(schema["items"], "object"): + return list(range(len(instance))) + evaluated_indexes += list(range(len(schema["items"]))) + + if "if" in schema: + if validator.evolve(schema=schema["if"]).is_valid(instance): + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, schema["if"], + ) + if "then" in schema: + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, schema["then"], + ) + elif "else" in schema: + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, schema["else"], + ) + + for keyword in ["contains", "unevaluatedItems"]: + if keyword in schema: + for k, v in enumerate(instance): + if validator.evolve(schema=schema[keyword]).is_valid(v): + evaluated_indexes.append(k) + + for keyword in ["allOf", "oneOf", "anyOf"]: + if keyword in schema: + for subschema in schema[keyword]: + errs = next(validator.descend(instance, subschema), None) + if errs is None: + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, subschema, + ) + + return evaluated_indexes + + +def unevaluatedItems_draft2019(validator, unevaluatedItems, instance, schema): + if not validator.is_type(instance, "array"): + return + evaluated_item_indexes = find_evaluated_item_indexes_by_schema( + validator, instance, schema, + ) + unevaluated_items = [ + item for index, item in enumerate(instance) + if index not in evaluated_item_indexes + ] + if unevaluated_items: + error = "Unevaluated items are not allowed (%s %s unexpected)" + yield ValidationError(error % _utils.extras_msg(unevaluated_items)) + + +def find_evaluated_property_keys_by_schema(validator, instance, schema): + if validator.is_type(schema, "boolean"): + return [] + evaluated_keys = [] + + ref = schema.get("$ref") + if ref is not None: + resolved = validator._resolver.lookup(ref) + evaluated_keys.extend( + find_evaluated_property_keys_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + if "$recursiveRef" in schema: + resolved = lookup_recursive_ref(validator._resolver) + evaluated_keys.extend( + find_evaluated_property_keys_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + for keyword in [ + "properties", "additionalProperties", "unevaluatedProperties", + ]: + if keyword in schema: + schema_value = schema[keyword] + if validator.is_type(schema_value, "boolean") and schema_value: + evaluated_keys += instance.keys() + + elif validator.is_type(schema_value, "object"): + for property in schema_value: + if property in instance: + evaluated_keys.append(property) + + if "patternProperties" in schema: + for property in instance: + for pattern in schema["patternProperties"]: + if re.search(pattern, property): + evaluated_keys.append(property) + + if "dependentSchemas" in schema: + for property, subschema in schema["dependentSchemas"].items(): + if property not in instance: + continue + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, subschema, + ) + + for keyword in ["allOf", "oneOf", "anyOf"]: + if keyword in schema: + for subschema in schema[keyword]: + errs = next(validator.descend(instance, subschema), None) + if errs is None: + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, subschema, + ) + + if "if" in schema: + if validator.evolve(schema=schema["if"]).is_valid(instance): + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, schema["if"], + ) + if "then" in schema: + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, schema["then"], + ) + elif "else" in schema: + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, schema["else"], + ) + + return evaluated_keys + + +def unevaluatedProperties_draft2019(validator, uP, instance, schema): + if not validator.is_type(instance, "object"): + return + evaluated_keys = find_evaluated_property_keys_by_schema( + validator, instance, schema, + ) + unevaluated_keys = [] + for property in instance: + if property not in evaluated_keys: + for _ in validator.descend( + instance[property], + uP, + path=property, + schema_path=property, + ): + # FIXME: Include context for each unevaluated property + # indicating why it's invalid under the subschema. + unevaluated_keys.append(property) # noqa: PERF401 + + if unevaluated_keys: + if uP is False: + error = "Unevaluated properties are not allowed (%s %s unexpected)" + extras = sorted(unevaluated_keys, key=str) + yield ValidationError(error % _utils.extras_msg(extras)) + else: + error = ( + "Unevaluated properties are not valid under " + "the given schema (%s %s unevaluated and invalid)" + ) + yield ValidationError(error % _utils.extras_msg(unevaluated_keys)) diff --git a/venv/lib/python3.12/site-packages/jsonschema/_types.py b/venv/lib/python3.12/site-packages/jsonschema/_types.py new file mode 100644 index 0000000..fcfe3f0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/_types.py @@ -0,0 +1,204 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +import numbers + +from attrs import evolve, field, frozen +from rpds import HashTrieMap + +from jsonschema.exceptions import UndefinedTypeCheck + +if TYPE_CHECKING: + from collections.abc import Callable, Mapping + from typing import Any + + +# unfortunately, the type of HashTrieMap is generic, and if used as an attrs +# converter, the generic type is presented to mypy, which then fails to match +# the concrete type of a type checker mapping +# this "do nothing" wrapper presents the correct information to mypy +def _typed_map_converter( + init_val: Mapping[str, Callable[[TypeChecker, Any], bool]], +) -> HashTrieMap[str, Callable[[TypeChecker, Any], bool]]: + return HashTrieMap.convert(init_val) + + +def is_array(checker, instance): + return isinstance(instance, list) + + +def is_bool(checker, instance): + return isinstance(instance, bool) + + +def is_integer(checker, instance): + # bool inherits from int, so ensure bools aren't reported as ints + if isinstance(instance, bool): + return False + return isinstance(instance, int) + + +def is_null(checker, instance): + return instance is None + + +def is_number(checker, instance): + # bool inherits from int, so ensure bools aren't reported as ints + if isinstance(instance, bool): + return False + return isinstance(instance, numbers.Number) + + +def is_object(checker, instance): + return isinstance(instance, dict) + + +def is_string(checker, instance): + return isinstance(instance, str) + + +def is_any(checker, instance): + return True + + +@frozen(repr=False) +class TypeChecker: + """ + A :kw:`type` property checker. + + A `TypeChecker` performs type checking for a `Validator`, converting + between the defined JSON Schema types and some associated Python types or + objects. + + Modifying the behavior just mentioned by redefining which Python objects + are considered to be of which JSON Schema types can be done using + `TypeChecker.redefine` or `TypeChecker.redefine_many`, and types can be + removed via `TypeChecker.remove`. Each of these return a new `TypeChecker`. + + Arguments: + + type_checkers: + + The initial mapping of types to their checking functions. + + """ + + _type_checkers: HashTrieMap[ + str, Callable[[TypeChecker, Any], bool], + ] = field(default=HashTrieMap(), converter=_typed_map_converter) + + def __repr__(self): + types = ", ".join(repr(k) for k in sorted(self._type_checkers)) + return f"<{self.__class__.__name__} types={{{types}}}>" + + def is_type(self, instance, type: str) -> bool: + """ + Check if the instance is of the appropriate type. + + Arguments: + + instance: + + The instance to check + + type: + + The name of the type that is expected. + + Raises: + + `jsonschema.exceptions.UndefinedTypeCheck`: + + if ``type`` is unknown to this object. + + """ + try: + fn = self._type_checkers[type] + except KeyError: + raise UndefinedTypeCheck(type) from None + + return fn(self, instance) + + def redefine(self, type: str, fn) -> TypeChecker: + """ + Produce a new checker with the given type redefined. + + Arguments: + + type: + + The name of the type to check. + + fn (collections.abc.Callable): + + A callable taking exactly two parameters - the type + checker calling the function and the instance to check. + The function should return true if instance is of this + type and false otherwise. + + """ + return self.redefine_many({type: fn}) + + def redefine_many(self, definitions=()) -> TypeChecker: + """ + Produce a new checker with the given types redefined. + + Arguments: + + definitions (dict): + + A dictionary mapping types to their checking functions. + + """ + type_checkers = self._type_checkers.update(definitions) + return evolve(self, type_checkers=type_checkers) + + def remove(self, *types) -> TypeChecker: + """ + Produce a new checker with the given types forgotten. + + Arguments: + + types: + + the names of the types to remove. + + Raises: + + `jsonschema.exceptions.UndefinedTypeCheck`: + + if any given type is unknown to this object + + """ + type_checkers = self._type_checkers + for each in types: + try: + type_checkers = type_checkers.remove(each) + except KeyError: + raise UndefinedTypeCheck(each) from None + return evolve(self, type_checkers=type_checkers) + + +draft3_type_checker = TypeChecker( + { + "any": is_any, + "array": is_array, + "boolean": is_bool, + "integer": is_integer, + "object": is_object, + "null": is_null, + "number": is_number, + "string": is_string, + }, +) +draft4_type_checker = draft3_type_checker.remove("any") +draft6_type_checker = draft4_type_checker.redefine( + "integer", + lambda checker, instance: ( + is_integer(checker, instance) + or (isinstance(instance, float) and instance.is_integer()) + ), +) +draft7_type_checker = draft6_type_checker +draft201909_type_checker = draft7_type_checker +draft202012_type_checker = draft201909_type_checker diff --git a/venv/lib/python3.12/site-packages/jsonschema/_typing.py b/venv/lib/python3.12/site-packages/jsonschema/_typing.py new file mode 100644 index 0000000..f8dda63 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/_typing.py @@ -0,0 +1,29 @@ +""" +Some (initially private) typing helpers for jsonschema's types. +""" +from collections.abc import Callable, Iterable +from typing import Any, Protocol + +import referencing.jsonschema + +from jsonschema.protocols import Validator + + +class SchemaKeywordValidator(Protocol): + def __call__( + self, + validator: Validator, + value: Any, + instance: Any, + schema: referencing.jsonschema.Schema, + ) -> None: + ... + + +id_of = Callable[[referencing.jsonschema.Schema], str | None] + + +ApplicableValidators = Callable[ + [referencing.jsonschema.Schema], + Iterable[tuple[str, Any]], +] diff --git a/venv/lib/python3.12/site-packages/jsonschema/_utils.py b/venv/lib/python3.12/site-packages/jsonschema/_utils.py new file mode 100644 index 0000000..84a0965 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/_utils.py @@ -0,0 +1,355 @@ +from collections.abc import Mapping, MutableMapping, Sequence +from urllib.parse import urlsplit +import itertools +import re + + +class URIDict(MutableMapping): + """ + Dictionary which uses normalized URIs as keys. + """ + + def normalize(self, uri): + return urlsplit(uri).geturl() + + def __init__(self, *args, **kwargs): + self.store = dict() + self.store.update(*args, **kwargs) + + def __getitem__(self, uri): + return self.store[self.normalize(uri)] + + def __setitem__(self, uri, value): + self.store[self.normalize(uri)] = value + + def __delitem__(self, uri): + del self.store[self.normalize(uri)] + + def __iter__(self): + return iter(self.store) + + def __len__(self): # pragma: no cover -- untested, but to be removed + return len(self.store) + + def __repr__(self): # pragma: no cover -- untested, but to be removed + return repr(self.store) + + +class Unset: + """ + An as-of-yet unset attribute or unprovided default parameter. + """ + + def __repr__(self): # pragma: no cover + return "" + + +def format_as_index(container, indices): + """ + Construct a single string containing indexing operations for the indices. + + For example for a container ``bar``, [1, 2, "foo"] -> bar[1][2]["foo"] + + Arguments: + + container (str): + + A word to use for the thing being indexed + + indices (sequence): + + The indices to format. + + """ + if not indices: + return container + return f"{container}[{']['.join(repr(index) for index in indices)}]" + + +def find_additional_properties(instance, schema): + """ + Return the set of additional properties for the given ``instance``. + + Weeds out properties that should have been validated by ``properties`` and + / or ``patternProperties``. + + Assumes ``instance`` is dict-like already. + """ + properties = schema.get("properties", {}) + patterns = "|".join(schema.get("patternProperties", {})) + for property in instance: + if property not in properties: + if patterns and re.search(patterns, property): + continue + yield property + + +def extras_msg(extras): + """ + Create an error message for extra items or properties. + """ + verb = "was" if len(extras) == 1 else "were" + return ", ".join(repr(extra) for extra in extras), verb + + +def ensure_list(thing): + """ + Wrap ``thing`` in a list if it's a single str. + + Otherwise, return it unchanged. + """ + if isinstance(thing, str): + return [thing] + return thing + + +def _mapping_equal(one, two): + """ + Check if two mappings are equal using the semantics of `equal`. + """ + if len(one) != len(two): + return False + return all( + key in two and equal(value, two[key]) + for key, value in one.items() + ) + + +def _sequence_equal(one, two): + """ + Check if two sequences are equal using the semantics of `equal`. + """ + if len(one) != len(two): + return False + return all(equal(i, j) for i, j in zip(one, two)) + + +def equal(one, two): + """ + Check if two things are equal evading some Python type hierarchy semantics. + + Specifically in JSON Schema, evade `bool` inheriting from `int`, + recursing into sequences to do the same. + """ + if one is two: + return True + if isinstance(one, str) or isinstance(two, str): + return one == two + if isinstance(one, Sequence) and isinstance(two, Sequence): + return _sequence_equal(one, two) + if isinstance(one, Mapping) and isinstance(two, Mapping): + return _mapping_equal(one, two) + return unbool(one) == unbool(two) + + +def unbool(element, true=object(), false=object()): + """ + A hack to make True and 1 and False and 0 unique for ``uniq``. + """ + if element is True: + return true + elif element is False: + return false + return element + + +def uniq(container): + """ + Check if all of a container's elements are unique. + + Tries to rely on the container being recursively sortable, or otherwise + falls back on (slow) brute force. + """ + try: + sort = sorted(unbool(i) for i in container) + sliced = itertools.islice(sort, 1, None) + + for i, j in zip(sort, sliced): + if equal(i, j): + return False + + except (NotImplementedError, TypeError): + seen = [] + for e in container: + e = unbool(e) + + for i in seen: + if equal(i, e): + return False + + seen.append(e) + return True + + +def find_evaluated_item_indexes_by_schema(validator, instance, schema): + """ + Get all indexes of items that get evaluated under the current schema. + + Covers all keywords related to unevaluatedItems: items, prefixItems, if, + then, else, contains, unevaluatedItems, allOf, oneOf, anyOf + """ + if validator.is_type(schema, "boolean"): + return [] + evaluated_indexes = [] + + if "items" in schema: + return list(range(len(instance))) + + ref = schema.get("$ref") + if ref is not None: + resolved = validator._resolver.lookup(ref) + evaluated_indexes.extend( + find_evaluated_item_indexes_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + dynamicRef = schema.get("$dynamicRef") + if dynamicRef is not None: + resolved = validator._resolver.lookup(dynamicRef) + evaluated_indexes.extend( + find_evaluated_item_indexes_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + if "prefixItems" in schema: + evaluated_indexes += list(range(len(schema["prefixItems"]))) + + if "if" in schema: + if validator.evolve(schema=schema["if"]).is_valid(instance): + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, schema["if"], + ) + if "then" in schema: + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, schema["then"], + ) + elif "else" in schema: + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, schema["else"], + ) + + for keyword in ["contains", "unevaluatedItems"]: + if keyword in schema: + for k, v in enumerate(instance): + if validator.evolve(schema=schema[keyword]).is_valid(v): + evaluated_indexes.append(k) + + for keyword in ["allOf", "oneOf", "anyOf"]: + if keyword in schema: + for subschema in schema[keyword]: + errs = next(validator.descend(instance, subschema), None) + if errs is None: + evaluated_indexes += find_evaluated_item_indexes_by_schema( + validator, instance, subschema, + ) + + return evaluated_indexes + + +def find_evaluated_property_keys_by_schema(validator, instance, schema): + """ + Get all keys of items that get evaluated under the current schema. + + Covers all keywords related to unevaluatedProperties: properties, + additionalProperties, unevaluatedProperties, patternProperties, + dependentSchemas, allOf, oneOf, anyOf, if, then, else + """ + if validator.is_type(schema, "boolean"): + return [] + evaluated_keys = [] + + ref = schema.get("$ref") + if ref is not None: + resolved = validator._resolver.lookup(ref) + evaluated_keys.extend( + find_evaluated_property_keys_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + dynamicRef = schema.get("$dynamicRef") + if dynamicRef is not None: + resolved = validator._resolver.lookup(dynamicRef) + evaluated_keys.extend( + find_evaluated_property_keys_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + properties = schema.get("properties") + if validator.is_type(properties, "object"): + evaluated_keys += properties.keys() & instance.keys() + + for keyword in ["additionalProperties", "unevaluatedProperties"]: + if (subschema := schema.get(keyword)) is None: + continue + evaluated_keys += ( + key + for key, value in instance.items() + if is_valid(validator.descend(value, subschema)) + ) + + if "patternProperties" in schema: + for property in instance: + for pattern in schema["patternProperties"]: + if re.search(pattern, property): + evaluated_keys.append(property) + + if "dependentSchemas" in schema: + for property, subschema in schema["dependentSchemas"].items(): + if property not in instance: + continue + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, subschema, + ) + + for keyword in ["allOf", "oneOf", "anyOf"]: + for subschema in schema.get(keyword, []): + if not is_valid(validator.descend(instance, subschema)): + continue + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, subschema, + ) + + if "if" in schema: + if validator.evolve(schema=schema["if"]).is_valid(instance): + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, schema["if"], + ) + if "then" in schema: + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, schema["then"], + ) + elif "else" in schema: + evaluated_keys += find_evaluated_property_keys_by_schema( + validator, instance, schema["else"], + ) + + return evaluated_keys + + +def is_valid(errs_it): + """Whether there are no errors in the given iterator.""" + return next(errs_it, None) is None diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__init__.py b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__init__.py new file mode 100644 index 0000000..e3dcc68 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__init__.py @@ -0,0 +1,5 @@ +""" +Benchmarks for validation. + +This package is *not* public API. +""" diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5e3e817 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/const_vs_enum.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/const_vs_enum.cpython-312.pyc new file mode 100644 index 0000000..9a312a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/const_vs_enum.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/contains.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/contains.cpython-312.pyc new file mode 100644 index 0000000..98cab80 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/contains.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/import_benchmark.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/import_benchmark.cpython-312.pyc new file mode 100644 index 0000000..899ff42 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/import_benchmark.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/issue232.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/issue232.cpython-312.pyc new file mode 100644 index 0000000..4b948fa Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/issue232.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/json_schema_test_suite.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/json_schema_test_suite.cpython-312.pyc new file mode 100644 index 0000000..6f597d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/json_schema_test_suite.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/nested_schemas.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/nested_schemas.cpython-312.pyc new file mode 100644 index 0000000..9919203 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/nested_schemas.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/subcomponents.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/subcomponents.cpython-312.pyc new file mode 100644 index 0000000..a399f7e Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/subcomponents.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/unused_registry.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/unused_registry.cpython-312.pyc new file mode 100644 index 0000000..6ad14ca Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/unused_registry.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/useless_applicator_schemas.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/useless_applicator_schemas.cpython-312.pyc new file mode 100644 index 0000000..6c6f78f Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/useless_applicator_schemas.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/useless_keywords.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/useless_keywords.cpython-312.pyc new file mode 100644 index 0000000..b6eb6ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/useless_keywords.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/validator_creation.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/validator_creation.cpython-312.pyc new file mode 100644 index 0000000..17f4d55 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/__pycache__/validator_creation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/const_vs_enum.py b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/const_vs_enum.py new file mode 100644 index 0000000..c6fecd1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/const_vs_enum.py @@ -0,0 +1,30 @@ +""" +A benchmark for comparing equivalent validation of `const` and `enum`. +""" + +from pyperf import Runner + +from jsonschema import Draft202012Validator + +value = [37] * 100 +const_schema = {"const": list(value)} +enum_schema = {"enum": [list(value)]} + +valid = list(value) +invalid = [*valid, 73] + +const = Draft202012Validator(const_schema) +enum = Draft202012Validator(enum_schema) + +assert const.is_valid(valid) +assert enum.is_valid(valid) +assert not const.is_valid(invalid) +assert not enum.is_valid(invalid) + + +if __name__ == "__main__": + runner = Runner() + runner.bench_func("const valid", lambda: const.is_valid(valid)) + runner.bench_func("const invalid", lambda: const.is_valid(invalid)) + runner.bench_func("enum valid", lambda: enum.is_valid(valid)) + runner.bench_func("enum invalid", lambda: enum.is_valid(invalid)) diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/contains.py b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/contains.py new file mode 100644 index 0000000..739cd04 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/contains.py @@ -0,0 +1,28 @@ +""" +A benchmark for validation of the `contains` keyword. +""" + +from pyperf import Runner + +from jsonschema import Draft202012Validator + +schema = { + "type": "array", + "contains": {"const": 37}, +} +validator = Draft202012Validator(schema) + +size = 1000 +beginning = [37] + [0] * (size - 1) +middle = [0] * (size // 2) + [37] + [0] * (size // 2) +end = [0] * (size - 1) + [37] +invalid = [0] * size + + +if __name__ == "__main__": + runner = Runner() + runner.bench_func("baseline", lambda: validator.is_valid([])) + runner.bench_func("beginning", lambda: validator.is_valid(beginning)) + runner.bench_func("middle", lambda: validator.is_valid(middle)) + runner.bench_func("end", lambda: validator.is_valid(end)) + runner.bench_func("invalid", lambda: validator.is_valid(invalid)) diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/import_benchmark.py b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/import_benchmark.py new file mode 100644 index 0000000..59fa921 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/import_benchmark.py @@ -0,0 +1,31 @@ +""" +A benchmark which measures the import time of jsonschema +""" + +import subprocess +import sys + + +def import_time(loops): + total_us = 0 + for _ in range(loops): + p = subprocess.run( # noqa: S603 (arguments are static) + [sys.executable, "-X", "importtime", "-c", "import jsonschema"], + stderr=subprocess.PIPE, + stdout=subprocess.DEVNULL, + check=True, + ) + + line = p.stderr.splitlines()[-1] + field = line.split(b"|")[-2].strip() + us = int(field) # microseconds + total_us += us + + # pyperf expects seconds + return total_us / 1_000_000.0 + +if __name__ == "__main__": + from pyperf import Runner + runner = Runner() + + runner.bench_time_func("Import time (cumulative)", import_time) diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/issue232.py b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/issue232.py new file mode 100644 index 0000000..efd0715 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/issue232.py @@ -0,0 +1,25 @@ +""" +A performance benchmark using the example from issue #232. + +See https://github.com/python-jsonschema/jsonschema/pull/232. +""" +from pathlib import Path + +from pyperf import Runner +from referencing import Registry + +from jsonschema.tests._suite import Version +import jsonschema + +issue232 = Version( + path=Path(__file__).parent / "issue232", + remotes=Registry(), + name="issue232", +) + + +if __name__ == "__main__": + issue232.benchmark( + runner=Runner(), + Validator=jsonschema.Draft4Validator, + ) diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/issue232/issue.json b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/issue232/issue.json new file mode 100644 index 0000000..804c340 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/issue232/issue.json @@ -0,0 +1,2653 @@ +[ + { + "description": "Petstore", + "schema": { + "title": "A JSON Schema for Swagger 2.0 API.", + "id": "http://swagger.io/v2/schema.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "required": [ + "swagger", + "info", + "paths" + ], + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "swagger": { + "type": "string", + "enum": [ + "2.0" + ], + "description": "The Swagger version of this document." + }, + "info": { + "$ref": "#/definitions/info" + }, + "host": { + "type": "string", + "pattern": "^[^{}/ :\\\\]+(?::\\d+)?$", + "description": "The host (name or ip) of the API. Example: 'swagger.io'" + }, + "basePath": { + "type": "string", + "pattern": "^/", + "description": "The base path to the API. Example: '/api'." + }, + "schemes": { + "$ref": "#/definitions/schemesList" + }, + "consumes": { + "description": "A list of MIME types accepted by the API.", + "allOf": [ + { + "$ref": "#/definitions/mediaTypeList" + } + ] + }, + "produces": { + "description": "A list of MIME types the API can produce.", + "allOf": [ + { + "$ref": "#/definitions/mediaTypeList" + } + ] + }, + "paths": { + "$ref": "#/definitions/paths" + }, + "definitions": { + "$ref": "#/definitions/definitions" + }, + "parameters": { + "$ref": "#/definitions/parameterDefinitions" + }, + "responses": { + "$ref": "#/definitions/responseDefinitions" + }, + "security": { + "$ref": "#/definitions/security" + }, + "securityDefinitions": { + "$ref": "#/definitions/securityDefinitions" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/tag" + }, + "uniqueItems": true + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + } + }, + "definitions": { + "info": { + "type": "object", + "description": "General information about the API.", + "required": [ + "version", + "title" + ], + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "title": { + "type": "string", + "description": "A unique and precise title of the API." + }, + "version": { + "type": "string", + "description": "A semantic version number of the API." + }, + "description": { + "type": "string", + "description": "A longer description of the API. Should be different from the title. GitHub Flavored Markdown is allowed." + }, + "termsOfService": { + "type": "string", + "description": "The terms of service for the API." + }, + "contact": { + "$ref": "#/definitions/contact" + }, + "license": { + "$ref": "#/definitions/license" + } + } + }, + "contact": { + "type": "object", + "description": "Contact information for the owners of the API.", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The identifying name of the contact person/organization." + }, + "url": { + "type": "string", + "description": "The URL pointing to the contact information.", + "format": "uri" + }, + "email": { + "type": "string", + "description": "The email address of the contact person/organization.", + "format": "email" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "license": { + "type": "object", + "required": [ + "name" + ], + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The name of the license type. It's encouraged to use an OSI compatible license." + }, + "url": { + "type": "string", + "description": "The URL pointing to the license.", + "format": "uri" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "paths": { + "type": "object", + "description": "Relative paths to the individual endpoints. They must be relative to the 'basePath'.", + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + }, + "^/": { + "$ref": "#/definitions/pathItem" + } + }, + "additionalProperties": false + }, + "definitions": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/schema" + }, + "description": "One or more JSON objects describing the schemas being consumed and produced by the API." + }, + "parameterDefinitions": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/parameter" + }, + "description": "One or more JSON representations for parameters" + }, + "responseDefinitions": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/response" + }, + "description": "One or more JSON representations for parameters" + }, + "externalDocs": { + "type": "object", + "additionalProperties": false, + "description": "information about external documentation", + "required": [ + "url" + ], + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "examples": { + "type": "object", + "additionalProperties": true + }, + "mimeType": { + "type": "string", + "description": "The MIME type of the HTTP message." + }, + "operation": { + "type": "object", + "required": [ + "responses" + ], + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "summary": { + "type": "string", + "description": "A brief summary of the operation." + }, + "description": { + "type": "string", + "description": "A longer description of the operation, GitHub Flavored Markdown is allowed." + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "operationId": { + "type": "string", + "description": "A unique identifier of the operation." + }, + "produces": { + "description": "A list of MIME types the API can produce.", + "allOf": [ + { + "$ref": "#/definitions/mediaTypeList" + } + ] + }, + "consumes": { + "description": "A list of MIME types the API can consume.", + "allOf": [ + { + "$ref": "#/definitions/mediaTypeList" + } + ] + }, + "parameters": { + "$ref": "#/definitions/parametersList" + }, + "responses": { + "$ref": "#/definitions/responses" + }, + "schemes": { + "$ref": "#/definitions/schemesList" + }, + "deprecated": { + "type": "boolean", + "default": false + }, + "security": { + "$ref": "#/definitions/security" + } + } + }, + "pathItem": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "$ref": { + "type": "string" + }, + "get": { + "$ref": "#/definitions/operation" + }, + "put": { + "$ref": "#/definitions/operation" + }, + "post": { + "$ref": "#/definitions/operation" + }, + "delete": { + "$ref": "#/definitions/operation" + }, + "options": { + "$ref": "#/definitions/operation" + }, + "head": { + "$ref": "#/definitions/operation" + }, + "patch": { + "$ref": "#/definitions/operation" + }, + "parameters": { + "$ref": "#/definitions/parametersList" + } + } + }, + "responses": { + "type": "object", + "description": "Response objects names can either be any valid HTTP status code or 'default'.", + "minProperties": 1, + "additionalProperties": false, + "patternProperties": { + "^([0-9]{3})$|^(default)$": { + "$ref": "#/definitions/responseValue" + }, + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "not": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + } + }, + "responseValue": { + "oneOf": [ + { + "$ref": "#/definitions/response" + }, + { + "$ref": "#/definitions/jsonReference" + } + ] + }, + "response": { + "type": "object", + "required": [ + "description" + ], + "properties": { + "description": { + "type": "string" + }, + "schema": { + "oneOf": [ + { + "$ref": "#/definitions/schema" + }, + { + "$ref": "#/definitions/fileSchema" + } + ] + }, + "headers": { + "$ref": "#/definitions/headers" + }, + "examples": { + "$ref": "#/definitions/examples" + } + }, + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/header" + } + }, + "header": { + "type": "object", + "additionalProperties": false, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "string", + "number", + "integer", + "boolean", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormat" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "vendorExtension": { + "description": "Any property starting with x- is valid.", + "additionalProperties": true, + "additionalItems": true + }, + "bodyParameter": { + "type": "object", + "required": [ + "name", + "in", + "schema" + ], + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "body" + ] + }, + "required": { + "type": "boolean", + "description": "Determines whether or not this parameter is required or optional.", + "default": false + }, + "schema": { + "$ref": "#/definitions/schema" + } + }, + "additionalProperties": false + }, + "headerParameterSubSchema": { + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "required": { + "type": "boolean", + "description": "Determines whether or not this parameter is required or optional.", + "default": false + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "header" + ] + }, + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "integer", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormat" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + } + }, + "queryParameterSubSchema": { + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "required": { + "type": "boolean", + "description": "Determines whether or not this parameter is required or optional.", + "default": false + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "query" + ] + }, + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "allowEmptyValue": { + "type": "boolean", + "default": false, + "description": "allows sending a parameter by name only or with an empty value." + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "integer", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormatWithMulti" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + } + }, + "formDataParameterSubSchema": { + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "required": { + "type": "boolean", + "description": "Determines whether or not this parameter is required or optional.", + "default": false + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "formData" + ] + }, + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "allowEmptyValue": { + "type": "boolean", + "default": false, + "description": "allows sending a parameter by name only or with an empty value." + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "integer", + "array", + "file" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormatWithMulti" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + } + }, + "pathParameterSubSchema": { + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "required": [ + "required" + ], + "properties": { + "required": { + "type": "boolean", + "enum": [ + true + ], + "description": "Determines whether or not this parameter is required or optional." + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "path" + ] + }, + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "integer", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormat" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + } + }, + "nonBodyParameter": { + "type": "object", + "required": [ + "name", + "in", + "type" + ], + "oneOf": [ + { + "$ref": "#/definitions/headerParameterSubSchema" + }, + { + "$ref": "#/definitions/formDataParameterSubSchema" + }, + { + "$ref": "#/definitions/queryParameterSubSchema" + }, + { + "$ref": "#/definitions/pathParameterSubSchema" + } + ] + }, + "parameter": { + "oneOf": [ + { + "$ref": "#/definitions/bodyParameter" + }, + { + "$ref": "#/definitions/nonBodyParameter" + } + ] + }, + "schema": { + "type": "object", + "description": "A deterministic version of a JSON Schema object.", + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "$ref": { + "type": "string" + }, + "format": { + "type": "string" + }, + "title": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/title" + }, + "description": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/description" + }, + "default": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/default" + }, + "multipleOf": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" + }, + "maximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" + }, + "exclusiveMaximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" + }, + "minimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" + }, + "exclusiveMinimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" + }, + "maxLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "pattern": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" + }, + "maxItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "uniqueItems": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" + }, + "maxProperties": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minProperties": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "required": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" + }, + "enum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" + }, + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/definitions/schema" + }, + { + "type": "boolean" + } + ], + "default": {} + }, + "type": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/type" + }, + "items": { + "anyOf": [ + { + "$ref": "#/definitions/schema" + }, + { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/schema" + } + } + ], + "default": {} + }, + "allOf": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/schema" + } + }, + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/schema" + }, + "default": {} + }, + "discriminator": { + "type": "string" + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "xml": { + "$ref": "#/definitions/xml" + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "example": {} + }, + "additionalProperties": false + }, + "fileSchema": { + "type": "object", + "description": "A deterministic version of a JSON Schema object.", + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "required": [ + "type" + ], + "properties": { + "format": { + "type": "string" + }, + "title": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/title" + }, + "description": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/description" + }, + "default": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/default" + }, + "required": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" + }, + "type": { + "type": "string", + "enum": [ + "file" + ] + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "example": {} + }, + "additionalProperties": false + }, + "primitivesItems": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "string", + "number", + "integer", + "boolean", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormat" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "security": { + "type": "array", + "items": { + "$ref": "#/definitions/securityRequirement" + }, + "uniqueItems": true + }, + "securityRequirement": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + }, + "xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "prefix": { + "type": "string" + }, + "attribute": { + "type": "boolean", + "default": false + }, + "wrapped": { + "type": "boolean", + "default": false + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "tag": { + "type": "object", + "additionalProperties": false, + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "securityDefinitions": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/definitions/basicAuthenticationSecurity" + }, + { + "$ref": "#/definitions/apiKeySecurity" + }, + { + "$ref": "#/definitions/oauth2ImplicitSecurity" + }, + { + "$ref": "#/definitions/oauth2PasswordSecurity" + }, + { + "$ref": "#/definitions/oauth2ApplicationSecurity" + }, + { + "$ref": "#/definitions/oauth2AccessCodeSecurity" + } + ] + } + }, + "basicAuthenticationSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "basic" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "apiKeySecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "name", + "in" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "apiKey" + ] + }, + "name": { + "type": "string" + }, + "in": { + "type": "string", + "enum": [ + "header", + "query" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2ImplicitSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "flow", + "authorizationUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flow": { + "type": "string", + "enum": [ + "implicit" + ] + }, + "scopes": { + "$ref": "#/definitions/oauth2Scopes" + }, + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2PasswordSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "flow", + "tokenUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flow": { + "type": "string", + "enum": [ + "password" + ] + }, + "scopes": { + "$ref": "#/definitions/oauth2Scopes" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2ApplicationSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "flow", + "tokenUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flow": { + "type": "string", + "enum": [ + "application" + ] + }, + "scopes": { + "$ref": "#/definitions/oauth2Scopes" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2AccessCodeSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "flow", + "authorizationUrl", + "tokenUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flow": { + "type": "string", + "enum": [ + "accessCode" + ] + }, + "scopes": { + "$ref": "#/definitions/oauth2Scopes" + }, + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2Scopes": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "mediaTypeList": { + "type": "array", + "items": { + "$ref": "#/definitions/mimeType" + }, + "uniqueItems": true + }, + "parametersList": { + "type": "array", + "description": "The parameters needed to send a valid API call.", + "additionalItems": false, + "items": { + "oneOf": [ + { + "$ref": "#/definitions/parameter" + }, + { + "$ref": "#/definitions/jsonReference" + } + ] + }, + "uniqueItems": true + }, + "schemesList": { + "type": "array", + "description": "The transfer protocol of the API.", + "items": { + "type": "string", + "enum": [ + "http", + "https", + "ws", + "wss" + ] + }, + "uniqueItems": true + }, + "collectionFormat": { + "type": "string", + "enum": [ + "csv", + "ssv", + "tsv", + "pipes" + ], + "default": "csv" + }, + "collectionFormatWithMulti": { + "type": "string", + "enum": [ + "csv", + "ssv", + "tsv", + "pipes", + "multi" + ], + "default": "csv" + }, + "title": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/title" + }, + "description": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/description" + }, + "default": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/default" + }, + "multipleOf": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" + }, + "maximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" + }, + "exclusiveMaximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" + }, + "minimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" + }, + "exclusiveMinimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" + }, + "maxLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "pattern": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" + }, + "maxItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "uniqueItems": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" + }, + "enum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" + }, + "jsonReference": { + "type": "object", + "required": [ + "$ref" + ], + "additionalProperties": false, + "properties": { + "$ref": { + "type": "string" + } + } + } + } + }, + "tests": [ + { + "description": "Example petsore", + "data": { + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Pet" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + } + ] + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10.0, + "minimum": 1.0, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1.0, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } + }, + "valid": true + } + ] + } +] diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/json_schema_test_suite.py b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/json_schema_test_suite.py new file mode 100644 index 0000000..905fb6a --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/json_schema_test_suite.py @@ -0,0 +1,12 @@ +""" +A performance benchmark using the official test suite. + +This benchmarks jsonschema using every valid example in the +JSON-Schema-Test-Suite. It will take some time to complete. +""" +from pyperf import Runner + +from jsonschema.tests._suite import Suite + +if __name__ == "__main__": + Suite().benchmark(runner=Runner()) diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/nested_schemas.py b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/nested_schemas.py new file mode 100644 index 0000000..b025c47 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/nested_schemas.py @@ -0,0 +1,56 @@ +""" +Validating highly nested schemas shouldn't cause exponential time blowups. + +See https://github.com/python-jsonschema/jsonschema/issues/1097. +""" +from itertools import cycle + +from jsonschema.validators import validator_for + +metaschemaish = { + "$id": "https://example.com/draft/2020-12/schema/strict", + "$schema": "https://json-schema.org/draft/2020-12/schema", + + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": True, + "https://json-schema.org/draft/2020-12/vocab/applicator": True, + "https://json-schema.org/draft/2020-12/vocab/unevaluated": True, + "https://json-schema.org/draft/2020-12/vocab/validation": True, + "https://json-schema.org/draft/2020-12/vocab/meta-data": True, + "https://json-schema.org/draft/2020-12/vocab/format-annotation": True, + "https://json-schema.org/draft/2020-12/vocab/content": True, + }, + "$dynamicAnchor": "meta", + + "$ref": "https://json-schema.org/draft/2020-12/schema", + "unevaluatedProperties": False, +} + + +def nested_schema(levels): + """ + Produce a schema which validates deeply nested objects and arrays. + """ + + names = cycle(["foo", "bar", "baz", "quux", "spam", "eggs"]) + schema = {"type": "object", "properties": {"ham": {"type": "string"}}} + for _, name in zip(range(levels - 1), names): + schema = {"type": "object", "properties": {name: schema}} + return schema + + +validator = validator_for(metaschemaish)(metaschemaish) + +if __name__ == "__main__": + from pyperf import Runner + runner = Runner() + + not_nested = nested_schema(levels=1) + runner.bench_func("not nested", lambda: validator.is_valid(not_nested)) + + for levels in range(1, 11, 3): + schema = nested_schema(levels=levels) + runner.bench_func( + f"nested * {levels}", + lambda schema=schema: validator.is_valid(schema), + ) diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/subcomponents.py b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/subcomponents.py new file mode 100644 index 0000000..6d78c7b --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/subcomponents.py @@ -0,0 +1,42 @@ +""" +A benchmark which tries to compare the possible slow subparts of validation. +""" +from referencing import Registry +from referencing.jsonschema import DRAFT202012 +from rpds import HashTrieMap, HashTrieSet + +from jsonschema import Draft202012Validator + +schema = { + "type": "array", + "minLength": 1, + "maxLength": 1, + "items": {"type": "integer"}, +} + +hmap = HashTrieMap() +hset = HashTrieSet() + +registry = Registry() + +v = Draft202012Validator(schema) + + +def registry_data_structures(): + return hmap.insert("foo", "bar"), hset.insert("foo") + + +def registry_add(): + resource = DRAFT202012.create_resource(schema) + return registry.with_resource(uri="urn:example", resource=resource) + + +if __name__ == "__main__": + from pyperf import Runner + runner = Runner() + + runner.bench_func("HashMap/HashSet insertion", registry_data_structures) + runner.bench_func("Registry insertion", registry_add) + runner.bench_func("Success", lambda: v.is_valid([1])) + runner.bench_func("Failure", lambda: v.is_valid(["foo"])) + runner.bench_func("Metaschema validation", lambda: v.check_schema(schema)) diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/unused_registry.py b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/unused_registry.py new file mode 100644 index 0000000..7b272c2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/unused_registry.py @@ -0,0 +1,35 @@ +""" +An unused schema registry should not cause slower validation. + +"Unused" here means one where no reference resolution is occurring anyhow. + +See https://github.com/python-jsonschema/jsonschema/issues/1088. +""" +from pyperf import Runner +from referencing import Registry +from referencing.jsonschema import DRAFT201909 + +from jsonschema import Draft201909Validator + +registry = Registry().with_resource( + "urn:example:foo", + DRAFT201909.create_resource({}), +) + +schema = {"$ref": "https://json-schema.org/draft/2019-09/schema"} +instance = {"maxLength": 4} + +no_registry = Draft201909Validator(schema) +with_useless_registry = Draft201909Validator(schema, registry=registry) + +if __name__ == "__main__": + runner = Runner() + + runner.bench_func( + "no registry", + lambda: no_registry.is_valid(instance), + ) + runner.bench_func( + "useless registry", + lambda: with_useless_registry.is_valid(instance), + ) diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/useless_applicator_schemas.py b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/useless_applicator_schemas.py new file mode 100644 index 0000000..f3229c0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/useless_applicator_schemas.py @@ -0,0 +1,106 @@ + +""" +A benchmark for validation of applicators containing lots of useless schemas. + +Signals a small possible optimization to remove all such schemas ahead of time. +""" + +from pyperf import Runner + +from jsonschema import Draft202012Validator as Validator + +NUM_USELESS = 100000 + +subschema = {"const": 37} + +valid = 37 +invalid = 12 + +baseline = Validator(subschema) + + +# These should be indistinguishable from just `subschema` +by_name = { + "single subschema": { + "anyOf": Validator({"anyOf": [subschema]}), + "allOf": Validator({"allOf": [subschema]}), + "oneOf": Validator({"oneOf": [subschema]}), + }, + "redundant subschemas": { + "anyOf": Validator({"anyOf": [subschema] * NUM_USELESS}), + "allOf": Validator({"allOf": [subschema] * NUM_USELESS}), + }, + "useless successful subschemas (beginning)": { + "anyOf": Validator({"anyOf": [subschema, *[True] * NUM_USELESS]}), + "allOf": Validator({"allOf": [subschema, *[True] * NUM_USELESS]}), + }, + "useless successful subschemas (middle)": { + "anyOf": Validator( + { + "anyOf": [ + *[True] * (NUM_USELESS // 2), + subschema, + *[True] * (NUM_USELESS // 2), + ], + }, + ), + "allOf": Validator( + { + "allOf": [ + *[True] * (NUM_USELESS // 2), + subschema, + *[True] * (NUM_USELESS // 2), + ], + }, + ), + }, + "useless successful subschemas (end)": { + "anyOf": Validator({"anyOf": [*[True] * NUM_USELESS, subschema]}), + "allOf": Validator({"allOf": [*[True] * NUM_USELESS, subschema]}), + }, + "useless failing subschemas (beginning)": { + "anyOf": Validator({"anyOf": [subschema, *[False] * NUM_USELESS]}), + "oneOf": Validator({"oneOf": [subschema, *[False] * NUM_USELESS]}), + }, + "useless failing subschemas (middle)": { + "anyOf": Validator( + { + "anyOf": [ + *[False] * (NUM_USELESS // 2), + subschema, + *[False] * (NUM_USELESS // 2), + ], + }, + ), + "oneOf": Validator( + { + "oneOf": [ + *[False] * (NUM_USELESS // 2), + subschema, + *[False] * (NUM_USELESS // 2), + ], + }, + ), + }, + "useless failing subschemas (end)": { + "anyOf": Validator({"anyOf": [*[False] * NUM_USELESS, subschema]}), + "oneOf": Validator({"oneOf": [*[False] * NUM_USELESS, subschema]}), + }, +} + +if __name__ == "__main__": + runner = Runner() + + runner.bench_func("baseline valid", lambda: baseline.is_valid(valid)) + runner.bench_func("baseline invalid", lambda: baseline.is_valid(invalid)) + + for group, applicators in by_name.items(): + for applicator, validator in applicators.items(): + runner.bench_func( + f"{group}: {applicator} valid", + lambda validator=validator: validator.is_valid(valid), + ) + runner.bench_func( + f"{group}: {applicator} invalid", + lambda validator=validator: validator.is_valid(invalid), + ) diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/useless_keywords.py b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/useless_keywords.py new file mode 100644 index 0000000..50f4359 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/useless_keywords.py @@ -0,0 +1,32 @@ +""" +A benchmark for validation of schemas containing lots of useless keywords. + +Checks we filter them out once, ahead of time. +""" + +from pyperf import Runner + +from jsonschema import Draft202012Validator + +NUM_USELESS = 100000 +schema = dict( + [ + ("not", {"const": 42}), + *((str(i), i) for i in range(NUM_USELESS)), + ("type", "integer"), + *((str(i), i) for i in range(NUM_USELESS, NUM_USELESS)), + ("minimum", 37), + ], +) +validator = Draft202012Validator(schema) + +valid = 3737 +invalid = 12 + + +if __name__ == "__main__": + runner = Runner() + runner.bench_func("beginning of schema", lambda: validator.is_valid(42)) + runner.bench_func("middle of schema", lambda: validator.is_valid("foo")) + runner.bench_func("end of schema", lambda: validator.is_valid(12)) + runner.bench_func("valid", lambda: validator.is_valid(3737)) diff --git a/venv/lib/python3.12/site-packages/jsonschema/benchmarks/validator_creation.py b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/validator_creation.py new file mode 100644 index 0000000..4baeb3a --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/benchmarks/validator_creation.py @@ -0,0 +1,14 @@ +from pyperf import Runner + +from jsonschema import Draft202012Validator + +schema = { + "type": "array", + "minLength": 1, + "maxLength": 1, + "items": {"type": "integer"}, +} + + +if __name__ == "__main__": + Runner().bench_func("validator creation", Draft202012Validator, schema) diff --git a/venv/lib/python3.12/site-packages/jsonschema/cli.py b/venv/lib/python3.12/site-packages/jsonschema/cli.py new file mode 100644 index 0000000..f3ca4d6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/cli.py @@ -0,0 +1,292 @@ +""" +The ``jsonschema`` command line. +""" + +from importlib import metadata +from json import JSONDecodeError +from pkgutil import resolve_name +from textwrap import dedent +import argparse +import json +import sys +import traceback +import warnings + +from attrs import define, field + +from jsonschema.exceptions import SchemaError +from jsonschema.validators import _RefResolver, validator_for + +warnings.warn( + ( + "The jsonschema CLI is deprecated and will be removed in a future " + "version. Please use check-jsonschema instead, which can be installed " + "from https://pypi.org/project/check-jsonschema/" + ), + DeprecationWarning, + stacklevel=2, +) + + +class _CannotLoadFile(Exception): + pass + + +@define +class _Outputter: + + _formatter = field() + _stdout = field() + _stderr = field() + + @classmethod + def from_arguments(cls, arguments, stdout, stderr): + if arguments["output"] == "plain": + formatter = _PlainFormatter(arguments["error_format"]) + elif arguments["output"] == "pretty": + formatter = _PrettyFormatter() + return cls(formatter=formatter, stdout=stdout, stderr=stderr) + + def load(self, path): + try: + file = open(path) # noqa: SIM115, PTH123 + except FileNotFoundError as error: + self.filenotfound_error(path=path, exc_info=sys.exc_info()) + raise _CannotLoadFile() from error + + with file: + try: + return json.load(file) + except JSONDecodeError as error: + self.parsing_error(path=path, exc_info=sys.exc_info()) + raise _CannotLoadFile() from error + + def filenotfound_error(self, **kwargs): + self._stderr.write(self._formatter.filenotfound_error(**kwargs)) + + def parsing_error(self, **kwargs): + self._stderr.write(self._formatter.parsing_error(**kwargs)) + + def validation_error(self, **kwargs): + self._stderr.write(self._formatter.validation_error(**kwargs)) + + def validation_success(self, **kwargs): + self._stdout.write(self._formatter.validation_success(**kwargs)) + + +@define +class _PrettyFormatter: + + _ERROR_MSG = dedent( + """\ + ===[{type}]===({path})=== + + {body} + ----------------------------- + """, + ) + _SUCCESS_MSG = "===[SUCCESS]===({path})===\n" + + def filenotfound_error(self, path, exc_info): + return self._ERROR_MSG.format( + path=path, + type="FileNotFoundError", + body=f"{path!r} does not exist.", + ) + + def parsing_error(self, path, exc_info): + exc_type, exc_value, exc_traceback = exc_info + exc_lines = "".join( + traceback.format_exception(exc_type, exc_value, exc_traceback), + ) + return self._ERROR_MSG.format( + path=path, + type=exc_type.__name__, + body=exc_lines, + ) + + def validation_error(self, instance_path, error): + return self._ERROR_MSG.format( + path=instance_path, + type=error.__class__.__name__, + body=error, + ) + + def validation_success(self, instance_path): + return self._SUCCESS_MSG.format(path=instance_path) + + +@define +class _PlainFormatter: + + _error_format = field() + + def filenotfound_error(self, path, exc_info): + return f"{path!r} does not exist.\n" + + def parsing_error(self, path, exc_info): + return "Failed to parse {}: {}\n".format( + "" if path == "" else repr(path), + exc_info[1], + ) + + def validation_error(self, instance_path, error): + return self._error_format.format(file_name=instance_path, error=error) + + def validation_success(self, instance_path): + return "" + + +def _resolve_name_with_default(name): + if "." not in name: + name = "jsonschema." + name + return resolve_name(name) + + +parser = argparse.ArgumentParser( + description="JSON Schema Validation CLI", +) +parser.add_argument( + "-i", "--instance", + action="append", + dest="instances", + help=""" + a path to a JSON instance (i.e. filename.json) to validate (may + be specified multiple times). If no instances are provided via this + option, one will be expected on standard input. + """, +) +parser.add_argument( + "-F", "--error-format", + help=""" + the format to use for each validation error message, specified + in a form suitable for str.format. This string will be passed + one formatted object named 'error' for each ValidationError. + Only provide this option when using --output=plain, which is the + default. If this argument is unprovided and --output=plain is + used, a simple default representation will be used. + """, +) +parser.add_argument( + "-o", "--output", + choices=["plain", "pretty"], + default="plain", + help=""" + an output format to use. 'plain' (default) will produce minimal + text with one line for each error, while 'pretty' will produce + more detailed human-readable output on multiple lines. + """, +) +parser.add_argument( + "-V", "--validator", + type=_resolve_name_with_default, + help=""" + the fully qualified object name of a validator to use, or, for + validators that are registered with jsonschema, simply the name + of the class. + """, +) +parser.add_argument( + "--base-uri", + help=""" + a base URI to assign to the provided schema, even if it does not + declare one (via e.g. $id). This option can be used if you wish to + resolve relative references to a particular URI (or local path) + """, +) +parser.add_argument( + "--version", + action="version", + version=metadata.version("jsonschema"), +) +parser.add_argument( + "schema", + help="the path to a JSON Schema to validate with (i.e. schema.json)", +) + + +def parse_args(args): # noqa: D103 + arguments = vars(parser.parse_args(args=args or ["--help"])) + if arguments["output"] != "plain" and arguments["error_format"]: + raise parser.error( + "--error-format can only be used with --output plain", + ) + if arguments["output"] == "plain" and arguments["error_format"] is None: + arguments["error_format"] = "{error.instance}: {error.message}\n" + return arguments + + +def _validate_instance(instance_path, instance, validator, outputter): + invalid = False + for error in validator.iter_errors(instance): + invalid = True + outputter.validation_error(instance_path=instance_path, error=error) + + if not invalid: + outputter.validation_success(instance_path=instance_path) + return invalid + + +def main(args=sys.argv[1:]): # noqa: D103 + sys.exit(run(arguments=parse_args(args=args))) + + +def run(arguments, stdout=sys.stdout, stderr=sys.stderr, stdin=sys.stdin): # noqa: D103 + outputter = _Outputter.from_arguments( + arguments=arguments, + stdout=stdout, + stderr=stderr, + ) + + try: + schema = outputter.load(arguments["schema"]) + except _CannotLoadFile: + return 1 + + Validator = arguments["validator"] + if Validator is None: + Validator = validator_for(schema) + + try: + Validator.check_schema(schema) + except SchemaError as error: + outputter.validation_error( + instance_path=arguments["schema"], + error=error, + ) + return 1 + + if arguments["instances"]: + load, instances = outputter.load, arguments["instances"] + else: + def load(_): + try: + return json.load(stdin) + except JSONDecodeError as error: + outputter.parsing_error( + path="", exc_info=sys.exc_info(), + ) + raise _CannotLoadFile() from error + instances = [""] + + resolver = _RefResolver( + base_uri=arguments["base_uri"], + referrer=schema, + ) if arguments["base_uri"] is not None else None + + validator = Validator(schema, resolver=resolver) + exit_code = 0 + for each in instances: + try: + instance = load(each) + except _CannotLoadFile: + exit_code = 1 + else: + exit_code |= _validate_instance( + instance_path=each, + instance=instance, + validator=validator, + outputter=outputter, + ) + + return exit_code diff --git a/venv/lib/python3.12/site-packages/jsonschema/exceptions.py b/venv/lib/python3.12/site-packages/jsonschema/exceptions.py new file mode 100644 index 0000000..d955e35 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/exceptions.py @@ -0,0 +1,490 @@ +""" +Validation errors, and some surrounding helpers. +""" +from __future__ import annotations + +from collections import defaultdict, deque +from pprint import pformat +from textwrap import dedent, indent +from typing import TYPE_CHECKING, Any, ClassVar +import heapq +import re +import warnings + +from attrs import define +from referencing.exceptions import Unresolvable as _Unresolvable + +from jsonschema import _utils + +if TYPE_CHECKING: + from collections.abc import Iterable, Mapping, MutableMapping, Sequence + + from jsonschema import _types + +WEAK_MATCHES: frozenset[str] = frozenset(["anyOf", "oneOf"]) +STRONG_MATCHES: frozenset[str] = frozenset() + +_JSON_PATH_COMPATIBLE_PROPERTY_PATTERN = re.compile("^[a-zA-Z][a-zA-Z0-9_]*$") + +_unset = _utils.Unset() + + +def _pretty(thing: Any, prefix: str): + """ + Format something for an error message as prettily as we currently can. + """ + return indent(pformat(thing, width=72, sort_dicts=False), prefix).lstrip() + + +def __getattr__(name): + if name == "RefResolutionError": + warnings.warn( + _RefResolutionError._DEPRECATION_MESSAGE, + DeprecationWarning, + stacklevel=2, + ) + return _RefResolutionError + raise AttributeError(f"module {__name__} has no attribute {name}") + + +class _Error(Exception): + + _word_for_schema_in_error_message: ClassVar[str] + _word_for_instance_in_error_message: ClassVar[str] + + def __init__( + self, + message: str, + validator: str = _unset, # type: ignore[assignment] + path: Iterable[str | int] = (), + cause: Exception | None = None, + context=(), + validator_value: Any = _unset, + instance: Any = _unset, + schema: Mapping[str, Any] | bool = _unset, # type: ignore[assignment] + schema_path: Iterable[str | int] = (), + parent: _Error | None = None, + type_checker: _types.TypeChecker = _unset, # type: ignore[assignment] + ) -> None: + super().__init__( + message, + validator, + path, + cause, + context, + validator_value, + instance, + schema, + schema_path, + parent, + ) + self.message = message + self.path = self.relative_path = deque(path) + self.schema_path = self.relative_schema_path = deque(schema_path) + self.context = list(context) + self.cause = self.__cause__ = cause + self.validator = validator + self.validator_value = validator_value + self.instance = instance + self.schema = schema + self.parent = parent + self._type_checker = type_checker + + for error in context: + error.parent = self + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}: {self.message!r}>" + + def __str__(self) -> str: + essential_for_verbose = ( + self.validator, self.validator_value, self.instance, self.schema, + ) + if any(m is _unset for m in essential_for_verbose): + return self.message + + schema_path = _utils.format_as_index( + container=self._word_for_schema_in_error_message, + indices=list(self.relative_schema_path)[:-1], + ) + instance_path = _utils.format_as_index( + container=self._word_for_instance_in_error_message, + indices=self.relative_path, + ) + prefix = 16 * " " + + return dedent( + f"""\ + {self.message} + + Failed validating {self.validator!r} in {schema_path}: + {_pretty(self.schema, prefix=prefix)} + + On {instance_path}: + {_pretty(self.instance, prefix=prefix)} + """.rstrip(), + ) + + @classmethod + def create_from(cls, other: _Error): + return cls(**other._contents()) + + @property + def absolute_path(self) -> Sequence[str | int]: + parent = self.parent + if parent is None: + return self.relative_path + + path = deque(self.relative_path) + path.extendleft(reversed(parent.absolute_path)) + return path + + @property + def absolute_schema_path(self) -> Sequence[str | int]: + parent = self.parent + if parent is None: + return self.relative_schema_path + + path = deque(self.relative_schema_path) + path.extendleft(reversed(parent.absolute_schema_path)) + return path + + @property + def json_path(self) -> str: + path = "$" + for elem in self.absolute_path: + if isinstance(elem, int): + path += "[" + str(elem) + "]" + elif _JSON_PATH_COMPATIBLE_PROPERTY_PATTERN.match(elem): + path += "." + elem + else: + escaped_elem = elem.replace("\\", "\\\\").replace("'", r"\'") + path += "['" + escaped_elem + "']" + return path + + def _set( + self, + type_checker: _types.TypeChecker | None = None, + **kwargs: Any, + ) -> None: + if type_checker is not None and self._type_checker is _unset: + self._type_checker = type_checker + + for k, v in kwargs.items(): + if getattr(self, k) is _unset: + setattr(self, k, v) + + def _contents(self): + attrs = ( + "message", "cause", "context", "validator", "validator_value", + "path", "schema_path", "instance", "schema", "parent", + ) + return {attr: getattr(self, attr) for attr in attrs} + + def _matches_type(self) -> bool: + try: + # We ignore this as we want to simply crash if this happens + expected = self.schema["type"] # type: ignore[index] + except (KeyError, TypeError): + return False + + if isinstance(expected, str): + return self._type_checker.is_type(self.instance, expected) + + return any( + self._type_checker.is_type(self.instance, expected_type) + for expected_type in expected + ) + + +class ValidationError(_Error): + """ + An instance was invalid under a provided schema. + """ + + _word_for_schema_in_error_message = "schema" + _word_for_instance_in_error_message = "instance" + + +class SchemaError(_Error): + """ + A schema was invalid under its corresponding metaschema. + """ + + _word_for_schema_in_error_message = "metaschema" + _word_for_instance_in_error_message = "schema" + + +@define(slots=False) +class _RefResolutionError(Exception): # noqa: PLW1641 + """ + A ref could not be resolved. + """ + + _DEPRECATION_MESSAGE = ( + "jsonschema.exceptions.RefResolutionError is deprecated as of version " + "4.18.0. If you wish to catch potential reference resolution errors, " + "directly catch referencing.exceptions.Unresolvable." + ) + + _cause: Exception + + def __eq__(self, other): + if self.__class__ is not other.__class__: + return NotImplemented # pragma: no cover -- uncovered but deprecated # noqa: E501 + return self._cause == other._cause + + def __str__(self) -> str: + return str(self._cause) + + +class _WrappedReferencingError(_RefResolutionError, _Unresolvable): # pragma: no cover -- partially uncovered but to be removed # noqa: E501 + def __init__(self, cause: _Unresolvable): + object.__setattr__(self, "_wrapped", cause) + + def __eq__(self, other): + if other.__class__ is self.__class__: + return self._wrapped == other._wrapped + elif other.__class__ is self._wrapped.__class__: + return self._wrapped == other + return NotImplemented + + def __getattr__(self, attr): + return getattr(self._wrapped, attr) + + def __hash__(self): + return hash(self._wrapped) + + def __repr__(self): + return f"" + + def __str__(self): + return f"{self._wrapped.__class__.__name__}: {self._wrapped}" + + +class UndefinedTypeCheck(Exception): + """ + A type checker was asked to check a type it did not have registered. + """ + + def __init__(self, type: str) -> None: + self.type = type + + def __str__(self) -> str: + return f"Type {self.type!r} is unknown to this type checker" + + +class UnknownType(Exception): + """ + A validator was asked to validate an instance against an unknown type. + """ + + def __init__(self, type, instance, schema): + self.type = type + self.instance = instance + self.schema = schema + + def __str__(self): + prefix = 16 * " " + + return dedent( + f"""\ + Unknown type {self.type!r} for validator with schema: + {_pretty(self.schema, prefix=prefix)} + + While checking instance: + {_pretty(self.instance, prefix=prefix)} + """.rstrip(), + ) + + +class FormatError(Exception): + """ + Validating a format failed. + """ + + def __init__(self, message, cause=None): + super().__init__(message, cause) + self.message = message + self.cause = self.__cause__ = cause + + def __str__(self): + return self.message + + +class ErrorTree: + """ + ErrorTrees make it easier to check which validations failed. + """ + + _instance = _unset + + def __init__(self, errors: Iterable[ValidationError] = ()): + self.errors: MutableMapping[str, ValidationError] = {} + self._contents: Mapping[str, ErrorTree] = defaultdict(self.__class__) + + for error in errors: + container = self + for element in error.path: + container = container[element] + container.errors[error.validator] = error + + container._instance = error.instance + + def __contains__(self, index: str | int): + """ + Check whether ``instance[index]`` has any errors. + """ + return index in self._contents + + def __getitem__(self, index): + """ + Retrieve the child tree one level down at the given ``index``. + + If the index is not in the instance that this tree corresponds + to and is not known by this tree, whatever error would be raised + by ``instance.__getitem__`` will be propagated (usually this is + some subclass of `LookupError`. + """ + if self._instance is not _unset and index not in self: + self._instance[index] + return self._contents[index] + + def __setitem__(self, index: str | int, value: ErrorTree): + """ + Add an error to the tree at the given ``index``. + + .. deprecated:: v4.20.0 + + Setting items on an `ErrorTree` is deprecated without replacement. + To populate a tree, provide all of its sub-errors when you + construct the tree. + """ + warnings.warn( + "ErrorTree.__setitem__ is deprecated without replacement.", + DeprecationWarning, + stacklevel=2, + ) + self._contents[index] = value # type: ignore[index] + + def __iter__(self): + """ + Iterate (non-recursively) over the indices in the instance with errors. + """ + return iter(self._contents) + + def __len__(self): + """ + Return the `total_errors`. + """ + return self.total_errors + + def __repr__(self): + total = len(self) + errors = "error" if total == 1 else "errors" + return f"<{self.__class__.__name__} ({total} total {errors})>" + + @property + def total_errors(self): + """ + The total number of errors in the entire tree, including children. + """ + child_errors = sum(len(tree) for _, tree in self._contents.items()) + return len(self.errors) + child_errors + + +def by_relevance(weak=WEAK_MATCHES, strong=STRONG_MATCHES): + """ + Create a key function that can be used to sort errors by relevance. + + Arguments: + weak (set): + a collection of validation keywords to consider to be + "weak". If there are two errors at the same level of the + instance and one is in the set of weak validation keywords, + the other error will take priority. By default, :kw:`anyOf` + and :kw:`oneOf` are considered weak keywords and will be + superseded by other same-level validation errors. + + strong (set): + a collection of validation keywords to consider to be + "strong" + + """ + + def relevance(error): + validator = error.validator + return ( # prefer errors which are ... + -len(error.path), # 'deeper' and thereby more specific + error.path, # earlier (for sibling errors) + validator not in weak, # for a non-low-priority keyword + validator in strong, # for a high priority keyword + not error._matches_type(), # at least match the instance's type + ) # otherwise we'll treat them the same + + return relevance + + +relevance = by_relevance() +""" +A key function (e.g. to use with `sorted`) which sorts errors by relevance. + +Example: + +.. code:: python + + sorted(validator.iter_errors(12), key=jsonschema.exceptions.relevance) +""" + + +def best_match(errors, key=relevance): + """ + Try to find an error that appears to be the best match among given errors. + + In general, errors that are higher up in the instance (i.e. for which + `ValidationError.path` is shorter) are considered better matches, + since they indicate "more" is wrong with the instance. + + If the resulting match is either :kw:`oneOf` or :kw:`anyOf`, the + *opposite* assumption is made -- i.e. the deepest error is picked, + since these keywords only need to match once, and any other errors + may not be relevant. + + Arguments: + errors (collections.abc.Iterable): + + the errors to select from. Do not provide a mixture of + errors from different validation attempts (i.e. from + different instances or schemas), since it won't produce + sensical output. + + key (collections.abc.Callable): + + the key to use when sorting errors. See `relevance` and + transitively `by_relevance` for more details (the default is + to sort with the defaults of that function). Changing the + default is only useful if you want to change the function + that rates errors but still want the error context descent + done by this function. + + Returns: + the best matching error, or ``None`` if the iterable was empty + + .. note:: + + This function is a heuristic. Its return value may change for a given + set of inputs from version to version if better heuristics are added. + + """ + best = max(errors, key=key, default=None) + if best is None: + return + + while best.context: + # Calculate the minimum via nsmallest, because we don't recurse if + # all nested errors have the same relevance (i.e. if min == max == all) + smallest = heapq.nsmallest(2, best.context, key=key) + if len(smallest) == 2 and key(smallest[0]) == key(smallest[1]): # noqa: PLR2004 + return best + best = smallest[0] + return best diff --git a/venv/lib/python3.12/site-packages/jsonschema/protocols.py b/venv/lib/python3.12/site-packages/jsonschema/protocols.py new file mode 100644 index 0000000..b6288dc --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/protocols.py @@ -0,0 +1,230 @@ +""" +typing.Protocol classes for jsonschema interfaces. +""" + +# for reference material on Protocols, see +# https://www.python.org/dev/peps/pep-0544/ + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, ClassVar, Protocol, runtime_checkable + +# in order for Sphinx to resolve references accurately from type annotations, +# it needs to see names like `jsonschema.TypeChecker` +# therefore, only import at type-checking time (to avoid circular references), +# but use `jsonschema` for any types which will otherwise not be resolvable +if TYPE_CHECKING: + from collections.abc import Iterable, Mapping + + import referencing.jsonschema + + from jsonschema import _typing + from jsonschema.exceptions import ValidationError + import jsonschema + import jsonschema.validators + +# For code authors working on the validator protocol, these are the three +# use-cases which should be kept in mind: +# +# 1. As a protocol class, it can be used in type annotations to describe the +# available methods and attributes of a validator +# 2. It is the source of autodoc for the validator documentation +# 3. It is runtime_checkable, meaning that it can be used in isinstance() +# checks. +# +# Since protocols are not base classes, isinstance() checking is limited in +# its capabilities. See docs on runtime_checkable for detail + + +@runtime_checkable +class Validator(Protocol): + """ + The protocol to which all validator classes adhere. + + Arguments: + + schema: + + The schema that the validator object will validate with. + It is assumed to be valid, and providing + an invalid schema can lead to undefined behavior. See + `Validator.check_schema` to validate a schema first. + + registry: + + a schema registry that will be used for looking up JSON references + + resolver: + + a resolver that will be used to resolve :kw:`$ref` + properties (JSON references). If unprovided, one will be created. + + .. deprecated:: v4.18.0 + + `RefResolver <_RefResolver>` has been deprecated in favor of + `referencing`, and with it, this argument. + + format_checker: + + if provided, a checker which will be used to assert about + :kw:`format` properties present in the schema. If unprovided, + *no* format validation is done, and the presence of format + within schemas is strictly informational. Certain formats + require additional packages to be installed in order to assert + against instances. Ensure you've installed `jsonschema` with + its `extra (optional) dependencies ` when + invoking ``pip``. + + .. deprecated:: v4.12.0 + + Subclassing validator classes now explicitly warns this is not part of + their public API. + + """ + + #: An object representing the validator's meta schema (the schema that + #: describes valid schemas in the given version). + META_SCHEMA: ClassVar[Mapping] + + #: A mapping of validation keywords (`str`\s) to functions that + #: validate the keyword with that name. For more information see + #: `creating-validators`. + VALIDATORS: ClassVar[Mapping] + + #: A `jsonschema.TypeChecker` that will be used when validating + #: :kw:`type` keywords in JSON schemas. + TYPE_CHECKER: ClassVar[jsonschema.TypeChecker] + + #: A `jsonschema.FormatChecker` that will be used when validating + #: :kw:`format` keywords in JSON schemas. + FORMAT_CHECKER: ClassVar[jsonschema.FormatChecker] + + #: A function which given a schema returns its ID. + ID_OF: _typing.id_of + + #: The schema that will be used to validate instances + schema: Mapping | bool + + def __init__( + self, + schema: Mapping | bool, + resolver: Any = None, # deprecated + format_checker: jsonschema.FormatChecker | None = None, + *, + registry: referencing.jsonschema.SchemaRegistry = ..., + ) -> None: ... + + @classmethod + def check_schema(cls, schema: Mapping | bool) -> None: + """ + Validate the given schema against the validator's `META_SCHEMA`. + + Raises: + + `jsonschema.exceptions.SchemaError`: + + if the schema is invalid + + """ + + def is_type(self, instance: Any, type: str) -> bool: + """ + Check if the instance is of the given (JSON Schema) type. + + Arguments: + + instance: + + the value to check + + type: + + the name of a known (JSON Schema) type + + Returns: + + whether the instance is of the given type + + Raises: + + `jsonschema.exceptions.UnknownType`: + + if ``type`` is not a known type + + """ + + def is_valid(self, instance: Any) -> bool: + """ + Check if the instance is valid under the current `schema`. + + Returns: + + whether the instance is valid or not + + >>> schema = {"maxItems" : 2} + >>> Draft202012Validator(schema).is_valid([2, 3, 4]) + False + + """ + + def iter_errors(self, instance: Any) -> Iterable[ValidationError]: + r""" + Lazily yield each of the validation errors in the given instance. + + >>> schema = { + ... "type" : "array", + ... "items" : {"enum" : [1, 2, 3]}, + ... "maxItems" : 2, + ... } + >>> v = Draft202012Validator(schema) + >>> for error in sorted(v.iter_errors([2, 3, 4]), key=str): + ... print(error.message) + 4 is not one of [1, 2, 3] + [2, 3, 4] is too long + + .. deprecated:: v4.0.0 + + Calling this function with a second schema argument is deprecated. + Use `Validator.evolve` instead. + """ + + def validate(self, instance: Any) -> None: + """ + Check if the instance is valid under the current `schema`. + + Raises: + + `jsonschema.exceptions.ValidationError`: + + if the instance is invalid + + >>> schema = {"maxItems" : 2} + >>> Draft202012Validator(schema).validate([2, 3, 4]) + Traceback (most recent call last): + ... + ValidationError: [2, 3, 4] is too long + + """ + + def evolve(self, **kwargs) -> Validator: + """ + Create a new validator like this one, but with given changes. + + Preserves all other attributes, so can be used to e.g. create a + validator with a different schema but with the same :kw:`$ref` + resolution behavior. + + >>> validator = Draft202012Validator({}) + >>> validator.evolve(schema={"type": "number"}) + Draft202012Validator(schema={'type': 'number'}, format_checker=None) + + The returned object satisfies the validator protocol, but may not + be of the same concrete class! In particular this occurs + when a :kw:`$ref` occurs to a schema with a different + :kw:`$schema` than this one (i.e. for a different draft). + + >>> validator.evolve( + ... schema={"$schema": Draft7Validator.META_SCHEMA["$id"]} + ... ) + Draft7Validator(schema=..., format_checker=None) + """ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/__init__.py b/venv/lib/python3.12/site-packages/jsonschema/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1ba1397 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/_suite.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/_suite.cpython-312.pyc new file mode 100644 index 0000000..ebfc8e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/_suite.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/fuzz_validate.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/fuzz_validate.cpython-312.pyc new file mode 100644 index 0000000..3b4d9eb Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/fuzz_validate.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_cli.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_cli.cpython-312.pyc new file mode 100644 index 0000000..bad4482 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_cli.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_deprecations.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_deprecations.cpython-312.pyc new file mode 100644 index 0000000..0f570d1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_deprecations.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_exceptions.cpython-312.pyc new file mode 100644 index 0000000..0be944a Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_format.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_format.cpython-312.pyc new file mode 100644 index 0000000..f352c40 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_format.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_jsonschema_test_suite.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_jsonschema_test_suite.cpython-312.pyc new file mode 100644 index 0000000..09eac1c Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_jsonschema_test_suite.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_types.cpython-312.pyc new file mode 100644 index 0000000..cfad923 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_utils.cpython-312.pyc new file mode 100644 index 0000000..f36786c Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_validators.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_validators.cpython-312.pyc new file mode 100644 index 0000000..e0954b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/tests/__pycache__/test_validators.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/_suite.py b/venv/lib/python3.12/site-packages/jsonschema/tests/_suite.py new file mode 100644 index 0000000..d61d382 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/tests/_suite.py @@ -0,0 +1,285 @@ +""" +Python representations of the JSON Schema Test Suite tests. +""" +from __future__ import annotations + +from contextlib import suppress +from functools import partial +from pathlib import Path +from typing import TYPE_CHECKING, Any +import json +import os +import re +import sys +import unittest + +from attrs import field, frozen +from referencing import Registry +import referencing.jsonschema + +if TYPE_CHECKING: + from collections.abc import Iterable, Mapping, Sequence + + from referencing.jsonschema import Schema + import pyperf + +from jsonschema.validators import _VALIDATORS +import jsonschema + +MAGIC_REMOTE_URL = "http://localhost:1234" + +_DELIMITERS = re.compile(r"[\W\- ]+") + + +def _find_suite(): + root = os.environ.get("JSON_SCHEMA_TEST_SUITE") + if root is not None: + return Path(root) + + root = Path(jsonschema.__file__).parent.parent / "json" + if not root.is_dir(): # pragma: no cover + raise ValueError( + ( + "Can't find the JSON-Schema-Test-Suite directory. " + "Set the 'JSON_SCHEMA_TEST_SUITE' environment " + "variable or run the tests from alongside a checkout " + "of the suite." + ), + ) + return root + + +@frozen +class Suite: + + _root: Path = field(factory=_find_suite) + + + def benchmark(self, runner: pyperf.Runner): # pragma: no cover + for name, Validator in _VALIDATORS.items(): + self.version(name=name).benchmark( + runner=runner, + Validator=Validator, + ) + + def version(self, name) -> Version: + Validator = _VALIDATORS[name] + uri: str = Validator.ID_OF(Validator.META_SCHEMA) # type: ignore[assignment] + specification = referencing.jsonschema.specification_with(uri) + + registry = Registry().with_contents( + remotes_in(root=self._root / "remotes", name=name, uri=uri), + default_specification=specification, + ) + return Version( + name=name, + path=self._root / "tests" / name, + remotes=registry, + ) + + +@frozen +class Version: + + _path: Path + _remotes: referencing.jsonschema.SchemaRegistry + + name: str + + def benchmark(self, **kwargs): # pragma: no cover + for case in self.cases(): + case.benchmark(**kwargs) + + def cases(self) -> Iterable[_Case]: + return self._cases_in(paths=self._path.glob("*.json")) + + def format_cases(self) -> Iterable[_Case]: + return self._cases_in(paths=self._path.glob("optional/format/*.json")) + + def optional_cases_of(self, name: str) -> Iterable[_Case]: + return self._cases_in(paths=[self._path / "optional" / f"{name}.json"]) + + def to_unittest_testcase(self, *groups, **kwargs): + name = kwargs.pop("name", "Test" + self.name.title().replace("-", "")) + methods = { + method.__name__: method + for method in ( + test.to_unittest_method(**kwargs) + for group in groups + for case in group + for test in case.tests + ) + } + cls = type(name, (unittest.TestCase,), methods) + + # We're doing crazy things, so if they go wrong, like a function + # behaving differently on some other interpreter, just make them + # not happen. + with suppress(Exception): + cls.__module__ = _someone_save_us_the_module_of_the_caller() + + return cls + + def _cases_in(self, paths: Iterable[Path]) -> Iterable[_Case]: + for path in paths: + for case in json.loads(path.read_text(encoding="utf-8")): + yield _Case.from_dict( + case, + version=self, + subject=path.stem, + remotes=self._remotes, + ) + + +@frozen +class _Case: + + version: Version + + subject: str + description: str + schema: Mapping[str, Any] | bool + tests: list[_Test] + comment: str | None = None + specification: Sequence[dict[str, str]] = () + + @classmethod + def from_dict(cls, data, remotes, **kwargs): + data.update(kwargs) + tests = [ + _Test( + version=data["version"], + subject=data["subject"], + case_description=data["description"], + schema=data["schema"], + remotes=remotes, + **test, + ) for test in data.pop("tests") + ] + return cls(tests=tests, **data) + + def benchmark(self, runner: pyperf.Runner, **kwargs): # pragma: no cover + for test in self.tests: + runner.bench_func( + test.fully_qualified_name, + partial(test.validate_ignoring_errors, **kwargs), + ) + + +def remotes_in( + root: Path, + name: str, + uri: str, +) -> Iterable[tuple[str, Schema]]: + # This messy logic is because the test suite is terrible at indicating + # what remotes are needed for what drafts, and mixes in schemas which + # have no $schema and which are invalid under earlier versions, in with + # other schemas which are needed for tests. + + for each in root.rglob("*.json"): + schema = json.loads(each.read_text()) + + relative = str(each.relative_to(root)).replace("\\", "/") + + if ( + ( # invalid boolean schema + name in {"draft3", "draft4"} + and each.stem == "tree" + ) or + ( # draft/*.json + "$schema" not in schema + and relative.startswith("draft") + and not relative.startswith(name) + ) + ): + continue + yield f"{MAGIC_REMOTE_URL}/{relative}", schema + + +@frozen(repr=False) +class _Test: + + version: Version + + subject: str + case_description: str + description: str + + data: Any + schema: Mapping[str, Any] | bool + + valid: bool + + _remotes: referencing.jsonschema.SchemaRegistry + + comment: str | None = None + + def __repr__(self): # pragma: no cover + return f"" + + @property + def fully_qualified_name(self): # pragma: no cover + return " > ".join( # noqa: FLY002 + [ + self.version.name, + self.subject, + self.case_description, + self.description, + ], + ) + + def to_unittest_method(self, skip=lambda test: None, **kwargs): + if self.valid: + def fn(this): + self.validate(**kwargs) + else: + def fn(this): + with this.assertRaises(jsonschema.ValidationError): + self.validate(**kwargs) + + fn.__name__ = "_".join( + [ + "test", + _DELIMITERS.sub("_", self.subject), + _DELIMITERS.sub("_", self.case_description), + _DELIMITERS.sub("_", self.description), + ], + ) + reason = skip(self) + if reason is None or os.environ.get("JSON_SCHEMA_DEBUG", "0") != "0": + return fn + elif os.environ.get("JSON_SCHEMA_EXPECTED_FAILURES", "0") != "0": # pragma: no cover # noqa: E501 + return unittest.expectedFailure(fn) + else: + return unittest.skip(reason)(fn) + + def validate(self, Validator, **kwargs): + Validator.check_schema(self.schema) + validator = Validator( + schema=self.schema, + registry=self._remotes, + **kwargs, + ) + if os.environ.get("JSON_SCHEMA_DEBUG", "0") != "0": # pragma: no cover + breakpoint() # noqa: T100 + validator.validate(instance=self.data) + + def validate_ignoring_errors(self, Validator): # pragma: no cover + with suppress(jsonschema.ValidationError): + self.validate(Validator=Validator) + + +def _someone_save_us_the_module_of_the_caller(): + """ + The FQON of the module 2nd stack frames up from here. + + This is intended to allow us to dynamically return test case classes that + are indistinguishable from being defined in the module that wants them. + + Otherwise, trial will mis-print the FQON, and copy pasting it won't re-run + the class that really is running. + + Save us all, this is all so so so so so terrible. + """ + + return sys._getframe(2).f_globals["__name__"] diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/fuzz_validate.py b/venv/lib/python3.12/site-packages/jsonschema/tests/fuzz_validate.py new file mode 100644 index 0000000..c12e88b --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/tests/fuzz_validate.py @@ -0,0 +1,50 @@ +""" +Fuzzing setup for OSS-Fuzz. + +See https://github.com/google/oss-fuzz/tree/master/projects/jsonschema for the +other half of the setup here. +""" +import sys + +from hypothesis import given, strategies + +import jsonschema + +PRIM = strategies.one_of( + strategies.booleans(), + strategies.integers(), + strategies.floats(allow_nan=False, allow_infinity=False), + strategies.text(), +) +DICT = strategies.recursive( + base=strategies.one_of( + strategies.booleans(), + strategies.dictionaries(strategies.text(), PRIM), + ), + extend=lambda inner: strategies.dictionaries(strategies.text(), inner), +) + + +@given(obj1=DICT, obj2=DICT) +def test_schemas(obj1, obj2): + try: + jsonschema.validate(instance=obj1, schema=obj2) + except jsonschema.exceptions.ValidationError: + pass + except jsonschema.exceptions.SchemaError: + pass + + +def main(): + atheris.instrument_all() + atheris.Setup( + sys.argv, + test_schemas.hypothesis.fuzz_one_input, + enable_python_coverage=True, + ) + atheris.Fuzz() + + +if __name__ == "__main__": + import atheris + main() diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/test_cli.py b/venv/lib/python3.12/site-packages/jsonschema/tests/test_cli.py new file mode 100644 index 0000000..bed9f3e --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/tests/test_cli.py @@ -0,0 +1,904 @@ +from contextlib import redirect_stderr, redirect_stdout +from importlib import metadata +from io import StringIO +from json import JSONDecodeError +from pathlib import Path +from textwrap import dedent +from unittest import TestCase +import json +import os +import subprocess +import sys +import tempfile +import warnings + +from jsonschema import Draft4Validator, Draft202012Validator +from jsonschema.exceptions import ( + SchemaError, + ValidationError, + _RefResolutionError, +) +from jsonschema.validators import _LATEST_VERSION, validate + +with warnings.catch_warnings(): + warnings.simplefilter("ignore") + from jsonschema import cli + + +def fake_validator(*errors): + errors = list(reversed(errors)) + + class FakeValidator: + def __init__(self, *args, **kwargs): + pass + + def iter_errors(self, instance): + if errors: + return errors.pop() + return [] # pragma: no cover + + @classmethod + def check_schema(self, schema): + pass + + return FakeValidator + + +def fake_open(all_contents): + def open(path): + contents = all_contents.get(path) + if contents is None: + raise FileNotFoundError(path) + return StringIO(contents) + return open + + +def _message_for(non_json): + try: + json.loads(non_json) + except JSONDecodeError as error: + return str(error) + else: # pragma: no cover + raise RuntimeError("Tried and failed to capture a JSON dump error.") + + +class TestCLI(TestCase): + def run_cli( + self, argv, files=None, stdin=StringIO(), exit_code=0, **override, + ): + arguments = cli.parse_args(argv) + arguments.update(override) + + self.assertFalse(hasattr(cli, "open")) + cli.open = fake_open(files or {}) + try: + stdout, stderr = StringIO(), StringIO() + actual_exit_code = cli.run( + arguments, + stdin=stdin, + stdout=stdout, + stderr=stderr, + ) + finally: + del cli.open + + self.assertEqual( + actual_exit_code, exit_code, msg=dedent( + f""" + Expected an exit code of {exit_code} != {actual_exit_code}. + + stdout: {stdout.getvalue()} + + stderr: {stderr.getvalue()} + """, + ), + ) + return stdout.getvalue(), stderr.getvalue() + + def assertOutputs(self, stdout="", stderr="", **kwargs): + self.assertEqual( + self.run_cli(**kwargs), + (dedent(stdout), dedent(stderr)), + ) + + def test_invalid_instance(self): + error = ValidationError("I am an error!", instance=12) + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_instance=json.dumps(error.instance), + ), + validator=fake_validator([error]), + + argv=["-i", "some_instance", "some_schema"], + + exit_code=1, + stderr="12: I am an error!\n", + ) + + def test_invalid_instance_pretty_output(self): + error = ValidationError("I am an error!", instance=12) + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_instance=json.dumps(error.instance), + ), + validator=fake_validator([error]), + + argv=["-i", "some_instance", "--output", "pretty", "some_schema"], + + exit_code=1, + stderr="""\ + ===[ValidationError]===(some_instance)=== + + I am an error! + ----------------------------- + """, + ) + + def test_invalid_instance_explicit_plain_output(self): + error = ValidationError("I am an error!", instance=12) + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_instance=json.dumps(error.instance), + ), + validator=fake_validator([error]), + + argv=["--output", "plain", "-i", "some_instance", "some_schema"], + + exit_code=1, + stderr="12: I am an error!\n", + ) + + def test_invalid_instance_multiple_errors(self): + instance = 12 + first = ValidationError("First error", instance=instance) + second = ValidationError("Second error", instance=instance) + + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_instance=json.dumps(instance), + ), + validator=fake_validator([first, second]), + + argv=["-i", "some_instance", "some_schema"], + + exit_code=1, + stderr="""\ + 12: First error + 12: Second error + """, + ) + + def test_invalid_instance_multiple_errors_pretty_output(self): + instance = 12 + first = ValidationError("First error", instance=instance) + second = ValidationError("Second error", instance=instance) + + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_instance=json.dumps(instance), + ), + validator=fake_validator([first, second]), + + argv=["-i", "some_instance", "--output", "pretty", "some_schema"], + + exit_code=1, + stderr="""\ + ===[ValidationError]===(some_instance)=== + + First error + ----------------------------- + ===[ValidationError]===(some_instance)=== + + Second error + ----------------------------- + """, + ) + + def test_multiple_invalid_instances(self): + first_instance = 12 + first_errors = [ + ValidationError("An error", instance=first_instance), + ValidationError("Another error", instance=first_instance), + ] + second_instance = "foo" + second_errors = [ValidationError("BOOM", instance=second_instance)] + + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_first_instance=json.dumps(first_instance), + some_second_instance=json.dumps(second_instance), + ), + validator=fake_validator(first_errors, second_errors), + + argv=[ + "-i", "some_first_instance", + "-i", "some_second_instance", + "some_schema", + ], + + exit_code=1, + stderr="""\ + 12: An error + 12: Another error + foo: BOOM + """, + ) + + def test_multiple_invalid_instances_pretty_output(self): + first_instance = 12 + first_errors = [ + ValidationError("An error", instance=first_instance), + ValidationError("Another error", instance=first_instance), + ] + second_instance = "foo" + second_errors = [ValidationError("BOOM", instance=second_instance)] + + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_first_instance=json.dumps(first_instance), + some_second_instance=json.dumps(second_instance), + ), + validator=fake_validator(first_errors, second_errors), + + argv=[ + "--output", "pretty", + "-i", "some_first_instance", + "-i", "some_second_instance", + "some_schema", + ], + + exit_code=1, + stderr="""\ + ===[ValidationError]===(some_first_instance)=== + + An error + ----------------------------- + ===[ValidationError]===(some_first_instance)=== + + Another error + ----------------------------- + ===[ValidationError]===(some_second_instance)=== + + BOOM + ----------------------------- + """, + ) + + def test_custom_error_format(self): + first_instance = 12 + first_errors = [ + ValidationError("An error", instance=first_instance), + ValidationError("Another error", instance=first_instance), + ] + second_instance = "foo" + second_errors = [ValidationError("BOOM", instance=second_instance)] + + self.assertOutputs( + files=dict( + some_schema='{"does not": "matter since it is stubbed"}', + some_first_instance=json.dumps(first_instance), + some_second_instance=json.dumps(second_instance), + ), + validator=fake_validator(first_errors, second_errors), + + argv=[ + "--error-format", ":{error.message}._-_.{error.instance}:", + "-i", "some_first_instance", + "-i", "some_second_instance", + "some_schema", + ], + + exit_code=1, + stderr=":An error._-_.12::Another error._-_.12::BOOM._-_.foo:", + ) + + def test_invalid_schema(self): + self.assertOutputs( + files=dict(some_schema='{"type": 12}'), + argv=["some_schema"], + + exit_code=1, + stderr="""\ + 12: 12 is not valid under any of the given schemas + """, + ) + + def test_invalid_schema_pretty_output(self): + schema = {"type": 12} + + with self.assertRaises(SchemaError) as e: + validate(schema=schema, instance="") + error = str(e.exception) + + self.assertOutputs( + files=dict(some_schema=json.dumps(schema)), + argv=["--output", "pretty", "some_schema"], + + exit_code=1, + stderr=( + "===[SchemaError]===(some_schema)===\n\n" + + str(error) + + "\n-----------------------------\n" + ), + ) + + def test_invalid_schema_multiple_errors(self): + self.assertOutputs( + files=dict(some_schema='{"type": 12, "items": 57}'), + argv=["some_schema"], + + exit_code=1, + stderr="""\ + 57: 57 is not of type 'object', 'boolean' + """, + ) + + def test_invalid_schema_multiple_errors_pretty_output(self): + schema = {"type": 12, "items": 57} + + with self.assertRaises(SchemaError) as e: + validate(schema=schema, instance="") + error = str(e.exception) + + self.assertOutputs( + files=dict(some_schema=json.dumps(schema)), + argv=["--output", "pretty", "some_schema"], + + exit_code=1, + stderr=( + "===[SchemaError]===(some_schema)===\n\n" + + str(error) + + "\n-----------------------------\n" + ), + ) + + def test_invalid_schema_with_invalid_instance(self): + """ + "Validating" an instance that's invalid under an invalid schema + just shows the schema error. + """ + self.assertOutputs( + files=dict( + some_schema='{"type": 12, "minimum": 30}', + some_instance="13", + ), + argv=["-i", "some_instance", "some_schema"], + + exit_code=1, + stderr="""\ + 12: 12 is not valid under any of the given schemas + """, + ) + + def test_invalid_schema_with_invalid_instance_pretty_output(self): + instance, schema = 13, {"type": 12, "minimum": 30} + + with self.assertRaises(SchemaError) as e: + validate(schema=schema, instance=instance) + error = str(e.exception) + + self.assertOutputs( + files=dict( + some_schema=json.dumps(schema), + some_instance=json.dumps(instance), + ), + argv=["--output", "pretty", "-i", "some_instance", "some_schema"], + + exit_code=1, + stderr=( + "===[SchemaError]===(some_schema)===\n\n" + + str(error) + + "\n-----------------------------\n" + ), + ) + + def test_invalid_instance_continues_with_the_rest(self): + self.assertOutputs( + files=dict( + some_schema='{"minimum": 30}', + first_instance="not valid JSON!", + second_instance="12", + ), + argv=[ + "-i", "first_instance", + "-i", "second_instance", + "some_schema", + ], + + exit_code=1, + stderr="""\ + Failed to parse 'first_instance': {} + 12: 12 is less than the minimum of 30 + """.format(_message_for("not valid JSON!")), + ) + + def test_custom_error_format_applies_to_schema_errors(self): + instance, schema = 13, {"type": 12, "minimum": 30} + + with self.assertRaises(SchemaError): + validate(schema=schema, instance=instance) + + self.assertOutputs( + files=dict(some_schema=json.dumps(schema)), + + argv=[ + "--error-format", ":{error.message}._-_.{error.instance}:", + "some_schema", + ], + + exit_code=1, + stderr=":12 is not valid under any of the given schemas._-_.12:", + ) + + def test_instance_is_invalid_JSON(self): + instance = "not valid JSON!" + + self.assertOutputs( + files=dict(some_schema="{}", some_instance=instance), + argv=["-i", "some_instance", "some_schema"], + + exit_code=1, + stderr=f"""\ + Failed to parse 'some_instance': {_message_for(instance)} + """, + ) + + def test_instance_is_invalid_JSON_pretty_output(self): + stdout, stderr = self.run_cli( + files=dict( + some_schema="{}", + some_instance="not valid JSON!", + ), + + argv=["--output", "pretty", "-i", "some_instance", "some_schema"], + + exit_code=1, + ) + self.assertFalse(stdout) + self.assertIn( + "(some_instance)===\n\nTraceback (most recent call last):\n", + stderr, + ) + self.assertNotIn("some_schema", stderr) + + def test_instance_is_invalid_JSON_on_stdin(self): + instance = "not valid JSON!" + + self.assertOutputs( + files=dict(some_schema="{}"), + stdin=StringIO(instance), + + argv=["some_schema"], + + exit_code=1, + stderr=f"""\ + Failed to parse : {_message_for(instance)} + """, + ) + + def test_instance_is_invalid_JSON_on_stdin_pretty_output(self): + stdout, stderr = self.run_cli( + files=dict(some_schema="{}"), + stdin=StringIO("not valid JSON!"), + + argv=["--output", "pretty", "some_schema"], + + exit_code=1, + ) + self.assertFalse(stdout) + self.assertIn( + "()===\n\nTraceback (most recent call last):\n", + stderr, + ) + self.assertNotIn("some_schema", stderr) + + def test_schema_is_invalid_JSON(self): + schema = "not valid JSON!" + + self.assertOutputs( + files=dict(some_schema=schema), + + argv=["some_schema"], + + exit_code=1, + stderr=f"""\ + Failed to parse 'some_schema': {_message_for(schema)} + """, + ) + + def test_schema_is_invalid_JSON_pretty_output(self): + stdout, stderr = self.run_cli( + files=dict(some_schema="not valid JSON!"), + + argv=["--output", "pretty", "some_schema"], + + exit_code=1, + ) + self.assertFalse(stdout) + self.assertIn( + "(some_schema)===\n\nTraceback (most recent call last):\n", + stderr, + ) + + def test_schema_and_instance_are_both_invalid_JSON(self): + """ + Only the schema error is reported, as we abort immediately. + """ + schema, instance = "not valid JSON!", "also not valid JSON!" + self.assertOutputs( + files=dict(some_schema=schema, some_instance=instance), + + argv=["some_schema"], + + exit_code=1, + stderr=f"""\ + Failed to parse 'some_schema': {_message_for(schema)} + """, + ) + + def test_schema_and_instance_are_both_invalid_JSON_pretty_output(self): + """ + Only the schema error is reported, as we abort immediately. + """ + stdout, stderr = self.run_cli( + files=dict( + some_schema="not valid JSON!", + some_instance="also not valid JSON!", + ), + + argv=["--output", "pretty", "-i", "some_instance", "some_schema"], + + exit_code=1, + ) + self.assertFalse(stdout) + self.assertIn( + "(some_schema)===\n\nTraceback (most recent call last):\n", + stderr, + ) + self.assertNotIn("some_instance", stderr) + + def test_instance_does_not_exist(self): + self.assertOutputs( + files=dict(some_schema="{}"), + argv=["-i", "nonexisting_instance", "some_schema"], + + exit_code=1, + stderr="""\ + 'nonexisting_instance' does not exist. + """, + ) + + def test_instance_does_not_exist_pretty_output(self): + self.assertOutputs( + files=dict(some_schema="{}"), + argv=[ + "--output", "pretty", + "-i", "nonexisting_instance", + "some_schema", + ], + + exit_code=1, + stderr="""\ + ===[FileNotFoundError]===(nonexisting_instance)=== + + 'nonexisting_instance' does not exist. + ----------------------------- + """, + ) + + def test_schema_does_not_exist(self): + self.assertOutputs( + argv=["nonexisting_schema"], + + exit_code=1, + stderr="'nonexisting_schema' does not exist.\n", + ) + + def test_schema_does_not_exist_pretty_output(self): + self.assertOutputs( + argv=["--output", "pretty", "nonexisting_schema"], + + exit_code=1, + stderr="""\ + ===[FileNotFoundError]===(nonexisting_schema)=== + + 'nonexisting_schema' does not exist. + ----------------------------- + """, + ) + + def test_neither_instance_nor_schema_exist(self): + self.assertOutputs( + argv=["-i", "nonexisting_instance", "nonexisting_schema"], + + exit_code=1, + stderr="'nonexisting_schema' does not exist.\n", + ) + + def test_neither_instance_nor_schema_exist_pretty_output(self): + self.assertOutputs( + argv=[ + "--output", "pretty", + "-i", "nonexisting_instance", + "nonexisting_schema", + ], + + exit_code=1, + stderr="""\ + ===[FileNotFoundError]===(nonexisting_schema)=== + + 'nonexisting_schema' does not exist. + ----------------------------- + """, + ) + + def test_successful_validation(self): + self.assertOutputs( + files=dict(some_schema="{}", some_instance="{}"), + argv=["-i", "some_instance", "some_schema"], + stdout="", + stderr="", + ) + + def test_successful_validation_pretty_output(self): + self.assertOutputs( + files=dict(some_schema="{}", some_instance="{}"), + argv=["--output", "pretty", "-i", "some_instance", "some_schema"], + stdout="===[SUCCESS]===(some_instance)===\n", + stderr="", + ) + + def test_successful_validation_of_stdin(self): + self.assertOutputs( + files=dict(some_schema="{}"), + stdin=StringIO("{}"), + argv=["some_schema"], + stdout="", + stderr="", + ) + + def test_successful_validation_of_stdin_pretty_output(self): + self.assertOutputs( + files=dict(some_schema="{}"), + stdin=StringIO("{}"), + argv=["--output", "pretty", "some_schema"], + stdout="===[SUCCESS]===()===\n", + stderr="", + ) + + def test_successful_validation_of_just_the_schema(self): + self.assertOutputs( + files=dict(some_schema="{}", some_instance="{}"), + argv=["-i", "some_instance", "some_schema"], + stdout="", + stderr="", + ) + + def test_successful_validation_of_just_the_schema_pretty_output(self): + self.assertOutputs( + files=dict(some_schema="{}", some_instance="{}"), + argv=["--output", "pretty", "-i", "some_instance", "some_schema"], + stdout="===[SUCCESS]===(some_instance)===\n", + stderr="", + ) + + def test_successful_validation_via_explicit_base_uri(self): + ref_schema_file = tempfile.NamedTemporaryFile(delete=False) # noqa: SIM115 + ref_schema_file.close() + self.addCleanup(os.remove, ref_schema_file.name) + + ref_path = Path(ref_schema_file.name) + ref_path.write_text('{"definitions": {"num": {"type": "integer"}}}') + + schema = f'{{"$ref": "{ref_path.name}#/definitions/num"}}' + + self.assertOutputs( + files=dict(some_schema=schema, some_instance="1"), + argv=[ + "-i", "some_instance", + "--base-uri", ref_path.parent.as_uri() + "/", + "some_schema", + ], + stdout="", + stderr="", + ) + + def test_unsuccessful_validation_via_explicit_base_uri(self): + ref_schema_file = tempfile.NamedTemporaryFile(delete=False) # noqa: SIM115 + ref_schema_file.close() + self.addCleanup(os.remove, ref_schema_file.name) + + ref_path = Path(ref_schema_file.name) + ref_path.write_text('{"definitions": {"num": {"type": "integer"}}}') + + schema = f'{{"$ref": "{ref_path.name}#/definitions/num"}}' + + self.assertOutputs( + files=dict(some_schema=schema, some_instance='"1"'), + argv=[ + "-i", "some_instance", + "--base-uri", ref_path.parent.as_uri() + "/", + "some_schema", + ], + exit_code=1, + stdout="", + stderr="1: '1' is not of type 'integer'\n", + ) + + def test_nonexistent_file_with_explicit_base_uri(self): + schema = '{"$ref": "someNonexistentFile.json#definitions/num"}' + instance = "1" + + with self.assertRaises(_RefResolutionError) as e: + self.assertOutputs( + files=dict( + some_schema=schema, + some_instance=instance, + ), + argv=[ + "-i", "some_instance", + "--base-uri", Path.cwd().as_uri(), + "some_schema", + ], + ) + error = str(e.exception) + self.assertIn(f"{os.sep}someNonexistentFile.json'", error) + + def test_invalid_explicit_base_uri(self): + schema = '{"$ref": "foo.json#definitions/num"}' + instance = "1" + + with self.assertRaises(_RefResolutionError) as e: + self.assertOutputs( + files=dict( + some_schema=schema, + some_instance=instance, + ), + argv=[ + "-i", "some_instance", + "--base-uri", "not@UR1", + "some_schema", + ], + ) + error = str(e.exception) + self.assertEqual( + error, "unknown url type: 'foo.json'", + ) + + def test_it_validates_using_the_latest_validator_when_unspecified(self): + # There isn't a better way now I can think of to ensure that the + # latest version was used, given that the call to validator_for + # is hidden inside the CLI, so guard that that's the case, and + # this test will have to be updated when versions change until + # we can think of a better way to ensure this behavior. + self.assertIs(Draft202012Validator, _LATEST_VERSION) + + self.assertOutputs( + files=dict(some_schema='{"const": "check"}', some_instance='"a"'), + argv=["-i", "some_instance", "some_schema"], + exit_code=1, + stdout="", + stderr="a: 'check' was expected\n", + ) + + def test_it_validates_using_draft7_when_specified(self): + """ + Specifically, `const` validation applies for Draft 7. + """ + schema = """ + { + "$schema": "http://json-schema.org/draft-07/schema#", + "const": "check" + } + """ + instance = '"foo"' + self.assertOutputs( + files=dict(some_schema=schema, some_instance=instance), + argv=["-i", "some_instance", "some_schema"], + exit_code=1, + stdout="", + stderr="foo: 'check' was expected\n", + ) + + def test_it_validates_using_draft4_when_specified(self): + """ + Specifically, `const` validation *does not* apply for Draft 4. + """ + schema = """ + { + "$schema": "http://json-schema.org/draft-04/schema#", + "const": "check" + } + """ + instance = '"foo"' + self.assertOutputs( + files=dict(some_schema=schema, some_instance=instance), + argv=["-i", "some_instance", "some_schema"], + stdout="", + stderr="", + ) + + +class TestParser(TestCase): + + FakeValidator = fake_validator() + + def test_find_validator_by_fully_qualified_object_name(self): + arguments = cli.parse_args( + [ + "--validator", + "jsonschema.tests.test_cli.TestParser.FakeValidator", + "--instance", "mem://some/instance", + "mem://some/schema", + ], + ) + self.assertIs(arguments["validator"], self.FakeValidator) + + def test_find_validator_in_jsonschema(self): + arguments = cli.parse_args( + [ + "--validator", "Draft4Validator", + "--instance", "mem://some/instance", + "mem://some/schema", + ], + ) + self.assertIs(arguments["validator"], Draft4Validator) + + def cli_output_for(self, *argv): + stdout, stderr = StringIO(), StringIO() + with redirect_stdout(stdout), redirect_stderr(stderr): # noqa: SIM117 + with self.assertRaises(SystemExit): + cli.parse_args(argv) + return stdout.getvalue(), stderr.getvalue() + + def test_unknown_output(self): + stdout, stderr = self.cli_output_for( + "--output", "foo", + "mem://some/schema", + ) + self.assertIn("invalid choice: 'foo'", stderr) + self.assertFalse(stdout) + + def test_useless_error_format(self): + stdout, stderr = self.cli_output_for( + "--output", "pretty", + "--error-format", "foo", + "mem://some/schema", + ) + self.assertIn( + "--error-format can only be used with --output plain", + stderr, + ) + self.assertFalse(stdout) + + +class TestCLIIntegration(TestCase): + def test_license(self): + our_metadata = metadata.metadata("jsonschema") + self.assertEqual(our_metadata.get("License-Expression"), "MIT") + + def test_version(self): + version = subprocess.check_output( + [sys.executable, "-W", "ignore", "-m", "jsonschema", "--version"], + stderr=subprocess.STDOUT, + ) + version = version.decode("utf-8").strip() + self.assertEqual(version, metadata.version("jsonschema")) + + def test_no_arguments_shows_usage_notes(self): + output = subprocess.check_output( + [sys.executable, "-m", "jsonschema"], + stderr=subprocess.STDOUT, + ) + output_for_help = subprocess.check_output( + [sys.executable, "-m", "jsonschema", "--help"], + stderr=subprocess.STDOUT, + ) + self.assertEqual(output, output_for_help) diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/test_deprecations.py b/venv/lib/python3.12/site-packages/jsonschema/tests/test_deprecations.py new file mode 100644 index 0000000..a54b02f --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/tests/test_deprecations.py @@ -0,0 +1,432 @@ +from contextlib import contextmanager +from io import BytesIO +from unittest import TestCase, mock +import importlib.metadata +import json +import subprocess +import sys +import urllib.request + +import referencing.exceptions + +from jsonschema import FormatChecker, exceptions, protocols, validators + + +class TestDeprecations(TestCase): + def test_version(self): + """ + As of v4.0.0, __version__ is deprecated in favor of importlib.metadata. + """ + + message = "Accessing jsonschema.__version__ is deprecated" + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import __version__ + + self.assertEqual(__version__, importlib.metadata.version("jsonschema")) + self.assertEqual(w.filename, __file__) + + def test_validators_ErrorTree(self): + """ + As of v4.0.0, importing ErrorTree from jsonschema.validators is + deprecated in favor of doing so from jsonschema.exceptions. + """ + + message = "Importing ErrorTree from jsonschema.validators is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema.validators import ErrorTree + + self.assertEqual(ErrorTree, exceptions.ErrorTree) + self.assertEqual(w.filename, __file__) + + def test_import_ErrorTree(self): + """ + As of v4.18.0, importing ErrorTree from the package root is + deprecated in favor of doing so from jsonschema.exceptions. + """ + + message = "Importing ErrorTree directly from the jsonschema package " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import ErrorTree + + self.assertEqual(ErrorTree, exceptions.ErrorTree) + self.assertEqual(w.filename, __file__) + + def test_ErrorTree_setitem(self): + """ + As of v4.20.0, setting items on an ErrorTree is deprecated. + """ + + e = exceptions.ValidationError("some error", path=["foo"]) + tree = exceptions.ErrorTree() + subtree = exceptions.ErrorTree(errors=[e]) + + message = "ErrorTree.__setitem__ is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + tree["foo"] = subtree + + self.assertEqual(tree["foo"], subtree) + self.assertEqual(w.filename, __file__) + + def test_import_FormatError(self): + """ + As of v4.18.0, importing FormatError from the package root is + deprecated in favor of doing so from jsonschema.exceptions. + """ + + message = "Importing FormatError directly from the jsonschema package " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import FormatError + + self.assertEqual(FormatError, exceptions.FormatError) + self.assertEqual(w.filename, __file__) + + def test_import_Validator(self): + """ + As of v4.19.0, importing Validator from the package root is + deprecated in favor of doing so from jsonschema.protocols. + """ + + message = "Importing Validator directly from the jsonschema package " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import Validator + + self.assertEqual(Validator, protocols.Validator) + self.assertEqual(w.filename, __file__) + + def test_validators_validators(self): + """ + As of v4.0.0, accessing jsonschema.validators.validators is + deprecated. + """ + + message = "Accessing jsonschema.validators.validators is deprecated" + with self.assertWarnsRegex(DeprecationWarning, message) as w: + value = validators.validators + + self.assertEqual(value, validators._VALIDATORS) + self.assertEqual(w.filename, __file__) + + def test_validators_meta_schemas(self): + """ + As of v4.0.0, accessing jsonschema.validators.meta_schemas is + deprecated. + """ + + message = "Accessing jsonschema.validators.meta_schemas is deprecated" + with self.assertWarnsRegex(DeprecationWarning, message) as w: + value = validators.meta_schemas + + self.assertEqual(value, validators._META_SCHEMAS) + self.assertEqual(w.filename, __file__) + + def test_RefResolver_in_scope(self): + """ + As of v4.0.0, RefResolver.in_scope is deprecated. + """ + + resolver = validators._RefResolver.from_schema({}) + message = "jsonschema.RefResolver.in_scope is deprecated " + with self.assertWarnsRegex(DeprecationWarning, message) as w: # noqa: SIM117 + with resolver.in_scope("foo"): + pass + + self.assertEqual(w.filename, __file__) + + def test_Validator_is_valid_two_arguments(self): + """ + As of v4.0.0, calling is_valid with two arguments (to provide a + different schema) is deprecated. + """ + + validator = validators.Draft7Validator({}) + message = "Passing a schema to Validator.is_valid is deprecated " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + result = validator.is_valid("foo", {"type": "number"}) + + self.assertFalse(result) + self.assertEqual(w.filename, __file__) + + def test_Validator_iter_errors_two_arguments(self): + """ + As of v4.0.0, calling iter_errors with two arguments (to provide a + different schema) is deprecated. + """ + + validator = validators.Draft7Validator({}) + message = "Passing a schema to Validator.iter_errors is deprecated " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + error, = validator.iter_errors("foo", {"type": "number"}) + + self.assertEqual(error.validator, "type") + self.assertEqual(w.filename, __file__) + + def test_Validator_resolver(self): + """ + As of v4.18.0, accessing Validator.resolver is deprecated. + """ + + validator = validators.Draft7Validator({}) + message = "Accessing Draft7Validator.resolver is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + self.assertIsInstance(validator.resolver, validators._RefResolver) + + self.assertEqual(w.filename, __file__) + + def test_RefResolver(self): + """ + As of v4.18.0, RefResolver is fully deprecated. + """ + + message = "jsonschema.RefResolver is deprecated" + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import RefResolver + self.assertEqual(w.filename, __file__) + + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema.validators import RefResolver # noqa: F401 + self.assertEqual(w.filename, __file__) + + def test_RefResolutionError(self): + """ + As of v4.18.0, RefResolutionError is deprecated in favor of directly + catching errors from the referencing library. + """ + + message = "jsonschema.exceptions.RefResolutionError is deprecated" + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import RefResolutionError + + self.assertEqual(RefResolutionError, exceptions._RefResolutionError) + self.assertEqual(w.filename, __file__) + + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema.exceptions import RefResolutionError + + self.assertEqual(RefResolutionError, exceptions._RefResolutionError) + self.assertEqual(w.filename, __file__) + + def test_catching_Unresolvable_directly(self): + """ + This behavior is the intended behavior (i.e. it's not deprecated), but + given we do "tricksy" things in the iterim to wrap exceptions in a + multiple inheritance subclass, we need to be extra sure it works and + stays working. + """ + validator = validators.Draft202012Validator({"$ref": "urn:nothing"}) + + with self.assertRaises(referencing.exceptions.Unresolvable) as e: + validator.validate(12) + + expected = referencing.exceptions.Unresolvable(ref="urn:nothing") + self.assertEqual( + (e.exception, str(e.exception)), + (expected, "Unresolvable: urn:nothing"), + ) + + def test_catching_Unresolvable_via_RefResolutionError(self): + """ + Until RefResolutionError is removed, it is still possible to catch + exceptions from reference resolution using it, even though they may + have been raised by referencing. + """ + with self.assertWarns(DeprecationWarning): + from jsonschema import RefResolutionError + + validator = validators.Draft202012Validator({"$ref": "urn:nothing"}) + + with self.assertRaises(referencing.exceptions.Unresolvable) as u: + validator.validate(12) + + with self.assertRaises(RefResolutionError) as e: + validator.validate(12) + + self.assertEqual( + (e.exception, str(e.exception)), + (u.exception, "Unresolvable: urn:nothing"), + ) + + def test_WrappedReferencingError_hashability(self): + """ + Ensure the wrapped referencing errors are hashable when possible. + """ + with self.assertWarns(DeprecationWarning): + from jsonschema import RefResolutionError + + validator = validators.Draft202012Validator({"$ref": "urn:nothing"}) + + with self.assertRaises(referencing.exceptions.Unresolvable) as u: + validator.validate(12) + + with self.assertRaises(RefResolutionError) as e: + validator.validate(12) + + self.assertIn(e.exception, {u.exception}) + self.assertIn(u.exception, {e.exception}) + + def test_Validator_subclassing(self): + """ + As of v4.12.0, subclassing a validator class produces an explicit + deprecation warning. + + This was never intended to be public API (and some comments over the + years in issues said so, but obviously that's not a great way to make + sure it's followed). + + A future version will explicitly raise an error. + """ + + message = "Subclassing validator classes is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + class Subclass(validators.Draft202012Validator): + pass + + self.assertEqual(w.filename, __file__) + + with self.assertWarnsRegex(DeprecationWarning, message) as w: + class AnotherSubclass(validators.create(meta_schema={})): + pass + + def test_FormatChecker_cls_checks(self): + """ + As of v4.14.0, FormatChecker.cls_checks is deprecated without + replacement. + """ + + self.addCleanup(FormatChecker.checkers.pop, "boom", None) + + message = "FormatChecker.cls_checks " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + FormatChecker.cls_checks("boom") + + self.assertEqual(w.filename, __file__) + + def test_draftN_format_checker(self): + """ + As of v4.16.0, accessing jsonschema.draftn_format_checker is deprecated + in favor of Validator.FORMAT_CHECKER. + """ + + message = "Accessing jsonschema.draft202012_format_checker is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import draft202012_format_checker + + self.assertIs( + draft202012_format_checker, + validators.Draft202012Validator.FORMAT_CHECKER, + ) + self.assertEqual(w.filename, __file__) + + message = "Accessing jsonschema.draft201909_format_checker is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import draft201909_format_checker + + self.assertIs( + draft201909_format_checker, + validators.Draft201909Validator.FORMAT_CHECKER, + ) + self.assertEqual(w.filename, __file__) + + message = "Accessing jsonschema.draft7_format_checker is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import draft7_format_checker + + self.assertIs( + draft7_format_checker, + validators.Draft7Validator.FORMAT_CHECKER, + ) + self.assertEqual(w.filename, __file__) + + message = "Accessing jsonschema.draft6_format_checker is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import draft6_format_checker + + self.assertIs( + draft6_format_checker, + validators.Draft6Validator.FORMAT_CHECKER, + ) + self.assertEqual(w.filename, __file__) + + message = "Accessing jsonschema.draft4_format_checker is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import draft4_format_checker + + self.assertIs( + draft4_format_checker, + validators.Draft4Validator.FORMAT_CHECKER, + ) + self.assertEqual(w.filename, __file__) + + message = "Accessing jsonschema.draft3_format_checker is " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + from jsonschema import draft3_format_checker + + self.assertIs( + draft3_format_checker, + validators.Draft3Validator.FORMAT_CHECKER, + ) + self.assertEqual(w.filename, __file__) + + with self.assertRaises(ImportError): + from jsonschema import draft1234_format_checker # noqa: F401 + + def test_import_cli(self): + """ + As of v4.17.0, importing jsonschema.cli is deprecated. + """ + + message = "The jsonschema CLI is deprecated and will be removed " + with self.assertWarnsRegex(DeprecationWarning, message) as w: + import jsonschema.cli + importlib.reload(jsonschema.cli) + + self.assertEqual(w.filename, importlib.__file__) + + def test_cli(self): + """ + As of v4.17.0, the jsonschema CLI is deprecated. + """ + + process = subprocess.run( + [sys.executable, "-m", "jsonschema"], + capture_output=True, + check=True, + ) + self.assertIn(b"The jsonschema CLI is deprecated ", process.stderr) + + def test_automatic_remote_retrieval(self): + """ + Automatic retrieval of remote references is deprecated as of v4.18.0. + """ + ref = "http://bar#/$defs/baz" + schema = {"$defs": {"baz": {"type": "integer"}}} + + if "requests" in sys.modules: # pragma: no cover + self.addCleanup( + sys.modules.__setitem__, "requests", sys.modules["requests"], + ) + sys.modules["requests"] = None + + @contextmanager + def fake_urlopen(request): + self.assertIsInstance(request, urllib.request.Request) + self.assertEqual(request.full_url, "http://bar") + + # Ha ha urllib.request.Request "normalizes" header names and + # Request.get_header does not also normalize them... + (header, value), = request.header_items() + self.assertEqual(header.lower(), "user-agent") + self.assertEqual( + value, "python-jsonschema (deprecated $ref resolution)", + ) + yield BytesIO(json.dumps(schema).encode("utf8")) + + validator = validators.Draft202012Validator({"$ref": ref}) + + message = "Automatically retrieving remote references " + patch = mock.patch.object(urllib.request, "urlopen", new=fake_urlopen) + + with patch, self.assertWarnsRegex(DeprecationWarning, message): + self.assertEqual( + (validator.is_valid({}), validator.is_valid(37)), + (False, True), + ) diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/test_exceptions.py b/venv/lib/python3.12/site-packages/jsonschema/tests/test_exceptions.py new file mode 100644 index 0000000..358b924 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/tests/test_exceptions.py @@ -0,0 +1,759 @@ +from unittest import TestCase +import textwrap + +import jsonpath_ng + +from jsonschema import exceptions +from jsonschema.validators import _LATEST_VERSION + + +class TestBestMatch(TestCase): + def best_match_of(self, instance, schema): + errors = list(_LATEST_VERSION(schema).iter_errors(instance)) + msg = f"No errors found for {instance} under {schema!r}!" + self.assertTrue(errors, msg=msg) + + best = exceptions.best_match(iter(errors)) + reversed_best = exceptions.best_match(reversed(errors)) + + self.assertEqual( + best._contents(), + reversed_best._contents(), + f"No consistent best match!\nGot: {best}\n\nThen: {reversed_best}", + ) + return best + + def test_shallower_errors_are_better_matches(self): + schema = { + "properties": { + "foo": { + "minProperties": 2, + "properties": {"bar": {"type": "object"}}, + }, + }, + } + best = self.best_match_of(instance={"foo": {"bar": []}}, schema=schema) + self.assertEqual(best.validator, "minProperties") + + def test_oneOf_and_anyOf_are_weak_matches(self): + """ + A property you *must* match is probably better than one you have to + match a part of. + """ + + schema = { + "minProperties": 2, + "anyOf": [{"type": "string"}, {"type": "number"}], + "oneOf": [{"type": "string"}, {"type": "number"}], + } + best = self.best_match_of(instance={}, schema=schema) + self.assertEqual(best.validator, "minProperties") + + def test_if_the_most_relevant_error_is_anyOf_it_is_traversed(self): + """ + If the most relevant error is an anyOf, then we traverse its context + and select the otherwise *least* relevant error, since in this case + that means the most specific, deep, error inside the instance. + + I.e. since only one of the schemas must match, we look for the most + relevant one. + """ + + schema = { + "properties": { + "foo": { + "anyOf": [ + {"type": "string"}, + {"properties": {"bar": {"type": "array"}}}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": {"bar": 12}}, schema=schema) + self.assertEqual(best.validator_value, "array") + + def test_no_anyOf_traversal_for_equally_relevant_errors(self): + """ + We don't traverse into an anyOf (as above) if all of its context errors + seem to be equally "wrong" against the instance. + """ + + schema = { + "anyOf": [ + {"type": "string"}, + {"type": "integer"}, + {"type": "object"}, + ], + } + best = self.best_match_of(instance=[], schema=schema) + self.assertEqual(best.validator, "anyOf") + + def test_anyOf_traversal_for_single_equally_relevant_error(self): + """ + We *do* traverse anyOf with a single nested error, even though it is + vacuously equally relevant to itself. + """ + + schema = { + "anyOf": [ + {"type": "string"}, + ], + } + best = self.best_match_of(instance=[], schema=schema) + self.assertEqual(best.validator, "type") + + def test_anyOf_traversal_for_single_sibling_errors(self): + """ + We *do* traverse anyOf with a single subschema that fails multiple + times (e.g. on multiple items). + """ + + schema = { + "anyOf": [ + {"items": {"const": 37}}, + ], + } + best = self.best_match_of(instance=[12, 12], schema=schema) + self.assertEqual(best.validator, "const") + + def test_anyOf_traversal_for_non_type_matching_sibling_errors(self): + """ + We *do* traverse anyOf with multiple subschemas when one does not type + match. + """ + + schema = { + "anyOf": [ + {"type": "object"}, + {"items": {"const": 37}}, + ], + } + best = self.best_match_of(instance=[12, 12], schema=schema) + self.assertEqual(best.validator, "const") + + def test_if_the_most_relevant_error_is_oneOf_it_is_traversed(self): + """ + If the most relevant error is an oneOf, then we traverse its context + and select the otherwise *least* relevant error, since in this case + that means the most specific, deep, error inside the instance. + + I.e. since only one of the schemas must match, we look for the most + relevant one. + """ + + schema = { + "properties": { + "foo": { + "oneOf": [ + {"type": "string"}, + {"properties": {"bar": {"type": "array"}}}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": {"bar": 12}}, schema=schema) + self.assertEqual(best.validator_value, "array") + + def test_no_oneOf_traversal_for_equally_relevant_errors(self): + """ + We don't traverse into an oneOf (as above) if all of its context errors + seem to be equally "wrong" against the instance. + """ + + schema = { + "oneOf": [ + {"type": "string"}, + {"type": "integer"}, + {"type": "object"}, + ], + } + best = self.best_match_of(instance=[], schema=schema) + self.assertEqual(best.validator, "oneOf") + + def test_oneOf_traversal_for_single_equally_relevant_error(self): + """ + We *do* traverse oneOf with a single nested error, even though it is + vacuously equally relevant to itself. + """ + + schema = { + "oneOf": [ + {"type": "string"}, + ], + } + best = self.best_match_of(instance=[], schema=schema) + self.assertEqual(best.validator, "type") + + def test_oneOf_traversal_for_single_sibling_errors(self): + """ + We *do* traverse oneOf with a single subschema that fails multiple + times (e.g. on multiple items). + """ + + schema = { + "oneOf": [ + {"items": {"const": 37}}, + ], + } + best = self.best_match_of(instance=[12, 12], schema=schema) + self.assertEqual(best.validator, "const") + + def test_oneOf_traversal_for_non_type_matching_sibling_errors(self): + """ + We *do* traverse oneOf with multiple subschemas when one does not type + match. + """ + + schema = { + "oneOf": [ + {"type": "object"}, + {"items": {"const": 37}}, + ], + } + best = self.best_match_of(instance=[12, 12], schema=schema) + self.assertEqual(best.validator, "const") + + def test_if_the_most_relevant_error_is_allOf_it_is_traversed(self): + """ + Now, if the error is allOf, we traverse but select the *most* relevant + error from the context, because all schemas here must match anyways. + """ + + schema = { + "properties": { + "foo": { + "allOf": [ + {"type": "string"}, + {"properties": {"bar": {"type": "array"}}}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": {"bar": 12}}, schema=schema) + self.assertEqual(best.validator_value, "string") + + def test_nested_context_for_oneOf(self): + """ + We traverse into nested contexts (a oneOf containing an error in a + nested oneOf here). + """ + + schema = { + "properties": { + "foo": { + "oneOf": [ + {"type": "string"}, + { + "oneOf": [ + {"type": "string"}, + { + "properties": { + "bar": {"type": "array"}, + }, + }, + ], + }, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": {"bar": 12}}, schema=schema) + self.assertEqual(best.validator_value, "array") + + def test_it_prioritizes_matching_types(self): + schema = { + "properties": { + "foo": { + "anyOf": [ + {"type": "array", "minItems": 2}, + {"type": "string", "minLength": 10}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": "bar"}, schema=schema) + self.assertEqual(best.validator, "minLength") + + reordered = { + "properties": { + "foo": { + "anyOf": [ + {"type": "string", "minLength": 10}, + {"type": "array", "minItems": 2}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": "bar"}, schema=reordered) + self.assertEqual(best.validator, "minLength") + + def test_it_prioritizes_matching_union_types(self): + schema = { + "properties": { + "foo": { + "anyOf": [ + {"type": ["array", "object"], "minItems": 2}, + {"type": ["integer", "string"], "minLength": 10}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": "bar"}, schema=schema) + self.assertEqual(best.validator, "minLength") + + reordered = { + "properties": { + "foo": { + "anyOf": [ + {"type": "string", "minLength": 10}, + {"type": "array", "minItems": 2}, + ], + }, + }, + } + best = self.best_match_of(instance={"foo": "bar"}, schema=reordered) + self.assertEqual(best.validator, "minLength") + + def test_boolean_schemas(self): + schema = {"properties": {"foo": False}} + best = self.best_match_of(instance={"foo": "bar"}, schema=schema) + self.assertIsNone(best.validator) + + def test_one_error(self): + validator = _LATEST_VERSION({"minProperties": 2}) + validator.iter_errors({}) + self.assertEqual( + exceptions.best_match(validator.iter_errors({})).validator, + "minProperties", + ) + + def test_no_errors(self): + validator = _LATEST_VERSION({}) + self.assertIsNone(exceptions.best_match(validator.iter_errors({}))) + + +class TestByRelevance(TestCase): + def test_short_paths_are_better_matches(self): + shallow = exceptions.ValidationError("Oh no!", path=["baz"]) + deep = exceptions.ValidationError("Oh yes!", path=["foo", "bar"]) + match = max([shallow, deep], key=exceptions.relevance) + self.assertIs(match, shallow) + + match = max([deep, shallow], key=exceptions.relevance) + self.assertIs(match, shallow) + + def test_global_errors_are_even_better_matches(self): + shallow = exceptions.ValidationError("Oh no!", path=[]) + deep = exceptions.ValidationError("Oh yes!", path=["foo"]) + + errors = sorted([shallow, deep], key=exceptions.relevance) + self.assertEqual( + [list(error.path) for error in errors], + [["foo"], []], + ) + + errors = sorted([deep, shallow], key=exceptions.relevance) + self.assertEqual( + [list(error.path) for error in errors], + [["foo"], []], + ) + + def test_weak_keywords_are_lower_priority(self): + weak = exceptions.ValidationError("Oh no!", path=[], validator="a") + normal = exceptions.ValidationError("Oh yes!", path=[], validator="b") + + best_match = exceptions.by_relevance(weak="a") + + match = max([weak, normal], key=best_match) + self.assertIs(match, normal) + + match = max([normal, weak], key=best_match) + self.assertIs(match, normal) + + def test_strong_keywords_are_higher_priority(self): + weak = exceptions.ValidationError("Oh no!", path=[], validator="a") + normal = exceptions.ValidationError("Oh yes!", path=[], validator="b") + strong = exceptions.ValidationError("Oh fine!", path=[], validator="c") + + best_match = exceptions.by_relevance(weak="a", strong="c") + + match = max([weak, normal, strong], key=best_match) + self.assertIs(match, strong) + + match = max([strong, normal, weak], key=best_match) + self.assertIs(match, strong) + + +class TestErrorTree(TestCase): + def test_it_knows_how_many_total_errors_it_contains(self): + # FIXME: #442 + errors = [ + exceptions.ValidationError("Something", validator=i) + for i in range(8) + ] + tree = exceptions.ErrorTree(errors) + self.assertEqual(tree.total_errors, 8) + + def test_it_contains_an_item_if_the_item_had_an_error(self): + errors = [exceptions.ValidationError("a message", path=["bar"])] + tree = exceptions.ErrorTree(errors) + self.assertIn("bar", tree) + + def test_it_does_not_contain_an_item_if_the_item_had_no_error(self): + errors = [exceptions.ValidationError("a message", path=["bar"])] + tree = exceptions.ErrorTree(errors) + self.assertNotIn("foo", tree) + + def test_keywords_that_failed_appear_in_errors_dict(self): + error = exceptions.ValidationError("a message", validator="foo") + tree = exceptions.ErrorTree([error]) + self.assertEqual(tree.errors, {"foo": error}) + + def test_it_creates_a_child_tree_for_each_nested_path(self): + errors = [ + exceptions.ValidationError("a bar message", path=["bar"]), + exceptions.ValidationError("a bar -> 0 message", path=["bar", 0]), + ] + tree = exceptions.ErrorTree(errors) + self.assertIn(0, tree["bar"]) + self.assertNotIn(1, tree["bar"]) + + def test_children_have_their_errors_dicts_built(self): + e1, e2 = ( + exceptions.ValidationError("1", validator="foo", path=["bar", 0]), + exceptions.ValidationError("2", validator="quux", path=["bar", 0]), + ) + tree = exceptions.ErrorTree([e1, e2]) + self.assertEqual(tree["bar"][0].errors, {"foo": e1, "quux": e2}) + + def test_multiple_errors_with_instance(self): + e1, e2 = ( + exceptions.ValidationError( + "1", + validator="foo", + path=["bar", "bar2"], + instance="i1"), + exceptions.ValidationError( + "2", + validator="quux", + path=["foobar", 2], + instance="i2"), + ) + exceptions.ErrorTree([e1, e2]) + + def test_it_does_not_contain_subtrees_that_are_not_in_the_instance(self): + error = exceptions.ValidationError("123", validator="foo", instance=[]) + tree = exceptions.ErrorTree([error]) + + with self.assertRaises(IndexError): + tree[0] + + def test_if_its_in_the_tree_anyhow_it_does_not_raise_an_error(self): + """ + If a keyword refers to a path that isn't in the instance, the + tree still properly returns a subtree for that path. + """ + + error = exceptions.ValidationError( + "a message", validator="foo", instance={}, path=["foo"], + ) + tree = exceptions.ErrorTree([error]) + self.assertIsInstance(tree["foo"], exceptions.ErrorTree) + + def test_iter(self): + e1, e2 = ( + exceptions.ValidationError( + "1", + validator="foo", + path=["bar", "bar2"], + instance="i1"), + exceptions.ValidationError( + "2", + validator="quux", + path=["foobar", 2], + instance="i2"), + ) + tree = exceptions.ErrorTree([e1, e2]) + self.assertEqual(set(tree), {"bar", "foobar"}) + + def test_repr_single(self): + error = exceptions.ValidationError( + "1", + validator="foo", + path=["bar", "bar2"], + instance="i1", + ) + tree = exceptions.ErrorTree([error]) + self.assertEqual(repr(tree), "") + + def test_repr_multiple(self): + e1, e2 = ( + exceptions.ValidationError( + "1", + validator="foo", + path=["bar", "bar2"], + instance="i1"), + exceptions.ValidationError( + "2", + validator="quux", + path=["foobar", 2], + instance="i2"), + ) + tree = exceptions.ErrorTree([e1, e2]) + self.assertEqual(repr(tree), "") + + def test_repr_empty(self): + tree = exceptions.ErrorTree([]) + self.assertEqual(repr(tree), "") + + +class TestErrorInitReprStr(TestCase): + def make_error(self, **kwargs): + defaults = dict( + message="hello", + validator="type", + validator_value="string", + instance=5, + schema={"type": "string"}, + ) + defaults.update(kwargs) + return exceptions.ValidationError(**defaults) + + def assertShows(self, expected, **kwargs): + expected = textwrap.dedent(expected).rstrip("\n") + + error = self.make_error(**kwargs) + message_line, _, rest = str(error).partition("\n") + self.assertEqual(message_line, error.message) + self.assertEqual(rest, expected) + + def test_it_calls_super_and_sets_args(self): + error = self.make_error() + self.assertGreater(len(error.args), 1) + + def test_repr(self): + self.assertEqual( + repr(exceptions.ValidationError(message="Hello!")), + "", + ) + + def test_unset_error(self): + error = exceptions.ValidationError("message") + self.assertEqual(str(error), "message") + + kwargs = { + "validator": "type", + "validator_value": "string", + "instance": 5, + "schema": {"type": "string"}, + } + # Just the message should show if any of the attributes are unset + for attr in kwargs: + k = dict(kwargs) + del k[attr] + error = exceptions.ValidationError("message", **k) + self.assertEqual(str(error), "message") + + def test_empty_paths(self): + self.assertShows( + """ + Failed validating 'type' in schema: + {'type': 'string'} + + On instance: + 5 + """, + path=[], + schema_path=[], + ) + + def test_one_item_paths(self): + self.assertShows( + """ + Failed validating 'type' in schema: + {'type': 'string'} + + On instance[0]: + 5 + """, + path=[0], + schema_path=["items"], + ) + + def test_multiple_item_paths(self): + self.assertShows( + """ + Failed validating 'type' in schema['items'][0]: + {'type': 'string'} + + On instance[0]['a']: + 5 + """, + path=[0, "a"], + schema_path=["items", 0, 1], + ) + + def test_uses_pprint(self): + self.assertShows( + """ + Failed validating 'maxLength' in schema: + {0: 0, + 1: 1, + 2: 2, + 3: 3, + 4: 4, + 5: 5, + 6: 6, + 7: 7, + 8: 8, + 9: 9, + 10: 10, + 11: 11, + 12: 12, + 13: 13, + 14: 14, + 15: 15, + 16: 16, + 17: 17, + 18: 18, + 19: 19} + + On instance: + [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24] + """, + instance=list(range(25)), + schema=dict(zip(range(20), range(20))), + validator="maxLength", + ) + + def test_does_not_reorder_dicts(self): + self.assertShows( + """ + Failed validating 'type' in schema: + {'do': 3, 'not': 7, 'sort': 37, 'me': 73} + + On instance: + {'here': 73, 'too': 37, 'no': 7, 'sorting': 3} + """, + schema={ + "do": 3, + "not": 7, + "sort": 37, + "me": 73, + }, + instance={ + "here": 73, + "too": 37, + "no": 7, + "sorting": 3, + }, + ) + + def test_str_works_with_instances_having_overriden_eq_operator(self): + """ + Check for #164 which rendered exceptions unusable when a + `ValidationError` involved instances with an `__eq__` method + that returned truthy values. + """ + + class DontEQMeBro: + def __eq__(this, other): # pragma: no cover + self.fail("Don't!") + + def __ne__(this, other): # pragma: no cover + self.fail("Don't!") + + instance = DontEQMeBro() + error = exceptions.ValidationError( + "a message", + validator="foo", + instance=instance, + validator_value="some", + schema="schema", + ) + self.assertIn(repr(instance), str(error)) + + +class TestHashable(TestCase): + def test_hashable(self): + {exceptions.ValidationError("")} + {exceptions.SchemaError("")} + + +class TestJsonPathRendering(TestCase): + def validate_json_path_rendering(self, property_name, expected_path): + error = exceptions.ValidationError( + path=[property_name], + message="1", + validator="foo", + instance="i1", + ) + + rendered_json_path = error.json_path + self.assertEqual(rendered_json_path, expected_path) + + re_parsed_name = jsonpath_ng.parse(rendered_json_path).right.fields[0] + self.assertEqual(re_parsed_name, property_name) + + def test_basic(self): + self.validate_json_path_rendering("x", "$.x") + + def test_empty(self): + self.validate_json_path_rendering("", "$['']") + + def test_number(self): + self.validate_json_path_rendering("1", "$['1']") + + def test_period(self): + self.validate_json_path_rendering(".", "$['.']") + + def test_single_quote(self): + self.validate_json_path_rendering("'", r"$['\'']") + + def test_space(self): + self.validate_json_path_rendering(" ", "$[' ']") + + def test_backslash(self): + self.validate_json_path_rendering("\\", r"$['\\']") + + def test_backslash_single_quote(self): + self.validate_json_path_rendering(r"\'", r"$['\\\'']") + + def test_underscore(self): + self.validate_json_path_rendering("_", r"$['_']") + + def test_double_quote(self): + self.validate_json_path_rendering('"', """$['"']""") + + def test_hyphen(self): + self.validate_json_path_rendering("-", "$['-']") + + def test_json_path_injection(self): + self.validate_json_path_rendering("a[0]", "$['a[0]']") + + def test_open_bracket(self): + self.validate_json_path_rendering("[", "$['[']") diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/test_format.py b/venv/lib/python3.12/site-packages/jsonschema/tests/test_format.py new file mode 100644 index 0000000..d829f98 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/tests/test_format.py @@ -0,0 +1,91 @@ +""" +Tests for the parts of jsonschema related to the :kw:`format` keyword. +""" + +from unittest import TestCase + +from jsonschema import FormatChecker, ValidationError +from jsonschema.exceptions import FormatError +from jsonschema.validators import Draft4Validator + +BOOM = ValueError("Boom!") +BANG = ZeroDivisionError("Bang!") + + +def boom(thing): + if thing == "bang": + raise BANG + raise BOOM + + +class TestFormatChecker(TestCase): + def test_it_can_validate_no_formats(self): + checker = FormatChecker(formats=()) + self.assertFalse(checker.checkers) + + def test_it_raises_a_key_error_for_unknown_formats(self): + with self.assertRaises(KeyError): + FormatChecker(formats=["o noes"]) + + def test_it_can_register_cls_checkers(self): + original = dict(FormatChecker.checkers) + self.addCleanup(FormatChecker.checkers.pop, "boom") + with self.assertWarns(DeprecationWarning): + FormatChecker.cls_checks("boom")(boom) + self.assertEqual( + FormatChecker.checkers, + dict(original, boom=(boom, ())), + ) + + def test_it_can_register_checkers(self): + checker = FormatChecker() + checker.checks("boom")(boom) + self.assertEqual( + checker.checkers, + dict(FormatChecker.checkers, boom=(boom, ())), + ) + + def test_it_catches_registered_errors(self): + checker = FormatChecker() + checker.checks("boom", raises=type(BOOM))(boom) + + with self.assertRaises(FormatError) as cm: + checker.check(instance=12, format="boom") + + self.assertIs(cm.exception.cause, BOOM) + self.assertIs(cm.exception.__cause__, BOOM) + self.assertEqual(str(cm.exception), "12 is not a 'boom'") + + # Unregistered errors should not be caught + with self.assertRaises(type(BANG)): + checker.check(instance="bang", format="boom") + + def test_format_error_causes_become_validation_error_causes(self): + checker = FormatChecker() + checker.checks("boom", raises=ValueError)(boom) + validator = Draft4Validator({"format": "boom"}, format_checker=checker) + + with self.assertRaises(ValidationError) as cm: + validator.validate("BOOM") + + self.assertIs(cm.exception.cause, BOOM) + self.assertIs(cm.exception.__cause__, BOOM) + + def test_format_checkers_come_with_defaults(self): + # This is bad :/ but relied upon. + # The docs for quite awhile recommended people do things like + # validate(..., format_checker=FormatChecker()) + # We should change that, but we can't without deprecation... + checker = FormatChecker() + with self.assertRaises(FormatError): + checker.check(instance="not-an-ipv4", format="ipv4") + + def test_repr(self): + checker = FormatChecker(formats=()) + checker.checks("foo")(lambda thing: True) # pragma: no cover + checker.checks("bar")(lambda thing: True) # pragma: no cover + checker.checks("baz")(lambda thing: True) # pragma: no cover + self.assertEqual( + repr(checker), + "", + ) diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/test_jsonschema_test_suite.py b/venv/lib/python3.12/site-packages/jsonschema/tests/test_jsonschema_test_suite.py new file mode 100644 index 0000000..41c9825 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/tests/test_jsonschema_test_suite.py @@ -0,0 +1,262 @@ +""" +Test runner for the JSON Schema official test suite + +Tests comprehensive correctness of each draft's validator. + +See https://github.com/json-schema-org/JSON-Schema-Test-Suite for details. +""" + + +from jsonschema.tests._suite import Suite +import jsonschema + +SUITE = Suite() +DRAFT3 = SUITE.version(name="draft3") +DRAFT4 = SUITE.version(name="draft4") +DRAFT6 = SUITE.version(name="draft6") +DRAFT7 = SUITE.version(name="draft7") +DRAFT201909 = SUITE.version(name="draft2019-09") +DRAFT202012 = SUITE.version(name="draft2020-12") + + +def skip(message, **kwargs): + def skipper(test): + if all(value == getattr(test, attr) for attr, value in kwargs.items()): + return message + return skipper + + +def ecmascript_regex(test): + if test.subject == "ecmascript-regex": + return "ECMA regex support will be added in #1142." + + +def missing_format(Validator): + def missing_format(test): # pragma: no cover + schema = test.schema + if ( + schema is True + or schema is False + or "format" not in schema + or schema["format"] in Validator.FORMAT_CHECKER.checkers + or test.valid + ): + return + + return f"Format checker {schema['format']!r} not found." + return missing_format + + +def complex_email_validation(test): + if test.subject != "email": + return + + message = "Complex email validation is (intentionally) unsupported." + return skip( + message=message, + description="an invalid domain", + )(test) or skip( + message=message, + description="an invalid IPv4-address-literal", + )(test) or skip( + message=message, + description="dot after local part is not valid", + )(test) or skip( + message=message, + description="dot before local part is not valid", + )(test) or skip( + message=message, + description="two subsequent dots inside local part are not valid", + )(test) + + +def leap_second(test): + message = "Leap seconds are unsupported." + return skip( + message=message, + subject="time", + description="a valid time string with leap second", + )(test) or skip( + message=message, + subject="time", + description="a valid time string with leap second, Zulu", + )(test) or skip( + message=message, + subject="time", + description="a valid time string with leap second with offset", + )(test) or skip( + message=message, + subject="time", + description="valid leap second, positive time-offset", + )(test) or skip( + message=message, + subject="time", + description="valid leap second, negative time-offset", + )(test) or skip( + message=message, + subject="time", + description="valid leap second, large positive time-offset", + )(test) or skip( + message=message, + subject="time", + description="valid leap second, large negative time-offset", + )(test) or skip( + message=message, + subject="time", + description="valid leap second, zero time-offset", + )(test) or skip( + message=message, + subject="date-time", + description="a valid date-time with a leap second, UTC", + )(test) or skip( + message=message, + subject="date-time", + description="a valid date-time with a leap second, with minus offset", + )(test) + + +TestDraft3 = DRAFT3.to_unittest_testcase( + DRAFT3.cases(), + DRAFT3.format_cases(), + DRAFT3.optional_cases_of(name="bignum"), + DRAFT3.optional_cases_of(name="non-bmp-regex"), + DRAFT3.optional_cases_of(name="zeroTerminatedFloats"), + Validator=jsonschema.Draft3Validator, + format_checker=jsonschema.Draft3Validator.FORMAT_CHECKER, + skip=lambda test: ( + ecmascript_regex(test) + or missing_format(jsonschema.Draft3Validator)(test) + or complex_email_validation(test) + ), +) + + +TestDraft4 = DRAFT4.to_unittest_testcase( + DRAFT4.cases(), + DRAFT4.format_cases(), + DRAFT4.optional_cases_of(name="bignum"), + DRAFT4.optional_cases_of(name="float-overflow"), + DRAFT4.optional_cases_of(name="id"), + DRAFT4.optional_cases_of(name="non-bmp-regex"), + DRAFT4.optional_cases_of(name="zeroTerminatedFloats"), + Validator=jsonschema.Draft4Validator, + format_checker=jsonschema.Draft4Validator.FORMAT_CHECKER, + skip=lambda test: ( + ecmascript_regex(test) + or leap_second(test) + or missing_format(jsonschema.Draft4Validator)(test) + or complex_email_validation(test) + ), +) + + +TestDraft6 = DRAFT6.to_unittest_testcase( + DRAFT6.cases(), + DRAFT6.format_cases(), + DRAFT6.optional_cases_of(name="bignum"), + DRAFT6.optional_cases_of(name="float-overflow"), + DRAFT6.optional_cases_of(name="id"), + DRAFT6.optional_cases_of(name="non-bmp-regex"), + Validator=jsonschema.Draft6Validator, + format_checker=jsonschema.Draft6Validator.FORMAT_CHECKER, + skip=lambda test: ( + ecmascript_regex(test) + or leap_second(test) + or missing_format(jsonschema.Draft6Validator)(test) + or complex_email_validation(test) + ), +) + + +TestDraft7 = DRAFT7.to_unittest_testcase( + DRAFT7.cases(), + DRAFT7.format_cases(), + DRAFT7.optional_cases_of(name="bignum"), + DRAFT7.optional_cases_of(name="cross-draft"), + DRAFT7.optional_cases_of(name="float-overflow"), + DRAFT6.optional_cases_of(name="id"), + DRAFT7.optional_cases_of(name="non-bmp-regex"), + DRAFT7.optional_cases_of(name="unknownKeyword"), + Validator=jsonschema.Draft7Validator, + format_checker=jsonschema.Draft7Validator.FORMAT_CHECKER, + skip=lambda test: ( + ecmascript_regex(test) + or leap_second(test) + or missing_format(jsonschema.Draft7Validator)(test) + or complex_email_validation(test) + ), +) + + +TestDraft201909 = DRAFT201909.to_unittest_testcase( + DRAFT201909.cases(), + DRAFT201909.optional_cases_of(name="anchor"), + DRAFT201909.optional_cases_of(name="bignum"), + DRAFT201909.optional_cases_of(name="cross-draft"), + DRAFT201909.optional_cases_of(name="float-overflow"), + DRAFT201909.optional_cases_of(name="id"), + DRAFT201909.optional_cases_of(name="no-schema"), + DRAFT201909.optional_cases_of(name="non-bmp-regex"), + DRAFT201909.optional_cases_of(name="refOfUnknownKeyword"), + DRAFT201909.optional_cases_of(name="unknownKeyword"), + Validator=jsonschema.Draft201909Validator, + skip=skip( + message="Vocabulary support is still in-progress.", + subject="vocabulary", + description=( + "no validation: invalid number, but it still validates" + ), + ), +) + + +TestDraft201909Format = DRAFT201909.to_unittest_testcase( + DRAFT201909.format_cases(), + name="TestDraft201909Format", + Validator=jsonschema.Draft201909Validator, + format_checker=jsonschema.Draft201909Validator.FORMAT_CHECKER, + skip=lambda test: ( + complex_email_validation(test) + or ecmascript_regex(test) + or leap_second(test) + or missing_format(jsonschema.Draft201909Validator)(test) + or complex_email_validation(test) + ), +) + + +TestDraft202012 = DRAFT202012.to_unittest_testcase( + DRAFT202012.cases(), + DRAFT201909.optional_cases_of(name="anchor"), + DRAFT202012.optional_cases_of(name="bignum"), + DRAFT202012.optional_cases_of(name="cross-draft"), + DRAFT202012.optional_cases_of(name="float-overflow"), + DRAFT202012.optional_cases_of(name="id"), + DRAFT202012.optional_cases_of(name="no-schema"), + DRAFT202012.optional_cases_of(name="non-bmp-regex"), + DRAFT202012.optional_cases_of(name="refOfUnknownKeyword"), + DRAFT202012.optional_cases_of(name="unknownKeyword"), + Validator=jsonschema.Draft202012Validator, + skip=skip( + message="Vocabulary support is still in-progress.", + subject="vocabulary", + description=( + "no validation: invalid number, but it still validates" + ), + ), +) + + +TestDraft202012Format = DRAFT202012.to_unittest_testcase( + DRAFT202012.format_cases(), + name="TestDraft202012Format", + Validator=jsonschema.Draft202012Validator, + format_checker=jsonschema.Draft202012Validator.FORMAT_CHECKER, + skip=lambda test: ( + complex_email_validation(test) + or ecmascript_regex(test) + or leap_second(test) + or missing_format(jsonschema.Draft202012Validator)(test) + or complex_email_validation(test) + ), +) diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/test_types.py b/venv/lib/python3.12/site-packages/jsonschema/tests/test_types.py new file mode 100644 index 0000000..bd97b18 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/tests/test_types.py @@ -0,0 +1,221 @@ +""" +Tests for the `TypeChecker`-based type interface. + +The actual correctness of the type checking is handled in +`test_jsonschema_test_suite`; these tests check that TypeChecker +functions correctly at a more granular level. +""" +from collections import namedtuple +from unittest import TestCase + +from jsonschema import ValidationError, _keywords +from jsonschema._types import TypeChecker +from jsonschema.exceptions import UndefinedTypeCheck, UnknownType +from jsonschema.validators import Draft202012Validator, extend + + +def equals_2(checker, instance): + return instance == 2 + + +def is_namedtuple(instance): + return isinstance(instance, tuple) and getattr(instance, "_fields", None) + + +def is_object_or_named_tuple(checker, instance): + if Draft202012Validator.TYPE_CHECKER.is_type(instance, "object"): + return True + return is_namedtuple(instance) + + +class TestTypeChecker(TestCase): + def test_is_type(self): + checker = TypeChecker({"two": equals_2}) + self.assertEqual( + ( + checker.is_type(instance=2, type="two"), + checker.is_type(instance="bar", type="two"), + ), + (True, False), + ) + + def test_is_unknown_type(self): + with self.assertRaises(UndefinedTypeCheck) as e: + TypeChecker().is_type(4, "foobar") + self.assertIn( + "'foobar' is unknown to this type checker", + str(e.exception), + ) + self.assertTrue( + e.exception.__suppress_context__, + msg="Expected the internal KeyError to be hidden.", + ) + + def test_checks_can_be_added_at_init(self): + checker = TypeChecker({"two": equals_2}) + self.assertEqual(checker, TypeChecker().redefine("two", equals_2)) + + def test_redefine_existing_type(self): + self.assertEqual( + TypeChecker().redefine("two", object()).redefine("two", equals_2), + TypeChecker().redefine("two", equals_2), + ) + + def test_remove(self): + self.assertEqual( + TypeChecker({"two": equals_2}).remove("two"), + TypeChecker(), + ) + + def test_remove_unknown_type(self): + with self.assertRaises(UndefinedTypeCheck) as context: + TypeChecker().remove("foobar") + self.assertIn("foobar", str(context.exception)) + + def test_redefine_many(self): + self.assertEqual( + TypeChecker().redefine_many({"foo": int, "bar": str}), + TypeChecker().redefine("foo", int).redefine("bar", str), + ) + + def test_remove_multiple(self): + self.assertEqual( + TypeChecker({"foo": int, "bar": str}).remove("foo", "bar"), + TypeChecker(), + ) + + def test_type_check_can_raise_key_error(self): + """ + Make sure no one writes: + + try: + self._type_checkers[type](...) + except KeyError: + + ignoring the fact that the function itself can raise that. + """ + + error = KeyError("Stuff") + + def raises_keyerror(checker, instance): + raise error + + with self.assertRaises(KeyError) as context: + TypeChecker({"foo": raises_keyerror}).is_type(4, "foo") + + self.assertIs(context.exception, error) + + def test_repr(self): + checker = TypeChecker({"foo": is_namedtuple, "bar": is_namedtuple}) + self.assertEqual(repr(checker), "") + + +class TestCustomTypes(TestCase): + def test_simple_type_can_be_extended(self): + def int_or_str_int(checker, instance): + if not isinstance(instance, (int, str)): + return False + try: + int(instance) + except ValueError: + return False + return True + + CustomValidator = extend( + Draft202012Validator, + type_checker=Draft202012Validator.TYPE_CHECKER.redefine( + "integer", int_or_str_int, + ), + ) + validator = CustomValidator({"type": "integer"}) + + validator.validate(4) + validator.validate("4") + + with self.assertRaises(ValidationError): + validator.validate(4.4) + + with self.assertRaises(ValidationError): + validator.validate("foo") + + def test_object_can_be_extended(self): + schema = {"type": "object"} + + Point = namedtuple("Point", ["x", "y"]) + + type_checker = Draft202012Validator.TYPE_CHECKER.redefine( + "object", is_object_or_named_tuple, + ) + + CustomValidator = extend( + Draft202012Validator, + type_checker=type_checker, + ) + validator = CustomValidator(schema) + + validator.validate(Point(x=4, y=5)) + + def test_object_extensions_require_custom_validators(self): + schema = {"type": "object", "required": ["x"]} + + type_checker = Draft202012Validator.TYPE_CHECKER.redefine( + "object", is_object_or_named_tuple, + ) + + CustomValidator = extend( + Draft202012Validator, + type_checker=type_checker, + ) + validator = CustomValidator(schema) + + Point = namedtuple("Point", ["x", "y"]) + # Cannot handle required + with self.assertRaises(ValidationError): + validator.validate(Point(x=4, y=5)) + + def test_object_extensions_can_handle_custom_validators(self): + schema = { + "type": "object", + "required": ["x"], + "properties": {"x": {"type": "integer"}}, + } + + type_checker = Draft202012Validator.TYPE_CHECKER.redefine( + "object", is_object_or_named_tuple, + ) + + def coerce_named_tuple(fn): + def coerced(validator, value, instance, schema): + if is_namedtuple(instance): + instance = instance._asdict() + return fn(validator, value, instance, schema) + return coerced + + required = coerce_named_tuple(_keywords.required) + properties = coerce_named_tuple(_keywords.properties) + + CustomValidator = extend( + Draft202012Validator, + type_checker=type_checker, + validators={"required": required, "properties": properties}, + ) + + validator = CustomValidator(schema) + + Point = namedtuple("Point", ["x", "y"]) + # Can now process required and properties + validator.validate(Point(x=4, y=5)) + + with self.assertRaises(ValidationError): + validator.validate(Point(x="not an integer", y=5)) + + # As well as still handle objects. + validator.validate({"x": 4, "y": 5}) + + with self.assertRaises(ValidationError): + validator.validate({"x": "not an integer", "y": 5}) + + def test_unknown_type(self): + with self.assertRaises(UnknownType) as e: + Draft202012Validator({}).is_type(12, "some unknown type") + self.assertIn("'some unknown type'", str(e.exception)) diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/test_utils.py b/venv/lib/python3.12/site-packages/jsonschema/tests/test_utils.py new file mode 100644 index 0000000..d9764b0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/tests/test_utils.py @@ -0,0 +1,138 @@ +from math import nan +from unittest import TestCase + +from jsonschema._utils import equal + + +class TestEqual(TestCase): + def test_none(self): + self.assertTrue(equal(None, None)) + + def test_nan(self): + self.assertTrue(equal(nan, nan)) + + +class TestDictEqual(TestCase): + def test_equal_dictionaries(self): + dict_1 = {"a": "b", "c": "d"} + dict_2 = {"c": "d", "a": "b"} + self.assertTrue(equal(dict_1, dict_2)) + + def test_equal_dictionaries_with_nan(self): + dict_1 = {"a": nan, "c": "d"} + dict_2 = {"c": "d", "a": nan} + self.assertTrue(equal(dict_1, dict_2)) + + def test_missing_key(self): + dict_1 = {"a": "b", "c": "d"} + dict_2 = {"c": "d", "x": "b"} + self.assertFalse(equal(dict_1, dict_2)) + + def test_additional_key(self): + dict_1 = {"a": "b", "c": "d"} + dict_2 = {"c": "d", "a": "b", "x": "x"} + self.assertFalse(equal(dict_1, dict_2)) + + def test_missing_value(self): + dict_1 = {"a": "b", "c": "d"} + dict_2 = {"c": "d", "a": "x"} + self.assertFalse(equal(dict_1, dict_2)) + + def test_empty_dictionaries(self): + dict_1 = {} + dict_2 = {} + self.assertTrue(equal(dict_1, dict_2)) + + def test_one_none(self): + dict_1 = None + dict_2 = {"a": "b", "c": "d"} + self.assertFalse(equal(dict_1, dict_2)) + + def test_same_item(self): + dict_1 = {"a": "b", "c": "d"} + self.assertTrue(equal(dict_1, dict_1)) + + def test_nested_equal(self): + dict_1 = {"a": {"a": "b", "c": "d"}, "c": "d"} + dict_2 = {"c": "d", "a": {"a": "b", "c": "d"}} + self.assertTrue(equal(dict_1, dict_2)) + + def test_nested_dict_unequal(self): + dict_1 = {"a": {"a": "b", "c": "d"}, "c": "d"} + dict_2 = {"c": "d", "a": {"a": "b", "c": "x"}} + self.assertFalse(equal(dict_1, dict_2)) + + def test_mixed_nested_equal(self): + dict_1 = {"a": ["a", "b", "c", "d"], "c": "d"} + dict_2 = {"c": "d", "a": ["a", "b", "c", "d"]} + self.assertTrue(equal(dict_1, dict_2)) + + def test_nested_list_unequal(self): + dict_1 = {"a": ["a", "b", "c", "d"], "c": "d"} + dict_2 = {"c": "d", "a": ["b", "c", "d", "a"]} + self.assertFalse(equal(dict_1, dict_2)) + + +class TestListEqual(TestCase): + def test_equal_lists(self): + list_1 = ["a", "b", "c"] + list_2 = ["a", "b", "c"] + self.assertTrue(equal(list_1, list_2)) + + def test_equal_lists_with_nan(self): + list_1 = ["a", nan, "c"] + list_2 = ["a", nan, "c"] + self.assertTrue(equal(list_1, list_2)) + + def test_unsorted_lists(self): + list_1 = ["a", "b", "c"] + list_2 = ["b", "b", "a"] + self.assertFalse(equal(list_1, list_2)) + + def test_first_list_larger(self): + list_1 = ["a", "b", "c"] + list_2 = ["a", "b"] + self.assertFalse(equal(list_1, list_2)) + + def test_second_list_larger(self): + list_1 = ["a", "b"] + list_2 = ["a", "b", "c"] + self.assertFalse(equal(list_1, list_2)) + + def test_list_with_none_unequal(self): + list_1 = ["a", "b", None] + list_2 = ["a", "b", "c"] + self.assertFalse(equal(list_1, list_2)) + + list_1 = ["a", "b", None] + list_2 = [None, "b", "c"] + self.assertFalse(equal(list_1, list_2)) + + def test_list_with_none_equal(self): + list_1 = ["a", None, "c"] + list_2 = ["a", None, "c"] + self.assertTrue(equal(list_1, list_2)) + + def test_empty_list(self): + list_1 = [] + list_2 = [] + self.assertTrue(equal(list_1, list_2)) + + def test_one_none(self): + list_1 = None + list_2 = [] + self.assertFalse(equal(list_1, list_2)) + + def test_same_list(self): + list_1 = ["a", "b", "c"] + self.assertTrue(equal(list_1, list_1)) + + def test_equal_nested_lists(self): + list_1 = ["a", ["b", "c"], "d"] + list_2 = ["a", ["b", "c"], "d"] + self.assertTrue(equal(list_1, list_2)) + + def test_unequal_nested_lists(self): + list_1 = ["a", ["b", "c"], "d"] + list_2 = ["a", [], "c"] + self.assertFalse(equal(list_1, list_2)) diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/test_validators.py b/venv/lib/python3.12/site-packages/jsonschema/tests/test_validators.py new file mode 100644 index 0000000..d419ddf --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/tests/test_validators.py @@ -0,0 +1,2573 @@ +from __future__ import annotations + +from collections import deque, namedtuple +from contextlib import contextmanager +from decimal import Decimal +from io import BytesIO +from typing import Any +from unittest import TestCase, mock +from urllib.request import pathname2url +import json +import os +import sys +import tempfile +import warnings + +from attrs import define, field +from referencing.jsonschema import DRAFT202012 +import referencing.exceptions + +from jsonschema import ( + FormatChecker, + TypeChecker, + exceptions, + protocols, + validators, +) + + +def fail(validator, errors, instance, schema): + for each in errors: + each.setdefault("message", "You told me to fail!") + yield exceptions.ValidationError(**each) + + +class TestCreateAndExtend(TestCase): + def setUp(self): + self.addCleanup( + self.assertEqual, + validators._META_SCHEMAS, + dict(validators._META_SCHEMAS), + ) + self.addCleanup( + self.assertEqual, + validators._VALIDATORS, + dict(validators._VALIDATORS), + ) + + self.meta_schema = {"$id": "some://meta/schema"} + self.validators = {"fail": fail} + self.type_checker = TypeChecker() + self.Validator = validators.create( + meta_schema=self.meta_schema, + validators=self.validators, + type_checker=self.type_checker, + ) + + def test_attrs(self): + self.assertEqual( + ( + self.Validator.VALIDATORS, + self.Validator.META_SCHEMA, + self.Validator.TYPE_CHECKER, + ), ( + self.validators, + self.meta_schema, + self.type_checker, + ), + ) + + def test_init(self): + schema = {"fail": []} + self.assertEqual(self.Validator(schema).schema, schema) + + def test_iter_errors_successful(self): + schema = {"fail": []} + validator = self.Validator(schema) + + errors = list(validator.iter_errors("hello")) + self.assertEqual(errors, []) + + def test_iter_errors_one_error(self): + schema = {"fail": [{"message": "Whoops!"}]} + validator = self.Validator(schema) + + expected_error = exceptions.ValidationError( + "Whoops!", + instance="goodbye", + schema=schema, + validator="fail", + validator_value=[{"message": "Whoops!"}], + schema_path=deque(["fail"]), + ) + + errors = list(validator.iter_errors("goodbye")) + self.assertEqual(len(errors), 1) + self.assertEqual(errors[0]._contents(), expected_error._contents()) + + def test_iter_errors_multiple_errors(self): + schema = { + "fail": [ + {"message": "First"}, + {"message": "Second!", "validator": "asdf"}, + {"message": "Third"}, + ], + } + validator = self.Validator(schema) + + errors = list(validator.iter_errors("goodbye")) + self.assertEqual(len(errors), 3) + + def test_if_a_version_is_provided_it_is_registered(self): + Validator = validators.create( + meta_schema={"$id": "something"}, + version="my version", + ) + self.addCleanup(validators._META_SCHEMAS.pop, "something") + self.addCleanup(validators._VALIDATORS.pop, "my version") + self.assertEqual(Validator.__name__, "MyVersionValidator") + self.assertEqual(Validator.__qualname__, "MyVersionValidator") + + def test_repr(self): + Validator = validators.create( + meta_schema={"$id": "something"}, + version="my version", + ) + self.addCleanup(validators._META_SCHEMAS.pop, "something") + self.addCleanup(validators._VALIDATORS.pop, "my version") + self.assertEqual( + repr(Validator({})), + "MyVersionValidator(schema={}, format_checker=None)", + ) + + def test_long_repr(self): + Validator = validators.create( + meta_schema={"$id": "something"}, + version="my version", + ) + self.addCleanup(validators._META_SCHEMAS.pop, "something") + self.addCleanup(validators._VALIDATORS.pop, "my version") + self.assertEqual( + repr(Validator({"a": list(range(1000))})), ( + "MyVersionValidator(schema={'a': [0, 1, 2, 3, 4, 5, ...]}, " + "format_checker=None)" + ), + ) + + def test_repr_no_version(self): + Validator = validators.create(meta_schema={}) + self.assertEqual( + repr(Validator({})), + "Validator(schema={}, format_checker=None)", + ) + + def test_dashes_are_stripped_from_validator_names(self): + Validator = validators.create( + meta_schema={"$id": "something"}, + version="foo-bar", + ) + self.addCleanup(validators._META_SCHEMAS.pop, "something") + self.addCleanup(validators._VALIDATORS.pop, "foo-bar") + self.assertEqual(Validator.__qualname__, "FooBarValidator") + + def test_if_a_version_is_not_provided_it_is_not_registered(self): + original = dict(validators._META_SCHEMAS) + validators.create(meta_schema={"id": "id"}) + self.assertEqual(validators._META_SCHEMAS, original) + + def test_validates_registers_meta_schema_id(self): + meta_schema_key = "meta schema id" + my_meta_schema = {"id": meta_schema_key} + + validators.create( + meta_schema=my_meta_schema, + version="my version", + id_of=lambda s: s.get("id", ""), + ) + self.addCleanup(validators._META_SCHEMAS.pop, meta_schema_key) + self.addCleanup(validators._VALIDATORS.pop, "my version") + + self.assertIn(meta_schema_key, validators._META_SCHEMAS) + + def test_validates_registers_meta_schema_draft6_id(self): + meta_schema_key = "meta schema $id" + my_meta_schema = {"$id": meta_schema_key} + + validators.create( + meta_schema=my_meta_schema, + version="my version", + ) + self.addCleanup(validators._META_SCHEMAS.pop, meta_schema_key) + self.addCleanup(validators._VALIDATORS.pop, "my version") + + self.assertIn(meta_schema_key, validators._META_SCHEMAS) + + def test_create_default_types(self): + Validator = validators.create(meta_schema={}, validators=()) + self.assertTrue( + all( + Validator({}).is_type(instance=instance, type=type) + for type, instance in [ + ("array", []), + ("boolean", True), + ("integer", 12), + ("null", None), + ("number", 12.0), + ("object", {}), + ("string", "foo"), + ] + ), + ) + + def test_check_schema_with_different_metaschema(self): + """ + One can create a validator class whose metaschema uses a different + dialect than itself. + """ + + NoEmptySchemasValidator = validators.create( + meta_schema={ + "$schema": validators.Draft202012Validator.META_SCHEMA["$id"], + "not": {"const": {}}, + }, + ) + NoEmptySchemasValidator.check_schema({"foo": "bar"}) + + with self.assertRaises(exceptions.SchemaError): + NoEmptySchemasValidator.check_schema({}) + + NoEmptySchemasValidator({"foo": "bar"}).validate("foo") + + def test_check_schema_with_different_metaschema_defaults_to_self(self): + """ + A validator whose metaschema doesn't declare $schema defaults to its + own validation behavior, not the latest "normal" specification. + """ + + NoEmptySchemasValidator = validators.create( + meta_schema={"fail": [{"message": "Meta schema whoops!"}]}, + validators={"fail": fail}, + ) + with self.assertRaises(exceptions.SchemaError): + NoEmptySchemasValidator.check_schema({}) + + def test_extend(self): + original = dict(self.Validator.VALIDATORS) + new = object() + + Extended = validators.extend( + self.Validator, + validators={"new": new}, + ) + self.assertEqual( + ( + Extended.VALIDATORS, + Extended.META_SCHEMA, + Extended.TYPE_CHECKER, + self.Validator.VALIDATORS, + ), ( + dict(original, new=new), + self.Validator.META_SCHEMA, + self.Validator.TYPE_CHECKER, + original, + ), + ) + + def test_extend_idof(self): + """ + Extending a validator preserves its notion of schema IDs. + """ + def id_of(schema): + return schema.get("__test__", self.Validator.ID_OF(schema)) + correct_id = "the://correct/id/" + meta_schema = { + "$id": "the://wrong/id/", + "__test__": correct_id, + } + Original = validators.create( + meta_schema=meta_schema, + validators=self.validators, + type_checker=self.type_checker, + id_of=id_of, + ) + self.assertEqual(Original.ID_OF(Original.META_SCHEMA), correct_id) + + Derived = validators.extend(Original) + self.assertEqual(Derived.ID_OF(Derived.META_SCHEMA), correct_id) + + def test_extend_applicable_validators(self): + """ + Extending a validator preserves its notion of applicable validators. + """ + + schema = { + "$defs": {"test": {"type": "number"}}, + "$ref": "#/$defs/test", + "maximum": 1, + } + + draft4 = validators.Draft4Validator(schema) + self.assertTrue(draft4.is_valid(37)) # as $ref ignores siblings + + Derived = validators.extend(validators.Draft4Validator) + self.assertTrue(Derived(schema).is_valid(37)) + + +class TestValidationErrorMessages(TestCase): + def message_for(self, instance, schema, *args, **kwargs): + cls = kwargs.pop("cls", validators._LATEST_VERSION) + cls.check_schema(schema) + validator = cls(schema, *args, **kwargs) + errors = list(validator.iter_errors(instance)) + self.assertTrue(errors, msg=f"No errors were raised for {instance!r}") + self.assertEqual( + len(errors), + 1, + msg=f"Expected exactly one error, found {errors!r}", + ) + return errors[0].message + + def test_single_type_failure(self): + message = self.message_for(instance=1, schema={"type": "string"}) + self.assertEqual(message, "1 is not of type 'string'") + + def test_single_type_list_failure(self): + message = self.message_for(instance=1, schema={"type": ["string"]}) + self.assertEqual(message, "1 is not of type 'string'") + + def test_multiple_type_failure(self): + types = "string", "object" + message = self.message_for(instance=1, schema={"type": list(types)}) + self.assertEqual(message, "1 is not of type 'string', 'object'") + + def test_object_with_named_type_failure(self): + schema = {"type": [{"name": "Foo", "minimum": 3}]} + message = self.message_for( + instance=1, + schema=schema, + cls=validators.Draft3Validator, + ) + self.assertEqual(message, "1 is not of type 'Foo'") + + def test_minimum(self): + message = self.message_for(instance=1, schema={"minimum": 2}) + self.assertEqual(message, "1 is less than the minimum of 2") + + def test_maximum(self): + message = self.message_for(instance=1, schema={"maximum": 0}) + self.assertEqual(message, "1 is greater than the maximum of 0") + + def test_dependencies_single_element(self): + depend, on = "bar", "foo" + schema = {"dependencies": {depend: on}} + message = self.message_for( + instance={"bar": 2}, + schema=schema, + cls=validators.Draft3Validator, + ) + self.assertEqual(message, "'foo' is a dependency of 'bar'") + + def test_object_without_title_type_failure_draft3(self): + type = {"type": [{"minimum": 3}]} + message = self.message_for( + instance=1, + schema={"type": [type]}, + cls=validators.Draft3Validator, + ) + self.assertEqual( + message, + "1 is not of type {'type': [{'minimum': 3}]}", + ) + + def test_dependencies_list_draft3(self): + depend, on = "bar", "foo" + schema = {"dependencies": {depend: [on]}} + message = self.message_for( + instance={"bar": 2}, + schema=schema, + cls=validators.Draft3Validator, + ) + self.assertEqual(message, "'foo' is a dependency of 'bar'") + + def test_dependencies_list_draft7(self): + depend, on = "bar", "foo" + schema = {"dependencies": {depend: [on]}} + message = self.message_for( + instance={"bar": 2}, + schema=schema, + cls=validators.Draft7Validator, + ) + self.assertEqual(message, "'foo' is a dependency of 'bar'") + + def test_additionalItems_single_failure(self): + message = self.message_for( + instance=[2], + schema={"items": [], "additionalItems": False}, + cls=validators.Draft3Validator, + ) + self.assertIn("(2 was unexpected)", message) + + def test_additionalItems_multiple_failures(self): + message = self.message_for( + instance=[1, 2, 3], + schema={"items": [], "additionalItems": False}, + cls=validators.Draft3Validator, + ) + self.assertIn("(1, 2, 3 were unexpected)", message) + + def test_additionalProperties_single_failure(self): + additional = "foo" + schema = {"additionalProperties": False} + message = self.message_for(instance={additional: 2}, schema=schema) + self.assertIn("('foo' was unexpected)", message) + + def test_additionalProperties_multiple_failures(self): + schema = {"additionalProperties": False} + message = self.message_for( + instance=dict.fromkeys(["foo", "bar"]), + schema=schema, + ) + + self.assertIn(repr("foo"), message) + self.assertIn(repr("bar"), message) + self.assertIn("were unexpected)", message) + + def test_const(self): + schema = {"const": 12} + message = self.message_for( + instance={"foo": "bar"}, + schema=schema, + ) + self.assertIn("12 was expected", message) + + def test_contains_draft_6(self): + schema = {"contains": {"const": 12}} + message = self.message_for( + instance=[2, {}, []], + schema=schema, + cls=validators.Draft6Validator, + ) + self.assertEqual( + message, + "None of [2, {}, []] are valid under the given schema", + ) + + def test_invalid_format_default_message(self): + checker = FormatChecker(formats=()) + checker.checks("thing")(lambda value: False) + + schema = {"format": "thing"} + message = self.message_for( + instance="bla", + schema=schema, + format_checker=checker, + ) + + self.assertIn(repr("bla"), message) + self.assertIn(repr("thing"), message) + self.assertIn("is not a", message) + + def test_additionalProperties_false_patternProperties(self): + schema = {"type": "object", + "additionalProperties": False, + "patternProperties": { + "^abc$": {"type": "string"}, + "^def$": {"type": "string"}, + }} + message = self.message_for( + instance={"zebra": 123}, + schema=schema, + cls=validators.Draft4Validator, + ) + self.assertEqual( + message, + "{} does not match any of the regexes: {}, {}".format( + repr("zebra"), repr("^abc$"), repr("^def$"), + ), + ) + message = self.message_for( + instance={"zebra": 123, "fish": 456}, + schema=schema, + cls=validators.Draft4Validator, + ) + self.assertEqual( + message, + "{}, {} do not match any of the regexes: {}, {}".format( + repr("fish"), repr("zebra"), repr("^abc$"), repr("^def$"), + ), + ) + + def test_False_schema(self): + message = self.message_for( + instance="something", + schema=False, + ) + self.assertEqual(message, "False schema does not allow 'something'") + + def test_multipleOf(self): + message = self.message_for( + instance=3, + schema={"multipleOf": 2}, + ) + self.assertEqual(message, "3 is not a multiple of 2") + + def test_minItems(self): + message = self.message_for(instance=[], schema={"minItems": 2}) + self.assertEqual(message, "[] is too short") + + def test_maxItems(self): + message = self.message_for(instance=[1, 2, 3], schema={"maxItems": 2}) + self.assertEqual(message, "[1, 2, 3] is too long") + + def test_minItems_1(self): + message = self.message_for(instance=[], schema={"minItems": 1}) + self.assertEqual(message, "[] should be non-empty") + + def test_maxItems_0(self): + message = self.message_for(instance=[1, 2, 3], schema={"maxItems": 0}) + self.assertEqual(message, "[1, 2, 3] is expected to be empty") + + def test_minLength(self): + message = self.message_for( + instance="", + schema={"minLength": 2}, + ) + self.assertEqual(message, "'' is too short") + + def test_maxLength(self): + message = self.message_for( + instance="abc", + schema={"maxLength": 2}, + ) + self.assertEqual(message, "'abc' is too long") + + def test_minLength_1(self): + message = self.message_for(instance="", schema={"minLength": 1}) + self.assertEqual(message, "'' should be non-empty") + + def test_maxLength_0(self): + message = self.message_for(instance="abc", schema={"maxLength": 0}) + self.assertEqual(message, "'abc' is expected to be empty") + + def test_minProperties(self): + message = self.message_for(instance={}, schema={"minProperties": 2}) + self.assertEqual(message, "{} does not have enough properties") + + def test_maxProperties(self): + message = self.message_for( + instance={"a": {}, "b": {}, "c": {}}, + schema={"maxProperties": 2}, + ) + self.assertEqual( + message, + "{'a': {}, 'b': {}, 'c': {}} has too many properties", + ) + + def test_minProperties_1(self): + message = self.message_for(instance={}, schema={"minProperties": 1}) + self.assertEqual(message, "{} should be non-empty") + + def test_maxProperties_0(self): + message = self.message_for( + instance={1: 2}, + schema={"maxProperties": 0}, + ) + self.assertEqual(message, "{1: 2} is expected to be empty") + + def test_prefixItems_with_items(self): + message = self.message_for( + instance=[1, 2, "foo"], + schema={"items": False, "prefixItems": [{}, {}]}, + ) + self.assertEqual( + message, + "Expected at most 2 items but found 1 extra: 'foo'", + ) + + def test_prefixItems_with_multiple_extra_items(self): + message = self.message_for( + instance=[1, 2, "foo", 5], + schema={"items": False, "prefixItems": [{}, {}]}, + ) + self.assertEqual( + message, + "Expected at most 2 items but found 2 extra: ['foo', 5]", + ) + + def test_pattern(self): + message = self.message_for( + instance="bbb", + schema={"pattern": "^a*$"}, + ) + self.assertEqual(message, "'bbb' does not match '^a*$'") + + def test_does_not_contain(self): + message = self.message_for( + instance=[], + schema={"contains": {"type": "string"}}, + ) + self.assertEqual( + message, + "[] does not contain items matching the given schema", + ) + + def test_contains_too_few(self): + message = self.message_for( + instance=["foo", 1], + schema={"contains": {"type": "string"}, "minContains": 2}, + ) + self.assertEqual( + message, + "Too few items match the given schema " + "(expected at least 2 but only 1 matched)", + ) + + def test_contains_too_few_both_constrained(self): + message = self.message_for( + instance=["foo", 1], + schema={ + "contains": {"type": "string"}, + "minContains": 2, + "maxContains": 4, + }, + ) + self.assertEqual( + message, + "Too few items match the given schema (expected at least 2 but " + "only 1 matched)", + ) + + def test_contains_too_many(self): + message = self.message_for( + instance=["foo", "bar", "baz"], + schema={"contains": {"type": "string"}, "maxContains": 2}, + ) + self.assertEqual( + message, + "Too many items match the given schema (expected at most 2)", + ) + + def test_contains_too_many_both_constrained(self): + message = self.message_for( + instance=["foo"] * 5, + schema={ + "contains": {"type": "string"}, + "minContains": 2, + "maxContains": 4, + }, + ) + self.assertEqual( + message, + "Too many items match the given schema (expected at most 4)", + ) + + def test_exclusiveMinimum(self): + message = self.message_for( + instance=3, + schema={"exclusiveMinimum": 5}, + ) + self.assertEqual( + message, + "3 is less than or equal to the minimum of 5", + ) + + def test_exclusiveMaximum(self): + message = self.message_for(instance=3, schema={"exclusiveMaximum": 2}) + self.assertEqual( + message, + "3 is greater than or equal to the maximum of 2", + ) + + def test_required(self): + message = self.message_for(instance={}, schema={"required": ["foo"]}) + self.assertEqual(message, "'foo' is a required property") + + def test_dependentRequired(self): + message = self.message_for( + instance={"foo": {}}, + schema={"dependentRequired": {"foo": ["bar"]}}, + ) + self.assertEqual(message, "'bar' is a dependency of 'foo'") + + def test_oneOf_matches_none(self): + message = self.message_for(instance={}, schema={"oneOf": [False]}) + self.assertEqual( + message, + "{} is not valid under any of the given schemas", + ) + + def test_oneOf_matches_too_many(self): + message = self.message_for(instance={}, schema={"oneOf": [True, True]}) + self.assertEqual(message, "{} is valid under each of True, True") + + def test_unevaluated_items(self): + schema = {"type": "array", "unevaluatedItems": False} + message = self.message_for(instance=["foo", "bar"], schema=schema) + self.assertIn( + message, + "Unevaluated items are not allowed ('foo', 'bar' were unexpected)", + ) + + def test_unevaluated_items_on_invalid_type(self): + schema = {"type": "array", "unevaluatedItems": False} + message = self.message_for(instance="foo", schema=schema) + self.assertEqual(message, "'foo' is not of type 'array'") + + def test_unevaluated_properties_invalid_against_subschema(self): + schema = { + "properties": {"foo": {"type": "string"}}, + "unevaluatedProperties": {"const": 12}, + } + message = self.message_for( + instance={ + "foo": "foo", + "bar": "bar", + "baz": 12, + }, + schema=schema, + ) + self.assertEqual( + message, + "Unevaluated properties are not valid under the given schema " + "('bar' was unevaluated and invalid)", + ) + + def test_unevaluated_properties_disallowed(self): + schema = {"type": "object", "unevaluatedProperties": False} + message = self.message_for( + instance={ + "foo": "foo", + "bar": "bar", + }, + schema=schema, + ) + self.assertEqual( + message, + "Unevaluated properties are not allowed " + "('bar', 'foo' were unexpected)", + ) + + def test_unevaluated_properties_on_invalid_type(self): + schema = {"type": "object", "unevaluatedProperties": False} + message = self.message_for(instance="foo", schema=schema) + self.assertEqual(message, "'foo' is not of type 'object'") + + def test_single_item(self): + schema = {"prefixItems": [{}], "items": False} + message = self.message_for( + instance=["foo", "bar", "baz"], + schema=schema, + ) + self.assertEqual( + message, + "Expected at most 1 item but found 2 extra: ['bar', 'baz']", + ) + + def test_heterogeneous_additionalItems_with_Items(self): + schema = {"items": [{}], "additionalItems": False} + message = self.message_for( + instance=["foo", "bar", 37], + schema=schema, + cls=validators.Draft7Validator, + ) + self.assertEqual( + message, + "Additional items are not allowed ('bar', 37 were unexpected)", + ) + + def test_heterogeneous_items_prefixItems(self): + schema = {"prefixItems": [{}], "items": False} + message = self.message_for( + instance=["foo", "bar", 37], + schema=schema, + ) + self.assertEqual( + message, + "Expected at most 1 item but found 2 extra: ['bar', 37]", + ) + + def test_heterogeneous_unevaluatedItems_prefixItems(self): + schema = {"prefixItems": [{}], "unevaluatedItems": False} + message = self.message_for( + instance=["foo", "bar", 37], + schema=schema, + ) + self.assertEqual( + message, + "Unevaluated items are not allowed ('bar', 37 were unexpected)", + ) + + def test_heterogeneous_properties_additionalProperties(self): + """ + Not valid deserialized JSON, but this should not blow up. + """ + schema = {"properties": {"foo": {}}, "additionalProperties": False} + message = self.message_for( + instance={"foo": {}, "a": "baz", 37: 12}, + schema=schema, + ) + self.assertEqual( + message, + "Additional properties are not allowed (37, 'a' were unexpected)", + ) + + def test_heterogeneous_properties_unevaluatedProperties(self): + """ + Not valid deserialized JSON, but this should not blow up. + """ + schema = {"properties": {"foo": {}}, "unevaluatedProperties": False} + message = self.message_for( + instance={"foo": {}, "a": "baz", 37: 12}, + schema=schema, + ) + self.assertEqual( + message, + "Unevaluated properties are not allowed (37, 'a' were unexpected)", + ) + + +class TestValidationErrorDetails(TestCase): + # TODO: These really need unit tests for each individual keyword, rather + # than just these higher level tests. + def test_anyOf(self): + instance = 5 + schema = { + "anyOf": [ + {"minimum": 20}, + {"type": "string"}, + ], + } + + validator = validators.Draft4Validator(schema) + errors = list(validator.iter_errors(instance)) + self.assertEqual(len(errors), 1) + e = errors[0] + + self.assertEqual(e.validator, "anyOf") + self.assertEqual(e.validator_value, schema["anyOf"]) + self.assertEqual(e.instance, instance) + self.assertEqual(e.schema, schema) + self.assertIsNone(e.parent) + + self.assertEqual(e.path, deque([])) + self.assertEqual(e.relative_path, deque([])) + self.assertEqual(e.absolute_path, deque([])) + self.assertEqual(e.json_path, "$") + + self.assertEqual(e.schema_path, deque(["anyOf"])) + self.assertEqual(e.relative_schema_path, deque(["anyOf"])) + self.assertEqual(e.absolute_schema_path, deque(["anyOf"])) + + self.assertEqual(len(e.context), 2) + + e1, e2 = sorted_errors(e.context) + + self.assertEqual(e1.validator, "minimum") + self.assertEqual(e1.validator_value, schema["anyOf"][0]["minimum"]) + self.assertEqual(e1.instance, instance) + self.assertEqual(e1.schema, schema["anyOf"][0]) + self.assertIs(e1.parent, e) + + self.assertEqual(e1.path, deque([])) + self.assertEqual(e1.absolute_path, deque([])) + self.assertEqual(e1.relative_path, deque([])) + self.assertEqual(e1.json_path, "$") + + self.assertEqual(e1.schema_path, deque([0, "minimum"])) + self.assertEqual(e1.relative_schema_path, deque([0, "minimum"])) + self.assertEqual( + e1.absolute_schema_path, deque(["anyOf", 0, "minimum"]), + ) + + self.assertFalse(e1.context) + + self.assertEqual(e2.validator, "type") + self.assertEqual(e2.validator_value, schema["anyOf"][1]["type"]) + self.assertEqual(e2.instance, instance) + self.assertEqual(e2.schema, schema["anyOf"][1]) + self.assertIs(e2.parent, e) + + self.assertEqual(e2.path, deque([])) + self.assertEqual(e2.relative_path, deque([])) + self.assertEqual(e2.absolute_path, deque([])) + self.assertEqual(e2.json_path, "$") + + self.assertEqual(e2.schema_path, deque([1, "type"])) + self.assertEqual(e2.relative_schema_path, deque([1, "type"])) + self.assertEqual(e2.absolute_schema_path, deque(["anyOf", 1, "type"])) + + self.assertEqual(len(e2.context), 0) + + def test_type(self): + instance = {"foo": 1} + schema = { + "type": [ + {"type": "integer"}, + { + "type": "object", + "properties": {"foo": {"enum": [2]}}, + }, + ], + } + + validator = validators.Draft3Validator(schema) + errors = list(validator.iter_errors(instance)) + self.assertEqual(len(errors), 1) + e = errors[0] + + self.assertEqual(e.validator, "type") + self.assertEqual(e.validator_value, schema["type"]) + self.assertEqual(e.instance, instance) + self.assertEqual(e.schema, schema) + self.assertIsNone(e.parent) + + self.assertEqual(e.path, deque([])) + self.assertEqual(e.relative_path, deque([])) + self.assertEqual(e.absolute_path, deque([])) + self.assertEqual(e.json_path, "$") + + self.assertEqual(e.schema_path, deque(["type"])) + self.assertEqual(e.relative_schema_path, deque(["type"])) + self.assertEqual(e.absolute_schema_path, deque(["type"])) + + self.assertEqual(len(e.context), 2) + + e1, e2 = sorted_errors(e.context) + + self.assertEqual(e1.validator, "type") + self.assertEqual(e1.validator_value, schema["type"][0]["type"]) + self.assertEqual(e1.instance, instance) + self.assertEqual(e1.schema, schema["type"][0]) + self.assertIs(e1.parent, e) + + self.assertEqual(e1.path, deque([])) + self.assertEqual(e1.relative_path, deque([])) + self.assertEqual(e1.absolute_path, deque([])) + self.assertEqual(e1.json_path, "$") + + self.assertEqual(e1.schema_path, deque([0, "type"])) + self.assertEqual(e1.relative_schema_path, deque([0, "type"])) + self.assertEqual(e1.absolute_schema_path, deque(["type", 0, "type"])) + + self.assertFalse(e1.context) + + self.assertEqual(e2.validator, "enum") + self.assertEqual(e2.validator_value, [2]) + self.assertEqual(e2.instance, 1) + self.assertEqual(e2.schema, {"enum": [2]}) + self.assertIs(e2.parent, e) + + self.assertEqual(e2.path, deque(["foo"])) + self.assertEqual(e2.relative_path, deque(["foo"])) + self.assertEqual(e2.absolute_path, deque(["foo"])) + self.assertEqual(e2.json_path, "$.foo") + + self.assertEqual( + e2.schema_path, deque([1, "properties", "foo", "enum"]), + ) + self.assertEqual( + e2.relative_schema_path, deque([1, "properties", "foo", "enum"]), + ) + self.assertEqual( + e2.absolute_schema_path, + deque(["type", 1, "properties", "foo", "enum"]), + ) + + self.assertFalse(e2.context) + + def test_single_nesting(self): + instance = {"foo": 2, "bar": [1], "baz": 15, "quux": "spam"} + schema = { + "properties": { + "foo": {"type": "string"}, + "bar": {"minItems": 2}, + "baz": {"maximum": 10, "enum": [2, 4, 6, 8]}, + }, + } + + validator = validators.Draft3Validator(schema) + errors = validator.iter_errors(instance) + e1, e2, e3, e4 = sorted_errors(errors) + + self.assertEqual(e1.path, deque(["bar"])) + self.assertEqual(e2.path, deque(["baz"])) + self.assertEqual(e3.path, deque(["baz"])) + self.assertEqual(e4.path, deque(["foo"])) + + self.assertEqual(e1.relative_path, deque(["bar"])) + self.assertEqual(e2.relative_path, deque(["baz"])) + self.assertEqual(e3.relative_path, deque(["baz"])) + self.assertEqual(e4.relative_path, deque(["foo"])) + + self.assertEqual(e1.absolute_path, deque(["bar"])) + self.assertEqual(e2.absolute_path, deque(["baz"])) + self.assertEqual(e3.absolute_path, deque(["baz"])) + self.assertEqual(e4.absolute_path, deque(["foo"])) + + self.assertEqual(e1.json_path, "$.bar") + self.assertEqual(e2.json_path, "$.baz") + self.assertEqual(e3.json_path, "$.baz") + self.assertEqual(e4.json_path, "$.foo") + + self.assertEqual(e1.validator, "minItems") + self.assertEqual(e2.validator, "enum") + self.assertEqual(e3.validator, "maximum") + self.assertEqual(e4.validator, "type") + + def test_multiple_nesting(self): + instance = [1, {"foo": 2, "bar": {"baz": [1]}}, "quux"] + schema = { + "type": "string", + "items": { + "type": ["string", "object"], + "properties": { + "foo": {"enum": [1, 3]}, + "bar": { + "type": "array", + "properties": { + "bar": {"required": True}, + "baz": {"minItems": 2}, + }, + }, + }, + }, + } + + validator = validators.Draft3Validator(schema) + errors = validator.iter_errors(instance) + e1, e2, e3, e4, e5, e6 = sorted_errors(errors) + + self.assertEqual(e1.path, deque([])) + self.assertEqual(e2.path, deque([0])) + self.assertEqual(e3.path, deque([1, "bar"])) + self.assertEqual(e4.path, deque([1, "bar", "bar"])) + self.assertEqual(e5.path, deque([1, "bar", "baz"])) + self.assertEqual(e6.path, deque([1, "foo"])) + + self.assertEqual(e1.json_path, "$") + self.assertEqual(e2.json_path, "$[0]") + self.assertEqual(e3.json_path, "$[1].bar") + self.assertEqual(e4.json_path, "$[1].bar.bar") + self.assertEqual(e5.json_path, "$[1].bar.baz") + self.assertEqual(e6.json_path, "$[1].foo") + + self.assertEqual(e1.schema_path, deque(["type"])) + self.assertEqual(e2.schema_path, deque(["items", "type"])) + self.assertEqual( + list(e3.schema_path), ["items", "properties", "bar", "type"], + ) + self.assertEqual( + list(e4.schema_path), + ["items", "properties", "bar", "properties", "bar", "required"], + ) + self.assertEqual( + list(e5.schema_path), + ["items", "properties", "bar", "properties", "baz", "minItems"], + ) + self.assertEqual( + list(e6.schema_path), ["items", "properties", "foo", "enum"], + ) + + self.assertEqual(e1.validator, "type") + self.assertEqual(e2.validator, "type") + self.assertEqual(e3.validator, "type") + self.assertEqual(e4.validator, "required") + self.assertEqual(e5.validator, "minItems") + self.assertEqual(e6.validator, "enum") + + def test_recursive(self): + schema = { + "definitions": { + "node": { + "anyOf": [{ + "type": "object", + "required": ["name", "children"], + "properties": { + "name": { + "type": "string", + }, + "children": { + "type": "object", + "patternProperties": { + "^.*$": { + "$ref": "#/definitions/node", + }, + }, + }, + }, + }], + }, + }, + "type": "object", + "required": ["root"], + "properties": {"root": {"$ref": "#/definitions/node"}}, + } + + instance = { + "root": { + "name": "root", + "children": { + "a": { + "name": "a", + "children": { + "ab": { + "name": "ab", + # missing "children" + }, + }, + }, + }, + }, + } + validator = validators.Draft4Validator(schema) + + e, = validator.iter_errors(instance) + self.assertEqual(e.absolute_path, deque(["root"])) + self.assertEqual( + e.absolute_schema_path, deque(["properties", "root", "anyOf"]), + ) + self.assertEqual(e.json_path, "$.root") + + e1, = e.context + self.assertEqual(e1.absolute_path, deque(["root", "children", "a"])) + self.assertEqual( + e1.absolute_schema_path, deque( + [ + "properties", + "root", + "anyOf", + 0, + "properties", + "children", + "patternProperties", + "^.*$", + "anyOf", + ], + ), + ) + self.assertEqual(e1.json_path, "$.root.children.a") + + e2, = e1.context + self.assertEqual( + e2.absolute_path, deque( + ["root", "children", "a", "children", "ab"], + ), + ) + self.assertEqual( + e2.absolute_schema_path, deque( + [ + "properties", + "root", + "anyOf", + 0, + "properties", + "children", + "patternProperties", + "^.*$", + "anyOf", + 0, + "properties", + "children", + "patternProperties", + "^.*$", + "anyOf", + ], + ), + ) + self.assertEqual(e2.json_path, "$.root.children.a.children.ab") + + def test_additionalProperties(self): + instance = {"bar": "bar", "foo": 2} + schema = {"additionalProperties": {"type": "integer", "minimum": 5}} + + validator = validators.Draft3Validator(schema) + errors = validator.iter_errors(instance) + e1, e2 = sorted_errors(errors) + + self.assertEqual(e1.path, deque(["bar"])) + self.assertEqual(e2.path, deque(["foo"])) + + self.assertEqual(e1.json_path, "$.bar") + self.assertEqual(e2.json_path, "$.foo") + + self.assertEqual(e1.validator, "type") + self.assertEqual(e2.validator, "minimum") + + def test_patternProperties(self): + instance = {"bar": 1, "foo": 2} + schema = { + "patternProperties": { + "bar": {"type": "string"}, + "foo": {"minimum": 5}, + }, + } + + validator = validators.Draft3Validator(schema) + errors = validator.iter_errors(instance) + e1, e2 = sorted_errors(errors) + + self.assertEqual(e1.path, deque(["bar"])) + self.assertEqual(e2.path, deque(["foo"])) + + self.assertEqual(e1.json_path, "$.bar") + self.assertEqual(e2.json_path, "$.foo") + + self.assertEqual(e1.validator, "type") + self.assertEqual(e2.validator, "minimum") + + def test_additionalItems(self): + instance = ["foo", 1] + schema = { + "items": [], + "additionalItems": {"type": "integer", "minimum": 5}, + } + + validator = validators.Draft3Validator(schema) + errors = validator.iter_errors(instance) + e1, e2 = sorted_errors(errors) + + self.assertEqual(e1.path, deque([0])) + self.assertEqual(e2.path, deque([1])) + + self.assertEqual(e1.json_path, "$[0]") + self.assertEqual(e2.json_path, "$[1]") + + self.assertEqual(e1.validator, "type") + self.assertEqual(e2.validator, "minimum") + + def test_additionalItems_with_items(self): + instance = ["foo", "bar", 1] + schema = { + "items": [{}], + "additionalItems": {"type": "integer", "minimum": 5}, + } + + validator = validators.Draft3Validator(schema) + errors = validator.iter_errors(instance) + e1, e2 = sorted_errors(errors) + + self.assertEqual(e1.path, deque([1])) + self.assertEqual(e2.path, deque([2])) + + self.assertEqual(e1.json_path, "$[1]") + self.assertEqual(e2.json_path, "$[2]") + + self.assertEqual(e1.validator, "type") + self.assertEqual(e2.validator, "minimum") + + def test_propertyNames(self): + instance = {"foo": 12} + schema = {"propertyNames": {"not": {"const": "foo"}}} + + validator = validators.Draft7Validator(schema) + error, = validator.iter_errors(instance) + + self.assertEqual(error.validator, "not") + self.assertEqual( + error.message, + "'foo' should not be valid under {'const': 'foo'}", + ) + self.assertEqual(error.path, deque([])) + self.assertEqual(error.json_path, "$") + self.assertEqual(error.schema_path, deque(["propertyNames", "not"])) + + def test_if_then(self): + schema = { + "if": {"const": 12}, + "then": {"const": 13}, + } + + validator = validators.Draft7Validator(schema) + error, = validator.iter_errors(12) + + self.assertEqual(error.validator, "const") + self.assertEqual(error.message, "13 was expected") + self.assertEqual(error.path, deque([])) + self.assertEqual(error.json_path, "$") + self.assertEqual(error.schema_path, deque(["then", "const"])) + + def test_if_else(self): + schema = { + "if": {"const": 12}, + "else": {"const": 13}, + } + + validator = validators.Draft7Validator(schema) + error, = validator.iter_errors(15) + + self.assertEqual(error.validator, "const") + self.assertEqual(error.message, "13 was expected") + self.assertEqual(error.path, deque([])) + self.assertEqual(error.json_path, "$") + self.assertEqual(error.schema_path, deque(["else", "const"])) + + def test_boolean_schema_False(self): + validator = validators.Draft7Validator(False) + error, = validator.iter_errors(12) + + self.assertEqual( + ( + error.message, + error.validator, + error.validator_value, + error.instance, + error.schema, + error.schema_path, + error.json_path, + ), + ( + "False schema does not allow 12", + None, + None, + 12, + False, + deque([]), + "$", + ), + ) + + def test_ref(self): + ref, schema = "someRef", {"additionalProperties": {"type": "integer"}} + validator = validators.Draft7Validator( + {"$ref": ref}, + resolver=validators._RefResolver("", {}, store={ref: schema}), + ) + error, = validator.iter_errors({"foo": "notAnInteger"}) + + self.assertEqual( + ( + error.message, + error.validator, + error.validator_value, + error.instance, + error.absolute_path, + error.schema, + error.schema_path, + error.json_path, + ), + ( + "'notAnInteger' is not of type 'integer'", + "type", + "integer", + "notAnInteger", + deque(["foo"]), + {"type": "integer"}, + deque(["additionalProperties", "type"]), + "$.foo", + ), + ) + + def test_prefixItems(self): + schema = {"prefixItems": [{"type": "string"}, {}, {}, {"maximum": 3}]} + validator = validators.Draft202012Validator(schema) + type_error, min_error = validator.iter_errors([1, 2, "foo", 5]) + self.assertEqual( + ( + type_error.message, + type_error.validator, + type_error.validator_value, + type_error.instance, + type_error.absolute_path, + type_error.schema, + type_error.schema_path, + type_error.json_path, + ), + ( + "1 is not of type 'string'", + "type", + "string", + 1, + deque([0]), + {"type": "string"}, + deque(["prefixItems", 0, "type"]), + "$[0]", + ), + ) + self.assertEqual( + ( + min_error.message, + min_error.validator, + min_error.validator_value, + min_error.instance, + min_error.absolute_path, + min_error.schema, + min_error.schema_path, + min_error.json_path, + ), + ( + "5 is greater than the maximum of 3", + "maximum", + 3, + 5, + deque([3]), + {"maximum": 3}, + deque(["prefixItems", 3, "maximum"]), + "$[3]", + ), + ) + + def test_prefixItems_with_items(self): + schema = { + "items": {"type": "string"}, + "prefixItems": [{}], + } + validator = validators.Draft202012Validator(schema) + e1, e2 = validator.iter_errors(["foo", 2, "bar", 4, "baz"]) + self.assertEqual( + ( + e1.message, + e1.validator, + e1.validator_value, + e1.instance, + e1.absolute_path, + e1.schema, + e1.schema_path, + e1.json_path, + ), + ( + "2 is not of type 'string'", + "type", + "string", + 2, + deque([1]), + {"type": "string"}, + deque(["items", "type"]), + "$[1]", + ), + ) + self.assertEqual( + ( + e2.message, + e2.validator, + e2.validator_value, + e2.instance, + e2.absolute_path, + e2.schema, + e2.schema_path, + e2.json_path, + ), + ( + "4 is not of type 'string'", + "type", + "string", + 4, + deque([3]), + {"type": "string"}, + deque(["items", "type"]), + "$[3]", + ), + ) + + def test_contains_too_many(self): + """ + `contains` + `maxContains` produces only one error, even if there are + many more incorrectly matching elements. + """ + schema = {"contains": {"type": "string"}, "maxContains": 2} + validator = validators.Draft202012Validator(schema) + error, = validator.iter_errors(["foo", 2, "bar", 4, "baz", "quux"]) + self.assertEqual( + ( + error.message, + error.validator, + error.validator_value, + error.instance, + error.absolute_path, + error.schema, + error.schema_path, + error.json_path, + ), + ( + "Too many items match the given schema (expected at most 2)", + "maxContains", + 2, + ["foo", 2, "bar", 4, "baz", "quux"], + deque([]), + {"contains": {"type": "string"}, "maxContains": 2}, + deque(["contains"]), + "$", + ), + ) + + def test_contains_too_few(self): + schema = {"contains": {"type": "string"}, "minContains": 2} + validator = validators.Draft202012Validator(schema) + error, = validator.iter_errors(["foo", 2, 4]) + self.assertEqual( + ( + error.message, + error.validator, + error.validator_value, + error.instance, + error.absolute_path, + error.schema, + error.schema_path, + error.json_path, + ), + ( + ( + "Too few items match the given schema " + "(expected at least 2 but only 1 matched)" + ), + "minContains", + 2, + ["foo", 2, 4], + deque([]), + {"contains": {"type": "string"}, "minContains": 2}, + deque(["contains"]), + "$", + ), + ) + + def test_contains_none(self): + schema = {"contains": {"type": "string"}, "minContains": 2} + validator = validators.Draft202012Validator(schema) + error, = validator.iter_errors([2, 4]) + self.assertEqual( + ( + error.message, + error.validator, + error.validator_value, + error.instance, + error.absolute_path, + error.schema, + error.schema_path, + error.json_path, + ), + ( + "[2, 4] does not contain items matching the given schema", + "contains", + {"type": "string"}, + [2, 4], + deque([]), + {"contains": {"type": "string"}, "minContains": 2}, + deque(["contains"]), + "$", + ), + ) + + def test_ref_sibling(self): + schema = { + "$defs": {"foo": {"required": ["bar"]}}, + "properties": { + "aprop": { + "$ref": "#/$defs/foo", + "required": ["baz"], + }, + }, + } + + validator = validators.Draft202012Validator(schema) + e1, e2 = validator.iter_errors({"aprop": {}}) + self.assertEqual( + ( + e1.message, + e1.validator, + e1.validator_value, + e1.instance, + e1.absolute_path, + e1.schema, + e1.schema_path, + e1.relative_schema_path, + e1.json_path, + ), + ( + "'bar' is a required property", + "required", + ["bar"], + {}, + deque(["aprop"]), + {"required": ["bar"]}, + deque(["properties", "aprop", "required"]), + deque(["properties", "aprop", "required"]), + "$.aprop", + ), + ) + self.assertEqual( + ( + e2.message, + e2.validator, + e2.validator_value, + e2.instance, + e2.absolute_path, + e2.schema, + e2.schema_path, + e2.relative_schema_path, + e2.json_path, + ), + ( + "'baz' is a required property", + "required", + ["baz"], + {}, + deque(["aprop"]), + {"$ref": "#/$defs/foo", "required": ["baz"]}, + deque(["properties", "aprop", "required"]), + deque(["properties", "aprop", "required"]), + "$.aprop", + ), + ) + + +class MetaSchemaTestsMixin: + # TODO: These all belong upstream + def test_invalid_properties(self): + with self.assertRaises(exceptions.SchemaError): + self.Validator.check_schema({"properties": 12}) + + def test_minItems_invalid_string(self): + with self.assertRaises(exceptions.SchemaError): + # needs to be an integer + self.Validator.check_schema({"minItems": "1"}) + + def test_enum_allows_empty_arrays(self): + """ + Technically, all the spec says is they SHOULD have elements, not MUST. + + (As of Draft 6. Previous drafts do say MUST). + + See #529. + """ + if self.Validator in { + validators.Draft3Validator, + validators.Draft4Validator, + }: + with self.assertRaises(exceptions.SchemaError): + self.Validator.check_schema({"enum": []}) + else: + self.Validator.check_schema({"enum": []}) + + def test_enum_allows_non_unique_items(self): + """ + Technically, all the spec says is they SHOULD be unique, not MUST. + + (As of Draft 6. Previous drafts do say MUST). + + See #529. + """ + if self.Validator in { + validators.Draft3Validator, + validators.Draft4Validator, + }: + with self.assertRaises(exceptions.SchemaError): + self.Validator.check_schema({"enum": [12, 12]}) + else: + self.Validator.check_schema({"enum": [12, 12]}) + + def test_schema_with_invalid_regex(self): + with self.assertRaises(exceptions.SchemaError): + self.Validator.check_schema({"pattern": "*notaregex"}) + + def test_schema_with_invalid_regex_with_disabled_format_validation(self): + self.Validator.check_schema( + {"pattern": "*notaregex"}, + format_checker=None, + ) + + +class ValidatorTestMixin(MetaSchemaTestsMixin): + def test_it_implements_the_validator_protocol(self): + self.assertIsInstance(self.Validator({}), protocols.Validator) + + def test_valid_instances_are_valid(self): + schema, instance = self.valid + self.assertTrue(self.Validator(schema).is_valid(instance)) + + def test_invalid_instances_are_not_valid(self): + schema, instance = self.invalid + self.assertFalse(self.Validator(schema).is_valid(instance)) + + def test_non_existent_properties_are_ignored(self): + self.Validator({object(): object()}).validate(instance=object()) + + def test_evolve(self): + schema, format_checker = {"type": "integer"}, FormatChecker() + original = self.Validator( + schema, + format_checker=format_checker, + ) + new = original.evolve( + schema={"type": "string"}, + format_checker=self.Validator.FORMAT_CHECKER, + ) + + expected = self.Validator( + {"type": "string"}, + format_checker=self.Validator.FORMAT_CHECKER, + _resolver=new._resolver, + ) + + self.assertEqual(new, expected) + self.assertNotEqual(new, original) + + def test_evolve_with_subclass(self): + """ + Subclassing validators isn't supported public API, but some users have + done it, because we don't actually error entirely when it's done :/ + + We need to deprecate doing so first to help as many of these users + ensure they can move to supported APIs, but this test ensures that in + the interim, we haven't broken those users. + """ + + with self.assertWarns(DeprecationWarning): + @define + class OhNo(self.Validator): + foo = field(factory=lambda: [1, 2, 3]) + _bar = field(default=37) + + validator = OhNo({}, bar=12) + self.assertEqual(validator.foo, [1, 2, 3]) + + new = validator.evolve(schema={"type": "integer"}) + self.assertEqual(new.foo, [1, 2, 3]) + self.assertEqual(new._bar, 12) + + def test_is_type_is_true_for_valid_type(self): + self.assertTrue(self.Validator({}).is_type("foo", "string")) + + def test_is_type_is_false_for_invalid_type(self): + self.assertFalse(self.Validator({}).is_type("foo", "array")) + + def test_is_type_evades_bool_inheriting_from_int(self): + self.assertFalse(self.Validator({}).is_type(True, "integer")) + self.assertFalse(self.Validator({}).is_type(True, "number")) + + def test_it_can_validate_with_decimals(self): + schema = {"items": {"type": "number"}} + Validator = validators.extend( + self.Validator, + type_checker=self.Validator.TYPE_CHECKER.redefine( + "number", + lambda checker, thing: isinstance( + thing, (int, float, Decimal), + ) and not isinstance(thing, bool), + ), + ) + + validator = Validator(schema) + validator.validate([1, 1.1, Decimal(1) / Decimal(8)]) + + invalid = ["foo", {}, [], True, None] + self.assertEqual( + [error.instance for error in validator.iter_errors(invalid)], + invalid, + ) + + def test_it_returns_true_for_formats_it_does_not_know_about(self): + validator = self.Validator( + {"format": "carrot"}, format_checker=FormatChecker(), + ) + validator.validate("bugs") + + def test_it_does_not_validate_formats_by_default(self): + validator = self.Validator({}) + self.assertIsNone(validator.format_checker) + + def test_it_validates_formats_if_a_checker_is_provided(self): + checker = FormatChecker() + bad = ValueError("Bad!") + + @checker.checks("foo", raises=ValueError) + def check(value): + if value == "good": + return True + elif value == "bad": + raise bad + else: # pragma: no cover + self.fail(f"What is {value}? [Baby Don't Hurt Me]") + + validator = self.Validator( + {"format": "foo"}, format_checker=checker, + ) + + validator.validate("good") + with self.assertRaises(exceptions.ValidationError) as cm: + validator.validate("bad") + + # Make sure original cause is attached + self.assertIs(cm.exception.cause, bad) + + def test_non_string_custom_type(self): + non_string_type = object() + schema = {"type": [non_string_type]} + Crazy = validators.extend( + self.Validator, + type_checker=self.Validator.TYPE_CHECKER.redefine( + non_string_type, + lambda checker, thing: isinstance(thing, int), + ), + ) + Crazy(schema).validate(15) + + def test_it_properly_formats_tuples_in_errors(self): + """ + A tuple instance properly formats validation errors for uniqueItems. + + See #224 + """ + TupleValidator = validators.extend( + self.Validator, + type_checker=self.Validator.TYPE_CHECKER.redefine( + "array", + lambda checker, thing: isinstance(thing, tuple), + ), + ) + with self.assertRaises(exceptions.ValidationError) as e: + TupleValidator({"uniqueItems": True}).validate((1, 1)) + self.assertIn("(1, 1) has non-unique elements", str(e.exception)) + + def test_check_redefined_sequence(self): + """ + Allow array to validate against another defined sequence type + """ + schema = {"type": "array", "uniqueItems": True} + MyMapping = namedtuple("MyMapping", "a, b") + Validator = validators.extend( + self.Validator, + type_checker=self.Validator.TYPE_CHECKER.redefine_many( + { + "array": lambda checker, thing: isinstance( + thing, (list, deque), + ), + "object": lambda checker, thing: isinstance( + thing, (dict, MyMapping), + ), + }, + ), + ) + validator = Validator(schema) + + valid_instances = [ + deque(["a", None, "1", "", True]), + deque([[False], [0]]), + [deque([False]), deque([0])], + [[deque([False])], [deque([0])]], + [[[[[deque([False])]]]], [[[[deque([0])]]]]], + [deque([deque([False])]), deque([deque([0])])], + [MyMapping("a", 0), MyMapping("a", False)], + [ + MyMapping("a", [deque([0])]), + MyMapping("a", [deque([False])]), + ], + [ + MyMapping("a", [MyMapping("a", deque([0]))]), + MyMapping("a", [MyMapping("a", deque([False]))]), + ], + [deque(deque(deque([False]))), deque(deque(deque([0])))], + ] + + for instance in valid_instances: + validator.validate(instance) + + invalid_instances = [ + deque(["a", "b", "a"]), + deque([[False], [False]]), + [deque([False]), deque([False])], + [[deque([False])], [deque([False])]], + [[[[[deque([False])]]]], [[[[deque([False])]]]]], + [deque([deque([False])]), deque([deque([False])])], + [MyMapping("a", False), MyMapping("a", False)], + [ + MyMapping("a", [deque([False])]), + MyMapping("a", [deque([False])]), + ], + [ + MyMapping("a", [MyMapping("a", deque([False]))]), + MyMapping("a", [MyMapping("a", deque([False]))]), + ], + [deque(deque(deque([False]))), deque(deque(deque([False])))], + ] + + for instance in invalid_instances: + with self.assertRaises(exceptions.ValidationError): + validator.validate(instance) + + def test_it_creates_a_ref_resolver_if_not_provided(self): + with self.assertWarns(DeprecationWarning): + resolver = self.Validator({}).resolver + self.assertIsInstance(resolver, validators._RefResolver) + + def test_it_upconverts_from_deprecated_RefResolvers(self): + ref, schema = "someCoolRef", {"type": "integer"} + resolver = validators._RefResolver("", {}, store={ref: schema}) + validator = self.Validator({"$ref": ref}, resolver=resolver) + + with self.assertRaises(exceptions.ValidationError): + validator.validate(None) + + def test_it_upconverts_from_yet_older_deprecated_legacy_RefResolvers(self): + """ + Legacy RefResolvers support only the context manager form of + resolution. + """ + + class LegacyRefResolver: + @contextmanager + def resolving(this, ref): + self.assertEqual(ref, "the ref") + yield {"type": "integer"} + + resolver = LegacyRefResolver() + schema = {"$ref": "the ref"} + + with self.assertRaises(exceptions.ValidationError): + self.Validator(schema, resolver=resolver).validate(None) + + +class AntiDraft6LeakMixin: + """ + Make sure functionality from draft 6 doesn't leak backwards in time. + """ + + def test_True_is_not_a_schema(self): + with self.assertRaises(exceptions.SchemaError) as e: + self.Validator.check_schema(True) + self.assertIn("True is not of type", str(e.exception)) + + def test_False_is_not_a_schema(self): + with self.assertRaises(exceptions.SchemaError) as e: + self.Validator.check_schema(False) + self.assertIn("False is not of type", str(e.exception)) + + def test_True_is_not_a_schema_even_if_you_forget_to_check(self): + with self.assertRaises(Exception) as e: + self.Validator(True).validate(12) + self.assertNotIsInstance(e.exception, exceptions.ValidationError) + + def test_False_is_not_a_schema_even_if_you_forget_to_check(self): + with self.assertRaises(Exception) as e: + self.Validator(False).validate(12) + self.assertNotIsInstance(e.exception, exceptions.ValidationError) + + +class TestDraft3Validator(AntiDraft6LeakMixin, ValidatorTestMixin, TestCase): + Validator = validators.Draft3Validator + valid: tuple[dict, dict] = ({}, {}) + invalid = {"type": "integer"}, "foo" + + def test_any_type_is_valid_for_type_any(self): + validator = self.Validator({"type": "any"}) + validator.validate(object()) + + def test_any_type_is_redefinable(self): + """ + Sigh, because why not. + """ + Crazy = validators.extend( + self.Validator, + type_checker=self.Validator.TYPE_CHECKER.redefine( + "any", lambda checker, thing: isinstance(thing, int), + ), + ) + validator = Crazy({"type": "any"}) + validator.validate(12) + with self.assertRaises(exceptions.ValidationError): + validator.validate("foo") + + def test_is_type_is_true_for_any_type(self): + self.assertTrue(self.Validator({"type": "any"}).is_valid(object())) + + def test_is_type_does_not_evade_bool_if_it_is_being_tested(self): + self.assertTrue(self.Validator({}).is_type(True, "boolean")) + self.assertTrue(self.Validator({"type": "any"}).is_valid(True)) + + +class TestDraft4Validator(AntiDraft6LeakMixin, ValidatorTestMixin, TestCase): + Validator = validators.Draft4Validator + valid: tuple[dict, dict] = ({}, {}) + invalid = {"type": "integer"}, "foo" + + +class TestDraft6Validator(ValidatorTestMixin, TestCase): + Validator = validators.Draft6Validator + valid: tuple[dict, dict] = ({}, {}) + invalid = {"type": "integer"}, "foo" + + +class TestDraft7Validator(ValidatorTestMixin, TestCase): + Validator = validators.Draft7Validator + valid: tuple[dict, dict] = ({}, {}) + invalid = {"type": "integer"}, "foo" + + +class TestDraft201909Validator(ValidatorTestMixin, TestCase): + Validator = validators.Draft201909Validator + valid: tuple[dict, dict] = ({}, {}) + invalid = {"type": "integer"}, "foo" + + +class TestDraft202012Validator(ValidatorTestMixin, TestCase): + Validator = validators.Draft202012Validator + valid: tuple[dict, dict] = ({}, {}) + invalid = {"type": "integer"}, "foo" + + +class TestLatestValidator(TestCase): + """ + These really apply to multiple versions but are easiest to test on one. + """ + + def test_ref_resolvers_may_have_boolean_schemas_stored(self): + ref = "someCoolRef" + schema = {"$ref": ref} + resolver = validators._RefResolver("", {}, store={ref: False}) + validator = validators._LATEST_VERSION(schema, resolver=resolver) + + with self.assertRaises(exceptions.ValidationError): + validator.validate(None) + + +class TestValidatorFor(TestCase): + def test_draft_3(self): + schema = {"$schema": "http://json-schema.org/draft-03/schema"} + self.assertIs( + validators.validator_for(schema), + validators.Draft3Validator, + ) + + schema = {"$schema": "http://json-schema.org/draft-03/schema#"} + self.assertIs( + validators.validator_for(schema), + validators.Draft3Validator, + ) + + def test_draft_4(self): + schema = {"$schema": "http://json-schema.org/draft-04/schema"} + self.assertIs( + validators.validator_for(schema), + validators.Draft4Validator, + ) + + schema = {"$schema": "http://json-schema.org/draft-04/schema#"} + self.assertIs( + validators.validator_for(schema), + validators.Draft4Validator, + ) + + def test_draft_6(self): + schema = {"$schema": "http://json-schema.org/draft-06/schema"} + self.assertIs( + validators.validator_for(schema), + validators.Draft6Validator, + ) + + schema = {"$schema": "http://json-schema.org/draft-06/schema#"} + self.assertIs( + validators.validator_for(schema), + validators.Draft6Validator, + ) + + def test_draft_7(self): + schema = {"$schema": "http://json-schema.org/draft-07/schema"} + self.assertIs( + validators.validator_for(schema), + validators.Draft7Validator, + ) + + schema = {"$schema": "http://json-schema.org/draft-07/schema#"} + self.assertIs( + validators.validator_for(schema), + validators.Draft7Validator, + ) + + def test_draft_201909(self): + schema = {"$schema": "https://json-schema.org/draft/2019-09/schema"} + self.assertIs( + validators.validator_for(schema), + validators.Draft201909Validator, + ) + + schema = {"$schema": "https://json-schema.org/draft/2019-09/schema#"} + self.assertIs( + validators.validator_for(schema), + validators.Draft201909Validator, + ) + + def test_draft_202012(self): + schema = {"$schema": "https://json-schema.org/draft/2020-12/schema"} + self.assertIs( + validators.validator_for(schema), + validators.Draft202012Validator, + ) + + schema = {"$schema": "https://json-schema.org/draft/2020-12/schema#"} + self.assertIs( + validators.validator_for(schema), + validators.Draft202012Validator, + ) + + def test_True(self): + self.assertIs( + validators.validator_for(True), + validators._LATEST_VERSION, + ) + + def test_False(self): + self.assertIs( + validators.validator_for(False), + validators._LATEST_VERSION, + ) + + def test_custom_validator(self): + Validator = validators.create( + meta_schema={"id": "meta schema id"}, + version="12", + id_of=lambda s: s.get("id", ""), + ) + schema = {"$schema": "meta schema id"} + self.assertIs( + validators.validator_for(schema), + Validator, + ) + + def test_custom_validator_draft6(self): + Validator = validators.create( + meta_schema={"$id": "meta schema $id"}, + version="13", + ) + schema = {"$schema": "meta schema $id"} + self.assertIs( + validators.validator_for(schema), + Validator, + ) + + def test_validator_for_jsonschema_default(self): + self.assertIs(validators.validator_for({}), validators._LATEST_VERSION) + + def test_validator_for_custom_default(self): + self.assertIs(validators.validator_for({}, default=None), None) + + def test_warns_if_meta_schema_specified_was_not_found(self): + with self.assertWarns(DeprecationWarning) as cm: + validators.validator_for(schema={"$schema": "unknownSchema"}) + + self.assertEqual(cm.filename, __file__) + self.assertEqual( + str(cm.warning), + "The metaschema specified by $schema was not found. " + "Using the latest draft to validate, but this will raise " + "an error in the future.", + ) + + def test_does_not_warn_if_meta_schema_is_unspecified(self): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + validators.validator_for(schema={}, default={}) + self.assertFalse(w) + + def test_validator_for_custom_default_with_schema(self): + schema, default = {"$schema": "mailto:foo@example.com"}, object() + self.assertIs(validators.validator_for(schema, default), default) + + +class TestValidate(TestCase): + def assertUses(self, schema, Validator): + result = [] + with mock.patch.object(Validator, "check_schema", result.append): + validators.validate({}, schema) + self.assertEqual(result, [schema]) + + def test_draft3_validator_is_chosen(self): + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-03/schema#"}, + Validator=validators.Draft3Validator, + ) + # Make sure it works without the empty fragment + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-03/schema"}, + Validator=validators.Draft3Validator, + ) + + def test_draft4_validator_is_chosen(self): + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-04/schema#"}, + Validator=validators.Draft4Validator, + ) + # Make sure it works without the empty fragment + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-04/schema"}, + Validator=validators.Draft4Validator, + ) + + def test_draft6_validator_is_chosen(self): + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-06/schema#"}, + Validator=validators.Draft6Validator, + ) + # Make sure it works without the empty fragment + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-06/schema"}, + Validator=validators.Draft6Validator, + ) + + def test_draft7_validator_is_chosen(self): + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-07/schema#"}, + Validator=validators.Draft7Validator, + ) + # Make sure it works without the empty fragment + self.assertUses( + schema={"$schema": "http://json-schema.org/draft-07/schema"}, + Validator=validators.Draft7Validator, + ) + + def test_draft202012_validator_is_chosen(self): + self.assertUses( + schema={ + "$schema": "https://json-schema.org/draft/2020-12/schema#", + }, + Validator=validators.Draft202012Validator, + ) + # Make sure it works without the empty fragment + self.assertUses( + schema={ + "$schema": "https://json-schema.org/draft/2020-12/schema", + }, + Validator=validators.Draft202012Validator, + ) + + def test_draft202012_validator_is_the_default(self): + self.assertUses(schema={}, Validator=validators.Draft202012Validator) + + def test_validation_error_message(self): + with self.assertRaises(exceptions.ValidationError) as e: + validators.validate(12, {"type": "string"}) + self.assertRegex( + str(e.exception), + "(?s)Failed validating '.*' in schema.*On instance", + ) + + def test_schema_error_message(self): + with self.assertRaises(exceptions.SchemaError) as e: + validators.validate(12, {"type": 12}) + self.assertRegex( + str(e.exception), + "(?s)Failed validating '.*' in metaschema.*On schema", + ) + + def test_it_uses_best_match(self): + schema = { + "oneOf": [ + {"type": "number", "minimum": 20}, + {"type": "array"}, + ], + } + with self.assertRaises(exceptions.ValidationError) as e: + validators.validate(12, schema) + self.assertIn("12 is less than the minimum of 20", str(e.exception)) + + +class TestThreading(TestCase): + """ + Threading-related functionality tests. + + jsonschema doesn't promise thread safety, and its validation behavior + across multiple threads may change at any time, but that means it isn't + safe to share *validators* across threads, not that anytime one has + multiple threads that jsonschema won't work (it certainly is intended to). + + These tests ensure that this minimal level of functionality continues to + work. + """ + + def test_validation_across_a_second_thread(self): + failed = [] + + def validate(): + try: + validators.validate(instance=37, schema=True) + except: # pragma: no cover # noqa: E722 + failed.append(sys.exc_info()) + + validate() # just verify it succeeds + + from threading import Thread + thread = Thread(target=validate) + thread.start() + thread.join() + self.assertEqual((thread.is_alive(), failed), (False, [])) + + +class TestReferencing(TestCase): + def test_registry_with_retrieve(self): + def retrieve(uri): + return DRAFT202012.create_resource({"type": "integer"}) + + registry = referencing.Registry(retrieve=retrieve) + schema = {"$ref": "https://example.com/"} + validator = validators.Draft202012Validator(schema, registry=registry) + + self.assertEqual( + (validator.is_valid(12), validator.is_valid("foo")), + (True, False), + ) + + def test_custom_registries_do_not_autoretrieve_remote_resources(self): + registry = referencing.Registry() + schema = {"$ref": "https://example.com/"} + validator = validators.Draft202012Validator(schema, registry=registry) + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + with self.assertRaises(referencing.exceptions.Unresolvable): + validator.validate(12) + self.assertFalse(w) + + +class TestRefResolver(TestCase): + + base_uri = "" + stored_uri = "foo://stored" + stored_schema = {"stored": "schema"} + + def setUp(self): + self.referrer = {} + self.store = {self.stored_uri: self.stored_schema} + self.resolver = validators._RefResolver( + self.base_uri, self.referrer, self.store, + ) + + def test_it_does_not_retrieve_schema_urls_from_the_network(self): + ref = validators.Draft3Validator.META_SCHEMA["id"] + with mock.patch.object(self.resolver, "resolve_remote") as patched: # noqa: SIM117 + with self.resolver.resolving(ref) as resolved: + pass + self.assertEqual(resolved, validators.Draft3Validator.META_SCHEMA) + self.assertFalse(patched.called) + + def test_it_resolves_local_refs(self): + ref = "#/properties/foo" + self.referrer["properties"] = {"foo": object()} + with self.resolver.resolving(ref) as resolved: + self.assertEqual(resolved, self.referrer["properties"]["foo"]) + + def test_it_resolves_local_refs_with_id(self): + schema = {"id": "http://bar/schema#", "a": {"foo": "bar"}} + resolver = validators._RefResolver.from_schema( + schema, + id_of=lambda schema: schema.get("id", ""), + ) + with resolver.resolving("#/a") as resolved: + self.assertEqual(resolved, schema["a"]) + with resolver.resolving("http://bar/schema#/a") as resolved: + self.assertEqual(resolved, schema["a"]) + + def test_it_retrieves_stored_refs(self): + with self.resolver.resolving(self.stored_uri) as resolved: + self.assertIs(resolved, self.stored_schema) + + self.resolver.store["cached_ref"] = {"foo": 12} + with self.resolver.resolving("cached_ref#/foo") as resolved: + self.assertEqual(resolved, 12) + + def test_it_retrieves_unstored_refs_via_requests(self): + ref = "http://bar#baz" + schema = {"baz": 12} + + if "requests" in sys.modules: # pragma: no cover + self.addCleanup( + sys.modules.__setitem__, "requests", sys.modules["requests"], + ) + sys.modules["requests"] = ReallyFakeRequests({"http://bar": schema}) + + with self.resolver.resolving(ref) as resolved: + self.assertEqual(resolved, 12) + + def test_it_retrieves_unstored_refs_via_urlopen(self): + ref = "http://bar#baz" + schema = {"baz": 12} + + if "requests" in sys.modules: # pragma: no cover + self.addCleanup( + sys.modules.__setitem__, "requests", sys.modules["requests"], + ) + sys.modules["requests"] = None + + @contextmanager + def fake_urlopen(url): + self.assertEqual(url, "http://bar") + yield BytesIO(json.dumps(schema).encode("utf8")) + + with mock.patch("urllib.request.urlopen", new=fake_urlopen): # noqa: SIM117 + with self.resolver.resolving(ref) as resolved: + pass + self.assertEqual(resolved, 12) + + def test_it_retrieves_local_refs_via_urlopen(self): + with tempfile.NamedTemporaryFile(delete=False, mode="wt") as tempf: + self.addCleanup(os.remove, tempf.name) + json.dump({"foo": "bar"}, tempf) + + ref = f"file://{pathname2url(tempf.name)}#foo" + with self.resolver.resolving(ref) as resolved: + self.assertEqual(resolved, "bar") + + def test_it_can_construct_a_base_uri_from_a_schema(self): + schema = {"id": "foo"} + resolver = validators._RefResolver.from_schema( + schema, + id_of=lambda schema: schema.get("id", ""), + ) + self.assertEqual(resolver.base_uri, "foo") + self.assertEqual(resolver.resolution_scope, "foo") + with resolver.resolving("") as resolved: + self.assertEqual(resolved, schema) + with resolver.resolving("#") as resolved: + self.assertEqual(resolved, schema) + with resolver.resolving("foo") as resolved: + self.assertEqual(resolved, schema) + with resolver.resolving("foo#") as resolved: + self.assertEqual(resolved, schema) + + def test_it_can_construct_a_base_uri_from_a_schema_without_id(self): + schema = {} + resolver = validators._RefResolver.from_schema(schema) + self.assertEqual(resolver.base_uri, "") + self.assertEqual(resolver.resolution_scope, "") + with resolver.resolving("") as resolved: + self.assertEqual(resolved, schema) + with resolver.resolving("#") as resolved: + self.assertEqual(resolved, schema) + + def test_custom_uri_scheme_handlers(self): + def handler(url): + self.assertEqual(url, ref) + return schema + + schema = {"foo": "bar"} + ref = "foo://bar" + resolver = validators._RefResolver("", {}, handlers={"foo": handler}) + with resolver.resolving(ref) as resolved: + self.assertEqual(resolved, schema) + + def test_cache_remote_on(self): + response = [object()] + + def handler(url): + try: + return response.pop() + except IndexError: # pragma: no cover + self.fail("Response must not have been cached!") + + ref = "foo://bar" + resolver = validators._RefResolver( + "", {}, cache_remote=True, handlers={"foo": handler}, + ) + with resolver.resolving(ref): + pass + with resolver.resolving(ref): + pass + + def test_cache_remote_off(self): + response = [object()] + + def handler(url): + try: + return response.pop() + except IndexError: # pragma: no cover + self.fail("Handler called twice!") + + ref = "foo://bar" + resolver = validators._RefResolver( + "", {}, cache_remote=False, handlers={"foo": handler}, + ) + with resolver.resolving(ref): + pass + + def test_if_you_give_it_junk_you_get_a_resolution_error(self): + error = ValueError("Oh no! What's this?") + + def handler(url): + raise error + + ref = "foo://bar" + resolver = validators._RefResolver("", {}, handlers={"foo": handler}) + with self.assertRaises(exceptions._RefResolutionError) as err: # noqa: SIM117 + with resolver.resolving(ref): + self.fail("Shouldn't get this far!") # pragma: no cover + self.assertEqual(err.exception, exceptions._RefResolutionError(error)) + + def test_helpful_error_message_on_failed_pop_scope(self): + resolver = validators._RefResolver("", {}) + resolver.pop_scope() + with self.assertRaises(exceptions._RefResolutionError) as exc: + resolver.pop_scope() + self.assertIn("Failed to pop the scope", str(exc.exception)) + + def test_pointer_within_schema_with_different_id(self): + """ + See #1085. + """ + schema = validators.Draft7Validator.META_SCHEMA + one = validators._RefResolver("", schema) + validator = validators.Draft7Validator(schema, resolver=one) + self.assertFalse(validator.is_valid({"maxLength": "foo"})) + + another = { + "allOf": [{"$ref": validators.Draft7Validator.META_SCHEMA["$id"]}], + } + two = validators._RefResolver("", another) + validator = validators.Draft7Validator(another, resolver=two) + self.assertFalse(validator.is_valid({"maxLength": "foo"})) + + def test_newly_created_validator_with_ref_resolver(self): + """ + See https://github.com/python-jsonschema/jsonschema/issues/1061#issuecomment-1624266555. + """ + + def handle(uri): + self.assertEqual(uri, "http://example.com/foo") + return {"type": "integer"} + + resolver = validators._RefResolver("", {}, handlers={"http": handle}) + Validator = validators.create( + meta_schema={}, + validators=validators.Draft4Validator.VALIDATORS, + ) + schema = {"$id": "http://example.com/bar", "$ref": "foo"} + validator = Validator(schema, resolver=resolver) + self.assertEqual( + (validator.is_valid({}), validator.is_valid(37)), + (False, True), + ) + + def test_refresolver_with_pointer_in_schema_with_no_id(self): + """ + See https://github.com/python-jsonschema/jsonschema/issues/1124#issuecomment-1632574249. + """ + + schema = { + "properties": {"x": {"$ref": "#/definitions/x"}}, + "definitions": {"x": {"type": "integer"}}, + } + + validator = validators.Draft202012Validator( + schema, + resolver=validators._RefResolver("", schema), + ) + self.assertEqual( + (validator.is_valid({"x": "y"}), validator.is_valid({"x": 37})), + (False, True), + ) + + + +def sorted_errors(errors): + def key(error): + return ( + [str(e) for e in error.path], + [str(e) for e in error.schema_path], + ) + return sorted(errors, key=key) + + +@define +class ReallyFakeRequests: + + _responses: dict[str, Any] + + def get(self, url): + response = self._responses.get(url) + if url is None: # pragma: no cover + raise ValueError("Unknown URL: " + repr(url)) + return _ReallyFakeJSONResponse(json.dumps(response)) + + +@define +class _ReallyFakeJSONResponse: + + _response: str + + def json(self): + return json.loads(self._response) diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/typing/__init__.py b/venv/lib/python3.12/site-packages/jsonschema/tests/typing/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/typing/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/tests/typing/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4438ece Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/tests/typing/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/typing/__pycache__/test_all_concrete_validators_match_protocol.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema/tests/typing/__pycache__/test_all_concrete_validators_match_protocol.cpython-312.pyc new file mode 100644 index 0000000..d4e4099 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema/tests/typing/__pycache__/test_all_concrete_validators_match_protocol.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema/tests/typing/test_all_concrete_validators_match_protocol.py b/venv/lib/python3.12/site-packages/jsonschema/tests/typing/test_all_concrete_validators_match_protocol.py new file mode 100644 index 0000000..63e8bd4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/tests/typing/test_all_concrete_validators_match_protocol.py @@ -0,0 +1,38 @@ +""" +This module acts as a test that type checkers will allow each validator +class to be assigned to a variable of type `type[Validator]` + +The assignation is only valid if type checkers recognize each Validator +implementation as a valid implementer of the protocol. +""" +from jsonschema.protocols import Validator +from jsonschema.validators import ( + Draft3Validator, + Draft4Validator, + Draft6Validator, + Draft7Validator, + Draft201909Validator, + Draft202012Validator, +) + +my_validator: type[Validator] + +my_validator = Draft3Validator +my_validator = Draft4Validator +my_validator = Draft6Validator +my_validator = Draft7Validator +my_validator = Draft201909Validator +my_validator = Draft202012Validator + + +# in order to confirm that none of the above were incorrectly typed as 'Any' +# ensure that each of these assignments to a non-validator variable requires an +# ignore +none_var: None + +none_var = Draft3Validator # type: ignore[assignment] +none_var = Draft4Validator # type: ignore[assignment] +none_var = Draft6Validator # type: ignore[assignment] +none_var = Draft7Validator # type: ignore[assignment] +none_var = Draft201909Validator # type: ignore[assignment] +none_var = Draft202012Validator # type: ignore[assignment] diff --git a/venv/lib/python3.12/site-packages/jsonschema/validators.py b/venv/lib/python3.12/site-packages/jsonschema/validators.py new file mode 100644 index 0000000..98ddf6b --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema/validators.py @@ -0,0 +1,1410 @@ +""" +Creation and extension of validators, with implementations for existing drafts. +""" +from __future__ import annotations + +from collections import deque +from collections.abc import Iterable, Mapping, Sequence +from functools import lru_cache +from operator import methodcaller +from typing import TYPE_CHECKING +from urllib.parse import unquote, urldefrag, urljoin, urlsplit +from warnings import warn +import contextlib +import json +import reprlib +import warnings + +from attrs import define, field, fields +from jsonschema_specifications import REGISTRY as SPECIFICATIONS +from rpds import HashTrieMap +import referencing.exceptions +import referencing.jsonschema + +from jsonschema import ( + _format, + _keywords, + _legacy_keywords, + _types, + _typing, + _utils, + exceptions, +) + +if TYPE_CHECKING: + from jsonschema.protocols import Validator + +_UNSET = _utils.Unset() + +_VALIDATORS: dict[str, Validator] = {} +_META_SCHEMAS = _utils.URIDict() + + +def __getattr__(name): + if name == "ErrorTree": + warnings.warn( + "Importing ErrorTree from jsonschema.validators is deprecated. " + "Instead import it from jsonschema.exceptions.", + DeprecationWarning, + stacklevel=2, + ) + from jsonschema.exceptions import ErrorTree + return ErrorTree + elif name == "validators": + warnings.warn( + "Accessing jsonschema.validators.validators is deprecated. " + "Use jsonschema.validators.validator_for with a given schema.", + DeprecationWarning, + stacklevel=2, + ) + return _VALIDATORS + elif name == "meta_schemas": + warnings.warn( + "Accessing jsonschema.validators.meta_schemas is deprecated. " + "Use jsonschema.validators.validator_for with a given schema.", + DeprecationWarning, + stacklevel=2, + ) + return _META_SCHEMAS + elif name == "RefResolver": + warnings.warn( + _RefResolver._DEPRECATION_MESSAGE, + DeprecationWarning, + stacklevel=2, + ) + return _RefResolver + raise AttributeError(f"module {__name__} has no attribute {name}") + + +def validates(version): + """ + Register the decorated validator for a ``version`` of the specification. + + Registered validators and their meta schemas will be considered when + parsing :kw:`$schema` keywords' URIs. + + Arguments: + + version (str): + + An identifier to use as the version's name + + Returns: + + collections.abc.Callable: + + a class decorator to decorate the validator with the version + + """ + + def _validates(cls): + _VALIDATORS[version] = cls + meta_schema_id = cls.ID_OF(cls.META_SCHEMA) + _META_SCHEMAS[meta_schema_id] = cls + return cls + return _validates + + +def _warn_for_remote_retrieve(uri: str): + from urllib.request import Request, urlopen + headers = {"User-Agent": "python-jsonschema (deprecated $ref resolution)"} + request = Request(uri, headers=headers) # noqa: S310 + with urlopen(request) as response: # noqa: S310 + warnings.warn( + "Automatically retrieving remote references can be a security " + "vulnerability and is discouraged by the JSON Schema " + "specifications. Relying on this behavior is deprecated " + "and will shortly become an error. If you are sure you want to " + "remotely retrieve your reference and that it is safe to do so, " + "you can find instructions for doing so via referencing.Registry " + "in the referencing documentation " + "(https://referencing.readthedocs.org).", + DeprecationWarning, + stacklevel=9, # Ha ha ha ha magic numbers :/ + ) + return referencing.Resource.from_contents( + json.load(response), + default_specification=referencing.jsonschema.DRAFT202012, + ) + + +_REMOTE_WARNING_REGISTRY = SPECIFICATIONS.combine( + referencing.Registry(retrieve=_warn_for_remote_retrieve), # type: ignore[call-arg] +) + + +def create( + meta_schema: referencing.jsonschema.ObjectSchema, + validators: ( + Mapping[str, _typing.SchemaKeywordValidator] + | Iterable[tuple[str, _typing.SchemaKeywordValidator]] + ) = (), + version: str | None = None, + type_checker: _types.TypeChecker = _types.draft202012_type_checker, + format_checker: _format.FormatChecker = _format.draft202012_format_checker, + id_of: _typing.id_of = referencing.jsonschema.DRAFT202012.id_of, + applicable_validators: _typing.ApplicableValidators = methodcaller( + "items", + ), +) -> type[Validator]: + """ + Create a new validator class. + + Arguments: + + meta_schema: + + the meta schema for the new validator class + + validators: + + a mapping from names to callables, where each callable will + validate the schema property with the given name. + + Each callable should take 4 arguments: + + 1. a validator instance, + 2. the value of the property being validated within the + instance + 3. the instance + 4. the schema + + version: + + an identifier for the version that this validator class will + validate. If provided, the returned validator class will + have its ``__name__`` set to include the version, and also + will have `jsonschema.validators.validates` automatically + called for the given version. + + type_checker: + + a type checker, used when applying the :kw:`type` keyword. + + If unprovided, a `jsonschema.TypeChecker` will be created + with a set of default types typical of JSON Schema drafts. + + format_checker: + + a format checker, used when applying the :kw:`format` keyword. + + If unprovided, a `jsonschema.FormatChecker` will be created + with a set of default formats typical of JSON Schema drafts. + + id_of: + + A function that given a schema, returns its ID. + + applicable_validators: + + A function that, given a schema, returns the list of + applicable schema keywords and associated values + which will be used to validate the instance. + This is mostly used to support pre-draft 7 versions of JSON Schema + which specified behavior around ignoring keywords if they were + siblings of a ``$ref`` keyword. If you're not attempting to + implement similar behavior, you can typically ignore this argument + and leave it at its default. + + Returns: + + a new `jsonschema.protocols.Validator` class + + """ + # preemptively don't shadow the `Validator.format_checker` local + format_checker_arg = format_checker + + specification = referencing.jsonschema.specification_with( + dialect_id=id_of(meta_schema) or "urn:unknown-dialect", + default=referencing.Specification.OPAQUE, + ) + + @define + class Validator: + + VALIDATORS = dict(validators) # noqa: RUF012 + META_SCHEMA = dict(meta_schema) # noqa: RUF012 + TYPE_CHECKER = type_checker + FORMAT_CHECKER = format_checker_arg + ID_OF = staticmethod(id_of) + + _APPLICABLE_VALIDATORS = applicable_validators + _validators = field(init=False, repr=False, eq=False) + + schema: referencing.jsonschema.Schema = field(repr=reprlib.repr) + _ref_resolver = field(default=None, repr=False, alias="resolver") + format_checker: _format.FormatChecker | None = field(default=None) + # TODO: include new meta-schemas added at runtime + _registry: referencing.jsonschema.SchemaRegistry = field( + default=_REMOTE_WARNING_REGISTRY, + kw_only=True, + repr=False, + ) + _resolver = field( + alias="_resolver", + default=None, + kw_only=True, + repr=False, + ) + + def __init_subclass__(cls): + warnings.warn( + ( + "Subclassing validator classes is not intended to " + "be part of their public API. A future version " + "will make doing so an error, as the behavior of " + "subclasses isn't guaranteed to stay the same " + "between releases of jsonschema. Instead, prefer " + "composition of validators, wrapping them in an object " + "owned entirely by the downstream library." + ), + DeprecationWarning, + stacklevel=2, + ) + + def evolve(self, **changes): + cls = self.__class__ + schema = changes.setdefault("schema", self.schema) + NewValidator = validator_for(schema, default=cls) + + for field in fields(cls): # noqa: F402 + if not field.init: + continue + attr_name = field.name + init_name = field.alias + if init_name not in changes: + changes[init_name] = getattr(self, attr_name) + + return NewValidator(**changes) + + cls.evolve = evolve + + def __attrs_post_init__(self): + if self._resolver is None: + registry = self._registry + if registry is not _REMOTE_WARNING_REGISTRY: + registry = SPECIFICATIONS.combine(registry) + resource = specification.create_resource(self.schema) + self._resolver = registry.resolver_with_root(resource) + + if self.schema is True or self.schema is False: + self._validators = [] + else: + self._validators = [ + (self.VALIDATORS[k], k, v) + for k, v in applicable_validators(self.schema) + if k in self.VALIDATORS + ] + + # REMOVEME: Legacy ref resolution state management. + push_scope = getattr(self._ref_resolver, "push_scope", None) + if push_scope is not None: + id = id_of(self.schema) + if id is not None: + push_scope(id) + + @classmethod + def check_schema(cls, schema, format_checker=_UNSET): + Validator = validator_for(cls.META_SCHEMA, default=cls) + if format_checker is _UNSET: + format_checker = Validator.FORMAT_CHECKER + validator = Validator( + schema=cls.META_SCHEMA, + format_checker=format_checker, + ) + for error in validator.iter_errors(schema): + raise exceptions.SchemaError.create_from(error) + + @property + def resolver(self): + warnings.warn( + ( + f"Accessing {self.__class__.__name__}.resolver is " + "deprecated as of v4.18.0, in favor of the " + "https://github.com/python-jsonschema/referencing " + "library, which provides more compliant referencing " + "behavior as well as more flexible APIs for " + "customization." + ), + DeprecationWarning, + stacklevel=2, + ) + if self._ref_resolver is None: + self._ref_resolver = _RefResolver.from_schema( + self.schema, + id_of=id_of, + ) + return self._ref_resolver + + def evolve(self, **changes): + schema = changes.setdefault("schema", self.schema) + NewValidator = validator_for(schema, default=self.__class__) + + for (attr_name, init_name) in evolve_fields: + if init_name not in changes: + changes[init_name] = getattr(self, attr_name) + + return NewValidator(**changes) + + def iter_errors(self, instance, _schema=None): + if _schema is not None: + warnings.warn( + ( + "Passing a schema to Validator.iter_errors " + "is deprecated and will be removed in a future " + "release. Call validator.evolve(schema=new_schema)." + "iter_errors(...) instead." + ), + DeprecationWarning, + stacklevel=2, + ) + validators = [ + (self.VALIDATORS[k], k, v) + for k, v in applicable_validators(_schema) + if k in self.VALIDATORS + ] + else: + _schema, validators = self.schema, self._validators + + if _schema is True: + return + elif _schema is False: + yield exceptions.ValidationError( + f"False schema does not allow {instance!r}", + validator=None, + validator_value=None, + instance=instance, + schema=_schema, + ) + return + + for validator, k, v in validators: + errors = validator(self, v, instance, _schema) or () + for error in errors: + # set details if not already set by the called fn + error._set( + validator=k, + validator_value=v, + instance=instance, + schema=_schema, + type_checker=self.TYPE_CHECKER, + ) + if k not in {"if", "$ref"}: + error.schema_path.appendleft(k) + yield error + + def descend( + self, + instance, + schema, + path=None, + schema_path=None, + resolver=None, + ): + if schema is True: + return + elif schema is False: + yield exceptions.ValidationError( + f"False schema does not allow {instance!r}", + validator=None, + validator_value=None, + instance=instance, + schema=schema, + ) + return + + if self._ref_resolver is not None: + evolved = self.evolve(schema=schema) + else: + if resolver is None: + resolver = self._resolver.in_subresource( + specification.create_resource(schema), + ) + evolved = self.evolve(schema=schema, _resolver=resolver) + + for k, v in applicable_validators(schema): + validator = evolved.VALIDATORS.get(k) + if validator is None: + continue + + errors = validator(evolved, v, instance, schema) or () + for error in errors: + # set details if not already set by the called fn + error._set( + validator=k, + validator_value=v, + instance=instance, + schema=schema, + type_checker=evolved.TYPE_CHECKER, + ) + if k not in {"if", "$ref"}: + error.schema_path.appendleft(k) + if path is not None: + error.path.appendleft(path) + if schema_path is not None: + error.schema_path.appendleft(schema_path) + yield error + + def validate(self, *args, **kwargs): + for error in self.iter_errors(*args, **kwargs): + raise error + + def is_type(self, instance, type): + try: + return self.TYPE_CHECKER.is_type(instance, type) + except exceptions.UndefinedTypeCheck: + exc = exceptions.UnknownType(type, instance, self.schema) + raise exc from None + + def _validate_reference(self, ref, instance): + if self._ref_resolver is None: + try: + resolved = self._resolver.lookup(ref) + except referencing.exceptions.Unresolvable as err: + raise exceptions._WrappedReferencingError(err) from err + + return self.descend( + instance, + resolved.contents, + resolver=resolved.resolver, + ) + else: + resolve = getattr(self._ref_resolver, "resolve", None) + if resolve is None: + with self._ref_resolver.resolving(ref) as resolved: + return self.descend(instance, resolved) + else: + scope, resolved = resolve(ref) + self._ref_resolver.push_scope(scope) + + try: + return list(self.descend(instance, resolved)) + finally: + self._ref_resolver.pop_scope() + + def is_valid(self, instance, _schema=None): + if _schema is not None: + warnings.warn( + ( + "Passing a schema to Validator.is_valid is deprecated " + "and will be removed in a future release. Call " + "validator.evolve(schema=new_schema).is_valid(...) " + "instead." + ), + DeprecationWarning, + stacklevel=2, + ) + self = self.evolve(schema=_schema) + + error = next(self.iter_errors(instance), None) + return error is None + + evolve_fields = [ + (field.name, field.alias) + for field in fields(Validator) + if field.init + ] + + if version is not None: + safe = version.title().replace(" ", "").replace("-", "") + Validator.__name__ = Validator.__qualname__ = f"{safe}Validator" + Validator = validates(version)(Validator) # type: ignore[misc] + + return Validator # type: ignore[return-value] + + +def extend( + validator, + validators=(), + version=None, + type_checker=None, + format_checker=None, +): + """ + Create a new validator class by extending an existing one. + + Arguments: + + validator (jsonschema.protocols.Validator): + + an existing validator class + + validators (collections.abc.Mapping): + + a mapping of new validator callables to extend with, whose + structure is as in `create`. + + .. note:: + + Any validator callables with the same name as an + existing one will (silently) replace the old validator + callable entirely, effectively overriding any validation + done in the "parent" validator class. + + If you wish to instead extend the behavior of a parent's + validator callable, delegate and call it directly in + the new validator function by retrieving it using + ``OldValidator.VALIDATORS["validation_keyword_name"]``. + + version (str): + + a version for the new validator class + + type_checker (jsonschema.TypeChecker): + + a type checker, used when applying the :kw:`type` keyword. + + If unprovided, the type checker of the extended + `jsonschema.protocols.Validator` will be carried along. + + format_checker (jsonschema.FormatChecker): + + a format checker, used when applying the :kw:`format` keyword. + + If unprovided, the format checker of the extended + `jsonschema.protocols.Validator` will be carried along. + + Returns: + + a new `jsonschema.protocols.Validator` class extending the one + provided + + .. note:: Meta Schemas + + The new validator class will have its parent's meta schema. + + If you wish to change or extend the meta schema in the new + validator class, modify ``META_SCHEMA`` directly on the returned + class. Note that no implicit copying is done, so a copy should + likely be made before modifying it, in order to not affect the + old validator. + + """ + all_validators = dict(validator.VALIDATORS) + all_validators.update(validators) + + if type_checker is None: + type_checker = validator.TYPE_CHECKER + if format_checker is None: + format_checker = validator.FORMAT_CHECKER + return create( + meta_schema=validator.META_SCHEMA, + validators=all_validators, + version=version, + type_checker=type_checker, + format_checker=format_checker, + id_of=validator.ID_OF, + applicable_validators=validator._APPLICABLE_VALIDATORS, + ) + + +Draft3Validator = create( + meta_schema=SPECIFICATIONS.contents( + "http://json-schema.org/draft-03/schema#", + ), + validators={ + "$ref": _keywords.ref, + "additionalItems": _legacy_keywords.additionalItems, + "additionalProperties": _keywords.additionalProperties, + "dependencies": _legacy_keywords.dependencies_draft3, + "disallow": _legacy_keywords.disallow_draft3, + "divisibleBy": _keywords.multipleOf, + "enum": _keywords.enum, + "extends": _legacy_keywords.extends_draft3, + "format": _keywords.format, + "items": _legacy_keywords.items_draft3_draft4, + "maxItems": _keywords.maxItems, + "maxLength": _keywords.maxLength, + "maximum": _legacy_keywords.maximum_draft3_draft4, + "minItems": _keywords.minItems, + "minLength": _keywords.minLength, + "minimum": _legacy_keywords.minimum_draft3_draft4, + "pattern": _keywords.pattern, + "patternProperties": _keywords.patternProperties, + "properties": _legacy_keywords.properties_draft3, + "type": _legacy_keywords.type_draft3, + "uniqueItems": _keywords.uniqueItems, + }, + type_checker=_types.draft3_type_checker, + format_checker=_format.draft3_format_checker, + version="draft3", + id_of=referencing.jsonschema.DRAFT3.id_of, + applicable_validators=_legacy_keywords.ignore_ref_siblings, +) + +Draft4Validator = create( + meta_schema=SPECIFICATIONS.contents( + "http://json-schema.org/draft-04/schema#", + ), + validators={ + "$ref": _keywords.ref, + "additionalItems": _legacy_keywords.additionalItems, + "additionalProperties": _keywords.additionalProperties, + "allOf": _keywords.allOf, + "anyOf": _keywords.anyOf, + "dependencies": _legacy_keywords.dependencies_draft4_draft6_draft7, + "enum": _keywords.enum, + "format": _keywords.format, + "items": _legacy_keywords.items_draft3_draft4, + "maxItems": _keywords.maxItems, + "maxLength": _keywords.maxLength, + "maxProperties": _keywords.maxProperties, + "maximum": _legacy_keywords.maximum_draft3_draft4, + "minItems": _keywords.minItems, + "minLength": _keywords.minLength, + "minProperties": _keywords.minProperties, + "minimum": _legacy_keywords.minimum_draft3_draft4, + "multipleOf": _keywords.multipleOf, + "not": _keywords.not_, + "oneOf": _keywords.oneOf, + "pattern": _keywords.pattern, + "patternProperties": _keywords.patternProperties, + "properties": _keywords.properties, + "required": _keywords.required, + "type": _keywords.type, + "uniqueItems": _keywords.uniqueItems, + }, + type_checker=_types.draft4_type_checker, + format_checker=_format.draft4_format_checker, + version="draft4", + id_of=referencing.jsonschema.DRAFT4.id_of, + applicable_validators=_legacy_keywords.ignore_ref_siblings, +) + +Draft6Validator = create( + meta_schema=SPECIFICATIONS.contents( + "http://json-schema.org/draft-06/schema#", + ), + validators={ + "$ref": _keywords.ref, + "additionalItems": _legacy_keywords.additionalItems, + "additionalProperties": _keywords.additionalProperties, + "allOf": _keywords.allOf, + "anyOf": _keywords.anyOf, + "const": _keywords.const, + "contains": _legacy_keywords.contains_draft6_draft7, + "dependencies": _legacy_keywords.dependencies_draft4_draft6_draft7, + "enum": _keywords.enum, + "exclusiveMaximum": _keywords.exclusiveMaximum, + "exclusiveMinimum": _keywords.exclusiveMinimum, + "format": _keywords.format, + "items": _legacy_keywords.items_draft6_draft7_draft201909, + "maxItems": _keywords.maxItems, + "maxLength": _keywords.maxLength, + "maxProperties": _keywords.maxProperties, + "maximum": _keywords.maximum, + "minItems": _keywords.minItems, + "minLength": _keywords.minLength, + "minProperties": _keywords.minProperties, + "minimum": _keywords.minimum, + "multipleOf": _keywords.multipleOf, + "not": _keywords.not_, + "oneOf": _keywords.oneOf, + "pattern": _keywords.pattern, + "patternProperties": _keywords.patternProperties, + "properties": _keywords.properties, + "propertyNames": _keywords.propertyNames, + "required": _keywords.required, + "type": _keywords.type, + "uniqueItems": _keywords.uniqueItems, + }, + type_checker=_types.draft6_type_checker, + format_checker=_format.draft6_format_checker, + version="draft6", + id_of=referencing.jsonschema.DRAFT6.id_of, + applicable_validators=_legacy_keywords.ignore_ref_siblings, +) + +Draft7Validator = create( + meta_schema=SPECIFICATIONS.contents( + "http://json-schema.org/draft-07/schema#", + ), + validators={ + "$ref": _keywords.ref, + "additionalItems": _legacy_keywords.additionalItems, + "additionalProperties": _keywords.additionalProperties, + "allOf": _keywords.allOf, + "anyOf": _keywords.anyOf, + "const": _keywords.const, + "contains": _legacy_keywords.contains_draft6_draft7, + "dependencies": _legacy_keywords.dependencies_draft4_draft6_draft7, + "enum": _keywords.enum, + "exclusiveMaximum": _keywords.exclusiveMaximum, + "exclusiveMinimum": _keywords.exclusiveMinimum, + "format": _keywords.format, + "if": _keywords.if_, + "items": _legacy_keywords.items_draft6_draft7_draft201909, + "maxItems": _keywords.maxItems, + "maxLength": _keywords.maxLength, + "maxProperties": _keywords.maxProperties, + "maximum": _keywords.maximum, + "minItems": _keywords.minItems, + "minLength": _keywords.minLength, + "minProperties": _keywords.minProperties, + "minimum": _keywords.minimum, + "multipleOf": _keywords.multipleOf, + "not": _keywords.not_, + "oneOf": _keywords.oneOf, + "pattern": _keywords.pattern, + "patternProperties": _keywords.patternProperties, + "properties": _keywords.properties, + "propertyNames": _keywords.propertyNames, + "required": _keywords.required, + "type": _keywords.type, + "uniqueItems": _keywords.uniqueItems, + }, + type_checker=_types.draft7_type_checker, + format_checker=_format.draft7_format_checker, + version="draft7", + id_of=referencing.jsonschema.DRAFT7.id_of, + applicable_validators=_legacy_keywords.ignore_ref_siblings, +) + +Draft201909Validator = create( + meta_schema=SPECIFICATIONS.contents( + "https://json-schema.org/draft/2019-09/schema", + ), + validators={ + "$recursiveRef": _legacy_keywords.recursiveRef, + "$ref": _keywords.ref, + "additionalItems": _legacy_keywords.additionalItems, + "additionalProperties": _keywords.additionalProperties, + "allOf": _keywords.allOf, + "anyOf": _keywords.anyOf, + "const": _keywords.const, + "contains": _keywords.contains, + "dependentRequired": _keywords.dependentRequired, + "dependentSchemas": _keywords.dependentSchemas, + "enum": _keywords.enum, + "exclusiveMaximum": _keywords.exclusiveMaximum, + "exclusiveMinimum": _keywords.exclusiveMinimum, + "format": _keywords.format, + "if": _keywords.if_, + "items": _legacy_keywords.items_draft6_draft7_draft201909, + "maxItems": _keywords.maxItems, + "maxLength": _keywords.maxLength, + "maxProperties": _keywords.maxProperties, + "maximum": _keywords.maximum, + "minItems": _keywords.minItems, + "minLength": _keywords.minLength, + "minProperties": _keywords.minProperties, + "minimum": _keywords.minimum, + "multipleOf": _keywords.multipleOf, + "not": _keywords.not_, + "oneOf": _keywords.oneOf, + "pattern": _keywords.pattern, + "patternProperties": _keywords.patternProperties, + "properties": _keywords.properties, + "propertyNames": _keywords.propertyNames, + "required": _keywords.required, + "type": _keywords.type, + "unevaluatedItems": _legacy_keywords.unevaluatedItems_draft2019, + "unevaluatedProperties": ( + _legacy_keywords.unevaluatedProperties_draft2019 + ), + "uniqueItems": _keywords.uniqueItems, + }, + type_checker=_types.draft201909_type_checker, + format_checker=_format.draft201909_format_checker, + version="draft2019-09", +) + +Draft202012Validator = create( + meta_schema=SPECIFICATIONS.contents( + "https://json-schema.org/draft/2020-12/schema", + ), + validators={ + "$dynamicRef": _keywords.dynamicRef, + "$ref": _keywords.ref, + "additionalProperties": _keywords.additionalProperties, + "allOf": _keywords.allOf, + "anyOf": _keywords.anyOf, + "const": _keywords.const, + "contains": _keywords.contains, + "dependentRequired": _keywords.dependentRequired, + "dependentSchemas": _keywords.dependentSchemas, + "enum": _keywords.enum, + "exclusiveMaximum": _keywords.exclusiveMaximum, + "exclusiveMinimum": _keywords.exclusiveMinimum, + "format": _keywords.format, + "if": _keywords.if_, + "items": _keywords.items, + "maxItems": _keywords.maxItems, + "maxLength": _keywords.maxLength, + "maxProperties": _keywords.maxProperties, + "maximum": _keywords.maximum, + "minItems": _keywords.minItems, + "minLength": _keywords.minLength, + "minProperties": _keywords.minProperties, + "minimum": _keywords.minimum, + "multipleOf": _keywords.multipleOf, + "not": _keywords.not_, + "oneOf": _keywords.oneOf, + "pattern": _keywords.pattern, + "patternProperties": _keywords.patternProperties, + "prefixItems": _keywords.prefixItems, + "properties": _keywords.properties, + "propertyNames": _keywords.propertyNames, + "required": _keywords.required, + "type": _keywords.type, + "unevaluatedItems": _keywords.unevaluatedItems, + "unevaluatedProperties": _keywords.unevaluatedProperties, + "uniqueItems": _keywords.uniqueItems, + }, + type_checker=_types.draft202012_type_checker, + format_checker=_format.draft202012_format_checker, + version="draft2020-12", +) + +_LATEST_VERSION: type[Validator] = Draft202012Validator + + +class _RefResolver: + """ + Resolve JSON References. + + Arguments: + + base_uri (str): + + The URI of the referring document + + referrer: + + The actual referring document + + store (dict): + + A mapping from URIs to documents to cache + + cache_remote (bool): + + Whether remote refs should be cached after first resolution + + handlers (dict): + + A mapping from URI schemes to functions that should be used + to retrieve them + + urljoin_cache (:func:`functools.lru_cache`): + + A cache that will be used for caching the results of joining + the resolution scope to subscopes. + + remote_cache (:func:`functools.lru_cache`): + + A cache that will be used for caching the results of + resolved remote URLs. + + Attributes: + + cache_remote (bool): + + Whether remote refs should be cached after first resolution + + .. deprecated:: v4.18.0 + + ``RefResolver`` has been deprecated in favor of `referencing`. + + """ + + _DEPRECATION_MESSAGE = ( + "jsonschema.RefResolver is deprecated as of v4.18.0, in favor of the " + "https://github.com/python-jsonschema/referencing library, which " + "provides more compliant referencing behavior as well as more " + "flexible APIs for customization. A future release will remove " + "RefResolver. Please file a feature request (on referencing) if you " + "are missing an API for the kind of customization you need." + ) + + def __init__( + self, + base_uri, + referrer, + store=HashTrieMap(), + cache_remote=True, + handlers=(), + urljoin_cache=None, + remote_cache=None, + ): + if urljoin_cache is None: + urljoin_cache = lru_cache(1024)(urljoin) + if remote_cache is None: + remote_cache = lru_cache(1024)(self.resolve_from_url) + + self.referrer = referrer + self.cache_remote = cache_remote + self.handlers = dict(handlers) + + self._scopes_stack = [base_uri] + + self.store = _utils.URIDict( + (uri, each.contents) for uri, each in SPECIFICATIONS.items() + ) + self.store.update( + (id, each.META_SCHEMA) for id, each in _META_SCHEMAS.items() + ) + self.store.update(store) + self.store.update( + (schema["$id"], schema) + for schema in store.values() + if isinstance(schema, Mapping) and "$id" in schema + ) + self.store[base_uri] = referrer + + self._urljoin_cache = urljoin_cache + self._remote_cache = remote_cache + + @classmethod + def from_schema( # noqa: D417 + cls, + schema, + id_of=referencing.jsonschema.DRAFT202012.id_of, + *args, + **kwargs, + ): + """ + Construct a resolver from a JSON schema object. + + Arguments: + + schema: + + the referring schema + + Returns: + + `_RefResolver` + + """ + return cls(base_uri=id_of(schema) or "", referrer=schema, *args, **kwargs) # noqa: B026, E501 + + def push_scope(self, scope): + """ + Enter a given sub-scope. + + Treats further dereferences as being performed underneath the + given scope. + """ + self._scopes_stack.append( + self._urljoin_cache(self.resolution_scope, scope), + ) + + def pop_scope(self): + """ + Exit the most recent entered scope. + + Treats further dereferences as being performed underneath the + original scope. + + Don't call this method more times than `push_scope` has been + called. + """ + try: + self._scopes_stack.pop() + except IndexError: + raise exceptions._RefResolutionError( + "Failed to pop the scope from an empty stack. " + "`pop_scope()` should only be called once for every " + "`push_scope()`", + ) from None + + @property + def resolution_scope(self): + """ + Retrieve the current resolution scope. + """ + return self._scopes_stack[-1] + + @property + def base_uri(self): + """ + Retrieve the current base URI, not including any fragment. + """ + uri, _ = urldefrag(self.resolution_scope) + return uri + + @contextlib.contextmanager + def in_scope(self, scope): + """ + Temporarily enter the given scope for the duration of the context. + + .. deprecated:: v4.0.0 + """ + warnings.warn( + "jsonschema.RefResolver.in_scope is deprecated and will be " + "removed in a future release.", + DeprecationWarning, + stacklevel=3, + ) + self.push_scope(scope) + try: + yield + finally: + self.pop_scope() + + @contextlib.contextmanager + def resolving(self, ref): + """ + Resolve the given ``ref`` and enter its resolution scope. + + Exits the scope on exit of this context manager. + + Arguments: + + ref (str): + + The reference to resolve + + """ + url, resolved = self.resolve(ref) + self.push_scope(url) + try: + yield resolved + finally: + self.pop_scope() + + def _find_in_referrer(self, key): + return self._get_subschemas_cache()[key] + + @lru_cache # noqa: B019 + def _get_subschemas_cache(self): + cache = {key: [] for key in _SUBSCHEMAS_KEYWORDS} + for keyword, subschema in _search_schema( + self.referrer, _match_subschema_keywords, + ): + cache[keyword].append(subschema) + return cache + + @lru_cache # noqa: B019 + def _find_in_subschemas(self, url): + subschemas = self._get_subschemas_cache()["$id"] + if not subschemas: + return None + uri, fragment = urldefrag(url) + for subschema in subschemas: + id = subschema["$id"] + if not isinstance(id, str): + continue + target_uri = self._urljoin_cache(self.resolution_scope, id) + if target_uri.rstrip("/") == uri.rstrip("/"): + if fragment: + subschema = self.resolve_fragment(subschema, fragment) + self.store[url] = subschema + return url, subschema + return None + + def resolve(self, ref): + """ + Resolve the given reference. + """ + url = self._urljoin_cache(self.resolution_scope, ref).rstrip("/") + + match = self._find_in_subschemas(url) + if match is not None: + return match + + return url, self._remote_cache(url) + + def resolve_from_url(self, url): + """ + Resolve the given URL. + """ + url, fragment = urldefrag(url) + if not url: + url = self.base_uri + + try: + document = self.store[url] + except KeyError: + try: + document = self.resolve_remote(url) + except Exception as exc: + raise exceptions._RefResolutionError(exc) from exc + + return self.resolve_fragment(document, fragment) + + def resolve_fragment(self, document, fragment): + """ + Resolve a ``fragment`` within the referenced ``document``. + + Arguments: + + document: + + The referent document + + fragment (str): + + a URI fragment to resolve within it + + """ + fragment = fragment.lstrip("/") + + if not fragment: + return document + + if document is self.referrer: + find = self._find_in_referrer + else: + + def find(key): + yield from _search_schema(document, _match_keyword(key)) + + for keyword in ["$anchor", "$dynamicAnchor"]: + for subschema in find(keyword): + if fragment == subschema[keyword]: + return subschema + for keyword in ["id", "$id"]: + for subschema in find(keyword): + if "#" + fragment == subschema[keyword]: + return subschema + + # Resolve via path + parts = unquote(fragment).split("/") if fragment else [] + for part in parts: + part = part.replace("~1", "/").replace("~0", "~") + + if isinstance(document, Sequence): + try: # noqa: SIM105 + part = int(part) + except ValueError: + pass + try: + document = document[part] + except (TypeError, LookupError) as err: + raise exceptions._RefResolutionError( + f"Unresolvable JSON pointer: {fragment!r}", + ) from err + + return document + + def resolve_remote(self, uri): + """ + Resolve a remote ``uri``. + + If called directly, does not check the store first, but after + retrieving the document at the specified URI it will be saved in + the store if :attr:`cache_remote` is True. + + .. note:: + + If the requests_ library is present, ``jsonschema`` will use it to + request the remote ``uri``, so that the correct encoding is + detected and used. + + If it isn't, or if the scheme of the ``uri`` is not ``http`` or + ``https``, UTF-8 is assumed. + + Arguments: + + uri (str): + + The URI to resolve + + Returns: + + The retrieved document + + .. _requests: https://pypi.org/project/requests/ + + """ + try: + import requests + except ImportError: + requests = None + + scheme = urlsplit(uri).scheme + + if scheme in self.handlers: + result = self.handlers[scheme](uri) + elif scheme in ["http", "https"] and requests: + # Requests has support for detecting the correct encoding of + # json over http + result = requests.get(uri).json() + else: + # Otherwise, pass off to urllib and assume utf-8 + from urllib.request import urlopen + with urlopen(uri) as url: # noqa: S310 + result = json.loads(url.read().decode("utf-8")) + + if self.cache_remote: + self.store[uri] = result + return result + + +_SUBSCHEMAS_KEYWORDS = ("$id", "id", "$anchor", "$dynamicAnchor") + + +def _match_keyword(keyword): + + def matcher(value): + if keyword in value: + yield value + + return matcher + + +def _match_subschema_keywords(value): + for keyword in _SUBSCHEMAS_KEYWORDS: + if keyword in value: + yield keyword, value + + +def _search_schema(schema, matcher): + """Breadth-first search routine.""" + values = deque([schema]) + while values: + value = values.pop() + if not isinstance(value, dict): + continue + yield from matcher(value) + values.extendleft(value.values()) + + +def validate(instance, schema, cls=None, *args, **kwargs): # noqa: D417 + """ + Validate an instance under the given schema. + + >>> validate([2, 3, 4], {"maxItems": 2}) + Traceback (most recent call last): + ... + ValidationError: [2, 3, 4] is too long + + :func:`~jsonschema.validators.validate` will first verify that the + provided schema is itself valid, since not doing so can lead to less + obvious error messages and fail in less obvious or consistent ways. + + If you know you have a valid schema already, especially + if you intend to validate multiple instances with + the same schema, you likely would prefer using the + `jsonschema.protocols.Validator.validate` method directly on a + specific validator (e.g. ``Draft202012Validator.validate``). + + + Arguments: + + instance: + + The instance to validate + + schema: + + The schema to validate with + + cls (jsonschema.protocols.Validator): + + The class that will be used to validate the instance. + + If the ``cls`` argument is not provided, two things will happen + in accordance with the specification. First, if the schema has a + :kw:`$schema` keyword containing a known meta-schema [#]_ then the + proper validator will be used. The specification recommends that + all schemas contain :kw:`$schema` properties for this reason. If no + :kw:`$schema` property is found, the default validator class is the + latest released draft. + + Any other provided positional and keyword arguments will be passed + on when instantiating the ``cls``. + + Raises: + + `jsonschema.exceptions.ValidationError`: + + if the instance is invalid + + `jsonschema.exceptions.SchemaError`: + + if the schema itself is invalid + + .. rubric:: Footnotes + .. [#] known by a validator registered with + `jsonschema.validators.validates` + + """ + if cls is None: + cls = validator_for(schema) + + cls.check_schema(schema) + validator = cls(schema, *args, **kwargs) + error = exceptions.best_match(validator.iter_errors(instance)) + if error is not None: + raise error + + +def validator_for( + schema, + default: type[Validator] | _utils.Unset = _UNSET, +) -> type[Validator]: + """ + Retrieve the validator class appropriate for validating the given schema. + + Uses the :kw:`$schema` keyword that should be present in the given + schema to look up the appropriate validator class. + + Arguments: + + schema (collections.abc.Mapping or bool): + + the schema to look at + + default: + + the default to return if the appropriate validator class + cannot be determined. + + If unprovided, the default is to return the latest supported + draft. + + Examples: + + The :kw:`$schema` JSON Schema keyword will control which validator + class is returned: + + >>> schema = { + ... "$schema": "https://json-schema.org/draft/2020-12/schema", + ... "type": "integer", + ... } + >>> jsonschema.validators.validator_for(schema) + + + + Here, a draft 7 schema instead will return the draft 7 validator: + + >>> schema = { + ... "$schema": "http://json-schema.org/draft-07/schema#", + ... "type": "integer", + ... } + >>> jsonschema.validators.validator_for(schema) + + + + Schemas with no ``$schema`` keyword will fallback to the default + argument: + + >>> schema = {"type": "integer"} + >>> jsonschema.validators.validator_for( + ... schema, default=Draft7Validator, + ... ) + + + or if none is provided, to the latest version supported. + Always including the keyword when authoring schemas is highly + recommended. + + """ + DefaultValidator = _LATEST_VERSION if default is _UNSET else default + + if schema is True or schema is False or "$schema" not in schema: + return DefaultValidator # type: ignore[return-value] + if schema["$schema"] not in _META_SCHEMAS and default is _UNSET: + warn( + ( + "The metaschema specified by $schema was not found. " + "Using the latest draft to validate, but this will raise " + "an error in the future." + ), + DeprecationWarning, + stacklevel=2, + ) + return _META_SCHEMAS.get(schema["$schema"], DefaultValidator) diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/METADATA b/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/METADATA new file mode 100644 index 0000000..ff5daef --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/METADATA @@ -0,0 +1,54 @@ +Metadata-Version: 2.4 +Name: jsonschema-specifications +Version: 2025.9.1 +Summary: The JSON Schema meta-schemas and vocabularies, exposed as a Registry +Project-URL: Documentation, https://jsonschema-specifications.readthedocs.io/ +Project-URL: Homepage, https://github.com/python-jsonschema/jsonschema-specifications +Project-URL: Issues, https://github.com/python-jsonschema/jsonschema-specifications/issues/ +Project-URL: Funding, https://github.com/sponsors/Julian +Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-jsonschema-specifications?utm_source=pypi-jsonschema-specifications&utm_medium=referral&utm_campaign=pypi-link +Project-URL: Source, https://github.com/python-jsonschema/jsonschema-specifications +Author-email: Julian Berman +License-Expression: MIT +License-File: COPYING +Keywords: data validation,json,json schema,jsonschema,validation +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: File Formats :: JSON +Classifier: Topic :: File Formats :: JSON :: JSON Schema +Requires-Python: >=3.9 +Requires-Dist: referencing>=0.31.0 +Description-Content-Type: text/x-rst + +============================= +``jsonschema-specifications`` +============================= + +|PyPI| |Pythons| |CI| |ReadTheDocs| + +JSON support files from the `JSON Schema Specifications `_ (metaschemas, vocabularies, etc.), packaged for runtime access from Python as a `referencing-based Schema Registry `_. + +.. |PyPI| image:: https://img.shields.io/pypi/v/jsonschema-specifications.svg + :alt: PyPI version + :target: https://pypi.org/project/jsonschema-specifications/ + +.. |Pythons| image:: https://img.shields.io/pypi/pyversions/jsonschema-specifications.svg + :alt: Supported Python versions + :target: https://pypi.org/project/jsonschema-specifications/ + +.. |CI| image:: https://github.com/python-jsonschema/jsonschema-specifications/workflows/CI/badge.svg + :alt: Build status + :target: https://github.com/python-jsonschema/jsonschema-specifications/actions?query=workflow%3ACI + +.. |ReadTheDocs| image:: https://readthedocs.org/projects/jsonschema-specifications/badge/?version=stable&style=flat + :alt: ReadTheDocs status + :target: https://jsonschema-specifications.readthedocs.io/en/stable/ diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/RECORD new file mode 100644 index 0000000..8e46690 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/RECORD @@ -0,0 +1,33 @@ +jsonschema_specifications-2025.9.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +jsonschema_specifications-2025.9.1.dist-info/METADATA,sha256=NavUF1fzK06iR1aSDe1HtwFz13y8BSpabTq1g7Lo2J0,2907 +jsonschema_specifications-2025.9.1.dist-info/RECORD,, +jsonschema_specifications-2025.9.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +jsonschema_specifications-2025.9.1.dist-info/licenses/COPYING,sha256=QtzWNJX4e063x3V6-jebtVpT-Ur9el9lfZrfVyNuUVw,1057 +jsonschema_specifications/__init__.py,sha256=qoTB2DKY7qvNrGhMPH6gtmAJRLilmVQ-fFZwT6ryqw0,386 +jsonschema_specifications/__pycache__/__init__.cpython-312.pyc,, +jsonschema_specifications/__pycache__/_core.cpython-312.pyc,, +jsonschema_specifications/_core.py,sha256=tFhc1CMleJ3AJOK_bjxOpFQTdrsUClFGfFxPBU_CebM,1140 +jsonschema_specifications/schemas/draft201909/metaschema.json,sha256=e3YbPhIfCgyh6ioLjizIVrz4AWBLgmjXG6yqICvAwTs,1785 +jsonschema_specifications/schemas/draft201909/vocabularies/applicator,sha256=aJUQDplyb7sQcFhRK77D7P1LJOj9L6zuPlBe5ysNTDE,1860 +jsonschema_specifications/schemas/draft201909/vocabularies/content,sha256=m31PVaTi_bAsQwBo_f-rxzKt3OI42j8d8mkCScM1MnQ,517 +jsonschema_specifications/schemas/draft201909/vocabularies/core,sha256=taLElX9kldClCB8ECevooU5BOayyA_x0hHH47eKvWyw,1531 +jsonschema_specifications/schemas/draft201909/vocabularies/format,sha256=UOu_55BhGoSbjMQAoJwdDg-2q1wNQ6DyIgH9NiUFa_Q,403 +jsonschema_specifications/schemas/draft201909/vocabularies/meta-data,sha256=1H4kRd1qgicaKY2DzGxsuNSuHhXg3Fa-zTehY-zwEoY,892 +jsonschema_specifications/schemas/draft201909/vocabularies/validation,sha256=HlJsHTNac0gF_ILPV5jBK5YK19olF8Zs2lobCTWcPBw,2834 +jsonschema_specifications/schemas/draft202012/metaschema.json,sha256=Qdp29a-3zgYtJI92JGOpL3ykfk4PkFsiS6av7vkd7Q8,2452 +jsonschema_specifications/schemas/draft202012/vocabularies/applicator,sha256=xKbkFHuR_vf-ptwFjLG_k0AvdBS3ZXiosWqvHa1qrO8,1659 +jsonschema_specifications/schemas/draft202012/vocabularies/content,sha256=CDQ3R3ZOSlgUJieTz01lIFenkThjxZUNQyl-jh_axbY,519 +jsonschema_specifications/schemas/draft202012/vocabularies/core,sha256=wtEqjk3RHTNt_IOj9mOqTGnwtJs76wlP_rJbUxb0gD0,1564 +jsonschema_specifications/schemas/draft202012/vocabularies/format-annotation,sha256=q8d1rf79idIjWBcNm_k_Tr0jSVY7u-3WDwK-98gSvMA,448 +jsonschema_specifications/schemas/draft202012/vocabularies/format-assertion,sha256=xSJCuaG7eGsmw-gset1CjDH5yW5XXc6Z5W6l_qptogw,445 +jsonschema_specifications/schemas/draft202012/vocabularies/meta-data,sha256=j3bW4U9Bubku-TO3CM3FFEyLUmhlGtEZGEhfsXVPHHY,892 +jsonschema_specifications/schemas/draft202012/vocabularies/unevaluated,sha256=Lb-8tzmUtnCwl2SSre4f_7RsIWgnhNL1pMpWH54tDLQ,506 +jsonschema_specifications/schemas/draft202012/vocabularies/validation,sha256=cBCjHlQfMtK-ch4t40jfdcmzaHaj7TBId_wKvaHTelg,2834 +jsonschema_specifications/schemas/draft3/metaschema.json,sha256=LPdfZENvtb43Si6qJ6uLfh_WUcm0ba6mxnsC_WTiRYs,2600 +jsonschema_specifications/schemas/draft4/metaschema.json,sha256=4UidC0dV8CeTMCWR0_y48Htok6gqlPJIlfjk7fEbguI,4357 +jsonschema_specifications/schemas/draft6/metaschema.json,sha256=wp386fVINcOgbAOzxdXsDtp3cGVo-cTffPvHVmpRAG0,4437 +jsonschema_specifications/schemas/draft7/metaschema.json,sha256=PVOSCIJhYGxVm2A_OFMpyfGrRbXWZ-uZBodFOwVdQF4,4819 +jsonschema_specifications/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jsonschema_specifications/tests/__pycache__/__init__.cpython-312.pyc,, +jsonschema_specifications/tests/__pycache__/test_jsonschema_specifications.cpython-312.pyc,, +jsonschema_specifications/tests/test_jsonschema_specifications.py,sha256=WkbYRW6A6FoZ0rivShfqVLSCsAiHJ2x8TxqECJTXPTY,1106 diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/WHEEL b/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/WHEEL new file mode 100644 index 0000000..12228d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/licenses/COPYING b/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/licenses/COPYING new file mode 100644 index 0000000..a9f853e --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications-2025.9.1.dist-info/licenses/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2022 Julian Berman + +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. diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/__init__.py b/venv/lib/python3.12/site-packages/jsonschema_specifications/__init__.py new file mode 100644 index 0000000..a423574 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/__init__.py @@ -0,0 +1,12 @@ +""" +The JSON Schema meta-schemas and vocabularies, exposed as a Registry. +""" + +from referencing.jsonschema import EMPTY_REGISTRY as _EMPTY_REGISTRY + +from jsonschema_specifications._core import _schemas + +#: A `referencing.jsonschema.SchemaRegistry` containing all of the official +#: meta-schemas and vocabularies. +REGISTRY = (_schemas() @ _EMPTY_REGISTRY).crawl() +__all__ = ["REGISTRY"] diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema_specifications/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..99e661b Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema_specifications/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/__pycache__/_core.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema_specifications/__pycache__/_core.cpython-312.pyc new file mode 100644 index 0000000..cf99f1b Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema_specifications/__pycache__/_core.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/_core.py b/venv/lib/python3.12/site-packages/jsonschema_specifications/_core.py new file mode 100644 index 0000000..e67bd71 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/_core.py @@ -0,0 +1,38 @@ +""" +Load all the JSON Schema specification's official schemas. +""" + +import json + +try: + from importlib.resources import files +except ImportError: + from importlib_resources import ( # type: ignore[import-not-found, no-redef] + files, + ) + +from referencing import Resource + + +def _schemas(): + """ + All schemas we ship. + """ + # importlib.resources.abc.Traversal doesn't have nice ways to do this that + # I'm aware of... + # + # It can't recurse arbitrarily, e.g. no ``.glob()``. + # + # So this takes some liberties given the real layout of what we ship + # (only 2 levels of nesting, no directories within the second level). + + for version in files(__package__).joinpath("schemas").iterdir(): + if version.name.startswith("."): + continue + for child in version.iterdir(): + children = [child] if child.is_file() else child.iterdir() + for path in children: + if path.name.startswith("."): + continue + contents = json.loads(path.read_text(encoding="utf-8")) + yield Resource.from_contents(contents) diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/metaschema.json b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/metaschema.json new file mode 100644 index 0000000..2248a0c --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/metaschema.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/schema", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/core": true, + "https://json-schema.org/draft/2019-09/vocab/applicator": true, + "https://json-schema.org/draft/2019-09/vocab/validation": true, + "https://json-schema.org/draft/2019-09/vocab/meta-data": true, + "https://json-schema.org/draft/2019-09/vocab/format": false, + "https://json-schema.org/draft/2019-09/vocab/content": true + }, + "$recursiveAnchor": true, + + "title": "Core and Validation specifications meta-schema", + "allOf": [ + {"$ref": "meta/core"}, + {"$ref": "meta/applicator"}, + {"$ref": "meta/validation"}, + {"$ref": "meta/meta-data"}, + {"$ref": "meta/format"}, + {"$ref": "meta/content"} + ], + "type": ["object", "boolean"], + "properties": { + "definitions": { + "$comment": "While no longer an official keyword as it is replaced by $defs, this keyword is retained in the meta-schema to prevent incompatible extensions as it remains in common use.", + "type": "object", + "additionalProperties": { "$recursiveRef": "#" }, + "default": {} + }, + "dependencies": { + "$comment": "\"dependencies\" is no longer a keyword, but schema authors should avoid redefining it to facilitate a smooth transition to \"dependentSchemas\" and \"dependentRequired\"", + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$recursiveRef": "#" }, + { "$ref": "meta/validation#/$defs/stringArray" } + ] + } + } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/applicator b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/applicator new file mode 100644 index 0000000..24a1cc4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/applicator @@ -0,0 +1,56 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/applicator", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/applicator": true + }, + "$recursiveAnchor": true, + + "title": "Applicator vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "additionalItems": { "$recursiveRef": "#" }, + "unevaluatedItems": { "$recursiveRef": "#" }, + "items": { + "anyOf": [ + { "$recursiveRef": "#" }, + { "$ref": "#/$defs/schemaArray" } + ] + }, + "contains": { "$recursiveRef": "#" }, + "additionalProperties": { "$recursiveRef": "#" }, + "unevaluatedProperties": { "$recursiveRef": "#" }, + "properties": { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependentSchemas": { + "type": "object", + "additionalProperties": { + "$recursiveRef": "#" + } + }, + "propertyNames": { "$recursiveRef": "#" }, + "if": { "$recursiveRef": "#" }, + "then": { "$recursiveRef": "#" }, + "else": { "$recursiveRef": "#" }, + "allOf": { "$ref": "#/$defs/schemaArray" }, + "anyOf": { "$ref": "#/$defs/schemaArray" }, + "oneOf": { "$ref": "#/$defs/schemaArray" }, + "not": { "$recursiveRef": "#" } + }, + "$defs": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$recursiveRef": "#" } + } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/content b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/content new file mode 100644 index 0000000..f6752a8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/content @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/content", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/content": true + }, + "$recursiveAnchor": true, + + "title": "Content vocabulary meta-schema", + + "type": ["object", "boolean"], + "properties": { + "contentMediaType": { "type": "string" }, + "contentEncoding": { "type": "string" }, + "contentSchema": { "$recursiveRef": "#" } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/core b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/core new file mode 100644 index 0000000..eb708a5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/core @@ -0,0 +1,57 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/core", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/core": true + }, + "$recursiveAnchor": true, + + "title": "Core vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference", + "$comment": "Non-empty fragments not allowed.", + "pattern": "^[^#]*#?$" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$anchor": { + "type": "string", + "pattern": "^[A-Za-z][-A-Za-z0-9.:_]*$" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$recursiveRef": { + "type": "string", + "format": "uri-reference" + }, + "$recursiveAnchor": { + "type": "boolean", + "default": false + }, + "$vocabulary": { + "type": "object", + "propertyNames": { + "type": "string", + "format": "uri" + }, + "additionalProperties": { + "type": "boolean" + } + }, + "$comment": { + "type": "string" + }, + "$defs": { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" }, + "default": {} + } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/format b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/format new file mode 100644 index 0000000..09bbfdd --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/format @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/format", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/format": true + }, + "$recursiveAnchor": true, + + "title": "Format vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "format": { "type": "string" } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/meta-data b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/meta-data new file mode 100644 index 0000000..da04cff --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/meta-data @@ -0,0 +1,37 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/meta-data", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/meta-data": true + }, + "$recursiveAnchor": true, + + "title": "Meta-data vocabulary meta-schema", + + "type": ["object", "boolean"], + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "deprecated": { + "type": "boolean", + "default": false + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/validation b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/validation new file mode 100644 index 0000000..9f59677 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/validation @@ -0,0 +1,98 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/meta/validation", + "$vocabulary": { + "https://json-schema.org/draft/2019-09/vocab/validation": true + }, + "$recursiveAnchor": true, + + "title": "Validation vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/$defs/nonNegativeInteger" }, + "minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "maxItems": { "$ref": "#/$defs/nonNegativeInteger" }, + "minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxContains": { "$ref": "#/$defs/nonNegativeInteger" }, + "minContains": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 1 + }, + "maxProperties": { "$ref": "#/$defs/nonNegativeInteger" }, + "minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/$defs/stringArray" }, + "dependentRequired": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/stringArray" + } + }, + "const": true, + "enum": { + "type": "array", + "items": true + }, + "type": { + "anyOf": [ + { "$ref": "#/$defs/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/$defs/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + } + }, + "$defs": { + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 0 + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/metaschema.json b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/metaschema.json new file mode 100644 index 0000000..d5e2d31 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/metaschema.json @@ -0,0 +1,58 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/schema", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": true, + "https://json-schema.org/draft/2020-12/vocab/applicator": true, + "https://json-schema.org/draft/2020-12/vocab/unevaluated": true, + "https://json-schema.org/draft/2020-12/vocab/validation": true, + "https://json-schema.org/draft/2020-12/vocab/meta-data": true, + "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, + "https://json-schema.org/draft/2020-12/vocab/content": true + }, + "$dynamicAnchor": "meta", + + "title": "Core and Validation specifications meta-schema", + "allOf": [ + {"$ref": "meta/core"}, + {"$ref": "meta/applicator"}, + {"$ref": "meta/unevaluated"}, + {"$ref": "meta/validation"}, + {"$ref": "meta/meta-data"}, + {"$ref": "meta/format-annotation"}, + {"$ref": "meta/content"} + ], + "type": ["object", "boolean"], + "$comment": "This meta-schema also defines keywords that have appeared in previous drafts in order to prevent incompatible extensions as they remain in common use.", + "properties": { + "definitions": { + "$comment": "\"definitions\" has been replaced by \"$defs\".", + "type": "object", + "additionalProperties": { "$dynamicRef": "#meta" }, + "deprecated": true, + "default": {} + }, + "dependencies": { + "$comment": "\"dependencies\" has been split and replaced by \"dependentSchemas\" and \"dependentRequired\" in order to serve their differing semantics.", + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$dynamicRef": "#meta" }, + { "$ref": "meta/validation#/$defs/stringArray" } + ] + }, + "deprecated": true, + "default": {} + }, + "$recursiveAnchor": { + "$comment": "\"$recursiveAnchor\" has been replaced by \"$dynamicAnchor\".", + "$ref": "meta/core#/$defs/anchorString", + "deprecated": true + }, + "$recursiveRef": { + "$comment": "\"$recursiveRef\" has been replaced by \"$dynamicRef\".", + "$ref": "meta/core#/$defs/uriReferenceString", + "deprecated": true + } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/applicator b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/applicator new file mode 100644 index 0000000..ca69923 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/applicator @@ -0,0 +1,48 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/applicator", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/applicator": true + }, + "$dynamicAnchor": "meta", + + "title": "Applicator vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "prefixItems": { "$ref": "#/$defs/schemaArray" }, + "items": { "$dynamicRef": "#meta" }, + "contains": { "$dynamicRef": "#meta" }, + "additionalProperties": { "$dynamicRef": "#meta" }, + "properties": { + "type": "object", + "additionalProperties": { "$dynamicRef": "#meta" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$dynamicRef": "#meta" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependentSchemas": { + "type": "object", + "additionalProperties": { "$dynamicRef": "#meta" }, + "default": {} + }, + "propertyNames": { "$dynamicRef": "#meta" }, + "if": { "$dynamicRef": "#meta" }, + "then": { "$dynamicRef": "#meta" }, + "else": { "$dynamicRef": "#meta" }, + "allOf": { "$ref": "#/$defs/schemaArray" }, + "anyOf": { "$ref": "#/$defs/schemaArray" }, + "oneOf": { "$ref": "#/$defs/schemaArray" }, + "not": { "$dynamicRef": "#meta" } + }, + "$defs": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$dynamicRef": "#meta" } + } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/content b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/content new file mode 100644 index 0000000..2f6e056 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/content @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/content", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/content": true + }, + "$dynamicAnchor": "meta", + + "title": "Content vocabulary meta-schema", + + "type": ["object", "boolean"], + "properties": { + "contentEncoding": { "type": "string" }, + "contentMediaType": { "type": "string" }, + "contentSchema": { "$dynamicRef": "#meta" } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/core b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/core new file mode 100644 index 0000000..dfc092d --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/core @@ -0,0 +1,51 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/core", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": true + }, + "$dynamicAnchor": "meta", + + "title": "Core vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "$id": { + "$ref": "#/$defs/uriReferenceString", + "$comment": "Non-empty fragments not allowed.", + "pattern": "^[^#]*#?$" + }, + "$schema": { "$ref": "#/$defs/uriString" }, + "$ref": { "$ref": "#/$defs/uriReferenceString" }, + "$anchor": { "$ref": "#/$defs/anchorString" }, + "$dynamicRef": { "$ref": "#/$defs/uriReferenceString" }, + "$dynamicAnchor": { "$ref": "#/$defs/anchorString" }, + "$vocabulary": { + "type": "object", + "propertyNames": { "$ref": "#/$defs/uriString" }, + "additionalProperties": { + "type": "boolean" + } + }, + "$comment": { + "type": "string" + }, + "$defs": { + "type": "object", + "additionalProperties": { "$dynamicRef": "#meta" } + } + }, + "$defs": { + "anchorString": { + "type": "string", + "pattern": "^[A-Za-z_][-A-Za-z0-9._]*$" + }, + "uriString": { + "type": "string", + "format": "uri" + }, + "uriReferenceString": { + "type": "string", + "format": "uri-reference" + } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/format-annotation b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/format-annotation new file mode 100644 index 0000000..51ef7ea --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/format-annotation @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/format-annotation", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/format-annotation": true + }, + "$dynamicAnchor": "meta", + + "title": "Format vocabulary meta-schema for annotation results", + "type": ["object", "boolean"], + "properties": { + "format": { "type": "string" } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/format-assertion b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/format-assertion new file mode 100644 index 0000000..5e73fd7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/format-assertion @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/format-assertion", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/format-assertion": true + }, + "$dynamicAnchor": "meta", + + "title": "Format vocabulary meta-schema for assertion results", + "type": ["object", "boolean"], + "properties": { + "format": { "type": "string" } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/meta-data b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/meta-data new file mode 100644 index 0000000..05cbc22 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/meta-data @@ -0,0 +1,37 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/meta-data", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/meta-data": true + }, + "$dynamicAnchor": "meta", + + "title": "Meta-data vocabulary meta-schema", + + "type": ["object", "boolean"], + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "deprecated": { + "type": "boolean", + "default": false + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/unevaluated b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/unevaluated new file mode 100644 index 0000000..5f62a3f --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/unevaluated @@ -0,0 +1,15 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/unevaluated", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/unevaluated": true + }, + "$dynamicAnchor": "meta", + + "title": "Unevaluated applicator vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "unevaluatedItems": { "$dynamicRef": "#meta" }, + "unevaluatedProperties": { "$dynamicRef": "#meta" } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/validation b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/validation new file mode 100644 index 0000000..606b87b --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/validation @@ -0,0 +1,98 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://json-schema.org/draft/2020-12/meta/validation", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/validation": true + }, + "$dynamicAnchor": "meta", + + "title": "Validation vocabulary meta-schema", + "type": ["object", "boolean"], + "properties": { + "type": { + "anyOf": [ + { "$ref": "#/$defs/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/$defs/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "const": true, + "enum": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/$defs/nonNegativeInteger" }, + "minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "maxItems": { "$ref": "#/$defs/nonNegativeInteger" }, + "minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxContains": { "$ref": "#/$defs/nonNegativeInteger" }, + "minContains": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 1 + }, + "maxProperties": { "$ref": "#/$defs/nonNegativeInteger" }, + "minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/$defs/stringArray" }, + "dependentRequired": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/stringArray" + } + } + }, + "$defs": { + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 0 + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + } +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft3/metaschema.json b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft3/metaschema.json new file mode 100644 index 0000000..8b26b1f --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft3/metaschema.json @@ -0,0 +1,172 @@ +{ + "$schema" : "http://json-schema.org/draft-03/schema#", + "id" : "http://json-schema.org/draft-03/schema#", + "type" : "object", + + "properties" : { + "type" : { + "type" : ["string", "array"], + "items" : { + "type" : ["string", {"$ref" : "#"}] + }, + "uniqueItems" : true, + "default" : "any" + }, + + "properties" : { + "type" : "object", + "additionalProperties" : {"$ref" : "#"}, + "default" : {} + }, + + "patternProperties" : { + "type" : "object", + "additionalProperties" : {"$ref" : "#"}, + "default" : {} + }, + + "additionalProperties" : { + "type" : [{"$ref" : "#"}, "boolean"], + "default" : {} + }, + + "items" : { + "type" : [{"$ref" : "#"}, "array"], + "items" : {"$ref" : "#"}, + "default" : {} + }, + + "additionalItems" : { + "type" : [{"$ref" : "#"}, "boolean"], + "default" : {} + }, + + "required" : { + "type" : "boolean", + "default" : false + }, + + "dependencies" : { + "type" : "object", + "additionalProperties" : { + "type" : ["string", "array", {"$ref" : "#"}], + "items" : { + "type" : "string" + } + }, + "default" : {} + }, + + "minimum" : { + "type" : "number" + }, + + "maximum" : { + "type" : "number" + }, + + "exclusiveMinimum" : { + "type" : "boolean", + "default" : false + }, + + "exclusiveMaximum" : { + "type" : "boolean", + "default" : false + }, + + "minItems" : { + "type" : "integer", + "minimum" : 0, + "default" : 0 + }, + + "maxItems" : { + "type" : "integer", + "minimum" : 0 + }, + + "uniqueItems" : { + "type" : "boolean", + "default" : false + }, + + "pattern" : { + "type" : "string", + "format" : "regex" + }, + + "minLength" : { + "type" : "integer", + "minimum" : 0, + "default" : 0 + }, + + "maxLength" : { + "type" : "integer" + }, + + "enum" : { + "type" : "array", + "minItems" : 1, + "uniqueItems" : true + }, + + "default" : { + "type" : "any" + }, + + "title" : { + "type" : "string" + }, + + "description" : { + "type" : "string" + }, + + "format" : { + "type" : "string" + }, + + "divisibleBy" : { + "type" : "number", + "minimum" : 0, + "exclusiveMinimum" : true, + "default" : 1 + }, + + "disallow" : { + "type" : ["string", "array"], + "items" : { + "type" : ["string", {"$ref" : "#"}] + }, + "uniqueItems" : true + }, + + "extends" : { + "type" : [{"$ref" : "#"}, "array"], + "items" : {"$ref" : "#"}, + "default" : {} + }, + + "id" : { + "type" : "string" + }, + + "$ref" : { + "type" : "string" + }, + + "$schema" : { + "type" : "string", + "format" : "uri" + } + }, + + "dependencies" : { + "exclusiveMinimum" : "minimum", + "exclusiveMaximum" : "maximum" + }, + + "default" : {} +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft4/metaschema.json b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft4/metaschema.json new file mode 100644 index 0000000..bcbb847 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft4/metaschema.json @@ -0,0 +1,149 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "positiveInteger": { + "type": "integer", + "minimum": 0 + }, + "positiveIntegerDefault0": { + "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] + }, + "simpleTypes": { + "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "uniqueItems": true + } + }, + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "$schema": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "multipleOf": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": true + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "boolean", + "default": false + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "boolean", + "default": false + }, + "maxLength": { "$ref": "#/definitions/positiveInteger" }, + "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { + "anyOf": [ + { "type": "boolean" }, + { "$ref": "#" } + ], + "default": {} + }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": {} + }, + "maxItems": { "$ref": "#/definitions/positiveInteger" }, + "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxProperties": { "$ref": "#/definitions/positiveInteger" }, + "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { + "anyOf": [ + { "type": "boolean" }, + { "$ref": "#" } + ], + "default": {} + }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "dependencies": { + "exclusiveMaximum": [ "maximum" ], + "exclusiveMinimum": [ "minimum" ] + }, + "default": {} +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft6/metaschema.json b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft6/metaschema.json new file mode 100644 index 0000000..a0d2bf7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft6/metaschema.json @@ -0,0 +1,153 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "http://json-schema.org/draft-06/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "examples": { + "type": "array", + "items": {} + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "#" }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": {} + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "#" }, + "const": {}, + "enum": { + "type": "array" + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": {} +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft7/metaschema.json b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft7/metaschema.json new file mode 100644 index 0000000..746cde9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/schemas/draft7/metaschema.json @@ -0,0 +1,166 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://json-schema.org/draft-07/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$comment": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "readOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "#" }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": true + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "#" }, + "const": true, + "enum": { + "type": "array", + "items": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "contentMediaType": { "type": "string" }, + "contentEncoding": { "type": "string" }, + "if": {"$ref": "#"}, + "then": {"$ref": "#"}, + "else": {"$ref": "#"}, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": true +} diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/tests/__init__.py b/venv/lib/python3.12/site-packages/jsonschema_specifications/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/tests/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema_specifications/tests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..9f231c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema_specifications/tests/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/tests/__pycache__/test_jsonschema_specifications.cpython-312.pyc b/venv/lib/python3.12/site-packages/jsonschema_specifications/tests/__pycache__/test_jsonschema_specifications.cpython-312.pyc new file mode 100644 index 0000000..3ebd4a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jsonschema_specifications/tests/__pycache__/test_jsonschema_specifications.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jsonschema_specifications/tests/test_jsonschema_specifications.py b/venv/lib/python3.12/site-packages/jsonschema_specifications/tests/test_jsonschema_specifications.py new file mode 100644 index 0000000..fd2927e --- /dev/null +++ b/venv/lib/python3.12/site-packages/jsonschema_specifications/tests/test_jsonschema_specifications.py @@ -0,0 +1,41 @@ +from collections.abc import Mapping +from pathlib import Path + +import pytest + +from jsonschema_specifications import REGISTRY + + +def test_it_contains_metaschemas(): + schema = REGISTRY.contents("http://json-schema.org/draft-07/schema#") + assert isinstance(schema, Mapping) + assert schema["$id"] == "http://json-schema.org/draft-07/schema#" + assert schema["title"] == "Core schema meta-schema" + + +def test_it_is_crawled(): + assert REGISTRY.crawl() == REGISTRY + + +@pytest.mark.parametrize( + "ignored_relative_path", + ["schemas/.DS_Store", "schemas/draft7/.DS_Store"], +) +def test_it_copes_with_dotfiles(ignored_relative_path): + """ + Ignore files like .DS_Store if someone has actually caused one to exist. + + We test here through the private interface as of course the global has + already loaded our schemas. + """ + + import jsonschema_specifications + + package = Path(jsonschema_specifications.__file__).parent + + ignored = package / ignored_relative_path + ignored.touch() + try: + list(jsonschema_specifications._schemas()) + finally: + ignored.unlink() diff --git a/venv/lib/python3.12/site-packages/jwt/__init__.py b/venv/lib/python3.12/site-packages/jwt/__init__.py new file mode 100644 index 0000000..a3c6c60 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jwt/__init__.py @@ -0,0 +1,78 @@ +from .api_jwk import PyJWK, PyJWKSet +from .api_jws import ( + PyJWS, + get_algorithm_by_name, + get_unverified_header, + register_algorithm, + unregister_algorithm, +) +from .api_jwt import PyJWT, decode, decode_complete, encode +from .exceptions import ( + DecodeError, + ExpiredSignatureError, + ImmatureSignatureError, + InvalidAlgorithmError, + InvalidAudienceError, + InvalidIssuedAtError, + InvalidIssuerError, + InvalidKeyError, + InvalidSignatureError, + InvalidTokenError, + MissingRequiredClaimError, + PyJWKClientConnectionError, + PyJWKClientError, + PyJWKError, + PyJWKSetError, + PyJWTError, +) +from .jwks_client import PyJWKClient +from .warnings import InsecureKeyLengthWarning + +__version__ = "2.15.1" + +__title__ = "PyJWT" +__description__ = "JSON Web Token implementation in Python" +__url__ = "https://pyjwt.readthedocs.io" +__uri__ = __url__ +__doc__ = f"{__description__} <{__uri__}>" + +__author__ = "José Padilla" +__email__ = "hello@jpadilla.com" + +__license__ = "MIT" +__copyright__ = "Copyright 2015-2026 José Padilla" + + +__all__ = [ + "PyJWS", + "PyJWT", + "PyJWKClient", + "PyJWK", + "PyJWKSet", + "decode", + "decode_complete", + "encode", + "get_unverified_header", + "register_algorithm", + "unregister_algorithm", + "get_algorithm_by_name", + # Warnings + "InsecureKeyLengthWarning", + # Exceptions + "DecodeError", + "ExpiredSignatureError", + "ImmatureSignatureError", + "InvalidAlgorithmError", + "InvalidAudienceError", + "InvalidIssuedAtError", + "InvalidIssuerError", + "InvalidKeyError", + "InvalidSignatureError", + "InvalidTokenError", + "MissingRequiredClaimError", + "PyJWKClientConnectionError", + "PyJWKClientError", + "PyJWKError", + "PyJWKSetError", + "PyJWTError", +] diff --git a/venv/lib/python3.12/site-packages/jwt/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/jwt/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..540b5cb Binary files /dev/null and b/venv/lib/python3.12/site-packages/jwt/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jwt/__pycache__/algorithms.cpython-312.pyc b/venv/lib/python3.12/site-packages/jwt/__pycache__/algorithms.cpython-312.pyc new file mode 100644 index 0000000..5ac1d63 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jwt/__pycache__/algorithms.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jwt/__pycache__/api_jwk.cpython-312.pyc b/venv/lib/python3.12/site-packages/jwt/__pycache__/api_jwk.cpython-312.pyc new file mode 100644 index 0000000..22f8c6c Binary files /dev/null and b/venv/lib/python3.12/site-packages/jwt/__pycache__/api_jwk.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jwt/__pycache__/api_jws.cpython-312.pyc b/venv/lib/python3.12/site-packages/jwt/__pycache__/api_jws.cpython-312.pyc new file mode 100644 index 0000000..875514c Binary files /dev/null and b/venv/lib/python3.12/site-packages/jwt/__pycache__/api_jws.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jwt/__pycache__/api_jwt.cpython-312.pyc b/venv/lib/python3.12/site-packages/jwt/__pycache__/api_jwt.cpython-312.pyc new file mode 100644 index 0000000..aae14d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jwt/__pycache__/api_jwt.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jwt/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/jwt/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..67a09d6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jwt/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jwt/__pycache__/help.cpython-312.pyc b/venv/lib/python3.12/site-packages/jwt/__pycache__/help.cpython-312.pyc new file mode 100644 index 0000000..f962815 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jwt/__pycache__/help.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jwt/__pycache__/jwk_set_cache.cpython-312.pyc b/venv/lib/python3.12/site-packages/jwt/__pycache__/jwk_set_cache.cpython-312.pyc new file mode 100644 index 0000000..78af7e5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jwt/__pycache__/jwk_set_cache.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jwt/__pycache__/jwks_client.cpython-312.pyc b/venv/lib/python3.12/site-packages/jwt/__pycache__/jwks_client.cpython-312.pyc new file mode 100644 index 0000000..2905013 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jwt/__pycache__/jwks_client.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jwt/__pycache__/types.cpython-312.pyc b/venv/lib/python3.12/site-packages/jwt/__pycache__/types.cpython-312.pyc new file mode 100644 index 0000000..48ee29d Binary files /dev/null and b/venv/lib/python3.12/site-packages/jwt/__pycache__/types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jwt/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/jwt/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..dd9f94a Binary files /dev/null and b/venv/lib/python3.12/site-packages/jwt/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jwt/__pycache__/warnings.cpython-312.pyc b/venv/lib/python3.12/site-packages/jwt/__pycache__/warnings.cpython-312.pyc new file mode 100644 index 0000000..ed8edb2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jwt/__pycache__/warnings.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jwt/algorithms.py b/venv/lib/python3.12/site-packages/jwt/algorithms.py new file mode 100644 index 0000000..75fdd07 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jwt/algorithms.py @@ -0,0 +1,1121 @@ +from __future__ import annotations + +import hashlib +import hmac +import json +import os +import sys +from abc import ABC, abstractmethod +from typing import ( + TYPE_CHECKING, + Any, + ClassVar, + Literal, + NoReturn, + Union, + cast, + get_args, + overload, +) + +from .exceptions import InvalidKeyError +from .types import HashlibHash, JWKDict +from .utils import ( + base64url_decode, + base64url_encode, + der_to_raw_signature, + force_bytes, + from_base64url_uint, + is_pem_format, + is_ssh_key, + raw_to_der_signature, + to_base64url_uint, +) + +try: + from cryptography import x509 + from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes + from cryptography.hazmat.primitives.asymmetric import padding + from cryptography.hazmat.primitives.asymmetric.ec import ( + ECDSA, + SECP256K1, + SECP256R1, + SECP384R1, + SECP521R1, + EllipticCurve, + EllipticCurvePrivateKey, + EllipticCurvePrivateNumbers, + EllipticCurvePublicKey, + EllipticCurvePublicNumbers, + ) + from cryptography.hazmat.primitives.asymmetric.ed448 import ( + Ed448PrivateKey, + Ed448PublicKey, + ) + from cryptography.hazmat.primitives.asymmetric.ed25519 import ( + Ed25519PrivateKey, + Ed25519PublicKey, + ) + from cryptography.hazmat.primitives.asymmetric.rsa import ( + RSAPrivateKey, + RSAPrivateNumbers, + RSAPublicKey, + RSAPublicNumbers, + rsa_crt_dmp1, + rsa_crt_dmq1, + rsa_crt_iqmp, + rsa_recover_prime_factors, + ) + from cryptography.hazmat.primitives.serialization import ( + Encoding, + NoEncryption, + PrivateFormat, + PublicFormat, + load_der_public_key, + load_pem_private_key, + load_pem_public_key, + load_ssh_public_key, + ) + + if sys.version_info >= (3, 10): + from typing import TypeAlias + else: + # Python 3.9 and lower + from typing_extensions import TypeAlias + + # Type aliases for convenience in algorithms method signatures + AllowedRSAKeys: TypeAlias = Union[RSAPrivateKey, RSAPublicKey] + AllowedECKeys: TypeAlias = Union[EllipticCurvePrivateKey, EllipticCurvePublicKey] + AllowedOKPKeys: TypeAlias = Union[ + Ed25519PrivateKey, Ed25519PublicKey, Ed448PrivateKey, Ed448PublicKey + ] + AllowedKeys: TypeAlias = Union[AllowedRSAKeys, AllowedECKeys, AllowedOKPKeys] + #: Type alias for allowed ``cryptography`` private keys (requires ``cryptography`` to be installed) + AllowedPrivateKeys: TypeAlias = Union[ + RSAPrivateKey, EllipticCurvePrivateKey, Ed25519PrivateKey, Ed448PrivateKey + ] + #: Type alias for allowed ``cryptography`` public keys (requires ``cryptography`` to be installed) + AllowedPublicKeys: TypeAlias = Union[ + RSAPublicKey, EllipticCurvePublicKey, Ed25519PublicKey, Ed448PublicKey + ] + + if TYPE_CHECKING or bool(os.getenv("SPHINX_BUILD", "")): + from cryptography.hazmat.primitives.asymmetric.types import ( + PrivateKeyTypes, + PublicKeyTypes, + ) + + has_crypto = True +except ModuleNotFoundError: + if sys.version_info >= (3, 11): + from typing import Never + else: + from typing_extensions import Never + + AllowedRSAKeys = Never # type: ignore[misc] + AllowedECKeys = Never # type: ignore[misc] + AllowedOKPKeys = Never # type: ignore[misc] + AllowedKeys = Never # type: ignore[misc] + AllowedPrivateKeys = Never # type: ignore[misc] + AllowedPublicKeys = Never # type: ignore[misc] + has_crypto = False + + +requires_cryptography = { + "RS256", + "RS384", + "RS512", + "ES256", + "ES256K", + "ES384", + "ES521", + "ES512", + "PS256", + "PS384", + "PS512", + "EdDSA", +} + + +def get_default_algorithms() -> dict[str, Algorithm]: + """ + Returns the algorithms that are implemented by the library. + """ + default_algorithms: dict[str, Algorithm] = { + "none": NoneAlgorithm(), + "HS256": HMACAlgorithm(HMACAlgorithm.SHA256), + "HS384": HMACAlgorithm(HMACAlgorithm.SHA384), + "HS512": HMACAlgorithm(HMACAlgorithm.SHA512), + } + + if has_crypto: + default_algorithms.update( + { + "RS256": RSAAlgorithm(RSAAlgorithm.SHA256), + "RS384": RSAAlgorithm(RSAAlgorithm.SHA384), + "RS512": RSAAlgorithm(RSAAlgorithm.SHA512), + "ES256": ECAlgorithm(ECAlgorithm.SHA256, SECP256R1), + "ES256K": ECAlgorithm(ECAlgorithm.SHA256, SECP256K1), + "ES384": ECAlgorithm(ECAlgorithm.SHA384, SECP384R1), + "ES521": ECAlgorithm(ECAlgorithm.SHA512, SECP521R1), + "ES512": ECAlgorithm( + ECAlgorithm.SHA512, SECP521R1 + ), # Backward compat for #219 fix + "PS256": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA256), + "PS384": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA384), + "PS512": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA512), + "EdDSA": OKPAlgorithm(), + } + ) + + return default_algorithms + + +class Algorithm(ABC): + """ + The interface for an algorithm used to sign and verify tokens. + """ + + # pyjwt-964: Validate to ensure the key passed in was decoded to the correct cryptography key family + _crypto_key_types: tuple[type[AllowedKeys], ...] | None = None + + def compute_hash_digest(self, bytestr: bytes) -> bytes: + """ + Compute a hash digest using the specified algorithm's hash algorithm. + + If there is no hash algorithm, raises a NotImplementedError. + """ + # lookup self.hash_alg if defined in a way that mypy can understand + hash_alg = getattr(self, "hash_alg", None) + if hash_alg is None: + raise NotImplementedError + + if ( + has_crypto + and isinstance(hash_alg, type) + and issubclass(hash_alg, hashes.HashAlgorithm) + ): + digest = hashes.Hash(hash_alg(), backend=default_backend()) + digest.update(bytestr) + return bytes(digest.finalize()) + else: + return bytes(hash_alg(bytestr).digest()) + + def check_crypto_key_type(self, key: PublicKeyTypes | PrivateKeyTypes) -> None: + """Check that the key belongs to the right cryptographic family. + + Note that this method only works when ``cryptography`` is installed. + + :param key: Potentially a cryptography key + :type key: :py:data:`PublicKeyTypes ` | :py:data:`PrivateKeyTypes ` + :raises ValueError: if ``cryptography`` is not installed, or this method is called by a non-cryptography algorithm + :raises InvalidKeyError: if the key doesn't match the expected key classes + """ + if not has_crypto or self._crypto_key_types is None: + raise ValueError( + "This method requires the cryptography library, and should only be used by cryptography-based algorithms." + ) + + if not isinstance(key, self._crypto_key_types): + valid_classes = (cls.__name__ for cls in self._crypto_key_types) + actual_class = key.__class__.__name__ + self_class = self.__class__.__name__ + raise InvalidKeyError( + f"Expected one of {valid_classes}, got: {actual_class}. Invalid Key type for {self_class}" + ) + + @abstractmethod + def prepare_key(self, key: Any) -> Any: + """ + Performs necessary validation and conversions on the key and returns + the key value in the proper format for sign() and verify(). + """ + + @abstractmethod + def sign(self, msg: bytes, key: Any) -> bytes: + """ + Returns a digital signature for the specified message + using the specified key value. + """ + + @abstractmethod + def verify(self, msg: bytes, key: Any, sig: bytes) -> bool: + """ + Verifies that the specified digital signature is valid + for the specified message and key values. + """ + + @overload + @staticmethod + @abstractmethod + def to_jwk(key_obj: Any, as_dict: Literal[True]) -> JWKDict: ... # pragma: no cover + + @overload + @staticmethod + @abstractmethod + def to_jwk( + key_obj: Any, as_dict: Literal[False] = False + ) -> str: ... # pragma: no cover + + @staticmethod + @abstractmethod + def to_jwk(key_obj: Any, as_dict: bool = False) -> JWKDict | str: + """ + Serializes a given key into a JWK + """ + + @staticmethod + @abstractmethod + def from_jwk(jwk: str | JWKDict) -> Any: + """ + Deserializes a given key from JWK back into a key object + """ + + def check_key_length(self, key: Any) -> str | None: + """ + Return a warning message if the key is below the minimum + recommended length for this algorithm, or None if adequate. + """ + return None + + +class NoneAlgorithm(Algorithm): + """ + Placeholder for use when no signing or verification + operations are required. + """ + + def prepare_key(self, key: str | None) -> None: + if key == "": + key = None + + if key is not None: + raise InvalidKeyError('When alg = "none", key value must be None.') + + return key + + def sign(self, msg: bytes, key: None) -> bytes: + return b"" + + def verify(self, msg: bytes, key: None, sig: bytes) -> bool: + return False + + @staticmethod + def to_jwk(key_obj: Any, as_dict: bool = False) -> NoReturn: + raise NotImplementedError() + + @staticmethod + def from_jwk(jwk: str | JWKDict) -> NoReturn: + raise NotImplementedError() + + +class HMACAlgorithm(Algorithm): + """ + Performs signing and verification operations using HMAC + and the specified hash function. + """ + + SHA256: ClassVar[HashlibHash] = hashlib.sha256 + SHA384: ClassVar[HashlibHash] = hashlib.sha384 + SHA512: ClassVar[HashlibHash] = hashlib.sha512 + + def __init__(self, hash_alg: HashlibHash) -> None: + self.hash_alg = hash_alg + + @staticmethod + def _is_der_key(key_bytes: bytes) -> bool: + if not has_crypto: + return False + + try: + load_der_public_key(key_bytes) + except (TypeError, ValueError, UnsupportedAlgorithm): + pass + else: + return True + + try: + x509.load_der_x509_certificate(key_bytes) + except (TypeError, ValueError, UnsupportedAlgorithm): + return False + else: + return True + + def prepare_key(self, key: str | bytes) -> bytes: + key_bytes = force_bytes(key) + + if len(key_bytes) == 0: + raise InvalidKeyError("HMAC key must not be empty.") + + if ( + is_pem_format(key_bytes) + or is_ssh_key(key_bytes) + or self._is_der_key(key_bytes) + ): + raise InvalidKeyError( + "The specified key is an asymmetric key or x509 certificate and" + " should not be used as an HMAC secret." + ) + + # Defense against algorithm-confusion attacks: an attacker with + # control over the token header can force this code path by setting + # alg=HS*, and HMACAlgorithm is the only algorithm that accepts + # arbitrary bytes as a valid secret. Other algorithms reject + # non-key-shaped input naturally. Even a symmetric (kty=oct) JWK + # should be loaded via PyJWK / from_jwk rather than fed as raw JSON + # bytes (whose contents are not the secret material). + try: + jwk_obj = json.loads(key_bytes, parse_int=lambda _: 0) + except RecursionError: + try: + decoded_key = key_bytes.decode( + json.detect_encoding(key_bytes), errors="surrogatepass" + ) + except UnicodeError: + decoded_key = "" + stripped_key = decoded_key.lstrip("\ufeff \t\r\n") + has_jwk_member = False + index = 0 + while index < len(decoded_key): + if decoded_key[index] != '"': + index += 1 + continue + end = index + 1 + while end < len(decoded_key): + if decoded_key[end] == "\\": + end += 2 + elif decoded_key[end] == '"': + break + else: + end += 1 + if end >= len(decoded_key): + break + next_index = end + 1 + while ( + next_index < len(decoded_key) + and decoded_key[next_index] in " \t\r\n" + ): + next_index += 1 + if next_index < len(decoded_key) and decoded_key[next_index] == ":": + try: + has_jwk_member = ( + json.loads(decoded_key[index : end + 1]) == "kty" + ) + except ValueError: + pass + if has_jwk_member: + break + index = end + 1 + if stripped_key.startswith("{") or ( + stripped_key.startswith("[") and has_jwk_member + ): + raise InvalidKeyError( + "The specified key looks like a JWK and should not be " + "used directly as an HMAC secret. Load it via " + "PyJWK / HMACAlgorithm.from_jwk first." + ) from None + jwk_obj = None + except ValueError: + jwk_obj = None + contains_jwk_member = False + objects_to_check = [jwk_obj] + while objects_to_check: + obj = objects_to_check.pop() + if isinstance(obj, dict): + if "kty" in obj: + contains_jwk_member = True + break + objects_to_check.extend(obj.values()) + elif isinstance(obj, list): + objects_to_check.extend(obj) + if contains_jwk_member: + raise InvalidKeyError( + "The specified key looks like a JWK and should not be " + "used directly as an HMAC secret. Load it via " + "PyJWK / HMACAlgorithm.from_jwk first." + ) + + return key_bytes + + @overload + @staticmethod + def to_jwk(key_obj: str | bytes, as_dict: Literal[True]) -> JWKDict: ... + + @overload + @staticmethod + def to_jwk(key_obj: str | bytes, as_dict: Literal[False] = False) -> str: ... + + @staticmethod + def to_jwk(key_obj: str | bytes, as_dict: bool = False) -> JWKDict | str: + jwk = { + "k": base64url_encode(force_bytes(key_obj)).decode(), + "kty": "oct", + } + + if as_dict: + return jwk + else: + return json.dumps(jwk) + + @staticmethod + def from_jwk(jwk: str | JWKDict) -> bytes: + try: + if isinstance(jwk, str): + obj: JWKDict = json.loads(jwk) + elif isinstance(jwk, dict): + obj = jwk + else: + raise ValueError + except ValueError: + raise InvalidKeyError("Key is not valid JSON") from None + + if obj.get("kty") != "oct": + raise InvalidKeyError("Not an HMAC key") + + key_bytes = base64url_decode(obj["k"]) + if len(key_bytes) == 0: + raise InvalidKeyError("HMAC key must not be empty.") + return key_bytes + + def check_key_length(self, key: bytes) -> str | None: + min_length = self.hash_alg().digest_size + if len(key) < min_length: + return ( + f"The HMAC key is {len(key)} bytes long, which is below " + f"the minimum recommended length of {min_length} bytes for " + f"{self.hash_alg().name.upper()}. " + f"See RFC 7518 Section 3.2." + ) + return None + + def sign(self, msg: bytes, key: bytes) -> bytes: + return hmac.new(key, msg, self.hash_alg).digest() + + def verify(self, msg: bytes, key: bytes, sig: bytes) -> bool: + return hmac.compare_digest(sig, self.sign(msg, key)) + + +if has_crypto: + + class RSAAlgorithm(Algorithm): + """ + Performs signing and verification operations using + RSASSA-PKCS-v1_5 and the specified hash function. + """ + + SHA256: ClassVar[type[hashes.HashAlgorithm]] = hashes.SHA256 + SHA384: ClassVar[type[hashes.HashAlgorithm]] = hashes.SHA384 + SHA512: ClassVar[type[hashes.HashAlgorithm]] = hashes.SHA512 + + _crypto_key_types = cast( + tuple[type[AllowedKeys], ...], + get_args(Union[RSAPrivateKey, RSAPublicKey]), + ) + _MIN_KEY_SIZE: ClassVar[int] = 2048 + + def __init__(self, hash_alg: type[hashes.HashAlgorithm]) -> None: + self.hash_alg = hash_alg + + def check_key_length(self, key: AllowedRSAKeys) -> str | None: + if key.key_size < self._MIN_KEY_SIZE: + return ( + f"The RSA key is {key.key_size} bits long, which is below " + f"the minimum recommended size of {self._MIN_KEY_SIZE} bits. " + f"See NIST SP 800-131A." + ) + return None + + def prepare_key(self, key: AllowedRSAKeys | str | bytes) -> AllowedRSAKeys: + if isinstance(key, self._crypto_key_types): + # Cast is required for type narrowing on Python 3.9's mypy + # but redundant on newer mypy versions; suppress both + # diagnostics so the line works across all supported envs. + return cast(AllowedRSAKeys, key) # type: ignore[redundant-cast,unused-ignore] + + if not isinstance(key, (bytes, str)): + raise TypeError("Expecting a PEM-formatted key.") + + key_bytes = force_bytes(key) + + try: + if key_bytes.startswith(b"ssh-rsa"): + public_key: PublicKeyTypes = load_ssh_public_key(key_bytes) + self.check_crypto_key_type(public_key) + return cast(RSAPublicKey, public_key) + else: + private_key: PrivateKeyTypes = load_pem_private_key( + key_bytes, password=None + ) + self.check_crypto_key_type(private_key) + return cast(RSAPrivateKey, private_key) + except ValueError: + try: + public_key = load_pem_public_key(key_bytes) + self.check_crypto_key_type(public_key) + return cast(RSAPublicKey, public_key) + except (ValueError, UnsupportedAlgorithm): + raise InvalidKeyError( + "Could not parse the provided public key." + ) from None + + @overload + @staticmethod + def to_jwk(key_obj: AllowedRSAKeys, as_dict: Literal[True]) -> JWKDict: ... + + @overload + @staticmethod + def to_jwk(key_obj: AllowedRSAKeys, as_dict: Literal[False] = False) -> str: ... + + @staticmethod + def to_jwk(key_obj: AllowedRSAKeys, as_dict: bool = False) -> JWKDict | str: + obj: dict[str, Any] | None = None + + if hasattr(key_obj, "private_numbers"): + # Private key + numbers = key_obj.private_numbers() + + obj = { + "kty": "RSA", + "key_ops": ["sign"], + "n": to_base64url_uint(numbers.public_numbers.n).decode(), + "e": to_base64url_uint(numbers.public_numbers.e).decode(), + "d": to_base64url_uint(numbers.d).decode(), + "p": to_base64url_uint(numbers.p).decode(), + "q": to_base64url_uint(numbers.q).decode(), + "dp": to_base64url_uint(numbers.dmp1).decode(), + "dq": to_base64url_uint(numbers.dmq1).decode(), + "qi": to_base64url_uint(numbers.iqmp).decode(), + } + + elif hasattr(key_obj, "verify"): + # Public key + numbers = key_obj.public_numbers() + + obj = { + "kty": "RSA", + "key_ops": ["verify"], + "n": to_base64url_uint(numbers.n).decode(), + "e": to_base64url_uint(numbers.e).decode(), + } + else: + raise InvalidKeyError("Not a public or private key") + + if as_dict: + return obj + else: + return json.dumps(obj) + + @staticmethod + def from_jwk(jwk: str | JWKDict) -> AllowedRSAKeys: + try: + if isinstance(jwk, str): + obj = json.loads(jwk) + elif isinstance(jwk, dict): + obj = jwk + else: + raise ValueError + except ValueError: + raise InvalidKeyError("Key is not valid JSON") from None + + if obj.get("kty") != "RSA": + raise InvalidKeyError("Not an RSA key") from None + + if "d" in obj and "e" in obj and "n" in obj: + # Private key + if "oth" in obj: + raise InvalidKeyError( + "Unsupported RSA private key: > 2 primes not supported" + ) + + other_props = ["p", "q", "dp", "dq", "qi"] + props_found = [prop in obj for prop in other_props] + any_props_found = any(props_found) + + if any_props_found and not all(props_found): + raise InvalidKeyError( + "RSA key must include all parameters if any are present besides d" + ) from None + + public_numbers = RSAPublicNumbers( + from_base64url_uint(obj["e"]), + from_base64url_uint(obj["n"]), + ) + + if any_props_found: + numbers = RSAPrivateNumbers( + d=from_base64url_uint(obj["d"]), + p=from_base64url_uint(obj["p"]), + q=from_base64url_uint(obj["q"]), + dmp1=from_base64url_uint(obj["dp"]), + dmq1=from_base64url_uint(obj["dq"]), + iqmp=from_base64url_uint(obj["qi"]), + public_numbers=public_numbers, + ) + else: + d = from_base64url_uint(obj["d"]) + p, q = rsa_recover_prime_factors( + public_numbers.n, d, public_numbers.e + ) + + numbers = RSAPrivateNumbers( + d=d, + p=p, + q=q, + dmp1=rsa_crt_dmp1(d, p), + dmq1=rsa_crt_dmq1(d, q), + iqmp=rsa_crt_iqmp(p, q), + public_numbers=public_numbers, + ) + + return numbers.private_key() + elif "n" in obj and "e" in obj: + # Public key + return RSAPublicNumbers( + from_base64url_uint(obj["e"]), + from_base64url_uint(obj["n"]), + ).public_key() + else: + raise InvalidKeyError("Not a public or private key") + + def sign(self, msg: bytes, key: RSAPrivateKey) -> bytes: + signature: bytes = key.sign(msg, padding.PKCS1v15(), self.hash_alg()) + return signature + + def verify(self, msg: bytes, key: RSAPublicKey, sig: bytes) -> bool: + try: + key.verify(sig, msg, padding.PKCS1v15(), self.hash_alg()) + return True + except InvalidSignature: + return False + + class ECAlgorithm(Algorithm): + """ + Performs signing and verification operations using + ECDSA and the specified hash function + """ + + SHA256: ClassVar[type[hashes.HashAlgorithm]] = hashes.SHA256 + SHA384: ClassVar[type[hashes.HashAlgorithm]] = hashes.SHA384 + SHA512: ClassVar[type[hashes.HashAlgorithm]] = hashes.SHA512 + + _crypto_key_types = cast( + tuple[type[AllowedKeys], ...], + get_args(Union[EllipticCurvePrivateKey, EllipticCurvePublicKey]), + ) + + def __init__( + self, + hash_alg: type[hashes.HashAlgorithm], + expected_curve: type[EllipticCurve] | None = None, + ) -> None: + self.hash_alg = hash_alg + self.expected_curve = expected_curve + + def _validate_curve(self, key: AllowedECKeys) -> None: + """Validate that the key's curve matches the expected curve.""" + if self.expected_curve is None: + return + + if not isinstance(key.curve, self.expected_curve): + raise InvalidKeyError( + f"The key's curve '{key.curve.name}' does not match the expected " + f"curve '{self.expected_curve.name}' for this algorithm" + ) + + def prepare_key(self, key: AllowedECKeys | str | bytes) -> AllowedECKeys: + if isinstance(key, self._crypto_key_types): + # See note in RSAAlgorithm.prepare_key. + ec_key = cast(AllowedECKeys, key) # type: ignore[redundant-cast,unused-ignore] + self._validate_curve(ec_key) + return ec_key + + if not isinstance(key, (bytes, str)): + raise TypeError("Expecting a PEM-formatted key.") + + key_bytes = force_bytes(key) + + # Attempt to load key. We don't know if it's + # a Signing Key or a Verifying Key, so we try + # the Verifying Key first. + try: + if key_bytes.startswith(b"ecdsa-sha2-"): + public_key: PublicKeyTypes = load_ssh_public_key(key_bytes) + else: + public_key = load_pem_public_key(key_bytes) + + # Explicit check the key to prevent confusing errors from cryptography + self.check_crypto_key_type(public_key) + ec_public_key = cast(EllipticCurvePublicKey, public_key) + self._validate_curve(ec_public_key) + return ec_public_key + except ValueError: + private_key = load_pem_private_key(key_bytes, password=None) + self.check_crypto_key_type(private_key) + ec_private_key = cast(EllipticCurvePrivateKey, private_key) + self._validate_curve(ec_private_key) + return ec_private_key + + def sign(self, msg: bytes, key: EllipticCurvePrivateKey) -> bytes: + der_sig = key.sign(msg, ECDSA(self.hash_alg())) + + return der_to_raw_signature(der_sig, key.curve) + + def verify(self, msg: bytes, key: AllowedECKeys, sig: bytes) -> bool: + try: + der_sig = raw_to_der_signature(sig, key.curve) + except ValueError: + return False + + try: + public_key = ( + key.public_key() + if isinstance(key, EllipticCurvePrivateKey) + else key + ) + public_key.verify(der_sig, msg, ECDSA(self.hash_alg())) + return True + except InvalidSignature: + return False + + @overload + @staticmethod + def to_jwk(key_obj: AllowedECKeys, as_dict: Literal[True]) -> JWKDict: ... + + @overload + @staticmethod + def to_jwk(key_obj: AllowedECKeys, as_dict: Literal[False] = False) -> str: ... + + @staticmethod + def to_jwk(key_obj: AllowedECKeys, as_dict: bool = False) -> JWKDict | str: + if isinstance(key_obj, EllipticCurvePrivateKey): + public_numbers = key_obj.public_key().public_numbers() + elif isinstance(key_obj, EllipticCurvePublicKey): + public_numbers = key_obj.public_numbers() + else: + raise InvalidKeyError("Not a public or private key") + + if isinstance(key_obj.curve, SECP256R1): + crv = "P-256" + elif isinstance(key_obj.curve, SECP384R1): + crv = "P-384" + elif isinstance(key_obj.curve, SECP521R1): + crv = "P-521" + elif isinstance(key_obj.curve, SECP256K1): + crv = "secp256k1" + else: + raise InvalidKeyError(f"Invalid curve: {key_obj.curve}") + + obj: dict[str, Any] = { + "kty": "EC", + "crv": crv, + "x": to_base64url_uint( + public_numbers.x, + bit_length=key_obj.curve.key_size, + ).decode(), + "y": to_base64url_uint( + public_numbers.y, + bit_length=key_obj.curve.key_size, + ).decode(), + } + + if isinstance(key_obj, EllipticCurvePrivateKey): + obj["d"] = to_base64url_uint( + key_obj.private_numbers().private_value, + bit_length=key_obj.curve.key_size, + ).decode() + + if as_dict: + return obj + else: + return json.dumps(obj) + + @staticmethod + def from_jwk(jwk: str | JWKDict) -> AllowedECKeys: + try: + if isinstance(jwk, str): + obj = json.loads(jwk) + elif isinstance(jwk, dict): + obj = jwk + else: + raise ValueError + except ValueError: + raise InvalidKeyError("Key is not valid JSON") from None + + if obj.get("kty") != "EC": + raise InvalidKeyError("Not an Elliptic curve key") from None + + if "x" not in obj or "y" not in obj: + raise InvalidKeyError("Not an Elliptic curve key") from None + + x = base64url_decode(obj.get("x")) + y = base64url_decode(obj.get("y")) + + curve = obj.get("crv") + curve_obj: EllipticCurve + + if curve == "P-256": + if len(x) == len(y) == 32: + curve_obj = SECP256R1() + else: + raise InvalidKeyError( + "Coords should be 32 bytes for curve P-256" + ) from None + elif curve == "P-384": + if len(x) == len(y) == 48: + curve_obj = SECP384R1() + else: + raise InvalidKeyError( + "Coords should be 48 bytes for curve P-384" + ) from None + elif curve == "P-521": + if len(x) == len(y) == 66: + curve_obj = SECP521R1() + else: + raise InvalidKeyError( + "Coords should be 66 bytes for curve P-521" + ) from None + elif curve == "secp256k1": + if len(x) == len(y) == 32: + curve_obj = SECP256K1() + else: + raise InvalidKeyError( + "Coords should be 32 bytes for curve secp256k1" + ) + else: + raise InvalidKeyError(f"Invalid curve: {curve}") + + public_numbers = EllipticCurvePublicNumbers( + x=int.from_bytes(x, byteorder="big"), + y=int.from_bytes(y, byteorder="big"), + curve=curve_obj, + ) + + if "d" not in obj: + return public_numbers.public_key() + + d = base64url_decode(obj.get("d")) + if len(d) != len(x): + raise InvalidKeyError( + "D should be {} bytes for curve {}", len(x), curve + ) + + return EllipticCurvePrivateNumbers( + int.from_bytes(d, byteorder="big"), public_numbers + ).private_key() + + class RSAPSSAlgorithm(RSAAlgorithm): + """ + Performs a signature using RSASSA-PSS with MGF1 + """ + + def sign(self, msg: bytes, key: RSAPrivateKey) -> bytes: + signature: bytes = key.sign( + msg, + padding.PSS( + mgf=padding.MGF1(self.hash_alg()), + salt_length=self.hash_alg().digest_size, + ), + self.hash_alg(), + ) + return signature + + def verify(self, msg: bytes, key: RSAPublicKey, sig: bytes) -> bool: + try: + key.verify( + sig, + msg, + padding.PSS( + mgf=padding.MGF1(self.hash_alg()), + salt_length=self.hash_alg().digest_size, + ), + self.hash_alg(), + ) + return True + except InvalidSignature: + return False + + class OKPAlgorithm(Algorithm): + """ + Performs signing and verification operations using EdDSA + + This class requires ``cryptography>=2.6`` to be installed. + """ + + _crypto_key_types = cast( + tuple[type[AllowedKeys], ...], + get_args( + Union[ + Ed25519PrivateKey, + Ed25519PublicKey, + Ed448PrivateKey, + Ed448PublicKey, + ] + ), + ) + + def __init__(self, **kwargs: Any) -> None: + pass + + def prepare_key(self, key: AllowedOKPKeys | str | bytes) -> AllowedOKPKeys: + if not isinstance(key, (str, bytes)): + self.check_crypto_key_type(key) + return key + + key_str = key.decode("utf-8") if isinstance(key, bytes) else key + key_bytes = key.encode("utf-8") if isinstance(key, str) else key + + loaded_key: PublicKeyTypes | PrivateKeyTypes + if "-----BEGIN PUBLIC" in key_str: + loaded_key = load_pem_public_key(key_bytes) + elif "-----BEGIN PRIVATE" in key_str: + loaded_key = load_pem_private_key(key_bytes, password=None) + elif key_str[0:4] == "ssh-": + loaded_key = load_ssh_public_key(key_bytes) + else: + raise InvalidKeyError("Not a public or private key") + + # Explicit check the key to prevent confusing errors from cryptography + self.check_crypto_key_type(loaded_key) + return cast("AllowedOKPKeys", loaded_key) + + def sign( + self, msg: str | bytes, key: Ed25519PrivateKey | Ed448PrivateKey + ) -> bytes: + """ + Sign a message ``msg`` using the EdDSA private key ``key`` + :param str|bytes msg: Message to sign + :param Ed25519PrivateKey}Ed448PrivateKey key: A :class:`.Ed25519PrivateKey` + or :class:`.Ed448PrivateKey` isinstance + :return bytes signature: The signature, as bytes + """ + msg_bytes = msg.encode("utf-8") if isinstance(msg, str) else msg + signature: bytes = key.sign(msg_bytes) + return signature + + def verify( + self, msg: str | bytes, key: AllowedOKPKeys, sig: str | bytes + ) -> bool: + """ + Verify a given ``msg`` against a signature ``sig`` using the EdDSA key ``key`` + + :param str|bytes sig: EdDSA signature to check ``msg`` against + :param str|bytes msg: Message to sign + :param Ed25519PrivateKey|Ed25519PublicKey|Ed448PrivateKey|Ed448PublicKey key: + A private or public EdDSA key instance + :return bool verified: True if signature is valid, False if not. + """ + try: + msg_bytes = msg.encode("utf-8") if isinstance(msg, str) else msg + sig_bytes = sig.encode("utf-8") if isinstance(sig, str) else sig + + public_key = ( + key.public_key() + if isinstance(key, (Ed25519PrivateKey, Ed448PrivateKey)) + else key + ) + public_key.verify(sig_bytes, msg_bytes) + return True # If no exception was raised, the signature is valid. + except InvalidSignature: + return False + + @overload + @staticmethod + def to_jwk(key: AllowedOKPKeys, as_dict: Literal[True]) -> JWKDict: ... + + @overload + @staticmethod + def to_jwk(key: AllowedOKPKeys, as_dict: Literal[False] = False) -> str: ... + + @staticmethod + def to_jwk(key: AllowedOKPKeys, as_dict: bool = False) -> JWKDict | str: + if isinstance(key, (Ed25519PublicKey, Ed448PublicKey)): + x = key.public_bytes( + encoding=Encoding.Raw, + format=PublicFormat.Raw, + ) + crv = "Ed25519" if isinstance(key, Ed25519PublicKey) else "Ed448" + + obj = { + "x": base64url_encode(force_bytes(x)).decode(), + "kty": "OKP", + "crv": crv, + } + + if as_dict: + return obj + else: + return json.dumps(obj) + + if isinstance(key, (Ed25519PrivateKey, Ed448PrivateKey)): + d = key.private_bytes( + encoding=Encoding.Raw, + format=PrivateFormat.Raw, + encryption_algorithm=NoEncryption(), + ) + + x = key.public_key().public_bytes( + encoding=Encoding.Raw, + format=PublicFormat.Raw, + ) + + crv = "Ed25519" if isinstance(key, Ed25519PrivateKey) else "Ed448" + obj = { + "x": base64url_encode(force_bytes(x)).decode(), + "d": base64url_encode(force_bytes(d)).decode(), + "kty": "OKP", + "crv": crv, + } + + if as_dict: + return obj + else: + return json.dumps(obj) + + raise InvalidKeyError("Not a public or private key") + + @staticmethod + def from_jwk(jwk: str | JWKDict) -> AllowedOKPKeys: + try: + if isinstance(jwk, str): + obj = json.loads(jwk) + elif isinstance(jwk, dict): + obj = jwk + else: + raise ValueError + except ValueError: + raise InvalidKeyError("Key is not valid JSON") from None + + if obj.get("kty") != "OKP": + raise InvalidKeyError("Not an Octet Key Pair") + + curve = obj.get("crv") + if curve != "Ed25519" and curve != "Ed448": + raise InvalidKeyError(f"Invalid curve: {curve}") + + if "x" not in obj: + raise InvalidKeyError('OKP should have "x" parameter') + x = base64url_decode(obj.get("x")) + + try: + if "d" not in obj: + if curve == "Ed25519": + return Ed25519PublicKey.from_public_bytes(x) + return Ed448PublicKey.from_public_bytes(x) + d = base64url_decode(obj.get("d")) + private_key: Ed25519PrivateKey | Ed448PrivateKey + if curve == "Ed25519": + private_key = Ed25519PrivateKey.from_private_bytes(d) + else: + private_key = Ed448PrivateKey.from_private_bytes(d) + if ( + private_key.public_key().public_bytes( + encoding=Encoding.Raw, format=PublicFormat.Raw + ) + != x + ): + raise InvalidKeyError("Public key does not match private key") + return private_key + except ValueError as err: + raise InvalidKeyError("Invalid key parameter") from err diff --git a/venv/lib/python3.12/site-packages/jwt/api_jwk.py b/venv/lib/python3.12/site-packages/jwt/api_jwk.py new file mode 100644 index 0000000..9aa4d52 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jwt/api_jwk.py @@ -0,0 +1,196 @@ +from __future__ import annotations + +import json +import time +from collections.abc import Iterator +from typing import Any + +from .algorithms import get_default_algorithms, has_crypto, requires_cryptography +from .exceptions import ( + InvalidKeyError, + MissingCryptographyError, + PyJWKError, + PyJWKSetError, + PyJWTError, +) +from .types import JWKDict + + +class PyJWK: + def __init__(self, jwk_data: JWKDict, algorithm: str | None = None) -> None: + """A class that represents a `JSON Web Key `_. + + :param jwk_data: The decoded JWK data. + :type jwk_data: dict[str, typing.Any] + :param algorithm: The key algorithm. If not specified, the key's ``alg`` will be used. + :type algorithm: str or None + :raises InvalidKeyError: If the key type (``kty``) is not found or unsupported, or if the curve (``crv``) is not found or unsupported. + :raises MissingCryptographyError: If the algorithm requires ``cryptography`` to be installed and it is not available. + :raises PyJWKError: If unable to find an algorithm for the key. + """ + self._jwk_data = jwk_data + + kty = self._jwk_data.get("kty", None) + if not kty: + raise InvalidKeyError(f"kty is not found: {self._jwk_data}") + + if not algorithm and isinstance(self._jwk_data, dict): + algorithm = self._jwk_data.get("alg", None) + + if not algorithm: + # Determine alg with kty (and crv). + crv = self._jwk_data.get("crv", None) + if kty == "EC": + if crv == "P-256" or not crv: + algorithm = "ES256" + elif crv == "P-384": + algorithm = "ES384" + elif crv == "P-521": + algorithm = "ES512" + elif crv == "secp256k1": + algorithm = "ES256K" + else: + raise InvalidKeyError(f"Unsupported crv: {crv}") + elif kty == "RSA": + algorithm = "RS256" + elif kty == "oct": + algorithm = "HS256" + elif kty == "OKP": + if not crv: + raise InvalidKeyError(f"crv is not found: {self._jwk_data}") + if crv == "Ed25519": + algorithm = "EdDSA" + else: + raise InvalidKeyError(f"Unsupported crv: {crv}") + else: + raise InvalidKeyError(f"Unsupported kty: {kty}") + + if not has_crypto and algorithm in requires_cryptography: + raise MissingCryptographyError( + f"{algorithm} requires 'cryptography' to be installed." + ) + + self.algorithm_name = algorithm + + try: + self.Algorithm = get_default_algorithms()[algorithm] + except KeyError: + raise PyJWKError( + f"Unable to find an algorithm for key: {self._jwk_data}", + ) from None + + try: + self.key = self.Algorithm.from_jwk(self._jwk_data) + except (TypeError, ValueError) as error: + raise InvalidKeyError( + f"Unable to construct key from JWK: {error}" + ) from error + + @staticmethod + def from_dict(obj: JWKDict, algorithm: str | None = None) -> PyJWK: + """Creates a :class:`PyJWK` object from a JSON-like dictionary. + + :param obj: The JWK data, as a dictionary + :type obj: dict[str, typing.Any] + :param algorithm: The key algorithm. If not specified, the key's ``alg`` will be used. + :type algorithm: str or None + :rtype: PyJWK + """ + return PyJWK(obj, algorithm) + + @staticmethod + def from_json(data: str, algorithm: str | None = None) -> PyJWK: + """Create a :class:`PyJWK` object from a JSON string. + Implicitly calls :meth:`PyJWK.from_dict()`. + + :param str data: The JWK data, as a JSON string. + :param algorithm: The key algorithm. If not specific, the key's ``alg`` will be used. + :type algorithm: str or None + + :rtype: PyJWK + """ + obj = json.loads(data) + return PyJWK.from_dict(obj, algorithm) + + @property + def key_type(self) -> str | None: + """The `kty` property from the JWK. + + :rtype: str or None + """ + return self._jwk_data.get("kty", None) + + @property + def key_id(self) -> str | None: + """The `kid` property from the JWK. + + :rtype: str or None + """ + return self._jwk_data.get("kid", None) + + @property + def public_key_use(self) -> str | None: + """The `use` property from the JWK. + + :rtype: str or None + """ + return self._jwk_data.get("use", None) + + +class PyJWKSet: + def __init__(self, keys: list[JWKDict]) -> None: + self.keys: list[PyJWK] = [] + + if not keys: + raise PyJWKSetError("The JWK Set did not contain any keys") + + if not isinstance(keys, list): + raise PyJWKSetError("Invalid JWK Set value") + + for key in keys: + if not isinstance(key, dict): + # skip members that aren't JWK objects + continue + try: + self.keys.append(PyJWK(key)) + except PyJWTError as error: + if isinstance(error, MissingCryptographyError): + raise error + # skip unusable keys + continue + + if len(self.keys) == 0: + raise PyJWKSetError( + "The JWK Set did not contain any usable keys. Perhaps 'cryptography' is not installed?" + ) + + @staticmethod + def from_dict(obj: dict[str, Any]) -> PyJWKSet: + keys = obj.get("keys", []) + return PyJWKSet(keys) + + @staticmethod + def from_json(data: str) -> PyJWKSet: + obj = json.loads(data) + return PyJWKSet.from_dict(obj) + + def __getitem__(self, kid: str) -> PyJWK: + for key in self.keys: + if key.key_id == kid: + return key + raise KeyError(f"keyset has no key for kid: {kid}") + + def __iter__(self) -> Iterator[PyJWK]: + return iter(self.keys) + + +class PyJWTSetWithTimestamp: + def __init__(self, jwk_set: PyJWKSet): + self.jwk_set = jwk_set + self.timestamp = time.monotonic() + + def get_jwk_set(self) -> PyJWKSet: + return self.jwk_set + + def get_timestamp(self) -> float: + return self.timestamp diff --git a/venv/lib/python3.12/site-packages/jwt/api_jws.py b/venv/lib/python3.12/site-packages/jwt/api_jws.py new file mode 100644 index 0000000..8665d64 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jwt/api_jws.py @@ -0,0 +1,485 @@ +from __future__ import annotations + +import binascii +import json +import warnings +from collections.abc import Sequence +from typing import TYPE_CHECKING, Any + +from .algorithms import ( + Algorithm, + get_default_algorithms, + has_crypto, + requires_cryptography, +) +from .api_jwk import PyJWK +from .exceptions import ( + DecodeError, + InvalidAlgorithmError, + InvalidKeyError, + InvalidSignatureError, + InvalidTokenError, +) +from .utils import base64url_decode, base64url_encode +from .warnings import InsecureKeyLengthWarning, RemovedInPyjwt3Warning + +if TYPE_CHECKING: + from .algorithms import AllowedPrivateKeys, AllowedPublicKeys + from .types import SigOptions + +_ALGORITHM_UNSET = object() +_BASE64URL_ALPHABET = ( + b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" +) + + +class PyJWS: + header_typ = "JWT" + + def __init__( + self, + algorithms: Sequence[str] | None = None, + options: SigOptions | None = None, + ) -> None: + self._algorithms = get_default_algorithms() + self._valid_algs = ( + set(algorithms) if algorithms is not None else set(self._algorithms) + ) + + # Remove algorithms that aren't on the whitelist + for key in list(self._algorithms.keys()): + if key not in self._valid_algs: + del self._algorithms[key] + + self.options: SigOptions = self._get_default_options() + if options is not None: + self.options = {**self.options, **options} + + @staticmethod + def _get_default_options() -> SigOptions: + return {"verify_signature": True, "enforce_minimum_key_length": False} + + def register_algorithm(self, alg_id: str, alg_obj: Algorithm) -> None: + """ + Registers a new Algorithm for use when creating and verifying tokens. + + :param str alg_id: the ID of the Algorithm + :param alg_obj: the Algorithm object + :type alg_obj: Algorithm + """ + if alg_id in self._algorithms: + raise ValueError("Algorithm already has a handler.") + + if not isinstance(alg_obj, Algorithm): + raise TypeError("Object is not of type `Algorithm`") + + self._algorithms[alg_id] = alg_obj + self._valid_algs.add(alg_id) + + def unregister_algorithm(self, alg_id: str) -> None: + """ + Unregisters an Algorithm for use when creating and verifying tokens + :param str alg_id: the ID of the Algorithm + :raises KeyError: if algorithm is not registered. + """ + if alg_id not in self._algorithms: + raise KeyError( + "The specified algorithm could not be removed" + " because it is not registered." + ) + + del self._algorithms[alg_id] + self._valid_algs.remove(alg_id) + + def get_algorithms(self) -> list[str]: + """ + Returns a list of supported values for the `alg` parameter. + + :rtype: list[str] + """ + return list(self._valid_algs) + + def get_algorithm_by_name(self, alg_name: str) -> Algorithm: + """ + For a given string name, return the matching Algorithm object. + + Example usage: + >>> jws_obj = PyJWS() + >>> jws_obj.get_algorithm_by_name("RS256") + + :param alg_name: The name of the algorithm to retrieve + :type alg_name: str + :rtype: Algorithm + """ + try: + return self._algorithms[alg_name] + except KeyError as e: + if not has_crypto and alg_name in requires_cryptography: + raise NotImplementedError( + f"Algorithm '{alg_name}' could not be found. Do you have cryptography installed?" + ) from e + raise NotImplementedError("Algorithm not supported") from e + + def encode( + self, + payload: bytes, + key: AllowedPrivateKeys | PyJWK | str | bytes, + algorithm: str | None = _ALGORITHM_UNSET, # type: ignore[assignment] + headers: dict[str, Any] | None = None, + json_encoder: type[json.JSONEncoder] | None = None, + is_payload_detached: bool = False, + sort_headers: bool = True, + ) -> str: + segments: list[bytes] = [] + + # declare a new var to narrow the type for type checkers + if algorithm is _ALGORITHM_UNSET: + if isinstance(key, PyJWK): + algorithm_ = key.algorithm_name + else: + algorithm_ = "HS256" + elif algorithm is None: + if isinstance(key, PyJWK): + algorithm_ = key.algorithm_name + else: + algorithm_ = "none" + else: + algorithm_ = algorithm + + # Prefer headers values if present to function parameters. + if headers: + headers_alg = headers.get("alg") + if headers_alg: + algorithm_ = headers["alg"] + + headers_b64 = headers.get("b64") + if headers_b64 is False: + is_payload_detached = True + + # Header + header: dict[str, Any] = {"typ": self.header_typ, "alg": algorithm_} + + if headers: + self._validate_headers(headers, encoding=True) + header.update(headers) + + if not header["typ"]: + del header["typ"] + + if is_payload_detached: + header["b64"] = False + # RFC 7797 §3: producers MUST list "b64" in "crit" whenever + # "b64" appears in the protected header, so b64-unaware + # verifiers don't silently treat an unencoded payload as + # base64-encoded. + existing_crit = header.get("crit", []) + if not isinstance(existing_crit, list): + raise InvalidTokenError("Invalid 'crit' header: must be a list") + if "b64" not in existing_crit: + header["crit"] = [*existing_crit, "b64"] + elif "b64" in header: + # True is the standard value for b64, so no need for it + del header["b64"] + + json_header = json.dumps( + header, separators=(",", ":"), cls=json_encoder, sort_keys=sort_headers + ).encode() + + segments.append(base64url_encode(json_header)) + + if is_payload_detached: + msg_payload = payload + else: + msg_payload = base64url_encode(payload) + segments.append(msg_payload) + + # Segments + signing_input = b".".join(segments) + + alg_obj = self.get_algorithm_by_name(algorithm_) + if isinstance(key, PyJWK): + key = key.key + key = alg_obj.prepare_key(key) + + key_length_msg = alg_obj.check_key_length(key) + if key_length_msg: + if self.options.get("enforce_minimum_key_length", False): + raise InvalidKeyError(key_length_msg) + else: + warnings.warn(key_length_msg, InsecureKeyLengthWarning, stacklevel=2) + + signature = alg_obj.sign(signing_input, key) + + segments.append(base64url_encode(signature)) + + # Don't put the payload content inside the encoded token when detached + if is_payload_detached: + segments[1] = b"" + encoded_string = b".".join(segments) + + return encoded_string.decode("utf-8") + + def decode_complete( + self, + jwt: str | bytes, + key: AllowedPublicKeys | PyJWK | str | bytes = "", + algorithms: Sequence[str] | None = None, + options: SigOptions | None = None, + detached_payload: bytes | None = None, + **kwargs: dict[str, Any], + ) -> dict[str, Any]: + if kwargs: + warnings.warn( + "passing additional kwargs to decode_complete() is deprecated " + "and will be removed in pyjwt version 3. " + f"Unsupported kwargs: {tuple(kwargs.keys())}", + RemovedInPyjwt3Warning, + stacklevel=2, + ) + merged_options: SigOptions + if options is None: + merged_options = self.options + else: + merged_options = {**self.options, **options} + + verify_signature = merged_options["verify_signature"] + + if verify_signature and not algorithms and not isinstance(key, PyJWK): + raise DecodeError( + 'It is required that you pass in a value for the "algorithms" argument when calling decode().' + ) + + payload, signing_input, header, signature = self._load(jwt) + + self._validate_headers(header) + + if detached_payload is not None and header.get("b64", True) is not False: + raise DecodeError( + 'It is only valid to pass "detached_payload" when the protected header has "b64" set to false.' + ) + + if header.get("b64", True) is False: + # RFC 7797 §3: when "b64" is present in the protected header, + # it MUST also appear in "crit". A token that sets b64=false + # without declaring it critical is malformed. + crit = header.get("crit") or [] + if not isinstance(crit, list) or "b64" not in crit: + raise InvalidTokenError( + "The 'b64' header parameter requires 'b64' to be listed in 'crit'." + ) + if detached_payload is None: + raise DecodeError( + 'It is required that you pass in a value for the "detached_payload" argument to decode a message having the b64 header set to false.' + ) + payload = detached_payload + signing_input = b".".join([signing_input.rsplit(b".", 1)[0], payload]) + + if verify_signature: + self._verify_signature( + signing_input, + header, + signature, + key, + algorithms, + options=merged_options, + ) + + return { + "payload": payload, + "header": header, + "signature": signature, + } + + def decode( + self, + jwt: str | bytes, + key: AllowedPublicKeys | PyJWK | str | bytes = "", + algorithms: Sequence[str] | None = None, + options: SigOptions | None = None, + detached_payload: bytes | None = None, + **kwargs: dict[str, Any], + ) -> Any: + if kwargs: + warnings.warn( + "passing additional kwargs to decode() is deprecated " + "and will be removed in pyjwt version 3. " + f"Unsupported kwargs: {tuple(kwargs.keys())}", + RemovedInPyjwt3Warning, + stacklevel=2, + ) + decoded = self.decode_complete( + jwt, key, algorithms, options, detached_payload=detached_payload + ) + return decoded["payload"] + + def get_unverified_header(self, jwt: str | bytes) -> dict[str, Any]: + """Returns back the JWT header parameters as a `dict` + + Note: The signature is not verified so the header parameters + should not be fully trusted until signature verification is complete + """ + headers = self._load(jwt)[2] + self._validate_headers(headers) + + return headers + + @staticmethod + def _decode_base64url_segment(segment: bytes, name: str) -> bytes: + # Accept trailing '=' used by some issuers (e.g. AWS ALB). Still + # reject non-alphabet junk such as '!!!!' (GHSA-hxm8-2xgr-2p9m). + padding = 0 + stripped = segment + while stripped.endswith(b"="): + stripped = stripped[:-1] + padding += 1 + if padding > 2: + raise DecodeError(f"Invalid {name} padding") + + if padding and len(segment) % 4 != 0: + raise DecodeError(f"Invalid {name} padding") + + if len(stripped) % 4 == 1 or any( + character not in _BASE64URL_ALPHABET for character in stripped + ): + raise DecodeError(f"Invalid {name} padding") + + try: + decoded = base64url_decode(stripped) + except (TypeError, binascii.Error) as err: + raise DecodeError(f"Invalid {name} padding") from err + + if base64url_encode(decoded) != stripped: + raise DecodeError(f"Invalid {name} padding") + + return decoded + + def _load(self, jwt: str | bytes) -> tuple[bytes, bytes, dict[str, Any], bytes]: + if isinstance(jwt, str): + jwt = jwt.encode("utf-8") + + if not isinstance(jwt, bytes): + raise DecodeError(f"Invalid token type. Token must be a {bytes}") + + try: + signing_input, crypto_segment = jwt.rsplit(b".", 1) + header_segment, payload_segment = signing_input.split(b".", 1) + except ValueError as err: + raise DecodeError("Not enough segments") from err + + header_data = self._decode_base64url_segment(header_segment, "header") + + try: + header: dict[str, Any] = json.loads(header_data) + except (ValueError, RecursionError) as e: + raise DecodeError(f"Invalid header string: {e}") from e + + if not isinstance(header, dict): + raise DecodeError("Invalid header string: must be a json object") + + if header.get("b64", True) is False: + # Detached payload form (RFC 7515 Appendix F): the compact-form + # payload segment must be empty; the caller supplies the actual + # payload via the `detached_payload` argument in decode_complete. + # Skipping the base64 decode here removes an unauthenticated work + # amplifier — otherwise an attacker can inflate the unused + # segment to force CPU + memory cost before the signature is + # even checked. + if payload_segment: + raise DecodeError("Payload segment must be empty when 'b64' is false.") + payload = b"" + else: + payload = self._decode_base64url_segment(payload_segment, "payload") + + signature = self._decode_base64url_segment(crypto_segment, "crypto") + + return (payload, signing_input, header, signature) + + def _verify_signature( + self, + signing_input: bytes, + header: dict[str, Any], + signature: bytes, + key: AllowedPublicKeys | PyJWK | str | bytes = "", + algorithms: Sequence[str] | None = None, + options: SigOptions | None = None, + ) -> None: + effective_options = options if options is not None else self.options + + if algorithms is None and isinstance(key, PyJWK): + algorithms = [key.algorithm_name] + try: + alg = header["alg"] + except KeyError: + raise InvalidAlgorithmError("Algorithm not specified") from None + + if not alg or (algorithms is not None and alg not in algorithms): + raise InvalidAlgorithmError("The specified alg value is not allowed") + + if isinstance(key, PyJWK): + # The PyJWK has a fixed algorithm bound at construction time. + # Verification must use that algorithm, not whatever the token + # header advertises, otherwise the caller's allow-list check + # above degenerates into a string compare with no behavioural + # effect on which algorithm actually verifies the signature. + if alg != key.algorithm_name: + raise InvalidAlgorithmError( + f"Token algorithm {alg!r} does not match the key's " + f"algorithm {key.algorithm_name!r}" + ) + alg_obj = key.Algorithm + prepared_key = alg_obj.prepare_key(key.key) + else: + try: + alg_obj = self.get_algorithm_by_name(alg) + except NotImplementedError as e: + raise InvalidAlgorithmError("Algorithm not supported") from e + prepared_key = alg_obj.prepare_key(key) + + key_length_msg = alg_obj.check_key_length(prepared_key) + if key_length_msg: + if effective_options.get("enforce_minimum_key_length", False): + raise InvalidKeyError(key_length_msg) + else: + warnings.warn(key_length_msg, InsecureKeyLengthWarning, stacklevel=4) + + if not alg_obj.verify(signing_input, prepared_key, signature): + raise InvalidSignatureError("Signature verification failed") + + # Extensions that PyJWT actually understands and supports + _supported_crit: set[str] = {"b64"} + + def _validate_headers( + self, headers: dict[str, Any], *, encoding: bool = False + ) -> None: + if "kid" in headers: + self._validate_kid(headers["kid"]) + if not encoding and "crit" in headers: + self._validate_crit(headers) + + def _validate_kid(self, kid: Any) -> None: + if not isinstance(kid, str): + raise InvalidTokenError("Key ID header parameter must be a string") + + def _validate_crit(self, headers: dict[str, Any]) -> None: + crit = headers["crit"] + if not isinstance(crit, list) or len(crit) == 0: + raise InvalidTokenError("Invalid 'crit' header: must be a non-empty list") + for ext in crit: + if not isinstance(ext, str): + raise InvalidTokenError("Invalid 'crit' header: values must be strings") + if ext not in self._supported_crit: + raise InvalidTokenError(f"Unsupported critical extension: {ext}") + if ext not in headers: + raise InvalidTokenError( + f"Critical extension '{ext}' is missing from headers" + ) + + +_jws_global_obj = PyJWS() +encode = _jws_global_obj.encode +decode_complete = _jws_global_obj.decode_complete +decode = _jws_global_obj.decode +register_algorithm = _jws_global_obj.register_algorithm +unregister_algorithm = _jws_global_obj.unregister_algorithm +get_algorithm_by_name = _jws_global_obj.get_algorithm_by_name +get_unverified_header = _jws_global_obj.get_unverified_header diff --git a/venv/lib/python3.12/site-packages/jwt/api_jwt.py b/venv/lib/python3.12/site-packages/jwt/api_jwt.py new file mode 100644 index 0000000..a63191d --- /dev/null +++ b/venv/lib/python3.12/site-packages/jwt/api_jwt.py @@ -0,0 +1,595 @@ +from __future__ import annotations + +import json +import os +import warnings +from calendar import timegm +from collections.abc import Container, Iterable, Sequence +from datetime import datetime, timedelta, timezone +from typing import TYPE_CHECKING, Any, Union, cast + +from .api_jws import PyJWS, _ALGORITHM_UNSET, _jws_global_obj +from .exceptions import ( + DecodeError, + ExpiredSignatureError, + ImmatureSignatureError, + InvalidAudienceError, + InvalidIssuedAtError, + InvalidIssuerError, + InvalidJTIError, + InvalidSubjectError, + MissingRequiredClaimError, +) +from .warnings import RemovedInPyjwt3Warning + +if TYPE_CHECKING or bool(os.getenv("SPHINX_BUILD", "")): + import sys + + if sys.version_info >= (3, 10): + from typing import TypeAlias + else: + # Python 3.9 and lower + from typing_extensions import TypeAlias + + from .algorithms import AllowedPrivateKeys, AllowedPublicKeys + from .api_jwk import PyJWK + from .types import FullOptions, Options, SigOptions + + AllowedPrivateKeyTypes: TypeAlias = Union[AllowedPrivateKeys, PyJWK, str, bytes] + AllowedPublicKeyTypes: TypeAlias = Union[AllowedPublicKeys, PyJWK, str, bytes] + + +class PyJWT: + def __init__(self, options: Options | None = None) -> None: + self.options: FullOptions + self.options = self._get_default_options() + if options is not None: + self.options = self._merge_options(options) + + self._jws = PyJWS(options=self._get_sig_options()) + + @staticmethod + def _get_default_options() -> FullOptions: + return { + "verify_signature": True, + "verify_exp": True, + "verify_nbf": True, + "verify_iat": True, + "verify_aud": True, + "verify_iss": True, + "verify_sub": True, + "verify_jti": True, + "require": [], + "strict_aud": False, + "enforce_minimum_key_length": False, + } + + def _get_sig_options(self) -> SigOptions: + return { + "verify_signature": self.options["verify_signature"], + "enforce_minimum_key_length": self.options.get( + "enforce_minimum_key_length", False + ), + } + + def _merge_options(self, options: Options | None = None) -> FullOptions: + if options is None: + return self.options + + merged_options = cast("Options", dict(options)) + + # (defensive) set defaults for verify_x to False if verify_signature is False + if not merged_options.get("verify_signature", True): + merged_options["verify_exp"] = merged_options.get("verify_exp", False) + merged_options["verify_nbf"] = merged_options.get("verify_nbf", False) + merged_options["verify_iat"] = merged_options.get("verify_iat", False) + merged_options["verify_aud"] = merged_options.get("verify_aud", False) + merged_options["verify_iss"] = merged_options.get("verify_iss", False) + merged_options["verify_sub"] = merged_options.get("verify_sub", False) + merged_options["verify_jti"] = merged_options.get("verify_jti", False) + return {**self.options, **merged_options} + + def encode( + self, + payload: dict[str, Any], + key: AllowedPrivateKeyTypes, + algorithm: str | None = _ALGORITHM_UNSET, # type: ignore[assignment] + headers: dict[str, Any] | None = None, + json_encoder: type[json.JSONEncoder] | None = None, + sort_headers: bool = True, + ) -> str: + """Encode the ``payload`` as JSON Web Token. + + :param payload: JWT claims, e.g. ``dict(iss=..., aud=..., sub=...)`` + :type payload: dict[str, typing.Any] + :param key: a key suitable for the chosen algorithm: + + * for **asymmetric algorithms**: PEM-formatted private key, a multiline string + * for **symmetric algorithms**: plain string, sufficiently long for security + + :type key: str or bytes or PyJWK or :py:class:`jwt.algorithms.AllowedPrivateKeys` + :param algorithm: algorithm to sign the token with, e.g. ``"ES256"``. + If ``headers`` includes ``alg``, it will be preferred to this parameter. + If ``key`` is a :class:`PyJWK` object, by default the key algorithm will be used. + :type algorithm: str or None + :param headers: additional JWT header fields, e.g. ``dict(kid="my-key-id")``. + :type headers: dict[str, typing.Any] or None + :param json_encoder: custom JSON encoder for ``payload`` and ``headers`` + :type json_encoder: json.JSONEncoder or None + + :rtype: str + :returns: a JSON Web Token + + :raises TypeError: if ``payload`` is not a ``dict`` + """ + # Check that we get a dict + if not isinstance(payload, dict): + raise TypeError( + "Expecting a dict object, as JWT only supports " + "JSON objects as payloads." + ) + + # Payload + payload = payload.copy() + for time_claim in ["exp", "iat", "nbf"]: + # Convert datetime to a intDate value in known time-format claims + if isinstance(payload.get(time_claim), datetime): + payload[time_claim] = timegm(payload[time_claim].utctimetuple()) + + # Issue #1039, iss being set to non-string + if "iss" in payload and not isinstance(payload["iss"], str): + raise TypeError("Issuer (iss) must be a string.") + + json_payload = self._encode_payload( + payload, + headers=headers, + json_encoder=json_encoder, + ) + + return self._jws.encode( + json_payload, + key, + algorithm, + headers, + json_encoder, + sort_headers=sort_headers, + ) + + def _encode_payload( + self, + payload: dict[str, Any], + headers: dict[str, Any] | None = None, + json_encoder: type[json.JSONEncoder] | None = None, + ) -> bytes: + """ + Encode a given payload to the bytes to be signed. + + This method is intended to be overridden by subclasses that need to + encode the payload in a different way, e.g. compress the payload. + """ + return json.dumps( + payload, + separators=(",", ":"), + cls=json_encoder, + ).encode("utf-8") + + def decode_complete( + self, + jwt: str | bytes, + key: AllowedPublicKeyTypes = "", + algorithms: Sequence[str] | None = None, + options: Options | None = None, + # deprecated arg, remove in pyjwt3 + verify: bool | None = None, + # could be used as passthrough to api_jws, consider removal in pyjwt3 + detached_payload: bytes | None = None, + # passthrough arguments to _validate_claims + # consider putting in options + audience: str | Iterable[str] | None = None, + issuer: str | Container[str] | None = None, + subject: str | None = None, + leeway: float | timedelta = 0, + # kwargs + **kwargs: Any, + ) -> dict[str, Any]: + """Identical to ``jwt.decode`` except for return value which is a dictionary containing the token header (JOSE Header), + the token payload (JWT Payload), and token signature (JWT Signature) on the keys "header", "payload", + and "signature" respectively. + + :param jwt: the token to be decoded + :type jwt: str or bytes + :param key: the key suitable for the allowed algorithm + :type key: str or bytes or PyJWK or :py:class:`jwt.algorithms.AllowedPublicKeys` + + :param algorithms: allowed algorithms, e.g. ``["ES256"]`` + + .. warning:: + + Do **not** compute the ``algorithms`` parameter based on + the ``alg`` from the token itself, or on any other data + that an attacker may be able to influence, as that might + expose you to various vulnerabilities (see `RFC 8725 §2.1 + `_). Instead, + either hard-code a fixed value for ``algorithms``, or + configure it in the same place you configure the + ``key``. Make sure not to mix symmetric and asymmetric + algorithms that interpret the ``key`` in different ways + (e.g. HS\\* and RS\\*). + :type algorithms: typing.Sequence[str] or None + + :param jwt.types.Options options: extended decoding and validation options + Refer to :py:class:`jwt.types.Options` for more information. + + :param audience: optional, the value for ``verify_aud`` check + :type audience: str or typing.Iterable[str] or None + :param issuer: optional, the value for ``verify_iss`` check + :type issuer: str or typing.Container[str] or None + :param leeway: a time margin in seconds for the expiration check + :type leeway: float or datetime.timedelta + :rtype: dict[str, typing.Any] + :returns: Decoded JWT with the JOSE Header on the key ``header``, the JWS + Payload on the key ``payload``, and the JWS Signature on the key ``signature``. + """ + if kwargs: + warnings.warn( + "passing additional kwargs to decode_complete() is deprecated " + "and will be removed in pyjwt version 3. " + f"Unsupported kwargs: {tuple(kwargs.keys())}", + RemovedInPyjwt3Warning, + stacklevel=2, + ) + + if options is None: + verify_signature = True + else: + verify_signature = options.get("verify_signature", True) + + # If the user has set the legacy `verify` argument, and it doesn't match + # what the relevant `options` entry for the argument is, inform the user + # that they're likely making a mistake. + if verify is not None and verify != verify_signature: + warnings.warn( + "The `verify` argument to `decode` does nothing in PyJWT 2.0 and newer. " + "The equivalent is setting `verify_signature` to False in the `options` dictionary. " + "This invocation has a mismatch between the kwarg and the option entry.", + category=DeprecationWarning, + stacklevel=2, + ) + + merged_options = self._merge_options(options) + + sig_options: SigOptions = { + "verify_signature": verify_signature, + "enforce_minimum_key_length": merged_options.get( + "enforce_minimum_key_length", False + ), + } + decoded = self._jws.decode_complete( + jwt, + key=key, + algorithms=algorithms, + options=sig_options, + detached_payload=detached_payload, + ) + + payload = self._decode_payload(decoded) + + self._validate_claims( + payload, + merged_options, + audience=audience, + issuer=issuer, + leeway=leeway, + subject=subject, + ) + + decoded["payload"] = payload + return decoded + + def _decode_payload(self, decoded: dict[str, Any]) -> dict[str, Any]: + """ + Decode the payload from a JWS dictionary (payload, signature, header). + + This method is intended to be overridden by subclasses that need to + decode the payload in a different way, e.g. decompress compressed + payloads. + """ + try: + payload: dict[str, Any] = json.loads(decoded["payload"]) + except (ValueError, RecursionError) as e: + raise DecodeError(f"Invalid payload string: {e}") from e + if not isinstance(payload, dict): + raise DecodeError("Invalid payload string: must be a json object") + return payload + + def decode( + self, + jwt: str | bytes, + key: AllowedPublicKeys | PyJWK | str | bytes = "", + algorithms: Sequence[str] | None = None, + options: Options | None = None, + # deprecated arg, remove in pyjwt3 + verify: bool | None = None, + # could be used as passthrough to api_jws, consider removal in pyjwt3 + detached_payload: bytes | None = None, + # passthrough arguments to _validate_claims + # consider putting in options + audience: str | Iterable[str] | None = None, + subject: str | None = None, + issuer: str | Container[str] | None = None, + leeway: float | timedelta = 0, + # kwargs + **kwargs: Any, + ) -> dict[str, Any]: + """Verify the ``jwt`` token signature and return the token claims. + + :param jwt: the token to be decoded + :type jwt: str or bytes + :param key: the key suitable for the allowed algorithm + :type key: str or bytes or PyJWK or :py:class:`jwt.algorithms.AllowedPublicKeys` + + :param algorithms: allowed algorithms, e.g. ``["ES256"]`` + If ``key`` is a :class:`PyJWK` object, allowed algorithms will default to the key algorithm. + + .. warning:: + + Do **not** compute the ``algorithms`` parameter based on + the ``alg`` from the token itself, or on any other data + that an attacker may be able to influence, as that might + expose you to various vulnerabilities (see `RFC 8725 §2.1 + `_). Instead, + either hard-code a fixed value for ``algorithms``, or + configure it in the same place you configure the + ``key``. Make sure not to mix symmetric and asymmetric + algorithms that interpret the ``key`` in different ways + (e.g. HS\\* and RS\\*). + :type algorithms: typing.Sequence[str] or None + + :param jwt.types.Options options: extended decoding and validation options + Refer to :py:class:`jwt.types.Options` for more information. + + :param audience: optional, the value for ``verify_aud`` check + :type audience: str or typing.Iterable[str] or None + :param subject: optional, the value for ``verify_sub`` check + :type subject: str or None + :param issuer: optional, the value for ``verify_iss`` check + :type issuer: str or typing.Container[str] or None + :param leeway: a time margin in seconds for the expiration check + :type leeway: float or datetime.timedelta + :rtype: dict[str, typing.Any] + :returns: the JWT claims + """ + if kwargs: + warnings.warn( + "passing additional kwargs to decode() is deprecated " + "and will be removed in pyjwt version 3. " + f"Unsupported kwargs: {tuple(kwargs.keys())}", + RemovedInPyjwt3Warning, + stacklevel=2, + ) + decoded = self.decode_complete( + jwt, + key, + algorithms, + options, + verify=verify, + detached_payload=detached_payload, + audience=audience, + subject=subject, + issuer=issuer, + leeway=leeway, + ) + return cast(dict[str, Any], decoded["payload"]) + + def _validate_claims( + self, + payload: dict[str, Any], + options: FullOptions, + audience: Iterable[str] | str | None = None, + issuer: Container[str] | str | None = None, + subject: str | None = None, + leeway: float | timedelta = 0, + ) -> None: + if isinstance(leeway, timedelta): + leeway = leeway.total_seconds() + + if audience is not None and not isinstance(audience, (str, Iterable)): + raise TypeError("audience must be a string, iterable or None") + + self._validate_required_claims(payload, options["require"]) + + now = datetime.now(tz=timezone.utc).timestamp() + + if "iat" in payload and options["verify_iat"]: + self._validate_iat(payload, now, leeway) + + if "nbf" in payload and options["verify_nbf"]: + self._validate_nbf(payload, now, leeway) + + if "exp" in payload and options["verify_exp"]: + self._validate_exp(payload, now, leeway) + + if options["verify_iss"]: + self._validate_iss(payload, issuer) + + if options["verify_aud"]: + self._validate_aud( + payload, audience, strict=options.get("strict_aud", False) + ) + + if options["verify_sub"]: + self._validate_sub(payload, subject) + + if options["verify_jti"]: + self._validate_jti(payload) + + def _validate_required_claims( + self, + payload: dict[str, Any], + claims: Iterable[str], + ) -> None: + for claim in claims: + if payload.get(claim) is None: + raise MissingRequiredClaimError(claim) + + def _validate_sub( + self, payload: dict[str, Any], subject: str | None = None + ) -> None: + """ + Checks whether "sub" if in the payload is valid or not. + This is an Optional claim + + :param payload(dict): The payload which needs to be validated + :param subject(str): The subject of the token + """ + + if "sub" not in payload: + return + + if not isinstance(payload["sub"], str): + raise InvalidSubjectError("Subject must be a string") + + if subject is not None: + if payload.get("sub") != subject: + raise InvalidSubjectError("Invalid subject") + + def _validate_jti(self, payload: dict[str, Any]) -> None: + """ + Checks whether "jti" in the payload is valid or not + This is an Optional claim + + :param payload(dict): The payload which needs to be validated + """ + + if "jti" not in payload: + return + + if not isinstance(payload.get("jti"), str): + raise InvalidJTIError("JWT ID must be a string") + + def _validate_iat( + self, + payload: dict[str, Any], + now: float, + leeway: float, + ) -> None: + try: + iat = int(payload["iat"]) + except (ValueError, TypeError, OverflowError): + raise InvalidIssuedAtError( + "Issued At claim (iat) must be an integer." + ) from None + if iat > (now + leeway): + raise ImmatureSignatureError("The token is not yet valid (iat)") + + def _validate_nbf( + self, + payload: dict[str, Any], + now: float, + leeway: float, + ) -> None: + try: + nbf = int(payload["nbf"]) + except (ValueError, TypeError, OverflowError): + raise DecodeError("Not Before claim (nbf) must be an integer.") from None + + if nbf > (now + leeway): + raise ImmatureSignatureError("The token is not yet valid (nbf)") + + def _validate_exp( + self, + payload: dict[str, Any], + now: float, + leeway: float, + ) -> None: + try: + exp = int(payload["exp"]) + except (ValueError, TypeError, OverflowError): + raise DecodeError( + "Expiration Time claim (exp) must be an integer." + ) from None + + if exp <= (now - leeway): + raise ExpiredSignatureError("Signature has expired") + + def _validate_aud( + self, + payload: dict[str, Any], + audience: str | Iterable[str] | None, + *, + strict: bool = False, + ) -> None: + if audience is None: + if "aud" not in payload or not payload["aud"]: + return + # Application did not specify an audience, but + # the token has the 'aud' claim + raise InvalidAudienceError("Invalid audience") + + if "aud" not in payload or not payload["aud"]: + # Application specified an audience, but it could not be + # verified since the token does not contain a claim. + raise MissingRequiredClaimError("aud") + + audience_claims = payload["aud"] + + # In strict mode, we forbid list matching: the supplied audience + # must be a string, and it must exactly match the audience claim. + if strict: + # Only a single audience is allowed in strict mode. + if not isinstance(audience, str): + raise InvalidAudienceError("Invalid audience (strict)") + + # Only a single audience claim is allowed in strict mode. + if not isinstance(audience_claims, str): + raise InvalidAudienceError("Invalid claim format in token (strict)") + + if audience != audience_claims: + raise InvalidAudienceError("Audience doesn't match (strict)") + + return + + if isinstance(audience_claims, str): + audience_claims = [audience_claims] + if not isinstance(audience_claims, list): + raise InvalidAudienceError("Invalid claim format in token") + if any(not isinstance(c, str) for c in audience_claims): + raise InvalidAudienceError("Invalid claim format in token") + + if isinstance(audience, str): + audience = [audience] + + if all(aud not in audience_claims for aud in audience): + raise InvalidAudienceError("Audience doesn't match") + + def _validate_iss( + self, payload: dict[str, Any], issuer: Container[str] | str | None + ) -> None: + if issuer is None: + return + + if "iss" not in payload: + raise MissingRequiredClaimError("iss") + + iss = payload["iss"] + if not isinstance(iss, str): + raise InvalidIssuerError("Payload Issuer (iss) must be a string") + + if isinstance(issuer, str): + if iss != issuer: + raise InvalidIssuerError("Invalid issuer") + else: + try: + if iss not in issuer: + raise InvalidIssuerError("Invalid issuer") + except TypeError: + raise InvalidIssuerError( + 'Issuer param must be "str" or "Container[str]"' + ) from None + + +_jwt_global_obj = PyJWT() +_jwt_global_obj._jws = _jws_global_obj +encode = _jwt_global_obj.encode +decode_complete = _jwt_global_obj.decode_complete +decode = _jwt_global_obj.decode diff --git a/venv/lib/python3.12/site-packages/jwt/exceptions.py b/venv/lib/python3.12/site-packages/jwt/exceptions.py new file mode 100644 index 0000000..57559fc --- /dev/null +++ b/venv/lib/python3.12/site-packages/jwt/exceptions.py @@ -0,0 +1,113 @@ +class PyJWTError(Exception): + """ + Base class for all exceptions + """ + + pass + + +class InvalidTokenError(PyJWTError): + """Base exception when ``decode()`` fails on a token""" + + pass + + +class DecodeError(InvalidTokenError): + """Raised when a token cannot be decoded because it failed validation""" + + pass + + +class InvalidSignatureError(DecodeError): + """Raised when a token's signature doesn't match the one provided as part of + the token.""" + + pass + + +class ExpiredSignatureError(InvalidTokenError): + """Raised when a token's ``exp`` claim indicates that it has expired""" + + pass + + +class InvalidAudienceError(InvalidTokenError): + """Raised when a token's ``aud`` claim does not match one of the expected + audience values""" + + pass + + +class InvalidIssuerError(InvalidTokenError): + """Raised when a token's ``iss`` claim does not match the expected issuer""" + + pass + + +class InvalidIssuedAtError(InvalidTokenError): + """Raised when a token's ``iat`` claim is non-numeric""" + + pass + + +class ImmatureSignatureError(InvalidTokenError): + """Raised when a token's ``nbf`` or ``iat`` claims represent a time in the future""" + + pass + + +class InvalidKeyError(PyJWTError): + """Raised when the specified key is not in the proper format""" + + pass + + +class InvalidAlgorithmError(InvalidTokenError): + """Raised when the specified algorithm is not recognized by PyJWT""" + + pass + + +class MissingRequiredClaimError(InvalidTokenError): + """Raised when a claim that is required to be present is not contained + in the claimset""" + + def __init__(self, claim: str) -> None: + self.claim = claim + + def __str__(self) -> str: + return f'Token is missing the "{self.claim}" claim' + + +class PyJWKError(PyJWTError): + pass + + +class MissingCryptographyError(PyJWKError): + """Raised if the algorithm requires ``cryptography`` to be installed and it is not available.""" + + pass + + +class PyJWKSetError(PyJWTError): + pass + + +class PyJWKClientError(PyJWTError): + pass + + +class PyJWKClientConnectionError(PyJWKClientError): + pass + + +class InvalidSubjectError(InvalidTokenError): + """Raised when a token's ``sub`` claim is not a string or doesn't match the expected ``subject``""" + + pass + + +class InvalidJTIError(InvalidTokenError): + """Raised when a token's ``jti`` claim is not a string""" + + pass diff --git a/venv/lib/python3.12/site-packages/jwt/help.py b/venv/lib/python3.12/site-packages/jwt/help.py new file mode 100644 index 0000000..2f42b5d --- /dev/null +++ b/venv/lib/python3.12/site-packages/jwt/help.py @@ -0,0 +1,66 @@ +import json +import platform +import sys + +from . import __version__ as pyjwt_version + +try: + import cryptography + + cryptography_version = cryptography.__version__ +except ModuleNotFoundError: + cryptography_version = "" + + +def info() -> dict[str, dict[str, str]]: + """ + Generate information for a bug report. + Based on the requests package help utility module. + """ + try: + platform_info = { + "system": platform.system(), + "release": platform.release(), + } + except OSError: + platform_info = {"system": "Unknown", "release": "Unknown"} + + implementation = platform.python_implementation() + + if implementation == "CPython": + implementation_version = platform.python_version() + elif implementation == "PyPy": + pypy_version_info = sys.pypy_version_info # type: ignore[attr-defined] + implementation_version = ( + f"{pypy_version_info.major}." + f"{pypy_version_info.minor}." + f"{pypy_version_info.micro}" + ) + if pypy_version_info.releaselevel != "final": + implementation_version = "".join( + [ + implementation_version, + pypy_version_info.releaselevel, + ] + ) + else: + implementation_version = "Unknown" + + return { + "platform": platform_info, + "implementation": { + "name": implementation, + "version": implementation_version, + }, + "cryptography": {"version": cryptography_version}, + "pyjwt": {"version": pyjwt_version}, + } + + +def main() -> None: + """Pretty-print the bug information as JSON.""" + print(json.dumps(info(), sort_keys=True, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/venv/lib/python3.12/site-packages/jwt/jwk_set_cache.py b/venv/lib/python3.12/site-packages/jwt/jwk_set_cache.py new file mode 100644 index 0000000..887de91 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jwt/jwk_set_cache.py @@ -0,0 +1,47 @@ +import time +from typing import Any, Optional, Union + +from .api_jwk import PyJWKSet, PyJWTSetWithTimestamp +from .exceptions import PyJWKSetError + + +class JWKSetCache: + def __init__(self, lifespan: float) -> None: + self.jwk_set_with_timestamp: Optional[PyJWTSetWithTimestamp] = None + self.lifespan = lifespan + + def put(self, jwk_set: Optional[Union[PyJWKSet, dict[str, Any]]]) -> None: + """Cache *jwk_set*, or clear the cache when it is ``None``. + + Accepts either an already-parsed ``PyJWKSet`` or the raw JWKS + payload it is parsed from, and always stores it parsed so that + reads never have to parse the keys again. + + :raises PyJWKSetError: If *jwk_set* is neither a ``PyJWKSet`` nor a + JWKS payload containing usable keys. + """ + if jwk_set is None: + # clear cache + self.jwk_set_with_timestamp = None + return + + if not isinstance(jwk_set, PyJWKSet): + if not isinstance(jwk_set, dict): + raise PyJWKSetError("Invalid JWK Set value") + jwk_set = PyJWKSet.from_dict(jwk_set) + + self.jwk_set_with_timestamp = PyJWTSetWithTimestamp(jwk_set) + + def get(self) -> Optional[PyJWKSet]: + if self.jwk_set_with_timestamp is None or self.is_expired(): + return None + + return self.jwk_set_with_timestamp.get_jwk_set() + + def is_expired(self) -> bool: + return ( + self.jwk_set_with_timestamp is not None + and self.lifespan > -1 + and time.monotonic() + > self.jwk_set_with_timestamp.get_timestamp() + self.lifespan + ) diff --git a/venv/lib/python3.12/site-packages/jwt/jwks_client.py b/venv/lib/python3.12/site-packages/jwt/jwks_client.py new file mode 100644 index 0000000..fb3712e --- /dev/null +++ b/venv/lib/python3.12/site-packages/jwt/jwks_client.py @@ -0,0 +1,333 @@ +from __future__ import annotations + +import http.client +import json +import math +import threading +import time +import urllib.request +from functools import lru_cache +from ssl import SSLContext +from typing import Any +from urllib.error import HTTPError, URLError +from urllib.parse import urlparse + +from .api_jwk import PyJWK, PyJWKSet +from .api_jwt import decode_complete as decode_token +from .exceptions import PyJWKClientConnectionError, PyJWKClientError +from .jwk_set_cache import JWKSetCache + + +class _NoRedirectHandler(urllib.request.HTTPRedirectHandler): + def redirect_request( + self, + req: urllib.request.Request, + fp: Any, + code: int, + msg: str, + headers: Any, + newurl: str, + ) -> urllib.request.Request | None: + return None + + +class PyJWKClient: + def __init__( + self, + uri: str, + cache_keys: bool = False, + max_cached_keys: int = 16, + cache_jwk_set: bool = True, + lifespan: float = 300, + headers: dict[str, Any] | None = None, + timeout: float = 30, + ssl_context: SSLContext | None = None, + cooldown_duration: float = 30, + ): + """A client for retrieving signing keys from a JWKS endpoint. + + ``PyJWKClient`` uses a two-tier caching system to avoid unnecessary + network requests: + + **Tier 1 — JWK Set cache** (enabled by default): + Caches the entire JSON Web Key Set response from the endpoint. + Controlled by: + + - ``cache_jwk_set``: Set to ``True`` (the default) to enable this + cache. When enabled, the JWK Set is fetched from the network only + when the cache is empty or expired. + - ``lifespan``: Time in seconds before the cached JWK Set expires. + Defaults to ``300`` (5 minutes). Must be greater than 0. + + Unknown key IDs can trigger one forced refresh after the cooldown + period configured by ``cooldown_duration``. Every successful fetch + starts this cooldown, including the initial fetch and cache-expiry + fetches. A newly rotated key may therefore wait for the cooldown + period before it is fetched; set ``cooldown_duration`` to ``0`` to + disable this behavior. The cooldown is bypassed when + ``cache_jwk_set`` is ``False``. + + **Tier 2 — Signing key cache** (disabled by default): + Caches individual signing keys (looked up by ``kid``) using an LRU + cache with **no time-based expiration**. Keys are evicted only when + the cache reaches its maximum size. Controlled by: + + - ``cache_keys``: Set to ``True`` to enable this cache. + Defaults to ``False``. + - ``max_cached_keys``: Maximum number of signing keys to keep in + the LRU cache. Defaults to ``16``. + + :param uri: The URL of the JWKS endpoint. + :type uri: str + :param cache_keys: Enable the per-key LRU cache (Tier 2). + :type cache_keys: bool + :param max_cached_keys: Max entries in the signing key LRU cache. + :type max_cached_keys: int + :param cache_jwk_set: Enable the JWK Set response cache (Tier 1). + :type cache_jwk_set: bool + :param lifespan: TTL in seconds for the JWK Set cache. + :type lifespan: float + :param headers: Optional HTTP headers to include in requests. + :type headers: dict or None + :param timeout: HTTP request timeout in seconds. + :type timeout: float + :param ssl_context: Optional SSL context for the request. + :type ssl_context: ssl.SSLContext or None + :param cooldown_duration: Minimum time in seconds between forced + refreshes after an unknown key ID. Defaults to ``30``. + :type cooldown_duration: float + """ + if headers is None: + headers = {} + # urllib's default OpenerDirector also handles file://, ftp://, and + # data: URIs. Reject anything that isn't http(s) eagerly so a caller + # passing an attacker-influenced URL (e.g. taken from a `jku` token + # header) can't read local files or reach other unintended schemes. + scheme = urlparse(uri).scheme.lower() + if scheme not in ("http", "https"): + raise PyJWKClientError( + f"Invalid JWKS URI scheme {scheme!r}: only 'http' and 'https' " + f"are supported." + ) + self.uri = uri + self.jwk_set_cache: JWKSetCache | None = None + self.headers = headers + self.timeout = timeout + self.ssl_context = ssl_context + if cooldown_duration < 0: + raise PyJWKClientError( + "Cooldown duration must be greater than or equal to 0, " + f'the input is "{cooldown_duration}"' + ) + if not math.isfinite(cooldown_duration): + raise PyJWKClientError( + f'Cooldown duration must be finite, the input is "{cooldown_duration}"' + ) + self.cooldown_duration = cooldown_duration + self._last_successful_fetch: float | None = None + self._client_lock = threading.RLock() + + if cache_jwk_set: + # Init jwt set cache with default or given lifespan. + # Default lifespan is 300 seconds (5 minutes). + if lifespan <= 0: + raise PyJWKClientError( + f'Lifespan must be greater than 0, the input is "{lifespan}"' + ) + self.jwk_set_cache = JWKSetCache(lifespan) + else: + self.jwk_set_cache = None + + if cache_keys: + # Cache signing keys + get_signing_key = lru_cache(maxsize=max_cached_keys)(self.get_signing_key) + # Ignore mypy (https://github.com/python/mypy/issues/2427) + self.get_signing_key = get_signing_key # type: ignore[method-assign] + + def fetch_data(self) -> Any: + """Fetch the JWK Set from the JWKS endpoint. + + Makes an HTTP request to the configured ``uri`` and returns the + parsed JSON response. If the JWK Set cache is enabled, the + response is stored in the cache. + + :returns: The parsed JWK Set as a dictionary. + :raises PyJWKClientConnectionError: If the HTTP request fails. + :raises PyJWKClientError: If the endpoint does not return a JSON + object. + :raises PyJWKSetError: If the JWK Set cache is enabled and the + response contains no usable keys. + """ + try: + r = urllib.request.Request(url=self.uri, headers=self.headers) + handlers: list[Any] = [_NoRedirectHandler()] + if self.ssl_context is not None: + handlers.append(urllib.request.HTTPSHandler(context=self.ssl_context)) + opener = urllib.request.build_opener(*handlers) + with opener.open(r, timeout=self.timeout) as response: + jwk_set = json.load(response) + except (URLError, TimeoutError, http.client.HTTPException) as e: + if isinstance(e, HTTPError): + e.close() + raise PyJWKClientConnectionError( + f'Fail to fetch data from the url, err: "{e}"' + ) from e + + # Validate the payload before it reaches the cache, so an endpoint + # returning something that isn't a JSON object is reported as such + # rather than as a malformed JWK Set. + jwk_set = self._as_jwk_set_payload(jwk_set) + + # Only update the cache on a successful fetch. Writing in a + # `finally` block with `jwk_set=None` on error clears any + # previously-cached JWKS, turning a transient outage into a cache + # wipe that breaks legitimate auth. + if self.jwk_set_cache is not None: + self.jwk_set_cache.put(jwk_set) + self._last_successful_fetch = time.monotonic() + return jwk_set + + def get_jwk_set(self, refresh: bool = False) -> PyJWKSet: + """Return the JWK Set, using the cache when available. + + :param refresh: Force a fresh fetch from the endpoint, bypassing + the cache. + :type refresh: bool + :returns: The JWK Set. + :rtype: PyJWKSet + :raises PyJWKClientError: If the endpoint does not return a JSON + object. + """ + data = None + fetched = False + if self.jwk_set_cache is not None and not refresh: + data = self.jwk_set_cache.get() + + if data is None: + data = self.fetch_data() + fetched = True + + # A cache hit is already parsed, so serve it as-is. Only a fresh + # fetch reaches the check below, which still matters because + # `fetch_data()` may be overridden by a subclass. + if isinstance(data, PyJWKSet): + jwk_set = data + else: + jwk_set = PyJWKSet.from_dict(self._as_jwk_set_payload(data)) + + # `fetch_data()` caches the payload it received from the endpoint, + # but a subclass may filter or transform it before returning. Cache + # the value actually being returned, so later cache hits serve the + # same key set as this call rather than the pre-transform one. + if fetched and self.jwk_set_cache is not None: + self.jwk_set_cache.put(jwk_set) + + return jwk_set + + @staticmethod + def _as_jwk_set_payload(data: Any) -> dict[str, Any]: + if not isinstance(data, dict): + raise PyJWKClientError("The JWKS endpoint did not return a JSON object") + + return data + + def get_signing_keys(self, refresh: bool = False) -> list[PyJWK]: + """Return all signing keys from the JWK Set. + + Filters the JWK Set to keys whose ``use`` is ``"sig"`` (or + unspecified) and that have a ``kid``. + + :param refresh: Force a fresh fetch from the endpoint, bypassing + the cache. + :type refresh: bool + :returns: A list of signing keys. + :rtype: list[PyJWK] + :raises PyJWKClientError: If no signing keys are found. + """ + jwk_set = self.get_jwk_set(refresh) + return self._get_signing_keys_from_jwk_set(jwk_set) + + @staticmethod + def _get_signing_keys_from_jwk_set(jwk_set: PyJWKSet) -> list[PyJWK]: + signing_keys = [ + jwk_set_key + for jwk_set_key in jwk_set.keys + if jwk_set_key.public_key_use in ["sig", None] and jwk_set_key.key_id + ] + + if not signing_keys: + raise PyJWKClientError("The JWKS endpoint did not contain any signing keys") + + return signing_keys + + def get_signing_key(self, kid: str) -> PyJWK: + """Return the signing key matching the given ``kid``. + + If no match is found in the current JWK Set, the set is refreshed + from the endpoint and the lookup is retried once when the refresh + cooldown permits it. + + :param kid: The key ID to look up. + :type kid: str + :returns: The matching signing key. + :rtype: PyJWK + :raises PyJWKClientError: If no matching key is found after + refreshing. + """ + with self._client_lock: + signing_keys = self.get_signing_keys() + signing_key = self.match_kid(signing_keys, kid) + + if not signing_key: + cooling_down = ( + self.jwk_set_cache is not None + and self._last_successful_fetch is not None + and time.monotonic() - self._last_successful_fetch + < self.cooldown_duration + ) + if not cooling_down: + signing_keys = self.get_signing_keys(refresh=True) + self._last_successful_fetch = time.monotonic() + signing_key = self.match_kid(signing_keys, kid) + + if not signing_key: + raise PyJWKClientError( + f'Unable to find a signing key that matches: "{kid}"' + ) + + return signing_key + + def get_signing_key_from_jwt(self, token: str | bytes) -> PyJWK: + """Return the signing key for a JWT by reading its ``kid`` header. + + Extracts the ``kid`` from the token's unverified header and + delegates to :meth:`get_signing_key`. + + :param token: The encoded JWT. + :type token: str or bytes + :returns: The matching signing key. + :rtype: PyJWK + """ + unverified = decode_token(token, options={"verify_signature": False}) + header = unverified["header"] + return self.get_signing_key(header.get("kid")) + + @staticmethod + def match_kid(signing_keys: list[PyJWK], kid: str) -> PyJWK | None: + """Find a key in *signing_keys* that matches *kid*. + + :param signing_keys: The list of keys to search. + :type signing_keys: list[PyJWK] + :param kid: The key ID to match. + :type kid: str + :returns: The matching key, or ``None`` if not found. + :rtype: PyJWK or None + """ + signing_key = None + + for key in signing_keys: + if key.key_id == kid: + signing_key = key + break + + return signing_key diff --git a/venv/lib/python3.12/site-packages/jwt/py.typed b/venv/lib/python3.12/site-packages/jwt/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/jwt/types.py b/venv/lib/python3.12/site-packages/jwt/types.py new file mode 100644 index 0000000..61bfdf0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jwt/types.py @@ -0,0 +1,69 @@ +from typing import Any, Callable, TypedDict + +JWKDict = dict[str, Any] + +HashlibHash = Callable[..., Any] + + +class SigOptions(TypedDict, total=False): + """Options for PyJWS class (TypedDict). Note that this is a smaller set of options than + for :py:func:`jwt.decode()`.""" + + verify_signature: bool + """verify the JWT cryptographic signature""" + enforce_minimum_key_length: bool + """Default: ``False``. Raise :py:class:`jwt.exceptions.InvalidKeyError` instead of warning when keys are below minimum recommended length.""" + + +class Options(TypedDict, total=False): + """Options for :py:func:`jwt.decode()` and :py:func:`jwt.decode_complete()` (TypedDict). + + .. warning:: + + Some claims, such as ``exp``, ``iat``, ``jti``, ``nbf``, and ``sub``, + will only be verified if present. Please refer to the documentation below + for which ones, and make sure to include them in the ``require`` param + if you want to make sure that they are always present (and therefore always verified + if ``verify_{claim} = True`` for that claim). + """ + + verify_signature: bool + """Default: ``True``. Verify the JWT cryptographic signature.""" + require: list[str] + """Default: ``[]``. List of claims that must be present. + Example: ``require=["exp", "iat", "nbf"]``. + **Only verifies that the claims exists**. Does not verify that the claims are valid.""" + strict_aud: bool + """Default: ``False``. (requires ``verify_aud=True``) Check that the ``aud`` claim is a single value (not a list), and matches ``audience`` exactly.""" + verify_aud: bool + """Default: ``verify_signature``. Check that ``aud`` (audience) claim matches ``audience``.""" + verify_exp: bool + """Default: ``verify_signature``. Check that ``exp`` (expiration) claim value is in the future (if present in payload). """ + verify_iat: bool + """Default: ``verify_signature``. Check that ``iat`` (issued at) claim value is an integer (if present in payload). """ + verify_iss: bool + """Default: ``verify_signature``. Check that ``iss`` (issuer) claim matches ``issuer``. """ + verify_jti: bool + """Default: ``verify_signature``. Check that ``jti`` (JWT ID) claim is a string (if present in payload). """ + verify_nbf: bool + """Default: ``verify_signature``. Check that ``nbf`` (not before) claim value is in the past (if present in payload). """ + verify_sub: bool + """Default: ``verify_signature``. Check that ``sub`` (subject) claim is a string and matches ``subject`` (if present in payload). """ + enforce_minimum_key_length: bool + """Default: ``False``. Raise :py:class:`jwt.exceptions.InvalidKeyError` instead of warning when keys are below minimum recommended length.""" + + +# The only difference between Options and FullOptions is that FullOptions +# required _every_ value to be there; Options doesn't require any +class FullOptions(TypedDict): + verify_signature: bool + require: list[str] + strict_aud: bool + verify_aud: bool + verify_exp: bool + verify_iat: bool + verify_iss: bool + verify_jti: bool + verify_nbf: bool + verify_sub: bool + enforce_minimum_key_length: bool diff --git a/venv/lib/python3.12/site-packages/jwt/utils.py b/venv/lib/python3.12/site-packages/jwt/utils.py new file mode 100644 index 0000000..e51c3e1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jwt/utils.py @@ -0,0 +1,146 @@ +import base64 +import binascii +import re +from typing import Optional, Union + +try: + from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurve + from cryptography.hazmat.primitives.asymmetric.utils import ( + decode_dss_signature, + encode_dss_signature, + ) +except ModuleNotFoundError: + pass + + +def force_bytes(value: Union[bytes, str]) -> bytes: + if isinstance(value, str): + return value.encode("utf-8") + elif isinstance(value, bytes): + return value + else: + raise TypeError("Expected a string or bytes value") + + +def base64url_decode(input: Union[bytes, str]) -> bytes: + input_bytes = force_bytes(input) + + rem = len(input_bytes) % 4 + + if rem > 0: + input_bytes += b"=" * (4 - rem) + + return base64.urlsafe_b64decode(input_bytes) + + +def base64url_encode(input: bytes) -> bytes: + return base64.urlsafe_b64encode(input).replace(b"=", b"") + + +def to_base64url_uint(val: int, *, bit_length: Optional[int] = None) -> bytes: + if val < 0: + raise ValueError("Must be a positive integer") + + int_bytes = bytes_from_int(val, bit_length=bit_length) + + if len(int_bytes) == 0: + int_bytes = b"\x00" + + return base64url_encode(int_bytes) + + +def from_base64url_uint(val: Union[bytes, str]) -> int: + data = base64url_decode(force_bytes(val)) + return int.from_bytes(data, byteorder="big") + + +def number_to_bytes(num: int, num_bytes: int) -> bytes: + padded_hex = "%0*x" % (2 * num_bytes, num) + return binascii.a2b_hex(padded_hex.encode("ascii")) + + +def bytes_to_number(string: bytes) -> int: + return int(binascii.b2a_hex(string), 16) + + +def bytes_from_int(val: int, *, bit_length: Optional[int] = None) -> bytes: + if bit_length is None: + bit_length = val.bit_length() + byte_length = (bit_length + 7) // 8 + + return val.to_bytes(byte_length, "big", signed=False) + + +def der_to_raw_signature(der_sig: bytes, curve: "EllipticCurve") -> bytes: + num_bits = curve.key_size + num_bytes = (num_bits + 7) // 8 + + r, s = decode_dss_signature(der_sig) + + return number_to_bytes(r, num_bytes) + number_to_bytes(s, num_bytes) + + +def raw_to_der_signature(raw_sig: bytes, curve: "EllipticCurve") -> bytes: + num_bits = curve.key_size + num_bytes = (num_bits + 7) // 8 + + if len(raw_sig) != 2 * num_bytes: + raise ValueError("Invalid signature") + + r = bytes_to_number(raw_sig[:num_bytes]) + s = bytes_to_number(raw_sig[num_bytes:]) + + return bytes(encode_dss_signature(r, s)) + + +# Based on https://github.com/hynek/pem/blob/7ad94db26b0bc21d10953f5dbad3acfdfacf57aa/src/pem/_core.py#L224-L252 +_PEMS = { + b"CERTIFICATE", + b"TRUSTED CERTIFICATE", + b"PRIVATE KEY", + b"PUBLIC KEY", + b"ENCRYPTED PRIVATE KEY", + b"OPENSSH PRIVATE KEY", + b"DSA PRIVATE KEY", + b"RSA PRIVATE KEY", + b"RSA PUBLIC KEY", + b"EC PRIVATE KEY", + b"DH PARAMETERS", + b"NEW CERTIFICATE REQUEST", + b"CERTIFICATE REQUEST", + b"SSH2 PUBLIC KEY", + b"SSH2 ENCRYPTED PRIVATE KEY", + b"X509 CRL", +} + +# Accept PEM formatting variants that the cryptography loader accepts, while +# retaining the explicit asymmetric-key marker check. +_PEM_MARKER_RE = re.compile( + b"(?=(----[- ](BEGIN|END) (" + b"|".join(_PEMS) + b")[- ]----))" +) + + +def is_pem_format(key: bytes) -> bool: + begin_markers: dict[bytes, int] = {} + for marker in _PEM_MARKER_RE.finditer(key): + marker_type, label = marker.group(2), marker.group(3) + if marker_type == b"BEGIN": + begin_markers[label] = marker.start(1) + len(marker.group(1)) + elif label in begin_markers and begin_markers[label] < marker.start(1): + return True + return False + + +# Based on https://github.com/pyca/cryptography/blob/bcb70852d577b3f490f015378c75cba74986297b/src/cryptography/hazmat/primitives/serialization/ssh.py#L40-L46 +_SSH_KEY_FORMATS = ( + b"ssh-ed25519", + b"ssh-rsa", + b"ssh-dss", + b"ecdsa-sha2-nistp256", + b"ecdsa-sha2-nistp384", + b"ecdsa-sha2-nistp521", +) + + +def is_ssh_key(key: bytes) -> bool: + return key.startswith(_SSH_KEY_FORMATS) diff --git a/venv/lib/python3.12/site-packages/jwt/warnings.py b/venv/lib/python3.12/site-packages/jwt/warnings.py new file mode 100644 index 0000000..8a41a36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jwt/warnings.py @@ -0,0 +1,11 @@ +class RemovedInPyjwt3Warning(DeprecationWarning): + """Warning for features that will be removed in PyJWT 3.""" + + pass + + +class InsecureKeyLengthWarning(UserWarning): + """Warning emitted when a cryptographic key is shorter than the minimum + recommended length. See :ref:`key-length-validation` for details.""" + + pass diff --git a/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/METADATA b/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/METADATA new file mode 100644 index 0000000..60b88fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/METADATA @@ -0,0 +1,179 @@ +Metadata-Version: 2.4 +Name: mcp +Version: 2.1.1 +Summary: Model Context Protocol SDK +Project-URL: Homepage, https://modelcontextprotocol.io +Project-URL: Documentation, https://py.sdk.modelcontextprotocol.io/ +Project-URL: Repository, https://github.com/modelcontextprotocol/python-sdk +Project-URL: Issues, https://github.com/modelcontextprotocol/python-sdk/issues +Author: Model Context Protocol a Series of LF Projects, LLC. +Maintainer-email: David Soria Parra , Marcelo Trylesinski , Max Isbey , Felix Weinberger +License: MIT +License-File: LICENSE +Keywords: automation,llm,mcp +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.10 +Requires-Dist: anyio>=4.10; python_version >= '3.14' +Requires-Dist: anyio>=4.9; python_version < '3.14' +Requires-Dist: httpx2>=2.5.0 +Requires-Dist: jsonschema>=4.20.0 +Requires-Dist: mcp-types==2.1.1 +Requires-Dist: opentelemetry-api>=1.28.0 +Requires-Dist: pydantic>=2.12.0 +Requires-Dist: pyjwt[crypto]>=2.10.1 +Requires-Dist: python-multipart>=0.0.9 +Requires-Dist: pywin32>=311; sys_platform == 'win32' +Requires-Dist: sse-starlette>=3.0.0 +Requires-Dist: starlette>=0.27; python_version < '3.14' +Requires-Dist: starlette>=0.48.0; python_version >= '3.14' +Requires-Dist: typing-extensions>=4.13.0 +Requires-Dist: typing-inspection>=0.4.1 +Requires-Dist: uvicorn>=0.31.1; sys_platform != 'emscripten' +Provides-Extra: cli +Requires-Dist: python-dotenv>=1.0.0; extra == 'cli' +Requires-Dist: typer>=0.16.0; extra == 'cli' +Provides-Extra: rich +Requires-Dist: rich>=13.9.4; extra == 'rich' +Description-Content-Type: text/markdown + +# MCP Python SDK + +
+ +Python implementation of the Model Context Protocol (MCP) + +[![PyPI][pypi-badge]][pypi-url] +[![MIT licensed][mit-badge]][mit-url] +[![Python Version][python-badge]][python-url] +[![Documentation][docs-badge]][docs-url] +[![Protocol][protocol-badge]][protocol-url] +[![Specification][spec-badge]][spec-url] + +
+ +> [!NOTE] +> **This is v2 of the MCP Python SDK, the current stable release line.** It is a major rework of the SDK, both to support the [2026-07-28 MCP specification](https://modelcontextprotocol.io/specification/2026-07-28) (and every earlier revision) and to fix long-standing architectural issues. Coming from v1? See [What's new in v2](https://py.sdk.modelcontextprotocol.io/whats-new/) for the tour of what changed and the [migration guide](https://py.sdk.modelcontextprotocol.io/migration/) for every breaking change. +> +> **Not ready to migrate?** v1.x lives on the [`v1.x` branch](https://github.com/modelcontextprotocol/python-sdk/tree/v1.x), continues to receive critical bug fixes and security patches, and is documented at . Since `pip install mcp` now installs 2.x, keep a `<2` upper bound on your requirement (for example `mcp>=1.28,<2`) until you've migrated. +> +> Something rough, confusing, or broken? [Open an issue](https://github.com/modelcontextprotocol/python-sdk/issues/new?template=v2-feedback.yaml) or find us in [#python-sdk-dev on the MCP Contributors Discord](https://discord.gg/6CSzBmMkjX). + +## Documentation + +**The documentation lives at .** + +It has a [Get started guide](https://py.sdk.modelcontextprotocol.io/get-started/), [What's new in v2](https://py.sdk.modelcontextprotocol.io/whats-new/), the [API reference](https://py.sdk.modelcontextprotocol.io/api/mcp/), and the [migration guide](https://py.sdk.modelcontextprotocol.io/migration/). + +## What is MCP? + +The [Model Context Protocol](https://modelcontextprotocol.io) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but designed for LLM interactions. With this SDK you can: + +- **Build MCP servers** that expose tools, resources, and prompts to any MCP host +- **Build MCP clients** that connect to any MCP server +- Speak every standard transport: stdio, Streamable HTTP, and SSE + +## Requirements + +Python 3.10+. + +## Installation + +```bash +uv add "mcp[cli]" # or: pip install "mcp[cli]" +``` + +The `cli` extra adds the `mcp` command-line tool (`mcp dev`, `mcp run`, `mcp install`) on top of the SDK; install plain `mcp` if you don't need it. For one-off commands, `uv run --with "mcp[cli]" mcp ...` works without a project. + +## A server in 15 lines + +Create a `server.py`: + + +```python +from mcp.server import MCPServer + +mcp = MCPServer("Demo") + + +@mcp.tool() +def add(a: int, b: int) -> int: + """Add two numbers.""" + return a + b + + +@mcp.resource("greeting://{name}") +def greeting(name: str) -> str: + """Greet someone by name.""" + return f"Hello, {name}!" +``` + +_Full example: [docs_src/index/tutorial001.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/docs_src/index/tutorial001.py)_ + + +That's a complete MCP server: one tool, one templated resource. Open it in the [MCP Inspector](https://github.com/modelcontextprotocol/inspector): + +```bash +uv run mcp dev server.py +``` + +Call `add` with `a=1`, `b=2` and you get `3` back. + +Notice what you did **not** write: no JSON Schema (`a: int, b: int` _is_ the schema), no request parsing, no validation code, no protocol handling. Two type-hinted Python functions and a docstring. + +[Get started](https://py.sdk.modelcontextprotocol.io/get-started/) takes it from here. + +## A client in 10 lines + +The same package is a full MCP **client**. Serve `server.py` over HTTP: + +```bash +uv run mcp run server.py --transport streamable-http +``` + +then point a `Client` at it: + +```python +import asyncio + +from mcp import Client + + +async def main() -> None: + async with Client("http://localhost:8000/mcp") as client: + result = await client.call_tool("add", {"a": 1, "b": 2}) + print(result.structured_content) # {'result': 3} + + +asyncio.run(main()) +``` + +A URL means Streamable HTTP, the transport you deploy. `Client` can also launch a local server as a stdio subprocess or take any custom transport; [Clients](https://py.sdk.modelcontextprotocol.io/client/) has the rest. + +## Contributing + +We are passionate about supporting contributors of all levels of experience and would love to see you get involved in the project. See the [contributing guide](https://github.com/modelcontextprotocol/python-sdk/blob/main/CONTRIBUTING.md) to get started. + +## License + +This project is licensed under the MIT License. See the [LICENSE](https://github.com/modelcontextprotocol/python-sdk/blob/main/LICENSE) file for details. + +[pypi-badge]: https://img.shields.io/pypi/v/mcp.svg +[pypi-url]: https://pypi.org/project/mcp/ +[mit-badge]: https://img.shields.io/pypi/l/mcp.svg +[mit-url]: https://github.com/modelcontextprotocol/python-sdk/blob/main/LICENSE +[python-badge]: https://img.shields.io/pypi/pyversions/mcp.svg +[python-url]: https://www.python.org/downloads/ +[docs-badge]: https://img.shields.io/badge/docs-python--sdk-blue.svg +[docs-url]: https://py.sdk.modelcontextprotocol.io/ +[protocol-badge]: https://img.shields.io/badge/protocol-modelcontextprotocol.io-blue.svg +[protocol-url]: https://modelcontextprotocol.io +[spec-badge]: https://img.shields.io/badge/spec-spec.modelcontextprotocol.io-blue.svg +[spec-url]: https://modelcontextprotocol.io/specification/latest diff --git a/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/RECORD new file mode 100644 index 0000000..40c4110 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/RECORD @@ -0,0 +1,254 @@ +../../../bin/mcp,sha256=d1MmRZKhh5vnLjensRY60YvLPEVUCaV5jjWfD2DI8QE,177 +mcp-2.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +mcp-2.1.1.dist-info/METADATA,sha256=IsEvFa4wP_PYYMbIlEKEPO2Xmfu29PAYz511kReU8mo,7794 +mcp-2.1.1.dist-info/RECORD,, +mcp-2.1.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87 +mcp-2.1.1.dist-info/entry_points.txt,sha256=gnGBbo3yGF0YJNXfZpgkAg1-CQtKxnzxrlA53c8viYA,42 +mcp-2.1.1.dist-info/licenses/LICENSE,sha256=XhPbvB0SD8KgPOzefJFCSuLX3hG2PVje0vRDHiYe5Q0,1071 +mcp/__init__.py,sha256=yYm2cScDTFl5ryD6SimPnb0DsTxSKhlUvI1ZrvsIT5o,3719 +mcp/__pycache__/__init__.cpython-312.pyc,, +mcp/cli/__init__.py,sha256=CIKfYFV5bAEtsFivmJkY7NHihqKlfp-kbX3lRm6DSqE,103 +mcp/cli/__pycache__/__init__.cpython-312.pyc,, +mcp/cli/__pycache__/claude.cpython-312.pyc,, +mcp/cli/__pycache__/cli.cpython-312.pyc,, +mcp/cli/claude.py,sha256=9eH_IY-hlxq4OlOySPLsa7TuKTwMM58fWKXVSQUJQG4,6096 +mcp/cli/cli.py,sha256=pq07xYh-NbckZE5YRsCzJr8Ebr6DF5A0GIkuSw4lAio,15170 +mcp/client/__init__.py,sha256=8WMVUrx02eQuKBXU6tD1eEbGnxRAnqTWhBeGHfBIfDk,1033 +mcp/client/__main__.py,sha256=4siuy244dpMqsVPteG3eA4_EY0-PChS3QDteHXWYi7Y,2413 +mcp/client/__pycache__/__init__.cpython-312.pyc,, +mcp/client/__pycache__/__main__.cpython-312.pyc,, +mcp/client/__pycache__/_input_required.cpython-312.pyc,, +mcp/client/__pycache__/_memory.cpython-312.pyc,, +mcp/client/__pycache__/_probe.cpython-312.pyc,, +mcp/client/__pycache__/_transport.cpython-312.pyc,, +mcp/client/__pycache__/caching.cpython-312.pyc,, +mcp/client/__pycache__/client.cpython-312.pyc,, +mcp/client/__pycache__/context.cpython-312.pyc,, +mcp/client/__pycache__/extension.cpython-312.pyc,, +mcp/client/__pycache__/session.cpython-312.pyc,, +mcp/client/__pycache__/session_group.cpython-312.pyc,, +mcp/client/__pycache__/sse.cpython-312.pyc,, +mcp/client/__pycache__/stdio.cpython-312.pyc,, +mcp/client/__pycache__/streamable_http.cpython-312.pyc,, +mcp/client/__pycache__/subscriptions.cpython-312.pyc,, +mcp/client/_input_required.py,sha256=sVpGcZ0tYuLff0PVWL_EIya4LQ5YOzpc2o7R8nbYgjs,5323 +mcp/client/_memory.py,sha256=nYngTy-aDh97AvTkhMDJohAVVk8SdvakI4Jy-QhlvUM,4938 +mcp/client/_probe.py,sha256=3F8liG_P8Ks-WaQwLVg3iXSuYp8L7td6O9rSU1FiIZI,5392 +mcp/client/_transport.py,sha256=EyPFBefm_GgEu491SKF0Dl1NAj9uKomjXBTGdSTjnUg,695 +mcp/client/auth/__init__.py,sha256=9plDBtxrVeOOXztjjv5JfbsDhwp-YtTQL2kCw7-0h9w,571 +mcp/client/auth/__pycache__/__init__.cpython-312.pyc,, +mcp/client/auth/__pycache__/exceptions.cpython-312.pyc,, +mcp/client/auth/__pycache__/oauth2.cpython-312.pyc,, +mcp/client/auth/__pycache__/utils.cpython-312.pyc,, +mcp/client/auth/exceptions.py,sha256=uUDHX_P0S3R_tt7oAS8ALQrUgOiV2Dgc0QQAdaaBcW8,264 +mcp/client/auth/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +mcp/client/auth/extensions/__pycache__/__init__.cpython-312.pyc,, +mcp/client/auth/extensions/__pycache__/client_credentials.cpython-312.pyc,, +mcp/client/auth/extensions/__pycache__/identity_assertion.cpython-312.pyc,, +mcp/client/auth/extensions/client_credentials.py,sha256=8qDflJFw8My3BU1RDyqh7eQVePv1UKq6SSoqsMa-6Z4,13367 +mcp/client/auth/extensions/identity_assertion.py,sha256=hRGRFe-fmwQ0Wg5TvsIRtlOwAW1Maf9rkZAZG7H1ygk,10785 +mcp/client/auth/oauth2.py,sha256=kS9D9goCO2awXRAqXyMSr_vMtqYmZE8ZWqZznVLIcXw,38993 +mcp/client/auth/utils.py,sha256=sdqg4jkjCveA_ufPp_OjGADursCJJ0J02Etqqe1oK1M,16547 +mcp/client/caching.py,sha256=LI_zvXquqT9gmaH5G1gkPWHjIfdw8qATGgmHoPWc1qg,17358 +mcp/client/client.py,sha256=_3iO8RHECTEFIo9rW4VxYL3SdwXsuymvAwpdyNU_Nl4,43562 +mcp/client/context.py,sha256=kC4AOFHdcrAF13DudyTSnekcxhOa7rabQkpawXbhIQc,136 +mcp/client/extension.py,sha256=YfU1M7MYrMRSsmJJ7tw6BrNxB96MFG-38FkiWHBadZs,8428 +mcp/client/session.py,sha256=8Xs9ET29tMnFIBjUzwzxbikCsFSd1t2A760gghL8i4s,69391 +mcp/client/session_group.py,sha256=pY4ONoN3WdXwfcYrnBmuHBOjxddeofQm3wsbjANbn50,17976 +mcp/client/sse.py,sha256=PMjUSwPQk1A7UzQsK1cYWLJMnCoRELl8gBOaQNI811o,7918 +mcp/client/stdio.py,sha256=vJh5JaPacvTKWczWJ6b8a6JYWC1QUVpTJQOr5tG526s,13827 +mcp/client/streamable_http.py,sha256=bMc_jsfScL8wQzqFxbBaY4Sv3QjVQzaMVBh_lIQOBOU,32909 +mcp/client/subscriptions.py,sha256=5C9MUqbY60E8nArgJmOaFgNnlaDEwfx3sECESN5Wr5Y,11563 +mcp/os/__init__.py,sha256=uqaGIfCF4I0IPeSsEEZMNwZuDF91U0WKAIbDcKzPRLo,43 +mcp/os/__pycache__/__init__.cpython-312.pyc,, +mcp/os/posix/__init__.py,sha256=l_2j8jhhgw69s_-eH4dCDk4QYxNlxKGIpaOCvcbv1Qk,40 +mcp/os/posix/__pycache__/__init__.cpython-312.pyc,, +mcp/os/posix/__pycache__/utilities.cpython-312.pyc,, +mcp/os/posix/utilities.py,sha256=yi36QfVndZKMZs-H1aKG8KS7isQL3E5Y_7h95rkMfVc,2392 +mcp/os/win32/__init__.py,sha256=7xyI7KWpZhDS3omGEBcu3200Q-DjIsS7c-a-vrn28Y4,42 +mcp/os/win32/__pycache__/__init__.cpython-312.pyc,, +mcp/os/win32/__pycache__/utilities.cpython-312.pyc,, +mcp/os/win32/utilities.py,sha256=mOh8d2oUzzJuY_jaAHEZFW061kOuf78n6bbm7x3FNL8,10336 +mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +mcp/server/__init__.py,sha256=jVxuDGAX-cxe4kLz2rg8WvzsURel4NeEE9g6fI6eqsY,318 +mcp/server/__main__.py,sha256=RTnPFUeIO3xPAkEfZBUenQ0vnBcY59TPTvgOfbetVMQ,585 +mcp/server/__pycache__/__init__.cpython-312.pyc,, +mcp/server/__pycache__/__main__.cpython-312.pyc,, +mcp/server/__pycache__/_otel.cpython-312.pyc,, +mcp/server/__pycache__/_streamable_http_modern.cpython-312.pyc,, +mcp/server/__pycache__/apps.cpython-312.pyc,, +mcp/server/__pycache__/caching.cpython-312.pyc,, +mcp/server/__pycache__/connection.cpython-312.pyc,, +mcp/server/__pycache__/context.cpython-312.pyc,, +mcp/server/__pycache__/elicitation.cpython-312.pyc,, +mcp/server/__pycache__/extension.cpython-312.pyc,, +mcp/server/__pycache__/fastmcp.cpython-312.pyc,, +mcp/server/__pycache__/models.cpython-312.pyc,, +mcp/server/__pycache__/request_state.cpython-312.pyc,, +mcp/server/__pycache__/runner.cpython-312.pyc,, +mcp/server/__pycache__/session.cpython-312.pyc,, +mcp/server/__pycache__/sse.cpython-312.pyc,, +mcp/server/__pycache__/stdio.cpython-312.pyc,, +mcp/server/__pycache__/streamable_http.cpython-312.pyc,, +mcp/server/__pycache__/streamable_http_manager.cpython-312.pyc,, +mcp/server/__pycache__/subscriptions.cpython-312.pyc,, +mcp/server/__pycache__/transport_security.cpython-312.pyc,, +mcp/server/__pycache__/validation.cpython-312.pyc,, +mcp/server/_otel.py,sha256=k71jSlD1U7uQjx0wWHkDW9W-wqUvddZIce5sNCrJjms,3359 +mcp/server/_streamable_http_modern.py,sha256=-ChrS2HxTRzzFmpCNtRrCL9Z3Y62P71lipfGx_qKcU8,21944 +mcp/server/apps.py,sha256=srksMEEFBHjtyrZ2S9q7upQ1jhEiuj4fP9hm9gBPOso,9680 +mcp/server/auth/__init__.py,sha256=_JhI3YU6YP6401tYr0L_Fi6rJhUusJTye-xZd0_xCqQ,49 +mcp/server/auth/__pycache__/__init__.cpython-312.pyc,, +mcp/server/auth/__pycache__/errors.cpython-312.pyc,, +mcp/server/auth/__pycache__/json_response.cpython-312.pyc,, +mcp/server/auth/__pycache__/provider.cpython-312.pyc,, +mcp/server/auth/__pycache__/routes.cpython-312.pyc,, +mcp/server/auth/__pycache__/settings.cpython-312.pyc,, +mcp/server/auth/errors.py,sha256=8YjlejJPl-qMjZrbJTDzMPBOAOITn0Da0pC5QWn9QdE,224 +mcp/server/auth/handlers/__init__.py,sha256=KuVuAHZLupFuUBp46a48h3AV9Z6Qp7_IPlxegGiW-xs,56 +mcp/server/auth/handlers/__pycache__/__init__.cpython-312.pyc,, +mcp/server/auth/handlers/__pycache__/authorize.cpython-312.pyc,, +mcp/server/auth/handlers/__pycache__/metadata.cpython-312.pyc,, +mcp/server/auth/handlers/__pycache__/register.cpython-312.pyc,, +mcp/server/auth/handlers/__pycache__/revoke.cpython-312.pyc,, +mcp/server/auth/handlers/__pycache__/token.cpython-312.pyc,, +mcp/server/auth/handlers/authorize.py,sha256=qMcje0xGcZcgFWAV6qTG1w8hEGgaK-l90_igKOrLtvQ,9773 +mcp/server/auth/handlers/metadata.py,sha256=mWPkrs1DfG-Qzq1nZU2HzwxSwk8K_vXe-ZpEDZ0k_QA,853 +mcp/server/auth/handlers/register.py,sha256=f03h4UFVdYGRuUG13-7MpyO4dxbSCeXTa6tRVUE2ObQ,7179 +mcp/server/auth/handlers/revoke.py,sha256=6z9TW1-9oJ08GlRN3prMokzQVyPdLhLaQFC_ndQYCU4,3083 +mcp/server/auth/handlers/token.py,sha256=OCbUZW_joKh7r83gH1qwyxVSoX039jOfxX-VcFpA3Ng,12544 +mcp/server/auth/json_response.py,sha256=EMv3qAH62vRuw8XBHdFU3kYp0S8Jd6DjRGfwc7Jc9XU,377 +mcp/server/auth/middleware/__init__.py,sha256=hzEnzjSR61OBZBrDxL8Om3LLET20teK1HS-pqnqQEVw,40 +mcp/server/auth/middleware/__pycache__/__init__.cpython-312.pyc,, +mcp/server/auth/middleware/__pycache__/auth_context.cpython-312.pyc,, +mcp/server/auth/middleware/__pycache__/bearer_auth.cpython-312.pyc,, +mcp/server/auth/middleware/__pycache__/client_auth.cpython-312.pyc,, +mcp/server/auth/middleware/auth_context.py,sha256=HwdfSIwEKvTfWNn1jtLTQbAYTI5JT3fPbF5MZx_k2X0,1686 +mcp/server/auth/middleware/bearer_auth.py,sha256=fTts5E_9f5C1WYS_RqD1EEnOaDSG9VErGgRmvmmqauA,5172 +mcp/server/auth/middleware/client_auth.py,sha256=z_sJU8Pfn3wnwKNc24zjuvPNr4Z9UlT1U7w7vyh5JLw,5019 +mcp/server/auth/provider.py,sha256=zFxkeqHeU9KhdsauSf1SqkJ96Vjedh2rJwEZEOarWDs,14164 +mcp/server/auth/routes.py,sha256=8NC7XQdrA4TVliOXCvkVDHjVGYCEOOsIH-9-l3MEPfc,10400 +mcp/server/auth/settings.py,sha256=rEIZxhX0Kw69zZm42sxBfDg1vL_Dv1yn9O3ZzfFN7E0,1802 +mcp/server/caching.py,sha256=AYLvl9PXNgPmVnsnUwp8ujbCS7My1xYuXDLTb7LUz3k,3623 +mcp/server/connection.py,sha256=TfQplwP8FEPNb7UwHyQlZWckmEnHhvWAGgqFuUhvLaQ,21924 +mcp/server/context.py,sha256=eWx8i0eed4d7qQb1HdB8y24xV6WAl4kjj7X8f9v-2s4,8546 +mcp/server/elicitation.py,sha256=DTer9VWiXBIygYqIxRr45Nvw3dhK9kzoTA2D_U5Erw4,7015 +mcp/server/extension.py,sha256=xcT7Mqo4CzEUw2EKgJItuaThqxht6Ke4jAznyJ51U5Q,8028 +mcp/server/fastmcp.py,sha256=IsBq7Rj7KuUbvnnHMQpFq1R0I0LZufLIdzWw41WDiwk,769 +mcp/server/lowlevel/__init__.py,sha256=zUMGFpya9MVyLjNBoo7lkMGjPUDLcDsEMhbCfouxCTQ,93 +mcp/server/lowlevel/__pycache__/__init__.cpython-312.pyc,, +mcp/server/lowlevel/__pycache__/helper_types.cpython-312.pyc,, +mcp/server/lowlevel/__pycache__/server.cpython-312.pyc,, +mcp/server/lowlevel/helper_types.py,sha256=LVC0N6JMPynVs4fMJSfBmqKIigHNLsRNhBvbU06rg6A,251 +mcp/server/lowlevel/server.py,sha256=zJLEXraoR4ZDsL3djNCdlOd1qmO0Lzj4Ycckivt3yzA,35992 +mcp/server/mcpserver/__init__.py,sha256=RHnKbXUnqXGTKcVnjhakcZmRgWUM0JZGOBzdWKS-tnw,1463 +mcp/server/mcpserver/__pycache__/__init__.cpython-312.pyc,, +mcp/server/mcpserver/__pycache__/context.cpython-312.pyc,, +mcp/server/mcpserver/__pycache__/exceptions.cpython-312.pyc,, +mcp/server/mcpserver/__pycache__/resolve.cpython-312.pyc,, +mcp/server/mcpserver/__pycache__/server.cpython-312.pyc,, +mcp/server/mcpserver/context.py,sha256=zSXY_g4yQVkMGcIHfXnuqsj9ciJONiRBdKK8ZdQfuRI,16730 +mcp/server/mcpserver/exceptions.py,sha256=FFZMDe3-edu-Glt039xzZTPe2WKix-bFBHFwar2GCx0,3435 +mcp/server/mcpserver/prompts/__init__.py,sha256=4BsMxoYolpoxg74xkkkzCFL8vvdkLVJ5cIPNs1ND1Jo,99 +mcp/server/mcpserver/prompts/__pycache__/__init__.cpython-312.pyc,, +mcp/server/mcpserver/prompts/__pycache__/base.cpython-312.pyc,, +mcp/server/mcpserver/prompts/__pycache__/manager.cpython-312.pyc,, +mcp/server/mcpserver/prompts/base.py,sha256=4opZv7DyDd8PJJlzyMxyvenlngHa57_2aZdm0t-LMbY,8552 +mcp/server/mcpserver/prompts/manager.py,sha256=j3fAmQCAQdbEL98A7Io1uAqwk4PcdR9rp-_ALh-ADaE,2028 +mcp/server/mcpserver/resolve.py,sha256=QBpg55Mz9EQ6pjpjV3nX3SttgC2259OdQkXzyq4ZltY,35985 +mcp/server/mcpserver/resources/__init__.py,sha256=FLGQHcOjvM-7ANl0To68SXUueovIGewXpzpUWpYzhKg,618 +mcp/server/mcpserver/resources/__pycache__/__init__.cpython-312.pyc,, +mcp/server/mcpserver/resources/__pycache__/base.cpython-312.pyc,, +mcp/server/mcpserver/resources/__pycache__/resource_manager.cpython-312.pyc,, +mcp/server/mcpserver/resources/__pycache__/templates.cpython-312.pyc,, +mcp/server/mcpserver/resources/__pycache__/types.cpython-312.pyc,, +mcp/server/mcpserver/resources/base.py,sha256=YkEG24pEbbsYYBTwEGX4qXeBRkpQhSkc_o6YXtY1w-0,1660 +mcp/server/mcpserver/resources/resource_manager.py,sha256=MVuw2vciN9NLHlND_evojeD9cCabZxqArkIrTjx-T9U,5124 +mcp/server/mcpserver/resources/templates.py,sha256=TQ1wLGOuMRhfd5j8yRJTYnHLL8osPoGKH-fSruQ84D4,10177 +mcp/server/mcpserver/resources/types.py,sha256=XuDiRHXOHPKAwyIBT2Xl4jwkent7Ci92uUh6LmbHRdE,8813 +mcp/server/mcpserver/server.py,sha256=_04HVjK0G74OyeiitzhkrM_cNn1gHQ235etPcAz-AC0,58651 +mcp/server/mcpserver/tools/__init__.py,sha256=ZboxhyMJDl87Svjov8YwNYwNZi55P9VhmpTjBZLesnk,96 +mcp/server/mcpserver/tools/__pycache__/__init__.cpython-312.pyc,, +mcp/server/mcpserver/tools/__pycache__/base.cpython-312.pyc,, +mcp/server/mcpserver/tools/__pycache__/tool_manager.cpython-312.pyc,, +mcp/server/mcpserver/tools/base.py,sha256=PQyd--Orn29kHyQ0dSOWNFo_sOtnKOmxBUB4xSt3Zx4,9723 +mcp/server/mcpserver/tools/tool_manager.py,sha256=peEBf_SpzOPJOtN51XjlccMYfJJQopV1DLLhhxeQODg,2805 +mcp/server/mcpserver/utilities/__init__.py,sha256=RU33vSfWMx0-WDN5Sowi2M9h4tchQmDizGTL9-kGPd8,33 +mcp/server/mcpserver/utilities/__pycache__/__init__.cpython-312.pyc,, +mcp/server/mcpserver/utilities/__pycache__/context_injection.cpython-312.pyc,, +mcp/server/mcpserver/utilities/__pycache__/func_metadata.cpython-312.pyc,, +mcp/server/mcpserver/utilities/__pycache__/logging.cpython-312.pyc,, +mcp/server/mcpserver/utilities/__pycache__/types.cpython-312.pyc,, +mcp/server/mcpserver/utilities/context_injection.py,sha256=4pDn-HrSdxzQM4LUTa6gAV9xbBLA4KkkxzwRJo8ar60,2053 +mcp/server/mcpserver/utilities/func_metadata.py,sha256=y0MnVpwL27OvPQThE7XPWJIW26OFVkWvzofACJVghjY,31606 +mcp/server/mcpserver/utilities/logging.py,sha256=pwvj0_Y9Eg9am3UtfEVQLUg1Lk07Iwqu_2pDK7Qtoh8,978 +mcp/server/mcpserver/utilities/types.py,sha256=irHdBNRsBkTDnpWmSpB5abkUQu4J-1rAuvDBQlrhLP8,3430 +mcp/server/models.py,sha256=mODTf-8u7fCzVahQh7PZZ2xvccOmZy4ZdRDrFSvB5M8,474 +mcp/server/request_state.py,sha256=DavmX50RO4XhfjjawctsVD2rbg2K3PRckxshLqTexCk,19798 +mcp/server/runner.py,sha256=vlXLQsvKJhwpl7tPVOKsiS-7XDWTVAnvlRrJ9ztSeyk,39549 +mcp/server/session.py,sha256=YTLKaChdDyog0Z8ONGsR-4ZvLPSFCgmw79RN-XRSByY,20478 +mcp/server/sse.py,sha256=4Sz7CtumtwIh3gTFPxjlcj0Z9-pMSQ97qWRTs65EQYQ,13014 +mcp/server/stdio.py,sha256=Kj5zruVwVqBd74HLrSEEWEN96XtNoMmZMyWxlja8qew,8137 +mcp/server/streamable_http.py,sha256=hexH1Ncnfns1AvIQaSS7pZaFVZHhss4pW4c9U_kzurs,49655 +mcp/server/streamable_http_manager.py,sha256=_m0h4QyQpIIfDNUd74ROhHC0QEXfZtY1kRMpYrjayyI,19672 +mcp/server/subscriptions.py,sha256=V7MIO8YUwaO7P0XRj2UCJuB2JorLHT9jdsnghYTLXZ0,11043 +mcp/server/transport_security.py,sha256=DvG-cFlAce0ki_BDjeQFyyf-mwfCNCE9qRA8quuRenw,7023 +mcp/server/validation.py,sha256=pTtwz1QK15UCZ3PSzFTGhSqiYyfQliLSQwua8kQxMUA,3665 +mcp/shared/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +mcp/shared/__pycache__/__init__.cpython-312.pyc,, +mcp/shared/__pycache__/_callable_inspection.cpython-312.pyc,, +mcp/shared/__pycache__/_compat.cpython-312.pyc,, +mcp/shared/__pycache__/_context_streams.cpython-312.pyc,, +mcp/shared/__pycache__/_httpx_utils.cpython-312.pyc,, +mcp/shared/__pycache__/_otel.cpython-312.pyc,, +mcp/shared/__pycache__/_stream_protocols.cpython-312.pyc,, +mcp/shared/__pycache__/auth.cpython-312.pyc,, +mcp/shared/__pycache__/auth_utils.cpython-312.pyc,, +mcp/shared/__pycache__/context.cpython-312.pyc,, +mcp/shared/__pycache__/direct_dispatcher.cpython-312.pyc,, +mcp/shared/__pycache__/dispatcher.cpython-312.pyc,, +mcp/shared/__pycache__/exceptions.cpython-312.pyc,, +mcp/shared/__pycache__/extension.cpython-312.pyc,, +mcp/shared/__pycache__/inbound.cpython-312.pyc,, +mcp/shared/__pycache__/jsonrpc_dispatcher.cpython-312.pyc,, +mcp/shared/__pycache__/memory.cpython-312.pyc,, +mcp/shared/__pycache__/message.cpython-312.pyc,, +mcp/shared/__pycache__/metadata_utils.cpython-312.pyc,, +mcp/shared/__pycache__/path_security.cpython-312.pyc,, +mcp/shared/__pycache__/peer.cpython-312.pyc,, +mcp/shared/__pycache__/subscriptions.cpython-312.pyc,, +mcp/shared/__pycache__/tool_name_validation.cpython-312.pyc,, +mcp/shared/__pycache__/transport_context.cpython-312.pyc,, +mcp/shared/__pycache__/uri_template.cpython-312.pyc,, +mcp/shared/_callable_inspection.py,sha256=8ODRH9Bls7m_lgPqS0comKAswWRLGHLbSXXO5qaAkpY,867 +mcp/shared/_compat.py,sha256=bzQH1crrW-VNX_qeAokeroLj4F3bqocRXRT9U271QSI,815 +mcp/shared/_context_streams.py,sha256=4OmFYdaxKgtDduD7DnIZXX9ZQcFdo-NqHYgfuVTiejA,3759 +mcp/shared/_httpx_utils.py,sha256=5rfKm1rC4s-qvhOvibqXOMyNYkyZGFlrrvpWm2zhR_Q,2977 +mcp/shared/_otel.py,sha256=1PMkVOc00FvlqxmTgCw_4hDvaqWlZFhtcoGfGU0VOSE,1748 +mcp/shared/_stream_protocols.py,sha256=8FqmXR9NN4rVrevHWnEZL0cHDe8H4ileZiJOWfJGFLc,1470 +mcp/shared/auth.py,sha256=iZr9-k82jonWZyFBQxtqMvg5-eOKfws6bmZUzaJSAOI,12116 +mcp/shared/auth_utils.py,sha256=aX1l1S3pDlE_9r0Fv25I76huJp1wqB25iUROb25xuuc,2881 +mcp/shared/context.py,sha256=r4ybUzYfkt9BzOUpzBwDYPZtJf0fTEOSUxZTSwFA_fQ,3096 +mcp/shared/direct_dispatcher.py,sha256=2FCGiL_t5Y-cK4hwc-CMGuKOqoolZHYK93VxeOiQ334,14610 +mcp/shared/dispatcher.py,sha256=OPHawShcL8SA8JgjFd2_vAru6Jm_Y0z6zTEbRldl0go,10902 +mcp/shared/exceptions.py,sha256=P3hVB1g0LspGyMF89KhlnUpfPh4HSwMulA_Afg-LX64,4225 +mcp/shared/extension.py,sha256=8Xyv-GR0ZB16GTX7mnVqZj9VTl6nfFRAt2o1gE2X_8A,1168 +mcp/shared/inbound.py,sha256=6wnAoGNo8BVTxUBF7PC5ygkxcX4eyDelRDORODb5GCk,25969 +mcp/shared/jsonrpc_dispatcher.py,sha256=gq32ZILKNr3Dzoj7WOmfd4UUlJ1T6rOBgCTvmFF4BS0,38066 +mcp/shared/memory.py,sha256=U-L68iheegxdooC--mxIgEwXolllxVdSSJWWOp19qP4,1479 +mcp/shared/message.py,sha256=UoeMEjzal32I1RmELn0zAj5k7TRluXdHm-JW_iBtHCk,2356 +mcp/shared/metadata_utils.py,sha256=NOwz9Gf_PqVcNSg6RO0juzA0m3onKXnodsXuKTnxvdA,1737 +mcp/shared/path_security.py,sha256=6QRfpL3cKOECpQu79Sf-YplTWGMLgTGkJ-oLLPJkfKE,6226 +mcp/shared/peer.py,sha256=F6L4dc66gzQKDnb9uIdMSRxfda796sbZKK9Ebqzh-as,9797 +mcp/shared/subscriptions.py,sha256=mMSwdFZUpmpCydHuKgwuHb_8VVpXzn4VKwqtvG5Ye5U,4049 +mcp/shared/tool_name_validation.py,sha256=2MQFDVgED5BMQyS0_L24as1Xf-kJNkbExeHiNnvptg8,4535 +mcp/shared/transport_context.py,sha256=W2Mg4wlLD2b97znWAcXs9yBnl0jwhmXCxeHTxfsCkxg,1867 +mcp/shared/uri_template.py,sha256=b3grvBhqaf4QHT1uodxEOg9aUNyOKOeAy3-F8HYXtEU,43857 +mcp/types/__init__.py,sha256=ovVpPdU3IhzC1uzF4qaVUTRHKjg_MHy3_hBy09AhFnY,1259 +mcp/types/__pycache__/__init__.cpython-312.pyc,, +mcp/types/__pycache__/jsonrpc.cpython-312.pyc,, +mcp/types/__pycache__/methods.cpython-312.pyc,, +mcp/types/__pycache__/version.cpython-312.pyc,, +mcp/types/jsonrpc.py,sha256=oClAyfV6VTSqu7x-xekGdT4CZxdeVUDOJ_noz10uIC0,589 +mcp/types/methods.py,sha256=TiilvvUqJzWhRH-ngzMyFEh1Fii8dJg-f_jIWn_gA1U,572 +mcp/types/version.py,sha256=ysQCr4rfyGeVXqZ-4KaCLbdrIwG1FPaVy6mBXOgbXUw,606 diff --git a/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/WHEEL b/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/WHEEL new file mode 100644 index 0000000..b1b94fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.29.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/entry_points.txt new file mode 100644 index 0000000..df7969b --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +mcp = mcp.cli:app [cli] diff --git a/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/licenses/LICENSE new file mode 100644 index 0000000..3d48435 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp-2.1.1.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Anthropic, PBC + +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. diff --git a/venv/lib/python3.12/site-packages/mcp/__init__.py b/venv/lib/python3.12/site-packages/mcp/__init__.py new file mode 100644 index 0000000..28bc470 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/__init__.py @@ -0,0 +1,145 @@ +from mcp_types import ( + CallToolRequest, + ClientCapabilities, + ClientNotification, + ClientRequest, + ClientResult, + CompleteRequest, + CreateMessageRequest, + CreateMessageResult, + CreateMessageResultWithTools, + ErrorData, + GetPromptRequest, + GetPromptResult, + Implementation, + IncludeContext, + InitializedNotification, + InitializeRequest, + InitializeResult, + JSONRPCError, + JSONRPCRequest, + JSONRPCResponse, + ListPromptsRequest, + ListPromptsResult, + ListResourcesRequest, + ListResourcesResult, + ListToolsResult, + LoggingLevel, + LoggingMessageNotification, + Notification, + PingRequest, + ProgressNotification, + PromptsCapability, + ReadResourceRequest, + ReadResourceResult, + Resource, + ResourcesCapability, + ResourceUpdatedNotification, + RootsCapability, + SamplingCapability, + SamplingContent, + SamplingContextCapability, + SamplingMessage, + SamplingMessageContentBlock, + SamplingToolsCapability, + ServerCapabilities, + ServerNotification, + ServerRequest, + ServerResult, + SetLevelRequest, + StopReason, + SubscribeRequest, + Tool, + ToolChoice, + ToolResultContent, + ToolsCapability, + ToolUseContent, + UnsubscribeRequest, +) +from mcp_types import Role as SamplingRole + +# Bind the `mcp.types` submodule on the package, as v1's `from .types import +# ...` did, so `import mcp` followed by `mcp.types.Tool` keeps working. +from . import types as types +from .client._input_required import InputRequiredRoundsExceededError +from .client.client import Client +from .client.session import ClientSession +from .client.session_group import ClientSessionGroup +from .client.stdio import StdioServerParameters, stdio_client +from .server.session import ServerSession +from .server.stdio import stdio_server +from .shared.exceptions import MCPDeprecationWarning, MCPError, UrlElicitationRequiredError +from .shared.uri_template import InvalidUriTemplate, UriTemplate + +__all__ = [ + "CallToolRequest", + "Client", + "ClientCapabilities", + "ClientNotification", + "ClientRequest", + "ClientResult", + "ClientSession", + "ClientSessionGroup", + "CompleteRequest", + "CreateMessageRequest", + "CreateMessageResult", + "CreateMessageResultWithTools", + "ErrorData", + "GetPromptRequest", + "GetPromptResult", + "Implementation", + "IncludeContext", + "InitializeRequest", + "InitializeResult", + "InitializedNotification", + "InputRequiredRoundsExceededError", + "JSONRPCError", + "JSONRPCRequest", + "JSONRPCResponse", + "ListPromptsRequest", + "ListPromptsResult", + "ListResourcesRequest", + "ListResourcesResult", + "ListToolsResult", + "LoggingLevel", + "LoggingMessageNotification", + "MCPDeprecationWarning", + "MCPError", + "Notification", + "PingRequest", + "ProgressNotification", + "PromptsCapability", + "ReadResourceRequest", + "ReadResourceResult", + "Resource", + "ResourcesCapability", + "ResourceUpdatedNotification", + "RootsCapability", + "SamplingCapability", + "SamplingContent", + "SamplingContextCapability", + "SamplingMessage", + "SamplingMessageContentBlock", + "SamplingRole", + "SamplingToolsCapability", + "ServerCapabilities", + "ServerNotification", + "ServerRequest", + "ServerResult", + "ServerSession", + "SetLevelRequest", + "StdioServerParameters", + "StopReason", + "SubscribeRequest", + "Tool", + "ToolChoice", + "ToolResultContent", + "ToolsCapability", + "ToolUseContent", + "UnsubscribeRequest", + "UriTemplate", + "UrlElicitationRequiredError", + "InvalidUriTemplate", + "stdio_client", + "stdio_server", +] diff --git a/venv/lib/python3.12/site-packages/mcp/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..96d96c8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/cli/__init__.py b/venv/lib/python3.12/site-packages/mcp/cli/__init__.py new file mode 100644 index 0000000..daf07c0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/cli/__init__.py @@ -0,0 +1,6 @@ +"""MCP CLI package.""" + +from .cli import app + +if __name__ == "__main__": # pragma: no cover + app() diff --git a/venv/lib/python3.12/site-packages/mcp/cli/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/cli/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..24603c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/cli/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/cli/__pycache__/claude.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/cli/__pycache__/claude.cpython-312.pyc new file mode 100644 index 0000000..f177623 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/cli/__pycache__/claude.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/cli/__pycache__/cli.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/cli/__pycache__/cli.cpython-312.pyc new file mode 100644 index 0000000..306066f Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/cli/__pycache__/cli.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/cli/claude.py b/venv/lib/python3.12/site-packages/mcp/cli/claude.py new file mode 100644 index 0000000..f3ba726 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/cli/claude.py @@ -0,0 +1,170 @@ +"""Claude app integration utilities.""" + +import importlib.metadata +import json +import os +import shutil +import sys +from pathlib import Path +from typing import Any + +from mcp.server.mcpserver.utilities.logging import get_logger + +logger = get_logger(__name__) + + +def mcp_requirement(package: str = "mcp") -> str: + """Requirement string pinning spawned environments to the running SDK version. + + `uv run --with mcp` resolves the requirement in a fresh environment, where + an unpinned `mcp` means the latest stable release — not necessarily the + version the user installed (pre-releases in particular are never selected + without an explicit pin). Source builds carry dev/local version segments + that are not published to PyPI, so they fall back to the unpinned form, + as does a missing distribution (no metadata to pin from). + """ + try: + version = importlib.metadata.version("mcp") + except importlib.metadata.PackageNotFoundError: + return package + if ".dev" in version or "+" in version: + return package + return f"{package}=={version}" + + +def get_claude_config_path() -> Path | None: # pragma: no cover + """Get the Claude config directory based on platform.""" + if sys.platform == "win32": + path = Path(Path.home(), "AppData", "Roaming", "Claude") + elif sys.platform == "darwin": + path = Path(Path.home(), "Library", "Application Support", "Claude") + elif sys.platform.startswith("linux"): + path = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"), "Claude") + else: + return None + + if path.exists(): + return path + return None + + +def get_uv_path() -> str: + """Get the full path to the uv executable.""" + uv_path = shutil.which("uv") + if not uv_path: + logger.error( + "uv executable not found in PATH, falling back to 'uv'. Please ensure uv is installed and in your PATH" + ) + return "uv" # Fall back to just "uv" if not found + return uv_path + + +def update_claude_config( + file_spec: str, + server_name: str, + *, + with_editable: Path | None = None, + with_packages: list[str] | None = None, + env_vars: dict[str, str] | None = None, +) -> bool: + """Add or update an MCP server in Claude's configuration. + + Args: + file_spec: Path to the server file, optionally with :object suffix + server_name: Name for the server in Claude's config + with_editable: Optional directory to install in editable mode + with_packages: Optional list of additional packages to install + env_vars: Optional dictionary of environment variables. These are merged with + any existing variables, with new values taking precedence. + + Raises: + RuntimeError: If Claude Desktop's config directory is not found, indicating + Claude Desktop may not be installed or properly set up. + """ + config_dir = get_claude_config_path() + uv_path = get_uv_path() + if not config_dir: + raise RuntimeError( + "Claude Desktop config directory not found. Please ensure Claude Desktop" + " is installed and has been run at least once to initialize its config." + ) + + config_file = config_dir / "claude_desktop_config.json" + if not config_file.exists(): # pragma: lax no cover + try: + config_file.write_text("{}", encoding="utf-8") + except Exception: + logger.exception( + "Failed to create Claude config file", + extra={ + "config_file": str(config_file), + }, + ) + return False + + try: + config = json.loads(config_file.read_bytes()) + if "mcpServers" not in config: + config["mcpServers"] = {} + + # Always preserve existing env vars and merge with new ones + if server_name in config["mcpServers"] and "env" in config["mcpServers"][server_name]: + existing_env = config["mcpServers"][server_name]["env"] + if env_vars: + # New vars take precedence over existing ones + env_vars = {**existing_env, **env_vars} + else: + env_vars = existing_env + + # Build uv run command + args = ["run", "--frozen"] + + # Collect all packages in a set to deduplicate + packages = {mcp_requirement("mcp[cli]")} + if with_packages: + packages.update(pkg for pkg in with_packages if pkg) + + # Add all packages with --with + for pkg in sorted(packages): + args.extend(["--with", pkg]) + + if with_editable: + args.extend(["--with-editable", str(with_editable)]) + + # Convert file path to absolute before adding to command + # Split off any :object suffix first + # First check if we have a Windows path (e.g., C:\...) + has_windows_drive = len(file_spec) > 1 and file_spec[1] == ":" + + # Split on the last colon, but only if it's not part of the Windows drive letter + if ":" in (file_spec[2:] if has_windows_drive else file_spec): + file_path, server_object = file_spec.rsplit(":", 1) + file_spec = f"{Path(file_path).resolve()}:{server_object}" + else: + file_spec = str(Path(file_spec).resolve()) + + # Add mcp run command + args.extend(["mcp", "run", file_spec]) + + server_config: dict[str, Any] = {"command": uv_path, "args": args} + + # Add environment variables if specified + if env_vars: + server_config["env"] = env_vars + + config["mcpServers"][server_name] = server_config + + config_file.write_text(json.dumps(config, indent=2), encoding="utf-8") + logger.info( + f"Added server '{server_name}' to Claude config", + extra={"config_file": str(config_file)}, + ) + return True + except Exception: # pragma: no cover + logger.exception( + "Failed to update Claude config", + extra={ + "config_file": str(config_file), + }, + ) + return False diff --git a/venv/lib/python3.12/site-packages/mcp/cli/cli.py b/venv/lib/python3.12/site-packages/mcp/cli/cli.py new file mode 100644 index 0000000..203034c --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/cli/cli.py @@ -0,0 +1,486 @@ +"""MCP CLI tools.""" + +import importlib.metadata +import importlib.util +import os +import subprocess +import sys +from pathlib import Path +from typing import Annotated, Any + +from mcp.server import MCPServer +from mcp.server import Server as LowLevelServer + +try: + import typer +except ImportError: # pragma: no cover + print("Error: typer is required. Install with 'pip install mcp[cli]'") + sys.exit(1) + +try: + from mcp.cli import claude + from mcp.server.mcpserver.utilities.logging import get_logger +except ImportError: # pragma: no cover + print("Error: mcp.server is not installed or not in PYTHONPATH") + sys.exit(1) + +try: + import dotenv +except ImportError: # pragma: no cover + dotenv = None + +logger = get_logger("cli") + +app = typer.Typer( + name="mcp", + help="MCP development tools", + add_completion=False, + no_args_is_help=True, # Show help if no args provided +) + + +def _get_npx_command(): + """Get the correct npx command for the current platform.""" + if sys.platform == "win32": + # Try both npx.cmd and npx.exe on Windows + for cmd in ["npx.cmd", "npx.exe", "npx"]: + try: + subprocess.run([cmd, "--version"], check=True, capture_output=True, shell=True) + return cmd + except subprocess.CalledProcessError: + continue + return None + return "npx" # On Unix-like systems, just use npx + + +def _parse_env_var(env_var: str) -> tuple[str, str]: # pragma: no cover + """Parse environment variable string in format KEY=VALUE.""" + if "=" not in env_var: + logger.error(f"Invalid environment variable format: {env_var}. Must be KEY=VALUE") + sys.exit(1) + key, value = env_var.split("=", 1) + return key.strip(), value.strip() + + +def _build_uv_command( + file_spec: str, + with_editable: Path | None = None, + with_packages: list[str] | None = None, +) -> list[str]: + """Build the uv run command that runs an MCP server through mcp run.""" + cmd = ["uv"] + + cmd.extend(["run", "--with", claude.mcp_requirement()]) + + if with_editable: + cmd.extend(["--with-editable", str(with_editable)]) + + if with_packages: + for pkg in with_packages: + if pkg: # pragma: no branch + cmd.extend(["--with", pkg]) + + # Add mcp run command + cmd.extend(["mcp", "run", file_spec]) + return cmd + + +def _parse_file_path(file_spec: str) -> tuple[Path, str | None]: + """Parse a file path that may include a server object specification. + + Args: + file_spec: Path to file, optionally with :object suffix + + Returns: + Tuple of (file_path, server_object) + """ + # First check if we have a Windows path (e.g., C:\...) + has_windows_drive = len(file_spec) > 1 and file_spec[1] == ":" + + # Split on the last colon, but only if it's not part of the Windows drive letter + # and there's actually another colon in the string after the drive letter + if ":" in (file_spec[2:] if has_windows_drive else file_spec): + file_str, server_object = file_spec.rsplit(":", 1) + else: + file_str, server_object = file_spec, None + + # Resolve the file path + file_path = Path(file_str).expanduser().resolve() + if not file_path.exists(): + logger.error(f"File not found: {file_path}") + sys.exit(1) + if not file_path.is_file(): + logger.error(f"Not a file: {file_path}") + sys.exit(1) + + return file_path, server_object + + +def _import_server(file: Path, server_object: str | None = None): # pragma: no cover + """Import an MCP server from a file. + + Args: + file: Path to the file + server_object: Optional object name in format "module:object" or just "object" + + Returns: + The server object + """ + # Add parent directory to Python path so imports can be resolved + file_dir = str(file.parent) + if file_dir not in sys.path: + sys.path.insert(0, file_dir) + + # Import the module + spec = importlib.util.spec_from_file_location("server_module", file) + if not spec or not spec.loader: + logger.error("Could not load module", extra={"file": str(file)}) + sys.exit(1) + + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + def _check_server_object(server_object: Any, object_name: str): + """Helper function to check that the server object is supported + + Args: + server_object: The server object to check. + + Returns: + True if it's supported. + """ + if not isinstance(server_object, MCPServer): + logger.error(f"The server object {object_name} is of type {type(server_object)} (expecting {MCPServer}).") + if isinstance(server_object, LowLevelServer): + logger.warning("Note that only MCPServer is supported. Low level Server class is not yet supported.") + return False + return True + + # If no object specified, try common server names + if not server_object: + # Look for the most common server object names + for name in ["mcp", "server", "app"]: + if hasattr(module, name): + if not _check_server_object(getattr(module, name), f"{file}:{name}"): + logger.error(f"Ignoring object '{file}:{name}' as it's not a valid server object") + continue + return getattr(module, name) + + logger.error( + f"No server object found in {file}. Please either:\n" + "1. Use a standard variable name (mcp, server, or app)\n" + "2. Specify the object name with file:object syntax" + "3. If the server creates the MCPServer object within main() " + " or another function, refactor the MCPServer object to be a " + " global variable named mcp, server, or app.", + extra={"file": str(file)}, + ) + sys.exit(1) + + # Handle module:object syntax + if ":" in server_object: + module_name, object_name = server_object.split(":", 1) + try: + server_module = importlib.import_module(module_name) + server = getattr(server_module, object_name, None) + except ImportError: + logger.error( + f"Could not import module '{module_name}'", + extra={"file": str(file)}, + ) + sys.exit(1) + else: + # Just object name + server = getattr(module, server_object, None) + + if server is None: + logger.error( + f"Server object '{server_object}' not found", + extra={"file": str(file)}, + ) + sys.exit(1) + + if not _check_server_object(server, server_object): + sys.exit(1) + + return server + + +@app.command() +def version() -> None: # pragma: no cover + """Show the MCP version.""" + try: + version = importlib.metadata.version("mcp") + print(f"MCP version {version}") + except importlib.metadata.PackageNotFoundError: + print("MCP version unknown (package not installed)") + sys.exit(1) + + +@app.command() +def dev( + file_spec: str = typer.Argument( + ..., + help="Python file to run, optionally with :object suffix", + ), + with_editable: Annotated[ + Path | None, + typer.Option( + "--with-editable", + "-e", + help="Directory containing pyproject.toml to install in editable mode", + exists=True, + file_okay=False, + resolve_path=True, + ), + ] = None, + with_packages: Annotated[ + list[str], + typer.Option( + "--with", + help="Additional packages to install", + ), + ] = [], +) -> None: # pragma: no cover + """Run an MCP server with the MCP Inspector.""" + file, server_object = _parse_file_path(file_spec) + + logger.debug( + "Starting dev server", + extra={ + "file": str(file), + "server_object": server_object, + "with_editable": str(with_editable) if with_editable else None, + "with_packages": with_packages, + }, + ) + + try: + # Import server to get dependencies + server = _import_server(file, server_object) + if hasattr(server, "dependencies"): + with_packages = list(set(with_packages + server.dependencies)) + + uv_cmd = _build_uv_command(file_spec, with_editable, with_packages) + + # Get the correct npx command + npx_cmd = _get_npx_command() + if not npx_cmd: + logger.error( + "npx not found. Please ensure Node.js and npm are properly installed and added to your system PATH." + ) + sys.exit(1) + + # Run the MCP Inspector command with shell=True on Windows + shell = sys.platform == "win32" + process = subprocess.run( + [npx_cmd, "@modelcontextprotocol/inspector"] + uv_cmd, + check=True, + shell=shell, + env=dict(os.environ.items()), # Copy the environment for subprocess launch + ) + sys.exit(process.returncode) + except subprocess.CalledProcessError as e: + logger.error( + "Dev server failed", + extra={ + "file": str(file), + "error": str(e), + "returncode": e.returncode, + }, + ) + sys.exit(e.returncode) + except FileNotFoundError: + logger.error( + "npx not found. Please ensure Node.js and npm are properly installed " + "and added to your system PATH. You may need to restart your terminal " + "after installation.", + extra={"file": str(file)}, + ) + sys.exit(1) + + +@app.command() +def run( + file_spec: str = typer.Argument( + ..., + help="Python file to run, optionally with :object suffix", + ), + transport: Annotated[ + str | None, + typer.Option( + "--transport", + "-t", + help="Transport protocol to use (stdio, sse, or streamable-http)", + ), + ] = None, +) -> None: # pragma: no cover + """Run an MCP server. + + The server can be specified in two ways: + 1. Module approach: server.py - runs the module directly, expecting a server.run() call. + 2. Import approach: server.py:app - imports and runs the specified server object. + + Note: This command runs the server directly. You are responsible for ensuring + all dependencies are available. + For dependency management, use `mcp install` or `mcp dev` instead. + """ # noqa: E501 + file, server_object = _parse_file_path(file_spec) + + logger.debug( + "Running server", + extra={ + "file": str(file), + "server_object": server_object, + "transport": transport, + }, + ) + + try: + # Import and get server object + server = _import_server(file, server_object) + + # Run the server + kwargs = {} + if transport: + kwargs["transport"] = transport + + server.run(**kwargs) + + except Exception: + logger.exception( + "Failed to run server", + extra={ + "file": str(file), + }, + ) + sys.exit(1) + + +@app.command() +def install( + file_spec: str = typer.Argument( + ..., + help="Python file to run, optionally with :object suffix", + ), + server_name: Annotated[ + str | None, + typer.Option( + "--name", + "-n", + help="Custom name for the server (defaults to server's name attribute or file name)", + ), + ] = None, + with_editable: Annotated[ + Path | None, + typer.Option( + "--with-editable", + "-e", + help="Directory containing pyproject.toml to install in editable mode", + exists=True, + file_okay=False, + resolve_path=True, + ), + ] = None, + with_packages: Annotated[ + list[str], + typer.Option( + "--with", + help="Additional packages to install", + ), + ] = [], + env_vars: Annotated[ + list[str], + typer.Option( + "--env-var", + "-v", + help="Environment variables in KEY=VALUE format", + ), + ] = [], + env_file: Annotated[ + Path | None, + typer.Option( + "--env-file", + "-f", + help="Load environment variables from a .env file", + exists=True, + file_okay=True, + dir_okay=False, + resolve_path=True, + ), + ] = None, +) -> None: # pragma: no cover + """Install an MCP server in the Claude desktop app. + + Environment variables are preserved once added and only updated if new values + are explicitly provided. + """ + file, server_object = _parse_file_path(file_spec) + + logger.debug( + "Installing server", + extra={ + "file": str(file), + "server_name": server_name, + "server_object": server_object, + "with_editable": str(with_editable) if with_editable else None, + "with_packages": with_packages, + }, + ) + + if not claude.get_claude_config_path(): + logger.error("Claude app not found") + sys.exit(1) + + # Try to import server to get its name, but fall back to file name if dependencies + # missing + name = server_name + server = None + if not name: + try: + server = _import_server(file, server_object) + name = server.name + except (ImportError, ModuleNotFoundError) as e: + logger.debug( + "Could not import server (likely missing dependencies), using file name", + extra={"error": str(e)}, + ) + name = file.stem + + # Get server dependencies if available + server_dependencies = getattr(server, "dependencies", []) if server else [] + if server_dependencies: + with_packages = list(set(with_packages + server_dependencies)) + + # Process environment variables if provided + env_dict: dict[str, str] | None = None + if env_file or env_vars: + env_dict = {} + # Load from .env file if specified + if env_file: + if dotenv: + try: + env_dict |= {k: v for k, v in dotenv.dotenv_values(env_file).items() if v is not None} + except (OSError, ValueError): + logger.exception("Failed to load .env file") + sys.exit(1) + else: + logger.error("python-dotenv is not installed. Cannot load .env file.") + sys.exit(1) + + # Add command line environment variables + for env_var in env_vars: + key, value = _parse_env_var(env_var) + env_dict[key] = value + + if claude.update_claude_config( + file_spec, + name, + with_editable=with_editable, + with_packages=with_packages, + env_vars=env_dict, + ): + logger.info(f"Successfully installed {name} in Claude app") + else: + logger.error(f"Failed to install {name} in Claude app") + sys.exit(1) diff --git a/venv/lib/python3.12/site-packages/mcp/client/__init__.py b/venv/lib/python3.12/site-packages/mcp/client/__init__.py new file mode 100644 index 0000000..d6b0704 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/__init__.py @@ -0,0 +1,44 @@ +"""MCP Client module.""" + +from mcp.client._input_required import InputRequiredRoundsExceededError +from mcp.client._transport import Transport +from mcp.client.caching import ( + CacheConfig, + CacheEntry, + CacheKey, + CacheMode, + InMemoryResponseCacheStore, + ResponseCacheStore, +) +from mcp.client.client import Client +from mcp.client.context import ClientRequestContext +from mcp.client.extension import ( + ClaimContext, + ClientExtension, + NotificationBinding, + ResultClaim, + UnexpectedClaimedResult, + advertise, +) +from mcp.client.session import ClientSession, IncomingMessage + +__all__ = [ + "CacheConfig", + "CacheEntry", + "CacheKey", + "CacheMode", + "ClaimContext", + "Client", + "ClientExtension", + "ClientRequestContext", + "ClientSession", + "IncomingMessage", + "InMemoryResponseCacheStore", + "InputRequiredRoundsExceededError", + "NotificationBinding", + "ResponseCacheStore", + "ResultClaim", + "Transport", + "UnexpectedClaimedResult", + "advertise", +] diff --git a/venv/lib/python3.12/site-packages/mcp/client/__main__.py b/venv/lib/python3.12/site-packages/mcp/client/__main__.py new file mode 100644 index 0000000..60e3b02 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/__main__.py @@ -0,0 +1,81 @@ +import argparse +import logging +import sys +import warnings +from functools import partial +from urllib.parse import urlparse + +import anyio +import mcp_types as types + +from mcp.client._transport import ReadStream, WriteStream +from mcp.client.session import ClientSession, IncomingMessage +from mcp.client.sse import sse_client +from mcp.client.stdio import StdioServerParameters, stdio_client +from mcp.shared.message import SessionMessage + +if not sys.warnoptions: + warnings.simplefilter("ignore") + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("client") + + +async def message_handler(message: IncomingMessage) -> None: + if isinstance(message, Exception): + logger.error("Error: %s", message) + return + + logger.info("Received message from server: %s", message) + + +async def run_session( + read_stream: ReadStream[SessionMessage | Exception], + write_stream: WriteStream[SessionMessage], + client_info: types.Implementation | None = None, +): + async with ClientSession( + read_stream, + write_stream, + message_handler=message_handler, + client_info=client_info, + ) as session: + logger.info("Initializing session") + await session.initialize() + logger.info("Initialized") + + +async def main(command_or_url: str, args: list[str], env: list[tuple[str, str]]): + env_dict = dict(env) + + if urlparse(command_or_url).scheme in ("http", "https"): + # Use SSE client for HTTP(S) URLs + async with sse_client(command_or_url) as streams: + await run_session(*streams) + else: + # Use stdio client for commands + server_parameters = StdioServerParameters(command=command_or_url, args=args, env=env_dict) + async with stdio_client(server_parameters) as streams: + await run_session(*streams) + + +def cli(): + parser = argparse.ArgumentParser() + parser.add_argument("command_or_url", help="Command or URL to connect to") + parser.add_argument("args", nargs="*", help="Additional arguments") + parser.add_argument( + "-e", + "--env", + nargs=2, + action="append", + metavar=("KEY", "VALUE"), + help="Environment variables to set. Can be used multiple times.", + default=[], + ) + + args = parser.parse_args() + anyio.run(partial(main, args.command_or_url, args.args, args.env), backend="trio") + + +if __name__ == "__main__": + cli() diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..55398c6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/__main__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000..4d11adc Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/__main__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/_input_required.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/_input_required.cpython-312.pyc new file mode 100644 index 0000000..6263df8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/_input_required.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/_memory.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/_memory.cpython-312.pyc new file mode 100644 index 0000000..9ac6c65 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/_memory.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/_probe.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/_probe.cpython-312.pyc new file mode 100644 index 0000000..8a0d3d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/_probe.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/_transport.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/_transport.cpython-312.pyc new file mode 100644 index 0000000..8299306 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/_transport.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/caching.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/caching.cpython-312.pyc new file mode 100644 index 0000000..4ce9a42 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/caching.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/client.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/client.cpython-312.pyc new file mode 100644 index 0000000..1cb0c94 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/client.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/context.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/context.cpython-312.pyc new file mode 100644 index 0000000..19f8814 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/context.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/extension.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/extension.cpython-312.pyc new file mode 100644 index 0000000..673ce91 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/extension.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/session.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/session.cpython-312.pyc new file mode 100644 index 0000000..d417cdc Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/session.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/session_group.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/session_group.cpython-312.pyc new file mode 100644 index 0000000..e3fbf58 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/session_group.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/sse.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/sse.cpython-312.pyc new file mode 100644 index 0000000..ee9af43 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/sse.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/stdio.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/stdio.cpython-312.pyc new file mode 100644 index 0000000..e7f73de Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/stdio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/streamable_http.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/streamable_http.cpython-312.pyc new file mode 100644 index 0000000..ba978e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/streamable_http.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/__pycache__/subscriptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/subscriptions.cpython-312.pyc new file mode 100644 index 0000000..90f80fd Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/__pycache__/subscriptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/_input_required.py b/venv/lib/python3.12/site-packages/mcp/client/_input_required.py new file mode 100644 index 0000000..fe3f59e --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/_input_required.py @@ -0,0 +1,127 @@ +"""SEP-2322 client-side multi-round-trip driver. + +When a server returns `InputRequiredResult` instead of the normal result of a +`tools/call` / `prompts/get` / `resources/read`, the client fulfils the +embedded `input_requests` (sampling, elicitation, roots) and retries the +original request carrying the responses and the echoed opaque `request_state`. +This module implements that retry loop as a pure function so it can drive any +of the three methods identically; `Client` builds the `dispatch` and `retry` +closures, `ClientSession` stays mechanics-only. +""" + +from __future__ import annotations + +from collections.abc import Awaitable, Callable +from typing import TypeVar + +import anyio +import anyio.abc +from mcp_types import ErrorData, InputRequest, InputRequiredResult, InputResponse, InputResponses + +from mcp.shared.exceptions import MCPError + +DEFAULT_INPUT_REQUIRED_MAX_ROUNDS = 10 +"""Default cap on `InputRequiredResult` retry rounds before the driver gives up. + +Matches the typescript-sdk default; csharp-sdk and go-sdk use the same value +as a hard constant. +""" + +_STATE_ONLY_BACKOFF_INITIAL_SECONDS = 0.05 +"""First sleep when an `InputRequiredResult` carries only `request_state` (no input requests).""" + +_STATE_ONLY_BACKOFF_CAP_SECONDS = 0.25 +"""Upper bound on the state-only backoff sleep; reached after three consecutive state-only legs.""" + + +ResultT = TypeVar("ResultT") + + +class InputRequiredRoundsExceededError(RuntimeError): + """The server kept returning `InputRequiredResult` past the configured `max_rounds`.""" + + def __init__(self, max_rounds: int) -> None: + super().__init__( + f"Server returned InputRequiredResult for more than {max_rounds} rounds; " + "raise input_required_max_rounds on the Client, or use " + "client.session.(..., allow_input_required=True) to drive the loop manually." + ) + self.max_rounds = max_rounds + + +async def run_input_required_driver( + first: InputRequiredResult, + *, + dispatch: Callable[[str, InputRequest], Awaitable[InputResponse | ErrorData]], + retry: Callable[[InputResponses | None, str | None], Awaitable[ResultT | InputRequiredResult]], + max_rounds: int = DEFAULT_INPUT_REQUIRED_MAX_ROUNDS, +) -> ResultT: + """Resolve an `InputRequiredResult` to its terminal result. + + Loops until `retry` returns a non-`InputRequiredResult`, or `max_rounds` is + exhausted. Each round either dispatches all `input_requests` concurrently + and retries with the collected responses, or — when the server sent only + `request_state` — sleeps with exponential backoff (50ms doubling to a 250ms + cap, reset by any leg that carries input requests) and retries empty. + `request_state` is passed through byte-exact and never inspected. + + Args: + first: The `InputRequiredResult` the original call returned. + dispatch: Runs one embedded `InputRequest` through the client's + sampling / elicitation / roots callbacks. Called concurrently per + request key. An `ErrorData` return aborts the loop as an `MCPError`. + retry: Re-issues the original request with the collected responses and + the latest `request_state`. Each call mints a fresh JSON-RPC id. + max_rounds: Cap on retry rounds. + + Raises: + InputRequiredRoundsExceededError: `max_rounds` exhausted. + MCPError: A `dispatch` call returned `ErrorData`. + """ + rounds = 0 + state_only_delay = _STATE_ONLY_BACKOFF_INITIAL_SECONDS + current: ResultT | InputRequiredResult = first + while isinstance(current, InputRequiredResult): + rounds += 1 + if rounds > max_rounds: + raise InputRequiredRoundsExceededError(max_rounds) + if current.input_requests: + state_only_delay = _STATE_ONLY_BACKOFF_INITIAL_SECONDS + responses: InputResponses | None = await _dispatch_all(current.input_requests, dispatch) + else: + await anyio.sleep(state_only_delay) + state_only_delay = min(state_only_delay * 2, _STATE_ONLY_BACKOFF_CAP_SECONDS) + responses = None + current = await retry(responses, current.request_state) + return current + + +async def _dispatch_all( + requests: dict[str, InputRequest], + dispatch: Callable[[str, InputRequest], Awaitable[InputResponse | ErrorData]], +) -> InputResponses: + """Run `dispatch` concurrently for every key, raising `MCPError` on the first `ErrorData`. + + The first task to return `ErrorData` cancels its siblings via the task + group's cancel scope, so a refused input does not wait on a slow peer. + A callback that *raises* propagates as an `ExceptionGroup` like any other + task-group failure. + """ + responses: InputResponses = {} + refused: ErrorData | None = None + + async def run_one(tg: anyio.abc.TaskGroup, key: str, req: InputRequest) -> None: + nonlocal refused + result = await dispatch(key, req) + if isinstance(result, ErrorData): + refused = result + tg.cancel_scope.cancel() + else: + responses[key] = result + + async with anyio.create_task_group() as tg: + for key, req in requests.items(): + tg.start_soon(run_one, tg, key, req) + if refused is not None: + raise MCPError.from_error_data(refused) + return responses diff --git a/venv/lib/python3.12/site-packages/mcp/client/_memory.py b/venv/lib/python3.12/site-packages/mcp/client/_memory.py new file mode 100644 index 0000000..187131e --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/_memory.py @@ -0,0 +1,109 @@ +"""In-memory transport for testing MCP servers without network overhead.""" + +from __future__ import annotations + +from collections.abc import AsyncIterator +from contextlib import AbstractAsyncContextManager, asynccontextmanager +from types import TracebackType +from typing import Any + +import anyio + +from mcp.client._transport import TransportStreams +from mcp.server import Server +from mcp.server.mcpserver import MCPServer +from mcp.shared.memory import create_client_server_memory_streams + +SERVER_SHUTDOWN_GRACE = 2.0 +"""Seconds to wait for the in-process server to exit on EOF before cancelling.""" + + +class InMemoryTransport: + """In-memory transport for testing MCP servers without network overhead. + + This transport starts the server in a background task and provides + streams for client-side communication. The server is automatically + stopped when the context manager exits. + """ + + def __init__(self, server: Server[Any] | MCPServer, *, raise_exceptions: bool = False) -> None: + """Initialize the in-memory transport. + + Args: + server: The MCP server to connect to (Server or MCPServer instance) + raise_exceptions: Whether to raise exceptions from the server + """ + self._server = server + self._raise_exceptions = raise_exceptions + self._cm: AbstractAsyncContextManager[TransportStreams] | None = None + + @asynccontextmanager + async def _connect(self) -> AsyncIterator[TransportStreams]: + """Connect to the server and yield streams for communication.""" + # Unwrap MCPServer to get underlying Server + if isinstance(self._server, MCPServer): + # TODO(Marcelo): Make `lowlevel_server` public. + actual_server: Server[Any] = self._server._lowlevel_server # type: ignore[reportPrivateUsage] + else: + actual_server = self._server + + async with create_client_server_memory_streams() as (client_streams, server_streams): + client_read, client_write = client_streams + server_read, server_write = server_streams + + server_done = anyio.Event() + + async def _run_server() -> None: + try: + await actual_server.run( + server_read, + server_write, + actual_server.create_initialization_options(), + raise_exceptions=self._raise_exceptions, + ) + finally: + server_done.set() + + async with anyio.create_task_group() as tg: + tg.start_soon(_run_server) + + try: + yield client_read, client_write + finally: + # EOF the server (and our own read side) instead of + # cancelling outright. The dispatcher's run() cancels its + # own in-flight handlers on read-stream EOF, so for a + # well-behaved server the task exits naturally and the + # task-group join below is immediate. Cancelling here + # unconditionally would `coro.throw()` into this task, + # which on CPython 3.11 (gh-106749) drops `'call'` trace + # events for the outer await chain and desyncs coverage's + # CTracer past the test frame. + await client_write.aclose() + await server_write.aclose() + # Backstop: the dispatcher exits on EOF, but the server's + # own teardown (lifespan __aexit__, connection.exit_stack + # callbacks) runs after that and is user code. If it never + # completes the join would hang forever, so bound the wait + # and fall back to cancelling. The healthy path returns + # from wait() without the timeout firing, so the cancel is + # never reached and gh-106749 stays avoided. If the cancel + # does fire, the checkpoint at the end of + # `create_client_server_memory_streams` resyncs the tracer. + with anyio.move_on_after(SERVER_SHUTDOWN_GRACE): + await server_done.wait() + if not server_done.is_set(): + tg.cancel_scope.cancel() + + async def __aenter__(self) -> TransportStreams: + """Connect to the server and return streams for communication.""" + self._cm = self._connect() + return await self._cm.__aenter__() + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None + ) -> None: + """Close the transport and stop the server.""" + if self._cm is not None: # pragma: no branch + await self._cm.__aexit__(exc_type, exc_val, exc_tb) + self._cm = None diff --git a/venv/lib/python3.12/site-packages/mcp/client/_probe.py b/venv/lib/python3.12/site-packages/mcp/client/_probe.py new file mode 100644 index 0000000..0e46ae5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/_probe.py @@ -0,0 +1,114 @@ +"""Connect-time era negotiation for ``mode='auto'``. + +The ``server/discover`` probe is sent at the newest modern version. Anything +that is not positive evidence the peer is a modern MCP server falls back to +the legacy ``initialize`` handshake — a *denylist* (only the disjoint-modern +case raises) rather than an allowlist of fallback codes. + +Every ``MCPError`` falls back except ``-32022`` with a disjoint modern-only +``supported`` list. The streamable-HTTP transport already maps HTTP-layer +4xx rejections (no JSON-RPC body) into ``MCPError`` codes, so those reach +the same path. Any non-``MCPError`` exception (network/connection errors, +anyio cancellation) propagates to the caller; an outage or in-process bug +is never an era verdict. + +A successful ``DiscoverResult`` whose ``supportedVersions`` shares no modern +version with this client is treated the same way: the server speaks discover +but advertises only handshake-era versions, which is a legacy advertisement, +not an incompatibility. + +The fallback handshake itself can be answered with ``-32022`` — e.g. a probe +that timed out client-side but succeeded on a slow-starting server locked the +connection modern before the pipelined ``initialize`` arrived. That code is +itself positive modern evidence (it names the server's versions), so it +triggers one re-probe at a mutual version instead of failing the connect. +""" + +from __future__ import annotations + +from typing import Any + +import mcp_types as types +from mcp_types import UNSUPPORTED_PROTOCOL_VERSION +from mcp_types.version import ( + HANDSHAKE_PROTOCOL_VERSIONS, + LATEST_MODERN_VERSION, + MODERN_PROTOCOL_VERSIONS, +) +from pydantic import ValidationError + +from mcp.client.session import ClientSession +from mcp.shared.exceptions import MCPError + + +def _parse_supported(data: Any) -> list[str] | None: + """Pull ``data.supported`` off a -32022 error, or ``None`` if not actionable.""" + try: + return types.UnsupportedProtocolVersionErrorData.model_validate(data).supported + except ValidationError: + return None + + +async def negotiate_auto(session: ClientSession) -> None: + """Drive the ``mode='auto'`` connect-time policy on ``session``. + + Probes ``server/discover`` once (twice if the server names a mutual + modern version via -32022), then either ``adopt()``s the result or falls + back to ``initialize()``. Idempotent only in the sense that one of + ``session.discover_result`` / ``session.initialize_result`` is set on + return. + + Raises: + MCPError: The server is modern-only and shares no version with this + client (-32022 with a disjoint ``supported`` list), or the + fallback handshake failed and one corrective re-probe did too. + Exception: Any transport/network error from the probe propagates as-is. + """ + version = LATEST_MODERN_VERSION + for attempt in range(2): + try: + raw = await session.send_discover(version) + except MCPError as e: + if e.code == UNSUPPORTED_PROTOCOL_VERSION: + supported = _parse_supported(e.error.data) + mutual = [v for v in MODERN_PROTOCOL_VERSIONS if v in (supported or ())] + if mutual and attempt == 0: + version = mutual[-1] + continue + if supported is not None and not any(v in HANDSHAKE_PROTOCOL_VERSIONS for v in supported): + raise # server is modern-only and disjoint — real incompatibility + try: + await session.initialize() # every other rpc-error → legacy (the denylist) + except MCPError as handshake_exc: + if handshake_exc.code != UNSUPPORTED_PROTOCOL_VERSION or attempt != 0: + raise + # -32022 from the handshake is itself modern evidence: a probe + # that timed out client-side but succeeded on the server locked + # the connection modern before this initialize arrived. Re-probe + # once at a version the server names; the era is already + # settled, so the second probe answers without the slow start. + supported = _parse_supported(handshake_exc.error.data) + mutual = [v for v in MODERN_PROTOCOL_VERSIONS if v in (supported or ())] + if not mutual: + raise + version = mutual[-1] + continue + return + # any other exception (httpx2.TransportError, ConnectionError, + # anyio errors) → propagate + try: + result = types.DiscoverResult.model_validate(raw) + except ValidationError: + await session.initialize() # unparseable result → not modern evidence + return + if not any(v in result.supported_versions for v in MODERN_PROTOCOL_VERSIONS): + # A discover-answering server that advertises no modern version + # (go-sdk's stateful streamable default does this) is an explicit + # legacy advertisement: fall back like the -32022 branch above + # instead of letting `adopt()` raise. The ts and go clients fall + # back here too. + await session.initialize() + return + session.adopt(result) + return + raise AssertionError("unreachable") # pragma: no cover — loop body always returns or raises diff --git a/venv/lib/python3.12/site-packages/mcp/client/_transport.py b/venv/lib/python3.12/site-packages/mcp/client/_transport.py new file mode 100644 index 0000000..0163fef --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/_transport.py @@ -0,0 +1,21 @@ +"""Transport protocol for MCP clients.""" + +from __future__ import annotations + +from contextlib import AbstractAsyncContextManager +from typing import Protocol + +from mcp.shared._stream_protocols import ReadStream, WriteStream +from mcp.shared.message import SessionMessage + +__all__ = ["ReadStream", "WriteStream", "Transport", "TransportStreams"] + +TransportStreams = tuple[ReadStream[SessionMessage | Exception], WriteStream[SessionMessage]] + + +class Transport(AbstractAsyncContextManager[TransportStreams], Protocol): + """Protocol for MCP transports. + + A transport is an async context manager that yields read and write streams + for bidirectional communication with an MCP server. + """ diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/__init__.py b/venv/lib/python3.12/site-packages/mcp/client/auth/__init__.py new file mode 100644 index 0000000..a6d6709 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/auth/__init__.py @@ -0,0 +1,22 @@ +"""OAuth2 Authentication implementation for httpx2. + +Implements authorization code flow with PKCE and automatic token refresh. +""" + +from mcp.client.auth.exceptions import OAuthFlowError, OAuthRegistrationError, OAuthTokenError +from mcp.client.auth.oauth2 import ( + OAuthClientProvider, + PKCEParameters, + TokenStorage, +) +from mcp.shared.auth import AuthorizationCodeResult + +__all__ = [ + "AuthorizationCodeResult", + "OAuthClientProvider", + "OAuthFlowError", + "OAuthRegistrationError", + "OAuthTokenError", + "PKCEParameters", + "TokenStorage", +] diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/auth/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e37859e Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/auth/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/auth/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..9b9be41 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/auth/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/__pycache__/oauth2.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/auth/__pycache__/oauth2.cpython-312.pyc new file mode 100644 index 0000000..11482c5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/auth/__pycache__/oauth2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/auth/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..03d13c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/auth/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/exceptions.py b/venv/lib/python3.12/site-packages/mcp/client/auth/exceptions.py new file mode 100644 index 0000000..5ce8777 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/auth/exceptions.py @@ -0,0 +1,10 @@ +class OAuthFlowError(Exception): + """Base exception for OAuth flow errors.""" + + +class OAuthTokenError(OAuthFlowError): + """Raised when token operations fail.""" + + +class OAuthRegistrationError(OAuthFlowError): + """Raised when client registration fails.""" diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/__init__.py b/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..aa1510d Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/__pycache__/client_credentials.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/__pycache__/client_credentials.cpython-312.pyc new file mode 100644 index 0000000..c27eafd Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/__pycache__/client_credentials.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/__pycache__/identity_assertion.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/__pycache__/identity_assertion.cpython-312.pyc new file mode 100644 index 0000000..cf07f34 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/__pycache__/identity_assertion.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/client_credentials.py b/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/client_credentials.py new file mode 100644 index 0000000..29197bb --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/client_credentials.py @@ -0,0 +1,333 @@ +"""OAuth client credential extensions for MCP. + +Provides OAuth providers for machine-to-machine authentication flows: +- ClientCredentialsOAuthProvider: For client_credentials with client_id + client_secret +- PrivateKeyJWTOAuthProvider: For client_credentials with private_key_jwt authentication + (typically using a pre-built JWT from workload identity federation) +""" + +import time +from collections.abc import Awaitable, Callable +from typing import Any, Literal +from uuid import uuid4 + +import httpx2 +import jwt +from pydantic import BaseModel, Field + +from mcp.client.auth import OAuthClientProvider, OAuthFlowError, TokenStorage +from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata + + +class ClientCredentialsOAuthProvider(OAuthClientProvider): + """OAuth provider for client_credentials grant with client_id + client_secret. + + This provider sets client_info directly, bypassing dynamic client registration. + Use this when you already have client credentials (client_id and client_secret). + + Example: + ```python + provider = ClientCredentialsOAuthProvider( + server_url="https://api.example.com", + storage=my_token_storage, + client_id="my-client-id", + client_secret="my-client-secret", + ) + ``` + """ + + def __init__( + self, + server_url: str, + storage: TokenStorage, + client_id: str, + client_secret: str, + token_endpoint_auth_method: Literal["client_secret_basic", "client_secret_post"] = "client_secret_basic", + scope: str | None = None, + ) -> None: + """Initialize client_credentials OAuth provider. + + Args: + server_url: The MCP server URL. + storage: Token storage implementation. + client_id: The OAuth client ID. + client_secret: The OAuth client secret. + token_endpoint_auth_method: Authentication method for token endpoint. + Either "client_secret_basic" (default) or "client_secret_post". + scope: Optional space-separated list of scopes to request. + """ + # Build minimal client_metadata for the base class + client_metadata = OAuthClientMetadata( + redirect_uris=None, + grant_types=["client_credentials"], + token_endpoint_auth_method=token_endpoint_auth_method, + scope=scope, + ) + super().__init__(server_url, client_metadata, storage, None, None) + # Store client_info to be set during _initialize - no dynamic registration needed + self._fixed_client_info = OAuthClientInformationFull( + redirect_uris=None, + client_id=client_id, + client_secret=client_secret, + grant_types=["client_credentials"], + token_endpoint_auth_method=token_endpoint_auth_method, + scope=scope, + ) + + async def _initialize(self) -> None: + """Load stored tokens and set pre-configured client_info.""" + self.context.current_tokens = await self.context.storage.get_tokens() + self.context.client_info = self._fixed_client_info + self._initialized = True + + async def _perform_authorization(self) -> httpx2.Request: + """Perform client_credentials authorization.""" + return await self._exchange_token_client_credentials() + + async def _exchange_token_client_credentials(self) -> httpx2.Request: + """Build token exchange request for client_credentials grant.""" + token_data: dict[str, Any] = { + "grant_type": "client_credentials", + } + + headers: dict[str, str] = {"Content-Type": "application/x-www-form-urlencoded"} + + # Use standard auth methods (client_secret_basic, client_secret_post, none) + token_data, headers = self.context.prepare_token_auth(token_data, headers) + + if self.context.should_include_resource_param(self.context.protocol_version): + token_data["resource"] = self.context.get_resource_url() + + if self.context.client_metadata.scope: + token_data["scope"] = self.context.client_metadata.scope + + token_url = self._get_token_endpoint() + return httpx2.Request("POST", token_url, data=token_data, headers=headers) + + +def static_assertion_provider(token: str) -> Callable[[str], Awaitable[str]]: + """Create an assertion provider that returns a static JWT token. + + Use this when you have a pre-built JWT (e.g., from workload identity federation) + that doesn't need the audience parameter. + + Example: + ```python + provider = PrivateKeyJWTOAuthProvider( + server_url="https://api.example.com", + storage=my_token_storage, + client_id="my-client-id", + assertion_provider=static_assertion_provider(my_prebuilt_jwt), + ) + ``` + + Args: + token: The pre-built JWT assertion string. + + Returns: + An async callback suitable for use as an assertion_provider. + """ + + async def provider(audience: str) -> str: + return token + + return provider + + +class SignedJWTParameters(BaseModel): + """Parameters for creating SDK-signed JWT assertions. + + Use `create_assertion_provider()` to create an assertion provider callback + for use with `PrivateKeyJWTOAuthProvider`. + + Example: + ```python + jwt_params = SignedJWTParameters( + issuer="my-client-id", + subject="my-client-id", + signing_key=private_key_pem, + ) + provider = PrivateKeyJWTOAuthProvider( + server_url="https://api.example.com", + storage=my_token_storage, + client_id="my-client-id", + assertion_provider=jwt_params.create_assertion_provider(), + ) + ``` + """ + + issuer: str = Field(description="Issuer for JWT assertions (typically client_id).") + subject: str = Field(description="Subject identifier for JWT assertions (typically client_id).") + signing_key: str = Field(description="Private key for JWT signing (PEM format).") + signing_algorithm: str = Field(default="RS256", description="Algorithm for signing JWT assertions.") + lifetime_seconds: int = Field(default=300, description="Lifetime of generated JWT in seconds.") + additional_claims: dict[str, Any] | None = Field(default=None, description="Additional claims.") + + def create_assertion_provider(self) -> Callable[[str], Awaitable[str]]: + """Create an assertion provider callback for use with PrivateKeyJWTOAuthProvider. + + Returns: + An async callback that takes the audience (authorization server issuer URL) + and returns a signed JWT assertion. + """ + + async def provider(audience: str) -> str: + now = int(time.time()) + claims: dict[str, Any] = { + "iss": self.issuer, + "sub": self.subject, + "aud": audience, + "exp": now + self.lifetime_seconds, + "iat": now, + "jti": str(uuid4()), + } + if self.additional_claims: + claims.update(self.additional_claims) + + return jwt.encode(claims, self.signing_key, algorithm=self.signing_algorithm) + + return provider + + +class PrivateKeyJWTOAuthProvider(OAuthClientProvider): + """OAuth provider for client_credentials grant with private_key_jwt authentication. + + Uses RFC 7523 Section 2.2 for client authentication via JWT assertion. + + The JWT assertion's audience MUST be the authorization server's issuer identifier + (per RFC 7523bis security updates). The `assertion_provider` callback receives + this audience value and must return a JWT with that audience. + + **Option 1: Pre-built JWT via Workload Identity Federation** + + In production scenarios, the JWT assertion is typically obtained from a workload + identity provider (e.g., GCP, AWS IAM, Azure AD): + + ```python + async def get_workload_identity_token(audience: str) -> str: + # Fetch JWT from your identity provider + # The JWT's audience must match the provided audience parameter + return await fetch_token_from_identity_provider(audience=audience) + + provider = PrivateKeyJWTOAuthProvider( + server_url="https://api.example.com", + storage=my_token_storage, + client_id="my-client-id", + assertion_provider=get_workload_identity_token, + ) + ``` + + **Option 2: Static pre-built JWT** + + If you have a static JWT that doesn't need the audience parameter: + + ```python + provider = PrivateKeyJWTOAuthProvider( + server_url="https://api.example.com", + storage=my_token_storage, + client_id="my-client-id", + assertion_provider=static_assertion_provider(my_prebuilt_jwt), + ) + ``` + + **Option 3: SDK-signed JWT (for testing/simple setups)** + + For testing or simple deployments, use `SignedJWTParameters.create_assertion_provider()`: + + ```python + jwt_params = SignedJWTParameters( + issuer="my-client-id", + subject="my-client-id", + signing_key=private_key_pem, + ) + provider = PrivateKeyJWTOAuthProvider( + server_url="https://api.example.com", + storage=my_token_storage, + client_id="my-client-id", + assertion_provider=jwt_params.create_assertion_provider(), + ) + ``` + """ + + def __init__( + self, + server_url: str, + storage: TokenStorage, + client_id: str, + assertion_provider: Callable[[str], Awaitable[str]], + scope: str | None = None, + ) -> None: + """Initialize private_key_jwt OAuth provider. + + Args: + server_url: The MCP server URL. + storage: Token storage implementation. + client_id: The OAuth client ID. + assertion_provider: Async callback that takes the audience (authorization + server's issuer identifier) and returns a JWT assertion. Use + `SignedJWTParameters.create_assertion_provider()` for SDK-signed JWTs, + `static_assertion_provider()` for pre-built JWTs, or provide your own + callback for workload identity federation. + scope: Optional space-separated list of scopes to request. + """ + # Build minimal client_metadata for the base class + client_metadata = OAuthClientMetadata( + redirect_uris=None, + grant_types=["client_credentials"], + token_endpoint_auth_method="private_key_jwt", + scope=scope, + ) + super().__init__(server_url, client_metadata, storage, None, None) + self._assertion_provider = assertion_provider + # Store client_info to be set during _initialize - no dynamic registration needed + self._fixed_client_info = OAuthClientInformationFull( + redirect_uris=None, + client_id=client_id, + grant_types=["client_credentials"], + token_endpoint_auth_method="private_key_jwt", + scope=scope, + ) + + async def _initialize(self) -> None: + """Load stored tokens and set pre-configured client_info.""" + self.context.current_tokens = await self.context.storage.get_tokens() + self.context.client_info = self._fixed_client_info + self._initialized = True + + async def _perform_authorization(self) -> httpx2.Request: + """Perform client_credentials authorization with private_key_jwt.""" + return await self._exchange_token_client_credentials() + + async def _add_client_authentication_jwt(self, *, token_data: dict[str, Any]) -> None: + """Add JWT assertion for client authentication to token endpoint parameters.""" + if not self.context.oauth_metadata: + raise OAuthFlowError("Missing OAuth metadata for private_key_jwt flow") # pragma: no cover + + # Audience MUST be the issuer identifier of the authorization server + # https://datatracker.ietf.org/doc/html/draft-ietf-oauth-rfc7523bis-01 + audience = str(self.context.oauth_metadata.issuer) + assertion = await self._assertion_provider(audience) + + # RFC 7523 Section 2.2: client authentication via JWT + token_data["client_assertion"] = assertion + token_data["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" + + async def _exchange_token_client_credentials(self) -> httpx2.Request: + """Build token exchange request for client_credentials grant with private_key_jwt.""" + token_data: dict[str, Any] = { + "grant_type": "client_credentials", + } + + headers: dict[str, str] = {"Content-Type": "application/x-www-form-urlencoded"} + + # Add JWT client authentication (RFC 7523 Section 2.2) + await self._add_client_authentication_jwt(token_data=token_data) + + if self.context.should_include_resource_param(self.context.protocol_version): + token_data["resource"] = self.context.get_resource_url() + + if self.context.client_metadata.scope: + token_data["scope"] = self.context.client_metadata.scope + + token_url = self._get_token_endpoint() + return httpx2.Request("POST", token_url, data=token_data, headers=headers) diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/identity_assertion.py b/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/identity_assertion.py new file mode 100644 index 0000000..17738a7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/auth/extensions/identity_assertion.py @@ -0,0 +1,213 @@ +"""SEP-990 Identity Assertion Authorization Grant (RFC 7523 jwt-bearer) client provider. + +`IdentityAssertionOAuthProvider` is the client side of SEP-990 leg 2: it presents an Identity +Assertion Authorization Grant (ID-JAG) - a signed JWT issued by the enterprise identity provider - +to the MCP authorization server's token endpoint using the RFC 7523 jwt-bearer grant +(`grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer`, ID-JAG as `assertion`), and receives an +MCP access token. + +The authorization server is configuration, not discovery. SEP-990's trust model is the inverse of +the default OAuth client's: the AS issuer is supplied at construction, authorization-server metadata +is fetched from that issuer's own RFC 8414 well-known, and the resource server is never asked which +AS to use - so it cannot redirect the ID-JAG or client secret elsewhere. There is no protected +resource metadata fetch, no dynamic client registration, and no server-driven scope selection. + +Obtaining the ID-JAG (logging into the IdP and the leg-1 token exchange against it) is +deployment-specific and out of scope for the SDK. The caller supplies it through the +`assertion_provider` callback, which receives the configured issuer (the `aud` the ID-JAG must +carry) and the MCP server's resource identifier (the `resource` claim it must carry, per ext-auth +section 4.3), and returns the ID-JAG. +""" + +import base64 +import time +from collections.abc import AsyncGenerator, Awaitable, Callable +from typing import Literal +from urllib.parse import quote, urlsplit + +import anyio +import httpx2 + +from mcp.client.auth import OAuthFlowError, OAuthTokenError, TokenStorage +from mcp.client.auth.utils import ( + build_oauth_authorization_server_metadata_discovery_urls, + create_oauth_metadata_request, + extract_field_from_www_auth, + extract_scope_from_www_auth, + handle_auth_metadata_response, + handle_token_response_scopes, + union_scopes, + validate_metadata_issuer, +) +from mcp.shared.auth import JWT_BEARER_GRANT_TYPE, OAuthClientInformationFull, OAuthToken +from mcp.shared.auth_utils import calculate_token_expiry, resource_url_from_server_url + +_DEFAULT_PORTS = {"https": 443, "http": 80} + + +def _origin(url: str) -> tuple[str, str, int | None]: + """Return the (scheme, host, port) origin of a URL for same-origin comparison. + + The port is normalized to the scheme's default so an explicit `:443`/`:80` compares equal to the + same origin written without a port. + """ + parsed = urlsplit(url) + port = parsed.port if parsed.port is not None else _DEFAULT_PORTS.get(parsed.scheme) + return (parsed.scheme, parsed.hostname or "", port) + + +class IdentityAssertionOAuthProvider(httpx2.Auth): + """`httpx2.Auth` for the SEP-990 ID-JAG flow (RFC 7523 jwt-bearer grant) against a configured AS. + + The authorization server `issuer` is fixed at construction; metadata is fetched from its + RFC 8414 well-known and the ID-JAG and client secret are sent only to that issuer's token + endpoint. The resource server is never consulted for AS selection. The ID-JAG is fetched lazily + from `assertion_provider` so a fresh assertion is used on each exchange. + + Example: + ```python + async def fetch_id_jag(audience: str, resource: str) -> str: + # `audience` is the configured issuer (the ID-JAG `aud`); `resource` is the MCP + # server's identifier (the ID-JAG `resource` claim). Obtaining the ID-JAG from the + # enterprise IdP is deployment-specific and not handled by the SDK. + return await my_idp.issue_id_jag(audience=audience, resource=resource) + + + provider = IdentityAssertionOAuthProvider( + server_url="https://mcp.example.com/mcp", + storage=my_token_storage, + client_id="my-client-id", + client_secret="my-client-secret", + issuer="https://auth.example.com", + assertion_provider=fetch_id_jag, + ) + ``` + """ + + requires_response_body = True + + def __init__( + self, + server_url: str, + storage: TokenStorage, + client_id: str, + client_secret: str, + issuer: str, + assertion_provider: Callable[[str, str], Awaitable[str]], + scope: str | None = None, + token_endpoint_auth_method: Literal["client_secret_basic", "client_secret_post"] = "client_secret_post", + ) -> None: + """Initialize the identity-assertion OAuth provider. + + Args: + server_url: The MCP server URL. + storage: Token storage implementation. + client_id: The OAuth client ID registered with the MCP authorization server. + client_secret: The client secret. SEP-990 section 5.1 requires a confidential client. + issuer: The issuer identifier of the MCP authorization server this client is provisioned + for. Authorization-server metadata is fetched from this issuer's well-known and the + ID-JAG and secret are sent only to its token endpoint. + assertion_provider: Async callback taking `(audience, resource)` - the configured issuer + and the MCP server's resource identifier - and returning the ID-JAG. + scope: Optional space-separated list of scopes to request. + token_endpoint_auth_method: Confidential-client auth method, either `client_secret_post` + (default) or `client_secret_basic`. + """ + if not client_secret: + raise ValueError("client_secret is required: SEP-990 mandates a confidential client") + if not issuer: + raise ValueError("issuer is required: the authorization server is configuration, not discovery") + self._resource = resource_url_from_server_url(server_url) + self._storage = storage + self._issuer = issuer + self._assertion_provider = assertion_provider + self._scope = scope + self._client = OAuthClientInformationFull( + client_id=client_id, + client_secret=client_secret, + redirect_uris=None, + grant_types=[JWT_BEARER_GRANT_TYPE], + token_endpoint_auth_method=token_endpoint_auth_method, + issuer=issuer, + ) + self._token_endpoint: str | None = None + self._tokens: OAuthToken | None = None + self._expiry: float | None = None + self._lock = anyio.Lock() + self._initialized = False + + def _build_token_request(self, scope: str | None, assertion: str) -> httpx2.Request: + """Build the RFC 7523 jwt-bearer token request, applying confidential-client auth.""" + assert self._token_endpoint is not None + assert self._client.client_id is not None and self._client.client_secret is not None + data: dict[str, str] = { + "grant_type": JWT_BEARER_GRANT_TYPE, + "assertion": assertion, + "client_id": self._client.client_id, + "resource": self._resource, + } + if scope: + data["scope"] = scope + headers = {"Content-Type": "application/x-www-form-urlencoded"} + if self._client.token_endpoint_auth_method == "client_secret_basic": + # RFC 6749 section 2.3.1: URL-encode each part, then base64 the colon-joined pair. + encoded_id = quote(self._client.client_id, safe="") + encoded_secret = quote(self._client.client_secret, safe="") + credentials = base64.b64encode(f"{encoded_id}:{encoded_secret}".encode()).decode() + headers["Authorization"] = f"Basic {credentials}" + else: + data["client_secret"] = self._client.client_secret + return httpx2.Request("POST", self._token_endpoint, data=data, headers=headers) + + async def async_auth_flow(self, request: httpx2.Request) -> AsyncGenerator[httpx2.Request, httpx2.Response]: + async with self._lock: + if not self._initialized: + self._tokens = await self._storage.get_tokens() + self._expiry = calculate_token_expiry(self._tokens.expires_in) if self._tokens else None + self._initialized = True + + if self._tokens and (self._expiry is None or time.time() <= self._expiry): + request.headers["Authorization"] = f"Bearer {self._tokens.access_token}" + response = yield request + + if response.status_code == 401: + scope_to_request = self._scope + elif response.status_code == 403 and extract_field_from_www_auth(response, "error") == "insufficient_scope": + scope_to_request = union_scopes(self._scope, extract_scope_from_www_auth(response)) + else: + return + + # Discover ASM from the configured issuer's well-known. The RS is not consulted: both + # arguments are the issuer, so even the helper's legacy fallback resolves there. + if self._token_endpoint is None: + for url in build_oauth_authorization_server_metadata_discovery_urls(self._issuer, self._issuer): + asm_response = yield create_oauth_metadata_request(url) + ok, asm = await handle_auth_metadata_response(asm_response) + if not ok: + break + if asm is not None: + validate_metadata_issuer(asm, self._issuer) + token_endpoint = str(asm.token_endpoint) + if _origin(token_endpoint) != _origin(self._issuer): + raise OAuthFlowError( + f"Token endpoint {token_endpoint} is not on the configured issuer origin {self._issuer}" + ) + self._token_endpoint = token_endpoint + break + if self._token_endpoint is None: + raise OAuthFlowError(f"No authorization server metadata at configured issuer {self._issuer}") + + assertion = await self._assertion_provider(self._issuer, self._resource) + token_response = yield self._build_token_request(scope_to_request, assertion) + if token_response.status_code != 200: + body = (await token_response.aread()).decode(errors="replace") + raise OAuthTokenError(f"Token exchange failed ({token_response.status_code}): {body}") + tokens = await handle_token_response_scopes(token_response) + if tokens.scope is None: + tokens.scope = scope_to_request + self._tokens = tokens + self._expiry = calculate_token_expiry(tokens.expires_in) + await self._storage.set_tokens(tokens) + + request.headers["Authorization"] = f"Bearer {tokens.access_token}" + yield request diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/oauth2.py b/venv/lib/python3.12/site-packages/mcp/client/auth/oauth2.py new file mode 100644 index 0000000..7dc62b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/auth/oauth2.py @@ -0,0 +1,785 @@ +"""OAuth2 Authentication implementation for httpx2. + +Implements authorization code flow with PKCE and automatic token refresh. +""" + +import base64 +import hashlib +import logging +import secrets +import string +import time +from collections.abc import AsyncGenerator, Awaitable, Callable +from dataclasses import dataclass, field +from typing import Any, Protocol, get_args +from urllib.parse import quote, urlencode, urljoin, urlparse + +import anyio +import httpx2 +from mcp_types.version import is_version_at_least +from pydantic import BaseModel, Field, ValidationError + +from mcp.client.auth.exceptions import OAuthFlowError, OAuthRegistrationError, OAuthTokenError +from mcp.client.auth.utils import ( + build_oauth_authorization_server_metadata_discovery_urls, + build_protected_resource_metadata_discovery_urls, + create_client_info_from_metadata_url, + create_client_registration_request, + create_oauth_metadata_request, + credentials_match_issuer, + extract_field_from_www_auth, + extract_resource_metadata_from_www_auth, + extract_scope_from_www_auth, + get_client_metadata_scopes, + handle_auth_metadata_response, + handle_protected_resource_response, + handle_registration_response, + handle_token_response_scopes, + is_valid_client_metadata_url, + should_use_client_metadata_url, + union_scopes, + validate_authorization_response_iss, + validate_metadata_issuer, +) +from mcp.shared.auth import ( + AuthorizationCodeResult, + OAuthClientInformationFull, + OAuthClientMetadata, + OAuthMetadata, + OAuthToken, + ProtectedResourceMetadata, + TokenEndpointAuthMethod, +) +from mcp.shared.auth_utils import ( + calculate_token_expiry, + check_resource_allowed, + resource_url_from_server_url, +) +from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER + +logger = logging.getLogger(__name__) + +# Methods a registered client's record may carry without a token request being an error, +# derived from the set the SDK is willing to request so the two cannot drift. `None`/"none" +# send no client secret. `private_key_jwt` sends none from here either: only +# `PrivateKeyJWTOAuthProvider` signs the assertion, and only in its client-credentials +# exchange, so its inherited refresh path must pass through here without raising - a refresh +# the server then rejects falls back to a fresh client-credentials exchange, which signs. +# Anything else is a method no client here can apply. +_KNOWN_TOKEN_ENDPOINT_AUTH_METHODS: tuple[str | None, ...] = (None, *get_args(TokenEndpointAuthMethod)) + +# Methods that authenticate the token request with the minted `client_secret`; a +# registration assigning one is only usable if the server issued that secret. +_SECRET_TOKEN_ENDPOINT_AUTH_METHODS = ("client_secret_post", "client_secret_basic") + +# Methods a registration completed by the authorization-code flow can act on. That flow +# authenticates the token request with the minted client secret (or nothing); it holds no key +# to sign a `private_key_jwt` assertion, so a server assigning that method has registered a +# client this flow cannot use. `PrivateKeyJWTOAuthProvider` never registers dynamically. +_REGISTRATION_USABLE_TOKEN_ENDPOINT_AUTH_METHODS: tuple[str | None, ...] = tuple( + method for method in _KNOWN_TOKEN_ENDPOINT_AUTH_METHODS if method != "private_key_jwt" +) + + +def check_registration_usable(client_info: OAuthClientInformationFull) -> None: + """Confirm a registration this flow completed is one it can act on. + + RFC 7591 §3.2.1 lets the authorization server replace requested metadata and leaves it to + the client to "check the values in the response to determine if the registration is + sufficient for use". Two substitutions make the minted credentials unusable, and both are + judged here - before the record is persisted or any interactive authorization begins - + rather than surfacing later as an opaque failure at the token endpoint: a token-endpoint + auth method the authorization-code flow cannot apply (one it does not implement, or + `private_key_jwt`, whose assertion this flow has no key to sign), and a secret-based + method the flow could apply but for which the server issued no `client_secret`. + + Raises: + OAuthRegistrationError: The server registered the client with a + `token_endpoint_auth_method` this flow cannot apply, or with a secret-based + method but no `client_secret`. + """ + method = client_info.token_endpoint_auth_method + if method not in _REGISTRATION_USABLE_TOKEN_ENDPOINT_AUTH_METHODS: + raise OAuthRegistrationError( + f"Authorization server registered the client with unsupported token_endpoint_auth_method {method!r}" + ) + if method in _SECRET_TOKEN_ENDPOINT_AUTH_METHODS and client_info.client_secret is None: + raise OAuthRegistrationError( + f"Authorization server registered the client for {method!r} but issued no client_secret" + ) + + +class PKCEParameters(BaseModel): + """PKCE (Proof Key for Code Exchange) parameters.""" + + code_verifier: str = Field(..., min_length=43, max_length=128) + code_challenge: str = Field(..., min_length=43, max_length=128) + + @classmethod + def generate(cls) -> "PKCEParameters": + """Generate new PKCE parameters.""" + code_verifier = "".join(secrets.choice(string.ascii_letters + string.digits + "-._~") for _ in range(128)) + digest = hashlib.sha256(code_verifier.encode()).digest() + code_challenge = base64.urlsafe_b64encode(digest).decode().rstrip("=") + return cls(code_verifier=code_verifier, code_challenge=code_challenge) + + +class TokenStorage(Protocol): + """Protocol for token storage implementations.""" + + async def get_tokens(self) -> OAuthToken | None: + """Get stored tokens.""" + ... + + async def set_tokens(self, tokens: OAuthToken) -> None: + """Store tokens.""" + ... + + async def get_client_info(self) -> OAuthClientInformationFull | None: + """Get stored client information.""" + ... + + async def set_client_info(self, client_info: OAuthClientInformationFull) -> None: + """Store client information.""" + ... + + +@dataclass +class OAuthContext: + """OAuth flow context.""" + + server_url: str + client_metadata: OAuthClientMetadata + storage: TokenStorage + redirect_handler: Callable[[str], Awaitable[None]] | None + callback_handler: Callable[[], Awaitable[AuthorizationCodeResult]] | None + client_metadata_url: str | None = None + + # Discovered metadata + protected_resource_metadata: ProtectedResourceMetadata | None = None + oauth_metadata: OAuthMetadata | None = None + auth_server_url: str | None = None + protocol_version: str | None = None + + # Client registration + client_info: OAuthClientInformationFull | None = None + + # Token management + current_tokens: OAuthToken | None = None + token_expiry_time: float | None = None + + # State + lock: anyio.Lock = field(default_factory=anyio.Lock) + + def get_authorization_base_url(self, server_url: str) -> str: + """Extract base URL by removing path component.""" + parsed = urlparse(server_url) + return f"{parsed.scheme}://{parsed.netloc}" + + def update_token_expiry(self, token: OAuthToken) -> None: + """Update token expiry time using shared util function.""" + self.token_expiry_time = calculate_token_expiry(token.expires_in) + + def is_token_valid(self) -> bool: + """Check if current token is valid.""" + return bool( + self.current_tokens + and self.current_tokens.access_token + and (not self.token_expiry_time or time.time() <= self.token_expiry_time) + ) + + def can_refresh_token(self) -> bool: + """Check if token can be refreshed.""" + return bool(self.current_tokens and self.current_tokens.refresh_token and self.client_info) + + def clear_tokens(self) -> None: + """Clear current tokens.""" + self.current_tokens = None + self.token_expiry_time = None + + def get_resource_url(self) -> str: + """Get resource URL for RFC 8707. + + Uses PRM resource if it's a valid parent, otherwise uses canonical server URL. + """ + resource = resource_url_from_server_url(self.server_url) + + # If PRM provides a resource that's a valid parent, use it + if self.protected_resource_metadata and self.protected_resource_metadata.resource: + prm_resource = str(self.protected_resource_metadata.resource) + if check_resource_allowed(requested_resource=resource, configured_resource=prm_resource): + resource = prm_resource + + return resource + + def should_include_resource_param(self, protocol_version: str | None = None) -> bool: + """Determine if the resource parameter should be included in OAuth requests. + + Returns True if: + - Protected resource metadata is available, OR + - MCP-Protocol-Version header is 2025-06-18 or later + """ + # If we have protected resource metadata, include the resource param + if self.protected_resource_metadata is not None: + return True + + # If no protocol version provided, don't include resource param + if not protocol_version: + return False + + return is_version_at_least(protocol_version, "2025-06-18") + + def prepare_token_auth( + self, data: dict[str, str], headers: dict[str, str] | None = None + ) -> tuple[dict[str, str], dict[str, str]]: + """Prepare authentication for token requests. + + Args: + data: The form data to send + headers: Optional headers dict to update + + Returns: + Tuple of (updated_data, updated_headers) + + Raises: + OAuthTokenError: The client record carries a `token_endpoint_auth_method` this + client does not know. A dynamic registration assigning an unusable method is + rejected earlier, by `check_registration_usable`; this fires for a stored or + pre-registered record that reaches a token request with such a method. + """ + if headers is None: + headers = {} # pragma: no cover + + if not self.client_info: + return data, headers + + auth_method = self.client_info.token_endpoint_auth_method + + if auth_method == "client_secret_basic" and self.client_info.client_secret: + # URL-encode client ID and secret per RFC 6749 Section 2.3.1 + encoded_id = quote(self.client_info.client_id, safe="") + encoded_secret = quote(self.client_info.client_secret, safe="") + credentials = f"{encoded_id}:{encoded_secret}" + encoded_credentials = base64.b64encode(credentials.encode()).decode() + headers["Authorization"] = f"Basic {encoded_credentials}" + # Don't include client_secret in body for basic auth + data = {k: v for k, v in data.items() if k != "client_secret"} + elif auth_method == "client_secret_post" and self.client_info.client_secret: + # Include client_id and client_secret in request body (RFC 6749 §2.3.1) + data["client_id"] = self.client_info.client_id + data["client_secret"] = self.client_info.client_secret + elif auth_method not in _KNOWN_TOKEN_ENDPOINT_AUTH_METHODS: + raise OAuthTokenError(f"Registered client uses unsupported token_endpoint_auth_method {auth_method!r}") + # For "none" (or absent), don't add any client_secret; "private_key_jwt" adds its + # assertion in the provider that implements it, not here. + + return data, headers + + +class OAuthClientProvider(httpx2.Auth): + """OAuth2 authentication for httpx2. + + Handles OAuth flow with automatic client registration and token storage. + """ + + requires_response_body = True + + def __init__( + self, + server_url: str, + client_metadata: OAuthClientMetadata, + storage: TokenStorage, + redirect_handler: Callable[[str], Awaitable[None]] | None = None, + callback_handler: Callable[[], Awaitable[AuthorizationCodeResult]] | None = None, + client_metadata_url: str | None = None, + validate_resource_url: Callable[[str, str | None], Awaitable[None]] | None = None, + ): + """Initialize OAuth2 authentication. + + Args: + server_url: The MCP server URL. + client_metadata: OAuth client metadata for registration. + storage: Token storage implementation. + redirect_handler: Handler for authorization redirects. + callback_handler: Handler for authorization callbacks. + client_metadata_url: URL-based client ID. When provided and the server + advertises client_id_metadata_document_supported=True, this URL will be + used as the client_id instead of performing dynamic client registration. + Must be a valid HTTPS URL with a non-root pathname. + validate_resource_url: Optional callback to override resource URL validation. + Called with (server_url, prm_resource) where prm_resource is the resource + from Protected Resource Metadata (or None if not present). If not provided, + default validation rejects mismatched resources per RFC 8707. + + Raises: + ValueError: If client_metadata_url is provided but not a valid HTTPS URL + with a non-root pathname. + """ + # Validate client_metadata_url if provided + if client_metadata_url is not None and not is_valid_client_metadata_url(client_metadata_url): + raise ValueError( + f"client_metadata_url must be a valid HTTPS URL with a non-root pathname, got: {client_metadata_url}" + ) + + self.context = OAuthContext( + server_url=server_url, + client_metadata=client_metadata, + storage=storage, + redirect_handler=redirect_handler, + callback_handler=callback_handler, + client_metadata_url=client_metadata_url, + ) + self._validate_resource_url_callback = validate_resource_url + self._initialized = False + + async def _handle_protected_resource_response(self, response: httpx2.Response) -> bool: + """Handle protected resource metadata discovery response. + + Per SEP-985, supports fallback when discovery fails at one URL. + + Returns: + True if metadata was successfully discovered, False if we should try next URL + """ + if response.status_code == 200: + try: + content = await response.aread() + metadata = ProtectedResourceMetadata.model_validate_json(content) + self.context.protected_resource_metadata = metadata + if metadata.authorization_servers: # pragma: no branch + self.context.auth_server_url = str(metadata.authorization_servers[0]) + return True + + except ValidationError: # pragma: no cover + # Invalid metadata - try next URL + logger.warning(f"Invalid protected resource metadata at {response.request.url}") + return False + elif response.status_code == 404: # pragma: no cover + # Not found - try next URL in fallback chain + logger.debug(f"Protected resource metadata not found at {response.request.url}, trying next URL") + return False + else: + # Other error - fail immediately + raise OAuthFlowError( + f"Protected Resource Metadata request failed: {response.status_code}" + ) # pragma: no cover + + async def _perform_authorization(self) -> httpx2.Request: + """Perform the authorization flow.""" + auth_code, code_verifier = await self._perform_authorization_code_grant() + token_request = await self._exchange_token_authorization_code(auth_code, code_verifier) + return token_request + + async def _perform_authorization_code_grant(self) -> tuple[str, str]: + """Perform the authorization redirect and get auth code.""" + if self.context.client_metadata.redirect_uris is None: + raise OAuthFlowError("No redirect URIs provided for authorization code grant") # pragma: no cover + if not self.context.redirect_handler: + raise OAuthFlowError("No redirect handler provided for authorization code grant") # pragma: no cover + if not self.context.callback_handler: + raise OAuthFlowError("No callback handler provided for authorization code grant") # pragma: no cover + + if self.context.oauth_metadata and self.context.oauth_metadata.authorization_endpoint: + auth_endpoint = str(self.context.oauth_metadata.authorization_endpoint) + else: + auth_base_url = self.context.get_authorization_base_url(self.context.server_url) + auth_endpoint = urljoin(auth_base_url, "/authorize") + + if not self.context.client_info: + raise OAuthFlowError("No client info available for authorization") # pragma: no cover + + # Generate PKCE parameters + pkce_params = PKCEParameters.generate() + state = secrets.token_urlsafe(32) + + auth_params = { + "response_type": "code", + "client_id": self.context.client_info.client_id, + "redirect_uri": str(self.context.client_metadata.redirect_uris[0]), + "state": state, + "code_challenge": pkce_params.code_challenge, + "code_challenge_method": "S256", + } + + # Only include resource param if conditions are met + if self.context.should_include_resource_param(self.context.protocol_version): + auth_params["resource"] = self.context.get_resource_url() # RFC 8707 + + if self.context.client_metadata.scope: # pragma: no branch + auth_params["scope"] = self.context.client_metadata.scope + + # OIDC requires prompt=consent when offline_access is requested + # https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess + if "offline_access" in self.context.client_metadata.scope.split(): + auth_params["prompt"] = "consent" + + authorization_url = f"{auth_endpoint}?{urlencode(auth_params)}" + await self.context.redirect_handler(authorization_url) + + # Wait for callback + result = await self.context.callback_handler() + + if result.state is None or not secrets.compare_digest(result.state, state): + raise OAuthFlowError(f"State parameter mismatch: {result.state} != {state}") + + # RFC 9207: validate the authorization-response issuer + validate_authorization_response_iss(result.iss, self.context.oauth_metadata) + + if not result.code: + raise OAuthFlowError("No authorization code received") + + # Return auth code and code verifier for token exchange + return result.code, pkce_params.code_verifier + + def _get_token_endpoint(self) -> str: + if self.context.oauth_metadata and self.context.oauth_metadata.token_endpoint: + token_url = str(self.context.oauth_metadata.token_endpoint) + else: + auth_base_url = self.context.get_authorization_base_url(self.context.server_url) + token_url = urljoin(auth_base_url, "/token") + return token_url + + async def _exchange_token_authorization_code(self, auth_code: str, code_verifier: str) -> httpx2.Request: + """Build token exchange request for authorization_code flow.""" + if self.context.client_metadata.redirect_uris is None: + raise OAuthFlowError("No redirect URIs provided for authorization code grant") # pragma: no cover + if not self.context.client_info: + raise OAuthFlowError("Missing client info") # pragma: no cover + + token_url = self._get_token_endpoint() + token_data: dict[str, Any] = { + "grant_type": "authorization_code", + "code": auth_code, + "redirect_uri": str(self.context.client_metadata.redirect_uris[0]), + "client_id": self.context.client_info.client_id, + "code_verifier": code_verifier, + } + + # Only include resource param if conditions are met + if self.context.should_include_resource_param(self.context.protocol_version): + token_data["resource"] = self.context.get_resource_url() # RFC 8707 + + # Prepare authentication based on preferred method + headers = {"Content-Type": "application/x-www-form-urlencoded"} + token_data, headers = self.context.prepare_token_auth(token_data, headers) + + return httpx2.Request("POST", token_url, data=token_data, headers=headers) + + async def _handle_token_response(self, response: httpx2.Response) -> None: + """Handle token exchange response.""" + if response.status_code not in {200, 201}: + body = await response.aread() + body_text = body.decode("utf-8") + raise OAuthTokenError(f"Token exchange failed ({response.status_code}): {body_text}") + + # Parse and validate response with scope validation + token_response = await handle_token_response_scopes(response) + + # RFC 6749 §5.1: an omitted scope means the granted scope equals the requested + # scope. Record it explicitly so the persisted token is self-describing — the + # SEP-2350 step-up union reads it after a restart, when client_metadata.scope + # has reverted to its constructor value. + if token_response.scope is None: + token_response.scope = self.context.client_metadata.scope + + # Store tokens in context + self.context.current_tokens = token_response + self.context.update_token_expiry(token_response) + await self.context.storage.set_tokens(token_response) + + async def _refresh_token(self) -> httpx2.Request: + """Build token refresh request.""" + if not self.context.current_tokens or not self.context.current_tokens.refresh_token: + raise OAuthTokenError("No refresh token available") # pragma: no cover + + if not self.context.client_info or not self.context.client_info.client_id: + raise OAuthTokenError("No client info available") # pragma: no cover + + if self.context.oauth_metadata and self.context.oauth_metadata.token_endpoint: + token_url = str(self.context.oauth_metadata.token_endpoint) + else: + auth_base_url = self.context.get_authorization_base_url(self.context.server_url) + token_url = urljoin(auth_base_url, "/token") + + refresh_data: dict[str, str] = { + "grant_type": "refresh_token", + "refresh_token": self.context.current_tokens.refresh_token, + "client_id": self.context.client_info.client_id, + } + + # Only include resource param if conditions are met + if self.context.should_include_resource_param(self.context.protocol_version): + refresh_data["resource"] = self.context.get_resource_url() # RFC 8707 + + # Prepare authentication based on preferred method + headers = {"Content-Type": "application/x-www-form-urlencoded"} + refresh_data, headers = self.context.prepare_token_auth(refresh_data, headers) + + return httpx2.Request("POST", token_url, data=refresh_data, headers=headers) + + async def _handle_refresh_response(self, response: httpx2.Response) -> bool: + """Handle token refresh response. Returns True if successful.""" + if response.status_code != 200: + logger.warning(f"Token refresh failed: {response.status_code}") + self.context.clear_tokens() + return False + + try: + content = await response.aread() + token_response = OAuthToken.model_validate_json(content) + + # RFC 6749 §6: a refresh response may omit scope (unchanged) and refresh_token + # (the AS does not rotate). Carry both forward so the persisted token stays + # self-describing for the SEP-2350 step-up union and the next expiry can + # still refresh instead of forcing a full re-authorization. + prior = self.context.current_tokens + if token_response.scope is None and prior is not None: + token_response.scope = prior.scope + if token_response.refresh_token is None and prior is not None: + token_response.refresh_token = prior.refresh_token + + self.context.current_tokens = token_response + self.context.update_token_expiry(token_response) + await self.context.storage.set_tokens(token_response) + + return True + except ValidationError: # pragma: no cover + logger.exception("Invalid refresh response") + self.context.clear_tokens() + return False + + async def _initialize(self) -> None: + """Load stored tokens and client info.""" + self.context.current_tokens = await self.context.storage.get_tokens() + self.context.client_info = await self.context.storage.get_client_info() + self._initialized = True + + def _add_auth_header(self, request: httpx2.Request) -> None: + """Add authorization header to request if we have valid tokens.""" + if self.context.current_tokens and self.context.current_tokens.access_token: # pragma: no branch + request.headers["Authorization"] = f"Bearer {self.context.current_tokens.access_token}" + + async def _handle_oauth_metadata_response(self, response: httpx2.Response) -> None: + content = await response.aread() + metadata = OAuthMetadata.model_validate_json(content) + self.context.oauth_metadata = metadata + + async def _validate_resource_match(self, prm: ProtectedResourceMetadata) -> None: + """Validate that PRM resource matches the server URL per RFC 8707.""" + prm_resource = str(prm.resource) if prm.resource else None + + if self._validate_resource_url_callback is not None: + await self._validate_resource_url_callback(self.context.server_url, prm_resource) + return + + if not prm_resource: + return # pragma: no cover + default_resource = resource_url_from_server_url(self.context.server_url) + if not check_resource_allowed(requested_resource=default_resource, configured_resource=prm_resource): + raise OAuthFlowError(f"Protected resource {prm_resource} does not match expected {default_resource}") + + async def async_auth_flow(self, request: httpx2.Request) -> AsyncGenerator[httpx2.Request, httpx2.Response]: + """httpx2 auth flow integration.""" + async with self.context.lock: + if not self._initialized: + await self._initialize() + + # Capture protocol version from request headers + self.context.protocol_version = request.headers.get(MCP_PROTOCOL_VERSION_HEADER) + + if not self.context.is_token_valid() and self.context.can_refresh_token(): + # Try to refresh token + refresh_request = await self._refresh_token() + refresh_response = yield refresh_request + + if not await self._handle_refresh_response(refresh_response): + # Refresh failed, need full re-authentication + self._initialized = False + + if self.context.is_token_valid(): + self._add_auth_header(request) + + response = yield request + + if response.status_code == 401: + # Perform full OAuth flow + try: + # OAuth flow must be inline due to generator constraints + www_auth_resource_metadata_url = extract_resource_metadata_from_www_auth(response) + + # Step 1: Discover protected resource metadata (SEP-985 with fallback support) + prm_discovery_urls = build_protected_resource_metadata_discovery_urls( + www_auth_resource_metadata_url, self.context.server_url + ) + + for url in prm_discovery_urls: # pragma: no branch + discovery_request = create_oauth_metadata_request(url) + + discovery_response = yield discovery_request # sending request + + prm = await handle_protected_resource_response(discovery_response) + if prm: + # Validate PRM resource matches server URL (RFC 8707) + await self._validate_resource_match(prm) + self.context.protected_resource_metadata = prm + + # todo: try all authorization_servers to find the OASM + assert ( + len(prm.authorization_servers) > 0 + ) # this is always true as authorization_servers has a min length of 1 + + self.context.auth_server_url = str(prm.authorization_servers[0]) + break + else: + logger.debug(f"Protected resource metadata discovery failed: {url}") + + # SEP-2352: stored credentials are bound to the issuer that registered them. + # If the authorization server changed, drop them (and the old tokens) so the + # flow re-registers instead of presenting another server's credentials. + if ( + self.context.client_info is not None + and self.context.auth_server_url is not None + and not credentials_match_issuer( + self.context.client_info, self.context.auth_server_url, self.context.client_metadata_url + ) + ): + logger.debug("Authorization server changed; discarding bound credentials and re-registering") + self.context.client_info = None + self.context.clear_tokens() + # Any cached AS metadata is for the old server; drop it so a failed + # rediscovery cannot leak the old registration/token endpoints into Step 4. + self.context.oauth_metadata = None + + asm_discovery_urls = build_oauth_authorization_server_metadata_discovery_urls( + self.context.auth_server_url, self.context.server_url + ) + + # Step 2: Discover OAuth Authorization Server Metadata (OASM) (with fallback for legacy servers) + for url in asm_discovery_urls: # pragma: no branch + oauth_metadata_request = create_oauth_metadata_request(url) + oauth_metadata_response = yield oauth_metadata_request + + ok, asm = await handle_auth_metadata_response(oauth_metadata_response) + if not ok: + break + if ok and asm: + # SEP-2468: metadata issuer must match the discovery issuer + if self.context.auth_server_url is not None: + validate_metadata_issuer(asm, self.context.auth_server_url) + self.context.oauth_metadata = asm + break + else: + logger.debug(f"OAuth metadata discovery failed: {url}") + + # SEP-2352: on the legacy no-PRM path the issuer is only known after ASM + # discovery, so re-evaluate the binding here using the discovered metadata + # issuer (mirroring the bound_issuer fallback in Step 4). + if ( + self.context.client_info is not None + and self.context.auth_server_url is None + and self.context.oauth_metadata is not None + and not credentials_match_issuer( + self.context.client_info, + str(self.context.oauth_metadata.issuer), + self.context.client_metadata_url, + ) + ): + logger.debug("Authorization server changed; discarding bound credentials and re-registering") + self.context.client_info = None + self.context.clear_tokens() + + # Step 3: Apply scope selection strategy + self.context.client_metadata.scope = get_client_metadata_scopes( + extract_scope_from_www_auth(response), + self.context.protected_resource_metadata, + self.context.oauth_metadata, + self.context.client_metadata.grant_types, + ) + + # Step 4: Register client or use URL-based client ID (CIMD) + if not self.context.client_info: + # SEP-2352: the issuer to bind these credentials to, when known. + discovered_issuer: str | None = None + if self.context.oauth_metadata is not None: + discovered_issuer = self.context.auth_server_url or str(self.context.oauth_metadata.issuer) + + if should_use_client_metadata_url( + self.context.oauth_metadata, self.context.client_metadata_url + ): + # Use URL-based client ID (CIMD). CIMD records are portable across + # authorization servers, so the issuer stamp is informational. + logger.debug(f"Using URL-based client ID (CIMD): {self.context.client_metadata_url}") + client_information = create_client_info_from_metadata_url( + self.context.client_metadata_url, # type: ignore[arg-type] + redirect_uris=self.context.client_metadata.redirect_uris, + ) + client_information.issuer = discovered_issuer + self.context.client_info = client_information + await self.context.storage.set_client_info(client_information) + else: + # Fallback to Dynamic Client Registration + fallback_base = self.context.get_authorization_base_url(self.context.server_url) + registration_request = create_client_registration_request( + self.context.oauth_metadata, self.context.client_metadata, fallback_base + ) + registration_response = yield registration_request + client_information = await handle_registration_response(registration_response) + check_registration_usable(client_information) + # Only record the issuer when the registration above actually targeted + # the discovered AS — either via its published registration_endpoint, + # or because the resource-origin /register fallback is on the issuer's + # own host (legacy same-origin embedded AS). Otherwise the fallback hit + # a different server and recording a binding to the PRM-advertised AS + # would persist a binding that was never established. + if ( + self.context.oauth_metadata is not None + and discovered_issuer is not None + and ( + self.context.oauth_metadata.registration_endpoint is not None + or self.context.get_authorization_base_url(discovered_issuer) == fallback_base + ) + ): + client_information.issuer = discovered_issuer + self.context.client_info = client_information + await self.context.storage.set_client_info(client_information) + + # Step 5: Perform authorization and complete token exchange + token_response = yield await self._perform_authorization() + await self._handle_token_response(token_response) + except Exception: + logger.exception("OAuth flow error") + raise + + # Retry with new tokens + self._add_auth_header(request) + yield request + elif response.status_code == 403: + # Step 1: Extract error field from WWW-Authenticate header + error = extract_field_from_www_auth(response, "error") + + # Step 2: Check if we need to step-up authorization + if error == "insufficient_scope": # pragma: no branch + try: + # Step 2a: Union previously requested scopes with the newly challenged + # scopes (SEP-2350) so escalating one operation keeps the others' grants. + # Fold in the stored token's scope too: on a restart the token is reloaded + # but client_metadata.scope is not, so it would otherwise be the only basis. + challenged_scope = get_client_metadata_scopes( + extract_scope_from_www_auth(response), + self.context.protected_resource_metadata, + self.context.oauth_metadata, + self.context.client_metadata.grant_types, + ) + granted_scope = self.context.current_tokens.scope if self.context.current_tokens else None + prior_scope = union_scopes(self.context.client_metadata.scope, granted_scope) + self.context.client_metadata.scope = union_scopes(prior_scope, challenged_scope) + + # Step 2b: Perform (re-)authorization and token exchange + token_response = yield await self._perform_authorization() + await self._handle_token_response(token_response) + except Exception: # pragma: no cover + logger.exception("OAuth flow error") + raise + + # Retry with new tokens + self._add_auth_header(request) + yield request diff --git a/venv/lib/python3.12/site-packages/mcp/client/auth/utils.py b/venv/lib/python3.12/site-packages/mcp/client/auth/utils.py new file mode 100644 index 0000000..31e2e5c --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/auth/utils.py @@ -0,0 +1,427 @@ +import re +from typing import Any, cast +from urllib.parse import urljoin, urlparse + +from httpx2 import Request, Response +from mcp_types import LATEST_PROTOCOL_VERSION +from pydantic import AnyUrl, ValidationError +from pydantic_core import from_json + +from mcp.client.auth import OAuthFlowError, OAuthRegistrationError, OAuthTokenError +from mcp.shared.auth import ( + OAuthClientInformationFull, + OAuthClientMetadata, + OAuthMetadata, + OAuthToken, + ProtectedResourceMetadata, +) +from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER + + +def extract_field_from_www_auth(response: Response, field_name: str) -> str | None: + """Extract field from WWW-Authenticate header. + + Returns: + Field value if found in WWW-Authenticate header, None otherwise + """ + www_auth_header = response.headers.get("WWW-Authenticate") + if not www_auth_header: + return None + + # Pattern matches: field_name="value" or field_name=value (unquoted) + pattern = rf'{field_name}=(?:"([^"]+)"|([^\s,]+))' + match = re.search(pattern, www_auth_header) + + if match: + # Return quoted value if present, otherwise unquoted value + return match.group(1) or match.group(2) + + return None + + +def extract_scope_from_www_auth(response: Response) -> str | None: + """Extract scope parameter from WWW-Authenticate header as per RFC 6750. + + Returns: + Scope string if found in WWW-Authenticate header, None otherwise + """ + return extract_field_from_www_auth(response, "scope") + + +def extract_resource_metadata_from_www_auth(response: Response) -> str | None: + """Extract protected resource metadata URL from WWW-Authenticate header as per RFC 9728. + + Returns: + Resource metadata URL if found in WWW-Authenticate header, None otherwise + """ + if not response or response.status_code != 401: + return None # pragma: no cover + + return extract_field_from_www_auth(response, "resource_metadata") + + +def build_protected_resource_metadata_discovery_urls(www_auth_url: str | None, server_url: str) -> list[str]: + """Build ordered list of URLs to try for protected resource metadata discovery. + + Per SEP-985, the client MUST: + 1. Try resource_metadata from WWW-Authenticate header (if present) + 2. Fall back to path-based well-known URI: /.well-known/oauth-protected-resource/{path} + 3. Fall back to root-based well-known URI: /.well-known/oauth-protected-resource + + Args: + www_auth_url: Optional resource_metadata URL extracted from the WWW-Authenticate header + server_url: Server URL + + Returns: + Ordered list of URLs to try for discovery + """ + urls: list[str] = [] + + # Priority 1: WWW-Authenticate header with resource_metadata parameter + if www_auth_url: + urls.append(www_auth_url) + + # Priority 2-3: Well-known URIs (RFC 9728) + parsed = urlparse(server_url) + base_url = f"{parsed.scheme}://{parsed.netloc}" + + # Priority 2: Path-based well-known URI (if server has a path component) + if parsed.path and parsed.path != "/": + path_based_url = urljoin(base_url, f"/.well-known/oauth-protected-resource{parsed.path}") + urls.append(path_based_url) + + # Priority 3: Root-based well-known URI + root_based_url = urljoin(base_url, "/.well-known/oauth-protected-resource") + urls.append(root_based_url) + + return urls + + +def get_client_metadata_scopes( + www_authenticate_scope: str | None, + protected_resource_metadata: ProtectedResourceMetadata | None, + authorization_server_metadata: OAuthMetadata | None = None, + client_grant_types: list[str] | None = None, +) -> str | None: + """Select effective scopes and augment for refresh token support.""" + selected_scope: str | None = None + + # MCP spec scope selection priority: + # 1. WWW-Authenticate header scope + # 2. PRM scopes_supported + # 3. AS scopes_supported (SDK fallback) + # 4. Omit scope parameter + if www_authenticate_scope is not None: + selected_scope = www_authenticate_scope + elif protected_resource_metadata is not None and protected_resource_metadata.scopes_supported is not None: + selected_scope = " ".join(protected_resource_metadata.scopes_supported) + elif authorization_server_metadata is not None and authorization_server_metadata.scopes_supported is not None: + selected_scope = " ".join(authorization_server_metadata.scopes_supported) + + # SEP-2207: append offline_access when the AS supports it and the client can use refresh tokens + if ( + selected_scope is not None + and authorization_server_metadata is not None + and authorization_server_metadata.scopes_supported is not None + and "offline_access" in authorization_server_metadata.scopes_supported + and client_grant_types is not None + and "refresh_token" in client_grant_types + and "offline_access" not in selected_scope.split() + ): + selected_scope = f"{selected_scope} offline_access" + + return selected_scope + + +def union_scopes(previous_scope: str | None, new_scope: str | None) -> str | None: + """Merge two space-delimited scope strings, preserving order and dropping duplicates. + + SEP-2350: on step-up re-authorization the client requests the union of previously requested + scopes and the newly challenged scopes, so escalating one operation does not drop the + permissions granted for another. Previously requested scopes come first; new scopes are + appended in order. + """ + if not previous_scope: + return new_scope + if not new_scope: + return previous_scope + + merged = previous_scope.split() + seen = set(merged) + for scope in new_scope.split(): + if scope not in seen: + merged.append(scope) + seen.add(scope) + return " ".join(merged) + + +def build_oauth_authorization_server_metadata_discovery_urls(auth_server_url: str | None, server_url: str) -> list[str]: + """Generate an ordered list of URLs for authorization server metadata discovery. + + Args: + auth_server_url: OAuth Authorization Server Metadata URL if found, otherwise None + server_url: URL for the MCP server, used as a fallback if auth_server_url is None + """ + + if not auth_server_url: + # Legacy path using the 2025-03-26 spec: + # link: https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization + parsed = urlparse(server_url) + return [f"{parsed.scheme}://{parsed.netloc}/.well-known/oauth-authorization-server"] + + urls: list[str] = [] + parsed = urlparse(auth_server_url) + base_url = f"{parsed.scheme}://{parsed.netloc}" + + # RFC 8414: Path-aware OAuth discovery + if parsed.path and parsed.path != "/": + oauth_path = f"/.well-known/oauth-authorization-server{parsed.path.rstrip('/')}" + urls.append(urljoin(base_url, oauth_path)) + + # RFC 8414 section 5: Path-aware OIDC discovery + # See https://www.rfc-editor.org/rfc/rfc8414.html#section-5 + oidc_path = f"/.well-known/openid-configuration{parsed.path.rstrip('/')}" + urls.append(urljoin(base_url, oidc_path)) + + # https://openid.net/specs/openid-connect-discovery-1_0.html + oidc_path = f"{parsed.path.rstrip('/')}/.well-known/openid-configuration" + urls.append(urljoin(base_url, oidc_path)) + return urls + + # OAuth root + urls.append(urljoin(base_url, "/.well-known/oauth-authorization-server")) + + # OIDC 1.0 fallback (appends to full URL per OIDC spec) + # https://openid.net/specs/openid-connect-discovery-1_0.html + urls.append(urljoin(base_url, "/.well-known/openid-configuration")) + + return urls + + +async def handle_protected_resource_response( + response: Response, +) -> ProtectedResourceMetadata | None: + """Handle protected resource metadata discovery response. + + Per SEP-985, supports fallback when discovery fails at one URL. + + Returns: + ProtectedResourceMetadata if successfully discovered, None if we should try next URL + """ + if response.status_code == 200: + try: + content = await response.aread() + metadata = ProtectedResourceMetadata.model_validate_json(content) + return metadata + + except ValidationError: # pragma: no cover + # Invalid metadata - try next URL + return None + else: + # Not found - try next URL in fallback chain + return None + + +async def handle_auth_metadata_response(response: Response) -> tuple[bool, OAuthMetadata | None]: + if response.status_code == 200: + try: + content = await response.aread() + asm = OAuthMetadata.model_validate_json(content) + return True, asm + except ValidationError: # pragma: no cover + return True, None + elif response.status_code < 400 or response.status_code >= 500: + return False, None # Non-4XX error, stop trying + return True, None + + +def validate_authorization_response_iss(iss: str | None, oauth_metadata: OAuthMetadata | None) -> None: + """Validate the RFC 9207 `iss` authorization-response parameter. + + Per RFC 9207 section 2.4, the client compares `iss` against the issuer of the + authorization server the request was sent to, using simple string comparison + (RFC 3986 section 6.2.1, i.e. without URL normalization), and rejects on mismatch. + A response that omits `iss` is rejected only when the server advertised support via + `authorization_response_iss_parameter_supported`. + + Raises: + OAuthFlowError: If `iss` is present and does not match, or is absent when the + authorization server advertised support. + """ + expected = str(oauth_metadata.issuer) if oauth_metadata else None + + if iss is not None: + if iss != expected: + raise OAuthFlowError(f"Authorization response iss mismatch: {iss} != {expected}") + return + + if oauth_metadata is not None and oauth_metadata.authorization_response_iss_parameter_supported: + raise OAuthFlowError("Authorization response missing iss parameter advertised by the authorization server") + + +def validate_metadata_issuer(oauth_metadata: OAuthMetadata, expected_issuer: str) -> None: + """Validate that authorization server metadata `issuer` matches the discovery issuer. + + Per RFC 8414 section 3.3 / SEP-2468, the `issuer` in the metadata must match the issuer + used to construct the well-known URL, compared as a simple string (RFC 3986 section 6.2.1). + + Raises: + OAuthFlowError: If the metadata issuer does not match `expected_issuer`. + """ + if str(oauth_metadata.issuer) != expected_issuer: + raise OAuthFlowError( + f"Authorization server metadata issuer mismatch: {oauth_metadata.issuer} != {expected_issuer}" + ) + + +def create_oauth_metadata_request(url: str) -> Request: + return Request("GET", url, headers={MCP_PROTOCOL_VERSION_HEADER: LATEST_PROTOCOL_VERSION}) + + +def create_client_registration_request( + auth_server_metadata: OAuthMetadata | None, client_metadata: OAuthClientMetadata, auth_base_url: str +) -> Request: + """Build a client registration request.""" + + if auth_server_metadata and auth_server_metadata.registration_endpoint: + registration_url = str(auth_server_metadata.registration_endpoint) + else: + registration_url = urljoin(auth_base_url, "/register") + + registration_data = client_metadata.model_dump(by_alias=True, mode="json", exclude_none=True) + + return Request("POST", registration_url, json=registration_data, headers={"Content-Type": "application/json"}) + + +async def handle_registration_response(response: Response) -> OAuthClientInformationFull: + """Handle registration response.""" + if response.status_code not in (200, 201): + await response.aread() + raise OAuthRegistrationError(f"Registration failed: {response.status_code} {response.text}") + + try: + content = await response.aread() + body = from_json(content) + # `issuer` is the SDK's own binding of these credentials to the server they were + # registered with (SEP-2352), stamped by the auth flow - never sourced from the + # wire, so it is dropped before the body is parsed rather than trusted or cleared. + if isinstance(body, dict): + cast(dict[str, Any], body).pop("issuer", None) + return OAuthClientInformationFull.model_validate(body) + except ValueError as e: + # `from_json` reports malformed bytes/JSON as ValueError, and pydantic's + # ValidationError is itself a ValueError, so both parse layers surface here. + raise OAuthRegistrationError(f"Invalid registration response: {e}") from e + + +def is_valid_client_metadata_url(url: str | None) -> bool: + """Validate that a URL is suitable for use as a client_id (CIMD). + + The URL must be HTTPS with a non-root pathname. + + Args: + url: The URL to validate + + Returns: + True if the URL is a valid HTTPS URL with a non-root pathname + """ + if not url: + return False + try: + parsed = urlparse(url) + return parsed.scheme == "https" and parsed.path not in ("", "/") + except Exception: + return False + + +def credentials_match_issuer( + client_info: OAuthClientInformationFull, issuer: str, client_metadata_url: str | None +) -> bool: + """Whether stored client credentials may be reused against `issuer` (SEP-2352). + + A URL-based client ID (CIMD) is portable across authorization servers — the same self-hosted + document is resolved by whichever server is in use — so it always matches; CIMD is identified + by the client ID being the configured `client_metadata_url`, not by URL shape (a registration + server may also issue URL-shaped IDs that are bound to it). Credentials with a recorded issuer + match only when it equals `issuer` (simple string comparison). Credentials with no recorded + issuer (pre-registered, or stored before issuer binding existed) carry no binding to enforce + and are left as-is. + """ + if client_metadata_url is not None and client_info.client_id == client_metadata_url: + return True + if client_info.issuer is None: + return True + return client_info.issuer == issuer + + +def should_use_client_metadata_url( + oauth_metadata: OAuthMetadata | None, + client_metadata_url: str | None, +) -> bool: + """Determine if URL-based client ID (CIMD) should be used instead of DCR. + + URL-based client IDs should be used when: + 1. The server advertises client_id_metadata_document_supported=True + 2. The client has a valid client_metadata_url configured + + Args: + oauth_metadata: OAuth authorization server metadata + client_metadata_url: URL-based client ID (already validated) + + Returns: + True if CIMD should be used, False if DCR should be used + """ + if not client_metadata_url: + return False + + if not oauth_metadata: + return False + + return oauth_metadata.client_id_metadata_document_supported is True + + +def create_client_info_from_metadata_url( + client_metadata_url: str, redirect_uris: list[AnyUrl] | None = None +) -> OAuthClientInformationFull: + """Create client information using a URL-based client ID (CIMD). + + When using URL-based client IDs, the URL itself becomes the client_id + and no client_secret is used (token_endpoint_auth_method="none"). + + Args: + client_metadata_url: The URL to use as the client_id + redirect_uris: The redirect URIs from the client metadata, recorded on the client + information alongside the client_id + + Returns: + OAuthClientInformationFull with the URL as client_id + """ + return OAuthClientInformationFull( + client_id=client_metadata_url, + token_endpoint_auth_method="none", + redirect_uris=redirect_uris, + ) + + +async def handle_token_response_scopes( + response: Response, +) -> OAuthToken: + """Parse and validate a token response. + + Parses token response JSON. Callers should check response.status_code before calling. + + Args: + response: HTTP response from token endpoint (status already checked by caller) + + Returns: + Validated OAuthToken model + + Raises: + OAuthTokenError: If response JSON is invalid + """ + try: + content = await response.aread() + token_response = OAuthToken.model_validate_json(content) + return token_response + except ValidationError as e: # pragma: no cover + raise OAuthTokenError(f"Invalid token response: {e}") diff --git a/venv/lib/python3.12/site-packages/mcp/client/caching.py b/venv/lib/python3.12/site-packages/mcp/client/caching.py new file mode 100644 index 0000000..a464acc --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/caching.py @@ -0,0 +1,387 @@ +"""Client-side response caching primitives (SEP-2549, protocol revision 2026-07-28).""" + +from __future__ import annotations + +import json +import logging +import time +from collections.abc import Callable +from dataclasses import dataclass +from typing import Any, Final, Literal, Protocol + +import anyio +import anyio.lowlevel +from mcp_types import ( + CacheableResult, + PromptListChangedNotification, + ResourceListChangedNotification, + ResourceUpdatedNotification, + ServerNotification, + ToolListChangedNotification, +) +from mcp_types.version import MODERN_PROTOCOL_VERSIONS + +__all__ = [ + "MAX_TTL_MS", + "CacheConfig", + "CacheEntry", + "CacheKey", + "CacheMode", + "InMemoryResponseCacheStore", + "ResponseCacheStore", +] + +logger = logging.getLogger(__name__) + +CacheMode = Literal["use", "refresh", "bypass"] +"""Per-call cache behavior: `"use"` serves and stores, `"refresh"` stores +without serving, `"bypass"` skips the cache entirely.""" + +MAX_TTL_MS: Final[int] = 24 * 60 * 60 * 1000 +"""Cap on any entry's time-to-live (24 hours, in milliseconds); larger `ttlMs` values are clamped down.""" + + +@dataclass(frozen=True, slots=True) +class CacheKey: + """Identity of one cached response; compare as the field tuple, never a flattened string (collision hazard).""" + + method: str + + params_key: str = "" + """Result-affecting params discriminator: the uri for `resources/read`, `""` for the list methods.""" + + partition: str = "" + """Coordinator-computed arm identifier; opaque to stores.""" + + +@dataclass(frozen=True, slots=True) +class CacheEntry: + """One cached response with its freshness and sharing metadata.""" + + value: Any + """The cached result; the SDK deep-copies on write and on serve, so a store may hold it as-is.""" + + scope: Literal["public", "private"] + """Server-asserted `cacheScope`: only `"public"` entries may be shared across authorization contexts.""" + + expires_at: float | None + """Epoch seconds after which the entry is stale; `None` is never fresh.""" + + +class ResponseCacheStore(Protocol): + """Storage contract for the client response cache. + + Each `Client` calls its store from a single event loop; per-operation + atomicity is the implementation's responsibility. Operations may raise - + the SDK degrades to a miss rather than failing the call. A serializing + store must round-trip `value` back to the result model object (a + wrong-shape entry is a miss, never an error). A lookup may issue two + sequential `get` calls (private arm, then public). + """ + + async def get(self, key: CacheKey) -> CacheEntry | None: ... + + async def set(self, key: CacheKey, entry: CacheEntry) -> None: ... + + async def delete(self, key: CacheKey) -> None: ... + + async def clear(self) -> None: ... + + +@dataclass(frozen=True, slots=True) +class CacheConfig: + """Configuration for a `Client`'s response cache. + + Raises: + ValueError: On a custom `store` without `partition`, an empty `target_id`, or a negative `default_ttl_ms`. + """ + + store: ResponseCacheStore | None = None + """Backing store; `None` means a per-client `InMemoryResponseCacheStore`. + A custom store requires an explicit `partition`.""" + + partition: str = "" + """Authorization-context identifier isolating `"private"`-scoped entries + within a shared store. Derive it from a verified credential - never from + request-supplied data or the server URL. Fixed for the `Client`'s + lifetime: construct a new `Client` when the principal changes.""" + + target_id: str | None = None + """Server-identity override for custom transports and proxies where the + SDK cannot derive one from a URL; must be non-empty when provided.""" + + default_ttl_ms: int = 0 + """TTL in milliseconds for results carrying no `ttlMs` hint; the default `0` leaves them uncached.""" + + clock: Callable[[], float] = time.time + """Wall-clock source returning epoch seconds; injectable for expiry tests.""" + + share_public: bool = False + """Serve server-marked `"public"` entries across every partition in the store. + + WARNING: this trusts the server's `"public"` classification for every + principal sharing the store - a mislabeled response leaks across tenants. + Constructor-level only: the per-call `cache_mode` can never widen sharing.""" + + def __post_init__(self) -> None: + if self.store is not None and not self.partition: + raise ValueError("a custom store requires an explicit partition") + if self.target_id == "": + raise ValueError("target_id must be a non-empty string or omitted") + if self.default_ttl_ms < 0: + raise ValueError(f"default_ttl_ms must be >= 0, got {self.default_ttl_ms}") + + +class InMemoryResponseCacheStore: + """Default in-process `ResponseCacheStore`. + + Method bodies are synchronous, so concurrent tasks never observe a torn + write. `max_entries` caps the whole store, evicting least-recently-used + at the cap (`0` disables it); `get` and `set` both refresh recency, so a + hot entry survives churn from other keys. + + Raises: + ValueError: If `max_entries` is negative. + """ + + def __init__(self, *, max_entries: int = 1024) -> None: + if max_entries < 0: + raise ValueError(f"max_entries must be >= 0, got {max_entries}") + self._max_entries = max_entries + self._entries: dict[CacheKey, CacheEntry] = {} + + async def get(self, key: CacheKey) -> CacheEntry | None: + entry = self._entries.get(key) + if entry is not None: + # Pop-and-reinsert moves the key to the back: the dict's insertion order is the LRU ledger. + self._entries[key] = self._entries.pop(key) + return entry + + async def set(self, key: CacheKey, entry: CacheEntry) -> None: + self._entries.pop(key, None) + self._entries[key] = entry + if self._max_entries and len(self._entries) > self._max_entries: + del self._entries[next(iter(self._entries))] + + async def delete(self, key: CacheKey) -> None: + self._entries.pop(key, None) + + async def clear(self) -> None: + self._entries.clear() + + +_GENERATION_MAP_CAP: Final[int] = 4096 +"""Cap on the generation map; at the cap the oldest key's eviction-race guard is dropped (FIFO).""" + +_STORE_CLEANUP_TIMEOUT: Final[float] = 5 +"""Bound for must-complete store cleanup deletes (mirrors the dispatcher's final-write bound); +a wedged store delete must not hold client teardown uncancellably.""" + + +class ClientResponseCache: + """Coordinates the `Client` caching verbs with a `ResponseCacheStore`: keys, era gate, TTL/scope, eviction.""" + + def __init__( + self, + *, + store: ResponseCacheStore, + partition: str, + arm_id: str, + default_ttl_ms: int, + clock: Callable[[], float], + share_public: bool, + negotiated_version: Callable[[], str | None], + generation_map_cap: int = _GENERATION_MAP_CAP, + store_cleanup_timeout: float = _STORE_CLEANUP_TIMEOUT, + ) -> None: + self._store = store + self._partition = partition + self._arm_id = arm_id + self._share_public = share_public + self._default_ttl_ms = default_ttl_ms + self._clock = clock + self._negotiated_version = negotiated_version + # A key is eviction-race-guarded iff registered here. + self._generations: dict[tuple[str, str], int] = {} + self._generation_map_cap = generation_map_cap + self._store_cleanup_timeout = store_cleanup_timeout + self._warned_store_ops: set[str] = set() + + def _arm(self, scope: Literal["public", "private"]) -> str: + # JSON arrays so crafted arm_id/partition values cannot collide across field boundaries. + # The negotiated version era-scopes every arm: a session never serves an entry written + # under a different protocol era (its content differs - sieve-stripped fields, header + # filtering). Every caller runs post-connect; were that ever untrue, the supplier's + # None still partitions harmlessly. + fields: list[str | None] = [scope, self._negotiated_version(), self._arm_id] + if scope == "private" or not self._share_public: + fields.append(self._partition) + return json.dumps(fields) + + async def read(self, method: str, params_key: str) -> CacheableResult | None: + """Serve a fresh entry for the key, or `None`; the served result is a deep copy.""" + # A hit completes without any other yielding await, so checkpoint here: a poll + # loop over a fresh entry must not starve spawned tasks (eviction dispatch). + await anyio.lowlevel.checkpoint() + # A wrong-shape entry raises as late as the copy, so the boundary wraps the whole read path. + try: + entry = await self._get_fresh(CacheKey(method, params_key, self._arm("private"))) + if entry is None: + # After a scope flip, a stale private entry must not shadow a fresh public one. + entry = await self._get_fresh(CacheKey(method, params_key, self._arm("public"))) + if entry is not None and entry.scope != "public": + # Never serve an entry the server scoped "private" out of the shared arm. + entry = None + copied: CacheableResult | None = None if entry is None else entry.value.model_copy(deep=True) + except Exception: # boundary around user store code: any read-path failure is a miss, never a failed call + self._warn_store_failure("get") + return None + self._warned_store_ops.discard("get") + return copied + + async def _get_fresh(self, key: CacheKey) -> CacheEntry | None: + entry = await self._store.get(key) + if entry is None or entry.expires_at is None or entry.expires_at <= self._clock(): + return None + return entry + + def capture(self, method: str, params_key: str) -> int: + """Register the key for eviction-race detection before the fetch; `write` takes the returned generation.""" + gen_key = (method, params_key) + if gen_key not in self._generations: + if len(self._generations) >= self._generation_map_cap: + # FIFO overflow: the dropped key's race guard degrades to the accepted co-tenant class. + del self._generations[next(iter(self._generations))] + self._generations[gen_key] = 0 + return self._generations[gen_key] + + async def write( + self, + method: str, + params_key: str, + result: CacheableResult, + gen_at_capture: int, + mode: Literal["use", "refresh"], + ) -> None: + """Store a fetched result under the arm its resolved scope selects.""" + gen_key = (method, params_key) + if self._generation_moved(gen_key, gen_at_capture): + return # the key was evicted while the fetch was in flight + ttl_ms, scope = self._resolve(result) + private_key = CacheKey(method, params_key, self._arm("private")) + public_key = CacheKey(method, params_key, self._arm("public")) + if ttl_ms <= 0: + if mode == "refresh": + # The refetch superseded the warm entry, which a cancellation must not leave serving. + await self._cleanup_delete(private_key, public_key) + return + own, opposite = (public_key, private_key) if scope == "public" else (private_key, public_key) + # Opposite arm first: a failed delete aborts before the set - never two arms answering for one key. + if not await self._delete(opposite): + # The own arm's entry is superseded too: best-effort delete, degrading to a full miss. + await self._cleanup_delete(own) + return + entry = CacheEntry(value=result.model_copy(deep=True), scope=scope, expires_at=self._clock() + ttl_ms / 1000) + try: + if not await self._set(own, entry): + # The fetch superseded any pre-existing own-arm entry, and the failed set + # left it in place: purge it (mirrors the opposite-arm-failure path). + await self._cleanup_delete(own) + finally: + # An eviction can land while the set commits - even when the await + # is cancelled - so re-check on every exit; the delete must complete + # so the pending cancellation cannot resurrect the evicted entry. + if self._generation_moved(gen_key, gen_at_capture): + await self._cleanup_delete(own) + + async def evict_method(self, method: str) -> None: + """Evict the method's cursor-less entry.""" + await self.evict_key(method, "") + + async def evict_key(self, method: str, params_key: str) -> None: + """Evict one key from both arms. + + Only the current era's arms are touched; other-era entries in a persistent store age out by TTL. + """ + gen_key = (method, params_key) + # Bump first so an in-flight fetch cannot write the evicted entry back. + # Unregistered keys skip the bump (uris must not grow the map) but not + # the deletes - a persistent store may hold uncaptured entries. + if gen_key in self._generations: + self._generations[gen_key] += 1 + # Must complete: a cancellation between the deletes would leave one arm serving the evicted entry. + await self._cleanup_delete( + CacheKey(method, params_key, self._arm("private")), + CacheKey(method, params_key, self._arm("public")), + ) + + async def evict_for_notification(self, notification: ServerNotification) -> None: + """Map a server notification to the entries it makes stale. + + Eviction is eventual (spawned-task dispatch): the generation bump closes + the write-back race; a racing read may briefly serve the old entry. + """ + match notification: + case ToolListChangedNotification(): + await self.evict_method("tools/list") + case PromptListChangedNotification(): + await self.evict_method("prompts/list") + case ResourceListChangedNotification(): + # Templates enumerate the same changed resource space. + await self.evict_method("resources/list") + await self.evict_method("resources/templates/list") + case ResourceUpdatedNotification(): + await self.evict_key("resources/read", notification.params.uri) + case _: + pass + + def _resolve(self, result: CacheableResult) -> tuple[int, Literal["public", "private"]]: + # A legacy peer can also put `ttlMs`/`cacheScope` keys on the wire, so + # wire presence is not a peer-era signal - hints count only when modern. + modern = self._negotiated_version() in MODERN_PROTOCOL_VERSIONS + if modern and "ttl_ms" in result.model_fields_set: + # An explicit `ttlMs: 0` stays 0, and negatives are unconstructible + # upstream (model ge=0, parse-seam floor) - only the cap applies. + ttl_ms = result.ttl_ms + else: + ttl_ms = self._default_ttl_ms + scope: Literal["public", "private"] = "public" if modern and result.cache_scope == "public" else "private" + return min(ttl_ms, MAX_TTL_MS), scope + + def _generation_moved(self, gen_key: tuple[str, str], gen_at_capture: int) -> bool: + # A FIFO-dropped key fails open (the accepted co-tenant race) rather than discarding the fetch. + return self._generations.get(gen_key, gen_at_capture) != gen_at_capture + + async def _set(self, key: CacheKey, entry: CacheEntry) -> bool: + try: + await self._store.set(key, entry) + except Exception: # boundary around user store code: nothing cached, the fetch already succeeded + self._warn_store_failure("set") + return False + self._warned_store_ops.discard("set") + return True + + async def _cleanup_delete(self, *keys: CacheKey) -> None: + # Must-complete cleanup: shielded so a pending cancellation cannot skip the deletes, + # bounded so a wedged store delete cannot hold client teardown uncancellably. + with anyio.move_on_after(self._store_cleanup_timeout, shield=True) as scope: + for key in keys: + await self._delete(key) + if scope.cancelled_caught: + logger.warning("Response cache store delete timed out; the entry will age out by TTL") + + async def _delete(self, key: CacheKey) -> bool: + try: + await self._store.delete(key) + except Exception: # boundary around user store code: callers decide whether a failed delete aborts + self._warn_store_failure("delete") + return False + self._warned_store_ops.discard("delete") + return True + + def _warn_store_failure(self, kind: Literal["get", "set", "delete"]) -> None: + # One warning per failure burst, per op kind; re-armed only when that + # same kind succeeds, so a healthy delete cannot re-arm a broken set. + if kind not in self._warned_store_ops: + self._warned_store_ops.add(kind) + logger.warning("Response cache store operation failed; continuing without the cache", exc_info=True) diff --git a/venv/lib/python3.12/site-packages/mcp/client/client.py b/venv/lib/python3.12/site-packages/mcp/client/client.py new file mode 100644 index 0000000..2f467c2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/client.py @@ -0,0 +1,954 @@ +"""Unified MCP Client that wraps ClientSession with transport management.""" + +from __future__ import annotations + +import hashlib +import logging +import uuid +from collections.abc import Awaitable, Callable, Mapping, Sequence +from contextlib import AbstractAsyncContextManager, AsyncExitStack +from dataclasses import KW_ONLY, dataclass, field +from typing import Any, Literal, TypeVar, cast + +import anyio +import anyio.lowlevel +import mcp_types as types +from mcp_types import ( + INVALID_PARAMS, + CacheableResult, + CallToolResult, + CompleteResult, + EmptyResult, + ErrorData, + GetPromptResult, + Implementation, + InputRequest, + InputRequiredResult, + InputResponse, + InputResponses, + ListPromptsResult, + ListResourcesResult, + ListResourceTemplatesResult, + ListToolsResult, + LoggingLevel, + PaginatedRequestParams, + PromptReference, + ReadResourceResult, + RequestParamsMeta, + ResourceTemplateReference, + Result, + ServerCapabilities, +) +from mcp_types.version import HANDSHAKE_PROTOCOL_VERSIONS, MODERN_PROTOCOL_VERSIONS +from typing_extensions import deprecated + +from mcp.client._input_required import DEFAULT_INPUT_REQUIRED_MAX_ROUNDS, run_input_required_driver +from mcp.client._memory import InMemoryTransport +from mcp.client._probe import negotiate_auto +from mcp.client._transport import Transport +from mcp.client.caching import CacheConfig, CacheMode, ClientResponseCache, InMemoryResponseCacheStore +from mcp.client.extension import ClaimContext, ClientExtension, NotificationBinding, ResultClaim +from mcp.client.session import ( + ClientRequestContext, + ClientSession, + ElicitationFnT, + IncomingMessage, + ListRootsFnT, + LoggingFnT, + MessageHandlerFnT, + SamplingFnT, +) +from mcp.client.stdio import StdioServerParameters, stdio_client +from mcp.client.streamable_http import streamable_http_client +from mcp.client.subscriptions import ServerEvent, Subscription +from mcp.client.subscriptions import listen as _listen +from mcp.server import Server +from mcp.server.mcpserver import MCPServer +from mcp.server.runner import modern_on_request +from mcp.shared.direct_dispatcher import create_direct_dispatcher_pair +from mcp.shared.dispatcher import Dispatcher, ProgressFnT +from mcp.shared.exceptions import MCPDeprecationWarning, MCPError +from mcp.shared.extension import validate_extension_identifier +from mcp.shared.jsonrpc_dispatcher import JSONRPCDispatcher +from mcp.shared.subscriptions import event_to_notification + +logger = logging.getLogger(__name__) + +ConnectMode = Literal["legacy", "auto"] | str +"""``mode=`` value: ``"legacy"`` (initialize handshake), ``"auto"`` (discover, fall back to +initialize), or a modern protocol-version string (adopt directly). The ``str`` arm is for +forward-compat; ``Client.__post_init__`` rejects anything outside that set at construction.""" + +_T = TypeVar("_T") +_ResultT = TypeVar("_ResultT") +_CacheableT = TypeVar("_CacheableT", bound=CacheableResult) + +_Connector = Callable[[AsyncExitStack, ConnectMode, bool], Awaitable["Dispatcher[Any]"]] +"""Resolved at ``__post_init__`` from the shape of ``server`` alone: enter whatever resources +are needed onto the exit stack and hand back the ``Dispatcher`` ``ClientSession`` will drive. +``mode`` and ``raise_exceptions`` are passed at call time so they're read at the same moment +``__aenter__`` reads them for the handshake step.""" + + +def _connect_transport(transport: Transport) -> _Connector: + """Connector for the stream-backed paths (URL, user-supplied ``Transport``).""" + + async def connect(exit_stack: AsyncExitStack, _mode: ConnectMode, _raise_exceptions: bool) -> Dispatcher[Any]: + read_stream, write_stream = await exit_stack.enter_async_context(transport) + return JSONRPCDispatcher(read_stream, write_stream) + + return connect + + +def _connect_inproc(server: Server[Any]) -> _Connector: + """Connector for an in-process ``Server``: legacy mode drives the stream loop via + ``InMemoryTransport``; any other mode drives the modern per-request path through a + ``DirectDispatcher`` peer pair (no streams, no JSON-RPC framing, no initialize handshake).""" + + async def connect(exit_stack: AsyncExitStack, mode: ConnectMode, raise_exceptions: bool) -> Dispatcher[Any]: + if mode == "legacy": + transport = InMemoryTransport(server, raise_exceptions=raise_exceptions) + read_stream, write_stream = await exit_stack.enter_async_context(transport) + return JSONRPCDispatcher(read_stream, write_stream) + lifespan_state = await exit_stack.enter_async_context(server.lifespan(server)) + client_disp, server_disp = create_direct_dispatcher_pair(raise_handler_exceptions=raise_exceptions) + tg = await exit_stack.enter_async_context(anyio.create_task_group()) + exit_stack.callback(server_disp.close) + on_request = modern_on_request(server, lifespan_state) + await tg.start(server_disp.run, on_request, _no_inbound_client_notifications) + return client_disp + + return connect + + +def _connected(value: _T | None) -> _T: + """Narrow a post-handshake session attribute from ``T | None`` to ``T``. + + ``Client.__aenter__`` only assigns ``_session`` after the handshake succeeds, so inside + ``async with Client(...)`` these attributes are always populated; the ``.session`` gate + raises before this is reached otherwise. The guard exists for pyright, not runtime. + """ + if value is None: # pragma: no cover + raise RuntimeError("Client must be used within an async context manager") + return value + + +def _strip_userinfo(url: str) -> str: + """Drop any userinfo from the URL's authority component; byte-exact otherwise. + + Credentials must not enter cache-key material; any further normalization could merge distinct servers. + """ + # Pure text, no urlsplit: it strips embedded tab/CR/LF before parsing, which would misalign slices. + sep = url.find("//") + if sep == -1: + return url + start = sep + 2 + end = len(url) + for delimiter in "/?#": + if (found := url.find(delimiter, start)) != -1: + end = min(end, found) + authority = url[start:end] + if "@" not in authority: + return url + return url[:start] + authority.rpartition("@")[2] + url[end:] + + +def _evicting_message_handler(cache: ClientResponseCache, user_handler: MessageHandlerFnT | None) -> MessageHandlerFnT: + """Wrap the session message handler with cache eviction on server notifications.""" + + async def handler(message: IncomingMessage) -> None: + if isinstance(message, types.ServerNotification): + try: + await cache.evict_for_notification(message) + except Exception: # boundary: eviction reaches user store code; a cache fault must not block delivery + logger.exception("Response cache eviction failed; the notification is still delivered") + if user_handler is not None: + await user_handler(message) + else: + # Mirrors ClientSession's default handler (session._default_message_handler). + await anyio.lowlevel.checkpoint() + + return handler + + +def _synthesize_discover(protocol_version: str) -> types.DiscoverResult: + return types.DiscoverResult( + supported_versions=[protocol_version], + capabilities=types.ServerCapabilities(), + result_type="complete", + ttl_ms=0, + cache_scope="public", + ) + + +async def _no_inbound_client_notifications(_dctx: Any, _method: str, _params: Mapping[str, Any] | None) -> None: + """Server-side inbound ``OnNotify`` for the modern in-process path — receives nothing. + + At 2026-07-28 the spec defines no client→server notifications: ``initialized`` and + ``roots/list_changed`` are removed, and cancellation is structural (anyio scope cancel + through the direct await, not a notify). Server→client notifications (progress, log + messages) flow the other way via the per-request ``DispatchContext`` into the client's + callbacks, and are not seen here. + """ + + +@dataclass(frozen=True) +class _FoldedExtensions: + """`Client.extensions` instances folded into the shapes `ClientSession` consumes.""" + + ad: dict[str, dict[str, Any]] | None + claims: dict[str, tuple[ResultClaim[Any], ...]] | None + bindings: tuple[NotificationBinding[Any], ...] | None + by_model: Mapping[type[Result], ResultClaim[Any]] + + +def _fold_extensions(extensions: Sequence[ClientExtension] | None) -> _FoldedExtensions: + """Fold extension contributions at construction, naming both owners on duplicate tags or methods.""" + if isinstance(extensions, Mapping): + raise TypeError( + "extensions= takes a sequence of ClientExtension instances. The mapping form was " + "replaced: use advertise(identifier, settings) for advertise-only entries" + ) + if not extensions: + return _FoldedExtensions(ad=None, claims=None, bindings=None, by_model={}) + ad: dict[str, dict[str, Any]] = {} + claims: dict[str, tuple[ResultClaim[Any], ...]] = {} + bindings: list[NotificationBinding[Any]] = [] + by_model: dict[type[Result], ResultClaim[Any]] = {} + claim_owners: dict[str, str] = {} + binding_owners: dict[str, str] = {} + for extension in extensions: + identifier = getattr(extension, "identifier", None) + if identifier is None: + raise ValueError( + f"{type(extension).__name__} has no `identifier`; a ClientExtension must set the " + "`identifier` class attribute (or assign one in `__init__`) before it can be used" + ) + validate_extension_identifier(identifier, owner=type(extension).__name__) + if identifier in ad: + raise ValueError(f"extension identifier {identifier!r} is passed more than once") + ad[identifier] = extension.settings() + extension_claims = tuple(extension.claims()) + for claim in extension_claims: + tag = claim.result_type + if tag in claim_owners: + owner = claim_owners[tag] + both = ( + f"extension {identifier!r} claims" + if owner == identifier + else (f"extensions {owner!r} and {identifier!r} both claim") + ) + raise ValueError(f"{both} resultType {tag!r}; a wire tag can have only one resolver") + claim_owners[tag] = identifier + # Each model pins its result_type Literal to one tag, so this index cannot collide. + by_model[claim.model] = claim + if extension_claims: + claims[identifier] = extension_claims + for binding in extension.notifications(): + if binding.method in binding_owners: + owner = binding_owners[binding.method] + both = ( + f"extension {identifier!r} binds" + if owner == identifier + else (f"extensions {owner!r} and {identifier!r} both bind") + ) + raise ValueError(f"{both} notification method {binding.method!r}; a method can have only one observer") + binding_owners[binding.method] = identifier + bindings.append(binding) + return _FoldedExtensions(ad=ad, claims=claims or None, bindings=tuple(bindings) or None, by_model=by_model) + + +@dataclass +class Client: + """A high-level MCP client for connecting to MCP servers. + + Pass a URL string (Streamable HTTP), a `StdioServerParameters` (launch the command as a + subprocess and talk over its stdin/stdout), any `Transport`, or - in tests - a `Server` or + `MCPServer` instance to connect to it in-process. + + Example: + ```python + from mcp.client import Client + from mcp.server.mcpserver import MCPServer + + server = MCPServer("test") + + @server.tool() + def add(a: int, b: int) -> int: + return a + b + + async def main(): + async with Client(server) as client: + result = await client.call_tool("add", {"a": 1, "b": 2}) + + asyncio.run(main()) + ``` + """ + + server: Server[Any] | MCPServer | Transport | StdioServerParameters | str + """The MCP server to connect to. + + If the server is a URL string, it will be used as the URL for a `streamable_http_client` transport. + If the server is a `StdioServerParameters`, the command is launched with `stdio_client`. + If the server is a `Transport` instance, it will be used directly. + If the server is a `Server` or `MCPServer` instance, it will be connected in-process. + """ + + _: KW_ONLY + + # TODO(Marcelo): When do `raise_exceptions=True` actually raises? + raise_exceptions: bool = False + """Whether to raise exceptions from the server.""" + + read_timeout_seconds: float | None = None + """Timeout for read operations.""" + + sampling_callback: SamplingFnT | None = None + """Callback for handling sampling requests.""" + + sampling_capabilities: types.SamplingCapability | None = None + """Sampling sub-capabilities (e.g. tools) declared alongside `sampling_callback`; no effect without it.""" + + list_roots_callback: ListRootsFnT | None = None + """Callback for handling list roots requests.""" + + logging_callback: LoggingFnT | None = None + """Callback for handling logging notifications.""" + + log_level: LoggingLevel | None = None + """The log level to opt in to on 2026-07-28+ connections (deprecated logging feature, SEP-2577). + + Modern (2026-07-28+) servers send `notifications/message` only for requests that opt in by + carrying `io.modelcontextprotocol/logLevel` in `_meta`, and only at or above that level. Setting + this stamps that opt-in on every request; `None` (the default) means no opt-in, so no log + messages arrive - a `logging_callback` alone is not an opt-in. No effect on handshake-era + connections, where the deprecated `logging/setLevel` request governs delivery instead. A + per-request `_meta` entry with the same key overrides this default.""" + + # TODO(Marcelo): Why do we have both "callback" and "handler"? + message_handler: MessageHandlerFnT | None = None + """Callback for handling raw messages.""" + + client_info: Implementation | None = None + """Client implementation info to send to server.""" + + mode: ConnectMode = "auto" + """How to negotiate the protocol version. + + 'auto' (the default) probes `server/discover` and falls back to the initialize handshake on legacy servers; + for an in-process `Server`/`MCPServer` it dispatches directly without JSON-RPC framing. 'legacy' forces the + initialize handshake (byte-identical pre-2026 behavior). A modern protocol-version string (e.g. '2026-07-28') + adopts that version directly without a probe — supply `prior_discover` to reuse a known DiscoverResult, or + omit it to synthesize a minimal one.""" + + prior_discover: types.DiscoverResult | None = None + """A previously-obtained DiscoverResult to install via .adopt() when mode is a version pin. + Ignored when mode='legacy'.""" + + elicitation_callback: ElicitationFnT | None = None + """Callback for handling elicitation requests.""" + + input_required_max_rounds: int = DEFAULT_INPUT_REQUIRED_MAX_ROUNDS + """Cap on `InputRequiredResult` retry rounds before `call_tool` / `get_prompt` / + `read_resource` give up. Use `client.session.(..., allow_input_required=True)` + to drive the loop manually instead.""" + + extensions: Sequence[ClientExtension] | None = None + """Opt-in client extensions (SEP-2133). + + Each instance contributes its capability ad, its result claims (resolved + transparently by `call_tool`), and its notification bindings. For an + ad-only entry use `mcp.client.advertise(identifier, settings)`.""" + + cache: CacheConfig | None = field(default_factory=CacheConfig) + """Client-side response caching for the SEP-2549 cacheable methods (2026-07-28). + + The default `CacheConfig()` honors server `ttlMs`/`cacheScope` hints with a + per-client in-memory store; pass a customized `CacheConfig`, or `None` to + disable. The cacheable verbs take a per-call `cache_mode` (see `CacheMode`); + calls carrying `meta` always reach the server. A `CacheConfig` with a custom + `store` requires `target_id` when the server is not a URL (no identity can be + derived).""" + + _entered: bool = field(init=False, default=False) + _session: ClientSession | None = field(init=False, default=None) + _exit_stack: AsyncExitStack | None = field(init=False, default=None) + _connect: _Connector = field(init=False, repr=False, compare=False) + _response_cache: ClientResponseCache | None = field(init=False, default=None, repr=False, compare=False) + _folded_extensions: _FoldedExtensions = field(init=False, repr=False, compare=False) + + def __post_init__(self) -> None: + if self.mode not in ("legacy", "auto") and self.mode not in MODERN_PROTOCOL_VERSIONS: + hint = ( + f" ({self.mode!r} is a handshake-era version; use mode='legacy')" + if self.mode in HANDSHAKE_PROTOCOL_VERSIONS + else "" + ) + raise ValueError( + f"mode must be 'legacy', 'auto', or one of {list(MODERN_PROTOCOL_VERSIONS)}; got {self.mode!r}{hint}" + ) + + self._folded_extensions = _fold_extensions(self.extensions) + + srv = self.server + if isinstance(srv, MCPServer): + srv = srv._lowlevel_server # pyright: ignore[reportPrivateUsage] + if isinstance(srv, Server): + self._connect = _connect_inproc(srv) + elif isinstance(srv, str): + self._connect = _connect_transport(streamable_http_client(srv)) + elif isinstance(srv, StdioServerParameters): + self._connect = _connect_transport(stdio_client(srv)) + else: + self._connect = _connect_transport(srv) + + if self.cache is not None: + config = self.cache + # Only the hash below leaves this scope - the raw identity may carry credentials; never log or store it. + target_id = config.target_id + if target_id is None and isinstance(self.server, str): + target_id = _strip_userinfo(self.server) + if target_id is None: + if config.store is not None: + raise ValueError( + "a custom cache store requires CacheConfig.target_id when the server is not a URL: " + "in-process servers and Transport instances get a random per-client identity, so " + "their entries in a shared store could never be served to another client" + ) + target_id = uuid.uuid4().hex + self._response_cache = ClientResponseCache( + store=config.store if config.store is not None else InMemoryResponseCacheStore(), + partition=config.partition, + arm_id=hashlib.sha256(target_id.encode()).hexdigest(), + default_ttl_ms=config.default_ttl_ms, + clock=config.clock, + share_public=config.share_public, + # Lazy: the negotiated version is unknown until __aenter__'s handshake. + negotiated_version=lambda: self._session.protocol_version if self._session is not None else None, + ) + + async def _build_session(self, exit_stack: AsyncExitStack) -> ClientSession: + """Enter the resolved connector and return an un-entered ClientSession.""" + dispatcher = await self._connect(exit_stack, self.mode, self.raise_exceptions) + message_handler = self.message_handler + if self._response_cache is not None: + message_handler = _evicting_message_handler(self._response_cache, self.message_handler) + return ClientSession( + dispatcher=dispatcher, + read_timeout_seconds=self.read_timeout_seconds, + sampling_callback=self.sampling_callback, + sampling_capabilities=self.sampling_capabilities, + list_roots_callback=self.list_roots_callback, + logging_callback=self.logging_callback, + log_level=self.log_level, + message_handler=message_handler, + client_info=self.client_info, + elicitation_callback=self.elicitation_callback, + extensions=self._folded_extensions.ad, + result_claims=self._folded_extensions.claims, + notification_bindings=self._folded_extensions.bindings, + ) + + async def __aenter__(self) -> Client: + """Enter the async context manager.""" + if self._entered: + raise RuntimeError("Client is already entered; cannot reenter") + self._entered = True + + async with AsyncExitStack() as exit_stack: + session = await self._build_session(exit_stack) + session = await exit_stack.enter_async_context(session) + + if self.mode == "legacy": + await session.initialize() + elif self.mode == "auto": + await negotiate_auto(session) + else: + session.adopt(self.prior_discover or _synthesize_discover(self.mode)) + + # Only publish the session after the handshake succeeds, so `_session is not None` + # implies the protocol_version/server_capabilities are populated (server_info + # stays optional: 2026-era servers may not identify themselves). If the + # handshake raised above, the local exit_stack unwinds the transport for us. + self._session = session + self._exit_stack = exit_stack.pop_all() + return self + + async def __aexit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: Any) -> None: + """Exit the async context manager.""" + if self._exit_stack: # pragma: no branch + await self._exit_stack.__aexit__(exc_type, exc_val, exc_tb) + self._session = None + + @property + def session(self) -> ClientSession: + """Get the underlying ClientSession. + + This provides access to the full ClientSession API for advanced use cases. + + Raises: + RuntimeError: If accessed before entering the context manager. + """ + if self._session is None: + raise RuntimeError("Client must be used within an async context manager") + return self._session + + # TODO(maxisbey): the by-construction shape is for __aenter__ to return a connected-view + # type whose protocol_version/server_capabilities are non-Optional fields, + # eliminating these guards (and the one in .session). Same family as resolving the + # transport/connector at __post_init__ so the Optional internal fields disappear. + # (server_info stays Optional even connected: the 2026-era stamp is optional.) + @property + def protocol_version(self) -> str: + """Negotiated protocol version (set by initialize/discover/adopt during ``__aenter__``).""" + return _connected(self.session.protocol_version) + + @property + def server_info(self) -> Implementation | None: + """Server name/version, or `None` when the server did not identify itself. + + Legacy connections always carry it (`InitializeResult.serverInfo` is + required); on 2026-era connections the `_meta` `serverInfo` stamp is + optional, so an anonymous server reads as `None`. + """ + return self.session.server_info + + @property + def server_capabilities(self) -> ServerCapabilities: + """Server capabilities (set by initialize/discover/adopt during ``__aenter__``).""" + return _connected(self.session.server_capabilities) + + @property + def instructions(self) -> str | None: + """Server-provided instructions text, if any.""" + return self.session.instructions + + @deprecated( + "ping is removed as of 2026-07-28; the method only works under mode='legacy'.", + category=MCPDeprecationWarning, + ) + async def send_ping(self, *, meta: RequestParamsMeta | None = None) -> EmptyResult: + """Send a ping request to the server.""" + return await self.session.send_ping(meta=meta) + + @deprecated( + "Client-to-server progress is deprecated as of 2026-07-28; progress is server-to-client only.", + category=MCPDeprecationWarning, + ) + async def send_progress_notification( + self, + progress_token: str | int, + progress: float, + total: float | None = None, + message: str | None = None, + ) -> None: + """Send a progress notification to the server.""" + await self.session.send_progress_notification( # pyright: ignore[reportDeprecated] + progress_token=progress_token, + progress=progress, + total=total, + message=message, + ) + + @deprecated("The logging capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def set_logging_level(self, level: LoggingLevel, *, meta: RequestParamsMeta | None = None) -> EmptyResult: + """Set the logging level on the server.""" + return await self.session.set_logging_level(level=level, meta=meta) # pyright: ignore[reportDeprecated] + + async def _cached_fetch( + self, + method: str, + *, + cursor: str | None, + meta: RequestParamsMeta | None, + cache_mode: CacheMode, + send: Callable[[], Awaitable[_CacheableT]], + absorb: Callable[[_CacheableT], _CacheableT] | None = None, + ) -> _CacheableT: + """Serve one of the four list verbs through the response cache. + + `absorb` (tools/list only) re-applies session-side derived state to a served cache hit. + """ + cache = self._response_cache + if cache is None or cache_mode == "bypass": + return await send() + # A closed (or never-entered) client must raise, never serve cached entries. + _ = self.session + if meta is not None and cache_mode == "use": + # meta (a progress token, tracing fields) expects a wire request; fetch and replace the entry. + cache_mode = "refresh" + if cursor is not None: + # Continuation pages skip the cache, but an expired cursor means the listing changed (spec SHOULD evict). + try: + return await send() + except MCPError as e: + if e.code == INVALID_PARAMS: + await cache.evict_method(method) + raise + if cache_mode == "use" and (hit := await cache.read(method, "")) is not None: + # The hit is a private deep copy, so absorption may mutate it freely. + served = cast(_CacheableT, hit) + return served if absorb is None else absorb(served) + gen = cache.capture(method, "") + result = await send() + await cache.write(method, "", result, gen, cache_mode) + return result + + async def list_resources( + self, + *, + cursor: str | None = None, + meta: RequestParamsMeta | None = None, + cache_mode: CacheMode = "use", + ) -> ListResourcesResult: + """List available resources from the server.""" + return await self._cached_fetch( + "resources/list", + cursor=cursor, + meta=meta, + cache_mode=cache_mode, + send=lambda: self.session.list_resources(params=PaginatedRequestParams(cursor=cursor, _meta=meta)), + ) + + async def list_resource_templates( + self, + *, + cursor: str | None = None, + meta: RequestParamsMeta | None = None, + cache_mode: CacheMode = "use", + ) -> ListResourceTemplatesResult: + """List available resource templates from the server.""" + return await self._cached_fetch( + "resources/templates/list", + cursor=cursor, + meta=meta, + cache_mode=cache_mode, + send=lambda: self.session.list_resource_templates(params=PaginatedRequestParams(cursor=cursor, _meta=meta)), + ) + + async def read_resource( + self, + uri: str, + *, + input_responses: InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + cache_mode: CacheMode = "use", + ) -> ReadResourceResult: + """Read a resource from the server. + + If the server returns an `InputRequiredResult`, the embedded input + requests are dispatched to this client's sampling / elicitation / roots + callbacks and the read is retried automatically (up to + `input_required_max_rounds`). + + Args: + uri: The URI of the resource to read. + input_responses: Responses to seed the first call with (e.g. when + resuming from a persisted `InputRequiredResult`). + request_state: Opaque state to seed the first call with. + meta: Additional metadata for the request. + cache_mode: Cache behavior for this call (see `CacheMode`); seeded + calls (`input_responses` or `request_state` set) ignore it. + + Returns: + The resource content. + + Raises: + InputRequiredRoundsExceededError: `input_required_max_rounds` exhausted. + MCPError: A callback returned `ErrorData` for an embedded input request. + pydantic.ValidationError: The server returned a result that does not + conform to the negotiated protocol version. + """ + + async def retry(r: InputResponses | None, s: str | None) -> ReadResourceResult | InputRequiredResult: + return await self.session.read_resource( + uri, input_responses=r, request_state=s, meta=meta, allow_input_required=True + ) + + # Seeded calls resume a specific exchange and must never be cached (spec MUST). + seeded = input_responses is not None or request_state is not None + cache = None if seeded else self._response_cache + if cache is None or cache_mode == "bypass": + return await self._drive_input_required(await retry(input_responses, request_state), retry) + # A closed (or never-entered) client must raise, never serve cached entries. + _ = self.session + if meta is not None and cache_mode == "use": + # Calls carrying meta always reach the server (mirrors `_cached_fetch`). + cache_mode = "refresh" + if cache_mode == "use" and (hit := await cache.read("resources/read", uri)) is not None: + # Only terminal first-round results are stored, so a hit legitimately skips the driver. + return cast(ReadResourceResult, hit) + gen = cache.capture("resources/read", uri) + first = await retry(None, None) + if not isinstance(first, InputRequiredResult): + await cache.write("resources/read", uri, first, gen, cache_mode) + elif cache_mode == "refresh": + # The refresh superseded whatever was cached, but an input_required resolution + # cannot be stored: purge the warm entry so it cannot be served again. + await cache.evict_key("resources/read", uri) + # Driver rounds carry inputResponses, so a terminal result reached through them is never cached (spec MUST). + return await self._drive_input_required(first, retry) + + def listen( + self, + *, + tools_list_changed: bool = False, + prompts_list_changed: bool = False, + resources_list_changed: bool = False, + resource_subscriptions: Sequence[str] = (), + ) -> AbstractAsyncContextManager[Subscription]: + """Open a `subscriptions/listen` stream of typed change events (2026-07-28 only). + + Keyword args mirror the wire `SubscriptionFilter`; entering waits for the ack (honored subset: `sub.honored`): + + async with client.listen(tools_list_changed=True) as sub: + async for event in sub: + tools = await client.list_tools() # refetch on change + + A graceful close ends the loop; an abrupt drop raises `SubscriptionLost`. No replay: re-listen and refetch. + + Raises: + ListenNotSupportedError: The negotiated protocol version predates 2026-07-28. + MCPError: The server rejected the request or the connection failed first. + SubscriptionLost: The stream ended before it was acknowledged. + TimeoutError: The read timeout elapsed before the acknowledgment. + """ + return _listen( + self.session, + tools_list_changed=tools_list_changed, + prompts_list_changed=prompts_list_changed, + resources_list_changed=resources_list_changed, + resource_subscriptions=resource_subscriptions, + on_event=self._evict_for_listen_event if self._response_cache is not None else None, + ) + + async def _evict_for_listen_event(self, event: ServerEvent) -> None: + """Finish response-cache eviction before a listen consumer can refetch. + + Without it the iterator wakes first and refetches a still-warm entry, with no + corrective wake (events are deduplicated level triggers). The tee path repeats + the eviction; deliberate: idempotent, and it covers non-iterating consumers. + """ + cache = self._response_cache + assert cache is not None # installed as the event barrier only when a cache exists + try: + await cache.evict_for_notification(event_to_notification(event, {})) + except Exception: # boundary: eviction reaches user store code; a cache fault must not block delivery + logger.exception("Response cache eviction failed; the event is still delivered") + + @deprecated( + "resources/subscribe is removed as of 2026-07-28; use Client.listen() instead.", + category=MCPDeprecationWarning, + ) + async def subscribe_resource(self, uri: str, *, meta: RequestParamsMeta | None = None) -> EmptyResult: + """Subscribe to resource updates (2025-era servers only).""" + return await self.session.subscribe_resource(uri, meta=meta) # pyright: ignore[reportDeprecated] + + @deprecated( + "resources/unsubscribe is removed as of 2026-07-28; use Client.listen() instead.", + category=MCPDeprecationWarning, + ) + async def unsubscribe_resource(self, uri: str, *, meta: RequestParamsMeta | None = None) -> EmptyResult: + """Unsubscribe from resource updates (2025-era servers only).""" + return await self.session.unsubscribe_resource(uri, meta=meta) # pyright: ignore[reportDeprecated] + + async def call_tool( + self, + name: str, + arguments: dict[str, Any] | None = None, + read_timeout_seconds: float | None = None, + progress_callback: ProgressFnT | None = None, + *, + input_responses: InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + ) -> CallToolResult: + """Call a tool on the server. + + If the server returns an `InputRequiredResult`, the embedded input + requests are dispatched to this client's sampling / elicitation / roots + callbacks and the call is retried automatically (up to + `input_required_max_rounds`). To drive the loop yourself — e.g. to + persist `request_state` across process restarts — use + `client.session.call_tool(..., allow_input_required=True)`. Persisted + state is still subject to the server's TTL, request binding, and key + lifetime; a server on the default process-local key rejects it after a restart. + + Result shapes claimed by this client's `extensions` are finished by the + owning claim's resolver, whose `CallToolResult` is returned; resolver + exceptions propagate as-is. To receive the claimed shape yourself, use + `client.session.call_tool(..., allow_claimed=True)`. + + Args: + name: The name of the tool to call. + arguments: Arguments to pass to the tool. + read_timeout_seconds: Timeout for each underlying `tools/call` round. + progress_callback: Callback for progress updates. + input_responses: Responses to seed the first call with (e.g. when + resuming from a persisted `InputRequiredResult`). + request_state: Opaque state to seed the first call with. + meta: Additional metadata for the request. + + Returns: + The tool result. + + Raises: + InputRequiredRoundsExceededError: `input_required_max_rounds` exhausted. + MCPError: A callback returned `ErrorData` for an embedded input request. + pydantic.ValidationError: The server returned a result that does not + conform to the negotiated protocol version. + """ + + async def retry(r: InputResponses | None, s: str | None) -> CallToolResult | InputRequiredResult | Result: + return await self.session.call_tool( + name, + arguments, + read_timeout_seconds=read_timeout_seconds, + progress_callback=progress_callback, + input_responses=r, + request_state=s, + meta=meta, + allow_input_required=True, + # Input rounds resolve before a claimed result, so a claim may end any round. + allow_claimed=True, + ) + + result = await self._drive_input_required(await retry(input_responses, request_state), retry) + if isinstance(result, CallToolResult): + return result + # Only claimed shapes reach this point, so the lookup is total. + claim = self._folded_extensions.by_model[type(result)] + final = await claim.resolve( + result, + ClaimContext(session=self.session, tool_name=name, read_timeout_seconds=read_timeout_seconds), + ) + if not final.is_error: + # Match the direct path: revalidate the output schema, but never for isError results. + await self.session.validate_tool_result(name, final) + return final + + async def list_prompts( + self, + *, + cursor: str | None = None, + meta: RequestParamsMeta | None = None, + cache_mode: CacheMode = "use", + ) -> ListPromptsResult: + """List available prompts from the server.""" + return await self._cached_fetch( + "prompts/list", + cursor=cursor, + meta=meta, + cache_mode=cache_mode, + send=lambda: self.session.list_prompts(params=PaginatedRequestParams(cursor=cursor, _meta=meta)), + ) + + async def get_prompt( + self, + name: str, + arguments: dict[str, str] | None = None, + *, + input_responses: InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + ) -> GetPromptResult: + """Get a prompt from the server. + + If the server returns an `InputRequiredResult`, the embedded input + requests are dispatched to this client's sampling / elicitation / roots + callbacks and the get is retried automatically (up to + `input_required_max_rounds`). + + Args: + name: The name of the prompt. + arguments: Arguments to pass to the prompt. + input_responses: Responses to seed the first call with (e.g. when + resuming from a persisted `InputRequiredResult`). + request_state: Opaque state to seed the first call with. + meta: Additional metadata for the request. + + Returns: + The prompt content. + + Raises: + InputRequiredRoundsExceededError: `input_required_max_rounds` exhausted. + MCPError: A callback returned `ErrorData` for an embedded input request. + pydantic.ValidationError: The server returned a result that does not + conform to the negotiated protocol version. + """ + + async def retry(r: InputResponses | None, s: str | None) -> GetPromptResult | InputRequiredResult: + return await self.session.get_prompt( + name, arguments, input_responses=r, request_state=s, meta=meta, allow_input_required=True + ) + + return await self._drive_input_required(await retry(input_responses, request_state), retry) + + async def _drive_input_required( + self, + first: _ResultT | InputRequiredResult, + retry: Callable[[InputResponses | None, str | None], Awaitable[_ResultT | InputRequiredResult]], + ) -> _ResultT: + """Hand an `InputRequiredResult` to the SEP-2322 driver, or pass a terminal result through. + + `dispatch` routes each embedded request through the same callback table + that serves legacy server→client RPCs, so the two paths stay + behaviourally identical by construction. + """ + if not isinstance(first, InputRequiredResult): + return first + session = self.session + + async def dispatch(key: str, req: InputRequest) -> InputResponse | ErrorData: + ctx = ClientRequestContext(session=session, request_id=key, meta=req.params.meta if req.params else None) + return await session.dispatch_input_request(ctx, req) + + return await run_input_required_driver( + first, dispatch=dispatch, retry=retry, max_rounds=self.input_required_max_rounds + ) + + async def complete( + self, + ref: ResourceTemplateReference | PromptReference, + argument: dict[str, str], + context_arguments: dict[str, str] | None = None, + ) -> CompleteResult: + """Get completions for a prompt or resource template argument. + + Args: + ref: Reference to the prompt or resource template + argument: The argument to complete + context_arguments: Additional context arguments + + Returns: + Completion suggestions. + """ + return await self.session.complete(ref=ref, argument=argument, context_arguments=context_arguments) + + async def list_tools( + self, + *, + cursor: str | None = None, + meta: RequestParamsMeta | None = None, + cache_mode: CacheMode = "use", + ) -> ListToolsResult: + """List available tools from the server.""" + return await self._cached_fetch( + "tools/list", + cursor=cursor, + meta=meta, + cache_mode=cache_mode, + send=lambda: self.session.list_tools(params=PaginatedRequestParams(cursor=cursor, _meta=meta)), + # A cache hit skips session.list_tools, so the session re-absorbs the served + # listing to rebuild its derived per-tool state. Hits are cursorless, but a + # cached page 1 can carry next_cursor - never prune on a partial listing. + absorb=lambda hit: self.session._absorb_tool_listing( # pyright: ignore[reportPrivateUsage] + hit, complete=hit.next_cursor is None + ), + ) + + @deprecated("The roots capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def send_roots_list_changed(self) -> None: + """Send a notification that the roots list has changed.""" + await self.session.send_roots_list_changed() # pyright: ignore[reportDeprecated] diff --git a/venv/lib/python3.12/site-packages/mcp/client/context.py b/venv/lib/python3.12/site-packages/mcp/client/context.py new file mode 100644 index 0000000..aecd295 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/context.py @@ -0,0 +1,5 @@ +"""Request context for MCP client handlers.""" + +from mcp.client.session import ClientRequestContext + +__all__ = ["ClientRequestContext"] diff --git a/venv/lib/python3.12/site-packages/mcp/client/extension.py b/venv/lib/python3.12/site-packages/mcp/client/extension.py new file mode 100644 index 0000000..a813475 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/extension.py @@ -0,0 +1,196 @@ +"""Opt-in extension interface for MCP clients. + +Subclass `ClientExtension`, set `identifier`, override the hooks you need, and +pass instances to `Client(extensions=[...])`. For an identifier-only +capability ad, use `advertise()`. +""" + +from __future__ import annotations + +from collections.abc import Awaitable, Callable, Sequence +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Final, Generic, Literal, TypeVar, get_args + +from mcp_types import CORE_RESULT_TYPES, CallToolResult, InputRequiredResult, Result +from mcp_types.version import MODERN_PROTOCOL_VERSIONS +from pydantic import AliasChoices, AliasPath, BaseModel +from pydantic.fields import FieldInfo + +from mcp.shared.extension import validate_extension_identifier + +if TYPE_CHECKING: + from mcp.client.session import ClientSession + +__all__ = [ + "ClaimContext", + "ClientExtension", + "NotificationBinding", + "ResultClaim", + "UnexpectedClaimedResult", + "advertise", +] + +_CLAIM_METHODS: Final[frozenset[str]] = frozenset({"tools/call"}) +"""The closed set of verbs a claim may attach to; widen together with the `method` Literal.""" + +_RESERVED_WIRE_ALIASES: Final[frozenset[str]] = frozenset({"requestState", "inputRequests"}) +"""Typed optional fields of the core result surface that pre-validates every inbound result.""" + + +def _wire_keys(name: str, field: FieldInfo) -> frozenset[str]: + """Every top-level wire key this field can read from or write to.""" + keys = {field.alias or name} + if field.serialization_alias: + keys.add(field.serialization_alias) + validation_alias = field.validation_alias + choices = validation_alias.choices if isinstance(validation_alias, AliasChoices) else [validation_alias] + for choice in choices: + if isinstance(choice, AliasPath): + choice = choice.path[0] + if isinstance(choice, str): + keys.add(choice) + return frozenset(keys) + + +ClaimedT = TypeVar("ClaimedT", bound=Result) +NotifyParamsT = TypeVar("NotifyParamsT", bound=BaseModel) + + +@dataclass(frozen=True, kw_only=True) +class ClaimContext: + """Host-injected context for one `ResultClaim.resolve` call.""" + + session: ClientSession + tool_name: str + read_timeout_seconds: float | None + + +@dataclass(frozen=True, kw_only=True) +class ResultClaim(Generic[ClaimedT]): + """One extra result shape on one spec verb, keyed by the wire `resultType`. + + Active only while the declaring extension is constructed into the client and + the negotiated protocol version admits it. `resolve` finishes a claimed + result, may send follow-ups through `ctx.session`, and must return the + verb's ordinary result. All field constraints are enforced at construction. + """ + + result_type: str + model: type[ClaimedT] + resolve: Callable[[ClaimedT, ClaimContext], Awaitable[CallToolResult]] + method: Literal["tools/call"] = "tools/call" + protocol_versions: frozenset[str] | None = None + + def __post_init__(self) -> None: + if self.method not in _CLAIM_METHODS: + raise ValueError(f"claims attach to {sorted(_CLAIM_METHODS)} only; got method {self.method!r}") + if self.result_type in CORE_RESULT_TYPES: + raise ValueError(f"resultType {self.result_type!r} is core protocol vocabulary") + if Result not in self.model.__mro__: # runtime guard; the ClaimedT bound only constrains checked callers + raise ValueError(f"{self.model.__name__} must subclass mcp_types.Result") + if issubclass(self.model, CallToolResult | InputRequiredResult): + raise ValueError("claim models must not subclass core result types") + for name, model_field in self.model.model_fields.items(): + for clash in sorted(_wire_keys(name, model_field) & _RESERVED_WIRE_ALIASES): + raise ValueError( + f"{self.model.__name__}.{name} aliases {clash!r}, a typed field of the core " + "result surface; a colliding value would fail core validation before the " + "claim adapter runs" + ) + field = self.model.model_fields.get("result_type") + if field is None or get_args(field.annotation) != (self.result_type,): + raise ValueError(f"{self.model.__name__}.result_type must be Literal[{self.result_type!r}]") + if self.protocol_versions is not None and not self.protocol_versions: + raise ValueError("empty protocol_versions could never activate; use None for all") + if self.protocol_versions is not None and not self.protocol_versions.issubset(MODERN_PROTOCOL_VERSIONS): + unrecognized = sorted(self.protocol_versions.difference(MODERN_PROTOCOL_VERSIONS)) + raise ValueError( + f"protocol_versions {unrecognized} are not modern protocol revisions; claimed shapes " + "cannot be delivered on a legacy wire (None means every modern version)" + ) + + +class UnexpectedClaimedResult(RuntimeError): + """A claimed (extension) result arrived on a `call_tool` that did not opt in. + + The parsed value is carried as `result`; the server may already hold state it + references. Opt in via `Client(extensions=[...])` or `allow_claimed=True`. + """ + + def __init__(self, result: Result) -> None: + super().__init__( + f"Server returned a claimed result ({type(result).__name__}); pass the owning extension to " + "Client(extensions=[...]) for transparent resolution, or call with allow_claimed=True " + "and handle the shape. The carried result may reference server-side state needing cleanup." + ) + self.result = result + + +@dataclass(frozen=True, kw_only=True) +class NotificationBinding(Generic[NotifyParamsT]): + """Deliver server notifications for `method` (the bare wire name) to `handler`. + + Observation-only: validated params arrive one at a time per binding, in + dispatch order, through a bounded queue that drops the oldest with a warning + on overflow. Stream transports dispatch each notification independently, so + near-simultaneous notifications may be dispatched out of wire order. Methods + the negotiated version's core tables handle are never delivered to bindings. + """ + + method: str + params_type: type[NotifyParamsT] + handler: Callable[[NotifyParamsT], Awaitable[None]] + + +class ClientExtension: + """Base class for an opt-in client extension; override only what you need. + + The surface is declarative, fixed at construction, and never receives the client. + """ + + #: Reverse-DNS extension identifier, advertised under `ClientCapabilities.extensions`. + identifier: str + + def __init_subclass__(cls, **kwargs: Any) -> None: + super().__init_subclass__(**kwargs) + # Per-instance identifiers (assigned in __init__) are validated at consumption instead. + if (identifier := cls.__dict__.get("identifier")) is not None: + validate_extension_identifier(identifier, owner=cls.__name__) + + def settings(self) -> dict[str, Any]: + """Per-extension settings advertised at `ClientCapabilities.extensions[identifier]`. + + Read once at `Client` construction. A claim-bearing extension is + advertised only at protocol versions where at least one of its claims + is active. + """ + return {} + + def claims(self) -> Sequence[ResultClaim[Any]]: + """Extra result shapes this extension claims, with their resolvers.""" + return () + + def notifications(self) -> Sequence[NotificationBinding[Any]]: + """Server notifications this extension observes.""" + return () + + +class _AdvertiseOnly(ClientExtension): + """Ad-only extension returned by `advertise()`.""" + + def __init__(self, identifier: str, settings: dict[str, Any]) -> None: + self.identifier = identifier + self._settings = settings + + def settings(self) -> dict[str, Any]: + return self._settings + + +def advertise(identifier: str, settings: dict[str, Any] | None = None) -> ClientExtension: + """Advertise an extension identifier (with optional settings) and nothing else. + + Advertising an extension you do not implement asserts wire support you do + not have; for behavioral extensions construct the real extension instead. + """ + validate_extension_identifier(identifier, owner="advertise") + return _AdvertiseOnly(identifier, {} if settings is None else settings) diff --git a/venv/lib/python3.12/site-packages/mcp/client/session.py b/venv/lib/python3.12/site-packages/mcp/client/session.py new file mode 100644 index 0000000..f18cc0e --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/session.py @@ -0,0 +1,1507 @@ +from __future__ import annotations + +import json +import logging +from collections.abc import Callable, Mapping, Sequence +from dataclasses import dataclass +from functools import cache, reduce +from operator import or_ +from types import TracebackType, UnionType +from typing import TYPE_CHECKING, Annotated, Any, Final, Literal, Protocol, TypeAlias, cast, get_args, overload + +import anyio +import anyio.abc +import anyio.lowlevel +import mcp_types as types +from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream +from mcp_types import ( + CLIENT_CAPABILITIES_META_KEY, + CLIENT_INFO_META_KEY, + CONNECTION_CLOSED, + INTERNAL_ERROR, + LOG_LEVEL_META_KEY, + METHOD_NOT_FOUND, + PROTOCOL_VERSION_META_KEY, + SERVER_INFO_META_KEY, + UNSUPPORTED_PROTOCOL_VERSION, + RequestId, + RequestParamsMeta, +) +from mcp_types import methods as _methods +from mcp_types.version import ( + HANDSHAKE_PROTOCOL_VERSIONS, + KNOWN_PROTOCOL_VERSIONS, + LATEST_HANDSHAKE_VERSION, + LATEST_MODERN_VERSION, + MODERN_PROTOCOL_VERSIONS, +) +from pydantic import BaseModel, Discriminator, Tag, TypeAdapter, ValidationError +from typing_extensions import Self, TypeVar, deprecated + +from mcp.client._transport import ReadStream, WriteStream +from mcp.client.extension import NotificationBinding, ResultClaim, UnexpectedClaimedResult +from mcp.client.subscriptions import ListenRoute +from mcp.shared._compat import resync_tracer +from mcp.shared.dispatcher import CallOptions, DispatchContext, Dispatcher, ProgressFnT, as_request_id +from mcp.shared.exceptions import MCPDeprecationWarning, MCPError +from mcp.shared.inbound import ( + MCP_METHOD_HEADER, + MCP_NAME_HEADER, + MCP_PROTOCOL_VERSION_HEADER, + NAME_BEARING_METHODS, + encode_header_value, + find_invalid_x_mcp_header, + mcp_param_headers, + x_mcp_header_map, +) +from mcp.shared.jsonrpc_dispatcher import JSONRPCDispatcher, cancelled_request_id_from_params +from mcp.shared.message import ClientMessageMetadata, SessionMessage +from mcp.shared.subscriptions import SUBSCRIPTION_ID_META_KEY, event_from_wire +from mcp.shared.transport_context import TransportContext + +if TYPE_CHECKING: + # `jsonschema` is imported lazily inside `validate_tool_result`: pulling it (and its + # `attrs`/`referencing` tree) in at module scope costs every client that never validates. + from jsonschema.protocols import Validator + +DEFAULT_CLIENT_INFO = types.Implementation(name="mcp", version="0.1.0") +DISCOVER_TIMEOUT_SECONDS = 10.0 +_NOTIFICATION_QUEUE_SIZE: Final = 256 + +logger = logging.getLogger("client") + + +def _clamp_inbound_ttl(raw: dict[str, Any]) -> None: + """Floor a negative inbound `ttlMs` to 0 before `ge=0` validation fails the call (2026-07-28 caching SHOULD).""" + ttl = raw.get("ttlMs") + if isinstance(ttl, int | float) and not isinstance(ttl, bool) and ttl < 0: + raw["ttlMs"] = 0 + + +@cache +def _wire_fields(target: type[BaseModel] | UnionType) -> frozenset[str]: + """Top-level wire keys `target` declares (its members', for a union). + + A `RootModel` row (e.g. an empty result carried as `RootModel[Result]`) + reports its wrapped type's keys, not the pydantic-internal `root`. + """ + members: tuple[Any, ...] = get_args(target) if isinstance(target, UnionType) else (target,) + models = [m for m in members if isinstance(m, type) and issubclass(m, BaseModel)] + fields: set[str] = set() + for model in models: + if getattr(model, "__pydantic_root_model__", False): # a RootModel wrapper row + fields |= _wire_fields(model.model_fields["root"].annotation) + else: + fields.update(field.alias or name for name, field in model.model_fields.items()) + return frozenset(fields) + + +@cache +def _later_revision_fields(method: str, version: str) -> frozenset[str]: + """Result keys a revision newer than `version` declares for `method` but `version` doesn't. + + The version-free result types carry every revision's fields, so such a key + (e.g. 2026-07-28 `ttlMs`/`cacheScope` on a pre-2026 session) is outside the + negotiated contract yet would still parse into the model and trip that later + revision's constraints. Empty at the newest known revision. + """ + current = _methods.SERVER_RESULTS.get((method, version)) + if current is None or version not in KNOWN_PROTOCOL_VERSIONS: + return frozenset() + newer = KNOWN_PROTOCOL_VERSIONS[KNOWN_PROTOCOL_VERSIONS.index(version) + 1 :] + later: set[str] = set() + for revision in newer: + row = _methods.SERVER_RESULTS.get((method, revision)) + if row is not None: + later |= _wire_fields(row) + return frozenset(later) - _wire_fields(current) + + +def _same_schema(a: dict[str, Any] | None, b: dict[str, Any] | None) -> bool: + """JSON equality for two output schemas. + + Python `==` is not JSON equality: it conflates `True`/`1` and `False`/`0`, which JSON + Schema keeps distinct (`const: true` vs `const: 1`). Canonical serialization compares as + JSON does; where it is stricter (`1` vs `1.0`), erring toward "changed" only costs a + recompile, never a stale validator. + """ + return json.dumps(a, sort_keys=True) == json.dumps(b, sort_keys=True) + + +def _preconnect_stamp(data: dict[str, Any], opts: CallOptions) -> None: + # initialize/discover forbid cancellation; other pre-handshake requests (lowlevel + # ClientSession callers may skip the handshake entirely) keep the courtesy cancel. + if data["method"] in ("initialize", "server/discover"): + opts["cancel_on_abandon"] = False + + +def _parse_server_info_stamp(result: types.DiscoverResult) -> types.Implementation | None: + """The typed identity from a discover result's `_meta` serverInfo stamp. + + The stamp is display-only per the spec, so absent and malformed both read + as `None` rather than failing the connection. + """ + raw = (result.meta or {}).get(SERVER_INFO_META_KEY) + if raw is None: + return None + try: + return types.Implementation.model_validate(raw) + except ValidationError: + return None + + +def _make_handshake_stamp(protocol_version: str) -> Callable[[dict[str, Any], CallOptions], None]: + def stamp(data: dict[str, Any], opts: CallOptions) -> None: + opts.setdefault("headers", {})[MCP_PROTOCOL_VERSION_HEADER] = protocol_version + + return stamp + + +def _make_modern_stamp( + protocol_version: str, + client_info: dict[str, Any], + capabilities: dict[str, Any], + resolve_param_headers: Callable[[str, Mapping[str, Any]], dict[str, str]], + *, + log_level: types.LoggingLevel | None = None, +) -> Callable[[dict[str, Any], CallOptions], None]: + def stamp(data: dict[str, Any], opts: CallOptions) -> None: + params = data.setdefault("params", {}) + meta = params.setdefault("_meta", {}) + meta[PROTOCOL_VERSION_META_KEY] = protocol_version + meta[CLIENT_INFO_META_KEY] = client_info + meta[CLIENT_CAPABILITIES_META_KEY] = capabilities + # The per-request log-delivery opt-in (2026 logging is opt-in per + # request). A default the caller can override on any single call by + # supplying the key in that request's `_meta`, hence setdefault. + if log_level is not None: + meta.setdefault(LOG_LEVEL_META_KEY, log_level) + # `cancel_on_abandon` stays at the dispatcher default (True): the + # courtesy `notifications/cancelled` is the abandon signal. On the + # stream transports it is the 2026 wire's cancellation spelling; the + # streamable-HTTP transport translates it into aborting the request's + # own POST instead of writing it (the 2026 HTTP wire has no + # client-to-server notifications - closing the stream is the signal). + # The negotiation methods still opt out, mirroring `_preconnect_stamp`: + # the spec forbids cancelling them. + if data["method"] in ("initialize", "server/discover"): + opts["cancel_on_abandon"] = False + headers = opts.setdefault("headers", {}) + headers[MCP_PROTOCOL_VERSION_HEADER] = protocol_version + headers[MCP_METHOD_HEADER] = data["method"] + name_key = NAME_BEARING_METHODS.get(data["method"]) + if name_key is not None and isinstance(name := params.get(name_key), str): + headers[MCP_NAME_HEADER] = encode_header_value(name) + if data["method"] == "tools/call" and isinstance(name := params.get("name"), str): + headers.update(resolve_param_headers(name, params.get("arguments") or {})) + + return stamp + + +ReceiveResultT = TypeVar("ReceiveResultT", bound=BaseModel) + + +@dataclass(kw_only=True) +class ClientRequestContext: + """Context for a server-initiated request, passed to the sampling/elicitation/list-roots callbacks.""" + + session: ClientSession + request_id: RequestId + meta: RequestParamsMeta | None = None + + +class SamplingFnT(Protocol): + async def __call__( + self, + context: ClientRequestContext, + params: types.CreateMessageRequestParams, + ) -> types.CreateMessageResult | types.CreateMessageResultWithTools | types.ErrorData: ... # pragma: no branch + + +class ElicitationFnT(Protocol): + async def __call__( + self, + context: ClientRequestContext, + params: types.ElicitRequestParams, + ) -> types.ElicitResult | types.ErrorData: ... # pragma: no branch + + +class ListRootsFnT(Protocol): + async def __call__( + self, context: ClientRequestContext + ) -> types.ListRootsResult | types.ErrorData: ... # pragma: no branch + + +class LoggingFnT(Protocol): + async def __call__(self, params: types.LoggingMessageNotificationParams) -> None: ... # pragma: no branch + + +IncomingMessage: TypeAlias = types.ServerNotification | Exception +"""What `message_handler` receives: the server notifications the session surfaces, plus transport-level exceptions. + +`notifications/cancelled` is applied by the dispatcher and never surfaced, and a +`notifications/subscriptions/acknowledged` for a live `listen()` stream is consumed by that +stream, so neither reaches the handler. +""" + + +class MessageHandlerFnT(Protocol): + async def __call__(self, message: IncomingMessage) -> None: ... # pragma: no branch + + +async def _default_message_handler(message: IncomingMessage) -> None: + await anyio.lowlevel.checkpoint() + + +async def _default_sampling_callback( + context: ClientRequestContext, + params: types.CreateMessageRequestParams, +) -> types.CreateMessageResult | types.CreateMessageResultWithTools | types.ErrorData: + return types.ErrorData( + code=types.INVALID_REQUEST, + message="Sampling not supported", + ) + + +async def _default_elicitation_callback( + context: ClientRequestContext, + params: types.ElicitRequestParams, +) -> types.ElicitResult | types.ErrorData: + return types.ErrorData( + code=types.INVALID_REQUEST, + message="Elicitation not supported", + ) + + +async def _default_list_roots_callback( + context: ClientRequestContext, +) -> types.ListRootsResult | types.ErrorData: + return types.ErrorData( + code=types.INVALID_REQUEST, + message="List roots not supported", + ) + + +async def _default_logging_callback( + params: types.LoggingMessageNotificationParams, +) -> None: + pass + + +ClientResponse: TypeAdapter[types.ClientResult | types.ErrorData] = TypeAdapter(types.ClientResult | types.ErrorData) + +# Typed against the wide parse union so adopt-built claim adapters share this attribute type. +_CallToolResultAdapter: TypeAdapter[types.CallToolResult | types.InputRequiredResult | types.Result] = TypeAdapter( + types.CallToolResult | types.InputRequiredResult +) +_GetPromptResultAdapter: TypeAdapter[types.GetPromptResult | types.InputRequiredResult] = TypeAdapter( + types.GetPromptResult | types.InputRequiredResult +) +_ReadResourceResultAdapter: TypeAdapter[types.ReadResourceResult | types.InputRequiredResult] = TypeAdapter( + types.ReadResourceResult | types.InputRequiredResult +) + + +def _claim_active(claim: ResultClaim[Any], version: str) -> bool: + """A claim is active at modern versions only, narrowed by its optional version subset.""" + return version in MODERN_PROTOCOL_VERSIONS and ( + claim.protocol_versions is None or version in claim.protocol_versions + ) + + +def _active_claims_at( + claims_by_extension: Mapping[str, tuple[ResultClaim[Any], ...]], version: str +) -> dict[str, ResultClaim[Any]]: + """Claims active at `version`, keyed by wire tag; empty at any legacy version.""" + return { + claim.result_type: claim + for claims in claims_by_extension.values() + for claim in claims + if _claim_active(claim, version) + } + + +def _build_call_tool_adapter( + active: Mapping[str, ResultClaim[Any]], +) -> TypeAdapter[types.CallToolResult | types.InputRequiredResult | types.Result]: + """Build a discriminated tools/call adapter: a core arm plus one arm per active claim.""" + if not active: + return _CallToolResultAdapter + tags = frozenset(active) + core_arm = "core" + while core_arm in tags: # the routing sentinel must never collide with a claimed tag + core_arm += "-" + + def _route(value: Any) -> str: + # pydantic hands the discriminator either the raw dict or an already-built model. + # Unknown or non-string tags route to the core arm and fail core validation there. + if isinstance(value, dict): + tag = cast("dict[str, Any]", value).get("resultType") + else: + tag = getattr(value, "result_type", None) + return tag if isinstance(tag, str) and tag in tags else core_arm + + arms: list[Any] = [Annotated[types.CallToolResult | types.InputRequiredResult, Tag(core_arm)]] + arms += [Annotated[claim.model, Tag(tag)] for tag, claim in active.items()] + # reduce(or_) rather than Union star-unpack, which needs py3.11+. + return TypeAdapter(Annotated[reduce(or_, arms), Discriminator(_route)]) + + +def _index_claims( + result_claims: Mapping[str, Sequence[ResultClaim[Any]]] | None, + extensions: dict[str, dict[str, Any]] | None, +) -> dict[str, tuple[ResultClaim[Any], ...]]: + """Validate and copy the claims-by-extension mapping.""" + indexed: dict[str, tuple[ResultClaim[Any], ...]] = {} + seen: set[str] = set() + for identifier, claims in (result_claims or {}).items(): + if extensions is None or identifier not in extensions: + raise ValueError( + f"result_claims key {identifier!r} has no extensions entry; a claim is only " + "advertised through its extension's capability ad" + ) + if not claims: + raise ValueError( + f"result_claims[{identifier!r}] is empty and would drop the extension from " + "the capability ad at every version. Omit the key instead" + ) + for claim in claims: + if claim.result_type in seen: + raise ValueError(f"duplicate result claim for resultType {claim.result_type!r}") + seen.add(claim.result_type) + indexed[identifier] = tuple(claims) + return indexed + + +def _index_bindings( + notification_bindings: Sequence[NotificationBinding[Any]] | None, +) -> dict[str, NotificationBinding[Any]]: + """Index bindings by wire method, rejecting duplicates.""" + indexed: dict[str, NotificationBinding[Any]] = {} + for binding in notification_bindings or (): + if binding.method in indexed: + raise ValueError(f"duplicate notification binding for method {binding.method!r}") + indexed[binding.method] = binding + return indexed + + +def _input_required_unexpected(method: str) -> RuntimeError: + return RuntimeError( + "Server returned InputRequiredResult; pass allow_input_required=True to receive it " + f"and retry {method}(..., input_responses=..., request_state=result.request_state)." + ) + + +class ClientSession: + """Client half of an MCP connection, running on a `Dispatcher`. + + Construct it over a transport's stream pair (or pass a pre-built + `dispatcher=`), enter as an async context manager, then call + `initialize()`. The dispatcher owns the receive loop and request + correlation; this class owns the typed MCP layer and the constructor + callbacks. Transport `Exception` items reach `message_handler` on any + stream-backed dispatcher (`JSONRPCDispatcher`), whether built here from a + stream pair or supplied without a stream-exception hook of its own; an + in-process `DirectDispatcher` carries none. + + Extension `result_claims` fold into tools/call parsing at `adopt()`; + `notification_bindings` observe vendor notifications via bounded FIFOs. + """ + + def __init__( + self, + read_stream: ReadStream[SessionMessage | Exception] | None = None, + write_stream: WriteStream[SessionMessage] | None = None, + read_timeout_seconds: float | None = None, + sampling_callback: SamplingFnT | None = None, + elicitation_callback: ElicitationFnT | None = None, + list_roots_callback: ListRootsFnT | None = None, + logging_callback: LoggingFnT | None = None, + message_handler: MessageHandlerFnT | None = None, + client_info: types.Implementation | None = None, + *, + log_level: types.LoggingLevel | None = None, + sampling_capabilities: types.SamplingCapability | None = None, + extensions: dict[str, dict[str, Any]] | None = None, + result_claims: Mapping[str, Sequence[ResultClaim[Any]]] | None = None, + notification_bindings: Sequence[NotificationBinding[Any]] | None = None, + dispatcher: Dispatcher[Any] | None = None, + ) -> None: + self._session_read_timeout_seconds = read_timeout_seconds + self._client_info = client_info or DEFAULT_CLIENT_INFO + self._sampling_callback = sampling_callback or _default_sampling_callback + self._sampling_capabilities = sampling_capabilities + self._extensions = dict(extensions) if extensions is not None else None + self._result_claims = _index_claims(result_claims, extensions) + self._notification_bindings = _index_bindings(notification_bindings) + self._active_claims: dict[str, ResultClaim[Any]] = {} + self._call_tool_adapter = _CallToolResultAdapter + self._binding_queues: dict[ + str, tuple[MemoryObjectSendStream[BaseModel], MemoryObjectReceiveStream[BaseModel]] + ] = {} + self._elicitation_callback = elicitation_callback or _default_elicitation_callback + self._list_roots_callback = list_roots_callback or _default_list_roots_callback + self._logging_callback = logging_callback or _default_logging_callback + self._log_level: types.LoggingLevel | None = log_level + self._message_handler = message_handler or _default_message_handler + self._tool_output_schemas: dict[str, dict[str, Any] | None] = {} + # Compiled output-schema validators, derived from `_tool_output_schemas` and owned by + # `_absorb_tool_listing`, which evicts a tool's entry whenever its schema changes. + self._tool_output_validators: dict[str, Validator] = {} + self._x_mcp_header_maps: dict[str, dict[tuple[str, ...], str]] = {} + self._initialize_result: types.InitializeResult | None = None + self._discover_result: types.DiscoverResult | None = None + self._discover_server_info: types.Implementation | None = None + self._negotiated_version: str | None = None + self._stamp: Callable[[dict[str, Any], CallOptions], None] = _preconnect_stamp + self._task_group: anyio.abc.TaskGroup | None = None + # subscriptions/listen demux routes; membership decides ack consumption (raw listens are never registered) + self._listen_routes: dict[RequestId, ListenRoute] = {} + if dispatcher is not None: + if read_stream is not None or write_stream is not None: + raise ValueError("pass read_stream/write_stream or dispatcher, not both") + self._dispatcher: Dispatcher[Any] = dispatcher + if isinstance(dispatcher, JSONRPCDispatcher) and dispatcher.on_stream_exception is None: + # Route transport-level Exception items into message_handler — only + # stream-backed dispatchers carry these; DirectDispatcher has none. + # Don't clobber a caller-supplied hook. + # TODO(L78): this leaves a bound-method ref on the dispatcher after the + # session exits (memory pin) and a second wrap of the same dispatcher would + # skip install. The Transport-as-Dispatcher rework (L77) removes this seam. + dispatcher.on_stream_exception = self._on_stream_exception + else: + if read_stream is None or write_stream is None: + raise ValueError("read_stream and write_stream are required when no dispatcher is given") + # Built eagerly so notifications can be sent before entering the context manager. + self._dispatcher = JSONRPCDispatcher( + read_stream, write_stream, on_stream_exception=self._on_stream_exception + ) + + async def __aenter__(self) -> Self: + self._task_group = anyio.create_task_group() + await self._task_group.__aenter__() + try: + # Queues must exist before the dispatcher starts: _on_notify enqueues into this dict. + for binding in self._notification_bindings.values(): + send, receive = anyio.create_memory_object_stream[BaseModel](_NOTIFICATION_QUEUE_SIZE) + self._binding_queues[binding.method] = (send, receive) + await self._task_group.start( + self._dispatcher.run, self._on_request, self._on_notify, self._intercept_notification + ) + for binding in self._notification_bindings.values(): + _, receive = self._binding_queues[binding.method] + self._task_group.start_soon(self._deliver_bound_notifications, binding, receive) + except BaseException: + # Unwind the entered task group before propagating: a cancellation + # landing here (e.g. `move_on_after` around connect) would abandon + # it and anyio would later raise "exited non-innermost cancel scope". + task_group = self._task_group + self._task_group = None + task_group.cancel_scope.cancel() + # Shield the group's own scope (a new one would break LIFO exit) + # so a pending outer cancellation cannot re-fire inside __aexit__. + task_group.cancel_scope.shield = True + try: + await task_group.__aexit__(None, None, None) + finally: + self._close_binding_queues() + raise + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + # Exit must not block: cancel the dispatcher, binding consumers, and in-flight callbacks. + assert self._task_group is not None + self._task_group.cancel_scope.cancel() + try: + result = await self._task_group.__aexit__(exc_type, exc_val, exc_tb) + finally: + self._close_binding_queues() + self._settle_listen_routes_closed() + await resync_tracer() + return result + + def _close_binding_queues(self) -> None: + # Unclosed memory object streams warn at garbage collection; close is idempotent. + for send, receive in self._binding_queues.values(): + send.close() + receive.close() + self._binding_queues.clear() + + async def _deliver_bound_notifications( + self, binding: NotificationBinding[Any], receive: MemoryObjectReceiveStream[BaseModel] + ) -> None: + """Consume one binding's FIFO, decoupled from the dispatcher so handlers can do session I/O.""" + while True: + params = await receive.receive() + try: + await binding.handler(params) + except Exception: + # A raising handler costs only that delivery, as in _on_notify. + logger.exception("notification binding handler for %r raised", binding.method) + + async def send_request( + self, + request: types.ClientRequest | types.Request[Any, Any], + result_type: type[ReceiveResultT] | TypeAdapter[ReceiveResultT], + request_read_timeout_seconds: float | None = None, + metadata: ClientMessageMetadata | None = None, + progress_callback: ProgressFnT | None = None, + ) -> ReceiveResultT: + """Send a request and wait for its typed result. + + Args: + metadata: Streamable HTTP resumption hints. + + Raises: + MCPError: Error response, read timeout, or connection closed. + RuntimeError: Called before entering the context manager. + ValueError: The request declares `name_param` but its params carry no string name. + pydantic.ValidationError: The server returned a result that does not + conform to the negotiated protocol version. + """ + data = request.model_dump(by_alias=True, mode="json", exclude_none=True) + method: str = data["method"] + opts: CallOptions = {} + self._stamp(data, opts) + # The stamp runs first, so its NAME_BEARING_METHODS rows win; a missing name fails loud. + headers = opts.setdefault("headers", {}) + if (key := type(request).name_param) is not None and MCP_NAME_HEADER not in headers: + params_data: dict[str, Any] = data.get("params") or {} + name = params_data.get(key) + if not isinstance(name, str): + raise ValueError(f"{method} requires params[{key!r}] for Mcp-Name") + headers[MCP_NAME_HEADER] = encode_header_value(name) + timeout = ( + request_read_timeout_seconds + if request_read_timeout_seconds is not None + else self._session_read_timeout_seconds + ) + if timeout is not None: + opts["timeout"] = timeout + if progress_callback is not None: + opts["on_progress"] = progress_callback + if metadata is not None: + if metadata.resumption_token is not None: + opts["resumption_token"] = metadata.resumption_token + if metadata.on_resumption_token_update is not None: + opts["on_resumption_token"] = metadata.on_resumption_token_update + raw = await self._dispatcher.send_raw_request(method, data.get("params"), opts) + _clamp_inbound_ttl(raw) + # Literal fallback covers pre-handshake and stateless; matches runner.py. + version = self._negotiated_version or "2025-11-25" + try: + _methods.validate_server_result(method, version, raw) + except KeyError: + pass + # Drop a later revision's fields (e.g. 2026-07-28 cache hints on a pre-2026 + # session): they are outside the negotiated contract, and the version-free + # result type would otherwise apply that revision's constraints to them. + if not (foreign := _later_revision_fields(method, version)).isdisjoint(raw): + raw = {key: value for key, value in raw.items() if key not in foreign} + if isinstance(result_type, TypeAdapter): + return result_type.validate_python(raw, by_name=False) + return result_type.model_validate(raw, by_name=False) + + async def send_notification(self, notification: types.ClientNotification) -> None: + """Send a one-way notification. Usable before entering the context manager. + + Fire-and-forget: after the connection has closed, the notification is + dropped with a debug log instead of raising. + """ + data = notification.model_dump(by_alias=True, mode="json", exclude_none=True) + opts: CallOptions = {} + self._stamp(data, opts) + await self._dispatcher.notify(data["method"], data.get("params"), opts) + + def _build_capabilities(self, version: str) -> types.ClientCapabilities: + """Build the capability ad for a wire speaking `version`. + + Claim-bearing identifiers whose claims are all inactive at `version` drop, so + the client never advertises result shapes it would reject; claim-less + identifiers always advertise. + """ + extensions = self._extensions + if extensions is not None and self._result_claims: + extensions = { + identifier: settings + for identifier, settings in extensions.items() + if identifier not in self._result_claims + or any(_claim_active(claim, version) for claim in self._result_claims[identifier]) + } or None + sampling = ( + (self._sampling_capabilities or types.SamplingCapability()) + if self._sampling_callback is not _default_sampling_callback + else None + ) + elicitation = ( + types.ElicitationCapability(form=types.FormElicitationCapability(), url=types.UrlElicitationCapability()) + if self._elicitation_callback is not _default_elicitation_callback + else None + ) + roots = ( + # TODO: Should this be based on whether we + # _will_ send notifications, or only whether + # they're supported? + types.RootsCapability(list_changed=True) + if self._list_roots_callback is not _default_list_roots_callback + else None + ) + return types.ClientCapabilities( + sampling=sampling, elicitation=elicitation, experimental=None, extensions=extensions, roots=roots + ) + + async def initialize(self) -> types.InitializeResult: + if self._initialize_result is not None: + return self._initialize_result + result = await self.send_request( + types.InitializeRequest( + params=types.InitializeRequestParams( + protocol_version=LATEST_HANDSHAKE_VERSION, + # The handshake negotiates only legacy versions, where no claim is active. + capabilities=self._build_capabilities(LATEST_HANDSHAKE_VERSION), + client_info=self._client_info, + ), + ), + types.InitializeResult, + ) + + if result.protocol_version not in HANDSHAKE_PROTOCOL_VERSIONS: + raise RuntimeError(f"Unsupported protocol version from the server: {result.protocol_version}") + + self.adopt(result) + + await self.send_notification(types.InitializedNotification()) + + return result + + def adopt(self, result: types.InitializeResult | types.DiscoverResult) -> None: + """Install negotiated state from a result the caller already holds (no wire traffic). + + Clears the opposite slot, so at most one of `initialize_result` / + `discover_result` is ever non-None. + + Raises: + RuntimeError: `result` is a `DiscoverResult` whose `supported_versions` + shares nothing with this client's `MODERN_PROTOCOL_VERSIONS`. + """ + if isinstance(result, types.DiscoverResult): + # ordered oldest→newest via MODERN_PROTOCOL_VERSIONS + mutual = [v for v in MODERN_PROTOCOL_VERSIONS if v in result.supported_versions] + if not mutual: + raise RuntimeError( + f"No mutually supported modern protocol version " + f"(server: {result.supported_versions}, client: {list(MODERN_PROTOCOL_VERSIONS)})" + ) + version = mutual[-1] + client_info = self._client_info.model_dump(by_alias=True, mode="json", exclude_none=True) + capabilities = self._build_capabilities(version).model_dump(by_alias=True, mode="json", exclude_none=True) + self._stamp = _make_modern_stamp( + version, client_info, capabilities, self._resolve_param_headers, log_level=self._log_level + ) + self._discover_result = result + self._discover_server_info = _parse_server_info_stamp(result) + self._initialize_result = None + else: + version = result.protocol_version + self._stamp = _make_handshake_stamp(version) + self._initialize_result = result + self._discover_result = None + self._discover_server_info = None + self._negotiated_version = version + # Both arms reach here, so re-adoption resets cleanly; legacy versions activate no claims. + # Core-vocabulary tags are unconstructible (ResultClaim.__post_init__), so no exclusion needed. + self._active_claims = _active_claims_at(self._result_claims, version) + self._call_tool_adapter = _build_call_tool_adapter(self._active_claims) + for method in self._notification_bindings: + # Bindings are consulted only for methods core does not know, so this one can never fire. + if (method, version) in _methods.SERVER_NOTIFICATIONS: + logger.warning( + "notification binding for %r will never fire at %s: the core protocol defines this method", + method, + version, + ) + + async def send_discover(self, version: str) -> dict[str, Any]: + """Send a single ``server/discover`` at ``version`` and return the raw result dict. + + No retry, no ``adopt()``. The ``_meta`` envelope and the + ``Mcp-Protocol-Version`` header are stamped at ``version`` so the + server-side era router sees a coherent probe. Used by ``discover()`` and + the connect-time auto-negotiation policy. + + Raises: + MCPError: The server returned a JSON-RPC error, or the transport + bounced the request at its own layer (a bare HTTP 4xx is + synthesized into a JSON-RPC error by the transport). + """ + client_info = self._client_info.model_dump(by_alias=True, mode="json", exclude_none=True) + capabilities = self._build_capabilities(version).model_dump(by_alias=True, mode="json", exclude_none=True) + request = types.DiscoverRequest( + params=types.RequestParams( + _meta={ + PROTOCOL_VERSION_META_KEY: version, + CLIENT_INFO_META_KEY: client_info, + CLIENT_CAPABILITIES_META_KEY: capabilities, + } + ) + ) + data = request.model_dump(by_alias=True, mode="json", exclude_none=True) + opts: CallOptions = { + "timeout": DISCOVER_TIMEOUT_SECONDS, + "cancel_on_abandon": False, + "headers": {MCP_PROTOCOL_VERSION_HEADER: version, MCP_METHOD_HEADER: data["method"]}, + } + raw = await self._dispatcher.send_raw_request(data["method"], data.get("params"), opts) + # Un-floored, a negative ttl fails the mode='auto' probe's validation and silently downgrades the handshake. + _clamp_inbound_ttl(raw) + return raw + + async def discover(self) -> types.DiscoverResult: + """Probe `server/discover` and adopt the result. + + Sends a single `server/discover` proposing the newest modern protocol + version. On `UNSUPPORTED_PROTOCOL_VERSION` (-32022) the server's + `supported` list is intersected with `MODERN_PROTOCOL_VERSIONS` and the + probe is retried once at the highest mutual version. Any other error — + including `METHOD_NOT_FOUND` (-32601) and `REQUEST_TIMEOUT` (-32001) — + propagates; the legacy `initialize()` fallback is the caller's policy. + + Raises: + MCPError: The server rejected `server/discover`, the probe timed + out, or the -32022 retry found no mutual version / failed again. + RuntimeError: `adopt()` found no mutual version in the returned + `supported_versions`. + """ + if self._discover_result is not None: + return self._discover_result + + try: + raw = await self.send_discover(LATEST_MODERN_VERSION) + except MCPError as e: + if e.code != UNSUPPORTED_PROTOCOL_VERSION: + raise + try: + data = types.UnsupportedProtocolVersionErrorData.model_validate(e.error.data) + except ValidationError: + raise e from None + # ordered oldest→newest via MODERN_PROTOCOL_VERSIONS + mutual = [v for v in MODERN_PROTOCOL_VERSIONS if v in data.supported] + if not mutual: + raise + raw = await self.send_discover(mutual[-1]) + + result = types.DiscoverResult.model_validate(raw) + self.adopt(result) + return result + + @property + def initialize_result(self) -> types.InitializeResult | None: + """The server's InitializeResult. None unless `initialize()` ran (or was adopted).""" + return self._initialize_result + + @property + def discover_result(self) -> types.DiscoverResult | None: + """The server's DiscoverResult. None unless `discover()` ran (or was adopted). + + Retained intact (supported_versions, ttl_ms, cache_scope) so callers + can round-trip it as ``prior_discover=``. + """ + return self._discover_result + + @property + def protocol_version(self) -> str | None: + """Negotiated protocol version. None until `initialize()`, `discover()`, or `adopt()`.""" + return self._negotiated_version + + @property + def server_info(self) -> types.Implementation | None: + """Server name/version. None until `initialize()`, `discover()`, or `adopt()`. + + On 2026-era connections this is the discover result's optional `_meta` + `serverInfo` stamp, parsed once at adopt time; `None` when the server + did not identify itself. The stamp is display-only per the spec, so a + malformed value reads as absent rather than failing the connection. + """ + if self._discover_result is not None: + return self._discover_server_info + if self._initialize_result is not None: + return self._initialize_result.server_info + return None + + @property + def server_capabilities(self) -> types.ServerCapabilities | None: + """Server capabilities. None until `initialize()`, `discover()`, or `adopt()`.""" + if self._discover_result is not None: + return self._discover_result.capabilities + if self._initialize_result is not None: + return self._initialize_result.capabilities + return None + + @property + def instructions(self) -> str | None: + """Server-provided instructions text, if any.""" + if self._discover_result is not None: + return self._discover_result.instructions + if self._initialize_result is not None: + return self._initialize_result.instructions + return None + + async def send_ping(self, *, meta: RequestParamsMeta | None = None) -> types.EmptyResult: + """Send a ping request.""" + return await self.send_request(types.PingRequest(params=types.RequestParams(_meta=meta)), types.EmptyResult) + + @deprecated( + "Client-to-server progress is deprecated as of 2026-07-28; progress is server-to-client only.", + category=MCPDeprecationWarning, + ) + async def send_progress_notification( + self, + progress_token: str | int, + progress: float, + total: float | None = None, + message: str | None = None, + *, + meta: RequestParamsMeta | None = None, + ) -> None: + """Send a progress notification.""" + await self.send_notification( + types.ProgressNotification( + params=types.ProgressNotificationParams( + progress_token=progress_token, + progress=progress, + total=total, + message=message, + _meta=meta, + ), + ) + ) + + @deprecated("The logging capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def set_logging_level( + self, + level: types.LoggingLevel, + *, + meta: RequestParamsMeta | None = None, + ) -> types.EmptyResult: + """Send a logging/setLevel request.""" + return await self.send_request( + types.SetLevelRequest(params=types.SetLevelRequestParams(level=level, _meta=meta)), + types.EmptyResult, + ) + + async def list_resources(self, *, params: types.PaginatedRequestParams | None = None) -> types.ListResourcesResult: + """Send a resources/list request. + + Args: + params: Full pagination parameters including cursor and any future fields + """ + return await self.send_request(types.ListResourcesRequest(params=params), types.ListResourcesResult) + + async def list_resource_templates( + self, *, params: types.PaginatedRequestParams | None = None + ) -> types.ListResourceTemplatesResult: + """Send a resources/templates/list request. + + Args: + params: Full pagination parameters including cursor and any future fields + """ + return await self.send_request( + types.ListResourceTemplatesRequest(params=params), + types.ListResourceTemplatesResult, + ) + + @overload + async def read_resource( + self, + uri: str, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + allow_input_required: Literal[False] = False, + ) -> types.ReadResourceResult: ... + + @overload + async def read_resource( + self, + uri: str, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + allow_input_required: bool, + ) -> types.ReadResourceResult | types.InputRequiredResult: ... + + async def read_resource( + self, + uri: str, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + allow_input_required: bool = False, + ) -> types.ReadResourceResult | types.InputRequiredResult: + """Send a resources/read request. + + Args: + input_responses: Responses to a prior `InputRequiredResult.input_requests`. + request_state: Opaque state echoed from a prior `InputRequiredResult`. + allow_input_required: When `False` (default), an `InputRequiredResult` + from the server raises `RuntimeError`; when `True`, it is returned + so the caller can resolve the requests and retry. + + Raises: + RuntimeError: If the server returns an `InputRequiredResult` and + `allow_input_required` is `False`. + """ + result = await self.send_request( + types.ReadResourceRequest( + params=types.ReadResourceRequestParams( + uri=uri, + input_responses=input_responses, + request_state=request_state, + _meta=meta, + ), + ), + _ReadResourceResultAdapter, + ) + if isinstance(result, types.InputRequiredResult) and not allow_input_required: + raise _input_required_unexpected("read_resource") + return result + + @deprecated( + "resources/subscribe is removed as of 2026-07-28; use Client.listen() instead.", + category=MCPDeprecationWarning, + ) + async def subscribe_resource(self, uri: str, *, meta: RequestParamsMeta | None = None) -> types.EmptyResult: + """Send a resources/subscribe request (2025-era servers only).""" + return await self.send_request( + types.SubscribeRequest(params=types.SubscribeRequestParams(uri=uri, _meta=meta)), + types.EmptyResult, + ) + + @deprecated( + "resources/unsubscribe is removed as of 2026-07-28; use Client.listen() instead.", + category=MCPDeprecationWarning, + ) + async def unsubscribe_resource(self, uri: str, *, meta: RequestParamsMeta | None = None) -> types.EmptyResult: + """Send a resources/unsubscribe request (2025-era servers only).""" + return await self.send_request( + types.UnsubscribeRequest(params=types.UnsubscribeRequestParams(uri=uri, _meta=meta)), + types.EmptyResult, + ) + + @overload + async def call_tool( + self, + name: str, + arguments: dict[str, Any] | None = None, + read_timeout_seconds: float | None = None, + progress_callback: ProgressFnT | None = None, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + allow_input_required: Literal[False] = False, + allow_claimed: Literal[False] = False, + ) -> types.CallToolResult: ... + + @overload + async def call_tool( + self, + name: str, + arguments: dict[str, Any] | None = None, + read_timeout_seconds: float | None = None, + progress_callback: ProgressFnT | None = None, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + allow_input_required: bool, + allow_claimed: Literal[False] = False, + ) -> types.CallToolResult | types.InputRequiredResult: ... + + @overload + async def call_tool( + self, + name: str, + arguments: dict[str, Any] | None = None, + read_timeout_seconds: float | None = None, + progress_callback: ProgressFnT | None = None, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + allow_input_required: Literal[False] = False, + allow_claimed: bool, + ) -> types.CallToolResult | types.Result: ... + + @overload + async def call_tool( + self, + name: str, + arguments: dict[str, Any] | None = None, + read_timeout_seconds: float | None = None, + progress_callback: ProgressFnT | None = None, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + allow_input_required: bool, + allow_claimed: bool, + ) -> types.CallToolResult | types.InputRequiredResult | types.Result: ... + + async def call_tool( + self, + name: str, + arguments: dict[str, Any] | None = None, + read_timeout_seconds: float | None = None, + progress_callback: ProgressFnT | None = None, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + allow_input_required: bool = False, + allow_claimed: bool = False, + ) -> types.CallToolResult | types.InputRequiredResult | types.Result: + """Send a tools/call request with optional progress callback support. + + On a modern (2026-07-28) connection, arguments annotated with `x-mcp-header` + in the tool's input schema are mirrored into `Mcp-Param-*` request headers. + The annotations are read from the tool's last `list_tools` entry, so list + the tool before calling it to enable header emission. + + Args: + input_responses: Responses to a prior `InputRequiredResult.input_requests`. + request_state: Opaque state echoed from a prior `InputRequiredResult`. + allow_input_required: When ``False`` (default), an `InputRequiredResult` + from the server raises `RuntimeError`; when ``True``, it is returned + so the caller can resolve the requests and retry. + allow_claimed: When `False` (default), a claimed extension result raises + `UnexpectedClaimedResult`; when `True`, the parsed claim model is returned. + + Raises: + RuntimeError: If the server returns an `InputRequiredResult` and + ``allow_input_required`` is ``False``. + UnexpectedClaimedResult: Claimed result with `allow_claimed` False; carries the parsed value. + """ + result = await self.send_request( + types.CallToolRequest( + params=types.CallToolRequestParams( + name=name, + arguments=arguments, + input_responses=input_responses, + request_state=request_state, + _meta=meta, + ), + ), + self._call_tool_adapter, + request_read_timeout_seconds=read_timeout_seconds, + progress_callback=progress_callback, + ) + + if isinstance(result, types.CallToolResult) and not result.is_error: + await self.validate_tool_result(name, result) + + # The input_required arm stays first; a claimed shape is terminal for the multi-round-trip driver. + if isinstance(result, types.InputRequiredResult) and not allow_input_required: + raise _input_required_unexpected("call_tool") + if not isinstance(result, types.CallToolResult | types.InputRequiredResult) and not allow_claimed: + raise UnexpectedClaimedResult(result) + return result + + def _resolve_param_headers(self, name: str, arguments: Mapping[str, Any]) -> dict[str, str]: + """`Mcp-Param-*` headers for a `tools/call`, or empty when the tool was never listed.""" + header_map = self._x_mcp_header_maps.get(name) + if header_map is None: + return {} + return mcp_param_headers(header_map, arguments) + + async def validate_tool_result(self, name: str, result: types.CallToolResult) -> None: + """Revalidate a `CallToolResult` against the tool's declared output schema. + + Raises: + RuntimeError: Structured content is missing or does not conform to the schema. + """ + if name not in self._tool_output_schemas: + # refresh output schema cache + await self.list_tools() + + output_schema = None + if name in self._tool_output_schemas: + output_schema = self._tool_output_schemas.get(name) + else: + logger.warning(f"Tool {name} not listed by server, cannot validate any structured content") + + if output_schema is not None: + from jsonschema import exceptions as jsonschema_exceptions + + if result.structured_content is None: + raise RuntimeError(f"Tool {name} has an output schema but did not return structured content") + validator = self._output_schema_validator(name, output_schema) + # `best_match` picks the same error the previous `jsonschema.validate()` call raised, + # so the message a caller sees is unchanged. It is untyped upstream. + errors = validator.iter_errors(result.structured_content) + error = cast( + "Exception | None", + jsonschema_exceptions.best_match(errors), # pyright: ignore[reportUnknownMemberType] + ) + if error is not None: + raise RuntimeError(f"Invalid structured content returned by tool {name}: {error}") from error + + def _output_schema_validator(self, name: str, output_schema: dict[str, Any]) -> Validator: + """Compiled validator for the tool's cached output schema, built once per schema value. + + Compiling is ~60x the cost of validating, so a one-shot `jsonschema.validate()` per + result dominates `call_tool`; the compiled validator is cached instead. It stays valid + because `_absorb_tool_listing` evicts a tool's validator whenever it absorbs a different + schema for that tool, so a cached entry always matches `output_schema`. + + Raises: + RuntimeError: The schema is not a valid JSON Schema. Raised on every call, since a + failed compile is never cached. + """ + from jsonschema import SchemaError + from jsonschema.validators import validator_for + + if (validator := self._tool_output_validators.get(name)) is not None: + return validator + + validator_cls = validator_for(output_schema) + try: + validator_cls.check_schema(output_schema) + except SchemaError as e: + raise RuntimeError(f"Invalid schema for tool {name}: {e}") + # jsonschema ships no `py.typed`, so pyright reads typeshed's stub, which declares + # `registry` as required (concrete validators default it); cast to a schema-only ctor. + validator = cast("Callable[[dict[str, Any]], Validator]", validator_cls)(output_schema) + self._tool_output_validators[name] = validator + return validator + + async def list_prompts(self, *, params: types.PaginatedRequestParams | None = None) -> types.ListPromptsResult: + """Send a prompts/list request. + + Args: + params: Full pagination parameters including cursor and any future fields + """ + return await self.send_request(types.ListPromptsRequest(params=params), types.ListPromptsResult) + + @overload + async def get_prompt( + self, + name: str, + arguments: dict[str, str] | None = None, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + allow_input_required: Literal[False] = False, + ) -> types.GetPromptResult: ... + + @overload + async def get_prompt( + self, + name: str, + arguments: dict[str, str] | None = None, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + allow_input_required: bool, + ) -> types.GetPromptResult | types.InputRequiredResult: ... + + async def get_prompt( + self, + name: str, + arguments: dict[str, str] | None = None, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: RequestParamsMeta | None = None, + allow_input_required: bool = False, + ) -> types.GetPromptResult | types.InputRequiredResult: + """Send a prompts/get request. + + Args: + input_responses: Responses to a prior `InputRequiredResult.input_requests`. + request_state: Opaque state echoed from a prior `InputRequiredResult`. + allow_input_required: When `False` (default), an `InputRequiredResult` + from the server raises `RuntimeError`; when `True`, it is returned + so the caller can resolve the requests and retry. + + Raises: + RuntimeError: If the server returns an `InputRequiredResult` and + `allow_input_required` is `False`. + """ + result = await self.send_request( + types.GetPromptRequest( + params=types.GetPromptRequestParams( + name=name, + arguments=arguments, + input_responses=input_responses, + request_state=request_state, + _meta=meta, + ), + ), + _GetPromptResultAdapter, + ) + if isinstance(result, types.InputRequiredResult) and not allow_input_required: + raise _input_required_unexpected("get_prompt") + return result + + async def complete( + self, + ref: types.ResourceTemplateReference | types.PromptReference, + argument: dict[str, str], + context_arguments: dict[str, str] | None = None, + ) -> types.CompleteResult: + """Send a completion/complete request.""" + context = None + if context_arguments is not None: + context = types.CompletionContext(arguments=context_arguments) + + return await self.send_request( + types.CompleteRequest( + params=types.CompleteRequestParams( + ref=ref, + argument=types.CompletionArgument(**argument), + context=context, + ), + ), + types.CompleteResult, + ) + + async def list_tools(self, *, params: types.PaginatedRequestParams | None = None) -> types.ListToolsResult: + """Send a tools/list request. + + Args: + params: Full pagination parameters including cursor and any future fields + """ + result = await self.send_request( + types.ListToolsRequest(params=params), + types.ListToolsResult, + ) + complete = (params is None or params.cursor is None) and result.next_cursor is None + return self._absorb_tool_listing(result, complete=complete) + + def _absorb_tool_listing(self, result: types.ListToolsResult, *, complete: bool) -> types.ListToolsResult: + """Filter the listing per the 2026 x-mcp-header MUST and rebuild derived per-tool state, in place. + + Idempotent: cached values are already post-filter, so the response cache can re-absorb a served listing. + `complete` (an uncursored single-page listing) prunes per-tool state down to the listing's tools. + """ + if self._negotiated_version in MODERN_PROTOCOL_VERSIONS: + # 2026-07-28: clients MUST drop tools whose x-mcp-header annotations are invalid. + kept: list[types.Tool] = [] + for tool in result.tools: + if (reason := find_invalid_x_mcp_header(tool.input_schema)) is not None: + logger.warning("dropping tool %r: invalid x-mcp-header (%s)", tool.name, reason) + # Evict any map cached from a prior valid listing so a stale entry can't + # mirror headers for a tool this listing dropped. + self._x_mcp_header_maps.pop(tool.name, None) + continue + # Cache the arg→header map so a later tools/call mirrors it into Mcp-Param-* headers. + self._x_mcp_header_maps[tool.name] = x_mcp_header_map(tool.input_schema) + kept.append(tool) + result.tools = kept + + # Cache tool output schemas for future validation; cursor pages only ever add. A + # changed schema evicts its compiled validator; an unchanged one (a re-listing, or the + # response cache re-absorbing a served hit) keeps it. Only validated tools pay the check. + for tool in result.tools: + if tool.name in self._tool_output_validators and not _same_schema( + self._tool_output_schemas.get(tool.name), tool.output_schema + ): + del self._tool_output_validators[tool.name] + self._tool_output_schemas[tool.name] = tool.output_schema + + if complete: + # The listing is the full tool universe, so state for unlisted tools is stale + # (the server dropped them, or a shared-cache writer's filter did). + names = {tool.name for tool in result.tools} + self._x_mcp_header_maps = {k: v for k, v in self._x_mcp_header_maps.items() if k in names} + self._tool_output_schemas = {k: v for k, v in self._tool_output_schemas.items() if k in names} + self._tool_output_validators = {k: v for k, v in self._tool_output_validators.items() if k in names} + + return result + + @deprecated("The roots capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def send_roots_list_changed(self) -> None: + """Send a roots/list_changed notification.""" + await self.send_notification(types.RootsListChangedNotification()) + + async def _on_request( + self, dctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None + ) -> dict[str, Any]: + """Answer a server-initiated request via the registered callbacks.""" + # Literal, not LATEST_PROTOCOL_VERSION: the fallback covers the initialize + # handshake (which only exists at <=2025) and stateless until the header + # is plumbed; its meaning is fixed regardless of LATEST bumps. + version = self._negotiated_version or "2025-11-25" + try: + request = cast(types.ServerRequest, _methods.parse_server_request(method, version, params)) + except KeyError: + raise MCPError(code=METHOD_NOT_FOUND, message="Method not found", data=method) from None + + response: types.ClientResult | types.ErrorData + if isinstance(request, types.PingRequest): + # Answered without a context: ping has no callback that would need one. + response = types.EmptyResult() + else: + assert dctx.request_id is not None # the callback-driving dispatchers always assign ids + ctx = ClientRequestContext( + session=self, request_id=dctx.request_id, meta=request.params.meta if request.params else None + ) + response = await self.dispatch_input_request(ctx, request) + client_response = ClientResponse.validate_python(response) + if isinstance(client_response, types.ErrorData): + raise MCPError.from_error_data(client_response) + dumped = client_response.model_dump(by_alias=True, mode="json", exclude_none=True) + try: + _methods.validate_client_result(method, version, dumped) + except ValidationError: + logger.exception("client callback for %r returned an invalid result", method) + raise MCPError(code=INTERNAL_ERROR, message="Client callback returned an invalid result") from None + return dumped + + async def dispatch_input_request( + self, ctx: ClientRequestContext, request: types.InputRequest + ) -> types.InputResponse | types.ErrorData: + """Route an input request through the client's callback table. + + Shared by the legacy server→client RPC path (`_on_request`) and the + 2026-07-28 multi-round-trip driver, which dispatches the embedded + `InputRequiredResult.input_requests` through the same callbacks. + + Returns the callback's `InputResponse`, or `ErrorData` when the callback declines. + """ + match request: + case types.CreateMessageRequest(params=p): + return await self._sampling_callback(ctx, p) + case types.ElicitRequest(params=p): + return await self._elicitation_callback(ctx, p) + case types.ListRootsRequest(): # pragma: no branch + return await self._list_roots_callback(ctx) + + def _register_listen_route(self, request_id: RequestId) -> ListenRoute: + """Create the demux route for a listen request id; the caller registers BEFORE sending.""" + route = ListenRoute() + self._listen_routes[request_id] = route + return route + + def _unregister_listen_route(self, request_id: RequestId) -> None: + """Drop a listen route; the handle owns membership, so a missing key is a no-op.""" + self._listen_routes.pop(request_id, None) + + def _settle_listen_routes_closed(self) -> None: + """Settle all open listen routes as lost on session exit; cancelled driver tasks cannot.""" + closed = MCPError(code=CONNECTION_CLOSED, message="Connection closed") + for route in self._listen_routes.values(): + route.settle("lost", error=closed) + self._listen_routes.clear() + + def _intercept_notification(self, method: str, params: Mapping[str, Any] | None) -> bool: + """Wire-order listen demux, run synchronously on the dispatcher's receive path. + + Bookkeeping must advance in receive order with the listen result (resolved on + this same path); the spawned `_on_notify` path would race it and drop events. + Returns True to consume the frame: a live route's ack is driver state, never surfaced. + """ + if not self._listen_routes: + return False + if method == "notifications/cancelled": + request_id = cancelled_request_id_from_params(params) + if request_id is not None and (listen_route := self._listen_routes.get(request_id)) is not None: + # a server-sent cancel naming a listen request is that stream's teardown signal + listen_route.settle("lost") + return False # _on_notify swallows every cancelled either way (v1 parity) + if params is None: + return False + meta = params.get("_meta") + if not isinstance(meta, Mapping): + return False + # as_request_id is not a tripwire: raw wire _meta can carry a non-id (even unhashable) value + subscription_id = as_request_id(cast("Mapping[str, Any]", meta).get(SUBSCRIPTION_ID_META_KEY)) + if subscription_id is None or (listen_route := self._listen_routes.get(subscription_id)) is None: + return False + if method == "notifications/subscriptions/acknowledged": + raw_filter = params.get("notifications") + if raw_filter is None: + # malformed, not an empty filter: leave it to the spawned path's validation warning + return False + try: + honored = types.SubscriptionFilter.model_validate(raw_filter) + except ValidationError: + return False + listen_route.set_acked(honored) + return True + if (event := event_from_wire(method, params)) is not None: + listen_route.deliver(event) + return False # events (and any other stamped frame) still tee as usual + + async def _on_notify( + self, dctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None + ) -> None: + """Route a server notification: validate, run the typed callback, tee to message_handler.""" + # Same fallback as `_on_request`: covers pre-handshake and stateless. + version = self._negotiated_version or "2025-11-25" + try: + notification = cast(types.ServerNotification, _methods.parse_server_notification(method, version, params)) + except KeyError: + # Only methods unknown to the negotiated version's core tables reach the bindings. + binding = self._notification_bindings.get(method) + if binding is None: + logger.debug("dropped %r: not defined at %s", method, version) + return + try: + bound_params = binding.params_type.model_validate(params or {}) + except ValidationError: + logger.warning("Failed to validate notification: %s", method, exc_info=True) + return + send, receive = self._binding_queues[method] + try: + # Must not await: DirectDispatcher calls _on_notify inline; blocking deadlocks in-process servers. + send.send_nowait(bound_params) + except anyio.WouldBlock: + # Evict the oldest event; no checkpoint since the failed send, + # so the buffer is still full and the retry cannot block. + receive.receive_nowait() + logger.warning("notification queue for %r is full; dropped the oldest event", method) + send.send_nowait(bound_params) + return + except ValidationError: + logger.warning("Failed to validate notification: %s", method, exc_info=True) + return + if isinstance(notification, types.CancelledNotification): + # Never surfaced (v1 parity): the dispatcher already applied it; listen cancels settled by the intercept. + return + try: + if isinstance(notification, types.LoggingMessageNotification): + await self._logging_callback(notification.params) + await self._message_handler(notification) + except Exception: + # Contain here, not in the dispatcher: DirectDispatcher awaits this + # handler inline in the peer's notify() call, so a raising callback + # would otherwise fail the peer's send. A raising logging_callback + # skips the message_handler tee for that notification (v1 parity). + logger.exception("notification callback for %r raised", method) + + async def _on_stream_exception(self, exc: Exception) -> None: + """Deliver a transport-level fault to message_handler via a spawned task. + + Running the handler inline would park the dispatcher's read loop and + deadlock handlers that await session I/O. + """ + assert self._task_group is not None + self._task_group.start_soon(self._deliver_stream_exception, exc) + + async def _deliver_stream_exception(self, exc: Exception) -> None: + try: + await self._message_handler(exc) + except Exception: + logger.exception("message_handler raised on transport exception") diff --git a/venv/lib/python3.12/site-packages/mcp/client/session_group.py b/venv/lib/python3.12/site-packages/mcp/client/session_group.py new file mode 100644 index 0000000..a544cec --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/session_group.py @@ -0,0 +1,449 @@ +"""SessionGroup concurrently manages multiple MCP session connections. + +Tools, resources, and prompts are aggregated across servers. Servers may +be connected to or disconnected from at any point after initialization. + +This abstraction can handle naming collisions using a custom user-provided hook. +""" + +import contextlib +import logging +from collections.abc import Callable +from dataclasses import dataclass +from types import TracebackType +from typing import Any, Literal, TypeAlias, overload + +import anyio +import httpx2 +import mcp_types as types +from pydantic import BaseModel, Field +from typing_extensions import Self + +import mcp +from mcp.client.session import ElicitationFnT, ListRootsFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT +from mcp.client.sse import sse_client +from mcp.client.stdio import StdioServerParameters +from mcp.client.streamable_http import streamable_http_client +from mcp.shared._httpx_utils import create_mcp_http_client +from mcp.shared.dispatcher import ProgressFnT +from mcp.shared.exceptions import MCPError + + +class SseServerParameters(BaseModel): + """Parameters for initializing an sse_client.""" + + # The endpoint URL. + url: str + + # Optional headers to include in requests. + headers: dict[str, Any] | None = None + + # HTTP timeout for regular operations (in seconds). + timeout: float = 5.0 + + # Timeout for SSE read operations (in seconds). + sse_read_timeout: float = 300.0 + + +class StreamableHttpParameters(BaseModel): + """Parameters for initializing a streamable_http_client.""" + + # The endpoint URL. + url: str + + # Optional headers to include in requests. + headers: dict[str, Any] | None = None + + # HTTP timeout for regular operations (in seconds). + timeout: float = 30.0 + + # Timeout for SSE read operations (in seconds). + sse_read_timeout: float = 300.0 + + # Close the client session when the transport closes. + terminate_on_close: bool = True + + +ServerParameters: TypeAlias = StdioServerParameters | SseServerParameters | StreamableHttpParameters + + +# Use dataclass instead of Pydantic BaseModel +# because Pydantic BaseModel cannot handle Protocol fields. +@dataclass +class ClientSessionParameters: + """Parameters for establishing a client session to an MCP server.""" + + read_timeout_seconds: float | None = None + sampling_callback: SamplingFnT | None = None + elicitation_callback: ElicitationFnT | None = None + list_roots_callback: ListRootsFnT | None = None + logging_callback: LoggingFnT | None = None + message_handler: MessageHandlerFnT | None = None + client_info: types.Implementation | None = None + + +class ClientSessionGroup: + """Client for managing connections to multiple MCP servers. + + This class is responsible for encapsulating management of server connections. + It aggregates tools, resources, and prompts from all connected servers. + + For auxiliary handlers, such as resource subscription, this is delegated to + the client and can be accessed via the session. + + Example: + ```python + name_fn = lambda name, server_info: f"{(server_info.name)}_{name}" + async with ClientSessionGroup(component_name_hook=name_fn) as group: + for server_param in server_params: + await group.connect_to_server(server_param) + ... + ``` + """ + + class _ComponentNames(BaseModel): + """Used for reverse index to find components.""" + + prompts: set[str] = Field(default_factory=set) + resources: set[str] = Field(default_factory=set) + tools: set[str] = Field(default_factory=set) + + # Standard MCP components. + _prompts: dict[str, types.Prompt] + _resources: dict[str, types.Resource] + _tools: dict[str, types.Tool] + + # Client-server connection management. + _sessions: dict[mcp.ClientSession, _ComponentNames] + _tool_to_session: dict[str, mcp.ClientSession] + _exit_stack: contextlib.AsyncExitStack + _session_exit_stacks: dict[mcp.ClientSession, contextlib.AsyncExitStack] + + # Optional fn consuming (component_name, server_info) for custom names. + # This is to provide a means to mitigate naming conflicts across servers. + # Example: (tool_name, server_info) => "{result.server_info.name}.{tool_name}" + _ComponentNameHook: TypeAlias = Callable[[str, types.Implementation], str] + _component_name_hook: _ComponentNameHook | None + + def __init__( + self, + exit_stack: contextlib.AsyncExitStack | None = None, + component_name_hook: _ComponentNameHook | None = None, + ) -> None: + """Initializes the MCP client.""" + + self._tools = {} + self._resources = {} + self._prompts = {} + + self._sessions = {} + self._tool_to_session = {} + if exit_stack is None: + self._exit_stack = contextlib.AsyncExitStack() + self._owns_exit_stack = True + else: + self._exit_stack = exit_stack + self._owns_exit_stack = False + self._session_exit_stacks = {} + self._component_name_hook = component_name_hook + + async def __aenter__(self) -> Self: # pragma: no cover + # Enter the exit stack only if we created it ourselves + if self._owns_exit_stack: + await self._exit_stack.__aenter__() + return self + + async def __aexit__( + self, + _exc_type: type[BaseException] | None, + _exc_val: BaseException | None, + _exc_tb: TracebackType | None, + ) -> bool | None: # pragma: no cover + """Closes session exit stacks and main exit stack upon completion.""" + + # Only close the main exit stack if we created it + if self._owns_exit_stack: + await self._exit_stack.aclose() + + # Concurrently close session stacks. + async with anyio.create_task_group() as tg: + for exit_stack in self._session_exit_stacks.values(): + tg.start_soon(exit_stack.aclose) + + @property + def sessions(self) -> list[mcp.ClientSession]: + """Returns the list of sessions being managed.""" + return list(self._sessions.keys()) # pragma: no cover + + @property + def prompts(self) -> dict[str, types.Prompt]: + """Returns the prompts as a dictionary of names to prompts.""" + return self._prompts + + @property + def resources(self) -> dict[str, types.Resource]: + """Returns the resources as a dictionary of names to resources.""" + return self._resources + + @property + def tools(self) -> dict[str, types.Tool]: + """Returns the tools as a dictionary of names to tools.""" + return self._tools + + @overload + async def call_tool( + self, + name: str, + arguments: dict[str, Any] | None = None, + read_timeout_seconds: float | None = None, + progress_callback: ProgressFnT | None = None, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: types.RequestParamsMeta | None = None, + allow_input_required: Literal[False] = False, + ) -> types.CallToolResult: ... + + @overload + async def call_tool( + self, + name: str, + arguments: dict[str, Any] | None = None, + read_timeout_seconds: float | None = None, + progress_callback: ProgressFnT | None = None, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: types.RequestParamsMeta | None = None, + allow_input_required: bool, + ) -> types.CallToolResult | types.InputRequiredResult: ... + + async def call_tool( + self, + name: str, + arguments: dict[str, Any] | None = None, + read_timeout_seconds: float | None = None, + progress_callback: ProgressFnT | None = None, + *, + input_responses: types.InputResponses | None = None, + request_state: str | None = None, + meta: types.RequestParamsMeta | None = None, + allow_input_required: bool = False, + ) -> types.CallToolResult | types.InputRequiredResult: + """Executes a tool given its name and arguments. + + Raises: + RuntimeError: If the server returns an `InputRequiredResult` and + ``allow_input_required`` is ``False``. + """ + session = self._tool_to_session[name] + session_tool_name = self.tools[name].name + return await session.call_tool( + session_tool_name, + arguments=arguments, + read_timeout_seconds=read_timeout_seconds, + progress_callback=progress_callback, + input_responses=input_responses, + request_state=request_state, + meta=meta, + allow_input_required=allow_input_required, + ) + + async def disconnect_from_server(self, session: mcp.ClientSession) -> None: + """Disconnects from a single MCP server.""" + + session_known_for_components = session in self._sessions + session_known_for_stack = session in self._session_exit_stacks + + if not session_known_for_components and not session_known_for_stack: + raise MCPError( + code=types.INVALID_PARAMS, + message="Provided session is not managed or already disconnected.", + ) + + if session_known_for_components: # pragma: no branch + component_names = self._sessions.pop(session) # Pop from _sessions tracking + + # Remove prompts associated with the session. + for name in component_names.prompts: + if name in self._prompts: # pragma: no branch + del self._prompts[name] + # Remove resources associated with the session. + for name in component_names.resources: + if name in self._resources: # pragma: no branch + del self._resources[name] + # Remove tools associated with the session. + for name in component_names.tools: + if name in self._tools: # pragma: no branch + del self._tools[name] + if name in self._tool_to_session: # pragma: no branch + del self._tool_to_session[name] + + # Clean up the session's resources via its dedicated exit stack + if session_known_for_stack: + session_stack_to_close = self._session_exit_stacks.pop(session) # pragma: no cover + await session_stack_to_close.aclose() # pragma: no cover + + async def connect_with_session( + self, server_info: types.Implementation, session: mcp.ClientSession + ) -> mcp.ClientSession: + """Connects to a single MCP server.""" + await self._aggregate_components(server_info, session) + return session + + async def connect_to_server( + self, + server_params: ServerParameters, + session_params: ClientSessionParameters | None = None, + ) -> mcp.ClientSession: + """Connects to a single MCP server.""" + server_info, session = await self._establish_session(server_params, session_params or ClientSessionParameters()) + return await self.connect_with_session(server_info, session) + + async def _establish_session( + self, + server_params: ServerParameters, + session_params: ClientSessionParameters, + ) -> tuple[types.Implementation, mcp.ClientSession]: + """Establish a client session to an MCP server.""" + + session_stack = contextlib.AsyncExitStack() + try: + # Create read and write streams that facilitate io with the server. + if isinstance(server_params, StdioServerParameters): + client = mcp.stdio_client(server_params) + read, write = await session_stack.enter_async_context(client) + elif isinstance(server_params, SseServerParameters): + client = sse_client( + url=server_params.url, + headers=server_params.headers, + timeout=server_params.timeout, + sse_read_timeout=server_params.sse_read_timeout, + ) + read, write = await session_stack.enter_async_context(client) + else: + httpx_client = create_mcp_http_client( + headers=server_params.headers, + timeout=httpx2.Timeout( + server_params.timeout, + read=server_params.sse_read_timeout, + ), + ) + await session_stack.enter_async_context(httpx_client) + + client = streamable_http_client( + url=server_params.url, + http_client=httpx_client, + terminate_on_close=server_params.terminate_on_close, + ) + read, write = await session_stack.enter_async_context(client) + + session = await session_stack.enter_async_context( + mcp.ClientSession( + read, + write, + read_timeout_seconds=session_params.read_timeout_seconds, + sampling_callback=session_params.sampling_callback, + elicitation_callback=session_params.elicitation_callback, + list_roots_callback=session_params.list_roots_callback, + logging_callback=session_params.logging_callback, + message_handler=session_params.message_handler, + client_info=session_params.client_info, + ) + ) + + result = await session.initialize() + + # Session successfully initialized. + # Store its stack and register the stack with the main group stack. + self._session_exit_stacks[session] = session_stack + # session_stack itself becomes a resource managed by the + # main _exit_stack. + await self._exit_stack.enter_async_context(session_stack) + + return result.server_info, session + except Exception: # pragma: no cover + # If anything during this setup fails, ensure the session-specific + # stack is closed. + await session_stack.aclose() + raise + + async def _aggregate_components(self, server_info: types.Implementation, session: mcp.ClientSession) -> None: + """Aggregates prompts, resources, and tools from a given session.""" + + # Create a reverse index so we can find all prompts, resources, and + # tools belonging to this session. Used for removing components from + # the session group via self.disconnect_from_server. + component_names = self._ComponentNames() + + # Temporary components dicts. We do not want to modify the aggregate + # lists in case of an intermediate failure. + prompts_temp: dict[str, types.Prompt] = {} + resources_temp: dict[str, types.Resource] = {} + tools_temp: dict[str, types.Tool] = {} + tool_to_session_temp: dict[str, mcp.ClientSession] = {} + + # Query the server for its prompts and aggregate to list. + try: + prompts = (await session.list_prompts()).prompts + for prompt in prompts: + name = self._component_name(prompt.name, server_info) + prompts_temp[name] = prompt + component_names.prompts.add(name) + except MCPError as err: # pragma: no cover + logging.warning(f"Could not fetch prompts: {err}") + + # Query the server for its resources and aggregate to list. + try: + resources = (await session.list_resources()).resources + for resource in resources: + name = self._component_name(resource.name, server_info) + resources_temp[name] = resource + component_names.resources.add(name) + except MCPError as err: # pragma: no cover + logging.warning(f"Could not fetch resources: {err}") + + # Query the server for its tools and aggregate to list. + try: + tools = (await session.list_tools()).tools + for tool in tools: + name = self._component_name(tool.name, server_info) + tools_temp[name] = tool + tool_to_session_temp[name] = session + component_names.tools.add(name) + except MCPError as err: # pragma: no cover + logging.warning(f"Could not fetch tools: {err}") + + # Clean up exit stack for session if we couldn't retrieve anything + # from the server. + if not any((prompts_temp, resources_temp, tools_temp)): + del self._session_exit_stacks[session] # pragma: no cover + + # Check for duplicates. + matching_prompts = prompts_temp.keys() & self._prompts.keys() + if matching_prompts: + raise MCPError( # pragma: no cover + code=types.INVALID_PARAMS, + message=f"{matching_prompts} already exist in group prompts.", + ) + matching_resources = resources_temp.keys() & self._resources.keys() + if matching_resources: + raise MCPError( # pragma: no cover + code=types.INVALID_PARAMS, + message=f"{matching_resources} already exist in group resources.", + ) + matching_tools = tools_temp.keys() & self._tools.keys() + if matching_tools: + raise MCPError(code=types.INVALID_PARAMS, message=f"{matching_tools} already exist in group tools.") + + # Aggregate components. + self._sessions[session] = component_names + self._prompts.update(prompts_temp) + self._resources.update(resources_temp) + self._tools.update(tools_temp) + self._tool_to_session.update(tool_to_session_temp) + + def _component_name(self, name: str, server_info: types.Implementation) -> str: + if self._component_name_hook: + return self._component_name_hook(name, server_info) + return name diff --git a/venv/lib/python3.12/site-packages/mcp/client/sse.py b/venv/lib/python3.12/site-packages/mcp/client/sse.py new file mode 100644 index 0000000..31d0f35 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/sse.py @@ -0,0 +1,161 @@ +import logging +from collections.abc import Callable +from contextlib import asynccontextmanager +from typing import Any +from urllib.parse import parse_qs, urljoin, urlparse + +import anyio +import httpx2 +import mcp_types as types +from anyio.abc import TaskStatus +from httpx2 import SSEError + +from mcp.shared._compat import resync_tracer +from mcp.shared._context_streams import create_context_streams +from mcp.shared._httpx_utils import McpHttpClientFactory, create_mcp_http_client +from mcp.shared.message import SessionMessage + +logger = logging.getLogger(__name__) + + +def remove_request_params(url: str) -> str: + return urljoin(url, urlparse(url).path) + + +def _extract_session_id_from_endpoint(endpoint_url: str) -> str | None: + query_params = parse_qs(urlparse(endpoint_url).query) + return query_params.get("sessionId", [None])[0] or query_params.get("session_id", [None])[0] + + +@asynccontextmanager +async def sse_client( + url: str, + headers: dict[str, Any] | None = None, + timeout: float = 5.0, + sse_read_timeout: float = 300.0, + httpx_client_factory: McpHttpClientFactory = create_mcp_http_client, + auth: httpx2.Auth | None = None, + on_session_created: Callable[[str], None] | None = None, +): + """Client transport for SSE. + + `sse_read_timeout` determines how long (in seconds) the client will wait for a new + event before disconnecting. All other HTTP operations are controlled by `timeout`. + + Args: + url: The SSE endpoint URL. + headers: Optional headers to include in requests. + timeout: HTTP timeout for regular operations (in seconds). + sse_read_timeout: Timeout for SSE read operations (in seconds). + httpx_client_factory: Factory function for creating the httpx2 client. + auth: Optional httpx2 authentication handler. + on_session_created: Optional callback invoked with the session ID when received. + """ + logger.debug(f"Connecting to SSE endpoint: {remove_request_params(url)}") + async with httpx_client_factory( + headers=headers, auth=auth, timeout=httpx2.Timeout(timeout, read=sse_read_timeout) + ) as client: + async with client.sse(url) as event_source: + event_source.response.raise_for_status() + logger.debug("SSE connection established") + + read_stream_writer, read_stream = create_context_streams[SessionMessage | Exception](0) + write_stream, write_stream_reader = create_context_streams[SessionMessage](0) + + async def sse_reader(task_status: TaskStatus[str] = anyio.TASK_STATUS_IGNORED): + try: + async for sse in event_source: # pragma: no branch + logger.debug(f"Received SSE event: {sse.event}") + match sse.event: + case "endpoint": + endpoint_url = urljoin(url, sse.data) + logger.debug(f"Received endpoint URL: {endpoint_url}") + + url_parsed = urlparse(url) + endpoint_parsed = urlparse(endpoint_url) + if ( # pragma: no cover + url_parsed.netloc != endpoint_parsed.netloc + or url_parsed.scheme != endpoint_parsed.scheme + ): + error_msg = ( # pragma: no cover + f"Endpoint origin does not match connection origin: {endpoint_url}" + ) + logger.error(error_msg) # pragma: no cover + raise ValueError(error_msg) # pragma: no cover + + if on_session_created: + session_id = _extract_session_id_from_endpoint(endpoint_url) + if session_id: + on_session_created(session_id) + + task_status.started(endpoint_url) + + case "message": + # Skip empty data (keep-alive pings) + if not sse.data: + continue + try: + message = types.jsonrpc_message_adapter.validate_json(sse.data, by_name=False) + logger.debug(f"Received server message: {message}") + except Exception as exc: # pragma: no cover + logger.exception("Error parsing server message") # pragma: no cover + await read_stream_writer.send(exc) # pragma: no cover + continue # pragma: no cover + + session_message = SessionMessage(message) + await read_stream_writer.send(session_message) + case _: # pragma: no cover + logger.warning(f"Unknown SSE event: {sse.event}") # pragma: no cover + except SSEError as sse_exc: # pragma: lax no cover + logger.exception("Encountered SSE exception") + raise sse_exc + except Exception as exc: # pragma: lax no cover + logger.exception("Error in sse_reader") + await read_stream_writer.send(exc) + finally: + await read_stream_writer.aclose() + + async def post_writer(endpoint_url: str): + try: + async with write_stream_reader, write_stream: + + async def _send_message(session_message: SessionMessage) -> None: + logger.debug(f"Sending client message: {session_message}") + response = await client.post( + endpoint_url, + json=session_message.message.model_dump( + by_alias=True, + mode="json", + exclude_unset=True, + ), + ) + response.raise_for_status() + logger.debug(f"Client message sent successfully: {response.status_code}") + + async for session_message in write_stream_reader: + sender_ctx = write_stream_reader.last_context + if sender_ctx is not None: + async with anyio.create_task_group() as tg: + sender_ctx.run(tg.start_soon, _send_message, session_message) + else: + await _send_message(session_message) # pragma: no cover + except Exception: # pragma: lax no cover + logger.exception("Error in post_writer") + + # On Python 3.14, coverage.py reports a phantom branch arc on this + # line (->yield) when nested two async-with levels deep. The branch + # is the unreachable "did __aexit__ suppress?" arm for memory streams. + async with ( # pragma: no branch + read_stream_writer, + read_stream, + write_stream, + write_stream_reader, + anyio.create_task_group() as tg, + ): + endpoint_url = await tg.start(sse_reader) + logger.debug(f"Starting post writer with endpoint URL: {endpoint_url}") + tg.start_soon(post_writer, endpoint_url) + + yield read_stream, write_stream + tg.cancel_scope.cancel() + await resync_tracer() diff --git a/venv/lib/python3.12/site-packages/mcp/client/stdio.py b/venv/lib/python3.12/site-packages/mcp/client/stdio.py new file mode 100644 index 0000000..3e03eef --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/stdio.py @@ -0,0 +1,354 @@ +"""stdio client transport. + +Runs an MCP server as a subprocess and exchanges newline-delimited JSON-RPC +messages with it over stdin/stdout. Two pipe tasks bridge the server's pipes +to the session's in-memory streams; shutdown follows the MCP spec sequence +(close stdin, wait, then kill the process tree) inside a cancellation shield +with every wait bounded, so a cancelled caller can neither leak a live server +process nor hang on one. +""" + +import logging +import os +import sys +from collections.abc import AsyncGenerator +from contextlib import asynccontextmanager, suppress +from pathlib import Path +from typing import Literal, TextIO + +import anyio +import anyio.lowlevel +import mcp_types as types +from anyio.abc import AsyncResource, Process +from anyio.streams.text import TextReceiveStream +from pydantic import BaseModel, Field + +from mcp.client._transport import TransportStreams +from mcp.os.posix.utilities import terminate_posix_process_tree +from mcp.os.win32.utilities import ( + ServerProcess, + close_process_job, + create_windows_process, + get_windows_executable_command, + terminate_windows_process_tree, +) +from mcp.shared.message import SessionMessage + +logger = logging.getLogger(__name__) + +# Environment variables to inherit by default +DEFAULT_INHERITED_ENV_VARS = ( + [ + "APPDATA", + "HOMEDRIVE", + "HOMEPATH", + "LOCALAPPDATA", + "PATH", + "PATHEXT", + "PROCESSOR_ARCHITECTURE", + "SYSTEMDRIVE", + "SYSTEMROOT", + "TEMP", + "USERNAME", + "USERPROFILE", + ] + if sys.platform == "win32" + else ["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER"] +) + +# Grace period for the server to exit on its own after its stdin closes. +PROCESS_TERMINATION_TIMEOUT = 2.0 + +# Extra time after SIGTERM before SIGKILL; POSIX only (Windows kills hard). +FORCE_KILL_TIMEOUT = 2.0 + +# Time for the event loop to observe a kill; only an unkillable process runs this out. +_KILL_REAP_TIMEOUT = 2.0 + +# Time for the writer to flush accepted messages before stdin closes. +_WRITER_FLUSH_TIMEOUT = 0.5 + +# How often to poll returncode while waiting for the process to die. +_EXIT_POLL_INTERVAL = 0.01 + + +def get_default_environment() -> dict[str, str]: + """Returns only the environment variables that are safe to inherit.""" + env: dict[str, str] = {} + + for key in DEFAULT_INHERITED_ENV_VARS: + value = os.environ.get(key) + if value is None: # pragma: lax no cover + continue + + if value.startswith("()"): # pragma: no cover + # Skip functions, which are a security risk + continue # pragma: no cover + + env[key] = value + + return env + + +class StdioServerParameters(BaseModel): + command: str + """The executable to run to start the server.""" + + args: list[str] = Field(default_factory=list) + """Command line arguments to pass to the executable.""" + + env: dict[str, str] | None = None + """Extra environment variables, merged over get_default_environment().""" + + cwd: str | Path | None = None + """The working directory to use when spawning the process.""" + + encoding: str = "utf-8" + """Text encoding for messages to and from the server.""" + + encoding_error_handler: Literal["strict", "ignore", "replace"] = "strict" + """Encoding error handler; see https://docs.python.org/3/library/codecs.html#error-handlers.""" + + +@asynccontextmanager +async def stdio_client( + server: StdioServerParameters, errlog: TextIO = sys.stderr +) -> AsyncGenerator[TransportStreams, None]: + """Spawns an MCP server subprocess and connects to it over stdin/stdout. + + Raises: + OSError: If the server process cannot be spawned. + ValueError: If the spawn parameters are invalid (embedded NUL bytes). + """ + command = _get_executable_command(server.command) + + process = await _create_platform_compatible_process( + command=command, + args=server.args, + env=get_default_environment() | (server.env or {}), + errlog=errlog, + cwd=server.cwd, + ) + + # The spawn succeeded; no awaits until the task group is entered, or a + # cancellation delivered in the gap would leak the live process. + read_stream_writer, read_stream = anyio.create_memory_object_stream[SessionMessage | Exception](0) + write_stream, write_stream_reader = anyio.create_memory_object_stream[SessionMessage](0) + + shutting_down = False + writer_done = anyio.Event() + + async def stdout_reader() -> None: + assert process.stdout, "Opened process is missing stdout" + + stdout = TextReceiveStream(process.stdout, encoding=server.encoding, errors=server.encoding_error_handler) + try: + async with read_stream_writer: + try: + # One line at a time; no read-ahead while a delivery is blocked. + buffer = "" + async for chunk in stdout: + lines = (buffer + chunk).split("\n") + buffer = lines.pop() + for line in lines: + try: + await read_stream_writer.send(_parse_line(line)) + except (anyio.ClosedResourceError, anyio.BrokenResourceError): + return # the session is gone; only the drain below remains + finally: + await _drain_stdout(process) + except anyio.ClosedResourceError: + pass # our own shutdown closed the stdout stream under the read + except (anyio.BrokenResourceError, ConnectionError): + # Teardown noise during shutdown, a real failure otherwise; either way + # the session sees clean closure when the read stream closes. + if not shutting_down: + logger.exception("Reading from the MCP server's stdout failed mid-session") + + async def stdin_writer() -> None: + assert process.stdin, "Opened process is missing stdin" + + try: + async with write_stream_reader: + async for session_message in write_stream_reader: + json = session_message.message.model_dump_json(by_alias=True, exclude_unset=True) + data = (json + "\n").encode(encoding=server.encoding, errors=server.encoding_error_handler) + await process.stdin.send(data) + except (anyio.ClosedResourceError, anyio.BrokenResourceError, OSError): + # The server may still be alive: close the read stream so the session + # sees the connection end instead of a request hanging forever. + await read_stream_writer.aclose() + finally: + writer_done.set() + + async def shutdown() -> None: + """Winds the transport down: stop traffic, flush, stop the server, release the streams.""" + # Unblock the reader into its drain: a server stuck writing stdout cannot + # read its stdin, so draining is what lets the flush below complete. + read_stream.close() + # Bounded window for the writer to flush already-accepted messages. + write_stream.close() + with anyio.move_on_after(_WRITER_FLUSH_TIMEOUT) as flush_scope: + await writer_done.wait() + if flush_scope.cancelled_caught: + await anyio.lowlevel.cancel_shielded_checkpoint() # resync coverage on 3.11 (gh-106749) + await _stop_server_process(process) + await _aclose_all(read_stream, write_stream, read_stream_writer, write_stream_reader) + # One pass so unblocked tasks exit via their except paths before the cancel. + await anyio.lowlevel.checkpoint() + + async with anyio.create_task_group() as tg: + tg.start_soon(stdout_reader) + tg.start_soon(stdin_writer) + try: + yield read_stream, write_stream + finally: + shutting_down = True + # Shutdown must finish even under caller cancellation, or the server + # process would leak; every wait inside is bounded. (Native + # task.cancel() and the fallback's worker threads can still defeat it.) + with anyio.CancelScope(shield=True): + await shutdown() + # Unstick pipe tasks a kill survivor's open pipe end could still block. + tg.cancel_scope.cancel() + # The cancel lands via throw(); one yield resyncs 3.11 coverage (gh-106749). + await anyio.lowlevel.cancel_shielded_checkpoint() + + +def _parse_line(line: str) -> SessionMessage | Exception: + """Parses one stdout line, returning parse errors as values for the session to surface.""" + try: + message = types.jsonrpc_message_adapter.validate_json(line, by_name=False) + except ValueError as exc: + logger.exception("Failed to parse JSONRPC message from server") + return exc + return SessionMessage(message) + + +async def _drain_stdout(process: ServerProcess) -> None: + """Consumes and discards the server's remaining stdout. + + Keeps a server flushing buffered output from blocking on a full pipe and + missing its chance to exit; shielded, raw bytes, ends when shutdown closes + the pipe. + """ + assert process.stdout + with anyio.CancelScope(shield=True): + with suppress( + anyio.EndOfStream, + anyio.ClosedResourceError, + anyio.BrokenResourceError, + ConnectionError, + OSError, + ): + while True: + await process.stdout.receive() + + +async def _stop_server_process(process: ServerProcess) -> None: + """Closes stdin, waits out the grace period, then kills the whole tree. + + The escalation order is spec text; timeouts and tree-wide scope are SDK policy: + https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle#shutdown + """ + assert process.stdin and process.stdout, "server process is spawned with pipes" + + await _close_pipe(process.stdin) + if not await _wait_for_process_exit(process, PROCESS_TERMINATION_TIMEOUT): + await _terminate_process_tree(process) + # Until the event loop observes the death, the transport cannot close. + if not await _wait_for_process_exit(process, _KILL_REAP_TIMEOUT): + logger.warning("MCP server process %d is still alive after the kill escalation; abandoning it", process.pid) + + # Reaps surviving Windows job members now, not at GC; no-op on POSIX. + close_process_job(process) + # A kill survivor can hold the stdout pipe open; poison the reader anyway. + await _close_pipe(process.stdout) + _close_subprocess_transport(process) + + +async def _close_pipe(stream: AsyncResource) -> None: + """Closes a pipe stream, tolerating one already closed, broken, or contended.""" + with suppress(OSError, anyio.BrokenResourceError, anyio.ClosedResourceError): + await stream.aclose() + + +async def _wait_for_process_exit(process: ServerProcess, timeout: float) -> bool: + """Returns whether the process died within the timeout, by polling returncode. + + Not process.wait(): on asyncio 3.11+ it also waits for pipe EOF, and a + child that inherited the pipes makes an exited server look hung. + """ + deadline = anyio.current_time() + timeout + while process.returncode is None: + if anyio.current_time() >= deadline: + return False + await anyio.sleep(_EXIT_POLL_INTERVAL) + return True + + +async def _terminate_process_tree(process: ServerProcess) -> None: + """Kills the process and all its descendants. + + POSIX: SIGTERM to the process group, SIGKILL after FORCE_KILL_TIMEOUT. + Windows: immediate Job Object termination (already a hard kill). + """ + if sys.platform == "win32": # pragma: no cover + await terminate_windows_process_tree(process) + else: # pragma: lax no cover + # The Windows-only FallbackProcess never reaches the POSIX path. + assert isinstance(process, Process) + await terminate_posix_process_tree(process, FORCE_KILL_TIMEOUT) + + +def _close_subprocess_transport(process: ServerProcess) -> None: + """Closes the asyncio subprocess transport, if there is one. + + The transport otherwise stays open (and warns at GC) while a surviving + descendant holds a pipe end; nothing public exposes it, hence the attribute + walk. No-op on trio and the Windows fallback. + """ + transport = getattr(getattr(process, "_process", None), "_transport", None) + # Duck-typed: uvloop's UVProcessTransport is not an asyncio.SubprocessTransport. + close = getattr(transport, "close", None) + if callable(close): + # close() on <=3.12 can raise PermissionError re-killing a setuid child. + with suppress(PermissionError): + close() + + +def _get_executable_command(command: str) -> str: + """Normalizes the command for the current platform.""" + if sys.platform == "win32": # pragma: no cover + return get_windows_executable_command(command) + else: # pragma: lax no cover + return command + + +async def _create_platform_compatible_process( + command: str, + args: list[str], + env: dict[str, str] | None = None, + errlog: TextIO = sys.stderr, + cwd: Path | str | None = None, +) -> ServerProcess: + """Spawns the server in its own kill scope. + + A new session/process group on POSIX, a Job Object on Windows. + """ + if sys.platform == "win32": # pragma: no cover + return await create_windows_process(command, args, env, errlog, cwd) + else: # pragma: lax no cover + return await anyio.open_process( + [command, *args], + env=env, + stderr=errlog, + cwd=cwd, + start_new_session=True, + ) + + +async def _aclose_all(*streams: AsyncResource) -> None: + """Closes every given stream.""" + for stream in streams: + await stream.aclose() diff --git a/venv/lib/python3.12/site-packages/mcp/client/streamable_http.py b/venv/lib/python3.12/site-packages/mcp/client/streamable_http.py new file mode 100644 index 0000000..226b0fe --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/streamable_http.py @@ -0,0 +1,710 @@ +"""Implements StreamableHTTP transport for MCP clients.""" + +from __future__ import annotations as _annotations + +import contextlib +import logging +from collections.abc import AsyncGenerator, Awaitable, Callable +from contextlib import asynccontextmanager +from dataclasses import dataclass + +import anyio +import httpx2 +from anyio.abc import TaskGroup +from httpx2 import EventSource, ServerSentEvent +from mcp_types import ( + CONNECTION_CLOSED, + INTERNAL_ERROR, + INVALID_REQUEST, + METHOD_NOT_FOUND, + PARSE_ERROR, + ErrorData, + JSONRPCError, + JSONRPCMessage, + JSONRPCNotification, + JSONRPCRequest, + JSONRPCResponse, + RequestId, + jsonrpc_message_adapter, +) +from mcp_types.version import MODERN_PROTOCOL_VERSIONS +from pydantic import ValidationError + +from mcp.client._transport import TransportStreams +from mcp.shared._compat import resync_tracer +from mcp.shared._context_streams import ContextReceiveStream, ContextSendStream, create_context_streams +from mcp.shared._httpx_utils import create_mcp_http_client +from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER +from mcp.shared.jsonrpc_dispatcher import cancelled_request_id_from_params +from mcp.shared.message import ClientMessageMetadata, SessionMessage + +logger = logging.getLogger(__name__) + + +# TODO(Marcelo): Put the TransportStreams in a module under shared, so we can import here. +SessionMessageOrError = SessionMessage | Exception +StreamWriter = ContextSendStream[SessionMessageOrError] +StreamReader = ContextReceiveStream[SessionMessage] + +MCP_SESSION_ID = "mcp-session-id" +LAST_EVENT_ID = "last-event-id" + +# Reconnection defaults +DEFAULT_RECONNECTION_DELAY_MS = 1000 # 1 second fallback when server doesn't provide retry +MAX_RECONNECTION_ATTEMPTS = 2 # Max retry attempts before giving up + + +class StreamableHTTPError(Exception): + """Base exception for StreamableHTTP transport errors.""" + + +class ResumptionError(StreamableHTTPError): + """Raised when resumption request is invalid.""" + + +@dataclass +class RequestContext: + """Context for a request operation.""" + + client: httpx2.AsyncClient + session_id: str | None + session_message: SessionMessage + metadata: ClientMessageMetadata | None + read_stream_writer: StreamWriter + + +@dataclass(slots=True) +class _InFlightPost: + """A request POST in flight: its abort scope and the era it was sent under. + + `modern` is the negotiated-version cache as of this request's dequeue, so a + later cancel frame is interpreted under the era the request actually ran + with, not whatever the cache says by then. + """ + + scope: anyio.CancelScope + modern: bool + + +class StreamableHTTPTransport: + """StreamableHTTP client transport implementation.""" + + def __init__(self, url: str) -> None: + """Initialize the StreamableHTTP transport. + + Args: + url: The endpoint URL. + """ + self.url = url + self.session_id: str | None = None + # Captured from each stamped message's metadata, synchronously in the + # post_writer loop so the cache always reflects wire order (a POST task's + # scheduling is arbitrary). Reused on outbound HTTP that carries no + # per-message header (transport-internal GET/DELETE, and dispatcher-written + # response/error POSTs that bypass the session's stamp), and consulted by + # `_consume_modern_cancellation`. Cleared when an `initialize` message is + # dequeued so a probe-stamped value cannot leak onto the handshake. + self._protocol_version_header: str | None = None + # Every request's POST runs inside one of these so an outbound + # `notifications/cancelled` at 2026 can abort it; see + # `_consume_modern_cancellation`. Keys are verbatim-typed ("1" is not 1). + self._in_flight_posts: dict[RequestId, _InFlightPost] = {} + + def _prepare_headers(self) -> dict[str, str]: + """Build MCP-specific request headers for any outbound HTTP request. + + These are merged with the ``httpx2.AsyncClient`` defaults (these take + precedence). The cached ``MCP-Protocol-Version`` is included whenever + present so messages that don't pass through the session's stamp — + response/error POSTs, legacy cancel frames, transport-internal + GET/DELETE — still carry the negotiated version. Per-message headers + are layered on top by the caller. + """ + headers: dict[str, str] = { + "accept": "application/json, text/event-stream", + "content-type": "application/json", + } + if self.session_id: + headers[MCP_SESSION_ID] = self.session_id + if self._protocol_version_header: + headers[MCP_PROTOCOL_VERSION_HEADER] = self._protocol_version_header + return headers + + def _is_initialization_request(self, message: JSONRPCMessage) -> bool: + """Check if the message is an initialization request.""" + return isinstance(message, JSONRPCRequest) and message.method == "initialize" + + def _is_initialized_notification(self, message: JSONRPCMessage) -> bool: + """Check if the message is an initialized notification.""" + return isinstance(message, JSONRPCNotification) and message.method == "notifications/initialized" + + def _maybe_extract_session_id_from_response(self, response: httpx2.Response) -> None: + """Extract and store session ID from response headers.""" + new_session_id = response.headers.get(MCP_SESSION_ID) + if new_session_id: + self.session_id = new_session_id + logger.info(f"Received session ID: {self.session_id}") + + async def _handle_sse_event( + self, + sse: ServerSentEvent, + read_stream_writer: StreamWriter, + original_request_id: RequestId | None = None, + resumption_callback: Callable[[str], Awaitable[None]] | None = None, + ) -> bool: + """Handle an SSE event, returning True if the response is complete.""" + if sse.event == "message": + # Handle priming events (empty data with ID) for resumability + if not sse.data: + # Call resumption callback for priming events that have an ID + if sse.id and resumption_callback: + await resumption_callback(sse.id) + return False + try: + message = jsonrpc_message_adapter.validate_json(sse.data, by_name=False) + logger.debug(f"SSE message: {message}") + + # If this is a response and we have original_request_id, replace it + if original_request_id is not None and isinstance(message, JSONRPCResponse | JSONRPCError): + message.id = original_request_id + + session_message = SessionMessage(message) + await read_stream_writer.send(session_message) + + # Call resumption token callback if we have an ID + if sse.id and resumption_callback: + await resumption_callback(sse.id) + + # If this is a response or error return True indicating completion + # Otherwise, return False to continue listening + return isinstance(message, JSONRPCResponse | JSONRPCError) + + # Forwarding to a closed read stream lands here when the caller cancels mid-SSE + # (BrokenResourceError, not a parse failure); coverage is timing-dependent in the + # streaming story's modern HTTP cancellation leg. + except Exception as exc: # pragma: lax no cover + logger.exception("Error parsing SSE message") + if original_request_id is not None: + error_data = ErrorData(code=PARSE_ERROR, message=f"Failed to parse SSE message: {exc}") + error_msg = SessionMessage(JSONRPCError(jsonrpc="2.0", id=original_request_id, error=error_data)) + await read_stream_writer.send(error_msg) + return True + await read_stream_writer.send(exc) + return False + else: # pragma: no cover + logger.warning(f"Unknown SSE event: {sse.event}") + return False + + async def handle_get_stream(self, client: httpx2.AsyncClient, read_stream_writer: StreamWriter) -> None: + """Handle GET stream for server-initiated messages with auto-reconnect.""" + last_event_id: str | None = None + retry_interval_ms: int | None = None + attempt: int = 0 + + while attempt < MAX_RECONNECTION_ATTEMPTS: # pragma: no branch + try: + if not self.session_id: + return + + headers = self._prepare_headers() + if last_event_id: + headers[LAST_EVENT_ID] = last_event_id + + async with client.sse(self.url, headers=headers) as event_source: + event_source.response.raise_for_status() + logger.debug("GET SSE connection established") + + async for sse in event_source: + # Track last event ID for reconnection + if sse.id: + last_event_id = sse.id + # Track retry interval from server + if sse.retry is not None: + retry_interval_ms = sse.retry + + await self._handle_sse_event(sse, read_stream_writer) + + # Stream ended normally (server closed) - reset attempt counter + attempt = 0 + + except Exception: + logger.debug("GET stream error", exc_info=True) + attempt += 1 + + if attempt >= MAX_RECONNECTION_ATTEMPTS: # pragma: no cover + logger.debug(f"GET stream max reconnection attempts ({MAX_RECONNECTION_ATTEMPTS}) exceeded") + return + + # Wait before reconnecting + delay_ms = retry_interval_ms if retry_interval_ms is not None else DEFAULT_RECONNECTION_DELAY_MS + logger.info(f"GET stream disconnected, reconnecting in {delay_ms}ms...") + await anyio.sleep(delay_ms / 1000.0) + + async def _handle_resumption_request(self, ctx: RequestContext) -> None: + """Handle a resumption request using GET with SSE.""" + headers = self._prepare_headers() + if ctx.metadata and ctx.metadata.resumption_token: + headers[LAST_EVENT_ID] = ctx.metadata.resumption_token + else: + raise ResumptionError("Resumption request requires a resumption token") # pragma: no cover + + # Extract original request ID to map responses + original_request_id = None + if isinstance(ctx.session_message.message, JSONRPCRequest): # pragma: no branch + original_request_id = ctx.session_message.message.id + + async with ctx.client.sse(self.url, headers=headers) as event_source: + event_source.response.raise_for_status() + logger.debug("Resumption GET SSE connection established") + + async for sse in event_source: # pragma: no branch + is_complete = await self._handle_sse_event( + sse, + ctx.read_stream_writer, + original_request_id, + ctx.metadata.on_resumption_token_update if ctx.metadata else None, + ) + if is_complete: + await event_source.response.aclose() + break + + def _consume_modern_cancellation(self, session_message: SessionMessage) -> bool: + """Translate an outbound `notifications/cancelled` at 2026; True means "do not POST". + + The 2026 wire defines no client-to-server notifications over streamable + HTTP: closing a request's response stream IS its cancellation signal. + The dispatcher still emits the courtesy frame as its abandon signal + (every outbound cancel names one of our own request ids - the spec + forbids cancelling a request the sender did not issue), so this + transport translates it: when the named request's POST is in flight, + that POST's own recorded era decides - abort-and-swallow at 2026, POST + the frame below it (where the frame is the signal and a disconnect + explicitly is not). With no POST to consult, the cached negotiated + version decides; at 2026 the frame is swallowed even unmatched, so a + late cancel racing the response cannot leak onto the wire. + """ + message = session_message.message + if not (isinstance(message, JSONRPCNotification) and message.method == "notifications/cancelled"): + return False + request_id = cancelled_request_id_from_params(message.params) + post = self._in_flight_posts.get(request_id) if request_id is not None else None + if post is not None: + if not post.modern: + return False + logger.debug("aborting in-flight POST for cancelled request %r", request_id) + post.scope.cancel() + return True + return self._protocol_version_header in MODERN_PROTOCOL_VERSIONS + + async def _run_request_post( + self, + post_fn: Callable[[], Awaitable[None]], + post: _InFlightPost, + request_id: RequestId, + ) -> None: + """Run one request's POST inside its abort scope (see `_consume_modern_cancellation`).""" + try: + with post.scope: + await post_fn() + finally: + # Identity-guarded: a reused id may already have a successor + # registered while this task unwinds - popping by key alone would + # evict the live entry and leave the new POST unabortable. + if self._in_flight_posts.get(request_id) is post: + del self._in_flight_posts[request_id] + + async def _handle_post_request(self, ctx: RequestContext) -> None: + """Handle a POST request with response processing.""" + message = ctx.session_message.message + headers = self._prepare_headers() + if ctx.metadata is not None and ctx.metadata.headers is not None: + headers.update(ctx.metadata.headers) + + async with ctx.client.stream( + "POST", + self.url, + json=message.model_dump(by_alias=True, mode="json", exclude_unset=True), + headers=headers, + ) as response: + if response.status_code == 202: + logger.debug("Received 202 Accepted") + if isinstance(message, JSONRPCRequest): + # A request's response arrives on this POST's body; 202 says + # none will follow. Resolve rather than park the caller forever. + await self._resolve_abandoned_request( + ctx.read_stream_writer, + message.id, + "server answered a request with 202 Accepted", + code=INVALID_REQUEST, + ) + return + + if response.status_code >= 400: + if isinstance(message, JSONRPCRequest): + # A spec-correct server may return the JSON-RPC error in the + # body at a non-2xx status (e.g. 400 for INVALID_PARAMS, 404 + # for METHOD_NOT_FOUND). Surface that error rather than the + # status-derived stand-in below. + if response.headers.get("content-type", "").lower().startswith("application/json"): + try: + body = await response.aread() + parsed = jsonrpc_message_adapter.validate_json(body, by_name=False) + if isinstance(parsed, JSONRPCError): + # The server may have set `id: null` (request rejected before its + # id was parsed); use this request's id so correlation works. + reply = JSONRPCError(jsonrpc="2.0", id=message.id, error=parsed.error) + await ctx.read_stream_writer.send(SessionMessage(reply)) + return + except (httpx2.StreamError, ValidationError): + pass + logger.debug("Non-2xx body was not a JSON-RPC error; using fallback") + if response.status_code == 404: + if self.session_id is None: + # No session yet → 404 is the HTTP-level spelling of + # METHOD_NOT_FOUND (gateway / legacy server doesn't know + # this method); "Session terminated" would be a lie here. + error_data = ErrorData(code=METHOD_NOT_FOUND, message="Not Found") + else: + error_data = ErrorData(code=INVALID_REQUEST, message="Session terminated") + else: + error_data = ErrorData(code=INTERNAL_ERROR, message="Server returned an error response") + session_message = SessionMessage(JSONRPCError(jsonrpc="2.0", id=message.id, error=error_data)) + await ctx.read_stream_writer.send(session_message) + return + + if self._is_initialization_request(message): + self._maybe_extract_session_id_from_response(response) + + # Per https://modelcontextprotocol.io/specification/2025-06-18/basic#notifications: + # The server MUST NOT send a response to notifications. + if isinstance(message, JSONRPCRequest): + content_type = response.headers.get("content-type", "").lower() + if content_type.startswith("application/json"): + await self._handle_json_response(response, ctx.read_stream_writer, request_id=message.id) + elif content_type.startswith("text/event-stream"): + await self._handle_sse_response(response, ctx) + else: + logger.error(f"Unexpected content type: {content_type}") + error_data = ErrorData(code=INVALID_REQUEST, message=f"Unexpected content type: {content_type}") + error_msg = SessionMessage(JSONRPCError(jsonrpc="2.0", id=message.id, error=error_data)) + await ctx.read_stream_writer.send(error_msg) + + async def _handle_json_response( + self, + response: httpx2.Response, + read_stream_writer: StreamWriter, + *, + request_id: RequestId, + ) -> None: + """Handle JSON response from the server.""" + try: + content = await response.aread() + message = jsonrpc_message_adapter.validate_json(content, by_name=False) + session_message = SessionMessage(message) + await read_stream_writer.send(session_message) + except (httpx2.StreamError, ValidationError) as exc: + logger.exception("Error parsing JSON response") + error_data = ErrorData(code=PARSE_ERROR, message=f"Failed to parse JSON response: {exc}") + error_msg = SessionMessage(JSONRPCError(jsonrpc="2.0", id=request_id, error=error_data)) + await read_stream_writer.send(error_msg) + + async def _handle_sse_response( + self, + response: httpx2.Response, + ctx: RequestContext, + ) -> None: + """Handle SSE response from the server.""" + last_event_id: str | None = None + retry_interval_ms: int | None = None + + # The caller (_handle_post_request) only reaches here inside + # isinstance(message, JSONRPCRequest), so this is always a JSONRPCRequest. + assert isinstance(ctx.session_message.message, JSONRPCRequest) + original_request_id = ctx.session_message.message.id + + try: + event_source = EventSource(response) + async for sse in event_source: # pragma: no branch + # Track last event ID for potential reconnection + if sse.id: + last_event_id = sse.id + + # Track retry interval from server + if sse.retry is not None: + retry_interval_ms = sse.retry + + is_complete = await self._handle_sse_event( + sse, + ctx.read_stream_writer, + original_request_id=original_request_id, + resumption_callback=(ctx.metadata.on_resumption_token_update if ctx.metadata else None), + ) + # If the SSE event indicates completion, like returning response/error + # break the loop + if is_complete: + await response.aclose() + return # Normal completion, no reconnect needed + except Exception: + logger.debug("SSE stream ended", exc_info=True) # pragma: lax no cover + + # Stream ended without response - reconnect if we received an event with ID + if last_event_id is not None: + logger.info("SSE stream disconnected, reconnecting...") + await self._handle_reconnection(ctx, last_event_id, retry_interval_ms) + else: + # Not resumable: resolve the waiter, else a listen stream's consumer + # would hang forever instead of learning the subscription is lost. + await self._resolve_abandoned_request( + ctx.read_stream_writer, original_request_id, "SSE stream ended without a response" + ) + + async def _resolve_abandoned_request( + self, read_stream_writer: StreamWriter, request_id: RequestId, message: str, *, code: int = CONNECTION_CLOSED + ) -> None: + """Resolve a request whose response can never arrive with a synthesized error. + + Best-effort: a closed read stream means the session is tearing down. + """ + error_data = ErrorData(code=code, message=message) + error_msg = SessionMessage(JSONRPCError(jsonrpc="2.0", id=request_id, error=error_data)) + try: + await read_stream_writer.send(error_msg) + except (anyio.BrokenResourceError, anyio.ClosedResourceError): + logger.debug("read stream closed before request %r could be resolved", request_id) + + async def _handle_reconnection( + self, + ctx: RequestContext, + last_event_id: str, + retry_interval_ms: int | None = None, + attempt: int = 0, + ) -> None: + """Reconnect with Last-Event-ID to resume stream after server disconnect.""" + # Only requests reconnect: every caller arrives from a request's response stream. + assert isinstance(ctx.session_message.message, JSONRPCRequest) + original_request_id = ctx.session_message.message.id + + if attempt >= MAX_RECONNECTION_ATTEMPTS: + # Resolve on give-up: a request with no read timeout (a listen + # stream) would otherwise hang its caller forever. + logger.debug(f"Max reconnection attempts ({MAX_RECONNECTION_ATTEMPTS}) exceeded") + await self._resolve_abandoned_request( + ctx.read_stream_writer, original_request_id, "SSE stream ended and reconnection attempts were exhausted" + ) + return + + # Always wait - use server value or default + delay_ms = retry_interval_ms if retry_interval_ms is not None else DEFAULT_RECONNECTION_DELAY_MS + await anyio.sleep(delay_ms / 1000.0) + + headers = self._prepare_headers() + headers[LAST_EVENT_ID] = last_event_id + + try: + async with ctx.client.sse(self.url, headers=headers) as event_source: + event_source.response.raise_for_status() + logger.info("Reconnected to SSE stream") + + # Track for potential further reconnection + reconnect_last_event_id: str = last_event_id + reconnect_retry_ms = retry_interval_ms + + async for sse in event_source: + if sse.id: # pragma: no branch + reconnect_last_event_id = sse.id + if sse.retry is not None: + reconnect_retry_ms = sse.retry + + is_complete = await self._handle_sse_event( + sse, + ctx.read_stream_writer, + original_request_id, + ctx.metadata.on_resumption_token_update if ctx.metadata else None, + ) + if is_complete: + await event_source.response.aclose() + return + + # Stream ended again without response - reconnect again (reset attempt counter) + logger.info("SSE stream disconnected, reconnecting...") + await self._handle_reconnection(ctx, reconnect_last_event_id, reconnect_retry_ms, 0) + except Exception as e: # pragma: no cover + logger.debug(f"Reconnection failed: {e}") + # Try to reconnect again if we still have an event ID + await self._handle_reconnection(ctx, last_event_id, retry_interval_ms, attempt + 1) + + async def post_writer( + self, + client: httpx2.AsyncClient, + write_stream_reader: StreamReader, + read_stream_writer: StreamWriter, + write_stream: ContextSendStream[SessionMessage], + start_get_stream: Callable[[], None], + tg: TaskGroup, + ) -> None: + """Handle writing requests to the server.""" + try: + async with write_stream_reader, read_stream_writer, write_stream: + + async def _handle_message(session_message: SessionMessage) -> None: + message = session_message.message + if self._consume_modern_cancellation(session_message): + return + metadata = ( + session_message.metadata + if isinstance(session_message.metadata, ClientMessageMetadata) + else None + ) + + # Check if this is a resumption request + is_resumption = bool(metadata and metadata.resumption_token) + + logger.debug(f"Sending client message: {message}") + + # Handle initialized notification + if self._is_initialized_notification(message): + start_get_stream() + + if self._is_initialization_request(message): + # `initialize` is the negotiation, not a "subsequent request" — discard any + # probe-stamped value so the discover→fallback path can't leak it onto the handshake. + self._protocol_version_header = None + elif metadata is not None and metadata.headers is not None: + stamped_version = metadata.headers.get(MCP_PROTOCOL_VERSION_HEADER) + if stamped_version is not None: + self._protocol_version_header = stamped_version + + ctx = RequestContext( + client=client, + session_id=self.session_id, + session_message=session_message, + metadata=metadata, + read_stream_writer=read_stream_writer, + ) + + async def handle_request_async(): + if is_resumption: + await self._handle_resumption_request(ctx) + else: + await self._handle_post_request(ctx) + + # If this is a request, start a new task to handle it + if isinstance(message, JSONRPCRequest): + # Register the abort scope before the spawn: the next + # message through this loop can already be the abandon + # signal for this id, ahead of the task ever running. + post = _InFlightPost( + scope=anyio.CancelScope(), + modern=self._protocol_version_header in MODERN_PROTOCOL_VERSIONS, + ) + superseded = self._in_flight_posts.get(message.id) + if superseded is not None: + # A reused id means the waiter belongs to this attempt now: + # sever the old POST so its zombie stream cannot answer, + # fail, or resolve the successor's request. + superseded.scope.cancel() + self._in_flight_posts[message.id] = post + tg.start_soon(self._run_request_post, handle_request_async, post, message.id) + else: + await handle_request_async() + + async for session_message in write_stream_reader: + sender_ctx = write_stream_reader.last_context + if sender_ctx is not None: + async with anyio.create_task_group() as tg_local: + sender_ctx.run(tg_local.start_soon, _handle_message, session_message) + else: + await _handle_message(session_message) # pragma: no cover + + except Exception: # pragma: lax no cover + logger.exception("Error in post_writer") + + async def terminate_session(self, client: httpx2.AsyncClient) -> None: + """Terminate the session by sending a DELETE request.""" + if not self.session_id: + return # pragma: no cover + + try: + headers = self._prepare_headers() + response = await client.delete(self.url, headers=headers) + + if response.status_code == 405: + logger.debug("Server does not allow session termination") + elif response.status_code not in (200, 204): + logger.warning(f"Session termination failed: {response.status_code}") # pragma: no cover + except Exception as exc: # pragma: no cover + logger.warning(f"Session termination failed: {exc}") + + +@asynccontextmanager +async def streamable_http_client( + url: str, + *, + http_client: httpx2.AsyncClient | None = None, + terminate_on_close: bool = True, +) -> AsyncGenerator[TransportStreams, None]: + """Client transport for StreamableHTTP. + + Args: + url: The MCP server endpoint URL. + http_client: Optional pre-configured httpx2.AsyncClient. If None, a default + client with recommended MCP timeouts will be created. To configure headers, + authentication, or other HTTP settings, create an httpx2.AsyncClient and pass it here. + terminate_on_close: If True, send a DELETE request to terminate the session when the context exits. + + Yields: + Tuple containing: + - read_stream: Stream for reading messages from the server + - write_stream: Stream for sending messages to the server + + Example: + See examples/snippets/clients/ for usage patterns. + """ + # Determine if we need to create and manage the client + client_provided = http_client is not None + client = http_client + + if client is None: + # Create default client with recommended MCP timeouts + client = create_mcp_http_client() + + transport = StreamableHTTPTransport(url) + + logger.debug(f"Connecting to StreamableHTTP endpoint: {url}") + + async with contextlib.AsyncExitStack() as stack: + # Only manage client lifecycle if we created it + if not client_provided: + await stack.enter_async_context(client) + + read_stream_writer, read_stream = create_context_streams[SessionMessage | Exception](0) + write_stream, write_stream_reader = create_context_streams[SessionMessage](0) + + async with ( + read_stream_writer, + read_stream, + write_stream, + write_stream_reader, + anyio.create_task_group() as tg, + ): + + def start_get_stream() -> None: + tg.start_soon(transport.handle_get_stream, client, read_stream_writer) + + tg.start_soon( + transport.post_writer, + client, + write_stream_reader, + read_stream_writer, + write_stream, + start_get_stream, + tg, + ) + + try: + yield read_stream, write_stream + finally: + if transport.session_id and terminate_on_close: + await transport.terminate_session(client) + tg.cancel_scope.cancel() + await resync_tracer() diff --git a/venv/lib/python3.12/site-packages/mcp/client/subscriptions.py b/venv/lib/python3.12/site-packages/mcp/client/subscriptions.py new file mode 100644 index 0000000..2728390 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/client/subscriptions.py @@ -0,0 +1,282 @@ +"""Client-side `subscriptions/listen` driver (2026-07-28, SEP-2575). + +`listen()` opens the stream as an async context manager: entering waits for +the server's acknowledgment, iteration yields typed change events, a graceful +server close ends the loop, and an abrupt drop raises `SubscriptionLost`. +There is no replay and no automatic re-listen: a client that re-opens a +subscription refetches what it depends on. +""" + +from __future__ import annotations + +from collections.abc import AsyncIterator, Awaitable, Callable, Sequence +from contextlib import asynccontextmanager +from itertools import count +from typing import TYPE_CHECKING, Literal + +import anyio +import mcp_types as types +from mcp_types.version import MODERN_PROTOCOL_VERSIONS + +from mcp.shared.dispatcher import CallOptions +from mcp.shared.exceptions import MCPError +from mcp.shared.subscriptions import ( + PromptsListChanged, + ResourcesListChanged, + ResourceUpdated, + ServerEvent, + ToolsListChanged, + event_matches, +) + +if TYPE_CHECKING: + from mcp.client.session import ClientSession + +__all__ = [ + "ListenNotSupportedError", + "OnEvent", + "PromptsListChanged", + "ResourceUpdated", + "ResourcesListChanged", + "ServerEvent", + "Subscription", + "SubscriptionLost", + "ToolsListChanged", + "listen", +] + +_listen_ids = count(1) +"""Process-wide `listen-N` sequence: string ids can never collide with a dispatcher's minted ints.""" + +_MAX_PENDING_EVENTS = 1024 +"""Backlog backstop: the spec allows sub-resource URIs, so distinct pending +`ResourceUpdated` events are unbounded; overflowing this cap settles the +subscription lost rather than growing client memory.""" + +_SubscriptionEnd = Literal["graceful", "lost", "local"] + + +class ListenNotSupportedError(RuntimeError): + """`subscriptions/listen` requires a 2026-07-28 connection.""" + + def __init__(self, negotiated_version: str | None) -> None: + self.negotiated_version = negotiated_version + super().__init__( + f"subscriptions/listen is not available at protocol version {negotiated_version!r}; it requires " + "2026-07-28. On earlier versions use subscribe_resource() and the change notifications delivered " + "through message_handler." + ) + + +class SubscriptionLost(RuntimeError): + """The stream ended without the server's graceful close; re-listen and refetch.""" + + +class ListenRoute: + """Package-internal demux state for one listen stream, fed synchronously in receive order by the session.""" + + def __init__(self) -> None: + self.honored: types.SubscriptionFilter | None = None + self.acked = anyio.Event() + self.error: MCPError | None = None + self.end: _SubscriptionEnd | None = None + self._honored_uris: frozenset[str] = frozenset() + self._pending: dict[ServerEvent, None] = {} + self._wake = anyio.Event() + + def set_acked(self, honored: types.SubscriptionFilter) -> None: + """Record the acknowledged filter; the first ack wins.""" + if not self.acked.is_set(): + self.honored = honored + self._honored_uris = frozenset(honored.resource_subscriptions or ()) + self.acked.set() + + def deliver(self, event: ServerEvent) -> None: + """Queue an event within the honored filter, deduplicated against the backlog. + + Any `ResourceUpdated` is admitted once URI subscriptions were honored at + all: the spec allows the stamped URI to be a sub-resource of a subscribed one. + """ + if self.end is not None or self.honored is None: + return + if isinstance(event, ResourceUpdated): + admitted = bool(self._honored_uris) + else: + admitted = event_matches(self.honored, self._honored_uris, event) + if not admitted or event in self._pending: + return + if len(self._pending) >= _MAX_PENDING_EVENTS: + self.settle( + "lost", + error=MCPError( + types.INTERNAL_ERROR, + f"subscription backlog exceeded {_MAX_PENDING_EVENTS} unconsumed events; re-listen and refetch", + ), + ) + return + self._pending[event] = None + self._wake.set() + + def settle(self, end: _SubscriptionEnd, error: MCPError | None = None) -> None: + """Record the stream's end; the first reason wins and wakes both waiters.""" + if self.end is None: + self.end = end + self.error = error + self.acked.set() + self._wake.set() + + async def next_event(self) -> ServerEvent | _SubscriptionEnd: + """Peek the next pending event, or the stream's end once the backlog drains. + + A "local" end short-circuits the backlog; the other endings drain it first, + so a graceful close never swallows events that preceded it. + """ + while True: + # Snapshot the wake event before checking state so a deliver landing after the checks cannot be missed. + wake = self._wake + if self.end == "local": + return self.end + if self._pending: + return next(iter(self._pending)) + if self.end is not None: + return self.end + await wake.wait() + self._wake = anyio.Event() + + def consume(self, event: ServerEvent) -> None: + """Remove a peeked event from the backlog.""" + self._pending.pop(event, None) + + +OnEvent = Callable[[ServerEvent], Awaitable[None]] +"""Per-event barrier awaited before a `Subscription` returns each event to its consumer.""" + + +class Subscription: + """One open `subscriptions/listen` stream: an async iterator of typed events. + + Produced by `listen()` / `Client.listen()`, not constructed directly. + """ + + def __init__( + self, + route: ListenRoute, + subscription_id: types.RequestId, + honored: types.SubscriptionFilter, + on_event: OnEvent | None = None, + ): + self._route = route + self._on_event = on_event + self.subscription_id = subscription_id + """The listen request's JSON-RPC id, stamped into every frame's `_meta`.""" + self.honored = honored + """The subset of the requested filter the server agreed to deliver.""" + + def __aiter__(self) -> Subscription: + return self + + async def __anext__(self) -> ServerEvent: + """Yield the next change event; the loop ends when the stream does. + + Raises: + SubscriptionLost: the stream dropped without the server's graceful close. + """ + outcome = await self._route.next_event() + if isinstance(outcome, str): + if outcome == "lost": + raise SubscriptionLost( + f"subscription {self.subscription_id!r} ended without the server's graceful close;" + " re-listen and refetch" + ) from self._route.error + raise StopAsyncIteration + if self._on_event is not None: + # The event stays pending while the barrier runs: a cancellation or a + # raising barrier leaves it for the next anext instead of dropping it. + await self._on_event(outcome) + self._route.consume(outcome) + return outcome + + +@asynccontextmanager +async def listen( + session: ClientSession, + *, + tools_list_changed: bool = False, + prompts_list_changed: bool = False, + resources_list_changed: bool = False, + resource_subscriptions: Sequence[str] = (), + on_event: OnEvent | None = None, +) -> AsyncIterator[Subscription]: + """Open one `subscriptions/listen` stream on `session` (2026-07-28 only). + + Entering sends the request and returns once the server's acknowledgment + arrives; exiting ends the subscription. `on_event` is awaited before each + event is returned - the seam `Client.listen` uses to finish cache eviction + before the consumer can refetch. + + Raises: + ListenNotSupportedError: negotiated version predates 2026-07-28. + MCPError: the server rejected the request, or the connection failed pre-ack. + SubscriptionLost: the stream ended before it was acknowledged. + TimeoutError: the session's read timeout elapsed before the acknowledgment. + """ + if session.protocol_version not in MODERN_PROTOCOL_VERSIONS: + raise ListenNotSupportedError(session.protocol_version) + if isinstance(resource_subscriptions, str): + raise TypeError("resource_subscriptions takes a sequence of URIs, not a bare string") + request = types.SubscriptionsListenRequest( + params=types.SubscriptionsListenRequestParams( + notifications=types.SubscriptionFilter( + tools_list_changed=tools_list_changed or None, + prompts_list_changed=prompts_list_changed or None, + resources_list_changed=resources_list_changed or None, + resource_subscriptions=list(resource_subscriptions) or None, + ) + ) + ) + task_group = session._task_group # pyright: ignore[reportPrivateUsage] + if task_group is None: + raise RuntimeError("listen() requires an entered session") + request_id: types.RequestId = f"listen-{next(_listen_ids)}" + data = request.model_dump(by_alias=True, mode="json", exclude_none=True) + opts: CallOptions = {"request_id": request_id} + session._stamp(data, opts) # pyright: ignore[reportPrivateUsage] + driver_scope = anyio.CancelScope() + + async def drive() -> None: + # Deliberately no result timeout: the response arrives when the stream ends. + with driver_scope: + try: + await session._dispatcher.send_raw_request( # pyright: ignore[reportPrivateUsage] + data["method"], data.get("params"), opts + ) + except MCPError as error: + route.settle("lost", error=error) + return + except ValueError as error: + # A raw request id collided with our minted listen id: fail this subscription + # and release the route in this same slice, so it cannot consume the raw caller's ack. + session._unregister_listen_route(request_id) # pyright: ignore[reportPrivateUsage] + route.settle("lost", error=MCPError(types.INTERNAL_ERROR, str(error))) + return + # A result, whatever its body, is the spec's graceful close; with no prior ack + # it opens the subscription already closed. + route.set_acked(types.SubscriptionFilter()) + route.settle("graceful") + + # Register the demux route before the request is written so the ack cannot race it. + route = session._register_listen_route(request_id) # pyright: ignore[reportPrivateUsage] + try: + task_group.start_soon(drive) + with anyio.fail_after(session._session_read_timeout_seconds): # pyright: ignore[reportPrivateUsage] + await route.acked.wait() + if route.honored is None: + # Only reachable on failure paths: a graceful no-ack result acked an empty filter in drive(). + if route.error is not None: + raise route.error + raise SubscriptionLost(f"subscription {request_id!r} ended before it was acknowledged") + yield Subscription(route, request_id, route.honored, on_event) + finally: + route.settle("local") + driver_scope.cancel() + session._unregister_listen_route(request_id) # pyright: ignore[reportPrivateUsage] diff --git a/venv/lib/python3.12/site-packages/mcp/os/__init__.py b/venv/lib/python3.12/site-packages/mcp/os/__init__.py new file mode 100644 index 0000000..fa5dbc8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/os/__init__.py @@ -0,0 +1 @@ +"""Platform-specific utilities for MCP.""" diff --git a/venv/lib/python3.12/site-packages/mcp/os/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/os/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1644888 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/os/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/os/posix/__init__.py b/venv/lib/python3.12/site-packages/mcp/os/posix/__init__.py new file mode 100644 index 0000000..23aff8b --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/os/posix/__init__.py @@ -0,0 +1 @@ +"""POSIX-specific utilities for MCP.""" diff --git a/venv/lib/python3.12/site-packages/mcp/os/posix/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/os/posix/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7e7810f Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/os/posix/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/os/posix/__pycache__/utilities.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/os/posix/__pycache__/utilities.cpython-312.pyc new file mode 100644 index 0000000..e8fd136 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/os/posix/__pycache__/utilities.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/os/posix/utilities.py b/venv/lib/python3.12/site-packages/mcp/os/posix/utilities.py new file mode 100644 index 0000000..d15be17 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/os/posix/utilities.py @@ -0,0 +1,63 @@ +"""POSIX-specific functionality for stdio client operations.""" + +import logging +import os +import signal +from contextlib import suppress + +import anyio +from anyio.abc import Process + +logger = logging.getLogger(__name__) + +# How often to probe for surviving group members between SIGTERM and SIGKILL. +_GROUP_POLL_INTERVAL = 0.01 + + +async def terminate_posix_process_tree(process: Process, timeout_seconds: float = 2.0) -> None: + """Terminates a process and all its descendants on POSIX. + + SIGTERMs the process group, waits up to timeout_seconds for it to + disappear, then SIGKILLs whatever remains. killpg reaches every descendant + atomically, even ones whose parent already exited; daemonizers that left + the group escape by design. A group only disappears once every member is + dead and reaped, so a client running as PID 1 should reap orphans (e.g. + docker run --init) or the wait below runs its full timeout. + """ + # The leader's pid is the pgid (start_new_session). Never use getpgid(): + # it fails once the leader is reaped, even with live members left. + pgid = process.pid + + try: + os.killpg(pgid, signal.SIGTERM) + except ProcessLookupError: + return # the whole group is already gone + except PermissionError: + # EPERM never proves the group is gone (macOS raises it for zombie or + # foreign-euid members), so keep waiting and escalating. + logger.warning( + "No permission to signal some of process group %d; waiting for it to exit anyway", pgid, exc_info=True + ) + + with anyio.move_on_after(timeout_seconds): + while _group_alive(pgid): + # Reading returncode reaps the leader on trio; a zombie leader would + # otherwise keep the group alive for the full timeout. + _ = process.returncode + await anyio.sleep(_GROUP_POLL_INTERVAL) + return + + # ESRCH: died since the last probe. EPERM: we killed what we were allowed to. + with suppress(ProcessLookupError, PermissionError): + os.killpg(pgid, signal.SIGKILL) + + +def _group_alive(pgid: int) -> bool: + """Probes the group with signal 0; only ESRCH proves it is gone.""" + try: + os.killpg(pgid, 0) + except ProcessLookupError: + return False + except PermissionError: + pass # unsignalable survivors or unreaped zombies; EPERM is ambiguous + return True diff --git a/venv/lib/python3.12/site-packages/mcp/os/win32/__init__.py b/venv/lib/python3.12/site-packages/mcp/os/win32/__init__.py new file mode 100644 index 0000000..f1ebab9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/os/win32/__init__.py @@ -0,0 +1 @@ +"""Windows-specific utilities for MCP.""" diff --git a/venv/lib/python3.12/site-packages/mcp/os/win32/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/os/win32/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..48b57f3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/os/win32/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/os/win32/__pycache__/utilities.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/os/win32/__pycache__/utilities.cpython-312.pyc new file mode 100644 index 0000000..87a9030 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/os/win32/__pycache__/utilities.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/os/win32/utilities.py b/venv/lib/python3.12/site-packages/mcp/os/win32/utilities.py new file mode 100644 index 0000000..321fda8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/os/win32/utilities.py @@ -0,0 +1,292 @@ +"""Windows-specific functionality for stdio transport operations.""" + +import logging +import shutil +import subprocess +import sys +import weakref +from contextlib import suppress +from pathlib import Path +from typing import BinaryIO, TextIO, TypeAlias, cast + +import anyio +from anyio.abc import Process +from anyio.streams.file import FileReadStream, FileWriteStream + +logger = logging.getLogger(__name__) + +# Windows-specific imports for Job Objects +if sys.platform == "win32": + import msvcrt + + import pywintypes + import win32api + import win32con + import win32job +else: + # Type stubs for non-Windows platforms + win32api = None + win32con = None + msvcrt = None + win32job = None + pywintypes = None + + +def rebind_std_handle_to_fd(fd: int) -> None: + """Points the Win32 standard-handle slot for fd 0, 1, or 2 at fd's current OS handle. + + os.dup2 updates only the CRT descriptor table; subprocess handle inheritance + reads the Win32 slot, so it must be repointed too. + + Raises: + OSError: The slot could not be set. + """ + if sys.platform != "win32" or not win32api or not msvcrt or not pywintypes: + return + std_ids = {0: win32api.STD_INPUT_HANDLE, 1: win32api.STD_OUTPUT_HANDLE, 2: win32api.STD_ERROR_HANDLE} + try: + win32api.SetStdHandle(std_ids[fd], msvcrt.get_osfhandle(fd)) + except pywintypes.error as exc: + # Normalized so callers' OSError-based best-effort handling covers it. + raise OSError(f"SetStdHandle failed for fd {fd}") from exc + + +# How often FallbackProcess polls the underlying Popen for exit. +_EXIT_POLL_INTERVAL = 0.01 + +# Job Object handle per spawned process, for tree termination at shutdown. +# Values stay pywin32 PyHANDLEs: if no pop site ever runs, the dying weak entry +# drops the last reference and the PyHANDLE destructor closes the handle, which +# is what makes KILL_ON_JOB_CLOSE reap an abandoned tree. +_process_jobs: "weakref.WeakKeyDictionary[Process | FallbackProcess, object]" = weakref.WeakKeyDictionary() + + +def get_windows_executable_command(command: str) -> str: + """Resolves the command to a Windows executable path. + + Tries the bare name first, then the common script extensions (.cmd, .bat, + .exe, .ps1). + """ + try: + if command_path := shutil.which(command): + return command_path + + for ext in [".cmd", ".bat", ".exe", ".ps1"]: + ext_version = f"{command}{ext}" + if ext_path := shutil.which(ext_version): + return ext_path + + return command + except OSError: + return command # path probing failed (permissions, broken symlinks) + + +class FallbackProcess: + """Async wrapper around subprocess.Popen for SelectorEventLoop. + + Windows event loops without async subprocess support get this Popen-backed + fallback, with anyio file streams wrapping the pipes. + """ + + def __init__(self, popen_obj: subprocess.Popen[bytes]) -> None: + self.popen: subprocess.Popen[bytes] = popen_obj + stdin = popen_obj.stdin + stdout = popen_obj.stdout + + self.stdin = FileWriteStream(cast(BinaryIO, stdin)) if stdin else None + self.stdout = FileReadStream(cast(BinaryIO, stdout)) if stdout else None + + async def wait(self) -> int: + """Waits for exit by polling the Popen. + + A thread blocked in Popen.wait() cannot be cancelled by anyio, which + would defeat every timeout placed around this call. + """ + while (returncode := self.popen.poll()) is None: + await anyio.sleep(_EXIT_POLL_INTERVAL) + return returncode + + def terminate(self) -> None: + """Terminates the subprocess.""" + self.popen.terminate() + + def kill(self) -> None: + """Kills the subprocess (on Windows the same hard kill as terminate).""" + self.popen.kill() + + @property + def pid(self) -> int: + """Returns the process ID.""" + return self.popen.pid + + @property + def returncode(self) -> int | None: + """The exit code, or None while the process is still running. + + Polls the Popen so death is observable without anyone calling wait(). + """ + return self.popen.poll() + + +# The process handle stdio_client drives: anyio's Process, or the Popen-backed +# fallback used on Windows event loops without async subprocess support. +ServerProcess: TypeAlias = Process | FallbackProcess + + +async def create_windows_process( + command: str, + args: list[str], + env: dict[str, str] | None = None, + errlog: TextIO | None = sys.stderr, + cwd: Path | str | None = None, +) -> Process | FallbackProcess: + """Creates a subprocess with Job Object support for tree termination. + + Spawns via anyio's open_process; event loops without async subprocess + support (notably the SelectorEventLoop) raise NotImplementedError, in which + case the spawn falls back to a Popen-backed FallbackProcess. Either way the + process is then assigned to a Job Object so its children can be terminated + with it; children spawned before the assignment completes are not captured + (see the inline note below). + + Returns: + Process | FallbackProcess: The spawned process with async stdin/stdout streams. + """ + try: + process = await anyio.open_process( + [command, *args], + env=env, + # Ensure we don't create console windows for each process + creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0), + stderr=errlog, + cwd=cwd, + ) + except NotImplementedError: + # Windows event loops without async subprocess support (SelectorEventLoop) + process = await _create_windows_fallback_process(command, args, env, errlog, cwd) + + # Children spawned before the assignment completes land outside the job + # (membership is inherited at CreateProcess, never acquired retroactively); + # if that ever bites, the fix is a CREATE_SUSPENDED spawn -> assign -> resume. + job = _create_job_object() + _maybe_assign_process_to_job(process, job) + return process + + +async def _create_windows_fallback_process( + command: str, + args: list[str], + env: dict[str, str] | None = None, + errlog: TextIO | None = sys.stderr, + cwd: Path | str | None = None, +) -> FallbackProcess: + """Spawns via subprocess.Popen and wraps it in FallbackProcess.""" + popen_obj = subprocess.Popen( + [command, *args], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=errlog, + env=env, + cwd=cwd, + bufsize=0, # Unbuffered output + creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0), + ) + return FallbackProcess(popen_obj) + + +def _create_job_object() -> object | None: + """Creates a Windows Job Object configured to terminate all its processes when closed.""" + if sys.platform != "win32" or not win32api or not win32job: + return None + + job = None + try: + job = win32job.CreateJobObject(None, "") + extended_info = win32job.QueryInformationJobObject(job, win32job.JobObjectExtendedLimitInformation) + + extended_info["BasicLimitInformation"]["LimitFlags"] |= win32job.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE + win32job.SetInformationJobObject(job, win32job.JobObjectExtendedLimitInformation, extended_info) + return job + except pywintypes.error: + logger.warning("Failed to create Job Object for process tree management", exc_info=True) + # If creation succeeded but configuration failed, close the handle now. + if job is not None: + _close_job_handle(job) + return None + + +def _maybe_assign_process_to_job(process: Process | FallbackProcess, job: object | None) -> None: + """Assigns the process to the job and records it for tree termination. + + On any failure the job handle is closed instead. + """ + if job is None: + return + + if sys.platform != "win32" or not win32api or not win32con or not win32job: + return + + try: + process_handle = win32api.OpenProcess( + win32con.PROCESS_SET_QUOTA | win32con.PROCESS_TERMINATE, False, process.pid + ) + if not process_handle: + raise pywintypes.error(0, "OpenProcess", "Failed to open process handle") + + try: + win32job.AssignProcessToJobObject(job, process_handle) + finally: + win32api.CloseHandle(process_handle) + # Record only after the CloseHandle above succeeded: had it failed, the + # except below would close the job and KILL_ON_JOB_CLOSE takes the server. + _process_jobs[process] = job + except pywintypes.error: + logger.warning("Failed to assign process %d to Job Object", process.pid, exc_info=True) + _close_job_handle(job) + + +def close_process_job(process: Process | FallbackProcess) -> None: + """Closes the process's Job Object handle, if it still has one. + + KILL_ON_JOB_CLOSE makes the close also kill any members still alive, + deterministically rather than at GC time; a deliberate divergence from + POSIX, where a graceful server's children are left alive. + """ + if sys.platform != "win32": + return + + job = _process_jobs.pop(process, None) + if job is not None: + _close_job_handle(job) + + +async def terminate_windows_process_tree(process: Process | FallbackProcess) -> None: + """Terminates the process's job, or just the process if it has no job. + + Job termination is an immediate hard kill of every member. Windows has no + tree-wide SIGTERM; the stdin-close grace period is the server's chance to + exit cleanly. + """ + if sys.platform != "win32": + return + + job = _process_jobs.pop(process, None) + if job is not None and win32job: + try: + with suppress(pywintypes.error): # the job might already be terminated + win32job.TerminateJobObject(job, 1) + finally: + _close_job_handle(job) + + # The process may have no job (creation or assignment failed); kill it directly too. + try: + process.terminate() + except OSError: + pass + + +def _close_job_handle(job: object) -> None: + """Closes a Job Object handle, tolerating one that is already closed.""" + if win32api and pywintypes: + with suppress(pywintypes.error): + win32api.CloseHandle(job) diff --git a/venv/lib/python3.12/site-packages/mcp/py.typed b/venv/lib/python3.12/site-packages/mcp/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/mcp/server/__init__.py b/venv/lib/python3.12/site-packages/mcp/server/__init__.py new file mode 100644 index 0000000..5897e8a --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/__init__.py @@ -0,0 +1,7 @@ +from .caching import CacheHint +from .context import ServerRequestContext +from .lowlevel import NotificationOptions, Server +from .mcpserver import MCPServer +from .models import InitializationOptions + +__all__ = ["CacheHint", "Server", "ServerRequestContext", "MCPServer", "NotificationOptions", "InitializationOptions"] diff --git a/venv/lib/python3.12/site-packages/mcp/server/__main__.py b/venv/lib/python3.12/site-packages/mcp/server/__main__.py new file mode 100644 index 0000000..4305b87 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/__main__.py @@ -0,0 +1,24 @@ +import logging +import sys +import warnings + +import anyio + +from mcp.server.lowlevel.server import Server +from mcp.server.stdio import stdio_server + +if not sys.warnoptions: + warnings.simplefilter("ignore") + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("server") + + +async def main() -> None: + server: Server[dict[str, object]] = Server("mcp") + async with stdio_server() as (read_stream, write_stream): + await server.run(read_stream, write_stream, server.create_initialization_options()) + + +if __name__ == "__main__": + anyio.run(main, backend="trio") diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ec4b0c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/__main__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000..f94b80e Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/__main__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/_otel.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/_otel.cpython-312.pyc new file mode 100644 index 0000000..3cf7141 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/_otel.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/_streamable_http_modern.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/_streamable_http_modern.cpython-312.pyc new file mode 100644 index 0000000..eaa547c Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/_streamable_http_modern.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/apps.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/apps.cpython-312.pyc new file mode 100644 index 0000000..ee8aa72 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/apps.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/caching.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/caching.cpython-312.pyc new file mode 100644 index 0000000..53320bb Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/caching.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/connection.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/connection.cpython-312.pyc new file mode 100644 index 0000000..0b46ffb Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/connection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/context.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/context.cpython-312.pyc new file mode 100644 index 0000000..39d46b7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/context.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/elicitation.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/elicitation.cpython-312.pyc new file mode 100644 index 0000000..9dbf86b Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/elicitation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/extension.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/extension.cpython-312.pyc new file mode 100644 index 0000000..2cf6235 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/extension.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/fastmcp.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/fastmcp.cpython-312.pyc new file mode 100644 index 0000000..ffc63e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/fastmcp.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/models.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000..2c2b4d3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/models.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/request_state.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/request_state.cpython-312.pyc new file mode 100644 index 0000000..295e1b7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/request_state.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/runner.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/runner.cpython-312.pyc new file mode 100644 index 0000000..5188318 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/runner.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/session.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/session.cpython-312.pyc new file mode 100644 index 0000000..b71323f Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/session.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/sse.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/sse.cpython-312.pyc new file mode 100644 index 0000000..ef209b6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/sse.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/stdio.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/stdio.cpython-312.pyc new file mode 100644 index 0000000..c28c16e Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/stdio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/streamable_http.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/streamable_http.cpython-312.pyc new file mode 100644 index 0000000..ac99c00 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/streamable_http.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/streamable_http_manager.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/streamable_http_manager.cpython-312.pyc new file mode 100644 index 0000000..b909ae7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/streamable_http_manager.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/subscriptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/subscriptions.cpython-312.pyc new file mode 100644 index 0000000..8ce4ef0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/subscriptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/transport_security.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/transport_security.cpython-312.pyc new file mode 100644 index 0000000..bbd63e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/transport_security.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/__pycache__/validation.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/validation.cpython-312.pyc new file mode 100644 index 0000000..675c512 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/__pycache__/validation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/_otel.py b/venv/lib/python3.12/site-packages/mcp/server/_otel.py new file mode 100644 index 0000000..ff722eb --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/_otel.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from typing import Any + +from mcp_types import INVALID_PARAMS, CallToolResult +from opentelemetry.trace import SpanKind, StatusCode +from pydantic import ValidationError + +from mcp.server.context import CallNext, HandlerResult, ServerMiddleware, ServerRequestContext +from mcp.shared._otel import extract_trace_context, otel_span +from mcp.shared.exceptions import MCPError + + +class OpenTelemetryMiddleware(ServerMiddleware[Any]): + """Context-tier middleware that wraps each inbound message in an OpenTelemetry span.""" + + async def __call__(self, ctx: ServerRequestContext[Any, Any], call_next: CallNext) -> HandlerResult: + name = ctx.params.get("name") if ctx.params else None + target = name if isinstance(name, str) else None + + attributes: dict[str, Any] = { + "mcp.method.name": ctx.method, + "mcp.protocol.version": ctx.protocol_version, + } + if ctx.request_id is not None: + attributes["jsonrpc.request.id"] = str(ctx.request_id) + + if ctx.method == "tools/call": + attributes["gen_ai.operation.name"] = "execute_tool" + if target is not None: + attributes["gen_ai.tool.name"] = target + elif ctx.method == "prompts/get" and target is not None: + attributes["gen_ai.prompt.name"] = target + + with otel_span( + name=f"{ctx.method}{f' {target}' if target else ''}", + kind=SpanKind.SERVER, + attributes=attributes, + context=extract_trace_context(ctx.meta), + record_exception=False, + set_status_on_exception=False, + ) as span: + try: + result = await call_next(ctx) + except MCPError as e: + code = str(e.error.code) + span.set_attributes({"error.type": code, "rpc.response.status_code": code}) + span.set_status(StatusCode.ERROR, e.error.message) + raise + except ValidationError: + # Mirror the sanitized wire response; pydantic messages carry client input. + code = str(INVALID_PARAMS) + span.set_attributes({"error.type": code, "rpc.response.status_code": code}) + span.set_status(StatusCode.ERROR, "Invalid request parameters") + raise + except Exception as e: + span.set_attribute("error.type", type(e).__qualname__) + span.record_exception(e) + span.set_status(StatusCode.ERROR, str(e)) + raise + if ctx.method == "tools/call": + # Tool errors are detected pre-serialization, so only shapes that reach the wire as an error + # count: the model, or the camelCase alias (`is_error` is dropped by the alias-only wire + # validation). A raw-dict `isError` is matched as a literal bool only - non-bool coercible + # values (1, "true") would serialize to an error but are rare enough to leave undetected. + match result: + case CallToolResult(is_error=True) | {"isError": True}: + span.set_attribute("error.type", "tool_error") + span.set_status(StatusCode.ERROR) + case _: + pass + return result diff --git a/venv/lib/python3.12/site-packages/mcp/server/_streamable_http_modern.py b/venv/lib/python3.12/site-packages/mcp/server/_streamable_http_modern.py new file mode 100644 index 0000000..1932a5d --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/_streamable_http_modern.py @@ -0,0 +1,516 @@ +"""Single-exchange HTTP serving for protocol version 2026-07-28. + +Private module — entry is via `StreamableHTTPSessionManager.handle_request`. +The legacy streamable-HTTP transport is untouched and remains the supported +path for earlier protocol revisions. + +A 2026-07-28 request is a self-contained POST: no `initialize` handshake, no +`Mcp-Session-Id`, one JSON-RPC request in, one JSON-RPC response out. A +notification POST is acknowledged `202` and dropped: the core protocol defines +no client-to-server notifications on this wire (cancellation is closing the +response stream), and a per-request entry has nothing for one to act on. JSON +mode handles the request directly in the ASGI task. SSE mode runs the handler +as a sibling task and defers committing to `text/event-stream` until the +handler emits a notification or `_SSE_PING_INTERVAL` elapses, whichever +comes first: a handler that completes (or raises) within that window without +emitting still gets a JSON response with the table-mapped HTTP status, so +the spec's `404`/`400` MUSTs hold for kernel-dispatch errors; a handler that +runs silent past the window commits SSE so the keepalive ping can keep the +connection open behind a proxy idle-read timeout. +""" + +from __future__ import annotations + +import json +import logging +from collections.abc import Awaitable, Mapping +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any, Final, cast + +import anyio +from anyio.streams.memory import MemoryObjectSendStream +from mcp_types import ( + CLIENT_CAPABILITIES_META_KEY, + CLIENT_INFO_META_KEY, + HEADER_MISMATCH, + INVALID_REQUEST, + PARSE_ERROR, + PROTOCOL_VERSION_META_KEY, + ErrorData, + JSONRPCError, + JSONRPCNotification, + JSONRPCRequest, + JSONRPCResponse, + ProgressToken, + RequestId, +) +from mcp_types import methods as _methods +from pydantic import ValidationError +from starlette.requests import Request +from starlette.responses import Response +from starlette.types import Receive, Scope, Send + +from mcp.server.connection import Connection +from mcp.server.runner import modern_error_data, serve_one +from mcp.server.streamable_http import check_accept_headers +from mcp.server.transport_security import TransportSecurityMiddleware, TransportSecuritySettings +from mcp.shared.dispatcher import CallOptions +from mcp.shared.exceptions import NoBackChannelError +from mcp.shared.inbound import ( + ERROR_CODE_HTTP_STATUS, + MCP_PARAM_HEADER_PREFIX, + MCP_PROTOCOL_VERSION_HEADER, + InboundLadderRejection, + InboundModernRoute, + classify_inbound_request, + find_duplicated_routing_header, + unsupported_protocol_version_rejection, + validate_mcp_param_headers, +) +from mcp.shared.jsonrpc_dispatcher import progress_token_from_params +from mcp.shared.message import MessageMetadata, ServerMessageMetadata +from mcp.shared.transport_context import TransportContext + +if TYPE_CHECKING: + from mcp.server.lowlevel.server import Server + +logger = logging.getLogger(__name__) + + +_OK_STATUS = 200 + + +@dataclass +class _SingleExchangeDispatchContext: + """`DispatchContext` for one inbound HTTP request. + + Structurally satisfies `mcp.shared.dispatcher.DispatchContext`. The + back-channel is closed by construction: a 2026-07-28 server cannot send + requests to the client. The SSE sink, when present, carries request-scoped + notifications onto this request's response stream. + """ + + transport: TransportContext + request_id: RequestId + message_metadata: MessageMetadata + progress_token: ProgressToken | None = None + sink: MemoryObjectSendStream[bytes] | None = None + cancel_requested: anyio.Event = field(default_factory=anyio.Event) + can_send_request: bool = field(default=False, init=False) + + async def send_raw_request( + self, + method: str, + params: Mapping[str, Any] | None, + opts: CallOptions | None = None, + ) -> dict[str, Any]: + raise NoBackChannelError(method) + + async def notify(self, method: str, params: Mapping[str, Any] | None, opts: CallOptions | None = None) -> None: + if self.sink is None: + return + body = dict(params) if params is not None else None + try: + await self.sink.send(_sse_event(JSONRPCNotification(jsonrpc="2.0", method=method, params=body))) + except (anyio.ClosedResourceError, anyio.BrokenResourceError): + logger.debug("dropped %s: response stream closed", method) + + async def progress(self, progress: float, total: float | None = None, message: str | None = None) -> None: + if self.progress_token is None: + return + params: dict[str, Any] = {"progressToken": self.progress_token, "progress": progress} + if total is not None: + params["total"] = total + if message is not None: + params["message"] = message + await self.notify("notifications/progress", params) + + +async def _to_jsonrpc_response( + request_id: RequestId, coro: Awaitable[dict[str, Any]] +) -> JSONRPCResponse | JSONRPCError: + """Await ``coro`` and wrap its outcome as the JSON-RPC reply for ``request_id``. + + The exception-to-wire boundary for the modern HTTP entry, composed around + `serve_one`: `modern_error_data` maps the shared ladder and surfaces + anything else as a generic `INTERNAL_ERROR` so handler internals never + reach the wire. + """ + try: + result = await coro + except Exception as exc: + return JSONRPCError(jsonrpc="2.0", id=request_id, error=modern_error_data(exc)) + return JSONRPCResponse(jsonrpc="2.0", id=request_id, result=result) + + +_SSE_PING_INTERVAL: float = 15.0 +"""Seconds between SSE comment-line keepalives once `text/event-stream` has committed.""" + +_SSE_HEADERS: Final[list[tuple[bytes, bytes]]] = [ + (b"content-type", b"text/event-stream"), + (b"cache-control", b"no-cache, no-transform"), + (b"connection", b"keep-alive"), + (b"x-accel-buffering", b"no"), +] + + +def _sse_event(msg: JSONRPCResponse | JSONRPCError | JSONRPCNotification) -> bytes: + """Serialise a JSON-RPC message as one SSE `event: message` frame. + + SSE mode begins after the handler has emitted, so a `JSONRPCError` here + always carries the request's id; the `id: null` case lives in `_write`. + """ + body = msg.model_dump(mode="json", by_alias=True, exclude_none=True) + data = json.dumps(body, separators=(",", ":")) + return f"event: message\r\ndata: {data}\r\n\r\n".encode() + + +async def _write_rejection( + rejection: InboundLadderRejection, + request_id: RequestId | None, + scope: Scope, + receive: Receive, + send: Send, +) -> None: + """Send a ladder rejection as its JSON-RPC error with the table-mapped HTTP status.""" + rej = JSONRPCError( + jsonrpc="2.0", + id=request_id, + error=ErrorData(code=rejection.code, message=rejection.message, data=rejection.data), + ) + await _write(rej, scope, receive, send) + + +async def _write( + msg: JSONRPCResponse | JSONRPCError, + scope: Scope, + receive: Receive, + send: Send, +) -> None: + """Serialise a JSON-RPC reply with the table-mapped HTTP status.""" + status = ERROR_CODE_HTTP_STATUS.get(msg.error.code, _OK_STATUS) if isinstance(msg, JSONRPCError) else _OK_STATUS + body = msg.model_dump(mode="json", by_alias=True, exclude_none=True) + if isinstance(msg, JSONRPCError) and msg.id is None: + # JSON-RPC requires `id: null` to appear on the wire when the request + # id couldn't be parsed; `exclude_none` would otherwise drop it. + body["id"] = None + await Response( + json.dumps(body, separators=(",", ":")), + status_code=status, + media_type="application/json", + )(scope, receive, send) + + +_INVALID_BODY: Final = JSONRPCError( + jsonrpc="2.0", + id=None, + error=ErrorData(code=INVALID_REQUEST, message="Body must be a single JSON-RPC request or notification object"), +) +"""Well-formed JSON that is not one request or notification: a batch, a posted response, a malformed envelope.""" + + +def _is_notification_shaped(decoded: Any) -> bool: + """Whether a decoded POST body is a single JSON object without an `id` member. + + JSON-RPC 2.0 §4.1: a notification is a request object without an "id" + member, so key presence — not which model happens to validate — picks the + arm. (The notification model ignores unknown keys; letting it catch a + request whose id is malformed would 202 a message that is owed an error.) + """ + return isinstance(decoded, dict) and "id" not in decoded + + +async def _acknowledge_notification( + decoded: dict[str, Any], + request: Request, + scope: Scope, + receive: Receive, + send: Send, +) -> None: + """Answer an id-less POST body: `202` for a notification at a served version, a rejection otherwise. + + Streamable-http §Sending Messages item 5 lets a server accept (202, no + body) or refuse (4xx) a notification POST; this entry accepts and drops. + The 2026-07-28 core protocol defines no client-to-server notifications over + HTTP (a client cancels by closing the response stream) and a per-request + entry holds no cross-request state for one to act on — honouring a posted + `notifications/cancelled` by client-chosen request id would let one + anonymous caller cancel another's work — but clients in the field still + POST them, and notifications are fire-and-forget, so they are acknowledged + as the handshake-era transport does rather than answered with an error + nobody reads. Header requirements for notification POSTs are undefined at + this revision; only the routing header that brought the POST here is + checked, so a version this entry does not serve is told so, as a request is. + """ + try: + notification = JSONRPCNotification.model_validate(decoded) + except ValidationError: + await _write(_INVALID_BODY, scope, receive, send) + return + requested = request.headers.get(MCP_PROTOCOL_VERSION_HEADER, "") + if (unsupported := unsupported_protocol_version_rejection(requested)) is not None: + await _write_rejection(unsupported, None, scope, receive, send) + return + logger.debug("acknowledged and dropped client notification %s at %s", notification.method, requested) + await Response(status_code=202)(scope, receive, send) + + +_MCP_PARAM_PREFIX_LOWER: Final = MCP_PARAM_HEADER_PREFIX.lower() + +_MCP_PARAM_LIST_PAGE_CAP: Final = 100 +"""Page cap for the schema-resolving tools/list walk: a buggy paginator degrades to a logged skip, not a hang.""" + + +async def _tool_input_schema( + app: Server[Any], + request: Request, + request_id: RequestId, + verdict: InboundModernRoute, + lifespan_state: Any, + name: str, +) -> Any | None: + """Resolve `name`'s inputSchema from the server's own registered `tools/list` handler. + + The listing runs through the normal `serve_one` path, so a visibility-scoped + catalog yields exactly what *this* caller was advertised. Returns None + (caller skips validation) when the listing fails or never advertises the tool. + """ + meta = { + PROTOCOL_VERSION_META_KEY: verdict.protocol_version, + CLIENT_CAPABILITIES_META_KEY: verdict.client_capabilities, + } + if verdict.client_info is not None: + # Optional key: a conforming pair-only caller omits it rather than sending null. + meta[CLIENT_INFO_META_KEY] = verdict.client_info + list_params: dict[str, Any] = {"_meta": meta} + try: + _methods.validate_client_request("tools/list", verdict.protocol_version, list_params) + except ValidationError: + # Client-fault envelope: the real dispatch produces the INVALID_PARAMS + # reply, and anything above a debug line would let clients flood the log. + logger.debug("Mcp-Param header validation skipped: the request envelope fails tools/list validation") + return None + seen_cursors: set[str] = set() + dctx = _SingleExchangeDispatchContext( + transport=TransportContext(kind="streamable-http", can_send_request=False, headers=request.headers), + request_id=request_id, + message_metadata=ServerMessageMetadata(request_context=request), + ) + for _ in range(_MCP_PARAM_LIST_PAGE_CAP): + # Fresh Connection per page: serve_one tears down the connection's exit stack on the way out. + connection = Connection.from_envelope( + verdict.protocol_version, verdict.client_info, verdict.client_capabilities + ) + try: + result = await serve_one( + app, dctx, "tools/list", list_params, connection=connection, lifespan_state=lifespan_state + ) + for tool in result.get("tools", []): + if tool.get("name") == name: + return tool.get("inputSchema") + cursor = result.get("nextCursor") + except Exception: + # Fail-open boundary by design: header validation must never break a + # working call path. Loud, precisely because the skip is fail-open. + logger.exception("Mcp-Param header validation skipped: the tools/list listing failed") + return None + if not isinstance(cursor, str): + # Listing exhausted without advertising `name`; dispatch owns rejecting an unknown tool. + return None + if cursor in seen_cursors: + logger.warning("Mcp-Param header validation skipped: the tools/list handler returned a cursor cycle") + return None + seen_cursors.add(cursor) + list_params = {"_meta": meta, "cursor": cursor} + logger.warning( + "Mcp-Param header validation skipped: tools/list pagination did not terminate within %d pages", + _MCP_PARAM_LIST_PAGE_CAP, + ) + return None + + +async def _mcp_param_rejection( + app: Server[Any], + request: Request, + req: JSONRPCRequest, + verdict: InboundModernRoute, + lifespan_state: Any, +) -> InboundLadderRejection | None: + """Validate a `tools/call` request's `Mcp-Param-*` headers against the called tool's schema. + + Runs pre-dispatch, before any SSE machinery, so a rejection is always a + plain `application/json` 400 (the spec's MUST). With no `tools/list` handler + the catalog is undiscoverable and there is no recognized header to validate. + """ + if req.method != "tools/call" or app.get_request_handler("tools/list") is None: + return None + params = req.params or {} + name = params.get("name") + if not isinstance(name, str): + return None + raw_arguments = params.get("arguments") + if raw_arguments is not None and not isinstance(raw_arguments, Mapping): + return None + arguments: Mapping[str, Any] = cast("Mapping[str, Any]", raw_arguments) if raw_arguments is not None else {} + # ASGI guarantees lowercase header names, so no case-folding here. + if not arguments and not any(header.startswith(_MCP_PARAM_PREFIX_LOWER) for header in request.headers): + # No argument values and no `Mcp-Param-*` headers: no declaration can be violated either way. + return None + input_schema = await _tool_input_schema(app, request, req.id, verdict, lifespan_state, name) + if input_schema is None: + return None + return validate_mcp_param_headers(input_schema, arguments, request.headers) + + +async def handle_modern_request( + app: Server[Any], + security_settings: TransportSecuritySettings | None, + json_response: bool, + lifespan_state: Any, + scope: Scope, + receive: Receive, + send: Send, +) -> None: + """ASGI handler for a single stateless-era POST. + + Called from `StreamableHTTPSessionManager.handle_request` when the + `MCP-Protocol-Version` header names a modern revision; the manager enters + `app.lifespan` once at startup and passes the state in. Never sets + `Mcp-Session-Id`. + """ + request = Request(scope, receive) + + security = TransportSecurityMiddleware(security_settings) + err = await security.validate_request(request, is_post=(request.method == "POST")) + if err is not None: + await err(scope, receive, send) + return + + if request.method != "POST": + # HTTP-layer rejection (Allow accompanies 405 per RFC 9110) — happens + # before JSON-RPC parsing, so it doesn't go through `_write`. + await Response(status_code=405, headers={"Allow": "POST"})(scope, receive, send) + return + + has_json, has_sse = check_accept_headers(request) + if not has_json or (not json_response and not has_sse): + await Response(status_code=406)(scope, receive, send) + return + + body = await request.body() + try: + decoded = json.loads(body) + except (ValueError, RecursionError): + # Not just JSONDecodeError: oversized integer literals raise bare ValueError, deep nesting RecursionError. + rej = JSONRPCError(jsonrpc="2.0", id=None, error=ErrorData(code=PARSE_ERROR, message="Parse error")) + await _write(rej, scope, receive, send) + return + if _is_notification_shaped(decoded): + await _acknowledge_notification(decoded, request, scope, receive, send) + return + try: + req = JSONRPCRequest.model_validate(decoded) + except ValidationError: + # A batch, a posted response (clients MUST NOT send those: streamable-http + # §Sending Messages item 4), or a request whose envelope is malformed. + await _write(_INVALID_BODY, scope, receive, send) + return + + if req.method == "subscriptions/listen" and not has_sse: + # A listen response IS a notification stream, never JSON (the + # json_response carve-out below), so this one method requires the + # SSE accept even in JSON-response mode; SSE mode gated it above. + await Response(status_code=406)(scope, receive, send) + return + + duplicated = find_duplicated_routing_header(request.headers.items()) + if duplicated is not None: + # The raw carrier is the only place duplicates are visible; the classifier sees a folded mapping. + rejection = InboundLadderRejection(code=HEADER_MISMATCH, message=f"{duplicated} header appears more than once") + await _write_rejection(rejection, req.id, scope, receive, send) + return + + verdict = classify_inbound_request(decoded, headers=dict(request.headers)) + if isinstance(verdict, InboundLadderRejection): + await _write_rejection(verdict, req.id, scope, receive, send) + return + + mcp_param_rejection = await _mcp_param_rejection(app, request, req, verdict, lifespan_state) + if mcp_param_rejection is not None: + await _write_rejection(mcp_param_rejection, req.id, scope, receive, send) + return + + connection = Connection.from_envelope( + verdict.protocol_version, + verdict.client_info, + verdict.client_capabilities, + ) + dctx = _SingleExchangeDispatchContext( + transport=TransportContext(kind="streamable-http", can_send_request=False, headers=request.headers), + request_id=req.id, + message_metadata=ServerMessageMetadata(request_context=request), + progress_token=progress_token_from_params(req.params), + ) + + if json_response and req.method != "subscriptions/listen": + # A listen response IS a notification stream, so it always takes the + # SSE path below regardless of the JSON-response preference (the + # TypeScript and Go SDKs route it the same way). + msg = await _to_jsonrpc_response( + req.id, serve_one(app, dctx, req.method, req.params, connection=connection, lifespan_state=lifespan_state) + ) + await _write(msg, scope, receive, send) + return + + send_ch, recv_ch = anyio.create_memory_object_stream[bytes](0) + dctx.sink = send_ch + result: list[JSONRPCResponse | JSONRPCError] = [] + + async def run_handler() -> None: + async with send_ch: + result.append( + await _to_jsonrpc_response( + req.id, + serve_one(app, dctx, req.method, req.params, connection=connection, lifespan_state=lifespan_state), + ) + ) + + async def watch_disconnect(cancel_scope: anyio.CancelScope) -> None: + while (await receive()).get("type") != "http.disconnect": + pass # pragma: no cover + cancel_scope.cancel() + + async with recv_ch, anyio.create_task_group() as tg: + tg.start_soon(run_handler) + tg.start_soon(watch_disconnect, tg.cancel_scope) + + event: bytes | None = None + done = False + with anyio.move_on_after(_SSE_PING_INTERVAL): + try: + event = await recv_ch.receive() + except anyio.EndOfStream: + done = True + + if done: + # Handler completed within the deferral window without emitting: + # `application/json` with the table-mapped status. Kernel-dispatch + # errors (METHOD_NOT_FOUND, missing-capability, INVALID_PARAMS) + # resolve here in practice. + await _write(result[0], scope, receive, send) + else: + # First notification arrived, or the deferral window elapsed: commit + # `text/event-stream` and start pinging so a proxy idle-read timeout + # cannot close the stream (which on this path cancels the handler). + await send({"type": "http.response.start", "status": _OK_STATUS, "headers": _SSE_HEADERS}) + while not done: + await send({"type": "http.response.body", "body": event or b": ping\r\n\r\n", "more_body": True}) + event = None + with anyio.move_on_after(_SSE_PING_INTERVAL): + try: + event = await recv_ch.receive() + except anyio.EndOfStream: + done = True + await send({"type": "http.response.body", "body": _sse_event(result[0]), "more_body": False}) + + tg.cancel_scope.cancel() diff --git a/venv/lib/python3.12/site-packages/mcp/server/apps.py b/venv/lib/python3.12/site-packages/mcp/server/apps.py new file mode 100644 index 0000000..583e203 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/apps.py @@ -0,0 +1,241 @@ +"""MCP Apps extension (`io.modelcontextprotocol/ui`). + +MCP Apps lets a tool carry a reference to an interactive UI: the tool's +`_meta.ui.resourceUri` points at a `ui://` resource (an HTML document served +with the `text/html;profile=mcp-app` MIME type) that the host renders in a +sandboxed iframe. See https://modelcontextprotocol.io/specification/draft/extensions/apps +and the ext-apps spec for the wire format, and SEP-2133 for the extension framework. + +This is a self-contained, additive `Extension`: it contributes tools and +resources and advertises the capability, but does not intercept any core method. +A server opts in by passing an `Apps` instance to `MCPServer(extensions=[...])`. + + apps = Apps() + + @apps.tool(resource_uri="ui://clock/app.html", description="Current time") + def get_time(ctx: Context) -> str: + return datetime.now(timezone.utc).isoformat() + + apps.add_html_resource("ui://clock/app.html", CLOCK_HTML) + + mcp = MCPServer("clock", extensions=[apps]) + +Per SEP-2133, an extension MUST degrade gracefully: a UI-enabled tool should +still return meaningful text for clients that did not negotiate Apps. Use +`client_supports_apps(ctx)` to branch on the client's advertised support. (The SDK +keeps Apps in-core under `mcp.server.apps` rather than a separate package; the +TypeScript and C# SDKs ship it as a standalone package.) +""" + +from __future__ import annotations + +from collections.abc import Callable, Sequence +from typing import Any, Literal, TypeVar + +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel + +from mcp.server.context import ServerRequestContext +from mcp.server.extension import Extension, ResourceBinding, ToolBinding +from mcp.server.mcpserver.context import Context +from mcp.server.mcpserver.resources import Resource, TextResource + +EXTENSION_ID = "io.modelcontextprotocol/ui" +"""The MCP Apps extension identifier (the shipped TS/C# constant).""" + +APP_MIME_TYPE = "text/html;profile=mcp-app" +"""MIME type for a `ui://` app resource.""" + +Visibility = Literal["model", "app"] +"""Where a UI-bound tool is surfaced (`_meta.ui.visibility`).""" + +_CallableT = TypeVar("_CallableT", bound=Callable[..., Any]) + + +class ResourcePermissions(BaseModel): + """Iframe permissions a `ui://` resource requests (`_meta.ui.permissions`).""" + + model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) + + camera: dict[str, Any] | None = None + microphone: dict[str, Any] | None = None + geolocation: dict[str, Any] | None = None + clipboard_write: dict[str, Any] | None = None + + +class ResourceCsp(BaseModel): + """Content-Security-Policy domains for a `ui://` resource (`_meta.ui.csp`).""" + + model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) + + connect_domains: list[str] | None = None + resource_domains: list[str] | None = None + frame_domains: list[str] | None = None + base_uri_domains: list[str] | None = None + + +class Apps(Extension): + """The MCP Apps extension: bind tools to `ui://` UI resources. + + Register UI-bound tools with `@apps.tool(resource_uri=...)` and their HTML + with `add_html_resource(...)`, then pass the instance to + `MCPServer(extensions=[apps])`. + """ + + identifier = EXTENSION_ID + + def __init__(self) -> None: + self._tools: list[tuple[ToolBinding, str]] = [] # (binding, bound resource_uri) + self._resources: list[ResourceBinding] = [] + + def tool( + self, + *, + resource_uri: str, + visibility: Sequence[Visibility] | None = None, + meta: dict[str, Any] | None = None, + **tool_kwargs: Any, + ) -> Callable[[_CallableT], _CallableT]: + """Decorator registering a tool bound to a `ui://` resource. + + Stamps `_meta.ui.resourceUri` (and `_meta.ui.visibility` when given) on the + tool. `tool_kwargs` are forwarded to `MCPServer.add_tool` (name, title, + description, annotations, ...); pass `meta=` to merge extra `_meta` keys + alongside the `ui` entry. + + Args: + resource_uri: The `ui://` URI of the UI resource this tool renders. + visibility: Where the tool is surfaced (`["model", "app"]`). + meta: Additional `_meta` keys to merge with the `ui` entry. + + Raises: + ValueError: If `resource_uri` does not use the `ui://` scheme, or + `meta` carries a `"ui"` key (the decorator owns `_meta["ui"]`). + """ + _require_ui_scheme(resource_uri) + if meta and "ui" in meta: + raise ValueError("Apps.tool() owns _meta['ui']; pass resource_uri=/visibility= instead of a 'ui' meta key") + ui: dict[str, Any] = {"resourceUri": resource_uri} + if visibility is not None: + ui["visibility"] = list(visibility) + + def decorator(fn: _CallableT) -> _CallableT: + binding = ToolBinding(fn=fn, meta={**(meta or {}), "ui": ui}, kwargs=tool_kwargs) + self._tools.append((binding, resource_uri)) + return fn + + return decorator + + def add_html_resource( + self, + uri: str, + html: str, + *, + name: str | None = None, + title: str | None = None, + description: str | None = None, + csp: ResourceCsp | None = None, + permissions: ResourcePermissions | None = None, + domain: str | None = None, + prefers_border: bool | None = None, + ) -> None: + """Register a `ui://` HTML resource served as `text/html;profile=mcp-app`. + + `csp`, `permissions`, `domain`, and `prefers_border` populate the + resource's `_meta.ui` per the ext-apps spec. + + Args: + uri: The `ui://` URI; a tool references it via `resource_uri`. + html: The HTML document the host renders. + + Raises: + ValueError: If `uri` does not use the `ui://` scheme. + """ + ui: dict[str, Any] = {} + if csp is not None: + ui["csp"] = csp.model_dump(by_alias=True, exclude_none=True) + if permissions is not None: + ui["permissions"] = permissions.model_dump(by_alias=True, exclude_none=True) + if domain is not None: + ui["domain"] = domain + if prefers_border is not None: + ui["prefersBorder"] = prefers_border + self.add_resource( + TextResource( + uri=uri, + name=name or uri, + title=title, + description=description, + mime_type=APP_MIME_TYPE, + meta={"ui": ui} if ui else None, + text=html, + ) + ) + + def add_resource(self, resource: Resource) -> None: + """Register a pre-built `ui://` resource. + + The escape hatch for resources `add_html_resource` cannot express (e.g. a + `FileResource` serving HTML from disk). A resource without an explicit + `mime_type` is served as `text/html;profile=mcp-app` — hosts will not + render a `ui://` resource under any other MIME type, so an explicit + mismatch is rejected. + + Raises: + ValueError: If the resource URI does not use the `ui://` scheme, or + its explicit `mime_type` is not `text/html;profile=mcp-app`. + """ + _require_ui_scheme(resource.uri) + if "mime_type" not in resource.model_fields_set: + resource = resource.model_copy(update={"mime_type": APP_MIME_TYPE}) + elif resource.mime_type != APP_MIME_TYPE: + raise ValueError(f"MCP Apps resources are served as {APP_MIME_TYPE!r}, got {resource.mime_type!r}") + self._resources.append(ResourceBinding(resource=resource)) + + def tools(self) -> Sequence[ToolBinding]: + """The bound tools. + + Raises: + ValueError: If a tool's `resource_uri` has no matching resource + registered on this instance — a tool advertising a + `_meta.ui.resourceUri` that 404s on `resources/read` is a + misconfiguration, caught when the server consumes the extension. + """ + registered = {binding.resource.uri for binding in self._resources} + for tool, uri in self._tools: + if uri not in registered: + raise ValueError( + f"Apps tool {tool.fn.__name__!r} binds resource_uri {uri!r}, but no such resource " + "is registered; add it with add_html_resource() or add_resource()" + ) + return [tool for tool, _ in self._tools] + + def resources(self) -> Sequence[ResourceBinding]: + return self._resources + + +def client_supports_apps(ctx: Context[Any] | ServerRequestContext[Any, Any]) -> bool: + """Whether the connected client negotiated MCP Apps support. + + Returns `True` only when the client advertised the extension AND listed the + `text/html;profile=mcp-app` MIME type in its settings, so a UI-enabled tool + can fall back to text-only output otherwise. + """ + capabilities = _client_capabilities(ctx) + extensions = capabilities.extensions if capabilities else None + settings = extensions.get(EXTENSION_ID) if extensions else None + if settings is None: + return False + mime_types = settings.get("mimeTypes") + return isinstance(mime_types, list | tuple) and APP_MIME_TYPE in mime_types + + +def _client_capabilities(ctx: Context[Any] | ServerRequestContext[Any, Any]) -> Any: + if isinstance(ctx, Context): + return ctx.client_capabilities + return ctx.session.client_capabilities + + +def _require_ui_scheme(uri: str) -> None: + if not uri.startswith("ui://"): + raise ValueError(f"MCP Apps URIs must use the ui:// scheme, got {uri!r}") diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/__init__.py b/venv/lib/python3.12/site-packages/mcp/server/auth/__init__.py new file mode 100644 index 0000000..61b60e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/__init__.py @@ -0,0 +1 @@ +"""MCP OAuth server authorization components.""" diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..70a99f3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/errors.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/errors.cpython-312.pyc new file mode 100644 index 0000000..5b2f44f Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/errors.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/json_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/json_response.cpython-312.pyc new file mode 100644 index 0000000..f341445 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/json_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/provider.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/provider.cpython-312.pyc new file mode 100644 index 0000000..c8ab2bb Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/provider.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/routes.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/routes.cpython-312.pyc new file mode 100644 index 0000000..5232f1f Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/routes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/settings.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/settings.cpython-312.pyc new file mode 100644 index 0000000..6d69987 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/__pycache__/settings.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/errors.py b/venv/lib/python3.12/site-packages/mcp/server/auth/errors.py new file mode 100644 index 0000000..117deea --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/errors.py @@ -0,0 +1,5 @@ +from pydantic import ValidationError + + +def stringify_pydantic_error(validation_error: ValidationError) -> str: + return "\n".join(f"{'.'.join(str(loc) for loc in e['loc'])}: {e['msg']}" for e in validation_error.errors()) diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__init__.py b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__init__.py new file mode 100644 index 0000000..fd8a462 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__init__.py @@ -0,0 +1 @@ +"""Request handlers for MCP authorization endpoints.""" diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1568ac3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/authorize.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/authorize.cpython-312.pyc new file mode 100644 index 0000000..c81a548 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/authorize.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/metadata.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/metadata.cpython-312.pyc new file mode 100644 index 0000000..3b0efc3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/metadata.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/register.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/register.cpython-312.pyc new file mode 100644 index 0000000..fc8bbe7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/register.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/revoke.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/revoke.cpython-312.pyc new file mode 100644 index 0000000..6811fe9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/revoke.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/token.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/token.cpython-312.pyc new file mode 100644 index 0000000..20939ba Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/__pycache__/token.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/authorize.py b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/authorize.py new file mode 100644 index 0000000..5cf93cf --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/authorize.py @@ -0,0 +1,225 @@ +import logging +from dataclasses import dataclass +from typing import Any, Literal + +# TODO(Marcelo): We should drop the `RootModel`. +from pydantic import AnyUrl, BaseModel, Field, RootModel, ValidationError # noqa: TID251 +from starlette.datastructures import FormData, QueryParams +from starlette.requests import Request +from starlette.responses import RedirectResponse, Response + +from mcp.server.auth.errors import stringify_pydantic_error +from mcp.server.auth.json_response import PydanticJSONResponse +from mcp.server.auth.provider import ( + AuthorizationErrorCode, + AuthorizationParams, + AuthorizeError, + OAuthAuthorizationServerProvider, + construct_redirect_uri, +) +from mcp.shared.auth import InvalidRedirectUriError, InvalidScopeError + +logger = logging.getLogger(__name__) + + +class AuthorizationRequest(BaseModel): + # See https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1 + client_id: str = Field(..., description="The client ID") + redirect_uri: AnyUrl | None = Field(None, description="URL to redirect to after authorization") + + # see OAuthClientMetadata; we only support `code` + response_type: Literal["code"] = Field(..., description="Must be 'code' for authorization code flow") + code_challenge: str = Field(..., description="PKCE code challenge") + code_challenge_method: Literal["S256"] = Field("S256", description="PKCE code challenge method, must be S256") + state: str | None = Field(None, description="Optional state parameter") + scope: str | None = Field( + None, + description="Optional scope; if specified, should be a space-separated list of scope strings", + ) + resource: str | None = Field( + None, + description="RFC 8707 resource indicator - the MCP server this token will be used with", + ) + + +class AuthorizationErrorResponse(BaseModel): + error: AuthorizationErrorCode + error_description: str | None + error_uri: AnyUrl | None = None + # must be set if provided in the request + state: str | None = None + + +def best_effort_extract_string(key: str, params: None | FormData | QueryParams) -> str | None: + if params is None: # pragma: no cover + return None + value = params.get(key) + if isinstance(value, str): + return value + return None + + +class AnyUrlModel(RootModel[AnyUrl]): + root: AnyUrl + + +@dataclass +class AuthorizationHandler: + provider: OAuthAuthorizationServerProvider[Any, Any, Any] + + async def handle(self, request: Request) -> Response: + # implements authorization requests for grant_type=code; + # see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1 + + state = None + redirect_uri = None + client = None + params = None + + async def error_response( + error: AuthorizationErrorCode, + error_description: str | None, + attempt_load_client: bool = True, + ): + # Error responses take two different formats: + # 1. The request has a valid client ID & redirect_uri: we issue a redirect + # back to the redirect_uri with the error response fields as query + # parameters. This allows the client to be notified of the error. + # 2. Otherwise, we return an error response directly to the end user; + # we choose to do so in JSON, but this is left undefined in the + # specification. + # See https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1 + # + # This logic is a bit awkward to handle, because the error might be thrown + # very early in request validation, before we've done the usual Pydantic + # validation, loaded the client, etc. To handle this, error_response() + # contains fallback logic which attempts to load the parameters directly + # from the request. + + nonlocal client, redirect_uri, state + if client is None and attempt_load_client: + # make last-ditch attempt to load the client + client_id = best_effort_extract_string("client_id", params) + client = await self.provider.get_client(client_id) if client_id else None + if redirect_uri is None and client: + # make last-ditch effort to load the redirect uri + try: + if params is not None and "redirect_uri" not in params: + raw_redirect_uri = None + else: + raw_redirect_uri = AnyUrlModel.model_validate( + best_effort_extract_string("redirect_uri", params) + ).root + redirect_uri = client.validate_redirect_uri(raw_redirect_uri) + except (ValidationError, InvalidRedirectUriError): + # if the redirect URI is invalid, ignore it & just return the + # initial error + pass + + # the error response MUST contain the state specified by the client, if any + if state is None: + # make last-ditch effort to load state + state = best_effort_extract_string("state", params) + + error_resp = AuthorizationErrorResponse( + error=error, + error_description=error_description, + state=state, + ) + + if redirect_uri and client: + return RedirectResponse( + url=construct_redirect_uri(str(redirect_uri), **error_resp.model_dump(exclude_none=True)), + status_code=302, + headers={"Cache-Control": "no-store"}, + ) + else: + return PydanticJSONResponse( + status_code=400, + content=error_resp, + headers={"Cache-Control": "no-store"}, + ) + + try: + # Parse request parameters + if request.method == "GET": + # Convert query_params to dict for pydantic validation + params = request.query_params + else: + # Parse form data for POST requests + params = await request.form() + + # Save state if it exists, even before validation + state = best_effort_extract_string("state", params) + + try: + auth_request = AuthorizationRequest.model_validate(params) + state = auth_request.state # Update with validated state + except ValidationError as validation_error: + error: AuthorizationErrorCode = "invalid_request" + for e in validation_error.errors(): + if e["loc"] == ("response_type",) and e["type"] == "literal_error": + error = "unsupported_response_type" + break + return await error_response(error, stringify_pydantic_error(validation_error)) + + # Get client information + client = await self.provider.get_client( + auth_request.client_id, + ) + if not client: + # For client_id validation errors, return direct error (no redirect) + return await error_response( + error="invalid_request", + error_description=f"Client ID '{auth_request.client_id}' not found", + attempt_load_client=False, + ) + + # Validate redirect_uri against client's registered URIs + try: + redirect_uri = client.validate_redirect_uri(auth_request.redirect_uri) + except InvalidRedirectUriError as validation_error: + # For redirect_uri validation errors, return direct error (no redirect) + return await error_response( + error="invalid_request", + error_description=validation_error.message, + ) + + # Validate scope - for scope errors, we can redirect + try: + scopes = client.validate_scope(auth_request.scope) + except InvalidScopeError as validation_error: + # For scope errors, redirect with error parameters + return await error_response( + error="invalid_scope", + error_description=validation_error.message, + ) + + # Setup authorization parameters + auth_params = AuthorizationParams( + state=state, + scopes=scopes, + code_challenge=auth_request.code_challenge, + redirect_uri=redirect_uri, + redirect_uri_provided_explicitly=auth_request.redirect_uri is not None, + resource=auth_request.resource, # RFC 8707 + ) + + try: + # Let the provider pick the next URI to redirect to + return RedirectResponse( + url=await self.provider.authorize( + client, + auth_params, + ), + status_code=302, + headers={"Cache-Control": "no-store"}, + ) + except AuthorizeError as e: + # Handle authorization errors as defined in RFC 6749 Section 4.1.2.1 + return await error_response(error=e.error, error_description=e.error_description) + + except Exception as validation_error: # pragma: no cover + # Catch-all for unexpected errors + logger.exception("Unexpected error in authorization_handler", exc_info=validation_error) + return await error_response(error="server_error", error_description="An unexpected error occurred") diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/metadata.py b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/metadata.py new file mode 100644 index 0000000..f126442 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/metadata.py @@ -0,0 +1,29 @@ +from dataclasses import dataclass + +from starlette.requests import Request +from starlette.responses import Response + +from mcp.server.auth.json_response import PydanticJSONResponse +from mcp.shared.auth import OAuthMetadata, ProtectedResourceMetadata + + +@dataclass +class MetadataHandler: + metadata: OAuthMetadata + + async def handle(self, request: Request) -> Response: + return PydanticJSONResponse( + content=self.metadata, + headers={"Cache-Control": "public, max-age=3600"}, # Cache for 1 hour + ) + + +@dataclass +class ProtectedResourceMetadataHandler: + metadata: ProtectedResourceMetadata + + async def handle(self, request: Request) -> Response: + return PydanticJSONResponse( + content=self.metadata, + headers={"Cache-Control": "public, max-age=3600"}, # Cache for 1 hour + ) diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/register.py b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/register.py new file mode 100644 index 0000000..7fb14b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/register.py @@ -0,0 +1,154 @@ +import secrets +import time +from dataclasses import dataclass +from typing import Any +from uuid import uuid4 + +from pydantic import BaseModel, ValidationError +from starlette.requests import Request +from starlette.responses import Response + +from mcp.server.auth.errors import stringify_pydantic_error +from mcp.server.auth.json_response import PydanticJSONResponse +from mcp.server.auth.provider import OAuthAuthorizationServerProvider, RegistrationError, RegistrationErrorCode +from mcp.server.auth.settings import ClientRegistrationOptions +from mcp.shared.auth import JWT_BEARER_GRANT_TYPE, OAuthClientInformationFull, OAuthClientMetadata + +# this alias is a no-op; it's just to separate out the types exposed to the +# provider from what we use in the HTTP handler +RegistrationRequest = OAuthClientMetadata + + +class RegistrationErrorResponse(BaseModel): + error: RegistrationErrorCode + error_description: str | None + + +@dataclass +class RegistrationHandler: + provider: OAuthAuthorizationServerProvider[Any, Any, Any] + options: ClientRegistrationOptions + + async def handle(self, request: Request) -> Response: + # Implements dynamic client registration as defined in https://datatracker.ietf.org/doc/html/rfc7591#section-3.1 + try: + body = await request.body() + client_metadata = OAuthClientMetadata.model_validate_json(body) + + # Scope validation is handled below + except ValidationError as validation_error: + return PydanticJSONResponse( + content=RegistrationErrorResponse( + error="invalid_client_metadata", + error_description=stringify_pydantic_error(validation_error), + ), + status_code=400, + ) + + client_id = str(uuid4()) + + # If auth method is None, default to client_secret_post + if client_metadata.token_endpoint_auth_method is None: + client_metadata.token_endpoint_auth_method = "client_secret_post" + # This server authenticates token requests with the client secret it mints; it holds + # no client key to verify a private_key_jwt assertion, so confirming that method would + # register a client whose every token request is then rejected. Refuse it instead + # (RFC 7591 §3.2.2), before minting credentials the client could never use. + if client_metadata.token_endpoint_auth_method == "private_key_jwt": + return PydanticJSONResponse( + content=RegistrationErrorResponse( + error="invalid_client_metadata", + error_description="token_endpoint_auth_method 'private_key_jwt' is not supported", + ), + status_code=400, + ) + + client_secret = None + if client_metadata.token_endpoint_auth_method != "none": # pragma: no branch + # cryptographically secure random 32-byte hex string + client_secret = secrets.token_hex(32) + + if client_metadata.scope is None and self.options.default_scopes is not None: + client_metadata.scope = " ".join(self.options.default_scopes) + elif client_metadata.scope is not None and self.options.valid_scopes is not None: + requested_scopes = set(client_metadata.scope.split()) + valid_scopes = set(self.options.valid_scopes) + if not requested_scopes.issubset(valid_scopes): # pragma: no branch + return PydanticJSONResponse( + content=RegistrationErrorResponse( + error="invalid_client_metadata", + error_description="Requested scopes are not valid: " + f"{', '.join(requested_scopes - valid_scopes)}", + ), + status_code=400, + ) + if "authorization_code" not in client_metadata.grant_types: + return PydanticJSONResponse( + content=RegistrationErrorResponse( + error="invalid_client_metadata", + error_description="grant_types must include 'authorization_code'", + ), + status_code=400, + ) + + # SEP-990 §5.1 / draft-ietf-oauth-identity-assertion-authz-grant §8.1: the ID-JAG flow is + # for confidential clients provisioned out of band. Refuse to grant it through DCR so a + # self-registered client cannot reach the identity-assertion provider hook. + if JWT_BEARER_GRANT_TYPE in client_metadata.grant_types: + return PydanticJSONResponse( + content=RegistrationErrorResponse( + error="invalid_client_metadata", + error_description=( + f"grant_types must not include '{JWT_BEARER_GRANT_TYPE}'; " + "the identity-assertion grant requires a pre-registered client" + ), + ), + status_code=400, + ) + + # The MCP spec requires servers to use the authorization `code` flow + # with PKCE + if "code" not in client_metadata.response_types: + return PydanticJSONResponse( + content=RegistrationErrorResponse( + error="invalid_client_metadata", + error_description="response_types must include 'code' for authorization_code grant", + ), + status_code=400, + ) + + client_id_issued_at = int(time.time()) + # RFC 7591 §3.2.1: client_secret_expires_at is REQUIRED whenever a client_secret is + # issued, with 0 (not omission) meaning it never expires; a public client gets none. + client_secret_expires_at = None + if client_secret is not None: + client_secret_expires_at = ( + client_id_issued_at + self.options.client_secret_expiry_seconds + if self.options.client_secret_expiry_seconds is not None + else 0 + ) + + # RFC 7591 §3.2.1: the response returns all registered metadata about the client, so + # the record is the whole validated request plus the credentials minted here - built + # from the request's dump so no metadata field can be silently omitted from the echo. + client_info = OAuthClientInformationFull.model_validate( + { + **client_metadata.model_dump(), + "client_id": client_id, + "client_id_issued_at": client_id_issued_at, + "client_secret": client_secret, + "client_secret_expires_at": client_secret_expires_at, + } + ) + try: + # Register client + await self.provider.register_client(client_info) + + # Return client information + return PydanticJSONResponse(content=client_info, status_code=201) + except RegistrationError as e: + # Handle registration errors as defined in RFC 7591 Section 3.2.2 + return PydanticJSONResponse( + content=RegistrationErrorResponse(error=e.error, error_description=e.error_description), + status_code=400, + ) diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/revoke.py b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/revoke.py new file mode 100644 index 0000000..4efd154 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/revoke.py @@ -0,0 +1,87 @@ +from dataclasses import dataclass +from functools import partial +from typing import Any, Literal + +from pydantic import BaseModel, ValidationError +from starlette.requests import Request +from starlette.responses import Response + +from mcp.server.auth.errors import ( + stringify_pydantic_error, +) +from mcp.server.auth.json_response import PydanticJSONResponse +from mcp.server.auth.middleware.client_auth import AuthenticationError, ClientAuthenticator +from mcp.server.auth.provider import AccessToken, OAuthAuthorizationServerProvider, RefreshToken + + +class RevocationRequest(BaseModel): + """See https://datatracker.ietf.org/doc/html/rfc7009#section-2.1""" + + token: str + token_type_hint: Literal["access_token", "refresh_token"] | None = None + client_id: str + client_secret: str | None + + +class RevocationErrorResponse(BaseModel): + error: Literal["invalid_request", "unauthorized_client"] + error_description: str | None = None + + +@dataclass +class RevocationHandler: + provider: OAuthAuthorizationServerProvider[Any, Any, Any] + client_authenticator: ClientAuthenticator + + async def handle(self, request: Request) -> Response: + """Handler for the OAuth 2.0 Token Revocation endpoint.""" + try: + client = await self.client_authenticator.authenticate_request(request) + except AuthenticationError as e: # pragma: no cover + return PydanticJSONResponse( + status_code=401, + content=RevocationErrorResponse( + error="unauthorized_client", + error_description=e.message, + ), + ) + + try: + form_data = await request.form() + revocation_request = RevocationRequest.model_validate(dict(form_data)) + except ValidationError as e: + return PydanticJSONResponse( + status_code=400, + content=RevocationErrorResponse( + error="invalid_request", + error_description=stringify_pydantic_error(e), + ), + ) + + loaders = [ + self.provider.load_access_token, + partial(self.provider.load_refresh_token, client), + ] + if revocation_request.token_type_hint == "refresh_token": # pragma: no cover + loaders = reversed(loaders) + + token: None | AccessToken | RefreshToken = None + for loader in loaders: + token = await loader(revocation_request.token) + if token is not None: + break + + # if token is not found, just return HTTP 200 per the RFC + if token and token.client_id == client.client_id: + # Revoke token; provider is not meant to be able to do validation + # at this point that would result in an error + await self.provider.revoke_token(token) + + # Return successful empty response + return Response( + status_code=200, + headers={ + "Cache-Control": "no-store", + "Pragma": "no-cache", + }, + ) diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/token.py b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/token.py new file mode 100644 index 0000000..0e644c3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/handlers/token.py @@ -0,0 +1,275 @@ +import base64 +import hashlib +import time +from dataclasses import dataclass +from typing import Annotated, Any, Literal + +from pydantic import AnyHttpUrl, AnyUrl, BaseModel, Field, TypeAdapter, ValidationError +from starlette.requests import Request + +from mcp.server.auth.errors import stringify_pydantic_error +from mcp.server.auth.json_response import PydanticJSONResponse +from mcp.server.auth.middleware.client_auth import AuthenticationError, ClientAuthenticator +from mcp.server.auth.provider import ( + IdentityAssertionParams, + OAuthAuthorizationServerProvider, + TokenError, + TokenErrorCode, +) +from mcp.shared.auth import OAuthToken + + +class AuthorizationCodeRequest(BaseModel): + # See https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3 + grant_type: Literal["authorization_code"] + code: str = Field(..., description="The authorization code") + redirect_uri: AnyUrl | None = Field(None, description="Must be the same as redirect URI provided in /authorize") + client_id: str + # we use the client_secret param, per https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1 + client_secret: str | None = None + # See https://datatracker.ietf.org/doc/html/rfc7636#section-4.5 + code_verifier: str = Field(..., description="PKCE code verifier") + # RFC 8707 resource indicator + resource: str | None = Field(None, description="Resource indicator for the token") + + +class RefreshTokenRequest(BaseModel): + # See https://datatracker.ietf.org/doc/html/rfc6749#section-6 + grant_type: Literal["refresh_token"] + refresh_token: str = Field(..., description="The refresh token") + scope: str | None = Field(None, description="Optional scope parameter") + client_id: str + # we use the client_secret param, per https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1 + client_secret: str | None = None + # RFC 8707 resource indicator + resource: str | None = Field(None, description="Resource indicator for the token") + + +class JwtBearerRequest(BaseModel): + # RFC 7523 §2.1 JWT bearer authorization grant. SEP-990 leg 2: the client presents the + # enterprise IdP-issued ID-JAG to the MCP authorization server as the `assertion`. + grant_type: Literal["urn:ietf:params:oauth:grant-type:jwt-bearer"] + # See https://datatracker.ietf.org/doc/html/rfc7523#section-2.1 + assertion: str = Field(..., description="The ID-JAG (a signed JWT) being presented as the grant") + scope: str | None = Field(None, description="Optional scope parameter") + client_id: str + # we use the client_secret param, per https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1 + client_secret: str | None = None + # RFC 8707 resource indicator + resource: str | None = Field(None, description="Resource indicator for the token") + + +TokenRequest = Annotated[ + AuthorizationCodeRequest | RefreshTokenRequest | JwtBearerRequest, + Field(discriminator="grant_type"), +] +token_request_adapter = TypeAdapter[TokenRequest](TokenRequest) + + +class TokenErrorResponse(BaseModel): + """See https://datatracker.ietf.org/doc/html/rfc6749#section-5.2""" + + error: TokenErrorCode + error_description: str | None = None + error_uri: AnyHttpUrl | None = None + + +# this is just an alias over OAuthToken; the only reason we do this +# is to have some separation between the HTTP response type, and the +# type returned by the provider +TokenSuccessResponse = OAuthToken + + +@dataclass +class TokenHandler: + provider: OAuthAuthorizationServerProvider[Any, Any, Any] + client_authenticator: ClientAuthenticator + identity_assertion_enabled: bool = False + + def response(self, obj: TokenSuccessResponse | TokenErrorResponse): + status_code = 200 + if isinstance(obj, TokenErrorResponse): + status_code = 400 + + return PydanticJSONResponse( + content=obj, + status_code=status_code, + headers={ + "Cache-Control": "no-store", + "Pragma": "no-cache", + }, + ) + + async def handle(self, request: Request): + try: + client_info = await self.client_authenticator.authenticate_request(request) + except AuthenticationError as e: + # Authentication failures should return 401 + return PydanticJSONResponse( + content=TokenErrorResponse( + error="invalid_client", + error_description=e.message, + ), + status_code=401, + headers={ + "Cache-Control": "no-store", + "Pragma": "no-cache", + }, + ) + + try: + form_data = await request.form() + # TODO(Marcelo): Can someone check if this `dict()` wrapper is necessary? + token_request = token_request_adapter.validate_python(dict(form_data)) + except ValidationError as validation_error: + return self.response( + TokenErrorResponse( + error="invalid_request", + error_description=stringify_pydantic_error(validation_error), + ) + ) + + if token_request.grant_type not in client_info.grant_types: + return self.response( + TokenErrorResponse( + error="unsupported_grant_type", + error_description=(f"Unsupported grant type (supported grant types are {client_info.grant_types})"), + ) + ) + + tokens: OAuthToken + + match token_request: + case AuthorizationCodeRequest(): + auth_code = await self.provider.load_authorization_code(client_info, token_request.code) + if auth_code is None or auth_code.client_id != token_request.client_id: + # if code belongs to different client, pretend it doesn't exist + return self.response( + TokenErrorResponse( + error="invalid_grant", + error_description="authorization code does not exist", + ) + ) + + # make auth codes expire after a deadline + # see https://datatracker.ietf.org/doc/html/rfc6749#section-10.5 + if auth_code.expires_at < time.time(): + return self.response( + TokenErrorResponse( + error="invalid_grant", + error_description="authorization code has expired", + ) + ) + + # verify redirect_uri doesn't change between /authorize and /tokens + # see https://datatracker.ietf.org/doc/html/rfc6749#section-10.6 + if auth_code.redirect_uri_provided_explicitly: + authorize_request_redirect_uri = auth_code.redirect_uri + else: # pragma: no cover + authorize_request_redirect_uri = None + + # Convert both sides to strings for comparison to handle AnyUrl vs string issues + token_redirect_str = str(token_request.redirect_uri) if token_request.redirect_uri is not None else None + auth_redirect_str = ( + str(authorize_request_redirect_uri) if authorize_request_redirect_uri is not None else None + ) + + if token_redirect_str != auth_redirect_str: + return self.response( + TokenErrorResponse( + error="invalid_request", + error_description=("redirect_uri did not match the one used when creating auth code"), + ) + ) + + # Verify PKCE code verifier + sha256 = hashlib.sha256(token_request.code_verifier.encode()).digest() + hashed_code_verifier = base64.urlsafe_b64encode(sha256).decode().rstrip("=") + + if hashed_code_verifier != auth_code.code_challenge: + # see https://datatracker.ietf.org/doc/html/rfc7636#section-4.6 + return self.response( + TokenErrorResponse( + error="invalid_grant", + error_description="incorrect code_verifier", + ) + ) + + try: + # Exchange authorization code for tokens + tokens = await self.provider.exchange_authorization_code(client_info, auth_code) + except TokenError as e: + return self.response(TokenErrorResponse(error=e.error, error_description=e.error_description)) + + case RefreshTokenRequest(): + refresh_token = await self.provider.load_refresh_token(client_info, token_request.refresh_token) + if refresh_token is None or refresh_token.client_id != token_request.client_id: + # if token belongs to different client, pretend it doesn't exist + return self.response( + TokenErrorResponse( + error="invalid_grant", + error_description="refresh token does not exist", + ) + ) + + if refresh_token.expires_at and refresh_token.expires_at < time.time(): + # if the refresh token has expired, pretend it doesn't exist + return self.response( + TokenErrorResponse( + error="invalid_grant", + error_description="refresh token has expired", + ) + ) + + # Parse scopes if provided + scopes = token_request.scope.split(" ") if token_request.scope else refresh_token.scopes + + for scope in scopes: + if scope not in refresh_token.scopes: + return self.response( + TokenErrorResponse( + error="invalid_scope", + error_description=(f"cannot request scope `{scope}` not provided by refresh token"), + ) + ) + + try: + # Exchange refresh token for new tokens + tokens = await self.provider.exchange_refresh_token(client_info, refresh_token, scopes) + except TokenError as e: + return self.response(TokenErrorResponse(error=e.error, error_description=e.error_description)) + + case JwtBearerRequest(): # pragma: no branch + if not self.identity_assertion_enabled: + return self.response( + TokenErrorResponse( + error="unsupported_grant_type", + error_description="The JWT bearer grant is not supported by this authorization server", + ) + ) + + # SEP-990 §5.1: only confidential clients may present an ID-JAG. ClientAuthenticator + # already rejects a secret-based method with no stored secret; this additionally + # rejects the public `none` method so an unauthenticated client never reaches the + # provider hook. + if not client_info.client_secret: + # RFC 6749 §5.2: the client authenticated but is not permitted this grant, so + # unauthorized_client (not invalid_client, which is for failed authentication). + return self.response( + TokenErrorResponse( + error="unauthorized_client", + error_description="The JWT bearer grant requires a confidential client", + ) + ) + + params = IdentityAssertionParams( + assertion=token_request.assertion, + scopes=token_request.scope.split(" ") if token_request.scope else None, + resource=token_request.resource, + ) + try: + tokens = await self.provider.exchange_identity_assertion(client_info, params) + except TokenError as e: + return self.response(TokenErrorResponse(error=e.error, error_description=e.error_description)) + + return self.response(tokens) diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/json_response.py b/venv/lib/python3.12/site-packages/mcp/server/auth/json_response.py new file mode 100644 index 0000000..bd95bd6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/json_response.py @@ -0,0 +1,10 @@ +from typing import Any + +from starlette.responses import JSONResponse + + +class PydanticJSONResponse(JSONResponse): + # use pydantic json serialization instead of the stock `json.dumps`, + # so that we can handle serializing pydantic models like AnyHttpUrl + def render(self, content: Any) -> bytes: + return content.model_dump_json(exclude_none=True).encode("utf-8") diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__init__.py b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__init__.py new file mode 100644 index 0000000..ab07d84 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__init__.py @@ -0,0 +1 @@ +"""Middleware for MCP authorization.""" diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4f1c8a4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__pycache__/auth_context.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__pycache__/auth_context.cpython-312.pyc new file mode 100644 index 0000000..2e8a318 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__pycache__/auth_context.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__pycache__/bearer_auth.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__pycache__/bearer_auth.cpython-312.pyc new file mode 100644 index 0000000..c5ba1b9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__pycache__/bearer_auth.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__pycache__/client_auth.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__pycache__/client_auth.cpython-312.pyc new file mode 100644 index 0000000..d0ecfab Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/__pycache__/client_auth.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/auth_context.py b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/auth_context.py new file mode 100644 index 0000000..1d34a55 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/auth_context.py @@ -0,0 +1,46 @@ +import contextvars + +from starlette.types import ASGIApp, Receive, Scope, Send + +from mcp.server.auth.middleware.bearer_auth import AuthenticatedUser +from mcp.server.auth.provider import AccessToken + +# Create a contextvar to store the authenticated user +# The default is None, indicating no authenticated user is present +auth_context_var = contextvars.ContextVar[AuthenticatedUser | None]("auth_context", default=None) + + +def get_access_token() -> AccessToken | None: + """Get the access token from the current context. + + Returns: + The access token if an authenticated user is available, None otherwise. + """ + auth_user = auth_context_var.get() + return auth_user.access_token if auth_user else None + + +class AuthContextMiddleware: + """Middleware that extracts the authenticated user from the request + and sets it in a contextvar for easy access throughout the request lifecycle. + + This middleware should be added after the AuthenticationMiddleware in the + middleware stack to ensure that the user is properly authenticated before + being stored in the context. + """ + + def __init__(self, app: ASGIApp): + self.app = app + + async def __call__(self, scope: Scope, receive: Receive, send: Send): + user = scope.get("user") + if isinstance(user, AuthenticatedUser): + # Set the authenticated user in the contextvar + token = auth_context_var.set(user) + try: + await self.app(scope, receive, send) + finally: + auth_context_var.reset(token) + else: + # No authenticated user, just process the request + await self.app(scope, receive, send) diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/bearer_auth.py b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/bearer_auth.py new file mode 100644 index 0000000..29413ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/bearer_auth.py @@ -0,0 +1,143 @@ +import json +import time +from typing import Any, TypedDict + +from pydantic import AnyHttpUrl +from starlette.authentication import AuthCredentials, AuthenticationBackend, SimpleUser +from starlette.requests import HTTPConnection +from starlette.types import Receive, Scope, Send + +from mcp.server.auth.provider import AccessToken, TokenVerifier, principal_components + + +class AuthenticatedUser(SimpleUser): + """User with authentication info.""" + + def __init__(self, auth_info: AccessToken): + super().__init__(auth_info.client_id) + self.access_token = auth_info + self.scopes = auth_info.scopes + + +class AuthorizationContext(TypedDict): + client_id: str + issuer: str | None + subject: str | None + + +def authorization_context(user: AuthenticatedUser) -> AuthorizationContext: + """Identify the principal `user` represents, for transports to compare + against the principal that created a session. Components the token + verifier does not supply are `None`, so the comparison degrades to the + remaining components. + + See `examples/servers/simple-auth/mcp_simple_auth/token_verifier.py` for + a verifier that populates `subject` and `claims` from an introspection + response.""" + client_id, issuer, subject = principal_components(user.access_token) + return AuthorizationContext(client_id=client_id, issuer=issuer, subject=subject) + + +class BearerAuthBackend(AuthenticationBackend): + """Authentication backend that validates Bearer tokens using a TokenVerifier.""" + + def __init__(self, token_verifier: TokenVerifier): + self.token_verifier = token_verifier + + async def authenticate(self, conn: HTTPConnection): + auth_header = next( + (conn.headers.get(key) for key in conn.headers if key.lower() == "authorization"), + None, + ) + if not auth_header or not auth_header.lower().startswith("bearer "): + return None + + token = auth_header[7:] # Remove "Bearer " prefix + + # Validate the token with the verifier + auth_info = await self.token_verifier.verify_token(token) + + if not auth_info: + return None + + if auth_info.expires_at and auth_info.expires_at < int(time.time()): + return None + + return AuthCredentials(auth_info.scopes), AuthenticatedUser(auth_info) + + +class RequireAuthMiddleware: + """Middleware that requires a valid Bearer token in the Authorization header. + + This will validate the token with the auth provider and store the resulting + auth info in the request state. + """ + + def __init__( + self, + app: Any, + required_scopes: list[str], + resource_metadata_url: AnyHttpUrl | None = None, + ): + """Initialize the middleware. + + Args: + app: ASGI application + required_scopes: List of scopes that the token must have + resource_metadata_url: Optional protected resource metadata URL for WWW-Authenticate header + """ + self.app = app + self.required_scopes = required_scopes + self.resource_metadata_url = resource_metadata_url + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + auth_user = scope.get("user") + if not isinstance(auth_user, AuthenticatedUser): + await self._send_auth_error( + send, status_code=401, error="invalid_token", description="Authentication required" + ) + return + + auth_credentials = scope.get("auth") + + for required_scope in self.required_scopes: + # auth_credentials should always be provided; this is just paranoia + if auth_credentials is None or required_scope not in auth_credentials.scopes: + await self._send_auth_error( + send, status_code=403, error="insufficient_scope", description=f"Required scope: {required_scope}" + ) + return + + await self.app(scope, receive, send) + + async def _send_auth_error(self, send: Send, status_code: int, error: str, description: str) -> None: + """Send an authentication error response with WWW-Authenticate header.""" + # Build WWW-Authenticate header value + www_auth_parts = [f'error="{error}"', f'error_description="{description}"'] + if self.resource_metadata_url: + www_auth_parts.append(f'resource_metadata="{self.resource_metadata_url}"') + + www_authenticate = f"Bearer {', '.join(www_auth_parts)}" + + # Send response + body = {"error": error, "error_description": description} + body_bytes = json.dumps(body).encode() + + await send( + { + "type": "http.response.start", + "status": status_code, + "headers": [ + (b"content-type", b"application/json"), + (b"content-length", str(len(body_bytes)).encode()), + (b"www-authenticate", www_authenticate.encode()), + ], + } + ) + + await send( + { + "type": "http.response.body", + "body": body_bytes, + } + ) diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/client_auth.py b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/client_auth.py new file mode 100644 index 0000000..3d5067d --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/middleware/client_auth.py @@ -0,0 +1,119 @@ +import base64 +import binascii +import hmac +import time +from typing import Any +from urllib.parse import unquote + +from starlette.requests import Request + +from mcp.server.auth.provider import OAuthAuthorizationServerProvider +from mcp.shared.auth import OAuthClientInformationFull + + +class AuthenticationError(Exception): + def __init__(self, message: str): + self.message = message + + +class ClientAuthenticator: + """ClientAuthenticator is a callable which validates requests from a client + application, used to verify /token calls. + + If, during registration, the client requested to be issued a secret, the + authenticator asserts that /token calls must be authenticated with + that same secret. + + NOTE: clients can opt for no authentication during registration, in which case this + logic is skipped. + """ + + def __init__(self, provider: OAuthAuthorizationServerProvider[Any, Any, Any]): + """Initialize the authenticator. + + Args: + provider: Provider to look up client information + """ + self.provider = provider + + async def authenticate_request(self, request: Request) -> OAuthClientInformationFull: + """Authenticate a client from an HTTP request. + + Extracts client credentials from the appropriate location based on the + client's registered authentication method and validates them. + + Args: + request: The HTTP request containing client credentials + + Returns: + The authenticated client information + + Raises: + AuthenticationError: If authentication fails + """ + form_data = await request.form() + client_id = form_data.get("client_id") + if not client_id: + raise AuthenticationError("Missing client_id") + + client = await self.provider.get_client(str(client_id)) + if not client: + raise AuthenticationError("Invalid client_id") # pragma: no cover + + request_client_secret: str | None = None + auth_header = request.headers.get("Authorization", "") + + if client.token_endpoint_auth_method == "client_secret_basic": + if not auth_header.startswith("Basic "): + raise AuthenticationError("Missing or invalid Basic authentication in Authorization header") + + try: + encoded_credentials = auth_header[6:] # Remove "Basic " prefix + decoded = base64.b64decode(encoded_credentials).decode("utf-8") + if ":" not in decoded: + raise ValueError("Invalid Basic auth format") + basic_client_id, request_client_secret = decoded.split(":", 1) + + # URL-decode both parts per RFC 6749 Section 2.3.1 + basic_client_id = unquote(basic_client_id) + request_client_secret = unquote(request_client_secret) + + if basic_client_id != client_id: + raise AuthenticationError("Client ID mismatch in Basic auth") + except (ValueError, UnicodeDecodeError, binascii.Error): + raise AuthenticationError("Invalid Basic authentication header") + + elif client.token_endpoint_auth_method == "client_secret_post": + raw_form_data = form_data.get("client_secret") + # form_data.get() can return an UploadFile or None, so we need to check if it's a string + if isinstance(raw_form_data, str): + request_client_secret = str(raw_form_data) + + elif client.token_endpoint_auth_method == "none": + request_client_secret = None + else: + raise AuthenticationError( # pragma: no cover + f"Unsupported auth method: {client.token_endpoint_auth_method}" + ) + + # A client registered for a secret-based auth method but with no stored secret is + # misconfigured: nothing was actually verified above, so it must not pass authentication. + if client.token_endpoint_auth_method != "none" and not client.client_secret: + raise AuthenticationError("Client is registered for secret-based authentication but has no stored secret") + + # If client from the store expects a secret, validate that the request provides + # that secret + if client.client_secret: + if not request_client_secret: + raise AuthenticationError("Client secret is required") + + # hmac.compare_digest requires that both arguments are either bytes or a `str` containing + # only ASCII characters. Since we do not control `request_client_secret`, we encode both + # arguments to bytes. + if not hmac.compare_digest(client.client_secret.encode(), request_client_secret.encode()): + raise AuthenticationError("Invalid client_secret") + + if client.client_secret_expires_at and client.client_secret_expires_at < int(time.time()): + raise AuthenticationError("Client secret has expired") # pragma: no cover + + return client diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/provider.py b/venv/lib/python3.12/site-packages/mcp/server/auth/provider.py new file mode 100644 index 0000000..644868f --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/provider.py @@ -0,0 +1,371 @@ +from dataclasses import dataclass +from typing import Any, Generic, Literal, Protocol, TypeVar +from urllib.parse import parse_qs, urlencode, urlparse, urlunparse + +from pydantic import AnyUrl, BaseModel + +from mcp.shared.auth import OAuthClientInformationFull, OAuthToken + + +class AuthorizationParams(BaseModel): + state: str | None + scopes: list[str] | None + code_challenge: str + redirect_uri: AnyUrl + redirect_uri_provided_explicitly: bool + resource: str | None = None # RFC 8707 resource indicator + + +class IdentityAssertionParams(BaseModel): + """Validated parameters of a SEP-990 identity-assertion (RFC 7523 jwt-bearer) request. + + Passed to ``OAuthAuthorizationServerProvider.exchange_identity_assertion``. ``assertion`` is the + ID-JAG (a signed JWT) the enterprise identity provider issued; the provider validates it per + RFC 7523 §3 and the SEP-990 §5.1 processing rules before issuing an access token. + """ + + assertion: str # RFC 7523 §2.1: the JWT (ID-JAG) presented as the authorization grant + scopes: list[str] | None = None + resource: str | None = None # RFC 8707 resource indicator from the token request + + +class AuthorizationCode(BaseModel): + code: str + scopes: list[str] + expires_at: float + client_id: str + code_challenge: str + redirect_uri: AnyUrl + redirect_uri_provided_explicitly: bool + resource: str | None = None # RFC 8707 resource indicator + subject: str | None = None # resource owner; propagate to the issued AccessToken + + +class RefreshToken(BaseModel): + token: str + client_id: str + scopes: list[str] + expires_at: int | None = None + subject: str | None = None # resource owner; propagate to refreshed AccessTokens + + +class AccessToken(BaseModel): + token: str + client_id: str + scopes: list[str] + expires_at: int | None = None + resource: str | None = None # RFC 8707 resource indicator + subject: str | None = None # RFC 7662/9068 `sub`: resource owner; unique only per issuer + claims: dict[str, Any] | None = None # additional claims (e.g. `iss`, `act`) + + +def principal_components(token: AccessToken) -> tuple[str, str | None, str | None]: + """The (client_id, issuer, subject) triple identifying the principal a token represents. + + The single source for "who is this token's principal": session ownership and + request-state binding both build on it. Components the token verifier does + not supply are `None`, so comparisons degrade to the remaining components. + """ + issuer = (token.claims or {}).get("iss") + return token.client_id, str(issuer) if issuer is not None else None, token.subject + + +RegistrationErrorCode = Literal[ + "invalid_redirect_uri", + "invalid_client_metadata", + "invalid_software_statement", + "unapproved_software_statement", +] + + +@dataclass(frozen=True) +class RegistrationError(Exception): + error: RegistrationErrorCode + error_description: str | None = None + + +AuthorizationErrorCode = Literal[ + "invalid_request", + "unauthorized_client", + "access_denied", + "unsupported_response_type", + "invalid_scope", + "server_error", + "temporarily_unavailable", + "invalid_target", +] + + +@dataclass(frozen=True) +class AuthorizeError(Exception): + error: AuthorizationErrorCode + error_description: str | None = None + + +TokenErrorCode = Literal[ + "invalid_request", + "invalid_client", + "invalid_grant", + "unauthorized_client", + "unsupported_grant_type", + "invalid_scope", + # RFC 8707 §2: the requested resource (RFC 8707 indicator) is unknown or unsupported. + "invalid_target", +] + + +@dataclass(frozen=True) +class TokenError(Exception): + error: TokenErrorCode + error_description: str | None = None + + +class TokenVerifier(Protocol): + """Protocol for verifying bearer tokens.""" + + async def verify_token(self, token: str) -> AccessToken | None: + """Verify a bearer token and return access info if valid.""" + + +# NOTE: MCPServer doesn't render any of these types in the user response, so it's +# OK to add fields to subclasses which should not be exposed externally. +AuthorizationCodeT = TypeVar("AuthorizationCodeT", bound=AuthorizationCode) +RefreshTokenT = TypeVar("RefreshTokenT", bound=RefreshToken) +AccessTokenT = TypeVar("AccessTokenT", bound=AccessToken) + + +class OAuthAuthorizationServerProvider(Protocol, Generic[AuthorizationCodeT, RefreshTokenT, AccessTokenT]): + async def get_client(self, client_id: str) -> OAuthClientInformationFull | None: + """Retrieves client information by client ID. + + Implementors MAY raise NotImplementedError if dynamic client registration is + disabled in ClientRegistrationOptions. + + Args: + client_id: The ID of the client to retrieve. + + Returns: + The client information, or None if the client does not exist. + """ + + async def register_client(self, client_info: OAuthClientInformationFull) -> None: + """Saves client information as part of registering it. + + Implementors MAY raise NotImplementedError if dynamic client registration is + disabled in ClientRegistrationOptions. + + Args: + client_info: The client metadata to register. + + Raises: + RegistrationError: If the client metadata is invalid. + """ + + async def authorize(self, client: OAuthClientInformationFull, params: AuthorizationParams) -> str: + """Handle the /authorize endpoint and return a URL that the client + will be redirected to. + + Many MCP implementations will redirect to a third-party provider to perform + a second OAuth exchange with that provider. In this sort of setup, the client + has an OAuth connection with the MCP server, and the MCP server has an OAuth + connection with the 3rd-party provider. At the end of this flow, the client + should be redirected to the redirect_uri from params.redirect_uri. + + +--------+ +------------+ +-------------------+ + | | | | | | + | Client | --> | MCP Server | --> | 3rd Party OAuth | + | | | | | Server | + +--------+ +------------+ +-------------------+ + | ^ | + +------------+ | | | + | | | | Redirect | + |redirect_uri|<-----+ +------------------+ + | | + +------------+ + + Implementations will need to define another handler on the MCP server's return + flow to perform the second redirect, and generate and store an authorization + code as part of completing the OAuth authorization step. + + Implementations SHOULD generate an authorization code with at least 160 bits of + entropy, + and MUST generate an authorization code with at least 128 bits of entropy. + See https://datatracker.ietf.org/doc/html/rfc6749#section-10.10. + + Args: + client: The client requesting authorization. + params: The parameters of the authorization request. + + Returns: + A URL to redirect the client to for authorization. + + Raises: + AuthorizeError: If the authorization request is invalid. + """ + ... + + async def load_authorization_code( + self, client: OAuthClientInformationFull, authorization_code: str + ) -> AuthorizationCodeT | None: + """Loads an AuthorizationCode by its code. + + Args: + client: The client that requested the authorization code. + authorization_code: The authorization code to get the challenge for. + + Returns: + The AuthorizationCode, or None if not found. + """ + ... + + async def exchange_authorization_code( + self, client: OAuthClientInformationFull, authorization_code: AuthorizationCodeT + ) -> OAuthToken: + """Exchanges an authorization code for an access token and refresh token. + + Args: + client: The client exchanging the authorization code. + authorization_code: The authorization code to exchange. + + Returns: + The OAuth token, containing access and refresh tokens. + + Raises: + TokenError: If the request is invalid. + """ + ... + + async def load_refresh_token(self, client: OAuthClientInformationFull, refresh_token: str) -> RefreshTokenT | None: + """Loads a RefreshToken by its token string. + + Args: + client: The client that is requesting to load the refresh token. + refresh_token: The refresh token string to load. + + Returns: + The RefreshToken object if found, or None if not found. + """ + ... + + async def exchange_refresh_token( + self, + client: OAuthClientInformationFull, + refresh_token: RefreshTokenT, + scopes: list[str], + ) -> OAuthToken: + """Exchanges a refresh token for an access token and refresh token. + + Implementations SHOULD rotate both the access token and refresh token. + + Args: + client: The client exchanging the refresh token. + refresh_token: The refresh token to exchange. + scopes: Optional scopes to request with the new access token. + + Returns: + The OAuth token, containing access and refresh tokens. + + Raises: + TokenError: If the request is invalid. + """ + ... + + async def load_access_token(self, token: str) -> AccessTokenT | None: + """Loads an access token by its token string. + + Args: + token: The access token to verify. + + Returns: + The access token, or None if the token is invalid. + """ + + async def revoke_token( + self, + token: AccessTokenT | RefreshTokenT, + ) -> None: + """Revokes an access or refresh token. + + If the given token is invalid or already revoked, this method should do nothing. + + Implementations SHOULD revoke both the access token and its corresponding + refresh token, regardless of which of the access token or refresh token is + provided. + + Args: + token: The token to revoke. + """ + + async def exchange_identity_assertion( + self, + client: OAuthClientInformationFull, + params: IdentityAssertionParams, + ) -> OAuthToken: + """Exchanges an Identity Assertion Authorization Grant (ID-JAG) for an access token. + + This is leg 2 of SEP-990: the client presents an ID-JAG - issued by the enterprise + identity provider - using the RFC 7523 ``urn:ietf:params:oauth:grant-type:jwt-bearer`` + grant, and receives an access token for this MCP server. The default implementation + rejects every request as an unsupported grant type; override it to enable the grant. + + The implementation is responsible for validating ``params.assertion`` per RFC 7523 §3 + and the SEP-990 §5.1 processing rules, in particular: + + - verify the JWT signature, ``iss``, and ``exp``, and that ``typ`` is ``oauth-id-jag+jwt``; + - require ``aud`` to identify this authorization server (its own issuer); + - require a ``sub`` (RFC 7523 §3 makes it mandatory) identifying the end user; + - reject replays - enforce ``exp``, and track ``jti`` for the assertion's lifetime; + - require the ID-JAG's ``client_id`` claim to match the authenticated ``client`` - do + NOT derive authorization from ``client.client_id`` alone, which for a confidential + client is authenticated but for any client is ultimately self-asserted in the request; + - audience-restrict the issued access token to the resource named in the ID-JAG's + ``resource`` claim, not merely ``params.resource`` (which the client controls); + - derive the granted scopes from the ID-JAG and policy rather than granting + ``params.scopes`` verbatim. + + The handler guarantees ``client`` is confidential (it rejects clients without a stored + secret before calling this hook), but the ID-JAG remains the authoritative grant. + + Args: + client: The authenticated client presenting the assertion. + params: The validated jwt-bearer request parameters (the ID-JAG and indicators). + + Returns: + The OAuth token, containing the issued access token. A refresh token SHOULD NOT be + issued: SEP-990 relies on the IdP to control session lifetime via re-issued ID-JAGs. + + Raises: + TokenError: If the assertion or request is invalid. Use ``invalid_grant`` for a + rejected assertion and ``invalid_target`` for an unknown ``resource``. + """ + raise TokenError( + error="unsupported_grant_type", + error_description="The JWT bearer grant is not supported by this authorization server", + ) + + +def construct_redirect_uri(redirect_uri_base: str, **params: str | None) -> str: + parsed_uri = urlparse(redirect_uri_base) + query_params = [(k, v) for k, vs in parse_qs(parsed_uri.query).items() for v in vs] + for k, v in params.items(): + if v is not None: + query_params.append((k, v)) + + redirect_uri = urlunparse(parsed_uri._replace(query=urlencode(query_params))) + return redirect_uri + + +class ProviderTokenVerifier(TokenVerifier): + """Token verifier that uses an OAuthAuthorizationServerProvider. + + This is provided for backwards compatibility with existing auth_server_provider + configurations. For new implementations using AS/RS separation, consider using + the TokenVerifier protocol with a dedicated implementation like IntrospectionTokenVerifier. + """ + + def __init__(self, provider: "OAuthAuthorizationServerProvider[AuthorizationCode, RefreshToken, AccessToken]"): + self.provider = provider + + async def verify_token(self, token: str) -> AccessToken | None: + """Verify token using the provider's load_access_token method.""" + return await self.provider.load_access_token(token) diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/routes.py b/venv/lib/python3.12/site-packages/mcp/server/auth/routes.py new file mode 100644 index 0000000..848604d --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/routes.py @@ -0,0 +1,261 @@ +from collections.abc import Awaitable, Callable +from typing import Any +from urllib.parse import urlparse + +from pydantic import AnyHttpUrl +from starlette.middleware.cors import CORSMiddleware +from starlette.requests import Request +from starlette.responses import Response +from starlette.routing import Route, request_response # type: ignore +from starlette.types import ASGIApp + +from mcp.server.auth.handlers.authorize import AuthorizationHandler +from mcp.server.auth.handlers.metadata import MetadataHandler, ProtectedResourceMetadataHandler +from mcp.server.auth.handlers.register import RegistrationHandler +from mcp.server.auth.handlers.revoke import RevocationHandler +from mcp.server.auth.handlers.token import TokenHandler +from mcp.server.auth.middleware.client_auth import ClientAuthenticator +from mcp.server.auth.provider import OAuthAuthorizationServerProvider +from mcp.server.auth.settings import ClientRegistrationOptions, RevocationOptions +from mcp.server.transport_security import DEFAULT_MAX_REQUEST_BODY_SIZE, RequestBodyLimitMiddleware +from mcp.shared.auth import JWT_BEARER_GRANT_TYPE, OAuthMetadata, ProtectedResourceMetadata +from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER + + +def validate_issuer_url(url: AnyHttpUrl): + """Validate that the issuer URL meets OAuth 2.0 requirements. + + Args: + url: The issuer URL to validate. + + Raises: + ValueError: If the issuer URL is invalid. + """ + + # RFC 8414 requires HTTPS, but we allow loopback/localhost HTTP for testing + if url.scheme != "https" and url.host not in ("localhost", "127.0.0.1", "[::1]"): + raise ValueError("Issuer URL must be HTTPS") + + # No fragments or query parameters allowed + if url.fragment: + raise ValueError("Issuer URL must not have a fragment") + if url.query: + raise ValueError("Issuer URL must not have a query string") + + +AUTHORIZATION_PATH = "/authorize" +TOKEN_PATH = "/token" +REGISTRATION_PATH = "/register" +REVOCATION_PATH = "/revoke" + +# SEP-990: leg 2 uses the RFC 7523 jwt-bearer grant; support is advertised as the ID-JAG profile. +ID_JAG_GRANT_PROFILE = "urn:ietf:params:oauth:grant-profile:id-jag" + + +def _cors(app: ASGIApp, allow_methods: list[str]) -> ASGIApp: + return CORSMiddleware( + app=app, + allow_origins="*", + allow_methods=allow_methods, + allow_headers=[MCP_PROTOCOL_VERSION_HEADER], + ) + + +def _body_limited(app: ASGIApp) -> ASGIApp: + return RequestBodyLimitMiddleware(app, DEFAULT_MAX_REQUEST_BODY_SIZE) + + +def cors_middleware( + handler: Callable[[Request], Response | Awaitable[Response]], + allow_methods: list[str], +) -> ASGIApp: + return _cors(request_response(handler), allow_methods) + + +def create_auth_routes( + provider: OAuthAuthorizationServerProvider[Any, Any, Any], + issuer_url: AnyHttpUrl, + service_documentation_url: AnyHttpUrl | None = None, + client_registration_options: ClientRegistrationOptions | None = None, + revocation_options: RevocationOptions | None = None, + identity_assertion_enabled: bool = False, +) -> list[Route]: + validate_issuer_url(issuer_url) + + client_registration_options = client_registration_options or ClientRegistrationOptions() + revocation_options = revocation_options or RevocationOptions() + metadata = build_metadata( + issuer_url, + service_documentation_url, + client_registration_options, + revocation_options, + supports_identity_assertion=identity_assertion_enabled, + ) + client_authenticator = ClientAuthenticator(provider) + token_handler = TokenHandler(provider, client_authenticator, identity_assertion_enabled=identity_assertion_enabled) + + # Create routes + # Allow CORS requests for endpoints meant to be hit by the OAuth client + # (with the client secret). This is intended to support things like MCP Inspector, + # where the client runs in a web browser. CORS is the outermost wrapper so that + # responses produced by inner layers (such as a 413) still carry CORS headers. + routes = [ + Route( + "/.well-known/oauth-authorization-server", + endpoint=cors_middleware( + MetadataHandler(metadata).handle, + ["GET", "OPTIONS"], + ), + methods=["GET", "OPTIONS"], + ), + Route( + AUTHORIZATION_PATH, + # do not allow CORS for authorization endpoint; + # clients should just redirect to this + endpoint=_body_limited(request_response(AuthorizationHandler(provider).handle)), + methods=["GET", "POST"], + ), + Route( + TOKEN_PATH, + endpoint=_cors(_body_limited(request_response(token_handler.handle)), ["POST", "OPTIONS"]), + methods=["POST", "OPTIONS"], + ), + ] + + if client_registration_options.enabled: # pragma: no branch + registration_handler = RegistrationHandler( + provider, + options=client_registration_options, + ) + routes.append( + Route( + REGISTRATION_PATH, + endpoint=_cors(_body_limited(request_response(registration_handler.handle)), ["POST", "OPTIONS"]), + methods=["POST", "OPTIONS"], + ) + ) + + if revocation_options.enabled: # pragma: no branch + revocation_handler = RevocationHandler(provider, client_authenticator) + routes.append( + Route( + REVOCATION_PATH, + endpoint=_cors(_body_limited(request_response(revocation_handler.handle)), ["POST", "OPTIONS"]), + methods=["POST", "OPTIONS"], + ) + ) + + return routes + + +def build_metadata( + issuer_url: AnyHttpUrl, + service_documentation_url: AnyHttpUrl | None, + client_registration_options: ClientRegistrationOptions, + revocation_options: RevocationOptions, + supports_identity_assertion: bool = False, +) -> OAuthMetadata: + authorization_url = AnyHttpUrl(str(issuer_url).rstrip("/") + AUTHORIZATION_PATH) + token_url = AnyHttpUrl(str(issuer_url).rstrip("/") + TOKEN_PATH) + + grant_types_supported = ["authorization_code", "refresh_token"] + # SEP-990 / ext-auth §6: support for the ID-JAG flow is advertised as a grant PROFILE, not as + # the jwt-bearer grant type (which an AS might support for other purposes). + authorization_grant_profiles_supported: list[str] | None = None + if supports_identity_assertion: + grant_types_supported.append(JWT_BEARER_GRANT_TYPE) + authorization_grant_profiles_supported = [ID_JAG_GRANT_PROFILE] + + # Create metadata + metadata = OAuthMetadata( + issuer=issuer_url, + authorization_endpoint=authorization_url, + token_endpoint=token_url, + scopes_supported=client_registration_options.valid_scopes, + response_types_supported=["code"], + response_modes_supported=None, + grant_types_supported=grant_types_supported, + token_endpoint_auth_methods_supported=["client_secret_post", "client_secret_basic"], + token_endpoint_auth_signing_alg_values_supported=None, + service_documentation=service_documentation_url, + ui_locales_supported=None, + op_policy_uri=None, + op_tos_uri=None, + introspection_endpoint=None, + code_challenge_methods_supported=["S256"], + authorization_grant_profiles_supported=authorization_grant_profiles_supported, + ) + + # Add registration endpoint if supported + if client_registration_options.enabled: # pragma: no branch + metadata.registration_endpoint = AnyHttpUrl(str(issuer_url).rstrip("/") + REGISTRATION_PATH) + + # Add revocation endpoint if supported + if revocation_options.enabled: # pragma: no branch + metadata.revocation_endpoint = AnyHttpUrl(str(issuer_url).rstrip("/") + REVOCATION_PATH) + metadata.revocation_endpoint_auth_methods_supported = ["client_secret_post", "client_secret_basic"] + + return metadata + + +def build_resource_metadata_url(resource_server_url: AnyHttpUrl) -> AnyHttpUrl: + """Build RFC 9728 compliant protected resource metadata URL. + + Inserts /.well-known/oauth-protected-resource between host and resource path + as specified in RFC 9728 §3.1. + + Args: + resource_server_url: The resource server URL (e.g., https://example.com/mcp) + + Returns: + The metadata URL (e.g., https://example.com/.well-known/oauth-protected-resource/mcp) + """ + parsed = urlparse(str(resource_server_url)) + # Handle trailing slash: if path is just "/", treat as empty + resource_path = parsed.path if parsed.path != "/" else "" + return AnyHttpUrl(f"{parsed.scheme}://{parsed.netloc}/.well-known/oauth-protected-resource{resource_path}") + + +def create_protected_resource_routes( + resource_url: AnyHttpUrl, + authorization_servers: list[AnyHttpUrl], + scopes_supported: list[str] | None = None, + resource_name: str | None = None, + resource_documentation: AnyHttpUrl | None = None, +) -> list[Route]: + """Create routes for OAuth 2.0 Protected Resource Metadata (RFC 9728). + + Args: + resource_url: The URL of this resource server + authorization_servers: List of authorization servers that can issue tokens + scopes_supported: Optional list of scopes supported by this resource + resource_name: Optional human-readable name for this resource + resource_documentation: Optional URL to documentation for this resource + + Returns: + List of Starlette routes for protected resource metadata + """ + metadata = ProtectedResourceMetadata( + resource=resource_url, + authorization_servers=authorization_servers, + scopes_supported=scopes_supported, + resource_name=resource_name, + resource_documentation=resource_documentation, + # bearer_methods_supported defaults to ["header"] in the model + ) + + handler = ProtectedResourceMetadataHandler(metadata) + + # RFC 9728 §3.1: Register route at /.well-known/oauth-protected-resource + resource path + metadata_url = build_resource_metadata_url(resource_url) + # Extract just the path part for route registration + parsed = urlparse(str(metadata_url)) + well_known_path = parsed.path + + return [ + Route( + well_known_path, + endpoint=cors_middleware(handler.handle, ["GET", "OPTIONS"]), + methods=["GET", "OPTIONS"], + ) + ] diff --git a/venv/lib/python3.12/site-packages/mcp/server/auth/settings.py b/venv/lib/python3.12/site-packages/mcp/server/auth/settings.py new file mode 100644 index 0000000..ae2083a --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/auth/settings.py @@ -0,0 +1,42 @@ +from pydantic import AnyHttpUrl, BaseModel, ConfigDict, Field + + +class ClientRegistrationOptions(BaseModel): + enabled: bool = False + client_secret_expiry_seconds: int | None = None + valid_scopes: list[str] | None = None + default_scopes: list[str] | None = None + + +class RevocationOptions(BaseModel): + enabled: bool = False + + +class AuthSettings(BaseModel): + # Preserve empty URL paths so a path-less issuer/resource passed as a string keeps its + # canonical form (no trailing slash). RFC 8414/9207 issuer comparison is exact string + # comparison, so a spurious trailing slash would break it. See PR #2925 for the metadata + # models; this applies the same to the server's own configured URLs. + model_config = ConfigDict(url_preserve_empty_path=True) + + issuer_url: AnyHttpUrl = Field( + ..., + description="OAuth authorization server URL that issues tokens for this resource server.", + ) + service_documentation_url: AnyHttpUrl | None = None + client_registration_options: ClientRegistrationOptions | None = None + revocation_options: RevocationOptions | None = None + required_scopes: list[str] | None = None + identity_assertion_enabled: bool = Field( + default=False, + description="Advertise and accept the SEP-990 Identity Assertion Authorization Grant " + "(the RFC 7523 jwt-bearer grant carrying an ID-JAG) at the token endpoint, for enterprise " + "IdP flows. The provider must implement `exchange_identity_assertion`.", + ) + + # Resource Server settings (when operating as RS only) + resource_server_url: AnyHttpUrl | None = Field( + ..., + description="The URL of the MCP server to be used as the resource identifier " + "and base route to look up OAuth Protected Resource Metadata.", + ) diff --git a/venv/lib/python3.12/site-packages/mcp/server/caching.py b/venv/lib/python3.12/site-packages/mcp/server/caching.py new file mode 100644 index 0000000..5e99303 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/caching.py @@ -0,0 +1,85 @@ +"""Server-side caching hints (SEP-2549, protocol revision 2026-07-28). + +Results for the cacheable methods carry `ttlMs`/`cacheScope` freshness hints. +A handler sets them by returning a result with explicit `ttl_ms`/`cache_scope` +values; `Server(cache_hints={method: CacheHint(...)})` fills them for handlers +that don't. Fields the handler set win, per field, so a server-wide hint never +overrides a handler's explicit choice. +""" + +from __future__ import annotations + +from collections.abc import Mapping +from dataclasses import dataclass +from typing import Any, Literal, TypeVar + +import mcp_types as types +from mcp_types.methods import CACHEABLE_METHODS, CacheableMethod + +__all__ = ["CACHEABLE_METHODS", "CacheHint", "CacheableMethod", "apply_cache_hint", "validate_cache_hints"] + + +@dataclass(frozen=True, slots=True) +class CacheHint: + """Freshness hint for one cacheable method's results. + + `ttl_ms` is how long, in milliseconds, a client may consider the result + fresh (`0` means immediately stale). `scope` is whether a cached result may + be shared across authorization contexts (`"public"`) or only reused within + the one that produced it (`"private"`). + """ + + ttl_ms: int = 0 + scope: Literal["public", "private"] = "private" + + def __post_init__(self) -> None: + if self.ttl_ms < 0: + raise ValueError(f"ttl_ms must be >= 0, got {self.ttl_ms}") + if self.scope not in ("public", "private"): + raise ValueError(f"scope must be 'public' or 'private', got {self.scope!r}") + + +CacheableResultT = TypeVar("CacheableResultT", bound=types.CacheableResult) + + +def apply_cache_hint(result: CacheableResultT, hint: CacheHint) -> CacheableResultT: + """Fill `ttl_ms`/`cache_scope` on `result` from `hint`. + + Per-field: a field the handler set explicitly - even to its default value, + tracked via `model_fields_set` - is left alone; only unset fields take the + hint. A handler constructing results with `model_construct` bypasses that + tracking and is treated as having set nothing. + """ + update: dict[str, int | str] = {} + if "ttl_ms" not in result.model_fields_set: + update["ttl_ms"] = hint.ttl_ms + if "cache_scope" not in result.model_fields_set: + update["cache_scope"] = hint.scope + return result.model_copy(update=update) if update else result + + +def validate_cache_hints(cache_hints: Mapping[Any, Any] | None) -> dict[str, CacheHint]: + """Validate a `cache_hints` constructor argument into a plain dict. + + The `Server`/`MCPServer` signatures already close the key set and value + type for type-checked callers; this runtime gate is deliberately loose in + its parameter so it covers everyone else (e.g. a map deserialized from + config) - a bad entry fails at construction, not on the first request to + that method. + + Raises: + ValueError: If a key is not a cacheable method. + TypeError: If a value is not a `CacheHint`. + """ + if cache_hints is None: + return {} + # repr-format keys so a non-string key raises this ValueError, not a TypeError from sorted/join. + unknown = sorted(repr(method) for method in cache_hints if method not in CACHEABLE_METHODS) + if unknown: + raise ValueError(f"cache_hints keys must be cacheable methods (see CacheableMethod); got: {', '.join(unknown)}") + validated: dict[str, CacheHint] = {} + for method, hint in cache_hints.items(): + if not isinstance(hint, CacheHint): + raise TypeError(f"cache_hints[{method!r}] must be a CacheHint, got {type(hint).__name__}") + validated[method] = hint + return validated diff --git a/venv/lib/python3.12/site-packages/mcp/server/connection.py b/venv/lib/python3.12/site-packages/mcp/server/connection.py new file mode 100644 index 0000000..99ba2da --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/connection.py @@ -0,0 +1,493 @@ +"""`Connection` - per-client connection state and the standalone outbound channel. + +Always present on `Context` (never `None`), even in stateless deployments. +Holds peer info, per-connection scratch `state` and an `exit_stack` for +teardown, and an `Outbound` for the standalone stream (the SSE GET stream in +streamable HTTP, or the single duplex stream in stdio). + +Construct via the factories: `Connection.from_envelope` for the 2026-era +single-exchange path (born ready, no back-channel) and `Connection.for_loop` +for the handshake-driven loop path. Both populate `protocol_version` so the +kernel reads it as a fact. + +`notify` is best-effort: it never raises. If there's no standalone channel +or the stream has been dropped, the notification is debug-logged and silently +discarded - server-initiated notifications are inherently advisory. +`send_raw_request` raises `NoBackChannelError` when there's no channel; `ping` +is the only spec-sanctioned standalone request. +""" + +from __future__ import annotations + +import logging +from collections.abc import Mapping +from contextlib import AsyncExitStack +from typing import Any, Final, TypeVar, get_args, overload + +import anyio +from mcp_types import ( + LOG_LEVEL_META_KEY, + ClientCapabilities, + CreateMessageRequest, + CreateMessageResult, + ElicitRequest, + ElicitResult, + EmptyResult, + Implementation, + InitializeRequestParams, + ListRootsRequest, + ListRootsResult, + LoggingLevel, + PingRequest, + Request, +) +from mcp_types import methods as _methods +from mcp_types.version import LATEST_HANDSHAKE_VERSION, MODERN_PROTOCOL_VERSIONS +from pydantic import BaseModel, ValidationError +from typing_extensions import deprecated + +from mcp.shared.dispatcher import CallOptions, Outbound +from mcp.shared.exceptions import MCPDeprecationWarning, NoBackChannelError +from mcp.shared.peer import Meta, dump_params +from mcp.shared.subscriptions import LISTEN_STREAM_METHODS + +__all__ = ["Connection"] + +logger = logging.getLogger(__name__) +# `Connection.log`'s `logger` parameter (public API, the spec's logger-name +# field) shadows the module logger inside that method; this alias keeps the +# module logger reachable there. +_logger = logger + +_LOG_LEVELS: Final[tuple[LoggingLevel, ...]] = get_args(LoggingLevel) +"""Severity-ascending, from the `LoggingLevel` literal's declaration order (the +RFC 5424 scale) - the literal is the single source of the ordering.""" + +_ALL_LOG_LEVELS: Final[frozenset[LoggingLevel]] = frozenset(_LOG_LEVELS) + + +def allowed_log_levels(protocol_version: str, meta: Mapping[str, Any] | None) -> frozenset[LoggingLevel]: + """The `notifications/message` levels deliverable for one inbound request. + + 2026-07-28+ makes log delivery a per-request opt-in (server/utilities/ + logging): the client sets the reserved `io.modelcontextprotocol/logLevel` + `_meta` key, absent means no levels - the server MUST NOT send - and + present means that level and above. An unrecognized value reads as absent; + spec methods already reject a malformed value at surface validation + before any handler runs, so that arm only serves custom methods, where + dropping is the safe direction. Connection-scoped emitters pass + `meta=None`: `logging/setLevel` is gone at 2026 and log delivery is + request-scoped only, so they deliver nothing. Handshake versions keep + their `logging/setLevel`-era semantics: every level may be sent, filtering + is the application's `logging/setLevel` handler's job as before. + """ + if protocol_version not in MODERN_PROTOCOL_VERSIONS: + return _ALL_LOG_LEVELS + requested = (meta or {}).get(LOG_LEVEL_META_KEY) + if requested not in _LOG_LEVELS: + return frozenset() + return frozenset(_LOG_LEVELS[_LOG_LEVELS.index(requested) :]) + + +ResultT = TypeVar("ResultT", bound=BaseModel) + +# Result types for the spec's server-to-client request set, used by +# `Connection.send_request` to infer the result type. If the spec's request +# set grows substantially, consider declaring the result mapping on the +# request types themselves (a `__mcp_result__` ClassVar read via a structural +# protocol) so this table and the overload ladder don't need maintaining. +_RESULT_FOR: dict[type[Request[Any, Any]], type[BaseModel]] = { + CreateMessageRequest: CreateMessageResult, + ElicitRequest: ElicitResult, + ListRootsRequest: ListRootsResult, + PingRequest: EmptyResult, +} + + +_ModelT = TypeVar("_ModelT", bound=BaseModel) + + +def _typed(model: type[_ModelT], raw: Any) -> _ModelT | None: + """Validate a raw envelope value into a typed model. + + A missing, null or mis-shaped value falls through to `ValidationError` + and is treated as not supplied so the request still routes. Spec methods + are separately re-validated by the kernel's per-version params surface, + which types the reserved `_meta` keys strictly. + """ + try: + return model.model_validate(raw, by_name=False) + except ValidationError: + return None + + +def _notification_params(payload: dict[str, Any] | None, meta: Meta | None) -> dict[str, Any] | None: + if not meta: + return payload + out = dict(payload or {}) + out["_meta"] = meta + return out + + +class _NoChannelOutbound: + """Connection-scoped `Outbound` for the no-back-channel case. + + The structural answer to "this connection cannot push to its peer": + `send_raw_request` raises `NoBackChannelError`; `notify` drops with a + debug log. `Connection.from_envelope` installs this so the modern + single-exchange path never needs a mode flag - the channel itself says no. + """ + + async def send_raw_request( + self, + method: str, + params: Mapping[str, Any] | None, + opts: CallOptions | None = None, + ) -> dict[str, Any]: + raise NoBackChannelError(method) + + async def notify(self, method: str, params: Mapping[str, Any] | None, opts: CallOptions | None = None) -> None: + logger.debug("dropped %s: no standalone channel", method) + + +_NO_CHANNEL = _NoChannelOutbound() + + +class NotifyOnlyOutbound(_NoChannelOutbound): + """Connection-scoped `Outbound` that forwards notifications and refuses requests. + + Installed by `serve_dual_era_loop` for modern (2026-07-28+) connections + over duplex stream transports: the pipe is real, so server notifications + ride it, but the modern protocol forbids server-initiated JSON-RPC + requests, so `send_raw_request` (inherited) refuses by construction. + + Change notifications (`notifications/*/list_changed`, + `notifications/resources/updated`) are dropped with a debug log: at this + era they reach a client only through a `subscriptions/listen` stream it + opened, so a bare copy on the shared channel would be an unrequested + notification. Publish them on the server's `SubscriptionBus` instead. + """ + + def __init__(self, outbound: Outbound) -> None: + self._outbound = outbound + + async def notify(self, method: str, params: Mapping[str, Any] | None, opts: CallOptions | None = None) -> None: + # At the 2026-07-28 era these are `subscriptions/listen` stream goods + # only: the spec forbids sending a change notification a subscription + # did not request, and listen streams deliver them (stamped, filtered) + # via the request-scoped outbound, never this connection-scoped channel. + if method in LISTEN_STREAM_METHODS: + logger.debug("dropped %s: delivered via subscriptions/listen at this era", method) + return + await self._outbound.notify(method, params, opts) + + +class Connection: + """Per-client connection state and standalone-stream `Outbound`. + + Construct via `from_envelope` (modern single-exchange: born ready, no + back-channel) or `for_loop` (handshake-driven: ready once the client's + `notifications/initialized` arrives). Either way `protocol_version` is + populated at construction. + """ + + outbound: Outbound + """The connection-scoped channel for server-initiated messages.""" + + session_id: str | None + + client_capabilities: ClientCapabilities | None + """The capabilities the peer declared: the handshake's on the loop path, + the request envelope's on the modern path. `None` when none were declared. + Kept in lockstep with `client_params` by its setter, and settable on its + own for the modern envelope, where capabilities are required but client + info is optional (spec PR #3002) - capability checks must not depend on the + peer having identified itself.""" + + protocol_version: str + """The protocol version this connection speaks. Populated at construction + by the factory and overwritten by `_handle_initialize` once the handshake + commits on the loop path.""" + + initialized: anyio.Event + """Set when `notifications/initialized` arrives (matches TS `oninitialized`); + the point from which the spec permits server-initiated requests beyond + ping/logging. Pre-set on connections built via `from_envelope`.""" + + state: dict[str, Any] + """Per-connection scratch state; persists across requests on this connection.""" + + exit_stack: AsyncExitStack + """Per-connection teardown, unwound LIFO (shielded) when the connection + closes. Push cleanup from handlers or middleware; exceptions are logged + and swallowed.""" + + def __init__( + self, + outbound: Outbound, + *, + protocol_version: str, + session_id: str | None = None, + client_params: InitializeRequestParams | None = None, + ) -> None: + self.outbound = outbound + self.protocol_version = protocol_version + self.session_id = session_id + self.client_capabilities = None + self.client_params = client_params + self.initialized = anyio.Event() + self.state = {} + self.exit_stack = AsyncExitStack() + + @property + def client_params(self) -> InitializeRequestParams | None: + """The full `initialize` request params, or the equivalent built from the + 2026-era envelope. `None` when no client info was supplied.""" + return self._client_params + + @client_params.setter + def client_params(self, value: InitializeRequestParams | None) -> None: + # Assignment is the sync point: recording full client params (the + # handshake commit, or a modern envelope carrying client info) also + # records the capabilities fact, so the two can never drift. Clearing + # to `None` leaves `client_capabilities` alone - the modern envelope + # declares capabilities without client info. + self._client_params = value + if value is not None: + self.client_capabilities = value.capabilities + + @classmethod + def from_envelope( + cls, + protocol_version: str, + client_info: Any, + client_capabilities: Any, + *, + outbound: Outbound = _NO_CHANNEL, + ) -> Connection: + """A born-ready connection populated from a request's `_meta` envelope. + + `protocol_version` must be an already-validated version string - the + inbound classification ladder owns rejecting non-string or unsupported + values. `client_info` and `client_capabilities` are the raw envelope + values: this constructor owns turning them into connection identity, + identically on every modern entry, so a mis-shaped value degrades to + not-supplied rather than failing the request. `initialized` is set, + well-formed capabilities are recorded as `client_capabilities` (client + info is optional per spec PR #3002, so capability checks never depend on + it), and the full `client_params` is additionally synthesized when + client info was supplied too. `outbound` defaults to the no-channel + sentinel for the single-exchange HTTP path; duplex modern transports + (e.g. stdio) pass a notify-only wrapper around the dispatcher so + server notifications ride the pipe while server-initiated requests + stay refused. + """ + info = _typed(Implementation, client_info) + capabilities = _typed(ClientCapabilities, client_capabilities) + client_params = None + if info is not None and capabilities is not None: + client_params = InitializeRequestParams( + protocol_version=protocol_version, + capabilities=capabilities, + client_info=info, + ) + connection = cls(outbound, protocol_version=protocol_version, client_params=client_params) + connection.client_capabilities = capabilities + connection.initialized.set() + return connection + + @classmethod + def for_loop( + cls, + outbound: Outbound, + *, + session_id: str | None = None, + protocol_version_hint: str | None = None, + ) -> Connection: + """A connection for the handshake-driven loop path. + + Not born-ready: `initialized` is set later by the kernel when + `notifications/initialized` arrives. `protocol_version` is seeded from + the transport hint (or `LATEST_HANDSHAKE_VERSION`) so it's never `None`; + the handshake overwrites it once negotiated. + """ + return cls( + outbound, + protocol_version=protocol_version_hint if protocol_version_hint is not None else LATEST_HANDSHAKE_VERSION, + session_id=session_id, + ) + + @property + def has_standalone_channel(self) -> bool: + """Whether this connection has a real back-channel for server-initiated + messages. Derived from `outbound` - the no-channel sentinel is the only + case that doesn't. + + Channel presence, not request permission: a modern (2026-07-28+) + duplex connection has a channel that carries notifications while + `send_raw_request` still refuses, because the protocol forbids + server-initiated requests.""" + return self.outbound is not _NO_CHANNEL + + @property + def initialize_accepted(self) -> bool: + """True once the inbound request gate is open: `initialize` recorded the + peer info, or the handshake completed outright (born-ready, or a bare + `notifications/initialized`). Derived, never stored.""" + return self.client_params is not None or self.initialized.is_set() + + async def send_raw_request( + self, + method: str, + params: Mapping[str, Any] | None, + opts: CallOptions | None = None, + ) -> dict[str, Any]: + """Send a raw request on the standalone stream. + + Low-level `Outbound` channel. Prefer the typed `send_request` or the + convenience methods below; use this directly only for off-spec + messages. `opts` carries per-call `timeout` / `on_progress` / + resumption hints; see `CallOptions`. + + Raises: + MCPError: The peer responded with an error. + NoBackChannelError: no back-channel for server-initiated requests - + `has_standalone_channel` is `False`, or a modern (2026-07-28+) + connection, where the protocol forbids them. + """ + return await self.outbound.send_raw_request(method, params, opts) + + @overload + async def send_request( + self, req: CreateMessageRequest, *, opts: CallOptions | None = None + ) -> CreateMessageResult: ... + @overload + async def send_request(self, req: ElicitRequest, *, opts: CallOptions | None = None) -> ElicitResult: ... + @overload + async def send_request(self, req: ListRootsRequest, *, opts: CallOptions | None = None) -> ListRootsResult: ... + @overload + async def send_request(self, req: PingRequest, *, opts: CallOptions | None = None) -> EmptyResult: ... + @overload + async def send_request( + self, req: Request[Any, Any], *, result_type: type[ResultT], opts: CallOptions | None = None + ) -> ResultT: ... + async def send_request( + self, + req: Request[Any, Any], + *, + result_type: type[BaseModel] | None = None, + opts: CallOptions | None = None, + ) -> BaseModel: + """Send a typed server-to-client request and return its typed result. + + For spec request types the result type is inferred. For custom requests + pass `result_type=` explicitly. + + Raises: + MCPError: The peer responded with an error. + NoBackChannelError: No back-channel for server-initiated requests. + pydantic.ValidationError: The peer's result does not match the expected result type. + KeyError: `result_type` omitted for a non-spec request type. + """ + raw = await self.send_raw_request(req.method, dump_params(req.params), opts) + if req.method in _methods.MONOLITH_REQUESTS: + try: + _methods.validate_client_result(req.method, self.protocol_version, raw) + except KeyError: + pass + cls = result_type if result_type is not None else _RESULT_FOR[type(req)] + return cls.model_validate(raw, by_name=False) + + async def notify(self, method: str, params: Mapping[str, Any] | None, opts: CallOptions | None = None) -> None: + """Send a best-effort notification on the standalone stream. + + Never raises. If there's no standalone channel or the stream is broken, + the notification is dropped and debug-logged. + """ + try: + await self.outbound.notify(method, params, opts) + except (anyio.BrokenResourceError, anyio.ClosedResourceError): + logger.debug("dropped %s: standalone stream closed", method) + + async def ping(self, *, meta: Meta | None = None, opts: CallOptions | None = None) -> None: + """Send a `ping` request on the standalone stream. + + Raises: + MCPError: The peer responded with an error. + NoBackChannelError: no back-channel for server-initiated requests - + `has_standalone_channel` is `False`, or a modern (2026-07-28+) + connection, where the protocol forbids them. + """ + await self.send_raw_request("ping", dump_params(None, meta), opts) + + @deprecated("The logging capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def log(self, level: LoggingLevel, data: Any, logger: str | None = None, *, meta: Meta | None = None) -> None: + """Send a `notifications/message` log entry on the standalone stream. Best-effort. + + On 2026-07-28+ connections this never sends: log delivery is a + per-request opt-in that rides the requesting stream (`ctx.log`, + `ctx.session.send_log_message`), and the standalone stream is + forbidden from carrying `notifications/message`, so the entry is + debug-logged and dropped. + """ + if level not in allowed_log_levels(self.protocol_version, None): + _logger.debug("dropped notifications/message: no connection-wide log delivery at %s", self.protocol_version) + return + params: dict[str, Any] = {"level": level, "data": data} + if logger is not None: + params["logger"] = logger + await self.notify("notifications/message", _notification_params(params, meta)) + + async def send_tool_list_changed(self, *, meta: Meta | None = None) -> None: + await self.notify("notifications/tools/list_changed", _notification_params(None, meta)) + + async def send_prompt_list_changed(self, *, meta: Meta | None = None) -> None: + await self.notify("notifications/prompts/list_changed", _notification_params(None, meta)) + + async def send_resource_list_changed(self, *, meta: Meta | None = None) -> None: + await self.notify("notifications/resources/list_changed", _notification_params(None, meta)) + + async def send_resource_updated(self, uri: str, *, meta: Meta | None = None) -> None: + await self.notify("notifications/resources/updated", _notification_params({"uri": uri}, meta)) + + def check_capability(self, capability: ClientCapabilities) -> bool: + """Return whether the connected client declared the given capability. + + Returns `False` when no capabilities have been recorded. + """ + # TODO(L53): redesign - mirrors v1 ServerSession.check_client_capability + # verbatim for parity. + if self.client_capabilities is None: + return False + have = self.client_capabilities + if capability.roots is not None: + if have.roots is None: + return False + if capability.roots.list_changed and not have.roots.list_changed: + return False + if capability.sampling is not None: + if have.sampling is None: + return False + if capability.sampling.context is not None and have.sampling.context is None: + return False + if capability.sampling.tools is not None and have.sampling.tools is None: + return False + if capability.elicitation is not None and have.elicitation is None: + return False + if capability.experimental is not None: + if have.experimental is None: + return False + for k, v in capability.experimental.items(): + if k not in have.experimental or have.experimental[k] != v: + return False + if capability.extensions is not None: + # SEP-2133: an extension is supported when the client declares its + # identifier. Settings are negotiated per-extension (the client may + # advertise more than the server asks for), so presence - not value + # equality - is the meaningful check. + if have.extensions is None: + return False + for identifier in capability.extensions: + if identifier not in have.extensions: + return False + return True diff --git a/venv/lib/python3.12/site-packages/mcp/server/context.py b/venv/lib/python3.12/site-packages/mcp/server/context.py new file mode 100644 index 0000000..bfcb9c9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/context.py @@ -0,0 +1,194 @@ +import logging +from collections.abc import Awaitable, Callable, Mapping +from dataclasses import dataclass +from typing import Any, Generic, Protocol + +from mcp_types import LoggingLevel, RequestId, RequestParamsMeta +from pydantic import BaseModel +from typing_extensions import TypeVar, deprecated + +from mcp.server.connection import Connection, allowed_log_levels +from mcp.server.session import ServerSession +from mcp.shared.context import BaseContext +from mcp.shared.dispatcher import DispatchContext +from mcp.shared.exceptions import MCPDeprecationWarning +from mcp.shared.message import CloseSSEStreamCallback +from mcp.shared.peer import Meta +from mcp.shared.transport_context import TransportContext + +logger = logging.getLogger(__name__) +# `Context.log`'s `logger` parameter (public API, the spec's logger-name +# field) shadows the module logger inside that method; this alias keeps it +# reachable there. +_logger = logger + +# Invariant: parametrizes a mutable dataclass field; dict default matches the default lifespan. +LifespanContextT = TypeVar("LifespanContextT", default=dict[str, Any]) +RequestT = TypeVar("RequestT", default=Any) + + +@dataclass(kw_only=True) +class ServerRequestContext(Generic[LifespanContextT, RequestT]): + """Per-request context handed to lowlevel request and notification handlers. + + Built by `ServerRunner._make_context` for each inbound message. Carries the + connection-scoped `ServerSession` (server-to-client requests and + notifications), per-request metadata, and any per-message data the + transport attached (the HTTP request, SSE stream-close callbacks). + """ + + session: ServerSession + lifespan_context: LifespanContextT + protocol_version: str + method: str + params: Mapping[str, Any] | None = None + request_id: RequestId | None = None + meta: RequestParamsMeta | None = None + request: RequestT | None = None + close_sse_stream: CloseSSEStreamCallback | None = None + close_standalone_sse_stream: CloseSSEStreamCallback | None = None + + +# Covariant: `lifespan` is exposed read-only, so a `Context[AppState]` passes as `Context[object]`. +LifespanT_co = TypeVar("LifespanT_co", default=Any, covariant=True) + + +class Context(BaseContext[TransportContext], Generic[LifespanT_co]): + """Server-side per-request context. + + Extends `BaseContext` (transport metadata, the raw back-channel, progress + reporting) with `lifespan`, `connection`, and request-scoped `log`. + + Not currently constructed by `ServerRunner`, which hands handlers a + `ServerRequestContext` instead. + """ + + def __init__( + self, + dctx: DispatchContext[TransportContext], + *, + lifespan: LifespanT_co, + connection: Connection, + meta: RequestParamsMeta | None = None, + ) -> None: + super().__init__(dctx, meta=meta) + self._lifespan = lifespan + self._connection = connection + # Same per-request log gate as `ServerSession`: fixed at construction + # from this request's `_meta` log-level opt-in and the connection's era. + self._allowed_log_levels = allowed_log_levels(connection.protocol_version, meta) + + @property + def lifespan(self) -> LifespanT_co: + """The server-wide lifespan output (what `Server(..., lifespan=...)` yielded).""" + return self._lifespan + + @property + def connection(self) -> Connection: + """The per-client `Connection` for this request's connection.""" + return self._connection + + @property + def session_id(self) -> str | None: + """The transport's session id for this connection, when one exists. + + Convenience for `ctx.connection.session_id`. `None` on stdio and + stateless HTTP. + """ + return self._connection.session_id + + @property + def headers(self) -> Mapping[str, str] | None: + """Request headers carried by this message, when the transport has them. + + Convenience for `ctx.transport.headers`. `None` on stdio. + """ + return self.transport.headers + + @deprecated("The logging capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def log(self, level: LoggingLevel, data: Any, logger: str | None = None, *, meta: Meta | None = None) -> None: + """Send a request-scoped `notifications/message` log entry. + + Uses this request's back-channel (so the entry rides the request's SSE + stream in streamable HTTP), not the standalone stream - use + `ctx.connection.log(...)` for that. + + On 2026-07-28+ delivery is a per-request opt-in: nothing is sent + unless this request's `_meta` carried the reserved log-level key, and + entries below the requested level are dropped (debug-logged). + Handshake versions send unconditionally, as before. + """ + if level not in self._allowed_log_levels: + _logger.debug("dropped notifications/message at %r: not opted in at that level on this request", level) + return + params: dict[str, Any] = {"level": level, "data": data} + if logger is not None: + params["logger"] = logger + if meta: + params["_meta"] = meta + await self.notify("notifications/message", params) + + +HandlerResult = BaseModel | dict[str, Any] | None +"""What a request handler (or middleware) may return. `ServerRunner` serializes +all three to a result dict.""" + +CallNext = Callable[["ServerRequestContext[Any, Any]"], Awaitable[HandlerResult]] +"""Invokes the rest of the chain with the given context. What a context +rewrite (`dataclasses.replace(ctx, ...)`) can alter depends on the tier: +`ServerMiddleware` runs before params validation, so its rewrites change what +the handler is invoked with; an `Extension` interceptor runs after, so its +rewrites change only what the handler observes on `ctx`.""" + +_MwLifespanT = TypeVar("_MwLifespanT") + + +class ServerMiddleware(Protocol[_MwLifespanT]): + """Context-tier middleware: `(ctx, call_next) -> result`. + + Runs at the top of `ServerRunner._on_request` / `_on_notify` after `ctx` + is built but before any validation, lookup, or handshake. Wraps every + inbound request and notification: `initialize`, the pre-init gate, + `METHOD_NOT_FOUND`, params validation, the handler call, and + `notifications/initialized` all run inside `call_next(ctx)`. + `notifications/cancelled` is observed too; the dispatcher applies the + cancellation itself, then forwards the notification. A request-side + failure reaches the middleware as a raised `MCPError` (or + `ValidationError` for malformed params) so observation/logging middleware + can record it. Listed outermost-first on `Server.middleware`. + + The method and the raw inbound params are `ctx.method` and `ctx.params` (no + model validation has happened yet). To rewrite either before the handler + runs, pass an adjusted context: `await call_next(replace(ctx, params=...))`. + `ctx.request_id is None` distinguishes a notification from a request. For + notifications `call_next(ctx)` returns `None` (a dropped or unhandled + notification also returns `None`) and the middleware's own return value is + discarded. + + !!! warning + `initialize` is handled inline - the dispatcher does not read + further inbound messages until the middleware chain returns. Awaiting a + server-to-client request (`ctx.session.send_request`, `send_ping`, ...) + while handling `initialize` therefore deadlocks the connection: the + response can never be dequeued. Send-and-forget notifications are safe. + `initialize` is observed but not rewritable: the post-chain handshake + commit reads the wire params, so to veto the handshake raise *before* + `call_next()`. + + `Server[L].middleware` holds `ServerMiddleware[L]`, so an app-specific + middleware sees `ctx.lifespan_context: L`. While the context is the + mutable `ServerRequestContext` dataclass it is invariant in `L`, so a + reusable middleware should be typed `ServerMiddleware[Any]` to register on + any `Server[L]`. + """ + + # TODO(maxisbey): once `_make_context` returns the (covariant) `Context[L]` + # again, restore `_MwLifespanT` to `contravariant=True` and retype `ctx` + # below to `Context[_MwLifespanT]` so reusable middleware can be + # `ServerMiddleware[object]` instead of `ServerMiddleware[Any]`. + + async def __call__( + self, + ctx: ServerRequestContext[_MwLifespanT, Any], + call_next: CallNext, + ) -> HandlerResult: ... diff --git a/venv/lib/python3.12/site-packages/mcp/server/elicitation.py b/venv/lib/python3.12/site-packages/mcp/server/elicitation.py new file mode 100644 index 0000000..26425c1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/elicitation.py @@ -0,0 +1,190 @@ +"""Elicitation utilities for MCP servers.""" + +from __future__ import annotations + +from typing import Any, Generic, Literal, TypeVar + +from mcp_types import RequestId + +# Internal surface package; imported as the gate's source of truth for spec-valid property schemas. +from mcp_types._v2025_11_25 import PrimitiveSchemaDefinition +from pydantic import BaseModel, ValidationError +from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue +from pydantic_core import core_schema +from typing_extensions import TypeAliasType + +from mcp.server.session import ServerSession + +ElicitSchemaModelT = TypeVar("ElicitSchemaModelT", bound=BaseModel) + + +class AcceptedElicitation(BaseModel, Generic[ElicitSchemaModelT]): + """Result when user accepts the elicitation.""" + + action: Literal["accept"] = "accept" + data: ElicitSchemaModelT + + +class DeclinedElicitation(BaseModel): + """Result when user declines the elicitation.""" + + action: Literal["decline"] = "decline" + + +class CancelledElicitation(BaseModel): + """Result when user cancels the elicitation.""" + + action: Literal["cancel"] = "cancel" + + +ElicitationResult = TypeAliasType( + "ElicitationResult", + AcceptedElicitation[ElicitSchemaModelT] | DeclinedElicitation | CancelledElicitation, + type_params=(ElicitSchemaModelT,), +) + + +class AcceptedUrlElicitation(BaseModel): + """Result when user accepts a URL mode elicitation.""" + + action: Literal["accept"] = "accept" + + +UrlElicitationResult = AcceptedUrlElicitation | DeclinedElicitation | CancelledElicitation + + +class _ElicitationJsonSchema(GenerateJsonSchema): + """JSON-Schema generator that flattens `T | None` to `T` and drops `None` defaults. + + The spec's `PrimitiveSchemaDefinition` admits no `anyOf` or null type; an + optional field is expressed by leaving it out of `required`, which pydantic + already does for any field with a default. + """ + + def nullable_schema(self, schema: core_schema.NullableSchema) -> JsonSchemaValue: + return self.generate_inner(schema["schema"]) + + def default_schema(self, schema: core_schema.WithDefaultSchema) -> JsonSchemaValue: + result = super().default_schema(schema) + if result.get("default") is None: + result.pop("default", None) + return result + + +def _validate_rendered_properties(json_schema: dict[str, Any]) -> None: + """Reject any `properties` entry the spec's `PrimitiveSchemaDefinition` won't accept. + + Catches whatever the renderer let through that isn't spec-valid: bare + `list[str]` (no enum), multi-primitive unions, nested models. + """ + for field_name, prop in json_schema.get("properties", {}).items(): + try: + PrimitiveSchemaDefinition.model_validate(prop) + except ValidationError: + raise TypeError( + f"Elicitation schema field {field_name!r} rendered as {prop!r}, " + f"which is not a valid PrimitiveSchemaDefinition" + ) from None + + +def render_elicitation_schema(schema: type[BaseModel]) -> dict[str, Any]: + """Render a model as the spec-valid `requested_schema` for an elicitation. + + Raises: + TypeError: If a field renders as something the spec's + `PrimitiveSchemaDefinition` does not accept. + """ + json_schema = schema.model_json_schema(schema_generator=_ElicitationJsonSchema) + _validate_rendered_properties(json_schema) + return json_schema + + +async def elicit_with_validation( + session: ServerSession, + message: str, + schema: type[ElicitSchemaModelT], + related_request_id: RequestId | None = None, +) -> ElicitationResult[ElicitSchemaModelT]: + """Elicit information from the client/user with schema validation (form mode). + + This method can be used to interactively ask for additional information from the + client within a tool's execution. The client might display the message to the + user and collect a response according to the provided schema. If the client + is an agent, it might decide how to handle the elicitation -- either by asking + the user or automatically generating a response. + + For sensitive data like credentials or OAuth flows, use elicit_url() instead. + + Raises: + ValueError: If the client accepted the elicitation without supplying + content, or with content that does not match the requested schema. + """ + json_schema = render_elicitation_schema(schema) + + result = await session.elicit_form( + message=message, + requested_schema=json_schema, + related_request_id=related_request_id, + ) + + if result.action == "accept": + if result.content is None: + raise ValueError("Received an accepted elicitation with no content") + try: + validated_data = schema.model_validate(result.content) + except ValidationError as e: + raise ValueError( + "Received an accepted elicitation whose content does not match the requested schema" + ) from e + return AcceptedElicitation(data=validated_data) + if result.action == "decline": + return DeclinedElicitation() + return CancelledElicitation() + + +async def elicit_url( + session: ServerSession, + message: str, + url: str, + elicitation_id: str, + related_request_id: RequestId | None = None, +) -> UrlElicitationResult: + """Elicit information from the user via out-of-band URL navigation (URL mode). + + This method directs the user to an external URL where sensitive interactions can + occur without passing data through the MCP client. Use this for: + - Collecting sensitive credentials (API keys, passwords) + - OAuth authorization flows with third-party services + - Payment and subscription flows + - Any interaction where data should not pass through the LLM context + + The response indicates whether the user consented to navigate to the URL. + The actual interaction happens out-of-band. When the elicitation completes, + the server should send an ElicitCompleteNotification to notify the client. + + Args: + session: The server session + message: Human-readable explanation of why the interaction is needed + url: The URL the user should navigate to + elicitation_id: Unique identifier for tracking this elicitation + related_request_id: Optional ID of the request that triggered this elicitation + + Returns: + UrlElicitationResult indicating accept, decline, or cancel + """ + result = await session.elicit_url( + message=message, + url=url, + elicitation_id=elicitation_id, + related_request_id=related_request_id, + ) + + if result.action == "accept": + return AcceptedUrlElicitation() + elif result.action == "decline": + return DeclinedElicitation() + elif result.action == "cancel": + return CancelledElicitation() + else: # pragma: no cover + # This should never happen, but handle it just in case + raise ValueError(f"Unexpected elicitation action: {result.action}") diff --git a/venv/lib/python3.12/site-packages/mcp/server/extension.py b/venv/lib/python3.12/site-packages/mcp/server/extension.py new file mode 100644 index 0000000..6205a70 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/extension.py @@ -0,0 +1,185 @@ +"""Pluggable extension interface for MCP servers (SEP-2133). + +An extension is a self-contained, opt-in bundle of MCP behaviour, identified by +a reverse-DNS string (e.g. `io.modelcontextprotocol/ui`). It is passed to +`MCPServer(extensions=[...])`, and the server applies a *closed* set of +contribution kinds: tools, resources, new request methods, and one `tools/call` +interceptor. The server never hands itself to an extension; the extension +declares what it adds, and the server consumes it. + +The shape follows the httpx2 `Transport`/`Auth` pattern: a narrow base class whose +methods have sensible defaults, so an extension overrides only what it needs. A +purely additive extension (Apps) overrides `tools`/`resources`; an interceptive +one overrides `methods`/`intercept_tool_call`. + +This module lives at the `mcp.server` tier (not `mcp.server.mcpserver`) so the +base class itself never drags in the composition tier that consumes it; +extensions remain importable without constructing an `MCPServer`. +""" + +from __future__ import annotations + +from collections.abc import Awaitable, Callable, Sequence +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any + +from mcp_types import CallToolRequestParams +from mcp_types.methods import SPEC_CLIENT_METHODS +from pydantic import BaseModel + +from mcp.server.context import CallNext, HandlerResult, ServerRequestContext + +# Re-exported from `mcp.shared.extension` (shared with the client surface) for existing importers. +from mcp.shared.extension import validate_extension_identifier as validate_extension_identifier + +if TYPE_CHECKING: + from mcp.server.mcpserver.resources import Resource + +RequestHandler = Callable[[ServerRequestContext[Any, Any], Any], Awaitable[HandlerResult]] + + +@dataclass(frozen=True) +class ToolBinding: + """A tool an extension contributes, plus the `_meta` to stamp on it.""" + + fn: Callable[..., Any] + meta: dict[str, Any] | None = None + kwargs: dict[str, Any] = field(default_factory=lambda: {}) + + +@dataclass(frozen=True) +class ResourceBinding: + """A pre-built resource an extension contributes.""" + + resource: Resource + + +@dataclass(frozen=True) +class MethodBinding: + """A new request method an extension serves, e.g. `tasks/get`. + + `params_type` validates incoming params before `handler` runs; it should + subclass `RequestParams` so `_meta` parses uniformly. `protocol_versions`, + when set, restricts the method to those wire versions - a request for the + method at any other version is rejected as `METHOD_NOT_FOUND`, mirroring the + spec's `(method, version)` boundary table. `None` (the default) admits the + method at every version. + + Extension methods are additive: `method` must not name a spec-defined + request method (`tools/list`, `completion/complete`, ...) — those handlers + belong to the server, and an extension binding one would silently shadow or + be shadowed by it. Both constraints are enforced at construction. To + re-provide a spec method the 2026 revision removed (e.g. `logging/setLevel` + for legacy clients), use the lowlevel `Server.add_request_handler` API + instead — the runner's per-version surface gate would never route such a + method to an extension handler anyway. + """ + + method: str + params_type: type[BaseModel] + handler: RequestHandler + protocol_versions: frozenset[str] | None = None + + def __post_init__(self) -> None: + if self.method in SPEC_CLIENT_METHODS: + raise ValueError( + f"MethodBinding cannot bind spec method {self.method!r}; extension methods are " + "additive — use Extension.intercept_tool_call or Server.middleware to wrap core behaviour" + ) + if self.protocol_versions is not None and not self.protocol_versions: + raise ValueError( + f"MethodBinding for {self.method!r} has an empty protocol_versions set, so it could " + "never be served; use None to admit every version" + ) + + +class Extension: + """Base class for an opt-in MCP extension. Override only the methods you need. + + Subclass and set `identifier`, then override the contribution methods that + apply. Every method has a default, so a minimal extension overrides nothing + but `identifier` and one of `tools`/`resources`/`methods`. `identifier` is + enforced at subclass-definition time. + """ + + #: Reverse-DNS extension identifier, advertised under `ServerCapabilities.extensions`. + identifier: str + + def __init_subclass__(cls, **kwargs: Any) -> None: + super().__init_subclass__(**kwargs) + # Validate a class-level `identifier` at definition time. A subclass may + # instead assign `identifier` in `__init__` (per-instance ids); that case + # is validated when the extension is applied, since no class attribute + # exists to inspect here. + identifier = cls.__dict__.get("identifier") + if identifier is not None: + validate_extension_identifier(identifier, owner=cls.__name__) + + def settings(self) -> dict[str, Any]: + """Per-extension settings advertised at `capabilities.extensions[identifier]`. + + An empty dict (the default) advertises the extension with no settings. + """ + return {} + + def tools(self) -> Sequence[ToolBinding]: + """Tools this extension contributes (additive).""" + return () + + def resources(self) -> Sequence[ResourceBinding]: + """Resources this extension contributes (additive).""" + return () + + def methods(self) -> Sequence[MethodBinding]: + """New request methods this extension serves (additive).""" + return () + + async def intercept_tool_call( + self, + params: CallToolRequestParams, + ctx: ServerRequestContext[Any, Any], + call_next: CallNext, + ) -> HandlerResult: + """Wrap `tools/call`. Default: pass through unchanged. + + Override to short-circuit (return a result without calling `call_next`) + or to observe the call. `params` is the validated `tools/call` params; + `call_next(ctx)` runs the rest of the chain and the real handler, and + returns the handler's domain result. Interceptors run at the handler + layer: whatever they return is serialized like any handler result, + including the 2026-era `serverInfo` `_meta` stamp. The `params` this + interceptor received is what the wrapped handler is invoked with - + passing a rewritten context through `call_next` adjusts what the + handler observes on `ctx`, not the tool invocation. Wire-level request + rewriting belongs to `Server.middleware`, above params validation. + """ + return await call_next(ctx) + + +def compose_tool_call_handler(extensions: Sequence[Extension], handler: RequestHandler) -> RequestHandler: + """Fold every extension's `intercept_tool_call` around the `tools/call` handler. + + The returned handler nests the interceptors (first extension outermost) and + replaces the plain `tools/call` registration. Interception happens at the + handler layer, below the runner's outbound envelope pass, so a + short-circuiting interceptor's result is sieved and stamped exactly like + the wrapped handler's would be. + """ + + async def wrapped(ctx: ServerRequestContext[Any, Any], params: CallToolRequestParams) -> HandlerResult: + async def innermost(inner_ctx: ServerRequestContext[Any, Any]) -> HandlerResult: + return await handler(inner_ctx, params) + + chain: CallNext = innermost + for extension in reversed(extensions): + chain = _bind_interceptor(extension, params, chain) + return await chain(ctx) + + return wrapped + + +def _bind_interceptor(extension: Extension, params: CallToolRequestParams, call_next: CallNext) -> CallNext: + async def call(ctx: ServerRequestContext[Any, Any]) -> HandlerResult: + return await extension.intercept_tool_call(params, ctx, call_next) + + return call diff --git a/venv/lib/python3.12/site-packages/mcp/server/fastmcp.py b/venv/lib/python3.12/site-packages/mcp/server/fastmcp.py new file mode 100644 index 0000000..7dd4646 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/fastmcp.py @@ -0,0 +1,16 @@ +"""Removed in mcp 2: `FastMCP` is now `mcp.server.mcpserver.MCPServer`. + +This module has no API. Importing it, or anything below it, raises +`ModuleNotFoundError` with a message that points at the migration guide. It +exists only because the bare "No module named 'mcp.server.fastmcp'" gave v1 +code no hint that the installed SDK is a different major version. +""" + +_MESSAGE = ( + "No module named 'mcp.server.fastmcp'. This is mcp 2.x, where FastMCP was renamed to MCPServer " + "(from mcp.server.mcpserver import MCPServer) and other APIs changed; see the migration guide at " + "https://py.sdk.modelcontextprotocol.io/v2/migration/#fastmcp-renamed-to-mcpserver " + "or pin 'mcp<2' to keep running v1 code." +) + +raise ModuleNotFoundError(_MESSAGE, name=__name__) diff --git a/venv/lib/python3.12/site-packages/mcp/server/lowlevel/__init__.py b/venv/lib/python3.12/site-packages/mcp/server/lowlevel/__init__.py new file mode 100644 index 0000000..37191ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/lowlevel/__init__.py @@ -0,0 +1,3 @@ +from .server import NotificationOptions, Server + +__all__ = ["NotificationOptions", "Server"] diff --git a/venv/lib/python3.12/site-packages/mcp/server/lowlevel/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/lowlevel/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7df45c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/lowlevel/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/lowlevel/__pycache__/helper_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/lowlevel/__pycache__/helper_types.cpython-312.pyc new file mode 100644 index 0000000..f1cf66e Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/lowlevel/__pycache__/helper_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/lowlevel/__pycache__/server.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/lowlevel/__pycache__/server.cpython-312.pyc new file mode 100644 index 0000000..2563708 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/lowlevel/__pycache__/server.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/lowlevel/helper_types.py b/venv/lib/python3.12/site-packages/mcp/server/lowlevel/helper_types.py new file mode 100644 index 0000000..fecc716 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/lowlevel/helper_types.py @@ -0,0 +1,11 @@ +from dataclasses import dataclass +from typing import Any + + +@dataclass +class ReadResourceContents: + """Contents returned from a read_resource call.""" + + content: str | bytes + mime_type: str | None = None + meta: dict[str, Any] | None = None diff --git a/venv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py b/venv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py new file mode 100644 index 0000000..4c327f4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py @@ -0,0 +1,829 @@ +"""MCP Server Module + +This module provides a framework for creating an MCP (Model Context Protocol) server. +It allows you to easily define and handle various types of requests and notifications +using constructor-based handler registration. + +Usage: +1. Define handler functions: + async def my_list_tools(ctx, params): + return types.ListToolsResult(tools=[...]) + + async def my_call_tool(ctx, params): + return types.CallToolResult(content=[...]) + +2. Create a Server instance with on_* handlers: + server = Server( + "your_server_name", + on_list_tools=my_list_tools, + on_call_tool=my_call_tool, + ) + +3. Run the server: + async def main(): + async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): + await server.run( + read_stream, + write_stream, + server.create_initialization_options(), + ) + + asyncio.run(main()) + +The Server class dispatches incoming requests and notifications to registered +handler callables by method string. +""" + +from __future__ import annotations + +import copy +import logging +import warnings +from collections.abc import AsyncIterator, Awaitable, Callable, Mapping +from contextlib import AbstractAsyncContextManager, asynccontextmanager +from dataclasses import dataclass +from functools import cached_property +from typing import Any, Generic, overload + +import mcp_types as types +from mcp_types.version import MODERN_PROTOCOL_VERSIONS +from pydantic import BaseModel +from starlette.applications import Starlette +from starlette.middleware import Middleware +from starlette.middleware.authentication import AuthenticationMiddleware +from starlette.routing import Mount, Route +from typing_extensions import TypeVar, deprecated + +from mcp.server._otel import OpenTelemetryMiddleware +from mcp.server.auth.middleware.auth_context import AuthContextMiddleware +from mcp.server.auth.middleware.bearer_auth import BearerAuthBackend, RequireAuthMiddleware +from mcp.server.auth.provider import OAuthAuthorizationServerProvider, TokenVerifier +from mcp.server.auth.routes import build_resource_metadata_url, create_auth_routes, create_protected_resource_routes +from mcp.server.auth.settings import AuthSettings +from mcp.server.caching import CacheableMethod, CacheHint, validate_cache_hints +from mcp.server.context import HandlerResult, ServerMiddleware, ServerRequestContext +from mcp.server.models import InitializationOptions +from mcp.server.runner import serve_dual_era_loop +from mcp.server.streamable_http import EventStore +from mcp.server.streamable_http_manager import StreamableHTTPASGIApp, StreamableHTTPSessionManager +from mcp.server.transport_security import DEFAULT_MAX_REQUEST_BODY_SIZE, TransportSecuritySettings +from mcp.shared._stream_protocols import ReadStream, WriteStream +from mcp.shared.exceptions import MCPDeprecationWarning +from mcp.shared.message import SessionMessage + +logger = logging.getLogger(__name__) + +LifespanResultT = TypeVar("LifespanResultT", default=Any) + +_ParamsT = TypeVar("_ParamsT", bound=BaseModel, default=BaseModel) + +RequestHandler = Callable[[ServerRequestContext[LifespanResultT], _ParamsT], Awaitable[HandlerResult]] +"""A registered request handler: `(ctx, params) -> result`.""" + +NotificationHandler = Callable[[ServerRequestContext[LifespanResultT], _ParamsT], Awaitable[None]] +"""A registered notification handler: `(ctx, params) -> None`.""" + + +@dataclass(frozen=True, slots=True) +class HandlerEntry(Generic[LifespanResultT]): + """A registered handler and the params model to validate incoming params against. + + Stored in `Server._request_handlers` / `_notification_handlers` and consumed + by `ServerRunner` to validate, build `Context`, and invoke. The handler's + second-argument type is erased to `Any` in storage (each entry has a + different concrete params type and `Callable` parameters are contravariant); + the precise type is recoverable via `params_type`. The correlation is + enforced at registration time by `Server.add_request_handler`. + """ + + params_type: type[BaseModel] + handler: RequestHandler[LifespanResultT, Any] + + +class NotificationOptions: + def __init__(self, prompts_changed: bool = False, resources_changed: bool = False, tools_changed: bool = False): + self.prompts_changed = prompts_changed + self.resources_changed = resources_changed + self.tools_changed = tools_changed + + +@asynccontextmanager +async def lifespan(_: Server[Any]) -> AsyncIterator[dict[str, Any]]: + """Default lifespan context manager that does nothing. + + Returns: + An empty context object + """ + yield {} + + +async def _ping_handler(ctx: ServerRequestContext[Any], params: types.RequestParams | None) -> types.EmptyResult: + return types.EmptyResult() + + +class Server(Generic[LifespanResultT]): + @overload + def __init__( + self, + name: str, + *, + version: str = "", + title: str | None = None, + description: str | None = None, + instructions: str | None = None, + website_url: str | None = None, + icons: list[types.Icon] | None = None, + cache_hints: Mapping[CacheableMethod, CacheHint] | None = None, + lifespan: Callable[ + [Server[LifespanResultT]], + AbstractAsyncContextManager[LifespanResultT], + ] = lifespan, + # Request handlers + on_list_tools: Callable[ + [ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None], + Awaitable[types.ListToolsResult], + ] + | None = None, + on_call_tool: Callable[ + [ServerRequestContext[LifespanResultT], types.CallToolRequestParams], + Awaitable[types.CallToolResult | types.InputRequiredResult], + ] + | None = None, + on_list_resources: Callable[ + [ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None], + Awaitable[types.ListResourcesResult], + ] + | None = None, + on_list_resource_templates: Callable[ + [ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None], + Awaitable[types.ListResourceTemplatesResult], + ] + | None = None, + on_read_resource: Callable[ + [ServerRequestContext[LifespanResultT], types.ReadResourceRequestParams], + Awaitable[types.ReadResourceResult | types.InputRequiredResult], + ] + | None = None, + on_subscribe_resource: Callable[ + [ServerRequestContext[LifespanResultT], types.SubscribeRequestParams], + Awaitable[types.EmptyResult], + ] + | None = None, + on_unsubscribe_resource: Callable[ + [ServerRequestContext[LifespanResultT], types.UnsubscribeRequestParams], + Awaitable[types.EmptyResult], + ] + | None = None, + on_subscriptions_listen: Callable[ + [ServerRequestContext[LifespanResultT], types.SubscriptionsListenRequestParams], + Awaitable[types.SubscriptionsListenResult], + ] + | None = None, + on_list_prompts: Callable[ + [ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None], + Awaitable[types.ListPromptsResult], + ] + | None = None, + on_get_prompt: Callable[ + [ServerRequestContext[LifespanResultT], types.GetPromptRequestParams], + Awaitable[types.GetPromptResult | types.InputRequiredResult], + ] + | None = None, + on_completion: Callable[ + [ServerRequestContext[LifespanResultT], types.CompleteRequestParams], + Awaitable[types.CompleteResult], + ] + | None = None, + on_ping: Callable[ + [ServerRequestContext[LifespanResultT], types.RequestParams | None], + Awaitable[types.EmptyResult], + ] = _ping_handler, + ) -> None: ... + @overload + @deprecated( + "on_set_logging_level (Logging) and on_roots_list_changed (Roots) are deprecated as of 2026-07-28 " + "(SEP-2577); on_progress (client-to-server progress) is deprecated as of 2026-07-28. Passing any of " + "them emits an MCPDeprecationWarning at runtime.", + category=MCPDeprecationWarning, + ) + def __init__( + self, + name: str, + *, + version: str = "", + title: str | None = None, + description: str | None = None, + instructions: str | None = None, + website_url: str | None = None, + icons: list[types.Icon] | None = None, + cache_hints: Mapping[CacheableMethod, CacheHint] | None = None, + lifespan: Callable[ + [Server[LifespanResultT]], + AbstractAsyncContextManager[LifespanResultT], + ] = lifespan, + # Request handlers + on_list_tools: Callable[ + [ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None], + Awaitable[types.ListToolsResult], + ] + | None = None, + on_call_tool: Callable[ + [ServerRequestContext[LifespanResultT], types.CallToolRequestParams], + Awaitable[types.CallToolResult | types.InputRequiredResult], + ] + | None = None, + on_list_resources: Callable[ + [ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None], + Awaitable[types.ListResourcesResult], + ] + | None = None, + on_list_resource_templates: Callable[ + [ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None], + Awaitable[types.ListResourceTemplatesResult], + ] + | None = None, + on_read_resource: Callable[ + [ServerRequestContext[LifespanResultT], types.ReadResourceRequestParams], + Awaitable[types.ReadResourceResult | types.InputRequiredResult], + ] + | None = None, + on_subscribe_resource: Callable[ + [ServerRequestContext[LifespanResultT], types.SubscribeRequestParams], + Awaitable[types.EmptyResult], + ] + | None = None, + on_unsubscribe_resource: Callable[ + [ServerRequestContext[LifespanResultT], types.UnsubscribeRequestParams], + Awaitable[types.EmptyResult], + ] + | None = None, + on_subscriptions_listen: Callable[ + [ServerRequestContext[LifespanResultT], types.SubscriptionsListenRequestParams], + Awaitable[types.SubscriptionsListenResult], + ] + | None = None, + on_list_prompts: Callable[ + [ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None], + Awaitable[types.ListPromptsResult], + ] + | None = None, + on_get_prompt: Callable[ + [ServerRequestContext[LifespanResultT], types.GetPromptRequestParams], + Awaitable[types.GetPromptResult | types.InputRequiredResult], + ] + | None = None, + on_completion: Callable[ + [ServerRequestContext[LifespanResultT], types.CompleteRequestParams], + Awaitable[types.CompleteResult], + ] + | None = None, + on_set_logging_level: Callable[ + [ServerRequestContext[LifespanResultT], types.SetLevelRequestParams], + Awaitable[types.EmptyResult], + ] + | None = None, + on_ping: Callable[ + [ServerRequestContext[LifespanResultT], types.RequestParams | None], + Awaitable[types.EmptyResult], + ] = _ping_handler, + # Notification handlers + on_roots_list_changed: Callable[ + [ServerRequestContext[LifespanResultT], types.NotificationParams | None], + Awaitable[None], + ] + | None = None, + on_progress: Callable[ + [ServerRequestContext[LifespanResultT], types.ProgressNotificationParams], + Awaitable[None], + ] + | None = None, + ) -> None: ... + def __init__( + self, + name: str, + *, + version: str = "", + title: str | None = None, + description: str | None = None, + instructions: str | None = None, + website_url: str | None = None, + icons: list[types.Icon] | None = None, + cache_hints: Mapping[CacheableMethod, CacheHint] | None = None, + lifespan: Callable[ + [Server[LifespanResultT]], + AbstractAsyncContextManager[LifespanResultT], + ] = lifespan, + # Request handlers + on_list_tools: Callable[ + [ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None], + Awaitable[types.ListToolsResult], + ] + | None = None, + on_call_tool: Callable[ + [ServerRequestContext[LifespanResultT], types.CallToolRequestParams], + Awaitable[types.CallToolResult | types.InputRequiredResult], + ] + | None = None, + on_list_resources: Callable[ + [ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None], + Awaitable[types.ListResourcesResult], + ] + | None = None, + on_list_resource_templates: Callable[ + [ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None], + Awaitable[types.ListResourceTemplatesResult], + ] + | None = None, + on_read_resource: Callable[ + [ServerRequestContext[LifespanResultT], types.ReadResourceRequestParams], + Awaitable[types.ReadResourceResult | types.InputRequiredResult], + ] + | None = None, + on_subscribe_resource: Callable[ + [ServerRequestContext[LifespanResultT], types.SubscribeRequestParams], + Awaitable[types.EmptyResult], + ] + | None = None, + on_unsubscribe_resource: Callable[ + [ServerRequestContext[LifespanResultT], types.UnsubscribeRequestParams], + Awaitable[types.EmptyResult], + ] + | None = None, + on_subscriptions_listen: Callable[ + [ServerRequestContext[LifespanResultT], types.SubscriptionsListenRequestParams], + Awaitable[types.SubscriptionsListenResult], + ] + | None = None, + on_list_prompts: Callable[ + [ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None], + Awaitable[types.ListPromptsResult], + ] + | None = None, + on_get_prompt: Callable[ + [ServerRequestContext[LifespanResultT], types.GetPromptRequestParams], + Awaitable[types.GetPromptResult | types.InputRequiredResult], + ] + | None = None, + on_completion: Callable[ + [ServerRequestContext[LifespanResultT], types.CompleteRequestParams], + Awaitable[types.CompleteResult], + ] + | None = None, + on_set_logging_level: Callable[ + [ServerRequestContext[LifespanResultT], types.SetLevelRequestParams], + Awaitable[types.EmptyResult], + ] + | None = None, + on_ping: Callable[ + [ServerRequestContext[LifespanResultT], types.RequestParams | None], + Awaitable[types.EmptyResult], + ] = _ping_handler, + # Notification handlers + on_roots_list_changed: Callable[ + [ServerRequestContext[LifespanResultT], types.NotificationParams | None], + Awaitable[None], + ] + | None = None, + on_progress: Callable[ + [ServerRequestContext[LifespanResultT], types.ProgressNotificationParams], + Awaitable[None], + ] + | None = None, + ) -> None: + if on_set_logging_level is not None: + warnings.warn( + "The logging capability is deprecated as of 2026-07-28 (SEP-2577).", + MCPDeprecationWarning, + stacklevel=2, + ) + if on_roots_list_changed is not None: + warnings.warn( + "The roots capability is deprecated as of 2026-07-28 (SEP-2577).", + MCPDeprecationWarning, + stacklevel=2, + ) + if on_progress is not None: + warnings.warn( + "Client-to-server progress is deprecated as of 2026-07-28.", + MCPDeprecationWarning, + stacklevel=2, + ) + + self.name = name + self.version = version + self.title = title + self.description = description + self.instructions = instructions + self.website_url = website_url + self.icons = icons + # Per-method `ttl_ms`/`cache_scope` fills, applied by `ServerRunner` + # after the handler returns; fields the handler set explicitly win. + self.cache_hints: dict[str, CacheHint] = validate_cache_hints(cache_hints) + self.lifespan = lifespan + self._request_handlers: dict[str, HandlerEntry[LifespanResultT]] = {} + self._notification_handlers: dict[str, HandlerEntry[LifespanResultT]] = {} + self._session_manager: StreamableHTTPSessionManager | None = None + # Context-tier middleware: wraps every inbound request (including + # `initialize`, lookup, validation, handler) with + # `(ctx, call_next)`. Applied in `ServerRunner._on_request`. + # `OpenTelemetryMiddleware` ships on by default so every server emits a + # SERVER span per message; it is a no-op until an OTel exporter is + # installed. Drop it from this list to opt out. + # TODO(L54): provisional - signature and semantics may change in a 2.x + # minor release with the Context/middleware rework (covariant + # `Context[L]`, outbound seam). + self.middleware: list[ServerMiddleware[LifespanResultT]] = [OpenTelemetryMiddleware()] + # SEP-2133 extension settings advertised under `ServerCapabilities.extensions` + # (identifier -> settings). Higher layers (e.g. `MCPServer(extensions=...)`) + # populate it; `get_capabilities` reads it when no explicit map is passed. + self.extensions: dict[str, dict[str, Any]] = {} + logger.debug("Initializing server %r", name) + + _spec_requests: list[tuple[str, type[BaseModel], RequestHandler[LifespanResultT, Any] | None]] = [ + ("ping", types.RequestParams, on_ping), + ("server/discover", types.RequestParams, self._handle_discover), + ("prompts/list", types.PaginatedRequestParams, on_list_prompts), + ("prompts/get", types.GetPromptRequestParams, on_get_prompt), + ("resources/list", types.PaginatedRequestParams, on_list_resources), + ("resources/templates/list", types.PaginatedRequestParams, on_list_resource_templates), + ("resources/read", types.ReadResourceRequestParams, on_read_resource), + ("resources/subscribe", types.SubscribeRequestParams, on_subscribe_resource), + ("resources/unsubscribe", types.UnsubscribeRequestParams, on_unsubscribe_resource), + ("subscriptions/listen", types.SubscriptionsListenRequestParams, on_subscriptions_listen), + ("tools/list", types.PaginatedRequestParams, on_list_tools), + ("tools/call", types.CallToolRequestParams, on_call_tool), + ("logging/setLevel", types.SetLevelRequestParams, on_set_logging_level), + ("completion/complete", types.CompleteRequestParams, on_completion), + ] + self._request_handlers.update({m: HandlerEntry(pt, h) for m, pt, h in _spec_requests if h is not None}) + + _spec_notifications: list[tuple[str, type[BaseModel], NotificationHandler[LifespanResultT, Any] | None]] = [ + ("notifications/roots/list_changed", types.NotificationParams, on_roots_list_changed), + ("notifications/progress", types.ProgressNotificationParams, on_progress), + ] + self._notification_handlers.update( + {m: HandlerEntry(pt, h) for m, pt, h in _spec_notifications if h is not None} + ) + + def add_request_handler( + self, + method: str, + params_type: type[_ParamsT], + handler: RequestHandler[LifespanResultT, _ParamsT], + ) -> None: + """Register a request handler for `method`. + + `params_type` is the model incoming params are validated against + before the handler is invoked. It should subclass `RequestParams` so + `_meta` parses uniformly. A message with no `params` member validates + `{}` against `params_type`: models with required fields reject it as + INVALID_PARAMS, all-optional models reach the handler with their + defaults - the handler never receives `None`. Replaces any existing + handler for the same method, except `initialize`, which is reserved: + the runner owns the handshake, so registering it raises `ValueError`. + Use `Server.middleware` to observe or wrap initialization. + """ + if method == "initialize": + raise ValueError( + "'initialize' is handled by the server runner and cannot be overridden; " + "use Server.middleware to observe or wrap initialization" + ) + self._request_handlers[method] = HandlerEntry(params_type, handler) + + def add_notification_handler( + self, + method: str, + params_type: type[_ParamsT], + handler: NotificationHandler[LifespanResultT, _ParamsT], + ) -> None: + """Register a notification handler for `method`. + + `params_type` should subclass `NotificationParams` so `_meta` + parses uniformly. Absent params follow the same contract as requests: + `{}` is validated, so the handler receives the model with its defaults, + never `None`. Replaces any existing handler. A handler for + `notifications/initialized` runs after the runner has marked the + connection initialized. + """ + self._notification_handlers[method] = HandlerEntry(params_type, handler) + + def get_request_handler(self, method: str) -> HandlerEntry[LifespanResultT] | None: + """Return the registered entry for a request method, or `None`.""" + return self._request_handlers.get(method) + + def get_notification_handler(self, method: str) -> HandlerEntry[LifespanResultT] | None: + """Return the registered entry for a notification method, or `None`.""" + return self._notification_handlers.get(method) + + # TODO(L53): Rethink capabilities API. Currently capabilities are derived from registered + # handlers but require NotificationOptions to be passed externally for list_changed + # flags, and experimental_capabilities as a separate dict. Consider deriving capabilities + # entirely from server state (e.g. constructor params for list_changed) instead of + # requiring callers to assemble them at create_initialization_options() time. + def create_initialization_options( + self, + notification_options: NotificationOptions | None = None, + experimental_capabilities: dict[str, dict[str, Any]] | None = None, + extensions: dict[str, dict[str, Any]] | None = None, + ) -> InitializationOptions: + """Create initialization options from this server instance. + + `extensions` advertises SEP-2133 extension support under + `ServerCapabilities.extensions`; keys are extension identifiers (e.g. + `io.modelcontextprotocol/ui`), values are per-extension settings. + Defaults to `self.extensions`, which higher layers populate. + """ + return InitializationOptions( + server_name=self.name, + server_version=self.version, + title=self.title, + description=self.description, + capabilities=self.get_capabilities( + notification_options or NotificationOptions(), + experimental_capabilities or {}, + extensions if extensions is not None else self.extensions, + ), + instructions=self.instructions, + website_url=self.website_url, + icons=self.icons, + ) + + def get_capabilities( + self, + notification_options: NotificationOptions | None = None, + experimental_capabilities: dict[str, dict[str, Any]] | None = None, + extensions: dict[str, dict[str, Any]] | None = None, + *, + protocol_version: str | None = None, + ) -> types.ServerCapabilities: + """Convert existing handlers to a ServerCapabilities object. + + `extensions` is the SEP-2133 extension map (identifier -> settings) + advertised under `ServerCapabilities.extensions`; it defaults to + `self.extensions`. + + `protocol_version` makes the subscription-delivered bits era-honest: + at 2026-07-28+ versions, change notifications are delivered only on + `subscriptions/listen` streams, so the `listChanged` flags and + `resources.subscribe` derive from whether that method is served - + `notification_options` and the legacy `resources/subscribe` handler + (which the modern wire cannot dispatch) are ignored. When omitted, the + handshake-era derivation applies unchanged. + """ + notification_options = notification_options or NotificationOptions() + prompts_capability = None + resources_capability = None + tools_capability = None + logging_capability = None + completions_capability = None + + if protocol_version in MODERN_PROTOCOL_VERSIONS: + listen_served = "subscriptions/listen" in self._request_handlers + prompts_changed = tools_changed = resources_changed = subscribe = listen_served + else: + prompts_changed = notification_options.prompts_changed + tools_changed = notification_options.tools_changed + resources_changed = notification_options.resources_changed + subscribe = "resources/subscribe" in self._request_handlers + + # Set prompt capabilities if handler exists + if "prompts/list" in self._request_handlers: + prompts_capability = types.PromptsCapability(list_changed=prompts_changed) + + # Set resource capabilities if handler exists + if "resources/list" in self._request_handlers: + resources_capability = types.ResourcesCapability( + subscribe=subscribe, + list_changed=resources_changed, + ) + + # Set tool capabilities if handler exists + if "tools/list" in self._request_handlers: + tools_capability = types.ToolsCapability(list_changed=tools_changed) + + # Set logging capabilities if handler exists + if "logging/setLevel" in self._request_handlers: + logging_capability = types.LoggingCapability() + + # Set completions capabilities if handler exists + if "completion/complete" in self._request_handlers: + completions_capability = types.CompletionsCapability() + + capabilities = types.ServerCapabilities( + prompts=prompts_capability, + resources=resources_capability, + tools=tools_capability, + logging=logging_capability, + experimental=experimental_capabilities, + extensions=extensions if extensions is not None else (self.extensions or None), + completions=completions_capability, + ) + return capabilities + + @property + def server_info(self) -> types.Implementation: + """The `serverInfo` block describing this implementation. + + Derived from the constructor's identity fields. An unversioned server + reports an empty `version`; the SDK never substitutes its own. + """ + return types.Implementation( + name=self.name, + version=self.version, + title=self.title, + description=self.description, + website_url=self.website_url, + icons=self.icons, + ) + + @cached_property + def _server_info_stamp_source(self) -> dict[str, Any]: + # Identity is fixed at construction, so the dump is computed once per + # server instead of per request. Never handed out directly: nested + # values (`icons`) would alias the cache into stamped responses. + return self.server_info.model_dump(by_alias=True, mode="json", exclude_none=True) + + @property + def server_info_stamp(self) -> dict[str, Any]: + """A fresh wire dump of `server_info`; callers own the returned dict. + + Each access materializes a deep copy of the once-per-server dump, so + a caller mutating a stamped response can never corrupt the identity + stamped into later responses. + """ + return copy.deepcopy(self._server_info_stamp_source) + + async def _handle_discover( + self, ctx: ServerRequestContext[LifespanResultT], params: types.RequestParams | None + ) -> types.DiscoverResult: + """Default `server/discover` handler. + + Auto-derived from server state at call time, so capabilities reflect + whatever has been registered (constructor `on_*` kwargs and later + `add_request_handler` calls). Operators can replace it wholesale via + `add_request_handler("server/discover", ...)`. Reachability for legacy + peers is decided at the boundary (`types.methods`), not here. + """ + return types.DiscoverResult( + supported_versions=list(MODERN_PROTOCOL_VERSIONS), + capabilities=self.get_capabilities(protocol_version=ctx.protocol_version), + instructions=self.instructions, + ) + + @property + def session_manager(self) -> StreamableHTTPSessionManager: + """Get the StreamableHTTP session manager. + + Raises: + RuntimeError: If called before streamable_http_app() has been called. + """ + if self._session_manager is None: + raise RuntimeError( # pragma: no cover + "Session manager can only be accessed after calling streamable_http_app(). " + "The session manager is created lazily to avoid unnecessary initialization." + ) + return self._session_manager + + async def run( + self, + read_stream: ReadStream[SessionMessage | Exception], + write_stream: WriteStream[SessionMessage], + initialization_options: InitializationOptions, + # When False, exceptions are returned as messages to the client. + # When True, exceptions are raised, which will cause the server to shut down + # but also make tracing exceptions much easier during testing and when using + # in-process servers. + raise_exceptions: bool = False, + ) -> None: + """Serve a single connection over the given streams until the read side closes. + + Thin wrapper over `serve_dual_era_loop`: enters the server lifespan, + then drives the loop, serving the legacy handshake era and the modern + per-request-envelope era (the client's first request decides which). + Transports with their own lifespan owner (the streamable-HTTP manager) + call `serve_loop` directly instead. + """ + async with self.lifespan(self) as lifespan_context: + await serve_dual_era_loop( + self, + read_stream, + write_stream, + lifespan_state=lifespan_context, + init_options=initialization_options, + raise_exceptions=raise_exceptions, + ) + + def streamable_http_app( + self, + *, + streamable_http_path: str = "/mcp", + json_response: bool = False, + stateless_http: bool = False, + event_store: EventStore | None = None, + retry_interval: int | None = None, + max_request_body_size: int = DEFAULT_MAX_REQUEST_BODY_SIZE, + transport_security: TransportSecuritySettings | None = None, + host: str = "127.0.0.1", + auth: AuthSettings | None = None, + token_verifier: TokenVerifier | None = None, + auth_server_provider: OAuthAuthorizationServerProvider[Any, Any, Any] | None = None, + custom_starlette_routes: list[Route] | None = None, + debug: bool = False, + ) -> Starlette: + """Return an instance of the StreamableHTTP server app.""" + # Auto-enable DNS rebinding protection for localhost (IPv4 and IPv6) + if transport_security is None and host in ("127.0.0.1", "localhost", "::1"): + transport_security = TransportSecuritySettings( + enable_dns_rebinding_protection=True, + allowed_hosts=["127.0.0.1:*", "localhost:*", "[::1]:*"], + allowed_origins=["http://127.0.0.1:*", "http://localhost:*", "http://[::1]:*"], + ) + + session_manager = StreamableHTTPSessionManager( + app=self, + event_store=event_store, + retry_interval=retry_interval, + json_response=json_response, + stateless=stateless_http, + security_settings=transport_security, + max_request_body_size=max_request_body_size, + ) + self._session_manager = session_manager + + # Create the ASGI handler + streamable_http_app = StreamableHTTPASGIApp(session_manager) + + # Create routes + routes: list[Route | Mount] = [] + middleware: list[Middleware] = [] + required_scopes: list[str] = [] + + # Set up auth if configured + if auth: + required_scopes = auth.required_scopes or [] + + # Add auth middleware if token verifier is available + if token_verifier: + middleware = [ + Middleware( + AuthenticationMiddleware, + backend=BearerAuthBackend(token_verifier), + ), + Middleware(AuthContextMiddleware), + ] + + # Add auth endpoints if auth server provider is configured + if auth_server_provider: + routes.extend( + create_auth_routes( + provider=auth_server_provider, + issuer_url=auth.issuer_url, + service_documentation_url=auth.service_documentation_url, + client_registration_options=auth.client_registration_options, + revocation_options=auth.revocation_options, + identity_assertion_enabled=auth.identity_assertion_enabled, + ) + ) + + # Set up routes with or without auth + if token_verifier: + # Determine resource metadata URL + resource_metadata_url = None + if auth and auth.resource_server_url: # pragma: no branch + # Build compliant metadata URL for WWW-Authenticate header + resource_metadata_url = build_resource_metadata_url(auth.resource_server_url) + + routes.append( + Route( + streamable_http_path, + endpoint=RequireAuthMiddleware(streamable_http_app, required_scopes, resource_metadata_url), + ) + ) + else: + # Auth is disabled, no wrapper needed + routes.append( + Route( + streamable_http_path, + endpoint=streamable_http_app, + ) + ) + + # Add protected resource metadata endpoint if configured as RS + if auth and auth.resource_server_url: + routes.extend( + create_protected_resource_routes( + resource_url=auth.resource_server_url, + authorization_servers=[auth.issuer_url], + scopes_supported=auth.required_scopes, + ) + ) + + if custom_starlette_routes: + routes.extend(custom_starlette_routes) + + return Starlette( + debug=debug, + routes=routes, + middleware=middleware, + lifespan=lambda app: session_manager.run(), + ) diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__init__.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__init__.py new file mode 100644 index 0000000..7c9b67e --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__init__.py @@ -0,0 +1,61 @@ +"""MCPServer - A more ergonomic interface for MCP servers.""" + +from mcp_types import Icon + +from mcp.server.extension import Extension, MethodBinding, ResourceBinding, ToolBinding +from mcp.server.request_state import ( + AESGCMRequestStateCodec, + InvalidRequestState, + RequestStateBoundary, + RequestStateCodec, + RequestStateSecurity, + authenticated_principal, +) + +from .context import Context +from .prompts.base import AssistantMessage, Message, UserMessage +from .resolve import ( + AcceptedElicitation, + CancelledElicitation, + DeclinedElicitation, + Elicit, + ElicitationResult, + ListRoots, + Resolve, + Sample, +) +from .resources import DEFAULT_RESOURCE_SECURITY, ResourceSecurity +from .server import MCPServer, require_client_extension +from .utilities.types import Audio, Image + +__all__ = [ + "MCPServer", + "Context", + "Image", + "Audio", + "Message", + "UserMessage", + "AssistantMessage", + "Icon", + "Resolve", + "Elicit", + "Sample", + "ListRoots", + "ElicitationResult", + "AcceptedElicitation", + "DeclinedElicitation", + "CancelledElicitation", + "Extension", + "ToolBinding", + "ResourceBinding", + "MethodBinding", + "require_client_extension", + "ResourceSecurity", + "DEFAULT_RESOURCE_SECURITY", + "RequestStateSecurity", + "RequestStateCodec", + "RequestStateBoundary", + "AESGCMRequestStateCodec", + "InvalidRequestState", + "authenticated_principal", +] diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4b3695b Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/context.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/context.cpython-312.pyc new file mode 100644 index 0000000..c78125b Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/context.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..40506a4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/resolve.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/resolve.cpython-312.pyc new file mode 100644 index 0000000..a5496e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/resolve.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/server.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/server.cpython-312.pyc new file mode 100644 index 0000000..72d4462 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/__pycache__/server.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/context.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/context.py new file mode 100644 index 0000000..07c4799 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/context.py @@ -0,0 +1,384 @@ +from __future__ import annotations + +from collections.abc import Iterable, Mapping +from typing import TYPE_CHECKING, Any, Generic, cast + +from mcp_types import ClientCapabilities, InputRequiredResult, InputResponseRequestParams, InputResponses, LoggingLevel +from pydantic import AnyUrl, BaseModel +from typing_extensions import deprecated + +from mcp.server.context import LifespanContextT, RequestT, ServerRequestContext +from mcp.server.elicitation import ( + ElicitationResult, + ElicitSchemaModelT, + UrlElicitationResult, + elicit_url, + elicit_with_validation, +) +from mcp.server.lowlevel.helper_types import ReadResourceContents +from mcp.server.subscriptions import SubscriptionBus +from mcp.shared.exceptions import MCPDeprecationWarning +from mcp.shared.subscriptions import ( + PromptsListChanged, + ResourcesListChanged, + ResourceUpdated, + ToolsListChanged, +) + +if TYPE_CHECKING: + from mcp.server.mcpserver.server import MCPServer + + +class Context(BaseModel, Generic[LifespanContextT, RequestT]): + """Context object providing access to MCP capabilities. + + This provides a cleaner interface to MCP's RequestContext functionality. + It gets injected into tool and resource functions that request it via type hints. + + To use context in a tool function, add a parameter with the Context type annotation: + + ```python + @server.tool() + async def my_tool(x: int, ctx: Context) -> str: + # Log messages to the client + await ctx.info(f"Processing {x}") + await ctx.debug("Debug info") + await ctx.warning("Warning message") + await ctx.error("Error message") + + # Report progress + await ctx.report_progress(50, 100) + + # Access resources + data = await ctx.read_resource("resource://data") + + # Get request info + request_id = ctx.request_id + + return str(x) + ``` + + The context parameter name can be anything as long as it's annotated with Context. + The context is optional - tools that don't need it can omit the parameter. + """ + + _request_context: ServerRequestContext[LifespanContextT, RequestT] | None + _mcp_server: MCPServer | None + _input_params: InputResponseRequestParams | None + _subscriptions: SubscriptionBus | None + + # TODO(maxisbey): Consider making request_context/mcp_server required, or refactor Context entirely. + def __init__( + self, + *, + request_context: ServerRequestContext[LifespanContextT, RequestT] | None = None, + mcp_server: MCPServer | None = None, + input_params: InputResponseRequestParams | None = None, + subscriptions: SubscriptionBus | None = None, + # TODO(Marcelo): We should drop this kwargs parameter. + **kwargs: Any, + ): + super().__init__(**kwargs) + self._request_context = request_context + self._mcp_server = mcp_server + self._input_params = input_params + self._subscriptions = subscriptions + + @property + def mcp_server(self) -> MCPServer: + """Access to the MCPServer instance.""" + if self._mcp_server is None: + raise ValueError("Context is not available outside of a request") + return self._mcp_server + + @property + def request_context(self) -> ServerRequestContext[LifespanContextT, RequestT]: + """Access to the underlying request context.""" + if self._request_context is None: + raise ValueError("Context is not available outside of a request") + return self._request_context + + def _nested_invocation(self) -> Context[LifespanContextT, RequestT]: + """A Context for invoking another handler's function from inside this request. + + Shares the request infrastructure (session, request metadata, lifespan) but + carries no `input_responses`/`request_state`: those are addressed to the wire + request's own target — their keys are ones that handler minted — so a nested + invocation always starts on round one. + """ + return Context( + request_context=self._request_context, mcp_server=self._mcp_server, subscriptions=self._subscriptions + ) + + async def report_progress(self, progress: float, total: float | None = None, message: str | None = None) -> None: + """Report progress for the current operation. + + Args: + progress: Current progress value (e.g., 24) + total: Optional total value (e.g., 100) + message: Optional message (e.g., "Starting render...") + """ + await self.request_context.session.report_progress(progress, total, message) + + @property + def _bus(self) -> SubscriptionBus: + if self._subscriptions is None: + raise ValueError("Context is not available outside of a request") + return self._subscriptions + + async def notify_tools_changed(self) -> None: + """Publish a tools list-changed event to `subscriptions/listen` subscribers.""" + await self._bus.publish(ToolsListChanged()) + + async def notify_prompts_changed(self) -> None: + """Publish a prompts list-changed event to `subscriptions/listen` subscribers.""" + await self._bus.publish(PromptsListChanged()) + + async def notify_resources_changed(self) -> None: + """Publish a resources list-changed event to `subscriptions/listen` subscribers.""" + await self._bus.publish(ResourcesListChanged()) + + async def notify_resource_updated(self, uri: str | AnyUrl) -> None: + """Publish a resource-updated event for `uri` to `subscriptions/listen` subscribers. + + The URI is matched as an exact string against each stream's filter. + Reaches `subscriptions/listen` streams only; clients on earlier + protocol versions that used `resources/subscribe` are notified via + `ctx.session.send_resource_updated(uri)` instead. + """ + await self._bus.publish(ResourceUpdated(uri=str(uri))) + + async def read_resource(self, uri: str | AnyUrl) -> Iterable[ReadResourceContents]: + """Read a resource by URI. + + This is a content reader: an `InputRequiredResult` returned by a + resource template function (the 2026-07-28 multi-round-trip flow) + raises here, and the nested template never sees this request's + `input_responses`/`request_state` — those answer the outer handler's + own questions, so the template always behaves as round one. A handler + that wants to receive and forward an `InputRequiredResult` as its own + result calls `MCPServer.read_resource(uri, context)` instead — but + not from a tool whose dependencies elicit via `Resolve(...)`: the + resolver owns that tool's `request_state` channel, and a forwarded + result's state would clobber it. + + Args: + uri: Resource URI to read + + Returns: + The resource content as either text or bytes + + Raises: + ResourceNotFoundError: If no resource or template matches the URI, or the + handler raised it. + ResourceError: If the resource or template function raises `ResourceError`. + UnexpectedResourceError: If the resource or template function raises anything + else. `__cause__` is the original exception. Left uncaught in a tool, this + is logged as the tool's crash, while the two above are not. + RuntimeError: If the resource returned an `InputRequiredResult`. + """ + assert self._mcp_server is not None, "Context is not available outside of a request" + result = await self._mcp_server.read_resource(uri, self._nested_invocation()) + if isinstance(result, InputRequiredResult): + raise RuntimeError( + "Resource returned InputRequiredResult; ctx.read_resource() only returns " + "content — use MCPServer.read_resource(uri, context) to receive and forward it." + ) + return result + + async def elicit( + self, + message: str, + schema: type[ElicitSchemaModelT], + ) -> ElicitationResult[ElicitSchemaModelT]: + """Elicit information from the client/user. + + This method can be used to interactively ask for additional information from the + client within a tool's execution. The client might display the message to the + user and collect a response according to the provided schema. If the client + is an agent, it might decide how to handle the elicitation -- either by asking + the user or automatically generating a response. + + Args: + message: Message to present to the user + schema: A Pydantic model class defining the expected response structure. + According to the specification, only primitive types are allowed. + + Returns: + An ElicitationResult containing the action taken and the data if accepted + + Note: + Check the result.action to determine if the user accepted, declined, or cancelled. + The result.data will only be populated if action is "accept" and validation succeeded. + """ + + return await elicit_with_validation( + session=self.request_context.session, + message=message, + schema=schema, + related_request_id=self.request_id, + ) + + async def elicit_url( + self, + message: str, + url: str, + elicitation_id: str, + ) -> UrlElicitationResult: + """Request URL mode elicitation from the client. + + This directs the user to an external URL for out-of-band interactions + that must not pass through the MCP client. Use this for: + - Collecting sensitive credentials (API keys, passwords) + - OAuth authorization flows with third-party services + - Payment and subscription flows + - Any interaction where data should not pass through the LLM context + + The response indicates whether the user consented to navigate to the URL. + The actual interaction happens out-of-band. When the elicitation completes, + call `ctx.session.send_elicit_complete(elicitation_id)` to notify the client. + + Args: + message: Human-readable explanation of why the interaction is needed + url: The URL the user should navigate to + elicitation_id: Unique identifier for tracking this elicitation + + Returns: + UrlElicitationResult indicating accept, decline, or cancel + """ + return await elicit_url( + session=self.request_context.session, + message=message, + url=url, + elicitation_id=elicitation_id, + related_request_id=self.request_id, + ) + + @deprecated("The logging capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def log( + self, + level: LoggingLevel, + data: Any, + *, + logger_name: str | None = None, + ) -> None: + """Send a log message to the client. + + Args: + level: Log level (debug, info, notice, warning, error, critical, + alert, emergency) + data: The data to be logged. Any JSON serializable type is allowed + (string, dict, list, number, bool, etc.) per the MCP specification. + logger_name: Optional logger name + """ + await self.request_context.session.send_log_message( # pyright: ignore[reportDeprecated] + level=level, + data=data, + logger=logger_name, + related_request_id=self.request_id, + ) + + @property + def headers(self) -> Mapping[str, str] | None: + """Request headers carried by this message, when the transport has them. + + Populated by HTTP-based transports; `None` on stdio or when the + transport's request object carries no headers. Headers are + client-supplied input - never treat one as an identity assertion. + """ + return cast("Mapping[str, str] | None", getattr(self.request_context.request, "headers", None)) + + @property + def request_id(self) -> str: + """Get the unique ID for this request.""" + return str(self.request_context.request_id) + + @property + def protocol_version(self) -> str | None: + """The negotiated protocol version, or `None` outside of an active request.""" + return self._request_context.protocol_version if self._request_context is not None else None + + @property + def input_responses(self) -> InputResponses | None: + """Client responses to a prior `InputRequiredResult.input_requests`. + + `None` on the initial round, or when the client retried without + responses. + """ + return self._input_params.input_responses if self._input_params else None + + @property + def request_state(self) -> str | None: + """Opaque state echoed from a prior `InputRequiredResult.request_state`. + + `None` on the initial round. + """ + return self._input_params.request_state if self._input_params else None + + @property + def client_capabilities(self) -> ClientCapabilities | None: + """The client's declared capabilities for this connection. + + `None` when the client declared none (e.g. an anonymous stateless + request without the reserved `_meta` keys). Client info is not + required for capabilities to be recorded. + """ + return self.request_context.session.client_capabilities + + @property + def session(self): + """Access to the underlying session for advanced usage.""" + return self.request_context.session + + async def close_sse_stream(self) -> None: + """Close the SSE stream to trigger client reconnection. + + This method closes the HTTP connection for the current request, triggering + client reconnection. Events continue to be stored in the event store and will + be replayed when the client reconnects with Last-Event-ID. + + Use this to implement polling behavior during long-running operations - + the client will reconnect after the retry interval specified in the priming event. + + Note: + This is a no-op if not using StreamableHTTP transport with event_store. + The callback is only available when event_store is configured. + """ + if self._request_context and self._request_context.close_sse_stream: # pragma: no branch + await self._request_context.close_sse_stream() + + async def close_standalone_sse_stream(self) -> None: + """Close the standalone GET SSE stream to trigger client reconnection. + + This method closes the HTTP connection for the standalone GET stream used + for unsolicited server-to-client notifications. The client SHOULD reconnect + with Last-Event-ID to resume receiving notifications. + + Note: + This is a no-op if not using StreamableHTTP transport with event_store. + Currently, client reconnection for standalone GET streams is NOT + implemented - this is a known gap. + """ + if self._request_context and self._request_context.close_standalone_sse_stream: # pragma: no cover + await self._request_context.close_standalone_sse_stream() + + # Convenience methods for common log levels + @deprecated("The logging capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def debug(self, data: Any, *, logger_name: str | None = None) -> None: + """Send a debug log message.""" + await self.log("debug", data, logger_name=logger_name) # pyright: ignore[reportDeprecated] + + @deprecated("The logging capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def info(self, data: Any, *, logger_name: str | None = None) -> None: + """Send an info log message.""" + await self.log("info", data, logger_name=logger_name) # pyright: ignore[reportDeprecated] + + @deprecated("The logging capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def warning(self, data: Any, *, logger_name: str | None = None) -> None: + """Send a warning log message.""" + await self.log("warning", data, logger_name=logger_name) # pyright: ignore[reportDeprecated] + + @deprecated("The logging capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def error(self, data: Any, *, logger_name: str | None = None) -> None: + """Send an error log message.""" + await self.log("error", data, logger_name=logger_name) # pyright: ignore[reportDeprecated] diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/exceptions.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/exceptions.py new file mode 100644 index 0000000..5e831b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/exceptions.py @@ -0,0 +1,76 @@ +"""Custom exceptions for MCPServer.""" + + +class MCPServerError(Exception): + """Base error for MCPServer.""" + + +class ResourceError(MCPServerError): + """A resource failure you anticipated. + + Raise this from a resource or resource template handler for a failure you saw + coming: the client receives a `-32603` protocol error carrying your message + (`ResourceNotFoundError` below is the `-32602` variant), and the server logs it + at INFO without a traceback. Any other exception (bar `MCPError`, which is a + protocol error) is treated as a crash: the client gets a generic message naming + only the URI, and the server logs the traceback at ERROR. + + The SDK raises it too, and `UnexpectedResourceError` subclasses it, so + `except ResourceError` around `MCPServer.read_resource()` catches every read + failure, crash or not. + """ + + +class ResourceNotFoundError(ResourceError): + """Resource does not exist. + + Raise this from a resource handler to signal that the requested instance does not exist. + Clients receive `-32602` (invalid params) per + [SEP-2164](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2164). + """ + + +class UnexpectedResourceError(ResourceError): + """A resource read failed with something other than `ResourceError` or `MCPError`. + + The SDK raises this itself, around a crash in a resource or resource template + handler. You never raise it. `__cause__` is the original exception, which the + server logs with its traceback. The message names only the URI, so the + original text is withheld from the client. + """ + + +class ToolError(MCPServerError): + """A tool failure you anticipated. + + Raise this from a tool (or a resolver) for a failure you saw coming: the + call returns `is_error=True` with your message in `content` for the model to + read, and the server logs it at INFO without a traceback. A `ResourceError` + that escapes the tool (say from `ctx.read_resource()`) counts the same. Any + other exception bar `MCPError` (a protocol error) is treated as a crash: the + model sees only `Error executing tool `, and the server logs the + traceback at ERROR. Inside a pydantic validator, raise `ValueError` as pydantic + expects; it arrives as an argument-validation failure, which is anticipated too. + + The SDK raises it too, for an unknown tool name and for arguments that fail + the input schema, and `UnexpectedToolError` subclasses it, so `except ToolError` + around `MCPServer.call_tool()` catches every tool failure, crash or not. + """ + + +class UnexpectedToolError(ToolError): + """A tool call failed with something other than `ToolError`, `ResourceError`, or `MCPError`. + + The SDK raises this itself, around a crash in the tool (or a resolver) or a + return value that fails output conversion. You never raise it. The message is + only `Error executing tool ` (followed by the same for a nested tool or + resource that crashed), so nothing from the original reaches the client. + `__cause__` is the original exception (or, for a nested tool or resource + crash, its `Unexpected...Error` wrapper), which the server logs with its + traceback before returning the `is_error=True` result. Catch it around + `MCPServer.call_tool()` to tell a crash from a deliberate `ToolError`. + """ + + +class InvalidSignature(Exception): + """Invalid signature for use with MCPServer.""" diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/__init__.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/__init__.py new file mode 100644 index 0000000..7637269 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/__init__.py @@ -0,0 +1,4 @@ +from .base import Prompt +from .manager import PromptManager + +__all__ = ["Prompt", "PromptManager"] diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..210eaeb Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..fd8005f Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/__pycache__/base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/__pycache__/manager.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/__pycache__/manager.cpython-312.pyc new file mode 100644 index 0000000..f0fd23b Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/__pycache__/manager.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/base.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/base.py new file mode 100644 index 0000000..a13b72f --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/base.py @@ -0,0 +1,213 @@ +"""Base classes for MCPServer prompts.""" + +from __future__ import annotations + +import functools +from collections.abc import Awaitable, Callable, Sequence +from typing import TYPE_CHECKING, Annotated, Any, Literal + +import anyio.to_thread +import pydantic_core +from mcp_types import ContentBlock, Icon, InputRequiredResult, TextContent +from pydantic import BaseModel, Field, TypeAdapter, validate_call + +from mcp.server.mcpserver.utilities.context_injection import find_context_parameter, inject_context +from mcp.server.mcpserver.utilities.func_metadata import func_metadata +from mcp.server.mcpserver.utilities.types import Audio, Image +from mcp.shared._callable_inspection import is_async_callable +from mcp.shared.exceptions import MCPError + +if TYPE_CHECKING: + from mcp.server.context import LifespanContextT, RequestT + from mcp.server.mcpserver.context import Context + + +class Message(BaseModel): + """Base class for all prompt messages. + + `content` may be a plain string (wrapped in `TextContent`), an `Image` or `Audio` + helper (converted to `ImageContent` / `AudioContent`, reading the file for path-backed + helpers), or any ready-made content block. + + Raises: + OSError: If a path-backed `Image` or `Audio` cannot be read. + """ + + role: Literal["user", "assistant"] + content: ContentBlock + + def __init__(self, content: str | ContentBlock | Image | Audio, **kwargs: Any): + if isinstance(content, str): + content = TextContent(type="text", text=content) + elif isinstance(content, Image): + content = content.to_image_content() + elif isinstance(content, Audio): + content = content.to_audio_content() + super().__init__(content=content, **kwargs) + + +class UserMessage(Message): + """A message from the user.""" + + role: Literal["user", "assistant"] = "user" + + def __init__(self, content: str | ContentBlock | Image | Audio, **kwargs: Any): + super().__init__(content=content, **kwargs) + + +class AssistantMessage(Message): + """A message from the assistant.""" + + role: Literal["user", "assistant"] = "assistant" + + def __init__(self, content: str | ContentBlock | Image | Audio, **kwargs: Any): + super().__init__(content=content, **kwargs) + + +# Both classes accept either role, so the first arm always matches: validate left to right rather than +# trying both (which converted - and for path-backed Image/Audio, read - the content twice). +message_validator: TypeAdapter[UserMessage | AssistantMessage] = TypeAdapter( + Annotated[UserMessage | AssistantMessage, Field(union_mode="left_to_right")] +) + +_PromptResultItem = str | ContentBlock | Image | Audio | Message | dict[str, Any] +SyncPromptResult = _PromptResultItem | InputRequiredResult | Sequence[_PromptResultItem] +PromptResult = SyncPromptResult | Awaitable[SyncPromptResult] + + +class PromptArgument(BaseModel): + """An argument that can be passed to a prompt.""" + + name: str = Field(description="Name of the argument") + description: str | None = Field(None, description="Description of what the argument does") + required: bool = Field(default=False, description="Whether the argument is required") + + +class Prompt(BaseModel): + """A prompt template that can be rendered with parameters.""" + + name: str = Field(description="Name of the prompt") + title: str | None = Field(None, description="Human-readable title of the prompt") + description: str | None = Field(None, description="Description of what the prompt does") + arguments: list[PromptArgument] | None = Field(None, description="Arguments that can be passed to the prompt") + fn: Callable[..., PromptResult | Awaitable[PromptResult]] = Field(exclude=True) + icons: list[Icon] | None = Field(default=None, description="Optional list of icons for this prompt") + context_kwarg: str | None = Field(None, description="Name of the kwarg that should receive context", exclude=True) + + @classmethod + def from_function( + cls, + fn: Callable[..., PromptResult | Awaitable[PromptResult]], + name: str | None = None, + title: str | None = None, + description: str | None = None, + icons: list[Icon] | None = None, + context_kwarg: str | None = None, + ) -> Prompt: + """Create a Prompt from a function. + + The function can return: + - A string, content block, `Image` or `Audio` (each becomes a user message) + - A Message object + - A dict (converted to a message) + - A sequence of any of the above + - An InputRequiredResult (passed through unchanged; the 2026-07-28 + multi-round-trip flow — read `ctx.input_responses` on the retry) + """ + func_name = name or fn.__name__ + + if func_name == "": # pragma: no cover + raise ValueError("You must provide a name for lambda functions") + + # Find context parameter if it exists + if context_kwarg is None: # pragma: no branch + context_kwarg = find_context_parameter(fn) + + # Only the argument model is needed; a prompt has no output schema to derive + func_arg_metadata = func_metadata( + fn, skip_names=[context_kwarg] if context_kwarg is not None else [], structured_output=False + ) + parameters = func_arg_metadata.arg_model.model_json_schema() + + # Convert parameters to PromptArguments + arguments: list[PromptArgument] = [] + if "properties" in parameters: # pragma: no branch + for param_name, param in parameters["properties"].items(): + required = param_name in parameters.get("required", []) + arguments.append( + PromptArgument( + name=param_name, + description=param.get("description"), + required=required, + ) + ) + + # ensure the arguments are properly cast + fn = validate_call(fn) + + return cls( + name=func_name, + title=title, + description=description or fn.__doc__ or "", + arguments=arguments, + fn=fn, + icons=icons, + context_kwarg=context_kwarg, + ) + + async def render( + self, + arguments: dict[str, Any] | None, + context: Context[LifespanContextT, RequestT], + ) -> list[Message] | InputRequiredResult: + """Render the prompt with arguments. + + An `InputRequiredResult` returned by the prompt function is passed + through unchanged so the multi-round-trip flow reaches the client. + + Raises: + ValueError: If required arguments are missing, or if rendering fails. + """ + # Validate required arguments + if self.arguments: + required = {arg.name for arg in self.arguments if arg.required} + provided = set(arguments or {}) + missing = required - provided + if missing: + raise ValueError(f"Missing required arguments: {missing}") + + try: + # Add context to arguments if needed + call_args = inject_context(self.fn, arguments or {}, context, self.context_kwarg) + + fn = self.fn + if is_async_callable(fn): + result = await fn(**call_args) + else: + result = await anyio.to_thread.run_sync(functools.partial(self.fn, **call_args)) + + if isinstance(result, InputRequiredResult): + return result + + # Validate messages + if not isinstance(result, list | tuple): + result = [result] + + # Convert result to messages + messages: list[Message] = [] + for msg in result: # type: ignore[reportUnknownVariableType] + if isinstance(msg, Message): + messages.append(msg) + elif isinstance(msg, dict): + messages.append(message_validator.validate_python(msg)) + elif isinstance(msg, str | ContentBlock | Image | Audio): # bare content is one user message + messages.append(UserMessage(msg)) + else: # pragma: no cover + content = pydantic_core.to_json(msg, fallback=str, indent=2).decode() + messages.append(Message(role="user", content=content)) + + return messages + except MCPError: + raise + except Exception as exc: + raise ValueError(f"Error rendering prompt {self.name}") from exc diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/manager.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/manager.py new file mode 100644 index 0000000..01a0823 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/prompts/manager.py @@ -0,0 +1,67 @@ +"""Prompt management functionality.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from mcp_types import InputRequiredResult + +from mcp.server.mcpserver.prompts.base import Message, Prompt +from mcp.server.mcpserver.utilities.logging import get_logger + +if TYPE_CHECKING: + from mcp.server.context import LifespanContextT, RequestT + from mcp.server.mcpserver.context import Context + +logger = get_logger(__name__) + + +class PromptManager: + """Manages MCPServer prompts.""" + + def __init__(self, warn_on_duplicate_prompts: bool = True): + self._prompts: dict[str, Prompt] = {} + self.warn_on_duplicate_prompts = warn_on_duplicate_prompts + + def get_prompt(self, name: str) -> Prompt | None: + """Get prompt by name.""" + return self._prompts.get(name) + + def list_prompts(self) -> list[Prompt]: + """List all registered prompts.""" + return list(self._prompts.values()) + + def add_prompt( + self, + prompt: Prompt, + ) -> Prompt: + """Add a prompt to the manager.""" + + # Check for duplicates + existing = self._prompts.get(prompt.name) + if existing: + if self.warn_on_duplicate_prompts: + logger.warning(f"Prompt already exists: {prompt.name}") + return existing + + self._prompts[prompt.name] = prompt + return prompt + + def remove_prompt(self, name: str) -> None: + """Remove a prompt by name.""" + if name not in self._prompts: + raise ValueError(f"Unknown prompt: {name}") + del self._prompts[name] + + async def render_prompt( + self, + name: str, + arguments: dict[str, Any] | None, + context: Context[LifespanContextT, RequestT], + ) -> list[Message] | InputRequiredResult: + """Render a prompt by name with arguments.""" + prompt = self.get_prompt(name) + if not prompt: + raise ValueError(f"Unknown prompt: {name}") + + return await prompt.render(arguments, context) diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resolve.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resolve.py new file mode 100644 index 0000000..2afbc51 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resolve.py @@ -0,0 +1,846 @@ +"""Resolver dependency injection for MCPServer tools. + +A tool parameter annotated `Annotated[T, Resolve(fn)]` is filled by running the +resolver `fn` before the tool body, instead of from the LLM-supplied arguments. +Resolvers form a DAG: a resolver may declare its own `Resolve(...)` dependencies, +take tool arguments by name, and take the `Context`. A resolver may return a +request marker (`Elicit[T]` to ask the user, `Sample` to sample the client's +LLM, `ListRoots` to fetch its roots); the framework injects the response. + +The transport follows the negotiated protocol: >= 2026-07-28 batches the requests +into an `InputRequiredResult` and resumes when the client retries with +`input_responses`/`request_state`; <= 2025-11-25 sends each standalone server-to-client +request mid-call. Only *asked* outcomes ride `request_state`, so each question is asked once. Resolver +bodies may re-run on every round; a recorded outcome is consulted only when the +body asks its question again, so a resolver's own computation always wins over +anything the client echoes back in `request_state`. + +Whether the consumer receives the unwrapped model or the full +`ElicitationResult` union is decided by the consumer's annotation: + +- `Annotated[T, Resolve(fn)]` -> unwrapped `T`; decline/cancel aborts the call. +- `Annotated[ElicitationResult[T], Resolve(fn)]` (or a specific member) -> the + full outcome; the consumer branches on accept/decline/cancel. + +`Sample` and `ListRoots` have no decline arm; their consumers annotate the result type directly. +""" + +from __future__ import annotations + +import base64 +import hashlib +import inspect +import json +import logging +import types +import typing +from collections.abc import Callable, Hashable, Mapping +from typing import Annotated, Any, Generic, Literal, TypeGuard, get_args, get_origin + +import anyio.to_thread +from mcp_types import ( + MISSING_REQUIRED_CLIENT_CAPABILITY, + ClientCapabilities, + CreateMessageRequest, + CreateMessageRequestParams, + CreateMessageResult, + CreateMessageResultWithTools, + ElicitationCapability, + ElicitRequest, + ElicitRequestFormParams, + ElicitResult, + FormElicitationCapability, + IncludeContext, + InputRequest, + InputRequests, + InputRequiredResult, + InputResponses, + ListRootsRequest, + ListRootsResult, + MissingRequiredClientCapabilityErrorData, + ModelPreferences, + RootsCapability, + SamplingCapability, + SamplingMessage, + SamplingToolsCapability, + Tool, + ToolChoice, +) +from mcp_types.version import is_version_at_least +from pydantic import BaseModel, ValidationError +from typing_extensions import TypeVar + +from mcp.server.elicitation import ( + AcceptedElicitation, + CancelledElicitation, + DeclinedElicitation, + ElicitationResult, + render_elicitation_schema, +) +from mcp.server.mcpserver.context import Context +from mcp.server.mcpserver.exceptions import InvalidSignature, ToolError +from mcp.server.request_state import compact_json +from mcp.server.validation import validate_tool_use_result_messages, wants_sampling_tools +from mcp.shared._callable_inspection import is_async_callable +from mcp.shared.exceptions import MCPError +from mcp.shared.message import ServerMessageMetadata + +T = TypeVar("T", bound=BaseModel) + +# The union members the framework injects when a consumer opts into the outcome. +_ELICITATION_RESULT_MEMBERS = (AcceptedElicitation, DeclinedElicitation, CancelledElicitation) + +# First protocol revision whose `tools/call` carries elicitation inside +# `InputRequiredResult` rather than as a standalone server-to-client request. +# Pinned (not `LATEST_MODERN_VERSION`, which moves when newer revisions are added). +_INPUT_REQUIRED_VERSION = "2026-07-28" +_STATE_VERSION = 3 # v3: recorded and pended outcomes pinned to ASCII-canonical question renders + +logger = logging.getLogger(__name__) + + +class Resolve: + """Marker for `Annotated[T, Resolve(fn)]`: fill the parameter by running `fn`.""" + + def __init__(self, fn: Callable[..., Any]) -> None: + self.fn = fn + + +class Elicit(Generic[T]): + """A resolver's request to ask the client. + + Returned from a resolver to signal that the value must be elicited. The + framework runs `ctx.elicit(message, schema)` and injects the outcome. + """ + + def __init__(self, message: str, schema: type[T]) -> None: + self.message = message + self.schema = schema + + +class Sample: + """A resolver's request to sample the client's LLM via `sampling/createMessage`. + + The framework injects a `CreateMessageResult` (`CreateMessageResultWithTools` when `tools` or + `tool_choice` are given, which also requires the client's `sampling.tools`); requires the + `sampling` capability. On >= 2026-07-28 the request must render identically across retry + rounds, and the sampled result rides `request_state` on every later round. `include_context` + other than "none" is deprecated in the draft spec. + """ + + def __init__( + self, + messages: list[SamplingMessage], + *, + max_tokens: int, + system_prompt: str | None = None, + include_context: IncludeContext | None = None, + temperature: float | None = None, + stop_sequences: list[str] | None = None, + metadata: dict[str, Any] | None = None, + model_preferences: ModelPreferences | None = None, + tools: list[Tool] | None = None, + tool_choice: ToolChoice | None = None, + ) -> None: + validate_tool_use_result_messages(messages) + self.params = CreateMessageRequestParams( + messages=messages, + max_tokens=max_tokens, + system_prompt=system_prompt, + include_context=include_context, + temperature=temperature, + stop_sequences=stop_sequences, + metadata=metadata, + model_preferences=model_preferences, + tools=tools, + tool_choice=tool_choice, + ) + + +class ListRoots: + """A resolver's request for the client's roots via `roots/list`; the framework injects the `ListRootsResult`.""" + + +_Marker = Elicit[Any] | Sample | ListRoots +"""The request markers a resolver may return.""" + + +class _ParamPlan: + """How to fill one resolver parameter, decided once at registration.""" + + kind: str # "context" | "resolve" | "by_name" + resolve: Resolve | None + wants_union: bool + + def __init__(self, kind: str, resolve: Resolve | None = None, wants_union: bool = False) -> None: + self.kind = kind + self.resolve = resolve + self.wants_union = wants_union + + +class _ResolverPlan: + """A resolver's parameters and whether it is async, analyzed once.""" + + def __init__( + self, + fn: Callable[..., Any], + params: dict[str, _ParamPlan], + is_async: bool, + wire_key: str, + ) -> None: + self.fn = fn + self.params = params + self.is_async = is_async + # Deterministic, collision-free key for this resolver's elicitation on the + # wire (`input_requests`/`request_state`). Assigned at registration so it is + # stable across rounds even when `module:qualname` collides (closures). + self.wire_key = wire_key + + +def _type_hints(fn: Callable[..., Any]) -> dict[str, Any]: + """Resolve type hints for a function or a callable object. + + `typing.get_type_hints` raises on a callable *instance*; fall back to its + `__call__`. Returns an empty mapping when hints cannot be resolved, matching + `find_context_parameter`'s tolerance so callables without annotations (or with + unresolvable ones) simply have no resolved parameters. + """ + target = fn if inspect.isroutine(fn) else getattr(type(fn), "__call__", fn) + try: + return typing.get_type_hints(target, include_extras=True) + except Exception: + return {} + + +def _resolver_name(fn: Callable[..., Any]) -> str: + """Best-effort display name for error messages (callable objects lack `__name__`).""" + return getattr(fn, "__name__", None) or type(fn).__name__ + + +def find_resolved_parameters(fn: Callable[..., Any]) -> dict[str, tuple[Resolve, bool]]: + """Find parameters of `fn` annotated `Annotated[_, Resolve(...)]`. + + Returns a mapping of parameter name to `(Resolve, wants_union)`, where + `wants_union` is True when the annotated type is an `ElicitationResult` member + (the consumer wants the full outcome rather than the unwrapped model). + """ + hints = _type_hints(fn) + resolved: dict[str, tuple[Resolve, bool]] = {} + for name in inspect.signature(fn).parameters: + annotation = hints.get(name) + if get_origin(annotation) is not Annotated: + # A `Resolve` marker is only honored at the top level; flag (rather than + # silently drop) one buried in a union, e.g. `Annotated[T, Resolve(f)] | None`. + if _contains_resolve(annotation): + raise InvalidSignature( + f"Parameter {name!r} of {_resolver_name(fn)!r} wraps `Resolve(...)` in a " + "union; annotate the parameter directly as `Annotated[T, Resolve(...)]`" + ) + continue + type_arg, *metadata = get_args(annotation) + marker = next((m for m in metadata if isinstance(m, Resolve)), None) + if marker is not None: + resolved[name] = (marker, _wants_union(type_arg)) + return resolved + + +def returns_input_required(fn: Callable[..., Any]) -> bool: + """True when `fn`'s return annotation carries an `InputRequiredResult` arm. + + Used at tool registration to reject combining `Resolve(...)` parameters with a + hand-rolled `InputRequiredResult` flow: a call has a single + `input_responses`/`request_state` channel, so the two flows would overwrite + each other's state and the call could never converge. + """ + return _has_input_required_arm(_type_hints(fn).get("return")) + + +def _has_input_required_arm(annotation: Any) -> bool: + """Walk an annotation's arms through `Annotated`, type aliases, and unions.""" + if get_origin(annotation) is Annotated: + return _has_input_required_arm(get_args(annotation)[0]) + # A `type X = ...` / `TypeAliasType` alias carries its target on `__value__` (a + # subscripted alias forwards the attribute to its origin). The access evaluates + # a PEP 695 alias lazily, so an alias naming things unavailable at runtime + # (TYPE_CHECKING-only imports) raises NameError; such an alias declares no arm + # this check can see, and the in-call guard in `Tool.run` still covers it. + try: + value = getattr(annotation, "__value__", None) + except NameError: + return False + if value is not None: + return _has_input_required_arm(value) + if _is_union(annotation): + return any(_has_input_required_arm(arg) for arg in get_args(annotation)) + return isinstance(annotation, type) and issubclass(annotation, InputRequiredResult) + + +def _contains_resolve(annotation: Any) -> bool: + """True when a `Resolve` marker is nested inside `annotation` (e.g. a union member).""" + if get_origin(annotation) is Annotated: + return any(isinstance(m, Resolve) for m in get_args(annotation)[1:]) + return any(_contains_resolve(arg) for arg in get_args(annotation)) + + +def _check_elicit_return(return_annotation: Any, name: str) -> None: + """Validate the request-marker arms of a resolver's return annotation. + + Raises: + InvalidSignature: If the annotation has more than one marker arm. + """ + candidates = get_args(return_annotation) if _is_union(return_annotation) else (return_annotation,) + # Typing dedupes equal union members, so two arms here are genuinely distinct. + arms: list[Any] = [ + c + for c in candidates + # Origin guard for 3.10: `dict[str, Any]` passes `isinstance(c, type)` there and would crash `issubclass`. + if get_origin(c) is Elicit + or (get_origin(c) is None and isinstance(c, type) and issubclass(c, Elicit | Sample | ListRoots)) + ] + if len(arms) > 1: + raise InvalidSignature( + f"Resolver {name!r} return annotation has multiple Elicit/Sample/ListRoots arms; " + "a resolver asks one question - split it into separate resolvers" + ) + + +def _is_union(annotation: Any) -> bool: + return get_origin(annotation) in (typing.Union, types.UnionType) + + +def _wants_union(type_arg: Any) -> bool: + """True when `type_arg` is an `ElicitationResult` member (or a union of them). + + Handles the subscripted `ElicitationResult[T]` alias (a `TypeAliasType` whose + union is on the origin's `__value__`), the bare `ElicitationResult` alias (the + `__value__` is on `type_arg` itself), an explicit `AcceptedElicitation[T] | ...` + union, and a single member. + """ + # Unwrap the `ElicitationResult` alias whether it is bare or subscripted. + value = getattr(type_arg, "__value__", None) or getattr(get_origin(type_arg), "__value__", None) + if value is not None: + type_arg = value + members = get_args(type_arg) if get_origin(type_arg) is not None else (type_arg,) + return any(isinstance(m, type) and issubclass(m, _ELICITATION_RESULT_MEMBERS) for m in members) + + +def _resolver_key(fn: Callable[..., Any]) -> Hashable: + """Identity key for memoizing a resolver. + + A bound method - pure-python (`inspect.ismethod`) or built-in (e.g. `obj.meth` + on a C-extension type) - is recreated on each attribute access, so `id(fn)` + differs every time. Key it by its underlying function (or name) plus its + `__self__` identity so `auth.login` referenced in two places memoizes to one + call. Everything else keys by `id`, so two distinct callables never collide + even if they compare equal. + """ + bound_self = getattr(fn, "__self__", None) + if bound_self is not None: + # `__func__` (pure-python) has a stable identity; built-ins expose only a + # stable `__name__`. Use the function's id or the name's value accordingly. + func = getattr(fn, "__func__", None) + underlying: Hashable = id(func) if func is not None else getattr(fn, "__name__", id(fn)) + return (underlying, id(bound_self)) + return id(fn) + + +def build_resolver_plans( + resolved_params: Mapping[str, tuple[Resolve, bool]], + tool_arg_names: set[str], +) -> dict[Hashable, _ResolverPlan]: + """Statically analyze the resolver DAG rooted at a tool's resolved parameters. + + Raises: + InvalidSignature: If a resolver has a cyclic dependency, or a resolver + parameter cannot be classified (not a `Context`, a nested `Resolve`, + or a tool argument by name). + """ + plans: dict[Hashable, _ResolverPlan] = {} + # Count how many distinct resolvers share each `module:qualname` base so closures + # from one factory get distinct, deterministic wire keys (`base`, `base#1`, ...). + base_counts: dict[str, int] = {} + + def analyze(fn: Callable[..., Any], stack: tuple[Hashable, ...]) -> None: + key = _resolver_key(fn) + if key in stack: + raise InvalidSignature(f"Resolver {_resolver_name(fn)!r} has a cyclic dependency") + if key in plans: + return + + base = _state_key(fn) + seen = base_counts.get(base, 0) + base_counts[base] = seen + 1 + wire_key = base if seen == 0 else f"{base}#{seen}" + + hints = _type_hints(fn) + sig = inspect.signature(fn) + params: dict[str, _ParamPlan] = {} + nested: list[Callable[..., Any]] = [] + for param_name in sig.parameters: + annotation = hints.get(param_name) + if annotation is not None and _is_context_annotation(annotation): + params[param_name] = _ParamPlan("context") + continue + marker, wants_union = _resolve_marker(annotation) + if marker is not None: + params[param_name] = _ParamPlan("resolve", marker, wants_union) + nested.append(marker.fn) + continue + if param_name in tool_arg_names: + params[param_name] = _ParamPlan("by_name") + continue + raise InvalidSignature( + f"Resolver {_resolver_name(fn)!r} parameter {param_name!r} cannot be resolved: " + "expected a Context, an Annotated[_, Resolve(...)], or a tool argument by name" + ) + + _check_elicit_return(hints.get("return"), _resolver_name(fn)) + plans[key] = _ResolverPlan(fn, params, is_async_callable(fn), wire_key) + for dep in nested: + analyze(dep, stack + (key,)) + + for marker, _ in resolved_params.values(): + analyze(marker.fn, ()) + return plans + + +def _resolve_marker(annotation: Any) -> tuple[Resolve | None, bool]: + if get_origin(annotation) is not Annotated: + return None, False + type_arg, *metadata = get_args(annotation) + marker = next((m for m in metadata if isinstance(m, Resolve)), None) + return marker, (_wants_union(type_arg) if marker is not None else False) + + +def _is_context_annotation(annotation: Any) -> bool: + if get_origin(annotation) is Annotated: + annotation = get_args(annotation)[0] + candidates = get_args(annotation) if get_origin(annotation) is not None else (annotation,) + return any(isinstance(c, type) and issubclass(c, Context) for c in candidates) + + +class _Pending(Exception): + """Internal: a resolver needs client input not yet available this round.""" + + +class _Resolution: + """Per-`tools/call` resolution state, shared across the DAG walk. + + `input_required` selects the transport: at >= 2026-07-28 requests are + batched into `pending` and surfaced as an `InputRequiredResult`; at older + revisions each marker is answered synchronously over the back-channel. + """ + + def __init__( + self, + plans: Mapping[Hashable, _ResolverPlan], + tool_args: Mapping[str, Any], + context: Context[Any, Any], + input_required: bool, + ) -> None: + self.plans = plans + self.tool_args = tool_args + self.context = context + self.input_required = input_required + self.answers: InputResponses = context.input_responses or {} if input_required else {} + decoded = _decode_state(context.request_state if input_required else None) + self.state = decoded.outcomes + # Digests of the questions asked last round: an answer is accepted only + # for the exact rendering the client was shown. + self.asked = decoded.asked + # In-call dedup keyed by resolver identity (distinguishes two instances of + # the same bound method); `persist` holds the wire-shaped record of each + # asked outcome, keyed by its wire key - exactly what the next round's `request_state` + # carries: the client's own validated content (elicitation) or the validated result's + # dump (sample/roots). Pure resolvers are cheap to re-run each round and are not persisted. + self.cache: dict[Hashable, ElicitationResult[Any]] = {} + self.persist: dict[str, _StateEntry] = {} + self.pending: InputRequests = {} + + +def _state_key(fn: Callable[..., Any]) -> str: + """Worker-stable base wire key for a resolver, derived only from registration data. + + `input_requests`/`request_state` must round-trip through the client and resume on + any worker (stateless HTTP), so the key carries no `id(...)`: it is the resolver's + `module:qualname` (a callable object uses its type's). Distinct resolvers that + share this base - two instances of one method, two closures from one factory - are + disambiguated deterministically by `build_resolver_plans` (`base`, `base#1`, ...). + """ + qualname = getattr(fn, "__qualname__", None) or type(fn).__qualname__ + module = getattr(fn, "__module__", None) or type(fn).__module__ + return f"{module}:{qualname}" + + +async def resolve_arguments( + resolved_params: Mapping[str, tuple[Resolve, bool]], + plans: Mapping[Hashable, _ResolverPlan], + tool_args: Mapping[str, Any], + context: Context[Any, Any], +) -> dict[str, Any] | InputRequiredResult: + """Resolve every `Resolve`-marked tool parameter into a concrete value. + + Returns the mapping of tool parameter name to injected value when every + resolver is satisfied. When a resolver still needs client input (and the + negotiated protocol is >= 2026-07-28), returns an `InputRequiredResult` + carrying the batched questions instead; the tool body is not run. + + Each question is asked once - its answer is carried in `request_state` across + rounds and satisfies the question when the resolver asks it again. Resolver + bodies themselves may re-run on each round; a recorded answer is consulted + only when the body asks, never in place of running it. + + Raises: + ToolError: If an elicited value is declined or cancelled and the consumer + asked for the unwrapped model (rather than the result union). + """ + # `ctx.protocol_version` is `None` outside an active request: `MCPServer.call_tool()` + # called directly builds such a `Context`, and a tool whose resolvers never elicit + # must still work there. A missing version means the synchronous (non-input_required) + # transport, which never reaches a server-to-client request anyway. + res = _Resolution(plans, tool_args, context, _uses_input_required(context.protocol_version)) + injected: dict[str, Any] = {} + for name, (marker, wants_union) in resolved_params.items(): + try: + outcome = await _resolve(marker.fn, res) + except _Pending: + continue + injected[name] = outcome if wants_union else _unwrap(outcome, name) + + if res.pending: + asked = {key: _request_digest(request) for key, request in res.pending.items()} + return InputRequiredResult(input_requests=res.pending, request_state=_encode_state(res.persist, asked)) + return injected + + +async def _resolve(fn: Callable[..., Any], res: _Resolution) -> ElicitationResult[Any]: + """Resolve one resolver, deduped within the call by its resolver identity. + + Raises `_Pending` when the resolver (or one of its dependencies) needs client + input that has not arrived yet. + """ + cache_key = _resolver_key(fn) + if cache_key in res.cache: + return res.cache[cache_key] + + plan = res.plans[cache_key] + wire_key = plan.wire_key + if wire_key in res.pending: + # Already asked this round by another consumer; don't run the resolver again. + raise _Pending + + kwargs: dict[str, Any] = {} + dep_pending = False + for param_name, param_plan in plan.params.items(): + if param_plan.kind == "context": + kwargs[param_name] = res.context + elif param_plan.kind == "by_name": + kwargs[param_name] = res.tool_args[param_name] + else: + assert param_plan.resolve is not None + try: + # Visit every dependency so independent ones that need input are all + # collected into `res.pending` and batched into a single round. + dep_outcome = await _resolve(param_plan.resolve.fn, res) + except _Pending: + dep_pending = True + continue + kwargs[param_name] = dep_outcome if param_plan.wants_union else _unwrap(dep_outcome, param_name) + if dep_pending: + raise _Pending + + result: Any + if plan.is_async: + result = await fn(**kwargs) + else: + result = await anyio.to_thread.run_sync(lambda: fn(**kwargs)) + + if _is_marker(result): + outcome = await _fulfil(result, wire_key, res) + else: + # A resolver may return any type (not just `BaseModel`), so accept it as the + # outcome without validating against the schema bound. Plain outcomes are not + # persisted in `request_state`; the resolver re-runs next round instead. + outcome = _accepted(result) + + res.cache[cache_key] = outcome + return outcome + + +async def _fulfil(marker: _Marker, key: str, res: _Resolution) -> ElicitationResult[Any]: + """Turn a resolver's request marker into an outcome via the negotiated transport.""" + if not res.input_required: + # Gate wherever the request could actually be sent; otherwise the send path + # itself reports the failure. + if res.context.session.can_send_request: + _require_capability(res.context, marker, key) + if isinstance(marker, Elicit): + try: + return await res.context.elicit(marker.message, marker.schema) + except ValueError as e: + # Accepted with no content, or content that fails the schema: the same + # client mistake the input_required path below reports as a ToolError. + # (A pydantic ValidationError here means a non-conformant client sent a + # malformed ElicitResult; its text is not repeated back.) + detail = "received an invalid elicitation response" if isinstance(e, ValidationError) else str(e) + raise ToolError(f"Resolver {key!r}: {detail}") from e + result = await res.context.session.send_request( + _render_request(marker), + _result_type(marker), + metadata=ServerMessageMetadata(related_request_id=res.context.request_id), + ) + return _accepted(result) + + request = _render_request(marker) + q = _request_digest(request) + + # A recorded outcome from a prior round is consulted only here, after the body + # decided to ask, so a `request_state` entry can never stand in for a resolver's + # own computation. A recorded outcome wins over a re-sent answer. + outcome = _restore_outcome(res, key, marker, q) + if outcome is not None: + return outcome + + answer = res.answers.get(key) + # An answer counts only for the rendering recorded when it was asked; an answer to + # an unrecorded or differently-worded question re-asks instead of being consumed. + if answer is not None and res.asked.get(key) != q: + logger.info("Discarding the answer for resolver %r: the question changed since it was asked", key) + answer = None + if answer is None: + _require_capability(res.context, marker, key) + res.pending[key] = request + raise _Pending + if not isinstance(marker, Elicit): + # A no-tool-use answer to a tools request parses as the plain result; validate against the marker's model. + wire = answer.model_dump(mode="json", by_alias=True, exclude_none=True) + try: + result = _result_type(marker).model_validate(wire) + except ValidationError as e: + raise ToolError(f"Resolver {key!r} received a response of the wrong kind") from e + res.persist[key] = _StateEntry(action="accept", data=wire, q=q) + return _accepted(result) + if not isinstance(answer, ElicitResult): + raise ToolError(f"Resolver {key!r} received a non-elicitation response") + if answer.action == "accept": + if answer.content is None: + raise ToolError(f"Resolver {key!r} received an accepted elicitation with no content") + try: + data = marker.schema.model_validate(answer.content) + except ValidationError as e: + raise ToolError( + f"Resolver {key!r} received an accepted elicitation whose content does not match the requested schema" + ) from e + # Persist the exact wire content that just passed validation - never the + # model - so restoring next round revalidates the same bytes the client sent. + res.persist[key] = _StateEntry(action="accept", data=answer.content, q=q) + return AcceptedElicitation(data=data) + if answer.action == "decline": + res.persist[key] = _StateEntry(action="decline", q=q) + return DeclinedElicitation() + res.persist[key] = _StateEntry(action="cancel", q=q) + return CancelledElicitation() + + +def _unwrap(outcome: ElicitationResult[Any], name: str) -> Any: + if isinstance(outcome, AcceptedElicitation): + return outcome.data + raise ToolError(f"Resolver for parameter {name!r} could not resolve: elicitation was {outcome.action}") + + +def _is_marker(value: Any) -> TypeGuard[_Marker]: + return isinstance(value, Elicit | Sample | ListRoots) + + +def _accepted(data: Any) -> AcceptedElicitation[Any]: + """Wrap a resolved value as an accepted outcome without schema validation. + + A resolver may return any type (the schema bound only constrains `Elicit[T]`), + and a value restored from `request_state` is already validated. + """ + return AcceptedElicitation[Any].model_construct(data=data) + + +def _uses_input_required(protocol_version: str | None) -> bool: + """True when this request must elicit via `InputRequiredResult` (>= 2026-07-28). + + Older revisions still carry a standalone `elicitation/create` server-to-client + request, so the framework keeps the synchronous `ctx.elicit()` path for them. + """ + return protocol_version is not None and is_version_at_least(protocol_version, _INPUT_REQUIRED_VERSION) + + +def _require_capability(context: Context[Any, Any], marker: _Marker, key: str) -> None: + """Assert the client declared the capability `marker`'s request needs. + + A bare `elicitation: {}` (the only shape before modes existed) counts as form support; url-only does not. + + Raises: + MCPError: With code `MISSING_REQUIRED_CLIENT_CAPABILITY` and a + `requiredCapabilities` payload when the capability is not declared. + """ + capabilities = context.client_capabilities + if isinstance(marker, Elicit): + elicitation = capabilities.elicitation if capabilities is not None else None + if elicitation is not None and (elicitation.form is not None or elicitation.url is None): + return + required = ClientCapabilities(elicitation=ElicitationCapability(form=FormElicitationCapability())) + name = "form elicitation" + elif isinstance(marker, Sample): + sampling = capabilities.sampling if capabilities is not None else None + wants_tools = wants_sampling_tools(marker.params.tools, marker.params.tool_choice) + if sampling is not None and (not wants_tools or sampling.tools is not None): + return + required = ClientCapabilities( + sampling=SamplingCapability(tools=SamplingToolsCapability() if wants_tools else None) + ) + name = "sampling.tools" if wants_tools else "sampling" + else: + if capabilities is not None and capabilities.roots is not None: + return + required = ClientCapabilities(roots=RootsCapability()) + name = "roots" + data = MissingRequiredClientCapabilityErrorData(required_capabilities=required) + raise MCPError( + code=MISSING_REQUIRED_CLIENT_CAPABILITY, + message=f"Client did not declare the {name} capability required by resolver {key!r}", + data=data.model_dump(by_alias=True, mode="json", exclude_none=True), + ) + + +def _render_request(marker: _Marker) -> InputRequest: + """Render a marker as its wire request - the same shape on both transports.""" + if isinstance(marker, Elicit): + json_schema = render_elicitation_schema(marker.schema) + return ElicitRequest(params=ElicitRequestFormParams(message=marker.message, requested_schema=json_schema)) + if isinstance(marker, Sample): + return CreateMessageRequest(params=marker.params) + return ListRootsRequest() + + +def _result_type( + marker: Sample | ListRoots, +) -> type[CreateMessageResult] | type[CreateMessageResultWithTools] | type[ListRootsResult]: + """The result model a `Sample`/`ListRoots` response must validate against.""" + if isinstance(marker, ListRoots): + return ListRootsResult + return ( + CreateMessageResultWithTools + if wants_sampling_tools(marker.params.tools, marker.params.tool_choice) + else CreateMessageResult + ) + + +class _StateEntry(BaseModel): + """One resolver's recorded outcome inside `request_state`.""" + + action: Literal["accept", "decline", "cancel"] + data: Any = None + q: str | None = None + """Digest of the exact rendered question this outcome answered.""" + + +def _request_digest(request: InputRequest) -> str: + """Pin an outcome to the exact rendered question the client was shown. + + A redeploy that rewords or reshapes a question re-asks it instead of reusing the recorded answer. + """ + params = request.params + rendered = compact_json(params.model_dump(mode="json", by_alias=True, exclude_none=True) if params else None) + digest = hashlib.sha256(rendered.encode()).digest()[:16] + return base64.urlsafe_b64encode(digest).decode().rstrip("=") + + +class _State(BaseModel): + """The decoded `request_state`: resolver progress from earlier rounds.""" + + v: int + outcomes: dict[str, _StateEntry] = {} + asked: dict[str, str] = {} + """Question digest of each elicitation asked last round, keyed by wire key.""" + + +def _decode_state(request_state: str | None) -> _State: + """Decode the per-call resolution progress from `request_state`. + + Parsed with stdlib `json.loads` because `_encode_state` may emit escaped + lone surrogates, which pydantic's JSON parser rejects. The string arrives + boundary-authenticated, so malformed content or a version mismatch is + drift within the operator's own fleet (e.g. a rolling upgrade) and is + treated as "no progress yet". + """ + empty = _State(v=_STATE_VERSION) + if not request_state: + return empty + try: + state = _State.model_validate(json.loads(request_state)) + except ValueError: + return empty + return state if state.v == _STATE_VERSION else empty + + +def _encode_state(outcomes: Mapping[str, _StateEntry], asked: Mapping[str, str]) -> str: + """Encode recorded outcomes and asked-question digests for the next round. + + Outcome entries are already wire-shaped, so encoding is pure wrapping. + """ + state = _State(v=_STATE_VERSION, outcomes=dict(outcomes), asked=dict(asked)) + return compact_json(state.model_dump(mode="json")) + + +def _outcome_from_state(entry: _StateEntry, marker: _Marker) -> ElicitationResult[Any]: + """Rebuild an outcome from a decoded `request_state` entry. + + Raises: + ValidationError: If the entry does not fit the live marker. + """ + if isinstance(marker, Elicit): + if entry.action == "decline": + return DeclinedElicitation() + if entry.action == "cancel": + return CancelledElicitation() + return _accepted(marker.schema.model_validate(entry.data)) + return _accepted(_result_type(marker).model_validate(entry.data)) + + +def _restore_outcome(res: _Resolution, key: str, marker: _Marker, q: str) -> ElicitationResult[Any] | None: + """Restore `key`'s recorded outcome from a prior round, or `None` when absent. + + An entry pinned to a question digest other than `q`, or that fails + validation against the live marker, is dropped as if no progress was + recorded, so the question is asked again. + + Carries the original decoded entry forward unchanged in `res.persist`: if a + later resolver is still pending, the next round's `request_state` is built from + `res.persist`, so an earlier answer must stay there - byte-identical, never + re-derived - or it would be dropped and re-asked. + """ + entry = res.state.get(key) + if entry is None: + return None + if entry.q != q: + del res.state[key] + return None + try: + outcome = _outcome_from_state(entry, marker) + except ValidationError: + del res.state[key] + return None + res.persist[key] = entry + return outcome + + +__all__ = [ + "Resolve", + "Elicit", + "Sample", + "ListRoots", + "ElicitationResult", + "AcceptedElicitation", + "DeclinedElicitation", + "CancelledElicitation", + "find_resolved_parameters", + "build_resolver_plans", + "resolve_arguments", + "returns_input_required", +] diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__init__.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__init__.py new file mode 100644 index 0000000..f54ea44 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__init__.py @@ -0,0 +1,26 @@ +from .base import Resource +from .resource_manager import ResourceManager +from .templates import DEFAULT_RESOURCE_SECURITY, ResourceSecurity, ResourceSecurityError, ResourceTemplate +from .types import ( + BinaryResource, + DirectoryResource, + FileResource, + FunctionResource, + HttpResource, + TextResource, +) + +__all__ = [ + "Resource", + "TextResource", + "BinaryResource", + "FunctionResource", + "FileResource", + "HttpResource", + "DirectoryResource", + "ResourceTemplate", + "ResourceManager", + "ResourceSecurity", + "ResourceSecurityError", + "DEFAULT_RESOURCE_SECURITY", +] diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6214ffe Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..9bdf40d Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/resource_manager.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/resource_manager.cpython-312.pyc new file mode 100644 index 0000000..f5dfe40 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/resource_manager.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/templates.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/templates.cpython-312.pyc new file mode 100644 index 0000000..bd99893 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/templates.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/types.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/types.cpython-312.pyc new file mode 100644 index 0000000..5659089 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/__pycache__/types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/base.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/base.py new file mode 100644 index 0000000..a1d9ceb --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/base.py @@ -0,0 +1,43 @@ +"""Base classes and interfaces for MCPServer resources.""" + +import abc +from typing import Any + +from mcp_types import Annotations, Icon +from pydantic import ( + BaseModel, + ConfigDict, + Field, + ValidationInfo, + field_validator, +) + + +class Resource(BaseModel, abc.ABC): + """Base class for all resources.""" + + model_config = ConfigDict(validate_default=True, extra="forbid") + + uri: str = Field(default=..., description="URI of the resource") + name: str | None = Field(description="Name of the resource", default=None) + title: str | None = Field(description="Human-readable title of the resource", default=None) + description: str | None = Field(description="Description of the resource", default=None) + mime_type: str = Field(default="text/plain", description="MIME type of the resource content") + icons: list[Icon] | None = Field(default=None, description="Optional list of icons for this resource") + annotations: Annotations | None = Field(default=None, description="Optional annotations for the resource") + meta: dict[str, Any] | None = Field(default=None, description="Optional metadata for this resource") + + @field_validator("name", mode="before") + @classmethod + def set_default_name(cls, name: str | None, info: ValidationInfo) -> str: + """Set default name from URI if not provided.""" + if name: + return name + if uri := info.data.get("uri"): + return str(uri) + raise ValueError("Either name or uri must be provided") + + @abc.abstractmethod + async def read(self) -> str | bytes: + """Read the resource content.""" + pass # pragma: no cover diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/resource_manager.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/resource_manager.py new file mode 100644 index 0000000..e56e3ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/resource_manager.py @@ -0,0 +1,137 @@ +"""Resource manager functionality.""" + +from __future__ import annotations + +from collections.abc import Callable +from typing import TYPE_CHECKING, Any + +from mcp_types import Annotations, Icon, InputRequiredResult +from pydantic import AnyUrl + +from mcp.server.mcpserver.exceptions import ResourceNotFoundError +from mcp.server.mcpserver.resources.base import Resource +from mcp.server.mcpserver.resources.templates import ( + DEFAULT_RESOURCE_SECURITY, + ResourceSecurity, + ResourceSecurityError, + ResourceTemplate, +) +from mcp.server.mcpserver.utilities.logging import get_logger + +if TYPE_CHECKING: + from mcp.server.context import LifespanContextT, RequestT + from mcp.server.mcpserver.context import Context + +logger = get_logger(__name__) + + +class ResourceManager: + """Manages MCPServer resources.""" + + def __init__(self, warn_on_duplicate_resources: bool = True, *, resources: list[Resource] | None = None): + self._resources: dict[str, Resource] = {} + self._templates: dict[str, ResourceTemplate] = {} + self.warn_on_duplicate_resources = warn_on_duplicate_resources + + for resource in resources or (): + self.add_resource(resource) + + def add_resource(self, resource: Resource) -> Resource: + """Add a resource to the manager. + + Args: + resource: A Resource instance to add. + + Returns: + The added resource. If a resource with the same URI already exists, returns the existing resource. + """ + logger.debug( + "Adding resource", + extra={"uri": resource.uri, "type": type(resource).__name__, "resource_name": resource.name}, + ) + existing = self._resources.get(str(resource.uri)) + if existing: + if self.warn_on_duplicate_resources: + logger.warning(f"Resource already exists: {resource.uri}") + return existing + self._resources[str(resource.uri)] = resource + return resource + + def add_template( + self, + fn: Callable[..., Any], + uri_template: str, + name: str | None = None, + title: str | None = None, + description: str | None = None, + mime_type: str | None = None, + icons: list[Icon] | None = None, + annotations: Annotations | None = None, + meta: dict[str, Any] | None = None, + security: ResourceSecurity = DEFAULT_RESOURCE_SECURITY, + ) -> ResourceTemplate: + """Add a template from a function.""" + template = ResourceTemplate.from_function( + fn, + uri_template=uri_template, + name=name, + title=title, + description=description, + mime_type=mime_type, + icons=icons, + annotations=annotations, + meta=meta, + security=security, + ) + self._templates[template.uri_template] = template + return template + + async def get_resource( + self, uri: AnyUrl | str, context: Context[LifespanContextT, RequestT] + ) -> Resource | InputRequiredResult: + """Get resource by URI, checking concrete resources first, then templates. + + A template function may return an `InputRequiredResult` instead of + resource content (the 2026-07-28 multi-round-trip flow); it is passed + through unchanged. + + Raises: + ResourceNotFoundError: If no resource or template matches the URI. + ResourceError: If a matching template fails to create the resource. + + Note: + Pydantic's ``AnyUrl`` normalises percent-encoding and + resolves ``..`` segments during validation, so a value + constructed as ``AnyUrl("file:///a/%2E%2E/b")`` arrives + here as ``file:///b``. The JSON-RPC protocol layer passes + raw ``str`` values and is unaffected, but internal callers + wrapping URIs in ``AnyUrl`` should be aware that security + checks see the already-normalised form. + """ + uri_str = str(uri) + logger.debug("Getting resource", extra={"uri": uri_str}) + + # First check concrete resources + if resource := self._resources.get(uri_str): + return resource + + # Then check templates + for template in self._templates.values(): + try: + params = template.matches(uri_str) + except ResourceSecurityError as e: + raise ResourceNotFoundError(f"Unknown resource: {uri}") from e + if params is not None: + return await template.create_resource(uri_str, params, context=context) + + raise ResourceNotFoundError(f"Unknown resource: {uri}") + + def list_resources(self) -> list[Resource]: + """List all registered resources.""" + logger.debug("Listing resources", extra={"count": len(self._resources)}) + return list(self._resources.values()) + + def list_templates(self) -> list[ResourceTemplate]: + """List all registered templates.""" + logger.debug("Listing templates", extra={"count": len(self._templates)}) + return list(self._templates.values()) diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/templates.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/templates.py new file mode 100644 index 0000000..621b2e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/templates.py @@ -0,0 +1,248 @@ +"""Resource template functionality.""" + +from __future__ import annotations + +import functools +from collections.abc import Callable, Mapping, Set +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any + +import anyio.to_thread +from mcp_types import Annotations, Icon, InputRequiredResult +from pydantic import BaseModel, Field, validate_call + +from mcp.server.mcpserver.exceptions import ResourceError, UnexpectedResourceError +from mcp.server.mcpserver.resources.types import FunctionResource, Resource +from mcp.server.mcpserver.utilities.context_injection import find_context_parameter, inject_context +from mcp.server.mcpserver.utilities.func_metadata import func_metadata +from mcp.shared._callable_inspection import is_async_callable +from mcp.shared.exceptions import MCPError +from mcp.shared.path_security import contains_path_traversal, is_absolute_path +from mcp.shared.uri_template import UriTemplate + +if TYPE_CHECKING: + from mcp.server.context import LifespanContextT, RequestT + from mcp.server.mcpserver.context import Context + + +@dataclass(frozen=True) +class ResourceSecurity: + """Security policy applied to extracted resource template parameters. + + These checks run after :meth:`~mcp.shared.uri_template.UriTemplate.match` + has extracted and decoded parameter values. They catch path-traversal + and absolute-path injection regardless of how the value was encoded in + the URI (literal, ``%2F``, ``%5C``, ``%2E%2E``). + + Example:: + + # Opt out for a parameter that legitimately contains .. + @mcp.resource( + "git://diff/{+range}", + security=ResourceSecurity(exempt_params={"range"}), + ) + def git_diff(range: str) -> str: ... + """ + + reject_path_traversal: bool = True + """Reject values containing ``..`` as a path component.""" + + reject_absolute_paths: bool = True + """Reject values that look like absolute filesystem paths.""" + + reject_null_bytes: bool = True + """Reject values containing NUL (``\\x00``). Null bytes defeat string + comparisons (``"..\\x00" != ".."``) and can cause truncation in C + extensions or subprocess calls.""" + + exempt_params: Set[str] = field(default_factory=frozenset[str]) + """Parameter names to skip all checks for.""" + + def validate(self, params: Mapping[str, str | list[str]]) -> str | None: + """Check all parameter values against the configured policy. + + Args: + params: Extracted template parameters. List values (from + explode variables) are checked element-wise. + + Returns: + The name of the first parameter that fails, or ``None`` if + all values pass. + """ + for name, value in params.items(): + if name in self.exempt_params: + continue + values = value if isinstance(value, list) else [value] + for v in values: + if self.reject_null_bytes and "\0" in v: + return name + if self.reject_path_traversal and contains_path_traversal(v): + return name + if self.reject_absolute_paths and is_absolute_path(v): + return name + return None + + +DEFAULT_RESOURCE_SECURITY = ResourceSecurity() +"""Secure-by-default policy: traversal, absolute paths, and null bytes rejected.""" + + +class ResourceSecurityError(ValueError): + """Raised when an extracted parameter fails :class:`ResourceSecurity` checks. + + Distinct from a simple ``None`` non-match so that template + iteration can stop at the first security rejection rather than + falling through to a later, possibly more permissive, template. + """ + + def __init__(self, template: str, param: str) -> None: + super().__init__(f"Parameter {param!r} of template {template!r} failed security validation") + self.template = template + self.param = param + + +class ResourceTemplate(BaseModel): + """A template for dynamically creating resources.""" + + uri_template: str = Field(description="URI template with parameters (e.g. weather://{city}/current)") + name: str = Field(description="Name of the resource") + title: str | None = Field(description="Human-readable title of the resource", default=None) + description: str | None = Field(description="Description of what the resource does") + mime_type: str = Field(default="text/plain", description="MIME type of the resource content") + icons: list[Icon] | None = Field(default=None, description="Optional list of icons for the resource template") + annotations: Annotations | None = Field(default=None, description="Optional annotations for the resource template") + meta: dict[str, Any] | None = Field(default=None, description="Optional metadata for this resource template") + fn: Callable[..., Any] = Field(exclude=True) + parameters: dict[str, Any] = Field(description="JSON schema for function parameters") + context_kwarg: str | None = Field(None, description="Name of the kwarg that should receive context") + parsed_template: UriTemplate = Field(exclude=True, description="Parsed RFC 6570 template") + security: ResourceSecurity = Field(exclude=True, description="Path-safety policy for extracted parameters") + + @classmethod + def from_function( + cls, + fn: Callable[..., Any], + uri_template: str, + name: str | None = None, + title: str | None = None, + description: str | None = None, + mime_type: str | None = None, + icons: list[Icon] | None = None, + annotations: Annotations | None = None, + meta: dict[str, Any] | None = None, + context_kwarg: str | None = None, + security: ResourceSecurity = DEFAULT_RESOURCE_SECURITY, + ) -> ResourceTemplate: + """Create a template from a function. + + Raises: + InvalidUriTemplate: If ``uri_template`` is malformed or uses + unsupported RFC 6570 features. + """ + func_name = name or fn.__name__ + if func_name == "": + raise ValueError("You must provide a name for lambda functions") # pragma: no cover + + parsed = UriTemplate.parse(uri_template) + + # Find context parameter if it exists + if context_kwarg is None: # pragma: no branch + context_kwarg = find_context_parameter(fn) + + # Only the argument model is needed; a resource has no output schema to derive + func_arg_metadata = func_metadata( + fn, skip_names=[context_kwarg] if context_kwarg is not None else [], structured_output=False + ) + parameters = func_arg_metadata.arg_model.model_json_schema() + + # ensure the arguments are properly cast + fn = validate_call(fn) + + return cls( + uri_template=uri_template, + name=func_name, + title=title, + description=description or fn.__doc__ or "", + mime_type=mime_type or "text/plain", + icons=icons, + annotations=annotations, + meta=meta, + fn=fn, + parameters=parameters, + context_kwarg=context_kwarg, + parsed_template=parsed, + security=security, + ) + + def matches(self, uri: str) -> dict[str, str | list[str]] | None: + """Check if a URI matches this template and extract parameters. + + Delegates to :meth:`UriTemplate.match` for RFC 6570 extraction, + then applies this template's :class:`ResourceSecurity` policy + (path traversal, absolute paths). + + Returns: + Extracted parameters on success, or ``None`` if the URI + doesn't match the template. + + Raises: + ResourceSecurityError: If the URI matches but an extracted + parameter fails security validation. Raising (rather + than returning ``None``) prevents the resource manager + from silently falling through to a later, possibly more + permissive, template. + """ + params = self.parsed_template.match(uri) + if params is None: + return None + failed = self.security.validate(params) + if failed is not None: + raise ResourceSecurityError(self.uri_template, failed) + return params + + async def create_resource( + self, + uri: str, + params: dict[str, Any], + context: Context[LifespanContextT, RequestT], + ) -> Resource | InputRequiredResult: + """Create a resource from the template with the given parameters. + + An `InputRequiredResult` returned by the template function is passed + through unchanged (the 2026-07-28 multi-round-trip flow); the retry's + answers arrive on `ctx.input_responses`, with `ctx.request_state` + carrying the echoed opaque state. + + Raises: + ResourceError: If the template function raises `ResourceError`. + UnexpectedResourceError: If the template function raises anything other + than `ResourceError` or `MCPError`. `__cause__` is the original exception. + """ + try: + # Add context to params if needed + params = inject_context(self.fn, params, context, self.context_kwarg) + + fn = self.fn + if is_async_callable(fn): + result = await fn(**params) + else: + result = await anyio.to_thread.run_sync(functools.partial(self.fn, **params)) + + if isinstance(result, InputRequiredResult): + return result + + return FunctionResource( + uri=uri, # type: ignore + name=self.name, + title=self.title, + description=self.description, + mime_type=self.mime_type, + icons=self.icons, + annotations=self.annotations, + meta=self.meta, + fn=lambda: result, # Capture result in closure + ) + except (ResourceError, MCPError): + raise + except Exception as exc: + raise UnexpectedResourceError(f"Error creating resource from template {uri}") from exc diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/types.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/types.py new file mode 100644 index 0000000..c8b479b --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/resources/types.py @@ -0,0 +1,230 @@ +"""Concrete resource implementations.""" + +from __future__ import annotations + +import json +from collections.abc import Callable +from functools import partial +from pathlib import Path +from typing import Any + +import anyio +import anyio.to_thread +import httpx2 +import pydantic +import pydantic_core +from mcp_types import Annotations, Icon, InputRequiredResult +from pydantic import Field, validate_call + +from mcp.server.mcpserver.resources.base import Resource +from mcp.shared._callable_inspection import is_async_callable + +# `application/*` types that are textual but predate the `+json`/`+xml` +# structured-syntax suffixes, so the suffix rule below can't catch them. +_TEXTUAL_APPLICATION_TYPES = frozenset({"application/json", "application/xml"}) + + +def _default_file_encoding(mime_type: str) -> str | None: + """The encoding a file of this mime type is decoded with by default. + + A declared `charset=` parameter wins. Otherwise textual types (`text/*`, JSON, + XML) are `utf-8-sig` — UTF-8 that also tolerates a byte-order mark — and + everything else is bytes (None). + """ + essence, *params = (part.strip() for part in mime_type.split(";")) + for param in params: + name, _, value = param.partition("=") + if name.strip().lower() == "charset" and value: + return value.strip().strip('"') + essence = essence.lower() + if essence.startswith("text/") or essence.endswith(("+json", "+xml")) or essence in _TEXTUAL_APPLICATION_TYPES: + return "utf-8-sig" + return None + + +class TextResource(Resource): + """A resource that reads from a string.""" + + text: str = Field(description="Text content of the resource") + + async def read(self) -> str: + """Read the text content.""" + return self.text + + +class BinaryResource(Resource): + """A resource that reads from bytes.""" + + data: bytes = Field(description="Binary content of the resource") + + async def read(self) -> bytes: + """Read the binary content.""" + return self.data # pragma: no cover + + +class FunctionResource(Resource): + """A resource that defers data loading by wrapping a function. + + The function is only called when the resource is read, allowing for lazy loading + of potentially expensive data. This is particularly useful when listing resources, + as the function won't be called until the resource is actually accessed. + + The function can return: + - str for text content (default) + - bytes for binary content + - other types will be converted to JSON + """ + + fn: Callable[[], Any] = Field(exclude=True) + + async def read(self) -> str | bytes: + """Read the resource by calling the wrapped function.""" + fn = self.fn + if is_async_callable(fn): + result = await fn() + else: + result = await anyio.to_thread.run_sync(self.fn) + + if isinstance(result, InputRequiredResult): + # A static resource function can never read the retry's + # input_responses (it takes no Context), so this can only be a + # mistake — reject it instead of JSON-dumping it as content. + raise ValueError( + "static resources cannot return InputRequiredResult; only resource " + "template functions participate in the multi-round-trip flow" + ) + if isinstance(result, Resource): # pragma: no cover + return await result.read() + elif isinstance(result, bytes): + return result + elif isinstance(result, str): + return result + else: + return pydantic_core.to_json(result, fallback=str, indent=2).decode() + + @classmethod + def from_function( + cls, + fn: Callable[..., Any], + uri: str, + name: str | None = None, + title: str | None = None, + description: str | None = None, + mime_type: str | None = None, + icons: list[Icon] | None = None, + annotations: Annotations | None = None, + meta: dict[str, Any] | None = None, + ) -> FunctionResource: + """Create a FunctionResource from a function.""" + func_name = name or fn.__name__ + if func_name == "": # pragma: no cover + raise ValueError("You must provide a name for lambda functions") + + # ensure the arguments are properly cast + fn = validate_call(fn) + + return cls( + uri=uri, + name=func_name, + title=title, + description=description or fn.__doc__ or "", + mime_type=mime_type or "text/plain", + fn=fn, + icons=icons, + annotations=annotations, + meta=meta, + ) + + +class FileResource(Resource): + """A resource that reads from a file. + + The file is decoded with `encoding` and served as text, or read as bytes and + served as a base64 blob when `encoding` is None. When `encoding` is omitted it + defaults to the `charset` declared in `mime_type`, else `"utf-8-sig"` for + textual mime types (`text/*`, JSON, XML) and None for everything else; pass + it explicitly to override either way. + """ + + path: Path = Field(description="Path to the file") + encoding: str | None = Field( + default_factory=lambda data: _default_file_encoding(data["mime_type"]), + description="Text encoding used to decode the file, or None to serve its bytes as a blob", + ) + + @pydantic.field_validator("path") + @classmethod + def validate_absolute_path(cls, path: Path) -> Path: + """Ensure path is absolute.""" + if not path.is_absolute(): + raise ValueError("Path must be absolute") + return path + + @pydantic.field_validator("encoding") + @classmethod + def validate_text_encoding(cls, encoding: str | None) -> str | None: + """Ensure the encoding names a usable text codec, so a mistake fails at construction not at read.""" + if encoding is not None: + # Decoding a probe byte rejects both unknown names and codecs that + # aren't text encodings (base64_codec, rot13, ...) via LookupError. + try: + b"x".decode(encoding) + except LookupError as e: + raise ValueError(str(e)) from e + except UnicodeError: + pass # a real text encoding; the probe byte just doesn't decode in it + return encoding + + async def read(self) -> str | bytes: + """Read the file content.""" + if self.encoding is None: + return await anyio.to_thread.run_sync(self.path.read_bytes) + return await anyio.to_thread.run_sync(partial(self.path.read_text, encoding=self.encoding)) + + +class HttpResource(Resource): + """A resource that reads from an HTTP endpoint.""" + + url: str = Field(description="URL to fetch content from") + mime_type: str = Field(default="application/json", description="MIME type of the resource content") + + async def read(self) -> str | bytes: + """Read the HTTP content.""" + async with httpx2.AsyncClient() as client: # pragma: no cover + response = await client.get(self.url) + response.raise_for_status() + return response.text + + +class DirectoryResource(Resource): + """A resource that lists files in a directory.""" + + path: Path = Field(description="Path to the directory") + recursive: bool = Field(default=False, description="Whether to list files recursively") + pattern: str | None = Field(default=None, description="Optional glob pattern to filter files") + mime_type: str = Field(default="application/json", description="MIME type of the resource content") + + @pydantic.field_validator("path") + @classmethod + def validate_absolute_path(cls, path: Path) -> Path: # pragma: no cover + """Ensure path is absolute.""" + if not path.is_absolute(): + raise ValueError("Path must be absolute") + return path + + def list_files(self) -> list[Path]: # pragma: no cover + """List files in the directory.""" + if not self.path.exists(): + raise FileNotFoundError(f"Directory not found: {self.path}") + if not self.path.is_dir(): + raise NotADirectoryError(f"Not a directory: {self.path}") + + if self.pattern: + return list(self.path.glob(self.pattern)) if not self.recursive else list(self.path.rglob(self.pattern)) + return list(self.path.glob("*")) if not self.recursive else list(self.path.rglob("*")) + + async def read(self) -> str: # Always returns JSON string # pragma: no cover + """Read the directory listing.""" + files = await anyio.to_thread.run_sync(self.list_files) + file_list = [str(f.relative_to(self.path)) for f in files if f.is_file()] + return json.dumps({"files": file_list}, indent=2) diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/server.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/server.py new file mode 100644 index 0000000..93bef16 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/server.py @@ -0,0 +1,1390 @@ +"""MCPServer - A more ergonomic interface for MCP servers.""" + +from __future__ import annotations + +import base64 +import inspect +from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Mapping, Sequence +from contextlib import AbstractAsyncContextManager, asynccontextmanager +from typing import Any, Generic, Literal, TypeVar, cast, overload + +import anyio +import pydantic_core +from mcp_types import ( + INTERNAL_ERROR, + INVALID_PARAMS, + METHOD_NOT_FOUND, + MISSING_REQUIRED_CLIENT_CAPABILITY, + Annotations, + BlobResourceContents, + CallToolRequestParams, + CallToolResult, + ClientCapabilities, + CompleteRequestParams, + CompleteResult, + Completion, + GetPromptRequestParams, + GetPromptResult, + Icon, + InputRequiredResult, + ListPromptsResult, + ListResourcesResult, + ListResourceTemplatesResult, + ListToolsResult, + MissingRequiredClientCapabilityErrorData, + PaginatedRequestParams, + ReadResourceRequestParams, + ReadResourceResult, + TextContent, + TextResourceContents, + ToolAnnotations, +) +from mcp_types import Prompt as MCPPrompt +from mcp_types import PromptArgument as MCPPromptArgument +from mcp_types import Resource as MCPResource +from mcp_types import ResourceTemplate as MCPResourceTemplate +from mcp_types import Tool as MCPTool +from pydantic import BaseModel, ValidationError +from pydantic.networks import AnyUrl +from starlette.applications import Starlette +from starlette.middleware import Middleware +from starlette.middleware.authentication import AuthenticationMiddleware +from starlette.requests import Request +from starlette.responses import Response +from starlette.routing import Mount, Route +from starlette.types import Receive, Scope, Send + +from mcp.server.auth.middleware.auth_context import AuthContextMiddleware +from mcp.server.auth.middleware.bearer_auth import BearerAuthBackend, RequireAuthMiddleware +from mcp.server.auth.provider import OAuthAuthorizationServerProvider, ProviderTokenVerifier, TokenVerifier +from mcp.server.auth.settings import AuthSettings +from mcp.server.caching import CacheableMethod, CacheHint +from mcp.server.context import HandlerResult, ServerMiddleware, ServerRequestContext +from mcp.server.extension import ( + Extension, + MethodBinding, + RequestHandler, + compose_tool_call_handler, + validate_extension_identifier, +) +from mcp.server.lowlevel.helper_types import ReadResourceContents +from mcp.server.lowlevel.server import LifespanResultT, Server +from mcp.server.lowlevel.server import lifespan as default_lifespan +from mcp.server.mcpserver.context import Context +from mcp.server.mcpserver.exceptions import ( + ResourceError, + ResourceNotFoundError, + ToolError, + UnexpectedResourceError, + UnexpectedToolError, +) +from mcp.server.mcpserver.prompts import Prompt, PromptManager +from mcp.server.mcpserver.resources import ( + DEFAULT_RESOURCE_SECURITY, + FunctionResource, + Resource, + ResourceManager, + ResourceSecurity, +) +from mcp.server.mcpserver.tools import Tool, ToolManager +from mcp.server.mcpserver.utilities.context_injection import find_context_parameter +from mcp.server.mcpserver.utilities.logging import configure_logging, get_logger +from mcp.server.request_state import RequestStateBoundary, RequestStateSecurity +from mcp.server.sse import SseServerTransport +from mcp.server.stdio import stdio_server +from mcp.server.streamable_http import EventStore +from mcp.server.streamable_http_manager import StreamableHTTPSessionManager +from mcp.server.subscriptions import InMemorySubscriptionBus, ListenHandler, SubscriptionBus +from mcp.server.transport_security import DEFAULT_MAX_REQUEST_BODY_SIZE, TransportSecuritySettings +from mcp.shared.exceptions import MCPError +from mcp.shared.uri_template import UriTemplate + +logger = get_logger(__name__) + +_CallableT = TypeVar("_CallableT", bound=Callable[..., Any]) + + +class Settings(BaseModel, Generic[LifespanResultT]): + """MCPServer settings, as passed to the `MCPServer` constructor.""" + + # Server settings + debug: bool + log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] + + # resource settings + warn_on_duplicate_resources: bool + + # tool settings + warn_on_duplicate_tools: bool + + # prompt settings + warn_on_duplicate_prompts: bool + + dependencies: list[str] + """List of dependencies to install in the server environment. Used by the `mcp install` and `mcp dev` CLI.""" + + lifespan: Callable[[MCPServer[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]] | None + """An async context manager that will be called when the server is started.""" + + auth: AuthSettings | None + + +_MISSING_AUDIENCE = ( + "request_state_security is configured but this server has no name. Sealed\n" + "requestState carries the server name as an audience claim, so state minted by\n" + "another service that shares the same keys is rejected; unnamed servers would\n" + "all stamp the same placeholder and the check would mean nothing. Name the\n" + 'server (MCPServer("my-service", ...)) or set RequestStateSecurity(audience=...).' +) + + +def lifespan_wrapper( + app: MCPServer[LifespanResultT], + lifespan: Callable[[MCPServer[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]], +) -> Callable[[Server[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]]: + @asynccontextmanager + async def wrap(_: Server[LifespanResultT]) -> AsyncIterator[LifespanResultT]: + async with lifespan(app) as context: + yield context + + return wrap + + +class MCPServer(Generic[LifespanResultT]): + def __init__( + self, + name: str | None = None, + title: str | None = None, + description: str | None = None, + instructions: str | None = None, + website_url: str | None = None, + icons: list[Icon] | None = None, + version: str = "", + auth_server_provider: OAuthAuthorizationServerProvider[Any, Any, Any] | None = None, + token_verifier: TokenVerifier | None = None, + *, + tools: list[Tool] | None = None, + resources: list[Resource] | None = None, + extensions: Sequence[Extension] | None = None, + debug: bool = False, + log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO", + warn_on_duplicate_resources: bool = True, + warn_on_duplicate_tools: bool = True, + warn_on_duplicate_prompts: bool = True, + dependencies: list[str] | None = None, + lifespan: Callable[[MCPServer[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]] | None = None, + auth: AuthSettings | None = None, + resource_security: ResourceSecurity = DEFAULT_RESOURCE_SECURITY, + request_state_security: RequestStateSecurity | None = None, + cache_hints: Mapping[CacheableMethod, CacheHint] | None = None, + subscriptions: SubscriptionBus | None = None, + middleware: Sequence[ServerMiddleware[Any]] | None = None, + ): + self._resource_security = resource_security + self.settings = Settings( + debug=debug, + log_level=log_level, + warn_on_duplicate_resources=warn_on_duplicate_resources, + warn_on_duplicate_tools=warn_on_duplicate_tools, + warn_on_duplicate_prompts=warn_on_duplicate_prompts, + dependencies=dependencies or [], + lifespan=lifespan, + auth=auth, + ) + self.dependencies = self.settings.dependencies + + self._tool_manager = ToolManager(tools=tools, warn_on_duplicate_tools=self.settings.warn_on_duplicate_tools) + self._resource_manager = ResourceManager( + resources=resources, warn_on_duplicate_resources=self.settings.warn_on_duplicate_resources + ) + self._prompt_manager = PromptManager(warn_on_duplicate_prompts=self.settings.warn_on_duplicate_prompts) + # The subscriptions/listen fan-out seam (2026-07-28). The default bus is + # in-process; pass an `SubscriptionBus` implementation over an external pub/sub + # backend to fan events out across replicas. + self._subscriptions: SubscriptionBus = subscriptions if subscriptions is not None else InMemorySubscriptionBus() + self._lowlevel_server = Server( + name=name or "mcp-server", + title=title, + description=description, + instructions=instructions, + website_url=website_url, + icons=icons, + version=version, + cache_hints=cache_hints, + on_list_tools=self._handle_list_tools, + on_call_tool=self._handle_call_tool, + on_list_resources=self._handle_list_resources, + on_read_resource=self._handle_read_resource, + on_list_resource_templates=self._handle_list_resource_templates, + on_list_prompts=self._handle_list_prompts, + on_get_prompt=self._handle_get_prompt, + on_subscriptions_listen=ListenHandler(self._subscriptions), + # TODO(Marcelo): It seems there's a type mismatch between the lifespan type from an MCPServer and Server. + # We need to create a Lifespan type that is a generic on the server type, like Starlette does. + lifespan=(lifespan_wrapper(self, self.settings.lifespan) if self.settings.lifespan else default_lifespan), # type: ignore + ) + # Ordering: inside OpenTelemetry (spans record the sealed wire form). + # Extension interceptors run at the handler layer, inside this + # boundary, so they see plaintext. + if request_state_security is None: + security = RequestStateSecurity.ephemeral() + else: + # A supplied policy usually means shared keys, where the audience claim is + # what separates services; an unnamed server would stamp the placeholder. + if not name and request_state_security.audience is None: + raise ValueError(_MISSING_AUDIENCE) + security = request_state_security + self._lowlevel_server.middleware.append(RequestStateBoundary(security, default_audience=self.name)) + # User middleware runs inside the SDK's built-ins (OpenTelemetry, then the + # request-state boundary), outermost-first in the order given. + self._lowlevel_server.middleware.extend(middleware or ()) + # Validate auth configuration + if self.settings.auth is not None: + if auth_server_provider and token_verifier: # pragma: no cover + raise ValueError("Cannot specify both auth_server_provider and token_verifier") + if not auth_server_provider and not token_verifier: # pragma: no cover + raise ValueError("Must specify either auth_server_provider or token_verifier when auth is enabled") + elif auth_server_provider or token_verifier: + raise ValueError("Cannot specify auth_server_provider or token_verifier without auth settings") + + self._auth_server_provider = auth_server_provider + self._token_verifier = token_verifier + + # Create token verifier from provider if needed (backwards compatibility) + if auth_server_provider and not token_verifier: + self._token_verifier = ProviderTokenVerifier(auth_server_provider) + self._custom_starlette_routes: list[Route] = [] + + # Configure logging + configure_logging(self.settings.log_level) + + self._extensions: list[Extension] = [] + for extension in extensions or (): + self._apply_extension(extension) + self._install_extension_interceptor() + + @property + def name(self) -> str: + return self._lowlevel_server.name + + @property + def middleware(self) -> list[ServerMiddleware[Any]]: + """The middleware chain wrapping every inbound message, outermost-first. + + The same list as the low-level `Server.middleware`: append an + `async (ctx, call_next)` callable to observe, refuse, or rewrite + messages before they reach a handler. Provisional - the signature may + change in a 2.x minor release; see the middleware guide. + """ + return self._lowlevel_server.middleware + + @property + def title(self) -> str | None: + return self._lowlevel_server.title + + @property + def description(self) -> str | None: + return self._lowlevel_server.description + + @property + def instructions(self) -> str | None: + return self._lowlevel_server.instructions + + @property + def website_url(self) -> str | None: + return self._lowlevel_server.website_url + + @property + def icons(self) -> list[Icon] | None: + return self._lowlevel_server.icons + + @property + def version(self) -> str: + return self._lowlevel_server.version + + @property + def session_manager(self) -> StreamableHTTPSessionManager: + """Get the StreamableHTTP session manager. + + This is exposed to enable advanced use cases like mounting multiple + MCPServer instances in a single FastAPI application. + + Raises: + RuntimeError: If called before streamable_http_app() has been called. + """ + return self._lowlevel_server.session_manager + + def _apply_extension(self, extension: Extension) -> None: + """Apply one opt-in extension's contributions through the public surface. + + Registers its tools/resources/methods and advertises its settings under + `ServerCapabilities.extensions[extension.identifier]`. Extensions are fixed + at construction, so this is private; the `tools/call` interceptor is + composed once afterwards by `_install_extension_interceptor`. + """ + identifier = getattr(extension, "identifier", None) + validate_extension_identifier(identifier, owner=type(extension).__name__) + if any(e.identifier == identifier for e in self._extensions): + raise ValueError(f"Extension {identifier!r} is already registered") + self._extensions.append(extension) + + for tool in extension.tools(): + self.add_tool(tool.fn, meta=tool.meta, **tool.kwargs) + for resource in extension.resources(): + self.add_resource(resource.resource) + for method in extension.methods(): + if self._lowlevel_server.get_request_handler(method.method) is not None: + raise ValueError( + f"Extension {identifier!r} binds method {method.method!r}, which is already " + "registered; extension methods are additive and cannot replace another handler" + ) + handler = _version_gated(method) if method.protocol_versions is not None else method.handler + self._lowlevel_server.add_request_handler(method.method, method.params_type, handler) + + self._lowlevel_server.extensions[extension.identifier] = extension.settings() + + def _install_extension_interceptor(self) -> None: + """Wrap the `tools/call` handler with every extension's interceptor. + + Installed only when at least one extension overrides `intercept_tool_call`, + so a server with purely additive extensions keeps the bare handler. The + chain wraps the handler itself, below the runner's outbound envelope + pass, so a short-circuiting interceptor's result is sieved and stamped + exactly like a handler result. + """ + if any(type(e).intercept_tool_call is not Extension.intercept_tool_call for e in self._extensions): + self._lowlevel_server.add_request_handler( + "tools/call", + CallToolRequestParams, + compose_tool_call_handler(self._extensions, self._handle_call_tool), + ) + + @overload + def run(self, transport: Literal["stdio"] = ...) -> None: ... + + @overload + def run( + self, + transport: Literal["sse"], + *, + host: str = ..., + port: int = ..., + sse_path: str = ..., + message_path: str = ..., + max_request_body_size: int = ..., + transport_security: TransportSecuritySettings | None = ..., + ) -> None: ... + + @overload + def run( + self, + transport: Literal["streamable-http"], + *, + host: str = ..., + port: int = ..., + streamable_http_path: str = ..., + json_response: bool = ..., + stateless_http: bool = ..., + event_store: EventStore | None = ..., + retry_interval: int | None = ..., + max_request_body_size: int = ..., + transport_security: TransportSecuritySettings | None = ..., + ) -> None: ... + + def run( + self, + transport: Literal["stdio", "sse", "streamable-http"] = "stdio", + **kwargs: Any, + ) -> None: + """Run the MCP server. Note this is a synchronous function. + + Args: + transport: Transport protocol to use ("stdio", "sse", or "streamable-http") + **kwargs: Transport-specific options (see overloads for details) + """ + TRANSPORTS = Literal["stdio", "sse", "streamable-http"] + if transport not in TRANSPORTS.__args__: # type: ignore # pragma: no cover + raise ValueError(f"Unknown transport: {transport}") + + match transport: + case "stdio": + anyio.run(self.run_stdio_async) + case "sse": # pragma: no cover + anyio.run(lambda: self.run_sse_async(**kwargs)) + case "streamable-http": # pragma: no cover + anyio.run(lambda: self.run_streamable_http_async(**kwargs)) + + async def _handle_list_tools( + self, ctx: ServerRequestContext[LifespanResultT], params: PaginatedRequestParams | None + ) -> ListToolsResult: + return ListToolsResult(tools=await self.list_tools()) + + async def _handle_call_tool( + self, ctx: ServerRequestContext[LifespanResultT], params: CallToolRequestParams + ) -> CallToolResult | InputRequiredResult: + context = Context(request_context=ctx, mcp_server=self, input_params=params, subscriptions=self._subscriptions) + try: + return await self.call_tool(params.name, params.arguments or {}, context) + except MCPError: + raise + except Exception as exc: + if isinstance(exc, ToolError) and not isinstance(exc, UnexpectedToolError): + if isinstance(exc.__cause__, ValidationError): + # Field names only: the rejected values are the caller's data. + fields = sorted({".".join(str(part) for part in err["loc"]) for err in exc.__cause__.errors()}) + logger.info("Tool %r rejected arguments: %r", params.name, fields) + else: + # %r keeps peer-supplied text on one line. + logger.info("Tool %r failed: %r", params.name, str(exc)) + else: + logger.exception("Tool %r raised an unexpected exception", params.name) + return CallToolResult(content=[TextContent(type="text", text=str(exc))], is_error=True) + + async def _handle_list_resources( + self, ctx: ServerRequestContext[LifespanResultT], params: PaginatedRequestParams | None + ) -> ListResourcesResult: + return ListResourcesResult(resources=await self.list_resources()) + + async def _handle_read_resource( + self, ctx: ServerRequestContext[LifespanResultT], params: ReadResourceRequestParams + ) -> ReadResourceResult | InputRequiredResult: + context = Context(request_context=ctx, mcp_server=self, input_params=params, subscriptions=self._subscriptions) + try: + results = await self.read_resource(params.uri, context) + except ResourceError as err: + if isinstance(err, UnexpectedResourceError): + logger.exception("Resource %r raised an unexpected exception", str(params.uri)) + else: + logger.info("Resource %r failed: %r", str(params.uri), str(err)) + code = INVALID_PARAMS if isinstance(err, ResourceNotFoundError) else INTERNAL_ERROR + raise MCPError(code=code, message=str(err), data={"uri": str(params.uri)}) + if isinstance(results, InputRequiredResult): + return results + contents: list[TextResourceContents | BlobResourceContents] = [] + for item in results: + if isinstance(item.content, bytes): + contents.append( + BlobResourceContents( + uri=params.uri, + blob=base64.b64encode(item.content).decode(), + mime_type=item.mime_type or "application/octet-stream", + _meta=item.meta, + ) + ) + else: + contents.append( + TextResourceContents( + uri=params.uri, + text=item.content, + mime_type=item.mime_type or "text/plain", + _meta=item.meta, + ) + ) + return ReadResourceResult(contents=contents) + + async def _handle_list_resource_templates( + self, ctx: ServerRequestContext[LifespanResultT], params: PaginatedRequestParams | None + ) -> ListResourceTemplatesResult: + return ListResourceTemplatesResult(resource_templates=await self.list_resource_templates()) + + async def _handle_list_prompts( + self, ctx: ServerRequestContext[LifespanResultT], params: PaginatedRequestParams | None + ) -> ListPromptsResult: + return ListPromptsResult(prompts=await self.list_prompts()) + + async def _handle_get_prompt( + self, ctx: ServerRequestContext[LifespanResultT], params: GetPromptRequestParams + ) -> GetPromptResult | InputRequiredResult: + context = Context(request_context=ctx, mcp_server=self, input_params=params, subscriptions=self._subscriptions) + return await self.get_prompt(params.name, params.arguments, context) + + async def list_tools(self) -> list[MCPTool]: + """List all available tools.""" + tools = self._tool_manager.list_tools() + return [ + MCPTool( + name=info.name, + title=info.title, + description=info.description, + input_schema=info.parameters, + output_schema=info.output_schema, + annotations=info.annotations, + icons=info.icons, + _meta=info.meta, + ) + for info in tools + ] + + async def call_tool( + self, name: str, arguments: dict[str, Any], context: Context[LifespanResultT, Any] | None = None + ) -> CallToolResult | InputRequiredResult: + """Call a tool by name with arguments. + + Raises: + ToolError: If the tool is unknown, the arguments fail validation, or the + tool (or a resolver) raises `ToolError` or `ResourceError`. + UnexpectedToolError: If the tool (or a resolver) raises anything else, or + its return value fails output conversion. `__cause__` is the original + exception (or, for a nested tool or resource crash, its wrapper). + MCPError: Raised by the tool or a resolver; passed through unchanged. + """ + if context is None: + context = Context(mcp_server=self, subscriptions=self._subscriptions) + return await self._tool_manager.call_tool(name, arguments, context, convert_result=True) + + async def list_resources(self) -> list[MCPResource]: + """List all available resources.""" + + resources = self._resource_manager.list_resources() + return [ + MCPResource( + uri=resource.uri, + name=resource.name or "", + title=resource.title, + description=resource.description, + mime_type=resource.mime_type, + icons=resource.icons, + annotations=resource.annotations, + _meta=resource.meta, + ) + for resource in resources + ] + + async def list_resource_templates(self) -> list[MCPResourceTemplate]: + templates = self._resource_manager.list_templates() + return [ + MCPResourceTemplate( + uri_template=template.uri_template, + name=template.name, + title=template.title, + description=template.description, + mime_type=template.mime_type, + icons=template.icons, + annotations=template.annotations, + _meta=template.meta, + ) + for template in templates + ] + + async def read_resource( + self, uri: AnyUrl | str, context: Context[LifespanResultT, Any] | None = None + ) -> Iterable[ReadResourceContents] | InputRequiredResult: + """Read a resource by URI. + + An `InputRequiredResult` returned by a resource template function is + passed through unchanged (the 2026-07-28 multi-round-trip flow); the + retry's answers arrive on `ctx.input_responses`, with + `ctx.request_state` carrying the echoed opaque state. + + Raises: + ResourceNotFoundError: If no resource or template matches the URI. + ResourceError: If the resource or template function raises `ResourceError`. + UnexpectedResourceError: If reading the resource (or creating it from a + template) raises anything other than `ResourceError` or `MCPError`. + `__cause__` is the original exception. + MCPError: Raised by the resource or template function; passed through unchanged. + """ + if context is None: + context = Context(mcp_server=self, subscriptions=self._subscriptions) + try: + resource = await self._resource_manager.get_resource(uri, context) + if isinstance(resource, InputRequiredResult): + return resource + # Checked at runtime because a Resource subclass may not honour the annotation. + content = cast(object, await resource.read()) + if not isinstance(content, str | bytes): + raise TypeError(f"Resource.read() must return str or bytes, not {type(content).__name__}") + return [ReadResourceContents(content=content, mime_type=resource.mime_type, meta=resource.meta)] + except (MCPError, ResourceError): + raise + except Exception as exc: + raise UnexpectedResourceError(f"Error reading resource {uri}") from exc + + def add_tool( + self, + fn: Callable[..., Any], + name: str | None = None, + title: str | None = None, + description: str | None = None, + annotations: ToolAnnotations | None = None, + icons: list[Icon] | None = None, + meta: dict[str, Any] | None = None, + structured_output: bool | None = None, + ) -> None: + """Add a tool to the server. + + The tool function can optionally request a Context object by adding a parameter + with the Context type annotation. See the @tool decorator for examples. + + Args: + fn: The function to register as a tool + name: Optional name for the tool (defaults to function name) + title: Optional human-readable title for the tool + description: Optional description of what the tool does + annotations: Optional ToolAnnotations providing additional tool information + icons: Optional list of icons for the tool + meta: Optional metadata dictionary for the tool + structured_output: Controls whether the tool's output is structured or unstructured + - If None, auto-detects based on the function's return type annotation + - If True, creates a structured tool (return type annotation permitting) + - If False, unconditionally creates an unstructured tool + """ + self._tool_manager.add_tool( + fn, + name=name, + title=title, + description=description, + annotations=annotations, + icons=icons, + meta=meta, + structured_output=structured_output, + ) + + def remove_tool(self, name: str) -> None: + """Remove a tool from the server by name. + + Args: + name: The name of the tool to remove + + Raises: + ToolError: If the tool does not exist + """ + self._tool_manager.remove_tool(name) + + def tool( + self, + name: str | None = None, + title: str | None = None, + description: str | None = None, + annotations: ToolAnnotations | None = None, + icons: list[Icon] | None = None, + meta: dict[str, Any] | None = None, + structured_output: bool | None = None, + ) -> Callable[[_CallableT], _CallableT]: + """Decorator to register a tool. + + Tools can optionally request a Context object by adding a parameter with the + Context type annotation. The context provides access to MCP capabilities like + logging, progress reporting, and resource access. + + Args: + name: Optional name for the tool (defaults to function name) + title: Optional human-readable title for the tool + description: Optional description of what the tool does + annotations: Optional ToolAnnotations providing additional tool information + icons: Optional list of icons for the tool + meta: Optional metadata dictionary for the tool + structured_output: Controls whether the tool's output is structured or unstructured + - If None, auto-detects based on the function's return type annotation + - If True, creates a structured tool (return type annotation permitting) + - If False, unconditionally creates an unstructured tool + + Example: + ```python + @server.tool() + def my_tool(x: int) -> str: + return str(x) + ``` + + ```python + @server.tool() + async def tool_with_context(x: int, ctx: Context) -> str: + await ctx.info(f"Processing {x}") + return str(x) + ``` + + ```python + @server.tool() + async def async_tool(x: int, context: Context) -> str: + await context.report_progress(50, 100) + return str(x) + ``` + """ + # Check if user passed function directly instead of calling decorator + if callable(name): + raise TypeError( + "The @tool decorator was used incorrectly. Did you forget to call it? Use @tool() instead of @tool" + ) + + def decorator(fn: _CallableT) -> _CallableT: + self.add_tool( + fn, + name=name, + title=title, + description=description, + annotations=annotations, + icons=icons, + meta=meta, + structured_output=structured_output, + ) + return fn + + return decorator + + def completion(self): + """Decorator to register a completion handler. + + The completion handler receives: + - ref: PromptReference or ResourceTemplateReference + - argument: CompletionArgument with name and partial value + - context: Optional CompletionContext with previously resolved arguments + + Example: + ```python + @mcp.completion() + async def handle_completion(ref, argument, context): + if isinstance(ref, ResourceTemplateReference): + # Return completions based on ref, argument, and context + return Completion(values=["option1", "option2"]) + return None + ``` + """ + + def decorator(func: _CallableT) -> _CallableT: + async def handler( + ctx: ServerRequestContext[LifespanResultT], params: CompleteRequestParams + ) -> CompleteResult: + try: + result = await func(params.ref, params.argument, params.context) + return CompleteResult( + completion=result if result is not None else Completion(values=[], total=None, has_more=None), + ) + except MCPError: + raise + except Exception as exc: + logger.exception("Completion for argument %r raised an unexpected exception", params.argument.name) + raise MCPError( + code=INTERNAL_ERROR, message=f"Error completing argument {params.argument.name}" + ) from exc + + self._lowlevel_server.add_request_handler("completion/complete", CompleteRequestParams, handler) + return func + + return decorator + + def add_resource(self, resource: Resource) -> None: + """Add a resource to the server. + + Args: + resource: A Resource instance to add + """ + self._resource_manager.add_resource(resource) + + def resource( + self, + uri: str, + *, + name: str | None = None, + title: str | None = None, + description: str | None = None, + mime_type: str | None = None, + icons: list[Icon] | None = None, + annotations: Annotations | None = None, + meta: dict[str, Any] | None = None, + security: ResourceSecurity | None = None, + ) -> Callable[[_CallableT], _CallableT]: + """Decorator to register a function as a resource. + + The function will be called when the resource is read to generate its content. + The function can return: + - str for text content + - bytes for binary content + - an InputRequiredResult (template resources only; passed through + unchanged for the 2026-07-28 multi-round-trip flow — read + `ctx.input_responses` on the retry) + - other types will be converted to JSON + + If the URI contains parameters (e.g. "resource://{param}"), it is + registered as a template resource. Otherwise it is registered as a + static resource; function parameters on a static URI raise an error. + + Args: + uri: URI for the resource (e.g. "resource://my-resource" or "resource://{param}") + name: Optional name for the resource + title: Optional human-readable title for the resource + description: Optional description of the resource + mime_type: Optional MIME type for the resource + icons: Optional list of icons for the resource + annotations: Optional annotations for the resource + meta: Optional metadata dictionary for the resource + security: Path-safety policy for extracted template parameters. + Defaults to the server's ``resource_security`` setting. + Only applies to template resources. + + Example: + ```python + @server.resource("resource://my-resource") + def get_data() -> str: + return "Hello, world!" + + @server.resource("resource://my-resource") + async def get_data() -> str: + data = await fetch_data() + return f"Hello, world! {data}" + + @server.resource("resource://{city}/weather") + def get_weather(city: str) -> str: + return f"Weather for {city}" + + @server.resource("resource://{city}/weather") + async def get_weather(city: str) -> str: + data = await fetch_weather(city) + return f"Weather for {city}: {data}" + ``` + + Raises: + InvalidUriTemplate: If ``uri`` is not a valid RFC 6570 template. + ValueError: If URI template parameters don't match the + function's parameters, or if a parameter bound to a + ``{?...}``/``{&...}`` query variable has no default + (the client may omit it). + TypeError: If the decorator is applied without being called + (``@resource`` instead of ``@resource("uri")``). + """ + # Check if user passed function directly instead of calling decorator + if callable(uri): + raise TypeError( + "The @resource decorator was used incorrectly. " + "Did you forget to call it? Use @resource('uri') instead of @resource" + ) + + # Parse once, early — surfaces malformed-template errors at + # decoration time with a clear position, and gives us correct + # variable names for all RFC 6570 operators. + parsed = UriTemplate.parse(uri) + uri_params = set(parsed.variable_names) + + def decorator(fn: _CallableT) -> _CallableT: + sig = inspect.signature(fn) + context_param = find_context_parameter(fn) + func_params = {p for p in sig.parameters.keys() if p != context_param} + + # Template/static is decided purely by the URI: variables + # present means template, none means static. + if uri_params: + if uri_params != func_params: + raise ValueError( + f"Mismatch between URI parameters {uri_params} and function parameters {func_params}" + ) + + # A {?...}/{&...} query variable is optional on the wire: + # match() omits it from the extracted parameters when the + # client leaves it out of the URI. The handler parameter + # bound to it must therefore have a Python default; without + # one, the author only finds out on the first request that + # omits it, as an opaque internal error. + missing_defaults = sorted( + name + for name in parsed.query_variable_names + if sig.parameters[name].default is inspect.Parameter.empty + ) + if missing_defaults: + raise ValueError( + f"Resource {uri!r}: query parameter(s) {missing_defaults} have no " + f"default value. A client may omit a {{?...}}/{{&...}} query " + f"parameter, so the matching handler parameter must declare a " + f"default." + ) + + # Register as template + self._resource_manager.add_template( + fn=fn, + uri_template=uri, + name=name, + title=title, + description=description, + mime_type=mime_type, + icons=icons, + annotations=annotations, + security=security if security is not None else self._resource_security, + meta=meta, + ) + else: + if func_params: + raise ValueError( + f"Resource {uri!r} has no URI template variables, but the " + f"handler declares parameters {func_params}. Add matching " + f"{{...}} variables to the URI or remove the parameters." + ) + if context_param is not None: + raise ValueError( + f"Resource {uri!r} has no URI template variables, but the " + f"handler declares a Context parameter. Context injection " + f"for static resources is not supported. " + f"Add a template variable to the URI or remove the " + f"Context parameter." + ) + # Register as regular resource + resource = FunctionResource.from_function( + fn=fn, + uri=uri, + name=name, + title=title, + description=description, + mime_type=mime_type, + icons=icons, + annotations=annotations, + meta=meta, + ) + self.add_resource(resource) + return fn + + return decorator + + def add_prompt(self, prompt: Prompt) -> None: + """Add a prompt to the server. + + Args: + prompt: A Prompt instance to add + """ + self._prompt_manager.add_prompt(prompt) + + def remove_prompt(self, name: str) -> None: + """Remove a prompt from the server by name. + + Args: + name: The name of the prompt to remove + + Raises: + ValueError: If the prompt does not exist + """ + self._prompt_manager.remove_prompt(name) + + def prompt( + self, + name: str | None = None, + title: str | None = None, + description: str | None = None, + icons: list[Icon] | None = None, + ) -> Callable[[_CallableT], _CallableT]: + """Decorator to register a prompt. + + The function returns the prompt messages (a string, content block, `Image`/`Audio`, + `Message`, dict, or a sequence of these), or an `InputRequiredResult` to request + client input first (the 2026-07-28 multi-round-trip flow — read + `ctx.input_responses` on the retry). + + Args: + name: Optional name for the prompt (defaults to function name) + title: Optional human-readable title for the prompt + description: Optional description of what the prompt does + icons: Optional list of icons for the prompt + + Example: + ```python + @server.prompt() + def analyze_table(table_name: str) -> list[Message]: + schema = read_table_schema(table_name) + return [ + { + "role": "user", + "content": f"Analyze this schema:\n{schema}" + } + ] + + @server.prompt() + async def analyze_file(path: str) -> list[Message]: + content = await read_file(path) + return [ + { + "role": "user", + "content": { + "type": "resource", + "resource": { + "uri": f"file://{path}", + "text": content + } + } + } + ] + ``` + """ + # Check if user passed function directly instead of calling decorator + if callable(name): + raise TypeError( + "The @prompt decorator was used incorrectly. " + "Did you forget to call it? Use @prompt() instead of @prompt" + ) + + def decorator(func: _CallableT) -> _CallableT: + prompt = Prompt.from_function(func, name=name, title=title, description=description, icons=icons) + self.add_prompt(prompt) + return func + + return decorator + + def custom_route( + self, + path: str, + methods: list[str], + name: str | None = None, + include_in_schema: bool = True, + ): + """Decorator to register a custom HTTP route on the MCP server. + + Allows adding arbitrary HTTP endpoints outside the standard MCP protocol, + which can be useful for OAuth callbacks, health checks, or admin APIs. + The handler function must be an async function that accepts a Starlette + Request and returns a Response. + + Routes using this decorator will not require authorization. It is intended + for uses that are either a part of authorization flows or intended to be + public such as health check endpoints. + + Args: + path: URL path for the route (e.g., "/oauth/callback") + methods: List of HTTP methods to support (e.g., ["GET", "POST"]) + name: Optional name for the route (to reference this route with + Starlette's reverse URL lookup feature) + include_in_schema: Whether to include in OpenAPI schema, defaults to True + + Example: + ```python + @server.custom_route("/health", methods=["GET"]) + async def health_check(request: Request) -> Response: + return JSONResponse({"status": "ok"}) + ``` + """ + + def decorator( + func: Callable[[Request], Awaitable[Response]], + ) -> Callable[[Request], Awaitable[Response]]: + self._custom_starlette_routes.append( + Route(path, endpoint=func, methods=methods, name=name, include_in_schema=include_in_schema) + ) + return func + + return decorator + + async def run_stdio_async(self) -> None: + """Run the server using stdio transport.""" + async with stdio_server() as (read_stream, write_stream): + await self._lowlevel_server.run( + read_stream, + write_stream, + self._lowlevel_server.create_initialization_options(), + ) + + async def run_sse_async( # pragma: no cover + self, + *, + host: str = "127.0.0.1", + port: int = 8000, + sse_path: str = "/sse", + message_path: str = "/messages/", + max_request_body_size: int = DEFAULT_MAX_REQUEST_BODY_SIZE, + transport_security: TransportSecuritySettings | None = None, + ) -> None: + """Run the server using SSE transport.""" + import uvicorn + + starlette_app = self.sse_app( + sse_path=sse_path, + message_path=message_path, + max_request_body_size=max_request_body_size, + transport_security=transport_security, + host=host, + ) + + config = uvicorn.Config( + starlette_app, + host=host, + port=port, + log_level=self.settings.log_level.lower(), + ) + server = uvicorn.Server(config) + await server.serve() + + async def run_streamable_http_async( # pragma: no cover + self, + *, + host: str = "127.0.0.1", + port: int = 8000, + streamable_http_path: str = "/mcp", + json_response: bool = False, + stateless_http: bool = False, + event_store: EventStore | None = None, + retry_interval: int | None = None, + max_request_body_size: int = DEFAULT_MAX_REQUEST_BODY_SIZE, + transport_security: TransportSecuritySettings | None = None, + ) -> None: + """Run the server using StreamableHTTP transport.""" + import uvicorn + + starlette_app = self.streamable_http_app( + streamable_http_path=streamable_http_path, + json_response=json_response, + stateless_http=stateless_http, + event_store=event_store, + retry_interval=retry_interval, + max_request_body_size=max_request_body_size, + transport_security=transport_security, + host=host, + ) + + config = uvicorn.Config( + starlette_app, + host=host, + port=port, + log_level=self.settings.log_level.lower(), + ) + server = uvicorn.Server(config) + await server.serve() + + def sse_app( + self, + *, + sse_path: str = "/sse", + message_path: str = "/messages/", + max_request_body_size: int = DEFAULT_MAX_REQUEST_BODY_SIZE, + transport_security: TransportSecuritySettings | None = None, + host: str = "127.0.0.1", + ) -> Starlette: + """Return an instance of the SSE server app.""" + # Auto-enable DNS rebinding protection for localhost (IPv4 and IPv6) + if transport_security is None and host in ("127.0.0.1", "localhost", "::1"): + transport_security = TransportSecuritySettings( + enable_dns_rebinding_protection=True, + allowed_hosts=["127.0.0.1:*", "localhost:*", "[::1]:*"], + allowed_origins=["http://127.0.0.1:*", "http://localhost:*", "http://[::1]:*"], + ) + + sse = SseServerTransport( + message_path, security_settings=transport_security, max_request_body_size=max_request_body_size + ) + + async def handle_sse(scope: Scope, receive: Receive, send: Send): # pragma: no cover + # Add client ID from auth context into request context if available + + async with sse.connect_sse(scope, receive, send) as streams: + await self._lowlevel_server.run( + streams[0], streams[1], self._lowlevel_server.create_initialization_options() + ) + return Response() + + # Create routes + routes: list[Route | Mount] = [] + middleware: list[Middleware] = [] + required_scopes: list[str] = [] + + # Set up auth if configured + if self.settings.auth: # pragma: no cover + required_scopes = self.settings.auth.required_scopes or [] + + # Add auth middleware if token verifier is available + if self._token_verifier: + middleware = [ + # extract auth info from request (but do not require it) + Middleware( + AuthenticationMiddleware, + backend=BearerAuthBackend(self._token_verifier), + ), + # Add the auth context middleware to store + # authenticated user in a contextvar + Middleware(AuthContextMiddleware), + ] + + # Add auth endpoints if auth server provider is configured + if self._auth_server_provider: + from mcp.server.auth.routes import create_auth_routes + + routes.extend( + create_auth_routes( + provider=self._auth_server_provider, + issuer_url=self.settings.auth.issuer_url, + service_documentation_url=self.settings.auth.service_documentation_url, + client_registration_options=self.settings.auth.client_registration_options, + revocation_options=self.settings.auth.revocation_options, + identity_assertion_enabled=self.settings.auth.identity_assertion_enabled, + ) + ) + + # When auth is configured, require authentication + if self._token_verifier: # pragma: no cover + # Determine resource metadata URL + resource_metadata_url = None + if self.settings.auth and self.settings.auth.resource_server_url: + from mcp.server.auth.routes import build_resource_metadata_url + + # Build compliant metadata URL for WWW-Authenticate header + resource_metadata_url = build_resource_metadata_url(self.settings.auth.resource_server_url) + + # Auth is enabled, wrap the endpoints with RequireAuthMiddleware + routes.append( + Route( + sse_path, + endpoint=RequireAuthMiddleware(handle_sse, required_scopes, resource_metadata_url), + methods=["GET"], + ) + ) + routes.append( + Mount( + message_path, + app=RequireAuthMiddleware(sse.handle_post_message, required_scopes, resource_metadata_url), + ) + ) + else: + # Auth is disabled, no need for RequireAuthMiddleware + # Since handle_sse is an ASGI app, we need to create a compatible endpoint + async def sse_endpoint(request: Request) -> Response: # pragma: no cover + # Convert the Starlette request to ASGI parameters + return await handle_sse(request.scope, request.receive, request._send) # type: ignore[reportPrivateUsage] + + routes.append( + Route( + sse_path, + endpoint=sse_endpoint, + methods=["GET"], + ) + ) + routes.append( + Mount( + message_path, + app=sse.handle_post_message, + ) + ) + # Add protected resource metadata endpoint if configured as RS + if self.settings.auth and self.settings.auth.resource_server_url: # pragma: no cover + from mcp.server.auth.routes import create_protected_resource_routes + + routes.extend( + create_protected_resource_routes( + resource_url=self.settings.auth.resource_server_url, + authorization_servers=[self.settings.auth.issuer_url], + scopes_supported=self.settings.auth.required_scopes, + ) + ) + + # mount these routes last, so they have the lowest route matching precedence + routes.extend(self._custom_starlette_routes) + + # Create Starlette app with routes and middleware + return Starlette(debug=self.settings.debug, routes=routes, middleware=middleware) + + def streamable_http_app( + self, + *, + streamable_http_path: str = "/mcp", + json_response: bool = False, + stateless_http: bool = False, + event_store: EventStore | None = None, + retry_interval: int | None = None, + max_request_body_size: int = DEFAULT_MAX_REQUEST_BODY_SIZE, + transport_security: TransportSecuritySettings | None = None, + host: str = "127.0.0.1", + ) -> Starlette: + """Return an instance of the StreamableHTTP server app.""" + return self._lowlevel_server.streamable_http_app( + streamable_http_path=streamable_http_path, + json_response=json_response, + stateless_http=stateless_http, + event_store=event_store, + retry_interval=retry_interval, + max_request_body_size=max_request_body_size, + transport_security=transport_security, + host=host, + auth=self.settings.auth, + token_verifier=self._token_verifier, + auth_server_provider=self._auth_server_provider, + custom_starlette_routes=self._custom_starlette_routes, + debug=self.settings.debug, + ) + + async def list_prompts(self) -> list[MCPPrompt]: + """List all available prompts.""" + prompts = self._prompt_manager.list_prompts() + return [ + MCPPrompt( + name=prompt.name, + title=prompt.title, + description=prompt.description, + arguments=[ + MCPPromptArgument( + name=arg.name, + description=arg.description, + required=arg.required, + ) + for arg in (prompt.arguments or []) + ], + icons=prompt.icons, + ) + for prompt in prompts + ] + + async def get_prompt( + self, name: str, arguments: dict[str, Any] | None = None, context: Context[LifespanResultT, Any] | None = None + ) -> GetPromptResult | InputRequiredResult: + """Get a prompt by name with arguments. + + An `InputRequiredResult` returned by the prompt function is passed + through unchanged (the 2026-07-28 multi-round-trip flow); the retry's + answers arrive on `ctx.input_responses`, with `ctx.request_state` + carrying the echoed opaque state. + """ + if context is None: + context = Context(mcp_server=self, subscriptions=self._subscriptions) + try: + prompt = self._prompt_manager.get_prompt(name) + if not prompt: + raise ValueError(f"Unknown prompt: {name}") + + rendered = await prompt.render(arguments, context) + if isinstance(rendered, InputRequiredResult): + return rendered + + return GetPromptResult( + description=prompt.description, + messages=pydantic_core.to_jsonable_python(rendered), + ) + except MCPError: + raise + except Exception as e: + # Not logged here: the dispatcher boundary logs it once. + raise ValueError(str(e)) from e + + +def _version_gated(method: MethodBinding) -> RequestHandler: + """Wrap a method handler so a request at a disallowed protocol version is rejected. + + The low-level `_request_handlers` dict is keyed by method only, so per-version + scoping is enforced here rather than at the runner's boundary table. + """ + versions = method.protocol_versions + assert versions is not None + + async def gated(ctx: ServerRequestContext[Any, Any], params: Any) -> HandlerResult: + if ctx.protocol_version not in versions: + raise MCPError(code=METHOD_NOT_FOUND, message="Method not found", data=method.method) + return await method.handler(ctx, params) + + return gated + + +def require_client_extension(ctx: ServerRequestContext[Any, Any], identifier: str) -> None: + """Assert the connected client declared support for `identifier`. + + Call this from an extension's handler or `intercept_tool_call` before + offering extension-specific behaviour. Raises `MCPError` with the + `-32021` (missing required client capability) code and a + `requiredCapabilities` payload when the client did not declare the + extension, per SEP-2133. + + Args: + ctx: The current request context. + identifier: The extension identifier the client must have declared. + + Raises: + MCPError: With code `MISSING_REQUIRED_CLIENT_CAPABILITY` if the client + did not advertise `identifier`. + """ + capabilities = ctx.session.client_capabilities + declared = capabilities.extensions if capabilities else None + if not declared or identifier not in declared: + data = MissingRequiredClientCapabilityErrorData( + required_capabilities=ClientCapabilities(extensions={identifier: {}}) + ) + raise MCPError( + code=MISSING_REQUIRED_CLIENT_CAPABILITY, + message=f"Client did not declare required extension {identifier!r}", + data=data.model_dump(by_alias=True, mode="json", exclude_none=True), + ) diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/__init__.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/__init__.py new file mode 100644 index 0000000..ae9c656 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/__init__.py @@ -0,0 +1,4 @@ +from .base import Tool +from .tool_manager import ToolManager + +__all__ = ["Tool", "ToolManager"] diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..3041af4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..83309ee Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/__pycache__/base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/__pycache__/tool_manager.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/__pycache__/tool_manager.cpython-312.pyc new file mode 100644 index 0000000..b2da486 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/__pycache__/tool_manager.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/base.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/base.py new file mode 100644 index 0000000..4a8bed7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/base.py @@ -0,0 +1,210 @@ +from __future__ import annotations + +from collections.abc import Callable, Hashable +from functools import cached_property +from typing import TYPE_CHECKING, Any + +from mcp_types import Icon, InputRequiredResult, ToolAnnotations +from pydantic import BaseModel, Field, ValidationError + +from mcp.server.mcpserver.exceptions import ( + InvalidSignature, + ResourceError, + ToolError, + UnexpectedResourceError, + UnexpectedToolError, +) +from mcp.server.mcpserver.resolve import ( + build_resolver_plans, + find_resolved_parameters, + resolve_arguments, + returns_input_required, +) +from mcp.server.mcpserver.utilities.context_injection import find_context_parameter +from mcp.server.mcpserver.utilities.func_metadata import FuncMetadata, func_metadata +from mcp.shared._callable_inspection import is_async_callable +from mcp.shared.exceptions import MCPError +from mcp.shared.tool_name_validation import validate_and_warn_tool_name + +if TYPE_CHECKING: + from mcp.server.context import LifespanContextT, RequestT + from mcp.server.mcpserver.context import Context + + +class Tool(BaseModel): + """Internal tool registration info.""" + + fn: Callable[..., Any] = Field(exclude=True) + name: str = Field(description="Name of the tool") + title: str | None = Field(None, description="Human-readable title of the tool") + description: str = Field(description="Description of what the tool does") + parameters: dict[str, Any] = Field(description="JSON schema for tool parameters") + fn_metadata: FuncMetadata = Field( + description="Metadata about the function including a pydantic model for tool arguments" + ) + is_async: bool = Field(description="Whether the tool is async") + context_kwarg: str | None = Field(None, description="Name of the kwarg that should receive context") + resolved_params: dict[str, Any] = Field( + default_factory=lambda: {}, + exclude=True, + description="Parameters filled by resolvers, mapped to (Resolve, wants_union)", + ) + resolver_plans: dict[Hashable, Any] = Field( + default_factory=lambda: {}, exclude=True, description="Static per-resolver parameter plans" + ) + annotations: ToolAnnotations | None = Field(None, description="Optional annotations for the tool") + icons: list[Icon] | None = Field(default=None, description="Optional list of icons for this tool") + meta: dict[str, Any] | None = Field(default=None, description="Optional metadata for this tool") + + @cached_property + def output_schema(self) -> dict[str, Any] | None: + return self.fn_metadata.output_schema + + @classmethod + def from_function( + cls, + fn: Callable[..., Any], + name: str | None = None, + title: str | None = None, + description: str | None = None, + context_kwarg: str | None = None, + annotations: ToolAnnotations | None = None, + icons: list[Icon] | None = None, + meta: dict[str, Any] | None = None, + structured_output: bool | None = None, + ) -> Tool: + """Create a Tool from a function.""" + func_name = name or fn.__name__ + + validate_and_warn_tool_name(func_name) + + if func_name == "": + raise ValueError("You must provide a name for lambda functions") + + func_doc = description or fn.__doc__ or "" + is_async = is_async_callable(fn) + + if context_kwarg is None: # pragma: no branch + context_kwarg = find_context_parameter(fn) + + resolved_params = find_resolved_parameters(fn) + if resolved_params and returns_input_required(fn): + raise InvalidSignature( + f"Tool {func_name!r} combines Resolve(...) parameters with an InputRequiredResult " + "return; a call has one input_required channel, so the multi-round flow is driven " + "either by resolvers or by the tool body, not both" + ) + + skip_names = [context_kwarg] if context_kwarg is not None else [] + skip_names.extend(resolved_params) + + func_arg_metadata = func_metadata( + fn, + skip_names=skip_names, + structured_output=structured_output, + ) + parameters = func_arg_metadata.arg_model.model_json_schema(by_alias=True) + + # Match `model_dump_one_level`'s kwarg keys (alias when present, else field name) + # so a by-name resolver param resolves to a key that exists at call time. + tool_arg_names = {field.alias or name for name, field in func_arg_metadata.arg_model.model_fields.items()} + resolver_plans = build_resolver_plans(resolved_params, tool_arg_names) + + return cls( + fn=fn, + name=func_name, + title=title, + description=func_doc, + parameters=parameters, + fn_metadata=func_arg_metadata, + is_async=is_async, + context_kwarg=context_kwarg, + resolved_params=dict(resolved_params), + resolver_plans=resolver_plans, + annotations=annotations, + icons=icons, + meta=meta, + ) + + async def run( + self, + arguments: dict[str, Any], + context: Context[LifespanContextT, RequestT], + convert_result: bool = False, + ) -> Any: + """Run the tool with arguments. + + Every failure other than `MCPError` is raised as a `ToolError` whose message + starts `Error executing tool ` and whose `__cause__` is what was raised. + An anticipated failure keeps its own text after the prefix. A crash does not, + so nothing from an unexpected exception reaches the client. + + Raises: + ToolError: If the arguments fail validation against the input schema, or + the tool function (or a resolver) raises `ToolError` or `ResourceError`. + UnexpectedToolError: If argument validation, the tool function, or a + resolver raises anything else, or the return value fails output conversion. + """ + try: + validated = self.fn_metadata.validate_arguments(arguments) + except ValidationError as exc: + # The caller's arguments don't match the input schema: the model's mistake + # to read and correct, so it is reported like a deliberate ToolError. + raise ToolError(f"Error executing tool {self.name}: {exc}") from exc + except MCPError: + raise + except Exception as exc: + # A custom validator or default_factory that raises is a crash. + raise UnexpectedToolError(f"Error executing tool {self.name}") from exc + + try: + pass_directly: dict[str, Any] = {} + if self.context_kwarg is not None: + pass_directly[self.context_kwarg] = context + + # Resolvers see the same validated arguments the tool body receives, so a + # `default_factory`/stateful validator can't hand a by-name resolver a + # different value than the body. + if self.resolved_params: + resolved = await resolve_arguments(self.resolved_params, self.resolver_plans, validated, context) + if isinstance(resolved, InputRequiredResult): + # A resolver still needs client input (>= 2026-07-28): surface the + # batched questions instead of running the tool body this round. + return self.fn_metadata.convert_result(resolved) if convert_result else resolved + pass_directly |= resolved + + result = await self.fn_metadata.call_fn(self.fn, self.is_async, validated, pass_directly) + + # Registration rejects the annotated form of this combination; this covers + # a body that returns an InputRequiredResult without declaring it. It is + # an authoring bug, so it is raised as a crash rather than a ToolError. + if self.resolved_params and isinstance(result, InputRequiredResult): + raise RuntimeError( + "the tool returned an InputRequiredResult but its parameters use Resolve(...); " + "a call has one input_required channel, so the multi-round flow is driven " + "either by resolvers or by the tool body, not both" + ) + + if convert_result: + result = self.fn_metadata.convert_result(result) + + return result + except MCPError: + # `MCPError` (and subclasses such as `UrlElicitationRequiredError`) + # carries a JSON-RPC `ErrorData(code, message, data)` and means + # "respond with a protocol error" - re-raise so the kernel surfaces + # it as a top-level JSON-RPC error rather than wrapping it as a + # `CallToolResult(isError=True)` execution failure. + raise + # Everything else reaches the model as an is_error result under this tool's + # name, and the wrapper's type tells the server whether to log a crash. + except (UnexpectedToolError, UnexpectedResourceError) as exc: + # A nested tool call or resource read crashed: still a crash here. Its + # message is already the generic one, so it is safe to carry along. + raise UnexpectedToolError(f"Error executing tool {self.name}: {exc}") from exc + except (ToolError, ResourceError) as exc: + # Raised deliberately by the tool, a resolver, or a resource it read. + raise ToolError(f"Error executing tool {self.name}: {exc}") from exc + except Exception as exc: + # A crash: the exception's own text stays on the server. + raise UnexpectedToolError(f"Error executing tool {self.name}") from exc diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/tool_manager.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/tool_manager.py new file mode 100644 index 0000000..9e7910e --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/tools/tool_manager.py @@ -0,0 +1,87 @@ +from __future__ import annotations + +from collections.abc import Callable +from typing import TYPE_CHECKING, Any + +from mcp_types import Icon, ToolAnnotations + +from mcp.server.mcpserver.exceptions import ToolError +from mcp.server.mcpserver.tools.base import Tool +from mcp.server.mcpserver.utilities.logging import get_logger + +if TYPE_CHECKING: + from mcp.server.context import LifespanContextT, RequestT + from mcp.server.mcpserver.context import Context + +logger = get_logger(__name__) + + +class ToolManager: + """Manages MCPServer tools.""" + + def __init__(self, warn_on_duplicate_tools: bool = True, *, tools: list[Tool] | None = None): + self._tools: dict[str, Tool] = {} + for tool in tools or (): + if warn_on_duplicate_tools and tool.name in self._tools: + logger.warning(f"Tool already exists: {tool.name}") + self._tools[tool.name] = tool + + self.warn_on_duplicate_tools = warn_on_duplicate_tools + + def get_tool(self, name: str) -> Tool | None: + """Get tool by name.""" + return self._tools.get(name) + + def list_tools(self) -> list[Tool]: + """List all registered tools.""" + return list(self._tools.values()) + + def add_tool( + self, + fn: Callable[..., Any], + name: str | None = None, + title: str | None = None, + description: str | None = None, + annotations: ToolAnnotations | None = None, + icons: list[Icon] | None = None, + meta: dict[str, Any] | None = None, + structured_output: bool | None = None, + ) -> Tool: + """Add a tool to the server.""" + tool = Tool.from_function( + fn, + name=name, + title=title, + description=description, + annotations=annotations, + icons=icons, + meta=meta, + structured_output=structured_output, + ) + existing = self._tools.get(tool.name) + if existing: + if self.warn_on_duplicate_tools: + logger.warning(f"Tool already exists: {tool.name}") + return existing + self._tools[tool.name] = tool + return tool + + def remove_tool(self, name: str) -> None: + """Remove a tool by name.""" + if name not in self._tools: + raise ToolError(f"Unknown tool: {name}") + del self._tools[name] + + async def call_tool( + self, + name: str, + arguments: dict[str, Any], + context: Context[LifespanContextT, RequestT], + convert_result: bool = False, + ) -> Any: + """Call a tool by name with arguments.""" + tool = self.get_tool(name) + if not tool: + raise ToolError(f"Unknown tool: {name}") + + return await tool.run(arguments, context, convert_result=convert_result) diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__init__.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__init__.py new file mode 100644 index 0000000..b9d00d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__init__.py @@ -0,0 +1 @@ +"""MCPServer utility modules.""" diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..bf7a5d6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/context_injection.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/context_injection.cpython-312.pyc new file mode 100644 index 0000000..b7ab327 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/context_injection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/func_metadata.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/func_metadata.cpython-312.pyc new file mode 100644 index 0000000..fc43dc6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/func_metadata.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/logging.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/logging.cpython-312.pyc new file mode 100644 index 0000000..a70b924 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/logging.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/types.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/types.cpython-312.pyc new file mode 100644 index 0000000..a5ee4f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/__pycache__/types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/context_injection.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/context_injection.py new file mode 100644 index 0000000..ac7ab82 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/context_injection.py @@ -0,0 +1,68 @@ +"""Context injection utilities for MCPServer.""" + +from __future__ import annotations + +import inspect +import typing +from collections.abc import Callable +from typing import Any + +from mcp.server.mcpserver.context import Context + + +def find_context_parameter(fn: Callable[..., Any]) -> str | None: + """Find the parameter that should receive the Context object. + + Searches through the function's signature to find a parameter + with a Context type annotation. + + Args: + fn: The function to inspect + + Returns: + The name of the context parameter, or None if not found + """ + # Get type hints to properly resolve string annotations + try: + hints = typing.get_type_hints(fn) + except Exception: # pragma: lax no cover + # If we can't resolve type hints, we can't find the context parameter + return None + + # Check each parameter's type hint + for param_name, annotation in hints.items(): + # Handle direct Context type + if inspect.isclass(annotation) and issubclass(annotation, Context): + return param_name + + # Handle generic types like Optional[Context] + origin = typing.get_origin(annotation) + if origin is not None: + args = typing.get_args(annotation) + for arg in args: + if inspect.isclass(arg) and issubclass(arg, Context): + return param_name + + return None + + +def inject_context( + fn: Callable[..., Any], + kwargs: dict[str, Any], + context: Any | None, + context_kwarg: str | None, +) -> dict[str, Any]: + """Inject context into function kwargs if needed. + + Args: + fn: The function that will be called + kwargs: The current keyword arguments + context: The context object to inject (if any) + context_kwarg: The name of the parameter to inject into + + Returns: + Updated kwargs with context injected if applicable + """ + if context_kwarg is not None and context is not None: + return {**kwargs, context_kwarg: context} + return kwargs diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/func_metadata.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/func_metadata.py new file mode 100644 index 0000000..cc32433 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/func_metadata.py @@ -0,0 +1,671 @@ +import functools +import inspect +import json +import sys +from collections.abc import Awaitable, Callable, Sequence +from itertools import chain +from types import GenericAlias +from typing import Annotated, Any, Union, cast, get_args, get_origin + +import anyio +import anyio.to_thread +import pydantic_core +from mcp_types import CallToolResult, ContentBlock, InputRequiredResult, TextContent +from pydantic import ( + BaseModel, + ConfigDict, + Field, + PrivateAttr, + PydanticUserError, + TypeAdapter, + WithJsonSchema, + create_model, +) +from pydantic.fields import FieldInfo +from pydantic.json_schema import GenerateJsonSchema, JsonSchemaWarningKind +from typing_extensions import NotRequired, ReadOnly, TypedDict, deprecated, get_type_hints, is_typeddict +from typing_inspection.introspection import ( + UNKNOWN, + AnnotationSource, + ForbiddenQualifier, + inspect_annotation, + is_union_origin, +) + +from mcp.server.mcpserver.exceptions import InvalidSignature +from mcp.server.mcpserver.utilities.logging import get_logger +from mcp.server.mcpserver.utilities.types import Audio, Image +from mcp.shared.exceptions import MCPDeprecationWarning + +logger = get_logger(__name__) + + +def _is_input_required_type(obj: Any) -> bool: + return isinstance(obj, type) and issubclass(obj, InputRequiredResult) + + +_CONTENT_TYPES = (*get_args(ContentBlock), Image, Audio) +# `_convert_to_content` unrolls list/tuple values; a `Sequence[...]` annotation is one of those at runtime. +_CONTENT_SEQUENCE_ORIGINS = (list, tuple, Sequence) + + +def _returns_content(annotation: Any) -> bool: + """Whether a return annotation declares content blocks or the `Image`/`Audio` helpers, bare or as + the items of a list/tuple or the arms of a union: the values `_convert_to_content` renders as blocks + rather than dumping as data. Keep the two in sync.""" + origin = get_origin(annotation) + if origin is None: + return isinstance(annotation, type) and issubclass(annotation, _CONTENT_TYPES) + if origin is Annotated: + return _returns_content(get_args(annotation)[0]) + if is_union_origin(origin) or origin in _CONTENT_SEQUENCE_ORIGINS: + return any(_returns_content(arg) for arg in get_args(annotation)) + return False + + +class StrictJsonSchema(GenerateJsonSchema): + """A JSON schema generator that raises exceptions instead of emitting warnings. + + This is used to detect non-serializable types during schema generation. + """ + + def emit_warning(self, kind: JsonSchemaWarningKind, detail: str) -> None: + # Raise an exception instead of emitting a warning + raise ValueError(f"JSON schema warning: {kind} - {detail}") + + +_LOCAL_DEFS_PREFIX = "#/$defs/" + + +def _inline_root_ref(schema: dict[str, Any]) -> dict[str, Any]: + """Give a schema whose root is a bare `$ref` into `$defs` an inline root. + + pydantic emits a self-referential model as `{"$defs": {...}, "$ref": "#/$defs/Model"}`, with no + `type` at the root; `Tool.outputSchema` needs an object root (required on the wire through + 2025-11-25). The referenced definition is copied onto the root and `$defs` is kept, since nested + references still point into it. Root siblings of the `$ref` win over the definition's keys. + """ + ref = schema.get("$ref") + if not isinstance(ref, str) or not ref.startswith(_LOCAL_DEFS_PREFIX): + return schema + definition = cast(dict[str, Any], schema["$defs"][ref.removeprefix(_LOCAL_DEFS_PREFIX)]) + siblings = {key: value for key, value in schema.items() if key != "$ref"} + return {**definition, **siblings} + + +class ArgModelBase(BaseModel): + """A model representing the arguments to a function.""" + + def model_dump_one_level(self) -> dict[str, Any]: + """Return a dict of the model's fields, one level deep. + + That is, sub-models etc are not dumped - they are kept as Pydantic models. + """ + kwargs: dict[str, Any] = {} + for field_name, field_info in self.__class__.model_fields.items(): + value = getattr(self, field_name) + # Use the alias if it exists, otherwise use the field name + output_name = field_info.alias if field_info.alias else field_name + kwargs[output_name] = value + return kwargs + + model_config = ConfigDict(arbitrary_types_allowed=True) + + +class FuncMetadata(BaseModel): + """A tool function's argument model plus, for structured output, the published `output_schema` and the + `output_model` results are validated against. Constructing one with an `output_model` and no schema derives + the schema (and raises if pydantic can't); the fields are read live, so clearing or reassigning them later + takes effect on the next call.""" + + arg_model: Annotated[type[ArgModelBase], WithJsonSchema(None)] + output_schema: dict[str, Any] | None = None + output_model: Annotated[type[Any], WithJsonSchema(None)] | None = None + wrap_output: bool = False + _adapter: tuple[type[Any], TypeAdapter[Any]] | None = PrivateAttr(default=None) + + def model_post_init(self, context: Any, /) -> None: + if self.output_model is not None and self.output_schema is None: + # StrictJsonSchema raises instead of warning, so an unserializable return type fails construction. + schema = self._output_adapter(self.output_model).json_schema(schema_generator=StrictJsonSchema) + self.output_schema = _inline_root_ref(schema) + + def _output_adapter(self, output_model: type[Any]) -> TypeAdapter[Any]: + """The validator/serializer for `output_model`, built once and rebuilt only if the field is reassigned.""" + if self._adapter is None or self._adapter[0] is not output_model: + self._adapter = (output_model, TypeAdapter(_pydantic_readable_typeddict(output_model))) + return self._adapter[1] + + def validate_arguments(self, arguments_to_validate: dict[str, Any]) -> dict[str, Any]: + """Validate raw arguments into a one-level kwargs dict (no function call). + + Used to feed resolver dependency injection the validated tool arguments + before the tool function itself runs. + """ + arguments_pre_parsed = self.pre_parse_json(arguments_to_validate) + arguments_parsed_model = self.arg_model.model_validate(arguments_pre_parsed) + return arguments_parsed_model.model_dump_one_level() + + async def call_fn( + self, + fn: Callable[..., Any | Awaitable[Any]], + fn_is_async: bool, + arguments: dict[str, Any], + arguments_to_pass_directly: dict[str, Any] | None = None, + ) -> Any: + """Call the function with already-validated `arguments` plus `arguments_to_pass_directly`. + + `arguments` is the output of `validate_arguments`. A sync function runs on a + worker thread. + """ + kwargs = arguments | (arguments_to_pass_directly or {}) + if fn_is_async: + return await fn(**kwargs) + return await anyio.to_thread.run_sync(functools.partial(fn, **kwargs)) + + @deprecated( + "FuncMetadata.call_fn_with_arg_validation() is deprecated and will be removed in 3.0; " + "call validate_arguments() and then call_fn() instead.", + category=MCPDeprecationWarning, + ) + async def call_fn_with_arg_validation( + self, + fn: Callable[..., Any | Awaitable[Any]], + fn_is_async: bool, + arguments_to_validate: dict[str, Any], + arguments_to_pass_directly: dict[str, Any] | None, + pre_validated: dict[str, Any] | None = None, + ) -> Any: + """Validate `arguments_to_validate` (unless `pre_validated` is given) and call the function. + + Deprecated: call `validate_arguments` and then `call_fn`. + """ + arguments = pre_validated if pre_validated is not None else self.validate_arguments(arguments_to_validate) + return await self.call_fn(fn, fn_is_async, arguments, arguments_to_pass_directly) + + def convert_result(self, result: Any) -> CallToolResult | InputRequiredResult: + """Convert a function call result into a `CallToolResult`. + + An `InputRequiredResult` is passed through unchanged so the multi-round + flow surfaces on the wire as `resultType: "input_required"` rather than + being JSON-dumped into a text block. + + Note: we build unstructured content here **even though the lowlevel server + tool call handler provides generic backwards compatibility serialization of + structured content**. This is for MCPServer backwards compatibility: we need to + retain MCPServer's ad hoc conversion logic for constructing unstructured output + from function return values, whereas the lowlevel server simply serializes + the structured output. + """ + if isinstance(result, InputRequiredResult): + return result + # A schema published without a model (hand-built metadata) is advertised but not validated here. + output_model = self.output_model if self.output_schema is not None else None + if isinstance(result, CallToolResult): + if output_model is not None and not result.is_error: + self._output_adapter(output_model).validate_python(result.structured_content) + return result + + unstructured_content = _convert_to_content(result) + + if output_model is None: + return CallToolResult(content=unstructured_content) + + if self.wrap_output: + result = {"result": result} + + # The tool hands back Python-side names; the wire (and outputSchema) use aliases. + adapter = self._output_adapter(output_model) + validated = adapter.validate_python(result, by_alias=True, by_name=True) + if isinstance(validated, BaseModel): + # Dump via the instance so a returned subclass keeps its own fields. + structured_content = validated.model_dump(mode="json", by_alias=True) + else: + structured_content = adapter.dump_python(validated, mode="json", by_alias=True) + + return CallToolResult(content=unstructured_content, structured_content=structured_content) + + def pre_parse_json(self, data: dict[str, Any]) -> dict[str, Any]: + """Pre-parse data from JSON. + + Return a dict with the same keys as input but with values parsed from JSON + if appropriate. + + This is to handle cases like `["a", "b", "c"]` being passed in as JSON inside + a string rather than an actual list. Claude Desktop is prone to this - in fact + it seems incapable of NOT doing this. For sub-models, it tends to pass + dicts (JSON objects) as JSON strings, which can be pre-parsed here. + """ + new_data = data.copy() # Shallow copy + + # Build a mapping from input keys (including aliases) to field info + key_to_field_info: dict[str, FieldInfo] = {} + for field_name, field_info in self.arg_model.model_fields.items(): + # Map both the field name and its alias (if any) to the field info + key_to_field_info[field_name] = field_info + if field_info.alias: + key_to_field_info[field_info.alias] = field_info + + for data_key, data_value in data.items(): + if data_key not in key_to_field_info: + continue + + field_info = key_to_field_info[data_key] + if isinstance(data_value, str) and field_info.annotation is not str: + try: + pre_parsed = json.loads(data_value) + except (ValueError, RecursionError): + # Not JSON, or JSON the parser refuses (over-long integers, deep + # nesting): leave the string for validation to accept or reject. + continue + if isinstance(pre_parsed, str | int | float): + # This is likely that the raw value is e.g. `"hello"` which we + # Should really be parsed as '"hello"' in Python - but if we parse + # it as JSON it'll turn into just 'hello'. So we skip it. + continue + new_data[data_key] = pre_parsed + assert new_data.keys() == data.keys() + return new_data + + model_config = ConfigDict( + arbitrary_types_allowed=True, + ) + + +def func_metadata( + func: Callable[..., Any], + skip_names: Sequence[str] = (), + structured_output: bool | None = None, +) -> FuncMetadata: + """Given a function, return metadata including a Pydantic model representing its signature. + + The use case for this is + ``` + meta = func_metadata(func) + validated_args = meta.arg_model.model_validate(some_raw_data_dict) + return func(**validated_args.model_dump_one_level()) + ``` + + **critically** it also provides a pre-parse helper to attempt to parse things from + JSON. + + Args: + func: The function to convert to a Pydantic model + skip_names: A list of parameter names to skip. These will not be included in + the model. + structured_output: Controls whether the tool's output is structured or unstructured + - If None, auto-detects based on the function's return type annotation + - If True, creates a structured tool (return type annotation permitting) + - If False, unconditionally creates an unstructured tool + + If structured, creates a Pydantic model for the function's result based on its annotation. + Supports various return types: + - BaseModel subclasses (used directly) + - Primitive types (str, int, float, bool, bytes, None) - wrapped in a + model with a 'result' field + - TypedDict - used directly + - Dataclasses and other annotated classes - converted to Pydantic models + - Generic types (list, dict, Union, etc.) - wrapped in a model with a 'result' field + - Content blocks (TextContent, EmbeddedResource, ...), Image and Audio, bare or inside a + list, tuple or union - unstructured when auto-detecting; structured_output=True bypasses + this rule (a content block then publishes its own schema; Image/Audio have none and raise) + + Returns: + A FuncMetadata object containing: + - arg_model: A Pydantic model representing the function's arguments + - output_schema: The published JSON schema for structured output, or None if the output is unstructured + - output_model: The type structured output is validated against: the declared BaseModel or TypedDict, + or a synthesized model for wrapped, `dict[str, T]` and annotated-class returns + - wrap_output: Whether the function result needs to be wrapped in `{"result": ...}` for structured output. + """ + try: + sig = inspect.signature(func, eval_str=True) + except NameError as e: # pragma: no cover + # This raise could perhaps be skipped, and we (MCPServer) just call + # model_rebuild right before using it 🤷 + raise InvalidSignature(f"Unable to evaluate type annotations for callable {func.__name__!r}") from e + params = sig.parameters + dynamic_pydantic_model_params: dict[str, Any] = {} + for param in params.values(): + if param.name.startswith("_"): # pragma: no cover + raise InvalidSignature(f"Parameter {param.name} of {func.__name__} cannot start with '_'") + if param.name in skip_names: + continue + + annotation = param.annotation if param.annotation is not inspect.Parameter.empty else Any + field_name = param.name + field_kwargs: dict[str, Any] = {} + field_metadata: list[Any] = [] + + if param.annotation is inspect.Parameter.empty: + field_metadata.append(WithJsonSchema({"title": param.name, "type": "string"})) + # Check if the parameter name conflicts with BaseModel attributes + # This is necessary because Pydantic warns about shadowing parent attributes + if hasattr(BaseModel, field_name) and callable(getattr(BaseModel, field_name)): + # Use an alias to avoid the shadowing warning + field_kwargs["alias"] = field_name + # Use a prefixed field name + field_name = f"field_{field_name}" + + if param.default is not inspect.Parameter.empty: + dynamic_pydantic_model_params[field_name] = ( + Annotated[(annotation, *field_metadata, Field(**field_kwargs))], + param.default, + ) + else: + dynamic_pydantic_model_params[field_name] = Annotated[(annotation, *field_metadata, Field(**field_kwargs))] + + arguments_model = create_model( + f"{func.__name__}Arguments", + __base__=ArgModelBase, + **dynamic_pydantic_model_params, + ) + + if structured_output is False: + return FuncMetadata(arg_model=arguments_model) + + # set up structured output support based on return type annotation + + if sig.return_annotation is inspect.Parameter.empty and structured_output is True: + raise InvalidSignature(f"Function {func.__name__}: return annotation required for structured output") + + try: + inspected_return_ann = inspect_annotation(sig.return_annotation, annotation_source=AnnotationSource.FUNCTION) + except ForbiddenQualifier as e: + raise InvalidSignature(f"Function {func.__name__}: return annotation contains an invalid type qualifier") from e + + return_type_expr = inspected_return_ann.type + + # `AnnotationSource.FUNCTION` allows no type qualifier to be used, so `return_type_expr` is guaranteed to *not* be + # unknown (i.e. a bare `Final`). + assert return_type_expr is not UNKNOWN + + if _is_input_required_type(return_type_expr): + # A tool annotated to return only InputRequiredResult never produces structured content. + return FuncMetadata(arg_model=arguments_model) + + # The annotation fed to schema derivation. Starts as the raw return annotation (preserving any + # Annotated[...] wrapper) and is narrowed below if InputRequiredResult arms are stripped. + effective_annotation: Any = sig.return_annotation + + if is_union_origin(get_origin(return_type_expr)): + args = get_args(return_type_expr) + # InputRequiredResult is a control-flow signal, not data: strip it so the residual arms + # drive schema derivation. convert_result short-circuits on an InputRequiredResult instance + # before output validation, so the schema only ever sees the data arms at runtime. + residual = tuple(a for a in args if not _is_input_required_type(a)) + if not residual: + return FuncMetadata(arg_model=arguments_model) + if len(residual) != len(args): + # PEP 604 has no syntax for "union of a runtime tuple"; Union[...] is the only spelling. + effective_annotation = residual[0] if len(residual) == 1 else Union[residual] # noqa: UP007 + # Re-normalize so the residual is processed exactly as if it had been the declared + # return annotation: unwraps a top-level Annotated[...] arm and re-derives metadata, + # so the CallToolResult/BaseModel/TypedDict dispatch below sees the bare type. + inspected_return_ann = inspect_annotation(effective_annotation, annotation_source=AnnotationSource.FUNCTION) + return_type_expr = inspected_return_ann.type + if len(residual) > 1 and any( + isinstance(a, type) and issubclass(a, CallToolResult) for a in residual if a is not type(None) + ): + raise InvalidSignature( + f"Function {func.__name__}: CallToolResult cannot be used in Union or Optional types. " + "To return empty results, use: CallToolResult(content=[])" + ) + + original_annotation: Any + # if the typehint is CallToolResult, the user either intends to return without validation + # or they provided validation as Annotated metadata + if isinstance(return_type_expr, type) and issubclass(return_type_expr, CallToolResult): + if inspected_return_ann.metadata: + return_type_expr = inspected_return_ann.metadata[0] + if len(inspected_return_ann.metadata) >= 2: + # Reconstruct the original annotation, by preserving the remaining metadata, + # i.e. from `Annotated[CallToolResult, ReturnType, Gt(1)]` to + # `Annotated[ReturnType, Gt(1)]`: + original_annotation = Annotated[ + (return_type_expr, *inspected_return_ann.metadata[1:]) + ] # pragma: no cover + else: + # We only had `Annotated[CallToolResult, ReturnType]`, treat the original annotation + # as being `ReturnType`: + original_annotation = return_type_expr + else: + return FuncMetadata(arg_model=arguments_model) + else: + original_annotation = effective_annotation + + if structured_output is None and _returns_content(return_type_expr): + # Content blocks and the Image/Audio helpers are what the model reads, not data for the + # application: a derived schema would advertise the block's own model as output_schema (and, + # unless the tool builds its own CallToolResult, echo every block into structured_content). + # structured_output=True still forces one. + return FuncMetadata(arg_model=arguments_model) + + output_model, wrap_output = _create_output_model(original_annotation, return_type_expr, func.__name__) + + if output_model is not None: + try: + # FuncMetadata builds the validator and schema on construction, so an unsupported return type + # surfaces here, at registration, rather than on the first call. + return FuncMetadata(arg_model=arguments_model, output_model=output_model, wrap_output=wrap_output) + except ( + PydanticUserError, + ForbiddenQualifier, + NameError, + TypeError, + ValueError, + pydantic_core.SchemaError, + pydantic_core.ValidationError, + ) as e: + # These are expected errors when a type can't be converted to a Pydantic schema + # PydanticUserError: When Pydantic can't handle the type (e.g. PydanticInvalidForJsonSchema); + # subclasses TypeError on pydantic <2.13 and RuntimeError on pydantic >=2.13 + # ForbiddenQualifier, NameError: an invalid qualifier or unresolvable annotation on a TypedDict key, + # met while rebuilding a stdlib TypedDict below 3.12 (pydantic reports both as PydanticUserError) + # ValueError: When there are issues with the type definition (including our custom warnings); + # arrives wrapped in a ValidationError when raised during FuncMetadata construction + # SchemaError: When Pydantic can't build a schema + # ValidationError: When validation fails + logger.info(f"Cannot create schema for type {return_type_expr} in {func.__name__}: {type(e).__name__}: {e}") + + if structured_output is True: + # Model creation failed or produced warnings - no structured output + raise InvalidSignature( + f"Function {func.__name__}: return type {return_type_expr} is not serializable for structured output" + ) + + return FuncMetadata(arg_model=arguments_model) + + +def _create_output_model(original_annotation: Any, type_expr: Any, func_name: str) -> tuple[type[Any] | None, bool]: + """Pick the type structured output is validated against for the given return annotation. + + Args: + original_annotation: The original return annotation (may be wrapped in `Annotated`). + type_expr: The underlying type expression derived from the return annotation + (`Annotated` and type qualifiers were stripped). + func_name: The name of the function. + + Returns: + tuple of (model or None, wrap_output) + Model is None if the type cannot carry structured output. + wrap_output is True if the result needs to be wrapped in {"result": ...} + """ + model: type[Any] | None = None + wrap_output = False + + # First handle special case: None + if type_expr is None: + model = _create_wrapped_model(func_name, original_annotation) + wrap_output = True + + # Handle GenericAlias types (list[str], dict[str, int], Union[str, int], etc.) + elif isinstance(type_expr, GenericAlias): + origin = get_origin(type_expr) + + # Special case: dict with string keys can use RootModel + if origin is dict: + args = get_args(type_expr) + if len(args) == 2 and args[0] is str: + # TODO: should we use the original annotation? We are losing any potential `Annotated` + # metadata for Pydantic here: + model = _create_dict_model(func_name, type_expr) + else: + # dict with non-str keys needs wrapping + model = _create_wrapped_model(func_name, original_annotation) + wrap_output = True + else: + # All other generic types need wrapping (list, tuple, Union, Optional, etc.) + model = _create_wrapped_model(func_name, original_annotation) + wrap_output = True + + # Handle regular type objects + elif isinstance(type_expr, type): + type_annotation = cast(type[Any], type_expr) + + # Case 1: BaseModel subclasses (can be used directly) + if issubclass(type_annotation, BaseModel): + model = type_annotation + + # Case 2: TypedDicts (pydantic reads qualifiers, totality, docstring and `Annotated` metadata natively) + elif is_typeddict(type_annotation): + model = type_annotation + + # Case 3: Primitive types that need wrapping + elif type_annotation in (str, int, float, bool, bytes, type(None)): + model = _create_wrapped_model(func_name, original_annotation) + wrap_output = True + + # Case 4: Other class types (dataclasses, regular classes with annotations) + else: + type_hints = get_type_hints(type_annotation) + if type_hints: + # Classes with type hints can be converted to Pydantic models + model = _create_model_from_class(type_annotation, type_hints) + # Classes without type hints are not serializable - model remains None + + # Handle any other types not covered above + else: + # This includes typing constructs that aren't GenericAlias in Python 3.10 + # (e.g., Union, Optional in some Python versions) + model = _create_wrapped_model(func_name, original_annotation) + wrap_output = True + + return model, wrap_output + + +_no_default = object() + + +def _create_model_from_class(cls: type[Any], type_hints: dict[str, Any]) -> type[BaseModel]: + """Create a Pydantic model from an ordinary class. + + The created model will: + - Have the same name as the class + - Have fields with the same names and types as the class's fields + - Include all fields whose type does not include None in the set of required fields + + Precondition: cls must have type hints (i.e., `type_hints` is non-empty) + """ + model_fields: dict[str, Any] = {} + for field_name, field_type in type_hints.items(): + if field_name.startswith("_"): # pragma: no cover + continue + + default = getattr(cls, field_name, _no_default) + if default is _no_default: + model_fields[field_name] = field_type + else: + model_fields[field_name] = (field_type, default) + + return create_model(cls.__name__, __config__=ConfigDict(from_attributes=True), **model_fields) + + +def _pydantic_readable_typeddict(output_model: type[Any]) -> type[Any]: + """pydantic refuses `typing.TypedDict` below Python 3.12 (it needs `__orig_bases__`); rebuild such a return + type as an equivalent `typing_extensions.TypedDict` so tool authors don't have to know. Only the class itself + (its keys, docstring and own config) is rebuilt: stdlib TypedDicts nested inside it, or config inherited from + one, still need `typing_extensions` there. Delete with 3.11 support.""" + if sys.version_info >= (3, 12) or not is_typeddict(output_model) or type(output_model).__module__ != "typing": + return output_model + return _as_typing_extensions_typeddict(output_model) # pragma: lax no cover + + +def _as_typing_extensions_typeddict(td_type: type[Any]) -> type[Any]: # pragma: lax no cover + items: dict[str, Any] = {} + for name, hint in get_type_hints(td_type, include_extras=True).items(): + key = inspect_annotation(hint, annotation_source=AnnotationSource.TYPED_DICT) + item: Any = Annotated[(key.type, *key.metadata)] if key.metadata else key.type + if "read_only" in key.qualifiers: + item = ReadOnly[item] + # pydantic's rule: an explicit qualifier wins over class totality. Needed because a stdlib TypedDict + # this old computes `__required_keys__` without seeing `typing_extensions` qualifiers. + required = (name in td_type.__required_keys__ or "required" in key.qualifiers) and ( + "not_required" not in key.qualifiers + ) + items[name] = item if required else NotRequired[item] + # The functional form, spelled so type checkers don't try to evaluate it statically. + rebuilt = cast("Callable[[str, dict[str, Any]], type[Any]]", TypedDict)(td_type.__name__, items) + for attr in ("__doc__", "__module__", "__qualname__", "__pydantic_config__"): + if hasattr(td_type, attr): + setattr(rebuilt, attr, getattr(td_type, attr)) + return rebuilt + + +def _create_wrapped_model(func_name: str, annotation: Any) -> type[BaseModel]: + """Create a model that wraps a type in a 'result' field. + + This is used for primitive types, generic types like list/dict, etc. + """ + model_name = f"{func_name}Output" + + return create_model(model_name, result=annotation) + + +def _create_dict_model(func_name: str, dict_annotation: Any) -> type[BaseModel]: + """Create a RootModel for dict[str, T] types.""" + # TODO(Marcelo): We should not rely on RootModel for this. + from pydantic import RootModel # noqa: TID251 + + class DictModel(RootModel[dict_annotation]): + pass + + # Give it a meaningful name + DictModel.__name__ = f"{func_name}DictOutput" + DictModel.__qualname__ = f"{func_name}DictOutput" + + return DictModel + + +def _convert_to_content(result: Any) -> list[ContentBlock]: + """Convert a result to a sequence of content objects. + + Note: This conversion logic comes from previous versions of MCPServer and is being + retained for purposes of backwards compatibility. It produces different unstructured + output than the lowlevel server tool call handler, which just serializes structured + content verbatim. `_returns_content` is the annotation-level mirror of these branches. + """ + if result is None: # pragma: no cover + return [] + + if isinstance(result, ContentBlock): + return [result] + + if isinstance(result, Image): + return [result.to_image_content()] + + if isinstance(result, Audio): + return [result.to_audio_content()] + + if isinstance(result, list | tuple): + return list( + chain.from_iterable( + _convert_to_content(item) + for item in result # type: ignore + ) + ) + + if not isinstance(result, str): + result = pydantic_core.to_json(result, fallback=str, indent=2).decode() + + return [TextContent(type="text", text=result)] diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/logging.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/logging.py new file mode 100644 index 0000000..04ca388 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/logging.py @@ -0,0 +1,39 @@ +"""Logging utilities for MCPServer.""" + +import logging +from typing import Literal + + +def get_logger(name: str) -> logging.Logger: + """Get a logger nested under MCP namespace. + + Args: + name: The name of the logger. + + Returns: + A configured logger instance. + """ + return logging.getLogger(name) + + +def configure_logging( + level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO", +) -> None: + """Configure logging for MCP. + + Args: + level: The log level to use. + """ + handlers: list[logging.Handler] = [] + try: + from rich.console import Console + from rich.logging import RichHandler + + handlers.append(RichHandler(console=Console(stderr=True), rich_tracebacks=True)) + except ImportError: # pragma: no cover + pass + + if not handlers: # pragma: no cover + handlers.append(logging.StreamHandler()) + + logging.basicConfig(level=level, format="%(message)s", handlers=handlers) diff --git a/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/types.py b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/types.py new file mode 100644 index 0000000..937a7fa --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/mcpserver/utilities/types.py @@ -0,0 +1,101 @@ +"""Common types used across MCPServer.""" + +import base64 +from pathlib import Path + +from mcp_types import AudioContent, ImageContent + + +class Image: + """Helper class for returning images from tools.""" + + def __init__( + self, + path: str | Path | None = None, + data: bytes | None = None, + format: str | None = None, + ): + if path is None and data is None: # pragma: no cover + raise ValueError("Either path or data must be provided") + if path is not None and data is not None: # pragma: no cover + raise ValueError("Only one of path or data can be provided") + + self.path = Path(path) if path else None + self.data = data + self._format = format + self._mime_type = self._get_mime_type() + + def _get_mime_type(self) -> str: + """Get MIME type from format or guess from file extension.""" + if self._format: + return f"image/{self._format.lower()}" + + if self.path: + suffix = self.path.suffix.lower() + return { + ".png": "image/png", + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".gif": "image/gif", + ".webp": "image/webp", + }.get(suffix, "application/octet-stream") + return "image/png" # default for raw binary data + + def to_image_content(self) -> ImageContent: + """Convert to MCP ImageContent.""" + if self.path: + with open(self.path, "rb") as f: + data = base64.b64encode(f.read()).decode() + elif self.data is not None: + data = base64.b64encode(self.data).decode() + else: # pragma: no cover + raise ValueError("No image data available") + + return ImageContent(type="image", data=data, mime_type=self._mime_type) + + +class Audio: + """Helper class for returning audio from tools.""" + + def __init__( + self, + path: str | Path | None = None, + data: bytes | None = None, + format: str | None = None, + ): + if not bool(path) ^ bool(data): # pragma: no cover + raise ValueError("Either path or data can be provided") + + self.path = Path(path) if path else None + self.data = data + self._format = format + self._mime_type = self._get_mime_type() + + def _get_mime_type(self) -> str: + """Get MIME type from format or guess from file extension.""" + if self._format: + return f"audio/{self._format.lower()}" + + if self.path: + suffix = self.path.suffix.lower() + return { + ".wav": "audio/wav", + ".mp3": "audio/mpeg", + ".ogg": "audio/ogg", + ".flac": "audio/flac", + ".aac": "audio/aac", + ".m4a": "audio/mp4", + }.get(suffix, "application/octet-stream") + return "audio/wav" # default for raw binary data + + def to_audio_content(self) -> AudioContent: + """Convert to MCP AudioContent.""" + if self.path: + with open(self.path, "rb") as f: + data = base64.b64encode(f.read()).decode() + elif self.data is not None: + data = base64.b64encode(self.data).decode() + else: # pragma: no cover + raise ValueError("No audio data available") + + return AudioContent(type="audio", data=data, mime_type=self._mime_type) diff --git a/venv/lib/python3.12/site-packages/mcp/server/models.py b/venv/lib/python3.12/site-packages/mcp/server/models.py new file mode 100644 index 0000000..6b12916 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/models.py @@ -0,0 +1,17 @@ +"""This module provides simplified types to use with the server for managing prompts +and tools. +""" + +from mcp_types import Icon, ServerCapabilities +from pydantic import BaseModel + + +class InitializationOptions(BaseModel): + server_name: str + server_version: str + title: str | None = None + description: str | None = None + capabilities: ServerCapabilities + instructions: str | None = None + website_url: str | None = None + icons: list[Icon] | None = None diff --git a/venv/lib/python3.12/site-packages/mcp/server/request_state.py b/venv/lib/python3.12/site-packages/mcp/server/request_state.py new file mode 100644 index 0000000..ad1abe8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/request_state.py @@ -0,0 +1,454 @@ +"""Integrity protection for the multi-round-trip `requestState` (MCP 2026-07-28). + +The spec requires servers to treat the client-echoed `requestState` as +attacker-controlled: `RequestStateBoundary` seals every outgoing value and +verifies every inbound echo, so handlers only ever see plaintext they minted. +""" + +from __future__ import annotations + +import base64 +import hashlib +import hmac +import json +import logging +import math +import os +import time +from collections.abc import Callable, Mapping, Sequence +from dataclasses import replace +from typing import Any, NoReturn, Protocol, cast + +from cryptography.exceptions import InvalidTag +from cryptography.hazmat.primitives.ciphers.aead import AESGCM +from cryptography.hazmat.primitives.hashes import SHA256 +from cryptography.hazmat.primitives.kdf.hkdf import HKDF +from mcp_types import INTERNAL_ERROR, INVALID_PARAMS +from mcp_types.methods import INPUT_REQUIRED_METHODS, is_input_required + +from mcp.server.auth.middleware.auth_context import get_access_token +from mcp.server.auth.provider import principal_components +from mcp.server.context import CallNext, HandlerResult, ServerRequestContext +from mcp.shared.exceptions import MCPError + +__all__ = [ + "AESGCMRequestStateCodec", + "InvalidRequestState", + "RequestStateBoundary", + "RequestStateCodec", + "RequestStateSecurity", + "authenticated_principal", +] + +logger = logging.getLogger(__name__) + + +class InvalidRequestState(Exception): + """A sealed `requestState` token failed verification. + + The message is a log-only reason code; the boundary never puts it on the wire. + """ + + +class RequestStateCodec(Protocol): + """Authenticated crypto over the framework's request-state envelope. + + The framework stamps and re-verifies every envelope claim (expiry, request + binding, principal); a codec only provides integrity and, ideally, + confidentiality (a sign-only codec leaves the payload client-readable). + + Requirements: `unseal(seal(payload))` round-trips, and `unseal` raises + `InvalidRequestState` for any token it did not mint unmodified; tokens + never name their algorithm (version with a format prefix bound under the + authentication tag, RFC 8725); comparisons are constant-time. Both methods + are synchronous, so cache key material rather than calling a KMS per token. + """ + + def seal(self, payload: bytes) -> str: + """Return an opaque URL-safe token protecting `payload`.""" + ... + + def unseal(self, token: str) -> bytes: + """Reverse `seal`. + + Raises: + InvalidRequestState: Malformed, unauthentic, or unknown-key token. + """ + ... + + +def authenticated_principal(ctx: ServerRequestContext[Any, Any]) -> str | None: + """Default principal binding: the authenticated (client, issuer, subject) identity. + + Uses the same components session ownership uses, so two users of one OAuth + client are distinct principals whenever the token verifier supplies a + subject, and the binding degrades to the client identity when it does not. + Returns `None` (state not principal-bound) on unauthenticated transports. + """ + token = get_access_token() + if token is None: + return None + return compact_json(principal_components(token)) + + +class RequestStateSecurity: + """Policy for protecting `requestState`: codec, TTL, principal, audience. + + Exactly one of `keys` or `codec`: + + RequestStateSecurity(keys=[secret]) # built-in AES-256-GCM + RequestStateSecurity(codec=MyKmsCodec()) # bring your own crypto + RequestStateSecurity.ephemeral() # process-local key + + `keys` is the rotation ring: `keys[0]` seals, every key unseals. + Zero-downtime rotation, each phase fully rolled out before the next: + `keys=[old, new]`, then `keys=[new, old]`, then `keys=[new]` after one TTL. + + The boundary enforces expiry, request binding, audience, and principal for + every codec, fail-closed in both directions. `audience=None` defers to the + boundary's `default_audience` (`MCPServer` passes its server name). + """ + + codec: RequestStateCodec + ttl: float + bind_principal: Callable[[ServerRequestContext[Any, Any]], str | None] | None + audience: str | None + + def __init__( + self, + *, + keys: Sequence[bytes | bytearray | str] | None = None, + codec: RequestStateCodec | None = None, + ttl: float = 600.0, + bind_principal: Callable[[ServerRequestContext[Any, Any]], str | None] | None = authenticated_principal, + audience: str | None = None, + ) -> None: + if (keys is None) == (codec is None): + raise ValueError("RequestStateSecurity takes exactly one of keys= or codec=") + if not (math.isfinite(ttl) and ttl > 0): + raise ValueError(f"request-state ttl must be a positive finite number, got {ttl!r}") + if keys is not None: + self.codec = AESGCMRequestStateCodec(keys) + else: + assert codec is not None + self.codec = codec + self.ttl = ttl + self.bind_principal = bind_principal + self.audience = audience + + @classmethod + def ephemeral(cls, *, ttl: float = 600.0, audience: str | None = None) -> RequestStateSecurity: + """Protection under a key generated now and held only by this process. + + This is the policy `MCPServer` installs when `request_state_security=` + is omitted; call it yourself on the lowlevel tier or to set `ttl`/ + `audience`. Suits single-process deployments (stdio, one HTTP worker): + state minted before a restart or by another worker is rejected. + Multi-instance deployments must share a key via `keys=[...]`. + """ + return cls(keys=[os.urandom(32)], ttl=ttl, audience=audience) + + +_KDF_INFO = b"mcp/request-state/v1/aes-256-gcm" +_KID_INFO = b"mcp/request-state/v1/kid:" +_TOKEN_PREFIX = "v1." +_KID_LEN = 4 +_NONCE_LEN = 12 + + +def compact_json(value: Any, *, sort_keys: bool = False) -> str: + """Canonical JSON for everything the state path digests or seals. + + ASCII output keeps the encode total: a lone surrogate in client-supplied + text escapes instead of raising. Anything consuming this must parse with + stdlib `json.loads`, which accepts those escapes (pydantic's JSON parser + does not). + """ + return json.dumps(value, sort_keys=sort_keys, separators=(",", ":")) + + +def _b64u(data: bytes) -> str: + return base64.urlsafe_b64encode(data).decode().rstrip("=") + + +def _b64u_decode(text: str) -> bytes: + """Strict inverse of `_b64u`: only the canonical unpadded encoding decodes.""" + raw = base64.urlsafe_b64decode(text + "=" * (-len(text) % 4)) + if _b64u(raw) != text: + raise ValueError("non-canonical base64url") + return raw + + +def _derive_key(secret: bytes) -> bytes: + """Stretch an operator secret (>= 32 bytes, any format) into the AES-256 key.""" + return HKDF(algorithm=SHA256(), length=32, salt=None, info=_KDF_INFO).derive(secret) + + +class AESGCMRequestStateCodec: + """Built-in codec: AES-256-GCM under key(s) derived with HKDF-SHA256. + + Tokens are encrypted, not merely signed, so clients cannot read the state. + `keys[0]` seals; all keys unseal (rotation, see `RequestStateSecurity`). + Each token carries a 4-byte non-secret key fingerprint for an O(1) ring + lookup, and the "v1." prefix and fingerprint are bound into the GCM + associated data, so a token cannot be replayed into another format version + or ring slot. Key bytes are copied at construction. + """ + + def __init__(self, keys: Sequence[bytes | bytearray | str]) -> None: + for i, key in enumerate(cast("Sequence[object]", keys)): + if not isinstance(key, bytes | bytearray | str): + # Never coerce: bytes(32) would silently build an all-zero key. + raise TypeError( + f"request-state keys must be bytes, bytearray, or str; keys[{i}] is {type(key).__name__}" + ) + material = [k.encode() if isinstance(k, str) else bytes(k) for k in keys] + if not material: + raise ValueError("AESGCMRequestStateCodec requires at least one key") + for i, k in enumerate(material): + if len(k) < 32: + raise ValueError( + f"request-state keys must be at least 32 bytes of secret randomness; " + f"keys[{i}] is {len(k)} bytes. " + 'Generate one with: python -c "import secrets; print(secrets.token_hex(32))"' + ) + self._ring: dict[bytes, AESGCM] = {} + self._mint_kid = b"" + for i, secret in enumerate(material): + key = _derive_key(secret) + kid = hashlib.sha256(_KID_INFO + key).digest()[:_KID_LEN] + if kid in self._ring: + raise ValueError(f"keys[{i}] duplicates an earlier ring key") + self._ring[kid] = AESGCM(key) + if i == 0: + self._mint_kid = kid + + def seal(self, payload: bytes) -> str: + kid = self._mint_kid + nonce = os.urandom(_NONCE_LEN) + sealed = self._ring[kid].encrypt(nonce, payload, _TOKEN_PREFIX.encode() + kid) + return _TOKEN_PREFIX + _b64u(kid + nonce + sealed) + + def unseal(self, token: str) -> bytes: + if not token.startswith(_TOKEN_PREFIX): + raise InvalidRequestState("malformed") + try: + raw = _b64u_decode(token[len(_TOKEN_PREFIX) :]) + except ValueError as exc: + raise InvalidRequestState("malformed") from exc + if len(raw) < _KID_LEN + _NONCE_LEN + 16: + raise InvalidRequestState("malformed") + kid, nonce, sealed = raw[:_KID_LEN], raw[_KID_LEN : _KID_LEN + _NONCE_LEN], raw[_KID_LEN + _NONCE_LEN :] + aead = self._ring.get(kid) + if aead is None: + raise InvalidRequestState("unknown key") + try: + return aead.decrypt(nonce, sealed, _TOKEN_PREFIX.encode() + kid) + except InvalidTag: + raise InvalidRequestState("seal") from None + + +# The multi-round-trip carriers: the only methods whose results may carry `requestState`. +_MRTR_METHODS = INPUT_REQUIRED_METHODS +_ENVELOPE_VERSION = 1 +_FUTURE_SKEW = 60.0 +_PRINCIPAL_LABEL = b"mcp/request-state/principal:" + +_RoundBinding = tuple[str, str, str | None] +"""The (target, args-digest, principal) one round's envelope binds, computed once per round.""" + + +def _reject(method: str, reason: str) -> NoReturn: + """Refuse a round: frozen wire error, real reason to the server log only.""" + logger.warning("requestState rejected on %s: %s", method, reason) + raise MCPError( + code=INVALID_PARAMS, + message="Invalid or expired requestState", + data={"reason": "invalid_request_state"}, + ) + + +def _request_identity(method: str, params: Mapping[str, Any] | None) -> tuple[str, str]: + """Salient (target, args-digest) for the request a token binds to. + + Per-method allowlist, never a denylist: a future wire field cannot silently join the digest. + """ + p: Mapping[str, Any] = params or {} + args: dict[str, Any] = {} + if method == "resources/read": + target = str(p.get("uri", "")) + else: + target, args = str(p.get("name", "")), p.get("arguments") or args + return target, _b64u(hashlib.sha256(compact_json(args, sort_keys=True).encode()).digest()[:16]) + + +def _principal_claim(principal: str) -> str: + salt = os.urandom(8) + tag = hashlib.sha256(_PRINCIPAL_LABEL + salt + _principal_bytes(principal)).digest()[:16] + return _b64u(salt + tag) + + +def _principal_matches(claim: str, principal: str) -> bool: + try: + raw = _b64u_decode(claim) + except ValueError: + return False + # A wrong-length claim never matches: compare_digest handles mismatched sizes. + expected = hashlib.sha256(_PRINCIPAL_LABEL + raw[:8] + _principal_bytes(principal)).digest()[:16] + return hmac.compare_digest(raw[8:], expected) + + +def _principal_bytes(principal: str) -> bytes: + # The digest input is one-way and never decoded, so surrogatepass keeps it total. + return principal.encode("utf-8", "surrogatepass") + + +def _bound_principal( + security: RequestStateSecurity, + ctx: ServerRequestContext[Any, Any], + fail: Callable[[str], NoReturn], +) -> str | None: + """Run `bind_principal` under the deny-on-error discipline, in one place for both directions. + + `fail` converts a failure into the calling direction's wire shape: the + frozen rejection when verifying, the sanitized internal error when sealing. + """ + try: + principal = security.bind_principal(ctx) if security.bind_principal is not None else None + except Exception: # deny-on-error: a raising principal binding must fail closed + logger.exception("bind_principal raised while processing requestState on %s", ctx.method) + fail("principal binding error") + # The declared return type is str | None, but a user callback can ignore it. + if principal is not None and not isinstance(cast("object", principal), str): + fail(f"bind_principal returned {type(principal).__name__}, expected str or None") + return principal + + +class RequestStateBoundary: + """Server middleware sealing/unsealing `requestState` at the wire boundary. + + Acts only on the multi-round-trip carriers (tools/call, prompts/get, + resources/read); every other method passes through untouched. + + Inbound state is verified (codec unseal plus claims check) and replaced + with the plaintext the server minted before any interceptor or handler + runs; failure answers -32602 with the frozen message "Invalid or expired + requestState", the real reason going to the server log only. Outbound, an + `input_required` result carrying `requestState` is sealed in a fresh + claims envelope; handlers and resolvers never call the codec. + + `default_audience` seeds the audience claim when the policy sets none, and + must be stated explicitly: it is the service identity that stops state + minted by another service sharing the same keys. `MCPServer` installs this + middleware with its server name by default (under an ephemeral policy + unless `request_state_security=` supplies one); lowlevel `Server` users + append one to `server.middleware`, passing their server's name (or `None` + to deliberately leave tokens audience-free). + """ + + def __init__(self, security: RequestStateSecurity, *, default_audience: str | None) -> None: + self._security = security + self._audience = security.audience if security.audience is not None else default_audience + + async def __call__(self, ctx: ServerRequestContext[Any, Any], call_next: CallNext) -> HandlerResult: + if ctx.method not in _MRTR_METHODS: + return await call_next(ctx) + binding: _RoundBinding | None = None + if ctx.params is not None and ctx.params.get("requestState") is not None: + # An explicit JSON null counts as absent: stripping the field is already in any client's power. + plaintext, binding = self._unseal(ctx) + ctx = replace(ctx, params={**ctx.params, "requestState": plaintext}) + result = await call_next(ctx) + return self._seal_result(ctx, result, binding) + + def _unseal(self, ctx: ServerRequestContext[Any, Any]) -> tuple[str, _RoundBinding]: + assert ctx.params is not None + wire = ctx.params["requestState"] + if not isinstance(wire, str): + _reject(ctx.method, "non-string requestState") + security = self._security + try: + payload = security.codec.unseal(wire) + except InvalidRequestState as exc: + _reject(ctx.method, str(exc)) + except Exception: # deny-on-error: a buggy custom codec must fail closed + logger.exception("requestState codec raised during unseal on %s", ctx.method) + _reject(ctx.method, "codec error") + try: + claims = json.loads(payload) + version, iat, exp, inner = claims["v"], claims["iat"], claims["exp"], claims["s"] + except (ValueError, KeyError, TypeError): + _reject(ctx.method, "malformed") + if version != _ENVELOPE_VERSION or not isinstance(inner, str): + _reject(ctx.method, "malformed") + now = time.time() + # Accept-conditions are stated positively so a NaN claim fails the comparison and rejects. + if not isinstance(iat, int | float) or not (iat <= now + _FUTURE_SKEW): + _reject(ctx.method, "minted in the future") + if not isinstance(exp, int | float) or not (now < exp): + _reject(ctx.method, "expired") + target, args_digest = _request_identity(ctx.method, ctx.params) + if claims.get("m") != ctx.method or claims.get("t") != target or claims.get("a") != args_digest: + _reject(ctx.method, "request binding") + if claims.get("aud") != self._audience: + _reject(ctx.method, "audience") + + def fail_verify(reason: str) -> NoReturn: + _reject(ctx.method, reason) + + principal = _bound_principal(security, ctx, fail_verify) + claim = claims.get("p") + if (claim is None) != (principal is None): + _reject(ctx.method, "principal drift") + if claim is not None and principal is not None: + if not isinstance(claim, str) or not _principal_matches(claim, principal): + _reject(ctx.method, "principal") + return inner, (target, args_digest, principal) + + def _seal_result( + self, ctx: ServerRequestContext[Any, Any], result: HandlerResult, binding: _RoundBinding | None + ) -> HandlerResult: + # Spec-path results arrive as wire mappings; a short-circuiting middleware may return a model. + if not is_input_required(result): + return result + state = result.get("requestState") if isinstance(result, Mapping) else result.request_state + if state is None: + return result + if isinstance(result, Mapping): + if not isinstance(state, str): + # Only a short-circuiting middleware can put a non-string here; nothing to seal. + return result + return {**result, "requestState": self._seal(ctx, state, binding)} + return result.model_copy(update={"request_state": self._seal(ctx, state, binding)}) + + def _seal(self, ctx: ServerRequestContext[Any, Any], state: str, binding: _RoundBinding | None = None) -> str: + security = self._security + if binding is None: + + def fail_seal(reason: str) -> NoReturn: + logger.error("refusing to seal requestState on %s: %s", ctx.method, reason) + raise MCPError(code=INTERNAL_ERROR, message="Internal error") + + target, args_digest = _request_identity(ctx.method, ctx.params) + binding = (target, args_digest, _bound_principal(security, ctx, fail_seal)) + target, args_digest, principal = binding + now = time.time() + claims: dict[str, Any] = { + "v": _ENVELOPE_VERSION, + "iat": now, + "exp": now + security.ttl, + "m": ctx.method, + "t": target, + "a": args_digest, + "s": state, + } + if self._audience is not None: + claims["aud"] = self._audience + if principal is not None: + claims["p"] = _principal_claim(principal) + payload = compact_json(claims).encode() + try: + return security.codec.seal(payload) + except Exception: # deny-on-error: a raising custom codec must not leak its failure + logger.exception("requestState codec raised during seal on %s", ctx.method) + raise MCPError(code=INTERNAL_ERROR, message="Internal error") from None diff --git a/venv/lib/python3.12/site-packages/mcp/server/runner.py b/venv/lib/python3.12/site-packages/mcp/server/runner.py new file mode 100644 index 0000000..26e8efb --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/runner.py @@ -0,0 +1,872 @@ +"""`ServerRunner` - the per-connection handler kernel. + +`ServerRunner` bridges the dispatch layer (`on_request` / `on_notify`, untyped +dicts) and the user's handler layer (typed `Context`, typed params). It is a +pure kernel: it holds a pre-populated `Connection` and reads +`connection.protocol_version` / `connection.outbound` as facts. Driving a +dispatcher loop and tearing down the connection live in the free-function +drivers (`serve_connection`, `serve_loop`, `serve_dual_era_loop`, `serve_one`); +the entry constructs the `Connection`, the driver tears it down. + +`ServerRunner` holds a `Server` directly - `Server` is the registry. +""" + +from __future__ import annotations + +import contextvars +import logging +from collections.abc import AsyncIterator, Awaitable, Mapping +from contextlib import asynccontextmanager +from dataclasses import KW_ONLY, dataclass, replace +from functools import cached_property, partial +from typing import TYPE_CHECKING, Any, Generic, cast + +import anyio +import anyio.abc +from mcp_types import ( + CLIENT_CAPABILITIES_META_KEY, + CLIENT_INFO_META_KEY, + CORE_RESULT_TYPES, + INTERNAL_ERROR, + INVALID_PARAMS, + INVALID_REQUEST, + METHOD_NOT_FOUND, + PROTOCOL_VERSION_META_KEY, + SERVER_INFO_META_KEY, + UNSUPPORTED_PROTOCOL_VERSION, + CacheableResult, + ErrorData, + Implementation, + InitializeRequestParams, + InitializeResult, + JSONRPCRequest, + RequestId, + RequestParams, + RequestParamsMeta, + UnsupportedProtocolVersionErrorData, +) +from mcp_types import methods as _methods +from mcp_types.version import ( + HANDSHAKE_PROTOCOL_VERSIONS, + LATEST_HANDSHAKE_VERSION, + LATEST_MODERN_VERSION, + MODERN_PROTOCOL_VERSIONS, +) +from pydantic import BaseModel, ValidationError +from typing_extensions import TypeVar + +from mcp.server.caching import apply_cache_hint +from mcp.server.connection import Connection, NotifyOnlyOutbound +from mcp.server.context import CallNext, HandlerResult, ServerMiddleware, ServerRequestContext +from mcp.server.models import InitializationOptions +from mcp.server.session import ServerSession +from mcp.shared._context_streams import ContextReceiveStream +from mcp.shared._stream_protocols import ReadStream, WriteStream +from mcp.shared.dispatcher import CallOptions, DispatchContext, Dispatcher, OnNotify, OnRequest +from mcp.shared.exceptions import MCPError, NoBackChannelError +from mcp.shared.inbound import InboundLadderRejection, classify_inbound_request +from mcp.shared.jsonrpc_dispatcher import JSONRPCDispatcher, handler_exception_to_error_data +from mcp.shared.message import MessageMetadata, ServerMessageMetadata, SessionMessage +from mcp.shared.transport_context import TransportContext + +if TYPE_CHECKING: + from mcp.server.lowlevel.server import Server + +__all__ = [ + "CallNext", + "ServerMiddleware", + "ServerRunner", + "aclose_shielded", + "modern_on_request", + "serve_connection", + "serve_dual_era_loop", + "serve_loop", + "serve_one", +] + +logger = logging.getLogger(__name__) + +LifespanT = TypeVar("LifespanT", default=Any) + + +_INIT_EXEMPT: frozenset[str] = frozenset({"ping"}) + +_EXIT_STACK_CLOSE_TIMEOUT: float = 5 +"""Bound for `aclose_shielded`'s exit-stack unwind; a hung cleanup callback +must not wedge shutdown.""" + + +def _extract_meta(params: Mapping[str, Any] | None) -> RequestParamsMeta | None: + """Lift `_meta` from raw params; `None` when absent or malformed, so + context construction is independent of params validity.""" + if not params or "_meta" not in params: + return None + try: + return RequestParams.model_validate(params, by_name=False).meta + except ValidationError: + return None + + +def _dump_result(result: Any) -> dict[str, Any]: + if result is None: + return {} + if isinstance(result, ErrorData): + # ErrorData is a JSON-RPC error, not a success result. Handler returns + # already raise in `_inner`; this catches middleware returning one. + raise MCPError.from_error_data(result) + if isinstance(result, BaseModel): + return result.model_dump(by_alias=True, mode="json", exclude_none=True) + if isinstance(result, dict): + # Copied so callers own the returned dict: handlers and middleware may + # retain the object they returned, and the outbound pipeline shapes the + # wire form without reaching into anything the handler still holds. + return dict(cast(dict[str, Any], result)) + raise TypeError(f"handler returned {type(result).__name__}; expected BaseModel, dict, or None") + + +async def aclose_shielded(connection: Connection) -> None: + """Unwind ``connection.exit_stack`` under a shielded, bounded scope. + + Called from a driver's ``finally``: the shield lets per-connection cleanup + callbacks run even when the driver itself is being cancelled, the + `_EXIT_STACK_CLOSE_TIMEOUT` bound stops a hung callback wedging shutdown, + and a raising callback is logged-and-swallowed so it never masks the + driver's own exception. + """ + with anyio.move_on_after(_EXIT_STACK_CLOSE_TIMEOUT, shield=True) as scope: + try: + await connection.exit_stack.aclose() + except Exception: + logger.exception("connection exit_stack cleanup raised") + if scope.cancelled_caught: + logger.warning( + "connection exit_stack cleanup exceeded %s seconds; abandoning remaining callbacks", + _EXIT_STACK_CLOSE_TIMEOUT, + ) + + +def _apply_middleware( + middleware: ServerMiddleware[Any], call_next: CallNext, ctx: ServerRequestContext[Any, Any] +) -> Awaitable[HandlerResult]: + """Adapt one middleware to the `CallNext` shape: bind `call_next`, take + `ctx` at call time so a rewritten context flows down the chain.""" + return middleware(ctx, call_next) + + +@dataclass +class ServerRunner(Generic[LifespanT]): + """Per-connection handler kernel. One instance per client connection.""" + + server: Server[LifespanT] + connection: Connection + lifespan_state: LifespanT + _: KW_ONLY + init_options: InitializationOptions | None = None + """`InitializeResult` payload. Defaults to `server.create_initialization_options()`.""" + + @cached_property + def on_request(self) -> OnRequest: + return self._on_request + + @cached_property + def on_notify(self) -> OnNotify: + return self._on_notify + + async def _on_request( + self, + dctx: DispatchContext[TransportContext], + method: str, + params: Mapping[str, Any] | None, + ) -> dict[str, Any]: + meta = _extract_meta(params) + version = self.connection.protocol_version + ctx = self._make_context(dctx, method, params, meta, version) + + async def _inner(ctx: ServerRequestContext[LifespanT, Any]) -> HandlerResult: + # Read method/params off `ctx` so a middleware that rewrote them via + # `call_next(replace(ctx, ...))` reaches lookup and the handler. + method, params = ctx.method, ctx.params + # Pinned compat: spec methods are surface-validated before lookup, + # so malformed params are INVALID_PARAMS even with no handler + # registered. Custom methods miss the monolith map and fall through + # to `entry.params_type` exactly as before. + if method in _methods.SPEC_CLIENT_METHODS: + try: + _methods.validate_client_request(method, version, params) + except KeyError: + raise MCPError(code=METHOD_NOT_FOUND, message="Method not found", data=method) from None + # TODO(L29): the 2026-07-28 spec drops the handshake; this branch and + # the gate become a per-version legacy path then. Initialize runs inline + # (read loop parked), so awaiting the peer anywhere on this path deadlocks. + if method == "initialize": + return self._serialize(method, version, self._handle_initialize(params)) + # Methods without a handler are METHOD_NOT_FOUND regardless of + # initialization state: JSON-RPC 2.0 reserves -32601 for "not + # available on this server", and clients probing a server before + # the handshake key off that code. The init gate below therefore + # only ever applies to methods the server actually serves. + entry = self.server.get_request_handler(method) + if entry is None: + raise MCPError(code=METHOD_NOT_FOUND, message="Method not found", data=method) + if not self.connection.initialize_accepted and method not in _INIT_EXEMPT: + # Pinned compat: the same error shape the union validation produced. + raise MCPError(code=INVALID_PARAMS, message="Invalid request parameters", data="") + # Absent params validate as {} (required fields still reject), so + # the handler receives the model with its defaults, never None. + typed_params = entry.params_type.model_validate({} if params is None else params, by_name=False) + result = await entry.handler(ctx, typed_params) + if isinstance(result, ErrorData): + # Raise inside the chain so middleware observes the failure. + raise MCPError.from_error_data(result) + # Shape for the wire inside the chain so the OpenTelemetry span (the + # outermost middleware) records a failing handler return shape too. + return self._serialize(method, version, result) + + call = self._compose_server_middleware(_inner) + # `_inner` already produced the wire dict; a middleware that short-circuited + # without `call_next` is trusted to return its own well-formed result - + # including its response envelope. The pipeline never patches it up after + # the fact. + result = _dump_result(await call(ctx)) + if method == "initialize": + # Commit only on chain success, so a middleware veto leaves no state. + # Race-free: the read loop is parked until this call returns. + # TODO: this re-reads the wire `params`, so a middleware that rewrote + # `ctx.params` (or `ctx.method`, or short-circuited without `call_next`) + # can leave `connection.protocol_version` out of step with the + # `InitializeResult` `_inner` produced. Resolve when `initialize` becomes + # a built-in handler so commit and result derive from one negotiation. + self.connection.client_params, self.connection.protocol_version = self._negotiate_initialize(params) + return result + + async def _on_notify( + self, + dctx: DispatchContext[TransportContext], + method: str, + params: Mapping[str, Any] | None, + ) -> None: + meta = _extract_meta(params) + version = self.connection.protocol_version + ctx = self._make_context(dctx, method, params, meta, version) + + async def _inner(ctx: ServerRequestContext[LifespanT, Any]) -> None: + method, params = ctx.method, ctx.params + if method in _methods.SPEC_CLIENT_NOTIFICATION_METHODS: + try: + _methods.validate_client_notification(method, version, params) + except KeyError: + logger.debug("dropped %r: not defined at %s", method, version) + return + except ValidationError: + logger.warning("dropped %r: malformed params", method) + return + if method == "notifications/initialized": + # Surface validation above already rejected a malformed body, so + # commit; fall through so a registered handler observes an + # initialized connection. + self.connection.initialized.set() + elif not self.connection.initialize_accepted: + logger.debug("dropped %s: received before initialization", method) + return + entry = self.server.get_notification_handler(method) + if entry is None: + logger.debug("no handler for notification %s", method) + return + # Same absent-params contract as requests. + try: + typed_params = entry.params_type.model_validate({} if params is None else params, by_name=False) + except ValidationError: + logger.warning("dropped %r: malformed params", method) + return + await entry.handler(ctx, typed_params) + + call = self._compose_server_middleware(_inner) + try: + await call(ctx) + except Exception: + # A crashing handler must not cancel the dispatcher's task group; + # middleware saw the raise out of call_next() first. + logger.exception("notification handler for %r raised", method) + + def _compose_server_middleware(self, inner: CallNext) -> CallNext: + """Wrap `inner` in `Server.middleware`, outermost-first. + + Shared by `_on_request` and `_on_notify` so the same middleware chain + observes every inbound message. The composed callable takes the `ctx` + at call time, so a middleware can rewrite it for the rest of the chain. + """ + call = inner + for middleware in reversed(self.server.middleware): + call = partial(_apply_middleware, middleware, call) + return call + + def _make_context( + self, + dctx: DispatchContext[TransportContext], + method: str, + params: Mapping[str, Any] | None, + meta: RequestParamsMeta | None, + protocol_version: str, + ) -> ServerRequestContext[LifespanT, Any]: + # TODO(L54): remove for Context rework. Reads the SHTTP per-request + # data off the raw `dctx.message_metadata` carrier; replace with the + # per-transport context once that lands. + md = dctx.message_metadata + if isinstance(md, ServerMessageMetadata): + request = md.request_context + close_sse_stream = md.close_sse_stream + close_standalone_sse_stream = md.close_standalone_sse_stream + else: + request = close_sse_stream = close_standalone_sse_stream = None + # Per-request session: `dctx` is the request-scoped channel (auto-threads + # its own request_id on streamable HTTP); the standalone channel is read + # off `connection.outbound`. `related_request_id` on the public API selects. + # `meta` carries a request's log-level opt-in for the session's log gate. A + # notification has no request to opt in (and no response stream to carry + # the log entry), so its `_meta` never opens the gate. + session = ServerSession(dctx, self.connection, request_meta=meta if dctx.request_id is not None else None) + return ServerRequestContext( + session=session, + lifespan_context=self.lifespan_state, + method=method, + params=params, + request_id=dctx.request_id, + meta=meta, + protocol_version=protocol_version, + request=request, + close_sse_stream=close_sse_stream, + close_standalone_sse_stream=close_standalone_sse_stream, + ) + + def _serialize(self, method: str, version: str, result: HandlerResult) -> dict[str, Any]: + """Shape a handler result into its wire form: the outbound counterpart + of the inbound classification ladder. + + One pass owns the whole response envelope, in order: cache hints fill + `ttlMs`/`cacheScope` the handler left unset, core-vocabulary spec-method + results are validated and sieved by the per-version surface (a claimed + extension `resultType` shape is the extension's to own), and 2026-era + results get the `serverInfo` `_meta` stamp (spec #3002). Runs inside the + middleware chain so the OpenTelemetry span observes a failing return + shape (unsupported type, malformed spec result) as an error rather + than closing on a request that the client sees fail - and so a + middleware that short-circuits without `call_next` owns its result, + envelope included. + """ + # MRTR carve-out: `input_required` interim results, typed or mapping, never get hints. + if (hint := self.server.cache_hints.get(method)) is not None: + if isinstance(result, CacheableResult): + result = apply_cache_hint(result, hint) + elif isinstance(result, Mapping) and not _methods.is_input_required(result): + # Hint keys first so wire keys the handler set win, matching `apply_cache_hint` precedence. + result = {"ttlMs": hint.ttl_ms, "cacheScope": hint.scope, **result} + dumped = _dump_result(result) + # A modern-era extension `resultType` (outside the core vocabulary) marks + # a claimed shape owned by the extension that defined it: the per-version + # surface doesn't describe it, so the sieve applies to core results only. + # Legacy connections sieve everything - claimed shapes are 2026-era + # vocabulary and cannot be delivered on a legacy wire (mirrors the + # client-side ResultClaim rule). + # TODO(L56): reject extension resultType values unless the corresponding + # extension is in this request's _meta clientCapabilities.extensions; the + # explicit MUST-reject is client-side (basic/index.mdx ResultType), this enforces it proactively. + result_type = dumped.get("resultType") + core_shape = ( + version not in MODERN_PROTOCOL_VERSIONS + or not isinstance(result_type, str) + or result_type in CORE_RESULT_TYPES + ) + if method in _methods.SPEC_CLIENT_METHODS and core_shape: + try: + dumped = _methods.serialize_server_result(method, version, dumped) + except ValidationError: + # Server bug, not client fault. Detail stays in the server log: + # pydantic messages echo the result body. + logger.exception("handler for %r returned an invalid result", method) + raise MCPError(code=INTERNAL_ERROR, message="Handler returned an invalid result") from None + if version in MODERN_PROTOCOL_VERSIONS and dumped.get("resultType") is None: + # Spec 2026-07-28: `Result.resultType` is required - servers MUST + # include it (the absent-means-complete bridge is for clients of + # older servers only). The sieve guarantees it for core methods; + # this covers everything else: custom methods, extension methods, + # and empty results. + dumped["resultType"] = "complete" + return self._stamp_server_info(version, dumped) + + def _stamp_server_info(self, version: str, result: dict[str, Any]) -> dict[str, Any]: + """Fill the `serverInfo` `_meta` stamp on a 2026-era result (spec #3002). + + A handler-authored value wins; an explicit `null` reads as absent and + is stamped over, mirroring the request-side `clientInfo` posture (a + `null` is not a valid `Implementation`, so presence means a value). A + non-mapping `_meta` is the handler's to own, and handshake-era results + are never stamped. `result` is + pipeline-owned (`_dump_result` copies dicts; the spec-method sieve + re-dumps), but `_meta` may still be the handler's object, so the stamp + replaces it rather than writing into it. `server_info_stamp` is a + fresh dict per access, so the response never aliases server state. + """ + if version not in MODERN_PROTOCOL_VERSIONS: + return result + raw_meta = result.get("_meta") + if raw_meta is None: + result["_meta"] = {SERVER_INFO_META_KEY: self.server.server_info_stamp} + elif isinstance(raw_meta, dict): + meta = cast("dict[str, Any]", raw_meta) + if meta.get(SERVER_INFO_META_KEY) is None: + result["_meta"] = {**meta, SERVER_INFO_META_KEY: self.server.server_info_stamp} + return result + + @staticmethod + def _negotiate_initialize(params: Mapping[str, Any] | None) -> tuple[InitializeRequestParams, str]: + """Validate `initialize` params and pick the protocol version.""" + init = InitializeRequestParams.model_validate(params or {}, by_name=False) + requested = init.protocol_version + negotiated = requested if requested in HANDSHAKE_PROTOCOL_VERSIONS else LATEST_HANDSHAKE_VERSION + return init, negotiated + + def _handle_initialize(self, params: Mapping[str, Any] | None) -> InitializeResult: + """Build the `initialize` result; state commits later in `_on_request`.""" + _, negotiated = self._negotiate_initialize(params) + opts = self.init_options if self.init_options is not None else self.server.create_initialization_options() + return InitializeResult( + protocol_version=negotiated, + capabilities=opts.capabilities, + server_info=Implementation( + name=opts.server_name, + title=opts.title, + description=opts.description, + version=opts.server_version, + website_url=opts.website_url, + icons=opts.icons, + ), + instructions=opts.instructions, + ) + + +async def serve_connection( + server: Server[LifespanT], + dispatcher: Dispatcher[Any], + *, + connection: Connection, + lifespan_state: LifespanT, + init_options: InitializationOptions | None = None, + task_status: anyio.abc.TaskStatus[None] = anyio.TASK_STATUS_IGNORED, +) -> None: + """Drive ``dispatcher`` until the underlying channel closes. + + The loop-mode driver: builds the kernel, hands `on_request`/`on_notify` + to `dispatcher.run()`, and tears down `connection.exit_stack` (shielded) + on the way out. The entry constructs the `Connection`; this only consumes + it. + """ + runner = ServerRunner(server, connection, lifespan_state, init_options=init_options) + try: + await dispatcher.run(runner.on_request, runner.on_notify, task_status=task_status) + finally: + await aclose_shielded(connection) + + +async def serve_loop( + server: Server[LifespanT], + read_stream: ReadStream[SessionMessage | Exception], + write_stream: WriteStream[SessionMessage], + *, + lifespan_state: LifespanT, + session_id: str | None = None, + init_options: InitializationOptions | None = None, + raise_exceptions: bool = False, +) -> None: + """Drive ``server`` in handshake-only loop mode over a stream pair until the channel closes. + + Builds the loop-mode `JSONRPCDispatcher` + `Connection` and hands them to + `serve_connection`. The streamable-HTTP manager (which owns its lifespan + and serves the modern era on the single-exchange entry instead) calls + this; `Server.run` drives `serve_dual_era_loop`, which extends the same + dispatcher recipe (notably the `inline_methods={"initialize"}` rule) with + era routing. + """ + dispatcher: JSONRPCDispatcher[TransportContext] = JSONRPCDispatcher( + read_stream, + write_stream, + raise_handler_exceptions=raise_exceptions, + # Handle `initialize` inline so a client that pipelines it with the + # next request (spec: SHOULD NOT, not MUST NOT) sees the initialized + # state instead of failing the init-gate. + inline_methods=frozenset({"initialize"}), + ) + connection = Connection.for_loop(dispatcher, session_id=session_id) + await serve_connection( + server, dispatcher, connection=connection, lifespan_state=lifespan_state, init_options=init_options + ) + + +def _has_modern_envelope(params: Mapping[str, Any] | None) -> bool: + """Whether `params._meta` carries the reserved protocol-version key. + + The `io.modelcontextprotocol/protocolVersion` key exists only in + 2026-07-28+ envelopes and its prefix is spec-reserved, so legacy traffic + never mints it (a bare `_meta` is not evidence - legacy requests carry + `progressToken` there). The version key alone is the signal, not the full + required pair, so a half-built envelope still routes modern and gets the + classifier's INVALID_PARAMS naming the missing key. + """ + if not params: + return False + meta = params.get("_meta") + return isinstance(meta, Mapping) and PROTOCOL_VERSION_META_KEY in meta + + +def _initialize_after_modern_data(params: Mapping[str, Any] | None) -> dict[str, Any]: + """Error data for an `initialize` arriving on a modern-locked connection. + + The typed -32022 payload when the client's proposed version is parseable; + otherwise just the supported list (the point is naming what we serve). + """ + requested = (params or {}).get("protocolVersion") + if isinstance(requested, str): + return UnsupportedProtocolVersionErrorData( + supported=list(MODERN_PROTOCOL_VERSIONS), requested=requested + ).model_dump(mode="json") + return {"supported": list(MODERN_PROTOCOL_VERSIONS)} + + +def modern_error_data(exc: Exception) -> ErrorData: + """Map a modern request's handler exception to its wire `ErrorData`. + + The exception-to-wire fact shared by the modern entries (the + single-exchange HTTP path and the dual-era stream loop), so an identical + modern request fails identically on every transport: `MCPError` and + `ValidationError` map via the shared `handler_exception_to_error_data` + ladder; anything else is logged server-side and surfaced as a generic + INTERNAL_ERROR so handler internals never reach the wire. + """ + error = handler_exception_to_error_data(exc) + if error is not None: + return error + logger.exception("modern request handler raised") + return ErrorData(code=INTERNAL_ERROR, message="Internal server error") + + +@dataclass +class _NoServerRequestsDispatchContext: + """Delegating `DispatchContext` that refuses server-initiated requests. + + Wraps the loop dispatcher's per-message context for modern-era dispatch: + the modern protocol forbids server-initiated JSON-RPC requests, so + `send_raw_request` refuses while notifications and progress still ride + the duplex pipe. + """ + + _inner: DispatchContext[TransportContext] + + @property + def transport(self) -> TransportContext: + # Mask the per-message flag so the transport metadata agrees with this + # wrapper's denial: the modern HTTP entry builds its context with + # can_send_request=False, while the loop's default builder says True. + transport = self._inner.transport + return replace(transport, can_send_request=False) if transport.can_send_request else transport + + @property + def can_send_request(self) -> bool: + return False + + @property + def request_id(self) -> RequestId | None: + return self._inner.request_id + + @property + def message_metadata(self) -> MessageMetadata: + return self._inner.message_metadata + + @property + def cancel_requested(self) -> anyio.Event: + return self._inner.cancel_requested + + async def send_raw_request( + self, + method: str, + params: Mapping[str, Any] | None, + opts: CallOptions | None = None, + ) -> dict[str, Any]: + raise NoBackChannelError(method) + + async def notify(self, method: str, params: Mapping[str, Any] | None, opts: CallOptions | None = None) -> None: + await self._inner.notify(method, params, opts) + + async def progress(self, progress: float, total: float | None = None, message: str | None = None) -> None: + await self._inner.progress(progress, total, message) + + +async def serve_dual_era_loop( + server: Server[LifespanT], + read_stream: ReadStream[SessionMessage | Exception], + write_stream: WriteStream[SessionMessage], + *, + lifespan_state: LifespanT, + session_id: str | None = None, + init_options: InitializationOptions | None = None, + raise_exceptions: bool = False, +) -> None: + """Drive `server` over a duplex stream pair, in the era the client opens with. + + The client's first request decides the connection's protocol era, once: + a request carrying the 2026-07-28 per-request `_meta` envelope opens a + modern connection, and anything else - the `initialize` handshake, which + does not exist at 2026 versions even when a client stamps the envelope on + it - opens a legacy one. The deciding frame is replayed into the chosen + serving loop along with everything the client sent before it. A later + claim from the other era is refused: `initialize` on a modern connection + gets UNSUPPORTED_PROTOCOL_VERSION naming the served versions, and an + enveloped request on a legacy connection gets INVALID_REQUEST. + """ + # This loop owns both streams from the moment it is called, so the write + # stream is closed even if the client leaves before sending any request. + try: + async with _replay_from_opening_request(read_stream) as (opening, replayed): + opens_modern = ( + opening is not None and opening.method != "initialize" and _has_modern_envelope(opening.params) + ) + if opens_modern: + await _serve_modern_stream( + server, replayed, write_stream, lifespan_state=lifespan_state, raise_exceptions=raise_exceptions + ) + else: + await _serve_legacy_stream( + server, + replayed, + write_stream, + lifespan_state=lifespan_state, + session_id=session_id, + init_options=init_options, + raise_exceptions=raise_exceptions, + ) + finally: + await write_stream.aclose() + + +_PRE_REQUEST_REPLAY_LIMIT: int = 8 +"""How many frames arriving ahead of the client's first request are kept +for the chosen era's loop (a bare `notifications/initialized` is the one that +matters); further ones are dropped and never decide the era.""" + + +def _sender_context(stream: ReadStream[Any]) -> contextvars.Context: + """The per-message sender context a context-aware stream carries, else the current one.""" + ctx = getattr(stream, "last_context", None) + return ctx if ctx is not None else contextvars.copy_context() + + +@asynccontextmanager +async def _replay_from_opening_request( + read_stream: ReadStream[SessionMessage | Exception], +) -> AsyncIterator[tuple[JSONRPCRequest | None, ReadStream[SessionMessage | Exception]]]: + """Peek at the client's first request without consuming it. + + Yields that request together with a stream that replays it - preceded by + up to `_PRE_REQUEST_REPLAY_LIMIT` earlier frames - and relays the rest of + `read_stream` behind it, sender contexts included. The request is `None` + if the channel closes before one arrives. + """ + lead: list[tuple[contextvars.Context, SessionMessage | Exception]] = [] + opening_request: JSONRPCRequest | None = None + replay_send, replay_receive = anyio.create_memory_object_stream[ + tuple[contextvars.Context, SessionMessage | Exception] + ]() + replayed = ContextReceiveStream(replay_receive) + + async def replay_then_relay() -> None: + async with replay_send: + for envelope in lead: + await replay_send.send(envelope) + try: + async for item in read_stream: + await replay_send.send((_sender_context(read_stream), item)) + except anyio.ClosedResourceError: + # Receive end closed under us (stateless SHTTP teardown); same as EOF. + logger.debug("read stream closed by transport; treating as EOF") + + # This helper takes ownership of `read_stream` from the serving loop, so + # every exit - including cancellation while awaiting the first request - + # closes it and the replay channel. + try: + try: + async for item in read_stream: + if isinstance(item, SessionMessage) and isinstance(item.message, JSONRPCRequest): + opening_request = item.message + elif len(lead) >= _PRE_REQUEST_REPLAY_LIMIT: + logger.debug("dropped a frame received before the first request: %r", item) + continue + lead.append((_sender_context(read_stream), item)) + if opening_request is not None: + break + except anyio.ClosedResourceError: + # Receive end closed under us (stateless SHTTP teardown); same as EOF. + logger.debug("read stream closed by transport; treating as EOF") + async with anyio.create_task_group() as tg: + tg.start_soon(replay_then_relay) + yield opening_request, replayed + tg.cancel_scope.cancel() + finally: + await read_stream.aclose() + replay_send.close() + replay_receive.close() + + +async def _serve_legacy_stream( + server: Server[LifespanT], + read_stream: ReadStream[SessionMessage | Exception], + write_stream: WriteStream[SessionMessage], + *, + lifespan_state: LifespanT, + session_id: str | None, + init_options: InitializationOptions | None, + raise_exceptions: bool, +) -> None: + """Serve a 2025 handshake connection; enveloped requests are refused.""" + dispatcher: JSONRPCDispatcher[TransportContext] = JSONRPCDispatcher( + read_stream, + write_stream, + raise_handler_exceptions=raise_exceptions, + # `initialize` inline for the same pipelining reason as `serve_loop`. + inline_methods=frozenset({"initialize"}), + ) + connection = Connection.for_loop(dispatcher, session_id=session_id) + runner = ServerRunner(server, connection, lifespan_state, init_options=init_options) + + async def on_request( + dctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None + ) -> dict[str, Any]: + if method != "initialize" and _has_modern_envelope(params): + raise MCPError( + code=INVALID_REQUEST, + message="this connection serves the handshake protocol era; " + "requests carrying the 2026-07-28 envelope are not accepted on it", + ) + return await runner.on_request(dctx, method, params) + + try: + await dispatcher.run(on_request, runner.on_notify) + finally: + await aclose_shielded(connection) + + +async def _serve_modern_stream( + server: Server[LifespanT], + read_stream: ReadStream[SessionMessage | Exception], + write_stream: WriteStream[SessionMessage], + *, + lifespan_state: LifespanT, + raise_exceptions: bool, +) -> None: + """Serve a 2026-07-28 connection: every request carries its own envelope.""" + dispatcher: JSONRPCDispatcher[TransportContext] = JSONRPCDispatcher( + read_stream, write_stream, raise_handler_exceptions=raise_exceptions + ) + outbound = NotifyOnlyOutbound(dispatcher) + + async def on_request( + dctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None + ) -> dict[str, Any]: + if method == "initialize": + raise MCPError( + code=UNSUPPORTED_PROTOCOL_VERSION, + message="connection is serving the 2026-07-28 protocol; the initialize handshake is not accepted", + data=_initialize_after_modern_data(params), + ) + route = classify_inbound_request({"method": method, "params": params}) + if isinstance(route, InboundLadderRejection): + raise MCPError(code=route.code, message=route.message, data=route.data) + connection = Connection.from_envelope( + route.protocol_version, route.client_info, route.client_capabilities, outbound=outbound + ) + try: + return await serve_one( + server, + _NoServerRequestsDispatchContext(dctx), + method, + params, + connection=connection, + lifespan_state=lifespan_state, + ) + except (MCPError, ValidationError): + # The dispatcher's shared ladder maps these to the wire error. + raise + except Exception as exc: + if raise_exceptions: + raise + error = modern_error_data(exc) + raise MCPError(code=error.code, message=error.message, data=error.data) from exc + + async def on_notify(dctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None) -> None: + # The envelope is request-only, so a notification runs at the latest + # served version; the modern protocol has nothing version-specific here. + connection = Connection.from_envelope(LATEST_MODERN_VERSION, None, None, outbound=outbound) + notify_runner = ServerRunner(server, connection, lifespan_state) + try: + await notify_runner.on_notify(_NoServerRequestsDispatchContext(dctx), method, params) + finally: + await aclose_shielded(connection) + + await dispatcher.run(on_request, on_notify) + + +async def serve_one( + server: Server[LifespanT], + dctx: DispatchContext[TransportContext], + method: str, + params: Mapping[str, Any] | None, + *, + connection: Connection, + lifespan_state: LifespanT, +) -> dict[str, Any]: + """Handle a single request ``(method, params)`` and return its result dict. + + The single-exchange driver: builds the kernel, runs `on_request` once under + `dctx`, and tears down `connection.exit_stack` (shielded) on the way out. + The entry constructs the (born-ready) `Connection` and the `dctx`; this + only consumes them. + + Raises whatever the handler chain raises (`MCPError` / `ValidationError` / + unmapped); callers own the exception-to-wire mapping. + """ + runner = ServerRunner(server, connection, lifespan_state) + try: + return await runner.on_request(dctx, method, params) + finally: + await aclose_shielded(connection) + + +def modern_on_request(server: Server[LifespanT], lifespan_state: LifespanT) -> OnRequest: + """Return an `OnRequest` callback that serves each call via `serve_one` with a fresh per-request `Connection`. + + Wire this into the server side of a `DirectDispatcher` peer-pair to drive an + in-process server on the modern per-request-envelope path (each request + carries protocol version, client info, and capabilities in `params._meta`; + no `initialize` handshake). The dispatch context is wrapped in the + server-requests denial, so the modern prohibition on server-initiated + JSON-RPC requests holds on this entry like on the others. Like `serve_one`, + this raises whatever the handler chain raises - the dispatcher owns the + exception-to-error mapping. + """ + + async def handle( + dctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None + ) -> dict[str, Any]: + meta = (params or {}).get("_meta", {}) + connection = Connection.from_envelope( + meta.get(PROTOCOL_VERSION_META_KEY, LATEST_MODERN_VERSION), + meta.get(CLIENT_INFO_META_KEY), + meta.get(CLIENT_CAPABILITIES_META_KEY), + ) + return await serve_one( + server, + _NoServerRequestsDispatchContext(dctx), + method, + params, + connection=connection, + lifespan_state=lifespan_state, + ) + + return handle diff --git a/venv/lib/python3.12/site-packages/mcp/server/session.py b/venv/lib/python3.12/site-packages/mcp/server/session.py new file mode 100644 index 0000000..bb44641 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/session.py @@ -0,0 +1,489 @@ +"""`ServerSession`: server-to-client requests and notifications. + +A per-request proxy built by the kernel for each inbound request. Exposes the +request-scoped outbound channel and the connection's standalone channel. +Handlers reach it as `ctx.session` and use the typed helpers (`elicit_form`, +`send_log_message`, ...) to call back to the client. +""" + +import logging +from typing import Any, TypeVar, overload + +import mcp_types as types +from mcp_types import methods as _methods +from mcp_types.version import MODERN_PROTOCOL_VERSIONS +from pydantic import AnyUrl, BaseModel +from typing_extensions import deprecated + +from mcp.server.connection import Connection, allowed_log_levels +from mcp.server.validation import validate_sampling_tools, validate_tool_use_result_messages, wants_sampling_tools +from mcp.shared.dispatcher import CallOptions, DispatchContext, ProgressFnT +from mcp.shared.exceptions import MCPDeprecationWarning +from mcp.shared.message import ServerMessageMetadata + +__all__ = ["ServerSession"] + +logger = logging.getLogger(__name__) +# `send_log_message`'s `logger` parameter (public API, the spec's logger-name +# field) shadows the module logger inside that method; this alias keeps it +# reachable there. +_logger = logger + +ResultT = TypeVar("ResultT", bound=BaseModel) + + +class ServerSession: + """Per-request proxy for server-to-client requests and notifications. + + Built once per inbound request by the kernel's `_make_context`. Holds two + `Outbound` channels: the request-scoped one (the per-request + `DispatchContext`, which on streamable HTTP routes onto the originating + POST's response stream) and the connection's standalone channel + (`connection.outbound`). `related_request_id` on the public methods is the + selector — present means request-scoped, absent means standalone — and + never crosses the `Outbound` Protocol. + """ + + def __init__( + self, + request_outbound: DispatchContext[Any], + connection: Connection, + *, + request_meta: types.RequestParamsMeta | None = None, + ) -> None: + self._request_outbound = request_outbound + self._connection = connection + # The per-request log-delivery contract, fixed at construction: on + # 2026-07-28+ the inbound request's `_meta` log-level opt-in decides + # which `notifications/message` levels may be sent for this request + # (and they ride this request's stream only); on handshake versions + # every level may be sent (`logging/setLevel`-era semantics). + self._log_is_request_scoped = connection.protocol_version in MODERN_PROTOCOL_VERSIONS + self._allowed_log_levels = allowed_log_levels(connection.protocol_version, request_meta) + + @property + def client_params(self) -> types.InitializeRequestParams | None: + """The client's `initialize` request params; `None` when no client info was supplied.""" + return self._connection.client_params + + @property + def client_capabilities(self) -> types.ClientCapabilities | None: + """The capabilities the client declared; `None` when none were declared. + + Prefer this over `client_params.capabilities`: on 2026-07-28+ the + request envelope declares capabilities while client info stays + optional, so capabilities can be present without `client_params`. + """ + return self._connection.client_capabilities + + @property + def can_send_request(self) -> bool: + """Whether this request's channel can currently deliver a server-initiated request.""" + return self._request_outbound.can_send_request + + @property + def protocol_version(self) -> str: + """The protocol version this connection speaks. + + Populated at `Connection` construction and overwritten once the + handshake commits on the loop path; never `None`. + """ + return self._connection.protocol_version + + async def send_request( + self, + request: types.ServerRequest, + result_type: type[ResultT], + request_read_timeout_seconds: float | None = None, + metadata: ServerMessageMetadata | None = None, + progress_callback: ProgressFnT | None = None, + ) -> ResultT: + """Send a typed server-to-client request and validate the result. + + Raises: + MCPError: The peer responded with an error. + NoBackChannelError: The connection has no back-channel for + server-initiated requests (raised by the held `Outbound`). + pydantic.ValidationError: The peer's result does not match `result_type`. + """ + related = metadata.related_request_id if metadata is not None else None + channel = self._request_outbound if related is not None else self._connection.outbound + data = request.model_dump(by_alias=True, mode="json", exclude_none=True) + opts: CallOptions = {} + if request_read_timeout_seconds is not None: + opts["timeout"] = request_read_timeout_seconds + if progress_callback is not None: + opts["on_progress"] = progress_callback + result = await channel.send_raw_request(data["method"], data.get("params"), opts or None) + try: + _methods.validate_client_result(request.method, self.protocol_version, result) + except KeyError: + pass + return result_type.model_validate(result, by_name=False) + + async def send_notification( + self, + notification: types.ServerNotification, + related_request_id: types.RequestId | None = None, + ) -> None: + """Send a typed server-to-client notification.""" + await self._notify(notification, request_scoped=related_request_id is not None) + + async def _notify(self, notification: types.ServerNotification, *, request_scoped: bool) -> None: + channel = self._request_outbound if request_scoped else self._connection.outbound + data = notification.model_dump(by_alias=True, mode="json", exclude_none=True) + await channel.notify(data["method"], data.get("params")) + + def check_client_capability(self, capability: types.ClientCapabilities) -> bool: + """Check if the client supports a specific capability.""" + return self._connection.check_capability(capability) + + @deprecated("The logging capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def send_log_message( + self, + level: types.LoggingLevel, + data: Any, + logger: str | None = None, + related_request_id: types.RequestId | None = None, + ) -> None: + """Send a log message notification. + + On 2026-07-28+ delivery is a per-request opt-in: nothing is sent + unless this request's `_meta` carried the reserved log-level key, and + entries below the requested level are dropped (debug-logged). What is + sent rides this request's stream regardless of `related_request_id` - + the spec forbids `notifications/message` on any stream but the one + carrying the response. Handshake versions send unconditionally on the + channel `related_request_id` selects, as before. + """ + if level not in self._allowed_log_levels: + _logger.debug("dropped notifications/message at %r: not opted in at that level on this request", level) + return + await self._notify( + types.LoggingMessageNotification( + params=types.LoggingMessageNotificationParams( + level=level, + data=data, + logger=logger, + ), + ), + request_scoped=self._log_is_request_scoped or related_request_id is not None, + ) + + async def send_resource_updated(self, uri: str | AnyUrl) -> None: + """Send a resource updated notification.""" + await self.send_notification( + types.ResourceUpdatedNotification( + params=types.ResourceUpdatedNotificationParams(uri=str(uri)), + ) + ) + + @overload + @deprecated("The sampling capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def create_message( + self, + messages: list[types.SamplingMessage], + *, + max_tokens: int, + system_prompt: str | None = None, + include_context: types.IncludeContext | None = None, + temperature: float | None = None, + stop_sequences: list[str] | None = None, + metadata: dict[str, Any] | None = None, + model_preferences: types.ModelPreferences | None = None, + tools: None = None, + tool_choice: None = None, + related_request_id: types.RequestId | None = None, + ) -> types.CreateMessageResult: + """Overload: Without tools or tool_choice, returns single content.""" + ... + + @overload + @deprecated("The sampling capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def create_message( + self, + messages: list[types.SamplingMessage], + *, + max_tokens: int, + system_prompt: str | None = None, + include_context: types.IncludeContext | None = None, + temperature: float | None = None, + stop_sequences: list[str] | None = None, + metadata: dict[str, Any] | None = None, + model_preferences: types.ModelPreferences | None = None, + tools: list[types.Tool], + tool_choice: types.ToolChoice | None = None, + related_request_id: types.RequestId | None = None, + ) -> types.CreateMessageResultWithTools: + """Overload: With tools, returns array-capable content.""" + ... + + @overload + @deprecated("The sampling capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def create_message( + self, + messages: list[types.SamplingMessage], + *, + max_tokens: int, + system_prompt: str | None = None, + include_context: types.IncludeContext | None = None, + temperature: float | None = None, + stop_sequences: list[str] | None = None, + metadata: dict[str, Any] | None = None, + model_preferences: types.ModelPreferences | None = None, + tools: list[types.Tool] | None = None, + tool_choice: types.ToolChoice, + related_request_id: types.RequestId | None = None, + ) -> types.CreateMessageResultWithTools: + """Overload: With tool_choice, returns array-capable content.""" + ... + + @deprecated("The sampling capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def create_message( + self, + messages: list[types.SamplingMessage], + *, + max_tokens: int, + system_prompt: str | None = None, + include_context: types.IncludeContext | None = None, + temperature: float | None = None, + stop_sequences: list[str] | None = None, + metadata: dict[str, Any] | None = None, + model_preferences: types.ModelPreferences | None = None, + tools: list[types.Tool] | None = None, + tool_choice: types.ToolChoice | None = None, + related_request_id: types.RequestId | None = None, + ) -> types.CreateMessageResult | types.CreateMessageResultWithTools: + """Send a sampling/create_message request. + + Args: + messages: The conversation messages to send. + max_tokens: Maximum number of tokens to generate. + system_prompt: Optional system prompt. + include_context: Optional context inclusion setting. + Should only be set to "thisServer" or "allServers" + if the client has sampling.context capability. + temperature: Optional sampling temperature. + stop_sequences: Optional stop sequences. + metadata: Optional metadata to pass through to the LLM provider. + model_preferences: Optional model selection preferences. + tools: Optional list of tools the LLM can use during sampling. + Requires client to have sampling.tools capability. + tool_choice: Optional control over tool usage behavior. + Requires client to have sampling.tools capability. + related_request_id: Optional ID of a related request. + + Returns: + The sampling result from the client. + + Raises: + MCPError: If tools are provided but client doesn't support them. + ValueError: If tool_use or tool_result message structure is invalid. + NoBackChannelError: The connection has no back-channel for + server-initiated requests. + """ + validate_sampling_tools(self.client_capabilities, tools, tool_choice) + validate_tool_use_result_messages(messages) + + request = types.CreateMessageRequest( + params=types.CreateMessageRequestParams( + messages=messages, + system_prompt=system_prompt, + include_context=include_context, + temperature=temperature, + max_tokens=max_tokens, + stop_sequences=stop_sequences, + metadata=metadata, + model_preferences=model_preferences, + tools=tools, + tool_choice=tool_choice, + ), + ) + metadata_obj = ServerMessageMetadata(related_request_id=related_request_id) + + if wants_sampling_tools(tools, tool_choice): + return await self.send_request( + request=request, + result_type=types.CreateMessageResultWithTools, + metadata=metadata_obj, + ) + return await self.send_request( + request=request, + result_type=types.CreateMessageResult, + metadata=metadata_obj, + ) + + @deprecated("The roots capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def list_roots(self) -> types.ListRootsResult: + """Send a roots/list request. + + Raises: + NoBackChannelError: The connection has no back-channel for + server-initiated requests. + """ + return await self.send_request( + types.ListRootsRequest(), + types.ListRootsResult, + ) + + async def elicit( + self, + message: str, + requested_schema: types.ElicitRequestedSchema, + related_request_id: types.RequestId | None = None, + ) -> types.ElicitResult: + """Send a form mode elicitation/create request. + + Args: + message: The message to present to the user. + requested_schema: Schema defining the expected response structure. + related_request_id: Optional ID of the request that triggered this elicitation. + + Returns: + The client's response. + + Note: + This method is deprecated in favor of elicit_form(). It remains for + backward compatibility but new code should use elicit_form(). + """ + return await self.elicit_form(message, requested_schema, related_request_id) + + async def elicit_form( + self, + message: str, + requested_schema: types.ElicitRequestedSchema, + related_request_id: types.RequestId | None = None, + ) -> types.ElicitResult: + """Send a form mode elicitation/create request. + + Args: + message: The message to present to the user. + requested_schema: Schema defining the expected response structure. + related_request_id: Optional ID of the request that triggered this elicitation. + + Returns: + The client's response with form data. + + Raises: + NoBackChannelError: The connection has no back-channel for + server-initiated requests. + """ + return await self.send_request( + types.ElicitRequest( + params=types.ElicitRequestFormParams( + message=message, + requested_schema=requested_schema, + ), + ), + types.ElicitResult, + metadata=ServerMessageMetadata(related_request_id=related_request_id), + ) + + async def elicit_url( + self, + message: str, + url: str, + elicitation_id: str, + related_request_id: types.RequestId | None = None, + ) -> types.ElicitResult: + """Send a URL mode elicitation/create request. + + This directs the user to an external URL for out-of-band interactions + like OAuth flows, credential collection, or payment processing. + + Args: + message: Human-readable explanation of why the interaction is needed. + url: The URL the user should navigate to. + elicitation_id: Unique identifier for tracking this elicitation. + related_request_id: Optional ID of the request that triggered this elicitation. + + Returns: + The client's response indicating acceptance, decline, or cancellation. + + Raises: + NoBackChannelError: The connection has no back-channel for + server-initiated requests. + """ + return await self.send_request( + types.ElicitRequest( + params=types.ElicitRequestURLParams( + message=message, + url=url, + elicitation_id=elicitation_id, + ), + ), + types.ElicitResult, + metadata=ServerMessageMetadata(related_request_id=related_request_id), + ) + + async def send_ping(self) -> types.EmptyResult: + """Send a ping request.""" + return await self.send_request( + types.PingRequest(), + types.EmptyResult, + ) + + async def report_progress(self, progress: float, total: float | None = None, message: str | None = None) -> None: + """Report progress for the inbound request this session is scoped to. + + A no-op when the caller did not request progress. Dispatcher-agnostic: + on JSON-RPC the held `DispatchContext` emits ``notifications/progress`` + against the caller's token; on the in-process direct dispatcher it + invokes the caller's callback directly. + """ + await self._request_outbound.progress(progress, total, message) + + async def send_progress_notification( + self, + progress_token: str | int, + progress: float, + total: float | None = None, + message: str | None = None, + related_request_id: str | None = None, + ) -> None: + """Send a progress notification.""" + await self.send_notification( + types.ProgressNotification( + params=types.ProgressNotificationParams( + progress_token=progress_token, + progress=progress, + total=total, + message=message, + ), + ), + related_request_id, + ) + + async def send_resource_list_changed(self) -> None: + """Send a resource list changed notification.""" + await self.send_notification(types.ResourceListChangedNotification()) + + async def send_tool_list_changed(self) -> None: + """Send a tool list changed notification.""" + await self.send_notification(types.ToolListChangedNotification()) + + async def send_prompt_list_changed(self) -> None: + """Send a prompt list changed notification.""" + await self.send_notification(types.PromptListChangedNotification()) + + async def send_elicit_complete( + self, + elicitation_id: str, + related_request_id: types.RequestId | None = None, + ) -> None: + """Send an elicitation completion notification. + + This should be sent when a URL mode elicitation has been completed + out-of-band to inform the client that it may retry any requests + that were waiting for this elicitation. + + Args: + elicitation_id: The unique identifier of the completed elicitation + related_request_id: Optional ID of the request that triggered this notification + """ + await self.send_notification( + types.ElicitCompleteNotification( + params=types.ElicitCompleteNotificationParams(elicitation_id=elicitation_id) + ), + related_request_id, + ) diff --git a/venv/lib/python3.12/site-packages/mcp/server/sse.py b/venv/lib/python3.12/site-packages/mcp/server/sse.py new file mode 100644 index 0000000..d71ef25 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/sse.py @@ -0,0 +1,287 @@ +"""SSE Server Transport Module + +This module implements a Server-Sent Events (SSE) transport layer for MCP servers. + +Example: + ```python + # Create an SSE transport at an endpoint + sse = SseServerTransport("/messages/") + + # Create Starlette routes for SSE and message handling + routes = [ + Route("/sse", endpoint=handle_sse, methods=["GET"]), + Mount("/messages/", app=sse.handle_post_message), + ] + + # Define handler functions + async def handle_sse(request): + async with sse.connect_sse( + request.scope, request.receive, request._send + ) as streams: + await app.run( + streams[0], streams[1], app.create_initialization_options() + ) + # Return empty response to avoid NoneType error + return Response() + + # Create and run Starlette app + starlette_app = Starlette(routes=routes) + uvicorn.run(starlette_app, host="127.0.0.1", port=port) + ``` + +Note: The handle_sse function must return a Response to avoid a +"TypeError: 'NoneType' object is not callable" error when client disconnects. The example above returns +an empty Response() after the SSE connection ends to fix this. + +See SseServerTransport class documentation for more details. +""" + +import logging +from contextlib import asynccontextmanager +from typing import Any +from urllib.parse import quote +from uuid import UUID, uuid4 + +import anyio +import mcp_types as types +from pydantic import ValidationError +from sse_starlette import EventSourceResponse +from starlette.requests import Request +from starlette.responses import Response +from starlette.types import Receive, Scope, Send + +from mcp.server.auth.middleware.bearer_auth import AuthenticatedUser, AuthorizationContext, authorization_context +from mcp.server.transport_security import ( + DEFAULT_MAX_REQUEST_BODY_SIZE, + RequestBodyLimitMiddleware, + TransportSecurityMiddleware, + TransportSecuritySettings, +) +from mcp.shared._context_streams import ContextSendStream, create_context_streams +from mcp.shared.message import ServerMessageMetadata, SessionMessage + +logger = logging.getLogger(__name__) + + +class SseServerTransport: + """SSE server transport for MCP. This class provides two ASGI applications, + suitable for use with a framework like Starlette and a server like Hypercorn: + + 1. connect_sse() is an ASGI application which receives incoming GET requests, + and sets up a new SSE stream to send server messages to the client. + 2. handle_post_message() is an ASGI application which receives incoming POST + requests, which should contain client messages that link to a + previously-established SSE session. + """ + + _endpoint: str + _read_stream_writers: dict[UUID, ContextSendStream[SessionMessage | Exception]] + # Identity of the credential that created each session; requests for a + # session must present the same credential. + _session_owners: dict[UUID, AuthorizationContext] + _security: TransportSecurityMiddleware + + def __init__( + self, + endpoint: str, + security_settings: TransportSecuritySettings | None = None, + max_request_body_size: int = DEFAULT_MAX_REQUEST_BODY_SIZE, + ) -> None: + """Creates a new SSE server transport, which will direct the client to POST + messages to the relative path given. + + Args: + endpoint: A relative path where messages should be posted + (e.g., "/messages/"). + security_settings: Optional security settings for DNS rebinding protection. + max_request_body_size: Maximum size in bytes for POSTed message bodies. Requests that + declare or stream a larger body receive HTTP 413. Defaults to 4 MiB, matching + `StreamableHTTPSessionManager`. + + Note: + We use relative paths instead of full URLs for several reasons: + 1. Security: Prevents cross-origin requests by ensuring clients only connect + to the same origin they established the SSE connection with + 2. Flexibility: The server can be mounted at any path without needing to + know its full URL + 3. Portability: The same endpoint configuration works across different + environments (development, staging, production) + + Raises: + ValueError: If the endpoint is a full URL instead of a relative path + """ + + super().__init__() + + if max_request_body_size <= 0: + raise ValueError("max_request_body_size must be a positive number of bytes") + + # Validate that endpoint is a relative path and not a full URL + if "://" in endpoint or endpoint.startswith("//") or "?" in endpoint or "#" in endpoint: + raise ValueError( + f"Given endpoint: {endpoint} is not a relative path (e.g., '/messages/'), " + "expecting a relative path (e.g., '/messages/')." + ) + + # Ensure endpoint starts with a forward slash + if not endpoint.startswith("/"): + endpoint = "/" + endpoint + + self._endpoint = endpoint + self._read_stream_writers = {} + self._session_owners = {} + self._security = TransportSecurityMiddleware(security_settings) + self._post_message_app = RequestBodyLimitMiddleware(self._handle_post_message, max_request_body_size) + logger.debug(f"SseServerTransport initialized with endpoint: {endpoint}") + + @asynccontextmanager + async def connect_sse(self, scope: Scope, receive: Receive, send: Send): + if scope["type"] != "http": + logger.error("connect_sse received non-HTTP request") + raise ValueError("connect_sse can only handle HTTP requests") + + # Validate request headers for DNS rebinding protection + request = Request(scope, receive) + error_response = await self._security.validate_request(request, is_post=False) + if error_response: + await error_response(scope, receive, send) + raise ValueError("Request validation failed") + + logger.debug("Setting up SSE connection") + + read_stream_writer, read_stream = create_context_streams[SessionMessage | Exception](0) + write_stream, write_stream_reader = create_context_streams[SessionMessage](0) + + session_id = uuid4() + user = scope.get("user") + if isinstance(user, AuthenticatedUser): + self._session_owners[session_id] = authorization_context(user) + self._read_stream_writers[session_id] = read_stream_writer + logger.debug(f"Created new session with ID: {session_id}") + + # Determine the full path for the message endpoint to be sent to the client. + # scope['root_path'] is the prefix where the current Starlette app + # instance is mounted. + # e.g., "" if top-level, or "/api_prefix" if mounted under "/api_prefix". + root_path = scope.get("root_path", "") + + # self._endpoint is the path *within* this app, e.g., "/messages". + # Concatenating them gives the full absolute path from the server root. + # e.g., "" + "/messages" -> "/messages" + # e.g., "/api_prefix" + "/messages" -> "/api_prefix/messages" + full_message_path_for_client = root_path.rstrip("/") + self._endpoint + + # This is the URI (path + query) the client will use to POST messages. + client_post_uri_data = f"{quote(full_message_path_for_client)}?session_id={session_id.hex}" + + sse_stream_writer, sse_stream_reader = anyio.create_memory_object_stream[dict[str, Any]](0) + + async def sse_writer(): + logger.debug("Starting SSE writer") + async with sse_stream_writer, write_stream_reader: + await sse_stream_writer.send({"event": "endpoint", "data": client_post_uri_data}) + logger.debug(f"Sent endpoint event: {client_post_uri_data}") + + async for session_message in write_stream_reader: + logger.debug(f"Sending message via SSE: {session_message}") + await sse_stream_writer.send( + { + "event": "message", + "data": session_message.message.model_dump_json(by_alias=True, exclude_unset=True), + } + ) + + try: + async with anyio.create_task_group() as tg: + + async def response_wrapper(scope: Scope, receive: Receive, send: Send): + """The EventSourceResponse returning signals a client close / disconnect. + In this case we close our side of the streams to signal the client that + the connection has been closed. + """ + await EventSourceResponse(content=sse_stream_reader, data_sender_callable=sse_writer)( + scope, receive, send + ) + await read_stream_writer.aclose() + await write_stream_reader.aclose() + await sse_stream_reader.aclose() + logging.debug(f"Client session disconnected {session_id}") + + logger.debug("Starting SSE response task") + tg.start_soon(response_wrapper, scope, receive, send) + + logger.debug("Yielding read and write streams") + yield (read_stream, write_stream) + finally: + self._read_stream_writers.pop(session_id, None) + self._session_owners.pop(session_id, None) + + async def handle_post_message(self, scope: Scope, receive: Receive, send: Send) -> None: + """ASGI application for the message endpoint. + + Only POST is accepted (other methods get 405), and bodies larger than + `max_request_body_size` are answered with 413 before the message is handled. + """ + if scope["method"] != "POST": + response = Response(status_code=405, headers={"Allow": "POST"}) + return await response(scope, receive, send) + await self._post_message_app(scope, receive, send) + + async def _handle_post_message(self, scope: Scope, receive: Receive, send: Send) -> None: + logger.debug("Handling POST message") + request = Request(scope, receive) + + # Validate request headers for DNS rebinding protection + error_response = await self._security.validate_request(request, is_post=True) + if error_response: + return await error_response(scope, receive, send) + + session_id_param = request.query_params.get("session_id") + if session_id_param is None: + logger.warning("Received request without session_id") + response = Response("session_id is required", status_code=400) + return await response(scope, receive, send) + + try: + session_id = UUID(hex=session_id_param) + logger.debug(f"Parsed session ID: {session_id}") + except ValueError: + logger.warning(f"Received invalid session ID: {session_id_param}") + response = Response("Invalid session ID", status_code=400) + return await response(scope, receive, send) + + writer = self._read_stream_writers.get(session_id) + if not writer: + logger.warning(f"Could not find session for ID: {session_id}") + response = Response("Could not find session", status_code=404) + return await response(scope, receive, send) + + user = scope.get("user") + requestor = authorization_context(user) if isinstance(user, AuthenticatedUser) else None + if requestor != self._session_owners.get(session_id): + # A session can only be used with the credential that created it. + # Respond exactly as if the session did not exist. + logger.warning("Rejecting message for session %s: credential does not match", session_id) + response = Response("Could not find session", status_code=404) + return await response(scope, receive, send) + + body = await request.body() + logger.debug(f"Received JSON: {body}") + + try: + message = types.jsonrpc_message_adapter.validate_json(body, by_name=False) + logger.debug(f"Validated client message: {message}") + except ValidationError as err: + logger.exception("Failed to parse message") + response = Response("Could not parse message", status_code=400) + await response(scope, receive, send) + await writer.send(err) + return + + # Pass the ASGI scope for framework-agnostic access to request data + metadata = ServerMessageMetadata(request_context=request) + session_message = SessionMessage(message, metadata=metadata) + logger.debug(f"Sending session message to writer: {session_message}") + response = Response("Accepted", status_code=202) + await response(scope, receive, send) + await writer.send(session_message) diff --git a/venv/lib/python3.12/site-packages/mcp/server/stdio.py b/venv/lib/python3.12/site-packages/mcp/server/stdio.py new file mode 100644 index 0000000..de8bbae --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/stdio.py @@ -0,0 +1,217 @@ +"""Stdio server transport for MCP. + +Example: + ```python + async def run_server(): + async with stdio_server() as (read_stream, write_stream): + server = await create_my_server() + await server.run(read_stream, write_stream, init_options) + + anyio.run(run_server) + ``` +""" + +import os +import sys +import threading +from collections.abc import Callable +from contextlib import asynccontextmanager, suppress +from dataclasses import dataclass +from io import TextIOWrapper +from typing import BinaryIO, Literal, TextIO + +import anyio +import anyio.lowlevel +import mcp_types as types + +from mcp.os.win32.utilities import rebind_std_handle_to_fd +from mcp.shared._context_streams import create_context_streams +from mcp.shared.message import SessionMessage + +if sys.platform != "win32": # pragma: no branch + import fcntl # pragma: lax no cover - POSIX-only line, uncovered on Windows runners + +# Stream-claim contract (design and attack log in PR #3117): +# - _claims is the single authority for who owns fd 0/1; mutated only under the +# lock, only by acquire's insert and release's deregister. +# - private_fd is recorded the instant the wire duplicate exists, before fd is +# ever moved, and is never closed while the claim is registered. +# - Release deregisters only after dup2(private_fd, fd) restores the wire; a +# failed release keeps the claim, so successors are refused, never fed a +# diverted descriptor. Every failure lands on that safe side. +_claims: dict[int, "_StreamClaim"] = {} +_claims_lock = threading.Lock() + + +@dataclass +class _StreamClaim: + fd: int + private_fd: int | None = None + + +class _UnownedTextWrapper(TextIOWrapper): + """Text layer whose close never closes the underlying buffer. + + The buffer is not the transport's to close: in the in-place paths it is the + sys stream's own buffer, and closing it at garbage collection destroyed + sys.stdout for the rest of the process (issue #1933). + """ + + def close(self) -> None: + with suppress(ValueError): + self.detach() + + +def _is_backed_by_fd(stream: TextIO, fd: int) -> bool: + try: + return stream.buffer.fileno() == fd + except (AttributeError, OSError, ValueError): + return False + + +def _dup_above_std(fd: int) -> int: + """Duplicate fd onto a descriptor that cannot land in the standard range.""" + if sys.platform == "win32": # pragma: no cover + duplicate = os.dup(fd) + if duplicate <= 2: + os.close(duplicate) + raise OSError(f"duplicate of fd {fd} landed in the standard range") + return duplicate + return fcntl.fcntl(fd, fcntl.F_DUPFD_CLOEXEC, 3) # pragma: lax no cover - POSIX-only + + +def _open_stdin_diversion() -> int: + return os.open(os.devnull, os.O_RDONLY) + + +def _open_stdout_diversion() -> int: + try: + return os.dup(2) + except OSError: + return os.open(os.devnull, os.O_WRONLY) + + +def _restore_fd(fd: int, private_fd: int) -> bool: + """Point fd back at the wire; the Windows handle rebind never affects the outcome.""" + try: + os.dup2(private_fd, fd) + except OSError: + return False + if sys.platform == "win32": # pragma: no cover + with suppress(OSError): + rebind_std_handle_to_fd(fd) + return True + + +def _claim_fd( + fd: int, stream: TextIO, mode: Literal["rb", "wb"], open_diversion: Callable[[], int] +) -> tuple[BinaryIO, Callable[[], None] | None]: + """Claim a standard stream: divert fd and serve the wire from a private duplicate. + + Best-effort: when descriptors cannot be duplicated or diverted, serves the + sys stream's buffer in place, exactly as v1 did, with the claim held. + + Raises: + RuntimeError: fd is already claimed by another transport in this process. + """ + if not _is_backed_by_fd(stream, fd): + return stream.buffer, None + claim = _StreamClaim(fd) + with _claims_lock: + if fd in _claims: + raise RuntimeError(f"another stdio_server() in this process has already claimed fd {fd}") + _claims[fd] = claim + + def release() -> None: + if claim.private_fd is None or _restore_fd(fd, claim.private_fd): + with _claims_lock: + del _claims[fd] + + try: + private_fd = _dup_above_std(fd) + except OSError: + return stream.buffer, release + claim.private_fd = private_fd + + try: + diversion_fd = open_diversion() + except OSError: + return stream.buffer, release + try: + os.dup2(diversion_fd, fd) + except OSError: + # The divert did not land; ensure fd carries the wire (a Windows dup2 can + # close its target before failing), then serve it in place through the + # shared buffer, since two writers on one pipe would tear frames. + with suppress(OSError): + os.close(diversion_fd) + _restore_fd(fd, private_fd) + return stream.buffer, release + with suppress(OSError): + os.close(diversion_fd) + if sys.platform == "win32": # pragma: no cover + with suppress(OSError): + rebind_std_handle_to_fd(fd) + + # closefd=False: a worker thread can still block on this descriptor after + # the transport exits, so it must never be closed and recycled under it. + return os.fdopen(private_fd, mode, closefd=False), release + + +@asynccontextmanager +async def stdio_server(stdin: anyio.AsyncFile[str] | None = None, stdout: anyio.AsyncFile[str] | None = None): + """Serve MCP over the process's stdin and stdout. + + While serving, fd 0 points at the null device and fd 1 at stderr, so handlers + and children read EOF and their stray output misses the wire; both descriptors + are restored on exit. Explicit streams skip the claim, and a second concurrent + stdio_server() raises RuntimeError. + """ + # Re-wrap the binary buffers as UTF-8 text; the std handles' platform encodings are unreliable. + restore_stdin: Callable[[], None] | None = None + restore_stdout: Callable[[], None] | None = None + try: + if not stdin: + stdin_buffer, restore_stdin = _claim_fd(0, sys.stdin, "rb", _open_stdin_diversion) + stdin = anyio.wrap_file(_UnownedTextWrapper(stdin_buffer, encoding="utf-8", errors="replace")) + if not stdout: + stdout_buffer, restore_stdout = _claim_fd(1, sys.stdout, "wb", _open_stdout_diversion) + stdout = anyio.wrap_file(_UnownedTextWrapper(stdout_buffer, encoding="utf-8")) + + read_stream_writer, read_stream = create_context_streams[SessionMessage | Exception](0) + write_stream, write_stream_reader = create_context_streams[SessionMessage](0) + + async def stdin_reader(): + try: + async with read_stream_writer: + async for line in stdin: + try: + message = types.jsonrpc_message_adapter.validate_json(line, by_name=False) + except Exception as exc: + await read_stream_writer.send(exc) + continue + + session_message = SessionMessage(message) + await read_stream_writer.send(session_message) + except anyio.ClosedResourceError: # pragma: no cover + await anyio.lowlevel.checkpoint() + + async def stdout_writer(): + try: + async with write_stream_reader: + async for session_message in write_stream_reader: + json = session_message.message.model_dump_json(by_alias=True, exclude_unset=True) + await stdout.write(json + "\n") + await stdout.flush() + except anyio.ClosedResourceError: # pragma: no cover + await anyio.lowlevel.checkpoint() + + async with anyio.create_task_group() as tg: + tg.start_soon(stdin_reader) + tg.start_soon(stdout_writer) + yield read_stream, write_stream + finally: + if restore_stdout is not None: + restore_stdout() + if restore_stdin is not None: + restore_stdin() diff --git a/venv/lib/python3.12/site-packages/mcp/server/streamable_http.py b/venv/lib/python3.12/site-packages/mcp/server/streamable_http.py new file mode 100644 index 0000000..1a4e993 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/streamable_http.py @@ -0,0 +1,1092 @@ +"""StreamableHTTP Server Transport Module + +This module implements an HTTP transport layer with Streamable HTTP. + +The transport handles bidirectional communication using HTTP requests and +responses, with streaming support for long-running operations. +""" + +import logging +import re +from abc import ABC, abstractmethod +from collections.abc import AsyncGenerator, Awaitable, Callable +from contextlib import asynccontextmanager +from dataclasses import dataclass +from functools import partial +from http import HTTPStatus +from typing import Any, Final + +import anyio +import pydantic_core +from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream +from mcp_types import ( + DEFAULT_NEGOTIATED_VERSION, + INTERNAL_ERROR, + INVALID_PARAMS, + INVALID_REQUEST, + PARSE_ERROR, + ErrorData, + JSONRPCError, + JSONRPCMessage, + JSONRPCRequest, + JSONRPCResponse, + RequestId, + jsonrpc_message_adapter, +) +from mcp_types.version import is_version_at_least +from pydantic import ValidationError +from sse_starlette import EventSourceResponse +from starlette.requests import Request +from starlette.responses import Response +from starlette.types import Receive, Scope, Send + +from mcp.server.transport_security import TransportSecurityMiddleware, TransportSecuritySettings +from mcp.shared._context_streams import ContextReceiveStream, ContextSendStream, create_context_streams +from mcp.shared._stream_protocols import ReadStream, WriteStream +from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER +from mcp.shared.message import CloseSSEStreamCallback, ServerMessageMetadata, SessionMessage + +logger = logging.getLogger(__name__) + + +# Header names +MCP_SESSION_ID_HEADER = "mcp-session-id" +LAST_EVENT_ID_HEADER = "last-event-id" + +# Content types +CONTENT_TYPE_JSON = "application/json" +CONTENT_TYPE_SSE = "text/event-stream" + +# Special key for the standalone GET stream +GET_STREAM_KEY = "_GET_stream" + +# Buffer for the per-request `_request_streams` so the serial `message_router` +# can deposit a response and move on instead of head-of-line blocking the +# whole session on a lazily-started `sse_writer`. See #1764. +REQUEST_STREAM_BUFFER_SIZE: Final = 16 + +# Error code answering a request that settled without a response (e.g. it was +# cancelled) on this 2025-era wire, which ends a request's stream only with a +# response. Mirrors LSP's RequestCancelled; not sent by the 2026 transports, where +# the spec forbids answering a cancelled request. See +# `StreamableHTTPServerTransport._terminate_unanswered_request`. +REQUEST_CANCELLED: Final = -32800 + +# Session ID validation pattern (visible ASCII characters ranging from 0x21 to 0x7E) +# Pattern ensures entire string contains only valid characters by using ^ and $ anchors +SESSION_ID_PATTERN = re.compile(r"^[\x21-\x7E]+$") + +# Type aliases +StreamId = str +EventId = str +# An SSE event-dict as accepted by sse-starlette (`event`, `data`, `id`, `retry`). +SSEEvent = dict[str, Any] + + +def check_accept_headers(request: Request) -> tuple[bool, bool]: + """Return (has_json, has_sse) for the request's Accept header, with RFC 7231 wildcard handling. + + Supports wildcard media types per RFC 7231, section 5.3.2: + - */* matches any media type + - application/* matches any application/ subtype + - text/* matches any text/ subtype + """ + accept_header = request.headers.get("accept", "") + accept_types = [media_type.strip().split(";")[0].strip().lower() for media_type in accept_header.split(",")] + + has_wildcard = "*/*" in accept_types + has_json = has_wildcard or any(t in (CONTENT_TYPE_JSON, "application/*") for t in accept_types) + has_sse = has_wildcard or any(t in (CONTENT_TYPE_SSE, "text/*") for t in accept_types) + + return has_json, has_sse + + +@dataclass +class EventMessage: + """A JSONRPCMessage with an optional event ID for stream resumability.""" + + message: JSONRPCMessage + event_id: str | None = None + + +EventCallback = Callable[[EventMessage], Awaitable[None]] + + +class EventStore(ABC): + """Interface for resumability support via event storage.""" + + @abstractmethod + async def store_event(self, stream_id: StreamId, message: JSONRPCMessage | None) -> EventId: + """Stores an event for later retrieval. + + Args: + stream_id: ID of the stream the event belongs to + message: The JSON-RPC message to store, or None for priming events + + Returns: + The generated event ID for the stored event. + """ + pass # pragma: no cover + + @abstractmethod + async def replay_events_after( + self, + last_event_id: EventId, + send_callback: EventCallback, + ) -> StreamId | None: + """Replays events that occurred after the specified event ID. + + Args: + last_event_id: The ID of the last event the client received + send_callback: A callback function to send events to the client + + Returns: + The stream ID of the replayed events, or None if no events were found. + """ + pass # pragma: no cover + + +class StreamableHTTPServerTransport: + """HTTP server transport with event streaming support for MCP. + + Handles JSON-RPC messages in HTTP POST requests with SSE streaming. + Supports optional JSON responses and session management. + """ + + # Server notification streams for POST requests as well as standalone SSE stream + _read_stream_writer: ContextSendStream[SessionMessage | Exception] | None = None + _read_stream: ContextReceiveStream[SessionMessage | Exception] | None = None + _write_stream: ContextSendStream[SessionMessage] | None = None + _write_stream_reader: ContextReceiveStream[SessionMessage] | None = None + _security: TransportSecurityMiddleware + + def __init__( + self, + mcp_session_id: str | None, + is_json_response_enabled: bool = False, + event_store: EventStore | None = None, + security_settings: TransportSecuritySettings | None = None, + retry_interval: int | None = None, + ) -> None: + """Initialize a new StreamableHTTP server transport. + + Args: + mcp_session_id: Optional session identifier for this connection. + Must contain only visible ASCII characters (0x21-0x7E). + is_json_response_enabled: If True, answer each request POST with a single + JSON body instead of an SSE stream, which removes + the request-scoped back-channel: a server-initiated + request tied to the call raises `NoBackChannelError` + and its notifications are dropped (see + `TransportContext.can_send_request`). Default is False. + event_store: Event store for resumability support. If provided, + resumability will be enabled, allowing clients to + reconnect and resume messages. + security_settings: Optional security settings for DNS rebinding protection. + retry_interval: Retry interval in milliseconds to suggest to clients in SSE + retry field. When set, the server will send a retry field in + SSE priming events to control client reconnection timing for + polling behavior. Only used when event_store is provided. + + Raises: + ValueError: If the session ID contains invalid characters. + """ + if mcp_session_id is not None and not SESSION_ID_PATTERN.fullmatch(mcp_session_id): + raise ValueError("Session ID must only contain visible ASCII characters (0x21-0x7E)") + + self.mcp_session_id = mcp_session_id + self.is_json_response_enabled = is_json_response_enabled + self._event_store = event_store + self._security = TransportSecurityMiddleware(security_settings) + self._retry_interval = retry_interval + self._request_streams: dict[ + RequestId, + tuple[ + MemoryObjectSendStream[EventMessage], + MemoryObjectReceiveStream[EventMessage], + ], + ] = {} + self._sse_stream_writers: dict[RequestId, MemoryObjectSendStream[SSEEvent]] = {} + self._terminated = False + # Idle timeout cancel scope; managed by the session manager. + self.idle_scope: anyio.CancelScope | None = None + + @property + def is_terminated(self) -> bool: + """Check if this transport has been explicitly terminated.""" + return self._terminated + + def _message_metadata( + self, + request: Request, + *, + close_sse_stream: CloseSSEStreamCallback | None = None, + close_standalone_sse_stream: CloseSSEStreamCallback | None = None, + on_request_unanswered: Callable[[], Awaitable[None]] | None = None, + ) -> ServerMessageMetadata: + """The metadata this transport frames every inbound message with. + + The one place `can_send_request` is stamped, so no construction site can + forget it: a JSON body carries only the response, so in JSON-response mode + the request-scoped channel cannot carry a server-initiated request (see + `TransportContext.can_send_request`). + """ + return ServerMessageMetadata( + request_context=request, + close_sse_stream=close_sse_stream, + close_standalone_sse_stream=close_standalone_sse_stream, + on_request_unanswered=on_request_unanswered, + can_send_request=not self.is_json_response_enabled, + ) + + def close_sse_stream(self, request_id: RequestId) -> None: + """Close SSE connection for a specific request without terminating the stream. + + This method closes the HTTP connection for the specified request, triggering + client reconnection. Events continue to be stored in the event store and will + be replayed when the client reconnects with Last-Event-ID. + + Use this to implement polling behavior during long-running operations - + the client will reconnect after the retry interval specified in the priming event. + + Args: + request_id: The request ID whose SSE stream should be closed. + + Note: + This is a no-op if there is no active stream for the request ID. + Requires event_store to be configured for events to be stored during + the disconnect. + """ + writer = self._sse_stream_writers.pop(request_id, None) + if writer: # pragma: no branch + writer.close() + + # Also close and remove request streams + if request_id in self._request_streams: # pragma: no branch + send_stream, receive_stream = self._request_streams.pop(request_id) + send_stream.close() + receive_stream.close() + + def close_standalone_sse_stream(self) -> None: + """Close the standalone GET SSE stream, triggering client reconnection. + + This method closes the HTTP connection for the standalone GET stream used + for unsolicited server-to-client notifications. The client SHOULD reconnect + with Last-Event-ID to resume receiving notifications. + + Use this to implement polling behavior for the notification stream - + the client will reconnect after the retry interval specified in the priming event. + + Note: + This is a no-op if there is no active standalone SSE stream. + Requires event_store to be configured for events to be stored during + the disconnect. + """ + self.close_sse_stream(GET_STREAM_KEY) + + def _create_session_message( + self, + message: JSONRPCRequest, + request: Request, + request_id: RequestId, + protocol_version: str, + ) -> SessionMessage: + """Create a session message with metadata including close_sse_stream callback. + + The close_sse_stream callbacks are only provided when the client supports + resumability (protocol version >= 2025-11-25). Old clients can't resume if + the stream is closed early because they didn't receive a priming event. + Every request carries `on_request_unanswered`, so a request that settles + without a response is still terminated on this era's wire. + """ + end_stream = partial(self._terminate_unanswered_request, message.id) + # Only provide close callbacks when client supports resumability + if self._event_store and is_version_at_least(protocol_version, "2025-11-25"): + + async def close_stream_callback() -> None: + self.close_sse_stream(request_id) + + async def close_standalone_stream_callback() -> None: + self.close_standalone_sse_stream() + + metadata = self._message_metadata( + request, + close_sse_stream=close_stream_callback, + close_standalone_sse_stream=close_standalone_stream_callback, + on_request_unanswered=end_stream, + ) + else: + metadata = self._message_metadata(request, on_request_unanswered=end_stream) + + return SessionMessage(message, metadata=metadata) + + async def _mint_priming_event(self, stream_id: StreamId, protocol_version: str) -> SSEEvent | None: + """Store the priming cursor for `stream_id` and return its SSE wire form. + + Called before the request is dispatched so the priming row precedes + anything `message_router` can store for this stream. Returns `None` + when no event store is configured or the client predates 2025-11-25 + (older clients cannot parse the empty-data event). + """ + if not self._event_store: + return None + if not is_version_at_least(protocol_version, "2025-11-25"): + return None + priming_event_id = await self._event_store.store_event(stream_id, None) + priming_event: SSEEvent = {"id": priming_event_id, "data": ""} + if self._retry_interval is not None: + priming_event["retry"] = self._retry_interval + return priming_event + + async def _run_sse_writer( + self, + request_id: RequestId, + sse_stream_writer: MemoryObjectSendStream[SSEEvent], + request_stream_reader: MemoryObjectReceiveStream[EventMessage], + priming_event: SSEEvent | None, + ) -> None: + """Forward `_request_streams[request_id]` onto the SSE wire for one POST.""" + try: + async with sse_stream_writer, request_stream_reader: + if priming_event is not None: + await sse_stream_writer.send(priming_event) + async for event_message in request_stream_reader: + await sse_stream_writer.send(self._create_event_data(event_message)) + if isinstance(event_message.message, JSONRPCResponse | JSONRPCError): + break + except anyio.ClosedResourceError: # pragma: lax no cover + logger.debug("SSE stream closed by close_sse_stream()") + except Exception: # pragma: lax no cover + logger.exception("Error in SSE writer") + finally: + logger.debug("Closing SSE writer") + self._sse_stream_writers.pop(request_id, None) + await self._clean_up_memory_streams(request_id) + + def _create_error_response( + self, + error_message: str, + status_code: HTTPStatus, + error_code: int = INVALID_REQUEST, + headers: dict[str, str] | None = None, + ) -> Response: + """Create an error response with a simple string message.""" + response_headers = {"Content-Type": CONTENT_TYPE_JSON} + if headers: + response_headers.update(headers) + + if self.mcp_session_id: + response_headers[MCP_SESSION_ID_HEADER] = self.mcp_session_id + + # Return a properly formatted JSON error response + error_response = JSONRPCError( + jsonrpc="2.0", + id=None, + error=ErrorData(code=error_code, message=error_message), + ) + + return Response( + error_response.model_dump_json(by_alias=True, exclude_unset=True), + status_code=status_code, + headers=response_headers, + ) + + def _create_json_response( + self, + response_message: JSONRPCMessage | None, + status_code: HTTPStatus = HTTPStatus.OK, + headers: dict[str, str] | None = None, + ) -> Response: + """Create a JSON response from a JSONRPCMessage.""" + response_headers = {"Content-Type": CONTENT_TYPE_JSON} + if headers: + response_headers.update(headers) # pragma: no cover + + if self.mcp_session_id: + response_headers[MCP_SESSION_ID_HEADER] = self.mcp_session_id + + return Response( + response_message.model_dump_json(by_alias=True, exclude_unset=True) if response_message else None, + status_code=status_code, + headers=response_headers, + ) + + def _get_session_id(self, request: Request) -> str | None: + """Extract the session ID from request headers.""" + return request.headers.get(MCP_SESSION_ID_HEADER) + + def _create_event_data(self, event_message: EventMessage) -> SSEEvent: + """Create event data dictionary from an EventMessage.""" + event_data = { + "event": "message", + "data": event_message.message.model_dump_json(by_alias=True, exclude_unset=True), + } + + # If an event ID was provided, include it + if event_message.event_id: + event_data["id"] = event_message.event_id + + return event_data + + async def _terminate_unanswered_request(self, request_id: RequestId) -> None: + """Terminate a request that settled without a response (e.g. cancelled). + + The 2025-era wire ends a request's stream only with a response for its + id - and stores that response so a resuming client's replay terminates + too - so this era answers a cancelled request with `REQUEST_CANCELLED` + where the dispatcher itself stays silent (the 2026 transports MUST NOT + answer). It is written through the same ordered channel as the request's + other messages, so it cannot overtake anything already queued for it. + """ + assert self._write_stream is not None # a dispatched request implies connect() ran + error = ErrorData(code=REQUEST_CANCELLED, message="Request cancelled") + await self._write_stream.send(SessionMessage(JSONRPCError(jsonrpc="2.0", id=request_id, error=error))) + + async def _clean_up_memory_streams(self, request_id: RequestId) -> None: + """Clean up memory streams for a given request ID.""" + if request_id in self._request_streams: # pragma: no branch + try: + # Close the request stream + await self._request_streams[request_id][0].aclose() + await self._request_streams[request_id][1].aclose() + except Exception: # pragma: no cover + # During cleanup, we catch all exceptions since streams might be in various states + logger.debug("Error closing memory streams - may already be closed") + finally: + # Remove the request stream from the mapping + self._request_streams.pop(request_id, None) + + async def handle_request(self, scope: Scope, receive: Receive, send: Send) -> None: + """Application entry point that handles all HTTP requests.""" + request = Request(scope, receive) + + # Validate request headers for DNS rebinding protection + is_post = request.method == "POST" + error_response = await self._security.validate_request(request, is_post=is_post) + if error_response: + await error_response(scope, receive, send) + return + + if self._terminated: + # If the session has been terminated, return 404 Not Found + response = self._create_error_response( + "Not Found: Session has been terminated", + HTTPStatus.NOT_FOUND, + ) + await response(scope, receive, send) + return + + if request.method == "POST": + await self._handle_post_request(scope, request, receive, send) + elif request.method == "GET": + await self._handle_get_request(request, send) + elif request.method == "DELETE": + await self._handle_delete_request(request, send) + else: + await self._handle_unsupported_request(request, send) + + def _check_content_type(self, request: Request) -> bool: + """Check if the request has the correct Content-Type.""" + content_type = request.headers.get("content-type", "") + content_type_parts = [part.strip() for part in content_type.split(";")[0].split(",")] + + return any(part == CONTENT_TYPE_JSON for part in content_type_parts) + + async def _validate_accept_header(self, request: Request, scope: Scope, send: Send) -> bool: + """Validate Accept header based on response mode. Returns True if valid.""" + has_json, has_sse = check_accept_headers(request) + if self.is_json_response_enabled: + # For JSON-only responses, only require application/json + if not has_json: + response = self._create_error_response( + "Not Acceptable: Client must accept application/json", + HTTPStatus.NOT_ACCEPTABLE, + ) + await response(scope, request.receive, send) + return False + # For SSE responses, require both content types + elif not (has_json and has_sse): + response = self._create_error_response( + "Not Acceptable: Client must accept both application/json and text/event-stream", + HTTPStatus.NOT_ACCEPTABLE, + ) + await response(scope, request.receive, send) + return False + return True + + async def _handle_post_request(self, scope: Scope, request: Request, receive: Receive, send: Send) -> None: + """Handle POST requests containing JSON-RPC messages.""" + writer = self._read_stream_writer + if writer is None: # pragma: no cover + raise ValueError("No read stream writer available. Ensure connect() is called first.") + try: + # Validate Accept header + if not await self._validate_accept_header(request, scope, send): + return + + # Validate Content-Type + if not self._check_content_type(request): # pragma: no cover + response = self._create_error_response( + "Unsupported Media Type: Content-Type must be application/json", + HTTPStatus.UNSUPPORTED_MEDIA_TYPE, + ) + await response(scope, receive, send) + return + + # Parse the body - only read it once + body = await request.body() + + try: + raw_message = pydantic_core.from_json(body) + except ValueError as e: + response = self._create_error_response(f"Parse error: {str(e)}", HTTPStatus.BAD_REQUEST, PARSE_ERROR) + await response(scope, receive, send) + return + + try: + message = jsonrpc_message_adapter.validate_python(raw_message, by_name=False) + except ValidationError as e: + response = self._create_error_response( + f"Validation error: {str(e)}", + HTTPStatus.BAD_REQUEST, + INVALID_PARAMS, + ) + await response(scope, receive, send) + return + + # Check if this is an initialization request + is_initialization_request = isinstance(message, JSONRPCRequest) and message.method == "initialize" + + if is_initialization_request: + # Check if the server already has an established session + if self.mcp_session_id: + # Check if request has a session ID + request_session_id = self._get_session_id(request) + + # If request has a session ID but doesn't match, return 404 + if request_session_id and request_session_id != self.mcp_session_id: # pragma: no cover + response = self._create_error_response( + "Not Found: Invalid or expired session ID", + HTTPStatus.NOT_FOUND, + ) + await response(scope, receive, send) + return + elif not await self._validate_request_headers(request, send): + return + + # For notifications and responses only, return 202 Accepted + if not isinstance(message, JSONRPCRequest): + # Create response object and send it + response = self._create_json_response( + None, + HTTPStatus.ACCEPTED, + ) + await response(scope, receive, send) + + # Process the message after sending the response + session_message = SessionMessage(message, metadata=self._message_metadata(request)) + await writer.send(session_message) + + return + + # Extract protocol version for priming event decision. + # For initialize requests, get from request params. + # For other requests, get from header (already validated). + protocol_version = ( + str(message.params.get("protocolVersion", DEFAULT_NEGOTIATED_VERSION)) + if is_initialization_request and message.params + else request.headers.get(MCP_PROTOCOL_VERSION_HEADER, DEFAULT_NEGOTIATED_VERSION) + ) + + request_id = str(message.id) + + if self.is_json_response_enabled: + self._request_streams[request_id] = anyio.create_memory_object_stream[EventMessage]( + REQUEST_STREAM_BUFFER_SIZE + ) + request_stream_reader = self._request_streams[request_id][1] + # Process the message + metadata = self._message_metadata( + request, on_request_unanswered=partial(self._terminate_unanswered_request, message.id) + ) + session_message = SessionMessage(message, metadata=metadata) + await writer.send(session_message) + try: + # `message_router` deposits only this request's own response + # here: anything else scoped to the request has no wire in + # JSON-response mode. + event_message = await request_stream_reader.receive() + except (anyio.EndOfStream, anyio.ClosedResourceError): + # The stream closed with no response: the session was + # terminated while this request was in flight. + logger.debug(f"Session terminated with request {request_id} in flight; no response to send") + response = self._create_error_response( + "Session terminated before the request completed", + HTTPStatus.INTERNAL_SERVER_ERROR, + INTERNAL_ERROR, + ) + else: + response = self._create_json_response(event_message.message) + finally: + await self._clean_up_memory_streams(request_id) + await response(scope, receive, send) + else: + # Mint the priming event before any per-request state exists: + # `EventStore.store_event` is user code and may raise, in which + # case the outer handler returns a 500 with nothing to clean up. + # Still strictly precedes dispatch, so storage order == wire order. + priming_event = await self._mint_priming_event(request_id, protocol_version) + + sse_stream_writer, sse_stream_reader = anyio.create_memory_object_stream[SSEEvent](0) + self._sse_stream_writers[request_id] = sse_stream_writer + self._request_streams[request_id] = anyio.create_memory_object_stream[EventMessage]( + REQUEST_STREAM_BUFFER_SIZE + ) + request_stream_reader = self._request_streams[request_id][1] + + headers = { + "Cache-Control": "no-cache, no-transform", + "Connection": "keep-alive", + "Content-Type": CONTENT_TYPE_SSE, + **({MCP_SESSION_ID_HEADER: self.mcp_session_id} if self.mcp_session_id else {}), + } + response = EventSourceResponse( + content=sse_stream_reader, + data_sender_callable=partial( + self._run_sse_writer, request_id, sse_stream_writer, request_stream_reader, priming_event + ), + headers=headers, + ) + + # Start the SSE response (this will send headers immediately) + try: + # First send the response to establish the SSE connection + async with anyio.create_task_group() as tg: + tg.start_soon(response, scope, receive, send) + # Then send the message to be processed by the server + session_message = self._create_session_message(message, request, request_id, protocol_version) + await writer.send(session_message) + except Exception: # pragma: lax no cover + logger.exception("SSE response error") + await sse_stream_writer.aclose() + await self._clean_up_memory_streams(request_id) + finally: + await sse_stream_reader.aclose() + + except Exception as err: + logger.exception("Error handling POST request") + response = self._create_error_response( + "Error handling POST request", + HTTPStatus.INTERNAL_SERVER_ERROR, + INTERNAL_ERROR, + ) + await response(scope, receive, send) + await writer.send(Exception(err)) + return + + async def _handle_get_request(self, request: Request, send: Send) -> None: + """Handle GET request to establish SSE. + + This allows the server to communicate to the client without the client + first sending data via HTTP POST. The server can send JSON-RPC requests + and notifications on this stream. + """ + writer = self._read_stream_writer + if writer is None: # pragma: no cover + raise ValueError("No read stream writer available. Ensure connect() is called first.") + + # Validate Accept header - must include text/event-stream + _, has_sse = check_accept_headers(request) + + if not has_sse: + response = self._create_error_response( + "Not Acceptable: Client must accept text/event-stream", + HTTPStatus.NOT_ACCEPTABLE, + ) + await response(request.scope, request.receive, send) + return + + if not await self._validate_request_headers(request, send): + return + + # Handle resumability: check for Last-Event-ID header + if last_event_id := request.headers.get(LAST_EVENT_ID_HEADER): + await self._replay_events(last_event_id, request, send) + return + + headers = { + "Cache-Control": "no-cache, no-transform", + "Connection": "keep-alive", + "Content-Type": CONTENT_TYPE_SSE, + } + + if self.mcp_session_id: # pragma: no branch + headers[MCP_SESSION_ID_HEADER] = self.mcp_session_id + + # Check if we already have an active GET stream + if GET_STREAM_KEY in self._request_streams: + response = self._create_error_response( + "Conflict: Only one SSE stream is allowed per session", + HTTPStatus.CONFLICT, + ) + await response(request.scope, request.receive, send) + return + + # Create SSE stream + sse_stream_writer, sse_stream_reader = anyio.create_memory_object_stream[SSEEvent](0) + + async def standalone_sse_writer(): + try: + # Create a standalone message stream for server-initiated messages + + self._request_streams[GET_STREAM_KEY] = anyio.create_memory_object_stream[EventMessage]( + REQUEST_STREAM_BUFFER_SIZE + ) + standalone_stream_reader = self._request_streams[GET_STREAM_KEY][1] + + async with sse_stream_writer, standalone_stream_reader: + # Process messages from the standalone stream + async for event_message in standalone_stream_reader: + # For the standalone stream, we handle: + # - JSONRPCNotification (server sends notifications to client) + # - JSONRPCRequest (server sends requests to client) + # We should NOT receive JSONRPCResponse + + # Send the message via SSE + event_data = self._create_event_data(event_message) + await sse_stream_writer.send(event_data) + except anyio.ClosedResourceError: + # Session teardown can close the stream while the writer is between dequeues. + pass + except Exception: + logger.exception("Error in standalone SSE writer") # pragma: no cover + finally: + logger.debug("Closing standalone SSE writer") + await self._clean_up_memory_streams(GET_STREAM_KEY) + + # Create and start EventSourceResponse + response = EventSourceResponse( + content=sse_stream_reader, + data_sender_callable=standalone_sse_writer, + headers=headers, + ) + + try: + # This will send headers immediately and establish the SSE connection + await response(request.scope, request.receive, send) + except Exception: # pragma: lax no cover + logger.exception("Error in standalone SSE response") + await self._clean_up_memory_streams(GET_STREAM_KEY) + finally: + await sse_stream_writer.aclose() + await sse_stream_reader.aclose() + + async def _handle_delete_request(self, request: Request, send: Send) -> None: + """Handle DELETE requests for explicit session termination.""" + # Validate session ID + if not self.mcp_session_id: # pragma: no cover + # If no session ID set, return Method Not Allowed + response = self._create_error_response( + "Method Not Allowed: Session termination not supported", + HTTPStatus.METHOD_NOT_ALLOWED, + ) + await response(request.scope, request.receive, send) + return + + if not await self._validate_request_headers(request, send): # pragma: no cover + return + + await self.terminate() + + response = self._create_json_response( + None, + HTTPStatus.OK, + ) + await response(request.scope, request.receive, send) + + async def terminate(self) -> None: + """Terminate the current session, closing all streams. + + Once terminated, all requests with this session ID will receive 404 Not Found. + """ + + self._terminated = True + logger.info(f"Terminating session: {self.mcp_session_id}") + + # We need a copy of the keys to avoid modification during iteration + request_stream_keys = list(self._request_streams.keys()) + + # Close all request streams asynchronously + for key in request_stream_keys: + await self._clean_up_memory_streams(key) + + # Clear the request streams dictionary immediately + self._request_streams.clear() + try: + if self._read_stream_writer is not None: # pragma: no branch + await self._read_stream_writer.aclose() + if self._read_stream is not None: # pragma: no branch + await self._read_stream.aclose() + if self._write_stream_reader is not None: # pragma: no branch + await self._write_stream_reader.aclose() + if self._write_stream is not None: # pragma: no branch + await self._write_stream.aclose() + except Exception as e: # pragma: no cover + # During cleanup, we catch all exceptions since streams might be in various states + logger.debug(f"Error closing streams: {e}") + + async def _handle_unsupported_request(self, request: Request, send: Send) -> None: + """Handle unsupported HTTP methods.""" + headers = { + "Content-Type": CONTENT_TYPE_JSON, + "Allow": "GET, POST, DELETE", + } + if self.mcp_session_id: # pragma: no branch + headers[MCP_SESSION_ID_HEADER] = self.mcp_session_id + + response = self._create_error_response( + "Method Not Allowed", + HTTPStatus.METHOD_NOT_ALLOWED, + headers=headers, + ) + await response(request.scope, request.receive, send) + + async def _validate_request_headers(self, request: Request, send: Send) -> bool: + # Protocol-version validation lives in the manager's era-routing: only + # values in `HANDSHAKE_PROTOCOL_VERSIONS` (or no header at all) reach + # this transport, so the legacy version-gate is gone. + return await self._validate_session(request, send) + + async def _validate_session(self, request: Request, send: Send) -> bool: + """Validate the session ID in the request.""" + if not self.mcp_session_id: + # If we're not using session IDs, return True + return True + + # Get the session ID from the request headers + request_session_id = self._get_session_id(request) + + # If no session ID provided but required, return error + if not request_session_id: + response = self._create_error_response( + "Bad Request: Missing session ID", + HTTPStatus.BAD_REQUEST, + ) + await response(request.scope, request.receive, send) + return False + + # If session ID doesn't match, return error + if request_session_id != self.mcp_session_id: # pragma: no cover + response = self._create_error_response( + "Not Found: Invalid or expired session ID", + HTTPStatus.NOT_FOUND, + ) + await response(request.scope, request.receive, send) + return False + + return True + + async def _replay_events(self, last_event_id: str, request: Request, send: Send) -> None: + """Replays events that would have been sent after the specified event ID. + + Only used when resumability is enabled. + """ + event_store = self._event_store + if not event_store: + return # pragma: no cover + + try: + headers = { + "Cache-Control": "no-cache, no-transform", + "Connection": "keep-alive", + "Content-Type": CONTENT_TYPE_SSE, + } + + if self.mcp_session_id: # pragma: no branch + headers[MCP_SESSION_ID_HEADER] = self.mcp_session_id + + # The manager only routes supported (or absent) header values to this transport + replay_protocol_version = request.headers.get(MCP_PROTOCOL_VERSION_HEADER, DEFAULT_NEGOTIATED_VERSION) + + # Create SSE stream for replay + sse_stream_writer, sse_stream_reader = anyio.create_memory_object_stream[SSEEvent](0) + + async def replay_sender(): + try: + async with sse_stream_writer: + # Define an async callback for sending events + async def send_event(event_message: EventMessage) -> None: + event_data = self._create_event_data(event_message) + await sse_stream_writer.send(event_data) + + # Replay past events and get the stream ID + stream_id = await event_store.replay_events_after(last_event_id, send_event) + + # If stream ID not in mapping, create it + if stream_id and stream_id not in self._request_streams: # pragma: no branch + try: + # Register SSE writer so close_sse_stream() can close it + self._sse_stream_writers[stream_id] = sse_stream_writer + + # Prime the resumed connection so the client sees the stream + # is re-registered. The replay→live-tail ordering window here + # is pre-existing and tracked separately. + priming_event = await self._mint_priming_event(stream_id, replay_protocol_version) + if priming_event is not None: + await sse_stream_writer.send(priming_event) + + # Create new request streams for this connection + self._request_streams[stream_id] = anyio.create_memory_object_stream[EventMessage]( + REQUEST_STREAM_BUFFER_SIZE + ) + msg_reader = self._request_streams[stream_id][1] + + # Forward messages to SSE + async with msg_reader: + async for event_message in msg_reader: + event_data = self._create_event_data(event_message) + + await sse_stream_writer.send(event_data) + finally: + self._sse_stream_writers.pop(stream_id, None) + await self._clean_up_memory_streams(stream_id) + except anyio.ClosedResourceError: # pragma: lax no cover + # Expected when close_sse_stream() is called + logger.debug("Replay SSE stream closed by close_sse_stream()") + except Exception: # pragma: lax no cover + logger.exception("Error in replay sender") + + # Create and start EventSourceResponse + response = EventSourceResponse( + content=sse_stream_reader, + data_sender_callable=replay_sender, + headers=headers, + ) + + try: + await response(request.scope, request.receive, send) + except Exception: # pragma: lax no cover + logger.exception("Error in replay response") + finally: + await sse_stream_writer.aclose() + await sse_stream_reader.aclose() + + except Exception: # pragma: lax no cover + logger.exception("Error replaying events") + response = self._create_error_response( + "Error replaying events", + HTTPStatus.INTERNAL_SERVER_ERROR, + INTERNAL_ERROR, + ) + await response(request.scope, request.receive, send) + + @asynccontextmanager + async def connect( + self, + ) -> AsyncGenerator[ + tuple[ + ReadStream[SessionMessage | Exception], + WriteStream[SessionMessage], + ], + None, + ]: + """Context manager that provides read and write streams for a connection. + + Yields: + Tuple of (read_stream, write_stream) for bidirectional communication + """ + + # Create the memory streams for this connection + + read_stream_writer, read_stream = create_context_streams[SessionMessage | Exception](0) + write_stream, write_stream_reader = create_context_streams[SessionMessage](0) + + # Store the streams + self._read_stream_writer = read_stream_writer + self._read_stream = read_stream + self._write_stream_reader = write_stream_reader + self._write_stream = write_stream + + # Start a task group for message routing + async with anyio.create_task_group() as tg: + # Create a message router that distributes messages to request streams + async def message_router(): + try: + async for session_message in write_stream_reader: # pragma: no branch + # Determine which request stream(s) should receive this message + message = session_message.message + target_request_id = None + # Check if this is a response with a known request id. + # Null-id errors (e.g., parse errors) fall through to + # the GET stream since they can't be correlated. + if isinstance(message, JSONRPCResponse | JSONRPCError) and message.id is not None: + target_request_id = str(message.id) + # Extract related_request_id from meta if it exists + elif ( + session_message.metadata is not None + and isinstance( + session_message.metadata, + ServerMessageMetadata, + ) + and session_message.metadata.related_request_id is not None + ): + related_request_id = session_message.metadata.related_request_id + if self.is_json_response_enabled: + # A JSON body carries only the response: this message + # has no wire form (nor a replay), so drop it before + # storing or queueing rather than park it (#1764). + logger.debug(f"Dropped message related to request {related_request_id} in JSON mode") + continue + target_request_id = str(related_request_id) + + request_stream_id = target_request_id if target_request_id is not None else GET_STREAM_KEY + + # Store the event if we have an event store, + # regardless of whether a client is connected + # messages will be replayed on the re-connect + event_id = None + if self._event_store: + event_id = await self._event_store.store_event(request_stream_id, message) + logger.debug(f"Stored {event_id} from {request_stream_id}") + + if request_stream_id in self._request_streams: + try: + # Send both the message and the event ID + await self._request_streams[request_stream_id][0].send(EventMessage(message, event_id)) + except (anyio.BrokenResourceError, anyio.ClosedResourceError): # pragma: no cover + # Stream might be closed, remove from registry + self._request_streams.pop(request_stream_id, None) + else: + logger.debug( + f"""Request stream {request_stream_id} not found + for message. Still processing message as the client + might reconnect and replay.""" + ) + except anyio.ClosedResourceError: + if self._terminated: # pragma: lax no cover + logger.debug("Read stream closed by client") + else: + logger.exception("Unexpected closure of read stream in message router") + except Exception: # pragma: lax no cover + logger.exception("Error in message router") + + # Start the message router + tg.start_soon(message_router) + + try: + # Yield the streams for the caller to use + yield read_stream, write_stream + finally: + for stream_id in list(self._request_streams.keys()): + await self._clean_up_memory_streams(stream_id) + self._request_streams.clear() + + # Clean up the read and write streams + try: + await read_stream_writer.aclose() + await read_stream.aclose() + await write_stream_reader.aclose() + await write_stream.aclose() + except Exception as e: # pragma: no cover + # During cleanup, we catch all exceptions since streams might be in various states + logger.debug(f"Error closing streams: {e}") diff --git a/venv/lib/python3.12/site-packages/mcp/server/streamable_http_manager.py b/venv/lib/python3.12/site-packages/mcp/server/streamable_http_manager.py new file mode 100644 index 0000000..e9a7d96 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/streamable_http_manager.py @@ -0,0 +1,378 @@ +"""StreamableHTTP Session Manager for MCP servers.""" + +from __future__ import annotations + +import contextlib +import logging +from collections.abc import AsyncIterator +from typing import TYPE_CHECKING, Any +from uuid import uuid4 + +import anyio +from anyio.abc import TaskStatus +from mcp_types import DEFAULT_NEGOTIATED_VERSION, INVALID_REQUEST, ErrorData, JSONRPCError +from mcp_types.version import HANDSHAKE_PROTOCOL_VERSIONS +from starlette.requests import Request +from starlette.responses import Response +from starlette.types import Receive, Scope, Send + +from mcp.server._streamable_http_modern import handle_modern_request +from mcp.server.auth.middleware.bearer_auth import AuthenticatedUser, AuthorizationContext, authorization_context +from mcp.server.connection import Connection +from mcp.server.runner import serve_connection, serve_loop +from mcp.server.streamable_http import MCP_SESSION_ID_HEADER, EventStore, StreamableHTTPServerTransport +from mcp.server.transport_security import DEFAULT_MAX_REQUEST_BODY_SIZE as DEFAULT_MAX_REQUEST_BODY_SIZE +from mcp.server.transport_security import RequestBodyLimitMiddleware as RequestBodyLimitMiddleware +from mcp.server.transport_security import TransportSecuritySettings +from mcp.shared._compat import resync_tracer +from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER +from mcp.shared.jsonrpc_dispatcher import JSONRPCDispatcher +from mcp.shared.transport_context import TransportContext + +if TYPE_CHECKING: + from mcp.server.lowlevel.server import Server + +logger = logging.getLogger(__name__) + + +class StreamableHTTPSessionManager: + """Manages StreamableHTTP sessions with optional resumability via event store. + + This class abstracts away the complexity of session management, event storage, + and request handling for StreamableHTTP transports. It handles: + + 1. Session tracking for clients + 2. Resumability via an optional event store + 3. Connection management and lifecycle + 4. Request handling and transport setup + 5. Idle session cleanup via optional timeout + + Important: Only one StreamableHTTPSessionManager instance should be created + per application. The instance cannot be reused after its run() context has + completed. If you need to restart the manager, create a new instance. + + Args: + app: The MCP server instance + event_store: Optional event store for resumability support. If provided, enables resumable connections + where clients can reconnect and receive missed events. If None, sessions are still tracked but not + resumable. + json_response: Whether to use JSON responses instead of SSE streams + stateless: If True, creates a completely fresh transport for each request with no session tracking or + state persistence between requests. + security_settings: Optional transport security settings. + retry_interval: Retry interval in milliseconds to suggest to clients in SSE retry field. Used for SSE + polling behavior. + session_idle_timeout: Optional idle timeout in seconds for stateful sessions. If set, sessions that + receive no HTTP requests for this duration will be automatically terminated and removed. When + retry_interval is also configured, ensure the idle timeout comfortably exceeds the retry interval to + avoid reaping sessions during normal SSE polling gaps. Default is None (no timeout). A value of 1800 + (30 minutes) is recommended for most deployments. + max_request_body_size: Maximum size in bytes for Streamable HTTP request bodies. Requests that + exceed this limit receive a 413 response before parsing or session creation. Defaults to 4 MiB. + """ + + def __init__( + self, + app: Server[Any], + event_store: EventStore | None = None, + json_response: bool = False, + stateless: bool = False, + security_settings: TransportSecuritySettings | None = None, + retry_interval: int | None = None, + session_idle_timeout: float | None = None, + max_request_body_size: int = DEFAULT_MAX_REQUEST_BODY_SIZE, + ): + if session_idle_timeout is not None and session_idle_timeout <= 0: + raise ValueError("session_idle_timeout must be a positive number of seconds") + if stateless and session_idle_timeout is not None: + raise RuntimeError("session_idle_timeout is not supported in stateless mode") + if max_request_body_size <= 0: + raise ValueError("max_request_body_size must be a positive number of bytes") + + self.app = app + self.event_store = event_store + self.json_response = json_response + self.stateless = stateless + self.security_settings = security_settings + self.retry_interval = retry_interval + self.session_idle_timeout = session_idle_timeout + self.max_request_body_size = max_request_body_size + self.asgi_app = RequestBodyLimitMiddleware(self._handle_request, max_request_body_size) + + # Session tracking (only used if not stateless) + self._session_creation_lock = anyio.Lock() + self._server_instances: dict[str, StreamableHTTPServerTransport] = {} + # Identity of the credential that created each session; requests for a + # session must present the same credential. + self._session_owners: dict[str, AuthorizationContext] = {} + + # The task group and lifespan state are set during run() + self._task_group = None + self._lifespan_state: Any = None + # Thread-safe tracking of run() calls + self._run_lock = anyio.Lock() + self._has_started = False + + @contextlib.asynccontextmanager + async def run(self) -> AsyncIterator[None]: + """Run the session manager with proper lifecycle management. + + This creates and manages the task group for all session operations. + + Important: This method can only be called once per instance. The same + StreamableHTTPSessionManager instance cannot be reused after this + context manager exits. Create a new instance if you need to restart. + + Use this in the lifespan context manager of your Starlette app: + + @contextlib.asynccontextmanager + async def lifespan(app: Starlette) -> AsyncIterator[None]: + async with session_manager.run(): + yield + """ + # Thread-safe check to ensure run() is only called once + async with self._run_lock: + if self._has_started: + raise RuntimeError( + "StreamableHTTPSessionManager .run() can only be called " + "once per instance. Create a new instance if you need to run again." + ) + self._has_started = True + + async with self.app.lifespan(self.app) as lifespan_state, anyio.create_task_group() as tg: + # Store for handle_request: lifespan is entered once for the + # manager's lifetime, not per request (per-connection cleanup + # belongs on `connection.exit_stack`). + self._lifespan_state = lifespan_state + self._task_group = tg + logger.info("StreamableHTTP session manager started") + try: + yield # Let the application run + finally: + logger.info("StreamableHTTP session manager shutting down") + # Cancel task group to stop all spawned tasks + tg.cancel_scope.cancel() + self._task_group = None + self._lifespan_state = None + # Clear any remaining server instances + self._server_instances.clear() + self._session_owners.clear() + await resync_tracer() + + async def handle_request(self, scope: Scope, receive: Receive, send: Send) -> None: + """Process ASGI request with proper session handling and transport setup. + + Dispatches to the appropriate handler based on stateless mode. + """ + await self.asgi_app(scope, receive, send) + + async def _handle_request(self, scope: Scope, receive: Receive, send: Send) -> None: + if self._task_group is None: + raise RuntimeError("Task group is not initialized. Make sure to use run().") + + # TODO(L49): header-only era-routing for now; body-primary classification + # is a follow-up. The legacy paths below own only the known + # initialize-handshake versions; anything else (including unknown + # values) goes to the modern entry so the classifier can validate it + # and return a structured rejection. 2025 paths below remain unchanged. + header = MCP_PROTOCOL_VERSION_HEADER.encode("ascii") + pv = next((v.decode("latin-1") for k, v in scope["headers"] if k == header), None) + if pv is not None and pv not in HANDSHAKE_PROTOCOL_VERSIONS: + await handle_modern_request( + self.app, self.security_settings, self.json_response, self._lifespan_state, scope, receive, send + ) + return + + # Dispatch to the appropriate handler + if self.stateless: + await self._handle_stateless_request(pv, scope, receive, send) + else: + await self._handle_stateful_request(scope, receive, send) + + async def _handle_stateless_request( + self, protocol_version_hint: str | None, scope: Scope, receive: Receive, send: Send + ) -> None: + """Process request in stateless mode - creating a new transport for each request.""" + logger.debug("Stateless mode: Creating new transport for this request") + # No session ID needed in stateless mode + http_transport = StreamableHTTPServerTransport( + mcp_session_id=None, # No session tracking in stateless mode + is_json_response_enabled=self.json_response, + event_store=None, # No event store in stateless mode + security_settings=self.security_settings, + ) + + # Start server in a new task + async def run_stateless_server(*, task_status: TaskStatus[None] = anyio.TASK_STATUS_IGNORED): + async with http_transport.connect() as streams: + read_stream, write_stream = streams + task_status.started() + dispatcher: JSONRPCDispatcher[TransportContext] = JSONRPCDispatcher( + read_stream, + write_stream, + inline_methods=frozenset({"initialize"}), + # No session ID means a server-to-client request can be + # written to this POST's response stream, but the client's + # reply has nowhere to land — `can_send_request=False` + # makes the per-request channel raise `NoBackChannelError` + # for requests while still allowing notifications. + transport_builder=lambda _md: TransportContext(kind="streamable-http", can_send_request=False), + ) + # Born-ready, no standalone channel: the legacy stateless path + # never opens a GET stream and need not see `initialize`. The + # header (or the spec's default-absent value) seeds + # `ctx.protocol_version`. + connection = Connection.from_envelope( + protocol_version_hint if protocol_version_hint is not None else DEFAULT_NEGOTIATED_VERSION, + None, + None, + ) + try: + await serve_connection( + self.app, dispatcher, connection=connection, lifespan_state=self._lifespan_state + ) + except Exception: # pragma: lax no cover + logger.exception("Stateless session crashed") + + # Assert task group is not None for type checking + assert self._task_group is not None + # Start the server task + await self._task_group.start(run_stateless_server) + + # Handle the HTTP request and return the response + await http_transport.handle_request(scope, receive, send) + + # Terminate the transport after the request is handled + await http_transport.terminate() + + async def _handle_stateful_request(self, scope: Scope, receive: Receive, send: Send) -> None: + """Process request in stateful mode - maintaining session state between requests.""" + request = Request(scope, receive) + request_mcp_session_id = request.headers.get(MCP_SESSION_ID_HEADER) + + user = scope.get("user") + requestor = authorization_context(user) if isinstance(user, AuthenticatedUser) else None + + # Existing session case + if request_mcp_session_id is not None and request_mcp_session_id in self._server_instances: + transport = self._server_instances[request_mcp_session_id] + if requestor != self._session_owners.get(request_mcp_session_id): + # A session can only be used with the credential that created + # it. Respond exactly as if the session did not exist. + logger.warning( + "Rejecting request for session %s: credential does not match the one that created the session", + request_mcp_session_id[:64], + ) + body = JSONRPCError( + jsonrpc="2.0", id=None, error=ErrorData(code=INVALID_REQUEST, message="Session not found") + ) + response = Response( + body.model_dump_json(by_alias=True, exclude_unset=True), + status_code=404, + media_type="application/json", + ) + await response(scope, receive, send) + return + logger.debug("Session already exists, handling request directly") + # Push back idle deadline on activity + if transport.idle_scope is not None and self.session_idle_timeout is not None: + transport.idle_scope.deadline = anyio.current_time() + self.session_idle_timeout # pragma: no cover + await transport.handle_request(scope, receive, send) + return + + if request_mcp_session_id is None: + # New session case + logger.debug("Creating new transport") + async with self._session_creation_lock: + new_session_id = uuid4().hex + http_transport = StreamableHTTPServerTransport( + mcp_session_id=new_session_id, + is_json_response_enabled=self.json_response, + event_store=self.event_store, # May be None (no resumability) + security_settings=self.security_settings, + retry_interval=self.retry_interval, + ) + + assert http_transport.mcp_session_id is not None + if requestor is not None: + self._session_owners[http_transport.mcp_session_id] = requestor + self._server_instances[http_transport.mcp_session_id] = http_transport + logger.info(f"Created new transport with session ID: {new_session_id}") + + # Define the server runner + async def run_server(*, task_status: TaskStatus[None] = anyio.TASK_STATUS_IGNORED) -> None: + async with http_transport.connect() as streams: + read_stream, write_stream = streams + task_status.started() + try: + # Use a cancel scope for idle timeout — when the + # deadline passes the scope cancels the loop and + # execution continues after the ``with`` block. + # Incoming requests push the deadline forward. + idle_scope = anyio.CancelScope() + if self.session_idle_timeout is not None: + idle_scope.deadline = anyio.current_time() + self.session_idle_timeout + http_transport.idle_scope = idle_scope + + with idle_scope: + # Drive via `serve_loop` (not `Server.run()`) so the + # manager's already-entered lifespan is reused + # rather than re-entered per session. + await serve_loop( + self.app, + read_stream, + write_stream, + lifespan_state=self._lifespan_state, + session_id=http_transport.mcp_session_id, + ) + + if idle_scope.cancelled_caught: + assert http_transport.mcp_session_id is not None + logger.info(f"Session {http_transport.mcp_session_id} idle timeout") + self._server_instances.pop(http_transport.mcp_session_id, None) + self._session_owners.pop(http_transport.mcp_session_id, None) + await http_transport.terminate() + except Exception: + logger.exception(f"Session {http_transport.mcp_session_id} crashed") + finally: + if ( # pragma: no branch + http_transport.mcp_session_id + and http_transport.mcp_session_id in self._server_instances + and not http_transport.is_terminated + ): + logger.info( + "Cleaning up crashed session " + f"{http_transport.mcp_session_id} from active instances." + ) + del self._server_instances[http_transport.mcp_session_id] + self._session_owners.pop(http_transport.mcp_session_id, None) + + # Assert task group is not None for type checking + assert self._task_group is not None + # Start the server task + await self._task_group.start(run_server) + + # Handle the HTTP request and return the response + await http_transport.handle_request(scope, receive, send) + else: + # Unknown or expired session ID - return 404 per MCP spec + # TODO(L62): Align error code once spec clarifies + # See: https://github.com/modelcontextprotocol/python-sdk/issues/1821 + logger.info(f"Rejected request with unknown or expired session ID: {request_mcp_session_id[:64]}") + body = JSONRPCError( + jsonrpc="2.0", id=None, error=ErrorData(code=INVALID_REQUEST, message="Session not found") + ) + response = Response( + body.model_dump_json(by_alias=True, exclude_unset=True), status_code=404, media_type="application/json" + ) + await response(scope, receive, send) + + +class StreamableHTTPASGIApp: + """ASGI application for Streamable HTTP server transport.""" + + def __init__(self, session_manager: StreamableHTTPSessionManager): + self.session_manager = session_manager + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + await self.session_manager.asgi_app(scope, receive, send) diff --git a/venv/lib/python3.12/site-packages/mcp/server/subscriptions.py b/venv/lib/python3.12/site-packages/mcp/server/subscriptions.py new file mode 100644 index 0000000..f5d3b5b --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/subscriptions.py @@ -0,0 +1,254 @@ +"""Server-side `subscriptions/listen` support (2026-07-28, SEP-2575). + +On the 2026-07-28 wire there is no standing GET stream: a client opts in to +server events by sending a `subscriptions/listen` request whose response IS +the stream. This module provides the two pieces a server needs: + +- `SubscriptionBus`: the pluggable fan-out seam. The bus carries typed `ServerEvent` + values, not wire notifications - the listen handler owns subscription-id + stamping and per-stream filtering, so a custom bus (e.g. backed by Redis + pub/sub for multi-replica deployments) never sees JSON-RPC. The in-process + default is `InMemorySubscriptionBus`. +- `ListenHandler`: the request handler that serves `subscriptions/listen`. + `MCPServer` registers one automatically; lowlevel `Server` users pass an + instance as `on_subscriptions_listen=`. + +The event vocabulary lives in `mcp.shared.subscriptions`, shared with the client driver, and is re-exported here. + +Per the spec, the handler acknowledges first (the ack is the first frame on +the stream), tags every frame with the listen request's JSON-RPC id under +`_meta["io.modelcontextprotocol/subscriptionId"]`, and never delivers an +event kind the client did not request. Delivery is fire-and-forget with no +replay: a dropped stream is not resumable - clients re-listen and refetch. +""" + +from __future__ import annotations + +import logging +from collections.abc import Callable +from typing import Any, Protocol + +import anyio +import anyio.lowlevel +import anyio.streams.memory +from mcp_types import ( + INTERNAL_ERROR, + INVALID_REQUEST, + SubscriptionFilter, + SubscriptionsAcknowledgedNotification, + SubscriptionsAcknowledgedNotificationParams, + SubscriptionsListenRequestParams, + SubscriptionsListenResult, +) + +from mcp.server.context import ServerRequestContext +from mcp.shared.exceptions import MCPError +from mcp.shared.subscriptions import ( + SUBSCRIPTION_ID_META_KEY, + PromptsListChanged, + ResourcesListChanged, + ResourceUpdated, + ServerEvent, + ToolsListChanged, + event_matches, + event_to_notification, +) + +__all__ = [ + "SUBSCRIPTION_ID_META_KEY", + "InMemorySubscriptionBus", + "ListenHandler", + "PromptsListChanged", + "ResourceUpdated", + "ResourcesListChanged", + "ServerEvent", + "SubscriptionBus", + "ToolsListChanged", +] + +logger = logging.getLogger(__name__) + + +class SubscriptionBus(Protocol): + """Fan-out seam between event publishers and open listen streams. + + Implement this over an external pub/sub backend (Redis, NATS, ...) to fan + events out across replicas: `publish` forwards the event to the backend, + and each replica's bus invokes its local listeners for events arriving + from the backend. The same instance can be shared across servers. + + `publish` is async so backend implementations can do network I/O. + `subscribe` is synchronous local registration. Listeners are synchronous, + must not raise, and are invoked on the server's event loop. + """ + + async def publish(self, event: ServerEvent) -> None: + """Deliver `event` to every subscribed listener.""" + ... + + def subscribe(self, listener: Callable[[ServerEvent], None]) -> Callable[[], None]: + """Register `listener` and return an idempotent unsubscribe callable.""" + ... + + +class InMemorySubscriptionBus: + """In-process `SubscriptionBus`: synchronous fan-out to listeners in subscription order.""" + + def __init__(self) -> None: + # Keyed by a per-subscription token so the same callable can be + # registered more than once (bound methods compare equal). + self._listeners: dict[object, Callable[[ServerEvent], None]] = {} + + async def publish(self, event: ServerEvent) -> None: + """Deliver `event` to every subscribed listener. + + A raising listener is logged and skipped: one bad listener must not + starve the others or fail the publishing handler. Ends with a + checkpoint so a burst of publishes from one task lets listen streams + drain between events instead of overflowing their buffers unread. + """ + for listener in list(self._listeners.values()): + try: + listener(event) + except Exception: # fan-out boundary: isolate listeners from each other + logger.exception("subscription listener raised; continuing") + await anyio.lowlevel.checkpoint() + + def subscribe(self, listener: Callable[[ServerEvent], None]) -> Callable[[], None]: + """Register `listener` and return an idempotent unsubscribe callable.""" + token = object() + self._listeners[token] = listener + + def unsubscribe() -> None: + self._listeners.pop(token, None) + + return unsubscribe + + +def _safe_unsubscribe(unsubscribe: Callable[[], None]) -> None: + """Run a bus's unsubscribe callable, isolating the stream from it raising. + + The callable comes from a custom `SubscriptionBus`; a raising one is + logged and skipped so it cannot stop the stream's own cleanup from + releasing its subscription slot. + """ + try: + unsubscribe() + except Exception: # fan-out boundary: a raising bus must not skip stream cleanup + logger.exception("bus unsubscribe raised; continuing stream cleanup") + + +def _honored_subset(requested: SubscriptionFilter) -> SubscriptionFilter: + """The subset of `requested` the server will deliver, for the ack. + + Every requested kind is honored - whether an event kind ever fires + depends on what the server publishes, exactly as a subscription to a + nonexistent resource URI is honored and never fires. Non-true flags and + an empty URI list are dropped rather than echoed as falsy values. + """ + return SubscriptionFilter( + tools_list_changed=True if requested.tools_list_changed else None, + prompts_list_changed=True if requested.prompts_list_changed else None, + resources_list_changed=True if requested.resources_list_changed else None, + resource_subscriptions=list(requested.resource_subscriptions) if requested.resource_subscriptions else None, + ) + + +class ListenHandler: + """Serves `subscriptions/listen`: one call is one subscription stream. + + Register on a lowlevel `Server` via `on_subscriptions_listen=` (or + `add_request_handler`); `MCPServer` does so automatically. Each call + acknowledges the honored filter first, then forwards matching bus events + onto the request's response stream until the client disconnects (which + cancels the handler; the stream just ends, per the spec's abrupt-close + contract) or `close` ends all streams gracefully. + + Served on any transport that can carry the request's response stream: + streamable HTTP's SSE mode, or a duplex stream pair such as stdio. + + `max_subscriptions` bounds concurrent streams (further listen requests are + rejected with `INTERNAL_ERROR`, before the ack). `max_buffered_events` + bounds each stream's event backlog: a stream whose client has stopped + reading is ended at the cap (the client re-listens and refetches - there + is no replay, so ending the stream loses nothing the backlog wasn't + already losing). + """ + + def __init__(self, bus: SubscriptionBus, *, max_subscriptions: int = 1024, max_buffered_events: int = 1024) -> None: + self._bus = bus + self._max_subscriptions = max_subscriptions + self._max_buffered_events = max_buffered_events + self._streams: set[anyio.streams.memory.MemoryObjectSendStream[ServerEvent]] = set() + + async def __call__( + self, + ctx: ServerRequestContext[Any, Any], + params: SubscriptionsListenRequestParams, + ) -> SubscriptionsListenResult: + """Serve one listen stream.""" + subscription_id = ctx.request_id + if subscription_id is None: + raise MCPError(INVALID_REQUEST, "subscriptions/listen requires a request id") + if len(self._streams) >= self._max_subscriptions: + raise MCPError(INTERNAL_ERROR, "Subscription limit reached") + honored = _honored_subset(params.notifications) + honored_uris = frozenset(honored.resource_subscriptions or ()) + meta: dict[str, Any] = {SUBSCRIPTION_ID_META_KEY: subscription_id} + + # Buffered so publishers don't block on a slow consumer (the transport + # write happens in this handler task, not the publisher's). A stream + # whose backlog hits the cap is ended - see the class docstring. + send, recv = anyio.create_memory_object_stream[ServerEvent](self._max_buffered_events) + + def deliver(event: ServerEvent) -> None: + if event_matches(honored, honored_uris, event): + try: + send.send_nowait(event) + except anyio.ClosedResourceError: + # `close` closed this stream; the loop below is unwinding. + pass + except anyio.WouldBlock: + logger.warning("listen stream %r backlog full; ending the stream", subscription_id) + # Release the subscription slot now: the handler's own + # cleanup can be wedged in a transport write that closing + # this buffer cannot wake (a client that stopped reading). + self._streams.discard(send) + send.close() + + # Subscribe before sending the ack so an event published while the + # ack write is suspended is buffered rather than lost. The ack is + # still the first frame: this task alone writes the stream, and it + # only starts draining the buffer after the ack send returns. + unsubscribe = self._bus.subscribe(deliver) + self._streams.add(send) + try: + await ctx.session.send_notification( + SubscriptionsAcknowledgedNotification( + params=SubscriptionsAcknowledgedNotificationParams(notifications=honored, _meta=meta) + ), + related_request_id=subscription_id, + ) + async for event in recv: + await ctx.session.send_notification( + event_to_notification(event, meta), related_request_id=subscription_id + ) + finally: + _safe_unsubscribe(unsubscribe) + self._streams.discard(send) + send.close() + recv.close() + return SubscriptionsListenResult(_meta=meta) + + def close(self) -> None: + """Initiate graceful closure of every open listen stream. + + Each stream then drains its buffered events and sends its + `SubscriptionsListenResult` (stamped with the subscription id) as the + final frame from its own handler task - the spec's graceful closure + flow, telling clients the stream ended deliberately rather than + dropping. This method only initiates that; it does not wait for the + streams to finish flushing. + """ + for stream in list(self._streams): + stream.close() diff --git a/venv/lib/python3.12/site-packages/mcp/server/transport_security.py b/venv/lib/python3.12/site-packages/mcp/server/transport_security.py new file mode 100644 index 0000000..91b5fa7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/transport_security.py @@ -0,0 +1,183 @@ +"""Request checks shared by the HTTP server transports: Host/Origin header validation and body size limits.""" + +import logging +from collections import deque +from typing import Final + +from pydantic import BaseModel, Field +from starlette.datastructures import Headers +from starlette.requests import Request +from starlette.responses import Response +from starlette.types import ASGIApp, Message, Receive, Scope, Send + +logger = logging.getLogger(__name__) + +DEFAULT_MAX_REQUEST_BODY_SIZE: Final = 4 * 1024 * 1024 +"""Default maximum HTTP request body size in bytes (4 MiB).""" + + +# TODO(Marcelo): We should flatten these settings. To be fair, I don't think we should even have this middleware. +class TransportSecuritySettings(BaseModel): + """Settings for MCP transport security features. + + These settings help protect against DNS rebinding attacks by validating incoming request headers. + """ + + enable_dns_rebinding_protection: bool = True + """Enable DNS rebinding protection (recommended for production).""" + + allowed_hosts: list[str] = Field(default_factory=list) + """List of allowed Host header values. + + Only applies when `enable_dns_rebinding_protection` is `True`. + """ + + allowed_origins: list[str] = Field(default_factory=list) + """List of allowed Origin header values. + + Only applies when `enable_dns_rebinding_protection` is `True`. + """ + + +# TODO(Marcelo): This should be a proper ASGI middleware. I'm sad to see this. +class TransportSecurityMiddleware: + """Middleware to enforce DNS rebinding protection for MCP transport endpoints.""" + + def __init__(self, settings: TransportSecuritySettings | None = None): + # If not specified, disable DNS rebinding protection by default for backwards compatibility + self.settings = settings or TransportSecuritySettings(enable_dns_rebinding_protection=False) + + def _validate_host(self, host: str | None) -> bool: + """Validate the Host header against allowed values.""" + if not host: + logger.warning("Missing Host header in request") + return False + + # Check exact match first + if host in self.settings.allowed_hosts: + return True + + # Check wildcard port patterns + for allowed in self.settings.allowed_hosts: + if allowed.endswith(":*"): + # Extract base host from pattern + base_host = allowed[:-2] + # Check if the actual host starts with base host and has a port + if host.startswith(base_host + ":"): + return True + + logger.warning(f"Invalid Host header: {host}") + return False + + def _validate_origin(self, origin: str | None) -> bool: + """Validate the Origin header against allowed values.""" + # Origin can be absent for same-origin requests + if not origin: + return True + + # Check exact match first + if origin in self.settings.allowed_origins: + return True + + # Check wildcard port patterns + for allowed in self.settings.allowed_origins: + if allowed.endswith(":*"): + # Extract base origin from pattern + base_origin = allowed[:-2] + # Check if the actual origin starts with base origin and has a port + if origin.startswith(base_origin + ":"): + return True + + logger.warning(f"Invalid Origin header: {origin}") + return False + + def _validate_content_type(self, content_type: str | None) -> bool: + """Validate the Content-Type header for POST requests.""" + return content_type is not None and content_type.lower().startswith("application/json") + + async def validate_request(self, request: Request, is_post: bool = False) -> Response | None: + """Validate request headers for DNS rebinding protection. + + Returns None if validation passes, or an error Response if validation fails. + """ + # Always validate Content-Type for POST requests + if is_post: + content_type = request.headers.get("content-type") + if not self._validate_content_type(content_type): + return Response("Invalid Content-Type header", status_code=400) + + # Skip remaining validation if DNS rebinding protection is disabled + if not self.settings.enable_dns_rebinding_protection: + return None + + # Validate Host header + host = request.headers.get("host") + if not self._validate_host(host): + return Response("Invalid Host header", status_code=421) + + # Validate Origin header + origin = request.headers.get("origin") + if not self._validate_origin(origin): + return Response("Invalid Origin header", status_code=403) + + return None + + +class RequestBodyLimitMiddleware: + """Reject oversized HTTP request bodies before invoking an ASGI application.""" + + def __init__(self, app: ASGIApp, max_body_size: int) -> None: + self.app = app + self.max_body_size = max_body_size + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] != "http": + await self.app(scope, receive, send) + return + + headers = Headers(scope=scope) + content_length = headers.get("content-length") + if content_length is not None: + try: + declared_size = int(content_length) + except ValueError: + pass + else: + if declared_size > self.max_body_size: + response = Response("Request body too large", status_code=413) + return await response(scope, receive, send) + + received_body = bytearray() + received_request = False + body_complete = False + trailing_message: Message | None = None + while True: + message = await receive() + if message["type"] != "http.request": + trailing_message = message + break + + received_request = True + body = message.get("body", b"") + if len(received_body) + len(body) > self.max_body_size: + response = Response("Request body too large", status_code=413) + return await response(scope, receive, send) + received_body.extend(body) + if not message.get("more_body", False): + body_complete = True + break + + cached_messages: deque[Message] = deque() + if received_request: + cached_messages.append( + {"type": "http.request", "body": bytes(received_body), "more_body": not body_complete} + ) + if trailing_message is not None: + cached_messages.append(trailing_message) + + async def replay() -> Message: + if cached_messages: + return cached_messages.popleft() + return await receive() + + await self.app(scope, replay, send) diff --git a/venv/lib/python3.12/site-packages/mcp/server/validation.py b/venv/lib/python3.12/site-packages/mcp/server/validation.py new file mode 100644 index 0000000..a281f4d --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/server/validation.py @@ -0,0 +1,93 @@ +"""Shared validation functions for server requests. + +This module provides validation logic for sampling and elicitation requests. +""" + +from mcp_types import INVALID_PARAMS, ClientCapabilities, SamplingMessage, Tool, ToolChoice + +from mcp.shared.exceptions import MCPError + + +def check_sampling_tools_capability(client_caps: ClientCapabilities | None) -> bool: + """Check if the client supports sampling tools capability. + + Args: + client_caps: The client's declared capabilities + + Returns: + True if client supports sampling.tools, False otherwise + """ + if client_caps is None: + return False + if client_caps.sampling is None: + return False + if client_caps.sampling.tools is None: + return False + return True + + +def wants_sampling_tools(tools: list[Tool] | None, tool_choice: ToolChoice | None) -> bool: + """Whether a sampling request is tools-mode: `sampling.tools` gated, array-capable answer.""" + return tools is not None or tool_choice is not None + + +def validate_sampling_tools( + client_caps: ClientCapabilities | None, + tools: list[Tool] | None, + tool_choice: ToolChoice | None, +) -> None: + """Validate that the client supports sampling tools if tools are being used. + + Args: + client_caps: The client's declared capabilities + tools: The tools list, if provided + tool_choice: The tool choice setting, if provided + + Raises: + MCPError: If tools/tool_choice are provided but client doesn't support them + """ + if wants_sampling_tools(tools, tool_choice): + if not check_sampling_tools_capability(client_caps): + raise MCPError(code=INVALID_PARAMS, message="Client does not support sampling tools capability") + + +def validate_tool_use_result_messages(messages: list[SamplingMessage]) -> None: + """Validate tool_use/tool_result message structure per SEP-1577. + + This validation ensures: + 1. Messages with tool_result content contain ONLY tool_result content + 2. tool_result messages are preceded by a message with tool_use + 3. tool_result IDs match the tool_use IDs from the previous message + + See: https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1577 + + Args: + messages: The list of sampling messages to validate + + Raises: + ValueError: If the message structure is invalid + """ + if not messages: + return + + last_content = messages[-1].content_as_list + has_tool_results = any(c.type == "tool_result" for c in last_content) + + previous_content = messages[-2].content_as_list if len(messages) >= 2 else None + has_previous_tool_use = previous_content and any(c.type == "tool_use" for c in previous_content) + + if has_tool_results: + # Per spec: "SamplingMessage with tool result content blocks + # MUST NOT contain other content types." + if any(c.type != "tool_result" for c in last_content): + raise ValueError("The last message must contain only tool_result content if any is present") + if previous_content is None: + raise ValueError("tool_result requires a previous message containing tool_use") + if not has_previous_tool_use: + raise ValueError("tool_result blocks do not match any tool_use in the previous message") + + if has_previous_tool_use and previous_content: + tool_use_ids = {c.id for c in previous_content if c.type == "tool_use"} + tool_result_ids = {c.tool_use_id for c in last_content if c.type == "tool_result"} + if tool_use_ids != tool_result_ids: + raise ValueError("ids of tool_result blocks and tool_use blocks from previous message do not match") diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__init__.py b/venv/lib/python3.12/site-packages/mcp/shared/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4137a24 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_callable_inspection.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_callable_inspection.cpython-312.pyc new file mode 100644 index 0000000..f0715b9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_callable_inspection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_compat.cpython-312.pyc new file mode 100644 index 0000000..14fe8e2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_compat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_context_streams.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_context_streams.cpython-312.pyc new file mode 100644 index 0000000..dc0eae6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_context_streams.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_httpx_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_httpx_utils.cpython-312.pyc new file mode 100644 index 0000000..9d0f686 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_httpx_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_otel.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_otel.cpython-312.pyc new file mode 100644 index 0000000..14cfdb5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_otel.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_stream_protocols.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_stream_protocols.cpython-312.pyc new file mode 100644 index 0000000..c237eb2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/_stream_protocols.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/auth.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/auth.cpython-312.pyc new file mode 100644 index 0000000..5ea4e79 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/auth.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/auth_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/auth_utils.cpython-312.pyc new file mode 100644 index 0000000..e8dd655 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/auth_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/context.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/context.cpython-312.pyc new file mode 100644 index 0000000..64eaa46 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/context.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/direct_dispatcher.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/direct_dispatcher.cpython-312.pyc new file mode 100644 index 0000000..01c0b62 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/direct_dispatcher.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/dispatcher.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/dispatcher.cpython-312.pyc new file mode 100644 index 0000000..97f5200 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/dispatcher.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..2cb0294 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/extension.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/extension.cpython-312.pyc new file mode 100644 index 0000000..a86c8a8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/extension.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/inbound.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/inbound.cpython-312.pyc new file mode 100644 index 0000000..e066502 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/inbound.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/jsonrpc_dispatcher.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/jsonrpc_dispatcher.cpython-312.pyc new file mode 100644 index 0000000..aa04b05 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/jsonrpc_dispatcher.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/memory.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/memory.cpython-312.pyc new file mode 100644 index 0000000..4dac0bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/memory.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/message.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/message.cpython-312.pyc new file mode 100644 index 0000000..f782ec8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/metadata_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/metadata_utils.cpython-312.pyc new file mode 100644 index 0000000..22a0dd6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/metadata_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/path_security.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/path_security.cpython-312.pyc new file mode 100644 index 0000000..7e970c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/path_security.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/peer.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/peer.cpython-312.pyc new file mode 100644 index 0000000..fd7f378 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/peer.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/subscriptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/subscriptions.cpython-312.pyc new file mode 100644 index 0000000..b288097 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/subscriptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/tool_name_validation.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/tool_name_validation.cpython-312.pyc new file mode 100644 index 0000000..1968db5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/tool_name_validation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/transport_context.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/transport_context.cpython-312.pyc new file mode 100644 index 0000000..3021c8b Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/transport_context.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/uri_template.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/uri_template.cpython-312.pyc new file mode 100644 index 0000000..b05486a Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/shared/__pycache__/uri_template.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/_callable_inspection.py b/venv/lib/python3.12/site-packages/mcp/shared/_callable_inspection.py new file mode 100644 index 0000000..0e89e44 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/_callable_inspection.py @@ -0,0 +1,33 @@ +"""Callable inspection utilities. + +Adapted from Starlette's `is_async_callable` implementation. +https://github.com/encode/starlette/blob/main/starlette/_utils.py +""" + +from __future__ import annotations + +import functools +import inspect +from collections.abc import Awaitable, Callable +from typing import Any, TypeGuard, TypeVar, overload + +T = TypeVar("T") + +AwaitableCallable = Callable[..., Awaitable[T]] + + +@overload +def is_async_callable(obj: AwaitableCallable[T]) -> TypeGuard[AwaitableCallable[T]]: ... + + +@overload +def is_async_callable(obj: Any) -> TypeGuard[AwaitableCallable[Any]]: ... + + +def is_async_callable(obj: Any) -> Any: + while isinstance(obj, functools.partial): # pragma: lax no cover + obj = obj.func + + return inspect.iscoroutinefunction(obj) or ( + callable(obj) and inspect.iscoroutinefunction(getattr(obj, "__call__", None)) + ) diff --git a/venv/lib/python3.12/site-packages/mcp/shared/_compat.py b/venv/lib/python3.12/site-packages/mcp/shared/_compat.py new file mode 100644 index 0000000..88d50ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/_compat.py @@ -0,0 +1,19 @@ +"""Workarounds for CPython interpreter bugs the SDK papers over.""" + +import anyio.lowlevel + +__all__ = ["resync_tracer"] + + +async def resync_tracer() -> None: + """Resync coverage tracing after a cancelled task-group join. + + A cancel delivered at a join resumes the awaiting coroutine chain via + `coro.throw()`; on CPython 3.11 (python/cpython#106749) that drops the + `'call'` trace events for the outer frames and desyncs coverage's CTracer + until the chain next suspends and resumes normally. Yielding once here + resumes via `.send()`, which re-stamps the missing events. Shielded so a + pending outer cancel is not re-delivered at this point; behaviorally a + no-op. Delete this module when Python 3.11 support ends (EOL 2027-10). + """ + await anyio.lowlevel.cancel_shielded_checkpoint() diff --git a/venv/lib/python3.12/site-packages/mcp/shared/_context_streams.py b/venv/lib/python3.12/site-packages/mcp/shared/_context_streams.py new file mode 100644 index 0000000..04c3330 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/_context_streams.py @@ -0,0 +1,119 @@ +"""Context-aware memory stream wrappers. + +anyio memory streams do not propagate ``contextvars.Context`` across task +boundaries. These thin wrappers capture the sender's context at ``send()`` +time and expose it on the receive side via ``last_context``, so consumers +can restore it with ``ctx.run(handler, item)``. + +The iteration interface is unchanged (yields ``T``, not tuples), keeping +these wrappers duck-type compatible with plain ``MemoryObjectSendStream`` +and ``MemoryObjectReceiveStream``. +""" + +from __future__ import annotations + +import contextvars +from types import TracebackType +from typing import Any, Generic, TypeVar + +import anyio +from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream + +T = TypeVar("T") + +# Internal payload carried through the underlying raw stream. +_Envelope = tuple[contextvars.Context, T] + + +class ContextSendStream(Generic[T]): + """Send-side wrapper that snapshots ``contextvars.copy_context()`` on every ``send()``.""" + + __slots__ = ("_inner",) + + def __init__(self, inner: MemoryObjectSendStream[_Envelope[T]]) -> None: + self._inner = inner + + async def send(self, item: T) -> None: + await self._inner.send((contextvars.copy_context(), item)) + + def close(self) -> None: + self._inner.close() + + async def aclose(self) -> None: + await self._inner.aclose() + + def clone(self) -> ContextSendStream[T]: # pragma: no cover + return ContextSendStream(self._inner.clone()) + + async def __aenter__(self) -> ContextSendStream[T]: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + await self.aclose() + return None + + +class ContextReceiveStream(Generic[T]): + """Receive-side wrapper that yields ``T`` and stores the sender's context in ``last_context``.""" + + __slots__ = ("_inner", "last_context") + + def __init__(self, inner: MemoryObjectReceiveStream[_Envelope[T]]) -> None: + self._inner = inner + self.last_context: contextvars.Context | None = None + + async def receive(self) -> T: + ctx, item = await self._inner.receive() + self.last_context = ctx + return item + + def close(self) -> None: + self._inner.close() + + async def aclose(self) -> None: + await self._inner.aclose() + + def clone(self) -> ContextReceiveStream[T]: # pragma: no cover + return ContextReceiveStream(self._inner.clone()) + + def __aiter__(self) -> ContextReceiveStream[T]: + return self + + async def __anext__(self) -> T: + try: + return await self.receive() + except anyio.EndOfStream: + raise StopAsyncIteration + + async def __aenter__(self) -> ContextReceiveStream[T]: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + await self.aclose() + return None + + +class create_context_streams( + tuple[ContextSendStream[T], ContextReceiveStream[T]], +): + """Create context-aware memory object streams. + + Supports ``create_context_streams[T](n)`` bracket syntax, + matching anyio's ``create_memory_object_stream`` API style. + """ + + def __new__(cls, max_buffer_size: float = 0) -> tuple[ContextSendStream[T], ContextReceiveStream[T]]: # type: ignore[type-var] + raw_send: MemoryObjectSendStream[Any] + raw_receive: MemoryObjectReceiveStream[Any] + raw_send, raw_receive = anyio.create_memory_object_stream(max_buffer_size) + return (ContextSendStream(raw_send), ContextReceiveStream(raw_receive)) diff --git a/venv/lib/python3.12/site-packages/mcp/shared/_httpx_utils.py b/venv/lib/python3.12/site-packages/mcp/shared/_httpx_utils.py new file mode 100644 index 0000000..6bb6388 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/_httpx_utils.py @@ -0,0 +1,95 @@ +"""Utilities for creating standardized httpx2 AsyncClient instances.""" + +from typing import Any, Protocol + +import httpx2 + +__all__ = ["create_mcp_http_client", "MCP_DEFAULT_TIMEOUT", "MCP_DEFAULT_SSE_READ_TIMEOUT"] + +# Default MCP timeout configuration +MCP_DEFAULT_TIMEOUT = 30.0 # General operations (seconds) +MCP_DEFAULT_SSE_READ_TIMEOUT = 300.0 # SSE streams - 5 minutes (seconds) + + +class McpHttpClientFactory(Protocol): # pragma: no branch + def __call__( # pragma: no branch + self, + headers: dict[str, str] | None = None, + timeout: httpx2.Timeout | None = None, + auth: httpx2.Auth | None = None, + ) -> httpx2.AsyncClient: ... + + +def create_mcp_http_client( + headers: dict[str, str] | None = None, + timeout: httpx2.Timeout | None = None, + auth: httpx2.Auth | None = None, +) -> httpx2.AsyncClient: + """Create a standardized httpx2 AsyncClient with MCP defaults. + + Always enables follow_redirects and applies an SSE-friendly default timeout. + + Args: + headers: Optional headers to include with all requests. + timeout: Request timeout as httpx2.Timeout object. Defaults to 30s for + connect/write/pool and 300s for read (for long-lived SSE streams). + auth: Optional authentication handler. + + Returns: + Configured httpx2.AsyncClient instance with MCP defaults. + + Note: + The returned AsyncClient must be used as a context manager to ensure + proper cleanup of connections. + + Example: + Basic usage with MCP defaults: + + ```python + async with create_mcp_http_client() as client: + response = await client.get("https://api.example.com") + ``` + + With custom headers: + + ```python + headers = {"Authorization": "Bearer token"} + async with create_mcp_http_client(headers) as client: + response = await client.get("/endpoint") + ``` + + With both custom headers and timeout: + + ```python + timeout = httpx2.Timeout(60.0, read=300.0) + async with create_mcp_http_client(headers, timeout) as client: + response = await client.get("/long-request") + ``` + + With authentication: + + ```python + from httpx2 import BasicAuth + auth = BasicAuth(username="user", password="pass") + async with create_mcp_http_client(headers, timeout, auth) as client: + response = await client.get("/protected-endpoint") + ``` + """ + # Set MCP defaults + kwargs: dict[str, Any] = {"follow_redirects": True} + + # Handle timeout + if timeout is None: + kwargs["timeout"] = httpx2.Timeout(MCP_DEFAULT_TIMEOUT, read=MCP_DEFAULT_SSE_READ_TIMEOUT) + else: + kwargs["timeout"] = timeout + + # Handle headers + if headers is not None: + kwargs["headers"] = headers + + # Handle authentication + if auth is not None: # pragma: no cover + kwargs["auth"] = auth + + return httpx2.AsyncClient(**kwargs) diff --git a/venv/lib/python3.12/site-packages/mcp/shared/_otel.py b/venv/lib/python3.12/site-packages/mcp/shared/_otel.py new file mode 100644 index 0000000..b7b05b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/_otel.py @@ -0,0 +1,60 @@ +"""OpenTelemetry helpers for MCP.""" + +from __future__ import annotations + +from collections.abc import Generator, Mapping +from contextlib import contextmanager +from typing import Any + +from opentelemetry.context import Context +from opentelemetry.propagate import extract, inject +from opentelemetry.trace import SpanKind, get_current_span, get_tracer +from opentelemetry.trace.span import Span + +_tracer = get_tracer("mcp-python-sdk") + + +@contextmanager +def otel_span( + name: str, + *, + kind: SpanKind, + attributes: dict[str, Any] | None = None, + context: Context | None = None, + record_exception: bool = True, + set_status_on_exception: bool = True, +) -> Generator[Span]: + """Create an OTel span.""" + with _tracer.start_as_current_span( + name, + kind=kind, + attributes=attributes, + context=context, + record_exception=record_exception, + set_status_on_exception=set_status_on_exception, + ) as span: + yield span + + +def inject_trace_context(meta: dict[str, Any]) -> None: + """Inject W3C trace context (traceparent/tracestate) into a `_meta` dict.""" + inject(meta) + + +def extract_trace_context(meta: Mapping[str, Any] | None) -> Context | None: + """Extract W3C trace context from a `_meta` dict. + + Returns `None` when the carrier is absent, malformed, or carries no + valid `traceparent`, so callers fall through to ambient parenting; an + explicit empty `Context` would orphan the span instead of nesting under + the current one. + """ + if not meta: + return None + try: + ctx = extract(meta) + except (ValueError, TypeError): + return None + if not get_current_span(ctx).get_span_context().is_valid: + return None + return ctx diff --git a/venv/lib/python3.12/site-packages/mcp/shared/_stream_protocols.py b/venv/lib/python3.12/site-packages/mcp/shared/_stream_protocols.py new file mode 100644 index 0000000..b799751 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/_stream_protocols.py @@ -0,0 +1,49 @@ +"""Stream protocols for MCP transports. + +These are general-purpose protocols satisfied by both ``MemoryObjectSendStream``/ +``MemoryObjectReceiveStream`` and the context-aware wrappers in ``_context_streams``. +""" + +from __future__ import annotations + +from types import TracebackType +from typing import Protocol, TypeVar + +from typing_extensions import Self + +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class ReadStream(Protocol[T_co]): + """Protocol for reading items from a stream. + + Consumers that need the sender's context should use + ``getattr(stream, 'last_context', None)``. + """ + + async def receive(self) -> T_co: ... + async def aclose(self) -> None: ... + def __aiter__(self) -> ReadStream[T_co]: ... + async def __anext__(self) -> T_co: ... + async def __aenter__(self) -> Self: ... + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: ... + + +class WriteStream(Protocol[T_contra]): + """Protocol for writing items to a stream.""" + + async def send(self, item: T_contra, /) -> None: ... + async def aclose(self) -> None: ... + async def __aenter__(self) -> Self: ... + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: ... diff --git a/venv/lib/python3.12/site-packages/mcp/shared/auth.py b/venv/lib/python3.12/site-packages/mcp/shared/auth.py new file mode 100644 index 0000000..881379d --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/auth.py @@ -0,0 +1,258 @@ +from typing import Any, Literal, cast + +from pydantic import AnyHttpUrl, AnyUrl, BaseModel, ConfigDict, Field, field_validator, model_validator + +# RFC 7523 JWT bearer grant; SEP-990 leg 2 uses this to present the ID-JAG. +JWT_BEARER_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer" + +# Token-endpoint client authentication methods this SDK's clients request, and the set +# `OAuthContext.prepare_token_auth` recognizes on a registered client (`private_key_jwt` is +# applied by `PrivateKeyJWTOAuthProvider`; the rest send a client secret or nothing). +TokenEndpointAuthMethod = Literal["none", "client_secret_post", "client_secret_basic", "private_key_jwt"] + +# grant_types a client requests when it does not specify its own (RFC 7591 §2). +DEFAULT_GRANT_TYPES = ["authorization_code", "refresh_token"] + + +def _empty_str_to_none(v: object) -> object: + # RFC 7591 §2 marks these URL fields OPTIONAL; a "" placeholder means absent, so it + # must not fail AnyHttpUrl validation. (The registered-client record applies the same + # rule to every member; this coercion serves the request model.) + if v == "": + return None + return v + + +class OAuthToken(BaseModel): + """See https://datatracker.ietf.org/doc/html/rfc6749#section-5.1""" + + access_token: str + token_type: Literal["Bearer"] = "Bearer" + expires_in: int | None = None + scope: str | None = None + refresh_token: str | None = None + + @field_validator("token_type", mode="before") + @classmethod + def normalize_token_type(cls, v: str | None) -> str | None: + if isinstance(v, str): + # Bearer is title-cased in the spec, so we normalize it + # https://datatracker.ietf.org/doc/html/rfc6750#section-4 + return v.title() + return v # pragma: no cover + + +class AuthorizationCodeResult(BaseModel): + """Authorization-code-grant redirect parameters returned by a callback handler. + + `iss` carries the RFC 9207 authorization-response issuer when the authorization server + includes it in the redirect; the client validates it against the expected issuer. + """ + + code: str + state: str | None = None + iss: str | None = None + + +class InvalidScopeError(Exception): + def __init__(self, message: str): + self.message = message + + +class InvalidRedirectUriError(Exception): + def __init__(self, message: str): + self.message = message + + +class OAuthClientMetadataBase(BaseModel): + """RFC 7591 OAuth 2.0 Dynamic Client Registration metadata shared verbatim by the + registration request (`OAuthClientMetadata`) and the authorization server's record of a + registered client (`OAuthClientInformationFull`). Fields whose acceptable values differ + between the two - what this SDK sends versus what a third-party server may echo - are + declared on each model rather than here. + See https://datatracker.ietf.org/doc/html/rfc7591#section-2 + """ + + model_config = ConfigDict(url_preserve_empty_path=True) + + # The MCP spec requires the "code" response type, but OAuth + # servers may also return additional types they support + response_types: list[str] = ["code"] + scope: str | None = None + + # these fields are currently unused, but we support & store them for potential + # future use + client_name: str | None = None + client_uri: AnyHttpUrl | None = None + logo_uri: AnyHttpUrl | None = None + contacts: list[str] | None = None + tos_uri: AnyHttpUrl | None = None + policy_uri: AnyHttpUrl | None = None + jwks_uri: AnyHttpUrl | None = None + jwks: Any | None = None + software_id: str | None = None + software_version: str | None = None + + @field_validator( + "client_uri", + "logo_uri", + "tos_uri", + "policy_uri", + "jwks_uri", + mode="before", + ) + @classmethod + def _empty_string_optional_url_to_none(cls, v: object) -> object: + # These URL fields are OPTIONAL; an echoed "" would otherwise fail AnyHttpUrl + # and throw away an otherwise valid registration response. + return _empty_str_to_none(v) + + +class OAuthClientMetadata(OAuthClientMetadataBase): + """RFC 7591 OAuth 2.0 Dynamic Client Registration request metadata: what an MCP + client sends when it registers. Field values are narrowed to what this SDK will put + on the wire; parsing the authorization server's response is `OAuthClientInformationFull`'s + job. See https://datatracker.ietf.org/doc/html/rfc7591#section-2 + """ + + redirect_uris: list[AnyUrl] | None = Field(..., min_length=1) + # supported auth methods for the token endpoint + token_endpoint_auth_method: TokenEndpointAuthMethod | None = None + # supported grant_types of this implementation + grant_types: list[ + Literal["authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:jwt-bearer"] | str + ] = list(DEFAULT_GRANT_TYPES) + # SEP-837: OIDC application_type. Defaults to "native" since MCP clients typically use + # loopback redirect URIs; set "web" for remote browser-based clients on a non-local host. + application_type: Literal["web", "native"] = "native" + + +class OAuthClientInformationFull(OAuthClientMetadataBase): + """RFC 7591 OAuth 2.0 Dynamic Client Registration client information response + (client information plus metadata) - the authorization server's record of a + registered client. See https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1 + + A third-party authorization server "MAY reject or replace any of the client's + requested metadata values submitted during the registration and substitute them with + suitable values", so `application_type`, `token_endpoint_auth_method`, and `grant_types` + are typed to accept any string the server echoes, and `redirect_uris` may be absent or + empty. A member the server serializes as a placeholder - an explicit `null`, or `""` - + is read as an omitted key, so the field's default applies rather than the parse failing. + Whether a substituted value is usable is decided where the value is used, not at parse. + `redirect_uris` elements are still parsed as URLs, as the authorization server compares + them against a client's requested `redirect_uri`. + """ + + redirect_uris: list[AnyUrl] | None = None + # RFC 7591 §3.2.1: the server may assign an auth method other than the one requested, + # including methods this SDK does not implement, or omit it. + token_endpoint_auth_method: str | None = None + grant_types: list[str] = list(DEFAULT_GRANT_TYPES) + # SEP-837: OIDC application_type. OIDC Registration §2 defines "web" and "native", but + # servers echo other strings or an explicit null; the value is informational here. + application_type: str | None = None + + # RFC 7591 §3.2.1: client_id is REQUIRED in a client information response - a body + # without one is not a registration, whatever else it echoes. + client_id: str + client_secret: str | None = None + client_id_issued_at: int | None = None + client_secret_expires_at: int | None = None + # SEP-2352: the issuer these credentials were registered with, recorded by the SDK (not an + # RFC 7591 field) to detect authorization-server migration and avoid cross-AS credential reuse. + issuer: str | None = None + + @model_validator(mode="before") + @classmethod + def _placeholder_members_read_as_omitted(cls, data: object) -> object: + # Servers dump unset members of their client record as null, or echo them as "", + # instead of omitting the keys. Either placeholder would otherwise fail the parse of a + # list field (or read "" as an unrecognized method) and discard an already-provisioned + # registration; a placeholder and an absent key mean the same thing. + if isinstance(data, dict): + members = cast(dict[str, Any], data) + return {key: value for key, value in members.items() if value is not None and value != ""} + return data + + def validate_scope(self, requested_scope: str | None) -> list[str] | None: + if requested_scope is None: + return None + requested_scopes = requested_scope.split(" ") + allowed_scopes = [] if self.scope is None else self.scope.split(" ") + for scope in requested_scopes: + if scope not in allowed_scopes: + raise InvalidScopeError(f"Client was not registered with scope {scope}") + return requested_scopes + + def validate_redirect_uri(self, redirect_uri: AnyUrl | None) -> AnyUrl: + if redirect_uri is not None: + # Validate redirect_uri against client's registered redirect URIs + if not self.redirect_uris or redirect_uri not in self.redirect_uris: + raise InvalidRedirectUriError(f"Redirect URI '{redirect_uri}' not registered for client") + return redirect_uri + elif self.redirect_uris and len(self.redirect_uris) == 1: + return self.redirect_uris[0] + else: + raise InvalidRedirectUriError( + "redirect_uri must be specified unless the client has exactly one registered URI" + ) + + +class OAuthMetadata(BaseModel): + """RFC 8414 OAuth 2.0 Authorization Server Metadata. + See https://datatracker.ietf.org/doc/html/rfc8414#section-2 + """ + + model_config = ConfigDict(url_preserve_empty_path=True) + + issuer: AnyHttpUrl + authorization_endpoint: AnyHttpUrl + token_endpoint: AnyHttpUrl + registration_endpoint: AnyHttpUrl | None = None + scopes_supported: list[str] | None = None + response_types_supported: list[str] = ["code"] + response_modes_supported: list[str] | None = None + grant_types_supported: list[str] | None = None + token_endpoint_auth_methods_supported: list[str] | None = None + token_endpoint_auth_signing_alg_values_supported: list[str] | None = None + service_documentation: AnyHttpUrl | None = None + ui_locales_supported: list[str] | None = None + op_policy_uri: AnyHttpUrl | None = None + op_tos_uri: AnyHttpUrl | None = None + revocation_endpoint: AnyHttpUrl | None = None + revocation_endpoint_auth_methods_supported: list[str] | None = None + revocation_endpoint_auth_signing_alg_values_supported: list[str] | None = None + introspection_endpoint: AnyHttpUrl | None = None + introspection_endpoint_auth_methods_supported: list[str] | None = None + introspection_endpoint_auth_signing_alg_values_supported: list[str] | None = None + code_challenge_methods_supported: list[str] | None = None + client_id_metadata_document_supported: bool | None = None + authorization_response_iss_parameter_supported: bool | None = None + # SEP-990 / draft-ietf-oauth-identity-assertion-authz-grant §7.2: profiles whose grants the + # authorization server supports, e.g. `urn:ietf:params:oauth:grant-profile:id-jag`. + authorization_grant_profiles_supported: list[str] | None = None + + +class ProtectedResourceMetadata(BaseModel): + """RFC 9728 OAuth 2.0 Protected Resource Metadata. + See https://datatracker.ietf.org/doc/html/rfc9728#section-2 + """ + + model_config = ConfigDict(url_preserve_empty_path=True) + + resource: AnyHttpUrl + authorization_servers: list[AnyHttpUrl] = Field(..., min_length=1) + jwks_uri: AnyHttpUrl | None = None + scopes_supported: list[str] | None = None + bearer_methods_supported: list[str] | None = Field(default=["header"]) # MCP only supports header method + resource_signing_alg_values_supported: list[str] | None = None + resource_name: str | None = None + resource_documentation: AnyHttpUrl | None = None + resource_policy_uri: AnyHttpUrl | None = None + resource_tos_uri: AnyHttpUrl | None = None + # tls_client_certificate_bound_access_tokens default is False, but omitted here for clarity + tls_client_certificate_bound_access_tokens: bool | None = None + authorization_details_types_supported: list[str] | None = None + dpop_signing_alg_values_supported: list[str] | None = None + # dpop_bound_access_tokens_required default is False, but omitted here for clarity + dpop_bound_access_tokens_required: bool | None = None diff --git a/venv/lib/python3.12/site-packages/mcp/shared/auth_utils.py b/venv/lib/python3.12/site-packages/mcp/shared/auth_utils.py new file mode 100644 index 0000000..3ba880f --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/auth_utils.py @@ -0,0 +1,80 @@ +"""Utilities for OAuth 2.0 Resource Indicators (RFC 8707) and PKCE (RFC 7636).""" + +import time +from urllib.parse import urlparse, urlsplit, urlunsplit + +from pydantic import AnyUrl, HttpUrl + + +def resource_url_from_server_url(url: str | HttpUrl | AnyUrl) -> str: + """Convert server URL to canonical resource URL per RFC 8707. + + RFC 8707 section 2 states that resource URIs "MUST NOT include a fragment component". + Returns absolute URI with lowercase scheme/host for canonical form. + + Args: + url: Server URL to convert + + Returns: + Canonical resource URL string + """ + # Convert to string if needed + url_str = str(url) + + # Parse the URL and remove fragment, create canonical form + parsed = urlsplit(url_str) + canonical = urlunsplit(parsed._replace(scheme=parsed.scheme.lower(), netloc=parsed.netloc.lower(), fragment="")) + + return canonical + + +def check_resource_allowed(requested_resource: str, configured_resource: str) -> bool: + """Check if a requested resource URL matches a configured resource URL. + + A requested resource matches if it has the same scheme, domain, port, + and its path starts with the configured resource's path. This allows + hierarchical matching where a token for a parent resource can be used + for child resources. + + Args: + requested_resource: The resource URL being requested + configured_resource: The resource URL that has been configured + + Returns: + True if the requested resource matches the configured resource + """ + # Parse both URLs + requested = urlparse(requested_resource) + configured = urlparse(configured_resource) + + # Compare scheme, host, and port (origin) + if requested.scheme.lower() != configured.scheme.lower() or requested.netloc.lower() != configured.netloc.lower(): + return False + + # Normalize trailing slashes before comparison so that + # "/foo" and "/foo/" are treated as equivalent. + requested_path = requested.path + configured_path = configured.path + if not requested_path.endswith("/"): + requested_path += "/" + if not configured_path.endswith("/"): + configured_path += "/" + + # Check hierarchical match: requested must start with configured path. + # The trailing-slash normalization ensures "/api123/" won't match "/api/". + return requested_path.startswith(configured_path) + + +def calculate_token_expiry(expires_in: int | str | None) -> float | None: + """Calculate token expiry timestamp from expires_in seconds. + + Args: + expires_in: Seconds until token expiration (may be string from some servers) + + Returns: + Unix timestamp when token expires, or None if no expiry specified + """ + if expires_in is None: + return None # pragma: no cover + # Defensive: handle servers that return expires_in as string + return time.time() + int(expires_in) diff --git a/venv/lib/python3.12/site-packages/mcp/shared/context.py b/venv/lib/python3.12/site-packages/mcp/shared/context.py new file mode 100644 index 0000000..13c145b --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/context.py @@ -0,0 +1,85 @@ +"""`BaseContext` - the user-facing per-request context. + +Composition over a `DispatchContext`: forwards the transport metadata, the +back-channel (`send_raw_request`/`notify`), progress reporting, and the cancel +event. Adds `meta` (the inbound request's `_meta` field). + +Satisfies `Outbound`, so `ClientPeer` can wrap it. Shared between client and +server: the server's `Context` extends this with `lifespan`/`connection`; +`ClientContext` is just an alias. +""" + +from collections.abc import Mapping +from typing import Any, Generic + +import anyio +from mcp_types import RequestParamsMeta +from typing_extensions import TypeVar + +from mcp.shared.dispatcher import CallOptions, DispatchContext +from mcp.shared.transport_context import TransportContext + +__all__ = ["BaseContext"] + +TransportT = TypeVar("TransportT", bound=TransportContext, default=TransportContext, covariant=True) + + +class BaseContext(Generic[TransportT]): + """Per-request context wrapping a `DispatchContext`. + + `ServerRunner` constructs one per inbound request and passes it to the + user's handler. + """ + + def __init__(self, dctx: DispatchContext[TransportT], meta: RequestParamsMeta | None = None) -> None: + self._dctx = dctx + self._meta = meta + + @property + def transport(self) -> TransportT: + """Transport-specific metadata for this inbound request.""" + return self._dctx.transport + + @property + def cancel_requested(self) -> anyio.Event: + """Set when the peer sends `notifications/cancelled` for this request.""" + return self._dctx.cancel_requested + + @property + def can_send_request(self) -> bool: + """Whether the back-channel can currently deliver server-initiated requests. + + `False` when the transport has no back-channel, or when the underlying + dispatch context has been closed because the inbound request finished. + """ + return self._dctx.can_send_request + + @property + def meta(self) -> RequestParamsMeta | None: + """The inbound request's `_meta` field, if present.""" + return self._meta + + async def send_raw_request( + self, + method: str, + params: Mapping[str, Any] | None, + opts: CallOptions | None = None, + ) -> dict[str, Any]: + """Send a request to the peer on the back-channel. + + Raises: + MCPError: The peer responded with an error. + NoBackChannelError: `can_send_request` is `False`. + """ + return await self._dctx.send_raw_request(method, params, opts) + + async def notify(self, method: str, params: Mapping[str, Any] | None, opts: CallOptions | None = None) -> None: + """Send a notification to the peer on the back-channel.""" + await self._dctx.notify(method, params, opts) + + async def report_progress(self, progress: float, total: float | None = None, message: str | None = None) -> None: + """Report progress for this request, if the peer supplied a progress token. + + A no-op when no token was supplied. + """ + await self._dctx.progress(progress, total, message) diff --git a/venv/lib/python3.12/site-packages/mcp/shared/direct_dispatcher.py b/venv/lib/python3.12/site-packages/mcp/shared/direct_dispatcher.py new file mode 100644 index 0000000..e17283a --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/direct_dispatcher.py @@ -0,0 +1,334 @@ +"""In-memory `Dispatcher` that wires two peers together with no transport. + +`DirectDispatcher` is the simplest possible `Dispatcher` implementation: a +request on one side directly invokes the other side's `on_request`. There is no +serialization, no JSON-RPC framing, and no streams. It exists to: + +* prove the `Dispatcher` Protocol is implementable without JSON-RPC +* provide a fast substrate for testing the layers above the dispatcher + (`ServerRunner`, `Context`, `Connection`) without wire-level moving parts +* embed a server in-process when the JSON-RPC overhead is unnecessary + +Like `JSONRPCDispatcher`, this is an exception-to-error boundary: a handler +exception surfaces to the caller as `MCPError`. The `raise_handler_exceptions` +knob controls whether unmapped exceptions are sanitized (matching the wire +path) or chained as ``__cause__`` for in-process debugging. +""" + +from __future__ import annotations + +import logging +from collections.abc import Awaitable, Callable, Mapping +from dataclasses import dataclass, field +from typing import Any + +import anyio +import anyio.abc +from mcp_types import CONNECTION_CLOSED, INTERNAL_ERROR, INVALID_PARAMS, REQUEST_TIMEOUT, RequestId +from pydantic import ValidationError + +from mcp.shared._compat import resync_tracer +from mcp.shared.dispatcher import ( + CallOptions, + OnNotify, + OnNotifyIntercept, + OnRequest, + ProgressFnT, + coerce_request_id, + run_notify_intercept, +) +from mcp.shared.exceptions import MCPError, NoBackChannelError +from mcp.shared.message import MessageMetadata +from mcp.shared.transport_context import TransportContext + +logger = logging.getLogger(__name__) + +__all__ = ["DirectDispatcher", "create_direct_dispatcher_pair"] + +DIRECT_TRANSPORT_KIND = "direct" + + +_Request = Callable[[str, Mapping[str, Any] | None, CallOptions | None], Awaitable[dict[str, Any]]] +_Notify = Callable[[str, Mapping[str, Any] | None], Awaitable[None]] + + +@dataclass +class _DirectDispatchContext: + """`DispatchContext` for an inbound request on a `DirectDispatcher`. + + The back-channel callables target the *originating* side, so a handler's + `send_raw_request` reaches the peer that made the inbound request. + """ + + transport: TransportContext + _back_request: _Request + _back_notify: _Notify + request_id: RequestId | None = None + """The caller-supplied `CallOptions["request_id"]`, else a dispatcher-synthesized + id for requests; `None` for notifications.""" + message_metadata: MessageMetadata = None # TODO(maxisbey): remove for Context rework + """Always `None`: in-memory dispatch attaches no transport metadata.""" + _on_progress: ProgressFnT | None = None + cancel_requested: anyio.Event = field(default_factory=anyio.Event) + + @property + def can_send_request(self) -> bool: + return self.transport.can_send_request + + async def notify(self, method: str, params: Mapping[str, Any] | None, opts: CallOptions | None = None) -> None: + await self._back_notify(method, params) + + async def send_raw_request( + self, + method: str, + params: Mapping[str, Any] | None, + opts: CallOptions | None = None, + ) -> dict[str, Any]: + if not self.can_send_request: + raise NoBackChannelError(method) + return await self._back_request(method, params, opts) + + async def progress(self, progress: float, total: float | None = None, message: str | None = None) -> None: + if self._on_progress is not None: + await self._on_progress(progress, total, message) + + +class DirectDispatcher: + """A `Dispatcher` that calls a peer's handlers directly, in-process. + + Two instances are wired together with `create_direct_dispatcher_pair`; each + holds a reference to the other. `send_raw_request` on one awaits the peer's + `on_request`. `run` parks until `close` is called. + + Lifecycle mirrors `JSONRPCDispatcher`: `send_raw_request` requires `run()` + to have started, and once a side has closed - via `close()` or `run()` + ending - `send_raw_request` raises `MCPError` (`CONNECTION_CLOSED`) and + inbound requests fail the peer's call the same way instead of invoking the + handler. Notifications are fire-and-forget in both directions: after close + they are silently dropped. + """ + + def __init__(self, transport_ctx: TransportContext, *, raise_handler_exceptions: bool = True): + self._transport_ctx = transport_ctx + self._raise_handler_exceptions = raise_handler_exceptions + self._peer: DirectDispatcher | None = None + self._on_request: OnRequest | None = None + self._on_notify: OnNotify | None = None + self._on_notify_intercept: OnNotifyIntercept | None = None + self._next_id = 0 + self._in_flight_ids: set[RequestId] = set() + self._ready = anyio.Event() + self._close_event = anyio.Event() + self._running = False + self._closed = False + + def connect_to(self, peer: DirectDispatcher) -> None: + self._peer = peer + + async def send_raw_request( + self, + method: str, + params: Mapping[str, Any] | None, + opts: CallOptions | None = None, + ) -> dict[str, Any]: + """Send a request by invoking the peer's `on_request` directly. + + Raises: + MCPError: The peer's handler raised; `REQUEST_TIMEOUT` if + `opts["timeout"]` elapsed; `CONNECTION_CLOSED` if either + side has closed. + RuntimeError: Called before `run()`. + """ + if self._peer is None: + raise RuntimeError("DirectDispatcher has no peer; use create_direct_dispatcher_pair()") + # Post-close sends get the same CONNECTION_CLOSED contract as JSONRPCDispatcher. + if self._closed: + raise MCPError(code=CONNECTION_CLOSED, message="Connection closed") + if not self._running: + raise RuntimeError("DirectDispatcher.send_raw_request called before run()") + return await self._peer._dispatch_request(method, params, opts) + + async def notify(self, method: str, params: Mapping[str, Any] | None, opts: CallOptions | None = None) -> None: + """Send a notification by invoking the peer's `on_notify` directly. + + Fire-and-forget: usable before `run()` (delivery waits for the peer to + start), and after close it is silently dropped, matching + `JSONRPCDispatcher.notify`. `opts` is accepted for `Dispatcher` + conformance; there is no HTTP layer here so `headers` is ignored. + """ + if self._peer is None: + raise RuntimeError("DirectDispatcher has no peer; use create_direct_dispatcher_pair()") + if self._closed: + logger.debug("dropped notification %r on closed DirectDispatcher", method) + return + await self._peer._dispatch_notify(method, params) + + async def run( + self, + on_request: OnRequest, + on_notify: OnNotify, + on_notify_intercept: OnNotifyIntercept | None = None, + *, + task_status: anyio.abc.TaskStatus[None] = anyio.TASK_STATUS_IGNORED, + ) -> None: + """Mark this side ready and park until `close()` is called. + + Single-shot, like `JSONRPCDispatcher.run`: once it returns the + dispatcher stays closed and cannot be restarted. + """ + try: + self._on_request = on_request + self._on_notify = on_notify + self._on_notify_intercept = on_notify_intercept + self._running = True + self._ready.set() + task_status.started() + await self._close_event.wait() + finally: + self._running = False + self._closed = True + # run() may end via cancellation without close() ever being + # called; setting the event wakes `_wait_ready` waiters so they + # observe the closed state instead of parking forever. + self._close_event.set() + + def close(self) -> None: + self._closed = True + self._close_event.set() + + def _make_context( + self, on_progress: ProgressFnT | None = None, request_id: RequestId | None = None + ) -> _DirectDispatchContext: + assert self._peer is not None + peer = self._peer + return _DirectDispatchContext( + transport=self._transport_ctx, + _back_request=lambda m, p, o: peer._dispatch_request(m, p, o), + _back_notify=lambda m, p: peer._dispatch_notify(m, p), + request_id=request_id, + _on_progress=on_progress, + ) + + async def _wait_ready(self) -> None: + """Park until `run()` has started, waking early if this side closes. + + Raises: + MCPError: `CONNECTION_CLOSED` if this side has closed. + """ + if not self._ready.is_set() and not self._close_event.is_set(): + async with anyio.create_task_group() as tg: + + async def wake_on(event: anyio.Event) -> None: + await event.wait() + tg.cancel_scope.cancel() + + tg.start_soon(wake_on, self._ready) + tg.start_soon(wake_on, self._close_event) + if self._closed: + raise MCPError(code=CONNECTION_CLOSED, message="Connection closed") + + async def _dispatch_request( + self, + method: str, + params: Mapping[str, Any] | None, + opts: CallOptions | None, + ) -> dict[str, Any]: + opts = opts or {} + try: + with anyio.fail_after(opts.get("timeout")): + # Inside the timeout scope, so a configured timeout also bounds + # waiting on a peer whose run() has not started yet. + await self._wait_ready() + assert self._on_request is not None + supplied_id = opts.get("request_id") + if supplied_id is not None: + request_id: RequestId = supplied_id + # Collisions use the same coerced domain as JSONRPCDispatcher's + # pending keys, so this in-memory stand-in raises for exactly + # the ids the wire dispatcher would; the context still sees + # the verbatim value. + in_flight_key = coerce_request_id(request_id) + if in_flight_key in self._in_flight_ids: + raise ValueError(f"request id {request_id!r} is already in flight") + else: + # Synthesize an id (the DispatchContext contract reserves None + # for notifications), minting past any key a supplied id + # occupies: the collision error is reserved for the caller + # who actually chose the id. + self._next_id += 1 + while self._next_id in self._in_flight_ids: + self._next_id += 1 + request_id = self._next_id + in_flight_key = request_id + self._in_flight_ids.add(in_flight_key) + dctx = self._make_context(on_progress=opts.get("on_progress"), request_id=request_id) + try: + return await self._on_request(dctx, method, params) + except MCPError: + raise + except ValidationError as e: + # Same shape JSONRPCDispatcher writes, so runner-over-direct + # tests see what runner-over-JSONRPC would. + raise MCPError(code=INVALID_PARAMS, message="Invalid request parameters", data="") from e + except Exception as e: + # Single owner of the in-proc exception-to-error policy (mirrors + # JSONRPCDispatcher / `_streamable_http_modern._to_jsonrpc_response` + # for the wire paths). True chains the original for in-process + # debugging; False sanitizes to match the wire path's leak guard. + if self._raise_handler_exceptions: + raise MCPError(code=INTERNAL_ERROR, message=str(e)) from e + logger.exception("request handler raised") + raise MCPError(code=INTERNAL_ERROR, message="Internal server error") from None + finally: + self._in_flight_ids.discard(in_flight_key) + except TimeoutError: + raise MCPError( + code=REQUEST_TIMEOUT, + message=f"Timed out after {opts.get('timeout')}s waiting for {method!r}", + ) from None + finally: + await resync_tracer() + + async def _dispatch_notify(self, method: str, params: Mapping[str, Any] | None) -> None: + try: + await self._wait_ready() + except MCPError: + # Notifications are fire-and-forget: a notify to a closed peer is + # dropped, not raised back into the sender's call. + logger.debug("dropped notification %r to closed DirectDispatcher", method) + return + if run_notify_intercept(self._on_notify_intercept, method, params): + return + assert self._on_notify is not None + dctx = self._make_context() + await self._on_notify(dctx, method, params) + + +def create_direct_dispatcher_pair( + *, + can_send_request: bool = True, + headers: Mapping[str, str] | None = None, + raise_handler_exceptions: bool = True, +) -> tuple[DirectDispatcher, DirectDispatcher]: + """Create two `DirectDispatcher` instances wired to each other. + + Args: + can_send_request: Sets `TransportContext.can_send_request` on both + sides. Pass `False` to simulate a transport with no back-channel. + headers: Sets `TransportContext.headers` on both sides. + raise_handler_exceptions: When `True` (the default - this is an + in-process debugging substrate), an unmapped handler exception + reaches the caller as `MCPError` with the original chained as + ``__cause__``. When `False` it is sanitized to an opaque + `INTERNAL_ERROR` so the in-process path matches the wire. + + Returns: + A `(client, server)` pair. The wiring is symmetric, so the roles + are conventional only. + """ + ctx = TransportContext(kind=DIRECT_TRANSPORT_KIND, can_send_request=can_send_request, headers=headers) + client = DirectDispatcher(ctx, raise_handler_exceptions=raise_handler_exceptions) + server = DirectDispatcher(ctx, raise_handler_exceptions=raise_handler_exceptions) + client.connect_to(server) + server.connect_to(client) + return client, server diff --git a/venv/lib/python3.12/site-packages/mcp/shared/dispatcher.py b/venv/lib/python3.12/site-packages/mcp/shared/dispatcher.py new file mode 100644 index 0000000..f2ff96e --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/dispatcher.py @@ -0,0 +1,277 @@ +"""Dispatcher Protocol - the call/return boundary between transports and handlers. + +A Dispatcher turns a duplex message channel into two things: + +* an outbound API: `send_raw_request(method, params)` and `notify(method, params)` +* an inbound pump: `run(on_request, on_notify)` that drives the receive loop + and invokes the supplied handlers for each incoming request/notification + +It is deliberately *not* MCP-aware. Method names are strings, params and +results are `dict[str, Any]`. The MCP type layer (request/result models, +capability negotiation, `Context`) sits above this; the wire encoding +(JSON-RPC, gRPC, in-process direct calls) sits below it. + +See `JSONRPCDispatcher` for the production implementation and +`DirectDispatcher` for an in-memory implementation used in tests and for +embedding a server in-process. +""" + +import logging +from collections.abc import Awaitable, Callable, Mapping +from typing import Any, Protocol, TypedDict, TypeVar, runtime_checkable + +import anyio +import anyio.abc +from mcp_types import RequestId + +from mcp.shared.message import MessageMetadata +from mcp.shared.transport_context import TransportContext + +logger = logging.getLogger(__name__) + +__all__ = [ + "CallOptions", + "DispatchContext", + "Dispatcher", + "OnNotify", + "OnNotifyIntercept", + "OnRequest", + "Outbound", + "ProgressFnT", + "as_request_id", + "coerce_request_id", + "run_notify_intercept", +] + +TransportT_co = TypeVar("TransportT_co", bound=TransportContext, covariant=True) + + +def as_request_id(value: object) -> RequestId | None: + """Narrow an untyped wire value to a `RequestId`, or None; rejects bool (True would alias request id 1).""" + if isinstance(value, str | int) and not isinstance(value, bool): + return value + return None + + +def coerce_request_id(request_id: RequestId) -> RequestId: + """Coerce a stringified int request id back to int so a peer-echoed id still correlates (matches the TS SDK). + + This is the collision/correlation domain dispatchers share: "7" and 7 are one + id for correlation purposes, even where the wire carries the verbatim value. + """ + if isinstance(request_id, str): + try: + return int(request_id) + except ValueError: + pass + return request_id + + +class ProgressFnT(Protocol): + """Callback invoked when a progress notification arrives for a pending request.""" + + async def __call__(self, progress: float, total: float | None, message: str | None) -> None: ... + + +class CallOptions(TypedDict, total=False): + """Per-call options for `Outbound.send_raw_request`. + + All keys are optional. Dispatchers ignore keys they do not understand. + """ + + request_id: RequestId + """Send the request under this caller-supplied id instead of a dispatcher-minted one. + + The peer sees the value verbatim ("7" stays a string). A value that collides + with one of the sender's own in-flight request ids raises `ValueError`. + Callers that need to know a request's id before its result arrives (a + `subscriptions/listen` stream is demultiplexed by it) mint their own ids + here; string ids that don't parse as integers can never collide with the + dispatcher's minted sequence. Per the class contract, dispatchers that + predate this key ignore it and mint as usual. + """ + + timeout: float + """Seconds to wait for a result before raising and sending `notifications/cancelled`.""" + + cancel_on_abandon: bool + """Whether abandoning this request (timeout or caller cancellation) sends `notifications/cancelled`. + + Defaults to `True`. Set `False` for requests the protocol forbids cancelling, such as `initialize`. + Also suppressed when resumption hints reach the transport, or when the request was never written. + """ + + on_progress: ProgressFnT + """Receive `notifications/progress` updates for this request.""" + + resumption_token: str + """Opaque token to resume a previously interrupted request. + + Client-side, streamable-HTTP only. Ignored by server dispatchers and other + transports, and also ignored (with a debug log) for requests sent from a + `DispatchContext`, where routing onto the inbound request's stream takes + precedence. Supports protocol version 2025-11-25 and earlier; SSE-stream + resumption is removed in the next protocol revision. + """ + + on_resumption_token: Callable[[str], Awaitable[None]] + """Receive a resumption token when the transport issues one for this request. + + Client-side, streamable-HTTP only. Ignored by server dispatchers and other + transports, and also ignored (with a debug log) for requests sent from a + `DispatchContext`, where routing onto the inbound request's stream takes + precedence. Supports protocol version 2025-11-25 and earlier; SSE-stream + resumption is removed in the next protocol revision. + """ + + headers: dict[str, str] + """Transport-layer hint: HTTP transports merge these onto the outgoing request; non-HTTP transports ignore.""" + + +@runtime_checkable +class Outbound(Protocol): + """Anything that can send requests and notifications to the peer. + + Both `Dispatcher` (top-level outbound) and `DispatchContext` (back-channel + during an inbound request) extend this. The MCP type layer (`ClientPeer`, + `Connection`) builds typed `send_request` / convenience methods on top of + this raw channel. + """ + + async def send_raw_request( + self, + method: str, + params: Mapping[str, Any] | None, + opts: CallOptions | None = None, + ) -> dict[str, Any]: + """Send a request and await its raw result dict. + + Raises: + MCPError: If the peer responded with an error, or the handler + raised. Implementations normalize all handler exceptions to + `MCPError` so callers see a single exception type. + """ + ... + + async def notify(self, method: str, params: Mapping[str, Any] | None, opts: CallOptions | None = None) -> None: + """Send a fire-and-forget notification.""" + ... + + +class DispatchContext(Outbound, Protocol[TransportT_co]): + """Per-request context handed to `on_request` / `on_notify`. + + Carries the transport metadata for the inbound message and provides the + back-channel for sending requests/notifications to the peer while handling + it. `send_raw_request` raises `NoBackChannelError` if `can_send_request` + is `False`. + """ + + @property + def transport(self) -> TransportT_co: + """Transport-specific metadata for this inbound message.""" + ... + + @property + def can_send_request(self) -> bool: + """Whether the back-channel can currently deliver server-initiated requests. + + `False` when the transport has no back-channel, or when this context has + been closed (the inbound request finished). `send_raw_request` raises + `NoBackChannelError` exactly when this is `False`. + """ + ... + + @property + def request_id(self) -> RequestId | None: + """The id of the inbound request, or `None` for a notification. + + For JSON-RPC this is the wire `id` field. Handlers thread it through + as `related_request_id` on outbound notifications so HTTP transports + can route them onto the originating request's response stream. + """ + ... + + @property + def message_metadata(self) -> MessageMetadata: + """The metadata the transport attached to this inbound message, if any. + + This is `SessionMessage.metadata` passed through verbatim: HTTP + transports attach `ServerMessageMetadata` (the HTTP request, SSE + stream-close callbacks); stdio and in-memory dispatch attach nothing. + Tied to the `SessionMessage` wire format - goes away when transports + stop delivering messages that way. + """ + # TODO(maxisbey): remove for context rework + ... + + @property + def cancel_requested(self) -> anyio.Event: + """Set when the peer sends `notifications/cancelled` for this request.""" + ... + + async def progress(self, progress: float, total: float | None = None, message: str | None = None) -> None: + """Report progress for the inbound request, if the peer supplied a progress token. + + A no-op when no token was supplied. + """ + ... + + +OnRequest = Callable[[DispatchContext[TransportContext], str, Mapping[str, Any] | None], Awaitable[dict[str, Any]]] +"""Handler for inbound requests: `(ctx, method, params) -> result`. Raise `MCPError` to send an error response.""" + +OnNotify = Callable[[DispatchContext[TransportContext], str, Mapping[str, Any] | None], Awaitable[None]] +"""Handler for inbound notifications: `(ctx, method, params)`.""" + +OnNotifyIntercept = Callable[[str, Mapping[str, Any] | None], bool] +"""Synchronous receive-order intercept for inbound notifications: `(method, params) -> consumed`. + +Runs before `on_notify` is scheduled so correlation state advances in wire order +relative to response resolution (the client's listen demux depends on this). +Returning True consumes the notification. Must not block the receive path. +""" + + +def run_notify_intercept(intercept: OnNotifyIntercept | None, method: str, params: Mapping[str, Any] | None) -> bool: + """Invoke `intercept`, containing a raise to that one notification (never the receive loop).""" + if intercept is None: + return False + try: + return intercept(method, params) + except Exception: + logger.exception("notification intercept raised; passing %r through", method) + return False + + +class Dispatcher(Outbound, Protocol[TransportT_co]): + """A duplex request/notification channel with call-return semantics. + + Implementations own correlation of outbound requests to inbound results, the + receive loop, per-request concurrency, and cancellation/progress wiring. + + The lifecycle surface is provisional; `run()` may change in a 2.x minor + release. + """ + + async def run( + self, + on_request: OnRequest, + on_notify: OnNotify, + on_notify_intercept: OnNotifyIntercept | None = None, + *, + task_status: anyio.abc.TaskStatus[None] = anyio.TASK_STATUS_IGNORED, + ) -> None: + """Drive the receive loop until the underlying channel closes. + + Each inbound request is dispatched to `on_request` in its own task; + the returned dict (or raised `MCPError`) is sent back as the response. + Implementations MUST offer every inbound notification to + `on_notify_intercept` synchronously in receive order (via + `run_notify_intercept`), handing only unconsumed ones to `on_notify`. + + `task_status.started()` is called once the dispatcher is ready to + accept `send_request`/`notify` calls, so callers can use + `await tg.start(dispatcher.run, on_request, on_notify)`. + """ + ... diff --git a/venv/lib/python3.12/site-packages/mcp/shared/exceptions.py b/venv/lib/python3.12/site-packages/mcp/shared/exceptions.py new file mode 100644 index 0000000..c2a7fd4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/exceptions.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +from typing import Any, cast + +from mcp_types import INVALID_REQUEST, URL_ELICITATION_REQUIRED, ElicitRequestURLParams, ErrorData, JSONRPCError + + +class MCPDeprecationWarning(UserWarning): + """A custom deprecation warning for the MCP SDK. + + Unlike the built-in `DeprecationWarning`, this inherits from `UserWarning` so + it is shown by default, helping users discover deprecated features without + enabling warnings explicitly. + + Reference: https://sethmlarson.dev/deprecations-via-warnings-dont-work-for-python-libraries + """ + + +class MCPError(Exception): + """Exception type raised when an error arrives over an MCP connection.""" + + error: ErrorData + + def __init__(self, code: int, message: str, data: Any = None): + super().__init__(code, message, data) + if data is not None: + self.error = ErrorData(code=code, message=message, data=data) + else: + self.error = ErrorData(code=code, message=message) + + @property + def code(self) -> int: + return self.error.code + + @property + def message(self) -> str: + return self.error.message + + @property + def data(self) -> Any: + return self.error.data + + @classmethod + def from_jsonrpc_error(cls, error: JSONRPCError) -> MCPError: + return cls.from_error_data(error.error) + + @classmethod + def from_error_data(cls, error: ErrorData) -> MCPError: + return cls(code=error.code, message=error.message, data=error.data) + + def __str__(self) -> str: + return self.message + + +class NoBackChannelError(MCPError): + """Raised when a server-initiated request has no channel that can deliver it. + + Raised by `DispatchContext.send_raw_request` when its request-scoped channel + reports `TransportContext.can_send_request` as `False` (the cases are + documented on that field), and by a connection's standalone channel when it + has none; serializes to an `INVALID_REQUEST` error response. + """ + + def __init__(self, method: str): + super().__init__( + code=INVALID_REQUEST, + message=( + f"Cannot send {method!r}: this transport context has no back-channel for server-initiated requests." + ), + ) + self.method = method + + +class UrlElicitationRequiredError(MCPError): + """Specialized error for when a tool requires URL mode elicitation(s) before proceeding. + + Servers can raise this error from tool handlers to indicate that the client + must complete one or more URL elicitations before the request can be processed. + + Example: + ```python + raise UrlElicitationRequiredError([ + ElicitRequestURLParams( + message="Authorization required for your files", + url="https://example.com/oauth/authorize", + elicitation_id="auth-001" + ) + ]) + ``` + """ + + def __init__(self, elicitations: list[ElicitRequestURLParams], message: str | None = None): + """Initialize UrlElicitationRequiredError.""" + if message is None: + message = f"URL elicitation{'s' if len(elicitations) > 1 else ''} required" + + self._elicitations = elicitations + + super().__init__( + code=URL_ELICITATION_REQUIRED, + message=message, + data={"elicitations": [e.model_dump(by_alias=True, exclude_none=True) for e in elicitations]}, + ) + + @property + def elicitations(self) -> list[ElicitRequestURLParams]: + """The list of URL elicitations required before the request can proceed.""" + return self._elicitations + + @classmethod + def from_error(cls, error: ErrorData) -> UrlElicitationRequiredError: + """Reconstruct from an ErrorData received over the wire.""" + if error.code != URL_ELICITATION_REQUIRED: + raise ValueError(f"Expected error code {URL_ELICITATION_REQUIRED}, got {error.code}") + + data = cast(dict[str, Any], error.data or {}) + raw_elicitations = cast(list[dict[str, Any]], data.get("elicitations", [])) + elicitations = [ElicitRequestURLParams.model_validate(e) for e in raw_elicitations] + return cls(elicitations, error.message) diff --git a/venv/lib/python3.12/site-packages/mcp/shared/extension.py b/venv/lib/python3.12/site-packages/mcp/shared/extension.py new file mode 100644 index 0000000..283e9ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/extension.py @@ -0,0 +1,28 @@ +"""Extension-identifier grammar shared by the server and client extension surfaces.""" + +from __future__ import annotations + +import re +from typing import Any + +__all__ = ["validate_extension_identifier"] + +# Extension identifiers follow the `_meta` key grammar with a mandatory prefix +# (SEP-2133 / basic/index.mdx): dot-separated labels, each starting with a +# letter and ending with a letter or digit (hyphens interior), then `/`, then a +# name that starts and ends alphanumeric (`.`/`_`/`-` interior). +_LABEL = r"[A-Za-z](?:[A-Za-z0-9-]*[A-Za-z0-9])?" +_NAME = r"[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?" +_IDENTIFIER_RE = re.compile(rf"{_LABEL}(?:\.{_LABEL})*/{_NAME}") + + +def validate_extension_identifier(identifier: Any, *, owner: str) -> None: + """Raise `TypeError` unless `identifier` is a `vendor-prefix/name` string. + + SEP-2133 requires extension identifiers to carry a reverse-DNS prefix. + """ + if not isinstance(identifier, str) or not _IDENTIFIER_RE.fullmatch(identifier): + raise TypeError( + f"{owner}.identifier must be a `vendor-prefix/name` string " + f"(reverse-DNS prefix required), got {identifier!r}" + ) diff --git a/venv/lib/python3.12/site-packages/mcp/shared/inbound.py b/venv/lib/python3.12/site-packages/mcp/shared/inbound.py new file mode 100644 index 0000000..e33d6f5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/inbound.py @@ -0,0 +1,595 @@ +"""Inbound request classification for the modern per-request-envelope path. + +Pure module: no I/O, no transport, no `mcp.server` imports. Runs the +validation ladder against a decoded JSON-RPC body and returns either an +:class:`InboundModernRoute` (every rung passed) or an +:class:`InboundLadderRejection` (the first rung that failed). Callers map a +rejection's `code` through :data:`ERROR_CODE_HTTP_STATUS` to pick the HTTP +status. + +Also hosts the shared header-value codec and the `x-mcp-header` schema +validator so client emit and server validate read the same source of truth. +""" + +import base64 +import binascii +import re +from collections.abc import Iterable, Iterator, Mapping, Sequence +from dataclasses import dataclass +from types import MappingProxyType +from typing import Any, Final, cast + +from mcp_types import ( + CLIENT_CAPABILITIES_META_KEY, + CLIENT_INFO_META_KEY, + PROTOCOL_VERSION_META_KEY, + UnsupportedProtocolVersionErrorData, +) +from mcp_types.jsonrpc import ( + HEADER_MISMATCH, + INVALID_PARAMS, + INVALID_REQUEST, + METHOD_NOT_FOUND, + MISSING_REQUIRED_CLIENT_CAPABILITY, + PARSE_ERROR, + UNSUPPORTED_PROTOCOL_VERSION, +) +from mcp_types.version import MODERN_PROTOCOL_VERSIONS + +__all__ = [ + "ERROR_CODE_HTTP_STATUS", + "InboundLadderRejection", + "InboundModernRoute", + "MCP_METHOD_HEADER", + "MCP_NAME_HEADER", + "MCP_PARAM_HEADER_PREFIX", + "MCP_PROTOCOL_VERSION_HEADER", + "NAME_BEARING_METHODS", + "X_MCP_HEADER_KEY", + "classify_inbound_request", + "decode_header_value", + "encode_header_value", + "find_duplicated_routing_header", + "find_invalid_x_mcp_header", + "mcp_param_headers", + "unsupported_protocol_version_rejection", + "validate_mcp_param_headers", + "x_mcp_header_map", +] + +MCP_PROTOCOL_VERSION_HEADER: Final = "mcp-protocol-version" +"""Canonical lowercase name of the HTTP header carrying the MCP protocol version.""" + +MCP_METHOD_HEADER: Final = "mcp-method" +"""Canonical lowercase name of the HTTP header carrying the JSON-RPC method.""" + +MCP_NAME_HEADER: Final = "mcp-name" +"""Canonical lowercase name of the HTTP header carrying the resource name (tool/prompt/resource URI).""" + +X_MCP_HEADER_KEY: Final = "x-mcp-header" +"""JSON-Schema property annotation that designates an `Mcp-Param-*` HTTP header.""" + +NAME_BEARING_METHODS: Final[Mapping[str, str]] = MappingProxyType( + { + "tools/call": "name", + "prompts/get": "name", + "resources/read": "uri", + } +) +"""Method → params key whose value is mirrored as the `Mcp-Name` HTTP header. + +Shared by client emit (which header to send) and server validate (which body +field to compare against), so both ends agree on the field by construction. +""" + +_B64_SENTINEL = re.compile(r"^=\?base64\?(?P.*)\?=$") +# RFC 7230 token chars minus DEL; visible ASCII 0x20-0x7E is the practical bound for a header value. +_HEADER_SAFE = re.compile(r"^[\x20-\x7E]*$") +# RFC 9110 §5.6.2 token: the only characters permitted in an HTTP field name. +_RFC9110_TOKEN = re.compile(r"^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$") +# JSON-Schema types the spec permits to carry `x-mcp-header` (transports.mdx +# §Custom Headers). `number` is explicitly forbidden — float→str is not +# portable across implementations. +_X_MCP_HEADER_PRIMITIVE_TYPES: Final = frozenset({"string", "integer", "boolean"}) + +# JSON Schema 2020-12 applicator keywords whose values are themselves schema +# positions, grouped by value shape. `properties` is handled separately as the +# only keyword that preserves the statically-reachable chain; every keyword +# here drops the chain to None. Instance-data keywords (`default`, `examples`, +# `const`, `enum`) and `$ref`/`$dynamicRef` are deliberately absent so the +# walk never mistakes data for an annotation and never dereferences. +_SUBSCHEMA_SINGLE: Final = frozenset( + { + "items", + "contains", + "unevaluatedItems", + "additionalProperties", + "propertyNames", + "unevaluatedProperties", + "not", + "if", + "then", + "else", + "contentSchema", + } +) +_SUBSCHEMA_LIST: Final = frozenset({"allOf", "anyOf", "oneOf", "prefixItems"}) +_SUBSCHEMA_MAP: Final = frozenset({"patternProperties", "dependentSchemas", "$defs", "definitions"}) + + +def _walk_schema_positions(root: Any) -> Iterator[tuple[tuple[str, ...] | None, dict[str, Any]]]: + """Yield `(properties_path, schema)` for every schema position in `root`. + + `properties_path` is the chain of `properties` keys from the root to the + position, or `None` once any other applicator keyword has been crossed. + The root itself yields `()`. Only the JSON Schema 2020-12 applicators + listed above are entered; instance-data keywords are not, and `$ref` is + not dereferenced, so the walk terminates on any finite JSON value. An + explicit stack keeps the function total even on pathologically deep input. + """ + stack: list[tuple[tuple[str, ...] | None, Any]] = [((), root)] + while stack: + path, node = stack.pop() + if not isinstance(node, dict): + continue + schema = cast(dict[str, Any], node) + yield path, schema + for kw, val in schema.items(): + if kw == "properties" and isinstance(val, dict): + for name, sub in cast(dict[str, Any], val).items(): + stack.append(((*path, name) if path is not None else None, sub)) + elif kw in _SUBSCHEMA_SINGLE: + stack.append((None, val)) + elif kw in _SUBSCHEMA_LIST and isinstance(val, list): + stack.extend((None, sub) for sub in cast(list[Any], val)) + elif kw in _SUBSCHEMA_MAP and isinstance(val, dict): + stack.extend((None, sub) for sub in cast(dict[str, Any], val).values()) + + +def encode_header_value(value: str) -> str: + """Wrap `value` in the `=?base64?...?=` sentinel when it would not survive an HTTP field round-trip. + + Plain printable ASCII without leading/trailing whitespace passes verbatim; + anything else (control chars, non-ASCII, edge whitespace, or a value that + already looks like the sentinel) is base64-wrapped so the receiver can + recover the exact bytes. + """ + if _HEADER_SAFE.fullmatch(value) and value == value.strip() and not _B64_SENTINEL.fullmatch(value): + return value + return f"=?base64?{base64.b64encode(value.encode('utf-8')).decode('ascii')}?=" + + +def decode_header_value(value: str | None) -> str | None: + """Inverse of :func:`encode_header_value`. + + Returns the value verbatim unless it carries the `=?base64?...?=` sentinel, + in which case the payload is decoded as UTF-8. A malformed sentinel (bad + base64, non-canonical base64, or bad UTF-8) yields `None` so a corrupt + header never matches a body value by accident. `None` in → `None` out so + callers can pass `headers.get(...)` directly. + """ + if value is None: + return None + m = _B64_SENTINEL.fullmatch(value) + if m is None: + return value + payload = m.group("payload") + try: + decoded = base64.b64decode(payload, validate=True) + except binascii.Error: + return None + # Reject non-canonical base64 (e.g. non-zero trailing bits), which + # `validate=True` tolerates; the encoder only ever emits canonical form. + if base64.b64encode(decoded).decode("ascii") != payload: + return None + try: + return decoded.decode("utf-8") + except UnicodeDecodeError: + return None + + +def find_invalid_x_mcp_header(input_schema: Any) -> str | None: + """Return a reason string if any `x-mcp-header` annotation in `input_schema` is invalid; else `None`. + + Walks every JSON Schema 2020-12 schema position. An annotation is valid + only when it sits on a property statically reachable from the root via a + chain of pure `properties` keys, names a non-empty RFC 9110 token, is on + an integer/string/boolean property, and is case-insensitively unique + across the whole schema. A `None` / non-mapping schema has no schema + positions and returns `None`. + """ + seen: dict[str, str] = {} + for path, schema in _walk_schema_positions(input_schema): + if X_MCP_HEADER_KEY not in schema: + continue + if not path: # None (off the pure-properties chain) or () (the root itself) + return f"{X_MCP_HEADER_KEY} found at a schema position not reachable via a pure `properties` chain" + where = ".".join(path) + header = schema[X_MCP_HEADER_KEY] + # Wrong type and malformed value are distinct failures with distinct messages: the + # non-str arm returns before any interpolation, because `repr` of an arbitrary + # schema value is not total (a large `int` exceeds `sys.get_int_max_str_digits`). + if not isinstance(header, str): + return f"property {where!r}: {X_MCP_HEADER_KEY} must be a string, not {type(header).__name__}" + if not _RFC9110_TOKEN.fullmatch(header): + return f"property {where!r}: {X_MCP_HEADER_KEY} {header!r} is not an RFC 9110 token" + prop_type = schema.get("type") + if not isinstance(prop_type, str): + return ( + f"property {where!r}: {X_MCP_HEADER_KEY} is only permitted on " + f"integer/string/boolean properties (the type keyword is {type(prop_type).__name__}, not a string)" + ) + if prop_type not in _X_MCP_HEADER_PRIMITIVE_TYPES: + return ( + f"property {where!r}: {X_MCP_HEADER_KEY} is only permitted on " + f"integer/string/boolean properties (got {prop_type!r})" + ) + lower = header.lower() + if lower in seen: + return f"{X_MCP_HEADER_KEY} {header!r} on property {where!r} duplicates property {seen[lower]!r}" + seen[lower] = where + return None + + +MCP_PARAM_HEADER_PREFIX: Final = "Mcp-Param-" +"""Prefix the `x-mcp-header` token is joined to, forming the per-parameter HTTP header name.""" + + +def x_mcp_header_map(input_schema: Any) -> dict[tuple[str, ...], str]: + """Map each property carrying a valid `x-mcp-header` to its annotation token, keyed by property path. + + The key is the chain of `properties` keys from the schema root to the + annotated property; a top-level property has a one-element path, a nested + one a longer path. Call only on a schema that + :func:`find_invalid_x_mcp_header` accepts; an invalid schema yields an + undefined subset. + """ + return {path: token for path, token, _ in _annotated_positions(input_schema)} + + +def _annotated_positions(input_schema: Any) -> Iterator[tuple[tuple[str, ...], str, dict[str, Any]]]: + """Yield `(path, token, schema)` for every statically-reachable `x-mcp-header` annotation. + + Shared by client emit and server validate so both ends agree on what counts as a declared header. + """ + for path, schema in _walk_schema_positions(input_schema): + if path and isinstance(token := schema.get(X_MCP_HEADER_KEY), str): + yield path, token, schema + + +def _render_header_scalar(value: Any) -> str | None: + """Render `value` the way the client mirrors it into a header, or `None` when no rendering exists. + + Shared by emit and validate so both sides agree on what is mirrorable: + non-primitives and ints beyond CPython's int-to-str digit limit are not. + """ + if isinstance(value, bool): + return "true" if value else "false" + if not isinstance(value, str | int | float): + return None + try: + return str(value) + except ValueError: + return None + + +def mcp_param_headers(header_map: Mapping[tuple[str, ...], str], arguments: Mapping[str, Any]) -> dict[str, str]: + """Build the `Mcp-Param-*` headers a `tools/call` mirrors from its arguments. + + For each `(path, token)` in `header_map`, read the value at that property + path in `arguments` and, when it is present and not `None`, emit + `Mcp-Param-` carrying it: `bool` as `true`/`false`, other scalars via + `str`, each passed through :func:`encode_header_value` so a non-token value + is base64-wrapped. A path that hits a missing key or a non-mapping node is + skipped, matching the spec's "omit the header when no value is present", + as is a value with no header rendering. + """ + headers: dict[str, str] = {} + for path, token in header_map.items(): + value = _value_at_path(arguments, path) + if value is None or (rendered := _render_header_scalar(value)) is None: + continue + headers[f"{MCP_PARAM_HEADER_PREFIX}{token}"] = encode_header_value(rendered) + return headers + + +def _value_at_path(arguments: Mapping[str, Any], path: tuple[str, ...]) -> Any: + """Read the value at a `properties`-key path in `arguments`, or `None` if any step is missing or non-mapping.""" + node: Any = arguments + for key in path: + if not isinstance(node, Mapping): + return None + node = cast("Mapping[str, Any]", node).get(key) + return node + + +# INTERNAL_ERROR is deliberately unmapped (→ HTTP 200): the spec assigns no status to +# -32603, and whether handler-origin errors get 5xx is an open S4 question — see TODO(L66). +ERROR_CODE_HTTP_STATUS: Final[Mapping[int, int]] = MappingProxyType( + { + PARSE_ERROR: 400, + INVALID_REQUEST: 400, + INVALID_PARAMS: 400, + HEADER_MISMATCH: 400, + MISSING_REQUIRED_CLIENT_CAPABILITY: 400, + UNSUPPORTED_PROTOCOL_VERSION: 400, + METHOD_NOT_FOUND: 404, + } +) +"""HTTP status to send for a JSON-RPC `error.code`. + +Consulted for classifier-origin *and* handler-origin errors, so one table +decides the wire status regardless of where the error was produced. Unmapped +codes fall back to the caller's default (typically 200). +""" + + +@dataclass(frozen=True) +class InboundModernRoute: + """A modern-protocol request whose envelope passed every ladder rung. + + `client_info` and `client_capabilities` are the raw envelope values; the + classifier checks presence only, not shape, and `client_info` is `None` + when the (optional, SHOULD-include) key is absent. Method existence is not + a ladder rung — kernel dispatch is the single source of truth for that. + """ + + protocol_version: str + client_info: Any + client_capabilities: Any + + +@dataclass(frozen=True) +class InboundLadderRejection: + """The first ladder rung that failed, as JSON-RPC error fields.""" + + code: int + message: str + data: Any = None + + +_ROUTING_HEADER_NAMES: Final = frozenset({MCP_PROTOCOL_VERSION_HEADER, MCP_METHOD_HEADER, MCP_NAME_HEADER}) + + +def find_duplicated_routing_header(headers: Iterable[tuple[str, str]]) -> str | None: + """Name of a routing header supplied more than once in raw header lines, or `None`. + + Takes raw `(name, value)` pairs — a folded mapping hides duplicates. A + duplicate is rejected because first-copy and last-copy readers would + disagree. `Mcp-Param-*` duplicates are :func:`validate_mcp_param_headers`'s job. + """ + seen: set[str] = set() + for name, _ in headers: + key = name.lower() + if key in _ROUTING_HEADER_NAMES: + if key in seen: + return key + seen.add(key) + return None + + +def unsupported_protocol_version_rejection( + requested: str, supported_modern_versions: Sequence[str] = MODERN_PROTOCOL_VERSIONS +) -> InboundLadderRejection | None: + """The `UNSUPPORTED_PROTOCOL_VERSION` rejection for `requested`, or `None` if it is served. + + The request ladder's last rung, shared with the transport's notification arm + so both message kinds name the same `supported` list in the same words. + """ + if requested in supported_modern_versions: + return None + return InboundLadderRejection( + code=UNSUPPORTED_PROTOCOL_VERSION, + message="Unsupported protocol version", + data=UnsupportedProtocolVersionErrorData( + supported=list(supported_modern_versions), requested=requested + ).model_dump(mode="json"), + ) + + +def classify_inbound_request( + body: Mapping[str, Any], + *, + headers: Mapping[str, str] | None = None, + supported_modern_versions: Sequence[str] = MODERN_PROTOCOL_VERSIONS, +) -> InboundModernRoute | InboundLadderRejection: + """Run the modern-protocol validation ladder over a decoded JSON-RPC body. + + Rungs, in order — first failure wins: + + 1. `params._meta` is a mapping carrying the required envelope pair + (protocol version, client capabilities) → else + :data:`~mcp_types.jsonrpc.INVALID_PARAMS` naming the missing key(s) + (basic/index.mdx "Per-request protocol fields"). Client info is + optional (SHOULD-include, spec PR #3002); absent reads as `None`. + 2. When `headers` is given, `MCP-Protocol-Version` equals the envelope's + protocol version, `Mcp-Method` equals `body.method`, and — for the + methods in :data:`NAME_BEARING_METHODS` — `Mcp-Name` equals the named + body param → else :data:`~mcp_types.jsonrpc.HEADER_MISMATCH`. Runs + before the supported-version rung so a client that disagrees with itself + is told so, rather than told the body's version is unsupported. + 3. The envelope's protocol version is a string in + `supported_modern_versions` → non-string values are + :data:`~mcp_types.jsonrpc.INVALID_PARAMS` (a shape defect, not a + negotiation outcome), else + :data:`~mcp_types.jsonrpc.UNSUPPORTED_PROTOCOL_VERSION` with + `data = {"supported": [...], "requested": }`. + + Method existence is *not* a rung: kernel dispatch owns that decision so + custom-registered methods route and the answer lives in one place. + + Args: + body: The decoded JSON-RPC request mapping. Envelope shape + (`jsonrpc` / `id`) is not checked here. + headers: Transport headers keyed by lowercase name, or `None` to + skip the header rung (non-HTTP callers). + supported_modern_versions: Modern protocol revisions this server + accepts on the per-request-envelope path. + """ + try: + meta_value = body["params"]["_meta"] + except (KeyError, TypeError): + meta_value = None + if not isinstance(meta_value, Mapping): + return InboundLadderRejection( + code=INVALID_PARAMS, + message="params._meta must be an object carrying the required " + f"{PROTOCOL_VERSION_META_KEY!r} and {CLIENT_CAPABILITIES_META_KEY!r} envelope keys", + ) + meta = cast("Mapping[str, Any]", meta_value) + if missing := [key for key in (PROTOCOL_VERSION_META_KEY, CLIENT_CAPABILITIES_META_KEY) if key not in meta]: + return InboundLadderRejection( + code=INVALID_PARAMS, + message=f"params._meta is missing the required envelope key(s): {', '.join(missing)}", + ) + protocol_version: Any = meta[PROTOCOL_VERSION_META_KEY] + client_info: Any = meta.get(CLIENT_INFO_META_KEY) + client_capabilities: Any = meta[CLIENT_CAPABILITIES_META_KEY] + if headers is not None: + version_header = headers.get(MCP_PROTOCOL_VERSION_HEADER) + # Presence is checked explicitly: a null body version would otherwise + # slip the equality check (None == None) and mask the absent header. + if version_header is None or version_header != protocol_version: + return InboundLadderRejection( + code=HEADER_MISMATCH, + message=f"{MCP_PROTOCOL_VERSION_HEADER} header does not match the request envelope's protocol version", + ) + method: Any = body.get("method") + if headers.get(MCP_METHOD_HEADER) != method: + return InboundLadderRejection( + code=HEADER_MISMATCH, + message=f"{MCP_METHOD_HEADER} header does not match the request body's method", + ) + name_key = NAME_BEARING_METHODS.get(method) + if name_key is not None: + # Rung 1 already proved body["params"] is a mapping (its `_meta` is one). + body_value = cast("Mapping[str, Any]", body["params"]).get(name_key) + if body_value is not None and decode_header_value(headers.get(MCP_NAME_HEADER)) != body_value: + return InboundLadderRejection( + code=HEADER_MISMATCH, + message=f"{MCP_NAME_HEADER} header does not match the request body's {name_key!r} parameter", + ) + + if not isinstance(protocol_version, str): + # Rung 3's precondition: a shape defect, not a version-negotiation + # outcome - -32022 is the one code auto-negotiating clients do NOT + # fall back from, and the typed rung-3 payload itself requires a + # string `requested`. Sits after the header rung, which fires first + # for every header-bearing entry (an absent version header is a + # mismatch, and a present one is a string that can never equal a + # non-string body value) - so this rejection is reachable only on + # header-less transports. + return InboundLadderRejection( + code=INVALID_PARAMS, + message="the protocol-version envelope value must be a string", + ) + + if (unsupported := unsupported_protocol_version_rejection(protocol_version, supported_modern_versions)) is not None: + return unsupported + + return InboundModernRoute( + protocol_version=protocol_version, + client_info=client_info, + client_capabilities=client_capabilities, + ) + + +# Header values eligible for the spec's numeric-comparison SHOULD; scientific +# notation never compares numerically (matching the typescript-sdk's gate). +_CANONICAL_DECIMAL = re.compile(r"^-?[0-9]+(\.[0-9]+)?$") + + +def _mcp_param_value_matches(prop_type: Any, value: Any, rendered: str, decoded: str) -> bool: + """True when a decoded `Mcp-Param-*` header value agrees with the body argument. + + Integer-typed declarations with an integral body value compare numerically + (`42` matches `42.0`, the spec's SHOULD) for canonical-decimal headers — + exact, no float round-trip, so values beyond the IEEE754 safe range still + compare. Anything else compares against `rendered`, the emit-side rendering. + """ + if ( + prop_type == "integer" + and not isinstance(value, bool) + and (isinstance(value, int) or (isinstance(value, float) and value.is_integer())) + and _CANONICAL_DECIMAL.fullmatch(decoded) is not None + ): + whole, _, fraction = decoded.partition(".") + if fraction and set(fraction) != {"0"}: + return False + try: + return int(whole) == int(value) + except ValueError: + return False + return decoded == rendered + + +def validate_mcp_param_headers( + input_schema: Any, + arguments: Mapping[str, Any], + headers: Mapping[str, str], +) -> InboundLadderRejection | None: + """Compare a `tools/call` request's `Mcp-Param-*` headers against its body arguments. + + Each annotated property's header and argument must agree: present together + and equal after sentinel decoding, or absent together (`null` counts as + absent). Returns the first failure as a `HEADER_MISMATCH` rejection, else `None`. + + A header whose argument is absent or unrenderable is deliberately rejected: + the spec's purpose clause is exactly an intermediary routing on a value the + body never carried. A duplicated recognized header is rejected — first-copy + and last-copy readers would disagree. A schema :func:`find_invalid_x_mcp_header` + rejects validates nothing: conforming clients drop the tool and emit no headers. + """ + if find_invalid_x_mcp_header(input_schema) is not None: + return None + folded: dict[str, str] = {} + duplicated: set[str] = set() + for name, value in headers.items(): + key = name.lower() + if key in folded: + duplicated.add(key) + folded[key] = value + for path, token, schema in _annotated_positions(input_schema): + header_name = f"{MCP_PARAM_HEADER_PREFIX}{token}" + key = header_name.lower() + raw = folded.get(key) + value = _value_at_path(arguments, path) + argument = ".".join(path) + if raw is not None and key in duplicated: + return InboundLadderRejection( + code=HEADER_MISMATCH, + message=f"{header_name} header appears more than once", + ) + if value is None: + if raw is not None: + return InboundLadderRejection( + code=HEADER_MISMATCH, + message=f"{header_name} header is present but the request body's {argument!r} argument is absent", + ) + continue + rendered = _render_header_scalar(value) + if rendered is None: + # Unrenderable value: a conforming client omitted the header, so one claiming it can never match. + if raw is not None: + return InboundLadderRejection( + code=HEADER_MISMATCH, + message=f"{header_name} header does not match the request body's {argument!r} argument", + ) + continue + if raw is None: + return InboundLadderRejection( + code=HEADER_MISMATCH, + message=f"{header_name} header is missing but the request body's {argument!r} argument is present", + ) + decoded = decode_header_value(raw) + if decoded is None: + return InboundLadderRejection( + code=HEADER_MISMATCH, + message=f"{header_name} header carries a malformed base64 sentinel value", + ) + if not _mcp_param_value_matches(schema.get("type"), value, rendered, decoded): + return InboundLadderRejection( + code=HEADER_MISMATCH, + message=f"{header_name} header does not match the request body's {argument!r} argument", + ) + return None diff --git a/venv/lib/python3.12/site-packages/mcp/shared/jsonrpc_dispatcher.py b/venv/lib/python3.12/site-packages/mcp/shared/jsonrpc_dispatcher.py new file mode 100644 index 0000000..87bdf31 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/jsonrpc_dispatcher.py @@ -0,0 +1,836 @@ +"""JSON-RPC `Dispatcher` over the `SessionMessage` stream contract all transports speak. + +Owns request-id correlation, the receive loop, per-request task isolation, +cancellation/progress wiring, and the single exception-to-wire boundary; +methods and params are otherwise opaque strings and dicts. +""" + +from __future__ import annotations + +import contextvars +import logging +from collections.abc import Awaitable, Callable, Mapping +from dataclasses import dataclass, field +from functools import partial +from typing import Any, Generic, Literal, cast + +import anyio +import anyio.abc +import anyio.lowlevel +from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream +from mcp_types import ( + CONNECTION_CLOSED, + INTERNAL_ERROR, + INVALID_PARAMS, + REQUEST_TIMEOUT, + ErrorData, + JSONRPCError, + JSONRPCMessage, + JSONRPCNotification, + JSONRPCRequest, + JSONRPCResponse, + ProgressToken, + RequestId, +) +from opentelemetry.trace import SpanKind +from pydantic import ValidationError +from typing_extensions import TypeVar + +from mcp.shared._compat import resync_tracer +from mcp.shared._otel import inject_trace_context, otel_span +from mcp.shared._stream_protocols import ReadStream, WriteStream +from mcp.shared.dispatcher import ( + CallOptions, + DispatchContext, + Dispatcher, + OnNotify, + OnNotifyIntercept, + OnRequest, + ProgressFnT, + as_request_id, + coerce_request_id, + run_notify_intercept, +) +from mcp.shared.exceptions import MCPError, NoBackChannelError +from mcp.shared.message import ( + ClientMessageMetadata, + MessageMetadata, + ServerMessageMetadata, + SessionMessage, +) +from mcp.shared.transport_context import TransportContext + +__all__ = [ + "JSONRPCDispatcher", + "cancelled_request_id_from_params", + "handler_exception_to_error_data", + "progress_token_from_params", +] + +logger = logging.getLogger(__name__) + +_ABANDON_WRITE_TIMEOUT: float = 5 +"""Bound for courtesy-cancel writes on the abandon paths; the caller-cancel +arm shields its write, so a wedged transport would otherwise hang it uncancellably.""" + +_SHUTDOWN_WRITE_TIMEOUT: float = 1 +"""Tighter bound for the shutdown-arm error write so a wedged transport can't hold session close.""" + +TransportT = TypeVar("TransportT", bound=TransportContext, default=TransportContext) + +PeerCancelMode = Literal["interrupt", "signal"] +"""How `notifications/cancelled` is applied: `"interrupt"` (default) cancels +the handler's scope; `"signal"` only sets `ctx.cancel_requested` and lets the +handler run to completion. Either way the cancelled request is never +answered - the handler's eventual result or error is dropped, not written.""" + + +def handler_exception_to_error_data(exc: BaseException) -> ErrorData | None: + """Map a handler-raised exception to its wire `ErrorData`. + + The two rungs every dispatcher shares: an `MCPError` carries its own + `ErrorData`; a pydantic `ValidationError` is the spec's INVALID_PARAMS + with empty ``data`` (no pydantic text on the wire). Returns ``None`` for + any other exception so each caller applies its own catch-all - + `JSONRPCDispatcher` currently pins ``code=0`` for v1 compat, + the modern HTTP entry uses `INTERNAL_ERROR`. + """ + if isinstance(exc, MCPError): + return exc.error + if isinstance(exc, ValidationError): + return ErrorData(code=INVALID_PARAMS, message="Invalid request parameters", data="") + return None + + +def progress_token_from_params(params: Mapping[str, Any] | None) -> ProgressToken | None: + """Read `params._meta.progressToken`; reject bool (bool subclasses int, so True would alias 1).""" + match params: + case {"_meta": {"progressToken": str() | int() as token}} if not isinstance(token, bool): + return token + case _: + return None + + +def cancelled_request_id_from_params(params: Mapping[str, Any] | None) -> RequestId | None: + """Read `params.requestId` from a `notifications/cancelled` (`as_request_id` shape rules).""" + return as_request_id((params or {}).get("requestId")) + + +@dataclass(slots=True) +class _Pending: + """An outbound request awaiting its response.""" + + send: MemoryObjectSendStream[dict[str, Any] | ErrorData] + receive: MemoryObjectReceiveStream[dict[str, Any] | ErrorData] + on_progress: ProgressFnT | None = None + + +@dataclass(slots=True) +class _InFlight(Generic[TransportT]): + """An inbound request currently being handled.""" + + scope: anyio.CancelScope + dctx: _JSONRPCDispatchContext[TransportT] + + +@dataclass +class _JSONRPCDispatchContext(Generic[TransportT]): + """Concrete `DispatchContext` produced for each inbound JSON-RPC message.""" + + transport: TransportT + _dispatcher: JSONRPCDispatcher[TransportT] + _request_id: RequestId | None + message_metadata: MessageMetadata = None # TODO(maxisbey): remove for Context rework + """Transport-attached `SessionMessage.metadata` that the server lifts onto its request context.""" + _progress_token: ProgressToken | None = None + _closed: bool = False + cancel_requested: anyio.Event = field(default_factory=anyio.Event) + + @property + def request_id(self) -> RequestId | None: + return self._request_id + + @property + def can_send_request(self) -> bool: + return self.transport.can_send_request and not self._closed + + async def notify(self, method: str, params: Mapping[str, Any] | None, opts: CallOptions | None = None) -> None: + if self._closed: + logger.debug("dropped %s: dispatch context closed", method) + return + await self._dispatcher.notify(method, params, opts, _related_request_id=self._request_id) + + async def send_raw_request( + self, + method: str, + params: Mapping[str, Any] | None, + opts: CallOptions | None = None, + ) -> dict[str, Any]: + if not self.can_send_request: + raise NoBackChannelError(method) + return await self._dispatcher.send_raw_request(method, params, opts, _related_request_id=self._request_id) + + async def progress(self, progress: float, total: float | None = None, message: str | None = None) -> None: + if self._progress_token is None: + return + params: dict[str, Any] = {"progressToken": self._progress_token, "progress": progress} + if total is not None: + params["total"] = total + if message is not None: + params["message"] = message + await self.notify("notifications/progress", params) + + def close(self) -> None: + self._closed = True + + +def _default_transport_builder(metadata: MessageMetadata) -> TransportContext: + """The `TransportContext` for a message, honoring the transport's own verdict when it stamps one. + + A message reads as riding a full duplex pipe (`can_send_request=True`) + unless the transport that framed it says otherwise on the metadata it + attached, so a transport whose response has no room for a server request + (streamable HTTP in JSON-response mode) needs no wiring from whoever drives + its streams. + """ + can_send_request = metadata.can_send_request if isinstance(metadata, ServerMessageMetadata) else True + return TransportContext(kind="jsonrpc", can_send_request=can_send_request) + + +def _shielded_progress(fn: ProgressFnT) -> ProgressFnT: + """Wrap a user progress callback so an exception can't cancel the dispatcher's task group.""" + + async def _wrapped(progress: float, total: float | None, message: str | None) -> None: + try: + await fn(progress, total, message) + except Exception: + logger.exception("progress callback raised") + + return _wrapped + + +def _contained_notify(fn: OnNotify) -> OnNotify: + """Wrap a notification handler so it can't crash the dispatcher (same boundary as `_shielded_progress`).""" + + async def _wrapped(dctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None) -> None: + try: + await fn(dctx, method, params) + except Exception: + logger.exception("notification handler for %r raised", method) + + return _wrapped + + +@dataclass(slots=True, frozen=True) +class _OutboundPlan: + """Outbound metadata plus whether abandoning the request sends a courtesy `notifications/cancelled`.""" + + metadata: MessageMetadata + cancel_on_abandon: bool + + +def _plan_outbound(related_request_id: RequestId | None, opts: CallOptions | None) -> _OutboundPlan: + """Choose the outbound `SessionMessage.metadata` and the abandon-cancellation policy. + + `related_request_id` wins over resumption hints (they are dropped). Only + hints that actually reach the transport suppress the courtesy cancel - a + request that is neither resumable nor cancelled would leak the peer's work. + """ + opts = opts or {} + cancel_on_abandon = opts.get("cancel_on_abandon", True) + token = opts.get("resumption_token") + on_token = opts.get("on_resumption_token") + headers = opts.get("headers") + if related_request_id is not None: + if token is not None or on_token is not None: + logger.debug( + "dropping resumption hints: related_request_id %r takes precedence on metadata", related_request_id + ) + return _OutboundPlan(ServerMessageMetadata(related_request_id=related_request_id), cancel_on_abandon) + if token is not None or on_token is not None: + return _OutboundPlan( + ClientMessageMetadata(resumption_token=token, on_resumption_token_update=on_token, headers=headers), + cancel_on_abandon=False, + ) + if headers: + return _OutboundPlan(ClientMessageMetadata(headers=headers), cancel_on_abandon) + return _OutboundPlan(None, cancel_on_abandon) + + +class JSONRPCDispatcher(Dispatcher[TransportT]): + """`Dispatcher` over the `SessionMessage` stream contract. + + Explicit Protocol base so pyright checks conformance at the class definition. + """ + + def __init__( + self, + read_stream: ReadStream[SessionMessage | Exception], + write_stream: WriteStream[SessionMessage], + *, + transport_builder: Callable[[MessageMetadata], TransportT] | None = None, + peer_cancel_mode: PeerCancelMode = "interrupt", + raise_handler_exceptions: bool = False, + inline_methods: frozenset[str] = frozenset(), + on_stream_exception: Callable[[Exception], Awaitable[None]] | None = None, + ) -> None: + """Wire a dispatcher over a transport's `SessionMessage` stream pair. + + Args: + transport_builder: Builds each message's `TransportContext` from + its `SessionMessage.metadata`. + raise_handler_exceptions: Re-raise handler exceptions out of + `run()` after the error response is written. + inline_methods: Methods awaited in the read loop before the next + message is dequeued (e.g. `initialize`); an inline handler + that awaits the peer deadlocks the parked loop. + on_stream_exception: Observer for `Exception` items on the read + stream; without it they are debug-logged and dropped. Awaited + inline in the read loop, so a slow observer stalls dispatch. + """ + self._read_stream = read_stream + self._write_stream = write_stream + # With transport_builder omitted, TransportT defaults to + # TransportContext; pyright can't connect the two, hence the cast. + self._transport_builder = cast( + "Callable[[MessageMetadata], TransportT]", + transport_builder or _default_transport_builder, + ) + self._peer_cancel_mode: PeerCancelMode = peer_cancel_mode + self._raise_handler_exceptions = raise_handler_exceptions + self._inline_methods = inline_methods + self.on_stream_exception = on_stream_exception + """Observer for ``Exception`` items on the read stream. Mutable so a session can + bind it after the dispatcher is built (e.g. ``ClientSession`` routing into + ``message_handler``); only consulted inside ``run()`` so pre-enter assignment is safe.""" + + self._next_id = 0 + self._pending: dict[RequestId, _Pending] = {} + self._in_flight: dict[RequestId, _InFlight[TransportT]] = {} + self._on_notify_intercept: OnNotifyIntercept | None = None + self._tg: anyio.abc.TaskGroup | None = None + self._running = False + self._closed = False + + async def send_raw_request( + self, + method: str, + params: Mapping[str, Any] | None, + opts: CallOptions | None = None, + *, + _related_request_id: RequestId | None = None, + ) -> dict[str, Any]: + """Send a JSON-RPC request and await its response. + + `_related_request_id` is set only by `_JSONRPCDispatchContext` so that + mid-handler requests route onto the inbound request's SSE stream. + + Raises: + MCPError: Peer error response; `REQUEST_TIMEOUT` if + `opts["timeout"]` elapsed; `CONNECTION_CLOSED` if the + transport closed or the dispatcher shut down. + RuntimeError: Called before `run()`. + """ + # Post-close sends get the same CONNECTION_CLOSED contract as in-flight waiters. + if self._closed: + raise MCPError(code=CONNECTION_CLOSED, message="Connection closed") + if not self._running: + raise RuntimeError("JSONRPCDispatcher.send_raw_request called before run()") + opts = opts or {} + supplied_id = opts.get("request_id") + if supplied_id is not None: + request_id: RequestId = supplied_id + # The pending key gets the same coercion `_resolve_pending` applies + # to inbound response ids, so a supplied "7" still correlates + # whether the peer echoes "7" or 7. The wire id stays verbatim. + pending_key = coerce_request_id(request_id) + if pending_key in self._pending: + raise ValueError(f"request id {request_id!r} is already in flight") + else: + # Mint past any key a supplied id occupies: the collision error is + # reserved for the caller who actually chose the id. + request_id = self._allocate_id() + while request_id in self._pending: + request_id = self._allocate_id() + pending_key = request_id + out_params = dict(params) if params is not None else {} + out_meta = dict(out_params.get("_meta") or {}) + on_progress = opts.get("on_progress") + if on_progress is not None: + # The request id doubles as the progress token, so `_pending[token]` finds `on_progress` directly. + out_meta["progressToken"] = request_id + out_params["_meta"] = out_meta + + # buffer=1: a close signal can arrive before the waiter parks in receive(); + # a WouldBlock later just means the waiter already has its one outcome. + send, receive = anyio.create_memory_object_stream[dict[str, Any] | ErrorData](1) + pending = _Pending(send=send, receive=receive, on_progress=on_progress) + self._pending[pending_key] = pending + + plan = _plan_outbound(_related_request_id, opts) + # Spec MUST: only previously-issued requests may be cancelled. A write + # interrupted by cancellation may still have delivered (a memory-stream + # send can hand its item to the receiver and still raise), so a started + # write counts as issued: the peer ignores a cancel for an id it never + # saw, while skipping it would leak a delivered request's handler. + request_write_started = False + timeout_armed = False + + target = out_params.get("name") + span_name = f"MCP send {method}{f' {target}' if isinstance(target, str) else ''}" + # TODO(maxisbey): move the otel span + inject into an outbound + # middleware once that seam exists; the dispatcher should not own otel. + try: + with otel_span( + span_name, + kind=SpanKind.CLIENT, + attributes={"mcp.method.name": method, "jsonrpc.request.id": str(request_id)}, + ): + # SEP-414: inject W3C trace context; `_meta` stays on the wire even with a no-op tracer. + inject_trace_context(out_meta) + msg = JSONRPCRequest(jsonrpc="2.0", id=request_id, method=method, params=out_params) + # Surface a pre-existing cancellation while the request provably + # never started; past this point a cancelled write counts as issued. + await anyio.lowlevel.checkpoint_if_cancelled() + request_write_started = True + try: + await self._write(msg, plan.metadata) + except (anyio.BrokenResourceError, anyio.ClosedResourceError): + # Transport tore down before run() noticed EOF; surface the documented contract. + raise MCPError(code=CONNECTION_CLOSED, message="Connection closed") from None + with anyio.fail_after(opts.get("timeout")): + timeout_armed = True + outcome = await receive.receive() + except TimeoutError: + if not timeout_armed: + # `fail_after` arms only after the write, so this TimeoutError is the + # transport's own bounded send() failing - a transport error, not + # `opts["timeout"]` elapsing. Propagate it raw (v1 kept the write + # outside the timeout-catching try and did the same). + raise + # Courtesy cancel (spec-recommended, new vs v1) so the peer stops work; + # unshielded so an outer caller cancellation can still interrupt the write. + if plan.cancel_on_abandon: + await self._final_write( + partial( + self._cancel_outbound, + request_id, + f"timed out after {opts.get('timeout')}s", + _related_request_id, + ), + shield=False, + timeout=_ABANDON_WRITE_TIMEOUT, + describe=f"courtesy cancel for timed-out request {request_id!r}", + ) + raise MCPError(code=REQUEST_TIMEOUT, message=f"Request {method!r} timed out") from None + except anyio.get_cancelled_exc_class(): + # Caller cancelled: bare awaits re-raise here, so the shielded helper + # lets the courtesy cancel go out before we propagate. + if plan.cancel_on_abandon and request_write_started: + await self._final_write( + partial(self._cancel_outbound, request_id, "caller cancelled", _related_request_id), + shield=True, + timeout=_ABANDON_WRITE_TIMEOUT, + describe=f"courtesy cancel for caller-cancelled request {request_id!r}", + ) + raise + finally: + # Remove the waiter on every path so a late response is dropped, not leaked. + self._pending.pop(pending_key, None) + send.close() + receive.close() + + if isinstance(outcome, ErrorData): + raise MCPError(code=outcome.code, message=outcome.message, data=outcome.data) + return outcome + + async def notify( + self, + method: str, + params: Mapping[str, Any] | None, + opts: CallOptions | None = None, + *, + _related_request_id: RequestId | None = None, + ) -> None: + """Send a fire-and-forget notification. + + Fire-and-forget all the way: a post-close send or a write onto a + torn-down transport drops the notification with a debug log instead + of raising (same policy as the response writes and `ctx.notify`). + """ + if self._closed: + logger.debug("dropped %s: dispatcher closed", method) + return + # Leave `params` unset when None: with `exclude_unset=True` an explicit + # None would serialize as `"params": null`, which JSON-RPC 2.0 forbids. + if params is not None: + msg = JSONRPCNotification(jsonrpc="2.0", method=method, params=dict(params)) + else: + msg = JSONRPCNotification(jsonrpc="2.0", method=method) + try: + await self._write(msg, _plan_outbound(_related_request_id, opts).metadata) + except (anyio.BrokenResourceError, anyio.ClosedResourceError): + # Transport tore down before run() noticed EOF. + logger.debug("dropped %s: write stream closed", method) + + async def run( + self, + on_request: OnRequest, + on_notify: OnNotify, + on_notify_intercept: OnNotifyIntercept | None = None, + *, + task_status: anyio.abc.TaskStatus[None] = anyio.TASK_STATUS_IGNORED, + ) -> None: + """Drive the receive loop until the read stream closes. + + `task_status.started()` fires once `send_raw_request` is usable. + Single-shot: once the loop ends the dispatcher stays closed and cannot be restarted. + """ + self._on_notify_intercept = on_notify_intercept + try: + # LIFO exits: the write stream closes only after the task-group join, so teardown writes still land. + async with self._write_stream: + async with anyio.create_task_group() as tg: + self._tg = tg + self._running = True + task_status.started() + try: + async with self._read_stream: + try: + async for item in self._read_stream: + # Duck-typed: only `ContextReceiveStream` carries the + # sender's per-message contextvars snapshot. + sender_ctx: contextvars.Context | None = getattr( + self._read_stream, "last_context", None + ) + await self._dispatch(item, on_request, on_notify, sender_ctx) + except anyio.ClosedResourceError: + # Receive end closed under us (stateless SHTTP teardown); same as EOF. + logger.debug("read stream closed by transport; treating as EOF") + # EOF: wake blocked `send_raw_request` waiters with CONNECTION_CLOSED. + self._running = False + self._closed = True + self._fan_out_closed() + finally: + # Cancel in-flight handlers; otherwise the task-group join + # waits on handlers whose callers are already gone. + tg.cancel_scope.cancel() + finally: + # Covers cancel/crash paths that skip the inline fan-out; idempotent. + self._running = False + self._closed = True + self._tg = None + self._fan_out_closed() + await resync_tracer() + + async def _dispatch( + self, + item: SessionMessage | Exception, + on_request: OnRequest, + on_notify: OnNotify, + sender_ctx: contextvars.Context | None, + ) -> None: + """Route one inbound item. + + Only `inline_methods` requests and the `on_stream_exception` observer + are awaited; any other `await` would head-of-line block the read loop. + """ + if isinstance(item, Exception): + if self.on_stream_exception is None: + logger.debug("transport yielded exception: %r", item) + return + try: + await self.on_stream_exception(item) + except Exception: + logger.exception("on_stream_exception observer raised") + return + metadata = item.metadata + msg = item.message + match msg: + case JSONRPCRequest(): + await self._dispatch_request(msg, metadata, on_request, sender_ctx) + case JSONRPCNotification(): + self._dispatch_notification(msg, metadata, on_notify, sender_ctx) + case JSONRPCResponse(): + self._resolve_pending(msg.id, msg.result) + case JSONRPCError(): # pragma: no branch + # Exhaustive over JSONRPCMessage, so the no-match arc is unreachable. + self._resolve_pending(msg.id, msg.error) + + async def _dispatch_request( + self, + req: JSONRPCRequest, + metadata: MessageMetadata, + on_request: OnRequest, + sender_ctx: contextvars.Context | None, + ) -> None: + progress_token = progress_token_from_params(req.params) + try: + transport_ctx = self._transport_builder(metadata) + except Exception: + # A raising builder must cost only this message, not the connection. + logger.exception("transport_builder raised; rejecting request %r", req.id) + self._spawn( + self._write_error, + req.id, + ErrorData(code=INTERNAL_ERROR, message="transport context unavailable"), + sender_ctx=sender_ctx, + ) + return + dctx = _JSONRPCDispatchContext( + transport=transport_ctx, + _dispatcher=self, + _request_id=req.id, + message_metadata=metadata, + _progress_token=progress_token, + ) + scope = anyio.CancelScope() + # TODO(maxisbey): duplicate ids blind-overwrite (v1/TS parity); revisit + # rejecting with INVALID_REQUEST. Key coerced so a stringified + # `notifications/cancelled` id still correlates. + self._in_flight[coerce_request_id(req.id)] = _InFlight(scope=scope, dctx=dctx) + if req.method in self._inline_methods: + # Spawn so `sender_ctx` applies, but park the read loop until the + # handler returns - that's the inline ordering guarantee. + done = anyio.Event() + + async def _run_inline() -> None: + try: + await self._handle_request(req, dctx, scope, on_request) + finally: + done.set() + + self._spawn(_run_inline, sender_ctx=sender_ctx) + await done.wait() + else: + self._spawn(self._handle_request, req, dctx, scope, on_request, sender_ctx=sender_ctx) + + def _dispatch_notification( + self, + msg: JSONRPCNotification, + metadata: MessageMetadata, + on_notify: OnNotify, + sender_ctx: contextvars.Context | None, + ) -> None: + """Route one inbound notification. + + `notifications/cancelled` and `notifications/progress` are intercepted + here (they correlate against the `_in_flight`/`_pending` tables this + layer owns) and still teed to `on_notify` afterwards. The caller's + `on_notify_intercept` then runs in receive order; only unconsumed + notifications reach the spawned `on_notify`. + """ + if msg.method == "notifications/cancelled": + rid = cancelled_request_id_from_params(msg.params) + if rid is not None and (in_flight := self._in_flight.get(coerce_request_id(rid))) is not None: + in_flight.dctx.cancel_requested.set() + if self._peer_cancel_mode == "interrupt": + in_flight.scope.cancel() + elif msg.method == "notifications/progress": + match msg.params: + case {"progressToken": str() | int() as token, "progress": int() | float() as progress} if ( + not isinstance(token, bool) + and not isinstance(progress, bool) + and (pending := self._pending.get(coerce_request_id(token))) is not None + and pending.on_progress is not None + ): + total = msg.params.get("total") + message = msg.params.get("message") + self._spawn( + _shielded_progress(pending.on_progress), + float(progress), + float(total) if isinstance(total, int | float) else None, + message if isinstance(message, str) else None, + sender_ctx=sender_ctx, + ) + case _: + pass + if run_notify_intercept(self._on_notify_intercept, msg.method, msg.params): + return + try: + transport_ctx = self._transport_builder(metadata) + except Exception: + # Same containment as `_dispatch_request`: drop the notification, keep the loop. + logger.exception("transport_builder raised; dropping notification %r", msg.method) + return + dctx = _JSONRPCDispatchContext( + transport=transport_ctx, _dispatcher=self, _request_id=None, message_metadata=metadata + ) + self._spawn(_contained_notify(on_notify), dctx, msg.method, msg.params, sender_ctx=sender_ctx) + + def _resolve_pending(self, request_id: RequestId | None, outcome: dict[str, Any] | ErrorData) -> None: + pending = self._pending.get(coerce_request_id(request_id)) if request_id is not None else None + if pending is None: + logger.debug("dropping response for unknown/late request id %r", request_id) + return + try: + pending.send.send_nowait(outcome) + except (anyio.WouldBlock, anyio.BrokenResourceError, anyio.ClosedResourceError): + logger.debug("waiter for request id %r already gone", request_id) + + def _spawn( + self, + fn: Callable[..., Awaitable[Any]], + *args: object, + sender_ctx: contextvars.Context | None, + ) -> None: + """Schedule `fn(*args)` in the run() task group, propagating the sender's contextvars. + + ASGI middleware (auth, OTel) sets contextvars on the task that wrote the + message; `Context.run` makes the spawned handler inherit that context. + """ + assert self._tg is not None + if sender_ctx is not None: + sender_ctx.run(self._tg.start_soon, fn, *args) + else: + self._tg.start_soon(fn, *args) + + def _fan_out_closed(self) -> None: + """Wake every pending `send_raw_request` waiter with `CONNECTION_CLOSED`. + + Synchronous: callers may be inside a cancelled scope. Idempotent. + """ + closed = ErrorData(code=CONNECTION_CLOSED, message="Connection closed") + for pending in self._pending.values(): + try: + pending.send.send_nowait(closed) + except (anyio.WouldBlock, anyio.BrokenResourceError, anyio.ClosedResourceError): + pass + self._pending.clear() + + async def _handle_request( + self, + req: JSONRPCRequest, + dctx: _JSONRPCDispatchContext[TransportT], + scope: anyio.CancelScope, + on_request: OnRequest, + ) -> None: + """Run `on_request` for one inbound request and write its response. + + The single exception-to-wire boundary: handler exceptions become + `JSONRPCError` here. A request the peer cancelled is never answered + (spec: MUST NOT send further messages for it) - it settles unanswered + instead, and `_settle_unanswered` tells the transport. + """ + answer_write_started = False + handler_failure: BaseException | None = None # re-raised once the request settles + try: + with scope: + try: + result = await on_request(dctx, req.method, req.params) + finally: + # Close the back-channel and drop from `_in_flight`; no checkpoint + # since handler return, so a peer cancel can't interleave. + # Identity guard: don't evict a duplicate id's newer entry. + dctx.close() + key = coerce_request_id(req.id) + if (entry := self._in_flight.get(key)) is not None and entry.dctx is dctx: + del self._in_flight[key] + if not dctx.cancel_requested.is_set(): + # A write interrupted by cancellation may still have delivered + # (a memory-stream send can hand its item to the receiver and + # still raise), so a started answer write counts as sent below: + # peers drop late responses, while a second answer for one id + # would break JSON-RPC. + answer_write_started = True + await self._write_result(req.id, result) + except anyio.get_cancelled_exc_class(): + # Shutdown: answer the request so the peer isn't left waiting - unless + # an answer write already started (it may have reached the transport; + # prefer possibly-zero answers over possibly-two), or the peer already + # cancelled it and stopped waiting. The shielded helper is needed + # because bare awaits re-raise here. + if not answer_write_started and not dctx.cancel_requested.is_set(): + await self._final_write( + partial(self._write_error, req.id, ErrorData(code=CONNECTION_CLOSED, message="Connection closed")), + shield=True, + timeout=_SHUTDOWN_WRITE_TIMEOUT, + describe=f"shutdown error response for request {req.id!r}", + ) + raise + except Exception as e: + error = handler_exception_to_error_data(e) + if error is None: + logger.exception("handler for %r raised", req.method) + # TODO(L58): code=0 pins existing-server compat; JSON-RPC says + # INTERNAL_ERROR. Revisit per the suite's divergence entry. + error = ErrorData(code=0, message=str(e)) + if self._raise_handler_exceptions: + handler_failure = e + # A cancel silences only the wire; the failure stays as visible as before. + if not dctx.cancel_requested.is_set(): + answer_write_started = True + await self._write_error(req.id, error) + # The one place a cancelled request settles: the handler is done (any + # mode) with nothing written. A peer-interrupt cancel is absorbed at + # scope __exit__ and lands here too. + if not answer_write_started: + await self._settle_unanswered(dctx) + if handler_failure is not None: + raise handler_failure + # No `_in_flight` pop here: the inner finally covers every path, and a late pop could evict a reused id. + + def _allocate_id(self) -> int: + self._next_id += 1 + return self._next_id + + async def _write(self, message: JSONRPCMessage, metadata: MessageMetadata = None) -> None: + await self._write_stream.send(SessionMessage(message=message, metadata=metadata)) + + async def _write_result(self, request_id: RequestId, result: dict[str, Any]) -> None: + try: + await self._write(JSONRPCResponse(jsonrpc="2.0", id=request_id, result=result)) + except (anyio.BrokenResourceError, anyio.ClosedResourceError): + logger.debug("dropped result for %r: write stream closed", request_id) + + async def _write_error(self, request_id: RequestId, error: ErrorData) -> None: + try: + await self._write(JSONRPCError(jsonrpc="2.0", id=request_id, error=error)) + except (anyio.BrokenResourceError, anyio.ClosedResourceError): + logger.debug("dropped error for %r: write stream closed", request_id) + + async def _settle_unanswered(self, dctx: _JSONRPCDispatchContext[TransportT]) -> None: + """Run the transport's `on_request_unanswered` hook: this request settled with no response. + + The dispatcher writes nothing for it; a transport whose wire must still + end the request (2025-era streamable HTTP) does so from this hook. A + raising hook is contained here, like the other callback boundaries. + """ + metadata = dctx.message_metadata + if not isinstance(metadata, ServerMessageMetadata) or metadata.on_request_unanswered is None: + return + try: + await metadata.on_request_unanswered() + except (anyio.BrokenResourceError, anyio.ClosedResourceError): + logger.debug("on_request_unanswered dropped: connection closing") + except Exception: + logger.exception("on_request_unanswered hook raised") + + async def _final_write( + self, + write: Callable[[], Awaitable[None]], + *, + shield: bool, + timeout: float, + describe: str, + ) -> None: + """Attempt one last write under the shared abandon/teardown policy. + + `shield=True` is for arms already inside a cancelled scope (a bare + `await` would re-raise); the bound keeps a wedged transport write + from becoming an uncancellable hang. + """ + with anyio.move_on_after(timeout, shield=shield) as scope: + await write() + if scope.cancelled_caught: + logger.warning("%s gave up: transport write blocked", describe) + + async def _cancel_outbound(self, request_id: RequestId, reason: str, related_request_id: RequestId | None) -> None: + # Thread `related_request_id` so streamable HTTP routes the cancel onto + # the request's own SSE stream instead of a possibly-absent GET stream. + # `notify` swallows connection-state errors itself, so no guard here. + await self.notify( + "notifications/cancelled", + {"requestId": request_id, "reason": reason}, + _related_request_id=related_request_id, + ) diff --git a/venv/lib/python3.12/site-packages/mcp/shared/memory.py b/venv/lib/python3.12/site-packages/mcp/shared/memory.py new file mode 100644 index 0000000..01cab77 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/memory.py @@ -0,0 +1,33 @@ +"""In-memory transports""" + +from __future__ import annotations + +from collections.abc import AsyncGenerator +from contextlib import asynccontextmanager + +from mcp.shared._compat import resync_tracer +from mcp.shared._context_streams import ContextReceiveStream, ContextSendStream, create_context_streams +from mcp.shared.message import SessionMessage + +MessageStream = tuple[ContextReceiveStream[SessionMessage | Exception], ContextSendStream[SessionMessage | Exception]] + + +@asynccontextmanager +async def create_client_server_memory_streams() -> AsyncGenerator[tuple[MessageStream, MessageStream], None]: + """Creates a pair of bidirectional memory streams for client-server communication. + + Yields: + A tuple of (client_streams, server_streams) where each is a tuple of + (read_stream, write_stream) + """ + # Create streams for both directions + server_to_client_send, server_to_client_receive = create_context_streams[SessionMessage | Exception](1) + client_to_server_send, client_to_server_receive = create_context_streams[SessionMessage | Exception](1) + + client_streams = (server_to_client_receive, client_to_server_send) + server_streams = (client_to_server_receive, server_to_client_send) + + async with server_to_client_receive, client_to_server_send, client_to_server_receive, server_to_client_send: + yield client_streams, server_streams + # Heals caller-driven cancels; closing memory streams never suspends. + await resync_tracer() diff --git a/venv/lib/python3.12/site-packages/mcp/shared/message.py b/venv/lib/python3.12/site-packages/mcp/shared/message.py new file mode 100644 index 0000000..31e51e7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/message.py @@ -0,0 +1,63 @@ +"""Message wrapper with metadata support. + +This module defines a wrapper type that combines JSONRPCMessage with metadata +to support transport-specific features like resumability. +""" + +from collections.abc import Awaitable, Callable +from dataclasses import dataclass +from typing import Any + +from mcp_types import JSONRPCMessage, RequestId + +ResumptionToken = str + +ResumptionTokenUpdateCallback = Callable[[ResumptionToken], Awaitable[None]] + +# Callback type for closing SSE streams without terminating +CloseSSEStreamCallback = Callable[[], Awaitable[None]] + + +@dataclass +class ClientMessageMetadata: + """Metadata specific to client messages.""" + + resumption_token: ResumptionToken | None = None + on_resumption_token_update: Callable[[ResumptionToken], Awaitable[None]] | None = None + # Per-message HTTP headers (e.g. MCP-Protocol-Version, Mcp-Method) the transport should set. + headers: dict[str, str] | None = None + + +@dataclass +class ServerMessageMetadata: + """Metadata specific to server messages.""" + + related_request_id: RequestId | None = None + # Transport-specific request context (e.g. starlette Request for HTTP + # transports, None for stdio). Typed as Any because the server layer is + # transport-agnostic. + request_context: Any = None + # Callback to close SSE stream for the current request without terminating + close_sse_stream: CloseSSEStreamCallback | None = None + # Callback to close the standalone GET SSE stream (for unsolicited notifications) + close_standalone_sse_stream: CloseSSEStreamCallback | None = None + # Callback the dispatcher runs when this request settles without a response + # (e.g. it was cancelled), for a transport whose wire must still end the + # request even though no response is written. + on_request_unanswered: Callable[[], Awaitable[None]] | None = None + # The transport's verdict on whether this message's request-scoped channel + # can deliver a server-initiated request (see + # `TransportContext.can_send_request`); a transport that says nothing leaves + # it True. + can_send_request: bool = True + + +MessageMetadata = ClientMessageMetadata | ServerMessageMetadata | None + + +@dataclass +class SessionMessage: + """A message with specific metadata for transport-specific features.""" + + message: JSONRPCMessage + metadata: MessageMetadata = None diff --git a/venv/lib/python3.12/site-packages/mcp/shared/metadata_utils.py b/venv/lib/python3.12/site-packages/mcp/shared/metadata_utils.py new file mode 100644 index 0000000..b646133 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/metadata_utils.py @@ -0,0 +1,46 @@ +"""Utility functions for working with metadata in MCP types. + +These utilities are primarily intended for client-side usage to properly display +human-readable names in user interfaces in a spec-compliant way. +""" + +from mcp_types import Implementation, Prompt, Resource, ResourceTemplate, Tool + + +def get_display_name(obj: Tool | Resource | Prompt | ResourceTemplate | Implementation) -> str: + """Get the display name for an MCP object with proper precedence. + + This is a client-side utility function designed to help MCP clients display + human-readable names in their user interfaces. When servers provide a 'title' + field, it should be preferred over the programmatic 'name' field for display. + + For tools: title > annotations.title > name + For other objects: title > name + + Example: + ```python + # In a client displaying available tools + tools = await session.list_tools() + for tool in tools.tools: + display_name = get_display_name(tool) + print(f"Available tool: {display_name}") + ``` + + Args: + obj: An MCP object with name and optional title fields + + Returns: + The display name to use for UI presentation + """ + if isinstance(obj, Tool): + # Tools have special precedence: title > annotations.title > name + if hasattr(obj, "title") and obj.title is not None: + return obj.title + if obj.annotations and hasattr(obj.annotations, "title") and obj.annotations.title is not None: + return obj.annotations.title + return obj.name + else: + # All other objects: title > name + if hasattr(obj, "title") and obj.title is not None: + return obj.title + return obj.name diff --git a/venv/lib/python3.12/site-packages/mcp/shared/path_security.py b/venv/lib/python3.12/site-packages/mcp/shared/path_security.py new file mode 100644 index 0000000..abe810d --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/path_security.py @@ -0,0 +1,176 @@ +"""Filesystem path safety primitives for resource handlers. + +These functions help MCP servers reject paths that would resolve +outside the served root when extracted URI template parameters are +used in filesystem operations. They are standalone utilities usable from both the +high-level :class:`~mcp.server.mcpserver.MCPServer` and lowlevel server +implementations. + +The canonical safe pattern:: + + from mcp.shared.path_security import safe_join + + @mcp.resource("file://docs/{+path}") + def read_doc(path: str) -> str: + return safe_join("/data/docs", path).read_text(encoding="utf-8") +""" + +import string +from pathlib import Path + +__all__ = ["PathEscapeError", "contains_path_traversal", "is_absolute_path", "safe_join"] + + +class PathEscapeError(ValueError): + """Raised by :func:`safe_join` when the resolved path escapes the base.""" + + +def contains_path_traversal(value: str) -> bool: + r"""Check whether a value, treated as a relative path, escapes its origin. + + This is a **base-free** check: it does not know the sandbox root, so + it detects only whether ``..`` components would move above the + starting point. Use :func:`safe_join` when you know the root — it + additionally catches symlink escapes and absolute-path injection. + + Note: + This is a string-level check on the value as supplied. It does + not model platform-specific filesystem normalisation (e.g. Win32 + stripping of trailing dots and spaces from the final path + component). For filesystem access, use :func:`safe_join`, which + resolves through the OS and verifies containment. + + The check is component-based: ``..`` is dangerous only as a + standalone path segment, not as a substring. Both ``/`` and ``\`` + are treated as separators. + + Example:: + + >>> contains_path_traversal("a/b/c") + False + >>> contains_path_traversal("../etc") + True + >>> contains_path_traversal("a/../../b") + True + >>> contains_path_traversal("a/../b") + False + >>> contains_path_traversal("1.0..2.0") + False + >>> contains_path_traversal("..") + True + + Args: + value: A string that may be used as a filesystem path. + + Returns: + ``True`` if the path would escape its starting directory. + """ + depth = 0 + for part in value.replace("\\", "/").split("/"): + if part == "..": + depth -= 1 + if depth < 0: + return True + elif part and part != ".": + depth += 1 + return False + + +def is_absolute_path(value: str) -> bool: + r"""Check whether a value is an absolute filesystem path. + + Absolute paths are dangerous when joined onto a base: in Python, + ``Path("/data") / "/etc/passwd"`` yields ``/etc/passwd`` — the + absolute right-hand side silently discards the base. + + Detects POSIX absolute (``/foo``), Windows drive-absolute + (``C:\foo``) and drive-relative (``C:foo``), and Windows + UNC/root-relative (``\\server\share``, ``\foo``). + + Example:: + + >>> is_absolute_path("relative/path") + False + >>> is_absolute_path("/etc/passwd") + True + >>> is_absolute_path("C:\\Windows") + True + >>> is_absolute_path("") + False + + Args: + value: A string that may be used as a filesystem path. + + Returns: + ``True`` if the path is absolute on any common platform. + """ + if not value: + return False + if value[0] in ("/", "\\"): + return True + # Windows drive form: C:, C:\, C:foo (drive-relative). A drive- + # relative right-hand side discards the join base when drives + # differ, so flag it even though PureWindowsPath.is_absolute() + # is False. This means single-letter-prefixed identifiers like + # "x:y" also match — opt out via ResourceSecurity(exempt_params=). + if len(value) >= 2 and value[1] == ":" and value[0] in string.ascii_letters: + return True + return False + + +def safe_join(base: str | Path, *parts: str) -> Path: + """Join path components onto a base, rejecting escapes. + + Resolves the joined path and verifies it remains within ``base``. + This is the **gold-standard** check: it catches ``..`` traversal, + absolute-path injection, and symlink escapes that the base-free + checks cannot. + + The symlink check is point-in-time: a directory swapped for a + symlink between this call and the caller's subsequent open would not + be re-checked. Handlers serving a tree that may be modified + concurrently should additionally open with ``O_NOFOLLOW`` or use + platform path-confinement primitives. + + Example:: + + >>> safe_join("/data/docs", "readme.txt") + PosixPath('/data/docs/readme.txt') + >>> safe_join("/data/docs", "../../../etc/passwd") + Traceback (most recent call last): + ... + PathEscapeError: ... + + Args: + base: The sandbox root. May be relative; it will be resolved. + parts: Path components to join. Each is checked for null bytes + and absolute form before joining. + + Returns: + The resolved path, verified to be within ``base`` at resolution + time. + + Raises: + PathEscapeError: If any part contains a null byte, any part is + absolute, or the resolved path is not contained within the + resolved base. + """ + base_resolved = Path(base).resolve() + + for part in parts: + # Null bytes pass through Path construction but fail at the + # syscall boundary with a cryptic error. Reject here so callers + # get a clear PathEscapeError instead. + if "\0" in part: + raise PathEscapeError(f"Path component contains a null byte; refusing to join onto {base_resolved}") + # Absolute parts would silently discard everything to the left + # in Path's / operator. + if is_absolute_path(part): + raise PathEscapeError(f"Path component {part!r} is absolute; refusing to join onto {base_resolved}") + + target = base_resolved.joinpath(*parts).resolve() + + if not target.is_relative_to(base_resolved): + raise PathEscapeError(f"Path {target} escapes base {base_resolved}") + + return target diff --git a/venv/lib/python3.12/site-packages/mcp/shared/peer.py b/venv/lib/python3.12/site-packages/mcp/shared/peer.py new file mode 100644 index 0000000..14e8fe1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/peer.py @@ -0,0 +1,240 @@ +"""Typed MCP request sugar over an `Outbound`. + +`ClientPeer` wraps any `Outbound` (anything with `send_raw_request` and +`notify`) and exposes the server-to-client request methods (sampling, +elicitation, roots, ping) as typed methods. + +`ClientPeer` does no capability gating: it builds the params, calls +`send_raw_request(method, params)`, and parses the result into the typed +model. Gating (and `NoBackChannelError`) is the wrapped `Outbound`'s job. +""" + +from collections.abc import Mapping +from typing import Any, cast, overload + +from mcp_types import ( + CreateMessageRequestParams, + CreateMessageResult, + CreateMessageResultWithTools, + ElicitRequestedSchema, + ElicitRequestFormParams, + ElicitRequestURLParams, + ElicitResult, + IncludeContext, + ListRootsResult, + ModelPreferences, + RequestParams, + RequestParamsMeta, + SamplingMessage, + Tool, + ToolChoice, +) +from pydantic import BaseModel +from typing_extensions import deprecated + +from mcp.shared.dispatcher import CallOptions, Outbound +from mcp.shared.exceptions import MCPDeprecationWarning + +__all__ = ["ClientPeer", "Meta"] + +Meta = dict[str, Any] +"""Type alias for the `_meta` field carried on request/notification params.""" + + +def dump_params(model: BaseModel | None, meta: Meta | None = None) -> dict[str, Any] | None: + """Serialize a params model to a wire dict, merging `meta` into `_meta`. + + Shared by `ClientPeer` and `Connection` so every typed convenience method + gets the same `_meta` handling. `meta` keys take precedence over any + `_meta` already present on the model. + + `meta` is serialized through `RequestParams` so Python field names emit + their wire aliases: an inbound `ctx.meta` carries `progress_token` (the + key `_extract_meta` validation produces), and forwarding it outbound via + `meta=ctx.meta` must put `progressToken` back on the wire. Keys not + declared on `RequestParamsMeta` pass through unchanged. + """ + out = model.model_dump(by_alias=True, mode="json", exclude_none=True) if model is not None else None + if meta: + wire_meta = RequestParams(_meta=cast(RequestParamsMeta, meta)).model_dump(by_alias=True, mode="json")["_meta"] + out = dict(out or {}) + out["_meta"] = {**out.get("_meta", {}), **wire_meta} + return out + + +class ClientPeer: + """Typed server-to-client request methods over a wrapped `Outbound`. + + Use this when you have a bare dispatcher (or any `Outbound`) and want the + typed methods (`sample`, `elicit_form`, `elicit_url`, `list_roots`, + `ping`) without writing your own host class. + """ + + def __init__(self, outbound: Outbound) -> None: + self._outbound = outbound + + async def send_raw_request( + self, + method: str, + params: Mapping[str, Any] | None, + opts: CallOptions | None = None, + ) -> dict[str, Any]: + return await self._outbound.send_raw_request(method, params, opts) + + async def notify(self, method: str, params: Mapping[str, Any] | None, opts: CallOptions | None = None) -> None: + await self._outbound.notify(method, params, opts) + + @overload + @deprecated("The sampling capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def sample( + self, + messages: list[SamplingMessage], + *, + max_tokens: int, + system_prompt: str | None = None, + include_context: IncludeContext | None = None, + temperature: float | None = None, + stop_sequences: list[str] | None = None, + metadata: dict[str, Any] | None = None, + model_preferences: ModelPreferences | None = None, + tools: None = None, + tool_choice: None = None, + meta: Meta | None = None, + opts: CallOptions | None = None, + ) -> CreateMessageResult: ... + @overload + @deprecated("The sampling capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def sample( + self, + messages: list[SamplingMessage], + *, + max_tokens: int, + system_prompt: str | None = None, + include_context: IncludeContext | None = None, + temperature: float | None = None, + stop_sequences: list[str] | None = None, + metadata: dict[str, Any] | None = None, + model_preferences: ModelPreferences | None = None, + tools: list[Tool], + tool_choice: ToolChoice | None = None, + meta: Meta | None = None, + opts: CallOptions | None = None, + ) -> CreateMessageResultWithTools: ... + @overload + @deprecated("The sampling capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def sample( + self, + messages: list[SamplingMessage], + *, + max_tokens: int, + system_prompt: str | None = None, + include_context: IncludeContext | None = None, + temperature: float | None = None, + stop_sequences: list[str] | None = None, + metadata: dict[str, Any] | None = None, + model_preferences: ModelPreferences | None = None, + tools: list[Tool] | None = None, + tool_choice: ToolChoice, + meta: Meta | None = None, + opts: CallOptions | None = None, + ) -> CreateMessageResultWithTools: ... + @deprecated("The sampling capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def sample( + self, + messages: list[SamplingMessage], + *, + max_tokens: int, + system_prompt: str | None = None, + include_context: IncludeContext | None = None, + temperature: float | None = None, + stop_sequences: list[str] | None = None, + metadata: dict[str, Any] | None = None, + model_preferences: ModelPreferences | None = None, + tools: list[Tool] | None = None, + tool_choice: ToolChoice | None = None, + meta: Meta | None = None, + opts: CallOptions | None = None, + ) -> CreateMessageResult | CreateMessageResultWithTools: + """Send a `sampling/createMessage` request to the peer. + + Raises: + MCPError: The peer responded with an error. + NoBackChannelError: No back-channel for server-initiated requests. + pydantic.ValidationError: The peer's result does not match the expected result type. + """ + params = CreateMessageRequestParams( + messages=messages, + system_prompt=system_prompt, + include_context=include_context, + temperature=temperature, + max_tokens=max_tokens, + stop_sequences=stop_sequences, + metadata=metadata, + model_preferences=model_preferences, + tools=tools, + tool_choice=tool_choice, + ) + result = await self.send_raw_request("sampling/createMessage", dump_params(params, meta), opts) + if tools is not None or tool_choice is not None: + return CreateMessageResultWithTools.model_validate(result, by_name=False) + return CreateMessageResult.model_validate(result, by_name=False) + + async def elicit_form( + self, + message: str, + requested_schema: ElicitRequestedSchema, + *, + meta: Meta | None = None, + opts: CallOptions | None = None, + ) -> ElicitResult: + """Send a form-mode `elicitation/create` request. + + Raises: + MCPError: The peer responded with an error. + NoBackChannelError: No back-channel for server-initiated requests. + pydantic.ValidationError: The peer's result does not match the expected result type. + """ + params = ElicitRequestFormParams(message=message, requested_schema=requested_schema) + result = await self.send_raw_request("elicitation/create", dump_params(params, meta), opts) + return ElicitResult.model_validate(result, by_name=False) + + async def elicit_url( + self, + message: str, + url: str, + elicitation_id: str, + *, + meta: Meta | None = None, + opts: CallOptions | None = None, + ) -> ElicitResult: + """Send a URL-mode `elicitation/create` request. + + Raises: + MCPError: The peer responded with an error. + NoBackChannelError: No back-channel for server-initiated requests. + pydantic.ValidationError: The peer's result does not match the expected result type. + """ + params = ElicitRequestURLParams(message=message, url=url, elicitation_id=elicitation_id) + result = await self.send_raw_request("elicitation/create", dump_params(params, meta), opts) + return ElicitResult.model_validate(result, by_name=False) + + @deprecated("The roots capability is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning) + async def list_roots(self, *, meta: Meta | None = None, opts: CallOptions | None = None) -> ListRootsResult: + """Send a `roots/list` request. + + Raises: + MCPError: The peer responded with an error. + NoBackChannelError: No back-channel for server-initiated requests. + pydantic.ValidationError: The peer's result does not match the expected result type. + """ + result = await self.send_raw_request("roots/list", dump_params(None, meta), opts) + return ListRootsResult.model_validate(result, by_name=False) + + async def ping(self, *, meta: Meta | None = None, opts: CallOptions | None = None) -> None: + """Send a `ping` request and ignore the result. + + Raises: + MCPError: The peer responded with an error. + NoBackChannelError: No back-channel for server-initiated requests. + """ + await self.send_raw_request("ping", dump_params(None, meta), opts) diff --git a/venv/lib/python3.12/site-packages/mcp/shared/subscriptions.py b/venv/lib/python3.12/site-packages/mcp/shared/subscriptions.py new file mode 100644 index 0000000..30449a8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/subscriptions.py @@ -0,0 +1,111 @@ +"""Typed event vocabulary for `subscriptions/listen` (2026-07-28, SEP-2575), shared by server and client. + +Every event is a level trigger ("this changed, refetch if you care"), so both sides bound buffers by dedupe. +""" + +from __future__ import annotations + +from collections.abc import Mapping +from dataclasses import dataclass +from typing import Any + +from mcp_types import ( + NotificationParams, + PromptListChangedNotification, + ResourceListChangedNotification, + ResourceUpdatedNotification, + ResourceUpdatedNotificationParams, + ServerNotification, + SubscriptionFilter, + ToolListChangedNotification, +) + +__all__ = [ + "LISTEN_STREAM_METHODS", + "SUBSCRIPTION_ID_META_KEY", + "PromptsListChanged", + "ResourceUpdated", + "ResourcesListChanged", + "ServerEvent", + "ToolsListChanged", + "event_from_wire", + "event_matches", + "event_to_notification", +] + +SUBSCRIPTION_ID_META_KEY = "io.modelcontextprotocol/subscriptionId" +"""The `_meta` key on every listen-stream frame; the value is the `subscriptions/listen` request's JSON-RPC id.""" + + +@dataclass(frozen=True) +class ToolsListChanged: + """The server's tool list changed.""" + + +@dataclass(frozen=True) +class PromptsListChanged: + """The server's prompt list changed.""" + + +@dataclass(frozen=True) +class ResourcesListChanged: + """The server's resource list changed.""" + + +@dataclass(frozen=True) +class ResourceUpdated: + """The resource at `uri` changed and may need to be read again.""" + + uri: str + + +ServerEvent = ToolsListChanged | PromptsListChanged | ResourcesListChanged | ResourceUpdated +"""An event a server publishes for delivery to listen subscribers.""" + + +def event_to_notification(event: ServerEvent, meta: dict[str, Any]) -> ServerNotification: + """Build the stamped wire notification for `event` (the server's direction).""" + if isinstance(event, ToolsListChanged): + return ToolListChangedNotification(params=NotificationParams(_meta=meta)) + if isinstance(event, PromptsListChanged): + return PromptListChangedNotification(params=NotificationParams(_meta=meta)) + if isinstance(event, ResourcesListChanged): + return ResourceListChangedNotification(params=NotificationParams(_meta=meta)) + return ResourceUpdatedNotification(params=ResourceUpdatedNotificationParams(uri=event.uri, _meta=meta)) + + +_LIST_CHANGED_EVENTS: dict[str, ServerEvent] = { + "notifications/tools/list_changed": ToolsListChanged(), + "notifications/prompts/list_changed": PromptsListChanged(), + "notifications/resources/list_changed": ResourcesListChanged(), +} + +LISTEN_STREAM_METHODS: frozenset[str] = frozenset({*_LIST_CHANGED_EVENTS, "notifications/resources/updated"}) +"""The notification methods that ride `subscriptions/listen` streams at 2026-07-28 +(and, at that era, nowhere else): the change-notification vocabulary.""" + + +def event_from_wire(method: str, params: Mapping[str, Any] | None) -> ServerEvent | None: + """The event a raw listen-stream frame announces, or None if it carries none. + + Takes the raw wire dict: the client demultiplexes before the typed notification parse.""" + if (event := _LIST_CHANGED_EVENTS.get(method)) is not None: + return event + if method == "notifications/resources/updated": + uri = (params or {}).get("uri") + if isinstance(uri, str): + return ResourceUpdated(uri=uri) + return None + + +def event_matches(honored: SubscriptionFilter, uris: frozenset[str], event: ServerEvent) -> bool: + """Whether `event` is within the stream's honored filter (`uris`: the honored resource subscriptions as a set). + + The admission predicate both sides share: server delivery and client intake honor only what was acknowledged.""" + if isinstance(event, ToolsListChanged): + return honored.tools_list_changed is True + if isinstance(event, PromptsListChanged): + return honored.prompts_list_changed is True + if isinstance(event, ResourcesListChanged): + return honored.resources_list_changed is True + return event.uri in uris diff --git a/venv/lib/python3.12/site-packages/mcp/shared/tool_name_validation.py b/venv/lib/python3.12/site-packages/mcp/shared/tool_name_validation.py new file mode 100644 index 0000000..96c34f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/tool_name_validation.py @@ -0,0 +1,129 @@ +"""Tool name validation utilities according to SEP-986. + +Tool names SHOULD be between 1 and 128 characters in length (inclusive). +Tool names are case-sensitive. +Allowed characters: uppercase and lowercase ASCII letters (A-Z, a-z), +digits (0-9), underscore (_), dash (-), and dot (.). +Tool names SHOULD NOT contain spaces, commas, or other special characters. + +See: https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool-names +""" + +from __future__ import annotations + +import logging +import re +from dataclasses import dataclass, field + +logger = logging.getLogger(__name__) + +# Regular expression for valid tool names according to SEP-986 specification +TOOL_NAME_REGEX = re.compile(r"^[A-Za-z0-9._-]{1,128}$") + +# SEP reference URL for warning messages +SEP_986_URL = "https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool-names" + + +@dataclass +class ToolNameValidationResult: + """Result of tool name validation. + + Attributes: + is_valid: Whether the tool name conforms to SEP-986 requirements. + warnings: List of warning messages for non-conforming aspects. + """ + + is_valid: bool + warnings: list[str] = field(default_factory=lambda: []) + + +def validate_tool_name(name: str) -> ToolNameValidationResult: + """Validate a tool name according to the SEP-986 specification. + + Args: + name: The tool name to validate. + + Returns: + ToolNameValidationResult containing validation status and any warnings. + """ + warnings: list[str] = [] + + # Check for empty name + if not name: + return ToolNameValidationResult( + is_valid=False, + warnings=["Tool name cannot be empty"], + ) + + # Check length + if len(name) > 128: + return ToolNameValidationResult( + is_valid=False, + warnings=[f"Tool name exceeds maximum length of 128 characters (current: {len(name)})"], + ) + + # Check for problematic patterns (warnings, not validation failures) + if " " in name: + warnings.append("Tool name contains spaces, which may cause parsing issues") + + if "," in name: + warnings.append("Tool name contains commas, which may cause parsing issues") + + # Check for potentially confusing leading/trailing characters + if name.startswith("-") or name.endswith("-"): + warnings.append("Tool name starts or ends with a dash, which may cause parsing issues in some contexts") + + if name.startswith(".") or name.endswith("."): + warnings.append("Tool name starts or ends with a dot, which may cause parsing issues in some contexts") + + # Check for invalid characters + if not TOOL_NAME_REGEX.fullmatch(name): + # Find all invalid characters (unique, preserving order) + invalid_chars: list[str] = [] + seen: set[str] = set() + for char in name: + if not re.match(r"[A-Za-z0-9._-]", char) and char not in seen: + invalid_chars.append(char) + seen.add(char) + + warnings.append(f"Tool name contains invalid characters: {', '.join(repr(c) for c in invalid_chars)}") + warnings.append("Allowed characters are: A-Z, a-z, 0-9, underscore (_), dash (-), and dot (.)") + + return ToolNameValidationResult(is_valid=False, warnings=warnings) + + return ToolNameValidationResult(is_valid=True, warnings=warnings) + + +def issue_tool_name_warning(name: str, warnings: list[str]) -> None: + """Log warnings for non-conforming tool names. + + Args: + name: The tool name that triggered the warnings. + warnings: List of warning messages to log. + """ + if not warnings: + return + + logger.warning(f'Tool name validation warning for "{name}":') + for warning in warnings: + logger.warning(f" - {warning}") + logger.warning("Tool registration will proceed, but this may cause compatibility issues.") + logger.warning("Consider updating the tool name to conform to the MCP tool naming standard.") + logger.warning(f"See SEP-986 ({SEP_986_URL}) for more details.") + + +def validate_and_warn_tool_name(name: str) -> bool: + """Validate a tool name and issue warnings for non-conforming names. + + This is the primary entry point for tool name validation. It validates + the name and logs any warnings via the logging module. + + Args: + name: The tool name to validate. + + Returns: + True if the name is valid, False otherwise. + """ + result = validate_tool_name(name) + issue_tool_name_warning(name, result.warnings) + return result.is_valid diff --git a/venv/lib/python3.12/site-packages/mcp/shared/transport_context.py b/venv/lib/python3.12/site-packages/mcp/shared/transport_context.py new file mode 100644 index 0000000..8d15a2e --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/transport_context.py @@ -0,0 +1,45 @@ +"""Transport-specific metadata attached to each inbound message. + +`TransportContext` is the base; each transport defines its own subclass with +whatever fields make sense (HTTP request id, ASGI scope, stdio process handle, +etc.). The dispatcher passes it through opaquely; only the layers above the +dispatcher (`ServerRunner`, `Context`, user handlers) read its concrete fields. +""" + +from collections.abc import Mapping +from dataclasses import dataclass + +__all__ = ["TransportContext"] + + +@dataclass(kw_only=True, frozen=True) +class TransportContext: + """Base transport metadata for an inbound message. + + Subclass per transport and add fields as needed. Instances are immutable. + """ + + kind: str + """Short identifier for the transport (e.g. `"stdio"`, `"streamable-http"`).""" + + can_send_request: bool + """Whether this message's request-scoped channel can deliver a server-initiated request. + + `False` for any of three reasons: the response has no room (streamable + HTTP in JSON-response mode and the 2026-07-28 single-exchange entry answer + with one JSON-RPC reply), the client's reply has nowhere to land (stateless + HTTP, no session), or the protocol forbids server-initiated requests (any + 2026-07-28 connection, whose dispatch masks the flag off). `True` for a + plain duplex pipe (stdio, SSE) and stateful streamable HTTP with SSE + responses, all pre-2026-07-28. When `False`, + `DispatchContext.send_raw_request` raises `NoBackChannelError` instead of + parking a waiter no reply can reach. Says nothing about the connection's + standalone channel, which refuses separately. + """ + + headers: Mapping[str, str] | None = None + """Request headers carried by this message, when the transport has them. + + Populated by HTTP-based transports; `None` on stdio. Handlers should + None-check before use. + """ diff --git a/venv/lib/python3.12/site-packages/mcp/shared/uri_template.py b/venv/lib/python3.12/site-packages/mcp/shared/uri_template.py new file mode 100644 index 0000000..20d6fa9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/shared/uri_template.py @@ -0,0 +1,1116 @@ +"""RFC 6570 URI Templates with bidirectional support. + +Provides both expansion (template + variables → URI) and matching +(URI → variables). RFC 6570 only specifies expansion; matching is the +inverse operation needed by MCP servers to route ``resources/read`` +requests to handlers. + +Supports Levels 1-3 fully, plus Level 4 explode modifier for path-like +operators (``{/var*}``, ``{.var*}``, ``{;var*}``). The Level 4 prefix +modifier (``{var:N}``) and query-explode (``{?var*}``) are not supported. + +Matching semantics +------------------ + +Matching is not specified by RFC 6570 (§1.4 explicitly defers to regex +languages). This implementation uses a two-ended scan that never +backtracks: match time is O(n·v) where n is URI length and v is the +number of template variables. Realistic templates have v < 10, making +this effectively linear; there is no input that produces +superpolynomial time. + +A template may contain **at most one multi-segment variable** — +``{+var}``, ``{#var}``, or an explode-modified variable (``{/var*}``, +``{.var*}``, ``{;var*}``). This variable greedily consumes whatever the +surrounding bounded variables and literals do not. Two such variables +in one template are inherently ambiguous (which one gets the extra +segment?) and are rejected at parse time. So are any two variables +adjacent with no literal between them — including a variable adjacent +to the multi-segment variable: the scan has nothing to anchor the +boundary on. Operators that emit their own lead character supply that +literal themselves, so ``{+path}{.ext}`` and ``{a}{.b}`` are fine +while ``{+path}{ext}`` and ``{a}{b}`` are not. + +Bounded variables before the multi-segment variable match **lazily** +(first occurrence of the following literal); those after match +**greedily** (last occurrence of the preceding literal). Templates +without a multi-segment variable match greedily throughout, identical +to regex semantics. + +Reserved expansion ``{+var}`` leaves ``?`` and ``#`` unencoded, but +the scan stops at those characters so ``{+path}{?q}`` can separate path +from query. A value containing a literal ``?`` or ``#`` expands fine +but will not round-trip through ``match()``. +""" + +from __future__ import annotations + +import re +from collections.abc import Mapping, Sequence +from dataclasses import dataclass, field +from typing import Literal, TypeAlias, cast +from urllib.parse import quote, unquote + +__all__ = [ + "DEFAULT_MAX_TEMPLATE_LENGTH", + "DEFAULT_MAX_VARIABLES", + "DEFAULT_MAX_URI_LENGTH", + "InvalidUriTemplate", + "Operator", + "UriTemplate", + "Variable", +] + +Operator = Literal["", "+", "#", ".", "/", ";", "?", "&"] + +_OPERATORS: frozenset[str] = frozenset({"+", "#", ".", "/", ";", "?", "&"}) + +# RFC 6570 §2.3: varname = varchar *(["."] varchar), varchar = ALPHA / DIGIT / "_" +# Dots appear only between varchar groups — not consecutive, not trailing. +# (Percent-encoded varchars are technically allowed but unseen in practice.) +_VARNAME_RE = re.compile(r"^[A-Za-z0-9_]+(?:\.[A-Za-z0-9_]+)*$") + +DEFAULT_MAX_TEMPLATE_LENGTH = 8_192 +DEFAULT_MAX_VARIABLES = 256 +DEFAULT_MAX_URI_LENGTH = 65_536 + +# RFC 3986 reserved characters, kept unencoded by {+var} and {#var}. +_RESERVED = ":/?#[]@!$&'()*+,;=" + + +@dataclass(frozen=True) +class _OperatorSpec: + """Expansion behavior for a single operator (RFC 6570 §3.2, Table in §A).""" + + prefix: str + """Leading character emitted before the first variable.""" + separator: str + """Character between variables (and between exploded list items).""" + named: bool + """Emit ``name=value`` pairs (query/path-param style) rather than bare values.""" + allow_reserved: bool + """Keep reserved characters unencoded ({+var}, {#var}).""" + ifemp: str + """Suffix after a named variable whose expanded value is empty (RFC §A): '' for ;, '=' for ?/&.""" + + +_OPERATOR_SPECS: dict[Operator, _OperatorSpec] = { + "": _OperatorSpec(prefix="", separator=",", named=False, allow_reserved=False, ifemp=""), + "+": _OperatorSpec(prefix="", separator=",", named=False, allow_reserved=True, ifemp=""), + "#": _OperatorSpec(prefix="#", separator=",", named=False, allow_reserved=True, ifemp=""), + ".": _OperatorSpec(prefix=".", separator=".", named=False, allow_reserved=False, ifemp=""), + "/": _OperatorSpec(prefix="/", separator="/", named=False, allow_reserved=False, ifemp=""), + ";": _OperatorSpec(prefix=";", separator=";", named=True, allow_reserved=False, ifemp=""), + "?": _OperatorSpec(prefix="?", separator="&", named=True, allow_reserved=False, ifemp="="), + "&": _OperatorSpec(prefix="&", separator="&", named=True, allow_reserved=False, ifemp="="), +} + +# Per-operator stop characters for the linear scan. A bounded variable's +# value ends at the first occurrence of any character in its stop set, +# mirroring the character-class boundaries a regex would use but without +# the backtracking. +_STOP_CHARS: dict[Operator, str] = { + "": "/?#&,", # simple: everything structural is pct-encoded + "+": "?#", # reserved: / allowed, stop at query/fragment + "#": "", # fragment: tail of URI, nothing stops it + ".": "./?#", # label: stop at next . + "/": "/?#", # path segment: stop at next / + ";": ";/?#", # path-param value (may be empty: ;name) + "?": "&#", # query value (may be empty: ?name=) + "&": "&#", # query-cont value +} + + +class InvalidUriTemplate(ValueError): + """Raised when a URI template string is malformed or unsupported. + + Attributes: + template: The template string that failed to parse. + position: Character offset where the error was detected, or None + if the error is not tied to a specific position. + """ + + def __init__(self, message: str, *, template: str, position: int | None = None) -> None: + super().__init__(message) + self.template = template + self.position = position + + +@dataclass(frozen=True) +class Variable: + """A single variable within a URI template expression.""" + + name: str + operator: Operator + explode: bool = False + + +@dataclass +class _Expression: + """A parsed ``{...}`` expression: one operator, one or more variables.""" + + operator: Operator + variables: list[Variable] + + +_Part = str | _Expression + + +@dataclass(frozen=True) +class _Lit: + """A literal run in the flattened match-atom sequence.""" + + text: str + + +@dataclass(frozen=True) +class _Cap: + """A single-variable capture in the flattened match-atom sequence. + + ``ifemp`` marks the ``;`` operator's optional-equals quirk: ``{;id}`` + expands to ``;id=value`` or bare ``;id`` when the value is empty, so + the scan must accept both forms. + """ + + var: Variable + ifemp: bool = False + + +_Atom: TypeAlias = _Lit | _Cap + + +def _is_greedy(var: Variable) -> bool: + """Return True if this variable can span multiple path segments. + + Reserved/fragment expansion and explode variables are the only + constructs whose match range is not bounded by a single structural + delimiter. A template may contain at most one such variable. + """ + return var.explode or var.operator in ("+", "#") + + +def _is_str_sequence(value: object) -> bool: + """Check if value is a non-string sequence whose items are all strings.""" + if isinstance(value, str) or not isinstance(value, Sequence): + return False + seq = cast(Sequence[object], value) + return all(isinstance(item, str) for item in seq) + + +_PCT_TRIPLET_RE = re.compile(r"%[0-9A-Fa-f]{2}") + + +def _encode(value: str, *, allow_reserved: bool) -> str: + """Percent-encode a value per RFC 6570 §3.2.1. + + Simple expansion encodes everything except unreserved characters. + Reserved expansion (``{+var}``, ``{#var}``) additionally keeps + RFC 3986 reserved characters intact and passes through existing + ``%XX`` pct-triplets unchanged (RFC 6570 §3.2.3). A bare ``%`` not + followed by two hex digits is still encoded to ``%25``. + """ + if not allow_reserved: + return quote(value, safe="") + + # Reserved expansion: walk the string, pass through triplets as-is, + # quote the gaps between them. A bare % with no triplet lands in a + # gap and gets encoded normally. + out: list[str] = [] + last = 0 + for m in _PCT_TRIPLET_RE.finditer(value): + out.append(quote(value[last : m.start()], safe=_RESERVED)) + out.append(m.group()) + last = m.end() + out.append(quote(value[last:], safe=_RESERVED)) + return "".join(out) + + +def _expand_expression(expr: _Expression, variables: Mapping[str, str | Sequence[str]]) -> str: + """Expand a single ``{...}`` expression into its URI fragment. + + Walks the expression's variables, encoding and joining defined ones + according to the operator's spec. Undefined variables are skipped + (RFC 6570 §2.3); if all are undefined, the expression contributes + nothing (no prefix is emitted). + """ + spec = _OPERATOR_SPECS[expr.operator] + rendered: list[str] = [] + + for var in expr.variables: + if var.name not in variables: + # Undefined: skip entirely, no placeholder. + continue + + value = variables[var.name] + + # Explicit type guard: reject non-str scalars with a clear message + # rather than a confusing "not iterable" from the sequence branch. + if not isinstance(value, str) and not _is_str_sequence(value): + raise TypeError(f"Variable {var.name!r} must be str or a sequence of str, got {type(value).__name__}") + + if isinstance(value, str): + encoded = _encode(value, allow_reserved=spec.allow_reserved) + if spec.named: + rendered.append(f"{var.name}{spec.ifemp}" if value == "" else f"{var.name}={encoded}") + else: + rendered.append(encoded) + else: + # Sequence value. + items = [_encode(v, allow_reserved=spec.allow_reserved) for v in value] + if not items: + continue + if var.explode: + # Each item gets the operator's separator; named ops repeat the key. + if spec.named: + rendered.append( + spec.separator.join(f"{var.name}{spec.ifemp}" if v == "" else f"{var.name}={v}" for v in items) + ) + else: + rendered.append(spec.separator.join(items)) + else: + # Non-explode: comma-join into a single value, then apply + # ifemp to the joined result (RFC §3.2.1: behaves as if the + # value were the joined string). + joined = ",".join(items) + if spec.named: + rendered.append(f"{var.name}{spec.ifemp}" if joined == "" else f"{var.name}={joined}") + else: + rendered.append(joined) + + if not rendered: + return "" + return spec.prefix + spec.separator.join(rendered) + + +@dataclass(frozen=True) +class UriTemplate: + """A parsed RFC 6570 URI template. + + Construct via :meth:`parse`. Instances are immutable and hashable; + equality is based on the template string alone. + """ + + template: str + _parts: list[_Part] = field(repr=False, compare=False) + _variables: list[Variable] = field(repr=False, compare=False) + _prefix: list[_Atom] = field(repr=False, compare=False) + _greedy: Variable | None = field(repr=False, compare=False) + _suffix: list[_Atom] = field(repr=False, compare=False) + _query_variables: list[Variable] = field(repr=False, compare=False) + + @staticmethod + def is_template(value: str) -> bool: + """Check whether a string contains URI template expressions. + + A cheap heuristic for distinguishing concrete URIs from templates + without the cost of full parsing. Returns ``True`` if the string + contains at least one ``{...}`` pair. + + Example:: + + >>> UriTemplate.is_template("file://docs/{name}") + True + >>> UriTemplate.is_template("file://docs/readme.txt") + False + + Note: + This does not validate the template. A ``True`` result does + not guarantee :meth:`parse` will succeed. + """ + open_i = value.find("{") + return open_i != -1 and value.find("}", open_i) != -1 + + @classmethod + def parse( + cls, + template: str, + *, + max_length: int = DEFAULT_MAX_TEMPLATE_LENGTH, + max_variables: int = DEFAULT_MAX_VARIABLES, + ) -> UriTemplate: + """Parse a URI template string. + + Args: + template: An RFC 6570 URI template. + max_length: Maximum permitted length of the template string. + Guards against resource exhaustion. + max_variables: Maximum number of variables permitted across + all expressions. Counting variables rather than + ``{...}`` expressions closes the gap where a single + ``{v0,v1,...,vN}`` expression packs arbitrarily many + variables under one expression count. + + Raises: + InvalidUriTemplate: If the template is malformed, exceeds the + size limits, or uses unsupported RFC 6570 features. + """ + if len(template) > max_length: + raise InvalidUriTemplate( + f"Template exceeds maximum length of {max_length}", + template=template, + ) + + parts, variables = _parse(template, max_variables=max_variables) + + # Trailing {?...}/{&...} expressions are split off and matched as + # a query string (order-agnostic, partial, extras ignored) rather + # than via the linear scan. + path_parts, query_vars = _split_query_tail(parts) + atoms = _flatten(path_parts) + prefix, greedy, suffix = _partition_greedy(atoms, template) + + return cls( + template=template, + _parts=parts, + _variables=variables, + _prefix=prefix, + _greedy=greedy, + _suffix=suffix, + _query_variables=query_vars, + ) + + @property + def variables(self) -> list[Variable]: + """All variables in the template, in order of appearance.""" + return list(self._variables) + + @property + def variable_names(self) -> list[str]: + """All variable names in the template, in order of appearance.""" + return [v.name for v in self._variables] + + @property + def query_variable_names(self) -> frozenset[str]: + """Names of variables that :meth:`match` treats as optional query parameters. + + These are the variables in a trailing run of ``{?...}``/``{&...}`` + expressions, which are matched leniently: a URI that omits some + (or all) of them still matches, and the omitted names are simply + absent from the result. Any value bound to such a name therefore + needs a fallback for the omitted case. + + Every other variable is bound on every successful :meth:`match` + (possibly to an empty string) and is *not* in this set. That + includes a ``{&...}`` expression with no preceding ``{?...}``: it + never emits the ``?`` the lenient query split keys on, so it is + matched strictly. + """ + return frozenset(v.name for v in self._query_variables) + + def expand(self, variables: Mapping[str, str | Sequence[str]]) -> str: + """Expand the template by substituting variable values. + + String values are percent-encoded according to their operator: + simple ``{var}`` encodes reserved characters; ``{+var}`` and + ``{#var}`` leave them intact. Sequence values are joined with + commas for non-explode variables, or with the operator's + separator for explode variables. + + Example:: + + >>> t = UriTemplate.parse("file://docs/{name}") + >>> t.expand({"name": "hello world.txt"}) + 'file://docs/hello%20world.txt' + + >>> t = UriTemplate.parse("file://docs/{+path}") + >>> t.expand({"path": "src/main.py"}) + 'file://docs/src/main.py' + + >>> t = UriTemplate.parse("/search{?q,lang}") + >>> t.expand({"q": "mcp", "lang": "en"}) + '/search?q=mcp&lang=en' + + >>> t = UriTemplate.parse("/files{/path*}") + >>> t.expand({"path": ["a", "b", "c"]}) + '/files/a/b/c' + + Args: + variables: Values for each template variable. Keys must be + strings; values must be ``str`` or a sequence of ``str``. + + Returns: + The expanded URI string. + + Note: + Per RFC 6570, variables absent from the mapping are + **silently omitted**. This is the correct behavior for + optional query parameters (``{?page}`` with no page yields + no ``?page=``), but for required path segments it produces + a structurally incomplete URI. If you need all variables + present, validate before calling:: + + missing = set(t.variable_names) - variables.keys() + if missing: + raise ValueError(f"Missing: {missing}") + + Raises: + TypeError: If a value is neither ``str`` nor an iterable of + ``str``. Non-string scalars (``int``, ``None``) are not + coerced. + """ + out: list[str] = [] + for part in self._parts: + if isinstance(part, str): + out.append(part) + else: + out.append(_expand_expression(part, variables)) + return "".join(out) + + def match(self, uri: str, *, max_uri_length: int = DEFAULT_MAX_URI_LENGTH) -> dict[str, str | list[str]] | None: + """Match a concrete URI against this template and extract variables. + + This is the inverse of :meth:`expand`. The URI is matched via a + linear scan of the template and captured values are + percent-decoded. The round-trip ``match(expand({k: v})) == {k: v}`` + holds when ``v`` does not contain its operator's separator + unencoded: ``{.ext}`` with ``ext="tar.gz"`` expands to + ``.tar.gz`` but does not match — the scan stops ``ext`` at the + first ``.`` and the trailing ``.gz`` has nothing to consume it. + RFC 6570 §1.4 notes this is an inherent reversal limitation. + + Matching is structural at the URI level only: a simple ``{name}`` + will not match across a literal ``/`` in the URI (the scan stops + there), but a percent-encoded ``%2F`` that decodes to ``/`` is + accepted as part of the value. Path-safety validation belongs at + a higher layer; see :mod:`mcp.shared.path_security`. + + Example:: + + >>> t = UriTemplate.parse("file://docs/{name}") + >>> t.match("file://docs/readme.txt") + {'name': 'readme.txt'} + >>> t.match("file://docs/hello%20world.txt") + {'name': 'hello world.txt'} + + >>> t = UriTemplate.parse("file://docs/{+path}") + >>> t.match("file://docs/src/main.py") + {'path': 'src/main.py'} + + >>> t = UriTemplate.parse("/files{/path*}") + >>> t.match("/files/a/b/c") + {'path': ['a', 'b', 'c']} + + **Query parameters** (``{?q,lang}`` at the end of a template) + are matched leniently: order-agnostic, partial, and unrecognized + params are ignored. Absent params are omitted from the result so + downstream function defaults can apply:: + + >>> t = UriTemplate.parse("logs://{service}{?since,level}") + >>> t.match("logs://api") + {'service': 'api'} + >>> t.match("logs://api?level=error") + {'service': 'api', 'level': 'error'} + >>> t.match("logs://api?level=error&since=5m&utm=x") + {'service': 'api', 'since': '5m', 'level': 'error'} + + Args: + uri: A concrete URI string. + max_uri_length: Maximum permitted length of the input URI. + Oversized inputs return ``None`` without scanning, + guarding against resource exhaustion. + + Returns: + A mapping from variable names to decoded values (``str`` for + scalar variables, ``list[str]`` for explode variables), or + ``None`` if the URI does not match the template or exceeds + ``max_uri_length``. + """ + if len(uri) > max_uri_length: + return None + + if self._query_variables: + # Two-phase: scan matches the path, the query is split and + # decoded manually. Query params may be partial, reordered, + # or include extras; absent params stay absent so downstream + # defaults can apply. Fragment is stripped first since the + # template's {?...} tail never describes a fragment. + before_fragment, _, _ = uri.partition("#") + path, _, query = before_fragment.partition("?") + result = self._scan(path) + if result is None: + return None + if query: + parsed = _parse_query(query) + for var in self._query_variables: + if var.name in parsed: + result[var.name] = parsed[var.name] + return result + + return self._scan(uri) + + def _scan(self, uri: str) -> dict[str, str | list[str]] | None: + """Run the two-ended linear scan against the path portion of a URI.""" + n = len(uri) + + if self._greedy is None: + # No greedy var: the suffix IS the whole template, scanned + # right-to-left and anchored so atoms[0] matches at position 0. + suffix = _scan_suffix(self._suffix, uri, n, anchored=True) + if suffix is None: + return None + suffix_result, suffix_start = suffix + return suffix_result if suffix_start == 0 else None + + # Greedy var present. The parser rejects a capture adjacent to + # the greedy slot, so a non-empty suffix begins with a _Lit whose + # rfind-derived anchor does not depend on how far the prefix + # scans. Scan the suffix first, then give the prefix that exact + # position as its ceiling so it cannot consume past the anchor. + suffix = _scan_suffix(self._suffix, uri, n, anchored=False) + if suffix is None: + return None + suffix_result, suffix_start = suffix + prefix = _scan_prefix(self._prefix, uri, 0, suffix_start) + if prefix is None: + return None + prefix_result, prefix_end = prefix + + # Prefix consumed [0, prefix_end); suffix consumed [suffix_start, n); + # the greedy var takes the gap. The prefix scan is bounded by + # suffix_start, so this holds by construction; guard explicitly + # rather than asserting so a future regression surfaces as a + # non-match, not an exception. + if suffix_start < prefix_end: + return None # pragma: no cover - unreachable while bounds hold + middle = uri[prefix_end:suffix_start] + greedy_value = _extract_greedy(self._greedy, middle) + if greedy_value is None: + return None + + return {**prefix_result, self._greedy.name: greedy_value, **suffix_result} + + def __str__(self) -> str: + return self.template + + +def _parse_query(query: str) -> dict[str, str]: + """Parse a query string into a name→value mapping. + + Unlike ``urllib.parse.parse_qs``, this follows RFC 3986 semantics: + ``+`` is a literal sub-delim, not a space. Form-urlencoding treats + ``+`` as space for HTML form submissions, but RFC 6570 and MCP + resource URIs follow RFC 3986 where only ``%20`` encodes a space. + + Parameter names are **not** percent-decoded. RFC 6570 expansion + never encodes variable names, so a legitimate match will always + have the name in literal form. Decoding names would let + ``%74oken=evil&token=real`` shadow the real ``token`` parameter + via first-wins. + + Duplicate keys keep the first value. Pairs without ``=`` are + treated as empty-valued. + """ + result: dict[str, str] = {} + for pair in query.split("&"): + name, _, value = pair.partition("=") + if name and name not in result: + result[name] = unquote(value) + return result + + +def _extract_greedy(var: Variable, raw: str) -> str | list[str] | None: + """Decode the greedy variable's isolated middle span. + + For scalar greedy (``{+var}``, ``{#var}``) this is a stop-char + validation and a single ``unquote``. For explode variables the span + is a run of separator-delimited segments (``/a/b/c`` or + ``;keys=a;keys=b``) that is split, validated, and decoded per item. + """ + spec = _OPERATOR_SPECS[var.operator] + stops = _STOP_CHARS[var.operator] + + if not var.explode: + if any(c in stops for c in raw): + return None + return unquote(raw) + + sep = spec.separator + if not raw: + return [] + # A non-empty explode span must begin with the separator: {/a*} + # expands to "/x/y", never "x/y". The scan does not consume the + # separator itself, so it must be the first character here. + if raw[0] != sep: + return None + # Segments must not contain the operator's non-separator stop + # characters (e.g. {/path*} segments may contain neither ? nor #). + body_stops = set(stops) - {sep} + if any(c in body_stops for c in raw): + return None + + segments: list[str] = [] + prefix = f"{var.name}=" + # split()[0] is always "" because raw starts with the separator; + # subsequent empties are legitimate values ({/path*} with + # ["a","","c"] expands to /a//c). + for seg in raw.split(sep)[1:]: + if spec.named: + # Named explode emits name=value per item (or bare name + # under ; with empty value). Validate the name and strip + # the prefix before decoding. + if seg.startswith(prefix): + seg = seg[len(prefix) :] + elif seg == var.name: + seg = "" + else: + return None + segments.append(unquote(seg)) + return segments + + +def _split_query_tail(parts: list[_Part]) -> tuple[list[_Part], list[Variable]]: + """Separate trailing ``?``/``&`` expressions from the path portion. + + Lenient query matching (order-agnostic, partial, ignores extras) + applies when a template ends with one or more consecutive ``?``/``&`` + expressions and the preceding path portion contains no literal + ``?``. If the path has a literal ``?`` (e.g., ``?fixed=1{&page}``), + the URI's ``?`` split won't align with the template's expression + boundary, so the strict scan is used instead. + + Returns: + A pair ``(path_parts, query_vars)``. If lenient matching does + not apply, ``query_vars`` is empty and ``path_parts`` is the + full input. + """ + split = len(parts) + for i in range(len(parts) - 1, -1, -1): + part = parts[i] + if isinstance(part, _Expression) and part.operator in ("?", "&"): + split = i + else: + break + + if split == len(parts): + return parts, [] + + # The tail must start with a {?...} expression so that expand() + # emits a ? the URI can split on. A standalone {&page} expands + # with an & prefix, which partition("?") won't find. + first = parts[split] + assert isinstance(first, _Expression) + if first.operator != "?": + return parts, [] + + # If the path portion contains a literal ?/# or a {?...}/{#...} + # expression, lenient matching's partition("#") then partition("?") + # would strip content the path scan expects to see. Fall back to + # the strict scan. + for part in parts[:split]: + if isinstance(part, str): + if "?" in part or "#" in part: + return parts, [] + elif part.operator in ("?", "#"): + return parts, [] + + query_vars: list[Variable] = [] + for part in parts[split:]: + assert isinstance(part, _Expression) + query_vars.extend(part.variables) + + return parts[:split], query_vars + + +def _parse(template: str, *, max_variables: int) -> tuple[list[_Part], list[Variable]]: + """Split a template into an ordered sequence of literals and expressions. + + Walks the string, alternating between collecting literal runs and + parsing ``{...}`` expressions. The resulting ``parts`` sequence + preserves positional interleaving so ``match()`` and ``expand()`` can + walk it in order. + + Raises: + InvalidUriTemplate: On unclosed braces, too many expressions, or + any error surfaced by :func:`_parse_expression`. + """ + parts: list[_Part] = [] + variables: list[Variable] = [] + i = 0 + n = len(template) + + while i < n: + # Find the next expression opener from the current cursor. + brace = template.find("{", i) + + if brace == -1: + # No more expressions; everything left is a trailing literal. + parts.append(template[i:]) + break + + if brace > i: + # Literal text between cursor and the brace. + parts.append(template[i:brace]) + + end = template.find("}", brace) + if end == -1: + raise InvalidUriTemplate( + f"Unclosed expression at position {brace}", + template=template, + position=brace, + ) + + # Delegate body (between braces, exclusive) to the expression parser. + expr = _parse_expression(template, template[brace + 1 : end], brace) + parts.append(expr) + variables.extend(expr.variables) + + if len(variables) > max_variables: + raise InvalidUriTemplate( + f"Template exceeds maximum of {max_variables} variables", + template=template, + ) + + # Advance past the closing brace. + i = end + 1 + + _check_duplicate_variables(template, variables) + _check_single_query_expression(template, parts) + return parts, variables + + +def _parse_expression(template: str, body: str, pos: int) -> _Expression: + """Parse the body of a single ``{...}`` expression. + + The body is everything between the braces. It consists of an optional + leading operator character followed by one or more comma-separated + variable specifiers. Each specifier is a name with an optional + trailing ``*`` (explode modifier). + + Args: + template: The full template string, for error reporting. + body: The expression body, braces excluded. + pos: Character offset of the opening brace, for error reporting. + + Raises: + InvalidUriTemplate: On empty body, invalid variable names, or + unsupported modifiers. + """ + if not body: + raise InvalidUriTemplate(f"Empty expression at position {pos}", template=template, position=pos) + + # Peel off the operator, if any. Membership check justifies the cast. + operator: Operator = "" + if body[0] in _OPERATORS: + operator = cast(Operator, body[0]) + body = body[1:] + if not body: + raise InvalidUriTemplate( + f"Expression has operator but no variables at position {pos}", + template=template, + position=pos, + ) + + # Remaining body is comma-separated variable specs: name[*] + variables: list[Variable] = [] + for spec in body.split(","): + if ":" in spec: + raise InvalidUriTemplate( + f"Prefix modifier {{var:N}} is not supported (in {spec!r} at position {pos})", + template=template, + position=pos, + ) + + explode = spec.endswith("*") + name = spec[:-1] if explode else spec + + if not _VARNAME_RE.fullmatch(name): + raise InvalidUriTemplate( + f"Invalid variable name {name!r} at position {pos}", + template=template, + position=pos, + ) + + # Explode only makes sense for operators that repeat a separator. + # Simple/reserved/fragment have no per-item separator; query-explode + # needs order-agnostic dict matching which we don't support yet. + if explode and operator in ("", "+", "#", "?", "&"): + raise InvalidUriTemplate( + f"Explode modifier on {{{operator}{name}*}} is not supported for matching", + template=template, + position=pos, + ) + + variables.append(Variable(name=name, operator=operator, explode=explode)) + + return _Expression(operator=operator, variables=variables) + + +def _check_duplicate_variables(template: str, variables: list[Variable]) -> None: + """Reject templates that use the same variable name more than once. + + RFC 6570 requires repeated variables to expand to the same value, + which would require backreference matching with potentially + exponential cost. Rather than silently returning only the last + captured value, we reject at parse time. + + Raises: + InvalidUriTemplate: If any variable name appears more than once. + """ + seen: set[str] = set() + for var in variables: + if var.name in seen: + raise InvalidUriTemplate( + f"Variable {var.name!r} appears more than once; repeated variables are not supported", + template=template, + ) + seen.add(var.name) + + +def _check_single_query_expression(template: str, parts: list[_Part]) -> None: + """Reject templates with more than one ``{?...}`` expression. + + The ``?`` operator emits a leading ``?``, so two such expressions + expand to a URI with two ``?`` characters — malformed per RFC 3986 + §3.4. Use ``{?a,b}`` or ``{?a}{&b}`` for multiple query parameters. + """ + seen = False + for part in parts: + if isinstance(part, _Expression) and part.operator == "?": + if seen: + raise InvalidUriTemplate( + "Template contains more than one {?...} expression; " + "use {?a,b} or {?a}{&b} for multiple query parameters", + template=template, + ) + seen = True + + +def _flatten(parts: list[_Part]) -> list[_Atom]: + """Lower expressions into a flat sequence of literals and single-variable captures. + + Operator prefixes and separators become explicit ``_Lit`` atoms so + the scan only ever sees two atom kinds. Adjacent literals are + coalesced so that anchor-finding (``find``/``rfind``) operates on + the longest possible literal, reducing false matches. + + Explode variables emit no lead literal: the explode capture + includes its own separator-prefixed repetitions (``{/a*}`` → + ``/x/y/z``, not ``/`` then ``x/y/z``). + """ + atoms: list[_Atom] = [] + + def push_lit(text: str) -> None: + if not text: + return + if atoms and isinstance(atoms[-1], _Lit): + atoms[-1] = _Lit(atoms[-1].text + text) + else: + atoms.append(_Lit(text)) + + for part in parts: + if isinstance(part, str): + push_lit(part) + continue + spec = _OPERATOR_SPECS[part.operator] + for i, var in enumerate(part.variables): + lead = spec.prefix if i == 0 else spec.separator + if var.explode: + atoms.append(_Cap(var)) + elif spec.named: + # ; uses ifemp (bare name when empty); ? and & always + # emit name= so the equals is part of the literal. + if part.operator == ";": + push_lit(f"{lead}{var.name}") + atoms.append(_Cap(var, ifemp=True)) + else: + push_lit(f"{lead}{var.name}=") + atoms.append(_Cap(var)) + else: + push_lit(lead) + atoms.append(_Cap(var)) + return atoms + + +def _partition_greedy(atoms: list[_Atom], template: str) -> tuple[list[_Atom], Variable | None, list[_Atom]]: + """Split atoms at the single greedy variable, if any. + + Returns ``(prefix, greedy_var, suffix)``. If there is no greedy + variable the entire atom list is returned as the suffix so that + the right-to-left scan (which matches regex-greedy semantics) + handles it. + + Raises: + InvalidUriTemplate: If two variables are adjacent with no + literal between them — whether or not one is the + multi-segment variable, the scan has nothing to anchor the + boundary on — or if more than one multi-segment variable + is present (two are inherently ambiguous: there is no + principled way to decide which one absorbs an extra + segment). + """ + greedy_idx: int | None = None + prev: _Atom | None = None + for i, atom in enumerate(atoms): + if isinstance(atom, _Cap): + if isinstance(prev, _Cap): + raise InvalidUriTemplate( + f"Variables {prev.var.name!r} and {atom.var.name!r} are adjacent " + "with no literal separator; matching cannot determine where one " + "ends and the other begins. Add a literal between them or use a " + "single variable.", + template=template, + ) + if _is_greedy(atom.var): + if greedy_idx is not None: + raise InvalidUriTemplate( + "Template contains more than one multi-segment variable " + "({+var}, {#var}, or explode modifier); matching would be ambiguous", + template=template, + ) + greedy_idx = i + prev = atom + if greedy_idx is None: + return [], None, atoms + greedy = atoms[greedy_idx] + assert isinstance(greedy, _Cap) + return atoms[:greedy_idx], greedy.var, atoms[greedy_idx + 1 :] + + +def _scan_suffix( + atoms: Sequence[_Atom], uri: str, end: int, *, anchored: bool +) -> tuple[dict[str, str | list[str]], int] | None: + """Scan atoms right-to-left from ``end``, returning captures and start position. + + Each bounded variable takes the minimum span that lets its + preceding literal match (found via ``rfind``), which makes the + *first* variable in template order greedy — identical to Python + regex semantics for a sequence of greedy groups. + + When ``anchored`` is true the atom sequence is the entire template + (no greedy variable), so ``atoms[0]`` must match at URI position 0 + rather than at its rightmost occurrence. + """ + result: dict[str, str | list[str]] = {} + pos = end + i = len(atoms) - 1 + while i >= 0: + atom = atoms[i] + if isinstance(atom, _Lit): + n = len(atom.text) + if pos < n or uri[pos - n : pos] != atom.text: + return None + pos -= n + i -= 1 + continue + + var = atom.var + stops = _STOP_CHARS[var.operator] + prev = atoms[i - 1] if i > 0 else None + + if atom.ifemp: + # ;name or ;name=value. The preceding _Lit is ";name". + # Try empty first: if the lit ends at pos the value is + # absent (RFC ifemp). Otherwise require =value. + assert isinstance(prev, _Lit) + if uri.endswith(prev.text, 0, pos): + result[var.name] = "" + i -= 1 + continue + earliest = pos + while earliest > 0 and uri[earliest - 1] not in stops: + earliest -= 1 + eq = uri.find("=", earliest, pos) + if eq == -1: + return None + result[var.name] = unquote(uri[eq + 1 : pos]) + pos = eq + i -= 1 + continue + + # Earliest valid start: the var cannot extend left past any + # stop-char, so scan backward to find that boundary. + earliest = pos + while earliest > 0 and uri[earliest - 1] not in stops: + earliest -= 1 + + if prev is None: + start = earliest + else: + # prev is a _Lit: the parser rejects two adjacent captures, + # so the only possible neighbour kind is a literal. + assert isinstance(prev, _Lit) + if anchored and i - 1 == 0: + # First atom of the whole template: positionally fixed at + # 0, not rightmost occurrence. rfind would land inside the + # value when the literal repeats there (e.g. "prefix-{id}" + # against "prefix-prefix-123"). + start = len(prev.text) + if start < earliest or start > pos: + return None + else: + # Rightmost occurrence of the preceding literal whose end + # falls within the var's valid range. + idx = uri.rfind(prev.text, 0, pos) + if idx == -1 or idx + len(prev.text) < earliest: + return None + start = idx + len(prev.text) + + result[var.name] = unquote(uri[start:pos]) + pos = start + i -= 1 + return result, pos + + +def _scan_prefix( + atoms: Sequence[_Atom], uri: str, start: int, limit: int +) -> tuple[dict[str, str | list[str]], int] | None: + """Scan atoms left-to-right from ``start``, not exceeding ``limit``. + + Each bounded variable takes the minimum span that lets its + following literal match (found via ``find``), leaving the + greedy variable as much of the URI as possible. + """ + result: dict[str, str | list[str]] = {} + pos = start + for i, atom in enumerate(atoms): + if isinstance(atom, _Lit): + end = pos + len(atom.text) + if end > limit or uri[pos:end] != atom.text: + return None + pos = end + continue + + var = atom.var + stops = _STOP_CHARS[var.operator] + # Every capture here is followed by a literal: the parser rejects + # two adjacent captures, and a capture at the END of the prefix + # would be adjacent to the greedy variable. + nxt = atoms[i + 1] + assert isinstance(nxt, _Lit) + + if atom.ifemp: + # RFC §3.2.7 ifemp: ;name=val for non-empty, bare ;name for + # empty. Decide which form is present without falling through + # to the stop-char scan when the value is empty. + if uri.startswith(nxt.text, pos): + # Following literal begins immediately: value is empty. + # Checked before '=' so a literal that itself starts + # with '=' is not mistaken for the ifemp separator. + result[var.name] = "" + continue + if pos < limit and uri[pos] == "=": + pos += 1 # value follows; fall through to the scan + else: + # The following literal does not start here and there is + # no '=': the URI's name continued past the template's + # (e.g. ;keys vs ;key) — no parse. + return None + + # Latest valid end: the var stops at the first stop-char or + # the scan limit, whichever comes first. + latest = pos + while latest < limit and uri[latest] not in stops: + latest += 1 + + # First occurrence of the following literal: the capture takes + # the minimum span, leaving the greedy variable as much of the + # URI as possible. The search window's upper bound already + # forces any hit to start at or before ``latest``, so the var + # never extends past a stop-char. + end = uri.find(nxt.text, pos, latest + len(nxt.text)) + if end == -1: + return None + + result[var.name] = unquote(uri[pos:end]) + pos = end + return result, pos diff --git a/venv/lib/python3.12/site-packages/mcp/types/__init__.py b/venv/lib/python3.12/site-packages/mcp/types/__init__.py new file mode 100644 index 0000000..9bdfa06 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/types/__init__.py @@ -0,0 +1,31 @@ +"""The MCP protocol wire types, as the `mcp.types` namespace. + +This module mirrors the standalone `mcp_types` package exactly (every name is the +same object), so SDK users can keep the familiar v1 spelling: + + import mcp.types as types + + types.TextContent(type="text", text="hi") + +The `mcp.types.jsonrpc`, `mcp.types.methods`, and `mcp.types.version` +submodules mirror `mcp_types.jsonrpc`, `mcp_types.methods`, and +`mcp_types.version` the same way, so every supported `mcp_types` import has +an `mcp.types` spelling. + +Depend on and import `mcp_types` directly instead when you only need to +(de)serialize MCP traffic and don't want the SDK's transport stack: its only +runtime dependencies are `pydantic` and `typing-extensions`. +""" + +# A wildcard mirror of the mcp_types namespace is the whole point of this module. +# pyright: reportWildcardImportFromLibrary=false + +from mcp_types import * +from mcp_types import __all__ as __all__ + +# Bind the mirror submodules on the package, so `mcp.types.version.X` is as +# reachable by attribute access as `mcp_types.version.X` (whose `__init__` +# binds `.version` by importing from it), not only via `from ... import`. +from . import jsonrpc as jsonrpc +from . import methods as methods +from . import version as version diff --git a/venv/lib/python3.12/site-packages/mcp/types/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/types/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..354e16d Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/types/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/types/__pycache__/jsonrpc.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/types/__pycache__/jsonrpc.cpython-312.pyc new file mode 100644 index 0000000..adab893 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/types/__pycache__/jsonrpc.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/types/__pycache__/methods.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/types/__pycache__/methods.cpython-312.pyc new file mode 100644 index 0000000..766ddf7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/types/__pycache__/methods.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/types/__pycache__/version.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp/types/__pycache__/version.cpython-312.pyc new file mode 100644 index 0000000..32503d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp/types/__pycache__/version.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp/types/jsonrpc.py b/venv/lib/python3.12/site-packages/mcp/types/jsonrpc.py new file mode 100644 index 0000000..a76b74e --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/types/jsonrpc.py @@ -0,0 +1,13 @@ +"""The JSON-RPC 2.0 message and error types, as the `mcp.types.jsonrpc` namespace. + +A mirror of `mcp_types.jsonrpc` (every name is the same object), so code that +depends on `mcp` can import from `mcp.types.jsonrpc` without importing the +`mcp_types` distribution directly. Depend on and import `mcp_types.jsonrpc` +instead when you use `mcp-types` without the SDK. +""" + +# A wildcard mirror of the mcp_types.jsonrpc namespace is the whole point of this module. +# pyright: reportWildcardImportFromLibrary=false + +from mcp_types.jsonrpc import * +from mcp_types.jsonrpc import __all__ as __all__ diff --git a/venv/lib/python3.12/site-packages/mcp/types/methods.py b/venv/lib/python3.12/site-packages/mcp/types/methods.py new file mode 100644 index 0000000..4b01fcc --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/types/methods.py @@ -0,0 +1,13 @@ +"""The MCP method registry, as the `mcp.types.methods` namespace. + +A mirror of `mcp_types.methods` (every name is the same object), so code that +depends on `mcp` can import from `mcp.types.methods` without importing the +`mcp_types` distribution directly. Depend on and import `mcp_types.methods` +instead when you use `mcp-types` without the SDK. +""" + +# A wildcard mirror of the mcp_types.methods namespace is the whole point of this module. +# pyright: reportWildcardImportFromLibrary=false + +from mcp_types.methods import * +from mcp_types.methods import __all__ as __all__ diff --git a/venv/lib/python3.12/site-packages/mcp/types/version.py b/venv/lib/python3.12/site-packages/mcp/types/version.py new file mode 100644 index 0000000..857c53f --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp/types/version.py @@ -0,0 +1,13 @@ +"""The protocol-version registry, as the `mcp.types.version` namespace. + +A mirror of `mcp_types.version` (every name is the same object), so code that +depends on `mcp` can write `from mcp.types.version import LATEST_MODERN_VERSION` +without importing the `mcp_types` distribution directly. Depend on and import +`mcp_types.version` instead when you use `mcp-types` without the SDK. +""" + +# A wildcard mirror of the mcp_types.version namespace is the whole point of this module. +# pyright: reportWildcardImportFromLibrary=false + +from mcp_types.version import * +from mcp_types.version import __all__ as __all__ diff --git a/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/METADATA b/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/METADATA new file mode 100644 index 0000000..44850c7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/METADATA @@ -0,0 +1,43 @@ +Metadata-Version: 2.4 +Name: mcp-types +Version: 2.1.1 +Summary: Model Context Protocol wire types +Project-URL: Homepage, https://modelcontextprotocol.io +Project-URL: Documentation, https://py.sdk.modelcontextprotocol.io/ +Project-URL: Repository, https://github.com/modelcontextprotocol/python-sdk +Project-URL: Issues, https://github.com/modelcontextprotocol/python-sdk/issues +Author: Model Context Protocol a Series of LF Projects, LLC. +Maintainer-email: David Soria Parra , Marcelo Trylesinski , Max Isbey , Felix Weinberger +License: MIT +License-File: LICENSE +Keywords: automation,llm,mcp +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.10 +Requires-Dist: pydantic>=2.12.0 +Requires-Dist: typing-extensions>=4.13.0 +Description-Content-Type: text/markdown + +# MCP Types + +The wire types for the [Model Context Protocol](https://modelcontextprotocol.io). + +This package holds the protocol message models, JSON-RPC envelope types, per-version +surface validators, and the protocol-version registry. Its only runtime dependencies are +`pydantic` and `typing-extensions`, so it can be installed on its own when you need to +(de)serialize MCP traffic without pulling in the full `mcp` SDK. + +```python +from mcp_types import Tool, CallToolRequest +from mcp_types.version import LATEST_PROTOCOL_VERSION +``` + +The `mcp` package re-exports these names, so existing `from mcp import Tool` imports +keep working. diff --git a/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/RECORD new file mode 100644 index 0000000..a9baf92 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/RECORD @@ -0,0 +1,22 @@ +mcp_types-2.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +mcp_types-2.1.1.dist-info/METADATA,sha256=iltVtVz9FHb9UX5FSpNkZ2CyzU1NItoQ3vjlb-TCrtM,1914 +mcp_types-2.1.1.dist-info/RECORD,, +mcp_types-2.1.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87 +mcp_types-2.1.1.dist-info/licenses/LICENSE,sha256=XhPbvB0SD8KgPOzefJFCSuLX3hG2PVje0vRDHiYe5Q0,1071 +mcp_types/__init__.py,sha256=eR3nUuX5XKk8OASZVhzLO2t4die9NOYqPp8yF2JYOS8,11548 +mcp_types/__pycache__/__init__.cpython-312.pyc,, +mcp_types/__pycache__/_types.cpython-312.pyc,, +mcp_types/__pycache__/_wire_base.cpython-312.pyc,, +mcp_types/__pycache__/jsonrpc.cpython-312.pyc,, +mcp_types/__pycache__/methods.cpython-312.pyc,, +mcp_types/__pycache__/version.cpython-312.pyc,, +mcp_types/_types.py,sha256=DQcAe_UFLF_hI9bxKt2uj9zU6r_Ss6L9Y5w6R2dbfZg,79979 +mcp_types/_v2025_11_25/__init__.py,sha256=pDUUNXEBcuQ64cRsLEpj9jF5IEUUoEm4Q60Mu86haqw,104143 +mcp_types/_v2025_11_25/__pycache__/__init__.cpython-312.pyc,, +mcp_types/_v2026_07_28/__init__.py,sha256=RNt1DinD9pt6nWIUnmdsuqEZIwult5xS6Uw70VhKT-A,121356 +mcp_types/_v2026_07_28/__pycache__/__init__.cpython-312.pyc,, +mcp_types/_wire_base.py,sha256=Xcy2Q4X8UIPLK8ynK790a0HvI5XVkfR4TuqdVDhVM7Y,315 +mcp_types/jsonrpc.py,sha256=wtG8HQfF68dZeRnk9lYRw4h9UEEPEVtKSdeRj2j_v4k,4485 +mcp_types/methods.py,sha256=6qC_exE-3X7QkaCnkoFWsapQI9Y8XEkaSt0fxiRooQ0,36220 +mcp_types/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +mcp_types/version.py,sha256=4kTAuw3ad0tQr0ovGiFFvmUReU_f1j5qbHr-YO5i5R4,2785 diff --git a/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/WHEEL b/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/WHEEL new file mode 100644 index 0000000..b1b94fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.29.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/licenses/LICENSE new file mode 100644 index 0000000..3d48435 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp_types-2.1.1.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Anthropic, PBC + +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. diff --git a/venv/lib/python3.12/site-packages/mcp_types/__init__.py b/venv/lib/python3.12/site-packages/mcp_types/__init__.py new file mode 100644 index 0000000..41cec2e --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp_types/__init__.py @@ -0,0 +1,453 @@ +"""This module defines the types for the MCP protocol. + +Check the latest schema at: +https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/draft/schema.json +""" + +# Re-export everything from _types for backward compatibility +from mcp_types._types import ( + CLIENT_CAPABILITIES_META_KEY, + CLIENT_INFO_META_KEY, + CORE_RESULT_TYPES, + DEFAULT_NEGOTIATED_VERSION, + LOG_LEVEL_META_KEY, + PROTOCOL_VERSION_META_KEY, + SERVER_INFO_META_KEY, + Annotations, + AudioContent, + BaseMetadata, + BlobResourceContents, + CacheableResult, + CallToolRequest, + CallToolRequestParams, + CallToolResult, + CancelledNotification, + CancelledNotificationParams, + CancelTaskRequest, + CancelTaskRequestParams, + CancelTaskResult, + ClientCapabilities, + ClientNotification, + ClientRequest, + ClientResult, + ClientTasksCapability, + ClientTasksRequestsCapability, + CompleteRequest, + CompleteRequestParams, + CompleteResult, + Completion, + CompletionArgument, + CompletionContext, + CompletionsCapability, + ContentBlock, + CreateMessageRequest, + CreateMessageRequestParams, + CreateMessageResult, + CreateMessageResultWithTools, + CreateTaskResult, + DiscoverRequest, + DiscoverResult, + ElicitationCapability, + ElicitationRequiredErrorData, + ElicitCompleteNotification, + ElicitCompleteNotificationParams, + ElicitRequest, + ElicitRequestedSchema, + ElicitRequestFormParams, + ElicitRequestParams, + ElicitRequestURLParams, + ElicitResult, + EmbeddedResource, + EmptyResult, + FormElicitationCapability, + GetPromptRequest, + GetPromptRequestParams, + GetPromptResult, + GetTaskPayloadRequest, + GetTaskPayloadRequestParams, + GetTaskPayloadResult, + GetTaskRequest, + GetTaskRequestParams, + GetTaskResult, + Icon, + IconTheme, + ImageContent, + Implementation, + IncludeContext, + InitializedNotification, + InitializeRequest, + InitializeRequestParams, + InitializeResult, + InputRequest, + InputRequests, + InputRequiredResult, + InputResponse, + InputResponseRequestParams, + InputResponses, + ListPromptsRequest, + ListPromptsResult, + ListResourcesRequest, + ListResourcesResult, + ListResourceTemplatesRequest, + ListResourceTemplatesResult, + ListRootsRequest, + ListRootsResult, + ListTasksRequest, + ListTasksResult, + ListToolsRequest, + ListToolsResult, + LoggingCapability, + LoggingLevel, + LoggingMessageNotification, + LoggingMessageNotificationParams, + MissingRequiredClientCapabilityErrorData, + ModelHint, + ModelPreferences, + Notification, + NotificationParams, + PaginatedRequest, + PaginatedRequestParams, + PaginatedResult, + PingRequest, + ProgressNotification, + ProgressNotificationParams, + ProgressToken, + Prompt, + PromptArgument, + PromptListChangedNotification, + PromptMessage, + PromptReference, + PromptsCapability, + ReadResourceRequest, + ReadResourceRequestParams, + ReadResourceResult, + RelatedTaskMetadata, + Request, + RequestParams, + RequestParamsMeta, + Resource, + ResourceContents, + ResourceLink, + ResourceListChangedNotification, + ResourcesCapability, + ResourceTemplate, + ResourceTemplateReference, + ResourceUpdatedNotification, + ResourceUpdatedNotificationParams, + Result, + ResultType, + Role, + Root, + RootsCapability, + RootsListChangedNotification, + SamplingCapability, + SamplingContent, + SamplingContextCapability, + SamplingMessage, + SamplingMessageContentBlock, + SamplingToolsCapability, + ServerCapabilities, + ServerNotification, + ServerRequest, + ServerResult, + ServerTasksCapability, + ServerTasksRequestsCapability, + SetLevelRequest, + SetLevelRequestParams, + StopReason, + SubscribeRequest, + SubscribeRequestParams, + SubscriptionFilter, + SubscriptionsAcknowledgedNotification, + SubscriptionsAcknowledgedNotificationParams, + SubscriptionsListenRequest, + SubscriptionsListenRequestParams, + SubscriptionsListenResult, + Task, + TaskMetadata, + TasksCallCapability, + TasksCancelCapability, + TasksCreateElicitationCapability, + TasksCreateMessageCapability, + TasksElicitationCapability, + TasksListCapability, + TasksSamplingCapability, + TaskStatus, + TaskStatusNotification, + TaskStatusNotificationParams, + TasksToolsCapability, + TextContent, + TextResourceContents, + Tool, + ToolAnnotations, + ToolChoice, + ToolExecution, + ToolListChangedNotification, + ToolResultContent, + ToolsCapability, + ToolUseContent, + UnsubscribeRequest, + UnsubscribeRequestParams, + UnsupportedProtocolVersionErrorData, + UrlElicitationCapability, + client_notification_adapter, + client_request_adapter, + client_result_adapter, + server_notification_adapter, + server_request_adapter, + server_result_adapter, +) + +# Re-export JSONRPC types +from mcp_types.jsonrpc import ( + CONNECTION_CLOSED, + HEADER_MISMATCH, + INTERNAL_ERROR, + INVALID_PARAMS, + INVALID_REQUEST, + JSONRPC_VERSION, + METHOD_NOT_FOUND, + MISSING_REQUIRED_CLIENT_CAPABILITY, + PARSE_ERROR, + REQUEST_TIMEOUT, + UNSUPPORTED_PROTOCOL_VERSION, + URL_ELICITATION_REQUIRED, + ErrorData, + JSONRPCError, + JSONRPCMessage, + JSONRPCNotification, + JSONRPCRequest, + JSONRPCResponse, + RequestId, + jsonrpc_message_adapter, +) +from mcp_types.version import LATEST_PROTOCOL_VERSION + +__all__ = [ + # Protocol version constants + "LATEST_PROTOCOL_VERSION", + "DEFAULT_NEGOTIATED_VERSION", + # Reserved request _meta keys + "PROTOCOL_VERSION_META_KEY", + "CLIENT_INFO_META_KEY", + "CLIENT_CAPABILITIES_META_KEY", + "LOG_LEVEL_META_KEY", + # Reserved result _meta keys + "SERVER_INFO_META_KEY", + # Type aliases and variables + "CORE_RESULT_TYPES", + "ContentBlock", + "ElicitRequestedSchema", + "ElicitRequestParams", + "IncludeContext", + "InputRequest", + "InputRequests", + "InputResponse", + "InputResponses", + "LoggingLevel", + "ProgressToken", + "ResultType", + "Role", + "SamplingContent", + "SamplingMessageContentBlock", + "StopReason", + "TaskStatus", + # Base classes + "BaseMetadata", + "Request", + "Notification", + "Result", + "RequestParams", + "RequestParamsMeta", + "InputResponseRequestParams", + "NotificationParams", + "PaginatedRequest", + "PaginatedRequestParams", + "PaginatedResult", + "CacheableResult", + "EmptyResult", + # Capabilities + "ClientCapabilities", + "ClientTasksCapability", + "ClientTasksRequestsCapability", + "CompletionsCapability", + "ElicitationCapability", + "FormElicitationCapability", + "LoggingCapability", + "PromptsCapability", + "ResourcesCapability", + "RootsCapability", + "SamplingCapability", + "SamplingContextCapability", + "SamplingToolsCapability", + "ServerCapabilities", + "ServerTasksCapability", + "ServerTasksRequestsCapability", + "TasksCallCapability", + "TasksCancelCapability", + "TasksCreateElicitationCapability", + "TasksCreateMessageCapability", + "TasksElicitationCapability", + "TasksListCapability", + "TasksSamplingCapability", + "TasksToolsCapability", + "ToolsCapability", + "UrlElicitationCapability", + # Content types + "Annotations", + "AudioContent", + "BlobResourceContents", + "EmbeddedResource", + "Icon", + "IconTheme", + "ImageContent", + "ResourceContents", + "ResourceLink", + "TextContent", + "TextResourceContents", + "ToolResultContent", + "ToolUseContent", + # Entity types + "Completion", + "CompletionArgument", + "CompletionContext", + "Implementation", + "ModelHint", + "ModelPreferences", + "Prompt", + "PromptArgument", + "PromptMessage", + "PromptReference", + "Resource", + "ResourceTemplate", + "ResourceTemplateReference", + "Root", + "SamplingMessage", + "SubscriptionFilter", + "Task", + "TaskMetadata", + "RelatedTaskMetadata", + "Tool", + "ToolAnnotations", + "ToolChoice", + "ToolExecution", + # Requests + "CallToolRequest", + "CallToolRequestParams", + "CompleteRequest", + "CompleteRequestParams", + "CancelTaskRequest", + "CancelTaskRequestParams", + "CreateMessageRequest", + "CreateMessageRequestParams", + "DiscoverRequest", + "ElicitRequest", + "ElicitRequestFormParams", + "ElicitRequestURLParams", + "GetPromptRequest", + "GetPromptRequestParams", + "GetTaskPayloadRequest", + "GetTaskPayloadRequestParams", + "GetTaskRequest", + "GetTaskRequestParams", + "InitializeRequest", + "InitializeRequestParams", + "ListPromptsRequest", + "ListResourcesRequest", + "ListResourceTemplatesRequest", + "ListRootsRequest", + "ListTasksRequest", + "ListToolsRequest", + "PingRequest", + "ReadResourceRequest", + "ReadResourceRequestParams", + "SetLevelRequest", + "SetLevelRequestParams", + "SubscribeRequest", + "SubscribeRequestParams", + "SubscriptionsListenRequest", + "SubscriptionsListenRequestParams", + "UnsubscribeRequest", + "UnsubscribeRequestParams", + # Results + "CallToolResult", + "CancelTaskResult", + "CompleteResult", + "CreateMessageResult", + "CreateMessageResultWithTools", + "CreateTaskResult", + "DiscoverResult", + "ElicitResult", + "ElicitationRequiredErrorData", + "GetPromptResult", + "GetTaskPayloadResult", + "GetTaskResult", + "InitializeResult", + "InputRequiredResult", + "ListPromptsResult", + "ListResourcesResult", + "ListResourceTemplatesResult", + "ListRootsResult", + "ListTasksResult", + "ListToolsResult", + "ReadResourceResult", + "SubscriptionsListenResult", + # Error data payloads + "MissingRequiredClientCapabilityErrorData", + "UnsupportedProtocolVersionErrorData", + # Notifications + "CancelledNotification", + "CancelledNotificationParams", + "ElicitCompleteNotification", + "ElicitCompleteNotificationParams", + "InitializedNotification", + "LoggingMessageNotification", + "LoggingMessageNotificationParams", + "ProgressNotification", + "ProgressNotificationParams", + "PromptListChangedNotification", + "ResourceListChangedNotification", + "ResourceUpdatedNotification", + "ResourceUpdatedNotificationParams", + "RootsListChangedNotification", + "SubscriptionsAcknowledgedNotification", + "SubscriptionsAcknowledgedNotificationParams", + "TaskStatusNotification", + "TaskStatusNotificationParams", + "ToolListChangedNotification", + # Union types for request/response routing + "ClientNotification", + "ClientRequest", + "ClientResult", + "ServerNotification", + "ServerRequest", + "ServerResult", + # Type adapters + "client_notification_adapter", + "client_request_adapter", + "client_result_adapter", + "server_notification_adapter", + "server_request_adapter", + "server_result_adapter", + # JSON-RPC types + "CONNECTION_CLOSED", + "HEADER_MISMATCH", + "INTERNAL_ERROR", + "INVALID_PARAMS", + "INVALID_REQUEST", + "JSONRPC_VERSION", + "METHOD_NOT_FOUND", + "MISSING_REQUIRED_CLIENT_CAPABILITY", + "PARSE_ERROR", + "REQUEST_TIMEOUT", + "UNSUPPORTED_PROTOCOL_VERSION", + "URL_ELICITATION_REQUIRED", + "ErrorData", + "JSONRPCError", + "JSONRPCMessage", + "JSONRPCNotification", + "JSONRPCRequest", + "JSONRPCResponse", + "RequestId", + "jsonrpc_message_adapter", +] diff --git a/venv/lib/python3.12/site-packages/mcp_types/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp_types/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..227b5d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp_types/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp_types/__pycache__/_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp_types/__pycache__/_types.cpython-312.pyc new file mode 100644 index 0000000..b07b7de Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp_types/__pycache__/_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp_types/__pycache__/_wire_base.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp_types/__pycache__/_wire_base.cpython-312.pyc new file mode 100644 index 0000000..0112709 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp_types/__pycache__/_wire_base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp_types/__pycache__/jsonrpc.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp_types/__pycache__/jsonrpc.cpython-312.pyc new file mode 100644 index 0000000..51ef26b Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp_types/__pycache__/jsonrpc.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp_types/__pycache__/methods.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp_types/__pycache__/methods.cpython-312.pyc new file mode 100644 index 0000000..55dfaf8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp_types/__pycache__/methods.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp_types/__pycache__/version.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp_types/__pycache__/version.cpython-312.pyc new file mode 100644 index 0000000..b7eaa4b Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp_types/__pycache__/version.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp_types/_types.py b/venv/lib/python3.12/site-packages/mcp_types/_types.py new file mode 100644 index 0000000..5852d9b --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp_types/_types.py @@ -0,0 +1,2198 @@ +"""Version-superset MCP protocol models. + +One model per protocol construct, carrying every field from every supported +protocol version, so application code sees a single set of types regardless of +the negotiated version. Per-field docstrings note version availability. The +`mcp_types._v*` surface packages carry the schema-exact wire shapes. +""" + +from __future__ import annotations + +from typing import Annotated, Any, ClassVar, Final, Generic, Literal, TypeAlias, TypeVar, get_args + +from pydantic import ( + BaseModel, + ConfigDict, + Field, + FileUrl, + TypeAdapter, + model_validator, +) +from pydantic.alias_generators import to_camel +from typing_extensions import NotRequired, Self, TypedDict + +from mcp_types.jsonrpc import RequestId + +DEFAULT_NEGOTIATED_VERSION: Final[str] = "2025-03-26" +"""The default negotiated version of the Model Context Protocol when no version is specified. + +We need this to satisfy the MCP specification, which requires the server to assume a specific version if none is +provided by the client. + +See the "Protocol Version Header" at +https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#protocol-version-header. +""" + +ProgressToken = str | int +"""A progress token, used to associate progress notifications with the original request.""" +Role = Literal["user", "assistant"] +"""The sender or recipient of messages and data in a conversation.""" + +IconTheme = Literal["light", "dark"] +"""Theme an icon is designed for. Wire values of `Icon.theme` (2025-11-25+).""" + + +class MCPModel(BaseModel): + """Base class for all MCP protocol types.""" + + model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) + + +Meta: TypeAlias = dict[str, Any] + +PROTOCOL_VERSION_META_KEY = "io.modelcontextprotocol/protocolVersion" +"""Reserved request `_meta` key: the MCP protocol version for this request (2026-07-28). + +SDK-managed; for HTTP its value must match the `MCP-Protocol-Version` header. +""" + +CLIENT_INFO_META_KEY = "io.modelcontextprotocol/clientInfo" +"""Reserved request `_meta` key: the client `Implementation` (2026-07-28). SDK-managed.""" + +CLIENT_CAPABILITIES_META_KEY = "io.modelcontextprotocol/clientCapabilities" +"""Reserved request `_meta` key: per-request `ClientCapabilities` (2026-07-28). SDK-managed.""" + +LOG_LEVEL_META_KEY = "io.modelcontextprotocol/logLevel" +"""Reserved request `_meta` key: desired log level for this request (2026-07-28). + +Deprecated (with the rest of logging) by SEP-2577 in the same revision that +introduces it. If absent, the server must not send log notifications. +""" + +SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo" +"""Reserved result `_meta` key: the server `Implementation` (2026-07-28). SDK-managed. + +Servers SHOULD stamp it on every result. The value is self-reported and +unverified - display, logging, and debugging only; never behavior or security. +""" + + +class RequestParamsMeta(TypedDict, extra_items=Any): + """The `_meta` object on request params (schema name: `RequestMetaObject`). + + An open map: arbitrary keys round-trip via `extra_items=Any`. Read or set + the reserved `io.modelcontextprotocol/*` keys via the `*_META_KEY` constants. + """ + + progress_token: NotRequired[ProgressToken] + """ + If specified, the caller requests out-of-band progress notifications for + this request (as represented by notifications/progress). The value of this + parameter is an opaque token that will be attached to any subsequent + notifications. The receiver is not obligated to provide these notifications. + """ + + +class RequestParams(MCPModel): + meta: RequestParamsMeta | None = Field(alias="_meta", default=None) + """Metadata reserved by MCP for protocol-level concerns (wire name `_meta`). + + Carries the optional progress token and, on 2026-07-28+ sessions, the + reserved `io.modelcontextprotocol/*` keys. Required on the wire for + 2026-07-28+ client requests; the session layer supplies the reserved + entries, so code sending through an SDK session leaves this unset. + """ + + +class PaginatedRequestParams(RequestParams): + cursor: str | None = None + """An opaque token representing the current pagination position. + + If provided, the server should return results starting after this cursor. + """ + + +class NotificationParams(MCPModel): + meta: Meta | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + + +RequestParamsT = TypeVar("RequestParamsT", bound=RequestParams | dict[str, Any] | None) +NotificationParamsT = TypeVar("NotificationParamsT", bound=NotificationParams | dict[str, Any] | None) +MethodT = TypeVar("MethodT", bound=str) + + +class Request(MCPModel, Generic[RequestParamsT, MethodT]): + """Base class for JSON-RPC requests. + + The JSON-RPC envelope (`jsonrpc`, `id`) is attached by the session layer + (see `mcp_types.jsonrpc`), not carried here. + """ + + method: MethodT + params: RequestParamsT + + name_param: ClassVar[str | None] = None + """Wire-params key mirrored into the `Mcp-Name` header on sends; SEP-2663 requires it for tasks/*. + + Subclasses override by bare assignment: re-annotating as `ClassVar` trips pyright's invariance check. + """ + + +class PaginatedRequest(Request[PaginatedRequestParams | None, MethodT], Generic[MethodT]): + """Base class for paginated requests, matching the schema's PaginatedRequest interface.""" + + params: PaginatedRequestParams | None = None + """Pagination params. Required on the 2026-07-28+ wire (because `_meta` is); + the session layer materializes it there. Optional on earlier versions.""" + + +class Notification(MCPModel, Generic[NotificationParamsT, MethodT]): + """Base class for JSON-RPC notifications.""" + + method: MethodT + params: NotificationParamsT + + +_CoreResultType = Literal["complete", "input_required"] + +ResultType = _CoreResultType | str +"""Tags a `Result` so the client knows how to parse it (2026-07-28). + +"complete" means the result is final; "input_required" means it is an +`InputRequiredResult`. The union is open (the tasks extension reserves "task"). +Absent `resultType` is equivalent to "complete". +""" + +CORE_RESULT_TYPES: Final[frozenset[str]] = frozenset(get_args(_CoreResultType)) +"""The `resultType` tags owned by the core protocol vocabulary; extension claims may not re-key them.""" + + +class Result(MCPModel): + """Base class for JSON-RPC results. + + `result_type` is declared per concrete subclass, not here, because defaults + differ: most results default to "complete", `EmptyResult` defaults to None + (so it dumps as `{}`; some peer SDKs strict-validate empty results), and + `InputRequiredResult` carries a literal. + """ + + meta: Meta | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + + +class PaginatedResult(Result): + next_cursor: str | None = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + + +class CacheableResult(Result): + """Base class for results that carry client-side caching directives (2026-07-28). + + Both fields are required on the 2026-07-28 wire. The SDK defaults to + `ttl_ms=0` (immediately stale) and `cache_scope="private"` so a handler + that doesn't set them still produces a valid 2026-07-28 result without + accidentally enabling shared caching. + """ + + ttl_ms: Annotated[int, Field(ge=0)] = 0 + """How long (ms) the client MAY cache this response, analogous to HTTP + `Cache-Control: max-age`. 0 means immediately stale.""" + + cache_scope: Literal["public", "private"] = "private" + """Analogous to HTTP `Cache-Control: public` vs `private`: "public" allows + shared caches to serve the response to any user; "private" forbids that.""" + + +class EmptyResult(Result): + """A result that indicates success but carries no data. + + `result_type` defaults to None so this dumps as `{}`: deployed TypeScript + and Rust SDK peers (clients and servers) validate empty results strictly + and reject extra keys. The 2026-07-28 schema requires `resultType`, so code + answering an empty result on a 2026-07-28+ session must pass + `result_type="complete"`. + """ + + result_type: ResultType | None = None + """None keeps the dump empty; see the class docstring.""" + + +class BaseMetadata(MCPModel): + """Base class for entities with a programmatic name and an optional display title.""" + + name: str + """Intended for programmatic or logical use, but used as a display name in past + specs or fallback (if title isn't present).""" + + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for Tool, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + + +class Icon(MCPModel): + """An optionally-sized icon for display in a user interface (2025-11-25+).""" + + src: str + """A standard URI pointing to an icon resource (`http(s):` or `data:`). + + Consumers SHOULD ensure icon URLs come from a trusted domain and SHOULD + take appropriate precautions when consuming SVGs (which can contain script). + """ + + mime_type: str | None = None + """Optional MIME type override if the source MIME type is missing or generic.""" + + sizes: list[str] | None = None + """Optional sizes this icon is available in: WxH (e.g. `"48x48"`) or `"any"`. + If not provided, assume the icon can be used at any size.""" + + theme: IconTheme | None = None + """The theme this icon is designed for. If not provided, assume any theme.""" + + +class Implementation(BaseMetadata): + """Describes the name and version of an MCP implementation (`clientInfo` / `serverInfo`).""" + + version: str + description: str | None = None + """An optional human-readable description of what this implementation does.""" + + website_url: str | None = None + """An optional URL of the website for this implementation.""" + + icons: list[Icon] | None = None + """Optional set of sized icons that the client can display in a user interface.""" + + +class RootsCapability(MCPModel): + """Capability for root operations. + + Deprecated in protocol 2026-07-28 (SEP-2577) but still carried there as an + empty object (`list_changed` exists only through 2025-11-25). + """ + + list_changed: bool | None = None + """Whether the client supports notifications for changes to the roots list.""" + + +class SamplingContextCapability(MCPModel): + """Capability for context inclusion during sampling. + + Indicates support for non-'none' values in the includeContext parameter. + SOFT-DEPRECATED: New implementations should use tools parameter instead. + """ + + +class SamplingToolsCapability(MCPModel): + """Capability indicating support for tool calling during sampling. + + When present in ClientCapabilities.sampling, indicates that the client + supports the tools and toolChoice parameters in sampling requests. + """ + + +class FormElicitationCapability(MCPModel): + """Capability for form mode elicitation.""" + + +class UrlElicitationCapability(MCPModel): + """Capability for URL mode elicitation (2025-11-25+).""" + + +class ElicitationCapability(MCPModel): + """Capability for elicitation operations. + + Clients must support at least one mode (form or url). + """ + + form: FormElicitationCapability | None = None + """Present if the client supports form mode elicitation.""" + + url: UrlElicitationCapability | None = None + """Present if the client supports URL mode elicitation (2025-11-25 and later).""" + + +class SamplingCapability(MCPModel): + """Sampling capability structure. Deprecated in 2026-07-28 (SEP-2577); shape unchanged.""" + + context: SamplingContextCapability | None = None + """ + Present if the client supports non-'none' values for includeContext parameter. + SOFT-DEPRECATED: New implementations should use tools parameter instead. + """ + tools: SamplingToolsCapability | None = None + """ + Present if the client supports tools and toolChoice parameters in sampling requests. + Presence indicates full tool calling support during sampling. + """ + + +class TasksListCapability(MCPModel): + """Capability for tasks listing operations (2025-11-25 only).""" + + +class TasksCancelCapability(MCPModel): + """Capability for tasks cancel operations (2025-11-25 only).""" + + +class TasksCreateMessageCapability(MCPModel): + """Capability for task-augmented sampling/createMessage requests (2025-11-25 only).""" + + +class TasksSamplingCapability(MCPModel): + """Capability for task-augmented sampling operations (2025-11-25 only).""" + + create_message: TasksCreateMessageCapability | None = None + + +class TasksCreateElicitationCapability(MCPModel): + """Capability for task-augmented elicitation/create requests (2025-11-25 only).""" + + +class TasksElicitationCapability(MCPModel): + """Capability for task-augmented elicitation operations (2025-11-25 only).""" + + create: TasksCreateElicitationCapability | None = None + + +class ClientTasksRequestsCapability(MCPModel): + """Specifies which request types the client can augment with tasks (2025-11-25 only).""" + + sampling: TasksSamplingCapability | None = None + elicitation: TasksElicitationCapability | None = None + + +class ClientTasksCapability(MCPModel): + """Capability for client tasks operations (2025-11-25 only).""" + + list: TasksListCapability | None = None + cancel: TasksCancelCapability | None = None + requests: ClientTasksRequestsCapability | None = None + + +class ClientCapabilities(MCPModel): + """Capabilities a client may support. + + Not a closed set: any client can define additional capabilities. Sent once in + `initialize` through 2025-11-25; per-request in `_meta` on 2026-07-28. + """ + + experimental: dict[str, dict[str, Any]] | None = None + """Experimental, non-standard capabilities that the client supports.""" + sampling: SamplingCapability | None = None + """ + Present if the client supports sampling from an LLM. + Can contain fine-grained capabilities like context and tools support. + """ + elicitation: ElicitationCapability | None = None + """Present if the client supports elicitation from the user.""" + roots: RootsCapability | None = None + """Present if the client supports listing roots.""" + extensions: dict[str, dict[str, Any]] | None = None + """MCP extensions the client supports (2026-07-28). Keys are extension + identifiers; values are per-extension settings (empty object = no settings).""" + tasks: ClientTasksCapability | None = None + """Present if the client supports task-augmented requests (2025-11-25 only).""" + + +class UnsupportedProtocolVersionErrorData(MCPModel): + """Error data for the -32022 unsupported-protocol-version error (2026-07-28).""" + + supported: list[str] + """Protocol versions the server supports; the client should pick one and retry.""" + + requested: str + + +class MissingRequiredClientCapabilityErrorData(MCPModel): + """Error data for the -32021 missing-required-client-capability error (2026-07-28).""" + + required_capabilities: ClientCapabilities + """The capabilities the server requires from the client to process this request.""" + + +class PromptsCapability(MCPModel): + """Capability for prompts operations.""" + + list_changed: bool | None = None + """Whether this server supports notifications for changes to the prompt list.""" + + +class ResourcesCapability(MCPModel): + """Capability for resources operations.""" + + subscribe: bool | None = None + """Whether this server supports subscribing to resource updates.""" + list_changed: bool | None = None + """Whether this server supports notifications for changes to the resource list.""" + + +class ToolsCapability(MCPModel): + """Capability for tools operations.""" + + list_changed: bool | None = None + """Whether this server supports notifications for changes to the tool list.""" + + +class LoggingCapability(MCPModel): + """Capability for logging operations.""" + + +class CompletionsCapability(MCPModel): + """Capability for completions operations.""" + + +class TasksCallCapability(MCPModel): + """Capability for task-augmented tools/call requests (2025-11-25 only).""" + + +class TasksToolsCapability(MCPModel): + """Capability for task-augmented tool operations (2025-11-25 only).""" + + call: TasksCallCapability | None = None + + +class ServerTasksRequestsCapability(MCPModel): + """Specifies which request types the server can augment with tasks (2025-11-25 only).""" + + tools: TasksToolsCapability | None = None + + +class ServerTasksCapability(MCPModel): + """Capability for server tasks operations (2025-11-25 only).""" + + list: TasksListCapability | None = None + cancel: TasksCancelCapability | None = None + requests: ServerTasksRequestsCapability | None = None + + +class ServerCapabilities(MCPModel): + """Capabilities that a server may support. Not a closed set.""" + + experimental: dict[str, dict[str, Any]] | None = None + """Experimental, non-standard capabilities that the server supports.""" + + logging: LoggingCapability | None = None + """Present if the server supports sending log messages to the client. + Deprecated in 2026-07-28 (SEP-2577).""" + + prompts: PromptsCapability | None = None + """Present if the server offers any prompt templates.""" + + resources: ResourcesCapability | None = None + """Present if the server offers any resources to read.""" + + tools: ToolsCapability | None = None + """Present if the server offers any tools to call.""" + + completions: CompletionsCapability | None = None + """Present if the server offers autocompletion suggestions for prompts and resources.""" + + extensions: dict[str, dict[str, Any]] | None = None + """MCP extensions the server supports (2026-07-28). Keys are extension + identifiers; values are per-extension settings (empty object = no settings).""" + + tasks: ServerTasksCapability | None = None + """Present if the server supports task-augmented requests (2025-11-25 only).""" + + +class InitializeRequestParams(RequestParams): + """Parameters for the `initialize` request. + + Removed in protocol 2026-07-28; sent/received on sessions negotiating <= 2025-11-25. + """ + + protocol_version: str + """The latest version of the Model Context Protocol that the client supports.""" + capabilities: ClientCapabilities + client_info: Implementation + + +class InitializeRequest(Request[InitializeRequestParams, Literal["initialize"]]): + """This request is sent from the client to the server when it first connects, asking it + to begin initialization. + + Removed in protocol 2026-07-28; sent/received on sessions negotiating <= 2025-11-25. + On 2026-07-28 the handshake is `server/discover` plus per-request `_meta`. + """ + + method: Literal["initialize"] = "initialize" + params: InitializeRequestParams + + +class InitializeResult(Result): + """After receiving an initialize request from the client, the server sends this response. + + Removed in protocol 2026-07-28; sent/received on sessions negotiating <= 2025-11-25. + """ + + protocol_version: str + """The version of the Model Context Protocol that the server wants to use. + If the client cannot support this version, it MUST disconnect.""" + capabilities: ServerCapabilities + server_info: Implementation + instructions: str | None = None + """Instructions describing how to use the server and its features. + + Clients may use this to improve an LLM's understanding of available tools, + resources, etc., for example by adding it to the system prompt. + """ + + +class InitializedNotification(Notification[NotificationParams | None, Literal["notifications/initialized"]]): + """This notification is sent from the client to the server after initialization has + finished. + + Removed in protocol 2026-07-28; sent/received on sessions negotiating <= 2025-11-25. + """ + + method: Literal["notifications/initialized"] = "notifications/initialized" + params: NotificationParams | None = None + + +class PingRequest(Request[RequestParams | None, Literal["ping"]]): + """A ping, issued by either the server or the client, to check that the other party is + still alive. The receiver must promptly respond, or else may be disconnected. + + Removed in protocol 2026-07-28; sent/received on sessions negotiating <= 2025-11-25. + """ + + method: Literal["ping"] = "ping" + params: RequestParams | None = None + + +class DiscoverRequest(Request[RequestParams | None, Literal["server/discover"]]): + """Asks the server to advertise its supported protocol versions, capabilities, + and other metadata (2026-07-28). + + Servers speaking 2026-07-28 MUST implement this; clients MAY call it but are + not required to (version negotiation can also happen via per-request `_meta`). + """ + + method: Literal["server/discover"] = "server/discover" + params: RequestParams | None = None + """Required on the 2026-07-28 wire (for `_meta`); the session layer materializes it.""" + + +class DiscoverResult(CacheableResult): + """The result returned by the server for a `server/discover` request (2026-07-28).""" + + supported_versions: list[str] + """MCP protocol versions this server supports; the client should pick one for subsequent requests.""" + + capabilities: ServerCapabilities + + instructions: str | None = None + """Natural-language guidance describing the server and its features, e.g. for + a system prompt. Should not duplicate information already in tool descriptions.""" + + result_type: ResultType = "complete" + """See `ResultType`. Always serialized; required on the 2026-07-28 wire, + ignored by older peers, and defaulted on inbound bodies that omit it.""" + + +# Tasks: introduced in 2025-11-25, removed from the core spec in 2026-07-28 +# (continuing as an extension). Defined here types-only; their methods are not +# in the request/notification unions below, so they are never dispatched. + + +class ToolExecution(MCPModel): + """Execution-related properties for a tool (2025-11-25 only).""" + + task_support: Literal["forbidden", "optional", "required"] | None = None + """Whether this tool supports task-augmented execution. Absent means "forbidden".""" + + +class TaskMetadata(MCPModel): + """Metadata for augmenting a request with task execution (the `task` params field; 2025-11-25 only).""" + + ttl: int | None = None + """Requested duration in milliseconds to retain task from creation.""" + + +class RelatedTaskMetadata(MCPModel): + """Associates a message with a task, via `_meta["io.modelcontextprotocol/related-task"]` (2025-11-25 only).""" + + task_id: str + + +TaskStatus = Literal["working", "input_required", "completed", "failed", "cancelled"] +"""The status of a task (2025-11-25 only).""" + + +class Task(MCPModel): + """Data associated with a task (2025-11-25 only).""" + + task_id: str + + status: TaskStatus + + status_message: str | None = None + """Optional human-readable message describing the current task state.""" + + created_at: str + """ISO 8601 timestamp when the task was created.""" + + last_updated_at: str + """ISO 8601 timestamp when the task was last updated.""" + + ttl: int | None + """Actual retention duration from creation in milliseconds, null for unlimited.""" + + poll_interval: int | None = None + """Suggested polling interval in milliseconds.""" + + +class CreateTaskResult(Result): + """A response to a task-augmented request (2025-11-25 only).""" + + task: Task + + +class GetTaskRequestParams(RequestParams): + task_id: str + + +class GetTaskRequest(Request[GetTaskRequestParams, Literal["tasks/get"]]): + """A request to retrieve the state of a task (2025-11-25 only).""" + + method: Literal["tasks/get"] = "tasks/get" + params: GetTaskRequestParams + + +class GetTaskResult(Result, Task): + """The response to a tasks/get request (2025-11-25 only).""" + + +class CancelTaskRequestParams(RequestParams): + task_id: str + + +class CancelTaskRequest(Request[CancelTaskRequestParams, Literal["tasks/cancel"]]): + """A request to cancel a task (2025-11-25 only).""" + + method: Literal["tasks/cancel"] = "tasks/cancel" + params: CancelTaskRequestParams + + +class CancelTaskResult(Result, Task): + """The response to a tasks/cancel request (2025-11-25 only).""" + + +class TaskStatusNotificationParams(NotificationParams, Task): + """Parameters for a `notifications/tasks/status` notification.""" + + +class TaskStatusNotification(Notification[TaskStatusNotificationParams, Literal["notifications/tasks/status"]]): + """An optional notification informing the requestor that a task's status has changed (2025-11-25 only).""" + + method: Literal["notifications/tasks/status"] = "notifications/tasks/status" + params: TaskStatusNotificationParams + + +class GetTaskPayloadRequestParams(RequestParams): + """Parameters for a tasks/result request.""" + + task_id: str + + +class GetTaskPayloadRequest(Request[GetTaskPayloadRequestParams, Literal["tasks/result"]]): + """A request to retrieve the result of a completed task (2025-11-25 only).""" + + method: Literal["tasks/result"] = "tasks/result" + params: GetTaskPayloadRequestParams + + +class GetTaskPayloadResult(Result): + """The response to a tasks/result request (2025-11-25 only). + + The structure matches the result type of the original request. The payload + arrives as extra wire fields, which `MCPModel` does not retain; validate the + response into the original request's result type (e.g. `CallToolResult`) + instead of this class. + """ + + +class ListTasksRequest(PaginatedRequest[Literal["tasks/list"]]): + """A request to retrieve a list of tasks (2025-11-25 only).""" + + method: Literal["tasks/list"] = "tasks/list" + + +class ListTasksResult(PaginatedResult): + """The response to a tasks/list request (2025-11-25 only).""" + + tasks: list[Task] + + +class ProgressNotificationParams(NotificationParams): + """Parameters for progress notifications.""" + + progress_token: ProgressToken + """ + The progress token which was given in the initial request, used to associate this + notification with the request that is proceeding. + """ + progress: float + """ + The progress thus far. This should increase every time progress is made, even if the + total is unknown. + """ + total: float | None = None + """Total number of items to process (or total progress required), if known.""" + message: str | None = None + """Message related to progress. + + This should provide relevant human-readable progress information. + """ + + +class ProgressNotification(Notification[ProgressNotificationParams, Literal["notifications/progress"]]): + """An out-of-band notification used to inform the receiver of a progress update for a long-running request.""" + + method: Literal["notifications/progress"] = "notifications/progress" + params: ProgressNotificationParams + + +class ListResourcesRequest(PaginatedRequest[Literal["resources/list"]]): + """Sent from the client to request a list of resources the server has.""" + + method: Literal["resources/list"] = "resources/list" + + +class Annotations(MCPModel): + """Optional annotations the client can use to inform how objects are used or displayed.""" + + audience: list[Role] | None = None + """Who the intended audience is, e.g. `["user", "assistant"]`.""" + + priority: Annotated[float, Field(ge=0.0, le=1.0)] | None = None + """How important this data is for operating the server: 1 means effectively + required, 0 means entirely optional.""" + + last_modified: str | None = None + """ISO 8601 timestamp of when the item was last modified.""" + + +class Resource(BaseMetadata): + """A known resource that the server is capable of reading.""" + + uri: str + """The URI of this resource.""" + + description: str | None = None + """A description of what this resource represents.""" + + mime_type: str | None = None + """The MIME type of this resource, if known.""" + + size: int | None = None + """The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known. + + This can be used by Hosts to display file sizes and estimate context window usage. + """ + + icons: list[Icon] | None = None + """Optional set of sized icons that the client can display in a user interface.""" + + annotations: Annotations | None = None + """Optional annotations for the client.""" + + meta: Meta | None = Field(alias="_meta", default=None) + """See the MCP specification for notes on `_meta` usage.""" + + +class ResourceTemplate(BaseMetadata): + """A template description for resources available on the server.""" + + uri_template: str + """A URI template (according to RFC 6570) that can be used to construct resource URIs.""" + + description: str | None = None + """A description of what this template is for.""" + + mime_type: str | None = None + """The MIME type for all resources that match this template. + + This should only be included if all resources matching this template have the same type. + """ + + icons: list[Icon] | None = None + """An optional set of sized icons that the client can display in a user interface.""" + + annotations: Annotations | None = None + """Optional annotations for the client.""" + + meta: Meta | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + + +class ListResourcesResult(PaginatedResult, CacheableResult): + """The server's response to a resources/list request from the client.""" + + resources: list[Resource] + result_type: ResultType = "complete" + """See `ResultType`. Always serialized; older peers ignore it.""" + + +class ListResourceTemplatesRequest(PaginatedRequest[Literal["resources/templates/list"]]): + """Sent from the client to request a list of resource templates the server has.""" + + method: Literal["resources/templates/list"] = "resources/templates/list" + + +class ListResourceTemplatesResult(PaginatedResult, CacheableResult): + """The server's response to a resources/templates/list request from the client.""" + + resource_templates: list[ResourceTemplate] + result_type: ResultType = "complete" + """See `ResultType`. Always serialized; older peers ignore it.""" + + +class InputResponseRequestParams(RequestParams): + """Base params for client requests that can carry responses to a server's + input requests (2026-07-28 multi-round-trip flow). + + When a request returns an `InputRequiredResult`, the client retries the + original request with these fields populated. + """ + + input_responses: InputResponses | None = None + """Responses to the server's `InputRequiredResult.input_requests`, keyed identically.""" + request_state: str | None = None + """Opaque state from the `InputRequiredResult`, passed back verbatim on retry.""" + + +class ReadResourceRequestParams(InputResponseRequestParams): + uri: str + """ + The URI of the resource. The URI can use any protocol; it is up to the server + how to interpret it. + """ + + +class ReadResourceRequest(Request[ReadResourceRequestParams, Literal["resources/read"]]): + """Sent from the client to the server, to read a specific resource URI.""" + + method: Literal["resources/read"] = "resources/read" + params: ReadResourceRequestParams + + +class ResourceContents(MCPModel): + """The contents of a specific resource or sub-resource.""" + + uri: str + """The URI of this resource.""" + mime_type: str | None = None + """The MIME type of this resource, if known.""" + meta: Meta | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + + +class TextResourceContents(ResourceContents): + """Text contents of a resource.""" + + text: str + """ + The text of the item. This must only be set if the item can actually be represented + as text (not binary data). + """ + + +class BlobResourceContents(ResourceContents): + """Binary contents of a resource.""" + + blob: str + """A base64-encoded string representing the binary data of the item.""" + + +class ReadResourceResult(CacheableResult): + """The server's response to a resources/read request from the client.""" + + contents: list[TextResourceContents | BlobResourceContents] + """The contents of the resource or sub-resources that were read.""" + + result_type: ResultType = "complete" + """See `ResultType`. Always serialized; older peers ignore it.""" + + +class ResourceListChangedNotification( + Notification[NotificationParams | None, Literal["notifications/resources/list_changed"]] +): + """An optional notification from the server to the client, informing it that the list + of resources it can read from has changed. + + May be sent spontaneously through 2025-11-25; on 2026-07-28 sessions the + client must opt in via `subscriptions/listen`. + """ + + method: Literal["notifications/resources/list_changed"] = "notifications/resources/list_changed" + params: NotificationParams | None = None + + +class SubscribeRequestParams(RequestParams): + """Parameters for subscribing to a resource. + + Removed in protocol 2026-07-28; sent/received on sessions negotiating <= 2025-11-25. + """ + + uri: str + """ + The URI of the resource to subscribe to. The URI can use any protocol; it is up to + the server how to interpret it. + """ + + +class SubscribeRequest(Request[SubscribeRequestParams, Literal["resources/subscribe"]]): + """Sent from the client to request resources/updated notifications from the server + whenever a particular resource changes. + + Removed in protocol 2026-07-28; sent/received on sessions negotiating <= 2025-11-25. + On 2026-07-28 use `subscriptions/listen` instead. + """ + + method: Literal["resources/subscribe"] = "resources/subscribe" + params: SubscribeRequestParams + + +class UnsubscribeRequestParams(RequestParams): + """Parameters for a resources/unsubscribe request. + + Removed in protocol 2026-07-28; sent/received on sessions negotiating <= 2025-11-25. + """ + + uri: str + """The URI of the resource to unsubscribe from.""" + + +class UnsubscribeRequest(Request[UnsubscribeRequestParams, Literal["resources/unsubscribe"]]): + """Sent from the client to request cancellation of resources/updated notifications + from the server. This should follow a previous resources/subscribe request. + + Removed in protocol 2026-07-28; sent/received on sessions negotiating <= 2025-11-25. + On 2026-07-28 use `subscriptions/listen` instead. + """ + + method: Literal["resources/unsubscribe"] = "resources/unsubscribe" + params: UnsubscribeRequestParams + + +class ResourceUpdatedNotificationParams(NotificationParams): + uri: str + """ + The URI of the resource that has been updated. This might be a sub-resource of the + one that the client actually subscribed to. + """ + + +class ResourceUpdatedNotification( + Notification[ResourceUpdatedNotificationParams, Literal["notifications/resources/updated"]] +): + """A notification from the server to the client, informing it that a resource has + changed and may need to be read again. + + Only sent if the client subscribed: via `resources/subscribe` through + 2025-11-25, or `subscriptions/listen` on 2026-07-28. + """ + + method: Literal["notifications/resources/updated"] = "notifications/resources/updated" + params: ResourceUpdatedNotificationParams + + +class SubscriptionFilter(MCPModel): + """The set of notification types a client opts in to via `subscriptions/listen` (2026-07-28). + + Each type is opt-in; the server MUST NOT send types not requested here. + Echoed back in `notifications/subscriptions/acknowledged` as the subset the + server agreed to honor. Extensions merge additional keys (e.g. `taskIds`), + so unknown keys round-trip. + """ + + model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True, extra="allow") + + tools_list_changed: bool | None = None + """If true, receive notifications/tools/list_changed.""" + + prompts_list_changed: bool | None = None + """If true, receive notifications/prompts/list_changed.""" + + resources_list_changed: bool | None = None + """If true, receive notifications/resources/list_changed.""" + + resource_subscriptions: list[str] | None = None + """Subscribe to notifications/resources/updated for these resource URIs.""" + + +class SubscriptionsListenRequestParams(RequestParams): + notifications: SubscriptionFilter + """The notifications the client opts in to on this stream.""" + + +class SubscriptionsListenRequest(Request[SubscriptionsListenRequestParams, Literal["subscriptions/listen"]]): + """Opens a long-lived channel for receiving notifications outside the context + of a specific request (2026-07-28). + """ + + method: Literal["subscriptions/listen"] = "subscriptions/listen" + params: SubscriptionsListenRequestParams + + +class SubscriptionsAcknowledgedNotificationParams(NotificationParams): + notifications: SubscriptionFilter + """The subset of requested notification types the server agreed to honor. + Unsupported types are omitted.""" + + +class SubscriptionsAcknowledgedNotification( + Notification[ + SubscriptionsAcknowledgedNotificationParams, + Literal["notifications/subscriptions/acknowledged"], + ] +): + """First message on a `subscriptions/listen` stream: acknowledges the + subscription and reports which notification types the server will honor (2026-07-28). + """ + + method: Literal["notifications/subscriptions/acknowledged"] = "notifications/subscriptions/acknowledged" + params: SubscriptionsAcknowledgedNotificationParams + + +class SubscriptionsListenResult(Result): + """Signals that a `subscriptions/listen` stream has ended gracefully (2026-07-28). + + Because the listen stream is long-lived, this result is sent only when the + server tears the subscription down (for example during shutdown); an abrupt + transport close carries no response. The body is otherwise empty: the + `_meta["io.modelcontextprotocol/subscriptionId"]` key is required on the + wire and equals the JSON-RPC id of the originating `subscriptions/listen` + request. + """ + + result_type: ResultType = "complete" + """See `ResultType`. Always serialized; older peers ignore it.""" + + +class ListPromptsRequest(PaginatedRequest[Literal["prompts/list"]]): + """Sent from the client to request a list of prompts and prompt templates the server has.""" + + method: Literal["prompts/list"] = "prompts/list" + + +class PromptArgument(BaseMetadata): + """Describes an argument that a prompt can accept.""" + + description: str | None = None + """A human-readable description of the argument.""" + required: bool | None = None + """Whether this argument must be provided.""" + + +class Prompt(BaseMetadata): + """A prompt or prompt template that the server offers.""" + + description: str | None = None + """An optional description of what this prompt provides.""" + arguments: list[PromptArgument] | None = None + """A list of arguments to use for templating the prompt.""" + icons: list[Icon] | None = None + """An optional list of icons for this prompt.""" + meta: Meta | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + + +class ListPromptsResult(PaginatedResult, CacheableResult): + """The server's response to a prompts/list request from the client.""" + + prompts: list[Prompt] + result_type: ResultType = "complete" + """See `ResultType`. Always serialized; older peers ignore it.""" + + +class GetPromptRequestParams(InputResponseRequestParams): + name: str + """The name of the prompt or prompt template.""" + arguments: dict[str, str] | None = None + """Arguments to use for templating the prompt.""" + + +class GetPromptRequest(Request[GetPromptRequestParams, Literal["prompts/get"]]): + """Used by the client to get a prompt provided by the server.""" + + method: Literal["prompts/get"] = "prompts/get" + params: GetPromptRequestParams + + +class TextContent(MCPModel): + """Text provided to or from an LLM.""" + + type: Literal["text"] = "text" + text: str + """The text content of the message.""" + annotations: Annotations | None = None + """Optional annotations for the client.""" + meta: Meta | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + + +class ImageContent(MCPModel): + """An image provided to or from an LLM.""" + + type: Literal["image"] = "image" + data: str + """The base64-encoded image data.""" + mime_type: str + """ + The MIME type of the image. Different providers may support different + image types. + """ + annotations: Annotations | None = None + """Optional annotations for the client.""" + meta: Meta | None = Field(alias="_meta", default=None) + """See the MCP specification's "General fields: _meta" section for notes on _meta usage.""" + + +class AudioContent(MCPModel): + """Audio provided to or from an LLM.""" + + type: Literal["audio"] = "audio" + data: str + """The base64-encoded audio data.""" + mime_type: str + """ + The MIME type of the audio. Different providers may support different + audio types. + """ + annotations: Annotations | None = None + """Optional annotations for the client.""" + meta: Meta | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + + +class ToolUseContent(MCPModel): + """An assistant's request to invoke a tool during sampling (2025-11-25+). + + Appears in `sampling/createMessage` results and replayed assistant messages. + The server should execute the tool and return a `ToolResultContent` in the + next user message. Deprecated in 2026-07-28 (SEP-2577). + """ + + type: Literal["tool_use"] = "tool_use" + """Discriminator for tool use content.""" + + name: str + """The name of the tool to invoke. Must match a tool name from the request's tools array.""" + + id: str + """Unique identifier for this tool call, used to correlate with ToolResultContent.""" + + input: dict[str, Any] + """Arguments to pass to the tool. Must conform to the tool's inputSchema.""" + + meta: Meta | None = Field(alias="_meta", default=None) + """Optional metadata. Clients SHOULD preserve this in subsequent sampling + requests to enable caching optimizations.""" + + +class ToolResultContent(MCPModel): + """The result of a tool use, provided by the user back to the assistant (2025-11-25+). + + Appears in sampling messages as a response to a `ToolUseContent` block. + Requires the `sampling.tools` client capability. Deprecated in 2026-07-28 (SEP-2577). + """ + + type: Literal["tool_result"] = "tool_result" + """Discriminator for tool result content.""" + + tool_use_id: str + """The `id` of the `ToolUseContent` this result corresponds to.""" + + content: list[ContentBlock] = [] + """The unstructured result content (same format as `CallToolResult.content`).""" + + structured_content: Any = None + """An optional structured result value. Any JSON value on 2026-07-28; + restricted to a JSON object on 2025-11-25.""" + + is_error: bool | None = None + """Whether the tool use resulted in an error. Absent is equivalent to false.""" + + meta: Meta | None = Field(alias="_meta", default=None) + """Optional metadata. Clients SHOULD preserve this in subsequent sampling + requests to enable caching optimizations.""" + + +SamplingMessageContentBlock: TypeAlias = TextContent | ImageContent | AudioContent | ToolUseContent | ToolResultContent +"""Content block types allowed in sampling messages. + +This is the widest (2025-11-25+) membership; older sessions allow only a subset +on the wire. Serialization never narrows a value to fit; version gating is the +session layer's responsibility. Deprecated in 2026-07-28 (SEP-2577). +""" + +SamplingContent: TypeAlias = TextContent | ImageContent | AudioContent +"""Basic content types for sampling responses (without tool use). + +Used for backwards-compatible CreateMessageResult when tools are not used. +""" + + +class SamplingMessage(MCPModel): + """Describes a message issued to or received from an LLM API.""" + + role: Role + content: SamplingMessageContentBlock | list[SamplingMessageContentBlock] + """ + Message content. Can be a single content block or an array of content blocks + for multi-modal messages and tool interactions. + """ + meta: Meta | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + + @property + def content_as_list(self) -> list[SamplingMessageContentBlock]: + """Returns the content as a list of content blocks, regardless of whether + it was originally a single block or a list.""" + return self.content if isinstance(self.content, list) else [self.content] + + +class EmbeddedResource(MCPModel): + """The contents of a resource, embedded into a prompt or tool call result. + + It is up to the client how best to render embedded resources for the benefit + of the LLM and/or the user. + """ + + type: Literal["resource"] = "resource" + resource: TextResourceContents | BlobResourceContents + annotations: Annotations | None = None + """Optional annotations for the client.""" + meta: Meta | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + + +class ResourceLink(Resource): + """A resource that the server is capable of reading, included in a prompt or tool call result. + + Note: resource links returned by tools are not guaranteed to appear in the results of `resources/list` requests. + """ + + type: Literal["resource_link"] = "resource_link" + + +ContentBlock = TextContent | ImageContent | AudioContent | ResourceLink | EmbeddedResource +"""A content block that can be used in prompts and tool results.""" + + +class PromptMessage(MCPModel): + """Describes a message returned as part of a prompt. + + Similar to `SamplingMessage`, but also supports embedded resources. + """ + + role: Role + content: ContentBlock + + +class GetPromptResult(Result): + """The server's response to a prompts/get request from the client.""" + + description: str | None = None + """An optional description for the prompt.""" + messages: list[PromptMessage] + """The messages composing the prompt, in the order they should be presented.""" + + result_type: ResultType = "complete" + """See `ResultType`. Always serialized; older peers ignore it.""" + + +class PromptListChangedNotification( + Notification[NotificationParams | None, Literal["notifications/prompts/list_changed"]] +): + """An optional notification from the server to the client, informing it that the list + of prompts it offers has changed. + + May be sent spontaneously through 2025-11-25; on 2026-07-28 sessions the + client must opt in via `subscriptions/listen`. + """ + + method: Literal["notifications/prompts/list_changed"] = "notifications/prompts/list_changed" + params: NotificationParams | None = None + + +class ListToolsRequest(PaginatedRequest[Literal["tools/list"]]): + """Sent from the client to request a list of tools the server has.""" + + method: Literal["tools/list"] = "tools/list" + + +class ToolAnnotations(MCPModel): + """Additional properties describing a Tool to clients. + + NOTE: all properties in ToolAnnotations are **hints**. + They are not guaranteed to provide a faithful description of + tool behavior (including descriptive properties like `title`). + + Clients should never make tool use decisions based on ToolAnnotations + received from untrusted servers. + """ + + title: str | None = None + """A human-readable title for the tool.""" + + read_only_hint: bool | None = None + """ + If true, the tool does not modify its environment. + Default: false + """ + + destructive_hint: bool | None = None + """ + If true, the tool may perform destructive updates to its environment. + If false, the tool performs only additive updates. + (This property is meaningful only when `read_only_hint == false`) + Default: true + """ + + idempotent_hint: bool | None = None + """ + If true, calling the tool repeatedly with the same arguments + will have no additional effect on its environment. + (This property is meaningful only when `read_only_hint == false`) + Default: false + """ + + open_world_hint: bool | None = None + """ + If true, this tool may interact with an "open world" of external + entities. If false, the tool's domain of interaction is closed. + For example, the world of a web search tool is open, whereas that + of a memory tool is not. + Default: true + """ + + +class Tool(BaseMetadata): + """Definition for a tool the client can call.""" + + description: str | None = None + """A human-readable description of the tool.""" + input_schema: dict[str, Any] + """A JSON Schema object defining the expected parameters for the tool. + + `type: "object"` is required at the root. 2026-07-28 allows any JSON Schema + 2020-12 keyword; earlier versions define only `type`/`properties`/`required`. + """ + execution: ToolExecution | None = None + """Execution-related properties (2025-11-25 only; removed in 2026-07-28).""" + output_schema: dict[str, Any] | None = None + """An optional JSON Schema object defining the structure of the tool's output + returned in the `structured_content` field of a `CallToolResult`. + + Restricted to `type: "object"` at the root through 2025-11-25; any valid + JSON Schema 2020-12 on 2026-07-28. + """ + icons: list[Icon] | None = None + """Optional set of sized icons for display (2025-11-25+).""" + annotations: ToolAnnotations | None = None + """Optional additional tool information. + Display-name precedence: `title`, `annotations.title`, then `name`.""" + meta: Meta | None = Field(alias="_meta", default=None) + """See the MCP specification for notes on `_meta` usage.""" + + +class ListToolsResult(PaginatedResult, CacheableResult): + """The server's response to a tools/list request from the client.""" + + tools: list[Tool] + + result_type: ResultType = "complete" + """See `ResultType`. Always serialized; older peers ignore it.""" + + +class CallToolRequestParams(InputResponseRequestParams): + name: str + arguments: dict[str, Any] | None = None + task: TaskMetadata | None = None + """If specified, the caller requests task-augmented execution (2025-11-25 only).""" + + +class CallToolRequest(Request[CallToolRequestParams, Literal["tools/call"]]): + """Used by the client to invoke a tool provided by the server.""" + + method: Literal["tools/call"] = "tools/call" + params: CallToolRequestParams + + +class CallToolResult(Result): + """The server's response to a tool call. + + Errors that originate from the tool SHOULD be reported inside the result + with `is_error` set to true, not as an MCP protocol-level error, so the LLM + can see and self-correct. Errors in finding the tool, or any other + exceptional condition, should be reported as an MCP error response. + """ + + content: list[ContentBlock] + """A list of content objects that represent the unstructured result of the tool call.""" + structured_content: Any = None + """An optional JSON value representing the structured result of the tool call. + + Any JSON value on 2026-07-28; restricted to a JSON object on 2025-06-18 and + 2025-11-25. + """ + is_error: bool = False + """Whether the tool call ended in an error.""" + + result_type: ResultType = "complete" + """See `ResultType`. Always serialized; older peers ignore it.""" + + +class ToolListChangedNotification(Notification[NotificationParams | None, Literal["notifications/tools/list_changed"]]): + """An optional notification from the server to the client, informing it that the list + of tools it offers has changed. + + May be sent spontaneously through 2025-11-25; on 2026-07-28 sessions the + client must opt in via `subscriptions/listen`. + """ + + method: Literal["notifications/tools/list_changed"] = "notifications/tools/list_changed" + params: NotificationParams | None = None + + +LoggingLevel = Literal["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"] +"""The severity of a log message. + +These map to syslog severities (RFC-5424 section 6.2.1). Logging is deprecated +in 2026-07-28 (SEP-2577); the level scale is unchanged across versions. +""" + + +class SetLevelRequestParams(RequestParams): + """Parameters for setting the logging level. + + Removed in protocol 2026-07-28; sent/received on sessions negotiating <= 2025-11-25. + """ + + level: LoggingLevel + """The level of logging that the client wants to receive from the server. + The server should send all logs at this level and higher (more severe).""" + + +class SetLevelRequest(Request[SetLevelRequestParams, Literal["logging/setLevel"]]): + """A request from the client to the server, to enable or adjust logging. + + Removed in protocol 2026-07-28; sent/received on sessions negotiating <= 2025-11-25. + On 2026-07-28 the client opts in per-request via `_meta` (`LOG_LEVEL_META_KEY`). + """ + + method: Literal["logging/setLevel"] = "logging/setLevel" + params: SetLevelRequestParams + + +class LoggingMessageNotificationParams(NotificationParams): + level: LoggingLevel + """The severity of this log message.""" + logger: str | None = None + """An optional name of the logger issuing this message.""" + data: Any + """ + The data to be logged, such as a string message or an object. Any JSON serializable + type is allowed here. + """ + + +class LoggingMessageNotification(Notification[LoggingMessageNotificationParams, Literal["notifications/message"]]): + """Notification of a log message passed from server to client. + + Through 2025-11-25 the client subscribes via `logging/setLevel`. On + 2026-07-28 the client opts in per-request via `_meta` (`LOG_LEVEL_META_KEY`) + and the server MUST NOT send this without it. Deprecated in 2026-07-28 (SEP-2577). + """ + + method: Literal["notifications/message"] = "notifications/message" + params: LoggingMessageNotificationParams + + +IncludeContext = Literal["none", "thisServer", "allServers"] +"""Scope of MCP-server context a sampling request asks the client to attach. + +"thisServer" and "allServers" are deprecated (SEP-2596). +""" + + +class ModelHint(MCPModel): + """Hints to use for model selection. + + Keys not declared here are up to the client to interpret. Deprecated in + 2026-07-28 (SEP-2577) with the rest of sampling. + """ + + name: str | None = None + """A hint for a model name. + + The client SHOULD treat this as a substring (e.g. `sonnet` matches + `claude-3-5-sonnet-20241022`) and MAY map it to another provider's model + that fills a similar niche. + """ + + +class ModelPreferences(MCPModel): + """The server's preferences for model selection, requested of the client during + sampling. + + Because LLMs can vary along multiple dimensions, choosing the "best" model is + rarely straightforward. Different models excel in different areas—some are + faster but less capable, others are more capable but more expensive, and so + on. This interface allows servers to express their priorities across multiple + dimensions to help clients make an appropriate selection for their use case. + + These preferences are always advisory. The client MAY ignore them. It is also + up to the client to decide how to interpret these preferences and how to + balance them against other considerations. + + Deprecated in 2026-07-28 (SEP-2577) with the rest of sampling. + """ + + hints: list[ModelHint] | None = None + """ + Optional hints to use for model selection. + + If multiple hints are specified, the client MUST evaluate them in order + (such that the first match is taken). + + The client SHOULD prioritize these hints over the numeric priorities, but + MAY still use the priorities to select from ambiguous matches. + """ + + cost_priority: float | None = None + """ + How much to prioritize cost when selecting a model. A value of 0 means cost + is not important, while a value of 1 means cost is the most important + factor. + """ + + speed_priority: float | None = None + """ + How much to prioritize sampling speed (latency) when selecting a model. A + value of 0 means speed is not important, while a value of 1 means speed is + the most important factor. + """ + + intelligence_priority: float | None = None + """ + How much to prioritize intelligence and capabilities when selecting a + model. A value of 0 means intelligence is not important, while a value of 1 + means intelligence is the most important factor. + """ + + +class ToolChoice(MCPModel): + """Controls tool selection behavior for sampling requests (2025-11-25+). + + The client MUST return an error if this is received without the + `sampling.tools` capability. Absent means `{"mode": "auto"}`. + """ + + mode: Literal["auto", "required", "none"] | None = None + """ + Controls the tool use ability of the model: + - "auto": Model decides whether to use tools (default) + - "required": Model MUST use at least one tool before completing + - "none": Model MUST NOT use any tools + """ + + +class CreateMessageRequestParams(RequestParams): + messages: list[SamplingMessage] + """The conversation to sample from.""" + model_preferences: ModelPreferences | None = None + """ + The server's preferences for which model to select. The client MAY ignore + these preferences. + """ + system_prompt: str | None = None + """An optional system prompt the server wants to use for sampling.""" + include_context: IncludeContext | None = None + """ + A request to include context from one or more MCP servers (including the + caller), to be attached to the prompt. The client MAY ignore this request. + Default is "none". "thisServer" and "allServers" are deprecated (SEP-2596). + """ + temperature: float | None = None + max_tokens: int + """The maximum number of tokens to sample, as requested by the server.""" + stop_sequences: list[str] | None = None + metadata: dict[str, Any] | None = None + """Optional metadata to pass through to the LLM provider. Provider-specific.""" + tools: list[Tool] | None = None + """Tools the model may use during generation (2025-11-25+). Requires the + `sampling.tools` client capability.""" + tool_choice: ToolChoice | None = None + """Controls how the model uses tools (2025-11-25+). Requires the + `sampling.tools` client capability.""" + task: TaskMetadata | None = None + """If specified, the caller requests task-augmented execution (2025-11-25 only).""" + + +class CreateMessageRequest(Request[CreateMessageRequestParams, Literal["sampling/createMessage"]]): + """A request from the server to sample an LLM via the client. + + The client has full discretion over which model to select and should inform + the user before sampling (human in the loop). A standalone JSON-RPC request + through 2025-11-25; on 2026-07-28 it is embedded in + `InputRequiredResult.input_requests` instead. Deprecated in 2026-07-28 (SEP-2577). + """ + + method: Literal["sampling/createMessage"] = "sampling/createMessage" + params: CreateMessageRequestParams + + +StopReason = Literal["endTurn", "stopSequence", "maxTokens", "toolUse"] | str +"""The reason why sampling stopped, if known. + +An open union to allow provider-specific stop reasons. "toolUse" is 2025-11-25+. +""" + + +class CreateMessageResult(Result): + """The client's response to a sampling/createMessage request from the server. + + This is the backwards-compatible version that returns single content (no arrays). + Used when the request does not include tools. + + On 2026-07-28 this travels embedded in an `InputResponses` map rather than + as a top-level JSON-RPC result. Deprecated in 2026-07-28 (SEP-2577). + """ + + role: Role + """The role of the message sender (typically 'assistant' for LLM responses).""" + content: SamplingContent + """Response content. Single content block (text, image, or audio).""" + model: str + """The name of the model that generated the message.""" + stop_reason: StopReason | None = None + """The reason why sampling stopped, if known.""" + + +class CreateMessageResultWithTools(Result): + """The client's response to a sampling/createMessage request when tools were provided. + + This version supports array content for tool use flows (2025-11-25 and later). + """ + + role: Role + """The role of the message sender (typically 'assistant' for LLM responses).""" + content: SamplingMessageContentBlock | list[SamplingMessageContentBlock] + """ + Response content. May be a single content block or an array. + May include ToolUseContent if stop_reason is 'toolUse'. + """ + model: str + """The name of the model that generated the message.""" + stop_reason: StopReason | None = None + """ + The reason why sampling stopped, if known. + 'toolUse' indicates the model wants to use a tool. + """ + + @property + def content_as_list(self) -> list[SamplingMessageContentBlock]: + """Returns the content as a list of content blocks, regardless of whether + it was originally a single block or a list.""" + return self.content if isinstance(self.content, list) else [self.content] + + +class ResourceTemplateReference(MCPModel): + """A reference to a resource or resource template definition.""" + + type: Literal["ref/resource"] = "ref/resource" + uri: str + """The URI or URI template of the resource.""" + + +# Not BaseMetadata: inheriting would reorder dump keys for existing callers. +class PromptReference(MCPModel): + """Identifies a prompt.""" + + type: Literal["ref/prompt"] = "ref/prompt" + name: str + """The name of the prompt or prompt template.""" + title: str | None = None + """Human-readable display title. If not provided, `name` should be used for display.""" + + +class CompletionArgument(MCPModel): + """The argument's information for completion requests.""" + + name: str + """The name of the argument.""" + value: str + """The value of the argument to use for completion matching.""" + + +class CompletionContext(MCPModel): + """Additional, optional context for completions.""" + + arguments: dict[str, str] | None = None + """Previously-resolved variables in a URI template or prompt.""" + + +class CompleteRequestParams(RequestParams): + ref: ResourceTemplateReference | PromptReference + """The prompt or resource-template reference to complete against.""" + argument: CompletionArgument + context: CompletionContext | None = None + """Additional, optional context for completions.""" + + +class CompleteRequest(Request[CompleteRequestParams, Literal["completion/complete"]]): + """A request from the client to the server, to ask for completion options.""" + + method: Literal["completion/complete"] = "completion/complete" + params: CompleteRequestParams + + +class Completion(MCPModel): + """Completion information.""" + + values: list[str] + """An array of completion values. Must not exceed 100 items.""" + total: int | None = None + """ + The total number of completion options available. This can exceed the number of + values actually sent in the response. + """ + has_more: bool | None = None + """ + Indicates whether there are additional completion options beyond those provided in + the current response, even if the exact total is unknown. + """ + + +class CompleteResult(Result): + """The server's response to a completion/complete request.""" + + completion: Completion + """The completion values, with optional total / has-more pagination hints.""" + + result_type: ResultType = "complete" + """See `ResultType`. Always serialized; older peers ignore it.""" + + +class ListRootsRequest(Request[RequestParams | None, Literal["roots/list"]]): + """Sent from the server to request a list of root URIs from the client. Roots allow + servers to ask for specific directories or files to operate on. A common example + for roots is providing a set of repositories or directories a server should operate + on. + + This request is typically used when the server needs to understand the file system + structure or access specific locations that the client has permission to read from. + + A standalone JSON-RPC request through 2025-11-25; on 2026-07-28 it is + embedded in `InputRequiredResult.input_requests`. Deprecated in 2026-07-28 (SEP-2577). + """ + + method: Literal["roots/list"] = "roots/list" + params: RequestParams | None = None + """Stays optional on 2026-07-28 (reserved client `_meta` keys do not apply + to server-to-client payloads).""" + + +class Root(MCPModel): + """Represents a root directory or file that the server can operate on. + + Deprecated in 2026-07-28 (SEP-2577) with the rest of roots. + """ + + uri: FileUrl + """ + The URI identifying the root. This *must* start with file:// for now. + This restriction may be relaxed in future versions of the protocol to allow + other URI schemes. + """ + name: str | None = None + """ + An optional name for the root. This can be used to provide a human-readable + identifier for the root, which may be useful for display purposes or for + referencing the root in other parts of the application. + """ + meta: Meta | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + + +class ListRootsResult(Result): + """The client's response to a roots/list request from the server. + + This result contains an array of Root objects, each representing a root + directory or file that the server can operate on. + + On 2026-07-28 this is carried as an `InputResponses` entry, not a JSON-RPC + result. Deprecated in 2026-07-28 (SEP-2577). + """ + + roots: list[Root] + + +class RootsListChangedNotification( + Notification[NotificationParams | None, Literal["notifications/roots/list_changed"]] +): + """A notification from the client to the server, informing it that the list of + roots has changed. + + This notification should be sent whenever the client adds, removes, or + modifies any root. The server should then request an updated list of roots + using the ListRootsRequest. + + Removed in protocol 2026-07-28; sent/received on sessions negotiating <= 2025-11-25. + """ + + method: Literal["notifications/roots/list_changed"] = "notifications/roots/list_changed" + params: NotificationParams | None = None + + +class CancelledNotificationParams(NotificationParams): + request_id: RequestId | None = None + """ + The ID of the request to cancel. + + This MUST correspond to the ID of a request previously issued in the same direction. + Required on the wire through 2025-06-18; optional at 2025-11-25; required again from + 2026-07-28, where it must name a request the client previously issued (servers send + this notification only to terminate a `subscriptions/listen` stream). + """ + reason: str | None = None + """An optional string describing the reason for the cancellation.""" + + +class CancelledNotification(Notification[CancelledNotificationParams, Literal["notifications/cancelled"]]): + """This notification can be sent by either side to indicate that it is canceling a + previously-issued request. + + The request SHOULD still be in-flight, but due to communication latency, it + is always possible that this notification MAY arrive after the request has + already finished. A client MUST NOT attempt to cancel its `initialize` request. + """ + + method: Literal["notifications/cancelled"] = "notifications/cancelled" + params: CancelledNotificationParams + + +class ElicitCompleteNotificationParams(NotificationParams): + """Parameters for elicitation completion notifications.""" + + elicitation_id: str + """The unique identifier of the elicitation that was completed.""" + + +class ElicitCompleteNotification( + Notification[ElicitCompleteNotificationParams, Literal["notifications/elicitation/complete"]] +): + """A notification from the server to the client, informing it that a URL mode + elicitation has been completed. + + Clients MAY use the notification to automatically retry requests that received a + URLElicitationRequiredError, update the user interface, or otherwise continue + an interaction. However, because delivery of the notification is not guaranteed, + clients must not wait indefinitely for a notification from the server. + + New in protocol 2025-11-25 with URL mode itself. + """ + + method: Literal["notifications/elicitation/complete"] = "notifications/elicitation/complete" + params: ElicitCompleteNotificationParams + + +# Kept as a raw JSON Schema dict so callers can hand it straight to a validator; +# the per-version packages model RequestedSchema/PrimitiveSchemaDefinition strictly. +ElicitRequestedSchema: TypeAlias = dict[str, Any] + + +class ElicitRequestFormParams(RequestParams): + """Parameters for form mode elicitation requests. + + Form mode collects non-sensitive information from the user via an in-band form + rendered by the client. + """ + + mode: Literal["form"] = "form" + """The elicitation mode (always "form" for this type).""" + + message: str + """The message to present to the user describing what information is being requested.""" + + requested_schema: ElicitRequestedSchema + """ + A restricted subset of JSON Schema defining the structure of the expected response. + Only top-level properties are allowed, without nesting. + """ + + task: TaskMetadata | None = None + """If specified, the caller requests task-augmented execution (2025-11-25 only).""" + + +class ElicitRequestURLParams(RequestParams): + """Parameters for URL mode elicitation requests. + + URL mode directs users to external URLs for sensitive out-of-band interactions + like OAuth flows, credential collection, or payment processing. New in 2025-11-25. + """ + + mode: Literal["url"] = "url" + """The elicitation mode (always "url" for this type).""" + + message: str + """The message to present to the user explaining why the interaction is needed.""" + + url: str + """The URL that the user should navigate to.""" + + elicitation_id: str | None = None + """The ID of the elicitation, which must be unique within the context of the server. + + The client MUST treat this ID as an opaque value. Required on the wire at + 2025-11-25; removed at 2026-07-28. + """ + + task: TaskMetadata | None = None + """If specified, the caller requests task-augmented execution (2025-11-25 only).""" + + +# Union type for elicitation request parameters +ElicitRequestParams: TypeAlias = ElicitRequestURLParams | ElicitRequestFormParams +"""Parameters for elicitation requests - either form or URL mode.""" + + +class ElicitRequest(Request[ElicitRequestParams, Literal["elicitation/create"]]): + """A request from the server to elicit additional information from the user via the client.""" + + method: Literal["elicitation/create"] = "elicitation/create" + params: ElicitRequestParams + + +class ElicitResult(Result): + """The client's response to an elicitation request.""" + + action: Literal["accept", "decline", "cancel"] + """ + The user action in response to the elicitation. + - "accept": User submitted the form/confirmed the action (or consented to URL navigation) + - "decline": User explicitly declined the action + - "cancel": User dismissed without making an explicit choice + """ + + content: dict[str, str | int | float | bool | list[str] | None] | None = None + """ + The submitted form data, only present when action is "accept" in form mode. + Contains values matching the requested schema. Values can be strings, integers, floats, + booleans, arrays of strings, or null. + For URL mode, this field is omitted. + """ + + +class ElicitationRequiredErrorData(MCPModel): + """Error data for the -32042 URL-elicitation-required error. + + Servers return this when a request cannot be processed until one or more + URL mode elicitations are completed. + + Removed in protocol 2026-07-28; sent/received on sessions negotiating 2025-11-25. + """ + + elicitations: list[ElicitRequestURLParams] + """List of URL mode elicitations that must be completed.""" + + +InputRequest: TypeAlias = CreateMessageRequest | ListRootsRequest | ElicitRequest +"""A single server-initiated input request embedded in `InputRequiredResult` (2026-07-28). + +Discriminated by `method`. On 2026-07-28 these embedded payloads take the place +of standalone server-to-client JSON-RPC requests. +""" + +InputRequests: TypeAlias = dict[str, InputRequest] +"""A map of server-initiated requests that the client must fulfill (2026-07-28). + +Keys are server-assigned identifiers. Carried by `InputRequiredResult.input_requests` +and by the tasks extension. +""" + +InputResponse: TypeAlias = CreateMessageResult | CreateMessageResultWithTools | ListRootsResult | ElicitResult +"""A client response to a single server-initiated input request (2026-07-28). + +`CreateMessageResultWithTools` is this SDK's array-content split of the schema's +single `CreateMessageResult` arm; the wire union has three arms. +""" + +InputResponses: TypeAlias = dict[str, InputResponse] +"""A map of client responses to server-initiated input requests (2026-07-28). + +Keys match those of the `InputRequests` map the server sent. Also used by the +tasks extension's `tasks/update` params. +""" + + +class InputRequiredResult(Result): + """The server needs additional input before the original request can complete (2026-07-28). + + Returned in place of the normal result of an interactive client request + (`tools/call`, `prompts/get`, `resources/read`). The client fulfills + `input_requests` and retries the original request, carrying the responses + and the echoed `request_state`. At least one of those two fields is + present on the wire (spec MUST). + """ + + result_type: Literal["input_required"] = "input_required" + """Discriminating tag for the dual-result response unions.""" + + input_requests: InputRequests | None = None + """Requests the client must complete before retrying. Keys are server-assigned.""" + + request_state: str | None = None + """Opaque state to pass back verbatim when the client retries the original request.""" + + @model_validator(mode="after") + def _require_one_field(self) -> Self: + if not self.input_requests and self.request_state is None: + raise ValueError("InputRequiredResult requires at least one of input_requests or request_state") + return self + + +# Forward refs to InputResponses; rebuild at import time rather than first use. +InputResponseRequestParams.model_rebuild() +ReadResourceRequestParams.model_rebuild() +GetPromptRequestParams.model_rebuild() +CallToolRequestParams.model_rebuild() + +# Top-level message unions: superset across all supported protocol versions. +# Per-version validity is recorded in `mcp_types.methods`, not enforced here. + +ClientRequest = ( + PingRequest + | InitializeRequest + | CompleteRequest + | SetLevelRequest + | GetPromptRequest + | ListPromptsRequest + | ListResourcesRequest + | ListResourceTemplatesRequest + | ReadResourceRequest + | SubscribeRequest + | UnsubscribeRequest + | CallToolRequest + | ListToolsRequest + | DiscoverRequest + | SubscriptionsListenRequest +) +"""Union of client-to-server request payloads across all supported protocol versions. + +The 2025-11-25 task requests are deliberately excluded (types-only). +""" + +client_request_adapter = TypeAdapter[ClientRequest](ClientRequest) + + +ClientNotification = ( + CancelledNotification | ProgressNotification | InitializedNotification | RootsListChangedNotification +) +"""Notifications sent from the client to the server. + +`TaskStatusNotification` is deliberately excluded (types-only). +""" + +client_notification_adapter = TypeAdapter[ClientNotification](ClientNotification) + + +ClientResult = EmptyResult | CreateMessageResult | CreateMessageResultWithTools | ListRootsResult | ElicitResult +client_result_adapter = TypeAdapter[ClientResult](ClientResult) + + +ServerRequest = PingRequest | CreateMessageRequest | ListRootsRequest | ElicitRequest +"""Union of standalone JSON-RPC requests a server can send to a client. + +Live through 2025-11-25 only: 2026-07-28 has no server-to-client JSON-RPC +requests (these payloads are embedded in `InputRequiredResult` instead). +""" + +server_request_adapter = TypeAdapter[ServerRequest](ServerRequest) + + +ServerNotification = ( + CancelledNotification + | ProgressNotification + | LoggingMessageNotification + | ResourceUpdatedNotification + | ResourceListChangedNotification + | ToolListChangedNotification + | PromptListChangedNotification + | ElicitCompleteNotification + | SubscriptionsAcknowledgedNotification +) +"""Union of server-to-client notification payloads across all supported protocol versions. + +`TaskStatusNotification` is deliberately excluded (types-only). +""" + +server_notification_adapter = TypeAdapter[ServerNotification](ServerNotification) + + +ServerResult = ( + EmptyResult + | InitializeResult + | DiscoverResult + | CompleteResult + | GetPromptResult + | ListPromptsResult + | ListResourcesResult + | ListResourceTemplatesResult + | ReadResourceResult + | CallToolResult + | ListToolsResult + | SubscriptionsListenResult + | InputRequiredResult +) +"""Union of every result payload a server can return for a client request. + +`InputRequiredResult` is deliberately last: both of its fields are optional, +so an earlier position would shadow other members during union resolution. +""" +server_result_adapter = TypeAdapter[ServerResult](ServerResult) diff --git a/venv/lib/python3.12/site-packages/mcp_types/_v2025_11_25/__init__.py b/venv/lib/python3.12/site-packages/mcp_types/_v2025_11_25/__init__.py new file mode 100644 index 0000000..b5f5b96 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp_types/_v2025_11_25/__init__.py @@ -0,0 +1,3526 @@ +"""Internal wire-shape models for protocol 2025-11-25. Generated; do not edit. + +Regenerate with `scripts/gen_surface_types.py` from `schema/2025-11-25.json` +(sha256 `4e01628360a2149892eab8f298ceee626d24a58862184eb8ec85d95b8f353e31`).""" +# pyright: reportIncompatibleVariableOverride=false, reportGeneralTypeIssues=false + +from __future__ import annotations + +from typing import Annotated, Any, Literal + +from mcp_types._wire_base import WireModel +from pydantic import ConfigDict, Field, RootModel + + +class BaseMetadata(WireModel): + """ + Base interface for metadata with name (identifier) and title (display name) properties. + """ + + model_config = ConfigDict( + extra="ignore", + ) + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for Tool, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + + +class BlobResourceContents(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + blob: str + """ + A base64-encoded string representing the binary data of the item. + """ + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + The MIME type of this resource, if known. + """ + uri: str + """ + The URI of this resource. + """ + + +class BooleanSchema(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + default: bool | None = None + description: str | None = None + title: str | None = None + type: Literal["boolean"] + + +class Params(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + task_id: Annotated[str, Field(alias="taskId")] + """ + The task identifier to cancel. + """ + + +class Elicitation(WireModel): + """ + Present if the client supports elicitation from the server. + """ + + model_config = ConfigDict( + extra="ignore", + ) + form: dict[str, Any] | None = None + url: dict[str, Any] | None = None + + +class Roots(WireModel): + """ + Present if the client supports listing roots. + """ + + model_config = ConfigDict( + extra="ignore", + ) + list_changed: Annotated[bool | None, Field(alias="listChanged")] = None + """ + Whether the client supports notifications for changes to the roots list. + """ + + +class Sampling(WireModel): + """ + Present if the client supports sampling from an LLM. + """ + + model_config = ConfigDict( + extra="ignore", + ) + context: dict[str, Any] | None = None + """ + Whether the client supports context inclusion via includeContext parameter. + If not declared, servers SHOULD only use `includeContext: "none"` (or omit it). + """ + tools: dict[str, Any] | None = None + """ + Whether the client supports tool use via tools and toolChoice parameters. + """ + + +class Elicitation1(WireModel): + """ + Task support for elicitation-related requests. + """ + + model_config = ConfigDict( + extra="ignore", + ) + create: dict[str, Any] | None = None + """ + Whether the client supports task-augmented elicitation/create requests. + """ + + +class Sampling1(WireModel): + """ + Task support for sampling-related requests. + """ + + model_config = ConfigDict( + extra="ignore", + ) + create_message: Annotated[dict[str, Any] | None, Field(alias="createMessage")] = None + """ + Whether the client supports task-augmented sampling/createMessage requests. + """ + + +class Requests(WireModel): + """ + Specifies which request types can be augmented with tasks. + """ + + model_config = ConfigDict( + extra="ignore", + ) + elicitation: Elicitation1 | None = None + """ + Task support for elicitation-related requests. + """ + sampling: Sampling1 | None = None + """ + Task support for sampling-related requests. + """ + + +class Tasks(WireModel): + """ + Present if the client supports task-augmented requests. + """ + + model_config = ConfigDict( + extra="ignore", + ) + cancel: dict[str, Any] | None = None + """ + Whether this client supports tasks/cancel. + """ + list: dict[str, Any] | None = None + """ + Whether this client supports tasks/list. + """ + requests: Requests | None = None + """ + Specifies which request types can be augmented with tasks. + """ + + +class ClientCapabilities(WireModel): + """ + Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities. + """ + + model_config = ConfigDict( + extra="ignore", + ) + elicitation: Elicitation | None = None + """ + Present if the client supports elicitation from the server. + """ + experimental: dict[str, dict[str, Any]] | None = None + """ + Experimental, non-standard capabilities that the client supports. + """ + roots: Roots | None = None + """ + Present if the client supports listing roots. + """ + sampling: Sampling | None = None + """ + Present if the client supports sampling from an LLM. + """ + tasks: Tasks | None = None + """ + Present if the client supports task-augmented requests. + """ + + +class Argument(WireModel): + """ + The argument's information + """ + + model_config = ConfigDict( + extra="ignore", + ) + name: str + """ + The name of the argument + """ + value: str + """ + The value of the argument to use for completion matching. + """ + + +class Context(WireModel): + """ + Additional, optional context for completions + """ + + model_config = ConfigDict( + extra="ignore", + ) + arguments: dict[str, str] | None = None + """ + Previously-resolved variables in a URI template or prompt. + """ + + +class Completion(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + has_more: Annotated[bool | None, Field(alias="hasMore")] = None + """ + Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown. + """ + total: int | None = None + """ + The total number of completion options available. This can exceed the number of values actually sent in the response. + """ + values: list[str] + """ + An array of completion values. Must not exceed 100 items. + """ + + +class CompleteResult(WireModel): + """ + The server's response to a completion/complete request + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + completion: Completion + + +class Cursor(RootModel[str]): + root: str + """ + An opaque token used to represent a cursor for pagination. + """ + + +class RequestedSchema(WireModel): + """ + A restricted subset of JSON Schema. + Only top-level properties are allowed, without nesting. + """ + + model_config = ConfigDict( + extra="ignore", + ) + schema_: Annotated[str | None, Field(alias="$schema")] = None + properties: dict[str, Any] + required: list[str] | None = None + type: Literal["object"] + + +class ElicitResult(WireModel): + """ + The client's response to an elicitation request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + action: Literal["accept", "cancel", "decline"] + """ + The user action in response to the elicitation. + - "accept": User submitted the form/confirmed the action + - "decline": User explicitly decline the action + - "cancel": User dismissed without making an explicit choice + """ + content: dict[str, list[str] | str | int | float | bool | None] | None = None + """ + The submitted form data, only present when action is "accept" and mode was "form". + Contains values matching the requested schema. + Omitted for out-of-band mode responses. + """ + + +class Params1(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + elicitation_id: Annotated[str, Field(alias="elicitationId")] + """ + The ID of the elicitation that completed. + """ + + +class ElicitationCompleteNotification(WireModel): + """ + An optional notification from the server to the client, informing it of a completion of a out-of-band elicitation request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/elicitation/complete"] + params: Params1 + + +class Error(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + code: int + """ + The error type that occurred. + """ + data: Any | None = None + """ + Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + """ + message: str + """ + A short description of the error. The message SHOULD be limited to a concise single sentence. + """ + + +class Params2(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + task_id: Annotated[str, Field(alias="taskId")] + """ + The task identifier to retrieve results for. + """ + + +class GetTaskPayloadResult(WireModel): + """ + The response to a tasks/result request. + The structure matches the result type of the original request. + For example, a tools/call task would return the CallToolResult structure. + """ + + model_config = ConfigDict( + extra="allow", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + + +class Params3(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + task_id: Annotated[str, Field(alias="taskId")] + """ + The task identifier to query. + """ + + +class Icon(WireModel): + """ + An optionally-sized icon that can be displayed in a user interface. + """ + + model_config = ConfigDict( + extra="ignore", + ) + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + Optional MIME type override if the source MIME type is missing or generic. + For example: `"image/png"`, `"image/jpeg"`, or `"image/svg+xml"`. + """ + sizes: list[str] | None = None + """ + Optional array of strings that specify sizes at which the icon can be used. + Each string should be in WxH format (e.g., `"48x48"`, `"96x96"`) or `"any"` for scalable formats like SVG. + + If not provided, the client should assume that the icon can be used at any size. + """ + src: str + """ + A standard URI pointing to an icon resource. May be an HTTP/HTTPS URL or a + `data:` URI with Base64-encoded image data. + + Consumers SHOULD takes steps to ensure URLs serving icons are from the + same domain as the client/server or a trusted domain. + + Consumers SHOULD take appropriate precautions when consuming SVGs as they can contain + executable JavaScript. + """ + theme: Literal["dark", "light"] | None = None + """ + Optional specifier for the theme this icon is designed for. `light` indicates + the icon is designed to be used with a light background, and `dark` indicates + the icon is designed to be used with a dark background. + + If not provided, the client should assume the icon can be used with any theme. + """ + + +class Icons(WireModel): + """ + Base interface to add `icons` property. + """ + + model_config = ConfigDict( + extra="ignore", + ) + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + + +class Implementation(WireModel): + """ + Describes the MCP implementation. + """ + + model_config = ConfigDict( + extra="ignore", + ) + description: str | None = None + """ + An optional human-readable description of what this implementation does. + + This can be used by clients or servers to provide context about their purpose + and capabilities. For example, a server might describe the types of resources + or tools it provides, while a client might describe its intended use case. + """ + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for Tool, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + version: str + website_url: Annotated[str | None, Field(alias="websiteUrl")] = None + """ + An optional URL of the website for this implementation. + """ + + +class JSONRPCNotification(WireModel): + """ + A notification which does not expect a response. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: str + params: dict[str, Any] | None = None + + +class LegacyTitledEnumSchema(WireModel): + """ + Use TitledSingleSelectEnumSchema instead. + This interface will be removed in a future version. + """ + + model_config = ConfigDict( + extra="ignore", + ) + default: str | None = None + description: str | None = None + enum: list[str] + enum_names: Annotated[list[str] | None, Field(alias="enumNames")] = None + """ + (Legacy) Display names for enum values. + Non-standard according to JSON schema 2020-12. + """ + title: str | None = None + type: Literal["string"] + + +class LoggingLevel( + RootModel[ + Literal[ + "alert", + "critical", + "debug", + "emergency", + "error", + "info", + "notice", + "warning", + ] + ] +): + root: Literal["alert", "critical", "debug", "emergency", "error", "info", "notice", "warning"] + """ + The severity of a log message. + + These map to syslog message severities, as specified in RFC-5424: + https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1 + """ + + +class LoggingMessageNotificationParams(WireModel): + """ + Parameters for a `notifications/message` notification. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + data: Any + """ + The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here. + """ + level: LoggingLevel + """ + The severity of this log message. + """ + logger: str | None = None + """ + An optional name of the logger issuing this message. + """ + + +class ModelHint(WireModel): + """ + Hints to use for model selection. + + Keys not declared here are currently left unspecified by the spec and are up + to the client to interpret. + """ + + model_config = ConfigDict( + extra="ignore", + ) + name: str | None = None + """ + A hint for a model name. + + The client SHOULD treat this as a substring of a model name; for example: + - `claude-3-5-sonnet` should match `claude-3-5-sonnet-20241022` + - `sonnet` should match `claude-3-5-sonnet-20241022`, `claude-3-sonnet-20240229`, etc. + - `claude` should match any Claude model + + The client MAY also map the string to a different provider's model name or a different model family, as long as it fills a similar niche; for example: + - `gemini-1.5-flash` could match `claude-3-haiku-20240307` + """ + + +class ModelPreferences(WireModel): + """ + The server's preferences for model selection, requested of the client during sampling. + + Because LLMs can vary along multiple dimensions, choosing the "best" model is + rarely straightforward. Different models excel in different areas—some are + faster but less capable, others are more capable but more expensive, and so + on. This interface allows servers to express their priorities across multiple + dimensions to help clients make an appropriate selection for their use case. + + These preferences are always advisory. The client MAY ignore them. It is also + up to the client to decide how to interpret these preferences and how to + balance them against other considerations. + """ + + model_config = ConfigDict( + extra="ignore", + ) + cost_priority: Annotated[float | None, Field(alias="costPriority", ge=0.0, le=1.0)] = None + """ + How much to prioritize cost when selecting a model. A value of 0 means cost + is not important, while a value of 1 means cost is the most important + factor. + """ + hints: list[ModelHint] | None = None + """ + Optional hints to use for model selection. + + If multiple hints are specified, the client MUST evaluate them in order + (such that the first match is taken). + + The client SHOULD prioritize these hints over the numeric priorities, but + MAY still use the priorities to select from ambiguous matches. + """ + intelligence_priority: Annotated[float | None, Field(alias="intelligencePriority", ge=0.0, le=1.0)] = None + """ + How much to prioritize intelligence and capabilities when selecting a + model. A value of 0 means intelligence is not important, while a value of 1 + means intelligence is the most important factor. + """ + speed_priority: Annotated[float | None, Field(alias="speedPriority", ge=0.0, le=1.0)] = None + """ + How much to prioritize sampling speed (latency) when selecting a model. A + value of 0 means speed is not important, while a value of 1 means speed is + the most important factor. + """ + + +class Notification(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + method: str + params: dict[str, Any] | None = None + + +class NotificationParams(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + + +class NumberSchema(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + default: int | float | None = None + description: str | None = None + maximum: int | float | None = None + minimum: int | float | None = None + title: str | None = None + type: Literal["integer", "number"] + + +class PaginatedResult(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + + +class ProgressToken(RootModel[str | int]): + root: str | int + """ + A progress token, used to associate progress notifications with the original request. + """ + + +class PromptArgument(WireModel): + """ + Describes an argument that a prompt can accept. + """ + + model_config = ConfigDict( + extra="ignore", + ) + description: str | None = None + """ + A human-readable description of the argument. + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + required: bool | None = None + """ + Whether this argument must be provided. + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for Tool, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + + +class PromptListChangedNotification(WireModel): + """ + An optional notification from the server to the client, informing it that the list of prompts it offers has changed. This may be issued by servers without any previous subscription from the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/prompts/list_changed"] + params: NotificationParams | None = None + + +class PromptReference(WireModel): + """ + Identifies a prompt. + """ + + model_config = ConfigDict( + extra="ignore", + ) + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for Tool, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + type: Literal["ref/prompt"] + + +class Meta(WireModel): + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + + model_config = ConfigDict( + extra="allow", + ) + progress_token: Annotated[ProgressToken | None, Field(alias="progressToken")] = None + """ + If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications. + """ + + +class ReadResourceRequestParams(WireModel): + """ + Parameters for a `resources/read` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + uri: str + """ + The URI of the resource. The URI can use any protocol; it is up to the server how to interpret it. + """ + + +class RelatedTaskMetadata(WireModel): + """ + Metadata for associating messages with a task. + Include this in the `_meta` field under the key `io.modelcontextprotocol/related-task`. + """ + + model_config = ConfigDict( + extra="ignore", + ) + task_id: Annotated[str, Field(alias="taskId")] + """ + The task identifier this message is associated with. + """ + + +class Request(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + method: str + params: dict[str, Any] | None = None + + +class RequestId(RootModel[str | int]): + root: str | int + """ + A uniquely identifying ID for a request in JSON-RPC. + """ + + +class RequestParams(WireModel): + """ + Common params for any request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + + +class ResourceContents(WireModel): + """ + The contents of a specific resource or sub-resource. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + The MIME type of this resource, if known. + """ + uri: str + """ + The URI of this resource. + """ + + +class ResourceListChangedNotification(WireModel): + """ + An optional notification from the server to the client, informing it that the list of resources it can read from has changed. This may be issued by servers without any previous subscription from the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/resources/list_changed"] + params: NotificationParams | None = None + + +class ResourceRequestParams(WireModel): + """ + Common parameters when working with resources. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + uri: str + """ + The URI of the resource. The URI can use any protocol; it is up to the server how to interpret it. + """ + + +class ResourceTemplateReference(WireModel): + """ + A reference to a resource or resource template definition. + """ + + model_config = ConfigDict( + extra="ignore", + ) + type: Literal["ref/resource"] + uri: str + """ + The URI or URI template of the resource. + """ + + +class ResourceUpdatedNotificationParams(WireModel): + """ + Parameters for a `notifications/resources/updated` notification. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + uri: str + """ + The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to. + """ + + +class Result(WireModel): + model_config = ConfigDict( + extra="allow", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + + +class Role(RootModel[Literal["assistant", "user"]]): + root: Literal["assistant", "user"] + """ + The sender or recipient of messages and data in a conversation. + """ + + +class Root(WireModel): + """ + Represents a root directory or file that the server can operate on. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + name: str | None = None + """ + An optional name for the root. This can be used to provide a human-readable + identifier for the root, which may be useful for display purposes or for + referencing the root in other parts of the application. + """ + uri: str + """ + The URI identifying the root. This *must* start with file:// for now. + This restriction may be relaxed in future versions of the protocol to allow + other URI schemes. + """ + + +class RootsListChangedNotification(WireModel): + """ + A notification from the client to the server, informing it that the list of roots has changed. + This notification should be sent whenever the client adds, removes, or modifies any root. + The server should then request an updated list of roots using the ListRootsRequest. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/roots/list_changed"] + params: NotificationParams | None = None + + +class Prompts(WireModel): + """ + Present if the server offers any prompt templates. + """ + + model_config = ConfigDict( + extra="ignore", + ) + list_changed: Annotated[bool | None, Field(alias="listChanged")] = None + """ + Whether this server supports notifications for changes to the prompt list. + """ + + +class Resources(WireModel): + """ + Present if the server offers any resources to read. + """ + + model_config = ConfigDict( + extra="ignore", + ) + list_changed: Annotated[bool | None, Field(alias="listChanged")] = None + """ + Whether this server supports notifications for changes to the resource list. + """ + subscribe: bool | None = None + """ + Whether this server supports subscribing to resource updates. + """ + + +class Tools(WireModel): + """ + Task support for tool-related requests. + """ + + model_config = ConfigDict( + extra="ignore", + ) + call: dict[str, Any] | None = None + """ + Whether the server supports task-augmented tools/call requests. + """ + + +class Requests1(WireModel): + """ + Specifies which request types can be augmented with tasks. + """ + + model_config = ConfigDict( + extra="ignore", + ) + tools: Tools | None = None + """ + Task support for tool-related requests. + """ + + +class Tasks1(WireModel): + """ + Present if the server supports task-augmented requests. + """ + + model_config = ConfigDict( + extra="ignore", + ) + cancel: dict[str, Any] | None = None + """ + Whether this server supports tasks/cancel. + """ + list: dict[str, Any] | None = None + """ + Whether this server supports tasks/list. + """ + requests: Requests1 | None = None + """ + Specifies which request types can be augmented with tasks. + """ + + +class Tools1(WireModel): + """ + Present if the server offers any tools to call. + """ + + model_config = ConfigDict( + extra="ignore", + ) + list_changed: Annotated[bool | None, Field(alias="listChanged")] = None + """ + Whether this server supports notifications for changes to the tool list. + """ + + +class ServerCapabilities(WireModel): + """ + Capabilities that a server may support. Known capabilities are defined here, in this schema, but this is not a closed set: any server can define its own, additional capabilities. + """ + + model_config = ConfigDict( + extra="ignore", + ) + completions: dict[str, Any] | None = None + """ + Present if the server supports argument autocompletion suggestions. + """ + experimental: dict[str, dict[str, Any]] | None = None + """ + Experimental, non-standard capabilities that the server supports. + """ + logging: dict[str, Any] | None = None + """ + Present if the server supports sending log messages to the client. + """ + prompts: Prompts | None = None + """ + Present if the server offers any prompt templates. + """ + resources: Resources | None = None + """ + Present if the server offers any resources to read. + """ + tasks: Tasks1 | None = None + """ + Present if the server supports task-augmented requests. + """ + tools: Tools1 | None = None + """ + Present if the server offers any tools to call. + """ + + +class SetLevelRequestParams(WireModel): + """ + Parameters for a `logging/setLevel` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + level: LoggingLevel + """ + The level of logging that the client wants to receive from the server. The server should send all logs at this level and higher (i.e., more severe) to the client as notifications/message. + """ + + +class StringSchema(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + default: str | None = None + description: str | None = None + format: Literal["date", "date-time", "email", "uri"] | None = None + max_length: Annotated[int | None, Field(alias="maxLength")] = None + min_length: Annotated[int | None, Field(alias="minLength")] = None + title: str | None = None + type: Literal["string"] + + +class SubscribeRequestParams(WireModel): + """ + Parameters for a `resources/subscribe` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + uri: str + """ + The URI of the resource. The URI can use any protocol; it is up to the server how to interpret it. + """ + + +class TaskMetadata(WireModel): + """ + Metadata for augmenting a request with task execution. + Include this in the `task` field of the request parameters. + """ + + model_config = ConfigDict( + extra="ignore", + ) + ttl: int | None = None + """ + Requested duration in milliseconds to retain task from creation. + """ + + +class TaskStatus(RootModel[Literal["cancelled", "completed", "failed", "input_required", "working"]]): + root: Literal["cancelled", "completed", "failed", "input_required", "working"] + """ + The status of a task. + """ + + +class TextResourceContents(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + The MIME type of this resource, if known. + """ + text: str + """ + The text of the item. This must only be set if the item can actually be represented as text (not binary data). + """ + uri: str + """ + The URI of this resource. + """ + + +class AnyOfItem(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + const: str + """ + The constant enum value. + """ + title: str + """ + Display title for this option. + """ + + +class Items(WireModel): + """ + Schema for array items with enum options and display labels. + """ + + model_config = ConfigDict( + extra="ignore", + ) + any_of: Annotated[list[AnyOfItem], Field(alias="anyOf")] + """ + Array of enum options with values and display labels. + """ + + +class TitledMultiSelectEnumSchema(WireModel): + """ + Schema for multiple-selection enumeration with display titles for each option. + """ + + model_config = ConfigDict( + extra="ignore", + ) + default: list[str] | None = None + """ + Optional default value. + """ + description: str | None = None + """ + Optional description for the enum field. + """ + items: Items + """ + Schema for array items with enum options and display labels. + """ + max_items: Annotated[int | None, Field(alias="maxItems")] = None + """ + Maximum number of items to select. + """ + min_items: Annotated[int | None, Field(alias="minItems")] = None + """ + Minimum number of items to select. + """ + title: str | None = None + """ + Optional title for the enum field. + """ + type: Literal["array"] + + +class OneOfItem(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + const: str + """ + The enum value. + """ + title: str + """ + Display label for this option. + """ + + +class TitledSingleSelectEnumSchema(WireModel): + """ + Schema for single-selection enumeration with display titles for each option. + """ + + model_config = ConfigDict( + extra="ignore", + ) + default: str | None = None + """ + Optional default value. + """ + description: str | None = None + """ + Optional description for the enum field. + """ + one_of: Annotated[list[OneOfItem], Field(alias="oneOf")] + """ + Array of enum options with values and display labels. + """ + title: str | None = None + """ + Optional title for the enum field. + """ + type: Literal["string"] + + +class InputSchema(WireModel): + """ + A JSON Schema object defining the expected parameters for the tool. + """ + + model_config = ConfigDict( + extra="allow", + ) + schema_: Annotated[str | None, Field(alias="$schema")] = None + properties: dict[str, dict[str, Any] | bool] | None = None + required: list[str] | None = None + type: Literal["object"] + + +class OutputSchema(WireModel): + """ + An optional JSON Schema object defining the structure of the tool's output returned in + the structuredContent field of a CallToolResult. + + Defaults to JSON Schema 2020-12 when no explicit $schema is provided. + Currently restricted to type: "object" at the root level. + """ + + model_config = ConfigDict( + extra="allow", + ) + schema_: Annotated[str | None, Field(alias="$schema")] = None + properties: dict[str, dict[str, Any] | bool] | None = None + required: list[str] | None = None + type: Literal["object"] + + +class ToolAnnotations(WireModel): + """ + Additional properties describing a Tool to clients. + + NOTE: all properties in ToolAnnotations are **hints**. + They are not guaranteed to provide a faithful description of + tool behavior (including descriptive properties like `title`). + + Clients should never make tool use decisions based on ToolAnnotations + received from untrusted servers. + """ + + model_config = ConfigDict( + extra="ignore", + ) + destructive_hint: Annotated[bool | None, Field(alias="destructiveHint")] = None + """ + If true, the tool may perform destructive updates to its environment. + If false, the tool performs only additive updates. + + (This property is meaningful only when `readOnlyHint == false`) + + Default: true + """ + idempotent_hint: Annotated[bool | None, Field(alias="idempotentHint")] = None + """ + If true, calling the tool repeatedly with the same arguments + will have no additional effect on its environment. + + (This property is meaningful only when `readOnlyHint == false`) + + Default: false + """ + open_world_hint: Annotated[bool | None, Field(alias="openWorldHint")] = None + """ + If true, this tool may interact with an "open world" of external + entities. If false, the tool's domain of interaction is closed. + For example, the world of a web search tool is open, whereas that + of a memory tool is not. + + Default: true + """ + read_only_hint: Annotated[bool | None, Field(alias="readOnlyHint")] = None + """ + If true, the tool does not modify its environment. + + Default: false + """ + title: str | None = None + """ + A human-readable title for the tool. + """ + + +class ToolChoice(WireModel): + """ + Controls tool selection behavior for sampling requests. + """ + + model_config = ConfigDict( + extra="ignore", + ) + mode: Literal["auto", "none", "required"] | None = None + """ + Controls the tool use ability of the model: + - "auto": Model decides whether to use tools (default) + - "required": Model MUST use at least one tool before completing + - "none": Model MUST NOT use any tools + """ + + +class ToolExecution(WireModel): + """ + Execution-related properties for a tool. + """ + + model_config = ConfigDict( + extra="ignore", + ) + task_support: Annotated[Literal["forbidden", "optional", "required"] | None, Field(alias="taskSupport")] = None + """ + Indicates whether this tool supports task-augmented execution. + This allows clients to handle long-running operations through polling + the task system. + + - "forbidden": Tool does not support task-augmented execution (default when absent) + - "optional": Tool may support task-augmented execution + - "required": Tool requires task-augmented execution + + Default: "forbidden" + """ + + +class ToolListChangedNotification(WireModel): + """ + An optional notification from the server to the client, informing it that the list of tools it offers has changed. This may be issued by servers without any previous subscription from the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/tools/list_changed"] + params: NotificationParams | None = None + + +class ToolUseContent(WireModel): + """ + A request from the assistant to call a tool. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + Optional metadata about the tool use. Clients SHOULD preserve this field when + including tool uses in subsequent sampling requests to enable caching optimizations. + + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + id: str + """ + A unique identifier for this tool use. + + This ID is used to match tool results to their corresponding tool uses. + """ + input: dict[str, Any] + """ + The arguments to pass to the tool, conforming to the tool's input schema. + """ + name: str + """ + The name of the tool to call. + """ + type: Literal["tool_use"] + + +class UnsubscribeRequestParams(WireModel): + """ + Parameters for a `resources/unsubscribe` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + uri: str + """ + The URI of the resource. The URI can use any protocol; it is up to the server how to interpret it. + """ + + +class Items1(WireModel): + """ + Schema for the array items. + """ + + model_config = ConfigDict( + extra="ignore", + ) + enum: list[str] + """ + Array of enum values to choose from. + """ + type: Literal["string"] + + +class UntitledMultiSelectEnumSchema(WireModel): + """ + Schema for multiple-selection enumeration without display titles for options. + """ + + model_config = ConfigDict( + extra="ignore", + ) + default: list[str] | None = None + """ + Optional default value. + """ + description: str | None = None + """ + Optional description for the enum field. + """ + items: Items1 + """ + Schema for the array items. + """ + max_items: Annotated[int | None, Field(alias="maxItems")] = None + """ + Maximum number of items to select. + """ + min_items: Annotated[int | None, Field(alias="minItems")] = None + """ + Minimum number of items to select. + """ + title: str | None = None + """ + Optional title for the enum field. + """ + type: Literal["array"] + + +class UntitledSingleSelectEnumSchema(WireModel): + """ + Schema for single-selection enumeration without display titles for options. + """ + + model_config = ConfigDict( + extra="ignore", + ) + default: str | None = None + """ + Optional default value. + """ + description: str | None = None + """ + Optional description for the enum field. + """ + enum: list[str] + """ + Array of enum values to choose from. + """ + title: str | None = None + """ + Optional title for the enum field. + """ + type: Literal["string"] + + +class Annotations(WireModel): + """ + Optional annotations for the client. The client can use annotations to inform how objects are used or displayed + """ + + model_config = ConfigDict( + extra="ignore", + ) + audience: list[Role] | None = None + """ + Describes who the intended audience of this object or data is. + + It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`). + """ + last_modified: Annotated[str | None, Field(alias="lastModified")] = None + """ + The moment the resource was last modified, as an ISO 8601 formatted string. + + Should be an ISO 8601 formatted string (e.g., "2025-01-12T15:00:58Z"). + + Examples: last activity timestamp in an open file, timestamp when the resource + was attached, etc. + """ + priority: Annotated[float | None, Field(ge=0.0, le=1.0)] = None + """ + Describes how important this data is for operating the server. + + A value of 1 means "most important," and indicates that the data is + effectively required, while 0 means "least important," and indicates that + the data is entirely optional. + """ + + +class AudioContent(WireModel): + """ + Audio provided to or from an LLM. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + data: str + """ + The base64-encoded audio data. + """ + mime_type: Annotated[str, Field(alias="mimeType")] + """ + The MIME type of the audio. Different providers may support different audio types. + """ + type: Literal["audio"] + + +class CallToolRequestParams(WireModel): + """ + Parameters for a `tools/call` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + arguments: dict[str, Any] | None = None + """ + Arguments to use for the tool call. + """ + name: str + """ + The name of the tool. + """ + task: TaskMetadata | None = None + """ + If specified, the caller is requesting task-augmented execution for this request. + The request will return a CreateTaskResult immediately, and the actual result can be + retrieved later via tasks/result. + + Task augmentation is subject to capability negotiation - receivers MUST declare support + for task augmentation of specific request types in their capabilities. + """ + + +class CancelTaskRequest(WireModel): + """ + A request to cancel a task. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["tasks/cancel"] + params: Params + + +class CancelledNotificationParams(WireModel): + """ + Parameters for a `notifications/cancelled` notification. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + reason: str | None = None + """ + An optional string describing the reason for the cancellation. This MAY be logged or presented to the user. + """ + request_id: Annotated[RequestId | None, Field(alias="requestId")] = None + """ + The ID of the request to cancel. + + This MUST correspond to the ID of a request previously issued in the same direction. + This MUST be provided for cancelling non-task requests. + This MUST NOT be used for cancelling tasks (use the `tasks/cancel` request instead). + """ + + +class CompleteRequestParams(WireModel): + """ + Parameters for a `completion/complete` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + argument: Argument + """ + The argument's information + """ + context: Context | None = None + """ + Additional, optional context for completions + """ + ref: PromptReference | ResourceTemplateReference + + +class ElicitRequestFormParams(WireModel): + """ + The parameters for a request to elicit non-sensitive information from the user via a form in the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + message: str + """ + The message to present to the user describing what information is being requested. + """ + mode: Literal["form"] = "form" + """ + The elicitation mode. + """ + requested_schema: Annotated[RequestedSchema, Field(alias="requestedSchema")] + """ + A restricted subset of JSON Schema. + Only top-level properties are allowed, without nesting. + """ + task: TaskMetadata | None = None + """ + If specified, the caller is requesting task-augmented execution for this request. + The request will return a CreateTaskResult immediately, and the actual result can be + retrieved later via tasks/result. + + Task augmentation is subject to capability negotiation - receivers MUST declare support + for task augmentation of specific request types in their capabilities. + """ + + +class ElicitRequestURLParams(WireModel): + """ + The parameters for a request to elicit information from the user via a URL in the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + elicitation_id: Annotated[str, Field(alias="elicitationId")] + """ + The ID of the elicitation, which must be unique within the context of the server. + The client MUST treat this ID as an opaque value. + """ + message: str + """ + The message to present to the user explaining why the interaction is needed. + """ + mode: Literal["url"] + """ + The elicitation mode. + """ + task: TaskMetadata | None = None + """ + If specified, the caller is requesting task-augmented execution for this request. + The request will return a CreateTaskResult immediately, and the actual result can be + retrieved later via tasks/result. + + Task augmentation is subject to capability negotiation - receivers MUST declare support + for task augmentation of specific request types in their capabilities. + """ + url: str + """ + The URL that the user should navigate to. + """ + + +class EmbeddedResource(WireModel): + """ + The contents of a resource, embedded into a prompt or tool call result. + + It is up to the client how best to render embedded resources for the benefit + of the LLM and/or the user. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + resource: TextResourceContents | BlobResourceContents + type: Literal["resource"] + + +class EmptyResult(RootModel[Result]): + root: Result + + +class EnumSchema( + RootModel[ + UntitledSingleSelectEnumSchema + | TitledSingleSelectEnumSchema + | UntitledMultiSelectEnumSchema + | TitledMultiSelectEnumSchema + | LegacyTitledEnumSchema + ] +): + root: ( + UntitledSingleSelectEnumSchema + | TitledSingleSelectEnumSchema + | UntitledMultiSelectEnumSchema + | TitledMultiSelectEnumSchema + | LegacyTitledEnumSchema + ) + + +class GetPromptRequestParams(WireModel): + """ + Parameters for a `prompts/get` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + arguments: dict[str, str] | None = None + """ + Arguments to use for templating the prompt. + """ + name: str + """ + The name of the prompt or prompt template. + """ + + +class GetTaskPayloadRequest(WireModel): + """ + A request to retrieve the result of a completed task. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["tasks/result"] + params: Params2 + + +class GetTaskRequest(WireModel): + """ + A request to retrieve the state of a task. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["tasks/get"] + params: Params3 + + +class ImageContent(WireModel): + """ + An image provided to or from an LLM. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + data: str + """ + The base64-encoded image data. + """ + mime_type: Annotated[str, Field(alias="mimeType")] + """ + The MIME type of the image. Different providers may support different image types. + """ + type: Literal["image"] + + +class InitializeRequestParams(WireModel): + """ + Parameters for an `initialize` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + capabilities: ClientCapabilities + client_info: Annotated[Implementation, Field(alias="clientInfo")] + protocol_version: Annotated[str, Field(alias="protocolVersion")] + """ + The latest version of the Model Context Protocol that the client supports. The client MAY decide to support older versions as well. + """ + + +class InitializeResult(WireModel): + """ + After receiving an initialize request from the client, the server sends this response. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + capabilities: ServerCapabilities + instructions: str | None = None + """ + Instructions describing how to use the server and its features. + + This can be used by clients to improve the LLM's understanding of available tools, resources, etc. It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt. + """ + protocol_version: Annotated[str, Field(alias="protocolVersion")] + """ + The version of the Model Context Protocol that the server wants to use. This may not match the version that the client requested. If the client cannot support this version, it MUST disconnect. + """ + server_info: Annotated[Implementation, Field(alias="serverInfo")] + + +class InitializedNotification(WireModel): + """ + This notification is sent from the client to the server after initialization has finished. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/initialized"] + params: NotificationParams | None = None + + +class JSONRPCErrorResponse(WireModel): + """ + A response to a request that indicates an error occurred. + """ + + model_config = ConfigDict( + extra="ignore", + ) + error: Error + id: RequestId | None = None + jsonrpc: Literal["2.0"] + + +class JSONRPCRequest(WireModel): + """ + A request that expects a response. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: str + params: dict[str, Any] | None = None + + +class JSONRPCResultResponse(WireModel): + """ + A successful (non-error) response to a request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + result: Result + + +class ListRootsRequest(WireModel): + """ + Sent from the server to request a list of root URIs from the client. Roots allow + servers to ask for specific directories or files to operate on. A common example + for roots is providing a set of repositories or directories a server should operate + on. + + This request is typically used when the server needs to understand the file system + structure or access specific locations that the client has permission to read from. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["roots/list"] + params: RequestParams | None = None + + +class ListRootsResult(WireModel): + """ + The client's response to a roots/list request from the server. + This result contains an array of Root objects, each representing a root directory + or file that the server can operate on. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + roots: list[Root] + + +class LoggingMessageNotification(WireModel): + """ + JSONRPCNotification of a log message passed from server to client. If no logging/setLevel request has been sent from the client, the server MAY decide which messages to send automatically. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/message"] + params: LoggingMessageNotificationParams + + +class MultiSelectEnumSchema(RootModel[UntitledMultiSelectEnumSchema | TitledMultiSelectEnumSchema]): + root: UntitledMultiSelectEnumSchema | TitledMultiSelectEnumSchema + + +class PaginatedRequestParams(WireModel): + """ + Common parameters for paginated requests. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + cursor: str | None = None + """ + An opaque token representing the current pagination position. + If provided, the server should return results starting after this cursor. + """ + + +class PingRequest(WireModel): + """ + A ping, issued by either the server or the client, to check that the other party is still alive. The receiver must promptly respond, or else may be disconnected. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["ping"] + params: RequestParams | None = None + + +class PrimitiveSchemaDefinition( + RootModel[ + StringSchema + | NumberSchema + | BooleanSchema + | UntitledSingleSelectEnumSchema + | TitledSingleSelectEnumSchema + | UntitledMultiSelectEnumSchema + | TitledMultiSelectEnumSchema + | LegacyTitledEnumSchema + ] +): + root: ( + StringSchema + | NumberSchema + | BooleanSchema + | UntitledSingleSelectEnumSchema + | TitledSingleSelectEnumSchema + | UntitledMultiSelectEnumSchema + | TitledMultiSelectEnumSchema + | LegacyTitledEnumSchema + ) + """ + Restricted schema definitions that only allow primitive types + without nested objects or arrays. + """ + + +class ProgressNotificationParams(WireModel): + """ + Parameters for a `notifications/progress` notification. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + message: str | None = None + """ + An optional message describing the current progress. + """ + progress: float + """ + The progress thus far. This should increase every time progress is made, even if the total is unknown. + """ + progress_token: Annotated[ProgressToken, Field(alias="progressToken")] + """ + The progress token which was given in the initial request, used to associate this notification with the request that is proceeding. + """ + total: float | None = None + """ + Total number of items to process (or total progress required), if known. + """ + + +class Prompt(WireModel): + """ + A prompt or prompt template that the server offers. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + arguments: list[PromptArgument] | None = None + """ + A list of arguments to use for templating the prompt. + """ + description: str | None = None + """ + An optional description of what this prompt provides + """ + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for Tool, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + + +class ReadResourceRequest(WireModel): + """ + Sent from the client to the server, to read a specific resource URI. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["resources/read"] + params: ReadResourceRequestParams + + +class ReadResourceResult(WireModel): + """ + The server's response to a resources/read request from the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + contents: list[TextResourceContents | BlobResourceContents] + + +class Resource(WireModel): + """ + A known resource that the server is capable of reading. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + description: str | None = None + """ + A description of what this resource represents. + + This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + """ + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + The MIME type of this resource, if known. + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + size: int | None = None + """ + The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known. + + This can be used by Hosts to display file sizes and estimate context window usage. + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for Tool, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + uri: str + """ + The URI of this resource. + """ + + +class ResourceLink(WireModel): + """ + A resource that the server is capable of reading, included in a prompt or tool call result. + + Note: resource links returned by tools are not guaranteed to appear in the results of `resources/list` requests. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + description: str | None = None + """ + A description of what this resource represents. + + This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + """ + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + The MIME type of this resource, if known. + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + size: int | None = None + """ + The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known. + + This can be used by Hosts to display file sizes and estimate context window usage. + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for Tool, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + type: Literal["resource_link"] + uri: str + """ + The URI of this resource. + """ + + +class ResourceTemplate(WireModel): + """ + A template description for resources available on the server. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + description: str | None = None + """ + A description of what this template is for. + + This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + """ + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + The MIME type for all resources that match this template. This should only be included if all resources matching this template have the same type. + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for Tool, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + uri_template: Annotated[str, Field(alias="uriTemplate")] + """ + A URI template (according to RFC 6570) that can be used to construct resource URIs. + """ + + +class ResourceUpdatedNotification(WireModel): + """ + A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/resources/updated"] + params: ResourceUpdatedNotificationParams + + +class SetLevelRequest(WireModel): + """ + A request from the client to the server, to enable or adjust logging. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["logging/setLevel"] + params: SetLevelRequestParams + + +class SingleSelectEnumSchema(RootModel[UntitledSingleSelectEnumSchema | TitledSingleSelectEnumSchema]): + root: UntitledSingleSelectEnumSchema | TitledSingleSelectEnumSchema + + +class SubscribeRequest(WireModel): + """ + Sent from the client to request resources/updated notifications from the server whenever a particular resource changes. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["resources/subscribe"] + params: SubscribeRequestParams + + +class Task(WireModel): + """ + Data associated with a task. + """ + + model_config = ConfigDict( + extra="ignore", + ) + created_at: Annotated[str, Field(alias="createdAt")] + """ + ISO 8601 timestamp when the task was created. + """ + last_updated_at: Annotated[str, Field(alias="lastUpdatedAt")] + """ + ISO 8601 timestamp when the task was last updated. + """ + poll_interval: Annotated[int | None, Field(alias="pollInterval")] = None + """ + Suggested polling interval in milliseconds. + """ + status: TaskStatus + """ + Current task state. + """ + status_message: Annotated[str | None, Field(alias="statusMessage")] = None + """ + Optional human-readable message describing the current task state. + This can provide context for any status, including: + - Reasons for "cancelled" status + - Summaries for "completed" status + - Diagnostic information for "failed" status (e.g., error details, what went wrong) + """ + task_id: Annotated[str, Field(alias="taskId")] + """ + The task identifier. + """ + ttl: int | None + """ + Actual retention duration from creation in milliseconds, null for unlimited. + """ + + +class TaskAugmentedRequestParams(WireModel): + """ + Common params for any task-augmented request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + task: TaskMetadata | None = None + """ + If specified, the caller is requesting task-augmented execution for this request. + The request will return a CreateTaskResult immediately, and the actual result can be + retrieved later via tasks/result. + + Task augmentation is subject to capability negotiation - receivers MUST declare support + for task augmentation of specific request types in their capabilities. + """ + + +class TaskStatusNotificationParams(NotificationParams, Task): + """ + Parameters for a `notifications/tasks/status` notification. + """ + + model_config = ConfigDict( + extra="ignore", + ) + + +class TextContent(WireModel): + """ + Text provided to or from an LLM. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + text: str + """ + The text content of the message. + """ + type: Literal["text"] + + +class Tool(WireModel): + """ + Definition for a tool the client can call. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + annotations: ToolAnnotations | None = None + """ + Optional additional tool information. + + Display name precedence order is: title, annotations.title, then name. + """ + description: str | None = None + """ + A human-readable description of the tool. + + This can be used by clients to improve the LLM's understanding of available tools. It can be thought of like a "hint" to the model. + """ + execution: ToolExecution | None = None + """ + Execution-related properties for this tool. + """ + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + input_schema: Annotated[InputSchema, Field(alias="inputSchema")] + """ + A JSON Schema object defining the expected parameters for the tool. + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + output_schema: Annotated[OutputSchema | None, Field(alias="outputSchema")] = None + """ + An optional JSON Schema object defining the structure of the tool's output returned in + the structuredContent field of a CallToolResult. + + Defaults to JSON Schema 2020-12 when no explicit $schema is provided. + Currently restricted to type: "object" at the root level. + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for Tool, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + + +class Data(WireModel): + """ + Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + """ + + model_config = ConfigDict( + extra="allow", + ) + elicitations: list[ElicitRequestURLParams] + + +class Error1(Error): + model_config = ConfigDict( + extra="ignore", + ) + code: Literal[-32042] + """ + The error type that occurred. + """ + data: Data + """ + Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + """ + + +class URLElicitationRequiredError(WireModel): + """ + An error response that indicates that the server requires the client to provide additional information via an elicitation request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + error: Error1 + id: RequestId | None = None + jsonrpc: Literal["2.0"] + + +class UnsubscribeRequest(WireModel): + """ + Sent from the client to request cancellation of resources/updated notifications from the server. This should follow a previous resources/subscribe request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["resources/unsubscribe"] + params: UnsubscribeRequestParams + + +class CallToolRequest(WireModel): + """ + Used by the client to invoke a tool provided by the server. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["tools/call"] + params: CallToolRequestParams + + +class CancelTaskResult(Result, Task): + """ + The response to a tasks/cancel request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + + +class CancelledNotification(WireModel): + """ + This notification can be sent by either side to indicate that it is cancelling a previously-issued request. + + The request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished. + + This notification indicates that the result will be unused, so any associated processing SHOULD cease. + + A client MUST NOT attempt to cancel its `initialize` request. + + For task cancellation, use the `tasks/cancel` request instead of this notification. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/cancelled"] + params: CancelledNotificationParams + + +class CompleteRequest(WireModel): + """ + A request from the client to the server, to ask for completion options. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["completion/complete"] + params: CompleteRequestParams + + +class ContentBlock(RootModel[TextContent | ImageContent | AudioContent | ResourceLink | EmbeddedResource]): + root: TextContent | ImageContent | AudioContent | ResourceLink | EmbeddedResource + + +class CreateTaskResult(WireModel): + """ + A response to a task-augmented request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + task: Task + + +class ElicitRequestParams(RootModel[ElicitRequestURLParams | ElicitRequestFormParams]): + root: ElicitRequestURLParams | ElicitRequestFormParams + """ + The parameters for a request to elicit additional information from the user via the client. + """ + + +class GetPromptRequest(WireModel): + """ + Used by the client to get a prompt provided by the server. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["prompts/get"] + params: GetPromptRequestParams + + +class GetTaskResult(Result, Task): + """ + The response to a tasks/get request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + + +class InitializeRequest(WireModel): + """ + This request is sent from the client to the server when it first connects, asking it to begin initialization. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["initialize"] + params: InitializeRequestParams + + +class JSONRPCMessage(RootModel[JSONRPCRequest | JSONRPCNotification | JSONRPCResultResponse | JSONRPCErrorResponse]): + root: JSONRPCRequest | JSONRPCNotification | JSONRPCResultResponse | JSONRPCErrorResponse + """ + Refers to any valid JSON-RPC object that can be decoded off the wire, or encoded to be sent. + """ + + +class JSONRPCResponse(RootModel[JSONRPCResultResponse | JSONRPCErrorResponse]): + root: JSONRPCResultResponse | JSONRPCErrorResponse + """ + A response to a request, containing either the result or error. + """ + + +class ListPromptsRequest(WireModel): + """ + Sent from the client to request a list of prompts and prompt templates the server has. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["prompts/list"] + params: PaginatedRequestParams | None = None + + +class ListPromptsResult(WireModel): + """ + The server's response to a prompts/list request from the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + prompts: list[Prompt] + + +class ListResourceTemplatesRequest(WireModel): + """ + Sent from the client to request a list of resource templates the server has. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["resources/templates/list"] + params: PaginatedRequestParams | None = None + + +class ListResourceTemplatesResult(WireModel): + """ + The server's response to a resources/templates/list request from the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + resource_templates: Annotated[list[ResourceTemplate], Field(alias="resourceTemplates")] + + +class ListResourcesRequest(WireModel): + """ + Sent from the client to request a list of resources the server has. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["resources/list"] + params: PaginatedRequestParams | None = None + + +class ListResourcesResult(WireModel): + """ + The server's response to a resources/list request from the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + resources: list[Resource] + + +class ListTasksRequest(WireModel): + """ + A request to retrieve a list of tasks. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["tasks/list"] + params: PaginatedRequestParams | None = None + + +class ListTasksResult(WireModel): + """ + The response to a tasks/list request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + tasks: list[Task] + + +class ListToolsRequest(WireModel): + """ + Sent from the client to request a list of tools the server has. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["tools/list"] + params: PaginatedRequestParams | None = None + + +class ListToolsResult(WireModel): + """ + The server's response to a tools/list request from the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + tools: list[Tool] + + +class PaginatedRequest(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: str + params: PaginatedRequestParams | None = None + + +class ProgressNotification(WireModel): + """ + An out-of-band notification used to inform the receiver of a progress update for a long-running request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/progress"] + params: ProgressNotificationParams + + +class PromptMessage(WireModel): + """ + Describes a message returned as part of a prompt. + + This is similar to `SamplingMessage`, but also supports the embedding of + resources from the MCP server. + """ + + model_config = ConfigDict( + extra="ignore", + ) + content: ContentBlock + role: Role + + +class TaskStatusNotification(WireModel): + """ + An optional notification from the receiver to the requestor, informing them that a task's status has changed. Receivers are not required to send these notifications. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/tasks/status"] + params: TaskStatusNotificationParams + + +class ToolResultContent(WireModel): + """ + The result of a tool use, provided by the user back to the assistant. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + Optional metadata about the tool result. Clients SHOULD preserve this field when + including tool results in subsequent sampling requests to enable caching optimizations. + + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + content: list[ContentBlock] + """ + The unstructured result content of the tool use. + + This has the same format as CallToolResult.content and can include text, images, + audio, resource links, and embedded resources. + """ + is_error: Annotated[bool | None, Field(alias="isError")] = None + """ + Whether the tool use resulted in an error. + + If true, the content typically describes the error that occurred. + Default: false + """ + structured_content: Annotated[dict[str, Any] | None, Field(alias="structuredContent")] = None + """ + An optional structured result object. + + If the tool defined an outputSchema, this SHOULD conform to that schema. + """ + tool_use_id: Annotated[str, Field(alias="toolUseId")] + """ + The ID of the tool use this result corresponds to. + + This MUST match the ID from a previous ToolUseContent. + """ + type: Literal["tool_result"] + + +class CallToolResult(WireModel): + """ + The server's response to a tool call. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + content: list[ContentBlock] + """ + A list of content objects that represent the unstructured result of the tool call. + """ + is_error: Annotated[bool | None, Field(alias="isError")] = None + """ + Whether the tool call ended in an error. + + If not set, this is assumed to be false (the call was successful). + + Any errors that originate from the tool SHOULD be reported inside the result + object, with `isError` set to true, _not_ as an MCP protocol-level error + response. Otherwise, the LLM would not be able to see that an error occurred + and self-correct. + + However, any errors in _finding_ the tool, an error indicating that the + server does not support tool calls, or any other exceptional conditions, + should be reported as an MCP error response. + """ + structured_content: Annotated[dict[str, Any] | None, Field(alias="structuredContent")] = None + """ + An optional JSON object that represents the structured result of the tool call. + """ + + +class ClientNotification( + RootModel[ + CancelledNotification + | InitializedNotification + | ProgressNotification + | TaskStatusNotification + | RootsListChangedNotification + ] +): + root: ( + CancelledNotification + | InitializedNotification + | ProgressNotification + | TaskStatusNotification + | RootsListChangedNotification + ) + + +class ClientRequest( + RootModel[ + InitializeRequest + | PingRequest + | ListResourcesRequest + | ListResourceTemplatesRequest + | ReadResourceRequest + | SubscribeRequest + | UnsubscribeRequest + | ListPromptsRequest + | GetPromptRequest + | ListToolsRequest + | CallToolRequest + | GetTaskRequest + | GetTaskPayloadRequest + | CancelTaskRequest + | ListTasksRequest + | SetLevelRequest + | CompleteRequest + ] +): + root: ( + InitializeRequest + | PingRequest + | ListResourcesRequest + | ListResourceTemplatesRequest + | ReadResourceRequest + | SubscribeRequest + | UnsubscribeRequest + | ListPromptsRequest + | GetPromptRequest + | ListToolsRequest + | CallToolRequest + | GetTaskRequest + | GetTaskPayloadRequest + | CancelTaskRequest + | ListTasksRequest + | SetLevelRequest + | CompleteRequest + ) + + +class ElicitRequest(WireModel): + """ + A request from the server to elicit additional information from the user via the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["elicitation/create"] + params: ElicitRequestParams + + +class GetPromptResult(WireModel): + """ + The server's response to a prompts/get request from the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + description: str | None = None + """ + An optional description for the prompt. + """ + messages: list[PromptMessage] + + +class SamplingMessageContentBlock( + RootModel[TextContent | ImageContent | AudioContent | ToolUseContent | ToolResultContent] +): + root: TextContent | ImageContent | AudioContent | ToolUseContent | ToolResultContent + + +class ServerNotification( + RootModel[ + CancelledNotification + | ProgressNotification + | ResourceListChangedNotification + | ResourceUpdatedNotification + | PromptListChangedNotification + | ToolListChangedNotification + | TaskStatusNotification + | LoggingMessageNotification + | ElicitationCompleteNotification + ] +): + root: ( + CancelledNotification + | ProgressNotification + | ResourceListChangedNotification + | ResourceUpdatedNotification + | PromptListChangedNotification + | ToolListChangedNotification + | TaskStatusNotification + | LoggingMessageNotification + | ElicitationCompleteNotification + ) + + +class ServerResult( + RootModel[ + Result + | InitializeResult + | ListResourcesResult + | ListResourceTemplatesResult + | ReadResourceResult + | ListPromptsResult + | GetPromptResult + | ListToolsResult + | CallToolResult + | GetTaskResult + | GetTaskPayloadResult + | CancelTaskResult + | ListTasksResult + | CompleteResult + ] +): + root: ( + Result + | InitializeResult + | ListResourcesResult + | ListResourceTemplatesResult + | ReadResourceResult + | ListPromptsResult + | GetPromptResult + | ListToolsResult + | CallToolResult + | GetTaskResult + | GetTaskPayloadResult + | CancelTaskResult + | ListTasksResult + | CompleteResult + ) + + +class CreateMessageResult(WireModel): + """ + The client's response to a sampling/createMessage request from the server. + The client should inform the user before returning the sampled message, to allow them + to inspect the response (human in the loop) and decide whether to allow the server to see it. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + content: ( + TextContent + | ImageContent + | AudioContent + | ToolUseContent + | ToolResultContent + | list[SamplingMessageContentBlock] + ) + model: str + """ + The name of the model that generated the message. + """ + role: Role + stop_reason: Annotated[str | None, Field(alias="stopReason")] = None + """ + The reason why sampling stopped, if known. + + Standard values: + - "endTurn": Natural end of the assistant's turn + - "stopSequence": A stop sequence was encountered + - "maxTokens": Maximum token limit was reached + - "toolUse": The model wants to use one or more tools + + This field is an open string to allow for provider-specific stop reasons. + """ + + +class SamplingMessage(WireModel): + """ + Describes a message issued to or received from an LLM API. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[dict[str, Any] | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + content: ( + TextContent + | ImageContent + | AudioContent + | ToolUseContent + | ToolResultContent + | list[SamplingMessageContentBlock] + ) + role: Role + + +class ClientResult( + RootModel[ + Result + | GetTaskResult + | GetTaskPayloadResult + | CancelTaskResult + | ListTasksResult + | CreateMessageResult + | ListRootsResult + | ElicitResult + ] +): + root: ( + Result + | GetTaskResult + | GetTaskPayloadResult + | CancelTaskResult + | ListTasksResult + | CreateMessageResult + | ListRootsResult + | ElicitResult + ) + + +class CreateMessageRequestParams(WireModel): + """ + Parameters for a `sampling/createMessage` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[Meta | None, Field(alias="_meta")] = None + """ + See [General fields: `_meta`](https://modelcontextprotocol.io/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage. + """ + include_context: Annotated[ + Literal["allServers", "none", "thisServer"] | None, + Field(alias="includeContext"), + ] = None + """ + A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. + The client MAY ignore this request. + + Default is "none". Values "thisServer" and "allServers" are soft-deprecated. Servers SHOULD only use these values if the client + declares ClientCapabilities.sampling.context. These values may be removed in future spec releases. + """ + max_tokens: Annotated[int, Field(alias="maxTokens")] + """ + The requested maximum number of tokens to sample (to prevent runaway completions). + + The client MAY choose to sample fewer tokens than the requested maximum. + """ + messages: list[SamplingMessage] + metadata: dict[str, Any] | None = None + """ + Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific. + """ + model_preferences: Annotated[ModelPreferences | None, Field(alias="modelPreferences")] = None + """ + The server's preferences for which model to select. The client MAY ignore these preferences. + """ + stop_sequences: Annotated[list[str] | None, Field(alias="stopSequences")] = None + system_prompt: Annotated[str | None, Field(alias="systemPrompt")] = None + """ + An optional system prompt the server wants to use for sampling. The client MAY modify or omit this prompt. + """ + task: TaskMetadata | None = None + """ + If specified, the caller is requesting task-augmented execution for this request. + The request will return a CreateTaskResult immediately, and the actual result can be + retrieved later via tasks/result. + + Task augmentation is subject to capability negotiation - receivers MUST declare support + for task augmentation of specific request types in their capabilities. + """ + temperature: float | None = None + tool_choice: Annotated[ToolChoice | None, Field(alias="toolChoice")] = None + """ + Controls how the model uses tools. + The client MUST return an error if this field is provided but ClientCapabilities.sampling.tools is not declared. + Default is `{ mode: "auto" }`. + """ + tools: list[Tool] | None = None + """ + Tools that the model may use during generation. + The client MUST return an error if this field is provided but ClientCapabilities.sampling.tools is not declared. + """ + + +class CreateMessageRequest(WireModel): + """ + A request from the server to sample an LLM via the client. The client has full discretion over which model to select. The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["sampling/createMessage"] + params: CreateMessageRequestParams + + +class ServerRequest( + RootModel[ + PingRequest + | GetTaskRequest + | GetTaskPayloadRequest + | CancelTaskRequest + | ListTasksRequest + | CreateMessageRequest + | ListRootsRequest + | ElicitRequest + ] +): + root: ( + PingRequest + | GetTaskRequest + | GetTaskPayloadRequest + | CancelTaskRequest + | ListTasksRequest + | CreateMessageRequest + | ListRootsRequest + | ElicitRequest + ) diff --git a/venv/lib/python3.12/site-packages/mcp_types/_v2025_11_25/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp_types/_v2025_11_25/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e922b7a Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp_types/_v2025_11_25/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp_types/_v2026_07_28/__init__.py b/venv/lib/python3.12/site-packages/mcp_types/_v2026_07_28/__init__.py new file mode 100644 index 0000000..fb168b3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp_types/_v2026_07_28/__init__.py @@ -0,0 +1,3746 @@ +"""Internal wire-shape models for protocol 2026-07-28. Generated; do not edit. + +Regenerate with `scripts/gen_surface_types.py` from `schema/2026-07-28.json` +(sha256 `6293cdfe015c14bd36eda4b1331ce37bda377609e58ed6d09d16f28e7d3c7ad4`).""" +# pyright: reportIncompatibleVariableOverride=false, reportGeneralTypeIssues=false + +from __future__ import annotations + +from typing import Annotated, Any, Literal, Union + +from mcp_types._wire_base import WireModel +from pydantic import ConfigDict, Field, RootModel + + +class BaseMetadata(WireModel): + """ + Base interface for metadata with name (identifier) and title (display name) properties. + """ + + model_config = ConfigDict( + extra="ignore", + ) + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for {@link Tool}, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + + +class BooleanSchema(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + default: bool | None = None + description: str | None = None + title: str | None = None + type: Literal["boolean"] + + +class Argument(WireModel): + """ + The argument's information + """ + + model_config = ConfigDict( + extra="ignore", + ) + name: str + """ + The name of the argument + """ + value: str + """ + The value of the argument to use for completion matching. + """ + + +class Context(WireModel): + """ + Additional, optional context for completions + """ + + model_config = ConfigDict( + extra="ignore", + ) + arguments: dict[str, str] | None = None + """ + Previously-resolved variables in a URI template or prompt. + """ + + +class Completion(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + has_more: Annotated[bool | None, Field(alias="hasMore")] = None + """ + Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown. + """ + total: int | None = None + """ + The total number of completion options available. This can exceed the number of values actually sent in the response. + """ + values: Annotated[list[str], Field(max_length=100)] + """ + An array of completion values. Must not exceed 100 items. + """ + + +class Cursor(RootModel[str]): + root: str + """ + An opaque token used to represent a cursor for pagination. + """ + + +class RequestedSchema(WireModel): + """ + A restricted subset of JSON Schema. + Only top-level properties are allowed, without nesting. + """ + + model_config = ConfigDict( + extra="ignore", + ) + schema_: Annotated[str | None, Field(alias="$schema")] = None + properties: dict[str, Any] + required: list[str] | None = None + type: Literal["object"] + + +class ElicitRequestFormParams(WireModel): + """ + The parameters for a request to elicit non-sensitive information from the user via a form in the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + message: str + """ + The message to present to the user describing what information is being requested. + """ + mode: Literal["form"] = "form" + """ + The elicitation mode. + """ + requested_schema: Annotated[RequestedSchema, Field(alias="requestedSchema")] + """ + A restricted subset of JSON Schema. + Only top-level properties are allowed, without nesting. + """ + + +class ElicitRequestURLParams(WireModel): + """ + The parameters for a request to elicit information from the user via a URL in the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + message: str + """ + The message to present to the user explaining why the interaction is needed. + """ + mode: Literal["url"] + """ + The elicitation mode. + """ + url: str + """ + The URL that the user should navigate to. + """ + + +class ElicitResult(WireModel): + """ + The result returned by the client for an {@link ElicitRequestelicitation/create} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + action: Literal["accept", "cancel", "decline"] + """ + The user action in response to the elicitation. + - `"accept"`: User submitted the form/confirmed the action + - `"decline"`: User explicitly declined the action + - `"cancel"`: User dismissed without making an explicit choice + """ + content: dict[str, list[str] | str | int | float | bool | None] | None = None + """ + The submitted form data, only present when action is `"accept"` and mode was `"form"`. + Contains values matching the requested schema. + Omitted for out-of-band mode responses. + """ + + +class Error(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + code: int + """ + The error type that occurred. + """ + data: Any | None = None + """ + Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + """ + message: str + """ + A short description of the error. The message SHOULD be limited to a concise single sentence. + """ + + +class Error1(Error): + model_config = ConfigDict( + extra="ignore", + ) + code: Literal[-32020] + """ + The error type that occurred. + """ + + +class Icon(WireModel): + """ + An optionally-sized icon that can be displayed in a user interface. + """ + + model_config = ConfigDict( + extra="ignore", + ) + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + Optional MIME type override if the source MIME type is missing or generic. + For example: `"image/png"`, `"image/jpeg"`, or `"image/svg+xml"`. + """ + sizes: list[str] | None = None + """ + Optional array of strings that specify sizes at which the icon can be used. + Each string should be in WxH format (e.g., `"48x48"`, `"96x96"`) or `"any"` for scalable formats like SVG. + + If not provided, the client should assume that the icon can be used at any size. + """ + src: str + """ + A standard URI pointing to an icon resource. May be an HTTP/HTTPS URL or a + `data:` URI with Base64-encoded image data. + + Consumers SHOULD take steps to ensure URLs serving icons are from the + same domain as the client/server or a trusted domain. + + Consumers SHOULD take appropriate precautions when consuming SVGs as they can contain + executable JavaScript. + """ + theme: Literal["dark", "light"] | None = None + """ + Optional specifier for the theme this icon is designed for. `"light"` indicates + the icon is designed to be used with a light background, and `"dark"` indicates + the icon is designed to be used with a dark background. + + If not provided, the client should assume the icon can be used with any theme. + """ + + +class Icons(WireModel): + """ + Base interface to add `icons` property. + """ + + model_config = ConfigDict( + extra="ignore", + ) + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + + +class Implementation(WireModel): + """ + Describes the MCP implementation. + """ + + model_config = ConfigDict( + extra="ignore", + ) + description: str | None = None + """ + An optional human-readable description of what this implementation does. + + This can be used by clients or servers to provide context about their purpose + and capabilities. For example, a server might describe the types of resources + or tools it provides, while a client might describe its intended use case. + """ + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for {@link Tool}, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + version: str + """ + The version of this implementation. + """ + website_url: Annotated[str | None, Field(alias="websiteUrl")] = None + """ + An optional URL of the website for this implementation. + """ + + +class InternalError(WireModel): + """ + A JSON-RPC error indicating that an internal error occurred on the receiver. This error is returned when the receiver encounters an unexpected condition that prevents it from fulfilling the request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + code: Literal[-32603] + """ + The error type that occurred. + """ + data: Any | None = None + """ + Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + """ + message: str + """ + A short description of the error. The message SHOULD be limited to a concise single sentence. + """ + + +class InvalidParamsError(WireModel): + """ + A JSON-RPC error indicating that the method parameters are invalid or malformed. + + In MCP, this error is returned in various contexts when request parameters fail validation: + + - **Tools**: Unknown tool name or invalid tool arguments + - **Prompts**: Unknown prompt name or missing required arguments + - **Pagination**: Invalid or expired cursor values + - **Logging**: Invalid log level + - **Elicitation**: Server requests an elicitation mode not declared in client capabilities + - **Sampling**: Missing tool result or tool results mixed with other content + """ + + model_config = ConfigDict( + extra="ignore", + ) + code: Literal[-32602] + """ + The error type that occurred. + """ + data: Any | None = None + """ + Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + """ + message: str + """ + A short description of the error. The message SHOULD be limited to a concise single sentence. + """ + + +class InvalidRequestError(WireModel): + """ + A JSON-RPC error indicating that the request is not a valid request object. This error is returned when the message structure does not conform to the JSON-RPC 2.0 specification requirements for a request (e.g., missing required fields like `jsonrpc` or `method`, or using invalid types for these fields). + """ + + model_config = ConfigDict( + extra="ignore", + ) + code: Literal[-32600] + """ + The error type that occurred. + """ + data: Any | None = None + """ + Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + """ + message: str + """ + A short description of the error. The message SHOULD be limited to a concise single sentence. + """ + + +class JSONRPCNotification(WireModel): + """ + A notification which does not expect a response. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: str + params: dict[str, Any] | None = None + + +class LegacyTitledEnumSchema(WireModel): + """ + Use {@link TitledSingleSelectEnumSchema} instead. + This interface will be removed in a future version. + """ + + model_config = ConfigDict( + extra="ignore", + ) + default: str | None = None + description: str | None = None + enum: list[str] + enum_names: Annotated[list[str] | None, Field(alias="enumNames")] = None + """ + (Legacy) Display names for enum values. + Non-standard according to JSON schema 2020-12. + """ + title: str | None = None + type: Literal["string"] + + +class LoggingLevel( + RootModel[ + Literal[ + "alert", + "critical", + "debug", + "emergency", + "error", + "info", + "notice", + "warning", + ] + ] +): + root: Literal["alert", "critical", "debug", "emergency", "error", "info", "notice", "warning"] + """ + The severity of a log message. + + These map to syslog message severities, as specified in RFC-5424: + https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1 + """ + + +class MetaObject(WireModel): + """ + Represents the contents of a `_meta` field, which clients and servers use to attach additional metadata to their interactions. + + Certain key names are reserved by MCP for protocol-level metadata; implementations MUST NOT make assumptions about values at these keys. Additionally, specific schema definitions may reserve particular names for purpose-specific metadata, as declared in those definitions. + + Valid keys have two segments: + + **Prefix:** + - Optional — if specified, MUST be a series of _labels_ separated by dots (`.`), followed by a slash (`/`). + - Labels MUST start with a letter and end with a letter or digit. Interior characters may be letters, digits, or hyphens (`-`). + - Implementations SHOULD use reverse DNS notation (e.g., `com.example/` rather than `example.com/`). + - Any prefix where the second label is `modelcontextprotocol` or `mcp` is **reserved** for MCP use. For example: `io.modelcontextprotocol/`, `dev.mcp/`, `org.modelcontextprotocol.api/`, and `com.mcp.tools/` are all reserved. However, `com.example.mcp/` is NOT reserved, as the second label is `example`. + + **Name:** + - Unless empty, MUST start and end with an alphanumeric character (`[a-z0-9A-Z]`). + - Interior characters may be alphanumeric, hyphens (`-`), underscores (`_`), or dots (`.`). + """ + + model_config = ConfigDict( + extra="allow", + ) + + +class MethodNotFoundError(WireModel): + """ + A JSON-RPC error indicating that the requested method does not exist or is not available. + + In MCP, a server returns this error when a client invokes a method the server does not implement — either a genuinely unknown method, or one gated behind a server capability the server did not advertise (e.g., calling `prompts/list` when the `prompts` capability was not advertised). + + A request that requires a client capability the client did not declare is signalled instead by {@link MissingRequiredClientCapabilityError} (`-32021`). + """ + + model_config = ConfigDict( + extra="ignore", + ) + code: Literal[-32601] + """ + The error type that occurred. + """ + data: Any | None = None + """ + Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + """ + message: str + """ + A short description of the error. The message SHOULD be limited to a concise single sentence. + """ + + +class ModelHint(WireModel): + """ + Hints to use for model selection. + + Keys not declared here are currently left unspecified by the spec and are up + to the client to interpret. + """ + + model_config = ConfigDict( + extra="ignore", + ) + name: str | None = None + """ + A hint for a model name. + + The client SHOULD treat this as a substring of a model name; for example: + - `claude-3-5-sonnet` should match `claude-3-5-sonnet-20241022` + - `sonnet` should match `claude-3-5-sonnet-20241022`, `claude-3-sonnet-20240229`, etc. + - `claude` should match any Claude model + + The client MAY also map the string to a different provider's model name or a different model family, as long as it fills a similar niche; for example: + - `gemini-1.5-flash` could match `claude-3-haiku-20240307` + """ + + +class ModelPreferences(WireModel): + """ + The server's preferences for model selection, requested of the client during sampling. + + Because LLMs can vary along multiple dimensions, choosing the "best" model is + rarely straightforward. Different models excel in different areas—some are + faster but less capable, others are more capable but more expensive, and so + on. This interface allows servers to express their priorities across multiple + dimensions to help clients make an appropriate selection for their use case. + + These preferences are always advisory. The client MAY ignore them. It is also + up to the client to decide how to interpret these preferences and how to + balance them against other considerations. + """ + + model_config = ConfigDict( + extra="ignore", + ) + cost_priority: Annotated[float | None, Field(alias="costPriority", ge=0.0, le=1.0)] = None + """ + How much to prioritize cost when selecting a model. A value of 0 means cost + is not important, while a value of 1 means cost is the most important + factor. + """ + hints: list[ModelHint] | None = None + """ + Optional hints to use for model selection. + + If multiple hints are specified, the client MUST evaluate them in order + (such that the first match is taken). + + The client SHOULD prioritize these hints over the numeric priorities, but + MAY still use the priorities to select from ambiguous matches. + """ + intelligence_priority: Annotated[float | None, Field(alias="intelligencePriority", ge=0.0, le=1.0)] = None + """ + How much to prioritize intelligence and capabilities when selecting a + model. A value of 0 means intelligence is not important, while a value of 1 + means intelligence is the most important factor. + """ + speed_priority: Annotated[float | None, Field(alias="speedPriority", ge=0.0, le=1.0)] = None + """ + How much to prioritize sampling speed (latency) when selecting a model. A + value of 0 means speed is not important, while a value of 1 means speed is + the most important factor. + """ + + +class Notification(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + method: str + params: dict[str, Any] | None = None + + +class NumberSchema(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + default: int | float | None = None + description: str | None = None + maximum: int | float | None = None + minimum: int | float | None = None + title: str | None = None + type: Literal["integer", "number"] + + +class ParseError(WireModel): + """ + A JSON-RPC error indicating that invalid JSON was received by the server. This error is returned when the server cannot parse the JSON text of a message. + """ + + model_config = ConfigDict( + extra="ignore", + ) + code: Literal[-32700] + """ + The error type that occurred. + """ + data: Any | None = None + """ + Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + """ + message: str + """ + A short description of the error. The message SHOULD be limited to a concise single sentence. + """ + + +class ProgressToken(RootModel[str | int]): + root: str | int + """ + A progress token, used to associate progress notifications with the original request. + """ + + +class PromptArgument(WireModel): + """ + Describes an argument that a prompt can accept. + """ + + model_config = ConfigDict( + extra="ignore", + ) + description: str | None = None + """ + A human-readable description of the argument. + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + required: bool | None = None + """ + Whether this argument must be provided. + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for {@link Tool}, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + + +class PromptReference(WireModel): + """ + Identifies a prompt. + """ + + model_config = ConfigDict( + extra="ignore", + ) + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for {@link Tool}, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + type: Literal["ref/prompt"] + + +class Request(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + method: str + params: dict[str, Any] | None = None + + +class RequestId(RootModel[str | int]): + root: str | int + """ + A uniquely identifying ID for a request in JSON-RPC. + """ + + +class ResourceContents(WireModel): + """ + The contents of a specific resource or sub-resource. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + The MIME type of this resource, if known. + """ + uri: str + """ + The URI of this resource. + """ + + +class ResourceTemplateReference(WireModel): + """ + A reference to a resource or resource template definition. + """ + + model_config = ConfigDict( + extra="ignore", + ) + type: Literal["ref/resource"] + uri: str + """ + The URI or URI template of the resource. + """ + + +class ResultMetaObject(WireModel): + """ + Extends {@link MetaObject} with additional result-specific fields. All key naming rules from `MetaObject` apply. + """ + + model_config = ConfigDict( + extra="allow", + ) + io_modelcontextprotocol_server_info: Annotated[Any | None, Field(alias="io.modelcontextprotocol/serverInfo")] = None + """ + Identifies the server software producing the response. Servers SHOULD + include this field on every response unless specifically configured not + to do so. + + The {@link Implementation} schema requires `name` and `version`; other + fields are optional. + + The value is self-reported by the server and is not verified by the + protocol. It is intended for display, logging, and debugging. Clients + SHOULD NOT use it to change their behavior, and SHOULD NOT rely on it for + security decisions. + """ + + +class ResultType(RootModel[str]): + root: str + """ + Indicates the type of a {@link Result} object, allowing the client to + determine how to parse the response. + + complete - the request completed successfully and the result contains the final content. + input_required - the request requires additional input and the result contains an {@link InputRequiredResult} object with instructions for the client to provide additional input before retrying the original request. + """ + + +class Role(RootModel[Literal["assistant", "user"]]): + root: Literal["assistant", "user"] + """ + The sender or recipient of messages and data in a conversation. + """ + + +class Root(WireModel): + """ + Represents a root directory or file that the server can operate on. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + name: str | None = None + """ + An optional name for the root. This can be used to provide a human-readable + identifier for the root, which may be useful for display purposes or for + referencing the root in other parts of the application. + """ + uri: str + """ + The URI identifying the root. This *must* start with `file://` for now. + This restriction may be relaxed in future versions of the protocol to allow + other URI schemes. + """ + + +class Prompts(WireModel): + """ + Present if the server offers any prompt templates. + """ + + model_config = ConfigDict( + extra="ignore", + ) + list_changed: Annotated[bool | None, Field(alias="listChanged")] = None + """ + Whether this server supports notifications for changes to the prompt list. + """ + + +class Resources(WireModel): + """ + Present if the server offers any resources to read. + """ + + model_config = ConfigDict( + extra="ignore", + ) + list_changed: Annotated[bool | None, Field(alias="listChanged")] = None + """ + Whether this server supports notifications for changes to the resource list. + """ + subscribe: bool | None = None + """ + Whether this server supports subscribing to resource updates. + """ + + +class Tools(WireModel): + """ + Present if the server offers any tools to call. + """ + + model_config = ConfigDict( + extra="ignore", + ) + list_changed: Annotated[bool | None, Field(alias="listChanged")] = None + """ + Whether this server supports notifications for changes to the tool list. + """ + + +class StringSchema(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + default: str | None = None + description: str | None = None + format: Literal["date", "date-time", "email", "uri"] | None = None + max_length: Annotated[int | None, Field(alias="maxLength")] = None + min_length: Annotated[int | None, Field(alias="minLength")] = None + title: str | None = None + type: Literal["string"] + + +class SubscriptionFilter(WireModel): + """ + The set of notification types a client may opt in to on a + {@link SubscriptionsListenRequestsubscriptions/listen} request. + + Each notification type is **opt-in**; the server **MUST NOT** send + notification types the client has not explicitly requested here. + """ + + model_config = ConfigDict( + extra="ignore", + ) + prompts_list_changed: Annotated[bool | None, Field(alias="promptsListChanged")] = None + """ + If true, receive {@link PromptListChangedNotificationnotifications/prompts/list_changed}. + """ + resource_subscriptions: Annotated[list[str] | None, Field(alias="resourceSubscriptions")] = None + """ + Subscribe to {@link ResourceUpdatedNotificationnotifications/resources/updated} for these resource URIs. + Replaces the former `resources/subscribe` RPC. + """ + resources_list_changed: Annotated[bool | None, Field(alias="resourcesListChanged")] = None + """ + If true, receive {@link ResourceListChangedNotificationnotifications/resources/list_changed}. + """ + tools_list_changed: Annotated[bool | None, Field(alias="toolsListChanged")] = None + """ + If true, receive {@link ToolListChangedNotificationnotifications/tools/list_changed}. + """ + + +class SubscriptionsListenResultMeta(WireModel): + """ + Extends {@link ResultMetaObject} with the subscription-stream identifier carried by a + {@link SubscriptionsListenResult}. All key naming rules from `MetaObject` apply. + """ + + model_config = ConfigDict( + extra="allow", + ) + io_modelcontextprotocol_server_info: Annotated[Any | None, Field(alias="io.modelcontextprotocol/serverInfo")] = None + """ + Identifies the server software producing the response. Servers SHOULD + include this field on every response unless specifically configured not + to do so. + + The {@link Implementation} schema requires `name` and `version`; other + fields are optional. + + The value is self-reported by the server and is not verified by the + protocol. It is intended for display, logging, and debugging. Clients + SHOULD NOT use it to change their behavior, and SHOULD NOT rely on it for + security decisions. + """ + io_modelcontextprotocol_subscription_id: Annotated[RequestId, Field(alias="io.modelcontextprotocol/subscriptionId")] + """ + Identifies the subscription stream this response closes, so the client can + correlate it with the originating subscription — mirroring the same key on + the stream's notifications. The value is the JSON-RPC ID of the + `subscriptions/listen` request that opened the stream (and equals this + response's `id`). + """ + + +class TextResourceContents(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + The MIME type of this resource, if known. + """ + text: str + """ + The text of the item. This must only be set if the item can actually be represented as text (not binary data). + """ + uri: str + """ + The URI of this resource. + """ + + +class AnyOfItem(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + const: str + """ + The constant enum value. + """ + title: str + """ + Display title for this option. + """ + + +class Items(WireModel): + """ + Schema for array items with enum options and display labels. + """ + + model_config = ConfigDict( + extra="ignore", + ) + any_of: Annotated[list[AnyOfItem], Field(alias="anyOf")] + """ + Array of enum options with values and display labels. + """ + + +class TitledMultiSelectEnumSchema(WireModel): + """ + Schema for multiple-selection enumeration with display titles for each option. + """ + + model_config = ConfigDict( + extra="ignore", + ) + default: list[str] | None = None + """ + Optional default value. + """ + description: str | None = None + """ + Optional description for the enum field. + """ + items: Items + """ + Schema for array items with enum options and display labels. + """ + max_items: Annotated[int | None, Field(alias="maxItems")] = None + """ + Maximum number of items to select. + """ + min_items: Annotated[int | None, Field(alias="minItems")] = None + """ + Minimum number of items to select. + """ + title: str | None = None + """ + Optional title for the enum field. + """ + type: Literal["array"] + + +class OneOfItem(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + const: str + """ + The enum value. + """ + title: str + """ + Display label for this option. + """ + + +class TitledSingleSelectEnumSchema(WireModel): + """ + Schema for single-selection enumeration with display titles for each option. + """ + + model_config = ConfigDict( + extra="ignore", + ) + default: str | None = None + """ + Optional default value. + """ + description: str | None = None + """ + Optional description for the enum field. + """ + one_of: Annotated[list[OneOfItem], Field(alias="oneOf")] + """ + Array of enum options with values and display labels. + """ + title: str | None = None + """ + Optional title for the enum field. + """ + type: Literal["string"] + + +class InputSchema(WireModel): + """ + A JSON Schema object defining the expected parameters for the tool. + + Tool arguments are always JSON objects, so `type: "object"` is required at the root. + Beyond that, any JSON Schema 2020-12 keyword may appear alongside `type` — including + composition keywords (`oneOf`, `anyOf`, `allOf`, `not`), conditional keywords + (`if`/`then`/`else`), reference keywords (`$ref`, `$defs`, `$anchor`), and any other + standard validation or annotation keywords. + + Property schemas may carry an `x-mcp-header` annotation to mirror the + argument value into an HTTP header on the Streamable HTTP transport. See + the Streamable HTTP transport specification for the validity and + extraction rules. + + Defaults to JSON Schema 2020-12 when no explicit `$schema` is provided. + """ + + model_config = ConfigDict( + extra="allow", + ) + schema_: Annotated[str | None, Field(alias="$schema")] = None + type: Literal["object"] + + +class OutputSchema(WireModel): + """ + An optional JSON Schema object defining the structure of the tool's output returned in + the structuredContent field of a {@link CallToolResult}. This can be any valid JSON Schema 2020-12. + + Defaults to JSON Schema 2020-12 when no explicit `$schema` is provided. + """ + + model_config = ConfigDict( + extra="allow", + ) + schema_: Annotated[str | None, Field(alias="$schema")] = None + + +class ToolAnnotations(WireModel): + """ + Additional properties describing a {@link Tool} to clients. + + NOTE: all properties in `ToolAnnotations` are **hints**. + They are not guaranteed to provide a faithful description of + tool behavior (including descriptive properties like `title`). + + Clients should never make tool use decisions based on `ToolAnnotations` + received from untrusted servers. + """ + + model_config = ConfigDict( + extra="ignore", + ) + destructive_hint: Annotated[bool | None, Field(alias="destructiveHint")] = None + """ + If true, the tool may perform destructive updates to its environment. + If false, the tool performs only additive updates. + + (This property is meaningful only when `readOnlyHint == false`) + + Default: true + """ + idempotent_hint: Annotated[bool | None, Field(alias="idempotentHint")] = None + """ + If true, calling the tool repeatedly with the same arguments + will have no additional effect on its environment. + + (This property is meaningful only when `readOnlyHint == false`) + + Default: false + """ + open_world_hint: Annotated[bool | None, Field(alias="openWorldHint")] = None + """ + If true, this tool may interact with an "open world" of external + entities. If false, the tool's domain of interaction is closed. + For example, the world of a web search tool is open, whereas that + of a memory tool is not. + + Default: true + """ + read_only_hint: Annotated[bool | None, Field(alias="readOnlyHint")] = None + """ + If true, the tool does not modify its environment. + + Default: false + """ + title: str | None = None + """ + A human-readable title for the tool. + """ + + +class ToolChoice(WireModel): + """ + Controls tool selection behavior for sampling requests. + """ + + model_config = ConfigDict( + extra="ignore", + ) + mode: Literal["auto", "none", "required"] | None = None + """ + Controls the tool use ability of the model: + - `"auto"`: Model decides whether to use tools (default) + - `"required"`: Model MUST use at least one tool before completing + - `"none"`: Model MUST NOT use any tools + """ + + +class ToolUseContent(WireModel): + """ + A request from the assistant to call a tool. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + """ + Optional metadata about the tool use. Clients SHOULD preserve this field when + including tool uses in subsequent sampling requests to enable caching optimizations. + """ + id: str + """ + A unique identifier for this tool use. + + This ID is used to match tool results to their corresponding tool uses. + """ + input: dict[str, Any] + """ + The arguments to pass to the tool, conforming to the tool's input schema. + """ + name: str + """ + The name of the tool to call. + """ + type: Literal["tool_use"] + + +class Data1(WireModel): + """ + Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + """ + + model_config = ConfigDict( + extra="ignore", + ) + requested: str + """ + The protocol version that was requested by the client. + """ + supported: list[str] + """ + Protocol versions the server supports. The client should choose a + mutually supported version from this list and retry. + """ + + +class Error3(Error): + model_config = ConfigDict( + extra="ignore", + ) + code: Literal[-32022] + """ + The error type that occurred. + """ + data: Data1 + """ + Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + """ + + +class UnsupportedProtocolVersionError(WireModel): + """ + Returned when the request's protocol version is unknown to the server or + unsupported (e.g., a known experimental or draft version the server has + chosen not to implement). For HTTP, the response status code MUST be + `400 Bad Request`. + """ + + model_config = ConfigDict( + extra="ignore", + ) + error: Error3 + id: RequestId | None = None + jsonrpc: Literal["2.0"] + + +class Items1(WireModel): + """ + Schema for the array items. + """ + + model_config = ConfigDict( + extra="ignore", + ) + enum: list[str] + """ + Array of enum values to choose from. + """ + type: Literal["string"] + + +class UntitledMultiSelectEnumSchema(WireModel): + """ + Schema for multiple-selection enumeration without display titles for options. + """ + + model_config = ConfigDict( + extra="ignore", + ) + default: list[str] | None = None + """ + Optional default value. + """ + description: str | None = None + """ + Optional description for the enum field. + """ + items: Items1 + """ + Schema for the array items. + """ + max_items: Annotated[int | None, Field(alias="maxItems")] = None + """ + Maximum number of items to select. + """ + min_items: Annotated[int | None, Field(alias="minItems")] = None + """ + Minimum number of items to select. + """ + title: str | None = None + """ + Optional title for the enum field. + """ + type: Literal["array"] + + +class UntitledSingleSelectEnumSchema(WireModel): + """ + Schema for single-selection enumeration without display titles for options. + """ + + model_config = ConfigDict( + extra="ignore", + ) + default: str | None = None + """ + Optional default value. + """ + description: str | None = None + """ + Optional description for the enum field. + """ + enum: list[str] + """ + Array of enum values to choose from. + """ + title: str | None = None + """ + Optional title for the enum field. + """ + type: Literal["string"] + + +class Annotations(WireModel): + """ + Optional annotations for the client. The client can use annotations to inform how objects are used or displayed + """ + + model_config = ConfigDict( + extra="ignore", + ) + audience: list[Role] | None = None + """ + Describes who the intended audience of this object or data is. + + It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`). + """ + last_modified: Annotated[str | None, Field(alias="lastModified")] = None + """ + The moment the resource was last modified, as an ISO 8601 formatted string. + + Should be an ISO 8601 formatted string (e.g., "2025-01-12T15:00:58Z"). + + Examples: last activity timestamp in an open file, timestamp when the resource + was attached, etc. + """ + priority: Annotated[float | None, Field(ge=0.0, le=1.0)] = None + """ + Describes how important this data is for operating the server. + + A value of 1 means "most important," and indicates that the data is + effectively required, while 0 means "least important," and indicates that + the data is entirely optional. + """ + + +class AudioContent(WireModel): + """ + Audio provided to or from an LLM. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + data: str + """ + The base64-encoded audio data. + """ + mime_type: Annotated[str, Field(alias="mimeType")] + """ + The MIME type of the audio. Different providers may support different audio types. + """ + type: Literal["audio"] + + +class BlobResourceContents(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + blob: str + """ + A base64-encoded string representing the binary data of the item. + """ + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + The MIME type of this resource, if known. + """ + uri: str + """ + The URI of this resource. + """ + + +class CacheableResult(WireModel): + """ + A result that supports a time-to-live (TTL) hint for client-side caching. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[ResultMetaObject | None, Field(alias="_meta")] = None + cache_scope: Annotated[Literal["private", "public"], Field(alias="cacheScope")] + """ + Indicates the intended scope of the cached response, analogous to HTTP + `Cache-Control: public` vs `Cache-Control: private`. + + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). + """ + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + ttl_ms: Annotated[int, Field(alias="ttlMs", ge=0)] + """ + A hint from the server indicating how long (in milliseconds) the + client MAY cache this response before re-fetching. Semantics are + analogous to HTTP Cache-Control max-age. + + - If 0, The response SHOULD be considered immediately stale, + The client MAY re-fetch every time the result is needed. + - If positive, the client SHOULD consider the result fresh for this many + milliseconds after receiving the response. + """ + + +class CompleteResult(WireModel): + """ + The result returned by the server for a {@link CompleteRequestcompletion/complete} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[ResultMetaObject | None, Field(alias="_meta")] = None + completion: Completion + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + + +class CompleteResultResponse(WireModel): + """ + A successful response from the server for a {@link CompleteRequestcompletion/complete} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + result: CompleteResult + + +class ElicitRequestParams(RootModel[ElicitRequestFormParams | ElicitRequestURLParams]): + root: ElicitRequestFormParams | ElicitRequestURLParams + """ + The parameters for a request to elicit additional information from the user via the client. + """ + + +class EmbeddedResource(WireModel): + """ + The contents of a resource, embedded into a prompt or tool call result. + + It is up to the client how best to render embedded resources for the benefit + of the LLM and/or the user. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + resource: TextResourceContents | BlobResourceContents + type: Literal["resource"] + + +class EnumSchema( + RootModel[ + UntitledSingleSelectEnumSchema + | TitledSingleSelectEnumSchema + | UntitledMultiSelectEnumSchema + | TitledMultiSelectEnumSchema + | LegacyTitledEnumSchema + ] +): + root: ( + UntitledSingleSelectEnumSchema + | TitledSingleSelectEnumSchema + | UntitledMultiSelectEnumSchema + | TitledMultiSelectEnumSchema + | LegacyTitledEnumSchema + ) + + +class HeaderMismatchError(WireModel): + """ + Returned when a server rejects a request because the values in the HTTP + headers do not match the corresponding values in the request body, or + because required headers are missing or malformed. For HTTP, the response + status code MUST be `400 Bad Request`. + """ + + model_config = ConfigDict( + extra="ignore", + ) + error: Error1 + id: RequestId | None = None + jsonrpc: Literal["2.0"] + + +class ImageContent(WireModel): + """ + An image provided to or from an LLM. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + data: str + """ + The base64-encoded image data. + """ + mime_type: Annotated[str, Field(alias="mimeType")] + """ + The MIME type of the image. Different providers may support different image types. + """ + type: Literal["image"] + + +class JSONRPCErrorResponse(WireModel): + """ + A response to a request that indicates an error occurred. + """ + + model_config = ConfigDict( + extra="ignore", + ) + error: Error + id: RequestId | None = None + jsonrpc: Literal["2.0"] + + +class JSONRPCRequest(WireModel): + """ + A request that expects a response. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: str + params: dict[str, Any] | None = None + + +class Params(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + + +class ListRootsRequest(WireModel): + """ + Sent from the server to request a list of root URIs from the client. Roots allow + servers to ask for specific directories or files to operate on. A common example + for roots is providing a set of repositories or directories a server should operate + on. + + This request is typically used when the server needs to understand the file system + structure or access specific locations that the client has permission to read from. + """ + + model_config = ConfigDict( + extra="ignore", + ) + method: Literal["roots/list"] + params: Params | None = None + + +class ListRootsResult(WireModel): + """ + The result returned by the client for a {@link ListRootsRequestroots/list} request. + This result contains an array of {@link Root} objects, each representing a root directory + or file that the server can operate on. + """ + + model_config = ConfigDict( + extra="ignore", + ) + roots: list[Root] + + +class MultiSelectEnumSchema(RootModel[UntitledMultiSelectEnumSchema | TitledMultiSelectEnumSchema]): + root: UntitledMultiSelectEnumSchema | TitledMultiSelectEnumSchema + + +class NotificationMetaObject(WireModel): + """ + Extends {@link MetaObject} with additional notification-specific fields. All key naming rules from `MetaObject` apply. + """ + + model_config = ConfigDict( + extra="allow", + ) + io_modelcontextprotocol_subscription_id: Annotated[ + RequestId | None, Field(alias="io.modelcontextprotocol/subscriptionId") + ] = None + """ + Identifies the subscription stream a notification was delivered on. The + server MUST include this key on every notification delivered via a + {@link SubscriptionsListenRequestsubscriptions/listen} stream, so the + client can correlate the notification with the originating subscription. + The key is absent on notifications not delivered via a subscription + stream (e.g. progress notifications for an in-flight request), which is + why it is optional here. + + The value is the JSON-RPC ID of the `subscriptions/listen` request that + opened the stream. + """ + + +class NotificationParams(WireModel): + """ + Common params for any notification. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[NotificationMetaObject | None, Field(alias="_meta")] = None + + +class PaginatedResult(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[ResultMetaObject | None, Field(alias="_meta")] = None + next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + + +class PrimitiveSchemaDefinition( + RootModel[ + StringSchema + | NumberSchema + | BooleanSchema + | UntitledSingleSelectEnumSchema + | TitledSingleSelectEnumSchema + | UntitledMultiSelectEnumSchema + | TitledMultiSelectEnumSchema + | LegacyTitledEnumSchema + ] +): + root: ( + StringSchema + | NumberSchema + | BooleanSchema + | UntitledSingleSelectEnumSchema + | TitledSingleSelectEnumSchema + | UntitledMultiSelectEnumSchema + | TitledMultiSelectEnumSchema + | LegacyTitledEnumSchema + ) + """ + Restricted schema definitions that only allow primitive types + without nested objects or arrays. + """ + + +class ProgressNotificationParams(WireModel): + """ + Parameters for a {@link ProgressNotificationnotifications/progress} notification. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[NotificationMetaObject | None, Field(alias="_meta")] = None + message: str | None = None + """ + An optional message describing the current progress. + """ + progress: float + """ + The progress thus far. This should increase every time progress is made, even if the total is unknown. + """ + progress_token: Annotated[ProgressToken, Field(alias="progressToken")] + """ + The progress token which was given in the initial request, used to associate this notification with the request that is proceeding. + """ + total: float | None = None + """ + Total number of items to process (or total progress required), if known. + """ + + +class Prompt(WireModel): + """ + A prompt or prompt template that the server offers. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + arguments: list[PromptArgument] | None = None + """ + A list of arguments to use for templating the prompt. + """ + description: str | None = None + """ + An optional description of what this prompt provides + """ + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for {@link Tool}, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + + +class PromptListChangedNotification(WireModel): + """ + An optional notification from the server to the client, informing it that the list of prompts it offers has changed. This is only delivered on a {@link SubscriptionsListenRequestsubscriptions/listen} stream when the client requested it via the `promptsListChanged` filter field. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/prompts/list_changed"] + params: NotificationParams | None = None + + +class ReadResourceResult(WireModel): + """ + The result returned by the server for a {@link ReadResourceRequestresources/read} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[ResultMetaObject | None, Field(alias="_meta")] = None + cache_scope: Annotated[Literal["private", "public"], Field(alias="cacheScope")] + """ + Indicates the intended scope of the cached response, analogous to HTTP + `Cache-Control: public` vs `Cache-Control: private`. + + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). + """ + contents: list[TextResourceContents | BlobResourceContents] + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + ttl_ms: Annotated[int, Field(alias="ttlMs", ge=0)] + """ + A hint from the server indicating how long (in milliseconds) the + client MAY cache this response before re-fetching. Semantics are + analogous to HTTP Cache-Control max-age. + + - If 0, The response SHOULD be considered immediately stale, + The client MAY re-fetch every time the result is needed. + - If positive, the client SHOULD consider the result fresh for this many + milliseconds after receiving the response. + """ + + +class Resource(WireModel): + """ + A known resource that the server is capable of reading. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + description: str | None = None + """ + A description of what this resource represents. + + This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + """ + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + The MIME type of this resource, if known. + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + size: int | None = None + """ + The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known. + + This can be used by Hosts to display file sizes and estimate context window usage. + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for {@link Tool}, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + uri: str + """ + The URI of this resource. + """ + + +class ResourceLink(WireModel): + """ + A resource that the server is capable of reading, included in a prompt or tool call result. + + Note: resource links returned by tools are not guaranteed to appear in the results of {@link ListResourcesRequestresources/list} requests. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + description: str | None = None + """ + A description of what this resource represents. + + This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + """ + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + The MIME type of this resource, if known. + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + size: int | None = None + """ + The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known. + + This can be used by Hosts to display file sizes and estimate context window usage. + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for {@link Tool}, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + type: Literal["resource_link"] + uri: str + """ + The URI of this resource. + """ + + +class ResourceListChangedNotification(WireModel): + """ + An optional notification from the server to the client, informing it that the list of resources it can read from has changed. This is only delivered on a {@link SubscriptionsListenRequestsubscriptions/listen} stream when the client requested it via the `resourcesListChanged` filter field. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/resources/list_changed"] + params: NotificationParams | None = None + + +class ResourceTemplate(WireModel): + """ + A template description for resources available on the server. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + description: str | None = None + """ + A description of what this template is for. + + This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + """ + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + """ + The MIME type for all resources that match this template. This should only be included if all resources matching this template have the same type. + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for {@link Tool}, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + uri_template: Annotated[str, Field(alias="uriTemplate")] + """ + A URI template (according to RFC 6570) that can be used to construct resource URIs. + """ + + +class ResourceUpdatedNotificationParams(WireModel): + """ + Parameters for a `notifications/resources/updated` notification. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[NotificationMetaObject | None, Field(alias="_meta")] = None + uri: str + """ + The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to. + """ + + +class Result(WireModel): + """ + Common result fields. + """ + + model_config = ConfigDict( + extra="allow", + ) + meta: Annotated[ResultMetaObject | None, Field(alias="_meta")] = None + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + + +class SingleSelectEnumSchema(RootModel[UntitledSingleSelectEnumSchema | TitledSingleSelectEnumSchema]): + root: UntitledSingleSelectEnumSchema | TitledSingleSelectEnumSchema + + +class SubscriptionsAcknowledgedNotificationParams(WireModel): + """ + Parameters for a {@link SubscriptionsAcknowledgedNotificationnotifications/subscriptions/acknowledged} notification. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[NotificationMetaObject | None, Field(alias="_meta")] = None + notifications: SubscriptionFilter + """ + The subset of requested notification types the server agreed to honor. + Only includes notification types the server actually supports; if the + client requested an unsupported type (e.g., `promptsListChanged` when + the server has no prompts), it is omitted from this set. + """ + + +class SubscriptionsListenResult(WireModel): + """ + The response to a {@link SubscriptionsListenRequestsubscriptions/listen} + request, signalling that the subscription has ended gracefully (for example, + during server shutdown). Because the listen stream is long-lived, this result + is sent only when the server tears the subscription down; an abrupt transport + close carries no response. The result body is otherwise empty. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[SubscriptionsListenResultMeta, Field(alias="_meta")] + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + + +class TextContent(WireModel): + """ + Text provided to or from an LLM. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + annotations: Annotations | None = None + """ + Optional annotations for the client. + """ + text: str + """ + The text content of the message. + """ + type: Literal["text"] + + +class Tool(WireModel): + """ + Definition for a tool the client can call. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + annotations: ToolAnnotations | None = None + """ + Optional additional tool information. + + Display name precedence order is: `title`, `annotations.title`, then `name`. + """ + description: str | None = None + """ + A human-readable description of the tool. + + This can be used by clients to improve the LLM's understanding of available tools. It can be thought of like a "hint" to the model. + """ + icons: list[Icon] | None = None + """ + Optional set of sized icons that the client can display in a user interface. + + Clients that support rendering icons MUST support at least the following MIME types: + - `image/png` - PNG images (safe, universal compatibility) + - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + + Clients that support rendering icons SHOULD also support: + - `image/svg+xml` - SVG images (scalable but requires security precautions) + - `image/webp` - WebP images (modern, efficient format) + """ + input_schema: Annotated[InputSchema, Field(alias="inputSchema")] + """ + A JSON Schema object defining the expected parameters for the tool. + + Tool arguments are always JSON objects, so `type: "object"` is required at the root. + Beyond that, any JSON Schema 2020-12 keyword may appear alongside `type` — including + composition keywords (`oneOf`, `anyOf`, `allOf`, `not`), conditional keywords + (`if`/`then`/`else`), reference keywords (`$ref`, `$defs`, `$anchor`), and any other + standard validation or annotation keywords. + + Property schemas may carry an `x-mcp-header` annotation to mirror the + argument value into an HTTP header on the Streamable HTTP transport. See + the Streamable HTTP transport specification for the validity and + extraction rules. + + Defaults to JSON Schema 2020-12 when no explicit `$schema` is provided. + """ + name: str + """ + Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present). + """ + output_schema: Annotated[OutputSchema | None, Field(alias="outputSchema")] = None + """ + An optional JSON Schema object defining the structure of the tool's output returned in + the structuredContent field of a {@link CallToolResult}. This can be any valid JSON Schema 2020-12. + + Defaults to JSON Schema 2020-12 when no explicit `$schema` is provided. + """ + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for {@link Tool}, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + + +class ToolListChangedNotification(WireModel): + """ + An optional notification from the server to the client, informing it that the list of tools it offers has changed. This is only delivered on a {@link SubscriptionsListenRequestsubscriptions/listen} stream when the client requested it via the `toolsListChanged` filter field. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/tools/list_changed"] + params: NotificationParams | None = None + + +class CancelledNotificationParams(WireModel): + """ + Parameters for a `notifications/cancelled` notification. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[NotificationMetaObject | None, Field(alias="_meta")] = None + reason: str | None = None + """ + An optional string describing the reason for the cancellation. This MAY be logged or presented to the user. + """ + request_id: Annotated[RequestId, Field(alias="requestId")] + """ + The ID of the request to cancel. + + This MUST correspond to the ID of a request the client previously issued. + """ + + +class ClientNotification(WireModel): + """ + This notification is sent by the client to indicate that it is cancelling a request it previously issued. + + On stdio, the server also sends this notification, solely to terminate a {@link SubscriptionsListenRequestsubscriptions/listen} stream: it references the ID of the `subscriptions/listen` request that opened the stream. Servers MUST NOT use this notification to cancel any other request. + + The request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished. + + This notification indicates that the result will be unused, so any associated processing SHOULD cease. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/cancelled"] + params: CancelledNotificationParams + + +class ClientResult(RootModel[Result]): + root: Result + """ + Common result fields. + """ + + +class ContentBlock(RootModel[TextContent | ImageContent | AudioContent | ResourceLink | EmbeddedResource]): + root: TextContent | ImageContent | AudioContent | ResourceLink | EmbeddedResource + + +class ElicitRequest(WireModel): + """ + A request from the server to elicit additional information from the user via the client. + """ + + model_config = ConfigDict( + extra="ignore", + ) + method: Literal["elicitation/create"] + params: ElicitRequestParams + + +class EmptyResult(RootModel[Result]): + root: Result + """ + Common result fields. + """ + + +class JSONRPCResultResponse(WireModel): + """ + A successful (non-error) response to a request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + result: Result + + +class ListPromptsResult(WireModel): + """ + The result returned by the server for a {@link ListPromptsRequestprompts/list} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[ResultMetaObject | None, Field(alias="_meta")] = None + cache_scope: Annotated[Literal["private", "public"], Field(alias="cacheScope")] + """ + Indicates the intended scope of the cached response, analogous to HTTP + `Cache-Control: public` vs `Cache-Control: private`. + + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). + """ + next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + prompts: list[Prompt] + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + ttl_ms: Annotated[int, Field(alias="ttlMs", ge=0)] + """ + A hint from the server indicating how long (in milliseconds) the + client MAY cache this response before re-fetching. Semantics are + analogous to HTTP Cache-Control max-age. + + - If 0, The response SHOULD be considered immediately stale, + The client MAY re-fetch every time the result is needed. + - If positive, the client SHOULD consider the result fresh for this many + milliseconds after receiving the response. + """ + + +class ListPromptsResultResponse(WireModel): + """ + A successful response from the server for a {@link ListPromptsRequestprompts/list} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + result: ListPromptsResult + + +class ListResourceTemplatesResult(WireModel): + """ + The result returned by the server for a {@link ListResourceTemplatesRequestresources/templates/list} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[ResultMetaObject | None, Field(alias="_meta")] = None + cache_scope: Annotated[Literal["private", "public"], Field(alias="cacheScope")] + """ + Indicates the intended scope of the cached response, analogous to HTTP + `Cache-Control: public` vs `Cache-Control: private`. + + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). + """ + next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + resource_templates: Annotated[list[ResourceTemplate], Field(alias="resourceTemplates")] + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + ttl_ms: Annotated[int, Field(alias="ttlMs", ge=0)] + """ + A hint from the server indicating how long (in milliseconds) the + client MAY cache this response before re-fetching. Semantics are + analogous to HTTP Cache-Control max-age. + + - If 0, The response SHOULD be considered immediately stale, + The client MAY re-fetch every time the result is needed. + - If positive, the client SHOULD consider the result fresh for this many + milliseconds after receiving the response. + """ + + +class ListResourceTemplatesResultResponse(WireModel): + """ + A successful response from the server for a {@link ListResourceTemplatesRequestresources/templates/list} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + result: ListResourceTemplatesResult + + +class ListResourcesResult(WireModel): + """ + The result returned by the server for a {@link ListResourcesRequestresources/list} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[ResultMetaObject | None, Field(alias="_meta")] = None + cache_scope: Annotated[Literal["private", "public"], Field(alias="cacheScope")] + """ + Indicates the intended scope of the cached response, analogous to HTTP + `Cache-Control: public` vs `Cache-Control: private`. + + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). + """ + next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + resources: list[Resource] + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + ttl_ms: Annotated[int, Field(alias="ttlMs", ge=0)] + """ + A hint from the server indicating how long (in milliseconds) the + client MAY cache this response before re-fetching. Semantics are + analogous to HTTP Cache-Control max-age. + + - If 0, The response SHOULD be considered immediately stale, + The client MAY re-fetch every time the result is needed. + - If positive, the client SHOULD consider the result fresh for this many + milliseconds after receiving the response. + """ + + +class ListResourcesResultResponse(WireModel): + """ + A successful response from the server for a {@link ListResourcesRequestresources/list} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + result: ListResourcesResult + + +class ListToolsResult(WireModel): + """ + The result returned by the server for a {@link ListToolsRequesttools/list} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[ResultMetaObject | None, Field(alias="_meta")] = None + cache_scope: Annotated[Literal["private", "public"], Field(alias="cacheScope")] + """ + Indicates the intended scope of the cached response, analogous to HTTP + `Cache-Control: public` vs `Cache-Control: private`. + + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). + """ + next_cursor: Annotated[str | None, Field(alias="nextCursor")] = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + tools: list[Tool] + ttl_ms: Annotated[int, Field(alias="ttlMs", ge=0)] + """ + A hint from the server indicating how long (in milliseconds) the + client MAY cache this response before re-fetching. Semantics are + analogous to HTTP Cache-Control max-age. + + - If 0, The response SHOULD be considered immediately stale, + The client MAY re-fetch every time the result is needed. + - If positive, the client SHOULD consider the result fresh for this many + milliseconds after receiving the response. + """ + + +class ListToolsResultResponse(WireModel): + """ + A successful response from the server for a {@link ListToolsRequesttools/list} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + result: ListToolsResult + + +class LoggingMessageNotificationParams(WireModel): + """ + Parameters for a `notifications/message` notification. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[NotificationMetaObject | None, Field(alias="_meta")] = None + data: Any + """ + The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here. + """ + level: LoggingLevel + """ + The severity of this log message. + """ + logger: str | None = None + """ + An optional name of the logger issuing this message. + """ + + +class ProgressNotification(WireModel): + """ + An out-of-band notification used to inform the receiver of a progress update for a long-running request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/progress"] + params: ProgressNotificationParams + + +class PromptMessage(WireModel): + """ + Describes a message returned as part of a prompt. + + This is similar to {@link SamplingMessage}, but also supports the embedding of + resources from the MCP server. + """ + + model_config = ConfigDict( + extra="ignore", + ) + content: ContentBlock + role: Role + + +class ResourceUpdatedNotification(WireModel): + """ + A notification from the server to the client, informing it that a resource has changed and may need to be read again. This is only sent for resources the client opted in to via the `resourceSubscriptions` field of a {@link SubscriptionsListenRequestsubscriptions/listen} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/resources/updated"] + params: ResourceUpdatedNotificationParams + + +class SubscriptionsAcknowledgedNotification(WireModel): + """ + Sent by the server to acknowledge that a + {@link SubscriptionsListenRequestsubscriptions/listen} subscription has been + established and to report which notification types it agreed to honor. + + This notification MUST be the first message the server sends carrying the + subscription's ID in `io.modelcontextprotocol/subscriptionId`. The server MUST + NOT send any notification on the subscription before acknowledging it. On + stdio, where every subscription shares one channel, this ordering is defined + per subscription ID and not per channel: messages belonging to other + subscriptions MAY be interleaved before it. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/subscriptions/acknowledged"] + params: SubscriptionsAcknowledgedNotificationParams + + +class ToolResultContent(WireModel): + """ + The result of a tool use, provided by the user back to the assistant. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + """ + Optional metadata about the tool result. Clients SHOULD preserve this field when + including tool results in subsequent sampling requests to enable caching optimizations. + """ + content: list[ContentBlock] + """ + The unstructured result content of the tool use. + + This has the same format as {@link CallToolResult.content} and can include text, images, + audio, resource links, and embedded resources. + """ + is_error: Annotated[bool | None, Field(alias="isError")] = None + """ + Whether the tool use resulted in an error. + + If true, the content typically describes the error that occurred. + Default: false + """ + structured_content: Annotated[Any | None, Field(alias="structuredContent")] = None + """ + An optional structured result value. + + This can be any JSON value (object, array, string, number, boolean, or null). + If the tool defined an {@link Tool.outputSchema}, this SHOULD conform to that schema. + """ + tool_use_id: Annotated[str, Field(alias="toolUseId")] + """ + The ID of the tool use this result corresponds to. + + This MUST match the ID from a previous {@link ToolUseContent}. + """ + type: Literal["tool_result"] + + +class CallToolResult(WireModel): + """ + The result returned by the server for a {@link CallToolRequesttools/call} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[ResultMetaObject | None, Field(alias="_meta")] = None + content: list[ContentBlock] + """ + A list of content objects that represent the unstructured result of the tool call. + """ + is_error: Annotated[bool | None, Field(alias="isError")] = None + """ + Whether the tool call ended in an error. + + If not set, this is assumed to be false (the call was successful). + + Any errors that originate from the tool SHOULD be reported inside the result + object, with `isError` set to true, _not_ as an MCP protocol-level error + response. Otherwise, the LLM would not be able to see that an error occurred + and self-correct. + + However, any errors in _finding_ the tool, an error indicating that the + server does not support tool calls, or any other exceptional conditions, + should be reported as an MCP error response. + """ + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + structured_content: Annotated[Any | None, Field(alias="structuredContent")] = None + """ + An optional JSON value that represents the structured result of the tool call. + + This can be any JSON value (object, array, string, number, boolean, or null) + that conforms to the tool's outputSchema if one is defined. + """ + + +class CancelledNotification(WireModel): + """ + This notification is sent by the client to indicate that it is cancelling a request it previously issued. + + On stdio, the server also sends this notification, solely to terminate a {@link SubscriptionsListenRequestsubscriptions/listen} stream: it references the ID of the `subscriptions/listen` request that opened the stream. Servers MUST NOT use this notification to cancel any other request. + + The request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished. + + This notification indicates that the result will be unused, so any associated processing SHOULD cease. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/cancelled"] + params: CancelledNotificationParams + + +class GetPromptResult(WireModel): + """ + The result returned by the server for a {@link GetPromptRequestprompts/get} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[ResultMetaObject | None, Field(alias="_meta")] = None + description: str | None = None + """ + An optional description for the prompt. + """ + messages: list[PromptMessage] + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + + +class JSONRPCMessage(RootModel[JSONRPCRequest | JSONRPCNotification | JSONRPCResultResponse | JSONRPCErrorResponse]): + root: JSONRPCRequest | JSONRPCNotification | JSONRPCResultResponse | JSONRPCErrorResponse + """ + Refers to any valid JSON-RPC object that can be decoded off the wire, or encoded to be sent. + """ + + +class JSONRPCResponse(RootModel[JSONRPCResultResponse | JSONRPCErrorResponse]): + root: JSONRPCResultResponse | JSONRPCErrorResponse + """ + A response to a request, containing either the result or error. + """ + + +class LoggingMessageNotification(WireModel): + """ + JSONRPCNotification of a log message passed from server to client. The client opts in by setting `"io.modelcontextprotocol/logLevel"` in a request's `_meta`. + """ + + model_config = ConfigDict( + extra="ignore", + ) + jsonrpc: Literal["2.0"] + method: Literal["notifications/message"] + params: LoggingMessageNotificationParams + + +class SamplingMessageContentBlock( + RootModel[TextContent | ImageContent | AudioContent | ToolUseContent | ToolResultContent] +): + root: TextContent | ImageContent | AudioContent | ToolUseContent | ToolResultContent + + +class ServerNotification( + RootModel[ + CancelledNotification + | ProgressNotification + | ResourceListChangedNotification + | SubscriptionsAcknowledgedNotification + | ResourceUpdatedNotification + | PromptListChangedNotification + | ToolListChangedNotification + | LoggingMessageNotification + ] +): + root: ( + CancelledNotification + | ProgressNotification + | ResourceListChangedNotification + | SubscriptionsAcknowledgedNotification + | ResourceUpdatedNotification + | PromptListChangedNotification + | ToolListChangedNotification + | LoggingMessageNotification + ) + + +class CreateMessageResult(WireModel): + """ + The result returned by the client for a {@link CreateMessageRequestsampling/createMessage} request. + The client should inform the user before returning the sampled message, to allow them + to inspect the response (human in the loop) and decide whether to allow the server to see it. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + content: ( + TextContent + | ImageContent + | AudioContent + | ToolUseContent + | ToolResultContent + | list[SamplingMessageContentBlock] + ) + model: str + """ + The name of the model that generated the message. + """ + role: Role + stop_reason: Annotated[str | None, Field(alias="stopReason")] = None + """ + The reason why sampling stopped, if known. + + Standard values: + - `"endTurn"`: Natural end of the assistant's turn + - `"stopSequence"`: A stop sequence was encountered + - `"maxTokens"`: Maximum token limit was reached + - `"toolUse"`: The model wants to use one or more tools + + This field is an open string to allow for provider-specific stop reasons. + """ + + +class InputResponse(RootModel[CreateMessageResult | ListRootsResult | ElicitResult]): + root: CreateMessageResult | ListRootsResult | ElicitResult + + +class InputResponses(RootModel[dict[str, InputResponse]]): + """ + A map of client responses to server-initiated requests. + Keys correspond to the keys in the {@link InputRequests} map; + values are the client's result for each request. + """ + + root: dict[str, InputResponse] + + +class SamplingMessage(WireModel): + """ + Describes a message issued to or received from an LLM API. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[MetaObject | None, Field(alias="_meta")] = None + content: ( + TextContent + | ImageContent + | AudioContent + | ToolUseContent + | ToolResultContent + | list[SamplingMessageContentBlock] + ) + role: Role + + +class CallToolRequest(WireModel): + """ + Used by the client to invoke a tool provided by the server. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["tools/call"] + params: CallToolRequestParams + + +class CallToolRequestParams(WireModel): + """ + Parameters for a `tools/call` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[RequestMetaObject, Field(alias="_meta")] + arguments: dict[str, Any] | None = None + """ + Arguments to use for the tool call. + """ + input_responses: Annotated[InputResponses | None, Field(alias="inputResponses")] = None + name: str + """ + The name of the tool. + """ + request_state: Annotated[str | None, Field(alias="requestState")] = None + + +class CallToolResultResponse(WireModel): + """ + A successful response from the server for a {@link CallToolRequesttools/call} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + result: InputRequiredResult | CallToolResult + + +class Elicitation(WireModel): + """ + Present if the client supports elicitation from the server. + """ + + model_config = ConfigDict( + extra="ignore", + ) + form: JSONObject | None = None + url: JSONObject | None = None + + +class Sampling(WireModel): + """ + Present if the client supports sampling from an LLM. + """ + + model_config = ConfigDict( + extra="ignore", + ) + context: JSONObject | None = None + """ + Whether the client supports context inclusion via `includeContext` parameter. + If not declared, servers SHOULD only use `includeContext: "none"` (or omit it). + """ + tools: JSONObject | None = None + """ + Whether the client supports tool use via `tools` and `toolChoice` parameters. + """ + + +class ClientCapabilities(WireModel): + """ + Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities. + """ + + model_config = ConfigDict( + extra="ignore", + ) + elicitation: Elicitation | None = None + """ + Present if the client supports elicitation from the server. + """ + experimental: dict[str, JSONObject] | None = None + """ + Experimental, non-standard capabilities that the client supports. + """ + extensions: dict[str, JSONObject] | None = None + """ + Optional MCP extensions that the client supports. Keys are extension identifiers + (e.g., "io.modelcontextprotocol/oauth-client-credentials"), and values are + per-extension settings objects. An empty object indicates support with no settings. + + Keys MUST follow the {@link MetaObject`_meta` key naming rules}, with a + mandatory prefix. + """ + roots: dict[str, Any] | None = None + """ + Present if the client supports listing roots. + """ + sampling: Sampling | None = None + """ + Present if the client supports sampling from an LLM. + """ + + +class CompleteRequest(WireModel): + """ + A request from the client to the server, to ask for completion options. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["completion/complete"] + params: CompleteRequestParams + + +class CompleteRequestParams(WireModel): + """ + Parameters for a `completion/complete` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[RequestMetaObject, Field(alias="_meta")] + argument: Argument + """ + The argument's information + """ + context: Context | None = None + """ + Additional, optional context for completions + """ + ref: PromptReference | ResourceTemplateReference + + +class CreateMessageRequest(WireModel): + """ + A request from the server to sample an LLM via the client. The client has full discretion over which model to select. The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it. + """ + + model_config = ConfigDict( + extra="ignore", + ) + method: Literal["sampling/createMessage"] + params: CreateMessageRequestParams + + +class CreateMessageRequestParams(WireModel): + """ + Parameters for a `sampling/createMessage` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + include_context: Annotated[ + Literal["allServers", "none", "thisServer"] | None, + Field(alias="includeContext"), + ] = None + """ + A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. + The client MAY ignore this request. + + Default is `"none"`. The values `"thisServer"` and `"allServers"` are deprecated (SEP-2596): servers SHOULD + omit this field or use `"none"`, and SHOULD only use the deprecated values if the client declares + {@link ClientCapabilities.sampling.context}. + """ + max_tokens: Annotated[int, Field(alias="maxTokens")] + """ + The requested maximum number of tokens to sample (to prevent runaway completions). + + The client MAY choose to sample fewer tokens than the requested maximum. + """ + messages: list[SamplingMessage] + metadata: JSONObject | None = None + """ + Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific. + """ + model_preferences: Annotated[ModelPreferences | None, Field(alias="modelPreferences")] = None + """ + The server's preferences for which model to select. The client MAY ignore these preferences. + """ + stop_sequences: Annotated[list[str] | None, Field(alias="stopSequences")] = None + system_prompt: Annotated[str | None, Field(alias="systemPrompt")] = None + """ + An optional system prompt the server wants to use for sampling. The client MAY modify or omit this prompt. + """ + temperature: float | None = None + tool_choice: Annotated[ToolChoice | None, Field(alias="toolChoice")] = None + """ + Controls how the model uses tools. + The client MUST return an error if this field is provided but {@link ClientCapabilities.sampling.tools} is not declared. + Default is `{ mode: "auto" }`. + """ + tools: list[Tool] | None = None + """ + Tools that the model may use during generation. + The client MUST return an error if this field is provided but {@link ClientCapabilities.sampling.tools} is not declared. + """ + + +class DiscoverRequest(WireModel): + """ + A request from the client asking the server to advertise its supported + protocol versions, capabilities, and other metadata. Servers **MUST** + implement `server/discover`. Clients **MAY** call it but are not required + to — version negotiation can also happen inline via per-request `_meta`. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["server/discover"] + params: RequestParams + + +class DiscoverResult(WireModel): + """ + The result returned by the server for a {@link DiscoverRequestserver/discover} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[ResultMetaObject | None, Field(alias="_meta")] = None + cache_scope: Annotated[Literal["private", "public"], Field(alias="cacheScope")] + """ + Indicates the intended scope of the cached response, analogous to HTTP + `Cache-Control: public` vs `Cache-Control: private`. + + - `"public"`: The response does not contain user-specific data. Any + client or intermediary (e.g., shared gateway, caching proxy) MAY cache + the response and serve it across authorization contexts. + - `"private"`: The response MAY be cached and reused only within the + same authorization context. Caches MUST NOT be shared across + authorization contexts (e.g., a different access token requires a + different cache). + """ + capabilities: ServerCapabilities + """ + The capabilities of the server. + """ + instructions: str | None = None + """ + Natural-language guidance describing the server and its features. + + This can be used by clients to improve an LLM's understanding of + available tools (e.g., by including it in a system prompt). It should + focus on information that helps the model use the server effectively + and should not duplicate information already in tool descriptions. + """ + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + supported_versions: Annotated[list[str], Field(alias="supportedVersions")] + """ + MCP Protocol Versions this server supports. The client should choose a + version from this list for use in subsequent requests. + """ + ttl_ms: Annotated[int, Field(alias="ttlMs", ge=0)] + """ + A hint from the server indicating how long (in milliseconds) the + client MAY cache this response before re-fetching. Semantics are + analogous to HTTP Cache-Control max-age. + + - If 0, The response SHOULD be considered immediately stale, + The client MAY re-fetch every time the result is needed. + - If positive, the client SHOULD consider the result fresh for this many + milliseconds after receiving the response. + """ + + +class DiscoverResultResponse(WireModel): + """ + A successful response from the server for a {@link DiscoverRequestserver/discover} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + result: DiscoverResult + + +class GetPromptRequest(WireModel): + """ + Used by the client to get a prompt provided by the server. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["prompts/get"] + params: GetPromptRequestParams + + +class GetPromptRequestParams(WireModel): + """ + Parameters for a `prompts/get` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[RequestMetaObject, Field(alias="_meta")] + arguments: dict[str, str] | None = None + """ + Arguments to use for templating the prompt. + """ + input_responses: Annotated[InputResponses | None, Field(alias="inputResponses")] = None + name: str + """ + The name of the prompt or prompt template. + """ + request_state: Annotated[str | None, Field(alias="requestState")] = None + + +class GetPromptResultResponse(WireModel): + """ + A successful response from the server for a {@link GetPromptRequestprompts/get} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + result: InputRequiredResult | GetPromptResult + + +class InputRequiredResult(WireModel): + """ + An InputRequiredResult sent by the server to indicate that additional input is needed + before the request can be completed. + + At least one of `inputRequests` or `requestState` MUST be present. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[ResultMetaObject | None, Field(alias="_meta")] = None + input_requests: Annotated[InputRequests | None, Field(alias="inputRequests")] = None + request_state: Annotated[str | None, Field(alias="requestState")] = None + result_type: Annotated[str, Field(alias="resultType")] + """ + Indicates the type of the result, which allows the client to determine + how to parse the result object. + + Servers implementing this protocol version MUST include this field. + For backward compatibility, when a client receives a result from a + server implementing an earlier protocol version (which does not include + `resultType`), the client MUST treat the absent field as `"complete"`. + """ + + +class InputResponseRequestParams(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[RequestMetaObject, Field(alias="_meta")] + input_responses: Annotated[InputResponses | None, Field(alias="inputResponses")] = None + request_state: Annotated[str | None, Field(alias="requestState")] = None + + +class ListPromptsRequest(WireModel): + """ + Sent from the client to request a list of prompts and prompt templates the server has. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["prompts/list"] + params: PaginatedRequestParams + + +class ListResourceTemplatesRequest(WireModel): + """ + Sent from the client to request a list of resource templates the server has. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["resources/templates/list"] + params: PaginatedRequestParams + + +class ListResourcesRequest(WireModel): + """ + Sent from the client to request a list of resources the server has. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["resources/list"] + params: PaginatedRequestParams + + +class ListToolsRequest(WireModel): + """ + Sent from the client to request a list of tools the server has. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["tools/list"] + params: PaginatedRequestParams + + +class Data(WireModel): + """ + Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + """ + + model_config = ConfigDict( + extra="ignore", + ) + required_capabilities: Annotated[ClientCapabilities, Field(alias="requiredCapabilities")] + """ + The capabilities the server requires from the client to process this request. + """ + + +class Error2(Error): + model_config = ConfigDict( + extra="ignore", + ) + code: Literal[-32021] + """ + The error type that occurred. + """ + data: Data + """ + Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + """ + + +class MissingRequiredClientCapabilityError(WireModel): + """ + Returned when processing a request requires a capability the client did not + declare in `clientCapabilities`. For HTTP, the response status code MUST be + `400 Bad Request`. + """ + + model_config = ConfigDict( + extra="ignore", + ) + error: Error2 + id: RequestId | None = None + jsonrpc: Literal["2.0"] + + +class PaginatedRequest(WireModel): + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: str + params: PaginatedRequestParams + + +class PaginatedRequestParams(WireModel): + """ + Common params for paginated requests. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[RequestMetaObject, Field(alias="_meta")] + cursor: str | None = None + """ + An opaque token representing the current pagination position. + If provided, the server should return results starting after this cursor. + """ + + +class ReadResourceRequest(WireModel): + """ + Sent from the client to the server, to read a specific resource URI. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["resources/read"] + params: ReadResourceRequestParams + + +class ReadResourceRequestParams(WireModel): + """ + Parameters for a `resources/read` request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[RequestMetaObject, Field(alias="_meta")] + input_responses: Annotated[InputResponses | None, Field(alias="inputResponses")] = None + request_state: Annotated[str | None, Field(alias="requestState")] = None + uri: str + """ + The URI of the resource. The URI can use any protocol; it is up to the server how to interpret it. + """ + + +class ReadResourceResultResponse(WireModel): + """ + A successful response from the server for a {@link ReadResourceRequestresources/read} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + result: InputRequiredResult | ReadResourceResult + + +class RequestMetaObject(WireModel): + """ + Extends {@link MetaObject} with additional request-specific fields. All key naming rules from `MetaObject` apply. + """ + + model_config = ConfigDict( + extra="allow", + ) + io_modelcontextprotocol_client_capabilities: Annotated[ + ClientCapabilities, Field(alias="io.modelcontextprotocol/clientCapabilities") + ] + """ + The client's capabilities for this specific request. Required. + + Capabilities are declared per-request rather than once at initialization; + an empty object means the client supports no optional capabilities. + Servers MUST NOT infer capabilities from prior requests. + """ + io_modelcontextprotocol_client_info: Annotated[ + Implementation | None, Field(alias="io.modelcontextprotocol/clientInfo") + ] = None + """ + Identifies the client software making the request. Clients SHOULD + include this field on every request unless specifically configured not + to do so. + + The {@link Implementation} schema requires `name` and `version`; other + fields are optional. + + The value is self-reported by the client and is not verified by the + protocol. It is intended for display, logging, and debugging. Servers + SHOULD NOT use it to change their behavior, and SHOULD NOT rely on it for + security decisions. + """ + io_modelcontextprotocol_log_level: Annotated[ + LoggingLevel | None, Field(alias="io.modelcontextprotocol/logLevel") + ] = None + """ + The desired log level for this request. Optional. + + If absent, the server MUST NOT send any {@link LoggingMessageNotificationnotifications/message} + notifications for this request. The client opts in to log messages by + explicitly setting a level. Replaces the former `logging/setLevel` RPC. + """ + io_modelcontextprotocol_protocol_version: Annotated[str, Field(alias="io.modelcontextprotocol/protocolVersion")] + """ + The MCP Protocol Version being used for this request. Required. + + For the HTTP transport, this value MUST match the `MCP-Protocol-Version` + header; otherwise the server MUST return a `400 Bad Request`. If the + server does not support the requested version, it MUST return an + {@link UnsupportedProtocolVersionError}. + """ + progress_token: Annotated[ProgressToken | None, Field(alias="progressToken")] = None + """ + If specified, the caller is requesting out-of-band progress notifications for this request (as represented by {@link ProgressNotificationnotifications/progress}). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications. + """ + + +class RequestParams(WireModel): + """ + Common params for any request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[RequestMetaObject, Field(alias="_meta")] + + +class ResourceRequestParams(WireModel): + """ + Common params for resource-related requests. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[RequestMetaObject, Field(alias="_meta")] + uri: str + """ + The URI of the resource. The URI can use any protocol; it is up to the server how to interpret it. + """ + + +class ServerCapabilities(WireModel): + """ + Capabilities that a server may support. Known capabilities are defined here, in this schema, but this is not a closed set: any server can define its own, additional capabilities. + """ + + model_config = ConfigDict( + extra="ignore", + ) + completions: JSONObject | None = None + """ + Present if the server supports argument autocompletion suggestions. + """ + experimental: dict[str, JSONObject] | None = None + """ + Experimental, non-standard capabilities that the server supports. + """ + extensions: dict[str, JSONObject] | None = None + """ + Optional MCP extensions that the server supports. Keys are extension identifiers + (e.g., "io.modelcontextprotocol/tasks"), and values are per-extension settings + objects. An empty object indicates support with no settings. + + Keys MUST follow the {@link MetaObject`_meta` key naming rules}, with a + mandatory prefix. + """ + logging: JSONObject | None = None + """ + Present if the server supports sending log messages to the client. + """ + prompts: Prompts | None = None + """ + Present if the server offers any prompt templates. + """ + resources: Resources | None = None + """ + Present if the server offers any resources to read. + """ + tools: Tools | None = None + """ + Present if the server offers any tools to call. + """ + + +class SubscriptionsListenRequest(WireModel): + """ + Sent from the client to open a long-lived channel for receiving notifications + outside the context of a specific request. Replaces the previous HTTP GET + endpoint and ensures consistent behavior between HTTP and STDIO. + """ + + model_config = ConfigDict( + extra="ignore", + ) + id: RequestId + jsonrpc: Literal["2.0"] + method: Literal["subscriptions/listen"] + params: SubscriptionsListenRequestParams + + +class SubscriptionsListenRequestParams(WireModel): + """ + Parameters for a {@link SubscriptionsListenRequestsubscriptions/listen} request. + """ + + model_config = ConfigDict( + extra="ignore", + ) + meta: Annotated[RequestMetaObject, Field(alias="_meta")] + notifications: SubscriptionFilter + """ + The notifications the client opts in to on this stream. The server + **MUST NOT** send notification types the client has not explicitly + requested. + """ + + +class InputRequest(RootModel[CreateMessageRequest | ListRootsRequest | ElicitRequest]): + root: CreateMessageRequest | ListRootsRequest | ElicitRequest + + +class ServerResult( + RootModel[ + Result + | InputRequiredResult + | DiscoverResult + | ListResourcesResult + | ListResourceTemplatesResult + | ReadResourceResult + | SubscriptionsListenResult + | ListPromptsResult + | GetPromptResult + | ListToolsResult + | CallToolResult + | CompleteResult + ] +): + root: ( + Result + | InputRequiredResult + | DiscoverResult + | ListResourcesResult + | ListResourceTemplatesResult + | ReadResourceResult + | SubscriptionsListenResult + | ListPromptsResult + | GetPromptResult + | ListToolsResult + | CallToolResult + | CompleteResult + ) + + +class ClientRequest( + RootModel[ + DiscoverRequest + | ListResourcesRequest + | ListResourceTemplatesRequest + | ReadResourceRequest + | SubscriptionsListenRequest + | ListPromptsRequest + | GetPromptRequest + | ListToolsRequest + | CallToolRequest + | CompleteRequest + ] +): + root: ( + DiscoverRequest + | ListResourcesRequest + | ListResourceTemplatesRequest + | ReadResourceRequest + | SubscriptionsListenRequest + | ListPromptsRequest + | GetPromptRequest + | ListToolsRequest + | CallToolRequest + | CompleteRequest + ) + + +class InputRequests(RootModel[dict[str, InputRequest]]): + """ + A map of server-initiated requests that the client must fulfill. + Keys are server-assigned identifiers; values are the request objects. + """ + + root: dict[str, InputRequest] + + +class JSONArray(RootModel[list["JSONValue"]]): + root: list["JSONValue"] + + +class JSONObject(RootModel[dict[str, "JSONValue"]]): + root: dict[str, "JSONValue"] + + +class JSONValue(RootModel[Union[JSONObject, list["JSONValue"], str | int | float | bool | None]]): + root: Union[JSONObject, list["JSONValue"], str | int | float | bool | None] + + +AnyCallToolResult = CallToolResult | InputRequiredResult +AnyGetPromptResult = GetPromptResult | InputRequiredResult +AnyReadResourceResult = ReadResourceResult | InputRequiredResult + + +CallToolRequest.model_rebuild() +CallToolRequestParams.model_rebuild() +CallToolResultResponse.model_rebuild() +Elicitation.model_rebuild() +Sampling.model_rebuild() +ClientCapabilities.model_rebuild() +CompleteRequest.model_rebuild() +CompleteRequestParams.model_rebuild() +CreateMessageRequest.model_rebuild() +CreateMessageRequestParams.model_rebuild() +DiscoverRequest.model_rebuild() +DiscoverResult.model_rebuild() +GetPromptRequest.model_rebuild() +GetPromptRequestParams.model_rebuild() +GetPromptResultResponse.model_rebuild() +InputRequiredResult.model_rebuild() +InputResponseRequestParams.model_rebuild() +ListPromptsRequest.model_rebuild() +ListResourceTemplatesRequest.model_rebuild() +ListResourcesRequest.model_rebuild() +ListToolsRequest.model_rebuild() +PaginatedRequest.model_rebuild() +PaginatedRequestParams.model_rebuild() +ReadResourceRequest.model_rebuild() +ReadResourceRequestParams.model_rebuild() +ServerCapabilities.model_rebuild() +SubscriptionsListenRequest.model_rebuild() +JSONArray.model_rebuild() +JSONObject.model_rebuild() diff --git a/venv/lib/python3.12/site-packages/mcp_types/_v2026_07_28/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/mcp_types/_v2026_07_28/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..fc1f622 Binary files /dev/null and b/venv/lib/python3.12/site-packages/mcp_types/_v2026_07_28/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/mcp_types/_wire_base.py b/venv/lib/python3.12/site-packages/mcp_types/_wire_base.py new file mode 100644 index 0000000..69254a8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp_types/_wire_base.py @@ -0,0 +1,9 @@ +"""Shared pydantic base for the generated `mcp_types._v*` wire-shape packages.""" + +from pydantic import BaseModel, ConfigDict + + +class WireModel(BaseModel): + """Base for generated wire models: enables `populate_by_name`; subclasses set `extra` themselves.""" + + model_config = ConfigDict(populate_by_name=True) diff --git a/venv/lib/python3.12/site-packages/mcp_types/jsonrpc.py b/venv/lib/python3.12/site-packages/mcp_types/jsonrpc.py new file mode 100644 index 0000000..e9c6db9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp_types/jsonrpc.py @@ -0,0 +1,148 @@ +"""This module follows the JSON-RPC 2.0 specification: https://www.jsonrpc.org/specification.""" + +from __future__ import annotations + +from typing import Annotated, Any, Final, Literal + +from pydantic import BaseModel, Field, TypeAdapter + +__all__ = [ + "CONNECTION_CLOSED", + "HEADER_MISMATCH", + "INTERNAL_ERROR", + "INVALID_PARAMS", + "INVALID_REQUEST", + "JSONRPC_VERSION", + "METHOD_NOT_FOUND", + "MISSING_REQUIRED_CLIENT_CAPABILITY", + "PARSE_ERROR", + "REQUEST_TIMEOUT", + "UNSUPPORTED_PROTOCOL_VERSION", + "URL_ELICITATION_REQUIRED", + "ErrorData", + "JSONRPCError", + "JSONRPCMessage", + "JSONRPCNotification", + "JSONRPCRequest", + "JSONRPCResponse", + "RequestId", + "jsonrpc_message_adapter", +] + +RequestId = Annotated[int, Field(strict=True)] | str +"""The ID of a JSON-RPC request.""" + +JSONRPC_VERSION: Final[Literal["2.0"]] = "2.0" +"""The JSON-RPC version string carried by every MCP message envelope.""" + + +class JSONRPCRequest(BaseModel): + """A JSON-RPC request that expects a response.""" + + jsonrpc: Literal["2.0"] + id: RequestId + method: str + params: dict[str, Any] | None = None + + +class JSONRPCNotification(BaseModel): + """A JSON-RPC notification which does not expect a response.""" + + jsonrpc: Literal["2.0"] + method: str + params: dict[str, Any] | None = None + + +class JSONRPCResponse(BaseModel): + """A successful (non-error) response to a request. + + Named `JSONRPCResultResponse` in the 2025-11-25+ schemas; the SDK keeps the original name. + """ + + jsonrpc: Literal["2.0"] + id: RequestId + result: dict[str, Any] + + +# MCP error codes occupy the JSON-RPC server-error range -32000..-32099. +# Per the 2026-07-28 spec's allocation policy: +# -32000..-32019 implementation-defined +# -32020..-32099 reserved for spec-defined codes, allocated sequentially from -32020 +# -32002, -32042 reserved-never-reused (retired by earlier protocol versions) + +HEADER_MISMATCH = -32020 +"""HTTP headers do not match the request body, or required headers are missing/malformed (protocol 2026-07-28).""" + +MISSING_REQUIRED_CLIENT_CAPABILITY = -32021 +"""The server requires a client capability the request did not declare (protocol 2026-07-28).""" + +UNSUPPORTED_PROTOCOL_VERSION = -32022 +"""The request's protocol version is not supported by the server (protocol 2026-07-28).""" + +URL_ELICITATION_REQUIRED = -32042 +"""A URL-mode elicitation is required before the request can be processed (protocol 2025-11-25 only).""" + +# SDK error codes: SDK-internal allocations in the implementation-defined band +# -32000..-32019; not defined by the MCP schema. +CONNECTION_CLOSED = -32000 +"""SDK-only: the connection closed before a response arrived; never emitted on the wire.""" + +REQUEST_TIMEOUT = -32001 +"""SDK-only: a request timed out waiting for its response.""" + +# Standard JSON-RPC error codes +PARSE_ERROR = -32700 +"""Standard JSON-RPC: invalid JSON was received.""" + +INVALID_REQUEST = -32600 +"""Standard JSON-RPC: the message is not a valid request object.""" + +METHOD_NOT_FOUND = -32601 +"""Standard JSON-RPC: the requested method does not exist or is not available.""" + +INVALID_PARAMS = -32602 +"""Standard JSON-RPC: invalid method parameters.""" + +INTERNAL_ERROR = -32603 +"""Standard JSON-RPC: an internal error occurred on the receiver. + +The SDK uses the generic `ErrorData` envelope; the schema's per-code wrapper types are not constructed. +""" + + +class ErrorData(BaseModel): + """Error information for JSON-RPC error responses.""" + + code: int + """The error type that occurred.""" + + message: str + """A short description of the error. + + The message SHOULD be limited to a concise single sentence. + """ + + data: Any = None + """Additional information about the error. + + The value of this member is defined by the sender (e.g. detailed error information, nested errors, etc.). + """ + + +class JSONRPCError(BaseModel): + """A response to a request that indicates an error occurred.""" + + jsonrpc: Literal["2.0"] + id: RequestId | None + """The id of the request this error responds to. + + Required but nullable per JSON-RPC 2.0: `None` encodes `"id": null` (the id could not be determined). + """ + + error: ErrorData + + +JSONRPCMessage = JSONRPCRequest | JSONRPCNotification | JSONRPCResponse | JSONRPCError +"""Any JSON-RPC envelope that can be decoded off the wire or encoded to be sent.""" + +jsonrpc_message_adapter: TypeAdapter[JSONRPCMessage] = TypeAdapter(JSONRPCMessage) diff --git a/venv/lib/python3.12/site-packages/mcp_types/methods.py b/venv/lib/python3.12/site-packages/mcp_types/methods.py new file mode 100644 index 0000000..41959c5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp_types/methods.py @@ -0,0 +1,746 @@ +"""Per-version method maps and parse/serialize functions for MCP traffic. + +This module is supported public API; the `mcp_types._v*` packages it draws on +are internal validators and not for direct import. + +Surface maps key `(method, version)` to per-version wire types (key absence is +the version gate; shape validation is per schema era, i.e. 2025-11-25 for every +pre-2026 version and 2026-07-28 for 2026). Monolith maps key `method` to the +version-free `mcp_types` models user code receives.""" + +from __future__ import annotations + +from collections.abc import Mapping +from functools import cache +from types import MappingProxyType, UnionType +from typing import Any, Final, Literal, TypeGuard, TypeVar, cast, get_args + +from pydantic import BaseModel, TypeAdapter + +import mcp_types as types +import mcp_types._v2025_11_25 as v2025 +import mcp_types._v2026_07_28 as v2026 +from mcp_types.version import KNOWN_PROTOCOL_VERSIONS + +__all__ = [ + "CACHEABLE_METHODS", + "CLIENT_NOTIFICATIONS", + "CLIENT_REQUESTS", + "CLIENT_RESULTS", + "CacheableMethod", + "INPUT_REQUIRED_METHODS", + "MONOLITH_NOTIFICATIONS", + "MONOLITH_REQUESTS", + "MONOLITH_RESULTS", + "SERVER_NOTIFICATIONS", + "SERVER_REQUESTS", + "SERVER_RESULTS", + "SPEC_CLIENT_METHODS", + "SPEC_CLIENT_NOTIFICATION_METHODS", + "is_input_required", + "parse_client_notification", + "parse_client_request", + "parse_client_result", + "parse_server_notification", + "parse_server_request", + "parse_server_result", + "serialize_server_result", + "validate_client_notification", + "validate_client_request", + "validate_client_result", + "validate_server_result", +] + + +# --- Surface maps: client-to-server --- + +CLIENT_REQUESTS: Final[Mapping[tuple[str, str], type[BaseModel]]] = MappingProxyType( + { + # 2024-11-05 + ("completion/complete", "2024-11-05"): v2025.CompleteRequest, + ("initialize", "2024-11-05"): v2025.InitializeRequest, + ("logging/setLevel", "2024-11-05"): v2025.SetLevelRequest, + ("ping", "2024-11-05"): v2025.PingRequest, + ("prompts/get", "2024-11-05"): v2025.GetPromptRequest, + ("prompts/list", "2024-11-05"): v2025.ListPromptsRequest, + ("resources/list", "2024-11-05"): v2025.ListResourcesRequest, + ("resources/read", "2024-11-05"): v2025.ReadResourceRequest, + ("resources/subscribe", "2024-11-05"): v2025.SubscribeRequest, + ("resources/templates/list", "2024-11-05"): v2025.ListResourceTemplatesRequest, + ("resources/unsubscribe", "2024-11-05"): v2025.UnsubscribeRequest, + ("tools/call", "2024-11-05"): v2025.CallToolRequest, + ("tools/list", "2024-11-05"): v2025.ListToolsRequest, + # 2025-03-26 + ("completion/complete", "2025-03-26"): v2025.CompleteRequest, + ("initialize", "2025-03-26"): v2025.InitializeRequest, + ("logging/setLevel", "2025-03-26"): v2025.SetLevelRequest, + ("ping", "2025-03-26"): v2025.PingRequest, + ("prompts/get", "2025-03-26"): v2025.GetPromptRequest, + ("prompts/list", "2025-03-26"): v2025.ListPromptsRequest, + ("resources/list", "2025-03-26"): v2025.ListResourcesRequest, + ("resources/read", "2025-03-26"): v2025.ReadResourceRequest, + ("resources/subscribe", "2025-03-26"): v2025.SubscribeRequest, + ("resources/templates/list", "2025-03-26"): v2025.ListResourceTemplatesRequest, + ("resources/unsubscribe", "2025-03-26"): v2025.UnsubscribeRequest, + ("tools/call", "2025-03-26"): v2025.CallToolRequest, + ("tools/list", "2025-03-26"): v2025.ListToolsRequest, + # 2025-06-18 + ("completion/complete", "2025-06-18"): v2025.CompleteRequest, + ("initialize", "2025-06-18"): v2025.InitializeRequest, + ("logging/setLevel", "2025-06-18"): v2025.SetLevelRequest, + ("ping", "2025-06-18"): v2025.PingRequest, + ("prompts/get", "2025-06-18"): v2025.GetPromptRequest, + ("prompts/list", "2025-06-18"): v2025.ListPromptsRequest, + ("resources/list", "2025-06-18"): v2025.ListResourcesRequest, + ("resources/read", "2025-06-18"): v2025.ReadResourceRequest, + ("resources/subscribe", "2025-06-18"): v2025.SubscribeRequest, + ("resources/templates/list", "2025-06-18"): v2025.ListResourceTemplatesRequest, + ("resources/unsubscribe", "2025-06-18"): v2025.UnsubscribeRequest, + ("tools/call", "2025-06-18"): v2025.CallToolRequest, + ("tools/list", "2025-06-18"): v2025.ListToolsRequest, + # 2025-11-25 (tasks/* deliberately absent) + ("completion/complete", "2025-11-25"): v2025.CompleteRequest, + ("initialize", "2025-11-25"): v2025.InitializeRequest, + ("logging/setLevel", "2025-11-25"): v2025.SetLevelRequest, + ("ping", "2025-11-25"): v2025.PingRequest, + ("prompts/get", "2025-11-25"): v2025.GetPromptRequest, + ("prompts/list", "2025-11-25"): v2025.ListPromptsRequest, + ("resources/list", "2025-11-25"): v2025.ListResourcesRequest, + ("resources/read", "2025-11-25"): v2025.ReadResourceRequest, + ("resources/subscribe", "2025-11-25"): v2025.SubscribeRequest, + ("resources/templates/list", "2025-11-25"): v2025.ListResourceTemplatesRequest, + ("resources/unsubscribe", "2025-11-25"): v2025.UnsubscribeRequest, + ("tools/call", "2025-11-25"): v2025.CallToolRequest, + ("tools/list", "2025-11-25"): v2025.ListToolsRequest, + # 2026-07-28 (lifecycle, logging, subscribe pair removed; discover/listen added) + ("completion/complete", "2026-07-28"): v2026.CompleteRequest, + ("prompts/get", "2026-07-28"): v2026.GetPromptRequest, + ("prompts/list", "2026-07-28"): v2026.ListPromptsRequest, + ("resources/list", "2026-07-28"): v2026.ListResourcesRequest, + ("resources/read", "2026-07-28"): v2026.ReadResourceRequest, + ("resources/templates/list", "2026-07-28"): v2026.ListResourceTemplatesRequest, + ("server/discover", "2026-07-28"): v2026.DiscoverRequest, + ("subscriptions/listen", "2026-07-28"): v2026.SubscriptionsListenRequest, + ("tools/call", "2026-07-28"): v2026.CallToolRequest, + ("tools/list", "2026-07-28"): v2026.ListToolsRequest, + } +) + +CLIENT_NOTIFICATIONS: Final[Mapping[tuple[str, str], type[BaseModel]]] = MappingProxyType( + { + # 2024-11-05 + ("notifications/cancelled", "2024-11-05"): v2025.CancelledNotification, + ("notifications/initialized", "2024-11-05"): v2025.InitializedNotification, + ("notifications/progress", "2024-11-05"): v2025.ProgressNotification, + ("notifications/roots/list_changed", "2024-11-05"): v2025.RootsListChangedNotification, + # 2025-03-26 + ("notifications/cancelled", "2025-03-26"): v2025.CancelledNotification, + ("notifications/initialized", "2025-03-26"): v2025.InitializedNotification, + ("notifications/progress", "2025-03-26"): v2025.ProgressNotification, + ("notifications/roots/list_changed", "2025-03-26"): v2025.RootsListChangedNotification, + # 2025-06-18 + ("notifications/cancelled", "2025-06-18"): v2025.CancelledNotification, + ("notifications/initialized", "2025-06-18"): v2025.InitializedNotification, + ("notifications/progress", "2025-06-18"): v2025.ProgressNotification, + ("notifications/roots/list_changed", "2025-06-18"): v2025.RootsListChangedNotification, + # 2025-11-25 (tasks/status deliberately absent) + ("notifications/cancelled", "2025-11-25"): v2025.CancelledNotification, + ("notifications/initialized", "2025-11-25"): v2025.InitializedNotification, + ("notifications/progress", "2025-11-25"): v2025.ProgressNotification, + ("notifications/roots/list_changed", "2025-11-25"): v2025.RootsListChangedNotification, + # 2026-07-28 (initialized, progress and roots/list_changed removed) + ("notifications/cancelled", "2026-07-28"): v2026.CancelledNotification, + } +) + + +# --- Surface maps: server-to-client --- + +SERVER_REQUESTS: Final[Mapping[tuple[str, str], type[BaseModel]]] = MappingProxyType( + { + # 2024-11-05 + ("ping", "2024-11-05"): v2025.PingRequest, + ("roots/list", "2024-11-05"): v2025.ListRootsRequest, + ("sampling/createMessage", "2024-11-05"): v2025.CreateMessageRequest, + # 2025-03-26 + ("ping", "2025-03-26"): v2025.PingRequest, + ("roots/list", "2025-03-26"): v2025.ListRootsRequest, + ("sampling/createMessage", "2025-03-26"): v2025.CreateMessageRequest, + # 2025-06-18 (adds elicitation/create) + ("elicitation/create", "2025-06-18"): v2025.ElicitRequest, + ("ping", "2025-06-18"): v2025.PingRequest, + ("roots/list", "2025-06-18"): v2025.ListRootsRequest, + ("sampling/createMessage", "2025-06-18"): v2025.CreateMessageRequest, + # 2025-11-25 (tasks/* deliberately absent) + ("elicitation/create", "2025-11-25"): v2025.ElicitRequest, + ("ping", "2025-11-25"): v2025.PingRequest, + ("roots/list", "2025-11-25"): v2025.ListRootsRequest, + ("sampling/createMessage", "2025-11-25"): v2025.CreateMessageRequest, + # 2026-07-28: none (schema defines no ServerRequest union) + } +) + +SERVER_NOTIFICATIONS: Final[Mapping[tuple[str, str], type[BaseModel]]] = MappingProxyType( + { + # 2024-11-05 + ("notifications/cancelled", "2024-11-05"): v2025.CancelledNotification, + ("notifications/message", "2024-11-05"): v2025.LoggingMessageNotification, + ("notifications/progress", "2024-11-05"): v2025.ProgressNotification, + ("notifications/prompts/list_changed", "2024-11-05"): v2025.PromptListChangedNotification, + ("notifications/resources/list_changed", "2024-11-05"): v2025.ResourceListChangedNotification, + ("notifications/resources/updated", "2024-11-05"): v2025.ResourceUpdatedNotification, + ("notifications/tools/list_changed", "2024-11-05"): v2025.ToolListChangedNotification, + # 2025-03-26 + ("notifications/cancelled", "2025-03-26"): v2025.CancelledNotification, + ("notifications/message", "2025-03-26"): v2025.LoggingMessageNotification, + ("notifications/progress", "2025-03-26"): v2025.ProgressNotification, + ("notifications/prompts/list_changed", "2025-03-26"): v2025.PromptListChangedNotification, + ("notifications/resources/list_changed", "2025-03-26"): v2025.ResourceListChangedNotification, + ("notifications/resources/updated", "2025-03-26"): v2025.ResourceUpdatedNotification, + ("notifications/tools/list_changed", "2025-03-26"): v2025.ToolListChangedNotification, + # 2025-06-18 + ("notifications/cancelled", "2025-06-18"): v2025.CancelledNotification, + ("notifications/message", "2025-06-18"): v2025.LoggingMessageNotification, + ("notifications/progress", "2025-06-18"): v2025.ProgressNotification, + ("notifications/prompts/list_changed", "2025-06-18"): v2025.PromptListChangedNotification, + ("notifications/resources/list_changed", "2025-06-18"): v2025.ResourceListChangedNotification, + ("notifications/resources/updated", "2025-06-18"): v2025.ResourceUpdatedNotification, + ("notifications/tools/list_changed", "2025-06-18"): v2025.ToolListChangedNotification, + # 2025-11-25 (adds elicitation/complete; tasks/status deliberately absent) + ("notifications/cancelled", "2025-11-25"): v2025.CancelledNotification, + ("notifications/elicitation/complete", "2025-11-25"): v2025.ElicitationCompleteNotification, + ("notifications/message", "2025-11-25"): v2025.LoggingMessageNotification, + ("notifications/progress", "2025-11-25"): v2025.ProgressNotification, + ("notifications/prompts/list_changed", "2025-11-25"): v2025.PromptListChangedNotification, + ("notifications/resources/list_changed", "2025-11-25"): v2025.ResourceListChangedNotification, + ("notifications/resources/updated", "2025-11-25"): v2025.ResourceUpdatedNotification, + ("notifications/tools/list_changed", "2025-11-25"): v2025.ToolListChangedNotification, + # 2026-07-28 (adds subscriptions/acknowledged; elicitation/complete removed) + ("notifications/cancelled", "2026-07-28"): v2026.CancelledNotification, + ("notifications/message", "2026-07-28"): v2026.LoggingMessageNotification, + ("notifications/progress", "2026-07-28"): v2026.ProgressNotification, + ("notifications/prompts/list_changed", "2026-07-28"): v2026.PromptListChangedNotification, + ("notifications/resources/list_changed", "2026-07-28"): v2026.ResourceListChangedNotification, + ("notifications/resources/updated", "2026-07-28"): v2026.ResourceUpdatedNotification, + ("notifications/subscriptions/acknowledged", "2026-07-28"): v2026.SubscriptionsAcknowledgedNotification, + ("notifications/tools/list_changed", "2026-07-28"): v2026.ToolListChangedNotification, + } +) + + +# --- Surface maps: results --- + +SERVER_RESULTS: Final[Mapping[tuple[str, str], type[BaseModel] | UnionType]] = MappingProxyType( + { + # 2024-11-05 + ("completion/complete", "2024-11-05"): v2025.CompleteResult, + ("initialize", "2024-11-05"): v2025.InitializeResult, + ("logging/setLevel", "2024-11-05"): v2025.EmptyResult, + ("ping", "2024-11-05"): v2025.EmptyResult, + ("prompts/get", "2024-11-05"): v2025.GetPromptResult, + ("prompts/list", "2024-11-05"): v2025.ListPromptsResult, + ("resources/list", "2024-11-05"): v2025.ListResourcesResult, + ("resources/read", "2024-11-05"): v2025.ReadResourceResult, + ("resources/subscribe", "2024-11-05"): v2025.EmptyResult, + ("resources/templates/list", "2024-11-05"): v2025.ListResourceTemplatesResult, + ("resources/unsubscribe", "2024-11-05"): v2025.EmptyResult, + ("tools/call", "2024-11-05"): v2025.CallToolResult, + ("tools/list", "2024-11-05"): v2025.ListToolsResult, + # 2025-03-26 + ("completion/complete", "2025-03-26"): v2025.CompleteResult, + ("initialize", "2025-03-26"): v2025.InitializeResult, + ("logging/setLevel", "2025-03-26"): v2025.EmptyResult, + ("ping", "2025-03-26"): v2025.EmptyResult, + ("prompts/get", "2025-03-26"): v2025.GetPromptResult, + ("prompts/list", "2025-03-26"): v2025.ListPromptsResult, + ("resources/list", "2025-03-26"): v2025.ListResourcesResult, + ("resources/read", "2025-03-26"): v2025.ReadResourceResult, + ("resources/subscribe", "2025-03-26"): v2025.EmptyResult, + ("resources/templates/list", "2025-03-26"): v2025.ListResourceTemplatesResult, + ("resources/unsubscribe", "2025-03-26"): v2025.EmptyResult, + ("tools/call", "2025-03-26"): v2025.CallToolResult, + ("tools/list", "2025-03-26"): v2025.ListToolsResult, + # 2025-06-18 + ("completion/complete", "2025-06-18"): v2025.CompleteResult, + ("initialize", "2025-06-18"): v2025.InitializeResult, + ("logging/setLevel", "2025-06-18"): v2025.EmptyResult, + ("ping", "2025-06-18"): v2025.EmptyResult, + ("prompts/get", "2025-06-18"): v2025.GetPromptResult, + ("prompts/list", "2025-06-18"): v2025.ListPromptsResult, + ("resources/list", "2025-06-18"): v2025.ListResourcesResult, + ("resources/read", "2025-06-18"): v2025.ReadResourceResult, + ("resources/subscribe", "2025-06-18"): v2025.EmptyResult, + ("resources/templates/list", "2025-06-18"): v2025.ListResourceTemplatesResult, + ("resources/unsubscribe", "2025-06-18"): v2025.EmptyResult, + ("tools/call", "2025-06-18"): v2025.CallToolResult, + ("tools/list", "2025-06-18"): v2025.ListToolsResult, + # 2025-11-25 + ("completion/complete", "2025-11-25"): v2025.CompleteResult, + ("initialize", "2025-11-25"): v2025.InitializeResult, + ("logging/setLevel", "2025-11-25"): v2025.EmptyResult, + ("ping", "2025-11-25"): v2025.EmptyResult, + ("prompts/get", "2025-11-25"): v2025.GetPromptResult, + ("prompts/list", "2025-11-25"): v2025.ListPromptsResult, + ("resources/list", "2025-11-25"): v2025.ListResourcesResult, + ("resources/read", "2025-11-25"): v2025.ReadResourceResult, + ("resources/subscribe", "2025-11-25"): v2025.EmptyResult, + ("resources/templates/list", "2025-11-25"): v2025.ListResourceTemplatesResult, + ("resources/unsubscribe", "2025-11-25"): v2025.EmptyResult, + ("tools/call", "2025-11-25"): v2025.CallToolResult, + ("tools/list", "2025-11-25"): v2025.ListToolsResult, + # 2026-07-28 (dual-result rows use the version's union aliases) + ("completion/complete", "2026-07-28"): v2026.CompleteResult, + ("prompts/get", "2026-07-28"): v2026.AnyGetPromptResult, + ("prompts/list", "2026-07-28"): v2026.ListPromptsResult, + ("resources/list", "2026-07-28"): v2026.ListResourcesResult, + ("resources/read", "2026-07-28"): v2026.AnyReadResourceResult, + ("resources/templates/list", "2026-07-28"): v2026.ListResourceTemplatesResult, + ("server/discover", "2026-07-28"): v2026.DiscoverResult, + ("subscriptions/listen", "2026-07-28"): v2026.SubscriptionsListenResult, + ("tools/call", "2026-07-28"): v2026.AnyCallToolResult, + ("tools/list", "2026-07-28"): v2026.ListToolsResult, + } +) +"""Results servers send, keyed by the originating client request's (method, version).""" + +CLIENT_RESULTS: Final[Mapping[tuple[str, str], type[BaseModel] | UnionType]] = MappingProxyType( + { + # 2024-11-05 + ("ping", "2024-11-05"): v2025.EmptyResult, + ("roots/list", "2024-11-05"): v2025.ListRootsResult, + ("sampling/createMessage", "2024-11-05"): v2025.CreateMessageResult, + # 2025-03-26 + ("ping", "2025-03-26"): v2025.EmptyResult, + ("roots/list", "2025-03-26"): v2025.ListRootsResult, + ("sampling/createMessage", "2025-03-26"): v2025.CreateMessageResult, + # 2025-06-18 + ("elicitation/create", "2025-06-18"): v2025.ElicitResult, + ("ping", "2025-06-18"): v2025.EmptyResult, + ("roots/list", "2025-06-18"): v2025.ListRootsResult, + ("sampling/createMessage", "2025-06-18"): v2025.CreateMessageResult, + # 2025-11-25 + ("elicitation/create", "2025-11-25"): v2025.ElicitResult, + ("ping", "2025-11-25"): v2025.EmptyResult, + ("roots/list", "2025-11-25"): v2025.ListRootsResult, + ("sampling/createMessage", "2025-11-25"): v2025.CreateMessageResult, + # 2026-07-28: none (no server-to-client requests at this version) + } +) +"""Results clients send, keyed by the originating server request's (method, version).""" + + +# --- Direction-specific method sets --- + +SPEC_CLIENT_METHODS: Final[frozenset[str]] = frozenset(m for m, _ in CLIENT_REQUESTS) +"""Spec request methods a client may send (any version); the server-side spec-method discriminator.""" + +SPEC_CLIENT_NOTIFICATION_METHODS: Final[frozenset[str]] = frozenset(m for m, _ in CLIENT_NOTIFICATIONS) +"""Spec notification methods a client may send (any version); the server-side spec-method discriminator.""" + + +# --- Monolith maps --- + +MONOLITH_REQUESTS: Final[Mapping[str, type[types.Request[Any, Any]]]] = MappingProxyType( + { + "completion/complete": types.CompleteRequest, + "elicitation/create": types.ElicitRequest, + "initialize": types.InitializeRequest, + "logging/setLevel": types.SetLevelRequest, + "ping": types.PingRequest, + "prompts/get": types.GetPromptRequest, + "prompts/list": types.ListPromptsRequest, + "resources/list": types.ListResourcesRequest, + "resources/read": types.ReadResourceRequest, + "resources/subscribe": types.SubscribeRequest, + "resources/templates/list": types.ListResourceTemplatesRequest, + "resources/unsubscribe": types.UnsubscribeRequest, + "roots/list": types.ListRootsRequest, + "sampling/createMessage": types.CreateMessageRequest, + "server/discover": types.DiscoverRequest, + "subscriptions/listen": types.SubscriptionsListenRequest, + "tools/call": types.CallToolRequest, + "tools/list": types.ListToolsRequest, + } +) +"""Monolith request model per method, both directions.""" + +MONOLITH_NOTIFICATIONS: Final[Mapping[str, type[types.Notification[Any, Any]]]] = MappingProxyType( + { + "notifications/cancelled": types.CancelledNotification, + "notifications/elicitation/complete": types.ElicitCompleteNotification, + "notifications/initialized": types.InitializedNotification, + "notifications/message": types.LoggingMessageNotification, + "notifications/progress": types.ProgressNotification, + "notifications/prompts/list_changed": types.PromptListChangedNotification, + "notifications/resources/list_changed": types.ResourceListChangedNotification, + "notifications/resources/updated": types.ResourceUpdatedNotification, + "notifications/roots/list_changed": types.RootsListChangedNotification, + "notifications/subscriptions/acknowledged": types.SubscriptionsAcknowledgedNotification, + "notifications/tools/list_changed": types.ToolListChangedNotification, + } +) +"""Monolith notification model per method, both directions.""" + +MONOLITH_RESULTS: Final[Mapping[str, type[types.Result] | UnionType]] = MappingProxyType( + { + "completion/complete": types.CompleteResult, + "elicitation/create": types.ElicitResult, + "initialize": types.InitializeResult, + "logging/setLevel": types.EmptyResult, + "ping": types.EmptyResult, + "prompts/get": types.GetPromptResult | types.InputRequiredResult, + "prompts/list": types.ListPromptsResult, + "resources/list": types.ListResourcesResult, + "resources/read": types.ReadResourceResult | types.InputRequiredResult, + "resources/subscribe": types.EmptyResult, + "resources/templates/list": types.ListResourceTemplatesResult, + "resources/unsubscribe": types.EmptyResult, + "roots/list": types.ListRootsResult, + # Arm order load-bearing: a single-block body satisfies both arms and + # smart-union ties resolve leftmost. Pinned by tests/types/test_methods.py. + "sampling/createMessage": types.CreateMessageResult | types.CreateMessageResultWithTools, + "server/discover": types.DiscoverResult, + "subscriptions/listen": types.SubscriptionsListenResult, + "tools/call": types.CallToolResult | types.InputRequiredResult, + "tools/list": types.ListToolsResult, + } +) +"""Monolith result model (or two-arm union) per request method.""" + + +CacheableMethod = Literal[ + "prompts/list", + "resources/list", + "resources/read", + "resources/templates/list", + "server/discover", + "tools/list", +] +"""Methods whose results carry `ttlMs`/`cacheScope`; hand-written Literal, welded to `CACHEABLE_METHODS` by tests.""" + +CACHEABLE_METHODS: Final[frozenset[str]] = frozenset( + method + for method, row in MONOLITH_RESULTS.items() + if any(issubclass(arm, types.CacheableResult) for arm in (get_args(row) if isinstance(row, UnionType) else (row,))) +) +"""Runtime mirror of `CacheableMethod`, derived from `MONOLITH_RESULTS`.""" + +INPUT_REQUIRED_METHODS: Final[frozenset[str]] = frozenset( + method + for method, row in MONOLITH_RESULTS.items() + if any( + issubclass(arm, types.InputRequiredResult) for arm in (get_args(row) if isinstance(row, UnionType) else (row,)) + ) +) +"""Methods whose results may be `InputRequiredResult`, derived from `MONOLITH_RESULTS`.""" + + +def is_input_required(result: object) -> TypeGuard[types.InputRequiredResult | dict[str, Any]]: + """True when `result` is an `input_required` interim result, typed or wire-shaped.""" + if isinstance(result, types.InputRequiredResult): + return True + return isinstance(result, Mapping) and cast("Mapping[str, Any]", result).get("resultType") == "input_required" + + +# --- Parse functions --- + +# Envelope stubs merged into bodies for surface validation (surface classes are full frames). +_REQUEST_STUB: Final[Mapping[str, Any]] = MappingProxyType({"jsonrpc": "2.0", "id": 0}) +_NOTIFICATION_STUB: Final[Mapping[str, Any]] = MappingProxyType({"jsonrpc": "2.0"}) + + +def _check_known_version(version: str) -> None: + """Raise ValueError for unknown `version` so a typo cannot silently gate every method.""" + if version not in KNOWN_PROTOCOL_VERSIONS: + raise ValueError(f"version must be a known protocol version, got {version!r}") + + +def _body(method: str, params: Mapping[str, Any] | None) -> dict[str, Any]: + """Build a JSON-RPC body, omitting `params` when None.""" + body: dict[str, Any] = {"method": method} + if params is not None: + body["params"] = params + return body + + +@cache +def _adapter(target: type[BaseModel] | UnionType) -> TypeAdapter[Any]: + return TypeAdapter(target) + + +_MonolithT = TypeVar("_MonolithT") + + +def _monolith_row(monolith: Mapping[str, _MonolithT], method: str) -> _MonolithT: + """Look up `method` in `monolith`, raising RuntimeError on miss. + + Not KeyError: the surface row already matched, so a miss is inconsistent + extension maps and must not be caught by the session's `except KeyError` gate. + """ + try: + return monolith[method] + except KeyError: + raise RuntimeError(f"inconsistent extension maps: surface defines {method!r} but monolith does not") from None + + +def validate_client_request( + method: str, + version: str, + params: Mapping[str, Any] | None, + *, + surface: Mapping[tuple[str, str], type[BaseModel]] = CLIENT_REQUESTS, +) -> None: + """Validate a client request against `surface` only. + + Raises: + ValueError: `version` is not a known protocol version. + KeyError: `(method, version)` is not in `surface` (the version gate). + pydantic.ValidationError: body fails surface validation. + """ + _check_known_version(version) + surface[(method, version)].model_validate({**_REQUEST_STUB, **_body(method, params)}, by_name=False) + + +def parse_client_request( + method: str, + version: str, + params: Mapping[str, Any] | None, + *, + surface: Mapping[tuple[str, str], type[BaseModel]] = CLIENT_REQUESTS, + monolith: Mapping[str, type[types.Request[Any, Any]]] = MONOLITH_REQUESTS, +) -> types.Request[Any, Any]: + """Validate a client request against `surface`, then parse and return its `monolith` model. + + Args: + surface: `(method, version)` to wire-type map; the version-gate lookup + and (per-schema-era) shape check run against this. Pass an extended + map to admit custom methods. + monolith: `method` to version-free model map; the returned instance is + parsed from this row. Must cover every method `surface` admits. + + Raises: + ValueError: `version` is not a known protocol version. + KeyError: `(method, version)` is not in `surface` (the version gate). + pydantic.ValidationError: body fails surface or monolith validation. + RuntimeError: surface matched but `method` has no monolith row. + """ + validate_client_request(method, version, params, surface=surface) + return _monolith_row(monolith, method).model_validate(_body(method, params), by_name=False) + + +def parse_server_request( + method: str, + version: str, + params: Mapping[str, Any] | None, + *, + surface: Mapping[tuple[str, str], type[BaseModel]] = SERVER_REQUESTS, + monolith: Mapping[str, type[types.Request[Any, Any]]] = MONOLITH_REQUESTS, +) -> types.Request[Any, Any]: + """Validate a server request against `surface`, then parse and return its `monolith` model. + + Args: + surface: `(method, version)` to wire-type map; the version-gate lookup + and (per-schema-era) shape check run against this. Pass an extended + map to admit custom methods. + monolith: `method` to version-free model map; the returned instance is + parsed from this row. Must cover every method `surface` admits. + + Raises: + ValueError: `version` is not a known protocol version. + KeyError: `(method, version)` is not in `surface` (the version gate). + pydantic.ValidationError: body fails surface or monolith validation. + RuntimeError: surface matched but `method` has no monolith row. + """ + _check_known_version(version) + surface_type = surface[(method, version)] + surface_type.model_validate({**_REQUEST_STUB, **_body(method, params)}, by_name=False) + return _monolith_row(monolith, method).model_validate(_body(method, params), by_name=False) + + +def validate_client_notification( + method: str, + version: str, + params: Mapping[str, Any] | None, + *, + surface: Mapping[tuple[str, str], type[BaseModel]] = CLIENT_NOTIFICATIONS, +) -> None: + """Validate a client notification against `surface` only. + + Raises: + ValueError: `version` is not a known protocol version. + KeyError: `(method, version)` is not in `surface`. + pydantic.ValidationError: body fails surface validation. + """ + _check_known_version(version) + surface[(method, version)].model_validate({**_NOTIFICATION_STUB, **_body(method, params)}, by_name=False) + + +def parse_client_notification( + method: str, + version: str, + params: Mapping[str, Any] | None, + *, + surface: Mapping[tuple[str, str], type[BaseModel]] = CLIENT_NOTIFICATIONS, + monolith: Mapping[str, type[types.Notification[Any, Any]]] = MONOLITH_NOTIFICATIONS, +) -> types.Notification[Any, Any]: + """Validate a client notification against `surface`, then parse and return its `monolith` model. + + Args: + surface: `(method, version)` to wire-type map; the version-gate lookup + and (per-schema-era) shape check run against this. Pass an extended + map to admit custom methods. + monolith: `method` to version-free model map; the returned instance is + parsed from this row. Must cover every method `surface` admits. + + Raises: + ValueError: `version` is not a known protocol version. + KeyError: `(method, version)` is not in `surface`. + pydantic.ValidationError: body fails surface or monolith validation. + RuntimeError: surface matched but `method` has no monolith row. + """ + validate_client_notification(method, version, params, surface=surface) + return _monolith_row(monolith, method).model_validate(_body(method, params), by_name=False) + + +def parse_server_notification( + method: str, + version: str, + params: Mapping[str, Any] | None, + *, + surface: Mapping[tuple[str, str], type[BaseModel]] = SERVER_NOTIFICATIONS, + monolith: Mapping[str, type[types.Notification[Any, Any]]] = MONOLITH_NOTIFICATIONS, +) -> types.Notification[Any, Any]: + """Validate a server notification against `surface`, then parse and return its `monolith` model. + + Args: + surface: `(method, version)` to wire-type map; the version-gate lookup + and (per-schema-era) shape check run against this. Pass an extended + map to admit custom methods. + monolith: `method` to version-free model map; the returned instance is + parsed from this row. Must cover every method `surface` admits. + + Raises: + ValueError: `version` is not a known protocol version. + KeyError: `(method, version)` is not in `surface`. + pydantic.ValidationError: body fails surface or monolith validation. + RuntimeError: surface matched but `method` has no monolith row. + """ + _check_known_version(version) + surface_type = surface[(method, version)] + surface_type.model_validate({**_NOTIFICATION_STUB, **_body(method, params)}, by_name=False) + return _monolith_row(monolith, method).model_validate(_body(method, params), by_name=False) + + +def serialize_server_result( + method: str, + version: str, + data: Mapping[str, Any], + *, + surface: Mapping[tuple[str, str], type[BaseModel] | UnionType] = SERVER_RESULTS, +) -> dict[str, Any]: + """Validate `data` against `surface` and return its surface-shaped dump. + + The surface model carries `extra="ignore"`, so fields not in `version`'s + schema are dropped from the returned dict. + + Raises: + ValueError: `version` is not a known protocol version. + KeyError: `(method, version)` is not in `surface`. + pydantic.ValidationError: result fails surface validation. + """ + _check_known_version(version) + adapter = _adapter(surface[(method, version)]) + return adapter.dump_python( + adapter.validate_python(data, by_name=False), by_alias=True, mode="json", exclude_none=True + ) + + +def validate_server_result( + method: str, + version: str, + data: Mapping[str, Any], + *, + surface: Mapping[tuple[str, str], type[BaseModel] | UnionType] = SERVER_RESULTS, +) -> None: + """Validate a server result against `surface` only. + + Raises: + ValueError: `version` is not a known protocol version. + KeyError: `(method, version)` is not in `surface`. + pydantic.ValidationError: result fails surface validation. + """ + serialize_server_result(method, version, data, surface=surface) + + +def parse_server_result( + method: str, + version: str, + data: Mapping[str, Any], + *, + surface: Mapping[tuple[str, str], type[BaseModel] | UnionType] = SERVER_RESULTS, + monolith: Mapping[str, type[types.Result] | UnionType] = MONOLITH_RESULTS, +) -> types.Result: + """Validate a server result against `surface`, then parse and return its `monolith` model. + + Args: + surface: `(method, version)` to wire-type map; the version-gate lookup + and (per-schema-era) shape check run against this. Pass an extended + map to admit custom methods. + monolith: `method` to version-free model map; the returned instance is + parsed from this row. Must cover every method `surface` admits. + + Raises: + ValueError: `version` is not a known protocol version. + KeyError: `(method, version)` is not in `surface`. + pydantic.ValidationError: result fails surface or monolith validation. + RuntimeError: surface matched but `method` has no monolith row. + """ + validate_server_result(method, version, data, surface=surface) + result: types.Result = _adapter(_monolith_row(monolith, method)).validate_python(data, by_name=False) + return result + + +def validate_client_result( + method: str, + version: str, + data: Mapping[str, Any], + *, + surface: Mapping[tuple[str, str], type[BaseModel] | UnionType] = CLIENT_RESULTS, +) -> None: + """Validate a client result against `surface` only. + + Raises: + ValueError: `version` is not a known protocol version. + KeyError: `(method, version)` is not in `surface`. + pydantic.ValidationError: result fails surface validation. + """ + _check_known_version(version) + _adapter(surface[(method, version)]).validate_python(data, by_name=False) + + +def parse_client_result( + method: str, + version: str, + data: Mapping[str, Any], + *, + surface: Mapping[tuple[str, str], type[BaseModel] | UnionType] = CLIENT_RESULTS, + monolith: Mapping[str, type[types.Result] | UnionType] = MONOLITH_RESULTS, +) -> types.Result: + """Validate a client result against `surface`, then parse and return its `monolith` model. + + Args: + surface: `(method, version)` to wire-type map; the version-gate lookup + and (per-schema-era) shape check run against this. Pass an extended + map to admit custom methods. + monolith: `method` to version-free model map; the returned instance is + parsed from this row. Must cover every method `surface` admits. + + Raises: + ValueError: `version` is not a known protocol version. + KeyError: `(method, version)` is not in `surface`. + pydantic.ValidationError: result fails surface or monolith validation. + RuntimeError: surface matched but `method` has no monolith row. + """ + validate_client_result(method, version, data, surface=surface) + result: types.Result = _adapter(_monolith_row(monolith, method)).validate_python(data, by_name=False) + return result diff --git a/venv/lib/python3.12/site-packages/mcp_types/py.typed b/venv/lib/python3.12/site-packages/mcp_types/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/mcp_types/version.py b/venv/lib/python3.12/site-packages/mcp_types/version.py new file mode 100644 index 0000000..e2f7e1a --- /dev/null +++ b/venv/lib/python3.12/site-packages/mcp_types/version.py @@ -0,0 +1,74 @@ +"""Protocol-version registry and comparison helpers. + +Date-string protocol revisions happen to sort lexicographically, but versions +are an enumerated set, not an ordered scalar: future identifiers are not +guaranteed to be date-shaped, and unrecognized peer strings must compare +conservatively instead of accidentally (e.g. "zzz" > "2025-11-25"). All +ordering questions go through KNOWN_PROTOCOL_VERSIONS. +""" + +from typing import Final + +__all__ = [ + "KNOWN_PROTOCOL_VERSIONS", + "HANDSHAKE_PROTOCOL_VERSIONS", + "MODERN_PROTOCOL_VERSIONS", + "SUPPORTED_PROTOCOL_VERSIONS", + "LATEST_PROTOCOL_VERSION", + "LATEST_HANDSHAKE_VERSION", + "LATEST_MODERN_VERSION", + "OLDEST_SUPPORTED_VERSION", + "is_version_at_least", +] + +KNOWN_PROTOCOL_VERSIONS: Final[tuple[str, ...]] = ( + "2024-11-05", + "2025-03-26", + "2025-06-18", + "2025-11-25", + "2026-07-28", +) +"""Every released protocol revision, oldest to newest.""" + +HANDSHAKE_PROTOCOL_VERSIONS: Final[tuple[str, ...]] = ( + "2024-11-05", + "2025-03-26", + "2025-06-18", + "2025-11-25", +) +"""Protocol revisions reachable via the initialize handshake.""" + +MODERN_PROTOCOL_VERSIONS: Final[tuple[str, ...]] = ("2026-07-28",) +"""Protocol revisions that use the stateless per-request envelope.""" + +SUPPORTED_PROTOCOL_VERSIONS: tuple[str, ...] = (*HANDSHAKE_PROTOCOL_VERSIONS, *MODERN_PROTOCOL_VERSIONS) +"""Deprecated: prefer HANDSHAKE_PROTOCOL_VERSIONS or MODERN_PROTOCOL_VERSIONS. + +Kept as the union for v1.x compatibility. +""" + +LATEST_PROTOCOL_VERSION: Final[str] = KNOWN_PROTOCOL_VERSIONS[-1] +"""Newest protocol revision this SDK speaks (any era).""" + +LATEST_HANDSHAKE_VERSION: Final[str] = HANDSHAKE_PROTOCOL_VERSIONS[-1] +"""Newest revision reachable via the ``initialize`` handshake; the client's offer and server's counter-offer default.""" + +LATEST_MODERN_VERSION: Final[str] = MODERN_PROTOCOL_VERSIONS[-1] +"""Newest per-request-envelope revision; the ``server/discover`` probe default.""" + +OLDEST_SUPPORTED_VERSION: Final[str] = HANDSHAKE_PROTOCOL_VERSIONS[0] +"""Oldest revision this SDK still negotiates via the ``initialize`` handshake.""" + + +def is_version_at_least(version: str, minimum: str) -> bool: + """Return True if `version` is a known revision at least as new as `minimum`. + + Unknown `version` strings return False (treat unrecognized peers + conservatively). `minimum` must be a member of KNOWN_PROTOCOL_VERSIONS; + passing anything else is programmer error and raises ValueError. + """ + if minimum not in KNOWN_PROTOCOL_VERSIONS: + raise ValueError(f"minimum must be a known protocol version, got {minimum!r}") + if version not in KNOWN_PROTOCOL_VERSIONS: + return False + return KNOWN_PROTOCOL_VERSIONS.index(version) >= KNOWN_PROTOCOL_VERSIONS.index(minimum) diff --git a/venv/lib/python3.12/site-packages/multipart/__init__.py b/venv/lib/python3.12/site-packages/multipart/__init__.py new file mode 100644 index 0000000..53c08d2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/multipart/__init__.py @@ -0,0 +1,38 @@ +# Copyright 2012, Andrew Dunham +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This only works if using a file system, other loaders not implemented. + +import importlib.util +import sys +import warnings +from pathlib import Path + +for p in sys.path: + file_path = Path(p, "multipart.py") + try: + if file_path.is_file(): + spec = importlib.util.spec_from_file_location("multipart", file_path) + assert spec is not None, f"{file_path} found but not loadable!" + module = importlib.util.module_from_spec(spec) + sys.modules["multipart"] = module + assert spec.loader is not None, f"{file_path} must be loadable!" + spec.loader.exec_module(module) + break + except PermissionError: + pass +else: + warnings.warn("Please use `import python_multipart` instead.", PendingDeprecationWarning, stacklevel=2) + from python_multipart import * + from python_multipart import __all__, __version__ diff --git a/venv/lib/python3.12/site-packages/multipart/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/multipart/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..3e44e41 Binary files /dev/null and b/venv/lib/python3.12/site-packages/multipart/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/multipart/__pycache__/decoders.cpython-312.pyc b/venv/lib/python3.12/site-packages/multipart/__pycache__/decoders.cpython-312.pyc new file mode 100644 index 0000000..8debca1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/multipart/__pycache__/decoders.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/multipart/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/multipart/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..f0cc9a4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/multipart/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/multipart/__pycache__/multipart.cpython-312.pyc b/venv/lib/python3.12/site-packages/multipart/__pycache__/multipart.cpython-312.pyc new file mode 100644 index 0000000..1d2b359 Binary files /dev/null and b/venv/lib/python3.12/site-packages/multipart/__pycache__/multipart.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/multipart/decoders.py b/venv/lib/python3.12/site-packages/multipart/decoders.py new file mode 100644 index 0000000..31acdfb --- /dev/null +++ b/venv/lib/python3.12/site-packages/multipart/decoders.py @@ -0,0 +1 @@ +from python_multipart.decoders import * diff --git a/venv/lib/python3.12/site-packages/multipart/exceptions.py b/venv/lib/python3.12/site-packages/multipart/exceptions.py new file mode 100644 index 0000000..36815d1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/multipart/exceptions.py @@ -0,0 +1 @@ +from python_multipart.exceptions import * diff --git a/venv/lib/python3.12/site-packages/multipart/multipart.py b/venv/lib/python3.12/site-packages/multipart/multipart.py new file mode 100644 index 0000000..7bf567d --- /dev/null +++ b/venv/lib/python3.12/site-packages/multipart/multipart.py @@ -0,0 +1 @@ +from python_multipart.multipart import * diff --git a/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/METADATA new file mode 100644 index 0000000..f3286ca --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/METADATA @@ -0,0 +1,1300 @@ +Metadata-Version: 2.4 +Name: openai +Version: 3.20.0 +Summary: The official Python library for the openai API +Project-URL: Homepage, https://github.com/openai/openai-python +Project-URL: Repository, https://github.com/openai/openai-python +Author-email: OpenAI +License-Expression: Apache-2.0 +License-File: LICENSE +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: MacOS +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: OS Independent +Classifier: Operating System :: POSIX +Classifier: Operating System :: POSIX :: Linux +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Typing :: Typed +Requires-Python: >=3.10 +Requires-Dist: anyio<5,>=4.10.0 +Requires-Dist: httpx2<3,>=2.12.0 +Requires-Dist: jiter<1,>=0.16.0 +Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,<3,>=1.10.13 +Requires-Dist: sniffio +Requires-Dist: typing-extensions<5,>=4.14 +Provides-Extra: aiohttp +Requires-Dist: aiohttp>=3.14.3; extra == 'aiohttp' +Provides-Extra: bedrock +Requires-Dist: botocore<2,>=1.40.0; extra == 'bedrock' +Requires-Dist: urllib3<3,>=2.7.0; extra == 'bedrock' +Provides-Extra: datalib +Requires-Dist: numpy>=1; extra == 'datalib' +Requires-Dist: pandas>=1.2.3; extra == 'datalib' +Provides-Extra: realtime +Requires-Dist: websockets<16,>=13; extra == 'realtime' +Provides-Extra: voice-helpers +Requires-Dist: numpy>=2.0.2; extra == 'voice-helpers' +Requires-Dist: sounddevice>=0.5.1; extra == 'voice-helpers' +Description-Content-Type: text/markdown + +# OpenAI Python API library + + +[![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/) + +The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.10+ +application. The library includes type definitions for all request params and response fields, +and offers both synchronous and asynchronous clients powered by [HTTPX2](https://httpx2.pydantic.dev/). + +It is generated from our [OpenAPI specification](https://github.com/openai/openai-openapi). + +## Documentation + +The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs/api-reference). The full API of this library can be found in [api.md](https://github.com/openai/openai-python/tree/main/api.md). + +## Installation + +```sh +# install from PyPI +pip install openai +``` + +## Usage + +The full API of this library can be found in [api.md](https://github.com/openai/openai-python/tree/main/api.md). + +The primary API for interacting with OpenAI models is the [Responses API](https://developers.openai.com/api/reference/resources/responses). You can generate text from the model with the code below. + +```python +import os +from openai import OpenAI + +client = OpenAI( + # This is the default and can be omitted + api_key=os.environ.get("OPENAI_API_KEY"), +) + +response = client.responses.create( + model="gpt-5.5", + instructions="You are a coding assistant that talks like a pirate.", + input="How do I check if a Python object is an instance of a class?", +) + +print(response.output_text) +``` + +The previous standard (supported indefinitely) for generating text is the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat). You can use that API to generate text from the model with the code below. + +```python +from openai import OpenAI + +client = OpenAI() + +completion = client.chat.completions.create( + model="gpt-5.5", + messages=[ + {"role": "developer", "content": "Talk like a pirate."}, + { + "role": "user", + "content": "How do I check if a Python object is an instance of a class?", + }, + ], +) + +print(completion.choices[0].message.content) +``` + +While you can provide an `api_key` keyword argument, +we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) +to add `OPENAI_API_KEY="My API Key"` to your `.env` file +so that your API key is not stored in source control. +[Get an API key here](https://platform.openai.com/settings/organization/api-keys). + +### Workload Identity Authentication + +For secure, automated environments like cloud-managed Kubernetes, Azure, and Google Cloud Platform, you can use workload identity authentication with short-lived tokens from cloud identity providers instead of long-lived API keys. + +#### Kubernetes (service account tokens) + +```python +from openai import OpenAI +from openai.auth import k8s_service_account_token_provider + +client = OpenAI( + workload_identity={ + "identity_provider_id": "idp-123", + "service_account_id": "sa-456", + "provider": k8s_service_account_token_provider( + "/var/run/secrets/kubernetes.io/serviceaccount/token" + ), + }, +) + +response = client.chat.completions.create( + model="gpt-5.5", + messages=[{"role": "user", "content": "Hello!"}], +) +``` + +#### Azure (managed identity) + +```python +from openai import OpenAI +from openai.auth import azure_managed_identity_token_provider + +client = OpenAI( + workload_identity={ + "identity_provider_id": "idp-123", + "service_account_id": "sa-456", + "provider": azure_managed_identity_token_provider( + resource="https://management.azure.com/", + ), + }, +) +``` + +#### Google Cloud Platform (compute engine metadata) + +```python +from openai import OpenAI +from openai.auth import gcp_id_token_provider + +client = OpenAI( + workload_identity={ + "identity_provider_id": "idp-123", + "service_account_id": "sa-456", + "provider": gcp_id_token_provider(audience="https://api.openai.com/v1"), + }, +) +``` + +#### Custom subject token provider + +```python +from openai import OpenAI + + +def get_custom_token() -> str: + return "your-jwt-token" + + +client = OpenAI( + workload_identity={ + "identity_provider_id": "idp-123", + "service_account_id": "sa-456", + "provider": { + "token_type": "jwt", + "get_token": get_custom_token, + }, + } +) +``` + +You can also customize the token refresh buffer (default is 1200 seconds (20 minutes) before expiration): + +```python +from openai import OpenAI +from openai.auth import k8s_service_account_token_provider + +client = OpenAI( + workload_identity={ + "identity_provider_id": "idp-123", + "service_account_id": "sa-456", + "provider": k8s_service_account_token_provider("/var/token"), + "refresh_buffer_seconds": 120.0, + } +) +``` + +#### X.509 workload identity (mutual TLS) + +For X.509 workload identity federation, configure the client certificate and +server trust on an HTTPX2 client, then pass only the identity-provider and +service-account IDs to the SDK: + +```python +import os +import ssl + +from openai import OpenAI, DefaultHttpx2Client +from openai.auth import x509_workload_identity + +tls_context = ssl.create_default_context( + cafile=os.getenv("OPENAI_MTLS_CA_BUNDLE"), +) +tls_context.load_cert_chain( + certfile=os.environ["OPENAI_MTLS_CERTIFICATE_CHAIN"], + keyfile=os.environ["OPENAI_MTLS_PRIVATE_KEY"], + password=os.getenv("OPENAI_MTLS_PRIVATE_KEY_PASSWORD"), +) + +client = OpenAI( + workload_identity=x509_workload_identity( + identity_provider_id=os.environ["OPENAI_IDENTITY_PROVIDER_ID"], + service_account_id=os.environ["OPENAI_SERVICE_ACCOUNT_ID"], + # refresh_buffer_seconds=120.0, + ), + http_client=DefaultHttpx2Client( + verify=tls_context, + follow_redirects=False, + ), +) +``` + +X.509 mode defaults to `https://mtls.api.openai.com/v1` when neither `base_url` +nor `OPENAI_BASE_URL` is set. The same configured HTTP client presents its +certificate to the fixed mTLS token-exchange endpoint and to the API. Tokens +are exchanged lazily, cached, and refreshed automatically. Certificate files, +private keys, passwords, server trust, proxies, and rotation remain application +and transport concerns. + +X.509 API requests require HTTPS and must stay on the configured API origin. +The effective HTTP Host authority must match that origin. +Provider API-key and proxy-only headers cannot be sent to the API alongside +X.509 authentication. +Token exchanges do not inherit API request hooks, authentication, or cookies. +Identity settings are captured when the client is constructed; create a new +client to change the identity. Azure clients do not support X.509 workload +identity. + +For asynchronous requests, use `AsyncOpenAI` with +`DefaultAsyncHttpx2Client`. See the complete [sync rollout-toggle +example](examples/x509_workload_identity.py) and [async rollout-toggle +example](examples/x509_workload_identity_async.py), which select API-key or +X.509 authentication with the application-owned `OPENAI_AUTH_MODE` +environment variable. X.509 workload identity currently supports HTTP APIs; +Realtime and WebSockets are not included. + +### Vision + +With an image URL: + +```python +prompt = "What is in this image?" +img_url = "https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg" + +response = client.responses.create( + model="gpt-5.5", + input=[ + { + "role": "user", + "content": [ + {"type": "input_text", "text": prompt}, + {"type": "input_image", "image_url": f"{img_url}"}, + ], + } + ], +) +``` + +With the image as a base64 encoded string: + +```python +import base64 +from openai import OpenAI + +client = OpenAI() + +prompt = "What is in this image?" +with open("path/to/image.png", "rb") as image_file: + b64_image = base64.b64encode(image_file.read()).decode("utf-8") + +response = client.responses.create( + model="gpt-5.5", + input=[ + { + "role": "user", + "content": [ + {"type": "input_text", "text": prompt}, + {"type": "input_image", "image_url": f"data:image/png;base64,{b64_image}"}, + ], + } + ], +) +``` + +## Async usage + +Simply import `AsyncOpenAI` instead of `OpenAI` and use `await` with each API call: + +```python +import os +import asyncio +from openai import AsyncOpenAI + +client = AsyncOpenAI( + # This is the default and can be omitted + api_key=os.environ.get("OPENAI_API_KEY"), +) + + +async def main() -> None: + response = await client.responses.create( + model="gpt-5.5", input="Explain disestablishmentarianism to a smart five year old." + ) + print(response.output_text) + + +asyncio.run(main()) +``` + +Functionality between the synchronous and asynchronous clients is otherwise identical. + +### With aiohttp + +By default, the async client uses HTTPX2. For improved concurrency performance, you may also use `aiohttp` as the HTTPX2 transport. + +You can enable this by installing `aiohttp`: + +```sh +# install from PyPI +pip install openai[aiohttp] +``` + +Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: + +```python +import os +import asyncio +from openai import DefaultAioHttpClient +from openai import AsyncOpenAI + + +async def main() -> None: + async with AsyncOpenAI( + api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted + http_client=DefaultAioHttpClient(), + ) as client: + chat_completion = await client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-5.5", + ) + + +asyncio.run(main()) +``` + +### HTTPX2 migration + +HTTPX2 is the default HTTP client. If you configure a custom HTTP client, transport, timeout, authentication handler, event hook, or request mock, see the [HTTPX2 migration guide](https://github.com/openai/openai-python/tree/main/httpx2.md). + +## Streaming responses + +We provide support for streaming responses using Server-Sent Events (SSE). + +```python +from openai import OpenAI + +client = OpenAI() + +stream = client.responses.create( + model="gpt-5.5", + input="Write a one-sentence bedtime story about a unicorn.", + stream=True, +) + +for event in stream: + print(event) +``` + +The async client uses the exact same interface. + +```python +import asyncio +from openai import AsyncOpenAI + +client = AsyncOpenAI() + + +async def main(): + stream = await client.responses.create( + model="gpt-5.5", + input="Write a one-sentence bedtime story about a unicorn.", + stream=True, + ) + + async for event in stream: + print(event) + + +asyncio.run(main()) +``` + +## Realtime API + +The Realtime API enables you to build low-latency, multi-modal conversational experiences. It currently supports text and audio as both input and output, as well as [function calling](https://platform.openai.com/docs/guides/function-calling) through a WebSocket connection. + +Under the hood the SDK uses the [`websockets`](https://websockets.readthedocs.io/en/stable/) library to manage connections. + +The Realtime API works through a combination of client-sent events and server-sent events. Clients can send events to do things like update session configuration or send text and audio inputs. Server events confirm when audio responses have completed, or when a text response from the model has been received. A full event reference can be found [here](https://platform.openai.com/docs/api-reference/realtime-client-events) and a guide can be found [here](https://platform.openai.com/docs/guides/realtime). + +Basic text based example: + +```py +import asyncio +from openai import AsyncOpenAI + +async def main(): + client = AsyncOpenAI() + + async with client.realtime.connect(model="gpt-realtime-2") as connection: + await connection.session.update( + session={"type": "realtime", "output_modalities": ["text"]} + ) + + await connection.conversation.item.create( + item={ + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "Say hello!"}], + } + ) + await connection.response.create() + + async for event in connection: + if event.type == "response.output_text.delta": + print(event.delta, flush=True, end="") + + elif event.type == "response.output_text.done": + print() + + elif event.type == "response.done": + break + +asyncio.run(main()) +``` + +However the real magic of the Realtime API is handling audio inputs / outputs, see this example [TUI script](https://github.com/openai/openai-python/blob/main/examples/realtime/push_to_talk_app.py) for a fully fledged example. + +### Realtime error handling + +Whenever an error occurs, the Realtime API will send an [`error` event](https://platform.openai.com/docs/guides/realtime-model-capabilities#error-handling) and the connection will stay open and remain usable. This means you need to handle it yourself, as _no errors are raised directly_ by the SDK when an `error` event comes in. + +```py +client = AsyncOpenAI() + +async with client.realtime.connect(model="gpt-realtime-2") as connection: + ... + async for event in connection: + if event.type == 'error': + print(event.error.type) + print(event.error.code) + print(event.error.event_id) + print(event.error.message) +``` + +## Using types + +Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: + +- Serializing back into JSON, `model.to_json()` +- Converting to a dictionary, `model.to_dict()` + +Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. + +## Pagination + +List methods in the OpenAI API are paginated. + +This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: + +```python +from openai import OpenAI + +client = OpenAI() + +all_jobs = [] +# Automatically fetches more pages as needed. +for job in client.fine_tuning.jobs.list( + limit=20, +): + # Do something with job here + all_jobs.append(job) +print(all_jobs) +``` + +Or, asynchronously: + +```python +import asyncio +from openai import AsyncOpenAI + +client = AsyncOpenAI() + + +async def main() -> None: + all_jobs = [] + # Iterate through items across all pages, issuing requests as needed. + async for job in client.fine_tuning.jobs.list( + limit=20, + ): + all_jobs.append(job) + print(all_jobs) + + +asyncio.run(main()) +``` + +Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: + +```python +first_page = await client.fine_tuning.jobs.list( + limit=20, +) +if first_page.has_next_page(): + print(f"will fetch next page using these details: {first_page.next_page_info()}") + next_page = await first_page.get_next_page() + print(f"number of items we just fetched: {len(next_page.data)}") + +# Remove `await` for non-async usage. +``` + +Or just work directly with the returned data: + +```python +first_page = await client.fine_tuning.jobs.list( + limit=20, +) + +print(f"next page cursor: {first_page.after}") # => "next page cursor: ..." +for job in first_page.data: + print(job.id) + +# Remove `await` for non-async usage. +``` + +## Nested params + +Nested parameters are dictionaries, typed using `TypedDict`, for example: + +```python +from openai import OpenAI + +client = OpenAI() + +response = client.responses.create( + input=[ + { + "role": "user", + "content": "How much ?", + } + ], + model="gpt-5.5", + text={"format": {"type": "json_object"}}, +) +``` + +## File uploads + +Request parameters that correspond to file uploads can be passed as `bytes`, a file-like object, a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, or a tuple of `(filename, contents, media type)`. + +```python +from pathlib import Path +from openai import OpenAI + +client = OpenAI() + +client.files.create( + file=Path("input.jsonl"), + purpose="fine-tune", +) +``` + +When uploading an in-memory file-like object such as `io.BytesIO`, include a filename when the API needs the file extension to determine its format. Passing a tuple is the most explicit option: + +```python +import io + +audio = io.BytesIO(audio_bytes) +transcription = client.audio.transcriptions.create( + model="gpt-4o-transcribe", + file=("audio.wav", audio, "audio/wav"), +) +``` + +You can also set a `.name` attribute such as `audio.name = "audio.wav"` on a mutable file-like object before passing it directly. Without a filename, multipart transports may use a generic name such as `upload`, which does not provide an audio extension for format detection. + +The async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically. + +## Webhook Verification + +Verifying webhook signatures is _optional but encouraged_. + +For more information about webhooks, see [the API docs](https://platform.openai.com/docs/guides/webhooks). + +### Parsing webhook payloads + +For most use cases, you will likely want to verify the webhook and parse the payload at the same time. To achieve this, we provide the method `client.webhooks.unwrap()`, which parses a webhook request and verifies that it was sent by OpenAI. This method will raise an error if the signature is invalid. + +Note that the `body` parameter must be the raw JSON string sent from the server (do not parse it first). The `.unwrap()` method will parse this JSON for you into an event object after verifying the webhook was sent from OpenAI. + +```python +from openai import OpenAI +from flask import Flask, request + +app = Flask(__name__) +client = OpenAI() # OPENAI_WEBHOOK_SECRET environment variable is used by default + + +@app.route("/webhook", methods=["POST"]) +def webhook(): + request_body = request.get_data(as_text=True) + + try: + event = client.webhooks.unwrap(request_body, request.headers) + + if event.type == "response.completed": + print("Response completed:", event.data) + elif event.type == "response.failed": + print("Response failed:", event.data) + else: + print("Unhandled event type:", event.type) + + return "ok" + except Exception as e: + print("Invalid signature:", e) + return "Invalid signature", 400 + + +if __name__ == "__main__": + app.run(port=8000) +``` + +### Verifying webhook payloads directly + +In some cases, you may want to verify the webhook separately from parsing the payload. If you prefer to handle these steps separately, we provide the method `client.webhooks.verify_signature()` to _only verify_ the signature of a webhook request. Like `.unwrap()`, this method will raise an error if the signature is invalid. + +Note that the `body` parameter must be the raw JSON string sent from the server (do not parse it first). You will then need to parse the body after verifying the signature. + +```python +import json +from openai import OpenAI +from flask import Flask, request + +app = Flask(__name__) +client = OpenAI() # OPENAI_WEBHOOK_SECRET environment variable is used by default + + +@app.route("/webhook", methods=["POST"]) +def webhook(): + request_body = request.get_data(as_text=True) + + try: + client.webhooks.verify_signature(request_body, request.headers) + + # Parse the body after verification + event = json.loads(request_body) + print("Verified event:", event) + + return "ok" + except Exception as e: + print("Invalid signature:", e) + return "Invalid signature", 400 + + +if __name__ == "__main__": + app.run(port=8000) +``` + +## Handling errors + +When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `openai.APIConnectionError` is raised. + +When the API returns a non-success status code (that is, 4xx or 5xx +response), a subclass of `openai.APIStatusError` is raised, containing `status_code` and `response` properties. + +All errors inherit from `openai.APIError`. + +When consuming a `Stream` or `AsyncStream`, read timeouts raise `APITimeoutError` +and other HTTPX request failures raise `APIConnectionError`. Catch these SDK +exceptions instead of raw HTTPX exceptions; the original exception is available +as `__cause__`. Stream consumption is not automatically retried, because replaying +a request could duplicate output already delivered to your application. +The Assistants event-handler helpers and raw `with_streaming_response` iterators +retain their existing exception behavior. + +```python +import openai +from openai import OpenAI + +client = OpenAI() + +try: + client.fine_tuning.jobs.create( + model="gpt-4o", + training_file="file-abc123", + ) +except openai.APIConnectionError as e: + print("The server could not be reached") + print(e.__cause__) # an underlying Exception, likely raised within HTTPX2. +except openai.RateLimitError as e: + print("A 429 status code was received; we should back off a bit.") +except openai.APIStatusError as e: + print("Another non-200-range status code was received") + print(e.status_code) + print(e.response) +``` + +Error codes are as follows: + +| Status Code | Error Type | +| ----------- | -------------------------- | +| 400 | `BadRequestError` | +| 401 | `AuthenticationError` | +| 403 | `PermissionDeniedError` | +| 404 | `NotFoundError` | +| 422 | `UnprocessableEntityError` | +| 429 | `RateLimitError` | +| >=500 | `InternalServerError` | +| N/A | `APIConnectionError` | + +## Request IDs + +> For more information on debugging requests, see [these docs](https://platform.openai.com/docs/api-reference/debugging-requests) + +All object responses in the SDK provide a `_request_id` property which is added from the `x-request-id` response header so that you can quickly log failing requests and report them back to OpenAI. + +```python +response = await client.responses.create( + model="gpt-5.5", + input="Say 'this is a test'.", +) +print(response._request_id) # req_123 +``` + +Note that unlike other properties that use an `_` prefix, the `_request_id` property +_is_ public. Unless documented otherwise, _all_ other `_` prefix properties, +methods and modules are _private_. + +> [!IMPORTANT] +> If you need to access request IDs for failed requests you must catch the `APIStatusError` exception + +```python +import openai + +try: + completion = await client.chat.completions.create( + messages=[{"role": "user", "content": "Say this is a test"}], model="gpt-5.5" + ) +except openai.APIStatusError as exc: + print(exc.request_id) # req_123 + raise exc +``` + +## Retries + +Certain errors are automatically retried 2 times by default, with a short exponential backoff. +Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, +429 Rate Limit, and >=500 Internal errors are all retried by default. + +Requests are retried only when their body can be safely resent. + +You can use the `max_retries` option to configure or disable retry settings: + +```python +from openai import OpenAI + +# Configure the default for all requests: +client = OpenAI( + # default is 2 + max_retries=0, +) + +# Or, configure per-request: +client.with_options(max_retries=5).chat.completions.create( + messages=[ + { + "role": "user", + "content": "How can I get the name of the current day in JavaScript?", + } + ], + model="gpt-5.5", +) +``` + +`max_retries` must be a non-negative integer. `0` disables retries. Use a large +integer, such as `1000`, for a larger retry budget. Other values raise an error +before a request is sent. + +Application exceptions raised by custom transports or hooks propagate unchanged, +including task-executor cancellation signals. + +## Timeouts + +By default requests time out after 10 minutes. You can configure this with a `timeout` option, +which accepts a float or an [`httpx2.Timeout`](https://httpx2.pydantic.dev/) object: + +```python +import httpx2 +from openai import OpenAI + +# Configure the default for all requests: +client = OpenAI( + # 20 seconds (default is 10 minutes) + timeout=20.0, +) + +# More granular control: +client = OpenAI( + timeout=httpx2.Timeout(60.0, read=5.0, write=10.0, connect=2.0), +) + +# Override per-request: +client.with_options(timeout=5.0).chat.completions.create( + messages=[ + { + "role": "user", + "content": "How can I list all files in a directory using Python?", + } + ], + model="gpt-5.5", +) +``` + +On timeout, an `APITimeoutError` is thrown. + +Note that requests that time out are [retried twice by default](https://github.com/openai/openai-python/tree/main/#retries). + +## Advanced + +### Logging + +We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module. + +You can enable logging by setting the environment variable `OPENAI_LOG` to `info`. + +```shell +$ export OPENAI_LOG=info +``` + +Or to `debug` for more verbose logging. Set it to `warning`, `error`, or `critical` +to show only messages at that level or higher. `OPENAI_LOG` configures the `openai` +logger; configure HTTP transport loggers separately using Python logging. + +### How to tell whether `None` means `null` or missing + +In an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`: + +```py +if response.my_field is None: + if 'my_field' not in response.model_fields_set: + print('Got json like {}, without a "my_field" key present at all.') + else: + print('Got json like {"my_field": null}.') +``` + +### Accessing raw response data (e.g. headers) + +The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g., + +```py +from openai import OpenAI + +client = OpenAI() +response = client.chat.completions.with_raw_response.create( + messages=[{ + "role": "user", + "content": "Say this is a test", + }], + model="gpt-5.5", +) +print(response.headers.get('X-My-Header')) + +completion = response.parse() # get the object that `chat.completions.create()` would have returned +print(completion) +``` + +These methods return a [`LegacyAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_legacy_response.py) object. This is a legacy class as we're changing it slightly in the next major version. + +For the sync client this will mostly be the same with the exception +of `content` & `text` will be methods instead of properties. In the +async client, all methods will be async. + +A migration script will be provided & the migration in general should +be smooth. + +#### `.with_streaming_response` + +The above interface eagerly reads the full response body when you make the request, which may not always be what you want. + +To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. + +As such, `.with_streaming_response` methods return a different [`APIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object, and the async client returns an [`AsyncAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object. + +```python +with client.chat.completions.with_streaming_response.create( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-5.5", +) as response: + print(response.headers.get("X-My-Header")) + + for line in response.iter_lines(): + print(line) +``` + +The context manager is required so that the response will reliably be closed. + +### Making custom/undocumented requests + +This library is typed for convenient access to the documented API. + +If you need to access undocumented endpoints, params, or response properties, the library can still be used. + +#### Undocumented endpoints + +To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other +http verbs. Options on the client will be respected (such as retries) when making this request. + +```py +import httpx2 + +response = client.post( + "/foo", + cast_to=httpx2.Response, + body={"my_param": True}, +) + +print(response.headers.get("x-foo")) +``` + +#### Undocumented request params + +If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request +options. + +#### Undocumented response properties + +To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You +can also get all the extra fields on the Pydantic model as a dict with +[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra). + +### Configuring the HTTP client + +You can override the [HTTPX2 client](https://httpx2.pydantic.dev/) to customize proxies, transports, authentication, event hooks, and other advanced HTTP behavior. See the [HTTPX2 migration guide](https://github.com/openai/openai-python/tree/main/httpx2.md) when updating an existing custom client. + +```python +import httpx2 +from openai import OpenAI, DefaultHttpx2Client + +client = OpenAI( + # Or use the `OPENAI_BASE_URL` env var + base_url="http://my.test.server.example.com:8083/v1", + http_client=DefaultHttpx2Client( + proxy="http://my.test.proxy.example.com", + transport=httpx2.HTTPTransport(local_address="0.0.0.0"), + ), +) +``` + +You can also customize the client on a per-request basis by using `with_options()`: + +```python +client.with_options(http_client=DefaultHttpx2Client(...)) +``` + +#### Mutual TLS + +Before configuring a client, review the +[OpenAI Mutual TLS Beta Program](https://help.openai.com/en/articles/10876024-openai-mutual-tls-beta-program) +for enrollment, currently supported endpoints, and certificate requirements. + +For API-key authenticated HTTP requests that require mutual TLS (mTLS), configure +a native [`ssl.SSLContext`](https://docs.python.org/3/library/ssl.html#ssl.SSLContext) +and pass it through the custom HTTP client: + +```python +import os +import ssl + +from openai import OpenAI, DefaultHttpx2Client + +# Server trust is configured independently. Without `cafile`, this uses the +# operating system's normal trusted certificate authorities. +ssl_context = ssl.create_default_context( + cafile=os.environ.get("OPENAI_MTLS_CA_BUNDLE"), +) +ssl_context.load_cert_chain( + # This PEM must contain the leaf certificate first, followed by every + # intermediate certificate needed to reach the server's trust anchor. + certfile=os.environ["OPENAI_MTLS_CERTIFICATE_CHAIN"], + keyfile=os.environ["OPENAI_MTLS_PRIVATE_KEY"], + password=os.environ.get("OPENAI_MTLS_PRIVATE_KEY_PASSWORD"), +) + +client = OpenAI( + api_key=os.environ["OPENAI_API_KEY"], + # A custom HTTP client does not tell the SDK that mTLS is configured, so + # select the mTLS endpoint explicitly. Preserve an EU or custom override. + base_url=os.environ.get( + "OPENAI_BASE_URL", + "https://mtls.api.openai.com/v1", + ), + # A client certificate belongs to the HTTP client, not the base URL. + # Disable redirects so it cannot follow a response to another origin. + http_client=DefaultHttpx2Client( + verify=ssl_context, + follow_redirects=False, + ), +) +``` + +The async configuration is equivalent: + +```python +import os +import ssl + +from openai import AsyncOpenAI, DefaultAsyncHttpx2Client + +ssl_context = ssl.create_default_context( + cafile=os.environ.get("OPENAI_MTLS_CA_BUNDLE"), +) +ssl_context.load_cert_chain( + certfile=os.environ["OPENAI_MTLS_CERTIFICATE_CHAIN"], + keyfile=os.environ["OPENAI_MTLS_PRIVATE_KEY"], + password=os.environ.get("OPENAI_MTLS_PRIVATE_KEY_PASSWORD"), +) + +client = AsyncOpenAI( + api_key=os.environ["OPENAI_API_KEY"], + base_url=os.environ.get( + "OPENAI_BASE_URL", + "https://mtls.api.openai.com/v1", + ), + http_client=DefaultAsyncHttpx2Client( + verify=ssl_context, + follow_redirects=False, + ), +) +``` + +See the complete [sync HTTPX2](https://github.com/openai/openai-python/tree/main/examples/mtls_httpx2.py) and +[async HTTPX2](https://github.com/openai/openai-python/tree/main/examples/mtls_httpx2_async.py) examples. + +The certificate-bearing HTTP client is transport-wide. For API-key mTLS, +dedicate it to the selected API origin; X.509 workload identity also uses the +fixed OpenAI mTLS token-exchange origin. Do not reuse the client for unrelated +services or pass it through `with_options()` with a different `base_url`. +If redirects are required for API-key mTLS, add an +HTTPX2 request hook that rejects requests whose scheme, host, or port differs +from the configured mTLS origin before enabling `follow_redirects`. + +`SSLContext.load_cert_chain()` raises during setup for unreadable or malformed +files and for a private key that does not match the leaf certificate. Certificate +expiry, key usage, extended key usage, SAN, and trust policy remain TLS server +decisions. OpenAI does not fetch missing intermediates through AIA, so provide a +complete, leaf-first client-chain PEM. Intermediate-chain support is currently +enabled by request. Until it is enabled for your organization, use a client leaf +certificate directly signed by the uploaded CA. + +For certificate rotation, build a new `SSLContext`, HTTP client, and `OpenAI` or +`AsyncOpenAI` client. This creates a fresh connection pool; close the old SDK +client after its in-flight requests finish. Do not assume existing TLS +connections will renegotiate. + +This recipe applies to ordinary API-key HTTP traffic. For certificate-backed +token exchange, use the X.509 workload identity configuration described above. +Realtime WebSocket mTLS is not included. + +### Managing HTTP resources + +By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting. + +```py +from openai import OpenAI + +with OpenAI() as client: + # make requests here + ... + +# HTTP client is now closed +``` + +For `AsyncOpenAI`, use `async with` or `await client.close()` before shutting down +the event loop. Garbage collection cannot reliably await asynchronous cleanup. + +```py +import asyncio +from openai import AsyncOpenAI + + +async def main() -> None: + async with AsyncOpenAI() as client: + response = await client.responses.create( + model="gpt-5.5", input="Say this is a test" + ) + print(response.output_text) + + +asyncio.run(main()) +``` + +## Microsoft Azure OpenAI + +To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the `AzureOpenAI` +class instead of the `OpenAI` class. + +> [!IMPORTANT] +> The Azure API shape differs from the core API shape which means that the static types for responses / params +> won't always be correct. + +```py +from openai import AzureOpenAI + +# gets the API Key from environment variable AZURE_OPENAI_API_KEY +client = AzureOpenAI( + # https://learn.microsoft.com/azure/ai-services/openai/reference#rest-api-versioning + api_version="2023-07-01-preview", + # https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource + azure_endpoint="https://example-endpoint.openai.azure.com", +) + +completion = client.chat.completions.create( + model="deployment-name", # e.g. gpt-35-instant + messages=[ + { + "role": "user", + "content": "How do I output all files in a directory using Python?", + }, + ], +) +print(completion.to_json()) +``` + +In addition to the options provided in the base `OpenAI` client, the following options are provided: + +- `azure_endpoint` (or the `AZURE_OPENAI_ENDPOINT` environment variable) +- `azure_deployment` +- `api_version` (or the `OPENAI_API_VERSION` environment variable) +- `azure_ad_token` (or the `AZURE_OPENAI_AD_TOKEN` environment variable) +- `azure_ad_token_provider` + +An example of using the client with Microsoft Entra ID (formerly known as Azure Active Directory) can be found [here](https://github.com/openai/openai-python/blob/main/examples/azure_ad.py). + +## Amazon Bedrock + +To use this library with [Amazon Bedrock's OpenAI-compatible API](https://docs.aws.amazon.com/bedrock/latest/userguide/models-api-compatibility.html), configure the standard `OpenAI` client with the Bedrock provider. + +Install the optional Bedrock dependencies to use the standard AWS credential chain and SigV4 authentication: + +```sh +pip install 'openai[bedrock]' +``` + +```py +from openai import OpenAI +from openai.providers import bedrock + +# Uses your normal AWS credentials. You can omit region when it is +# configured through AWS_REGION, AWS_DEFAULT_REGION, or your AWS profile. +client = OpenAI( + provider=bedrock( + region="us-west-2", + ) +) + +response = client.responses.create( + model="openai.gpt-5.4", + input="Say hello!", +) + +print(response.output_text) +``` + +The provider configures AWS authentication and the Bedrock Mantle endpoint while retaining the normal SDK resources, retries, streaming, and error handling. AWS controls which endpoints and features are supported; unsupported calls surface the provider's normal HTTP errors through the SDK. + +The default AWS credential chain supports environment credentials, shared credentials and config files, named profiles, SSO and assume-role profiles, and workload credentials such as ECS, EKS, and EC2 metadata. To select a named profile: + +```py +client = OpenAI( + provider=bedrock( + profile="my-profile", + ) +) +``` + +You can also pass `access_key_id` and `secret_access_key`, with an optional `session_token`, or a refreshable `credential_provider` that returns botocore-compatible credentials. Explicit bearer and AWS credential options are mutually exclusive. + +Pass `base_url` to `bedrock(...)` or set `AWS_BEDROCK_BASE_URL` to override the derived `https://bedrock-mantle..api.aws/openai/v1` endpoint. Custom URLs retain Mantle signing by default; pass `endpoint="runtime"` to use Runtime signing. + +SigV4 requests require replayable, fully serialized request bodies. Standard JSON requests already meet this requirement, and response streaming is unaffected. Low-level one-shot request streams must be buffered before sending, or sent with bearer authentication and retries disabled. + +Bearer tokens remain available as a compatibility or manual authentication mode. Set `AWS_BEARER_TOKEN_BEDROCK` to an [Amazon Bedrock API key](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html), pass `api_key`, or provide a refresh callback: + +```py +client = OpenAI( + provider=bedrock( + region="us-west-2", + token_provider=lambda: refresh_bedrock_token(), + ) +) +``` + +Without explicit authentication, `AWS_BEARER_TOKEN_BEDROCK` takes precedence over the default AWS credential chain for backwards compatibility. + +### Legacy `BedrockOpenAI` client + +`BedrockOpenAI` and `AsyncBedrockOpenAI` remain available for existing applications and delegate to the same provider implementation. New applications should prefer `OpenAI(provider=bedrock(...))`. + +```py +from openai import BedrockOpenAI + +client = BedrockOpenAI( + aws_region="us-west-2", + aws_profile="my-profile", +) +``` + +The legacy module client also continues to support `openai.api_type = "amazon-bedrock"` or `OPENAI_API_TYPE=amazon-bedrock`. + +## Versioning + +This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: + +1. Changes that only affect static types, without breaking runtime behavior. +2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_ +3. Changes that we do not expect to impact the vast majority of users in practice. + +Minimum supported Python version increases are released as minor versions, not patches, when package metadata can keep users on the final compatible SDK release. See the [Python version support policy](https://github.com/openai/openai-python/tree/main/./PYTHON_VERSION_POLICY.md) for the support window, release treatment, and compatibility history. + +We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. + +We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-python/issues) with questions, bugs, or suggestions. + +### Determining the installed version + +If you've upgraded to the latest version but aren't seeing any new features you were expecting then your python environment is likely still using an older version. + +You can determine the version that is being used at runtime with: + +```py +import openai +print(openai.__version__) +``` + +## Requirements + +Python 3.10 or higher. + +## Contributing + +Please share bug reports and feature requests through [GitHub issues](https://github.com/openai/openai-python/issues). +Pull requests are limited to repository collaborators; we do not accept pull requests from non-collaborators. +See [CONTRIBUTING.md](https://github.com/openai/openai-python/blob/main/CONTRIBUTING.md) for the contribution policy and development guide. +For security vulnerabilities, follow [SECURITY.md](https://github.com/openai/openai-python/blob/main/SECURITY.md). diff --git a/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/RECORD new file mode 100644 index 0000000..863e032 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/RECORD @@ -0,0 +1,3760 @@ +openai-3.20.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +openai-3.20.0.dist-info/METADATA,sha256=0ktaFOQrUztEFeeJt5RCgacfj7AaEu2d4_Rp1JN9c3E,44303 +openai-3.20.0.dist-info/RECORD,, +openai-3.20.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +openai-3.20.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +openai-3.20.0.dist-info/licenses/LICENSE,sha256=Y263152pu21RWks_1BeqJmees88WOW3atLxV-nTmFuQ,11336 +openai/__init__.py,sha256=3wSttrekSBlWNCtNut65uFVeeJ4u_R-Eafpt200i96g,14243 +openai/__pycache__/__init__.cpython-312.pyc,, +openai/__pycache__/_base_client.cpython-312.pyc,, +openai/__pycache__/_client.cpython-312.pyc,, +openai/__pycache__/_compat.cpython-312.pyc,, +openai/__pycache__/_constants.cpython-312.pyc,, +openai/__pycache__/_data_residency.cpython-312.pyc,, +openai/__pycache__/_event_handler.cpython-312.pyc,, +openai/__pycache__/_exceptions.cpython-312.pyc,, +openai/__pycache__/_files.cpython-312.pyc,, +openai/__pycache__/_httpx2.cpython-312.pyc,, +openai/__pycache__/_legacy_response.cpython-312.pyc,, +openai/__pycache__/_models.cpython-312.pyc,, +openai/__pycache__/_module_client.cpython-312.pyc,, +openai/__pycache__/_multipart.cpython-312.pyc,, +openai/__pycache__/_provider.cpython-312.pyc,, +openai/__pycache__/_qs.cpython-312.pyc,, +openai/__pycache__/_resource.cpython-312.pyc,, +openai/__pycache__/_response.cpython-312.pyc,, +openai/__pycache__/_send_queue.cpython-312.pyc,, +openai/__pycache__/_streaming.cpython-312.pyc,, +openai/__pycache__/_types.cpython-312.pyc,, +openai/__pycache__/_version.cpython-312.pyc,, +openai/__pycache__/pagination.cpython-312.pyc,, +openai/__pycache__/version.cpython-312.pyc,, +openai/_base_client.py,sha256=egoXPt8_rbazEIldXbb6CxQmS9qiWzPdTrKdhgqCY5s,82260 +openai/_client.py,sha256=DiqzqP0ipVyTEs1xLJs4lIVmEhpjhKCyntVbfzOQ3iY,94230 +openai/_compat.py,sha256=yntbvsd6TcP1hjcLEpqhozb1JW5pZWy81caHiE8rv50,7213 +openai/_constants.py,sha256=7sy8goIvDkNy9C9mav0dHh_oDLLvcTVwGKAXCsa5zjI,414 +openai/_data_residency.py,sha256=uj_5YyVtS6_gl3kybhU3s08Z6fFvJwVoxstYd0iYwKs,1631 +openai/_event_handler.py,sha256=QU7JgAh8bc_iqgAwk_Nj9v6L_Qhp1ZessCGmAfI-kZI,2771 +openai/_exceptions.py,sha256=OQu2tR_FbcWwWbpypupYeooA0z9G-HPwKzHWiPW5BFY,6662 +openai/_extras/__init__.py,sha256=sainrYWujCxIyL24wNpKfMVr-ZyBPlnSZfqXcg2S6Xg,165 +openai/_extras/__pycache__/__init__.cpython-312.pyc,, +openai/_extras/__pycache__/_common.cpython-312.pyc,, +openai/_extras/__pycache__/numpy_proxy.cpython-312.pyc,, +openai/_extras/__pycache__/pandas_proxy.cpython-312.pyc,, +openai/_extras/__pycache__/sounddevice_proxy.cpython-312.pyc,, +openai/_extras/_common.py,sha256=NWWtgbdJsO3hQGQxaXGfVk0LjeIE5AFZ8VS_795hhMc,364 +openai/_extras/numpy_proxy.py,sha256=LyTZkKDdnjz0h1SKLsphrhmXyUsJ_xEUhTFMrCf7k7g,805 +openai/_extras/pandas_proxy.py,sha256=NCEt1Dqwc_0H85YdsWPDE3lPDJtYnBT8G-gJE_BCeEc,637 +openai/_extras/sounddevice_proxy.py,sha256=xDoE21YGu13dSAJJkiOM9Qdb7uOIv5zskaJRX6xciEg,725 +openai/_files.py,sha256=GkIH1CTkNfemw7ubismdnveBnHshXXs_Of8qY0AzQlQ,5502 +openai/_httpx2.py,sha256=V5EaMVxF4PHQqUqMMovYFTmRFFk8TFoDZR7D_BPTjXQ,5270 +openai/_legacy_response.py,sha256=-gADHOls_0uJTdGTz--BNYTJtyC787iH4AP59Dtp1uI,16481 +openai/_models.py,sha256=QfpXGTU5iB2oVDvA5ZgpUL7Nsu9f7DzZ4iJu73qQmfQ,37041 +openai/_module_client.py,sha256=4B7M3qP4VgRNfr-BoM8P-q6-i6Ot0d7dwx2yWhwRUvA,6278 +openai/_multipart.py,sha256=ZzWcBBboLnWdF3gkUuYbmt9lZ1KL-9NE9VRLjoej9aE,2254 +openai/_provider.py,sha256=5q4HqhFSW-CpjJDKhcw2HG9eo-nADpsCQcktPmeeWhc,2499 +openai/_qs.py,sha256=ZSfQv6arGBBoWlcy60zK1zmPE2Ije0i8kzKdnoxWUjU,4833 +openai/_resource.py,sha256=SO3DwG-W43B2Y-p9lyD8qqXpYVtLrRSSkqC2rm2qMlI,1013 +openai/_response.py,sha256=R06XaIFPQUINgmmQoX08iIdWjFTz9IC9yoC5Qs-kuSU,29811 +openai/_send_queue.py,sha256=w2gmVQpbWru6-ern6s4ozjE15EYxQXQntPfMK-4atws,4358 +openai/_streaming.py,sha256=l_HrPd9nGa7CWuZZKCZ9UMNaJiOFjycU1j175aSAL_8,15811 +openai/_types.py,sha256=blrxMbXW7J6G5yOOc1G-qgQpWlCLxi4Eig36JyBXGm0,7916 +openai/_utils/__init__.py,sha256=-N2uXyxcgXx-UFmwiyPhsCI1cIu_e2duStUVTEXixKk,2473 +openai/_utils/__pycache__/__init__.cpython-312.pyc,, +openai/_utils/__pycache__/_compat.cpython-312.pyc,, +openai/_utils/__pycache__/_datetime_parse.cpython-312.pyc,, +openai/_utils/__pycache__/_json.cpython-312.pyc,, +openai/_utils/__pycache__/_logs.cpython-312.pyc,, +openai/_utils/__pycache__/_path.cpython-312.pyc,, +openai/_utils/__pycache__/_proxy.cpython-312.pyc,, +openai/_utils/__pycache__/_reflection.cpython-312.pyc,, +openai/_utils/__pycache__/_resources_proxy.cpython-312.pyc,, +openai/_utils/__pycache__/_streams.cpython-312.pyc,, +openai/_utils/__pycache__/_sync.cpython-312.pyc,, +openai/_utils/__pycache__/_transform.cpython-312.pyc,, +openai/_utils/__pycache__/_typing.cpython-312.pyc,, +openai/_utils/__pycache__/_utils.cpython-312.pyc,, +openai/_utils/_compat.py,sha256=uItVxVstOeA9SIX8dl7nDCdZq98o0jLGzMMVdHh9_Ek,1244 +openai/_utils/_datetime_parse.py,sha256=bABTs0Bc6rabdFvnIwXjEhWL15TcRgWZ_6XGTqN8xUk,4204 +openai/_utils/_json.py,sha256=bl95uuIWwgSfXX-gP1trK_lDAPwJujYfJ05Cxo2SEC4,962 +openai/_utils/_logs.py,sha256=wSaHBo1syXxqJGdYJPfMxgG-8mwGkpez_-Vf9QZ6Nns,1738 +openai/_utils/_path.py,sha256=QuqkunDoGSPbzDvTOXfmbzk5QZ-ByE_SUxtYxHMNBgs,4701 +openai/_utils/_proxy.py,sha256=aglnj2yBTDyGX9Akk2crZHrl10oqRmceUy2Zp008XEs,1975 +openai/_utils/_reflection.py,sha256=aTXm-W0Kww4PJo5LPkUnQ92N-2UvrK1-D67cJVBlIgw,1426 +openai/_utils/_resources_proxy.py,sha256=AHHZCOgv-2CRqB4B52dB7ySlE5q6QCWj0bsTqNmzikw,589 +openai/_utils/_streams.py,sha256=SMC90diFFecpEg_zgDRVbdR3hSEIgVVij4taD-noMLM,289 +openai/_utils/_sync.py,sha256=HBnZkkBnzxtwOZe0212C4EyoRvxhTVtTrLFDz2_xVCg,1589 +openai/_utils/_transform.py,sha256=_gvB1zlvuE0-8_tXJjX8Am4Sk9xoAU70EfEB2ldBs6s,16211 +openai/_utils/_typing.py,sha256=N_5PPuFNsaygbtA_npZd98SVN1LQQvFTKL6bkWPBZGU,4786 +openai/_utils/_utils.py,sha256=PpHOWkadWzhjEQADW3-a_0yfrXeA8znWsd9Zht3nsLY,13566 +openai/_vendor/__init__.py,sha256=ivXEBhotcVjt8kMnPk_meHgy5Muo6Z_5mfAWVLaAO5A,85 +openai/_vendor/__pycache__/__init__.cpython-312.pyc,, +openai/_vendor/httpx_aiohttp/FORK.md,sha256=ZyBjc47ZbsX_lVOhjQoaNx7P0xVqcS3rdK7pv1fYHqI,1109 +openai/_vendor/httpx_aiohttp/LICENSE,sha256=FUdZwenrWOQmQ-5D7FnVVt700dcC2RGkhMRp4C-F3V0,1493 +openai/_vendor/httpx_aiohttp/README.md,sha256=akXYvcHjd_7MbBIrPKWGyrsL1ZWzwVBebMXwr4XXr3s,834 +openai/_vendor/httpx_aiohttp/__init__.py,sha256=8ZJcoEnDcoR7ZreEJAi6fdYDdRzPB6Ovl8wZ4fSnKqU,305 +openai/_vendor/httpx_aiohttp/__pycache__/__init__.cpython-312.pyc,, +openai/_vendor/httpx_aiohttp/__pycache__/client.cpython-312.pyc,, +openai/_vendor/httpx_aiohttp/__pycache__/transport.cpython-312.pyc,, +openai/_vendor/httpx_aiohttp/client.py,sha256=x_bLdQcyMi74u8GK4SnXQHbkWwJF6_sJziQl0rFGH6g,1805 +openai/_vendor/httpx_aiohttp/transport.py,sha256=zt8X-_V-okLDbRQWkOOblEFiyahPH9-xyxlNaY4yzEA,7121 +openai/_version.py,sha256=RhdH1-zCE7SSQN1MoMhhImAubxCwVwuQs4UrnT9pWx0,72 +openai/auth/__init__.py,sha256=qVmT8HPponu9Dj1FUH0wwPIMUIl9ZG5DYIB6GcDQXAQ,894 +openai/auth/__pycache__/__init__.cpython-312.pyc,, +openai/auth/__pycache__/_workload.cpython-312.pyc,, +openai/auth/__pycache__/_x509.cpython-312.pyc,, +openai/auth/_workload.py,sha256=9h_42wDsZdKT586mViiaxrDEf6oJl_pwwXMez1eUlRI,15643 +openai/auth/_x509.py,sha256=HAi3s1-fq7vQwyGToZyht4CRFRvjjAWKs3b5ag2QmsU,27296 +openai/helpers/__init__.py,sha256=F0x_Pguq1XC2KXZYbfxUG-G_FxJ3mlsi7HaFZ1x-g9A,130 +openai/helpers/__pycache__/__init__.cpython-312.pyc,, +openai/helpers/__pycache__/local_audio_player.cpython-312.pyc,, +openai/helpers/__pycache__/microphone.cpython-312.pyc,, +openai/helpers/local_audio_player.py,sha256=Z7VNHRQnB8JzsG67BVpAmQAS8AvuGpk9NAqQqO-z6I0,5856 +openai/helpers/microphone.py,sha256=mNXWMv1vdrXV2im5fPEI35WK3u-TbXCuFGIQ5VnyH1g,3343 +openai/lib/.keep,sha256=0Vj9JNClXIfVZ-wzz7DrNsl7bpQqIlNSb8W560qOsIk,163 +openai/lib/__init__.py,sha256=BMTfMnlbugMgDA1STDIAlx4bI4t4l_8bQmJxd0th0n8,126 +openai/lib/__pycache__/__init__.cpython-312.pyc,, +openai/lib/__pycache__/_azure_websocket.cpython-312.pyc,, +openai/lib/__pycache__/_bedrock_auth.cpython-312.pyc,, +openai/lib/__pycache__/_files.cpython-312.pyc,, +openai/lib/__pycache__/_old_api.cpython-312.pyc,, +openai/lib/__pycache__/_pydantic.cpython-312.pyc,, +openai/lib/__pycache__/_realtime.cpython-312.pyc,, +openai/lib/__pycache__/_tools.cpython-312.pyc,, +openai/lib/__pycache__/_vector_stores.cpython-312.pyc,, +openai/lib/__pycache__/_webhooks.cpython-312.pyc,, +openai/lib/__pycache__/_websocket.cpython-312.pyc,, +openai/lib/__pycache__/azure.cpython-312.pyc,, +openai/lib/__pycache__/bedrock.cpython-312.pyc,, +openai/lib/_azure_websocket.py,sha256=HAjjIffhFiwSz7TWS1Jx-YsqpWQc8Gx4761y8Aj4N1U,327 +openai/lib/_bedrock_auth.py,sha256=_S3vfyMDYICTtTPmR-BVC_J4A8dn7de9Ct0qzl-tl7k,7432 +openai/lib/_files.py,sha256=i7C6xINdkQK_JMQLGhwY_1y7rfJdFLpoPbh32BJXzJQ,1557 +openai/lib/_old_api.py,sha256=XZnXBrEKuTd70iJirj5mGW35fZoqruJobbBTq6bvg10,1947 +openai/lib/_parsing/__init__.py,sha256=HR96Np47LbxwP5w4TYiEi0sOSg_bk5yLTteoKYKAfYw,539 +openai/lib/_parsing/__pycache__/__init__.cpython-312.pyc,, +openai/lib/_parsing/__pycache__/_audio.cpython-312.pyc,, +openai/lib/_parsing/__pycache__/_completions.cpython-312.pyc,, +openai/lib/_parsing/__pycache__/_embeddings.cpython-312.pyc,, +openai/lib/_parsing/__pycache__/_responses.cpython-312.pyc,, +openai/lib/_parsing/_audio.py,sha256=35ja0078wpsboJTX51bU51Fx245stTs2H5KPNHhrB8M,2243 +openai/lib/_parsing/_completions.py,sha256=Jdrm7RXVIv_lTAaIQkX8HHFC0l0Gwlfk-Bq9EYtRYbU,10301 +openai/lib/_parsing/_embeddings.py,sha256=hSu_-pY4hsopg3menu7HIr5fst7ojUKLmDhHK8_YWj4,1241 +openai/lib/_parsing/_responses.py,sha256=mzlOVaO1WjdcevyD_TOy_F0lshiz5yyXi0dgCVB3r1I,7307 +openai/lib/_pydantic.py,sha256=Cf0vGwuWdNEuIUg8WNREjWRGApMObgl8DjdLU4f5jAc,5623 +openai/lib/_realtime.py,sha256=8zj7Zw9j3kGIqPKaGuTveLAOmCi2PsbZPn0fQbJBgZs,318 +openai/lib/_tools.py,sha256=Dc4U2TXKvfAvVUvDS30SDeftrwgGM2vZ85t5ojLHiEg,1969 +openai/lib/_vector_stores.py,sha256=DPLgdZUAraHoq7zUn8UoUnj9QoSDzJnaL5y4D93wrBw,6320 +openai/lib/_webhooks.py,sha256=72NrnAzfzM14L-zmgs80HO-arUVXGcMS7sjat1c04zw,2208 +openai/lib/_websocket.py,sha256=GfMQJSNRMVtpq7XOSsywVZ8vbk6K8ZjBCV6azDXOU5g,1455 +openai/lib/azure.py,sha256=7f4bJenbTNGQlN0Z8lSktUgjhopkZ3OJYIdpU3LQIHA,38095 +openai/lib/bedrock.py,sha256=k80ExOaJqPPVr4DlzE2iHucl9mIDpw4EhvptFwm_LP0,39266 +openai/lib/live/README.md,sha256=rS_uiuusBzazha-Cv-NQGoXf16L4-ph0wVw_NB_tHr8,2047 +openai/lib/live/__init__.py,sha256=0AkMmG8q91YtO51Rs5O6rVmNNHoTOFRkEh8O5N9qaXQ,335 +openai/lib/live/__pycache__/__init__.cpython-312.pyc,, +openai/lib/live/__pycache__/_listeners.cpython-312.pyc,, +openai/lib/live/__pycache__/_transcript_grouper.cpython-312.pyc,, +openai/lib/live/__pycache__/_transcript_grouping.cpython-312.pyc,, +openai/lib/live/__pycache__/_transcript_state.cpython-312.pyc,, +openai/lib/live/__pycache__/_types.cpython-312.pyc,, +openai/lib/live/_listeners.py,sha256=yC5osYSfzMS8fVALJ7xx9Z6vGF_toYkNSgyHVyCoSMM,4060 +openai/lib/live/_transcript_grouper.py,sha256=ZsePSZd74MLDa2aeD5PNKCqKOFjaymkaPZckj1fXHEg,11824 +openai/lib/live/_transcript_grouping.py,sha256=B49ljzgFZ9pHa3gHEPfPmPUzRNSw8OxPdUDjvhl15k0,12393 +openai/lib/live/_transcript_state.py,sha256=lNxDtJLPVNk3WiNwwIxhwOegDRfk04W_GLfk91Rk2pI,6435 +openai/lib/live/_types.py,sha256=9scai4uCvNT7HNFZ1LOqfQmEF5qcTFSOkj9Gn13pqTs,1444 +openai/lib/responses_websocket/README.md,sha256=xGwBcHBvuFnAVlOwJqlifYV84mANOPJc1KdvLXR6Xgo,16209 +openai/lib/responses_websocket/__init__.py,sha256=Pnf8Uirrd7jH_KAIU0aJTLrZ0O1A9AdPvNXAL-kc9d4,674 +openai/lib/responses_websocket/__pycache__/__init__.cpython-312.pyc,, +openai/lib/responses_websocket/__pycache__/_accumulator.cpython-312.pyc,, +openai/lib/responses_websocket/__pycache__/_session.cpython-312.pyc,, +openai/lib/responses_websocket/_accumulator.py,sha256=zhLvuNb785asLcyN49bsNj_FUo2NQHo2HQt-WI4yrOk,20557 +openai/lib/responses_websocket/_session.py,sha256=utTzycUvD00rrRf0Vh57xhMSXLwQKH6rs-M021B8PtY,26654 +openai/lib/streaming/__init__.py,sha256=ACQCnNbey9Xlf4teIG75kdfBPrZaXtcoIGjqesTyCNU,1089 +openai/lib/streaming/__pycache__/__init__.cpython-312.pyc,, +openai/lib/streaming/__pycache__/_assistants.cpython-312.pyc,, +openai/lib/streaming/__pycache__/_deltas.cpython-312.pyc,, +openai/lib/streaming/_assistants.py,sha256=_irWOdRh9wq9O5lkriWGff8PqiDCXwiXwANcZhLWHXs,39197 +openai/lib/streaming/_deltas.py,sha256=4pbQkHMsYbV_kQwI81JxHnDxiZMlMP1hl1Fnh8TnX1I,3061 +openai/lib/streaming/agents/__init__.py,sha256=E7_MgaF8nAhK8z7hX8Na56jQYoCGhDtinPuTaWaOnUE,226 +openai/lib/streaming/agents/__pycache__/__init__.cpython-312.pyc,, +openai/lib/streaming/agents/__pycache__/_streams.cpython-312.pyc,, +openai/lib/streaming/agents/__pycache__/_tools.cpython-312.pyc,, +openai/lib/streaming/agents/__pycache__/_types.cpython-312.pyc,, +openai/lib/streaming/agents/_streams.py,sha256=2ZRwWB2CTTxFQAs7_UnsZ7S6pNCmPxJgRXI3Xqlk5Os,15186 +openai/lib/streaming/agents/_tools.py,sha256=vtO9C-ecPmXBSHZ7A8uRlu05Jsdz2tZYpA6PLDf6XlQ,2286 +openai/lib/streaming/agents/_types.py,sha256=fBlF8Nc2_OidvYWhU0Yj3-KjGC4cDi3Vrr1beMYofck,471 +openai/lib/streaming/chat/__init__.py,sha256=7krL_atOvvpQkY_byWSglSfDsMs5hdoxHmz4Ulq7lcc,1305 +openai/lib/streaming/chat/__pycache__/__init__.cpython-312.pyc,, +openai/lib/streaming/chat/__pycache__/_completions.cpython-312.pyc,, +openai/lib/streaming/chat/__pycache__/_events.cpython-312.pyc,, +openai/lib/streaming/chat/__pycache__/_types.cpython-312.pyc,, +openai/lib/streaming/chat/_completions.py,sha256=p8mLp-Q7-8qlTKxHCEVSPu9EYjn_BGJMVC4s_zbReh0,31086 +openai/lib/streaming/chat/_events.py,sha256=lstVmM6YR2Cs9drikzrY9JCZn9Nbfym0aKIPtNpxL6w,2618 +openai/lib/streaming/chat/_types.py,sha256=-SYVBNhGkOUoJ-8dotxpCRqPJpfyOQ8hwR2_HrsQCRI,739 +openai/lib/streaming/responses/__init__.py,sha256=MwE1Oc3OIiXjtuRFsuP_k5Ra8pNiqKpc1GZum-8ZRJM,543 +openai/lib/streaming/responses/__pycache__/__init__.cpython-312.pyc,, +openai/lib/streaming/responses/__pycache__/_events.cpython-312.pyc,, +openai/lib/streaming/responses/__pycache__/_responses.cpython-312.pyc,, +openai/lib/streaming/responses/__pycache__/_types.cpython-312.pyc,, +openai/lib/streaming/responses/_events.py,sha256=izFv3Bpz2Ub84bgSzShTPIJt8jssDnM9vNmyDdYmL_0,6098 +openai/lib/streaming/responses/_responses.py,sha256=ubdpOhgvNyvL926Ak3x6nWcQPXPObCdiRjzxnKYnWwo,15129 +openai/lib/streaming/responses/_types.py,sha256=msq1KWj3e3BLn7NKu5j2kzHgj9kShuoitgXEyTmQxus,276 +openai/pagination.py,sha256=TMfw7o3pprQo3PbdNxghxnrNXUfgHoX_JfYFBjXfTls,7555 +openai/providers/__init__.py,sha256=0Kvjpkej3dErLhPAtW0iCr3y_4ujzAfmNrMvoLoIwsM,63 +openai/providers/__pycache__/__init__.cpython-312.pyc,, +openai/providers/__pycache__/bedrock.cpython-312.pyc,, +openai/providers/bedrock.py,sha256=CVnxGu7LVpJIK3yp61UMTDPRwLzbVS4ffqlRYKkq_BY,20690 +openai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +openai/resources/__init__.py,sha256=yIh_e0vr-nyoaz6p_OWHfkjF_zm15SBEWGHrxmizZAM,21866 +openai/resources/__pycache__/__init__.cpython-312.pyc,, +openai/resources/__pycache__/batches.cpython-312.pyc,, +openai/resources/__pycache__/completions.cpython-312.pyc,, +openai/resources/__pycache__/content_provenance_checks.cpython-312.pyc,, +openai/resources/__pycache__/embeddings.cpython-312.pyc,, +openai/resources/__pycache__/files.cpython-312.pyc,, +openai/resources/__pycache__/images.cpython-312.pyc,, +openai/resources/__pycache__/models.cpython-312.pyc,, +openai/resources/__pycache__/moderations.cpython-312.pyc,, +openai/resources/__pycache__/videos.cpython-312.pyc,, +openai/resources/admin/__init__.py,sha256=McYwt8pjK9iXR72fH3wQuiZLMGsB_DjToyLKcm83zkI,2924 +openai/resources/admin/__pycache__/__init__.cpython-312.pyc,, +openai/resources/admin/__pycache__/admin.cpython-312.pyc,, +openai/resources/admin/admin.py,sha256=fImNJ5RWpFNLpp1ruuG3tG1wTTQAdjCl3IVTNPiVlAc,3429 +openai/resources/admin/organization/__init__.py,sha256=A0pufax3HdBrTQdCQCnyJGXpJU5owXKptmLjubbMXmE,16190 +openai/resources/admin/organization/__pycache__/__init__.cpython-312.pyc,, +openai/resources/admin/organization/__pycache__/admin_api_keys.cpython-312.pyc,, +openai/resources/admin/organization/__pycache__/audit_logs.cpython-312.pyc,, +openai/resources/admin/organization/__pycache__/certificates.cpython-312.pyc,, +openai/resources/admin/organization/__pycache__/data_retention.cpython-312.pyc,, +openai/resources/admin/organization/__pycache__/external_storage.cpython-312.pyc,, +openai/resources/admin/organization/__pycache__/invites.cpython-312.pyc,, +openai/resources/admin/organization/__pycache__/organization.cpython-312.pyc,, +openai/resources/admin/organization/__pycache__/roles.cpython-312.pyc,, +openai/resources/admin/organization/__pycache__/spend_alerts.cpython-312.pyc,, +openai/resources/admin/organization/__pycache__/spend_limit.cpython-312.pyc,, +openai/resources/admin/organization/__pycache__/usage.cpython-312.pyc,, +openai/resources/admin/organization/admin_api_keys.py,sha256=_9tsVdmm3JYMPGeUfDR5FFbOxWKw48H6dS6dcrs7u78,18497 +openai/resources/admin/organization/audit_logs.py,sha256=bFtZ-13vJJZjHJFYH3mUDnC34T5aTxYBQa8R9oRJdqU,27014 +openai/resources/admin/organization/certificates.py,sha256=tBF-clU8RwDVNBd3dJ0sw6FGh_eeQREu3-xR7D_Nehw,32633 +openai/resources/admin/organization/data_retention.py,sha256=kXMnaWI0hAQBXgTv6wDJVRRZv3zsH_5YkPFk0adu6ZQ,9420 +openai/resources/admin/organization/external_storage.py,sha256=PaJMnFLI45zdbYyoZhk-pIefu0uGXVvBtqPxaw-9YMo,23326 +openai/resources/admin/organization/groups/__init__.py,sha256=Iv6REi7b3sdsxZ4yoMhEQrBqIK-ZgUFkLDtrAb2xrbw,3629 +openai/resources/admin/organization/groups/__pycache__/__init__.cpython-312.pyc,, +openai/resources/admin/organization/groups/__pycache__/groups.cpython-312.pyc,, +openai/resources/admin/organization/groups/__pycache__/roles.cpython-312.pyc,, +openai/resources/admin/organization/groups/__pycache__/users.cpython-312.pyc,, +openai/resources/admin/organization/groups/groups.py,sha256=vUWZviqR0Iy5R1oyTt2W4a8MQQLXYuIS8FVjk1Vdhxo,23158 +openai/resources/admin/organization/groups/roles.py,sha256=hI0lhLr--W6PWdfhGfOb5KFRy8IpB788iWHOV670Bjk,19305 +openai/resources/admin/organization/groups/users.py,sha256=tq0LFQvYB-xgfGktA9oZ2-U_l7ZacFbsdxu1A8P_49Y,19196 +openai/resources/admin/organization/invites.py,sha256=lJTzkLx1j30xVjjnyznbJqspoXkdyttriUhiKkLKzbs,19149 +openai/resources/admin/organization/organization.py,sha256=8Di0Pw8bAgBHaC2GS0l4LqH7dsXUiN72m5BAeqS-gaU,16647 +openai/resources/admin/organization/projects/__init__.py,sha256=bOfwFgdAb4gXI4aXheGVBrHfOLXhT4eUOhT3Tf6pYMI,15945 +openai/resources/admin/organization/projects/__pycache__/__init__.cpython-312.pyc,, +openai/resources/admin/organization/projects/__pycache__/api_keys.cpython-312.pyc,, +openai/resources/admin/organization/projects/__pycache__/certificates.cpython-312.pyc,, +openai/resources/admin/organization/projects/__pycache__/data_retention.cpython-312.pyc,, +openai/resources/admin/organization/projects/__pycache__/hosted_tool_permissions.cpython-312.pyc,, +openai/resources/admin/organization/projects/__pycache__/model_permissions.cpython-312.pyc,, +openai/resources/admin/organization/projects/__pycache__/projects.cpython-312.pyc,, +openai/resources/admin/organization/projects/__pycache__/rate_limits.cpython-312.pyc,, +openai/resources/admin/organization/projects/__pycache__/roles.cpython-312.pyc,, +openai/resources/admin/organization/projects/__pycache__/spend_alerts.cpython-312.pyc,, +openai/resources/admin/organization/projects/__pycache__/spend_limit.cpython-312.pyc,, +openai/resources/admin/organization/projects/api_keys.py,sha256=0LpreRQk4XCXXXBP9we_P13__ViEuMhswabfre-f9n4,17297 +openai/resources/admin/organization/projects/certificates.py,sha256=3MpgY9Wbe6Bsnmc7HsmttW_hdp5PnaSiI4so8ogsgWk,17809 +openai/resources/admin/organization/projects/data_retention.py,sha256=tybvXRJmOKwiZQtkWwVNj4zqZfVsY22xEfhW3V6xIds,10978 +openai/resources/admin/organization/projects/groups/__init__.py,sha256=rXcojwo6UaLuGRntdFfxv_xPxzLxzw0xeXbo5zgySqk,2696 +openai/resources/admin/organization/projects/groups/__pycache__/__init__.cpython-312.pyc,, +openai/resources/admin/organization/projects/groups/__pycache__/groups.cpython-312.pyc,, +openai/resources/admin/organization/projects/groups/__pycache__/roles.cpython-312.pyc,, +openai/resources/admin/organization/projects/groups/groups.py,sha256=QI5rRX6cOT_NhZaDkwj6xmE0SFafyxbvHg9XHUAxlvk,21012 +openai/resources/admin/organization/projects/groups/roles.py,sha256=52biWQxCDjgFhoQ5d_roMwTzERadn8ojRmvoWMvqpWk,21073 +openai/resources/admin/organization/projects/hosted_tool_permissions.py,sha256=Z7GmhNkmMpMI3RMt-nZk0LkOH_dzeHSb5YPukSrn5k8,12847 +openai/resources/admin/organization/projects/model_permissions.py,sha256=qhlvFbrHYQgplLyE1ReotImRefFcAta9j_AccBEqDDc,14662 +openai/resources/admin/organization/projects/projects.py,sha256=VnnkIFHiBQrktRN1hBSTVkZPwAKA6RP3ctdkjumS6AE,39074 +openai/resources/admin/organization/projects/rate_limits.py,sha256=VcI_fdj_euMf-P0Y_TtK4ouNGRmIhqlHKseFpQeo0vY,16313 +openai/resources/admin/organization/projects/roles.py,sha256=v-6q20Af5Mt1RL0i7aby0w2RNgUCiLIA4BmhF5Qcq-4,24276 +openai/resources/admin/organization/projects/service_accounts/__init__.py,sha256=MOv74r24_g307ouWlkazIcO2H9G-W-sbZVPbhux2_Bg,3130 +openai/resources/admin/organization/projects/service_accounts/__pycache__/__init__.cpython-312.pyc,, +openai/resources/admin/organization/projects/service_accounts/__pycache__/api_keys.cpython-312.pyc,, +openai/resources/admin/organization/projects/service_accounts/__pycache__/service_accounts.cpython-312.pyc,, +openai/resources/admin/organization/projects/service_accounts/api_keys.py,sha256=_aX6iKkDCnLNutK-m9Z2Vr9lEW7B5JGRESingMAGdjs,8419 +openai/resources/admin/organization/projects/service_accounts/service_accounts.py,sha256=ueMSJrZkdUpXluQPEObIqRTGYxB82KJgqsSQis2uFQw,29072 +openai/resources/admin/organization/projects/spend_alerts.py,sha256=GflkJWm7BV2kYFj4tfMoB7c3ngoIYjPIqN3-UVLP6t0,26851 +openai/resources/admin/organization/projects/spend_limit.py,sha256=ZwN2DnuQ6V69vnip6DreE8puLYyyXfJAOI23e6XzgvA,14711 +openai/resources/admin/organization/projects/users/__init__.py,sha256=qWnuQAsZw9hscqveOM8Fdnwb_t5W5gq77c_6WDY4ah0,2658 +openai/resources/admin/organization/projects/users/__pycache__/__init__.cpython-312.pyc,, +openai/resources/admin/organization/projects/users/__pycache__/roles.cpython-312.pyc,, +openai/resources/admin/organization/projects/users/__pycache__/users.cpython-312.pyc,, +openai/resources/admin/organization/projects/users/roles.py,sha256=48ALQbKg5OS3pHv6QnnAnUMcGMOMxpLbUaVYYloamJQ,20996 +openai/resources/admin/organization/projects/users/users.py,sha256=ZGyV7HUjAIpFZEtGTZBdbYQb-HhdqoUOzHXhvbgDyks,25246 +openai/resources/admin/organization/roles.py,sha256=sfWVBd5PAORsMvdyE1lmfuT65I-wssTqnFjiho6Gww4,22361 +openai/resources/admin/organization/spend_alerts.py,sha256=XaTW4g45FjDudT7HPe8kiDbj6mRw4wFspKcFArrZ0no,24729 +openai/resources/admin/organization/spend_limit.py,sha256=0vmfsP2ZD-IlDEw_nZRkgLYHpLsv6ITLkRew9Hnv2V8,12247 +openai/resources/admin/organization/usage.py,sha256=9ujb0n5moqUlsdNOYDjXnPGA9S1MuxEj-wUYlyNJ9Lc,87093 +openai/resources/admin/organization/users/__init__.py,sha256=qWnuQAsZw9hscqveOM8Fdnwb_t5W5gq77c_6WDY4ah0,2658 +openai/resources/admin/organization/users/__pycache__/__init__.cpython-312.pyc,, +openai/resources/admin/organization/users/__pycache__/roles.cpython-312.pyc,, +openai/resources/admin/organization/users/__pycache__/users.cpython-312.pyc,, +openai/resources/admin/organization/users/roles.py,sha256=yzOZ9i1J06x0IGEatBMHZJX6dvEz081796Fs5ayA7vM,19228 +openai/resources/admin/organization/users/users.py,sha256=RqatPRGbegKSUq4I0HDpFnalomtzAWJY5yQJ3uwFLQE,20221 +openai/resources/audio/__init__.py,sha256=83m5cO9w0RkvqX6PtB5AGN1gi-Mmxu1k0iYduJRJjIs,5170 +openai/resources/audio/__pycache__/__init__.cpython-312.pyc,, +openai/resources/audio/__pycache__/audio.cpython-312.pyc,, +openai/resources/audio/__pycache__/speech.cpython-312.pyc,, +openai/resources/audio/__pycache__/transcriptions.cpython-312.pyc,, +openai/resources/audio/__pycache__/translations.cpython-312.pyc,, +openai/resources/audio/audio.py,sha256=KK6-POMJAvszg7o04xJc5VBIDLsrx_Tjyma5hxc9oH4,6480 +openai/resources/audio/speech.py,sha256=ZMQ7Hn0jMacXrSy8Bmh5_PBjW9NJbkXO7-ZfvCaTwts,10840 +openai/resources/audio/transcriptions.py,sha256=PIQG20BSK_Eei83He80G2aEqfw9t2lePonZefubUK3Y,58008 +openai/resources/audio/translations.py,sha256=-SHlvj2-Xy3uQOhfAqpAhQ-rjNDbQB_bA0IYCsCWqsA,15789 +openai/resources/batches.py,sha256=M3lZ0bLTbys8C-moNj9HQ834jkrGgCelH-sflO72aSU,22495 +openai/resources/beta/__init__.py,sha256=BhH9RGSs_egQCbWOQjYo7DIYiQdynn3l81Y9anYYdZo,6884 +openai/resources/beta/__pycache__/__init__.cpython-312.pyc,, +openai/resources/beta/__pycache__/assistants.cpython-312.pyc,, +openai/resources/beta/__pycache__/beta.cpython-312.pyc,, +openai/resources/beta/agents/__init__.py,sha256=Ejw5HQuCrjIcvBeQwf8vyeie2nMYM6Epg0bQ4_iyMdk,4980 +openai/resources/beta/agents/__pycache__/__init__.cpython-312.pyc,, +openai/resources/beta/agents/__pycache__/agents.cpython-312.pyc,, +openai/resources/beta/agents/agents.py,sha256=8p26o4vKnFqu4KAlv4-wWw_1T7sFEsfyGLkd6bRWxsA,34790 +openai/resources/beta/agents/environments/__init__.py,sha256=xrnFSQAzXB7TE3xDf_wZ-m9U1BjbeT0uc2Z-QJ9NE_s,4009 +openai/resources/beta/agents/environments/__pycache__/__init__.cpython-312.pyc,, +openai/resources/beta/agents/environments/__pycache__/environments.cpython-312.pyc,, +openai/resources/beta/agents/environments/__pycache__/files.cpython-312.pyc,, +openai/resources/beta/agents/environments/__pycache__/templates.cpython-312.pyc,, +openai/resources/beta/agents/environments/environments.py,sha256=1a1ahQ5cTyAXXWSdIiWtHnAMMwtsPXKJQr-gSWw_fa0,8770 +openai/resources/beta/agents/environments/files.py,sha256=Ti-eimXSfEkN0xOsOOaXa8dNu80SqyxQJlFxWsPhtbk,18107 +openai/resources/beta/agents/environments/templates.py,sha256=AOS2GQSF89sJpKHBvjdfeV3xE-hytpMCbBMpSG5sWGM,33115 +openai/resources/beta/agents/sessions/__init__.py,sha256=E3_-V5mJSRnn0EU4jlAUKMlR632RpCyrUB3-Fbxs2is,6846 +openai/resources/beta/agents/sessions/__pycache__/__init__.cpython-312.pyc,, +openai/resources/beta/agents/sessions/__pycache__/artifacts.cpython-312.pyc,, +openai/resources/beta/agents/sessions/__pycache__/events.cpython-312.pyc,, +openai/resources/beta/agents/sessions/__pycache__/items.cpython-312.pyc,, +openai/resources/beta/agents/sessions/__pycache__/sessions.cpython-312.pyc,, +openai/resources/beta/agents/sessions/__pycache__/turns.cpython-312.pyc,, +openai/resources/beta/agents/sessions/artifacts.py,sha256=h6H3OB2YCSwXEeBbWIXEHorLOByIMg-A-jFD9RhnbAs,22700 +openai/resources/beta/agents/sessions/events.py,sha256=PpbxNVCo5W6tQOxILZ48Glv0QNDDYd-zj_CjPq4CdPI,12386 +openai/resources/beta/agents/sessions/items.py,sha256=to91MlM4f2GmEd6lwYedltaBEB5J_Z8R0GPuozLX0hM,8680 +openai/resources/beta/agents/sessions/sessions.py,sha256=w8bfxhHoNVlj0g1jLhKM6kGWU0G349zE1tvJwW05Ir0,48986 +openai/resources/beta/agents/sessions/subagents/__init__.py,sha256=CcYpc5U9q5XBQc6CnlSHI15Jk-UlBKLa3o8JxkaOxlw,3743 +openai/resources/beta/agents/sessions/subagents/__pycache__/__init__.cpython-312.pyc,, +openai/resources/beta/agents/sessions/subagents/__pycache__/items.cpython-312.pyc,, +openai/resources/beta/agents/sessions/subagents/__pycache__/subagents.cpython-312.pyc,, +openai/resources/beta/agents/sessions/subagents/items.py,sha256=96PDcUlzobLEBrugSi15av4hAo6l7yNDIM4kJIiCyiY,9064 +openai/resources/beta/agents/sessions/subagents/subagents.py,sha256=2bEGr4VfuxuDGiSBWkA9xM_cWPaaAvBlvWSv_Mvzg60,14423 +openai/resources/beta/agents/sessions/subagents/turns/__init__.py,sha256=QksqFdWZclKClmBbzq_qPPj5PDsSOzjEX6MChu96JeA,2658 +openai/resources/beta/agents/sessions/subagents/turns/__pycache__/__init__.cpython-312.pyc,, +openai/resources/beta/agents/sessions/subagents/turns/__pycache__/items.cpython-312.pyc,, +openai/resources/beta/agents/sessions/subagents/turns/__pycache__/turns.cpython-312.pyc,, +openai/resources/beta/agents/sessions/subagents/turns/items.py,sha256=pFXTADiBUwPegDw_pd5wADPhe5pldJDiaeBSEr-XS6o,9458 +openai/resources/beta/agents/sessions/subagents/turns/turns.py,sha256=4G54R8jg_SbUfYDVowYtmoa5VlylD8Ot3ptzw-H6B1g,14277 +openai/resources/beta/agents/sessions/turns.py,sha256=otgyq8_QWRF933cDTcms2Mw9GKd6ZtS4jvBdVwO3Gpg,12557 +openai/resources/beta/agents/vaults/__init__.py,sha256=lXcpdWupdwu3LldmjFf7_hoVlkvWu33GWIASWohz55Q,2924 +openai/resources/beta/agents/vaults/__pycache__/__init__.cpython-312.pyc,, +openai/resources/beta/agents/vaults/__pycache__/credentials.cpython-312.pyc,, +openai/resources/beta/agents/vaults/__pycache__/vaults.cpython-312.pyc,, +openai/resources/beta/agents/vaults/credentials.py,sha256=ZnfwlQnzKTqXpdDi49_kRutravCoP9cdVHucVfDdTck,28600 +openai/resources/beta/agents/vaults/vaults.py,sha256=F5F1SG-zNznirOmBhzSYMMg_bdhCc4TEIHga7vNyyG4,21215 +openai/resources/beta/assistants.py,sha256=M52VadaBE9brUMZdGSwYHWw6-cFVkpcXKnJF1FG9_YY,50241 +openai/resources/beta/beta.py,sha256=NJA-vEO7Toi8U_GVXkXjbpU7UX7pdKpiz94unhXDhrA,8760 +openai/resources/beta/chatkit/__init__.py,sha256=mEulS563e998i_mF6_5YwkOsa1osWiIqB0C71rWTVMU,3857 +openai/resources/beta/chatkit/__pycache__/__init__.cpython-312.pyc,, +openai/resources/beta/chatkit/__pycache__/chatkit.cpython-312.pyc,, +openai/resources/beta/chatkit/__pycache__/sessions.cpython-312.pyc,, +openai/resources/beta/chatkit/__pycache__/threads.cpython-312.pyc,, +openai/resources/beta/chatkit/chatkit.py,sha256=9xQ71h5UCVnw4inUCT0TuE6qi8JWo9YwwTz08xvJG8g,4332 +openai/resources/beta/chatkit/sessions.py,sha256=151GqYsN64vSwx4WTN7aPudvyNvqEKbWZrA52DF070U,12568 +openai/resources/beta/chatkit/threads.py,sha256=7qJMal9vdDoSKBDVLM2DlyU7ZP07yRZRH7XeRug-usI,21140 +openai/resources/beta/realtime/__init__.py,sha256=VJFYLWGLYTUA1pnfzh1hSTfGHdmYGNtxLPCxpNGzIHE,1325 +openai/resources/beta/realtime/__pycache__/__init__.cpython-312.pyc,, +openai/resources/beta/realtime/__pycache__/realtime.cpython-312.pyc,, +openai/resources/beta/realtime/__pycache__/sessions.cpython-312.pyc,, +openai/resources/beta/realtime/__pycache__/transcription_sessions.cpython-312.pyc,, +openai/resources/beta/realtime/realtime.py,sha256=ySU8ZGXG4zoiJCxcJOvwTLobHkK0JvqQgTqPgxWd1eU,43774 +openai/resources/beta/realtime/sessions.py,sha256=n-bbc6IXpUuJHAfxsxPSFTTu-9ZCVYOKw0gV4tSlLT8,22014 +openai/resources/beta/realtime/transcription_sessions.py,sha256=NkWwUhIVHgQHadrbMUhzUHzt5R0K2tTFPCt379fXk0I,14040 +openai/resources/beta/responses/__init__.py,sha256=cANO8WmvLNGZGqrtJvtWXSotWltrFDCacqsfSD8KuhU,4177 +openai/resources/beta/responses/__pycache__/__init__.cpython-312.pyc,, +openai/resources/beta/responses/__pycache__/input_items.cpython-312.pyc,, +openai/resources/beta/responses/__pycache__/input_tokens.cpython-312.pyc,, +openai/resources/beta/responses/__pycache__/responses.cpython-312.pyc,, +openai/resources/beta/responses/input_items.py,sha256=OCAyeSNIv-sCWgLqWpY9AnrHnGmty8lhAVvxy_Ze0Jg,9684 +openai/resources/beta/responses/input_tokens.py,sha256=0vjQK55SLnmie90aaoM9l9R6fZy6NQs1YXyMMjOJbF4,16157 +openai/resources/beta/responses/responses.py,sha256=VA_YVXm05RDKJ5KpuAxErYoMBauoFI2FFvYHMkDgOmY,272529 +openai/resources/beta/threads/__init__.py,sha256=b5A0Ozl1qkbI_-TqN1ENAzbPkwgC5njIyZQKpQeRs5U,3743 +openai/resources/beta/threads/__pycache__/__init__.cpython-312.pyc,, +openai/resources/beta/threads/__pycache__/messages.cpython-312.pyc,, +openai/resources/beta/threads/__pycache__/threads.cpython-312.pyc,, +openai/resources/beta/threads/messages.py,sha256=fOpMDW9e5JgzORPmuurvDqw63JOBuc40-r_x7-Sxz64,32294 +openai/resources/beta/threads/runs/__init__.py,sha256=MizxxAK94IGpnhUY8IHSCN59XNUXnRSj0zbVRapXBqs,2620 +openai/resources/beta/threads/runs/__pycache__/__init__.cpython-312.pyc,, +openai/resources/beta/threads/runs/__pycache__/runs.cpython-312.pyc,, +openai/resources/beta/threads/runs/__pycache__/steps.cpython-312.pyc,, +openai/resources/beta/threads/runs/runs.py,sha256=4fCN8v8Gwl1CnNjpX6Dj-Yql46Et21_PrFn7sZz0dxU,155958 +openai/resources/beta/threads/runs/steps.py,sha256=gvDVdkjBit82-VQQ0QIeeRsSGVVJ1hLnvx-ULlNUXt4,17684 +openai/resources/beta/threads/threads.py,sha256=UINmRbPvXvJStMkMZDpp3Np3aHPRmAr8bkAFJG2wgJA,100278 +openai/resources/chat/__init__.py,sha256=i4np1YjOdsvQOpWx2xRZECNxJ2eVOqAUxOc_b0rkffo,2848 +openai/resources/chat/__pycache__/__init__.cpython-312.pyc,, +openai/resources/chat/__pycache__/chat.cpython-312.pyc,, +openai/resources/chat/chat.py,sha256=HIszXOIdErEMlVvH6xLUr-fHgITkJEwLtLySl_s07wE,4071 +openai/resources/chat/completions/__init__.py,sha256=WRlMDduVSFPcRvzwI--XYIeOg_o8KJBfT5hchrcfxhM,3000 +openai/resources/chat/completions/__pycache__/__init__.cpython-312.pyc,, +openai/resources/chat/completions/__pycache__/completions.cpython-312.pyc,, +openai/resources/chat/completions/__pycache__/messages.cpython-312.pyc,, +openai/resources/chat/completions/completions.py,sha256=wvklCyTmZ_gqX05AlnsO2sdUvb7d2EYntbKa_kkfYbw,184166 +openai/resources/chat/completions/messages.py,sha256=Jb2DcZCF2n9tu-9TcYIpqd-gVxwg25SYb6ele5nK3TU,8423 +openai/resources/completions.py,sha256=3oPMougDHPETMVntzRpANAh2ZrS2uupvBfWYkpWs_QI,61004 +openai/resources/containers/__init__.py,sha256=nIlsxKL4MsU4xa6k9K2KmFWAcNMbqDlhVAyQkRo_A3A,2848 +openai/resources/containers/__pycache__/__init__.cpython-312.pyc,, +openai/resources/containers/__pycache__/containers.cpython-312.pyc,, +openai/resources/containers/containers.py,sha256=pJZXunJAEIQeIAQ0xkdF7Km_ZB8crOJsIWndcLhujaM,21561 +openai/resources/containers/files/__init__.py,sha256=EKQwLYMaXvOuWfIMSrf0cIU8fJUMeYp96j7Vhe2rbTk,2734 +openai/resources/containers/files/__pycache__/__init__.cpython-312.pyc,, +openai/resources/containers/files/__pycache__/content.cpython-312.pyc,, +openai/resources/containers/files/__pycache__/files.cpython-312.pyc,, +openai/resources/containers/files/content.py,sha256=nihWB5whoFCRL7KUW1K4A0gCNJaElxKW_T8XpdKwYIY,6900 +openai/resources/containers/files/files.py,sha256=z2a1qePI0yM1cH3bR4goDBoRe8BRNRuFJClKfquCrUE,22288 +openai/resources/content_provenance_checks.py,sha256=fM02W2dj6xiDJa3DWggXoQdvIdSfSUr8xcSkw124c_0,9166 +openai/resources/conversations/__init__.py,sha256=EXjRqHQNodxcU9h6xxO-SlRg43NhMmIDWdX1lg1CSXo,2962 +openai/resources/conversations/__pycache__/__init__.cpython-312.pyc,, +openai/resources/conversations/__pycache__/conversations.cpython-312.pyc,, +openai/resources/conversations/__pycache__/items.cpython-312.pyc,, +openai/resources/conversations/api.md,sha256=aja7OOHpcYKeuoZV_lBWBT950-4tdVVOf_M2GwRXv3k,3047 +openai/resources/conversations/conversations.py,sha256=cnpCNXzEvBeYgYRoxb5TjhscarzexKKex8dX9K_y2vU,20696 +openai/resources/conversations/items.py,sha256=zwT3YUXAy6SFfDMdqaFR-mw2RjgU5RHTqDRDtGEQETI,25742 +openai/resources/embeddings.py,sha256=LWeGjBGTPqPKFImhanxz1x71PgGqFb_4PlaM9FaJ1ag,11163 +openai/resources/evals/__init__.py,sha256=CmZZdiUsVMhMMSzoqpDJ4UA_pT0OrzwUqNIN0SWPb54,2620 +openai/resources/evals/__pycache__/__init__.cpython-312.pyc,, +openai/resources/evals/__pycache__/evals.cpython-312.pyc,, +openai/resources/evals/evals.py,sha256=qr6Ytp42xNGiQnpb2_AAB1V5w5UOzh3vPuPyb50uIZ4,27465 +openai/resources/evals/runs/__init__.py,sha256=mtbjErEfsZs1obRF4NGA4dkhL52ocNF7nYHI2CmcVXA,2856 +openai/resources/evals/runs/__pycache__/__init__.cpython-312.pyc,, +openai/resources/evals/runs/__pycache__/output_items.cpython-312.pyc,, +openai/resources/evals/runs/__pycache__/runs.cpython-312.pyc,, +openai/resources/evals/runs/output_items.py,sha256=5Mbl0CHAO7KeIaNSn5-SCyzbPotayE-KZJ5kmMLpTC8,13355 +openai/resources/evals/runs/runs.py,sha256=PtP9XmI1kq2xmA2sQEEOkHTY1WFItD9UhzMeOj0jBZk,26122 +openai/resources/files.py,sha256=lMEoj5Q08nKEt1GFXXMK-yy874xCuKjnz10Q8P7Z2pU,33111 +openai/resources/fine_tuning/__init__.py,sha256=BtA3DKoPsuyM25AoZjDGLtER47PzZkbQCYZ_7lPDJgQ,4912 +openai/resources/fine_tuning/__pycache__/__init__.cpython-312.pyc,, +openai/resources/fine_tuning/__pycache__/fine_tuning.cpython-312.pyc,, +openai/resources/fine_tuning/alpha/__init__.py,sha256=NEVQVPQzdtoA4iUH9_OznAeJPnsXQT9tvw6MNEA3l_M,2734 +openai/resources/fine_tuning/alpha/__pycache__/__init__.cpython-312.pyc,, +openai/resources/fine_tuning/alpha/__pycache__/alpha.cpython-312.pyc,, +openai/resources/fine_tuning/alpha/__pycache__/graders.cpython-312.pyc,, +openai/resources/fine_tuning/alpha/alpha.py,sha256=t3due3Iz7NOVKKjU3wG3Ppnx1PlblTGaLkALuCET1YE,3801 +openai/resources/fine_tuning/alpha/graders.py,sha256=PxMFW5kRqgUoDNKz-o6GfpcDaPvflj7OWEc1q4_ULvY,11372 +openai/resources/fine_tuning/checkpoints/__init__.py,sha256=44830uBQ5PSE3j9ojZNvY_IlV4ayZLoFQLyiGORPWjU,3114 +openai/resources/fine_tuning/checkpoints/__pycache__/__init__.cpython-312.pyc,, +openai/resources/fine_tuning/checkpoints/__pycache__/checkpoints.cpython-312.pyc,, +openai/resources/fine_tuning/checkpoints/__pycache__/permissions.cpython-312.pyc,, +openai/resources/fine_tuning/checkpoints/checkpoints.py,sha256=SgYsEtFWiTyHQpWfh8fkos4YN-id3Ybe1TWIocdUs0w,4133 +openai/resources/fine_tuning/checkpoints/permissions.py,sha256=0Z52Ye-fTxtPro1EioVPFEWqCPzjcEXReWVNYi7VLDI,26141 +openai/resources/fine_tuning/fine_tuning.py,sha256=Xy73aIntkKyyZ3AiaeNjdF_r6aYByOAia9yt9SyK8lg,5937 +openai/resources/fine_tuning/jobs/__init__.py,sha256=J3c4dVyYRUefQ55SNHSDcEWe4C0kG-fmtrfPio5KaRY,2848 +openai/resources/fine_tuning/jobs/__pycache__/__init__.cpython-312.pyc,, +openai/resources/fine_tuning/jobs/__pycache__/checkpoints.cpython-312.pyc,, +openai/resources/fine_tuning/jobs/__pycache__/jobs.cpython-312.pyc,, +openai/resources/fine_tuning/jobs/checkpoints.py,sha256=5PJ62DY5kI9iSwtn7zLmwv5qqo7M2MrIdmAFyI9uIv0,7818 +openai/resources/fine_tuning/jobs/jobs.py,sha256=nxFqW_6DkQ4yu4cnChR71vhRhJs-MONQZFG-RhMigL0,39617 +openai/resources/images.py,sha256=81HvIcleqqCLniiqkwICKYwMdVa2mW6_W4NXU0BnQQ8,115562 +openai/resources/live/__init__.py,sha256=4pjoqVy4A0oFttjIcbmHkVijsWREGsmVQ967xKD5Dt8,3222 +openai/resources/live/__pycache__/__init__.cpython-312.pyc,, +openai/resources/live/__pycache__/forks.cpython-312.pyc,, +openai/resources/live/__pycache__/live.cpython-312.pyc,, +openai/resources/live/__pycache__/sessions.cpython-312.pyc,, +openai/resources/live/__pycache__/sideband.cpython-312.pyc,, +openai/resources/live/api.md,sha256=Qbb0zfzPmXvEqRHJJ7GED1hPNBIxm0miDxQurJoaK4o,3218 +openai/resources/live/forks.py,sha256=KhDkYH2GAKAJZyRizWztSVQlZkh2kBw7elYOTv31-MQ,56107 +openai/resources/live/live.py,sha256=ZO29fuiN7Jkag-zaJnDFEaFu4h4HgZeg05daAQFAqzU,62915 +openai/resources/live/sessions.py,sha256=m6LVnZx0mMlqwufkhmDJFGjg7rXfiB538SPuN5hcPf0,27996 +openai/resources/live/sideband.py,sha256=f3I0Dc2kfWR6uXFADqREGWy7ml4I0FhFiuYf50CfRnA,54980 +openai/resources/models.py,sha256=RJLE5F1PzaozpTukYqovow5LNnYv8Xux4XSBP82NTAs,12090 +openai/resources/moderations.py,sha256=TYzFphzb56Kr0_lYIriItMgU6e7hIbEQHvT0W54AUFA,8266 +openai/resources/realtime/__init__.py,sha256=oNP3ZDAccon5BX64t748_KmqUKWd-Po0UoB_8nsrkJU,4017 +openai/resources/realtime/__pycache__/__init__.cpython-312.pyc,, +openai/resources/realtime/__pycache__/calls.cpython-312.pyc,, +openai/resources/realtime/__pycache__/client_secrets.cpython-312.pyc,, +openai/resources/realtime/__pycache__/realtime.cpython-312.pyc,, +openai/resources/realtime/api.md,sha256=jCQCFQYeu9ssYw0wCLx-NLljv2voAA_LUZr_TncYMXE,5055 +openai/resources/realtime/calls.py,sha256=7D9Y5hY-xTrjXPr69CUbSYdGSW5OJqa5WVCXGvmsY-8,37941 +openai/resources/realtime/client_secrets.py,sha256=m1mZTY-yzzrt9xhoFHq55nXVRDimUpwbczHpnvvdkc4,9470 +openai/resources/realtime/realtime.py,sha256=pzO3xMu2rNJddnNeI9YCotrnZlu2-Gh1vCfuqzTQES4,73099 +openai/resources/responses/__init__.py,sha256=cANO8WmvLNGZGqrtJvtWXSotWltrFDCacqsfSD8KuhU,4177 +openai/resources/responses/__pycache__/__init__.cpython-312.pyc,, +openai/resources/responses/__pycache__/input_items.cpython-312.pyc,, +openai/resources/responses/__pycache__/input_tokens.cpython-312.pyc,, +openai/resources/responses/__pycache__/responses.cpython-312.pyc,, +openai/resources/responses/api.md,sha256=JCImBAxPc3GG8zaQ7RBwgJNImkcyn4ZgY-d27dKSzIo,7392 +openai/resources/responses/input_items.py,sha256=L9WSWfx-xpwjcfRtcabD5_33y-lnP-_tDgNdDbBRNtQ,9057 +openai/resources/responses/input_tokens.py,sha256=tl6U9X9p6oCJ4GGIXdqSJfSiApx9_kShQ9VqZ0IJOh4,15544 +openai/resources/responses/responses.py,sha256=0KA_ukr1SjrSLiMYKTt9PtMrrreDKYmgTR8RgKnF1JA,264482 +openai/resources/safety/__init__.py,sha256=g7zHrW1UbCuriarUQAVHBRBro83tXYRAQBi33bHcTpw,3667 +openai/resources/safety/__pycache__/__init__.cpython-312.pyc,, +openai/resources/safety/__pycache__/alerts.cpython-312.pyc,, +openai/resources/safety/__pycache__/cases.cpython-312.pyc,, +openai/resources/safety/__pycache__/safety.cpython-312.pyc,, +openai/resources/safety/alerts.py,sha256=PkRVp2PE7wEsOSGftLN6cneH0MYvRV81_0UoJJinva0,6001 +openai/resources/safety/cases.py,sha256=B9Hl8QqBLPyYmPsmMK9LlEMCX-ao1QjW8PHN-8i_1u0,5863 +openai/resources/safety/safety.py,sha256=gMs4dq4gsuOG6I4Q7sEGEeHZbQqO4bwP8ju4WYR5kNM,4174 +openai/resources/skills/__init__.py,sha256=gDeV0jYanHWmLF4YFgPkMQxLOiYyab0xal8O-MbXE7M,3819 +openai/resources/skills/__pycache__/__init__.cpython-312.pyc,, +openai/resources/skills/__pycache__/content.cpython-312.pyc,, +openai/resources/skills/__pycache__/skills.cpython-312.pyc,, +openai/resources/skills/content.py,sha256=Fsvw5R9XdyZvmLW3hWD8NgbbJ5SjY85NcotK14s-808,6427 +openai/resources/skills/skills.py,sha256=t0lufxLGuwmyUPnZXI6W7qcxsccPR2nE0mBEhnTXs3Q,23833 +openai/resources/skills/versions/__init__.py,sha256=ABYy8jR9wgoPKqTnnLGEWTOWuAEhsl9yZoMv4BYza_c,2848 +openai/resources/skills/versions/__pycache__/__init__.cpython-312.pyc,, +openai/resources/skills/versions/__pycache__/content.cpython-312.pyc,, +openai/resources/skills/versions/__pycache__/versions.cpython-312.pyc,, +openai/resources/skills/versions/content.py,sha256=E12h081WDiYdl0VOMYXiuFkZG1JLZ2n9lowalWuHYi8,6884 +openai/resources/skills/versions/versions.py,sha256=Y-CWiz01hhHMo9M56ivBJ-SuVNzXQkRkNRs8hPHsrYs,21424 +openai/resources/uploads/__init__.py,sha256=hhjOT-uo-s9BqPO_iplrrFe7NqhK9cQrSSfBY5Ou6uw,2734 +openai/resources/uploads/__pycache__/__init__.cpython-312.pyc,, +openai/resources/uploads/__pycache__/parts.cpython-312.pyc,, +openai/resources/uploads/__pycache__/uploads.cpython-312.pyc,, +openai/resources/uploads/parts.py,sha256=1erbEq8SjS0RxElGmj0LTPNm6Z8k8620-NWCVeCbSGA,8658 +openai/resources/uploads/uploads.py,sha256=7lOXvE1NAaXapLGz_vbQTRt0GWzvC5nihtcizW01-cY,27560 +openai/resources/vector_stores/__init__.py,sha256=4-_N0UlqJH1M8VmsfYu2iFCaptTvKuBJQ6onU9C7QOo,4101 +openai/resources/vector_stores/__pycache__/__init__.cpython-312.pyc,, +openai/resources/vector_stores/__pycache__/file_batches.cpython-312.pyc,, +openai/resources/vector_stores/__pycache__/files.cpython-312.pyc,, +openai/resources/vector_stores/__pycache__/vector_stores.cpython-312.pyc,, +openai/resources/vector_stores/file_batches.py,sha256=Lh5eCKjXGiMrUsdLhhfWhZPvE7MeprAaKLta4zrm7kk,36851 +openai/resources/vector_stores/files.py,sha256=JiE6jAl0yh29E6jSfYcG4sqsahbBFMwWr48aBp6atk0,43776 +openai/resources/vector_stores/vector_stores.py,sha256=JBn1Rq65NjSUIYRg69yRfCROlW_B8Gv0ebtAVxJExP0,36934 +openai/resources/videos.py,sha256=kWierExi3Gt658dYd3wnZSaVVF1Ezbk2zllch3dv5dE,59207 +openai/resources/webhooks/__init__.py,sha256=Qms2H0JDEsS_c5BWDwBWbU7onakNs0Y8wtdPRqxDulU,1006 +openai/resources/webhooks/__pycache__/__init__.cpython-312.pyc,, +openai/resources/webhooks/__pycache__/event_types.cpython-312.pyc,, +openai/resources/webhooks/__pycache__/webhooks.cpython-312.pyc,, +openai/resources/webhooks/api.md,sha256=vPnHy_Y1F9xzvHipAedBsxu2vrN71IZk0MAGUZin-cE,3460 +openai/resources/webhooks/event_types.py,sha256=d2TrmEqqx2Kw7tAa7THgvu4XfdYQG0yRpSMNVhyAkpo,5163 +openai/resources/webhooks/webhooks.py,sha256=_dZ1ILc4k0scZpgcs5jRTquDfZFE3mrJwVNvM3J_pog,41124 +openai/types/__init__.py,sha256=uFz08xvIAuX_OQFxT_rorlWs6i4H36xlrIuGtDC61L0,8977 +openai/types/__pycache__/__init__.cpython-312.pyc,, +openai/types/__pycache__/audio_model.cpython-312.pyc,, +openai/types/__pycache__/audio_response_format.cpython-312.pyc,, +openai/types/__pycache__/auto_file_chunking_strategy_param.cpython-312.pyc,, +openai/types/__pycache__/batch.cpython-312.pyc,, +openai/types/__pycache__/batch_create_params.cpython-312.pyc,, +openai/types/__pycache__/batch_error.cpython-312.pyc,, +openai/types/__pycache__/batch_list_params.cpython-312.pyc,, +openai/types/__pycache__/batch_request_counts.cpython-312.pyc,, +openai/types/__pycache__/batch_usage.cpython-312.pyc,, +openai/types/__pycache__/chat_model.cpython-312.pyc,, +openai/types/__pycache__/completion.cpython-312.pyc,, +openai/types/__pycache__/completion_choice.cpython-312.pyc,, +openai/types/__pycache__/completion_create_params.cpython-312.pyc,, +openai/types/__pycache__/completion_usage.cpython-312.pyc,, +openai/types/__pycache__/container_create_params.cpython-312.pyc,, +openai/types/__pycache__/container_create_response.cpython-312.pyc,, +openai/types/__pycache__/container_list_params.cpython-312.pyc,, +openai/types/__pycache__/container_list_response.cpython-312.pyc,, +openai/types/__pycache__/container_retrieve_response.cpython-312.pyc,, +openai/types/__pycache__/content_provenance_check.cpython-312.pyc,, +openai/types/__pycache__/content_provenance_check_create_params.cpython-312.pyc,, +openai/types/__pycache__/create_embedding_response.cpython-312.pyc,, +openai/types/__pycache__/deleted_skill.cpython-312.pyc,, +openai/types/__pycache__/embedding.cpython-312.pyc,, +openai/types/__pycache__/embedding_create_params.cpython-312.pyc,, +openai/types/__pycache__/embedding_model.cpython-312.pyc,, +openai/types/__pycache__/eval_create_params.cpython-312.pyc,, +openai/types/__pycache__/eval_create_response.cpython-312.pyc,, +openai/types/__pycache__/eval_custom_data_source_config.cpython-312.pyc,, +openai/types/__pycache__/eval_delete_response.cpython-312.pyc,, +openai/types/__pycache__/eval_list_params.cpython-312.pyc,, +openai/types/__pycache__/eval_list_response.cpython-312.pyc,, +openai/types/__pycache__/eval_retrieve_response.cpython-312.pyc,, +openai/types/__pycache__/eval_stored_completions_data_source_config.cpython-312.pyc,, +openai/types/__pycache__/eval_update_params.cpython-312.pyc,, +openai/types/__pycache__/eval_update_response.cpython-312.pyc,, +openai/types/__pycache__/file_chunking_strategy.cpython-312.pyc,, +openai/types/__pycache__/file_chunking_strategy_param.cpython-312.pyc,, +openai/types/__pycache__/file_content.cpython-312.pyc,, +openai/types/__pycache__/file_create_params.cpython-312.pyc,, +openai/types/__pycache__/file_deleted.cpython-312.pyc,, +openai/types/__pycache__/file_list_params.cpython-312.pyc,, +openai/types/__pycache__/file_object.cpython-312.pyc,, +openai/types/__pycache__/file_purpose.cpython-312.pyc,, +openai/types/__pycache__/image.cpython-312.pyc,, +openai/types/__pycache__/image_create_variation_params.cpython-312.pyc,, +openai/types/__pycache__/image_edit_completed_event.cpython-312.pyc,, +openai/types/__pycache__/image_edit_params.cpython-312.pyc,, +openai/types/__pycache__/image_edit_partial_image_event.cpython-312.pyc,, +openai/types/__pycache__/image_edit_stream_event.cpython-312.pyc,, +openai/types/__pycache__/image_gen_completed_event.cpython-312.pyc,, +openai/types/__pycache__/image_gen_partial_image_event.cpython-312.pyc,, +openai/types/__pycache__/image_gen_stream_event.cpython-312.pyc,, +openai/types/__pycache__/image_generate_params.cpython-312.pyc,, +openai/types/__pycache__/image_input_reference_param.cpython-312.pyc,, +openai/types/__pycache__/image_model.cpython-312.pyc,, +openai/types/__pycache__/images_response.cpython-312.pyc,, +openai/types/__pycache__/model.cpython-312.pyc,, +openai/types/__pycache__/model_deleted.cpython-312.pyc,, +openai/types/__pycache__/moderation.cpython-312.pyc,, +openai/types/__pycache__/moderation_create_params.cpython-312.pyc,, +openai/types/__pycache__/moderation_create_response.cpython-312.pyc,, +openai/types/__pycache__/moderation_image_url_input_param.cpython-312.pyc,, +openai/types/__pycache__/moderation_model.cpython-312.pyc,, +openai/types/__pycache__/moderation_multi_modal_input_param.cpython-312.pyc,, +openai/types/__pycache__/moderation_text_input_param.cpython-312.pyc,, +openai/types/__pycache__/other_file_chunking_strategy_object.cpython-312.pyc,, +openai/types/__pycache__/skill.cpython-312.pyc,, +openai/types/__pycache__/skill_create_params.cpython-312.pyc,, +openai/types/__pycache__/skill_list.cpython-312.pyc,, +openai/types/__pycache__/skill_list_params.cpython-312.pyc,, +openai/types/__pycache__/skill_update_params.cpython-312.pyc,, +openai/types/__pycache__/static_file_chunking_strategy.cpython-312.pyc,, +openai/types/__pycache__/static_file_chunking_strategy_object.cpython-312.pyc,, +openai/types/__pycache__/static_file_chunking_strategy_object_param.cpython-312.pyc,, +openai/types/__pycache__/static_file_chunking_strategy_param.cpython-312.pyc,, +openai/types/__pycache__/upload.cpython-312.pyc,, +openai/types/__pycache__/upload_complete_params.cpython-312.pyc,, +openai/types/__pycache__/upload_create_params.cpython-312.pyc,, +openai/types/__pycache__/vector_store.cpython-312.pyc,, +openai/types/__pycache__/vector_store_create_params.cpython-312.pyc,, +openai/types/__pycache__/vector_store_deleted.cpython-312.pyc,, +openai/types/__pycache__/vector_store_list_params.cpython-312.pyc,, +openai/types/__pycache__/vector_store_search_params.cpython-312.pyc,, +openai/types/__pycache__/vector_store_search_response.cpython-312.pyc,, +openai/types/__pycache__/vector_store_update_params.cpython-312.pyc,, +openai/types/__pycache__/video.cpython-312.pyc,, +openai/types/__pycache__/video_create_character_params.cpython-312.pyc,, +openai/types/__pycache__/video_create_character_response.cpython-312.pyc,, +openai/types/__pycache__/video_create_error.cpython-312.pyc,, +openai/types/__pycache__/video_create_params.cpython-312.pyc,, +openai/types/__pycache__/video_delete_response.cpython-312.pyc,, +openai/types/__pycache__/video_download_content_params.cpython-312.pyc,, +openai/types/__pycache__/video_edit_params.cpython-312.pyc,, +openai/types/__pycache__/video_extend_params.cpython-312.pyc,, +openai/types/__pycache__/video_get_character_response.cpython-312.pyc,, +openai/types/__pycache__/video_list_params.cpython-312.pyc,, +openai/types/__pycache__/video_model.cpython-312.pyc,, +openai/types/__pycache__/video_model_param.cpython-312.pyc,, +openai/types/__pycache__/video_remix_params.cpython-312.pyc,, +openai/types/__pycache__/video_seconds.cpython-312.pyc,, +openai/types/__pycache__/video_size.cpython-312.pyc,, +openai/types/__pycache__/websocket_connection_options.cpython-312.pyc,, +openai/types/__pycache__/websocket_reconnection.cpython-312.pyc,, +openai/types/admin/__init__.py,sha256=vQjznC-rGi6u665EpV_FgNWu3tRwEkpL0f6KNLAsRVU,121 +openai/types/admin/__pycache__/__init__.cpython-312.pyc,, +openai/types/admin/organization/__init__.py,sha256=JdWFvK0D9lRtygBmDRIWIPsSHgESAVTmCehFBEag25k,7116 +openai/types/admin/organization/__pycache__/__init__.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/admin_api_key.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/admin_api_key_create_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/admin_api_key_create_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/admin_api_key_delete_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/admin_api_key_list_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/audit_log_list_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/audit_log_list_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/aws_external_storage_provider.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/azure_external_storage_provider.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/certificate.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/certificate_activate_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/certificate_activate_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/certificate_create_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/certificate_deactivate_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/certificate_deactivate_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/certificate_delete_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/certificate_list_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/certificate_list_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/certificate_retrieve_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/certificate_update_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/cost_quantity_unit.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/data_retention_update_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/external_storage_configuration.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/external_storage_create_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/external_storage_deleted.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/external_storage_list_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/gcp_external_storage_provider.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/group.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/group_create_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/group_delete_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/group_list_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/group_update_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/group_update_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/invite.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/invite_create_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/invite_delete_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/invite_list_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/organization_data_retention.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/organization_spend_alert.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/organization_spend_alert_deleted.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/organization_spend_limit.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/organization_spend_limit_deleted.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/organization_user.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/project.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/project_create_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/project_list_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/project_residency.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/project_update_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/role.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/role_create_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/role_delete_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/role_list_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/role_update_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/spend_alert_create_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/spend_alert_list_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/spend_alert_update_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/spend_limit_update_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_audio_speeches_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_audio_speeches_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_audio_transcriptions_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_audio_transcriptions_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_code_interpreter_sessions_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_code_interpreter_sessions_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_completions_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_completions_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_costs_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_costs_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_embeddings_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_embeddings_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_file_search_calls_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_file_search_calls_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_images_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_images_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_moderations_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_moderations_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_vector_stores_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_vector_stores_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_web_search_calls_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/usage_web_search_calls_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/user_delete_response.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/user_list_params.cpython-312.pyc,, +openai/types/admin/organization/__pycache__/user_update_params.cpython-312.pyc,, +openai/types/admin/organization/admin_api_key.py,sha256=GwkSEWJ3KfYB3UBJoA3Q4XbizHNBaPvfZGegekuyajo,1492 +openai/types/admin/organization/admin_api_key_create_params.py,sha256=cz3AFdOCDOShaetdAfLCNqjxX0XMPElMmzNoGwSvjXk,438 +openai/types/admin/organization/admin_api_key_create_response.py,sha256=LU_SlxDj8E5FDzLrmQfH7TOT87e6KPS9j4Yrbm3Cgvc,348 +openai/types/admin/organization/admin_api_key_delete_response.py,sha256=IXv6iPrultWXegNH4YjNi6E__QPUmokH9rModw5dmKo,336 +openai/types/admin/organization/admin_api_key_list_params.py,sha256=kEJXZuJpmOGf0vTnVCTpyVxdvzG_NrC885et_tYe8FM,558 +openai/types/admin/organization/audit_log_list_params.py,sha256=5eXgut9zPezO9l3TIPkPF3Z5itU6oImVCYj6XWLS1rw,9526 +openai/types/admin/organization/audit_log_list_response.py,sha256=-IJgXKKJMw7roIX1OEwHdM7dMBWLumBikCjXh8BbsNs,44402 +openai/types/admin/organization/aws_external_storage_provider.py,sha256=sIJLNISY_9ki2aDfoWH7qKpcFpsSjbEqDzJ0BcG3OIc,369 +openai/types/admin/organization/azure_external_storage_provider.py,sha256=TPpORepvSCWtLrzbhXgTHzEQqqeKtxeWUu6YYvD0i0c,410 +openai/types/admin/organization/certificate.py,sha256=0b3O_Cnc7a710DUewtpi1rpTQjXkgD_Epo_drCUQPKY,1694 +openai/types/admin/organization/certificate_activate_params.py,sha256=VjW6n40Fl2PQoghdnAYzKdAEAFk_Zth7Q6DUB7J2WY0,362 +openai/types/admin/organization/certificate_activate_response.py,sha256=JutpgtmDBmvgSr7Ac5OICqBQx-rq1pEM83jSHq3hi8Q,1169 +openai/types/admin/organization/certificate_create_params.py,sha256=5E5LkyIE_ldWV5dDoVj1aTiq_tiAATRmnPk4Y84jYa0,409 +openai/types/admin/organization/certificate_deactivate_params.py,sha256=2vEaQMu016Huhj96-X5uMIpMI-GbTlcPCdnEIVhzUG8,366 +openai/types/admin/organization/certificate_deactivate_response.py,sha256=QmJFQ3E22tUOFjgKT9dc34HCgqfWYqdfadk45LYraFg,1173 +openai/types/admin/organization/certificate_delete_response.py,sha256=QPKAIN8Ua5VEDzAUJ-zXawts61dC5nt0VQg6cm5WpcQ,414 +openai/types/admin/organization/certificate_list_params.py,sha256=A0_8VahRInMS4HNdQB3I6LmG059ZivFYm5dY5yfN6M4,896 +openai/types/admin/organization/certificate_list_response.py,sha256=Ou3GDUduoZsG5X9OHnBzjuxpmpUx2Tc4VGF3F8Oxra4,1161 +openai/types/admin/organization/certificate_retrieve_params.py,sha256=WUOe7eSM1MmJnZbEmNrLiX4URjgNYo4KzTDNCqrO7ck,506 +openai/types/admin/organization/certificate_update_params.py,sha256=rIAOdkX47SUtB3qZJU5tEcEG5LvX5hCCMP39BQZXRwE,319 +openai/types/admin/organization/cost_quantity_unit.py,sha256=vdzd_3lVx-l_m6EIEcgXGgqg6sL_lBzKtI7R3noyr8M,450 +openai/types/admin/organization/data_retention_update_params.py,sha256=l6BDv7uR18tLjxo3cJRjY38clule02nts5OBMk6V7eY,570 +openai/types/admin/organization/external_storage_configuration.py,sha256=6ocGYZVCTz0qmbBdrXiuXZk1BthGCBznDfSPthyrhlU,944 +openai/types/admin/organization/external_storage_create_params.py,sha256=aeQ_DPkzyBGkC13PQ2HKXkHEgOxcVvfkmaIFXnMwkZ4,1155 +openai/types/admin/organization/external_storage_deleted.py,sha256=F3TyMeindzk3SeIA5vDrVIQP3UrvEV3IXaeGiOYKLyg,333 +openai/types/admin/organization/external_storage_list_params.py,sha256=ItEb7QOiKy4JMO7W-XjD_mnuMgUiw5iwrGTYK5KVfkw,470 +openai/types/admin/organization/gcp_external_storage_provider.py,sha256=eY-HR6jRxbjlZYJC4a4jTLp5dPfjkqgn17JjgwFSJ88,445 +openai/types/admin/organization/group.py,sha256=nTNwioEXiT7heX-ZVpvzXEbuMocnaYrYlANHCMHkids,659 +openai/types/admin/organization/group_create_params.py,sha256=yOd2yQVCx-rt_rivgMNHW8_o3TV09oUFYcoEEe6x-3Y,325 +openai/types/admin/organization/group_delete_response.py,sha256=QeuPWWGHhvw3w5z1AfOBTkI8spHj9KsK5hibg9G52-0,486 +openai/types/admin/organization/group_list_params.py,sha256=Ktl2C_AtUkB9oJdQEQwStYs7uNhrWKPEiJ3pjzVdvnM,812 +openai/types/admin/organization/group_update_params.py,sha256=9yOkXsDZpYCBhBCyXoTJxquuhyzbzCYHNSbJKUv6diM,322 +openai/types/admin/organization/group_update_response.py,sha256=afCQ_psq82iDa2P6zAZ3KY3NelSMrPds_j85yAb27Xg,579 +openai/types/admin/organization/groups/__init__.py,sha256=Iqxb6OA-mavdUYDQfByn-xnvD-iUV76I0_WuZDqtpC4,1001 +openai/types/admin/organization/groups/__pycache__/__init__.cpython-312.pyc,, +openai/types/admin/organization/groups/__pycache__/organization_group_user.cpython-312.pyc,, +openai/types/admin/organization/groups/__pycache__/role_create_params.cpython-312.pyc,, +openai/types/admin/organization/groups/__pycache__/role_create_response.cpython-312.pyc,, +openai/types/admin/organization/groups/__pycache__/role_delete_response.cpython-312.pyc,, +openai/types/admin/organization/groups/__pycache__/role_list_params.cpython-312.pyc,, +openai/types/admin/organization/groups/__pycache__/role_list_response.cpython-312.pyc,, +openai/types/admin/organization/groups/__pycache__/role_retrieve_response.cpython-312.pyc,, +openai/types/admin/organization/groups/__pycache__/user_create_params.cpython-312.pyc,, +openai/types/admin/organization/groups/__pycache__/user_create_response.cpython-312.pyc,, +openai/types/admin/organization/groups/__pycache__/user_delete_response.cpython-312.pyc,, +openai/types/admin/organization/groups/__pycache__/user_list_params.cpython-312.pyc,, +openai/types/admin/organization/groups/__pycache__/user_retrieve_response.cpython-312.pyc,, +openai/types/admin/organization/groups/organization_group_user.py,sha256=78ycmjuiIMkLOEc3S335DXSbqWnHDvDcYwi7p75aRLI,513 +openai/types/admin/organization/groups/role_create_params.py,sha256=_CX_uIKM2XAimP360DVnrTBtnQ1BmnuZ1ky3GwwMdcY,325 +openai/types/admin/organization/groups/role_create_response.py,sha256=-FSazlFtmEo7sZ6LOjsG1-qzwWQupu99J-9Qhzyf-ns,1014 +openai/types/admin/organization/groups/role_delete_response.py,sha256=mWFd8GL2Bwa2P38KOMy2qJzf6DZu6O_t_Mb8PZIOV9A,458 +openai/types/admin/organization/groups/role_list_params.py,sha256=zk6w6LrHVelrQ-GW_Y8LOtMMaQ7LqEPcVtlOWRaASyM,596 +openai/types/admin/organization/groups/role_list_response.py,sha256=JyUnw4mms8vHhXlijLhus9Pv_97Rv1FL3dIdwSnKO1w,1462 +openai/types/admin/organization/groups/role_retrieve_response.py,sha256=Nr4vMhyxctQmBdNuMNTZ2VfXGgx0jEWUlouynedWG58,1470 +openai/types/admin/organization/groups/user_create_params.py,sha256=EinBbw2N4NYWgaEly7oC6u1hGOpjrLHzWGjJQabGxok,335 +openai/types/admin/organization/groups/user_create_response.py,sha256=2PAFJJ2BjU5MvvKkKnxTIt5ob7EkrXWNR751FUYmZXM,514 +openai/types/admin/organization/groups/user_delete_response.py,sha256=cn8Hwu4KahwGl1QEE0cgw2j16G2pAEjutsrt-6_a2aA,462 +openai/types/admin/organization/groups/user_list_params.py,sha256=_y8-M3V1lhE634pPBHhKRbAhbqWyg7I4H01-jJB8qAo,646 +openai/types/admin/organization/groups/user_retrieve_response.py,sha256=wDe9BcGXxTotnzvssj07PYYSrWk0boBmnLUJOQXZx2w,823 +openai/types/admin/organization/invite.py,sha256=fFXLCMdOk6BWbG0otrkEuuaMF3DH4EO42-ahJshG3L4,1333 +openai/types/admin/organization/invite_create_params.py,sha256=SbN6ttx4HvCxaSQrCDDhyfIC6avIR8YokpVLQJgf7L8,984 +openai/types/admin/organization/invite_delete_response.py,sha256=uRwPnt-XZEwIadrAAjXsqbwtL4nOzsiZNU2gVNTyDSQ,392 +openai/types/admin/organization/invite_list_params.py,sha256=gcquw6t28munKABGa-pND9_KbKZ0oWcd3FhxlS-N7OQ,706 +openai/types/admin/organization/organization_data_retention.py,sha256=6Qp7hr_MMUXL9fpbwbMCKSsOCVmCTadbEFH8WHnFTms,683 +openai/types/admin/organization/organization_spend_alert.py,sha256=mENCm3X-YfpbInTcN7s5t56dUSrIn2-Jn5tGl0h3fTY,1326 +openai/types/admin/organization/organization_spend_alert_deleted.py,sha256=zjYL98l-ObZ6iUzXORWV5jCIxH1V43AOV94eYQR7PKA,565 +openai/types/admin/organization/organization_spend_limit.py,sha256=omUR8R3phQYYydiouoqJueFYUmsTuKC02QYYfwO-Mgk,1158 +openai/types/admin/organization/organization_spend_limit_deleted.py,sha256=iSySTczoy7wuAQHlnznwQxQl1EJ1ogBf-xl2MfbsEJE,550 +openai/types/admin/organization/organization_user.py,sha256=f1iwGpE0F3Mcpq2p-aOI1tXZ3DTDsuOVEzxxi8lTZTA,2427 +openai/types/admin/organization/project.py,sha256=Ka25DWWNw4roRAEDy4xJZehULqWa0PLCfqnFLdfMFkk,1107 +openai/types/admin/organization/project_create_params.py,sha256=qF-q7gD9w5XjGYZi3h7mzUlVHi7APWnoabIYl6mIB-k,1415 +openai/types/admin/organization/project_list_params.py,sha256=_07OgbbKEYfTZhzzZ1lvDEByZ1qkqqPyPYqLrZv-uC0,877 +openai/types/admin/organization/project_residency.py,sha256=DIEC_IGrOG2SASSfFha5eBE4pQRZP9Agip2rPQRq-TI,454 +openai/types/admin/organization/project_update_params.py,sha256=2PJAgcJG896tjYI0l-I_Xtp_em9Rck-GmYbC-_5lNpU,681 +openai/types/admin/organization/projects/__init__.py,sha256=vFsbQJY1IL3kPO5wrX9elxcxCuTJ2cL6cHUw-8Btiek,4141 +openai/types/admin/organization/projects/__pycache__/__init__.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/api_key_delete_response.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/api_key_list_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/certificate_activate_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/certificate_activate_response.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/certificate_deactivate_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/certificate_deactivate_response.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/certificate_list_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/certificate_list_response.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/data_retention_update_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/group_create_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/group_delete_response.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/group_list_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/group_retrieve_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/hosted_tool_permission_update_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/model_permission_update_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/project_api_key.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/project_data_retention.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/project_group.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/project_hosted_tool_permissions.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/project_model_permissions.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/project_model_permissions_deleted.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/project_rate_limit.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/project_service_account.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/project_spend_alert.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/project_spend_alert_deleted.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/project_spend_limit.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/project_spend_limit_deleted.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/project_user.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/rate_limit_list_rate_limits_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/rate_limit_update_rate_limit_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/role_create_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/role_delete_response.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/role_list_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/role_update_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/service_account_create_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/service_account_create_response.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/service_account_delete_response.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/service_account_list_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/service_account_update_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/spend_alert_create_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/spend_alert_list_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/spend_alert_update_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/spend_limit_update_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/user_create_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/user_delete_response.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/user_list_params.cpython-312.pyc,, +openai/types/admin/organization/projects/__pycache__/user_update_params.cpython-312.pyc,, +openai/types/admin/organization/projects/api_key_delete_response.py,sha256=gZU1bEvJQzfgOX1MBmMYFWpnbWYZubM2ju4Jnal-DK8,329 +openai/types/admin/organization/projects/api_key_list_params.py,sha256=5N-FNl7gvl-6dMYcb0daVF1eMwUnS9B63L7knAIWapA,1135 +openai/types/admin/organization/projects/certificate_activate_params.py,sha256=3VCIRw6yFs7IvupR6O2EtUFtLItNmz3l2z1xOCaRyt8,363 +openai/types/admin/organization/projects/certificate_activate_response.py,sha256=wTvZmpBymLHLKYIp940HzP_Y5TEQ4iP9r_pfW9WpAuU,1176 +openai/types/admin/organization/projects/certificate_deactivate_params.py,sha256=fOkACojtMCiyVWxPIiy8t6ybm-H1vKDGYnnNEvp-DQ0,367 +openai/types/admin/organization/projects/certificate_deactivate_response.py,sha256=mS90hR1tTQvjZ7aWjYa363lqXkhe6eyC5lHt465jbbQ,1180 +openai/types/admin/organization/projects/certificate_list_params.py,sha256=A0_8VahRInMS4HNdQB3I6LmG059ZivFYm5dY5yfN6M4,896 +openai/types/admin/organization/projects/certificate_list_response.py,sha256=UeqroP-qa5i4zVT3UBjfU_OxvirwzlvkLyEzm8dRbdg,1168 +openai/types/admin/organization/projects/data_retention_update_params.py,sha256=E_E9Nych-Hmqkh1sTQCNjotSuzGkPnx7tScrnnAg5F4,621 +openai/types/admin/organization/projects/group_create_params.py,sha256=sJ63ER64f2J-J6HivWt3VVXHGFA8vI1NtiNBcrbI9RU,430 +openai/types/admin/organization/projects/group_delete_response.py,sha256=lutamMKzBf53goRtTPUqSp-KRRBv3TDdX8rd7It4soU,488 +openai/types/admin/organization/projects/group_list_params.py,sha256=yVqNVuNOy2nbAL5F_YY7HZGElB3BrwMSCBdYg2JlgPY,571 +openai/types/admin/organization/projects/group_retrieve_params.py,sha256=RhtwLf-HdPJBPdo4tus-xIWeB4oEMTryb50G6ZFjQuE,390 +openai/types/admin/organization/projects/groups/__init__.py,sha256=9AID9bJ6PSK-MH4JOAqLBw5PmWUnullDCTD3AI50Pe0,554 +openai/types/admin/organization/projects/groups/__pycache__/__init__.cpython-312.pyc,, +openai/types/admin/organization/projects/groups/__pycache__/role_create_params.cpython-312.pyc,, +openai/types/admin/organization/projects/groups/__pycache__/role_create_response.cpython-312.pyc,, +openai/types/admin/organization/projects/groups/__pycache__/role_delete_response.cpython-312.pyc,, +openai/types/admin/organization/projects/groups/__pycache__/role_list_params.cpython-312.pyc,, +openai/types/admin/organization/projects/groups/__pycache__/role_list_response.cpython-312.pyc,, +openai/types/admin/organization/projects/groups/__pycache__/role_retrieve_response.cpython-312.pyc,, +openai/types/admin/organization/projects/groups/role_create_params.py,sha256=UzSHLgKN4G3tegiDOzZUeZgysfXQo8ZAZ9v8Zog7xCU,356 +openai/types/admin/organization/projects/groups/role_create_response.py,sha256=ugyJBLty7y3Q-1Sa05ea7v46Ok0kahSSkfvGx_0yPGg,1016 +openai/types/admin/organization/projects/groups/role_delete_response.py,sha256=nBqR7Ngzz8AJOMCGPWArU9NcPZooLbGmJQ-VLW0RRaw,459 +openai/types/admin/organization/projects/groups/role_list_params.py,sha256=pD9D5OFUQ_RmP72aJCkSHHOPmHe3weaHb7fPgT4KtP8,622 +openai/types/admin/organization/projects/groups/role_list_response.py,sha256=CJCBZ__iY_6mvS8SrZ1A6_t0zTNHSou53W6pI-C2Wyc,1463 +openai/types/admin/organization/projects/groups/role_retrieve_response.py,sha256=x4f5a-dLyc7gpCZsLqBlMYDa3Vo2yd3gRLnMjdZgw4w,1471 +openai/types/admin/organization/projects/hosted_tool_permission_update_params.py,sha256=KCcWgYSEA5SFTfE8A2eFEWx6T7pgvRV7uf-rgEHBjAQ,1707 +openai/types/admin/organization/projects/model_permission_update_params.py,sha256=Mr1AVky7cVHcNsWOYQ6UvbbiSbt3lv2K7TqM7HTMP4A,534 +openai/types/admin/organization/projects/project_api_key.py,sha256=jMeNKVgp9RrG87jvEjYYYYMROT_w_WH7GJSNyCDTTow,2338 +openai/types/admin/organization/projects/project_data_retention.py,sha256=zuI4BLAZME8ibrhSpNDjBGTiuK5U30IBM814XrsIXWo,700 +openai/types/admin/organization/projects/project_group.py,sha256=zMBgMbVCtrfLJtaHoZMzOf9MyEVSaj4EF9NwzGpwzH8,738 +openai/types/admin/organization/projects/project_hosted_tool_permissions.py,sha256=dAyWeM7f5kdXcAygHBfRB0jQ9PR0N1Z8yB5uzDI_1mQ,1716 +openai/types/admin/organization/projects/project_model_permissions.py,sha256=ApYk20DPj0Ax9cY9p5lO5OhM9ofkgkQHRpIC_aESJoY,741 +openai/types/admin/organization/projects/project_model_permissions_deleted.py,sha256=927MdhD4gEl9Qut4xZ1kqUcwPIEdrNdPwFKNHgVr24c,558 +openai/types/admin/organization/projects/project_rate_limit.py,sha256=vX8yZGBKxnTSQZcI4yq8OryqN6D5EY49cTNbgG0Xf4U,1247 +openai/types/admin/organization/projects/project_service_account.py,sha256=Eb7CYzfrS0Lij5xBgMc7CAvyNcW08ZgNpSZuNo3xSFE,772 +openai/types/admin/organization/projects/project_spend_alert.py,sha256=FOFB6DOTLXxpJFEHZfKrneSjumcVUEM-fId20kYc0vc,1302 +openai/types/admin/organization/projects/project_spend_alert_deleted.py,sha256=ozQE9wi_AlUiKJ4fGwES95xqWB0nCNOrC7QyxhpR7oM,540 +openai/types/admin/organization/projects/project_spend_limit.py,sha256=AxiGu858esNSeQ_kN-XA4q6-53OqG4UaHh_5k7lXE58,1134 +openai/types/admin/organization/projects/project_spend_limit_deleted.py,sha256=Oa2tAGlGhXvhFRLEdBBTJtzLLbVFqDfzwnbTr281KVc,525 +openai/types/admin/organization/projects/project_user.py,sha256=W-Y7j_aAPZt-Vuu3J4GBCRKAlZKFsU-0P1Tj9YPcP-o,775 +openai/types/admin/organization/projects/rate_limit_list_rate_limits_params.py,sha256=3VOkKeNKOs4zttYtbpzHClYIhuTitKGV_4chdbCqMGs,1014 +openai/types/admin/organization/projects/rate_limit_update_rate_limit_params.py,sha256=MQf559E5G83YT3cVzM3_eDtFGX9nuVRvREeTTfd3p9I,932 +openai/types/admin/organization/projects/role_create_params.py,sha256=mhHdeUgmCttNUXz-pKlnOi_b-7204B2Zpim697hH8CU,555 +openai/types/admin/organization/projects/role_delete_response.py,sha256=hX9f4SorvQ_SVljoRe0ZvrzGlT_zQefLHo08MclbgQ4,480 +openai/types/admin/organization/projects/role_list_params.py,sha256=k3lH_0borWYwTpEJB9J2d2mJzW0LVbcp9hOZ-7QczrA,564 +openai/types/admin/organization/projects/role_update_params.py,sha256=bL4NtGdcsNK5iIrO6ELmtuaBKcIx-MHcgRQdQNPvn-g,586 +openai/types/admin/organization/projects/service_account_create_params.py,sha256=XtcAh-9vcWUBVGmtKiVdsGwjPDuFzRu4UaEChTWoKLg,925 +openai/types/admin/organization/projects/service_account_create_response.py,sha256=cQgFSMXV8aHglNypiVGGcgcKERrV3xWerGOv1U1RZtA,1043 +openai/types/admin/organization/projects/service_account_delete_response.py,sha256=eGtF_cSv87zd5GVtR6ljoZa-QfZXMDd-YQyKm_Vaup8,353 +openai/types/admin/organization/projects/service_account_list_params.py,sha256=OmL-xnt02VZPS2wsTig5WYkGm5smnP9v9_5YEIlwMQE,722 +openai/types/admin/organization/projects/service_account_update_params.py,sha256=ZJmSEqK3irhQQ7YiF1rmHPfzbq0ZM3AQyKFYgzxBmqo,454 +openai/types/admin/organization/projects/service_accounts/__init__.py,sha256=tT6vToFzyUsAITIecCUuWOEveYn2CjRpE18XqmvLJs4,280 +openai/types/admin/organization/projects/service_accounts/__pycache__/__init__.cpython-312.pyc,, +openai/types/admin/organization/projects/service_accounts/__pycache__/api_key_create_params.cpython-312.pyc,, +openai/types/admin/organization/projects/service_accounts/__pycache__/api_key_create_response.cpython-312.pyc,, +openai/types/admin/organization/projects/service_accounts/api_key_create_params.py,sha256=01hNkNMvX_gwGv91PRA_6J7D8QxQYecwsEs3IR2jglI,582 +openai/types/admin/organization/projects/service_accounts/api_key_create_response.py,sha256=IHoCMCbMReYYoMUJte3ZgtCZLE0Q6pLVcnM2Pj9T8ho,828 +openai/types/admin/organization/projects/spend_alert_create_params.py,sha256=SAW94_9S9Yq9tkkAqdfK1TPfmBPQd8KvMk-3qovU1pY,1196 +openai/types/admin/organization/projects/spend_alert_list_params.py,sha256=VZY8H_p_RZnBeDfgjiWsxZuf-L3lQfKNhzAakvdSIPA,750 +openai/types/admin/organization/projects/spend_alert_update_params.py,sha256=Q2vEY-hzqyYCjf60uYLCrBPY7s4nonO_K6SZ0yZPgO8,1227 +openai/types/admin/organization/projects/spend_limit_update_params.py,sha256=iaLgIycNJVj62GCUctji8kOInkLF0EyKLxj-Rjws3pA,647 +openai/types/admin/organization/projects/user_create_params.py,sha256=gqSvOZnffspZN2LiB-AwhhaIRrE4NMMFpF2ErhYqPx4,456 +openai/types/admin/organization/projects/user_delete_response.py,sha256=Qq9KnKQnUiM-vY-DtYcI5ltsuTfxWSskz1wwcO2bdRw,322 +openai/types/admin/organization/projects/user_list_params.py,sha256=UIsiHZLdRbPQuOkCPfj_d6jzu0yR1Vh7kn7wxdScggE,702 +openai/types/admin/organization/projects/user_update_params.py,sha256=jXlrGsjwxGEcQsNxWDeqSZFF_np_3CQe2c24rdsBJTI,367 +openai/types/admin/organization/projects/users/__init__.py,sha256=9AID9bJ6PSK-MH4JOAqLBw5PmWUnullDCTD3AI50Pe0,554 +openai/types/admin/organization/projects/users/__pycache__/__init__.cpython-312.pyc,, +openai/types/admin/organization/projects/users/__pycache__/role_create_params.cpython-312.pyc,, +openai/types/admin/organization/projects/users/__pycache__/role_create_response.cpython-312.pyc,, +openai/types/admin/organization/projects/users/__pycache__/role_delete_response.cpython-312.pyc,, +openai/types/admin/organization/projects/users/__pycache__/role_list_params.cpython-312.pyc,, +openai/types/admin/organization/projects/users/__pycache__/role_list_response.cpython-312.pyc,, +openai/types/admin/organization/projects/users/__pycache__/role_retrieve_response.cpython-312.pyc,, +openai/types/admin/organization/projects/users/role_create_params.py,sha256=UzSHLgKN4G3tegiDOzZUeZgysfXQo8ZAZ9v8Zog7xCU,356 +openai/types/admin/organization/projects/users/role_create_response.py,sha256=BYlhcxpO75dlgLMBo6XyR7EIRL98EtMYilRs-Dj39o4,617 +openai/types/admin/organization/projects/users/role_delete_response.py,sha256=nBqR7Ngzz8AJOMCGPWArU9NcPZooLbGmJQ-VLW0RRaw,459 +openai/types/admin/organization/projects/users/role_list_params.py,sha256=pD9D5OFUQ_RmP72aJCkSHHOPmHe3weaHb7fPgT4KtP8,622 +openai/types/admin/organization/projects/users/role_list_response.py,sha256=CJCBZ__iY_6mvS8SrZ1A6_t0zTNHSou53W6pI-C2Wyc,1463 +openai/types/admin/organization/projects/users/role_retrieve_response.py,sha256=x4f5a-dLyc7gpCZsLqBlMYDa3Vo2yd3gRLnMjdZgw4w,1471 +openai/types/admin/organization/role.py,sha256=ecMhBh5UCHnJubOXjkN91vPhOZ0X-s7NXBdGaWYMWjo,852 +openai/types/admin/organization/role_create_params.py,sha256=LJjAJCRB08zp3TLAjA2_HzQ5Xoi5OnpkaRoThBGLFSM,554 +openai/types/admin/organization/role_delete_response.py,sha256=o-MiOVyERYg4_rbEvu4ATOwNwWdx2E0VM3vWlc1_K0g,479 +openai/types/admin/organization/role_list_params.py,sha256=k3lH_0borWYwTpEJB9J2d2mJzW0LVbcp9hOZ-7QczrA,564 +openai/types/admin/organization/role_update_params.py,sha256=_tiKRB64krVf6V1I3occ4o1ckVhqJB6tRwgGxMnzPGM,544 +openai/types/admin/organization/spend_alert_create_params.py,sha256=H4HEdQoSPCCY_sOd_LWdlW16HABB8dQ9gGdg1cFOmJw,1195 +openai/types/admin/organization/spend_alert_list_params.py,sha256=VZY8H_p_RZnBeDfgjiWsxZuf-L3lQfKNhzAakvdSIPA,750 +openai/types/admin/organization/spend_alert_update_params.py,sha256=Url_v0Muho8dUl6UWW4YUbwR6BXVi7NpNhgnPTDSuv8,1195 +openai/types/admin/organization/spend_limit_update_params.py,sha256=iaLgIycNJVj62GCUctji8kOInkLF0EyKLxj-Rjws3pA,647 +openai/types/admin/organization/usage_audio_speeches_params.py,sha256=XmQHWF1RkKZDY32atSfE0PZJe5mzBsh9UuKtC1YIvDg,1624 +openai/types/admin/organization/usage_audio_speeches_response.py,sha256=W-wboyy73UnCFTvNr33AYvxY-D1tvpyi6ji7pEjTBnI,14694 +openai/types/admin/organization/usage_audio_transcriptions_params.py,sha256=tpeUf8xa4Bie3YAbRdKriGaEvBMNh5SFyEog0tcqlzY,1636 +openai/types/admin/organization/usage_audio_transcriptions_response.py,sha256=8Bbtx7Ta692FgWS-3kVuLAibQ8iZMbmlXVJLltfHqLI,14706 +openai/types/admin/organization/usage_code_interpreter_sessions_params.py,sha256=JONnc1uMCc_hGtUCdt-O22Q4CUutclL0d_z2mAYr56Y,1300 +openai/types/admin/organization/usage_code_interpreter_sessions_response.py,sha256=cfoqcX5RilKHFAj1fEEslFDIUOx0bk6bZsegUA5Y4yk,14714 +openai/types/admin/organization/usage_completions_params.py,sha256=XcVYFgseM3N8DVBk7oI-LGLYq9klvnIQRiFrrMBC2sk,1807 +openai/types/admin/organization/usage_completions_response.py,sha256=BI2TwTIVwCvvqDubOKPRbL2J6Wptvu8r9oX7r_cM0K0,14690 +openai/types/admin/organization/usage_costs_params.py,sha256=VetkMyvu8bAIyAKXLzJqQ1RtZA3y3hBR_12iCSL7msk,1542 +openai/types/admin/organization/usage_costs_response.py,sha256=WIDF_DtJFrqfJ-XC14kNNTqRxaiBcRxUh_JVvI5jZ-Y,14678 +openai/types/admin/organization/usage_embeddings_params.py,sha256=4cHFOBcDxmgRQn8ts0VAGz9CndzYcKlFwctFIlZ6Kjo,1618 +openai/types/admin/organization/usage_embeddings_response.py,sha256=efFogOSN6hLit0s9nIXJveSXfHHW6rNyinH7WN7QG9Q,14688 +openai/types/admin/organization/usage_file_search_calls_params.py,sha256=8YH9DwaZjQ2thJuH43kXL-mY7xkgLMRy_TZgWFctNd4,1665 +openai/types/admin/organization/usage_file_search_calls_response.py,sha256=eNvQBjr9Wkgr044X25MAAUjrNjoFSAq0WvJphNBHTz8,14698 +openai/types/admin/organization/usage_images_params.py,sha256=XjJpC-V6F4udboqxIAn5QdJbBb_GM-IHypBRTYlrHKQ,2150 +openai/types/admin/organization/usage_images_response.py,sha256=naXPve5Dr5s8nQGYvI9PFg6ytouikGDuQNNOqLLixD8,14680 +openai/types/admin/organization/usage_moderations_params.py,sha256=uDb7CBsN1ZxKzVSO5rSveXYD0UsfxtuTxLCrDn4BWeM,1620 +openai/types/admin/organization/usage_moderations_response.py,sha256=c3P2Rm3--repQSjQHKfCTLx6X30ThZCNx1sRKHI2Uc4,14690 +openai/types/admin/organization/usage_vector_stores_params.py,sha256=bwvrhJGnVblISWDhKdMTVW6jgXzlFshCgheEwplqfJA,1278 +openai/types/admin/organization/usage_vector_stores_response.py,sha256=QYf395Z8M0Uw-Zp_tQM8vdcxFFwbjgCOtIXiBJIWzmo,14692 +openai/types/admin/organization/usage_web_search_calls_params.py,sha256=xVTeNFq1zy986er3z5W9H50X0GJNWLTUntkItgBVtNo,1785 +openai/types/admin/organization/usage_web_search_calls_response.py,sha256=60JaICndZJN2q0hJHDj7nMzGN9Bhun12hDlwS3thAR4,14696 +openai/types/admin/organization/user_delete_response.py,sha256=91zqX5pGvrsJzcrfQ1lx4923Udf3KnKH_0S5KpV_deY,313 +openai/types/admin/organization/user_list_params.py,sha256=F6lPCxk-I6oC7fPQ0PqgTpmYpEwhSv2x41LaTp7exJM,822 +openai/types/admin/organization/user_update_params.py,sha256=ifrAESVIBPNcuVghVhecJIzSY_MUxa7Fnm34o5mYp6g,543 +openai/types/admin/organization/users/__init__.py,sha256=9AID9bJ6PSK-MH4JOAqLBw5PmWUnullDCTD3AI50Pe0,554 +openai/types/admin/organization/users/__pycache__/__init__.cpython-312.pyc,, +openai/types/admin/organization/users/__pycache__/role_create_params.cpython-312.pyc,, +openai/types/admin/organization/users/__pycache__/role_create_response.cpython-312.pyc,, +openai/types/admin/organization/users/__pycache__/role_delete_response.cpython-312.pyc,, +openai/types/admin/organization/users/__pycache__/role_list_params.cpython-312.pyc,, +openai/types/admin/organization/users/__pycache__/role_list_response.cpython-312.pyc,, +openai/types/admin/organization/users/__pycache__/role_retrieve_response.cpython-312.pyc,, +openai/types/admin/organization/users/role_create_params.py,sha256=_CX_uIKM2XAimP360DVnrTBtnQ1BmnuZ1ky3GwwMdcY,325 +openai/types/admin/organization/users/role_create_response.py,sha256=JRsxDecNrm2yoahncLfDxrWSevW_ir0YvpUQVYe-Uq4,614 +openai/types/admin/organization/users/role_delete_response.py,sha256=mWFd8GL2Bwa2P38KOMy2qJzf6DZu6O_t_Mb8PZIOV9A,458 +openai/types/admin/organization/users/role_list_params.py,sha256=zk6w6LrHVelrQ-GW_Y8LOtMMaQ7LqEPcVtlOWRaASyM,596 +openai/types/admin/organization/users/role_list_response.py,sha256=JyUnw4mms8vHhXlijLhus9Pv_97Rv1FL3dIdwSnKO1w,1462 +openai/types/admin/organization/users/role_retrieve_response.py,sha256=Nr4vMhyxctQmBdNuMNTZ2VfXGgx0jEWUlouynedWG58,1470 +openai/types/audio/__init__.py,sha256=PjxNVxX7AL6dbGTDJwONdqHUalhovuloWCJMBZuQ-hg,1805 +openai/types/audio/__pycache__/__init__.cpython-312.pyc,, +openai/types/audio/__pycache__/speech_create_params.cpython-312.pyc,, +openai/types/audio/__pycache__/speech_model.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription_create_params.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription_create_response.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription_diarized.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription_diarized_segment.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription_include.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription_language.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription_segment.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription_stream_event.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription_text_delta_event.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription_text_done_event.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription_text_segment_event.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription_verbose.cpython-312.pyc,, +openai/types/audio/__pycache__/transcription_word.cpython-312.pyc,, +openai/types/audio/__pycache__/translation.cpython-312.pyc,, +openai/types/audio/__pycache__/translation_create_params.cpython-312.pyc,, +openai/types/audio/__pycache__/translation_create_response.cpython-312.pyc,, +openai/types/audio/__pycache__/translation_verbose.cpython-312.pyc,, +openai/types/audio/speech_create_params.py,sha256=eGq38N8NY4rvGJGNLJVKvrWLEQ-GIUYWnwoxG4E5wso,2218 +openai/types/audio/speech_model.py,sha256=-37RbNe1VGtVQvh0g9apSiZcBKfwU1nVXht1wVMtHI8,266 +openai/types/audio/transcription.py,sha256=mMZYwuLakaulHMUL8mO-TpodoT5aPqBoDQI8uwQNBBs,2686 +openai/types/audio/transcription_create_params.py,sha256=VIcNvB0NJtopLhlvyzO_RAmX7Umj9GMzpRHcZLUcNqU,7507 +openai/types/audio/transcription_create_response.py,sha256=P1dCtFWagIqUUOfiloi0B2esJNkfRPQLG4iQ6OAcOT0,458 +openai/types/audio/transcription_diarized.py,sha256=xIhFeOV2IhZpaIEPlM0_xz842v39zkChmZzr7pNrTkw,2373 +openai/types/audio/transcription_diarized_segment.py,sha256=9-toolsZAZvpwmUwJfKCJaWozJu9RscOkk8-M_9BwbA,930 +openai/types/audio/transcription_include.py,sha256=1HaNa9fhVAJl6STOgEHk57bFpJLYBoYCPUc31sRT06o,226 +openai/types/audio/transcription_language.py,sha256=ga5s0k09E1ACHBVZle2heWMPe05-Ny23xR7EzWsHf14,321 +openai/types/audio/transcription_segment.py,sha256=rKCNgM0PppddcotPpS44RQkER_nJggb3lMYNxEPQOEs,1152 +openai/types/audio/transcription_stream_event.py,sha256=a7psOFf2RQ4b9juJXGzWAoBCHGAzmwm1udcDcTBCoZs,647 +openai/types/audio/transcription_text_delta_event.py,sha256=wOCwMqIQseWmK0QXlR9NVBJ0qawLJ479G6K0qvq59-g,1608 +openai/types/audio/transcription_text_done_event.py,sha256=5n7kxpbLLYajR_pYdOn2ydGYff-IauFAGMn-l2h9hO0,2678 +openai/types/audio/transcription_text_segment_event.py,sha256=atVfVCyleoaKdEAndqpe5gLCFzILjWYgLiGTJoUD58E,1018 +openai/types/audio/transcription_verbose.py,sha256=0Xu-fsGl9mFDRCgdQVNribjHMS4gzogjnrUBfKBs6dE,1302 +openai/types/audio/transcription_word.py,sha256=eJwn1rWqJsp260vvLP-19Ipr5rYjezW5KJ0BHUWqgYY,366 +openai/types/audio/translation.py,sha256=l3UkBhTVST9hvyLjzxoSSNTkm9qcyIyN_WRpdWrLjAM,192 +openai/types/audio/translation_create_params.py,sha256=lmwSlcCaYXN2IRNgBEAgjP5XwpOsodP4wGdA5Cct_6A,1744 +openai/types/audio/translation_create_response.py,sha256=jeoNSyS_3V78lJZHN9Hgex7VCSwoqriKlfs3YW-iiwE,361 +openai/types/audio/translation_verbose.py,sha256=wDS2XXZt3Zf_hpn-IA90T5UuMjI3xFt9jkB4PqU6hSc,614 +openai/types/audio_model.py,sha256=EMTEbZGvxLL40aWENGS8TM5E19vuRyl4xgmZ5Dij3Us,365 +openai/types/audio_response_format.py,sha256=M1ITbuIqAz9DsIE_6hWTSOZzPtg5NUt4tejGi_xD4Ws,275 +openai/types/auto_file_chunking_strategy_param.py,sha256=JN1SOFtYN5etWv68JWQ74YCsN8-pfC8_cjCMixuiHTQ,494 +openai/types/batch.py,sha256=p0QOWOYt0HiHFazEAiTt5HviRjqZbcJtqHpYqHzNfTc,3517 +openai/types/batch_create_params.py,sha256=4tqwlmhS1qvQi7TVAEKAGy01RZnRwcFpKyam87Ukg2Q,2887 +openai/types/batch_error.py,sha256=8FNFqkS4kmfrbtuNJ9GfSdzA_vQ30-8eUY2UOMwlxII,621 +openai/types/batch_list_params.py,sha256=U7hZsFlxE2TnuUtsftHU0BLDVC8HbBa-vpYgJW0VAa8,704 +openai/types/batch_request_counts.py,sha256=Qll480Lq1ztbBUn8lnbjfvdNhJ0gnwy4eWHS8WVqZBY,478 +openai/types/batch_usage.py,sha256=NoLZ0AHa-wMqaUfRtkjUfhoBT-e_KgvXw3MviS79Eps,1265 +openai/types/beta/__init__.py,sha256=Pb1ZNxy9towZSxQPale8EdPi5Jz0LQzfWhKfE0hMZQI,38999 +openai/types/beta/__pycache__/__init__.cpython-312.pyc,, +openai/types/beta/__pycache__/agent.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_close_subagent_call_item.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_command_execution_item.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_content.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_create_params.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_create_subagent_call_item.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_deleted.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_function_call_item.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_function_call_output.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_function_call_output_param.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_function_call_status.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_interrupt_subagent_call_item.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_list_params.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_mcp_call_item.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_output_command_execution_output_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_output_item.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_output_item_status.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_reasoning.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_reasoning_item.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_reasoning_param.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_resume_subagent_call_item.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_send_subagent_input_call_item.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_assistant_message.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_created_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_deleted.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_environment_connected_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_environment_disconnected_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_environment_failed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_environment_pending_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_environment_ready_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_environment_reset_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_environment_state.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_error_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_failed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_idle_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_in_progress_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_input_message_param.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_input_param.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_item.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_message.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_message_content.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_requires_action_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_subagent_active_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_subagent_closed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_subagent_created_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_cancelled_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_completed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_content_part_added_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_content_part_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_created_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_failed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_in_progress_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_item_added_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_item_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_output_text_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_output_text_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_part_added_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_part_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_text_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_text_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_text.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_text_param.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_update_params.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_wait_for_subagents_call_item.cpython-312.pyc,, +openai/types/beta/__pycache__/agent_web_search_call_item.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_create_params.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_deleted.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_list_params.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_response_format_option.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_response_format_option_param.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_stream_event.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_tool_choice.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_tool_choice_function.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_tool_choice_function_param.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_tool_choice_option.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_tool_choice_option_param.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_tool_choice_param.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/assistant_update_params.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_apply_patch_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_apply_patch_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_compacted_response.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_computer_action.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_computer_action_list.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_computer_action_list_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_computer_action_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_computer_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_computer_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_computer_use_preview_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_computer_use_preview_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_container_auto.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_container_auto_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_container_network_policy_allowlist.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_container_network_policy_allowlist_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_container_network_policy_disabled.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_container_network_policy_disabled_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_container_network_policy_domain_secret.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_container_network_policy_domain_secret_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_container_reference.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_container_reference_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_custom_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_custom_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_easy_input_message.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_easy_input_message_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_file_search_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_file_search_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_function_shell_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_function_shell_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_function_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_function_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_image_detail.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_inline_skill.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_inline_skill_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_inline_skill_source.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_inline_skill_source_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_local_environment.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_local_environment_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_local_skill.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_local_skill_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_mcp_tool_call_error.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_mcp_tool_call_error_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_namespace_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_namespace_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_apply_patch_tool_call.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_apply_patch_tool_call_output.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_audio_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_audio_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_audio_transcript_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_audio_transcript_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_code_interpreter_call_code_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_code_interpreter_call_code_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_code_interpreter_call_completed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_code_interpreter_call_in_progress_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_code_interpreter_call_interpreting_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_code_interpreter_tool_call.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_code_interpreter_tool_call_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_compaction_compacting_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_compaction_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_compaction_item_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_compaction_item_param_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_completed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_computer_tool_call.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_computer_tool_call_output_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_computer_tool_call_output_screenshot.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_computer_tool_call_output_screenshot_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_computer_tool_call_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_configuration_update_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_configuration_update_item_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_configuration_update_item_param_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_container_reference.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_content_part_added_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_content_part_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_conversation_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_conversation_param_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_created_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_custom_tool_call.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_custom_tool_call_input_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_custom_tool_call_input_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_custom_tool_call_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_custom_tool_call_output.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_custom_tool_call_output_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_custom_tool_call_output_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_custom_tool_call_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_error.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_error_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_failed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_file_search_call_completed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_file_search_call_in_progress_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_file_search_call_searching_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_file_search_tool_call.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_file_search_tool_call_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_format_text_config.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_format_text_config_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_format_text_json_schema_config.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_format_text_json_schema_config_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_call_arguments_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_call_arguments_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_call_output_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_call_output_item_list.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_call_output_item_list_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_call_output_item_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_shell_call_output_content.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_shell_call_output_content_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_shell_tool_call.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_shell_tool_call_output.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_tool_call.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_tool_call_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_tool_call_output_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_tool_call_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_web_search.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_function_web_search_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_image_gen_call_completed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_image_gen_call_generating_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_image_gen_call_in_progress_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_image_gen_call_partial_image_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_in_progress_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_includable.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_incomplete_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_inject_created_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_inject_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_inject_event_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_inject_failed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_content.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_content_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_file.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_file_content.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_file_content_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_file_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_image.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_image_content.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_image_content_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_image_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_item_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_message_content_list.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_message_content_list_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_message_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_text.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_text_content.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_text_content_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_input_text_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_local_environment.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_mcp_call_arguments_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_mcp_call_arguments_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_mcp_call_completed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_mcp_call_failed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_mcp_call_in_progress_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_mcp_list_tools_completed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_mcp_list_tools_failed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_mcp_list_tools_in_progress_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_output_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_output_item_added_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_output_item_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_output_message.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_output_message_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_output_refusal.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_output_refusal_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_output_text.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_output_text_annotation_added_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_output_text_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_prompt.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_prompt_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_queued_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_reasoning_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_reasoning_item_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_reasoning_summary_part_added_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_reasoning_summary_part_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_reasoning_summary_text_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_reasoning_summary_text_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_reasoning_text_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_reasoning_text_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_refusal_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_refusal_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_shell_call_command_added_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_shell_call_command_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_shell_call_command_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_shell_call_output_content_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_shell_call_output_content_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_status.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_steer_accepted_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_steer_error_code.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_steer_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_steer_event_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_steer_failed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_steer_input.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_steer_input_content.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_steer_input_content_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_steer_input_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_steer_pending_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_steer_pending_reason.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_steer_required_input.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_stream_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_text_config.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_text_config_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_text_delta_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_text_done_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_tool_search_call.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_tool_search_output_item.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_tool_search_output_item_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_tool_search_output_item_param_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_usage.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_web_search_call_completed_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_web_search_call_in_progress_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_response_web_search_call_searching_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_responses_client_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_responses_client_event_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_responses_server_event.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_service_tier.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_skill_reference.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_skill_reference_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_allowed.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_allowed_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_apply_patch.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_apply_patch_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_custom.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_custom_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_function.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_function_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_mcp.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_mcp_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_options.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_shell.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_shell_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_types.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_choice_types_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_search_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_tool_search_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_web_search_preview_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_web_search_preview_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_web_search_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/beta_web_search_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/chatkit_workflow.cpython-312.pyc,, +openai/types/beta/__pycache__/code_interpreter_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/code_interpreter_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/environment.cpython-312.pyc,, +openai/types/beta/__pycache__/environment_param.cpython-312.pyc,, +openai/types/beta/__pycache__/file_search_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/file_search_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/function_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/function_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/hosted_environment_file.cpython-312.pyc,, +openai/types/beta/__pycache__/hosted_environment_file_id.cpython-312.pyc,, +openai/types/beta/__pycache__/hosted_environment_file_param.cpython-312.pyc,, +openai/types/beta/__pycache__/hosted_plugin.cpython-312.pyc,, +openai/types/beta/__pycache__/hosted_plugin_param.cpython-312.pyc,, +openai/types/beta/__pycache__/hosted_skill.cpython-312.pyc,, +openai/types/beta/__pycache__/hosted_skill_param.cpython-312.pyc,, +openai/types/beta/__pycache__/hosted_skill_reference.cpython-312.pyc,, +openai/types/beta/__pycache__/inline_capability_source_param.cpython-312.pyc,, +openai/types/beta/__pycache__/input_content.cpython-312.pyc,, +openai/types/beta/__pycache__/input_content_param.cpython-312.pyc,, +openai/types/beta/__pycache__/mcp_transport.cpython-312.pyc,, +openai/types/beta/__pycache__/mcp_transport_param.cpython-312.pyc,, +openai/types/beta/__pycache__/multi_agent_config.cpython-312.pyc,, +openai/types/beta/__pycache__/multi_agent_config_param.cpython-312.pyc,, +openai/types/beta/__pycache__/output_text.cpython-312.pyc,, +openai/types/beta/__pycache__/persisted_agent_tool.cpython-312.pyc,, +openai/types/beta/__pycache__/persisted_agent_tool_param.cpython-312.pyc,, +openai/types/beta/__pycache__/persisted_mcp_transport.cpython-312.pyc,, +openai/types/beta/__pycache__/persisted_mcp_transport_param.cpython-312.pyc,, +openai/types/beta/__pycache__/response_compact_params.cpython-312.pyc,, +openai/types/beta/__pycache__/response_create_params.cpython-312.pyc,, +openai/types/beta/__pycache__/response_retrieve_params.cpython-312.pyc,, +openai/types/beta/__pycache__/session_error.cpython-312.pyc,, +openai/types/beta/__pycache__/session_turn_error.cpython-312.pyc,, +openai/types/beta/__pycache__/setup_command_param.cpython-312.pyc,, +openai/types/beta/__pycache__/subagent.cpython-312.pyc,, +openai/types/beta/__pycache__/summary_text.cpython-312.pyc,, +openai/types/beta/__pycache__/text_format.cpython-312.pyc,, +openai/types/beta/__pycache__/text_format_param.cpython-312.pyc,, +openai/types/beta/__pycache__/thread.cpython-312.pyc,, +openai/types/beta/__pycache__/thread_create_and_run_params.cpython-312.pyc,, +openai/types/beta/__pycache__/thread_create_params.cpython-312.pyc,, +openai/types/beta/__pycache__/thread_deleted.cpython-312.pyc,, +openai/types/beta/__pycache__/thread_update_params.cpython-312.pyc,, +openai/types/beta/__pycache__/token_usage.cpython-312.pyc,, +openai/types/beta/__pycache__/web_search_action.cpython-312.pyc,, +openai/types/beta/agent.py,sha256=SVkuVQIG_R_GFfPLTsGpZ4GF7OcAWd5rYHS5Z6NbTYw,1767 +openai/types/beta/agent_close_subagent_call_item.py,sha256=roXkjqxF8x-jJuD8lFSQSnaUbe8kB6G-tDouIM2OyLU,863 +openai/types/beta/agent_command_execution_item.py,sha256=2jQjfZRKqQh0kAGubRnIayj8c6m2_9rfj9i4xER1FIw,1112 +openai/types/beta/agent_content.py,sha256=D-rHI4aptUxFhYMe-SkZBUT8NEddctAajd-zZiKnLM0,715 +openai/types/beta/agent_create_params.py,sha256=WkQkg3uXVlyuth42KrCY396qv9CY6Yaeuf895VSs3po,2158 +openai/types/beta/agent_create_subagent_call_item.py,sha256=eLNTAXCZ3mzs6Xfi6Zdtek0Mt4NIEu1Jny6pKBAdxY0,1147 +openai/types/beta/agent_deleted.py,sha256=nIf0viflDib3R89UHZx-m6j8bRU-gHkO6PHFHXWFvLg,471 +openai/types/beta/agent_function_call_item.py,sha256=EBAh4TksyqLMPcdmhf-yhdIJ_86pXQcTtwDM0_lQD6U,846 +openai/types/beta/agent_function_call_output.py,sha256=WIUVQtqCRtc00bsmfMM0200Pq--sGdxVny4KpuaGOT4,306 +openai/types/beta/agent_function_call_output_param.py,sha256=IHAxk5H3Ge3ONSadTXho21I9369JpgOCUlSH53UvfNo,1001 +openai/types/beta/agent_function_call_status.py,sha256=HjX6o8LGFFJqubYJhM3CFHmzYWfaQARhBChbP0wxGV0,272 +openai/types/beta/agent_interrupt_subagent_call_item.py,sha256=bZLVG33K-UP_hdPUQ5tekKz_tCR9AMnRKV6opxitP1Y,952 +openai/types/beta/agent_list_params.py,sha256=mfDB0sqvE89IDCIciVfVWRJLFdOTPKa-X8p3dEGrkmA,666 +openai/types/beta/agent_mcp_call_item.py,sha256=DdZsoImil2bVzJ2IJNGtVNFND2QsNIqT5hOY01gBoc4,947 +openai/types/beta/agent_output_command_execution_output_delta_event.py,sha256=E08iwEto98AjMEw_UgLDCLXLWe--tVXWgsGUtBfd6D0,977 +openai/types/beta/agent_output_item.py,sha256=-ObS8uBAs-zQYRJ39vO-X-y2d26vfF2TpYtwSisyg2w,1571 +openai/types/beta/agent_output_item_status.py,sha256=CbBxhtJ65h0wLO-qsYNkf6xxBWS9K6z9dg7nldx8lww,258 +openai/types/beta/agent_reasoning.py,sha256=6Jf38FWZGNCBmiFQEDPlXHpiiA1TDjEh4XTWvLQVjWM,911 +openai/types/beta/agent_reasoning_item.py,sha256=sYFzhfXEnz9TjlaoCJTjThX0oUEVKIA_Fr28bDq8sZ4,800 +openai/types/beta/agent_reasoning_param.py,sha256=l7STJgO43l6U3W9oQwJsQTssmkyuZtxT1HMlXIAhERw,909 +openai/types/beta/agent_resume_subagent_call_item.py,sha256=nNDvmactMQjoe5MGOlIafptilixkfo0Md_-jz2W3dHw,871 +openai/types/beta/agent_send_subagent_input_call_item.py,sha256=EjK5bD7VYBV0OtMGkxZoBd0bkQFGKhjTECg4Vq9jdJM,1051 +openai/types/beta/agent_session.py,sha256=qSPE8UlBGEUxk8sBN55QmdnYArFV_qFjR2sGm9Aio20,4256 +openai/types/beta/agent_session_assistant_message.py,sha256=zWWOp8Vhk0A7cYiVDABwIZDJKacvw9C-DnhT1xDE9Xo,1093 +openai/types/beta/agent_session_created_event.py,sha256=-YDDoe4nCAOC-WhMXl3lUwGqZ2yi9KqxuJmJUDVVNao,562 +openai/types/beta/agent_session_deleted.py,sha256=Cw6WuQiG8H4gyX7RuEe8iBBKa1olewZhvW5BgtlofXs,667 +openai/types/beta/agent_session_environment_connected_event.py,sha256=1tJELFWRJUMzfEg06oB7xa2UavNvBbI-RgWU5UxMakU,897 +openai/types/beta/agent_session_environment_disconnected_event.py,sha256=SPTvk9Hkg3rXYTTkyYUE2Rs1BcdJoeyH8oroov7Xk-U,912 +openai/types/beta/agent_session_environment_failed_event.py,sha256=OG5aa1oWwqa7dPb0NKrOUdK6YGTYUPSGn0YzuZ3C3aM,882 +openai/types/beta/agent_session_environment_pending_event.py,sha256=GP4pAWbj3aiy3OQXCLVYZk432n1h32eIJgZmtZx_D0I,899 +openai/types/beta/agent_session_environment_ready_event.py,sha256=X4XlKGN7BxiDxAWyvbTOMSlpAeHyHNRNeFqqq02haa0,899 +openai/types/beta/agent_session_environment_reset_event.py,sha256=7udoy4vzanWnPaYnl91IWgExUB-aXR4ex-OuJ4f6sQc,1019 +openai/types/beta/agent_session_environment_state.py,sha256=rmonN5xOE37kYGLXcuTQR_Qd2Ru8sBVIk3eO89ugRGk,1201 +openai/types/beta/agent_session_error_event.py,sha256=Uuxb9ptkRRwAWF51xP2ashF_zxwZx6N7ZQweLnSpl0c,602 +openai/types/beta/agent_session_event.py,sha256=hpI5rBtAJb6qYu8_75f_n3pEX5DZGQflG6lLwXl-A7I,4338 +openai/types/beta/agent_session_failed_event.py,sha256=KAYD8FNBJtBZMflwcIprTc8K9NaUCaQpAoT2UDsqa9Y,543 +openai/types/beta/agent_session_idle_event.py,sha256=bTyXduE0O_JDz8cuEOHuLkQnJEeuoGnrjR0Km1x0HFI,552 +openai/types/beta/agent_session_in_progress_event.py,sha256=VFe6jeBlQ0neWIn46K9VRRryB-RDVziZFTSb5MbwEBo,597 +openai/types/beta/agent_session_input_message_param.py,sha256=asfino5-6b0kOXEDL7JXhwKFKIx7wkEc1KeDjZkjqqk,688 +openai/types/beta/agent_session_input_param.py,sha256=nO2nLaJRA-Cx0nOd4fCH2qdLAssS8dK2va2XZmZdpE4,2087 +openai/types/beta/agent_session_item.py,sha256=_mJac2hss554eCY7kl7SWl6zQjMMxPnW6luD8X_og1g,3135 +openai/types/beta/agent_session_message.py,sha256=quF_SOowoDXFaNPNz24Es1R2BTje1KxoFwprMEQD-uQ,1605 +openai/types/beta/agent_session_message_content.py,sha256=mC4kIYhtzxOfTuFFek1SeTDdW_9tBWVn6vwlKaCQ75Y,1405 +openai/types/beta/agent_session_requires_action_event.py,sha256=eAgbfTK9oWTmw8dIhGlXHf07TbaaVuZIZLdhLd8Flqo,641 +openai/types/beta/agent_session_subagent_active_event.py,sha256=bJVVdMPyU5HTImJvIwxIgUf-RcKYWpywJjq6Nf9WV9s,595 +openai/types/beta/agent_session_subagent_closed_event.py,sha256=Co4QyHZ561EIexzAfCmVswdag2xqAXCuNtPPS-LdUOI,580 +openai/types/beta/agent_session_subagent_created_event.py,sha256=pC_b5-pnRixUs71MSyMvDm7iknJChGt2jruhuhGOMBQ,586 +openai/types/beta/agent_session_turn_cancelled_event.py,sha256=zPVtwiyexxX_YCCSsX9GbLWzavzzfGYntvEgXtAWvU0,896 +openai/types/beta/agent_session_turn_completed_event.py,sha256=JeU811dt3GvRKEM7rf3Y0ZyejZ87UdHCBr4sw8RSSPQ,893 +openai/types/beta/agent_session_turn_content_part_added_event.py,sha256=kdBT4WFNG2OLJ8LXiqdRuSnbfu4bCEh272TIlQjEojI,1050 +openai/types/beta/agent_session_turn_content_part_done_event.py,sha256=wcp7ODOXwEJfzkj8x8v0LSB6hctCvrFseGXoG57zL28,1046 +openai/types/beta/agent_session_turn_created_event.py,sha256=K-hGGOFqIMlIWekc07qCR3zcrBUqwuMZdhPM_apTnBY,726 +openai/types/beta/agent_session_turn_failed_event.py,sha256=xIdX_KIQYzHU7uBWmykX7fJ-ehhZxfIZWVx960WZbX4,874 +openai/types/beta/agent_session_turn_in_progress_event.py,sha256=RVqZFApzffWju3_H3nVscwbJQvcInUmlj-5EfN-PNp0,748 +openai/types/beta/agent_session_turn_item_added_event.py,sha256=s5v1aFwVrb9naOuDkEIGrxGeOs1tU0yedOA30pLAoLs,940 +openai/types/beta/agent_session_turn_item_done_event.py,sha256=fy-r9X7F95LfqTgq5oqKKg8gN7g2AmXVhGX94KJHaCc,894 +openai/types/beta/agent_session_turn_output_text_delta_event.py,sha256=TyuYBt10utoIvTPF4IBwfuynKHFv5x2SvGkCwv6kx60,1017 +openai/types/beta/agent_session_turn_output_text_done_event.py,sha256=MhzU5qkX3ZEA5F2ZLGT4E9xvL4_2qIjaPwupntNApPQ,1002 +openai/types/beta/agent_session_turn_reasoning_summary_part_added_event.py,sha256=jXMsmwaGWRhhiKa3juMJ7c23CarFVu_cbxFNfZm1P8c,1101 +openai/types/beta/agent_session_turn_reasoning_summary_part_done_event.py,sha256=C20m6e5lz1WfzZsdvA8L6EbQKpBfgVGeMhY99P1lLmc,1213 +openai/types/beta/agent_session_turn_reasoning_summary_text_delta_event.py,sha256=Mc75W40cQs4uz1-2kgt5b-GdscVg6tw5aaVptJr-RTo,1064 +openai/types/beta/agent_session_turn_reasoning_summary_text_done_event.py,sha256=ov4fhFFbFGrbNcaOO3JlKcupAypgny9EcarQG-x0WUY,1065 +openai/types/beta/agent_text.py,sha256=rmp4g_DsviBP-x8a5JNLhMTSP4lRv34JHN78CK_hlu0,513 +openai/types/beta/agent_text_param.py,sha256=Dv7JX5f-6f06MlJfk7YgnvGRxNsWLG2tOtBU6EzIX_s,786 +openai/types/beta/agent_tool.py,sha256=1vqsQi4cdocb0yXs41FzQg-zbJ7xe3lP_OsWTBetiBw,3654 +openai/types/beta/agent_tool_param.py,sha256=Cg3z1yyf0EhZhWRtjRujyk3e8nJVv9Ts9vDh7_EkJTA,4613 +openai/types/beta/agent_update_params.py,sha256=poveIT7ejuWjkF6z5WgZM6CESpqI3xnJxpRatdFt_Ak,2053 +openai/types/beta/agent_wait_for_subagents_call_item.py,sha256=nixsK0MACXRJWbLDNGiBOMeRknc6Mkv6CdA55-Qv1qg,930 +openai/types/beta/agent_web_search_call_item.py,sha256=KtkAshOGECSoyfJ8hFAf6Naf0-BIc8wlY5tMo2_aMqI,821 +openai/types/beta/agents/__init__.py,sha256=-DMOeyvImA_RZdvNDZSbYMC0n0eCGmGp3AjbB3UV96A,784 +openai/types/beta/agents/__pycache__/__init__.cpython-312.pyc,, +openai/types/beta/agents/__pycache__/environment_info.cpython-312.pyc,, +openai/types/beta/agents/__pycache__/session_create_params.cpython-312.pyc,, +openai/types/beta/agents/__pycache__/session_list_params.cpython-312.pyc,, +openai/types/beta/agents/__pycache__/session_update_params.cpython-312.pyc,, +openai/types/beta/agents/__pycache__/vault.cpython-312.pyc,, +openai/types/beta/agents/__pycache__/vault_create_params.cpython-312.pyc,, +openai/types/beta/agents/__pycache__/vault_deleted.cpython-312.pyc,, +openai/types/beta/agents/__pycache__/vault_list_params.cpython-312.pyc,, +openai/types/beta/agents/__pycache__/vault_status.cpython-312.pyc,, +openai/types/beta/agents/__pycache__/vault_status_filter_param.cpython-312.pyc,, +openai/types/beta/agents/environment_info.py,sha256=Kc-6y2ALtvsVwTfvUA_StfbG52UF2vIAOoHYmRyDGos,1198 +openai/types/beta/agents/environments/__init__.py,sha256=1NsSHqkXZf59t8WO_JlJv3DZMa3KLRQw-Lxi8CF4U1U,732 +openai/types/beta/agents/environments/__pycache__/__init__.cpython-312.pyc,, +openai/types/beta/agents/environments/__pycache__/environment_file.cpython-312.pyc,, +openai/types/beta/agents/environments/__pycache__/environment_template.cpython-312.pyc,, +openai/types/beta/agents/environments/__pycache__/environment_template_deleted.cpython-312.pyc,, +openai/types/beta/agents/environments/__pycache__/file_create_params.cpython-312.pyc,, +openai/types/beta/agents/environments/__pycache__/file_list_params.cpython-312.pyc,, +openai/types/beta/agents/environments/__pycache__/template_create_params.cpython-312.pyc,, +openai/types/beta/agents/environments/__pycache__/template_list_params.cpython-312.pyc,, +openai/types/beta/agents/environments/__pycache__/template_update_params.cpython-312.pyc,, +openai/types/beta/agents/environments/environment_file.py,sha256=jMEnHKu4IA8hTaan9ACXYQaXJ_7yIfzGFG-hSC_t2Yc,607 +openai/types/beta/agents/environments/environment_template.py,sha256=XepGh3RiZ3xVqQhyKAwyK4jQ7IU6Y9ohx8I7sp2Q9dU,4385 +openai/types/beta/agents/environments/environment_template_deleted.py,sha256=Nt9AFBJuD_z1GKqBaLb_iINRS7D3zN-Z5A6SUm7buI0,588 +openai/types/beta/agents/environments/file_create_params.py,sha256=sllUkh8J_wqBeprTnekC1hgBivf1i_RQha6nelxwNW8,1056 +openai/types/beta/agents/environments/file_list_params.py,sha256=tmlum84158MJs_Yk5V2H1yOUVHkO55SMOowbYTLhPGs,789 +openai/types/beta/agents/environments/template_create_params.py,sha256=Cuwv6oxXrm2s2EvbOxflJHhU2X7U-iTk1zISfHAxqyk,2847 +openai/types/beta/agents/environments/template_list_params.py,sha256=8sLPRrbNlc-3LSGjimsMMmccrlLV4mi9MOrE2SNcuFI,669 +openai/types/beta/agents/environments/template_update_params.py,sha256=lflalCTkZ5BhWtdqiTs3b1s1wtmUILEzETRviUZ_wpg,2871 +openai/types/beta/agents/session_create_params.py,sha256=kOExMMuwVctpBoYkevukZse32PXSHvU_mERtvsNGjPA,3795 +openai/types/beta/agents/session_list_params.py,sha256=2yxmp2WYtMnw6Q5KhV3SgR99TEfRrkwjGO4fH83ZUUw,870 +openai/types/beta/agents/session_update_params.py,sha256=ah8ZstdE5GEGEPYgUOCzc7c2bdWor9Nww4sfJq6NXUY,1713 +openai/types/beta/agents/sessions/__init__.py,sha256=oSqGxj6-jQT-phQ5aikrlSCD1oNman64JXzEV0GL2Cs,653 +openai/types/beta/agents/sessions/__pycache__/__init__.cpython-312.pyc,, +openai/types/beta/agents/sessions/__pycache__/artifact_list_params.cpython-312.pyc,, +openai/types/beta/agents/sessions/__pycache__/event_create_params.cpython-312.pyc,, +openai/types/beta/agents/sessions/__pycache__/item_list_params.cpython-312.pyc,, +openai/types/beta/agents/sessions/__pycache__/session_artifact.cpython-312.pyc,, +openai/types/beta/agents/sessions/__pycache__/session_artifact_deleted.cpython-312.pyc,, +openai/types/beta/agents/sessions/__pycache__/subagent_list_params.cpython-312.pyc,, +openai/types/beta/agents/sessions/__pycache__/turn.cpython-312.pyc,, +openai/types/beta/agents/sessions/__pycache__/turn_list_params.cpython-312.pyc,, +openai/types/beta/agents/sessions/artifact_list_params.py,sha256=AbC0zYUOlXgHvndP3iNpYsRITfq-si66thOf6-7iYCY,789 +openai/types/beta/agents/sessions/event_create_params.py,sha256=u_w_7tjkePr-X1o6V8Z_OvfSTkJvJGn9HrZH0MmWXT4,581 +openai/types/beta/agents/sessions/item_list_params.py,sha256=0U3JQH_hnXHqH21mEUXP4h8cLiUVhIkjSKp260449rg,661 +openai/types/beta/agents/sessions/session_artifact.py,sha256=yOMvpKfSpJ0IHuOS0CO3OY7EZC_MPtoVCJUs21lAsFk,962 +openai/types/beta/agents/sessions/session_artifact_deleted.py,sha256=r3aOh7_RBEPWcQ3rs9sLWeS_JmF8v-dEwT24-4WB_gk,584 +openai/types/beta/agents/sessions/subagent_list_params.py,sha256=_8SC2A7zpQYO3x785F3RmczKerNw0Aj4zBsv3UNLnaY,669 +openai/types/beta/agents/sessions/subagents/__init__.py,sha256=rPru1JHdwGS0Oq8dVqec7AxKiClqBRbigRQeoSG3V9Q,248 +openai/types/beta/agents/sessions/subagents/__pycache__/__init__.cpython-312.pyc,, +openai/types/beta/agents/sessions/subagents/__pycache__/item_list_params.cpython-312.pyc,, +openai/types/beta/agents/sessions/subagents/__pycache__/turn_list_params.cpython-312.pyc,, +openai/types/beta/agents/sessions/subagents/item_list_params.py,sha256=ORe5QaN-QwtYb_S3OL1EcC_oCm4J0hHElDiWLs7sJyg,702 +openai/types/beta/agents/sessions/subagents/turn_list_params.py,sha256=ihLmU92-P-ZzgpTiGQysZir8wtvcKBjOv1pHP0eCwfE,702 +openai/types/beta/agents/sessions/subagents/turns/__init__.py,sha256=O9sqC3nIiQ1MjhqNWwD8MO3FSCzz6qyi1-JmyaEWGPE,185 +openai/types/beta/agents/sessions/subagents/turns/__pycache__/__init__.cpython-312.pyc,, +openai/types/beta/agents/sessions/subagents/turns/__pycache__/item_list_params.cpython-312.pyc,, +openai/types/beta/agents/sessions/subagents/turns/item_list_params.py,sha256=McW9DvyFE0a7sI6gT-opH3sXSAG7dy1tbk5dEHa3ZkE,734 +openai/types/beta/agents/sessions/turn.py,sha256=bsv2D3XCsFUDlqQ5baDJqsEv-qY_WxM4njB4Pl_bNhU,1945 +openai/types/beta/agents/sessions/turn_list_params.py,sha256=7jYSaxl2QdyfAzZbkPYS4XlDLQY3F7NwmZem2yLtkdA,661 +openai/types/beta/agents/vault.py,sha256=vC4oV6MrYoQ1a0F5VZHSVHnJ8Vibc1M7Ir4wwOn_pXY,762 +openai/types/beta/agents/vault_create_params.py,sha256=VdDqme3Qmk2wWCYtnKIXJfNUwNyZs5ssK6F02HkRyPI,555 +openai/types/beta/agents/vault_deleted.py,sha256=QTPUW2zA45CmZzeP_70_fL_ttLagWSy46Nqdb4iKhhw,488 +openai/types/beta/agents/vault_list_params.py,sha256=pNMbwbNZQUD5x-r5Q6vLWz2dbVbbugWirkhDnq09gyk,1058 +openai/types/beta/agents/vault_status.py,sha256=SM--rCdzoZDU765OV75LU6cTdMTRLJLIyoxClpKl9go,218 +openai/types/beta/agents/vault_status_filter_param.py,sha256=0Fz4fa_QUlIXiZP4ZwLSeYmsFDLRsBd_q83UA3JF480,353 +openai/types/beta/agents/vaults/__init__.py,sha256=SszksU3J_Rw3vz8EyK5ZYYPy0cLle9RUL0qD9bQYjX4,1311 +openai/types/beta/agents/vaults/__pycache__/__init__.cpython-312.pyc,, +openai/types/beta/agents/vaults/__pycache__/credential.cpython-312.pyc,, +openai/types/beta/agents/vaults/__pycache__/credential_auth.cpython-312.pyc,, +openai/types/beta/agents/vaults/__pycache__/credential_auth_create_param.cpython-312.pyc,, +openai/types/beta/agents/vaults/__pycache__/credential_auth_rotate_param.cpython-312.pyc,, +openai/types/beta/agents/vaults/__pycache__/credential_create_params.cpython-312.pyc,, +openai/types/beta/agents/vaults/__pycache__/credential_deleted.cpython-312.pyc,, +openai/types/beta/agents/vaults/__pycache__/credential_list_params.cpython-312.pyc,, +openai/types/beta/agents/vaults/__pycache__/credential_networking.cpython-312.pyc,, +openai/types/beta/agents/vaults/__pycache__/credential_networking_param.cpython-312.pyc,, +openai/types/beta/agents/vaults/__pycache__/credential_update_params.cpython-312.pyc,, +openai/types/beta/agents/vaults/__pycache__/mcp_oauth_token_endpoint_auth.cpython-312.pyc,, +openai/types/beta/agents/vaults/__pycache__/mcp_oauth_token_endpoint_auth_create_param.cpython-312.pyc,, +openai/types/beta/agents/vaults/__pycache__/mcp_oauth_token_endpoint_auth_rotate_param.cpython-312.pyc,, +openai/types/beta/agents/vaults/credential.py,sha256=VvQEI2P9SVdireDrilirYqORQoe8Ua40zkcuWNOhBZ4,1068 +openai/types/beta/agents/vaults/credential_auth.py,sha256=GDA101jVuGPtqN0knO6icZAMQQtscpehoXSXr5gCM4s,3315 +openai/types/beta/agents/vaults/credential_auth_create_param.py,sha256=0ckTZk6NLil8mN9GaC_KdC_2_PrjIObhBM5_VPmvnYY,4340 +openai/types/beta/agents/vaults/credential_auth_rotate_param.py,sha256=QvPt5CZrVX8iHw1eO9_Exw0VJPlzO60FxrATZeF-RvI,3143 +openai/types/beta/agents/vaults/credential_create_params.py,sha256=DBjScJQ7Yyzu3Vig0FTblD6hHzoEP2mlWZHELb8sG9Y,779 +openai/types/beta/agents/vaults/credential_deleted.py,sha256=3mwfN_GWlvOmYiE-OyPZ9EfREPBTooXuBOaEsV76kGs,537 +openai/types/beta/agents/vaults/credential_list_params.py,sha256=O4-iqaJw29Y_w4pmK9Znk09DNGF3winW4lFoYXxRZy8,1069 +openai/types/beta/agents/vaults/credential_networking.py,sha256=-32XMEm_c0rdZ4AwI3lwVB5iPUtDuGIj8g8EN9UdWkk,1431 +openai/types/beta/agents/vaults/credential_networking_param.py,sha256=4HUZqNRnys47AVHZDs6hFsktEs4R4jXkkVGILfpXe0U,1444 +openai/types/beta/agents/vaults/credential_update_params.py,sha256=HNOs8C8Rd_YkXU68QsrBq1c9RB-yBCkyTIZt-9TKYYI,713 +openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth.py,sha256=fBcDEwjm2HDqsV24kkZTEcyHrU4LRD4BNXm_kKZ1Oeo,1412 +openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_create_param.py,sha256=2gj5IOXuXSCZeVFfChoDk-RfPPiRLmF_--3dhKQWMdc,1665 +openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_rotate_param.py,sha256=vdlpe2V3BKZSqwjmFqKrrh8fMfYxCOuQGn6g_LdmT54,1485 +openai/types/beta/assistant.py,sha256=69ymHfjWsdsNiFWasv5qCf2_6aSUHG0rJXLAk3ZhxlU,5429 +openai/types/beta/assistant_create_params.py,sha256=lhowTNmIzc3VxqFVEiOPgL4yIfb_sZai3o6odO_jLz0,8658 +openai/types/beta/assistant_deleted.py,sha256=MuZEretf0k5TUZtEODTWaUIV6O3M0R8xQ8Ko87m-OPI,300 +openai/types/beta/assistant_list_params.py,sha256=4ena53f4oii1OWF3E6oL6cONQr8iLX9X2fwMWrqxQ6Y,1221 +openai/types/beta/assistant_response_format_option.py,sha256=Se53vr8x_gK3rntEYZI4UWC1GZHos6AMJbUGlRV-AXI,560 +openai/types/beta/assistant_response_format_option_param.py,sha256=UUtwj1UPVy7DgAi3MXuzCLf3T81CqnE4R9WwSVg4u4I,627 +openai/types/beta/assistant_stream_event.py,sha256=QauZbnbsDUh2M2v5IZm7IoiupQJIPe9K6IZAYDEtaP4,10064 +openai/types/beta/assistant_tool.py,sha256=qAmwO47Ff7N9oPCLLHYP7zZHB0BgCBPa30TspHqqVBM,505 +openai/types/beta/assistant_tool_choice.py,sha256=niSZ-nafMjvnT1gX-SuZF6YPK6y3WYVyGQtO-AJVzGk,651 +openai/types/beta/assistant_tool_choice_function.py,sha256=BfOghHvUa3cbqMbQCEu8aJuS79xgH7rlhe1z5AJ_Acc,268 +openai/types/beta/assistant_tool_choice_function_param.py,sha256=sE9N44LnQ4lvWB9upRAdWOSJu8tcWpjhV8JFdzUHJLM,354 +openai/types/beta/assistant_tool_choice_option.py,sha256=pB9_CmTIWKRqU7Z1ypI6v5YV70WpddZUSC5WZoKXHFo,361 +openai/types/beta/assistant_tool_choice_option_param.py,sha256=JSKVWRW-rnUZOuRXlnlEwDahtdK880yTrniAs_ZHgzM,423 +openai/types/beta/assistant_tool_choice_param.py,sha256=zlHTLJ1LUV1XLqY398sNr_ThBH8i_l9X4X2Y-WMLvJY,679 +openai/types/beta/assistant_tool_param.py,sha256=ylLO07B4Bo3iqYnzPeBWZcHB0-ifTlUgk4GwWvJnvCQ,500 +openai/types/beta/assistant_update_params.py,sha256=I6bOO2IQ5Y5XlV1hI7UnU2clsIhEfWIEdNg6D845ovQ,7077 +openai/types/beta/beta_apply_patch_tool.py,sha256=4QebZ_ioZuhVKm1fVfY8aesWatkocR8jDQaeIPvMh98,561 +openai/types/beta/beta_apply_patch_tool_param.py,sha256=INTFq-tzT5_MlaXrHQS8W2LCfxHuoRzUGykf6o2iOno,610 +openai/types/beta/beta_compacted_response.py,sha256=kR8dciqh0q2Q3NQcdjM3bzkqNQG42ZBf9WJUs_oQQhY,967 +openai/types/beta/beta_computer_action.py,sha256=60tajhcRcZoReyuLAALP6e8aiwAFGWz9d_8lZc2cLP4,4271 +openai/types/beta/beta_computer_action_list.py,sha256=gv7WeHRIyohPGttKj4mm24Qmm5dO4cSrJJp9owfc6Rk,304 +openai/types/beta/beta_computer_action_list_param.py,sha256=aKltLdFE6glbWqdz1JPCgqe5Ww2G7qZRr26TKRVk55Q,4779 +openai/types/beta/beta_computer_action_param.py,sha256=7G2_c5BVjMcTw83dynuu9dF8r8-vQvXwiBgbzId_dik,4666 +openai/types/beta/beta_computer_tool.py,sha256=x1E5bXGWdiwusx9e8B6SBqttsx9DaeeEoGydTPJ_BYs,483 +openai/types/beta/beta_computer_tool_param.py,sha256=fCf0i3nngRqh6yf-6AFI_NsW_yBt9lEM_mcGFkspLCs,539 +openai/types/beta/beta_computer_use_preview_tool.py,sha256=QdF_qk3VgIEH8RzrIbn_V1ZtmtVw_kRhiS1hqY3XFUw,800 +openai/types/beta/beta_computer_use_preview_tool_param.py,sha256=MUoMc6SRSxqBDx6QROKHNPEF12tF2x-XOrWLvSTvY98,886 +openai/types/beta/beta_container_auto.py,sha256=dDVzAJImlKQQkhMejHiq87flTqoiPnYaLUdqc61Ot7I,1446 +openai/types/beta/beta_container_auto_param.py,sha256=spY_I48N7q51dc9Cive0-dOcJw3RfX9TFoiDQ1pAvKM,1418 +openai/types/beta/beta_container_network_policy_allowlist.py,sha256=DgrGqcG8O5segXSfukvmzOYM0kNi0uT86Lmuta-vfXA,756 +openai/types/beta/beta_container_network_policy_allowlist_param.py,sha256=TpflB0dSi6juTAQ9yNkpoJLzhDDYZM68JGU2TNjvyC8,867 +openai/types/beta/beta_container_network_policy_disabled.py,sha256=LXFxOeJCp0qjfKwEr9Rq23oTk3pv9EXOfjg307s-E7c,355 +openai/types/beta/beta_container_network_policy_disabled_param.py,sha256=uMBBI35uZoQKctA_uI5ntxjHr6Ou-oylmtn2s7oUhQE,411 +openai/types/beta/beta_container_network_policy_domain_secret.py,sha256=bC4mSD-V1Vd8NIC7s9IUPfe6fVrdCLv4aBG4E46aZ-M,440 +openai/types/beta/beta_container_network_policy_domain_secret_param.py,sha256=PnJ5IKHAE1c2AGeun0UUVpv0sv4WyulJ4YuBHMR0_0k,546 +openai/types/beta/beta_container_reference.py,sha256=TcZSnr1b0FBN3ymM4aPaL7H7n9Caeqx7x0nlcG2EIuY,423 +openai/types/beta/beta_container_reference_param.py,sha256=TP6z9v7W3yevS33WdlhJEbJfWZ5kxhAxg3XTrWlj6J4,489 +openai/types/beta/beta_custom_tool.py,sha256=4ZsqMdWNrMrjC3r1LAa-kNlXCGQPvTFKfNeVbK2qQZE,2020 +openai/types/beta/beta_custom_tool_param.py,sha256=1KM84JG3Gg3jQiJsIHK0RlxVkKG236Irm-h1c63z2uQ,1933 +openai/types/beta/beta_easy_input_message.py,sha256=1AyjBnT3ugTi3kkZ1cA6apcIn1Evkb-rkItAmHWPqGE,1595 +openai/types/beta/beta_easy_input_message_param.py,sha256=fhtdZbUIFjgPYqnT8AIJIwJdv39RmC3SJlBIMZJ_BEY,1654 +openai/types/beta/beta_file_search_tool.py,sha256=1Bs6OLoeVui38G11o-uhcLfnkZouZv46eJFWPjDq-3s,4348 +openai/types/beta/beta_file_search_tool_param.py,sha256=bSyDlVDDVbnfHGJciKVllKU40R5X7vH5Fv6ZLo-Xn2Y,4557 +openai/types/beta/beta_function_shell_tool.py,sha256=YCX0H6saXzC78_GxQRnX6i1dGVfDJcvyrrumPJ5d1Xg,991 +openai/types/beta/beta_function_shell_tool_param.py,sha256=YEC_yQiSCLwqZ7OZ6wh54gObcGFv2yKZx2qgaiIsg-c,977 +openai/types/beta/beta_function_tool.py,sha256=wOQEZp-maZ5JxbnLzITIEbGnHWqlg4GWRq3FFlISb3U,1521 +openai/types/beta/beta_function_tool_param.py,sha256=comoFen2TGjQR2JRQqXR9Y73fk_NRcFGWec9-3XIJ20,1628 +openai/types/beta/beta_image_detail.py,sha256=ExGJm7mVRPTUFLzSNtXwaUbpKArO_JdwzL3Q42EpRlw,239 +openai/types/beta/beta_inline_skill.py,sha256=NisoAwsay7Z8nGK9o7F1Gtq2hbgYQtxdT8qkPGkYSVg,541 +openai/types/beta/beta_inline_skill_param.py,sha256=jbil0mEfeTyGYoCIPLBFcUiSbZYQULL_cEna-LBhAeo,644 +openai/types/beta/beta_inline_skill_source.py,sha256=TzyO4kmy-H10nyGeW5G2sjXVbGQWsgJFg3HqkKs3iDo,545 +openai/types/beta/beta_inline_skill_source_param.py,sha256=z004R-vtbJTNpWvaGxJfMPxg7qHK9trt6qIxYPmNaRY,621 +openai/types/beta/beta_local_environment.py,sha256=s-jJPsskF4PjGei9IinVfFjDSAgfQCR2V6Pfacmef8Y,474 +openai/types/beta/beta_local_environment_param.py,sha256=86r3lBNXdvNxRya9lcAZh7jBv4Q5JDFgKdgqLTX53TM,528 +openai/types/beta/beta_local_skill.py,sha256=blbNHr4vV4DbWDr30HHtEgnrc0wQymw3rzyVpVBgWqE,366 +openai/types/beta/beta_local_skill_param.py,sha256=IjxXgTmEna2iig3zmko4_zvfMIRP3CzO9oso-upsPGY,472 +openai/types/beta/beta_mcp_tool_call_error.py,sha256=3anRO71kgEQiU9jwmf_f_VOjKlkXkkmcbjeNCdN3HfA,795 +openai/types/beta/beta_mcp_tool_call_error_param.py,sha256=7Vq_ZGIeqrlatTuws650HJou98BUFt2Hx4LYTl-n5Uc,848 +openai/types/beta/beta_namespace_tool.py,sha256=PtUnkBDS4mSWy3LrQcO8VW2yrssU1z6es2UPRmqPfVE,2024 +openai/types/beta/beta_namespace_tool_param.py,sha256=6L3kfcgDo2dsUfIgf3DKQRPVjTLSPxc9gpuFyz9qZ8s,1939 +openai/types/beta/beta_response.py,sha256=zbJmPqMn0-y78DLOUJHKCYFtuy0EsTJuwTKyDUZq7LE,25708 +openai/types/beta/beta_response_apply_patch_tool_call.py,sha256=dmSWdQtL9ZIWlE2AmxWlbmTPBsv7plH0KF1D70eZRXU,2956 +openai/types/beta/beta_response_apply_patch_tool_call_output.py,sha256=pZataIAJCxrXgq2GRxi-jXVLnAC_he_co2fNfp9OmlU,1811 +openai/types/beta/beta_response_audio_delta_event.py,sha256=pc0eIrvm6ae08rFaGWz6CX3a5ggOs3xYQ3zIuDfiRqE,895 +openai/types/beta/beta_response_audio_done_event.py,sha256=pYZRFTkaCs-MUMHLd17EZyfzoI5myDk4vcq1VIO7erI,791 +openai/types/beta/beta_response_audio_transcript_delta_event.py,sha256=SGR8Or0MTX-KPv0JKiQ71_YprbTAO1_5EBhxJagPKU8,918 +openai/types/beta/beta_response_audio_transcript_done_event.py,sha256=sqOlCI6cWHBMazaug8lIis6XkvBDb8M43Fylx9Fl8nQ,842 +openai/types/beta/beta_response_code_interpreter_call_code_delta_event.py,sha256=eawpd6bcW6bwTVrBr1ifWk2sXNEOgiIJ5FPDAQ5R9C0,1245 +openai/types/beta/beta_response_code_interpreter_call_code_done_event.py,sha256=odFUT3X7-i3q4V1_ctunCv9qPZhj3OqS1V07M9VmYNw,1206 +openai/types/beta/beta_response_code_interpreter_call_completed_event.py,sha256=2ZnSWl_ziR1uffIs-yeOAtIQsyaaOzMKeJzKRNiKtUA,1144 +openai/types/beta/beta_response_code_interpreter_call_in_progress_event.py,sha256=Qi93xxc_NHYblONQwyFLOJtSi36JRVoWqTXIZ7FV-9c,1152 +openai/types/beta/beta_response_code_interpreter_call_interpreting_event.py,sha256=Q5K2BqL7AQsyPRNENViMaqsX-ohL42zQPHCIhR_2aSc,1183 +openai/types/beta/beta_response_code_interpreter_tool_call.py,sha256=VgwtcqsWOJRVMV2E3jqFP7ajztMHmewQTY63YwJ4724,2050 +openai/types/beta/beta_response_code_interpreter_tool_call_param.py,sha256=cYMgVw_hxvL3lW-7AO-K_kcYr6WoOVBettQG1L4iEaA,2139 +openai/types/beta/beta_response_compaction_compacting_event.py,sha256=Tk-lhDRtYc3sn3V6YICpru12M3gMBVzTJlULrkSFKhk,1056 +openai/types/beta/beta_response_compaction_item.py,sha256=VU-M6nkWw4o_RPx1gi58rw0MqQZ6ARcz6rgZvCkgUKA,1028 +openai/types/beta/beta_response_compaction_item_param.py,sha256=oXlZehlnkOfVlCqpV6M8vIy23teswW1WpbaNWIdjg-g,943 +openai/types/beta/beta_response_compaction_item_param_param.py,sha256=XKGlaGx7zdGcfZKiOb-UwA2vnRyg0e58xPzTasfMhyQ,1018 +openai/types/beta/beta_response_completed_event.py,sha256=FKV63yEhLp9wx9leLU-43-k30rNzK8llw77fNHszMuQ,907 +openai/types/beta/beta_response_computer_tool_call.py,sha256=xYsxlxMMAPLi_vegtIhCPC4DyYAwnofaMrUaJnRY8ck,2005 +openai/types/beta/beta_response_computer_tool_call_output_item.py,sha256=YOz7bgZToOJkzdRuBhrjGmCnySVTyfn7peQg1sc2emg,1944 +openai/types/beta/beta_response_computer_tool_call_output_screenshot.py,sha256=1xGn__sw85jSB7OkiRVqyzKJsePsk_cBHXXS-Rq5pzE,741 +openai/types/beta/beta_response_computer_tool_call_output_screenshot_param.py,sha256=AsH_GR7WMiiMAImLTS9KlhsIi3rTqVp7-czUnIZRNVo,735 +openai/types/beta/beta_response_computer_tool_call_param.py,sha256=NlLXlru_kzvDsRzQ7XxfTryJ7r9Y0jPfkQpwp0zlz_s,2133 +openai/types/beta/beta_response_configuration_update_item.py,sha256=S9l-MucQ7b1oi_jG6sDdjyTR0KZNpg79pSL_76B5AEY,1280 +openai/types/beta/beta_response_configuration_update_item_param.py,sha256=6wbxB7J-8fo6kma_r4khtoOE-vfA3Q0wDlVHZ7t67TA,1390 +openai/types/beta/beta_response_configuration_update_item_param_param.py,sha256=PJvNLX5zCxArBnRCTlbL015r7QZkpYrMGDBGbuoo0r4,1444 +openai/types/beta/beta_response_container_reference.py,sha256=noLVSq6pvFEdYYFiunZ3hYQoVxvXmF63HLFLS_ytyfw,444 +openai/types/beta/beta_response_content_part_added_event.py,sha256=wT_XUYws3jxYJ2uwAAJ6Oq29MJ56k9dqLY3Lgo38ffk,1761 +openai/types/beta/beta_response_content_part_done_event.py,sha256=op00TFdEhAgCLyPYZngcuK8iqFFlryRHQVTJYUvAvJs,1748 +openai/types/beta/beta_response_conversation_param.py,sha256=aBMpvWCfeKPtOK55MbzjqG2zMln9Svs7EXW2mkff9mU,330 +openai/types/beta/beta_response_conversation_param_param.py,sha256=7z1vdLkzD6t_rbbpmC-MJxdYcklMz6a-Cm5g32J500o,416 +openai/types/beta/beta_response_created_event.py,sha256=Zbic1C4V2k1AaTARlnm4-UyA0tdYvBLm7o1l8eQChtE,900 +openai/types/beta/beta_response_custom_tool_call.py,sha256=N9VQHH3l_Ohr-Anshmssz8P-PMR4I60AIo0QVS51VhQ,1838 +openai/types/beta/beta_response_custom_tool_call_input_delta_event.py,sha256=mMeoqiakK6yr_Azg2vgmV17IOLeRyupG7m4t-tsNjko,1107 +openai/types/beta/beta_response_custom_tool_call_input_done_event.py,sha256=RP-yFihG32cyaBgt4Oexrv6O0fh5_5bCJVmF7TUA6_U,1077 +openai/types/beta/beta_response_custom_tool_call_item.py,sha256=hiD1hlqeoAvZ1CKBisixzqQvyW_ULw0yAWvRnR920P0,782 +openai/types/beta/beta_response_custom_tool_call_output.py,sha256=UGOxgmU8V8jpEK-lOc-qWu6HnyE7b74DL4o9nST_pSc,2185 +openai/types/beta/beta_response_custom_tool_call_output_item.py,sha256=qbVJCVMAMfP4vZlqPL6zZ32tBun8EWbAlLPOx6xLQ1U,853 +openai/types/beta/beta_response_custom_tool_call_output_param.py,sha256=dC6TgG8NURHunuZmQg4uAioGjD3BB590D6hcPy7g0Gg,2220 +openai/types/beta/beta_response_custom_tool_call_param.py,sha256=iVS6LsHVMelsiOUAFgd3ElhLucT72dPd32jf-ZfRA0E,1900 +openai/types/beta/beta_response_error.py,sha256=zFcFKseUhnagGKbPi397UyDHTdus5D6T_LeTq53ZZhc,1962 +openai/types/beta/beta_response_error_event.py,sha256=yd2jRRZ8GTSbngg2UYn0qzFpNdZdeAlevIQjmFScn7g,910 +openai/types/beta/beta_response_failed_event.py,sha256=iHoilNAQrRXXbSSzuNc8i9XO5DSHvGzmJHQD3aduUQ8,885 +openai/types/beta/beta_response_file_search_call_completed_event.py,sha256=y01O_HJg4HwOz_Opi-rtPyKAvvVeTD00CUQBqOcRGhY,1065 +openai/types/beta/beta_response_file_search_call_in_progress_event.py,sha256=lEeH99uhaHrtX3u9oPyL29sOVXk-qD8Yc-ZuHz3ewoE,1055 +openai/types/beta/beta_response_file_search_call_searching_event.py,sha256=mXaomtrHRp2hVLSIsvCf116MScn1BzXck2cwc4FdicI,1054 +openai/types/beta/beta_response_file_search_tool_call.py,sha256=iGJVU9p_YNVq93rGedMiVzHGYaX4Xx9GCXAwdXG9R0w,2099 +openai/types/beta/beta_response_file_search_tool_call_param.py,sha256=s1vS4LprGuPafzSaEkZ7JBRfkuL_R3RhHd8eJ0r7Fy8,2188 +openai/types/beta/beta_response_format_text_config.py,sha256=kTAuSD1OC1IVWd7hcmBf66fyp-VHLMEjs0ozNTdRQlY,1163 +openai/types/beta/beta_response_format_text_config_param.py,sha256=ZGbhKmimS0bnds_8f8gKaYhYq82GTf19JMJKR4imXlo,1160 +openai/types/beta/beta_response_format_text_json_schema_config.py,sha256=XvXDQ8RUbhBc2Ie9xeVu2I2XmW_LNwM4Gi88OO6A1iQ,1630 +openai/types/beta/beta_response_format_text_json_schema_config_param.py,sha256=75yRZOPQVktTSLYU2f6D3EvmkOtffviAMwfJHQKoe_I,1612 +openai/types/beta/beta_response_function_call_arguments_delta_event.py,sha256=lU-OTBdKuuY5bAQ1VqYFhkgItwoFqMTbY1_-FoTfQLI,1187 +openai/types/beta/beta_response_function_call_arguments_done_event.py,sha256=M2gieSw85DtfnkN_suYwTAsA06e_9crxQKJbrXa9Gcs,956 +openai/types/beta/beta_response_function_call_output_item.py,sha256=90Hrt7pmiVtr5eciKrFOK7GOrkvNfc9mFQRsUBwR66I,679 +openai/types/beta/beta_response_function_call_output_item_list.py,sha256=RSf_812vpEN05qbisuNEn0D-8gsPL2IHOalxjNJM8N4,387 +openai/types/beta/beta_response_function_call_output_item_list_param.py,sha256=soVw0SgicL-BB2KxHIy4qY3WHnDNJpqUZP7wTnD0ySw,832 +openai/types/beta/beta_response_function_call_output_item_param.py,sha256=84LFLFVuYYY0NiIQ2SggkU4QRQf_4fkAnXTf76tpkII,675 +openai/types/beta/beta_response_function_shell_call_output_content.py,sha256=sxcc6cH37bC_o7Rt9M4OJ-fLgKK8dCRCtIkzDhYWvv4,1267 +openai/types/beta/beta_response_function_shell_call_output_content_param.py,sha256=ZkD7egyWylErpgH5a_xJHlYxTGipNXjcbXTbtE5BEx4,1306 +openai/types/beta/beta_response_function_shell_tool_call.py,sha256=QW9HFUgbQN1PkU7Nui9kOQGaeiIjg9ZjZyWpT87qFTU,2659 +openai/types/beta/beta_response_function_shell_tool_call_output.py,sha256=83f4uYHZolB0WAGQsGfqEORdxChpTqPhS4imFbNiDhI,3174 +openai/types/beta/beta_response_function_tool_call.py,sha256=LAxLq_9kAVX9I7fVvUjXk982ER5MemgB5BwN2ssNVsc,2136 +openai/types/beta/beta_response_function_tool_call_item.py,sha256=Vb6Nt4VTq2EhFM3exwkBDK4AjsJFD9MwIEdrhQhfa3A,923 +openai/types/beta/beta_response_function_tool_call_output_item.py,sha256=-hEAI2MQUR6bKsY8iLQDD1VkqCQLR6bvH-fnxwNjfFE,2559 +openai/types/beta/beta_response_function_tool_call_param.py,sha256=3Ga0cjTCb_afP5izFbez959HM7eI0iOEiL8bUiJR424,2185 +openai/types/beta/beta_response_function_web_search.py,sha256=2iIFr40BOU1g_wb5jdu6nlBH2piGTVsG7TbWFR7Xjl0,2676 +openai/types/beta/beta_response_function_web_search_param.py,sha256=irHev_oKPUAC9cuSjGD1Qr5ij1UYc6Xyvgc8A7ukSmc,2805 +openai/types/beta/beta_response_image_gen_call_completed_event.py,sha256=i9749830BN_k6v1pQzyECDmfgZpCEEDAX4fDR1hO4RU,1104 +openai/types/beta/beta_response_image_gen_call_generating_event.py,sha256=pnyjz5rLzYa9Tg7ne5kfLiuy1_i-AMg4uQzbJ5KpiQ0,1145 +openai/types/beta/beta_response_image_gen_call_in_progress_event.py,sha256=hUtNH1t1BcsTu2O75tr_ZDdBdSLiXsLlAsRg81dsD4s,1099 +openai/types/beta/beta_response_image_gen_call_partial_image_event.py,sha256=EIlYt5Mva1M84asFkZLCgrYIfVOJTy2g-RKi0_J9GRI,1700 +openai/types/beta/beta_response_in_progress_event.py,sha256=36ST1xVsqmd2NwIrmNm0hIlmIdg-yDmpGqfbVr02Sw4,905 +openai/types/beta/beta_response_includable.py,sha256=M8rd4MiLkkJGjR7dePTntglicrmfrVuF-H1_-z2ll8w,512 +openai/types/beta/beta_response_incomplete_event.py,sha256=NnjDxDbbDqc0X189TUF6PtE9QWRB1e8sVPSgqfQzgwI,1148 +openai/types/beta/beta_response_inject_created_event.py,sha256=MdGD0hmdZGcYCDW-wTAr8AIu9oTwtGr49OCdQxHfhBU,857 +openai/types/beta/beta_response_inject_event.py,sha256=X40KLhUKo0TmieIhEVJY_unQhhIrXGsXtoHEKSTkKwM,851 +openai/types/beta/beta_response_inject_event_param.py,sha256=Db_Kin-sJCYJG2ZZV8B6UrW6CJ9zAQ6M9KO1c2be94k,952 +openai/types/beta/beta_response_inject_failed_event.py,sha256=S-PpHoohhjkVz-F7QeynByGQkepDDCkilqCn32gVY_c,1460 +openai/types/beta/beta_response_input.py,sha256=V8Bbs1WpmRP4f7kvb5qSoP0iKbV6YA4uCjgrUuDjMrI,304 +openai/types/beta/beta_response_input_content.py,sha256=NeGhFn3kWCtxwybQEPSlx-DH4RmEhhsvRhvamvZ0my8,588 +openai/types/beta/beta_response_input_content_param.py,sha256=Abidd18nUimbR46XURkZ4nVYSj5-7YTR0rCFN_6CV3g,589 +openai/types/beta/beta_response_input_file.py,sha256=wsnI1A3z6_vZB4FXtEUqOEK9iuh7Hc54i29nMp4ZNto,1780 +openai/types/beta/beta_response_input_file_content.py,sha256=irzfSi6mfInD4R32xN90l_4AOi9mVozu4aegaXOTN8E,1806 +openai/types/beta/beta_response_input_file_content_param.py,sha256=HxjFLXVLz-m_Eeh4WuYHga8QiHZO6zw8pHQ7PhHf888,1833 +openai/types/beta/beta_response_input_file_param.py,sha256=SaViAsJHhf6I_HuOPOn4T9lcugbxKAd0aaujKUgogyU,1767 +openai/types/beta/beta_response_input_image.py,sha256=zgUUnuoNestAu3c9keHHQ_trt_sL8q1liWCqshDypWU,1577 +openai/types/beta/beta_response_input_image_content.py,sha256=iX_Gmx6uCZ780QPtbTS8ZcJ2XyPoKKyPDm5gPobqQek,1607 +openai/types/beta/beta_response_input_image_content_param.py,sha256=cbvzq-qEZr9KZKuUDfoydMOTthsdOUSTmrZFZJJk41E,1659 +openai/types/beta/beta_response_input_image_param.py,sha256=VTQ71jjdrQObc8wszRdwT-0-U2bj90IZIM6vUUsFyAs,1636 +openai/types/beta/beta_response_input_item.py,sha256=GvSOkG1sFQWnSOw6JbnsD0dPdpUTfWRzmpJiyJKoHQQ,35520 +openai/types/beta/beta_response_input_item_param.py,sha256=sM87PAABoUVJeG5wNl_wR3nAotCgmjCYhcuO2V1k1Xk,37022 +openai/types/beta/beta_response_input_message_content_list.py,sha256=aa-te4JLK3YrHAUap_CmTPZoI2eMOizLcAedC49CqH4,349 +openai/types/beta/beta_response_input_message_content_list_param.py,sha256=KSAZmFKdIVJfuboJAJnk35AUK5s-wl-BmJGKCLne1Yc,730 +openai/types/beta/beta_response_input_message_item.py,sha256=rLj_JrlRWTnm_9eJ8wF0HCkuRcT-taCfdg3oM5qvOUM,1280 +openai/types/beta/beta_response_input_param.py,sha256=ylIpVOk_3PdpuLHFqYzgHsAEsfpOWXw9CRKa_EwrxhQ,37128 +openai/types/beta/beta_response_input_text.py,sha256=FRgpQew-d-2b3-NrTgJYDwY1wLro8jg1mgNLnjeNe_8,1057 +openai/types/beta/beta_response_input_text_content.py,sha256=tWEtDTtLJ4-4JBVXvzwN1O_6LF9b2POkNeQnEQ4cA9I,1071 +openai/types/beta/beta_response_input_text_content_param.py,sha256=RscSpofNwhzFQs1hmB9IStXBW9gWwa_bIYfk1_p0ue4,1153 +openai/types/beta/beta_response_input_text_param.py,sha256=De-22TfVC9adRN5AcwpD3BnNyj_wG2HJmoecXatVsSQ,1101 +openai/types/beta/beta_response_item.py,sha256=q8_bFEuG2BA59Uohefiu3GDfGLnhUCOiJiHqOAjtckg,19657 +openai/types/beta/beta_response_local_environment.py,sha256=Mhy6oGAwhF-uOcVrwvlK_ZgE_hsWZM1yLU8v8A_Urp8,405 +openai/types/beta/beta_response_mcp_call_arguments_delta_event.py,sha256=F3wOI3h011Xya8XY-GmJXD8shsVTpjMQUfNVL328F-o,1205 +openai/types/beta/beta_response_mcp_call_arguments_done_event.py,sha256=_o2b4-n11x59hYn0W6J6m3bIOCsRAZf0chCYnR7KhLY,1147 +openai/types/beta/beta_response_mcp_call_completed_event.py,sha256=RfhqcVWS6uDfWc-2M1NcmhScqLZ377CPNkOLyFS0dFk,991 +openai/types/beta/beta_response_mcp_call_failed_event.py,sha256=tXHOnX32NiWhcpi8UEAL_-R-CASUC7uZQWbZdMNCcm8,957 +openai/types/beta/beta_response_mcp_call_in_progress_event.py,sha256=lUtTFOzo5Hb07FmWok8ZADdhsP6P_gfw-EmvA76qp7k,1017 +openai/types/beta/beta_response_mcp_list_tools_completed_event.py,sha256=cj1l9D9pyAAvj0fUiaFLBQg71Mk9YOh_0xJ2U9r98kI,1047 +openai/types/beta/beta_response_mcp_list_tools_failed_event.py,sha256=yF96eLbbC8R4qHgr2E7rRpm2R5lEkOp_OXR6ubJtaEM,1001 +openai/types/beta/beta_response_mcp_list_tools_in_progress_event.py,sha256=qWDvwAQMNk2riudMwF3fr3ugXaxXZFjH2CDum9QmqN0,1077 +openai/types/beta/beta_response_output_item.py,sha256=7EjzhGqBEM_fOCYItLHfxPJQIA1AIw4csYBrY9xkRDU,19396 +openai/types/beta/beta_response_output_item_added_event.py,sha256=IOZAHvijILAhOZ3-UH3ZrhCDny9L4E9wnyKXAoTmlGY,1272 +openai/types/beta/beta_response_output_item_done_event.py,sha256=jzakq9AOypARTpYtns5zBAch1Mdl8llpQZBzPtgNmpg,1041 +openai/types/beta/beta_response_output_message.py,sha256=frVGFK6kQdMvCm_nEpMgMHp70tLKzFetTXrcZGzIKdQ,1853 +openai/types/beta/beta_response_output_message_param.py,sha256=prUlSZNMI1hk6uSuk-U7uAsuRkX-5O1mJonirj9KI60,1900 +openai/types/beta/beta_response_output_refusal.py,sha256=BwVst5wO0QfYh2zZhOQcuLh9OT0ENNz91VZ-T-H-Ong,432 +openai/types/beta/beta_response_output_refusal_param.py,sha256=7hPVBmKTEE9Wcyv6YgNGcpFhiMU134RiAROVyAIm9Ls,498 +openai/types/beta/beta_response_output_text.py,sha256=JGMQdeoH9jRgFsqNs7IXXuZ5QK95rlZrTTuD1cXEcfs,3164 +openai/types/beta/beta_response_output_text_annotation_added_event.py,sha256=V1GSPolzJJs3X6NI7EuRVRsDJYe1omgjVX4ycFk6nmw,3605 +openai/types/beta/beta_response_output_text_param.py,sha256=WZ0bH1Fz9_hF_q8gMxRoMv9DIwVUAhxDvJFGftmARAo,3467 +openai/types/beta/beta_response_prompt.py,sha256=WI0fMSHq81Mj9FqIUfLlFs9mk59l_gUyMcaeXLtC5Sk,1166 +openai/types/beta/beta_response_prompt_param.py,sha256=1WoQO9b0Pcdb3phOKfxhirhqiW-7ecIJvhPRCHZLjc0,1257 +openai/types/beta/beta_response_queued_event.py,sha256=zgppD0r42INl8YJMDggR_VEN_fzrAC1s1X0LdcJhlSw,916 +openai/types/beta/beta_response_reasoning_item.py,sha256=6z2E0d9SM8-rvhIm246VsdYLSm0EEo-31Wv_UCefYr0,2449 +openai/types/beta/beta_response_reasoning_item_param.py,sha256=m8cO_uf-yRUrKt7I-ZavGJ6H9cNTi0TjYEm0AfpSPho,2578 +openai/types/beta/beta_response_reasoning_summary_part_added_event.py,sha256=j8gyNnRpBMqDjJVmBSff62OaEPxoWVYibYK4DuC8TWM,1434 +openai/types/beta/beta_response_reasoning_summary_part_done_event.py,sha256=GDZYk95OTGoLlK8nGRa-Sm6hvf7IqgSOQmHygW2nqKA,1637 +openai/types/beta/beta_response_reasoning_summary_text_delta_event.py,sha256=GMNE7TGD7NbZnKXg5fU3UMVZzazI-Z9dWqSs3fd5JvE,1237 +openai/types/beta/beta_response_reasoning_summary_text_done_event.py,sha256=C7R6uFzoLUenqHt2E5jv8u42AHGVCMJRpQK2-SgNMNM,1217 +openai/types/beta/beta_response_reasoning_text_delta_event.py,sha256=-B3mKeOpkNCXETewpS-KS0nP7apoP1Vue7kMd7y9YGE,1224 +openai/types/beta/beta_response_reasoning_text_done_event.py,sha256=cZWKxC6NUNP-B1GWR_TZwXn0YC4inFLR97lc-BqddDI,1164 +openai/types/beta/beta_response_refusal_delta_event.py,sha256=MxRRe_E12Y1FIE_b-dINF6r_nIto_EP_vXhibXL9yIM,1148 +openai/types/beta/beta_response_refusal_done_event.py,sha256=sxjqIIwh9KdkV4MtYMue7lXpyErKAwmsve-fTHofRps,1147 +openai/types/beta/beta_response_shell_call_command_added_event.py,sha256=sqjUKl-8hMK-2iTq2JdOQ12dHh80Kob2g7d1CXjMXAE,1113 +openai/types/beta/beta_response_shell_call_command_delta_event.py,sha256=Pt-AP8i4ArMjMAINyv5WiZv10kODxtODFMxmfgfcv7g,1235 +openai/types/beta/beta_response_shell_call_command_done_event.py,sha256=VenvRZgubfCzSRA6roWMIH2CDFyyE17bjgkuQqTkEag,1110 +openai/types/beta/beta_response_shell_call_output_content_delta_event.py,sha256=peQ6noVXbNLXxiSK0KheiEGgevqnm0Z4Y2Tafvk5EpU,1471 +openai/types/beta/beta_response_shell_call_output_content_done_event.py,sha256=JCnOGhegNro1oNZMg4tPj_WxRM8J9Ah67z0a6Scv6-Q,2456 +openai/types/beta/beta_response_status.py,sha256=WQNdYGEvCBIbt3Mv2G79omKST3oPvFZ1eJZ_YOsoBDU,285 +openai/types/beta/beta_response_steer_accepted_event.py,sha256=uAWIOWaq5VKe12wtuCi_1qcmFHvuJafH0z01Vz0CAIA,1650 +openai/types/beta/beta_response_steer_error_code.py,sha256=niH8K2dcxAv9UL9WeVa0LNhQdNVYAWd03yRqqKtCNYk,511 +openai/types/beta/beta_response_steer_event.py,sha256=hkF6l-ITyvMWaUJ6jJhMzc2mHKXArnf45qMty_MoJeY,2827 +openai/types/beta/beta_response_steer_event_param.py,sha256=MQnhvX5PmSsh114SMleLmeXCxX7S0AgdNeohQjm0eMc,2920 +openai/types/beta/beta_response_steer_failed_event.py,sha256=iPDpBpUbM4d22IWpSOqBuQSKTqXhu34T6zCJkXfhL_M,3437 +openai/types/beta/beta_response_steer_input.py,sha256=am5wlra0VjFl4VKajwTB4M86x6Q40YVQsHHeqqgf9z4,4115 +openai/types/beta/beta_response_steer_input_content.py,sha256=-qr5VgTBXGLNYAxHDlrWausISDbPH5nVyzFwb0zDmdY,669 +openai/types/beta/beta_response_steer_input_content_param.py,sha256=KxFyO8bQkCT1xeuwMgqOC7zjKWn3uv84Sa5iINNHqXM,665 +openai/types/beta/beta_response_steer_input_param.py,sha256=JHYtM8RFBw5LSF3lN4dadR9-kXpsrmV9Lrm7CWKIvQQ,4114 +openai/types/beta/beta_response_steer_pending_event.py,sha256=YqeurgVO1__UEZwel4K6nNJcvyjgnz7GSi2Cv2AQGP0,2851 +openai/types/beta/beta_response_steer_pending_reason.py,sha256=YvEgGLu_1IiDwXP2vQoOi0m17GXKjorOF16T1C7yp64,301 +openai/types/beta/beta_response_steer_required_input.py,sha256=n5UwtEYeEh9Ms6xH--ZjrztTUUEusIVJrasIG3qXHCA,2509 +openai/types/beta/beta_response_stream_event.py,sha256=2iGVnFRddTy1pV5bMw4uKQhb7cUv6gamLZWJdVmZ4oM,8509 +openai/types/beta/beta_response_text_config.py,sha256=0bA3aoZPOWaN7J57t7QP6p34i8R-3BOpVIo6tpQ6x24,1718 +openai/types/beta/beta_response_text_config_param.py,sha256=9J2XZF9xHNiUoS7QPWOi07sNOtnr8dqnPmBUBVfteTs,1747 +openai/types/beta/beta_response_text_delta_event.py,sha256=3sqR7LbF0xMRPY3PqGjCrV7L4ML8gGpSbrEH6jjWRE0,1989 +openai/types/beta/beta_response_text_done_event.py,sha256=ft5NXjYtL1UVArPU_emp5croPJmTw1-gDfLlkYnOlOA,1987 +openai/types/beta/beta_response_tool_search_call.py,sha256=Bm9H_hYeX39OHOgZDib1OdCMufjbjutS-meqytKhCGI,1218 +openai/types/beta/beta_response_tool_search_output_item.py,sha256=pv_3_TX7eHkwrJ6ZMTFH7IaZihTijhzjDM5FLyGdN1M,1292 +openai/types/beta/beta_response_tool_search_output_item_param.py,sha256=SUjw1lO_6OrvsoSDLE9YRU6_UD1mhYyQ1I-6Zc9EItM,1231 +openai/types/beta/beta_response_tool_search_output_item_param_param.py,sha256=CJCHWMyru1kVyrGqA1QhaAyDPMzFuaqgSTvRbGHkhW4,1300 +openai/types/beta/beta_response_usage.py,sha256=wGOG_UB6viUZTfv033oUt3KHQkiP8ESXDukE2GgyESI,1319 +openai/types/beta/beta_response_web_search_call_completed_event.py,sha256=BXTc7jy4sXJhTSPPFhQdEUSoieIcwwGFozZv0cuuubQ,1075 +openai/types/beta/beta_response_web_search_call_in_progress_event.py,sha256=zhT2QCEuBvrqIX8TrpeMorDCsNBcK6Fw8CneD2lpq2s,1081 +openai/types/beta/beta_response_web_search_call_searching_event.py,sha256=coAnk1DLcAu8Ql41fS_xDR5aR5e0oT_MPNsJYEE8DFQ,1075 +openai/types/beta/beta_responses_client_event.py,sha256=heq3zuljTmGcxVGAsc-FUd4M0B9WNyL8ZnAaH-d3Pno,29092 +openai/types/beta/beta_responses_client_event_param.py,sha256=UwHJE3TqXJ-rlPH2lsKGx7MG98ebswrKVITWdeGlJe8,28989 +openai/types/beta/beta_responses_server_event.py,sha256=0cA0Sp6C-MBxbL399JH-r3bA-asB6JvjzzsE5cBcqO8,34269 +openai/types/beta/beta_service_tier.py,sha256=jzrHlQZzRm0_ZH8-eFVUVVUP3lwPbJJCRVNdhN3EEXk,311 +openai/types/beta/beta_skill_reference.py,sha256=bOh-pca6VKKzgkp2qCtkRvQyf9HxP8bXjd5PvkIAVVE,547 +openai/types/beta/beta_skill_reference_param.py,sha256=Z1MfkhKQwxR_cGnN8w7v8CfRoJ4vYaoKzGCZwgQlZtc,568 +openai/types/beta/beta_tool.py,sha256=0vwlgka3zhLu1Gt288D3jEGxrWMHR4wD6subwH9wAJQ,13945 +openai/types/beta/beta_tool_choice_allowed.py,sha256=G10DF0Kx8qumvNxKDvmG8JSXz7Kl066_ANX0JBMsnlE,1107 +openai/types/beta/beta_tool_choice_allowed_param.py,sha256=cmZozpS4quLQYzLN7AwHmTZ5o1vaMjrglYH5r1P2Kao,1191 +openai/types/beta/beta_tool_choice_apply_patch.py,sha256=d0jFVkUXEPnXx0U5TE1fjO3cmM596oPNDJrLlnl5GRg,411 +openai/types/beta/beta_tool_choice_apply_patch_param.py,sha256=BZ8VD9PkfOx86FFCfMZ2Wszl3vBUtBh2N0VDhE8d6yw,467 +openai/types/beta/beta_tool_choice_custom.py,sha256=bPw3vXvp6UZESgenZy0Uif-C53mVdGFJNIia_ZJylcs,467 +openai/types/beta/beta_tool_choice_custom_param.py,sha256=RZlniTuBYzELs49dqepBfdT3BHHHxzFa71tIHDuDoJs,533 +openai/types/beta/beta_tool_choice_function.py,sha256=mkwwTOTw-Wf-ZyIC6BspVynMKZmrTSPRHeaVk3FRjZc,466 +openai/types/beta/beta_tool_choice_function_param.py,sha256=JHHTflQF7GuhnHuxHcDJFUtQe-TRLdhTWbsO4rmS_TM,532 +openai/types/beta/beta_tool_choice_mcp.py,sha256=jQmBzn8ix62rAl-cJZtUihoXTB0Im-2RYD05N6pT2nU,592 +openai/types/beta/beta_tool_choice_mcp_param.py,sha256=k1OeJ0f-RJ_HRc2MGtf4M97Yo3C9uXTLUunGh_elfGs,651 +openai/types/beta/beta_tool_choice_options.py,sha256=sYS1DFei2BkAWOaS1zxCfv5cRqYsPjho4y07BAktwMo,244 +openai/types/beta/beta_tool_choice_shell.py,sha256=Agz-CMdranFcN6Gm8zu9N23mu0puiElLqHEDWYQSUw0,385 +openai/types/beta/beta_tool_choice_shell_param.py,sha256=hZtJpppaRUUZlF02cctqpNCBGQBojrM1lHJHjgRasGY,441 +openai/types/beta/beta_tool_choice_types.py,sha256=1-UuAB_EBcvUlPWPV1yqxcwJy8Cw7jG6lyIdttCJrdc,1024 +openai/types/beta/beta_tool_choice_types_param.py,sha256=3Bo3tWugrYNQh5nZlZtaeiIVFDZSWDY45_pWeCvC63A,1130 +openai/types/beta/beta_tool_param.py,sha256=ICwsu6IpQFkAd3BFN3jwpXQz36Iz_nCycGxTeyDBAmQ,13691 +openai/types/beta/beta_tool_search_tool.py,sha256=KNS-YiNYAr7a0rcWILXVRp-NGehIwjzVcNOB35mOlEs,781 +openai/types/beta/beta_tool_search_tool_param.py,sha256=oyaYgoag5Od3m75Gevxl94iCNLW9goIYOn2a1IUaUoE,806 +openai/types/beta/beta_web_search_preview_tool.py,sha256=Y_-wCQftp_7oR3QGnLFoxY-rXpZkxpbTP-Q-iU_TqDQ,2226 +openai/types/beta/beta_web_search_preview_tool_param.py,sha256=2cXLHuFrMh6NG6bxrFpWIFslM8g_azYGmw314ySpTWc,2236 +openai/types/beta/beta_web_search_tool.py,sha256=wdH0lfBIZMNYr_LgY2DY-Z3HzSvVrDoPU8HYF4nBAWg,2763 +openai/types/beta/beta_web_search_tool_param.py,sha256=axLh6HXktjvh8GBvseM-FRSWa6YxcXTwbZv_TxTEXuA,2787 +openai/types/beta/chat/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35 +openai/types/beta/chat/__pycache__/__init__.cpython-312.pyc,, +openai/types/beta/chatkit/__init__.py,sha256=9GVfiZoObGa02pv2Ax1Qs0LF1UbjuY1iTnZw75aCF7Q,2115 +openai/types/beta/chatkit/__pycache__/__init__.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chat_session.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chat_session_automatic_thread_titling.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chat_session_chatkit_configuration.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chat_session_chatkit_configuration_param.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chat_session_expires_after_param.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chat_session_file_upload.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chat_session_history.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chat_session_rate_limits.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chat_session_rate_limits_param.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chat_session_status.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chat_session_workflow_param.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chatkit_attachment.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chatkit_response_output_text.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chatkit_thread.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chatkit_thread_assistant_message_item.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chatkit_thread_item_list.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chatkit_thread_user_message_item.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/chatkit_widget_item.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/session_create_params.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/thread_delete_response.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/thread_list_items_params.cpython-312.pyc,, +openai/types/beta/chatkit/__pycache__/thread_list_params.cpython-312.pyc,, +openai/types/beta/chatkit/chat_session.py,sha256=LY-gsExMFOywDwtp8Xism71Ew9ObEVH-UAL8QSGmM1M,1400 +openai/types/beta/chatkit/chat_session_automatic_thread_titling.py,sha256=Breq6fb7QU28hpcng6u2WmuT28zKj9_A8A1DnaXqZ0A,359 +openai/types/beta/chatkit/chat_session_chatkit_configuration.py,sha256=IwFU_HdXLX_R1SlNq6PfZy2ZWFB-AfADa5gDw1bGTAI,738 +openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py,sha256=roWd-s2lNoingyt3nG0J0iDpd1FOPVuDDsvGoyNgzoo,2177 +openai/types/beta/chatkit/chat_session_expires_after_param.py,sha256=9pxrP4GNn6NWvAb1o4NLX9q4mgMeoY7e8xyAODgUbHs,596 +openai/types/beta/chatkit/chat_session_file_upload.py,sha256=dR64mjrnA93gQTft-2t-JpRmovC_9TpnYmNgi7et7kU,557 +openai/types/beta/chatkit/chat_session_history.py,sha256=3igjdGA03JXZevu6ysWzp4P-FuWPFlU9dFtCbpMe9qg,533 +openai/types/beta/chatkit/chat_session_rate_limits.py,sha256=7BZQhesASGc2p7q2C5qOjlPVavE6Iq7_HsSr0lqTXS8,352 +openai/types/beta/chatkit/chat_session_rate_limits_param.py,sha256=Fagn2aaewvH9bIALzW1o8cYgg-OvGjuYe5XOjEk4htw,445 +openai/types/beta/chatkit/chat_session_status.py,sha256=lO7p8_pE397H8FMGtZzqCcZ-Hx7ekm30q9OwWvt-fIY,242 +openai/types/beta/chatkit/chat_session_workflow_param.py,sha256=8-i7LWzaIYKCJxDiMaf7lCCnmYyu5MI6lbPe6-iLcZs,1201 +openai/types/beta/chatkit/chatkit_attachment.py,sha256=E1LjSa4XXguls4-diebXFF5MS-IyOP7ScvG-mHmx9_E,664 +openai/types/beta/chatkit/chatkit_response_output_text.py,sha256=x94fDDyQ5EtMfd9TVhfdRP8r7YAY2FxNuGPpbZl7tOY,1881 +openai/types/beta/chatkit/chatkit_thread.py,sha256=sA0yvwa9ZvV7VUvcg2HqlG0gDdDncb4e4x47ZS1suos,1917 +openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py,sha256=V7UGL1FTCx_8AEC76Yhv1ti1wFWLhcDwCBjUkCd-3lU,940 +openai/types/beta/chatkit/chatkit_thread_item_list.py,sha256=ruNw1v49EN4AJntz1JWoKzUU3C5MhP0p5ftL3Qmmyb4,4301 +openai/types/beta/chatkit/chatkit_thread_user_message_item.py,sha256=2PRXup8kOqkFIxZ9QLAMfdSIfk0mp5HVvKKpql-L-z8,2504 +openai/types/beta/chatkit/chatkit_widget_item.py,sha256=wM0sEguBZmJmYQmLSWydHOHznR6FhZPYp_vgn3P0eZo,773 +openai/types/beta/chatkit/session_create_params.py,sha256=4diAF01RuE6Mn0iv447IXFa6MHBy9WeZh4BYFKlBeDw,1273 +openai/types/beta/chatkit/thread_delete_response.py,sha256=rDAmTS5ej4Y-BY5iT0wJY8H0wouXhwe-m8llnNcvW0o,548 +openai/types/beta/chatkit/thread_list_items_params.py,sha256=xo1TEplz72aZ1DmimjzQrDdyzzgNGrCaoPH765gQomc,696 +openai/types/beta/chatkit/thread_list_params.py,sha256=zuIz6t5Dc_OB9sVKn1D_rD5tzNiORauvGM6x8lAHTwk,811 +openai/types/beta/chatkit_workflow.py,sha256=qTzyJfc4Tf1KnM5IEp0-CSIW7S5ipfbTb3MCJqIMTro,960 +openai/types/beta/code_interpreter_tool.py,sha256=emDceKOjHyTJTt4GFOE6WysF61mPqc0klMU14Gv4f50,332 +openai/types/beta/code_interpreter_tool_param.py,sha256=mbqvPjtOkvI27VjK-SjlfRHZCP0jrS-cNpmDuOz_y4Q,388 +openai/types/beta/environment.py,sha256=GzFhYd9l8E-p2mNChRDydjGVtMRIBu34j29qb0sKPAg,3425 +openai/types/beta/environment_param.py,sha256=kKcaaFusXL1-7AdpZMaQM_eW3lqPLJXrtH0gHfh4PFY,4292 +openai/types/beta/file_search_tool.py,sha256=XaXOH9LnxhROPRKZ7Idl3pLGpYLTYpMyV2EURC9J5uE,2114 +openai/types/beta/file_search_tool_param.py,sha256=AF7wTum08vweyMttptO7nKLzFfaFqNqET_FCdvBDrQ4,2110 +openai/types/beta/function_tool.py,sha256=97pUifLWO5IBQavAe97Yp4QGpur3TBEwMWcAtqEy6PQ,396 +openai/types/beta/function_tool_param.py,sha256=qHOQ5i2dAZ02u8EExm6p5yglxzp8_-SHJqRcOdgg8dI,470 +openai/types/beta/hosted_environment_file.py,sha256=eRqcnUFtDlxpElXiTmBw33U1FdjKF1QvCkKVYmRNhsg,961 +openai/types/beta/hosted_environment_file_id.py,sha256=HM_dc2arPhygkiqOLNR6q_eTe8r3-sZnjgRjKBPK2Yg,655 +openai/types/beta/hosted_environment_file_param.py,sha256=2UCflu10G5ttH_GaQicUijTanvAEjR87pqKcEz-RaNw,1205 +openai/types/beta/hosted_plugin.py,sha256=tPFot0E1LPhE61mbOG0yN16dCkG3hPfibVvUw3DJp4A,474 +openai/types/beta/hosted_plugin_param.py,sha256=sadWlKw0-mcx1A8dcYAhGHYDj2pdVUMbtjaKOar_lJc,801 +openai/types/beta/hosted_skill.py,sha256=VHwtpqvf5glq_r3XSpgQZtiSkDv7oy5hLS2bjPDkORg,786 +openai/types/beta/hosted_skill_param.py,sha256=-K6t-GIPFkGY1y2UYvxNhgHpV8NQpnmLHL42mEMDv2Y,1420 +openai/types/beta/hosted_skill_reference.py,sha256=QfQPJwU9p3R-jWcFARAh3NKoFDlO3MZeTY9m08To5lc,635 +openai/types/beta/inline_capability_source_param.py,sha256=6Fi-cFoezEDyH_bcX1Z6F1ETVrmj1yvARL2RDXyRgds,673 +openai/types/beta/input_content.py,sha256=7EYDVCgo3bZ_bjVJBF88pfaRdOYjGKEelFO6_7RL4MA,1048 +openai/types/beta/input_content_param.py,sha256=SgHzYHJl8mGTvPFnn-nmBttxqsw05Vp7gtSDTchGZmg,940 +openai/types/beta/mcp_transport.py,sha256=Z-vnlbETBVhfLiSkFmm_sGV1icwvv8dNVyUKcfd1sGQ,1197 +openai/types/beta/mcp_transport_param.py,sha256=ULvrTlGZ7b_EVPZ2AvPVM5TLMtXsvkEyBSCcD4TluLM,1619 +openai/types/beta/multi_agent_config.py,sha256=-gaO6oKFRTX-WvjWcg0FPea5RtK-3kumC4spdDKJocE,557 +openai/types/beta/multi_agent_config_param.py,sha256=e8nqFl-bgpqff0n6sFLe_iDBLSGPhRVqXcN6Zygo5NI,528 +openai/types/beta/output_text.py,sha256=HVQjfH1o7-QgXzZ_K_KTC7skawLWUD_5A8RplHb-_uM,409 +openai/types/beta/persisted_agent_tool.py,sha256=WWtLPs1hb0h7pminJCoI4Q8dpRex2k6G6LuGh0Goy10,4152 +openai/types/beta/persisted_agent_tool_param.py,sha256=fCHq27YT56SMZ_GkyT06rmQZq5khp4DslHNwyZznmxc,4721 +openai/types/beta/persisted_mcp_transport.py,sha256=v_pqTNwY8jyaKU1Nq4jV3ioiLL43E42vr5t9lBSKFXk,1362 +openai/types/beta/persisted_mcp_transport_param.py,sha256=s-f1ppFm7yTH5aCNp2ei7w-S2xFjN1csa1uafpfUuJY,1513 +openai/types/beta/realtime/__init__.py,sha256=gkjna4Zi59jiQzyD6qOIL0qZKdu3jSh50GsckE956F4,7277 +openai/types/beta/realtime/__pycache__/__init__.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_created_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_content.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_content_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_create_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_create_event_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_created_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_delete_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_delete_event_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_deleted_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_input_audio_transcription_completed_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_input_audio_transcription_delta_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_input_audio_transcription_failed_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_retrieve_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_retrieve_event_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_truncate_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_truncate_event_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_truncated_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_with_reference.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/conversation_item_with_reference_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/error_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/input_audio_buffer_append_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/input_audio_buffer_append_event_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/input_audio_buffer_clear_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/input_audio_buffer_clear_event_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/input_audio_buffer_cleared_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/input_audio_buffer_commit_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/input_audio_buffer_commit_event_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/input_audio_buffer_committed_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/input_audio_buffer_speech_started_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/input_audio_buffer_speech_stopped_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/rate_limits_updated_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/realtime_client_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/realtime_client_event_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/realtime_connect_params.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/realtime_response.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/realtime_response_status.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/realtime_response_usage.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/realtime_server_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_audio_delta_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_audio_done_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_audio_transcript_delta_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_audio_transcript_done_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_cancel_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_cancel_event_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_content_part_added_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_content_part_done_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_create_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_create_event_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_created_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_done_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_function_call_arguments_delta_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_function_call_arguments_done_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_output_item_added_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_output_item_done_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_text_delta_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/response_text_done_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/session.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/session_create_params.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/session_create_response.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/session_created_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/session_update_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/session_update_event_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/session_updated_event.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/transcription_session.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/transcription_session_create_params.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/transcription_session_update.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/transcription_session_update_param.cpython-312.pyc,, +openai/types/beta/realtime/__pycache__/transcription_session_updated_event.cpython-312.pyc,, +openai/types/beta/realtime/conversation_created_event.py,sha256=shLwS4S5cSyu2GguKOFyavVfdHgX3ZhC2aNuxcUIQ6E,665 +openai/types/beta/realtime/conversation_item.py,sha256=npw9pk3HR6DpYLQYYsVNz4yL5JfGkszfjlyApEu7VM0,2240 +openai/types/beta/realtime/conversation_item_content.py,sha256=ejEoziMFI-b2qvgPh8oWJP5EzUPpk7vNfbfkgIg9zDA,963 +openai/types/beta/realtime/conversation_item_content_param.py,sha256=XmWdilkxV0DPu_j4C_wxKPtEoG3HMMyirvSH_tssK-I,885 +openai/types/beta/realtime/conversation_item_create_event.py,sha256=dM7SJOnGpbXzCG75146UTsKSP6VwjbzrPX-b4kNU2MM,994 +openai/types/beta/realtime/conversation_item_create_event_param.py,sha256=O6CTrdiUxyo3wPYiJg7e0Dbpb72MZuuvzlfgQgl6uN4,1014 +openai/types/beta/realtime/conversation_item_created_event.py,sha256=aSMrMzXckiCmoql5nvDzKBcyrFNcdiimgIAq8tjYccE,730 +openai/types/beta/realtime/conversation_item_delete_event.py,sha256=DrsNc8PNqIq4tlAZYNnQKS6lwOqUTUcqP78tCDDW4ts,462 +openai/types/beta/realtime/conversation_item_delete_event_param.py,sha256=TmUI-iamvyt1tQxfVugK0qaGa8wR-VqCP0CMwq6QhXg,482 +openai/types/beta/realtime/conversation_item_deleted_event.py,sha256=oa-JHZcAF_XNY-i0dfKxAY0TvY420B8u3LIx0v0npZc,405 +openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py,sha256=1JjgPx4nHQo9Wm2saBYnRIkJnwWEEd_82_OjkPi9G34,2451 +openai/types/beta/realtime/conversation_item_input_audio_transcription_delta_event.py,sha256=GzDp1y0CiW1A5SPn2v6MwfQFYpMoZAR4WCxq4TSFdUQ,1056 +openai/types/beta/realtime/conversation_item_input_audio_transcription_failed_event.py,sha256=-SaiNByDyh6n6n9wcnfU-U-odzLgb8nYUD9PB4eE-C4,990 +openai/types/beta/realtime/conversation_item_param.py,sha256=f6SSHCtwoWMXFQbDsDpqV9vk2llqeEuOouhwKAX_1ro,2120 +openai/types/beta/realtime/conversation_item_retrieve_event.py,sha256=cNkgs2hUv3cGIJkJBMzkv4SnfKJ44ywvzyXwsKW_Alc,472 +openai/types/beta/realtime/conversation_item_retrieve_event_param.py,sha256=B7FGAXwLmYx1DZ0ZffTegvzTZSVpvSwSu1_EacZydd4,492 +openai/types/beta/realtime/conversation_item_truncate_event.py,sha256=pKA88WtX8C9bTPhnCaMDlUxXmivmTrLwkk9SzMhl0uw,856 +openai/types/beta/realtime/conversation_item_truncate_event_param.py,sha256=o5CD_YzNSfyGAe41lsshuPgmzCT0Gh4YfvqxLTQmTgw,896 +openai/types/beta/realtime/conversation_item_truncated_event.py,sha256=qBdOyAAs4QxhW4RGb7wq0MZ5Vf_CISU_80K8vjBkqcs,617 +openai/types/beta/realtime/conversation_item_with_reference.py,sha256=EHGeKt-mUhxAf2rCd-dIgFFYuXGSZYbIACCIpYpitsU,3201 +openai/types/beta/realtime/conversation_item_with_reference_param.py,sha256=0RFZSOOu0Vrj09uCeW30QXZMWoLoYHKScEPeOC2eDRY,2992 +openai/types/beta/realtime/error_event.py,sha256=ZDqe_GdwenSCsBYytFbUmAAMR4_zn7rQzHiIY8_v1qo,790 +openai/types/beta/realtime/input_audio_buffer_append_event.py,sha256=3aJ9TdXapoeFiy-xyTMc_t5ULq-8_WYFwirppFue1pQ,575 +openai/types/beta/realtime/input_audio_buffer_append_event_param.py,sha256=EFOQibTuzujtCHrQ9oqbPkgiZKH_fHf3a91wt43Ykao,595 +openai/types/beta/realtime/input_audio_buffer_clear_event.py,sha256=h4Ad3rLtqoFJvur07xpPuPfq__FbJavCo0tCOwXws1A,402 +openai/types/beta/realtime/input_audio_buffer_clear_event_param.py,sha256=aaUpkY3qgNdqVlRNQ8bVufE1gzv3VER_wxum_WYvfE4,412 +openai/types/beta/realtime/input_audio_buffer_cleared_event.py,sha256=0slE_AN-Jt8FqitadEzgRutLl7aFRjginz24Iq8HC-Q,342 +openai/types/beta/realtime/input_audio_buffer_commit_event.py,sha256=1tfYLefnL_UBvzlfyEomL_elH0SB5pxCFnth-24KeTQ,406 +openai/types/beta/realtime/input_audio_buffer_commit_event_param.py,sha256=OVxZJv7EBwBDjM94AoVDnq0SQz753YWWl_LfDVk6INw,416 +openai/types/beta/realtime/input_audio_buffer_committed_event.py,sha256=uRW9pgNjwwl_JVA_GDLZ25A3cvodIkB44AQ4oIA9FEU,646 +openai/types/beta/realtime/input_audio_buffer_speech_started_event.py,sha256=Hi79pvVrSM_4mYPWZo0akxbXqmNoUtPO-GCntchfLkE,774 +openai/types/beta/realtime/input_audio_buffer_speech_stopped_event.py,sha256=BQnNVPqgrMMMbTrHFVv9jsup-ZMTxDLCKS7iGj6HHl0,692 +openai/types/beta/realtime/rate_limits_updated_event.py,sha256=ygOjxHvqm7oH93lfGKoWoq3l_jbhf2yHdzRuGaJldzA,862 +openai/types/beta/realtime/realtime_client_event.py,sha256=-MjFsD3UmrENLeP922-f7g1qhrNbX0bPkmCA7YsYcj8,1752 +openai/types/beta/realtime/realtime_client_event_param.py,sha256=ql1fUosEmt4Jc2oOJlA1fbL56Y2QGS4NdXK379PIs3g,1800 +openai/types/beta/realtime/realtime_connect_params.py,sha256=XqHUIybmlEdpIfgr6KNEHwGDjfB-gb8UNnaeKDJ8U9Q,203 +openai/types/beta/realtime/realtime_response.py,sha256=CZdXfG81f2QN5Rq970zbD-aOtUSMSem9cJXYz26sjyE,3480 +openai/types/beta/realtime/realtime_response_status.py,sha256=fQKcotHsf3PvVSPp1csCdxCOw8bFwG3hKza79jDGb9Y,1239 +openai/types/beta/realtime/realtime_response_usage.py,sha256=HhOaJ6eZxpk9e2euZ__KjsoLHFd781I7q9bMKsl1NJ0,1454 +openai/types/beta/realtime/realtime_server_event.py,sha256=Pu3N0IR1EPUy5Qr7_FzZkCUNUNF1ibn6YcFlHzc0RqI,5321 +openai/types/beta/realtime/response_audio_delta_event.py,sha256=6Rc4mm4YSc0JjseVIhlzzVPIQK_4VPUOs7fc8YYJiAY,655 +openai/types/beta/realtime/response_audio_done_event.py,sha256=SE85elOWzFT0QziA9qZeCUlH4MiQRLbwUBxMGXyyFgw,592 +openai/types/beta/realtime/response_audio_transcript_delta_event.py,sha256=WLtOFIBIow9lyS4_lXbyKYFrlegi5j5pvfjfY-Wx5Yk,686 +openai/types/beta/realtime/response_audio_transcript_done_event.py,sha256=nFpWTwtvli9ys3JmzkX_EAiDxZUvNb_MsnxwEHhXScg,700 +openai/types/beta/realtime/response_cancel_event.py,sha256=cmhGL7ykbWwHt5EDYeaOCX5X4t23iytOe_GR3DYLCu8,550 +openai/types/beta/realtime/response_cancel_event_param.py,sha256=2Xe6qRFbIxpAY1MjHqIYj8C3SOmrnPfPs_8MKYzVZyM,543 +openai/types/beta/realtime/response_content_part_added_event.py,sha256=TrOm4oXl9lptnVYVCZ1kzvu7KrH1pC1CLltIfulGrLM,1145 +openai/types/beta/realtime/response_content_part_done_event.py,sha256=JY4yxcj_XGvMp0Ae7Qw8-Vi0Y2GjyHD7FEglzGtUPQ8,1103 +openai/types/beta/realtime/response_create_event.py,sha256=0JZhD6TLXtkD4uMWwTQ9zqLBJTqPvNDYw-2--L5Qu-Y,4676 +openai/types/beta/realtime/response_create_event_param.py,sha256=B2GAynA1dpkbos8fcrTt9b1ZENw8VWgEachnDcFBjBc,4532 +openai/types/beta/realtime/response_created_event.py,sha256=N0D5kegZEriG-mlSxUIr3MrKGv_uaANkwWkJY7t2Wws,419 +openai/types/beta/realtime/response_done_event.py,sha256=J1QbBxYr11E6qAsqTpTYPoV-Uj_MaqNuUr_HIRDdpXc,407 +openai/types/beta/realtime/response_function_call_arguments_delta_event.py,sha256=Yxv5ishffBjhZhkuhFyHGcQUUiILfKTwrl0nW3trbOU,706 +openai/types/beta/realtime/response_function_call_arguments_done_event.py,sha256=S2Rn3JtELfPwHZgyXWPki8L3yNGQDpXptEbXTNWEr4E,706 +openai/types/beta/realtime/response_output_item_added_event.py,sha256=L4MvmNtnAuDJ_9StVP_kvyqJZmQpwBeXlbMSdxD8Ak0,626 +openai/types/beta/realtime/response_output_item_done_event.py,sha256=gKmcMZ-1OlIaLe3pphIH4TO1BPCGTWu_d82W9P0SAwk,622 +openai/types/beta/realtime/response_text_delta_event.py,sha256=dWpB4lmAd4detmV0Eq1ShglY2mtua7Q7F4DD-Yguebk,634 +openai/types/beta/realtime/response_text_done_event.py,sha256=vsbaqQePPRGDAahbDakLXjGRCjyxivhNePM80KiRimw,637 +openai/types/beta/realtime/session.py,sha256=OPXVZ8b1bUOsHOTN45vPvbJyQvRzQOT34v2jcXjlTmM,10096 +openai/types/beta/realtime/session_create_params.py,sha256=4zGfREC5BFE4NPD6y4cnMvq1yxZLXp3FcR3BXK6_WQM,10164 +openai/types/beta/realtime/session_create_response.py,sha256=_wTKGOGQ1KJJ48F83fR6BsJOGYaDZ41yJotc_ez-cLQ,6487 +openai/types/beta/realtime/session_created_event.py,sha256=keOrzqdVwDoEvtotNreIMI-gXvXr5m7DCvIQftT9Q_M,402 +openai/types/beta/realtime/session_update_event.py,sha256=SB1E0GKoDkDb51ViNRv-lEpcSk3MHJRftQaoKTG43W4,11150 +openai/types/beta/realtime/session_update_event_param.py,sha256=EHRvGDdCaaU1p4lAKpTC4kyTlMIUbc6uGOwAubXq2ic,10662 +openai/types/beta/realtime/session_updated_event.py,sha256=J4RjBvW0Wt7XkSjMh1ZDTHCvA9kzkuAjw7M7IzYH9YA,402 +openai/types/beta/realtime/transcription_session.py,sha256=ZfCg8usoDpdHsJ4xxqw1K9g9u-P7ujz-CF7xocRkfTI,3097 +openai/types/beta/realtime/transcription_session_create_params.py,sha256=2jASJQAHLUXmgu9TI-qwOdkDAK9DoUG5iraaQ47vbaM,5889 +openai/types/beta/realtime/transcription_session_update.py,sha256=DlidTYAGlsHRPzEaEWyra19xkEApwJaOFZxKoUJm3Yo,6607 +openai/types/beta/realtime/transcription_session_update_param.py,sha256=i8jL2uuojLiJ-splOl2jKdKyqFoIO3XErIkhfNLBEgs,6366 +openai/types/beta/realtime/transcription_session_updated_event.py,sha256=YfqluSPVhcSJ925WBV1ADancaowy5UNTOYIN5rzV_hE,746 +openai/types/beta/response_compact_params.py,sha256=SAfoTSQl3poQOLtYfS86GG-0gTM6PjKYGRvRFWK0O24,9622 +openai/types/beta/response_create_params.py,sha256=ljj7DgfdssJIfN_s1eda27W1JGGsVUSSj5b46KA1BKA,28182 +openai/types/beta/response_retrieve_params.py,sha256=_Gtdrfx43ynGPDyIvgf-sCsll_tWofyBe0Y6aEX7c4Y,2558 +openai/types/beta/responses/__init__.py,sha256=cdUzBRuqrYtR_TdfHETLE82F5v8XSZ2TJO9NxlWqp2c,459 +openai/types/beta/responses/__pycache__/__init__.cpython-312.pyc,, +openai/types/beta/responses/__pycache__/beta_response_item_list.cpython-312.pyc,, +openai/types/beta/responses/__pycache__/input_item_list_params.cpython-312.pyc,, +openai/types/beta/responses/__pycache__/input_token_count_params.cpython-312.pyc,, +openai/types/beta/responses/__pycache__/input_token_count_response.cpython-312.pyc,, +openai/types/beta/responses/beta_response_item_list.py,sha256=DYTnDRXOypBOJoy7rOCMpyyFzrIRU5vuClLhrDMLn8U,724 +openai/types/beta/responses/input_item_list_params.py,sha256=y0SgzUuYQD-AQyiSOre7XdxvbghygcYGPxguJq8PhIY,1124 +openai/types/beta/responses/input_token_count_params.py,sha256=Gmmb-8JKIV0A8bfo_LDMkOX19hsoelIY--pqptR6Cv4,8724 +openai/types/beta/responses/input_token_count_response.py,sha256=7xALrKByOLGjfJ-sO0MumtArm8j6G07h_ALA6d0rk8U,310 +openai/types/beta/session_error.py,sha256=X64bdtzxl6frlSYnUgoJR203fioMgTQ-W3OvbENm7Qo,593 +openai/types/beta/session_turn_error.py,sha256=XOV7RVEmO4Rxkxz1WjckvM4Ouh3VGzzNLoyO8Zn2eHU,2571 +openai/types/beta/setup_command_param.py,sha256=0dKMA6eOiGeBtuV1SHnak8_5pBYPcDsQ1T2Su8v4F2c,524 +openai/types/beta/subagent.py,sha256=_1_4V7edVcwDlTzKFaNWm_8Xvza-E1OrwK6PVp0UtLY,1474 +openai/types/beta/summary_text.py,sha256=b3_NPbi2jtEAhm0NrFAUdzjeMPswhP7ewaVlwVmv69U,400 +openai/types/beta/text_format.py,sha256=aAigH_FnyR4pGIUFSVkZQYnpxzNi7CLyKvNJBC3kw4s,1014 +openai/types/beta/text_format_param.py,sha256=OcXVxzxXa-LuQf1faSywythcaBuEVTzYcwY0a6yRWUU,916 +openai/types/beta/thread.py,sha256=H8lsPqWqsuwzss0kQ9RAEhqp911SAgazdXUUflmc5ZM,2555 +openai/types/beta/thread_create_and_run_params.py,sha256=tjUCDQfJzU7JrwwKlkg2JezV44JKOsmhRfm0cNXOWkc,16024 +openai/types/beta/thread_create_params.py,sha256=2TX2t1cFVJk31lN8lwI1LxyWyifoh37ZJywVZyVEQWg,7143 +openai/types/beta/thread_deleted.py,sha256=Wfhr-lPmdxZaI101jvmOjybKCEYsM7ZZz7FBgDQk6Gk,291 +openai/types/beta/thread_update_params.py,sha256=f6IcELfixs03wbBtZecekm_4ZlC3BTQPasHVP3nwcjM,2223 +openai/types/beta/threads/__init__.py,sha256=HOslAHnmEZsdtfnHe6o8y-RDIKX-RhuAlNXDr2_e_n0,3065 +openai/types/beta/threads/__pycache__/__init__.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/annotation.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/annotation_delta.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/file_citation_annotation.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/file_citation_delta_annotation.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/file_path_annotation.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/file_path_delta_annotation.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/image_file.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/image_file_content_block.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/image_file_content_block_param.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/image_file_delta.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/image_file_delta_block.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/image_file_param.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/image_url.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/image_url_content_block.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/image_url_content_block_param.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/image_url_delta.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/image_url_delta_block.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/image_url_param.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/message.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/message_content.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/message_content_delta.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/message_content_part_param.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/message_create_params.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/message_deleted.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/message_delta.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/message_delta_event.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/message_list_params.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/message_update_params.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/refusal_content_block.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/refusal_delta_block.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/required_action_function_tool_call.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/run.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/run_create_params.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/run_list_params.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/run_status.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/run_submit_tool_outputs_params.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/run_update_params.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/text.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/text_content_block.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/text_content_block_param.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/text_delta.cpython-312.pyc,, +openai/types/beta/threads/__pycache__/text_delta_block.cpython-312.pyc,, +openai/types/beta/threads/annotation.py,sha256=KkmQNMORntQ910m2DTvOLVpuV0wGly1FFU8jt8wfcZw,461 +openai/types/beta/threads/annotation_delta.py,sha256=ksJkC3Vsf7VIIXgKrByMZzQSiPvvWMAxpVPBv1lOikc,509 +openai/types/beta/threads/file_citation_annotation.py,sha256=RCrjzEBFVAyAi0Q7OwrOLdhHYhJ5pr8KXAGnG3Uifyk,818 +openai/types/beta/threads/file_citation_delta_annotation.py,sha256=zxhwF1EAzAHtvVE_BXX4wIxKwVWmGDE2Xx05TgUE4Jg,1096 +openai/types/beta/threads/file_path_annotation.py,sha256=Bcz4hoPlDIw0y8MGiIORDWmtdhfApZL4y1iycsXNsWI,680 +openai/types/beta/threads/file_path_delta_annotation.py,sha256=uagRK7XrkuOVClMOIG4CA29aIVZIHldmkMMvgAiUAbs,883 +openai/types/beta/threads/image_file.py,sha256=FJk85FIApf2Qoot_1uFctiEAPht9-Tb4ZB22ZqruLMg,713 +openai/types/beta/threads/image_file_content_block.py,sha256=ca7aqR25zWDBbi6dQIIeAmnGlwYPWUuQkHl3DDr2dZQ,500 +openai/types/beta/threads/image_file_content_block_param.py,sha256=ifd98lcz9qGWf3pQSgta557KsU_vldQMJF6fvVHFrhI,582 +openai/types/beta/threads/image_file_delta.py,sha256=k8bppclZJ6OGTK0DI1czCL1stLnOauUwGANNavJmeok,740 +openai/types/beta/threads/image_file_delta_block.py,sha256=qNEPG5aeXazDlTYke8ZIa8spOSK2w1LwIw29Y0SkXd8,629 +openai/types/beta/threads/image_file_param.py,sha256=YZbOmh0oR7JDpscNOsTOncfdyY3yKrrEi0ig03LVysM,723 +openai/types/beta/threads/image_url.py,sha256=nqXpRTMNLZpnTvK-Gmmcq2s-X6jEj82oNt8nH21Ij78,591 +openai/types/beta/threads/image_url_content_block.py,sha256=WwZiyN3AswlEWP4K_0oZ8o73Nu04VJaCm7Z33FnEpS8,428 +openai/types/beta/threads/image_url_content_block_param.py,sha256=X55olgkY55GDiWnuPRaHafiTW5NIFPPext3DL78jh7k,510 +openai/types/beta/threads/image_url_delta.py,sha256=wZipfTU4FLqkmEA-2MzGOcnhqmpieOVsR5-KyfYtnfU,581 +openai/types/beta/threads/image_url_delta_block.py,sha256=9skaHWxp6I6bbWOt5XxChn9ChTGE9X-shEWooS2814g,547 +openai/types/beta/threads/image_url_param.py,sha256=KgIcQHzfDZmm1iQcSW0yB2q3Dn36yu_4WJc_P8RYlPc,601 +openai/types/beta/threads/message.py,sha256=Rb8SNaRX7YrRqmtKy8dXKTIgYLptWDUMsHTeHmObr9Y,3632 +openai/types/beta/threads/message_content.py,sha256=TG5Az6SEjuQ2BaDTJHO73hggb2nDF9glTqf67M_36BU,628 +openai/types/beta/threads/message_content_delta.py,sha256=m6oQRyMvQtFslr3b3xIHzJfveE91t1qUZFlDea5PGOU,614 +openai/types/beta/threads/message_content_part_param.py,sha256=qUO6LB6aZ6gy9IKthoeYw18VozACEiwsLULj4EIo_Q8,549 +openai/types/beta/threads/message_create_params.py,sha256=f_o64Vp1IBM3XoPuD3ouPZFggxTk-aTcrUOJiW5e-nM,2082 +openai/types/beta/threads/message_deleted.py,sha256=l-10MBlpbsNgwLupn30U9njlR38RX4R5TnmQsEL8K98,302 +openai/types/beta/threads/message_delta.py,sha256=B_jU0m4eXqhaY9ftcumGQRpQezPL_RE2gWOqKMGBB3s,646 +openai/types/beta/threads/message_delta_event.py,sha256=csNFbAHzQN7mdhIq9lJRqhE_-9Evn2UHFHvL3i6DMoU,681 +openai/types/beta/threads/message_list_params.py,sha256=ORQvbV1lXgYOvwJTyqietrtRWDDpFIB8L-fx7xHyQSk,1295 +openai/types/beta/threads/message_update_params.py,sha256=WpDD80CGeeqCbkd7wLp8mnzyJJnq7S2YkGYKTuCqBc8,756 +openai/types/beta/threads/refusal_content_block.py,sha256=TlwyLHuJdj7zP60QogVwCBvVD-fCTbSWBmOwlyNBGI4,368 +openai/types/beta/threads/refusal_delta_block.py,sha256=V_9EJgyv_-ayTTX69sU2qUV8Q8y5uYEyNygDVOHoekw,480 +openai/types/beta/threads/required_action_function_tool_call.py,sha256=lUQBDosXloHQCqr91w1wS2dcZMLv6UtJNegc0jxtJ8Q,942 +openai/types/beta/threads/run.py,sha256=tqE-b_Ouqr4WG4ocwhBBeRC4JC9TyGNh0cgD3VpVcso,9164 +openai/types/beta/threads/run_create_params.py,sha256=HLskEnQi9FtuV9f_Mv4a1qs96TEp3gVu4Bqk0iRdwPc,10574 +openai/types/beta/threads/run_list_params.py,sha256=d74cD4uvD7cmS1975w8JykrFIRWp0GSVw82M2xPgd_k,1209 +openai/types/beta/threads/run_status.py,sha256=DBSWkzBqedhm4dBIJ1VQYiyRo7tE2fzeqTofilfPvG4,350 +openai/types/beta/threads/run_submit_tool_outputs_params.py,sha256=bSO0ExERnr7-a_42A_Awsl8gm5Z1mnuhuZrRea7mZtM,1642 +openai/types/beta/threads/run_update_params.py,sha256=xt-5E2JsSlPFuh_65eWNuSe2AnRWAUSL4zuBI7hBOTs,748 +openai/types/beta/threads/runs/__init__.py,sha256=JXswKUBeYO37sh5cQUSLp9tET2ZDjWMQDkEl7_fdSZ0,1652 +openai/types/beta/threads/runs/__pycache__/__init__.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/code_interpreter_logs.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/code_interpreter_output_image.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call_delta.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/file_search_tool_call.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/file_search_tool_call_delta.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/function_tool_call.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/function_tool_call_delta.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/message_creation_step_details.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/run_step.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/run_step_delta.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/run_step_delta_event.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/run_step_delta_message_delta.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/run_step_include.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/step_list_params.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/step_retrieve_params.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/tool_call.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/tool_call_delta.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/tool_call_delta_object.cpython-312.pyc,, +openai/types/beta/threads/runs/__pycache__/tool_calls_step_details.cpython-312.pyc,, +openai/types/beta/threads/runs/code_interpreter_logs.py,sha256=A18zuhp3moUtm8PrKKeP1APOqw7rgzMBx-mHQOs10mY,563 +openai/types/beta/threads/runs/code_interpreter_output_image.py,sha256=2EVyxmMgQzZCGEc6cubvcNI2bVtSTjM4Y4vmcm6WHfA,619 +openai/types/beta/threads/runs/code_interpreter_tool_call.py,sha256=G8SvMik9NA0EoeA_9rdv7T35lrLZEL7KgFG0FnHoWyk,2035 +openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py,sha256=BAasukdHBup9dK0fJDT-ZZvp-1I0mqZq4LlXdcvaglA,1615 +openai/types/beta/threads/runs/file_search_tool_call.py,sha256=nM351fs9U2UrA7OquUWO2iEFV9L3cSB6G1NoKzxkaX4,2126 +openai/types/beta/threads/runs/file_search_tool_call_delta.py,sha256=2F1-_eiaizL5TW-XA7WJ8DrKVE4qC_9httSxbaL3GiQ,654 +openai/types/beta/threads/runs/function_tool_call.py,sha256=-2m2p8zXllPNdRb68gy5U4gfbJiwpryLZpA0kmmUpa4,964 +openai/types/beta/threads/runs/function_tool_call_delta.py,sha256=stz37tlHX6bRQFHx_E78b6M8k8KL9rLjuYEsdg7JYOI,1120 +openai/types/beta/threads/runs/message_creation_step_details.py,sha256=G5_yyYXsF3EtEsTa1Gaa_2AAMHmBgz5sbODTxv8Dhqs,565 +openai/types/beta/threads/runs/run_step.py,sha256=JBp_Co2rq8-QimByCts-e48DbFKIoh5PUzmiH_YASOo,3900 +openai/types/beta/threads/runs/run_step_delta.py,sha256=UBF5nAt7oFt1_DezPbUALoCCvyXNQoFPWs1oDTYEHT0,740 +openai/types/beta/threads/runs/run_step_delta_event.py,sha256=2tVB3XOh1KTODndVQ6Zu3FOA2Kc5Y10ieOYlz3Q8lEU,689 +openai/types/beta/threads/runs/run_step_delta_message_delta.py,sha256=xw7vy7LUcacJHJc4dxDx_pkg2hX3u3laF5ZjwWqF9sw,623 +openai/types/beta/threads/runs/run_step_include.py,sha256=8lfOQr-MoQE7sal0iRewuu2OE9zYl01OsHVw5YBCJRU,263 +openai/types/beta/threads/runs/step_list_params.py,sha256=QPtRno3oqJoyPcNz5TJFJmrC7OOkSBaMsmG0hsB_Ujw,1744 +openai/types/beta/threads/runs/step_retrieve_params.py,sha256=P11FvzlFLEeifEBs75XTY1bIoag6TlNnSeC1LMUhT-w,807 +openai/types/beta/threads/runs/tool_call.py,sha256=HqEPuBqIFc7assjCZFaaLsNqlR24B-RYT0C_h8jRoVk,536 +openai/types/beta/threads/runs/tool_call_delta.py,sha256=P_h3sm2Xg_971Y2IEQH9Bz4ZWiY-qBytFigNL3LVew4,599 +openai/types/beta/threads/runs/tool_call_delta_object.py,sha256=XYUT33R_dWN9y5GdSn9wonqeYekhDTA5umDxy1Urvvw,651 +openai/types/beta/threads/runs/tool_calls_step_details.py,sha256=YnR74YUw70HmYpG3QrmNJu-Nhvrr65lxIgHUcKdVfYI,610 +openai/types/beta/threads/text.py,sha256=UHp0lhE2KG19vXGus457Z0-5cGt-0kOno_xB7tlFv2U,318 +openai/types/beta/threads/text_content_block.py,sha256=zHwx-nIIFm2QrbfUFKQ_C80v9h8gHBcOkjdMc6FRw3U,373 +openai/types/beta/threads/text_content_block_param.py,sha256=4HtVgqqhauhpFqEn5iu3Pzl8H8HNUsFVVS9sEH5M2ic,461 +openai/types/beta/threads/text_delta.py,sha256=eAx08ZMmzAhE7WkYI-pYFTaI9k2ulGtTkGhoQnrcrQk,388 +openai/types/beta/threads/text_delta_block.py,sha256=KcsDFhf5s_KVokRfOKfy8Pc3UtzRnysV6D2oc5F4W5k,502 +openai/types/beta/token_usage.py,sha256=OZWL6TAOR22-NuPseZcE1ek43Y1NjulwkGxDjGl-ozo,1161 +openai/types/beta/web_search_action.py,sha256=d4rauCHufk-I1VyiU75LbM2NsdJ6eJwwHxLIjSSaGrA,1847 +openai/types/chat/__init__.py,sha256=RYilfyPKC1PBJ-9V6MIQ_ZjSVZSdrSD6S0-BchhRZIE,6101 +openai/types/chat/__pycache__/__init__.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_allowed_tool_choice_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_allowed_tools_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_assistant_message_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_audio.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_audio_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_chunk.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_content_part_image.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_content_part_image_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_content_part_input_audio_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_content_part_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_content_part_refusal_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_content_part_text.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_content_part_text_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_custom_tool_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_deleted.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_developer_message_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_function_call_option_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_function_message_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_function_tool.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_function_tool_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_message.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_message_custom_tool_call.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_message_custom_tool_call_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_message_function_tool_call.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_message_function_tool_call_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_message_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_message_tool_call.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_message_tool_call_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_message_tool_call_union_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_modality.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_named_tool_choice_custom_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_named_tool_choice_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_prediction_content_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_reasoning_effort.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_role.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_store_message.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_stream_options_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_system_message_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_token_logprob.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_tool_choice_option_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_tool_message_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_tool_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_tool_union_param.cpython-312.pyc,, +openai/types/chat/__pycache__/chat_completion_user_message_param.cpython-312.pyc,, +openai/types/chat/__pycache__/completion_create_params.cpython-312.pyc,, +openai/types/chat/__pycache__/completion_list_params.cpython-312.pyc,, +openai/types/chat/__pycache__/completion_update_params.cpython-312.pyc,, +openai/types/chat/__pycache__/parsed_chat_completion.cpython-312.pyc,, +openai/types/chat/__pycache__/parsed_function_tool_call.cpython-312.pyc,, +openai/types/chat/chat_completion.py,sha256=cbJU8LQddAu7vBqUpSmP7H_Fip_M8qzsGUjCofK2LxY,8770 +openai/types/chat/chat_completion_allowed_tool_choice_param.py,sha256=UznK5h3Ia41W5EXbkCuOlk0YiVjHnEdUVUeIBJoNEKk,712 +openai/types/chat/chat_completion_allowed_tools_param.py,sha256=Z1gDZrrldAVoPZrjhBWr_m4TsxDUV_jqCgjTrsy9Jjc,1086 +openai/types/chat/chat_completion_assistant_message_param.py,sha256=pDWq4sMK0QM_ujoFMyTJcGA0isxwnq9H58MEECf8JW8,2808 +openai/types/chat/chat_completion_audio.py,sha256=Jaa81Td9lJhTIWikG5EAdv1IVMbbz326QKJzcP8aT70,856 +openai/types/chat/chat_completion_audio_param.py,sha256=mu96lEf3EclN1mvEx-CjFLgcPCIj7CIE6-zBb3QjQzA,1338 +openai/types/chat/chat_completion_chunk.py,sha256=RLwoqW4IimqhdrlCbElWSdUrcI_X2rf2UsrXXPGIuwg,11435 +openai/types/chat/chat_completion_content_part_image.py,sha256=--T3pjvyErDSWEjvrLEsXDpYR6Hl4swjIpuratMzX1I,1472 +openai/types/chat/chat_completion_content_part_image_param.py,sha256=LHGJwmIjI50zq1uFya0OOIHsK_Xy1qVocquQABTi-38,1522 +openai/types/chat/chat_completion_content_part_input_audio_param.py,sha256=ZvoKssZj2fUEJIEZrecfFY7dfz9LAP23TJq2sCuvUU4,1420 +openai/types/chat/chat_completion_content_part_param.py,sha256=Ym8lsxQ9zU_ejiYp1GcNM8qd6Hdr25Ga7_k-_muv4FA,2005 +openai/types/chat/chat_completion_content_part_refusal_param.py,sha256=vVwNegieWoKRe4Fv1kZosL-uEsp61PsT3oHxS4Ww7fw,466 +openai/types/chat/chat_completion_content_part_text.py,sha256=ttpjzNSVdJJhEj1q5OsVJlY3bf8ddotrHfMwSr1Wq30,1089 +openai/types/chat/chat_completion_content_part_text_param.py,sha256=BtFwnSFyEtXvsQMMU64LUv40Ab4dc5-wF3vASwhWWoE,1133 +openai/types/chat/chat_completion_custom_tool_param.py,sha256=OvtI_du5aWDWMbUIufNW6nw2mYSP0WHKjMaMVabB4UI,1866 +openai/types/chat/chat_completion_deleted.py,sha256=fven68z8NVxcdmiL_xPHqa7NaOwZlkPxwK9OqMbuACY,469 +openai/types/chat/chat_completion_developer_message_param.py,sha256=jK9rSFK5SXn0xAO1iOdK3a0fbvt1WYYJiOX3qGo214g,1048 +openai/types/chat/chat_completion_function_call_option_param.py,sha256=AQk74K-2CngnFOGX2KjjwfHPhnFbGLdnJSzW2_NVAnQ,488 +openai/types/chat/chat_completion_function_message_param.py,sha256=TrZXvS09DPhL_qFd5VU3RqDZezn_OtCTAnDYCytyeRc,590 +openai/types/chat/chat_completion_function_tool.py,sha256=ypNIavkNkaBe9uKnb1dOWam6nP2_Cz4Qd0diEAVT7HQ,512 +openai/types/chat/chat_completion_function_tool_param.py,sha256=ejFO3hyZvLV6y2b3wxvTtsbqBZmf9S2PSvtZTeamIj8,586 +openai/types/chat/chat_completion_message.py,sha256=n7Np5UCHGzp93V6KzXPPwDtpc6UiRVFNdQ8AqXfHSjQ,2826 +openai/types/chat/chat_completion_message_custom_tool_call.py,sha256=BaQRgYGI0FMZpFRmTtxOV54Wfn0jpHsyy96v0mkSe4M,749 +openai/types/chat/chat_completion_message_custom_tool_call_param.py,sha256=WGmJZ8gubh22VWMrXBy0RrIP-i3o6J8MTeoEMqQbF8s,858 +openai/types/chat/chat_completion_message_function_tool_call.py,sha256=N-CKK78hC62qmpvHCbbp73PpE371iRPlknxbjk_TC4k,1021 +openai/types/chat/chat_completion_message_function_tool_call_param.py,sha256=wxFHQW4LEemjROAqcGslKyeVkNG-aoRaWy4ThA8ZZuE,1130 +openai/types/chat/chat_completion_message_param.py,sha256=hSa4xe0m4q_MzW8o3kmIW37Tti3O6cYeULB8t5aUeos,1018 +openai/types/chat/chat_completion_message_tool_call.py,sha256=6S0f7QiW8OBFiK1A6VKY6MXSao7OzAn8LTOtENEFX3U,737 +openai/types/chat/chat_completion_message_tool_call_param.py,sha256=FP8EpqYJ-F_nzi2ePBPFyQ4L-w1IBGhld3H6ghsNCAg,371 +openai/types/chat/chat_completion_message_tool_call_union_param.py,sha256=t6P87MisHhnXTQsOUJhoEBKJ0TkrCnxtoAd04EdP2RQ,601 +openai/types/chat/chat_completion_modality.py,sha256=R_KEYCd7be2xNyETzin_t19wQ-Y8RwuI5taVyBzKzFQ,235 +openai/types/chat/chat_completion_named_tool_choice_custom_param.py,sha256=X193J82JCRyERguvswpN0224l-epyojQ1z5Gg0srjn4,679 +openai/types/chat/chat_completion_named_tool_choice_param.py,sha256=kD1baUDBzPf-zmaPBVQx24uc5n7PREIeWzEtdKJjM3g,670 +openai/types/chat/chat_completion_prediction_content_param.py,sha256=INMN3hh_C7esjdqrFtankNul0XsCyEvIctGq2rDDTF4,987 +openai/types/chat/chat_completion_reasoning_effort.py,sha256=QzFDnY4cA5n1u8c7_VMGrNb3VJQgw8K0YSgKqOGpt5c,234 +openai/types/chat/chat_completion_role.py,sha256=igWe3hG2N4eTvlmpzxchMvJ6M70uM9QUiM4VFey8CHI,274 +openai/types/chat/chat_completion_store_message.py,sha256=rA-jj0zEgnJ6b_f1ReXeqOw3wArS7E0xeCohMPZ4BmY,976 +openai/types/chat/chat_completion_stream_options_param.py,sha256=4QIDpjeFA3zgGDX986bpJNsnOTpnOmCEpV9POGpPamc,1396 +openai/types/chat/chat_completion_system_message_param.py,sha256=zlu-74xc2_ChclAYLRU6yXNQJmp9FCc5sO3hRredh7s,1023 +openai/types/chat/chat_completion_token_logprob.py,sha256=KAiHY4eOy7ixUVCNk-i5N8A_xs0Dr8MJWMWIG7lfpOI,1746 +openai/types/chat/chat_completion_tool_choice_option_param.py,sha256=KK9tZHcIBlVvYQE4COH0D2pHZd0i01SCmOcTLgwEE2s,757 +openai/types/chat/chat_completion_tool_message_param.py,sha256=0Tzcbdr-HOlvjZ6uJuJRliwjsfJ11yyCrdkLdRQZsEY,729 +openai/types/chat/chat_completion_tool_param.py,sha256=YDQFz1AmIdix59xpNdilVmHyfb2zTxMB55NQuXogf9Y,344 +openai/types/chat/chat_completion_tool_union_param.py,sha256=uZUBEyRlw9bUz2dT--nhTVdN5Y7QB9qyGOm1RyAwauY,503 +openai/types/chat/chat_completion_user_message_param.py,sha256=Nch9auYKp-2B0Ej8i7CaMY0RjKKfU-zvVaKAp5QZ_QM,900 +openai/types/chat/completion_create_params.py,sha256=WDBvZQUZeHNCo_wQ9VAOkKF6iCg05LVx4eLyw8VbT5w,23201 +openai/types/chat/completion_list_params.py,sha256=3m_q93dqR-bsmN3iMzgMZfkT6-iQu2U3N2QJ3ywKoiY,930 +openai/types/chat/completion_update_params.py,sha256=ppl1PbW4XjQ6d_eDgW8IjNm_qqBNd4qQih-FFJ14YC8,741 +openai/types/chat/completions/__init__.py,sha256=dv_WpNJZK15_r4ENL02Nb1zRhfb0aylSYgPJcY4iHsk,194 +openai/types/chat/completions/__pycache__/__init__.cpython-312.pyc,, +openai/types/chat/completions/__pycache__/message_list_params.cpython-312.pyc,, +openai/types/chat/completions/message_list_params.py,sha256=6gCSW49A8vA1c0iZUC_IWxXK7H7IDXmGnY-wZGOsSaE,582 +openai/types/chat/parsed_chat_completion.py,sha256=hP71ByZ2DS1GvKvYqTUYiBx34nRp4lqtMmMuAZeJ65U,1350 +openai/types/chat/parsed_function_tool_call.py,sha256=txjBDVTi25gTpvMBYfp_5mqS51BZINwUA8W-zEGcnrc,858 +openai/types/chat_model.py,sha256=REqgwLZuM60EqHjHYu3JrHsVJlPsZCfDvwigmWNMPjk,176 +openai/types/completion.py,sha256=NunfGldha2kY-0nlCs7ISSnV9p6tfgeMpO08KQUDJrA,1346 +openai/types/completion_choice.py,sha256=RzgjiNJ4O2Vt-6izQag32O7RvHsKsItQEDNMuNS2yZM,964 +openai/types/completion_create_params.py,sha256=WmHhbU5HV60kck0EJgiYoZLl7PtzAcHBtHBmbovKCvM,7742 +openai/types/completion_usage.py,sha256=UFl1aU4T0To8rH6bLv9wfvixJfDekeMszIn8S3whYaI,2281 +openai/types/container_create_params.py,sha256=ud93Qz63IDrhIDGp2I33iW6FfDGK_i1UpGtI1-sB27E,1770 +openai/types/container_create_response.py,sha256=6dqba5rcftG0V99oI7X1Ko-AbxSGLAFMo-l7N4yMYcc,1990 +openai/types/container_list_params.py,sha256=-I-KVSYiollwomYA03CM80t_XtAUIgld4hIvJZ7s22w,951 +openai/types/container_list_response.py,sha256=OKevhhKQW_Jc0NIycoNew86AIoJ-iEoPPwQdW8Vh1-E,1986 +openai/types/container_retrieve_response.py,sha256=OoGeaq-C-GScANyDTRx6MbqWs9iEWqG3aRT13eOOY8Y,1994 +openai/types/containers/__init__.py,sha256=MzJlHEa2Iksrd5n3Rqycmb2qrnwsocVBPzlm9cdH-oI,479 +openai/types/containers/__pycache__/__init__.cpython-312.pyc,, +openai/types/containers/__pycache__/file_create_params.cpython-312.pyc,, +openai/types/containers/__pycache__/file_create_response.cpython-312.pyc,, +openai/types/containers/__pycache__/file_list_params.cpython-312.pyc,, +openai/types/containers/__pycache__/file_list_response.cpython-312.pyc,, +openai/types/containers/__pycache__/file_retrieve_response.cpython-312.pyc,, +openai/types/containers/file_create_params.py,sha256=LtM7XaomAUrRaEHGqyLtuSS0CqQAxTRD1omN4gBSt9A,411 +openai/types/containers/file_create_response.py,sha256=nsws5wunbNEug_J3BlhF7VIOFk8dIeZBszxefbMG1l8,721 +openai/types/containers/file_list_params.py,sha256=FyYIxSrRSFqZML_uWO30bPomOv7vGF7EIiGGyBwdXAo,882 +openai/types/containers/file_list_response.py,sha256=eFsZDuffjxi_aJojgsJAwbPcSQxGjzeK0iTG5h-k4Qs,717 +openai/types/containers/file_retrieve_response.py,sha256=feFK1SqI__EWJq3sQgVzZ7Rm8L1p-27Ho5ZAxpuZZqQ,725 +openai/types/containers/files/__init__.py,sha256=vQjznC-rGi6u665EpV_FgNWu3tRwEkpL0f6KNLAsRVU,121 +openai/types/containers/files/__pycache__/__init__.cpython-312.pyc,, +openai/types/content_provenance_check.py,sha256=0TF2Am9v4mTyC7r9qraG_iu64iaKHSZ7k8akdtzJdl4,3119 +openai/types/content_provenance_check_create_params.py,sha256=BD_tj6hLjsl2zeWJaPOpgv0yHpxvWE_btNYZxJAHPO4,436 +openai/types/conversations/__init__.py,sha256=GfW2Vj2D0LhONHx-Z0MRxc9gZycYOP_jHB37exr_oyw,1853 +openai/types/conversations/__pycache__/__init__.cpython-312.pyc,, +openai/types/conversations/__pycache__/computer_screenshot_content.cpython-312.pyc,, +openai/types/conversations/__pycache__/conversation.cpython-312.pyc,, +openai/types/conversations/__pycache__/conversation_create_params.cpython-312.pyc,, +openai/types/conversations/__pycache__/conversation_deleted_resource.cpython-312.pyc,, +openai/types/conversations/__pycache__/conversation_item.cpython-312.pyc,, +openai/types/conversations/__pycache__/conversation_item_list.cpython-312.pyc,, +openai/types/conversations/__pycache__/conversation_update_params.cpython-312.pyc,, +openai/types/conversations/__pycache__/input_file_content.cpython-312.pyc,, +openai/types/conversations/__pycache__/input_file_content_param.cpython-312.pyc,, +openai/types/conversations/__pycache__/input_image_content.cpython-312.pyc,, +openai/types/conversations/__pycache__/input_image_content_param.cpython-312.pyc,, +openai/types/conversations/__pycache__/input_text_content.cpython-312.pyc,, +openai/types/conversations/__pycache__/input_text_content_param.cpython-312.pyc,, +openai/types/conversations/__pycache__/item_create_params.cpython-312.pyc,, +openai/types/conversations/__pycache__/item_list_params.cpython-312.pyc,, +openai/types/conversations/__pycache__/item_retrieve_params.cpython-312.pyc,, +openai/types/conversations/__pycache__/message.cpython-312.pyc,, +openai/types/conversations/__pycache__/output_text_content.cpython-312.pyc,, +openai/types/conversations/__pycache__/output_text_content_param.cpython-312.pyc,, +openai/types/conversations/__pycache__/refusal_content.cpython-312.pyc,, +openai/types/conversations/__pycache__/refusal_content_param.cpython-312.pyc,, +openai/types/conversations/__pycache__/summary_text_content.cpython-312.pyc,, +openai/types/conversations/__pycache__/text_content.cpython-312.pyc,, +openai/types/conversations/computer_screenshot_content.py,sha256=0anym_jUd9UzJ1GJngs0u4cTfHZVbHyWtr1BEx5kTK8,1504 +openai/types/conversations/conversation.py,sha256=F27xtwiLkZUzUhmBRthp5Mm_EAY7P62e8OOnZPX-VhI,885 +openai/types/conversations/conversation_create_params.py,sha256=ycLySKao6e3_kFwDjy_8PMi-0eYXqJk-zK7KnQa7qdA,975 +openai/types/conversations/conversation_deleted_resource.py,sha256=rI-px8srjpNmYP16-JCMoVGV_D4Db_LkfpNom2es5cU,325 +openai/types/conversations/conversation_item.py,sha256=vWIfmcb0bC-iyCuc_fSmA7sYs2Z_5XxTC2SO3vyfg4Y,10540 +openai/types/conversations/conversation_item_list.py,sha256=xbbPYxHfmLkNWShyfTBtO2Dz0T4NijiDSxTuEe-6n8o,707 +openai/types/conversations/conversation_update_params.py,sha256=Tk82_SATsC12xFyqYXhDRic1xWnzEihC7_IyPcjK3sw,744 +openai/types/conversations/input_file_content.py,sha256=SoOB00v4R9o-Nx7aVsc1TIfCfGI7X1Z25gPj_F5PFSs,218 +openai/types/conversations/input_file_content_param.py,sha256=7LpYXZcFp9U6lEwybqNL9qxrevs00UhjQtHPwzMuwoY,243 +openai/types/conversations/input_image_content.py,sha256=f7-tssji80PUlb1w1KV3386IN1lCaFR8l60yKI6hHs0,223 +openai/types/conversations/input_image_content_param.py,sha256=ErgsnmG2cb9DrWwrR-5ZmaG86D3B3_Am-k-GI9zCs6Q,247 +openai/types/conversations/input_text_content.py,sha256=O4sBgyaD3vby0GpxqMLADY6OtWEfwPpsMRSPkiwrVz4,218 +openai/types/conversations/input_text_content_param.py,sha256=MW5-agxNO2xbjrUQ-L6F_ZL3P5alHvC60aK8Myhq6qI,243 +openai/types/conversations/item_create_params.py,sha256=xoPpjUH5saAXCcjLnJH-sTpZ8B5CcUmth-FUuImPf2U,1015 +openai/types/conversations/item_list_params.py,sha256=bB7TQqBR88GQSIDKjo-XvK3IaECsu3y4GIBt_tcegKk,1895 +openai/types/conversations/item_retrieve_params.py,sha256=FuMpstLwCRixYT_EwZzGUQW822Ejzu6dQhjZkxQ9My8,831 +openai/types/conversations/message.py,sha256=mYGZVr4jL83SdGGPbhI24Rr0e18HjMTxXJpreZbNzs0,2534 +openai/types/conversations/output_text_content.py,sha256=yuIivXF9W1yXSbhJ0Adgx1h6ZSljIQNqeZ5pHGVjSNM,223 +openai/types/conversations/output_text_content_param.py,sha256=9D1_BdFY1CN8m94xGkN_Be1Bt1UyLAF6brppbPFJYzY,247 +openai/types/conversations/refusal_content.py,sha256=v5kaZpQbOOSVEfwHI_NoSXobj24N3yoO2DOXoD0Bbxc,226 +openai/types/conversations/refusal_content_param.py,sha256=p3emWUTNEUmRrye-FCLdnxbOGFmFfKPvxL745070Aq4,253 +openai/types/conversations/summary_text_content.py,sha256=Zz8hfI5WcKVMoogWk4mn2_c9Gk9LPFQoryOCdUGphvI,446 +openai/types/conversations/text_content.py,sha256=1AJBztm2az-JTO13WoCO6afml-dO64wROKDZmoXhnXI,285 +openai/types/create_embedding_response.py,sha256=elolMUoltfwTNh3j937s_c1cd-UsQClVWIlXv_zr4Tw,847 +openai/types/deleted_skill.py,sha256=VuEU-JsJpk6Pu-W101VQwaB2zyNUA_ifnHrO0NOrWJU,287 +openai/types/embedding.py,sha256=ETSO9TfNOZTAOVt-9VZAtVUE0bL-nVxN7sManhYLaUI,716 +openai/types/embedding_create_params.py,sha256=C0-CNZg7nG7QT5q1VjsiyfFE5G5Zf_jfwISi8CGmfD8,2081 +openai/types/embedding_model.py,sha256=2Q_PHUfS2Zr7QjOKDLmO_d-5pVysyxDFASCGFigQV24,280 +openai/types/eval_create_params.py,sha256=mtuISse7pPszGZVHB-MVPoWUOubTkXHZxU1D5I7K6G0,8238 +openai/types/eval_create_response.py,sha256=ik4M4MyvG3jxacuRY19TXrkI15YJt4TDSgZLajRhOIo,4472 +openai/types/eval_custom_data_source_config.py,sha256=qv6cpaz0ZVi2k3Y6QHdo-p2VsD42EO-MW-N0VLO1E6E,875 +openai/types/eval_delete_response.py,sha256=XnJBUCERAUBJjsyltleZjczJklIyiTE0zcrIzcnlIMg,244 +openai/types/eval_list_params.py,sha256=51Ca9f7P3jDcDfeQyfEYYFXM0HplSV6vOZfW6gl76U0,753 +openai/types/eval_list_response.py,sha256=ZoVhZkdBzQ5ZAzR-V82KxdzdfUZbcbIuh2OxfrV0I_I,4468 +openai/types/eval_retrieve_response.py,sha256=mRWc98QOazv3rSE8DZZMuuYGZa0jfjkat4gh7PpMx1Y,4476 +openai/types/eval_stored_completions_data_source_config.py,sha256=J0n4dXaVOoYOgZgr7SvRyJUT2ilrR6W494LOVk2HCSE,1136 +openai/types/eval_update_params.py,sha256=_4QgRdZZknMLdScVoPOiwbMQsWSHLCTzRsj24uBCXCw,756 +openai/types/eval_update_response.py,sha256=0_NQRSxuqh1XGTEyA7DJxkzjdEbEmQRmqPInKYAw7tA,4472 +openai/types/evals/__init__.py,sha256=B4O9D-A37ycZWPe2Zl8eN_59mYDba2TvWW4lVd-EJZc,1192 +openai/types/evals/__pycache__/__init__.cpython-312.pyc,, +openai/types/evals/__pycache__/create_eval_completions_run_data_source.cpython-312.pyc,, +openai/types/evals/__pycache__/create_eval_completions_run_data_source_param.cpython-312.pyc,, +openai/types/evals/__pycache__/create_eval_jsonl_run_data_source.cpython-312.pyc,, +openai/types/evals/__pycache__/create_eval_jsonl_run_data_source_param.cpython-312.pyc,, +openai/types/evals/__pycache__/eval_api_error.cpython-312.pyc,, +openai/types/evals/__pycache__/run_cancel_response.cpython-312.pyc,, +openai/types/evals/__pycache__/run_create_params.cpython-312.pyc,, +openai/types/evals/__pycache__/run_create_response.cpython-312.pyc,, +openai/types/evals/__pycache__/run_delete_response.cpython-312.pyc,, +openai/types/evals/__pycache__/run_list_params.cpython-312.pyc,, +openai/types/evals/__pycache__/run_list_response.cpython-312.pyc,, +openai/types/evals/__pycache__/run_retrieve_response.cpython-312.pyc,, +openai/types/evals/create_eval_completions_run_data_source.py,sha256=JcLiiDVIRiwlhSlSSkg7RyMf-PsW4fKTA681zPfgOVc,8762 +openai/types/evals/create_eval_completions_run_data_source_param.py,sha256=yN6g9x5G44W_zAJIlFpH28E0aENR2kW4mLdMl9ZEjgo,8845 +openai/types/evals/create_eval_jsonl_run_data_source.py,sha256=NsyIZeY7_n6pDtilkdF1NunG-AE2MXc0sI5I_maiMUA,1322 +openai/types/evals/create_eval_jsonl_run_data_source_param.py,sha256=0SpUl2AejpSMIaZ-cFJ3L2MwwW12BbYPJphtecgjOUQ,1388 +openai/types/evals/eval_api_error.py,sha256=nnsAff6DBLIVJYON9pzrbH1YEPotJvnVYk4uflDGeto,338 +openai/types/evals/run_cancel_response.py,sha256=EnscmWBDuSllYhqNFXysPnl8BigBE2FY72ud5zxVWZg,15214 +openai/types/evals/run_create_params.py,sha256=RUr9LjvgtxW1ES2di_ve6jYEFhxQO-N1e9GZcGEhlEA,14158 +openai/types/evals/run_create_response.py,sha256=DCsDgXoyfGDZ6FHjFFESG-AVnN3DYMI3u5BLI80yIro,15214 +openai/types/evals/run_delete_response.py,sha256=3vNmLv0tuKO6KFM3Csa47DKb3c7s5GmbBspJRAQJ_dM,322 +openai/types/evals/run_list_params.py,sha256=5iT1dPQ2X1Hs1hFCHw6YKgfqbPw-rCMM759ADSUYApw,757 +openai/types/evals/run_list_response.py,sha256=IaLCgtf1IkynRG9_2pxhKAA0a0RzaOCtM51HPh60tq0,15210 +openai/types/evals/run_retrieve_response.py,sha256=jMOo7mc99zhWqbC_ud3FD1k-RwSX2WPeQ9iJ6_PRw88,15218 +openai/types/evals/runs/__init__.py,sha256=qTf6-Gclkr8tMXndUvtN0vQyeris_L4TSAPkSYmrP-0,392 +openai/types/evals/runs/__pycache__/__init__.cpython-312.pyc,, +openai/types/evals/runs/__pycache__/output_item_list_params.cpython-312.pyc,, +openai/types/evals/runs/__pycache__/output_item_list_response.cpython-312.pyc,, +openai/types/evals/runs/__pycache__/output_item_retrieve_response.cpython-312.pyc,, +openai/types/evals/runs/output_item_list_params.py,sha256=-_pylGGPD5MG50z1RUs4bd-RvpRW45lYjdKGmar89FE,820 +openai/types/evals/runs/output_item_list_response.py,sha256=aaIREhhutp8ZRm2Si6-IlgvxjXa_6pDQAVluIuBBqIc,4119 +openai/types/evals/runs/output_item_retrieve_response.py,sha256=ex5BNhKtm8i91rKAdhPMb5lkNa-OmtbJBAxHqDHhqAU,4127 +openai/types/file_chunking_strategy.py,sha256=Mv55ozGv-5e3IuERf2U2dUOmeI0AhSSgFjlP4n4USNk,558 +openai/types/file_chunking_strategy_param.py,sha256=O1rwu83s3j96Y2l53Wt7TXtZCd6XJ7-wOBJw_IHL8ps,516 +openai/types/file_content.py,sha256=zidAV08MvugbA5sl8AptCTYXgMzmzhDcDTfipaUezUA,183 +openai/types/file_create_params.py,sha256=TiAiHkAHQkl6Mi4_qPxbpJzXx6sm2qPUzTBncVWjU38,1583 +openai/types/file_deleted.py,sha256=vsx17gGcCfsO_xOvC2qYRqgK-kaOlIyyclc9nR6pE2s,276 +openai/types/file_list_params.py,sha256=CdV4x--4j2HPDlGWi2BY7iQojF6LkEhpUxV9PKa8wKM,959 +openai/types/file_object.py,sha256=mZIKiODTIPRx8JschmXxZRTJl1yjfqq4_yy4EmlW60Y,1588 +openai/types/file_purpose.py,sha256=0an8X-L8qDsflOqMO0WDG0sJmZ1x88dtiU4JC3ckbHM,264 +openai/types/fine_tuning/__init__.py,sha256=jPQXX6d9TPR-U9nAMBBpHpxW4MYd_dRPmIIJTH1hfvA,1831 +openai/types/fine_tuning/__pycache__/__init__.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/dpo_hyperparameters.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/dpo_hyperparameters_param.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/dpo_method.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/dpo_method_param.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/fine_tuning_job.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/fine_tuning_job_event.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/fine_tuning_job_integration.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration_object.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/job_create_params.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/job_list_events_params.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/job_list_params.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/reinforcement_hyperparameters.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/reinforcement_hyperparameters_param.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/reinforcement_method.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/reinforcement_method_param.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/supervised_hyperparameters.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/supervised_hyperparameters_param.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/supervised_method.cpython-312.pyc,, +openai/types/fine_tuning/__pycache__/supervised_method_param.cpython-312.pyc,, +openai/types/fine_tuning/alpha/__init__.py,sha256=vUFdl3uLZOk1efIktg-P_RYjxJD1OB-qjkZaXckqM3Y,428 +openai/types/fine_tuning/alpha/__pycache__/__init__.cpython-312.pyc,, +openai/types/fine_tuning/alpha/__pycache__/grader_run_params.cpython-312.pyc,, +openai/types/fine_tuning/alpha/__pycache__/grader_run_response.cpython-312.pyc,, +openai/types/fine_tuning/alpha/__pycache__/grader_validate_params.cpython-312.pyc,, +openai/types/fine_tuning/alpha/__pycache__/grader_validate_response.cpython-312.pyc,, +openai/types/fine_tuning/alpha/grader_run_params.py,sha256=3T-5X9eCPmMNkTQs35s2PZPEaDhYp5xoyJ0gzeoihTU,1429 +openai/types/fine_tuning/alpha/grader_run_response.py,sha256=JKrvxto7xLEGDZBhsug2Fgfk2iAQC8vm3L99X_alNE8,1555 +openai/types/fine_tuning/alpha/grader_validate_params.py,sha256=5tZ2ZkKUbtkP_GFpf4eQi5o3SiBlLqWIdSy1Evuqv_8,874 +openai/types/fine_tuning/alpha/grader_validate_response.py,sha256=OqHytCvNkh2y2zM1g5bnjC8Acz9emFKh6Q7VpHp3xa4,772 +openai/types/fine_tuning/checkpoints/__init__.py,sha256=6XRVdCZBxKfZKAhA7ADWy6Os3jVlVfiuggs6sr99H64,755 +openai/types/fine_tuning/checkpoints/__pycache__/__init__.cpython-312.pyc,, +openai/types/fine_tuning/checkpoints/__pycache__/permission_create_params.cpython-312.pyc,, +openai/types/fine_tuning/checkpoints/__pycache__/permission_create_response.cpython-312.pyc,, +openai/types/fine_tuning/checkpoints/__pycache__/permission_delete_response.cpython-312.pyc,, +openai/types/fine_tuning/checkpoints/__pycache__/permission_list_params.cpython-312.pyc,, +openai/types/fine_tuning/checkpoints/__pycache__/permission_list_response.cpython-312.pyc,, +openai/types/fine_tuning/checkpoints/__pycache__/permission_retrieve_params.cpython-312.pyc,, +openai/types/fine_tuning/checkpoints/__pycache__/permission_retrieve_response.cpython-312.pyc,, +openai/types/fine_tuning/checkpoints/permission_create_params.py,sha256=ID8woQR86RK2s-03JoFu0FmvQqGKCrFA6K9X5lOobP8,406 +openai/types/fine_tuning/checkpoints/permission_create_response.py,sha256=X0PTp1UbEvSotWK118qYcgRqyBQSSXsflDe8QOyFkwc,750 +openai/types/fine_tuning/checkpoints/permission_delete_response.py,sha256=UT4qnF-8zHam9CFWJvx2iAKpr9XxcVkkz3deyw_KvWE,557 +openai/types/fine_tuning/checkpoints/permission_list_params.py,sha256=a4ytwg2IabYHcRSHragLQhpTt0w8KMRgAeJszHBukQw,601 +openai/types/fine_tuning/checkpoints/permission_list_response.py,sha256=mB8wVBGg9zVk_AFrUyjiBCxqATIf71CnFFEawieB9hc,746 +openai/types/fine_tuning/checkpoints/permission_retrieve_params.py,sha256=b7-ZsAeyZOgLdvtY0hV6plimFGUrhxmAjqrqQsqDX8Q,609 +openai/types/fine_tuning/checkpoints/permission_retrieve_response.py,sha256=z5M-4h_kgKSFdcWpY81Vz7TxBsm9nYU3E5OrDWnM0bM,962 +openai/types/fine_tuning/dpo_hyperparameters.py,sha256=ldvISF6kgUNucxHnmDgKFH1rqLjkXF_NSW1RG7JKYSg,1128 +openai/types/fine_tuning/dpo_hyperparameters_param.py,sha256=wPO3jwAB1OTiUCD0QTFHZQnOjyzehz0J28BIjq6o3eM,1112 +openai/types/fine_tuning/dpo_method.py,sha256=Jxmzw0yCVLV_q8pWiXEVLjtYKtcos9B7xbwDMBMz-Mg,433 +openai/types/fine_tuning/dpo_method_param.py,sha256=xvNWgNNTXQxOR6VxcnH_OaTiAVZ6BUN4746N6kiCL64,470 +openai/types/fine_tuning/fine_tuning_job.py,sha256=ZW-lUq5iRf9qgRgU1_SrgL0fqNDGu5QqXgRqzrW-HSM,5676 +openai/types/fine_tuning/fine_tuning_job_event.py,sha256=xTcnU5Qqg5boHjxLjMWoochZs0nTglOlChEcTZ0GHL4,893 +openai/types/fine_tuning/fine_tuning_job_integration.py,sha256=rdb1QOJJPOHJ5LGMRRZprNREQ_hP_DZNA7pSmRNMP4U,240 +openai/types/fine_tuning/fine_tuning_job_wandb_integration.py,sha256=jSMFX7JAw0Kbj0-9p3RLzDJJXNJQyyhBk4sfdqhEpbw,1342 +openai/types/fine_tuning/fine_tuning_job_wandb_integration_object.py,sha256=heaXrNsvCG0-vBpszSl_USiiYbB6fOknFlwXi8JPQuw,803 +openai/types/fine_tuning/job_create_params.py,sha256=Lr00HFWjxJLvA6EdOF_pGVytZBiO3ZZbxJV49WUxTHA,6785 +openai/types/fine_tuning/job_list_events_params.py,sha256=MqT6DfGTbgY99aXhOE1b24HJgWQwjMNI3aMuYgt2GNw,399 +openai/types/fine_tuning/job_list_params.py,sha256=0q_Zrz3i7LfTpw7Vs-MSI-NEGfG2D11os1wmlavAhV0,622 +openai/types/fine_tuning/jobs/__init__.py,sha256=nnMcjbW4iP1CG9xOT6UVlDehZZKxqP44-qdAZdDSxrA,294 +openai/types/fine_tuning/jobs/__pycache__/__init__.cpython-312.pyc,, +openai/types/fine_tuning/jobs/__pycache__/checkpoint_list_params.cpython-312.pyc,, +openai/types/fine_tuning/jobs/__pycache__/fine_tuning_job_checkpoint.cpython-312.pyc,, +openai/types/fine_tuning/jobs/checkpoint_list_params.py,sha256=QWrfAlUliYoarNmnWYJu0TWTSupYzm7AaGQwbeYbL3U,414 +openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py,sha256=9d9QH5BXspTnVVtXfX-ER46whShPlCmE_EtyA22Ks94,1547 +openai/types/fine_tuning/reinforcement_hyperparameters.py,sha256=Qkue-y19nesbC1HOQv1qyLkzkX0NTkrurrA64rDbUO0,1500 +openai/types/fine_tuning/reinforcement_hyperparameters_param.py,sha256=Fnj3yV0AY7QsFmpR3agYApdC-rVO_OpzDWeqAJ7uxdA,1431 +openai/types/fine_tuning/reinforcement_method.py,sha256=6FFvjDWkQlwDrr8Ab-9En7-DP88-OrZTNkCCIZzJF2E,1024 +openai/types/fine_tuning/reinforcement_method_param.py,sha256=tcnYrGa0fAC3rFIiGspgT0zhkUsVvtOy5DQePiNlM1U,1156 +openai/types/fine_tuning/supervised_hyperparameters.py,sha256=ld40Lv0fkbqUSTx6oceQUoVvG4BIuWhK-nzWuWAIZa0,925 +openai/types/fine_tuning/supervised_hyperparameters_param.py,sha256=0EkSjTwew2_xLbXab2yhDn7XBOZLKSVP9zL1PQ88g5s,922 +openai/types/fine_tuning/supervised_method.py,sha256=tlmLA5_toKtYg0m5FZR1hlYktrb8pwnkAw_vL1kPOF4,471 +openai/types/fine_tuning/supervised_method_param.py,sha256=Sah_xgH19Mv0cf1TxDd5PJQpP3GSBmoCiK4lSV0kSzk,508 +openai/types/graders/__init__.py,sha256=bDfeEYwEmJvz-VbwJrqIAj9QXGJxJ1k-BEl8E9FPgRg,1146 +openai/types/graders/__pycache__/__init__.cpython-312.pyc,, +openai/types/graders/__pycache__/grader_inputs.cpython-312.pyc,, +openai/types/graders/__pycache__/grader_inputs_param.cpython-312.pyc,, +openai/types/graders/__pycache__/label_model_grader.cpython-312.pyc,, +openai/types/graders/__pycache__/label_model_grader_param.cpython-312.pyc,, +openai/types/graders/__pycache__/multi_grader.cpython-312.pyc,, +openai/types/graders/__pycache__/multi_grader_param.cpython-312.pyc,, +openai/types/graders/__pycache__/python_grader.cpython-312.pyc,, +openai/types/graders/__pycache__/python_grader_param.cpython-312.pyc,, +openai/types/graders/__pycache__/score_model_grader.cpython-312.pyc,, +openai/types/graders/__pycache__/score_model_grader_param.cpython-312.pyc,, +openai/types/graders/__pycache__/string_check_grader.cpython-312.pyc,, +openai/types/graders/__pycache__/string_check_grader_param.cpython-312.pyc,, +openai/types/graders/__pycache__/text_similarity_grader.cpython-312.pyc,, +openai/types/graders/__pycache__/text_similarity_grader_param.cpython-312.pyc,, +openai/types/graders/grader_inputs.py,sha256=JsyciOjxcOfET6hcZ1HavEbd4o8beSkLhBJgO8cIBn8,1298 +openai/types/graders/grader_inputs_param.py,sha256=bHV89uHacD2I90znBB33oSQr_8Y39rwUonFjdhtw4yw,1493 +openai/types/graders/label_model_grader.py,sha256=t5uh2FUPNViY2zmYMCg2CsfOxFtA33jdokvo4dUuSXE,2764 +openai/types/graders/label_model_grader_param.py,sha256=0XNzZhwZOd02pRHTojPRKakLBSGtkZtbJD9T0V4rhg0,3060 +openai/types/graders/multi_grader.py,sha256=Q5jm5Ot5l_MKeeH-w6wFngzjayS3SHvLs2uQwdFuX3Q,1126 +openai/types/graders/multi_grader_param.py,sha256=Hb7GXQbNxZ2iPjyBLmW-iNbIBsLVoqxJPsRWkShE7KM,1299 +openai/types/graders/python_grader.py,sha256=CH3YbDzFn3CNmhJ2GyhuniLUQCDMK2qTljxN0EhzhiY,606 +openai/types/graders/python_grader_param.py,sha256=F5i2LwfhU9Dw6em0CFChm0EfvrVla_FQP0rtZyJLuUo,637 +openai/types/graders/score_model_grader.py,sha256=iatJboePNxV5LZcPjCkkw6spaVy-mDYVCmnBJrDdedg,4036 +openai/types/graders/score_model_grader_param.py,sha256=VB74MUtEzMo7avGI-q_pc_3GbLl-3eKsIW6PZ8YtI6U,4213 +openai/types/graders/string_check_grader.py,sha256=cXH23eY2HvJF89-gvBD0TFkjfffEbe97ayLeCSlGS5U,813 +openai/types/graders/string_check_grader_param.py,sha256=PYvdrov8i2YcXqFDe6HenISh6lCkSSgw023AVcGC32s,909 +openai/types/graders/text_similarity_grader.py,sha256=wLMi8nl63X0dIkKbodusA8foqMhH9IImuO1X4JWBg-Y,974 +openai/types/graders/text_similarity_grader_param.py,sha256=nJdD_wvB-_JZZHJ_gEWLVT81HwMRttCDlt-ogLVs0wg,1132 +openai/types/image.py,sha256=WWqJiYC7y5jW5hZfeNYqTPgpRBDGyMEf-5FjEkssuI8,871 +openai/types/image_create_variation_params.py,sha256=MxjpdQmSGFIX5eSiQ4R41a_2pdZ4-hq0G4LE-BAlLvM,1447 +openai/types/image_edit_completed_event.py,sha256=vS8tSSwds703zqRFuc0RIhgeO8tgp0IBliNI8f7zYyU,2119 +openai/types/image_edit_params.py,sha256=nVfbdfHhEqSkQysnE5IEofsGyhxwsDMwJrhWirZC4YQ,6843 +openai/types/image_edit_partial_image_event.py,sha256=xXAqyYgsRwCYUmhfpmO_n7ZHg0jeTBy5sKIVgb2qTL0,1265 +openai/types/image_edit_stream_event.py,sha256=P3QvxPcbc9J9O2B3ri0ZPvQAuvdp2iwdHKmga2pvvUc,515 +openai/types/image_gen_completed_event.py,sha256=iPyXxFFXFIcXHYWKD8SnP9ZCAFHfQkY4-g0BrAOcnmA,2128 +openai/types/image_gen_partial_image_event.py,sha256=NzFREf1kT_a1qQHtd4Bzn3GVvJVmmFrCy6YZRJMH5go,1257 +openai/types/image_gen_stream_event.py,sha256=L6j86Qem5IkrI1Ls8rRIYjE8g06QbHkH1u9c_rsh2Zo,507 +openai/types/image_generate_params.py,sha256=Biy3vVcvNrXhRWW_eRmdK7zha9NCPmENQO0QkXhqfDg,6634 +openai/types/image_input_reference_param.py,sha256=dz4RBL50wsIPOMXgS3yVGbgquMVW17htsecJ7aSvRZ0,357 +openai/types/image_model.py,sha256=UB3bAaUq-6KK2T2DKNiFMPAesLvwAOavwCuyabqFc6c,506 +openai/types/images_response.py,sha256=RVQ3dt-YDLK0zl0N-hgAlZr4hkGkqRW_GeZl_tzLtCI,2611 +openai/types/live/__init__.py,sha256=QW961zw256Q0D6iBG9vk0tnjY7grHBsAe0q5Kxc3KX0,7233 +openai/types/live/__pycache__/__init__.cpython-312.pyc,, +openai/types/live/__pycache__/audio_format.cpython-312.pyc,, +openai/types/live/__pycache__/audio_format_param.cpython-312.pyc,, +openai/types/live/__pycache__/built_in_voice.cpython-312.pyc,, +openai/types/live/__pycache__/client_config.cpython-312.pyc,, +openai/types/live/__pycache__/client_config_param.cpython-312.pyc,, +openai/types/live/__pycache__/client_delegation.cpython-312.pyc,, +openai/types/live/__pycache__/client_delegation_param.cpython-312.pyc,, +openai/types/live/__pycache__/client_event.cpython-312.pyc,, +openai/types/live/__pycache__/client_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/commentary_append_event.cpython-312.pyc,, +openai/types/live/__pycache__/commentary_append_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/commentary_appended_event.cpython-312.pyc,, +openai/types/live/__pycache__/connect_client_event.cpython-312.pyc,, +openai/types/live/__pycache__/connect_client_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/connect_server_event.cpython-312.pyc,, +openai/types/live/__pycache__/custom_voice.cpython-312.pyc,, +openai/types/live/__pycache__/custom_voice_param.cpython-312.pyc,, +openai/types/live/__pycache__/data_channel_config.cpython-312.pyc,, +openai/types/live/__pycache__/data_channel_config_param.cpython-312.pyc,, +openai/types/live/__pycache__/delegation_created_event.cpython-312.pyc,, +openai/types/live/__pycache__/error.cpython-312.pyc,, +openai/types/live/__pycache__/error_event.cpython-312.pyc,, +openai/types/live/__pycache__/fork_client_event.cpython-312.pyc,, +openai/types/live/__pycache__/fork_client_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/fork_server_event.cpython-312.pyc,, +openai/types/live/__pycache__/fork_session_config.cpython-312.pyc,, +openai/types/live/__pycache__/fork_session_config_param.cpython-312.pyc,, +openai/types/live/__pycache__/fork_session_start_event.cpython-312.pyc,, +openai/types/live/__pycache__/fork_session_start_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/function_tool.cpython-312.pyc,, +openai/types/live/__pycache__/function_tool_param.cpython-312.pyc,, +openai/types/live/__pycache__/info_event.cpython-312.pyc,, +openai/types/live/__pycache__/initial_item.cpython-312.pyc,, +openai/types/live/__pycache__/initial_item_param.cpython-312.pyc,, +openai/types/live/__pycache__/input_audio_append_event.cpython-312.pyc,, +openai/types/live/__pycache__/input_audio_append_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/input_audio_mute_event.cpython-312.pyc,, +openai/types/live/__pycache__/input_audio_mute_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/input_audio_muted_event.cpython-312.pyc,, +openai/types/live/__pycache__/input_audio_unmute_event.cpython-312.pyc,, +openai/types/live/__pycache__/input_audio_unmute_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/input_audio_unmuted_event.cpython-312.pyc,, +openai/types/live/__pycache__/input_transcript_delta_event.cpython-312.pyc,, +openai/types/live/__pycache__/instructions_append_event.cpython-312.pyc,, +openai/types/live/__pycache__/instructions_append_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/instructions_appended_event.cpython-312.pyc,, +openai/types/live/__pycache__/live_create_params.cpython-312.pyc,, +openai/types/live/__pycache__/live_create_response.cpython-312.pyc,, +openai/types/live/__pycache__/media_session_config_param.cpython-312.pyc,, +openai/types/live/__pycache__/media_session_fork_config_param.cpython-312.pyc,, +openai/types/live/__pycache__/output_audio_delta_event.cpython-312.pyc,, +openai/types/live/__pycache__/output_transcript_delta_event.cpython-312.pyc,, +openai/types/live/__pycache__/response_create_event.cpython-312.pyc,, +openai/types/live/__pycache__/response_create_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/response_event.cpython-312.pyc,, +openai/types/live/__pycache__/response_item_create_event.cpython-312.pyc,, +openai/types/live/__pycache__/response_item_create_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/responses_delegation_config.cpython-312.pyc,, +openai/types/live/__pycache__/responses_delegation_config_param.cpython-312.pyc,, +openai/types/live/__pycache__/responses_delegation_update_config.cpython-312.pyc,, +openai/types/live/__pycache__/responses_delegation_update_config_param.cpython-312.pyc,, +openai/types/live/__pycache__/server_event.cpython-312.pyc,, +openai/types/live/__pycache__/server_event_selector.cpython-312.pyc,, +openai/types/live/__pycache__/server_event_selector_param.cpython-312.pyc,, +openai/types/live/__pycache__/session_accept_params.cpython-312.pyc,, +openai/types/live/__pycache__/session_close_event.cpython-312.pyc,, +openai/types/live/__pycache__/session_close_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/session_closed_event.cpython-312.pyc,, +openai/types/live/__pycache__/session_config.cpython-312.pyc,, +openai/types/live/__pycache__/session_config_param.cpython-312.pyc,, +openai/types/live/__pycache__/session_fork_params.cpython-312.pyc,, +openai/types/live/__pycache__/session_fork_response.cpython-312.pyc,, +openai/types/live/__pycache__/session_refer_params.cpython-312.pyc,, +openai/types/live/__pycache__/session_reject_params.cpython-312.pyc,, +openai/types/live/__pycache__/session_resource.cpython-312.pyc,, +openai/types/live/__pycache__/session_start_event.cpython-312.pyc,, +openai/types/live/__pycache__/session_start_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/session_started_event.cpython-312.pyc,, +openai/types/live/__pycache__/session_update_config.cpython-312.pyc,, +openai/types/live/__pycache__/session_update_config_param.cpython-312.pyc,, +openai/types/live/__pycache__/session_update_event.cpython-312.pyc,, +openai/types/live/__pycache__/session_update_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/session_updated_event.cpython-312.pyc,, +openai/types/live/__pycache__/session_usage.cpython-312.pyc,, +openai/types/live/__pycache__/session_usage_updated_event.cpython-312.pyc,, +openai/types/live/__pycache__/sideband_connect_params.cpython-312.pyc,, +openai/types/live/__pycache__/thinking_append_event.cpython-312.pyc,, +openai/types/live/__pycache__/thinking_append_event_param.cpython-312.pyc,, +openai/types/live/__pycache__/thinking_appended_event.cpython-312.pyc,, +openai/types/live/audio_format.py,sha256=c18VqBDuIjhZfNFURCaLdBLXv5oRdnOhDod4IQL-Egc,1275 +openai/types/live/audio_format_param.py,sha256=X38F1DmdpectYaOB9NI7Bt_OvmNY5RGC8wfH8t5WPEA,1314 +openai/types/live/built_in_voice.py,sha256=3-dVpfRGf-IhLOygWa6-u2WERqS2Rx4vtQPPsA7mKbg,495 +openai/types/live/client_config.py,sha256=AgCgJciI2NofBK8H3TQKRAzNu5n53Xdb3JgTMG_f-lo,507 +openai/types/live/client_config_param.py,sha256=URwiuoHRGDoEtrR0Kem4eJVhwQ-kp5D8x3jmNbEZm_M,610 +openai/types/live/client_delegation.py,sha256=5u_CmtT07Ldlp0MsbFzjjeQuPJAIZ-AwFIj2e-WNOkA,467 +openai/types/live/client_delegation_param.py,sha256=gPQKGp2xctEEgbw8j-V0abxYlBjYw0oBl6HeMfu3S5Q,523 +openai/types/live/client_event.py,sha256=r54o6WTqSE7RWYWLE9F82fngn_mNcQvHgHLfrO5-Qz0,1277 +openai/types/live/client_event_param.py,sha256=PPm9KvlrI5liILUOtslbMFLMqhdDTCWqfWNWCwylgi8,1347 +openai/types/live/commentary_append_event.py,sha256=_BrkHE_rQCxdMGSL0HhY2GHZn6m7Go9MCxdFw6tcGn4,1161 +openai/types/live/commentary_append_event_param.py,sha256=kxHWiXor-BMoysCz8LNC53xB0S-ihm0WmBARmbFRdv8,1223 +openai/types/live/commentary_appended_event.py,sha256=qjGowIY0vEKh-kkgJ1oU80B1zIkXex-t_p1Tv9QPoPE,1155 +openai/types/live/connect_client_event.py,sha256=sOfxZoLkn8vb_Inr7Y47W-nR2n-yOtM8S-6r_-qiInc,1122 +openai/types/live/connect_client_event_param.py,sha256=z33WM-dGzX3s6bxUfwXTEJay1YzCN7EEGN95HEbAEoU,1168 +openai/types/live/connect_server_event.py,sha256=QtTMnktvoeRf-5vYV32ZfvIZwcjGxHz_ZPv6qYRC0ao,1631 +openai/types/live/custom_voice.py,sha256=Vp-KLkFWcCDK0K7SIXQivAtGq01ZxD8UB8tbWUq_-7U,190 +openai/types/live/custom_voice_param.py,sha256=7mBNVoqf0Hacgx49RFqnFbPsrX2iQR-eTOsxsZCVX_s,276 +openai/types/live/data_channel_config.py,sha256=27ozd1TRqOBE00k4BFpw9c7QUNvcWcVhnJD6_sS-6lU,1163 +openai/types/live/data_channel_config_param.py,sha256=74BPtJ9MokLJJECSJzlY7fXUyaQvg1dVQuR2UqxLl2E,1245 +openai/types/live/delegation_created_event.py,sha256=st3UFfr6kznvDxyI7mvDs1iZZb7PtkLs-mxKUfY1PvY,1892 +openai/types/live/error.py,sha256=fZtP4DlYGwWo_9oENQWaJirT7knMDVWiguUD0NIGQj0,905 +openai/types/live/error_event.py,sha256=CkKFA1w-3szv5wvd89PqYy1lQuKqKypd5FYGBpqoits,845 +openai/types/live/fork_client_event.py,sha256=JJdk91ZOIaqoC2C58_gdxECqHm7LQUg4cmTPWZxEvqY,1298 +openai/types/live/fork_client_event_param.py,sha256=1IWnepZiBJJ_nITCAFY3o9dDVQzQuS-UwhHAqVaMEa8,1368 +openai/types/live/fork_server_event.py,sha256=K23hYFFrnPdQUs1Wg9F3Mw5lRjOvSZniY8MJwsYJ9rE,4312 +openai/types/live/fork_session_config.py,sha256=d-zH3qUEVSOPH_jiHYM4B8xTkWmCgiyzKtD7oOR-3X4,2334 +openai/types/live/fork_session_config_param.py,sha256=Fx3zJOmZw12XewM59lwTOJGsbIYEgl_kAS36MwYWcDM,2335 +openai/types/live/fork_session_start_event.py,sha256=fc33x41PQ-d68qrQq0A_Bjbbp0MpMPQ8NF4ajaC1XyI,1078 +openai/types/live/fork_session_start_event_param.py,sha256=E17Eb3CNafogJM4d6DWOUad99skzDAh_pTdHfMpb47k,1154 +openai/types/live/function_tool.py,sha256=Fm4pH2kl69TpGNSFWagWwtGIj6X3hy9XGbpE8B6xIhE,947 +openai/types/live/function_tool_param.py,sha256=Jjg_Bg9GpbrW643fyl-OrPObefOXOJUcbsMaaerA8Rk,992 +openai/types/live/info_event.py,sha256=Q3uEg7SvPs6qCZgV6EzFSUtjE7dgMzbrOB7Lye2OrBQ,850 +openai/types/live/initial_item.py,sha256=NEKS2qmz3qick-9aQ_OhwivYk8tJRhUPrWrO_J8RsQk,4523 +openai/types/live/initial_item_param.py,sha256=Kt_ubBIhlcKXSCeHwiz525-_pRTwYA997r_gRHP2WvM,4483 +openai/types/live/input_audio_append_event.py,sha256=WmU9Te2b6Zc0wZZ64FXxbCrQVHE8qcd8Modx77WmqvM,1109 +openai/types/live/input_audio_append_event_param.py,sha256=voLjyzjatIvu7-FG7HOJPAyyCqssqtImM5_8FzSPwI4,1168 +openai/types/live/input_audio_mute_event.py,sha256=wBfQ5ViuMnA5ng97L3oykWS5WBqOAv_DEkMKshc3ev0,703 +openai/types/live/input_audio_mute_event_param.py,sha256=2rO-FXME7Ew6wx1ovuICkOKc9HeL9Z-wdNTcIDkMjUc,752 +openai/types/live/input_audio_muted_event.py,sha256=Ex4f7eK9f6188oogZ9As5LYzSEs7i8YuAQ3Wkz342Pg,757 +openai/types/live/input_audio_unmute_event.py,sha256=HogqS0PtdKUZ79Fo1ehFl5nTfJvOa6_bOAOI8z8C3fk,701 +openai/types/live/input_audio_unmute_event_param.py,sha256=nzhH492fVa-lLb7MLJjITqBDDeH0tgr8mGVdGZ20bQ0,750 +openai/types/live/input_audio_unmuted_event.py,sha256=wbYYt31Ghoi2jc9HzoTd2r4msbY65rBw0TYTWlzYP5I,726 +openai/types/live/input_transcript_delta_event.py,sha256=muGSTEDq-v_aUfj6FeJ9i9OvTEMKJxe35MS-K3e67wM,1314 +openai/types/live/instructions_append_event.py,sha256=u7wiV0lSY5i2Gc4y4rw_ay6DrK6UEd3hO5lY5SDyzVE,1134 +openai/types/live/instructions_append_event_param.py,sha256=T4uYDDY-gc-1OYJSCl143xRVz53SykfHgM4AXfRctu0,1196 +openai/types/live/instructions_appended_event.py,sha256=VHHMJE3ISWLPj3TZX4F_K0ENLP9S1CJ72F20Zx7mcAo,1161 +openai/types/live/live_create_params.py,sha256=Jdz8-DzIEf_Jup4xNi9BJWqIHGbqppIyAW3XnlvXvmA,840 +openai/types/live/live_create_response.py,sha256=SAun4fHAEoYPyJMV6DyC2bmju8-TTxIDieKyDDji-1U,1180 +openai/types/live/media_session_config_param.py,sha256=mMuYoE76CesB7H8qzD_mZjDCM6L4X5xnMEDN3DPNgkY,3934 +openai/types/live/media_session_fork_config_param.py,sha256=jk4yuD5oWTLWGFQHcLgQPsID552nQ755mm1g2QzquSo,1642 +openai/types/live/output_audio_delta_event.py,sha256=rSV7EvlvCkyHHxN1I9i0jdilWK4DTZ9I992VFFfPqH4,1211 +openai/types/live/output_transcript_delta_event.py,sha256=8VPwPRf1Uk6OjjDYBqJdjZQyjMKS7G46e-sDPlbPcMQ,1324 +openai/types/live/response_create_event.py,sha256=KjdqEKWqQ3RARcyXtdMJXLcgSkaGIH5Pdb0kqg3_xRQ,717 +openai/types/live/response_create_event_param.py,sha256=gxxluT3VT1F2nYGHJDGKJ-fqx0rZz3VTWKjsqPt6knI,766 +openai/types/live/response_event.py,sha256=bEzP52MZLZn_3FtEKb6JI3fFh2RKzSeNwMisTy5Qyec,1241 +openai/types/live/response_item_create_event.py,sha256=tf9gebd1TpEVnKT4PtDk17UwPXxdMOKWpMY1VTrY8zw,946 +openai/types/live/response_item_create_event_param.py,sha256=zEX8Zw9BBu-JuEoRa-4PgrXdKfssZ5hZ8ZDudyxqnWI,1022 +openai/types/live/responses_delegation_config.py,sha256=enZn2T3FbUeDX5WXgXAV8YvFGa-13wteDShTWkESfkg,3520 +openai/types/live/responses_delegation_config_param.py,sha256=xTG8Gie22sv585FgF4fkzHnTcHu_cdVWPis6zhUdTIc,3536 +openai/types/live/responses_delegation_update_config.py,sha256=OfFqDVW6EeAC86wQxbU99cSK2PLYHIrEgcALdsPaL8I,3625 +openai/types/live/responses_delegation_update_config_param.py,sha256=v15_BegO1G_GWkdXs8aTTNjVe8w_sOgJLNkVXlHAqjc,3614 +openai/types/live/server_event.py,sha256=3z42tqREwJFfj_qm-rv_rX-UAKXF2hLfzWpVy9VowwU,4304 +openai/types/live/server_event_selector.py,sha256=I6mK_1hhH-W_-cN-LeLQSWgbT5I2UAQ56QtJ7lfpIuA,570 +openai/types/live/server_event_selector_param.py,sha256=VuGeprzhl3_0OOI_HTfsaa6pogvgQmfINEt62vJUvUI,610 +openai/types/live/session_accept_params.py,sha256=w4wrWLnAb9f1fpkOBSioLzqqDJZ1FohfVGk_eQaV758,3985 +openai/types/live/session_close_event.py,sha256=CEfMZBhgqb0xin3IurcbfjqX4ouCZDJ_ISozjjHHeVk,671 +openai/types/live/session_close_event_param.py,sha256=7mwahFWj3_Naa1AQTloeuofCWJ5XNxo_E1f5qo8flLE,720 +openai/types/live/session_closed_event.py,sha256=9B7JnDv18PPM_SL_eMc1q85aG1CnZwJqoOfppQRsEnQ,1520 +openai/types/live/session_config.py,sha256=etLTNwqYT58syqL-4FJeLJ0RTQR6hwBG_cW6ljcKDs0,4117 +openai/types/live/session_config_param.py,sha256=MI0zdzM8ccM3v5LnhFJ05v99_NfgEk2VEZ1hNDHQkmE,4099 +openai/types/live/session_fork_params.py,sha256=NWtS6tKPqlH1PcNLCMYzy9TEmIhR57rLKEe8C3B73a8,1045 +openai/types/live/session_fork_response.py,sha256=QHlRbuKxRmLDQiLXOO0mU3uUGG0BEhxSxHnRE_DNXO0,1182 +openai/types/live/session_refer_params.py,sha256=00hk3Y_LiLT7Hv0X5QB_DLcc59QsVobwrVS7zAHzvus,405 +openai/types/live/session_reject_params.py,sha256=2DATaPpfzMyWQ6ln6jxmhdPJOB9iUAS6YFadmr68QzI,366 +openai/types/live/session_resource.py,sha256=QIwOsez0Euw2kBZqF2y4bYjcGKznd6NlTNIMq1CUrkA,4517 +openai/types/live/session_start_event.py,sha256=vbjhUvPsvN-J8LjZfrvzhw1M8vY8LjXuDI3XfLJWhzo,992 +openai/types/live/session_start_event_param.py,sha256=81Ao_H_EiAYD2VwqFeRRHhnJIJhM2zBA29BGK5Jt4tY,1068 +openai/types/live/session_started_event.py,sha256=t6rg11Xa6N_DDHWvN4ayIY7Fm1xqME-1df0CV6163pM,870 +openai/types/live/session_update_config.py,sha256=i1d69n_frVo9f35xkENC57USbsp0x0rObCXLjpTDXrQ,1386 +openai/types/live/session_update_config_param.py,sha256=AqstZEA88oj_n58qLZ4NX_JTtu7YGmk1y6ZmzvGuoFM,1359 +openai/types/live/session_update_event.py,sha256=0tHpWj_i2FW4hZzn5q0ezWqSlWwIYLPrpXpiBZcaD_0,1029 +openai/types/live/session_update_event_param.py,sha256=4Uq1uuLSiwUgi_rKRClCqqW93jQCYarXMnoRYFiDZe0,1105 +openai/types/live/session_updated_event.py,sha256=aRO9uXrvFrbBtCtVNwbipeO_Y9SXc6W4hMmt3IYQaWU,867 +openai/types/live/session_usage.py,sha256=LX2qbHKH_E7W2LRWW7dhYArAQEZe2SE82OygrEy4-B4,454 +openai/types/live/session_usage_updated_event.py,sha256=nLo8rxLzpIamEXNd9QqF0YH8tRAXrsdP4b07hdXOfAw,1424 +openai/types/live/sideband_connect_params.py,sha256=a_Kl1FdKnpCCzk2wRn4rVonHjq2IMS61KZEH0xNYTkc,426 +openai/types/live/thinking_append_event.py,sha256=Nj9pDoi9EivDexVt9GHq1JNmSN7nJgJgkRmmPnP9EbM,1156 +openai/types/live/thinking_append_event_param.py,sha256=sa5-0JM5zQNRXzukpfvvAaWenLAXHx0w-KisHv6eUq4,1218 +openai/types/live/thinking_appended_event.py,sha256=JWccTuxezryu14iQmfGz1su3aXEeXa2rxvHMVlOJVAY,1128 +openai/types/model.py,sha256=Q1_ouye5WzHSYG_vGi3EVcRDSY1PZmjN-FE7AmjVjdw,753 +openai/types/model_deleted.py,sha256=GluQ5wITAV8i7TD3VDdzs-ysXatXF5cGjF7ulBqpJaE,227 +openai/types/moderation.py,sha256=QPWVeOCijNdGiktJ5KD0ZgfbfFK27mJDFYu3ItxZBac,7098 +openai/types/moderation_create_params.py,sha256=M5y8vcN8EW1ZNCyAmZAfqA7qPQn_oQUw0s7TskFV8w8,1043 +openai/types/moderation_create_response.py,sha256=Qxa8YqEnGbBaKorqloN2BvTZVLFBnWo26lAMbKYBe1w,551 +openai/types/moderation_image_url_input_param.py,sha256=vj4HY-y4vof1eRyOUsjOUpmTVrCYCNBGJjfEkVVO7tQ,757 +openai/types/moderation_model.py,sha256=3a3l2n2CKZfkon3jpvSkzCKbkNxuiKLOnuzinUsJRmE,318 +openai/types/moderation_multi_modal_input_param.py,sha256=lu_DdermX9nqKGuoJZgwqjBc96FwPsIgZVTWQi6BEnI,482 +openai/types/moderation_text_input_param.py,sha256=k484-M0edRERi0kHbLV9iDcfrOcu6e24ET1aGeSpBpQ,455 +openai/types/other_file_chunking_strategy_object.py,sha256=trCUsY20-5puyTPnB9mLVoXXCvBRbeF8Bnu4nXqHcVQ,500 +openai/types/realtime/__init__.py,sha256=qPMtQ7QXWCkW-qf0wdgR9FM3wgTXa7WQHJP4uSfZEgs,17229 +openai/types/realtime/__pycache__/__init__.cpython-312.pyc,, +openai/types/realtime/__pycache__/audio_transcription.cpython-312.pyc,, +openai/types/realtime/__pycache__/audio_transcription_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/call_accept_params.cpython-312.pyc,, +openai/types/realtime/__pycache__/call_create_params.cpython-312.pyc,, +openai/types/realtime/__pycache__/call_refer_params.cpython-312.pyc,, +openai/types/realtime/__pycache__/call_reject_params.cpython-312.pyc,, +openai/types/realtime/__pycache__/client_secret_create_params.cpython-312.pyc,, +openai/types/realtime/__pycache__/client_secret_create_response.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_created_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_added.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_create_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_create_event_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_created_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_delete_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_delete_event_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_deleted_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_done.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_completed_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_delta_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_failed_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_segment.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_retrieve_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_retrieve_event_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_truncate_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_truncate_event_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/conversation_item_truncated_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/input_audio_buffer_append_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/input_audio_buffer_append_event_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/input_audio_buffer_clear_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/input_audio_buffer_clear_event_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/input_audio_buffer_cleared_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/input_audio_buffer_commit_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/input_audio_buffer_commit_event_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/input_audio_buffer_committed_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/input_audio_buffer_dtmf_event_received_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/input_audio_buffer_speech_started_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/input_audio_buffer_speech_stopped_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/input_audio_buffer_timeout_triggered.cpython-312.pyc,, +openai/types/realtime/__pycache__/log_prob_properties.cpython-312.pyc,, +openai/types/realtime/__pycache__/mcp_list_tools_completed.cpython-312.pyc,, +openai/types/realtime/__pycache__/mcp_list_tools_failed.cpython-312.pyc,, +openai/types/realtime/__pycache__/mcp_list_tools_in_progress.cpython-312.pyc,, +openai/types/realtime/__pycache__/noise_reduction_type.cpython-312.pyc,, +openai/types/realtime/__pycache__/output_audio_buffer_clear_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/output_audio_buffer_clear_event_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/rate_limits_updated_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_audio_config.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_audio_config_input.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_audio_config_input_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_audio_config_output.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_audio_config_output_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_audio_config_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_audio_formats.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_audio_formats_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_audio_input_turn_detection.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_audio_input_turn_detection_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_client_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_client_event_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_connect_params.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_conversation_item_assistant_message.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_conversation_item_assistant_message_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_conversation_item_function_call.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_conversation_item_function_call_output.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_conversation_item_function_call_output_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_conversation_item_function_call_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_conversation_item_system_message.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_conversation_item_system_message_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_conversation_item_user_message.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_conversation_item_user_message_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_error.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_error_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_function_tool.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_function_tool_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcp_approval_request.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcp_approval_request_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcp_approval_response.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcp_approval_response_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcp_list_tools.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcp_list_tools_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcp_protocol_error.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcp_protocol_error_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcp_tool_call.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcp_tool_call_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcp_tool_execution_error.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcp_tool_execution_error_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcphttp_error.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_mcphttp_error_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_reasoning.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_reasoning_effort.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_reasoning_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_response.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_response_create_audio_output.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_response_create_audio_output_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_response_create_mcp_tool.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_response_create_mcp_tool_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_response_create_params.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_response_create_params_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_response_status.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_response_usage.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_response_usage_input_token_details.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_response_usage_output_token_details.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_server_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_session_create_request.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_session_create_request_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_session_create_response.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_tool_choice_config.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_tool_choice_config_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_tools_config.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_tools_config_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_tools_config_union.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_tools_config_union_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_tracing_config.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_tracing_config_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_transcription_session_audio.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_transcription_session_audio_input.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_transcription_session_audio_input_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_transcription_session_audio_input_turn_detection.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_transcription_session_audio_input_turn_detection_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_transcription_session_audio_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_transcription_session_create_request.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_transcription_session_create_request_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_transcription_session_create_response.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_transcription_session_turn_detection.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_truncation.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_truncation_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_truncation_retention_ratio.cpython-312.pyc,, +openai/types/realtime/__pycache__/realtime_truncation_retention_ratio_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_audio_delta_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_audio_done_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_audio_transcript_delta_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_audio_transcript_done_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_cancel_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_cancel_event_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_content_part_added_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_content_part_done_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_create_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_create_event_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_created_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_done_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_function_call_arguments_delta_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_function_call_arguments_done_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_mcp_call_arguments_delta.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_mcp_call_arguments_done.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_mcp_call_completed.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_mcp_call_failed.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_mcp_call_in_progress.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_output_item_added_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_output_item_done_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_text_delta_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/response_text_done_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/session_created_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/session_update_event.cpython-312.pyc,, +openai/types/realtime/__pycache__/session_update_event_param.cpython-312.pyc,, +openai/types/realtime/__pycache__/session_updated_event.cpython-312.pyc,, +openai/types/realtime/audio_transcription.py,sha256=6LZUtySNQFc7_hykEzkCXaVs-34jQCxsWfosnhAqrp0,2516 +openai/types/realtime/audio_transcription_param.py,sha256=qEnXKmMH7ng6twf0w_d6mX8ygEhIZxPObGUc83LO3Nc,2488 +openai/types/realtime/call_accept_params.py,sha256=qUZF-vy5QiHxXcTZ6ZoWp6MKzt-BjbCyczzRR8TPR7s,5834 +openai/types/realtime/call_create_params.py,sha256=dKvJhOD7jFt48_DV4-zDWPvLbUtPyCvMGkrp4C08dd4,543 +openai/types/realtime/call_refer_params.py,sha256=SZWid1asE3lihhdloFa2_THS8vX24AWZ7RQ93HBTpcU,421 +openai/types/realtime/call_reject_params.py,sha256=qt2I-O91zklZIHUMb4eT8Qv6iJT1pOLiUvfjXlgopOY,373 +openai/types/realtime/client_secret_create_params.py,sha256=RbIPDDFFFtflLmbjLSBzFv6LiCtl1y1OIBi_bA2AO_A,1988 +openai/types/realtime/client_secret_create_response.py,sha256=AF7zlR4DuTest7-uWvA3ZaVDBV5Fez7r621LMnzQQCA,1015 +openai/types/realtime/conversation_created_event.py,sha256=EEnwSigxxNPD7B06gy5xlsORXTx6px5HyHc41acd2Jc,878 +openai/types/realtime/conversation_item.py,sha256=iQD0ZjR0eROVK3RN3oIj1woM5nwudaOgzKKXbbYyn7U,1436 +openai/types/realtime/conversation_item_added.py,sha256=jEa913-tG-8JhMc0bEM9ejHRHd_Px0j3ps4Ig9Pt9oY,1515 +openai/types/realtime/conversation_item_create_event.py,sha256=MMaWhq9Eogeg_nxLCVdUa8CFFXxTl6uHhtQ-SFGJUwc,1620 +openai/types/realtime/conversation_item_create_event_param.py,sha256=TIvdlWWzdIUuuyjE9WtfbShsZ0dIvurMZkuFSHfCHcw,1641 +openai/types/realtime/conversation_item_created_event.py,sha256=68bFKTCYWfWOKKh1K4sSeSn1j0shydBs4I5GowNZHYU,1473 +openai/types/realtime/conversation_item_delete_event.py,sha256=eWQsNgwytthsNk_wm-I_U_HBVruHhMUg0MTQ5Ni7m5s,839 +openai/types/realtime/conversation_item_delete_event_param.py,sha256=TPq4sef817OI_uJVR85tW0q9qJb7gWLpLHMTs3oqDLE,860 +openai/types/realtime/conversation_item_deleted_event.py,sha256=mfXbcVeKGea-UjnCk-lwqIvPz11cKAATZVMJTfwLeT4,740 +openai/types/realtime/conversation_item_done.py,sha256=PKnj8qlLH6E3aIeTGyXhL-cwtppK8606aInxN78fBGg,968 +openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py,sha256=mYY_u7AikT1_bGc9g6_3jdQf9TKtJV9eGycEwb-An64,3567 +openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py,sha256=4OlVuy0I0l-SF6Kz9IsqwKIDKK8qwXZb-5T2Ft6cHn8,1492 +openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py,sha256=mKeraBN6N7LgHhbaCE3rPpTez5z8HnLiviMwkXnje1s,1363 +openai/types/realtime/conversation_item_input_audio_transcription_segment.py,sha256=McFNA2L-KzpQYtUF_InK_1ZHS6LfxNiAk079wHe982s,1078 +openai/types/realtime/conversation_item_param.py,sha256=CT3-rpPu0ISR2HbaeWinrqRqYXAPw1a-ZrcNSC8GaZE,1482 +openai/types/realtime/conversation_item_retrieve_event.py,sha256=3fiWakmXwfUT4nVl3V27P1MBz9bQKeHSQaZ8Nx4eN6o,974 +openai/types/realtime/conversation_item_retrieve_event_param.py,sha256=j4yjNbGmqeaqUM7r4ndfK5RCSVnK_xe0brabmAV0PgA,995 +openai/types/realtime/conversation_item_truncate_event.py,sha256=Fg2Kbd6HNbo4ezlsoVCl2ghn3ws4uGE455C5wYwuKYg,1559 +openai/types/realtime/conversation_item_truncate_event_param.py,sha256=geCH-qQC7BaUcnXu42R1xVQCjVV4pHrJXddrz2QTbjU,1600 +openai/types/realtime/conversation_item_truncated_event.py,sha256=CJBecppcOdTR-CHf8M7-TsyKGSyoq4w_WhenWH_Dm38,1123 +openai/types/realtime/input_audio_buffer_append_event.py,sha256=0bMH6IZi1QrFq3pkmF4EcZr1fLjgQiaCNgKM1Kj5ukQ,1590 +openai/types/realtime/input_audio_buffer_append_event_param.py,sha256=vdt2f82X7Vna5EEQJuQpzOmBT41uSIlmFwGeVKrZ9LI,1611 +openai/types/realtime/input_audio_buffer_clear_event.py,sha256=hSOzAtFWqrq8wv-Y1APh7pEf0kKXyDyuujOQlSRpRa0,636 +openai/types/realtime/input_audio_buffer_clear_event_param.py,sha256=yNW6v8EW8QBIa9KFZBMtJhec3htvdXrDEUhxniiHefA,647 +openai/types/realtime/input_audio_buffer_cleared_event.py,sha256=irQkzgS_uc3QYgfS3x6FTwqTFSDxiBcuzVxbtjGVnsI,555 +openai/types/realtime/input_audio_buffer_commit_event.py,sha256=fNeZ77uNkx5JvJs8AZ4tpPqDjQ2bnNMSUkHJIITANYY,1067 +openai/types/realtime/input_audio_buffer_commit_event_param.py,sha256=4kFtQSFdNIYcKPVwOJqY3DaJZosE4Ihx2YfeTRV1ep0,1078 +openai/types/realtime/input_audio_buffer_committed_event.py,sha256=CvrR-uwO6zonahksT2NHD4y6Kme6qjQGBa5sLQL2gpw,1026 +openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py,sha256=uGWeGvP8WugJEWNCXnv9VxablQSuFRqsu7bFJMYe4os,888 +openai/types/realtime/input_audio_buffer_speech_started_event.py,sha256=7xIOXtMnE1b_WVhvlASI56b1eyPqLPc7_mRBSxFxrwM,1566 +openai/types/realtime/input_audio_buffer_speech_stopped_event.py,sha256=SIcsNfM-IrXlEDBm0xasw1YjkN4OhimZdDFUsfFr9O4,1028 +openai/types/realtime/input_audio_buffer_timeout_triggered.py,sha256=nqAgHvgXtkwXiV3wYUbk_X97_kh_jmRhv2JXSO9VpoM,1897 +openai/types/realtime/log_prob_properties.py,sha256=DlJaKot85ODLFVXQyCJGaJbm2q31ePlj2AQjR3za77k,488 +openai/types/realtime/mcp_list_tools_completed.py,sha256=63xTyXNNFzZH9CRlOfp0N9VU3fmivgw2vUHbMkvf5uI,542 +openai/types/realtime/mcp_list_tools_failed.py,sha256=ezC8X6ZHUEgTHJxJQS7etG9RvfpFZrVko-BKzWZDf-g,527 +openai/types/realtime/mcp_list_tools_in_progress.py,sha256=FjK1Bo8-EvlZi1sQMqL6ERS-Eq09GWugMuwTaBLLYgE,549 +openai/types/realtime/noise_reduction_type.py,sha256=ECHPMsD91AbJXlis4HVYmXNvnEJv9WbhA_f3x_ZIbek,237 +openai/types/realtime/output_audio_buffer_clear_event.py,sha256=h7mAK9Sq7_YyJlxGTTAj5BZOYonx5sS__sSVH3B8kik,939 +openai/types/realtime/output_audio_buffer_clear_event_param.py,sha256=aT0Iibge5nO7jslgrBhJAws3LIS8C-DpS46iVxjRyfY,950 +openai/types/realtime/rate_limits_updated_event.py,sha256=xvY-bIuhV-3ErImUfqde9Odi0pCF-DshPb6whHLvtoE,1254 +openai/types/realtime/realtime_audio_config.py,sha256=SbasFanHXd21_yTXubvesiZkHJUHWL5jfF0GiSUWZfk,519 +openai/types/realtime/realtime_audio_config_input.py,sha256=__x6NArYOobrVpiikG5z0sy1i4pRLsqp6InQdFgBIeg,3397 +openai/types/realtime/realtime_audio_config_input_param.py,sha256=08lCtK4Ou_dasxAr8JNs2fX0AAhYkGJIqOZh9Ic7Jos,3449 +openai/types/realtime/realtime_audio_config_output.py,sha256=Ee40KbAx1UZLq2M1m6FKSnZ8gyh_SVJYDc4pJ7UJ6lE,1679 +openai/types/realtime/realtime_audio_config_output_param.py,sha256=LZqHwRKQr3XBco0230AkuugtPvF6cxsNq5HJxYWp68M,1704 +openai/types/realtime/realtime_audio_config_param.py,sha256=i78oQoco6MGtSavnu4IgLGNQ7gAl9Xoq0LUy4Gt1fFo,555 +openai/types/realtime/realtime_audio_formats.py,sha256=Ih9FzeBB6XNqufCa0H5fgYPFPcldvzBFVErI-pw-tvs,1068 +openai/types/realtime/realtime_audio_formats_param.py,sha256=fZJc6ObYZnTq2flzD8UmrMuk2YKiXT2gMxdZQDGVJv4,959 +openai/types/realtime/realtime_audio_input_turn_detection.py,sha256=0saA3oNEBrK1BSlBAO92Xvp289D6APMYKLsVSvMy8dU,4259 +openai/types/realtime/realtime_audio_input_turn_detection_param.py,sha256=-EWV0jdtO8iMX2HiNf2GWHLGhdsddHP36rrNBEEOKjg,4090 +openai/types/realtime/realtime_client_event.py,sha256=PvHLkQnMOVXauZ9H2a1aHtnzvNPRBzj6xjen3KXHYZk,1472 +openai/types/realtime/realtime_client_event_param.py,sha256=KRAT1gMDbsHxN2m2rn2guyHNmGpICMpqZNGqPDDKZfM,1542 +openai/types/realtime/realtime_connect_params.py,sha256=vf2OqbwpdWxkFKAfnTwR3aWwZtxzzTUv7kkb6tyFUz0,287 +openai/types/realtime/realtime_conversation_item_assistant_message.py,sha256=xxxuuZrZqw6MPT7m_ssSDIc1V-iO2Bcc7oOZBTQG-nc,1779 +openai/types/realtime/realtime_conversation_item_assistant_message_param.py,sha256=J-mK-EZxFA-LvkV1ovyM7Lm1IBqpvQRNmXyuYI7yHZQ,1747 +openai/types/realtime/realtime_conversation_item_function_call.py,sha256=r-nbm2tygdrLQ5GE0nSL5gJYbAxWSq0tEo8fDF-8LDo,1261 +openai/types/realtime/realtime_conversation_item_function_call_output.py,sha256=OJDCtPeGgC5rfTP56i_WV_ikDFv1CbHu3Z6kULDw6MY,1169 +openai/types/realtime/realtime_conversation_item_function_call_output_param.py,sha256=ZICz3knYnBuzMe2q3kNaEgjHPuZVPXcGIlRVkhLKB04,1166 +openai/types/realtime/realtime_conversation_item_function_call_param.py,sha256=wX8lF7H8nvbUu9tiQOW2u4aL7BVBRJUosM9OxVsd_3w,1241 +openai/types/realtime/realtime_conversation_item_system_message.py,sha256=gJFs-Ist7iV_WsEhF93R1fOxEUx54_FIw3JM_MMRoU0,1694 +openai/types/realtime/realtime_conversation_item_system_message_param.py,sha256=zFFQ9HTycS7E61JmJ-ZGUymQ70Ju_b5fnAbH7sbYSJw,1696 +openai/types/realtime/realtime_conversation_item_user_message.py,sha256=e9t44Jt09IFOBaMxSwnQl_v4ufWFRDWyDCpb-44U-ug,2169 +openai/types/realtime/realtime_conversation_item_user_message_param.py,sha256=qkXrd80mwb0nKcDdh6_jvbRec63RpEh-iBEQd3ZvqK0,2103 +openai/types/realtime/realtime_error.py,sha256=S_5NE2jNiZWvq8tlRt5QLeCsnomK9I55Xck1AtubHj4,657 +openai/types/realtime/realtime_error_event.py,sha256=PK7ybnjMqOB2heXEC48S98w0FvRzQH6yVLbHo4trUv4,713 +openai/types/realtime/realtime_function_tool.py,sha256=FC1Pa-zfx6HakNIAzkXlGlfZHjwbRzYPdS-rmlbi4ss,733 +openai/types/realtime/realtime_function_tool_param.py,sha256=V0QpbBPM7RDdiNut1TJhHhvSP53dH8pI7zhF2m0yEV8,673 +openai/types/realtime/realtime_mcp_approval_request.py,sha256=5whbzI1p6uXNSQQeBONApFEUdGY4UJaq4DhcVOOYeJo,695 +openai/types/realtime/realtime_mcp_approval_request_param.py,sha256=EGEr5wdDqSNi4DMynMwHkLmj-GdsCWNDCTrtH1mpVso,791 +openai/types/realtime/realtime_mcp_approval_response.py,sha256=HjejTniQahGvUIdfGyxEKxG2DoJwDckOBRDng2uTw7M,741 +openai/types/realtime/realtime_mcp_approval_response_param.py,sha256=-EQpeHNP5jeinY5dlUSGVACk4FCSdVMkB21KAFDhuQE,820 +openai/types/realtime/realtime_mcp_list_tools.py,sha256=jyXQAHmkQ0dgKjUKMVfWRcb_gmyj08r8MUmzfKUOtQg,1003 +openai/types/realtime/realtime_mcp_list_tools_param.py,sha256=_zbG3hEPCWEij0d7SNA_JFEyuD1Q0dVAwnqBNR4qc6o,1089 +openai/types/realtime/realtime_mcp_protocol_error.py,sha256=t39OGMe1QOLs1RqPF3dzH_B0lOvFXhHkR_ctW9p1WW0,312 +openai/types/realtime/realtime_mcp_protocol_error_param.py,sha256=pY8_LEYG8_BkltBdMQMyHdb4dlzrf8ArvvUlHbSi_6w,388 +openai/types/realtime/realtime_mcp_tool_call.py,sha256=pPdF8ePv5b6vycLaNSPjZTRWAUwXxPJSWJeljt6-Swc,1406 +openai/types/realtime/realtime_mcp_tool_call_param.py,sha256=frNEfDgrMOUXxKnhfgTEBO-mCEWXJww6825UvwII954,1420 +openai/types/realtime/realtime_mcp_tool_execution_error.py,sha256=xdVeWUTMAC0Q0PO7UcBR-L7BBUlMRaszO53OCIvwtDM,313 +openai/types/realtime/realtime_mcp_tool_execution_error_param.py,sha256=EPV0ZGim0Fqo1lep2YW3DxERDP0VPLHm2xWEjrFM3A0,379 +openai/types/realtime/realtime_mcphttp_error.py,sha256=2Tp4IMx0F8ns3YL6EHgQHlpKhIgFOOx51LWzIaz81ZY,300 +openai/types/realtime/realtime_mcphttp_error_param.py,sha256=0TfyvgS05rOuFfkTN7Rz4ZoANXhTT9Lm6n39VgJApQw,376 +openai/types/realtime/realtime_reasoning.py,sha256=R1K4f7uyRk1SiiNRUMAWyMou9rO_j1k0w1w2ZpF4AaY,543 +openai/types/realtime/realtime_reasoning_effort.py,sha256=3W_iHc4KUmn-xxxrjeKx6re-PDzt16ZIeCwEbkTY55Y,265 +openai/types/realtime/realtime_reasoning_param.py,sha256=Fp4m_20WTuanw_ME3vBd0ROVBG8jmDuI7Tc9AEjoETg,564 +openai/types/realtime/realtime_response.py,sha256=fckaxoK8KNy6IipvaOM5xY76jvVYpJm9SvnOi1g8gI8,3902 +openai/types/realtime/realtime_response_create_audio_output.py,sha256=DhfJBM_6YIRa1SGOu-FPlGm3bdsUtT5ESQUQPIYVUcQ,1383 +openai/types/realtime/realtime_response_create_audio_output_param.py,sha256=BhWpQF8W1nkWmG5Bkh7I_eLR22L1s3dpuGTIgAoEQMI,1421 +openai/types/realtime/realtime_response_create_mcp_tool.py,sha256=Bb6kCcdt67xT1LfipyIXJAwxGGiuu2hWw5q9FQIVIck,5743 +openai/types/realtime/realtime_response_create_mcp_tool_param.py,sha256=SsVnYk61uIrdmwqKpJFRBwtVaodGVNVfM19BgTSYpj0,5611 +openai/types/realtime/realtime_response_create_params.py,sha256=A_PRvzL5cTBeVFeWsZE6U4ieU_0qKMNoC6AuEWC0i6E,4742 +openai/types/realtime/realtime_response_create_params_param.py,sha256=2LMPXPKzRjd5DKBVz5XlA5E0bc9y5f8An2tEr8S8tUc,4756 +openai/types/realtime/realtime_response_status.py,sha256=ZB0VTQBKm0JtRRN_hK2DjJbpOMoiEQCaECAhEfKhy68,1499 +openai/types/realtime/realtime_response_usage.py,sha256=S6NYsg86VvEZJBXRzFnLRyO3ywp8Pp0e_RDFp0FdWEo,1726 +openai/types/realtime/realtime_response_usage_input_token_details.py,sha256=XDTdjDifVpgrYLtXeWlIAaEuVUW0Twj-t7mo-3M17B8,1634 +openai/types/realtime/realtime_response_usage_output_token_details.py,sha256=PRScFmxhoDK00jY6omsARZt5YwPm-3o7Vd0Bxr5Rqc4,523 +openai/types/realtime/realtime_server_event.py,sha256=m2uDnETVF34XoqCimheFFuC0knoOaIjOx2mlatZEh4Q,8265 +openai/types/realtime/realtime_session_create_request.py,sha256=ki45BZv6FP7C0t5QbTvJPAE-WN-k4nRMhzpnjiQH_vY,5950 +openai/types/realtime/realtime_session_create_request_param.py,sha256=c7YrUPQRQ6iTKfte_3tAVm4VOHe7bebPgPxvS2ito_U,5918 +openai/types/realtime/realtime_session_create_response.py,sha256=t6s3wfavMZOyHABcxoih0voNKLPRGO2OyjteVwsHvBw,20544 +openai/types/realtime/realtime_tool_choice_config.py,sha256=nPh8Lg-ON-XblC5x992z_ePQyIUslNszpqzrXWGIHPI,471 +openai/types/realtime/realtime_tool_choice_config_param.py,sha256=WabV67RoJG9mAh7u_odhfNFBR8idt_N1hBPMbULUqSo,549 +openai/types/realtime/realtime_tools_config.py,sha256=VWI4ovIrImDzRzYYO2fQwGUOfcy0W_L-PaPwo1MwB04,317 +openai/types/realtime/realtime_tools_config_param.py,sha256=F2VpMQ0OUWRB67o0gWTtpZ5qoBaSMzts3uUZkQXDmIM,5898 +openai/types/realtime/realtime_tools_config_union.py,sha256=cUAyzIEFLhIu6hMaz4Lq-JcM-1s2IBL0qtEKu8rFnyE,6000 +openai/types/realtime/realtime_tools_config_union_param.py,sha256=PyQXthGwerM4K079cRqiiA2hfArvhUu5nfzUC543lMc,5791 +openai/types/realtime/realtime_tracing_config.py,sha256=zmII6g0E9yjHkgdgSTNQACrG-fwvqf0npflq-6u2t9c,917 +openai/types/realtime/realtime_tracing_config_param.py,sha256=QiJl8sQ0KrNd9WDehDa49UMQZ3-hb2zGuVbKf_bvYv4,886 +openai/types/realtime/realtime_transcription_session_audio.py,sha256=ClNeOKFjDD2crr9TGpcJf2HlT_qqcKhakmnITYq79-g,466 +openai/types/realtime/realtime_transcription_session_audio_input.py,sha256=LRZ2bnbZYkH9Ry1saLqAwujVO71leBtFDhE8Oh3myTo,3526 +openai/types/realtime/realtime_transcription_session_audio_input_param.py,sha256=UCH3xuRUqspGOP0hypEd9gerIFLIzbZW_ZTWCBIPRKA,3578 +openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py,sha256=QOnyJwrn2Kn5tWc7bhfba-cG3GA3lRjePbbMj4hjRMs,4299 +openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py,sha256=QOXOcbQAGpuQNowuUG0qpHAEm4HDaalpxA1I17OFmDE,4130 +openai/types/realtime/realtime_transcription_session_audio_param.py,sha256=5Vm5_FUsI4NtFhEU6tnYhDr1-8ni6YWtAv-Mx8P06-c,503 +openai/types/realtime/realtime_transcription_session_create_request.py,sha256=EfgCPT5ux8vG7k0w5dQE7w6aoyl3PdRgQN71Lgl-N0s,962 +openai/types/realtime/realtime_transcription_session_create_request_param.py,sha256=Ar7mdM1rFsqT3LjH3ee6musvkncJunLIEc70rBW9hxk,991 +openai/types/realtime/realtime_transcription_session_create_response.py,sha256=dGreTkX5LeFn0vH5co3XB4q0avUyZpF4gHK3dKU3n8Y,2634 +openai/types/realtime/realtime_transcription_session_turn_detection.py,sha256=rxgsqE6V-EUgfOT_Crc17beYMN3Eqv_ZAaImgtX6qE4,1340 +openai/types/realtime/realtime_truncation.py,sha256=pRX_6wPd3FUEKLhDulakTVfDOnWjIEDc3RkWKRoLvJI,379 +openai/types/realtime/realtime_truncation_param.py,sha256=LG-ZNc2D4Vy8WhiQFbf3jQ8IW5TD5ghQLbrSrW860n4,441 +openai/types/realtime/realtime_truncation_retention_ratio.py,sha256=hsaNaZcT4GPQlVD87_yfyvYbD_CSN35jZPE25q2-c4I,1748 +openai/types/realtime/realtime_truncation_retention_ratio_param.py,sha256=xZaoKXz7wWWrvJSnwKp9WA4Iwr9C_cMsJg3AksjOCR8,1765 +openai/types/realtime/response_audio_delta_event.py,sha256=dLALR6nbPB-Tx0hHt2JDFYjqcrcFvZmj2jBiMsqecNs,817 +openai/types/realtime/response_audio_done_event.py,sha256=P-ekh9thK2PFBRCfUzFQnB3Bl-IckOxJ-o_AG7dlRGQ,836 +openai/types/realtime/response_audio_transcript_delta_event.py,sha256=3QMKBZm1T9o6gQNeVgH7WdhraG-HcL27kv9DGqCID3E,872 +openai/types/realtime/response_audio_transcript_done_event.py,sha256=adYA0-UJNob9xu5lH_W1Jk98uSf_APfiUyt75btsci0,982 +openai/types/realtime/response_cancel_event.py,sha256=oVOhiiWYvn_wJ8MixFeteeXv3NJEgdv0jGTEdVjspK4,1030 +openai/types/realtime/response_cancel_event_param.py,sha256=M_17UtbnuZ0x60SRkR1zeuJYzHvBtWcv_L7sc1TfuqA,1024 +openai/types/realtime/response_content_part_added_event.py,sha256=oaZCqtVnVMTv6LeKt1eNbBHEMsdr95bDnXrYxUtAdeU,1398 +openai/types/realtime/response_content_part_done_event.py,sha256=PUrkXo1y3V5m-DqWHsaQ1ZRxfd6KIDdxQGm1to7dr-c,1403 +openai/types/realtime/response_create_event.py,sha256=rODXe9MBkrz9izay4hUZ3HwYd-lTWWkUNiu2S27rns4,2084 +openai/types/realtime/response_create_event_param.py,sha256=XKYkUBea0UFdnEBX2KqIUEY6NG11QcH1z0zRzENzqwM,2095 +openai/types/realtime/response_created_event.py,sha256=Nr82DLXWWmAOBYCmz2iHoimgNgfZYhj0AMSQO0nqVyg,668 +openai/types/realtime/response_done_event.py,sha256=p_FcS6uHvvYAQ0psg1nfKTeP371fc3bwG7bC4twfWqM,1052 +openai/types/realtime/response_function_call_arguments_delta_event.py,sha256=bucCSZgjT8u91R8SFm3wNK2jvZ2mbgT0Xc3bre0xnu4,873 +openai/types/realtime/response_function_call_arguments_done_event.py,sha256=dVhvuIFpSKceABx4wKjzrCq4Iah48vm1ik8TeGfsBcs,1032 +openai/types/realtime/response_mcp_call_arguments_delta.py,sha256=Wa9rEnaF6kR8lO4ld6q4UJrfD6b3TvpFcGqrvrwLQdQ,919 +openai/types/realtime/response_mcp_call_arguments_done.py,sha256=gjtGLlPkEDhV8VczzNSZywtdW8NRFNEBu8z1VdX3Z1M,798 +openai/types/realtime/response_mcp_call_completed.py,sha256=Yl7_6q18O885vKxU01vxU5ei4gHAbPkJGNIfBKgNIgc,632 +openai/types/realtime/response_mcp_call_failed.py,sha256=t3hMhgTIoYQ93u7Mh4rKnTIRyKJnbecwckae9U0oKf8,604 +openai/types/realtime/response_mcp_call_in_progress.py,sha256=pFj0UuLEWg3NaBodIHv4FichHTjbduuSogA0Rxfup0g,642 +openai/types/realtime/response_output_item_added_event.py,sha256=fn20qNfJJHxJSEnZ9xXVe8NYjRuHSJcvGEJFyGjEQp0,795 +openai/types/realtime/response_output_item_done_event.py,sha256=jfv0TF1h4AO4JfOYuvqFKBYU4LQ4CdUMTFzh2y3PNVo,853 +openai/types/realtime/response_text_delta_event.py,sha256=Y-3cCAGs5fTuP3s78gHEdZTTRp5Q97X4vZHoEKPqa7k,818 +openai/types/realtime/response_text_done_event.py,sha256=qnH_0XNBD_nuEcjWgZ79NToaSgxpzykbWATEEYOQPO0,913 +openai/types/realtime/session_created_event.py,sha256=R2FgNDwOD_kpYlCArzvBuFmXf_a48vPZ2_mi2H5_rMk,979 +openai/types/realtime/session_update_event.py,sha256=vxllfk5tVfT3aQMR3h08aLVR-dUgivYq3Goxz_1KoFE,1734 +openai/types/realtime/session_update_event_param.py,sha256=ryDP0jf1R8ogZvKowMBC9r6McoZ6w5wZfEt-Bmb-a4U,1806 +openai/types/realtime/session_updated_event.py,sha256=p7JmK2Jpm2ViIG1Hh35bhRa4bikynejQrs3mf8iIJlg,886 +openai/types/responses/__init__.py,sha256=KIHTqAearl61MTiAMHA6Vm5jh-xpK7_PkTIzssGZzJw,24662 +openai/types/responses/__pycache__/__init__.cpython-312.pyc,, +openai/types/responses/__pycache__/apply_patch_tool.cpython-312.pyc,, +openai/types/responses/__pycache__/apply_patch_tool_param.cpython-312.pyc,, +openai/types/responses/__pycache__/compacted_response.cpython-312.pyc,, +openai/types/responses/__pycache__/computer_action.cpython-312.pyc,, +openai/types/responses/__pycache__/computer_action_list.cpython-312.pyc,, +openai/types/responses/__pycache__/computer_action_list_param.cpython-312.pyc,, +openai/types/responses/__pycache__/computer_action_param.cpython-312.pyc,, +openai/types/responses/__pycache__/computer_tool.cpython-312.pyc,, +openai/types/responses/__pycache__/computer_tool_param.cpython-312.pyc,, +openai/types/responses/__pycache__/computer_use_preview_tool.cpython-312.pyc,, +openai/types/responses/__pycache__/computer_use_preview_tool_param.cpython-312.pyc,, +openai/types/responses/__pycache__/container_auto.cpython-312.pyc,, +openai/types/responses/__pycache__/container_auto_param.cpython-312.pyc,, +openai/types/responses/__pycache__/container_network_policy_allowlist.cpython-312.pyc,, +openai/types/responses/__pycache__/container_network_policy_allowlist_param.cpython-312.pyc,, +openai/types/responses/__pycache__/container_network_policy_disabled.cpython-312.pyc,, +openai/types/responses/__pycache__/container_network_policy_disabled_param.cpython-312.pyc,, +openai/types/responses/__pycache__/container_network_policy_domain_secret.cpython-312.pyc,, +openai/types/responses/__pycache__/container_network_policy_domain_secret_param.cpython-312.pyc,, +openai/types/responses/__pycache__/container_reference.cpython-312.pyc,, +openai/types/responses/__pycache__/container_reference_param.cpython-312.pyc,, +openai/types/responses/__pycache__/custom_tool.cpython-312.pyc,, +openai/types/responses/__pycache__/custom_tool_param.cpython-312.pyc,, +openai/types/responses/__pycache__/easy_input_message.cpython-312.pyc,, +openai/types/responses/__pycache__/easy_input_message_param.cpython-312.pyc,, +openai/types/responses/__pycache__/file_search_tool.cpython-312.pyc,, +openai/types/responses/__pycache__/file_search_tool_param.cpython-312.pyc,, +openai/types/responses/__pycache__/function_shell_tool.cpython-312.pyc,, +openai/types/responses/__pycache__/function_shell_tool_param.cpython-312.pyc,, +openai/types/responses/__pycache__/function_tool.cpython-312.pyc,, +openai/types/responses/__pycache__/function_tool_param.cpython-312.pyc,, +openai/types/responses/__pycache__/image_detail.cpython-312.pyc,, +openai/types/responses/__pycache__/inline_skill.cpython-312.pyc,, +openai/types/responses/__pycache__/inline_skill_param.cpython-312.pyc,, +openai/types/responses/__pycache__/inline_skill_source.cpython-312.pyc,, +openai/types/responses/__pycache__/inline_skill_source_param.cpython-312.pyc,, +openai/types/responses/__pycache__/input_item_list_params.cpython-312.pyc,, +openai/types/responses/__pycache__/input_token_count_params.cpython-312.pyc,, +openai/types/responses/__pycache__/input_token_count_response.cpython-312.pyc,, +openai/types/responses/__pycache__/local_environment.cpython-312.pyc,, +openai/types/responses/__pycache__/local_environment_param.cpython-312.pyc,, +openai/types/responses/__pycache__/local_skill.cpython-312.pyc,, +openai/types/responses/__pycache__/local_skill_param.cpython-312.pyc,, +openai/types/responses/__pycache__/mcp_tool_call_error.cpython-312.pyc,, +openai/types/responses/__pycache__/mcp_tool_call_error_param.cpython-312.pyc,, +openai/types/responses/__pycache__/namespace_tool.cpython-312.pyc,, +openai/types/responses/__pycache__/namespace_tool_param.cpython-312.pyc,, +openai/types/responses/__pycache__/parsed_response.cpython-312.pyc,, +openai/types/responses/__pycache__/response.cpython-312.pyc,, +openai/types/responses/__pycache__/response_apply_patch_tool_call.cpython-312.pyc,, +openai/types/responses/__pycache__/response_apply_patch_tool_call_output.cpython-312.pyc,, +openai/types/responses/__pycache__/response_audio_delta_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_audio_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_audio_transcript_delta_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_audio_transcript_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_code_interpreter_call_code_delta_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_code_interpreter_call_code_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_code_interpreter_call_completed_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_code_interpreter_call_in_progress_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_code_interpreter_call_interpreting_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_code_interpreter_tool_call.cpython-312.pyc,, +openai/types/responses/__pycache__/response_code_interpreter_tool_call_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_compact_params.cpython-312.pyc,, +openai/types/responses/__pycache__/response_compaction_compacting_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_compaction_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_compaction_item_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_compaction_item_param_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_completed_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_computer_tool_call.cpython-312.pyc,, +openai/types/responses/__pycache__/response_computer_tool_call_output_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_computer_tool_call_output_screenshot.cpython-312.pyc,, +openai/types/responses/__pycache__/response_computer_tool_call_output_screenshot_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_computer_tool_call_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_configuration_update_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_configuration_update_item_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_configuration_update_item_param_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_container_reference.cpython-312.pyc,, +openai/types/responses/__pycache__/response_content_part_added_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_content_part_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_conversation_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_conversation_param_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_create_params.cpython-312.pyc,, +openai/types/responses/__pycache__/response_created_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_custom_tool_call.cpython-312.pyc,, +openai/types/responses/__pycache__/response_custom_tool_call_input_delta_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_custom_tool_call_input_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_custom_tool_call_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_custom_tool_call_output.cpython-312.pyc,, +openai/types/responses/__pycache__/response_custom_tool_call_output_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_custom_tool_call_output_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_custom_tool_call_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_error.cpython-312.pyc,, +openai/types/responses/__pycache__/response_error_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_failed_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_file_search_call_completed_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_file_search_call_in_progress_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_file_search_call_searching_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_file_search_tool_call.cpython-312.pyc,, +openai/types/responses/__pycache__/response_file_search_tool_call_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_format_text_config.cpython-312.pyc,, +openai/types/responses/__pycache__/response_format_text_config_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_format_text_json_schema_config.cpython-312.pyc,, +openai/types/responses/__pycache__/response_format_text_json_schema_config_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_call_arguments_delta_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_call_arguments_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_call_output_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_call_output_item_list.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_call_output_item_list_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_call_output_item_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_shell_call_output_content.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_shell_call_output_content_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_shell_tool_call.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_shell_tool_call_output.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_tool_call.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_tool_call_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_tool_call_output_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_tool_call_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_web_search.cpython-312.pyc,, +openai/types/responses/__pycache__/response_function_web_search_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_image_gen_call_completed_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_image_gen_call_generating_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_image_gen_call_in_progress_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_image_gen_call_partial_image_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_in_progress_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_includable.cpython-312.pyc,, +openai/types/responses/__pycache__/response_incomplete_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_audio.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_audio_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_content.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_content_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_file.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_file_content.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_file_content_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_file_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_image.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_image_content.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_image_content_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_image_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_item_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_message_content_list.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_message_content_list_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_message_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_text.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_text_content.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_text_content_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_input_text_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_item_list.cpython-312.pyc,, +openai/types/responses/__pycache__/response_local_environment.cpython-312.pyc,, +openai/types/responses/__pycache__/response_mcp_call_arguments_delta_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_mcp_call_arguments_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_mcp_call_completed_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_mcp_call_failed_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_mcp_call_in_progress_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_mcp_list_tools_completed_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_mcp_list_tools_failed_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_mcp_list_tools_in_progress_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_output_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_output_item_added_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_output_item_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_output_message.cpython-312.pyc,, +openai/types/responses/__pycache__/response_output_message_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_output_refusal.cpython-312.pyc,, +openai/types/responses/__pycache__/response_output_refusal_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_output_text.cpython-312.pyc,, +openai/types/responses/__pycache__/response_output_text_annotation_added_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_output_text_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_prompt.cpython-312.pyc,, +openai/types/responses/__pycache__/response_prompt_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_queued_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_reasoning_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_reasoning_item_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_reasoning_summary_part_added_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_reasoning_summary_part_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_reasoning_summary_text_delta_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_reasoning_summary_text_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_reasoning_text_delta_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_reasoning_text_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_refusal_delta_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_refusal_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_retrieve_params.cpython-312.pyc,, +openai/types/responses/__pycache__/response_shell_call_command_added_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_shell_call_command_delta_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_shell_call_command_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_shell_call_output_content_delta_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_shell_call_output_content_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_status.cpython-312.pyc,, +openai/types/responses/__pycache__/response_steer_accepted_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_steer_error_code.cpython-312.pyc,, +openai/types/responses/__pycache__/response_steer_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_steer_event_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_steer_failed_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_steer_input.cpython-312.pyc,, +openai/types/responses/__pycache__/response_steer_input_content.cpython-312.pyc,, +openai/types/responses/__pycache__/response_steer_input_content_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_steer_input_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_steer_pending_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_steer_pending_reason.cpython-312.pyc,, +openai/types/responses/__pycache__/response_steer_required_input.cpython-312.pyc,, +openai/types/responses/__pycache__/response_stream_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_text_config.cpython-312.pyc,, +openai/types/responses/__pycache__/response_text_config_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_text_delta_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_text_done_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_tool_search_call.cpython-312.pyc,, +openai/types/responses/__pycache__/response_tool_search_output_item.cpython-312.pyc,, +openai/types/responses/__pycache__/response_tool_search_output_item_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_tool_search_output_item_param_param.cpython-312.pyc,, +openai/types/responses/__pycache__/response_usage.cpython-312.pyc,, +openai/types/responses/__pycache__/response_web_search_call_completed_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_web_search_call_in_progress_event.cpython-312.pyc,, +openai/types/responses/__pycache__/response_web_search_call_searching_event.cpython-312.pyc,, +openai/types/responses/__pycache__/responses_client_event.cpython-312.pyc,, +openai/types/responses/__pycache__/responses_client_event_param.cpython-312.pyc,, +openai/types/responses/__pycache__/responses_server_event.cpython-312.pyc,, +openai/types/responses/__pycache__/service_tier.cpython-312.pyc,, +openai/types/responses/__pycache__/skill_reference.cpython-312.pyc,, +openai/types/responses/__pycache__/skill_reference_param.cpython-312.pyc,, +openai/types/responses/__pycache__/tool.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_allowed.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_allowed_param.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_apply_patch.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_apply_patch_param.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_custom.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_custom_param.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_function.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_function_param.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_mcp.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_mcp_param.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_options.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_shell.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_shell_param.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_types.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_choice_types_param.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_param.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_search_tool.cpython-312.pyc,, +openai/types/responses/__pycache__/tool_search_tool_param.cpython-312.pyc,, +openai/types/responses/__pycache__/web_search_preview_tool.cpython-312.pyc,, +openai/types/responses/__pycache__/web_search_preview_tool_param.cpython-312.pyc,, +openai/types/responses/__pycache__/web_search_tool.cpython-312.pyc,, +openai/types/responses/__pycache__/web_search_tool_param.cpython-312.pyc,, +openai/types/responses/apply_patch_tool.py,sha256=SaaloaCuJRBM9HwMmySf3sE2eTlc9VXNm9_lVLq07Rs,553 +openai/types/responses/apply_patch_tool_param.py,sha256=TtDYr586F6U1ioZ7yVmCroxtTpNc6aEffqfx_h_dR5k,602 +openai/types/responses/compacted_response.py,sha256=ZlHisBsLEhq1Y1zbU-dsPpj5NbXrHU0XaJl3iDVLW74,933 +openai/types/responses/computer_action.py,sha256=U3AxQdoKdSNMVqIVYef6fp9fpVb-ci6eVv2eMl2ka2Q,4263 +openai/types/responses/computer_action_list.py,sha256=kd8zYugzcWswGtOWxRT3-unKfVu2N_pmq6e_YUqvVS8,283 +openai/types/responses/computer_action_list_param.py,sha256=v3PHTwi6eEHtI8uQeUBiAD0Da3YNO7klScMnp_JC6mY,4759 +openai/types/responses/computer_action_param.py,sha256=i5vUm8qENc3fQsPuksJ5pJveApLcYMv2vDDIQUWjMHs,4658 +openai/types/responses/computer_tool.py,sha256=hRV0ad3PXRZkFMBB-uYH9Fl1BKO4DlYEDNt_FO3hk0s,475 +openai/types/responses/computer_tool_param.py,sha256=3wWO_VpBTPGLTTNSGV0RPshbRes9EmP2iXlqCeZwbD8,531 +openai/types/responses/computer_use_preview_tool.py,sha256=zkUsZTcy9YAu3PD4DFEIAh623CqpUmW7dIxF-4mkcrw,792 +openai/types/responses/computer_use_preview_tool_param.py,sha256=44XUnAotG8n2O__t4Dn68LPlxLBxaSPEkvpKouv2WFo,878 +openai/types/responses/container_auto.py,sha256=7UfLbZ_5rOn-Y064WQrZrc3m-V8GP2R9p0q5YdZBwbU,1386 +openai/types/responses/container_auto_param.py,sha256=4E8hCC8EgoIuezwabulcx8VWxrQJCn41-F4K5TMM1D8,1358 +openai/types/responses/container_network_policy_allowlist.py,sha256=q7PuBrsYTLMuD0_CHAmv-UazeKRvIKqbKtXWFQ7HgVA,735 +openai/types/responses/container_network_policy_allowlist_param.py,sha256=RfOeZUDFQlnt-bAZdWwMk00WXK2-4cyAlnQl4TgoRJo,846 +openai/types/responses/container_network_policy_disabled.py,sha256=-GzgoKShLAf6aNaHyq-KTSzy1WdP2orKz7S-uv_82B0,347 +openai/types/responses/container_network_policy_disabled_param.py,sha256=zIrW8QbAJfYc2CfQqx1DOEnReQsT41QbsCJ2yL3UrNs,403 +openai/types/responses/container_network_policy_domain_secret.py,sha256=pj_0FSOlMPjgDtFqL2omWlDl2lMlgou2WyCN3Odg66Q,432 +openai/types/responses/container_network_policy_domain_secret_param.py,sha256=mx12KJxAP2sbkOoW5QTETd4OCfXuBKbCupfmWzTkQhY,538 +openai/types/responses/container_reference.py,sha256=oRL7LPRpdZQJlAYzDsANvgO9JOydKj1srQbIdJYGs-o,415 +openai/types/responses/container_reference_param.py,sha256=37wmH83T-uCYJtwlt50bCRfRLnSA6XConthQbaB7bTw,481 +openai/types/responses/custom_tool.py,sha256=rBDpjH4bhAne2CPdlG4L8zfMG3WaG_pBB9C182sngRQ,1422 +openai/types/responses/custom_tool_param.py,sha256=YnSFvrFTeY0dtWm3bBPQ6fgUwCwVkdmeHr5EXGCPR_0,1357 +openai/types/responses/easy_input_message.py,sha256=aLpknCrdFTsxp3uPsenRfjHSZYTtozhwSDMl1hK6ifA,1574 +openai/types/responses/easy_input_message_param.py,sha256=AMM0dgoeroykC1GYrUJOV8_K45dLhhIs5qWrtxHQBlQ,1633 +openai/types/responses/file_search_tool.py,sha256=0Aq8h1AKK2vcN5PDUrPauCG0BJJWIvfc1TD5nmIP31M,2264 +openai/types/responses/file_search_tool_param.py,sha256=NngmxqDgRPwJQWJgyPTpoCrOoAXQzhTk79AaaQYvJiQ,2334 +openai/types/responses/function_shell_tool.py,sha256=QqutF1X5a38GAbDdOhRrTFi9H1nO2Nk9gcA-W0EtSxk,944 +openai/types/responses/function_shell_tool_param.py,sha256=MIo-H0c-DLJZOozk8KYGuIF0lsTvjs8OxzkDEMDI6Zg,930 +openai/types/responses/function_tool.py,sha256=ZjUju4B0msgho4nQg0-fVGTktUBQdI4t-g2fkaRqem8,1513 +openai/types/responses/function_tool_param.py,sha256=AgNCk0DitgHruNDwYgvetyI-gIolnKQDyRufX9bMK4Y,1608 +openai/types/responses/image_detail.py,sha256=F_yGPtoiySs5PIvEB_9iIV2VPpcSYYpzksbvgDLCZr8,231 +openai/types/responses/inline_skill.py,sha256=jpco5BT5swnjzS5utzD2HgUn9fFirtcDIK6AECkySo4,520 +openai/types/responses/inline_skill_param.py,sha256=wYgt_lIfSv9ulr2_z-4kmjqhhp6l6WjLwdyhs0EU4Ts,623 +openai/types/responses/inline_skill_source.py,sha256=jtMISjpzE8KF3ZgmjkHYHR4MZ5LTKlWuZhGD0m7fF0E,537 +openai/types/responses/inline_skill_source_param.py,sha256=cEYzuB75msGf-jimEfMwXYCR-kuxGHB6XrUPDnj9QOY,613 +openai/types/responses/input_item_list_params.py,sha256=PkPyuP-bHk3BMQQkQT-xc_cGLgvl1gpF-Wrzr-2uP4g,963 +openai/types/responses/input_token_count_params.py,sha256=Ym30MgoDxDYoE1TAwOjWNtfHAI66sNJlv6-C7ERrVcs,6363 +openai/types/responses/input_token_count_response.py,sha256=s8xR6NXlvtgh1Z6ZCVTrfWM9NWbWsoCG2WxdevTXbKA,309 +openai/types/responses/local_environment.py,sha256=Ia9i-SSs-q9wOZu9DgorbA7x0wtsZebuDVP2xfAvFb8,453 +openai/types/responses/local_environment_param.py,sha256=FLRfjZfdtUKdy4wTb1QW-NOy5XJ_-jz4vCyQ2zjtpfI,507 +openai/types/responses/local_skill.py,sha256=UYRFJYbnXRt7lsX2UB42jB_zwoiapSyN3Qz-4yAe8AQ,358 +openai/types/responses/local_skill_param.py,sha256=90r_5kY9wYKSROD8pGjhRRV5b9MuegkW8sOUXB-HTTk,464 +openai/types/responses/mcp_tool_call_error.py,sha256=lKGCgBKEcmymN3dfQQ8RzGeaU1gzAV_U7dlF33Etonk,787 +openai/types/responses/mcp_tool_call_error_param.py,sha256=vQVKp_xNJzz4Y5E4CbA8S2RvB2CMxPH-sDFWr8bYQKM,840 +openai/types/responses/namespace_tool.py,sha256=jxSfh2x6KuT5cHvDZygF8aUfjvRBOAvnHXUCcNucaSU,2003 +openai/types/responses/namespace_tool_param.py,sha256=6CsWq_BODNolw5UV-8YKSPll8WwUNu2-imNaCHlhwwY,1918 +openai/types/responses/parsed_response.py,sha256=zOK8Dut_TCLbMfulK5Zs6k3z-AfTxWziaOQ5jNM_Uxo,4584 +openai/types/responses/response.py,sha256=OWuF9VkVQ4Y5Rke0xoLh36sQs_J7jV7pE86QVcE_pRM,20694 +openai/types/responses/response_apply_patch_tool_call.py,sha256=t3R91oJthgPuhfohuTnMAAJYGoirzMKHC21Y5ThGKUo,2696 +openai/types/responses/response_apply_patch_tool_call_output.py,sha256=cYMcyY_psooUvG0UygAw5nzNNS-GfGKDbyf469GYb4U,1555 +openai/types/responses/response_audio_delta_event.py,sha256=e5qHj1IyyFpID2R1RSNfJiOL8ar121jI1YI2b5xbGtY,573 +openai/types/responses/response_audio_done_event.py,sha256=wYnaAJUYcDGCDWgm3o0KTe2zUAcn55AnBZbRnWpw0_E,469 +openai/types/responses/response_audio_transcript_delta_event.py,sha256=ZKgsX8EReRv0kf0vwIjLH6yPTHX2kPOynOmy-9tlyag,596 +openai/types/responses/response_audio_transcript_done_event.py,sha256=wTsuodJdqOgx2ZWJ5LFlI3OqBwAtUrL0Kg5qVwnMkNk,520 +openai/types/responses/response_code_interpreter_call_code_delta_event.py,sha256=kjLCHpXxmvT1T9hnC9oo0S17_XpCZGQm2vXQGQ8drk0,923 +openai/types/responses/response_code_interpreter_call_code_done_event.py,sha256=KCajbhacOdtTQKV_qIHL5K7bFYqab_mi9wur8JVIimU,884 +openai/types/responses/response_code_interpreter_call_completed_event.py,sha256=4q1f1xTI5dhLrCaF9QEBFXpyhzjvwZSvQUzmrGARgjU,822 +openai/types/responses/response_code_interpreter_call_in_progress_event.py,sha256=z_fXl9cFIICyTA0xofb8pGyWFWhHxL7D8c06mI09aKI,830 +openai/types/responses/response_code_interpreter_call_interpreting_event.py,sha256=kx_TrjhOlAlaUdQ7MZ9fn_nLRy4cCp9Kc70rl_lUyWc,861 +openai/types/responses/response_code_interpreter_tool_call.py,sha256=xNlKbjX8Y_yBhwO4OtTGsbDwR8M-2A1g6fksk9eWh2o,1794 +openai/types/responses/response_code_interpreter_tool_call_param.py,sha256=LOi5ZdA5onI4pGh2u3528-uV8UjRgMPt3RMvzUC3xNE,1867 +openai/types/responses/response_compact_params.py,sha256=SOAJAMbHnADWuWblmdUueU4moFvTCFp-H1SVECiTNDw,9457 +openai/types/responses/response_compaction_compacting_event.py,sha256=kGcsHI09e3KOeZ_219ckLx545tZcxvGAnodS0fmz1Es,734 +openai/types/responses/response_compaction_item.py,sha256=3m45CdmlMkzk_njOIB9jNOMaOZUni660dwASe-9qK60,772 +openai/types/responses/response_compaction_item_param.py,sha256=oiPVLJx7P4EZhLdAeFwvO6zSoERZxhCGjA_JSdrf-_o,687 +openai/types/responses/response_compaction_item_param_param.py,sha256=NpkkC8Tqt1fqhm0iBi12u605YYAmA9PMwR3rTI1jVt0,746 +openai/types/responses/response_completed_event.py,sha256=01FU2iKsNjWGFKJGFUKIyIrYQYkfOjLRwBwtMOFabJA,572 +openai/types/responses/response_computer_tool_call.py,sha256=RS9PJ76wHoy6sdLB_ZJhudFOeH7lvTds-1neReEBG-0,5986 +openai/types/responses/response_computer_tool_call_output_item.py,sha256=DcmiwRgd_lpKrvgqcU9WCQ_2PRf2juNcLqwUnl5dhJY,1675 +openai/types/responses/response_computer_tool_call_output_screenshot.py,sha256=Qw4Cxgrye69EWVBrYLUnySxVGI-a7E1ImuW7WIhskds,733 +openai/types/responses/response_computer_tool_call_output_screenshot_param.py,sha256=4uEGhHvbx8gVSikcvNL1vAi0MhOvlMrkAfRk-nM9Ifs,727 +openai/types/responses/response_computer_tool_call_param.py,sha256=wiNcOeNKOvxmoY_HwvmQy5ZQ69vgMwp5K9pL6U4VzjE,6394 +openai/types/responses/response_configuration_update_item.py,sha256=TQesoPXbtvh_HbMfwW27ouSknQZw7b1GJWkHBmYFpwM,1026 +openai/types/responses/response_configuration_update_item_param.py,sha256=yOsbGPSJxNggvBfBDlBel6TagFlZLpA4gqrl7TypGa0,1136 +openai/types/responses/response_configuration_update_item_param_param.py,sha256=jhd9v6MJk0qK7bxb0ni0i936M2WkLPtmHq2Gd6qSSps,1175 +openai/types/responses/response_container_reference.py,sha256=XvtvKL6TWSqJDk4lIgBKyhdkmZfrosiT0HhKnxdwtCw,436 +openai/types/responses/response_content_part_added_event.py,sha256=W6nxlf0XbK59JrrBB0ghDbECpGEbGuAnsZ17QF9jHKw,1431 +openai/types/responses/response_content_part_done_event.py,sha256=Pn10zK98FhCMfTCvPEDpauBiaPI_dFTh7wtB2zG8Z7A,1418 +openai/types/responses/response_conversation_param.py,sha256=rgswSvlGxG-j4RSF-re8bmB4SzO_WU2xpTC0KKPq9-o,322 +openai/types/responses/response_conversation_param_param.py,sha256=RGxsj_j6uwKPaMfLLAV5uc3f89SyreAvGOrbwknllwQ,408 +openai/types/responses/response_create_params.py,sha256=rls7tWW4BfdqqYTdDZ4CDqQ6wTv71swRnGmAtJY9Ffg,21497 +openai/types/responses/response_created_event.py,sha256=MQ12ODjjBmpr6HLmwX_Evp66hreutFwLMIurzdrBIsw,565 +openai/types/responses/response_custom_tool_call.py,sha256=uqdDXhVlug5JLSiK58An76llKDTIqdspgQOQ7Glu5mA,1582 +openai/types/responses/response_custom_tool_call_input_delta_event.py,sha256=o7R87qVw5VY9QDciJx8btCOWy3vQufnktNioVsJT8BI,785 +openai/types/responses/response_custom_tool_call_input_done_event.py,sha256=nBfTXIw0rNR8bFvoqPt6LpXgH5984dHFk1kebpwvhC0,755 +openai/types/responses/response_custom_tool_call_item.py,sha256=VM5d1GmrnH2PZRV4NKfaTOwkG-4aV10JqCffoJYPjeE,761 +openai/types/responses/response_custom_tool_call_output.py,sha256=sif6OllM_TgGHcv-xonthZ4jpFbdGMEYS88nXbCv2hQ,1863 +openai/types/responses/response_custom_tool_call_output_item.py,sha256=t2A6MCuALtB8nFY7XCv2bfH4_hy8ClbOngW8pWRjw_4,832 +openai/types/responses/response_custom_tool_call_output_param.py,sha256=dNj0zEyyrksXLk2I0RwuFcJMUZKehbtW29agl1PM3Xo,1876 +openai/types/responses/response_custom_tool_call_param.py,sha256=J6re9VaZvGBx8n4Sm7tnB-DyVFFN9eV89Uk4Eaz32xE,1616 +openai/types/responses/response_error.py,sha256=AJibBzPsI0erWv0j5TTL-N4zKknUvKgQ4VSWAzFG8E0,1954 +openai/types/responses/response_error_event.py,sha256=0fPfbBHEE9ptHN18Dn2iKxNhwWojrXVgTqvCCijWARc,616 +openai/types/responses/response_failed_event.py,sha256=avRIbCQtIp5uUZNCzHJzYSCXGu89_0KXMCuTPoYx48A,550 +openai/types/responses/response_file_search_call_completed_event.py,sha256=swcZQu7d7VWCpmtJpEI-Kn1BmyAhU_Q-WBdhv3UFyGs,743 +openai/types/responses/response_file_search_call_in_progress_event.py,sha256=y4woG3EndRPbWGlFe4QqMtsJbdsuKwhNG3EjdMXYw44,733 +openai/types/responses/response_file_search_call_searching_event.py,sha256=XFVPUmXSMSUNvvkEsMK22hXJ97S_Cp9oIxrvJtAHcQ0,732 +openai/types/responses/response_file_search_tool_call.py,sha256=im5Yrnl1jRaI9jwdLQLdMt-mWjXIJLweUmr6Rf8GKYY,1843 +openai/types/responses/response_file_search_tool_call_param.py,sha256=L0uUTFFNq5FZhgyf1u4WZWK6ikpmR_F_NWI9DTCiEM4,1916 +openai/types/responses/response_format_text_config.py,sha256=CdvVVjfQKB_F5B9A8-pSKeCTe6IGqyUwoo665raIjes,646 +openai/types/responses/response_format_text_config_param.py,sha256=IVbmu8IKRLLZcaW8ZBubcNkKitbod788_Fp4Gz0Ei8I,624 +openai/types/responses/response_format_text_json_schema_config.py,sha256=muPPGjkBtnstf5MTSlEoH6tKlklAN3fL0Qjmf1Q1VQg,1622 +openai/types/responses/response_format_text_json_schema_config_param.py,sha256=XXd-WNph_Vt-HmAbYHNK81EymaMiX92HmxC405YKteY,1604 +openai/types/responses/response_function_call_arguments_delta_event.py,sha256=DbH3zdgzeJ4sAjXhovqoB7ByMVx9tm-mNomEEKLu3sk,865 +openai/types/responses/response_function_call_arguments_done_event.py,sha256=YyZmOZSCHVlSaXpdoHtIq1WKBmjaQIZTdGxb72asLas,634 +openai/types/responses/response_function_call_output_item.py,sha256=MvUcj_eK_IPVb3ltGR6rudn3Ejm-IKAhfNKzC9QwhMk,632 +openai/types/responses/response_function_call_output_item_list.py,sha256=Ql26jpejLLJWYUfAvuJPsnUr0-YonHLlaZApimD2-pk,366 +openai/types/responses/response_function_call_output_item_list_param.py,sha256=UzEbxEE-wJKZ-GaMMWslt5bSzmXVXusG61G9LHjYWDY,773 +openai/types/responses/response_function_call_output_item_param.py,sha256=gfgAQ3m1uK9okzAXOcbcA55qH-jbDNCopP_asxX5X-U,628 +openai/types/responses/response_function_shell_call_output_content.py,sha256=kRb8Qd7CVvZdZK3DYIq6faih95bjjKClhisJ2seR0OE,1259 +openai/types/responses/response_function_shell_call_output_content_param.py,sha256=VeSJ8gkLdQeun4FRg3FUGI5C2D2q-_5N6QbTXoMjp_I,1298 +openai/types/responses/response_function_shell_tool_call.py,sha256=8wJWzyRgENsrYdXul1BSXelX8JkBwpVKeBEoWFNHgiY,2346 +openai/types/responses/response_function_shell_tool_call_output.py,sha256=8zdc2VH474QkMotLrwI-jAcIDGFri3luwO_DqEeyUdA,2914 +openai/types/responses/response_function_tool_call.py,sha256=t-aob2kfGLBTm40cCMFjhu8gKDa2ku8Wx_hnJJ80mJE,1880 +openai/types/responses/response_function_tool_call_item.py,sha256=bj-CWOznESJJ0oawdWwg58xRtWf2EbzzQ14VMlkGtaw,902 +openai/types/responses/response_function_tool_call_output_item.py,sha256=kIJOdAUFnN09IcYLTTDHk320afKEWUW52OUhjG6-EWs,2237 +openai/types/responses/response_function_tool_call_param.py,sha256=rXweJ--75OLDOp8i86FGrfZ5O-sNaCzR0pT1GFAqNDU,1901 +openai/types/responses/response_function_web_search.py,sha256=ybFJ0cdSe2XNWGuFJykWJ12ZRWF5rhRY8HpjEG04a9Q,2398 +openai/types/responses/response_function_web_search_param.py,sha256=YHG6tliNedTIBiqRsu4fgoe3BR98iHEEE4oKIcqstU8,2511 +openai/types/responses/response_image_gen_call_completed_event.py,sha256=9KYnYqldmX1DbYy61Lf2A9yc8Vuhwhxk2safsBEUdQ0,782 +openai/types/responses/response_image_gen_call_generating_event.py,sha256=Rv-5f2zbacGr56ZlGJPT1hyX4deNiXsbXDLWVShB_qc,823 +openai/types/responses/response_image_gen_call_in_progress_event.py,sha256=ZTPExer6ac1Q_WYvmvWCLTmrOKzj2j0-DubmE9PGGpY,777 +openai/types/responses/response_image_gen_call_partial_image_event.py,sha256=UyxpUPEwKS19ZrPytgenCLCQ7aCjufhhAiVxtUSktPg,1406 +openai/types/responses/response_in_progress_event.py,sha256=RjBkgjQVEx7AUiXvInd1P5WpQK7IKADTXQLQQipE84U,570 +openai/types/responses/response_includable.py,sha256=KXHq3EtoFYECukGRmAUfY997Mf5WBcHmP2DJU3xB_QQ,504 +openai/types/responses/response_incomplete_event.py,sha256=pgW0vlTloDdayiprCYqjPfUTE6l79NP7136TjJSQ2O8,813 +openai/types/responses/response_input.py,sha256=_o0-IH0MxldiuSqyUE1YSpgA7cTE5gMfS8Fnk76Yn7Y,283 +openai/types/responses/response_input_audio.py,sha256=8nOjSzWFzZeBHVUwIb3WeiBFFvWp6DgYa87a3TrhMz0,613 +openai/types/responses/response_input_audio_param.py,sha256=MxEd12I0dTerMW3mH5QukCbUr8j6ppj32KqwEoVoivY,712 +openai/types/responses/response_input_content.py,sha256=-VLG417Mbkxoppj4cG0XfbLcluFbU69UDLIdvctW-hU,541 +openai/types/responses/response_input_content_param.py,sha256=3Lf1b5h44own5JNppyE5M4zTID7oOO3XApzRQNIuLwA,536 +openai/types/responses/response_input_file.py,sha256=MdQWpccDv_bPfEcnELRygGBaEg4fglluroyhOE5FjuA,1772 +openai/types/responses/response_input_file_content.py,sha256=xUwspashF1nRt9SywE-sdVnyARJyRHKnuc-bdVfc9D0,1798 +openai/types/responses/response_input_file_content_param.py,sha256=MhYjoV47ruUiBCCIbWYQPYhkfRUvca3fMAYvfxlFEYg,1825 +openai/types/responses/response_input_file_param.py,sha256=CBZBSU9kz-IfsTd9bnZU6Dvh7n--pUfLPyS3YvMY9VA,1759 +openai/types/responses/response_input_image.py,sha256=F0yRn9ZqKH2A-B3cvKxClKmPERBIW7a1sOZwtTf4Xn8,1556 +openai/types/responses/response_input_image_content.py,sha256=IK96gj8C8Bjplx_wB8cvn21JaANTZ1XGqqUWqVMEvwc,1586 +openai/types/responses/response_input_image_content_param.py,sha256=lHqhj8AvXSQIgdlL-9Qn2G4DDvTAo9Sx3NiqePT-J2Q,1638 +openai/types/responses/response_input_image_param.py,sha256=ut52gp935MlbkBexjCix6whDqvQSTt9AzhOt8FyjGQk,1615 +openai/types/responses/response_input_item.py,sha256=Sl8flRryHW8jlm-nxKdQGa-_wx2HNagx45ssdwtWK50,23751 +openai/types/responses/response_input_item_param.py,sha256=a4TXNBI-NxRTkhFIbAWra1eaGIrfRumlXICOAlvmzPg,24575 +openai/types/responses/response_input_message_content_list.py,sha256=JsedUHkgje3LKzp5NWQzcUyD7Lyz6w5XxcjTYz1ends,328 +openai/types/responses/response_input_message_content_list_param.py,sha256=BTKasoYHO1fL9udx2O5J5DmbLPQfJdKhjWz7Lxj3P1w,665 +openai/types/responses/response_input_message_item.py,sha256=N48WlGNnNoSxvKwi852NPPCbYZOcSJx1w6VL1asBDJw,1011 +openai/types/responses/response_input_param.py,sha256=A7xa3EB1bJPt2mEXEeUjw7LA3O6YFVL7VQwR6lzmbCM,24669 +openai/types/responses/response_input_text.py,sha256=z3y20wueGoiv2cUjq-hBWkYhDdGWK6UCFvy4Fy8b_30,1049 +openai/types/responses/response_input_text_content.py,sha256=108TGdPmkeI7RpZZO8ObFS-3aUkNGntQTKXInPX6zyY,1063 +openai/types/responses/response_input_text_content_param.py,sha256=wBGWBJULx5dSdHgvFx1IPlmkBrOK94GsQrXc8pccDVc,1145 +openai/types/responses/response_input_text_param.py,sha256=uVUEA5qXfAZH-NQmviGU4ecinNvsnw5wZbM0961DOMo,1093 +openai/types/responses/response_item.py,sha256=5uD4s2Lz9ELRkteuXnvwbFQHd5fyf5A0VYnGYG17Qwc,10482 +openai/types/responses/response_item_list.py,sha256=DzL783qPRSw1IaLYNV9bzlrIMhLCAt0IgjBupfGo2J0,701 +openai/types/responses/response_local_environment.py,sha256=v68KayHnjrXNUwydYzPZFm-3VFY-LQyGgTMtTeLKAcY,397 +openai/types/responses/response_mcp_call_arguments_delta_event.py,sha256=eJXbGmtwpkC6FsYy4EkyyVW7W1rJMv3l-GCq3dLLS7s,883 +openai/types/responses/response_mcp_call_arguments_done_event.py,sha256=drqLf_aXhI72HxQVPaS0S8WbHYQier_16AMmVoatWoQ,825 +openai/types/responses/response_mcp_call_completed_event.py,sha256=FGo7PGrYZOaGsbUDnM98HAHNNM3QvXxcGpcJYXcYmOk,669 +openai/types/responses/response_mcp_call_failed_event.py,sha256=5PidtJDrUMWLTIyLH7hgL4m9aCloEMqWKKo89gXmARM,635 +openai/types/responses/response_mcp_call_in_progress_event.py,sha256=7OjK1D7qFamCfbEdqnEtSCc5CXyQhRbCusSEptR8njY,695 +openai/types/responses/response_mcp_list_tools_completed_event.py,sha256=bAGIUDL0y643P98VuXs8jPS3zOziii8e9hK-_cFs4Co,725 +openai/types/responses/response_mcp_list_tools_failed_event.py,sha256=d7o_rHLbVy12pNVEti7yIC4_yEXYErcQcZIIQk3b80U,679 +openai/types/responses/response_mcp_list_tools_in_progress_event.py,sha256=308qwtfD1umVd3u0nd0JlXfDdNGsR2MYX1fIRnd8HZ8,755 +openai/types/responses/response_output_item.py,sha256=0jQ1zDK6oY4n7KJeKBqlsmDJVCU-xhVyZ3ApngkxFe8,10247 +openai/types/responses/response_output_item_added_event.py,sha256=bbhbZ6RbGclSX_J3WPwlUpa-9E8Tn1lwQWR3QcHTKu4,937 +openai/types/responses/response_output_item_done_event.py,sha256=H1Y1khkcZgQH2QMykn1zL0L2MEKZHdefWohk9qbfJa8,706 +openai/types/responses/response_output_message.py,sha256=2wTQaMw0nbSg6oEvby6xozarXmS7xIGyu8knrI0OJtg,1565 +openai/types/responses/response_output_message_param.py,sha256=v0P5BYhaMSga8KvQ5vCL7ECqFqigns5TYcU4GegkddY,1602 +openai/types/responses/response_output_refusal.py,sha256=YR3oavV7rGiVlJw0QG6LWIFqFGP-LGRZ26lg6f0RoL0,424 +openai/types/responses/response_output_refusal_param.py,sha256=8nyp397-DtyteNQttuDq2Z0OklOLz2GeDg3Ra0_DRiQ,490 +openai/types/responses/response_output_text.py,sha256=ZZ6JfEthcmj7_nbdBOual2KhZbljE5Rm6WoVMmMzPrE,3156 +openai/types/responses/response_output_text_annotation_added_event.py,sha256=o5S0B-Z3SFQ8A1ODvkrr454C2hqHem_vHif5eZ0mjDc,3307 +openai/types/responses/response_output_text_param.py,sha256=8S9zls6e_GxUmRRvGC8tq10azXl-L79TWwX-xhWjklk,3459 +openai/types/responses/response_prompt.py,sha256=rd1XRl_6NxXSgK-CLBZ4ev8ckIc5rz8I7Vs7D-ZPgNQ,1119 +openai/types/responses/response_prompt_param.py,sha256=zhVSCvbwGSTH6kvo8DakVME0q9WHNH2j_MHQzc36cAE,1210 +openai/types/responses/response_queued_event.py,sha256=Q79rmv7cWjiINVL_MGzXjhl8wcmh57dwHYXsrX3bGfw,581 +openai/types/responses/response_reasoning_item.py,sha256=pAfOJeBF0R-TLUgbEVSWzxe_0IYArYhCRW7q44DlXkQ,2193 +openai/types/responses/response_reasoning_item_param.py,sha256=tCTW8N7ygl-5rGNUKPMDLeeAE3F9MDsaonfR5e42Xo8,2306 +openai/types/responses/response_reasoning_summary_part_added_event.py,sha256=NG5o6VDwNTFlrRInMxfyIN0C7-qf97dJ6dp42DitpXE,1112 +openai/types/responses/response_reasoning_summary_part_done_event.py,sha256=SZdnMxNzjdzh9Q8OfcLnGcE7pnj-4rMiz_Fc63aKp7I,1343 +openai/types/responses/response_reasoning_summary_text_delta_event.py,sha256=lFIIatfkOQrHdFQlBRXAv2snA52wcQBCglUpwLGyuA4,915 +openai/types/responses/response_reasoning_summary_text_done_event.py,sha256=hSq6TPWpVpqtMEnF2BG4DFM9In5O1EL6Zj05endaTOk,895 +openai/types/responses/response_reasoning_text_delta_event.py,sha256=6HoG65BIV4csuQEPmjGAMVOp1hU-d9x9OnjN-menuFk,902 +openai/types/responses/response_reasoning_text_done_event.py,sha256=oViLqVNrydxchTCIb19YwWZclfviRtkKQTynoOWURlo,842 +openai/types/responses/response_refusal_delta_event.py,sha256=q5w5ZkGyUrnKjpAZJ76W7tWwvx2-Z0bWHkwoWO-dtOI,826 +openai/types/responses/response_refusal_done_event.py,sha256=CQdC9UakeofchckXPWLFLJ_J5ij1cDfq0TP5gI7CTI4,825 +openai/types/responses/response_retrieve_params.py,sha256=5l9JYOgRAitGBhpr_t1BHf2G9UqlxCW3i21_6Gn3CB4,2399 +openai/types/responses/response_shell_call_command_added_event.py,sha256=_gcmIHmeS8r_FyJhP16yVTuD_SthyLELbptIS53Sh3g,791 +openai/types/responses/response_shell_call_command_delta_event.py,sha256=bdWBrqOFpkVNtWQ2YtBB291qVubHdCn6Z54Tr2QjoRE,941 +openai/types/responses/response_shell_call_command_done_event.py,sha256=RLcIC0HMyFaVxGfB_yW1Ont-hffzCrn6UpOmx7p9skQ,788 +openai/types/responses/response_shell_call_output_content_delta_event.py,sha256=yvckhupTELG6G_3aKCaMdh_paxvGoqWvgkJd8D6sYyc,1177 +openai/types/responses/response_shell_call_output_content_done_event.py,sha256=bSYO93XpS_E4qlnmQvCTP__shDk-6Byd5afqwOeTErc,2158 +openai/types/responses/response_status.py,sha256=P0UNeZYxwYIzHt5SgNROSsU2dxq6JlgSE267rfpLgx8,277 +openai/types/responses/response_steer_accepted_event.py,sha256=eK343spOweuqsGY3qHc8NpOWmhtI1Yj0KGqJVltFDpo,1642 +openai/types/responses/response_steer_error_code.py,sha256=FkrBsr3tG-x7e3aiLCFDKr19BApede-Z8gNK-owFKNg,503 +openai/types/responses/response_steer_event.py,sha256=LI7uAsf3T-6FgC6H6pd6ArIj5nxsDQKML-bb2WY_r_Q,2806 +openai/types/responses/response_steer_event_param.py,sha256=5JTGAAduNcHcw-B6Pe4UUH9ZVIPxNaNYeKOAizBdYlA,2899 +openai/types/responses/response_steer_failed_event.py,sha256=syOdV_KI360cOff95pgDJGoPBYa6xBdvCt2rI3Jd4gA,3403 +openai/types/responses/response_steer_input.py,sha256=nmZD0DwQhQgHexEmxYWfmlsoVNkVQL_G3Lv-HmJH5O0,3346 +openai/types/responses/response_steer_input_content.py,sha256=xcMmfJsvSJfW0l5FQrBsqxr8VBGAF1V1GA3VppExwSw,622 +openai/types/responses/response_steer_input_content_param.py,sha256=H-5vPvez57RUc0O1eE1HKu2KWlPrGQ1gblZPAI3OfUA,618 +openai/types/responses/response_steer_input_param.py,sha256=0rRJ2qcaNBeKc2Ow4lysLgtO1-JTK8bGwBOLeCifE4w,3313 +openai/types/responses/response_steer_pending_event.py,sha256=1nw7LJimVS3FVIyLm8zPnsxlUDvQ1XB9VlbfT8yKWC8,2817 +openai/types/responses/response_steer_pending_reason.py,sha256=bBxYaZbmD4WUFPsSVb0swBLUz0WgQBKUMpi013PishU,293 +openai/types/responses/response_steer_required_input.py,sha256=eO_rSKKVX_6B-0lnsPRRXSICB99g0e4bchZxDtrJJ1U,2501 +openai/types/responses/response_stream_event.py,sha256=8bBzAZjHWSdqKy7xOIEsLnIutQtGieE2i5PRbp6D5i8,7734 +openai/types/responses/response_text_config.py,sha256=qBNIexXVpjjWwCNum9rSJ9VdYoEqKnCUyVPh8lZl-0Y,1697 +openai/types/responses/response_text_config_param.py,sha256=9Re0eD0lTq6U68my5Me7Zg5NjtIAA7u7svVEh9wEaq0,1726 +openai/types/responses/response_text_delta_event.py,sha256=AVbPbKIpifnmDMRQGQvTnXp1ENE9ZkMLMQdaiq3_ako,1695 +openai/types/responses/response_text_done_event.py,sha256=cbQIGrbjOZMbkqq5qRyOuvFSCrlIqyeWqMpeJWtx090,1693 +openai/types/responses/response_tool_search_call.py,sha256=9SjKHl6BHlENeqiTCSTCV9_ARyrnmb2N-bxCv7UOQe8,962 +openai/types/responses/response_tool_search_output_item.py,sha256=C7k4o8FK-Ql0kK2jejXcCk8JBCp5CF1QKOuNvBxiYrg,1023 +openai/types/responses/response_tool_search_output_item_param.py,sha256=fkIWtFC_CXCsAEfAOHUgaInydMflbkpXFnMkkfBkt74,962 +openai/types/responses/response_tool_search_output_item_param_param.py,sha256=uj5Rydp51mMEygkaFAMIFMmNr4uemvPamV6ey31ZhZc,1015 +openai/types/responses/response_usage.py,sha256=ruscJHjtwWPDdYMPHMjN1flJkmOe908Kb9uvP_kEtaE,1311 +openai/types/responses/response_web_search_call_completed_event.py,sha256=vmk8G3fEqrk8utuA6k7qqmW5S_WJT80dLAxii2WhPrE,753 +openai/types/responses/response_web_search_call_in_progress_event.py,sha256=ld-aawwGdlJN4KhzMAW6lylWqD9AQj9BGs-yGtk0JEE,759 +openai/types/responses/response_web_search_call_searching_event.py,sha256=n7uWSPPzcuMyhyTvLxm9NTrMDy7LArAQOZHt31R86RY,753 +openai/types/responses/responses_client_event.py,sha256=ZQhw6-OWTuzmjlLv8gaSz9ARlpb3osxY5RzEbYBCbws,23481 +openai/types/responses/responses_client_event_param.py,sha256=NUWYAE5i7ymsDZONvztBt5tzZLTjeF1ZD8zlYTCXEUU,23410 +openai/types/responses/responses_server_event.py,sha256=yhpDJsa3Ab65vzZaWe0cChZFa3wOGqrv30_1pMQcgQA,32142 +openai/types/responses/service_tier.py,sha256=NZ6YdTMdYc8ZN0V6ks_j57ifgko8e-HgZkccmSIYWG4,303 +openai/types/responses/skill_reference.py,sha256=nESTAMeTmIlprEl7Az_P_2Vj8yj4JdfUCymuVlJFk1w,539 +openai/types/responses/skill_reference_param.py,sha256=b2RmEyqpeS5GNE2QMvAs5Sq3d6i_t5hmoL3bR0g-6_U,560 +openai/types/responses/tool.py,sha256=Gc6UqAp4NXWr7NXgBNH7gf2OeQTaygMvzk7bsOBz6DE,13924 +openai/types/responses/tool_choice_allowed.py,sha256=UUSYxIJ1FmuBRpS55Us4787yh_9v72oVkDxybb09Hus,1099 +openai/types/responses/tool_choice_allowed_param.py,sha256=xDcB2vY2tilS3jedGeEOBJNJFSdVtatmTKXHb1QVinA,1183 +openai/types/responses/tool_choice_apply_patch.py,sha256=Ngk2f1kK1QCxUJl1Pj5ylw1LGmcbb3EGChss1WiM8sM,403 +openai/types/responses/tool_choice_apply_patch_param.py,sha256=SrrDCbnTnJeT_mvMSAF1XsmuD0wIhl0fV43S95QD78c,459 +openai/types/responses/tool_choice_custom.py,sha256=LjZeWr-Qi5Il0VaAFGiOkJu7HCRPUjdyLz5HZNhA-RA,459 +openai/types/responses/tool_choice_custom_param.py,sha256=-K91sBTy9YFqJNnSMbS1Hqge3vxY5M3mmxtvN7zpH-0,525 +openai/types/responses/tool_choice_function.py,sha256=ZBqg1DkKZmyl-wiuIUYFuzsz_UIrH1E8MrDNgCRGtec,458 +openai/types/responses/tool_choice_function_param.py,sha256=qqYf_mJbcwHDXfKwCUMJSYpMoDFtYTP3O0q0Vv7Pm5c,524 +openai/types/responses/tool_choice_mcp.py,sha256=CPmCg_5s4adFDqa7iFzfWy1BQNqYgyiWqQWMwUisVwQ,584 +openai/types/responses/tool_choice_mcp_param.py,sha256=DOuedmKVqgyAAGRl5kPzIJhDrSxmGQfv0gRmDa3Bkeg,643 +openai/types/responses/tool_choice_options.py,sha256=YpmeKHYlMOUh5s3acAFcCJFrPGEcuZaUO1iaOcPms6M,236 +openai/types/responses/tool_choice_shell.py,sha256=HVeRZmb_PAk5mycTl91dJO6vd9yAWEPJKhgH2QouNH0,377 +openai/types/responses/tool_choice_shell_param.py,sha256=KmreCwHFHNbRQ6TXU9jmXyFDEbsRCviJLsFmClVEhG4,433 +openai/types/responses/tool_choice_types.py,sha256=aPMdqCXHHsP-2zqmqLkZpV5544aEPSWvrtc4mj8jR9c,1016 +openai/types/responses/tool_choice_types_param.py,sha256=4CjKpMgW-JDI5cTdlqKc7JZ3ZFYzNLWCYYQCvr6Otoc,1122 +openai/types/responses/tool_param.py,sha256=VkY0_kf6_vNNGuqZ0HQM7bEzc5zXW37J0ubiaZ0KMp0,13859 +openai/types/responses/tool_search_tool.py,sha256=YgcORJKE1-FeuB5Km2r6JYhVD-yWKNossoNDjSnuTkA,773 +openai/types/responses/tool_search_tool_param.py,sha256=T-OECpTaGSLxs2MKTprd8MFyd-D7ZkqXYeb4JGwr4L0,798 +openai/types/responses/web_search_preview_tool.py,sha256=dVPS1tyZKEMhbzMOfQk9uL1SsmdRwwhMHdLv0wl9qeo,2218 +openai/types/responses/web_search_preview_tool_param.py,sha256=-B9TBiluIJONPNj8R7Vk48pC9C733JkM9IxYMktJZUM,2228 +openai/types/responses/web_search_tool.py,sha256=JnMcMD2LKNExEeWkg0-JyCVVV2q9gCA5h4I2qGM5nUk,2755 +openai/types/responses/web_search_tool_param.py,sha256=8TXXf19V4C2JNHsptouk06IYVZUM7mXxDktM3DusJks,2779 +openai/types/safety/__init__.py,sha256=I0hXtp5ot-3Suj6pV70X1UzPfDhdXo5PQ-la_Cm2Sao,225 +openai/types/safety/__pycache__/__init__.cpython-312.pyc,, +openai/types/safety/__pycache__/safety_alert.cpython-312.pyc,, +openai/types/safety/__pycache__/safety_case.cpython-312.pyc,, +openai/types/safety/safety_alert.py,sha256=IlgAIgPv46avX1L0ACE5wDtzSBrn-OvDYrljSC6WqlQ,881 +openai/types/safety/safety_case.py,sha256=M_pLvZvHfCDTDHcFlFbd2U-iZ24ZWVynE6JFHYYhSV0,476 +openai/types/shared/__init__.py,sha256=4hSw_ncXFD2WUrjMGxK-8sbUX-u-g22-Glmr4OZvj5A,1329 +openai/types/shared/__pycache__/__init__.cpython-312.pyc,, +openai/types/shared/__pycache__/all_models.cpython-312.pyc,, +openai/types/shared/__pycache__/chat_model.cpython-312.pyc,, +openai/types/shared/__pycache__/comparison_filter.cpython-312.pyc,, +openai/types/shared/__pycache__/compound_filter.cpython-312.pyc,, +openai/types/shared/__pycache__/custom_tool_input_format.cpython-312.pyc,, +openai/types/shared/__pycache__/error_object.cpython-312.pyc,, +openai/types/shared/__pycache__/function_definition.cpython-312.pyc,, +openai/types/shared/__pycache__/function_parameters.cpython-312.pyc,, +openai/types/shared/__pycache__/metadata.cpython-312.pyc,, +openai/types/shared/__pycache__/oauth_error_code.cpython-312.pyc,, +openai/types/shared/__pycache__/reasoning.cpython-312.pyc,, +openai/types/shared/__pycache__/reasoning_effort.cpython-312.pyc,, +openai/types/shared/__pycache__/response_format_json_object.cpython-312.pyc,, +openai/types/shared/__pycache__/response_format_json_schema.cpython-312.pyc,, +openai/types/shared/__pycache__/response_format_text.cpython-312.pyc,, +openai/types/shared/__pycache__/response_format_text_grammar.cpython-312.pyc,, +openai/types/shared/__pycache__/response_format_text_python.cpython-312.pyc,, +openai/types/shared/__pycache__/responses_model.cpython-312.pyc,, +openai/types/shared/all_models.py,sha256=FSw-b_VIcHYNzq8M4sRTqgEr2yet35rsHEUqG6pkC3I,901 +openai/types/shared/chat_model.py,sha256=8NeDgff98ebH_0xAjxNE_fEOrZ6INkNm9lrxiOm5CCs,2310 +openai/types/shared/comparison_filter.py,sha256=ZyTLLTRQDr6nzwoPYB-z3DOjYrsVMXZSU-t2f4Lem0c,993 +openai/types/shared/compound_filter.py,sha256=WFjX86Qz49_vBkSE6CnJ613EelQeHumLOLWmdo_f-cc,637 +openai/types/shared/custom_tool_input_format.py,sha256=qv144iGkBUkrmK-hJLRatzTXs1HDoOtDpuJlGzqrZtQ,855 +openai/types/shared/error_object.py,sha256=KLG2Us5XC3Zt1XTKH6Uwzn6_fCKr25sSv59pss33i1U,1175 +openai/types/shared/function_definition.py,sha256=I9UO_dDqe3lN_OHbZS07hEfL0Gb2L6xeviS2j0qZI9I,1486 +openai/types/shared/function_parameters.py,sha256=YSIK3R3XUhBEcE_tFP9CY7ryOAYJMm1K4a66QMGXqJY,235 +openai/types/shared/metadata.py,sha256=3E8_XznOmsO0q1WBy4eSneeBOsZOdljKIR8PblGQZqE,212 +openai/types/shared/oauth_error_code.py,sha256=T-sOgiqYs7yRZFKpqfgfvoy9rqUa_ad8-p9Er7-qs24,281 +openai/types/shared/reasoning.py,sha256=Q1Or6vl6Oy9itoPkr1Z4ouRKyX_mZYGWBYKE_WbRTKA,2268 +openai/types/shared/reasoning_effort.py,sha256=2lfMT40ML9ZvZjinOlUd1n_AnUlIKNUa1ITtDc1ju0Q,302 +openai/types/shared/response_format_json_object.py,sha256=Da3J10nq8HGMQgoMW0BsYL0jZLh5UH0GPbEBs14v3yY,623 +openai/types/shared/response_format_json_schema.py,sha256=mE-63L8fxxUGkF5CkCaHimhOW8f7uHeAsKq-u7wdHRM,1854 +openai/types/shared/response_format_text.py,sha256=DLjshsxoHqWKOeUiFdJ8s3HA-lG30tKgt2LM2Ee9dpI,394 +openai/types/shared/response_format_text_grammar.py,sha256=EDjCWPj62Xek8qNh-A-v3-PoeU4yhnsS8d7aGpS8lbI,634 +openai/types/shared/response_format_text_python.py,sha256=VNFnYJ7TQtnU5baFCjCEkLUQhhYmTIekAce4LD98t5k,553 +openai/types/shared/responses_model.py,sha256=6T-KFxkFbAAzk5TSPnbYJlEeNvAKnjXR44zqTU4kOro,911 +openai/types/shared_params/__init__.py,sha256=0pXxs6hXi1FpQ9jWGQoYbPGU2u1sM5TZgBn-_0maFUo,1038 +openai/types/shared_params/__pycache__/__init__.cpython-312.pyc,, +openai/types/shared_params/__pycache__/chat_model.cpython-312.pyc,, +openai/types/shared_params/__pycache__/comparison_filter.cpython-312.pyc,, +openai/types/shared_params/__pycache__/compound_filter.cpython-312.pyc,, +openai/types/shared_params/__pycache__/custom_tool_input_format.cpython-312.pyc,, +openai/types/shared_params/__pycache__/function_definition.cpython-312.pyc,, +openai/types/shared_params/__pycache__/function_parameters.cpython-312.pyc,, +openai/types/shared_params/__pycache__/metadata.cpython-312.pyc,, +openai/types/shared_params/__pycache__/oauth_error_code.cpython-312.pyc,, +openai/types/shared_params/__pycache__/reasoning.cpython-312.pyc,, +openai/types/shared_params/__pycache__/reasoning_effort.cpython-312.pyc,, +openai/types/shared_params/__pycache__/response_format_json_object.cpython-312.pyc,, +openai/types/shared_params/__pycache__/response_format_json_schema.cpython-312.pyc,, +openai/types/shared_params/__pycache__/response_format_text.cpython-312.pyc,, +openai/types/shared_params/__pycache__/responses_model.cpython-312.pyc,, +openai/types/shared_params/chat_model.py,sha256=Vxx6zmD3x2L8dg7A78_l5WBIcslvH22WUMstXe4_s5k,2346 +openai/types/shared_params/comparison_filter.py,sha256=2mYsMSrx_ysW1HX6DVx-Ji156P8HwWuU6SbFDQ9DLoY,1101 +openai/types/shared_params/compound_filter.py,sha256=t1ucQx5cqpBE77MmedLNrFV8x12_6eMpLvo1exVV_aM,702 +openai/types/shared_params/custom_tool_input_format.py,sha256=5YQqZbbIB61oxrHgsiHE_3pnp3I0pSqH6PaeDLxQsuE,851 +openai/types/shared_params/function_definition.py,sha256=gwbeVBorRHzih78CwnNFGfiSEJxFUjIddtxYyaKe0Dk,1521 +openai/types/shared_params/function_parameters.py,sha256=Q5BWb_woCthBBMsxLXhQGylCjpcuSF1rNTjLTPoA9pM,271 +openai/types/shared_params/metadata.py,sha256=b6xUIprClpIFQngbDu03yPRK4yYXNMb16Xl0me2Cjgg,248 +openai/types/shared_params/oauth_error_code.py,sha256=IGslUk9sx5alalVDOFujljCeVVXFG3L_L2NixhBtPrU,317 +openai/types/shared_params/reasoning.py,sha256=A63zhvOn7Z-Ishw9aHhYGWTq3aUopV1e-FKBON_kvOo,2262 +openai/types/shared_params/reasoning_effort.py,sha256=qFAqXNAowgOZLYSXOXgrzSajcG36N_NG8veZFDqIXqM,338 +openai/types/shared_params/response_format_json_object.py,sha256=0-IqJwBeD3gcNVo2xPZU7MQbWSBo1RiB33gqF9detAg,669 +openai/types/shared_params/response_format_json_schema.py,sha256=SoFSJYftX6dXHzdoVMh4cPiwpef25HPrn8Lil7-9hWo,1815 +openai/types/shared_params/response_format_text.py,sha256=5m_v4htoBamioIPFVITLjnrCyGBhJCwcxq7emUVOh0Q,440 +openai/types/shared_params/responses_model.py,sha256=VanfkP0nVURBH8Ftc0wZ9KgUN7fzD6wfIYk-a4Eq2Ps,955 +openai/types/skill.py,sha256=drFlxM0ZD3Dd2_1bMLMz5CCJ7Zp4vVv-21wVUU-ZekY,656 +openai/types/skill_create_params.py,sha256=1GW-vP_0SWA5bBfpecTTWs1lJlKJdkMrYxdnTOApBio,447 +openai/types/skill_list.py,sha256=nDq7FRNyP1qDPpYORWljVOFjaSdc8IMMiPWvbhi3AiA,639 +openai/types/skill_list_params.py,sha256=AGCJwh6rHJL4ktgwbMXYiQrBqDy09X8o2KhSo4EI2vs,549 +openai/types/skill_update_params.py,sha256=tWeTBFG06pyH-XL6bNNHeniGCvPcNwpSIqU-3F1UyxA,345 +openai/types/skills/__init__.py,sha256=fds3QUZyHf8_CbzVHJW7j0LC-tfGppeG08lpN2aZm3U,475 +openai/types/skills/__pycache__/__init__.cpython-312.pyc,, +openai/types/skills/__pycache__/deleted_skill_version.cpython-312.pyc,, +openai/types/skills/__pycache__/skill_version.cpython-312.pyc,, +openai/types/skills/__pycache__/skill_version_list.cpython-312.pyc,, +openai/types/skills/__pycache__/version_create_params.cpython-312.pyc,, +openai/types/skills/__pycache__/version_list_params.cpython-312.pyc,, +openai/types/skills/deleted_skill_version.py,sha256=cha28OWi_Gklj4jaWV7K3xiPR38yiu7QbmRGdMj7Dng,365 +openai/types/skills/skill_version.py,sha256=8Q83QyZDtgWiYCbdbBgV1cPzrB5f6a3SowEBXGQUFw8,711 +openai/types/skills/skill_version_list.py,sha256=prdLOEY9ojeBZqSGc4sR4SjqoOeeZ8UP2CukabEQrko,676 +openai/types/skills/version_create_params.py,sha256=pNN4CJUFc2p8kyxQcqYZuAf97fc561mJfZd8umInKTA,525 +openai/types/skills/version_list_params.py,sha256=6DkTM76G7lZnghixF_2pdBucT22cCyEaCISRUgzGgn0,461 +openai/types/skills/versions/__init__.py,sha256=vQjznC-rGi6u665EpV_FgNWu3tRwEkpL0f6KNLAsRVU,121 +openai/types/skills/versions/__pycache__/__init__.cpython-312.pyc,, +openai/types/static_file_chunking_strategy.py,sha256=DMErxA3CYl0yegsTXPOijNEdkFv_XNi0pfgWiBdpigY,594 +openai/types/static_file_chunking_strategy_object.py,sha256=Nc2Yr9SPOIZ8JtpYctLQVqTnp7TuwcbJlSN-ZmJROKE,423 +openai/types/static_file_chunking_strategy_object_param.py,sha256=akdsPsdDOtSM6Rtpj0uHO3wWhaDh8n642aF_H9XTJa4,596 +openai/types/static_file_chunking_strategy_param.py,sha256=cBO561djRVAI1lNOIN48Ke3GIc_5l-jSO1fu_PzDTTI,691 +openai/types/upload.py,sha256=KgU68PS6gJg1O_iVwnT7aJmep58TeAwzILfbK11U_6k,1370 +openai/types/upload_complete_params.py,sha256=2KIPCJwlCuavHQu64UhJkzOCLCYpWBLs6G5mDgkwteQ,526 +openai/types/upload_create_params.py,sha256=t1fQ_29-pI6SDKU4AheVmobhzOwYSEdfqim3aANlGWg,1811 +openai/types/uploads/__init__.py,sha256=vc-UlXGGTR6Yijm1u85XV0wpcZ1RvESEuM1H7wz6NZU,241 +openai/types/uploads/__pycache__/__init__.cpython-312.pyc,, +openai/types/uploads/__pycache__/part_create_params.cpython-312.pyc,, +openai/types/uploads/__pycache__/upload_part.cpython-312.pyc,, +openai/types/uploads/part_create_params.py,sha256=PxyCxAhf9gLIQMe-3z7AiDC3m5PNiNB2pOPTmPlTZjk,361 +openai/types/uploads/upload_part.py,sha256=yF7y5cJnJGpmnw1eOGZbPCge7Q41bRFifPoQ2UEFwKs,676 +openai/types/vector_store.py,sha256=2ZWqSMg7-jv1j17-3rGeLpGzaaqPywddUggRmeYXSjE,2632 +openai/types/vector_store_create_params.py,sha256=5RRD2NpuEYa5G4kXUwApL2EBPs7ESbbKOylsYelHe-A,1953 +openai/types/vector_store_deleted.py,sha256=D0t669Row_5kvzs_IfZPK7rkaItgYo1SkYCy9jFKcFI,306 +openai/types/vector_store_list_params.py,sha256=EZyRiBa-ZVH0hiBWrvCP7r2793Omv8zAGCu0cXwA-jM,1225 +openai/types/vector_store_search_params.py,sha256=092goiW7RyD0enE4ttdn4JEp5qHStkN5Z3mQJVNn-mc,1300 +openai/types/vector_store_search_response.py,sha256=-DO3kgMHSMWzLAhrwgTnQuyCtXTdnJ6ljhF0s6hj9kw,1155 +openai/types/vector_store_update_params.py,sha256=ZEALd7wpWz7iiVI73NpYErFvToZ3NJ5krt--thJDt7U,1292 +openai/types/vector_stores/__init__.py,sha256=5sxEfH6t58b8U7fOuXuvceT3RFqtsk3JpmOcnH6f4yg,817 +openai/types/vector_stores/__pycache__/__init__.cpython-312.pyc,, +openai/types/vector_stores/__pycache__/file_batch_create_params.cpython-312.pyc,, +openai/types/vector_stores/__pycache__/file_batch_list_files_params.cpython-312.pyc,, +openai/types/vector_stores/__pycache__/file_content_response.cpython-312.pyc,, +openai/types/vector_stores/__pycache__/file_create_params.cpython-312.pyc,, +openai/types/vector_stores/__pycache__/file_list_params.cpython-312.pyc,, +openai/types/vector_stores/__pycache__/file_update_params.cpython-312.pyc,, +openai/types/vector_stores/__pycache__/vector_store_file.cpython-312.pyc,, +openai/types/vector_stores/__pycache__/vector_store_file_batch.cpython-312.pyc,, +openai/types/vector_stores/__pycache__/vector_store_file_deleted.cpython-312.pyc,, +openai/types/vector_stores/file_batch_create_params.py,sha256=a308Z5GTXEs3fE6m37lTmFZVcIK34ZTvceFE16vu2jI,3255 +openai/types/vector_stores/file_batch_list_files_params.py,sha256=0nrRm_zbzFHuz6pG-vBp2RTzoR3uJztSPME9R53RgKE,1450 +openai/types/vector_stores/file_content_response.py,sha256=vBAqG1L6Vt1uWaXGMauPPVOXoqCbHWww_YxZk82_AuU,366 +openai/types/vector_stores/file_create_params.py,sha256=iSj0qiO1Qdl9U3FFOV8vY04aqsFbqRmvosFK9yUsy7U,1454 +openai/types/vector_stores/file_list_params.py,sha256=qb73CqYZ8MrKBeimyCQfEL13-_Q4F0IWdPdvWDZfcU0,1384 +openai/types/vector_stores/file_update_params.py,sha256=0TXJCwLkHJeThtgOXbEKizOWQWPRKl71llVAxhUBwjM,780 +openai/types/vector_stores/vector_store_file.py,sha256=b3xpr2qE6QQFaYqQVN09JcrXN-iRQbaihALn1bbLQlw,2437 +openai/types/vector_stores/vector_store_file_batch.py,sha256=loyQSBw0tddk0GpyYSOHge_jKg82YkCMiSe_7Z4JonQ,1518 +openai/types/vector_stores/vector_store_file_deleted.py,sha256=rtfEiIdBZcI8BZ0fWzM-DxyqnLjYETrX-XZy6YmdsUA,320 +openai/types/video.py,sha256=EXvF2ZzW9mBBzBNgoneRC3GtR9TTamTSc5nnzedoC6I,1779 +openai/types/video_create_character_params.py,sha256=q7j5ubtqL3jT9ETLncdiUPgvdOEOdQ3VnQs1IyPrMSs,458 +openai/types/video_create_character_response.py,sha256=VAYiTsh6DMMaoOjGEo72asyfg-gQoXY-zcByCUX0wz8,489 +openai/types/video_create_error.py,sha256=VY4NfWSk5CbeGlG8ah0PWOwsadysnhIfptKV1zLahmQ,1475 +openai/types/video_create_params.py,sha256=iov83H9Mt3fKL0p9K5z_OBknzlHYM0DpgcA4Nc_MV24,1239 +openai/types/video_delete_response.py,sha256=S2UjENMRvb4XQKX4zOVvUusH_BT0jrrpLAwCeLopO18,528 +openai/types/video_download_content_params.py,sha256=bQf2PfliiAqXRMkT0YoxfysFmsY-CzgF3qRru8aYndU,404 +openai/types/video_edit_params.py,sha256=LI8h7HuNcuNMVxlXwzDcO542UmIACiwcBvtVdWuZKMI,791 +openai/types/video_extend_params.py,sha256=b0axvClZVh1Vix7M_paLvFPXBRCQiakFOJiR9cwOCGs,987 +openai/types/video_get_character_response.py,sha256=VvAm3MIMATXJHZ7jYdxd1zpRIR86kRWvzyiQFAvtpoY,483 +openai/types/video_list_params.py,sha256=c2m0x5z5S9lMr2dr22xby6HiGAbj8ukAE6qmx0W2N80,549 +openai/types/video_model.py,sha256=rIq-at0X5WQtYQm7wmHzKn_0sMFlZhcxHa6eDmN6ZNs,328 +openai/types/video_model_param.py,sha256=9VsABh3vGf9yFgVbMZJNn8n3VoA-wEbN-X6VQNTL5YM,374 +openai/types/video_remix_params.py,sha256=gR4nSJtJaLYvWzFeLvgiq4c9oyTm71xDmXM4KIVJqiA,345 +openai/types/video_seconds.py,sha256=efPy66iKxxMjWFZDcJpqa5t3i37WGHtSKX0XOXuQG9U,214 +openai/types/video_size.py,sha256=pyQpAEiRxcRTaYbOxy2aCsA-svpMPXHXcu9KiNyTbX0,242 +openai/types/webhooks/__init__.py,sha256=46fdbAtZIIbOqOqLOlNRs3AtI-34XYfz96dTRlZsapg,3652 +openai/types/webhooks/__pycache__/__init__.cpython-312.pyc,, +openai/types/webhooks/__pycache__/batch_cancelled_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/batch_completed_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/batch_expired_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/batch_failed_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/deleted_webhook_endpoint.cpython-312.pyc,, +openai/types/webhooks/__pycache__/eval_run_canceled_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/eval_run_failed_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/eval_run_succeeded_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/fine_tuning_job_cancelled_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/fine_tuning_job_failed_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/fine_tuning_job_succeeded_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/live_call_incoming_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/live_transport_incoming_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/realtime_call_incoming_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/response_cancelled_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/response_completed_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/response_failed_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/response_incomplete_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/safety_alert_created_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/safety_deactivation_issued_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/safety_identifier_blocked_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/safety_org_alert_created_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/safety_warning_issued_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/unwrap_webhook_event.cpython-312.pyc,, +openai/types/webhooks/__pycache__/webhook_create_params.cpython-312.pyc,, +openai/types/webhooks/__pycache__/webhook_endpoint.cpython-312.pyc,, +openai/types/webhooks/__pycache__/webhook_endpoint_list.cpython-312.pyc,, +openai/types/webhooks/__pycache__/webhook_endpoint_test_result.cpython-312.pyc,, +openai/types/webhooks/__pycache__/webhook_endpoint_with_secret.cpython-312.pyc,, +openai/types/webhooks/__pycache__/webhook_event_type_list.cpython-312.pyc,, +openai/types/webhooks/__pycache__/webhook_list_params.cpython-312.pyc,, +openai/types/webhooks/__pycache__/webhook_rotate_secret_params.cpython-312.pyc,, +openai/types/webhooks/__pycache__/webhook_test_params.cpython-312.pyc,, +openai/types/webhooks/__pycache__/webhook_update_params.cpython-312.pyc,, +openai/types/webhooks/batch_cancelled_webhook_event.py,sha256=ui_dyydlSOFQPCriyIK39hB2SNWewR5pa3URYKnpalQ,861 +openai/types/webhooks/batch_completed_webhook_event.py,sha256=kAinGaeC4YySyqCklrfcTsjiG4XZpVbeA6fLP6MTPHw,861 +openai/types/webhooks/batch_expired_webhook_event.py,sha256=ygOYlfoqqVJg1LqTjPXF-4gZgmkHOqbtMMLyz68Og0o,840 +openai/types/webhooks/batch_failed_webhook_event.py,sha256=TTR_O4B4i8Ffb1gWScuRle2l2Rs154I_seuhK5L8h-w,834 +openai/types/webhooks/deleted_webhook_endpoint.py,sha256=AoJQtai2MS9qrCDqiiPapaa1bHrDimBv6FWmn3CwtCA,482 +openai/types/webhooks/eval_run_canceled_webhook_event.py,sha256=Q-tIU02Ac8CMSZCbeaR-zepAgp8QD8VJ7EjvLhgN3M8,839 +openai/types/webhooks/eval_run_failed_webhook_event.py,sha256=c3f4CavhZv1HJNxT8RKCnl1Yg0iGeCS3QB04t9Fzhc0,818 +openai/types/webhooks/eval_run_succeeded_webhook_event.py,sha256=Wco1v19wKA_1GFR-Hoz-ksi6RCetfbdY3nZ-RUd8SDY,836 +openai/types/webhooks/fine_tuning_job_cancelled_webhook_event.py,sha256=8f1EyCr8PZy-VRQXBRevad_3-6FHh0cu7CRZqwnxsjA,891 +openai/types/webhooks/fine_tuning_job_failed_webhook_event.py,sha256=kQ1Yjz2pOHW87i7lLEsY-Z4dp4-4qvSUNym3W1CAlvY,864 +openai/types/webhooks/fine_tuning_job_succeeded_webhook_event.py,sha256=iXN9wvXcVcMEtdfkGj-xkdimXX4NljQvvmHw7xYX48o,882 +openai/types/webhooks/live_call_incoming_webhook_event.py,sha256=LdO8cKnfHxLlMzmDlG4d8Demn3Es4QTHUyR2gDTfXCQ,2328 +openai/types/webhooks/live_transport_incoming_webhook_event.py,sha256=_ZURtOXaPkHBHqP4QLbKBbtleT5bSysfDxCBHHp0PiE,2088 +openai/types/webhooks/realtime_call_incoming_webhook_event.py,sha256=4QQLTN9qEC2Mzjaq70aeYOsIuQpqXXaxIqtWehuioDM,2088 +openai/types/webhooks/response_cancelled_webhook_event.py,sha256=B2kM_rCeorS6kM2k0LsiT4NJJgTjisL78azoRIpf3QQ,869 +openai/types/webhooks/response_completed_webhook_event.py,sha256=CUuso3nqhc8NCcgMoxCoEp2fWRPtIcNOW5UKRgp5_lo,869 +openai/types/webhooks/response_failed_webhook_event.py,sha256=S10NsLeIr88On1ZKsCrkdJH7TNnniDGsb553hxmnFdQ,842 +openai/types/webhooks/response_incomplete_webhook_event.py,sha256=3KG8WOKz--u0PNwM2IZeH17Xr0K1va9kwt8BC_KroQQ,877 +openai/types/webhooks/safety_alert_created_webhook_event.py,sha256=76P6p7LRNUbn575tvK43ROI429GpI6EvEO99suZNQaw,753 +openai/types/webhooks/safety_deactivation_issued_webhook_event.py,sha256=6Y_9rzXhkfX1pNSqUF6b07JwpkqA0eMeAX5zerOleso,798 +openai/types/webhooks/safety_identifier_blocked_webhook_event.py,sha256=4RfCjsic85cNFrDlAQzfxov3u8WCzVjtgT7FNOb7rb4,1291 +openai/types/webhooks/safety_org_alert_created_webhook_event.py,sha256=eI8zm6h5rp3-r2fMgrTJi9mBY24EyqNhVG_xmFWHoB8,776 +openai/types/webhooks/safety_warning_issued_webhook_event.py,sha256=X02rHBa0Fj3wfn6wX5It8gIoOxlCt7LMigl32wyAxgk,763 +openai/types/webhooks/unwrap_webhook_event.py,sha256=SJrejcI7PJ1apaKCPsGackg5vYXg9rdT1v7-QxEUQo4,2896 +openai/types/webhooks/webhook_create_params.py,sha256=rgjXAfkEIkvbTFVM-PwzzivGXRIwk4h4WgZsOh0NdeM,1294 +openai/types/webhooks/webhook_endpoint.py,sha256=WvV6hNeXPa5Hizf_8CcA5LRu9ADl6I71Ez1eoCJZoGE,1053 +openai/types/webhooks/webhook_endpoint_list.py,sha256=B3T-v0fvcRU61_9HorhsvRMuyO0wplciInfW9JmZIdA,721 +openai/types/webhooks/webhook_endpoint_test_result.py,sha256=0RWLTMfo8yKeMuSSv3Ww_onqoaVotWTJVsd-CTat-NA,768 +openai/types/webhooks/webhook_endpoint_with_secret.py,sha256=VD7GI7gNNBFd6-IFUDaR_auii57FUvw4SxnSBZk8_Es,1274 +openai/types/webhooks/webhook_event_type_list.py,sha256=37CC7OE3vAlrrK_Y-zxZ9AH-qnHsDDu1eNDlym4LosY,431 +openai/types/webhooks/webhook_list_params.py,sha256=zFVTqRBO1q8zoVrskGstzeVN66b4UDdtfYXtt8u1Rzk,454 +openai/types/webhooks/webhook_rotate_secret_params.py,sha256=yGVlyk9a1ISBhDtzvwHjh4B6Ov0afwoL7gcE-uiLTMc,477 +openai/types/webhooks/webhook_test_params.py,sha256=FBZaLVxeQXtQgp_7vgh-yTGfv25bAQ7Tmnnv5N5t8oA,983 +openai/types/webhooks/webhook_update_params.py,sha256=Ya6xwkFyqtKHB2btX4Lslv1uW0IHlmTy39vUg4omdbQ,1168 +openai/types/websocket_connection_options.py,sha256=OZ_4rw2uWlhfd-Uw8BhycRJGA71eIsCTitJiw1XR3H8,2040 +openai/types/websocket_reconnection.py,sha256=Ax3RLj1VuYZa231X2mijATZCJ645sB0p1pZhHfQFoT0,1792 +openai/version.py,sha256=cjbXKO8Ut3aiv4YlQnugff7AdC48MpSndcx96q88Yb8,62 diff --git a/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/REQUESTED b/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/WHEEL new file mode 100644 index 0000000..12228d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..cbb5bb2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai-3.20.0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 OpenAI + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/openai/__init__.py b/venv/lib/python3.12/site-packages/openai/__init__.py new file mode 100644 index 0000000..e4ee6ad --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/__init__.py @@ -0,0 +1,488 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os as _os +import typing as _t +from typing_extensions import override + +from . import types +from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given +from ._utils import file_from_path +from ._client import Client, OpenAI, Stream, Timeout, Transport, AsyncClient, AsyncOpenAI, AsyncStream, RequestOptions +from ._httpx2 import DefaultHttpx2Client, DefaultAsyncHttpx2Client, normalize_httpx_url as _normalize_httpx_url +from ._models import BaseModel +from ._version import __title__, __version__ +from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse +from ._constants import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_CONNECTION_LIMITS +from ._exceptions import ( + APIError, + OAuthError, + OpenAIError, + ConflictError, + NotFoundError, + APIStatusError, + RateLimitError, + APITimeoutError, + BadRequestError, + APIConnectionError, + AuthenticationError, + InternalServerError, + PermissionDeniedError, + LengthFinishReasonError, + WebSocketQueueFullError, + UnprocessableEntityError, + APIResponseValidationError, + InvalidWebhookSignatureError, + ContentFilterFinishReasonError, + WebSocketConnectionClosedError, +) +from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient +from ._utils._logs import setup_logging as _setup_logging +from ._data_residency import DataResidency +from ._legacy_response import HttpxBinaryResponseContent as HttpxBinaryResponseContent +from .types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides + +__all__ = [ + "types", + "__version__", + "__title__", + "NoneType", + "Transport", + "ProxiesTypes", + "NotGiven", + "NOT_GIVEN", + "not_given", + "Omit", + "omit", + "OpenAIError", + "APIError", + "APIStatusError", + "APITimeoutError", + "APIConnectionError", + "APIResponseValidationError", + "BadRequestError", + "AuthenticationError", + "OAuthError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", + "LengthFinishReasonError", + "ContentFilterFinishReasonError", + "InvalidWebhookSignatureError", + "Timeout", + "RequestOptions", + "DataResidency", + "Client", + "AsyncClient", + "Stream", + "AsyncStream", + "OpenAI", + "AsyncOpenAI", + "BedrockOpenAI", + "AsyncBedrockOpenAI", + "file_from_path", + "BaseModel", + "DEFAULT_TIMEOUT", + "DEFAULT_MAX_RETRIES", + "DEFAULT_CONNECTION_LIMITS", + "DefaultHttpxClient", + "DefaultAsyncHttpxClient", + "DefaultAioHttpClient", + "DefaultHttpx2Client", + "DefaultAsyncHttpx2Client", + "ReconnectingEvent", + "ReconnectingOverrides", + "WebSocketQueueFullError", + "WebSocketConnectionClosedError", +] + +if not _t.TYPE_CHECKING: + from ._utils._resources_proxy import resources as resources + +from .lib import azure as _azure, bedrock as _bedrock, pydantic_function_tool as pydantic_function_tool +from .version import VERSION as VERSION +from .lib.azure import AzureOpenAI as AzureOpenAI, AsyncAzureOpenAI as AsyncAzureOpenAI +from .lib.bedrock import BedrockOpenAI as BedrockOpenAI, AsyncBedrockOpenAI as AsyncBedrockOpenAI +from .lib._old_api import * + +if _t.TYPE_CHECKING: + from .lib.streaming import ( + AssistantEventHandler as AssistantEventHandler, + AsyncAssistantEventHandler as AsyncAssistantEventHandler, + ) +else: + _STREAMING_EXPORTS = ("AssistantEventHandler", "AsyncAssistantEventHandler") + + def __getattr__(name: str) -> _t.Any: + if name in _STREAMING_EXPORTS: + from importlib import import_module + + value = getattr(import_module(".lib.streaming", __name__), name) + globals()[name] = value + return value + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_STREAMING_EXPORTS)) + + +_setup_logging() + +# Update the __module__ attribute for exported symbols so that +# error messages point to this module instead of the module +# it was originally defined in, e.g. +# openai._exceptions.NotFoundError -> openai.NotFoundError +__locals = locals() +for __name in __all__: + if not __name.startswith("__"): + try: + __locals[__name].__module__ = "openai" + except (TypeError, AttributeError): + # Some of our exported symbols are builtins which we can't set attributes for. + pass + +# ------ Module level client ------ +import typing as _t +import typing_extensions as _te + +import httpx2 as _httpx + +from ._base_client import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES + +api_key: str | None = None + +admin_api_key: str | None = None + +organization: str | None = None + +project: str | None = None + +webhook_secret: str | None = None + +base_url: str | _httpx.URL | None = None + +timeout: float | Timeout | None = DEFAULT_TIMEOUT + +max_retries: int = DEFAULT_MAX_RETRIES + +default_headers: _t.Mapping[str, str] | None = None + +default_query: _t.Mapping[str, object] | None = None + +http_client: _httpx.Client | None = None + +_ApiType = _te.Literal["openai", "azure", "amazon-bedrock"] + +api_type: _ApiType | None = _t.cast(_ApiType, _os.environ.get("OPENAI_API_TYPE")) + +api_version: str | None = _os.environ.get("OPENAI_API_VERSION") + +azure_endpoint: str | None = _os.environ.get("AZURE_OPENAI_ENDPOINT") + +# Keep explicit module configuration separate from Azure's environment fallback. +azure_ad_token: str | None = None + +azure_ad_token_provider: _azure.AzureADTokenProvider | None = None + +_bedrock_api_key: str | None = None + +bedrock_token_provider: _bedrock.BedrockTokenProvider | None = None + + +class _ModuleClient(OpenAI): + # Note: we have to use type: ignores here as overriding class members + # with properties is technically unsafe but it is fine for our use case + + @property # type: ignore + @override + def api_key(self) -> str | None: + return api_key + + @api_key.setter # type: ignore + def api_key(self, value: str | None) -> None: # type: ignore + global api_key + + api_key = value + + @property # type: ignore + @override + def admin_api_key(self) -> str | None: + return admin_api_key + + @admin_api_key.setter # type: ignore + def admin_api_key(self, value: str | None) -> None: # type: ignore + global admin_api_key + + admin_api_key = value + + @property # type: ignore + @override + def organization(self) -> str | None: + return organization + + @organization.setter # type: ignore + def organization(self, value: str | None) -> None: # type: ignore + global organization + + organization = value + + @property # type: ignore + @override + def project(self) -> str | None: + return project + + @project.setter # type: ignore + def project(self, value: str | None) -> None: # type: ignore + global project + + project = value + + @property # type: ignore + @override + def webhook_secret(self) -> str | None: + return webhook_secret + + @webhook_secret.setter # type: ignore + def webhook_secret(self, value: str | None) -> None: # type: ignore + global webhook_secret + + webhook_secret = value + + @property + @override + def base_url(self) -> _httpx.URL: + if base_url is not None: + return _normalize_httpx_url(base_url) + + return super().base_url + + @base_url.setter + def base_url(self, url: _httpx.URL | str) -> None: + super().base_url = url # type: ignore[misc] + + @property # type: ignore + @override + def timeout(self) -> float | Timeout | None: + return timeout + + @timeout.setter # type: ignore + def timeout(self, value: float | Timeout | None) -> None: # type: ignore + global timeout + + timeout = value + + @property # type: ignore + @override + def max_retries(self) -> int: + return max_retries + + @max_retries.setter # type: ignore + def max_retries(self, value: int) -> None: # type: ignore + global max_retries + + max_retries = value + + @property # type: ignore + @override + def _custom_headers(self) -> _t.Mapping[str, str] | None: + return default_headers + + @_custom_headers.setter # type: ignore + def _custom_headers(self, value: _t.Mapping[str, str] | None) -> None: # type: ignore + global default_headers + + default_headers = value + + @property # type: ignore + @override + def _custom_query(self) -> _t.Mapping[str, object] | None: + return default_query + + @_custom_query.setter # type: ignore + def _custom_query(self, value: _t.Mapping[str, object] | None) -> None: # type: ignore + global default_query + + default_query = value + + @property # type: ignore + @override + def _client(self) -> _httpx.Client: + return http_client or super()._client + + @_client.setter # type: ignore + def _client(self, value: _httpx.Client) -> None: # type: ignore + global http_client + + http_client = value + + +class _AzureModuleClient(_ModuleClient, AzureOpenAI): # type: ignore + ... + + +class _BedrockModuleClient(_ModuleClient, BedrockOpenAI): # type: ignore + @property # type: ignore + @override + def api_key(self) -> str | None: + return api_key if api_key is not None else _bedrock_api_key + + @api_key.setter # type: ignore + def api_key(self, value: str | None) -> None: # type: ignore + global _bedrock_api_key + + _bedrock_api_key = value + + @override + def _refresh_api_key(self) -> str: + if api_key is not None: + return api_key + + return super()._refresh_api_key() + + @override + def _legacy_auth_configuration(self) -> _bedrock._LegacyAuthConfiguration: + if api_key is not None: + return ("bearer", api_key) + return super()._legacy_auth_configuration() + + +class _AmbiguousModuleClientUsageError(OpenAIError): + def __init__(self) -> None: + super().__init__( + "Ambiguous use of module client; please set `openai.api_type` or the `OPENAI_API_TYPE` environment variable to `openai`, `azure`, or `amazon-bedrock`" + ) + + +def _has_openai_credentials() -> bool: + return _os.environ.get("OPENAI_API_KEY") is not None + + +def _has_azure_credentials() -> bool: + return azure_endpoint is not None or _os.environ.get("AZURE_OPENAI_API_KEY") is not None + + +def _has_azure_ad_credentials() -> bool: + return ( + _os.environ.get("AZURE_OPENAI_AD_TOKEN") is not None + or azure_ad_token is not None + or azure_ad_token_provider is not None + ) + + +_client: OpenAI | None = None + + +def _load_client() -> OpenAI: # type: ignore[reportUnusedFunction] + global _client + + if _client is None: + global api_type, azure_endpoint, api_version + + if azure_endpoint is None: + azure_endpoint = _os.environ.get("AZURE_OPENAI_ENDPOINT") + + if api_version is None: + api_version = _os.environ.get("OPENAI_API_VERSION") + + if api_type is None: + has_openai = _has_openai_credentials() + has_azure = _has_azure_credentials() + has_azure_ad = _has_azure_ad_credentials() + + if has_openai and (has_azure or has_azure_ad): + raise _AmbiguousModuleClientUsageError() + + if has_azure or has_azure_ad: + api_type = "azure" + else: + api_type = "openai" + + if api_type == "azure": + _client = _AzureModuleClient( # type: ignore + api_version=api_version, + azure_endpoint=azure_endpoint, + api_key=api_key, + azure_ad_token=azure_ad_token, + azure_ad_token_provider=azure_ad_token_provider, + organization=organization, + base_url=base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + ) + return _client + + if api_type == "amazon-bedrock": + _client = _BedrockModuleClient( # type: ignore + api_key=api_key, + bedrock_token_provider=bedrock_token_provider, + organization=organization, + project=project, + webhook_secret=webhook_secret, + base_url=base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + ) + return _client + + _client = _ModuleClient( + api_key=api_key, + admin_api_key=admin_api_key, + organization=organization, + project=project, + webhook_secret=webhook_secret, + base_url=base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + _enforce_credentials=False, + ) + return _client + + return _client + + +def _reset_client() -> None: # type: ignore[reportUnusedFunction] + global _client + + _client = None + + +from ._module_client import ( + beta as beta, + chat as chat, + live as live, + admin as admin, + audio as audio, + evals as evals, + files as files, + images as images, + models as models, + safety as safety, + skills as skills, + videos as videos, + batches as batches, + uploads as uploads, + realtime as realtime, + webhooks as webhooks, + responses as responses, + containers as containers, + embeddings as embeddings, + completions as completions, + fine_tuning as fine_tuning, + moderations as moderations, + conversations as conversations, + vector_stores as vector_stores, + content_provenance_checks as content_provenance_checks, +) diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2d266ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_base_client.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_base_client.cpython-312.pyc new file mode 100644 index 0000000..e8a6710 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_base_client.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_client.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_client.cpython-312.pyc new file mode 100644 index 0000000..ae0a3da Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_client.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_compat.cpython-312.pyc new file mode 100644 index 0000000..7628773 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_compat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_constants.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_constants.cpython-312.pyc new file mode 100644 index 0000000..0ea8900 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_constants.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_data_residency.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_data_residency.cpython-312.pyc new file mode 100644 index 0000000..6867861 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_data_residency.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_event_handler.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_event_handler.cpython-312.pyc new file mode 100644 index 0000000..10af4cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_event_handler.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_exceptions.cpython-312.pyc new file mode 100644 index 0000000..956e261 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_files.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_files.cpython-312.pyc new file mode 100644 index 0000000..d33a268 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_files.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_httpx2.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_httpx2.cpython-312.pyc new file mode 100644 index 0000000..b37bce6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_httpx2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_legacy_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_legacy_response.cpython-312.pyc new file mode 100644 index 0000000..b42f3c3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_legacy_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_models.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_models.cpython-312.pyc new file mode 100644 index 0000000..c10ba79 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_models.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_module_client.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_module_client.cpython-312.pyc new file mode 100644 index 0000000..fa0d8e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_module_client.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_multipart.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_multipart.cpython-312.pyc new file mode 100644 index 0000000..d9102db Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_multipart.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_provider.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_provider.cpython-312.pyc new file mode 100644 index 0000000..08c4a49 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_provider.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_qs.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_qs.cpython-312.pyc new file mode 100644 index 0000000..f7bb76e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_qs.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_resource.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_resource.cpython-312.pyc new file mode 100644 index 0000000..02d801f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_resource.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_response.cpython-312.pyc new file mode 100644 index 0000000..841caa2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_send_queue.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_send_queue.cpython-312.pyc new file mode 100644 index 0000000..b01755e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_send_queue.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_streaming.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_streaming.cpython-312.pyc new file mode 100644 index 0000000..fabf421 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_streaming.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_types.cpython-312.pyc new file mode 100644 index 0000000..72ce882 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/_version.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/_version.cpython-312.pyc new file mode 100644 index 0000000..349bfb5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/_version.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/pagination.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/pagination.cpython-312.pyc new file mode 100644 index 0000000..cc4a9fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/pagination.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/__pycache__/version.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/__pycache__/version.cpython-312.pyc new file mode 100644 index 0000000..b34be44 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/__pycache__/version.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_base_client.py b/venv/lib/python3.12/site-packages/openai/_base_client.py new file mode 100644 index 0000000..883bed6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_base_client.py @@ -0,0 +1,2359 @@ +from __future__ import annotations + +import sys +import json +import math +import time +import uuid +import email +import asyncio +import inspect +import logging +import platform +import warnings +import email.utils +from types import TracebackType +from random import random +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Type, + Union, + Generic, + Mapping, + TypeVar, + Iterable, + Iterator, + Optional, + Generator, + AsyncIterator, + cast, + overload, +) +from typing_extensions import Unpack, Literal, override, get_origin + +import anyio +import httpx2 +import pydantic +from httpx2 import URL +from pydantic import PrivateAttr + +from . import _exceptions +from ._qs import Querystring +from ._files import to_httpx_files, async_to_httpx_files +from ._types import ( + Body, + Omit, + Query, + Headers, + Timeout, + NotGiven, + ResponseT, + AnyMapping, + PostParser, + BinaryTypes, + RequestFiles, + HttpxSendArgs, + RequestOptions, + AsyncBinaryTypes, + HttpxRequestFiles, + ModelBuilderProtocol, + not_given, +) +from ._utils import SensitiveHeadersFilter, is_dict, is_list, asyncify, is_given, lru_cache, is_mapping +from ._compat import PYDANTIC_V1, model_copy +from ._httpx2 import ( + status_exceptions, + request_exceptions, + timeout_exceptions, + http_response_types, + normalize_httpx_url, + is_httpx2_sync_client, + normalize_httpx2_auth, + is_httpx2_async_client, + normalize_httpx_timeout, + normalize_httpx2_timeout, + is_legacy_httpx_sync_client, + normalize_legacy_httpx_auth, + is_legacy_httpx_async_client, + normalize_legacy_httpx_timeout, +) +from ._models import GenericModel, SecurityOptions, FinalRequestOptions, validate_type, construct_type +from ._response import ( + APIResponse, + BaseAPIResponse, + AsyncAPIResponse, + extract_response_type, +) +from ._constants import ( + DEFAULT_TIMEOUT, + MAX_RETRY_DELAY, + DEFAULT_MAX_RETRIES, + INITIAL_RETRY_DELAY, + RAW_RESPONSE_HEADER, + MAX_RETRY_AFTER_DELAY, + OVERRIDE_CAST_TO_HEADER, + DEFAULT_CONNECTION_LIMITS, +) +from ._streaming import Stream, SSEDecoder, AsyncStream, SSEBytesDecoder +from ._exceptions import ( + OpenAIError, + APIStatusError, + APITimeoutError, + APIConnectionError, + APIResponseValidationError, +) +from ._utils._json import openapi_dumps +from ._utils._logs import get_http_method_for_logging +from ._legacy_response import LegacyAPIResponse + +log: logging.Logger = logging.getLogger(__name__) +log.addFilter(SensitiveHeadersFilter()) + + +class _RequestBodyReplay: + def __init__(self, options: FinalRequestOptions) -> None: + self._positions: list[tuple[object, int]] = [] + self._replayable = all( + self._capture(content) for content in (options.content, *_iter_file_contents(options.files)) + ) + + def _capture(self, content: object) -> bool: + if content is None or isinstance(content, (bytes, bytearray)): + return True + + if not callable(getattr(content, "read", None)): + # Iterable protocols do not guarantee a fresh iterator: a wrapper can + # return the same stored generator. Only trust known concrete containers. + return type(content) in (list, tuple) + + seekable = getattr(content, "seekable", None) + tell = getattr(content, "tell", None) + seek = getattr(content, "seek", None) + if not callable(seekable) or not callable(tell) or not callable(seek): + return False + try: + if not seekable(): + return False + position = tell() + except (OSError, ValueError): + return False + if not isinstance(position, int): + return False + self._positions.append((content, position)) + return True + + def rewind(self) -> bool: + # Reject a non-replayable body before moving any of its streams. + if not self._replayable: + return False + for content, position in self._positions: + seek = getattr(content, "seek", None) + if not callable(seek): + return False + try: + seek(position) + except (OSError, ValueError): + return False + return True + + +def _iter_file_contents(files: HttpxRequestFiles | None) -> Iterator[object]: + if files is None: + return + + entries = files.items() if isinstance(files, Mapping) else files + for _, file in entries: + if isinstance(file, tuple) and len(file) > 1: + yield file[1] + else: + yield file + + +# TODO: make base page type vars covariant +SyncPageT = TypeVar("SyncPageT", bound="BaseSyncPage[Any]") +AsyncPageT = TypeVar("AsyncPageT", bound="BaseAsyncPage[Any]") + + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) + +_StreamT = TypeVar("_StreamT", bound=Stream[Any]) +_AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any]) + +if TYPE_CHECKING: + from httpx2._config import ( + DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage] + ) + + HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG +else: + try: + from httpx2._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT + except ImportError: + # taken from https://github.com/encode/httpx/blob/3ba5fe0d7ac70222590e759c31442b1cab263791/httpx/_config.py#L366 + HTTPX_DEFAULT_TIMEOUT = Timeout(5.0) + + +class PageInfo: + """Stores the necessary information to build the request to retrieve the next page. + + Either `url` or `params` must be set. + """ + + url: URL | NotGiven + params: Query | NotGiven + json: Body | NotGiven + + @overload + def __init__( + self, + *, + url: URL, + ) -> None: ... + + @overload + def __init__( + self, + *, + params: Query, + ) -> None: ... + + @overload + def __init__( + self, + *, + json: Body, + ) -> None: ... + + def __init__( + self, + *, + url: URL | NotGiven = not_given, + json: Body | NotGiven = not_given, + params: Query | NotGiven = not_given, + ) -> None: + self.url = url + self.json = json + self.params = params + + @override + def __repr__(self) -> str: + if self.url: + return f"{self.__class__.__name__}(url={self.url})" + if self.json: + return f"{self.__class__.__name__}(json={self.json})" + return f"{self.__class__.__name__}(params={self.params})" + + +class BasePage(GenericModel, Generic[_T]): + """ + Defines the core interface for pagination. + + Type Args: + ModelT: The pydantic model that represents an item in the response. + + Methods: + has_next_page(): Check if there is another page available + next_page_info(): Get the necessary information to make a request for the next page + """ + + _options: FinalRequestOptions = PrivateAttr() + _model: Type[_T] = PrivateAttr() + + def has_next_page(self) -> bool: + items = self._get_page_items() + if not items: + return False + return self.next_page_info() is not None + + def next_page_info(self) -> Optional[PageInfo]: ... + + def _get_page_items(self) -> Iterable[_T]: # type: ignore[empty-body] + ... + + def _params_from_url(self, url: URL) -> httpx2.QueryParams: + # TODO: do we have to preprocess params here? + return httpx2.QueryParams(cast(Any, self._options.params)).merge(url.params) + + def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: + options = model_copy(self._options) + options._strip_raw_response_header() + + if not isinstance(info.params, NotGiven): + options.params = {**options.params, **info.params} + return options + + if not isinstance(info.url, NotGiven): + params = self._params_from_url(info.url) + url = info.url.copy_with(params=params) + options.params = dict(url.params) + options.url = str(url) + return options + + if not isinstance(info.json, NotGiven): + if not is_mapping(info.json): + raise TypeError("Pagination is only supported with mappings") + + if not options.json_data: + options.json_data = {**info.json} + else: + if not is_mapping(options.json_data): + raise TypeError("Pagination is only supported with mappings") + + options.json_data = {**options.json_data, **info.json} + return options + + raise ValueError("Unexpected PageInfo state") + + +class BaseSyncPage(BasePage[_T], Generic[_T]): + _client: SyncAPIClient = pydantic.PrivateAttr() + + def _set_private_attributes( + self, + client: SyncAPIClient, + model: Type[_T], + options: FinalRequestOptions, + ) -> None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + + self._model = model + self._client = client + self._options = options + + # Pydantic uses a custom `__iter__` method to support casting BaseModels + # to dictionaries. e.g. dict(model). + # As we want to support `for item in page`, this is inherently incompatible + # with the default pydantic behaviour. It is not possible to support both + # use cases at once. Fortunately, this is not a big deal as all other pydantic + # methods should continue to work as expected as there is an alternative method + # to cast a model to a dictionary, model.dict(), which is used internally + # by pydantic. + def __iter__(self) -> Iterator[_T]: # type: ignore + for page in self.iter_pages(): + for item in page._get_page_items(): + yield item + + def iter_pages(self: SyncPageT) -> Iterator[SyncPageT]: + page = self + while True: + yield page + if page.has_next_page(): + page = page.get_next_page() + else: + return + + def get_next_page(self: SyncPageT) -> SyncPageT: + info = self.next_page_info() + if not info: + raise RuntimeError( + "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." + ) + + options = self._info_to_options(info) + return self._client._request_api_list(self._model, page=self.__class__, options=options) + + +class AsyncPaginator(Generic[_T, AsyncPageT]): + def __init__( + self, + client: AsyncAPIClient, + options: FinalRequestOptions, + page_cls: Type[AsyncPageT], + model: Type[_T], + ) -> None: + self._model = model + self._client = client + self._options = options + self._page_cls = page_cls + + def __await__(self) -> Generator[Any, None, AsyncPageT]: + return self._get_page().__await__() + + async def _get_page(self) -> AsyncPageT: + def _parser(resp: AsyncPageT) -> AsyncPageT: + resp._set_private_attributes( + model=self._model, + options=self._options, + client=self._client, + ) + return resp + + self._options.post_parser = _parser + + return await self._client.request(self._page_cls, self._options) + + async def __aiter__(self) -> AsyncIterator[_T]: + # https://github.com/microsoft/pyright/issues/3464 + page = cast( + AsyncPageT, + await self, # type: ignore + ) + async for item in page: + yield item + + +class BaseAsyncPage(BasePage[_T], Generic[_T]): + _client: AsyncAPIClient = pydantic.PrivateAttr() + + def _set_private_attributes( + self, + model: Type[_T], + client: AsyncAPIClient, + options: FinalRequestOptions, + ) -> None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + + self._model = model + self._client = client + self._options = options + + async def __aiter__(self) -> AsyncIterator[_T]: + async for page in self.iter_pages(): + for item in page._get_page_items(): + yield item + + async def iter_pages(self: AsyncPageT) -> AsyncIterator[AsyncPageT]: + page = self + while True: + yield page + if page.has_next_page(): + page = await page.get_next_page() + else: + return + + async def get_next_page(self: AsyncPageT) -> AsyncPageT: + info = self.next_page_info() + if not info: + raise RuntimeError( + "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." + ) + + options = self._info_to_options(info) + return await self._client._request_api_list(self._model, page=self.__class__, options=options) + + +_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx2.Client, httpx2.AsyncClient]) +_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) + + +class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]): + _client: _HttpxClientT + _version: str + _base_url: URL + max_retries: int + timeout: Union[float, Timeout, None] + _strict_response_validation: bool + _idempotency_header: str | None + _default_stream_cls: type[_DefaultStreamT] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + _strict_response_validation: bool, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None = DEFAULT_TIMEOUT, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + ) -> None: + self._version = version + self._base_url = self._enforce_trailing_slash(normalize_httpx_url(base_url)) + self.max_retries = max_retries + self.timeout = timeout + self._custom_headers = custom_headers or {} + self._custom_query = custom_query or {} + self._strict_response_validation = _strict_response_validation + self._idempotency_header = None + self._platform: Platform | None = None + + self._validate_max_retries(max_retries) + + def _enforce_trailing_slash(self, url: URL) -> URL: + path, separator, query = url.raw_path.partition(b"?") + if path.endswith(b"/"): + return url + return url.copy_with(raw_path=path + b"/" + separator + query) + + def _make_status_error_from_response( + self, + response: httpx2.Response, + ) -> APIStatusError: + if response.is_closed and not response.is_stream_consumed: + # We can't read the response body as it has been closed + # before it was read. This can happen if an event hook + # raises a status error. + body = None + err_msg = f"Error code: {response.status_code}" + else: + err_text = response.text.strip() + body = err_text + + try: + body = json.loads(err_text) + err_msg = f"Error code: {response.status_code} - {body}" + except Exception: + err_msg = err_text or f"Error code: {response.status_code}" + + return self._make_status_error(err_msg, body=body, response=response) + + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx2.Response, + ) -> _exceptions.APIStatusError: + raise NotImplementedError() + + def _auth_headers( + self, + security: SecurityOptions, # noqa: ARG002 + ) -> dict[str, str]: + return {} + + def _auth_query( + self, + security: SecurityOptions, # noqa: ARG002 + ) -> dict[str, str]: + return {} + + def _custom_auth( + self, + security: SecurityOptions, # noqa: ARG002 + ) -> httpx2.Auth | None: + return None + + def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx2.Headers: + custom_headers = options.headers or {} + headers_dict = _merge_headers(self._auth_headers(options.security), self.default_headers, custom_headers) + self._validate_headers(headers_dict, custom_headers) + + # headers are case-insensitive while dictionaries are not. + headers = httpx2.Headers(headers_dict) + + idempotency_header = self._idempotency_header + if idempotency_header and options.idempotency_key and idempotency_header not in headers: + headers[idempotency_header] = options.idempotency_key + + # Don't set these headers if they were already set or removed by the caller. We check + # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. + lower_custom_headers = [header.lower() for header in custom_headers] + if "x-stainless-retry-count" not in lower_custom_headers: + headers["x-stainless-retry-count"] = str(retries_taken) + if "x-stainless-read-timeout" not in lower_custom_headers: + timeout = normalize_httpx_timeout( + self.timeout if isinstance(options.timeout, NotGiven) else options.timeout + ) + if isinstance(timeout, Timeout): + timeout = timeout.read + if timeout is not None: + headers["x-stainless-read-timeout"] = str(timeout) + + return headers + + def _prepare_url(self, url: str) -> URL: + """ + Merge a URL argument together with any 'base_url' on the client, + to create the URL used for the outgoing request. + """ + # Copied from httpx's `_merge_url` method. + merge_url = URL(url) + if merge_url.is_relative_url: + merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/") + return self.base_url.copy_with(raw_path=merge_raw_path) + + return merge_url + + def _make_sse_decoder(self) -> SSEDecoder | SSEBytesDecoder: + return SSEDecoder() + + def _build_request( + self, + options: FinalRequestOptions, + *, + retries_taken: int = 0, + ) -> httpx2.Request: + # Request bodies, files, URLs, and custom options can contain private data. + log.debug( + "Building HTTP request: method=%s retries_taken=%i", + get_http_method_for_logging(options.method), + retries_taken, + ) + kwargs: dict[str, Any] = {} + + json_data = options.json_data + if options.extra_json is not None: + if json_data is None: + json_data = cast(Body, options.extra_json) + elif is_mapping(json_data): + json_data = _merge_mappings(json_data, options.extra_json) + else: + raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") + + headers = self._build_headers(options, retries_taken=retries_taken) + params = _merge_mappings({**self._auth_query(options.security), **self.default_query}, options.params) + content_type = headers.get("Content-Type") + files = options.files + + # If the given Content-Type header is multipart/form-data then it + # has to be removed so that httpx can generate the header with + # additional information for us as it has to be in this form + # for the server to be able to correctly parse the request: + # multipart/form-data; boundary=---abc-- + if content_type is not None and content_type.startswith("multipart/form-data"): + if "boundary" not in content_type: + # only remove the header if the boundary hasn't been explicitly set + # as the caller doesn't want httpx to come up with their own boundary + headers.pop("Content-Type") + + # As we are now sending multipart/form-data instead of application/json + # we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding + if json_data: + if not is_dict(json_data): + raise TypeError( + f"Expected query input to be a dictionary for multipart requests but got {type(json_data)} instead." + ) + kwargs["data"] = self._serialize_multipartform(json_data) + + # httpx determines whether or not to send a "multipart/form-data" + # request based on the truthiness of the "files" argument. + # This gets around that issue by generating a dict value that + # evaluates to true. + # + # https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186 + if not files: + files = cast(HttpxRequestFiles, ForceMultipartDict()) + + prepared_url = self._prepare_url(options.url) + # preserve hard-coded query params from the url + if params and prepared_url.query: + params = {**dict(prepared_url.params.items()), **params} + prepared_url = prepared_url.copy_with(raw_path=prepared_url.raw_path.split(b"?", 1)[0]) + + is_body_allowed = options.method.lower() != "get" + + if is_body_allowed: + if options.content is not None and json_data is not None: + raise TypeError("Passing both `content` and `json_data` is not supported") + if options.content is not None and files is not None: + raise TypeError("Passing both `content` and `files` is not supported") + if options.content is not None: + kwargs["content"] = options.content + elif isinstance(json_data, bytes): + kwargs["content"] = json_data + elif not files: + # Don't set content when JSON is sent as multipart/form-data, + # since httpx's content param overrides other body arguments + kwargs["content"] = openapi_dumps(json_data) if is_given(json_data) and json_data is not None else None + kwargs["files"] = files + else: + headers.pop("Content-Type", None) + kwargs.pop("data", None) + + timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout + request_url = str(prepared_url) + request_headers = list(headers.multi_items()) + if is_legacy_httpx_sync_client(self._client) or is_legacy_httpx_async_client(self._client): + timeout = normalize_legacy_httpx_timeout(timeout) + else: + timeout = normalize_httpx2_timeout(timeout) + + # TODO: report this error to httpx + return self._client.build_request( # pyright: ignore[reportUnknownMemberType] + headers=request_headers, + timeout=timeout, + method=options.method, + url=request_url, + # the `Query` type that we use is incompatible with qs' + # `Params` type as it needs to be typed as `Mapping[str, object]` + # so that passing a `TypedDict` doesn't cause an error. + # https://github.com/microsoft/pyright/issues/3526#event-6715453066 + params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, + **kwargs, + ) + + def _serialize_multipartform(self, data: Mapping[object, object]) -> dict[str, object]: + items = self.qs.stringify_items( + # TODO: type ignore is required as stringify_items is well typed but we can't be + # well typed without heavy validation. + data, # type: ignore + array_format="brackets", + ) + serialized: dict[str, object] = {} + for key, value in items: + existing = serialized.get(key) + + if not existing: + serialized[key] = value + continue + + # If a value has already been set for this key then that + # means we're sending data like `array[]=[1, 2, 3]` and we + # need to tell httpx that we want to send multiple values with + # the same key which is done by using a list or a tuple. + # + # Note: 2d arrays should never result in the same key at both + # levels so it's safe to assume that if the value is a list, + # it was because we changed it to be a list. + if is_list(existing): + existing.append(value) + else: + serialized[key] = [existing, value] + + return serialized + + def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalRequestOptions) -> type[ResponseT]: + if not is_given(options.headers): + return cast_to + + # make a copy of the headers so we don't mutate user-input + headers = dict(options.headers) + + # we internally support defining a temporary header to override the + # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` + # see _response.py for implementation details + override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given) + if is_given(override_cast_to): + options.headers = headers + return cast(Type[ResponseT], override_cast_to) + + return cast_to + + def _should_stream_response_body(self, request: httpx2.Request) -> bool: + return request.headers.get(RAW_RESPONSE_HEADER) == "stream" # type: ignore[no-any-return] + + def _process_response_data( + self, + *, + data: object, + cast_to: type[ResponseT], + response: httpx2.Response, + ) -> ResponseT: + if data is None: + return cast(ResponseT, None) + + if cast_to is object: + return cast(ResponseT, data) + + try: + if inspect.isclass(cast_to) and issubclass(cast_to, ModelBuilderProtocol): + return cast(ResponseT, cast_to.build(response=response, data=data)) + + if self._strict_response_validation: + return cast(ResponseT, validate_type(type_=cast_to, value=data)) + + return cast(ResponseT, construct_type(type_=cast_to, value=data)) + except pydantic.ValidationError as err: + raise APIResponseValidationError(response=response, body=data) from err + + @property + def qs(self) -> Querystring: + return Querystring() + + @property + def custom_auth(self) -> httpx2.Auth | None: + return None + + @property + def auth_headers(self) -> dict[str, str]: + return {} + + @property + def default_headers(self) -> dict[str, str | Omit]: + return { + "Accept": "application/json", + "Content-Type": "application/json", + "User-Agent": self.user_agent, + **self.platform_headers(), + **self._custom_headers, + } + + @property + def default_query(self) -> dict[str, object]: + return { + **self._custom_query, + } + + def _validate_headers( + self, + headers: Headers, # noqa: ARG002 + custom_headers: Headers, # noqa: ARG002 + ) -> None: + """Validate the given default headers and custom headers. + + Does nothing by default. + """ + return + + @property + def user_agent(self) -> str: + return f"{self.__class__.__name__}/Python {self._version}" + + @property + def base_url(self) -> URL: + return self._base_url + + @base_url.setter + def base_url(self, url: URL | str) -> None: + self._base_url = self._enforce_trailing_slash(normalize_httpx_url(url)) + + def platform_headers(self) -> Dict[str, str]: + # the actual implementation is in a separate `lru_cache` decorated + # function because adding `lru_cache` to methods will leak memory + # https://github.com/python/cpython/issues/88476 + return platform_headers(self._version, platform=self._platform) + + def _parse_retry_after_header(self, response_headers: Optional[httpx2.Headers] = None) -> float | None: + """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified. + + About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + See also https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax + """ + if response_headers is None: + return None + + # Prefer milliseconds, then seconds (allowing nonstandard floats). + for header, divisor in (("retry-after-ms", 1000), ("retry-after", 1)): + value = response_headers.get(header) + if value is None: + continue + try: + delay = float(value) + except ValueError: + continue + if delay == math.inf and value.strip().lower() not in ("inf", "+inf", "infinity", "+infinity"): + # Numeric overflow is an excessive server delay, not a malformed + # infinity literal. Keep a finite sentinel so retry eligibility + # refuses it instead of falling back to a shorter wait. + return MAX_RETRY_AFTER_DELAY + 1 + return delay / divisor + + # Last, try parsing `retry-after` as a date. + retry_header = response_headers.get("retry-after") + try: + retry_date_tuple = email.utils.parsedate_tz(retry_header) + if retry_date_tuple is None: + return None + + retry_date = email.utils.mktime_tz(retry_date_tuple) + except (TypeError, ValueError, OverflowError, OSError): + return None + + return float(retry_date - time.time()) + + @staticmethod + def _validate_max_retries(value: object) -> None: + if value is None: + raise TypeError( + "max_retries cannot be None. Use 0 to disable retries or a large integer for a larger retry budget." + ) + if not isinstance(value, int): + raise TypeError("max_retries must be a non-negative integer") + if value < 0: + raise ValueError("max_retries must be a non-negative integer") + + def _calculate_retry_timeout( + self, + remaining_retries: int, + options: FinalRequestOptions, + response_headers: Optional[httpx2.Headers] = None, + ) -> float: + max_retries = options.get_max_retries(self.max_retries) + + # Honor server-directed delays up to two minutes. + retry_after = self._parse_retry_after_header(response_headers) + if retry_after is not None and math.isfinite(retry_after) and 0 < retry_after <= MAX_RETRY_AFTER_DELAY: + return retry_after + + # Also cap retry count to 1000 to avoid any potential overflows with `pow` + nb_retries = min(max_retries - remaining_retries, 1000) + + # Apply exponential backoff, but not more than the max. + sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY) + + # Reduce the calculated timeout by a random range between 0-25% + jitter = 1 - 0.25 * random() + timeout = sleep_seconds * jitter + return timeout if timeout >= 0 else 0 + + def _should_retry(self, response: httpx2.Response) -> bool: + retry_after = self._parse_retry_after_header(response.headers) + if retry_after is not None and math.isfinite(retry_after) and retry_after > MAX_RETRY_AFTER_DELAY: + log.debug( + "Not retrying because `Retry-After` of %s seconds exceeds the maximum of %s seconds", + retry_after, + MAX_RETRY_AFTER_DELAY, + ) + return False + + # Note: this is not a standard header + should_retry_header = response.headers.get("x-should-retry") + + # If the server explicitly says whether or not to retry, obey. + if should_retry_header == "true": + log.debug("Retrying as header `x-should-retry` is set to `true`") + return True + if should_retry_header == "false": + log.debug("Not retrying as header `x-should-retry` is set to `false`") + return False + + # Retry on request timeouts. + if response.status_code == 408: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry on lock timeouts. + if response.status_code == 409: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry on rate limits. + if response.status_code == 429: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry internal errors. + if response.status_code >= 500: + log.debug("Retrying due to status code %i", response.status_code) + return True + + log.debug("Not retrying") + return False + + def _idempotency_key(self) -> str: + return f"stainless-python-retry-{uuid.uuid4()}" + + +class _DefaultHttpxClient(httpx2.Client): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultHttpxClient = httpx2.Client + """An alias to `httpx2.Client` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx2.Client` will result in HTTPX2's defaults being used, not ours. + """ +else: + DefaultHttpxClient = _DefaultHttpxClient + + +class SyncHttpxClientWrapper(DefaultHttpxClient): + def __del__(self) -> None: + if self.is_closed: + return + + try: + self.close() + except Exception: + pass + + +class SyncAPIClient(BaseClient[httpx2.Client, Stream[Any]]): + _client: httpx2.Client + _default_stream_cls: type[Stream[Any]] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx2.Client | None = None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + _strict_response_validation: bool, + ) -> None: + if not is_given(timeout): + # if the user passed in a custom http client with a non-default + # timeout set then we use that timeout. + # + # note: there is an edge case here where the user passes in a client + # where they've explicitly set the timeout to match the default timeout + # as this check is structural, meaning that we'll think they didn't + # pass in a timeout and will ignore it + client_timeout = normalize_httpx_timeout(http_client.timeout) if http_client else None + if http_client and client_timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = client_timeout + else: + timeout = DEFAULT_TIMEOUT + + if ( + http_client is not None + and not is_httpx2_sync_client(http_client) + and not is_legacy_httpx_sync_client(http_client) + ): + raise TypeError( + "Invalid `http_client` argument; Expected an instance of `httpx.Client` or `httpx2.Client` " + f"but got {type(http_client)}" + ) + + super().__init__( + version=version, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + base_url=base_url, + max_retries=max_retries, + custom_query=custom_query, + custom_headers=custom_headers, + _strict_response_validation=_strict_response_validation, + ) + self._client = http_client or SyncHttpxClientWrapper( + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + ) + + def is_closed(self) -> bool: + return self._client.is_closed + + def close(self) -> None: + """Close the underlying HTTPX client. + + The client will *not* be usable after this. + """ + # If an error is thrown while constructing a client, self._client + # may not be present + if hasattr(self, "_client"): + self._client.close() + + def __enter__(self: _T) -> _T: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def _prepare_options( + self, + options: FinalRequestOptions, # noqa: ARG002 + ) -> FinalRequestOptions: + """Hook for mutating the given options""" + return options + + def _prepare_request( + self, + request: httpx2.Request, # noqa: ARG002 + ) -> None: + """This method is used as a callback for mutating the `Request` object + after it has been constructed. + This is useful for cases where you want to add certain headers based off of + the request properties, e.g. `url`, `method` etc. + """ + return None + + def _send_request( + self, + request: httpx2.Request, + *, + stream: bool, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx2.Response: + return self._client.send(request, stream=stream, **kwargs) + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[True], + stream_cls: Type[_StreamT], + ) -> _StreamT: ... + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: Type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + cast_to = self._maybe_override_cast_to(cast_to, options) + + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + if input_options.idempotency_key is None and input_options.method.lower() != "get": + # ensure the idempotency key is reused between requests + input_options.idempotency_key = self._idempotency_key() + + response: httpx2.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) + self._validate_max_retries(max_retries) + + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = self._prepare_options(options) + request_body_replay = _RequestBodyReplay(options) + + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + self._prepare_request(request) + + kwargs: HttpxSendArgs = {} + custom_auth = self._custom_auth(options.security) + if custom_auth is not None: + kwargs["auth"] = ( + normalize_httpx2_auth(custom_auth) + if is_httpx2_sync_client(self._client) + else normalize_legacy_httpx_auth(custom_auth) + ) + + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects + + log.debug("Sending HTTP Request: %s", get_http_method_for_logging(request.method)) + + response = None + try: + response = self._send_request( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, + ) + except timeout_exceptions() as err: + log.debug("Encountered a timeout exception: %s", type(err).__name__) + + if remaining_retries > 0 and request_body_replay.rewind(): + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except OpenAIError as err: + # Propagate OpenAIErrors as-is, without retrying or wrapping in APIConnectionError + raise err + except request_exceptions() as err: + log.debug("Encountered exception: %s", type(err).__name__) + + if remaining_retries > 0 and request_body_replay.rewind(): + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + "HTTP Response: %s %i", + get_http_method_for_logging(request.method), + response.status_code, + ) + log.debug("request_id: %s", response.headers.get("x-request-id")) + + try: + response.raise_for_status() + except status_exceptions() as err: # thrown on 4xx and 5xx status code + log.debug("Encountered an HTTP status error: %i", response.status_code) + + if remaining_retries > 0 and self._should_retry(err.response) and request_body_replay.rewind(): + err.response.close() + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue + + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + err.response.read() + + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None + + break + + assert response is not None, "could not resolve response (should never happen)" + return self._process_response( + cast_to=cast_to, + options=options, + response=response, + stream=stream, + stream_cls=stream_cls, + retries_taken=retries_taken, + ) + + def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx2.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken + if remaining_retries == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining_retries) + + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) + log.info("Retrying request in %f seconds (retry %i of %s)", timeout, retries_taken + 1, max_retries) + + time.sleep(timeout) + + def _process_response( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + response: httpx2.Response, + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, + ) -> ResponseT: + if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": + return cast( + ResponseT, + LegacyAPIResponse( + raw=response, + client=self, + cast_to=cast_to, + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ), + ) + + origin = get_origin(cast_to) or cast_to + + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): + if not issubclass(origin, APIResponse): + raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}") + + response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) + return cast( + ResponseT, + response_cls( + raw=response, + client=self, + cast_to=extract_response_type(response_cls), + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ), + ) + + if cast_to in http_response_types(): + return cast(ResponseT, response) + + api_response = APIResponse( + raw=response, + client=self, + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ) + if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): + return cast(ResponseT, api_response) + + return api_response.parse() + + def _request_api_list( + self, + model: Type[object], + page: Type[SyncPageT], + options: FinalRequestOptions, + ) -> SyncPageT: + def _parser(resp: SyncPageT) -> SyncPageT: + resp._set_private_attributes( + client=self, + model=model, + options=options, + ) + return resp + + options.post_parser = _parser + + return self.request(page, options, stream=False) + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_StreamT], + ) -> _StreamT: ... + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + opts = FinalRequestOptions.construct(method="get", url=path, **options) + # cast is required because mypy complains about returning Any even though + # it understands the type variables + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[True], + stream_cls: type[_StreamT], + ) -> _StreamT: ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, content=content, files=to_httpx_files(files), **options + ) + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + + def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="patch", url=path, json_data=body, content=content, files=to_httpx_files(files), **options + ) + return self.request(cast_to, opts) + + def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, content=content, files=to_httpx_files(files), **options + ) + return self.request(cast_to, opts) + + def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) + return self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + model: Type[object], + page: Type[SyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> SyncPageT: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +class _DefaultAsyncHttpxClient(httpx2.AsyncClient): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +_DefaultAioHttpClient: type[httpx2.AsyncClient] + +try: + from ._vendor.httpx_aiohttp import Httpx2AiohttpClient +except ImportError: + + class _MissingAioHttpClient(httpx2.AsyncClient): + def __init__(self, **_kwargs: Any) -> None: + raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") + + _DefaultAioHttpClient = _MissingAioHttpClient +else: + + class _InstalledAioHttpClient(Httpx2AiohttpClient): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + _DefaultAioHttpClient = _InstalledAioHttpClient + + +if TYPE_CHECKING: + DefaultAsyncHttpxClient = httpx2.AsyncClient + """An alias to `httpx2.AsyncClient` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx2.AsyncClient` will result in HTTPX2's defaults being used, not ours. + """ + + DefaultAioHttpClient = httpx2.AsyncClient + """An alias to `httpx2.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" +else: + DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + DefaultAioHttpClient = _DefaultAioHttpClient + + +class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): + def __del__(self) -> None: + if self.is_closed: + return + + try: + # TODO(someday): support non asyncio runtimes here + asyncio.get_running_loop().create_task(self.aclose()) + except Exception: + pass + + +class AsyncAPIClient(BaseClient[httpx2.AsyncClient, AsyncStream[Any]]): + _client: httpx2.AsyncClient + _default_stream_cls: type[AsyncStream[Any]] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + _strict_response_validation: bool, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx2.AsyncClient | None = None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + ) -> None: + if not is_given(timeout): + # if the user passed in a custom http client with a non-default + # timeout set then we use that timeout. + # + # note: there is an edge case here where the user passes in a client + # where they've explicitly set the timeout to match the default timeout + # as this check is structural, meaning that we'll think they didn't + # pass in a timeout and will ignore it + client_timeout = normalize_httpx_timeout(http_client.timeout) if http_client else None + if http_client and client_timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = client_timeout + else: + timeout = DEFAULT_TIMEOUT + + if ( + http_client is not None + and not is_httpx2_async_client(http_client) + and not is_legacy_httpx_async_client(http_client) + ): + raise TypeError( + "Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` or " + f"`httpx2.AsyncClient` but got {type(http_client)}" + ) + + super().__init__( + version=version, + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + max_retries=max_retries, + custom_query=custom_query, + custom_headers=custom_headers, + _strict_response_validation=_strict_response_validation, + ) + self._client = http_client or AsyncHttpxClientWrapper( + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + ) + + def is_closed(self) -> bool: + return self._client.is_closed + + async def close(self) -> None: + """Close the underlying HTTPX client. + + The client will *not* be usable after this. + """ + await self._client.aclose() + + async def __aenter__(self: _T) -> _T: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def _prepare_options( + self, + options: FinalRequestOptions, # noqa: ARG002 + ) -> FinalRequestOptions: + """Hook for mutating the given options""" + return options + + async def _prepare_request( + self, + request: httpx2.Request, # noqa: ARG002 + ) -> None: + """This method is used as a callback for mutating the `Request` object + after it has been constructed. + This is useful for cases where you want to add certain headers based off of + the request properties, e.g. `url`, `method` etc. + """ + return None + + async def _send_request( + self, + request: httpx2.Request, + *, + stream: bool, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx2.Response: + return await self._client.send(request, stream=stream, **kwargs) + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + if self._platform is None: + # `get_platform` can make blocking IO calls so we + # execute it earlier while we are in an async context + self._platform = await asyncify(get_platform)() + + cast_to = self._maybe_override_cast_to(cast_to, options) + + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + if input_options.idempotency_key is None and input_options.method.lower() != "get": + # ensure the idempotency key is reused between requests + input_options.idempotency_key = self._idempotency_key() + + response: httpx2.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) + self._validate_max_retries(max_retries) + + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = await self._prepare_options(options) + request_body_replay = _RequestBodyReplay(options) + + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + await self._prepare_request(request) + + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = ( + normalize_httpx2_auth(self.custom_auth) + if is_httpx2_async_client(self._client) + else normalize_legacy_httpx_auth(self.custom_auth) + ) + + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects + + log.debug("Sending HTTP Request: %s", get_http_method_for_logging(request.method)) + + response = None + try: + response = await self._send_request( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, + ) + except timeout_exceptions() as err: + log.debug("Encountered a timeout exception: %s", type(err).__name__) + + if remaining_retries > 0 and request_body_replay.rewind(): + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except OpenAIError as err: + # Propagate OpenAIErrors as-is, without retrying or wrapping in APIConnectionError + raise err + except request_exceptions() as err: + log.debug("Encountered exception: %s", type(err).__name__) + + if remaining_retries > 0 and request_body_replay.rewind(): + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + "HTTP Response: %s %i", + get_http_method_for_logging(request.method), + response.status_code, + ) + log.debug("request_id: %s", response.headers.get("x-request-id")) + + try: + response.raise_for_status() + except status_exceptions() as err: # thrown on 4xx and 5xx status code + log.debug("Encountered an HTTP status error: %i", response.status_code) + + if remaining_retries > 0 and self._should_retry(err.response) and request_body_replay.rewind(): + await err.response.aclose() + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue + + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + await err.response.aread() + + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None + + break + + assert response is not None, "could not resolve response (should never happen)" + return await self._process_response( + cast_to=cast_to, + options=options, + response=response, + stream=stream, + stream_cls=stream_cls, + retries_taken=retries_taken, + ) + + async def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx2.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken + if remaining_retries == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining_retries) + + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) + log.info("Retrying request in %f seconds (retry %i of %s)", timeout, retries_taken + 1, max_retries) + + await anyio.sleep(timeout) + + async def _process_response( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + response: httpx2.Response, + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, + ) -> ResponseT: + if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": + return cast( + ResponseT, + LegacyAPIResponse( + raw=response, + client=self, + cast_to=cast_to, + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ), + ) + + origin = get_origin(cast_to) or cast_to + + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): + if not issubclass(origin, AsyncAPIResponse): + raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}") + + response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) + return cast( + "ResponseT", + response_cls( + raw=response, + client=self, + cast_to=extract_response_type(response_cls), + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ), + ) + + if cast_to in http_response_types(): + return cast(ResponseT, response) + + api_response = AsyncAPIResponse( + raw=response, + client=self, + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ) + if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): + return cast(ResponseT, api_response) + + return await api_response.parse() + + def _request_api_list( + self, + model: Type[_T], + page: Type[AsyncPageT], + options: FinalRequestOptions, + ) -> AsyncPaginator[_T, AsyncPageT]: + return AsyncPaginator(client=self, options=options, page_cls=page, model=model) + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + opts = FinalRequestOptions.construct(method="get", url=path, **options) + return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + + async def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="patch", + url=path, + json_data=body, + content=content, + files=await async_to_httpx_files(files), + **options, + ) + return await self.request(cast_to, opts) + + async def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts) + + async def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) + return await self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + model: Type[_T], + page: Type[AsyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> AsyncPaginator[_T, AsyncPageT]: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +def make_request_options( + *, + query: Query | None = None, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + idempotency_key: str | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + post_parser: PostParser | NotGiven = not_given, + security: SecurityOptions | None = None, + synthesize_event_and_data: bool | None = None, +) -> RequestOptions: + """Create a dict of type RequestOptions without keys of NotGiven values.""" + options: RequestOptions = {} + if extra_headers is not None: + options["headers"] = extra_headers + + if extra_body is not None: + options["extra_json"] = cast(AnyMapping, extra_body) + + if query is not None: + options["params"] = query + + if extra_query is not None: + options["params"] = {**options.get("params", {}), **extra_query} + + if not isinstance(timeout, NotGiven): + options["timeout"] = timeout + + if idempotency_key is not None: + options["idempotency_key"] = idempotency_key + + if is_given(post_parser): + # internal + options["post_parser"] = post_parser # type: ignore + + if security is not None: + options["security"] = security + + if synthesize_event_and_data is not None: + options["synthesize_event_and_data"] = synthesize_event_and_data + + return options + + +class ForceMultipartDict(Dict[str, None]): + def __bool__(self) -> bool: + return True + + +class OtherPlatform: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"Other:{self.name}" + + +Platform = Union[ + OtherPlatform, + Literal[ + "MacOS", + "Linux", + "Windows", + "FreeBSD", + "OpenBSD", + "iOS", + "Android", + "Unknown", + ], +] + + +def get_platform() -> Platform: + try: + system = platform.system().lower() + platform_name = platform.platform().lower() + except Exception: + return "Unknown" + + if "iphone" in platform_name or "ipad" in platform_name: + # Tested using Python3IDE on an iPhone 11 and Pythonista on an iPad 7 + # system is Darwin and platform_name is a string like: + # - Darwin-21.6.0-iPhone12,1-64bit + # - Darwin-21.6.0-iPad7,11-64bit + return "iOS" + + if system == "darwin": + return "MacOS" + + if system == "windows": + return "Windows" + + if "android" in platform_name: + # Tested using Pydroid 3 + # system is Linux and platform_name is a string like 'Linux-5.10.81-android12-9-00001-geba40aecb3b7-ab8534902-aarch64-with-libc' + return "Android" + + if system == "freebsd": + return "FreeBSD" + + if system == "openbsd": + return "OpenBSD" + + if system == "linux": + try: + distro_id = platform.freedesktop_os_release().get("ID", "").lower() + except OSError: + # This diagnostic header does not require an os-release file. + distro_id = "" + if distro_id == "freebsd": + return "FreeBSD" + + if distro_id == "openbsd": + return "OpenBSD" + + return "Linux" + + if platform_name: + return OtherPlatform(platform_name) + + return "Unknown" + + +@lru_cache(maxsize=None) +def platform_headers(version: str, *, platform: Platform | None) -> Dict[str, str]: + return { + "X-Stainless-Lang": "python", + "X-Stainless-Package-Version": version, + "X-Stainless-OS": str(platform or get_platform()), + "X-Stainless-Arch": str(get_architecture()), + "X-Stainless-Runtime": get_python_runtime(), + "X-Stainless-Runtime-Version": get_python_version(), + } + + +class OtherArch: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"other:{self.name}" + + +Arch = Union[OtherArch, Literal["x32", "x64", "arm", "arm64", "unknown"]] + + +def get_python_runtime() -> str: + try: + return platform.python_implementation() + except Exception: + return "unknown" + + +def get_python_version() -> str: + try: + return platform.python_version() + except Exception: + return "unknown" + + +def get_architecture() -> Arch: + try: + machine = platform.machine().lower() + except Exception: + return "unknown" + + if machine in ("arm64", "aarch64"): + return "arm64" + + # TODO: untested + if machine == "arm": + return "arm" + + if machine == "x86_64": + return "x64" + + # TODO: untested + if sys.maxsize <= 2**32: + return "x32" + + if machine: + return OtherArch(machine) + + return "unknown" + + +def _merge_mappings( + obj1: Mapping[_T_co, Union[_T, Omit]], + obj2: Mapping[_T_co, Union[_T, Omit]], +) -> Dict[_T_co, _T]: + """Merge two mappings of the same type, removing any values that are instances of `Omit`. + + In cases with duplicate keys the second mapping takes precedence. + """ + merged = {**obj1, **obj2} + return {key: value for key, value in merged.items() if not isinstance(value, Omit)} + + +def _merge_headers(*mappings: Headers) -> dict[str, str]: + """Merge headers case-insensitively, with later mappings taking precedence.""" + merged: dict[str, tuple[str, str]] = {} + for mapping in mappings: + for name, value in mapping.items(): + normalized_name = name.lower() + if isinstance(value, Omit): + merged.pop(normalized_name, None) + else: + merged[normalized_name] = (name, value) + + return {name: value for name, value in merged.values()} diff --git a/venv/lib/python3.12/site-packages/openai/_client.py b/venv/lib/python3.12/site-packages/openai/_client.py new file mode 100644 index 0000000..0a5330a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_client.py @@ -0,0 +1,2363 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import TYPE_CHECKING, Any, Mapping, Callable, Awaitable +from typing_extensions import Self, Unpack, override + +import httpx2 + +from . import _exceptions +from ._qs import Querystring +from .auth import WorkloadIdentity, WorkloadIdentityAuth, X509WorkloadIdentity +from ._types import ( + Omit, + Headers, + Timeout, + NotGiven, + Transport, + ProxiesTypes, + HttpxSendArgs, + RequestOptions, + not_given, +) +from ._utils import ( + is_given, + is_mapping, + is_mapping_t, + get_async_library, +) +from ._compat import cached_property +from ._httpx2 import normalize_httpx_url, is_httpx2_sync_client, is_httpx2_async_client +from ._models import SecurityOptions, FinalRequestOptions +from ._version import __version__ +from ._provider import _Provider, _provider_name, _ProviderRuntime, _configure_provider +from ._streaming import Stream as Stream, AsyncStream as AsyncStream +from .auth._x509 import ( + MTLS_API_BASE_URL, + SyncX509WorkloadIdentityAuth, + AsyncX509WorkloadIdentityAuth, + validate_x509_api_url, + is_x509_workload_identity, + x509_data_residency_base_url, + validate_x509_api_credentials, + x509_safe_environment_headers, + validate_x509_request_authority, +) +from ._exceptions import OpenAIError, APIStatusError +from ._base_client import ( + DEFAULT_MAX_RETRIES, + SyncAPIClient, + AsyncAPIClient, +) +from ._data_residency import DataResidency, resolve_data_residency + +if TYPE_CHECKING: + from .resources import ( + beta, + chat, + live, + admin, + audio, + evals, + files, + images, + models, + safety, + skills, + videos, + batches, + uploads, + realtime, + webhooks, + responses, + containers, + embeddings, + completions, + fine_tuning, + moderations, + conversations, + vector_stores, + content_provenance_checks, + ) + from .resources.files import Files, AsyncFiles + from .resources.images import Images, AsyncImages + from .resources.models import Models, AsyncModels + from .resources.videos import Videos, AsyncVideos + from .resources.batches import Batches, AsyncBatches + from .resources.beta.beta import Beta, AsyncBeta + from .resources.chat.chat import Chat, AsyncChat + from .resources.live.live import Live, AsyncLive + from .resources.embeddings import Embeddings, AsyncEmbeddings + from .resources.admin.admin import Admin, AsyncAdmin + from .resources.audio.audio import Audio, AsyncAudio + from .resources.completions import Completions, AsyncCompletions + from .resources.evals.evals import Evals, AsyncEvals + from .resources.moderations import Moderations, AsyncModerations + from .resources.safety.safety import Safety, AsyncSafety + from .resources.skills.skills import Skills, AsyncSkills + from .resources.uploads.uploads import Uploads, AsyncUploads + from .resources.realtime.realtime import Realtime, AsyncRealtime + from .resources.webhooks.webhooks import Webhooks, AsyncWebhooks + from .resources.responses.responses import Responses, AsyncResponses + from .resources.containers.containers import Containers, AsyncContainers + from .resources.fine_tuning.fine_tuning import FineTuning, AsyncFineTuning + from .resources.content_provenance_checks import ContentProvenanceChecks, AsyncContentProvenanceChecks + from .resources.conversations.conversations import Conversations, AsyncConversations + from .resources.vector_stores.vector_stores import VectorStores, AsyncVectorStores + +__all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "OpenAI", "AsyncOpenAI", "Client", "AsyncClient"] + +WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER = "workload-identity-auth" + + +def _has_header(headers: Headers, header: str) -> bool: + header = header.lower() + return any(key.lower() == header for key in headers) + + +def _has_omitted_header(headers: Headers, header: str) -> bool: + header = header.lower() + return any(key.lower() == header and isinstance(value, Omit) for key, value in headers.items()) + + +class OpenAI(SyncAPIClient): + # client options + api_key: str + admin_api_key: str | None + workload_identity: WorkloadIdentity | X509WorkloadIdentity | None + organization: str | None + project: str | None + webhook_secret: str | None + _workload_identity_auth: WorkloadIdentityAuth | SyncX509WorkloadIdentityAuth | None + _provider: _Provider | None + _provider_runtime: _ProviderRuntime | None + _base_url_was_default: bool + _data_residency: DataResidency | None + _ambient_authorizations: frozenset[str] + + websocket_base_url: str | httpx2.URL | None + """Base URL for WebSocket connections. + + If not specified, the default base URL will be used, with 'wss://' replacing the + 'http://' or 'https://' scheme. For example: 'http://example.com' becomes + 'wss://example.com' + """ + + @property + @override + def base_url(self) -> httpx2.URL: + return self._base_url + + @base_url.setter + def base_url(self, url: httpx2.URL | str) -> None: + normalized_url = normalize_httpx_url(url) + if is_x509_workload_identity(self.workload_identity): + validate_x509_api_url(normalized_url) + self._base_url = self._enforce_trailing_slash(normalized_url) + self._base_url_was_default = False + self._data_residency = None + + def __init__( + self, + *, + api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, + workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + provider: _Provider | None = None, + base_url: str | httpx2.URL | None | NotGiven = not_given, + data_residency: DataResidency | None = None, + websocket_base_url: str | httpx2.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx2 client. + # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx2 documentation](https://httpx2.pydantic.dev/api/#client) for more details. + http_client: httpx2.Client | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + ) -> None: + """Construct a new synchronous OpenAI client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `OPENAI_API_KEY` + - `admin_api_key` from `OPENAI_ADMIN_KEY` + - `organization` from `OPENAI_ORG_ID` + - `project` from `OPENAI_PROJECT_ID` + - `webhook_secret` from `OPENAI_WEBHOOK_SECRET` + + When `provider` is supplied, authentication and the base URL are configured by that provider instead. + `data_residency` selects an OpenAI regional endpoint and cannot be combined with + `base_url`, `websocket_base_url`, or `provider`. + """ + base_url = resolve_data_residency( + data_residency, base_url, provider=provider, websocket_base_url=websocket_base_url + ) + base_url = x509_data_residency_base_url(base_url, data_residency, workload_identity) + provider_runtime: _ProviderRuntime | None = None + if provider is not None: + provider_name = _provider_name(provider) + conflicts = [ + name + for name, value in ( + ("api_key", api_key), + ("admin_api_key", admin_api_key), + ("workload_identity", workload_identity), + ("base_url", base_url), + ) + if value is not None + ] + if conflicts: + formatted = ", ".join(f"`{name}`" for name in conflicts) + raise OpenAIError( + f"`provider` cannot be combined with top-level {formatted}. " + f"Move provider authentication and routing options into `{provider_name}(...)`." + ) + + provider_runtime = _configure_provider(provider) + + self._provider = provider + self._provider_runtime = provider_runtime + + if api_key is not None and api_key != WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER and workload_identity is not None: + raise OpenAIError("The `api_key` and `workload_identity` arguments are mutually exclusive") + + if is_x509_workload_identity(workload_identity): + workload_identity = workload_identity.copy() + self.workload_identity = workload_identity if provider_runtime is None else None + + if provider_runtime is not None: + self.api_key = "" + self._api_key_provider = None + self._workload_identity_auth = None + elif workload_identity is not None: + self.api_key = WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER + self._api_key_provider = None + self._workload_identity_auth = None + else: + if api_key is None: + api_key = os.environ.get("OPENAI_API_KEY") + if callable(api_key): + self.api_key = "" + self._api_key_provider: Callable[[], str] | None = api_key # type: ignore[no-redef] + else: + self.api_key = api_key or "" + self._api_key_provider = None + self._workload_identity_auth = None + + if admin_api_key is None and provider_runtime is None: + admin_api_key = os.environ.get("OPENAI_ADMIN_KEY") + self.admin_api_key = admin_api_key if provider_runtime is None else None + + if ( + provider_runtime is None + and _enforce_credentials + and not self.api_key + and self._api_key_provider is None + and workload_identity is None + and self.admin_api_key is None + ): + raise OpenAIError( + "Missing credentials. Please pass an `api_key`, `workload_identity`, `admin_api_key`, or set the `OPENAI_API_KEY` or `OPENAI_ADMIN_KEY` environment variable." + ) + + if organization is None and provider_runtime is None: + organization = os.environ.get("OPENAI_ORG_ID") + self.organization = organization + + if project is None and provider_runtime is None: + project = os.environ.get("OPENAI_PROJECT_ID") + self.project = project + + if webhook_secret is None: + webhook_secret = os.environ.get("OPENAI_WEBHOOK_SECRET") + self.webhook_secret = webhook_secret + + self.websocket_base_url = websocket_base_url + + if is_x509_workload_identity(workload_identity): + x509_identity = workload_identity + subject_token_identity = None + elif workload_identity is None: + x509_identity = None + subject_token_identity = None + elif "provider" in workload_identity: + x509_identity = None + subject_token_identity = workload_identity + else: + raise OpenAIError("Invalid `workload_identity` configuration: expected an X.509 or subject-token identity") + if provider_runtime is not None: + base_url = provider_runtime.base_url + elif base_url is None: + base_url = os.environ.get("OPENAI_BASE_URL") + self._base_url_was_default = provider_runtime is None and base_url is None + self._data_residency = data_residency + if base_url is None: + base_url = MTLS_API_BASE_URL if x509_identity is not None else "https://api.openai.com/v1" + if x509_identity is not None: + validate_x509_api_url(base_url) + + self._ambient_authorizations = frozenset() + custom_headers_env = os.environ.get("OPENAI_CUSTOM_HEADERS") if provider_runtime is None else None + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + explicit_headers: Mapping[str, str] = default_headers if is_mapping_t(default_headers) else {} + explicit_authorization = any(name.lower() == "authorization" for name in explicit_headers) + if explicit_authorization: + parsed = {name: value for name, value in parsed.items() if name.lower() != "authorization"} + elif x509_identity is None: + self._ambient_authorizations = frozenset( + value for name, value in parsed.items() if name.lower() == "authorization" + ) + default_headers = { + **x509_safe_environment_headers(parsed, x509_identity), + **explicit_headers, + } + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + if x509_identity is not None: + self._workload_identity_auth = SyncX509WorkloadIdentityAuth( + workload_identity=x509_identity.copy(), http_client=self._client, max_retries=max_retries + ) + elif subject_token_identity is not None: + self._workload_identity_auth = WorkloadIdentityAuth( + workload_identity=subject_token_identity, + _use_httpx2=is_httpx2_sync_client(self._client), + ) + + self._default_stream_cls = Stream + + @cached_property + def completions(self) -> Completions: + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ + from .resources.completions import Completions + + return Completions(self) + + @cached_property + def chat(self) -> Chat: + from .resources.chat import Chat + + return Chat(self) + + @cached_property + def embeddings(self) -> Embeddings: + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ + from .resources.embeddings import Embeddings + + return Embeddings(self) + + @cached_property + def files(self) -> Files: + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ + from .resources.files import Files + + return Files(self) + + @cached_property + def images(self) -> Images: + """Given a prompt and/or an input image, the model will generate a new image.""" + from .resources.images import Images + + return Images(self) + + @cached_property + def content_provenance_checks(self) -> ContentProvenanceChecks: + from .resources.content_provenance_checks import ContentProvenanceChecks + + return ContentProvenanceChecks(self) + + @cached_property + def audio(self) -> Audio: + from .resources.audio import Audio + + return Audio(self) + + @cached_property + def moderations(self) -> Moderations: + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ + from .resources.moderations import Moderations + + return Moderations(self) + + @cached_property + def models(self) -> Models: + """List and describe the various models available in the API.""" + from .resources.models import Models + + return Models(self) + + @cached_property + def fine_tuning(self) -> FineTuning: + from .resources.fine_tuning import FineTuning + + return FineTuning(self) + + @cached_property + def vector_stores(self) -> VectorStores: + from .resources.vector_stores import VectorStores + + return VectorStores(self) + + @cached_property + def safety(self) -> Safety: + from .resources.safety import Safety + + return Safety(self) + + @cached_property + def webhooks(self) -> Webhooks: + from .resources.webhooks import Webhooks + + return Webhooks(self) + + @cached_property + def beta(self) -> Beta: + from .resources.beta import Beta + + return Beta(self) + + @cached_property + def batches(self) -> Batches: + """Create large batches of API requests to run asynchronously.""" + from .resources.batches import Batches + + return Batches(self) + + @cached_property + def uploads(self) -> Uploads: + """Use Uploads to upload large files in multiple parts.""" + from .resources.uploads import Uploads + + return Uploads(self) + + @cached_property + def admin(self) -> Admin: + from .resources.admin import Admin + + return Admin(self) + + @cached_property + def responses(self) -> Responses: + """Create and manage model responses.""" + from .resources.responses import Responses + + return Responses(self) + + @cached_property + def live(self) -> Live: + from .resources.live import Live + + return Live(self) + + @cached_property + def realtime(self) -> Realtime: + from .resources.realtime import Realtime + + return Realtime(self) + + @cached_property + def conversations(self) -> Conversations: + """Manage conversations and conversation items.""" + from .resources.conversations import Conversations + + return Conversations(self) + + @cached_property + def evals(self) -> Evals: + """Manage and run evals in the OpenAI platform.""" + from .resources.evals import Evals + + return Evals(self) + + @cached_property + def containers(self) -> Containers: + from .resources.containers import Containers + + return Containers(self) + + @cached_property + def skills(self) -> Skills: + from .resources.skills import Skills + + return Skills(self) + + @cached_property + def videos(self) -> Videos: + from .resources.videos import Videos + + return Videos(self) + + @cached_property + def with_raw_response(self) -> OpenAIWithRawResponse: + return OpenAIWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> OpenAIWithStreamedResponse: + return OpenAIWithStreamedResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="brackets") + + def _send_with_auth_retry( + self, + request: httpx2.Request, + *, + stream: bool, + retried: bool = False, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx2.Response: + used_access_token: str | None = None + request_is_replayable = False + x509_auth = self._workload_identity_auth + + if x509_auth is not None: + if isinstance(x509_auth, SyncX509WorkloadIdentityAuth): + if x509_auth.workload_identity != self.workload_identity: + raise OpenAIError("X.509 workload identity cannot be changed after client construction") + validate_x509_api_url(request.url, expected_origin=self.base_url) + validate_x509_request_authority(request) + validate_x509_api_credentials(request) + if x509_auth._follow_redirects is not None: + kwargs["follow_redirects"] = x509_auth._follow_redirects + authorization = request.headers.get("Authorization") + if authorization == f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}": + used_access_token = ( + x509_auth.get_token_for_request(request) + if isinstance(x509_auth, SyncX509WorkloadIdentityAuth) + else x509_auth.get_token() + ) + request.headers["Authorization"] = f"Bearer {used_access_token}" + request_is_replayable = x509_auth._can_retry_request(request) + + if isinstance(x509_auth, SyncX509WorkloadIdentityAuth): + response = x509_auth.send_api_request( + request, + expected_origin=self.base_url, + expected_authorization=request.headers.get("Authorization"), + stream=stream, + **kwargs, + ) + else: + response = super()._send_request(request, stream=stream, **kwargs) + if response.status_code != 401 or self._workload_identity_auth is None or used_access_token is None: + return response + + self._workload_identity_auth.invalidate_token(used_access_token) + if retried or not request_is_replayable: + return response + + response.close() + self._workload_identity_auth._prepare_retry_request(request) + request.headers["Authorization"] = f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}" + return self._send_with_auth_retry(request, stream=stream, retried=True, **kwargs) + + @override + def _send_request( + self, + request: httpx2.Request, + *, + stream: bool, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx2.Response: + response = self._send_with_auth_retry(request, stream=stream, **kwargs) + if self._provider_runtime is not None and self._provider_runtime.normalize_response is not None: + response = self._provider_runtime.normalize_response(response) + return response + + @override + def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: + if self._provider_runtime is not None: + return {} + + headers: dict[str, str] = {} + if security.get("bearer_auth", False): + for key, value in self._bearer_auth.items(): + headers.setdefault(key, value) + if security.get("admin_api_key_auth", False): + for key, value in self._admin_api_key_auth.items(): + headers.setdefault(key, value) + return headers + + @property + def _bearer_auth(self) -> dict[str, str]: + api_key = self.api_key + if not api_key: + return {} + return {"Authorization": f"Bearer {api_key}"} + + @property + @override + def auth_headers(self) -> dict[str, str]: + if self._provider_runtime is not None: + return {} + + api_key = self.api_key + if not api_key or api_key == WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER: + return {} + return {"Authorization": f"Bearer {api_key}"} + + @property + def _admin_api_key_auth(self) -> dict[str, str]: + admin_api_key = self.admin_api_key + if admin_api_key is None: + return {} + return {"Authorization": f"Bearer {admin_api_key}"} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": "false", + "OpenAI-Organization": self.organization if self.organization is not None else Omit(), + "OpenAI-Project": self.project if self.project is not None else Omit(), + **self._custom_headers, + } + + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if self._provider_runtime is not None: + return + + if _has_header(headers, "Authorization") or _has_omitted_header(custom_headers, "Authorization"): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' + ) + + @override + def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + if options.url.startswith(("ws://", "wss://")) and ( + self._provider_runtime is not None or self.workload_identity is not None + ): + raise OpenAIError("This authentication method is not supported by WebSocket connections") + + if self._provider_runtime is not None: + if self._provider_runtime.transform_request is not None: + options = self._provider_runtime.transform_request(options) + elif self._api_key_provider is not None and options.security.get("bearer_auth", False): + self._refresh_api_key() + + return super()._prepare_options(options) + + @override + def _prepare_request(self, request: httpx2.Request) -> None: + if self._provider_runtime is not None and self._provider_runtime.prepare_request is not None: + self._provider_runtime.prepare_request(request) + + @override + def _custom_auth(self, security: SecurityOptions) -> httpx2.Auth | None: + if self._provider_runtime is not None or isinstance(self._workload_identity_auth, SyncX509WorkloadIdentityAuth): + return httpx2.Auth() + + return super()._custom_auth(security) + + def _refresh_api_key(self) -> str: + if self._api_key_provider is not None: + self.api_key = self._api_key_provider() + + return self.api_key + + def copy( + self, + *, + api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, + workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None, + provider: _Provider | None | NotGiven = not_given, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + base_url: str | httpx2.URL | None | NotGiven = not_given, + data_residency: DataResidency | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx2.Client | None = None, + max_retries: int | NotGiven = not_given, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _enforce_credentials: bool | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + `data_residency` replaces the inherited HTTP and WebSocket endpoints, without changing this client. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + provider_changed = not isinstance(provider, NotGiven) and provider is not self._provider + inherited_organization = None if provider_changed else self.organization + inherited_project = None if provider_changed else self.project + + headers: Mapping[str, str] = {} if provider_changed else self._custom_headers + if ( + is_x509_workload_identity(workload_identity) + and not is_x509_workload_identity(self.workload_identity) + and self._ambient_authorizations + ): + headers = { + name: value + for name, value in headers.items() + if name.lower() != "authorization" or value not in self._ambient_authorizations + } + if default_headers is not None: + if any(name.lower() == "authorization" for name in default_headers): + headers = {name: value for name, value in headers.items() if name.lower() != "authorization"} + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + + next_provider = self._provider if isinstance(provider, NotGiven) else provider + explicit_base_url = base_url is not None and not isinstance(base_url, NotGiven) + next_workload_identity = workload_identity if workload_identity is not None else self.workload_identity + if api_key is not None and workload_identity is None: + next_workload_identity = None + current_x509 = is_x509_workload_identity(self.workload_identity) + next_x509 = is_x509_workload_identity(next_workload_identity) + mode_changed = current_x509 != next_x509 + effective_data_residency = data_residency + if effective_data_residency is None and mode_changed and not explicit_base_url: + effective_data_residency = self._data_residency + base_url = resolve_data_residency( + effective_data_residency, + not_given if base_url is None and data_residency is None else base_url, + provider=next_provider, + websocket_base_url=websocket_base_url, + ) + base_url = x509_data_residency_base_url(base_url, effective_data_residency, next_workload_identity) + preserve_default_base_url = False + auth_options: dict[str, Any] + if next_provider is not None: + auth_options = { + "provider": next_provider, + "api_key": api_key, + "admin_api_key": admin_api_key, + "workload_identity": workload_identity, + "base_url": base_url, + } + elif self._provider is not None: + auth_options = { + "api_key": api_key, + "admin_api_key": admin_api_key, + "workload_identity": workload_identity, + "base_url": base_url, + } + else: + inherited_base_url = None if mode_changed and self._base_url_was_default else self.base_url + preserve_default_base_url = base_url is None and not mode_changed and self._base_url_was_default + auth_options = { + "api_key": api_key + if workload_identity is not None + else api_key or self._api_key_provider or self.api_key, + "admin_api_key": admin_api_key or self.admin_api_key, + "workload_identity": next_workload_identity, + "base_url": base_url or inherited_base_url, + } + + copied = self.__class__( + organization=organization or inherited_organization, + project=project or inherited_project, + webhook_secret=webhook_secret or self.webhook_secret, + websocket_base_url=None if data_residency is not None else websocket_base_url or self.websocket_base_url, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + _enforce_credentials=True if _enforce_credentials is None else _enforce_credentials, + **auth_options, + **_extra_kwargs, + ) + if preserve_default_base_url: + copied._base_url_was_default = True + overridden_authorizations = default_headers if default_headers is not None else set_default_headers + explicit_authorization_override = overridden_authorizations is not None and any( + name.lower() == "authorization" for name in overridden_authorizations + ) + if ( + self._ambient_authorizations + and not explicit_authorization_override + and any( + name.lower() == "authorization" and value in self._ambient_authorizations + for name, value in copied._custom_headers.items() + ) + ): + copied._ambient_authorizations = self._ambient_authorizations + if data_residency is not None: + copied._data_residency = data_residency + elif not explicit_base_url and not provider_changed: + copied._data_residency = self._data_residency + return copied + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx2.Response, + ) -> APIStatusError: + data = body.get("error", body) if is_mapping(body) else body + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=data) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=data) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=data) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=data) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=data) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=data) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=data) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=data) + return APIStatusError(err_msg, response=response, body=data) + + +class AsyncOpenAI(AsyncAPIClient): + # client options + api_key: str + admin_api_key: str | None + workload_identity: WorkloadIdentity | X509WorkloadIdentity | None + organization: str | None + project: str | None + webhook_secret: str | None + _workload_identity_auth: WorkloadIdentityAuth | AsyncX509WorkloadIdentityAuth | None + _provider: _Provider | None + _provider_runtime: _ProviderRuntime | None + _base_url_was_default: bool + _data_residency: DataResidency | None + _ambient_authorizations: frozenset[str] + + websocket_base_url: str | httpx2.URL | None + """Base URL for WebSocket connections. + + If not specified, the default base URL will be used, with 'wss://' replacing the + 'http://' or 'https://' scheme. For example: 'http://example.com' becomes + 'wss://example.com' + """ + + @property + @override + def base_url(self) -> httpx2.URL: + return self._base_url + + @base_url.setter + def base_url(self, url: httpx2.URL | str) -> None: + normalized_url = normalize_httpx_url(url) + if is_x509_workload_identity(self.workload_identity): + validate_x509_api_url(normalized_url) + self._base_url = self._enforce_trailing_slash(normalized_url) + self._base_url_was_default = False + self._data_residency = None + + def __init__( + self, + *, + api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, + workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + provider: _Provider | None = None, + base_url: str | httpx2.URL | None | NotGiven = not_given, + data_residency: DataResidency | None = None, + websocket_base_url: str | httpx2.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx2 client. + # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx2 documentation](https://httpx2.pydantic.dev/api/#asyncclient) for more details. + http_client: httpx2.AsyncClient | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + ) -> None: + """Construct a new async AsyncOpenAI client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `OPENAI_API_KEY` + - `admin_api_key` from `OPENAI_ADMIN_KEY` + - `organization` from `OPENAI_ORG_ID` + - `project` from `OPENAI_PROJECT_ID` + - `webhook_secret` from `OPENAI_WEBHOOK_SECRET` + + When `provider` is supplied, authentication and the base URL are configured by that provider instead. + `data_residency` selects an OpenAI regional endpoint and cannot be combined with + `base_url`, `websocket_base_url`, or `provider`. + """ + base_url = resolve_data_residency( + data_residency, base_url, provider=provider, websocket_base_url=websocket_base_url + ) + base_url = x509_data_residency_base_url(base_url, data_residency, workload_identity) + provider_runtime: _ProviderRuntime | None = None + if provider is not None: + provider_name = _provider_name(provider) + conflicts = [ + name + for name, value in ( + ("api_key", api_key), + ("admin_api_key", admin_api_key), + ("workload_identity", workload_identity), + ("base_url", base_url), + ) + if value is not None + ] + if conflicts: + formatted = ", ".join(f"`{name}`" for name in conflicts) + raise OpenAIError( + f"`provider` cannot be combined with top-level {formatted}. " + f"Move provider authentication and routing options into `{provider_name}(...)`." + ) + + provider_runtime = _configure_provider(provider) + + self._provider = provider + self._provider_runtime = provider_runtime + + if api_key is not None and api_key != WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER and workload_identity is not None: + raise OpenAIError("The `api_key` and `workload_identity` arguments are mutually exclusive") + + if is_x509_workload_identity(workload_identity): + workload_identity = workload_identity.copy() + self.workload_identity = workload_identity if provider_runtime is None else None + + if provider_runtime is not None: + self.api_key = "" + self._api_key_provider = None + self._workload_identity_auth = None + elif workload_identity is not None: + self.api_key = WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER + self._api_key_provider = None + self._workload_identity_auth = None + else: + if api_key is None: + api_key = os.environ.get("OPENAI_API_KEY") + if callable(api_key): + self.api_key = "" + self._api_key_provider: Callable[[], Awaitable[str]] | None = api_key # type: ignore[no-redef] + else: + self.api_key = api_key or "" + self._api_key_provider = None + self._workload_identity_auth = None + + if admin_api_key is None and provider_runtime is None: + admin_api_key = os.environ.get("OPENAI_ADMIN_KEY") + self.admin_api_key = admin_api_key if provider_runtime is None else None + + if ( + provider_runtime is None + and _enforce_credentials + and not self.api_key + and self._api_key_provider is None + and workload_identity is None + and self.admin_api_key is None + ): + raise OpenAIError( + "Missing credentials. Please pass an `api_key`, `workload_identity`, `admin_api_key`, or set the `OPENAI_API_KEY` or `OPENAI_ADMIN_KEY` environment variable." + ) + + if organization is None and provider_runtime is None: + organization = os.environ.get("OPENAI_ORG_ID") + self.organization = organization + + if project is None and provider_runtime is None: + project = os.environ.get("OPENAI_PROJECT_ID") + self.project = project + + if webhook_secret is None: + webhook_secret = os.environ.get("OPENAI_WEBHOOK_SECRET") + self.webhook_secret = webhook_secret + + self.websocket_base_url = websocket_base_url + + if is_x509_workload_identity(workload_identity): + x509_identity = workload_identity + subject_token_identity = None + elif workload_identity is None: + x509_identity = None + subject_token_identity = None + elif "provider" in workload_identity: + x509_identity = None + subject_token_identity = workload_identity + else: + raise OpenAIError("Invalid `workload_identity` configuration: expected an X.509 or subject-token identity") + if provider_runtime is not None: + base_url = provider_runtime.base_url + elif base_url is None: + base_url = os.environ.get("OPENAI_BASE_URL") + self._base_url_was_default = provider_runtime is None and base_url is None + self._data_residency = data_residency + if base_url is None: + base_url = MTLS_API_BASE_URL if x509_identity is not None else "https://api.openai.com/v1" + if x509_identity is not None: + validate_x509_api_url(base_url) + + self._ambient_authorizations = frozenset() + custom_headers_env = os.environ.get("OPENAI_CUSTOM_HEADERS") if provider_runtime is None else None + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + explicit_headers: Mapping[str, str] = default_headers if is_mapping_t(default_headers) else {} + explicit_authorization = any(name.lower() == "authorization" for name in explicit_headers) + if explicit_authorization: + parsed = {name: value for name, value in parsed.items() if name.lower() != "authorization"} + elif x509_identity is None: + self._ambient_authorizations = frozenset( + value for name, value in parsed.items() if name.lower() == "authorization" + ) + default_headers = { + **x509_safe_environment_headers(parsed, x509_identity), + **explicit_headers, + } + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + if x509_identity is not None: + self._workload_identity_auth = AsyncX509WorkloadIdentityAuth( + workload_identity=x509_identity.copy(), http_client=self._client, max_retries=max_retries + ) + elif subject_token_identity is not None: + self._workload_identity_auth = WorkloadIdentityAuth( + workload_identity=subject_token_identity, + _use_httpx2=is_httpx2_async_client(self._client), + ) + + self._default_stream_cls = AsyncStream + + @cached_property + def completions(self) -> AsyncCompletions: + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ + from .resources.completions import AsyncCompletions + + return AsyncCompletions(self) + + @cached_property + def chat(self) -> AsyncChat: + from .resources.chat import AsyncChat + + return AsyncChat(self) + + @cached_property + def embeddings(self) -> AsyncEmbeddings: + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ + from .resources.embeddings import AsyncEmbeddings + + return AsyncEmbeddings(self) + + @cached_property + def files(self) -> AsyncFiles: + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ + from .resources.files import AsyncFiles + + return AsyncFiles(self) + + @cached_property + def images(self) -> AsyncImages: + """Given a prompt and/or an input image, the model will generate a new image.""" + from .resources.images import AsyncImages + + return AsyncImages(self) + + @cached_property + def content_provenance_checks(self) -> AsyncContentProvenanceChecks: + from .resources.content_provenance_checks import AsyncContentProvenanceChecks + + return AsyncContentProvenanceChecks(self) + + @cached_property + def audio(self) -> AsyncAudio: + from .resources.audio import AsyncAudio + + return AsyncAudio(self) + + @cached_property + def moderations(self) -> AsyncModerations: + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ + from .resources.moderations import AsyncModerations + + return AsyncModerations(self) + + @cached_property + def models(self) -> AsyncModels: + """List and describe the various models available in the API.""" + from .resources.models import AsyncModels + + return AsyncModels(self) + + @cached_property + def fine_tuning(self) -> AsyncFineTuning: + from .resources.fine_tuning import AsyncFineTuning + + return AsyncFineTuning(self) + + @cached_property + def vector_stores(self) -> AsyncVectorStores: + from .resources.vector_stores import AsyncVectorStores + + return AsyncVectorStores(self) + + @cached_property + def safety(self) -> AsyncSafety: + from .resources.safety import AsyncSafety + + return AsyncSafety(self) + + @cached_property + def webhooks(self) -> AsyncWebhooks: + from .resources.webhooks import AsyncWebhooks + + return AsyncWebhooks(self) + + @cached_property + def beta(self) -> AsyncBeta: + from .resources.beta import AsyncBeta + + return AsyncBeta(self) + + @cached_property + def batches(self) -> AsyncBatches: + """Create large batches of API requests to run asynchronously.""" + from .resources.batches import AsyncBatches + + return AsyncBatches(self) + + @cached_property + def uploads(self) -> AsyncUploads: + """Use Uploads to upload large files in multiple parts.""" + from .resources.uploads import AsyncUploads + + return AsyncUploads(self) + + @cached_property + def admin(self) -> AsyncAdmin: + from .resources.admin import AsyncAdmin + + return AsyncAdmin(self) + + @cached_property + def responses(self) -> AsyncResponses: + """Create and manage model responses.""" + from .resources.responses import AsyncResponses + + return AsyncResponses(self) + + @cached_property + def live(self) -> AsyncLive: + from .resources.live import AsyncLive + + return AsyncLive(self) + + @cached_property + def realtime(self) -> AsyncRealtime: + from .resources.realtime import AsyncRealtime + + return AsyncRealtime(self) + + @cached_property + def conversations(self) -> AsyncConversations: + """Manage conversations and conversation items.""" + from .resources.conversations import AsyncConversations + + return AsyncConversations(self) + + @cached_property + def evals(self) -> AsyncEvals: + """Manage and run evals in the OpenAI platform.""" + from .resources.evals import AsyncEvals + + return AsyncEvals(self) + + @cached_property + def containers(self) -> AsyncContainers: + from .resources.containers import AsyncContainers + + return AsyncContainers(self) + + @cached_property + def skills(self) -> AsyncSkills: + from .resources.skills import AsyncSkills + + return AsyncSkills(self) + + @cached_property + def videos(self) -> AsyncVideos: + from .resources.videos import AsyncVideos + + return AsyncVideos(self) + + @cached_property + def with_raw_response(self) -> AsyncOpenAIWithRawResponse: + return AsyncOpenAIWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncOpenAIWithStreamedResponse: + return AsyncOpenAIWithStreamedResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="brackets") + + async def _send_with_auth_retry( + self, + request: httpx2.Request, + *, + stream: bool, + retried: bool = False, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx2.Response: + used_access_token: str | None = None + request_is_replayable = False + x509_auth = self._workload_identity_auth + + if x509_auth is not None: + if isinstance(x509_auth, AsyncX509WorkloadIdentityAuth): + if x509_auth.workload_identity != self.workload_identity: + raise OpenAIError("X.509 workload identity cannot be changed after client construction") + validate_x509_api_url(request.url, expected_origin=self.base_url) + validate_x509_request_authority(request) + validate_x509_api_credentials(request) + if x509_auth._follow_redirects is not None: + kwargs["follow_redirects"] = x509_auth._follow_redirects + authorization = request.headers.get("Authorization") + if authorization == f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}": + used_access_token = ( + await x509_auth.get_token_for_request(request) + if isinstance(x509_auth, AsyncX509WorkloadIdentityAuth) + else await x509_auth.get_token_async() + ) + request.headers["Authorization"] = f"Bearer {used_access_token}" + request_is_replayable = x509_auth._can_retry_request(request) + + if isinstance(x509_auth, AsyncX509WorkloadIdentityAuth): + response = await x509_auth.send_api_request( + request, + expected_origin=self.base_url, + expected_authorization=request.headers.get("Authorization"), + stream=stream, + **kwargs, + ) + else: + response = await super()._send_request(request, stream=stream, **kwargs) + if response.status_code != 401 or self._workload_identity_auth is None or used_access_token is None: + return response + + self._workload_identity_auth.invalidate_token(used_access_token) + if retried or not request_is_replayable: + return response + + await response.aclose() + self._workload_identity_auth._prepare_retry_request(request) + request.headers["Authorization"] = f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}" + return await self._send_with_auth_retry(request, stream=stream, retried=True, **kwargs) + + @override + async def _send_request( + self, + request: httpx2.Request, + *, + stream: bool, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx2.Response: + response = await self._send_with_auth_retry(request, stream=stream, **kwargs) + if self._provider_runtime is not None: + if self._provider_runtime.normalize_async_response is not None: + response = await self._provider_runtime.normalize_async_response(response) + elif self._provider_runtime.normalize_response is not None: + response = self._provider_runtime.normalize_response(response) + return response + + @override + def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: + if self._provider_runtime is not None: + return {} + + headers: dict[str, str] = {} + if security.get("bearer_auth", False): + for key, value in self._bearer_auth.items(): + headers.setdefault(key, value) + if security.get("admin_api_key_auth", False): + for key, value in self._admin_api_key_auth.items(): + headers.setdefault(key, value) + return headers + + @property + def _bearer_auth(self) -> dict[str, str]: + api_key = self.api_key + if not api_key: + return {} + return {"Authorization": f"Bearer {api_key}"} + + @property + @override + def auth_headers(self) -> dict[str, str]: + if self._provider_runtime is not None: + return {} + + api_key = self.api_key + if not api_key or api_key == WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER: + return {} + return {"Authorization": f"Bearer {api_key}"} + + @property + def _admin_api_key_auth(self) -> dict[str, str]: + admin_api_key = self.admin_api_key + if admin_api_key is None: + return {} + return {"Authorization": f"Bearer {admin_api_key}"} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": f"async:{get_async_library()}", + "OpenAI-Organization": self.organization if self.organization is not None else Omit(), + "OpenAI-Project": self.project if self.project is not None else Omit(), + **self._custom_headers, + } + + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if self._provider_runtime is not None: + return + + if _has_header(headers, "Authorization") or _has_omitted_header(custom_headers, "Authorization"): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' + ) + + @override + async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + if options.url.startswith(("ws://", "wss://")) and ( + self._provider_runtime is not None or self.workload_identity is not None + ): + raise OpenAIError("This authentication method is not supported by WebSocket connections") + + if self._provider_runtime is not None: + if self._provider_runtime.transform_async_request is not None: + options = await self._provider_runtime.transform_async_request(options) + elif self._provider_runtime.transform_request is not None: + options = self._provider_runtime.transform_request(options) + elif self._api_key_provider is not None and options.security.get("bearer_auth", False): + await self._refresh_api_key() + + return await super()._prepare_options(options) + + @override + async def _prepare_request(self, request: httpx2.Request) -> None: + if self._provider_runtime is None: + return + + if self._provider_runtime.prepare_async_request is not None: + await self._provider_runtime.prepare_async_request(request) + elif self._provider_runtime.prepare_request is not None: + self._provider_runtime.prepare_request(request) + + @property + @override + def custom_auth(self) -> httpx2.Auth | None: + if self._provider_runtime is not None or isinstance( + self._workload_identity_auth, AsyncX509WorkloadIdentityAuth + ): + return httpx2.Auth() + + return super().custom_auth + + async def _refresh_api_key(self) -> str: + if self._api_key_provider is not None: + self.api_key = await self._api_key_provider() + + return self.api_key + + def copy( + self, + *, + api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, + workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None, + provider: _Provider | None | NotGiven = not_given, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + base_url: str | httpx2.URL | None | NotGiven = not_given, + data_residency: DataResidency | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx2.AsyncClient | None = None, + max_retries: int | NotGiven = not_given, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _enforce_credentials: bool | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + `data_residency` replaces the inherited HTTP and WebSocket endpoints, without changing this client. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + provider_changed = not isinstance(provider, NotGiven) and provider is not self._provider + inherited_organization = None if provider_changed else self.organization + inherited_project = None if provider_changed else self.project + + headers: Mapping[str, str] = {} if provider_changed else self._custom_headers + if ( + is_x509_workload_identity(workload_identity) + and not is_x509_workload_identity(self.workload_identity) + and self._ambient_authorizations + ): + headers = { + name: value + for name, value in headers.items() + if name.lower() != "authorization" or value not in self._ambient_authorizations + } + if default_headers is not None: + if any(name.lower() == "authorization" for name in default_headers): + headers = {name: value for name, value in headers.items() if name.lower() != "authorization"} + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + next_provider = self._provider if isinstance(provider, NotGiven) else provider + explicit_base_url = base_url is not None and not isinstance(base_url, NotGiven) + next_workload_identity = workload_identity if workload_identity is not None else self.workload_identity + if api_key is not None and workload_identity is None: + next_workload_identity = None + current_x509 = is_x509_workload_identity(self.workload_identity) + next_x509 = is_x509_workload_identity(next_workload_identity) + mode_changed = current_x509 != next_x509 + effective_data_residency = data_residency + if effective_data_residency is None and mode_changed and not explicit_base_url: + effective_data_residency = self._data_residency + base_url = resolve_data_residency( + effective_data_residency, + not_given if base_url is None and data_residency is None else base_url, + provider=next_provider, + websocket_base_url=websocket_base_url, + ) + base_url = x509_data_residency_base_url(base_url, effective_data_residency, next_workload_identity) + preserve_default_base_url = False + auth_options: dict[str, Any] + if next_provider is not None: + auth_options = { + "provider": next_provider, + "api_key": api_key, + "admin_api_key": admin_api_key, + "workload_identity": workload_identity, + "base_url": base_url, + } + elif self._provider is not None: + auth_options = { + "api_key": api_key, + "admin_api_key": admin_api_key, + "workload_identity": workload_identity, + "base_url": base_url, + } + else: + inherited_base_url = None if mode_changed and self._base_url_was_default else self.base_url + preserve_default_base_url = base_url is None and not mode_changed and self._base_url_was_default + auth_options = { + "api_key": api_key + if workload_identity is not None + else api_key or self._api_key_provider or self.api_key, + "admin_api_key": admin_api_key or self.admin_api_key, + "workload_identity": next_workload_identity, + "base_url": base_url or inherited_base_url, + } + + copied = self.__class__( + organization=organization or inherited_organization, + project=project or inherited_project, + webhook_secret=webhook_secret or self.webhook_secret, + websocket_base_url=None if data_residency is not None else websocket_base_url or self.websocket_base_url, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + _enforce_credentials=True if _enforce_credentials is None else _enforce_credentials, + **auth_options, + **_extra_kwargs, + ) + if preserve_default_base_url: + copied._base_url_was_default = True + overridden_authorizations = default_headers if default_headers is not None else set_default_headers + explicit_authorization_override = overridden_authorizations is not None and any( + name.lower() == "authorization" for name in overridden_authorizations + ) + if ( + self._ambient_authorizations + and not explicit_authorization_override + and any( + name.lower() == "authorization" and value in self._ambient_authorizations + for name, value in copied._custom_headers.items() + ) + ): + copied._ambient_authorizations = self._ambient_authorizations + if data_residency is not None: + copied._data_residency = data_residency + elif not explicit_base_url and not provider_changed: + copied._data_residency = self._data_residency + return copied + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx2.Response, + ) -> APIStatusError: + data = body.get("error", body) if is_mapping(body) else body + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=data) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=data) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=data) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=data) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=data) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=data) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=data) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=data) + return APIStatusError(err_msg, response=response, body=data) + + +class OpenAIWithRawResponse: + _client: OpenAI + + def __init__(self, client: OpenAI) -> None: + self._client = client + + @cached_property + def completions(self) -> completions.CompletionsWithRawResponse: + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ + from .resources.completions import CompletionsWithRawResponse + + return CompletionsWithRawResponse(self._client.completions) + + @cached_property + def chat(self) -> chat.ChatWithRawResponse: + from .resources.chat import ChatWithRawResponse + + return ChatWithRawResponse(self._client.chat) + + @cached_property + def embeddings(self) -> embeddings.EmbeddingsWithRawResponse: + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ + from .resources.embeddings import EmbeddingsWithRawResponse + + return EmbeddingsWithRawResponse(self._client.embeddings) + + @cached_property + def files(self) -> files.FilesWithRawResponse: + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ + from .resources.files import FilesWithRawResponse + + return FilesWithRawResponse(self._client.files) + + @cached_property + def images(self) -> images.ImagesWithRawResponse: + """Given a prompt and/or an input image, the model will generate a new image.""" + from .resources.images import ImagesWithRawResponse + + return ImagesWithRawResponse(self._client.images) + + @cached_property + def content_provenance_checks(self) -> content_provenance_checks.ContentProvenanceChecksWithRawResponse: + from .resources.content_provenance_checks import ContentProvenanceChecksWithRawResponse + + return ContentProvenanceChecksWithRawResponse(self._client.content_provenance_checks) + + @cached_property + def audio(self) -> audio.AudioWithRawResponse: + from .resources.audio import AudioWithRawResponse + + return AudioWithRawResponse(self._client.audio) + + @cached_property + def moderations(self) -> moderations.ModerationsWithRawResponse: + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ + from .resources.moderations import ModerationsWithRawResponse + + return ModerationsWithRawResponse(self._client.moderations) + + @cached_property + def models(self) -> models.ModelsWithRawResponse: + """List and describe the various models available in the API.""" + from .resources.models import ModelsWithRawResponse + + return ModelsWithRawResponse(self._client.models) + + @cached_property + def fine_tuning(self) -> fine_tuning.FineTuningWithRawResponse: + from .resources.fine_tuning import FineTuningWithRawResponse + + return FineTuningWithRawResponse(self._client.fine_tuning) + + @cached_property + def vector_stores(self) -> vector_stores.VectorStoresWithRawResponse: + from .resources.vector_stores import VectorStoresWithRawResponse + + return VectorStoresWithRawResponse(self._client.vector_stores) + + @cached_property + def safety(self) -> safety.SafetyWithRawResponse: + from .resources.safety import SafetyWithRawResponse + + return SafetyWithRawResponse(self._client.safety) + + @cached_property + def webhooks(self) -> webhooks.WebhooksWithRawResponse: + from .resources.webhooks import WebhooksWithRawResponse + + return WebhooksWithRawResponse(self._client.webhooks) + + @cached_property + def beta(self) -> beta.BetaWithRawResponse: + from .resources.beta import BetaWithRawResponse + + return BetaWithRawResponse(self._client.beta) + + @cached_property + def batches(self) -> batches.BatchesWithRawResponse: + """Create large batches of API requests to run asynchronously.""" + from .resources.batches import BatchesWithRawResponse + + return BatchesWithRawResponse(self._client.batches) + + @cached_property + def uploads(self) -> uploads.UploadsWithRawResponse: + """Use Uploads to upload large files in multiple parts.""" + from .resources.uploads import UploadsWithRawResponse + + return UploadsWithRawResponse(self._client.uploads) + + @cached_property + def admin(self) -> admin.AdminWithRawResponse: + from .resources.admin import AdminWithRawResponse + + return AdminWithRawResponse(self._client.admin) + + @cached_property + def responses(self) -> responses.ResponsesWithRawResponse: + """Create and manage model responses.""" + from .resources.responses import ResponsesWithRawResponse + + return ResponsesWithRawResponse(self._client.responses) + + @cached_property + def live(self) -> live.LiveWithRawResponse: + from .resources.live import LiveWithRawResponse + + return LiveWithRawResponse(self._client.live) + + @cached_property + def realtime(self) -> realtime.RealtimeWithRawResponse: + from .resources.realtime import RealtimeWithRawResponse + + return RealtimeWithRawResponse(self._client.realtime) + + @cached_property + def conversations(self) -> conversations.ConversationsWithRawResponse: + """Manage conversations and conversation items.""" + from .resources.conversations import ConversationsWithRawResponse + + return ConversationsWithRawResponse(self._client.conversations) + + @cached_property + def evals(self) -> evals.EvalsWithRawResponse: + """Manage and run evals in the OpenAI platform.""" + from .resources.evals import EvalsWithRawResponse + + return EvalsWithRawResponse(self._client.evals) + + @cached_property + def containers(self) -> containers.ContainersWithRawResponse: + from .resources.containers import ContainersWithRawResponse + + return ContainersWithRawResponse(self._client.containers) + + @cached_property + def skills(self) -> skills.SkillsWithRawResponse: + from .resources.skills import SkillsWithRawResponse + + return SkillsWithRawResponse(self._client.skills) + + @cached_property + def videos(self) -> videos.VideosWithRawResponse: + from .resources.videos import VideosWithRawResponse + + return VideosWithRawResponse(self._client.videos) + + +class AsyncOpenAIWithRawResponse: + _client: AsyncOpenAI + + def __init__(self, client: AsyncOpenAI) -> None: + self._client = client + + @cached_property + def completions(self) -> completions.AsyncCompletionsWithRawResponse: + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ + from .resources.completions import AsyncCompletionsWithRawResponse + + return AsyncCompletionsWithRawResponse(self._client.completions) + + @cached_property + def chat(self) -> chat.AsyncChatWithRawResponse: + from .resources.chat import AsyncChatWithRawResponse + + return AsyncChatWithRawResponse(self._client.chat) + + @cached_property + def embeddings(self) -> embeddings.AsyncEmbeddingsWithRawResponse: + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ + from .resources.embeddings import AsyncEmbeddingsWithRawResponse + + return AsyncEmbeddingsWithRawResponse(self._client.embeddings) + + @cached_property + def files(self) -> files.AsyncFilesWithRawResponse: + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ + from .resources.files import AsyncFilesWithRawResponse + + return AsyncFilesWithRawResponse(self._client.files) + + @cached_property + def images(self) -> images.AsyncImagesWithRawResponse: + """Given a prompt and/or an input image, the model will generate a new image.""" + from .resources.images import AsyncImagesWithRawResponse + + return AsyncImagesWithRawResponse(self._client.images) + + @cached_property + def content_provenance_checks(self) -> content_provenance_checks.AsyncContentProvenanceChecksWithRawResponse: + from .resources.content_provenance_checks import AsyncContentProvenanceChecksWithRawResponse + + return AsyncContentProvenanceChecksWithRawResponse(self._client.content_provenance_checks) + + @cached_property + def audio(self) -> audio.AsyncAudioWithRawResponse: + from .resources.audio import AsyncAudioWithRawResponse + + return AsyncAudioWithRawResponse(self._client.audio) + + @cached_property + def moderations(self) -> moderations.AsyncModerationsWithRawResponse: + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ + from .resources.moderations import AsyncModerationsWithRawResponse + + return AsyncModerationsWithRawResponse(self._client.moderations) + + @cached_property + def models(self) -> models.AsyncModelsWithRawResponse: + """List and describe the various models available in the API.""" + from .resources.models import AsyncModelsWithRawResponse + + return AsyncModelsWithRawResponse(self._client.models) + + @cached_property + def fine_tuning(self) -> fine_tuning.AsyncFineTuningWithRawResponse: + from .resources.fine_tuning import AsyncFineTuningWithRawResponse + + return AsyncFineTuningWithRawResponse(self._client.fine_tuning) + + @cached_property + def vector_stores(self) -> vector_stores.AsyncVectorStoresWithRawResponse: + from .resources.vector_stores import AsyncVectorStoresWithRawResponse + + return AsyncVectorStoresWithRawResponse(self._client.vector_stores) + + @cached_property + def safety(self) -> safety.AsyncSafetyWithRawResponse: + from .resources.safety import AsyncSafetyWithRawResponse + + return AsyncSafetyWithRawResponse(self._client.safety) + + @cached_property + def webhooks(self) -> webhooks.AsyncWebhooksWithRawResponse: + from .resources.webhooks import AsyncWebhooksWithRawResponse + + return AsyncWebhooksWithRawResponse(self._client.webhooks) + + @cached_property + def beta(self) -> beta.AsyncBetaWithRawResponse: + from .resources.beta import AsyncBetaWithRawResponse + + return AsyncBetaWithRawResponse(self._client.beta) + + @cached_property + def batches(self) -> batches.AsyncBatchesWithRawResponse: + """Create large batches of API requests to run asynchronously.""" + from .resources.batches import AsyncBatchesWithRawResponse + + return AsyncBatchesWithRawResponse(self._client.batches) + + @cached_property + def uploads(self) -> uploads.AsyncUploadsWithRawResponse: + """Use Uploads to upload large files in multiple parts.""" + from .resources.uploads import AsyncUploadsWithRawResponse + + return AsyncUploadsWithRawResponse(self._client.uploads) + + @cached_property + def admin(self) -> admin.AsyncAdminWithRawResponse: + from .resources.admin import AsyncAdminWithRawResponse + + return AsyncAdminWithRawResponse(self._client.admin) + + @cached_property + def responses(self) -> responses.AsyncResponsesWithRawResponse: + """Create and manage model responses.""" + from .resources.responses import AsyncResponsesWithRawResponse + + return AsyncResponsesWithRawResponse(self._client.responses) + + @cached_property + def live(self) -> live.AsyncLiveWithRawResponse: + from .resources.live import AsyncLiveWithRawResponse + + return AsyncLiveWithRawResponse(self._client.live) + + @cached_property + def realtime(self) -> realtime.AsyncRealtimeWithRawResponse: + from .resources.realtime import AsyncRealtimeWithRawResponse + + return AsyncRealtimeWithRawResponse(self._client.realtime) + + @cached_property + def conversations(self) -> conversations.AsyncConversationsWithRawResponse: + """Manage conversations and conversation items.""" + from .resources.conversations import AsyncConversationsWithRawResponse + + return AsyncConversationsWithRawResponse(self._client.conversations) + + @cached_property + def evals(self) -> evals.AsyncEvalsWithRawResponse: + """Manage and run evals in the OpenAI platform.""" + from .resources.evals import AsyncEvalsWithRawResponse + + return AsyncEvalsWithRawResponse(self._client.evals) + + @cached_property + def containers(self) -> containers.AsyncContainersWithRawResponse: + from .resources.containers import AsyncContainersWithRawResponse + + return AsyncContainersWithRawResponse(self._client.containers) + + @cached_property + def skills(self) -> skills.AsyncSkillsWithRawResponse: + from .resources.skills import AsyncSkillsWithRawResponse + + return AsyncSkillsWithRawResponse(self._client.skills) + + @cached_property + def videos(self) -> videos.AsyncVideosWithRawResponse: + from .resources.videos import AsyncVideosWithRawResponse + + return AsyncVideosWithRawResponse(self._client.videos) + + +class OpenAIWithStreamedResponse: + _client: OpenAI + + def __init__(self, client: OpenAI) -> None: + self._client = client + + @cached_property + def completions(self) -> completions.CompletionsWithStreamingResponse: + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ + from .resources.completions import CompletionsWithStreamingResponse + + return CompletionsWithStreamingResponse(self._client.completions) + + @cached_property + def chat(self) -> chat.ChatWithStreamingResponse: + from .resources.chat import ChatWithStreamingResponse + + return ChatWithStreamingResponse(self._client.chat) + + @cached_property + def embeddings(self) -> embeddings.EmbeddingsWithStreamingResponse: + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ + from .resources.embeddings import EmbeddingsWithStreamingResponse + + return EmbeddingsWithStreamingResponse(self._client.embeddings) + + @cached_property + def files(self) -> files.FilesWithStreamingResponse: + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ + from .resources.files import FilesWithStreamingResponse + + return FilesWithStreamingResponse(self._client.files) + + @cached_property + def images(self) -> images.ImagesWithStreamingResponse: + """Given a prompt and/or an input image, the model will generate a new image.""" + from .resources.images import ImagesWithStreamingResponse + + return ImagesWithStreamingResponse(self._client.images) + + @cached_property + def content_provenance_checks(self) -> content_provenance_checks.ContentProvenanceChecksWithStreamingResponse: + from .resources.content_provenance_checks import ContentProvenanceChecksWithStreamingResponse + + return ContentProvenanceChecksWithStreamingResponse(self._client.content_provenance_checks) + + @cached_property + def audio(self) -> audio.AudioWithStreamingResponse: + from .resources.audio import AudioWithStreamingResponse + + return AudioWithStreamingResponse(self._client.audio) + + @cached_property + def moderations(self) -> moderations.ModerationsWithStreamingResponse: + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ + from .resources.moderations import ModerationsWithStreamingResponse + + return ModerationsWithStreamingResponse(self._client.moderations) + + @cached_property + def models(self) -> models.ModelsWithStreamingResponse: + """List and describe the various models available in the API.""" + from .resources.models import ModelsWithStreamingResponse + + return ModelsWithStreamingResponse(self._client.models) + + @cached_property + def fine_tuning(self) -> fine_tuning.FineTuningWithStreamingResponse: + from .resources.fine_tuning import FineTuningWithStreamingResponse + + return FineTuningWithStreamingResponse(self._client.fine_tuning) + + @cached_property + def vector_stores(self) -> vector_stores.VectorStoresWithStreamingResponse: + from .resources.vector_stores import VectorStoresWithStreamingResponse + + return VectorStoresWithStreamingResponse(self._client.vector_stores) + + @cached_property + def safety(self) -> safety.SafetyWithStreamingResponse: + from .resources.safety import SafetyWithStreamingResponse + + return SafetyWithStreamingResponse(self._client.safety) + + @cached_property + def webhooks(self) -> webhooks.WebhooksWithStreamingResponse: + from .resources.webhooks import WebhooksWithStreamingResponse + + return WebhooksWithStreamingResponse(self._client.webhooks) + + @cached_property + def beta(self) -> beta.BetaWithStreamingResponse: + from .resources.beta import BetaWithStreamingResponse + + return BetaWithStreamingResponse(self._client.beta) + + @cached_property + def batches(self) -> batches.BatchesWithStreamingResponse: + """Create large batches of API requests to run asynchronously.""" + from .resources.batches import BatchesWithStreamingResponse + + return BatchesWithStreamingResponse(self._client.batches) + + @cached_property + def uploads(self) -> uploads.UploadsWithStreamingResponse: + """Use Uploads to upload large files in multiple parts.""" + from .resources.uploads import UploadsWithStreamingResponse + + return UploadsWithStreamingResponse(self._client.uploads) + + @cached_property + def admin(self) -> admin.AdminWithStreamingResponse: + from .resources.admin import AdminWithStreamingResponse + + return AdminWithStreamingResponse(self._client.admin) + + @cached_property + def responses(self) -> responses.ResponsesWithStreamingResponse: + """Create and manage model responses.""" + from .resources.responses import ResponsesWithStreamingResponse + + return ResponsesWithStreamingResponse(self._client.responses) + + @cached_property + def live(self) -> live.LiveWithStreamingResponse: + from .resources.live import LiveWithStreamingResponse + + return LiveWithStreamingResponse(self._client.live) + + @cached_property + def realtime(self) -> realtime.RealtimeWithStreamingResponse: + from .resources.realtime import RealtimeWithStreamingResponse + + return RealtimeWithStreamingResponse(self._client.realtime) + + @cached_property + def conversations(self) -> conversations.ConversationsWithStreamingResponse: + """Manage conversations and conversation items.""" + from .resources.conversations import ConversationsWithStreamingResponse + + return ConversationsWithStreamingResponse(self._client.conversations) + + @cached_property + def evals(self) -> evals.EvalsWithStreamingResponse: + """Manage and run evals in the OpenAI platform.""" + from .resources.evals import EvalsWithStreamingResponse + + return EvalsWithStreamingResponse(self._client.evals) + + @cached_property + def containers(self) -> containers.ContainersWithStreamingResponse: + from .resources.containers import ContainersWithStreamingResponse + + return ContainersWithStreamingResponse(self._client.containers) + + @cached_property + def skills(self) -> skills.SkillsWithStreamingResponse: + from .resources.skills import SkillsWithStreamingResponse + + return SkillsWithStreamingResponse(self._client.skills) + + @cached_property + def videos(self) -> videos.VideosWithStreamingResponse: + from .resources.videos import VideosWithStreamingResponse + + return VideosWithStreamingResponse(self._client.videos) + + +class AsyncOpenAIWithStreamedResponse: + _client: AsyncOpenAI + + def __init__(self, client: AsyncOpenAI) -> None: + self._client = client + + @cached_property + def completions(self) -> completions.AsyncCompletionsWithStreamingResponse: + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ + from .resources.completions import AsyncCompletionsWithStreamingResponse + + return AsyncCompletionsWithStreamingResponse(self._client.completions) + + @cached_property + def chat(self) -> chat.AsyncChatWithStreamingResponse: + from .resources.chat import AsyncChatWithStreamingResponse + + return AsyncChatWithStreamingResponse(self._client.chat) + + @cached_property + def embeddings(self) -> embeddings.AsyncEmbeddingsWithStreamingResponse: + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ + from .resources.embeddings import AsyncEmbeddingsWithStreamingResponse + + return AsyncEmbeddingsWithStreamingResponse(self._client.embeddings) + + @cached_property + def files(self) -> files.AsyncFilesWithStreamingResponse: + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ + from .resources.files import AsyncFilesWithStreamingResponse + + return AsyncFilesWithStreamingResponse(self._client.files) + + @cached_property + def images(self) -> images.AsyncImagesWithStreamingResponse: + """Given a prompt and/or an input image, the model will generate a new image.""" + from .resources.images import AsyncImagesWithStreamingResponse + + return AsyncImagesWithStreamingResponse(self._client.images) + + @cached_property + def content_provenance_checks(self) -> content_provenance_checks.AsyncContentProvenanceChecksWithStreamingResponse: + from .resources.content_provenance_checks import AsyncContentProvenanceChecksWithStreamingResponse + + return AsyncContentProvenanceChecksWithStreamingResponse(self._client.content_provenance_checks) + + @cached_property + def audio(self) -> audio.AsyncAudioWithStreamingResponse: + from .resources.audio import AsyncAudioWithStreamingResponse + + return AsyncAudioWithStreamingResponse(self._client.audio) + + @cached_property + def moderations(self) -> moderations.AsyncModerationsWithStreamingResponse: + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ + from .resources.moderations import AsyncModerationsWithStreamingResponse + + return AsyncModerationsWithStreamingResponse(self._client.moderations) + + @cached_property + def models(self) -> models.AsyncModelsWithStreamingResponse: + """List and describe the various models available in the API.""" + from .resources.models import AsyncModelsWithStreamingResponse + + return AsyncModelsWithStreamingResponse(self._client.models) + + @cached_property + def fine_tuning(self) -> fine_tuning.AsyncFineTuningWithStreamingResponse: + from .resources.fine_tuning import AsyncFineTuningWithStreamingResponse + + return AsyncFineTuningWithStreamingResponse(self._client.fine_tuning) + + @cached_property + def vector_stores(self) -> vector_stores.AsyncVectorStoresWithStreamingResponse: + from .resources.vector_stores import AsyncVectorStoresWithStreamingResponse + + return AsyncVectorStoresWithStreamingResponse(self._client.vector_stores) + + @cached_property + def safety(self) -> safety.AsyncSafetyWithStreamingResponse: + from .resources.safety import AsyncSafetyWithStreamingResponse + + return AsyncSafetyWithStreamingResponse(self._client.safety) + + @cached_property + def webhooks(self) -> webhooks.AsyncWebhooksWithStreamingResponse: + from .resources.webhooks import AsyncWebhooksWithStreamingResponse + + return AsyncWebhooksWithStreamingResponse(self._client.webhooks) + + @cached_property + def beta(self) -> beta.AsyncBetaWithStreamingResponse: + from .resources.beta import AsyncBetaWithStreamingResponse + + return AsyncBetaWithStreamingResponse(self._client.beta) + + @cached_property + def batches(self) -> batches.AsyncBatchesWithStreamingResponse: + """Create large batches of API requests to run asynchronously.""" + from .resources.batches import AsyncBatchesWithStreamingResponse + + return AsyncBatchesWithStreamingResponse(self._client.batches) + + @cached_property + def uploads(self) -> uploads.AsyncUploadsWithStreamingResponse: + """Use Uploads to upload large files in multiple parts.""" + from .resources.uploads import AsyncUploadsWithStreamingResponse + + return AsyncUploadsWithStreamingResponse(self._client.uploads) + + @cached_property + def admin(self) -> admin.AsyncAdminWithStreamingResponse: + from .resources.admin import AsyncAdminWithStreamingResponse + + return AsyncAdminWithStreamingResponse(self._client.admin) + + @cached_property + def responses(self) -> responses.AsyncResponsesWithStreamingResponse: + """Create and manage model responses.""" + from .resources.responses import AsyncResponsesWithStreamingResponse + + return AsyncResponsesWithStreamingResponse(self._client.responses) + + @cached_property + def live(self) -> live.AsyncLiveWithStreamingResponse: + from .resources.live import AsyncLiveWithStreamingResponse + + return AsyncLiveWithStreamingResponse(self._client.live) + + @cached_property + def realtime(self) -> realtime.AsyncRealtimeWithStreamingResponse: + from .resources.realtime import AsyncRealtimeWithStreamingResponse + + return AsyncRealtimeWithStreamingResponse(self._client.realtime) + + @cached_property + def conversations(self) -> conversations.AsyncConversationsWithStreamingResponse: + """Manage conversations and conversation items.""" + from .resources.conversations import AsyncConversationsWithStreamingResponse + + return AsyncConversationsWithStreamingResponse(self._client.conversations) + + @cached_property + def evals(self) -> evals.AsyncEvalsWithStreamingResponse: + """Manage and run evals in the OpenAI platform.""" + from .resources.evals import AsyncEvalsWithStreamingResponse + + return AsyncEvalsWithStreamingResponse(self._client.evals) + + @cached_property + def containers(self) -> containers.AsyncContainersWithStreamingResponse: + from .resources.containers import AsyncContainersWithStreamingResponse + + return AsyncContainersWithStreamingResponse(self._client.containers) + + @cached_property + def skills(self) -> skills.AsyncSkillsWithStreamingResponse: + from .resources.skills import AsyncSkillsWithStreamingResponse + + return AsyncSkillsWithStreamingResponse(self._client.skills) + + @cached_property + def videos(self) -> videos.AsyncVideosWithStreamingResponse: + from .resources.videos import AsyncVideosWithStreamingResponse + + return AsyncVideosWithStreamingResponse(self._client.videos) + + +Client = OpenAI + +AsyncClient = AsyncOpenAI diff --git a/venv/lib/python3.12/site-packages/openai/_compat.py b/venv/lib/python3.12/site-packages/openai/_compat.py new file mode 100644 index 0000000..340c91a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_compat.py @@ -0,0 +1,238 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload +from datetime import date, datetime +from typing_extensions import Self, Literal, TypedDict + +import pydantic +from pydantic.fields import FieldInfo + +from ._types import IncEx, StrBytesIntFloat + +_T = TypeVar("_T") +_ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) + +# --------------- Pydantic v2, v3 compatibility --------------- + +# Pyright incorrectly reports some of our functions as overriding a method when they don't +# pyright: reportIncompatibleMethodOverride=false + +PYDANTIC_V1 = pydantic.VERSION.startswith("1.") + +if TYPE_CHECKING: + + def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001 + ... + + def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: # noqa: ARG001 + ... + + def get_args(t: type[Any]) -> tuple[Any, ...]: # noqa: ARG001 + ... + + def is_union(tp: type[Any] | None) -> bool: # noqa: ARG001 + ... + + def get_origin(t: type[Any]) -> type[Any] | None: # noqa: ARG001 + ... + + def is_literal_type(type_: type[Any]) -> bool: # noqa: ARG001 + ... + + def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 + ... + +else: + # v1 re-exports + if PYDANTIC_V1: + from pydantic.typing import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, + ) + from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + else: + from ._utils import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + parse_date as parse_date, + is_typeddict as is_typeddict, + parse_datetime as parse_datetime, + is_literal_type as is_literal_type, + ) + + +# refactored config +if TYPE_CHECKING: + from pydantic import ConfigDict as ConfigDict +else: + if PYDANTIC_V1: + # TODO: provide an error message here? + ConfigDict = None + else: + from pydantic import ConfigDict as ConfigDict + + +# renamed methods / properties +def parse_obj(model: type[_ModelT], value: object) -> _ModelT: + if PYDANTIC_V1: + return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + else: + return model.model_validate(value) + + +def field_is_required(field: FieldInfo) -> bool: + if PYDANTIC_V1: + return field.required # type: ignore + return field.is_required() + + +def field_get_default(field: FieldInfo) -> Any: + value = field.get_default() + if PYDANTIC_V1: + return value + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + + +def field_outer_type(field: FieldInfo) -> Any: + if PYDANTIC_V1: + return field.outer_type_ # type: ignore + return field.annotation + + +def get_model_config(model: type[pydantic.BaseModel]) -> Any: + if PYDANTIC_V1: + return model.__config__ # type: ignore + return model.model_config + + +def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: + if PYDANTIC_V1: + return model.__fields__ # type: ignore + return model.model_fields + + +def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: + if PYDANTIC_V1: + return model.copy(deep=deep) # type: ignore + return model.model_copy(deep=deep) + + +def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: + if PYDANTIC_V1: + return model.json(indent=indent) # type: ignore + return model.model_dump_json(indent=indent) + + +class _ModelDumpKwargs(TypedDict, total=False): + by_alias: bool + + +def model_dump( + model: pydantic.BaseModel, + *, + exclude: IncEx | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + warnings: bool = True, + mode: Literal["json", "python"] = "python", + by_alias: bool | None = None, +) -> dict[str, Any]: + if (not PYDANTIC_V1) or hasattr(model, "model_dump"): + kwargs: _ModelDumpKwargs = {} + if by_alias is not None: + kwargs["by_alias"] = by_alias + return model.model_dump( + mode=mode, + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + # warnings are not supported in Pydantic v1 + warnings=True if PYDANTIC_V1 else warnings, + **kwargs, + ) + return cast( + "dict[str, Any]", + model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias) + ), + ) + + +def model_parse(model: type[_ModelT], data: Any) -> _ModelT: + if PYDANTIC_V1: + return model.parse_obj(data) # pyright: ignore[reportDeprecated] + return model.model_validate(data) + + +def model_parse_json(model: type[_ModelT], data: str | bytes) -> _ModelT: + if PYDANTIC_V1: + return model.parse_raw(data) # pyright: ignore[reportDeprecated] + return model.model_validate_json(data) + + +def model_json_schema(model: type[_ModelT]) -> dict[str, Any]: + if PYDANTIC_V1: + return model.schema() # pyright: ignore[reportDeprecated] + return model.model_json_schema() + + +# generic models +if TYPE_CHECKING: + + class GenericModel(pydantic.BaseModel): ... + +else: + if PYDANTIC_V1: + import pydantic.generics + + class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... + else: + # there no longer needs to be a distinction in v2 but + # we still have to create our own subclass to avoid + # inconsistent MRO ordering errors + class GenericModel(pydantic.BaseModel): ... + + +# cached properties +if TYPE_CHECKING: + cached_property = property + + # we define a separate type (copied from typeshed) + # that represents that `cached_property` is `set`able + # at runtime, which differs from `@property`. + # + # this is a separate type as editors likely special case + # `@property` and we don't want to cause issues just to have + # more helpful internal types. + + class typed_cached_property(Generic[_T]): + func: Callable[[Any], _T] + attrname: str | None + + def __init__(self, func: Callable[[Any], _T]) -> None: ... + + @overload + def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... + + @overload + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... + + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T | Self: + raise NotImplementedError() + + def __set_name__(self, owner: type[Any], name: str) -> None: ... + + # __set__ is not defined at runtime, but @cached_property is designed to be settable + def __set__(self, instance: object, value: _T) -> None: ... +else: + from functools import cached_property as cached_property + + typed_cached_property = cached_property diff --git a/venv/lib/python3.12/site-packages/openai/_constants.py b/venv/lib/python3.12/site-packages/openai/_constants.py new file mode 100644 index 0000000..19d2d6b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_constants.py @@ -0,0 +1,13 @@ +import httpx2 + +RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" +OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to" + +# default timeout is 10 minutes +DEFAULT_TIMEOUT = httpx2.Timeout(timeout=600, connect=5.0) +DEFAULT_MAX_RETRIES = 2 +DEFAULT_CONNECTION_LIMITS = httpx2.Limits(max_connections=1000, max_keepalive_connections=100) + +INITIAL_RETRY_DELAY = 0.5 +MAX_RETRY_DELAY = 8.0 +MAX_RETRY_AFTER_DELAY = 2 * 60 diff --git a/venv/lib/python3.12/site-packages/openai/_data_residency.py b/venv/lib/python3.12/site-packages/openai/_data_residency.py new file mode 100644 index 0000000..965eb99 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_data_residency.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Literal, cast + +import httpx2 + +from ._types import NotGiven +from ._exceptions import OpenAIError + +DataResidency = Literal["global", "us", "eu", "ae"] + +_DATA_RESIDENCY_BASE_URLS: dict[DataResidency, str] = { + "global": "https://api.openai.com/v1", + "us": "https://us.api.openai.com/v1", + "eu": "https://eu.api.openai.com/v1", + "ae": "https://ae.api.openai.com/v1", +} + + +def resolve_data_residency( + data_residency: DataResidency | None, + base_url: str | httpx2.URL | None | NotGiven, + *, + provider: object | None = None, + websocket_base_url: str | httpx2.URL | None = None, +) -> str | httpx2.URL | None: + """Resolve a named endpoint before inherited or environment options are applied.""" + if data_residency is None: + return None if isinstance(base_url, NotGiven) else base_url + if not isinstance(base_url, NotGiven): + raise ValueError("The `data_residency` and `base_url` arguments are mutually exclusive") + if websocket_base_url is not None: + raise ValueError("The `data_residency` and `websocket_base_url` arguments are mutually exclusive") + if provider is not None: + raise OpenAIError("The `data_residency` and `provider` arguments are mutually exclusive") + if not isinstance(cast(object, data_residency), str) or data_residency not in _DATA_RESIDENCY_BASE_URLS: + raise ValueError("Invalid `data_residency`; expected one of 'global', 'us', 'eu', or 'ae'") + return _DATA_RESIDENCY_BASE_URLS[data_residency] diff --git a/venv/lib/python3.12/site-packages/openai/_event_handler.py b/venv/lib/python3.12/site-packages/openai/_event_handler.py new file mode 100644 index 0000000..3aa6370 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_event_handler.py @@ -0,0 +1,83 @@ +from __future__ import annotations + +import threading +from typing import Any, Callable + +EventHandler = Callable[..., Any] + + +class EventHandlerRegistry: + """Thread-safe (optional) registry of event handlers.""" + + def __init__(self, *, use_lock: bool = False) -> None: + self._handlers: dict[str, list[EventHandler]] = {} + self._once_ids: set[int] = set() + self._lock: threading.Lock | None = threading.Lock() if use_lock else None + + def _acquire(self) -> None: + if self._lock is not None: + self._lock.acquire() + + def _release(self) -> None: + if self._lock is not None: + self._lock.release() + + def add(self, event_type: str, handler: EventHandler, *, once: bool = False) -> None: + self._acquire() + try: + handlers = self._handlers.setdefault(event_type, []) + handlers.append(handler) + if once: + self._once_ids.add(id(handler)) + finally: + self._release() + + def remove(self, event_type: str, handler: EventHandler) -> None: + self._acquire() + try: + handlers = self._handlers.get(event_type) + if handlers is not None: + try: + handlers.remove(handler) + except ValueError: + pass + self._once_ids.discard(id(handler)) + finally: + self._release() + + def get_handlers(self, event_type: str) -> list[EventHandler]: + """Return a snapshot of handlers for the given event type, removing once-handlers.""" + self._acquire() + try: + handlers = self._handlers.get(event_type) + if not handlers: + return [] + result = list(handlers) + to_remove = [h for h in result if id(h) in self._once_ids] + for h in to_remove: + handlers.remove(h) + self._once_ids.discard(id(h)) + return result + finally: + self._release() + + def has_handlers(self, event_type: str) -> bool: + self._acquire() + try: + handlers = self._handlers.get(event_type) + return bool(handlers) + finally: + self._release() + + def merge_into(self, target: EventHandlerRegistry) -> None: + """Move all handlers from this registry into *target*, then clear self.""" + self._acquire() + try: + for event_type, handlers in self._handlers.items(): + for handler in handlers: + once = id(handler) in self._once_ids + target.add(event_type, handler, once=once) + self._handlers.clear() + self._once_ids.clear() + finally: + self._release() diff --git a/venv/lib/python3.12/site-packages/openai/_exceptions.py b/venv/lib/python3.12/site-packages/openai/_exceptions.py new file mode 100644 index 0000000..bdaa103 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_exceptions.py @@ -0,0 +1,213 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Optional, cast +from typing_extensions import Literal + +import httpx2 + +from ._utils import is_dict +from ._models import construct_type +from .types.shared.oauth_error_code import OAuthErrorCode + +if TYPE_CHECKING: + from .types.chat import ChatCompletion + +__all__ = [ + "BadRequestError", + "AuthenticationError", + "OAuthError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", + "LengthFinishReasonError", + "ContentFilterFinishReasonError", + "InvalidWebhookSignatureError", + "SubjectTokenProviderError", + "WebSocketConnectionClosedError", + "WebSocketQueueFullError", +] + + +class OpenAIError(Exception): + pass + + +class SubjectTokenProviderError(OpenAIError): + response: httpx2.Response | None + + def __init__(self, message: str, *, response: httpx2.Response | None = None) -> None: + super().__init__(message) + self.response = response + + +class APIError(OpenAIError): + message: str + request: httpx2.Request + + body: object | None + """The API response body. + + If the API responded with a valid JSON structure then this property will be the + decoded result. + + If it isn't a valid JSON structure then this will be the raw response. + + If there was no response associated with this error then it will be `None`. + """ + + code: Optional[str] = None + param: Optional[str] = None + type: Optional[str] + + def __init__(self, message: str, request: httpx2.Request, *, body: object | None) -> None: + super().__init__(message) + self.request = request + self.message = message + self.body = body + + if is_dict(body): + code = body.get("code") + self.code = str(code) if code is not None else None + self.param = cast(Any, construct_type(type_=Optional[str], value=body.get("param"))) + self.type = cast(Any, construct_type(type_=str, value=body.get("type"))) + else: + self.code = None + self.param = None + self.type = None + + +class APIResponseValidationError(APIError): + response: httpx2.Response + status_code: int + + def __init__(self, response: httpx2.Response, body: object | None, *, message: str | None = None) -> None: + super().__init__(message or "Data returned by API invalid for expected schema.", response.request, body=body) + self.response = response + self.status_code = response.status_code + + +class APIStatusError(APIError): + """Raised when an API response has a status code of 4xx or 5xx.""" + + response: httpx2.Response + status_code: int + request_id: str | None + + def __init__(self, message: str, *, response: httpx2.Response, body: object | None) -> None: + super().__init__(message, response.request, body=body) + self.response = response + self.status_code = response.status_code + self.request_id = response.headers.get("x-request-id") + + +class APIConnectionError(APIError): + def __init__(self, *, message: str = "Connection error.", request: httpx2.Request) -> None: + super().__init__(message, request, body=None) + + +class APITimeoutError(APIConnectionError): + def __init__(self, request: httpx2.Request) -> None: + super().__init__(message="Request timed out.", request=request) + + +class BadRequestError(APIStatusError): + status_code: Literal[400] = 400 # pyright: ignore[reportIncompatibleVariableOverride] + + +class AuthenticationError(APIStatusError): + status_code: Literal[401] = 401 # pyright: ignore[reportIncompatibleVariableOverride] + + +class OAuthError(AuthenticationError): + error: Optional[OAuthErrorCode] + + def __init__(self, *, response: httpx2.Response, body: object | None) -> None: + message = "OAuth authentication error." + error = None + + if is_dict(body): + error = body.get("error") + description = body.get("error_description") + if description and isinstance(description, str): + message = description + + super().__init__(message, response=response, body=body) + self.error = cast(Optional[OAuthErrorCode], error) + + +class PermissionDeniedError(APIStatusError): + status_code: Literal[403] = 403 # pyright: ignore[reportIncompatibleVariableOverride] + + +class NotFoundError(APIStatusError): + status_code: Literal[404] = 404 # pyright: ignore[reportIncompatibleVariableOverride] + + +class ConflictError(APIStatusError): + status_code: Literal[409] = 409 # pyright: ignore[reportIncompatibleVariableOverride] + + +class UnprocessableEntityError(APIStatusError): + status_code: Literal[422] = 422 # pyright: ignore[reportIncompatibleVariableOverride] + + +class RateLimitError(APIStatusError): + status_code: Literal[429] = 429 # pyright: ignore[reportIncompatibleVariableOverride] + + +class InternalServerError(APIStatusError): + pass + + +class LengthFinishReasonError(OpenAIError): + completion: ChatCompletion + """The completion that caused this error. + + Note: this will *not* be a complete `ChatCompletion` object when streaming as `usage` + will not be included. + """ + + def __init__(self, *, completion: ChatCompletion) -> None: + msg = "Could not parse response content as the length limit was reached" + if completion.usage: + msg += f" - {completion.usage}" + + super().__init__(msg) + self.completion = completion + + +class ContentFilterFinishReasonError(OpenAIError): + completion: ChatCompletion | None + """The completion that caused this error, if provided. + + When streaming, this is the accumulated completion and may not include usage. + """ + + def __init__(self, *, completion: ChatCompletion | None = None) -> None: + super().__init__( + "Could not parse response content as the request was rejected by the content filter", + ) + self.completion = completion + + +class InvalidWebhookSignatureError(ValueError): + """Raised when a webhook signature is invalid, meaning the computed signature does not match the expected signature.""" + + +class WebSocketConnectionClosedError(OpenAIError): + """Raised when a WebSocket connection closes with unsent messages.""" + + unsent_messages: list[str] + + def __init__(self, message: str, *, unsent_messages: list[str]) -> None: + super().__init__(message) + self.unsent_messages = unsent_messages + + +class WebSocketQueueFullError(OpenAIError): + """Raised when the outgoing WebSocket message queue exceeds its byte-size limit.""" + + pass diff --git a/venv/lib/python3.12/site-packages/openai/_extras/__init__.py b/venv/lib/python3.12/site-packages/openai/_extras/__init__.py new file mode 100644 index 0000000..692de24 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_extras/__init__.py @@ -0,0 +1,3 @@ +from .numpy_proxy import numpy as numpy, has_numpy as has_numpy +from .pandas_proxy import pandas as pandas +from .sounddevice_proxy import sounddevice as sounddevice diff --git a/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4d162d6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/_common.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/_common.cpython-312.pyc new file mode 100644 index 0000000..4785db5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/_common.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/numpy_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/numpy_proxy.cpython-312.pyc new file mode 100644 index 0000000..2575e61 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/numpy_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/pandas_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/pandas_proxy.cpython-312.pyc new file mode 100644 index 0000000..567cef2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/pandas_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/sounddevice_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/sounddevice_proxy.cpython-312.pyc new file mode 100644 index 0000000..1f7db07 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_extras/__pycache__/sounddevice_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_extras/_common.py b/venv/lib/python3.12/site-packages/openai/_extras/_common.py new file mode 100644 index 0000000..6e71720 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_extras/_common.py @@ -0,0 +1,21 @@ +from .._exceptions import OpenAIError + +INSTRUCTIONS = """ + +OpenAI error: + + missing `{library}` + +This feature requires additional dependencies: + + $ pip install openai[{extra}] + +""" + + +def format_instructions(*, library: str, extra: str) -> str: + return INSTRUCTIONS.format(library=library, extra=extra) + + +class MissingDependencyError(OpenAIError): + pass diff --git a/venv/lib/python3.12/site-packages/openai/_extras/numpy_proxy.py b/venv/lib/python3.12/site-packages/openai/_extras/numpy_proxy.py new file mode 100644 index 0000000..2b06695 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_extras/numpy_proxy.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any +from typing_extensions import override + +from .._utils import LazyProxy +from ._common import MissingDependencyError, format_instructions + +if TYPE_CHECKING: + import numpy as numpy + + +NUMPY_INSTRUCTIONS = format_instructions(library="numpy", extra="voice_helpers") + + +class NumpyProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + try: + import numpy + except ImportError as err: + raise MissingDependencyError(NUMPY_INSTRUCTIONS) from err + + return numpy + + +if not TYPE_CHECKING: + numpy = NumpyProxy() + + +def has_numpy() -> bool: + try: + import numpy # noqa: F401 # pyright: ignore[reportUnusedImport] + except ImportError: + return False + + return True diff --git a/venv/lib/python3.12/site-packages/openai/_extras/pandas_proxy.py b/venv/lib/python3.12/site-packages/openai/_extras/pandas_proxy.py new file mode 100644 index 0000000..686377b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_extras/pandas_proxy.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any +from typing_extensions import override + +from .._utils import LazyProxy +from ._common import MissingDependencyError, format_instructions + +if TYPE_CHECKING: + import pandas as pandas + + +PANDAS_INSTRUCTIONS = format_instructions(library="pandas", extra="datalib") + + +class PandasProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + try: + import pandas + except ImportError as err: + raise MissingDependencyError(PANDAS_INSTRUCTIONS) from err + + return pandas + + +if not TYPE_CHECKING: + pandas = PandasProxy() diff --git a/venv/lib/python3.12/site-packages/openai/_extras/sounddevice_proxy.py b/venv/lib/python3.12/site-packages/openai/_extras/sounddevice_proxy.py new file mode 100644 index 0000000..482d4c6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_extras/sounddevice_proxy.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any +from typing_extensions import override + +from .._utils import LazyProxy +from ._common import MissingDependencyError, format_instructions + +if TYPE_CHECKING: + import sounddevice as sounddevice # type: ignore + + +SOUNDDEVICE_INSTRUCTIONS = format_instructions(library="sounddevice", extra="voice_helpers") + + +class SounddeviceProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + try: + import sounddevice # type: ignore + except ImportError as err: + raise MissingDependencyError(SOUNDDEVICE_INSTRUCTIONS) from err + + return sounddevice + + +if not TYPE_CHECKING: + sounddevice = SounddeviceProxy() diff --git a/venv/lib/python3.12/site-packages/openai/_files.py b/venv/lib/python3.12/site-packages/openai/_files.py new file mode 100644 index 0000000..76f7123 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_files.py @@ -0,0 +1,171 @@ +from __future__ import annotations + +import io +import os +import pathlib +from typing import Sequence, cast, overload +from typing_extensions import TypeVar, TypeGuard + +import anyio + +from ._types import ( + FileTypes, + FileContent, + RequestFiles, + HttpxFileTypes, + Base64FileInput, + HttpxFileContent, + HttpxRequestFiles, +) +from ._utils import is_list, is_mapping, is_tuple_t, is_mapping_t, is_sequence_t + +_T = TypeVar("_T") + + +def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: + return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + + +def is_file_content(obj: object) -> TypeGuard[FileContent]: + return isinstance(obj, bytes) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + + +def assert_is_file_content(obj: object, *, key: str | None = None) -> None: + if not is_file_content(obj) and not is_tuple_t(obj): + prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" + raise RuntimeError( + f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/openai/openai-python/tree/main#file-uploads" + ) from None + + +@overload +def to_httpx_files(files: None) -> None: ... + + +@overload +def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... + + +def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: _transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, _transform_file(file)) for key, file in files] + else: + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +def _transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = pathlib.Path(file) + return (path.name, path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +def read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return pathlib.Path(file).read_bytes() + return file + + +@overload +async def async_to_httpx_files(files: None) -> None: ... + + +@overload +async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... + + +async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: await _async_transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, await _async_transform_file(file)) for key, file in files] + else: + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +async def _async_transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = anyio.Path(file) + return (path.name, await path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], await async_read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +async def async_read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return await anyio.Path(file).read_bytes() + + return file + + +def deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]]) -> _T: + """Copy only the containers along the given paths. + + Used to guard against mutation by extract_files without copying the entire structure. + Only dicts and lists that lie on a path are copied; everything else + is returned by reference. + + For example, given paths=[["foo", "files", "file"]] and the structure: + { + "foo": { + "bar": {"baz": {}}, + "files": {"file": } + } + } + The root dict, "foo", and "files" are copied (they lie on the path). + "bar" and "baz" are returned by reference (off the path). + """ + return _deepcopy_with_paths(item, paths, 0) + + +def _deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]], index: int) -> _T: + if not paths: + return item + if is_mapping(item): + key_to_paths: dict[str, list[Sequence[str]]] = {} + for path in paths: + if index < len(path): + key_to_paths.setdefault(path[index], []).append(path) + + # if no path continues through this mapping, it won't be mutated and copying it is redundant + if not key_to_paths: + return item + + result = dict(item) + for key, subpaths in key_to_paths.items(): + if key in result: + result[key] = _deepcopy_with_paths(result[key], subpaths, index + 1) + return cast(_T, result) + if is_list(item): + array_paths = [path for path in paths if index < len(path) and path[index] == ""] + + # if no path expects a list here, nothing will be mutated inside it - return by reference + if not array_paths: + return cast(_T, item) + return cast(_T, [_deepcopy_with_paths(entry, array_paths, index + 1) for entry in item]) + return item diff --git a/venv/lib/python3.12/site-packages/openai/_httpx2.py b/venv/lib/python3.12/site-packages/openai/_httpx2.py new file mode 100644 index 0000000..48222a1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_httpx2.py @@ -0,0 +1,151 @@ +from __future__ import annotations + +import ssl +import sys +from typing import Any, Protocol, cast + +import anyio +import httpx2 + +from ._constants import DEFAULT_TIMEOUT, DEFAULT_CONNECTION_LIMITS + + +class _LegacyHttpxModule(Protocol): + Auth: type[httpx2.Auth] + Client: type[httpx2.Client] + AsyncClient: type[httpx2.AsyncClient] + URL: type[httpx2.URL] + Response: type[httpx2.Response] + Timeout: type[httpx2.Timeout] + Limits: type[httpx2.Limits] + TimeoutException: type[httpx2.TimeoutException] + RequestError: type[httpx2.RequestError] + HTTPStatusError: type[httpx2.HTTPStatusError] + StreamConsumed: type[httpx2.StreamConsumed] + RequestNotRead: type[httpx2.RequestNotRead] + + +def _loaded_legacy_httpx() -> _LegacyHttpxModule | None: + module = sys.modules.get("httpx") + return cast(_LegacyHttpxModule, module) if module is not None else None + + +def is_httpx2_sync_client(value: object) -> bool: + return isinstance(value, httpx2.Client) + + +def is_httpx2_async_client(value: object) -> bool: + return isinstance(value, httpx2.AsyncClient) + + +def is_legacy_httpx_sync_client(value: object) -> bool: + module = _loaded_legacy_httpx() + return module is not None and isinstance(value, module.Client) + + +def is_legacy_httpx_async_client(value: object) -> bool: + module = _loaded_legacy_httpx() + return module is not None and isinstance(value, module.AsyncClient) + + +def normalize_httpx_url(value: str | httpx2.URL) -> httpx2.URL: + if isinstance(value, httpx2.URL): + return value + + module = _loaded_legacy_httpx() + legacy_value: object = value + if module is not None and isinstance(legacy_value, module.URL): + return httpx2.URL(str(legacy_value)) + + return httpx2.URL(value) + + +def http_response_types() -> tuple[type[httpx2.Response], ...]: + module = _loaded_legacy_httpx() + return (httpx2.Response,) if module is None else (httpx2.Response, module.Response) + + +def normalize_httpx_timeout(value: float | httpx2.Timeout | None) -> float | httpx2.Timeout | None: + module = _loaded_legacy_httpx() + if module is not None and isinstance(value, module.Timeout): + return httpx2.Timeout(**value.as_dict()) + return value + + +def normalize_httpx2_timeout(value: float | httpx2.Timeout | None) -> float | httpx2.Timeout | None: + return normalize_httpx_timeout(value) + + +def normalize_legacy_httpx_timeout(value: float | httpx2.Timeout | None) -> float | httpx2.Timeout | None: + module = _loaded_legacy_httpx() + if module is not None and isinstance(value, httpx2.Timeout): + return module.Timeout(**value.as_dict()) + return value + + +def normalize_httpx2_auth(value: httpx2.Auth) -> httpx2.Auth: + module = _loaded_legacy_httpx() + if module is not None and type(value) is module.Auth: + return httpx2.Auth() + return value + + +def normalize_legacy_httpx_auth(value: httpx2.Auth) -> httpx2.Auth: + module = _loaded_legacy_httpx() + if module is not None and type(value) is httpx2.Auth: + return module.Auth() + return value + + +def timeout_exceptions() -> tuple[type[httpx2.TimeoutException], ...]: + module = _loaded_legacy_httpx() + return (httpx2.TimeoutException,) if module is None else (httpx2.TimeoutException, module.TimeoutException) + + +def request_exceptions() -> tuple[type[Exception], ...]: + module = _loaded_legacy_httpx() + # Shim unmapped AnyIO TLS failures pending https://github.com/pydantic/httpx2/issues/854. + errors = (httpx2.RequestError, ssl.SSLError, anyio.EndOfStream) + return errors if module is None else (*errors, module.RequestError) + + +def status_exceptions() -> tuple[type[httpx2.HTTPStatusError], ...]: + module = _loaded_legacy_httpx() + return (httpx2.HTTPStatusError,) if module is None else (httpx2.HTTPStatusError, module.HTTPStatusError) + + +def stream_consumed_exceptions() -> tuple[type[httpx2.StreamConsumed], ...]: + module = _loaded_legacy_httpx() + return (httpx2.StreamConsumed,) if module is None else (httpx2.StreamConsumed, module.StreamConsumed) + + +def request_not_read_exceptions() -> tuple[type[httpx2.RequestNotRead], ...]: + module = _loaded_legacy_httpx() + return (httpx2.RequestNotRead,) if module is None else (httpx2.RequestNotRead, module.RequestNotRead) + + +def _set_httpx2_defaults(kwargs: dict[str, Any]) -> None: + kwargs["timeout"] = normalize_httpx2_timeout(kwargs.get("timeout", DEFAULT_TIMEOUT)) + + limits = kwargs.get("limits", DEFAULT_CONNECTION_LIMITS) + module = _loaded_legacy_httpx() + if module is not None and isinstance(limits, module.Limits): + limits = httpx2.Limits( + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + ) + kwargs["limits"] = limits + kwargs.setdefault("follow_redirects", True) + + +def DefaultHttpx2Client(**kwargs: Any) -> httpx2.Client: + """Create an HTTPX2 client with the SDK's recommended defaults.""" + _set_httpx2_defaults(kwargs) + return httpx2.Client(**kwargs) + + +def DefaultAsyncHttpx2Client(**kwargs: Any) -> httpx2.AsyncClient: + """Create an async HTTPX2 client with the SDK's recommended defaults.""" + _set_httpx2_defaults(kwargs) + return httpx2.AsyncClient(**kwargs) diff --git a/venv/lib/python3.12/site-packages/openai/_legacy_response.py b/venv/lib/python3.12/site-packages/openai/_legacy_response.py new file mode 100644 index 0000000..a66bca2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_legacy_response.py @@ -0,0 +1,493 @@ +from __future__ import annotations + +import os +import inspect +import logging +import datetime +import functools +from typing import ( + TYPE_CHECKING, + Any, + Union, + Generic, + TypeVar, + Callable, + Iterator, + AsyncIterator, + cast, + overload, +) +from typing_extensions import Awaitable, ParamSpec, override, deprecated, get_origin + +import anyio +import httpx2 +import pydantic + +from ._types import NoneType +from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type +from ._httpx2 import http_response_types +from ._models import BaseModel, is_basemodel, add_request_id +from ._constants import RAW_RESPONSE_HEADER +from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type +from ._exceptions import APIResponseValidationError + +if TYPE_CHECKING: + from ._models import FinalRequestOptions + from ._base_client import BaseClient + + +P = ParamSpec("P") +R = TypeVar("R") +_T = TypeVar("_T") + +log: logging.Logger = logging.getLogger(__name__) + + +class LegacyAPIResponse(Generic[R]): + """This is a legacy class as it will be replaced by `APIResponse` + and `AsyncAPIResponse` in the `_response.py` file in the next major + release. + + For the sync client this will mostly be the same with the exception + of `content` & `text` will be methods instead of properties. In the + async client, all methods will be async. + + A migration script will be provided & the migration in general should + be smooth. + """ + + _cast_to: type[R] + _client: BaseClient[Any, Any] + _parsed_by_type: dict[type[Any], Any] + _stream: bool + _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None + _options: FinalRequestOptions + + http_response: httpx2.Response + + retries_taken: int + """The number of retries made. If no retries happened this will be `0`""" + + def __init__( + self, + *, + raw: httpx2.Response, + cast_to: type[R], + client: BaseClient[Any, Any], + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + options: FinalRequestOptions, + retries_taken: int = 0, + ) -> None: + self._cast_to = cast_to + self._client = client + self._parsed_by_type = {} + self._stream = stream + self._stream_cls = stream_cls + self._options = options + self.http_response = raw + self.retries_taken = retries_taken + + @property + def request_id(self) -> str | None: + return self.http_response.headers.get("x-request-id") # type: ignore[no-any-return] + + @overload + def parse(self, *, to: type[_T]) -> _T: ... + + @overload + def parse(self) -> R: ... + + def parse(self, *, to: type[_T] | None = None) -> R | _T: + """Returns the rich python representation of this response's data. + + NOTE: For the async client: this will become a coroutine in the next major version. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from openai import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `int` + - `float` + - `httpx2.Response` + """ + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] + + parsed = self._parse(to=to) + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + if isinstance(parsed, BaseModel): + add_request_id(parsed, self.request_id) + + self._parsed_by_type[cache_key] = parsed + return cast(R, parsed) + + @property + def headers(self) -> httpx2.Headers: + return self.http_response.headers + + @property + def http_request(self) -> httpx2.Request: + return self.http_response.request + + @property + def status_code(self) -> int: + return self.http_response.status_code + + @property + def url(self) -> httpx2.URL: + return self.http_response.url + + @property + def method(self) -> str: + return self.http_request.method + + @property + def content(self) -> bytes: + """Return the binary response content. + + NOTE: this will be removed in favour of `.read()` in the + next major version. + """ + return self.http_response.content + + @property + def text(self) -> str: + """Return the decoded response content. + + NOTE: this will be turned into a method in the next major version. + """ + return self.http_response.text + + @property + def http_version(self) -> str: + return self.http_response.http_version + + @property + def is_closed(self) -> bool: + return self.http_response.is_closed + + @property + def elapsed(self) -> datetime.timedelta: + """The time taken for the complete request/response cycle to complete.""" + return self.http_response.elapsed + + def _parse(self, *, to: type[_T] | None = None) -> R | _T: + cast_to = to if to is not None else self._cast_to + + # unwrap `TypeAlias('Name', T)` -> `T` + if is_type_alias_type(cast_to): + cast_to = cast_to.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if cast_to and is_annotated_type(cast_to): + cast_to = extract_type_arg(cast_to, 0) + + origin = get_origin(cast_to) or cast_to + + if self._stream: + if to: + if not is_stream_class_type(to): + raise TypeError(f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}") + + return cast( + _T, + to( + cast_to=extract_stream_chunk_type( + to, + failure_message="Expected custom stream type to be passed with a type argument, e.g. Stream[ChunkType]", + ), + response=self.http_response, + client=cast(Any, self._client), + options=self._options, + ), + ) + + if self._stream_cls: + return cast( + R, + self._stream_cls( + cast_to=extract_stream_chunk_type(self._stream_cls), + response=self.http_response, + client=cast(Any, self._client), + options=self._options, + ), + ) + + stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls) + if stream_cls is None: + raise MissingStreamClassError() + + return cast( + R, + stream_cls( + cast_to=cast_to, + response=self.http_response, + client=cast(Any, self._client), + options=self._options, + ), + ) + + if cast_to is NoneType: + return cast(R, None) + + response = self.http_response + if cast_to == str: + return cast(R, response.text) + + if cast_to == int: + return cast(R, int(response.text)) + + if cast_to == float: + return cast(R, float(response.text)) + + if cast_to == bool: + return cast(R, response.text.lower() == "true") + + if inspect.isclass(origin) and issubclass(origin, HttpxBinaryResponseContent): + return cast(R, cast_to(response)) # type: ignore + + if origin == LegacyAPIResponse: + raise RuntimeError("Unexpected state - cast_to is `APIResponse`") + + response_types = http_response_types() + if inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) and issubclass(origin, response_types): + # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response + # and pass that class to our request functions. We cannot change the variance to be either + # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct + # the response class ourselves but that is something that should be supported directly in httpx + # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. + if cast_to not in response_types: + raise ValueError("Subclasses of HTTP response classes cannot be passed to `cast_to`") + return cast(R, response) + + if ( + inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) + and not issubclass(origin, BaseModel) + and issubclass(origin, pydantic.BaseModel) + ): + raise TypeError("Pydantic models must subclass our base model type, e.g. `from openai import BaseModel`") + + if ( + cast_to is not object + and not origin is list + and not origin is dict + and not origin is Union + and not issubclass(origin, BaseModel) + ): + raise RuntimeError( + f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx2.Response}." + ) + + # split is required to handle cases where additional information is included + # in the response, e.g. application/json; charset=utf-8 + content_type, *_ = response.headers.get("content-type", "*").split(";") + if not content_type.endswith("json"): + if is_basemodel(cast_to): + try: + data = response.json() + except Exception as exc: + log.debug("Could not read JSON from response data due to %s", type(exc).__name__) + else: + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + if self._client._strict_response_validation: + raise APIResponseValidationError( + response=response, + message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", + body=response.text, + ) + + # If the API responds with content that isn't JSON then we just return + # the (decoded) text without performing any parsing so that you can still + # handle the response however you need to. + return response.text # type: ignore + + data = response.json() + + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + @override + def __repr__(self) -> str: + return f"" + + +class MissingStreamClassError(TypeError): + def __init__(self) -> None: + super().__init__( + "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `openai._streaming` for reference", + ) + + +def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, LegacyAPIResponse[R]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "true" + + kwargs["extra_headers"] = extra_headers + + return cast(LegacyAPIResponse[R], func(*args, **kwargs)) + + return wrapped + + +def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[LegacyAPIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + async def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "true" + + kwargs["extra_headers"] = extra_headers + + return cast(LegacyAPIResponse[R], await func(*args, **kwargs)) + + return wrapped + + +class HttpxBinaryResponseContent: + response: httpx2.Response + + def __init__(self, response: httpx2.Response) -> None: + self.response = response + + @property + def content(self) -> bytes: + return self.response.content + + @property + def text(self) -> str: + return self.response.text + + @property + def encoding(self) -> str | None: + return self.response.encoding + + @property + def charset_encoding(self) -> str | None: + return self.response.charset_encoding + + def json(self, **kwargs: Any) -> Any: + return self.response.json(**kwargs) + + def read(self) -> bytes: + return self.response.read() + + def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: + return self.response.iter_bytes(chunk_size) + + def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: + return self.response.iter_text(chunk_size) + + def iter_lines(self) -> Iterator[str]: + return self.response.iter_lines() + + def iter_raw(self, chunk_size: int | None = None) -> Iterator[bytes]: + return self.response.iter_raw(chunk_size) + + def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `client.with_streaming_response.foo().stream_to_file('my_filename.txt')` + """ + with open(file, mode="wb") as f: + for data in self.response.iter_bytes(): + f.write(data) + + @deprecated( + "Due to a bug, this method doesn't actually stream the response content, `.with_streaming_response.method()` should be used instead" + ) + def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + with open(file, mode="wb") as f: + for data in self.response.iter_bytes(chunk_size): + f.write(data) + + def close(self) -> None: + return self.response.close() + + async def aread(self) -> bytes: + return await self.response.aread() + + async def aiter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: + return self.response.aiter_bytes(chunk_size) + + async def aiter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: + return self.response.aiter_text(chunk_size) + + async def aiter_lines(self) -> AsyncIterator[str]: + return self.response.aiter_lines() + + async def aiter_raw(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: + return self.response.aiter_raw(chunk_size) + + @deprecated( + "Due to a bug, this method doesn't actually stream the response content, `.with_streaming_response.method()` should be used instead" + ) + async def astream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.response.aiter_bytes(chunk_size): + await f.write(data) + + async def aclose(self) -> None: + return await self.response.aclose() diff --git a/venv/lib/python3.12/site-packages/openai/_models.py b/venv/lib/python3.12/site-packages/openai/_models.py new file mode 100644 index 0000000..f0d2f97 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_models.py @@ -0,0 +1,1006 @@ +from __future__ import annotations + +import os +import inspect +import weakref +from typing import ( + IO, + TYPE_CHECKING, + Any, + Type, + Tuple, + Union, + Generic, + TypeVar, + Callable, + Iterable, + Optional, + AsyncIterable, + cast, +) +from datetime import date, datetime +from typing_extensions import ( + List, + Unpack, + Literal, + ClassVar, + Protocol, + Required, + Sequence, + Annotated, + ParamSpec, + TypeAlias, + TypedDict, + TypeGuard, + final, + override, + runtime_checkable, +) + +import pydantic +from pydantic.fields import FieldInfo + +from ._types import ( + Body, + IncEx, + Query, + ModelT, + Headers, + Timeout, + NotGiven, + AnyMapping, + HttpxRequestFiles, +) +from ._utils import ( + PropertyInfo, + is_list, + is_given, + json_safe, + lru_cache, + is_mapping, + parse_date, + coerce_boolean, + parse_datetime, + strip_not_given, + extract_type_arg, + is_annotated_type, + is_type_alias_type, + strip_annotated_type, +) +from ._compat import ( + PYDANTIC_V1, + ConfigDict, + GenericModel as BaseGenericModel, + get_args, + is_union, + parse_obj, + get_origin, + is_literal_type, + get_model_config, + get_model_fields, + field_get_default, +) +from ._constants import RAW_RESPONSE_HEADER + +if TYPE_CHECKING: + from pydantic import GetCoreSchemaHandler, ValidatorFunctionWrapHandler + from pydantic_core import CoreSchema, core_schema + from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema +else: + try: + from pydantic_core import CoreSchema, core_schema + except ImportError: + CoreSchema = None + core_schema = None + +__all__ = ["BaseModel", "GenericModel"] + +_T = TypeVar("_T") +_BaseModelT = TypeVar("_BaseModelT", bound="BaseModel") + +P = ParamSpec("P") + +ReprArgs = Sequence[Tuple[Optional[str], Any]] + + +@runtime_checkable +class _ConfigProtocol(Protocol): + allow_population_by_field_name: bool + + +class BaseModel(pydantic.BaseModel): + if PYDANTIC_V1: + + @property + @override + def model_fields_set(self) -> set[str]: + # a forwards-compat shim for pydantic v2 + return self.__fields_set__ # type: ignore + + class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] + extra: Any = pydantic.Extra.allow # type: ignore + + @override + def __repr_args__(self) -> ReprArgs: + # we don't want these attributes to be included when something like `rich.print` is used + return [arg for arg in super().__repr_args__() if arg[0] not in {"_request_id", "__exclude_fields__"}] + else: + model_config: ClassVar[ConfigDict] = ConfigDict( + extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) + ) + + if TYPE_CHECKING: + _request_id: Optional[str] = None + """The ID of the request, returned via the X-Request-ID header. Useful for debugging requests and reporting issues to OpenAI. + + This will **only** be set for the top-level response object, it will not be defined for nested objects. For example: + + ```py + completion = await client.chat.completions.create(...) + completion._request_id # req_id_xxx + completion.usage._request_id # raises `AttributeError` + ``` + + Note: unlike other properties that use an `_` prefix, this property + *is* public. Unless documented otherwise, all other `_` prefix properties, + methods and modules are *private*. + """ + + def to_dict( + self, + *, + mode: Literal["json", "python"] = "python", + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> dict[str, object]: + """Recursively generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + mode: + If mode is 'json', the dictionary will only contain JSON serializable types. e.g. `datetime` will be turned into a string, `"2024-3-22T18:11:19.117000Z"`. + If mode is 'python', the dictionary may contain any Python objects. e.g. `datetime(2024, 3, 22)` + + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value from the output. + exclude_none: Whether to exclude fields that have a value of `None` from the output. + warnings: Whether to log warnings when invalid fields are encountered. This is only supported in Pydantic v2. + """ + return self.model_dump( + mode=mode, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + def to_json( + self, + *, + indent: int | None = 2, + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> str: + """Generates a JSON string representing this model as it would be received from or sent to the API (but with indentation). + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + indent: Indentation to use in the JSON output. If `None` is passed, the output will be compact. Defaults to `2` + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that have the default value. + exclude_none: Whether to exclude fields that have a value of `None`. + warnings: Whether to show any warnings that occurred during serialization. This is only supported in Pydantic v2. + """ + return self.model_dump_json( + indent=indent, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + @override + def __str__(self) -> str: + # mypy complains about an invalid self arg + return f"{self.__repr_name__()}({self.__repr_str__(', ')})" # type: ignore[misc] + + # Override the 'construct' method in a way that supports recursive parsing without validation. + # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836. + @classmethod + @override + def construct( # pyright: ignore[reportIncompatibleMethodOverride] + __cls: Type[ModelT], + _fields_set: set[str] | None = None, + **values: object, + ) -> ModelT: + m = __cls.__new__(__cls) + fields_values: dict[str, object] = {} + + config = get_model_config(__cls) + populate_by_name = ( + config.allow_population_by_field_name + if isinstance(config, _ConfigProtocol) + else config.get("populate_by_name") + ) + + if _fields_set is None: + _fields_set = set() + + model_fields = get_model_fields(__cls) + for name, field in model_fields.items(): + key = field.alias + if key is None or (key not in values and populate_by_name): + key = name + + if key in values: + fields_values[name] = _construct_field(value=values[key], field=field, key=key) + _fields_set.add(name) + else: + fields_values[name] = field_get_default(field) + + extra_field_type = _get_extra_fields_type(__cls) + + _extra = {} + for key, value in values.items(): + if key not in model_fields: + parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value + + if PYDANTIC_V1: + _fields_set.add(key) + fields_values[key] = parsed + else: + _extra[key] = parsed + + object.__setattr__(m, "__dict__", fields_values) + + if PYDANTIC_V1: + # init_private_attributes() does not exist in v2 + m._init_private_attributes() # type: ignore + + # copied from Pydantic v1's `construct()` method + object.__setattr__(m, "__fields_set__", _fields_set) + else: + # these properties are copied from Pydantic's `model_construct()` method + object.__setattr__(m, "__pydantic_private__", None) + object.__setattr__(m, "__pydantic_extra__", _extra) + object.__setattr__(m, "__pydantic_fields_set__", _fields_set) + + return m + + if not TYPE_CHECKING: + # type checkers incorrectly complain about this assignment + # because the type signatures are technically different + # although not in practice + model_construct = construct + + if PYDANTIC_V1: + # we define aliases for some of the new pydantic v2 methods so + # that we can just document these methods without having to specify + # a specific pydantic version as some users may not know which + # pydantic version they are currently using + + @override + def model_dump( + self, + *, + mode: Literal["json", "python"] | str = "python", + include: IncEx | None = None, + exclude: IncEx | None = None, + context: Any | None = None, + by_alias: bool | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + exclude_computed_fields: bool = False, + round_trip: bool = False, + warnings: bool | Literal["none", "warn", "error"] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + ) -> dict[str, Any]: + """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump + + Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + Args: + mode: The mode in which `to_python` should run. + If mode is 'json', the output will only contain JSON serializable types. + If mode is 'python', the output may contain non-JSON-serializable Python objects. + include: A set of fields to include in the output. + exclude: A set of fields to exclude from the output. + context: Additional context to pass to the serializer. + by_alias: Whether to use the field's alias in the dictionary key if defined. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. + While this can be useful for round-tripping, it is usually recommended to use the dedicated + `round_trip` parameter instead. + round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered. If not provided, + a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + + Returns: + A dictionary representation of the model. + """ + if mode not in {"json", "python"}: + raise ValueError("mode must be either 'json' or 'python'") + if round_trip != False: + raise ValueError("round_trip is only supported in Pydantic v2") + if warnings != True: + raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") + dumped = super().dict( # pyright: ignore[reportDeprecated] + include=include, + exclude=exclude, + by_alias=by_alias if by_alias is not None else False, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + return cast("dict[str, Any]", json_safe(dumped)) if mode == "json" else dumped + + @override + def model_dump_json( + self, + *, + indent: int | None = None, + ensure_ascii: bool = False, + include: IncEx | None = None, + exclude: IncEx | None = None, + context: Any | None = None, + by_alias: bool | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + exclude_computed_fields: bool = False, + round_trip: bool = False, + warnings: bool | Literal["none", "warn", "error"] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + ) -> str: + """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json + + Generates a JSON representation of the model using Pydantic's `to_json` method. + + Args: + indent: Indentation to use in the JSON output. If None is passed, the output will be compact. + include: Field(s) to include in the JSON output. Can take either a string or set of strings. + exclude: Field(s) to exclude from the JSON output. Can take either a string or set of strings. + by_alias: Whether to serialize using field aliases. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that have the default value. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: Whether to use serialization/deserialization between JSON and class instance. + warnings: Whether to show any warnings that occurred during serialization. + + Returns: + A JSON string representation of the model. + """ + if round_trip != False: + raise ValueError("round_trip is only supported in Pydantic v2") + if warnings != True: + raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") + if ensure_ascii != False: + raise ValueError("ensure_ascii is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") + return super().json( # type: ignore[reportDeprecated] + indent=indent, + include=include, + exclude=exclude, + by_alias=by_alias if by_alias is not None else False, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + +class _EagerIterable(list[_T], Generic[_T]): + """ + Accepts any Iterable[T] input (including generators), consumes it + eagerly, and validates all items upfront. + + Validation preserves the original container type where possible + (e.g. a set[T] stays a set[T]). Serialization (model_dump / JSON) + always emits a list — round-tripping through model_dump() will not + restore the original container type. + """ + + @classmethod + def __get_pydantic_core_schema__( + cls, + source_type: Any, + handler: GetCoreSchemaHandler, + ) -> CoreSchema: + (item_type,) = get_args(source_type) or (Any,) + item_schema: CoreSchema = handler.generate_schema(item_type) + list_of_items_schema: CoreSchema = core_schema.list_schema(item_schema) + + return core_schema.no_info_wrap_validator_function( + cls._validate, + list_of_items_schema, + serialization=core_schema.plain_serializer_function_ser_schema( + cls._serialize, + info_arg=False, + ), + ) + + @staticmethod + def _validate(v: Iterable[_T], handler: "ValidatorFunctionWrapHandler") -> Any: + original_type: type[Any] = type(v) + + # Normalize to list so list_schema can validate each item + if isinstance(v, list): + items: list[_T] = v + else: + try: + items = list(v) + except TypeError as e: + raise TypeError("Value is not iterable") from e + + # Validate items against the inner schema + validated: list[_T] = handler(items) + + # Reconstruct original container type + if original_type is list: + return validated + # str(list) produces the list's repr, not a string built from items, + # so skip reconstruction for str and its subclasses. + if issubclass(original_type, str): + return validated + try: + return original_type(validated) + except (TypeError, ValueError): + # If the type cannot be reconstructed, just return the validated list + return validated + + @staticmethod + def _serialize(v: Iterable[_T]) -> list[_T]: + """Always serialize as a list so Pydantic's JSON encoder is happy.""" + if isinstance(v, list): + return v + return list(v) + + +EagerIterable: TypeAlias = Annotated[Iterable[_T], _EagerIterable] + + +def _construct_field(value: object, field: FieldInfo, key: str) -> object: + if value is None: + return field_get_default(field) + + if PYDANTIC_V1: + type_ = cast(type, field.outer_type_) # type: ignore + else: + type_ = field.annotation # type: ignore + + if type_ is None: + raise RuntimeError(f"Unexpected field type is None for {key}") + + return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None)) + + +def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: + if PYDANTIC_V1: + # TODO + return None + + schema = cls.__pydantic_core_schema__ + if schema["type"] == "model": + fields = schema["schema"] + if fields["type"] == "model-fields": + extras = fields.get("extras_schema") + if extras and "cls" in extras: + # mypy can't narrow the type + return extras["cls"] # type: ignore[no-any-return] + + return None + + +def is_basemodel(type_: type) -> bool: + """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" + if is_union(type_): + for variant in get_args(type_): + if is_basemodel(variant): + return True + + return False + + return is_basemodel_type(type_) + + +def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericModel]]: + origin = get_origin(type_) or type_ + if not inspect.isclass(origin): + return False + return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) + + +def build( + base_model_cls: Callable[P, _BaseModelT], + *args: P.args, + **kwargs: P.kwargs, +) -> _BaseModelT: + """Construct a BaseModel class without validation. + + This is useful for cases where you need to instantiate a `BaseModel` + from an API response as this provides type-safe params which isn't supported + by helpers like `construct_type()`. + + ```py + build(MyModel, my_field_a="foo", my_field_b=123) + ``` + """ + if args: + raise TypeError( + "Received positional arguments which are not supported; Keyword arguments must be used instead", + ) + + return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs)) + + +def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: + """Loose coercion to the expected type with construction of nested values. + + Note: the returned value from this function is not guaranteed to match the + given type. + """ + return cast(_T, construct_type(value=value, type_=type_)) + + +def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object: + """Loose coercion to the expected type with construction of nested values. + + If the given value does not match the expected type then it is returned as-is. + """ + + # store a reference to the original type we were given before we extract any inner + # types so that we can properly resolve forward references in `TypeAliasType` annotations + original_type = None + + # we allow `object` as the input type because otherwise, passing things like + # `Literal['value']` will be reported as a type error by type checkers + type_ = cast("type[object]", type_) + if is_type_alias_type(type_): + original_type = type_ # type: ignore[unreachable] + type_ = type_.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if metadata is not None and len(metadata) > 0: + meta: tuple[Any, ...] = tuple(metadata) + elif is_annotated_type(type_): + meta = get_args(type_)[1:] + type_ = extract_type_arg(type_, 0) + else: + meta = tuple() + + # we need to use the origin class for any types that are subscripted generics + # e.g. Dict[str, object] + origin = get_origin(type_) or type_ + args = get_args(type_) + + if is_union(origin): + try: + return validate_type(type_=cast("type[object]", original_type or type_), value=value) + except Exception: + pass + + # if the type is a discriminated union then we want to construct the right variant + # in the union, even if the data doesn't match exactly, otherwise we'd break code + # that relies on the constructed class types, e.g. + # + # class FooType: + # kind: Literal['foo'] + # value: str + # + # class BarType: + # kind: Literal['bar'] + # value: int + # + # without this block, if the data we get is something like `{'kind': 'bar', 'value': 'foo'}` then + # we'd end up constructing `FooType` when it should be `BarType`. + discriminator = _build_discriminated_union_meta(union=type_, meta_annotations=meta) + if discriminator and is_mapping(value): + variant_value = value.get(discriminator.field_alias_from or discriminator.field_name) + if variant_value and isinstance(variant_value, str): + variant_type = discriminator.mapping.get(variant_value) + if variant_type: + return construct_type(type_=variant_type, value=value) + + # if the data is not valid, use the first variant that doesn't fail while deserializing + for variant in args: + try: + return construct_type(value=value, type_=variant) + except Exception: + continue + + raise RuntimeError(f"Could not convert data into a valid instance of {type_}") + + if origin == dict: + if not is_mapping(value): + return value + + # Bare containers have no type arguments; leave their contents untyped. + items_type = args[1] if len(args) > 1 else object # Dict[_, items_type] + return {key: construct_type(value=item, type_=items_type) for key, item in value.items()} + + if ( + not is_literal_type(type_) + and inspect.isclass(origin) + and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)) + ): + if is_list(value): + return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value] + + if is_mapping(value): + if issubclass(type_, BaseModel): + return type_.construct(**value) # type: ignore[arg-type] + + return cast(Any, type_).construct(**value) + + if origin == list: + if not is_list(value): + return value + + inner_type = args[0] if args else object # List[inner_type] + return [construct_type(value=entry, type_=inner_type) for entry in value] + + if origin == float: + if isinstance(value, int): + coerced = float(value) + if coerced != value: + return value + return coerced + + return value + + if type_ == datetime: + try: + return parse_datetime(value) # type: ignore + except Exception: + return value + + if type_ == date: + try: + return parse_date(value) # type: ignore + except Exception: + return value + + return value + + +@runtime_checkable +class CachedDiscriminatorType(Protocol): + __discriminator__: DiscriminatorDetails + + +DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() + + +class DiscriminatorDetails: + field_name: str + """The name of the discriminator field in the variant class, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] + ``` + + Will result in field_name='type' + """ + + field_alias_from: str | None + """The name of the discriminator field in the API response, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] = Field(alias='type_from_api') + ``` + + Will result in field_alias_from='type_from_api' + """ + + mapping: dict[str, type] + """Mapping of discriminator value to variant type, e.g. + + {'foo': FooVariant, 'bar': BarVariant} + """ + + def __init__( + self, + *, + mapping: dict[str, type], + discriminator_field: str, + discriminator_alias: str | None, + ) -> None: + self.mapping = mapping + self.field_name = discriminator_field + self.field_alias_from = discriminator_alias + + +def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: + cached = DISCRIMINATOR_CACHE.get(union) + if cached is not None: + return cached + + discriminator_field_name: str | None = None + + for annotation in meta_annotations: + if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None: + discriminator_field_name = annotation.discriminator + break + + if not discriminator_field_name: + return None + + mapping: dict[str, type] = {} + discriminator_alias: str | None = None + + for variant in get_args(union): + variant = strip_annotated_type(variant) + if is_basemodel_type(variant): + if PYDANTIC_V1: + field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + if not field_info: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field_info.alias + + if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): + for entry in get_args(annotation): + if isinstance(entry, str): + mapping[entry] = variant + else: + field = _extract_field_schema_pv2(variant, discriminator_field_name) + if not field: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field.get("serialization_alias") + + field_schema = field["schema"] + + if field_schema["type"] == "literal": + for entry in cast("LiteralSchema", field_schema)["expected"]: + if isinstance(entry, str): + mapping[entry] = variant + + if not mapping: + return None + + details = DiscriminatorDetails( + mapping=mapping, + discriminator_field=discriminator_field_name, + discriminator_alias=discriminator_alias, + ) + DISCRIMINATOR_CACHE.setdefault(union, details) + return details + + +def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: + schema = model.__pydantic_core_schema__ + if schema["type"] == "definitions": + schema = schema["schema"] + + if schema["type"] != "model": + return None + + schema = cast("ModelSchema", schema) + fields_schema = schema["schema"] + if fields_schema["type"] != "model-fields": + return None + + fields_schema = cast("ModelFieldsSchema", fields_schema) + field = fields_schema["fields"].get(field_name) + if not field: + return None + + return cast("ModelField", field) # pyright: ignore[reportUnnecessaryCast] + + +def validate_type(*, type_: type[_T], value: object) -> _T: + """Strict validation that the given value matches the expected type""" + if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): + return cast(_T, parse_obj(type_, value)) + + return cast(_T, _validate_non_model_type(type_=type_, value=value)) + + +def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None: + """Add a pydantic config for the given type. + + Note: this is a no-op on Pydantic v1. + """ + setattr(typ, "__pydantic_config__", config) # noqa: B010 + + +def add_request_id(obj: BaseModel, request_id: str | None) -> None: + obj._request_id = request_id + + # in Pydantic v1, using setattr like we do above causes the attribute + # to be included when serializing the model which we don't want in this + # case so we need to explicitly exclude it + if PYDANTIC_V1: + try: + exclude_fields = obj.__exclude_fields__ # type: ignore + except AttributeError: + cast(Any, obj).__exclude_fields__ = {"_request_id", "__exclude_fields__"} + else: + cast(Any, obj).__exclude_fields__ = {*(exclude_fields or {}), "_request_id", "__exclude_fields__"} + + +# our use of subclassing here causes weirdness for type checkers, +# so we just pretend that we don't subclass +if TYPE_CHECKING: + GenericModel = BaseModel +else: + + class GenericModel(BaseGenericModel, BaseModel): + pass + + +if not PYDANTIC_V1: + from pydantic import TypeAdapter as _TypeAdapter + + _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) + + if TYPE_CHECKING: + from pydantic import TypeAdapter + else: + TypeAdapter = _CachedTypeAdapter + + def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: + return TypeAdapter(type_).validate_python(value) + +elif not TYPE_CHECKING: # TODO: condition is weird + + class RootModel(GenericModel, Generic[_T]): + """Used as a placeholder to easily convert runtime types to a Pydantic format + to provide validation. + + For example: + ```py + validated = RootModel[int](__root__="5").__root__ + # validated: 5 + ``` + """ + + __root__: _T + + def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: + model = _create_pydantic_model(type_).validate(value) + return cast(_T, model.__root__) + + def _create_pydantic_model(type_: _T) -> Type[RootModel[_T]]: + return RootModel[type_] # type: ignore + + +class SecurityOptions(TypedDict, total=False): + bearer_auth: bool + admin_api_key_auth: bool + + +class FinalRequestOptionsInput(TypedDict, total=False): + method: Required[str] + url: Required[str] + params: Query + headers: Headers + max_retries: int + timeout: float | Timeout | None + files: HttpxRequestFiles | None + idempotency_key: str + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] + json_data: Body + extra_json: AnyMapping + follow_redirects: bool + security: SecurityOptions + synthesize_event_and_data: bool + + +@final +class FinalRequestOptions(pydantic.BaseModel): + method: str + url: str + params: Query = {} + headers: Union[Headers, NotGiven] = NotGiven() + max_retries: Union[int, NotGiven] = NotGiven() + timeout: Union[float, Timeout, None, NotGiven] = NotGiven() + files: Union[HttpxRequestFiles, None] = None + idempotency_key: Union[str, None] = None + post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() + follow_redirects: Union[bool, None] = None + security: SecurityOptions = { + "bearer_auth": True, + "admin_api_key_auth": True, + } + synthesize_event_and_data: Optional[bool] = None + + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None + # It should be noted that we cannot use `json` here as that would override + # a BaseModel method in an incompatible fashion. + json_data: Union[Body, None] = None + extra_json: Union[AnyMapping, None] = None + + if PYDANTIC_V1: + + class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] + arbitrary_types_allowed: bool = True + else: + model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) + + def get_max_retries(self, max_retries: int) -> int: + if isinstance(self.max_retries, NotGiven): + return max_retries + return self.max_retries + + def _strip_raw_response_header(self) -> None: + if not is_given(self.headers): + return + + if self.headers.get(RAW_RESPONSE_HEADER): + self.headers = {**self.headers} + self.headers.pop(RAW_RESPONSE_HEADER) + + # override the `construct` method so that we can run custom transformations. + # this is necessary as we don't want to do any actual runtime type checking + # (which means we can't use validators) but we do want to ensure that `NotGiven` + # values are not present + # + # type ignore required because we're adding explicit types to `**values` + @classmethod + def construct( # type: ignore + cls, + _fields_set: set[str] | None = None, + **values: Unpack[FinalRequestOptionsInput], + ) -> FinalRequestOptions: + kwargs: dict[str, Any] = { + # we unconditionally call `strip_not_given` on any value + # as it will just ignore any non-mapping types + key: strip_not_given(value) + for key, value in values.items() + } + if PYDANTIC_V1: + return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + return super().model_construct(_fields_set, **kwargs) + + if not TYPE_CHECKING: + # type checkers incorrectly complain about this assignment + model_construct = construct diff --git a/venv/lib/python3.12/site-packages/openai/_module_client.py b/venv/lib/python3.12/site-packages/openai/_module_client.py new file mode 100644 index 0000000..3bfeb80 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_module_client.py @@ -0,0 +1,213 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import TYPE_CHECKING +from typing_extensions import override + +if TYPE_CHECKING: + from .resources.files import Files + from .resources.images import Images + from .resources.models import Models + from .resources.videos import Videos + from .resources.batches import Batches + from .resources.beta.beta import Beta + from .resources.chat.chat import Chat + from .resources.live.live import Live + from .resources.embeddings import Embeddings + from .resources.admin.admin import Admin + from .resources.audio.audio import Audio + from .resources.completions import Completions + from .resources.evals.evals import Evals + from .resources.moderations import Moderations + from .resources.safety.safety import Safety + from .resources.skills.skills import Skills + from .resources.uploads.uploads import Uploads + from .resources.realtime.realtime import Realtime + from .resources.webhooks.webhooks import Webhooks + from .resources.responses.responses import Responses + from .resources.containers.containers import Containers + from .resources.fine_tuning.fine_tuning import FineTuning + from .resources.content_provenance_checks import ContentProvenanceChecks + from .resources.conversations.conversations import Conversations + from .resources.vector_stores.vector_stores import VectorStores + +from . import _load_client +from ._utils import LazyProxy + + +class ChatProxy(LazyProxy["Chat"]): + @override + def __load__(self) -> Chat: + return _load_client().chat + + +class BetaProxy(LazyProxy["Beta"]): + @override + def __load__(self) -> Beta: + return _load_client().beta + + +class LiveProxy(LazyProxy["Live"]): + @override + def __load__(self) -> Live: + return _load_client().live + + +class FilesProxy(LazyProxy["Files"]): + @override + def __load__(self) -> Files: + return _load_client().files + + +class AudioProxy(LazyProxy["Audio"]): + @override + def __load__(self) -> Audio: + return _load_client().audio + + +class AdminProxy(LazyProxy["Admin"]): + @override + def __load__(self) -> Admin: + return _load_client().admin + + +class EvalsProxy(LazyProxy["Evals"]): + @override + def __load__(self) -> Evals: + return _load_client().evals + + +class ImagesProxy(LazyProxy["Images"]): + @override + def __load__(self) -> Images: + return _load_client().images + + +class ModelsProxy(LazyProxy["Models"]): + @override + def __load__(self) -> Models: + return _load_client().models + + +class SafetyProxy(LazyProxy["Safety"]): + @override + def __load__(self) -> Safety: + return _load_client().safety + + +class SkillsProxy(LazyProxy["Skills"]): + @override + def __load__(self) -> Skills: + return _load_client().skills + + +class VideosProxy(LazyProxy["Videos"]): + @override + def __load__(self) -> Videos: + return _load_client().videos + + +class BatchesProxy(LazyProxy["Batches"]): + @override + def __load__(self) -> Batches: + return _load_client().batches + + +class UploadsProxy(LazyProxy["Uploads"]): + @override + def __load__(self) -> Uploads: + return _load_client().uploads + + +class WebhooksProxy(LazyProxy["Webhooks"]): + @override + def __load__(self) -> Webhooks: + return _load_client().webhooks + + +class RealtimeProxy(LazyProxy["Realtime"]): + @override + def __load__(self) -> Realtime: + return _load_client().realtime + + +class ResponsesProxy(LazyProxy["Responses"]): + @override + def __load__(self) -> Responses: + return _load_client().responses + + +class EmbeddingsProxy(LazyProxy["Embeddings"]): + @override + def __load__(self) -> Embeddings: + return _load_client().embeddings + + +class ContainersProxy(LazyProxy["Containers"]): + @override + def __load__(self) -> Containers: + return _load_client().containers + + +class CompletionsProxy(LazyProxy["Completions"]): + @override + def __load__(self) -> Completions: + return _load_client().completions + + +class ModerationsProxy(LazyProxy["Moderations"]): + @override + def __load__(self) -> Moderations: + return _load_client().moderations + + +class FineTuningProxy(LazyProxy["FineTuning"]): + @override + def __load__(self) -> FineTuning: + return _load_client().fine_tuning + + +class VectorStoresProxy(LazyProxy["VectorStores"]): + @override + def __load__(self) -> VectorStores: + return _load_client().vector_stores + + +class ConversationsProxy(LazyProxy["Conversations"]): + @override + def __load__(self) -> Conversations: + return _load_client().conversations + + +class ContentProvenanceChecksProxy(LazyProxy["ContentProvenanceChecks"]): + @override + def __load__(self) -> ContentProvenanceChecks: + return _load_client().content_provenance_checks + + +chat: Chat = ChatProxy().__as_proxied__() +beta: Beta = BetaProxy().__as_proxied__() +live: Live = LiveProxy().__as_proxied__() +files: Files = FilesProxy().__as_proxied__() +audio: Audio = AudioProxy().__as_proxied__() +admin: Admin = AdminProxy().__as_proxied__() +evals: Evals = EvalsProxy().__as_proxied__() +images: Images = ImagesProxy().__as_proxied__() +models: Models = ModelsProxy().__as_proxied__() +safety: Safety = SafetyProxy().__as_proxied__() +skills: Skills = SkillsProxy().__as_proxied__() +videos: Videos = VideosProxy().__as_proxied__() +batches: Batches = BatchesProxy().__as_proxied__() +uploads: Uploads = UploadsProxy().__as_proxied__() +webhooks: Webhooks = WebhooksProxy().__as_proxied__() +realtime: Realtime = RealtimeProxy().__as_proxied__() +responses: Responses = ResponsesProxy().__as_proxied__() +embeddings: Embeddings = EmbeddingsProxy().__as_proxied__() +containers: Containers = ContainersProxy().__as_proxied__() +completions: Completions = CompletionsProxy().__as_proxied__() +moderations: Moderations = ModerationsProxy().__as_proxied__() +fine_tuning: FineTuning = FineTuningProxy().__as_proxied__() +vector_stores: VectorStores = VectorStoresProxy().__as_proxied__() +conversations: Conversations = ConversationsProxy().__as_proxied__() +content_provenance_checks: ContentProvenanceChecks = ContentProvenanceChecksProxy().__as_proxied__() diff --git a/venv/lib/python3.12/site-packages/openai/_multipart.py b/venv/lib/python3.12/site-packages/openai/_multipart.py new file mode 100644 index 0000000..a1257b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_multipart.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Mapping, cast + +from ._types import Body, Omit, NotGiven, FileTypes, RequestFiles +from ._utils._json import openapi_dumps + + +def encode_multipart( + body: object, + extra_body: Body | None, + encodings: Mapping[str, tuple[str, bool]], + raw_body_field: str | None = None, + existing_files: RequestFiles | None = None, +) -> tuple[dict[str, object] | None, RequestFiles | None, bytes | None, str]: + """Prepare explicitly encoded form fields without flattening their JSON contents.""" + if not isinstance(body, Mapping): + raise TypeError("Multipart request body must be a mapping") + if extra_body is not None and not isinstance(extra_body, Mapping): + raise TypeError("Multipart extra_body must be a mapping") + original = cast(Mapping[str, object], body) + overrides = cast(Mapping[str, object], extra_body or {}) + merged = {key: value for key, value in {**original, **overrides}.items() if not isinstance(value, (Omit, NotGiven))} + # A raw request alternative is safe only when there is no other payload to lose. + # Explicit null remains a JSON part; only an omitted field selects the raw body. + if not existing_files and raw_body_field is not None and set(merged) == {raw_body_field}: + value = merged[raw_body_field] + if not isinstance(value, str): + raise TypeError("Raw multipart alternative must be a string") + return None, None, value.encode("utf-8"), encodings[raw_body_field][0] + + files: list[tuple[str, FileTypes]] = list( + existing_files.items() if isinstance(existing_files, Mapping) else (existing_files or []) + ) + for name, (content_type, as_json) in encodings.items(): + if name not in merged: + continue + value = merged.pop(name) + if as_json: + data = openapi_dumps(value) + else: + if not isinstance(value, str): + raise TypeError(f"Multipart field {name!r} must be a string") + data = value.encode("utf-8") + files.append((name, (None, data, content_type))) + return merged or None, files, None, "multipart/form-data" diff --git a/venv/lib/python3.12/site-packages/openai/_provider.py b/venv/lib/python3.12/site-packages/openai/_provider.py new file mode 100644 index 0000000..8a6c83e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_provider.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +from typing import Callable, Protocol, Awaitable +from weakref import WeakKeyDictionary +from dataclasses import dataclass + +import httpx2 + +from ._models import FinalRequestOptions +from ._exceptions import OpenAIError + + +class _Provider: + """Opaque configuration returned by an OpenAI-owned provider factory.""" + + __slots__ = ("__weakref__",) + + +@dataclass +class _ProviderRuntime: + name: str + base_url: str | httpx2.URL + transform_request: Callable[[FinalRequestOptions], FinalRequestOptions] | None = None + transform_async_request: Callable[[FinalRequestOptions], Awaitable[FinalRequestOptions]] | None = None + prepare_request: Callable[[httpx2.Request], None] | None = None + prepare_async_request: Callable[[httpx2.Request], Awaitable[None]] | None = None + normalize_response: Callable[[httpx2.Response], httpx2.Response] | None = None + normalize_async_response: Callable[[httpx2.Response], Awaitable[httpx2.Response]] | None = None + + +class _ProviderDefinition(Protocol): + @property + def name(self) -> str: ... + + def configure(self) -> _ProviderRuntime: ... + + +# Provider factories capture configuration in definitions, while every client +# gets fresh runtime state from ``definition.configure()``. Keeping definitions +# outside the opaque provider object prevents arbitrary objects (including a +# directly constructed ``_Provider``) from imitating an OpenAI-owned provider +# and keeps credentials out of the object's representation. The weak mapping +# also avoids retaining provider configuration after the public handle is gone. +_provider_definitions: WeakKeyDictionary[_Provider, _ProviderDefinition] = WeakKeyDictionary() + + +def _create_provider(definition: _ProviderDefinition) -> _Provider: # pyright: ignore[reportUnusedFunction] + provider = _Provider() + _provider_definitions[provider] = definition + return provider + + +def _provider_name(provider: _Provider) -> str: # pyright: ignore[reportUnusedFunction] + return _get_provider_definition(provider).name + + +def _configure_provider(provider: _Provider) -> _ProviderRuntime: # pyright: ignore[reportUnusedFunction] + return _get_provider_definition(provider).configure() + + +def _get_provider_definition(provider: _Provider) -> _ProviderDefinition: + try: + return _provider_definitions[provider] + except (KeyError, TypeError) as exc: + raise OpenAIError("Invalid provider. Providers must be created by an OpenAI provider factory.") from exc diff --git a/venv/lib/python3.12/site-packages/openai/_qs.py b/venv/lib/python3.12/site-packages/openai/_qs.py new file mode 100644 index 0000000..4127c19 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_qs.py @@ -0,0 +1,149 @@ +from __future__ import annotations + +from typing import Any, List, Tuple, Union, Mapping, TypeVar +from urllib.parse import parse_qs, urlencode +from typing_extensions import get_args + +from ._types import NotGiven, ArrayFormat, NestedFormat, not_given +from ._utils import flatten + +_T = TypeVar("_T") + +PrimitiveData = Union[str, int, float, bool, None] +# this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] +# https://github.com/microsoft/pyright/issues/3555 +Data = Union[PrimitiveData, List[Any], Tuple[Any], "Mapping[str, Any]"] +Params = Mapping[str, Data] + + +class Querystring: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + *, + array_format: ArrayFormat = "repeat", + nested_format: NestedFormat = "brackets", + ) -> None: + self.array_format = array_format + self.nested_format = nested_format + + def parse(self, query: str) -> Mapping[str, object]: + # Note: custom format syntax is not supported yet + return parse_qs(query) + + def stringify( + self, + params: Params, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> str: + return urlencode( + self.stringify_items( + params, + array_format=array_format, + nested_format=nested_format, + ) + ) + + def stringify_items( + self, + params: Params, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> list[tuple[str, str]]: + opts = Options( + qs=self, + array_format=array_format, + nested_format=nested_format, + ) + return flatten([self._stringify_item(key, value, opts) for key, value in params.items()]) + + def _stringify_item( + self, + key: str, + value: Data, + opts: Options, + ) -> list[tuple[str, str]]: + if isinstance(value, Mapping): + items: list[tuple[str, str]] = [] + nested_format = opts.nested_format + for subkey, subvalue in value.items(): + items.extend( + self._stringify_item( + # TODO: error if unknown format + f"{key}.{subkey}" if nested_format == "dots" else f"{key}[{subkey}]", + subvalue, + opts, + ) + ) + return items + + if isinstance(value, (list, tuple)): + array_format = opts.array_format + if array_format == "comma": + return [ + ( + key, + ",".join(self._primitive_value_to_str(item) for item in value if item is not None), + ), + ] + elif array_format == "repeat": + items = [] + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + elif array_format == "indices": + items = [] + for i, item in enumerate(value): + items.extend(self._stringify_item(f"{key}[{i}]", item, opts)) + return items + elif array_format == "brackets": + items = [] + key = key + "[]" + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + else: + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + serialised = self._primitive_value_to_str(value) + if not serialised: + return [] + return [(key, serialised)] + + def _primitive_value_to_str(self, value: PrimitiveData) -> str: + # copied from httpx + if value is True: + return "true" + elif value is False: + return "false" + elif value is None: + return "" + return str(value) + + +_qs = Querystring() +parse = _qs.parse +stringify = _qs.stringify +stringify_items = _qs.stringify_items + + +class Options: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + qs: Querystring = _qs, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> None: + self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format + self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format diff --git a/venv/lib/python3.12/site-packages/openai/_resource.py b/venv/lib/python3.12/site-packages/openai/_resource.py new file mode 100644 index 0000000..d1aecd5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_resource.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +import time +from typing import TYPE_CHECKING + +import anyio + +if TYPE_CHECKING: + from ._client import OpenAI, AsyncOpenAI + + +class SyncAPIResource: + _client: OpenAI + + def __init__(self, client: OpenAI) -> None: + self._client = client + self._get = client.get + self._post = client.post + self._patch = client.patch + self._put = client.put + self._delete = client.delete + self._get_api_list = client.get_api_list + + def _sleep(self, seconds: float) -> None: + time.sleep(seconds) + + +class AsyncAPIResource: + _client: AsyncOpenAI + + def __init__(self, client: AsyncOpenAI) -> None: + self._client = client + self._get = client.get + self._post = client.post + self._patch = client.patch + self._put = client.put + self._delete = client.delete + self._get_api_list = client.get_api_list + + async def _sleep(self, seconds: float) -> None: + await anyio.sleep(seconds) diff --git a/venv/lib/python3.12/site-packages/openai/_response.py b/venv/lib/python3.12/site-packages/openai/_response.py new file mode 100644 index 0000000..e18f927 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_response.py @@ -0,0 +1,853 @@ +from __future__ import annotations + +import os +import inspect +import logging +import datetime +import functools +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Union, + Generic, + TypeVar, + Callable, + Iterator, + AsyncIterator, + cast, + overload, +) +from typing_extensions import Awaitable, ParamSpec, override, get_origin + +import anyio +import httpx2 +import pydantic + +from ._types import NoneType +from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type, extract_type_var_from_base +from ._httpx2 import http_response_types, stream_consumed_exceptions +from ._models import BaseModel, is_basemodel, add_request_id +from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER +from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type +from ._exceptions import OpenAIError, APIResponseValidationError + +if TYPE_CHECKING: + from ._models import FinalRequestOptions + from ._base_client import BaseClient + + +P = ParamSpec("P") +R = TypeVar("R") +_T = TypeVar("_T") +_APIResponseT = TypeVar("_APIResponseT", bound="APIResponse[Any]") +_AsyncAPIResponseT = TypeVar("_AsyncAPIResponseT", bound="AsyncAPIResponse[Any]") + +log: logging.Logger = logging.getLogger(__name__) + + +class BaseAPIResponse(Generic[R]): + _cast_to: type[R] + _client: BaseClient[Any, Any] + _parsed_by_type: dict[type[Any], Any] + _is_sse_stream: bool + _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None + _options: FinalRequestOptions + + http_response: httpx2.Response + + retries_taken: int + """The number of retries made. If no retries happened this will be `0`""" + + def __init__( + self, + *, + raw: httpx2.Response, + cast_to: type[R], + client: BaseClient[Any, Any], + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + options: FinalRequestOptions, + retries_taken: int = 0, + ) -> None: + self._cast_to = cast_to + self._client = client + self._parsed_by_type = {} + self._is_sse_stream = stream + self._stream_cls = stream_cls + self._options = options + self.http_response = raw + self.retries_taken = retries_taken + + @property + def headers(self) -> httpx2.Headers: + return self.http_response.headers + + @property + def http_request(self) -> httpx2.Request: + """Returns the HTTPX2 Request instance associated with the current response.""" + return self.http_response.request + + @property + def status_code(self) -> int: + return self.http_response.status_code + + @property + def url(self) -> httpx2.URL: + """Returns the URL for which the request was made.""" + return self.http_response.url + + @property + def method(self) -> str: + return self.http_request.method + + @property + def http_version(self) -> str: + return self.http_response.http_version + + @property + def elapsed(self) -> datetime.timedelta: + """The time taken for the complete request/response cycle to complete.""" + return self.http_response.elapsed + + @property + def is_closed(self) -> bool: + """Whether or not the response body has been closed. + + If this is False then there is response data that has not been read yet. + You must either fully consume the response body or call `.close()` + before discarding the response to prevent resource leaks. + """ + return self.http_response.is_closed + + @override + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>" + ) + + def _parse(self, *, to: type[_T] | None = None) -> R | _T: + cast_to = to if to is not None else self._cast_to + + # unwrap `TypeAlias('Name', T)` -> `T` + if is_type_alias_type(cast_to): + cast_to = cast_to.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if cast_to and is_annotated_type(cast_to): + cast_to = extract_type_arg(cast_to, 0) + + origin = get_origin(cast_to) or cast_to + + if self._is_sse_stream: + if to: + if not is_stream_class_type(to): + raise TypeError(f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}") + + return cast( + _T, + to( + cast_to=extract_stream_chunk_type( + to, + failure_message="Expected custom stream type to be passed with a type argument, e.g. Stream[ChunkType]", + ), + response=self.http_response, + client=cast(Any, self._client), + options=self._options, + ), + ) + + if self._stream_cls: + return cast( + R, + self._stream_cls( + cast_to=extract_stream_chunk_type(self._stream_cls), + response=self.http_response, + client=cast(Any, self._client), + options=self._options, + ), + ) + + stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls) + if stream_cls is None: + raise MissingStreamClassError() + + return cast( + R, + stream_cls( + cast_to=cast_to, + response=self.http_response, + client=cast(Any, self._client), + options=self._options, + ), + ) + + if cast_to is NoneType: + return cast(R, None) + + response = self.http_response + if cast_to == str: + return cast(R, response.text) + + if cast_to == bytes: + return cast(R, response.content) + + if cast_to == int: + return cast(R, int(response.text)) + + if cast_to == float: + return cast(R, float(response.text)) + + if cast_to == bool: + return cast(R, response.text.lower() == "true") + + # handle the legacy binary response case + if inspect.isclass(cast_to) and cast_to.__name__ == "HttpxBinaryResponseContent": + return cast(R, cast_to(response)) # type: ignore + + if origin == APIResponse: + raise RuntimeError("Unexpected state - cast_to is `APIResponse`") + + response_types = http_response_types() + if inspect.isclass(origin) and issubclass(origin, response_types): + # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response + # and pass that class to our request functions. We cannot change the variance to be either + # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct + # the response class ourselves but that is something that should be supported directly in httpx + # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. + if cast_to not in response_types: + raise ValueError("Subclasses of HTTP response classes cannot be passed to `cast_to`") + return cast(R, response) + + if ( + inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) + and not issubclass(origin, BaseModel) + and issubclass(origin, pydantic.BaseModel) + ): + raise TypeError("Pydantic models must subclass our base model type, e.g. `from openai import BaseModel`") + + if ( + cast_to is not object + and not origin is list + and not origin is dict + and not origin is Union + and not issubclass(origin, BaseModel) + ): + raise RuntimeError( + f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx2.Response}." + ) + + # split is required to handle cases where additional information is included + # in the response, e.g. application/json; charset=utf-8 + content_type, *_ = response.headers.get("content-type", "*").split(";") + if not content_type.endswith("json"): + if is_basemodel(cast(type, cast_to)): + try: + data = response.json() + except Exception as exc: + log.debug("Could not read JSON from response data due to %s", type(exc).__name__) + else: + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + if self._client._strict_response_validation: + raise APIResponseValidationError( + response=response, + message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", + body=response.text, + ) + + # If the API responds with content that isn't JSON then we just return + # the (decoded) text without performing any parsing so that you can still + # handle the response however you need to. + return response.text # type: ignore + + data = response.json() + + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + +class APIResponse(BaseAPIResponse[R]): + @property + def request_id(self) -> str | None: + return self.http_response.headers.get("x-request-id") # type: ignore[no-any-return] + + @overload + def parse(self, *, to: type[_T]) -> _T: ... + + @overload + def parse(self) -> R: ... + + def parse(self, *, to: type[_T] | None = None) -> R | _T: + """Returns the rich python representation of this response's data. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from openai import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `int` + - `float` + - `httpx2.Response` + """ + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] + + if not self._is_sse_stream: + self.read() + + parsed = self._parse(to=to) + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + if isinstance(parsed, BaseModel): + add_request_id(parsed, self.request_id) + + self._parsed_by_type[cache_key] = parsed + return cast(R, parsed) + + def read(self) -> bytes: + """Read and return the binary response content.""" + try: + return self.http_response.read() + except stream_consumed_exceptions() as exc: + # The default error raised by httpx isn't very + # helpful in our case so we re-raise it with + # a different error message. + raise StreamAlreadyConsumed() from exc + + def text(self) -> str: + """Read and decode the response content into a string.""" + self.read() + return self.http_response.text + + def json(self) -> object: + """Read and decode the JSON response content.""" + self.read() + return self.http_response.json() + + def close(self) -> None: + """Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.http_response.close() + + def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: + """ + A byte-iterator over the decoded response content. + + This automatically handles gzip, deflate and brotli encoded responses. + """ + for chunk in self.http_response.iter_bytes(chunk_size): + yield chunk + + def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: + """A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + for chunk in self.http_response.iter_text(chunk_size): + yield chunk + + def iter_lines(self) -> Iterator[str]: + """Like `iter_text()` but will only yield chunks for each line""" + for chunk in self.http_response.iter_lines(): + yield chunk + + +class AsyncAPIResponse(BaseAPIResponse[R]): + @property + def request_id(self) -> str | None: + return self.http_response.headers.get("x-request-id") # type: ignore[no-any-return] + + @overload + async def parse(self, *, to: type[_T]) -> _T: ... + + @overload + async def parse(self) -> R: ... + + async def parse(self, *, to: type[_T] | None = None) -> R | _T: + """Returns the rich python representation of this response's data. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from openai import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `httpx2.Response` + """ + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] + + if not self._is_sse_stream: + await self.read() + + parsed = self._parse(to=to) + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + if isinstance(parsed, BaseModel): + add_request_id(parsed, self.request_id) + + self._parsed_by_type[cache_key] = parsed + return cast(R, parsed) + + async def read(self) -> bytes: + """Read and return the binary response content.""" + try: + return await self.http_response.aread() + except stream_consumed_exceptions() as exc: + # the default error raised by httpx isn't very + # helpful in our case so we re-raise it with + # a different error message + raise StreamAlreadyConsumed() from exc + + async def text(self) -> str: + """Read and decode the response content into a string.""" + await self.read() + return self.http_response.text + + async def json(self) -> object: + """Read and decode the JSON response content.""" + await self.read() + return self.http_response.json() + + async def close(self) -> None: + """Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.http_response.aclose() + + async def iter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: + """ + A byte-iterator over the decoded response content. + + This automatically handles gzip, deflate and brotli encoded responses. + """ + async for chunk in self.http_response.aiter_bytes(chunk_size): + yield chunk + + async def iter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: + """A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + async for chunk in self.http_response.aiter_text(chunk_size): + yield chunk + + async def iter_lines(self) -> AsyncIterator[str]: + """Like `iter_text()` but will only yield chunks for each line""" + async for chunk in self.http_response.aiter_lines(): + yield chunk + + +class BinaryAPIResponse(APIResponse[bytes]): + """Subclass of APIResponse providing helpers for dealing with binary data. + + Note: If you want to stream the response data instead of eagerly reading it + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + + def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + with open(file, mode="wb") as f: + for data in self.iter_bytes(): + f.write(data) + + +class AsyncBinaryAPIResponse(AsyncAPIResponse[bytes]): + """Subclass of APIResponse providing helpers for dealing with binary data. + + Note: If you want to stream the response data instead of eagerly reading it + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + + async def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.iter_bytes(): + await f.write(data) + + +class StreamedBinaryAPIResponse(APIResponse[bytes]): + def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + """Streams the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + """ + with open(file, mode="wb") as f: + for data in self.iter_bytes(chunk_size): + f.write(data) + + +class AsyncStreamedBinaryAPIResponse(AsyncAPIResponse[bytes]): + async def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + """Streams the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + """ + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.iter_bytes(chunk_size): + await f.write(data) + + +class MissingStreamClassError(TypeError): + def __init__(self) -> None: + super().__init__( + "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `openai._streaming` for reference", + ) + + +class StreamAlreadyConsumed(OpenAIError): + """ + Attempted to read or stream content, but the content has already + been streamed. + + This can happen if you use a method like `.iter_lines()` and then attempt + to read the entire response body afterwards, e.g. + + ```py + response = await client.post(...) + async for line in response.iter_lines(): + ... # do something with `line` + + content = await response.read() + # ^ error + ``` + + If you want this behaviour you'll need to either manually accumulate the response + content or call `await response.read()` before iterating over the stream. + """ + + def __init__(self) -> None: + message = ( + "Attempted to read or stream some content, but the content has " + "already been streamed. " + "This could be due to attempting to stream the response " + "content more than once." + "\n\n" + "You can fix this by manually accumulating the response content while streaming " + "or by calling `.read()` before starting to stream." + ) + super().__init__(message) + + +class ResponseContextManager(Generic[_APIResponseT]): + """Context manager for ensuring that a request is not made + until it is entered and that the response will always be closed + when the context manager exits + """ + + def __init__(self, request_func: Callable[[], _APIResponseT]) -> None: + self._request_func = request_func + self.__response: _APIResponseT | None = None + + def __enter__(self) -> _APIResponseT: + self.__response = self._request_func() + return self.__response + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__response is not None: + self.__response.close() + + +class AsyncResponseContextManager(Generic[_AsyncAPIResponseT]): + """Context manager for ensuring that a request is not made + until it is entered and that the response will always be closed + when the context manager exits + """ + + def __init__(self, api_request: Awaitable[_AsyncAPIResponseT]) -> None: + self._api_request = api_request + self.__response: _AsyncAPIResponseT | None = None + + async def __aenter__(self) -> _AsyncAPIResponseT: + self.__response = await self._api_request + return self.__response + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__response is not None: + await self.__response.close() + + +def to_streamed_response_wrapper(func: Callable[P, R]) -> Callable[P, ResponseContextManager[APIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support streaming and returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[APIResponse[R]]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + + kwargs["extra_headers"] = extra_headers + + make_request = functools.partial(func, *args, **kwargs) + + return ResponseContextManager(cast(Callable[[], APIResponse[R]], make_request)) + + return wrapped + + +def async_to_streamed_response_wrapper( + func: Callable[P, Awaitable[R]], +) -> Callable[P, AsyncResponseContextManager[AsyncAPIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support streaming and returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[AsyncAPIResponse[R]]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + + kwargs["extra_headers"] = extra_headers + + make_request = func(*args, **kwargs) + + return AsyncResponseContextManager(cast(Awaitable[AsyncAPIResponse[R]], make_request)) + + return wrapped + + +def to_custom_streamed_response_wrapper( + func: Callable[P, object], + response_cls: type[_APIResponseT], +) -> Callable[P, ResponseContextManager[_APIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support streaming and returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[_APIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + make_request = functools.partial(func, *args, **kwargs) + + return ResponseContextManager(cast(Callable[[], _APIResponseT], make_request)) + + return wrapped + + +def async_to_custom_streamed_response_wrapper( + func: Callable[P, Awaitable[object]], + response_cls: type[_AsyncAPIResponseT], +) -> Callable[P, AsyncResponseContextManager[_AsyncAPIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support streaming and returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[_AsyncAPIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + make_request = func(*args, **kwargs) + + return AsyncResponseContextManager(cast(Awaitable[_AsyncAPIResponseT], make_request)) + + return wrapped + + +def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + + kwargs["extra_headers"] = extra_headers + + return cast(APIResponse[R], func(*args, **kwargs)) + + return wrapped + + +def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[AsyncAPIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncAPIResponse[R]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + + kwargs["extra_headers"] = extra_headers + + return cast(AsyncAPIResponse[R], await func(*args, **kwargs)) + + return wrapped + + +def to_custom_raw_response_wrapper( + func: Callable[P, object], + response_cls: type[_APIResponseT], +) -> Callable[P, _APIResponseT]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> _APIResponseT: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + return cast(_APIResponseT, func(*args, **kwargs)) + + return wrapped + + +def async_to_custom_raw_response_wrapper( + func: Callable[P, Awaitable[object]], + response_cls: type[_AsyncAPIResponseT], +) -> Callable[P, Awaitable[_AsyncAPIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> Awaitable[_AsyncAPIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + return cast(Awaitable[_AsyncAPIResponseT], func(*args, **kwargs)) + + return wrapped + + +def extract_response_type(typ: type[BaseAPIResponse[Any]]) -> type: + """Given a type like `APIResponse[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyResponse(APIResponse[bytes]): + ... + + extract_response_type(MyResponse) -> bytes + ``` + """ + return extract_type_var_from_base( + typ, + generic_bases=cast("tuple[type, ...]", (BaseAPIResponse, APIResponse, AsyncAPIResponse)), + index=0, + ) diff --git a/venv/lib/python3.12/site-packages/openai/_send_queue.py b/venv/lib/python3.12/site-packages/openai/_send_queue.py new file mode 100644 index 0000000..b3eb812 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_send_queue.py @@ -0,0 +1,120 @@ +from __future__ import annotations + +import typing +import threading +from collections import deque + +import anyio.to_thread + +from ._exceptions import WebSocketQueueFullError + + +class SendQueue: + """Bounded byte-size queue for outgoing WebSocket messages. + + Messages are stored as pre-serialized strings. The queue enforces a + maximum byte budget so that unbounded buffering cannot occur during + reconnection windows. + """ + + def __init__(self, max_bytes: int = 1_048_576) -> None: + self._queue: list[tuple[str, int]] = [] # (data, byte_length) + self._bytes: int = 0 + self._max_bytes = max_bytes + self._lock = threading.Lock() + self._flush_done: threading.Event | None = None + + def enqueue(self, data: str) -> None: + """Append *data* to the queue. + + Raises :class:`WebSocketQueueFullError` if the message would + exceed the byte-size limit. + """ + byte_length = len(data.encode("utf-8")) + with self._lock: + if self._bytes + byte_length > self._max_bytes: + raise WebSocketQueueFullError("send queue is full, message discarded") + self._queue.append((data, byte_length)) + self._bytes += byte_length + + def flush_sync(self, send: typing.Callable[[str], object], *, requeue_failed: bool = True) -> None: + """Send every queued message via *send*. + + If *send* raises, the failing message and all subsequent messages + are re-queued and the error is re-raised. When `requeue_failed` is + false, release the attempted message even on failure or interruption. + """ + while isinstance(pending := self._begin_flush(), threading.Event): + pending.wait() + + try: + while pending: + data, byte_length = pending[0] + sent = False + try: + send(data) + sent = True + finally: + if sent or not requeue_failed: + with self._lock: + pending.popleft() + self._bytes -= byte_length + finally: + self._end_flush(pending) + + async def flush_async( + self, send: typing.Callable[[str], typing.Awaitable[object]], *, requeue_failed: bool = True + ) -> None: + """Async variant of :meth:`flush_sync`.""" + while isinstance(pending := self._begin_flush(), threading.Event): + # Waiting in a worker keeps the event loop responsive. Cancellation + # cannot strand ownership: the worker only waits, never acquires it. + await anyio.to_thread.run_sync(pending.wait, abandon_on_cancel=True) + + try: + while pending: + data, byte_length = pending[0] + sent = False + try: + await send(data) + sent = True + finally: + if sent or not requeue_failed: + with self._lock: + pending.popleft() + self._bytes -= byte_length + finally: + self._end_flush(pending) + + def _begin_flush(self) -> deque[tuple[str, int]] | threading.Event: + with self._lock: + if self._flush_done is not None: + return self._flush_done + pending = deque(self._queue) + self._queue.clear() + self._flush_done = threading.Event() + # Pending messages remain charged until their sends succeed. + return pending + + def _end_flush(self, pending: deque[tuple[str, int]]) -> None: + with self._lock: + self._queue = list(pending) + self._queue + assert self._flush_done is not None + self._flush_done.set() + self._flush_done = None + + def drain(self) -> list[str]: + """Remove and return all queued messages.""" + with self._lock: + items = [data for data, _ in self._queue] + self._bytes -= sum(byte_length for _, byte_length in self._queue) + self._queue.clear() + return items + + def __len__(self) -> int: + with self._lock: + return len(self._queue) + + def __bool__(self) -> bool: + with self._lock: + return len(self._queue) > 0 diff --git a/venv/lib/python3.12/site-packages/openai/_streaming.py b/venv/lib/python3.12/site-packages/openai/_streaming.py new file mode 100644 index 0000000..bb45a70 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_streaming.py @@ -0,0 +1,471 @@ +# Note: initially copied from https://github.com/florimondmanca/httpx-sse/blob/master/src/httpx_sse/_decoders.py +from __future__ import annotations + +import re +import json +import inspect +from types import TracebackType +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, Optional, AsyncIterator, cast +from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable + +import httpx2 + +from ._utils import is_mapping, extract_type_var_from_base +from ._httpx2 import request_exceptions, timeout_exceptions +from ._exceptions import APIError, APITimeoutError, APIConnectionError + +if TYPE_CHECKING: + from ._client import OpenAI, AsyncOpenAI + from ._models import FinalRequestOptions + + +_T = TypeVar("_T") + + +class Stream(Generic[_T]): + """Provides the core interface to iterate over a synchronous stream response.""" + + response: httpx2.Response + _options: Optional[FinalRequestOptions] = None + _decoder: SSEBytesDecoder + + def __init__( + self, + *, + cast_to: type[_T], + response: httpx2.Response, + client: OpenAI, + options: Optional[FinalRequestOptions] = None, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._options = options + self._decoder = client._make_sse_decoder() + self._iterator = self.__stream__() + + def __next__(self) -> _T: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[_T]: + for item in self._iterator: + yield item + + def _iter_events(self) -> Iterator[ServerSentEvent]: + try: + yield from self._decoder.iter_bytes(self.response.iter_bytes()) + except timeout_exceptions() as err: + raise APITimeoutError(request=self.response.request) from err + except request_exceptions() as err: + raise APIConnectionError(request=self.response.request) from err + + def __stream__(self) -> Iterator[_T]: + cast_to = cast(Any, self._cast_to) + response = self.response + process_data = self._client._process_response_data + iterator = self._iter_events() + + try: + for sse in iterator: + if sse.data.startswith("[DONE]"): + break + + # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data + if sse.event and sse.event.startswith("thread."): + data = sse.json() + + if sse.event == "error" and is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) + else: + data = sse.json() + if is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data( + data={"data": data, "event": sse.event} + if self._options is not None and self._options.synthesize_event_and_data + else data, + cast_to=cast_to, + response=response, + ) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + response.close() + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.response.close() + + +class AsyncStream(Generic[_T]): + """Provides the core interface to iterate over an asynchronous stream response.""" + + response: httpx2.Response + _options: Optional[FinalRequestOptions] = None + _decoder: SSEDecoder | SSEBytesDecoder + + def __init__( + self, + *, + cast_to: type[_T], + response: httpx2.Response, + client: AsyncOpenAI, + options: Optional[FinalRequestOptions] = None, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._options = options + self._decoder = client._make_sse_decoder() + self._iterator = self.__stream__() + + async def __anext__(self) -> _T: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[_T]: + async for item in self._iterator: + yield item + + async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: + try: + async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()): + yield sse + except timeout_exceptions() as err: + raise APITimeoutError(request=self.response.request) from err + except request_exceptions() as err: + raise APIConnectionError(request=self.response.request) from err + + async def __stream__(self) -> AsyncIterator[_T]: + cast_to = cast(Any, self._cast_to) + response = self.response + process_data = self._client._process_response_data + iterator = self._iter_events() + + try: + async for sse in iterator: + if sse.data.startswith("[DONE]"): + break + + # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data + if sse.event and sse.event.startswith("thread."): + data = sse.json() + + if sse.event == "error" and is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) + else: + data = sse.json() + if is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data( + data={"data": data, "event": sse.event} + if self._options is not None and self._options.synthesize_event_and_data + else data, + cast_to=cast_to, + response=response, + ) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + await response.aclose() + + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.response.aclose() + + async def aclose(self) -> None: + """Close the response and release the connection. Alias for `close()`.""" + await self.close() + + +class ServerSentEvent: + def __init__( + self, + *, + event: str | None = None, + data: str | None = None, + id: str | None = None, + retry: int | None = None, + ) -> None: + if data is None: + data = "" + + self._id = id + self._data = data + self._event = event or None + self._retry = retry + + @property + def event(self) -> str | None: + return self._event + + @property + def id(self) -> str | None: + return self._id + + @property + def retry(self) -> int | None: + return self._retry + + @property + def data(self) -> str: + return self._data + + def json(self) -> Any: + return json.loads(self.data) + + @override + def __repr__(self) -> str: + return f"ServerSentEvent(event={self.event}, data={self.data}, id={self.id}, retry={self.retry})" + + +_SSE_LINE_END = re.compile(b"[\\r\\n]") + + +class _SSELineDecoder: + """Incrementally split CR, LF, and CRLF without copying growing prefixes.""" + + def __init__(self) -> None: + self._buffer = bytearray() + self._skip_lf = False + + def _append(self, chunk: bytes, start: int, end: int) -> None: + self._buffer.extend(memoryview(chunk)[start:end]) + + def feed(self, chunk: bytes) -> Iterator[bytes]: + start = 0 + for match in _SSE_LINE_END.finditer(chunk): + end = match.start() + if self._skip_lf and end == start and chunk[end] == 10: + self._skip_lf = False + start = end + 1 + continue + self._skip_lf = False + self._append(chunk, start, end) + line = bytes(self._buffer) + self._buffer.clear() + self._skip_lf = chunk[end] == 13 + start = end + 1 + yield line + if start < len(chunk): + self._skip_lf = False + self._append(chunk, start, len(chunk)) + + def finish(self) -> Iterator[bytes]: + if self._buffer: + line = bytes(self._buffer) + self._buffer.clear() + yield line + + +class SSEDecoder: + """Decode SSE incrementally without imposing a line or event size limit.""" + + _data: list[str] + _event: str | None + _retry: int | None + _last_event_id: str | None + + def __init__(self) -> None: + self._reset() + + def _reset(self) -> None: + self._event = None + self._data = [] + self._last_event_id = None + self._retry = None + + def _decode_lines(self, lines: Iterator[bytes]) -> Iterator[ServerSentEvent]: + for raw_line in lines: + sse = self.decode(raw_line.decode("utf-8")) + if sse: + yield sse + + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given raw binary data, yield every event encountered.""" + decoder = _SSELineDecoder() + try: + for chunk in iterator: + yield from self._decode_lines(decoder.feed(chunk)) + yield from self._decode_lines(decoder.finish()) + finally: + self._reset() + + async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given async raw binary data, yield every event encountered.""" + decoder = _SSELineDecoder() + try: + async for chunk in iterator: + for sse in self._decode_lines(decoder.feed(chunk)): + yield sse + for sse in self._decode_lines(decoder.finish()): + yield sse + finally: + self._reset() + + def decode(self, line: str) -> ServerSentEvent | None: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if not self._event and not self._data and not self._last_event_id and self._retry is None: + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = None + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None + + +@runtime_checkable +class SSEBytesDecoder(Protocol): + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an async iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + +def is_stream_class_type(typ: type) -> TypeGuard[type[Stream[object]] | type[AsyncStream[object]]]: + """TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`""" + origin = get_origin(typ) or typ + return inspect.isclass(origin) and issubclass(origin, (Stream, AsyncStream)) + + +def extract_stream_chunk_type( + stream_cls: type, + *, + failure_message: str | None = None, +) -> type: + """Given a type like `Stream[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyStream(Stream[bytes]): + ... + + extract_stream_chunk_type(MyStream) -> bytes + ``` + """ + from ._base_client import Stream, AsyncStream + + return extract_type_var_from_base( + stream_cls, + index=0, + generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), + failure_message=failure_message, + ) diff --git a/venv/lib/python3.12/site-packages/openai/_types.py b/venv/lib/python3.12/site-packages/openai/_types.py new file mode 100644 index 0000000..d49aab2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_types.py @@ -0,0 +1,279 @@ +from __future__ import annotations + +from os import PathLike +from typing import ( + IO, + TYPE_CHECKING, + Any, + Dict, + List, + Type, + Tuple, + Union, + Mapping, + TypeVar, + Callable, + Iterable, + Iterator, + Optional, + Sequence, + AsyncIterable, +) +from typing_extensions import ( + Set, + Literal, + Protocol, + TypeAlias, + TypedDict, + SupportsIndex, + overload, + override, + runtime_checkable, +) + +import httpx2 +import pydantic +from httpx2 import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport + +if TYPE_CHECKING: + from ._models import BaseModel, SecurityOptions + from ._response import APIResponse, AsyncAPIResponse + from ._legacy_response import HttpxBinaryResponseContent + +Transport = BaseTransport +AsyncTransport = AsyncBaseTransport +Query = Mapping[str, object] +Body = object +AnyMapping = Mapping[str, object] +ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) +_T = TypeVar("_T") + +ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] +NestedFormat = Literal["dots", "brackets"] + + +# Approximates httpx internal ProxiesTypes and RequestFiles types +# while adding support for `PathLike` instances +ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]] +ProxiesTypes = Union[str, Proxy, ProxiesDict] +if TYPE_CHECKING: + Base64FileInput = Union[IO[bytes], PathLike[str]] + FileContent = Union[IO[bytes], bytes, PathLike[str]] +else: + Base64FileInput = Union[IO[bytes], PathLike] + FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. + + +# Used for sending raw binary data / streaming data in request bodies +# e.g. for file uploads without multipart encoding +BinaryTypes = Union[bytes, bytearray, IO[bytes], Iterable[bytes]] +AsyncBinaryTypes = Union[bytes, bytearray, IO[bytes], AsyncIterable[bytes]] + +FileTypes = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], +] +RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] + +# duplicate of the above but without our custom file support +HttpxFileContent = Union[IO[bytes], bytes] +HttpxFileTypes = Union[ + # file (or bytes) + HttpxFileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], HttpxFileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], HttpxFileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]], +] +HttpxRequestFiles = Union[Mapping[str, HttpxFileTypes], Sequence[Tuple[str, HttpxFileTypes]]] + +# Workaround to support (cast_to: Type[ResponseT]) -> ResponseT +# where ResponseT includes `None`. In order to support directly +# passing `None`, overloads would have to be defined for every +# method that uses `ResponseT` which would lead to an unacceptable +# amount of code duplication and make it unreadable. See _base_client.py +# for example usage. +# +# This unfortunately means that you will either have +# to import this type and pass it explicitly: +# +# from openai import NoneType +# client.get('/foo', cast_to=NoneType) +# +# or build it yourself: +# +# client.get('/foo', cast_to=type(None)) +if TYPE_CHECKING: + NoneType: Type[None] +else: + NoneType = type(None) + + +class RequestOptions(TypedDict, total=False): + headers: Headers + max_retries: int + timeout: float | Timeout | None + params: Query + extra_json: AnyMapping + idempotency_key: str + follow_redirects: bool + security: SecurityOptions + synthesize_event_and_data: bool + + +# Sentinel class used until PEP 0661 is accepted +class NotGiven: + """ + For parameters with a meaningful None value, we need to distinguish between + the user explicitly passing None, and the user not passing the parameter at + all. + + User code shouldn't need to use not_given directly. + + For example: + + ```py + def create(timeout: Timeout | None | NotGiven = not_given): ... + + + create(timeout=1) # 1s timeout + create(timeout=None) # No timeout + create() # Default timeout behavior + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + @override + def __repr__(self) -> str: + return "NOT_GIVEN" + + +not_given = NotGiven() +# for backwards compatibility: +NOT_GIVEN = NotGiven() + + +class Omit: + """ + To explicitly omit something from being sent in a request, use `omit`. + + ```py + # as the default `Content-Type` header is `application/json` that will be sent + client.post("/upload/files", files={"file": b"my raw file content"}) + + # you can't explicitly override the header as it has to be dynamically generated + # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' + client.post(..., headers={"Content-Type": "multipart/form-data"}) + + # instead you can remove the default `application/json` header by passing omit + client.post(..., headers={"Content-Type": omit}) + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + +omit = Omit() + +Omittable = Union[_T, Omit] + + +@runtime_checkable +class ModelBuilderProtocol(Protocol): + @classmethod + def build( + cls: type[_T], + *, + response: Response, + data: object, + ) -> _T: ... + + +Headers = Mapping[str, Union[str, Omit]] + + +class HeadersLikeProtocol(Protocol): + def get(self, __key: str) -> str | None: ... + + +HeadersLike = Union[Headers, HeadersLikeProtocol] + +ResponseT = TypeVar( + "ResponseT", + bound=Union[ + object, + str, + None, + "BaseModel", + List[Any], + Dict[str, Any], + Response, + ModelBuilderProtocol, + "APIResponse[Any]", + "AsyncAPIResponse[Any]", + "HttpxBinaryResponseContent", + ], +) + +StrBytesIntFloat = Union[str, bytes, int, float] + +# Note: copied from Pydantic +# https://github.com/pydantic/pydantic/blob/6f31f8f68ef011f84357330186f603ff295312fd/pydantic/main.py#L79 +IncEx: TypeAlias = Union[Set[int], Set[str], Mapping[int, Union["IncEx", bool]], Mapping[str, Union["IncEx", bool]]] + +PostParser = Callable[[Any], Any] + + +@runtime_checkable +class InheritsGeneric(Protocol): + """Represents a type that has inherited from `Generic` + + The `__orig_bases__` property can be used to determine the resolved + type variable for a given base class. + """ + + __orig_bases__: tuple[_GenericAlias] + + +class _GenericAlias(Protocol): + __origin__: type[object] + + +class HttpxSendArgs(TypedDict, total=False): + auth: httpx2.Auth + follow_redirects: bool + + +_T_co = TypeVar("_T_co", covariant=True) + + +if TYPE_CHECKING: + # This works because str.__contains__ does not accept object (either in typeshed or at runtime) + # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 + # + # Note: index() and count() methods are intentionally omitted to allow pyright to properly + # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr. + class SequenceNotStr(Protocol[_T_co]): + @overload + def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... + @overload + def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... + def __contains__(self, value: object, /) -> bool: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_T_co]: ... + def __reversed__(self) -> Iterator[_T_co]: ... +else: + # just point this to a normal `Sequence` at runtime to avoid having to special case + # deserializing our custom sequence type + SequenceNotStr = Sequence diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__init__.py b/venv/lib/python3.12/site-packages/openai/_utils/__init__.py new file mode 100644 index 0000000..bbd7969 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/__init__.py @@ -0,0 +1,67 @@ +from ._logs import SensitiveHeadersFilter as SensitiveHeadersFilter +from ._path import path_template as path_template +from ._sync import asyncify as asyncify +from ._proxy import LazyProxy as LazyProxy +from ._utils import ( + flatten as flatten, + is_dict as is_dict, + is_list as is_list, + is_given as is_given, + is_tuple as is_tuple, + json_safe as json_safe, + lru_cache as lru_cache, + is_mapping as is_mapping, + is_tuple_t as is_tuple_t, + is_iterable as is_iterable, + is_sequence as is_sequence, + coerce_float as coerce_float, + is_mapping_t as is_mapping_t, + removeprefix as removeprefix, + removesuffix as removesuffix, + extract_files as extract_files, + is_sequence_t as is_sequence_t, + required_args as required_args, + coerce_boolean as coerce_boolean, + coerce_integer as coerce_integer, + file_from_path as file_from_path, + is_azure_client as is_azure_client, + strip_not_given as strip_not_given, + get_async_library as get_async_library, + maybe_coerce_float as maybe_coerce_float, + get_required_header as get_required_header, + maybe_coerce_boolean as maybe_coerce_boolean, + maybe_coerce_integer as maybe_coerce_integer, + is_async_azure_client as is_async_azure_client, +) +from ._compat import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, +) +from ._typing import ( + is_list_type as is_list_type, + is_union_type as is_union_type, + extract_type_arg as extract_type_arg, + is_iterable_type as is_iterable_type, + is_required_type as is_required_type, + is_sequence_type as is_sequence_type, + is_annotated_type as is_annotated_type, + is_type_alias_type as is_type_alias_type, + strip_annotated_type as strip_annotated_type, + extract_type_var_from_base as extract_type_var_from_base, +) +from ._streams import consume_sync_iterator as consume_sync_iterator, consume_async_iterator as consume_async_iterator +from ._transform import ( + PropertyInfo as PropertyInfo, + transform as transform, + async_transform as async_transform, + maybe_transform as maybe_transform, + async_maybe_transform as async_maybe_transform, +) +from ._reflection import ( + function_has_argument as function_has_argument, + assert_signatures_in_sync as assert_signatures_in_sync, +) +from ._datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f5ea1e7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_compat.cpython-312.pyc new file mode 100644 index 0000000..2e9d4af Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_compat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_datetime_parse.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_datetime_parse.cpython-312.pyc new file mode 100644 index 0000000..26b1f0f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_datetime_parse.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_json.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_json.cpython-312.pyc new file mode 100644 index 0000000..68ef66d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_json.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_logs.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_logs.cpython-312.pyc new file mode 100644 index 0000000..bb52a61 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_logs.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_path.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_path.cpython-312.pyc new file mode 100644 index 0000000..9e5730a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_path.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_proxy.cpython-312.pyc new file mode 100644 index 0000000..e6202e0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_reflection.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_reflection.cpython-312.pyc new file mode 100644 index 0000000..3108612 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_reflection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_resources_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_resources_proxy.cpython-312.pyc new file mode 100644 index 0000000..ec38cb4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_resources_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_streams.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_streams.cpython-312.pyc new file mode 100644 index 0000000..49b14e2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_streams.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_sync.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_sync.cpython-312.pyc new file mode 100644 index 0000000..2c4ef37 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_sync.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_transform.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_transform.cpython-312.pyc new file mode 100644 index 0000000..97037ab Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_transform.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_typing.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_typing.cpython-312.pyc new file mode 100644 index 0000000..6a7e590 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_typing.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_utils.cpython-312.pyc new file mode 100644 index 0000000..a1ab605 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_utils/__pycache__/_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_utils/_compat.py b/venv/lib/python3.12/site-packages/openai/_utils/_compat.py new file mode 100644 index 0000000..03881b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/_compat.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import sys +import typing_extensions +from typing import Any, Type, Union, Literal, Optional +from datetime import date, datetime +from typing_extensions import get_args as _get_args, get_origin as _get_origin + +from .._types import StrBytesIntFloat +from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime + +_LITERAL_TYPES: set[object] = {Literal, typing_extensions.Literal} + + +def get_args(tp: type[Any]) -> tuple[Any, ...]: + return _get_args(tp) + + +def get_origin(tp: type[Any]) -> type[Any] | None: + return _get_origin(tp) + + +def is_union(tp: Optional[Type[Any]]) -> bool: + if sys.version_info < (3, 10): + return tp is Union # type: ignore[comparison-overlap] + else: + import types + + return tp is Union or tp is types.UnionType # type: ignore[comparison-overlap] + + +def is_typeddict(tp: Type[Any]) -> bool: + return typing_extensions.is_typeddict(tp) + + +def is_literal_type(tp: Type[Any]) -> bool: + return get_origin(tp) in _LITERAL_TYPES + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + return _parse_date(value) + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + return _parse_datetime(value) diff --git a/venv/lib/python3.12/site-packages/openai/_utils/_datetime_parse.py b/venv/lib/python3.12/site-packages/openai/_utils/_datetime_parse.py new file mode 100644 index 0000000..7cb9d9e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/_datetime_parse.py @@ -0,0 +1,136 @@ +""" +This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py +without the Pydantic v1 specific errors. +""" + +from __future__ import annotations + +import re +from typing import Dict, Union, Optional +from datetime import date, datetime, timezone, timedelta + +from .._types import StrBytesIntFloat + +date_expr = r"(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})" +time_expr = ( + r"(?P\d{1,2}):(?P\d{1,2})" + r"(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?" + r"(?PZ|[+-]\d{2}(?::?\d{2})?)?$" +) + +date_re = re.compile(f"{date_expr}$") +datetime_re = re.compile(f"{date_expr}[T ]{time_expr}") + + +EPOCH = datetime(1970, 1, 1) +# if greater than this, the number is in ms, if less than or equal it's in seconds +# (in seconds this is 11th October 2603, in ms it's 20th August 1970) +MS_WATERSHED = int(2e10) +# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9 +MAX_NUMBER = int(3e20) + + +def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]: + if isinstance(value, (int, float)): + return value + try: + return float(value) + except ValueError: + return None + except TypeError: + raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None + + +def _from_unix_seconds(seconds: Union[int, float]) -> datetime: + if seconds > MAX_NUMBER: + return datetime.max + elif seconds < -MAX_NUMBER: + return datetime.min + + while abs(seconds) > MS_WATERSHED: + seconds /= 1000 + dt = EPOCH + timedelta(seconds=seconds) + return dt.replace(tzinfo=timezone.utc) + + +def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]: + if value == "Z": + return timezone.utc + elif value is not None: + offset_mins = int(value[-2:]) if len(value) > 3 else 0 + offset = 60 * int(value[1:3]) + offset_mins + if value[0] == "-": + offset = -offset + return timezone(timedelta(minutes=offset)) + else: + return None + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + """ + Parse a datetime/int/float/string and return a datetime.datetime. + + This function supports time zone offsets. When the input contains one, + the output uses a timezone with a fixed offset from UTC. + + Raise ValueError if the input is well formatted but not a valid datetime. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, datetime): + return value + + number = _get_numeric(value, "datetime") + if number is not None: + return _from_unix_seconds(number) + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + + match = datetime_re.match(value) + if match is None: + raise ValueError("invalid datetime format") + + kw = match.groupdict() + if kw["microsecond"]: + kw["microsecond"] = kw["microsecond"].ljust(6, "0") + + tzinfo = _parse_timezone(kw.pop("tzinfo")) + kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} + kw_["tzinfo"] = tzinfo + + return datetime(**kw_) # type: ignore + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + """ + Parse a date/int/float/string and return a datetime.date. + + Raise ValueError if the input is well formatted but not a valid date. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, date): + if isinstance(value, datetime): + return value.date() + else: + return value + + number = _get_numeric(value, "date") + if number is not None: + return _from_unix_seconds(number).date() + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + match = date_re.match(value) + if match is None: + raise ValueError("invalid date format") + + kw = {k: int(v) for k, v in match.groupdict().items()} + + try: + return date(**kw) + except ValueError: + raise ValueError("invalid date format") from None diff --git a/venv/lib/python3.12/site-packages/openai/_utils/_json.py b/venv/lib/python3.12/site-packages/openai/_utils/_json.py new file mode 100644 index 0000000..6058421 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/_json.py @@ -0,0 +1,35 @@ +import json +from typing import Any +from datetime import datetime +from typing_extensions import override + +import pydantic + +from .._compat import model_dump + + +def openapi_dumps(obj: Any) -> bytes: + """ + Serialize an object to UTF-8 encoded JSON bytes. + + Extends the standard json.dumps with support for additional types + commonly used in the SDK, such as `datetime`, `pydantic.BaseModel`, etc. + """ + return json.dumps( + obj, + cls=_CustomEncoder, + # Uses the same defaults as httpx's JSON serialization + ensure_ascii=False, + separators=(",", ":"), + allow_nan=False, + ).encode() + + +class _CustomEncoder(json.JSONEncoder): + @override + def default(self, o: Any) -> Any: + if isinstance(o, datetime): + return o.isoformat() + if isinstance(o, pydantic.BaseModel): + return model_dump(o, exclude_unset=True, mode="json", by_alias=True) + return super().default(o) diff --git a/venv/lib/python3.12/site-packages/openai/_utils/_logs.py b/venv/lib/python3.12/site-packages/openai/_utils/_logs.py new file mode 100644 index 0000000..f41d9df --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/_logs.py @@ -0,0 +1,58 @@ +import os +import logging +from typing_extensions import override + +from ._utils import is_dict + +logger: logging.Logger = logging.getLogger("openai") + + +SENSITIVE_HEADERS = {"api-key", "authorization", "x-amz-security-token"} +_LOG_LEVELS = { + "debug": logging.DEBUG, + "info": logging.INFO, + "warning": logging.WARNING, + "error": logging.ERROR, + "critical": logging.CRITICAL, +} + + +def _basic_config() -> None: + # e.g. [2023-10-05 14:12:26 - openai._base_client:818 - DEBUG] HTTP Response: POST 200 + logging.basicConfig( + format="[%(asctime)s - %(name)s:%(lineno)d - %(levelname)s] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + +def get_http_method_for_logging(method: str) -> str: + normalized = method.upper() + if normalized in {"GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"}: + return normalized + return "" + + +def setup_logging() -> None: + # Transport loggers may include complete URLs. Leave their configuration to + # the application instead of enabling them with the SDK's logging switch. + env = os.environ.get("OPENAI_LOG") + if env is None: + return + + level = _LOG_LEVELS.get(env) + if level is None: + return + + _basic_config() + logger.setLevel(level) + + +class SensitiveHeadersFilter(logging.Filter): + @override + def filter(self, record: logging.LogRecord) -> bool: + if is_dict(record.args) and "headers" in record.args and is_dict(record.args["headers"]): + headers = record.args["headers"] = {**record.args["headers"]} + for header in headers: + if str(header).lower() in SENSITIVE_HEADERS: + headers[header] = "" + return True diff --git a/venv/lib/python3.12/site-packages/openai/_utils/_path.py b/venv/lib/python3.12/site-packages/openai/_utils/_path.py new file mode 100644 index 0000000..64a38a7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/_path.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +import re +from typing import ( + Any, + Mapping, + Callable, +) +from urllib.parse import quote + +# Matches '.' or '..' where each dot is either literal or percent-encoded (%2e / %2E). +_DOT_SEGMENT_RE = re.compile(r"^(?:\.|%2[eE]){1,2}$") + +_PLACEHOLDER_RE = re.compile(r"\{(\w+)\}") + + +def _quote_path_segment_part(value: str) -> str: + """Percent-encode `value` for use in a URI path segment. + + Considers characters not in `pchar` set from RFC 3986 §3.3 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 + """ + # quote() already treats unreserved characters (letters, digits, and -._~) + # as safe, so we only need to add sub-delims, ':', and '@'. + # Notably, unlike the default `safe` for quote(), / is unsafe and must be quoted. + return quote(value, safe="!$&'()*+,;=:@") + + +def _quote_query_part(value: str) -> str: + """Percent-encode `value` for use in a URI query string. + + Considers &, = and characters not in `query` set from RFC 3986 §3.4 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.4 + """ + return quote(value, safe="!$'()*+,;:@/?") + + +def _quote_fragment_part(value: str) -> str: + """Percent-encode `value` for use in a URI fragment. + + Considers characters not in `fragment` set from RFC 3986 §3.5 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.5 + """ + return quote(value, safe="!$&'()*+,;=:@/?") + + +def _interpolate( + template: str, + values: Mapping[str, Any], + quoter: Callable[[str], str], +) -> str: + """Replace {name} placeholders in `template`, quoting each value with `quoter`. + + Placeholder names are looked up in `values`. + + Raises: + KeyError: If a placeholder is not found in `values`. + """ + # re.split with a capturing group returns alternating + # [text, name, text, name, ..., text] elements. + parts = _PLACEHOLDER_RE.split(template) + + for i in range(1, len(parts), 2): + name = parts[i] + if name not in values: + raise KeyError(f"a value for placeholder {{{name}}} was not provided") + val = values[name] + if val is None: + parts[i] = "null" + elif isinstance(val, bool): + parts[i] = "true" if val else "false" + else: + parts[i] = quoter(str(values[name])) + + return "".join(parts) + + +def path_template(template: str, /, **kwargs: Any) -> str: + """Interpolate {name} placeholders in `template` from keyword arguments. + + Args: + template: The template string containing {name} placeholders. + **kwargs: Keyword arguments to interpolate into the template. + + Returns: + The template with placeholders interpolated and percent-encoded. + + Safe characters for percent-encoding are dependent on the URI component. + Placeholders in path and fragment portions are percent-encoded where the `segment` + and `fragment` sets from RFC 3986 respectively are considered safe. + Placeholders in the query portion are percent-encoded where the `query` set from + RFC 3986 §3.4 is considered safe except for = and & characters. + + Raises: + KeyError: If a placeholder is not found in `kwargs`. + ValueError: If resulting path contains /./ or /../ segments (including percent-encoded dot-segments). + """ + # Split the template into path, query, and fragment portions. + fragment_template: str | None = None + query_template: str | None = None + + rest = template + if "#" in rest: + rest, fragment_template = rest.split("#", 1) + if "?" in rest: + rest, query_template = rest.split("?", 1) + path_template = rest + + # Interpolate each portion with the appropriate quoting rules. + path_result = _interpolate(path_template, kwargs, _quote_path_segment_part) + + # Reject dot-segments (. and ..) in the final assembled path. The check + # runs after interpolation so that adjacent placeholders or a mix of static + # text and placeholders that together form a dot-segment are caught. + # Also reject percent-encoded dot-segments to protect against incorrectly + # implemented normalization in servers/proxies. + for segment in path_result.split("/"): + if _DOT_SEGMENT_RE.match(segment): + raise ValueError(f"Constructed path {path_result!r} contains dot-segment {segment!r} which is not allowed") + + result = path_result + if query_template is not None: + result += "?" + _interpolate(query_template, kwargs, _quote_query_part) + if fragment_template is not None: + result += "#" + _interpolate(fragment_template, kwargs, _quote_fragment_part) + + return result diff --git a/venv/lib/python3.12/site-packages/openai/_utils/_proxy.py b/venv/lib/python3.12/site-packages/openai/_utils/_proxy.py new file mode 100644 index 0000000..0f239a3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/_proxy.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Generic, TypeVar, Iterable, cast +from typing_extensions import override + +T = TypeVar("T") + + +class LazyProxy(Generic[T], ABC): + """Implements data methods to pretend that an instance is another instance. + + This includes forwarding attribute access and other methods. + """ + + # Note: we have to special case proxies that themselves return proxies + # to support using a proxy as a catch-all for any random access, e.g. `proxy.foo.bar.baz` + + def __getattr__(self, attr: str) -> object: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied # pyright: ignore + return getattr(proxied, attr) + + @override + def __repr__(self) -> str: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ + return repr(self.__get_proxied__()) + + @override + def __str__(self) -> str: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ + return str(proxied) + + @override + def __dir__(self) -> Iterable[str]: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return [] + return proxied.__dir__() + + @property # type: ignore + @override + def __class__(self) -> type: # pyright: ignore + try: + proxied = self.__get_proxied__() + except Exception: + return type(self) + if issubclass(type(proxied), LazyProxy): + return type(proxied) + return proxied.__class__ + + def __get_proxied__(self) -> T: + return self.__load__() + + def __as_proxied__(self) -> T: + """Helper method that returns the current proxy, typed as the loaded object""" + return cast(T, self) + + @abstractmethod + def __load__(self) -> T: ... diff --git a/venv/lib/python3.12/site-packages/openai/_utils/_reflection.py b/venv/lib/python3.12/site-packages/openai/_utils/_reflection.py new file mode 100644 index 0000000..bdaca29 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/_reflection.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import inspect +from typing import Any, Callable + + +def function_has_argument(func: Callable[..., Any], arg_name: str) -> bool: + """Returns whether or not the given function has a specific parameter""" + sig = inspect.signature(func) + return arg_name in sig.parameters + + +def assert_signatures_in_sync( + source_func: Callable[..., Any], + check_func: Callable[..., Any], + *, + exclude_params: set[str] = set(), + description: str = "", +) -> None: + """Ensure that the signature of the second function matches the first.""" + + check_sig = inspect.signature(check_func) + source_sig = inspect.signature(source_func) + + errors: list[str] = [] + + for name, source_param in source_sig.parameters.items(): + if name in exclude_params: + continue + + custom_param = check_sig.parameters.get(name) + if not custom_param: + errors.append(f"the `{name}` param is missing") + continue + + if custom_param.annotation != source_param.annotation: + errors.append( + f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(custom_param.annotation)}" + ) + continue + + if errors: + raise AssertionError( + f"{len(errors)} errors encountered when comparing signatures{description}:\n\n" + "\n\n".join(errors) + ) diff --git a/venv/lib/python3.12/site-packages/openai/_utils/_resources_proxy.py b/venv/lib/python3.12/site-packages/openai/_utils/_resources_proxy.py new file mode 100644 index 0000000..e5b9ec7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/_resources_proxy.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import Any +from typing_extensions import override + +from ._proxy import LazyProxy + + +class ResourcesProxy(LazyProxy[Any]): + """A proxy for the `openai.resources` module. + + This is used so that we can lazily import `openai.resources` only when + needed *and* so that users can just import `openai` and reference `openai.resources` + """ + + @override + def __load__(self) -> Any: + import importlib + + mod = importlib.import_module("openai.resources") + return mod + + +resources = ResourcesProxy().__as_proxied__() diff --git a/venv/lib/python3.12/site-packages/openai/_utils/_streams.py b/venv/lib/python3.12/site-packages/openai/_utils/_streams.py new file mode 100644 index 0000000..f4a0208 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/_streams.py @@ -0,0 +1,12 @@ +from typing import Any +from typing_extensions import Iterator, AsyncIterator + + +def consume_sync_iterator(iterator: Iterator[Any]) -> None: + for _ in iterator: + ... + + +async def consume_async_iterator(iterator: AsyncIterator[Any]) -> None: + async for _ in iterator: + ... diff --git a/venv/lib/python3.12/site-packages/openai/_utils/_sync.py b/venv/lib/python3.12/site-packages/openai/_utils/_sync.py new file mode 100644 index 0000000..f6027c1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/_sync.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +import asyncio +import functools +from typing import TypeVar, Callable, Awaitable +from typing_extensions import ParamSpec + +import anyio +import sniffio +import anyio.to_thread + +T_Retval = TypeVar("T_Retval") +T_ParamSpec = ParamSpec("T_ParamSpec") + + +async def to_thread( + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs +) -> T_Retval: + if sniffio.current_async_library() == "asyncio": + return await asyncio.to_thread(func, *args, **kwargs) + + return await anyio.to_thread.run_sync( + functools.partial(func, *args, **kwargs), + ) + + +# inspired by `asyncer`, https://github.com/tiangolo/asyncer +def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: + """ + Take a blocking function and create an async one that receives the same + positional and keyword arguments. + + Usage: + + ```python + def blocking_func(arg1, arg2, kwarg1=None): + # blocking code + return result + + + result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1) + ``` + + ## Arguments + + `function`: a blocking regular callable (e.g. a function) + + ## Return + + An async function that takes the same positional and keyword arguments as the + original one, that when called runs the same original function in a thread worker + and returns the result. + """ + + async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval: + return await to_thread(function, *args, **kwargs) + + return wrapper diff --git a/venv/lib/python3.12/site-packages/openai/_utils/_transform.py b/venv/lib/python3.12/site-packages/openai/_utils/_transform.py new file mode 100644 index 0000000..9f1e66e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/_transform.py @@ -0,0 +1,460 @@ +from __future__ import annotations + +import io +import base64 +import pathlib +from typing import Any, Mapping, TypeVar, cast +from datetime import date, datetime +from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints + +import anyio +import pydantic + +from ._utils import ( + is_list, + is_given, + lru_cache, + is_mapping, + is_iterable, + is_sequence, +) +from .._files import is_base64_file_input +from ._compat import get_origin, is_typeddict +from ._typing import ( + is_list_type, + is_union_type, + is_iterable_type, + is_required_type, + is_sequence_type, + is_annotated_type, + strip_annotated_type, +) + +_T = TypeVar("_T") + + +# TODO: support for drilling globals() and locals() +# TODO: ensure works correctly with forward references in all cases + + +PropertyFormat = Literal["iso8601", "base64", "custom"] + + +class PropertyInfo: + """Metadata class to be used in Annotated types to provide information about a given type. + + For example: + + class MyParams(TypedDict): + account_holder_name: Annotated[str, PropertyInfo(alias='accountHolderName')] + + This means that {'account_holder_name': 'Robert'} will be transformed to {'accountHolderName': 'Robert'} before being sent to the API. + """ + + alias: str | None + format: PropertyFormat | None + format_template: str | None + discriminator: str | None + + def __init__( + self, + *, + alias: str | None = None, + format: PropertyFormat | None = None, + format_template: str | None = None, + discriminator: str | None = None, + ) -> None: + self.alias = alias + self.format = format + self.format_template = format_template + self.discriminator = discriminator + + @override + def __repr__(self) -> str: + return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}', discriminator='{self.discriminator}')" + + +def maybe_transform( + data: object, + expected_type: object, +) -> Any | None: + """Wrapper over `transform()` that allows `None` to be passed. + + See `transform()` for more details. + """ + if data is None: + return None + return transform(data, expected_type) + + +# Wrapper over _transform_recursive providing fake types +def transform( + data: _T, + expected_type: object, +) -> _T: + """Transform dictionaries based off of type information from the given type, for example: + + ```py + class Params(TypedDict, total=False): + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] + + + transformed = transform({"card_id": ""}, Params) + # {'cardID': ''} + ``` + + Any keys / data that does not have type information given will be included as is. + + It should be noted that the transformations that this function does are not represented in the type system. + """ + transformed = _transform_recursive(data, annotation=cast(type, expected_type)) + return cast(_T, transformed) + + +@lru_cache(maxsize=8096) +def _get_annotated_type(type_: type) -> type | None: + """If the given type is an `Annotated` type then it is returned, if not `None` is returned. + + This also unwraps the type when applicable, e.g. `Required[Annotated[T, ...]]` + """ + if is_required_type(type_): + # Unwrap `Required[Annotated[T, ...]]` to `Annotated[T, ...]` + type_ = get_args(type_)[0] + + if is_annotated_type(type_): + return type_ + + return None + + +def _maybe_transform_key(key: str, type_: type) -> str: + """Transform the given `data` based on the annotations provided in `type_`. + + Note: this function only looks at `Annotated` types that contain `PropertyInfo` metadata. + """ + annotated_type = _get_annotated_type(type_) + if annotated_type is None: + # no `Annotated` definition for this type, no transformation needed + return key + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.alias is not None: + return annotation.alias + + return key + + +def _no_transform_needed(annotation: type) -> bool: + return annotation == float or annotation == int + + +def _transform_recursive( + data: object, + *, + annotation: type, + inner_type: type | None = None, +) -> object: + """Transform the given data against the expected type. + + Args: + annotation: The direct type annotation given to the particular piece of data. + This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc + + inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type + is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in + the list can be transformed using the metadata from the container type. + + Defaults to the same value as the `annotation` argument. + """ + from .._compat import model_dump + + if inner_type is None: + inner_type = annotation + + stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type + if is_typeddict(stripped_type) and is_mapping(data): + return _transform_typeddict(data, stripped_type) + + if origin == dict and is_mapping(data): + args = get_args(stripped_type) + items_type = args[1] if len(args) > 1 else object + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) + ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + + args = get_args(stripped_type) + inner_type = cast(type, args[0]) if args else object + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + + return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] + + if is_union_type(stripped_type): + # For union types we run the transformation against all subtypes to ensure that everything is transformed. + # + # TODO: there may be edge cases where the same normalized field name will transform to two different names + # in different subtypes. + for subtype in get_args(stripped_type): + data = _transform_recursive(data, annotation=annotation, inner_type=subtype) + return data + + if isinstance(data, pydantic.BaseModel): + return model_dump(data, exclude_unset=True, mode="json", exclude=getattr(data, "__api_exclude__", None)) + + annotated_type = _get_annotated_type(annotation) + if annotated_type is None: + return data + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.format is not None: + return _format_data(data, annotation.format, annotation.format_template) + + return data + + +def _format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: + if isinstance(data, (date, datetime)): + if format_ == "iso8601": + return data.isoformat() + + if format_ == "custom" and format_template is not None: + return data.strftime(format_template) + + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = data.read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + + return data + + +def _transform_typeddict( + data: Mapping[str, object], + expected_type: type, +) -> Mapping[str, object]: + result: dict[str, object] = {} + annotations = get_type_hints(expected_type, include_extras=True) + for key, value in data.items(): + if not is_given(value): + # we don't need to include omitted values here as they'll + # be stripped out before the request is sent anyway + continue + + type_ = annotations.get(key) + if type_ is None: + # we do not have a type annotation for this field, leave it as is + result[key] = value + else: + result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_) + return result + + +async def async_maybe_transform( + data: object, + expected_type: object, +) -> Any | None: + """Wrapper over `async_transform()` that allows `None` to be passed. + + See `async_transform()` for more details. + """ + if data is None: + return None + return await async_transform(data, expected_type) + + +async def async_transform( + data: _T, + expected_type: object, +) -> _T: + """Transform dictionaries based off of type information from the given type, for example: + + ```py + class Params(TypedDict, total=False): + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] + + + transformed = transform({"card_id": ""}, Params) + # {'cardID': ''} + ``` + + Any keys / data that does not have type information given will be included as is. + + It should be noted that the transformations that this function does are not represented in the type system. + """ + transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type)) + return cast(_T, transformed) + + +async def _async_transform_recursive( + data: object, + *, + annotation: type, + inner_type: type | None = None, +) -> object: + """Transform the given data against the expected type. + + Args: + annotation: The direct type annotation given to the particular piece of data. + This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc + + inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type + is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in + the list can be transformed using the metadata from the container type. + + Defaults to the same value as the `annotation` argument. + """ + from .._compat import model_dump + + if inner_type is None: + inner_type = annotation + + stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type + if is_typeddict(stripped_type) and is_mapping(data): + return await _async_transform_typeddict(data, stripped_type) + + if origin == dict and is_mapping(data): + args = get_args(stripped_type) + items_type = args[1] if len(args) > 1 else object + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) + ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + + args = get_args(stripped_type) + inner_type = cast(type, args[0]) if args else object + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + + return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] + + if is_union_type(stripped_type): + # For union types we run the transformation against all subtypes to ensure that everything is transformed. + # + # TODO: there may be edge cases where the same normalized field name will transform to two different names + # in different subtypes. + for subtype in get_args(stripped_type): + data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype) + return data + + if isinstance(data, pydantic.BaseModel): + return model_dump(data, exclude_unset=True, mode="json", exclude=getattr(data, "__api_exclude__", None)) + + annotated_type = _get_annotated_type(annotation) + if annotated_type is None: + return data + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.format is not None: + return await _async_format_data(data, annotation.format, annotation.format_template) + + return data + + +async def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: + if isinstance(data, (date, datetime)): + if format_ == "iso8601": + return data.isoformat() + + if format_ == "custom" and format_template is not None: + return data.strftime(format_template) + + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = await anyio.Path(data).read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + + return data + + +async def _async_transform_typeddict( + data: Mapping[str, object], + expected_type: type, +) -> Mapping[str, object]: + result: dict[str, object] = {} + annotations = get_type_hints(expected_type, include_extras=True) + for key, value in data.items(): + if not is_given(value): + # we don't need to include omitted values here as they'll + # be stripped out before the request is sent anyway + continue + + type_ = annotations.get(key) + if type_ is None: + # we do not have a type annotation for this field, leave it as is + result[key] = value + else: + result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) + return result + + +@lru_cache(maxsize=8096) +def get_type_hints( + obj: Any, + globalns: dict[str, Any] | None = None, + localns: Mapping[str, Any] | None = None, + include_extras: bool = False, +) -> dict[str, Any]: + return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras) diff --git a/venv/lib/python3.12/site-packages/openai/_utils/_typing.py b/venv/lib/python3.12/site-packages/openai/_utils/_typing.py new file mode 100644 index 0000000..193109f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/_typing.py @@ -0,0 +1,156 @@ +from __future__ import annotations + +import sys +import typing +import typing_extensions +from typing import Any, TypeVar, Iterable, cast +from collections import abc as _c_abc +from typing_extensions import ( + TypeIs, + Required, + Annotated, + get_args, + get_origin, +) + +from ._utils import lru_cache +from .._types import InheritsGeneric +from ._compat import is_union as _is_union + + +def is_annotated_type(typ: type) -> bool: + return get_origin(typ) == Annotated + + +def is_list_type(typ: type) -> bool: + return (get_origin(typ) or typ) == list + + +def is_sequence_type(typ: type) -> bool: + origin = get_origin(typ) or typ + return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence + + +def is_iterable_type(typ: type) -> bool: + """If the given type is `typing.Iterable[T]`""" + origin = get_origin(typ) or typ + return origin == Iterable or origin == _c_abc.Iterable + + +def is_union_type(typ: type) -> bool: + return _is_union(get_origin(typ)) + + +def is_required_type(typ: type) -> bool: + return get_origin(typ) == Required + + +def is_typevar(typ: type) -> bool: + # type ignore is required because type checkers + # think this expression will always return False + return type(typ) == TypeVar # type: ignore + + +_TYPE_ALIAS_TYPES: tuple[type[typing_extensions.TypeAliasType], ...] = (typing_extensions.TypeAliasType,) +if sys.version_info >= (3, 12): + _TYPE_ALIAS_TYPES = (*_TYPE_ALIAS_TYPES, typing.TypeAliasType) + + +def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]: + """Return whether the provided argument is an instance of `TypeAliasType`. + + ```python + type Int = int + is_type_alias_type(Int) + # > True + Str = TypeAliasType("Str", str) + is_type_alias_type(Str) + # > True + ``` + """ + return isinstance(tp, _TYPE_ALIAS_TYPES) + + +# Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]] +@lru_cache(maxsize=8096) +def strip_annotated_type(typ: type) -> type: + if is_required_type(typ) or is_annotated_type(typ): + return strip_annotated_type(cast(type, get_args(typ)[0])) + + return typ + + +def extract_type_arg(typ: type, index: int) -> type: + args = get_args(typ) + try: + return cast(type, args[index]) + except IndexError as err: + raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") from err + + +def extract_type_var_from_base( + typ: type, + *, + generic_bases: tuple[type, ...], + index: int, + failure_message: str | None = None, +) -> type: + """Given a type like `Foo[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyResponse(Foo[bytes]): + ... + + extract_type_var(MyResponse, bases=(Foo,), index=0) -> bytes + ``` + + And where a generic subclass is given: + ```py + _T = TypeVar('_T') + class MyResponse(Foo[_T]): + ... + + extract_type_var(MyResponse[bytes], bases=(Foo,), index=0) -> bytes + ``` + """ + cls = cast(object, get_origin(typ) or typ) + if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains] + # we're given the class directly + return extract_type_arg(typ, index) + + # if a subclass is given + # --- + # this is needed as __orig_bases__ is not present in the typeshed stubs + # because it is intended to be for internal use only, however there does + # not seem to be a way to resolve generic TypeVars for inherited subclasses + # without using it. + if isinstance(cls, InheritsGeneric): + target_base_class: Any | None = None + for base in cls.__orig_bases__: + if base.__origin__ in generic_bases: + target_base_class = base + break + + if target_base_class is None: + raise RuntimeError( + "Could not find the generic base class;\n" + "This should never happen;\n" + f"Does {cls} inherit from one of {generic_bases} ?" + ) + + extracted = extract_type_arg(target_base_class, index) + if is_typevar(extracted): + # If the extracted type argument is itself a type variable + # then that means the subclass itself is generic, so we have + # to resolve the type argument from the class itself, not + # the base class. + # + # Note: if there is more than 1 type argument, the subclass could + # change the ordering of the type arguments, this is not currently + # supported. + return extract_type_arg(typ, index) + + return extracted + + raise RuntimeError(failure_message or f"Could not resolve inner type variable at index {index} for {typ}") diff --git a/venv/lib/python3.12/site-packages/openai/_utils/_utils.py b/venv/lib/python3.12/site-packages/openai/_utils/_utils.py new file mode 100644 index 0000000..95641eb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_utils/_utils.py @@ -0,0 +1,449 @@ +from __future__ import annotations + +import os +import re +import inspect +import functools +from typing import ( + TYPE_CHECKING, + Any, + Tuple, + Mapping, + TypeVar, + Callable, + Iterable, + Sequence, + cast, + overload, +) +from pathlib import Path +from datetime import date, datetime +from typing_extensions import TypeGuard, get_args + +import sniffio + +from .._types import Omit, NotGiven, FileTypes, ArrayFormat, HeadersLike + +_T = TypeVar("_T") +_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) +_MappingT = TypeVar("_MappingT", bound=Mapping[str, object]) +_SequenceT = TypeVar("_SequenceT", bound=Sequence[object]) +CallableT = TypeVar("CallableT", bound=Callable[..., Any]) + +if TYPE_CHECKING: + from ..lib.azure import AzureOpenAI, AsyncAzureOpenAI + + +def flatten(t: Iterable[Iterable[_T]]) -> list[_T]: + return [item for sublist in t for item in sublist] + + +def extract_files( + # TODO: this needs to take Dict but variance issues..... + # create protocol type ? + query: Mapping[str, object], + *, + paths: Sequence[Sequence[str]], + array_format: ArrayFormat = "brackets", +) -> list[tuple[str, FileTypes]]: + """Recursively extract files from the given dictionary based on specified paths. + + A path may look like this ['foo', 'files', '', 'data']. + + ``array_format`` controls how ```` segments contribute to the emitted + field name. Supported values: ``"brackets"`` (``foo[]``), ``"repeat"`` and + ``"comma"`` (``foo``), ``"indices"`` (``foo[0]``, ``foo[1]``). + + Note: this mutates the given dictionary. + """ + files: list[tuple[str, FileTypes]] = [] + for path in paths: + files.extend(_extract_items(query, path, index=0, flattened_key=None, array_format=array_format)) + return files + + +def _array_suffix(array_format: ArrayFormat, array_index: int) -> str: + if array_format == "brackets": + return "[]" + if array_format == "indices": + return f"[{array_index}]" + if array_format == "repeat" or array_format == "comma": + # Both repeat the bare field name for each file part; there is no + # meaningful way to comma-join binary parts. + return "" + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + +def _extract_items( + obj: object, + path: Sequence[str], + *, + index: int, + flattened_key: str | None, + array_format: ArrayFormat, +) -> list[tuple[str, FileTypes]]: + try: + key = path[index] + except IndexError: + if not is_given(obj): + # no value was provided - we can safely ignore + return [] + + # cyclical import + from .._files import assert_is_file_content + + # We have exhausted the path, return the entry we found. + assert flattened_key is not None + + if is_list(obj): + files: list[tuple[str, FileTypes]] = [] + for array_index, entry in enumerate(obj): + suffix = _array_suffix(array_format, array_index) + emitted_key = (flattened_key + suffix) if flattened_key else suffix + assert_is_file_content(entry, key=emitted_key) + files.append((emitted_key, cast(FileTypes, entry))) + return files + + assert_is_file_content(obj, key=flattened_key) + return [(flattened_key, cast(FileTypes, obj))] + + index += 1 + if is_dict(obj): + try: + # Remove the field if there are no more dict keys in the path, + # only "" traversal markers or end. + if all(p == "" for p in path[index:]) and (index == len(path) or is_list(obj[key])): + item = obj.pop(key) + else: + item = obj[key] + except KeyError: + # Key was not present in the dictionary, this is not indicative of an error + # as the given path may not point to a required field. We also do not want + # to enforce required fields as the API may differ from the spec in some cases. + return [] + if flattened_key is None: + flattened_key = key + else: + flattened_key += f"[{key}]" + return _extract_items( + item, + path, + index=index, + flattened_key=flattened_key, + array_format=array_format, + ) + elif is_list(obj): + if key != "": + return [] + + return flatten( + [ + _extract_items( + item, + path, + index=index, + flattened_key=( + (flattened_key if flattened_key is not None else "") + _array_suffix(array_format, array_index) + ), + array_format=array_format, + ) + for array_index, item in enumerate(obj) + ] + ) + + # Something unexpected was passed, just ignore it. + return [] + + +def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: + return not isinstance(obj, NotGiven) and not isinstance(obj, Omit) + + +# Type safe methods for narrowing types with TypeVars. +# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], +# however this cause Pyright to rightfully report errors. As we know we don't +# care about the contained types we can safely use `object` in its place. +# +# There are two separate functions defined, `is_*` and `is_*_t` for different use cases. +# `is_*` is for when you're dealing with an unknown input +# `is_*_t` is for when you're narrowing a known union type to a specific subset + + +def is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]: + return isinstance(obj, tuple) + + +def is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]: + return isinstance(obj, tuple) + + +def is_sequence(obj: object) -> TypeGuard[Sequence[object]]: + return isinstance(obj, Sequence) + + +def is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]: + return isinstance(obj, Sequence) + + +def is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]: + return isinstance(obj, Mapping) + + +def is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]: + return isinstance(obj, Mapping) + + +def is_dict(obj: object) -> TypeGuard[dict[object, object]]: + return isinstance(obj, dict) + + +def is_list(obj: object) -> TypeGuard[list[object]]: + return isinstance(obj, list) + + +def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: + return isinstance(obj, Iterable) + + +# copied from https://github.com/Rapptz/RoboDanny +def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: + size = len(seq) + if size == 0: + return "" + + if size == 1: + return seq[0] + + if size == 2: + return f"{seq[0]} {final} {seq[1]}" + + return delim.join(seq[:-1]) + f" {final} {seq[-1]}" + + +def quote(string: str) -> str: + """Add single quotation marks around the given string. Does *not* do any escaping.""" + return f"'{string}'" + + +def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: + """Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function. + + Useful for enforcing runtime validation of overloaded functions. + + Example usage: + ```py + @overload + def foo(*, a: str) -> str: ... + + + @overload + def foo(*, b: bool) -> str: ... + + + # This enforces the same constraints that a static type checker would + # i.e. that either a or b must be passed to the function + @required_args(["a"], ["b"]) + def foo(*, a: str | None = None, b: bool | None = None) -> str: ... + ``` + """ + + def inner(func: CallableT) -> CallableT: + params = inspect.signature(func).parameters + positional = [ + name + for name, param in params.items() + if param.kind + in { + param.POSITIONAL_ONLY, + param.POSITIONAL_OR_KEYWORD, + } + ] + + @functools.wraps(func) + def wrapper(*args: object, **kwargs: object) -> object: + given_params: set[str] = set() + for i, _ in enumerate(args): + try: + given_params.add(positional[i]) + except IndexError: + raise TypeError( + f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given" + ) from None + + for key in kwargs.keys(): + given_params.add(key) + + for variant in variants: + matches = all((param in given_params for param in variant)) + if matches: + break + else: # no break + if len(variants) > 1: + variations = human_join( + ["(" + human_join([quote(arg) for arg in variant], final="and") + ")" for variant in variants] + ) + msg = f"Missing required arguments; Expected either {variations} arguments to be given" + else: + assert len(variants) > 0 + + # TODO: this error message is not deterministic + missing = list(set(variants[0]) - given_params) + if len(missing) > 1: + msg = f"Missing required arguments: {human_join([quote(arg) for arg in missing])}" + else: + msg = f"Missing required argument: {quote(missing[0])}" + raise TypeError(msg) + return func(*args, **kwargs) + + return wrapper # type: ignore + + return inner + + +_K = TypeVar("_K") +_V = TypeVar("_V") + + +@overload +def strip_not_given(obj: None) -> None: ... + + +@overload +def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ... + + +@overload +def strip_not_given(obj: object) -> object: ... + + +def strip_not_given(obj: object | None) -> object: + """Remove all top-level keys where their values are instances of `NotGiven`""" + if obj is None: + return None + + if not is_mapping(obj): + return obj + + return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)} + + +def coerce_integer(val: str) -> int: + return int(val, base=10) + + +def coerce_float(val: str) -> float: + return float(val) + + +def coerce_boolean(val: str) -> bool: + return val == "true" or val == "1" or val == "on" + + +def maybe_coerce_integer(val: str | None) -> int | None: + if val is None: + return None + return coerce_integer(val) + + +def maybe_coerce_float(val: str | None) -> float | None: + if val is None: + return None + return coerce_float(val) + + +def maybe_coerce_boolean(val: str | None) -> bool | None: + if val is None: + return None + return coerce_boolean(val) + + +def removeprefix(string: str, prefix: str) -> str: + """Remove a prefix from a string. + + Backport of `str.removeprefix` for Python < 3.9 + """ + if string.startswith(prefix): + return string[len(prefix) :] + return string + + +def removesuffix(string: str, suffix: str) -> str: + """Remove a suffix from a string. + + Backport of `str.removesuffix` for Python < 3.9 + """ + if string.endswith(suffix): + return string[: -len(suffix)] + return string + + +def file_from_path(path: str) -> FileTypes: + contents = Path(path).read_bytes() + file_name = os.path.basename(path) + return (file_name, contents) + + +def get_required_header(headers: HeadersLike, header: str) -> str: + lower_header = header.lower() + if is_mapping_t(headers): + # mypy doesn't understand the type narrowing here + for k, v in headers.items(): # type: ignore + if k.lower() == lower_header and isinstance(v, str): + return v + + # to deal with the case where the header looks like X-Request-Id + intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize()) + + for normalized_header in [header, lower_header, header.upper(), intercaps_header]: + value = headers.get(normalized_header) + if value: + return value + + raise ValueError(f"Could not find {header} header") + + +def get_async_library() -> str: + try: + return sniffio.current_async_library() + except Exception: + return "false" + + +def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]: + """A version of functools.lru_cache that retains the type signature + for the wrapped function arguments. + """ + wrapper = functools.lru_cache( # noqa: TID251 + maxsize=maxsize, + ) + return cast(Any, wrapper) # type: ignore[no-any-return] + + +def json_safe(data: object) -> object: + """Translates a mapping / sequence recursively in the same fashion + as `pydantic` v2's `model_dump(mode="json")`. + """ + if is_mapping(data): + return {json_safe(key): json_safe(value) for key, value in data.items()} + + if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)): + return [json_safe(item) for item in data] + + if isinstance(data, (datetime, date)): + return data.isoformat() + + return data + + +def is_azure_client(client: object) -> TypeGuard[AzureOpenAI]: + from ..lib.azure import AzureOpenAI + + return isinstance(client, AzureOpenAI) + + +def is_async_azure_client(client: object) -> TypeGuard[AsyncAzureOpenAI]: + from ..lib.azure import AsyncAzureOpenAI + + return isinstance(client, AsyncAzureOpenAI) diff --git a/venv/lib/python3.12/site-packages/openai/_vendor/__init__.py b/venv/lib/python3.12/site-packages/openai/_vendor/__init__.py new file mode 100644 index 0000000..16d93a5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_vendor/__init__.py @@ -0,0 +1 @@ +"""Small attributed third-party components required by optional SDK integrations.""" diff --git a/venv/lib/python3.12/site-packages/openai/_vendor/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_vendor/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..89f3cc6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_vendor/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/FORK.md b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/FORK.md new file mode 100644 index 0000000..2c7117d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/FORK.md @@ -0,0 +1,20 @@ +# HTTPX2-native aiohttp adapter + +This directory vendors the HTTPX2-specific adapter from httpx-aiohttp 0.2.0. + +- Upstream: https://github.com/karpetrosyan/httpx-aiohttp +- Upstream tag: `0.2.0` +- Upstream commit: `52266a66f6bd73f828133d0fd09114179fd45b60` +- License: BSD 3-Clause; the original license is preserved in `LICENSE`. +- The upstream README is preserved without modification in `README.md`. + +The upstream distribution always installs and imports legacy HTTPX even when only its HTTPX2 adapter is used. Vendoring the three HTTPX2-specific modules lets `openai[aiohttp]` depend only on HTTPX2 and aiohttp while preserving the upstream transport behavior. + +Local changes: + +- Import `SocketTimeoutError` explicitly. aiohttp before 3.10 does not export + this exception, so importing the adapter raises `ImportError` and the SDK + keeps its default HTTPX2 clients available. This does not enable the aiohttp + adapter on older versions or lower the `openai[aiohttp]` dependency minimum. + +The fork may be removed when upstream provides an HTTPX2-only distribution or makes legacy HTTPX genuinely optional. diff --git a/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/LICENSE b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/LICENSE new file mode 100644 index 0000000..f12539f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/LICENSE @@ -0,0 +1,27 @@ +Copyright © 2025, Karen Petrosyan. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/README.md b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/README.md new file mode 100644 index 0000000..938283c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/README.md @@ -0,0 +1,25 @@ + +

httpx-aiohttp - provides transports for httpx to work on top of aiohttp, handling all high-level features like authentication, retries, and cookies through httpx, while delegating low-level socket-level HTTP messaging to aiohttp

+ +

+ + + pypi + +

+ +## Installation + +```shell +uv pip install httpx-aiohttp +``` + +For [httpx2](https://github.com/pydantic/httpx2) support, install the `httpx2` extra and use `httpx_aiohttp.httpx2.Httpx2AiohttpClient` / `httpx_aiohttp.httpx2.AiohttpTransport`: + +```shell +uv pip install httpx-aiohttp[httpx2] +``` + +## Documentation + +Project documentation is available at https://karpetrosyan.github.io/httpx-aiohttp/ diff --git a/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/__init__.py b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/__init__.py new file mode 100644 index 0000000..3029b3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/__init__.py @@ -0,0 +1,9 @@ +"""Variant of httpx_aiohttp for `httpx2 `_. + +Requires the optional ``httpx2`` dependency: ``pip install httpx-aiohttp[httpx2]``. +""" + +from .client import Httpx2AiohttpClient +from .transport import AiohttpTransport + +__all__ = ["AiohttpTransport", "Httpx2AiohttpClient"] diff --git a/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6773351 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/__pycache__/client.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/__pycache__/client.cpython-312.pyc new file mode 100644 index 0000000..36bc5f5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/__pycache__/client.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/__pycache__/transport.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/__pycache__/transport.cpython-312.pyc new file mode 100644 index 0000000..70e5a80 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/__pycache__/transport.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/client.py b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/client.py new file mode 100644 index 0000000..1187fcb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/client.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +import ssl +import typing as t + +import httpx2 as httpx + +SOCKET_OPTION = t.Union[ + t.Tuple[int, int, int], + t.Tuple[int, int, t.Union[bytes, bytearray]], + t.Tuple[int, int, None, int], +] + + +class Httpx2AiohttpClient(httpx.AsyncClient): + def _init_transport( + self, + verify: ssl.SSLContext | str | bool = True, + cert: t.Union[str, t.Tuple[str, str], t.Tuple[str, str, str], None] = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: httpx.Limits = httpx.Limits(max_connections=100, max_keepalive_connections=20), + transport: httpx.AsyncBaseTransport | None = None, + **kwargs: t.Any, + ) -> httpx.AsyncBaseTransport: + from .transport import AiohttpTransport + + if transport is not None: + return transport + + return AiohttpTransport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + ) + + def _init_proxy_transport( + self, + proxy: httpx.Proxy, + verify: ssl.SSLContext | str | bool = True, + cert: t.Union[str, t.Tuple[str, str], t.Tuple[str, str, str], None] = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: httpx.Limits = httpx.Limits(max_connections=100, max_keepalive_connections=20), + **kwargs: t.Any, + ) -> httpx.AsyncBaseTransport: + from .transport import AiohttpTransport + + return AiohttpTransport( + verify=verify, + cert=cert, + trust_env=trust_env, + http1=http1, + http2=http2, + limits=limits, + proxy=proxy, + ) diff --git a/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/transport.py b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/transport.py new file mode 100644 index 0000000..27f6b5e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_vendor/httpx_aiohttp/transport.py @@ -0,0 +1,201 @@ +from __future__ import annotations + +import contextlib +import ssl +import typing +import typing as t +from importlib import metadata +from logging import warning + +import aiohttp +import httpx2 as httpx +from aiohttp import BasicAuth, ClientTimeout, SocketTimeoutError +from aiohttp.client import ClientResponse, ClientSession + +AIOHTTP_EXC_MAP = { + aiohttp.ServerTimeoutError: httpx.TimeoutException, + SocketTimeoutError: httpx.ReadTimeout, + aiohttp.ClientConnectionError: httpx.ConnectTimeout, + aiohttp.ClientConnectorError: httpx.ConnectError, + aiohttp.ClientPayloadError: httpx.ReadError, + aiohttp.ClientProxyConnectionError: httpx.ProxyError, + aiohttp.ClientHttpProxyError: httpx.ProxyError, +} + +if metadata.version("aiohttp") >= "3.10.0": + AIOHTTP_EXC_MAP.update( + { + aiohttp.client_exceptions.NonHttpUrlClientError: httpx.UnsupportedProtocol, # type: ignore[reportAttributeAccessIssue] + aiohttp.client_exceptions.InvalidUrlClientError: httpx.UnsupportedProtocol, # type: ignore[reportAttributeAccessIssue] + } + ) + +SOCKET_OPTION = t.Union[ + t.Tuple[int, int, int], + t.Tuple[int, int, t.Union[bytes, bytearray]], + t.Tuple[int, int, None, int], +] + + +@contextlib.contextmanager +def map_aiohttp_exceptions() -> typing.Iterator[None]: + try: + yield + except Exception as exc: + mapped_exc = None + + for from_exc, to_exc in AIOHTTP_EXC_MAP.items(): + if not isinstance(exc, from_exc): # type: ignore + continue + if mapped_exc is None or issubclass(to_exc, mapped_exc): + mapped_exc = to_exc + + if mapped_exc is None: # pragma: no cover + raise + + message = str(exc) + raise mapped_exc(message) from exc + + +class AiohttpResponseStream(httpx.AsyncByteStream): + CHUNK_SIZE = 1024 * 16 + + def __init__(self, aiohttp_response: ClientResponse) -> None: + self._aiohttp_response = aiohttp_response + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + with map_aiohttp_exceptions(): + async for chunk in self._aiohttp_response.content.iter_chunked(self.CHUNK_SIZE): + yield chunk + + async def aclose(self) -> None: + with map_aiohttp_exceptions(): + await self._aiohttp_response.__aexit__(None, None, None) + + +class AiohttpTransport(httpx.AsyncBaseTransport): + def __init__( + self, + verify: ssl.SSLContext | str | bool = True, + cert: t.Union[str, t.Tuple[str, str], t.Tuple[str, str, str], None] = None, + trust_env: bool = True, + http1: bool = True, + http2: bool = False, + limits: httpx.Limits = httpx.Limits(max_connections=100, max_keepalive_connections=20), + proxy: httpx.Proxy | None = None, + uds: str | None = None, + local_address: str | None = None, + retries: int = 0, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + client: ClientSession | t.Callable[[], ClientSession] | None = None, + # Additional keyword arguments for future compatibility + # If httpx decides to add one, we won't break the API + **kwargs: t.Dict[str, t.Any], + ) -> None: + if http2: + if not http1: + raise httpx.UnsupportedProtocol("HTTP/2 is not supported by aiohttp transport, use HTTP/1.1 instead.") + warning("HTTP/2 is not supported by aiohttp transport, using HTTP/1.1 instead.") + + ssl_context = httpx.create_ssl_context( + verify=verify, + cert=cert, + trust_env=trust_env, + ) + + self.ssl_context = ssl_context + self.proxy = proxy + self.limits = limits + self.retries = retries + self.socket_options = socket_options or [] + self.uds = uds + self.local_address = local_address + + self.client = client + + def get_client(self) -> ClientSession: + if callable(self.client): + return self.client() + elif isinstance(self.client, ClientSession): + return self.client + else: + limit_kwarg = ( + { + "limit": self.limits.max_connections, + } + if self.limits.max_connections is not None + else {} + ) + if self.uds: + connector = aiohttp.UnixConnector( + path=self.uds, + keepalive_timeout=self.limits.keepalive_expiry, + **limit_kwarg, # type: ignore + ) + else: + connector = aiohttp.TCPConnector( + keepalive_timeout=self.limits.keepalive_expiry, + ssl=self.ssl_context, + local_addr=(self.local_address, 0) if self.local_address else None, + **limit_kwarg, # type: ignore + ) + return ClientSession(connector=connector) + + async def handle_async_request( + self, + request: httpx.Request, + ) -> httpx.Response: + if not isinstance(self.client, ClientSession): + self.client = self.get_client() + + timeout = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname") + + with map_aiohttp_exceptions(): + data: t.Union[bytes, httpx.AsyncByteStream, None] + try: + data = request.content + if data == b"": + data = None + + except httpx.RequestNotRead: + data = request.stream # type: ignore + request.headers.pop("transfer-encoding", None) # handled by aiohttp + + response = await self.client.request( + method=request.method, + url=str(request.url) if request.url else "https://127.0.0.1:8000/", + headers=request.headers, + data=data, + allow_redirects=False, + auto_decompress=False, + compress=False, + timeout=ClientTimeout( + sock_connect=timeout.get("connect"), + sock_read=timeout.get("read"), + connect=timeout.get("pool"), + ), + server_hostname=sni_hostname, + proxy=str(self.proxy.url) if self.proxy else None, + proxy_auth=BasicAuth(self.proxy.auth[0], self.proxy.auth[1]) + if self.proxy and self.proxy.auth + else None, + proxy_headers=self.proxy.headers if self.proxy else None, + ).__aenter__() + + extensions = {"http_version": b"HTTP/1.1"} + + if response.reason: + extensions["reason_phrase"] = response.reason.encode() + + return httpx.Response( + status_code=response.status, + headers=response.raw_headers, + stream=AiohttpResponseStream(response), + request=request, + extensions=extensions, + ) + + async def aclose(self) -> None: + if isinstance(self.client, ClientSession): + await self.client.close() diff --git a/venv/lib/python3.12/site-packages/openai/_version.py b/venv/lib/python3.12/site-packages/openai/_version.py new file mode 100644 index 0000000..cf3e856 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/_version.py @@ -0,0 +1,2 @@ +__title__ = "openai" +__version__ = "3.20.0" # x-release-please-version diff --git a/venv/lib/python3.12/site-packages/openai/auth/__init__.py b/venv/lib/python3.12/site-packages/openai/auth/__init__.py new file mode 100644 index 0000000..fbe72c8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/auth/__init__.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from ._workload import ( + WorkloadIdentity as WorkloadIdentity, + SubjectTokenProvider as SubjectTokenProvider, + WorkloadIdentityAuth as WorkloadIdentityAuth, + X509WorkloadIdentity as X509WorkloadIdentity, + SubjectTokenWorkloadIdentity as SubjectTokenWorkloadIdentity, + gcp_id_token_provider as gcp_id_token_provider, + x509_workload_identity as x509_workload_identity, + k8s_service_account_token_provider as k8s_service_account_token_provider, + azure_managed_identity_token_provider as azure_managed_identity_token_provider, +) + +__all__ = [ + "SubjectTokenProvider", + "WorkloadIdentity", + "SubjectTokenWorkloadIdentity", + "X509WorkloadIdentity", + "WorkloadIdentityAuth", + "x509_workload_identity", + "k8s_service_account_token_provider", + "azure_managed_identity_token_provider", + "gcp_id_token_provider", +] diff --git a/venv/lib/python3.12/site-packages/openai/auth/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/auth/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a433616 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/auth/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/auth/__pycache__/_workload.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/auth/__pycache__/_workload.cpython-312.pyc new file mode 100644 index 0000000..fdc6ca4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/auth/__pycache__/_workload.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/auth/__pycache__/_x509.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/auth/__pycache__/_x509.cpython-312.pyc new file mode 100644 index 0000000..66069f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/auth/__pycache__/_x509.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/auth/_workload.py b/venv/lib/python3.12/site-packages/openai/auth/_workload.py new file mode 100644 index 0000000..4f9797f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/auth/_workload.py @@ -0,0 +1,387 @@ +from __future__ import annotations + +import time +import threading +from typing import Any, Generic, TypeVar, Callable, TypedDict, cast +from pathlib import Path +from typing_extensions import Literal, TypeAlias, NotRequired, override + +import httpx2 + +from .._utils import is_dict +from .._httpx2 import DefaultHttpx2Client, _loaded_legacy_httpx +from .._exceptions import OAuthError, OpenAIError, SubjectTokenProviderError +from .._utils._sync import to_thread + +TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange" +DEFAULT_TOKEN_EXCHANGE_URL = "https://auth.openai.com/oauth/token" +DEFAULT_REFRESH_BUFFER_SECONDS = 1200 + +SUBJECT_TOKEN_TYPES = { + "jwt": "urn:ietf:params:oauth:token-type:jwt", + "id": "urn:ietf:params:oauth:token-type:id_token", +} + + +class SubjectTokenProvider(TypedDict): + token_type: Literal["jwt", "id"] + get_token: Callable[[], str] + + +class WorkloadIdentity(TypedDict): + """Identity provider resource id in WIFAPI.""" + + identity_provider_id: str + + """Service account id to bind the verified external identity to.""" + service_account_id: str + + """The provider configuration for obtaining the subject token.""" + provider: SubjectTokenProvider + + """Optional buffer time in seconds to refresh the OpenAI token before it expires. Defaults to 1200 seconds (20 minutes).""" + refresh_buffer_seconds: NotRequired[float] + + +SubjectTokenWorkloadIdentity: TypeAlias = WorkloadIdentity + + +class X509WorkloadIdentity(TypedDict): + """Authenticate with the client certificate configured on the HTTP transport.""" + + type: Literal["x509"] + identity_provider_id: str + service_account_id: str + refresh_buffer_seconds: NotRequired[float] + + +_WorkloadIdentityT = TypeVar("_WorkloadIdentityT", WorkloadIdentity, X509WorkloadIdentity) + + +def x509_workload_identity( + *, + identity_provider_id: str, + service_account_id: str, + refresh_buffer_seconds: float | None = None, +) -> X509WorkloadIdentity: + """Configure X.509 workload identity without handling certificate material.""" + identity: X509WorkloadIdentity = { + "type": "x509", + "identity_provider_id": identity_provider_id, + "service_account_id": service_account_id, + } + if refresh_buffer_seconds is not None: + identity["refresh_buffer_seconds"] = refresh_buffer_seconds + return identity + + +def k8s_service_account_token_provider( + token_file_path: str | Path = "/var/run/secrets/kubernetes.io/serviceaccount/token", +) -> SubjectTokenProvider: + """ + Get a subject token provider for Kubernetes clusters with Workload Identity configured. + + Cloud providers typically mount the subject token as a file in the container. + + Args: + token_file_path: path to the mounted service account token file. Defaults to `/var/run/secrets/kubernetes.io/serviceaccount/token`. + """ + + def get_token() -> str: + try: + with open(token_file_path, "r") as f: + token = f.read().strip() + if not token: + raise SubjectTokenProviderError(f"The token file at {token_file_path} is empty.") + return token + except Exception as e: + raise SubjectTokenProviderError(f"Failed to read the token file at {token_file_path}: {e}") from e + + return {"token_type": "jwt", "get_token": get_token} + + +def azure_managed_identity_token_provider( + resource: str = "https://management.azure.com/", + *, + object_id: str | None = None, + client_id: str | None = None, + msi_res_id: str | None = None, + api_version: str = "2018-02-01", + timeout: float = 10.0, + http_client: httpx2.Client | None = None, +) -> SubjectTokenProvider: + """ + Get a subject token provider for Azure Managed Identities. + + See: https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http + + Args: + resource: the resource URI to request a token for. Defaults to `https://management.azure.com/` (Azure Resource Manager). + object_id: the object ID of the managed identity to use, when multiple are assigned. + client_id: the client ID of the managed identity to use, when multiple are assigned. + msi_res_id: the ARM resource ID of the managed identity to use, when multiple are assigned. + api_version: the Azure IMDS API version. Defaults to `2018-02-01`. + timeout: the request timeout in seconds. Defaults to 10.0. + http_client: optional httpx2.Client instance to use for requests. If not provided, a new client will be created for each request. + """ + + def get_token() -> str: + try: + url = "http://169.254.169.254/metadata/identity/oauth2/token" + params: dict[str, str] = {"api-version": api_version, "resource": resource} + if object_id is not None: + params["object_id"] = object_id + if client_id is not None: + params["client_id"] = client_id + if msi_res_id is not None: + params["msi_res_id"] = msi_res_id + + if http_client is not None: + response = http_client.get(url, params=params, headers={"Metadata": "true"}, timeout=timeout) + else: + with httpx2.Client() as client: + response = client.get(url, params=params, headers={"Metadata": "true"}, timeout=timeout) + + if response.is_error: + raise SubjectTokenProviderError( + f"Failed to fetch Azure subject token from IMDS: HTTP {response.status_code}", + response=response, + ) + data = response.json() + token = data.get("access_token") + if not token: + raise SubjectTokenProviderError( + "Azure IMDS response did not include an access_token", response=response + ) + return cast(str, token) + except Exception as e: + raise SubjectTokenProviderError(f"Failed to fetch Azure subject token from IMDS: {e}") from e + + return {"token_type": "jwt", "get_token": get_token} + + +def gcp_id_token_provider( + audience: str = "https://api.openai.com/v1", + *, + timeout: float = 10.0, + http_client: httpx2.Client | None = None, +) -> SubjectTokenProvider: + """ + Get a subject token provider for GCP VM instances using the instance metadata server. + + See: https://cloud.google.com/compute/docs/instances/verifying-instance-identity + + Args: + audience: the unique URI agreed upon by both the instance and the system verifying + the instance's identity. Defaults to `https://api.openai.com/v1`. + timeout: the request timeout in seconds. Defaults to 10.0. + http_client: optional httpx2.Client instance to use for requests. If not provided, a new client will be created for each request. + """ + + def get_token() -> str: + try: + url = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity" + params = {"audience": audience} + + if http_client is not None: + response = http_client.get(url, params=params, headers={"Metadata-Flavor": "Google"}, timeout=timeout) + else: + with httpx2.Client() as client: + response = client.get(url, params=params, headers={"Metadata-Flavor": "Google"}, timeout=timeout) + + if response.is_error: + raise SubjectTokenProviderError( + f"Failed to fetch GCP subject token from metadata server: HTTP {response.status_code}", + response=response, + ) + token = response.text.strip() + if not token: + raise SubjectTokenProviderError("GCP metadata server returned an empty token", response=response) + return token + except Exception as e: + raise SubjectTokenProviderError(f"Failed to fetch GCP subject token from metadata server: {e}") from e + + return {"token_type": "id", "get_token": get_token} + + +class _WorkloadIdentityAuth(Generic[_WorkloadIdentityT]): + def __init__( + self, + *, + workload_identity: _WorkloadIdentityT, + token_exchange_url: str = DEFAULT_TOKEN_EXCHANGE_URL, + _use_httpx2: bool = True, + ) -> None: + self.workload_identity: _WorkloadIdentityT = workload_identity + self.token_exchange_url = token_exchange_url + self._use_httpx2 = _use_httpx2 + self._follow_redirects: bool | None = None + + self._cached_token: str | None = None + self._cached_token_expires_at_monotonic: float | None = None + self._cached_token_refresh_at_monotonic: float | None = None + self._refreshing: bool = False + self._lock = threading.Lock() + self._condition = threading.Condition(self._lock) + + def get_token(self) -> str: + with self._lock: + while self._refreshing and self._token_unusable(): + self._condition.wait() + + if not self._token_unusable() and not self._needs_refresh(): + return cast(str, self._cached_token) + + if self._refreshing: + while self._refreshing: + self._condition.wait() + token = self._cached_token # type: ignore[unreachable] + if self._token_unusable(): + raise RuntimeError("Token is unusable after refresh completed") + return cast(str, token) + + self._refreshing = True + + try: + self._perform_refresh() + with self._lock: + if self._token_unusable(): + raise RuntimeError("Token is unusable after refresh completed") + return cast(str, self._cached_token) + finally: + with self._lock: + self._refreshing = False + self._condition.notify_all() + + async def get_token_async(self) -> str: + return await to_thread(self.get_token) + + def invalidate_token(self, token: str | None = None) -> None: + with self._lock: + if token is not None and self._cached_token != token: + return + self._cached_token = None + self._cached_token_expires_at_monotonic = None + self._cached_token_refresh_at_monotonic = None + + def _perform_refresh(self) -> None: + token_data = self._fetch_token_from_exchange() + self._store_token(token_data) + + def _store_token(self, token_data: dict[str, Any]) -> None: + now = time.monotonic() + expires_in = token_data["expires_in"] + + with self._lock: + self._cached_token = token_data["access_token"] + self._cached_token_expires_at_monotonic = now + expires_in + self._cached_token_refresh_at_monotonic = now + self._refresh_delay_seconds(expires_in) + + def _fetch_token_from_exchange(self) -> dict[str, Any]: + raise NotImplementedError("Workload identity authentication must implement token exchange") + + def _handle_token_response(self, response: httpx2.Response) -> dict[str, Any]: + try: + body = response.json() if response.content else None + except ValueError: + body = None + + if response.status_code in (400, 401, 403): + raise OAuthError(response=response, body=body) + + if response.is_success: + if body is None: + raise OpenAIError("Token exchange succeeded but response body was empty") + if not is_dict(body): + raise OpenAIError("Token exchange succeeded but response body was not a JSON object") + access_token = body.get("access_token") + expires_in = body.get("expires_in") + if not isinstance(access_token, str) or not access_token: + raise OpenAIError("Token exchange response did not include a valid access_token") + return {"access_token": access_token, "expires_in": self._validate_expires_in(expires_in)} + + raise OpenAIError( + f"Token exchange failed with status {response.status_code}", + ) + + def _validate_expires_in(self, expires_in: object) -> float: + if not isinstance(expires_in, (int, float)): + raise OpenAIError("Token exchange response did not include a valid expires_in") + return float(expires_in) + + def _token_unusable(self) -> bool: + return self._cached_token is None or self._token_expired() + + def _token_expired(self) -> bool: + if self._cached_token_expires_at_monotonic is None: + return True + return time.monotonic() >= self._cached_token_expires_at_monotonic + + def _needs_refresh(self) -> bool: + if self._cached_token_refresh_at_monotonic is None: + return False + return time.monotonic() >= self._cached_token_refresh_at_monotonic + + def _refresh_delay_seconds(self, expires_in: float) -> float: + configured_buffer = self.workload_identity.get("refresh_buffer_seconds", DEFAULT_REFRESH_BUFFER_SECONDS) + effective_buffer = min(configured_buffer, expires_in / 2) + return max(expires_in - effective_buffer, 0.0) + + def _can_retry_request(self, request: httpx2.Request) -> bool: + """Preserve the established subject-token request retry behavior.""" + del request + return True + + def _prepare_retry_request(self, request: httpx2.Request) -> None: + """Preserve the established subject-token request retry behavior.""" + del request + + +class WorkloadIdentityAuth(_WorkloadIdentityAuth[WorkloadIdentity]): + def __init__( + self, + *, + workload_identity: WorkloadIdentity, + token_exchange_url: str = DEFAULT_TOKEN_EXCHANGE_URL, + _use_httpx2: bool = True, + ) -> None: + super().__init__( + workload_identity=workload_identity, + token_exchange_url=token_exchange_url, + _use_httpx2=_use_httpx2, + ) + + @override + def _fetch_token_from_exchange(self) -> dict[str, Any]: + subject_token = self._get_subject_token() + token_type = self.workload_identity["provider"]["token_type"] + subject_token_type = SUBJECT_TOKEN_TYPES.get(token_type) + if subject_token_type is None: + raise OpenAIError( + f"Unsupported token type: {token_type!r}. Supported types: {', '.join(SUBJECT_TOKEN_TYPES.keys())}" + ) + + legacy_httpx = _loaded_legacy_httpx() if not self._use_httpx2 else None + exchange_client = ( + legacy_httpx.Client() if legacy_httpx is not None else DefaultHttpx2Client(follow_redirects=False) + ) + with exchange_client as client: + response = client.post( + self.token_exchange_url, + json={ + "grant_type": TOKEN_EXCHANGE_GRANT_TYPE, + "subject_token": subject_token, + "subject_token_type": subject_token_type, + "identity_provider_id": self.workload_identity["identity_provider_id"], + "service_account_id": self.workload_identity["service_account_id"], + }, + timeout=10.0, + ) + return self._handle_token_response(response) + + def _get_subject_token(self) -> str: + provider = self.workload_identity["provider"] + subject_token = provider["get_token"]() + if not subject_token: + raise OpenAIError("The workload identity provider returned an empty subject token") + return subject_token diff --git a/venv/lib/python3.12/site-packages/openai/auth/_x509.py b/venv/lib/python3.12/site-packages/openai/auth/_x509.py new file mode 100644 index 0000000..f87e9f5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/auth/_x509.py @@ -0,0 +1,674 @@ +from __future__ import annotations + +import re +import math +import time +import email.utils +from typing import Any, NoReturn, cast +from contextvars import ContextVar +from typing_extensions import TypeIs, override + +import anyio +import httpx2 + +from .._utils import is_dict +from .._httpx2 import timeout_exceptions, normalize_httpx_url, _loaded_legacy_httpx +from ._workload import ( + TOKEN_EXCHANGE_GRANT_TYPE, + WorkloadIdentity, + X509WorkloadIdentity, + _WorkloadIdentityAuth, +) +from .._constants import MAX_RETRY_DELAY, INITIAL_RETRY_DELAY, MAX_RETRY_AFTER_DELAY +from .._exceptions import OAuthError, OpenAIError, APITimeoutError, APIConnectionError + +MTLS_API_BASE_URL = "https://mtls.api.openai.com/v1" +_X509_TOKEN_EXCHANGE_URL = "https://mtls.auth.openai.com/oauth/token" +_X509_SUBJECT_TOKEN_TYPE = "urn:openai:params:oauth:token-type:x509" +_MAX_EXCHANGE_RETRIES = 2 +_REPLAY_POSITION_EXTENSION = "openai_x509_replay_position" +_REPLAY_FILE_POSITIONS_EXTENSION = "openai_x509_replay_file_positions" +_ALLOWED_IDENTITY_FIELDS = {"type", "identity_provider_id", "service_account_id", "refresh_buffer_seconds"} +_BEARER_ACCESS_TOKEN = re.compile(r"[A-Za-z0-9._~+/-]+=*") +_MTLS_REGIONAL_BASE_URLS = { + "global": MTLS_API_BASE_URL, + "us": "https://mtls-us.api.openai.com/v1", + "eu": "https://mtls-eu.api.openai.com/v1", +} +_OPENAI_MTLS_HOSTS = {httpx2.URL(url).host for url in _MTLS_REGIONAL_BASE_URLS.values()} +_EXCHANGE_REQUEST_TIMEOUT: ContextVar[dict[str, float | None] | None] = ContextVar( + "openai_x509_exchange_request_timeout", default=None +) + + +class _TransientTokenExchangeError(Exception): + def __init__(self, error: OpenAIError) -> None: + self.error = error + + +def validate_x509_api_url(url: httpx2.URL | str, *, expected_origin: httpx2.URL | None = None) -> None: + normalized_url = normalize_httpx_url(url) + if normalized_url.scheme != "https" or not normalized_url.host: + raise OpenAIError("X.509 workload identity requires an absolute HTTPS API URL") + + if normalized_url.username or normalized_url.password: + raise OpenAIError("X.509 workload identity API URLs cannot contain user credentials") + + if expected_origin is not None and (normalized_url.host, normalized_url.port) != ( + expected_origin.host, + expected_origin.port, + ): + raise OpenAIError("X.509 workload identity requests must use the configured API origin") + + +def validate_x509_api_credentials(request: httpx2.Request) -> None: + if any( + header.lower().replace("_", "-") in ("api-key", "x-api-key", "proxy-authorization") + for header in request.headers + ): + raise OpenAIError("X.509 workload identity requests cannot include API-key or proxy credentials") + + +def validate_x509_request_authority(request: httpx2.Request) -> None: + host_headers = request.headers.get_list("host") + if ( + len(host_headers) != 1 + or any(delimiter in host_headers[0] for delimiter in "/?#@\\") + or any(header.startswith(":") for header in request.headers) + ): + raise OpenAIError("X.509 workload identity requests require exactly one valid Host authority") + + try: + host_url = httpx2.URL(f"https://{host_headers[0]}") + except httpx2.InvalidURL as error: + raise OpenAIError("X.509 workload identity requests require a valid Host authority") from error + + if ( + host_url.username + or host_url.password + or host_url.path != "/" + or host_url.query + or host_url.fragment + or (host_url.host, host_url.port) != (request.url.host, request.url.port) + ): + raise OpenAIError("X.509 workload identity Host authority must match the request URL") + + +def _validate_transport_request( + request: httpx2.Request, + *, + expected_origin: httpx2.URL, + expected_authorization: str | None, + token_exchange: bool, +) -> None: + validate_x509_api_url(request.url, expected_origin=expected_origin) + validate_x509_request_authority(request) + + target = request.extensions.get("target") + if target is not None and target != request.url.raw_path: + raise OpenAIError("X.509 workload identity request target must match the request URL") + + sni_hostname = request.extensions.get("sni_hostname") + if request.url.host in _OPENAI_MTLS_HOSTS and sni_hostname is not None: + if not isinstance(sni_hostname, str) or sni_hostname.lower() != expected_origin.host.lower(): + raise OpenAIError("X.509 workload identity TLS hostname must match the configured origin") + + if token_exchange: + if str(request.url) != _X509_TOKEN_EXCHANGE_URL: + raise OpenAIError("X.509 token exchange requests must use the pinned authentication URL") + if any( + header.lower().replace("_", "-") + in ("authorization", "proxy-authorization", "cookie", "x-api-key", "api-key") + for header in request.headers + ): + raise OpenAIError("X.509 token exchange requests cannot include API credentials") + else: + validate_x509_api_credentials(request) + if request.headers.get("Authorization") != expected_authorization: + raise OpenAIError("X.509 workload identity authorization cannot be changed by HTTP request hooks") + + +class _SyncX509ForwardingTransport(httpx2.BaseTransport): + def __init__( + self, + *, + http_client: httpx2.Client, + expected_origin: httpx2.URL, + expected_authorization: str | None, + token_exchange: bool, + ) -> None: + self._http_client = http_client + self._expected_origin = expected_origin + self._expected_authorization = expected_authorization + self._token_exchange = token_exchange + + @override + def handle_request(self, request: httpx2.Request) -> httpx2.Response: + _validate_transport_request( + request, + expected_origin=self._expected_origin, + expected_authorization=self._expected_authorization, + token_exchange=self._token_exchange, + ) + if self._http_client.is_closed: + raise RuntimeError("Cannot send a request, as the client has been closed.") + return self._http_client._transport_for_url(request.url).handle_request(request) + + @override + def close(self) -> None: + # The caller owns the selected connection pool and proxy transports. + return None + + +class _AsyncX509ForwardingTransport(httpx2.AsyncBaseTransport): + def __init__( + self, + *, + http_client: httpx2.AsyncClient, + expected_origin: httpx2.URL, + expected_authorization: str | None, + token_exchange: bool, + ) -> None: + self._http_client = http_client + self._expected_origin = expected_origin + self._expected_authorization = expected_authorization + self._token_exchange = token_exchange + + @override + async def handle_async_request(self, request: httpx2.Request) -> httpx2.Response: + _validate_transport_request( + request, + expected_origin=self._expected_origin, + expected_authorization=self._expected_authorization, + token_exchange=self._token_exchange, + ) + if self._http_client.is_closed: + raise RuntimeError("Cannot send a request, as the client has been closed.") + return await self._http_client._transport_for_url(request.url).handle_async_request(request) + + @override + async def aclose(self) -> None: + # The caller owns the selected connection pool and proxy transports. + return None + + +def _scoped_sync_client( + http_client: httpx2.Client, + *, + expected_origin: httpx2.URL, + expected_authorization: str | None = None, + token_exchange: bool = False, +) -> httpx2.Client: + transport = _SyncX509ForwardingTransport( + http_client=http_client, + expected_origin=expected_origin, + expected_authorization=expected_authorization, + token_exchange=token_exchange, + ) + legacy_httpx = _loaded_legacy_httpx() + client_type = httpx2.Client + if legacy_httpx is not None and not isinstance(cast(object, http_client), httpx2.Client): + client_type = legacy_httpx.Client + scoped_client = client_type( + transport=transport, + timeout=http_client.timeout, + event_hooks=None if token_exchange else http_client.event_hooks, + default_encoding=http_client._default_encoding, + trust_env=False, + ) + if not token_exchange: + scoped_client._cookies = http_client.cookies + return scoped_client + + +def _scoped_async_client( + http_client: httpx2.AsyncClient, + *, + expected_origin: httpx2.URL, + expected_authorization: str | None = None, + token_exchange: bool = False, +) -> httpx2.AsyncClient: + transport = _AsyncX509ForwardingTransport( + http_client=http_client, + expected_origin=expected_origin, + expected_authorization=expected_authorization, + token_exchange=token_exchange, + ) + legacy_httpx = _loaded_legacy_httpx() + client_type = httpx2.AsyncClient + if legacy_httpx is not None and not isinstance(cast(object, http_client), httpx2.AsyncClient): + client_type = legacy_httpx.AsyncClient + scoped_client = client_type( + transport=transport, + timeout=http_client.timeout, + event_hooks=None if token_exchange else http_client.event_hooks, + default_encoding=http_client._default_encoding, + trust_env=False, + ) + if not token_exchange: + scoped_client._cookies = http_client.cookies + return scoped_client + + +def _as_finite_float(value: object) -> float | None: + if isinstance(value, bool) or not isinstance(value, (int, float)): + return None + try: + value = float(value) + except OverflowError: + return None + return value if math.isfinite(value) else None + + +def is_x509_workload_identity( + identity: WorkloadIdentity | X509WorkloadIdentity | None, +) -> TypeIs[X509WorkloadIdentity]: + return identity is not None and identity.get("type") == "x509" + + +def x509_data_residency_base_url( + base_url: httpx2.URL | str | None, + data_residency: str | None, + workload_identity: WorkloadIdentity | X509WorkloadIdentity | None, +) -> httpx2.URL | str | None: + if data_residency is None or not is_x509_workload_identity(workload_identity): + return base_url + if data_residency not in _MTLS_REGIONAL_BASE_URLS: + raise OpenAIError("X.509 workload identity requires a supported regional mTLS endpoint") + return _MTLS_REGIONAL_BASE_URLS[data_residency] + + +def x509_safe_environment_headers( + headers: dict[str, str], workload_identity: X509WorkloadIdentity | None +) -> dict[str, str]: + if workload_identity is None: + return headers + return {name: value for name, value in headers.items() if name.lower() != "authorization"} + + +def _validate_identity(identity: X509WorkloadIdentity) -> None: + if "provider" in identity or "client_id" in identity: + raise OpenAIError("X.509 workload identity does not accept a subject-token provider or client ID") + + if set(identity) - _ALLOWED_IDENTITY_FIELDS: + raise OpenAIError("X.509 workload identity accepts only identity IDs and an optional refresh buffer") + + if any( + not isinstance(identity.get(field), str) or not identity.get(field) + for field in ( + "identity_provider_id", + "service_account_id", + ) + ): + raise OpenAIError("X.509 workload identity requires identity-provider and service-account IDs") + + refresh_buffer = cast(object, identity.get("refresh_buffer_seconds")) + if refresh_buffer is not None: + refresh_buffer_value = _as_finite_float(refresh_buffer) + if refresh_buffer_value is None or refresh_buffer_value < 0: + raise OpenAIError("X.509 workload identity requires a finite, non-negative refresh buffer") + + +def _exchange_payload(identity: X509WorkloadIdentity) -> dict[str, str]: + return { + "grant_type": TOKEN_EXCHANGE_GRANT_TYPE, + "subject_token_type": _X509_SUBJECT_TOKEN_TYPE, + "identity_provider_id": identity["identity_provider_id"], + "service_account_id": identity["service_account_id"], + } + + +def _token_exchange_request( + identity: X509WorkloadIdentity, + *, + http_client: httpx2.Client | httpx2.AsyncClient, +) -> httpx2.Request: + request_type = httpx2.Request + legacy_httpx = _loaded_legacy_httpx() + if legacy_httpx is not None and not isinstance(cast(object, http_client), (httpx2.Client, httpx2.AsyncClient)): + request_type = cast(type[httpx2.Request], cast(Any, legacy_httpx).Request) + + configured_timeout = _EXCHANGE_REQUEST_TIMEOUT.get() + timeout = { + phase: min(value, 10.0) if value is not None else 10.0 + for phase, value in (configured_timeout or httpx2.Timeout(10.0).as_dict()).items() + } + return request_type( + "POST", + _X509_TOKEN_EXCHANGE_URL, + json=_exchange_payload(identity), + extensions={"timeout": timeout}, + ) + + +def _retry_delay(response: httpx2.Response | None, attempt: int) -> float | None: + if response is not None: + should_retry = response.headers.get("x-should-retry") + if response.status_code in (400, 401, 403) or should_retry == "false": + return None + if should_retry != "true" and response.status_code not in (408, 409, 429) and response.status_code < 500: + return None + + retry_after_ms = response.headers.get("retry-after-ms") + retry_after = response.headers.get("retry-after") + if retry_after_ms is not None: + try: + millisecond_delay = float(retry_after_ms) / 1000 + except ValueError: + pass + else: + if math.isfinite(millisecond_delay) and 0 <= millisecond_delay <= MAX_RETRY_AFTER_DELAY: + return millisecond_delay + if millisecond_delay > MAX_RETRY_AFTER_DELAY: + return None + if retry_after is not None: + try: + delay = float(retry_after) + except ValueError: + try: + parsed = email.utils.parsedate_tz(retry_after) + delay = float(email.utils.mktime_tz(parsed) - time.time()) if parsed is not None else -1 + except (OverflowError, OSError, ValueError): + delay = -1 + + if math.isfinite(delay) and 0 <= delay <= MAX_RETRY_AFTER_DELAY: + return delay + if delay > MAX_RETRY_AFTER_DELAY: + return None + + return float(min(INITIAL_RETRY_DELAY * 2**attempt, MAX_RETRY_DELAY)) + + +def _is_replayable_request(request: httpx2.Request) -> bool: + if isinstance(getattr(request, "_content", None), bytes): + return True + + stream = request.stream + fields = getattr(stream, "fields", None) + if isinstance(fields, list): + file_positions: list[tuple[object, int]] = [] + for field in cast(list[object], fields): + file = getattr(field, "file", None) + if file is None or isinstance(file, (str, bytes)): + continue + seekable = getattr(file, "seekable", None) + seek = getattr(file, "seek", None) + tell = getattr(file, "tell", None) + try: + if not callable(seekable) or not seekable() or not callable(seek) or not callable(tell): + return False + position = tell() + except (OSError, ValueError): + return False + if not isinstance(position, int): + return False + file_positions.append((file, position)) + request.extensions[_REPLAY_FILE_POSITIONS_EXTENSION] = file_positions + return True + + source = getattr(stream, "_stream", stream) + seekable = getattr(source, "seekable", None) + seek = getattr(source, "seek", None) + tell = getattr(source, "tell", None) + try: + if not callable(seekable) or not seekable() or not callable(seek) or not callable(tell): + return False + request.extensions[_REPLAY_POSITION_EXTENSION] = tell() + except (OSError, ValueError): + return False + return True + + +def _transport_errors() -> tuple[type[Exception], ...]: + legacy_httpx = _loaded_legacy_httpx() + if legacy_httpx is None: + return (httpx2.TransportError,) + legacy_transport_error = cast(type[Exception], getattr(legacy_httpx, "TransportError", httpx2.TransportError)) + return (httpx2.TransportError, legacy_transport_error) + + +def _raise_transport_error(error: Exception, *, request: httpx2.Request) -> NoReturn: + if isinstance(error, timeout_exceptions()): + raise APITimeoutError(request=request) from error + raise APIConnectionError(request=request) from error + + +class _X509WorkloadIdentityAuth(_WorkloadIdentityAuth[X509WorkloadIdentity]): + def __init__(self, *, workload_identity: X509WorkloadIdentity, max_retries: int) -> None: + _validate_identity(workload_identity) + super().__init__(workload_identity=workload_identity, token_exchange_url=_X509_TOKEN_EXCHANGE_URL) + self._max_exchange_retries = min(max(max_retries, 0), _MAX_EXCHANGE_RETRIES) + self._follow_redirects = False + + @override + def _handle_token_response(self, response: httpx2.Response) -> dict[str, Any]: + if response.status_code not in (400, 401, 403): + token_data = super()._handle_token_response(response) + response_body = response.json() + token_type = response_body.get("token_type") + if "token_type" in response_body and (not isinstance(token_type, str) or token_type.lower() != "bearer"): + raise OpenAIError("X.509 token exchange response must use the Bearer token type") + if _BEARER_ACCESS_TOKEN.fullmatch(token_data["access_token"]) is None: + raise OpenAIError("X.509 token exchange response did not include a valid Bearer access_token") + return token_data + + try: + response_body = response.json() if response.content else None + except ValueError: + response_body = None + + oauth_error = response_body.get("error") if is_dict(response_body) else None + safe_body = {"error": oauth_error} if isinstance(oauth_error, str) else None + raise OAuthError(response=response, body=safe_body) + + @override + def _validate_expires_in(self, expires_in: object) -> float: + expires_in_value = _as_finite_float(expires_in) + if expires_in_value is None or expires_in_value <= 0: + raise OpenAIError("X.509 token exchange response did not include a positive, finite expires_in") + return expires_in_value + + @override + def _can_retry_request(self, request: httpx2.Request) -> bool: + return _is_replayable_request(request) + + @override + def _prepare_retry_request(self, request: httpx2.Request) -> None: + file_positions = request.extensions.get(_REPLAY_FILE_POSITIONS_EXTENSION) + if isinstance(file_positions, list): + for file, file_position in cast(list[tuple[object, int]], file_positions): + seek = getattr(file, "seek", None) + if callable(seek): + seek(file_position) + return + + position = request.extensions.get(_REPLAY_POSITION_EXTENSION) + if not isinstance(position, int): + return + source = getattr(request.stream, "_stream", request.stream) + seek = getattr(source, "seek", None) + if callable(seek): + seek(position) + + def _usable_token_after_transient_failure(self) -> str | None: + with self._lock: + if self._token_unusable(): + return None + self._cached_token_refresh_at_monotonic = time.monotonic() + INITIAL_RETRY_DELAY + return self._cached_token + + @override + def _perform_refresh(self) -> None: + try: + super()._perform_refresh() + except (APIConnectionError, _TransientTokenExchangeError): + if self._usable_token_after_transient_failure() is None: + raise + + def _handle_exchange_response(self, response: httpx2.Response) -> dict[str, Any]: + try: + return self._handle_token_response(response) + except OpenAIError as error: + if ( + response.status_code in (408, 409, 429) + or response.status_code >= 500 + or (response.status_code not in (400, 401, 403) and response.headers.get("x-should-retry") == "true") + ): + raise _TransientTokenExchangeError(error) from error + raise + + +class SyncX509WorkloadIdentityAuth(_X509WorkloadIdentityAuth): + _http_client: httpx2.Client + + def __init__( + self, *, workload_identity: X509WorkloadIdentity, http_client: httpx2.Client, max_retries: int + ) -> None: + super().__init__(workload_identity=workload_identity, max_retries=max_retries) + self._http_client = http_client + + def send_api_request( + self, + request: httpx2.Request, + *, + expected_origin: httpx2.URL, + expected_authorization: str | None, + stream: bool, + **kwargs: Any, + ) -> httpx2.Response: + with _scoped_sync_client( + self._http_client, expected_origin=expected_origin, expected_authorization=expected_authorization + ) as scoped_client: + kwargs.setdefault("auth", None) + return scoped_client.send(request, stream=stream, **kwargs) + + def get_token_for_request(self, request: httpx2.Request) -> str: + timeout_token = _EXCHANGE_REQUEST_TIMEOUT.set(request.extensions.get("timeout")) + try: + try: + return self.get_token() + except (APIConnectionError, _TransientTokenExchangeError) as error: + token = self._usable_token_after_transient_failure() + if token is None: + if isinstance(error, _TransientTokenExchangeError): + raise error.error from None + raise + return token + finally: + _EXCHANGE_REQUEST_TIMEOUT.reset(timeout_token) + + @override + def _fetch_token_from_exchange(self) -> dict[str, Any]: + for attempt in range(self._max_exchange_retries + 1): + exchange_request = _token_exchange_request(self.workload_identity, http_client=self._http_client) + try: + with _scoped_sync_client( + self._http_client, + expected_origin=httpx2.URL(_X509_TOKEN_EXCHANGE_URL), + token_exchange=True, + ) as scoped_client: + response = scoped_client.send( + exchange_request, + auth=None, + follow_redirects=False, + ) + except _transport_errors() as error: + if attempt >= self._max_exchange_retries: + _raise_transport_error(error, request=exchange_request) + delay = _retry_delay(None, attempt) + else: + delay = _retry_delay(response, attempt) + if attempt >= self._max_exchange_retries or delay is None: + return self._handle_exchange_response(response) + + if delay is not None: + time.sleep(delay) + + raise AssertionError("X.509 token exchange retry loop exhausted unexpectedly") + + +class AsyncX509WorkloadIdentityAuth(_X509WorkloadIdentityAuth): + _http_client: httpx2.AsyncClient + + def __init__( + self, *, workload_identity: X509WorkloadIdentity, http_client: httpx2.AsyncClient, max_retries: int + ) -> None: + super().__init__(workload_identity=workload_identity, max_retries=max_retries) + self._http_client = http_client + self._async_lock = anyio.Lock() + + async def send_api_request( + self, + request: httpx2.Request, + *, + expected_origin: httpx2.URL, + expected_authorization: str | None, + stream: bool, + **kwargs: Any, + ) -> httpx2.Response: + async with _scoped_async_client( + self._http_client, expected_origin=expected_origin, expected_authorization=expected_authorization + ) as scoped_client: + kwargs.setdefault("auth", None) + return await scoped_client.send(request, stream=stream, **kwargs) + + async def get_token_for_request(self, request: httpx2.Request) -> str: + timeout_token = _EXCHANGE_REQUEST_TIMEOUT.set(request.extensions.get("timeout")) + try: + try: + return await self.get_token_async() + except (APIConnectionError, _TransientTokenExchangeError) as error: + token = self._usable_token_after_transient_failure() + if token is None: + if isinstance(error, _TransientTokenExchangeError): + raise error.error from None + raise + return token + finally: + _EXCHANGE_REQUEST_TIMEOUT.reset(timeout_token) + + @override + async def get_token_async(self) -> str: + async with self._async_lock: + with self._lock: + if not self._token_unusable() and not self._needs_refresh(): + return cast(str, self._cached_token) + + try: + token_data = await self._fetch_token_from_exchange_async() + except (APIConnectionError, _TransientTokenExchangeError): + token = self._usable_token_after_transient_failure() + if token is None: + raise + return token + self._store_token(token_data) + with self._lock: + return cast(str, self._cached_token) + + async def _fetch_token_from_exchange_async(self) -> dict[str, Any]: + for attempt in range(self._max_exchange_retries + 1): + exchange_request = _token_exchange_request(self.workload_identity, http_client=self._http_client) + try: + async with _scoped_async_client( + self._http_client, + expected_origin=httpx2.URL(_X509_TOKEN_EXCHANGE_URL), + token_exchange=True, + ) as scoped_client: + response = await scoped_client.send( + exchange_request, + auth=None, + follow_redirects=False, + ) + except _transport_errors() as error: + if attempt >= self._max_exchange_retries: + _raise_transport_error(error, request=exchange_request) + delay = _retry_delay(None, attempt) + else: + delay = _retry_delay(response, attempt) + if attempt >= self._max_exchange_retries or delay is None: + return self._handle_exchange_response(response) + + if delay is not None: + await anyio.sleep(delay) + + raise AssertionError("X.509 token exchange retry loop exhausted unexpectedly") diff --git a/venv/lib/python3.12/site-packages/openai/helpers/__init__.py b/venv/lib/python3.12/site-packages/openai/helpers/__init__.py new file mode 100644 index 0000000..ab3044d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/helpers/__init__.py @@ -0,0 +1,4 @@ +from .microphone import Microphone +from .local_audio_player import LocalAudioPlayer + +__all__ = ["Microphone", "LocalAudioPlayer"] diff --git a/venv/lib/python3.12/site-packages/openai/helpers/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/helpers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..9a76efa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/helpers/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/helpers/__pycache__/local_audio_player.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/helpers/__pycache__/local_audio_player.cpython-312.pyc new file mode 100644 index 0000000..a4e10bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/helpers/__pycache__/local_audio_player.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/helpers/__pycache__/microphone.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/helpers/__pycache__/microphone.cpython-312.pyc new file mode 100644 index 0000000..c2bca8a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/helpers/__pycache__/microphone.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/helpers/local_audio_player.py b/venv/lib/python3.12/site-packages/openai/helpers/local_audio_player.py new file mode 100644 index 0000000..8d8cb3d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/helpers/local_audio_player.py @@ -0,0 +1,165 @@ +# mypy: ignore-errors +from __future__ import annotations + +import queue +import asyncio +from typing import Any, Union, Callable, AsyncGenerator, cast +from typing_extensions import TYPE_CHECKING + +from .. import _legacy_response +from .._extras import numpy as np, sounddevice as sd +from .._response import StreamedBinaryAPIResponse, AsyncStreamedBinaryAPIResponse + +if TYPE_CHECKING: + import numpy.typing as npt + +SAMPLE_RATE = 24000 + + +class LocalAudioPlayer: + def __init__( + self, + should_stop: Union[Callable[[], bool], None] = None, + ): + self.channels = 1 + self.dtype = np.float32 + self.should_stop = should_stop + + async def _tts_response_to_buffer( + self, + response: Union[ + _legacy_response.HttpxBinaryResponseContent, + AsyncStreamedBinaryAPIResponse, + StreamedBinaryAPIResponse, + ], + ) -> npt.NDArray[np.float32]: + chunks: list[bytes] = [] + if isinstance(response, _legacy_response.HttpxBinaryResponseContent) or isinstance( + response, StreamedBinaryAPIResponse + ): + for chunk in response.iter_bytes(chunk_size=1024): + if chunk: + chunks.append(chunk) + else: + async for chunk in response.iter_bytes(chunk_size=1024): + if chunk: + chunks.append(chunk) + + audio_bytes = b"".join(chunks) + audio_np = np.frombuffer(audio_bytes, dtype=np.int16).astype(np.float32) / 32767.0 + audio_np = audio_np.reshape(-1, 1) + return audio_np + + async def play( + self, + input: Union[ + npt.NDArray[np.int16], + npt.NDArray[np.float32], + _legacy_response.HttpxBinaryResponseContent, + AsyncStreamedBinaryAPIResponse, + StreamedBinaryAPIResponse, + ], + ) -> None: + audio_content: npt.NDArray[np.float32] + if isinstance(input, np.ndarray): + if input.dtype == np.int16 and self.dtype == np.float32: + audio_content = (input.astype(np.float32) / 32767.0).reshape(-1, self.channels) + elif input.dtype == np.float32: + audio_content = cast("npt.NDArray[np.float32]", input) + else: + raise ValueError(f"Unsupported dtype: {input.dtype}") + else: + audio_content = await self._tts_response_to_buffer(input) + + loop = asyncio.get_running_loop() + event = asyncio.Event() + idx = 0 + + def callback( + outdata: npt.NDArray[np.float32], + frame_count: int, + _time_info: Any, + _status: Any, + ): + nonlocal idx + + remainder = len(audio_content) - idx + if remainder == 0 or (callable(self.should_stop) and self.should_stop()): + loop.call_soon_threadsafe(event.set) + raise sd.CallbackStop + valid_frames = frame_count if remainder >= frame_count else remainder + outdata[:valid_frames] = audio_content[idx : idx + valid_frames] + outdata[valid_frames:] = 0 + idx += valid_frames + + stream = sd.OutputStream( + samplerate=SAMPLE_RATE, + callback=callback, + dtype=audio_content.dtype, + channels=audio_content.shape[1], + ) + with stream: + await event.wait() + + async def play_stream( + self, + buffer_stream: AsyncGenerator[Union[npt.NDArray[np.float32], npt.NDArray[np.int16], None], None], + ) -> None: + loop = asyncio.get_running_loop() + event = asyncio.Event() + buffer_queue: queue.Queue[Union[npt.NDArray[np.float32], npt.NDArray[np.int16], None]] = queue.Queue(maxsize=50) + + async def buffer_producer(): + async for buffer in buffer_stream: + if buffer is None: + break + await loop.run_in_executor(None, buffer_queue.put, buffer) + await loop.run_in_executor(None, buffer_queue.put, None) # Signal completion + + def callback( + outdata: npt.NDArray[np.float32], + frame_count: int, + _time_info: Any, + _status: Any, + ): + nonlocal current_buffer, buffer_pos + + frames_written = 0 + while frames_written < frame_count: + if current_buffer is None or buffer_pos >= len(current_buffer): + try: + current_buffer = buffer_queue.get(timeout=0.1) + if current_buffer is None: + loop.call_soon_threadsafe(event.set) + raise sd.CallbackStop + buffer_pos = 0 + + if current_buffer.dtype == np.int16 and self.dtype == np.float32: + current_buffer = (current_buffer.astype(np.float32) / 32767.0).reshape(-1, self.channels) + + except queue.Empty: + outdata[frames_written:] = 0 + return + + remaining_frames = len(current_buffer) - buffer_pos + frames_to_write = min(frame_count - frames_written, remaining_frames) + outdata[frames_written : frames_written + frames_to_write] = current_buffer[ + buffer_pos : buffer_pos + frames_to_write + ] + buffer_pos += frames_to_write + frames_written += frames_to_write + + current_buffer = None + buffer_pos = 0 + + producer_task = asyncio.create_task(buffer_producer()) + + with sd.OutputStream( + samplerate=SAMPLE_RATE, + channels=self.channels, + dtype=self.dtype, + callback=callback, + ): + await event.wait() + + await producer_task diff --git a/venv/lib/python3.12/site-packages/openai/helpers/microphone.py b/venv/lib/python3.12/site-packages/openai/helpers/microphone.py new file mode 100644 index 0000000..5a1e8ae --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/helpers/microphone.py @@ -0,0 +1,100 @@ +# mypy: ignore-errors +from __future__ import annotations + +import io +import time +import wave +import asyncio +from typing import Any, Type, Union, Generic, TypeVar, Callable, overload +from typing_extensions import TYPE_CHECKING, Literal + +from .._types import FileTypes, FileContent +from .._extras import numpy as np, sounddevice as sd + +if TYPE_CHECKING: + import numpy.typing as npt + +SAMPLE_RATE = 24000 + +DType = TypeVar("DType", bound=np.generic) + + +class Microphone(Generic[DType]): + def __init__( + self, + channels: int = 1, + dtype: Type[DType] = np.int16, + should_record: Union[Callable[[], bool], None] = None, + timeout: Union[float, None] = None, + ): + self.channels = channels + self.dtype = dtype + self.should_record = should_record + self.buffer_chunks = [] + self.timeout = timeout + self.has_record_function = callable(should_record) + + def _ndarray_to_wav(self, audio_data: npt.NDArray[DType]) -> FileTypes: + buffer: FileContent = io.BytesIO() + with wave.open(buffer, "w") as wav_file: + wav_file.setnchannels(self.channels) + wav_file.setsampwidth(np.dtype(self.dtype).itemsize) + wav_file.setframerate(SAMPLE_RATE) + wav_file.writeframes(audio_data.tobytes()) + buffer.seek(0) + return ("audio.wav", buffer, "audio/wav") + + @overload + async def record(self, return_ndarray: Literal[True]) -> npt.NDArray[DType]: ... + + @overload + async def record(self, return_ndarray: Literal[False]) -> FileTypes: ... + + @overload + async def record(self, return_ndarray: None = ...) -> FileTypes: ... + + async def record(self, return_ndarray: Union[bool, None] = False) -> Union[npt.NDArray[DType], FileTypes]: + loop = asyncio.get_running_loop() + event = asyncio.Event() + self.buffer_chunks: list[npt.NDArray[DType]] = [] + start_time = time.perf_counter() + + def callback( + indata: npt.NDArray[DType], + _frame_count: int, + _time_info: Any, + _status: Any, + ): + execution_time = time.perf_counter() - start_time + reached_recording_timeout = execution_time > self.timeout if self.timeout is not None else False + if reached_recording_timeout: + loop.call_soon_threadsafe(event.set) + raise sd.CallbackStop + + should_be_recording = self.should_record() if callable(self.should_record) else True + if not should_be_recording: + loop.call_soon_threadsafe(event.set) + raise sd.CallbackStop + + self.buffer_chunks.append(indata.copy()) + + stream = sd.InputStream( + callback=callback, + dtype=self.dtype, + samplerate=SAMPLE_RATE, + channels=self.channels, + ) + with stream: + await event.wait() + + # Concatenate all chunks into a single buffer, handle empty case + concatenated_chunks: npt.NDArray[DType] = ( + np.concatenate(self.buffer_chunks, axis=0) + if len(self.buffer_chunks) > 0 + else np.array([], dtype=self.dtype) + ) + + if return_ndarray: + return concatenated_chunks + else: + return self._ndarray_to_wav(concatenated_chunks) diff --git a/venv/lib/python3.12/site-packages/openai/lib/.keep b/venv/lib/python3.12/site-packages/openai/lib/.keep new file mode 100644 index 0000000..a51e020 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/.keep @@ -0,0 +1,2 @@ +This directory can be used to store custom files to expand the SDK. +It is ignored by code generation and its content (other than this keep file) won't be touched. diff --git a/venv/lib/python3.12/site-packages/openai/lib/__init__.py b/venv/lib/python3.12/site-packages/openai/lib/__init__.py new file mode 100644 index 0000000..5c6cb78 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/__init__.py @@ -0,0 +1,2 @@ +from ._tools import pydantic_function_tool as pydantic_function_tool +from ._parsing import ResponseFormatT as ResponseFormatT diff --git a/venv/lib/python3.12/site-packages/openai/lib/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..17e28f5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_azure_websocket.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_azure_websocket.cpython-312.pyc new file mode 100644 index 0000000..d04ad62 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_azure_websocket.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_bedrock_auth.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_bedrock_auth.cpython-312.pyc new file mode 100644 index 0000000..acbe1b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_bedrock_auth.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_files.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_files.cpython-312.pyc new file mode 100644 index 0000000..411da44 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_files.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_old_api.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_old_api.cpython-312.pyc new file mode 100644 index 0000000..8865b3b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_old_api.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_pydantic.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_pydantic.cpython-312.pyc new file mode 100644 index 0000000..8695f32 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_pydantic.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_realtime.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_realtime.cpython-312.pyc new file mode 100644 index 0000000..a3261e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_realtime.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_tools.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_tools.cpython-312.pyc new file mode 100644 index 0000000..d4b16b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_tools.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_vector_stores.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_vector_stores.cpython-312.pyc new file mode 100644 index 0000000..f7958c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_vector_stores.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_webhooks.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_webhooks.cpython-312.pyc new file mode 100644 index 0000000..faec7eb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_webhooks.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_websocket.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_websocket.cpython-312.pyc new file mode 100644 index 0000000..6478713 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/_websocket.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/__pycache__/azure.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/azure.cpython-312.pyc new file mode 100644 index 0000000..8752d38 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/azure.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/__pycache__/bedrock.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/bedrock.cpython-312.pyc new file mode 100644 index 0000000..45bf465 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/__pycache__/bedrock.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/_azure_websocket.py b/venv/lib/python3.12/site-packages/openai/lib/_azure_websocket.py new file mode 100644 index 0000000..fb47d6a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_azure_websocket.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +from ._websocket import _WebSocketConnect + +__all__ = ["_AzureWebSocketConnect"] + + +class _AzureWebSocketConnect(_WebSocketConnect): + """Keep Azure's WebSocket authentication on the original origin.""" + + _redirect_error_message = "Cross-origin Azure WebSocket redirects are not allowed" diff --git a/venv/lib/python3.12/site-packages/openai/lib/_bedrock_auth.py b/venv/lib/python3.12/site-packages/openai/lib/_bedrock_auth.py new file mode 100644 index 0000000..e9076ef --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_bedrock_auth.py @@ -0,0 +1,189 @@ +# pyright: reportMissingTypeStubs=false + +from __future__ import annotations + +import os +import hashlib +from typing import Any, Literal, Mapping, Callable, Protocol, cast +from dataclasses import field, dataclass + +from .._exceptions import OpenAIError + +AwsCredentialsProvider = Callable[[], object] + + +class _BotocoreSession(Protocol): + def get_credentials(self) -> object | None: ... + + +_AUTHORIZATION = "authorization" +_AWS_SIGNING_HEADERS = ( + _AUTHORIZATION, + "x-amz-content-sha256", + "x-amz-date", + "x-amz-security-token", +) + + +def _load_botocore() -> tuple[Any, Any, Any, Any]: + try: + from botocore.auth import SigV4Auth # type: ignore[import-untyped] + from botocore.session import Session # type: ignore[import-untyped] + from botocore.awsrequest import AWSRequest # type: ignore[import-untyped] + from botocore.credentials import Credentials # type: ignore[import-untyped] + except ImportError as exc: + raise OpenAIError( + "Bedrock AWS authentication requires optional AWS dependencies. " + "Install them with `pip install openai[bedrock]` and try again." + ) from exc + + return cast("tuple[Any, Any, Any, Any]", (SigV4Auth, AWSRequest, Credentials, Session)) + + +@dataclass(frozen=True) +class BedrockAwsAuthConfig: + region: str + source: Literal["static", "profile", "provider", "default"] + service: Literal["bedrock-mantle", "bedrock"] = "bedrock-mantle" + region_source: Literal["explicit", "environment", "profile"] = "explicit" + profile: str | None = None + access_key_id: str | None = field(default=None, repr=False) + secret_access_key: str | None = field(default=None, repr=False) + session_token: str | None = field(default=None, repr=False) + credentials_provider: AwsCredentialsProvider | None = field(default=None, repr=False, compare=False) + + +class BedrockAwsAuth: + def __init__(self, config: BedrockAwsAuthConfig, *, session: _BotocoreSession | None = None) -> None: + sigv4_auth_cls, aws_request_cls, credentials_cls, session_cls = _load_botocore() + + if session is None: + try: + session = session_cls(profile=config.profile) + except Exception as exc: + raise OpenAIError( + "Failed to resolve AWS credentials for Bedrock. Verify your AWS profile, environment variables, " + "or runtime identity configuration and try again." + ) from exc + + assert session is not None + self.config = config + self._session = session + self._credentials_provider = config.credentials_provider + self._explicit_credentials = ( + credentials_cls(config.access_key_id, config.secret_access_key, config.session_token) + if config.access_key_id is not None and config.secret_access_key is not None + else None + ) + self._aws_request_cls = aws_request_cls + self._sigv4_auth_cls = sigv4_auth_cls + + @classmethod + def resolve( + cls, + *, + region: str | None, + profile: str | None, + access_key_id: str | None, + secret_access_key: str | None, + session_token: str | None, + credentials_provider: AwsCredentialsProvider | None, + service: Literal["bedrock-mantle", "bedrock"] = "bedrock-mantle", + ) -> BedrockAwsAuth: + _, _, _, session_cls = _load_botocore() + + try: + session = session_cls(profile=profile) + resolved_region, region_source = resolve_aws_region_with_source(region, session=session) + except OpenAIError: + raise + except Exception as exc: + raise OpenAIError( + "Failed to resolve AWS credentials for Bedrock. Verify your AWS profile, environment variables, " + "or runtime identity configuration and try again." + ) from exc + + source: Literal["static", "profile", "provider", "default"] + if access_key_id is not None: + source = "static" + elif profile is not None: + source = "profile" + elif credentials_provider is not None: + source = "provider" + else: + source = "default" + + config = BedrockAwsAuthConfig( + region=resolved_region, + source=source, + service=service, + region_source=region_source, + profile=profile, + access_key_id=access_key_id, + secret_access_key=secret_access_key, + session_token=session_token, + credentials_provider=credentials_provider, + ) + return cls(config, session=session) + + def sign(self, *, method: str, url: str, headers: Mapping[str, str], body: bytes | None) -> dict[str, str]: + try: + credentials = ( + self._credentials_provider() + if self._credentials_provider is not None + else self._explicit_credentials or self._session.get_credentials() + ) + if credentials is None: + raise OpenAIError( + "Could not find credentials for Bedrock. Pass a bearer credential or AWS credentials to " + "`bedrock(...)`, " + "set `AWS_BEARER_TOKEN_BEDROCK`, or configure the default AWS credential chain." + ) + + get_frozen_credentials = getattr(credentials, "get_frozen_credentials", None) + if callable(get_frozen_credentials): + credentials = get_frozen_credentials() + + signed_headers = { + name: value for name, value in headers.items() if name.lower() not in _AWS_SIGNING_HEADERS + } + signed_headers["X-Amz-Content-SHA256"] = hashlib.sha256(body or b"").hexdigest() + aws_request = self._aws_request_cls( + method=method, + url=url, + data=body, + headers=signed_headers, + ) + self._sigv4_auth_cls(credentials, self.config.service, self.config.region).add_auth(aws_request) + except OpenAIError: + raise + except Exception as exc: + raise OpenAIError( + "Failed to resolve AWS credentials for Bedrock. Verify your AWS profile, environment variables, " + "or runtime identity configuration and try again." + ) from exc + + return dict(aws_request.headers.items()) + + +def resolve_aws_region_with_source( + aws_region: str | None, *, session: object | None = None +) -> tuple[str, Literal["explicit", "environment", "profile"]]: + region = aws_region + source: Literal["explicit", "environment", "profile"] = "explicit" + if region is None or not region.strip(): + region = os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION") + source = "environment" + if (region is None or not region.strip()) and session is not None: + get_config_variable = getattr(session, "get_config_variable", None) + if callable(get_config_variable): + region = cast("str | None", get_config_variable("region")) + source = "profile" + + if region is None or not region.strip(): + raise OpenAIError( + "Bedrock requires an AWS region. Pass `region` to `bedrock(...)`, or set `AWS_REGION` or " + "`AWS_DEFAULT_REGION`." + ) + + return region.strip(), source diff --git a/venv/lib/python3.12/site-packages/openai/lib/_files.py b/venv/lib/python3.12/site-packages/openai/lib/_files.py new file mode 100644 index 0000000..ce88c68 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_files.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +import time +from typing import TYPE_CHECKING + +from ..types.file_object import FileObject + +if TYPE_CHECKING: + from ..resources.files import Files, AsyncFiles + + +def wait_for_file_processing( + files: Files, + id: str, + *, + poll_interval: float, + max_wait_seconds: float, +) -> FileObject: + """Poll a file using the caller's resource and sleep hooks.""" + TERMINAL_STATES = {"processed", "error", "deleted"} + + start = time.time() + file = files.retrieve(id) + while file.status not in TERMINAL_STATES: + files._sleep(poll_interval) + + file = files.retrieve(id) + if time.time() - start > max_wait_seconds: + raise RuntimeError( + f"Giving up on waiting for file {id} to finish processing after {max_wait_seconds} seconds." + ) + + return file + + +async def async_wait_for_file_processing( + files: AsyncFiles, + id: str, + *, + poll_interval: float, + max_wait_seconds: float, +) -> FileObject: + """Poll a file using the caller's async resource and sleep hooks.""" + TERMINAL_STATES = {"processed", "error", "deleted"} + + start = time.time() + file = await files.retrieve(id) + while file.status not in TERMINAL_STATES: + await files._sleep(poll_interval) + + file = await files.retrieve(id) + if time.time() - start > max_wait_seconds: + raise RuntimeError( + f"Giving up on waiting for file {id} to finish processing after {max_wait_seconds} seconds." + ) + + return file diff --git a/venv/lib/python3.12/site-packages/openai/lib/_old_api.py b/venv/lib/python3.12/site-packages/openai/lib/_old_api.py new file mode 100644 index 0000000..929c87e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_old_api.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any +from typing_extensions import override + +from .._utils import LazyProxy +from .._exceptions import OpenAIError + +INSTRUCTIONS = """ + +You tried to access openai.{symbol}, but this is no longer supported in openai>=1.0.0 - see the README at https://github.com/openai/openai-python for the API. + +You can run `openai migrate` to automatically upgrade your codebase to use the 1.0.0 interface. + +Alternatively, you can pin your installation to the old version, e.g. `pip install openai==0.28` + +A detailed migration guide is available here: https://github.com/openai/openai-python/discussions/742 +""" + + +class APIRemovedInV1(OpenAIError): + def __init__(self, *, symbol: str) -> None: + super().__init__(INSTRUCTIONS.format(symbol=symbol)) + + +class APIRemovedInV1Proxy(LazyProxy[Any]): + def __init__(self, *, symbol: str) -> None: + super().__init__() + self._symbol = symbol + + @override + def __load__(self) -> Any: + # return the proxy until it is eventually called so that + # we don't break people that are just checking the attributes + # of a module + return self + + def __call__(self, *_args: Any, **_kwargs: Any) -> Any: + raise APIRemovedInV1(symbol=self._symbol) + + +SYMBOLS = [ + "Edit", + "File", + "Audio", + "Image", + "Model", + "Engine", + "Customer", + "FineTune", + "Embedding", + "Completion", + "Deployment", + "Moderation", + "ErrorObject", + "FineTuningJob", + "ChatCompletion", +] + +# we explicitly tell type checkers that nothing is exported +# from this file so that when we re-export the old symbols +# in `openai/__init__.py` they aren't added to the auto-complete +# suggestions given by editors +if TYPE_CHECKING: + __all__: list[str] = [] +else: + __all__ = SYMBOLS + + +__locals = locals() +for symbol in SYMBOLS: + __locals[symbol] = APIRemovedInV1Proxy(symbol=symbol) diff --git a/venv/lib/python3.12/site-packages/openai/lib/_parsing/__init__.py b/venv/lib/python3.12/site-packages/openai/lib/_parsing/__init__.py new file mode 100644 index 0000000..206e91e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_parsing/__init__.py @@ -0,0 +1,12 @@ +from ._completions import ( + ResponseFormatT as ResponseFormatT, + has_parseable_input, + has_parseable_input as has_parseable_input, + maybe_parse_content as maybe_parse_content, + validate_input_tools as validate_input_tools, + parse_chat_completion as parse_chat_completion, + get_input_tool_by_name as get_input_tool_by_name, + materialize_input_tools as materialize_input_tools, + parse_function_tool_arguments as parse_function_tool_arguments, + type_to_response_format_param as type_to_response_format_param, +) diff --git a/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1b45376 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/_audio.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/_audio.cpython-312.pyc new file mode 100644 index 0000000..4a77cd4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/_audio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/_completions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/_completions.cpython-312.pyc new file mode 100644 index 0000000..52d47e2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/_completions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/_embeddings.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/_embeddings.cpython-312.pyc new file mode 100644 index 0000000..b075d55 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/_embeddings.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/_responses.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/_responses.cpython-312.pyc new file mode 100644 index 0000000..f179d25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/_parsing/__pycache__/_responses.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/_parsing/_audio.py b/venv/lib/python3.12/site-packages/openai/lib/_parsing/_audio.py new file mode 100644 index 0000000..cd76e23 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_parsing/_audio.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +import logging +from typing import TYPE_CHECKING +from typing_extensions import assert_never + +from ..._types import Omit +from ...types.audio.translation import Translation +from ...types.audio.transcription import Transcription +from ...types.audio_response_format import AudioResponseFormat +from ...types.audio.translation_verbose import TranslationVerbose +from ...types.audio.transcription_verbose import TranscriptionVerbose +from ...types.audio.transcription_diarized import TranscriptionDiarized + + +def get_transcription_response_format_type( + response_format: AudioResponseFormat | Omit, + *, + log: logging.Logger, +) -> type[Transcription | TranscriptionVerbose | TranscriptionDiarized | str]: + if isinstance(response_format, Omit) or response_format is None: # pyright: ignore[reportUnnecessaryComparison] + return Transcription + + if response_format == "json": + return Transcription + elif response_format == "verbose_json": + return TranscriptionVerbose + elif response_format == "diarized_json": + return TranscriptionDiarized + elif response_format == "srt" or response_format == "text" or response_format == "vtt": + return str + elif TYPE_CHECKING: # type: ignore[unreachable] + assert_never(response_format) + else: + log.warning("Unexpected audio response format: %s", response_format) + return Transcription + + +def get_translation_response_format_type( + response_format: AudioResponseFormat | Omit, + *, + log: logging.Logger, +) -> type[Translation | TranslationVerbose | str]: + if isinstance(response_format, Omit) or response_format is None: # pyright: ignore[reportUnnecessaryComparison] + return Translation + + if response_format == "json": + return Translation + elif response_format == "verbose_json": + return TranslationVerbose + elif response_format == "srt" or response_format == "text" or response_format == "vtt": + return str + elif TYPE_CHECKING and response_format != "diarized_json": # type: ignore[unreachable] + assert_never(response_format) + else: + log.warning("Unexpected audio response format: %s", response_format) + return Translation diff --git a/venv/lib/python3.12/site-packages/openai/lib/_parsing/_completions.py b/venv/lib/python3.12/site-packages/openai/lib/_parsing/_completions.py new file mode 100644 index 0000000..21be8ac --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_parsing/_completions.py @@ -0,0 +1,298 @@ +from __future__ import annotations + +import json +import logging +from typing import TYPE_CHECKING, Any, Iterable, cast +from typing_extensions import TypeVar, TypeGuard, assert_never + +import pydantic + +from .._tools import PydanticFunctionTool +from ..._types import Omit, omit +from ..._utils import is_dict, is_given +from ..._compat import PYDANTIC_V1, model_parse_json +from ..._models import construct_type_unchecked +from .._pydantic import is_basemodel_type, to_strict_json_schema, is_dataclass_like_type +from ...types.chat import ( + ParsedChoice, + ChatCompletion, + ParsedFunction, + ParsedChatCompletion, + ChatCompletionMessage, + ParsedFunctionToolCall, + ParsedChatCompletionMessage, + ChatCompletionToolUnionParam, + ChatCompletionFunctionToolParam, + completion_create_params, +) +from ..._exceptions import LengthFinishReasonError, ContentFilterFinishReasonError +from ...types.shared_params import FunctionDefinition +from ...types.chat.completion_create_params import ResponseFormat as ResponseFormatParam +from ...types.chat.chat_completion_message_function_tool_call import Function + +ResponseFormatT = TypeVar( + "ResponseFormatT", + # if it isn't given then we don't do any parsing + default=None, +) +_default_response_format: None = None + +log: logging.Logger = logging.getLogger("openai.lib.parsing") + + +def is_strict_chat_completion_tool_param( + tool: ChatCompletionToolUnionParam, +) -> TypeGuard[ChatCompletionFunctionToolParam]: + """Check if the given tool is a strict ChatCompletionFunctionToolParam.""" + if not tool["type"] == "function": + return False + if tool["function"].get("strict") is not True: + return False + + return True + + +def select_strict_chat_completion_tools( + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, +) -> Iterable[ChatCompletionFunctionToolParam] | Omit: + """Select only the strict ChatCompletionFunctionToolParams from the given tools.""" + if not is_given(tools): + return omit + + return [t for t in tools if is_strict_chat_completion_tool_param(t)] + + +def materialize_input_tools( + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, +) -> list[ChatCompletionToolUnionParam] | Omit: + if not is_given(tools): + return omit + + return list(tools) + + +def validate_input_tools( + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, +) -> list[ChatCompletionFunctionToolParam] | Omit: + input_tools = materialize_input_tools(tools) + if not is_given(input_tools): + return omit + + for tool in input_tools: + if tool["type"] != "function": + raise ValueError( + f"Currently only `function` tool types support auto-parsing; Received `{tool['type']}`", + ) + + strict = tool["function"].get("strict") + if strict is not True: + raise ValueError( + f"`{tool['function']['name']}` is not strict. Only `strict` function tools can be auto-parsed" + ) + + return cast(list[ChatCompletionFunctionToolParam], input_tools) + + +def parse_chat_completion( + *, + response_format: type[ResponseFormatT] | completion_create_params.ResponseFormat | Omit, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit, + chat_completion: ChatCompletion | ParsedChatCompletion[object], +) -> ParsedChatCompletion[ResponseFormatT]: + if is_given(input_tools): + input_tools = [t for t in input_tools] + else: + input_tools = [] + + choices: list[ParsedChoice[ResponseFormatT]] = [] + for choice in chat_completion.choices: + if choice.finish_reason == "length": + raise LengthFinishReasonError(completion=chat_completion) + + if choice.finish_reason == "content_filter": + raise ContentFilterFinishReasonError(completion=chat_completion) + + message = choice.message + + tool_calls: list[ParsedFunctionToolCall] = [] + if message.tool_calls: + for tool_call in message.tool_calls: + if tool_call.type == "function": + tool_call_dict = tool_call.to_dict() + tool_calls.append( + construct_type_unchecked( + value={ + **tool_call_dict, + "function": { + **cast(Any, tool_call_dict["function"]), + "parsed_arguments": parse_function_tool_arguments( + input_tools=input_tools, function=tool_call.function + ), + }, + }, + type_=ParsedFunctionToolCall, + ) + ) + elif tool_call.type == "custom": + # warn user that custom tool calls are not callable here + log.warning( + "Custom tool calls are not callable. Ignoring tool call: %s - %s", + tool_call.id, + tool_call.custom.name, + stacklevel=2, + ) + elif TYPE_CHECKING: # type: ignore[unreachable] + assert_never(tool_call) + else: + tool_calls.append(tool_call) + + choices.append( + construct_type_unchecked( + type_=ParsedChoice[ResponseFormatT], + value={ + **choice.to_dict(), + "message": { + **message.to_dict(), + "parsed": maybe_parse_content( + response_format=response_format, + message=message, + ), + "tool_calls": tool_calls if tool_calls else None, + }, + }, + ) + ) + + return construct_type_unchecked( + type_=ParsedChatCompletion[ResponseFormatT], + value={ + **chat_completion.to_dict(), + "choices": choices, + }, + ) + + +def get_input_tool_by_name( + *, input_tools: list[ChatCompletionToolUnionParam], name: str +) -> ChatCompletionFunctionToolParam | None: + return next((t for t in input_tools if t["type"] == "function" and t.get("function", {}).get("name") == name), None) + + +def parse_function_tool_arguments( + *, input_tools: list[ChatCompletionToolUnionParam], function: Function | ParsedFunction +) -> object | None: + input_tool = get_input_tool_by_name(input_tools=input_tools, name=function.name) + if not input_tool: + return None + + input_fn = cast(object, input_tool.get("function")) + if isinstance(input_fn, PydanticFunctionTool): + return model_parse_json(input_fn.model, function.arguments) + + input_fn = cast(FunctionDefinition, input_fn) + + if not input_fn.get("strict"): + return None + + return json.loads(function.arguments) # type: ignore[no-any-return] + + +def maybe_parse_content( + *, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, + message: ChatCompletionMessage | ParsedChatCompletionMessage[object], +) -> ResponseFormatT | None: + if has_rich_response_format(response_format) and message.content and not message.refusal: + return _parse_content(response_format, message.content) + + return None + + +def has_parseable_input( + *, + response_format: type | ResponseFormatParam | Omit, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, +) -> bool: + if has_rich_response_format(response_format): + return True + + for input_tool in input_tools or []: + if is_parseable_tool(input_tool): + return True + + return False + + +def has_rich_response_format( + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, +) -> TypeGuard[type[ResponseFormatT]]: + if not is_given(response_format): + return False + + if is_response_format_param(response_format): + return False + + return True + + +def is_response_format_param(response_format: object) -> TypeGuard[ResponseFormatParam]: + return is_dict(response_format) + + +def is_parseable_tool(input_tool: ChatCompletionToolUnionParam) -> bool: + if input_tool["type"] != "function": + return False + + input_fn = cast(object, input_tool.get("function")) + if isinstance(input_fn, PydanticFunctionTool): + return True + + return cast(FunctionDefinition, input_fn).get("strict") or False + + +def _parse_content(response_format: type[ResponseFormatT], content: str) -> ResponseFormatT: + if is_basemodel_type(response_format): + return cast(ResponseFormatT, model_parse_json(response_format, content)) + + if is_dataclass_like_type(response_format): + if PYDANTIC_V1: + raise TypeError(f"Non BaseModel types are only supported with Pydantic v2 - {response_format}") + + return pydantic.TypeAdapter(response_format).validate_json(content) + + raise TypeError(f"Unable to automatically parse response format type {response_format}") + + +def type_to_response_format_param( + response_format: type | completion_create_params.ResponseFormat | Omit, +) -> ResponseFormatParam | Omit: + if not is_given(response_format): + return omit + + if is_response_format_param(response_format): + return response_format + + # type checkers don't narrow the negation of a `TypeGuard` as it isn't + # a safe default behaviour but we know that at this point the `response_format` + # can only be a `type` + response_format = cast(type, response_format) + + json_schema_type: type[pydantic.BaseModel] | pydantic.TypeAdapter[Any] | None = None + + if is_basemodel_type(response_format): + name = response_format.__name__ + json_schema_type = response_format + elif is_dataclass_like_type(response_format): + name = response_format.__name__ + json_schema_type = pydantic.TypeAdapter(response_format) + else: + raise TypeError(f"Unsupported response_format type - {response_format}") + + return { + "type": "json_schema", + "json_schema": { + "schema": to_strict_json_schema(json_schema_type), + "name": name, + "strict": True, + }, + } diff --git a/venv/lib/python3.12/site-packages/openai/lib/_parsing/_embeddings.py b/venv/lib/python3.12/site-packages/openai/lib/_parsing/_embeddings.py new file mode 100644 index 0000000..5b03068 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_parsing/_embeddings.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +import array +import base64 +from typing import cast + +from ..._types import Omit, NotGiven +from ..._utils import is_given +from ..._extras import numpy as np, has_numpy +from ...types.create_embedding_response import CreateEmbeddingResponse + + +def parse_embedding_response( + obj: CreateEmbeddingResponse, *, encoding_format: str | Omit | NotGiven +) -> CreateEmbeddingResponse: + if is_given(encoding_format): + # don't modify the response object if a user explicitly asked for a format + return obj + + if not obj.data: + raise ValueError("No embedding data received") + + if not any(isinstance(embedding.embedding, str) for embedding in obj.data): + return obj + + use_numpy = has_numpy() + for embedding in obj.data: + data = cast(object, embedding.embedding) + if not isinstance(data, str): + continue + if not use_numpy: + # use array for base64 optimisation + embedding.embedding = array.array("f", base64.b64decode(data)).tolist() + else: + embedding.embedding = np.frombuffer( # type: ignore[no-untyped-call] + base64.b64decode(data), dtype="float32" + ).tolist() + + return obj diff --git a/venv/lib/python3.12/site-packages/openai/lib/_parsing/_responses.py b/venv/lib/python3.12/site-packages/openai/lib/_parsing/_responses.py new file mode 100644 index 0000000..fdf5132 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_parsing/_responses.py @@ -0,0 +1,198 @@ +from __future__ import annotations + +import json +from typing import TYPE_CHECKING, List, Iterable, cast +from typing_extensions import TypeVar, assert_never + +import pydantic + +from .._tools import ResponsesPydanticFunctionTool +from ..._types import Omit +from ..._utils import is_given +from ..._compat import PYDANTIC_V1, model_parse_json +from ..._models import construct_type_unchecked +from .._pydantic import is_basemodel_type, is_dataclass_like_type +from ._completions import type_to_response_format_param +from ...types.responses import ( + Response, + ToolParam, + ParsedContent, + ParsedResponse, + FunctionToolParam, + ParsedResponseOutputItem, + ParsedResponseOutputText, + ResponseFunctionToolCall, + ParsedResponseOutputMessage, + ResponseFormatTextConfigParam, + ParsedResponseFunctionToolCall, +) +from ...types.chat.completion_create_params import ResponseFormat + +TextFormatT = TypeVar( + "TextFormatT", + # if it isn't given then we don't do any parsing + default=None, +) + + +def type_to_text_format_param(type_: type) -> ResponseFormatTextConfigParam: + response_format_dict = type_to_response_format_param(type_) + assert is_given(response_format_dict) + response_format_dict = cast(ResponseFormat, response_format_dict) # pyright: ignore[reportUnnecessaryCast] + assert response_format_dict["type"] == "json_schema" + assert "schema" in response_format_dict["json_schema"] + + return { + "type": "json_schema", + "strict": True, + "name": response_format_dict["json_schema"]["name"], + "schema": response_format_dict["json_schema"]["schema"], + } + + +def parse_response( + *, + text_format: type[TextFormatT] | Omit, + input_tools: Iterable[ToolParam] | Omit | None, + response: Response | ParsedResponse[object], +) -> ParsedResponse[TextFormatT]: + # Keep generic annotations in quoted casts: runtime specialization can create + # new model classes in each async context and retain them in the type adapter cache. + output_list: List[ParsedResponseOutputItem[TextFormatT]] = [] + + for output in response.output or []: + if output.type == "message": + content_list: List[ParsedContent[TextFormatT]] = [] + for item in output.content or []: + if item.type != "output_text": + content_list.append(item) + continue + + content_list.append( + construct_type_unchecked( + type_=cast("type[ParsedResponseOutputText[TextFormatT]]", ParsedResponseOutputText), + value={ + **item.to_dict(), + "parsed": parse_text(item.text, text_format=text_format, phase=output.phase), + }, + ) + ) + + output_list.append( + construct_type_unchecked( + type_=cast("type[ParsedResponseOutputMessage[TextFormatT]]", ParsedResponseOutputMessage), + value={ + **output.to_dict(), + "content": content_list, + }, + ) + ) + elif output.type == "function_call": + output_list.append( + construct_type_unchecked( + type_=ParsedResponseFunctionToolCall, + value={ + **output.to_dict(), + "parsed_arguments": parse_function_tool_arguments( + input_tools=input_tools, function_call=output + ), + }, + ) + ) + elif ( + output.type == "computer_call" + or output.type == "file_search_call" + or output.type == "web_search_call" + or output.type == "tool_search_call" + or output.type == "tool_search_output" + or output.type == "additional_tools" + or output.type == "reasoning" + or output.type == "program" + or output.type == "program_output" + or output.type == "compaction" + or output.type == "mcp_call" + or output.type == "mcp_approval_request" + or output.type == "mcp_approval_response" + or output.type == "image_generation_call" + or output.type == "code_interpreter_call" + or output.type == "local_shell_call" + or output.type == "local_shell_call_output" + or output.type == "shell_call" + or output.type == "shell_call_output" + or output.type == "apply_patch_call" + or output.type == "apply_patch_call_output" + or output.type == "mcp_list_tools" + or output.type == "exec" + or output.type == "custom_tool_call" + or output.type == "function_call_output" + or output.type == "computer_call_output" + or output.type == "custom_tool_call_output" + ): + output_list.append(output) + elif TYPE_CHECKING: # type: ignore + assert_never(output) + else: + output_list.append(output) + + return construct_type_unchecked( + type_=cast("type[ParsedResponse[TextFormatT]]", ParsedResponse), + value={ + **response.to_dict(), + "output": output_list, + }, + ) + + +def parse_text(text: str, text_format: type[TextFormatT] | Omit, *, phase: str | None) -> TextFormatT | None: + """Parse final-answer text, retaining legacy behavior for messages without a phase. + + Explicit phases other than `final_answer` are not structured results. Leave + their text unparsed, including phases introduced by future API versions. + Both completed responses and streamed text-done events use this rule. + """ + if phase is not None and phase != "final_answer": + return None + + if not is_given(text_format): + return None + + if is_basemodel_type(text_format): + return cast(TextFormatT, model_parse_json(text_format, text)) + + if is_dataclass_like_type(text_format): + if PYDANTIC_V1: + raise TypeError(f"Non BaseModel types are only supported with Pydantic v2 - {text_format}") + + return pydantic.TypeAdapter(text_format).validate_json(text) + + raise TypeError(f"Unable to automatically parse response format type {text_format}") + + +def get_input_tool_by_name(*, input_tools: Iterable[ToolParam], name: str) -> FunctionToolParam | None: + for tool in input_tools: + if tool["type"] == "function" and tool.get("name") == name: + return tool + + return None + + +def parse_function_tool_arguments( + *, + input_tools: Iterable[ToolParam] | Omit | None, + function_call: ParsedResponseFunctionToolCall | ResponseFunctionToolCall, +) -> object: + if input_tools is None or not is_given(input_tools): + return None + + input_tool = get_input_tool_by_name(input_tools=input_tools, name=function_call.name) + if not input_tool: + return None + + tool = cast(object, input_tool) + if isinstance(tool, ResponsesPydanticFunctionTool): + return model_parse_json(tool.model, function_call.arguments) + + if not input_tool.get("strict"): + return None + + return json.loads(function_call.arguments) diff --git a/venv/lib/python3.12/site-packages/openai/lib/_pydantic.py b/venv/lib/python3.12/site-packages/openai/lib/_pydantic.py new file mode 100644 index 0000000..3cfe224 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_pydantic.py @@ -0,0 +1,155 @@ +from __future__ import annotations + +import inspect +from typing import Any, TypeVar +from typing_extensions import TypeGuard + +import pydantic + +from .._types import NOT_GIVEN +from .._utils import is_dict as _is_dict, is_list +from .._compat import PYDANTIC_V1, model_json_schema + +_T = TypeVar("_T") + + +def to_strict_json_schema(model: type[pydantic.BaseModel] | pydantic.TypeAdapter[Any]) -> dict[str, Any]: + if inspect.isclass(model) and is_basemodel_type(model): + schema = model_json_schema(model) + elif (not PYDANTIC_V1) and isinstance(model, pydantic.TypeAdapter): + schema = model.json_schema() + else: + raise TypeError(f"Non BaseModel types are only supported with Pydantic v2 - {model}") + + return _ensure_strict_json_schema(schema, path=(), root=schema) + + +def _ensure_strict_json_schema( + json_schema: object, + *, + path: tuple[str, ...], + root: dict[str, object], +) -> dict[str, Any]: + """Mutates the given JSON schema to ensure it conforms to the `strict` standard + that the API expects. + """ + if not is_dict(json_schema): + raise TypeError(f"Expected {json_schema} to be a dictionary; path={path}") + + defs = json_schema.get("$defs") + if is_dict(defs): + for def_name, def_schema in defs.items(): + _ensure_strict_json_schema(def_schema, path=(*path, "$defs", def_name), root=root) + + definitions = json_schema.get("definitions") + if is_dict(definitions): + for definition_name, definition_schema in definitions.items(): + _ensure_strict_json_schema(definition_schema, path=(*path, "definitions", definition_name), root=root) + + typ = json_schema.get("type") + if typ == "object" and "additionalProperties" not in json_schema: + json_schema["additionalProperties"] = False + + # object types + # { 'type': 'object', 'properties': { 'a': {...} } } + properties = json_schema.get("properties") + if is_dict(properties): + json_schema["required"] = [prop for prop in properties.keys()] + json_schema["properties"] = { + key: _ensure_strict_json_schema(prop_schema, path=(*path, "properties", key), root=root) + for key, prop_schema in properties.items() + } + + # arrays + # { 'type': 'array', 'items': {...} } + items = json_schema.get("items") + if is_dict(items): + json_schema["items"] = _ensure_strict_json_schema(items, path=(*path, "items"), root=root) + + # unions + any_of = json_schema.get("anyOf") + if is_list(any_of): + json_schema["anyOf"] = [ + _ensure_strict_json_schema(variant, path=(*path, "anyOf", str(i)), root=root) + for i, variant in enumerate(any_of) + ] + + # intersections + all_of = json_schema.get("allOf") + if is_list(all_of): + if len(all_of) == 1: + json_schema.update(_ensure_strict_json_schema(all_of[0], path=(*path, "allOf", "0"), root=root)) + json_schema.pop("allOf") + else: + json_schema["allOf"] = [ + _ensure_strict_json_schema(entry, path=(*path, "allOf", str(i)), root=root) + for i, entry in enumerate(all_of) + ] + + # strip `None` defaults as there's no meaningful distinction here + # the schema will still be `nullable` and the model will default + # to using `None` anyway + if json_schema.get("default", NOT_GIVEN) is None: + json_schema.pop("default") + + # we can't use `$ref`s if there are also other properties defined, e.g. + # `{"$ref": "...", "description": "my description"}` + # + # so we unravel the ref + # `{"type": "string", "description": "my description"}` + ref = json_schema.get("$ref") + if ref and has_more_than_n_keys(json_schema, 1): + assert isinstance(ref, str), f"Received non-string $ref - {ref}" + + resolved = resolve_ref(root=root, ref=ref) + if not is_dict(resolved): + raise ValueError(f"Expected `$ref: {ref}` to resolved to a dictionary but got {resolved}") + + # properties from the json schema take priority over the ones on the `$ref` + json_schema.update({**resolved, **json_schema}) + json_schema.pop("$ref") + # Since the schema expanded from `$ref` might not have `additionalProperties: false` applied, + # we call `_ensure_strict_json_schema` again to fix the inlined schema and ensure it's valid. + return _ensure_strict_json_schema(json_schema, path=path, root=root) + + return json_schema + + +def resolve_ref(*, root: dict[str, object], ref: str) -> object: + if not ref.startswith("#/"): + raise ValueError(f"Unexpected $ref format {ref!r}; Does not start with #/") + + path = ref[2:].split("/") + resolved = root + for key in path: + value = resolved[key] + assert is_dict(value), f"encountered non-dictionary entry while resolving {ref} - {resolved}" + resolved = value + + return resolved + + +def is_basemodel_type(typ: type) -> TypeGuard[type[pydantic.BaseModel]]: + if not inspect.isclass(typ): + return False + return issubclass(typ, pydantic.BaseModel) + + +def is_dataclass_like_type(typ: type) -> bool: + """Returns True if the given type likely used `@pydantic.dataclass`""" + return hasattr(typ, "__pydantic_config__") + + +def is_dict(obj: object) -> TypeGuard[dict[str, object]]: + # just pretend that we know there are only `str` keys + # as that check is not worth the performance cost + return _is_dict(obj) + + +def has_more_than_n_keys(obj: dict[str, object], n: int) -> bool: + i = 0 + for _ in obj.keys(): + i += 1 + if i > n: + return True + return False diff --git a/venv/lib/python3.12/site-packages/openai/lib/_realtime.py b/venv/lib/python3.12/site-packages/openai/lib/_realtime.py new file mode 100644 index 0000000..d106b98 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_realtime.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +# Compatibility aliases for the former handwritten call-creation implementation. +# Multipart and raw SDP serialization now come from the generated resources. +from openai.resources.realtime.calls import Calls as _Calls, AsyncCalls as _AsyncCalls + +__all__ = ["_Calls", "_AsyncCalls"] diff --git a/venv/lib/python3.12/site-packages/openai/lib/_tools.py b/venv/lib/python3.12/site-packages/openai/lib/_tools.py new file mode 100644 index 0000000..4070ad6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_tools.py @@ -0,0 +1,66 @@ +from __future__ import annotations + +from typing import Any, Dict, cast + +import pydantic + +from ._pydantic import to_strict_json_schema +from ..types.chat import ChatCompletionFunctionToolParam +from ..types.shared_params import FunctionDefinition +from ..types.responses.function_tool_param import FunctionToolParam as ResponsesFunctionToolParam + + +class PydanticFunctionTool(Dict[str, Any]): + """Dictionary wrapper so we can pass the given base model + throughout the entire request stack without having to special + case it. + """ + + model: type[pydantic.BaseModel] + + def __init__(self, defn: FunctionDefinition, model: type[pydantic.BaseModel]) -> None: + super().__init__(defn) + self.model = model + + def cast(self) -> FunctionDefinition: + return cast(FunctionDefinition, self) + + +class ResponsesPydanticFunctionTool(Dict[str, Any]): + model: type[pydantic.BaseModel] + + def __init__(self, tool: ResponsesFunctionToolParam, model: type[pydantic.BaseModel]) -> None: + super().__init__(tool) + self.model = model + + def cast(self) -> ResponsesFunctionToolParam: + return cast(ResponsesFunctionToolParam, self) + + +def pydantic_function_tool( + model: type[pydantic.BaseModel], + *, + name: str | None = None, # inferred from class name by default + description: str | None = None, # inferred from class docstring by default +) -> ChatCompletionFunctionToolParam: + if description is None: + # note: we intentionally don't use `.getdoc()` to avoid + # including pydantic's docstrings + description = model.__doc__ + + function = PydanticFunctionTool( + { + "name": name or model.__name__, + "strict": True, + "parameters": to_strict_json_schema(model), + }, + model, + ).cast() + + if description is not None: + function["description"] = description + + return { + "type": "function", + "function": function, + } diff --git a/venv/lib/python3.12/site-packages/openai/lib/_vector_stores.py b/venv/lib/python3.12/site-packages/openai/lib/_vector_stores.py new file mode 100644 index 0000000..1451219 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_vector_stores.py @@ -0,0 +1,181 @@ +from __future__ import annotations + +import math +import time +from typing import TYPE_CHECKING, Mapping +from typing_extensions import assert_never + +from .._types import Omit, omit +from .._utils import is_given +from ..types.vector_stores.vector_store_file import VectorStoreFile +from ..types.vector_stores.vector_store_file_batch import VectorStoreFileBatch + +if TYPE_CHECKING: + from ..resources.vector_stores.files import Files, AsyncFiles + from ..resources.vector_stores.file_batches import FileBatches, AsyncFileBatches + + +def _get_poll_interval_ms(headers: Mapping[str, str]) -> int: + """Use the server hint or the existing one-second default.""" + from_header = headers.get("openai-poll-after-ms") + if from_header is not None: + return int(from_header) + return 1000 + + +def validate_max_wait_seconds(max_wait_seconds: float | Omit) -> None: + if is_given(max_wait_seconds) and (not math.isfinite(max_wait_seconds) or max_wait_seconds < 0): + raise ValueError("Expected a finite, non-negative value for `max_wait_seconds`") + + +def _remaining_poll_time(deadline: float, file_id: str) -> float: + remaining = deadline - time.monotonic() + if remaining <= 0: + raise TimeoutError(f"Timed out waiting for vector store file {file_id!r} to finish processing") + return remaining + + +def poll_vector_store_file( + resource: Files, + file_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | Omit, + max_wait_seconds: float | Omit = omit, +) -> VectorStoreFile: + """Poll a vector-store file using the caller's resource hooks.""" + validate_max_wait_seconds(max_wait_seconds) + deadline = time.monotonic() + max_wait_seconds if is_given(max_wait_seconds) else None + headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"} + if is_given(poll_interval_ms): + headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + while True: + if deadline is not None: + _remaining_poll_time(deadline, file_id) + + response = resource.with_raw_response.retrieve( + file_id, + vector_store_id=vector_store_id, + extra_headers=headers, + ) + + file = response.parse() + if file.status == "in_progress": + if not is_given(poll_interval_ms): + poll_interval_ms = _get_poll_interval_ms(response.headers) + + sleep_seconds = poll_interval_ms / 1000 + if deadline is not None: + sleep_seconds = min(sleep_seconds, _remaining_poll_time(deadline, file_id)) + resource._sleep(sleep_seconds) + elif file.status == "cancelled" or file.status == "completed" or file.status == "failed": + return file + else: + if TYPE_CHECKING: # type: ignore[unreachable] + assert_never(file.status) + else: + return file + + +async def async_poll_vector_store_file( + resource: AsyncFiles, + file_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | Omit, + max_wait_seconds: float | Omit = omit, +) -> VectorStoreFile: + """Poll a vector-store file using the caller's async resource hooks.""" + validate_max_wait_seconds(max_wait_seconds) + deadline = time.monotonic() + max_wait_seconds if is_given(max_wait_seconds) else None + headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"} + if is_given(poll_interval_ms): + headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + while True: + if deadline is not None: + _remaining_poll_time(deadline, file_id) + + response = await resource.with_raw_response.retrieve( + file_id, + vector_store_id=vector_store_id, + extra_headers=headers, + ) + + file = response.parse() + if file.status == "in_progress": + if not is_given(poll_interval_ms): + poll_interval_ms = _get_poll_interval_ms(response.headers) + + sleep_seconds = poll_interval_ms / 1000 + if deadline is not None: + sleep_seconds = min(sleep_seconds, _remaining_poll_time(deadline, file_id)) + await resource._sleep(sleep_seconds) + elif file.status == "cancelled" or file.status == "completed" or file.status == "failed": + return file + else: + if TYPE_CHECKING: # type: ignore[unreachable] + assert_never(file.status) + else: + return file + + +def poll_vector_store_file_batch( + resource: FileBatches, + batch_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | Omit, +) -> VectorStoreFileBatch: + """Poll a vector-store batch using the caller's resource hooks.""" + headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"} + if is_given(poll_interval_ms): + headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + while True: + response = resource.with_raw_response.retrieve( + batch_id, + vector_store_id=vector_store_id, + extra_headers=headers, + ) + + batch = response.parse() + if batch.file_counts.in_progress > 0: + if not is_given(poll_interval_ms): + poll_interval_ms = _get_poll_interval_ms(response.headers) + + resource._sleep(poll_interval_ms / 1000) + continue + + return batch + + +async def async_poll_vector_store_file_batch( + resource: AsyncFileBatches, + batch_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | Omit, +) -> VectorStoreFileBatch: + """Poll a vector-store batch using the caller's async resource hooks.""" + headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"} + if is_given(poll_interval_ms): + headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + while True: + response = await resource.with_raw_response.retrieve( + batch_id, + vector_store_id=vector_store_id, + extra_headers=headers, + ) + + batch = response.parse() + if batch.file_counts.in_progress > 0: + if not is_given(poll_interval_ms): + poll_interval_ms = _get_poll_interval_ms(response.headers) + + await resource._sleep(poll_interval_ms / 1000) + continue + + return batch diff --git a/venv/lib/python3.12/site-packages/openai/lib/_webhooks.py b/venv/lib/python3.12/site-packages/openai/lib/_webhooks.py new file mode 100644 index 0000000..a5cb547 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_webhooks.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +import hmac +import time +import base64 +import hashlib + +from .._types import HeadersLike +from .._utils import get_required_header +from .._exceptions import InvalidWebhookSignatureError + + +def webhook_signature_matches( + payload: str | bytes, + headers: HeadersLike, + *, + secret: str, + tolerance: int, +) -> bool: + """Validate the replay window and compare the supplied signatures.""" + signature_header = get_required_header(headers, "webhook-signature") + timestamp = get_required_header(headers, "webhook-timestamp") + webhook_id = get_required_header(headers, "webhook-id") + + # Validate timestamp to prevent replay attacks + try: + timestamp_seconds = int(timestamp) + except ValueError: + raise InvalidWebhookSignatureError("Invalid webhook timestamp format") from None + + now = int(time.time()) + + if now - timestamp_seconds > tolerance: + raise InvalidWebhookSignatureError("Webhook timestamp is too old") from None + + if timestamp_seconds > now + tolerance: + raise InvalidWebhookSignatureError("Webhook timestamp is too new") from None + + # Extract signatures from v1, format + # The signature header can have multiple values, separated by spaces. + # Each value is in the format v1,. We should accept if any match. + signatures: list[str] = [] + for part in signature_header.split(): + if part.startswith("v1,"): + signatures.append(part[3:]) + else: + signatures.append(part) + + # Decode the secret if it starts with whsec_ + if secret.startswith("whsec_"): + decoded_secret = base64.b64decode(secret[6:]) + else: + decoded_secret = secret.encode() + + body = payload.decode("utf-8") if isinstance(payload, bytes) else payload + + # Prepare the signed payload (OpenAI uses webhookId.timestamp.payload format) + signed_payload = f"{webhook_id}.{timestamp}.{body}" + expected_signature = base64.b64encode( + hmac.new(decoded_secret, signed_payload.encode(), hashlib.sha256).digest() + ).decode() + + # Accept if any signature matches + return any(hmac.compare_digest(expected_signature, sig) for sig in signatures) diff --git a/venv/lib/python3.12/site-packages/openai/lib/_websocket.py b/venv/lib/python3.12/site-packages/openai/lib/_websocket.py new file mode 100644 index 0000000..42231d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/_websocket.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +from typing_extensions import override + +from websockets.uri import parse_uri +from websockets.version import version as websockets_version +from websockets.exceptions import SecurityError +from websockets.sync.client import ClientConnection as SyncWebSocketConnection +from websockets.asyncio.client import connect + +__all__ = ["_WebSocketConnect", "_recv_bytes"] + + +def _recv_bytes(connection: SyncWebSocketConnection) -> bytes: + if not websockets_version.startswith("13."): + return connection.recv(decode=False) + # websockets 13 decodes text frames unconditionally in its sync API. + # Re-encoding preserves their wire bytes; binary messages are already bytes. + message = connection.recv() + return message.encode("utf-8") if isinstance(message, str) else message + + +class _WebSocketConnect(connect): + """Keep WebSocket authentication on the original origin.""" + + _redirect_error_message = "Cross-origin WebSocket redirects are not allowed" + + @override + def process_redirect(self, exc: Exception) -> Exception | str: + uri_or_exc = super().process_redirect(exc) + if isinstance(uri_or_exc, str): + current = parse_uri(self.uri) + target = parse_uri(uri_or_exc) + if (current.secure, current.host, current.port) != (target.secure, target.host, target.port): + return SecurityError(self._redirect_error_message) + return uri_or_exc diff --git a/venv/lib/python3.12/site-packages/openai/lib/azure.py b/venv/lib/python3.12/site-packages/openai/lib/azure.py new file mode 100644 index 0000000..d5f9a01 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/azure.py @@ -0,0 +1,893 @@ +from __future__ import annotations + +import os +import inspect +from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, cast, overload +from typing_extensions import Self, override + +import httpx2 + +from ..auth import WorkloadIdentity, X509WorkloadIdentity +from .._types import NOT_GIVEN, Omit, Query, Headers, Timeout, NotGiven +from .._utils import is_given, is_mapping, path_template +from .._client import OpenAI, AsyncOpenAI +from .._compat import model_copy +from .._httpx2 import normalize_httpx_url +from .._models import SecurityOptions, FinalRequestOptions +from .._provider import _Provider +from .._streaming import Stream, AsyncStream +from ..auth._x509 import is_x509_workload_identity +from .._exceptions import OpenAIError +from .._base_client import DEFAULT_MAX_RETRIES, BaseClient +from .._data_residency import DataResidency + +_deployments_endpoints = set( + [ + "/completions", + "/chat/completions", + "/embeddings", + "/audio/transcriptions", + "/audio/translations", + "/audio/speech", + "/images/generations", + "/images/edits", + ] +) + + +AzureADTokenProvider = Callable[[], str] +AsyncAzureADTokenProvider = Callable[[], "str | Awaitable[str]"] +_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx2.Client, httpx2.AsyncClient]) +_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) + + +# we need to use a sentinel API key value for Azure AD +# as we don't want to make the `api_key` in the main client Optional +# and Azure AD tokens may be retrieved on a per-request basis +API_KEY_SENTINEL = "".join(["<", "missing API key", ">"]) + + +def _has_header(headers: Headers, header: str) -> bool: + header = header.lower() + return any(key.lower() == header for key in headers) + + +def _has_auth_header(headers: Headers) -> bool: + return _has_header(headers, "Authorization") or _has_header(headers, "api-key") + + +_AZURE_AUTH_ORIGIN = "openai.azure_auth_origin" + + +def _origin(url: httpx2.URL) -> tuple[str, str, int | None]: + port = url.port + if port is None: + port = {"http": 80, "https": 443}.get(url.scheme) + return url.scheme, url.host, port + + +def _strip_azure_api_key_on_redirect(request: httpx2.Request) -> None: + origin = request.extensions.get(_AZURE_AUTH_ORIGIN) + if origin is not None and origin != _origin(request.url): + request.headers.pop("api-key", None) + + +async def _async_strip_azure_api_key_on_redirect(request: httpx2.Request) -> None: + _strip_azure_api_key_on_redirect(request) + + +class MutuallyExclusiveAuthError(OpenAIError): + def __init__(self) -> None: + super().__init__( + "The `api_key`, `azure_ad_token` and `azure_ad_token_provider` arguments are mutually exclusive; Only one can be passed at a time" + ) + + +_AzureApiKeyT = TypeVar("_AzureApiKeyT", bound=Union[str, Callable[..., Any]]) +_AzureTokenProviderT = TypeVar("_AzureTokenProviderT", bound=Callable[..., Any]) + + +def _resolve_azure_auth( + api_key: _AzureApiKeyT | None, + azure_ad_token: str | None, + azure_ad_token_provider: _AzureTokenProviderT | None, +) -> tuple[_AzureApiKeyT | str | None, str | None, _AzureTokenProviderT | None]: + # The sentinel is internal state carried by copies, not a second credential. + if ( + sum( + ( + api_key is not None and api_key != API_KEY_SENTINEL, + azure_ad_token is not None, + azure_ad_token_provider is not None, + ) + ) + > 1 + ): + raise MutuallyExclusiveAuthError() + + # Explicit credentials select the principal. Only an entirely unspecified + # configuration may consult the environment; retain AD-token precedence. + resolved_api_key: _AzureApiKeyT | str | None = api_key + if api_key is None and azure_ad_token is None and azure_ad_token_provider is None: + azure_ad_token = os.environ.get("AZURE_OPENAI_AD_TOKEN") + if azure_ad_token is None: + resolved_api_key = os.environ.get("AZURE_OPENAI_API_KEY") + + return resolved_api_key, azure_ad_token, azure_ad_token_provider + + +def _copy_azure_auth( + api_key: _AzureApiKeyT | None, + azure_ad_token: str | None, + azure_ad_token_provider: _AzureTokenProviderT | None, + *, + current_api_key: _AzureApiKeyT | str, + current_token: str | None, + current_provider: _AzureTokenProviderT | None, +) -> tuple[_AzureApiKeyT | str, str | None, _AzureTokenProviderT | None]: + if api_key is None and azure_ad_token is None and azure_ad_token_provider is None: + return current_api_key or API_KEY_SENTINEL, current_token, current_provider + + # Prevent OpenAI.copy() from inheriting the old API key when switching to AD. + key, token, provider = _resolve_azure_auth(api_key, azure_ad_token, azure_ad_token_provider) + return key or API_KEY_SENTINEL, token, provider + + +class BaseAzureClient(BaseClient[_HttpxClientT, _DefaultStreamT]): + _azure_endpoint: httpx2.URL | None + _azure_deployment: str | None + + @override + def _build_request( + self, + options: FinalRequestOptions, + *, + retries_taken: int = 0, + ) -> httpx2.Request: + if options.url in _deployments_endpoints and is_mapping(options.json_data): + model = options.json_data.get("model") + if model is not None and "/deployments" not in str(self.base_url.path): + options.url = path_template("/deployments/{model}", model=model) + options.url + + request = super()._build_request(options, retries_taken=retries_taken) + # HTTPX preserves request extensions through redirects. Scope the hook + # to this Azure request, including when its HTTP client is shared. + request.extensions[_AZURE_AUTH_ORIGIN] = _origin(request.url) + return request + + @override + def _prepare_url(self, url: str) -> httpx2.URL: + """Adjust the URL if the client was configured with an Azure endpoint + deployment + and the API feature being called is **not** a deployments-based endpoint + (i.e. requires /deployments/deployment-name in the URL path). + """ + if self._azure_deployment and self._azure_endpoint and url not in _deployments_endpoints: + merge_url = httpx2.URL(url) + if merge_url.is_relative_url: + merge_raw_path = ( + self._azure_endpoint.raw_path.rstrip(b"/") + b"/openai/" + merge_url.raw_path.lstrip(b"/") + ) + return self._azure_endpoint.copy_with(raw_path=merge_raw_path) + + return merge_url + + return super()._prepare_url(url) + + +class AzureOpenAI(BaseAzureClient[httpx2.Client, Stream[Any]], OpenAI): + @overload + def __init__( + self, + *, + azure_endpoint: str, + azure_deployment: str | None = None, + api_version: str | None = None, + api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AzureADTokenProvider | None = None, + organization: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx2.Client | None = None, + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + ) -> None: ... + + @overload + def __init__( + self, + *, + azure_deployment: str | None = None, + api_version: str | None = None, + api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AzureADTokenProvider | None = None, + organization: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx2.Client | None = None, + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + ) -> None: ... + + @overload + def __init__( + self, + *, + base_url: str, + api_version: str | None = None, + api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AzureADTokenProvider | None = None, + organization: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx2.Client | None = None, + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + ) -> None: ... + + def __init__( + self, + *, + api_version: str | None = None, + azure_endpoint: str | None = None, + azure_deployment: str | None = None, + api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, + # workload_identity is not functional in the Azure client + workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AzureADTokenProvider | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + base_url: str | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx2.Client | None = None, + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + ) -> None: + """Construct a new synchronous azure openai client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `AZURE_OPENAI_API_KEY` + - `organization` from `OPENAI_ORG_ID` + - `project` from `OPENAI_PROJECT_ID` + - `azure_ad_token` from `AZURE_OPENAI_AD_TOKEN` + - `api_version` from `OPENAI_API_VERSION` + - `azure_endpoint` from `AZURE_OPENAI_ENDPOINT` + + Pass at most one of `api_key`, `azure_ad_token`, or `azure_ad_token_provider`. + An explicit credential takes precedence over Azure credential environment variables. + With no explicit credential, `AZURE_OPENAI_AD_TOKEN` takes precedence over `AZURE_OPENAI_API_KEY`. + + Args: + azure_endpoint: Your Azure endpoint, including the resource, e.g. `https://example-resource.azure.openai.com/` + + azure_ad_token: Your Azure Active Directory token, https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id + + azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request. + + azure_deployment: A model deployment, if given with `azure_endpoint`, sets the base client URL to include `/deployments/{azure_deployment}`. + Not supported with Assistants APIs. + """ + if is_x509_workload_identity(workload_identity): + raise OpenAIError("X.509 workload identity is not supported by Azure clients") + + api_key, azure_ad_token, azure_ad_token_provider = _resolve_azure_auth( + api_key, azure_ad_token, azure_ad_token_provider + ) + + if _enforce_credentials and api_key is None and azure_ad_token is None and azure_ad_token_provider is None: + raise OpenAIError( + "Missing credentials. Please pass one of `api_key`, `azure_ad_token`, `azure_ad_token_provider`, or the `AZURE_OPENAI_API_KEY` or `AZURE_OPENAI_AD_TOKEN` environment variables." + ) + + if api_version is None: + api_version = os.environ.get("OPENAI_API_VERSION") + + if api_version is None: + raise ValueError( + "Must provide either the `api_version` argument or the `OPENAI_API_VERSION` environment variable" + ) + + if default_query is None: + default_query = {"api-version": api_version} + else: + default_query = {**default_query, "api-version": api_version} + + if base_url is None: + if azure_endpoint is None: + azure_endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT") + + if azure_endpoint is None: + raise ValueError( + "Must provide one of the `base_url` or `azure_endpoint` arguments, or the `AZURE_OPENAI_ENDPOINT` environment variable" + ) + + if azure_deployment is not None: + base_url = f"{azure_endpoint.rstrip('/')}/openai/deployments/{azure_deployment}" + else: + base_url = f"{azure_endpoint.rstrip('/')}/openai" + else: + if azure_endpoint is not None: + raise ValueError("base_url and azure_endpoint are mutually exclusive") + + if api_key is None: + # define a sentinel value to avoid any typing issues + api_key = API_KEY_SENTINEL + + super().__init__( + api_key=api_key, + admin_api_key=admin_api_key, + organization=organization, + project=project, + webhook_secret=webhook_secret, + base_url=base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + websocket_base_url=websocket_base_url, + _strict_response_validation=_strict_response_validation, + _enforce_credentials=_enforce_credentials, + ) + self._api_version = api_version + self._azure_ad_token = azure_ad_token + self._azure_ad_token_provider = azure_ad_token_provider + self._azure_deployment = azure_deployment if azure_endpoint else None + self._azure_endpoint = httpx2.URL(azure_endpoint) if azure_endpoint else None + + hooks = self._client.event_hooks["request"] + if _strip_azure_api_key_on_redirect not in hooks: + hooks.append(_strip_azure_api_key_on_redirect) + + @override + def copy( + self, + *, + api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, + workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None, + provider: _Provider | None | NotGiven = NOT_GIVEN, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + api_version: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AzureADTokenProvider | None = None, + base_url: str | httpx2.URL | None | NotGiven = NOT_GIVEN, + data_residency: DataResidency | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + http_client: httpx2.Client | None = None, + max_retries: int | NotGiven = NOT_GIVEN, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _enforce_credentials: bool | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance reusing the same options given to the current client with optional overriding. + """ + if data_residency is not None: + raise OpenAIError("`data_residency` is only supported by OpenAI clients") + base_url = None if isinstance(base_url, NotGiven) else base_url + if not isinstance(provider, NotGiven): + raise OpenAIError("Configure `provider` on `OpenAI`, not on `AzureOpenAI.with_options()`.") + if is_x509_workload_identity(workload_identity): + raise OpenAIError("X.509 workload identity is not supported by Azure clients") + + api_key, azure_ad_token, azure_ad_token_provider = _copy_azure_auth( + api_key, + azure_ad_token, + azure_ad_token_provider, + current_api_key=self._api_key_provider or self.api_key, + current_token=self._azure_ad_token, + current_provider=self._azure_ad_token_provider, + ) + + copied = super().copy( + api_key=api_key, + admin_api_key=admin_api_key, + workload_identity=workload_identity, + organization=organization, + project=project, + webhook_secret=webhook_secret, + websocket_base_url=websocket_base_url, + base_url=base_url, + timeout=timeout, + http_client=http_client, + max_retries=max_retries, + default_headers=default_headers, + set_default_headers=set_default_headers, + default_query=default_query, + set_default_query=set_default_query, + _enforce_credentials=_enforce_credentials, + _extra_kwargs={ + "api_version": api_version or self._api_version, + "azure_ad_token": azure_ad_token, + "azure_ad_token_provider": azure_ad_token_provider, + **_extra_kwargs, + }, + ) + # `super().copy()` reconstructs the client from `base_url`, which does not carry the + # Azure endpoint/deployment context that `_prepare_url` relies on to route + # non-deployment endpoints (e.g. `/models`). Preserve it unless the caller overrides + # the base URL. + if base_url is None: + copied._azure_endpoint = self._azure_endpoint + copied._azure_deployment = self._azure_deployment + return copied + + with_options = copy + + def _get_azure_ad_token(self) -> str | None: + if self._azure_ad_token is not None: + return self._azure_ad_token + + provider = self._azure_ad_token_provider + if provider is not None: + token = cast(object, provider()) + if isinstance(token, str): + # Bypass subclass methods before validating or interpolating credentials. + token = str.__str__(token) + if not isinstance(token, str) or not token: + raise ValueError("Expected `azure_ad_token_provider` argument to return a non-empty string.") + return token + + return None + + @override + def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: # noqa: ARG002 + if self._azure_ad_token is not None: + return {"Authorization": f"Bearer {self._azure_ad_token}"} + + if self.api_key and self.api_key != API_KEY_SENTINEL: + return {"api-key": self.api_key} + + return {} + + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if _has_auth_header(headers) or _has_auth_header(custom_headers): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either api_key, azure_ad_token or azure_ad_token_provider to be set. Or for one of the `Authorization` or `api-key` headers to be explicitly supplied or omitted"' + ) + + @override + def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + if self._api_key_provider is not None: + self._refresh_api_key() + + headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {} + + options = model_copy(options) + options.headers = headers + + azure_ad_token = self._get_azure_ad_token() + if azure_ad_token is not None: + if not _has_header(headers, "Authorization"): + headers["Authorization"] = f"Bearer {azure_ad_token}" + elif self.api_key and self.api_key != API_KEY_SENTINEL: + if not _has_header(headers, "api-key"): + headers["api-key"] = self.api_key + elif _has_auth_header(headers) or _has_auth_header(self.default_headers): + pass + else: + # should never be hit + raise ValueError("Unable to handle auth") + + return options + + def _configure_realtime(self, model: str, extra_query: Query) -> tuple[httpx2.URL, dict[str, str]]: + auth_headers = {} + query = { + **extra_query, + "api-version": self._api_version, + "deployment": self._azure_deployment or model, + } + if self.api_key and self.api_key != "": + auth_headers = {"api-key": self.api_key} + else: + token = self._get_azure_ad_token() + if token: + auth_headers = {"Authorization": f"Bearer {token}"} + + if self.websocket_base_url is not None: + base_url = normalize_httpx_url(self.websocket_base_url) + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" + realtime_url = base_url.copy_with(raw_path=merge_raw_path) + else: + base_url = self._prepare_url("/realtime") + realtime_url = base_url.copy_with(scheme="wss") + + url = realtime_url.copy_with(params={**query}) + return url, auth_headers + + +class AsyncAzureOpenAI(BaseAzureClient[httpx2.AsyncClient, AsyncStream[Any]], AsyncOpenAI): + @overload + def __init__( + self, + *, + azure_endpoint: str, + azure_deployment: str | None = None, + api_version: str | None = None, + api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx2.AsyncClient | None = None, + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + ) -> None: ... + + @overload + def __init__( + self, + *, + azure_deployment: str | None = None, + api_version: str | None = None, + api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx2.AsyncClient | None = None, + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + ) -> None: ... + + @overload + def __init__( + self, + *, + base_url: str, + api_version: str | None = None, + api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx2.AsyncClient | None = None, + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + ) -> None: ... + + def __init__( + self, + *, + azure_endpoint: str | None = None, + azure_deployment: str | None = None, + api_version: str | None = None, + api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, + # workload_identity is not functional in the Azure client + workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + base_url: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx2.AsyncClient | None = None, + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + ) -> None: + """Construct a new asynchronous azure openai client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `AZURE_OPENAI_API_KEY` + - `organization` from `OPENAI_ORG_ID` + - `project` from `OPENAI_PROJECT_ID` + - `azure_ad_token` from `AZURE_OPENAI_AD_TOKEN` + - `api_version` from `OPENAI_API_VERSION` + - `azure_endpoint` from `AZURE_OPENAI_ENDPOINT` + + Pass at most one of `api_key`, `azure_ad_token`, or `azure_ad_token_provider`. + An explicit credential takes precedence over Azure credential environment variables. + With no explicit credential, `AZURE_OPENAI_AD_TOKEN` takes precedence over `AZURE_OPENAI_API_KEY`. + + Args: + azure_endpoint: Your Azure endpoint, including the resource, e.g. `https://example-resource.azure.openai.com/` + + azure_ad_token: Your Azure Active Directory token, https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id + + azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request. + + azure_deployment: A model deployment, if given with `azure_endpoint`, sets the base client URL to include `/deployments/{azure_deployment}`. + Not supported with Assistants APIs. + """ + if is_x509_workload_identity(workload_identity): + raise OpenAIError("X.509 workload identity is not supported by Azure clients") + + api_key, azure_ad_token, azure_ad_token_provider = _resolve_azure_auth( + api_key, azure_ad_token, azure_ad_token_provider + ) + + if _enforce_credentials and api_key is None and azure_ad_token is None and azure_ad_token_provider is None: + raise OpenAIError( + "Missing credentials. Please pass one of `api_key`, `azure_ad_token`, `azure_ad_token_provider`, or the `AZURE_OPENAI_API_KEY` or `AZURE_OPENAI_AD_TOKEN` environment variables." + ) + + if api_version is None: + api_version = os.environ.get("OPENAI_API_VERSION") + + if api_version is None: + raise ValueError( + "Must provide either the `api_version` argument or the `OPENAI_API_VERSION` environment variable" + ) + + if default_query is None: + default_query = {"api-version": api_version} + else: + default_query = {**default_query, "api-version": api_version} + + if base_url is None: + if azure_endpoint is None: + azure_endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT") + + if azure_endpoint is None: + raise ValueError( + "Must provide one of the `base_url` or `azure_endpoint` arguments, or the `AZURE_OPENAI_ENDPOINT` environment variable" + ) + + if azure_deployment is not None: + base_url = f"{azure_endpoint.rstrip('/')}/openai/deployments/{azure_deployment}" + else: + base_url = f"{azure_endpoint.rstrip('/')}/openai" + else: + if azure_endpoint is not None: + raise ValueError("base_url and azure_endpoint are mutually exclusive") + + if api_key is None: + # define a sentinel value to avoid any typing issues + api_key = API_KEY_SENTINEL + + super().__init__( + api_key=api_key, + admin_api_key=admin_api_key, + organization=organization, + project=project, + webhook_secret=webhook_secret, + base_url=base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + websocket_base_url=websocket_base_url, + _strict_response_validation=_strict_response_validation, + _enforce_credentials=_enforce_credentials, + ) + self._api_version = api_version + self._azure_ad_token = azure_ad_token + self._azure_ad_token_provider = azure_ad_token_provider + self._azure_deployment = azure_deployment if azure_endpoint else None + self._azure_endpoint = httpx2.URL(azure_endpoint) if azure_endpoint else None + + hooks = self._client.event_hooks["request"] + if _async_strip_azure_api_key_on_redirect not in hooks: + hooks.append(_async_strip_azure_api_key_on_redirect) + + @override + def copy( + self, + *, + api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, + workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None, + provider: _Provider | None | NotGiven = NOT_GIVEN, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + api_version: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, + base_url: str | httpx2.URL | None | NotGiven = NOT_GIVEN, + data_residency: DataResidency | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + http_client: httpx2.AsyncClient | None = None, + max_retries: int | NotGiven = NOT_GIVEN, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _enforce_credentials: bool | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance reusing the same options given to the current client with optional overriding. + """ + if data_residency is not None: + raise OpenAIError("`data_residency` is only supported by OpenAI clients") + base_url = None if isinstance(base_url, NotGiven) else base_url + if not isinstance(provider, NotGiven): + raise OpenAIError("Configure `provider` on `AsyncOpenAI`, not on `AsyncAzureOpenAI.with_options()`.") + if is_x509_workload_identity(workload_identity): + raise OpenAIError("X.509 workload identity is not supported by Azure clients") + + api_key, azure_ad_token, azure_ad_token_provider = _copy_azure_auth( + api_key, + azure_ad_token, + azure_ad_token_provider, + current_api_key=self._api_key_provider or self.api_key, + current_token=self._azure_ad_token, + current_provider=self._azure_ad_token_provider, + ) + + copied = super().copy( + api_key=api_key, + admin_api_key=admin_api_key, + workload_identity=workload_identity, + organization=organization, + project=project, + webhook_secret=webhook_secret, + websocket_base_url=websocket_base_url, + base_url=base_url, + timeout=timeout, + http_client=http_client, + max_retries=max_retries, + default_headers=default_headers, + set_default_headers=set_default_headers, + default_query=default_query, + set_default_query=set_default_query, + _enforce_credentials=_enforce_credentials, + _extra_kwargs={ + "api_version": api_version or self._api_version, + "azure_ad_token": azure_ad_token, + "azure_ad_token_provider": azure_ad_token_provider, + **_extra_kwargs, + }, + ) + # `super().copy()` reconstructs the client from `base_url`, which does not carry the + # Azure endpoint/deployment context that `_prepare_url` relies on to route + # non-deployment endpoints (e.g. `/models`). Preserve it unless the caller overrides + # the base URL. + if base_url is None: + copied._azure_endpoint = self._azure_endpoint + copied._azure_deployment = self._azure_deployment + return copied + + with_options = copy + + async def _get_azure_ad_token(self) -> str | None: + if self._azure_ad_token is not None: + return self._azure_ad_token + + provider = self._azure_ad_token_provider + if provider is not None: + token = cast(object, provider()) + if inspect.isawaitable(token): + token = await token + if isinstance(token, str): + # Bypass subclass methods before validating or interpolating credentials. + token = str.__str__(token) + if not isinstance(token, str) or not token: + raise ValueError("Expected `azure_ad_token_provider` argument to return a non-empty string.") + return token + + return None + + @override + def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: # noqa: ARG002 + if self._azure_ad_token is not None: + return {"Authorization": f"Bearer {self._azure_ad_token}"} + + if self.api_key and self.api_key != API_KEY_SENTINEL: + return {"api-key": self.api_key} + + return {} + + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if _has_auth_header(headers) or _has_auth_header(custom_headers): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either api_key, azure_ad_token or azure_ad_token_provider to be set. Or for one of the `Authorization` or `api-key` headers to be explicitly supplied or omitted"' + ) + + @override + async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + if self._api_key_provider is not None: + await self._refresh_api_key() + + headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {} + + options = model_copy(options) + options.headers = headers + + azure_ad_token = await self._get_azure_ad_token() + if azure_ad_token is not None: + if not _has_header(headers, "Authorization"): + headers["Authorization"] = f"Bearer {azure_ad_token}" + elif self.api_key and self.api_key != API_KEY_SENTINEL: + if not _has_header(headers, "api-key"): + headers["api-key"] = self.api_key + elif _has_auth_header(headers) or _has_auth_header(self.default_headers): + pass + else: + # should never be hit + raise ValueError("Unable to handle auth") + + return options + + async def _configure_realtime(self, model: str, extra_query: Query) -> tuple[httpx2.URL, dict[str, str]]: + auth_headers = {} + query = { + **extra_query, + "api-version": self._api_version, + "deployment": self._azure_deployment or model, + } + if self.api_key and self.api_key != "": + auth_headers = {"api-key": self.api_key} + else: + token = await self._get_azure_ad_token() + if token: + auth_headers = {"Authorization": f"Bearer {token}"} + + if self.websocket_base_url is not None: + base_url = normalize_httpx_url(self.websocket_base_url) + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" + realtime_url = base_url.copy_with(raw_path=merge_raw_path) + else: + base_url = self._prepare_url("/realtime") + realtime_url = base_url.copy_with(scheme="wss") + + url = realtime_url.copy_with(params={**query}) + return url, auth_headers diff --git a/venv/lib/python3.12/site-packages/openai/lib/bedrock.py b/venv/lib/python3.12/site-packages/openai/lib/bedrock.py new file mode 100644 index 0000000..2779a41 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/bedrock.py @@ -0,0 +1,891 @@ +from __future__ import annotations + +import os +import hashlib +import inspect +from typing import Any, Literal, Mapping, Callable, Optional, Awaitable, cast +from dataclasses import field, replace, dataclass +from typing_extensions import Self, override + +import httpx2 + +from ..auth import WorkloadIdentity, X509WorkloadIdentity +from .._types import NOT_GIVEN, Timeout, NotGiven +from .._utils import is_given +from .._client import OpenAI, AsyncOpenAI +from .._httpx2 import normalize_httpx_url +from .._models import FinalRequestOptions +from .._provider import _Provider, _configure_provider +from .._exceptions import OpenAIError +from .._base_client import DEFAULT_MAX_RETRIES +from .._data_residency import DataResidency +from ..providers.bedrock import ( + AwsCredentialsProvider, + bedrock, + _BedrockProviderRuntime, + _parse_bedrock_endpoint_hostname, +) + +BedrockTokenProvider = Callable[[], str] +AsyncBedrockTokenProvider = Callable[[], "str | Awaitable[str]"] +_LegacyAuthMode = Literal["bearer", "token_provider", "aws"] +_LegacyAuthConfiguration = tuple[_LegacyAuthMode, Optional[object]] +_LEGACY_SIGNATURE_KEY = os.urandom(32) + + +@dataclass(frozen=True) +class _LegacyRuntimeSignature: + mode: _LegacyAuthMode + base_url: str + region: str | None + credential_identity: object = field(repr=False) + + +@dataclass(frozen=True) +class _LegacyBedrockState: + explicit_api_key: str | None = field(repr=False) + token_provider: BedrockTokenProvider | AsyncBedrockTokenProvider | None = field(repr=False, compare=False) + aws_region: str | None + region_was_explicit: bool + aws_profile: str | None + aws_access_key_id: str | None = field(repr=False) + aws_secret_access_key: str | None = field(repr=False) + aws_session_token: str | None = field(repr=False) + aws_credentials_provider: AwsCredentialsProvider | None = field(repr=False, compare=False) + uses_environment_bearer: bool + environment_bearer_token: str | None = field(repr=False) + uses_region_derived_base_url: bool + + +def _state_api_key(state: _LegacyBedrockState) -> str: + return state.explicit_api_key or (state.environment_bearer_token if state.uses_environment_bearer else "") or "" + + +def _constructor_accepts_keyword(constructor: Callable[..., object], name: str) -> bool: + try: + parameters = inspect.signature(constructor).parameters + except (TypeError, ValueError): + return False + + return name in parameters or any( + parameter.kind is inspect.Parameter.VAR_KEYWORD for parameter in parameters.values() + ) + + +def _configured_region(region: str | None) -> str | None: + configured = region or os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION") + return configured.strip() if configured is not None and configured.strip() else None + + +def _legacy_endpoint(base_url: str | httpx2.URL | None | NotGiven) -> Literal["mantle"] | None: + configured = os.environ.get("AWS_BEDROCK_BASE_URL") if isinstance(base_url, NotGiven) else base_url + if configured is None or isinstance(configured, str) and not configured.strip(): + return None + return None if _parse_bedrock_endpoint_hostname(normalize_httpx_url(configured).host) is not None else "mantle" + + +def _uses_region_derived_base_url(base_url: str | httpx2.URL | None) -> bool: + if isinstance(base_url, str) and not base_url.strip(): + base_url = None + if base_url is not None: + return False + + environment_base_url = os.environ.get("AWS_BEDROCK_BASE_URL") + return environment_base_url is None or not environment_base_url.strip() + + +def _has_explicit_aws_auth( + *, + aws_profile: str | None, + aws_access_key_id: str | None, + aws_secret_access_key: str | None, + aws_session_token: str | None, + aws_credentials_provider: AwsCredentialsProvider | None, +) -> bool: + return any( + value is not None + for value in ( + aws_profile, + aws_access_key_id, + aws_secret_access_key, + aws_session_token, + aws_credentials_provider, + ) + ) + + +def _environment_bearer_token() -> str: + token = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") + if not token: + raise OpenAIError( + "Could not find credentials for Bedrock. Set `AWS_BEARER_TOKEN_BEDROCK` or configure the default " + "AWS credential chain." + ) + return token + + +def _legacy_provider( + *, + api_key: str | None, + token_provider: BedrockTokenProvider | AsyncBedrockTokenProvider | None, + aws_region: str | None, + aws_profile: str | None, + aws_access_key_id: str | None, + aws_secret_access_key: str | None, + aws_session_token: str | None, + aws_credentials_provider: AwsCredentialsProvider | None, + base_url: str | httpx2.URL | None, + region_was_explicit: bool | None = None, +) -> tuple[_Provider, _LegacyBedrockState, str]: + if callable(cast(object, api_key)): + raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") + if api_key == "": + raise OpenAIError("The `api_key` argument must not be empty.") + if api_key is not None and token_provider is not None: + raise OpenAIError( + "Bedrock authentication is ambiguous. Configure exactly one explicit mode: bearer credential, " + "static AWS credentials, profile, or credential provider." + ) + + explicit_aws_auth = _has_explicit_aws_auth( + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + ) + if (api_key is not None or token_provider is not None) and explicit_aws_auth: + raise OpenAIError( + "Bedrock authentication is ambiguous. Configure exactly one explicit mode: bearer credential, " + "static AWS credentials, profile, or credential provider." + ) + + environment_token = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") + uses_environment_bearer = ( + api_key is None and token_provider is None and not explicit_aws_auth and bool(environment_token) + ) + resolved_region = _configured_region(aws_region) + uses_region_derived_base_url = _uses_region_derived_base_url(base_url) + + provider_base_url: str | httpx2.URL | None | NotGiven + if isinstance(base_url, str) and not base_url.strip(): + provider_base_url = None + elif base_url is None: + provider_base_url = NOT_GIVEN + else: + provider_base_url = base_url + + provider = bedrock( + endpoint=_legacy_endpoint(provider_base_url), + region=aws_region, + base_url=provider_base_url, + api_key=api_key if api_key is not None else environment_token if uses_environment_bearer else NOT_GIVEN, + token_provider=token_provider, + access_key_id=aws_access_key_id, + secret_access_key=aws_secret_access_key, + session_token=aws_session_token, + profile=aws_profile, + credential_provider=aws_credentials_provider, + ) + state = _LegacyBedrockState( + explicit_api_key=api_key, + token_provider=token_provider, + aws_region=resolved_region, + region_was_explicit=( + bool(aws_region and aws_region.strip()) if region_was_explicit is None else region_was_explicit + ), + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + uses_environment_bearer=uses_environment_bearer, + environment_bearer_token=environment_token if uses_environment_bearer else None, + uses_region_derived_base_url=uses_region_derived_base_url, + ) + return provider, state, api_key or (environment_token if uses_environment_bearer else "") or "" + + +def _copy_configuration( + client: BedrockOpenAI | AsyncBedrockOpenAI, + *, + api_key: str | None, + token_provider: BedrockTokenProvider | AsyncBedrockTokenProvider | None, + aws_region: str | None, + aws_profile: str | None, + aws_access_key_id: str | None, + aws_secret_access_key: str | None, + aws_session_token: str | None, + aws_credentials_provider: AwsCredentialsProvider | None, + base_url: str | httpx2.URL | None, +) -> tuple[dict[str, object], _Provider | None, _LegacyBedrockState | None]: + _synchronize_legacy_routing_state(client) + state = client._bedrock_state + current_api_key = client.api_key or "" + api_key_was_mutated = state.token_provider is None and current_api_key != _state_api_key(state) + aws_override = _has_explicit_aws_auth( + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + ) + explicit_bearer_override = api_key is not None or token_provider is not None + if explicit_bearer_override and aws_override: + raise OpenAIError( + "Bedrock authentication is ambiguous. Configure exactly one explicit mode: bearer credential, " + "static AWS credentials, profile, or credential provider." + ) + + effective_api_key = ( + api_key + if api_key is not None + else current_api_key + if api_key_was_mutated and token_provider is None and not aws_override + else None + ) + bearer_override = effective_api_key is not None or token_provider is not None + + routing_override = aws_region is not None or base_url is not None + if not bearer_override and not aws_override and not routing_override: + _refresh_legacy_provider_runtime(client) + return {}, client._bedrock_provider, client._bedrock_state + + if bearer_override: + next_api_key = effective_api_key + next_token_provider = token_provider + next_profile = None + next_access_key_id = None + next_secret_access_key = None + next_session_token = None + next_credentials_provider = None + elif aws_override: + next_api_key = None + next_token_provider = None + next_profile = aws_profile + next_access_key_id = aws_access_key_id + next_secret_access_key = aws_secret_access_key + next_session_token = aws_session_token + next_credentials_provider = aws_credentials_provider + else: + next_api_key = state.explicit_api_key + next_token_provider = state.token_provider + if state.uses_environment_bearer: + next_api_key = state.environment_bearer_token or _environment_bearer_token() + next_token_provider = None + next_profile = state.aws_profile + next_access_key_id = state.aws_access_key_id + next_secret_access_key = state.aws_secret_access_key + next_session_token = state.aws_session_token + next_credentials_provider = state.aws_credentials_provider + + next_region = aws_region if aws_region is not None else client.aws_region + next_region_was_explicit = aws_region is not None or state.region_was_explicit + restores_region_derived_base_url = isinstance(base_url, str) and not base_url.strip() + if ( + (next_api_key is not None or next_token_provider is not None) + and not next_region_was_explicit + and not restores_region_derived_base_url + and (base_url is not None or not state.uses_region_derived_base_url) + ): + next_region = None + if aws_profile is not None and aws_region is None and not state.region_was_explicit: + next_region = None + + if base_url is not None: + next_base_url: str | httpx2.URL | None = base_url + elif state.uses_region_derived_base_url: + next_base_url = "" + else: + next_base_url = client.base_url + + provider_kwargs: dict[str, object] = { + "api_key": next_api_key, + "bedrock_token_provider": next_token_provider, + "aws_region": next_region, + "aws_profile": next_profile, + "aws_access_key_id": next_access_key_id, + "aws_secret_access_key": next_secret_access_key, + "aws_session_token": next_session_token, + "aws_credentials_provider": next_credentials_provider, + "base_url": next_base_url, + } + if _constructor_accepts_keyword(client.__class__.__init__, "_region_was_explicit"): + provider_kwargs["_region_was_explicit"] = next_region_was_explicit + + return provider_kwargs, None, None + + +def _legacy_runtime_signature( + client: BedrockOpenAI | AsyncBedrockOpenAI, + configuration: _LegacyAuthConfiguration, +) -> _LegacyRuntimeSignature: + mode, credential = configuration + credential_identity: object = ( + hashlib.blake2s(credential.encode(), key=_LEGACY_SIGNATURE_KEY).digest() + if isinstance(credential, str) + else id(credential) + ) + return _LegacyRuntimeSignature( + mode=mode, + base_url=str(client.base_url), + region=client.aws_region, + credential_identity=credential_identity, + ) + + +def _provider_for_legacy_client( + client: BedrockOpenAI | AsyncBedrockOpenAI, + configuration: _LegacyAuthConfiguration, +) -> _Provider: + mode, credential = configuration + state = client._bedrock_state + bearer_region = client.aws_region if state.region_was_explicit else None + if mode == "bearer": + if not isinstance(credential, str) or not credential: + raise OpenAIError("The Bedrock bearer credential must not be empty.") + return bedrock( + endpoint=_legacy_endpoint(client.base_url), + region=bearer_region, + base_url=client.base_url, + api_key=credential, + ) + if mode == "token_provider": + return bedrock( + endpoint=_legacy_endpoint(client.base_url), + region=bearer_region, + base_url=client.base_url, + token_provider=cast("AsyncBedrockTokenProvider", credential), + ) + + return bedrock( + endpoint=_legacy_endpoint(client.base_url), + region=client.aws_region, + base_url=client.base_url, + profile=state.aws_profile, + access_key_id=state.aws_access_key_id, + secret_access_key=state.aws_secret_access_key, + session_token=state.aws_session_token, + credential_provider=state.aws_credentials_provider, + ) + + +def _synchronize_legacy_routing_state(client: BedrockOpenAI | AsyncBedrockOpenAI) -> None: + previous_signature = client._bedrock_runtime_signature + base_url_changed = str(client.base_url) != previous_signature.base_url + region_changed = client.aws_region != previous_signature.region + if base_url_changed: + client._bedrock_state = replace(client._bedrock_state, uses_region_derived_base_url=False) + client._uses_region_derived_base_url = False + if region_changed: + client._bedrock_state = replace( + client._bedrock_state, + aws_region=client.aws_region, + region_was_explicit=client.aws_region is not None, + ) + if client._bedrock_state.uses_region_derived_base_url and client.aws_region is not None: + client.base_url = f"https://bedrock-mantle.{client.aws_region}.api.aws/openai/v1" + + +def _refresh_legacy_provider_runtime(client: BedrockOpenAI | AsyncBedrockOpenAI) -> None: + _synchronize_legacy_routing_state(client) + configuration = client._legacy_auth_configuration() + signature = _legacy_runtime_signature(client, configuration) + if signature == client._bedrock_runtime_signature: + return + + provider = _provider_for_legacy_client(client, configuration) + client._bedrock_provider = provider + client._provider = provider + client._provider_runtime = _configure_provider(provider) + if ( + isinstance(client._provider_runtime, _BedrockProviderRuntime) + and client.aws_region is None + and client._provider_runtime.region is not None + ): + client.aws_region = client._provider_runtime.region + client._bedrock_state = replace(client._bedrock_state, aws_region=client.aws_region) + client._bedrock_runtime_signature = _legacy_runtime_signature(client, configuration) + + +class BedrockOpenAI(OpenAI): + """Compatibility client for Amazon Bedrock's OpenAI-compatible endpoint.""" + + _bedrock_provider: _Provider + _bedrock_state: _LegacyBedrockState + _bedrock_token_provider: BedrockTokenProvider | None + _uses_region_derived_base_url: bool + _bedrock_runtime_signature: _LegacyRuntimeSignature + aws_region: str | None + + def __init__( + self, + *, + api_key: str | None = None, + bedrock_token_provider: BedrockTokenProvider | None = None, + aws_region: str | None = None, + aws_profile: str | None = None, + aws_access_key_id: str | None = None, + aws_secret_access_key: str | None = None, + aws_session_token: str | None = None, + aws_credentials_provider: AwsCredentialsProvider | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + base_url: str | httpx2.URL | None = None, + websocket_base_url: str | httpx2.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx2.Client | None = None, + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + _provider: _Provider | None = None, + _state: _LegacyBedrockState | None = None, + _region_was_explicit: bool | None = None, + ) -> None: + if _provider is None or _state is None: + _provider, _state, public_api_key = _legacy_provider( + api_key=api_key, + token_provider=bedrock_token_provider, + aws_region=aws_region, + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + base_url=base_url, + region_was_explicit=_region_was_explicit, + ) + else: + public_api_key = ( + _state.explicit_api_key + or (_state.environment_bearer_token if _state.uses_environment_bearer else "") + or "" + ) + + super().__init__( + provider=_provider, + organization=organization, + project=project, + webhook_secret=webhook_secret, + websocket_base_url=websocket_base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + _strict_response_validation=_strict_response_validation, + _enforce_credentials=False, + ) + + self._bedrock_provider = _provider + self._bedrock_state = _state + self._bedrock_token_provider = cast("BedrockTokenProvider | None", _state.token_provider) + self._uses_region_derived_base_url = _state.uses_region_derived_base_url + canonical_endpoint = _parse_bedrock_endpoint_hostname(self.base_url.host) + provider_region = ( + self._provider_runtime.region if isinstance(self._provider_runtime, _BedrockProviderRuntime) else None + ) + self.aws_region = ( + _state.aws_region or provider_region or (canonical_endpoint[1] if canonical_endpoint is not None else None) + ) + self._bedrock_state = replace(_state, aws_region=self.aws_region) + self.api_key = public_api_key or "" + self._bedrock_runtime_signature = _legacy_runtime_signature(self, self._legacy_auth_configuration()) + + def _legacy_auth_configuration(self) -> _LegacyAuthConfiguration: + if self._bedrock_token_provider is not None: + return ("token_provider", self._bedrock_token_provider) + if ( + self._bedrock_state.explicit_api_key is not None + or self._bedrock_state.uses_environment_bearer + or self.api_key + ): + return ("bearer", self.api_key) + return ("aws", None) + + def _uses_aws_auth(self) -> bool: + return ( + self._bedrock_state.explicit_api_key is None + and not self.api_key + and self._bedrock_token_provider is None + and not self._bedrock_state.uses_environment_bearer + ) + + @override + def _refresh_api_key(self) -> str: + if self._bedrock_state.uses_environment_bearer: + captured = self._bedrock_state.environment_bearer_token or "" + return self.api_key if self.api_key and self.api_key != captured else captured + if self._bedrock_token_provider is not None: + token = cast(object, self._bedrock_token_provider()) + if not isinstance(token, str) or not token: + raise ValueError("Expected `bedrock_token_provider` argument to return a non-empty string.") + return token + return self.api_key + + @override + def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + _refresh_legacy_provider_runtime(self) + return super()._prepare_options(options) + + @override + def copy( + self, + *, + api_key: str | BedrockTokenProvider | None = None, + admin_api_key: str | None = None, + workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None, + provider: _Provider | None | NotGiven = NOT_GIVEN, + bedrock_token_provider: BedrockTokenProvider | None = None, + aws_region: str | None = None, + aws_profile: str | None = None, + aws_access_key_id: str | None = None, + aws_secret_access_key: str | None = None, + aws_session_token: str | None = None, + aws_credentials_provider: AwsCredentialsProvider | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + base_url: str | httpx2.URL | None | NotGiven = NOT_GIVEN, + data_residency: DataResidency | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + http_client: httpx2.Client | None = None, + max_retries: int | NotGiven = NOT_GIVEN, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _enforce_credentials: bool | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + if data_residency is not None: + raise OpenAIError("`data_residency` is only supported by OpenAI clients") + base_url = None if isinstance(base_url, NotGiven) else base_url + if callable(api_key): + raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") + if not isinstance(provider, NotGiven): + raise OpenAIError("Configure `provider` on `OpenAI`, not on `BedrockOpenAI.with_options()`.") + if admin_api_key is not None or workload_identity is not None: + raise OpenAIError("BedrockOpenAI only supports Bedrock bearer token or AWS credential authentication.") + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + provider_kwargs, inherited_provider, inherited_state = _copy_configuration( + self, + api_key=api_key, + token_provider=bedrock_token_provider, + aws_region=aws_region, + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + base_url=base_url, + ) + constructor_kwargs: dict[str, Any] = { + **provider_kwargs, + "organization": organization if organization is not None else self.organization, + "project": project if project is not None else self.project, + "webhook_secret": webhook_secret if webhook_secret is not None else self.webhook_secret, + "websocket_base_url": websocket_base_url if websocket_base_url is not None else self.websocket_base_url, + "timeout": self.timeout if isinstance(timeout, NotGiven) else timeout, + "http_client": http_client or self._client, + "max_retries": max_retries if is_given(max_retries) else self.max_retries, + "default_headers": headers, + "default_query": params, + "_enforce_credentials": True if _enforce_credentials is None else _enforce_credentials, + **_extra_kwargs, + } + if inherited_provider is not None and _constructor_accepts_keyword(self.__class__.__init__, "_provider"): + constructor_kwargs["_provider"] = inherited_provider + constructor_kwargs["_state"] = inherited_state + elif inherited_provider is not None: + constructor_kwargs.update( + api_key=self._bedrock_state.explicit_api_key or self._bedrock_state.environment_bearer_token, + bedrock_token_provider=self._bedrock_state.token_provider, + aws_region=self._bedrock_state.aws_region, + aws_profile=self._bedrock_state.aws_profile, + aws_access_key_id=self._bedrock_state.aws_access_key_id, + aws_secret_access_key=self._bedrock_state.aws_secret_access_key, + aws_session_token=self._bedrock_state.aws_session_token, + aws_credentials_provider=self._bedrock_state.aws_credentials_provider, + base_url="" if self._bedrock_state.uses_region_derived_base_url else self.base_url, + ) + constructor_kwargs = { + name: value + for name, value in constructor_kwargs.items() + if _constructor_accepts_keyword(self.__class__.__init__, name) + } + elif self.__class__ is not BedrockOpenAI: + constructor_kwargs = { + name: value + for name, value in constructor_kwargs.items() + if value is not None or _constructor_accepts_keyword(self.__class__.__init__, name) + } + return self.__class__(**constructor_kwargs) + + with_options = copy + + +class AsyncBedrockOpenAI(AsyncOpenAI): + """Async compatibility client for Amazon Bedrock's OpenAI-compatible endpoint.""" + + _bedrock_provider: _Provider + _bedrock_state: _LegacyBedrockState + _bedrock_token_provider: AsyncBedrockTokenProvider | None + _uses_region_derived_base_url: bool + _bedrock_runtime_signature: _LegacyRuntimeSignature + aws_region: str | None + + def __init__( + self, + *, + api_key: str | None = None, + bedrock_token_provider: AsyncBedrockTokenProvider | None = None, + aws_region: str | None = None, + aws_profile: str | None = None, + aws_access_key_id: str | None = None, + aws_secret_access_key: str | None = None, + aws_session_token: str | None = None, + aws_credentials_provider: AwsCredentialsProvider | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + base_url: str | httpx2.URL | None = None, + websocket_base_url: str | httpx2.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx2.AsyncClient | None = None, + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + _provider: _Provider | None = None, + _state: _LegacyBedrockState | None = None, + _region_was_explicit: bool | None = None, + ) -> None: + if _provider is None or _state is None: + _provider, _state, public_api_key = _legacy_provider( + api_key=api_key, + token_provider=bedrock_token_provider, + aws_region=aws_region, + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + base_url=base_url, + region_was_explicit=_region_was_explicit, + ) + else: + public_api_key = ( + _state.explicit_api_key + or (_state.environment_bearer_token if _state.uses_environment_bearer else "") + or "" + ) + + super().__init__( + provider=_provider, + organization=organization, + project=project, + webhook_secret=webhook_secret, + websocket_base_url=websocket_base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + _strict_response_validation=_strict_response_validation, + _enforce_credentials=False, + ) + + self._bedrock_provider = _provider + self._bedrock_state = _state + self._bedrock_token_provider = cast("AsyncBedrockTokenProvider | None", _state.token_provider) + self._uses_region_derived_base_url = _state.uses_region_derived_base_url + canonical_endpoint = _parse_bedrock_endpoint_hostname(self.base_url.host) + provider_region = ( + self._provider_runtime.region if isinstance(self._provider_runtime, _BedrockProviderRuntime) else None + ) + self.aws_region = ( + _state.aws_region or provider_region or (canonical_endpoint[1] if canonical_endpoint is not None else None) + ) + self._bedrock_state = replace(_state, aws_region=self.aws_region) + self.api_key = public_api_key or "" + self._bedrock_runtime_signature = _legacy_runtime_signature(self, self._legacy_auth_configuration()) + + def _legacy_auth_configuration(self) -> _LegacyAuthConfiguration: + if self._bedrock_token_provider is not None: + return ("token_provider", self._bedrock_token_provider) + if ( + self._bedrock_state.explicit_api_key is not None + or self._bedrock_state.uses_environment_bearer + or self.api_key + ): + return ("bearer", self.api_key) + return ("aws", None) + + def _uses_aws_auth(self) -> bool: + return ( + self._bedrock_state.explicit_api_key is None + and not self.api_key + and self._bedrock_token_provider is None + and not self._bedrock_state.uses_environment_bearer + ) + + @override + async def _refresh_api_key(self) -> str: + if self._bedrock_state.uses_environment_bearer: + captured = self._bedrock_state.environment_bearer_token or "" + return self.api_key if self.api_key and self.api_key != captured else captured + if self._bedrock_token_provider is not None: + token = cast(object, self._bedrock_token_provider()) + if inspect.isawaitable(token): + token = await token + if not isinstance(token, str) or not token: + raise ValueError("Expected `bedrock_token_provider` argument to return a non-empty string.") + return token + return self.api_key + + @override + async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + _refresh_legacy_provider_runtime(self) + return await super()._prepare_options(options) + + @override + def copy( + self, + *, + api_key: str | AsyncBedrockTokenProvider | None = None, + admin_api_key: str | None = None, + workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None, + provider: _Provider | None | NotGiven = NOT_GIVEN, + bedrock_token_provider: AsyncBedrockTokenProvider | None = None, + aws_region: str | None = None, + aws_profile: str | None = None, + aws_access_key_id: str | None = None, + aws_secret_access_key: str | None = None, + aws_session_token: str | None = None, + aws_credentials_provider: AwsCredentialsProvider | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx2.URL | None = None, + base_url: str | httpx2.URL | None | NotGiven = NOT_GIVEN, + data_residency: DataResidency | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + http_client: httpx2.AsyncClient | None = None, + max_retries: int | NotGiven = NOT_GIVEN, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _enforce_credentials: bool | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + if data_residency is not None: + raise OpenAIError("`data_residency` is only supported by OpenAI clients") + base_url = None if isinstance(base_url, NotGiven) else base_url + if callable(api_key): + raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") + if not isinstance(provider, NotGiven): + raise OpenAIError("Configure `provider` on `AsyncOpenAI`, not on `AsyncBedrockOpenAI.with_options()`.") + if admin_api_key is not None or workload_identity is not None: + raise OpenAIError("AsyncBedrockOpenAI only supports Bedrock bearer token or AWS credential authentication.") + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + provider_kwargs, inherited_provider, inherited_state = _copy_configuration( + self, + api_key=api_key, + token_provider=bedrock_token_provider, + aws_region=aws_region, + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + base_url=base_url, + ) + constructor_kwargs: dict[str, Any] = { + **provider_kwargs, + "organization": organization if organization is not None else self.organization, + "project": project if project is not None else self.project, + "webhook_secret": webhook_secret if webhook_secret is not None else self.webhook_secret, + "websocket_base_url": websocket_base_url if websocket_base_url is not None else self.websocket_base_url, + "timeout": self.timeout if isinstance(timeout, NotGiven) else timeout, + "http_client": http_client or self._client, + "max_retries": max_retries if is_given(max_retries) else self.max_retries, + "default_headers": headers, + "default_query": params, + "_enforce_credentials": True if _enforce_credentials is None else _enforce_credentials, + **_extra_kwargs, + } + if inherited_provider is not None and _constructor_accepts_keyword(self.__class__.__init__, "_provider"): + constructor_kwargs["_provider"] = inherited_provider + constructor_kwargs["_state"] = inherited_state + elif inherited_provider is not None: + constructor_kwargs.update( + api_key=self._bedrock_state.explicit_api_key or self._bedrock_state.environment_bearer_token, + bedrock_token_provider=self._bedrock_state.token_provider, + aws_region=self._bedrock_state.aws_region, + aws_profile=self._bedrock_state.aws_profile, + aws_access_key_id=self._bedrock_state.aws_access_key_id, + aws_secret_access_key=self._bedrock_state.aws_secret_access_key, + aws_session_token=self._bedrock_state.aws_session_token, + aws_credentials_provider=self._bedrock_state.aws_credentials_provider, + base_url="" if self._bedrock_state.uses_region_derived_base_url else self.base_url, + ) + constructor_kwargs = { + name: value + for name, value in constructor_kwargs.items() + if _constructor_accepts_keyword(self.__class__.__init__, name) + } + elif self.__class__ is not AsyncBedrockOpenAI: + constructor_kwargs = { + name: value + for name, value in constructor_kwargs.items() + if value is not None or _constructor_accepts_keyword(self.__class__.__init__, name) + } + return self.__class__(**constructor_kwargs) + + with_options = copy + + +__all__ = [ + "BedrockOpenAI", + "AsyncBedrockOpenAI", + "BedrockTokenProvider", + "AsyncBedrockTokenProvider", + "AwsCredentialsProvider", +] diff --git a/venv/lib/python3.12/site-packages/openai/lib/live/README.md b/venv/lib/python3.12/site-packages/openai/lib/live/README.md new file mode 100644 index 0000000..55394b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/live/README.md @@ -0,0 +1,33 @@ +# Live WebSocket lifecycle + +`client.live.connect()` and `client.live.forks.connect(session_id=...)` leave +startup to the caller: send `connection.session.start(...)` and wait for +`session.started`. A sideband connection created by +`client.live.sideband.connect(session_id=...)` attaches to an existing session. +It does not send a start or require a new `session.started`; an attachment's +short replay is not a complete session snapshot. + +Direct `recv()` and `recv_bytes()` calls report transport errors to their +caller. Iterator reconnection is available only when an `on_reconnecting` +callback was explicitly supplied. The callback controls retry and can update +credentials or query parameters. A new socket is not proof that the previous +Live session, recording, or application state was restored. Applications own +their recovery decision and any necessary startup or state reconstruction. +Don't use multiple physical readers: a dispatcher owns the read loop while it +runs. Detaching or closing one transcript grouper doesn't cancel another +observer or the connection. + +All modes preserve the manager's caller-configured `max_queue_size`, including +if the queue was empty at connection time. Only messages that haven't been +attempted on a socket remain eligible for flushing after a retry. A direct send +exception is raised to its caller and cannot prove the server didn't receive +that message. A failure while flushing an already queued message still logs a +warning, as before; it cannot be raised at the original queueing call. Neither +failed attempt is automatically retried. Unattempted pre-open messages and +messages explicitly queued during recovery remain in order within their +existing budget. No automatic session reopening or restoration is implied. + +Treat each wire error as an event to handle, not as a successful session result. +In particular, preserve `session_storage_failed` if `session.closed` follows; +the close doesn't mean that recording storage succeeded. Unknown events and +fields are preserved for callers that need them. diff --git a/venv/lib/python3.12/site-packages/openai/lib/live/__init__.py b/venv/lib/python3.12/site-packages/openai/lib/live/__init__.py new file mode 100644 index 0000000..3feb12e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/live/__init__.py @@ -0,0 +1,9 @@ +from ._types import ( + TranscriptSegment as TranscriptSegment, + TranscriptSegmentClosedEvent as TranscriptSegmentClosedEvent, + TranscriptSegmentCloseReason as TranscriptSegmentCloseReason, +) +from ._transcript_grouper import ( + TranscriptGrouper as TranscriptGrouper, + AsyncTranscriptGrouper as AsyncTranscriptGrouper, +) diff --git a/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..14d9dcc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_listeners.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_listeners.cpython-312.pyc new file mode 100644 index 0000000..090f745 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_listeners.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_transcript_grouper.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_transcript_grouper.cpython-312.pyc new file mode 100644 index 0000000..971c38e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_transcript_grouper.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_transcript_grouping.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_transcript_grouping.cpython-312.pyc new file mode 100644 index 0000000..b16dc43 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_transcript_grouping.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_transcript_state.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_transcript_state.cpython-312.pyc new file mode 100644 index 0000000..9642450 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_transcript_state.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_types.cpython-312.pyc new file mode 100644 index 0000000..a6b2b2b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/live/__pycache__/_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/live/_listeners.py b/venv/lib/python3.12/site-packages/openai/lib/live/_listeners.py new file mode 100644 index 0000000..22e711f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/live/_listeners.py @@ -0,0 +1,103 @@ +from __future__ import annotations + +import threading +from typing import Any, Generic, TypeVar, Callable, overload +from dataclasses import dataclass +from typing_extensions import Self, Literal + +from ._types import GroupingUpdate, TranscriptSegment, TranscriptSegmentClosedEvent + +_ReturnT = TypeVar("_ReturnT") +_EventName = Literal["segment.updated", "segment.closed"] + + +@dataclass(eq=False) +class _Registration(Generic[_ReturnT]): + handler: Callable[..., _ReturnT] + once: bool + + +class TranscriptListeners(Generic[_ReturnT]): + """Typed, session-local callbacks; listener registration never holds the state lock.""" + + def __init__(self) -> None: + self._listeners: dict[str, list[_Registration[_ReturnT]]] = {} + self._listeners_lock = threading.Lock() + + @overload + def on(self, event_type: Literal["segment.updated"], handler: Callable[[TranscriptSegment], _ReturnT]) -> Self: ... + + @overload + def on( + self, event_type: Literal["segment.closed"], handler: Callable[[TranscriptSegmentClosedEvent], _ReturnT] + ) -> Self: ... + + @overload + def on( + self, event_type: Literal["segment.updated"] + ) -> Callable[[Callable[[TranscriptSegment], _ReturnT]], Callable[[TranscriptSegment], _ReturnT]]: ... + + @overload + def on( + self, event_type: Literal["segment.closed"] + ) -> Callable[ + [Callable[[TranscriptSegmentClosedEvent], _ReturnT]], Callable[[TranscriptSegmentClosedEvent], _ReturnT] + ]: ... + + def on(self, event_type: _EventName, handler: Callable[..., _ReturnT] | None = None) -> Any: + """Register a callback, or use as a decorator. Return self when given a handler.""" + return self._register(event_type, handler, once=False) + + @overload + def once( + self, event_type: Literal["segment.updated"], handler: Callable[[TranscriptSegment], _ReturnT] + ) -> Self: ... + + @overload + def once( + self, event_type: Literal["segment.closed"], handler: Callable[[TranscriptSegmentClosedEvent], _ReturnT] + ) -> Self: ... + + @overload + def once( + self, event_type: Literal["segment.updated"] + ) -> Callable[[Callable[[TranscriptSegment], _ReturnT]], Callable[[TranscriptSegment], _ReturnT]]: ... + + @overload + def once( + self, event_type: Literal["segment.closed"] + ) -> Callable[ + [Callable[[TranscriptSegmentClosedEvent], _ReturnT]], Callable[[TranscriptSegmentClosedEvent], _ReturnT] + ]: ... + + def once(self, event_type: _EventName, handler: Callable[..., _ReturnT] | None = None) -> Any: + """Register a callback for the next event only, optionally as a decorator.""" + return self._register(event_type, handler, once=True) + + def _register(self, event_type: _EventName, handler: Callable[..., _ReturnT] | None, *, once: bool) -> Any: + def register(fn: Callable[..., _ReturnT]) -> Callable[..., _ReturnT]: + with self._listeners_lock: + self._listeners.setdefault(event_type, []).append(_Registration(fn, once)) + return fn + + if handler is None: + return register + register(handler) + return self + + def off(self, event_type: _EventName, handler: Callable[..., _ReturnT]) -> Self: + """Remove the first matching registration, if present.""" + with self._listeners_lock: + listeners = self._listeners.get(event_type, []) + for index, registration in enumerate(listeners): + if registration.handler == handler: + del listeners[index] + break + return self + + def _handlers(self, update: GroupingUpdate) -> list[Callable[..., _ReturnT]]: + event_type = "segment.updated" if isinstance(update, TranscriptSegment) else "segment.closed" + with self._listeners_lock: + listeners = self._listeners.get(event_type, []) + self._listeners[event_type] = [registration for registration in listeners if not registration.once] + return [registration.handler for registration in listeners] diff --git a/venv/lib/python3.12/site-packages/openai/lib/live/_transcript_grouper.py b/venv/lib/python3.12/site-packages/openai/lib/live/_transcript_grouper.py new file mode 100644 index 0000000..4788608 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/live/_transcript_grouper.py @@ -0,0 +1,325 @@ +from __future__ import annotations + +import time +import asyncio +import inspect +import threading +from types import TracebackType +from typing import TypeVar, Callable, Sequence, Awaitable +from collections import deque +from dataclasses import dataclass +from typing_extensions import Self, Protocol, override + +from ._types import GroupingUpdate, TranscriptGrouperOptions +from ._listeners import TranscriptListeners +from ...types.live import ServerEvent +from ..._exceptions import OpenAIError +from ._transcript_state import TranscriptState + +_ReturnT = TypeVar("_ReturnT") + + +class _Timer(Protocol): + def cancel(self) -> None: ... + + +@dataclass +class _DispatchRun: + task: asyncio.Task[None] | None = None + waiters: int = 0 + observed: bool = False + + +class _BaseTranscriptGrouper(TranscriptListeners[_ReturnT]): + def __init__(self, options: TranscriptGrouperOptions) -> None: + super().__init__() + self._state = TranscriptState(options, self._now) + self._updates: deque[GroupingUpdate] = deque() + self._timer: _Timer | None = None + self._timer_generation = 0 + + def _now(self) -> float: + return time.monotonic() * 1000 + + def _clear_timer(self) -> None: + self._timer_generation += 1 + if self._timer is not None: + self._timer.cancel() + self._timer = None + + def _schedule(self) -> None: + self._clear_timer() + delay = self._state.delay() + if delay is not None: + generation = self._timer_generation + self._timer = self._call_later(delay, lambda: self._timer_fired(generation)) + + def _call_later(self, delay_ms: int, callback: Callable[[], None]) -> _Timer: + raise NotImplementedError + + def _timer_fired(self, generation: int) -> None: + raise NotImplementedError + + +def _options( + min_turn_separation_ms: float | None, + assistant_silence_ms: float | None, + backchannel_max_duration_ms: float | None, + backchannel_isolation_ms: float | None, + additional_acknowledgments: Sequence[str] | None, +) -> TranscriptGrouperOptions: + return TranscriptGrouperOptions( + 500 if min_turn_separation_ms is None else min_turn_separation_ms, + 2000 if assistant_silence_ms is None else assistant_silence_ms, + 1000 if backchannel_max_duration_ms is None else backchannel_max_duration_ms, + 2000 if backchannel_isolation_ms is None else backchannel_isolation_ms, + tuple(additional_acknowledgments or ()), + ) + + +class TranscriptGrouper(_BaseTranscriptGrouper[None]): + """Group public Live transcript events into immutable display segments. + + This is the TypeScript SDK's speaker/backchannel policy: only transcript + additions and session.closed are consumed. It is not VAD, playback tracking, + or a lossless transcript; brief overlapping acknowledgments may be suppressed. + Set backchannel_max_duration_ms=0 to disable suppression. + additional_acknowledgments extends the built-in phrases using the same timing + thresholds. Phrases are copied at construction and normalized like transcript + text: case, hyphens, whitespace, and surrounding punctuation are normalized. + + Use one instance per session. Call close() on disconnect or use a with block; + the grouper never owns, closes or reconnects your transport. Delayed delivery + can change grouping because monotonic local time supplies inactivity fallback. + + Callbacks are serialized and run outside the state lock. A timer-triggered + callback may run on a daemon timer thread. Callback exceptions propagate to + push/close callers, or to threading.excepthook for timer-triggered callbacks. + """ + + def __init__( + self, + *, + min_turn_separation_ms: float | None = None, + assistant_silence_ms: float | None = None, + backchannel_max_duration_ms: float | None = None, + backchannel_isolation_ms: float | None = None, + additional_acknowledgments: Sequence[str] | None = None, + ) -> None: + super().__init__( + _options( + min_turn_separation_ms, + assistant_silence_ms, + backchannel_max_duration_ms, + backchannel_isolation_ms, + additional_acknowledgments, + ) + ) + self._lock = threading.Lock() + self._dispatching = False + + def push(self, event: ServerEvent) -> None: + """Consume one typed Live event; duplicates and unrelated events are ignored. + + Invalid transcript fields or use after close raise OpenAIError before + changing state. Empty text does not restart inactivity. + """ + with self._lock: + updates = self._state.push(event) + if updates is None: + return + self._updates.extend(updates) + self._schedule() + self._dispatch() + + def close(self) -> None: + """Flush/finalize once and cancel timers, without closing the transport. + + Already-running callbacks finish in order, including when close is called + from a callback. Further push calls fail. + """ + with self._lock: + self._updates.extend(self._state.finish("manual")) + self._clear_timer() + self._dispatch() + + def __enter__(self) -> Self: + return self + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, traceback: TracebackType | None + ) -> None: + self.close() + + @override + def _call_later(self, delay_ms: int, callback: Callable[[], None]) -> _Timer: + timer = threading.Timer(delay_ms / 1000, callback) + timer.daemon = True + timer.start() + return timer + + @override + def _timer_fired(self, generation: int) -> None: + with self._lock: + if generation != self._timer_generation or self._state.closed: + return + self._timer = None + self._updates.extend(self._state.tick()) + self._schedule() + self._dispatch() + + def _dispatch(self) -> None: + with self._lock: + if self._dispatching: + return + self._dispatching = True + try: + while True: + with self._lock: + if not self._updates: + self._dispatching = False + return + update = self._updates.popleft() + for handler in self._handlers(update): + handler(update) + except BaseException: + with self._lock: + self._dispatching = False + raise + + +class AsyncTranscriptGrouper(_BaseTranscriptGrouper[None | Awaitable[None]]): + """Asyncio counterpart of TranscriptGrouper, sharing exactly the same policy. + + additional_acknowledgments extends the built-in phrases with the same + normalization and timing thresholds as TranscriptGrouper. + + Use async with, await push(event), and await close(). Register synchronous + or asynchronous callbacks with on/once. Handlers run sequentially on the + owning event loop; timer callbacks never run on a worker thread. Exceptions + from timer-triggered handlers go to the event loop's exception handler. + """ + + def __init__( + self, + *, + min_turn_separation_ms: float | None = None, + assistant_silence_ms: float | None = None, + backchannel_max_duration_ms: float | None = None, + backchannel_isolation_ms: float | None = None, + additional_acknowledgments: Sequence[str] | None = None, + ) -> None: + super().__init__( + _options( + min_turn_separation_ms, + assistant_silence_ms, + backchannel_max_duration_ms, + backchannel_isolation_ms, + additional_acknowledgments, + ) + ) + self._loop: asyncio.AbstractEventLoop | None = None + self._dispatch_run: _DispatchRun | None = None + + def _get_loop(self) -> asyncio.AbstractEventLoop: + loop = asyncio.get_running_loop() + if self._loop is None: + self._loop = loop + elif self._loop is not loop: + raise OpenAIError("The async transcript grouper must be used on its owning event loop") + return loop + + async def push(self, event: ServerEvent) -> None: + """Consume one event and dispatch its updates; await async handlers in order.""" + self._get_loop() + updates = self._state.push(event) + if updates is None: + return + self._updates.extend(updates) + self._schedule() + await self._dispatch() + + async def close(self) -> None: + """Finalize the projection and cancel timers. Idempotent; owns no transport.""" + self._get_loop() + self._updates.extend(self._state.finish("manual")) + self._clear_timer() + await self._dispatch() + + async def __aenter__(self) -> Self: + self._get_loop() + return self + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, traceback: TracebackType | None + ) -> None: + await self.close() + + @override + def _call_later(self, delay_ms: int, callback: Callable[[], None]) -> _Timer: + return self._get_loop().call_later(delay_ms / 1000, callback) + + @override + def _timer_fired(self, generation: int) -> None: + if generation != self._timer_generation or self._state.closed: + return + self._timer = None + self._updates.extend(self._state.tick()) + self._schedule() + self._start_dispatch() + + def _dispatch_finished(self, run: _DispatchRun, task: asyncio.Task[None]) -> None: + if self._dispatch_run is run: + self._dispatch_run = None + if task.cancelled(): + return + error = task.exception() + if error is not None and not run.waiters and not run.observed: + self._get_loop().call_exception_handler( + {"message": "Live transcript callback failed", "exception": error, "task": task} + ) + + def _start_dispatch(self) -> _DispatchRun | None: + run = self._dispatch_run + if run is not None and run.task is not None and not run.task.done(): + return run + if not self._updates: + return None + run = _DispatchRun() + self._dispatch_run = run + run.task = self._get_loop().create_task(self._dispatch_updates(run)) + run.task.add_done_callback(lambda task: self._dispatch_finished(run, task)) + return run + + async def _dispatch(self) -> None: + run = self._dispatch_run + if run is not None and run.task is asyncio.current_task(): + return + run = self._start_dispatch() + if run is None: + return + assert run.task is not None + run.waiters += 1 + try: + # A cancelled push/close caller must not interrupt an event halfway + # through its listeners, especially once session closure is committed. + await asyncio.shield(run.task) + except asyncio.CancelledError: + raise + except BaseException: + run.observed = True + raise + else: + run.observed = True + finally: + run.waiters -= 1 + + async def _dispatch_updates(self, run: _DispatchRun) -> None: + # Set ownership before handlers run, including with an eager task factory. + run.task = asyncio.current_task() + while self._updates: + update = self._updates.popleft() + for handler in self._handlers(update): + result = handler(update) + if inspect.isawaitable(result): + await result diff --git a/venv/lib/python3.12/site-packages/openai/lib/live/_transcript_grouping.py b/venv/lib/python3.12/site-packages/openai/lib/live/_transcript_grouping.py new file mode 100644 index 0000000..77bed0a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/live/_transcript_grouping.py @@ -0,0 +1,316 @@ +from __future__ import annotations + +import re +import unicodedata +from dataclasses import dataclass +from typing_extensions import Literal + +from ._types import ( + GroupingUpdate, + TranscriptSegment, + TranscriptFragment, + TranscriptGrouperOptions, + TranscriptSegmentClosedEvent, + TranscriptSegmentCloseReason, +) + +# Match ECMAScript \s, including BOM but excluding Python-only whitespace. +_WHITESPACE = "\u0009\u000a\u000b\u000c\u000d\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\ufeff" +_ACKNOWLEDGMENTS = ( + "aha", + "alright", + "gotcha", + "hm", + "hmm", + "mhm", + "mm", + "mm hmm", + "okay", + "ok", + "right", + "sure", + "uh huh", + "yeah", + "yep", + "yes", +) + + +def _normalize_acknowledgment(text: str) -> str: + text = text.lower().replace("-", " ").strip(_WHITESPACE + ".,!?;:\"'()[]{}") + return re.sub(f"[{_WHITESPACE}]+", " ", text) + + +def _letter_or_number(char: str) -> bool: + return unicodedata.category(char)[0] in ("L", "N") + + +@dataclass +class _Turn: + speaker: Literal["user", "assistant"] + text: str + start_ms: int + end_ms: int + id: str + previous_id: str | None = None + emitted: bool = False + can_drop_as_backchannel: bool = True + + +class TranscriptGrouping: + """Direct port of the TypeScript policy; no clocks, callbacks or transport.""" + + def __init__(self, options: TranscriptGrouperOptions, id_prefix: str) -> None: + self._options = options + self._id_prefix = id_prefix + self._acknowledgments = _ACKNOWLEDGMENTS + tuple( + ack for phrase in options.additional_acknowledgments if (ack := _normalize_acknowledgment(phrase)) + ) + self._current: _Turn | None = None + self._buffered: _Turn | None = None + self._last_id: str | None = None + self._next_id = 0 + self._last_assistant_end: int | None = None + + @property + def speaker(self) -> str | None: + return self._current.speaker if self._current is not None else None + + def process(self, fragments: list[TranscriptFragment]) -> list[GroupingUpdate]: + events: list[GroupingUpdate] = [] + preferred = self.speaker or "user" + ordered = [part for part in fragments if part.speaker == preferred] + [ + part for part in fragments if part.speaker != preferred + ] + if not ordered: + return events + first = ordered[0] + deadline = self.deadline() + while deadline is not None and deadline < first.start_ms: + events.extend(self.advance(deadline)) + deadline = self.deadline() + if self._current is not None and not any(part.speaker == self.speaker for part in ordered): + events.extend(self.advance(first.start_ms, any(part.speaker == "user" for part in ordered))) + for fragment in ordered: + events.extend(self._ingest(fragment)) + return events + + def advance(self, time_ms: float, has_incoming_user: bool = False) -> list[GroupingUpdate]: + # Use the same sums as deadline() so fractional deadlines remain reachable. + if ( + self._current is not None + and self._buffered is not None + and time_ms >= self._current.end_ms + self._options.min_turn_separation_ms + and not self._keep_backchannel(time_ms) + ): + self._buffered = self._maybe_drop_backchannel(time_ms) + if self._buffered is not None: + return self._promote() + if ( + self._current is not None + and self._current.speaker == "assistant" + and not has_incoming_user + and time_ms >= self._current.end_ms + self._options.assistant_silence_ms + ): + return self._finish_current("inactivity") + return [] + + def deadline(self) -> float | None: + if self._current is None: + return None + if self._buffered is not None: + separation = self._current.end_ms + self._options.min_turn_separation_ms + if ( + self._might_be_backchannel() + and self._buffered.can_drop_as_backchannel + and not self._user_continued() + and not self._recent_assistant() + ): + return max(separation, self._buffered.end_ms + self._options.backchannel_isolation_ms) + return separation + if self._current.speaker == "assistant": + return self._current.end_ms + self._options.assistant_silence_ms + return None + + def close(self, time_ms: float, reason: TranscriptSegmentCloseReason) -> list[GroupingUpdate]: + buffered = self._maybe_drop_backchannel(time_ms) + events = self._finish_current(reason) + self._buffered = None + if buffered is not None and buffered.text: + events.extend(self._emit(buffered)) + events.extend(self._finish(buffered, reason)) + if reason == "timestamp_reset": + self._last_assistant_end = None + return events + + def _ingest(self, fragment: TranscriptFragment) -> list[GroupingUpdate]: + if self._current is None: + self._current = self._new_turn(fragment) + return self._emit(self._current) + if fragment.speaker == self._current.speaker: + self._append(self._current, fragment, self._buffered is not None) + return self._emit(self._current) + if self._current.speaker == "user" and self._user_continued(): + self._buffered = None + separation = fragment.start_ms - self._current.end_ms + if self._current.speaker == "assistant": + self._buffer(fragment) + return self._promote() + if separation < self._options.min_turn_separation_ms: + self._buffer(fragment, self._possible_acknowledgment(fragment)) + return [] + if self._buffered is not None and self._standalone_acknowledgment(fragment, self._buffered): + self._buffer(fragment, True) + return [] + if self._buffered is None and self._standalone_acknowledgment(fragment): + self._buffer(fragment, False) + return [] + self._buffered = self._maybe_drop_backchannel(next_fragment=fragment) + events = self._finish_current("speaker_change") + if self._buffered is not None: + self._current = self._buffered + self._buffered = None + self._append(self._current, fragment) + else: + self._current = self._new_turn(fragment) + events.extend(self._emit(self._current)) + return events + + def _new_turn(self, fragment: TranscriptFragment) -> _Turn: + turn = _Turn( + speaker=fragment.speaker, + text=fragment.text, + start_ms=fragment.start_ms, + end_ms=fragment.end_ms, + id=f"{self._id_prefix}_{self._next_id}", + ) + self._next_id += 1 + return turn + + @staticmethod + def _append(turn: _Turn, fragment: TranscriptFragment, separate: bool = False) -> None: + separator = ( + " " + if separate + and turn.text + and fragment.text + and _letter_or_number(turn.text[-1]) + and _letter_or_number(fragment.text[0]) + else "" + ) + turn.text += separator + fragment.text + turn.end_ms = max(turn.end_ms, fragment.end_ms) + + def _buffer(self, fragment: TranscriptFragment, can_drop: bool | None = None) -> None: + if self._buffered is not None: + self._append(self._buffered, fragment) + else: + self._buffered = self._new_turn(fragment) + if can_drop is not None: + self._buffered.can_drop_as_backchannel = can_drop + + def _promote(self) -> list[GroupingUpdate]: + if self._buffered is None: + return [] + events = self._finish_current("speaker_change") + self._current = self._buffered + self._buffered = None + events.extend(self._emit(self._current)) + return events + + def _finish_current(self, reason: TranscriptSegmentCloseReason) -> list[GroupingUpdate]: + current, self._current = self._current, None + return self._finish(current, reason) if current is not None else [] + + def _finish(self, turn: _Turn, reason: TranscriptSegmentCloseReason) -> list[GroupingUpdate]: + if turn.speaker == "assistant": + self._last_assistant_end = turn.end_ms + return [TranscriptSegmentClosedEvent(self._snapshot(turn), reason)] if turn.emitted else [] + + def _emit(self, turn: _Turn) -> list[GroupingUpdate]: + if not turn.text: + return [] + if not turn.emitted: + turn.previous_id = self._last_id + self._last_id = turn.id + turn.emitted = True + return [self._snapshot(turn)] + + @staticmethod + def _snapshot(turn: _Turn) -> TranscriptSegment: + assert turn.speaker in ("user", "assistant") + return TranscriptSegment(turn.id, turn.previous_id, turn.speaker, turn.text, turn.start_ms, turn.end_ms) + + def _might_be_backchannel(self) -> bool: + return ( + self._current is not None + and self._current.speaker == "user" + and self._buffered is not None + and self._buffered.speaker == "assistant" + and self._buffered.end_ms - self._buffered.start_ms < self._options.backchannel_max_duration_ms + ) + + def _user_continued(self) -> bool: + return ( + self._might_be_backchannel() + and self._buffered is not None + and self._buffered.can_drop_as_backchannel + and self._current is not None + and self._current.end_ms > self._buffered.end_ms + ) + + def _recent_assistant(self) -> bool: + return ( + self._current is not None + and self._buffered is not None + and self._last_assistant_end is not None + and self._buffered.start_ms - self._last_assistant_end < self._options.backchannel_isolation_ms + and self._buffered.start_ms <= self._current.start_ms + ) + + def _keep_backchannel(self, time_ms: float) -> bool: + return ( + self._might_be_backchannel() + and self._buffered is not None + and self._buffered.can_drop_as_backchannel + and not self._user_continued() + and not self._recent_assistant() + and time_ms < self._buffered.end_ms + self._options.backchannel_isolation_ms + ) + + def _maybe_drop_backchannel( + self, time_ms: float | None = None, next_fragment: TranscriptFragment | None = None + ) -> _Turn | None: + if not self._might_be_backchannel() or self._buffered is None: + return self._buffered + if self._user_continued(): + return None + if self._recent_assistant(): + return self._buffered + if ( + next_fragment is not None + and next_fragment.start_ms - self._buffered.end_ms < self._options.backchannel_isolation_ms + ): + return self._buffered + if next_fragment is None and ( + time_ms is None or time_ms < self._buffered.end_ms + self._options.backchannel_isolation_ms + ): + return self._buffered + return None if self._buffered.can_drop_as_backchannel else self._buffered + + def _standalone_acknowledgment(self, fragment: TranscriptFragment, previous: _Turn | None = None) -> bool: + return ( + fragment.end_ms - (previous.start_ms if previous is not None else fragment.start_ms) + < self._options.backchannel_max_duration_ms + and _normalize_acknowledgment((previous.text if previous is not None else "") + fragment.text) + in self._acknowledgments + ) + + def _possible_acknowledgment(self, fragment: TranscriptFragment) -> bool: + text = _normalize_acknowledgment((self._buffered.text if self._buffered is not None else "") + fragment.text) + return ( + fragment.end_ms - (self._buffered.start_ms if self._buffered is not None else fragment.start_ms) + < self._options.backchannel_max_duration_ms + and bool(text) + and any(ack.startswith(text) for ack in self._acknowledgments) + ) diff --git a/venv/lib/python3.12/site-packages/openai/lib/live/_transcript_state.py b/venv/lib/python3.12/site-packages/openai/lib/live/_transcript_state.py new file mode 100644 index 0000000..e31f6c6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/live/_transcript_state.py @@ -0,0 +1,157 @@ +from __future__ import annotations + +import math +import itertools +from typing import Callable +from dataclasses import fields, dataclass +from typing_extensions import Literal, TypeGuard + +from ._types import GroupingUpdate, TranscriptFragment, TranscriptGrouperOptions +from ...types.live import ServerEvent +from ..._exceptions import OpenAIError +from ._transcript_grouping import TranscriptGrouping + +MAX_TIMEOUT_MS = 2_147_483_647 +_MAX_SAFE_INTEGER = 9_007_199_254_740_991 +_GROUPER_IDS = itertools.count() + + +def validated_options(options: TranscriptGrouperOptions) -> TranscriptGrouperOptions: + for field in fields(options): + if field.name == "additional_acknowledgments": + continue + value = getattr(options, field.name) + if isinstance(value, bool) or not isinstance(value, (int, float)) or not 0 <= value <= MAX_TIMEOUT_MS: + raise OpenAIError(f"{field.name} must be a finite number between 0 and {MAX_TIMEOUT_MS}") + return options + + +@dataclass(frozen=True) +class _ReceivedFragment: + fragment: TranscriptFragment + received_at: float + + +class TranscriptState: + """Clock-driven state shared by the sync and async adapters; callers serialize access.""" + + def __init__(self, options: TranscriptGrouperOptions, now: Callable[[], float]) -> None: + self._now = now + self._grouping = TranscriptGrouping(validated_options(options), f"segment_{next(_GROUPER_IDS)}") + self._seen_ids: set[str] = set() + self._pending: list[_ReceivedFragment] = [] + self._anchor: tuple[int, float] | None = None + self._last_start_ms: int | None = None + self.closed = False + + def push(self, event: ServerEvent) -> list[GroupingUpdate] | None: + if self.closed: + raise OpenAIError("Cannot push events after closing the transcript grouper") + if event.type == "session.closed": + return self.finish("session_closed") + if event.type not in ("session.input_transcript.delta", "session.output_transcript.delta"): + return None + # SDK events may be constructed without validation. Check only consumed + # variants and copy their primitive fields before updating any state. + event_id = getattr(event, "event_id", None) + text = getattr(event, "delta", None) + start_ms = getattr(event, "start_ms", None) + end_ms = getattr(event, "end_ms", None) + speaker: Literal["user", "assistant"] = ( + "user" if event.type == "session.input_transcript.delta" else "assistant" + ) + if ( + not isinstance(event_id, str) + or not event_id + or not isinstance(text, str) + or not self._valid_timestamp(start_ms) + or not self._valid_timestamp(end_ms) + or end_ms < start_ms + ): + raise OpenAIError( + "Invalid public Live transcript delta: expected an ID, text, and a nonnegative timed interval" + ) + if event_id in self._seen_ids: + return None + self._seen_ids.add(event_id) + if not text: + return None + fragment = TranscriptFragment(speaker, text, int(start_ms), int(end_ms)) + received = _ReceivedFragment(fragment, self._now()) + updates: list[GroupingUpdate] = [] + pending = self._pending[0].fragment if self._pending else None + if pending is not None and pending.start_ms == start_ms and pending.end_ms == end_ms: + self._pending.append(received) + if any(part.fragment.speaker != speaker for part in self._pending): + updates.extend(self._flush_pending()) + else: + updates.extend(self._flush_pending()) + if self._grouping.speaker == speaker: + updates.extend(self._commit([received])) + else: + self._pending.append(received) + return updates + + @staticmethod + def _valid_timestamp(value: object) -> TypeGuard[int | float]: + # JavaScript's Number.isSafeInteger also accepts 1.0. Preserve that + # behavior for unchecked models, but never treat Python bool as a time. + return ( + not isinstance(value, bool) + and isinstance(value, (int, float)) + and 0 <= value <= _MAX_SAFE_INTEGER + and int(value) == value + ) + + def finish(self, reason: Literal["manual", "session_closed"]) -> list[GroupingUpdate]: + if self.closed: + return [] + assert reason in ("manual", "session_closed") + self.closed = True + updates = self._flush_pending() + updates.extend(self._grouping.close(self._source_now(), reason)) + self._seen_ids.clear() + return updates + + def tick(self) -> list[GroupingUpdate]: + updates = self._flush_pending() + updates.extend(self._grouping.advance(self._source_now())) + return updates + + def delay(self) -> int | None: + if self.closed: + return None + deadline = self._grouping.deadline() + if self._pending: + delay = self._pending[0].received_at + 50 - self._now() + elif deadline is not None: + delay = deadline - self._source_now() + else: + return None + return min(MAX_TIMEOUT_MS, max(0, math.ceil(delay))) + + def _source_now(self) -> float: + if self._anchor is None: + return 0 + source_ms, received_at = self._anchor + return source_ms + max(0, self._now() - received_at) + + def _flush_pending(self) -> list[GroupingUpdate]: + pending, self._pending = self._pending, [] + return self._commit(pending) + + def _commit(self, fragments: list[_ReceivedFragment]) -> list[GroupingUpdate]: + if not fragments: + return [] + first = fragments[0].fragment + updates: list[GroupingUpdate] = [] + if self._last_start_ms is not None and first.start_ms < self._last_start_ms: + updates.extend(self._grouping.close(self._source_now(), "timestamp_reset")) + self._anchor = None + self._last_start_ms = first.start_ms + self._anchor = ( + max(self._anchor[0] if self._anchor is not None else 0, *(part.fragment.end_ms for part in fragments)), + max(part.received_at for part in fragments), + ) + updates.extend(self._grouping.process([part.fragment for part in fragments])) + return updates diff --git a/venv/lib/python3.12/site-packages/openai/lib/live/_types.py b/venv/lib/python3.12/site-packages/openai/lib/live/_types.py new file mode 100644 index 0000000..4025d21 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/live/_types.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing_extensions import Literal + +TranscriptSegmentCloseReason = Literal["speaker_change", "inactivity", "timestamp_reset", "session_closed", "manual"] + + +@dataclass(frozen=True) +class TranscriptSegment: + """An immutable display snapshot, not a server conversation item or turn. + + Replace the displayed text on each update. IDs are local to this projection; + start/end times describe public transcript intervals, not audio playback. + """ + + id: str + previous_id: str | None + speaker: Literal["user", "assistant"] + text: str + start_ms: int + end_ms: int + + +@dataclass(frozen=True) +class TranscriptSegmentClosedEvent: + """The final segment snapshot and the local reason it was closed.""" + + segment: TranscriptSegment + reason: TranscriptSegmentCloseReason + + +@dataclass(frozen=True) +class TranscriptGrouperOptions: + """Resolved options for the grouping policy; timing thresholds are in milliseconds.""" + + min_turn_separation_ms: float = 500 + assistant_silence_ms: float = 2000 + backchannel_max_duration_ms: float = 1000 + backchannel_isolation_ms: float = 2000 + additional_acknowledgments: tuple[str, ...] = () + + +@dataclass(frozen=True) +class TranscriptFragment: + speaker: Literal["user", "assistant"] + text: str + start_ms: int + end_ms: int + + +GroupingUpdate = TranscriptSegment | TranscriptSegmentClosedEvent diff --git a/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/README.md b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/README.md new file mode 100644 index 0000000..ef04678 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/README.md @@ -0,0 +1,276 @@ +# Responses WebSocket sessions + +`ResponsesWebSocketSession` and `AsyncResponsesWebSocketSession` add routed +lanes and final-response collection to an existing `client.responses.connect()` +connection. The existing connection API and its defaults are unchanged. Install +the `openai[realtime]` extra to use WebSockets. + +```python +from openai import AsyncOpenAI +from openai.lib.responses_websocket import ( + AsyncResponsesWebSocketSession, + ResponsesWebSocketLimits, +) + +# Choose budgets for your application's expected responses. +limits = ResponsesWebSocketLimits( + max_lanes=8, # Includes the default lane and closed reservations until reconnect. + max_events_per_lane=128, + max_events=512, + max_bytes_per_lane=16 * 1024 * 1024, + max_bytes=32 * 1024 * 1024, + max_response_bytes=None, # Default: no cumulative response accumulation cap. +) + +async def run(): + async with AsyncOpenAI() as client: + async with client.responses.connect(websocket_connection_options={"max_size": None}) as connection: + async with AsyncResponsesWebSocketSession(connection, limits=limits) as session: + lane = session.lane("conversation") + await lane.send({ + "type": "response.create", + "model": "gpt-4o-mini", + "input": "Say hello.", + }) + response = await lane.get_final_response() + return response +``` + +The session owns the connection's read side and lifetime. Its one reader uses the +connection's raw receive, event parsing, and recovery methods. While the session +is active, receive through its lanes instead of calling the connection's `recv`, +iterator, or `dispatch_events`. +Direct sends through the connection can bypass lane ownership; send through the +lane when using these helpers. Closing the session closes the connection. + +Register each named lane before sending. For `response.create`, `lane.send` adds +its `stream_id` and rejects conflicting routing metadata. Steering commands keep +their original fields and route through `previous_response_id`. The lane ID is independent of +`previous_response_id`, which you can supply explicitly for continuation. Consume +one response's terminal event before sending the next create on that lane. +Different lanes can run concurrently; use one consumer per lane. The default +lane (`session.default`) receives events without a registered routing ID, +including unknown events and connection-level errors. Observe it when using +named lanes so these events and errors are not left unread. + +`lane.recv()` preserves typed events and unknown fields. `get_final_response()` +consumes remaining events and returns the terminal `Response`, including `failed` +and `incomplete` results. Finalized output items fill an omitted terminal output +through the existing Responses accumulator. Text deltas remain available through +`recv`; final response collection does not require SSE-only item setup events. +No tool is executed automatically. A nested protocol error raises +`ResponsesWebSocketError`, whose `event` is the original event; the exception +message does not include request or response data. + +A repeated `get_final_response()` returns the cached result unless the reader +has observed a newer `response.created` on that lane, in which case it consumes +the next response. It does not wait for a hypothetical future successor. Use +`recv()` to observe that boundary when coordinating steering; accepted steering +alone does not prove that a successor has started. + +For incremental text and tool-input snapshots, opt in with a separate helper +fed by the events you already receive: + +```python +from openai.lib.responses_websocket import ResponsesWebSocketAccumulator + +accumulator = ResponsesWebSocketAccumulator() +expected_stream_id = "conversation" # Same ID passed to session.lane(); None for session.default. +while True: + event = await lane.recv() # Use lane.recv(timeout=...) in a sync session. + # Original event fields, including unknown variants/fields, remain available. + # Inspect events in memory before filtering. Never log raw events: they can + # contain customer data, tool arguments, credentials, or error details. + if getattr(event, "stream_id", None) != expected_stream_id: + continue + accumulator.add_event(event) + event_type = event.type + if event.type == "response.output_text.delta": + pass # Update the application's UI with event.delta (provisional). + elif event.type == "response.output_item.done": + pass # Replace the provisional item at event.output_index with event.item. + elif isinstance(event_type, str) and event_type in {"response.completed", "response.failed", "response.incomplete"}: + snapshot = accumulator.snapshot() # One full projection of all items. + response = accumulator.get_final_response() + break +accumulator.reset() # Does not close the lane or connection. +``` + +Use one helper per lane and reset it before the next turn. The default lane +also receives raw events for unregistered or detached lanes. Observe those +events before filtering, then feed only the response you are collecting. +The same check applies when consuming directly from a connection. + +Snapshots are immutable and contain selected text, function arguments, and custom tool input, +grouped by output/content index and item ID. They never execute tools. Done +events replace provisional fields. Full item replacements discard old fields; +a changed nonempty item ID starts fresh at its index. A supplied response +output list overrides the projected items, including an explicit empty list; +omitted or null output retains only the helper's earlier projections. + +`detailed_snapshot()` returns a separate mutable view when you need the fields +beyond selected text and tool inputs. It contains `stream_id`, `response_id`, +`terminal_type`, `response` and `output`. `response` is the last observed +lifecycle response metadata (excluding `output`), or `None` if none arrived. +`output` is a list of `{"output_index": index, "item": metadata, "content": rows}`. +Each content row is `{"content_index": index, "part": observed_fields}`. The +part's `annotations`, when present as a list, use +`{"annotation_index": index, "annotation": observed_fields}` rows. Indices may +be sparse; list position is not the API index. +For a message that has no projected content, the row's `content` is omitted, +null or empty according to what was actually received. + +For example, after collecting an item or terminal as above: + +```python +details = accumulator.detailed_snapshot() +for output in details["output"]: + for content in output.get("content") or []: + part = content["part"] + # Fields exist only if received: a WS delta may have no part/item type. + text = part.get("text") + citations = part.get("annotations") + token_scores = part.get("logprobs") +``` + +Logprobs accumulate with text deltas and a supplied `output_text.done.logprobs` +replaces them, including empty or null values. Content/item/lifecycle replacements +also replace their corresponding annotations and other metadata; text-only done +events do not erase citations. Refusal, tool/MCP and unknown item/part fields are +retained as observed, with unset and null distinct. Unknown standalone events still +pass through unchanged and are not accumulated. Annotation events enrich matching +known items on the accumulator's lane; annotations received before any matching +item/text remain available in the original event and do not start or replace a turn. +A partial field or unknown type +is provisional, not a fabricated validated response or a successful tool result. +You may mutate this returned view without changing the accumulator, events, or +earlier snapshots. The original `snapshot()` remains immutable and hashable. + +`snapshot()` materializes the entire current projection and joins retained +fragments. `detailed_snapshot()` also materializes and copies its whole projection. +Both are proportional to the accumulated output, so requesting either after +every delta or completed item repeatedly rebuilds growing prefixes. Use the +original event for progress, including the final item itself on +`response.output_item.done`. Read the full snapshot at a terminal or on explicit +user demand. A per-delta progress display is provisional; done events can shorten or +correct earlier text. A UI can use the received item to replace the item at +`event.output_index`, and use the terminal snapshot for the whole projection. + +The helper's `get_final_response()` returns a copy of the exact received server +response, including its original missing/null/empty output, on completed, +failed or incomplete. It never substitutes the projection for that response. +Before a valid terminal it raises; protocol errors retain their original event. +This caller-fed helper owns no socket, reader or timers and can also observe +events from a connection when there is no session. + +Cancel an async `recv` or `get_final_response` wait with normal asyncio +cancellation. It leaves queued events and accumulated state available for a later +wait, and does not close the lane or connection. Synchronous waits accept +`timeout=` and likewise preserve data on timeout. `lane.close()` explicitly +detaches that lane and discards its queued events/state without closing the +socket. Future events for a detached named lane go to the default lane for raw +inspection; they do not change its accumulated response or in-flight state. Closing +the default lane opts out of those otherwise unclaimed events. +Every registered ID, including the default lane and unused or closed lanes, +remains reserved against `max_lanes` until the physical socket reconnects. +Terminal responses and errors do not release IDs: accepted steering can start a +successor after a terminal event. Keep the same lane handle open for sequential +responses. Reconnect releases closed reservations; open lanes stay registered on +the replacement socket. Close a lane before reconnecting if you need to recreate +it. After reconnect releases a closed default lane, `session.lane()` (or +`session.lane(None)`) registers a fresh default and updates `session.default`. + +Limits apply only to this opt-in session. Event queues are bounded per lane and +across the session; overflow reports `ResponsesWebSocketBufferError` and closes +the owned connection so ordered events are not silently dropped. Queue byte +counts measure received message bytes before parsing, not Python object overhead or +transport decoding memory. Response accumulation is uncapped by default: +omit `max_response_bytes` or set it to `None`. A positive integer opts into a +budget for the cumulative compatible event bytes passed to an accumulator; +exceeding it disables that response's collection, while raw events and other +lanes remain usable. Queue and lane limits still require positive integers. +Choose budgets for large legitimate outputs. These are application budgets, +not protocol limits. +The examples disable the transport's separate 1 MiB message limit with +`websocket_connection_options={"max_size": None}` so large events can reach the +session. If you set a transport limit, choose it separately for the largest +expected individual event; raising session budgets does not raise that limit. + +The connection's existing opt-in reconnection policy still applies. Reconnecting +does not restore server-side response state. Lane sends reject while reconnecting, +before starting a response, so wait for recovery and restore application state. +Lane sends do not enqueue a +delivery-uncertain write for replay; handle that failure explicitly in your +application. A request whose delivery is uncertain remains in flight until its +terminal event arrives or you detach its lane. When recovery loses that state, +use a fresh ID, or close the lane and reconnect before recreating it, then +explicitly restore the application state. +Existing low-level connection sends retain their existing semantics. + +## Continuation and service state + +The [WebSocket mode guide](https://developers.openai.com/api/docs/guides/websocket-mode) +defines the server contract. Every new turn sends `response.create`; omit the +HTTP-only `stream` and `background` fields. + +- **Warmup:** construct a `ResponseCreate` event from + `openai.types.responses.responses_client_event` with `generate=False`, using + its existing extra-field support. Send it through `lane.send()` and collect + its terminal response with `lane.get_final_response()` (await both in async + code). Use that response's ID as `previous_response_id` on a later create + with new input. Warmup prepares state without generating model output. +- **Tools:** after the response completes, execute the application's tool and + send its `function_call_output` with the original `call_id`, the completed + response's ID, and only new input items. See + [the tool-turn example](../../../../examples/responses/websocket.py). +- **Forks:** send the source response's ID on a different lane. With + `store=False` or ZDR, wait for the fork lane's `response.in_progress` before + advancing the source lane. Consume lane events with `recv()` to observe this + barrier, handling errors and premature close while waiting. +- **Automatic compaction:** with `context_management` configured for compaction, + continue using the latest response ID and only new input items. +- **Standalone compaction:** `client.responses.compact(...)` returns a compacted + input window, not a response ID for continuation. Send its complete `output` + as input to a new WebSocket chain, omitting or nulling `previous_response_id`. + Do not prune items from the compacted output. + +The server keeps recent response state in a connection-local cache. With +`store=True`, an older response may be loaded from persisted state. With +`store=False` or ZDR, an uncached ID produces `previous_response_not_found`. +A same-lane continuation returning a 4xx or 5xx evicts its referenced cached +parent; an errored cross-lane fork preserves the shared parent for the source +lane. Do not blindly retry the same parent after a cache miss. Restore the full +retained input and output history as a new chain when needed, including tool +and reasoning items rather than only displayed text. Request +`reasoning.encrypted_content` when retaining reasoning-model output for replay. + +## Lanes, limits and connection lifetime + +Named stream IDs contain 1–256 ASCII letters, digits, underscores, hyphens or periods; +the helper validates this exact string before reserving an ID. Other types and an +empty string are invalid. Omit the ID for the default lane. Reusing an open lane does +not select conversation history: supply `previous_response_id` explicitly. +The server executes requests on each lane FIFO without overlap, and may +interleave events from different lanes. Named-lane terminals and request errors +echo the ID; default-lane events omit it. + +The service allows 16 active responses and queues additional creates. It accepts +32 distinct named IDs per connection; the default lane does not count. These +are separate from the helper's application budgets. Detaching a local lane does +not reset the server's distinct-ID count. + +Connections last up to 60 minutes. Plan rotation at completed-turn boundaries. +Reconnecting loses every lane's connection-local cache: use persisted IDs when +available or restore full context explicitly. Handle `previous_response_not_found`, +`invalid_stream_id`, `websocket_stream_limit_reached`, and +`websocket_connection_limit_reached` through the original error event. Observe +the default lane for connection-scoped errors as well as each named lane. + +Steering is a separate command: send only `type`, `previous_response_id` and +`input`, without `stream_id`. Accepted steering is not yet committed; the +successor's `response.created` is the commit point. If `response.steer.pending` +requires tool output or approval, fill its `required_input` stubs and send one +create on the parent's lane with that parent ID. Reuse saved results rather +than rerunning tools or resending already accepted steering input. + +For the synchronous form, see `examples/responses_websocket.py`. diff --git a/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/__init__.py b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/__init__.py new file mode 100644 index 0000000..9b89a90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/__init__.py @@ -0,0 +1,14 @@ +from ._session import ( + ResponsesWebSocketLane as ResponsesWebSocketLane, + ResponsesWebSocketError as ResponsesWebSocketError, + ResponsesWebSocketLimits as ResponsesWebSocketLimits, + ResponsesWebSocketSession as ResponsesWebSocketSession, + AsyncResponsesWebSocketLane as AsyncResponsesWebSocketLane, + ResponsesWebSocketBufferError as ResponsesWebSocketBufferError, + AsyncResponsesWebSocketSession as AsyncResponsesWebSocketSession, +) +from ._accumulator import ( + ResponsesWebSocketOutput as ResponsesWebSocketOutput, + ResponsesWebSocketSnapshot as ResponsesWebSocketSnapshot, + ResponsesWebSocketAccumulator as ResponsesWebSocketAccumulator, +) diff --git a/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e24e9b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/__pycache__/_accumulator.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/__pycache__/_accumulator.cpython-312.pyc new file mode 100644 index 0000000..41b09b3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/__pycache__/_accumulator.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/__pycache__/_session.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/__pycache__/_session.cpython-312.pyc new file mode 100644 index 0000000..de17dd3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/__pycache__/_session.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/_accumulator.py b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/_accumulator.py new file mode 100644 index 0000000..cba9f5f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/_accumulator.py @@ -0,0 +1,445 @@ +from __future__ import annotations + +from copy import deepcopy +from typing import Any, cast +from dataclasses import field, dataclass + +from ._session import ResponsesWebSocketError, _field +from ..._compat import PYDANTIC_V1, model_copy +from ..._models import BaseModel +from ...types.responses import Response +from ...types.responses.responses_server_event import ResponsesServerEvent + + +@dataclass(frozen=True) +class ResponsesWebSocketOutput: + """Selected fields of one output item. Text pairs contain (content_index, text).""" + + output_index: int + item_id: str | None + type: str | None + name: str | None + call_id: str | None + arguments: str + input: str + text: tuple[tuple[int, str], ...] + + +@dataclass(frozen=True) +class ResponsesWebSocketSnapshot: + """Immutable partial projection; terminal_type is None until a valid terminal arrives.""" + + stream_id: str | None + response_id: str | None + terminal_type: str | None + output: tuple[ResponsesWebSocketOutput, ...] + + @property + def output_text(self) -> str: + return "".join(text for item in self.output for _, text in item.text) + + +@dataclass +class _Output: + item_id: str | None + retired_ids: set[str] = field(default_factory=set[str]) + type: str | None = None + name: str | None = None + call_id: str | None = None + arguments: list[str] = field(default_factory=list[str]) + input: list[str] = field(default_factory=list[str]) + text: dict[str, list[str]] = field(default_factory=dict[str, list[str]]) + data: dict[str, object] = field(default_factory=dict[str, object]) + content: dict[str, dict[str, object]] = field(default_factory=dict[str, dict[str, object]]) + annotations: dict[str, dict[str, object]] = field(default_factory=dict[str, dict[str, object]]) + + +class ResponsesWebSocketAccumulator: + """Opt-in, caller-fed collection of text, function/MCP arguments and custom tool input. + + Feed typed events from a connection or lane's recv(). Use one accumulator per + lane and call reset() before another turn. The helper never reads, sends, + closes the connection, or executes tools. It is not safe for concurrent use. + Snapshot fields are projections; get_final_response() retains the exact + server response, including failed/incomplete and missing/null/empty output. + """ + + def __init__(self) -> None: + self._stream_id: str | None = None + self._response_id: str | None = None + self._terminal_type: str | None = None + self._bound = False + # String keys get Python's randomized hash. Hex preserves arbitrary-size + # non-negative indices without decimal string conversion limits. + self._output: dict[str, _Output] = {} + self._response: dict[str, object] | None = None + self._final: Response | None = None + self._error: Exception | None = None + + def reset(self) -> None: + """Release this helper's state only; prior snapshots and the socket remain usable.""" + self._stream_id = self._response_id = self._terminal_type = None + self._bound = False + self._output.clear() + self._response = None + self._final = self._error = None + + def snapshot(self) -> ResponsesWebSocketSnapshot: + """Materialize the entire current immutable projection. + + This joins retained fragments. Use original events for delta and item + progress; read a full snapshot at a terminal or on explicit user demand. + """ + return ResponsesWebSocketSnapshot( + stream_id=self._stream_id, + response_id=self._response_id, + terminal_type=self._terminal_type, + output=tuple( + ResponsesWebSocketOutput( + output_index=int(index, 16), + item_id=item.item_id, + type=item.type, + name=item.name, + call_id=item.call_id, + arguments="".join(item.arguments), + input="".join(item.input), + text=tuple( + (int(pos, 16), "".join(parts)) + for pos, parts in sorted(item.text.items(), key=lambda pair: int(pair[0], 16)) + ), + ) + for index, item in sorted(self._output.items(), key=lambda pair: int(pair[0], 16)) + ), + ) + + def detailed_snapshot(self) -> dict[str, Any]: + """Return an independent mutable projection of observed response, item and part data. + + Output and content are lists of indexed rows, even when wire indices are + sparse. Part annotations use the same indexed-row form. Unset and null + fields stay distinct; missing scaffolding never invents a Response or + an item/part type. Cost is proportional to the full accumulated data. + """ + output: list[dict[str, object]] = [] + for index, item in sorted(self._output.items(), key=lambda pair: int(pair[0], 16)): + data = deepcopy(item.data) + if item.item_id: + data["id"] = item.item_id + if item.arguments: + data["arguments"] = "".join(item.arguments) + if item.input: + data["input"] = "".join(item.input) + content: list[dict[str, object]] = [] + for pos in sorted( + item.content.keys() | item.text.keys() | item.annotations.keys(), key=lambda k: int(k, 16) + ): + part = deepcopy(item.content.get(pos, {})) + if pos in item.text: + part["text"] = "".join(item.text[pos]) + original = part.get("annotations") + if isinstance(original, list) or pos in item.annotations: + annotations = ( + {hex(i): value for i, value in enumerate(cast("list[object]", original))} + if isinstance(original, list) + else {} + ) + annotations.update(deepcopy(item.annotations.get(pos, {}))) + part["annotations"] = [ + {"annotation_index": int(i, 16), "annotation": annotation} + for i, annotation in sorted(annotations.items(), key=lambda pair: int(pair[0], 16)) + ] + content.append({"content_index": int(pos, 16), "part": part}) + row: dict[str, object] = {"output_index": int(index, 16), "item": data} + if item.type == "message": + # Message content is extracted into indexed rows. Retain the + # presence marker until a part/delta supplies projected content. + if "content" in data or content: + original_content = data.pop("content", None) + row["content"] = content if content or isinstance(original_content, list) else None + else: + row["content"] = content + output.append(row) + return { + "stream_id": self._stream_id, + "response_id": self._response_id, + "terminal_type": self._terminal_type, + "response": deepcopy(self._response), + "output": output, + } + + def get_final_response(self) -> Response: + """Return an independent copy of the received terminal response, never a partial success.""" + error = self._error + if error is not None: + raise error + if self._final is None: + raise RuntimeError("No terminal response has been received") + return model_copy(self._final, deep=True) + + def add_event(self, event: ResponsesServerEvent) -> None: + """Observe an event without consuming or changing it; unknown events are ignored. + + Final fields replace deltas. A supplied output list, even [], replaces + the projection; null or omitted output retains prior projected fields. + Original event fields remain accessible to the caller. + """ + kind = _field(event, "type") + if not isinstance(kind, str) or kind not in { + "response.created", + "response.in_progress", + "response.completed", + "response.failed", + "response.incomplete", + "response.output_item.added", + "response.output_item.done", + "response.content_part.added", + "response.content_part.done", + "response.output_text.delta", + "response.output_text.done", + "response.output_text.annotation.added", + "response.function_call_arguments.delta", + "response.function_call_arguments.done", + "response.mcp_call_arguments.delta", + "response.mcp_call_arguments.done", + "response.custom_tool_call_input.delta", + "response.custom_tool_call_input.done", + "error", + }: + return + stream_id = _field(event, "stream_id") + # Annotations were historically ignored. They may enrich the matching + # known item, but never bind a lane/turn, replace or retire an item, or + # change the errors observed by existing accumulator callers. + if kind == "response.output_text.annotation.added": + output_pos = _field(event, "output_index") + content_pos = _field(event, "content_index") + annotation_pos = _field(event, "annotation_index") + if ( + self._bound + and self._stream_id == stream_id + and self._terminal_type is None + and self._error is None + and all( + isinstance(value, int) and not isinstance(value, bool) and value >= 0 + for value in (output_pos, content_pos, annotation_pos) + ) + ): + existing = self._output.get(hex(output_pos)) + if existing is not None and existing.item_id == _field(event, "item_id"): + value_data = _data(event, include={"annotation"}) + if "annotation" in value_data: + existing.annotations.setdefault(hex(content_pos), {})[hex(annotation_pos)] = value_data[ + "annotation" + ] + return + if stream_id is not None and not isinstance(stream_id, str): + raise ValueError("WebSocket stream_id must be a string or null") + if self._bound and self._stream_id != stream_id: + raise ValueError("Event belongs to another WebSocket lane") + if self._terminal_type is not None or self._error is not None: + raise RuntimeError("Reset the accumulator before adding another turn") + if kind == "error": + protocol_error = ResponsesWebSocketError(event) + self._error = protocol_error + raise protocol_error + terminal = kind in {"response.completed", "response.failed", "response.incomplete"} + if kind in {"response.created", "response.in_progress"} or terminal: + response = _field(event, "response") + if not isinstance(response, Response): + error = ValueError("WebSocket event is missing a valid response") + if terminal: + self._error = error + raise error + response_id = _field(response, "id") + if response_id is not None and not isinstance(response_id, str): + error = ValueError("WebSocket response id must be a string or null") + if terminal: + self._error = error + raise error + if self._response_id is not None and response_id is not None and self._response_id != response_id: + raise ValueError("Event belongs to another response") + output = _field(response, "output") + if output is not None and not isinstance(output, list): + error = ValueError("WebSocket response output must be a list or null") + if terminal: + self._error = error + raise error + if isinstance(output, list): + replacement: dict[str, _Output] = {} + try: + for index, item in enumerate(cast("list[object]", output)): + self._add_item(replacement, index, item) + except ValueError as error: + if terminal: + self._error = error + raise + self._output = replacement + self._response = _data(response, exclude={"output"}) + self._response_id = response_id or self._response_id + self._bound, self._stream_id = True, stream_id + if terminal: + self._final = model_copy(response, deep=True) + self._terminal_type = kind + return + index = _field(event, "output_index") + if not isinstance(index, int) or isinstance(index, bool) or index < 0: + raise ValueError("WebSocket output_index must be a non-negative integer") + if kind in {"response.output_item.added", "response.output_item.done"}: + self._add_item(self._output, index, _field(event, "item")) + self._bound, self._stream_id = True, stream_id + return + # Validate consumed values before replacing an item's retained state. + # The wire decoder preserves known events even when fields are malformed. + value = "" + if kind in { + "response.output_text.delta", + "response.function_call_arguments.delta", + "response.mcp_call_arguments.delta", + "response.custom_tool_call_input.delta", + }: + value = _text_field(event, "delta") + elif kind == "response.output_text.done": + value = _text_field(event, "text") + elif kind in {"response.function_call_arguments.done", "response.mcp_call_arguments.done"}: + value = _text_field(event, "arguments") + elif kind == "response.custom_tool_call_input.done": + value = _text_field(event, "input") + elif kind in {"response.content_part.added", "response.content_part.done"}: + part = _field(event, "part") + if _text_field(part, "type") == "output_text": + value = _text_field(part, "text") + pos = _field(event, "content_index") + if kind in { + "response.content_part.added", + "response.content_part.done", + "response.output_text.delta", + "response.output_text.done", + } and (not isinstance(pos, int) or isinstance(pos, bool) or pos < 0): + raise ValueError("WebSocket content_index must be a non-negative integer") + item_id = _text_field(event, "item_id") + self._bound, self._stream_id = True, stream_id + key = hex(index) + item = self._output.get(key) + if item is not None and item_id in item.retired_ids: + return + if item is None: + item = _Output(item_id=item_id) + self._output[key] = item + elif item_id and item.item_id and item_id != item.item_id: + item.retired_ids.add(item.item_id) + item = _Output(item_id=item_id, retired_ids=item.retired_ids) + self._output[key] = item + if item_id: + item.item_id = item_id + if kind in { + "response.content_part.added", + "response.content_part.done", + "response.output_text.delta", + "response.output_text.done", + }: + position = hex(pos) + if kind == "response.output_text.delta": + item.text.setdefault(position, []).append(value) + data = item.content.setdefault(position, {}) + prob_data = _data(event, include={"logprobs"}) + previous = data.get("logprobs") + incoming = prob_data.get("logprobs") + if isinstance(previous, list) and isinstance(incoming, list): + cast("list[object]", previous).extend(cast("list[object]", incoming)) + else: + data.update(prob_data) + elif kind == "response.output_text.done": + item.text[position] = [value] + item.content.setdefault(position, {}).update(_data(event, include={"logprobs"})) + else: + part = _field(event, "part") + item.content[position] = _data(part) + item.annotations.pop(position, None) + if _field(part, "type") == "output_text": + item.text[position] = [value] + else: + item.text.pop(position, None) + elif kind in {"response.function_call_arguments.delta", "response.mcp_call_arguments.delta"}: + item.arguments.append(value) + elif kind in {"response.function_call_arguments.done", "response.mcp_call_arguments.done"}: + item.arguments = [value] + elif kind == "response.custom_tool_call_input.delta": + item.input.append(value) + elif kind == "response.custom_tool_call_input.done": + item.input = [value] + + @staticmethod + def _add_item(output: dict[str, _Output], index: int, source: object) -> None: + if source is None: + return + item = _Output( + item_id=_optional_text_field(source, "id"), + type=_text_field(source, "type"), + name=_optional_text_field(source, "name"), + call_id=_optional_text_field(source, "call_id"), + data=_data(source, exclude={"content"} if _field(source, "type") == "message" else None), + ) + if item.type == "message": + content = _field(source, "content") + if content is not None: + if not isinstance(content, list): + raise ValueError("WebSocket message content must be a list or null") + # Marker only; keep actual parts indexed once, never copied twice. + item.data["content"] = [] + for pos, part in enumerate(cast("list[object]", content)): + if part is not None: + item.content[hex(pos)] = _data(part) + if part is not None and _text_field(part, "type") == "output_text": + text = _optional_text_field(part, "text") + if text is not None: + item.text[hex(pos)] = [text] + else: + item.data.update(_data(source, include={"content"})) + elif item.type in {"function_call", "mcp_call", "mcp_approval_request"}: + arguments = _optional_text_field(source, "arguments") + if arguments is not None: + item.arguments = [arguments] + elif item.type == "custom_tool_call": + input_text = _optional_text_field(source, "input") + if input_text is not None: + item.input = [input_text] + key = hex(index) + previous = output.get(key) + if previous is not None: + if item.item_id and item.item_id in previous.retired_ids: + return + item.retired_ids = previous.retired_ids + if previous.item_id and item.item_id and previous.item_id != item.item_id: + item.retired_ids.add(previous.item_id) + output[key] = item + + +def _data(source: object, *, include: set[str] | None = None, exclude: set[str] | None = None) -> dict[str, object]: + if isinstance(source, BaseModel): + return deepcopy( + source.model_dump( + mode="python", by_alias=True, exclude_unset=True, include=include, exclude=exclude, warnings=PYDANTIC_V1 + ) + ) + if isinstance(source, dict): + return deepcopy( + { + key: value + for key, value in cast("dict[str, object]", source).items() + if (include is None or key in include) and (exclude is None or key not in exclude) + } + ) + return {} + + +def _text_field(value: object, name: str) -> str: + text = _field(value, name) + if not isinstance(text, str): + raise ValueError(f"WebSocket output {name} must be a string") + return text + + +def _optional_text_field(value: object, name: str) -> str | None: + if _field(value, name) is None: + return None + return _text_field(value, name) diff --git a/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/_session.py b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/_session.py new file mode 100644 index 0000000..3b8bac9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/responses_websocket/_session.py @@ -0,0 +1,619 @@ +from __future__ import annotations + +import re +import json +import time +import asyncio +import threading +from types import TracebackType +from typing import Any, cast +from weakref import WeakSet +from collections import deque +from dataclasses import dataclass +from typing_extensions import Self + +from ..._types import omit +from ..._utils import maybe_transform +from ..._compat import model_copy +from ..._models import BaseModel +from ..._exceptions import WebSocketConnectionClosedError +from ...types.responses import Response, ResponseStreamEvent +from ..streaming.responses import ResponseStreamState +from ...resources.responses.responses import ResponsesConnection, AsyncResponsesConnection +from ...types.responses.responses_client_event import ResponsesClientEvent +from ...types.responses.responses_server_event import ResponsesServerEvent +from ...types.responses.responses_client_event_param import ResponsesClientEventParam + + +@dataclass(frozen=True) +class ResponsesWebSocketLimits: + """Application-selected limits for the opt-in session, not server limits. + + Byte counts use received message bytes, before parsing. They bound + buffered event data, not transport framing, decoding, or Python object overhead. + ``max_response_bytes`` optionally bounds the total event data fed into one + accumulator; its default of ``None`` leaves response accumulation uncapped. + Choose values appropriate to your output, including images and tool results. + """ + + max_lanes: int + max_events_per_lane: int + max_events: int + max_bytes_per_lane: int + max_bytes: int + max_response_bytes: int | None = None + + def __post_init__(self) -> None: + for name, value in vars(self).items(): + if name == "max_response_bytes" and value is None: + continue + if not isinstance(value, int) or isinstance(value, bool) or value <= 0: + raise ValueError("WebSocket session limits must be positive integers") + + +class ResponsesWebSocketBufferError(RuntimeError): + """A session or response exceeded an application-selected buffer limit.""" + + +class ResponsesWebSocketError(RuntimeError): + """A request-scoped protocol error. The original event is available as ``event``.""" + + def __init__(self, event: ResponsesServerEvent) -> None: + super().__init__("The Responses WebSocket request returned an error event") + self.event = event + + +_claimed: WeakSet[object] = WeakSet() +_claim_lock = threading.Lock() + + +def _claim(connection: object) -> None: + with _claim_lock: + if connection in _claimed: + raise RuntimeError("This connection already has a WebSocket session") + _claimed.add(connection) + + +def _release(connection: object) -> None: + with _claim_lock: + _claimed.discard(connection) + + +def _field(event: object, name: str) -> Any: + return cast("dict[str, Any]", event).get(name) if isinstance(event, dict) else getattr(event, name, None) + + +def _command(event: ResponsesClientEvent | ResponsesClientEventParam, stream_id: str | None) -> tuple[str, bool]: + data = event.to_dict(exclude_unset=True) if isinstance(event, BaseModel) else dict(event) + is_create = bool(data.get("type") == "response.create") + if is_create: + if "stream_id" in data and data["stream_id"] != stream_id: + raise ValueError("The command stream_id does not match its lane") + if stream_id is not None: + data["stream_id"] = stream_id + return json.dumps(maybe_transform(data, ResponsesClientEventParam)), is_create + + +class _Accumulator: + def __init__(self, limit: int | None) -> None: + self.limit = limit + self.size = 0 + self.state: ResponseStreamState[object] | None = None + self.final: Response | None = None + self.error: Exception | None = None + + def add(self, event: ResponsesServerEvent, size: int) -> None: + kind = _field(event, "type") + if kind == "response.created": + self.size = 0 + self.final = None + self.error = None + self.state = ResponseStreamState(input_tools=omit, text_format=omit) + if kind == "error": + self.state = None + self.size = 0 + self.final = None + self.error = ResponsesWebSocketError(event) + return + if self.error is not None: + return + # Finalized items and the terminal response are authoritative. WebSocket + # deltas need not include all of the SSE item/part setup events, so feed + # only these compatible events to the existing final-response accumulator. + # Unknown events and deltas remain available untouched to the caller. + compatible = kind in { + "response.created", + "response.output_item.done", + "response.completed", + } + terminal = kind in {"response.completed", "response.failed", "response.incomplete"} + if terminal and _field(event, "response") is None: + raise ValueError("Terminal WebSocket event is missing response") + if compatible or terminal: + self.size += size + if self.limit is not None and self.size > self.limit: + self.state = None + self.final = None + raise ResponsesWebSocketBufferError("Response accumulation exceeded max_response_bytes") + if self.state is not None and compatible: + # Accumulation must not mutate the raw event visible to callers. + self.state.handle_event(cast(ResponseStreamEvent, model_copy(event, deep=True))) + if terminal: + self.final = cast(Response, _field(event, "response")) + if kind == "response.completed" and self.state is not None: + self.final = self.state._completed_response + elif self.state is not None and getattr(self.final, "output", None) is None: + self.final = model_copy(self.final) + self.final.output = [ + self.state._completed_output[index] for index in sorted(self.state._completed_output) + ] + self.state = None + self.size = 0 + + +class _LaneState: + def __init__(self, stream_id: str | None, limit: int | None) -> None: + self.stream_id = stream_id + self.queue: deque[tuple[ResponsesServerEvent, int]] = deque() + self.size = 0 + self.closed = False + self.in_flight = False + self.closed_connection: object | None = None + self.received_generation = 0 + self.consumed_generation = 0 + self.wake = asyncio.Event() + self.accumulator = _Accumulator(limit) + + +class _Router: + def __init__( + self, limits: ResponsesWebSocketLimits, connection: ResponsesConnection | AsyncResponsesConnection + ) -> None: + self.connection = connection + self.limits = limits + self.condition = threading.Condition() + self.lanes: dict[str | None, _LaneState] = {} + self.events = 0 + self.size = 0 + self.ended = False + self.error: BaseException | None = None + self.default = self.register(None) + + def register(self, stream_id: object) -> _LaneState: + if stream_id is not None and ( + not isinstance(stream_id, str) or re.fullmatch(r"[A-Za-z0-9_.-]{1,256}", stream_id) is None + ): + raise ValueError("stream_id must be None or 1–256 ASCII letters, digits, underscores, hyphens or periods") + with self.condition: + if self.ended: + raise RuntimeError("The WebSocket session has ended") + if stream_id in self.lanes: + raise ValueError("A lane with this stream_id is already registered") + if len(self.lanes) >= self.limits.max_lanes: + raise ResponsesWebSocketBufferError("The session exceeded max_lanes") + lane = _LaneState(stream_id, self.limits.max_response_bytes) + self.lanes[stream_id] = lane + if stream_id is None: + self.default = lane + return lane + + def detach(self, lane: _LaneState) -> None: + with self.condition: + if lane.closed: + return + lane.closed = True + lane.closed_connection = self.connection._connection + self.events -= len(lane.queue) + self.size -= lane.size + lane.queue.clear() + lane.size = 0 + lane.accumulator = _Accumulator(self.limits.max_response_bytes) + # A terminal can be followed by an automatic steering successor. + # Keep every registered ID and its max_lanes slot until reconnect. + lane.wake.set() + self.condition.notify_all() + + def reconnected(self, connection: object) -> None: + with self.condition: + for stream_id, lane in list(self.lanes.items()): + if lane.closed and lane.closed_connection is not connection: + # A lane closed on the new socket must survive cleanup, + # including when it was registered before reconnect. + del self.lanes[stream_id] + + def dispatch(self, event: ResponsesServerEvent, size: int) -> None: + with self.condition: + if self.ended: + return + stream_id = _field(event, "stream_id") + lane = self.lanes.get(stream_id, self.default) + if lane.closed: + lane = self.default + if lane.closed: + return + if ( + len(lane.queue) >= self.limits.max_events_per_lane + or self.events >= self.limits.max_events + or lane.size + size > self.limits.max_bytes_per_lane + or self.size + size > self.limits.max_bytes + ): + raise ResponsesWebSocketBufferError("The session exceeded its buffered event limits") + lane.queue.append((event, size)) + if _field(event, "stream_id") == lane.stream_id and _field(event, "type") == "response.created": + lane.received_generation += 1 + lane.in_flight = True + lane.size += size + self.size += size + self.events += 1 + lane.wake.set() + self.condition.notify_all() + + def finish(self, error: BaseException | None = None) -> None: + with self.condition: + self.ended = True + if self.error is None: + self.error = error + for lane in self.lanes.values(): + lane.wake.set() + self.condition.notify_all() + + def pop(self, lane: _LaneState) -> ResponsesServerEvent | None: + # The caller holds the condition. No await occurs after dequeueing. + if lane.closed: + raise EOFError("The WebSocket lane is detached") + if lane.queue: + event, size = lane.queue.popleft() + lane.size -= size + self.size -= size + self.events -= 1 + # Unregistered named streams remain visible on the default lane, + # but cannot change that lane's response or in-flight state. + if _field(event, "stream_id") != lane.stream_id: + return event + try: + lane.accumulator.add(event, size) + except Exception as exc: + lane.accumulator.error = exc + lane.accumulator.state = None + if _field(event, "type") == "response.created": + lane.consumed_generation += 1 + lane.in_flight = True + elif _field(event, "type") in {"response.completed", "response.failed", "response.incomplete", "error"}: + lane.in_flight = lane.received_generation > lane.consumed_generation + return event + if self.ended: + if self.error is not None: + raise self.error + raise EOFError("The WebSocket connection closed") + return None + + def prepare(self, lane: _LaneState, is_create: bool) -> None: + with self.condition: + if self.ended or lane.closed: + raise RuntimeError("The WebSocket session or lane is not active") + if is_create: + if lane.in_flight: + raise RuntimeError("Consume the current response before sending another create on this lane") + lane.accumulator = _Accumulator(self.limits.max_response_bytes) + lane.in_flight = True + + +class ResponsesWebSocketLane: + """A routed view of one session. ``close`` detaches it without closing the socket. + + Use a single consumer per lane. Cancel a synchronous wait with ``recv(timeout=...)``; + a timeout never consumes an event. Different lanes may be consumed concurrently. + """ + + def __init__(self, session: ResponsesWebSocketSession, state: _LaneState) -> None: + self._session = session + self._state = state + + def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: + # Pin the transport before checking recovery so this write cannot migrate. + connection = self._session.connection._connection + self._session._check_send(self._state) + # Serializing supported iterables can call back into the connection. + data, is_create = _command(event, self._state.stream_id) + with self._session.connection._connection_lock: + if self._session.connection._is_reconnecting: + raise RuntimeError("The WebSocket connection is reconnecting; restore state before sending") + if connection is not self._session.connection._connection: + raise RuntimeError("The WebSocket connection changed; restore state before sending") + # Reserve the lane before recovery can publish a replacement socket. + self._session._router.prepare(self._state, is_create) + # The legacy send_raw recovery queue can replay uncertain writes. + # Send directly to this physical socket so lane requests never enter it. + connection.send(data) + + def recv(self, *, timeout: float | None = None) -> ResponsesServerEvent: + if self._session._thread is None: + raise RuntimeError("Enter the WebSocket session before receiving events") + deadline = None if timeout is None else time.monotonic() + timeout + router = self._session._router + with router.condition: + while True: + event = router.pop(self._state) + if event is not None: + return event + remaining = None if deadline is None else deadline - time.monotonic() + if remaining is not None and remaining <= 0: + raise TimeoutError("Timed out waiting for a Responses WebSocket event") + router.condition.wait(remaining) + + def get_final_response(self, *, timeout: float | None = None) -> Response: + """Consume this response's remaining events; failed/incomplete are final results.""" + deadline = None if timeout is None else time.monotonic() + timeout + generation = self._state.consumed_generation + while ( + self._state.accumulator.final is not None + and self._state.consumed_generation == generation + and self._state.received_generation > generation + ): + remaining = None if deadline is None else max(0.0, deadline - time.monotonic()) + self.recv(timeout=remaining) + while self._state.accumulator.final is None: + if self._state.accumulator.error is not None: + raise self._state.accumulator.error + remaining = None if deadline is None else max(0.0, deadline - time.monotonic()) + self.recv(timeout=remaining) + return self._state.accumulator.final + + def close(self) -> None: + self._session._router.detach(self._state) + + +class ResponsesWebSocketSession: + """Own one existing connection and route its events with one background reader. + + Enter the session before sending. While active, consume events only through + its lanes, not the connection's recv/iterator/dispatch_events. Closing the + session closes its connection; closing a lane does not. Register lanes before + sending requests. Events for unregistered lanes go to the default lane for + inspection, without changing its response state. Every registered lane, including + the default and closed lanes, reserves its ID and max_lanes slot until reconnect. + Keep a lane open for sequential responses. Reconnect releases closed lanes; + open lanes remain registered on the new socket. + + Recovery remains the existing connection's opt-in policy. Reconnecting a + transport does not restore server-side response state or replay lane sends. + """ + + def __init__(self, connection: ResponsesConnection, *, limits: ResponsesWebSocketLimits) -> None: + self.connection = connection + self._router = _Router(limits, connection) + self._thread: threading.Thread | None = None + self._closed = False + self._owns_connection = False + self.default = ResponsesWebSocketLane(self, self._router.default) + + def __enter__(self) -> Self: + if self._thread is not None or self._closed: + raise RuntimeError("A WebSocket session can only be entered once") + _claim(self.connection) + self._owns_connection = True + self._thread = threading.Thread(target=self._read, name="responses-websocket", daemon=True) + try: + self._thread.start() + except Exception: + self._release_connection() + self._thread = None + raise + return self + + def _release_connection(self) -> None: + # The reader and close caller may both finish cleanup. Release only our claim. + with _claim_lock: + if self._owns_connection: + _claimed.discard(self.connection) + self._owns_connection = False + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, traceback: TracebackType | None + ) -> None: + try: + self.close() + except Exception: + if exc is None: + raise + + def lane(self, stream_id: str | None = None) -> ResponsesWebSocketLane: + lane = ResponsesWebSocketLane(self, self._router.register(stream_id)) + if stream_id is None: + self.default = lane + return lane + + def _check_send(self, lane: _LaneState) -> None: + if self._thread is None or self._closed or self._router.ended or lane.closed: + raise RuntimeError("The WebSocket session or lane is not active") + if self.connection._is_reconnecting: + raise RuntimeError("The WebSocket connection is reconnecting; restore state before sending") + + def _read(self) -> None: + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + try: + while True: + try: + data = self.connection.recv_bytes() + except ConnectionClosedOK: + break + except ConnectionClosedError as exc: + if self.connection._reconnect(exc): + self._router.reconnected(self.connection._connection) + continue + unsent = self.connection._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", unsent_messages=unsent + ) from exc + raise + self._router.dispatch(self.connection.parse_event(data), len(data)) + del data + except Exception as exc: + self._router.finish(exc) + try: + self.connection.close() + except Exception: + pass # Preserve the original reader/overflow failure. + finally: + self._router.finish() + if self._closed: + self._release_connection() + + def close(self) -> None: + if self._closed and (self._thread is None or not self._thread.is_alive()): + return + self._closed = True + self._router.finish() + if self._thread is None: + return + try: + self.connection.close() + finally: + if self._thread is not threading.current_thread(): + self._thread.join(timeout=5) + if self._thread.is_alive(): + raise RuntimeError("The Responses WebSocket reader did not stop after close") + self._release_connection() + + +class AsyncResponsesWebSocketLane: + """An async routed lane. Canceling a recv/final-response wait never closes it.""" + + def __init__(self, session: AsyncResponsesWebSocketSession, state: _LaneState) -> None: + self._session = session + self._state = state + + async def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: + # Pin the transport before checking recovery so this write cannot migrate. + connection = self._session.connection._connection + self._session._check_send(self._state) + data, is_create = _command(event, self._state.stream_id) + self._session._router.prepare(self._state, is_create) + # Bypass the legacy recovery queue, including a reconnect starting now. + await connection.send(data) + + async def recv(self) -> ResponsesServerEvent: + if self._session._task is None: + raise RuntimeError("Enter the WebSocket session before receiving events") + router = self._session._router + while True: + with router.condition: + event = router.pop(self._state) + if event is not None: + return event + self._state.wake.clear() + await self._state.wake.wait() + + async def get_final_response(self) -> Response: + generation = self._state.consumed_generation + while ( + self._state.accumulator.final is not None + and self._state.consumed_generation == generation + and self._state.received_generation > generation + ): + await self.recv() + while self._state.accumulator.final is None: + if self._state.accumulator.error is not None: + raise self._state.accumulator.error + await self.recv() + return self._state.accumulator.final + + def close(self) -> None: + self._session._router.detach(self._state) + + +class AsyncResponsesWebSocketSession: + """Async counterpart of ResponsesWebSocketSession, owning the connection lifetime.""" + + def __init__(self, connection: AsyncResponsesConnection, *, limits: ResponsesWebSocketLimits) -> None: + self.connection = connection + self._router = _Router(limits, connection) + self._task: asyncio.Task[None] | None = None + self._close_task: asyncio.Task[None] | None = None + self._closed = False + self.default = AsyncResponsesWebSocketLane(self, self._router.default) + + async def __aenter__(self) -> Self: + if self._task is not None or self._closed: + raise RuntimeError("A WebSocket session can only be entered once") + _claim(self.connection) + self._task = asyncio.create_task(self._read(), name="responses-websocket") + return self + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, traceback: TracebackType | None + ) -> None: + try: + await self.close() + except Exception: + if exc is None: + raise + + def lane(self, stream_id: str | None = None) -> AsyncResponsesWebSocketLane: + lane = AsyncResponsesWebSocketLane(self, self._router.register(stream_id)) + if stream_id is None: + self.default = lane + return lane + + def _check_send(self, lane: _LaneState) -> None: + if self._task is None or self._closed or self._router.ended or lane.closed: + raise RuntimeError("The WebSocket session or lane is not active") + if self.connection._is_reconnecting: + raise RuntimeError("The WebSocket connection is reconnecting; restore state before sending") + + async def _read(self) -> None: + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + try: + while True: + try: + data = await self.connection.recv_bytes() + except ConnectionClosedOK: + break + except ConnectionClosedError as exc: + if await self.connection._reconnect(exc): + self._router.reconnected(self.connection._connection) + continue + unsent = self.connection._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", unsent_messages=unsent + ) from exc + raise + self._router.dispatch(self.connection.parse_event(data), len(data)) + del data + except asyncio.CancelledError: + raise + except Exception as exc: + self._router.finish(exc) + try: + await self.connection.close() + except Exception: + # Preserve the reader failure already delivered to every lane. + pass + finally: + self._router.finish() + + async def close(self) -> None: + if self._close_task is None: + self._closed = True + self._router.finish() + if self._task is None: + return + self._close_task = asyncio.create_task(self._close(self._task), name="responses-websocket-close") + # Cancellation stops this caller's wait, not cleanup or its ownership claim. + await asyncio.shield(self._close_task) + + async def _close(self, reader: asyncio.Task[None]) -> None: + try: + reader.cancel() + try: + await reader + except asyncio.CancelledError: + # Cancellation is expected after stopping the reader above. + pass + await self.connection.close() + finally: + _release(self.connection) diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/__init__.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/__init__.py new file mode 100644 index 0000000..3d0a782 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/__init__.py @@ -0,0 +1,34 @@ +import typing as _t + +if _t.TYPE_CHECKING: + from ._assistants import ( + AssistantEventHandler as AssistantEventHandler, + AssistantEventHandlerT as AssistantEventHandlerT, + AssistantStreamManager as AssistantStreamManager, + AsyncAssistantEventHandler as AsyncAssistantEventHandler, + AsyncAssistantEventHandlerT as AsyncAssistantEventHandlerT, + AsyncAssistantStreamManager as AsyncAssistantStreamManager, + ) +else: + + def __getattr__(name: str) -> _t.Any: + if name in __all__: + from importlib import import_module + + value = getattr(import_module("._assistants", __name__), name) + globals()[name] = value + return value + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(__all__)) + + +__all__ = [ + "AssistantEventHandler", + "AssistantEventHandlerT", + "AssistantStreamManager", + "AsyncAssistantEventHandler", + "AsyncAssistantEventHandlerT", + "AsyncAssistantStreamManager", +] diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..cee6bf9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/__pycache__/_assistants.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/__pycache__/_assistants.cpython-312.pyc new file mode 100644 index 0000000..c597056 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/__pycache__/_assistants.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/__pycache__/_deltas.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/__pycache__/_deltas.cpython-312.pyc new file mode 100644 index 0000000..bb53d35 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/__pycache__/_deltas.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/_assistants.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/_assistants.py new file mode 100644 index 0000000..676ffe6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/_assistants.py @@ -0,0 +1,1002 @@ +from __future__ import annotations + +import asyncio +from types import TracebackType +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Callable, Iterable, Iterator, cast +from typing_extensions import Awaitable, AsyncIterable, AsyncIterator, assert_never + +from ._deltas import accumulate_delta as accumulate_delta +from ..._utils import consume_sync_iterator, consume_async_iterator +from ..._compat import model_dump +from ..._httpx2 import request_exceptions, timeout_exceptions +from ..._models import construct_type +from ..._streaming import Stream, AsyncStream +from ...types.beta import AssistantStreamEvent +from ..._exceptions import APIConnectionError +from ...types.beta.threads import ( + Run, + Text, + Message, + ImageFile, + TextDelta, + MessageDelta, + MessageContent, + MessageContentDelta, +) +from ...types.beta.threads.runs import RunStep, ToolCall, RunStepDelta, ToolCallDelta + + +def _timeout_exceptions() -> tuple[type[Exception], ...]: + return (*timeout_exceptions(), asyncio.TimeoutError) + + +def _iter_events(stream: Stream[AssistantStreamEvent]) -> Iterator[AssistantStreamEvent]: + # Preserve legacy transport exceptions without unwrapping errors from user callbacks. + try: + yield from stream + except APIConnectionError as exc: + if isinstance(exc.__cause__, request_exceptions()): + raise exc.__cause__ from None + raise + + +async def _aiter_events(stream: AsyncStream[AssistantStreamEvent]) -> AsyncIterator[AssistantStreamEvent]: + try: + async for event in stream: + yield event + except APIConnectionError as exc: + if isinstance(exc.__cause__, request_exceptions()): + raise exc.__cause__ from None + raise + + +class AssistantEventHandler: + text_deltas: Iterable[str] + """Iterator over just the text deltas in the stream. + + This corresponds to the `thread.message.delta` event + in the API. + + ```py + for text in stream.text_deltas: + print(text, end="", flush=True) + print() + ``` + """ + + def __init__(self) -> None: + self._current_event: AssistantStreamEvent | None = None + self._current_message_content_index: int | None = None + self._current_message_content: MessageContent | None = None + self._current_tool_call_index: int | None = None + self._current_tool_call: ToolCall | None = None + self.__current_run_step_id: str | None = None + self.__current_run: Run | None = None + self.__run_step_snapshots: dict[str, RunStep] = {} + self.__message_snapshots: dict[str, Message] = {} + self.__current_message_snapshot: Message | None = None + + self.text_deltas = self.__text_deltas__() + self._iterator = self.__stream__() + self.__stream: Stream[AssistantStreamEvent] | None = None + + def _init(self, stream: Stream[AssistantStreamEvent]) -> None: + if self.__stream: + raise RuntimeError( + "A single event handler cannot be shared between multiple streams; You will need to construct a new event handler instance" + ) + + self.__stream = stream + + def __next__(self) -> AssistantStreamEvent: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[AssistantStreamEvent]: + for item in self._iterator: + yield item + + @property + def current_event(self) -> AssistantStreamEvent | None: + return self._current_event + + @property + def current_run(self) -> Run | None: + return self.__current_run + + @property + def current_run_step_snapshot(self) -> RunStep | None: + if not self.__current_run_step_id: + return None + + return self.__run_step_snapshots[self.__current_run_step_id] + + @property + def current_message_snapshot(self) -> Message | None: + return self.__current_message_snapshot + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called when the context manager exits. + """ + if self.__stream: + self.__stream.close() + + def until_done(self) -> None: + """Waits until the stream has been consumed""" + consume_sync_iterator(self) + + def get_final_run(self) -> Run: + """Wait for the stream to finish and returns the completed Run object""" + self.until_done() + + if not self.__current_run: + raise RuntimeError("No final run object found") + + return self.__current_run + + def get_final_run_steps(self) -> list[RunStep]: + """Wait for the stream to finish and returns the steps taken in this run""" + self.until_done() + + if not self.__run_step_snapshots: + raise RuntimeError("No run steps found") + + return [step for step in self.__run_step_snapshots.values()] + + def get_final_messages(self) -> list[Message]: + """Wait for the stream to finish and returns the messages emitted in this run""" + self.until_done() + + if not self.__message_snapshots: + raise RuntimeError("No messages found") + + return [message for message in self.__message_snapshots.values()] + + def __text_deltas__(self) -> Iterator[str]: + for event in self: + if event.event != "thread.message.delta": + continue + + for content_delta in event.data.delta.content or []: + if content_delta.type == "text" and content_delta.text and content_delta.text.value: + yield content_delta.text.value + + # event handlers + + def on_end(self) -> None: + """Fires when the stream has finished. + + This happens if the stream is read to completion + or if an exception occurs during iteration. + """ + + def on_event(self, event: AssistantStreamEvent) -> None: + """Callback that is fired for every Server-Sent-Event""" + + def on_run_step_created(self, run_step: RunStep) -> None: + """Callback that is fired when a run step is created""" + + def on_run_step_delta(self, delta: RunStepDelta, snapshot: RunStep) -> None: + """Callback that is fired whenever a run step delta is returned from the API + + The first argument is just the delta as sent by the API and the second argument + is the accumulated snapshot of the run step. For example, a tool calls event may + look like this: + + # delta + tool_calls=[ + RunStepDeltaToolCallsCodeInterpreter( + index=0, + type='code_interpreter', + id=None, + code_interpreter=CodeInterpreter(input=' sympy', outputs=None) + ) + ] + # snapshot + tool_calls=[ + CodeToolCall( + id='call_wKayJlcYV12NiadiZuJXxcfx', + code_interpreter=CodeInterpreter(input='from sympy', outputs=[]), + type='code_interpreter', + index=0 + ) + ], + """ + + def on_run_step_done(self, run_step: RunStep) -> None: + """Callback that is fired when a run step is completed""" + + def on_tool_call_created(self, tool_call: ToolCall) -> None: + """Callback that is fired when a tool call is created""" + + def on_tool_call_delta(self, delta: ToolCallDelta, snapshot: ToolCall) -> None: + """Callback that is fired when a tool call delta is encountered""" + + def on_tool_call_done(self, tool_call: ToolCall) -> None: + """Callback that is fired when a tool call delta is encountered""" + + def on_exception(self, exception: Exception) -> None: + """Fired whenever an exception happens during streaming""" + + def on_timeout(self) -> None: + """Fires if the request times out""" + + def on_message_created(self, message: Message) -> None: + """Callback that is fired when a message is created""" + + def on_message_delta(self, delta: MessageDelta, snapshot: Message) -> None: + """Callback that is fired whenever a message delta is returned from the API + + The first argument is just the delta as sent by the API and the second argument + is the accumulated snapshot of the message. For example, a text content event may + look like this: + + # delta + MessageDeltaText( + index=0, + type='text', + text=Text( + value=' Jane' + ), + ) + # snapshot + MessageContentText( + index=0, + type='text', + text=Text( + value='Certainly, Jane' + ), + ) + """ + + def on_message_done(self, message: Message) -> None: + """Callback that is fired when a message is completed""" + + def on_text_created(self, text: Text) -> None: + """Callback that is fired when a text content block is created""" + + def on_text_delta(self, delta: TextDelta, snapshot: Text) -> None: + """Callback that is fired whenever a text content delta is returned + by the API. + + The first argument is just the delta as sent by the API and the second argument + is the accumulated snapshot of the text. For example: + + on_text_delta(TextDelta(value="The"), Text(value="The")), + on_text_delta(TextDelta(value=" solution"), Text(value="The solution")), + on_text_delta(TextDelta(value=" to"), Text(value="The solution to")), + on_text_delta(TextDelta(value=" the"), Text(value="The solution to the")), + on_text_delta(TextDelta(value=" equation"), Text(value="The solution to the equation")), + """ + + def on_text_done(self, text: Text) -> None: + """Callback that is fired when a text content block is finished""" + + def on_image_file_done(self, image_file: ImageFile) -> None: + """Callback that is fired when an image file block is finished""" + + def _emit_sse_event(self, event: AssistantStreamEvent) -> None: + self._current_event = event + self.on_event(event) + + self.__current_message_snapshot, new_content = accumulate_event( + event=event, + current_message_snapshot=self.__current_message_snapshot, + ) + if self.__current_message_snapshot is not None: + self.__message_snapshots[self.__current_message_snapshot.id] = self.__current_message_snapshot + + accumulate_run_step( + event=event, + run_step_snapshots=self.__run_step_snapshots, + ) + + for content_delta in new_content: + assert self.__current_message_snapshot is not None + + block = self.__current_message_snapshot.content[content_delta.index] + if block.type == "text": + self.on_text_created(block.text) + + if ( + event.event == "thread.run.completed" + or event.event == "thread.run.cancelled" + or event.event == "thread.run.expired" + or event.event == "thread.run.failed" + or event.event == "thread.run.requires_action" + or event.event == "thread.run.incomplete" + ): + self.__current_run = event.data + if self._current_tool_call: + self.on_tool_call_done(self._current_tool_call) + elif ( + event.event == "thread.run.created" + or event.event == "thread.run.in_progress" + or event.event == "thread.run.cancelling" + or event.event == "thread.run.queued" + ): + self.__current_run = event.data + elif event.event == "thread.message.created": + self.on_message_created(event.data) + elif event.event == "thread.message.delta": + snapshot = self.__current_message_snapshot + assert snapshot is not None + + message_delta = event.data.delta + if message_delta.content is not None: + for content_delta in message_delta.content: + if content_delta.type == "text" and content_delta.text: + snapshot_content = snapshot.content[content_delta.index] + assert snapshot_content.type == "text" + self.on_text_delta(content_delta.text, snapshot_content.text) + + # If the delta is for a new message content: + # - emit on_text_done/on_image_file_done for the previous message content + # - emit on_text_created/on_image_created for the new message content + if content_delta.index != self._current_message_content_index: + if self._current_message_content is not None: + if self._current_message_content.type == "text": + self.on_text_done(self._current_message_content.text) + elif self._current_message_content.type == "image_file": + self.on_image_file_done(self._current_message_content.image_file) + + self._current_message_content_index = content_delta.index + self._current_message_content = snapshot.content[content_delta.index] + + # Update the current_message_content (delta event is correctly emitted already) + self._current_message_content = snapshot.content[content_delta.index] + + self.on_message_delta(event.data.delta, snapshot) + elif event.event == "thread.message.completed" or event.event == "thread.message.incomplete": + self.__current_message_snapshot = event.data + self.__message_snapshots[event.data.id] = event.data + + if self._current_message_content_index is not None: + content = event.data.content[self._current_message_content_index] + if content.type == "text": + self.on_text_done(content.text) + elif content.type == "image_file": + self.on_image_file_done(content.image_file) + + self.on_message_done(event.data) + elif event.event == "thread.run.step.created": + self.__current_run_step_id = event.data.id + self.on_run_step_created(event.data) + elif event.event == "thread.run.step.in_progress": + self.__current_run_step_id = event.data.id + elif event.event == "thread.run.step.delta": + step_snapshot = self.__run_step_snapshots[event.data.id] + + run_step_delta = event.data.delta + if ( + run_step_delta.step_details + and run_step_delta.step_details.type == "tool_calls" + and run_step_delta.step_details.tool_calls is not None + ): + assert step_snapshot.step_details.type == "tool_calls" + for tool_call_delta in run_step_delta.step_details.tool_calls: + if tool_call_delta.index == self._current_tool_call_index: + self.on_tool_call_delta( + tool_call_delta, + step_snapshot.step_details.tool_calls[tool_call_delta.index], + ) + + # If the delta is for a new tool call: + # - emit on_tool_call_done for the previous tool_call + # - emit on_tool_call_created for the new tool_call + if tool_call_delta.index != self._current_tool_call_index: + if self._current_tool_call is not None: + self.on_tool_call_done(self._current_tool_call) + + self._current_tool_call_index = tool_call_delta.index + self._current_tool_call = step_snapshot.step_details.tool_calls[tool_call_delta.index] + self.on_tool_call_created(self._current_tool_call) + + # Update the current_tool_call (delta event is correctly emitted already) + self._current_tool_call = step_snapshot.step_details.tool_calls[tool_call_delta.index] + + self.on_run_step_delta( + event.data.delta, + step_snapshot, + ) + elif ( + event.event == "thread.run.step.completed" + or event.event == "thread.run.step.cancelled" + or event.event == "thread.run.step.expired" + or event.event == "thread.run.step.failed" + ): + if self._current_tool_call: + self.on_tool_call_done(self._current_tool_call) + + self.on_run_step_done(event.data) + self.__current_run_step_id = None + elif event.event == "thread.created" or event.event == "thread.message.in_progress" or event.event == "error": + # currently no special handling + ... + else: + # we only want to error at build-time + if TYPE_CHECKING: # type: ignore[unreachable] + assert_never(event) + + self._current_event = None + + def __stream__(self) -> Iterator[AssistantStreamEvent]: + stream = self.__stream + if not stream: + raise RuntimeError("Stream has not been started yet") + + try: + for event in _iter_events(stream): + self._emit_sse_event(event) + + yield event + except _timeout_exceptions() as exc: + self.on_timeout() + self.on_exception(exc) + raise + except Exception as exc: + self.on_exception(exc) + raise + finally: + self.on_end() + + +AssistantEventHandlerT = TypeVar("AssistantEventHandlerT", bound=AssistantEventHandler) + + +class AssistantStreamManager(Generic[AssistantEventHandlerT]): + """Wrapper over AssistantStreamEventHandler that is returned by `.stream()` + so that a context manager can be used. + + ```py + with client.threads.create_and_run_stream(...) as stream: + for event in stream: + ... + ``` + """ + + def __init__( + self, + api_request: Callable[[], Stream[AssistantStreamEvent]], + *, + event_handler: AssistantEventHandlerT, + ) -> None: + self.__stream: Stream[AssistantStreamEvent] | None = None + self.__event_handler = event_handler + self.__api_request = api_request + + def __enter__(self) -> AssistantEventHandlerT: + self.__stream = self.__api_request() + self.__event_handler._init(self.__stream) + return self.__event_handler + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__stream is not None: + self.__stream.close() + + +class AsyncAssistantEventHandler: + text_deltas: AsyncIterable[str] + """Iterator over just the text deltas in the stream. + + This corresponds to the `thread.message.delta` event + in the API. + + ```py + async for text in stream.text_deltas: + print(text, end="", flush=True) + print() + ``` + """ + + def __init__(self) -> None: + self._current_event: AssistantStreamEvent | None = None + self._current_message_content_index: int | None = None + self._current_message_content: MessageContent | None = None + self._current_tool_call_index: int | None = None + self._current_tool_call: ToolCall | None = None + self.__current_run_step_id: str | None = None + self.__current_run: Run | None = None + self.__run_step_snapshots: dict[str, RunStep] = {} + self.__message_snapshots: dict[str, Message] = {} + self.__current_message_snapshot: Message | None = None + + self.text_deltas = self.__text_deltas__() + self._iterator = self.__stream__() + self.__stream: AsyncStream[AssistantStreamEvent] | None = None + + def _init(self, stream: AsyncStream[AssistantStreamEvent]) -> None: + if self.__stream: + raise RuntimeError( + "A single event handler cannot be shared between multiple streams; You will need to construct a new event handler instance" + ) + + self.__stream = stream + + async def __anext__(self) -> AssistantStreamEvent: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[AssistantStreamEvent]: + async for item in self._iterator: + yield item + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called when the context manager exits. + """ + if self.__stream: + await self.__stream.close() + + @property + def current_event(self) -> AssistantStreamEvent | None: + return self._current_event + + @property + def current_run(self) -> Run | None: + return self.__current_run + + @property + def current_run_step_snapshot(self) -> RunStep | None: + if not self.__current_run_step_id: + return None + + return self.__run_step_snapshots[self.__current_run_step_id] + + @property + def current_message_snapshot(self) -> Message | None: + return self.__current_message_snapshot + + async def until_done(self) -> None: + """Waits until the stream has been consumed""" + await consume_async_iterator(self) + + async def get_final_run(self) -> Run: + """Wait for the stream to finish and returns the completed Run object""" + await self.until_done() + + if not self.__current_run: + raise RuntimeError("No final run object found") + + return self.__current_run + + async def get_final_run_steps(self) -> list[RunStep]: + """Wait for the stream to finish and returns the steps taken in this run""" + await self.until_done() + + if not self.__run_step_snapshots: + raise RuntimeError("No run steps found") + + return [step for step in self.__run_step_snapshots.values()] + + async def get_final_messages(self) -> list[Message]: + """Wait for the stream to finish and returns the messages emitted in this run""" + await self.until_done() + + if not self.__message_snapshots: + raise RuntimeError("No messages found") + + return [message for message in self.__message_snapshots.values()] + + async def __text_deltas__(self) -> AsyncIterator[str]: + async for event in self: + if event.event != "thread.message.delta": + continue + + for content_delta in event.data.delta.content or []: + if content_delta.type == "text" and content_delta.text and content_delta.text.value: + yield content_delta.text.value + + # event handlers + + async def on_end(self) -> None: + """Fires when the stream has finished. + + This happens if the stream is read to completion + or if an exception occurs during iteration. + """ + + async def on_event(self, event: AssistantStreamEvent) -> None: + """Callback that is fired for every Server-Sent-Event""" + + async def on_run_step_created(self, run_step: RunStep) -> None: + """Callback that is fired when a run step is created""" + + async def on_run_step_delta(self, delta: RunStepDelta, snapshot: RunStep) -> None: + """Callback that is fired whenever a run step delta is returned from the API + + The first argument is just the delta as sent by the API and the second argument + is the accumulated snapshot of the run step. For example, a tool calls event may + look like this: + + # delta + tool_calls=[ + RunStepDeltaToolCallsCodeInterpreter( + index=0, + type='code_interpreter', + id=None, + code_interpreter=CodeInterpreter(input=' sympy', outputs=None) + ) + ] + # snapshot + tool_calls=[ + CodeToolCall( + id='call_wKayJlcYV12NiadiZuJXxcfx', + code_interpreter=CodeInterpreter(input='from sympy', outputs=[]), + type='code_interpreter', + index=0 + ) + ], + """ + + async def on_run_step_done(self, run_step: RunStep) -> None: + """Callback that is fired when a run step is completed""" + + async def on_tool_call_created(self, tool_call: ToolCall) -> None: + """Callback that is fired when a tool call is created""" + + async def on_tool_call_delta(self, delta: ToolCallDelta, snapshot: ToolCall) -> None: + """Callback that is fired when a tool call delta is encountered""" + + async def on_tool_call_done(self, tool_call: ToolCall) -> None: + """Callback that is fired when a tool call delta is encountered""" + + async def on_exception(self, exception: Exception) -> None: + """Fired whenever an exception happens during streaming""" + + async def on_timeout(self) -> None: + """Fires if the request times out""" + + async def on_message_created(self, message: Message) -> None: + """Callback that is fired when a message is created""" + + async def on_message_delta(self, delta: MessageDelta, snapshot: Message) -> None: + """Callback that is fired whenever a message delta is returned from the API + + The first argument is just the delta as sent by the API and the second argument + is the accumulated snapshot of the message. For example, a text content event may + look like this: + + # delta + MessageDeltaText( + index=0, + type='text', + text=Text( + value=' Jane' + ), + ) + # snapshot + MessageContentText( + index=0, + type='text', + text=Text( + value='Certainly, Jane' + ), + ) + """ + + async def on_message_done(self, message: Message) -> None: + """Callback that is fired when a message is completed""" + + async def on_text_created(self, text: Text) -> None: + """Callback that is fired when a text content block is created""" + + async def on_text_delta(self, delta: TextDelta, snapshot: Text) -> None: + """Callback that is fired whenever a text content delta is returned + by the API. + + The first argument is just the delta as sent by the API and the second argument + is the accumulated snapshot of the text. For example: + + on_text_delta(TextDelta(value="The"), Text(value="The")), + on_text_delta(TextDelta(value=" solution"), Text(value="The solution")), + on_text_delta(TextDelta(value=" to"), Text(value="The solution to")), + on_text_delta(TextDelta(value=" the"), Text(value="The solution to the")), + on_text_delta(TextDelta(value=" equation"), Text(value="The solution to the equivalent")), + """ + + async def on_text_done(self, text: Text) -> None: + """Callback that is fired when a text content block is finished""" + + async def on_image_file_done(self, image_file: ImageFile) -> None: + """Callback that is fired when an image file block is finished""" + + async def _emit_sse_event(self, event: AssistantStreamEvent) -> None: + self._current_event = event + await self.on_event(event) + + self.__current_message_snapshot, new_content = accumulate_event( + event=event, + current_message_snapshot=self.__current_message_snapshot, + ) + if self.__current_message_snapshot is not None: + self.__message_snapshots[self.__current_message_snapshot.id] = self.__current_message_snapshot + + accumulate_run_step( + event=event, + run_step_snapshots=self.__run_step_snapshots, + ) + + for content_delta in new_content: + assert self.__current_message_snapshot is not None + + block = self.__current_message_snapshot.content[content_delta.index] + if block.type == "text": + await self.on_text_created(block.text) + + if ( + event.event == "thread.run.completed" + or event.event == "thread.run.cancelled" + or event.event == "thread.run.expired" + or event.event == "thread.run.failed" + or event.event == "thread.run.requires_action" + or event.event == "thread.run.incomplete" + ): + self.__current_run = event.data + if self._current_tool_call: + await self.on_tool_call_done(self._current_tool_call) + elif ( + event.event == "thread.run.created" + or event.event == "thread.run.in_progress" + or event.event == "thread.run.cancelling" + or event.event == "thread.run.queued" + ): + self.__current_run = event.data + elif event.event == "thread.message.created": + await self.on_message_created(event.data) + elif event.event == "thread.message.delta": + snapshot = self.__current_message_snapshot + assert snapshot is not None + + message_delta = event.data.delta + if message_delta.content is not None: + for content_delta in message_delta.content: + if content_delta.type == "text" and content_delta.text: + snapshot_content = snapshot.content[content_delta.index] + assert snapshot_content.type == "text" + await self.on_text_delta(content_delta.text, snapshot_content.text) + + # If the delta is for a new message content: + # - emit on_text_done/on_image_file_done for the previous message content + # - emit on_text_created/on_image_created for the new message content + if content_delta.index != self._current_message_content_index: + if self._current_message_content is not None: + if self._current_message_content.type == "text": + await self.on_text_done(self._current_message_content.text) + elif self._current_message_content.type == "image_file": + await self.on_image_file_done(self._current_message_content.image_file) + + self._current_message_content_index = content_delta.index + self._current_message_content = snapshot.content[content_delta.index] + + # Update the current_message_content (delta event is correctly emitted already) + self._current_message_content = snapshot.content[content_delta.index] + + await self.on_message_delta(event.data.delta, snapshot) + elif event.event == "thread.message.completed" or event.event == "thread.message.incomplete": + self.__current_message_snapshot = event.data + self.__message_snapshots[event.data.id] = event.data + + if self._current_message_content_index is not None: + content = event.data.content[self._current_message_content_index] + if content.type == "text": + await self.on_text_done(content.text) + elif content.type == "image_file": + await self.on_image_file_done(content.image_file) + + await self.on_message_done(event.data) + elif event.event == "thread.run.step.created": + self.__current_run_step_id = event.data.id + await self.on_run_step_created(event.data) + elif event.event == "thread.run.step.in_progress": + self.__current_run_step_id = event.data.id + elif event.event == "thread.run.step.delta": + step_snapshot = self.__run_step_snapshots[event.data.id] + + run_step_delta = event.data.delta + if ( + run_step_delta.step_details + and run_step_delta.step_details.type == "tool_calls" + and run_step_delta.step_details.tool_calls is not None + ): + assert step_snapshot.step_details.type == "tool_calls" + for tool_call_delta in run_step_delta.step_details.tool_calls: + if tool_call_delta.index == self._current_tool_call_index: + await self.on_tool_call_delta( + tool_call_delta, + step_snapshot.step_details.tool_calls[tool_call_delta.index], + ) + + # If the delta is for a new tool call: + # - emit on_tool_call_done for the previous tool_call + # - emit on_tool_call_created for the new tool_call + if tool_call_delta.index != self._current_tool_call_index: + if self._current_tool_call is not None: + await self.on_tool_call_done(self._current_tool_call) + + self._current_tool_call_index = tool_call_delta.index + self._current_tool_call = step_snapshot.step_details.tool_calls[tool_call_delta.index] + await self.on_tool_call_created(self._current_tool_call) + + # Update the current_tool_call (delta event is correctly emitted already) + self._current_tool_call = step_snapshot.step_details.tool_calls[tool_call_delta.index] + + await self.on_run_step_delta( + event.data.delta, + step_snapshot, + ) + elif ( + event.event == "thread.run.step.completed" + or event.event == "thread.run.step.cancelled" + or event.event == "thread.run.step.expired" + or event.event == "thread.run.step.failed" + ): + if self._current_tool_call: + await self.on_tool_call_done(self._current_tool_call) + + await self.on_run_step_done(event.data) + self.__current_run_step_id = None + elif event.event == "thread.created" or event.event == "thread.message.in_progress" or event.event == "error": + # currently no special handling + ... + else: + # we only want to error at build-time + if TYPE_CHECKING: # type: ignore[unreachable] + assert_never(event) + + self._current_event = None + + async def __stream__(self) -> AsyncIterator[AssistantStreamEvent]: + stream = self.__stream + if not stream: + raise RuntimeError("Stream has not been started yet") + + try: + async for event in _aiter_events(stream): + await self._emit_sse_event(event) + + yield event + except _timeout_exceptions() as exc: + await self.on_timeout() + await self.on_exception(exc) + raise + except Exception as exc: + await self.on_exception(exc) + raise + finally: + await self.on_end() + + +AsyncAssistantEventHandlerT = TypeVar("AsyncAssistantEventHandlerT", bound=AsyncAssistantEventHandler) + + +class AsyncAssistantStreamManager(Generic[AsyncAssistantEventHandlerT]): + """Wrapper over AsyncAssistantStreamEventHandler that is returned by `.stream()` + so that an async context manager can be used without `await`ing the + original client call. + + ```py + async with client.threads.create_and_run_stream(...) as stream: + async for event in stream: + ... + ``` + """ + + def __init__( + self, + api_request: Awaitable[AsyncStream[AssistantStreamEvent]], + *, + event_handler: AsyncAssistantEventHandlerT, + ) -> None: + self.__stream: AsyncStream[AssistantStreamEvent] | None = None + self.__event_handler = event_handler + self.__api_request = api_request + + async def __aenter__(self) -> AsyncAssistantEventHandlerT: + self.__stream = await self.__api_request + self.__event_handler._init(self.__stream) + return self.__event_handler + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__stream is not None: + await self.__stream.close() + + +def accumulate_run_step( + *, + event: AssistantStreamEvent, + run_step_snapshots: dict[str, RunStep], +) -> None: + if event.event == "thread.run.step.created": + run_step_snapshots[event.data.id] = event.data + return + + if event.event == "thread.run.step.delta": + data = event.data + snapshot = run_step_snapshots[data.id] + + if data.delta: + merged = accumulate_delta( + cast( + "dict[object, object]", + model_dump(snapshot, exclude_unset=True, warnings=False), + ), + cast( + "dict[object, object]", + model_dump(data.delta, exclude_unset=True, warnings=False), + ), + ) + run_step_snapshots[snapshot.id] = cast(RunStep, construct_type(type_=RunStep, value=merged)) + + return None + + +def accumulate_event( + *, + event: AssistantStreamEvent, + current_message_snapshot: Message | None, +) -> tuple[Message | None, list[MessageContentDelta]]: + """Returns a tuple of message snapshot and newly created text message deltas""" + if event.event == "thread.message.created": + return event.data, [] + + new_content: list[MessageContentDelta] = [] + + if event.event != "thread.message.delta": + return current_message_snapshot, [] + + if not current_message_snapshot: + raise RuntimeError("Encountered a message delta with no previous snapshot") + + data = event.data + if data.delta.content: + for content_delta in data.delta.content: + try: + block = current_message_snapshot.content[content_delta.index] + except IndexError: + current_message_snapshot.content.insert( + content_delta.index, + cast( + MessageContent, + construct_type( + # mypy doesn't allow Content for some reason + type_=cast(Any, MessageContent), + value=model_dump(content_delta, exclude_unset=True, warnings=False), + ), + ), + ) + new_content.append(content_delta) + else: + merged = accumulate_delta( + cast( + "dict[object, object]", + model_dump(block, exclude_unset=True, warnings=False), + ), + cast( + "dict[object, object]", + model_dump(content_delta, exclude_unset=True, warnings=False), + ), + ) + current_message_snapshot.content[content_delta.index] = cast( + MessageContent, + construct_type( + # mypy doesn't allow Content for some reason + type_=cast(Any, MessageContent), + value=merged, + ), + ) + + return current_message_snapshot, new_content diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/_deltas.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/_deltas.py new file mode 100644 index 0000000..dc80325 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/_deltas.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from ..._utils import is_dict, is_list + + +def _has_indexed_entries(value: object) -> bool: + return is_list(value) and any(is_dict(entry) and "index" in entry for entry in value) + + +def accumulate_delta(acc: dict[object, object], delta: dict[object, object]) -> dict[object, object]: + for key, delta_value in delta.items(): + if key not in acc or acc[key] is None: + if _has_indexed_entries(delta_value): + # Even the first indexed list must be merged: one chunk can + # contain several fragments for the same index. + acc[key] = [] + else: + acc[key] = delta_value + continue + + acc_value = acc[key] + + # the `index` property is used in arrays of objects so it should + # not be accumulated like other values e.g. + # [{'foo': 'bar', 'index': 0}] + # + # the same applies to `type` properties as they're used for + # discriminated unions + if key == "index" or key == "type": + acc[key] = delta_value + continue + + if isinstance(acc_value, str) and isinstance(delta_value, str): + acc_value += delta_value + elif isinstance(acc_value, (int, float)) and isinstance(delta_value, (int, float)): + acc_value += delta_value + elif is_dict(acc_value) and is_dict(delta_value): + acc_value = accumulate_delta(acc_value, delta_value) + elif is_list(acc_value) and is_list(delta_value): + # for lists of non-dictionary items we'll only ever get new entries + # in the array, existing entries will never be changed + # An empty accumulator cannot tell us whether this is an indexed + # list. Inspect the delta before taking the append-only path. + if all(isinstance(x, (str, int, float)) for x in acc_value) and ( + acc_value or not _has_indexed_entries(delta_value) + ): + acc_value.extend(delta_value) + continue + + for delta_entry in delta_value: + if not is_dict(delta_entry): + raise TypeError(f"Unexpected list delta entry is not a dictionary: {delta_entry}") + + try: + index = delta_entry["index"] + except KeyError as exc: + raise RuntimeError(f"Expected list delta entry to have an `index` key; {delta_entry}") from exc + + if not isinstance(index, int): + raise TypeError(f"Unexpected, list delta entry `index` value is not an integer; {index}") + + try: + acc_entry = acc_value[index] + except IndexError: + acc_value.insert(index, delta_entry) + else: + if not is_dict(acc_entry): + raise TypeError("not handled yet") + + acc_value[index] = accumulate_delta(acc_entry, delta_entry) + + acc[key] = acc_value + + return acc diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__init__.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__init__.py new file mode 100644 index 0000000..c7f4a27 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__init__.py @@ -0,0 +1,2 @@ +from ._types import ToolOutput as ToolOutput, ToolHandler as ToolHandler, AsyncToolHandler as AsyncToolHandler +from ._streams import AgentSessionStream as AgentSessionStream, AsyncAgentSessionStream as AsyncAgentSessionStream diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..cc7f09c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__pycache__/_streams.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__pycache__/_streams.cpython-312.pyc new file mode 100644 index 0000000..e4de6aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__pycache__/_streams.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__pycache__/_tools.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__pycache__/_tools.cpython-312.pyc new file mode 100644 index 0000000..e94511e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__pycache__/_tools.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__pycache__/_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__pycache__/_types.cpython-312.pyc new file mode 100644 index 0000000..08749c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/__pycache__/_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/_streams.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/_streams.py new file mode 100644 index 0000000..9edb22a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/_streams.py @@ -0,0 +1,362 @@ +from __future__ import annotations + +import inspect +from copy import deepcopy +from uuid import uuid4 +from types import TracebackType +from typing import TYPE_CHECKING, Mapping, Iterable, Iterator, AsyncIterator +from collections import deque +from typing_extensions import Self, TypedDict + +import httpx2 + +from ._tools import arguments, failed_event, result_event, is_pending_call_race +from ._types import ToolHandler, AsyncToolHandler +from ...._types import Omit, Headers, NotGiven, omit, not_given +from ...._streaming import Stream, AsyncStream +from ...._exceptions import BadRequestError +from ....types.beta.agent_session_event import AgentSessionEvent +from ....types.beta.agent_function_call_item import AgentFunctionCallItem +from ....types.beta.agent_session_input_param import ( + SessionInputParamAgentSessionInputMessage, + SessionInputParamAgentSessionInputToolResult, +) +from ....types.beta.agent_session_input_message_param import AgentSessionInputMessageParam + +if TYPE_CHECKING: + from ....resources.beta.agents.sessions.sessions import Sessions, AsyncSessions + + +class _RequestOptions(TypedDict): + extra_headers: Headers | None + timeout: float | httpx2.Timeout | None | NotGiven + + +def _request_options(options: _RequestOptions) -> _RequestOptions: + headers = options["extra_headers"] + return { + **options, + "extra_headers": {key: value for key, value in headers.items() if key.lower() != "idempotency-key"} + if headers is not None + else None, + } + + +def _input_key(idempotency_key: str | Omit, headers: Headers | None) -> str | Omit: + # As with other SDK methods, extra_headers overrides the named parameter. + # Consume it here so differing header capitalization cannot append a second key. + for key, value in (headers or {}).items(): + if key.lower() == "idempotency-key": + return value + return str(uuid4()) if isinstance(idempotency_key, Omit) else idempotency_key + + +def _input_event(input: str | Iterable[AgentSessionInputMessageParam]) -> SessionInputParamAgentSessionInputMessage: + messages: list[AgentSessionInputMessageParam] + if isinstance(input, str): + if not input: + raise ValueError("input must not be empty") + messages = [{"role": "user", "content": [{"type": "input_text", "text": input}]}] + else: + messages = list(input) + if not messages: + raise ValueError("input must not be empty") + return {"type": "agent.session.input.message", "input": messages} + + +_RECENT_EVENT_LIMIT = 1024 + + +class _TurnState: + def __init__(self) -> None: + self.turn_id: str | None = None + self.turn_ended = False + self.event_ids: set[str] = set() + self.recent_events: deque[str] = deque() + self.handled_calls: set[tuple[str, str]] = set() + + def accept(self, event: AgentSessionEvent) -> bool: + if event.event_id in self.event_ids: + return False + # Suppress recent duplicate deliveries without retaining every text delta. + # Tool calls are tracked separately for the full invocation. + if len(self.recent_events) == _RECENT_EVENT_LIMIT: + self.event_ids.remove(self.recent_events.popleft()) + self.recent_events.append(event.event_id) + self.event_ids.add(event.event_id) + if event.type == "agent.session.turn.created" and event.turn.subagent_id is None and self.turn_id is None: + self.turn_id = event.turn_id + if ( + event.type == "agent.session.turn.completed" + or event.type == "agent.session.turn.failed" + or event.type == "agent.session.turn.cancelled" + ): + if self.turn_id is not None and event.turn_id == self.turn_id: + self.turn_ended = True + return True + + def terminal(self, event: AgentSessionEvent) -> bool: + return event.type == "agent.session.failed" or (event.type == "agent.session.idle" and self.turn_ended) + + def call(self, event: AgentSessionEvent) -> AgentFunctionCallItem | None: + if event.type != "agent.session.turn.item.added" or event.item.type != "function_call": + return None + call = event.item + key = (call.turn_id, call.call_id) + if key in self.handled_calls: + return None + self.handled_calls.add(key) + return call + + +class AgentSessionStream: + """Submit input to an idle session and stream through the resulting turn's terminal session event. + + Use as a context manager. Only one caller may submit input to this session while + the helper runs: the input endpoint does not return a turn ID for correlating + concurrent writers. Initial idle events and subagent turn completions do not + end iteration. Failed/cancelled turns remain visible as events; an unexpected + end of the connection raises RuntimeError. Closing the stream does not cancel + the backend turn. + + Optional tool handlers run sequentially during iteration, after their call + event is yielded. Unregistered tools are left for the caller to handle. A + handler exception submits a generic failure result without exception text. + Input and tool submissions each use an idempotency key across retries. The + optional idempotency_key argument applies only to the input submission. + """ + + def __init__( + self, + sessions: Sessions, + session_id: str, + *, + input: str | Iterable[AgentSessionInputMessageParam], + tool_handlers: Mapping[str, ToolHandler] | None = None, + idempotency_key: str | Omit = omit, + extra_headers: Headers | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + self._sessions = sessions + self._session_id = session_id + self._input = _input_event(input) + self._handlers = dict(tool_handlers or {}) + self._idempotency_key = _input_key(idempotency_key, extra_headers) + self._options = _request_options({"extra_headers": extra_headers, "timeout": timeout}) + self._state = _TurnState() + self._stream: Stream[AgentSessionEvent] | None = None + self._iterator: Iterator[AgentSessionEvent] | None = None + self._entered = False + self._closed = False + + def __enter__(self) -> Self: + if self._entered or self._closed: + raise RuntimeError("An AgentSessionStream can only be entered once") + self._entered = True + session = self._sessions.retrieve(self._session_id, **self._options) + if session.status != "idle": + raise ValueError( + "sessions.stream requires an idle session; use sessions.events.stream to follow an active session" + ) + self._stream = self._sessions.events.stream(self._session_id, **self._options) + try: + self._sessions.events.create( + self._session_id, events=[self._input], idempotency_key=self._idempotency_key, **self._options + ) + except BaseException: + self.close() + raise + self._iterator = self._iterate() + return self + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + self.close() + + def __iter__(self) -> Self: + return self + + def __next__(self) -> AgentSessionEvent: + if self._closed: + raise StopIteration + if self._iterator is None: + raise RuntimeError("Use sessions.stream in a with block") + return next(self._iterator) + + def until_done(self) -> None: + """Consume the remaining events, including any registered tool calls.""" + for _ in self: + pass + + def close(self) -> None: + """Close the event connection without cancelling the backend turn.""" + self._closed = True + if self._stream is not None: + self._stream.close() + + def _iterate(self) -> Iterator[AgentSessionEvent]: + assert self._stream is not None + try: + for event in self._stream: + if not self._state.accept(event): + continue + terminal = self._state.terminal(event) + if terminal: + self.close() + # Capture routing and arguments before exposing the mutable event. + call = self._state.call(event) + handler = self._handlers.get(call.name) if call is not None else None + if handler is not None: + call = deepcopy(call) + yield event + if terminal or self._closed: + return + if call is not None and handler is not None: + try: + result = result_event(call, handler(arguments(call))) + except Exception: + result = failed_event(call) + self._submit_result(result) + raise RuntimeError("Session event stream ended before the turn reached idle or failed") + finally: + self.close() + + def _submit_result(self, result: SessionInputParamAgentSessionInputToolResult) -> None: + # Reuse one key for transport retries and the pending-call registration race. + # An input-specific key in extra_headers must not be reused for tool results. + idempotency_key = str(uuid4()) + for delay in (0.1, 0.3, 0.6, None): + try: + self._sessions.events.create( + self._session_id, events=[result], idempotency_key=idempotency_key, **self._options + ) + return + except BadRequestError as error: + if delay is None or not is_pending_call_race(error, result["call_id"]): + raise + self._sessions._sleep(delay) + + +class AsyncAgentSessionStream: + """Async counterpart of AgentSessionStream; use with ``async with``. + + Requires an idle session with a single input writer. Handlers may return a + value or an awaitable; synchronous handlers run inline, without a thread + pool. Cancellation propagates and closes the event connection. It does not + cancel the backend turn. + """ + + def __init__( + self, + sessions: AsyncSessions, + session_id: str, + *, + input: str | Iterable[AgentSessionInputMessageParam], + tool_handlers: Mapping[str, AsyncToolHandler] | None = None, + idempotency_key: str | Omit = omit, + extra_headers: Headers | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + self._sessions = sessions + self._session_id = session_id + self._input = _input_event(input) + self._handlers = dict(tool_handlers or {}) + self._idempotency_key = _input_key(idempotency_key, extra_headers) + self._options = _request_options({"extra_headers": extra_headers, "timeout": timeout}) + self._state = _TurnState() + self._stream: AsyncStream[AgentSessionEvent] | None = None + self._iterator: AsyncIterator[AgentSessionEvent] | None = None + self._entered = False + self._closed = False + + async def __aenter__(self) -> Self: + if self._entered or self._closed: + raise RuntimeError("An AsyncAgentSessionStream can only be entered once") + self._entered = True + session = await self._sessions.retrieve(self._session_id, **self._options) + if session.status != "idle": + raise ValueError( + "sessions.stream requires an idle session; use sessions.events.stream to follow an active session" + ) + self._stream = await self._sessions.events.stream(self._session_id, **self._options) + try: + await self._sessions.events.create( + self._session_id, events=[self._input], idempotency_key=self._idempotency_key, **self._options + ) + except BaseException: + await self.close() + raise + self._iterator = self._iterate() + return self + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + await self.close() + + def __aiter__(self) -> Self: + return self + + async def __anext__(self) -> AgentSessionEvent: + if self._closed: + raise StopAsyncIteration + if self._iterator is None: + raise RuntimeError("Use sessions.stream in an async with block") + return await self._iterator.__anext__() + + async def until_done(self) -> None: + """Consume the remaining events, including any registered tool calls.""" + async for _ in self: + pass + + async def close(self) -> None: + """Close the event connection without cancelling the backend turn.""" + self._closed = True + if self._stream is not None: + await self._stream.close() + + async def _iterate(self) -> AsyncIterator[AgentSessionEvent]: + assert self._stream is not None + try: + async for event in self._stream: + if not self._state.accept(event): + continue + terminal = self._state.terminal(event) + if terminal: + await self.close() + # Capture routing and arguments before exposing the mutable event. + call = self._state.call(event) + handler = self._handlers.get(call.name) if call is not None else None + if handler is not None: + call = deepcopy(call) + yield event + if terminal or self._closed: + return + if call is not None and handler is not None: + try: + output = handler(arguments(call)) + if inspect.isawaitable(output): + output = await output + result = result_event(call, output) + except Exception: + result = failed_event(call) + await self._submit_result(result) + raise RuntimeError("Session event stream ended before the turn reached idle or failed") + finally: + await self.close() + + async def _submit_result(self, result: SessionInputParamAgentSessionInputToolResult) -> None: + # Reuse one key for transport retries and the pending-call registration race. + # An input-specific key in extra_headers must not be reused for tool results. + idempotency_key = str(uuid4()) + for delay in (0.1, 0.3, 0.6, None): + try: + await self._sessions.events.create( + self._session_id, events=[result], idempotency_key=idempotency_key, **self._options + ) + return + except BadRequestError as error: + if delay is None or not is_pending_call_race(error, result["call_id"]): + raise + await self._sessions._sleep(delay) diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/_tools.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/_tools.py new file mode 100644 index 0000000..f252436 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/_tools.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +import json +from copy import deepcopy +from typing import Any, Dict, Mapping, cast + +from ._types import ToolOutput +from ...._exceptions import BadRequestError +from ....types.beta.agent_function_call_item import AgentFunctionCallItem +from ....types.beta.agent_session_input_param import SessionInputParamAgentSessionInputToolResult +from ....types.beta.agent_function_call_output_param import AgentFunctionCallOutputParam + + +def arguments(call: AgentFunctionCallItem) -> Dict[str, Any]: + value = json.loads(call.arguments) if isinstance(call.arguments, str) else deepcopy(call.arguments) + if not isinstance(value, dict): + raise ValueError("Function arguments must be a JSON object") + return cast(Dict[str, Any], value) + + +def result_event(call: AgentFunctionCallItem, output: ToolOutput) -> SessionInputParamAgentSessionInputToolResult: + if isinstance(output, Mapping): + output = json.dumps(dict(output), separators=(",", ":")) + # Materialize iterators here so serialization failures produce a failed tool result. + if output is not None and not isinstance(output, str): + output = list(output) + return { + "type": "agent.session.input.tool_result", + "turn_id": call.turn_id, + "call_id": call.call_id, + "success": True, + "output": cast(AgentFunctionCallOutputParam, output) if output is not None else None, + } + + +def failed_event(call: AgentFunctionCallItem) -> SessionInputParamAgentSessionInputToolResult: + return { + "type": "agent.session.input.tool_result", + "turn_id": call.turn_id, + "call_id": call.call_id, + "success": False, + # Do not send exception text, which may include application secrets, to the model. + "error": "Tool handler failed.", + } + + +def is_pending_call_race(error: BadRequestError, call_id: str) -> bool: + # The item event can arrive before the server registers the pending call. + # Match only this known registration race; other HTTP failures propagate. + raw_body = error.body + body = cast(Dict[str, object], raw_body) if isinstance(raw_body, dict) else {} + return ( + body.get("code") == "invalid_request_error" and body.get("message") == f"Unknown pending tool call: {call_id}" + ) diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/_types.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/_types.py new file mode 100644 index 0000000..3658c47 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/agents/_types.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from typing import Any, Dict, Union, Mapping, Callable, Iterable, Awaitable +from typing_extensions import TypeAlias + +from ....types.beta.input_content_param import InputContentParam + +ToolOutput: TypeAlias = Union[str, Mapping[str, Any], Iterable[InputContentParam], None] +ToolHandler: TypeAlias = Callable[[Dict[str, Any]], ToolOutput] +AsyncToolHandler: TypeAlias = Callable[[Dict[str, Any]], Union[ToolOutput, Awaitable[ToolOutput]]] diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__init__.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__init__.py new file mode 100644 index 0000000..dfa3f3f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__init__.py @@ -0,0 +1,27 @@ +from ._types import ( + ParsedChoiceSnapshot as ParsedChoiceSnapshot, + ParsedChatCompletionSnapshot as ParsedChatCompletionSnapshot, + ParsedChatCompletionMessageSnapshot as ParsedChatCompletionMessageSnapshot, +) +from ._events import ( + ChunkEvent as ChunkEvent, + ContentDoneEvent as ContentDoneEvent, + RefusalDoneEvent as RefusalDoneEvent, + ContentDeltaEvent as ContentDeltaEvent, + RefusalDeltaEvent as RefusalDeltaEvent, + LogprobsContentDoneEvent as LogprobsContentDoneEvent, + LogprobsRefusalDoneEvent as LogprobsRefusalDoneEvent, + ChatCompletionStreamEvent as ChatCompletionStreamEvent, + LogprobsContentDeltaEvent as LogprobsContentDeltaEvent, + LogprobsRefusalDeltaEvent as LogprobsRefusalDeltaEvent, + ParsedChatCompletionSnapshot as ParsedChatCompletionSnapshot, + FunctionToolCallArgumentsDoneEvent as FunctionToolCallArgumentsDoneEvent, + FunctionToolCallArgumentsDeltaEvent as FunctionToolCallArgumentsDeltaEvent, +) +from ._completions import ( + ChatCompletionStream as ChatCompletionStream, + AsyncChatCompletionStream as AsyncChatCompletionStream, + ChatCompletionStreamState as ChatCompletionStreamState, + ChatCompletionStreamManager as ChatCompletionStreamManager, + AsyncChatCompletionStreamManager as AsyncChatCompletionStreamManager, +) diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0f2953d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__pycache__/_completions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__pycache__/_completions.cpython-312.pyc new file mode 100644 index 0000000..f9c4713 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__pycache__/_completions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__pycache__/_events.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__pycache__/_events.cpython-312.pyc new file mode 100644 index 0000000..ff9d5a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__pycache__/_events.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__pycache__/_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__pycache__/_types.cpython-312.pyc new file mode 100644 index 0000000..7ca7856 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/__pycache__/_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/_completions.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/_completions.py new file mode 100644 index 0000000..3476314 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/_completions.py @@ -0,0 +1,774 @@ +from __future__ import annotations + +import inspect +from types import TracebackType +from typing import TYPE_CHECKING, Any, Generic, Callable, Iterable, Awaitable, AsyncIterator, cast +from typing_extensions import Self, Iterator, assert_never + +from jiter import from_json + +from ._types import ParsedChoiceSnapshot, ParsedChatCompletionSnapshot, ParsedChatCompletionMessageSnapshot +from ._events import ( + ChunkEvent, + ContentDoneEvent, + RefusalDoneEvent, + ContentDeltaEvent, + RefusalDeltaEvent, + LogprobsContentDoneEvent, + LogprobsRefusalDoneEvent, + ChatCompletionStreamEvent, + LogprobsContentDeltaEvent, + LogprobsRefusalDeltaEvent, + FunctionToolCallArgumentsDoneEvent, + FunctionToolCallArgumentsDeltaEvent, +) +from .._deltas import accumulate_delta +from ...._types import Omit, IncEx, omit +from ...._utils import is_given, consume_sync_iterator, consume_async_iterator +from ...._compat import model_dump +from ...._models import build, construct_type +from ..._parsing import ( + ResponseFormatT, + has_parseable_input, + maybe_parse_content, + parse_chat_completion, + get_input_tool_by_name, + parse_function_tool_arguments, +) +from ...._streaming import Stream, AsyncStream +from ....types.chat import ChatCompletionChunk, ParsedChatCompletion, ChatCompletionToolUnionParam +from ...._exceptions import LengthFinishReasonError, ContentFilterFinishReasonError +from ....types.chat.chat_completion import Moderation, ChoiceLogprobs +from ....types.chat.chat_completion_chunk import Choice as ChoiceChunk +from ....types.chat.completion_create_params import ResponseFormat as ResponseFormatParam + + +class ChatCompletionStream(Generic[ResponseFormatT]): + """Wrapper over the Chat Completions streaming API that adds helpful + events such as `content.done`, supports automatically parsing + responses & tool calls and accumulates a `ChatCompletion` object + from each individual chunk. + + https://platform.openai.com/docs/api-reference/streaming + """ + + def __init__( + self, + *, + raw_stream: Stream[ChatCompletionChunk], + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit, + ) -> None: + self._raw_stream = raw_stream + self._response = raw_stream.response + self._iterator = self.__stream__() + self._state = ChatCompletionStreamState(response_format=response_format, input_tools=input_tools) + + def __next__(self) -> ChatCompletionStreamEvent[ResponseFormatT]: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[ChatCompletionStreamEvent[ResponseFormatT]]: + for item in self._iterator: + yield item + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self._response.close() + + def get_final_completion(self) -> ParsedChatCompletion[ResponseFormatT]: + """Waits until the stream has been read to completion and returns + the accumulated `ParsedChatCompletion` object. + + If you passed a class type to `.stream()`, the `completion.choices[0].message.parsed` + property will be the content deserialised into that class, if there was any content returned + by the API. + """ + self.until_done() + return self._state.get_final_completion() + + def until_done(self) -> Self: + """Blocks until the stream has been consumed.""" + consume_sync_iterator(self) + return self + + @property + def current_completion_snapshot(self) -> ParsedChatCompletionSnapshot: + return self._state.current_completion_snapshot + + def __stream__(self) -> Iterator[ChatCompletionStreamEvent[ResponseFormatT]]: + for sse_event in self._raw_stream: + if not _is_valid_chat_completion_chunk_weak(sse_event): + continue + events_to_fire = self._state.handle_chunk(sse_event) + for event in events_to_fire: + yield event + + +class ChatCompletionStreamManager(Generic[ResponseFormatT]): + """Context manager over a `ChatCompletionStream` that is returned by `.stream()`. + + This context manager ensures the response cannot be leaked if you don't read + the stream to completion. + + Usage: + ```py + with client.chat.completions.stream(...) as stream: + for event in stream: + ... + ``` + """ + + def __init__( + self, + api_request: Callable[[], Stream[ChatCompletionChunk]], + *, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit, + ) -> None: + self.__stream: ChatCompletionStream[ResponseFormatT] | None = None + self.__api_request = api_request + self.__response_format = response_format + self.__input_tools = input_tools + + def __enter__(self) -> ChatCompletionStream[ResponseFormatT]: + raw_stream = self.__api_request() + + self.__stream = ChatCompletionStream( + raw_stream=raw_stream, + response_format=self.__response_format, + input_tools=self.__input_tools, + ) + + return self.__stream + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__stream is not None: + self.__stream.close() + + +class AsyncChatCompletionStream(Generic[ResponseFormatT]): + """Wrapper over the Chat Completions streaming API that adds helpful + events such as `content.done`, supports automatically parsing + responses & tool calls and accumulates a `ChatCompletion` object + from each individual chunk. + + https://platform.openai.com/docs/api-reference/streaming + """ + + def __init__( + self, + *, + raw_stream: AsyncStream[ChatCompletionChunk], + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit, + ) -> None: + self._raw_stream = raw_stream + self._response = raw_stream.response + self._iterator = self.__stream__() + self._state = ChatCompletionStreamState(response_format=response_format, input_tools=input_tools) + + async def __anext__(self) -> ChatCompletionStreamEvent[ResponseFormatT]: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[ChatCompletionStreamEvent[ResponseFormatT]]: + async for item in self._iterator: + yield item + + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self._response.aclose() + + async def get_final_completion(self) -> ParsedChatCompletion[ResponseFormatT]: + """Waits until the stream has been read to completion and returns + the accumulated `ParsedChatCompletion` object. + + If you passed a class type to `.stream()`, the `completion.choices[0].message.parsed` + property will be the content deserialised into that class, if there was any content returned + by the API. + """ + await self.until_done() + return self._state.get_final_completion() + + async def until_done(self) -> Self: + """Blocks until the stream has been consumed.""" + await consume_async_iterator(self) + return self + + @property + def current_completion_snapshot(self) -> ParsedChatCompletionSnapshot: + return self._state.current_completion_snapshot + + async def __stream__(self) -> AsyncIterator[ChatCompletionStreamEvent[ResponseFormatT]]: + async for sse_event in self._raw_stream: + if not _is_valid_chat_completion_chunk_weak(sse_event): + continue + events_to_fire = self._state.handle_chunk(sse_event) + for event in events_to_fire: + yield event + + +class AsyncChatCompletionStreamManager(Generic[ResponseFormatT]): + """Context manager over a `AsyncChatCompletionStream` that is returned by `.stream()`. + + This context manager ensures the response cannot be leaked if you don't read + the stream to completion. + + Usage: + ```py + async with client.chat.completions.stream(...) as stream: + for event in stream: + ... + ``` + """ + + def __init__( + self, + api_request: Awaitable[AsyncStream[ChatCompletionChunk]], + *, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit, + ) -> None: + self.__stream: AsyncChatCompletionStream[ResponseFormatT] | None = None + self.__api_request = api_request + self.__response_format = response_format + self.__input_tools = input_tools + + async def __aenter__(self) -> AsyncChatCompletionStream[ResponseFormatT]: + raw_stream = await self.__api_request + + self.__stream = AsyncChatCompletionStream( + raw_stream=raw_stream, + response_format=self.__response_format, + input_tools=self.__input_tools, + ) + + return self.__stream + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__stream is not None: + await self.__stream.close() + + +class ChatCompletionStreamState(Generic[ResponseFormatT]): + """Helper class for manually accumulating `ChatCompletionChunk`s into a final `ChatCompletion` object. + + This is useful in cases where you can't always use the `.stream()` method, e.g. + + ```py + from openai.lib.streaming.chat import ChatCompletionStreamState + + state = ChatCompletionStreamState() + + stream = client.chat.completions.create(..., stream=True) + for chunk in response: + state.handle_chunk(chunk) + + # can also access the accumulated `ChatCompletion` mid-stream + state.current_completion_snapshot + + print(state.get_final_completion()) + ``` + """ + + def __init__( + self, + *, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit = omit, + ) -> None: + self.__current_completion_snapshot: ParsedChatCompletionSnapshot | None = None + self.__choice_event_states: list[ChoiceEventState] = [] + + self._input_tools = [tool for tool in input_tools] if is_given(input_tools) else [] + self._response_format = response_format + self._rich_response_format: type | Omit = response_format if inspect.isclass(response_format) else omit + + def get_final_completion(self) -> ParsedChatCompletion[ResponseFormatT]: + """Parse the final completion object. + + Note this does not provide any guarantees that the stream has actually finished, you must + only call this method when the stream is finished. + """ + return parse_chat_completion( + chat_completion=self.current_completion_snapshot, + response_format=self._rich_response_format, + input_tools=self._input_tools, + ) + + @property + def current_completion_snapshot(self) -> ParsedChatCompletionSnapshot: + assert self.__current_completion_snapshot is not None + return self.__current_completion_snapshot + + def handle_chunk(self, chunk: ChatCompletionChunk) -> Iterable[ChatCompletionStreamEvent[ResponseFormatT]]: + """Accumulate a new chunk into the snapshot and returns an iterable of events to yield.""" + self.__current_completion_snapshot = self._accumulate_chunk(chunk) + + return self._build_events( + chunk=chunk, + completion_snapshot=self.__current_completion_snapshot, + ) + + def _get_choice_state(self, choice: ChoiceChunk) -> ChoiceEventState: + try: + return self.__choice_event_states[choice.index] + except IndexError: + choice_state = ChoiceEventState(input_tools=self._input_tools) + self.__choice_event_states.append(choice_state) + return choice_state + + def _accumulate_chunk(self, chunk: ChatCompletionChunk) -> ParsedChatCompletionSnapshot: + completion_snapshot = self.__current_completion_snapshot + + if completion_snapshot is None: + return _convert_initial_chunk_into_snapshot(chunk) + + for choice in chunk.choices: + try: + choice_snapshot = completion_snapshot.choices[choice.index] + previous_tool_calls = choice_snapshot.message.tool_calls or [] + + choice_snapshot.message = cast( + ParsedChatCompletionMessageSnapshot, + construct_type( + type_=ParsedChatCompletionMessageSnapshot, + value=accumulate_delta( + cast( + "dict[object, object]", + model_dump( + choice_snapshot.message, + # we don't want to serialise / deserialise our custom properties + # as they won't appear in the delta and we don't want to have to + # continuously reparse the content + exclude=cast( + # cast required as mypy isn't smart enough to infer `True` here to `Literal[True]` + IncEx, + { + "parsed": True, + "tool_calls": { + idx: {"function": {"parsed_arguments": True}} + for idx, _ in enumerate(choice_snapshot.message.tool_calls or []) + }, + }, + ), + ), + ), + cast("dict[object, object]", choice.delta.to_dict()), + ), + ), + ) + + # ensure tools that have already been parsed are added back into the newly + # constructed message snapshot + for tool_index, prev_tool in enumerate(previous_tool_calls): + new_tool = (choice_snapshot.message.tool_calls or [])[tool_index] + + if prev_tool.type == "function": + assert new_tool.type == "function" + new_tool.function.parsed_arguments = prev_tool.function.parsed_arguments + elif TYPE_CHECKING: # type: ignore[unreachable] + assert_never(prev_tool) + except IndexError: + choice_snapshot = cast( + ParsedChoiceSnapshot, + construct_type( + type_=ParsedChoiceSnapshot, + value={ + **choice.model_dump(exclude_unset=True, exclude={"delta"}), + "message": accumulate_delta({}, cast("dict[object, object]", choice.delta.to_dict())), + }, + ), + ) + completion_snapshot.choices.append(choice_snapshot) + + if choice.finish_reason: + choice_snapshot.finish_reason = choice.finish_reason + + if has_parseable_input(response_format=self._response_format, input_tools=self._input_tools): + if choice.finish_reason == "length": + # at the time of writing, `.usage` will always be `None` but + # we include it here in case that is changed in the future + raise LengthFinishReasonError(completion=completion_snapshot) + + if choice.finish_reason == "content_filter": + raise ContentFilterFinishReasonError(completion=completion_snapshot) + + if ( + choice_snapshot.message.content + and not choice_snapshot.message.refusal + and is_given(self._rich_response_format) + # partial parsing fails on white-space + and choice_snapshot.message.content.lstrip() + ): + choice_snapshot.message.parsed = from_json( + bytes(choice_snapshot.message.content, "utf-8"), + partial_mode=True, + ) + + for tool_call_chunk in choice.delta.tool_calls or []: + tool_call_snapshot = (choice_snapshot.message.tool_calls or [])[tool_call_chunk.index] + + if tool_call_snapshot.type == "function": + input_tool = get_input_tool_by_name( + input_tools=self._input_tools, name=tool_call_snapshot.function.name + ) + + if ( + input_tool + and input_tool.get("function", {}).get("strict") + and tool_call_snapshot.function.arguments + ): + tool_call_snapshot.function.parsed_arguments = from_json( + bytes(tool_call_snapshot.function.arguments, "utf-8"), + partial_mode=True, + ) + elif TYPE_CHECKING: # type: ignore[unreachable] + assert_never(tool_call_snapshot) + + if choice.logprobs is not None: + if choice_snapshot.logprobs is None: + choice_snapshot.logprobs = build( + ChoiceLogprobs, + content=choice.logprobs.content, + refusal=choice.logprobs.refusal, + ) + else: + if choice.logprobs.content: + if choice_snapshot.logprobs.content is None: + choice_snapshot.logprobs.content = [] + + choice_snapshot.logprobs.content.extend(choice.logprobs.content) + + if choice.logprobs.refusal: + if choice_snapshot.logprobs.refusal is None: + choice_snapshot.logprobs.refusal = [] + + choice_snapshot.logprobs.refusal.extend(choice.logprobs.refusal) + + completion_snapshot.usage = chunk.usage + completion_snapshot.system_fingerprint = chunk.system_fingerprint + if chunk.moderation is not None: + completion_snapshot.moderation = cast( + Moderation, construct_type(type_=Moderation, value=chunk.moderation.to_dict()) + ) + + return completion_snapshot + + def _build_events( + self, + *, + chunk: ChatCompletionChunk, + completion_snapshot: ParsedChatCompletionSnapshot, + ) -> list[ChatCompletionStreamEvent[ResponseFormatT]]: + events_to_fire: list[ChatCompletionStreamEvent[ResponseFormatT]] = [] + + events_to_fire.append( + build(ChunkEvent, type="chunk", chunk=chunk, snapshot=completion_snapshot), + ) + + for choice in chunk.choices: + choice_state = self._get_choice_state(choice) + choice_snapshot = completion_snapshot.choices[choice.index] + + if choice.delta.content is not None and choice_snapshot.message.content is not None: + events_to_fire.append( + build( + ContentDeltaEvent, + type="content.delta", + delta=choice.delta.content, + snapshot=choice_snapshot.message.content, + parsed=choice_snapshot.message.parsed, + ) + ) + + if choice.delta.refusal is not None and choice_snapshot.message.refusal is not None: + events_to_fire.append( + build( + RefusalDeltaEvent, + type="refusal.delta", + delta=choice.delta.refusal, + snapshot=choice_snapshot.message.refusal, + ) + ) + + if choice.delta.tool_calls: + tool_calls = choice_snapshot.message.tool_calls + assert tool_calls is not None + + for tool_call_delta in choice.delta.tool_calls: + tool_call = tool_calls[tool_call_delta.index] + + if tool_call.type == "function": + assert tool_call_delta.function is not None + events_to_fire.append( + build( + FunctionToolCallArgumentsDeltaEvent, + type="tool_calls.function.arguments.delta", + name=tool_call.function.name, + index=tool_call_delta.index, + arguments=tool_call.function.arguments, + parsed_arguments=tool_call.function.parsed_arguments, + arguments_delta=tool_call_delta.function.arguments or "", + ) + ) + elif TYPE_CHECKING: # type: ignore[unreachable] + assert_never(tool_call) + + if choice.logprobs is not None and choice_snapshot.logprobs is not None: + if choice.logprobs.content and choice_snapshot.logprobs.content: + events_to_fire.append( + build( + LogprobsContentDeltaEvent, + type="logprobs.content.delta", + content=choice.logprobs.content, + snapshot=choice_snapshot.logprobs.content, + ), + ) + + if choice.logprobs.refusal and choice_snapshot.logprobs.refusal: + events_to_fire.append( + build( + LogprobsRefusalDeltaEvent, + type="logprobs.refusal.delta", + refusal=choice.logprobs.refusal, + snapshot=choice_snapshot.logprobs.refusal, + ), + ) + + events_to_fire.extend( + choice_state.get_done_events( + choice_chunk=choice, + choice_snapshot=choice_snapshot, + response_format=self._response_format, + ) + ) + + return events_to_fire + + +class ChoiceEventState: + def __init__(self, *, input_tools: list[ChatCompletionToolUnionParam]) -> None: + self._input_tools = input_tools + + self._content_done = False + self._refusal_done = False + self._logprobs_content_done = False + self._logprobs_refusal_done = False + self._done_tool_calls: set[int] = set() + self.__current_tool_call_index: int | None = None + + def get_done_events( + self, + *, + choice_chunk: ChoiceChunk, + choice_snapshot: ParsedChoiceSnapshot, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, + ) -> list[ChatCompletionStreamEvent[ResponseFormatT]]: + events_to_fire: list[ChatCompletionStreamEvent[ResponseFormatT]] = [] + + if choice_snapshot.finish_reason: + events_to_fire.extend( + self._content_done_events(choice_snapshot=choice_snapshot, response_format=response_format) + ) + + if ( + self.__current_tool_call_index is not None + and self.__current_tool_call_index not in self._done_tool_calls + ): + self._add_tool_done_event( + events_to_fire=events_to_fire, + choice_snapshot=choice_snapshot, + tool_index=self.__current_tool_call_index, + ) + + for tool_call in choice_chunk.delta.tool_calls or []: + if self.__current_tool_call_index != tool_call.index: + events_to_fire.extend( + self._content_done_events(choice_snapshot=choice_snapshot, response_format=response_format) + ) + + if self.__current_tool_call_index is not None: + self._add_tool_done_event( + events_to_fire=events_to_fire, + choice_snapshot=choice_snapshot, + tool_index=self.__current_tool_call_index, + ) + + self.__current_tool_call_index = tool_call.index + + return events_to_fire + + def _content_done_events( + self, + *, + choice_snapshot: ParsedChoiceSnapshot, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, + ) -> list[ChatCompletionStreamEvent[ResponseFormatT]]: + events_to_fire: list[ChatCompletionStreamEvent[ResponseFormatT]] = [] + + if choice_snapshot.message.content and not self._content_done: + self._content_done = True + + parsed = maybe_parse_content( + response_format=response_format, + message=choice_snapshot.message, + ) + + # update the parsed content to now use the richer `response_format` + # as opposed to the raw JSON-parsed object as the content is now + # complete and can be fully validated. + choice_snapshot.message.parsed = parsed + + events_to_fire.append( + build( + # we do this dance so that when the `ContentDoneEvent` instance + # is printed at runtime the class name will include the solved + # type variable, e.g. `ContentDoneEvent[MyModelType]` + cast( # pyright: ignore[reportUnnecessaryCast] + "type[ContentDoneEvent[ResponseFormatT]]", + cast(Any, ContentDoneEvent), + ), + type="content.done", + content=choice_snapshot.message.content, + parsed=parsed, + ), + ) + + if choice_snapshot.message.refusal is not None and not self._refusal_done: + self._refusal_done = True + events_to_fire.append( + build(RefusalDoneEvent, type="refusal.done", refusal=choice_snapshot.message.refusal), + ) + + if ( + choice_snapshot.logprobs is not None + and choice_snapshot.logprobs.content is not None + and not self._logprobs_content_done + ): + self._logprobs_content_done = True + events_to_fire.append( + build(LogprobsContentDoneEvent, type="logprobs.content.done", content=choice_snapshot.logprobs.content), + ) + + if ( + choice_snapshot.logprobs is not None + and choice_snapshot.logprobs.refusal is not None + and not self._logprobs_refusal_done + ): + self._logprobs_refusal_done = True + events_to_fire.append( + build(LogprobsRefusalDoneEvent, type="logprobs.refusal.done", refusal=choice_snapshot.logprobs.refusal), + ) + + return events_to_fire + + def _add_tool_done_event( + self, + *, + events_to_fire: list[ChatCompletionStreamEvent[ResponseFormatT]], + choice_snapshot: ParsedChoiceSnapshot, + tool_index: int, + ) -> None: + if tool_index in self._done_tool_calls: + return + + self._done_tool_calls.add(tool_index) + + assert choice_snapshot.message.tool_calls is not None + tool_call_snapshot = choice_snapshot.message.tool_calls[tool_index] + + if tool_call_snapshot.type == "function": + parsed_arguments = parse_function_tool_arguments( + input_tools=self._input_tools, function=tool_call_snapshot.function + ) + + # update the parsed content to potentially use a richer type + # as opposed to the raw JSON-parsed object as the content is now + # complete and can be fully validated. + tool_call_snapshot.function.parsed_arguments = parsed_arguments + + events_to_fire.append( + build( + FunctionToolCallArgumentsDoneEvent, + type="tool_calls.function.arguments.done", + index=tool_index, + name=tool_call_snapshot.function.name, + arguments=tool_call_snapshot.function.arguments, + parsed_arguments=parsed_arguments, + ) + ) + elif TYPE_CHECKING: # type: ignore[unreachable] + assert_never(tool_call_snapshot) + + +def _convert_initial_chunk_into_snapshot(chunk: ChatCompletionChunk) -> ParsedChatCompletionSnapshot: + data = chunk.to_dict() + data.pop("obfuscation", None) + choices = cast("list[object]", data["choices"]) + + for choice in chunk.choices: + choices[choice.index] = { + **choice.model_dump(exclude_unset=True, exclude={"delta"}), + "message": accumulate_delta({}, cast("dict[object, object]", choice.delta.to_dict())), + } + + return cast( + ParsedChatCompletionSnapshot, + construct_type( + type_=ParsedChatCompletionSnapshot, + value={ + "system_fingerprint": None, + **data, + "object": "chat.completion", + }, + ), + ) + + +def _is_valid_chat_completion_chunk_weak(sse_event: ChatCompletionChunk) -> bool: + # Although the _raw_stream is always supposed to contain only objects adhering to ChatCompletionChunk schema, + # this is broken by the Azure OpenAI in case of Asynchronous Filter enabled. + # An easy filter is to check for the "object" property: + # - should be "chat.completion.chunk" for a ChatCompletionChunk; + # - is an empty string for Asynchronous Filter events. + return sse_event.object == "chat.completion.chunk" # type: ignore # pylance reports this as a useless check diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/_events.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/_events.py new file mode 100644 index 0000000..d4c1f28 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/_events.py @@ -0,0 +1,123 @@ +from typing import List, Union, Generic, Optional +from typing_extensions import Literal + +from ._types import ParsedChatCompletionSnapshot +from ...._models import BaseModel, GenericModel +from ..._parsing import ResponseFormatT +from ....types.chat import ChatCompletionChunk, ChatCompletionTokenLogprob + + +class ChunkEvent(BaseModel): + type: Literal["chunk"] + + chunk: ChatCompletionChunk + + snapshot: ParsedChatCompletionSnapshot + + +class ContentDeltaEvent(BaseModel): + """This event is yielded for every chunk with `choice.delta.content` data.""" + + type: Literal["content.delta"] + + delta: str + + snapshot: str + + parsed: Optional[object] = None + + +class ContentDoneEvent(GenericModel, Generic[ResponseFormatT]): + type: Literal["content.done"] + + content: str + + parsed: Optional[ResponseFormatT] = None + + +class RefusalDeltaEvent(BaseModel): + type: Literal["refusal.delta"] + + delta: str + + snapshot: str + + +class RefusalDoneEvent(BaseModel): + type: Literal["refusal.done"] + + refusal: str + + +class FunctionToolCallArgumentsDeltaEvent(BaseModel): + type: Literal["tool_calls.function.arguments.delta"] + + name: str + + index: int + + arguments: str + """Accumulated raw JSON string""" + + parsed_arguments: object + """The parsed arguments so far""" + + arguments_delta: str + """The JSON string delta""" + + +class FunctionToolCallArgumentsDoneEvent(BaseModel): + type: Literal["tool_calls.function.arguments.done"] + + name: str + + index: int + + arguments: str + """Accumulated raw JSON string""" + + parsed_arguments: object + """The parsed arguments""" + + +class LogprobsContentDeltaEvent(BaseModel): + type: Literal["logprobs.content.delta"] + + content: List[ChatCompletionTokenLogprob] + + snapshot: List[ChatCompletionTokenLogprob] + + +class LogprobsContentDoneEvent(BaseModel): + type: Literal["logprobs.content.done"] + + content: List[ChatCompletionTokenLogprob] + + +class LogprobsRefusalDeltaEvent(BaseModel): + type: Literal["logprobs.refusal.delta"] + + refusal: List[ChatCompletionTokenLogprob] + + snapshot: List[ChatCompletionTokenLogprob] + + +class LogprobsRefusalDoneEvent(BaseModel): + type: Literal["logprobs.refusal.done"] + + refusal: List[ChatCompletionTokenLogprob] + + +ChatCompletionStreamEvent = Union[ + ChunkEvent, + ContentDeltaEvent, + ContentDoneEvent[ResponseFormatT], + RefusalDeltaEvent, + RefusalDoneEvent, + FunctionToolCallArgumentsDeltaEvent, + FunctionToolCallArgumentsDoneEvent, + LogprobsContentDeltaEvent, + LogprobsContentDoneEvent, + LogprobsRefusalDeltaEvent, + LogprobsRefusalDoneEvent, +] diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/_types.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/_types.py new file mode 100644 index 0000000..4255289 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/chat/_types.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from typing_extensions import TypeAlias + +from ....types.chat import ParsedChoice, ParsedChatCompletion, ParsedChatCompletionMessage + +ParsedChatCompletionSnapshot: TypeAlias = ParsedChatCompletion[object] +"""Snapshot type representing an in-progress accumulation of +a `ParsedChatCompletion` object. +""" + +ParsedChatCompletionMessageSnapshot: TypeAlias = ParsedChatCompletionMessage[object] +"""Snapshot type representing an in-progress accumulation of +a `ParsedChatCompletionMessage` object. + +If the content has been fully accumulated, the `.parsed` content will be +the `response_format` instance, otherwise it'll be the raw JSON parsed version. +""" + +ParsedChoiceSnapshot: TypeAlias = ParsedChoice[object] diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__init__.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__init__.py new file mode 100644 index 0000000..ff07363 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__init__.py @@ -0,0 +1,13 @@ +from ._events import ( + ResponseTextDoneEvent as ResponseTextDoneEvent, + ResponseTextDeltaEvent as ResponseTextDeltaEvent, + ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, +) +from ._responses import ( + ResponseStream as ResponseStream, + AsyncResponseStream as AsyncResponseStream, + ResponseStreamEvent as ResponseStreamEvent, + ResponseStreamState as ResponseStreamState, + ResponseStreamManager as ResponseStreamManager, + AsyncResponseStreamManager as AsyncResponseStreamManager, +) diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6a1d4f9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__pycache__/_events.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__pycache__/_events.cpython-312.pyc new file mode 100644 index 0000000..6482515 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__pycache__/_events.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__pycache__/_responses.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__pycache__/_responses.cpython-312.pyc new file mode 100644 index 0000000..ba6397a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__pycache__/_responses.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__pycache__/_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__pycache__/_types.cpython-312.pyc new file mode 100644 index 0000000..f2c09a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/__pycache__/_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/_events.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/_events.py new file mode 100644 index 0000000..69e106e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/_events.py @@ -0,0 +1,160 @@ +from __future__ import annotations + +from typing import Optional +from typing_extensions import Union, Generic, TypeVar, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._compat import GenericModel +from ....types.responses import ( + ParsedResponse, + ResponseErrorEvent, + ResponseFailedEvent, + ResponseQueuedEvent, + ResponseCreatedEvent, + ResponseTextDoneEvent as RawResponseTextDoneEvent, + ResponseAudioDoneEvent, + ResponseCompletedEvent as RawResponseCompletedEvent, + ResponseTextDeltaEvent as RawResponseTextDeltaEvent, + ResponseAudioDeltaEvent, + ResponseIncompleteEvent, + ResponseInProgressEvent, + ResponseRefusalDoneEvent, + ResponseRefusalDeltaEvent, + ResponseMcpCallFailedEvent, + ResponseOutputItemDoneEvent, + ResponseContentPartDoneEvent, + ResponseOutputItemAddedEvent, + ResponseContentPartAddedEvent, + ResponseMcpCallCompletedEvent, + ResponseMcpCallInProgressEvent, + ResponseMcpListToolsFailedEvent, + ResponseAudioTranscriptDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseCompactionCompactingEvent, + ResponseMcpCallArgumentsDoneEvent, + ResponseShellCallCommandDoneEvent, + ResponseImageGenCallCompletedEvent, + ResponseMcpCallArgumentsDeltaEvent, + ResponseMcpListToolsCompletedEvent, + ResponseShellCallCommandAddedEvent, + ResponseShellCallCommandDeltaEvent, + ResponseImageGenCallGeneratingEvent, + ResponseImageGenCallInProgressEvent, + ResponseMcpListToolsInProgressEvent, + ResponseWebSearchCallCompletedEvent, + ResponseWebSearchCallSearchingEvent, + ResponseCustomToolCallInputDoneEvent, + ResponseFileSearchCallCompletedEvent, + ResponseFileSearchCallSearchingEvent, + ResponseWebSearchCallInProgressEvent, + ResponseCustomToolCallInputDeltaEvent, + ResponseFileSearchCallInProgressEvent, + ResponseImageGenCallPartialImageEvent, + ResponseReasoningSummaryPartDoneEvent, + ResponseReasoningSummaryTextDoneEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseOutputTextAnnotationAddedEvent, + ResponseReasoningSummaryPartAddedEvent, + ResponseReasoningSummaryTextDeltaEvent, + ResponseFunctionCallArgumentsDeltaEvent as RawResponseFunctionCallArgumentsDeltaEvent, + ResponseShellCallOutputContentDoneEvent, + ResponseCodeInterpreterCallCodeDoneEvent, + ResponseShellCallOutputContentDeltaEvent, + ResponseCodeInterpreterCallCodeDeltaEvent, + ResponseCodeInterpreterCallCompletedEvent, + ResponseCodeInterpreterCallInProgressEvent, + ResponseCodeInterpreterCallInterpretingEvent, +) +from ....types.responses.response_reasoning_text_done_event import ResponseReasoningTextDoneEvent +from ....types.responses.response_reasoning_text_delta_event import ResponseReasoningTextDeltaEvent + +TextFormatT = TypeVar( + "TextFormatT", + # if it isn't given then we don't do any parsing + default=None, +) + + +class ResponseTextDeltaEvent(RawResponseTextDeltaEvent): + snapshot: str + + +class ResponseTextDoneEvent(RawResponseTextDoneEvent, GenericModel, Generic[TextFormatT]): + parsed: Optional[TextFormatT] = None + + +class ResponseFunctionCallArgumentsDeltaEvent(RawResponseFunctionCallArgumentsDeltaEvent): + snapshot: str + + +class ResponseCompletedEvent(RawResponseCompletedEvent, GenericModel, Generic[TextFormatT]): + response: ParsedResponse[TextFormatT] # type: ignore[assignment] + + +ResponseStreamEvent: TypeAlias = Annotated[ + Union[ + # wrappers with snapshots added on + ResponseTextDeltaEvent, + ResponseTextDoneEvent[TextFormatT], + ResponseFunctionCallArgumentsDeltaEvent, + ResponseCompletedEvent[TextFormatT], + # the same as the non-accumulated API + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseCodeInterpreterCallCodeDeltaEvent, + ResponseCodeInterpreterCallCodeDoneEvent, + ResponseCodeInterpreterCallCompletedEvent, + ResponseCodeInterpreterCallInProgressEvent, + ResponseCodeInterpreterCallInterpretingEvent, + ResponseCompactionCompactingEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreatedEvent, + ResponseErrorEvent, + ResponseFileSearchCallCompletedEvent, + ResponseFileSearchCallInProgressEvent, + ResponseFileSearchCallSearchingEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseShellCallCommandAddedEvent, + ResponseShellCallCommandDeltaEvent, + ResponseShellCallCommandDoneEvent, + ResponseShellCallOutputContentDeltaEvent, + ResponseShellCallOutputContentDoneEvent, + ResponseInProgressEvent, + ResponseFailedEvent, + ResponseIncompleteEvent, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseRefusalDeltaEvent, + ResponseRefusalDoneEvent, + ResponseTextDoneEvent, + ResponseWebSearchCallCompletedEvent, + ResponseWebSearchCallInProgressEvent, + ResponseWebSearchCallSearchingEvent, + ResponseReasoningSummaryPartAddedEvent, + ResponseReasoningSummaryPartDoneEvent, + ResponseReasoningSummaryTextDeltaEvent, + ResponseReasoningSummaryTextDoneEvent, + ResponseImageGenCallCompletedEvent, + ResponseImageGenCallInProgressEvent, + ResponseImageGenCallGeneratingEvent, + ResponseImageGenCallPartialImageEvent, + ResponseMcpCallCompletedEvent, + ResponseMcpCallArgumentsDeltaEvent, + ResponseMcpCallArgumentsDoneEvent, + ResponseMcpCallFailedEvent, + ResponseMcpCallInProgressEvent, + ResponseMcpListToolsCompletedEvent, + ResponseMcpListToolsFailedEvent, + ResponseMcpListToolsInProgressEvent, + ResponseOutputTextAnnotationAddedEvent, + ResponseQueuedEvent, + ResponseReasoningTextDeltaEvent, + ResponseReasoningTextDoneEvent, + ResponseCustomToolCallInputDeltaEvent, + ResponseCustomToolCallInputDoneEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/_responses.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/_responses.py new file mode 100644 index 0000000..708de7b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/_responses.py @@ -0,0 +1,399 @@ +from __future__ import annotations + +import inspect +from types import TracebackType +from typing import Any, List, Generic, Iterable, Awaitable, cast +from typing_extensions import Self, Callable, Iterator, AsyncIterator + +from ._types import ParsedResponseSnapshot +from ._events import ( + ResponseStreamEvent, + ResponseTextDoneEvent, + ResponseCompletedEvent, + ResponseTextDeltaEvent, + ResponseFunctionCallArgumentsDeltaEvent, +) +from ...._types import Omit, omit +from ...._utils import is_given, consume_sync_iterator, consume_async_iterator +from ...._compat import model_copy +from ...._models import build, construct_type_unchecked +from ...._streaming import Stream, AsyncStream +from ....types.responses import ParsedResponse, ResponseStreamEvent as RawResponseStreamEvent +from ..._parsing._responses import TextFormatT, parse_text, parse_response +from ....types.responses.tool_param import ToolParam +from ....types.responses.parsed_response import ( + ParsedContent, + ParsedResponseOutputItem, + ParsedResponseOutputMessage, + ParsedResponseFunctionToolCall, +) +from ....types.responses.response_output_item import ResponseOutputItem + + +class ResponseStream(Generic[TextFormatT]): + def __init__( + self, + *, + raw_stream: Stream[RawResponseStreamEvent], + text_format: type[TextFormatT] | Omit, + input_tools: Iterable[ToolParam] | Omit, + starting_after: int | None, + ) -> None: + self._raw_stream = raw_stream + self._response = raw_stream.response + self._iterator = self.__stream__() + self._state = ResponseStreamState(text_format=text_format, input_tools=input_tools) + self._starting_after = starting_after + + def __next__(self) -> ResponseStreamEvent[TextFormatT]: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[ResponseStreamEvent[TextFormatT]]: + for item in self._iterator: + yield item + + def __enter__(self) -> Self: + return self + + def __stream__(self) -> Iterator[ResponseStreamEvent[TextFormatT]]: + for sse_event in self._raw_stream: + events_to_fire = self._state.handle_event(sse_event) + for event in events_to_fire: + if self._starting_after is None or event.sequence_number > self._starting_after: + yield event + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self._response.close() + + def get_final_response(self) -> ParsedResponse[TextFormatT]: + """Waits until the stream has been read to completion and returns + the accumulated `ParsedResponse` object. + """ + self.until_done() + response = self._state._completed_response + if not response: + raise RuntimeError("Didn't receive a `response.completed` event.") + + return response + + def until_done(self) -> Self: + """Blocks until the stream has been consumed.""" + consume_sync_iterator(self) + return self + + +class ResponseStreamManager(Generic[TextFormatT]): + def __init__( + self, + api_request: Callable[[], Stream[RawResponseStreamEvent]], + *, + text_format: type[TextFormatT] | Omit, + input_tools: Iterable[ToolParam] | Omit, + starting_after: int | None, + ) -> None: + self.__stream: ResponseStream[TextFormatT] | None = None + self.__api_request = api_request + self.__text_format = text_format + self.__input_tools = input_tools + self.__starting_after = starting_after + + def __enter__(self) -> ResponseStream[TextFormatT]: + raw_stream = self.__api_request() + + self.__stream = ResponseStream( + raw_stream=raw_stream, + text_format=self.__text_format, + input_tools=self.__input_tools, + starting_after=self.__starting_after, + ) + + return self.__stream + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__stream is not None: + self.__stream.close() + + +class AsyncResponseStream(Generic[TextFormatT]): + def __init__( + self, + *, + raw_stream: AsyncStream[RawResponseStreamEvent], + text_format: type[TextFormatT] | Omit, + input_tools: Iterable[ToolParam] | Omit, + starting_after: int | None, + ) -> None: + self._raw_stream = raw_stream + self._response = raw_stream.response + self._iterator = self.__stream__() + self._state = ResponseStreamState(text_format=text_format, input_tools=input_tools) + self._starting_after = starting_after + + async def __anext__(self) -> ResponseStreamEvent[TextFormatT]: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[ResponseStreamEvent[TextFormatT]]: + async for item in self._iterator: + yield item + + async def __stream__(self) -> AsyncIterator[ResponseStreamEvent[TextFormatT]]: + async for sse_event in self._raw_stream: + events_to_fire = self._state.handle_event(sse_event) + for event in events_to_fire: + if self._starting_after is None or event.sequence_number > self._starting_after: + yield event + + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self._response.aclose() + + async def get_final_response(self) -> ParsedResponse[TextFormatT]: + """Waits until the stream has been read to completion and returns + the accumulated `ParsedResponse` object. + """ + await self.until_done() + response = self._state._completed_response + if not response: + raise RuntimeError("Didn't receive a `response.completed` event.") + + return response + + async def until_done(self) -> Self: + """Blocks until the stream has been consumed.""" + await consume_async_iterator(self) + return self + + +class AsyncResponseStreamManager(Generic[TextFormatT]): + def __init__( + self, + api_request: Awaitable[AsyncStream[RawResponseStreamEvent]], + *, + text_format: type[TextFormatT] | Omit, + input_tools: Iterable[ToolParam] | Omit, + starting_after: int | None, + ) -> None: + self.__stream: AsyncResponseStream[TextFormatT] | None = None + self.__api_request = api_request + self.__text_format = text_format + self.__input_tools = input_tools + self.__starting_after = starting_after + + async def __aenter__(self) -> AsyncResponseStream[TextFormatT]: + raw_stream = await self.__api_request + + self.__stream = AsyncResponseStream( + raw_stream=raw_stream, + text_format=self.__text_format, + input_tools=self.__input_tools, + starting_after=self.__starting_after, + ) + + return self.__stream + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__stream is not None: + await self.__stream.close() + + +class ResponseStreamState(Generic[TextFormatT]): + def __init__( + self, + *, + input_tools: Iterable[ToolParam] | Omit, + text_format: type[TextFormatT] | Omit, + ) -> None: + self.__current_snapshot: ParsedResponseSnapshot | None = None + self._completed_response: ParsedResponse[TextFormatT] | None = None + self._completed_output: dict[int, ResponseOutputItem] = {} + self._output_items: dict[int, ParsedResponseOutputItem[object]] = {} + self._input_tools = [tool for tool in input_tools] if is_given(input_tools) else [] + self._text_format = text_format + self._rich_text_format: type | Omit = text_format if inspect.isclass(text_format) else omit + + def handle_event(self, event: RawResponseStreamEvent) -> List[ResponseStreamEvent[TextFormatT]]: + self.__current_snapshot = self.accumulate_event(event) + + events: List[ResponseStreamEvent[TextFormatT]] = [] + + if event.type == "response.output_text.delta": + output = self._get_output_item(event.output_index) + assert output.type == "message" + + content = output.content[event.content_index] + assert content.type == "output_text" + + events.append( + build( + ResponseTextDeltaEvent, + content_index=event.content_index, + delta=event.delta, + item_id=event.item_id, + output_index=event.output_index, + sequence_number=event.sequence_number, + logprobs=event.logprobs, + type="response.output_text.delta", + snapshot=content.text, + ) + ) + elif event.type == "response.output_text.done": + output = self._get_output_item(event.output_index) + assert output.type == "message" + + content = output.content[event.content_index] + assert content.type == "output_text" + + events.append( + build( + cast("type[ResponseTextDoneEvent[TextFormatT]]", ResponseTextDoneEvent), + content_index=event.content_index, + item_id=event.item_id, + output_index=event.output_index, + sequence_number=event.sequence_number, + logprobs=event.logprobs, + type="response.output_text.done", + text=event.text, + parsed=parse_text(event.text, text_format=self._text_format, phase=output.phase), + ) + ) + elif event.type == "response.function_call_arguments.delta": + output = self._get_output_item(event.output_index) + assert output.type == "function_call" + + events.append( + build( + ResponseFunctionCallArgumentsDeltaEvent, + delta=event.delta, + item_id=event.item_id, + output_index=event.output_index, + sequence_number=event.sequence_number, + type="response.function_call_arguments.delta", + snapshot=output.arguments, + ) + ) + + elif event.type == "response.completed": + response = self._completed_response + assert response is not None + + events.append( + build( + ResponseCompletedEvent, + sequence_number=event.sequence_number, + type="response.completed", + response=response, + ) + ) + else: + events.append(event) + + return events + + def accumulate_event(self, event: RawResponseStreamEvent) -> ParsedResponseSnapshot: + snapshot = self.__current_snapshot + if snapshot is None: + return self._create_initial_response(event) + + if event.type == "response.output_item.added": + if getattr(event, "item", None) is None: + return snapshot + + if event.item.type == "function_call": + snapshot.output.append( + construct_type_unchecked( + type_=cast(Any, ParsedResponseFunctionToolCall), value=event.item.to_dict() + ) + ) + elif event.item.type == "message": + snapshot.output.append( + construct_type_unchecked(type_=cast(Any, ParsedResponseOutputMessage), value=event.item.to_dict()) + ) + else: + snapshot.output.append(event.item) + self._output_items[event.output_index] = snapshot.output[-1] + elif event.type == "response.content_part.added": + output = self._get_output_item(event.output_index) + if output.type == "message": + output.content.append( + construct_type_unchecked(type_=cast(Any, ParsedContent), value=event.part.to_dict()) + ) + elif event.type == "response.output_text.delta": + output = self._get_output_item(event.output_index) + if output.type == "message": + content = output.content[event.content_index] + assert content.type == "output_text" + content.text += event.delta + elif event.type == "response.function_call_arguments.delta": + output = self._get_output_item(event.output_index) + if output.type == "function_call": + output.arguments += event.delta + elif event.type == "response.output_item.done": + self._completed_output[event.output_index] = event.item + elif event.type == "response.completed": + response = event.response + if getattr(response, "output", None) is None: + # Recover finalized items; deltas can omit final status, annotations, or refusals. + response = model_copy(response) + response.output = [self._completed_output[index] for index in sorted(self._completed_output)] + self._completed_response = parse_response( + text_format=self._text_format, + response=response, + input_tools=self._input_tools, + ) + + return snapshot + + def _get_output_item(self, output_index: int) -> ParsedResponseOutputItem[object]: + try: + return self._output_items[output_index] + except KeyError: + raise RuntimeError( + f"Received a content event for output index {output_index} before receiving its output item" + ) from None + + def _create_initial_response(self, event: RawResponseStreamEvent) -> ParsedResponseSnapshot: + if event.type != "response.created": + raise RuntimeError(f"Expected to have received `response.created` before `{event.type}`") + + snapshot = construct_type_unchecked(type_=ParsedResponseSnapshot, value=event.response.to_dict()) + # Stream indexes can have gaps when a provider emits an empty added event. + self._output_items = dict(enumerate(snapshot.output or [])) + return snapshot diff --git a/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/_types.py b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/_types.py new file mode 100644 index 0000000..6d3fd90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/lib/streaming/responses/_types.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from typing_extensions import TypeAlias + +from ....types.responses import ParsedResponse + +ParsedResponseSnapshot: TypeAlias = ParsedResponse[object] +"""Snapshot type representing an in-progress accumulation of +a `ParsedResponse` object. +""" diff --git a/venv/lib/python3.12/site-packages/openai/pagination.py b/venv/lib/python3.12/site-packages/openai/pagination.py new file mode 100644 index 0000000..4f376fe --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/pagination.py @@ -0,0 +1,308 @@ +from typing import Any, List, Generic, TypeVar, Optional, cast +from typing_extensions import Protocol, override, runtime_checkable + +from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage + +__all__ = [ + "SyncPage", + "AsyncPage", + "SyncCursorPage", + "AsyncCursorPage", + "SyncConversationCursorPage", + "AsyncConversationCursorPage", + "SyncNextCursorPage", + "AsyncNextCursorPage", + "SyncTokenPage", + "AsyncTokenPage", +] + +_T = TypeVar("_T") + + +@runtime_checkable +class CursorPageItem(Protocol): + id: Optional[str] + + +class SyncPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + """Note: no pagination actually occurs yet, this is for forwards-compatibility.""" + + data: List[_T] + object: str + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def next_page_info(self) -> None: + """ + This page represents a response that isn't actually paginated at the API level + so there will never be a next page. + """ + return None + + +class AsyncPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + """Note: no pagination actually occurs yet, this is for forwards-compatibility.""" + + data: List[_T] + object: str + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def next_page_info(self) -> None: + """ + This page represents a response that isn't actually paginated at the API level + so there will never be a next page. + """ + return None + + +class SyncCursorPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + has_more: Optional[bool] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + data = self.data + if not data: + return None + + item = cast(Any, data[-1]) + if not isinstance(item, CursorPageItem) or item.id is None: + # TODO emit warning log + return None + + return PageInfo(params={"after": item.id}) + + +class AsyncCursorPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + has_more: Optional[bool] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + data = self.data + if not data: + return None + + item = cast(Any, data[-1]) + if not isinstance(item, CursorPageItem) or item.id is None: + # TODO emit warning log + return None + + return PageInfo(params={"after": item.id}) + + +class SyncConversationCursorPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + has_more: Optional[bool] = None + last_id: Optional[str] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + last_id = self.last_id + if not last_id: + return None + + return PageInfo(params={"after": last_id}) + + +class AsyncConversationCursorPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + has_more: Optional[bool] = None + last_id: Optional[str] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + last_id = self.last_id + if not last_id: + return None + + return PageInfo(params={"after": last_id}) + + +class SyncNextCursorPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + has_more: Optional[bool] = None + next: Optional[str] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + next = self.next + if not next: + return None + + return PageInfo(params={"after": next}) + + +class AsyncNextCursorPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + has_more: Optional[bool] = None + next: Optional[str] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + next = self.next + if not next: + return None + + return PageInfo(params={"after": next}) + + +class SyncTokenPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + has_more: Optional[bool] = None + next: Optional[str] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + next = self.next + if not next: + return None + + return PageInfo(params={"page": next}) + + +class AsyncTokenPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + has_more: Optional[bool] = None + next: Optional[str] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + next = self.next + if not next: + return None + + return PageInfo(params={"page": next}) diff --git a/venv/lib/python3.12/site-packages/openai/providers/__init__.py b/venv/lib/python3.12/site-packages/openai/providers/__init__.py new file mode 100644 index 0000000..bb5bcdb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/providers/__init__.py @@ -0,0 +1,3 @@ +from .bedrock import bedrock as bedrock + +__all__ = ["bedrock"] diff --git a/venv/lib/python3.12/site-packages/openai/providers/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/providers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..de7276e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/providers/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/providers/__pycache__/bedrock.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/providers/__pycache__/bedrock.cpython-312.pyc new file mode 100644 index 0000000..1afd170 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/providers/__pycache__/bedrock.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/providers/bedrock.py b/venv/lib/python3.12/site-packages/openai/providers/bedrock.py new file mode 100644 index 0000000..1912df1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/providers/bedrock.py @@ -0,0 +1,502 @@ +from __future__ import annotations + +import os +import re +import inspect +from typing import Literal, Callable, Awaitable, cast +from dataclasses import field, dataclass + +import httpx2 + +from .._types import NOT_GIVEN, NotGiven +from .._utils import asyncify +from .._httpx2 import normalize_httpx_url, request_not_read_exceptions +from .._models import FinalRequestOptions +from .._provider import _Provider, _create_provider, _ProviderRuntime +from .._exceptions import OpenAIError +from ..lib._bedrock_auth import ( + BedrockAwsAuth, + BedrockAwsAuthConfig, + AwsCredentialsProvider, +) + +BedrockTokenProvider = Callable[[], "str | Awaitable[str]"] +BedrockEndpoint = Literal["mantle", "runtime"] + +_AWS_SIGNING_HEADERS = ("authorization", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token") +_AWS_REGION = re.compile(r"^[a-z]{2,8}(?:-[a-z0-9]+)+-\d+$") + + +def _normalize_optional_string(value: str | None) -> str | None: + if value is None: + return None + + normalized = value.strip() + return normalized or None + + +def _normalize_base_url(base_url: str | httpx2.URL) -> httpx2.URL: + url = normalize_httpx_url(base_url) + path = url.path.rstrip("/") + responses_match = re.search(r"/responses(?:/.*)?$", path) + if responses_match is not None: + path = path[: responses_match.start()] + + return url.copy_with(path=path or "/") + + +def _runtime_dns_suffixes(region: str) -> tuple[str, str]: + if region.startswith("cn-"): + return "amazonaws.com.cn", "api.amazonwebservices.com.cn" + if region.startswith("eusc-"): + return "amazonaws.eu", "api.amazonwebservices.eu" + if region.startswith("us-iso-"): + return "c2s.ic.gov", "api.aws.ic.gov" + if region.startswith("us-isob-"): + return "sc2s.sgov.gov", "api.aws.scloud" + if region.startswith("eu-isoe-"): + return "cloud.adc-e.uk", "api.cloud-aws.adc-e.uk" + if region.startswith("us-isof-"): + return "csp.hci.ic.gov", "api.aws.hci.ic.gov" + return "amazonaws.com", "api.aws" + + +def _parse_bedrock_endpoint_hostname(hostname: str) -> tuple[BedrockEndpoint, str] | None: + service, separator, remainder = hostname.removesuffix(".").lower().partition(".") + region, region_separator, suffix = remainder.partition(".") + if not separator or not region_separator or _AWS_REGION.fullmatch(region) is None: + return None + + if service == "bedrock-mantle" and suffix == "api.aws": + return "mantle", region + if service in {"bedrock-runtime", "bedrock-runtime-fips"} and suffix in _runtime_dns_suffixes(region): + return "runtime", region + return None + + +def _validate_bedrock_region(region: str | None) -> None: + if region is not None and _AWS_REGION.fullmatch(region) is None: + raise OpenAIError("The Bedrock AWS `region` is invalid. Use a standard AWS region such as `us-east-1`.") + + +def _validate_canonical_bedrock_endpoint( + base_url: httpx2.URL, *, endpoint: BedrockEndpoint, region: str | None +) -> None: + canonical_endpoint = _parse_bedrock_endpoint_hostname(base_url.host) + if canonical_endpoint is None: + return + + canonical_family, canonical_region = canonical_endpoint + if base_url.scheme != "https": + raise OpenAIError("Canonical Amazon Bedrock endpoints require HTTPS.") + if canonical_family != endpoint: + raise OpenAIError( + f"The Bedrock {canonical_family} hostname does not match the selected `{endpoint}` endpoint. " + f"Set `endpoint='{canonical_family}'` to use this hostname." + ) + if region is not None and canonical_region != region: + raise OpenAIError( + f"The Bedrock endpoint region `{canonical_region}` does not match the configured AWS region `{region}`." + ) + + +def _default_bedrock_base_url(endpoint: BedrockEndpoint, region: str) -> httpx2.URL: + hostname = ( + f"bedrock-runtime.{region}.{_runtime_dns_suffixes(region)[0]}" + if endpoint == "runtime" + else f"bedrock-mantle.{region}.api.aws" + ) + return _normalize_base_url(f"https://{hostname}/openai/v1") + + +def _same_origin(left: httpx2.URL, right: httpx2.URL) -> bool: + return (left.scheme, left.host, left.port) == (right.scheme, right.host, right.port) + + +def _body_for_signing(request: httpx2.Request) -> bytes: + try: + return request.content + except request_not_read_exceptions() as exc: + raise OpenAIError( + "Bedrock SigV4 authentication requires a replayable request body. " + "Buffer the body before sending or use bearer authentication." + ) from exc + + +def _assert_provider_owns_authorization(request: httpx2.Request) -> None: + if "Authorization" in request.headers: + raise OpenAIError("Bedrock provider authentication cannot be combined with a custom `Authorization` header.") + + +def _without_redirects(options: FinalRequestOptions) -> FinalRequestOptions: + if options.follow_redirects: + raise OpenAIError( + "Bedrock SigV4 authentication does not support automatic redirects. " + "Send a new request to the redirect target so it can be signed again." + ) + options.follow_redirects = False + return options + + +class _BedrockBearerAuth: + def __init__(self, token_provider: BedrockTokenProvider, *, base_url: httpx2.URL) -> None: + self._token_provider = token_provider + self._base_url = base_url + + def _validate_request(self, request: httpx2.Request) -> None: + _assert_provider_owns_authorization(request) + if not _same_origin(request.url, self._base_url): + raise OpenAIError( + "Refusing to authenticate a Bedrock request for an origin other than the configured provider URL." + ) + + def _resolve_token(self) -> str: + try: + token = cast(object, self._token_provider()) + except OpenAIError: + raise + except Exception as exc: + raise OpenAIError("Failed to resolve a bearer credential for Bedrock.") from exc + + if inspect.isawaitable(token): + close = getattr(token, "close", None) + if callable(close): + close() + raise OpenAIError("An async Bedrock token provider requires `AsyncOpenAI`.") + if not isinstance(token, str) or not token.strip(): + raise OpenAIError("The Bedrock bearer credential provider must return a non-empty string.") + return token + + async def _resolve_token_async(self) -> str: + try: + token = cast(object, self._token_provider()) + if inspect.isawaitable(token): + token = await token + except OpenAIError: + raise + except Exception as exc: + raise OpenAIError("Failed to resolve a bearer credential for Bedrock.") from exc + + if not isinstance(token, str) or not token.strip(): + raise OpenAIError("The Bedrock bearer credential provider must return a non-empty string.") + return token + + def prepare_request(self, request: httpx2.Request) -> None: + self._validate_request(request) + request.headers["Authorization"] = f"Bearer {self._resolve_token()}" + + async def prepare_async_request(self, request: httpx2.Request) -> None: + self._validate_request(request) + request.headers["Authorization"] = f"Bearer {await self._resolve_token_async()}" + + +class _BedrockSigV4Auth: + def __init__( + self, + *, + config: BedrockAwsAuthConfig, + base_url: httpx2.URL, + auth: BedrockAwsAuth | None = None, + ) -> None: + self._config = config + self._base_url = base_url + self._auth = auth + + def _validate_request(self, request: httpx2.Request) -> bytes: + _assert_provider_owns_authorization(request) + if not _same_origin(request.url, self._base_url): + raise OpenAIError( + "Refusing to sign a Bedrock request for an origin other than the configured provider URL." + ) + + canonical_endpoint = _parse_bedrock_endpoint_hostname(request.url.host) + if canonical_endpoint is not None: + endpoint, region = canonical_endpoint + expected_endpoint = "runtime" if self._config.service == "bedrock" else "mantle" + if endpoint != expected_endpoint: + raise OpenAIError( + f"The Bedrock {endpoint} hostname does not match the selected `{expected_endpoint}` endpoint." + ) + if region != self._config.region: + raise OpenAIError( + f"The Bedrock endpoint region `{region}` does not match the SigV4 region `{self._config.region}`." + ) + + return _body_for_signing(request) + + def _sign(self, request: httpx2.Request, *, auth: BedrockAwsAuth, body: bytes) -> None: + for header in _AWS_SIGNING_HEADERS: + request.headers.pop(header, None) + + signed_headers = auth.sign( + method=request.method, + url=str(request.url), + headers=dict(request.headers), + body=body, + ) + request.headers.clear() + request.headers.update(signed_headers) + + def prepare_request(self, request: httpx2.Request) -> None: + body = self._validate_request(request) + if self._auth is None: + self._auth = BedrockAwsAuth(self._config) + self._sign(request, auth=self._auth, body=body) + + async def prepare_async_request(self, request: httpx2.Request) -> None: + body = self._validate_request(request) + if self._auth is None: + self._auth = await asyncify(BedrockAwsAuth)(self._config) + + signed_headers = await asyncify(self._auth.sign)( + method=request.method, + url=str(request.url), + headers={ + name: value for name, value in request.headers.items() if name.lower() not in _AWS_SIGNING_HEADERS + }, + body=body, + ) + request.headers.clear() + request.headers.update(signed_headers) + + +@dataclass +class _BedrockProviderRuntime(_ProviderRuntime): + region: str | None = None + + +@dataclass(frozen=True) +class _BedrockProviderDefinition: + endpoint: BedrockEndpoint + configured_region: str | None + region_source: Literal["explicit", "environment"] | None + configured_base_url: httpx2.URL | None + api_key: str | None = field(default=None, repr=False) + token_provider: BedrockTokenProvider | None = field(default=None, repr=False, compare=False) + use_environment_bearer: bool = False + profile: str | None = None + access_key_id: str | None = field(default=None, repr=False) + secret_access_key: str | None = field(default=None, repr=False) + session_token: str | None = field(default=None, repr=False) + credential_provider: AwsCredentialsProvider | None = field(default=None, repr=False, compare=False) + name: str = field(default="bedrock", init=False) + + def _aws_source(self) -> Literal["static", "profile", "provider", "default"]: + if self.access_key_id is not None: + return "static" + if self.profile is not None: + return "profile" + if self.credential_provider is not None: + return "provider" + return "default" + + def _resolve_aws_auth(self) -> tuple[BedrockAwsAuthConfig, BedrockAwsAuth | None]: + if self.configured_region is not None: + return ( + BedrockAwsAuthConfig( + region=self.configured_region, + source=self._aws_source(), + service="bedrock" if self.endpoint == "runtime" else "bedrock-mantle", + region_source=self.region_source or "explicit", + profile=self.profile, + access_key_id=self.access_key_id, + secret_access_key=self.secret_access_key, + session_token=self.session_token, + credentials_provider=self.credential_provider, + ), + None, + ) + + auth = BedrockAwsAuth.resolve( + region=None, + profile=self.profile, + access_key_id=self.access_key_id, + secret_access_key=self.secret_access_key, + session_token=self.session_token, + credentials_provider=self.credential_provider, + service="bedrock" if self.endpoint == "runtime" else "bedrock-mantle", + ) + return auth.config, auth + + def configure(self) -> _ProviderRuntime: + def environment_token() -> str: + token = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") + if not token: + raise OpenAIError( + "Could not find credentials for Bedrock. Pass a bearer credential or AWS credentials to " + "`bedrock(...)`, set `AWS_BEARER_TOKEN_BEDROCK`, or configure the default AWS credential chain." + ) + return token + + auth: _BedrockBearerAuth | _BedrockSigV4Auth | None = None + bearer_provider: BedrockTokenProvider | None = None + if self.api_key is not None: + bearer_provider = lambda: self.api_key or "" + region = self.configured_region + elif self.token_provider is not None: + bearer_provider = self.token_provider + region = self.configured_region + elif self.use_environment_bearer: + bearer_provider = environment_token + region = self.configured_region + else: + aws_config, aws_auth = self._resolve_aws_auth() + region = aws_config.region + _validate_bedrock_region(region) + base_url = self.configured_base_url or _default_bedrock_base_url(self.endpoint, region) + _validate_canonical_bedrock_endpoint(base_url, endpoint=self.endpoint, region=region) + auth = _BedrockSigV4Auth(config=aws_config, base_url=base_url, auth=aws_auth) + + if self.configured_base_url is not None: + base_url = self.configured_base_url + elif region is not None: + base_url = _default_bedrock_base_url(self.endpoint, region) + else: + raise OpenAIError( + "Bedrock requires an AWS region. Pass `region` to `bedrock(...)`, or set `AWS_REGION` or " + "`AWS_DEFAULT_REGION`." + ) + + if bearer_provider is not None: + auth = _BedrockBearerAuth(bearer_provider, base_url=base_url) + + assert auth is not None + if isinstance(auth, _BedrockSigV4Auth): + return _BedrockProviderRuntime( + name=self.name, + base_url=base_url, + region=region, + transform_request=_without_redirects, + prepare_request=auth.prepare_request, + prepare_async_request=auth.prepare_async_request, + ) + + return _BedrockProviderRuntime( + name=self.name, + base_url=base_url, + region=region, + prepare_request=auth.prepare_request, + prepare_async_request=auth.prepare_async_request, + ) + + +def bedrock( + *, + endpoint: BedrockEndpoint | None = None, + region: str | None = None, + base_url: str | httpx2.URL | None | NotGiven = NOT_GIVEN, + api_key: str | None | NotGiven = NOT_GIVEN, + token_provider: BedrockTokenProvider | None = None, + access_key_id: str | None = None, + secret_access_key: str | None = None, + session_token: str | None = None, + profile: str | None = None, + credential_provider: AwsCredentialsProvider | None = None, +) -> _Provider: + """Configure the standard OpenAI client for Amazon Bedrock Mantle or Runtime.""" + + if endpoint is not None and endpoint not in {"mantle", "runtime"}: + raise OpenAIError("The Bedrock `endpoint` must be either `mantle` or `runtime`.") + + normalized_region = _normalize_optional_string(region) + if region is not None and normalized_region is None: + raise OpenAIError("The Bedrock AWS `region` must not be empty.") + _validate_bedrock_region(normalized_region) + + region_source: Literal["explicit", "environment"] | None = "explicit" if normalized_region is not None else None + + configured_base_url: httpx2.URL | None + if isinstance(base_url, NotGiven): + environment_base_url = _normalize_optional_string(os.environ.get("AWS_BEDROCK_BASE_URL")) + configured_base_url = _normalize_base_url(environment_base_url) if environment_base_url else None + elif base_url is None: + configured_base_url = None + else: + if isinstance(base_url, str) and not base_url.strip(): + raise OpenAIError("The Bedrock `base_url` must not be empty.") + configured_base_url = _normalize_base_url(base_url) + + canonical_endpoint = ( + _parse_bedrock_endpoint_hostname(configured_base_url.host) if configured_base_url is not None else None + ) + resolved_endpoint: BedrockEndpoint = endpoint or ( + canonical_endpoint[0] if canonical_endpoint is not None else "mantle" + ) + + normalized_profile = _normalize_optional_string(profile) + if profile is not None and normalized_profile is None: + raise OpenAIError("The Bedrock AWS `profile` must not be empty.") + + if (access_key_id is None) != (secret_access_key is None) or (session_token is not None and access_key_id is None): + raise OpenAIError( + "Static AWS credentials require both `access_key_id` and `secret_access_key`. " + "A `session_token` may only be used with both." + ) + if access_key_id is not None and (not access_key_id.strip() or not cast(str, secret_access_key).strip()): + raise OpenAIError("Static AWS credentials require non-empty `access_key_id` and `secret_access_key` values.") + if session_token is not None and not session_token.strip(): + raise OpenAIError("A static AWS `session_token` must not be empty when provided.") + + explicit_api_key = not isinstance(api_key, NotGiven) and api_key is not None + if explicit_api_key and (not isinstance(api_key, str) or not api_key.strip()): + raise OpenAIError("The Bedrock bearer credential must not be empty.") + if explicit_api_key and token_provider is not None: + raise OpenAIError("The `api_key` and `token_provider` options are mutually exclusive. Configure only one.") + + explicit_bearer = explicit_api_key or token_provider is not None + aws_modes = sum( + ( + access_key_id is not None, + normalized_profile is not None, + credential_provider is not None, + ) + ) + if aws_modes > 1: + raise OpenAIError( + "Bedrock authentication is ambiguous. Configure exactly one explicit AWS mode: static credentials, " + "profile, or credential provider." + ) + if explicit_bearer and aws_modes: + raise OpenAIError( + "Bedrock authentication is ambiguous. Configure exactly one explicit mode: bearer credential, " + "static AWS credentials, profile, or credential provider." + ) + + skip_environment_bearer = not isinstance(api_key, NotGiven) and api_key is None + use_environment_bearer = ( + not explicit_bearer + and not aws_modes + and not skip_environment_bearer + and bool(os.environ.get("AWS_BEARER_TOKEN_BEDROCK")) + ) + + if normalized_region is None and (configured_base_url is None or not (explicit_bearer or use_environment_bearer)): + normalized_region = _normalize_optional_string( + os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION") + ) + _validate_bedrock_region(normalized_region) + if normalized_region is not None: + region_source = "environment" + + if configured_base_url is not None: + _validate_canonical_bedrock_endpoint(configured_base_url, endpoint=resolved_endpoint, region=normalized_region) + + return _create_provider( + _BedrockProviderDefinition( + endpoint=resolved_endpoint, + configured_region=normalized_region, + region_source=region_source, + configured_base_url=configured_base_url, + api_key=cast("str | None", api_key) if explicit_api_key else None, + token_provider=token_provider, + use_environment_bearer=use_environment_bearer, + profile=normalized_profile, + access_key_id=access_key_id, + secret_access_key=secret_access_key, + session_token=session_token, + credential_provider=credential_provider, + ) + ) + + +__all__ = ["bedrock", "BedrockEndpoint", "BedrockTokenProvider", "AwsCredentialsProvider"] diff --git a/venv/lib/python3.12/site-packages/openai/py.typed b/venv/lib/python3.12/site-packages/openai/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/openai/resources/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/__init__.py new file mode 100644 index 0000000..8bb98ed --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/__init__.py @@ -0,0 +1,463 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .beta import ( + Beta as Beta, + AsyncBeta as AsyncBeta, + BetaWithRawResponse as BetaWithRawResponse, + AsyncBetaWithRawResponse as AsyncBetaWithRawResponse, + BetaWithStreamingResponse as BetaWithStreamingResponse, + AsyncBetaWithStreamingResponse as AsyncBetaWithStreamingResponse, + ) + from .chat import ( + Chat as Chat, + AsyncChat as AsyncChat, + ChatWithRawResponse as ChatWithRawResponse, + AsyncChatWithRawResponse as AsyncChatWithRawResponse, + ChatWithStreamingResponse as ChatWithStreamingResponse, + AsyncChatWithStreamingResponse as AsyncChatWithStreamingResponse, + ) + from .admin import ( + Admin as Admin, + AsyncAdmin as AsyncAdmin, + AdminWithRawResponse as AdminWithRawResponse, + AsyncAdminWithRawResponse as AsyncAdminWithRawResponse, + AdminWithStreamingResponse as AdminWithStreamingResponse, + AsyncAdminWithStreamingResponse as AsyncAdminWithStreamingResponse, + ) + from .audio import ( + Audio as Audio, + AsyncAudio as AsyncAudio, + AudioWithRawResponse as AudioWithRawResponse, + AsyncAudioWithRawResponse as AsyncAudioWithRawResponse, + AudioWithStreamingResponse as AudioWithStreamingResponse, + AsyncAudioWithStreamingResponse as AsyncAudioWithStreamingResponse, + ) + from .evals import ( + Evals as Evals, + AsyncEvals as AsyncEvals, + EvalsWithRawResponse as EvalsWithRawResponse, + AsyncEvalsWithRawResponse as AsyncEvalsWithRawResponse, + EvalsWithStreamingResponse as EvalsWithStreamingResponse, + AsyncEvalsWithStreamingResponse as AsyncEvalsWithStreamingResponse, + ) + from .files import ( + Files as Files, + AsyncFiles as AsyncFiles, + FilesWithRawResponse as FilesWithRawResponse, + AsyncFilesWithRawResponse as AsyncFilesWithRawResponse, + FilesWithStreamingResponse as FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse as AsyncFilesWithStreamingResponse, + ) + from .images import ( + Images as Images, + AsyncImages as AsyncImages, + ImagesWithRawResponse as ImagesWithRawResponse, + AsyncImagesWithRawResponse as AsyncImagesWithRawResponse, + ImagesWithStreamingResponse as ImagesWithStreamingResponse, + AsyncImagesWithStreamingResponse as AsyncImagesWithStreamingResponse, + ) + from .models import ( + Models as Models, + AsyncModels as AsyncModels, + ModelsWithRawResponse as ModelsWithRawResponse, + AsyncModelsWithRawResponse as AsyncModelsWithRawResponse, + ModelsWithStreamingResponse as ModelsWithStreamingResponse, + AsyncModelsWithStreamingResponse as AsyncModelsWithStreamingResponse, + ) + from .safety import ( + Safety as Safety, + AsyncSafety as AsyncSafety, + SafetyWithRawResponse as SafetyWithRawResponse, + AsyncSafetyWithRawResponse as AsyncSafetyWithRawResponse, + SafetyWithStreamingResponse as SafetyWithStreamingResponse, + AsyncSafetyWithStreamingResponse as AsyncSafetyWithStreamingResponse, + ) + from .skills import ( + Skills as Skills, + AsyncSkills as AsyncSkills, + SkillsWithRawResponse as SkillsWithRawResponse, + AsyncSkillsWithRawResponse as AsyncSkillsWithRawResponse, + SkillsWithStreamingResponse as SkillsWithStreamingResponse, + AsyncSkillsWithStreamingResponse as AsyncSkillsWithStreamingResponse, + ) + from .videos import ( + Videos as Videos, + AsyncVideos as AsyncVideos, + VideosWithRawResponse as VideosWithRawResponse, + AsyncVideosWithRawResponse as AsyncVideosWithRawResponse, + VideosWithStreamingResponse as VideosWithStreamingResponse, + AsyncVideosWithStreamingResponse as AsyncVideosWithStreamingResponse, + ) + from .batches import ( + Batches as Batches, + AsyncBatches as AsyncBatches, + BatchesWithRawResponse as BatchesWithRawResponse, + AsyncBatchesWithRawResponse as AsyncBatchesWithRawResponse, + BatchesWithStreamingResponse as BatchesWithStreamingResponse, + AsyncBatchesWithStreamingResponse as AsyncBatchesWithStreamingResponse, + ) + from .uploads import ( + Uploads as Uploads, + AsyncUploads as AsyncUploads, + UploadsWithRawResponse as UploadsWithRawResponse, + AsyncUploadsWithRawResponse as AsyncUploadsWithRawResponse, + UploadsWithStreamingResponse as UploadsWithStreamingResponse, + AsyncUploadsWithStreamingResponse as AsyncUploadsWithStreamingResponse, + ) + from .containers import ( + Containers as Containers, + AsyncContainers as AsyncContainers, + ContainersWithRawResponse as ContainersWithRawResponse, + AsyncContainersWithRawResponse as AsyncContainersWithRawResponse, + ContainersWithStreamingResponse as ContainersWithStreamingResponse, + AsyncContainersWithStreamingResponse as AsyncContainersWithStreamingResponse, + ) + from .embeddings import ( + Embeddings as Embeddings, + AsyncEmbeddings as AsyncEmbeddings, + EmbeddingsWithRawResponse as EmbeddingsWithRawResponse, + AsyncEmbeddingsWithRawResponse as AsyncEmbeddingsWithRawResponse, + EmbeddingsWithStreamingResponse as EmbeddingsWithStreamingResponse, + AsyncEmbeddingsWithStreamingResponse as AsyncEmbeddingsWithStreamingResponse, + ) + from .completions import ( + Completions as Completions, + AsyncCompletions as AsyncCompletions, + CompletionsWithRawResponse as CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse as AsyncCompletionsWithRawResponse, + CompletionsWithStreamingResponse as CompletionsWithStreamingResponse, + AsyncCompletionsWithStreamingResponse as AsyncCompletionsWithStreamingResponse, + ) + from .fine_tuning import ( + FineTuning as FineTuning, + AsyncFineTuning as AsyncFineTuning, + FineTuningWithRawResponse as FineTuningWithRawResponse, + AsyncFineTuningWithRawResponse as AsyncFineTuningWithRawResponse, + FineTuningWithStreamingResponse as FineTuningWithStreamingResponse, + AsyncFineTuningWithStreamingResponse as AsyncFineTuningWithStreamingResponse, + ) + from .moderations import ( + Moderations as Moderations, + AsyncModerations as AsyncModerations, + ModerationsWithRawResponse as ModerationsWithRawResponse, + AsyncModerationsWithRawResponse as AsyncModerationsWithRawResponse, + ModerationsWithStreamingResponse as ModerationsWithStreamingResponse, + AsyncModerationsWithStreamingResponse as AsyncModerationsWithStreamingResponse, + ) + from .vector_stores import ( + VectorStores as VectorStores, + AsyncVectorStores as AsyncVectorStores, + VectorStoresWithRawResponse as VectorStoresWithRawResponse, + AsyncVectorStoresWithRawResponse as AsyncVectorStoresWithRawResponse, + VectorStoresWithStreamingResponse as VectorStoresWithStreamingResponse, + AsyncVectorStoresWithStreamingResponse as AsyncVectorStoresWithStreamingResponse, + ) + from .content_provenance_checks import ( + ContentProvenanceChecks as ContentProvenanceChecks, + AsyncContentProvenanceChecks as AsyncContentProvenanceChecks, + ContentProvenanceChecksWithRawResponse as ContentProvenanceChecksWithRawResponse, + AsyncContentProvenanceChecksWithRawResponse as AsyncContentProvenanceChecksWithRawResponse, + ContentProvenanceChecksWithStreamingResponse as ContentProvenanceChecksWithStreamingResponse, + AsyncContentProvenanceChecksWithStreamingResponse as AsyncContentProvenanceChecksWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Completions": (".completions", "Completions"), + "AsyncCompletions": (".completions", "AsyncCompletions"), + "CompletionsWithRawResponse": (".completions", "CompletionsWithRawResponse"), + "AsyncCompletionsWithRawResponse": (".completions", "AsyncCompletionsWithRawResponse"), + "CompletionsWithStreamingResponse": (".completions", "CompletionsWithStreamingResponse"), + "AsyncCompletionsWithStreamingResponse": (".completions", "AsyncCompletionsWithStreamingResponse"), + "Chat": (".chat", "Chat"), + "AsyncChat": (".chat", "AsyncChat"), + "ChatWithRawResponse": (".chat", "ChatWithRawResponse"), + "AsyncChatWithRawResponse": (".chat", "AsyncChatWithRawResponse"), + "ChatWithStreamingResponse": (".chat", "ChatWithStreamingResponse"), + "AsyncChatWithStreamingResponse": (".chat", "AsyncChatWithStreamingResponse"), + "Embeddings": (".embeddings", "Embeddings"), + "AsyncEmbeddings": (".embeddings", "AsyncEmbeddings"), + "EmbeddingsWithRawResponse": (".embeddings", "EmbeddingsWithRawResponse"), + "AsyncEmbeddingsWithRawResponse": (".embeddings", "AsyncEmbeddingsWithRawResponse"), + "EmbeddingsWithStreamingResponse": (".embeddings", "EmbeddingsWithStreamingResponse"), + "AsyncEmbeddingsWithStreamingResponse": (".embeddings", "AsyncEmbeddingsWithStreamingResponse"), + "Files": (".files", "Files"), + "AsyncFiles": (".files", "AsyncFiles"), + "FilesWithRawResponse": (".files", "FilesWithRawResponse"), + "AsyncFilesWithRawResponse": (".files", "AsyncFilesWithRawResponse"), + "FilesWithStreamingResponse": (".files", "FilesWithStreamingResponse"), + "AsyncFilesWithStreamingResponse": (".files", "AsyncFilesWithStreamingResponse"), + "Images": (".images", "Images"), + "AsyncImages": (".images", "AsyncImages"), + "ImagesWithRawResponse": (".images", "ImagesWithRawResponse"), + "AsyncImagesWithRawResponse": (".images", "AsyncImagesWithRawResponse"), + "ImagesWithStreamingResponse": (".images", "ImagesWithStreamingResponse"), + "AsyncImagesWithStreamingResponse": (".images", "AsyncImagesWithStreamingResponse"), + "ContentProvenanceChecks": (".content_provenance_checks", "ContentProvenanceChecks"), + "AsyncContentProvenanceChecks": (".content_provenance_checks", "AsyncContentProvenanceChecks"), + "ContentProvenanceChecksWithRawResponse": ( + ".content_provenance_checks", + "ContentProvenanceChecksWithRawResponse", + ), + "AsyncContentProvenanceChecksWithRawResponse": ( + ".content_provenance_checks", + "AsyncContentProvenanceChecksWithRawResponse", + ), + "ContentProvenanceChecksWithStreamingResponse": ( + ".content_provenance_checks", + "ContentProvenanceChecksWithStreamingResponse", + ), + "AsyncContentProvenanceChecksWithStreamingResponse": ( + ".content_provenance_checks", + "AsyncContentProvenanceChecksWithStreamingResponse", + ), + "Audio": (".audio", "Audio"), + "AsyncAudio": (".audio", "AsyncAudio"), + "AudioWithRawResponse": (".audio", "AudioWithRawResponse"), + "AsyncAudioWithRawResponse": (".audio", "AsyncAudioWithRawResponse"), + "AudioWithStreamingResponse": (".audio", "AudioWithStreamingResponse"), + "AsyncAudioWithStreamingResponse": (".audio", "AsyncAudioWithStreamingResponse"), + "Moderations": (".moderations", "Moderations"), + "AsyncModerations": (".moderations", "AsyncModerations"), + "ModerationsWithRawResponse": (".moderations", "ModerationsWithRawResponse"), + "AsyncModerationsWithRawResponse": (".moderations", "AsyncModerationsWithRawResponse"), + "ModerationsWithStreamingResponse": (".moderations", "ModerationsWithStreamingResponse"), + "AsyncModerationsWithStreamingResponse": (".moderations", "AsyncModerationsWithStreamingResponse"), + "Models": (".models", "Models"), + "AsyncModels": (".models", "AsyncModels"), + "ModelsWithRawResponse": (".models", "ModelsWithRawResponse"), + "AsyncModelsWithRawResponse": (".models", "AsyncModelsWithRawResponse"), + "ModelsWithStreamingResponse": (".models", "ModelsWithStreamingResponse"), + "AsyncModelsWithStreamingResponse": (".models", "AsyncModelsWithStreamingResponse"), + "FineTuning": (".fine_tuning", "FineTuning"), + "AsyncFineTuning": (".fine_tuning", "AsyncFineTuning"), + "FineTuningWithRawResponse": (".fine_tuning", "FineTuningWithRawResponse"), + "AsyncFineTuningWithRawResponse": (".fine_tuning", "AsyncFineTuningWithRawResponse"), + "FineTuningWithStreamingResponse": (".fine_tuning", "FineTuningWithStreamingResponse"), + "AsyncFineTuningWithStreamingResponse": (".fine_tuning", "AsyncFineTuningWithStreamingResponse"), + "VectorStores": (".vector_stores", "VectorStores"), + "AsyncVectorStores": (".vector_stores", "AsyncVectorStores"), + "VectorStoresWithRawResponse": (".vector_stores", "VectorStoresWithRawResponse"), + "AsyncVectorStoresWithRawResponse": (".vector_stores", "AsyncVectorStoresWithRawResponse"), + "VectorStoresWithStreamingResponse": (".vector_stores", "VectorStoresWithStreamingResponse"), + "AsyncVectorStoresWithStreamingResponse": (".vector_stores", "AsyncVectorStoresWithStreamingResponse"), + "Safety": (".safety", "Safety"), + "AsyncSafety": (".safety", "AsyncSafety"), + "SafetyWithRawResponse": (".safety", "SafetyWithRawResponse"), + "AsyncSafetyWithRawResponse": (".safety", "AsyncSafetyWithRawResponse"), + "SafetyWithStreamingResponse": (".safety", "SafetyWithStreamingResponse"), + "AsyncSafetyWithStreamingResponse": (".safety", "AsyncSafetyWithStreamingResponse"), + "Beta": (".beta", "Beta"), + "AsyncBeta": (".beta", "AsyncBeta"), + "BetaWithRawResponse": (".beta", "BetaWithRawResponse"), + "AsyncBetaWithRawResponse": (".beta", "AsyncBetaWithRawResponse"), + "BetaWithStreamingResponse": (".beta", "BetaWithStreamingResponse"), + "AsyncBetaWithStreamingResponse": (".beta", "AsyncBetaWithStreamingResponse"), + "Batches": (".batches", "Batches"), + "AsyncBatches": (".batches", "AsyncBatches"), + "BatchesWithRawResponse": (".batches", "BatchesWithRawResponse"), + "AsyncBatchesWithRawResponse": (".batches", "AsyncBatchesWithRawResponse"), + "BatchesWithStreamingResponse": (".batches", "BatchesWithStreamingResponse"), + "AsyncBatchesWithStreamingResponse": (".batches", "AsyncBatchesWithStreamingResponse"), + "Uploads": (".uploads", "Uploads"), + "AsyncUploads": (".uploads", "AsyncUploads"), + "UploadsWithRawResponse": (".uploads", "UploadsWithRawResponse"), + "AsyncUploadsWithRawResponse": (".uploads", "AsyncUploadsWithRawResponse"), + "UploadsWithStreamingResponse": (".uploads", "UploadsWithStreamingResponse"), + "AsyncUploadsWithStreamingResponse": (".uploads", "AsyncUploadsWithStreamingResponse"), + "Admin": (".admin", "Admin"), + "AsyncAdmin": (".admin", "AsyncAdmin"), + "AdminWithRawResponse": (".admin", "AdminWithRawResponse"), + "AsyncAdminWithRawResponse": (".admin", "AsyncAdminWithRawResponse"), + "AdminWithStreamingResponse": (".admin", "AdminWithStreamingResponse"), + "AsyncAdminWithStreamingResponse": (".admin", "AsyncAdminWithStreamingResponse"), + "Evals": (".evals", "Evals"), + "AsyncEvals": (".evals", "AsyncEvals"), + "EvalsWithRawResponse": (".evals", "EvalsWithRawResponse"), + "AsyncEvalsWithRawResponse": (".evals", "AsyncEvalsWithRawResponse"), + "EvalsWithStreamingResponse": (".evals", "EvalsWithStreamingResponse"), + "AsyncEvalsWithStreamingResponse": (".evals", "AsyncEvalsWithStreamingResponse"), + "Containers": (".containers", "Containers"), + "AsyncContainers": (".containers", "AsyncContainers"), + "ContainersWithRawResponse": (".containers", "ContainersWithRawResponse"), + "AsyncContainersWithRawResponse": (".containers", "AsyncContainersWithRawResponse"), + "ContainersWithStreamingResponse": (".containers", "ContainersWithStreamingResponse"), + "AsyncContainersWithStreamingResponse": (".containers", "AsyncContainersWithStreamingResponse"), + "Skills": (".skills", "Skills"), + "AsyncSkills": (".skills", "AsyncSkills"), + "SkillsWithRawResponse": (".skills", "SkillsWithRawResponse"), + "AsyncSkillsWithRawResponse": (".skills", "AsyncSkillsWithRawResponse"), + "SkillsWithStreamingResponse": (".skills", "SkillsWithStreamingResponse"), + "AsyncSkillsWithStreamingResponse": (".skills", "AsyncSkillsWithStreamingResponse"), + "Videos": (".videos", "Videos"), + "AsyncVideos": (".videos", "AsyncVideos"), + "VideosWithRawResponse": (".videos", "VideosWithRawResponse"), + "AsyncVideosWithRawResponse": (".videos", "AsyncVideosWithRawResponse"), + "VideosWithStreamingResponse": (".videos", "VideosWithStreamingResponse"), + "AsyncVideosWithStreamingResponse": (".videos", "AsyncVideosWithStreamingResponse"), + } + _SUBMODULES = { + "admin", + "audio", + "batches", + "beta", + "chat", + "completions", + "containers", + "content_provenance_checks", + "embeddings", + "evals", + "files", + "fine_tuning", + "images", + "models", + "moderations", + "safety", + "skills", + "uploads", + "vector_stores", + "videos", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Completions", + "AsyncCompletions", + "CompletionsWithRawResponse", + "AsyncCompletionsWithRawResponse", + "CompletionsWithStreamingResponse", + "AsyncCompletionsWithStreamingResponse", + "Chat", + "AsyncChat", + "ChatWithRawResponse", + "AsyncChatWithRawResponse", + "ChatWithStreamingResponse", + "AsyncChatWithStreamingResponse", + "Embeddings", + "AsyncEmbeddings", + "EmbeddingsWithRawResponse", + "AsyncEmbeddingsWithRawResponse", + "EmbeddingsWithStreamingResponse", + "AsyncEmbeddingsWithStreamingResponse", + "Files", + "AsyncFiles", + "FilesWithRawResponse", + "AsyncFilesWithRawResponse", + "FilesWithStreamingResponse", + "AsyncFilesWithStreamingResponse", + "Images", + "AsyncImages", + "ImagesWithRawResponse", + "AsyncImagesWithRawResponse", + "ImagesWithStreamingResponse", + "AsyncImagesWithStreamingResponse", + "ContentProvenanceChecks", + "AsyncContentProvenanceChecks", + "ContentProvenanceChecksWithRawResponse", + "AsyncContentProvenanceChecksWithRawResponse", + "ContentProvenanceChecksWithStreamingResponse", + "AsyncContentProvenanceChecksWithStreamingResponse", + "Audio", + "AsyncAudio", + "AudioWithRawResponse", + "AsyncAudioWithRawResponse", + "AudioWithStreamingResponse", + "AsyncAudioWithStreamingResponse", + "Moderations", + "AsyncModerations", + "ModerationsWithRawResponse", + "AsyncModerationsWithRawResponse", + "ModerationsWithStreamingResponse", + "AsyncModerationsWithStreamingResponse", + "Models", + "AsyncModels", + "ModelsWithRawResponse", + "AsyncModelsWithRawResponse", + "ModelsWithStreamingResponse", + "AsyncModelsWithStreamingResponse", + "FineTuning", + "AsyncFineTuning", + "FineTuningWithRawResponse", + "AsyncFineTuningWithRawResponse", + "FineTuningWithStreamingResponse", + "AsyncFineTuningWithStreamingResponse", + "VectorStores", + "AsyncVectorStores", + "VectorStoresWithRawResponse", + "AsyncVectorStoresWithRawResponse", + "VectorStoresWithStreamingResponse", + "AsyncVectorStoresWithStreamingResponse", + "Safety", + "AsyncSafety", + "SafetyWithRawResponse", + "AsyncSafetyWithRawResponse", + "SafetyWithStreamingResponse", + "AsyncSafetyWithStreamingResponse", + "Beta", + "AsyncBeta", + "BetaWithRawResponse", + "AsyncBetaWithRawResponse", + "BetaWithStreamingResponse", + "AsyncBetaWithStreamingResponse", + "Batches", + "AsyncBatches", + "BatchesWithRawResponse", + "AsyncBatchesWithRawResponse", + "BatchesWithStreamingResponse", + "AsyncBatchesWithStreamingResponse", + "Uploads", + "AsyncUploads", + "UploadsWithRawResponse", + "AsyncUploadsWithRawResponse", + "UploadsWithStreamingResponse", + "AsyncUploadsWithStreamingResponse", + "Admin", + "AsyncAdmin", + "AdminWithRawResponse", + "AsyncAdminWithRawResponse", + "AdminWithStreamingResponse", + "AsyncAdminWithStreamingResponse", + "Evals", + "AsyncEvals", + "EvalsWithRawResponse", + "AsyncEvalsWithRawResponse", + "EvalsWithStreamingResponse", + "AsyncEvalsWithStreamingResponse", + "Containers", + "AsyncContainers", + "ContainersWithRawResponse", + "AsyncContainersWithRawResponse", + "ContainersWithStreamingResponse", + "AsyncContainersWithStreamingResponse", + "Skills", + "AsyncSkills", + "SkillsWithRawResponse", + "AsyncSkillsWithRawResponse", + "SkillsWithStreamingResponse", + "AsyncSkillsWithStreamingResponse", + "Videos", + "AsyncVideos", + "VideosWithRawResponse", + "AsyncVideosWithRawResponse", + "VideosWithStreamingResponse", + "AsyncVideosWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c63f544 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/__pycache__/batches.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/batches.cpython-312.pyc new file mode 100644 index 0000000..c3a49a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/batches.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/__pycache__/completions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/completions.cpython-312.pyc new file mode 100644 index 0000000..9278533 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/completions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/__pycache__/content_provenance_checks.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/content_provenance_checks.cpython-312.pyc new file mode 100644 index 0000000..cc81ff2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/content_provenance_checks.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/__pycache__/embeddings.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/embeddings.cpython-312.pyc new file mode 100644 index 0000000..aac0204 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/embeddings.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/__pycache__/files.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/files.cpython-312.pyc new file mode 100644 index 0000000..ee8f6e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/files.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/__pycache__/images.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/images.cpython-312.pyc new file mode 100644 index 0000000..ed094a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/images.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/__pycache__/models.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000..55d527d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/models.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/__pycache__/moderations.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/moderations.cpython-312.pyc new file mode 100644 index 0000000..47139ea Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/moderations.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/__pycache__/videos.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/videos.cpython-312.pyc new file mode 100644 index 0000000..c01818b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/__pycache__/videos.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/admin/__init__.py new file mode 100644 index 0000000..6cb3ea1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .admin import ( + Admin as Admin, + AsyncAdmin as AsyncAdmin, + AdminWithRawResponse as AdminWithRawResponse, + AsyncAdminWithRawResponse as AsyncAdminWithRawResponse, + AdminWithStreamingResponse as AdminWithStreamingResponse, + AsyncAdminWithStreamingResponse as AsyncAdminWithStreamingResponse, + ) + from .organization import ( + Organization as Organization, + AsyncOrganization as AsyncOrganization, + OrganizationWithRawResponse as OrganizationWithRawResponse, + AsyncOrganizationWithRawResponse as AsyncOrganizationWithRawResponse, + OrganizationWithStreamingResponse as OrganizationWithStreamingResponse, + AsyncOrganizationWithStreamingResponse as AsyncOrganizationWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Organization": (".organization", "Organization"), + "AsyncOrganization": (".organization", "AsyncOrganization"), + "OrganizationWithRawResponse": (".organization", "OrganizationWithRawResponse"), + "AsyncOrganizationWithRawResponse": (".organization", "AsyncOrganizationWithRawResponse"), + "OrganizationWithStreamingResponse": (".organization", "OrganizationWithStreamingResponse"), + "AsyncOrganizationWithStreamingResponse": (".organization", "AsyncOrganizationWithStreamingResponse"), + "Admin": (".admin", "Admin"), + "AsyncAdmin": (".admin", "AsyncAdmin"), + "AdminWithRawResponse": (".admin", "AdminWithRawResponse"), + "AsyncAdminWithRawResponse": (".admin", "AsyncAdminWithRawResponse"), + "AdminWithStreamingResponse": (".admin", "AdminWithStreamingResponse"), + "AsyncAdminWithStreamingResponse": (".admin", "AsyncAdminWithStreamingResponse"), + } + _SUBMODULES = { + "admin", + "organization", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Organization", + "AsyncOrganization", + "OrganizationWithRawResponse", + "AsyncOrganizationWithRawResponse", + "OrganizationWithStreamingResponse", + "AsyncOrganizationWithStreamingResponse", + "Admin", + "AsyncAdmin", + "AdminWithRawResponse", + "AsyncAdminWithRawResponse", + "AdminWithStreamingResponse", + "AsyncAdminWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..57bd9af Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/__pycache__/admin.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/__pycache__/admin.cpython-312.pyc new file mode 100644 index 0000000..4aadf15 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/__pycache__/admin.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/admin.py b/venv/lib/python3.12/site-packages/openai/resources/admin/admin.py new file mode 100644 index 0000000..52cca51 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/admin.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .organization.organization import ( + Organization, + AsyncOrganization, + OrganizationWithRawResponse, + AsyncOrganizationWithRawResponse, + OrganizationWithStreamingResponse, + AsyncOrganizationWithStreamingResponse, +) + +__all__ = ["Admin", "AsyncAdmin"] + + +class Admin(SyncAPIResource): + @cached_property + def organization(self) -> Organization: + return Organization(self._client) + + @cached_property + def with_raw_response(self) -> AdminWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AdminWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AdminWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AdminWithStreamingResponse(self) + + +class AsyncAdmin(AsyncAPIResource): + @cached_property + def organization(self) -> AsyncOrganization: + return AsyncOrganization(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAdminWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAdminWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAdminWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAdminWithStreamingResponse(self) + + +class AdminWithRawResponse: + def __init__(self, admin: Admin) -> None: + self._admin = admin + + @cached_property + def organization(self) -> OrganizationWithRawResponse: + return OrganizationWithRawResponse(self._admin.organization) + + +class AsyncAdminWithRawResponse: + def __init__(self, admin: AsyncAdmin) -> None: + self._admin = admin + + @cached_property + def organization(self) -> AsyncOrganizationWithRawResponse: + return AsyncOrganizationWithRawResponse(self._admin.organization) + + +class AdminWithStreamingResponse: + def __init__(self, admin: Admin) -> None: + self._admin = admin + + @cached_property + def organization(self) -> OrganizationWithStreamingResponse: + return OrganizationWithStreamingResponse(self._admin.organization) + + +class AsyncAdminWithStreamingResponse: + def __init__(self, admin: AsyncAdmin) -> None: + self._admin = admin + + @cached_property + def organization(self) -> AsyncOrganizationWithStreamingResponse: + return AsyncOrganizationWithStreamingResponse(self._admin.organization) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__init__.py new file mode 100644 index 0000000..d1036e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__init__.py @@ -0,0 +1,325 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .roles import ( + Roles as Roles, + AsyncRoles as AsyncRoles, + RolesWithRawResponse as RolesWithRawResponse, + AsyncRolesWithRawResponse as AsyncRolesWithRawResponse, + RolesWithStreamingResponse as RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse as AsyncRolesWithStreamingResponse, + ) + from .usage import ( + Usage as Usage, + AsyncUsage as AsyncUsage, + UsageWithRawResponse as UsageWithRawResponse, + AsyncUsageWithRawResponse as AsyncUsageWithRawResponse, + UsageWithStreamingResponse as UsageWithStreamingResponse, + AsyncUsageWithStreamingResponse as AsyncUsageWithStreamingResponse, + ) + from .users import ( + Users as Users, + AsyncUsers as AsyncUsers, + UsersWithRawResponse as UsersWithRawResponse, + AsyncUsersWithRawResponse as AsyncUsersWithRawResponse, + UsersWithStreamingResponse as UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse as AsyncUsersWithStreamingResponse, + ) + from .groups import ( + Groups as Groups, + AsyncGroups as AsyncGroups, + GroupsWithRawResponse as GroupsWithRawResponse, + AsyncGroupsWithRawResponse as AsyncGroupsWithRawResponse, + GroupsWithStreamingResponse as GroupsWithStreamingResponse, + AsyncGroupsWithStreamingResponse as AsyncGroupsWithStreamingResponse, + ) + from .invites import ( + Invites as Invites, + AsyncInvites as AsyncInvites, + InvitesWithRawResponse as InvitesWithRawResponse, + AsyncInvitesWithRawResponse as AsyncInvitesWithRawResponse, + InvitesWithStreamingResponse as InvitesWithStreamingResponse, + AsyncInvitesWithStreamingResponse as AsyncInvitesWithStreamingResponse, + ) + from .projects import ( + Projects as Projects, + AsyncProjects as AsyncProjects, + ProjectsWithRawResponse as ProjectsWithRawResponse, + AsyncProjectsWithRawResponse as AsyncProjectsWithRawResponse, + ProjectsWithStreamingResponse as ProjectsWithStreamingResponse, + AsyncProjectsWithStreamingResponse as AsyncProjectsWithStreamingResponse, + ) + from .audit_logs import ( + AuditLogs as AuditLogs, + AsyncAuditLogs as AsyncAuditLogs, + AuditLogsWithRawResponse as AuditLogsWithRawResponse, + AsyncAuditLogsWithRawResponse as AsyncAuditLogsWithRawResponse, + AuditLogsWithStreamingResponse as AuditLogsWithStreamingResponse, + AsyncAuditLogsWithStreamingResponse as AsyncAuditLogsWithStreamingResponse, + ) + from .spend_limit import ( + SpendLimit as SpendLimit, + AsyncSpendLimit as AsyncSpendLimit, + SpendLimitWithRawResponse as SpendLimitWithRawResponse, + AsyncSpendLimitWithRawResponse as AsyncSpendLimitWithRawResponse, + SpendLimitWithStreamingResponse as SpendLimitWithStreamingResponse, + AsyncSpendLimitWithStreamingResponse as AsyncSpendLimitWithStreamingResponse, + ) + from .certificates import ( + Certificates as Certificates, + AsyncCertificates as AsyncCertificates, + CertificatesWithRawResponse as CertificatesWithRawResponse, + AsyncCertificatesWithRawResponse as AsyncCertificatesWithRawResponse, + CertificatesWithStreamingResponse as CertificatesWithStreamingResponse, + AsyncCertificatesWithStreamingResponse as AsyncCertificatesWithStreamingResponse, + ) + from .organization import ( + Organization as Organization, + AsyncOrganization as AsyncOrganization, + OrganizationWithRawResponse as OrganizationWithRawResponse, + AsyncOrganizationWithRawResponse as AsyncOrganizationWithRawResponse, + OrganizationWithStreamingResponse as OrganizationWithStreamingResponse, + AsyncOrganizationWithStreamingResponse as AsyncOrganizationWithStreamingResponse, + ) + from .spend_alerts import ( + SpendAlerts as SpendAlerts, + AsyncSpendAlerts as AsyncSpendAlerts, + SpendAlertsWithRawResponse as SpendAlertsWithRawResponse, + AsyncSpendAlertsWithRawResponse as AsyncSpendAlertsWithRawResponse, + SpendAlertsWithStreamingResponse as SpendAlertsWithStreamingResponse, + AsyncSpendAlertsWithStreamingResponse as AsyncSpendAlertsWithStreamingResponse, + ) + from .admin_api_keys import ( + AdminAPIKeys as AdminAPIKeys, + AsyncAdminAPIKeys as AsyncAdminAPIKeys, + AdminAPIKeysWithRawResponse as AdminAPIKeysWithRawResponse, + AsyncAdminAPIKeysWithRawResponse as AsyncAdminAPIKeysWithRawResponse, + AdminAPIKeysWithStreamingResponse as AdminAPIKeysWithStreamingResponse, + AsyncAdminAPIKeysWithStreamingResponse as AsyncAdminAPIKeysWithStreamingResponse, + ) + from .data_retention import ( + DataRetention as DataRetention, + AsyncDataRetention as AsyncDataRetention, + DataRetentionWithRawResponse as DataRetentionWithRawResponse, + AsyncDataRetentionWithRawResponse as AsyncDataRetentionWithRawResponse, + DataRetentionWithStreamingResponse as DataRetentionWithStreamingResponse, + AsyncDataRetentionWithStreamingResponse as AsyncDataRetentionWithStreamingResponse, + ) + from .external_storage import ( + ExternalStorage as ExternalStorage, + AsyncExternalStorage as AsyncExternalStorage, + ExternalStorageWithRawResponse as ExternalStorageWithRawResponse, + AsyncExternalStorageWithRawResponse as AsyncExternalStorageWithRawResponse, + ExternalStorageWithStreamingResponse as ExternalStorageWithStreamingResponse, + AsyncExternalStorageWithStreamingResponse as AsyncExternalStorageWithStreamingResponse, + ) + +else: + _EXPORTS = { + "AuditLogs": (".audit_logs", "AuditLogs"), + "AsyncAuditLogs": (".audit_logs", "AsyncAuditLogs"), + "AuditLogsWithRawResponse": (".audit_logs", "AuditLogsWithRawResponse"), + "AsyncAuditLogsWithRawResponse": (".audit_logs", "AsyncAuditLogsWithRawResponse"), + "AuditLogsWithStreamingResponse": (".audit_logs", "AuditLogsWithStreamingResponse"), + "AsyncAuditLogsWithStreamingResponse": (".audit_logs", "AsyncAuditLogsWithStreamingResponse"), + "AdminAPIKeys": (".admin_api_keys", "AdminAPIKeys"), + "AsyncAdminAPIKeys": (".admin_api_keys", "AsyncAdminAPIKeys"), + "AdminAPIKeysWithRawResponse": (".admin_api_keys", "AdminAPIKeysWithRawResponse"), + "AsyncAdminAPIKeysWithRawResponse": (".admin_api_keys", "AsyncAdminAPIKeysWithRawResponse"), + "AdminAPIKeysWithStreamingResponse": (".admin_api_keys", "AdminAPIKeysWithStreamingResponse"), + "AsyncAdminAPIKeysWithStreamingResponse": (".admin_api_keys", "AsyncAdminAPIKeysWithStreamingResponse"), + "Usage": (".usage", "Usage"), + "AsyncUsage": (".usage", "AsyncUsage"), + "UsageWithRawResponse": (".usage", "UsageWithRawResponse"), + "AsyncUsageWithRawResponse": (".usage", "AsyncUsageWithRawResponse"), + "UsageWithStreamingResponse": (".usage", "UsageWithStreamingResponse"), + "AsyncUsageWithStreamingResponse": (".usage", "AsyncUsageWithStreamingResponse"), + "Invites": (".invites", "Invites"), + "AsyncInvites": (".invites", "AsyncInvites"), + "InvitesWithRawResponse": (".invites", "InvitesWithRawResponse"), + "AsyncInvitesWithRawResponse": (".invites", "AsyncInvitesWithRawResponse"), + "InvitesWithStreamingResponse": (".invites", "InvitesWithStreamingResponse"), + "AsyncInvitesWithStreamingResponse": (".invites", "AsyncInvitesWithStreamingResponse"), + "Users": (".users", "Users"), + "AsyncUsers": (".users", "AsyncUsers"), + "UsersWithRawResponse": (".users", "UsersWithRawResponse"), + "AsyncUsersWithRawResponse": (".users", "AsyncUsersWithRawResponse"), + "UsersWithStreamingResponse": (".users", "UsersWithStreamingResponse"), + "AsyncUsersWithStreamingResponse": (".users", "AsyncUsersWithStreamingResponse"), + "Groups": (".groups", "Groups"), + "AsyncGroups": (".groups", "AsyncGroups"), + "GroupsWithRawResponse": (".groups", "GroupsWithRawResponse"), + "AsyncGroupsWithRawResponse": (".groups", "AsyncGroupsWithRawResponse"), + "GroupsWithStreamingResponse": (".groups", "GroupsWithStreamingResponse"), + "AsyncGroupsWithStreamingResponse": (".groups", "AsyncGroupsWithStreamingResponse"), + "Roles": (".roles", "Roles"), + "AsyncRoles": (".roles", "AsyncRoles"), + "RolesWithRawResponse": (".roles", "RolesWithRawResponse"), + "AsyncRolesWithRawResponse": (".roles", "AsyncRolesWithRawResponse"), + "RolesWithStreamingResponse": (".roles", "RolesWithStreamingResponse"), + "AsyncRolesWithStreamingResponse": (".roles", "AsyncRolesWithStreamingResponse"), + "DataRetention": (".data_retention", "DataRetention"), + "AsyncDataRetention": (".data_retention", "AsyncDataRetention"), + "DataRetentionWithRawResponse": (".data_retention", "DataRetentionWithRawResponse"), + "AsyncDataRetentionWithRawResponse": (".data_retention", "AsyncDataRetentionWithRawResponse"), + "DataRetentionWithStreamingResponse": (".data_retention", "DataRetentionWithStreamingResponse"), + "AsyncDataRetentionWithStreamingResponse": (".data_retention", "AsyncDataRetentionWithStreamingResponse"), + "ExternalStorage": (".external_storage", "ExternalStorage"), + "AsyncExternalStorage": (".external_storage", "AsyncExternalStorage"), + "ExternalStorageWithRawResponse": (".external_storage", "ExternalStorageWithRawResponse"), + "AsyncExternalStorageWithRawResponse": (".external_storage", "AsyncExternalStorageWithRawResponse"), + "ExternalStorageWithStreamingResponse": (".external_storage", "ExternalStorageWithStreamingResponse"), + "AsyncExternalStorageWithStreamingResponse": (".external_storage", "AsyncExternalStorageWithStreamingResponse"), + "SpendLimit": (".spend_limit", "SpendLimit"), + "AsyncSpendLimit": (".spend_limit", "AsyncSpendLimit"), + "SpendLimitWithRawResponse": (".spend_limit", "SpendLimitWithRawResponse"), + "AsyncSpendLimitWithRawResponse": (".spend_limit", "AsyncSpendLimitWithRawResponse"), + "SpendLimitWithStreamingResponse": (".spend_limit", "SpendLimitWithStreamingResponse"), + "AsyncSpendLimitWithStreamingResponse": (".spend_limit", "AsyncSpendLimitWithStreamingResponse"), + "SpendAlerts": (".spend_alerts", "SpendAlerts"), + "AsyncSpendAlerts": (".spend_alerts", "AsyncSpendAlerts"), + "SpendAlertsWithRawResponse": (".spend_alerts", "SpendAlertsWithRawResponse"), + "AsyncSpendAlertsWithRawResponse": (".spend_alerts", "AsyncSpendAlertsWithRawResponse"), + "SpendAlertsWithStreamingResponse": (".spend_alerts", "SpendAlertsWithStreamingResponse"), + "AsyncSpendAlertsWithStreamingResponse": (".spend_alerts", "AsyncSpendAlertsWithStreamingResponse"), + "Certificates": (".certificates", "Certificates"), + "AsyncCertificates": (".certificates", "AsyncCertificates"), + "CertificatesWithRawResponse": (".certificates", "CertificatesWithRawResponse"), + "AsyncCertificatesWithRawResponse": (".certificates", "AsyncCertificatesWithRawResponse"), + "CertificatesWithStreamingResponse": (".certificates", "CertificatesWithStreamingResponse"), + "AsyncCertificatesWithStreamingResponse": (".certificates", "AsyncCertificatesWithStreamingResponse"), + "Projects": (".projects", "Projects"), + "AsyncProjects": (".projects", "AsyncProjects"), + "ProjectsWithRawResponse": (".projects", "ProjectsWithRawResponse"), + "AsyncProjectsWithRawResponse": (".projects", "AsyncProjectsWithRawResponse"), + "ProjectsWithStreamingResponse": (".projects", "ProjectsWithStreamingResponse"), + "AsyncProjectsWithStreamingResponse": (".projects", "AsyncProjectsWithStreamingResponse"), + "Organization": (".organization", "Organization"), + "AsyncOrganization": (".organization", "AsyncOrganization"), + "OrganizationWithRawResponse": (".organization", "OrganizationWithRawResponse"), + "AsyncOrganizationWithRawResponse": (".organization", "AsyncOrganizationWithRawResponse"), + "OrganizationWithStreamingResponse": (".organization", "OrganizationWithStreamingResponse"), + "AsyncOrganizationWithStreamingResponse": (".organization", "AsyncOrganizationWithStreamingResponse"), + } + _SUBMODULES = { + "admin_api_keys", + "audit_logs", + "certificates", + "data_retention", + "external_storage", + "groups", + "invites", + "organization", + "projects", + "roles", + "spend_alerts", + "spend_limit", + "usage", + "users", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "AuditLogs", + "AsyncAuditLogs", + "AuditLogsWithRawResponse", + "AsyncAuditLogsWithRawResponse", + "AuditLogsWithStreamingResponse", + "AsyncAuditLogsWithStreamingResponse", + "AdminAPIKeys", + "AsyncAdminAPIKeys", + "AdminAPIKeysWithRawResponse", + "AsyncAdminAPIKeysWithRawResponse", + "AdminAPIKeysWithStreamingResponse", + "AsyncAdminAPIKeysWithStreamingResponse", + "Usage", + "AsyncUsage", + "UsageWithRawResponse", + "AsyncUsageWithRawResponse", + "UsageWithStreamingResponse", + "AsyncUsageWithStreamingResponse", + "Invites", + "AsyncInvites", + "InvitesWithRawResponse", + "AsyncInvitesWithRawResponse", + "InvitesWithStreamingResponse", + "AsyncInvitesWithStreamingResponse", + "Users", + "AsyncUsers", + "UsersWithRawResponse", + "AsyncUsersWithRawResponse", + "UsersWithStreamingResponse", + "AsyncUsersWithStreamingResponse", + "Groups", + "AsyncGroups", + "GroupsWithRawResponse", + "AsyncGroupsWithRawResponse", + "GroupsWithStreamingResponse", + "AsyncGroupsWithStreamingResponse", + "Roles", + "AsyncRoles", + "RolesWithRawResponse", + "AsyncRolesWithRawResponse", + "RolesWithStreamingResponse", + "AsyncRolesWithStreamingResponse", + "DataRetention", + "AsyncDataRetention", + "DataRetentionWithRawResponse", + "AsyncDataRetentionWithRawResponse", + "DataRetentionWithStreamingResponse", + "AsyncDataRetentionWithStreamingResponse", + "ExternalStorage", + "AsyncExternalStorage", + "ExternalStorageWithRawResponse", + "AsyncExternalStorageWithRawResponse", + "ExternalStorageWithStreamingResponse", + "AsyncExternalStorageWithStreamingResponse", + "SpendLimit", + "AsyncSpendLimit", + "SpendLimitWithRawResponse", + "AsyncSpendLimitWithRawResponse", + "SpendLimitWithStreamingResponse", + "AsyncSpendLimitWithStreamingResponse", + "SpendAlerts", + "AsyncSpendAlerts", + "SpendAlertsWithRawResponse", + "AsyncSpendAlertsWithRawResponse", + "SpendAlertsWithStreamingResponse", + "AsyncSpendAlertsWithStreamingResponse", + "Certificates", + "AsyncCertificates", + "CertificatesWithRawResponse", + "AsyncCertificatesWithRawResponse", + "CertificatesWithStreamingResponse", + "AsyncCertificatesWithStreamingResponse", + "Projects", + "AsyncProjects", + "ProjectsWithRawResponse", + "AsyncProjectsWithRawResponse", + "ProjectsWithStreamingResponse", + "AsyncProjectsWithStreamingResponse", + "Organization", + "AsyncOrganization", + "OrganizationWithRawResponse", + "AsyncOrganizationWithRawResponse", + "OrganizationWithStreamingResponse", + "AsyncOrganizationWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0eaafab Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/admin_api_keys.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/admin_api_keys.cpython-312.pyc new file mode 100644 index 0000000..5a07abe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/admin_api_keys.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/audit_logs.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/audit_logs.cpython-312.pyc new file mode 100644 index 0000000..70e685d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/audit_logs.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/certificates.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/certificates.cpython-312.pyc new file mode 100644 index 0000000..bc0d5d2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/certificates.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/data_retention.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/data_retention.cpython-312.pyc new file mode 100644 index 0000000..a4701c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/data_retention.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/external_storage.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/external_storage.cpython-312.pyc new file mode 100644 index 0000000..f94139a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/external_storage.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/invites.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/invites.cpython-312.pyc new file mode 100644 index 0000000..0587ae4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/invites.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/organization.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/organization.cpython-312.pyc new file mode 100644 index 0000000..351f547 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/organization.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/roles.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/roles.cpython-312.pyc new file mode 100644 index 0000000..b63996c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/roles.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/spend_alerts.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/spend_alerts.cpython-312.pyc new file mode 100644 index 0000000..f63e261 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/spend_alerts.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/spend_limit.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/spend_limit.cpython-312.pyc new file mode 100644 index 0000000..3489d61 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/spend_limit.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/usage.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/usage.cpython-312.pyc new file mode 100644 index 0000000..498aee2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/__pycache__/usage.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/admin_api_keys.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/admin_api_keys.py new file mode 100644 index 0000000..518413e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/admin_api_keys.py @@ -0,0 +1,489 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import admin_api_key_list_params, admin_api_key_create_params +from ....types.admin.organization.admin_api_key import AdminAPIKey +from ....types.admin.organization.admin_api_key_create_response import AdminAPIKeyCreateResponse +from ....types.admin.organization.admin_api_key_delete_response import AdminAPIKeyDeleteResponse + +__all__ = ["AdminAPIKeys", "AsyncAdminAPIKeys"] + + +class AdminAPIKeys(SyncAPIResource): + @cached_property + def with_raw_response(self) -> AdminAPIKeysWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AdminAPIKeysWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AdminAPIKeysWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AdminAPIKeysWithStreamingResponse(self) + + def create( + self, + *, + name: str, + expires_in_seconds: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AdminAPIKeyCreateResponse: + """ + Create an organization admin API key + + Args: + expires_in_seconds: The number of seconds until the API key expires. Omit this field for a key that + does not expire. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/admin_api_keys", + body=maybe_transform( + { + "name": name, + "expires_in_seconds": expires_in_seconds, + }, + admin_api_key_create_params.AdminAPIKeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=AdminAPIKeyCreateResponse, + ) + + def retrieve( + self, + key_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AdminAPIKey: + """ + Retrieve a single organization API key + + Args: + key_id: The ID of the API key. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not key_id: + raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + return self._get( + path_template("/organization/admin_api_keys/{key_id}", key_id=key_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=AdminAPIKey, + ) + + def list( + self, + *, + after: Optional[str] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[AdminAPIKey]: + """ + List organization API keys + + Args: + after: Return keys with IDs that come after this ID in the pagination order. + + limit: Maximum number of keys to return. + + order: Order results by creation time, ascending or descending. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/admin_api_keys", + page=SyncCursorPage[AdminAPIKey], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + admin_api_key_list_params.AdminAPIKeyListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=AdminAPIKey, + ) + + def delete( + self, + key_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AdminAPIKeyDeleteResponse: + """ + Delete an organization admin API key + + Args: + key_id: The ID of the API key to be deleted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not key_id: + raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + return self._delete( + path_template("/organization/admin_api_keys/{key_id}", key_id=key_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=AdminAPIKeyDeleteResponse, + ) + + +class AsyncAdminAPIKeys(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAdminAPIKeysWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAdminAPIKeysWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAdminAPIKeysWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAdminAPIKeysWithStreamingResponse(self) + + async def create( + self, + *, + name: str, + expires_in_seconds: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AdminAPIKeyCreateResponse: + """ + Create an organization admin API key + + Args: + expires_in_seconds: The number of seconds until the API key expires. Omit this field for a key that + does not expire. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/admin_api_keys", + body=await async_maybe_transform( + { + "name": name, + "expires_in_seconds": expires_in_seconds, + }, + admin_api_key_create_params.AdminAPIKeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=AdminAPIKeyCreateResponse, + ) + + async def retrieve( + self, + key_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AdminAPIKey: + """ + Retrieve a single organization API key + + Args: + key_id: The ID of the API key. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not key_id: + raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + return await self._get( + path_template("/organization/admin_api_keys/{key_id}", key_id=key_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=AdminAPIKey, + ) + + def list( + self, + *, + after: Optional[str] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[AdminAPIKey, AsyncCursorPage[AdminAPIKey]]: + """ + List organization API keys + + Args: + after: Return keys with IDs that come after this ID in the pagination order. + + limit: Maximum number of keys to return. + + order: Order results by creation time, ascending or descending. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/admin_api_keys", + page=AsyncCursorPage[AdminAPIKey], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + admin_api_key_list_params.AdminAPIKeyListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=AdminAPIKey, + ) + + async def delete( + self, + key_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AdminAPIKeyDeleteResponse: + """ + Delete an organization admin API key + + Args: + key_id: The ID of the API key to be deleted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not key_id: + raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + return await self._delete( + path_template("/organization/admin_api_keys/{key_id}", key_id=key_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=AdminAPIKeyDeleteResponse, + ) + + +class AdminAPIKeysWithRawResponse: + def __init__(self, admin_api_keys: AdminAPIKeys) -> None: + self._admin_api_keys = admin_api_keys + + self.create = _legacy_response.to_raw_response_wrapper( + admin_api_keys.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + admin_api_keys.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + admin_api_keys.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + admin_api_keys.delete, + ) + + +class AsyncAdminAPIKeysWithRawResponse: + def __init__(self, admin_api_keys: AsyncAdminAPIKeys) -> None: + self._admin_api_keys = admin_api_keys + + self.create = _legacy_response.async_to_raw_response_wrapper( + admin_api_keys.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + admin_api_keys.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + admin_api_keys.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + admin_api_keys.delete, + ) + + +class AdminAPIKeysWithStreamingResponse: + def __init__(self, admin_api_keys: AdminAPIKeys) -> None: + self._admin_api_keys = admin_api_keys + + self.create = to_streamed_response_wrapper( + admin_api_keys.create, + ) + self.retrieve = to_streamed_response_wrapper( + admin_api_keys.retrieve, + ) + self.list = to_streamed_response_wrapper( + admin_api_keys.list, + ) + self.delete = to_streamed_response_wrapper( + admin_api_keys.delete, + ) + + +class AsyncAdminAPIKeysWithStreamingResponse: + def __init__(self, admin_api_keys: AsyncAdminAPIKeys) -> None: + self._admin_api_keys = admin_api_keys + + self.create = async_to_streamed_response_wrapper( + admin_api_keys.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + admin_api_keys.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + admin_api_keys.list, + ) + self.delete = async_to_streamed_response_wrapper( + admin_api_keys.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/audit_logs.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/audit_logs.py new file mode 100644 index 0000000..f1ceb46 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/audit_logs.py @@ -0,0 +1,599 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import audit_log_list_params +from ....types.admin.organization.audit_log_list_response import AuditLogListResponse + +__all__ = ["AuditLogs", "AsyncAuditLogs"] + + +class AuditLogs(SyncAPIResource): + """List user actions and configuration changes within this organization.""" + + @cached_property + def with_raw_response(self) -> AuditLogsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AuditLogsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AuditLogsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AuditLogsWithStreamingResponse(self) + + def list( + self, + *, + actor_emails: SequenceNotStr[str] | Omit = omit, + actor_ids: SequenceNotStr[str] | Omit = omit, + after: str | Omit = omit, + before: str | Omit = omit, + effective_at: audit_log_list_params.EffectiveAt | Omit = omit, + event_types: List[ + Literal[ + "api_key.created", + "api_key.updated", + "api_key.deleted", + "certificate.created", + "certificate.updated", + "certificate.deleted", + "certificates.activated", + "certificates.deactivated", + "checkpoint.permission.created", + "checkpoint.permission.deleted", + "external_key.registered", + "external_key.removed", + "external_storage.registered", + "external_storage.removed", + "group.created", + "group.updated", + "group.deleted", + "invite.sent", + "invite.accepted", + "invite.deleted", + "ip_allowlist.created", + "ip_allowlist.updated", + "ip_allowlist.deleted", + "ip_allowlist.config.activated", + "ip_allowlist.config.deactivated", + "login.succeeded", + "login.failed", + "logout.succeeded", + "logout.failed", + "organization.updated", + "project.created", + "project.updated", + "project.archived", + "project.deleted", + "rate_limit.updated", + "rate_limit.deleted", + "resource.deleted", + "tunnel.created", + "tunnel.updated", + "tunnel.deleted", + "workload_identity_provider.created", + "workload_identity_provider.updated", + "workload_identity_provider.deleted", + "workload_identity_provider_mapping.created", + "workload_identity_provider_mapping.updated", + "workload_identity_provider_mapping.deleted", + "role.created", + "role.updated", + "role.deleted", + "role.assignment.created", + "role.assignment.deleted", + "role.bound_to_resource", + "role.unbound_from_resource", + "scim.enabled", + "scim.disabled", + "service_account.created", + "service_account.updated", + "service_account.deleted", + "user.added", + "user.updated", + "user.deleted", + "tenant.metadata.updated", + "tenant.microsoft_entra_mapping.upserted", + "tenant.microsoft_entra_mapping.deleted", + "tenant.workload_identity.provider.created", + "tenant.workload_identity.provider.updated", + "tenant.workload_identity.provider.archived", + "tenant.workload_identity.mapping.created", + "tenant.workload_identity.mapping.updated", + "tenant.workload_identity.mapping.archived", + "tenant.workload_identity.binding.created", + "tenant.workload_identity.principal.provisioned", + "tenant.workload_identity.access_token.issued", + "tenant.admin_api_key.created", + "tenant.admin_api_key.updated", + "tenant.admin_api_key.deleted", + "tenant.project_api_key.created", + "tenant.trusted_access.business_verification.started", + "tenant.trusted_access.application.submitted", + "tenant.chatgpt_access_token.revoked", + "tenant.migration.completed", + "tenant.sso.migrated", + "tenant.domains.migrated", + "tenant.sso_connection.created", + "tenant.sso_connection.updated", + "tenant.sso_connection.deleted", + "tenant.sso_connection.setup.started", + "tenant.policy.created", + "tenant.policy.updated", + "tenant.policy.deleted", + "tenant.policy.attached", + "tenant.policy.detached", + "tenant.principal_authentication_policy.resolved", + "tenant.scim.setup.started", + "tenant.scim.deletion.requested", + "tenant.scim.directory.created", + "tenant.product_access_policy.updated", + "tenant.resource_share_grant.created", + "tenant.resource_share_grant.updated", + "tenant.resource_share_grant.accepted", + "tenant.resource_share_grant.declined", + "tenant.resource_share_grant.revoked", + "tenant.resource_share_grant.deleted", + "tenant.service_account.updated", + "tenant.service_account.deleted", + "tenant.service_account.token.revoked", + "tenant.billing.overage_limit.updated", + "tenant.billing.alerts.updated", + "tenant.billing.info.updated", + "tenant.usage_limit.workspace.updated", + "tenant.usage_limit.group.updated", + "tenant.usage_limit.user.updated", + "tenant.usage_limit.increase_request.updated", + "tenant.usage_limit.increase_request.resolved", + "tenant.group.created", + "tenant.group.updated", + "tenant.group.deleted", + "tenant.group.member.added", + "tenant.group.member.removed", + "tenant.migration_rollout.status.updated", + "tenant.migration_rollout.tier.updated", + "tenant.role.metadata.updated", + "tenant.custom_role.created", + "tenant.custom_role.updated", + "tenant.custom_role.deleted", + "tenant.role_assignment.created", + "tenant.role_assignment.deleted", + "tenant.resource_role_assignment.created", + "tenant.resource_role_assignment.deleted", + "tenant.resource_access.updated", + "tenant.resource_access.deleted", + "tenant.ads_account.onboarding.redemption", + "tenant.session_policy.created", + "tenant.session_policy.updated", + "tenant.session_policy.deleted", + "tenant.session_revocation.started", + "tenant.third_party_app_policy.updated", + "tenant.user.added", + "tenant.user.updated", + "tenant.user.removed", + "tenant.user.looked_up", + "tenant.user.invited", + "tenant.membership.revoked", + "tenant.api_organization_invite.upserted", + "tenant.api_organization_invite.deleted", + "tenant.chatgpt_workspace_invite.upserted", + "tenant.membership.accepted", + "tenant.membership.declined", + "tenant.workspace_invite_email_settings.updated", + ] + ] + | Omit = omit, + limit: int | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + resource_ids: SequenceNotStr[str] | Omit = omit, + tenant_only: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[AuditLogListResponse]: + """ + List user actions and configuration changes within this organization. + + Args: + actor_emails: Return only events performed by users with these emails. + + actor_ids: Return only events performed by these actors. Can be a user ID, a service + account ID, or an api key tracking ID. + + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + effective_at: Return only events whose `effective_at` (Unix seconds) is in this range. + + event_types: Return only events with a `type` in one of these values. For example, + `project.created`. For all options, see the documentation for the + [audit log object](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/audit_logs). + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + project_ids: Return only events for these projects. + + resource_ids: Return only events performed on these targets. For example, a project ID + updated. For ChatGPT connector role events, use the workspace connector resource + ID shown in `details.id`, such as `__`. + + tenant_only: Return only tenant-scoped events associated with this organization. Required for + tenant-scoped events such as `role.bound_to_resource` and + `role.unbound_from_resource`. When `true`, all supplied event types must be + tenant-scoped. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/audit_logs", + page=SyncConversationCursorPage[AuditLogListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "actor_emails": actor_emails, + "actor_ids": actor_ids, + "after": after, + "before": before, + "effective_at": effective_at, + "event_types": event_types, + "limit": limit, + "project_ids": project_ids, + "resource_ids": resource_ids, + "tenant_only": tenant_only, + }, + audit_log_list_params.AuditLogListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=AuditLogListResponse, + ) + + +class AsyncAuditLogs(AsyncAPIResource): + """List user actions and configuration changes within this organization.""" + + @cached_property + def with_raw_response(self) -> AsyncAuditLogsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAuditLogsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAuditLogsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAuditLogsWithStreamingResponse(self) + + def list( + self, + *, + actor_emails: SequenceNotStr[str] | Omit = omit, + actor_ids: SequenceNotStr[str] | Omit = omit, + after: str | Omit = omit, + before: str | Omit = omit, + effective_at: audit_log_list_params.EffectiveAt | Omit = omit, + event_types: List[ + Literal[ + "api_key.created", + "api_key.updated", + "api_key.deleted", + "certificate.created", + "certificate.updated", + "certificate.deleted", + "certificates.activated", + "certificates.deactivated", + "checkpoint.permission.created", + "checkpoint.permission.deleted", + "external_key.registered", + "external_key.removed", + "external_storage.registered", + "external_storage.removed", + "group.created", + "group.updated", + "group.deleted", + "invite.sent", + "invite.accepted", + "invite.deleted", + "ip_allowlist.created", + "ip_allowlist.updated", + "ip_allowlist.deleted", + "ip_allowlist.config.activated", + "ip_allowlist.config.deactivated", + "login.succeeded", + "login.failed", + "logout.succeeded", + "logout.failed", + "organization.updated", + "project.created", + "project.updated", + "project.archived", + "project.deleted", + "rate_limit.updated", + "rate_limit.deleted", + "resource.deleted", + "tunnel.created", + "tunnel.updated", + "tunnel.deleted", + "workload_identity_provider.created", + "workload_identity_provider.updated", + "workload_identity_provider.deleted", + "workload_identity_provider_mapping.created", + "workload_identity_provider_mapping.updated", + "workload_identity_provider_mapping.deleted", + "role.created", + "role.updated", + "role.deleted", + "role.assignment.created", + "role.assignment.deleted", + "role.bound_to_resource", + "role.unbound_from_resource", + "scim.enabled", + "scim.disabled", + "service_account.created", + "service_account.updated", + "service_account.deleted", + "user.added", + "user.updated", + "user.deleted", + "tenant.metadata.updated", + "tenant.microsoft_entra_mapping.upserted", + "tenant.microsoft_entra_mapping.deleted", + "tenant.workload_identity.provider.created", + "tenant.workload_identity.provider.updated", + "tenant.workload_identity.provider.archived", + "tenant.workload_identity.mapping.created", + "tenant.workload_identity.mapping.updated", + "tenant.workload_identity.mapping.archived", + "tenant.workload_identity.binding.created", + "tenant.workload_identity.principal.provisioned", + "tenant.workload_identity.access_token.issued", + "tenant.admin_api_key.created", + "tenant.admin_api_key.updated", + "tenant.admin_api_key.deleted", + "tenant.project_api_key.created", + "tenant.trusted_access.business_verification.started", + "tenant.trusted_access.application.submitted", + "tenant.chatgpt_access_token.revoked", + "tenant.migration.completed", + "tenant.sso.migrated", + "tenant.domains.migrated", + "tenant.sso_connection.created", + "tenant.sso_connection.updated", + "tenant.sso_connection.deleted", + "tenant.sso_connection.setup.started", + "tenant.policy.created", + "tenant.policy.updated", + "tenant.policy.deleted", + "tenant.policy.attached", + "tenant.policy.detached", + "tenant.principal_authentication_policy.resolved", + "tenant.scim.setup.started", + "tenant.scim.deletion.requested", + "tenant.scim.directory.created", + "tenant.product_access_policy.updated", + "tenant.resource_share_grant.created", + "tenant.resource_share_grant.updated", + "tenant.resource_share_grant.accepted", + "tenant.resource_share_grant.declined", + "tenant.resource_share_grant.revoked", + "tenant.resource_share_grant.deleted", + "tenant.service_account.updated", + "tenant.service_account.deleted", + "tenant.service_account.token.revoked", + "tenant.billing.overage_limit.updated", + "tenant.billing.alerts.updated", + "tenant.billing.info.updated", + "tenant.usage_limit.workspace.updated", + "tenant.usage_limit.group.updated", + "tenant.usage_limit.user.updated", + "tenant.usage_limit.increase_request.updated", + "tenant.usage_limit.increase_request.resolved", + "tenant.group.created", + "tenant.group.updated", + "tenant.group.deleted", + "tenant.group.member.added", + "tenant.group.member.removed", + "tenant.migration_rollout.status.updated", + "tenant.migration_rollout.tier.updated", + "tenant.role.metadata.updated", + "tenant.custom_role.created", + "tenant.custom_role.updated", + "tenant.custom_role.deleted", + "tenant.role_assignment.created", + "tenant.role_assignment.deleted", + "tenant.resource_role_assignment.created", + "tenant.resource_role_assignment.deleted", + "tenant.resource_access.updated", + "tenant.resource_access.deleted", + "tenant.ads_account.onboarding.redemption", + "tenant.session_policy.created", + "tenant.session_policy.updated", + "tenant.session_policy.deleted", + "tenant.session_revocation.started", + "tenant.third_party_app_policy.updated", + "tenant.user.added", + "tenant.user.updated", + "tenant.user.removed", + "tenant.user.looked_up", + "tenant.user.invited", + "tenant.membership.revoked", + "tenant.api_organization_invite.upserted", + "tenant.api_organization_invite.deleted", + "tenant.chatgpt_workspace_invite.upserted", + "tenant.membership.accepted", + "tenant.membership.declined", + "tenant.workspace_invite_email_settings.updated", + ] + ] + | Omit = omit, + limit: int | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + resource_ids: SequenceNotStr[str] | Omit = omit, + tenant_only: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[AuditLogListResponse, AsyncConversationCursorPage[AuditLogListResponse]]: + """ + List user actions and configuration changes within this organization. + + Args: + actor_emails: Return only events performed by users with these emails. + + actor_ids: Return only events performed by these actors. Can be a user ID, a service + account ID, or an api key tracking ID. + + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + effective_at: Return only events whose `effective_at` (Unix seconds) is in this range. + + event_types: Return only events with a `type` in one of these values. For example, + `project.created`. For all options, see the documentation for the + [audit log object](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/audit_logs). + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + project_ids: Return only events for these projects. + + resource_ids: Return only events performed on these targets. For example, a project ID + updated. For ChatGPT connector role events, use the workspace connector resource + ID shown in `details.id`, such as `__`. + + tenant_only: Return only tenant-scoped events associated with this organization. Required for + tenant-scoped events such as `role.bound_to_resource` and + `role.unbound_from_resource`. When `true`, all supplied event types must be + tenant-scoped. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/audit_logs", + page=AsyncConversationCursorPage[AuditLogListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "actor_emails": actor_emails, + "actor_ids": actor_ids, + "after": after, + "before": before, + "effective_at": effective_at, + "event_types": event_types, + "limit": limit, + "project_ids": project_ids, + "resource_ids": resource_ids, + "tenant_only": tenant_only, + }, + audit_log_list_params.AuditLogListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=AuditLogListResponse, + ) + + +class AuditLogsWithRawResponse: + def __init__(self, audit_logs: AuditLogs) -> None: + self._audit_logs = audit_logs + + self.list = _legacy_response.to_raw_response_wrapper( + audit_logs.list, + ) + + +class AsyncAuditLogsWithRawResponse: + def __init__(self, audit_logs: AsyncAuditLogs) -> None: + self._audit_logs = audit_logs + + self.list = _legacy_response.async_to_raw_response_wrapper( + audit_logs.list, + ) + + +class AuditLogsWithStreamingResponse: + def __init__(self, audit_logs: AuditLogs) -> None: + self._audit_logs = audit_logs + + self.list = to_streamed_response_wrapper( + audit_logs.list, + ) + + +class AsyncAuditLogsWithStreamingResponse: + def __init__(self, audit_logs: AsyncAuditLogs) -> None: + self._audit_logs = audit_logs + + self.list = async_to_streamed_response_wrapper( + audit_logs.list, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/certificates.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/certificates.py new file mode 100644 index 0000000..e2f0c08 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/certificates.py @@ -0,0 +1,818 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncPage, AsyncPage, SyncConversationCursorPage, AsyncConversationCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import ( + certificate_list_params, + certificate_create_params, + certificate_update_params, + certificate_activate_params, + certificate_retrieve_params, + certificate_deactivate_params, +) +from ....types.admin.organization.certificate import Certificate +from ....types.admin.organization.certificate_list_response import CertificateListResponse +from ....types.admin.organization.certificate_delete_response import CertificateDeleteResponse +from ....types.admin.organization.certificate_activate_response import CertificateActivateResponse +from ....types.admin.organization.certificate_deactivate_response import CertificateDeactivateResponse + +__all__ = ["Certificates", "AsyncCertificates"] + + +class Certificates(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CertificatesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CertificatesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CertificatesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CertificatesWithStreamingResponse(self) + + def create( + self, + *, + certificate: str, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Certificate: + """Upload a certificate to the organization. + + This does **not** automatically + activate the certificate. + + Organizations can upload up to 50 certificates. + + Args: + certificate: The certificate content in PEM format + + name: An optional name for the certificate + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/certificates", + body=maybe_transform( + { + "certificate": certificate, + "name": name, + }, + certificate_create_params.CertificateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Certificate, + ) + + def retrieve( + self, + certificate_id: str, + *, + include: List[Literal["content"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Certificate: + """ + Get a certificate that has been uploaded to the organization. + + You can get a certificate regardless of whether it is active or not. + + Args: + include: A list of additional fields to include in the response. Currently the only + supported value is `content` to fetch the PEM content of the certificate. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not certificate_id: + raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") + return self._get( + path_template("/organization/certificates/{certificate_id}", certificate_id=certificate_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, certificate_retrieve_params.CertificateRetrieveParams), + security={"admin_api_key_auth": True}, + ), + cast_to=Certificate, + ) + + def update( + self, + certificate_id: str, + *, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Certificate: + """Modify a certificate. + + Note that only the name can be modified. + + Args: + name: The updated name for the certificate + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not certificate_id: + raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") + return self._post( + path_template("/organization/certificates/{certificate_id}", certificate_id=certificate_id), + body=maybe_transform({"name": name}, certificate_update_params.CertificateUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Certificate, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[CertificateListResponse]: + """ + List uploaded certificates for this organization. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/certificates", + page=SyncConversationCursorPage[CertificateListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + certificate_list_params.CertificateListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=CertificateListResponse, + ) + + def delete( + self, + certificate_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> CertificateDeleteResponse: + """ + Delete a certificate from the organization. + + The certificate must be inactive for the organization and all projects. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not certificate_id: + raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") + return self._delete( + path_template("/organization/certificates/{certificate_id}", certificate_id=certificate_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=CertificateDeleteResponse, + ) + + def activate( + self, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncPage[CertificateActivateResponse]: + """ + Activate certificates at the organization level. + + You can atomically and idempotently activate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/certificates/activate", + page=SyncPage[CertificateActivateResponse], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_activate_params.CertificateActivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=CertificateActivateResponse, + method="post", + ) + + def deactivate( + self, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncPage[CertificateDeactivateResponse]: + """ + Deactivate certificates at the organization level. + + You can atomically and idempotently deactivate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/certificates/deactivate", + page=SyncPage[CertificateDeactivateResponse], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_deactivate_params.CertificateDeactivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=CertificateDeactivateResponse, + method="post", + ) + + +class AsyncCertificates(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCertificatesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCertificatesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCertificatesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCertificatesWithStreamingResponse(self) + + async def create( + self, + *, + certificate: str, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Certificate: + """Upload a certificate to the organization. + + This does **not** automatically + activate the certificate. + + Organizations can upload up to 50 certificates. + + Args: + certificate: The certificate content in PEM format + + name: An optional name for the certificate + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/certificates", + body=await async_maybe_transform( + { + "certificate": certificate, + "name": name, + }, + certificate_create_params.CertificateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Certificate, + ) + + async def retrieve( + self, + certificate_id: str, + *, + include: List[Literal["content"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Certificate: + """ + Get a certificate that has been uploaded to the organization. + + You can get a certificate regardless of whether it is active or not. + + Args: + include: A list of additional fields to include in the response. Currently the only + supported value is `content` to fetch the PEM content of the certificate. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not certificate_id: + raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") + return await self._get( + path_template("/organization/certificates/{certificate_id}", certificate_id=certificate_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"include": include}, certificate_retrieve_params.CertificateRetrieveParams + ), + security={"admin_api_key_auth": True}, + ), + cast_to=Certificate, + ) + + async def update( + self, + certificate_id: str, + *, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Certificate: + """Modify a certificate. + + Note that only the name can be modified. + + Args: + name: The updated name for the certificate + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not certificate_id: + raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") + return await self._post( + path_template("/organization/certificates/{certificate_id}", certificate_id=certificate_id), + body=await async_maybe_transform({"name": name}, certificate_update_params.CertificateUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Certificate, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[CertificateListResponse, AsyncConversationCursorPage[CertificateListResponse]]: + """ + List uploaded certificates for this organization. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/certificates", + page=AsyncConversationCursorPage[CertificateListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + certificate_list_params.CertificateListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=CertificateListResponse, + ) + + async def delete( + self, + certificate_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> CertificateDeleteResponse: + """ + Delete a certificate from the organization. + + The certificate must be inactive for the organization and all projects. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not certificate_id: + raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") + return await self._delete( + path_template("/organization/certificates/{certificate_id}", certificate_id=certificate_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=CertificateDeleteResponse, + ) + + def activate( + self, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[CertificateActivateResponse, AsyncPage[CertificateActivateResponse]]: + """ + Activate certificates at the organization level. + + You can atomically and idempotently activate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/certificates/activate", + page=AsyncPage[CertificateActivateResponse], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_activate_params.CertificateActivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=CertificateActivateResponse, + method="post", + ) + + def deactivate( + self, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[CertificateDeactivateResponse, AsyncPage[CertificateDeactivateResponse]]: + """ + Deactivate certificates at the organization level. + + You can atomically and idempotently deactivate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/certificates/deactivate", + page=AsyncPage[CertificateDeactivateResponse], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_deactivate_params.CertificateDeactivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=CertificateDeactivateResponse, + method="post", + ) + + +class CertificatesWithRawResponse: + def __init__(self, certificates: Certificates) -> None: + self._certificates = certificates + + self.create = _legacy_response.to_raw_response_wrapper( + certificates.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + certificates.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + certificates.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + certificates.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + certificates.delete, + ) + self.activate = _legacy_response.to_raw_response_wrapper( + certificates.activate, + ) + self.deactivate = _legacy_response.to_raw_response_wrapper( + certificates.deactivate, + ) + + +class AsyncCertificatesWithRawResponse: + def __init__(self, certificates: AsyncCertificates) -> None: + self._certificates = certificates + + self.create = _legacy_response.async_to_raw_response_wrapper( + certificates.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + certificates.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + certificates.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + certificates.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + certificates.delete, + ) + self.activate = _legacy_response.async_to_raw_response_wrapper( + certificates.activate, + ) + self.deactivate = _legacy_response.async_to_raw_response_wrapper( + certificates.deactivate, + ) + + +class CertificatesWithStreamingResponse: + def __init__(self, certificates: Certificates) -> None: + self._certificates = certificates + + self.create = to_streamed_response_wrapper( + certificates.create, + ) + self.retrieve = to_streamed_response_wrapper( + certificates.retrieve, + ) + self.update = to_streamed_response_wrapper( + certificates.update, + ) + self.list = to_streamed_response_wrapper( + certificates.list, + ) + self.delete = to_streamed_response_wrapper( + certificates.delete, + ) + self.activate = to_streamed_response_wrapper( + certificates.activate, + ) + self.deactivate = to_streamed_response_wrapper( + certificates.deactivate, + ) + + +class AsyncCertificatesWithStreamingResponse: + def __init__(self, certificates: AsyncCertificates) -> None: + self._certificates = certificates + + self.create = async_to_streamed_response_wrapper( + certificates.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + certificates.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + certificates.update, + ) + self.list = async_to_streamed_response_wrapper( + certificates.list, + ) + self.delete = async_to_streamed_response_wrapper( + certificates.delete, + ) + self.activate = async_to_streamed_response_wrapper( + certificates.activate, + ) + self.deactivate = async_to_streamed_response_wrapper( + certificates.deactivate, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/data_retention.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/data_retention.py new file mode 100644 index 0000000..cb2ff61 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/data_retention.py @@ -0,0 +1,245 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.admin.organization import data_retention_update_params +from ....types.admin.organization.organization_data_retention import OrganizationDataRetention + +__all__ = ["DataRetention", "AsyncDataRetention"] + + +class DataRetention(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DataRetentionWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return DataRetentionWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DataRetentionWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return DataRetentionWithStreamingResponse(self) + + def retrieve( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationDataRetention: + """Retrieves organization data retention controls.""" + return self._get( + "/organization/data_retention", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationDataRetention, + ) + + def update( + self, + *, + retention_type: Literal[ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationDataRetention: + """ + Updates organization data retention controls. + + Args: + retention_type: The desired organization data retention type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/data_retention", + body=maybe_transform( + {"retention_type": retention_type}, data_retention_update_params.DataRetentionUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationDataRetention, + ) + + +class AsyncDataRetention(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDataRetentionWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncDataRetentionWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDataRetentionWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncDataRetentionWithStreamingResponse(self) + + async def retrieve( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationDataRetention: + """Retrieves organization data retention controls.""" + return await self._get( + "/organization/data_retention", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationDataRetention, + ) + + async def update( + self, + *, + retention_type: Literal[ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationDataRetention: + """ + Updates organization data retention controls. + + Args: + retention_type: The desired organization data retention type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/data_retention", + body=await async_maybe_transform( + {"retention_type": retention_type}, data_retention_update_params.DataRetentionUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationDataRetention, + ) + + +class DataRetentionWithRawResponse: + def __init__(self, data_retention: DataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = _legacy_response.to_raw_response_wrapper( + data_retention.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + data_retention.update, + ) + + +class AsyncDataRetentionWithRawResponse: + def __init__(self, data_retention: AsyncDataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + data_retention.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + data_retention.update, + ) + + +class DataRetentionWithStreamingResponse: + def __init__(self, data_retention: DataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = to_streamed_response_wrapper( + data_retention.retrieve, + ) + self.update = to_streamed_response_wrapper( + data_retention.update, + ) + + +class AsyncDataRetentionWithStreamingResponse: + def __init__(self, data_retention: AsyncDataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = async_to_streamed_response_wrapper( + data_retention.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + data_retention.update, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/external_storage.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/external_storage.py new file mode 100644 index 0000000..20fc34a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/external_storage.py @@ -0,0 +1,591 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import ( + external_storage_list_params, + external_storage_create_params, +) +from ....types.admin.organization.external_storage_deleted import ExternalStorageDeleted +from ....types.admin.organization.external_storage_configuration import ExternalStorageConfiguration + +__all__ = ["ExternalStorage", "AsyncExternalStorage"] + + +class ExternalStorage(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ExternalStorageWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ExternalStorageWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExternalStorageWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ExternalStorageWithStreamingResponse(self) + + def create( + self, + *, + project_id: str, + provider: external_storage_create_params.Provider, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ExternalStorageConfiguration: + """ + Register one customer-managed external storage configuration. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/external_storage", + body=maybe_transform( + { + "project_id": project_id, + "provider": provider, + }, + external_storage_create_params.ExternalStorageCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ExternalStorageConfiguration, + ) + + def retrieve( + self, + external_storage_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ExternalStorageConfiguration: + """ + Get one customer-managed external storage configuration. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not external_storage_id: + raise ValueError( + f"Expected a non-empty value for `external_storage_id` but received {external_storage_id!r}" + ) + return self._get( + path_template( + "/organization/external_storage/{external_storage_id}", external_storage_id=external_storage_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ExternalStorageConfiguration, + ) + + def list( + self, + *, + after: Optional[str] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + project_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[ExternalStorageConfiguration]: + """ + List the organization's customer-managed external storage configurations. + + Args: + after: Return external storage configurations after this ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/external_storage", + page=SyncCursorPage[ExternalStorageConfiguration], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "project_id": project_id, + }, + external_storage_list_params.ExternalStorageListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ExternalStorageConfiguration, + ) + + def delete( + self, + external_storage_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ExternalStorageDeleted: + """Disconnect a customer-managed external storage configuration. + + Removing the + project's last configuration restores organization-default retention if + customer-managed retention was active. Repeating a deletion also completes any + interrupted retention update. Cloud storage is unchanged. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not external_storage_id: + raise ValueError( + f"Expected a non-empty value for `external_storage_id` but received {external_storage_id!r}" + ) + return self._delete( + path_template( + "/organization/external_storage/{external_storage_id}", external_storage_id=external_storage_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ExternalStorageDeleted, + ) + + def validate( + self, + external_storage_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ExternalStorageConfiguration: + """ + Validate one customer-managed external storage configuration. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not external_storage_id: + raise ValueError( + f"Expected a non-empty value for `external_storage_id` but received {external_storage_id!r}" + ) + return self._post( + path_template( + "/organization/external_storage/{external_storage_id}/validate", external_storage_id=external_storage_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ExternalStorageConfiguration, + ) + + +class AsyncExternalStorage(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncExternalStorageWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncExternalStorageWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExternalStorageWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncExternalStorageWithStreamingResponse(self) + + async def create( + self, + *, + project_id: str, + provider: external_storage_create_params.Provider, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ExternalStorageConfiguration: + """ + Register one customer-managed external storage configuration. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/external_storage", + body=await async_maybe_transform( + { + "project_id": project_id, + "provider": provider, + }, + external_storage_create_params.ExternalStorageCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ExternalStorageConfiguration, + ) + + async def retrieve( + self, + external_storage_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ExternalStorageConfiguration: + """ + Get one customer-managed external storage configuration. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not external_storage_id: + raise ValueError( + f"Expected a non-empty value for `external_storage_id` but received {external_storage_id!r}" + ) + return await self._get( + path_template( + "/organization/external_storage/{external_storage_id}", external_storage_id=external_storage_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ExternalStorageConfiguration, + ) + + def list( + self, + *, + after: Optional[str] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + project_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ExternalStorageConfiguration, AsyncCursorPage[ExternalStorageConfiguration]]: + """ + List the organization's customer-managed external storage configurations. + + Args: + after: Return external storage configurations after this ID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/external_storage", + page=AsyncCursorPage[ExternalStorageConfiguration], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "project_id": project_id, + }, + external_storage_list_params.ExternalStorageListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ExternalStorageConfiguration, + ) + + async def delete( + self, + external_storage_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ExternalStorageDeleted: + """Disconnect a customer-managed external storage configuration. + + Removing the + project's last configuration restores organization-default retention if + customer-managed retention was active. Repeating a deletion also completes any + interrupted retention update. Cloud storage is unchanged. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not external_storage_id: + raise ValueError( + f"Expected a non-empty value for `external_storage_id` but received {external_storage_id!r}" + ) + return await self._delete( + path_template( + "/organization/external_storage/{external_storage_id}", external_storage_id=external_storage_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ExternalStorageDeleted, + ) + + async def validate( + self, + external_storage_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ExternalStorageConfiguration: + """ + Validate one customer-managed external storage configuration. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not external_storage_id: + raise ValueError( + f"Expected a non-empty value for `external_storage_id` but received {external_storage_id!r}" + ) + return await self._post( + path_template( + "/organization/external_storage/{external_storage_id}/validate", external_storage_id=external_storage_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ExternalStorageConfiguration, + ) + + +class ExternalStorageWithRawResponse: + def __init__(self, external_storage: ExternalStorage) -> None: + self._external_storage = external_storage + + self.create = _legacy_response.to_raw_response_wrapper( + external_storage.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + external_storage.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + external_storage.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + external_storage.delete, + ) + self.validate = _legacy_response.to_raw_response_wrapper( + external_storage.validate, + ) + + +class AsyncExternalStorageWithRawResponse: + def __init__(self, external_storage: AsyncExternalStorage) -> None: + self._external_storage = external_storage + + self.create = _legacy_response.async_to_raw_response_wrapper( + external_storage.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + external_storage.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + external_storage.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + external_storage.delete, + ) + self.validate = _legacy_response.async_to_raw_response_wrapper( + external_storage.validate, + ) + + +class ExternalStorageWithStreamingResponse: + def __init__(self, external_storage: ExternalStorage) -> None: + self._external_storage = external_storage + + self.create = to_streamed_response_wrapper( + external_storage.create, + ) + self.retrieve = to_streamed_response_wrapper( + external_storage.retrieve, + ) + self.list = to_streamed_response_wrapper( + external_storage.list, + ) + self.delete = to_streamed_response_wrapper( + external_storage.delete, + ) + self.validate = to_streamed_response_wrapper( + external_storage.validate, + ) + + +class AsyncExternalStorageWithStreamingResponse: + def __init__(self, external_storage: AsyncExternalStorage) -> None: + self._external_storage = external_storage + + self.create = async_to_streamed_response_wrapper( + external_storage.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + external_storage.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + external_storage.list, + ) + self.delete = async_to_streamed_response_wrapper( + external_storage.delete, + ) + self.validate = async_to_streamed_response_wrapper( + external_storage.validate, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__init__.py new file mode 100644 index 0000000..f8a531b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__init__.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .roles import ( + Roles as Roles, + AsyncRoles as AsyncRoles, + RolesWithRawResponse as RolesWithRawResponse, + AsyncRolesWithRawResponse as AsyncRolesWithRawResponse, + RolesWithStreamingResponse as RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse as AsyncRolesWithStreamingResponse, + ) + from .users import ( + Users as Users, + AsyncUsers as AsyncUsers, + UsersWithRawResponse as UsersWithRawResponse, + AsyncUsersWithRawResponse as AsyncUsersWithRawResponse, + UsersWithStreamingResponse as UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse as AsyncUsersWithStreamingResponse, + ) + from .groups import ( + Groups as Groups, + AsyncGroups as AsyncGroups, + GroupsWithRawResponse as GroupsWithRawResponse, + AsyncGroupsWithRawResponse as AsyncGroupsWithRawResponse, + GroupsWithStreamingResponse as GroupsWithStreamingResponse, + AsyncGroupsWithStreamingResponse as AsyncGroupsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Users": (".users", "Users"), + "AsyncUsers": (".users", "AsyncUsers"), + "UsersWithRawResponse": (".users", "UsersWithRawResponse"), + "AsyncUsersWithRawResponse": (".users", "AsyncUsersWithRawResponse"), + "UsersWithStreamingResponse": (".users", "UsersWithStreamingResponse"), + "AsyncUsersWithStreamingResponse": (".users", "AsyncUsersWithStreamingResponse"), + "Roles": (".roles", "Roles"), + "AsyncRoles": (".roles", "AsyncRoles"), + "RolesWithRawResponse": (".roles", "RolesWithRawResponse"), + "AsyncRolesWithRawResponse": (".roles", "AsyncRolesWithRawResponse"), + "RolesWithStreamingResponse": (".roles", "RolesWithStreamingResponse"), + "AsyncRolesWithStreamingResponse": (".roles", "AsyncRolesWithStreamingResponse"), + "Groups": (".groups", "Groups"), + "AsyncGroups": (".groups", "AsyncGroups"), + "GroupsWithRawResponse": (".groups", "GroupsWithRawResponse"), + "AsyncGroupsWithRawResponse": (".groups", "AsyncGroupsWithRawResponse"), + "GroupsWithStreamingResponse": (".groups", "GroupsWithStreamingResponse"), + "AsyncGroupsWithStreamingResponse": (".groups", "AsyncGroupsWithStreamingResponse"), + } + _SUBMODULES = { + "groups", + "roles", + "users", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Users", + "AsyncUsers", + "UsersWithRawResponse", + "AsyncUsersWithRawResponse", + "UsersWithStreamingResponse", + "AsyncUsersWithStreamingResponse", + "Roles", + "AsyncRoles", + "RolesWithRawResponse", + "AsyncRolesWithRawResponse", + "RolesWithStreamingResponse", + "AsyncRolesWithStreamingResponse", + "Groups", + "AsyncGroups", + "GroupsWithRawResponse", + "AsyncGroupsWithRawResponse", + "GroupsWithStreamingResponse", + "AsyncGroupsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f6ff057 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__pycache__/groups.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__pycache__/groups.cpython-312.pyc new file mode 100644 index 0000000..1a357f9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__pycache__/groups.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__pycache__/roles.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__pycache__/roles.cpython-312.pyc new file mode 100644 index 0000000..224ca4e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__pycache__/roles.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__pycache__/users.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__pycache__/users.cpython-312.pyc new file mode 100644 index 0000000..5b1aa0a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/__pycache__/users.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/groups.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/groups.py new file mode 100644 index 0000000..24e44f4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/groups.py @@ -0,0 +1,630 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from .users import ( + Users, + AsyncUsers, + UsersWithRawResponse, + AsyncUsersWithRawResponse, + UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse, +) +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncNextCursorPage, AsyncNextCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization import group_list_params, group_create_params, group_update_params +from .....types.admin.organization.group import Group +from .....types.admin.organization.group_delete_response import GroupDeleteResponse +from .....types.admin.organization.group_update_response import GroupUpdateResponse + +__all__ = ["Groups", "AsyncGroups"] + + +class Groups(SyncAPIResource): + @cached_property + def users(self) -> Users: + return Users(self._client) + + @cached_property + def roles(self) -> Roles: + return Roles(self._client) + + @cached_property + def with_raw_response(self) -> GroupsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return GroupsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GroupsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return GroupsWithStreamingResponse(self) + + def create( + self, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Group: + """ + Creates a new group in the organization. + + Args: + name: Human readable name for the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/groups", + body=maybe_transform({"name": name}, group_create_params.GroupCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Group, + ) + + def retrieve( + self, + group_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Group: + """ + Retrieves a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get( + path_template("/organization/groups/{group_id}", group_id=group_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Group, + ) + + def update( + self, + group_id: str, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> GroupUpdateResponse: + """ + Updates a group's information. + + Args: + name: New display name for the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._post( + path_template("/organization/groups/{group_id}", group_id=group_id), + body=maybe_transform({"name": name}, group_update_params.GroupUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=GroupUpdateResponse, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncNextCursorPage[Group]: + """ + Lists all groups in the organization. + + Args: + after: A cursor for use in pagination. `after` is a group ID that defines your place in + the list. For instance, if you make a list request and receive 100 objects, + ending with group_abc, your subsequent call can include `after=group_abc` in + order to fetch the next page of the list. + + limit: A limit on the number of groups to be returned. Limit can range between 0 and + 1000, and the default is 100. + + order: Specifies the sort order of the returned groups. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/groups", + page=SyncNextCursorPage[Group], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + group_list_params.GroupListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Group, + ) + + def delete( + self, + group_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> GroupDeleteResponse: + """ + Deletes a group from the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._delete( + path_template("/organization/groups/{group_id}", group_id=group_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=GroupDeleteResponse, + ) + + +class AsyncGroups(AsyncAPIResource): + @cached_property + def users(self) -> AsyncUsers: + return AsyncUsers(self._client) + + @cached_property + def roles(self) -> AsyncRoles: + return AsyncRoles(self._client) + + @cached_property + def with_raw_response(self) -> AsyncGroupsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncGroupsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGroupsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncGroupsWithStreamingResponse(self) + + async def create( + self, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Group: + """ + Creates a new group in the organization. + + Args: + name: Human readable name for the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/groups", + body=await async_maybe_transform({"name": name}, group_create_params.GroupCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Group, + ) + + async def retrieve( + self, + group_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Group: + """ + Retrieves a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._get( + path_template("/organization/groups/{group_id}", group_id=group_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Group, + ) + + async def update( + self, + group_id: str, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> GroupUpdateResponse: + """ + Updates a group's information. + + Args: + name: New display name for the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._post( + path_template("/organization/groups/{group_id}", group_id=group_id), + body=await async_maybe_transform({"name": name}, group_update_params.GroupUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=GroupUpdateResponse, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Group, AsyncNextCursorPage[Group]]: + """ + Lists all groups in the organization. + + Args: + after: A cursor for use in pagination. `after` is a group ID that defines your place in + the list. For instance, if you make a list request and receive 100 objects, + ending with group_abc, your subsequent call can include `after=group_abc` in + order to fetch the next page of the list. + + limit: A limit on the number of groups to be returned. Limit can range between 0 and + 1000, and the default is 100. + + order: Specifies the sort order of the returned groups. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/groups", + page=AsyncNextCursorPage[Group], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + group_list_params.GroupListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Group, + ) + + async def delete( + self, + group_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> GroupDeleteResponse: + """ + Deletes a group from the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._delete( + path_template("/organization/groups/{group_id}", group_id=group_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=GroupDeleteResponse, + ) + + +class GroupsWithRawResponse: + def __init__(self, groups: Groups) -> None: + self._groups = groups + + self.create = _legacy_response.to_raw_response_wrapper( + groups.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + groups.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + groups.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + groups.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + groups.delete, + ) + + @cached_property + def users(self) -> UsersWithRawResponse: + return UsersWithRawResponse(self._groups.users) + + @cached_property + def roles(self) -> RolesWithRawResponse: + return RolesWithRawResponse(self._groups.roles) + + +class AsyncGroupsWithRawResponse: + def __init__(self, groups: AsyncGroups) -> None: + self._groups = groups + + self.create = _legacy_response.async_to_raw_response_wrapper( + groups.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + groups.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + groups.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + groups.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + groups.delete, + ) + + @cached_property + def users(self) -> AsyncUsersWithRawResponse: + return AsyncUsersWithRawResponse(self._groups.users) + + @cached_property + def roles(self) -> AsyncRolesWithRawResponse: + return AsyncRolesWithRawResponse(self._groups.roles) + + +class GroupsWithStreamingResponse: + def __init__(self, groups: Groups) -> None: + self._groups = groups + + self.create = to_streamed_response_wrapper( + groups.create, + ) + self.retrieve = to_streamed_response_wrapper( + groups.retrieve, + ) + self.update = to_streamed_response_wrapper( + groups.update, + ) + self.list = to_streamed_response_wrapper( + groups.list, + ) + self.delete = to_streamed_response_wrapper( + groups.delete, + ) + + @cached_property + def users(self) -> UsersWithStreamingResponse: + return UsersWithStreamingResponse(self._groups.users) + + @cached_property + def roles(self) -> RolesWithStreamingResponse: + return RolesWithStreamingResponse(self._groups.roles) + + +class AsyncGroupsWithStreamingResponse: + def __init__(self, groups: AsyncGroups) -> None: + self._groups = groups + + self.create = async_to_streamed_response_wrapper( + groups.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + groups.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + groups.update, + ) + self.list = async_to_streamed_response_wrapper( + groups.list, + ) + self.delete = async_to_streamed_response_wrapper( + groups.delete, + ) + + @cached_property + def users(self) -> AsyncUsersWithStreamingResponse: + return AsyncUsersWithStreamingResponse(self._groups.users) + + @cached_property + def roles(self) -> AsyncRolesWithStreamingResponse: + return AsyncRolesWithStreamingResponse(self._groups.roles) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/roles.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/roles.py new file mode 100644 index 0000000..b6f0e85 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/roles.py @@ -0,0 +1,491 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncNextCursorPage, AsyncNextCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.groups import role_list_params, role_create_params +from .....types.admin.organization.groups.role_list_response import RoleListResponse +from .....types.admin.organization.groups.role_create_response import RoleCreateResponse +from .....types.admin.organization.groups.role_delete_response import RoleDeleteResponse +from .....types.admin.organization.groups.role_retrieve_response import RoleRetrieveResponse + +__all__ = ["Roles", "AsyncRoles"] + + +class Roles(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RolesWithStreamingResponse(self) + + def create( + self, + group_id: str, + *, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns an organization role to a group within the organization. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._post( + path_template("/organization/groups/{group_id}/roles", group_id=group_id), + body=maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + def retrieve( + self, + role_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves an organization role assigned to a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template("/organization/groups/{group_id}/roles/{role_id}", group_id=group_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + + def list( + self, + group_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncNextCursorPage[RoleListResponse]: + """ + Lists the organization roles assigned to a group within the organization. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing organization roles. + + limit: A limit on the number of organization role assignments to return. + + order: Sort order for the returned organization roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get_api_list( + path_template("/organization/groups/{group_id}/roles", group_id=group_id), + page=SyncNextCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + def delete( + self, + role_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns an organization role from a group within the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._delete( + path_template("/organization/groups/{group_id}/roles/{role_id}", group_id=group_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class AsyncRoles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRolesWithStreamingResponse(self) + + async def create( + self, + group_id: str, + *, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns an organization role to a group within the organization. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._post( + path_template("/organization/groups/{group_id}/roles", group_id=group_id), + body=await async_maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + async def retrieve( + self, + role_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves an organization role assigned to a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template("/organization/groups/{group_id}/roles/{role_id}", group_id=group_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + + def list( + self, + group_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[RoleListResponse, AsyncNextCursorPage[RoleListResponse]]: + """ + Lists the organization roles assigned to a group within the organization. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing organization roles. + + limit: A limit on the number of organization role assignments to return. + + order: Sort order for the returned organization roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get_api_list( + path_template("/organization/groups/{group_id}/roles", group_id=group_id), + page=AsyncNextCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + async def delete( + self, + role_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns an organization role from a group within the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._delete( + path_template("/organization/groups/{group_id}/roles/{role_id}", group_id=group_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class RolesWithRawResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = _legacy_response.to_raw_response_wrapper( + roles.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithRawResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = _legacy_response.async_to_raw_response_wrapper( + roles.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + roles.delete, + ) + + +class RolesWithStreamingResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = to_streamed_response_wrapper( + roles.create, + ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) + self.list = to_streamed_response_wrapper( + roles.list, + ) + self.delete = to_streamed_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithStreamingResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = async_to_streamed_response_wrapper( + roles.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + roles.list, + ) + self.delete = async_to_streamed_response_wrapper( + roles.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/users.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/users.py new file mode 100644 index 0000000..9dd8feb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/groups/users.py @@ -0,0 +1,493 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncNextCursorPage, AsyncNextCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.groups import user_list_params, user_create_params +from .....types.admin.organization.groups.user_create_response import UserCreateResponse +from .....types.admin.organization.groups.user_delete_response import UserDeleteResponse +from .....types.admin.organization.groups.user_retrieve_response import UserRetrieveResponse +from .....types.admin.organization.groups.organization_group_user import OrganizationGroupUser + +__all__ = ["Users", "AsyncUsers"] + + +class Users(SyncAPIResource): + @cached_property + def with_raw_response(self) -> UsersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return UsersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UsersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return UsersWithStreamingResponse(self) + + def create( + self, + group_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UserCreateResponse: + """ + Adds a user to a group. + + Args: + user_id: Identifier of the user to add to the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._post( + path_template("/organization/groups/{group_id}/users", group_id=group_id), + body=maybe_transform({"user_id": user_id}, user_create_params.UserCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserCreateResponse, + ) + + def retrieve( + self, + user_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UserRetrieveResponse: + """ + Retrieves a user in a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get( + path_template("/organization/groups/{group_id}/users/{user_id}", group_id=group_id, user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserRetrieveResponse, + ) + + def list( + self, + group_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncNextCursorPage[OrganizationGroupUser]: + """ + Lists the users assigned to a group. + + Args: + after: A cursor for use in pagination. Provide the ID of the last user from the + previous list response to retrieve the next page. + + limit: A limit on the number of users to be returned. Limit can range between 0 and + 1000, and the default is 100. + + order: Specifies the sort order of users in the list. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get_api_list( + path_template("/organization/groups/{group_id}/users", group_id=group_id), + page=SyncNextCursorPage[OrganizationGroupUser], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + user_list_params.UserListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationGroupUser, + ) + + def delete( + self, + user_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UserDeleteResponse: + """ + Removes a user from a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._delete( + path_template("/organization/groups/{group_id}/users/{user_id}", group_id=group_id, user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserDeleteResponse, + ) + + +class AsyncUsers(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncUsersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncUsersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUsersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncUsersWithStreamingResponse(self) + + async def create( + self, + group_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UserCreateResponse: + """ + Adds a user to a group. + + Args: + user_id: Identifier of the user to add to the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._post( + path_template("/organization/groups/{group_id}/users", group_id=group_id), + body=await async_maybe_transform({"user_id": user_id}, user_create_params.UserCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserCreateResponse, + ) + + async def retrieve( + self, + user_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UserRetrieveResponse: + """ + Retrieves a user in a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._get( + path_template("/organization/groups/{group_id}/users/{user_id}", group_id=group_id, user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserRetrieveResponse, + ) + + def list( + self, + group_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[OrganizationGroupUser, AsyncNextCursorPage[OrganizationGroupUser]]: + """ + Lists the users assigned to a group. + + Args: + after: A cursor for use in pagination. Provide the ID of the last user from the + previous list response to retrieve the next page. + + limit: A limit on the number of users to be returned. Limit can range between 0 and + 1000, and the default is 100. + + order: Specifies the sort order of users in the list. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get_api_list( + path_template("/organization/groups/{group_id}/users", group_id=group_id), + page=AsyncNextCursorPage[OrganizationGroupUser], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + user_list_params.UserListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationGroupUser, + ) + + async def delete( + self, + user_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UserDeleteResponse: + """ + Removes a user from a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._delete( + path_template("/organization/groups/{group_id}/users/{user_id}", group_id=group_id, user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserDeleteResponse, + ) + + +class UsersWithRawResponse: + def __init__(self, users: Users) -> None: + self._users = users + + self.create = _legacy_response.to_raw_response_wrapper( + users.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + users.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + users.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + users.delete, + ) + + +class AsyncUsersWithRawResponse: + def __init__(self, users: AsyncUsers) -> None: + self._users = users + + self.create = _legacy_response.async_to_raw_response_wrapper( + users.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + users.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + users.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + users.delete, + ) + + +class UsersWithStreamingResponse: + def __init__(self, users: Users) -> None: + self._users = users + + self.create = to_streamed_response_wrapper( + users.create, + ) + self.retrieve = to_streamed_response_wrapper( + users.retrieve, + ) + self.list = to_streamed_response_wrapper( + users.list, + ) + self.delete = to_streamed_response_wrapper( + users.delete, + ) + + +class AsyncUsersWithStreamingResponse: + def __init__(self, users: AsyncUsers) -> None: + self._users = users + + self.create = async_to_streamed_response_wrapper( + users.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + users.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + users.list, + ) + self.delete = async_to_streamed_response_wrapper( + users.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/invites.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/invites.py new file mode 100644 index 0000000..cfe63a9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/invites.py @@ -0,0 +1,502 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import invite_list_params, invite_create_params +from ....types.admin.organization.invite import Invite +from ....types.admin.organization.invite_delete_response import InviteDeleteResponse + +__all__ = ["Invites", "AsyncInvites"] + + +class Invites(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InvitesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return InvitesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InvitesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return InvitesWithStreamingResponse(self) + + def create( + self, + *, + email: str, + role: Literal["reader", "owner"], + projects: Iterable[invite_create_params.Project] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Invite: + """Create an invite for a user to the organization. + + The invite must be accepted by + the user before they have access to the organization. + + Args: + email: Send an email to this address + + role: `owner` or `reader` + + projects: An array of projects to which membership is granted at the same time the org + invite is accepted. If omitted, the user will be invited to the default project + for compatibility with legacy behavior. If empty list is passed, the user will + not be invited to any projects, including the default one. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/invites", + body=maybe_transform( + { + "email": email, + "role": role, + "projects": projects, + }, + invite_create_params.InviteCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Invite, + ) + + def retrieve( + self, + invite_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Invite: + """ + Retrieves an invite. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not invite_id: + raise ValueError(f"Expected a non-empty value for `invite_id` but received {invite_id!r}") + return self._get( + path_template("/organization/invites/{invite_id}", invite_id=invite_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Invite, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[Invite]: + """ + Returns a list of invites in the organization. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/invites", + page=SyncConversationCursorPage[Invite], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + invite_list_params.InviteListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Invite, + ) + + def delete( + self, + invite_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> InviteDeleteResponse: + """Delete an invite. + + If the invite has already been accepted, it cannot be deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not invite_id: + raise ValueError(f"Expected a non-empty value for `invite_id` but received {invite_id!r}") + return self._delete( + path_template("/organization/invites/{invite_id}", invite_id=invite_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=InviteDeleteResponse, + ) + + +class AsyncInvites(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInvitesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncInvitesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInvitesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncInvitesWithStreamingResponse(self) + + async def create( + self, + *, + email: str, + role: Literal["reader", "owner"], + projects: Iterable[invite_create_params.Project] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Invite: + """Create an invite for a user to the organization. + + The invite must be accepted by + the user before they have access to the organization. + + Args: + email: Send an email to this address + + role: `owner` or `reader` + + projects: An array of projects to which membership is granted at the same time the org + invite is accepted. If omitted, the user will be invited to the default project + for compatibility with legacy behavior. If empty list is passed, the user will + not be invited to any projects, including the default one. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/invites", + body=await async_maybe_transform( + { + "email": email, + "role": role, + "projects": projects, + }, + invite_create_params.InviteCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Invite, + ) + + async def retrieve( + self, + invite_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Invite: + """ + Retrieves an invite. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not invite_id: + raise ValueError(f"Expected a non-empty value for `invite_id` but received {invite_id!r}") + return await self._get( + path_template("/organization/invites/{invite_id}", invite_id=invite_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Invite, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Invite, AsyncConversationCursorPage[Invite]]: + """ + Returns a list of invites in the organization. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/invites", + page=AsyncConversationCursorPage[Invite], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + invite_list_params.InviteListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Invite, + ) + + async def delete( + self, + invite_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> InviteDeleteResponse: + """Delete an invite. + + If the invite has already been accepted, it cannot be deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not invite_id: + raise ValueError(f"Expected a non-empty value for `invite_id` but received {invite_id!r}") + return await self._delete( + path_template("/organization/invites/{invite_id}", invite_id=invite_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=InviteDeleteResponse, + ) + + +class InvitesWithRawResponse: + def __init__(self, invites: Invites) -> None: + self._invites = invites + + self.create = _legacy_response.to_raw_response_wrapper( + invites.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + invites.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + invites.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + invites.delete, + ) + + +class AsyncInvitesWithRawResponse: + def __init__(self, invites: AsyncInvites) -> None: + self._invites = invites + + self.create = _legacy_response.async_to_raw_response_wrapper( + invites.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + invites.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + invites.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + invites.delete, + ) + + +class InvitesWithStreamingResponse: + def __init__(self, invites: Invites) -> None: + self._invites = invites + + self.create = to_streamed_response_wrapper( + invites.create, + ) + self.retrieve = to_streamed_response_wrapper( + invites.retrieve, + ) + self.list = to_streamed_response_wrapper( + invites.list, + ) + self.delete = to_streamed_response_wrapper( + invites.delete, + ) + + +class AsyncInvitesWithStreamingResponse: + def __init__(self, invites: AsyncInvites) -> None: + self._invites = invites + + self.create = async_to_streamed_response_wrapper( + invites.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + invites.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + invites.list, + ) + self.delete = async_to_streamed_response_wrapper( + invites.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/organization.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/organization.py new file mode 100644 index 0000000..f9d9594 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/organization.py @@ -0,0 +1,492 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from .usage import ( + Usage, + AsyncUsage, + UsageWithRawResponse, + AsyncUsageWithRawResponse, + UsageWithStreamingResponse, + AsyncUsageWithStreamingResponse, +) +from .invites import ( + Invites, + AsyncInvites, + InvitesWithRawResponse, + AsyncInvitesWithRawResponse, + InvitesWithStreamingResponse, + AsyncInvitesWithStreamingResponse, +) +from ...._compat import cached_property +from .audit_logs import ( + AuditLogs, + AsyncAuditLogs, + AuditLogsWithRawResponse, + AsyncAuditLogsWithRawResponse, + AuditLogsWithStreamingResponse, + AsyncAuditLogsWithStreamingResponse, +) +from .spend_limit import ( + SpendLimit, + AsyncSpendLimit, + SpendLimitWithRawResponse, + AsyncSpendLimitWithRawResponse, + SpendLimitWithStreamingResponse, + AsyncSpendLimitWithStreamingResponse, +) +from .users.users import ( + Users, + AsyncUsers, + UsersWithRawResponse, + AsyncUsersWithRawResponse, + UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse, +) +from ...._resource import SyncAPIResource, AsyncAPIResource +from .certificates import ( + Certificates, + AsyncCertificates, + CertificatesWithRawResponse, + AsyncCertificatesWithRawResponse, + CertificatesWithStreamingResponse, + AsyncCertificatesWithStreamingResponse, +) +from .spend_alerts import ( + SpendAlerts, + AsyncSpendAlerts, + SpendAlertsWithRawResponse, + AsyncSpendAlertsWithRawResponse, + SpendAlertsWithStreamingResponse, + AsyncSpendAlertsWithStreamingResponse, +) +from .groups.groups import ( + Groups, + AsyncGroups, + GroupsWithRawResponse, + AsyncGroupsWithRawResponse, + GroupsWithStreamingResponse, + AsyncGroupsWithStreamingResponse, +) +from .admin_api_keys import ( + AdminAPIKeys, + AsyncAdminAPIKeys, + AdminAPIKeysWithRawResponse, + AsyncAdminAPIKeysWithRawResponse, + AdminAPIKeysWithStreamingResponse, + AsyncAdminAPIKeysWithStreamingResponse, +) +from .data_retention import ( + DataRetention, + AsyncDataRetention, + DataRetentionWithRawResponse, + AsyncDataRetentionWithRawResponse, + DataRetentionWithStreamingResponse, + AsyncDataRetentionWithStreamingResponse, +) +from .external_storage import ( + ExternalStorage, + AsyncExternalStorage, + ExternalStorageWithRawResponse, + AsyncExternalStorageWithRawResponse, + ExternalStorageWithStreamingResponse, + AsyncExternalStorageWithStreamingResponse, +) +from .projects.projects import ( + Projects, + AsyncProjects, + ProjectsWithRawResponse, + AsyncProjectsWithRawResponse, + ProjectsWithStreamingResponse, + AsyncProjectsWithStreamingResponse, +) + +__all__ = ["Organization", "AsyncOrganization"] + + +class Organization(SyncAPIResource): + @cached_property + def audit_logs(self) -> AuditLogs: + """List user actions and configuration changes within this organization.""" + return AuditLogs(self._client) + + @cached_property + def admin_api_keys(self) -> AdminAPIKeys: + return AdminAPIKeys(self._client) + + @cached_property + def usage(self) -> Usage: + return Usage(self._client) + + @cached_property + def invites(self) -> Invites: + return Invites(self._client) + + @cached_property + def users(self) -> Users: + return Users(self._client) + + @cached_property + def groups(self) -> Groups: + return Groups(self._client) + + @cached_property + def roles(self) -> Roles: + return Roles(self._client) + + @cached_property + def data_retention(self) -> DataRetention: + return DataRetention(self._client) + + @cached_property + def external_storage(self) -> ExternalStorage: + return ExternalStorage(self._client) + + @cached_property + def spend_limit(self) -> SpendLimit: + return SpendLimit(self._client) + + @cached_property + def spend_alerts(self) -> SpendAlerts: + return SpendAlerts(self._client) + + @cached_property + def certificates(self) -> Certificates: + return Certificates(self._client) + + @cached_property + def projects(self) -> Projects: + return Projects(self._client) + + @cached_property + def with_raw_response(self) -> OrganizationWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return OrganizationWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> OrganizationWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return OrganizationWithStreamingResponse(self) + + +class AsyncOrganization(AsyncAPIResource): + @cached_property + def audit_logs(self) -> AsyncAuditLogs: + """List user actions and configuration changes within this organization.""" + return AsyncAuditLogs(self._client) + + @cached_property + def admin_api_keys(self) -> AsyncAdminAPIKeys: + return AsyncAdminAPIKeys(self._client) + + @cached_property + def usage(self) -> AsyncUsage: + return AsyncUsage(self._client) + + @cached_property + def invites(self) -> AsyncInvites: + return AsyncInvites(self._client) + + @cached_property + def users(self) -> AsyncUsers: + return AsyncUsers(self._client) + + @cached_property + def groups(self) -> AsyncGroups: + return AsyncGroups(self._client) + + @cached_property + def roles(self) -> AsyncRoles: + return AsyncRoles(self._client) + + @cached_property + def data_retention(self) -> AsyncDataRetention: + return AsyncDataRetention(self._client) + + @cached_property + def external_storage(self) -> AsyncExternalStorage: + return AsyncExternalStorage(self._client) + + @cached_property + def spend_limit(self) -> AsyncSpendLimit: + return AsyncSpendLimit(self._client) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlerts: + return AsyncSpendAlerts(self._client) + + @cached_property + def certificates(self) -> AsyncCertificates: + return AsyncCertificates(self._client) + + @cached_property + def projects(self) -> AsyncProjects: + return AsyncProjects(self._client) + + @cached_property + def with_raw_response(self) -> AsyncOrganizationWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncOrganizationWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncOrganizationWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncOrganizationWithStreamingResponse(self) + + +class OrganizationWithRawResponse: + def __init__(self, organization: Organization) -> None: + self._organization = organization + + @cached_property + def audit_logs(self) -> AuditLogsWithRawResponse: + """List user actions and configuration changes within this organization.""" + return AuditLogsWithRawResponse(self._organization.audit_logs) + + @cached_property + def admin_api_keys(self) -> AdminAPIKeysWithRawResponse: + return AdminAPIKeysWithRawResponse(self._organization.admin_api_keys) + + @cached_property + def usage(self) -> UsageWithRawResponse: + return UsageWithRawResponse(self._organization.usage) + + @cached_property + def invites(self) -> InvitesWithRawResponse: + return InvitesWithRawResponse(self._organization.invites) + + @cached_property + def users(self) -> UsersWithRawResponse: + return UsersWithRawResponse(self._organization.users) + + @cached_property + def groups(self) -> GroupsWithRawResponse: + return GroupsWithRawResponse(self._organization.groups) + + @cached_property + def roles(self) -> RolesWithRawResponse: + return RolesWithRawResponse(self._organization.roles) + + @cached_property + def data_retention(self) -> DataRetentionWithRawResponse: + return DataRetentionWithRawResponse(self._organization.data_retention) + + @cached_property + def external_storage(self) -> ExternalStorageWithRawResponse: + return ExternalStorageWithRawResponse(self._organization.external_storage) + + @cached_property + def spend_limit(self) -> SpendLimitWithRawResponse: + return SpendLimitWithRawResponse(self._organization.spend_limit) + + @cached_property + def spend_alerts(self) -> SpendAlertsWithRawResponse: + return SpendAlertsWithRawResponse(self._organization.spend_alerts) + + @cached_property + def certificates(self) -> CertificatesWithRawResponse: + return CertificatesWithRawResponse(self._organization.certificates) + + @cached_property + def projects(self) -> ProjectsWithRawResponse: + return ProjectsWithRawResponse(self._organization.projects) + + +class AsyncOrganizationWithRawResponse: + def __init__(self, organization: AsyncOrganization) -> None: + self._organization = organization + + @cached_property + def audit_logs(self) -> AsyncAuditLogsWithRawResponse: + """List user actions and configuration changes within this organization.""" + return AsyncAuditLogsWithRawResponse(self._organization.audit_logs) + + @cached_property + def admin_api_keys(self) -> AsyncAdminAPIKeysWithRawResponse: + return AsyncAdminAPIKeysWithRawResponse(self._organization.admin_api_keys) + + @cached_property + def usage(self) -> AsyncUsageWithRawResponse: + return AsyncUsageWithRawResponse(self._organization.usage) + + @cached_property + def invites(self) -> AsyncInvitesWithRawResponse: + return AsyncInvitesWithRawResponse(self._organization.invites) + + @cached_property + def users(self) -> AsyncUsersWithRawResponse: + return AsyncUsersWithRawResponse(self._organization.users) + + @cached_property + def groups(self) -> AsyncGroupsWithRawResponse: + return AsyncGroupsWithRawResponse(self._organization.groups) + + @cached_property + def roles(self) -> AsyncRolesWithRawResponse: + return AsyncRolesWithRawResponse(self._organization.roles) + + @cached_property + def data_retention(self) -> AsyncDataRetentionWithRawResponse: + return AsyncDataRetentionWithRawResponse(self._organization.data_retention) + + @cached_property + def external_storage(self) -> AsyncExternalStorageWithRawResponse: + return AsyncExternalStorageWithRawResponse(self._organization.external_storage) + + @cached_property + def spend_limit(self) -> AsyncSpendLimitWithRawResponse: + return AsyncSpendLimitWithRawResponse(self._organization.spend_limit) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlertsWithRawResponse: + return AsyncSpendAlertsWithRawResponse(self._organization.spend_alerts) + + @cached_property + def certificates(self) -> AsyncCertificatesWithRawResponse: + return AsyncCertificatesWithRawResponse(self._organization.certificates) + + @cached_property + def projects(self) -> AsyncProjectsWithRawResponse: + return AsyncProjectsWithRawResponse(self._organization.projects) + + +class OrganizationWithStreamingResponse: + def __init__(self, organization: Organization) -> None: + self._organization = organization + + @cached_property + def audit_logs(self) -> AuditLogsWithStreamingResponse: + """List user actions and configuration changes within this organization.""" + return AuditLogsWithStreamingResponse(self._organization.audit_logs) + + @cached_property + def admin_api_keys(self) -> AdminAPIKeysWithStreamingResponse: + return AdminAPIKeysWithStreamingResponse(self._organization.admin_api_keys) + + @cached_property + def usage(self) -> UsageWithStreamingResponse: + return UsageWithStreamingResponse(self._organization.usage) + + @cached_property + def invites(self) -> InvitesWithStreamingResponse: + return InvitesWithStreamingResponse(self._organization.invites) + + @cached_property + def users(self) -> UsersWithStreamingResponse: + return UsersWithStreamingResponse(self._organization.users) + + @cached_property + def groups(self) -> GroupsWithStreamingResponse: + return GroupsWithStreamingResponse(self._organization.groups) + + @cached_property + def roles(self) -> RolesWithStreamingResponse: + return RolesWithStreamingResponse(self._organization.roles) + + @cached_property + def data_retention(self) -> DataRetentionWithStreamingResponse: + return DataRetentionWithStreamingResponse(self._organization.data_retention) + + @cached_property + def external_storage(self) -> ExternalStorageWithStreamingResponse: + return ExternalStorageWithStreamingResponse(self._organization.external_storage) + + @cached_property + def spend_limit(self) -> SpendLimitWithStreamingResponse: + return SpendLimitWithStreamingResponse(self._organization.spend_limit) + + @cached_property + def spend_alerts(self) -> SpendAlertsWithStreamingResponse: + return SpendAlertsWithStreamingResponse(self._organization.spend_alerts) + + @cached_property + def certificates(self) -> CertificatesWithStreamingResponse: + return CertificatesWithStreamingResponse(self._organization.certificates) + + @cached_property + def projects(self) -> ProjectsWithStreamingResponse: + return ProjectsWithStreamingResponse(self._organization.projects) + + +class AsyncOrganizationWithStreamingResponse: + def __init__(self, organization: AsyncOrganization) -> None: + self._organization = organization + + @cached_property + def audit_logs(self) -> AsyncAuditLogsWithStreamingResponse: + """List user actions and configuration changes within this organization.""" + return AsyncAuditLogsWithStreamingResponse(self._organization.audit_logs) + + @cached_property + def admin_api_keys(self) -> AsyncAdminAPIKeysWithStreamingResponse: + return AsyncAdminAPIKeysWithStreamingResponse(self._organization.admin_api_keys) + + @cached_property + def usage(self) -> AsyncUsageWithStreamingResponse: + return AsyncUsageWithStreamingResponse(self._organization.usage) + + @cached_property + def invites(self) -> AsyncInvitesWithStreamingResponse: + return AsyncInvitesWithStreamingResponse(self._organization.invites) + + @cached_property + def users(self) -> AsyncUsersWithStreamingResponse: + return AsyncUsersWithStreamingResponse(self._organization.users) + + @cached_property + def groups(self) -> AsyncGroupsWithStreamingResponse: + return AsyncGroupsWithStreamingResponse(self._organization.groups) + + @cached_property + def roles(self) -> AsyncRolesWithStreamingResponse: + return AsyncRolesWithStreamingResponse(self._organization.roles) + + @cached_property + def data_retention(self) -> AsyncDataRetentionWithStreamingResponse: + return AsyncDataRetentionWithStreamingResponse(self._organization.data_retention) + + @cached_property + def external_storage(self) -> AsyncExternalStorageWithStreamingResponse: + return AsyncExternalStorageWithStreamingResponse(self._organization.external_storage) + + @cached_property + def spend_limit(self) -> AsyncSpendLimitWithStreamingResponse: + return AsyncSpendLimitWithStreamingResponse(self._organization.spend_limit) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlertsWithStreamingResponse: + return AsyncSpendAlertsWithStreamingResponse(self._organization.spend_alerts) + + @cached_property + def certificates(self) -> AsyncCertificatesWithStreamingResponse: + return AsyncCertificatesWithStreamingResponse(self._organization.certificates) + + @cached_property + def projects(self) -> AsyncProjectsWithStreamingResponse: + return AsyncProjectsWithStreamingResponse(self._organization.projects) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__init__.py new file mode 100644 index 0000000..9336ac9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__init__.py @@ -0,0 +1,316 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .roles import ( + Roles as Roles, + AsyncRoles as AsyncRoles, + RolesWithRawResponse as RolesWithRawResponse, + AsyncRolesWithRawResponse as AsyncRolesWithRawResponse, + RolesWithStreamingResponse as RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse as AsyncRolesWithStreamingResponse, + ) + from .users import ( + Users as Users, + AsyncUsers as AsyncUsers, + UsersWithRawResponse as UsersWithRawResponse, + AsyncUsersWithRawResponse as AsyncUsersWithRawResponse, + UsersWithStreamingResponse as UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse as AsyncUsersWithStreamingResponse, + ) + from .groups import ( + Groups as Groups, + AsyncGroups as AsyncGroups, + GroupsWithRawResponse as GroupsWithRawResponse, + AsyncGroupsWithRawResponse as AsyncGroupsWithRawResponse, + GroupsWithStreamingResponse as GroupsWithStreamingResponse, + AsyncGroupsWithStreamingResponse as AsyncGroupsWithStreamingResponse, + ) + from .api_keys import ( + APIKeys as APIKeys, + AsyncAPIKeys as AsyncAPIKeys, + APIKeysWithRawResponse as APIKeysWithRawResponse, + AsyncAPIKeysWithRawResponse as AsyncAPIKeysWithRawResponse, + APIKeysWithStreamingResponse as APIKeysWithStreamingResponse, + AsyncAPIKeysWithStreamingResponse as AsyncAPIKeysWithStreamingResponse, + ) + from .projects import ( + Projects as Projects, + AsyncProjects as AsyncProjects, + ProjectsWithRawResponse as ProjectsWithRawResponse, + AsyncProjectsWithRawResponse as AsyncProjectsWithRawResponse, + ProjectsWithStreamingResponse as ProjectsWithStreamingResponse, + AsyncProjectsWithStreamingResponse as AsyncProjectsWithStreamingResponse, + ) + from .rate_limits import ( + RateLimits as RateLimits, + AsyncRateLimits as AsyncRateLimits, + RateLimitsWithRawResponse as RateLimitsWithRawResponse, + AsyncRateLimitsWithRawResponse as AsyncRateLimitsWithRawResponse, + RateLimitsWithStreamingResponse as RateLimitsWithStreamingResponse, + AsyncRateLimitsWithStreamingResponse as AsyncRateLimitsWithStreamingResponse, + ) + from .spend_limit import ( + SpendLimit as SpendLimit, + AsyncSpendLimit as AsyncSpendLimit, + SpendLimitWithRawResponse as SpendLimitWithRawResponse, + AsyncSpendLimitWithRawResponse as AsyncSpendLimitWithRawResponse, + SpendLimitWithStreamingResponse as SpendLimitWithStreamingResponse, + AsyncSpendLimitWithStreamingResponse as AsyncSpendLimitWithStreamingResponse, + ) + from .certificates import ( + Certificates as Certificates, + AsyncCertificates as AsyncCertificates, + CertificatesWithRawResponse as CertificatesWithRawResponse, + AsyncCertificatesWithRawResponse as AsyncCertificatesWithRawResponse, + CertificatesWithStreamingResponse as CertificatesWithStreamingResponse, + AsyncCertificatesWithStreamingResponse as AsyncCertificatesWithStreamingResponse, + ) + from .spend_alerts import ( + SpendAlerts as SpendAlerts, + AsyncSpendAlerts as AsyncSpendAlerts, + SpendAlertsWithRawResponse as SpendAlertsWithRawResponse, + AsyncSpendAlertsWithRawResponse as AsyncSpendAlertsWithRawResponse, + SpendAlertsWithStreamingResponse as SpendAlertsWithStreamingResponse, + AsyncSpendAlertsWithStreamingResponse as AsyncSpendAlertsWithStreamingResponse, + ) + from .data_retention import ( + DataRetention as DataRetention, + AsyncDataRetention as AsyncDataRetention, + DataRetentionWithRawResponse as DataRetentionWithRawResponse, + AsyncDataRetentionWithRawResponse as AsyncDataRetentionWithRawResponse, + DataRetentionWithStreamingResponse as DataRetentionWithStreamingResponse, + AsyncDataRetentionWithStreamingResponse as AsyncDataRetentionWithStreamingResponse, + ) + from .service_accounts import ( + ServiceAccounts as ServiceAccounts, + AsyncServiceAccounts as AsyncServiceAccounts, + ServiceAccountsWithRawResponse as ServiceAccountsWithRawResponse, + AsyncServiceAccountsWithRawResponse as AsyncServiceAccountsWithRawResponse, + ServiceAccountsWithStreamingResponse as ServiceAccountsWithStreamingResponse, + AsyncServiceAccountsWithStreamingResponse as AsyncServiceAccountsWithStreamingResponse, + ) + from .model_permissions import ( + ModelPermissions as ModelPermissions, + AsyncModelPermissions as AsyncModelPermissions, + ModelPermissionsWithRawResponse as ModelPermissionsWithRawResponse, + AsyncModelPermissionsWithRawResponse as AsyncModelPermissionsWithRawResponse, + ModelPermissionsWithStreamingResponse as ModelPermissionsWithStreamingResponse, + AsyncModelPermissionsWithStreamingResponse as AsyncModelPermissionsWithStreamingResponse, + ) + from .hosted_tool_permissions import ( + HostedToolPermissions as HostedToolPermissions, + AsyncHostedToolPermissions as AsyncHostedToolPermissions, + HostedToolPermissionsWithRawResponse as HostedToolPermissionsWithRawResponse, + AsyncHostedToolPermissionsWithRawResponse as AsyncHostedToolPermissionsWithRawResponse, + HostedToolPermissionsWithStreamingResponse as HostedToolPermissionsWithStreamingResponse, + AsyncHostedToolPermissionsWithStreamingResponse as AsyncHostedToolPermissionsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Users": (".users", "Users"), + "AsyncUsers": (".users", "AsyncUsers"), + "UsersWithRawResponse": (".users", "UsersWithRawResponse"), + "AsyncUsersWithRawResponse": (".users", "AsyncUsersWithRawResponse"), + "UsersWithStreamingResponse": (".users", "UsersWithStreamingResponse"), + "AsyncUsersWithStreamingResponse": (".users", "AsyncUsersWithStreamingResponse"), + "ServiceAccounts": (".service_accounts", "ServiceAccounts"), + "AsyncServiceAccounts": (".service_accounts", "AsyncServiceAccounts"), + "ServiceAccountsWithRawResponse": (".service_accounts", "ServiceAccountsWithRawResponse"), + "AsyncServiceAccountsWithRawResponse": (".service_accounts", "AsyncServiceAccountsWithRawResponse"), + "ServiceAccountsWithStreamingResponse": (".service_accounts", "ServiceAccountsWithStreamingResponse"), + "AsyncServiceAccountsWithStreamingResponse": (".service_accounts", "AsyncServiceAccountsWithStreamingResponse"), + "APIKeys": (".api_keys", "APIKeys"), + "AsyncAPIKeys": (".api_keys", "AsyncAPIKeys"), + "APIKeysWithRawResponse": (".api_keys", "APIKeysWithRawResponse"), + "AsyncAPIKeysWithRawResponse": (".api_keys", "AsyncAPIKeysWithRawResponse"), + "APIKeysWithStreamingResponse": (".api_keys", "APIKeysWithStreamingResponse"), + "AsyncAPIKeysWithStreamingResponse": (".api_keys", "AsyncAPIKeysWithStreamingResponse"), + "RateLimits": (".rate_limits", "RateLimits"), + "AsyncRateLimits": (".rate_limits", "AsyncRateLimits"), + "RateLimitsWithRawResponse": (".rate_limits", "RateLimitsWithRawResponse"), + "AsyncRateLimitsWithRawResponse": (".rate_limits", "AsyncRateLimitsWithRawResponse"), + "RateLimitsWithStreamingResponse": (".rate_limits", "RateLimitsWithStreamingResponse"), + "AsyncRateLimitsWithStreamingResponse": (".rate_limits", "AsyncRateLimitsWithStreamingResponse"), + "ModelPermissions": (".model_permissions", "ModelPermissions"), + "AsyncModelPermissions": (".model_permissions", "AsyncModelPermissions"), + "ModelPermissionsWithRawResponse": (".model_permissions", "ModelPermissionsWithRawResponse"), + "AsyncModelPermissionsWithRawResponse": (".model_permissions", "AsyncModelPermissionsWithRawResponse"), + "ModelPermissionsWithStreamingResponse": (".model_permissions", "ModelPermissionsWithStreamingResponse"), + "AsyncModelPermissionsWithStreamingResponse": ( + ".model_permissions", + "AsyncModelPermissionsWithStreamingResponse", + ), + "HostedToolPermissions": (".hosted_tool_permissions", "HostedToolPermissions"), + "AsyncHostedToolPermissions": (".hosted_tool_permissions", "AsyncHostedToolPermissions"), + "HostedToolPermissionsWithRawResponse": (".hosted_tool_permissions", "HostedToolPermissionsWithRawResponse"), + "AsyncHostedToolPermissionsWithRawResponse": ( + ".hosted_tool_permissions", + "AsyncHostedToolPermissionsWithRawResponse", + ), + "HostedToolPermissionsWithStreamingResponse": ( + ".hosted_tool_permissions", + "HostedToolPermissionsWithStreamingResponse", + ), + "AsyncHostedToolPermissionsWithStreamingResponse": ( + ".hosted_tool_permissions", + "AsyncHostedToolPermissionsWithStreamingResponse", + ), + "Groups": (".groups", "Groups"), + "AsyncGroups": (".groups", "AsyncGroups"), + "GroupsWithRawResponse": (".groups", "GroupsWithRawResponse"), + "AsyncGroupsWithRawResponse": (".groups", "AsyncGroupsWithRawResponse"), + "GroupsWithStreamingResponse": (".groups", "GroupsWithStreamingResponse"), + "AsyncGroupsWithStreamingResponse": (".groups", "AsyncGroupsWithStreamingResponse"), + "Roles": (".roles", "Roles"), + "AsyncRoles": (".roles", "AsyncRoles"), + "RolesWithRawResponse": (".roles", "RolesWithRawResponse"), + "AsyncRolesWithRawResponse": (".roles", "AsyncRolesWithRawResponse"), + "RolesWithStreamingResponse": (".roles", "RolesWithStreamingResponse"), + "AsyncRolesWithStreamingResponse": (".roles", "AsyncRolesWithStreamingResponse"), + "DataRetention": (".data_retention", "DataRetention"), + "AsyncDataRetention": (".data_retention", "AsyncDataRetention"), + "DataRetentionWithRawResponse": (".data_retention", "DataRetentionWithRawResponse"), + "AsyncDataRetentionWithRawResponse": (".data_retention", "AsyncDataRetentionWithRawResponse"), + "DataRetentionWithStreamingResponse": (".data_retention", "DataRetentionWithStreamingResponse"), + "AsyncDataRetentionWithStreamingResponse": (".data_retention", "AsyncDataRetentionWithStreamingResponse"), + "SpendLimit": (".spend_limit", "SpendLimit"), + "AsyncSpendLimit": (".spend_limit", "AsyncSpendLimit"), + "SpendLimitWithRawResponse": (".spend_limit", "SpendLimitWithRawResponse"), + "AsyncSpendLimitWithRawResponse": (".spend_limit", "AsyncSpendLimitWithRawResponse"), + "SpendLimitWithStreamingResponse": (".spend_limit", "SpendLimitWithStreamingResponse"), + "AsyncSpendLimitWithStreamingResponse": (".spend_limit", "AsyncSpendLimitWithStreamingResponse"), + "SpendAlerts": (".spend_alerts", "SpendAlerts"), + "AsyncSpendAlerts": (".spend_alerts", "AsyncSpendAlerts"), + "SpendAlertsWithRawResponse": (".spend_alerts", "SpendAlertsWithRawResponse"), + "AsyncSpendAlertsWithRawResponse": (".spend_alerts", "AsyncSpendAlertsWithRawResponse"), + "SpendAlertsWithStreamingResponse": (".spend_alerts", "SpendAlertsWithStreamingResponse"), + "AsyncSpendAlertsWithStreamingResponse": (".spend_alerts", "AsyncSpendAlertsWithStreamingResponse"), + "Certificates": (".certificates", "Certificates"), + "AsyncCertificates": (".certificates", "AsyncCertificates"), + "CertificatesWithRawResponse": (".certificates", "CertificatesWithRawResponse"), + "AsyncCertificatesWithRawResponse": (".certificates", "AsyncCertificatesWithRawResponse"), + "CertificatesWithStreamingResponse": (".certificates", "CertificatesWithStreamingResponse"), + "AsyncCertificatesWithStreamingResponse": (".certificates", "AsyncCertificatesWithStreamingResponse"), + "Projects": (".projects", "Projects"), + "AsyncProjects": (".projects", "AsyncProjects"), + "ProjectsWithRawResponse": (".projects", "ProjectsWithRawResponse"), + "AsyncProjectsWithRawResponse": (".projects", "AsyncProjectsWithRawResponse"), + "ProjectsWithStreamingResponse": (".projects", "ProjectsWithStreamingResponse"), + "AsyncProjectsWithStreamingResponse": (".projects", "AsyncProjectsWithStreamingResponse"), + } + _SUBMODULES = { + "api_keys", + "certificates", + "data_retention", + "groups", + "hosted_tool_permissions", + "model_permissions", + "projects", + "rate_limits", + "roles", + "service_accounts", + "spend_alerts", + "spend_limit", + "users", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Users", + "AsyncUsers", + "UsersWithRawResponse", + "AsyncUsersWithRawResponse", + "UsersWithStreamingResponse", + "AsyncUsersWithStreamingResponse", + "ServiceAccounts", + "AsyncServiceAccounts", + "ServiceAccountsWithRawResponse", + "AsyncServiceAccountsWithRawResponse", + "ServiceAccountsWithStreamingResponse", + "AsyncServiceAccountsWithStreamingResponse", + "APIKeys", + "AsyncAPIKeys", + "APIKeysWithRawResponse", + "AsyncAPIKeysWithRawResponse", + "APIKeysWithStreamingResponse", + "AsyncAPIKeysWithStreamingResponse", + "RateLimits", + "AsyncRateLimits", + "RateLimitsWithRawResponse", + "AsyncRateLimitsWithRawResponse", + "RateLimitsWithStreamingResponse", + "AsyncRateLimitsWithStreamingResponse", + "ModelPermissions", + "AsyncModelPermissions", + "ModelPermissionsWithRawResponse", + "AsyncModelPermissionsWithRawResponse", + "ModelPermissionsWithStreamingResponse", + "AsyncModelPermissionsWithStreamingResponse", + "HostedToolPermissions", + "AsyncHostedToolPermissions", + "HostedToolPermissionsWithRawResponse", + "AsyncHostedToolPermissionsWithRawResponse", + "HostedToolPermissionsWithStreamingResponse", + "AsyncHostedToolPermissionsWithStreamingResponse", + "Groups", + "AsyncGroups", + "GroupsWithRawResponse", + "AsyncGroupsWithRawResponse", + "GroupsWithStreamingResponse", + "AsyncGroupsWithStreamingResponse", + "Roles", + "AsyncRoles", + "RolesWithRawResponse", + "AsyncRolesWithRawResponse", + "RolesWithStreamingResponse", + "AsyncRolesWithStreamingResponse", + "DataRetention", + "AsyncDataRetention", + "DataRetentionWithRawResponse", + "AsyncDataRetentionWithRawResponse", + "DataRetentionWithStreamingResponse", + "AsyncDataRetentionWithStreamingResponse", + "SpendLimit", + "AsyncSpendLimit", + "SpendLimitWithRawResponse", + "AsyncSpendLimitWithRawResponse", + "SpendLimitWithStreamingResponse", + "AsyncSpendLimitWithStreamingResponse", + "SpendAlerts", + "AsyncSpendAlerts", + "SpendAlertsWithRawResponse", + "AsyncSpendAlertsWithRawResponse", + "SpendAlertsWithStreamingResponse", + "AsyncSpendAlertsWithStreamingResponse", + "Certificates", + "AsyncCertificates", + "CertificatesWithRawResponse", + "AsyncCertificatesWithRawResponse", + "CertificatesWithStreamingResponse", + "AsyncCertificatesWithStreamingResponse", + "Projects", + "AsyncProjects", + "ProjectsWithRawResponse", + "AsyncProjectsWithRawResponse", + "ProjectsWithStreamingResponse", + "AsyncProjectsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2d79beb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/api_keys.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/api_keys.cpython-312.pyc new file mode 100644 index 0000000..28d3bba Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/api_keys.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/certificates.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/certificates.cpython-312.pyc new file mode 100644 index 0000000..85781a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/certificates.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/data_retention.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/data_retention.cpython-312.pyc new file mode 100644 index 0000000..c85465b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/data_retention.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/hosted_tool_permissions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/hosted_tool_permissions.cpython-312.pyc new file mode 100644 index 0000000..c6521d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/hosted_tool_permissions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/model_permissions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/model_permissions.cpython-312.pyc new file mode 100644 index 0000000..328ff43 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/model_permissions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/projects.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/projects.cpython-312.pyc new file mode 100644 index 0000000..695f69f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/projects.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/rate_limits.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/rate_limits.cpython-312.pyc new file mode 100644 index 0000000..707b03c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/rate_limits.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/roles.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/roles.cpython-312.pyc new file mode 100644 index 0000000..3ee38b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/roles.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/spend_alerts.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/spend_alerts.cpython-312.pyc new file mode 100644 index 0000000..765d452 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/spend_alerts.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/spend_limit.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/spend_limit.cpython-312.pyc new file mode 100644 index 0000000..2cfbb77 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/__pycache__/spend_limit.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/api_keys.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/api_keys.py new file mode 100644 index 0000000..50ba64f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/api_keys.py @@ -0,0 +1,431 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.projects import api_key_list_params +from .....types.admin.organization.projects.project_api_key import ProjectAPIKey +from .....types.admin.organization.projects.api_key_delete_response import APIKeyDeleteResponse + +__all__ = ["APIKeys", "AsyncAPIKeys"] + + +class APIKeys(SyncAPIResource): + @cached_property + def with_raw_response(self) -> APIKeysWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return APIKeysWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> APIKeysWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return APIKeysWithStreamingResponse(self) + + def retrieve( + self, + api_key_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectAPIKey: + """ + Retrieves an API key in the project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not api_key_id: + raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}") + return self._get( + path_template( + "/organization/projects/{project_id}/api_keys/{api_key_id}", + project_id=project_id, + api_key_id=api_key_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectAPIKey, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + owner_project_access: Literal["active", "inactive", "any"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ProjectAPIKey]: + """ + Returns a list of API keys in the project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + owner_project_access: Filter API keys by whether the owner currently has effective access to the + project. Use `active` for owners with access, `inactive` for owners without + access, or `any` for all enabled project API keys. If omitted, the endpoint + applies its existing membership-based visibility rules, which may exclude some + enabled keys. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/api_keys", project_id=project_id), + page=SyncConversationCursorPage[ProjectAPIKey], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "owner_project_access": owner_project_access, + }, + api_key_list_params.APIKeyListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectAPIKey, + ) + + def delete( + self, + api_key_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> APIKeyDeleteResponse: + """ + Deletes an API key from the project. + + Returns confirmation of the key deletion, or an error if the key belonged to a + service account. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not api_key_id: + raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}") + return self._delete( + path_template( + "/organization/projects/{project_id}/api_keys/{api_key_id}", + project_id=project_id, + api_key_id=api_key_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=APIKeyDeleteResponse, + ) + + +class AsyncAPIKeys(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAPIKeysWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAPIKeysWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAPIKeysWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAPIKeysWithStreamingResponse(self) + + async def retrieve( + self, + api_key_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectAPIKey: + """ + Retrieves an API key in the project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not api_key_id: + raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}") + return await self._get( + path_template( + "/organization/projects/{project_id}/api_keys/{api_key_id}", + project_id=project_id, + api_key_id=api_key_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectAPIKey, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + owner_project_access: Literal["active", "inactive", "any"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ProjectAPIKey, AsyncConversationCursorPage[ProjectAPIKey]]: + """ + Returns a list of API keys in the project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + owner_project_access: Filter API keys by whether the owner currently has effective access to the + project. Use `active` for owners with access, `inactive` for owners without + access, or `any` for all enabled project API keys. If omitted, the endpoint + applies its existing membership-based visibility rules, which may exclude some + enabled keys. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/api_keys", project_id=project_id), + page=AsyncConversationCursorPage[ProjectAPIKey], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "owner_project_access": owner_project_access, + }, + api_key_list_params.APIKeyListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectAPIKey, + ) + + async def delete( + self, + api_key_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> APIKeyDeleteResponse: + """ + Deletes an API key from the project. + + Returns confirmation of the key deletion, or an error if the key belonged to a + service account. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not api_key_id: + raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}") + return await self._delete( + path_template( + "/organization/projects/{project_id}/api_keys/{api_key_id}", + project_id=project_id, + api_key_id=api_key_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=APIKeyDeleteResponse, + ) + + +class APIKeysWithRawResponse: + def __init__(self, api_keys: APIKeys) -> None: + self._api_keys = api_keys + + self.retrieve = _legacy_response.to_raw_response_wrapper( + api_keys.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + api_keys.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + api_keys.delete, + ) + + +class AsyncAPIKeysWithRawResponse: + def __init__(self, api_keys: AsyncAPIKeys) -> None: + self._api_keys = api_keys + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + api_keys.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + api_keys.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + api_keys.delete, + ) + + +class APIKeysWithStreamingResponse: + def __init__(self, api_keys: APIKeys) -> None: + self._api_keys = api_keys + + self.retrieve = to_streamed_response_wrapper( + api_keys.retrieve, + ) + self.list = to_streamed_response_wrapper( + api_keys.list, + ) + self.delete = to_streamed_response_wrapper( + api_keys.delete, + ) + + +class AsyncAPIKeysWithStreamingResponse: + def __init__(self, api_keys: AsyncAPIKeys) -> None: + self._api_keys = api_keys + + self.retrieve = async_to_streamed_response_wrapper( + api_keys.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + api_keys.list, + ) + self.delete = async_to_streamed_response_wrapper( + api_keys.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/certificates.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/certificates.py new file mode 100644 index 0000000..c57f1d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/certificates.py @@ -0,0 +1,428 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ....._utils import path_template, maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncPage, AsyncPage, SyncConversationCursorPage, AsyncConversationCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.projects import ( + certificate_list_params, + certificate_activate_params, + certificate_deactivate_params, +) +from .....types.admin.organization.projects.certificate_list_response import CertificateListResponse +from .....types.admin.organization.projects.certificate_activate_response import CertificateActivateResponse +from .....types.admin.organization.projects.certificate_deactivate_response import CertificateDeactivateResponse + +__all__ = ["Certificates", "AsyncCertificates"] + + +class Certificates(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CertificatesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CertificatesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CertificatesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CertificatesWithStreamingResponse(self) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[CertificateListResponse]: + """ + List certificates for this project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/certificates", project_id=project_id), + page=SyncConversationCursorPage[CertificateListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + certificate_list_params.CertificateListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=CertificateListResponse, + ) + + def activate( + self, + project_id: str, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncPage[CertificateActivateResponse]: + """ + Activate certificates at the project level. + + You can atomically and idempotently activate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/certificates/activate", project_id=project_id), + page=SyncPage[CertificateActivateResponse], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_activate_params.CertificateActivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=CertificateActivateResponse, + method="post", + ) + + def deactivate( + self, + project_id: str, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncPage[CertificateDeactivateResponse]: + """Deactivate certificates at the project level. + + You can atomically and + idempotently deactivate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/certificates/deactivate", project_id=project_id), + page=SyncPage[CertificateDeactivateResponse], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_deactivate_params.CertificateDeactivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=CertificateDeactivateResponse, + method="post", + ) + + +class AsyncCertificates(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCertificatesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCertificatesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCertificatesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCertificatesWithStreamingResponse(self) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[CertificateListResponse, AsyncConversationCursorPage[CertificateListResponse]]: + """ + List certificates for this project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/certificates", project_id=project_id), + page=AsyncConversationCursorPage[CertificateListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + certificate_list_params.CertificateListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=CertificateListResponse, + ) + + def activate( + self, + project_id: str, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[CertificateActivateResponse, AsyncPage[CertificateActivateResponse]]: + """ + Activate certificates at the project level. + + You can atomically and idempotently activate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/certificates/activate", project_id=project_id), + page=AsyncPage[CertificateActivateResponse], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_activate_params.CertificateActivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=CertificateActivateResponse, + method="post", + ) + + def deactivate( + self, + project_id: str, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[CertificateDeactivateResponse, AsyncPage[CertificateDeactivateResponse]]: + """Deactivate certificates at the project level. + + You can atomically and + idempotently deactivate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/certificates/deactivate", project_id=project_id), + page=AsyncPage[CertificateDeactivateResponse], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_deactivate_params.CertificateDeactivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=CertificateDeactivateResponse, + method="post", + ) + + +class CertificatesWithRawResponse: + def __init__(self, certificates: Certificates) -> None: + self._certificates = certificates + + self.list = _legacy_response.to_raw_response_wrapper( + certificates.list, + ) + self.activate = _legacy_response.to_raw_response_wrapper( + certificates.activate, + ) + self.deactivate = _legacy_response.to_raw_response_wrapper( + certificates.deactivate, + ) + + +class AsyncCertificatesWithRawResponse: + def __init__(self, certificates: AsyncCertificates) -> None: + self._certificates = certificates + + self.list = _legacy_response.async_to_raw_response_wrapper( + certificates.list, + ) + self.activate = _legacy_response.async_to_raw_response_wrapper( + certificates.activate, + ) + self.deactivate = _legacy_response.async_to_raw_response_wrapper( + certificates.deactivate, + ) + + +class CertificatesWithStreamingResponse: + def __init__(self, certificates: Certificates) -> None: + self._certificates = certificates + + self.list = to_streamed_response_wrapper( + certificates.list, + ) + self.activate = to_streamed_response_wrapper( + certificates.activate, + ) + self.deactivate = to_streamed_response_wrapper( + certificates.deactivate, + ) + + +class AsyncCertificatesWithStreamingResponse: + def __init__(self, certificates: AsyncCertificates) -> None: + self._certificates = certificates + + self.list = async_to_streamed_response_wrapper( + certificates.list, + ) + self.activate = async_to_streamed_response_wrapper( + certificates.activate, + ) + self.deactivate = async_to_streamed_response_wrapper( + certificates.deactivate, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/data_retention.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/data_retention.py new file mode 100644 index 0000000..90ab30d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/data_retention.py @@ -0,0 +1,283 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Query, Headers, NotGiven, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._base_client import make_request_options +from .....types.admin.organization.projects import data_retention_update_params +from .....types.admin.organization.projects.project_data_retention import ProjectDataRetention + +__all__ = ["DataRetention", "AsyncDataRetention"] + + +class DataRetention(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DataRetentionWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return DataRetentionWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DataRetentionWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return DataRetentionWithStreamingResponse(self) + + def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectDataRetention: + """ + Retrieves project data retention controls. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get( + path_template("/organization/projects/{project_id}/data_retention", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectDataRetention, + ) + + def update( + self, + project_id: str, + *, + retention_type: Literal[ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectDataRetention: + """ + Updates project data retention controls. + + Args: + retention_type: The desired project data retention type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/data_retention", project_id=project_id), + body=maybe_transform( + {"retention_type": retention_type}, data_retention_update_params.DataRetentionUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectDataRetention, + ) + + +class AsyncDataRetention(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDataRetentionWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncDataRetentionWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDataRetentionWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncDataRetentionWithStreamingResponse(self) + + async def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectDataRetention: + """ + Retrieves project data retention controls. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._get( + path_template("/organization/projects/{project_id}/data_retention", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectDataRetention, + ) + + async def update( + self, + project_id: str, + *, + retention_type: Literal[ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectDataRetention: + """ + Updates project data retention controls. + + Args: + retention_type: The desired project data retention type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/data_retention", project_id=project_id), + body=await async_maybe_transform( + {"retention_type": retention_type}, data_retention_update_params.DataRetentionUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectDataRetention, + ) + + +class DataRetentionWithRawResponse: + def __init__(self, data_retention: DataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = _legacy_response.to_raw_response_wrapper( + data_retention.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + data_retention.update, + ) + + +class AsyncDataRetentionWithRawResponse: + def __init__(self, data_retention: AsyncDataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + data_retention.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + data_retention.update, + ) + + +class DataRetentionWithStreamingResponse: + def __init__(self, data_retention: DataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = to_streamed_response_wrapper( + data_retention.retrieve, + ) + self.update = to_streamed_response_wrapper( + data_retention.update, + ) + + +class AsyncDataRetentionWithStreamingResponse: + def __init__(self, data_retention: AsyncDataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = async_to_streamed_response_wrapper( + data_retention.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + data_retention.update, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/__init__.py new file mode 100644 index 0000000..50fbe34 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .roles import ( + Roles as Roles, + AsyncRoles as AsyncRoles, + RolesWithRawResponse as RolesWithRawResponse, + AsyncRolesWithRawResponse as AsyncRolesWithRawResponse, + RolesWithStreamingResponse as RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse as AsyncRolesWithStreamingResponse, + ) + from .groups import ( + Groups as Groups, + AsyncGroups as AsyncGroups, + GroupsWithRawResponse as GroupsWithRawResponse, + AsyncGroupsWithRawResponse as AsyncGroupsWithRawResponse, + GroupsWithStreamingResponse as GroupsWithStreamingResponse, + AsyncGroupsWithStreamingResponse as AsyncGroupsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Roles": (".roles", "Roles"), + "AsyncRoles": (".roles", "AsyncRoles"), + "RolesWithRawResponse": (".roles", "RolesWithRawResponse"), + "AsyncRolesWithRawResponse": (".roles", "AsyncRolesWithRawResponse"), + "RolesWithStreamingResponse": (".roles", "RolesWithStreamingResponse"), + "AsyncRolesWithStreamingResponse": (".roles", "AsyncRolesWithStreamingResponse"), + "Groups": (".groups", "Groups"), + "AsyncGroups": (".groups", "AsyncGroups"), + "GroupsWithRawResponse": (".groups", "GroupsWithRawResponse"), + "AsyncGroupsWithRawResponse": (".groups", "AsyncGroupsWithRawResponse"), + "GroupsWithStreamingResponse": (".groups", "GroupsWithStreamingResponse"), + "AsyncGroupsWithStreamingResponse": (".groups", "AsyncGroupsWithStreamingResponse"), + } + _SUBMODULES = { + "groups", + "roles", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Roles", + "AsyncRoles", + "RolesWithRawResponse", + "AsyncRolesWithRawResponse", + "RolesWithStreamingResponse", + "AsyncRolesWithStreamingResponse", + "Groups", + "AsyncGroups", + "GroupsWithRawResponse", + "AsyncGroupsWithRawResponse", + "GroupsWithStreamingResponse", + "AsyncGroupsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..041e355 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/__pycache__/groups.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/__pycache__/groups.cpython-312.pyc new file mode 100644 index 0000000..a3080ce Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/__pycache__/groups.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/__pycache__/roles.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/__pycache__/roles.cpython-312.pyc new file mode 100644 index 0000000..665c662 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/__pycache__/roles.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/groups.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/groups.py new file mode 100644 index 0000000..e86700c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/groups.py @@ -0,0 +1,557 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ...... import _legacy_response +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......_utils import path_template, maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......pagination import SyncNextCursorPage, AsyncNextCursorPage +from ......_base_client import AsyncPaginator, make_request_options +from ......types.admin.organization.projects import group_list_params, group_create_params, group_retrieve_params +from ......types.admin.organization.projects.project_group import ProjectGroup +from ......types.admin.organization.projects.group_delete_response import GroupDeleteResponse + +__all__ = ["Groups", "AsyncGroups"] + + +class Groups(SyncAPIResource): + @cached_property + def roles(self) -> Roles: + return Roles(self._client) + + @cached_property + def with_raw_response(self) -> GroupsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return GroupsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GroupsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return GroupsWithStreamingResponse(self) + + def create( + self, + project_id: str, + *, + group_id: str, + role: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectGroup: + """ + Grants a group access to a project. + + Args: + group_id: Identifier of the group to add to the project. + + role: Identifier of the project role to grant to the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/groups", project_id=project_id), + body=maybe_transform( + { + "group_id": group_id, + "role": role, + }, + group_create_params.GroupCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectGroup, + ) + + def retrieve( + self, + group_id: str, + *, + project_id: str, + group_type: Literal["group", "tenant_group"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectGroup: + """ + Retrieves a project's group. + + Args: + group_type: The type of group to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get( + path_template( + "/organization/projects/{project_id}/groups/{group_id}", project_id=project_id, group_id=group_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"group_type": group_type}, group_retrieve_params.GroupRetrieveParams), + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectGroup, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncNextCursorPage[ProjectGroup]: + """ + Lists the groups that have access to a project. + + Args: + after: Cursor for pagination. Provide the ID of the last group from the previous + response to fetch the next page. + + limit: A limit on the number of project groups to return. Defaults to 20. + + order: Sort order for the returned groups. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/groups", project_id=project_id), + page=SyncNextCursorPage[ProjectGroup], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + group_list_params.GroupListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectGroup, + ) + + def delete( + self, + group_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> GroupDeleteResponse: + """ + Revokes a group's access to a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._delete( + path_template( + "/organization/projects/{project_id}/groups/{group_id}", project_id=project_id, group_id=group_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=GroupDeleteResponse, + ) + + +class AsyncGroups(AsyncAPIResource): + @cached_property + def roles(self) -> AsyncRoles: + return AsyncRoles(self._client) + + @cached_property + def with_raw_response(self) -> AsyncGroupsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncGroupsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGroupsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncGroupsWithStreamingResponse(self) + + async def create( + self, + project_id: str, + *, + group_id: str, + role: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectGroup: + """ + Grants a group access to a project. + + Args: + group_id: Identifier of the group to add to the project. + + role: Identifier of the project role to grant to the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/groups", project_id=project_id), + body=await async_maybe_transform( + { + "group_id": group_id, + "role": role, + }, + group_create_params.GroupCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectGroup, + ) + + async def retrieve( + self, + group_id: str, + *, + project_id: str, + group_type: Literal["group", "tenant_group"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectGroup: + """ + Retrieves a project's group. + + Args: + group_type: The type of group to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._get( + path_template( + "/organization/projects/{project_id}/groups/{group_id}", project_id=project_id, group_id=group_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"group_type": group_type}, group_retrieve_params.GroupRetrieveParams + ), + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectGroup, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ProjectGroup, AsyncNextCursorPage[ProjectGroup]]: + """ + Lists the groups that have access to a project. + + Args: + after: Cursor for pagination. Provide the ID of the last group from the previous + response to fetch the next page. + + limit: A limit on the number of project groups to return. Defaults to 20. + + order: Sort order for the returned groups. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/groups", project_id=project_id), + page=AsyncNextCursorPage[ProjectGroup], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + group_list_params.GroupListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectGroup, + ) + + async def delete( + self, + group_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> GroupDeleteResponse: + """ + Revokes a group's access to a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._delete( + path_template( + "/organization/projects/{project_id}/groups/{group_id}", project_id=project_id, group_id=group_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=GroupDeleteResponse, + ) + + +class GroupsWithRawResponse: + def __init__(self, groups: Groups) -> None: + self._groups = groups + + self.create = _legacy_response.to_raw_response_wrapper( + groups.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + groups.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + groups.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + groups.delete, + ) + + @cached_property + def roles(self) -> RolesWithRawResponse: + return RolesWithRawResponse(self._groups.roles) + + +class AsyncGroupsWithRawResponse: + def __init__(self, groups: AsyncGroups) -> None: + self._groups = groups + + self.create = _legacy_response.async_to_raw_response_wrapper( + groups.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + groups.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + groups.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + groups.delete, + ) + + @cached_property + def roles(self) -> AsyncRolesWithRawResponse: + return AsyncRolesWithRawResponse(self._groups.roles) + + +class GroupsWithStreamingResponse: + def __init__(self, groups: Groups) -> None: + self._groups = groups + + self.create = to_streamed_response_wrapper( + groups.create, + ) + self.retrieve = to_streamed_response_wrapper( + groups.retrieve, + ) + self.list = to_streamed_response_wrapper( + groups.list, + ) + self.delete = to_streamed_response_wrapper( + groups.delete, + ) + + @cached_property + def roles(self) -> RolesWithStreamingResponse: + return RolesWithStreamingResponse(self._groups.roles) + + +class AsyncGroupsWithStreamingResponse: + def __init__(self, groups: AsyncGroups) -> None: + self._groups = groups + + self.create = async_to_streamed_response_wrapper( + groups.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + groups.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + groups.list, + ) + self.delete = async_to_streamed_response_wrapper( + groups.delete, + ) + + @cached_property + def roles(self) -> AsyncRolesWithStreamingResponse: + return AsyncRolesWithStreamingResponse(self._groups.roles) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/roles.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/roles.py new file mode 100644 index 0000000..a3f9cff --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/groups/roles.py @@ -0,0 +1,535 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ...... import _legacy_response +from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......_utils import path_template, maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......pagination import SyncNextCursorPage, AsyncNextCursorPage +from ......_base_client import AsyncPaginator, make_request_options +from ......types.admin.organization.projects.groups import role_list_params, role_create_params +from ......types.admin.organization.projects.groups.role_list_response import RoleListResponse +from ......types.admin.organization.projects.groups.role_create_response import RoleCreateResponse +from ......types.admin.organization.projects.groups.role_delete_response import RoleDeleteResponse +from ......types.admin.organization.projects.groups.role_retrieve_response import RoleRetrieveResponse + +__all__ = ["Roles", "AsyncRoles"] + + +class Roles(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RolesWithStreamingResponse(self) + + def create( + self, + group_id: str, + *, + project_id: str, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns a project role to a group within a project. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._post( + path_template("/projects/{project_id}/groups/{group_id}/roles", project_id=project_id, group_id=group_id), + body=maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + def retrieve( + self, + role_id: str, + *, + project_id: str, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves a project role assigned to a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template( + "/projects/{project_id}/groups/{group_id}/roles/{role_id}", + project_id=project_id, + group_id=group_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + + def list( + self, + group_id: str, + *, + project_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncNextCursorPage[RoleListResponse]: + """ + Lists the project roles assigned to a group within a project. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing project roles. + + limit: A limit on the number of project role assignments to return. + + order: Sort order for the returned project roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get_api_list( + path_template("/projects/{project_id}/groups/{group_id}/roles", project_id=project_id, group_id=group_id), + page=SyncNextCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + def delete( + self, + role_id: str, + *, + project_id: str, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns a project role from a group within a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._delete( + path_template( + "/projects/{project_id}/groups/{group_id}/roles/{role_id}", + project_id=project_id, + group_id=group_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class AsyncRoles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRolesWithStreamingResponse(self) + + async def create( + self, + group_id: str, + *, + project_id: str, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns a project role to a group within a project. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._post( + path_template("/projects/{project_id}/groups/{group_id}/roles", project_id=project_id, group_id=group_id), + body=await async_maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + async def retrieve( + self, + role_id: str, + *, + project_id: str, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves a project role assigned to a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template( + "/projects/{project_id}/groups/{group_id}/roles/{role_id}", + project_id=project_id, + group_id=group_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + + def list( + self, + group_id: str, + *, + project_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[RoleListResponse, AsyncNextCursorPage[RoleListResponse]]: + """ + Lists the project roles assigned to a group within a project. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing project roles. + + limit: A limit on the number of project role assignments to return. + + order: Sort order for the returned project roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get_api_list( + path_template("/projects/{project_id}/groups/{group_id}/roles", project_id=project_id, group_id=group_id), + page=AsyncNextCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + async def delete( + self, + role_id: str, + *, + project_id: str, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns a project role from a group within a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._delete( + path_template( + "/projects/{project_id}/groups/{group_id}/roles/{role_id}", + project_id=project_id, + group_id=group_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class RolesWithRawResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = _legacy_response.to_raw_response_wrapper( + roles.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithRawResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = _legacy_response.async_to_raw_response_wrapper( + roles.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + roles.delete, + ) + + +class RolesWithStreamingResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = to_streamed_response_wrapper( + roles.create, + ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) + self.list = to_streamed_response_wrapper( + roles.list, + ) + self.delete = to_streamed_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithStreamingResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = async_to_streamed_response_wrapper( + roles.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + roles.list, + ) + self.delete = async_to_streamed_response_wrapper( + roles.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/hosted_tool_permissions.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/hosted_tool_permissions.py new file mode 100644 index 0000000..60ad9e8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/hosted_tool_permissions.py @@ -0,0 +1,307 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._base_client import make_request_options +from .....types.admin.organization.projects import hosted_tool_permission_update_params +from .....types.admin.organization.projects.project_hosted_tool_permissions import ProjectHostedToolPermissions + +__all__ = ["HostedToolPermissions", "AsyncHostedToolPermissions"] + + +class HostedToolPermissions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> HostedToolPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return HostedToolPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> HostedToolPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return HostedToolPermissionsWithStreamingResponse(self) + + def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectHostedToolPermissions: + """ + Returns hosted tool permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get( + path_template("/organization/projects/{project_id}/hosted_tool_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectHostedToolPermissions, + ) + + def update( + self, + project_id: str, + *, + code_interpreter: Optional[hosted_tool_permission_update_params.CodeInterpreter] | Omit = omit, + file_search: Optional[hosted_tool_permission_update_params.FileSearch] | Omit = omit, + image_generation: Optional[hosted_tool_permission_update_params.ImageGeneration] | Omit = omit, + mcp: Optional[hosted_tool_permission_update_params.Mcp] | Omit = omit, + web_search: Optional[hosted_tool_permission_update_params.WebSearch] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectHostedToolPermissions: + """ + Updates hosted tool permissions for a project. + + Args: + code_interpreter: The code interpreter permission update. + + file_search: The file search permission update. + + image_generation: The image generation permission update. + + mcp: The MCP permission update. + + web_search: The web search permission update. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/hosted_tool_permissions", project_id=project_id), + body=maybe_transform( + { + "code_interpreter": code_interpreter, + "file_search": file_search, + "image_generation": image_generation, + "mcp": mcp, + "web_search": web_search, + }, + hosted_tool_permission_update_params.HostedToolPermissionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectHostedToolPermissions, + ) + + +class AsyncHostedToolPermissions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncHostedToolPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncHostedToolPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncHostedToolPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncHostedToolPermissionsWithStreamingResponse(self) + + async def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectHostedToolPermissions: + """ + Returns hosted tool permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._get( + path_template("/organization/projects/{project_id}/hosted_tool_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectHostedToolPermissions, + ) + + async def update( + self, + project_id: str, + *, + code_interpreter: Optional[hosted_tool_permission_update_params.CodeInterpreter] | Omit = omit, + file_search: Optional[hosted_tool_permission_update_params.FileSearch] | Omit = omit, + image_generation: Optional[hosted_tool_permission_update_params.ImageGeneration] | Omit = omit, + mcp: Optional[hosted_tool_permission_update_params.Mcp] | Omit = omit, + web_search: Optional[hosted_tool_permission_update_params.WebSearch] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectHostedToolPermissions: + """ + Updates hosted tool permissions for a project. + + Args: + code_interpreter: The code interpreter permission update. + + file_search: The file search permission update. + + image_generation: The image generation permission update. + + mcp: The MCP permission update. + + web_search: The web search permission update. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/hosted_tool_permissions", project_id=project_id), + body=await async_maybe_transform( + { + "code_interpreter": code_interpreter, + "file_search": file_search, + "image_generation": image_generation, + "mcp": mcp, + "web_search": web_search, + }, + hosted_tool_permission_update_params.HostedToolPermissionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectHostedToolPermissions, + ) + + +class HostedToolPermissionsWithRawResponse: + def __init__(self, hosted_tool_permissions: HostedToolPermissions) -> None: + self._hosted_tool_permissions = hosted_tool_permissions + + self.retrieve = _legacy_response.to_raw_response_wrapper( + hosted_tool_permissions.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + hosted_tool_permissions.update, + ) + + +class AsyncHostedToolPermissionsWithRawResponse: + def __init__(self, hosted_tool_permissions: AsyncHostedToolPermissions) -> None: + self._hosted_tool_permissions = hosted_tool_permissions + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + hosted_tool_permissions.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + hosted_tool_permissions.update, + ) + + +class HostedToolPermissionsWithStreamingResponse: + def __init__(self, hosted_tool_permissions: HostedToolPermissions) -> None: + self._hosted_tool_permissions = hosted_tool_permissions + + self.retrieve = to_streamed_response_wrapper( + hosted_tool_permissions.retrieve, + ) + self.update = to_streamed_response_wrapper( + hosted_tool_permissions.update, + ) + + +class AsyncHostedToolPermissionsWithStreamingResponse: + def __init__(self, hosted_tool_permissions: AsyncHostedToolPermissions) -> None: + self._hosted_tool_permissions = hosted_tool_permissions + + self.retrieve = async_to_streamed_response_wrapper( + hosted_tool_permissions.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + hosted_tool_permissions.update, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/model_permissions.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/model_permissions.py new file mode 100644 index 0000000..9f9fcb4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/model_permissions.py @@ -0,0 +1,370 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Query, Headers, NotGiven, SequenceNotStr, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._base_client import make_request_options +from .....types.admin.organization.projects import model_permission_update_params +from .....types.admin.organization.projects.project_model_permissions import ProjectModelPermissions +from .....types.admin.organization.projects.project_model_permissions_deleted import ProjectModelPermissionsDeleted + +__all__ = ["ModelPermissions", "AsyncModelPermissions"] + + +class ModelPermissions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ModelPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ModelPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ModelPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ModelPermissionsWithStreamingResponse(self) + + def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissions: + """ + Returns model permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissions, + ) + + def update( + self, + project_id: str, + *, + mode: Literal["allow_list", "deny_list"], + model_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissions: + """ + Updates model permissions for a project. + + Args: + mode: The model permissions mode to apply. + + model_ids: The model IDs included in this permissions policy. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + body=maybe_transform( + { + "mode": mode, + "model_ids": model_ids, + }, + model_permission_update_params.ModelPermissionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissions, + ) + + def delete( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissionsDeleted: + """ + Deletes model permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._delete( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissionsDeleted, + ) + + +class AsyncModelPermissions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncModelPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncModelPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncModelPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncModelPermissionsWithStreamingResponse(self) + + async def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissions: + """ + Returns model permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._get( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissions, + ) + + async def update( + self, + project_id: str, + *, + mode: Literal["allow_list", "deny_list"], + model_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissions: + """ + Updates model permissions for a project. + + Args: + mode: The model permissions mode to apply. + + model_ids: The model IDs included in this permissions policy. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + body=await async_maybe_transform( + { + "mode": mode, + "model_ids": model_ids, + }, + model_permission_update_params.ModelPermissionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissions, + ) + + async def delete( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissionsDeleted: + """ + Deletes model permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._delete( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissionsDeleted, + ) + + +class ModelPermissionsWithRawResponse: + def __init__(self, model_permissions: ModelPermissions) -> None: + self._model_permissions = model_permissions + + self.retrieve = _legacy_response.to_raw_response_wrapper( + model_permissions.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + model_permissions.update, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + model_permissions.delete, + ) + + +class AsyncModelPermissionsWithRawResponse: + def __init__(self, model_permissions: AsyncModelPermissions) -> None: + self._model_permissions = model_permissions + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + model_permissions.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + model_permissions.update, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + model_permissions.delete, + ) + + +class ModelPermissionsWithStreamingResponse: + def __init__(self, model_permissions: ModelPermissions) -> None: + self._model_permissions = model_permissions + + self.retrieve = to_streamed_response_wrapper( + model_permissions.retrieve, + ) + self.update = to_streamed_response_wrapper( + model_permissions.update, + ) + self.delete = to_streamed_response_wrapper( + model_permissions.delete, + ) + + +class AsyncModelPermissionsWithStreamingResponse: + def __init__(self, model_permissions: AsyncModelPermissions) -> None: + self._model_permissions = model_permissions + + self.retrieve = async_to_streamed_response_wrapper( + model_permissions.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + model_permissions.update, + ) + self.delete = async_to_streamed_response_wrapper( + model_permissions.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/projects.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/projects.py new file mode 100644 index 0000000..f85e73c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/projects.py @@ -0,0 +1,1044 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx2 + +from ..... import _legacy_response +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from .api_keys import ( + APIKeys, + AsyncAPIKeys, + APIKeysWithRawResponse, + AsyncAPIKeysWithRawResponse, + APIKeysWithStreamingResponse, + AsyncAPIKeysWithStreamingResponse, +) +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from .rate_limits import ( + RateLimits, + AsyncRateLimits, + RateLimitsWithRawResponse, + AsyncRateLimitsWithRawResponse, + RateLimitsWithStreamingResponse, + AsyncRateLimitsWithStreamingResponse, +) +from .spend_limit import ( + SpendLimit, + AsyncSpendLimit, + SpendLimitWithRawResponse, + AsyncSpendLimitWithRawResponse, + SpendLimitWithStreamingResponse, + AsyncSpendLimitWithStreamingResponse, +) +from .users.users import ( + Users, + AsyncUsers, + UsersWithRawResponse, + AsyncUsersWithRawResponse, + UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse, +) +from .certificates import ( + Certificates, + AsyncCertificates, + CertificatesWithRawResponse, + AsyncCertificatesWithRawResponse, + CertificatesWithStreamingResponse, + AsyncCertificatesWithStreamingResponse, +) +from .spend_alerts import ( + SpendAlerts, + AsyncSpendAlerts, + SpendAlertsWithRawResponse, + AsyncSpendAlertsWithRawResponse, + SpendAlertsWithStreamingResponse, + AsyncSpendAlertsWithStreamingResponse, +) +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .groups.groups import ( + Groups, + AsyncGroups, + GroupsWithRawResponse, + AsyncGroupsWithRawResponse, + GroupsWithStreamingResponse, + AsyncGroupsWithStreamingResponse, +) +from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from .data_retention import ( + DataRetention, + AsyncDataRetention, + DataRetentionWithRawResponse, + AsyncDataRetentionWithRawResponse, + DataRetentionWithStreamingResponse, + AsyncDataRetentionWithStreamingResponse, +) +from ....._base_client import AsyncPaginator, make_request_options +from .model_permissions import ( + ModelPermissions, + AsyncModelPermissions, + ModelPermissionsWithRawResponse, + AsyncModelPermissionsWithRawResponse, + ModelPermissionsWithStreamingResponse, + AsyncModelPermissionsWithStreamingResponse, +) +from .hosted_tool_permissions import ( + HostedToolPermissions, + AsyncHostedToolPermissions, + HostedToolPermissionsWithRawResponse, + AsyncHostedToolPermissionsWithRawResponse, + HostedToolPermissionsWithStreamingResponse, + AsyncHostedToolPermissionsWithStreamingResponse, +) +from .....types.admin.organization import ( + ProjectResidency, + project_list_params, + project_create_params, + project_update_params, +) +from .service_accounts.service_accounts import ( + ServiceAccounts, + AsyncServiceAccounts, + ServiceAccountsWithRawResponse, + AsyncServiceAccountsWithRawResponse, + ServiceAccountsWithStreamingResponse, + AsyncServiceAccountsWithStreamingResponse, +) +from .....types.admin.organization.project import Project +from .....types.admin.organization.project_residency import ProjectResidency + +__all__ = ["Projects", "AsyncProjects"] + + +class Projects(SyncAPIResource): + @cached_property + def users(self) -> Users: + return Users(self._client) + + @cached_property + def service_accounts(self) -> ServiceAccounts: + return ServiceAccounts(self._client) + + @cached_property + def api_keys(self) -> APIKeys: + return APIKeys(self._client) + + @cached_property + def rate_limits(self) -> RateLimits: + return RateLimits(self._client) + + @cached_property + def model_permissions(self) -> ModelPermissions: + return ModelPermissions(self._client) + + @cached_property + def hosted_tool_permissions(self) -> HostedToolPermissions: + return HostedToolPermissions(self._client) + + @cached_property + def groups(self) -> Groups: + return Groups(self._client) + + @cached_property + def roles(self) -> Roles: + return Roles(self._client) + + @cached_property + def data_retention(self) -> DataRetention: + return DataRetention(self._client) + + @cached_property + def spend_limit(self) -> SpendLimit: + return SpendLimit(self._client) + + @cached_property + def spend_alerts(self) -> SpendAlerts: + return SpendAlerts(self._client) + + @cached_property + def certificates(self) -> Certificates: + return Certificates(self._client) + + @cached_property + def with_raw_response(self) -> ProjectsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ProjectsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ProjectsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ProjectsWithStreamingResponse(self) + + def create( + self, + *, + name: str, + external_key_id: Optional[str] | Omit = omit, + geography: Optional[str] | Omit = omit, + residency: Optional[ProjectResidency] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Project: + """Create a new project in the organization. + + Projects can be created and archived, + but cannot be deleted. + + Args: + name: The friendly name of the project, this name appears in reports. + + external_key_id: External key ID to associate with the project. + + geography: Create the project with the specified data residency region. Your organization + must have access to Data residency functionality in order to use. See + [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) + to review the functionality and limitations of setting this field. Deprecated: + use `residency` instead. Do not provide both `geography` and `residency`. + + residency: Create the project with the specified residency configuration. Your organization + must have access to the requested residency configuration in order to use it. + See + [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) + to review the functionality and limitations of setting this field. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/projects", + body=maybe_transform( + { + "name": name, + "external_key_id": external_key_id, + "geography": geography, + "residency": residency, + }, + project_create_params.ProjectCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Project: + """ + Retrieves a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get( + path_template("/organization/projects/{project_id}", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + def update( + self, + project_id: str, + *, + external_key_id: Optional[str] | Omit = omit, + geography: Optional[str] | Omit = omit, + name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Project: + """ + Modifies a project in the organization. + + Args: + external_key_id: External key ID to associate with the project. + + geography: Geography for the project. Deprecated: use `residency` when creating a project + to configure data residency. This field is retained for backward compatibility. + + name: The updated name of the project, this name appears in reports. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}", project_id=project_id), + body=maybe_transform( + { + "external_key_id": external_key_id, + "geography": geography, + "name": name, + }, + project_update_params.ProjectUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + def list( + self, + *, + after: str | Omit = omit, + include_archived: bool | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[Project]: + """Returns a list of projects. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + include_archived: If `true` returns all projects including those that have been `archived`. + Archived projects are not included by default. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/projects", + page=SyncConversationCursorPage[Project], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include_archived": include_archived, + "limit": limit, + }, + project_list_params.ProjectListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Project, + ) + + def archive( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Project: + """Archives a project in the organization. + + Archived projects cannot be used or + updated. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/archive", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + +class AsyncProjects(AsyncAPIResource): + @cached_property + def users(self) -> AsyncUsers: + return AsyncUsers(self._client) + + @cached_property + def service_accounts(self) -> AsyncServiceAccounts: + return AsyncServiceAccounts(self._client) + + @cached_property + def api_keys(self) -> AsyncAPIKeys: + return AsyncAPIKeys(self._client) + + @cached_property + def rate_limits(self) -> AsyncRateLimits: + return AsyncRateLimits(self._client) + + @cached_property + def model_permissions(self) -> AsyncModelPermissions: + return AsyncModelPermissions(self._client) + + @cached_property + def hosted_tool_permissions(self) -> AsyncHostedToolPermissions: + return AsyncHostedToolPermissions(self._client) + + @cached_property + def groups(self) -> AsyncGroups: + return AsyncGroups(self._client) + + @cached_property + def roles(self) -> AsyncRoles: + return AsyncRoles(self._client) + + @cached_property + def data_retention(self) -> AsyncDataRetention: + return AsyncDataRetention(self._client) + + @cached_property + def spend_limit(self) -> AsyncSpendLimit: + return AsyncSpendLimit(self._client) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlerts: + return AsyncSpendAlerts(self._client) + + @cached_property + def certificates(self) -> AsyncCertificates: + return AsyncCertificates(self._client) + + @cached_property + def with_raw_response(self) -> AsyncProjectsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncProjectsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncProjectsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncProjectsWithStreamingResponse(self) + + async def create( + self, + *, + name: str, + external_key_id: Optional[str] | Omit = omit, + geography: Optional[str] | Omit = omit, + residency: Optional[ProjectResidency] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Project: + """Create a new project in the organization. + + Projects can be created and archived, + but cannot be deleted. + + Args: + name: The friendly name of the project, this name appears in reports. + + external_key_id: External key ID to associate with the project. + + geography: Create the project with the specified data residency region. Your organization + must have access to Data residency functionality in order to use. See + [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) + to review the functionality and limitations of setting this field. Deprecated: + use `residency` instead. Do not provide both `geography` and `residency`. + + residency: Create the project with the specified residency configuration. Your organization + must have access to the requested residency configuration in order to use it. + See + [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) + to review the functionality and limitations of setting this field. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/projects", + body=await async_maybe_transform( + { + "name": name, + "external_key_id": external_key_id, + "geography": geography, + "residency": residency, + }, + project_create_params.ProjectCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + async def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Project: + """ + Retrieves a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._get( + path_template("/organization/projects/{project_id}", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + async def update( + self, + project_id: str, + *, + external_key_id: Optional[str] | Omit = omit, + geography: Optional[str] | Omit = omit, + name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Project: + """ + Modifies a project in the organization. + + Args: + external_key_id: External key ID to associate with the project. + + geography: Geography for the project. Deprecated: use `residency` when creating a project + to configure data residency. This field is retained for backward compatibility. + + name: The updated name of the project, this name appears in reports. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}", project_id=project_id), + body=await async_maybe_transform( + { + "external_key_id": external_key_id, + "geography": geography, + "name": name, + }, + project_update_params.ProjectUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + def list( + self, + *, + after: str | Omit = omit, + include_archived: bool | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Project, AsyncConversationCursorPage[Project]]: + """Returns a list of projects. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + include_archived: If `true` returns all projects including those that have been `archived`. + Archived projects are not included by default. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/projects", + page=AsyncConversationCursorPage[Project], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include_archived": include_archived, + "limit": limit, + }, + project_list_params.ProjectListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Project, + ) + + async def archive( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Project: + """Archives a project in the organization. + + Archived projects cannot be used or + updated. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/archive", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + +class ProjectsWithRawResponse: + def __init__(self, projects: Projects) -> None: + self._projects = projects + + self.create = _legacy_response.to_raw_response_wrapper( + projects.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + projects.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + projects.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + projects.list, + ) + self.archive = _legacy_response.to_raw_response_wrapper( + projects.archive, + ) + + @cached_property + def users(self) -> UsersWithRawResponse: + return UsersWithRawResponse(self._projects.users) + + @cached_property + def service_accounts(self) -> ServiceAccountsWithRawResponse: + return ServiceAccountsWithRawResponse(self._projects.service_accounts) + + @cached_property + def api_keys(self) -> APIKeysWithRawResponse: + return APIKeysWithRawResponse(self._projects.api_keys) + + @cached_property + def rate_limits(self) -> RateLimitsWithRawResponse: + return RateLimitsWithRawResponse(self._projects.rate_limits) + + @cached_property + def model_permissions(self) -> ModelPermissionsWithRawResponse: + return ModelPermissionsWithRawResponse(self._projects.model_permissions) + + @cached_property + def hosted_tool_permissions(self) -> HostedToolPermissionsWithRawResponse: + return HostedToolPermissionsWithRawResponse(self._projects.hosted_tool_permissions) + + @cached_property + def groups(self) -> GroupsWithRawResponse: + return GroupsWithRawResponse(self._projects.groups) + + @cached_property + def roles(self) -> RolesWithRawResponse: + return RolesWithRawResponse(self._projects.roles) + + @cached_property + def data_retention(self) -> DataRetentionWithRawResponse: + return DataRetentionWithRawResponse(self._projects.data_retention) + + @cached_property + def spend_limit(self) -> SpendLimitWithRawResponse: + return SpendLimitWithRawResponse(self._projects.spend_limit) + + @cached_property + def spend_alerts(self) -> SpendAlertsWithRawResponse: + return SpendAlertsWithRawResponse(self._projects.spend_alerts) + + @cached_property + def certificates(self) -> CertificatesWithRawResponse: + return CertificatesWithRawResponse(self._projects.certificates) + + +class AsyncProjectsWithRawResponse: + def __init__(self, projects: AsyncProjects) -> None: + self._projects = projects + + self.create = _legacy_response.async_to_raw_response_wrapper( + projects.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + projects.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + projects.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + projects.list, + ) + self.archive = _legacy_response.async_to_raw_response_wrapper( + projects.archive, + ) + + @cached_property + def users(self) -> AsyncUsersWithRawResponse: + return AsyncUsersWithRawResponse(self._projects.users) + + @cached_property + def service_accounts(self) -> AsyncServiceAccountsWithRawResponse: + return AsyncServiceAccountsWithRawResponse(self._projects.service_accounts) + + @cached_property + def api_keys(self) -> AsyncAPIKeysWithRawResponse: + return AsyncAPIKeysWithRawResponse(self._projects.api_keys) + + @cached_property + def rate_limits(self) -> AsyncRateLimitsWithRawResponse: + return AsyncRateLimitsWithRawResponse(self._projects.rate_limits) + + @cached_property + def model_permissions(self) -> AsyncModelPermissionsWithRawResponse: + return AsyncModelPermissionsWithRawResponse(self._projects.model_permissions) + + @cached_property + def hosted_tool_permissions(self) -> AsyncHostedToolPermissionsWithRawResponse: + return AsyncHostedToolPermissionsWithRawResponse(self._projects.hosted_tool_permissions) + + @cached_property + def groups(self) -> AsyncGroupsWithRawResponse: + return AsyncGroupsWithRawResponse(self._projects.groups) + + @cached_property + def roles(self) -> AsyncRolesWithRawResponse: + return AsyncRolesWithRawResponse(self._projects.roles) + + @cached_property + def data_retention(self) -> AsyncDataRetentionWithRawResponse: + return AsyncDataRetentionWithRawResponse(self._projects.data_retention) + + @cached_property + def spend_limit(self) -> AsyncSpendLimitWithRawResponse: + return AsyncSpendLimitWithRawResponse(self._projects.spend_limit) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlertsWithRawResponse: + return AsyncSpendAlertsWithRawResponse(self._projects.spend_alerts) + + @cached_property + def certificates(self) -> AsyncCertificatesWithRawResponse: + return AsyncCertificatesWithRawResponse(self._projects.certificates) + + +class ProjectsWithStreamingResponse: + def __init__(self, projects: Projects) -> None: + self._projects = projects + + self.create = to_streamed_response_wrapper( + projects.create, + ) + self.retrieve = to_streamed_response_wrapper( + projects.retrieve, + ) + self.update = to_streamed_response_wrapper( + projects.update, + ) + self.list = to_streamed_response_wrapper( + projects.list, + ) + self.archive = to_streamed_response_wrapper( + projects.archive, + ) + + @cached_property + def users(self) -> UsersWithStreamingResponse: + return UsersWithStreamingResponse(self._projects.users) + + @cached_property + def service_accounts(self) -> ServiceAccountsWithStreamingResponse: + return ServiceAccountsWithStreamingResponse(self._projects.service_accounts) + + @cached_property + def api_keys(self) -> APIKeysWithStreamingResponse: + return APIKeysWithStreamingResponse(self._projects.api_keys) + + @cached_property + def rate_limits(self) -> RateLimitsWithStreamingResponse: + return RateLimitsWithStreamingResponse(self._projects.rate_limits) + + @cached_property + def model_permissions(self) -> ModelPermissionsWithStreamingResponse: + return ModelPermissionsWithStreamingResponse(self._projects.model_permissions) + + @cached_property + def hosted_tool_permissions(self) -> HostedToolPermissionsWithStreamingResponse: + return HostedToolPermissionsWithStreamingResponse(self._projects.hosted_tool_permissions) + + @cached_property + def groups(self) -> GroupsWithStreamingResponse: + return GroupsWithStreamingResponse(self._projects.groups) + + @cached_property + def roles(self) -> RolesWithStreamingResponse: + return RolesWithStreamingResponse(self._projects.roles) + + @cached_property + def data_retention(self) -> DataRetentionWithStreamingResponse: + return DataRetentionWithStreamingResponse(self._projects.data_retention) + + @cached_property + def spend_limit(self) -> SpendLimitWithStreamingResponse: + return SpendLimitWithStreamingResponse(self._projects.spend_limit) + + @cached_property + def spend_alerts(self) -> SpendAlertsWithStreamingResponse: + return SpendAlertsWithStreamingResponse(self._projects.spend_alerts) + + @cached_property + def certificates(self) -> CertificatesWithStreamingResponse: + return CertificatesWithStreamingResponse(self._projects.certificates) + + +class AsyncProjectsWithStreamingResponse: + def __init__(self, projects: AsyncProjects) -> None: + self._projects = projects + + self.create = async_to_streamed_response_wrapper( + projects.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + projects.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + projects.update, + ) + self.list = async_to_streamed_response_wrapper( + projects.list, + ) + self.archive = async_to_streamed_response_wrapper( + projects.archive, + ) + + @cached_property + def users(self) -> AsyncUsersWithStreamingResponse: + return AsyncUsersWithStreamingResponse(self._projects.users) + + @cached_property + def service_accounts(self) -> AsyncServiceAccountsWithStreamingResponse: + return AsyncServiceAccountsWithStreamingResponse(self._projects.service_accounts) + + @cached_property + def api_keys(self) -> AsyncAPIKeysWithStreamingResponse: + return AsyncAPIKeysWithStreamingResponse(self._projects.api_keys) + + @cached_property + def rate_limits(self) -> AsyncRateLimitsWithStreamingResponse: + return AsyncRateLimitsWithStreamingResponse(self._projects.rate_limits) + + @cached_property + def model_permissions(self) -> AsyncModelPermissionsWithStreamingResponse: + return AsyncModelPermissionsWithStreamingResponse(self._projects.model_permissions) + + @cached_property + def hosted_tool_permissions(self) -> AsyncHostedToolPermissionsWithStreamingResponse: + return AsyncHostedToolPermissionsWithStreamingResponse(self._projects.hosted_tool_permissions) + + @cached_property + def groups(self) -> AsyncGroupsWithStreamingResponse: + return AsyncGroupsWithStreamingResponse(self._projects.groups) + + @cached_property + def roles(self) -> AsyncRolesWithStreamingResponse: + return AsyncRolesWithStreamingResponse(self._projects.roles) + + @cached_property + def data_retention(self) -> AsyncDataRetentionWithStreamingResponse: + return AsyncDataRetentionWithStreamingResponse(self._projects.data_retention) + + @cached_property + def spend_limit(self) -> AsyncSpendLimitWithStreamingResponse: + return AsyncSpendLimitWithStreamingResponse(self._projects.spend_limit) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlertsWithStreamingResponse: + return AsyncSpendAlertsWithStreamingResponse(self._projects.spend_alerts) + + @cached_property + def certificates(self) -> AsyncCertificatesWithStreamingResponse: + return AsyncCertificatesWithStreamingResponse(self._projects.certificates) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/rate_limits.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/rate_limits.py new file mode 100644 index 0000000..f40ea76 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/rate_limits.py @@ -0,0 +1,379 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.projects import ( + rate_limit_list_rate_limits_params, + rate_limit_update_rate_limit_params, +) +from .....types.admin.organization.projects.project_rate_limit import ProjectRateLimit + +__all__ = ["RateLimits", "AsyncRateLimits"] + + +class RateLimits(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RateLimitsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RateLimitsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RateLimitsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RateLimitsWithStreamingResponse(self) + + def list_rate_limits( + self, + project_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ProjectRateLimit]: + """ + Returns the rate limits per model for a project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + beginning with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. The default is 100. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/rate_limits", project_id=project_id), + page=SyncConversationCursorPage[ProjectRateLimit], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + }, + rate_limit_list_rate_limits_params.RateLimitListRateLimitsParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectRateLimit, + ) + + def update_rate_limit( + self, + rate_limit_id: str, + *, + project_id: str, + batch_1_day_max_input_tokens: int | Omit = omit, + max_audio_megabytes_per_1_minute: int | Omit = omit, + max_images_per_1_minute: int | Omit = omit, + max_requests_per_1_day: int | Omit = omit, + max_requests_per_1_minute: int | Omit = omit, + max_tokens_per_1_minute: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectRateLimit: + """ + Updates a project rate limit. + + Args: + batch_1_day_max_input_tokens: The maximum batch input tokens per day. Only relevant for certain models. + + max_audio_megabytes_per_1_minute: The maximum audio megabytes per minute. Only relevant for certain models. + + max_images_per_1_minute: The maximum images per minute. Only relevant for certain models. + + max_requests_per_1_day: The maximum requests per day. Only relevant for certain models. + + max_requests_per_1_minute: The maximum requests per minute. + + max_tokens_per_1_minute: The maximum tokens per minute. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not rate_limit_id: + raise ValueError(f"Expected a non-empty value for `rate_limit_id` but received {rate_limit_id!r}") + return self._post( + path_template( + "/organization/projects/{project_id}/rate_limits/{rate_limit_id}", + project_id=project_id, + rate_limit_id=rate_limit_id, + ), + body=maybe_transform( + { + "batch_1_day_max_input_tokens": batch_1_day_max_input_tokens, + "max_audio_megabytes_per_1_minute": max_audio_megabytes_per_1_minute, + "max_images_per_1_minute": max_images_per_1_minute, + "max_requests_per_1_day": max_requests_per_1_day, + "max_requests_per_1_minute": max_requests_per_1_minute, + "max_tokens_per_1_minute": max_tokens_per_1_minute, + }, + rate_limit_update_rate_limit_params.RateLimitUpdateRateLimitParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectRateLimit, + ) + + +class AsyncRateLimits(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRateLimitsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRateLimitsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRateLimitsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRateLimitsWithStreamingResponse(self) + + def list_rate_limits( + self, + project_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ProjectRateLimit, AsyncConversationCursorPage[ProjectRateLimit]]: + """ + Returns the rate limits per model for a project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + beginning with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. The default is 100. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/rate_limits", project_id=project_id), + page=AsyncConversationCursorPage[ProjectRateLimit], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + }, + rate_limit_list_rate_limits_params.RateLimitListRateLimitsParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectRateLimit, + ) + + async def update_rate_limit( + self, + rate_limit_id: str, + *, + project_id: str, + batch_1_day_max_input_tokens: int | Omit = omit, + max_audio_megabytes_per_1_minute: int | Omit = omit, + max_images_per_1_minute: int | Omit = omit, + max_requests_per_1_day: int | Omit = omit, + max_requests_per_1_minute: int | Omit = omit, + max_tokens_per_1_minute: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectRateLimit: + """ + Updates a project rate limit. + + Args: + batch_1_day_max_input_tokens: The maximum batch input tokens per day. Only relevant for certain models. + + max_audio_megabytes_per_1_minute: The maximum audio megabytes per minute. Only relevant for certain models. + + max_images_per_1_minute: The maximum images per minute. Only relevant for certain models. + + max_requests_per_1_day: The maximum requests per day. Only relevant for certain models. + + max_requests_per_1_minute: The maximum requests per minute. + + max_tokens_per_1_minute: The maximum tokens per minute. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not rate_limit_id: + raise ValueError(f"Expected a non-empty value for `rate_limit_id` but received {rate_limit_id!r}") + return await self._post( + path_template( + "/organization/projects/{project_id}/rate_limits/{rate_limit_id}", + project_id=project_id, + rate_limit_id=rate_limit_id, + ), + body=await async_maybe_transform( + { + "batch_1_day_max_input_tokens": batch_1_day_max_input_tokens, + "max_audio_megabytes_per_1_minute": max_audio_megabytes_per_1_minute, + "max_images_per_1_minute": max_images_per_1_minute, + "max_requests_per_1_day": max_requests_per_1_day, + "max_requests_per_1_minute": max_requests_per_1_minute, + "max_tokens_per_1_minute": max_tokens_per_1_minute, + }, + rate_limit_update_rate_limit_params.RateLimitUpdateRateLimitParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectRateLimit, + ) + + +class RateLimitsWithRawResponse: + def __init__(self, rate_limits: RateLimits) -> None: + self._rate_limits = rate_limits + + self.list_rate_limits = _legacy_response.to_raw_response_wrapper( + rate_limits.list_rate_limits, + ) + self.update_rate_limit = _legacy_response.to_raw_response_wrapper( + rate_limits.update_rate_limit, + ) + + +class AsyncRateLimitsWithRawResponse: + def __init__(self, rate_limits: AsyncRateLimits) -> None: + self._rate_limits = rate_limits + + self.list_rate_limits = _legacy_response.async_to_raw_response_wrapper( + rate_limits.list_rate_limits, + ) + self.update_rate_limit = _legacy_response.async_to_raw_response_wrapper( + rate_limits.update_rate_limit, + ) + + +class RateLimitsWithStreamingResponse: + def __init__(self, rate_limits: RateLimits) -> None: + self._rate_limits = rate_limits + + self.list_rate_limits = to_streamed_response_wrapper( + rate_limits.list_rate_limits, + ) + self.update_rate_limit = to_streamed_response_wrapper( + rate_limits.update_rate_limit, + ) + + +class AsyncRateLimitsWithStreamingResponse: + def __init__(self, rate_limits: AsyncRateLimits) -> None: + self._rate_limits = rate_limits + + self.list_rate_limits = async_to_streamed_response_wrapper( + rate_limits.list_rate_limits, + ) + self.update_rate_limit = async_to_streamed_response_wrapper( + rate_limits.update_rate_limit, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/roles.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/roles.py new file mode 100644 index 0000000..bb93cfe --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/roles.py @@ -0,0 +1,644 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncNextCursorPage, AsyncNextCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.role import Role +from .....types.admin.organization.projects import role_list_params, role_create_params, role_update_params +from .....types.admin.organization.projects.role_delete_response import RoleDeleteResponse + +__all__ = ["Roles", "AsyncRoles"] + + +class Roles(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RolesWithStreamingResponse(self) + + def create( + self, + project_id: str, + *, + permissions: SequenceNotStr[str], + role_name: str, + description: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Creates a custom role for a project. + + Args: + permissions: Permissions to grant to the role. + + role_name: Unique name for the role. + + description: Optional description of the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/projects/{project_id}/roles", project_id=project_id), + body=maybe_transform( + { + "permissions": permissions, + "role_name": role_name, + "description": description, + }, + role_create_params.RoleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def retrieve( + self, + role_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Retrieves a project role. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def update( + self, + role_id: str, + *, + project_id: str, + description: Optional[str] | Omit = omit, + permissions: Optional[SequenceNotStr[str]] | Omit = omit, + role_name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Updates an existing project role. + + Args: + description: New description for the role. + + permissions: Updated set of permissions for the role. + + role_name: New name for the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._post( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + body=maybe_transform( + { + "description": description, + "permissions": permissions, + "role_name": role_name, + }, + role_update_params.RoleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncNextCursorPage[Role]: + """Lists the roles configured for a project. + + Args: + after: Cursor for pagination. + + Provide the value from the previous response's `next` + field to continue listing roles. + + limit: A limit on the number of roles to return. Defaults to 1000. + + order: Sort order for the returned roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/projects/{project_id}/roles", project_id=project_id), + page=SyncNextCursorPage[Role], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Role, + ) + + def delete( + self, + role_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Deletes a custom role from a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._delete( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class AsyncRoles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRolesWithStreamingResponse(self) + + async def create( + self, + project_id: str, + *, + permissions: SequenceNotStr[str], + role_name: str, + description: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Creates a custom role for a project. + + Args: + permissions: Permissions to grant to the role. + + role_name: Unique name for the role. + + description: Optional description of the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/projects/{project_id}/roles", project_id=project_id), + body=await async_maybe_transform( + { + "permissions": permissions, + "role_name": role_name, + "description": description, + }, + role_create_params.RoleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + async def retrieve( + self, + role_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Retrieves a project role. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + async def update( + self, + role_id: str, + *, + project_id: str, + description: Optional[str] | Omit = omit, + permissions: Optional[SequenceNotStr[str]] | Omit = omit, + role_name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Updates an existing project role. + + Args: + description: New description for the role. + + permissions: Updated set of permissions for the role. + + role_name: New name for the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._post( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + body=await async_maybe_transform( + { + "description": description, + "permissions": permissions, + "role_name": role_name, + }, + role_update_params.RoleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Role, AsyncNextCursorPage[Role]]: + """Lists the roles configured for a project. + + Args: + after: Cursor for pagination. + + Provide the value from the previous response's `next` + field to continue listing roles. + + limit: A limit on the number of roles to return. Defaults to 1000. + + order: Sort order for the returned roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/projects/{project_id}/roles", project_id=project_id), + page=AsyncNextCursorPage[Role], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Role, + ) + + async def delete( + self, + role_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Deletes a custom role from a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._delete( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class RolesWithRawResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = _legacy_response.to_raw_response_wrapper( + roles.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + roles.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithRawResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = _legacy_response.async_to_raw_response_wrapper( + roles.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + roles.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + roles.delete, + ) + + +class RolesWithStreamingResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = to_streamed_response_wrapper( + roles.create, + ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) + self.update = to_streamed_response_wrapper( + roles.update, + ) + self.list = to_streamed_response_wrapper( + roles.list, + ) + self.delete = to_streamed_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithStreamingResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = async_to_streamed_response_wrapper( + roles.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + roles.update, + ) + self.list = async_to_streamed_response_wrapper( + roles.list, + ) + self.delete = async_to_streamed_response_wrapper( + roles.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/__init__.py new file mode 100644 index 0000000..21287c8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .api_keys import ( + APIKeys as APIKeys, + AsyncAPIKeys as AsyncAPIKeys, + APIKeysWithRawResponse as APIKeysWithRawResponse, + AsyncAPIKeysWithRawResponse as AsyncAPIKeysWithRawResponse, + APIKeysWithStreamingResponse as APIKeysWithStreamingResponse, + AsyncAPIKeysWithStreamingResponse as AsyncAPIKeysWithStreamingResponse, + ) + from .service_accounts import ( + ServiceAccounts as ServiceAccounts, + AsyncServiceAccounts as AsyncServiceAccounts, + ServiceAccountsWithRawResponse as ServiceAccountsWithRawResponse, + AsyncServiceAccountsWithRawResponse as AsyncServiceAccountsWithRawResponse, + ServiceAccountsWithStreamingResponse as ServiceAccountsWithStreamingResponse, + AsyncServiceAccountsWithStreamingResponse as AsyncServiceAccountsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "APIKeys": (".api_keys", "APIKeys"), + "AsyncAPIKeys": (".api_keys", "AsyncAPIKeys"), + "APIKeysWithRawResponse": (".api_keys", "APIKeysWithRawResponse"), + "AsyncAPIKeysWithRawResponse": (".api_keys", "AsyncAPIKeysWithRawResponse"), + "APIKeysWithStreamingResponse": (".api_keys", "APIKeysWithStreamingResponse"), + "AsyncAPIKeysWithStreamingResponse": (".api_keys", "AsyncAPIKeysWithStreamingResponse"), + "ServiceAccounts": (".service_accounts", "ServiceAccounts"), + "AsyncServiceAccounts": (".service_accounts", "AsyncServiceAccounts"), + "ServiceAccountsWithRawResponse": (".service_accounts", "ServiceAccountsWithRawResponse"), + "AsyncServiceAccountsWithRawResponse": (".service_accounts", "AsyncServiceAccountsWithRawResponse"), + "ServiceAccountsWithStreamingResponse": (".service_accounts", "ServiceAccountsWithStreamingResponse"), + "AsyncServiceAccountsWithStreamingResponse": (".service_accounts", "AsyncServiceAccountsWithStreamingResponse"), + } + _SUBMODULES = { + "api_keys", + "service_accounts", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "APIKeys", + "AsyncAPIKeys", + "APIKeysWithRawResponse", + "AsyncAPIKeysWithRawResponse", + "APIKeysWithStreamingResponse", + "AsyncAPIKeysWithStreamingResponse", + "ServiceAccounts", + "AsyncServiceAccounts", + "ServiceAccountsWithRawResponse", + "AsyncServiceAccountsWithRawResponse", + "ServiceAccountsWithStreamingResponse", + "AsyncServiceAccountsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..8100f95 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/__pycache__/api_keys.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/__pycache__/api_keys.cpython-312.pyc new file mode 100644 index 0000000..b220bdc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/__pycache__/api_keys.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/__pycache__/service_accounts.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/__pycache__/service_accounts.cpython-312.pyc new file mode 100644 index 0000000..2672bfd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/__pycache__/service_accounts.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/api_keys.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/api_keys.py new file mode 100644 index 0000000..cedd32e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/api_keys.py @@ -0,0 +1,227 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx2 + +from ...... import _legacy_response +from ......_types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ......_utils import path_template, maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......_base_client import make_request_options +from ......types.admin.organization.projects.service_accounts import api_key_create_params +from ......types.admin.organization.projects.service_accounts.api_key_create_response import APIKeyCreateResponse + +__all__ = ["APIKeys", "AsyncAPIKeys"] + + +class APIKeys(SyncAPIResource): + @cached_property + def with_raw_response(self) -> APIKeysWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return APIKeysWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> APIKeysWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return APIKeysWithStreamingResponse(self) + + def create( + self, + service_account_id: str, + *, + project_id: str, + expires_in_seconds: Optional[int] | Omit = omit, + name: str | Omit = omit, + scopes: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> APIKeyCreateResponse: + """ + Creates an API key for a service account in the project. + + Args: + project_id: The ID of the project. + + service_account_id: The ID of the service account. + + expires_in_seconds: Number of seconds until the API key expires. + + name: API key name. + + scopes: API key scopes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return self._post( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}/api_keys", + project_id=project_id, + service_account_id=service_account_id, + ), + body=maybe_transform( + { + "expires_in_seconds": expires_in_seconds, + "name": name, + "scopes": scopes, + }, + api_key_create_params.APIKeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=APIKeyCreateResponse, + ) + + +class AsyncAPIKeys(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAPIKeysWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAPIKeysWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAPIKeysWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAPIKeysWithStreamingResponse(self) + + async def create( + self, + service_account_id: str, + *, + project_id: str, + expires_in_seconds: Optional[int] | Omit = omit, + name: str | Omit = omit, + scopes: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> APIKeyCreateResponse: + """ + Creates an API key for a service account in the project. + + Args: + project_id: The ID of the project. + + service_account_id: The ID of the service account. + + expires_in_seconds: Number of seconds until the API key expires. + + name: API key name. + + scopes: API key scopes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return await self._post( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}/api_keys", + project_id=project_id, + service_account_id=service_account_id, + ), + body=await async_maybe_transform( + { + "expires_in_seconds": expires_in_seconds, + "name": name, + "scopes": scopes, + }, + api_key_create_params.APIKeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=APIKeyCreateResponse, + ) + + +class APIKeysWithRawResponse: + def __init__(self, api_keys: APIKeys) -> None: + self._api_keys = api_keys + + self.create = _legacy_response.to_raw_response_wrapper( + api_keys.create, + ) + + +class AsyncAPIKeysWithRawResponse: + def __init__(self, api_keys: AsyncAPIKeys) -> None: + self._api_keys = api_keys + + self.create = _legacy_response.async_to_raw_response_wrapper( + api_keys.create, + ) + + +class APIKeysWithStreamingResponse: + def __init__(self, api_keys: APIKeys) -> None: + self._api_keys = api_keys + + self.create = to_streamed_response_wrapper( + api_keys.create, + ) + + +class AsyncAPIKeysWithStreamingResponse: + def __init__(self, api_keys: AsyncAPIKeys) -> None: + self._api_keys = api_keys + + self.create = async_to_streamed_response_wrapper( + api_keys.create, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/service_accounts.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/service_accounts.py new file mode 100644 index 0000000..be653b3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/service_accounts/service_accounts.py @@ -0,0 +1,711 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx2 + +from ...... import _legacy_response +from .api_keys import ( + APIKeys, + AsyncAPIKeys, + APIKeysWithRawResponse, + AsyncAPIKeysWithRawResponse, + APIKeysWithStreamingResponse, + AsyncAPIKeysWithStreamingResponse, +) +from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......_utils import path_template, maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ......_base_client import AsyncPaginator, make_request_options +from ......types.admin.organization.projects import ( + service_account_list_params, + service_account_create_params, + service_account_update_params, +) +from ......types.admin.organization.projects.project_service_account import ProjectServiceAccount +from ......types.admin.organization.projects.service_account_create_response import ServiceAccountCreateResponse +from ......types.admin.organization.projects.service_account_delete_response import ServiceAccountDeleteResponse + +__all__ = ["ServiceAccounts", "AsyncServiceAccounts"] + + +class ServiceAccounts(SyncAPIResource): + @cached_property + def api_keys(self) -> APIKeys: + return APIKeys(self._client) + + @cached_property + def with_raw_response(self) -> ServiceAccountsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ServiceAccountsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ServiceAccountsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ServiceAccountsWithStreamingResponse(self) + + def create( + self, + project_id: str, + *, + name: str, + create_service_account_only: Optional[bool] | Omit = omit, + expires_in_seconds: Optional[int] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ServiceAccountCreateResponse: + """Creates a new service account in the project. + + By default, this also returns an + unredacted API key for the service account. + + Args: + name: The name of the service account being created. + + create_service_account_only: Create the service account without default roles or an API key. + + expires_in_seconds: Number of seconds until the initial API key expires. If omitted or null, the key + does not expire unless the effective organization or project policy requires an + expiration. When a policy sets a maximum lifetime, this value must be provided + and must not exceed that limit. A non-null value cannot be used when + `create_service_account_only` is true. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/service_accounts", project_id=project_id), + body=maybe_transform( + { + "name": name, + "create_service_account_only": create_service_account_only, + "expires_in_seconds": expires_in_seconds, + }, + service_account_create_params.ServiceAccountCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ServiceAccountCreateResponse, + ) + + def retrieve( + self, + service_account_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectServiceAccount: + """ + Retrieves a service account in the project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return self._get( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectServiceAccount, + ) + + def update( + self, + service_account_id: str, + *, + project_id: str, + name: str | Omit = omit, + role: Literal["member", "owner"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectServiceAccount: + """ + Updates a service account in the project. + + Args: + name: The updated service account name. + + role: The updated service account role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return self._post( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + body=maybe_transform( + { + "name": name, + "role": role, + }, + service_account_update_params.ServiceAccountUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectServiceAccount, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ProjectServiceAccount]: + """ + Returns a list of service accounts in the project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/service_accounts", project_id=project_id), + page=SyncConversationCursorPage[ProjectServiceAccount], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + service_account_list_params.ServiceAccountListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectServiceAccount, + ) + + def delete( + self, + service_account_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ServiceAccountDeleteResponse: + """ + Deletes a service account from the project. + + Returns confirmation of service account deletion, or an error if the project is + archived (archived projects have no service accounts). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return self._delete( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ServiceAccountDeleteResponse, + ) + + +class AsyncServiceAccounts(AsyncAPIResource): + @cached_property + def api_keys(self) -> AsyncAPIKeys: + return AsyncAPIKeys(self._client) + + @cached_property + def with_raw_response(self) -> AsyncServiceAccountsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncServiceAccountsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncServiceAccountsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncServiceAccountsWithStreamingResponse(self) + + async def create( + self, + project_id: str, + *, + name: str, + create_service_account_only: Optional[bool] | Omit = omit, + expires_in_seconds: Optional[int] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ServiceAccountCreateResponse: + """Creates a new service account in the project. + + By default, this also returns an + unredacted API key for the service account. + + Args: + name: The name of the service account being created. + + create_service_account_only: Create the service account without default roles or an API key. + + expires_in_seconds: Number of seconds until the initial API key expires. If omitted or null, the key + does not expire unless the effective organization or project policy requires an + expiration. When a policy sets a maximum lifetime, this value must be provided + and must not exceed that limit. A non-null value cannot be used when + `create_service_account_only` is true. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/service_accounts", project_id=project_id), + body=await async_maybe_transform( + { + "name": name, + "create_service_account_only": create_service_account_only, + "expires_in_seconds": expires_in_seconds, + }, + service_account_create_params.ServiceAccountCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ServiceAccountCreateResponse, + ) + + async def retrieve( + self, + service_account_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectServiceAccount: + """ + Retrieves a service account in the project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return await self._get( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectServiceAccount, + ) + + async def update( + self, + service_account_id: str, + *, + project_id: str, + name: str | Omit = omit, + role: Literal["member", "owner"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectServiceAccount: + """ + Updates a service account in the project. + + Args: + name: The updated service account name. + + role: The updated service account role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return await self._post( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + body=await async_maybe_transform( + { + "name": name, + "role": role, + }, + service_account_update_params.ServiceAccountUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectServiceAccount, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ProjectServiceAccount, AsyncConversationCursorPage[ProjectServiceAccount]]: + """ + Returns a list of service accounts in the project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/service_accounts", project_id=project_id), + page=AsyncConversationCursorPage[ProjectServiceAccount], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + service_account_list_params.ServiceAccountListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectServiceAccount, + ) + + async def delete( + self, + service_account_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ServiceAccountDeleteResponse: + """ + Deletes a service account from the project. + + Returns confirmation of service account deletion, or an error if the project is + archived (archived projects have no service accounts). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return await self._delete( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ServiceAccountDeleteResponse, + ) + + +class ServiceAccountsWithRawResponse: + def __init__(self, service_accounts: ServiceAccounts) -> None: + self._service_accounts = service_accounts + + self.create = _legacy_response.to_raw_response_wrapper( + service_accounts.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + service_accounts.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + service_accounts.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + service_accounts.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + service_accounts.delete, + ) + + @cached_property + def api_keys(self) -> APIKeysWithRawResponse: + return APIKeysWithRawResponse(self._service_accounts.api_keys) + + +class AsyncServiceAccountsWithRawResponse: + def __init__(self, service_accounts: AsyncServiceAccounts) -> None: + self._service_accounts = service_accounts + + self.create = _legacy_response.async_to_raw_response_wrapper( + service_accounts.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + service_accounts.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + service_accounts.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + service_accounts.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + service_accounts.delete, + ) + + @cached_property + def api_keys(self) -> AsyncAPIKeysWithRawResponse: + return AsyncAPIKeysWithRawResponse(self._service_accounts.api_keys) + + +class ServiceAccountsWithStreamingResponse: + def __init__(self, service_accounts: ServiceAccounts) -> None: + self._service_accounts = service_accounts + + self.create = to_streamed_response_wrapper( + service_accounts.create, + ) + self.retrieve = to_streamed_response_wrapper( + service_accounts.retrieve, + ) + self.update = to_streamed_response_wrapper( + service_accounts.update, + ) + self.list = to_streamed_response_wrapper( + service_accounts.list, + ) + self.delete = to_streamed_response_wrapper( + service_accounts.delete, + ) + + @cached_property + def api_keys(self) -> APIKeysWithStreamingResponse: + return APIKeysWithStreamingResponse(self._service_accounts.api_keys) + + +class AsyncServiceAccountsWithStreamingResponse: + def __init__(self, service_accounts: AsyncServiceAccounts) -> None: + self._service_accounts = service_accounts + + self.create = async_to_streamed_response_wrapper( + service_accounts.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + service_accounts.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + service_accounts.update, + ) + self.list = async_to_streamed_response_wrapper( + service_accounts.list, + ) + self.delete = async_to_streamed_response_wrapper( + service_accounts.delete, + ) + + @cached_property + def api_keys(self) -> AsyncAPIKeysWithStreamingResponse: + return AsyncAPIKeysWithStreamingResponse(self._service_accounts.api_keys) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/spend_alerts.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/spend_alerts.py new file mode 100644 index 0000000..3fab082 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/spend_alerts.py @@ -0,0 +1,685 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.projects import ( + spend_alert_list_params, + spend_alert_create_params, + spend_alert_update_params, +) +from .....types.admin.organization.projects.project_spend_alert import ProjectSpendAlert +from .....types.admin.organization.projects.project_spend_alert_deleted import ProjectSpendAlertDeleted + +__all__ = ["SpendAlerts", "AsyncSpendAlerts"] + + +class SpendAlerts(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SpendAlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SpendAlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SpendAlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SpendAlertsWithStreamingResponse(self) + + def create( + self, + project_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_create_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Creates a project spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/spend_alerts", project_id=project_id), + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_create_params.SpendAlertCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + def retrieve( + self, + alert_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Retrieves a project spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._get( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + def update( + self, + alert_id: str, + *, + project_id: str, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_update_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Updates a project spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._post( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_update_params.SpendAlertUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ProjectSpendAlert]: + """Lists project spend alerts. + + Args: + after: Cursor for pagination. + + Provide the ID of the last spend alert from the previous + response to fetch the next page. + + before: Cursor for pagination. Provide the ID of the first spend alert from the previous + response to fetch the previous page. + + limit: A limit on the number of spend alerts to return. Defaults to 20. + + order: Sort order for the returned spend alerts. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/spend_alerts", project_id=project_id), + page=SyncConversationCursorPage[ProjectSpendAlert], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + spend_alert_list_params.SpendAlertListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectSpendAlert, + ) + + def delete( + self, + alert_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlertDeleted: + """ + Deletes a project spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._delete( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlertDeleted, + ) + + +class AsyncSpendAlerts(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSpendAlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSpendAlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSpendAlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSpendAlertsWithStreamingResponse(self) + + async def create( + self, + project_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_create_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Creates a project spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/spend_alerts", project_id=project_id), + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_create_params.SpendAlertCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + async def retrieve( + self, + alert_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Retrieves a project spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._get( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + async def update( + self, + alert_id: str, + *, + project_id: str, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_update_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Updates a project spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._post( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_update_params.SpendAlertUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ProjectSpendAlert, AsyncConversationCursorPage[ProjectSpendAlert]]: + """Lists project spend alerts. + + Args: + after: Cursor for pagination. + + Provide the ID of the last spend alert from the previous + response to fetch the next page. + + before: Cursor for pagination. Provide the ID of the first spend alert from the previous + response to fetch the previous page. + + limit: A limit on the number of spend alerts to return. Defaults to 20. + + order: Sort order for the returned spend alerts. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/spend_alerts", project_id=project_id), + page=AsyncConversationCursorPage[ProjectSpendAlert], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + spend_alert_list_params.SpendAlertListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectSpendAlert, + ) + + async def delete( + self, + alert_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlertDeleted: + """ + Deletes a project spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._delete( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlertDeleted, + ) + + +class SpendAlertsWithRawResponse: + def __init__(self, spend_alerts: SpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = _legacy_response.to_raw_response_wrapper( + spend_alerts.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + spend_alerts.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + spend_alerts.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + spend_alerts.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + spend_alerts.delete, + ) + + +class AsyncSpendAlertsWithRawResponse: + def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.delete, + ) + + +class SpendAlertsWithStreamingResponse: + def __init__(self, spend_alerts: SpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = to_streamed_response_wrapper( + spend_alerts.create, + ) + self.retrieve = to_streamed_response_wrapper( + spend_alerts.retrieve, + ) + self.update = to_streamed_response_wrapper( + spend_alerts.update, + ) + self.list = to_streamed_response_wrapper( + spend_alerts.list, + ) + self.delete = to_streamed_response_wrapper( + spend_alerts.delete, + ) + + +class AsyncSpendAlertsWithStreamingResponse: + def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = async_to_streamed_response_wrapper( + spend_alerts.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + spend_alerts.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + spend_alerts.update, + ) + self.list = async_to_streamed_response_wrapper( + spend_alerts.list, + ) + self.delete = async_to_streamed_response_wrapper( + spend_alerts.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/spend_limit.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/spend_limit.py new file mode 100644 index 0000000..8eec885 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/spend_limit.py @@ -0,0 +1,380 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Query, Headers, NotGiven, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._base_client import make_request_options +from .....types.admin.organization.projects import spend_limit_update_params +from .....types.admin.organization.projects.project_spend_limit import ProjectSpendLimit +from .....types.admin.organization.projects.project_spend_limit_deleted import ProjectSpendLimitDeleted + +__all__ = ["SpendLimit", "AsyncSpendLimit"] + + +class SpendLimit(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SpendLimitWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SpendLimitWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SpendLimitWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SpendLimitWithStreamingResponse(self) + + def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendLimit: + """ + Get a project's hard spend limit. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get( + path_template("/organization/projects/{project_id}/spend_limit", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendLimit, + ) + + def update( + self, + project_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendLimit: + """ + Create or replace a project's hard spend limit. + + Args: + currency: The currency for the threshold amount. Currently, only `USD` is supported. + + interval: The time interval for evaluating spend against the threshold. Currently, only + `month` is supported. + + threshold_amount: The hard spend limit amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/spend_limit", project_id=project_id), + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "threshold_amount": threshold_amount, + }, + spend_limit_update_params.SpendLimitUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendLimit, + ) + + def delete( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendLimitDeleted: + """ + Delete a project's hard spend limit. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._delete( + path_template("/organization/projects/{project_id}/spend_limit", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendLimitDeleted, + ) + + +class AsyncSpendLimit(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSpendLimitWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSpendLimitWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSpendLimitWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSpendLimitWithStreamingResponse(self) + + async def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendLimit: + """ + Get a project's hard spend limit. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._get( + path_template("/organization/projects/{project_id}/spend_limit", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendLimit, + ) + + async def update( + self, + project_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendLimit: + """ + Create or replace a project's hard spend limit. + + Args: + currency: The currency for the threshold amount. Currently, only `USD` is supported. + + interval: The time interval for evaluating spend against the threshold. Currently, only + `month` is supported. + + threshold_amount: The hard spend limit amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/spend_limit", project_id=project_id), + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "threshold_amount": threshold_amount, + }, + spend_limit_update_params.SpendLimitUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendLimit, + ) + + async def delete( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendLimitDeleted: + """ + Delete a project's hard spend limit. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._delete( + path_template("/organization/projects/{project_id}/spend_limit", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendLimitDeleted, + ) + + +class SpendLimitWithRawResponse: + def __init__(self, spend_limit: SpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = _legacy_response.to_raw_response_wrapper( + spend_limit.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + spend_limit.update, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + spend_limit.delete, + ) + + +class AsyncSpendLimitWithRawResponse: + def __init__(self, spend_limit: AsyncSpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + spend_limit.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + spend_limit.update, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + spend_limit.delete, + ) + + +class SpendLimitWithStreamingResponse: + def __init__(self, spend_limit: SpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = to_streamed_response_wrapper( + spend_limit.retrieve, + ) + self.update = to_streamed_response_wrapper( + spend_limit.update, + ) + self.delete = to_streamed_response_wrapper( + spend_limit.delete, + ) + + +class AsyncSpendLimitWithStreamingResponse: + def __init__(self, spend_limit: AsyncSpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = async_to_streamed_response_wrapper( + spend_limit.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + spend_limit.update, + ) + self.delete = async_to_streamed_response_wrapper( + spend_limit.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/__init__.py new file mode 100644 index 0000000..386f5ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .roles import ( + Roles as Roles, + AsyncRoles as AsyncRoles, + RolesWithRawResponse as RolesWithRawResponse, + AsyncRolesWithRawResponse as AsyncRolesWithRawResponse, + RolesWithStreamingResponse as RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse as AsyncRolesWithStreamingResponse, + ) + from .users import ( + Users as Users, + AsyncUsers as AsyncUsers, + UsersWithRawResponse as UsersWithRawResponse, + AsyncUsersWithRawResponse as AsyncUsersWithRawResponse, + UsersWithStreamingResponse as UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse as AsyncUsersWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Roles": (".roles", "Roles"), + "AsyncRoles": (".roles", "AsyncRoles"), + "RolesWithRawResponse": (".roles", "RolesWithRawResponse"), + "AsyncRolesWithRawResponse": (".roles", "AsyncRolesWithRawResponse"), + "RolesWithStreamingResponse": (".roles", "RolesWithStreamingResponse"), + "AsyncRolesWithStreamingResponse": (".roles", "AsyncRolesWithStreamingResponse"), + "Users": (".users", "Users"), + "AsyncUsers": (".users", "AsyncUsers"), + "UsersWithRawResponse": (".users", "UsersWithRawResponse"), + "AsyncUsersWithRawResponse": (".users", "AsyncUsersWithRawResponse"), + "UsersWithStreamingResponse": (".users", "UsersWithStreamingResponse"), + "AsyncUsersWithStreamingResponse": (".users", "AsyncUsersWithStreamingResponse"), + } + _SUBMODULES = { + "roles", + "users", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Roles", + "AsyncRoles", + "RolesWithRawResponse", + "AsyncRolesWithRawResponse", + "RolesWithStreamingResponse", + "AsyncRolesWithStreamingResponse", + "Users", + "AsyncUsers", + "UsersWithRawResponse", + "AsyncUsersWithRawResponse", + "UsersWithStreamingResponse", + "AsyncUsersWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..be48f49 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/__pycache__/roles.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/__pycache__/roles.cpython-312.pyc new file mode 100644 index 0000000..b720ee3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/__pycache__/roles.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/__pycache__/users.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/__pycache__/users.cpython-312.pyc new file mode 100644 index 0000000..5509a2e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/__pycache__/users.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/roles.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/roles.py new file mode 100644 index 0000000..d7ede48 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/roles.py @@ -0,0 +1,535 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ...... import _legacy_response +from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......_utils import path_template, maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......pagination import SyncNextCursorPage, AsyncNextCursorPage +from ......_base_client import AsyncPaginator, make_request_options +from ......types.admin.organization.projects.users import role_list_params, role_create_params +from ......types.admin.organization.projects.users.role_list_response import RoleListResponse +from ......types.admin.organization.projects.users.role_create_response import RoleCreateResponse +from ......types.admin.organization.projects.users.role_delete_response import RoleDeleteResponse +from ......types.admin.organization.projects.users.role_retrieve_response import RoleRetrieveResponse + +__all__ = ["Roles", "AsyncRoles"] + + +class Roles(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RolesWithStreamingResponse(self) + + def create( + self, + user_id: str, + *, + project_id: str, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns a project role to a user within a project. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._post( + path_template("/projects/{project_id}/users/{user_id}/roles", project_id=project_id, user_id=user_id), + body=maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + def retrieve( + self, + role_id: str, + *, + project_id: str, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves a project role assigned to a user. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template( + "/projects/{project_id}/users/{user_id}/roles/{role_id}", + project_id=project_id, + user_id=user_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + + def list( + self, + user_id: str, + *, + project_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncNextCursorPage[RoleListResponse]: + """ + Lists the project roles assigned to a user within a project. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing project roles. + + limit: A limit on the number of project role assignments to return. + + order: Sort order for the returned project roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get_api_list( + path_template("/projects/{project_id}/users/{user_id}/roles", project_id=project_id, user_id=user_id), + page=SyncNextCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + def delete( + self, + role_id: str, + *, + project_id: str, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns a project role from a user within a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._delete( + path_template( + "/projects/{project_id}/users/{user_id}/roles/{role_id}", + project_id=project_id, + user_id=user_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class AsyncRoles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRolesWithStreamingResponse(self) + + async def create( + self, + user_id: str, + *, + project_id: str, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns a project role to a user within a project. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._post( + path_template("/projects/{project_id}/users/{user_id}/roles", project_id=project_id, user_id=user_id), + body=await async_maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + async def retrieve( + self, + role_id: str, + *, + project_id: str, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves a project role assigned to a user. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template( + "/projects/{project_id}/users/{user_id}/roles/{role_id}", + project_id=project_id, + user_id=user_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + + def list( + self, + user_id: str, + *, + project_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[RoleListResponse, AsyncNextCursorPage[RoleListResponse]]: + """ + Lists the project roles assigned to a user within a project. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing project roles. + + limit: A limit on the number of project role assignments to return. + + order: Sort order for the returned project roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get_api_list( + path_template("/projects/{project_id}/users/{user_id}/roles", project_id=project_id, user_id=user_id), + page=AsyncNextCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + async def delete( + self, + role_id: str, + *, + project_id: str, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns a project role from a user within a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._delete( + path_template( + "/projects/{project_id}/users/{user_id}/roles/{role_id}", + project_id=project_id, + user_id=user_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class RolesWithRawResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = _legacy_response.to_raw_response_wrapper( + roles.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithRawResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = _legacy_response.async_to_raw_response_wrapper( + roles.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + roles.delete, + ) + + +class RolesWithStreamingResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = to_streamed_response_wrapper( + roles.create, + ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) + self.list = to_streamed_response_wrapper( + roles.list, + ) + self.delete = to_streamed_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithStreamingResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = async_to_streamed_response_wrapper( + roles.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + roles.list, + ) + self.delete = async_to_streamed_response_wrapper( + roles.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/users.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/users.py new file mode 100644 index 0000000..e714b86 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/projects/users/users.py @@ -0,0 +1,667 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx2 + +from ...... import _legacy_response +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......_utils import path_template, maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ......_base_client import AsyncPaginator, make_request_options +from ......types.admin.organization.projects import user_list_params, user_create_params, user_update_params +from ......types.admin.organization.projects.project_user import ProjectUser +from ......types.admin.organization.projects.user_delete_response import UserDeleteResponse + +__all__ = ["Users", "AsyncUsers"] + + +class Users(SyncAPIResource): + @cached_property + def roles(self) -> Roles: + return Roles(self._client) + + @cached_property + def with_raw_response(self) -> UsersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return UsersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UsersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return UsersWithStreamingResponse(self) + + def create( + self, + project_id: str, + *, + role: str, + email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectUser: + """Adds a user to the project. + + Users must already be members of the organization to + be added to a project. + + Args: + role: `owner` or `member` + + email: Email of the user to add. + + user_id: The ID of the user. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/users", project_id=project_id), + body=maybe_transform( + { + "role": role, + "email": email, + "user_id": user_id, + }, + user_create_params.UserCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectUser, + ) + + def retrieve( + self, + user_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectUser: + """ + Retrieves a user in the project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get( + path_template( + "/organization/projects/{project_id}/users/{user_id}", project_id=project_id, user_id=user_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectUser, + ) + + def update( + self, + user_id: str, + *, + project_id: str, + role: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectUser: + """ + Modifies a user's role in the project. + + Args: + role: `owner` or `member` + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._post( + path_template( + "/organization/projects/{project_id}/users/{user_id}", project_id=project_id, user_id=user_id + ), + body=maybe_transform({"role": role}, user_update_params.UserUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectUser, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ProjectUser]: + """ + Returns a list of users in the project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/users", project_id=project_id), + page=SyncConversationCursorPage[ProjectUser], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + user_list_params.UserListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectUser, + ) + + def delete( + self, + user_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UserDeleteResponse: + """ + Deletes a user from the project. + + Returns confirmation of project user deletion, or an error if the project is + archived (archived projects have no users). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._delete( + path_template( + "/organization/projects/{project_id}/users/{user_id}", project_id=project_id, user_id=user_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserDeleteResponse, + ) + + +class AsyncUsers(AsyncAPIResource): + @cached_property + def roles(self) -> AsyncRoles: + return AsyncRoles(self._client) + + @cached_property + def with_raw_response(self) -> AsyncUsersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncUsersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUsersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncUsersWithStreamingResponse(self) + + async def create( + self, + project_id: str, + *, + role: str, + email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectUser: + """Adds a user to the project. + + Users must already be members of the organization to + be added to a project. + + Args: + role: `owner` or `member` + + email: Email of the user to add. + + user_id: The ID of the user. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/users", project_id=project_id), + body=await async_maybe_transform( + { + "role": role, + "email": email, + "user_id": user_id, + }, + user_create_params.UserCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectUser, + ) + + async def retrieve( + self, + user_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectUser: + """ + Retrieves a user in the project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._get( + path_template( + "/organization/projects/{project_id}/users/{user_id}", project_id=project_id, user_id=user_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectUser, + ) + + async def update( + self, + user_id: str, + *, + project_id: str, + role: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ProjectUser: + """ + Modifies a user's role in the project. + + Args: + role: `owner` or `member` + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._post( + path_template( + "/organization/projects/{project_id}/users/{user_id}", project_id=project_id, user_id=user_id + ), + body=await async_maybe_transform({"role": role}, user_update_params.UserUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectUser, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ProjectUser, AsyncConversationCursorPage[ProjectUser]]: + """ + Returns a list of users in the project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/users", project_id=project_id), + page=AsyncConversationCursorPage[ProjectUser], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + user_list_params.UserListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectUser, + ) + + async def delete( + self, + user_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UserDeleteResponse: + """ + Deletes a user from the project. + + Returns confirmation of project user deletion, or an error if the project is + archived (archived projects have no users). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._delete( + path_template( + "/organization/projects/{project_id}/users/{user_id}", project_id=project_id, user_id=user_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserDeleteResponse, + ) + + +class UsersWithRawResponse: + def __init__(self, users: Users) -> None: + self._users = users + + self.create = _legacy_response.to_raw_response_wrapper( + users.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + users.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + users.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + users.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> RolesWithRawResponse: + return RolesWithRawResponse(self._users.roles) + + +class AsyncUsersWithRawResponse: + def __init__(self, users: AsyncUsers) -> None: + self._users = users + + self.create = _legacy_response.async_to_raw_response_wrapper( + users.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + users.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + users.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + users.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> AsyncRolesWithRawResponse: + return AsyncRolesWithRawResponse(self._users.roles) + + +class UsersWithStreamingResponse: + def __init__(self, users: Users) -> None: + self._users = users + + self.create = to_streamed_response_wrapper( + users.create, + ) + self.retrieve = to_streamed_response_wrapper( + users.retrieve, + ) + self.update = to_streamed_response_wrapper( + users.update, + ) + self.list = to_streamed_response_wrapper( + users.list, + ) + self.delete = to_streamed_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> RolesWithStreamingResponse: + return RolesWithStreamingResponse(self._users.roles) + + +class AsyncUsersWithStreamingResponse: + def __init__(self, users: AsyncUsers) -> None: + self._users = users + + self.create = async_to_streamed_response_wrapper( + users.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + users.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + users.update, + ) + self.list = async_to_streamed_response_wrapper( + users.list, + ) + self.delete = async_to_streamed_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> AsyncRolesWithStreamingResponse: + return AsyncRolesWithStreamingResponse(self._users.roles) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/roles.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/roles.py new file mode 100644 index 0000000..5ecd2f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/roles.py @@ -0,0 +1,612 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncNextCursorPage, AsyncNextCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import role_list_params, role_create_params, role_update_params +from ....types.admin.organization.role import Role +from ....types.admin.organization.role_delete_response import RoleDeleteResponse + +__all__ = ["Roles", "AsyncRoles"] + + +class Roles(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RolesWithStreamingResponse(self) + + def create( + self, + *, + permissions: SequenceNotStr[str], + role_name: str, + description: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Creates a custom role for the organization. + + Args: + permissions: Permissions to grant to the role. + + role_name: Unique name for the role. + + description: Optional description of the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/roles", + body=maybe_transform( + { + "permissions": permissions, + "role_name": role_name, + "description": description, + }, + role_create_params.RoleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def retrieve( + self, + role_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Retrieves an organization role. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template("/organization/roles/{role_id}", role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def update( + self, + role_id: str, + *, + description: Optional[str] | Omit = omit, + permissions: Optional[SequenceNotStr[str]] | Omit = omit, + role_name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Updates an existing organization role. + + Args: + description: New description for the role. + + permissions: Updated set of permissions for the role. + + role_name: New name for the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._post( + path_template("/organization/roles/{role_id}", role_id=role_id), + body=maybe_transform( + { + "description": description, + "permissions": permissions, + "role_name": role_name, + }, + role_update_params.RoleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncNextCursorPage[Role]: + """ + Lists the roles configured for the organization. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing roles. + + limit: A limit on the number of roles to return. Defaults to 1000. + + order: Sort order for the returned roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/roles", + page=SyncNextCursorPage[Role], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Role, + ) + + def delete( + self, + role_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Deletes a custom role from the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._delete( + path_template("/organization/roles/{role_id}", role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class AsyncRoles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRolesWithStreamingResponse(self) + + async def create( + self, + *, + permissions: SequenceNotStr[str], + role_name: str, + description: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Creates a custom role for the organization. + + Args: + permissions: Permissions to grant to the role. + + role_name: Unique name for the role. + + description: Optional description of the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/roles", + body=await async_maybe_transform( + { + "permissions": permissions, + "role_name": role_name, + "description": description, + }, + role_create_params.RoleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + async def retrieve( + self, + role_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Retrieves an organization role. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template("/organization/roles/{role_id}", role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + async def update( + self, + role_id: str, + *, + description: Optional[str] | Omit = omit, + permissions: Optional[SequenceNotStr[str]] | Omit = omit, + role_name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Updates an existing organization role. + + Args: + description: New description for the role. + + permissions: Updated set of permissions for the role. + + role_name: New name for the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._post( + path_template("/organization/roles/{role_id}", role_id=role_id), + body=await async_maybe_transform( + { + "description": description, + "permissions": permissions, + "role_name": role_name, + }, + role_update_params.RoleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Role, AsyncNextCursorPage[Role]]: + """ + Lists the roles configured for the organization. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing roles. + + limit: A limit on the number of roles to return. Defaults to 1000. + + order: Sort order for the returned roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/roles", + page=AsyncNextCursorPage[Role], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Role, + ) + + async def delete( + self, + role_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Deletes a custom role from the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._delete( + path_template("/organization/roles/{role_id}", role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class RolesWithRawResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = _legacy_response.to_raw_response_wrapper( + roles.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + roles.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithRawResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = _legacy_response.async_to_raw_response_wrapper( + roles.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + roles.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + roles.delete, + ) + + +class RolesWithStreamingResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = to_streamed_response_wrapper( + roles.create, + ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) + self.update = to_streamed_response_wrapper( + roles.update, + ) + self.list = to_streamed_response_wrapper( + roles.list, + ) + self.delete = to_streamed_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithStreamingResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = async_to_streamed_response_wrapper( + roles.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + roles.update, + ) + self.list = async_to_streamed_response_wrapper( + roles.list, + ) + self.delete = async_to_streamed_response_wrapper( + roles.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/spend_alerts.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/spend_alerts.py new file mode 100644 index 0000000..8b60183 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/spend_alerts.py @@ -0,0 +1,639 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import spend_alert_list_params, spend_alert_create_params, spend_alert_update_params +from ....types.admin.organization.organization_spend_alert import OrganizationSpendAlert +from ....types.admin.organization.organization_spend_alert_deleted import OrganizationSpendAlertDeleted + +__all__ = ["SpendAlerts", "AsyncSpendAlerts"] + + +class SpendAlerts(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SpendAlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SpendAlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SpendAlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SpendAlertsWithStreamingResponse(self) + + def create( + self, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_create_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Creates an organization spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/spend_alerts", + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_create_params.SpendAlertCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + def retrieve( + self, + alert_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Retrieves an organization spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._get( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + def update( + self, + alert_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_update_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Updates an organization spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._post( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_update_params.SpendAlertUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[OrganizationSpendAlert]: + """Lists organization spend alerts. + + Args: + after: Cursor for pagination. + + Provide the ID of the last spend alert from the previous + response to fetch the next page. + + before: Cursor for pagination. Provide the ID of the first spend alert from the previous + response to fetch the previous page. + + limit: A limit on the number of spend alerts to return. Defaults to 20. + + order: Sort order for the returned spend alerts. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/spend_alerts", + page=SyncConversationCursorPage[OrganizationSpendAlert], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + spend_alert_list_params.SpendAlertListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationSpendAlert, + ) + + def delete( + self, + alert_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlertDeleted: + """ + Deletes an organization spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._delete( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlertDeleted, + ) + + +class AsyncSpendAlerts(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSpendAlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSpendAlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSpendAlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSpendAlertsWithStreamingResponse(self) + + async def create( + self, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_create_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Creates an organization spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/spend_alerts", + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_create_params.SpendAlertCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + async def retrieve( + self, + alert_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Retrieves an organization spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._get( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + async def update( + self, + alert_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_update_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Updates an organization spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._post( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_update_params.SpendAlertUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[OrganizationSpendAlert, AsyncConversationCursorPage[OrganizationSpendAlert]]: + """Lists organization spend alerts. + + Args: + after: Cursor for pagination. + + Provide the ID of the last spend alert from the previous + response to fetch the next page. + + before: Cursor for pagination. Provide the ID of the first spend alert from the previous + response to fetch the previous page. + + limit: A limit on the number of spend alerts to return. Defaults to 20. + + order: Sort order for the returned spend alerts. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/spend_alerts", + page=AsyncConversationCursorPage[OrganizationSpendAlert], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + spend_alert_list_params.SpendAlertListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationSpendAlert, + ) + + async def delete( + self, + alert_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlertDeleted: + """ + Deletes an organization spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._delete( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlertDeleted, + ) + + +class SpendAlertsWithRawResponse: + def __init__(self, spend_alerts: SpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = _legacy_response.to_raw_response_wrapper( + spend_alerts.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + spend_alerts.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + spend_alerts.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + spend_alerts.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + spend_alerts.delete, + ) + + +class AsyncSpendAlertsWithRawResponse: + def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.delete, + ) + + +class SpendAlertsWithStreamingResponse: + def __init__(self, spend_alerts: SpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = to_streamed_response_wrapper( + spend_alerts.create, + ) + self.retrieve = to_streamed_response_wrapper( + spend_alerts.retrieve, + ) + self.update = to_streamed_response_wrapper( + spend_alerts.update, + ) + self.list = to_streamed_response_wrapper( + spend_alerts.list, + ) + self.delete = to_streamed_response_wrapper( + spend_alerts.delete, + ) + + +class AsyncSpendAlertsWithStreamingResponse: + def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = async_to_streamed_response_wrapper( + spend_alerts.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + spend_alerts.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + spend_alerts.update, + ) + self.list = async_to_streamed_response_wrapper( + spend_alerts.list, + ) + self.delete = async_to_streamed_response_wrapper( + spend_alerts.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/spend_limit.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/spend_limit.py new file mode 100644 index 0000000..fd79775 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/spend_limit.py @@ -0,0 +1,318 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.admin.organization import spend_limit_update_params +from ....types.admin.organization.organization_spend_limit import OrganizationSpendLimit +from ....types.admin.organization.organization_spend_limit_deleted import OrganizationSpendLimitDeleted + +__all__ = ["SpendLimit", "AsyncSpendLimit"] + + +class SpendLimit(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SpendLimitWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SpendLimitWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SpendLimitWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SpendLimitWithStreamingResponse(self) + + def retrieve( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendLimit: + """Get the organization's hard spend limit.""" + return self._get( + "/organization/spend_limit", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendLimit, + ) + + def update( + self, + *, + currency: Literal["USD"], + interval: Literal["month"], + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendLimit: + """ + Create or replace the organization's hard spend limit. + + Args: + currency: The currency for the threshold amount. Currently, only `USD` is supported. + + interval: The time interval for evaluating spend against the threshold. Currently, only + `month` is supported. + + threshold_amount: The hard spend limit amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/spend_limit", + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "threshold_amount": threshold_amount, + }, + spend_limit_update_params.SpendLimitUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendLimit, + ) + + def delete( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendLimitDeleted: + """Delete the organization's hard spend limit.""" + return self._delete( + "/organization/spend_limit", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendLimitDeleted, + ) + + +class AsyncSpendLimit(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSpendLimitWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSpendLimitWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSpendLimitWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSpendLimitWithStreamingResponse(self) + + async def retrieve( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendLimit: + """Get the organization's hard spend limit.""" + return await self._get( + "/organization/spend_limit", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendLimit, + ) + + async def update( + self, + *, + currency: Literal["USD"], + interval: Literal["month"], + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendLimit: + """ + Create or replace the organization's hard spend limit. + + Args: + currency: The currency for the threshold amount. Currently, only `USD` is supported. + + interval: The time interval for evaluating spend against the threshold. Currently, only + `month` is supported. + + threshold_amount: The hard spend limit amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/spend_limit", + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "threshold_amount": threshold_amount, + }, + spend_limit_update_params.SpendLimitUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendLimit, + ) + + async def delete( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendLimitDeleted: + """Delete the organization's hard spend limit.""" + return await self._delete( + "/organization/spend_limit", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendLimitDeleted, + ) + + +class SpendLimitWithRawResponse: + def __init__(self, spend_limit: SpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = _legacy_response.to_raw_response_wrapper( + spend_limit.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + spend_limit.update, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + spend_limit.delete, + ) + + +class AsyncSpendLimitWithRawResponse: + def __init__(self, spend_limit: AsyncSpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + spend_limit.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + spend_limit.update, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + spend_limit.delete, + ) + + +class SpendLimitWithStreamingResponse: + def __init__(self, spend_limit: SpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = to_streamed_response_wrapper( + spend_limit.retrieve, + ) + self.update = to_streamed_response_wrapper( + spend_limit.update, + ) + self.delete = to_streamed_response_wrapper( + spend_limit.delete, + ) + + +class AsyncSpendLimitWithStreamingResponse: + def __init__(self, spend_limit: AsyncSpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = async_to_streamed_response_wrapper( + spend_limit.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + spend_limit.update, + ) + self.delete = async_to_streamed_response_wrapper( + spend_limit.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/usage.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/usage.py new file mode 100644 index 0000000..21d9a42 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/usage.py @@ -0,0 +1,2118 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.admin.organization import ( + usage_costs_params, + usage_images_params, + usage_embeddings_params, + usage_completions_params, + usage_moderations_params, + usage_vector_stores_params, + usage_audio_speeches_params, + usage_web_search_calls_params, + usage_file_search_calls_params, + usage_audio_transcriptions_params, + usage_code_interpreter_sessions_params, +) +from ....types.admin.organization.usage_costs_response import UsageCostsResponse +from ....types.admin.organization.usage_images_response import UsageImagesResponse +from ....types.admin.organization.usage_embeddings_response import UsageEmbeddingsResponse +from ....types.admin.organization.usage_completions_response import UsageCompletionsResponse +from ....types.admin.organization.usage_moderations_response import UsageModerationsResponse +from ....types.admin.organization.usage_vector_stores_response import UsageVectorStoresResponse +from ....types.admin.organization.usage_audio_speeches_response import UsageAudioSpeechesResponse +from ....types.admin.organization.usage_web_search_calls_response import UsageWebSearchCallsResponse +from ....types.admin.organization.usage_file_search_calls_response import UsageFileSearchCallsResponse +from ....types.admin.organization.usage_audio_transcriptions_response import UsageAudioTranscriptionsResponse +from ....types.admin.organization.usage_code_interpreter_sessions_response import UsageCodeInterpreterSessionsResponse + +__all__ = ["Usage", "AsyncUsage"] + + +class Usage(SyncAPIResource): + @cached_property + def with_raw_response(self) -> UsageWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return UsageWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UsageWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return UsageWithStreamingResponse(self) + + def audio_speeches( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageAudioSpeechesResponse: + """ + Get audio speeches usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/audio_speeches", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_audio_speeches_params.UsageAudioSpeechesParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageAudioSpeechesResponse, + ) + + def audio_transcriptions( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageAudioTranscriptionsResponse: + """ + Get audio transcriptions usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/audio_transcriptions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_audio_transcriptions_params.UsageAudioTranscriptionsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageAudioTranscriptionsResponse, + ) + + def code_interpreter_sessions( + self, + *, + start_time: int, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageCodeInterpreterSessionsResponse: + """ + Get code interpreter sessions usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/code_interpreter_sessions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + }, + usage_code_interpreter_sessions_params.UsageCodeInterpreterSessionsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageCodeInterpreterSessionsResponse, + ) + + def completions( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + batch: bool | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "batch", "service_tier"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageCompletionsResponse: + """ + Get completions usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + batch: If `true`, return batch jobs only. If `false`, return non-batch jobs only. By + default, return both. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `batch`, `service_tier` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/completions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "batch": batch, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_completions_params.UsageCompletionsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageCompletionsResponse, + ) + + def costs( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "line_item", "api_key_id"]] | Omit = omit, + limit: int | Omit = omit, + line_items: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageCostsResponse: + """ + Get costs details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only costs for these API keys. + + bucket_width: Width of each time bucket in response. Currently only `1d` is supported, default + to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the costs by the specified fields. Support fields include `project_id`, + `line_item`, `api_key_id` and any combination of them. + + limit: A limit on the number of buckets to be returned. Limit can range between 1 and + 180, and the default is 7. + + line_items: Return only costs for these exact line item names. Each value must match the + complete `line_item` value, for example `gpt-6-astra, input_tokens`. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only costs for these projects. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/costs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "line_items": line_items, + "page": page, + "project_ids": project_ids, + }, + usage_costs_params.UsageCostsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageCostsResponse, + ) + + def embeddings( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageEmbeddingsResponse: + """ + Get embeddings usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/embeddings", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_embeddings_params.UsageEmbeddingsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageEmbeddingsResponse, + ) + + def file_search_calls( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "vector_store_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + vector_store_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageFileSearchCallsResponse: + """ + Get file search calls usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `vector_store_id` or any combination of + them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + vector_store_ids: Return only usage for these vector stores. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/file_search_calls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + "vector_store_ids": vector_store_ids, + }, + usage_file_search_calls_params.UsageFileSearchCallsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageFileSearchCallsResponse, + ) + + def images( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "size", "source"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + sizes: List[Literal["256x256", "512x512", "1024x1024", "1792x1792", "1024x1792"]] | Omit = omit, + sources: List[Literal["image.generation", "image.edit", "image.variation"]] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageImagesResponse: + """ + Get images usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `size`, `source` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + sizes: Return only usages for these image sizes. Possible values are `256x256`, + `512x512`, `1024x1024`, `1792x1792`, `1024x1792` or any combination of them. + + sources: Return only usages for these sources. Possible values are `image.generation`, + `image.edit`, `image.variation` or any combination of them. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/images", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "sizes": sizes, + "sources": sources, + "user_ids": user_ids, + }, + usage_images_params.UsageImagesParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageImagesResponse, + ) + + def moderations( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageModerationsResponse: + """ + Get moderations usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/moderations", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_moderations_params.UsageModerationsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageModerationsResponse, + ) + + def vector_stores( + self, + *, + start_time: int, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageVectorStoresResponse: + """ + Get vector stores usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/vector_stores", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + }, + usage_vector_stores_params.UsageVectorStoresParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageVectorStoresResponse, + ) + + def web_search_calls( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + context_levels: List[Literal["low", "medium", "high"]] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "context_level"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageWebSearchCallsResponse: + """ + Get web search calls usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + context_levels: Return only web search usage for these context levels. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `context_level` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/web_search_calls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "context_levels": context_levels, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_web_search_calls_params.UsageWebSearchCallsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageWebSearchCallsResponse, + ) + + +class AsyncUsage(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncUsageWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncUsageWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUsageWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncUsageWithStreamingResponse(self) + + async def audio_speeches( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageAudioSpeechesResponse: + """ + Get audio speeches usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/audio_speeches", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_audio_speeches_params.UsageAudioSpeechesParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageAudioSpeechesResponse, + ) + + async def audio_transcriptions( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageAudioTranscriptionsResponse: + """ + Get audio transcriptions usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/audio_transcriptions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_audio_transcriptions_params.UsageAudioTranscriptionsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageAudioTranscriptionsResponse, + ) + + async def code_interpreter_sessions( + self, + *, + start_time: int, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageCodeInterpreterSessionsResponse: + """ + Get code interpreter sessions usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/code_interpreter_sessions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + }, + usage_code_interpreter_sessions_params.UsageCodeInterpreterSessionsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageCodeInterpreterSessionsResponse, + ) + + async def completions( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + batch: bool | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "batch", "service_tier"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageCompletionsResponse: + """ + Get completions usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + batch: If `true`, return batch jobs only. If `false`, return non-batch jobs only. By + default, return both. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `batch`, `service_tier` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/completions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "batch": batch, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_completions_params.UsageCompletionsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageCompletionsResponse, + ) + + async def costs( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "line_item", "api_key_id"]] | Omit = omit, + limit: int | Omit = omit, + line_items: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageCostsResponse: + """ + Get costs details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only costs for these API keys. + + bucket_width: Width of each time bucket in response. Currently only `1d` is supported, default + to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the costs by the specified fields. Support fields include `project_id`, + `line_item`, `api_key_id` and any combination of them. + + limit: A limit on the number of buckets to be returned. Limit can range between 1 and + 180, and the default is 7. + + line_items: Return only costs for these exact line item names. Each value must match the + complete `line_item` value, for example `gpt-6-astra, input_tokens`. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only costs for these projects. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/costs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "line_items": line_items, + "page": page, + "project_ids": project_ids, + }, + usage_costs_params.UsageCostsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageCostsResponse, + ) + + async def embeddings( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageEmbeddingsResponse: + """ + Get embeddings usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/embeddings", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_embeddings_params.UsageEmbeddingsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageEmbeddingsResponse, + ) + + async def file_search_calls( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "vector_store_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + vector_store_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageFileSearchCallsResponse: + """ + Get file search calls usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `vector_store_id` or any combination of + them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + vector_store_ids: Return only usage for these vector stores. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/file_search_calls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + "vector_store_ids": vector_store_ids, + }, + usage_file_search_calls_params.UsageFileSearchCallsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageFileSearchCallsResponse, + ) + + async def images( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "size", "source"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + sizes: List[Literal["256x256", "512x512", "1024x1024", "1792x1792", "1024x1792"]] | Omit = omit, + sources: List[Literal["image.generation", "image.edit", "image.variation"]] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageImagesResponse: + """ + Get images usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `size`, `source` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + sizes: Return only usages for these image sizes. Possible values are `256x256`, + `512x512`, `1024x1024`, `1792x1792`, `1024x1792` or any combination of them. + + sources: Return only usages for these sources. Possible values are `image.generation`, + `image.edit`, `image.variation` or any combination of them. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/images", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "sizes": sizes, + "sources": sources, + "user_ids": user_ids, + }, + usage_images_params.UsageImagesParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageImagesResponse, + ) + + async def moderations( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageModerationsResponse: + """ + Get moderations usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/moderations", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_moderations_params.UsageModerationsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageModerationsResponse, + ) + + async def vector_stores( + self, + *, + start_time: int, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageVectorStoresResponse: + """ + Get vector stores usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/vector_stores", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + }, + usage_vector_stores_params.UsageVectorStoresParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageVectorStoresResponse, + ) + + async def web_search_calls( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + context_levels: List[Literal["low", "medium", "high"]] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "context_level"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UsageWebSearchCallsResponse: + """ + Get web search calls usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + context_levels: Return only web search usage for these context levels. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `context_level` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/web_search_calls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "context_levels": context_levels, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_web_search_calls_params.UsageWebSearchCallsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageWebSearchCallsResponse, + ) + + +class UsageWithRawResponse: + def __init__(self, usage: Usage) -> None: + self._usage = usage + + self.audio_speeches = _legacy_response.to_raw_response_wrapper( + usage.audio_speeches, + ) + self.audio_transcriptions = _legacy_response.to_raw_response_wrapper( + usage.audio_transcriptions, + ) + self.code_interpreter_sessions = _legacy_response.to_raw_response_wrapper( + usage.code_interpreter_sessions, + ) + self.completions = _legacy_response.to_raw_response_wrapper( + usage.completions, + ) + self.costs = _legacy_response.to_raw_response_wrapper( + usage.costs, + ) + self.embeddings = _legacy_response.to_raw_response_wrapper( + usage.embeddings, + ) + self.file_search_calls = _legacy_response.to_raw_response_wrapper( + usage.file_search_calls, + ) + self.images = _legacy_response.to_raw_response_wrapper( + usage.images, + ) + self.moderations = _legacy_response.to_raw_response_wrapper( + usage.moderations, + ) + self.vector_stores = _legacy_response.to_raw_response_wrapper( + usage.vector_stores, + ) + self.web_search_calls = _legacy_response.to_raw_response_wrapper( + usage.web_search_calls, + ) + + +class AsyncUsageWithRawResponse: + def __init__(self, usage: AsyncUsage) -> None: + self._usage = usage + + self.audio_speeches = _legacy_response.async_to_raw_response_wrapper( + usage.audio_speeches, + ) + self.audio_transcriptions = _legacy_response.async_to_raw_response_wrapper( + usage.audio_transcriptions, + ) + self.code_interpreter_sessions = _legacy_response.async_to_raw_response_wrapper( + usage.code_interpreter_sessions, + ) + self.completions = _legacy_response.async_to_raw_response_wrapper( + usage.completions, + ) + self.costs = _legacy_response.async_to_raw_response_wrapper( + usage.costs, + ) + self.embeddings = _legacy_response.async_to_raw_response_wrapper( + usage.embeddings, + ) + self.file_search_calls = _legacy_response.async_to_raw_response_wrapper( + usage.file_search_calls, + ) + self.images = _legacy_response.async_to_raw_response_wrapper( + usage.images, + ) + self.moderations = _legacy_response.async_to_raw_response_wrapper( + usage.moderations, + ) + self.vector_stores = _legacy_response.async_to_raw_response_wrapper( + usage.vector_stores, + ) + self.web_search_calls = _legacy_response.async_to_raw_response_wrapper( + usage.web_search_calls, + ) + + +class UsageWithStreamingResponse: + def __init__(self, usage: Usage) -> None: + self._usage = usage + + self.audio_speeches = to_streamed_response_wrapper( + usage.audio_speeches, + ) + self.audio_transcriptions = to_streamed_response_wrapper( + usage.audio_transcriptions, + ) + self.code_interpreter_sessions = to_streamed_response_wrapper( + usage.code_interpreter_sessions, + ) + self.completions = to_streamed_response_wrapper( + usage.completions, + ) + self.costs = to_streamed_response_wrapper( + usage.costs, + ) + self.embeddings = to_streamed_response_wrapper( + usage.embeddings, + ) + self.file_search_calls = to_streamed_response_wrapper( + usage.file_search_calls, + ) + self.images = to_streamed_response_wrapper( + usage.images, + ) + self.moderations = to_streamed_response_wrapper( + usage.moderations, + ) + self.vector_stores = to_streamed_response_wrapper( + usage.vector_stores, + ) + self.web_search_calls = to_streamed_response_wrapper( + usage.web_search_calls, + ) + + +class AsyncUsageWithStreamingResponse: + def __init__(self, usage: AsyncUsage) -> None: + self._usage = usage + + self.audio_speeches = async_to_streamed_response_wrapper( + usage.audio_speeches, + ) + self.audio_transcriptions = async_to_streamed_response_wrapper( + usage.audio_transcriptions, + ) + self.code_interpreter_sessions = async_to_streamed_response_wrapper( + usage.code_interpreter_sessions, + ) + self.completions = async_to_streamed_response_wrapper( + usage.completions, + ) + self.costs = async_to_streamed_response_wrapper( + usage.costs, + ) + self.embeddings = async_to_streamed_response_wrapper( + usage.embeddings, + ) + self.file_search_calls = async_to_streamed_response_wrapper( + usage.file_search_calls, + ) + self.images = async_to_streamed_response_wrapper( + usage.images, + ) + self.moderations = async_to_streamed_response_wrapper( + usage.moderations, + ) + self.vector_stores = async_to_streamed_response_wrapper( + usage.vector_stores, + ) + self.web_search_calls = async_to_streamed_response_wrapper( + usage.web_search_calls, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/__init__.py new file mode 100644 index 0000000..386f5ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .roles import ( + Roles as Roles, + AsyncRoles as AsyncRoles, + RolesWithRawResponse as RolesWithRawResponse, + AsyncRolesWithRawResponse as AsyncRolesWithRawResponse, + RolesWithStreamingResponse as RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse as AsyncRolesWithStreamingResponse, + ) + from .users import ( + Users as Users, + AsyncUsers as AsyncUsers, + UsersWithRawResponse as UsersWithRawResponse, + AsyncUsersWithRawResponse as AsyncUsersWithRawResponse, + UsersWithStreamingResponse as UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse as AsyncUsersWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Roles": (".roles", "Roles"), + "AsyncRoles": (".roles", "AsyncRoles"), + "RolesWithRawResponse": (".roles", "RolesWithRawResponse"), + "AsyncRolesWithRawResponse": (".roles", "AsyncRolesWithRawResponse"), + "RolesWithStreamingResponse": (".roles", "RolesWithStreamingResponse"), + "AsyncRolesWithStreamingResponse": (".roles", "AsyncRolesWithStreamingResponse"), + "Users": (".users", "Users"), + "AsyncUsers": (".users", "AsyncUsers"), + "UsersWithRawResponse": (".users", "UsersWithRawResponse"), + "AsyncUsersWithRawResponse": (".users", "AsyncUsersWithRawResponse"), + "UsersWithStreamingResponse": (".users", "UsersWithStreamingResponse"), + "AsyncUsersWithStreamingResponse": (".users", "AsyncUsersWithStreamingResponse"), + } + _SUBMODULES = { + "roles", + "users", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Roles", + "AsyncRoles", + "RolesWithRawResponse", + "AsyncRolesWithRawResponse", + "RolesWithStreamingResponse", + "AsyncRolesWithStreamingResponse", + "Users", + "AsyncUsers", + "UsersWithRawResponse", + "AsyncUsersWithRawResponse", + "UsersWithStreamingResponse", + "AsyncUsersWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..908a2db Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/__pycache__/roles.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/__pycache__/roles.cpython-312.pyc new file mode 100644 index 0000000..9500df1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/__pycache__/roles.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/__pycache__/users.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/__pycache__/users.cpython-312.pyc new file mode 100644 index 0000000..c881b0b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/__pycache__/users.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/roles.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/roles.py new file mode 100644 index 0000000..bc9304e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/roles.py @@ -0,0 +1,491 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncNextCursorPage, AsyncNextCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.users import role_list_params, role_create_params +from .....types.admin.organization.users.role_list_response import RoleListResponse +from .....types.admin.organization.users.role_create_response import RoleCreateResponse +from .....types.admin.organization.users.role_delete_response import RoleDeleteResponse +from .....types.admin.organization.users.role_retrieve_response import RoleRetrieveResponse + +__all__ = ["Roles", "AsyncRoles"] + + +class Roles(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RolesWithStreamingResponse(self) + + def create( + self, + user_id: str, + *, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns an organization role to a user within the organization. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._post( + path_template("/organization/users/{user_id}/roles", user_id=user_id), + body=maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + def retrieve( + self, + role_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves an organization role assigned to a user. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template("/organization/users/{user_id}/roles/{role_id}", user_id=user_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + + def list( + self, + user_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncNextCursorPage[RoleListResponse]: + """ + Lists the organization roles assigned to a user within the organization. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing organization roles. + + limit: A limit on the number of organization role assignments to return. + + order: Sort order for the returned organization roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get_api_list( + path_template("/organization/users/{user_id}/roles", user_id=user_id), + page=SyncNextCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + def delete( + self, + role_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns an organization role from a user within the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._delete( + path_template("/organization/users/{user_id}/roles/{role_id}", user_id=user_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class AsyncRoles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRolesWithStreamingResponse(self) + + async def create( + self, + user_id: str, + *, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns an organization role to a user within the organization. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._post( + path_template("/organization/users/{user_id}/roles", user_id=user_id), + body=await async_maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + async def retrieve( + self, + role_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves an organization role assigned to a user. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template("/organization/users/{user_id}/roles/{role_id}", user_id=user_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + + def list( + self, + user_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[RoleListResponse, AsyncNextCursorPage[RoleListResponse]]: + """ + Lists the organization roles assigned to a user within the organization. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing organization roles. + + limit: A limit on the number of organization role assignments to return. + + order: Sort order for the returned organization roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get_api_list( + path_template("/organization/users/{user_id}/roles", user_id=user_id), + page=AsyncNextCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + async def delete( + self, + role_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns an organization role from a user within the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._delete( + path_template("/organization/users/{user_id}/roles/{role_id}", user_id=user_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class RolesWithRawResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = _legacy_response.to_raw_response_wrapper( + roles.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithRawResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = _legacy_response.async_to_raw_response_wrapper( + roles.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + roles.delete, + ) + + +class RolesWithStreamingResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = to_streamed_response_wrapper( + roles.create, + ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) + self.list = to_streamed_response_wrapper( + roles.list, + ) + self.delete = to_streamed_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithStreamingResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = async_to_streamed_response_wrapper( + roles.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + roles.list, + ) + self.delete = async_to_streamed_response_wrapper( + roles.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/users.py b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/users.py new file mode 100644 index 0000000..4a66571 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/admin/organization/users/users.py @@ -0,0 +1,543 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx2 + +from ..... import _legacy_response +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization import user_list_params, user_update_params +from .....types.admin.organization.organization_user import OrganizationUser +from .....types.admin.organization.user_delete_response import UserDeleteResponse + +__all__ = ["Users", "AsyncUsers"] + + +class Users(SyncAPIResource): + @cached_property + def roles(self) -> Roles: + return Roles(self._client) + + @cached_property + def with_raw_response(self) -> UsersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return UsersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UsersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return UsersWithStreamingResponse(self) + + def retrieve( + self, + user_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationUser: + """ + Retrieves a user by their identifier. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get( + path_template("/organization/users/{user_id}", user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationUser, + ) + + def update( + self, + user_id: str, + *, + developer_persona: Optional[str] | Omit = omit, + role: Optional[str] | Omit = omit, + role_id: Optional[str] | Omit = omit, + technical_level: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationUser: + """ + Modifies a user's role in the organization. + + Args: + developer_persona: Developer persona metadata. + + role: `owner` or `reader` + + role_id: Role ID to assign to the user. + + technical_level: Technical level metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._post( + path_template("/organization/users/{user_id}", user_id=user_id), + body=maybe_transform( + { + "developer_persona": developer_persona, + "role": role, + "role_id": role_id, + "technical_level": technical_level, + }, + user_update_params.UserUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationUser, + ) + + def list( + self, + *, + after: str | Omit = omit, + emails: SequenceNotStr[str] | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[OrganizationUser]: + """ + Lists all of the users in the organization. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + emails: Filter by the email address of users. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/users", + page=SyncConversationCursorPage[OrganizationUser], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "emails": emails, + "limit": limit, + }, + user_list_params.UserListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationUser, + ) + + def delete( + self, + user_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UserDeleteResponse: + """ + Deletes a user from the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._delete( + path_template("/organization/users/{user_id}", user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserDeleteResponse, + ) + + +class AsyncUsers(AsyncAPIResource): + @cached_property + def roles(self) -> AsyncRoles: + return AsyncRoles(self._client) + + @cached_property + def with_raw_response(self) -> AsyncUsersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncUsersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUsersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncUsersWithStreamingResponse(self) + + async def retrieve( + self, + user_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationUser: + """ + Retrieves a user by their identifier. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._get( + path_template("/organization/users/{user_id}", user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationUser, + ) + + async def update( + self, + user_id: str, + *, + developer_persona: Optional[str] | Omit = omit, + role: Optional[str] | Omit = omit, + role_id: Optional[str] | Omit = omit, + technical_level: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OrganizationUser: + """ + Modifies a user's role in the organization. + + Args: + developer_persona: Developer persona metadata. + + role: `owner` or `reader` + + role_id: Role ID to assign to the user. + + technical_level: Technical level metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._post( + path_template("/organization/users/{user_id}", user_id=user_id), + body=await async_maybe_transform( + { + "developer_persona": developer_persona, + "role": role, + "role_id": role_id, + "technical_level": technical_level, + }, + user_update_params.UserUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationUser, + ) + + def list( + self, + *, + after: str | Omit = omit, + emails: SequenceNotStr[str] | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[OrganizationUser, AsyncConversationCursorPage[OrganizationUser]]: + """ + Lists all of the users in the organization. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + emails: Filter by the email address of users. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/users", + page=AsyncConversationCursorPage[OrganizationUser], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "emails": emails, + "limit": limit, + }, + user_list_params.UserListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationUser, + ) + + async def delete( + self, + user_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UserDeleteResponse: + """ + Deletes a user from the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._delete( + path_template("/organization/users/{user_id}", user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserDeleteResponse, + ) + + +class UsersWithRawResponse: + def __init__(self, users: Users) -> None: + self._users = users + + self.retrieve = _legacy_response.to_raw_response_wrapper( + users.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + users.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + users.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> RolesWithRawResponse: + return RolesWithRawResponse(self._users.roles) + + +class AsyncUsersWithRawResponse: + def __init__(self, users: AsyncUsers) -> None: + self._users = users + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + users.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + users.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + users.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> AsyncRolesWithRawResponse: + return AsyncRolesWithRawResponse(self._users.roles) + + +class UsersWithStreamingResponse: + def __init__(self, users: Users) -> None: + self._users = users + + self.retrieve = to_streamed_response_wrapper( + users.retrieve, + ) + self.update = to_streamed_response_wrapper( + users.update, + ) + self.list = to_streamed_response_wrapper( + users.list, + ) + self.delete = to_streamed_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> RolesWithStreamingResponse: + return RolesWithStreamingResponse(self._users.roles) + + +class AsyncUsersWithStreamingResponse: + def __init__(self, users: AsyncUsers) -> None: + self._users = users + + self.retrieve = async_to_streamed_response_wrapper( + users.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + users.update, + ) + self.list = async_to_streamed_response_wrapper( + users.list, + ) + self.delete = async_to_streamed_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> AsyncRolesWithStreamingResponse: + return AsyncRolesWithStreamingResponse(self._users.roles) diff --git a/venv/lib/python3.12/site-packages/openai/resources/audio/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/audio/__init__.py new file mode 100644 index 0000000..9b02948 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/audio/__init__.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .audio import ( + Audio as Audio, + AsyncAudio as AsyncAudio, + AudioWithRawResponse as AudioWithRawResponse, + AsyncAudioWithRawResponse as AsyncAudioWithRawResponse, + AudioWithStreamingResponse as AudioWithStreamingResponse, + AsyncAudioWithStreamingResponse as AsyncAudioWithStreamingResponse, + ) + from .speech import ( + Speech as Speech, + AsyncSpeech as AsyncSpeech, + SpeechWithRawResponse as SpeechWithRawResponse, + AsyncSpeechWithRawResponse as AsyncSpeechWithRawResponse, + SpeechWithStreamingResponse as SpeechWithStreamingResponse, + AsyncSpeechWithStreamingResponse as AsyncSpeechWithStreamingResponse, + ) + from .translations import ( + Translations as Translations, + AsyncTranslations as AsyncTranslations, + TranslationsWithRawResponse as TranslationsWithRawResponse, + AsyncTranslationsWithRawResponse as AsyncTranslationsWithRawResponse, + TranslationsWithStreamingResponse as TranslationsWithStreamingResponse, + AsyncTranslationsWithStreamingResponse as AsyncTranslationsWithStreamingResponse, + ) + from .transcriptions import ( + Transcriptions as Transcriptions, + AsyncTranscriptions as AsyncTranscriptions, + TranscriptionsWithRawResponse as TranscriptionsWithRawResponse, + AsyncTranscriptionsWithRawResponse as AsyncTranscriptionsWithRawResponse, + TranscriptionsWithStreamingResponse as TranscriptionsWithStreamingResponse, + AsyncTranscriptionsWithStreamingResponse as AsyncTranscriptionsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Transcriptions": (".transcriptions", "Transcriptions"), + "AsyncTranscriptions": (".transcriptions", "AsyncTranscriptions"), + "TranscriptionsWithRawResponse": (".transcriptions", "TranscriptionsWithRawResponse"), + "AsyncTranscriptionsWithRawResponse": (".transcriptions", "AsyncTranscriptionsWithRawResponse"), + "TranscriptionsWithStreamingResponse": (".transcriptions", "TranscriptionsWithStreamingResponse"), + "AsyncTranscriptionsWithStreamingResponse": (".transcriptions", "AsyncTranscriptionsWithStreamingResponse"), + "Translations": (".translations", "Translations"), + "AsyncTranslations": (".translations", "AsyncTranslations"), + "TranslationsWithRawResponse": (".translations", "TranslationsWithRawResponse"), + "AsyncTranslationsWithRawResponse": (".translations", "AsyncTranslationsWithRawResponse"), + "TranslationsWithStreamingResponse": (".translations", "TranslationsWithStreamingResponse"), + "AsyncTranslationsWithStreamingResponse": (".translations", "AsyncTranslationsWithStreamingResponse"), + "Speech": (".speech", "Speech"), + "AsyncSpeech": (".speech", "AsyncSpeech"), + "SpeechWithRawResponse": (".speech", "SpeechWithRawResponse"), + "AsyncSpeechWithRawResponse": (".speech", "AsyncSpeechWithRawResponse"), + "SpeechWithStreamingResponse": (".speech", "SpeechWithStreamingResponse"), + "AsyncSpeechWithStreamingResponse": (".speech", "AsyncSpeechWithStreamingResponse"), + "Audio": (".audio", "Audio"), + "AsyncAudio": (".audio", "AsyncAudio"), + "AudioWithRawResponse": (".audio", "AudioWithRawResponse"), + "AsyncAudioWithRawResponse": (".audio", "AsyncAudioWithRawResponse"), + "AudioWithStreamingResponse": (".audio", "AudioWithStreamingResponse"), + "AsyncAudioWithStreamingResponse": (".audio", "AsyncAudioWithStreamingResponse"), + } + _SUBMODULES = { + "audio", + "speech", + "transcriptions", + "translations", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Transcriptions", + "AsyncTranscriptions", + "TranscriptionsWithRawResponse", + "AsyncTranscriptionsWithRawResponse", + "TranscriptionsWithStreamingResponse", + "AsyncTranscriptionsWithStreamingResponse", + "Translations", + "AsyncTranslations", + "TranslationsWithRawResponse", + "AsyncTranslationsWithRawResponse", + "TranslationsWithStreamingResponse", + "AsyncTranslationsWithStreamingResponse", + "Speech", + "AsyncSpeech", + "SpeechWithRawResponse", + "AsyncSpeechWithRawResponse", + "SpeechWithStreamingResponse", + "AsyncSpeechWithStreamingResponse", + "Audio", + "AsyncAudio", + "AudioWithRawResponse", + "AsyncAudioWithRawResponse", + "AudioWithStreamingResponse", + "AsyncAudioWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..33146b3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/audio.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/audio.cpython-312.pyc new file mode 100644 index 0000000..2e7cc43 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/audio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/speech.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/speech.cpython-312.pyc new file mode 100644 index 0000000..65d76dc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/speech.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/transcriptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/transcriptions.cpython-312.pyc new file mode 100644 index 0000000..b2e9dbe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/transcriptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/translations.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/translations.cpython-312.pyc new file mode 100644 index 0000000..aa8c042 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/audio/__pycache__/translations.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/audio/audio.py b/venv/lib/python3.12/site-packages/openai/resources/audio/audio.py new file mode 100644 index 0000000..12d54d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/audio/audio.py @@ -0,0 +1,184 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .speech import ( + Speech, + AsyncSpeech, + SpeechWithRawResponse, + AsyncSpeechWithRawResponse, + SpeechWithStreamingResponse, + AsyncSpeechWithStreamingResponse, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .translations import ( + Translations, + AsyncTranslations, + TranslationsWithRawResponse, + AsyncTranslationsWithRawResponse, + TranslationsWithStreamingResponse, + AsyncTranslationsWithStreamingResponse, +) +from .transcriptions import ( + Transcriptions, + AsyncTranscriptions, + TranscriptionsWithRawResponse, + AsyncTranscriptionsWithRawResponse, + TranscriptionsWithStreamingResponse, + AsyncTranscriptionsWithStreamingResponse, +) + +__all__ = ["Audio", "AsyncAudio"] + + +class Audio(SyncAPIResource): + @cached_property + def transcriptions(self) -> Transcriptions: + """Turn audio into text or text into audio.""" + return Transcriptions(self._client) + + @cached_property + def translations(self) -> Translations: + """Turn audio into text or text into audio.""" + return Translations(self._client) + + @cached_property + def speech(self) -> Speech: + """Turn audio into text or text into audio.""" + return Speech(self._client) + + @cached_property + def with_raw_response(self) -> AudioWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AudioWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AudioWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AudioWithStreamingResponse(self) + + +class AsyncAudio(AsyncAPIResource): + @cached_property + def transcriptions(self) -> AsyncTranscriptions: + """Turn audio into text or text into audio.""" + return AsyncTranscriptions(self._client) + + @cached_property + def translations(self) -> AsyncTranslations: + """Turn audio into text or text into audio.""" + return AsyncTranslations(self._client) + + @cached_property + def speech(self) -> AsyncSpeech: + """Turn audio into text or text into audio.""" + return AsyncSpeech(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAudioWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAudioWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAudioWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAudioWithStreamingResponse(self) + + +class AudioWithRawResponse: + def __init__(self, audio: Audio) -> None: + self._audio = audio + + @cached_property + def transcriptions(self) -> TranscriptionsWithRawResponse: + """Turn audio into text or text into audio.""" + return TranscriptionsWithRawResponse(self._audio.transcriptions) + + @cached_property + def translations(self) -> TranslationsWithRawResponse: + """Turn audio into text or text into audio.""" + return TranslationsWithRawResponse(self._audio.translations) + + @cached_property + def speech(self) -> SpeechWithRawResponse: + """Turn audio into text or text into audio.""" + return SpeechWithRawResponse(self._audio.speech) + + +class AsyncAudioWithRawResponse: + def __init__(self, audio: AsyncAudio) -> None: + self._audio = audio + + @cached_property + def transcriptions(self) -> AsyncTranscriptionsWithRawResponse: + """Turn audio into text or text into audio.""" + return AsyncTranscriptionsWithRawResponse(self._audio.transcriptions) + + @cached_property + def translations(self) -> AsyncTranslationsWithRawResponse: + """Turn audio into text or text into audio.""" + return AsyncTranslationsWithRawResponse(self._audio.translations) + + @cached_property + def speech(self) -> AsyncSpeechWithRawResponse: + """Turn audio into text or text into audio.""" + return AsyncSpeechWithRawResponse(self._audio.speech) + + +class AudioWithStreamingResponse: + def __init__(self, audio: Audio) -> None: + self._audio = audio + + @cached_property + def transcriptions(self) -> TranscriptionsWithStreamingResponse: + """Turn audio into text or text into audio.""" + return TranscriptionsWithStreamingResponse(self._audio.transcriptions) + + @cached_property + def translations(self) -> TranslationsWithStreamingResponse: + """Turn audio into text or text into audio.""" + return TranslationsWithStreamingResponse(self._audio.translations) + + @cached_property + def speech(self) -> SpeechWithStreamingResponse: + """Turn audio into text or text into audio.""" + return SpeechWithStreamingResponse(self._audio.speech) + + +class AsyncAudioWithStreamingResponse: + def __init__(self, audio: AsyncAudio) -> None: + self._audio = audio + + @cached_property + def transcriptions(self) -> AsyncTranscriptionsWithStreamingResponse: + """Turn audio into text or text into audio.""" + return AsyncTranscriptionsWithStreamingResponse(self._audio.transcriptions) + + @cached_property + def translations(self) -> AsyncTranslationsWithStreamingResponse: + """Turn audio into text or text into audio.""" + return AsyncTranslationsWithStreamingResponse(self._audio.translations) + + @cached_property + def speech(self) -> AsyncSpeechWithStreamingResponse: + """Turn audio into text or text into audio.""" + return AsyncSpeechWithStreamingResponse(self._audio.speech) diff --git a/venv/lib/python3.12/site-packages/openai/resources/audio/speech.py b/venv/lib/python3.12/site-packages/openai/resources/audio/speech.py new file mode 100644 index 0000000..fc698e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/audio/speech.py @@ -0,0 +1,273 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal + +import httpx2 + +from ... import _legacy_response +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ...types.audio import speech_create_params +from ..._base_client import make_request_options +from ...types.audio.speech_model import SpeechModel + +__all__ = ["Speech", "AsyncSpeech"] + + +class Speech(SyncAPIResource): + """Turn audio into text or text into audio.""" + + @cached_property + def with_raw_response(self) -> SpeechWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SpeechWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SpeechWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SpeechWithStreamingResponse(self) + + def create( + self, + *, + input: str, + model: Union[str, SpeechModel], + voice: speech_create_params.Voice, + instructions: str | Omit = omit, + response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | Omit = omit, + speed: float | Omit = omit, + stream_format: Literal["sse", "audio"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Generates audio from the input text. + + Returns the audio file content, or a stream of audio events. + + Args: + input: The text to generate audio for. The maximum length is 4096 characters. + + model: + One of the available + [TTS models](https://developers.openai.com/api/docs/guides/text-to-speech): + `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`. + + voice: The voice to use when generating the audio. Supported built-in voices are + `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, + `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice + object with an `id`, for example `{ "id": "voice_1234" }`. Previews of the + voices are available in the + [Text to speech guide](https://developers.openai.com/api/docs/guides/text-to-speech#voice-options). + + instructions: Control the voice of your generated audio with additional instructions. Does not + work with `tts-1` or `tts-1-hd`. + + response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, + `wav`, and `pcm`. + + speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is + the default. + + stream_format: The format to stream the audio in. Supported formats are `sse` and `audio`. + `sse` is not supported for `tts-1` or `tts-1-hd`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} + return self._post( + "/audio/speech", + body=maybe_transform( + { + "input": input, + "model": model, + "voice": voice, + "instructions": instructions, + "response_format": response_format, + "speed": speed, + "stream_format": stream_format, + }, + speech_create_params.SpeechCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class AsyncSpeech(AsyncAPIResource): + """Turn audio into text or text into audio.""" + + @cached_property + def with_raw_response(self) -> AsyncSpeechWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSpeechWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSpeechWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSpeechWithStreamingResponse(self) + + async def create( + self, + *, + input: str, + model: Union[str, SpeechModel], + voice: speech_create_params.Voice, + instructions: str | Omit = omit, + response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | Omit = omit, + speed: float | Omit = omit, + stream_format: Literal["sse", "audio"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Generates audio from the input text. + + Returns the audio file content, or a stream of audio events. + + Args: + input: The text to generate audio for. The maximum length is 4096 characters. + + model: + One of the available + [TTS models](https://developers.openai.com/api/docs/guides/text-to-speech): + `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`. + + voice: The voice to use when generating the audio. Supported built-in voices are + `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, + `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice + object with an `id`, for example `{ "id": "voice_1234" }`. Previews of the + voices are available in the + [Text to speech guide](https://developers.openai.com/api/docs/guides/text-to-speech#voice-options). + + instructions: Control the voice of your generated audio with additional instructions. Does not + work with `tts-1` or `tts-1-hd`. + + response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, + `wav`, and `pcm`. + + speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is + the default. + + stream_format: The format to stream the audio in. Supported formats are `sse` and `audio`. + `sse` is not supported for `tts-1` or `tts-1-hd`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} + return await self._post( + "/audio/speech", + body=await async_maybe_transform( + { + "input": input, + "model": model, + "voice": voice, + "instructions": instructions, + "response_format": response_format, + "speed": speed, + "stream_format": stream_format, + }, + speech_create_params.SpeechCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class SpeechWithRawResponse: + def __init__(self, speech: Speech) -> None: + self._speech = speech + + self.create = _legacy_response.to_raw_response_wrapper( + speech.create, + ) + + +class AsyncSpeechWithRawResponse: + def __init__(self, speech: AsyncSpeech) -> None: + self._speech = speech + + self.create = _legacy_response.async_to_raw_response_wrapper( + speech.create, + ) + + +class SpeechWithStreamingResponse: + def __init__(self, speech: Speech) -> None: + self._speech = speech + + self.create = to_custom_streamed_response_wrapper( + speech.create, + StreamedBinaryAPIResponse, + ) + + +class AsyncSpeechWithStreamingResponse: + def __init__(self, speech: AsyncSpeech) -> None: + self._speech = speech + + self.create = async_to_custom_streamed_response_wrapper( + speech.create, + AsyncStreamedBinaryAPIResponse, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/audio/transcriptions.py b/venv/lib/python3.12/site-packages/openai/resources/audio/transcriptions.py new file mode 100644 index 0000000..17f4d67 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/audio/transcriptions.py @@ -0,0 +1,1089 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import logging +from typing import List, Union, Mapping, Optional, cast +from typing_extensions import Literal, overload + +import httpx2 + +from ... import _legacy_response +from ..._files import deepcopy_with_paths +from ..._types import ( + Body, + Omit, + Query, + Headers, + NotGiven, + FileTypes, + SequenceNotStr, + omit, + not_given, +) +from ..._utils import extract_files, required_args, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._streaming import Stream, AsyncStream +from ...types.audio import transcription_create_params +from ..._base_client import make_request_options +from ...types.audio_model import AudioModel +from ...lib._parsing._audio import get_transcription_response_format_type as _get_transcription_response_format_type +from ...types.audio.transcription import Transcription +from ...types.audio_response_format import AudioResponseFormat +from ...types.audio.transcription_include import TranscriptionInclude +from ...types.audio.transcription_verbose import TranscriptionVerbose +from ...types.audio.transcription_diarized import TranscriptionDiarized +from ...types.audio.transcription_stream_event import TranscriptionStreamEvent +from ...types.audio.transcription_create_response import TranscriptionCreateResponse + +__all__ = ["Transcriptions", "AsyncTranscriptions"] + +log: logging.Logger = logging.getLogger("openai.audio.transcriptions") + + +class Transcriptions(SyncAPIResource): + """Turn audio into text or text into audio.""" + + @cached_property + def with_raw_response(self) -> TranscriptionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return TranscriptionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TranscriptionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return TranscriptionsWithStreamingResponse(self) + + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + keywords: SequenceNotStr[str] | Omit = omit, + language: str | Omit = omit, + languages: SequenceNotStr[str] | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[Literal["json"], Omit] = omit, + stream: Optional[Literal[False]] | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Transcription: + """ + Transcribes audio into the input language. + + Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + format, plain text in `text`, `srt`, or `vtt` format, or a stream of + transcript events. Supported formats depend on the model. + + Args: + file: The audio file object (not file name) to transcribe, in one of these formats: + flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include + enough format metadata for the file to be identified. We recommend an + extension-bearing filename and an appropriate content type. + + model: ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` + (which is powered by our open source Whisper V2 model), and + `gpt-4o-transcribe-diarize`. + + chunking_strategy: Controls how the audio is cut into chunks. When set to `"auto"`, the server + first normalizes loudness and then uses voice activity detection (VAD) to choose + boundaries. `server_vad` object can be provided to tweak VAD detection + parameters manually. If unset, the audio is transcribed as a single block. + + include: Additional information to include in the transcription response. `logprobs` will + return the log probabilities of the tokens in the response to understand the + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. + + + language: The language of the input audio. Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + + languages: Possible languages of the input audio, in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format. + Supported by `gpt-transcribe`. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. + + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, + the only supported format is `json`. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + timestamp_granularities: The timestamp granularities to populate for this transcription. + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + """ + + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + keywords: SequenceNotStr[str] | Omit = omit, + response_format: Literal["verbose_json"], + language: str | Omit = omit, + languages: SequenceNotStr[str] | Omit = omit, + prompt: str | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> TranscriptionVerbose: ... + + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + response_format: Literal["text", "srt", "vtt"], + include: List[TranscriptionInclude] | Omit = omit, + keywords: SequenceNotStr[str] | Omit = omit, + language: str | Omit = omit, + languages: SequenceNotStr[str] | Omit = omit, + prompt: str | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> str: ... + + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + response_format: Literal["diarized_json"], + keywords: SequenceNotStr[str] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, + language: str | Omit = omit, + languages: SequenceNotStr[str] | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> TranscriptionDiarized: ... + + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + stream: Literal[True], + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + keywords: SequenceNotStr[str] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, + language: str | Omit = omit, + languages: SequenceNotStr[str] | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[TranscriptionStreamEvent]: + """ + Transcribes audio into the input language. + + Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + format, plain text in `text`, `srt`, or `vtt` format, or a stream of + transcript events. Supported formats depend on the model. + + Args: + file: The audio file object (not file name) to transcribe, in one of these formats: + flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include + enough format metadata for the file to be identified. We recommend an + extension-bearing filename and an appropriate content type. + + model: ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` + (which is powered by our open source Whisper V2 model), and + `gpt-4o-transcribe-diarize`. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + + chunking_strategy: Controls how the audio is cut into chunks. When set to `"auto"`, the server + first normalizes loudness and then uses voice activity detection (VAD) to choose + boundaries. `server_vad` object can be provided to tweak VAD detection + parameters manually. If unset, the audio is transcribed as a single block. + Required when using `gpt-4o-transcribe-diarize` for inputs longer than 30 + seconds. + + include: Additional information to include in the transcription response. `logprobs` will + return the log probabilities of the tokens in the response to understand the + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. + + keywords: Words or phrases to guide transcription of the input audio. Supported by + `gpt-transcribe`. + + known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in + `known_speaker_references[]`. Each entry should be a short identifier (for + example `customer` or `agent`). Up to 4 speakers are supported. + + known_speaker_references: Optional list of audio samples (as + [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) + that contain known speaker references matching `known_speaker_names[]`. Each + sample must be between 2 and 10 seconds, and can use any of the same input audio + formats supported by `file`. + + language: The language of the input audio. Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + + languages: Possible languages of the input audio, in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format. + Supported by `gpt-transcribe`. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. + + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`, the only supported format is `json`. For + `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and + `diarized_json`, with `diarized_json` required to receive speaker annotations. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + timestamp_granularities: The timestamp granularities to populate for this transcription. + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. This option is not available for + `gpt-4o-transcribe-diarize`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + stream: bool, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + keywords: SequenceNotStr[str] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, + language: str | Omit = omit, + languages: SequenceNotStr[str] | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> TranscriptionCreateResponse | Stream[TranscriptionStreamEvent]: + """ + Transcribes audio into the input language. + + Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + format, plain text in `text`, `srt`, or `vtt` format, or a stream of + transcript events. Supported formats depend on the model. + + Args: + file: The audio file object (not file name) to transcribe, in one of these formats: + flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include + enough format metadata for the file to be identified. We recommend an + extension-bearing filename and an appropriate content type. + + model: ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` + (which is powered by our open source Whisper V2 model), and + `gpt-4o-transcribe-diarize`. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + + chunking_strategy: Controls how the audio is cut into chunks. When set to `"auto"`, the server + first normalizes loudness and then uses voice activity detection (VAD) to choose + boundaries. `server_vad` object can be provided to tweak VAD detection + parameters manually. If unset, the audio is transcribed as a single block. + Required when using `gpt-4o-transcribe-diarize` for inputs longer than 30 + seconds. + + include: Additional information to include in the transcription response. `logprobs` will + return the log probabilities of the tokens in the response to understand the + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. + + keywords: Words or phrases to guide transcription of the input audio. Supported by + `gpt-transcribe`. + + known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in + `known_speaker_references[]`. Each entry should be a short identifier (for + example `customer` or `agent`). Up to 4 speakers are supported. + + known_speaker_references: Optional list of audio samples (as + [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) + that contain known speaker references matching `known_speaker_names[]`. Each + sample must be between 2 and 10 seconds, and can use any of the same input audio + formats supported by `file`. + + language: The language of the input audio. Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + + languages: Possible languages of the input audio, in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format. + Supported by `gpt-transcribe`. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. + + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`, the only supported format is `json`. For + `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and + `diarized_json`, with `diarized_json` required to receive speaker annotations. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + timestamp_granularities: The timestamp granularities to populate for this transcription. + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. This option is not available for + `gpt-4o-transcribe-diarize`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["file", "model"], ["file", "model", "stream"]) + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + keywords: SequenceNotStr[str] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, + language: str | Omit = omit, + languages: SequenceNotStr[str] | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> str | Transcription | TranscriptionDiarized | TranscriptionVerbose | Stream[TranscriptionStreamEvent]: + body = deepcopy_with_paths( + { + "file": file, + "model": model, + "chunking_strategy": chunking_strategy, + "include": include, + "keywords": keywords, + "known_speaker_names": known_speaker_names, + "known_speaker_references": known_speaker_references, + "language": language, + "languages": languages, + "prompt": prompt, + "response_format": response_format, + "stream": stream, + "temperature": temperature, + "timestamp_granularities": timestamp_granularities, + }, + [["file"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( # type: ignore[return-value] + "/audio/transcriptions", + body=maybe_transform( + body, + transcription_create_params.TranscriptionCreateParamsStreaming + if stream + else transcription_create_params.TranscriptionCreateParamsNonStreaming, + ), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_get_response_format_type(response_format), + stream=stream or False, + stream_cls=Stream[TranscriptionStreamEvent], + ) + + +class AsyncTranscriptions(AsyncAPIResource): + """Turn audio into text or text into audio.""" + + @cached_property + def with_raw_response(self) -> AsyncTranscriptionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncTranscriptionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTranscriptionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncTranscriptionsWithStreamingResponse(self) + + @overload + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + keywords: SequenceNotStr[str] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, + language: str | Omit = omit, + languages: SequenceNotStr[str] | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[Literal["json"], Omit] = omit, + stream: Optional[Literal[False]] | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> TranscriptionCreateResponse: + """ + Transcribes audio into the input language. + + Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + format, plain text in `text`, `srt`, or `vtt` format, or a stream of + transcript events. Supported formats depend on the model. + + Args: + file: The audio file object (not file name) to transcribe, in one of these formats: + flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include + enough format metadata for the file to be identified. We recommend an + extension-bearing filename and an appropriate content type. + + model: ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` + (which is powered by our open source Whisper V2 model), and + `gpt-4o-transcribe-diarize`. + + chunking_strategy: Controls how the audio is cut into chunks. When set to `"auto"`, the server + first normalizes loudness and then uses voice activity detection (VAD) to choose + boundaries. `server_vad` object can be provided to tweak VAD detection + parameters manually. If unset, the audio is transcribed as a single block. + Required when using `gpt-4o-transcribe-diarize` for inputs longer than 30 + seconds. + + include: Additional information to include in the transcription response. `logprobs` will + return the log probabilities of the tokens in the response to understand the + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. + + keywords: Words or phrases to guide transcription of the input audio. Supported by + `gpt-transcribe`. + + known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in + `known_speaker_references[]`. Each entry should be a short identifier (for + example `customer` or `agent`). Up to 4 speakers are supported. + + known_speaker_references: Optional list of audio samples (as + [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) + that contain known speaker references matching `known_speaker_names[]`. Each + sample must be between 2 and 10 seconds, and can use any of the same input audio + formats supported by `file`. + + language: The language of the input audio. Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + + languages: Possible languages of the input audio, in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format. + Supported by `gpt-transcribe`. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. + + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`, the only supported format is `json`. For + `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and + `diarized_json`, with `diarized_json` required to receive speaker annotations. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + timestamp_granularities: The timestamp granularities to populate for this transcription. + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. This option is not available for + `gpt-4o-transcribe-diarize`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + """ + + @overload + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + keywords: SequenceNotStr[str] | Omit = omit, + response_format: Literal["verbose_json"], + language: str | Omit = omit, + languages: SequenceNotStr[str] | Omit = omit, + prompt: str | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> TranscriptionVerbose: ... + + @overload + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + response_format: Literal["text", "srt", "vtt"], + keywords: SequenceNotStr[str] | Omit = omit, + language: str | Omit = omit, + languages: SequenceNotStr[str] | Omit = omit, + prompt: str | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> str: ... + + @overload + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + stream: Literal[True], + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + keywords: SequenceNotStr[str] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, + language: str | Omit = omit, + languages: SequenceNotStr[str] | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[TranscriptionStreamEvent]: + """ + Transcribes audio into the input language. + + Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + format, plain text in `text`, `srt`, or `vtt` format, or a stream of + transcript events. Supported formats depend on the model. + + Args: + file: The audio file object (not file name) to transcribe, in one of these formats: + flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include + enough format metadata for the file to be identified. We recommend an + extension-bearing filename and an appropriate content type. + + model: ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` + (which is powered by our open source Whisper V2 model), and + `gpt-4o-transcribe-diarize`. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + + chunking_strategy: Controls how the audio is cut into chunks. When set to `"auto"`, the server + first normalizes loudness and then uses voice activity detection (VAD) to choose + boundaries. `server_vad` object can be provided to tweak VAD detection + parameters manually. If unset, the audio is transcribed as a single block. + Required when using `gpt-4o-transcribe-diarize` for inputs longer than 30 + seconds. + + include: Additional information to include in the transcription response. `logprobs` will + return the log probabilities of the tokens in the response to understand the + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. + + keywords: Words or phrases to guide transcription of the input audio. Supported by + `gpt-transcribe`. + + known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in + `known_speaker_references[]`. Each entry should be a short identifier (for + example `customer` or `agent`). Up to 4 speakers are supported. + + known_speaker_references: Optional list of audio samples (as + [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) + that contain known speaker references matching `known_speaker_names[]`. Each + sample must be between 2 and 10 seconds, and can use any of the same input audio + formats supported by `file`. + + language: The language of the input audio. Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + + languages: Possible languages of the input audio, in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format. + Supported by `gpt-transcribe`. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. + + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`, the only supported format is `json`. For + `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and + `diarized_json`, with `diarized_json` required to receive speaker annotations. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + timestamp_granularities: The timestamp granularities to populate for this transcription. + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. This option is not available for + `gpt-4o-transcribe-diarize`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + stream: bool, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + keywords: SequenceNotStr[str] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, + language: str | Omit = omit, + languages: SequenceNotStr[str] | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> TranscriptionCreateResponse | AsyncStream[TranscriptionStreamEvent]: + """ + Transcribes audio into the input language. + + Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + format, plain text in `text`, `srt`, or `vtt` format, or a stream of + transcript events. Supported formats depend on the model. + + Args: + file: The audio file object (not file name) to transcribe, in one of these formats: + flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include + enough format metadata for the file to be identified. We recommend an + extension-bearing filename and an appropriate content type. + + model: ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` + (which is powered by our open source Whisper V2 model), and + `gpt-4o-transcribe-diarize`. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + + chunking_strategy: Controls how the audio is cut into chunks. When set to `"auto"`, the server + first normalizes loudness and then uses voice activity detection (VAD) to choose + boundaries. `server_vad` object can be provided to tweak VAD detection + parameters manually. If unset, the audio is transcribed as a single block. + Required when using `gpt-4o-transcribe-diarize` for inputs longer than 30 + seconds. + + include: Additional information to include in the transcription response. `logprobs` will + return the log probabilities of the tokens in the response to understand the + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. + + keywords: Words or phrases to guide transcription of the input audio. Supported by + `gpt-transcribe`. + + known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in + `known_speaker_references[]`. Each entry should be a short identifier (for + example `customer` or `agent`). Up to 4 speakers are supported. + + known_speaker_references: Optional list of audio samples (as + [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) + that contain known speaker references matching `known_speaker_names[]`. Each + sample must be between 2 and 10 seconds, and can use any of the same input audio + formats supported by `file`. + + language: The language of the input audio. Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + + languages: Possible languages of the input audio, in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format. + Supported by `gpt-transcribe`. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. + + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`, the only supported format is `json`. For + `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and + `diarized_json`, with `diarized_json` required to receive speaker annotations. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + timestamp_granularities: The timestamp granularities to populate for this transcription. + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. This option is not available for + `gpt-4o-transcribe-diarize`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["file", "model"], ["file", "model", "stream"]) + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + keywords: SequenceNotStr[str] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, + language: str | Omit = omit, + languages: SequenceNotStr[str] | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Transcription | TranscriptionVerbose | TranscriptionDiarized | str | AsyncStream[TranscriptionStreamEvent]: + body = deepcopy_with_paths( + { + "file": file, + "model": model, + "chunking_strategy": chunking_strategy, + "include": include, + "keywords": keywords, + "known_speaker_names": known_speaker_names, + "known_speaker_references": known_speaker_references, + "language": language, + "languages": languages, + "prompt": prompt, + "response_format": response_format, + "stream": stream, + "temperature": temperature, + "timestamp_granularities": timestamp_granularities, + }, + [["file"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/audio/transcriptions", + body=await async_maybe_transform( + body, + transcription_create_params.TranscriptionCreateParamsStreaming + if stream + else transcription_create_params.TranscriptionCreateParamsNonStreaming, + ), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_get_response_format_type(response_format), + stream=stream or False, + stream_cls=AsyncStream[TranscriptionStreamEvent], + ) + + +class TranscriptionsWithRawResponse: + def __init__(self, transcriptions: Transcriptions) -> None: + self._transcriptions = transcriptions + + self.create = _legacy_response.to_raw_response_wrapper( + transcriptions.create, + ) + + +class AsyncTranscriptionsWithRawResponse: + def __init__(self, transcriptions: AsyncTranscriptions) -> None: + self._transcriptions = transcriptions + + self.create = _legacy_response.async_to_raw_response_wrapper( + transcriptions.create, + ) + + +class TranscriptionsWithStreamingResponse: + def __init__(self, transcriptions: Transcriptions) -> None: + self._transcriptions = transcriptions + + self.create = to_streamed_response_wrapper( + transcriptions.create, + ) + + +class AsyncTranscriptionsWithStreamingResponse: + def __init__(self, transcriptions: AsyncTranscriptions) -> None: + self._transcriptions = transcriptions + + self.create = async_to_streamed_response_wrapper( + transcriptions.create, + ) + + +def _get_response_format_type( + response_format: AudioResponseFormat | Omit, +) -> type[Transcription | TranscriptionVerbose | TranscriptionDiarized | str]: + return _get_transcription_response_format_type(response_format, log=log) diff --git a/venv/lib/python3.12/site-packages/openai/resources/audio/translations.py b/venv/lib/python3.12/site-packages/openai/resources/audio/translations.py new file mode 100644 index 0000000..492b02d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/audio/translations.py @@ -0,0 +1,374 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import logging +from typing import Union, Mapping, cast +from typing_extensions import Literal, overload + +import httpx2 + +from ... import _legacy_response +from ..._files import deepcopy_with_paths +from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given +from ..._utils import extract_files, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...types.audio import translation_create_params +from ..._base_client import make_request_options +from ...types.audio_model import AudioModel +from ...lib._parsing._audio import get_translation_response_format_type as _get_translation_response_format_type +from ...types.audio.translation import Translation +from ...types.audio_response_format import AudioResponseFormat +from ...types.audio.translation_verbose import TranslationVerbose + +__all__ = ["Translations", "AsyncTranslations"] + +log: logging.Logger = logging.getLogger("openai.audio.transcriptions") + + +class Translations(SyncAPIResource): + """Turn audio into text or text into audio.""" + + @cached_property + def with_raw_response(self) -> TranslationsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return TranslationsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TranslationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return TranslationsWithStreamingResponse(self) + + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Union[Literal["json"], Omit] = omit, + prompt: str | Omit = omit, + temperature: float | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Translation: ... + + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Literal["verbose_json"], + prompt: str | Omit = omit, + temperature: float | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> TranslationVerbose: ... + + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Literal["text", "srt", "vtt"], + prompt: str | Omit = omit, + temperature: float | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> str: ... + + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + prompt: str | Omit = omit, + response_format: Union[Literal["json", "text", "srt", "verbose_json", "vtt"], Omit] = omit, + temperature: float | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Translation | TranslationVerbose | str: + """ + Translates audio into English. + + Args: + file: The audio file object (not file name) translate, in one of these formats: flac, + mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include enough + format metadata for the file to be identified. We recommend an extension-bearing + filename and an appropriate content type. + + model: ID of the model to use. Only `whisper-1` (which is powered by our open source + Whisper V2 model) is currently available. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) + should be in English. + + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, or `vtt`. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "file": file, + "model": model, + "prompt": prompt, + "response_format": response_format, + "temperature": temperature, + }, + [["file"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( # type: ignore[return-value] + "/audio/translations", + body=maybe_transform(body, translation_create_params.TranslationCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_get_response_format_type(response_format), + ) + + +class AsyncTranslations(AsyncAPIResource): + """Turn audio into text or text into audio.""" + + @cached_property + def with_raw_response(self) -> AsyncTranslationsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncTranslationsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTranslationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncTranslationsWithStreamingResponse(self) + + @overload + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Union[Literal["json"], Omit] = omit, + prompt: str | Omit = omit, + temperature: float | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Translation: ... + + @overload + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Literal["verbose_json"], + prompt: str | Omit = omit, + temperature: float | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> TranslationVerbose: ... + + @overload + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Literal["text", "srt", "vtt"], + prompt: str | Omit = omit, + temperature: float | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> str: ... + + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + temperature: float | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Translation | TranslationVerbose | str: + """ + Translates audio into English. + + Args: + file: The audio file object (not file name) translate, in one of these formats: flac, + mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include enough + format metadata for the file to be identified. We recommend an extension-bearing + filename and an appropriate content type. + + model: ID of the model to use. Only `whisper-1` (which is powered by our open source + Whisper V2 model) is currently available. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) + should be in English. + + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, or `vtt`. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "file": file, + "model": model, + "prompt": prompt, + "response_format": response_format, + "temperature": temperature, + }, + [["file"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/audio/translations", + body=await async_maybe_transform(body, translation_create_params.TranslationCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_get_response_format_type(response_format), + ) + + +class TranslationsWithRawResponse: + def __init__(self, translations: Translations) -> None: + self._translations = translations + + self.create = _legacy_response.to_raw_response_wrapper( + translations.create, + ) + + +class AsyncTranslationsWithRawResponse: + def __init__(self, translations: AsyncTranslations) -> None: + self._translations = translations + + self.create = _legacy_response.async_to_raw_response_wrapper( + translations.create, + ) + + +class TranslationsWithStreamingResponse: + def __init__(self, translations: Translations) -> None: + self._translations = translations + + self.create = to_streamed_response_wrapper( + translations.create, + ) + + +class AsyncTranslationsWithStreamingResponse: + def __init__(self, translations: AsyncTranslations) -> None: + self._translations = translations + + self.create = async_to_streamed_response_wrapper( + translations.create, + ) + + +def _get_response_format_type( + response_format: AudioResponseFormat | Omit, +) -> type[Translation | TranslationVerbose | str]: + return _get_translation_response_format_type(response_format, log=log) diff --git a/venv/lib/python3.12/site-packages/openai/resources/batches.py b/venv/lib/python3.12/site-packages/openai/resources/batches.py new file mode 100644 index 0000000..855ad86 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/batches.py @@ -0,0 +1,578 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx2 + +from .. import _legacy_response +from ..types import batch_list_params, batch_create_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import path_template, maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..pagination import SyncCursorPage, AsyncCursorPage +from ..types.batch import Batch +from .._base_client import AsyncPaginator, make_request_options +from ..types.shared_params.metadata import Metadata + +__all__ = ["Batches", "AsyncBatches"] + + +class Batches(SyncAPIResource): + """Create large batches of API requests to run asynchronously.""" + + @cached_property + def with_raw_response(self) -> BatchesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return BatchesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> BatchesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return BatchesWithStreamingResponse(self) + + def create( + self, + *, + completion_window: Literal["24h"], + endpoint: Literal[ + "/v1/responses", + "/v1/chat/completions", + "/v1/embeddings", + "/v1/completions", + "/v1/moderations", + "/v1/images/generations", + "/v1/images/edits", + "/v1/videos", + ], + input_file_id: str, + metadata: Optional[Metadata] | Omit = omit, + output_expires_after: batch_create_params.OutputExpiresAfter | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Batch: + """ + Creates and executes a batch from an uploaded file of requests + + Args: + completion_window: The time frame within which the batch should be processed. Currently only `24h` + is supported. + + endpoint: The endpoint to be used for all requests in the batch. Currently + `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, `/v1/completions`, + `/v1/moderations`, `/v1/images/generations`, `/v1/images/edits`, and + `/v1/videos` are supported. Note that `/v1/embeddings` batches are also + restricted to a maximum of 50,000 embedding inputs across all requests in the + batch. + + input_file_id: The ID of an uploaded file that contains requests for the new batch. + + See + [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) + for how to upload a file. + + Your input file must be formatted as a + [JSONL file](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file), + and must be uploaded with the purpose `batch`. The file can contain up to 50,000 + requests, and can be up to 200 MB in size. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + output_expires_after: The expiration policy for the output and/or error file that are generated for a + batch. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/batches", + body=maybe_transform( + { + "completion_window": completion_window, + "endpoint": endpoint, + "input_file_id": input_file_id, + "metadata": metadata, + "output_expires_after": output_expires_after, + }, + batch_create_params.BatchCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Batch, + ) + + def retrieve( + self, + batch_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Batch: + """ + Retrieves a batch. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + return self._get( + path_template("/batches/{batch_id}", batch_id=batch_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Batch, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Batch]: + """List your organization's batches. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/batches", + page=SyncCursorPage[Batch], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + batch_list_params.BatchListParams, + ), + security={"bearer_auth": True}, + ), + model=Batch, + ) + + def cancel( + self, + batch_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Batch: + """Cancels an in-progress batch. + + The batch will be in status `cancelling` for up to + 10 minutes, before changing to `cancelled`, where it will have partial results + (if any) available in the output file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + return self._post( + path_template("/batches/{batch_id}/cancel", batch_id=batch_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Batch, + ) + + +class AsyncBatches(AsyncAPIResource): + """Create large batches of API requests to run asynchronously.""" + + @cached_property + def with_raw_response(self) -> AsyncBatchesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncBatchesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncBatchesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncBatchesWithStreamingResponse(self) + + async def create( + self, + *, + completion_window: Literal["24h"], + endpoint: Literal[ + "/v1/responses", + "/v1/chat/completions", + "/v1/embeddings", + "/v1/completions", + "/v1/moderations", + "/v1/images/generations", + "/v1/images/edits", + "/v1/videos", + ], + input_file_id: str, + metadata: Optional[Metadata] | Omit = omit, + output_expires_after: batch_create_params.OutputExpiresAfter | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Batch: + """ + Creates and executes a batch from an uploaded file of requests + + Args: + completion_window: The time frame within which the batch should be processed. Currently only `24h` + is supported. + + endpoint: The endpoint to be used for all requests in the batch. Currently + `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, `/v1/completions`, + `/v1/moderations`, `/v1/images/generations`, `/v1/images/edits`, and + `/v1/videos` are supported. Note that `/v1/embeddings` batches are also + restricted to a maximum of 50,000 embedding inputs across all requests in the + batch. + + input_file_id: The ID of an uploaded file that contains requests for the new batch. + + See + [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) + for how to upload a file. + + Your input file must be formatted as a + [JSONL file](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file), + and must be uploaded with the purpose `batch`. The file can contain up to 50,000 + requests, and can be up to 200 MB in size. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + output_expires_after: The expiration policy for the output and/or error file that are generated for a + batch. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/batches", + body=await async_maybe_transform( + { + "completion_window": completion_window, + "endpoint": endpoint, + "input_file_id": input_file_id, + "metadata": metadata, + "output_expires_after": output_expires_after, + }, + batch_create_params.BatchCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Batch, + ) + + async def retrieve( + self, + batch_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Batch: + """ + Retrieves a batch. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + return await self._get( + path_template("/batches/{batch_id}", batch_id=batch_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Batch, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Batch, AsyncCursorPage[Batch]]: + """List your organization's batches. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/batches", + page=AsyncCursorPage[Batch], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + batch_list_params.BatchListParams, + ), + security={"bearer_auth": True}, + ), + model=Batch, + ) + + async def cancel( + self, + batch_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Batch: + """Cancels an in-progress batch. + + The batch will be in status `cancelling` for up to + 10 minutes, before changing to `cancelled`, where it will have partial results + (if any) available in the output file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + return await self._post( + path_template("/batches/{batch_id}/cancel", batch_id=batch_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Batch, + ) + + +class BatchesWithRawResponse: + def __init__(self, batches: Batches) -> None: + self._batches = batches + + self.create = _legacy_response.to_raw_response_wrapper( + batches.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + batches.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + batches.list, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + batches.cancel, + ) + + +class AsyncBatchesWithRawResponse: + def __init__(self, batches: AsyncBatches) -> None: + self._batches = batches + + self.create = _legacy_response.async_to_raw_response_wrapper( + batches.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + batches.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + batches.list, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + batches.cancel, + ) + + +class BatchesWithStreamingResponse: + def __init__(self, batches: Batches) -> None: + self._batches = batches + + self.create = to_streamed_response_wrapper( + batches.create, + ) + self.retrieve = to_streamed_response_wrapper( + batches.retrieve, + ) + self.list = to_streamed_response_wrapper( + batches.list, + ) + self.cancel = to_streamed_response_wrapper( + batches.cancel, + ) + + +class AsyncBatchesWithStreamingResponse: + def __init__(self, batches: AsyncBatches) -> None: + self._batches = batches + + self.create = async_to_streamed_response_wrapper( + batches.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + batches.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + batches.list, + ) + self.cancel = async_to_streamed_response_wrapper( + batches.cancel, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/beta/__init__.py new file mode 100644 index 0000000..f9788b6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/__init__.py @@ -0,0 +1,157 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .beta import ( + Beta as Beta, + AsyncBeta as AsyncBeta, + BetaWithRawResponse as BetaWithRawResponse, + AsyncBetaWithRawResponse as AsyncBetaWithRawResponse, + BetaWithStreamingResponse as BetaWithStreamingResponse, + AsyncBetaWithStreamingResponse as AsyncBetaWithStreamingResponse, + ) + from .agents import ( + Agents as Agents, + AsyncAgents as AsyncAgents, + AgentsWithRawResponse as AgentsWithRawResponse, + AsyncAgentsWithRawResponse as AsyncAgentsWithRawResponse, + AgentsWithStreamingResponse as AgentsWithStreamingResponse, + AsyncAgentsWithStreamingResponse as AsyncAgentsWithStreamingResponse, + ) + from .chatkit import ( + ChatKit as ChatKit, + AsyncChatKit as AsyncChatKit, + ChatKitWithRawResponse as ChatKitWithRawResponse, + AsyncChatKitWithRawResponse as AsyncChatKitWithRawResponse, + ChatKitWithStreamingResponse as ChatKitWithStreamingResponse, + AsyncChatKitWithStreamingResponse as AsyncChatKitWithStreamingResponse, + ) + from .threads import ( + Threads as Threads, + AsyncThreads as AsyncThreads, + ThreadsWithRawResponse as ThreadsWithRawResponse, + AsyncThreadsWithRawResponse as AsyncThreadsWithRawResponse, + ThreadsWithStreamingResponse as ThreadsWithStreamingResponse, + AsyncThreadsWithStreamingResponse as AsyncThreadsWithStreamingResponse, + ) + from .responses import ( + Responses as Responses, + AsyncResponses as AsyncResponses, + ResponsesWithRawResponse as ResponsesWithRawResponse, + AsyncResponsesWithRawResponse as AsyncResponsesWithRawResponse, + ResponsesWithStreamingResponse as ResponsesWithStreamingResponse, + AsyncResponsesWithStreamingResponse as AsyncResponsesWithStreamingResponse, + ) + from .assistants import ( + Assistants as Assistants, + AsyncAssistants as AsyncAssistants, + AssistantsWithRawResponse as AssistantsWithRawResponse, + AsyncAssistantsWithRawResponse as AsyncAssistantsWithRawResponse, + AssistantsWithStreamingResponse as AssistantsWithStreamingResponse, + AsyncAssistantsWithStreamingResponse as AsyncAssistantsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Agents": (".agents", "Agents"), + "AsyncAgents": (".agents", "AsyncAgents"), + "AgentsWithRawResponse": (".agents", "AgentsWithRawResponse"), + "AsyncAgentsWithRawResponse": (".agents", "AsyncAgentsWithRawResponse"), + "AgentsWithStreamingResponse": (".agents", "AgentsWithStreamingResponse"), + "AsyncAgentsWithStreamingResponse": (".agents", "AsyncAgentsWithStreamingResponse"), + "Responses": (".responses", "Responses"), + "AsyncResponses": (".responses", "AsyncResponses"), + "ResponsesWithRawResponse": (".responses", "ResponsesWithRawResponse"), + "AsyncResponsesWithRawResponse": (".responses", "AsyncResponsesWithRawResponse"), + "ResponsesWithStreamingResponse": (".responses", "ResponsesWithStreamingResponse"), + "AsyncResponsesWithStreamingResponse": (".responses", "AsyncResponsesWithStreamingResponse"), + "ChatKit": (".chatkit", "ChatKit"), + "AsyncChatKit": (".chatkit", "AsyncChatKit"), + "ChatKitWithRawResponse": (".chatkit", "ChatKitWithRawResponse"), + "AsyncChatKitWithRawResponse": (".chatkit", "AsyncChatKitWithRawResponse"), + "ChatKitWithStreamingResponse": (".chatkit", "ChatKitWithStreamingResponse"), + "AsyncChatKitWithStreamingResponse": (".chatkit", "AsyncChatKitWithStreamingResponse"), + "Assistants": (".assistants", "Assistants"), + "AsyncAssistants": (".assistants", "AsyncAssistants"), + "AssistantsWithRawResponse": (".assistants", "AssistantsWithRawResponse"), + "AsyncAssistantsWithRawResponse": (".assistants", "AsyncAssistantsWithRawResponse"), + "AssistantsWithStreamingResponse": (".assistants", "AssistantsWithStreamingResponse"), + "AsyncAssistantsWithStreamingResponse": (".assistants", "AsyncAssistantsWithStreamingResponse"), + "Threads": (".threads", "Threads"), + "AsyncThreads": (".threads", "AsyncThreads"), + "ThreadsWithRawResponse": (".threads", "ThreadsWithRawResponse"), + "AsyncThreadsWithRawResponse": (".threads", "AsyncThreadsWithRawResponse"), + "ThreadsWithStreamingResponse": (".threads", "ThreadsWithStreamingResponse"), + "AsyncThreadsWithStreamingResponse": (".threads", "AsyncThreadsWithStreamingResponse"), + "Beta": (".beta", "Beta"), + "AsyncBeta": (".beta", "AsyncBeta"), + "BetaWithRawResponse": (".beta", "BetaWithRawResponse"), + "AsyncBetaWithRawResponse": (".beta", "AsyncBetaWithRawResponse"), + "BetaWithStreamingResponse": (".beta", "BetaWithStreamingResponse"), + "AsyncBetaWithStreamingResponse": (".beta", "AsyncBetaWithStreamingResponse"), + } + _SUBMODULES = { + "agents", + "assistants", + "beta", + "chatkit", + "responses", + "threads", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Agents", + "AsyncAgents", + "AgentsWithRawResponse", + "AsyncAgentsWithRawResponse", + "AgentsWithStreamingResponse", + "AsyncAgentsWithStreamingResponse", + "Responses", + "AsyncResponses", + "ResponsesWithRawResponse", + "AsyncResponsesWithRawResponse", + "ResponsesWithStreamingResponse", + "AsyncResponsesWithStreamingResponse", + "ChatKit", + "AsyncChatKit", + "ChatKitWithRawResponse", + "AsyncChatKitWithRawResponse", + "ChatKitWithStreamingResponse", + "AsyncChatKitWithStreamingResponse", + "Assistants", + "AsyncAssistants", + "AssistantsWithRawResponse", + "AsyncAssistantsWithRawResponse", + "AssistantsWithStreamingResponse", + "AsyncAssistantsWithStreamingResponse", + "Threads", + "AsyncThreads", + "ThreadsWithRawResponse", + "AsyncThreadsWithRawResponse", + "ThreadsWithStreamingResponse", + "AsyncThreadsWithStreamingResponse", + "Beta", + "AsyncBeta", + "BetaWithRawResponse", + "AsyncBetaWithRawResponse", + "BetaWithStreamingResponse", + "AsyncBetaWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..284be04 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/__pycache__/assistants.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/__pycache__/assistants.cpython-312.pyc new file mode 100644 index 0000000..8dc8237 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/__pycache__/assistants.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/__pycache__/beta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/__pycache__/beta.cpython-312.pyc new file mode 100644 index 0000000..a74d975 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/__pycache__/beta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/__init__.py new file mode 100644 index 0000000..dc5ca3d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/__init__.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .agents import ( + Agents as Agents, + AsyncAgents as AsyncAgents, + AgentsWithRawResponse as AgentsWithRawResponse, + AsyncAgentsWithRawResponse as AsyncAgentsWithRawResponse, + AgentsWithStreamingResponse as AgentsWithStreamingResponse, + AsyncAgentsWithStreamingResponse as AsyncAgentsWithStreamingResponse, + ) + from .vaults import ( + Vaults as Vaults, + AsyncVaults as AsyncVaults, + VaultsWithRawResponse as VaultsWithRawResponse, + AsyncVaultsWithRawResponse as AsyncVaultsWithRawResponse, + VaultsWithStreamingResponse as VaultsWithStreamingResponse, + AsyncVaultsWithStreamingResponse as AsyncVaultsWithStreamingResponse, + ) + from .sessions import ( + Sessions as Sessions, + AsyncSessions as AsyncSessions, + SessionsWithRawResponse as SessionsWithRawResponse, + AsyncSessionsWithRawResponse as AsyncSessionsWithRawResponse, + SessionsWithStreamingResponse as SessionsWithStreamingResponse, + AsyncSessionsWithStreamingResponse as AsyncSessionsWithStreamingResponse, + ) + from .environments import ( + Environments as Environments, + AsyncEnvironments as AsyncEnvironments, + EnvironmentsWithRawResponse as EnvironmentsWithRawResponse, + AsyncEnvironmentsWithRawResponse as AsyncEnvironmentsWithRawResponse, + EnvironmentsWithStreamingResponse as EnvironmentsWithStreamingResponse, + AsyncEnvironmentsWithStreamingResponse as AsyncEnvironmentsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Environments": (".environments", "Environments"), + "AsyncEnvironments": (".environments", "AsyncEnvironments"), + "EnvironmentsWithRawResponse": (".environments", "EnvironmentsWithRawResponse"), + "AsyncEnvironmentsWithRawResponse": (".environments", "AsyncEnvironmentsWithRawResponse"), + "EnvironmentsWithStreamingResponse": (".environments", "EnvironmentsWithStreamingResponse"), + "AsyncEnvironmentsWithStreamingResponse": (".environments", "AsyncEnvironmentsWithStreamingResponse"), + "Vaults": (".vaults", "Vaults"), + "AsyncVaults": (".vaults", "AsyncVaults"), + "VaultsWithRawResponse": (".vaults", "VaultsWithRawResponse"), + "AsyncVaultsWithRawResponse": (".vaults", "AsyncVaultsWithRawResponse"), + "VaultsWithStreamingResponse": (".vaults", "VaultsWithStreamingResponse"), + "AsyncVaultsWithStreamingResponse": (".vaults", "AsyncVaultsWithStreamingResponse"), + "Sessions": (".sessions", "Sessions"), + "AsyncSessions": (".sessions", "AsyncSessions"), + "SessionsWithRawResponse": (".sessions", "SessionsWithRawResponse"), + "AsyncSessionsWithRawResponse": (".sessions", "AsyncSessionsWithRawResponse"), + "SessionsWithStreamingResponse": (".sessions", "SessionsWithStreamingResponse"), + "AsyncSessionsWithStreamingResponse": (".sessions", "AsyncSessionsWithStreamingResponse"), + "Agents": (".agents", "Agents"), + "AsyncAgents": (".agents", "AsyncAgents"), + "AgentsWithRawResponse": (".agents", "AgentsWithRawResponse"), + "AsyncAgentsWithRawResponse": (".agents", "AsyncAgentsWithRawResponse"), + "AgentsWithStreamingResponse": (".agents", "AgentsWithStreamingResponse"), + "AsyncAgentsWithStreamingResponse": (".agents", "AsyncAgentsWithStreamingResponse"), + } + _SUBMODULES = { + "agents", + "environments", + "sessions", + "vaults", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Environments", + "AsyncEnvironments", + "EnvironmentsWithRawResponse", + "AsyncEnvironmentsWithRawResponse", + "EnvironmentsWithStreamingResponse", + "AsyncEnvironmentsWithStreamingResponse", + "Vaults", + "AsyncVaults", + "VaultsWithRawResponse", + "AsyncVaultsWithRawResponse", + "VaultsWithStreamingResponse", + "AsyncVaultsWithStreamingResponse", + "Sessions", + "AsyncSessions", + "SessionsWithRawResponse", + "AsyncSessionsWithRawResponse", + "SessionsWithStreamingResponse", + "AsyncSessionsWithStreamingResponse", + "Agents", + "AsyncAgents", + "AgentsWithRawResponse", + "AsyncAgentsWithRawResponse", + "AgentsWithStreamingResponse", + "AsyncAgentsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..fd8bee5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/__pycache__/agents.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/__pycache__/agents.cpython-312.pyc new file mode 100644 index 0000000..d0eaa8f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/__pycache__/agents.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/agents.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/agents.py new file mode 100644 index 0000000..69c71bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/agents.py @@ -0,0 +1,886 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable, Optional +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ....types.beta import agent_list_params, agent_create_params, agent_update_params +from .vaults.vaults import ( + Vaults, + AsyncVaults, + VaultsWithRawResponse, + AsyncVaultsWithRawResponse, + VaultsWithStreamingResponse, + AsyncVaultsWithStreamingResponse, +) +from ...._base_client import AsyncPaginator, make_request_options +from .sessions.sessions import ( + Sessions, + AsyncSessions, + SessionsWithRawResponse, + AsyncSessionsWithRawResponse, + SessionsWithStreamingResponse, + AsyncSessionsWithStreamingResponse, +) +from ....types.beta.agent import Agent +from .environments.environments import ( + Environments, + AsyncEnvironments, + EnvironmentsWithRawResponse, + AsyncEnvironmentsWithRawResponse, + EnvironmentsWithStreamingResponse, + AsyncEnvironmentsWithStreamingResponse, +) +from ....types.beta.agent_deleted import AgentDeleted +from ....types.beta.agent_text_param import AgentTextParam +from ....types.beta.agent_reasoning_param import AgentReasoningParam +from ....types.beta.multi_agent_config_param import MultiAgentConfigParam +from ....types.beta.persisted_agent_tool_param import PersistedAgentToolParam + +__all__ = ["Agents", "AsyncAgents"] + + +class Agents(SyncAPIResource): + @cached_property + def environments(self) -> Environments: + return Environments(self._client) + + @cached_property + def vaults(self) -> Vaults: + return Vaults(self._client) + + @cached_property + def sessions(self) -> Sessions: + return Sessions(self._client) + + @cached_property + def with_raw_response(self) -> AgentsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AgentsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AgentsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AgentsWithStreamingResponse(self) + + def create( + self, + *, + model: str, + instructions: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + multi_agent: Optional[MultiAgentConfigParam] | Omit = omit, + name: Optional[str] | Omit = omit, + reasoning: Optional[AgentReasoningParam] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast", "ultrafast"]] | Omit = omit, + text: Optional[AgentTextParam] | Omit = omit, + tools: Optional[Iterable[PersistedAgentToolParam]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Agent: + """Creates a reusable agent without storing credentials. + + See + [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + + Args: + model: The model to use for the agent. The requested model name is preserved. + + instructions: Additional instructions appended to the agent's default base instructions. Omit + or set to null to add no custom instructions. + + metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. Omission or null defaults to an empty map. + + multi_agent: Configuration for creating and coordinating subagents. Subagent tools are + disabled by default. + + name: A human-readable name for the agent. Omission or null leaves the agent unnamed. + + reasoning: Configuration for model reasoning. Omission uses the model's default effort. + + service_tier: The service tier used for model requests. Defaults to `auto`. + + - `auto` - Selects the service tier automatically. + - `default` - Uses the default service tier. + - `flex` - Uses the flex service tier. + - `priority` - Uses the priority service tier. + - `fast` - Uses the fast service tier. + - `ultrafast` - Uses the ultrafast service tier. + + text: Configuration for generated text. Defaults to the `text` format and medium + verbosity. + + tools: Tools available to the agent. Defaults to an empty list. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._post( + "/agents", + body=maybe_transform( + { + "model": model, + "instructions": instructions, + "metadata": metadata, + "multi_agent": multi_agent, + "name": name, + "reasoning": reasoning, + "service_tier": service_tier, + "text": text, + "tools": tools, + }, + agent_create_params.AgentCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Agent, + ) + + def retrieve( + self, + agent_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Agent: + """Retrieves a reusable agent by ID. + + See + [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_id: + raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get( + path_template("/agents/{agent_id}", agent_id=agent_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Agent, + ) + + def update( + self, + agent_id: str, + *, + instructions: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: str | Omit = omit, + multi_agent: Optional[MultiAgentConfigParam] | Omit = omit, + name: Optional[str] | Omit = omit, + reasoning: Optional[AgentReasoningParam] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast", "ultrafast"]] | Omit = omit, + text: Optional[AgentTextParam] | Omit = omit, + tools: Optional[Iterable[PersistedAgentToolParam]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Agent: + """Updates a reusable agent. + + See + [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + + Args: + instructions: Additional instructions appended to the agent's default base instructions. Omit + to leave unchanged. + + metadata: Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it. + Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. + + model: The model to use for the agent. The requested model name is preserved. + + multi_agent: Configuration for creating and coordinating subagents. + + name: A replacement name. Omit to leave unchanged, or pass null to clear it. + + reasoning: Configuration for model reasoning. Omit to keep the current settings; pass + `null` to reset to the model's default effort. + + service_tier: The service tier used for model requests. + + - `auto` - Selects the service tier automatically. + - `default` - Uses the default service tier. + - `flex` - Uses the flex service tier. + - `priority` - Uses the priority service tier. + - `fast` - Uses the fast service tier. + - `ultrafast` - Uses the ultrafast service tier. + + text: Configuration for text generated by the agent. + + tools: Tools available to the agent. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_id: + raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._post( + path_template("/agents/{agent_id}", agent_id=agent_id), + body=maybe_transform( + { + "instructions": instructions, + "metadata": metadata, + "model": model, + "multi_agent": multi_agent, + "name": name, + "reasoning": reasoning, + "service_tier": service_tier, + "text": text, + "tools": tools, + }, + agent_update_params.AgentUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Agent, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: Optional[int] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Agent]: + """Lists reusable agents in the current project. + + See + [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + "/agents", + page=SyncCursorPage[Agent], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + agent_list_params.AgentListParams, + ), + security={"bearer_auth": True}, + ), + model=Agent, + ) + + def delete( + self, + agent_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentDeleted: + """Deletes a reusable agent. + + See + [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_id: + raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._delete( + path_template("/agents/{agent_id}", agent_id=agent_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=AgentDeleted, + ) + + +class AsyncAgents(AsyncAPIResource): + @cached_property + def environments(self) -> AsyncEnvironments: + return AsyncEnvironments(self._client) + + @cached_property + def vaults(self) -> AsyncVaults: + return AsyncVaults(self._client) + + @cached_property + def sessions(self) -> AsyncSessions: + return AsyncSessions(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAgentsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAgentsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAgentsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAgentsWithStreamingResponse(self) + + async def create( + self, + *, + model: str, + instructions: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + multi_agent: Optional[MultiAgentConfigParam] | Omit = omit, + name: Optional[str] | Omit = omit, + reasoning: Optional[AgentReasoningParam] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast", "ultrafast"]] | Omit = omit, + text: Optional[AgentTextParam] | Omit = omit, + tools: Optional[Iterable[PersistedAgentToolParam]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Agent: + """Creates a reusable agent without storing credentials. + + See + [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + + Args: + model: The model to use for the agent. The requested model name is preserved. + + instructions: Additional instructions appended to the agent's default base instructions. Omit + or set to null to add no custom instructions. + + metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. Omission or null defaults to an empty map. + + multi_agent: Configuration for creating and coordinating subagents. Subagent tools are + disabled by default. + + name: A human-readable name for the agent. Omission or null leaves the agent unnamed. + + reasoning: Configuration for model reasoning. Omission uses the model's default effort. + + service_tier: The service tier used for model requests. Defaults to `auto`. + + - `auto` - Selects the service tier automatically. + - `default` - Uses the default service tier. + - `flex` - Uses the flex service tier. + - `priority` - Uses the priority service tier. + - `fast` - Uses the fast service tier. + - `ultrafast` - Uses the ultrafast service tier. + + text: Configuration for generated text. Defaults to the `text` format and medium + verbosity. + + tools: Tools available to the agent. Defaults to an empty list. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._post( + "/agents", + body=await async_maybe_transform( + { + "model": model, + "instructions": instructions, + "metadata": metadata, + "multi_agent": multi_agent, + "name": name, + "reasoning": reasoning, + "service_tier": service_tier, + "text": text, + "tools": tools, + }, + agent_create_params.AgentCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Agent, + ) + + async def retrieve( + self, + agent_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Agent: + """Retrieves a reusable agent by ID. + + See + [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_id: + raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._get( + path_template("/agents/{agent_id}", agent_id=agent_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Agent, + ) + + async def update( + self, + agent_id: str, + *, + instructions: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: str | Omit = omit, + multi_agent: Optional[MultiAgentConfigParam] | Omit = omit, + name: Optional[str] | Omit = omit, + reasoning: Optional[AgentReasoningParam] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast", "ultrafast"]] | Omit = omit, + text: Optional[AgentTextParam] | Omit = omit, + tools: Optional[Iterable[PersistedAgentToolParam]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Agent: + """Updates a reusable agent. + + See + [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + + Args: + instructions: Additional instructions appended to the agent's default base instructions. Omit + to leave unchanged. + + metadata: Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it. + Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. + + model: The model to use for the agent. The requested model name is preserved. + + multi_agent: Configuration for creating and coordinating subagents. + + name: A replacement name. Omit to leave unchanged, or pass null to clear it. + + reasoning: Configuration for model reasoning. Omit to keep the current settings; pass + `null` to reset to the model's default effort. + + service_tier: The service tier used for model requests. + + - `auto` - Selects the service tier automatically. + - `default` - Uses the default service tier. + - `flex` - Uses the flex service tier. + - `priority` - Uses the priority service tier. + - `fast` - Uses the fast service tier. + - `ultrafast` - Uses the ultrafast service tier. + + text: Configuration for text generated by the agent. + + tools: Tools available to the agent. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_id: + raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._post( + path_template("/agents/{agent_id}", agent_id=agent_id), + body=await async_maybe_transform( + { + "instructions": instructions, + "metadata": metadata, + "model": model, + "multi_agent": multi_agent, + "name": name, + "reasoning": reasoning, + "service_tier": service_tier, + "text": text, + "tools": tools, + }, + agent_update_params.AgentUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Agent, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: Optional[int] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Agent, AsyncCursorPage[Agent]]: + """Lists reusable agents in the current project. + + See + [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + "/agents", + page=AsyncCursorPage[Agent], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + agent_list_params.AgentListParams, + ), + security={"bearer_auth": True}, + ), + model=Agent, + ) + + async def delete( + self, + agent_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentDeleted: + """Deletes a reusable agent. + + See + [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not agent_id: + raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._delete( + path_template("/agents/{agent_id}", agent_id=agent_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=AgentDeleted, + ) + + +class AgentsWithRawResponse: + def __init__(self, agents: Agents) -> None: + self._agents = agents + + self.create = _legacy_response.to_raw_response_wrapper( + agents.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + agents.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + agents.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + agents.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + agents.delete, + ) + + @cached_property + def environments(self) -> EnvironmentsWithRawResponse: + return EnvironmentsWithRawResponse(self._agents.environments) + + @cached_property + def vaults(self) -> VaultsWithRawResponse: + return VaultsWithRawResponse(self._agents.vaults) + + @cached_property + def sessions(self) -> SessionsWithRawResponse: + return SessionsWithRawResponse(self._agents.sessions) + + +class AsyncAgentsWithRawResponse: + def __init__(self, agents: AsyncAgents) -> None: + self._agents = agents + + self.create = _legacy_response.async_to_raw_response_wrapper( + agents.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + agents.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + agents.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + agents.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + agents.delete, + ) + + @cached_property + def environments(self) -> AsyncEnvironmentsWithRawResponse: + return AsyncEnvironmentsWithRawResponse(self._agents.environments) + + @cached_property + def vaults(self) -> AsyncVaultsWithRawResponse: + return AsyncVaultsWithRawResponse(self._agents.vaults) + + @cached_property + def sessions(self) -> AsyncSessionsWithRawResponse: + return AsyncSessionsWithRawResponse(self._agents.sessions) + + +class AgentsWithStreamingResponse: + def __init__(self, agents: Agents) -> None: + self._agents = agents + + self.create = to_streamed_response_wrapper( + agents.create, + ) + self.retrieve = to_streamed_response_wrapper( + agents.retrieve, + ) + self.update = to_streamed_response_wrapper( + agents.update, + ) + self.list = to_streamed_response_wrapper( + agents.list, + ) + self.delete = to_streamed_response_wrapper( + agents.delete, + ) + + @cached_property + def environments(self) -> EnvironmentsWithStreamingResponse: + return EnvironmentsWithStreamingResponse(self._agents.environments) + + @cached_property + def vaults(self) -> VaultsWithStreamingResponse: + return VaultsWithStreamingResponse(self._agents.vaults) + + @cached_property + def sessions(self) -> SessionsWithStreamingResponse: + return SessionsWithStreamingResponse(self._agents.sessions) + + +class AsyncAgentsWithStreamingResponse: + def __init__(self, agents: AsyncAgents) -> None: + self._agents = agents + + self.create = async_to_streamed_response_wrapper( + agents.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + agents.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + agents.update, + ) + self.list = async_to_streamed_response_wrapper( + agents.list, + ) + self.delete = async_to_streamed_response_wrapper( + agents.delete, + ) + + @cached_property + def environments(self) -> AsyncEnvironmentsWithStreamingResponse: + return AsyncEnvironmentsWithStreamingResponse(self._agents.environments) + + @cached_property + def vaults(self) -> AsyncVaultsWithStreamingResponse: + return AsyncVaultsWithStreamingResponse(self._agents.vaults) + + @cached_property + def sessions(self) -> AsyncSessionsWithStreamingResponse: + return AsyncSessionsWithStreamingResponse(self._agents.sessions) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__init__.py new file mode 100644 index 0000000..34a12b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__init__.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .files import ( + Files as Files, + AsyncFiles as AsyncFiles, + FilesWithRawResponse as FilesWithRawResponse, + AsyncFilesWithRawResponse as AsyncFilesWithRawResponse, + FilesWithStreamingResponse as FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse as AsyncFilesWithStreamingResponse, + ) + from .templates import ( + Templates as Templates, + AsyncTemplates as AsyncTemplates, + TemplatesWithRawResponse as TemplatesWithRawResponse, + AsyncTemplatesWithRawResponse as AsyncTemplatesWithRawResponse, + TemplatesWithStreamingResponse as TemplatesWithStreamingResponse, + AsyncTemplatesWithStreamingResponse as AsyncTemplatesWithStreamingResponse, + ) + from .environments import ( + Environments as Environments, + AsyncEnvironments as AsyncEnvironments, + EnvironmentsWithRawResponse as EnvironmentsWithRawResponse, + AsyncEnvironmentsWithRawResponse as AsyncEnvironmentsWithRawResponse, + EnvironmentsWithStreamingResponse as EnvironmentsWithStreamingResponse, + AsyncEnvironmentsWithStreamingResponse as AsyncEnvironmentsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Files": (".files", "Files"), + "AsyncFiles": (".files", "AsyncFiles"), + "FilesWithRawResponse": (".files", "FilesWithRawResponse"), + "AsyncFilesWithRawResponse": (".files", "AsyncFilesWithRawResponse"), + "FilesWithStreamingResponse": (".files", "FilesWithStreamingResponse"), + "AsyncFilesWithStreamingResponse": (".files", "AsyncFilesWithStreamingResponse"), + "Templates": (".templates", "Templates"), + "AsyncTemplates": (".templates", "AsyncTemplates"), + "TemplatesWithRawResponse": (".templates", "TemplatesWithRawResponse"), + "AsyncTemplatesWithRawResponse": (".templates", "AsyncTemplatesWithRawResponse"), + "TemplatesWithStreamingResponse": (".templates", "TemplatesWithStreamingResponse"), + "AsyncTemplatesWithStreamingResponse": (".templates", "AsyncTemplatesWithStreamingResponse"), + "Environments": (".environments", "Environments"), + "AsyncEnvironments": (".environments", "AsyncEnvironments"), + "EnvironmentsWithRawResponse": (".environments", "EnvironmentsWithRawResponse"), + "AsyncEnvironmentsWithRawResponse": (".environments", "AsyncEnvironmentsWithRawResponse"), + "EnvironmentsWithStreamingResponse": (".environments", "EnvironmentsWithStreamingResponse"), + "AsyncEnvironmentsWithStreamingResponse": (".environments", "AsyncEnvironmentsWithStreamingResponse"), + } + _SUBMODULES = { + "environments", + "files", + "templates", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Files", + "AsyncFiles", + "FilesWithRawResponse", + "AsyncFilesWithRawResponse", + "FilesWithStreamingResponse", + "AsyncFilesWithStreamingResponse", + "Templates", + "AsyncTemplates", + "TemplatesWithRawResponse", + "AsyncTemplatesWithRawResponse", + "TemplatesWithStreamingResponse", + "AsyncTemplatesWithStreamingResponse", + "Environments", + "AsyncEnvironments", + "EnvironmentsWithRawResponse", + "AsyncEnvironmentsWithRawResponse", + "EnvironmentsWithStreamingResponse", + "AsyncEnvironmentsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..79427a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__pycache__/environments.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__pycache__/environments.cpython-312.pyc new file mode 100644 index 0000000..cc1cda8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__pycache__/environments.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__pycache__/files.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__pycache__/files.cpython-312.pyc new file mode 100644 index 0000000..5ec9ed4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__pycache__/files.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__pycache__/templates.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__pycache__/templates.cpython-312.pyc new file mode 100644 index 0000000..a95f9dc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/__pycache__/templates.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/environments.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/environments.py new file mode 100644 index 0000000..6bd4105 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/environments.py @@ -0,0 +1,238 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from ..... import _legacy_response +from .files import ( + Files, + AsyncFiles, + FilesWithRawResponse, + AsyncFilesWithRawResponse, + FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse, +) +from .templates import ( + Templates, + AsyncTemplates, + TemplatesWithRawResponse, + AsyncTemplatesWithRawResponse, + TemplatesWithStreamingResponse, + AsyncTemplatesWithStreamingResponse, +) +from ....._types import Body, Query, Headers, NotGiven, not_given +from ....._utils import path_template +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._base_client import make_request_options +from .....types.beta.agents.environment_info import EnvironmentInfo + +__all__ = ["Environments", "AsyncEnvironments"] + + +class Environments(SyncAPIResource): + @cached_property + def files(self) -> Files: + return Files(self._client) + + @cached_property + def templates(self) -> Templates: + return Templates(self._client) + + @cached_property + def with_raw_response(self) -> EnvironmentsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return EnvironmentsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EnvironmentsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return EnvironmentsWithStreamingResponse(self) + + def retrieve( + self, + environment_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentInfo: + """ + Retrieves an execution environment's connection status and safe installed + metadata. See + [environment lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not environment_id: + raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get( + path_template("/agents/environments/{environment_id}", environment_id=environment_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EnvironmentInfo, + ) + + +class AsyncEnvironments(AsyncAPIResource): + @cached_property + def files(self) -> AsyncFiles: + return AsyncFiles(self._client) + + @cached_property + def templates(self) -> AsyncTemplates: + return AsyncTemplates(self._client) + + @cached_property + def with_raw_response(self) -> AsyncEnvironmentsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncEnvironmentsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEnvironmentsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncEnvironmentsWithStreamingResponse(self) + + async def retrieve( + self, + environment_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentInfo: + """ + Retrieves an execution environment's connection status and safe installed + metadata. See + [environment lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not environment_id: + raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._get( + path_template("/agents/environments/{environment_id}", environment_id=environment_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EnvironmentInfo, + ) + + +class EnvironmentsWithRawResponse: + def __init__(self, environments: Environments) -> None: + self._environments = environments + + self.retrieve = _legacy_response.to_raw_response_wrapper( + environments.retrieve, + ) + + @cached_property + def files(self) -> FilesWithRawResponse: + return FilesWithRawResponse(self._environments.files) + + @cached_property + def templates(self) -> TemplatesWithRawResponse: + return TemplatesWithRawResponse(self._environments.templates) + + +class AsyncEnvironmentsWithRawResponse: + def __init__(self, environments: AsyncEnvironments) -> None: + self._environments = environments + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + environments.retrieve, + ) + + @cached_property + def files(self) -> AsyncFilesWithRawResponse: + return AsyncFilesWithRawResponse(self._environments.files) + + @cached_property + def templates(self) -> AsyncTemplatesWithRawResponse: + return AsyncTemplatesWithRawResponse(self._environments.templates) + + +class EnvironmentsWithStreamingResponse: + def __init__(self, environments: Environments) -> None: + self._environments = environments + + self.retrieve = to_streamed_response_wrapper( + environments.retrieve, + ) + + @cached_property + def files(self) -> FilesWithStreamingResponse: + return FilesWithStreamingResponse(self._environments.files) + + @cached_property + def templates(self) -> TemplatesWithStreamingResponse: + return TemplatesWithStreamingResponse(self._environments.templates) + + +class AsyncEnvironmentsWithStreamingResponse: + def __init__(self, environments: AsyncEnvironments) -> None: + self._environments = environments + + self.retrieve = async_to_streamed_response_wrapper( + environments.retrieve, + ) + + @cached_property + def files(self) -> AsyncFilesWithStreamingResponse: + return AsyncFilesWithStreamingResponse(self._environments.files) + + @cached_property + def templates(self) -> AsyncTemplatesWithStreamingResponse: + return AsyncTemplatesWithStreamingResponse(self._environments.templates) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/files.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/files.py new file mode 100644 index 0000000..b2ad891 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/files.py @@ -0,0 +1,469 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, overload + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, required_args, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncTokenPage, AsyncTokenPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.beta.agents.environments import file_list_params, file_create_params +from .....types.beta.agents.environments.environment_file import EnvironmentFile + +__all__ = ["Files", "AsyncFiles"] + + +class Files(SyncAPIResource): + @cached_property + def with_raw_response(self) -> FilesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return FilesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FilesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return FilesWithStreamingResponse(self) + + @overload + def create( + self, + environment_id: str, + *, + file_id: str, + path: str, + type: Literal["file_id"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentFile: + """ + Copies inline bytes or a Files API file into a connected execution environment. + See + [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files). + + Args: + file_id: The ID of the uploaded file. + + path: The absolute destination path inside `/workspace`. + + type: The type of the object. Always `file_id`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + environment_id: str, + *, + data: str, + path: str, + type: Literal["inline"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentFile: + """ + Copies inline bytes or a Files API file into a connected execution environment. + See + [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files). + + Args: + data: The standard-base64-encoded file contents. + + path: The absolute destination path inside `/workspace`. + + type: The type of the object. Always `inline`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["file_id", "path", "type"], ["data", "path", "type"]) + def create( + self, + environment_id: str, + *, + file_id: str | Omit = omit, + path: str, + type: Literal["file_id"] | Literal["inline"], + data: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentFile: + if not environment_id: + raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._post( + path_template("/agents/environments/{environment_id}/files", environment_id=environment_id), + body=maybe_transform( + { + "file_id": file_id, + "path": path, + "type": type, + "data": data, + }, + file_create_params.FileCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EnvironmentFile, + ) + + def list( + self, + environment_id: str, + *, + limit: Optional[int] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + page: str | Omit = omit, + path: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncTokenPage[EnvironmentFile]: + """ + Lists live files on a connected execution environment with optional directory + filtering and opaque cursor pagination. See + [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files). + + Args: + limit: The maximum number of files to return, between 1 and 100. + + order: Sort by case-sensitive path components. Defaults to descending. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + page: The opaque token from the previous page. Keep the same path, order, and limit. + + path: Restrict the listing to this absolute workspace directory. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not environment_id: + raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/agents/environments/{environment_id}/files", environment_id=environment_id), + page=SyncTokenPage[EnvironmentFile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "order": order, + "page": page, + "path": path, + }, + file_list_params.FileListParams, + ), + security={"bearer_auth": True}, + ), + model=EnvironmentFile, + ) + + +class AsyncFiles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncFilesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncFilesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFilesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncFilesWithStreamingResponse(self) + + @overload + async def create( + self, + environment_id: str, + *, + file_id: str, + path: str, + type: Literal["file_id"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentFile: + """ + Copies inline bytes or a Files API file into a connected execution environment. + See + [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files). + + Args: + file_id: The ID of the uploaded file. + + path: The absolute destination path inside `/workspace`. + + type: The type of the object. Always `file_id`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + environment_id: str, + *, + data: str, + path: str, + type: Literal["inline"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentFile: + """ + Copies inline bytes or a Files API file into a connected execution environment. + See + [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files). + + Args: + data: The standard-base64-encoded file contents. + + path: The absolute destination path inside `/workspace`. + + type: The type of the object. Always `inline`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["file_id", "path", "type"], ["data", "path", "type"]) + async def create( + self, + environment_id: str, + *, + file_id: str | Omit = omit, + path: str, + type: Literal["file_id"] | Literal["inline"], + data: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentFile: + if not environment_id: + raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._post( + path_template("/agents/environments/{environment_id}/files", environment_id=environment_id), + body=await async_maybe_transform( + { + "file_id": file_id, + "path": path, + "type": type, + "data": data, + }, + file_create_params.FileCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EnvironmentFile, + ) + + def list( + self, + environment_id: str, + *, + limit: Optional[int] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + page: str | Omit = omit, + path: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[EnvironmentFile, AsyncTokenPage[EnvironmentFile]]: + """ + Lists live files on a connected execution environment with optional directory + filtering and opaque cursor pagination. See + [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files). + + Args: + limit: The maximum number of files to return, between 1 and 100. + + order: Sort by case-sensitive path components. Defaults to descending. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + page: The opaque token from the previous page. Keep the same path, order, and limit. + + path: Restrict the listing to this absolute workspace directory. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not environment_id: + raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/agents/environments/{environment_id}/files", environment_id=environment_id), + page=AsyncTokenPage[EnvironmentFile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "order": order, + "page": page, + "path": path, + }, + file_list_params.FileListParams, + ), + security={"bearer_auth": True}, + ), + model=EnvironmentFile, + ) + + +class FilesWithRawResponse: + def __init__(self, files: Files) -> None: + self._files = files + + self.create = _legacy_response.to_raw_response_wrapper( + files.create, + ) + self.list = _legacy_response.to_raw_response_wrapper( + files.list, + ) + + +class AsyncFilesWithRawResponse: + def __init__(self, files: AsyncFiles) -> None: + self._files = files + + self.create = _legacy_response.async_to_raw_response_wrapper( + files.create, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + files.list, + ) + + +class FilesWithStreamingResponse: + def __init__(self, files: Files) -> None: + self._files = files + + self.create = to_streamed_response_wrapper( + files.create, + ) + self.list = to_streamed_response_wrapper( + files.list, + ) + + +class AsyncFilesWithStreamingResponse: + def __init__(self, files: AsyncFiles) -> None: + self._files = files + + self.create = async_to_streamed_response_wrapper( + files.create, + ) + self.list = async_to_streamed_response_wrapper( + files.list, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/templates.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/templates.py new file mode 100644 index 0000000..fa9a2f3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/environments/templates.py @@ -0,0 +1,786 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable, Optional +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.beta.hosted_skill_param import HostedSkillParam +from .....types.beta.agents.environments import template_list_params, template_create_params, template_update_params +from .....types.beta.hosted_plugin_param import HostedPluginParam +from .....types.beta.setup_command_param import SetupCommandParam +from .....types.beta.hosted_environment_file_param import HostedEnvironmentFileParam +from .....types.beta.agents.environments.environment_template import EnvironmentTemplate +from .....types.beta.agents.environments.environment_template_deleted import EnvironmentTemplateDeleted + +__all__ = ["Templates", "AsyncTemplates"] + + +class Templates(SyncAPIResource): + @cached_property + def with_raw_response(self) -> TemplatesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return TemplatesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TemplatesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return TemplatesWithStreamingResponse(self) + + def create( + self, + *, + capability_directories: Optional[SequenceNotStr[str]] | Omit = omit, + env: Optional[Dict[str, str]] | Omit = omit, + files: Optional[Iterable[HostedEnvironmentFileParam]] | Omit = omit, + name: Optional[str] | Omit = omit, + network: Optional[template_create_params.Network] | Omit = omit, + packages: Optional[template_create_params.Packages] | Omit = omit, + plugins: Optional[Iterable[HostedPluginParam]] | Omit = omit, + setup_commands: Optional[Iterable[SetupCommandParam]] | Omit = omit, + skills: Optional[Iterable[HostedSkillParam]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentTemplate: + """ + Creates reusable environment configuration without returning confidential setup + commands or environment values. See + [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + + Args: + capability_directories: Directories that contain capabilities exposed to the agent. Defaults to an empty + list. + + env: Environment variables made available to the agent. + + files: Files available before the agent starts. Defaults to an empty list. + + name: An optional human-readable display name for the template. + + network: Network access policy for the environment. Defaults to disabled for GA requests + and enabled for beta requests. + + packages: Packages to install in the environment. Defaults to empty package lists. + + plugins: Plugins provided as inline ZIP archives. Defaults to an empty list. + + setup_commands: Ordered, confidential setup commands. Command bodies are never returned. + + skills: Skills referenced by ID or provided as inline ZIP archives. Defaults to an empty + list. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._post( + "/agents/environments/templates", + body=maybe_transform( + { + "capability_directories": capability_directories, + "env": env, + "files": files, + "name": name, + "network": network, + "packages": packages, + "plugins": plugins, + "setup_commands": setup_commands, + "skills": skills, + }, + template_create_params.TemplateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EnvironmentTemplate, + ) + + def retrieve( + self, + environment_template_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentTemplate: + """ + Retrieves reusable environment configuration without returning confidential + values. See + [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not environment_template_id: + raise ValueError( + f"Expected a non-empty value for `environment_template_id` but received {environment_template_id!r}" + ) + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get( + path_template( + "/agents/environments/templates/{environment_template_id}", + environment_template_id=environment_template_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EnvironmentTemplate, + ) + + def update( + self, + environment_template_id: str, + *, + capability_directories: Optional[SequenceNotStr[str]] | Omit = omit, + env: Optional[Dict[str, str]] | Omit = omit, + files: Optional[Iterable[HostedEnvironmentFileParam]] | Omit = omit, + name: Optional[str] | Omit = omit, + network: Optional[template_update_params.Network] | Omit = omit, + packages: Optional[template_update_params.Packages] | Omit = omit, + plugins: Optional[Iterable[HostedPluginParam]] | Omit = omit, + setup_commands: Optional[Iterable[SetupCommandParam]] | Omit = omit, + skills: Optional[Iterable[HostedSkillParam]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentTemplate: + """ + Updates reusable environment configuration without returning confidential + values. See + [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + + Args: + capability_directories: Directories that expose capabilities to the agent. + + env: Replacement confidential environment values. + + files: Replacement file configuration materialized for each new session. + + name: A replacement human-readable display name, or `null` to clear the name. + + network: Network access available after setup completes. Omit to preserve the current + policy, or pass `null` to reset to disabled for GA requests or enabled for beta + requests. + + packages: Packages installed before the runtime network policy applies. + + plugins: Replacement plugin configuration installed for each new session. + + setup_commands: Replacement confidential setup commands, never included in returned resources. + + skills: Replacement skill configuration installed for each new session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not environment_template_id: + raise ValueError( + f"Expected a non-empty value for `environment_template_id` but received {environment_template_id!r}" + ) + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._post( + path_template( + "/agents/environments/templates/{environment_template_id}", + environment_template_id=environment_template_id, + ), + body=maybe_transform( + { + "capability_directories": capability_directories, + "env": env, + "files": files, + "name": name, + "network": network, + "packages": packages, + "plugins": plugins, + "setup_commands": setup_commands, + "skills": skills, + }, + template_update_params.TemplateUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EnvironmentTemplate, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[EnvironmentTemplate]: + """Lists reusable environment templates without returning confidential values. + + See + [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + "/agents/environments/templates", + page=SyncCursorPage[EnvironmentTemplate], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + template_list_params.TemplateListParams, + ), + security={"bearer_auth": True}, + ), + model=EnvironmentTemplate, + ) + + def delete( + self, + environment_template_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentTemplateDeleted: + """ + Deletes reusable environment configuration and all confidential template inputs. + See + [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not environment_template_id: + raise ValueError( + f"Expected a non-empty value for `environment_template_id` but received {environment_template_id!r}" + ) + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._delete( + path_template( + "/agents/environments/templates/{environment_template_id}", + environment_template_id=environment_template_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EnvironmentTemplateDeleted, + ) + + +class AsyncTemplates(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncTemplatesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncTemplatesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTemplatesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncTemplatesWithStreamingResponse(self) + + async def create( + self, + *, + capability_directories: Optional[SequenceNotStr[str]] | Omit = omit, + env: Optional[Dict[str, str]] | Omit = omit, + files: Optional[Iterable[HostedEnvironmentFileParam]] | Omit = omit, + name: Optional[str] | Omit = omit, + network: Optional[template_create_params.Network] | Omit = omit, + packages: Optional[template_create_params.Packages] | Omit = omit, + plugins: Optional[Iterable[HostedPluginParam]] | Omit = omit, + setup_commands: Optional[Iterable[SetupCommandParam]] | Omit = omit, + skills: Optional[Iterable[HostedSkillParam]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentTemplate: + """ + Creates reusable environment configuration without returning confidential setup + commands or environment values. See + [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + + Args: + capability_directories: Directories that contain capabilities exposed to the agent. Defaults to an empty + list. + + env: Environment variables made available to the agent. + + files: Files available before the agent starts. Defaults to an empty list. + + name: An optional human-readable display name for the template. + + network: Network access policy for the environment. Defaults to disabled for GA requests + and enabled for beta requests. + + packages: Packages to install in the environment. Defaults to empty package lists. + + plugins: Plugins provided as inline ZIP archives. Defaults to an empty list. + + setup_commands: Ordered, confidential setup commands. Command bodies are never returned. + + skills: Skills referenced by ID or provided as inline ZIP archives. Defaults to an empty + list. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._post( + "/agents/environments/templates", + body=await async_maybe_transform( + { + "capability_directories": capability_directories, + "env": env, + "files": files, + "name": name, + "network": network, + "packages": packages, + "plugins": plugins, + "setup_commands": setup_commands, + "skills": skills, + }, + template_create_params.TemplateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EnvironmentTemplate, + ) + + async def retrieve( + self, + environment_template_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentTemplate: + """ + Retrieves reusable environment configuration without returning confidential + values. See + [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not environment_template_id: + raise ValueError( + f"Expected a non-empty value for `environment_template_id` but received {environment_template_id!r}" + ) + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._get( + path_template( + "/agents/environments/templates/{environment_template_id}", + environment_template_id=environment_template_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EnvironmentTemplate, + ) + + async def update( + self, + environment_template_id: str, + *, + capability_directories: Optional[SequenceNotStr[str]] | Omit = omit, + env: Optional[Dict[str, str]] | Omit = omit, + files: Optional[Iterable[HostedEnvironmentFileParam]] | Omit = omit, + name: Optional[str] | Omit = omit, + network: Optional[template_update_params.Network] | Omit = omit, + packages: Optional[template_update_params.Packages] | Omit = omit, + plugins: Optional[Iterable[HostedPluginParam]] | Omit = omit, + setup_commands: Optional[Iterable[SetupCommandParam]] | Omit = omit, + skills: Optional[Iterable[HostedSkillParam]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentTemplate: + """ + Updates reusable environment configuration without returning confidential + values. See + [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + + Args: + capability_directories: Directories that expose capabilities to the agent. + + env: Replacement confidential environment values. + + files: Replacement file configuration materialized for each new session. + + name: A replacement human-readable display name, or `null` to clear the name. + + network: Network access available after setup completes. Omit to preserve the current + policy, or pass `null` to reset to disabled for GA requests or enabled for beta + requests. + + packages: Packages installed before the runtime network policy applies. + + plugins: Replacement plugin configuration installed for each new session. + + setup_commands: Replacement confidential setup commands, never included in returned resources. + + skills: Replacement skill configuration installed for each new session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not environment_template_id: + raise ValueError( + f"Expected a non-empty value for `environment_template_id` but received {environment_template_id!r}" + ) + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._post( + path_template( + "/agents/environments/templates/{environment_template_id}", + environment_template_id=environment_template_id, + ), + body=await async_maybe_transform( + { + "capability_directories": capability_directories, + "env": env, + "files": files, + "name": name, + "network": network, + "packages": packages, + "plugins": plugins, + "setup_commands": setup_commands, + "skills": skills, + }, + template_update_params.TemplateUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EnvironmentTemplate, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[EnvironmentTemplate, AsyncCursorPage[EnvironmentTemplate]]: + """Lists reusable environment templates without returning confidential values. + + See + [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + "/agents/environments/templates", + page=AsyncCursorPage[EnvironmentTemplate], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + template_list_params.TemplateListParams, + ), + security={"bearer_auth": True}, + ), + model=EnvironmentTemplate, + ) + + async def delete( + self, + environment_template_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EnvironmentTemplateDeleted: + """ + Deletes reusable environment configuration and all confidential template inputs. + See + [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not environment_template_id: + raise ValueError( + f"Expected a non-empty value for `environment_template_id` but received {environment_template_id!r}" + ) + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._delete( + path_template( + "/agents/environments/templates/{environment_template_id}", + environment_template_id=environment_template_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EnvironmentTemplateDeleted, + ) + + +class TemplatesWithRawResponse: + def __init__(self, templates: Templates) -> None: + self._templates = templates + + self.create = _legacy_response.to_raw_response_wrapper( + templates.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + templates.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + templates.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + templates.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + templates.delete, + ) + + +class AsyncTemplatesWithRawResponse: + def __init__(self, templates: AsyncTemplates) -> None: + self._templates = templates + + self.create = _legacy_response.async_to_raw_response_wrapper( + templates.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + templates.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + templates.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + templates.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + templates.delete, + ) + + +class TemplatesWithStreamingResponse: + def __init__(self, templates: Templates) -> None: + self._templates = templates + + self.create = to_streamed_response_wrapper( + templates.create, + ) + self.retrieve = to_streamed_response_wrapper( + templates.retrieve, + ) + self.update = to_streamed_response_wrapper( + templates.update, + ) + self.list = to_streamed_response_wrapper( + templates.list, + ) + self.delete = to_streamed_response_wrapper( + templates.delete, + ) + + +class AsyncTemplatesWithStreamingResponse: + def __init__(self, templates: AsyncTemplates) -> None: + self._templates = templates + + self.create = async_to_streamed_response_wrapper( + templates.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + templates.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + templates.update, + ) + self.list = async_to_streamed_response_wrapper( + templates.list, + ) + self.delete = async_to_streamed_response_wrapper( + templates.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__init__.py new file mode 100644 index 0000000..fb77d25 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__init__.py @@ -0,0 +1,157 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .items import ( + Items as Items, + AsyncItems as AsyncItems, + ItemsWithRawResponse as ItemsWithRawResponse, + AsyncItemsWithRawResponse as AsyncItemsWithRawResponse, + ItemsWithStreamingResponse as ItemsWithStreamingResponse, + AsyncItemsWithStreamingResponse as AsyncItemsWithStreamingResponse, + ) + from .turns import ( + Turns as Turns, + AsyncTurns as AsyncTurns, + TurnsWithRawResponse as TurnsWithRawResponse, + AsyncTurnsWithRawResponse as AsyncTurnsWithRawResponse, + TurnsWithStreamingResponse as TurnsWithStreamingResponse, + AsyncTurnsWithStreamingResponse as AsyncTurnsWithStreamingResponse, + ) + from .events import ( + Events as Events, + AsyncEvents as AsyncEvents, + EventsWithRawResponse as EventsWithRawResponse, + AsyncEventsWithRawResponse as AsyncEventsWithRawResponse, + EventsWithStreamingResponse as EventsWithStreamingResponse, + AsyncEventsWithStreamingResponse as AsyncEventsWithStreamingResponse, + ) + from .sessions import ( + Sessions as Sessions, + AsyncSessions as AsyncSessions, + SessionsWithRawResponse as SessionsWithRawResponse, + AsyncSessionsWithRawResponse as AsyncSessionsWithRawResponse, + SessionsWithStreamingResponse as SessionsWithStreamingResponse, + AsyncSessionsWithStreamingResponse as AsyncSessionsWithStreamingResponse, + ) + from .artifacts import ( + Artifacts as Artifacts, + AsyncArtifacts as AsyncArtifacts, + ArtifactsWithRawResponse as ArtifactsWithRawResponse, + AsyncArtifactsWithRawResponse as AsyncArtifactsWithRawResponse, + ArtifactsWithStreamingResponse as ArtifactsWithStreamingResponse, + AsyncArtifactsWithStreamingResponse as AsyncArtifactsWithStreamingResponse, + ) + from .subagents import ( + Subagents as Subagents, + AsyncSubagents as AsyncSubagents, + SubagentsWithRawResponse as SubagentsWithRawResponse, + AsyncSubagentsWithRawResponse as AsyncSubagentsWithRawResponse, + SubagentsWithStreamingResponse as SubagentsWithStreamingResponse, + AsyncSubagentsWithStreamingResponse as AsyncSubagentsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Subagents": (".subagents", "Subagents"), + "AsyncSubagents": (".subagents", "AsyncSubagents"), + "SubagentsWithRawResponse": (".subagents", "SubagentsWithRawResponse"), + "AsyncSubagentsWithRawResponse": (".subagents", "AsyncSubagentsWithRawResponse"), + "SubagentsWithStreamingResponse": (".subagents", "SubagentsWithStreamingResponse"), + "AsyncSubagentsWithStreamingResponse": (".subagents", "AsyncSubagentsWithStreamingResponse"), + "Artifacts": (".artifacts", "Artifacts"), + "AsyncArtifacts": (".artifacts", "AsyncArtifacts"), + "ArtifactsWithRawResponse": (".artifacts", "ArtifactsWithRawResponse"), + "AsyncArtifactsWithRawResponse": (".artifacts", "AsyncArtifactsWithRawResponse"), + "ArtifactsWithStreamingResponse": (".artifacts", "ArtifactsWithStreamingResponse"), + "AsyncArtifactsWithStreamingResponse": (".artifacts", "AsyncArtifactsWithStreamingResponse"), + "Items": (".items", "Items"), + "AsyncItems": (".items", "AsyncItems"), + "ItemsWithRawResponse": (".items", "ItemsWithRawResponse"), + "AsyncItemsWithRawResponse": (".items", "AsyncItemsWithRawResponse"), + "ItemsWithStreamingResponse": (".items", "ItemsWithStreamingResponse"), + "AsyncItemsWithStreamingResponse": (".items", "AsyncItemsWithStreamingResponse"), + "Events": (".events", "Events"), + "AsyncEvents": (".events", "AsyncEvents"), + "EventsWithRawResponse": (".events", "EventsWithRawResponse"), + "AsyncEventsWithRawResponse": (".events", "AsyncEventsWithRawResponse"), + "EventsWithStreamingResponse": (".events", "EventsWithStreamingResponse"), + "AsyncEventsWithStreamingResponse": (".events", "AsyncEventsWithStreamingResponse"), + "Turns": (".turns", "Turns"), + "AsyncTurns": (".turns", "AsyncTurns"), + "TurnsWithRawResponse": (".turns", "TurnsWithRawResponse"), + "AsyncTurnsWithRawResponse": (".turns", "AsyncTurnsWithRawResponse"), + "TurnsWithStreamingResponse": (".turns", "TurnsWithStreamingResponse"), + "AsyncTurnsWithStreamingResponse": (".turns", "AsyncTurnsWithStreamingResponse"), + "Sessions": (".sessions", "Sessions"), + "AsyncSessions": (".sessions", "AsyncSessions"), + "SessionsWithRawResponse": (".sessions", "SessionsWithRawResponse"), + "AsyncSessionsWithRawResponse": (".sessions", "AsyncSessionsWithRawResponse"), + "SessionsWithStreamingResponse": (".sessions", "SessionsWithStreamingResponse"), + "AsyncSessionsWithStreamingResponse": (".sessions", "AsyncSessionsWithStreamingResponse"), + } + _SUBMODULES = { + "artifacts", + "events", + "items", + "sessions", + "subagents", + "turns", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Subagents", + "AsyncSubagents", + "SubagentsWithRawResponse", + "AsyncSubagentsWithRawResponse", + "SubagentsWithStreamingResponse", + "AsyncSubagentsWithStreamingResponse", + "Artifacts", + "AsyncArtifacts", + "ArtifactsWithRawResponse", + "AsyncArtifactsWithRawResponse", + "ArtifactsWithStreamingResponse", + "AsyncArtifactsWithStreamingResponse", + "Items", + "AsyncItems", + "ItemsWithRawResponse", + "AsyncItemsWithRawResponse", + "ItemsWithStreamingResponse", + "AsyncItemsWithStreamingResponse", + "Events", + "AsyncEvents", + "EventsWithRawResponse", + "AsyncEventsWithRawResponse", + "EventsWithStreamingResponse", + "AsyncEventsWithStreamingResponse", + "Turns", + "AsyncTurns", + "TurnsWithRawResponse", + "AsyncTurnsWithRawResponse", + "TurnsWithStreamingResponse", + "AsyncTurnsWithStreamingResponse", + "Sessions", + "AsyncSessions", + "SessionsWithRawResponse", + "AsyncSessionsWithRawResponse", + "SessionsWithStreamingResponse", + "AsyncSessionsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ebec963 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/artifacts.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/artifacts.cpython-312.pyc new file mode 100644 index 0000000..0b18abb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/artifacts.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/events.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/events.cpython-312.pyc new file mode 100644 index 0000000..a0a0e20 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/events.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/items.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/items.cpython-312.pyc new file mode 100644 index 0000000..28ad184 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/items.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/sessions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/sessions.cpython-312.pyc new file mode 100644 index 0000000..3d3da39 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/sessions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/turns.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/turns.cpython-312.pyc new file mode 100644 index 0000000..da353d3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/__pycache__/turns.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/artifacts.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/artifacts.py new file mode 100644 index 0000000..60e6d06 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/artifacts.py @@ -0,0 +1,551 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_streamed_response_wrapper, + async_to_streamed_response_wrapper, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.beta.agents.sessions import artifact_list_params +from .....types.beta.agents.sessions.session_artifact import SessionArtifact +from .....types.beta.agents.sessions.session_artifact_deleted import SessionArtifactDeleted + +__all__ = ["Artifacts", "AsyncArtifacts"] + + +class Artifacts(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ArtifactsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ArtifactsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ArtifactsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ArtifactsWithStreamingResponse(self) + + def retrieve( + self, + artifact_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SessionArtifact: + """Retrieves immutable metadata for one durable session artifact. + + See + [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not artifact_id: + raise ValueError(f"Expected a non-empty value for `artifact_id` but received {artifact_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get( + path_template( + "/agents/sessions/{session_id}/artifacts/{artifact_id}", session_id=session_id, artifact_id=artifact_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SessionArtifact, + ) + + def list( + self, + session_id: str, + *, + after: Optional[str] | Omit = omit, + environment_id: Optional[str] | Omit = omit, + limit: Optional[int] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[SessionArtifact]: + """Lists immutable artifacts published by completed hosted session turns. + + See + [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts). + + Args: + after: Return artifacts after this immutable artifact ID. + + environment_id: Restrict the listing to artifacts produced by this environment. + + limit: The maximum number of artifacts to return, between 1 and 100. + + order: Sort by creation time and ID. Defaults to descending. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/agents/sessions/{session_id}/artifacts", session_id=session_id), + page=SyncCursorPage[SessionArtifact], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "environment_id": environment_id, + "limit": limit, + "order": order, + }, + artifact_list_params.ArtifactListParams, + ), + security={"bearer_auth": True}, + ), + model=SessionArtifact, + ) + + def delete( + self, + artifact_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SessionArtifactDeleted: + """ + Deletes an immutable session artifact without deleting its live environment file + or original Files API object. See + [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not artifact_id: + raise ValueError(f"Expected a non-empty value for `artifact_id` but received {artifact_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._delete( + path_template( + "/agents/sessions/{session_id}/artifacts/{artifact_id}", session_id=session_id, artifact_id=artifact_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SessionArtifactDeleted, + ) + + def content( + self, + artifact_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Downloads immutable session artifact bytes after the execution environment + expires. See + [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not artifact_id: + raise ValueError(f"Expected a non-empty value for `artifact_id` but received {artifact_id!r}") + extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get( + path_template( + "/agents/sessions/{session_id}/artifacts/{artifact_id}/content", + session_id=session_id, + artifact_id=artifact_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class AsyncArtifacts(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncArtifactsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncArtifactsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncArtifactsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncArtifactsWithStreamingResponse(self) + + async def retrieve( + self, + artifact_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SessionArtifact: + """Retrieves immutable metadata for one durable session artifact. + + See + [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not artifact_id: + raise ValueError(f"Expected a non-empty value for `artifact_id` but received {artifact_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._get( + path_template( + "/agents/sessions/{session_id}/artifacts/{artifact_id}", session_id=session_id, artifact_id=artifact_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SessionArtifact, + ) + + def list( + self, + session_id: str, + *, + after: Optional[str] | Omit = omit, + environment_id: Optional[str] | Omit = omit, + limit: Optional[int] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[SessionArtifact, AsyncCursorPage[SessionArtifact]]: + """Lists immutable artifacts published by completed hosted session turns. + + See + [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts). + + Args: + after: Return artifacts after this immutable artifact ID. + + environment_id: Restrict the listing to artifacts produced by this environment. + + limit: The maximum number of artifacts to return, between 1 and 100. + + order: Sort by creation time and ID. Defaults to descending. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/agents/sessions/{session_id}/artifacts", session_id=session_id), + page=AsyncCursorPage[SessionArtifact], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "environment_id": environment_id, + "limit": limit, + "order": order, + }, + artifact_list_params.ArtifactListParams, + ), + security={"bearer_auth": True}, + ), + model=SessionArtifact, + ) + + async def delete( + self, + artifact_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SessionArtifactDeleted: + """ + Deletes an immutable session artifact without deleting its live environment file + or original Files API object. See + [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not artifact_id: + raise ValueError(f"Expected a non-empty value for `artifact_id` but received {artifact_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._delete( + path_template( + "/agents/sessions/{session_id}/artifacts/{artifact_id}", session_id=session_id, artifact_id=artifact_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SessionArtifactDeleted, + ) + + async def content( + self, + artifact_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Downloads immutable session artifact bytes after the execution environment + expires. See + [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not artifact_id: + raise ValueError(f"Expected a non-empty value for `artifact_id` but received {artifact_id!r}") + extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._get( + path_template( + "/agents/sessions/{session_id}/artifacts/{artifact_id}/content", + session_id=session_id, + artifact_id=artifact_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class ArtifactsWithRawResponse: + def __init__(self, artifacts: Artifacts) -> None: + self._artifacts = artifacts + + self.retrieve = _legacy_response.to_raw_response_wrapper( + artifacts.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + artifacts.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + artifacts.delete, + ) + self.content = _legacy_response.to_raw_response_wrapper( + artifacts.content, + ) + + +class AsyncArtifactsWithRawResponse: + def __init__(self, artifacts: AsyncArtifacts) -> None: + self._artifacts = artifacts + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + artifacts.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + artifacts.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + artifacts.delete, + ) + self.content = _legacy_response.async_to_raw_response_wrapper( + artifacts.content, + ) + + +class ArtifactsWithStreamingResponse: + def __init__(self, artifacts: Artifacts) -> None: + self._artifacts = artifacts + + self.retrieve = to_streamed_response_wrapper( + artifacts.retrieve, + ) + self.list = to_streamed_response_wrapper( + artifacts.list, + ) + self.delete = to_streamed_response_wrapper( + artifacts.delete, + ) + self.content = to_custom_streamed_response_wrapper( + artifacts.content, + StreamedBinaryAPIResponse, + ) + + +class AsyncArtifactsWithStreamingResponse: + def __init__(self, artifacts: AsyncArtifacts) -> None: + self._artifacts = artifacts + + self.retrieve = async_to_streamed_response_wrapper( + artifacts.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + artifacts.list, + ) + self.delete = async_to_streamed_response_wrapper( + artifacts.delete, + ) + self.content = async_to_custom_streamed_response_wrapper( + artifacts.content, + AsyncStreamedBinaryAPIResponse, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/events.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/events.py new file mode 100644 index 0000000..9c2b61f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/events.py @@ -0,0 +1,297 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, Iterable, cast + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._streaming import Stream, AsyncStream +from ....._base_client import make_request_options +from .....types.beta.agents.sessions import event_create_params +from .....types.beta.agent_session_event import AgentSessionEvent +from .....types.beta.agent_session_input_param import AgentSessionInputParam + +__all__ = ["Events", "AsyncEvents"] + + +class Events(SyncAPIResource): + @cached_property + def with_raw_response(self) -> EventsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return EventsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EventsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return EventsWithStreamingResponse(self) + + def create( + self, + session_id: str, + *, + events: Iterable[AgentSessionInputParam], + idempotency_key: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Submits message, cancellation, or tool-result events to a managed agent session. + Cancellation can recover a still-open turn whose backend execution has ended by + marking it cancelled and abandoning unpublished outputs. Saved results, + published files, and existing terminal outcomes are preserved. HTTP 202 confirms + acceptance, not durable completion. See + [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events). + + Args: + events: The input events to submit to the session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers = {**strip_not_given({"Idempotency-Key": idempotency_key}), **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._post( + path_template("/agents/sessions/{session_id}/events", session_id=session_id), + body=maybe_transform({"events": events}, event_create_params.EventCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + def stream( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[AgentSessionEvent]: + """Streams live events for an agent session. + + See + [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get( + path_template("/agents/sessions/{session_id}/events", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=cast(Any, AgentSessionEvent), # Union types cannot be passed in as arguments in the type system + stream=True, + stream_cls=Stream[AgentSessionEvent], + ) + + +class AsyncEvents(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncEventsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncEventsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEventsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncEventsWithStreamingResponse(self) + + async def create( + self, + session_id: str, + *, + events: Iterable[AgentSessionInputParam], + idempotency_key: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Submits message, cancellation, or tool-result events to a managed agent session. + Cancellation can recover a still-open turn whose backend execution has ended by + marking it cancelled and abandoning unpublished outputs. Saved results, + published files, and existing terminal outcomes are preserved. HTTP 202 confirms + acceptance, not durable completion. See + [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events). + + Args: + events: The input events to submit to the session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers = {**strip_not_given({"Idempotency-Key": idempotency_key}), **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._post( + path_template("/agents/sessions/{session_id}/events", session_id=session_id), + body=await async_maybe_transform({"events": events}, event_create_params.EventCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + async def stream( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[AgentSessionEvent]: + """Streams live events for an agent session. + + See + [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._get( + path_template("/agents/sessions/{session_id}/events", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=cast(Any, AgentSessionEvent), # Union types cannot be passed in as arguments in the type system + stream=True, + stream_cls=AsyncStream[AgentSessionEvent], + ) + + +class EventsWithRawResponse: + def __init__(self, events: Events) -> None: + self._events = events + + self.create = _legacy_response.to_raw_response_wrapper( + events.create, + ) + self.stream = _legacy_response.to_raw_response_wrapper( + events.stream, + ) + + +class AsyncEventsWithRawResponse: + def __init__(self, events: AsyncEvents) -> None: + self._events = events + + self.create = _legacy_response.async_to_raw_response_wrapper( + events.create, + ) + self.stream = _legacy_response.async_to_raw_response_wrapper( + events.stream, + ) + + +class EventsWithStreamingResponse: + def __init__(self, events: Events) -> None: + self._events = events + + self.create = to_streamed_response_wrapper( + events.create, + ) + self.stream = to_streamed_response_wrapper( + events.stream, + ) + + +class AsyncEventsWithStreamingResponse: + def __init__(self, events: AsyncEvents) -> None: + self._events = events + + self.create = async_to_streamed_response_wrapper( + events.create, + ) + self.stream = async_to_streamed_response_wrapper( + events.stream, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/items.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/items.py new file mode 100644 index 0000000..1bf2990 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/items.py @@ -0,0 +1,221 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, cast +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.beta.agents.sessions import item_list_params +from .....types.beta.agent_session_item import AgentSessionItem + +__all__ = ["Items", "AsyncItems"] + + +class Items(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ItemsWithStreamingResponse(self) + + def list( + self, + session_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[AgentSessionItem]: + """ + Lists items produced by the session's root agent, including its interactions + with subagents. Each subagent has its own item history. See + [inspecting agent output](https://developers.openai.com/api/docs/guides/agents-api/observability). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/agents/sessions/{session_id}/items", session_id=session_id), + page=SyncCursorPage[AgentSessionItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + item_list_params.ItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, AgentSessionItem), # Union types cannot be passed in as arguments in the type system + ) + + +class AsyncItems(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncItemsWithStreamingResponse(self) + + def list( + self, + session_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[AgentSessionItem, AsyncCursorPage[AgentSessionItem]]: + """ + Lists items produced by the session's root agent, including its interactions + with subagents. Each subagent has its own item history. See + [inspecting agent output](https://developers.openai.com/api/docs/guides/agents-api/observability). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/agents/sessions/{session_id}/items", session_id=session_id), + page=AsyncCursorPage[AgentSessionItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + item_list_params.ItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, AgentSessionItem), # Union types cannot be passed in as arguments in the type system + ) + + +class ItemsWithRawResponse: + def __init__(self, items: Items) -> None: + self._items = items + + self.list = _legacy_response.to_raw_response_wrapper( + items.list, + ) + + +class AsyncItemsWithRawResponse: + def __init__(self, items: AsyncItems) -> None: + self._items = items + + self.list = _legacy_response.async_to_raw_response_wrapper( + items.list, + ) + + +class ItemsWithStreamingResponse: + def __init__(self, items: Items) -> None: + self._items = items + + self.list = to_streamed_response_wrapper( + items.list, + ) + + +class AsyncItemsWithStreamingResponse: + def __init__(self, items: AsyncItems) -> None: + self._items = items + + self.list = async_to_streamed_response_wrapper( + items.list, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/sessions.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/sessions.py new file mode 100644 index 0000000..ff13450 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/sessions.py @@ -0,0 +1,1187 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Mapping, Iterable, Optional +from typing_extensions import Literal, overload + +import httpx2 + +from ..... import _legacy_response +from .items import ( + Items, + AsyncItems, + ItemsWithRawResponse, + AsyncItemsWithRawResponse, + ItemsWithStreamingResponse, + AsyncItemsWithStreamingResponse, +) +from .turns import ( + Turns, + AsyncTurns, + TurnsWithRawResponse, + AsyncTurnsWithRawResponse, + TurnsWithStreamingResponse, + AsyncTurnsWithStreamingResponse, +) +from .events import ( + Events, + AsyncEvents, + EventsWithRawResponse, + AsyncEventsWithRawResponse, + EventsWithStreamingResponse, + AsyncEventsWithStreamingResponse, +) +from .artifacts import ( + Artifacts, + AsyncArtifacts, + ArtifactsWithRawResponse, + AsyncArtifactsWithRawResponse, + ArtifactsWithStreamingResponse, + AsyncArtifactsWithStreamingResponse, +) +from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ....._utils import path_template, required_args, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._streaming import Stream, AsyncStream +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .subagents.subagents import ( + Subagents, + AsyncSubagents, + SubagentsWithRawResponse, + AsyncSubagentsWithRawResponse, + SubagentsWithStreamingResponse, + AsyncSubagentsWithStreamingResponse, +) +from .....types.beta.agents import session_list_params, session_create_params, session_update_params +from .....lib.streaming.agents import ToolHandler, AsyncToolHandler, AgentSessionStream, AsyncAgentSessionStream +from .....types.beta.agent_session import AgentSession +from .....types.beta.environment_param import EnvironmentParam +from .....types.beta.agent_session_event import AgentSessionEvent +from .....types.beta.agent_session_deleted import AgentSessionDeleted +from .....types.beta.agent_session_input_message_param import AgentSessionInputMessageParam + +__all__ = ["Sessions", "AsyncSessions"] + + +class Sessions(SyncAPIResource): + def stream( + self, + session_id: str, + *, + input: str | Iterable[AgentSessionInputMessageParam], + tool_handlers: Mapping[str, ToolHandler] | None = None, + idempotency_key: str | Omit = omit, + extra_headers: Headers | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentSessionStream: + """Stream one turn of an idle session, subscribing before submitting input. + + Use as a context manager. Only one caller may submit input to the session + while this helper runs. Optional tool handlers receive an arguments dict; + their results are submitted automatically. See AgentSessionStream for details. + """ + return AgentSessionStream( + self, + session_id, + input=input, + tool_handlers=tool_handlers, + idempotency_key=idempotency_key, + extra_headers=extra_headers, + timeout=timeout, + ) + + @cached_property + def subagents(self) -> Subagents: + return Subagents(self._client) + + @cached_property + def artifacts(self) -> Artifacts: + return Artifacts(self._client) + + @cached_property + def items(self) -> Items: + return Items(self._client) + + @cached_property + def events(self) -> Events: + return Events(self._client) + + @cached_property + def turns(self) -> Turns: + return Turns(self._client) + + @cached_property + def with_raw_response(self) -> SessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SessionsWithStreamingResponse(self) + + @overload + def create( + self, + *, + environment: EnvironmentParam, + agent: session_create_params.Agent | Omit = omit, + agent_id: str | Omit = omit, + input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + stream: Literal[False] | Omit = omit, + vault_ids: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentSession: + """ + Creates a managed agent session, optionally submits initial input, and returns + the session or streams its events when stream is true. See + [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions). + + Args: + environment: An inline execution environment or a reference to an environment template. + + agent: Agent configuration. With `agent_id`, supplied fields override the saved agent + for this session. Without `agent_id`, `model` is required. + + agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration + unchanged. + + input: Initial input to submit when the session is created. A string is shorthand for a + single user message. Required when `environment.type` is `none`, or when + `stream` is `true` for an environment that is not `self_hosted`; optional for + self-hosted and non-streaming execution environments. + + metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. Omission or null defaults to an empty map. + + stream: Whether to stream session events as server-sent events. Defaults to `false`. + + vault_ids: The IDs of vaults made available to the session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + environment: EnvironmentParam, + stream: Literal[True], + agent: session_create_params.Agent | Omit = omit, + agent_id: str | Omit = omit, + input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + vault_ids: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[AgentSessionEvent]: + """ + Creates a managed agent session, optionally submits initial input, and returns + the session or streams its events when stream is true. See + [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions). + + Args: + environment: An inline execution environment or a reference to an environment template. + + stream: Whether to stream session events as server-sent events. Defaults to `false`. + + agent: Agent configuration. With `agent_id`, supplied fields override the saved agent + for this session. Without `agent_id`, `model` is required. + + agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration + unchanged. + + input: Initial input to submit when the session is created. A string is shorthand for a + single user message. Required when `environment.type` is `none`, or when + `stream` is `true` for an environment that is not `self_hosted`; optional for + self-hosted and non-streaming execution environments. + + metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. Omission or null defaults to an empty map. + + vault_ids: The IDs of vaults made available to the session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + environment: EnvironmentParam, + stream: bool, + agent: session_create_params.Agent | Omit = omit, + agent_id: str | Omit = omit, + input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + vault_ids: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentSession | Stream[AgentSessionEvent]: + """ + Creates a managed agent session, optionally submits initial input, and returns + the session or streams its events when stream is true. See + [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions). + + Args: + environment: An inline execution environment or a reference to an environment template. + + stream: Whether to stream session events as server-sent events. Defaults to `false`. + + agent: Agent configuration. With `agent_id`, supplied fields override the saved agent + for this session. Without `agent_id`, `model` is required. + + agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration + unchanged. + + input: Initial input to submit when the session is created. A string is shorthand for a + single user message. Required when `environment.type` is `none`, or when + `stream` is `true` for an environment that is not `self_hosted`; optional for + self-hosted and non-streaming execution environments. + + metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. Omission or null defaults to an empty map. + + vault_ids: The IDs of vaults made available to the session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["environment"], ["environment", "stream"]) + def create( + self, + *, + environment: EnvironmentParam, + agent: session_create_params.Agent | Omit = omit, + agent_id: str | Omit = omit, + input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + vault_ids: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentSession | Stream[AgentSessionEvent]: + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._post( + "/agents/sessions", + body=maybe_transform( + { + "environment": environment, + "agent": agent, + "agent_id": agent_id, + "input": input, + "metadata": metadata, + "stream": stream, + "vault_ids": vault_ids, + }, + session_create_params.SessionCreateParamsStreaming + if stream + else session_create_params.SessionCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=AgentSession, + stream=stream or False, + stream_cls=Stream[AgentSessionEvent], + ) + + def retrieve( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentSession: + """Retrieves the current state of a managed agent session. + + See + [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get( + path_template("/agents/sessions/{session_id}", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=AgentSession, + ) + + def update( + self, + session_id: str, + *, + agent: session_update_params.Agent | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentSession: + """Updates session metadata, model, reasoning effort, or service tier. + + Model + settings apply to subsequent turns. Omitted fields are unchanged. See + [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage). + + Args: + agent: Model settings for subsequent turns. Omitted fields stay unchanged. + + metadata: Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it. + Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._post( + path_template("/agents/sessions/{session_id}", session_id=session_id), + body=maybe_transform( + { + "agent": agent, + "metadata": metadata, + }, + session_update_params.SessionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=AgentSession, + ) + + def list( + self, + *, + after: str | Omit = omit, + agent_id: str | Omit = omit, + limit: Optional[int] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[AgentSession]: + """ + Lists managed agent sessions using ID-based pagination and the requested sort + order. See + [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage). + + Args: + after: Return resources after this resource ID in the selected order. + + agent_id: Only return sessions whose root agent has this ID. Omit to return sessions for + all agents. + + limit: The maximum number of resources to return. + + order: Sort order by the `created_at` timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + "/agents/sessions", + page=SyncCursorPage[AgentSession], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "agent_id": agent_id, + "limit": limit, + "order": order, + }, + session_list_params.SessionListParams, + ), + security={"bearer_auth": True}, + ), + model=AgentSession, + ) + + def delete( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentSessionDeleted: + """ + Removes a managed agent session from the public API and returns a deletion + confirmation. If backend execution has ended, deletion can cancel a still-open + public turn and abandon unpublished outputs. Running execution must be cancelled + first. Physical cleanup may continue asynchronously. See + [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._delete( + path_template("/agents/sessions/{session_id}", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=AgentSessionDeleted, + ) + + +class AsyncSessions(AsyncAPIResource): + def stream( + self, + session_id: str, + *, + input: str | Iterable[AgentSessionInputMessageParam], + tool_handlers: Mapping[str, AsyncToolHandler] | None = None, + idempotency_key: str | Omit = omit, + extra_headers: Headers | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncAgentSessionStream: + """Stream one turn of an idle session, subscribing before submitting input. + + Use as an async context manager. Only one caller may submit input to the session + while this helper runs. Optional tool handlers receive an arguments dict; + their results are submitted automatically. See AsyncAgentSessionStream for details. + """ + return AsyncAgentSessionStream( + self, + session_id, + input=input, + tool_handlers=tool_handlers, + idempotency_key=idempotency_key, + extra_headers=extra_headers, + timeout=timeout, + ) + + @cached_property + def subagents(self) -> AsyncSubagents: + return AsyncSubagents(self._client) + + @cached_property + def artifacts(self) -> AsyncArtifacts: + return AsyncArtifacts(self._client) + + @cached_property + def items(self) -> AsyncItems: + return AsyncItems(self._client) + + @cached_property + def events(self) -> AsyncEvents: + return AsyncEvents(self._client) + + @cached_property + def turns(self) -> AsyncTurns: + return AsyncTurns(self._client) + + @cached_property + def with_raw_response(self) -> AsyncSessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSessionsWithStreamingResponse(self) + + @overload + async def create( + self, + *, + environment: EnvironmentParam, + agent: session_create_params.Agent | Omit = omit, + agent_id: str | Omit = omit, + input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + stream: Literal[False] | Omit = omit, + vault_ids: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentSession: + """ + Creates a managed agent session, optionally submits initial input, and returns + the session or streams its events when stream is true. See + [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions). + + Args: + environment: An inline execution environment or a reference to an environment template. + + agent: Agent configuration. With `agent_id`, supplied fields override the saved agent + for this session. Without `agent_id`, `model` is required. + + agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration + unchanged. + + input: Initial input to submit when the session is created. A string is shorthand for a + single user message. Required when `environment.type` is `none`, or when + `stream` is `true` for an environment that is not `self_hosted`; optional for + self-hosted and non-streaming execution environments. + + metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. Omission or null defaults to an empty map. + + stream: Whether to stream session events as server-sent events. Defaults to `false`. + + vault_ids: The IDs of vaults made available to the session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + environment: EnvironmentParam, + stream: Literal[True], + agent: session_create_params.Agent | Omit = omit, + agent_id: str | Omit = omit, + input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + vault_ids: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[AgentSessionEvent]: + """ + Creates a managed agent session, optionally submits initial input, and returns + the session or streams its events when stream is true. See + [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions). + + Args: + environment: An inline execution environment or a reference to an environment template. + + stream: Whether to stream session events as server-sent events. Defaults to `false`. + + agent: Agent configuration. With `agent_id`, supplied fields override the saved agent + for this session. Without `agent_id`, `model` is required. + + agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration + unchanged. + + input: Initial input to submit when the session is created. A string is shorthand for a + single user message. Required when `environment.type` is `none`, or when + `stream` is `true` for an environment that is not `self_hosted`; optional for + self-hosted and non-streaming execution environments. + + metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. Omission or null defaults to an empty map. + + vault_ids: The IDs of vaults made available to the session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + environment: EnvironmentParam, + stream: bool, + agent: session_create_params.Agent | Omit = omit, + agent_id: str | Omit = omit, + input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + vault_ids: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentSession | AsyncStream[AgentSessionEvent]: + """ + Creates a managed agent session, optionally submits initial input, and returns + the session or streams its events when stream is true. See + [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions). + + Args: + environment: An inline execution environment or a reference to an environment template. + + stream: Whether to stream session events as server-sent events. Defaults to `false`. + + agent: Agent configuration. With `agent_id`, supplied fields override the saved agent + for this session. Without `agent_id`, `model` is required. + + agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration + unchanged. + + input: Initial input to submit when the session is created. A string is shorthand for a + single user message. Required when `environment.type` is `none`, or when + `stream` is `true` for an environment that is not `self_hosted`; optional for + self-hosted and non-streaming execution environments. + + metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. Omission or null defaults to an empty map. + + vault_ids: The IDs of vaults made available to the session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["environment"], ["environment", "stream"]) + async def create( + self, + *, + environment: EnvironmentParam, + agent: session_create_params.Agent | Omit = omit, + agent_id: str | Omit = omit, + input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + vault_ids: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentSession | AsyncStream[AgentSessionEvent]: + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._post( + "/agents/sessions", + body=await async_maybe_transform( + { + "environment": environment, + "agent": agent, + "agent_id": agent_id, + "input": input, + "metadata": metadata, + "stream": stream, + "vault_ids": vault_ids, + }, + session_create_params.SessionCreateParamsStreaming + if stream + else session_create_params.SessionCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=AgentSession, + stream=stream or False, + stream_cls=AsyncStream[AgentSessionEvent], + ) + + async def retrieve( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentSession: + """Retrieves the current state of a managed agent session. + + See + [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._get( + path_template("/agents/sessions/{session_id}", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=AgentSession, + ) + + async def update( + self, + session_id: str, + *, + agent: session_update_params.Agent | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentSession: + """Updates session metadata, model, reasoning effort, or service tier. + + Model + settings apply to subsequent turns. Omitted fields are unchanged. See + [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage). + + Args: + agent: Model settings for subsequent turns. Omitted fields stay unchanged. + + metadata: Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it. + Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._post( + path_template("/agents/sessions/{session_id}", session_id=session_id), + body=await async_maybe_transform( + { + "agent": agent, + "metadata": metadata, + }, + session_update_params.SessionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=AgentSession, + ) + + def list( + self, + *, + after: str | Omit = omit, + agent_id: str | Omit = omit, + limit: Optional[int] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[AgentSession, AsyncCursorPage[AgentSession]]: + """ + Lists managed agent sessions using ID-based pagination and the requested sort + order. See + [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage). + + Args: + after: Return resources after this resource ID in the selected order. + + agent_id: Only return sessions whose root agent has this ID. Omit to return sessions for + all agents. + + limit: The maximum number of resources to return. + + order: Sort order by the `created_at` timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + "/agents/sessions", + page=AsyncCursorPage[AgentSession], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "agent_id": agent_id, + "limit": limit, + "order": order, + }, + session_list_params.SessionListParams, + ), + security={"bearer_auth": True}, + ), + model=AgentSession, + ) + + async def delete( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AgentSessionDeleted: + """ + Removes a managed agent session from the public API and returns a deletion + confirmation. If backend execution has ended, deletion can cancel a still-open + public turn and abandon unpublished outputs. Running execution must be cancelled + first. Physical cleanup may continue asynchronously. See + [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._delete( + path_template("/agents/sessions/{session_id}", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=AgentSessionDeleted, + ) + + +class SessionsWithRawResponse: + def __init__(self, sessions: Sessions) -> None: + self._sessions = sessions + + self.create = _legacy_response.to_raw_response_wrapper( + sessions.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + sessions.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + sessions.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + sessions.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + sessions.delete, + ) + + @cached_property + def subagents(self) -> SubagentsWithRawResponse: + return SubagentsWithRawResponse(self._sessions.subagents) + + @cached_property + def artifacts(self) -> ArtifactsWithRawResponse: + return ArtifactsWithRawResponse(self._sessions.artifacts) + + @cached_property + def items(self) -> ItemsWithRawResponse: + return ItemsWithRawResponse(self._sessions.items) + + @cached_property + def events(self) -> EventsWithRawResponse: + return EventsWithRawResponse(self._sessions.events) + + @cached_property + def turns(self) -> TurnsWithRawResponse: + return TurnsWithRawResponse(self._sessions.turns) + + +class AsyncSessionsWithRawResponse: + def __init__(self, sessions: AsyncSessions) -> None: + self._sessions = sessions + + self.create = _legacy_response.async_to_raw_response_wrapper( + sessions.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + sessions.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + sessions.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + sessions.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + sessions.delete, + ) + + @cached_property + def subagents(self) -> AsyncSubagentsWithRawResponse: + return AsyncSubagentsWithRawResponse(self._sessions.subagents) + + @cached_property + def artifacts(self) -> AsyncArtifactsWithRawResponse: + return AsyncArtifactsWithRawResponse(self._sessions.artifacts) + + @cached_property + def items(self) -> AsyncItemsWithRawResponse: + return AsyncItemsWithRawResponse(self._sessions.items) + + @cached_property + def events(self) -> AsyncEventsWithRawResponse: + return AsyncEventsWithRawResponse(self._sessions.events) + + @cached_property + def turns(self) -> AsyncTurnsWithRawResponse: + return AsyncTurnsWithRawResponse(self._sessions.turns) + + +class SessionsWithStreamingResponse: + def __init__(self, sessions: Sessions) -> None: + self._sessions = sessions + + self.create = to_streamed_response_wrapper( + sessions.create, + ) + self.retrieve = to_streamed_response_wrapper( + sessions.retrieve, + ) + self.update = to_streamed_response_wrapper( + sessions.update, + ) + self.list = to_streamed_response_wrapper( + sessions.list, + ) + self.delete = to_streamed_response_wrapper( + sessions.delete, + ) + + @cached_property + def subagents(self) -> SubagentsWithStreamingResponse: + return SubagentsWithStreamingResponse(self._sessions.subagents) + + @cached_property + def artifacts(self) -> ArtifactsWithStreamingResponse: + return ArtifactsWithStreamingResponse(self._sessions.artifacts) + + @cached_property + def items(self) -> ItemsWithStreamingResponse: + return ItemsWithStreamingResponse(self._sessions.items) + + @cached_property + def events(self) -> EventsWithStreamingResponse: + return EventsWithStreamingResponse(self._sessions.events) + + @cached_property + def turns(self) -> TurnsWithStreamingResponse: + return TurnsWithStreamingResponse(self._sessions.turns) + + +class AsyncSessionsWithStreamingResponse: + def __init__(self, sessions: AsyncSessions) -> None: + self._sessions = sessions + + self.create = async_to_streamed_response_wrapper( + sessions.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + sessions.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + sessions.update, + ) + self.list = async_to_streamed_response_wrapper( + sessions.list, + ) + self.delete = async_to_streamed_response_wrapper( + sessions.delete, + ) + + @cached_property + def subagents(self) -> AsyncSubagentsWithStreamingResponse: + return AsyncSubagentsWithStreamingResponse(self._sessions.subagents) + + @cached_property + def artifacts(self) -> AsyncArtifactsWithStreamingResponse: + return AsyncArtifactsWithStreamingResponse(self._sessions.artifacts) + + @cached_property + def items(self) -> AsyncItemsWithStreamingResponse: + return AsyncItemsWithStreamingResponse(self._sessions.items) + + @cached_property + def events(self) -> AsyncEventsWithStreamingResponse: + return AsyncEventsWithStreamingResponse(self._sessions.events) + + @cached_property + def turns(self) -> AsyncTurnsWithStreamingResponse: + return AsyncTurnsWithStreamingResponse(self._sessions.turns) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/__init__.py new file mode 100644 index 0000000..0e900ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/__init__.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .items import ( + Items as Items, + AsyncItems as AsyncItems, + ItemsWithRawResponse as ItemsWithRawResponse, + AsyncItemsWithRawResponse as AsyncItemsWithRawResponse, + ItemsWithStreamingResponse as ItemsWithStreamingResponse, + AsyncItemsWithStreamingResponse as AsyncItemsWithStreamingResponse, + ) + from .turns import ( + Turns as Turns, + AsyncTurns as AsyncTurns, + TurnsWithRawResponse as TurnsWithRawResponse, + AsyncTurnsWithRawResponse as AsyncTurnsWithRawResponse, + TurnsWithStreamingResponse as TurnsWithStreamingResponse, + AsyncTurnsWithStreamingResponse as AsyncTurnsWithStreamingResponse, + ) + from .subagents import ( + Subagents as Subagents, + AsyncSubagents as AsyncSubagents, + SubagentsWithRawResponse as SubagentsWithRawResponse, + AsyncSubagentsWithRawResponse as AsyncSubagentsWithRawResponse, + SubagentsWithStreamingResponse as SubagentsWithStreamingResponse, + AsyncSubagentsWithStreamingResponse as AsyncSubagentsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Items": (".items", "Items"), + "AsyncItems": (".items", "AsyncItems"), + "ItemsWithRawResponse": (".items", "ItemsWithRawResponse"), + "AsyncItemsWithRawResponse": (".items", "AsyncItemsWithRawResponse"), + "ItemsWithStreamingResponse": (".items", "ItemsWithStreamingResponse"), + "AsyncItemsWithStreamingResponse": (".items", "AsyncItemsWithStreamingResponse"), + "Turns": (".turns", "Turns"), + "AsyncTurns": (".turns", "AsyncTurns"), + "TurnsWithRawResponse": (".turns", "TurnsWithRawResponse"), + "AsyncTurnsWithRawResponse": (".turns", "AsyncTurnsWithRawResponse"), + "TurnsWithStreamingResponse": (".turns", "TurnsWithStreamingResponse"), + "AsyncTurnsWithStreamingResponse": (".turns", "AsyncTurnsWithStreamingResponse"), + "Subagents": (".subagents", "Subagents"), + "AsyncSubagents": (".subagents", "AsyncSubagents"), + "SubagentsWithRawResponse": (".subagents", "SubagentsWithRawResponse"), + "AsyncSubagentsWithRawResponse": (".subagents", "AsyncSubagentsWithRawResponse"), + "SubagentsWithStreamingResponse": (".subagents", "SubagentsWithStreamingResponse"), + "AsyncSubagentsWithStreamingResponse": (".subagents", "AsyncSubagentsWithStreamingResponse"), + } + _SUBMODULES = { + "items", + "subagents", + "turns", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Items", + "AsyncItems", + "ItemsWithRawResponse", + "AsyncItemsWithRawResponse", + "ItemsWithStreamingResponse", + "AsyncItemsWithStreamingResponse", + "Turns", + "AsyncTurns", + "TurnsWithRawResponse", + "AsyncTurnsWithRawResponse", + "TurnsWithStreamingResponse", + "AsyncTurnsWithStreamingResponse", + "Subagents", + "AsyncSubagents", + "SubagentsWithRawResponse", + "AsyncSubagentsWithRawResponse", + "SubagentsWithStreamingResponse", + "AsyncSubagentsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4d2e827 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/__pycache__/items.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/__pycache__/items.cpython-312.pyc new file mode 100644 index 0000000..35ad825 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/__pycache__/items.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/__pycache__/subagents.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/__pycache__/subagents.cpython-312.pyc new file mode 100644 index 0000000..6c9e53f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/__pycache__/subagents.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/items.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/items.py new file mode 100644 index 0000000..b1b7f58 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/items.py @@ -0,0 +1,235 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, cast +from typing_extensions import Literal + +import httpx2 + +from ...... import _legacy_response +from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......_utils import path_template, maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......pagination import SyncCursorPage, AsyncCursorPage +from ......_base_client import AsyncPaginator, make_request_options +from ......types.beta.agent_session_item import AgentSessionItem +from ......types.beta.agents.sessions.subagents import item_list_params + +__all__ = ["Items", "AsyncItems"] + + +class Items(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ItemsWithStreamingResponse(self) + + def list( + self, + subagent_id: str, + *, + session_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[AgentSessionItem]: + """Lists this subagent's own items across all of its turns. + + See + [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not subagent_id: + raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template( + "/agents/sessions/{session_id}/subagents/{subagent_id}/items", + session_id=session_id, + subagent_id=subagent_id, + ), + page=SyncCursorPage[AgentSessionItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + item_list_params.ItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, AgentSessionItem), # Union types cannot be passed in as arguments in the type system + ) + + +class AsyncItems(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncItemsWithStreamingResponse(self) + + def list( + self, + subagent_id: str, + *, + session_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[AgentSessionItem, AsyncCursorPage[AgentSessionItem]]: + """Lists this subagent's own items across all of its turns. + + See + [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not subagent_id: + raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template( + "/agents/sessions/{session_id}/subagents/{subagent_id}/items", + session_id=session_id, + subagent_id=subagent_id, + ), + page=AsyncCursorPage[AgentSessionItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + item_list_params.ItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, AgentSessionItem), # Union types cannot be passed in as arguments in the type system + ) + + +class ItemsWithRawResponse: + def __init__(self, items: Items) -> None: + self._items = items + + self.list = _legacy_response.to_raw_response_wrapper( + items.list, + ) + + +class AsyncItemsWithRawResponse: + def __init__(self, items: AsyncItems) -> None: + self._items = items + + self.list = _legacy_response.async_to_raw_response_wrapper( + items.list, + ) + + +class ItemsWithStreamingResponse: + def __init__(self, items: Items) -> None: + self._items = items + + self.list = to_streamed_response_wrapper( + items.list, + ) + + +class AsyncItemsWithStreamingResponse: + def __init__(self, items: AsyncItems) -> None: + self._items = items + + self.list = async_to_streamed_response_wrapper( + items.list, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/subagents.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/subagents.py new file mode 100644 index 0000000..47e7950 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/subagents.py @@ -0,0 +1,386 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ...... import _legacy_response +from .items import ( + Items, + AsyncItems, + ItemsWithRawResponse, + AsyncItemsWithRawResponse, + ItemsWithStreamingResponse, + AsyncItemsWithStreamingResponse, +) +from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......_utils import path_template, maybe_transform +from .turns.turns import ( + Turns, + AsyncTurns, + TurnsWithRawResponse, + AsyncTurnsWithRawResponse, + TurnsWithStreamingResponse, + AsyncTurnsWithStreamingResponse, +) +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......pagination import SyncCursorPage, AsyncCursorPage +from ......_base_client import AsyncPaginator, make_request_options +from ......types.beta.subagent import Subagent +from ......types.beta.agents.sessions import subagent_list_params + +__all__ = ["Subagents", "AsyncSubagents"] + + +class Subagents(SyncAPIResource): + @cached_property + def items(self) -> Items: + return Items(self._client) + + @cached_property + def turns(self) -> Turns: + return Turns(self._client) + + @cached_property + def with_raw_response(self) -> SubagentsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SubagentsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SubagentsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SubagentsWithStreamingResponse(self) + + def retrieve( + self, + subagent_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Subagent: + """Retrieves a subagent belonging to this session. + + See + [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not subagent_id: + raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get( + path_template( + "/agents/sessions/{session_id}/subagents/{subagent_id}", session_id=session_id, subagent_id=subagent_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Subagent, + ) + + def list( + self, + session_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Subagent]: + """Lists subagents in a session, including nested and closed subagents. + + See + [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/agents/sessions/{session_id}/subagents", session_id=session_id), + page=SyncCursorPage[Subagent], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + subagent_list_params.SubagentListParams, + ), + security={"bearer_auth": True}, + ), + model=Subagent, + ) + + +class AsyncSubagents(AsyncAPIResource): + @cached_property + def items(self) -> AsyncItems: + return AsyncItems(self._client) + + @cached_property + def turns(self) -> AsyncTurns: + return AsyncTurns(self._client) + + @cached_property + def with_raw_response(self) -> AsyncSubagentsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSubagentsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSubagentsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSubagentsWithStreamingResponse(self) + + async def retrieve( + self, + subagent_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Subagent: + """Retrieves a subagent belonging to this session. + + See + [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not subagent_id: + raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._get( + path_template( + "/agents/sessions/{session_id}/subagents/{subagent_id}", session_id=session_id, subagent_id=subagent_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Subagent, + ) + + def list( + self, + session_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Subagent, AsyncCursorPage[Subagent]]: + """Lists subagents in a session, including nested and closed subagents. + + See + [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/agents/sessions/{session_id}/subagents", session_id=session_id), + page=AsyncCursorPage[Subagent], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + subagent_list_params.SubagentListParams, + ), + security={"bearer_auth": True}, + ), + model=Subagent, + ) + + +class SubagentsWithRawResponse: + def __init__(self, subagents: Subagents) -> None: + self._subagents = subagents + + self.retrieve = _legacy_response.to_raw_response_wrapper( + subagents.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + subagents.list, + ) + + @cached_property + def items(self) -> ItemsWithRawResponse: + return ItemsWithRawResponse(self._subagents.items) + + @cached_property + def turns(self) -> TurnsWithRawResponse: + return TurnsWithRawResponse(self._subagents.turns) + + +class AsyncSubagentsWithRawResponse: + def __init__(self, subagents: AsyncSubagents) -> None: + self._subagents = subagents + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + subagents.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + subagents.list, + ) + + @cached_property + def items(self) -> AsyncItemsWithRawResponse: + return AsyncItemsWithRawResponse(self._subagents.items) + + @cached_property + def turns(self) -> AsyncTurnsWithRawResponse: + return AsyncTurnsWithRawResponse(self._subagents.turns) + + +class SubagentsWithStreamingResponse: + def __init__(self, subagents: Subagents) -> None: + self._subagents = subagents + + self.retrieve = to_streamed_response_wrapper( + subagents.retrieve, + ) + self.list = to_streamed_response_wrapper( + subagents.list, + ) + + @cached_property + def items(self) -> ItemsWithStreamingResponse: + return ItemsWithStreamingResponse(self._subagents.items) + + @cached_property + def turns(self) -> TurnsWithStreamingResponse: + return TurnsWithStreamingResponse(self._subagents.turns) + + +class AsyncSubagentsWithStreamingResponse: + def __init__(self, subagents: AsyncSubagents) -> None: + self._subagents = subagents + + self.retrieve = async_to_streamed_response_wrapper( + subagents.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + subagents.list, + ) + + @cached_property + def items(self) -> AsyncItemsWithStreamingResponse: + return AsyncItemsWithStreamingResponse(self._subagents.items) + + @cached_property + def turns(self) -> AsyncTurnsWithStreamingResponse: + return AsyncTurnsWithStreamingResponse(self._subagents.turns) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/__init__.py new file mode 100644 index 0000000..8894a02 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .items import ( + Items as Items, + AsyncItems as AsyncItems, + ItemsWithRawResponse as ItemsWithRawResponse, + AsyncItemsWithRawResponse as AsyncItemsWithRawResponse, + ItemsWithStreamingResponse as ItemsWithStreamingResponse, + AsyncItemsWithStreamingResponse as AsyncItemsWithStreamingResponse, + ) + from .turns import ( + Turns as Turns, + AsyncTurns as AsyncTurns, + TurnsWithRawResponse as TurnsWithRawResponse, + AsyncTurnsWithRawResponse as AsyncTurnsWithRawResponse, + TurnsWithStreamingResponse as TurnsWithStreamingResponse, + AsyncTurnsWithStreamingResponse as AsyncTurnsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Items": (".items", "Items"), + "AsyncItems": (".items", "AsyncItems"), + "ItemsWithRawResponse": (".items", "ItemsWithRawResponse"), + "AsyncItemsWithRawResponse": (".items", "AsyncItemsWithRawResponse"), + "ItemsWithStreamingResponse": (".items", "ItemsWithStreamingResponse"), + "AsyncItemsWithStreamingResponse": (".items", "AsyncItemsWithStreamingResponse"), + "Turns": (".turns", "Turns"), + "AsyncTurns": (".turns", "AsyncTurns"), + "TurnsWithRawResponse": (".turns", "TurnsWithRawResponse"), + "AsyncTurnsWithRawResponse": (".turns", "AsyncTurnsWithRawResponse"), + "TurnsWithStreamingResponse": (".turns", "TurnsWithStreamingResponse"), + "AsyncTurnsWithStreamingResponse": (".turns", "AsyncTurnsWithStreamingResponse"), + } + _SUBMODULES = { + "items", + "turns", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Items", + "AsyncItems", + "ItemsWithRawResponse", + "AsyncItemsWithRawResponse", + "ItemsWithStreamingResponse", + "AsyncItemsWithStreamingResponse", + "Turns", + "AsyncTurns", + "TurnsWithRawResponse", + "AsyncTurnsWithRawResponse", + "TurnsWithStreamingResponse", + "AsyncTurnsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f9aaca7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/__pycache__/items.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/__pycache__/items.cpython-312.pyc new file mode 100644 index 0000000..bc92537 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/__pycache__/items.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/__pycache__/turns.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/__pycache__/turns.cpython-312.pyc new file mode 100644 index 0000000..18a1a4d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/__pycache__/turns.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/items.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/items.py new file mode 100644 index 0000000..a58e8ef --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/items.py @@ -0,0 +1,243 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, cast +from typing_extensions import Literal + +import httpx2 + +from ....... import _legacy_response +from ......._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......._utils import path_template, maybe_transform +from ......._compat import cached_property +from ......._resource import SyncAPIResource, AsyncAPIResource +from ......._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .......pagination import SyncCursorPage, AsyncCursorPage +from ......._base_client import AsyncPaginator, make_request_options +from .......types.beta.agent_session_item import AgentSessionItem +from .......types.beta.agents.sessions.subagents.turns import item_list_params + +__all__ = ["Items", "AsyncItems"] + + +class Items(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ItemsWithStreamingResponse(self) + + def list( + self, + turn_id: str, + *, + session_id: str, + subagent_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[AgentSessionItem]: + """Lists items belonging to one turn of this subagent. + + See + [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not subagent_id: + raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}") + if not turn_id: + raise ValueError(f"Expected a non-empty value for `turn_id` but received {turn_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template( + "/agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}/items", + session_id=session_id, + subagent_id=subagent_id, + turn_id=turn_id, + ), + page=SyncCursorPage[AgentSessionItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + item_list_params.ItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, AgentSessionItem), # Union types cannot be passed in as arguments in the type system + ) + + +class AsyncItems(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncItemsWithStreamingResponse(self) + + def list( + self, + turn_id: str, + *, + session_id: str, + subagent_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[AgentSessionItem, AsyncCursorPage[AgentSessionItem]]: + """Lists items belonging to one turn of this subagent. + + See + [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not subagent_id: + raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}") + if not turn_id: + raise ValueError(f"Expected a non-empty value for `turn_id` but received {turn_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template( + "/agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}/items", + session_id=session_id, + subagent_id=subagent_id, + turn_id=turn_id, + ), + page=AsyncCursorPage[AgentSessionItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + item_list_params.ItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, AgentSessionItem), # Union types cannot be passed in as arguments in the type system + ) + + +class ItemsWithRawResponse: + def __init__(self, items: Items) -> None: + self._items = items + + self.list = _legacy_response.to_raw_response_wrapper( + items.list, + ) + + +class AsyncItemsWithRawResponse: + def __init__(self, items: AsyncItems) -> None: + self._items = items + + self.list = _legacy_response.async_to_raw_response_wrapper( + items.list, + ) + + +class ItemsWithStreamingResponse: + def __init__(self, items: Items) -> None: + self._items = items + + self.list = to_streamed_response_wrapper( + items.list, + ) + + +class AsyncItemsWithStreamingResponse: + def __init__(self, items: AsyncItems) -> None: + self._items = items + + self.list = async_to_streamed_response_wrapper( + items.list, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/turns.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/turns.py new file mode 100644 index 0000000..ef879ec --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/subagents/turns/turns.py @@ -0,0 +1,380 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from .items import ( + Items, + AsyncItems, + ItemsWithRawResponse, + AsyncItemsWithRawResponse, + ItemsWithStreamingResponse, + AsyncItemsWithStreamingResponse, +) +from ....... import _legacy_response +from ......._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......._utils import path_template, maybe_transform +from ......._compat import cached_property +from ......._resource import SyncAPIResource, AsyncAPIResource +from ......._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .......pagination import SyncCursorPage, AsyncCursorPage +from ......._base_client import AsyncPaginator, make_request_options +from .......types.beta.agents.sessions.turn import Turn +from .......types.beta.agents.sessions.subagents import turn_list_params + +__all__ = ["Turns", "AsyncTurns"] + + +class Turns(SyncAPIResource): + @cached_property + def items(self) -> Items: + return Items(self._client) + + @cached_property + def with_raw_response(self) -> TurnsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return TurnsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TurnsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return TurnsWithStreamingResponse(self) + + def retrieve( + self, + turn_id: str, + *, + session_id: str, + subagent_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Turn: + """Retrieves a turn belonging to this subagent. + + See + [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not subagent_id: + raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}") + if not turn_id: + raise ValueError(f"Expected a non-empty value for `turn_id` but received {turn_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get( + path_template( + "/agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}", + session_id=session_id, + subagent_id=subagent_id, + turn_id=turn_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Turn, + ) + + def list( + self, + subagent_id: str, + *, + session_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Turn]: + """Lists all turns of this subagent, including turns after a resume. + + See + [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not subagent_id: + raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template( + "/agents/sessions/{session_id}/subagents/{subagent_id}/turns", + session_id=session_id, + subagent_id=subagent_id, + ), + page=SyncCursorPage[Turn], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + turn_list_params.TurnListParams, + ), + security={"bearer_auth": True}, + ), + model=Turn, + ) + + +class AsyncTurns(AsyncAPIResource): + @cached_property + def items(self) -> AsyncItems: + return AsyncItems(self._client) + + @cached_property + def with_raw_response(self) -> AsyncTurnsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncTurnsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTurnsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncTurnsWithStreamingResponse(self) + + async def retrieve( + self, + turn_id: str, + *, + session_id: str, + subagent_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Turn: + """Retrieves a turn belonging to this subagent. + + See + [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not subagent_id: + raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}") + if not turn_id: + raise ValueError(f"Expected a non-empty value for `turn_id` but received {turn_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._get( + path_template( + "/agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}", + session_id=session_id, + subagent_id=subagent_id, + turn_id=turn_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Turn, + ) + + def list( + self, + subagent_id: str, + *, + session_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Turn, AsyncCursorPage[Turn]]: + """Lists all turns of this subagent, including turns after a resume. + + See + [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not subagent_id: + raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template( + "/agents/sessions/{session_id}/subagents/{subagent_id}/turns", + session_id=session_id, + subagent_id=subagent_id, + ), + page=AsyncCursorPage[Turn], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + turn_list_params.TurnListParams, + ), + security={"bearer_auth": True}, + ), + model=Turn, + ) + + +class TurnsWithRawResponse: + def __init__(self, turns: Turns) -> None: + self._turns = turns + + self.retrieve = _legacy_response.to_raw_response_wrapper( + turns.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + turns.list, + ) + + @cached_property + def items(self) -> ItemsWithRawResponse: + return ItemsWithRawResponse(self._turns.items) + + +class AsyncTurnsWithRawResponse: + def __init__(self, turns: AsyncTurns) -> None: + self._turns = turns + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + turns.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + turns.list, + ) + + @cached_property + def items(self) -> AsyncItemsWithRawResponse: + return AsyncItemsWithRawResponse(self._turns.items) + + +class TurnsWithStreamingResponse: + def __init__(self, turns: Turns) -> None: + self._turns = turns + + self.retrieve = to_streamed_response_wrapper( + turns.retrieve, + ) + self.list = to_streamed_response_wrapper( + turns.list, + ) + + @cached_property + def items(self) -> ItemsWithStreamingResponse: + return ItemsWithStreamingResponse(self._turns.items) + + +class AsyncTurnsWithStreamingResponse: + def __init__(self, turns: AsyncTurns) -> None: + self._turns = turns + + self.retrieve = async_to_streamed_response_wrapper( + turns.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + turns.list, + ) + + @cached_property + def items(self) -> AsyncItemsWithStreamingResponse: + return AsyncItemsWithStreamingResponse(self._turns.items) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/turns.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/turns.py new file mode 100644 index 0000000..7ef3d38 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/sessions/turns.py @@ -0,0 +1,322 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.beta.agents.sessions import turn_list_params +from .....types.beta.agents.sessions.turn import Turn + +__all__ = ["Turns", "AsyncTurns"] + + +class Turns(SyncAPIResource): + @cached_property + def with_raw_response(self) -> TurnsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return TurnsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TurnsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return TurnsWithStreamingResponse(self) + + def retrieve( + self, + turn_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Turn: + """Retrieves a turn's current status, timestamps, usage, and error. + + Returns 404 if + the turn does not belong to the session. See + [session turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#inspect-session-turns). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not turn_id: + raise ValueError(f"Expected a non-empty value for `turn_id` but received {turn_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get( + path_template("/agents/sessions/{session_id}/turns/{turn_id}", session_id=session_id, turn_id=turn_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Turn, + ) + + def list( + self, + session_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Turn]: + """Lists turns by creation time and turn ID. + + The after cursor is exclusive in the + selected order. See + [session turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#inspect-session-turns). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/agents/sessions/{session_id}/turns", session_id=session_id), + page=SyncCursorPage[Turn], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + turn_list_params.TurnListParams, + ), + security={"bearer_auth": True}, + ), + model=Turn, + ) + + +class AsyncTurns(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncTurnsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncTurnsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTurnsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncTurnsWithStreamingResponse(self) + + async def retrieve( + self, + turn_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Turn: + """Retrieves a turn's current status, timestamps, usage, and error. + + Returns 404 if + the turn does not belong to the session. See + [session turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#inspect-session-turns). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not turn_id: + raise ValueError(f"Expected a non-empty value for `turn_id` but received {turn_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._get( + path_template("/agents/sessions/{session_id}/turns/{turn_id}", session_id=session_id, turn_id=turn_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Turn, + ) + + def list( + self, + session_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Turn, AsyncCursorPage[Turn]]: + """Lists turns by creation time and turn ID. + + The after cursor is exclusive in the + selected order. See + [session turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#inspect-session-turns). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return, between 1 and 100. Defaults to 20. + + order: The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/agents/sessions/{session_id}/turns", session_id=session_id), + page=AsyncCursorPage[Turn], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + turn_list_params.TurnListParams, + ), + security={"bearer_auth": True}, + ), + model=Turn, + ) + + +class TurnsWithRawResponse: + def __init__(self, turns: Turns) -> None: + self._turns = turns + + self.retrieve = _legacy_response.to_raw_response_wrapper( + turns.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + turns.list, + ) + + +class AsyncTurnsWithRawResponse: + def __init__(self, turns: AsyncTurns) -> None: + self._turns = turns + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + turns.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + turns.list, + ) + + +class TurnsWithStreamingResponse: + def __init__(self, turns: Turns) -> None: + self._turns = turns + + self.retrieve = to_streamed_response_wrapper( + turns.retrieve, + ) + self.list = to_streamed_response_wrapper( + turns.list, + ) + + +class AsyncTurnsWithStreamingResponse: + def __init__(self, turns: AsyncTurns) -> None: + self._turns = turns + + self.retrieve = async_to_streamed_response_wrapper( + turns.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + turns.list, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/__init__.py new file mode 100644 index 0000000..f55bb84 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .vaults import ( + Vaults as Vaults, + AsyncVaults as AsyncVaults, + VaultsWithRawResponse as VaultsWithRawResponse, + AsyncVaultsWithRawResponse as AsyncVaultsWithRawResponse, + VaultsWithStreamingResponse as VaultsWithStreamingResponse, + AsyncVaultsWithStreamingResponse as AsyncVaultsWithStreamingResponse, + ) + from .credentials import ( + Credentials as Credentials, + AsyncCredentials as AsyncCredentials, + CredentialsWithRawResponse as CredentialsWithRawResponse, + AsyncCredentialsWithRawResponse as AsyncCredentialsWithRawResponse, + CredentialsWithStreamingResponse as CredentialsWithStreamingResponse, + AsyncCredentialsWithStreamingResponse as AsyncCredentialsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Credentials": (".credentials", "Credentials"), + "AsyncCredentials": (".credentials", "AsyncCredentials"), + "CredentialsWithRawResponse": (".credentials", "CredentialsWithRawResponse"), + "AsyncCredentialsWithRawResponse": (".credentials", "AsyncCredentialsWithRawResponse"), + "CredentialsWithStreamingResponse": (".credentials", "CredentialsWithStreamingResponse"), + "AsyncCredentialsWithStreamingResponse": (".credentials", "AsyncCredentialsWithStreamingResponse"), + "Vaults": (".vaults", "Vaults"), + "AsyncVaults": (".vaults", "AsyncVaults"), + "VaultsWithRawResponse": (".vaults", "VaultsWithRawResponse"), + "AsyncVaultsWithRawResponse": (".vaults", "AsyncVaultsWithRawResponse"), + "VaultsWithStreamingResponse": (".vaults", "VaultsWithStreamingResponse"), + "AsyncVaultsWithStreamingResponse": (".vaults", "AsyncVaultsWithStreamingResponse"), + } + _SUBMODULES = { + "credentials", + "vaults", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Credentials", + "AsyncCredentials", + "CredentialsWithRawResponse", + "AsyncCredentialsWithRawResponse", + "CredentialsWithStreamingResponse", + "AsyncCredentialsWithStreamingResponse", + "Vaults", + "AsyncVaults", + "VaultsWithRawResponse", + "AsyncVaultsWithRawResponse", + "VaultsWithStreamingResponse", + "AsyncVaultsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a2d8742 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/__pycache__/credentials.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/__pycache__/credentials.cpython-312.pyc new file mode 100644 index 0000000..871d034 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/__pycache__/credentials.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/__pycache__/vaults.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/__pycache__/vaults.cpython-312.pyc new file mode 100644 index 0000000..ae00db0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/__pycache__/vaults.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/credentials.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/credentials.py new file mode 100644 index 0000000..71cabc2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/credentials.py @@ -0,0 +1,709 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.beta.agents.vaults import ( + credential_list_params, + credential_create_params, + credential_update_params, +) +from .....types.beta.agents.vaults.credential import Credential +from .....types.beta.agents.vault_status_filter_param import VaultStatusFilterParam +from .....types.beta.agents.vaults.credential_deleted import CredentialDeleted +from .....types.beta.agents.vaults.credential_auth_create_param import CredentialAuthCreateParam +from .....types.beta.agents.vaults.credential_auth_rotate_param import CredentialAuthRotateParam + +__all__ = ["Credentials", "AsyncCredentials"] + + +class Credentials(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CredentialsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CredentialsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CredentialsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CredentialsWithStreamingResponse(self) + + def create( + self, + vault_id: str, + *, + auth: CredentialAuthCreateParam, + name: str, + metadata: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Credential: + """Creates a vault credential. + + Secret values are write-only and are never returned. + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + auth: The authentication method and write-only secret values to store. + + name: The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after + trimming. + + metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. Defaults to an empty map. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._post( + path_template("/vaults/{vault_id}/credentials", vault_id=vault_id), + body=maybe_transform( + { + "auth": auth, + "name": name, + "metadata": metadata, + }, + credential_create_params.CredentialCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Credential, + ) + + def retrieve( + self, + credential_id: str, + *, + vault_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Credential: + """Retrieves vault credential metadata without returning secret values. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + if not credential_id: + raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get( + path_template( + "/vaults/{vault_id}/credentials/{credential_id}", vault_id=vault_id, credential_id=credential_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Credential, + ) + + def update( + self, + credential_id: str, + *, + vault_id: str, + auth: CredentialAuthRotateParam | Omit = omit, + metadata: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Credential: + """Updates credential metadata or rotates its write-only secret. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + auth: Replacement values for the credential's existing authentication method. + + metadata: Replaces all metadata. Omit to preserve it, or pass {} to clear it. Up to 16 + string key-value pairs, with keys up to 64 and values up to 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + if not credential_id: + raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._post( + path_template( + "/vaults/{vault_id}/credentials/{credential_id}", vault_id=vault_id, credential_id=credential_id + ), + body=maybe_transform( + { + "auth": auth, + "metadata": metadata, + }, + credential_update_params.CredentialUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Credential, + ) + + def list( + self, + vault_id: str, + *, + after: str | Omit = omit, + limit: Optional[int] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: VaultStatusFilterParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Credential]: + """ + Lists a vault's credentials using ID-based pagination without returning secret + values. See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return. Defaults to 20. Values are clamped + between 1 and 100. + + order: Sort order by the `created_at` timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + status: Filter by one status or a list, such as `status=active` or + `status[]=active&status[]=archived`. Both statuses are included by default. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/vaults/{vault_id}/credentials", vault_id=vault_id), + page=SyncCursorPage[Credential], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "status": status, + }, + credential_list_params.CredentialListParams, + ), + security={"bearer_auth": True}, + ), + model=Credential, + ) + + def delete( + self, + credential_id: str, + *, + vault_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> CredentialDeleted: + """Deletes a vault credential. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + if not credential_id: + raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._delete( + path_template( + "/vaults/{vault_id}/credentials/{credential_id}", vault_id=vault_id, credential_id=credential_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=CredentialDeleted, + ) + + +class AsyncCredentials(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCredentialsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCredentialsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCredentialsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCredentialsWithStreamingResponse(self) + + async def create( + self, + vault_id: str, + *, + auth: CredentialAuthCreateParam, + name: str, + metadata: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Credential: + """Creates a vault credential. + + Secret values are write-only and are never returned. + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + auth: The authentication method and write-only secret values to store. + + name: The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after + trimming. + + metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. Defaults to an empty map. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._post( + path_template("/vaults/{vault_id}/credentials", vault_id=vault_id), + body=await async_maybe_transform( + { + "auth": auth, + "name": name, + "metadata": metadata, + }, + credential_create_params.CredentialCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Credential, + ) + + async def retrieve( + self, + credential_id: str, + *, + vault_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Credential: + """Retrieves vault credential metadata without returning secret values. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + if not credential_id: + raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._get( + path_template( + "/vaults/{vault_id}/credentials/{credential_id}", vault_id=vault_id, credential_id=credential_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Credential, + ) + + async def update( + self, + credential_id: str, + *, + vault_id: str, + auth: CredentialAuthRotateParam | Omit = omit, + metadata: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Credential: + """Updates credential metadata or rotates its write-only secret. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + auth: Replacement values for the credential's existing authentication method. + + metadata: Replaces all metadata. Omit to preserve it, or pass {} to clear it. Up to 16 + string key-value pairs, with keys up to 64 and values up to 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + if not credential_id: + raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._post( + path_template( + "/vaults/{vault_id}/credentials/{credential_id}", vault_id=vault_id, credential_id=credential_id + ), + body=await async_maybe_transform( + { + "auth": auth, + "metadata": metadata, + }, + credential_update_params.CredentialUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Credential, + ) + + def list( + self, + vault_id: str, + *, + after: str | Omit = omit, + limit: Optional[int] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: VaultStatusFilterParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Credential, AsyncCursorPage[Credential]]: + """ + Lists a vault's credentials using ID-based pagination without returning secret + values. See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return. Defaults to 20. Values are clamped + between 1 and 100. + + order: Sort order by the `created_at` timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + status: Filter by one status or a list, such as `status=active` or + `status[]=active&status[]=archived`. Both statuses are included by default. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/vaults/{vault_id}/credentials", vault_id=vault_id), + page=AsyncCursorPage[Credential], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "status": status, + }, + credential_list_params.CredentialListParams, + ), + security={"bearer_auth": True}, + ), + model=Credential, + ) + + async def delete( + self, + credential_id: str, + *, + vault_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> CredentialDeleted: + """Deletes a vault credential. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + if not credential_id: + raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._delete( + path_template( + "/vaults/{vault_id}/credentials/{credential_id}", vault_id=vault_id, credential_id=credential_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=CredentialDeleted, + ) + + +class CredentialsWithRawResponse: + def __init__(self, credentials: Credentials) -> None: + self._credentials = credentials + + self.create = _legacy_response.to_raw_response_wrapper( + credentials.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + credentials.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + credentials.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + credentials.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + credentials.delete, + ) + + +class AsyncCredentialsWithRawResponse: + def __init__(self, credentials: AsyncCredentials) -> None: + self._credentials = credentials + + self.create = _legacy_response.async_to_raw_response_wrapper( + credentials.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + credentials.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + credentials.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + credentials.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + credentials.delete, + ) + + +class CredentialsWithStreamingResponse: + def __init__(self, credentials: Credentials) -> None: + self._credentials = credentials + + self.create = to_streamed_response_wrapper( + credentials.create, + ) + self.retrieve = to_streamed_response_wrapper( + credentials.retrieve, + ) + self.update = to_streamed_response_wrapper( + credentials.update, + ) + self.list = to_streamed_response_wrapper( + credentials.list, + ) + self.delete = to_streamed_response_wrapper( + credentials.delete, + ) + + +class AsyncCredentialsWithStreamingResponse: + def __init__(self, credentials: AsyncCredentials) -> None: + self._credentials = credentials + + self.create = async_to_streamed_response_wrapper( + credentials.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + credentials.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + credentials.update, + ) + self.list = async_to_streamed_response_wrapper( + credentials.list, + ) + self.delete = async_to_streamed_response_wrapper( + credentials.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/vaults.py b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/vaults.py new file mode 100644 index 0000000..9688bd5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/agents/vaults/vaults.py @@ -0,0 +1,563 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from .credentials import ( + Credentials, + AsyncCredentials, + CredentialsWithRawResponse, + AsyncCredentialsWithRawResponse, + CredentialsWithStreamingResponse, + AsyncCredentialsWithStreamingResponse, +) +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.beta.agents import vault_list_params, vault_create_params +from .....types.beta.agents.vault import Vault +from .....types.beta.agents.vault_deleted import VaultDeleted +from .....types.beta.agents.vault_status_filter_param import VaultStatusFilterParam + +__all__ = ["Vaults", "AsyncVaults"] + + +class Vaults(SyncAPIResource): + @cached_property + def credentials(self) -> Credentials: + return Credentials(self._client) + + @cached_property + def with_raw_response(self) -> VaultsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return VaultsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> VaultsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return VaultsWithStreamingResponse(self) + + def create( + self, + *, + metadata: Optional[Dict[str, str]] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Vault: + """Creates a vault for the current project. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + metadata: Key-value pairs to associate with the vault, such as an application or team + identifier. + + name: The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after + trimming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._post( + "/vaults", + body=maybe_transform( + { + "metadata": metadata, + "name": name, + }, + vault_create_params.VaultCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Vault, + ) + + def retrieve( + self, + vault_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Vault: + """Retrieves a vault by its ID. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get( + path_template("/vaults/{vault_id}", vault_id=vault_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Vault, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: Optional[int] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: VaultStatusFilterParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Vault]: + """Lists vaults using ID-based pagination. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return. Defaults to 20. Values are clamped + between 1 and 100. + + order: Sort order by the `created_at` timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + status: Filter by one status or a list, such as `status=active` or + `status[]=active&status[]=archived`. Both statuses are included by default. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + "/vaults", + page=SyncCursorPage[Vault], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "status": status, + }, + vault_list_params.VaultListParams, + ), + security={"bearer_auth": True}, + ), + model=Vault, + ) + + def delete( + self, + vault_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VaultDeleted: + """Deletes a vault and all its credentials. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._delete( + path_template("/vaults/{vault_id}", vault_id=vault_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VaultDeleted, + ) + + +class AsyncVaults(AsyncAPIResource): + @cached_property + def credentials(self) -> AsyncCredentials: + return AsyncCredentials(self._client) + + @cached_property + def with_raw_response(self) -> AsyncVaultsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncVaultsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncVaultsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncVaultsWithStreamingResponse(self) + + async def create( + self, + *, + metadata: Optional[Dict[str, str]] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Vault: + """Creates a vault for the current project. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + metadata: Key-value pairs to associate with the vault, such as an application or team + identifier. + + name: The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after + trimming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._post( + "/vaults", + body=await async_maybe_transform( + { + "metadata": metadata, + "name": name, + }, + vault_create_params.VaultCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Vault, + ) + + async def retrieve( + self, + vault_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Vault: + """Retrieves a vault by its ID. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._get( + path_template("/vaults/{vault_id}", vault_id=vault_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Vault, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: Optional[int] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: VaultStatusFilterParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Vault, AsyncCursorPage[Vault]]: + """Lists vaults using ID-based pagination. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + after: Return resources after this resource ID in the selected order. + + limit: The maximum number of resources to return. Defaults to 20. Values are clamped + between 1 and 100. + + order: Sort order by the `created_at` timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + + status: Filter by one status or a list, such as `status=active` or + `status[]=active&status[]=archived`. Both statuses are included by default. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return self._get_api_list( + "/vaults", + page=AsyncCursorPage[Vault], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "status": status, + }, + vault_list_params.VaultListParams, + ), + security={"bearer_auth": True}, + ), + model=Vault, + ) + + async def delete( + self, + vault_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VaultDeleted: + """Deletes a vault and all its credentials. + + See + [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vault_id: + raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") + extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})} + return await self._delete( + path_template("/vaults/{vault_id}", vault_id=vault_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VaultDeleted, + ) + + +class VaultsWithRawResponse: + def __init__(self, vaults: Vaults) -> None: + self._vaults = vaults + + self.create = _legacy_response.to_raw_response_wrapper( + vaults.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + vaults.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + vaults.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + vaults.delete, + ) + + @cached_property + def credentials(self) -> CredentialsWithRawResponse: + return CredentialsWithRawResponse(self._vaults.credentials) + + +class AsyncVaultsWithRawResponse: + def __init__(self, vaults: AsyncVaults) -> None: + self._vaults = vaults + + self.create = _legacy_response.async_to_raw_response_wrapper( + vaults.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + vaults.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + vaults.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + vaults.delete, + ) + + @cached_property + def credentials(self) -> AsyncCredentialsWithRawResponse: + return AsyncCredentialsWithRawResponse(self._vaults.credentials) + + +class VaultsWithStreamingResponse: + def __init__(self, vaults: Vaults) -> None: + self._vaults = vaults + + self.create = to_streamed_response_wrapper( + vaults.create, + ) + self.retrieve = to_streamed_response_wrapper( + vaults.retrieve, + ) + self.list = to_streamed_response_wrapper( + vaults.list, + ) + self.delete = to_streamed_response_wrapper( + vaults.delete, + ) + + @cached_property + def credentials(self) -> CredentialsWithStreamingResponse: + return CredentialsWithStreamingResponse(self._vaults.credentials) + + +class AsyncVaultsWithStreamingResponse: + def __init__(self, vaults: AsyncVaults) -> None: + self._vaults = vaults + + self.create = async_to_streamed_response_wrapper( + vaults.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + vaults.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + vaults.list, + ) + self.delete = async_to_streamed_response_wrapper( + vaults.delete, + ) + + @cached_property + def credentials(self) -> AsyncCredentialsWithStreamingResponse: + return AsyncCredentialsWithStreamingResponse(self._vaults.credentials) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/assistants.py b/venv/lib/python3.12/site-packages/openai/resources/beta/assistants.py new file mode 100644 index 0000000..a5c616b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/assistants.py @@ -0,0 +1,1114 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import typing_extensions +from typing import Union, Iterable, Optional +from typing_extensions import Literal + +import httpx2 + +from ... import _legacy_response +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncCursorPage, AsyncCursorPage +from ...types.beta import ( + assistant_list_params, + assistant_create_params, + assistant_update_params, +) +from ..._base_client import AsyncPaginator, make_request_options +from ...types.beta.assistant import Assistant +from ...types.shared.chat_model import ChatModel +from ...types.beta.assistant_deleted import AssistantDeleted +from ...types.shared_params.metadata import Metadata +from ...types.shared.reasoning_effort import ReasoningEffort +from ...types.beta.assistant_tool_param import AssistantToolParam +from ...types.beta.assistant_response_format_option_param import AssistantResponseFormatOptionParam + +__all__ = ["Assistants", "AsyncAssistants"] + + +class Assistants(SyncAPIResource): + """Build Assistants that can call models and use tools.""" + + @cached_property + def with_raw_response(self) -> AssistantsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AssistantsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AssistantsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AssistantsWithStreamingResponse(self) + + @typing_extensions.deprecated("deprecated") + def create( + self, + *, + model: Union[str, ChatModel], + description: Optional[str] | Omit = omit, + instructions: Optional[str] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + name: Optional[str] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_resources: Optional[assistant_create_params.ToolResources] | Omit = omit, + tools: Iterable[AssistantToolParam] | Omit = omit, + top_p: Optional[float] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Assistant: + """ + Create an assistant with a model and instructions. + + Args: + model: ID of the model to use. You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + + description: The description of the assistant. The maximum length is 512 characters. + + instructions: The system instructions that the assistant uses. The maximum length is 256,000 + characters. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the assistant. The maximum length is 256 characters. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + + tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per + assistant. Tools can be of types `code_interpreter`, `file_search`, or + `function`. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + "/assistants", + body=maybe_transform( + { + "model": model, + "description": description, + "instructions": instructions, + "metadata": metadata, + "name": name, + "reasoning_effort": reasoning_effort, + "response_format": response_format, + "temperature": temperature, + "tool_resources": tool_resources, + "tools": tools, + "top_p": top_p, + }, + assistant_create_params.AssistantCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Assistant, + ) + + @typing_extensions.deprecated("deprecated") + def retrieve( + self, + assistant_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Assistant: + """ + Retrieves an assistant. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get( + path_template("/assistants/{assistant_id}", assistant_id=assistant_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Assistant, + ) + + @typing_extensions.deprecated("deprecated") + def update( + self, + assistant_id: str, + *, + description: Optional[str] | Omit = omit, + instructions: Optional[str] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[ + str, + Literal[ + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4.5-preview", + "gpt-4.5-preview-2025-02-27", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + ] + | Omit = omit, + name: Optional[str] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_resources: Optional[assistant_update_params.ToolResources] | Omit = omit, + tools: Iterable[AssistantToolParam] | Omit = omit, + top_p: Optional[float] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Assistant: + """Modifies an assistant. + + Args: + description: The description of the assistant. + + The maximum length is 512 characters. + + instructions: The system instructions that the assistant uses. The maximum length is 256,000 + characters. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: ID of the model to use. You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + + name: The name of the assistant. The maximum length is 256 characters. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + + tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per + assistant. Tools can be of types `code_interpreter`, `file_search`, or + `function`. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + path_template("/assistants/{assistant_id}", assistant_id=assistant_id), + body=maybe_transform( + { + "description": description, + "instructions": instructions, + "metadata": metadata, + "model": model, + "name": name, + "reasoning_effort": reasoning_effort, + "response_format": response_format, + "temperature": temperature, + "tool_resources": tool_resources, + "tools": tools, + "top_p": top_p, + }, + assistant_update_params.AssistantUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Assistant, + ) + + @typing_extensions.deprecated("deprecated") + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Assistant]: + """Returns a list of assistants. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + "/assistants", + page=SyncCursorPage[Assistant], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + assistant_list_params.AssistantListParams, + ), + security={"bearer_auth": True}, + ), + model=Assistant, + ) + + @typing_extensions.deprecated("deprecated") + def delete( + self, + assistant_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AssistantDeleted: + """ + Delete an assistant. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._delete( + path_template("/assistants/{assistant_id}", assistant_id=assistant_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=AssistantDeleted, + ) + + +class AsyncAssistants(AsyncAPIResource): + """Build Assistants that can call models and use tools.""" + + @cached_property + def with_raw_response(self) -> AsyncAssistantsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAssistantsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAssistantsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAssistantsWithStreamingResponse(self) + + @typing_extensions.deprecated("deprecated") + async def create( + self, + *, + model: Union[str, ChatModel], + description: Optional[str] | Omit = omit, + instructions: Optional[str] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + name: Optional[str] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_resources: Optional[assistant_create_params.ToolResources] | Omit = omit, + tools: Iterable[AssistantToolParam] | Omit = omit, + top_p: Optional[float] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Assistant: + """ + Create an assistant with a model and instructions. + + Args: + model: ID of the model to use. You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + + description: The description of the assistant. The maximum length is 512 characters. + + instructions: The system instructions that the assistant uses. The maximum length is 256,000 + characters. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the assistant. The maximum length is 256 characters. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + + tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per + assistant. Tools can be of types `code_interpreter`, `file_search`, or + `function`. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + "/assistants", + body=await async_maybe_transform( + { + "model": model, + "description": description, + "instructions": instructions, + "metadata": metadata, + "name": name, + "reasoning_effort": reasoning_effort, + "response_format": response_format, + "temperature": temperature, + "tool_resources": tool_resources, + "tools": tools, + "top_p": top_p, + }, + assistant_create_params.AssistantCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Assistant, + ) + + @typing_extensions.deprecated("deprecated") + async def retrieve( + self, + assistant_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Assistant: + """ + Retrieves an assistant. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._get( + path_template("/assistants/{assistant_id}", assistant_id=assistant_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Assistant, + ) + + @typing_extensions.deprecated("deprecated") + async def update( + self, + assistant_id: str, + *, + description: Optional[str] | Omit = omit, + instructions: Optional[str] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[ + str, + Literal[ + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4.5-preview", + "gpt-4.5-preview-2025-02-27", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + ] + | Omit = omit, + name: Optional[str] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_resources: Optional[assistant_update_params.ToolResources] | Omit = omit, + tools: Iterable[AssistantToolParam] | Omit = omit, + top_p: Optional[float] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Assistant: + """Modifies an assistant. + + Args: + description: The description of the assistant. + + The maximum length is 512 characters. + + instructions: The system instructions that the assistant uses. The maximum length is 256,000 + characters. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: ID of the model to use. You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + + name: The name of the assistant. The maximum length is 256 characters. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + + tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per + assistant. Tools can be of types `code_interpreter`, `file_search`, or + `function`. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + path_template("/assistants/{assistant_id}", assistant_id=assistant_id), + body=await async_maybe_transform( + { + "description": description, + "instructions": instructions, + "metadata": metadata, + "model": model, + "name": name, + "reasoning_effort": reasoning_effort, + "response_format": response_format, + "temperature": temperature, + "tool_resources": tool_resources, + "tools": tools, + "top_p": top_p, + }, + assistant_update_params.AssistantUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Assistant, + ) + + @typing_extensions.deprecated("deprecated") + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Assistant, AsyncCursorPage[Assistant]]: + """Returns a list of assistants. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + "/assistants", + page=AsyncCursorPage[Assistant], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + assistant_list_params.AssistantListParams, + ), + security={"bearer_auth": True}, + ), + model=Assistant, + ) + + @typing_extensions.deprecated("deprecated") + async def delete( + self, + assistant_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AssistantDeleted: + """ + Delete an assistant. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._delete( + path_template("/assistants/{assistant_id}", assistant_id=assistant_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=AssistantDeleted, + ) + + +class AssistantsWithRawResponse: + def __init__(self, assistants: Assistants) -> None: + self._assistants = assistants + + self.create = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + assistants.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + assistants.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + assistants.update, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + assistants.list, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + assistants.delete, # pyright: ignore[reportDeprecated], + ) + ) + + +class AsyncAssistantsWithRawResponse: + def __init__(self, assistants: AsyncAssistants) -> None: + self._assistants = assistants + + self.create = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + assistants.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + assistants.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + assistants.update, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + assistants.list, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + assistants.delete, # pyright: ignore[reportDeprecated], + ) + ) + + +class AssistantsWithStreamingResponse: + def __init__(self, assistants: Assistants) -> None: + self._assistants = assistants + + self.create = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + assistants.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + assistants.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + assistants.update, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + assistants.list, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + assistants.delete, # pyright: ignore[reportDeprecated], + ) + ) + + +class AsyncAssistantsWithStreamingResponse: + def __init__(self, assistants: AsyncAssistants) -> None: + self._assistants = assistants + + self.create = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + assistants.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + assistants.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + assistants.update, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + assistants.list, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + assistants.delete, # pyright: ignore[reportDeprecated], + ) + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/beta.py b/venv/lib/python3.12/site-packages/openai/resources/beta/beta.py new file mode 100644 index 0000000..0fd481d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/beta.py @@ -0,0 +1,269 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._compat import cached_property +from .assistants import ( + Assistants, + AsyncAssistants, + AssistantsWithRawResponse, + AsyncAssistantsWithRawResponse, + AssistantsWithStreamingResponse, + AsyncAssistantsWithStreamingResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource +from .agents.agents import ( + Agents, + AsyncAgents, + AgentsWithRawResponse, + AsyncAgentsWithRawResponse, + AgentsWithStreamingResponse, + AsyncAgentsWithStreamingResponse, +) +from .chatkit.chatkit import ( + ChatKit, + AsyncChatKit, + ChatKitWithRawResponse, + AsyncChatKitWithRawResponse, + ChatKitWithStreamingResponse, + AsyncChatKitWithStreamingResponse, +) +from .threads.threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, + ThreadsWithStreamingResponse, + AsyncThreadsWithStreamingResponse, +) +from ...resources.chat import Chat, AsyncChat +from .realtime.realtime import ( + Realtime, + AsyncRealtime, +) +from .responses.responses import ( + Responses, + AsyncResponses, + ResponsesWithRawResponse, + AsyncResponsesWithRawResponse, + ResponsesWithStreamingResponse, + AsyncResponsesWithStreamingResponse, +) + +__all__ = ["Beta", "AsyncBeta"] + + +class Beta(SyncAPIResource): + @cached_property + def chat(self) -> Chat: + return Chat(self._client) + + @cached_property + def realtime(self) -> Realtime: + return Realtime(self._client) + + @cached_property + def agents(self) -> Agents: + return Agents(self._client) + + @cached_property + def responses(self) -> Responses: + """Create and manage model responses.""" + return Responses(self._client) + + @cached_property + def chatkit(self) -> ChatKit: + return ChatKit(self._client) + + @cached_property + def assistants(self) -> Assistants: + """Build Assistants that can call models and use tools.""" + return Assistants(self._client) + + @cached_property + def threads(self) -> Threads: + """Build Assistants that can call models and use tools.""" + return Threads(self._client) + + @cached_property + def with_raw_response(self) -> BetaWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return BetaWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> BetaWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return BetaWithStreamingResponse(self) + + +class AsyncBeta(AsyncAPIResource): + @cached_property + def chat(self) -> AsyncChat: + return AsyncChat(self._client) + + @cached_property + def realtime(self) -> AsyncRealtime: + return AsyncRealtime(self._client) + + @cached_property + def agents(self) -> AsyncAgents: + return AsyncAgents(self._client) + + @cached_property + def responses(self) -> AsyncResponses: + """Create and manage model responses.""" + return AsyncResponses(self._client) + + @cached_property + def chatkit(self) -> AsyncChatKit: + return AsyncChatKit(self._client) + + @cached_property + def assistants(self) -> AsyncAssistants: + """Build Assistants that can call models and use tools.""" + return AsyncAssistants(self._client) + + @cached_property + def threads(self) -> AsyncThreads: + """Build Assistants that can call models and use tools.""" + return AsyncThreads(self._client) + + @cached_property + def with_raw_response(self) -> AsyncBetaWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncBetaWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncBetaWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncBetaWithStreamingResponse(self) + + +class BetaWithRawResponse: + def __init__(self, beta: Beta) -> None: + self._beta = beta + + @cached_property + def agents(self) -> AgentsWithRawResponse: + return AgentsWithRawResponse(self._beta.agents) + + @cached_property + def responses(self) -> ResponsesWithRawResponse: + """Create and manage model responses.""" + return ResponsesWithRawResponse(self._beta.responses) + + @cached_property + def chatkit(self) -> ChatKitWithRawResponse: + return ChatKitWithRawResponse(self._beta.chatkit) + + @cached_property + def assistants(self) -> AssistantsWithRawResponse: + """Build Assistants that can call models and use tools.""" + return AssistantsWithRawResponse(self._beta.assistants) + + @cached_property + def threads(self) -> ThreadsWithRawResponse: + """Build Assistants that can call models and use tools.""" + return ThreadsWithRawResponse(self._beta.threads) + + +class AsyncBetaWithRawResponse: + def __init__(self, beta: AsyncBeta) -> None: + self._beta = beta + + @cached_property + def agents(self) -> AsyncAgentsWithRawResponse: + return AsyncAgentsWithRawResponse(self._beta.agents) + + @cached_property + def responses(self) -> AsyncResponsesWithRawResponse: + """Create and manage model responses.""" + return AsyncResponsesWithRawResponse(self._beta.responses) + + @cached_property + def chatkit(self) -> AsyncChatKitWithRawResponse: + return AsyncChatKitWithRawResponse(self._beta.chatkit) + + @cached_property + def assistants(self) -> AsyncAssistantsWithRawResponse: + """Build Assistants that can call models and use tools.""" + return AsyncAssistantsWithRawResponse(self._beta.assistants) + + @cached_property + def threads(self) -> AsyncThreadsWithRawResponse: + """Build Assistants that can call models and use tools.""" + return AsyncThreadsWithRawResponse(self._beta.threads) + + +class BetaWithStreamingResponse: + def __init__(self, beta: Beta) -> None: + self._beta = beta + + @cached_property + def agents(self) -> AgentsWithStreamingResponse: + return AgentsWithStreamingResponse(self._beta.agents) + + @cached_property + def responses(self) -> ResponsesWithStreamingResponse: + """Create and manage model responses.""" + return ResponsesWithStreamingResponse(self._beta.responses) + + @cached_property + def chatkit(self) -> ChatKitWithStreamingResponse: + return ChatKitWithStreamingResponse(self._beta.chatkit) + + @cached_property + def assistants(self) -> AssistantsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" + return AssistantsWithStreamingResponse(self._beta.assistants) + + @cached_property + def threads(self) -> ThreadsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" + return ThreadsWithStreamingResponse(self._beta.threads) + + +class AsyncBetaWithStreamingResponse: + def __init__(self, beta: AsyncBeta) -> None: + self._beta = beta + + @cached_property + def agents(self) -> AsyncAgentsWithStreamingResponse: + return AsyncAgentsWithStreamingResponse(self._beta.agents) + + @cached_property + def responses(self) -> AsyncResponsesWithStreamingResponse: + """Create and manage model responses.""" + return AsyncResponsesWithStreamingResponse(self._beta.responses) + + @cached_property + def chatkit(self) -> AsyncChatKitWithStreamingResponse: + return AsyncChatKitWithStreamingResponse(self._beta.chatkit) + + @cached_property + def assistants(self) -> AsyncAssistantsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" + return AsyncAssistantsWithStreamingResponse(self._beta.assistants) + + @cached_property + def threads(self) -> AsyncThreadsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" + return AsyncThreadsWithStreamingResponse(self._beta.threads) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__init__.py new file mode 100644 index 0000000..8b22626 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__init__.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .chatkit import ( + ChatKit as ChatKit, + AsyncChatKit as AsyncChatKit, + ChatKitWithRawResponse as ChatKitWithRawResponse, + AsyncChatKitWithRawResponse as AsyncChatKitWithRawResponse, + ChatKitWithStreamingResponse as ChatKitWithStreamingResponse, + AsyncChatKitWithStreamingResponse as AsyncChatKitWithStreamingResponse, + ) + from .threads import ( + Threads as Threads, + AsyncThreads as AsyncThreads, + ThreadsWithRawResponse as ThreadsWithRawResponse, + AsyncThreadsWithRawResponse as AsyncThreadsWithRawResponse, + ThreadsWithStreamingResponse as ThreadsWithStreamingResponse, + AsyncThreadsWithStreamingResponse as AsyncThreadsWithStreamingResponse, + ) + from .sessions import ( + Sessions as Sessions, + AsyncSessions as AsyncSessions, + SessionsWithRawResponse as SessionsWithRawResponse, + AsyncSessionsWithRawResponse as AsyncSessionsWithRawResponse, + SessionsWithStreamingResponse as SessionsWithStreamingResponse, + AsyncSessionsWithStreamingResponse as AsyncSessionsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Sessions": (".sessions", "Sessions"), + "AsyncSessions": (".sessions", "AsyncSessions"), + "SessionsWithRawResponse": (".sessions", "SessionsWithRawResponse"), + "AsyncSessionsWithRawResponse": (".sessions", "AsyncSessionsWithRawResponse"), + "SessionsWithStreamingResponse": (".sessions", "SessionsWithStreamingResponse"), + "AsyncSessionsWithStreamingResponse": (".sessions", "AsyncSessionsWithStreamingResponse"), + "Threads": (".threads", "Threads"), + "AsyncThreads": (".threads", "AsyncThreads"), + "ThreadsWithRawResponse": (".threads", "ThreadsWithRawResponse"), + "AsyncThreadsWithRawResponse": (".threads", "AsyncThreadsWithRawResponse"), + "ThreadsWithStreamingResponse": (".threads", "ThreadsWithStreamingResponse"), + "AsyncThreadsWithStreamingResponse": (".threads", "AsyncThreadsWithStreamingResponse"), + "ChatKit": (".chatkit", "ChatKit"), + "AsyncChatKit": (".chatkit", "AsyncChatKit"), + "ChatKitWithRawResponse": (".chatkit", "ChatKitWithRawResponse"), + "AsyncChatKitWithRawResponse": (".chatkit", "AsyncChatKitWithRawResponse"), + "ChatKitWithStreamingResponse": (".chatkit", "ChatKitWithStreamingResponse"), + "AsyncChatKitWithStreamingResponse": (".chatkit", "AsyncChatKitWithStreamingResponse"), + } + _SUBMODULES = { + "chatkit", + "sessions", + "threads", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Sessions", + "AsyncSessions", + "SessionsWithRawResponse", + "AsyncSessionsWithRawResponse", + "SessionsWithStreamingResponse", + "AsyncSessionsWithStreamingResponse", + "Threads", + "AsyncThreads", + "ThreadsWithRawResponse", + "AsyncThreadsWithRawResponse", + "ThreadsWithStreamingResponse", + "AsyncThreadsWithStreamingResponse", + "ChatKit", + "AsyncChatKit", + "ChatKitWithRawResponse", + "AsyncChatKitWithRawResponse", + "ChatKitWithStreamingResponse", + "AsyncChatKitWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..17dd8ff Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__pycache__/chatkit.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__pycache__/chatkit.cpython-312.pyc new file mode 100644 index 0000000..25661a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__pycache__/chatkit.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__pycache__/sessions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__pycache__/sessions.cpython-312.pyc new file mode 100644 index 0000000..cf0ea02 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__pycache__/sessions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__pycache__/threads.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__pycache__/threads.cpython-312.pyc new file mode 100644 index 0000000..8abbef1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/__pycache__/threads.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/chatkit.py b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/chatkit.py new file mode 100644 index 0000000..29d8dec --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/chatkit.py @@ -0,0 +1,134 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, + ThreadsWithStreamingResponse, + AsyncThreadsWithStreamingResponse, +) +from .sessions import ( + Sessions, + AsyncSessions, + SessionsWithRawResponse, + AsyncSessionsWithRawResponse, + SessionsWithStreamingResponse, + AsyncSessionsWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["ChatKit", "AsyncChatKit"] + + +class ChatKit(SyncAPIResource): + @cached_property + def sessions(self) -> Sessions: + return Sessions(self._client) + + @cached_property + def threads(self) -> Threads: + return Threads(self._client) + + @cached_property + def with_raw_response(self) -> ChatKitWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ChatKitWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChatKitWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ChatKitWithStreamingResponse(self) + + +class AsyncChatKit(AsyncAPIResource): + @cached_property + def sessions(self) -> AsyncSessions: + return AsyncSessions(self._client) + + @cached_property + def threads(self) -> AsyncThreads: + return AsyncThreads(self._client) + + @cached_property + def with_raw_response(self) -> AsyncChatKitWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncChatKitWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChatKitWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncChatKitWithStreamingResponse(self) + + +class ChatKitWithRawResponse: + def __init__(self, chatkit: ChatKit) -> None: + self._chatkit = chatkit + + @cached_property + def sessions(self) -> SessionsWithRawResponse: + return SessionsWithRawResponse(self._chatkit.sessions) + + @cached_property + def threads(self) -> ThreadsWithRawResponse: + return ThreadsWithRawResponse(self._chatkit.threads) + + +class AsyncChatKitWithRawResponse: + def __init__(self, chatkit: AsyncChatKit) -> None: + self._chatkit = chatkit + + @cached_property + def sessions(self) -> AsyncSessionsWithRawResponse: + return AsyncSessionsWithRawResponse(self._chatkit.sessions) + + @cached_property + def threads(self) -> AsyncThreadsWithRawResponse: + return AsyncThreadsWithRawResponse(self._chatkit.threads) + + +class ChatKitWithStreamingResponse: + def __init__(self, chatkit: ChatKit) -> None: + self._chatkit = chatkit + + @cached_property + def sessions(self) -> SessionsWithStreamingResponse: + return SessionsWithStreamingResponse(self._chatkit.sessions) + + @cached_property + def threads(self) -> ThreadsWithStreamingResponse: + return ThreadsWithStreamingResponse(self._chatkit.threads) + + +class AsyncChatKitWithStreamingResponse: + def __init__(self, chatkit: AsyncChatKit) -> None: + self._chatkit = chatkit + + @cached_property + def sessions(self) -> AsyncSessionsWithStreamingResponse: + return AsyncSessionsWithStreamingResponse(self._chatkit.sessions) + + @cached_property + def threads(self) -> AsyncThreadsWithStreamingResponse: + return AsyncThreadsWithStreamingResponse(self._chatkit.threads) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/sessions.py b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/sessions.py new file mode 100644 index 0000000..6b02637 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/sessions.py @@ -0,0 +1,321 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.beta.chatkit import ( + ChatSessionWorkflowParam, + ChatSessionRateLimitsParam, + ChatSessionExpiresAfterParam, + ChatSessionChatKitConfigurationParam, + session_create_params, +) +from ....types.beta.chatkit.chat_session import ChatSession +from ....types.beta.chatkit.chat_session_workflow_param import ChatSessionWorkflowParam +from ....types.beta.chatkit.chat_session_rate_limits_param import ChatSessionRateLimitsParam +from ....types.beta.chatkit.chat_session_expires_after_param import ChatSessionExpiresAfterParam +from ....types.beta.chatkit.chat_session_chatkit_configuration_param import ChatSessionChatKitConfigurationParam + +__all__ = ["Sessions", "AsyncSessions"] + + +class Sessions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SessionsWithStreamingResponse(self) + + def create( + self, + *, + user: str, + workflow: ChatSessionWorkflowParam, + chatkit_configuration: ChatSessionChatKitConfigurationParam | Omit = omit, + expires_after: ChatSessionExpiresAfterParam | Omit = omit, + rate_limits: ChatSessionRateLimitsParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatSession: + """ + Create a ChatKit session. + + Args: + user: A free-form string that identifies your end user; ensures this Session can + access other objects that have the same `user` scope. + + workflow: Workflow that powers the session. + + chatkit_configuration: Optional overrides for ChatKit runtime configuration features + + expires_after: Optional override for session expiration timing in seconds from creation. + Defaults to 10 minutes. + + rate_limits: Optional override for per-minute request limits. When omitted, defaults to 10. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._post( + "/chatkit/sessions", + body=maybe_transform( + { + "user": user, + "workflow": workflow, + "chatkit_configuration": chatkit_configuration, + "expires_after": expires_after, + "rate_limits": rate_limits, + }, + session_create_params.SessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatSession, + ) + + def cancel( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatSession: + """ + Cancel an active ChatKit session and return its most recent metadata. + + Cancelling prevents new requests from using the issued client secret. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._post( + path_template("/chatkit/sessions/{session_id}/cancel", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatSession, + ) + + +class AsyncSessions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSessionsWithStreamingResponse(self) + + async def create( + self, + *, + user: str, + workflow: ChatSessionWorkflowParam, + chatkit_configuration: ChatSessionChatKitConfigurationParam | Omit = omit, + expires_after: ChatSessionExpiresAfterParam | Omit = omit, + rate_limits: ChatSessionRateLimitsParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatSession: + """ + Create a ChatKit session. + + Args: + user: A free-form string that identifies your end user; ensures this Session can + access other objects that have the same `user` scope. + + workflow: Workflow that powers the session. + + chatkit_configuration: Optional overrides for ChatKit runtime configuration features + + expires_after: Optional override for session expiration timing in seconds from creation. + Defaults to 10 minutes. + + rate_limits: Optional override for per-minute request limits. When omitted, defaults to 10. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return await self._post( + "/chatkit/sessions", + body=await async_maybe_transform( + { + "user": user, + "workflow": workflow, + "chatkit_configuration": chatkit_configuration, + "expires_after": expires_after, + "rate_limits": rate_limits, + }, + session_create_params.SessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatSession, + ) + + async def cancel( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatSession: + """ + Cancel an active ChatKit session and return its most recent metadata. + + Cancelling prevents new requests from using the issued client secret. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return await self._post( + path_template("/chatkit/sessions/{session_id}/cancel", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatSession, + ) + + +class SessionsWithRawResponse: + def __init__(self, sessions: Sessions) -> None: + self._sessions = sessions + + self.create = _legacy_response.to_raw_response_wrapper( + sessions.create, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + sessions.cancel, + ) + + +class AsyncSessionsWithRawResponse: + def __init__(self, sessions: AsyncSessions) -> None: + self._sessions = sessions + + self.create = _legacy_response.async_to_raw_response_wrapper( + sessions.create, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + sessions.cancel, + ) + + +class SessionsWithStreamingResponse: + def __init__(self, sessions: Sessions) -> None: + self._sessions = sessions + + self.create = to_streamed_response_wrapper( + sessions.create, + ) + self.cancel = to_streamed_response_wrapper( + sessions.cancel, + ) + + +class AsyncSessionsWithStreamingResponse: + def __init__(self, sessions: AsyncSessions) -> None: + self._sessions = sessions + + self.create = async_to_streamed_response_wrapper( + sessions.create, + ) + self.cancel = async_to_streamed_response_wrapper( + sessions.cancel, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/threads.py b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/threads.py new file mode 100644 index 0000000..28c493a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/chatkit/threads.py @@ -0,0 +1,541 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, cast +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.beta.chatkit import thread_list_params, thread_list_items_params +from ....types.beta.chatkit.chatkit_thread import ChatKitThread +from ....types.beta.chatkit.thread_delete_response import ThreadDeleteResponse +from ....types.beta.chatkit.chatkit_thread_item_list import Data + +__all__ = ["Threads", "AsyncThreads"] + + +class Threads(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ThreadsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ThreadsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ThreadsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ThreadsWithStreamingResponse(self) + + def retrieve( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatKitThread: + """ + Retrieve a ChatKit thread by its identifier. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._get( + path_template("/chatkit/threads/{thread_id}", thread_id=thread_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatKitThread, + ) + + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ChatKitThread]: + """ + List ChatKit threads with optional pagination and user filters. + + Args: + after: List items created after this thread item ID. Defaults to null for the first + page. + + before: List items created before this thread item ID. Defaults to null for the newest + results. + + limit: Maximum number of thread items to return. Defaults to 20. + + order: Sort order for results by creation time. Defaults to `desc`. + + user: Filter threads that belong to this user identifier. Defaults to null to return + all users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._get_api_list( + "/chatkit/threads", + page=SyncConversationCursorPage[ChatKitThread], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + "user": user, + }, + thread_list_params.ThreadListParams, + ), + security={"bearer_auth": True}, + ), + model=ChatKitThread, + ) + + def delete( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ThreadDeleteResponse: + """ + Delete a ChatKit thread along with its items and stored attachments. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._delete( + path_template("/chatkit/threads/{thread_id}", thread_id=thread_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ThreadDeleteResponse, + ) + + def list_items( + self, + thread_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[Data]: + """ + List items that belong to a ChatKit thread. + + Args: + after: List items created after this thread item ID. Defaults to null for the first + page. + + before: List items created before this thread item ID. Defaults to null for the newest + results. + + limit: Maximum number of thread items to return. Defaults to 20. + + order: Sort order for results by creation time. Defaults to `desc`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/chatkit/threads/{thread_id}/items", thread_id=thread_id), + page=SyncConversationCursorPage[Data], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + thread_list_items_params.ThreadListItemsParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, Data), # Union types cannot be passed in as arguments in the type system + ) + + +class AsyncThreads(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncThreadsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncThreadsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncThreadsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncThreadsWithStreamingResponse(self) + + async def retrieve( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatKitThread: + """ + Retrieve a ChatKit thread by its identifier. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return await self._get( + path_template("/chatkit/threads/{thread_id}", thread_id=thread_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatKitThread, + ) + + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ChatKitThread, AsyncConversationCursorPage[ChatKitThread]]: + """ + List ChatKit threads with optional pagination and user filters. + + Args: + after: List items created after this thread item ID. Defaults to null for the first + page. + + before: List items created before this thread item ID. Defaults to null for the newest + results. + + limit: Maximum number of thread items to return. Defaults to 20. + + order: Sort order for results by creation time. Defaults to `desc`. + + user: Filter threads that belong to this user identifier. Defaults to null to return + all users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._get_api_list( + "/chatkit/threads", + page=AsyncConversationCursorPage[ChatKitThread], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + "user": user, + }, + thread_list_params.ThreadListParams, + ), + security={"bearer_auth": True}, + ), + model=ChatKitThread, + ) + + async def delete( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ThreadDeleteResponse: + """ + Delete a ChatKit thread along with its items and stored attachments. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return await self._delete( + path_template("/chatkit/threads/{thread_id}", thread_id=thread_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ThreadDeleteResponse, + ) + + def list_items( + self, + thread_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Data, AsyncConversationCursorPage[Data]]: + """ + List items that belong to a ChatKit thread. + + Args: + after: List items created after this thread item ID. Defaults to null for the first + page. + + before: List items created before this thread item ID. Defaults to null for the newest + results. + + limit: Maximum number of thread items to return. Defaults to 20. + + order: Sort order for results by creation time. Defaults to `desc`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._get_api_list( + path_template("/chatkit/threads/{thread_id}/items", thread_id=thread_id), + page=AsyncConversationCursorPage[Data], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + thread_list_items_params.ThreadListItemsParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, Data), # Union types cannot be passed in as arguments in the type system + ) + + +class ThreadsWithRawResponse: + def __init__(self, threads: Threads) -> None: + self._threads = threads + + self.retrieve = _legacy_response.to_raw_response_wrapper( + threads.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + threads.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + threads.delete, + ) + self.list_items = _legacy_response.to_raw_response_wrapper( + threads.list_items, + ) + + +class AsyncThreadsWithRawResponse: + def __init__(self, threads: AsyncThreads) -> None: + self._threads = threads + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + threads.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + threads.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + threads.delete, + ) + self.list_items = _legacy_response.async_to_raw_response_wrapper( + threads.list_items, + ) + + +class ThreadsWithStreamingResponse: + def __init__(self, threads: Threads) -> None: + self._threads = threads + + self.retrieve = to_streamed_response_wrapper( + threads.retrieve, + ) + self.list = to_streamed_response_wrapper( + threads.list, + ) + self.delete = to_streamed_response_wrapper( + threads.delete, + ) + self.list_items = to_streamed_response_wrapper( + threads.list_items, + ) + + +class AsyncThreadsWithStreamingResponse: + def __init__(self, threads: AsyncThreads) -> None: + self._threads = threads + + self.retrieve = async_to_streamed_response_wrapper( + threads.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + threads.list, + ) + self.delete = async_to_streamed_response_wrapper( + threads.delete, + ) + self.list_items = async_to_streamed_response_wrapper( + threads.list_items, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__init__.py new file mode 100644 index 0000000..40fa611 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__init__.py @@ -0,0 +1,45 @@ +from .realtime import ( + Realtime, + AsyncRealtime, + RealtimeWithRawResponse, + AsyncRealtimeWithRawResponse, + RealtimeWithStreamingResponse, + AsyncRealtimeWithStreamingResponse, +) +from .sessions import ( + Sessions, + AsyncSessions, + SessionsWithRawResponse, + AsyncSessionsWithRawResponse, + SessionsWithStreamingResponse, + AsyncSessionsWithStreamingResponse, +) +from .transcription_sessions import ( + TranscriptionSessions, + AsyncTranscriptionSessions, + TranscriptionSessionsWithRawResponse, + AsyncTranscriptionSessionsWithRawResponse, + TranscriptionSessionsWithStreamingResponse, + AsyncTranscriptionSessionsWithStreamingResponse, +) + +__all__ = [ + "Sessions", + "AsyncSessions", + "SessionsWithRawResponse", + "AsyncSessionsWithRawResponse", + "SessionsWithStreamingResponse", + "AsyncSessionsWithStreamingResponse", + "TranscriptionSessions", + "AsyncTranscriptionSessions", + "TranscriptionSessionsWithRawResponse", + "AsyncTranscriptionSessionsWithRawResponse", + "TranscriptionSessionsWithStreamingResponse", + "AsyncTranscriptionSessionsWithStreamingResponse", + "Realtime", + "AsyncRealtime", + "RealtimeWithRawResponse", + "AsyncRealtimeWithRawResponse", + "RealtimeWithStreamingResponse", + "AsyncRealtimeWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1cfae3d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__pycache__/realtime.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__pycache__/realtime.cpython-312.pyc new file mode 100644 index 0000000..6382fcc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__pycache__/realtime.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__pycache__/sessions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__pycache__/sessions.cpython-312.pyc new file mode 100644 index 0000000..febda55 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__pycache__/sessions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__pycache__/transcription_sessions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__pycache__/transcription_sessions.cpython-312.pyc new file mode 100644 index 0000000..136095e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/__pycache__/transcription_sessions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/realtime.py b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/realtime.py new file mode 100644 index 0000000..7124287 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/realtime.py @@ -0,0 +1,1095 @@ +from __future__ import annotations + +import json +import logging +from types import TracebackType +from typing import TYPE_CHECKING, Any, Iterator, cast +from typing_extensions import AsyncIterator + +import httpx2 +from pydantic import BaseModel + +from .sessions import ( + Sessions, + AsyncSessions, + SessionsWithRawResponse, + AsyncSessionsWithRawResponse, + SessionsWithStreamingResponse, + AsyncSessionsWithStreamingResponse, +) +from ...._types import NOT_GIVEN, Query, Headers, NotGiven +from ...._utils import ( + is_azure_client, + maybe_transform, + strip_not_given, + async_maybe_transform, + is_async_azure_client, +) +from ...._compat import cached_property +from ...._httpx2 import normalize_httpx_url +from ...._models import construct_type_unchecked +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._exceptions import OpenAIError +from ...._base_client import _merge_mappings +from ....types.beta.realtime import ( + session_update_event_param, + response_create_event_param, + transcription_session_update_param, +) +from .transcription_sessions import ( + TranscriptionSessions, + AsyncTranscriptionSessions, + TranscriptionSessionsWithRawResponse, + AsyncTranscriptionSessionsWithRawResponse, + TranscriptionSessionsWithStreamingResponse, + AsyncTranscriptionSessionsWithStreamingResponse, +) +from ....types.websocket_connection_options import WebsocketConnectionOptions +from ....types.beta.realtime.realtime_client_event import RealtimeClientEvent +from ....types.beta.realtime.realtime_server_event import RealtimeServerEvent +from ....types.beta.realtime.conversation_item_param import ConversationItemParam +from ....types.beta.realtime.realtime_client_event_param import RealtimeClientEventParam + +if TYPE_CHECKING: + from websockets.sync.client import ClientConnection as WebsocketConnection + from websockets.asyncio.client import ClientConnection as AsyncWebsocketConnection + + from ...._client import OpenAI, AsyncOpenAI + +__all__ = ["Realtime", "AsyncRealtime"] + +log: logging.Logger = logging.getLogger(__name__) + + +class Realtime(SyncAPIResource): + @cached_property + def sessions(self) -> Sessions: + return Sessions(self._client) + + @cached_property + def transcription_sessions(self) -> TranscriptionSessions: + return TranscriptionSessions(self._client) + + @cached_property + def with_raw_response(self) -> RealtimeWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RealtimeWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RealtimeWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RealtimeWithStreamingResponse(self) + + def connect( + self, + *, + model: str, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebsocketConnectionOptions = {}, + ) -> RealtimeConnectionManager: + """ + The Realtime API enables you to build low-latency, multi-modal conversational experiences. It currently supports text and audio as both input and output, as well as function calling. + + Some notable benefits of the API include: + + - Native speech-to-speech: Skipping an intermediate text format means low latency and nuanced output. + - Natural, steerable voices: The models have natural inflection and can laugh, whisper, and adhere to tone direction. + - Simultaneous multimodal output: Text is useful for moderation; faster-than-realtime audio ensures stable playback. + + The Realtime API is a stateful, event-based API that communicates over a WebSocket. + """ + return RealtimeConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + model=model, + ) + + +class AsyncRealtime(AsyncAPIResource): + @cached_property + def sessions(self) -> AsyncSessions: + return AsyncSessions(self._client) + + @cached_property + def transcription_sessions(self) -> AsyncTranscriptionSessions: + return AsyncTranscriptionSessions(self._client) + + @cached_property + def with_raw_response(self) -> AsyncRealtimeWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRealtimeWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRealtimeWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRealtimeWithStreamingResponse(self) + + def connect( + self, + *, + model: str, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebsocketConnectionOptions = {}, + ) -> AsyncRealtimeConnectionManager: + """ + The Realtime API enables you to build low-latency, multi-modal conversational experiences. It currently supports text and audio as both input and output, as well as function calling. + + Some notable benefits of the API include: + + - Native speech-to-speech: Skipping an intermediate text format means low latency and nuanced output. + - Natural, steerable voices: The models have natural inflection and can laugh, whisper, and adhere to tone direction. + - Simultaneous multimodal output: Text is useful for moderation; faster-than-realtime audio ensures stable playback. + + The Realtime API is a stateful, event-based API that communicates over a WebSocket. + """ + return AsyncRealtimeConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + model=model, + ) + + +class RealtimeWithRawResponse: + def __init__(self, realtime: Realtime) -> None: + self._realtime = realtime + + @cached_property + def sessions(self) -> SessionsWithRawResponse: + return SessionsWithRawResponse(self._realtime.sessions) + + @cached_property + def transcription_sessions(self) -> TranscriptionSessionsWithRawResponse: + return TranscriptionSessionsWithRawResponse(self._realtime.transcription_sessions) + + +class AsyncRealtimeWithRawResponse: + def __init__(self, realtime: AsyncRealtime) -> None: + self._realtime = realtime + + @cached_property + def sessions(self) -> AsyncSessionsWithRawResponse: + return AsyncSessionsWithRawResponse(self._realtime.sessions) + + @cached_property + def transcription_sessions(self) -> AsyncTranscriptionSessionsWithRawResponse: + return AsyncTranscriptionSessionsWithRawResponse(self._realtime.transcription_sessions) + + +class RealtimeWithStreamingResponse: + def __init__(self, realtime: Realtime) -> None: + self._realtime = realtime + + @cached_property + def sessions(self) -> SessionsWithStreamingResponse: + return SessionsWithStreamingResponse(self._realtime.sessions) + + @cached_property + def transcription_sessions(self) -> TranscriptionSessionsWithStreamingResponse: + return TranscriptionSessionsWithStreamingResponse(self._realtime.transcription_sessions) + + +class AsyncRealtimeWithStreamingResponse: + def __init__(self, realtime: AsyncRealtime) -> None: + self._realtime = realtime + + @cached_property + def sessions(self) -> AsyncSessionsWithStreamingResponse: + return AsyncSessionsWithStreamingResponse(self._realtime.sessions) + + @cached_property + def transcription_sessions(self) -> AsyncTranscriptionSessionsWithStreamingResponse: + return AsyncTranscriptionSessionsWithStreamingResponse(self._realtime.transcription_sessions) + + +class AsyncRealtimeConnection: + """Represents a live websocket connection to the Realtime API""" + + session: AsyncRealtimeSessionResource + response: AsyncRealtimeResponseResource + input_audio_buffer: AsyncRealtimeInputAudioBufferResource + conversation: AsyncRealtimeConversationResource + output_audio_buffer: AsyncRealtimeOutputAudioBufferResource + transcription_session: AsyncRealtimeTranscriptionSessionResource + + _connection: AsyncWebsocketConnection + + def __init__(self, connection: AsyncWebsocketConnection) -> None: + self._connection = connection + + self.session = AsyncRealtimeSessionResource(self) + self.response = AsyncRealtimeResponseResource(self) + self.input_audio_buffer = AsyncRealtimeInputAudioBufferResource(self) + self.conversation = AsyncRealtimeConversationResource(self) + self.output_audio_buffer = AsyncRealtimeOutputAudioBufferResource(self) + self.transcription_session = AsyncRealtimeTranscriptionSessionResource(self) + + async def __aiter__(self) -> AsyncIterator[RealtimeServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK + + try: + while True: + yield await self.recv() + except ConnectionClosedOK: + return + + async def recv(self) -> RealtimeServerEvent: + """ + Receive the next message from the connection and parses it into a `RealtimeServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + return self.parse_event(await self.recv_bytes()) + + async def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `RealtimeServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = await self._connection.recv(decode=False) + log.debug("Received WebSocket message: %i bytes", len(message)) + return message + + async def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(await async_maybe_transform(event, RealtimeClientEventParam)) + ) + await self._connection.send(data) + + async def close(self, *, code: int = 1000, reason: str = "") -> None: + await self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> RealtimeServerEvent: + """ + Converts a raw `str` or `bytes` message into a `RealtimeServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + RealtimeServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, RealtimeServerEvent)) + ) + + +class AsyncRealtimeConnectionManager: + """ + Context manager over a `AsyncRealtimeConnection` that is returned by `beta.realtime.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.beta.realtime.connect(...).enter() + # ... + await connection.close() + ``` + """ + + def __init__( + self, + *, + client: AsyncOpenAI, + model: str, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebsocketConnectionOptions, + ) -> None: + self.__client = client + self.__model = model + self.__connection: AsyncRealtimeConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + + async def __aenter__(self) -> AsyncRealtimeConnection: + """ + 👋 If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.beta.realtime.connect(...).enter() + # ... + await connection.close() + ``` + """ + try: + from ....lib._websocket import _WebSocketConnect as connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + extra_query = self.__extra_query + await self.__client._refresh_api_key() + auth_headers = self.__client.auth_headers + if is_async_azure_client(self.__client): + from ....lib._azure_websocket import _AzureWebSocketConnect as connect + + url, auth_headers = await self.__client._configure_realtime(self.__model, extra_query) + else: + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + "model": self.__model, + **extra_query, + }, + ) + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + self.__connection = AsyncRealtimeConnection( + await connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **auth_headers, + "OpenAI-Beta": "realtime=v1", + }, + self.__extra_headers, + ), + **self.__websocket_connection_options, + ) + ) + + return self.__connection + + enter = __aenter__ + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = normalize_httpx_url(self.__client.websocket_base_url) + else: + base_url = self.__client._base_url.copy_with(scheme="wss") + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" + return base_url.copy_with(raw_path=merge_raw_path) + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + await self.__connection.close() + + +class RealtimeConnection: + """Represents a live websocket connection to the Realtime API""" + + session: RealtimeSessionResource + response: RealtimeResponseResource + input_audio_buffer: RealtimeInputAudioBufferResource + conversation: RealtimeConversationResource + output_audio_buffer: RealtimeOutputAudioBufferResource + transcription_session: RealtimeTranscriptionSessionResource + + _connection: WebsocketConnection + + def __init__(self, connection: WebsocketConnection) -> None: + self._connection = connection + + self.session = RealtimeSessionResource(self) + self.response = RealtimeResponseResource(self) + self.input_audio_buffer = RealtimeInputAudioBufferResource(self) + self.conversation = RealtimeConversationResource(self) + self.output_audio_buffer = RealtimeOutputAudioBufferResource(self) + self.transcription_session = RealtimeTranscriptionSessionResource(self) + + def __iter__(self) -> Iterator[RealtimeServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK + + try: + while True: + yield self.recv() + except ConnectionClosedOK: + return + + def recv(self) -> RealtimeServerEvent: + """ + Receive the next message from the connection and parses it into a `RealtimeServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + return self.parse_event(self.recv_bytes()) + + def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `RealtimeServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = self._connection.recv(decode=False) + log.debug("Received WebSocket message: %i bytes", len(message)) + return message + + def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, RealtimeClientEventParam)) + ) + self._connection.send(data) + + def close(self, *, code: int = 1000, reason: str = "") -> None: + self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> RealtimeServerEvent: + """ + Converts a raw `str` or `bytes` message into a `RealtimeServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + RealtimeServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, RealtimeServerEvent)) + ) + + +class RealtimeConnectionManager: + """ + Context manager over a `RealtimeConnection` that is returned by `beta.realtime.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.beta.realtime.connect(...).enter() + # ... + connection.close() + ``` + """ + + def __init__( + self, + *, + client: OpenAI, + model: str, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebsocketConnectionOptions, + ) -> None: + self.__client = client + self.__model = model + self.__connection: RealtimeConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + + def __enter__(self) -> RealtimeConnection: + """ + 👋 If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.beta.realtime.connect(...).enter() + # ... + connection.close() + ``` + """ + try: + from websockets.sync.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + extra_query = self.__extra_query + self.__client._refresh_api_key() + auth_headers = self.__client.auth_headers + if is_azure_client(self.__client): + url, auth_headers = self.__client._configure_realtime(self.__model, extra_query) + else: + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + "model": self.__model, + **extra_query, + }, + ) + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + self.__connection = RealtimeConnection( + connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **auth_headers, + "OpenAI-Beta": "realtime=v1", + }, + self.__extra_headers, + ), + **self.__websocket_connection_options, + ) + ) + + return self.__connection + + enter = __enter__ + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = normalize_httpx_url(self.__client.websocket_base_url) + else: + base_url = self.__client._base_url.copy_with(scheme="wss") + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" + return base_url.copy_with(raw_path=merge_raw_path) + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + self.__connection.close() + + +class BaseRealtimeConnectionResource: + def __init__(self, connection: RealtimeConnection) -> None: + self._connection = connection + + +class RealtimeSessionResource(BaseRealtimeConnectionResource): + def update(self, *, session: session_update_event_param.Session, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event to update the session’s default configuration. + The client may send this event at any time to update any field, + except for `voice`. However, note that once a session has been + initialized with a particular `model`, it can’t be changed to + another model using `session.update`. + + When the server receives a `session.update`, it will respond + with a `session.updated` event showing the full, effective configuration. + Only the fields that are present are updated. To clear a field like + `instructions`, pass an empty string. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "session.update", "session": session, "event_id": event_id}), + ) + ) + + +class RealtimeResponseResource(BaseRealtimeConnectionResource): + def create( + self, + *, + event_id: str | NotGiven = NOT_GIVEN, + response: response_create_event_param.Response | NotGiven = NOT_GIVEN, + ) -> None: + """ + This event instructs the server to create a Response, which means triggering + model inference. When in Server VAD mode, the server will create Responses + automatically. + + A Response will include at least one Item, and may have two, in which case + the second will be a function call. These Items will be appended to the + conversation history. + + The server will respond with a `response.created` event, events for Items + and content created, and finally a `response.done` event to indicate the + Response is complete. + + The `response.create` event includes inference configuration like + `instructions`, and `temperature`. These fields will override the Session's + configuration for this Response only. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.create", "event_id": event_id, "response": response}), + ) + ) + + def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.done` event with a status of `response.status=cancelled`. If + there is no response to cancel, the server will respond with an error. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.cancel", "event_id": event_id, "response_id": response_id}), + ) + ) + + +class RealtimeInputAudioBufferResource(BaseRealtimeConnectionResource): + def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) + ) + + def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event to commit the user input audio buffer, which will create a + new user message item in the conversation. This event will produce an error + if the input audio buffer is empty. When in Server VAD mode, the client does + not need to send this event, the server will commit the audio buffer + automatically. + + Committing the input audio buffer will trigger input audio transcription + (if enabled in session configuration), but it will not create a response + from the model. The server will respond with an `input_audio_buffer.committed` + event. + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) + ) + + def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. In Server VAD + mode, the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike made other client events, the server will + not send a confirmation response to this event. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "input_audio_buffer.append", "audio": audio, "event_id": event_id}), + ) + ) + + +class RealtimeConversationResource(BaseRealtimeConnectionResource): + @cached_property + def item(self) -> RealtimeConversationItemResource: + return RealtimeConversationItemResource(self._connection) + + +class RealtimeConversationItemResource(BaseRealtimeConnectionResource): + def delete(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event when you want to remove any item from the conversation + history. + + The server will respond with a `conversation.item.deleted` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.delete", "item_id": item_id, "event_id": event_id}), + ) + ) + + def create( + self, + *, + item: ConversationItemParam, + event_id: str | NotGiven = NOT_GIVEN, + previous_item_id: str | NotGiven = NOT_GIVEN, + ) -> None: + """ + Add a new Item to the Conversation's context, including messages, function + calls, and function call responses. This event can be used both to populate a + "history" of the conversation and to add new items mid-stream, but has the + current limitation that it cannot populate assistant audio messages. + + If successful, the server will respond with a `conversation.item.created` + event, otherwise an `error` event will be sent. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.create", + "item": item, + "event_id": event_id, + "previous_item_id": previous_item_id, + } + ), + ) + ) + + def truncate( + self, *, audio_end_ms: int, content_index: int, item_id: str, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """Send this event to truncate a previous assistant message’s audio. + + The server + will produce audio faster than realtime, so this event is useful when the user + interrupts to truncate audio that has already been sent to the client but not + yet played. This will synchronize the server's understanding of the audio with + the client's playback. + + Truncating audio will delete the server-side text transcript to ensure there + is not text in the context that hasn't been heard by the user. + + If successful, the server will respond with a `conversation.item.truncated` + event. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.truncate", + "audio_end_ms": audio_end_ms, + "content_index": content_index, + "item_id": item_id, + "event_id": event_id, + } + ), + ) + ) + + def retrieve(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. + The server will respond with a `conversation.item.retrieved` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.retrieve", "item_id": item_id, "event_id": event_id}), + ) + ) + + +class RealtimeOutputAudioBufferResource(BaseRealtimeConnectionResource): + def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """**WebRTC Only:** Emit to cut off the current audio response. + + This will trigger the server to + stop generating audio and emit a `output_audio_buffer.cleared` event. This + event should be preceded by a `response.cancel` client event to stop the + generation of the current response. + [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "output_audio_buffer.clear", "event_id": event_id})) + ) + + +class RealtimeTranscriptionSessionResource(BaseRealtimeConnectionResource): + def update( + self, *, session: transcription_session_update_param.Session, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """Send this event to update a transcription session.""" + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "transcription_session.update", "session": session, "event_id": event_id}), + ) + ) + + +class BaseAsyncRealtimeConnectionResource: + def __init__(self, connection: AsyncRealtimeConnection) -> None: + self._connection = connection + + +class AsyncRealtimeSessionResource(BaseAsyncRealtimeConnectionResource): + async def update( + self, *, session: session_update_event_param.Session, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """ + Send this event to update the session’s default configuration. + The client may send this event at any time to update any field, + except for `voice`. However, note that once a session has been + initialized with a particular `model`, it can’t be changed to + another model using `session.update`. + + When the server receives a `session.update`, it will respond + with a `session.updated` event showing the full, effective configuration. + Only the fields that are present are updated. To clear a field like + `instructions`, pass an empty string. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "session.update", "session": session, "event_id": event_id}), + ) + ) + + +class AsyncRealtimeResponseResource(BaseAsyncRealtimeConnectionResource): + async def create( + self, + *, + event_id: str | NotGiven = NOT_GIVEN, + response: response_create_event_param.Response | NotGiven = NOT_GIVEN, + ) -> None: + """ + This event instructs the server to create a Response, which means triggering + model inference. When in Server VAD mode, the server will create Responses + automatically. + + A Response will include at least one Item, and may have two, in which case + the second will be a function call. These Items will be appended to the + conversation history. + + The server will respond with a `response.created` event, events for Items + and content created, and finally a `response.done` event to indicate the + Response is complete. + + The `response.create` event includes inference configuration like + `instructions`, and `temperature`. These fields will override the Session's + configuration for this Response only. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.create", "event_id": event_id, "response": response}), + ) + ) + + async def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.done` event with a status of `response.status=cancelled`. If + there is no response to cancel, the server will respond with an error. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.cancel", "event_id": event_id, "response_id": response_id}), + ) + ) + + +class AsyncRealtimeInputAudioBufferResource(BaseAsyncRealtimeConnectionResource): + async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) + ) + + async def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event to commit the user input audio buffer, which will create a + new user message item in the conversation. This event will produce an error + if the input audio buffer is empty. When in Server VAD mode, the client does + not need to send this event, the server will commit the audio buffer + automatically. + + Committing the input audio buffer will trigger input audio transcription + (if enabled in session configuration), but it will not create a response + from the model. The server will respond with an `input_audio_buffer.committed` + event. + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) + ) + + async def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. In Server VAD + mode, the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike made other client events, the server will + not send a confirmation response to this event. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "input_audio_buffer.append", "audio": audio, "event_id": event_id}), + ) + ) + + +class AsyncRealtimeConversationResource(BaseAsyncRealtimeConnectionResource): + @cached_property + def item(self) -> AsyncRealtimeConversationItemResource: + return AsyncRealtimeConversationItemResource(self._connection) + + +class AsyncRealtimeConversationItemResource(BaseAsyncRealtimeConnectionResource): + async def delete(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event when you want to remove any item from the conversation + history. + + The server will respond with a `conversation.item.deleted` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.delete", "item_id": item_id, "event_id": event_id}), + ) + ) + + async def create( + self, + *, + item: ConversationItemParam, + event_id: str | NotGiven = NOT_GIVEN, + previous_item_id: str | NotGiven = NOT_GIVEN, + ) -> None: + """ + Add a new Item to the Conversation's context, including messages, function + calls, and function call responses. This event can be used both to populate a + "history" of the conversation and to add new items mid-stream, but has the + current limitation that it cannot populate assistant audio messages. + + If successful, the server will respond with a `conversation.item.created` + event, otherwise an `error` event will be sent. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.create", + "item": item, + "event_id": event_id, + "previous_item_id": previous_item_id, + } + ), + ) + ) + + async def truncate( + self, *, audio_end_ms: int, content_index: int, item_id: str, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """Send this event to truncate a previous assistant message’s audio. + + The server + will produce audio faster than realtime, so this event is useful when the user + interrupts to truncate audio that has already been sent to the client but not + yet played. This will synchronize the server's understanding of the audio with + the client's playback. + + Truncating audio will delete the server-side text transcript to ensure there + is not text in the context that hasn't been heard by the user. + + If successful, the server will respond with a `conversation.item.truncated` + event. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.truncate", + "audio_end_ms": audio_end_ms, + "content_index": content_index, + "item_id": item_id, + "event_id": event_id, + } + ), + ) + ) + + async def retrieve(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. + The server will respond with a `conversation.item.retrieved` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.retrieve", "item_id": item_id, "event_id": event_id}), + ) + ) + + +class AsyncRealtimeOutputAudioBufferResource(BaseAsyncRealtimeConnectionResource): + async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """**WebRTC Only:** Emit to cut off the current audio response. + + This will trigger the server to + stop generating audio and emit a `output_audio_buffer.cleared` event. This + event should be preceded by a `response.cancel` client event to stop the + generation of the current response. + [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "output_audio_buffer.clear", "event_id": event_id})) + ) + + +class AsyncRealtimeTranscriptionSessionResource(BaseAsyncRealtimeConnectionResource): + async def update( + self, *, session: transcription_session_update_param.Session, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """Send this event to update a transcription session.""" + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "transcription_session.update", "session": session, "event_id": event_id}), + ) + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/sessions.py b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/sessions.py new file mode 100644 index 0000000..c131a44 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/sessions.py @@ -0,0 +1,422 @@ +from __future__ import annotations + +from typing import List, Union, Iterable +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.beta.realtime import session_create_params +from ....types.beta.realtime.session_create_response import SessionCreateResponse + +__all__ = ["Sessions", "AsyncSessions"] + + +class Sessions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SessionsWithStreamingResponse(self) + + def create( + self, + *, + client_secret: session_create_params.ClientSecret | NotGiven = NOT_GIVEN, + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + input_audio_noise_reduction: session_create_params.InputAudioNoiseReduction | NotGiven = NOT_GIVEN, + input_audio_transcription: session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, + instructions: str | NotGiven = NOT_GIVEN, + max_response_output_tokens: Union[int, Literal["inf"]] | NotGiven = NOT_GIVEN, + modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, + model: Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ] + | NotGiven = NOT_GIVEN, + output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + speed: float | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + tool_choice: str | NotGiven = NOT_GIVEN, + tools: Iterable[session_create_params.Tool] | NotGiven = NOT_GIVEN, + tracing: session_create_params.Tracing | NotGiven = NOT_GIVEN, + turn_detection: session_create_params.TurnDetection | NotGiven = NOT_GIVEN, + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] + | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SessionCreateResponse: + """ + Create an ephemeral API token for use in client-side applications with the + Realtime API. Can be configured with the same session parameters as the + `session.update` client event. + + It responds with a session object, plus a `client_secret` key which contains a + usable ephemeral API token that can be used to authenticate browser clients for + the Realtime API. + + Args: + client_secret: Configuration options for the generated client secret. + + input_audio_format: The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For + `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate, single channel + (mono), and little-endian byte order. + + input_audio_noise_reduction: Configuration for input audio noise reduction. This can be set to `null` to turn + off. Noise reduction filters audio added to the input audio buffer before it is + sent to VAD and the model. Filtering the audio can improve VAD and turn + detection accuracy (reducing false positives) and model performance by improving + perception of the input audio. + + input_audio_transcription: Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + + instructions: The default system instructions (i.e. system message) prepended to model calls. + This field allows the client to guide the model on desired responses. The model + can be instructed on response content and format, (e.g. "be extremely succinct", + "act friendly", "here are examples of good responses") and on audio behavior + (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The + instructions are not guaranteed to be followed by the model, but they provide + guidance to the model on the desired behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + + max_response_output_tokens: Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + + modalities: The set of modalities the model can respond with. To disable audio, set this to + ["text"]. + + model: The Realtime model used for this session. + + output_audio_format: The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. + For `pcm16`, output audio is sampled at a rate of 24kHz. + + speed: The speed of the model's spoken response. 1.0 is the default speed. 0.25 is the + minimum speed. 1.5 is the maximum speed. This value can only be changed in + between model turns, not while a response is in progress. + + temperature: Sampling temperature for the model, limited to [0.6, 1.2]. For audio models a + temperature of 0.8 is highly recommended for best performance. + + tool_choice: How the model chooses tools. Options are `auto`, `none`, `required`, or specify + a function. + + tools: Tools (functions) available to the model. + + tracing: Configuration options for tracing. Set to null to disable tracing. Once tracing + is enabled for a session, the configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + + turn_detection: Configuration for turn detection, ether Server VAD or Semantic VAD. This can be + set to `null` to turn off, in which case the client must manually trigger model + response. Server VAD means that the model will detect the start and end of + speech based on audio volume and respond at the end of user speech. Semantic VAD + is more advanced and uses a turn detection model (in conjunction with VAD) to + semantically estimate whether the user has finished speaking, then dynamically + sets a timeout based on this probability. For example, if user audio trails off + with "uhhm", the model will score a low probability of turn end and wait longer + for the user to continue speaking. This can be useful for more natural + conversations, but may have a higher latency. + + voice: The voice the model uses to respond. Voice cannot be changed during the session + once the model has responded with audio at least once. Current voice options are + `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, and `verse`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + "/realtime/sessions", + body=maybe_transform( + { + "client_secret": client_secret, + "input_audio_format": input_audio_format, + "input_audio_noise_reduction": input_audio_noise_reduction, + "input_audio_transcription": input_audio_transcription, + "instructions": instructions, + "max_response_output_tokens": max_response_output_tokens, + "modalities": modalities, + "model": model, + "output_audio_format": output_audio_format, + "speed": speed, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "tracing": tracing, + "turn_detection": turn_detection, + "voice": voice, + }, + session_create_params.SessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SessionCreateResponse, + ) + + +class AsyncSessions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSessionsWithStreamingResponse(self) + + async def create( + self, + *, + client_secret: session_create_params.ClientSecret | NotGiven = NOT_GIVEN, + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + input_audio_noise_reduction: session_create_params.InputAudioNoiseReduction | NotGiven = NOT_GIVEN, + input_audio_transcription: session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, + instructions: str | NotGiven = NOT_GIVEN, + max_response_output_tokens: Union[int, Literal["inf"]] | NotGiven = NOT_GIVEN, + modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, + model: Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ] + | NotGiven = NOT_GIVEN, + output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + speed: float | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + tool_choice: str | NotGiven = NOT_GIVEN, + tools: Iterable[session_create_params.Tool] | NotGiven = NOT_GIVEN, + tracing: session_create_params.Tracing | NotGiven = NOT_GIVEN, + turn_detection: session_create_params.TurnDetection | NotGiven = NOT_GIVEN, + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] + | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SessionCreateResponse: + """ + Create an ephemeral API token for use in client-side applications with the + Realtime API. Can be configured with the same session parameters as the + `session.update` client event. + + It responds with a session object, plus a `client_secret` key which contains a + usable ephemeral API token that can be used to authenticate browser clients for + the Realtime API. + + Args: + client_secret: Configuration options for the generated client secret. + + input_audio_format: The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For + `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate, single channel + (mono), and little-endian byte order. + + input_audio_noise_reduction: Configuration for input audio noise reduction. This can be set to `null` to turn + off. Noise reduction filters audio added to the input audio buffer before it is + sent to VAD and the model. Filtering the audio can improve VAD and turn + detection accuracy (reducing false positives) and model performance by improving + perception of the input audio. + + input_audio_transcription: Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + + instructions: The default system instructions (i.e. system message) prepended to model calls. + This field allows the client to guide the model on desired responses. The model + can be instructed on response content and format, (e.g. "be extremely succinct", + "act friendly", "here are examples of good responses") and on audio behavior + (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The + instructions are not guaranteed to be followed by the model, but they provide + guidance to the model on the desired behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + + max_response_output_tokens: Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + + modalities: The set of modalities the model can respond with. To disable audio, set this to + ["text"]. + + model: The Realtime model used for this session. + + output_audio_format: The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. + For `pcm16`, output audio is sampled at a rate of 24kHz. + + speed: The speed of the model's spoken response. 1.0 is the default speed. 0.25 is the + minimum speed. 1.5 is the maximum speed. This value can only be changed in + between model turns, not while a response is in progress. + + temperature: Sampling temperature for the model, limited to [0.6, 1.2]. For audio models a + temperature of 0.8 is highly recommended for best performance. + + tool_choice: How the model chooses tools. Options are `auto`, `none`, `required`, or specify + a function. + + tools: Tools (functions) available to the model. + + tracing: Configuration options for tracing. Set to null to disable tracing. Once tracing + is enabled for a session, the configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + + turn_detection: Configuration for turn detection, ether Server VAD or Semantic VAD. This can be + set to `null` to turn off, in which case the client must manually trigger model + response. Server VAD means that the model will detect the start and end of + speech based on audio volume and respond at the end of user speech. Semantic VAD + is more advanced and uses a turn detection model (in conjunction with VAD) to + semantically estimate whether the user has finished speaking, then dynamically + sets a timeout based on this probability. For example, if user audio trails off + with "uhhm", the model will score a low probability of turn end and wait longer + for the user to continue speaking. This can be useful for more natural + conversations, but may have a higher latency. + + voice: The voice the model uses to respond. Voice cannot be changed during the session + once the model has responded with audio at least once. Current voice options are + `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, and `verse`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + "/realtime/sessions", + body=await async_maybe_transform( + { + "client_secret": client_secret, + "input_audio_format": input_audio_format, + "input_audio_noise_reduction": input_audio_noise_reduction, + "input_audio_transcription": input_audio_transcription, + "instructions": instructions, + "max_response_output_tokens": max_response_output_tokens, + "modalities": modalities, + "model": model, + "output_audio_format": output_audio_format, + "speed": speed, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "tracing": tracing, + "turn_detection": turn_detection, + "voice": voice, + }, + session_create_params.SessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SessionCreateResponse, + ) + + +class SessionsWithRawResponse: + def __init__(self, sessions: Sessions) -> None: + self._sessions = sessions + + self.create = _legacy_response.to_raw_response_wrapper( + sessions.create, + ) + + +class AsyncSessionsWithRawResponse: + def __init__(self, sessions: AsyncSessions) -> None: + self._sessions = sessions + + self.create = _legacy_response.async_to_raw_response_wrapper( + sessions.create, + ) + + +class SessionsWithStreamingResponse: + def __init__(self, sessions: Sessions) -> None: + self._sessions = sessions + + self.create = to_streamed_response_wrapper( + sessions.create, + ) + + +class AsyncSessionsWithStreamingResponse: + def __init__(self, sessions: AsyncSessions) -> None: + self._sessions = sessions + + self.create = async_to_streamed_response_wrapper( + sessions.create, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/transcription_sessions.py b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/transcription_sessions.py new file mode 100644 index 0000000..04191ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/realtime/transcription_sessions.py @@ -0,0 +1,280 @@ +from __future__ import annotations + +from typing import List +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.beta.realtime import transcription_session_create_params +from ....types.beta.realtime.transcription_session import TranscriptionSession + +__all__ = ["TranscriptionSessions", "AsyncTranscriptionSessions"] + + +class TranscriptionSessions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> TranscriptionSessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return TranscriptionSessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TranscriptionSessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return TranscriptionSessionsWithStreamingResponse(self) + + def create( + self, + *, + client_secret: transcription_session_create_params.ClientSecret | NotGiven = NOT_GIVEN, + include: List[str] | NotGiven = NOT_GIVEN, + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + input_audio_noise_reduction: transcription_session_create_params.InputAudioNoiseReduction + | NotGiven = NOT_GIVEN, + input_audio_transcription: transcription_session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, + modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, + turn_detection: transcription_session_create_params.TurnDetection | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> TranscriptionSession: + """ + Create an ephemeral API token for use in client-side applications with the + Realtime API specifically for realtime transcriptions. Can be configured with + the same session parameters as the `transcription_session.update` client event. + + It responds with a session object, plus a `client_secret` key which contains a + usable ephemeral API token that can be used to authenticate browser clients for + the Realtime API. + + Args: + client_secret: Configuration options for the generated client secret. + + include: + The set of items to include in the transcription. Current available items are: + + - `item.input_audio_transcription.logprobs` + + input_audio_format: The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For + `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate, single channel + (mono), and little-endian byte order. + + input_audio_noise_reduction: Configuration for input audio noise reduction. This can be set to `null` to turn + off. Noise reduction filters audio added to the input audio buffer before it is + sent to VAD and the model. Filtering the audio can improve VAD and turn + detection accuracy (reducing false positives) and model performance by improving + perception of the input audio. + + input_audio_transcription: Configuration for input audio transcription. The client can optionally set the + language and prompt for transcription, these offer additional guidance to the + transcription service. + + modalities: The set of modalities the model can respond with. To disable audio, set this to + ["text"]. + + turn_detection: Configuration for turn detection, ether Server VAD or Semantic VAD. This can be + set to `null` to turn off, in which case the client must manually trigger model + response. Server VAD means that the model will detect the start and end of + speech based on audio volume and respond at the end of user speech. Semantic VAD + is more advanced and uses a turn detection model (in conjunction with VAD) to + semantically estimate whether the user has finished speaking, then dynamically + sets a timeout based on this probability. For example, if user audio trails off + with "uhhm", the model will score a low probability of turn end and wait longer + for the user to continue speaking. This can be useful for more natural + conversations, but may have a higher latency. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + "/realtime/transcription_sessions", + body=maybe_transform( + { + "client_secret": client_secret, + "include": include, + "input_audio_format": input_audio_format, + "input_audio_noise_reduction": input_audio_noise_reduction, + "input_audio_transcription": input_audio_transcription, + "modalities": modalities, + "turn_detection": turn_detection, + }, + transcription_session_create_params.TranscriptionSessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TranscriptionSession, + ) + + +class AsyncTranscriptionSessions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncTranscriptionSessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncTranscriptionSessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTranscriptionSessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncTranscriptionSessionsWithStreamingResponse(self) + + async def create( + self, + *, + client_secret: transcription_session_create_params.ClientSecret | NotGiven = NOT_GIVEN, + include: List[str] | NotGiven = NOT_GIVEN, + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + input_audio_noise_reduction: transcription_session_create_params.InputAudioNoiseReduction + | NotGiven = NOT_GIVEN, + input_audio_transcription: transcription_session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, + modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, + turn_detection: transcription_session_create_params.TurnDetection | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> TranscriptionSession: + """ + Create an ephemeral API token for use in client-side applications with the + Realtime API specifically for realtime transcriptions. Can be configured with + the same session parameters as the `transcription_session.update` client event. + + It responds with a session object, plus a `client_secret` key which contains a + usable ephemeral API token that can be used to authenticate browser clients for + the Realtime API. + + Args: + client_secret: Configuration options for the generated client secret. + + include: + The set of items to include in the transcription. Current available items are: + + - `item.input_audio_transcription.logprobs` + + input_audio_format: The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For + `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate, single channel + (mono), and little-endian byte order. + + input_audio_noise_reduction: Configuration for input audio noise reduction. This can be set to `null` to turn + off. Noise reduction filters audio added to the input audio buffer before it is + sent to VAD and the model. Filtering the audio can improve VAD and turn + detection accuracy (reducing false positives) and model performance by improving + perception of the input audio. + + input_audio_transcription: Configuration for input audio transcription. The client can optionally set the + language and prompt for transcription, these offer additional guidance to the + transcription service. + + modalities: The set of modalities the model can respond with. To disable audio, set this to + ["text"]. + + turn_detection: Configuration for turn detection, ether Server VAD or Semantic VAD. This can be + set to `null` to turn off, in which case the client must manually trigger model + response. Server VAD means that the model will detect the start and end of + speech based on audio volume and respond at the end of user speech. Semantic VAD + is more advanced and uses a turn detection model (in conjunction with VAD) to + semantically estimate whether the user has finished speaking, then dynamically + sets a timeout based on this probability. For example, if user audio trails off + with "uhhm", the model will score a low probability of turn end and wait longer + for the user to continue speaking. This can be useful for more natural + conversations, but may have a higher latency. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + "/realtime/transcription_sessions", + body=await async_maybe_transform( + { + "client_secret": client_secret, + "include": include, + "input_audio_format": input_audio_format, + "input_audio_noise_reduction": input_audio_noise_reduction, + "input_audio_transcription": input_audio_transcription, + "modalities": modalities, + "turn_detection": turn_detection, + }, + transcription_session_create_params.TranscriptionSessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TranscriptionSession, + ) + + +class TranscriptionSessionsWithRawResponse: + def __init__(self, transcription_sessions: TranscriptionSessions) -> None: + self._transcription_sessions = transcription_sessions + + self.create = _legacy_response.to_raw_response_wrapper( + transcription_sessions.create, + ) + + +class AsyncTranscriptionSessionsWithRawResponse: + def __init__(self, transcription_sessions: AsyncTranscriptionSessions) -> None: + self._transcription_sessions = transcription_sessions + + self.create = _legacy_response.async_to_raw_response_wrapper( + transcription_sessions.create, + ) + + +class TranscriptionSessionsWithStreamingResponse: + def __init__(self, transcription_sessions: TranscriptionSessions) -> None: + self._transcription_sessions = transcription_sessions + + self.create = to_streamed_response_wrapper( + transcription_sessions.create, + ) + + +class AsyncTranscriptionSessionsWithStreamingResponse: + def __init__(self, transcription_sessions: AsyncTranscriptionSessions) -> None: + self._transcription_sessions = transcription_sessions + + self.create = async_to_streamed_response_wrapper( + transcription_sessions.create, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__init__.py new file mode 100644 index 0000000..d8eb8df --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__init__.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .responses import ( + Responses as Responses, + AsyncResponses as AsyncResponses, + ResponsesWithRawResponse as ResponsesWithRawResponse, + AsyncResponsesWithRawResponse as AsyncResponsesWithRawResponse, + ResponsesWithStreamingResponse as ResponsesWithStreamingResponse, + AsyncResponsesWithStreamingResponse as AsyncResponsesWithStreamingResponse, + ) + from .input_items import ( + InputItems as InputItems, + AsyncInputItems as AsyncInputItems, + InputItemsWithRawResponse as InputItemsWithRawResponse, + AsyncInputItemsWithRawResponse as AsyncInputItemsWithRawResponse, + InputItemsWithStreamingResponse as InputItemsWithStreamingResponse, + AsyncInputItemsWithStreamingResponse as AsyncInputItemsWithStreamingResponse, + ) + from .input_tokens import ( + InputTokens as InputTokens, + AsyncInputTokens as AsyncInputTokens, + InputTokensWithRawResponse as InputTokensWithRawResponse, + AsyncInputTokensWithRawResponse as AsyncInputTokensWithRawResponse, + InputTokensWithStreamingResponse as InputTokensWithStreamingResponse, + AsyncInputTokensWithStreamingResponse as AsyncInputTokensWithStreamingResponse, + ) + +else: + _EXPORTS = { + "InputItems": (".input_items", "InputItems"), + "AsyncInputItems": (".input_items", "AsyncInputItems"), + "InputItemsWithRawResponse": (".input_items", "InputItemsWithRawResponse"), + "AsyncInputItemsWithRawResponse": (".input_items", "AsyncInputItemsWithRawResponse"), + "InputItemsWithStreamingResponse": (".input_items", "InputItemsWithStreamingResponse"), + "AsyncInputItemsWithStreamingResponse": (".input_items", "AsyncInputItemsWithStreamingResponse"), + "InputTokens": (".input_tokens", "InputTokens"), + "AsyncInputTokens": (".input_tokens", "AsyncInputTokens"), + "InputTokensWithRawResponse": (".input_tokens", "InputTokensWithRawResponse"), + "AsyncInputTokensWithRawResponse": (".input_tokens", "AsyncInputTokensWithRawResponse"), + "InputTokensWithStreamingResponse": (".input_tokens", "InputTokensWithStreamingResponse"), + "AsyncInputTokensWithStreamingResponse": (".input_tokens", "AsyncInputTokensWithStreamingResponse"), + "Responses": (".responses", "Responses"), + "AsyncResponses": (".responses", "AsyncResponses"), + "ResponsesWithRawResponse": (".responses", "ResponsesWithRawResponse"), + "AsyncResponsesWithRawResponse": (".responses", "AsyncResponsesWithRawResponse"), + "ResponsesWithStreamingResponse": (".responses", "ResponsesWithStreamingResponse"), + "AsyncResponsesWithStreamingResponse": (".responses", "AsyncResponsesWithStreamingResponse"), + } + _SUBMODULES = { + "input_items", + "input_tokens", + "responses", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "InputItems", + "AsyncInputItems", + "InputItemsWithRawResponse", + "AsyncInputItemsWithRawResponse", + "InputItemsWithStreamingResponse", + "AsyncInputItemsWithStreamingResponse", + "InputTokens", + "AsyncInputTokens", + "InputTokensWithRawResponse", + "AsyncInputTokensWithRawResponse", + "InputTokensWithStreamingResponse", + "AsyncInputTokensWithStreamingResponse", + "Responses", + "AsyncResponses", + "ResponsesWithRawResponse", + "AsyncResponsesWithRawResponse", + "ResponsesWithStreamingResponse", + "AsyncResponsesWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..052a565 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__pycache__/input_items.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__pycache__/input_items.cpython-312.pyc new file mode 100644 index 0000000..6a241a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__pycache__/input_items.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__pycache__/input_tokens.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__pycache__/input_tokens.cpython-312.pyc new file mode 100644 index 0000000..bccfed1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__pycache__/input_tokens.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__pycache__/responses.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__pycache__/responses.cpython-312.pyc new file mode 100644 index 0000000..369902a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/__pycache__/responses.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/responses/input_items.py b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/input_items.py new file mode 100644 index 0000000..8b07181 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/input_items.py @@ -0,0 +1,242 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, List, cast +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import is_given, path_template, maybe_transform, strip_not_given +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.beta.responses import input_item_list_params +from ....types.beta.beta_response_item import BetaResponseItem +from ....types.beta.beta_response_includable import BetaResponseIncludable + +__all__ = ["InputItems", "AsyncInputItems"] + + +class InputItems(SyncAPIResource): + """Create and manage model responses.""" + + @cached_property + def with_raw_response(self) -> InputItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return InputItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InputItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return InputItemsWithStreamingResponse(self) + + def list( + self, + response_id: str, + *, + after: str | Omit = omit, + include: List[BetaResponseIncludable] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[BetaResponseItem]: + """ + Returns a list of input items for a given response. + + Args: + after: An item ID to list items after, used in pagination. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._get_api_list( + path_template("/responses/{response_id}/input_items?beta=true", response_id=response_id), + page=SyncCursorPage[BetaResponseItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include": include, + "limit": limit, + "order": order, + }, + input_item_list_params.InputItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, BetaResponseItem), # Union types cannot be passed in as arguments in the type system + ) + + +class AsyncInputItems(AsyncAPIResource): + """Create and manage model responses.""" + + @cached_property + def with_raw_response(self) -> AsyncInputItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncInputItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInputItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncInputItemsWithStreamingResponse(self) + + def list( + self, + response_id: str, + *, + after: str | Omit = omit, + include: List[BetaResponseIncludable] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[BetaResponseItem, AsyncCursorPage[BetaResponseItem]]: + """ + Returns a list of input items for a given response. + + Args: + after: An item ID to list items after, used in pagination. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._get_api_list( + path_template("/responses/{response_id}/input_items?beta=true", response_id=response_id), + page=AsyncCursorPage[BetaResponseItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include": include, + "limit": limit, + "order": order, + }, + input_item_list_params.InputItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, BetaResponseItem), # Union types cannot be passed in as arguments in the type system + ) + + +class InputItemsWithRawResponse: + def __init__(self, input_items: InputItems) -> None: + self._input_items = input_items + + self.list = _legacy_response.to_raw_response_wrapper( + input_items.list, + ) + + +class AsyncInputItemsWithRawResponse: + def __init__(self, input_items: AsyncInputItems) -> None: + self._input_items = input_items + + self.list = _legacy_response.async_to_raw_response_wrapper( + input_items.list, + ) + + +class InputItemsWithStreamingResponse: + def __init__(self, input_items: InputItems) -> None: + self._input_items = input_items + + self.list = to_streamed_response_wrapper( + input_items.list, + ) + + +class AsyncInputItemsWithStreamingResponse: + def __init__(self, input_items: AsyncInputItems) -> None: + self._input_items = input_items + + self.list = async_to_streamed_response_wrapper( + input_items.list, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/responses/input_tokens.py b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/input_tokens.py new file mode 100644 index 0000000..74c7b2b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/input_tokens.py @@ -0,0 +1,344 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import is_given, maybe_transform, strip_not_given, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.beta.responses import input_token_count_params +from ....types.beta.beta_tool_param import BetaToolParam +from ....types.beta.beta_response_input_item_param import BetaResponseInputItemParam +from ....types.beta.responses.input_token_count_response import InputTokenCountResponse + +__all__ = ["InputTokens", "AsyncInputTokens"] + + +class InputTokens(SyncAPIResource): + """Create and manage model responses.""" + + @cached_property + def with_raw_response(self) -> InputTokensWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return InputTokensWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InputTokensWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return InputTokensWithStreamingResponse(self) + + def count( + self, + *, + conversation: Optional[input_token_count_params.Conversation] | Omit = omit, + input: Union[str, Iterable[BetaResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + model: Optional[str] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + personality: Union[str, Literal["friendly", "pragmatic"]] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + reasoning: Optional[input_token_count_params.Reasoning] | Omit = omit, + text: Optional[input_token_count_params.Text] | Omit = omit, + tool_choice: Optional[input_token_count_params.ToolChoice] | Omit = omit, + tools: Optional[Iterable[BetaToolParam]] | Omit = omit, + truncation: Literal["auto", "disabled"] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> InputTokenCountResponse: + """ + Returns input token counts of the request. + + Returns an object with `object` set to `response.input_tokens` and an + `input_tokens` count. + + Args: + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + personality: A model-owned style preset to apply to this request. Omit this parameter to use + the model's default style. Supported values may expand over time. Values must be + at most 64 characters. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + reasoning: **gpt-5 and o-series models only** Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: Controls which tool the model should use, if any. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + truncation: The truncation strategy to use for the model response. - `auto`: If the input to + this Response exceeds the model's context window size, the model will truncate + the response to fit the context window by dropping items from the beginning of + the conversation. - `disabled` (default): If the input size will exceed the + context window size for a model, the request will fail with a 400 error. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._post( + "/responses/input_tokens?beta=true", + body=maybe_transform( + { + "conversation": conversation, + "input": input, + "instructions": instructions, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "personality": personality, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "truncation": truncation, + }, + input_token_count_params.InputTokenCountParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=InputTokenCountResponse, + ) + + +class AsyncInputTokens(AsyncAPIResource): + """Create and manage model responses.""" + + @cached_property + def with_raw_response(self) -> AsyncInputTokensWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncInputTokensWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInputTokensWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncInputTokensWithStreamingResponse(self) + + async def count( + self, + *, + conversation: Optional[input_token_count_params.Conversation] | Omit = omit, + input: Union[str, Iterable[BetaResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + model: Optional[str] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + personality: Union[str, Literal["friendly", "pragmatic"]] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + reasoning: Optional[input_token_count_params.Reasoning] | Omit = omit, + text: Optional[input_token_count_params.Text] | Omit = omit, + tool_choice: Optional[input_token_count_params.ToolChoice] | Omit = omit, + tools: Optional[Iterable[BetaToolParam]] | Omit = omit, + truncation: Literal["auto", "disabled"] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> InputTokenCountResponse: + """ + Returns input token counts of the request. + + Returns an object with `object` set to `response.input_tokens` and an + `input_tokens` count. + + Args: + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + personality: A model-owned style preset to apply to this request. Omit this parameter to use + the model's default style. Supported values may expand over time. Values must be + at most 64 characters. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + reasoning: **gpt-5 and o-series models only** Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: Controls which tool the model should use, if any. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + truncation: The truncation strategy to use for the model response. - `auto`: If the input to + this Response exceeds the model's context window size, the model will truncate + the response to fit the context window by dropping items from the beginning of + the conversation. - `disabled` (default): If the input size will exceed the + context window size for a model, the request will fail with a 400 error. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return await self._post( + "/responses/input_tokens?beta=true", + body=await async_maybe_transform( + { + "conversation": conversation, + "input": input, + "instructions": instructions, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "personality": personality, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "truncation": truncation, + }, + input_token_count_params.InputTokenCountParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=InputTokenCountResponse, + ) + + +class InputTokensWithRawResponse: + def __init__(self, input_tokens: InputTokens) -> None: + self._input_tokens = input_tokens + + self.count = _legacy_response.to_raw_response_wrapper( + input_tokens.count, + ) + + +class AsyncInputTokensWithRawResponse: + def __init__(self, input_tokens: AsyncInputTokens) -> None: + self._input_tokens = input_tokens + + self.count = _legacy_response.async_to_raw_response_wrapper( + input_tokens.count, + ) + + +class InputTokensWithStreamingResponse: + def __init__(self, input_tokens: InputTokens) -> None: + self._input_tokens = input_tokens + + self.count = to_streamed_response_wrapper( + input_tokens.count, + ) + + +class AsyncInputTokensWithStreamingResponse: + def __init__(self, input_tokens: AsyncInputTokens) -> None: + self._input_tokens = input_tokens + + self.count = async_to_streamed_response_wrapper( + input_tokens.count, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/responses/responses.py b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/responses.py new file mode 100644 index 0000000..5e8cbf3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/responses/responses.py @@ -0,0 +1,5722 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import json +import time +import random +import logging +from types import TracebackType +from typing import TYPE_CHECKING, Any, Dict, List, Union, Callable, Iterable, Iterator, Optional, Awaitable, cast +from typing_extensions import Literal, AsyncIterator, overload + +import httpx2 +from pydantic import BaseModel + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform +from ...._compat import cached_property +from ...._httpx2 import normalize_httpx_url +from ...._models import FinalRequestOptions, construct_type_unchecked +from .input_items import ( + InputItems, + AsyncInputItems, + InputItemsWithRawResponse, + AsyncInputItemsWithRawResponse, + InputItemsWithStreamingResponse, + AsyncInputItemsWithStreamingResponse, +) +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .input_tokens import ( + InputTokens, + AsyncInputTokens, + InputTokensWithRawResponse, + AsyncInputTokensWithRawResponse, + InputTokensWithStreamingResponse, + AsyncInputTokensWithStreamingResponse, +) +from ...._streaming import Stream, AsyncStream +from ....types.beta import ( + response_create_params, + response_compact_params, + response_retrieve_params, + beta_responses_client_event_param, +) +from ...._exceptions import OpenAIError, WebSocketConnectionClosedError +from ...._send_queue import SendQueue +from ...._base_client import _merge_mappings, make_request_options +from ...._event_handler import EventHandlerRegistry +from ....types.beta.beta_response import BetaResponse +from ....types.beta.beta_tool_param import BetaToolParam +from ....types.beta.beta_service_tier import BetaServiceTier +from ....types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close +from ....types.beta.beta_compacted_response import BetaCompactedResponse +from ....types.websocket_connection_options import WebSocketConnectionOptions +from ....types.beta.beta_response_includable import BetaResponseIncludable +from ....types.beta.beta_response_input_param import BetaResponseInputParam +from ....types.beta.beta_response_prompt_param import BetaResponsePromptParam +from ....types.beta.beta_response_stream_event import BetaResponseStreamEvent +from ....types.beta.beta_responses_client_event import BetaResponsesClientEvent +from ....types.beta.beta_responses_server_event import BetaResponseWsError, BetaResponsesServerEvent +from ....types.beta.beta_response_input_item_param import BetaResponseInputItemParam +from ....types.beta.beta_response_steer_input_param import BetaResponseSteerInputParam +from ....types.beta.beta_response_text_config_param import BetaResponseTextConfigParam +from ....types.beta.beta_responses_client_event_param import BetaResponsesClientEventParam + +if TYPE_CHECKING: + from websockets.sync.client import ClientConnection as WebSocketConnection + from websockets.asyncio.client import ClientConnection as AsyncWebSocketConnection + + from ...._client import OpenAI, AsyncOpenAI + +__all__ = ["Responses", "AsyncResponses"] + +log: logging.Logger = logging.getLogger(__name__) + + +class Responses(SyncAPIResource): + """Create and manage model responses.""" + + @cached_property + def input_items(self) -> InputItems: + """Create and manage model responses.""" + return InputItems(self._client) + + @cached_property + def input_tokens(self) -> InputTokens: + """Create and manage model responses.""" + return InputTokens(self._client) + + @cached_property + def with_raw_response(self) -> ResponsesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ResponsesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ResponsesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ResponsesWithStreamingResponse(self) + + @overload + def create( + self, + *, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[BetaServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse: + """Creates a model response. + + Provide + [text](https://developers.openai.com/api/docs/guides/text) or + [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to + generate [text](https://developers.openai.com/api/docs/guides/text) or + [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) + outputs. Have the model call your own + [custom code](https://developers.openai.com/api/docs/guides/function-calling) or + use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search) + to use your own data as input for the model's response. + + Args: + access_programs: Domain-specific access programs to use for this request. + + background: Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + moderation: Configuration for running moderation on the input and output of this response. + + multi_agent: Configuration for server-hosted multi-agent execution. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + stream: Literal[True], + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[BetaServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[BetaResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://developers.openai.com/api/docs/guides/text) or + [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to + generate [text](https://developers.openai.com/api/docs/guides/text) or + [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) + outputs. Have the model call your own + [custom code](https://developers.openai.com/api/docs/guides/function-calling) or + use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search) + to use your own data as input for the model's response. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + access_programs: Domain-specific access programs to use for this request. + + background: Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + moderation: Configuration for running moderation on the input and output of this response. + + multi_agent: Configuration for server-hosted multi-agent execution. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + stream: bool, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[BetaServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | Stream[BetaResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://developers.openai.com/api/docs/guides/text) or + [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to + generate [text](https://developers.openai.com/api/docs/guides/text) or + [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) + outputs. Have the model call your own + [custom code](https://developers.openai.com/api/docs/guides/function-calling) or + use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search) + to use your own data as input for the model's response. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + access_programs: Domain-specific access programs to use for this request. + + background: Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + moderation: Configuration for running moderation on the input and output of this response. + + multi_agent: Configuration for server-hosted multi-agent execution. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + def create( + self, + *, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[BetaServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | Stream[BetaResponseStreamEvent]: + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._post( + "/responses?beta=true", + body=maybe_transform( + { + "access_programs": access_programs, + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "multi_agent": multi_agent, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParamsStreaming + if stream + else response_create_params.ResponseCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=BetaResponse, + stream=stream or False, + stream_cls=Stream[BetaResponseStreamEvent], + ) + + @overload + def retrieve( + self, + response_id: str, + *, + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse: + """ + Retrieves a model response with the given ID. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def retrieve( + self, + response_id: str, + *, + stream: Literal[True], + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[BetaResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def retrieve( + self, + response_id: str, + *, + stream: bool, + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | Stream[BetaResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + def retrieve( + self, + response_id: str, + *, + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | Stream[BetaResponseStreamEvent]: + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._get( + path_template("/responses/{response_id}?beta=true", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "include": include, + "include_obfuscation": include_obfuscation, + "starting_after": starting_after, + "stream": stream, + }, + response_retrieve_params.ResponseRetrieveParams, + ), + security={"bearer_auth": True}, + ), + cast_to=BetaResponse, + stream=stream or False, + stream_cls=Stream[BetaResponseStreamEvent], + ) + + def delete( + self, + response_id: str, + *, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Deletes a model response with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._delete( + path_template("/responses/{response_id}?beta=true", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + def cancel( + self, + response_id: str, + *, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse: + """Cancels a model response with the given ID. + + Only responses created with the + `background` parameter set to `true` can be cancelled. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._post( + path_template("/responses/{response_id}/cancel?beta=true", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=BetaResponse, + ) + + def compact( + self, + *, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + None, + ], + input: Union[str, Iterable[BetaResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: Optional[response_compact_params.PromptCacheOptions] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "fast", "flex", "priority"]] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaCompactedResponse: + """Compact a conversation. + + Returns a compacted response object. + + Learn when and how to compact long-running conversations in the + [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window). + For ZDR-compatible compaction details, see + [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced). + + Args: + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt_cache_key: A key to use when reading from or writing to the prompt cache. + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: How long to retain a prompt cache entry created by this request. + + service_tier: Specifies the processing type used for serving the request. - If set to 'auto', + then the request will be processed with the service tier configured in the + Project settings. Unless otherwise configured, the Project will use 'default'. - + If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. - To opt-in + to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. For models with a dedicated Fast + tier, either value resolves to `service_tier=fast`; for other models, either + value resolves to `service_tier=priority`. - When not set, the default behavior + is 'auto'. When the `service_tier` parameter is set, the response body will + include the `service_tier` value based on the processing mode actually used to + serve the request. This response value may be different from the value set in + the parameter. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._post( + "/responses/compact?beta=true", + body=maybe_transform( + { + "model": model, + "input": input, + "instructions": instructions, + "previous_response_id": previous_response_id, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "service_tier": service_tier, + }, + response_compact_params.ResponseCompactParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=BetaCompactedResponse, + ) + + def connect( + self, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> ResponsesConnectionManager: + """Connect to a persistent Responses API WebSocket. + + Send `response.create` events to create responses and `response.steer` events to queue user input containing text, images, or files for a continuation. Receive response stream events and steering acceptance, pending, or failure events over the socket. + """ + return ResponsesConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + ) + + +class AsyncResponses(AsyncAPIResource): + """Create and manage model responses.""" + + @cached_property + def input_items(self) -> AsyncInputItems: + """Create and manage model responses.""" + return AsyncInputItems(self._client) + + @cached_property + def input_tokens(self) -> AsyncInputTokens: + """Create and manage model responses.""" + return AsyncInputTokens(self._client) + + @cached_property + def with_raw_response(self) -> AsyncResponsesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncResponsesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncResponsesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncResponsesWithStreamingResponse(self) + + @overload + async def create( + self, + *, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[BetaServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse: + """Creates a model response. + + Provide + [text](https://developers.openai.com/api/docs/guides/text) or + [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to + generate [text](https://developers.openai.com/api/docs/guides/text) or + [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) + outputs. Have the model call your own + [custom code](https://developers.openai.com/api/docs/guides/function-calling) or + use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search) + to use your own data as input for the model's response. + + Args: + access_programs: Domain-specific access programs to use for this request. + + background: Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + moderation: Configuration for running moderation on the input and output of this response. + + multi_agent: Configuration for server-hosted multi-agent execution. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + stream: Literal[True], + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[BetaServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[BetaResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://developers.openai.com/api/docs/guides/text) or + [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to + generate [text](https://developers.openai.com/api/docs/guides/text) or + [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) + outputs. Have the model call your own + [custom code](https://developers.openai.com/api/docs/guides/function-calling) or + use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search) + to use your own data as input for the model's response. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + access_programs: Domain-specific access programs to use for this request. + + background: Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + moderation: Configuration for running moderation on the input and output of this response. + + multi_agent: Configuration for server-hosted multi-agent execution. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + stream: bool, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[BetaServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | AsyncStream[BetaResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://developers.openai.com/api/docs/guides/text) or + [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to + generate [text](https://developers.openai.com/api/docs/guides/text) or + [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) + outputs. Have the model call your own + [custom code](https://developers.openai.com/api/docs/guides/function-calling) or + use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search) + to use your own data as input for the model's response. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + access_programs: Domain-specific access programs to use for this request. + + background: Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + moderation: Configuration for running moderation on the input and output of this response. + + multi_agent: Configuration for server-hosted multi-agent execution. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + async def create( + self, + *, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[BetaServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | AsyncStream[BetaResponseStreamEvent]: + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return await self._post( + "/responses?beta=true", + body=await async_maybe_transform( + { + "access_programs": access_programs, + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "multi_agent": multi_agent, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParamsStreaming + if stream + else response_create_params.ResponseCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=BetaResponse, + stream=stream or False, + stream_cls=AsyncStream[BetaResponseStreamEvent], + ) + + @overload + async def retrieve( + self, + response_id: str, + *, + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse: + """ + Retrieves a model response with the given ID. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def retrieve( + self, + response_id: str, + *, + stream: Literal[True], + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[BetaResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def retrieve( + self, + response_id: str, + *, + stream: bool, + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | AsyncStream[BetaResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + async def retrieve( + self, + response_id: str, + *, + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | AsyncStream[BetaResponseStreamEvent]: + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return await self._get( + path_template("/responses/{response_id}?beta=true", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "include": include, + "include_obfuscation": include_obfuscation, + "starting_after": starting_after, + "stream": stream, + }, + response_retrieve_params.ResponseRetrieveParams, + ), + security={"bearer_auth": True}, + ), + cast_to=BetaResponse, + stream=stream or False, + stream_cls=AsyncStream[BetaResponseStreamEvent], + ) + + async def delete( + self, + response_id: str, + *, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Deletes a model response with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return await self._delete( + path_template("/responses/{response_id}?beta=true", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + async def cancel( + self, + response_id: str, + *, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaResponse: + """Cancels a model response with the given ID. + + Only responses created with the + `background` parameter set to `true` can be cancelled. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return await self._post( + path_template("/responses/{response_id}/cancel?beta=true", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=BetaResponse, + ) + + async def compact( + self, + *, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + None, + ], + input: Union[str, Iterable[BetaResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: Optional[response_compact_params.PromptCacheOptions] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "fast", "flex", "priority"]] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> BetaCompactedResponse: + """Compact a conversation. + + Returns a compacted response object. + + Learn when and how to compact long-running conversations in the + [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window). + For ZDR-compatible compaction details, see + [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced). + + Args: + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt_cache_key: A key to use when reading from or writing to the prompt cache. + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: How long to retain a prompt cache entry created by this request. + + service_tier: Specifies the processing type used for serving the request. - If set to 'auto', + then the request will be processed with the service tier configured in the + Project settings. Unless otherwise configured, the Project will use 'default'. - + If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. - To opt-in + to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. For models with a dedicated Fast + tier, either value resolves to `service_tier=fast`; for other models, either + value resolves to `service_tier=priority`. - When not set, the default behavior + is 'auto'. When the `service_tier` parameter is set, the response body will + include the `service_tier` value based on the processing mode actually used to + serve the request. This response value may be different from the value set in + the parameter. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return await self._post( + "/responses/compact?beta=true", + body=await async_maybe_transform( + { + "model": model, + "input": input, + "instructions": instructions, + "previous_response_id": previous_response_id, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "service_tier": service_tier, + }, + response_compact_params.ResponseCompactParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=BetaCompactedResponse, + ) + + def connect( + self, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> AsyncResponsesConnectionManager: + """Connect to a persistent Responses API WebSocket. + + Send `response.create` events to create responses and `response.steer` events to queue user input containing text, images, or files for a continuation. Receive response stream events and steering acceptance, pending, or failure events over the socket. + """ + return AsyncResponsesConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + ) + + +class ResponsesWithRawResponse: + def __init__(self, responses: Responses) -> None: + self._responses = responses + + self.create = _legacy_response.to_raw_response_wrapper( + responses.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + responses.retrieve, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + responses.delete, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + responses.cancel, + ) + self.compact = _legacy_response.to_raw_response_wrapper( + responses.compact, + ) + + @cached_property + def input_items(self) -> InputItemsWithRawResponse: + """Create and manage model responses.""" + return InputItemsWithRawResponse(self._responses.input_items) + + @cached_property + def input_tokens(self) -> InputTokensWithRawResponse: + """Create and manage model responses.""" + return InputTokensWithRawResponse(self._responses.input_tokens) + + +class AsyncResponsesWithRawResponse: + def __init__(self, responses: AsyncResponses) -> None: + self._responses = responses + + self.create = _legacy_response.async_to_raw_response_wrapper( + responses.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + responses.retrieve, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + responses.delete, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + responses.cancel, + ) + self.compact = _legacy_response.async_to_raw_response_wrapper( + responses.compact, + ) + + @cached_property + def input_items(self) -> AsyncInputItemsWithRawResponse: + """Create and manage model responses.""" + return AsyncInputItemsWithRawResponse(self._responses.input_items) + + @cached_property + def input_tokens(self) -> AsyncInputTokensWithRawResponse: + """Create and manage model responses.""" + return AsyncInputTokensWithRawResponse(self._responses.input_tokens) + + +class ResponsesWithStreamingResponse: + def __init__(self, responses: Responses) -> None: + self._responses = responses + + self.create = to_streamed_response_wrapper( + responses.create, + ) + self.retrieve = to_streamed_response_wrapper( + responses.retrieve, + ) + self.delete = to_streamed_response_wrapper( + responses.delete, + ) + self.cancel = to_streamed_response_wrapper( + responses.cancel, + ) + self.compact = to_streamed_response_wrapper( + responses.compact, + ) + + @cached_property + def input_items(self) -> InputItemsWithStreamingResponse: + """Create and manage model responses.""" + return InputItemsWithStreamingResponse(self._responses.input_items) + + @cached_property + def input_tokens(self) -> InputTokensWithStreamingResponse: + """Create and manage model responses.""" + return InputTokensWithStreamingResponse(self._responses.input_tokens) + + +class AsyncResponsesWithStreamingResponse: + def __init__(self, responses: AsyncResponses) -> None: + self._responses = responses + + self.create = async_to_streamed_response_wrapper( + responses.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + responses.retrieve, + ) + self.delete = async_to_streamed_response_wrapper( + responses.delete, + ) + self.cancel = async_to_streamed_response_wrapper( + responses.cancel, + ) + self.compact = async_to_streamed_response_wrapper( + responses.compact, + ) + + @cached_property + def input_items(self) -> AsyncInputItemsWithStreamingResponse: + """Create and manage model responses.""" + return AsyncInputItemsWithStreamingResponse(self._responses.input_items) + + @cached_property + def input_tokens(self) -> AsyncInputTokensWithStreamingResponse: + """Create and manage model responses.""" + return AsyncInputTokensWithStreamingResponse(self._responses.input_tokens) + + +class AsyncResponsesConnection: + """Represents a live WebSocket connection to the Responses API""" + + response: AsyncResponsesResponseResource + + _connection: AsyncWebSocketConnection + + def __init__( + self, + connection: AsyncWebSocketConnection, + *, + make_ws: Callable[[Query, Headers], Awaitable[AsyncWebSocketConnection]] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._reconnect_attempt = 0 + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue or SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=False) + + self.response = AsyncResponsesResponseResource(self) + + async def __aiter__(self) -> AsyncIterator[BetaResponsesServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield await self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not await self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + async def recv(self) -> BetaResponsesServerEvent: + """ + Receive the next message from the connection and parses it into a `BetaResponsesServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + event = self.parse_event(await self.recv_bytes()) + event_type = ( + cast("dict[str, object]", event).get("type") + if isinstance(cast(object, event), dict) + else getattr(event, "type", None) + ) + # A successful upgrade can still be followed by an admission error. + # Reset the budget only after receiving a non-error application event. + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return event + + async def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `BetaResponsesServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = await self._connection.recv(decode=False) + log.debug("Received WebSocket message: %i bytes", len(message)) + if self._reconnect_attempt: + # Account for raw application progress without changing frame delivery. + try: + event_data: object = json.loads(message) + except (ValueError, RecursionError): + return message + event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return message + + async def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(await async_maybe_transform(event, BetaResponsesClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + try: + await self._connection.send(data) + except Exception: + self._send_queue.enqueue(data) + raise + + async def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + await self._connection.send(data) + + async def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + await self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> BetaResponsesServerEvent: + """ + Converts a raw `str` or `bytes` message into a `BetaResponsesServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + BetaResponsesServerEvent, + construct_type_unchecked(value=json.loads(data), type_=cast(Any, BetaResponsesServerEvent)), + ) + + async def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + import asyncio + + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1): + self._reconnect_attempt = attempt + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + await asyncio.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = await self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + await self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + async def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + + async def _send(data: str) -> None: + await self._connection.send(data) + + try: + await self._send_queue.flush_async(_send) + except Exception: + log.warning("Failed to flush send queue after reconnect") + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("response.steer.accepted", my_handler) + + Or as a decorator:: + + @connection.on("response.steer.accepted") + async def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncResponsesConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks until the connection is closed. This is the push-based + alternative to iterating with ``async for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + import asyncio + + async for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, BetaResponseWsError): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + for handler in generic: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + +class AsyncResponsesConnectionManager: + """ + Context manager over a `AsyncResponsesConnection` that is returned by `beta.responses.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.beta.responses.connect(...).enter() + # ... + await connection.close() + ``` + """ + + def __init__( + self, + *, + client: AsyncOpenAI, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__connection: AsyncResponsesConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=False) + + def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, BetaResponsesClientEventParam)) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``AsyncResponsesConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncResponsesConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def __aenter__(self) -> AsyncResponsesConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.beta.responses.connect(...).enter() + # ... + await connection.close() + ``` + """ + ws = await self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = AsyncResponsesConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + await self.__connection._flush_send_queue() + + return self.__connection + + enter = __aenter__ + + async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection: + try: + from ....lib._websocket import _WebSocketConnect as connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **extra_query, + }, + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + options = await self.__client._prepare_options( + FinalRequestOptions.construct( + method="get", + url=str(url), + headers=dict(extra_headers), + security={"bearer_auth": True}, + ) + ) + url = self.__client._prepare_url(options.url).copy_merge_params( + self.__client.qs.stringify(cast(Any, options.params)) + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + headers = { + key.lower(): (key, value) + for header_set in ( + self.__client.auth_headers, + {}, + self.__client.default_headers, + options.headers if is_given(options.headers) else {}, + ) + for key, value in header_set.items() + } + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + return await connect( + str(url), + user_agent_header=None, + additional_headers=_merge_mappings(dict(headers.values()), {}), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = normalize_httpx_url(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses" + return base_url.copy_with(raw_path=merge_raw_path) + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + await self.__connection.close() + + +class ResponsesConnection: + """Represents a live WebSocket connection to the Responses API""" + + response: ResponsesResponseResource + + _connection: WebSocketConnection + + def __init__( + self, + connection: WebSocketConnection, + *, + make_ws: Callable[[Query, Headers], WebSocketConnection] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._reconnect_attempt = 0 + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue or SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=True) + + self.response = ResponsesResponseResource(self) + + def __iter__(self) -> Iterator[BetaResponsesServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + def recv(self) -> BetaResponsesServerEvent: + """ + Receive the next message from the connection and parses it into a `BetaResponsesServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + event = self.parse_event(self.recv_bytes()) + event_type = ( + cast("dict[str, object]", event).get("type") + if isinstance(cast(object, event), dict) + else getattr(event, "type", None) + ) + # A successful upgrade can still be followed by an admission error. + # Reset the budget only after receiving a non-error application event. + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return event + + def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `BetaResponsesServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + from ....lib._websocket import _recv_bytes + + message = _recv_bytes(self._connection) + log.debug("Received WebSocket message: %i bytes", len(message)) + if self._reconnect_attempt: + # Account for raw application progress without changing frame delivery. + try: + event_data: object = json.loads(message) + except (ValueError, RecursionError): + return message + event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return message + + def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, BetaResponsesClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + try: + self._connection.send(data) + except Exception: + self._send_queue.enqueue(data) + raise + + def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + self._connection.send(data) + + def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> BetaResponsesServerEvent: + """ + Converts a raw `str` or `bytes` message into a `BetaResponsesServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + BetaResponsesServerEvent, + construct_type_unchecked(value=json.loads(data), type_=cast(Any, BetaResponsesServerEvent)), + ) + + def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1): + self._reconnect_attempt = attempt + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + time.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + try: + self._send_queue.flush_sync(lambda data: self._connection.send(data)) + except Exception: + log.warning("Failed to flush send queue after reconnect") + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("response.steer.accepted", my_handler) + + Or as a decorator:: + + @connection.on("response.steer.accepted") + def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> ResponsesConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks the current thread until the connection is closed. This is the push-based + alternative to iterating with ``for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, BetaResponseWsError): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + handler(event) + + for handler in generic: + handler(event) + + +class ResponsesConnectionManager: + """ + Context manager over a `ResponsesConnection` that is returned by `beta.responses.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.beta.responses.connect(...).enter() + # ... + connection.close() + ``` + """ + + def __init__( + self, + *, + client: OpenAI, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__connection: ResponsesConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=True) + + def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, BetaResponsesClientEventParam)) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``ResponsesConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> ResponsesConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def __enter__(self) -> ResponsesConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.beta.responses.connect(...).enter() + # ... + connection.close() + ``` + """ + ws = self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = ResponsesConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + self.__connection._flush_send_queue() + + return self.__connection + + enter = __enter__ + + def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketConnection: + try: + from websockets.sync.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **extra_query, + }, + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + options = self.__client._prepare_options( + FinalRequestOptions.construct( + method="get", + url=str(url), + headers=dict(extra_headers), + security={"bearer_auth": True}, + ) + ) + url = self.__client._prepare_url(options.url).copy_merge_params( + self.__client.qs.stringify(cast(Any, options.params)) + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + headers = { + key.lower(): (key, value) + for header_set in ( + self.__client.auth_headers, + {}, + self.__client.default_headers, + options.headers if is_given(options.headers) else {}, + ) + for key, value in header_set.items() + } + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + return connect( + str(url), + user_agent_header=None, + additional_headers=_merge_mappings(dict(headers.values()), {}), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = normalize_httpx_url(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses" + return base_url.copy_with(raw_path=merge_raw_path) + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + self.__connection.close() + + +class BaseResponsesConnectionResource: + def __init__(self, connection: ResponsesConnection) -> None: + self._connection = connection + + +class ResponsesResponseResource(BaseResponsesConnectionResource): + def create( + self, + *, + access_programs: beta_responses_client_event_param.ResponseCreateAccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[beta_responses_client_event_param.ResponseCreateContextManagement]] + | Omit = omit, + conversation: Optional[beta_responses_client_event_param.ResponseCreateConversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + ] + | Omit = omit, + moderation: Optional[beta_responses_client_event_param.ResponseCreateModeration] | Omit = omit, + multi_agent: Optional[beta_responses_client_event_param.ResponseCreateMultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: beta_responses_client_event_param.ResponseCreatePromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[beta_responses_client_event_param.ResponseCreateReasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[BetaServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[bool] | Omit = omit, + stream_id: str | Omit = omit, + stream_options: Optional[beta_responses_client_event_param.ResponseCreateStreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: beta_responses_client_event_param.ResponseCreateToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + ) -> None: + """ + Client event for creating a response over a persistent WebSocket connection. + This payload uses the same top-level fields as `POST /v1/responses`, plus + WebSocket-only envelope metadata. + + Notes: + - `stream` is implicit over WebSocket and should not be sent. + - `background` is not supported over WebSocket. + - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`. + """ + self._connection.send( + cast( + BetaResponsesClientEventParam, + strip_not_given( + { + "type": "response.create", + "access_programs": access_programs, + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "multi_agent": multi_agent, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_id": stream_id, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + } + ), + ) + ) + + def inject(self, *, input: Iterable[BetaResponseInputItemParam], response_id: str) -> None: + """ + Injects input items into an active response over a WebSocket connection. + The items are validated and committed atomically. Currently, the server + accepts client-owned tool outputs that resume a waiting agent. + """ + self._connection.send({"type": "response.inject", "input": input, "response_id": response_id}) + + def steer(self, *, input: BetaResponseSteerInputParam, previous_response_id: str) -> None: + """Queues user input to steer a response on this WebSocket connection. + + Input + can contain text, images, and files. Steering is supported only for + single-agent responses on models and execution modes that support steering. + Responses bound to a conversation or using automatic compaction do not + support steering. + + A `response.steer.accepted` event acknowledges that the server owns the + queued input, not that it has been applied. The successor's `response.created` + event is the commit point. Input that cannot be committed is returned in + `response.steer.failed`. + + Steering may cause the active response to finish at a safe output boundary + with `response.incomplete` and `incomplete_details.reason` set to `steered`, + followed automatically by a successor `response.created`. Normal completion + can also be followed by an automatic successor. Automatic successors inherit + the previous response's settings and continue from it with the queued input. + + If the response stops for client-owned tool output or approval, accepted + steering input remains queued and `response.steer.pending` is emitted after + `response.completed`. Fill the `required_input` stubs from that event with + saved tool results or approval decisions, and send one explicit + `response.create` per parent with the same `previous_response_id` and + WebSocket lane. Do not rerun tools or resend accepted steering input. The + queued input is prepended in submission order to that request's input, and + the explicit request retains its own settings. + + This event accepts only `type`, `previous_response_id`, and `input`. Do not + send `stream_id`; the target response determines the WebSocket lane. + """ + self._connection.send({"type": "response.steer", "input": input, "previous_response_id": previous_response_id}) + + +class BaseAsyncResponsesConnectionResource: + def __init__(self, connection: AsyncResponsesConnection) -> None: + self._connection = connection + + +class AsyncResponsesResponseResource(BaseAsyncResponsesConnectionResource): + async def create( + self, + *, + access_programs: beta_responses_client_event_param.ResponseCreateAccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[beta_responses_client_event_param.ResponseCreateContextManagement]] + | Omit = omit, + conversation: Optional[beta_responses_client_event_param.ResponseCreateConversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + ] + | Omit = omit, + moderation: Optional[beta_responses_client_event_param.ResponseCreateModeration] | Omit = omit, + multi_agent: Optional[beta_responses_client_event_param.ResponseCreateMultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: beta_responses_client_event_param.ResponseCreatePromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[beta_responses_client_event_param.ResponseCreateReasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[BetaServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[bool] | Omit = omit, + stream_id: str | Omit = omit, + stream_options: Optional[beta_responses_client_event_param.ResponseCreateStreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: beta_responses_client_event_param.ResponseCreateToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + ) -> None: + """ + Client event for creating a response over a persistent WebSocket connection. + This payload uses the same top-level fields as `POST /v1/responses`, plus + WebSocket-only envelope metadata. + + Notes: + - `stream` is implicit over WebSocket and should not be sent. + - `background` is not supported over WebSocket. + - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`. + """ + await self._connection.send( + cast( + BetaResponsesClientEventParam, + strip_not_given( + { + "type": "response.create", + "access_programs": access_programs, + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "multi_agent": multi_agent, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_id": stream_id, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + } + ), + ) + ) + + async def inject(self, *, input: Iterable[BetaResponseInputItemParam], response_id: str) -> None: + """ + Injects input items into an active response over a WebSocket connection. + The items are validated and committed atomically. Currently, the server + accepts client-owned tool outputs that resume a waiting agent. + """ + await self._connection.send({"type": "response.inject", "input": input, "response_id": response_id}) + + async def steer(self, *, input: BetaResponseSteerInputParam, previous_response_id: str) -> None: + """Queues user input to steer a response on this WebSocket connection. + + Input + can contain text, images, and files. Steering is supported only for + single-agent responses on models and execution modes that support steering. + Responses bound to a conversation or using automatic compaction do not + support steering. + + A `response.steer.accepted` event acknowledges that the server owns the + queued input, not that it has been applied. The successor's `response.created` + event is the commit point. Input that cannot be committed is returned in + `response.steer.failed`. + + Steering may cause the active response to finish at a safe output boundary + with `response.incomplete` and `incomplete_details.reason` set to `steered`, + followed automatically by a successor `response.created`. Normal completion + can also be followed by an automatic successor. Automatic successors inherit + the previous response's settings and continue from it with the queued input. + + If the response stops for client-owned tool output or approval, accepted + steering input remains queued and `response.steer.pending` is emitted after + `response.completed`. Fill the `required_input` stubs from that event with + saved tool results or approval decisions, and send one explicit + `response.create` per parent with the same `previous_response_id` and + WebSocket lane. Do not rerun tools or resend accepted steering input. The + queued input is prepended in submission order to that request's input, and + the explicit request retains its own settings. + + This event accepts only `type`, `previous_response_id`, and `input`. Do not + send `stream_id`; the target response determines the WebSocket lane. + """ + await self._connection.send( + {"type": "response.steer", "input": input, "previous_response_id": previous_response_id} + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/threads/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/__init__.py new file mode 100644 index 0000000..b367bf1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/__init__.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .runs import ( + Runs as Runs, + AsyncRuns as AsyncRuns, + RunsWithRawResponse as RunsWithRawResponse, + AsyncRunsWithRawResponse as AsyncRunsWithRawResponse, + RunsWithStreamingResponse as RunsWithStreamingResponse, + AsyncRunsWithStreamingResponse as AsyncRunsWithStreamingResponse, + ) + from .threads import ( + Threads as Threads, + AsyncThreads as AsyncThreads, + ThreadsWithRawResponse as ThreadsWithRawResponse, + AsyncThreadsWithRawResponse as AsyncThreadsWithRawResponse, + ThreadsWithStreamingResponse as ThreadsWithStreamingResponse, + AsyncThreadsWithStreamingResponse as AsyncThreadsWithStreamingResponse, + ) + from .messages import ( + Messages as Messages, + AsyncMessages as AsyncMessages, + MessagesWithRawResponse as MessagesWithRawResponse, + AsyncMessagesWithRawResponse as AsyncMessagesWithRawResponse, + MessagesWithStreamingResponse as MessagesWithStreamingResponse, + AsyncMessagesWithStreamingResponse as AsyncMessagesWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Runs": (".runs", "Runs"), + "AsyncRuns": (".runs", "AsyncRuns"), + "RunsWithRawResponse": (".runs", "RunsWithRawResponse"), + "AsyncRunsWithRawResponse": (".runs", "AsyncRunsWithRawResponse"), + "RunsWithStreamingResponse": (".runs", "RunsWithStreamingResponse"), + "AsyncRunsWithStreamingResponse": (".runs", "AsyncRunsWithStreamingResponse"), + "Messages": (".messages", "Messages"), + "AsyncMessages": (".messages", "AsyncMessages"), + "MessagesWithRawResponse": (".messages", "MessagesWithRawResponse"), + "AsyncMessagesWithRawResponse": (".messages", "AsyncMessagesWithRawResponse"), + "MessagesWithStreamingResponse": (".messages", "MessagesWithStreamingResponse"), + "AsyncMessagesWithStreamingResponse": (".messages", "AsyncMessagesWithStreamingResponse"), + "Threads": (".threads", "Threads"), + "AsyncThreads": (".threads", "AsyncThreads"), + "ThreadsWithRawResponse": (".threads", "ThreadsWithRawResponse"), + "AsyncThreadsWithRawResponse": (".threads", "AsyncThreadsWithRawResponse"), + "ThreadsWithStreamingResponse": (".threads", "ThreadsWithStreamingResponse"), + "AsyncThreadsWithStreamingResponse": (".threads", "AsyncThreadsWithStreamingResponse"), + } + _SUBMODULES = { + "messages", + "runs", + "threads", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Runs", + "AsyncRuns", + "RunsWithRawResponse", + "AsyncRunsWithRawResponse", + "RunsWithStreamingResponse", + "AsyncRunsWithStreamingResponse", + "Messages", + "AsyncMessages", + "MessagesWithRawResponse", + "AsyncMessagesWithRawResponse", + "MessagesWithStreamingResponse", + "AsyncMessagesWithStreamingResponse", + "Threads", + "AsyncThreads", + "ThreadsWithRawResponse", + "AsyncThreadsWithRawResponse", + "ThreadsWithStreamingResponse", + "AsyncThreadsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/threads/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..269db39 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/threads/__pycache__/messages.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/__pycache__/messages.cpython-312.pyc new file mode 100644 index 0000000..74b9d70 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/__pycache__/messages.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/threads/__pycache__/threads.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/__pycache__/threads.cpython-312.pyc new file mode 100644 index 0000000..5f8460a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/__pycache__/threads.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/threads/messages.py b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/messages.py new file mode 100644 index 0000000..28cd019 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/messages.py @@ -0,0 +1,753 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import typing_extensions +from typing import Union, Iterable, Optional +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.beta.threads import message_list_params, message_create_params, message_update_params +from ....types.beta.threads.message import Message +from ....types.shared_params.metadata import Metadata +from ....types.beta.threads.message_deleted import MessageDeleted +from ....types.beta.threads.message_content_part_param import MessageContentPartParam + +__all__ = ["Messages", "AsyncMessages"] + + +class Messages(SyncAPIResource): + """Build Assistants that can call models and use tools.""" + + @cached_property + def with_raw_response(self) -> MessagesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return MessagesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> MessagesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return MessagesWithStreamingResponse(self) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def create( + self, + thread_id: str, + *, + content: Union[str, Iterable[MessageContentPartParam]], + role: Literal["user", "assistant"], + attachments: Optional[Iterable[message_create_params.Attachment]] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Message: + """ + Create a message. + + Args: + content: The text contents of the message. + + role: + The role of the entity that is creating the message. Allowed values include: + + - `user`: Indicates the message is sent by an actual user and should be used in + most cases to represent user-generated messages. + - `assistant`: Indicates the message is generated by the assistant. Use this + value to insert messages from the assistant into the conversation. + + attachments: A list of files attached to the message, and the tools they should be added to. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + path_template("/threads/{thread_id}/messages", thread_id=thread_id), + body=maybe_transform( + { + "content": content, + "role": role, + "attachments": attachments, + "metadata": metadata, + }, + message_create_params.MessageCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Message, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def retrieve( + self, + message_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Message: + """ + Retrieve a message. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get( + path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Message, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def update( + self, + message_id: str, + *, + thread_id: str, + metadata: Optional[Metadata] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Message: + """ + Modifies a message. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), + body=maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Message, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def list( + self, + thread_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + run_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Message]: + """ + Returns a list of messages for a given thread. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + run_id: Filter messages by the run ID that generated them. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template("/threads/{thread_id}/messages", thread_id=thread_id), + page=SyncCursorPage[Message], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + "run_id": run_id, + }, + message_list_params.MessageListParams, + ), + security={"bearer_auth": True}, + ), + model=Message, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def delete( + self, + message_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> MessageDeleted: + """ + Deletes a message. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._delete( + path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=MessageDeleted, + ) + + +class AsyncMessages(AsyncAPIResource): + """Build Assistants that can call models and use tools.""" + + @cached_property + def with_raw_response(self) -> AsyncMessagesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncMessagesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncMessagesWithStreamingResponse(self) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def create( + self, + thread_id: str, + *, + content: Union[str, Iterable[MessageContentPartParam]], + role: Literal["user", "assistant"], + attachments: Optional[Iterable[message_create_params.Attachment]] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Message: + """ + Create a message. + + Args: + content: The text contents of the message. + + role: + The role of the entity that is creating the message. Allowed values include: + + - `user`: Indicates the message is sent by an actual user and should be used in + most cases to represent user-generated messages. + - `assistant`: Indicates the message is generated by the assistant. Use this + value to insert messages from the assistant into the conversation. + + attachments: A list of files attached to the message, and the tools they should be added to. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + path_template("/threads/{thread_id}/messages", thread_id=thread_id), + body=await async_maybe_transform( + { + "content": content, + "role": role, + "attachments": attachments, + "metadata": metadata, + }, + message_create_params.MessageCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Message, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def retrieve( + self, + message_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Message: + """ + Retrieve a message. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._get( + path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Message, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def update( + self, + message_id: str, + *, + thread_id: str, + metadata: Optional[Metadata] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Message: + """ + Modifies a message. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), + body=await async_maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Message, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def list( + self, + thread_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + run_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Message, AsyncCursorPage[Message]]: + """ + Returns a list of messages for a given thread. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + run_id: Filter messages by the run ID that generated them. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template("/threads/{thread_id}/messages", thread_id=thread_id), + page=AsyncCursorPage[Message], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + "run_id": run_id, + }, + message_list_params.MessageListParams, + ), + security={"bearer_auth": True}, + ), + model=Message, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def delete( + self, + message_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> MessageDeleted: + """ + Deletes a message. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._delete( + path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=MessageDeleted, + ) + + +class MessagesWithRawResponse: + def __init__(self, messages: Messages) -> None: + self._messages = messages + + self.create = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + messages.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + messages.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + messages.update, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + messages.list, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + messages.delete, # pyright: ignore[reportDeprecated], + ) + ) + + +class AsyncMessagesWithRawResponse: + def __init__(self, messages: AsyncMessages) -> None: + self._messages = messages + + self.create = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + messages.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + messages.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + messages.update, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + messages.list, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + messages.delete, # pyright: ignore[reportDeprecated], + ) + ) + + +class MessagesWithStreamingResponse: + def __init__(self, messages: Messages) -> None: + self._messages = messages + + self.create = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + messages.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + messages.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + messages.update, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + messages.list, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + messages.delete, # pyright: ignore[reportDeprecated], + ) + ) + + +class AsyncMessagesWithStreamingResponse: + def __init__(self, messages: AsyncMessages) -> None: + self._messages = messages + + self.create = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + messages.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + messages.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + messages.update, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + messages.list, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + messages.delete, # pyright: ignore[reportDeprecated], + ) + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/__init__.py new file mode 100644 index 0000000..3152e32 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .runs import ( + Runs as Runs, + AsyncRuns as AsyncRuns, + RunsWithRawResponse as RunsWithRawResponse, + AsyncRunsWithRawResponse as AsyncRunsWithRawResponse, + RunsWithStreamingResponse as RunsWithStreamingResponse, + AsyncRunsWithStreamingResponse as AsyncRunsWithStreamingResponse, + ) + from .steps import ( + Steps as Steps, + AsyncSteps as AsyncSteps, + StepsWithRawResponse as StepsWithRawResponse, + AsyncStepsWithRawResponse as AsyncStepsWithRawResponse, + StepsWithStreamingResponse as StepsWithStreamingResponse, + AsyncStepsWithStreamingResponse as AsyncStepsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Steps": (".steps", "Steps"), + "AsyncSteps": (".steps", "AsyncSteps"), + "StepsWithRawResponse": (".steps", "StepsWithRawResponse"), + "AsyncStepsWithRawResponse": (".steps", "AsyncStepsWithRawResponse"), + "StepsWithStreamingResponse": (".steps", "StepsWithStreamingResponse"), + "AsyncStepsWithStreamingResponse": (".steps", "AsyncStepsWithStreamingResponse"), + "Runs": (".runs", "Runs"), + "AsyncRuns": (".runs", "AsyncRuns"), + "RunsWithRawResponse": (".runs", "RunsWithRawResponse"), + "AsyncRunsWithRawResponse": (".runs", "AsyncRunsWithRawResponse"), + "RunsWithStreamingResponse": (".runs", "RunsWithStreamingResponse"), + "AsyncRunsWithStreamingResponse": (".runs", "AsyncRunsWithStreamingResponse"), + } + _SUBMODULES = { + "runs", + "steps", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Steps", + "AsyncSteps", + "StepsWithRawResponse", + "AsyncStepsWithRawResponse", + "StepsWithStreamingResponse", + "AsyncStepsWithStreamingResponse", + "Runs", + "AsyncRuns", + "RunsWithRawResponse", + "AsyncRunsWithRawResponse", + "RunsWithStreamingResponse", + "AsyncRunsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1961d39 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/__pycache__/runs.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/__pycache__/runs.cpython-312.pyc new file mode 100644 index 0000000..01635b1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/__pycache__/runs.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/__pycache__/steps.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/__pycache__/steps.cpython-312.pyc new file mode 100644 index 0000000..1022db4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/__pycache__/steps.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/runs.py b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/runs.py new file mode 100644 index 0000000..5f2ece0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/runs.py @@ -0,0 +1,3161 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import typing_extensions +from typing import List, Union, Iterable, Optional +from functools import partial +from typing_extensions import Literal, overload + +import httpx2 + +from ..... import _legacy_response +from .steps import ( + Steps, + AsyncSteps, + StepsWithRawResponse, + AsyncStepsWithRawResponse, + StepsWithStreamingResponse, + AsyncStepsWithStreamingResponse, +) +from ....._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import ( + is_given, + path_template, + required_args, + maybe_transform, + async_maybe_transform, +) +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._streaming import Stream, AsyncStream +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....lib.streaming import ( + AssistantEventHandler, + AssistantEventHandlerT, + AssistantStreamManager, + AsyncAssistantEventHandler, + AsyncAssistantEventHandlerT, + AsyncAssistantStreamManager, +) +from .....types.beta.threads import ( + run_list_params, + run_create_params, + run_update_params, + run_submit_tool_outputs_params, +) +from .....types.beta.threads.run import Run +from .....types.shared.chat_model import ChatModel +from .....types.shared_params.metadata import Metadata +from .....types.shared.reasoning_effort import ReasoningEffort +from .....types.beta.assistant_tool_param import AssistantToolParam +from .....types.beta.assistant_stream_event import AssistantStreamEvent +from .....types.beta.threads.runs.run_step_include import RunStepInclude +from .....types.beta.assistant_tool_choice_option_param import AssistantToolChoiceOptionParam +from .....types.beta.assistant_response_format_option_param import AssistantResponseFormatOptionParam + +__all__ = ["Runs", "AsyncRuns"] + + +class Runs(SyncAPIResource): + """Build Assistants that can call models and use tools.""" + + @cached_property + def steps(self) -> Steps: + """Build Assistants that can call models and use tools.""" + return Steps(self._client) + + @cached_property + def with_raw_response(self) -> RunsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RunsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RunsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RunsWithStreamingResponse(self) + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def create( + self, + thread_id: str, + *, + assistant_id: str, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run: + """ + Create a run. + + Args: + assistant_id: The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + + additional_instructions: Appends additional instructions at the end of the instructions for the run. This + is useful for modifying the behavior on a per-run basis without overriding other + instructions. + + additional_messages: Adds additional messages to the thread before creating the run. + + instructions: Overrides the + [instructions](https://developers.openai.com/api/docs/assistants/migration) of + the assistant. This is useful for modifying the behavior on a per-run basis. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the initial context window of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def create( + self, + thread_id: str, + *, + assistant_id: str, + stream: Literal[True], + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[AssistantStreamEvent]: + """ + Create a run. + + Args: + assistant_id: The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + + additional_instructions: Appends additional instructions at the end of the instructions for the run. This + is useful for modifying the behavior on a per-run basis without overriding other + instructions. + + additional_messages: Adds additional messages to the thread before creating the run. + + instructions: Overrides the + [instructions](https://developers.openai.com/api/docs/assistants/migration) of + the assistant. This is useful for modifying the behavior on a per-run basis. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the initial context window of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def create( + self, + thread_id: str, + *, + assistant_id: str, + stream: bool, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run | Stream[AssistantStreamEvent]: + """ + Create a run. + + Args: + assistant_id: The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + + additional_instructions: Appends additional instructions at the end of the instructions for the run. This + is useful for modifying the behavior on a per-run basis without overriding other + instructions. + + additional_messages: Adds additional messages to the thread before creating the run. + + instructions: Overrides the + [instructions](https://developers.openai.com/api/docs/assistants/migration) of + the assistant. This is useful for modifying the behavior on a per-run basis. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the initial context window of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + @required_args(["assistant_id"], ["assistant_id", "stream"]) + def create( + self, + thread_id: str, + *, + assistant_id: str, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run | Stream[AssistantStreamEvent]: + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + path_template("/threads/{thread_id}/runs", thread_id=thread_id), + body=maybe_transform( + { + "assistant_id": assistant_id, + "additional_instructions": additional_instructions, + "additional_messages": additional_messages, + "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, + "metadata": metadata, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "reasoning_effort": reasoning_effort, + "response_format": response_format, + "stream": stream, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "top_p": top_p, + "truncation_strategy": truncation_strategy, + }, + run_create_params.RunCreateParamsStreaming if stream else run_create_params.RunCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, run_create_params.RunCreateParams), + security={"bearer_auth": True}, + synthesize_event_and_data=True, + ), + cast_to=Run, + stream=stream or False, + stream_cls=Stream[AssistantStreamEvent], + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def retrieve( + self, + run_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run: + """ + Retrieves a run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get( + path_template("/threads/{thread_id}/runs/{run_id}", thread_id=thread_id, run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Run, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def update( + self, + run_id: str, + *, + thread_id: str, + metadata: Optional[Metadata] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run: + """ + Modifies a run. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + path_template("/threads/{thread_id}/runs/{run_id}", thread_id=thread_id, run_id=run_id), + body=maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Run, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def list( + self, + thread_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Run]: + """ + Returns a list of runs belonging to a thread. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template("/threads/{thread_id}/runs", thread_id=thread_id), + page=SyncCursorPage[Run], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + run_list_params.RunListParams, + ), + security={"bearer_auth": True}, + ), + model=Run, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def cancel( + self, + run_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run: + """ + Cancels a run that is `in_progress`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + path_template("/threads/{thread_id}/runs/{run_id}/cancel", thread_id=thread_id, run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Run, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def create_and_poll( + self, + *, + assistant_id: str, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + poll_interval_ms: int | Omit = omit, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to create a run an poll for a terminal state. More information on Run + lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + run = self.create( # pyright: ignore[reportDeprecated] + thread_id=thread_id, + assistant_id=assistant_id, + include=include, + additional_instructions=additional_instructions, + additional_messages=additional_messages, + instructions=instructions, + max_completion_tokens=max_completion_tokens, + max_prompt_tokens=max_prompt_tokens, + metadata=metadata, + model=model, + response_format=response_format, + temperature=temperature, + tool_choice=tool_choice, + parallel_tool_calls=parallel_tool_calls, + reasoning_effort=reasoning_effort, + # We assume we are not streaming when polling + stream=False, + tools=tools, + truncation_strategy=truncation_strategy, + top_p=top_p, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return self.poll( # pyright: ignore[reportDeprecated] + run.id, + thread_id=thread_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + poll_interval_ms=poll_interval_ms, + timeout=timeout, + ) + + @overload + @typing_extensions.deprecated("use `stream` instead") + def create_and_stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler]: + """Create a Run stream""" + ... + + @overload + @typing_extensions.deprecated("use `stream` instead") + def create_and_stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + thread_id: str, + event_handler: AssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandlerT]: + """Create a Run stream""" + ... + + @typing_extensions.deprecated("use `stream` instead") + def create_and_stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + thread_id: str, + event_handler: AssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]: + """Create a Run stream""" + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + + extra_headers = { + "OpenAI-Beta": "assistants=v2", + "X-Stainless-Stream-Helper": "threads.runs.create_and_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + make_request = partial( + self._post, + path_template("/threads/{thread_id}/runs", thread_id=thread_id), + body=maybe_transform( + { + "assistant_id": assistant_id, + "additional_instructions": additional_instructions, + "additional_messages": additional_messages, + "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, + "metadata": metadata, + "model": model, + "response_format": response_format, + "temperature": temperature, + "tool_choice": tool_choice, + "stream": True, + "tools": tools, + "truncation_strategy": truncation_strategy, + "parallel_tool_calls": parallel_tool_calls, + "reasoning_effort": reasoning_effort, + "top_p": top_p, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Run, + stream=True, + stream_cls=Stream[AssistantStreamEvent], + ) + return AssistantStreamManager(make_request, event_handler=event_handler or AssistantEventHandler()) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def poll( + self, + run_id: str, + thread_id: str, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + poll_interval_ms: int | Omit = omit, + ) -> Run: + """ + A helper to poll a run status until it reaches a terminal state. More + information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + extra_headers = {"X-Stainless-Poll-Helper": "true", **(extra_headers or {})} + + if is_given(poll_interval_ms): + extra_headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + terminal_states = {"requires_action", "cancelled", "completed", "failed", "expired", "incomplete"} + while True: + response = self.with_raw_response.retrieve( # pyright: ignore[reportDeprecated] + thread_id=thread_id, + run_id=run_id, + extra_headers=extra_headers, + extra_body=extra_body, + extra_query=extra_query, + timeout=timeout, + ) + + run = response.parse() + # Return if we reached a terminal state + if run.status in terminal_states: + return run + + if not is_given(poll_interval_ms): + from_header = response.headers.get("openai-poll-after-ms") + if from_header is not None: + poll_interval_ms = int(from_header) + else: + poll_interval_ms = 1000 + + self._sleep(poll_interval_ms / 1000) + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def stream( + self, + *, + assistant_id: str, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler]: + """Create a Run stream""" + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def stream( + self, + *, + assistant_id: str, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + thread_id: str, + event_handler: AssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandlerT]: + """Create a Run stream""" + ... + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def stream( + self, + *, + assistant_id: str, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + thread_id: str, + event_handler: AssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]: + """Create a Run stream""" + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + + extra_headers = { + "OpenAI-Beta": "assistants=v2", + "X-Stainless-Stream-Helper": "threads.runs.create_and_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + make_request = partial( + self._post, + path_template("/threads/{thread_id}/runs", thread_id=thread_id), + body=maybe_transform( + { + "assistant_id": assistant_id, + "additional_instructions": additional_instructions, + "additional_messages": additional_messages, + "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, + "metadata": metadata, + "model": model, + "response_format": response_format, + "temperature": temperature, + "tool_choice": tool_choice, + "stream": True, + "tools": tools, + "parallel_tool_calls": parallel_tool_calls, + "reasoning_effort": reasoning_effort, + "truncation_strategy": truncation_strategy, + "top_p": top_p, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, run_create_params.RunCreateParams), + security={"bearer_auth": True}, + ), + cast_to=Run, + stream=True, + stream_cls=Stream[AssistantStreamEvent], + ) + return AssistantStreamManager(make_request, event_handler=event_handler or AssistantEventHandler()) + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + stream: Optional[Literal[False]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run: + """ + When a run has the `status: "requires_action"` and `required_action.type` is + `submit_tool_outputs`, this endpoint can be used to submit the outputs from the + tool calls once they're all completed. All outputs must be submitted in a single + request. + + Args: + tool_outputs: A list of tools for which the outputs are being submitted. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + stream: Literal[True], + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[AssistantStreamEvent]: + """ + When a run has the `status: "requires_action"` and `required_action.type` is + `submit_tool_outputs`, this endpoint can be used to submit the outputs from the + tool calls once they're all completed. All outputs must be submitted in a single + request. + + Args: + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + tool_outputs: A list of tools for which the outputs are being submitted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + stream: bool, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run | Stream[AssistantStreamEvent]: + """ + When a run has the `status: "requires_action"` and `required_action.type` is + `submit_tool_outputs`, this endpoint can be used to submit the outputs from the + tool calls once they're all completed. All outputs must be submitted in a single + request. + + Args: + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + tool_outputs: A list of tools for which the outputs are being submitted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + @required_args(["thread_id", "tool_outputs"], ["thread_id", "stream", "tool_outputs"]) + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run | Stream[AssistantStreamEvent]: + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + path_template("/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", thread_id=thread_id, run_id=run_id), + body=maybe_transform( + { + "tool_outputs": tool_outputs, + "stream": stream, + }, + run_submit_tool_outputs_params.RunSubmitToolOutputsParamsStreaming + if stream + else run_submit_tool_outputs_params.RunSubmitToolOutputsParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + synthesize_event_and_data=True, + ), + cast_to=Run, + stream=stream or False, + stream_cls=Stream[AssistantStreamEvent], + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def submit_tool_outputs_and_poll( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + poll_interval_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to submit a tool output to a run and poll for a terminal run state. + More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + run = self.submit_tool_outputs( # pyright: ignore[reportDeprecated] + run_id=run_id, + thread_id=thread_id, + tool_outputs=tool_outputs, + stream=False, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return self.poll( # pyright: ignore[reportDeprecated] + run_id=run.id, + thread_id=thread_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + poll_interval_ms=poll_interval_ms, + ) + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def submit_tool_outputs_stream( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler]: + """ + Submit the tool outputs from a previous run and stream the run to a terminal + state. More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def submit_tool_outputs_stream( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + event_handler: AssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandlerT]: + """ + Submit the tool outputs from a previous run and stream the run to a terminal + state. More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + ... + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def submit_tool_outputs_stream( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + event_handler: AssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]: + """ + Submit the tool outputs from a previous run and stream the run to a terminal + state. More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + + extra_headers = { + "OpenAI-Beta": "assistants=v2", + "X-Stainless-Stream-Helper": "threads.runs.submit_tool_outputs_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + request = partial( + self._post, + path_template("/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", thread_id=thread_id, run_id=run_id), + body=maybe_transform( + { + "tool_outputs": tool_outputs, + "stream": True, + }, + run_submit_tool_outputs_params.RunSubmitToolOutputsParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Run, + stream=True, + stream_cls=Stream[AssistantStreamEvent], + ) + return AssistantStreamManager(request, event_handler=event_handler or AssistantEventHandler()) + + +class AsyncRuns(AsyncAPIResource): + """Build Assistants that can call models and use tools.""" + + @cached_property + def steps(self) -> AsyncSteps: + """Build Assistants that can call models and use tools.""" + return AsyncSteps(self._client) + + @cached_property + def with_raw_response(self) -> AsyncRunsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRunsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRunsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRunsWithStreamingResponse(self) + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def create( + self, + thread_id: str, + *, + assistant_id: str, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run: + """ + Create a run. + + Args: + assistant_id: The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + + additional_instructions: Appends additional instructions at the end of the instructions for the run. This + is useful for modifying the behavior on a per-run basis without overriding other + instructions. + + additional_messages: Adds additional messages to the thread before creating the run. + + instructions: Overrides the + [instructions](https://developers.openai.com/api/docs/assistants/migration) of + the assistant. This is useful for modifying the behavior on a per-run basis. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the initial context window of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def create( + self, + thread_id: str, + *, + assistant_id: str, + stream: Literal[True], + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[AssistantStreamEvent]: + """ + Create a run. + + Args: + assistant_id: The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + + additional_instructions: Appends additional instructions at the end of the instructions for the run. This + is useful for modifying the behavior on a per-run basis without overriding other + instructions. + + additional_messages: Adds additional messages to the thread before creating the run. + + instructions: Overrides the + [instructions](https://developers.openai.com/api/docs/assistants/migration) of + the assistant. This is useful for modifying the behavior on a per-run basis. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the initial context window of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def create( + self, + thread_id: str, + *, + assistant_id: str, + stream: bool, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run | AsyncStream[AssistantStreamEvent]: + """ + Create a run. + + Args: + assistant_id: The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + + additional_instructions: Appends additional instructions at the end of the instructions for the run. This + is useful for modifying the behavior on a per-run basis without overriding other + instructions. + + additional_messages: Adds additional messages to the thread before creating the run. + + instructions: Overrides the + [instructions](https://developers.openai.com/api/docs/assistants/migration) of + the assistant. This is useful for modifying the behavior on a per-run basis. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the initial context window of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + @required_args(["assistant_id"], ["assistant_id", "stream"]) + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def create( + self, + thread_id: str, + *, + assistant_id: str, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run | AsyncStream[AssistantStreamEvent]: + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + path_template("/threads/{thread_id}/runs", thread_id=thread_id), + body=await async_maybe_transform( + { + "assistant_id": assistant_id, + "additional_instructions": additional_instructions, + "additional_messages": additional_messages, + "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, + "metadata": metadata, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "reasoning_effort": reasoning_effort, + "response_format": response_format, + "stream": stream, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "top_p": top_p, + "truncation_strategy": truncation_strategy, + }, + run_create_params.RunCreateParamsStreaming if stream else run_create_params.RunCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"include": include}, run_create_params.RunCreateParams), + security={"bearer_auth": True}, + synthesize_event_and_data=True, + ), + cast_to=Run, + stream=stream or False, + stream_cls=AsyncStream[AssistantStreamEvent], + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def retrieve( + self, + run_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run: + """ + Retrieves a run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._get( + path_template("/threads/{thread_id}/runs/{run_id}", thread_id=thread_id, run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Run, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def update( + self, + run_id: str, + *, + thread_id: str, + metadata: Optional[Metadata] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run: + """ + Modifies a run. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + path_template("/threads/{thread_id}/runs/{run_id}", thread_id=thread_id, run_id=run_id), + body=await async_maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Run, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def list( + self, + thread_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Run, AsyncCursorPage[Run]]: + """ + Returns a list of runs belonging to a thread. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template("/threads/{thread_id}/runs", thread_id=thread_id), + page=AsyncCursorPage[Run], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + run_list_params.RunListParams, + ), + security={"bearer_auth": True}, + ), + model=Run, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def cancel( + self, + run_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run: + """ + Cancels a run that is `in_progress`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + path_template("/threads/{thread_id}/runs/{run_id}/cancel", thread_id=thread_id, run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Run, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def create_and_poll( + self, + *, + assistant_id: str, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + poll_interval_ms: int | Omit = omit, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to create a run an poll for a terminal state. More information on Run + lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + run = await self.create( # pyright: ignore[reportDeprecated] + thread_id=thread_id, + assistant_id=assistant_id, + include=include, + additional_instructions=additional_instructions, + additional_messages=additional_messages, + instructions=instructions, + max_completion_tokens=max_completion_tokens, + max_prompt_tokens=max_prompt_tokens, + metadata=metadata, + model=model, + response_format=response_format, + temperature=temperature, + tool_choice=tool_choice, + parallel_tool_calls=parallel_tool_calls, + reasoning_effort=reasoning_effort, + # We assume we are not streaming when polling + stream=False, + tools=tools, + truncation_strategy=truncation_strategy, + top_p=top_p, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return await self.poll( # pyright: ignore[reportDeprecated] + run.id, + thread_id=thread_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + poll_interval_ms=poll_interval_ms, + timeout=timeout, + ) + + @overload + @typing_extensions.deprecated("use `stream` instead") + def create_and_stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandler]: + """Create a Run stream""" + ... + + @overload + @typing_extensions.deprecated("use `stream` instead") + def create_and_stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + thread_id: str, + event_handler: AsyncAssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]: + """Create a Run stream""" + ... + + @typing_extensions.deprecated("use `stream` instead") + def create_and_stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + thread_id: str, + event_handler: AsyncAssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ( + AsyncAssistantStreamManager[AsyncAssistantEventHandler] + | AsyncAssistantStreamManager[AsyncAssistantEventHandlerT] + ): + """Create a Run stream""" + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + + extra_headers = { + "OpenAI-Beta": "assistants=v2", + "X-Stainless-Stream-Helper": "threads.runs.create_and_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + request = self._post( + path_template("/threads/{thread_id}/runs", thread_id=thread_id), + body=maybe_transform( + { + "assistant_id": assistant_id, + "additional_instructions": additional_instructions, + "additional_messages": additional_messages, + "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, + "metadata": metadata, + "model": model, + "response_format": response_format, + "temperature": temperature, + "tool_choice": tool_choice, + "stream": True, + "tools": tools, + "truncation_strategy": truncation_strategy, + "top_p": top_p, + "parallel_tool_calls": parallel_tool_calls, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Run, + stream=True, + stream_cls=AsyncStream[AssistantStreamEvent], + ) + return AsyncAssistantStreamManager(request, event_handler=event_handler or AsyncAssistantEventHandler()) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def poll( + self, + run_id: str, + thread_id: str, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + poll_interval_ms: int | Omit = omit, + ) -> Run: + """ + A helper to poll a run status until it reaches a terminal state. More + information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + extra_headers = {"X-Stainless-Poll-Helper": "true", **(extra_headers or {})} + + if is_given(poll_interval_ms): + extra_headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + terminal_states = {"requires_action", "cancelled", "completed", "failed", "expired", "incomplete"} + while True: + response = await self.with_raw_response.retrieve( # pyright: ignore[reportDeprecated] + thread_id=thread_id, + run_id=run_id, + extra_headers=extra_headers, + extra_body=extra_body, + extra_query=extra_query, + timeout=timeout, + ) + + run = response.parse() + # Return if we reached a terminal state + if run.status in terminal_states: + return run + + if not is_given(poll_interval_ms): + from_header = response.headers.get("openai-poll-after-ms") + if from_header is not None: + poll_interval_ms = int(from_header) + else: + poll_interval_ms = 1000 + + await self._sleep(poll_interval_ms / 1000) + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandler]: + """Create a Run stream""" + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def stream( + self, + *, + assistant_id: str, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + thread_id: str, + event_handler: AsyncAssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]: + """Create a Run stream""" + ... + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def stream( + self, + *, + assistant_id: str, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + thread_id: str, + event_handler: AsyncAssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ( + AsyncAssistantStreamManager[AsyncAssistantEventHandler] + | AsyncAssistantStreamManager[AsyncAssistantEventHandlerT] + ): + """Create a Run stream""" + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + + extra_headers = { + "OpenAI-Beta": "assistants=v2", + "X-Stainless-Stream-Helper": "threads.runs.create_and_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + request = self._post( + path_template("/threads/{thread_id}/runs", thread_id=thread_id), + body=maybe_transform( + { + "assistant_id": assistant_id, + "additional_instructions": additional_instructions, + "additional_messages": additional_messages, + "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, + "metadata": metadata, + "model": model, + "response_format": response_format, + "temperature": temperature, + "tool_choice": tool_choice, + "stream": True, + "tools": tools, + "parallel_tool_calls": parallel_tool_calls, + "reasoning_effort": reasoning_effort, + "truncation_strategy": truncation_strategy, + "top_p": top_p, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, run_create_params.RunCreateParams), + security={"bearer_auth": True}, + ), + cast_to=Run, + stream=True, + stream_cls=AsyncStream[AssistantStreamEvent], + ) + return AsyncAssistantStreamManager(request, event_handler=event_handler or AsyncAssistantEventHandler()) + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + stream: Optional[Literal[False]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run: + """ + When a run has the `status: "requires_action"` and `required_action.type` is + `submit_tool_outputs`, this endpoint can be used to submit the outputs from the + tool calls once they're all completed. All outputs must be submitted in a single + request. + + Args: + tool_outputs: A list of tools for which the outputs are being submitted. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + stream: Literal[True], + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[AssistantStreamEvent]: + """ + When a run has the `status: "requires_action"` and `required_action.type` is + `submit_tool_outputs`, this endpoint can be used to submit the outputs from the + tool calls once they're all completed. All outputs must be submitted in a single + request. + + Args: + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + tool_outputs: A list of tools for which the outputs are being submitted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + stream: bool, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run | AsyncStream[AssistantStreamEvent]: + """ + When a run has the `status: "requires_action"` and `required_action.type` is + `submit_tool_outputs`, this endpoint can be used to submit the outputs from the + tool calls once they're all completed. All outputs must be submitted in a single + request. + + Args: + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + tool_outputs: A list of tools for which the outputs are being submitted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + @required_args(["thread_id", "tool_outputs"], ["thread_id", "stream", "tool_outputs"]) + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run | AsyncStream[AssistantStreamEvent]: + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + path_template("/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", thread_id=thread_id, run_id=run_id), + body=await async_maybe_transform( + { + "tool_outputs": tool_outputs, + "stream": stream, + }, + run_submit_tool_outputs_params.RunSubmitToolOutputsParamsStreaming + if stream + else run_submit_tool_outputs_params.RunSubmitToolOutputsParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + synthesize_event_and_data=True, + ), + cast_to=Run, + stream=stream or False, + stream_cls=AsyncStream[AssistantStreamEvent], + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def submit_tool_outputs_and_poll( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + poll_interval_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to submit a tool output to a run and poll for a terminal run state. + More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + run = await self.submit_tool_outputs( # pyright: ignore[reportDeprecated] + run_id=run_id, + thread_id=thread_id, + tool_outputs=tool_outputs, + stream=False, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return await self.poll( # pyright: ignore[reportDeprecated] + run_id=run.id, + thread_id=thread_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + poll_interval_ms=poll_interval_ms, + ) + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def submit_tool_outputs_stream( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandler]: + """ + Submit the tool outputs from a previous run and stream the run to a terminal + state. More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def submit_tool_outputs_stream( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + event_handler: AsyncAssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]: + """ + Submit the tool outputs from a previous run and stream the run to a terminal + state. More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + ... + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def submit_tool_outputs_stream( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + event_handler: AsyncAssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ( + AsyncAssistantStreamManager[AsyncAssistantEventHandler] + | AsyncAssistantStreamManager[AsyncAssistantEventHandlerT] + ): + """ + Submit the tool outputs from a previous run and stream the run to a terminal + state. More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + + extra_headers = { + "OpenAI-Beta": "assistants=v2", + "X-Stainless-Stream-Helper": "threads.runs.submit_tool_outputs_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + request = self._post( + path_template("/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", thread_id=thread_id, run_id=run_id), + body=maybe_transform( + { + "tool_outputs": tool_outputs, + "stream": True, + }, + run_submit_tool_outputs_params.RunSubmitToolOutputsParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Run, + stream=True, + stream_cls=AsyncStream[AssistantStreamEvent], + ) + return AsyncAssistantStreamManager(request, event_handler=event_handler or AsyncAssistantEventHandler()) + + +class RunsWithRawResponse: + def __init__(self, runs: Runs) -> None: + self._runs = runs + + self.create = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + runs.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + runs.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + runs.update, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + runs.list, # pyright: ignore[reportDeprecated], + ) + ) + self.cancel = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + runs.cancel, # pyright: ignore[reportDeprecated], + ) + ) + self.submit_tool_outputs = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + runs.submit_tool_outputs, # pyright: ignore[reportDeprecated], + ) + ) + + @cached_property + def steps(self) -> StepsWithRawResponse: + """Build Assistants that can call models and use tools.""" + return StepsWithRawResponse(self._runs.steps) + + +class AsyncRunsWithRawResponse: + def __init__(self, runs: AsyncRuns) -> None: + self._runs = runs + + self.create = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + runs.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + runs.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + runs.update, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + runs.list, # pyright: ignore[reportDeprecated], + ) + ) + self.cancel = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + runs.cancel, # pyright: ignore[reportDeprecated], + ) + ) + self.submit_tool_outputs = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + runs.submit_tool_outputs, # pyright: ignore[reportDeprecated], + ) + ) + + @cached_property + def steps(self) -> AsyncStepsWithRawResponse: + """Build Assistants that can call models and use tools.""" + return AsyncStepsWithRawResponse(self._runs.steps) + + +class RunsWithStreamingResponse: + def __init__(self, runs: Runs) -> None: + self._runs = runs + + self.create = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + runs.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + runs.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + runs.update, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + runs.list, # pyright: ignore[reportDeprecated], + ) + ) + self.cancel = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + runs.cancel, # pyright: ignore[reportDeprecated], + ) + ) + self.submit_tool_outputs = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + runs.submit_tool_outputs, # pyright: ignore[reportDeprecated], + ) + ) + + @cached_property + def steps(self) -> StepsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" + return StepsWithStreamingResponse(self._runs.steps) + + +class AsyncRunsWithStreamingResponse: + def __init__(self, runs: AsyncRuns) -> None: + self._runs = runs + + self.create = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + runs.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + runs.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + runs.update, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + runs.list, # pyright: ignore[reportDeprecated], + ) + ) + self.cancel = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + runs.cancel, # pyright: ignore[reportDeprecated], + ) + ) + self.submit_tool_outputs = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + runs.submit_tool_outputs, # pyright: ignore[reportDeprecated], + ) + ) + + @cached_property + def steps(self) -> AsyncStepsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" + return AsyncStepsWithStreamingResponse(self._runs.steps) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/steps.py b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/steps.py new file mode 100644 index 0000000..13f6856 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/runs/steps.py @@ -0,0 +1,417 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import typing_extensions +from typing import List +from typing_extensions import Literal + +import httpx2 + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.beta.threads.runs import step_list_params, step_retrieve_params +from .....types.beta.threads.runs.run_step import RunStep +from .....types.beta.threads.runs.run_step_include import RunStepInclude + +__all__ = ["Steps", "AsyncSteps"] + + +class Steps(SyncAPIResource): + """Build Assistants that can call models and use tools.""" + + @cached_property + def with_raw_response(self) -> StepsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return StepsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> StepsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return StepsWithStreamingResponse(self) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def retrieve( + self, + step_id: str, + *, + thread_id: str, + run_id: str, + include: List[RunStepInclude] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RunStep: + """ + Retrieves a run step. + + Args: + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + if not step_id: + raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get( + path_template( + "/threads/{thread_id}/runs/{run_id}/steps/{step_id}", + thread_id=thread_id, + run_id=run_id, + step_id=step_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, step_retrieve_params.StepRetrieveParams), + security={"bearer_auth": True}, + ), + cast_to=RunStep, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def list( + self, + run_id: str, + *, + thread_id: str, + after: str | Omit = omit, + before: str | Omit = omit, + include: List[RunStepInclude] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[RunStep]: + """ + Returns a list of run steps belonging to a run. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template("/threads/{thread_id}/runs/{run_id}/steps", thread_id=thread_id, run_id=run_id), + page=SyncCursorPage[RunStep], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "include": include, + "limit": limit, + "order": order, + }, + step_list_params.StepListParams, + ), + security={"bearer_auth": True}, + ), + model=RunStep, + ) + + +class AsyncSteps(AsyncAPIResource): + """Build Assistants that can call models and use tools.""" + + @cached_property + def with_raw_response(self) -> AsyncStepsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncStepsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncStepsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncStepsWithStreamingResponse(self) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def retrieve( + self, + step_id: str, + *, + thread_id: str, + run_id: str, + include: List[RunStepInclude] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RunStep: + """ + Retrieves a run step. + + Args: + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + if not step_id: + raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._get( + path_template( + "/threads/{thread_id}/runs/{run_id}/steps/{step_id}", + thread_id=thread_id, + run_id=run_id, + step_id=step_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"include": include}, step_retrieve_params.StepRetrieveParams), + security={"bearer_auth": True}, + ), + cast_to=RunStep, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def list( + self, + run_id: str, + *, + thread_id: str, + after: str | Omit = omit, + before: str | Omit = omit, + include: List[RunStepInclude] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[RunStep, AsyncCursorPage[RunStep]]: + """ + Returns a list of run steps belonging to a run. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template("/threads/{thread_id}/runs/{run_id}/steps", thread_id=thread_id, run_id=run_id), + page=AsyncCursorPage[RunStep], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "include": include, + "limit": limit, + "order": order, + }, + step_list_params.StepListParams, + ), + security={"bearer_auth": True}, + ), + model=RunStep, + ) + + +class StepsWithRawResponse: + def __init__(self, steps: Steps) -> None: + self._steps = steps + + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + steps.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + steps.list, # pyright: ignore[reportDeprecated], + ) + ) + + +class AsyncStepsWithRawResponse: + def __init__(self, steps: AsyncSteps) -> None: + self._steps = steps + + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + steps.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + steps.list, # pyright: ignore[reportDeprecated], + ) + ) + + +class StepsWithStreamingResponse: + def __init__(self, steps: Steps) -> None: + self._steps = steps + + self.retrieve = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + steps.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + steps.list, # pyright: ignore[reportDeprecated], + ) + ) + + +class AsyncStepsWithStreamingResponse: + def __init__(self, steps: AsyncSteps) -> None: + self._steps = steps + + self.retrieve = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + steps.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + steps.list, # pyright: ignore[reportDeprecated], + ) + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/beta/threads/threads.py b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/threads.py new file mode 100644 index 0000000..09ecb75 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/beta/threads/threads.py @@ -0,0 +1,2009 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import typing_extensions +from typing import Union, Iterable, Optional +from functools import partial +from typing_extensions import Literal, overload + +import httpx2 + +from .... import _legacy_response +from .messages import ( + Messages, + AsyncMessages, + MessagesWithRawResponse, + AsyncMessagesWithRawResponse, + MessagesWithStreamingResponse, + AsyncMessagesWithStreamingResponse, +) +from ...._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, required_args, maybe_transform, async_maybe_transform +from .runs.runs import ( + Runs, + AsyncRuns, + RunsWithRawResponse, + AsyncRunsWithRawResponse, + RunsWithStreamingResponse, + AsyncRunsWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._streaming import Stream, AsyncStream +from ....types.beta import ( + thread_create_params, + thread_update_params, + thread_create_and_run_params, +) +from ...._base_client import make_request_options +from ....lib.streaming import ( + AssistantEventHandler, + AssistantEventHandlerT, + AssistantStreamManager, + AsyncAssistantEventHandler, + AsyncAssistantEventHandlerT, + AsyncAssistantStreamManager, +) +from ....types.beta.thread import Thread +from ....types.beta.threads.run import Run +from ....types.shared.chat_model import ChatModel +from ....types.beta.thread_deleted import ThreadDeleted +from ....types.shared_params.metadata import Metadata +from ....types.beta.assistant_tool_param import AssistantToolParam +from ....types.beta.assistant_stream_event import AssistantStreamEvent +from ....types.beta.assistant_tool_choice_option_param import AssistantToolChoiceOptionParam +from ....types.beta.assistant_response_format_option_param import AssistantResponseFormatOptionParam + +__all__ = ["Threads", "AsyncThreads"] + + +class Threads(SyncAPIResource): + """Build Assistants that can call models and use tools.""" + + @cached_property + def runs(self) -> Runs: + """Build Assistants that can call models and use tools.""" + return Runs(self._client) + + @cached_property + def messages(self) -> Messages: + """Build Assistants that can call models and use tools.""" + return Messages(self._client) + + @cached_property + def with_raw_response(self) -> ThreadsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ThreadsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ThreadsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ThreadsWithStreamingResponse(self) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def create( + self, + *, + messages: Iterable[thread_create_params.Message] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + tool_resources: Optional[thread_create_params.ToolResources] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Thread: + """ + Create a thread. + + Args: + messages: A list of + [messages](https://developers.openai.com/api/docs/assistants/migration) to start + the thread with. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + tool_resources: A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + "/threads", + body=maybe_transform( + { + "messages": messages, + "metadata": metadata, + "tool_resources": tool_resources, + }, + thread_create_params.ThreadCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Thread, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def retrieve( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Thread: + """ + Retrieves a thread. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get( + path_template("/threads/{thread_id}", thread_id=thread_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Thread, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def update( + self, + thread_id: str, + *, + metadata: Optional[Metadata] | Omit = omit, + tool_resources: Optional[thread_update_params.ToolResources] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Thread: + """ + Modifies a thread. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + tool_resources: A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + path_template("/threads/{thread_id}", thread_id=thread_id), + body=maybe_transform( + { + "metadata": metadata, + "tool_resources": tool_resources, + }, + thread_update_params.ThreadUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Thread, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def delete( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ThreadDeleted: + """ + Delete a thread. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._delete( + path_template("/threads/{thread_id}", thread_id=thread_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ThreadDeleted, + ) + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def create_and_run( + self, + *, + assistant_id: str, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run: + """ + Create a thread and run it in one request. + + Args: + assistant_id: The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + thread: Options to create a new thread. If no thread is provided when running a request, + an empty thread will be created. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the initial context window of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def create_and_run( + self, + *, + assistant_id: str, + stream: Literal[True], + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[AssistantStreamEvent]: + """ + Create a thread and run it in one request. + + Args: + assistant_id: The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + thread: Options to create a new thread. If no thread is provided when running a request, + an empty thread will be created. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the initial context window of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def create_and_run( + self, + *, + assistant_id: str, + stream: bool, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run | Stream[AssistantStreamEvent]: + """ + Create a thread and run it in one request. + + Args: + assistant_id: The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + thread: Options to create a new thread. If no thread is provided when running a request, + an empty thread will be created. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the initial context window of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + @required_args(["assistant_id"], ["assistant_id", "stream"]) + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + def create_and_run( + self, + *, + assistant_id: str, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run | Stream[AssistantStreamEvent]: + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + "/threads/runs", + body=maybe_transform( + { + "assistant_id": assistant_id, + "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, + "metadata": metadata, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "response_format": response_format, + "stream": stream, + "temperature": temperature, + "thread": thread, + "tool_choice": tool_choice, + "tool_resources": tool_resources, + "tools": tools, + "top_p": top_p, + "truncation_strategy": truncation_strategy, + }, + thread_create_and_run_params.ThreadCreateAndRunParamsStreaming + if stream + else thread_create_and_run_params.ThreadCreateAndRunParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + synthesize_event_and_data=True, + ), + cast_to=Run, + stream=stream or False, + stream_cls=Stream[AssistantStreamEvent], + ) + + def create_and_run_poll( + self, + *, + assistant_id: str, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + poll_interval_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to create a thread, start a run and then poll for a terminal state. + More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + run = self.create_and_run( # pyright: ignore[reportDeprecated] + assistant_id=assistant_id, + instructions=instructions, + max_completion_tokens=max_completion_tokens, + max_prompt_tokens=max_prompt_tokens, + metadata=metadata, + model=model, + parallel_tool_calls=parallel_tool_calls, + response_format=response_format, + temperature=temperature, + stream=False, + thread=thread, + tool_resources=tool_resources, + tool_choice=tool_choice, + truncation_strategy=truncation_strategy, + top_p=top_p, + tools=tools, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return self.runs.poll(run.id, run.thread_id, extra_headers, extra_query, extra_body, timeout, poll_interval_ms) # pyright: ignore[reportDeprecated] + + @overload + def create_and_run_stream( + self, + *, + assistant_id: str, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler]: + """Create a thread and stream the run back""" + ... + + @overload + def create_and_run_stream( + self, + *, + assistant_id: str, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + event_handler: AssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandlerT]: + """Create a thread and stream the run back""" + ... + + def create_and_run_stream( + self, + *, + assistant_id: str, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + event_handler: AssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]: + """Create a thread and stream the run back""" + extra_headers = { + "OpenAI-Beta": "assistants=v2", + "X-Stainless-Stream-Helper": "threads.create_and_run_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + make_request = partial( + self._post, + "/threads/runs", + body=maybe_transform( + { + "assistant_id": assistant_id, + "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, + "metadata": metadata, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "response_format": response_format, + "temperature": temperature, + "tool_choice": tool_choice, + "stream": True, + "thread": thread, + "tools": tools, + "tool_resources": tool_resources, + "truncation_strategy": truncation_strategy, + "top_p": top_p, + }, + thread_create_and_run_params.ThreadCreateAndRunParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Run, + stream=True, + stream_cls=Stream[AssistantStreamEvent], + ) + return AssistantStreamManager(make_request, event_handler=event_handler or AssistantEventHandler()) + + +class AsyncThreads(AsyncAPIResource): + """Build Assistants that can call models and use tools.""" + + @cached_property + def runs(self) -> AsyncRuns: + """Build Assistants that can call models and use tools.""" + return AsyncRuns(self._client) + + @cached_property + def messages(self) -> AsyncMessages: + """Build Assistants that can call models and use tools.""" + return AsyncMessages(self._client) + + @cached_property + def with_raw_response(self) -> AsyncThreadsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncThreadsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncThreadsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncThreadsWithStreamingResponse(self) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def create( + self, + *, + messages: Iterable[thread_create_params.Message] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + tool_resources: Optional[thread_create_params.ToolResources] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Thread: + """ + Create a thread. + + Args: + messages: A list of + [messages](https://developers.openai.com/api/docs/assistants/migration) to start + the thread with. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + tool_resources: A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + "/threads", + body=await async_maybe_transform( + { + "messages": messages, + "metadata": metadata, + "tool_resources": tool_resources, + }, + thread_create_params.ThreadCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Thread, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def retrieve( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Thread: + """ + Retrieves a thread. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._get( + path_template("/threads/{thread_id}", thread_id=thread_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Thread, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def update( + self, + thread_id: str, + *, + metadata: Optional[Metadata] | Omit = omit, + tool_resources: Optional[thread_update_params.ToolResources] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Thread: + """ + Modifies a thread. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + tool_resources: A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + path_template("/threads/{thread_id}", thread_id=thread_id), + body=await async_maybe_transform( + { + "metadata": metadata, + "tool_resources": tool_resources, + }, + thread_update_params.ThreadUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Thread, + ) + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def delete( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ThreadDeleted: + """ + Delete a thread. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._delete( + path_template("/threads/{thread_id}", thread_id=thread_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ThreadDeleted, + ) + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def create_and_run( + self, + *, + assistant_id: str, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run: + """ + Create a thread and run it in one request. + + Args: + assistant_id: The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + thread: Options to create a new thread. If no thread is provided when running a request, + an empty thread will be created. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the initial context window of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def create_and_run( + self, + *, + assistant_id: str, + stream: Literal[True], + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[AssistantStreamEvent]: + """ + Create a thread and run it in one request. + + Args: + assistant_id: The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + thread: Options to create a new thread. If no thread is provided when running a request, + an empty thread will be created. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the initial context window of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def create_and_run( + self, + *, + assistant_id: str, + stream: bool, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run | AsyncStream[AssistantStreamEvent]: + """ + Create a thread and run it in one request. + + Args: + assistant_id: The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + response_format: Specifies the format that the model must output. Compatible with + [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + thread: Options to create a new thread. If no thread is provided when running a request, + an empty thread will be created. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the initial context window of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + @required_args(["assistant_id"], ["assistant_id", "stream"]) + @typing_extensions.deprecated("The Assistants API is deprecated in favor of the Responses API") + async def create_and_run( + self, + *, + assistant_id: str, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Run | AsyncStream[AssistantStreamEvent]: + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + "/threads/runs", + body=await async_maybe_transform( + { + "assistant_id": assistant_id, + "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, + "metadata": metadata, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "response_format": response_format, + "stream": stream, + "temperature": temperature, + "thread": thread, + "tool_choice": tool_choice, + "tool_resources": tool_resources, + "tools": tools, + "top_p": top_p, + "truncation_strategy": truncation_strategy, + }, + thread_create_and_run_params.ThreadCreateAndRunParamsStreaming + if stream + else thread_create_and_run_params.ThreadCreateAndRunParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + synthesize_event_and_data=True, + ), + cast_to=Run, + stream=stream or False, + stream_cls=AsyncStream[AssistantStreamEvent], + ) + + async def create_and_run_poll( + self, + *, + assistant_id: str, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + poll_interval_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to create a thread, start a run and then poll for a terminal state. + More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + run = await self.create_and_run( # pyright: ignore[reportDeprecated] + assistant_id=assistant_id, + instructions=instructions, + max_completion_tokens=max_completion_tokens, + max_prompt_tokens=max_prompt_tokens, + metadata=metadata, + model=model, + parallel_tool_calls=parallel_tool_calls, + response_format=response_format, + temperature=temperature, + stream=False, + thread=thread, + tool_resources=tool_resources, + tool_choice=tool_choice, + truncation_strategy=truncation_strategy, + top_p=top_p, + tools=tools, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return await self.runs.poll( # pyright: ignore[reportDeprecated] + run.id, run.thread_id, extra_headers, extra_query, extra_body, timeout, poll_interval_ms + ) + + @overload + def create_and_run_stream( + self, + *, + assistant_id: str, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandler]: + """Create a thread and stream the run back""" + ... + + @overload + def create_and_run_stream( + self, + *, + assistant_id: str, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + event_handler: AsyncAssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]: + """Create a thread and stream the run back""" + ... + + def create_and_run_stream( + self, + *, + assistant_id: str, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + event_handler: AsyncAssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ( + AsyncAssistantStreamManager[AsyncAssistantEventHandler] + | AsyncAssistantStreamManager[AsyncAssistantEventHandlerT] + ): + """Create a thread and stream the run back""" + extra_headers = { + "OpenAI-Beta": "assistants=v2", + "X-Stainless-Stream-Helper": "threads.create_and_run_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + request = self._post( + "/threads/runs", + body=maybe_transform( + { + "assistant_id": assistant_id, + "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, + "metadata": metadata, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "response_format": response_format, + "temperature": temperature, + "tool_choice": tool_choice, + "stream": True, + "thread": thread, + "tools": tools, + "tool_resources": tool_resources, + "truncation_strategy": truncation_strategy, + "top_p": top_p, + }, + thread_create_and_run_params.ThreadCreateAndRunParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Run, + stream=True, + stream_cls=AsyncStream[AssistantStreamEvent], + ) + return AsyncAssistantStreamManager(request, event_handler=event_handler or AsyncAssistantEventHandler()) + + +class ThreadsWithRawResponse: + def __init__(self, threads: Threads) -> None: + self._threads = threads + + self.create = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + threads.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + threads.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + threads.update, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + threads.delete, # pyright: ignore[reportDeprecated], + ) + ) + self.create_and_run = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + threads.create_and_run, # pyright: ignore[reportDeprecated], + ) + ) + + @cached_property + def runs(self) -> RunsWithRawResponse: + """Build Assistants that can call models and use tools.""" + return RunsWithRawResponse(self._threads.runs) + + @cached_property + def messages(self) -> MessagesWithRawResponse: + """Build Assistants that can call models and use tools.""" + return MessagesWithRawResponse(self._threads.messages) + + +class AsyncThreadsWithRawResponse: + def __init__(self, threads: AsyncThreads) -> None: + self._threads = threads + + self.create = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + threads.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + threads.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + threads.update, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + threads.delete, # pyright: ignore[reportDeprecated], + ) + ) + self.create_and_run = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + threads.create_and_run, # pyright: ignore[reportDeprecated], + ) + ) + + @cached_property + def runs(self) -> AsyncRunsWithRawResponse: + """Build Assistants that can call models and use tools.""" + return AsyncRunsWithRawResponse(self._threads.runs) + + @cached_property + def messages(self) -> AsyncMessagesWithRawResponse: + """Build Assistants that can call models and use tools.""" + return AsyncMessagesWithRawResponse(self._threads.messages) + + +class ThreadsWithStreamingResponse: + def __init__(self, threads: Threads) -> None: + self._threads = threads + + self.create = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + threads.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + threads.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + threads.update, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + threads.delete, # pyright: ignore[reportDeprecated], + ) + ) + self.create_and_run = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + threads.create_and_run, # pyright: ignore[reportDeprecated], + ) + ) + + @cached_property + def runs(self) -> RunsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" + return RunsWithStreamingResponse(self._threads.runs) + + @cached_property + def messages(self) -> MessagesWithStreamingResponse: + """Build Assistants that can call models and use tools.""" + return MessagesWithStreamingResponse(self._threads.messages) + + +class AsyncThreadsWithStreamingResponse: + def __init__(self, threads: AsyncThreads) -> None: + self._threads = threads + + self.create = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + threads.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + threads.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.update = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + threads.update, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + threads.delete, # pyright: ignore[reportDeprecated], + ) + ) + self.create_and_run = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + threads.create_and_run, # pyright: ignore[reportDeprecated], + ) + ) + + @cached_property + def runs(self) -> AsyncRunsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" + return AsyncRunsWithStreamingResponse(self._threads.runs) + + @cached_property + def messages(self) -> AsyncMessagesWithStreamingResponse: + """Build Assistants that can call models and use tools.""" + return AsyncMessagesWithStreamingResponse(self._threads.messages) diff --git a/venv/lib/python3.12/site-packages/openai/resources/chat/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/chat/__init__.py new file mode 100644 index 0000000..2169f1b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/chat/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .chat import ( + Chat as Chat, + AsyncChat as AsyncChat, + ChatWithRawResponse as ChatWithRawResponse, + AsyncChatWithRawResponse as AsyncChatWithRawResponse, + ChatWithStreamingResponse as ChatWithStreamingResponse, + AsyncChatWithStreamingResponse as AsyncChatWithStreamingResponse, + ) + from .completions import ( + Completions as Completions, + AsyncCompletions as AsyncCompletions, + CompletionsWithRawResponse as CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse as AsyncCompletionsWithRawResponse, + CompletionsWithStreamingResponse as CompletionsWithStreamingResponse, + AsyncCompletionsWithStreamingResponse as AsyncCompletionsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Completions": (".completions", "Completions"), + "AsyncCompletions": (".completions", "AsyncCompletions"), + "CompletionsWithRawResponse": (".completions", "CompletionsWithRawResponse"), + "AsyncCompletionsWithRawResponse": (".completions", "AsyncCompletionsWithRawResponse"), + "CompletionsWithStreamingResponse": (".completions", "CompletionsWithStreamingResponse"), + "AsyncCompletionsWithStreamingResponse": (".completions", "AsyncCompletionsWithStreamingResponse"), + "Chat": (".chat", "Chat"), + "AsyncChat": (".chat", "AsyncChat"), + "ChatWithRawResponse": (".chat", "ChatWithRawResponse"), + "AsyncChatWithRawResponse": (".chat", "AsyncChatWithRawResponse"), + "ChatWithStreamingResponse": (".chat", "ChatWithStreamingResponse"), + "AsyncChatWithStreamingResponse": (".chat", "AsyncChatWithStreamingResponse"), + } + _SUBMODULES = { + "chat", + "completions", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Completions", + "AsyncCompletions", + "CompletionsWithRawResponse", + "AsyncCompletionsWithRawResponse", + "CompletionsWithStreamingResponse", + "AsyncCompletionsWithStreamingResponse", + "Chat", + "AsyncChat", + "ChatWithRawResponse", + "AsyncChatWithRawResponse", + "ChatWithStreamingResponse", + "AsyncChatWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/chat/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/chat/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..fec9141 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/chat/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/chat/__pycache__/chat.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/chat/__pycache__/chat.cpython-312.pyc new file mode 100644 index 0000000..e05f7d2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/chat/__pycache__/chat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/chat/chat.py b/venv/lib/python3.12/site-packages/openai/resources/chat/chat.py new file mode 100644 index 0000000..5e970ae --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/chat/chat.py @@ -0,0 +1,120 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .completions.completions import ( + Completions, + AsyncCompletions, + CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse, + CompletionsWithStreamingResponse, + AsyncCompletionsWithStreamingResponse, +) + +__all__ = ["Chat", "AsyncChat"] + + +class Chat(SyncAPIResource): + @cached_property + def completions(self) -> Completions: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + return Completions(self._client) + + @cached_property + def with_raw_response(self) -> ChatWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ChatWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChatWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ChatWithStreamingResponse(self) + + +class AsyncChat(AsyncAPIResource): + @cached_property + def completions(self) -> AsyncCompletions: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + return AsyncCompletions(self._client) + + @cached_property + def with_raw_response(self) -> AsyncChatWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncChatWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChatWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncChatWithStreamingResponse(self) + + +class ChatWithRawResponse: + def __init__(self, chat: Chat) -> None: + self._chat = chat + + @cached_property + def completions(self) -> CompletionsWithRawResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + return CompletionsWithRawResponse(self._chat.completions) + + +class AsyncChatWithRawResponse: + def __init__(self, chat: AsyncChat) -> None: + self._chat = chat + + @cached_property + def completions(self) -> AsyncCompletionsWithRawResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + return AsyncCompletionsWithRawResponse(self._chat.completions) + + +class ChatWithStreamingResponse: + def __init__(self, chat: Chat) -> None: + self._chat = chat + + @cached_property + def completions(self) -> CompletionsWithStreamingResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + return CompletionsWithStreamingResponse(self._chat.completions) + + +class AsyncChatWithStreamingResponse: + def __init__(self, chat: AsyncChat) -> None: + self._chat = chat + + @cached_property + def completions(self) -> AsyncCompletionsWithStreamingResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + return AsyncCompletionsWithStreamingResponse(self._chat.completions) diff --git a/venv/lib/python3.12/site-packages/openai/resources/chat/completions/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/chat/completions/__init__.py new file mode 100644 index 0000000..85b63ce --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/chat/completions/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .messages import ( + Messages as Messages, + AsyncMessages as AsyncMessages, + MessagesWithRawResponse as MessagesWithRawResponse, + AsyncMessagesWithRawResponse as AsyncMessagesWithRawResponse, + MessagesWithStreamingResponse as MessagesWithStreamingResponse, + AsyncMessagesWithStreamingResponse as AsyncMessagesWithStreamingResponse, + ) + from .completions import ( + Completions as Completions, + AsyncCompletions as AsyncCompletions, + CompletionsWithRawResponse as CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse as AsyncCompletionsWithRawResponse, + CompletionsWithStreamingResponse as CompletionsWithStreamingResponse, + AsyncCompletionsWithStreamingResponse as AsyncCompletionsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Messages": (".messages", "Messages"), + "AsyncMessages": (".messages", "AsyncMessages"), + "MessagesWithRawResponse": (".messages", "MessagesWithRawResponse"), + "AsyncMessagesWithRawResponse": (".messages", "AsyncMessagesWithRawResponse"), + "MessagesWithStreamingResponse": (".messages", "MessagesWithStreamingResponse"), + "AsyncMessagesWithStreamingResponse": (".messages", "AsyncMessagesWithStreamingResponse"), + "Completions": (".completions", "Completions"), + "AsyncCompletions": (".completions", "AsyncCompletions"), + "CompletionsWithRawResponse": (".completions", "CompletionsWithRawResponse"), + "AsyncCompletionsWithRawResponse": (".completions", "AsyncCompletionsWithRawResponse"), + "CompletionsWithStreamingResponse": (".completions", "CompletionsWithStreamingResponse"), + "AsyncCompletionsWithStreamingResponse": (".completions", "AsyncCompletionsWithStreamingResponse"), + } + _SUBMODULES = { + "completions", + "messages", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Messages", + "AsyncMessages", + "MessagesWithRawResponse", + "AsyncMessagesWithRawResponse", + "MessagesWithStreamingResponse", + "AsyncMessagesWithStreamingResponse", + "Completions", + "AsyncCompletions", + "CompletionsWithRawResponse", + "AsyncCompletionsWithRawResponse", + "CompletionsWithStreamingResponse", + "AsyncCompletionsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/chat/completions/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/chat/completions/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..388c08e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/chat/completions/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/chat/completions/__pycache__/completions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/chat/completions/__pycache__/completions.cpython-312.pyc new file mode 100644 index 0000000..e7ebc4b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/chat/completions/__pycache__/completions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/chat/completions/__pycache__/messages.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/chat/completions/__pycache__/messages.cpython-312.pyc new file mode 100644 index 0000000..4ef530a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/chat/completions/__pycache__/messages.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/chat/completions/completions.py b/venv/lib/python3.12/site-packages/openai/resources/chat/completions/completions.py new file mode 100644 index 0000000..0920d50 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/chat/completions/completions.py @@ -0,0 +1,3456 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import inspect +from typing import Dict, List, Type, Union, Iterable, Optional, cast +from functools import partial +from typing_extensions import Literal, overload + +import httpx2 +import pydantic + +from .... import _legacy_response +from .messages import ( + Messages, + AsyncMessages, + MessagesWithRawResponse, + AsyncMessagesWithRawResponse, + MessagesWithStreamingResponse, + AsyncMessagesWithStreamingResponse, +) +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import path_template, required_args, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._streaming import Stream, AsyncStream +from ....pagination import SyncCursorPage, AsyncCursorPage +from ....types.chat import ( + ChatCompletionAudioParam, + completion_list_params, + completion_create_params, + completion_update_params, +) +from ...._base_client import AsyncPaginator, make_request_options +from ....lib._parsing import ( + ResponseFormatT, + validate_input_tools as _validate_input_tools, + parse_chat_completion as _parse_chat_completion, + materialize_input_tools as _materialize_input_tools, + type_to_response_format_param as _type_to_response_format, +) +from ....lib.streaming.chat import ChatCompletionStreamManager, AsyncChatCompletionStreamManager +from ....types.shared.chat_model import ChatModel +from ....types.chat.chat_completion import ChatCompletion +from ....types.shared_params.metadata import Metadata +from ....types.shared.reasoning_effort import ReasoningEffort +from ....types.chat.chat_completion_chunk import ChatCompletionChunk +from ....types.chat.parsed_chat_completion import ParsedChatCompletion +from ....types.chat.chat_completion_deleted import ChatCompletionDeleted +from ....types.chat.chat_completion_audio_param import ChatCompletionAudioParam +from ....types.chat.chat_completion_message_param import ChatCompletionMessageParam +from ....types.chat.chat_completion_tool_union_param import ChatCompletionToolUnionParam +from ....types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam +from ....types.chat.chat_completion_prediction_content_param import ChatCompletionPredictionContentParam +from ....types.chat.chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam + +__all__ = ["Completions", "AsyncCompletions"] + + +class Completions(SyncAPIResource): + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + + @cached_property + def messages(self) -> Messages: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + return Messages(self._client) + + @cached_property + def with_raw_response(self) -> CompletionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CompletionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CompletionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CompletionsWithStreamingResponse(self) + + def parse( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + response_format: type[ResponseFormatT] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ParsedChatCompletion[ResponseFormatT]: + """Wrapper over the `client.chat.completions.create()` method that provides richer integrations with Python specific types + & returns a `ParsedChatCompletion` object, which is a subclass of the standard `ChatCompletion` class. + + You can pass a pydantic model to this method and it will automatically convert the model + into a JSON schema, send it to the API and parse the response content back into the given model. + + This method will also automatically parse `function` tool calls if: + - You use the `openai.pydantic_function_tool()` helper method + - You mark your tool schema with `"strict": True` + + Example usage: + ```py + from pydantic import BaseModel + from openai import OpenAI + + + class Step(BaseModel): + explanation: str + output: str + + + class MathResponse(BaseModel): + steps: List[Step] + final_answer: str + + + client = OpenAI() + completion = client.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + {"role": "system", "content": "You are a helpful math tutor."}, + {"role": "user", "content": "solve 8x + 31 = 2"}, + ], + response_format=MathResponse, + ) + + message = completion.choices[0].message + if message.parsed: + print(message.parsed.steps) + print("answer: ", message.parsed.final_answer) + ``` + """ + chat_completion_tools = _validate_input_tools(tools) + + extra_headers = { + "X-Stainless-Helper-Method": "chat.completions.parse", + **(extra_headers or {}), + } + + def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseFormatT]: + return _parse_chat_completion( + response_format=response_format, + chat_completion=raw_completion, + input_tools=chat_completion_tools, + ) + + return self._post( + "/chat/completions", + body=maybe_transform( + { + "messages": messages, + "model": model, + "audio": audio, + "frequency_penalty": frequency_penalty, + "function_call": function_call, + "functions": functions, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_completion_tokens": max_completion_tokens, + "max_tokens": max_tokens, + "metadata": metadata, + "modalities": modalities, + "moderation": moderation, + "n": n, + "parallel_tool_calls": parallel_tool_calls, + "prediction": prediction, + "presence_penalty": presence_penalty, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning_effort": reasoning_effort, + "response_format": _type_to_response_format(response_format), + "safety_identifier": safety_identifier, + "seed": seed, + "service_tier": service_tier, + "stop": stop, + "store": store, + "stream": False, + "stream_options": stream_options, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": chat_completion_tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "user": user, + "verbosity": verbosity, + "web_search_options": web_search_options, + }, + completion_create_params.CompletionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=parser, + security={"bearer_auth": True}, + ), + # we turn the `ChatCompletion` instance into a `ParsedChatCompletion` + # in the `parser` function above + cast_to=cast(Type[ParsedChatCompletion[ResponseFormatT]], ChatCompletion), + stream=False, + ) + + @overload + def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatCompletion: + """ + **Starting a new project?** We recommend trying + [Responses](https://developers.openai.com/api/reference/resources/responses) to + take advantage of the latest OpenAI platform features. Compare + [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses). + + --- + + Creates a model response for the given chat conversation. Learn more in the + [text generation](https://developers.openai.com/api/docs/guides/text), + [vision](https://developers.openai.com/api/docs/guides/images-vision), and + [audio](https://developers.openai.com/api/docs/guides/audio) guides. + + Parameter support can differ depending on the model used to generate the + response, particularly for newer reasoning models. Parameters that are only + supported for reasoning models are noted below. For the current state of + unsupported parameters in reasoning models, + [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning). + + Returns a chat completion object, or a streamed sequence of chat completion + chunk objects if the request is streamed. + + Args: + messages: A list of messages comprising the conversation so far. Depending on the + [model](https://developers.openai.com/api/docs/models) you use, different + message types (modalities) are supported, like + [text](https://developers.openai.com/api/docs/guides/text), + [images](https://developers.openai.com/api/docs/guides/images-vision), and + [audio](https://developers.openai.com/api/docs/guides/audio). + + model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI + offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + audio: Parameters for audio output. Required when audio output is requested with + `modalities: ["audio"]`. + [Learn more](https://developers.openai.com/api/docs/guides/audio). + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + function_call: Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a + function. + + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + + functions: Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the chat completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. + + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o-series models](https://developers.openai.com/api/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + modalities: Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://developers.openai.com/api/docs/guides/audio). To + request that this model generate both text and audio responses, you can use: + + `["text", "audio"]` + + moderation: Configuration for running moderation on the request input and generated output. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + prediction: Configuration for a + [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs), + which can greatly improve response times when large parts of the model response + are known ahead of time. This is most common when you are regenerating a file + with only minor changes to most of the content. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: An object specifying the format that the model must output. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + seed: This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + store: Whether or not to store the output of this chat completion request for use in + our + [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model) + or [evals](https://developers.openai.com/api/docs/guides/evals) products. + + Supports text and image inputs. Note: image inputs over 8MB will be dropped. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events) + for more information, along with the + [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. You can provide either + [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) + or + [function tools](https://developers.openai.com/api/docs/guides/function-calling). + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. The + default is `medium`. + + web_search_options: This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + stream: Literal[True], + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[ChatCompletionChunk]: + """ + **Starting a new project?** We recommend trying + [Responses](https://developers.openai.com/api/reference/resources/responses) to + take advantage of the latest OpenAI platform features. Compare + [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses). + + --- + + Creates a model response for the given chat conversation. Learn more in the + [text generation](https://developers.openai.com/api/docs/guides/text), + [vision](https://developers.openai.com/api/docs/guides/images-vision), and + [audio](https://developers.openai.com/api/docs/guides/audio) guides. + + Parameter support can differ depending on the model used to generate the + response, particularly for newer reasoning models. Parameters that are only + supported for reasoning models are noted below. For the current state of + unsupported parameters in reasoning models, + [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning). + + Returns a chat completion object, or a streamed sequence of chat completion + chunk objects if the request is streamed. + + Args: + messages: A list of messages comprising the conversation so far. Depending on the + [model](https://developers.openai.com/api/docs/models) you use, different + message types (modalities) are supported, like + [text](https://developers.openai.com/api/docs/guides/text), + [images](https://developers.openai.com/api/docs/guides/images-vision), and + [audio](https://developers.openai.com/api/docs/guides/audio). + + model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI + offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events) + for more information, along with the + [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. + + audio: Parameters for audio output. Required when audio output is requested with + `modalities: ["audio"]`. + [Learn more](https://developers.openai.com/api/docs/guides/audio). + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + function_call: Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a + function. + + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + + functions: Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the chat completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. + + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o-series models](https://developers.openai.com/api/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + modalities: Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://developers.openai.com/api/docs/guides/audio). To + request that this model generate both text and audio responses, you can use: + + `["text", "audio"]` + + moderation: Configuration for running moderation on the request input and generated output. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + prediction: Configuration for a + [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs), + which can greatly improve response times when large parts of the model response + are known ahead of time. This is most common when you are regenerating a file + with only minor changes to most of the content. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: An object specifying the format that the model must output. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + seed: This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + store: Whether or not to store the output of this chat completion request for use in + our + [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model) + or [evals](https://developers.openai.com/api/docs/guides/evals) products. + + Supports text and image inputs. Note: image inputs over 8MB will be dropped. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. You can provide either + [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) + or + [function tools](https://developers.openai.com/api/docs/guides/function-calling). + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. The + default is `medium`. + + web_search_options: This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + stream: bool, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatCompletion | Stream[ChatCompletionChunk]: + """ + **Starting a new project?** We recommend trying + [Responses](https://developers.openai.com/api/reference/resources/responses) to + take advantage of the latest OpenAI platform features. Compare + [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses). + + --- + + Creates a model response for the given chat conversation. Learn more in the + [text generation](https://developers.openai.com/api/docs/guides/text), + [vision](https://developers.openai.com/api/docs/guides/images-vision), and + [audio](https://developers.openai.com/api/docs/guides/audio) guides. + + Parameter support can differ depending on the model used to generate the + response, particularly for newer reasoning models. Parameters that are only + supported for reasoning models are noted below. For the current state of + unsupported parameters in reasoning models, + [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning). + + Returns a chat completion object, or a streamed sequence of chat completion + chunk objects if the request is streamed. + + Args: + messages: A list of messages comprising the conversation so far. Depending on the + [model](https://developers.openai.com/api/docs/models) you use, different + message types (modalities) are supported, like + [text](https://developers.openai.com/api/docs/guides/text), + [images](https://developers.openai.com/api/docs/guides/images-vision), and + [audio](https://developers.openai.com/api/docs/guides/audio). + + model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI + offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events) + for more information, along with the + [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. + + audio: Parameters for audio output. Required when audio output is requested with + `modalities: ["audio"]`. + [Learn more](https://developers.openai.com/api/docs/guides/audio). + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + function_call: Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a + function. + + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + + functions: Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the chat completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. + + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o-series models](https://developers.openai.com/api/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + modalities: Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://developers.openai.com/api/docs/guides/audio). To + request that this model generate both text and audio responses, you can use: + + `["text", "audio"]` + + moderation: Configuration for running moderation on the request input and generated output. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + prediction: Configuration for a + [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs), + which can greatly improve response times when large parts of the model response + are known ahead of time. This is most common when you are regenerating a file + with only minor changes to most of the content. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: An object specifying the format that the model must output. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + seed: This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + store: Whether or not to store the output of this chat completion request for use in + our + [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model) + or [evals](https://developers.openai.com/api/docs/guides/evals) products. + + Supports text and image inputs. Note: image inputs over 8MB will be dropped. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. You can provide either + [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) + or + [function tools](https://developers.openai.com/api/docs/guides/function-calling). + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. The + default is `medium`. + + web_search_options: This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["messages", "model"], ["messages", "model", "stream"]) + def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatCompletion | Stream[ChatCompletionChunk]: + validate_response_format(response_format) + return self._post( + "/chat/completions", + body=maybe_transform( + { + "messages": messages, + "model": model, + "audio": audio, + "frequency_penalty": frequency_penalty, + "function_call": function_call, + "functions": functions, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_completion_tokens": max_completion_tokens, + "max_tokens": max_tokens, + "metadata": metadata, + "modalities": modalities, + "moderation": moderation, + "n": n, + "parallel_tool_calls": parallel_tool_calls, + "prediction": prediction, + "presence_penalty": presence_penalty, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning_effort": reasoning_effort, + "response_format": response_format, + "safety_identifier": safety_identifier, + "seed": seed, + "service_tier": service_tier, + "stop": stop, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "user": user, + "verbosity": verbosity, + "web_search_options": web_search_options, + }, + completion_create_params.CompletionCreateParamsStreaming + if stream + else completion_create_params.CompletionCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatCompletion, + stream=stream or False, + stream_cls=Stream[ChatCompletionChunk], + ) + + def retrieve( + self, + completion_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatCompletion: + """Get a stored chat completion. + + Only Chat Completions that have been created with + the `store` parameter set to `true` will be returned. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return self._get( + path_template("/chat/completions/{completion_id}", completion_id=completion_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatCompletion, + ) + + def update( + self, + completion_id: str, + *, + metadata: Optional[Metadata], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatCompletion: + """Modify a stored chat completion. + + Only Chat Completions that have been created + with the `store` parameter set to `true` can be modified. Currently, the only + supported modification is to update the `metadata` field. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return self._post( + path_template("/chat/completions/{completion_id}", completion_id=completion_id), + body=maybe_transform({"metadata": metadata}, completion_update_params.CompletionUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatCompletion, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: str | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[ChatCompletion]: + """List stored Chat Completions. + + Only Chat Completions that have been stored with + the `store` parameter set to `true` will be returned. + + Args: + after: Identifier for the last chat completion from the previous pagination request. + + limit: Number of Chat Completions to retrieve. + + metadata: + A list of metadata keys to filter the Chat Completions by. Example: + + `metadata[key1]=value1&metadata[key2]=value2` + + model: The model used to generate the Chat Completions. + + order: Sort order for Chat Completions by timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `asc`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/chat/completions", + page=SyncCursorPage[ChatCompletion], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "metadata": metadata, + "model": model, + "order": order, + }, + completion_list_params.CompletionListParams, + ), + security={"bearer_auth": True}, + ), + model=ChatCompletion, + ) + + def delete( + self, + completion_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatCompletionDeleted: + """Delete a stored chat completion. + + Only Chat Completions that have been created + with the `store` parameter set to `true` can be deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return self._delete( + path_template("/chat/completions/{completion_id}", completion_id=completion_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatCompletionDeleted, + ) + + def stream( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + response_format: completion_create_params.ResponseFormat | type[ResponseFormatT] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatCompletionStreamManager[ResponseFormatT]: + """Wrapper over the `client.chat.completions.create(stream=True)` method that provides a more granular event API + and automatic accumulation of each delta. + + This also supports all of the parsing utilities that `.parse()` does. + + Unlike `.create(stream=True)`, the `.stream()` method requires usage within a context manager to prevent accidental leakage of the response: + + ```py + with client.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[...], + ) as stream: + for event in stream: + if event.type == "content.delta": + print(event.delta, flush=True, end="") + ``` + + When the context manager is entered, a `ChatCompletionStream` instance is returned which, like `.create(stream=True)` is an iterator. The full list of events that are yielded by the iterator are outlined in [these docs](https://github.com/openai/openai-python/blob/main/helpers.md#chat-completions-events). + + When the context manager exits, the response will be closed, however the `stream` instance is still available outside + the context manager. + """ + chat_completion_tools = _materialize_input_tools(tools) + + extra_headers = { + "X-Stainless-Helper-Method": "chat.completions.stream", + **(extra_headers or {}), + } + + api_request: partial[Stream[ChatCompletionChunk]] = partial( + self.create, + messages=messages, + model=model, + audio=audio, + stream=True, + response_format=_type_to_response_format(response_format), + frequency_penalty=frequency_penalty, + function_call=function_call, + functions=functions, + logit_bias=logit_bias, + logprobs=logprobs, + max_completion_tokens=max_completion_tokens, + max_tokens=max_tokens, + metadata=metadata, + modalities=modalities, + moderation=moderation, + n=n, + parallel_tool_calls=parallel_tool_calls, + prediction=prediction, + presence_penalty=presence_penalty, + prompt_cache_key=prompt_cache_key, + prompt_cache_options=prompt_cache_options, + prompt_cache_retention=prompt_cache_retention, + reasoning_effort=reasoning_effort, + safety_identifier=safety_identifier, + seed=seed, + service_tier=service_tier, + store=store, + stop=stop, + stream_options=stream_options, + temperature=temperature, + tool_choice=tool_choice, + tools=chat_completion_tools, + top_logprobs=top_logprobs, + top_p=top_p, + user=user, + verbosity=verbosity, + web_search_options=web_search_options, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return ChatCompletionStreamManager( + api_request, + response_format=response_format, + input_tools=chat_completion_tools, + ) + + +class AsyncCompletions(AsyncAPIResource): + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + + @cached_property + def messages(self) -> AsyncMessages: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + return AsyncMessages(self._client) + + @cached_property + def with_raw_response(self) -> AsyncCompletionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCompletionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCompletionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCompletionsWithStreamingResponse(self) + + async def parse( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + response_format: type[ResponseFormatT] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ParsedChatCompletion[ResponseFormatT]: + """Wrapper over the `client.chat.completions.create()` method that provides richer integrations with Python specific types + & returns a `ParsedChatCompletion` object, which is a subclass of the standard `ChatCompletion` class. + + You can pass a pydantic model to this method and it will automatically convert the model + into a JSON schema, send it to the API and parse the response content back into the given model. + + This method will also automatically parse `function` tool calls if: + - You use the `openai.pydantic_function_tool()` helper method + - You mark your tool schema with `"strict": True` + + Example usage: + ```py + from pydantic import BaseModel + from openai import AsyncOpenAI + + + class Step(BaseModel): + explanation: str + output: str + + + class MathResponse(BaseModel): + steps: List[Step] + final_answer: str + + + client = AsyncOpenAI() + completion = await client.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + {"role": "system", "content": "You are a helpful math tutor."}, + {"role": "user", "content": "solve 8x + 31 = 2"}, + ], + response_format=MathResponse, + ) + + message = completion.choices[0].message + if message.parsed: + print(message.parsed.steps) + print("answer: ", message.parsed.final_answer) + ``` + """ + chat_completion_tools = _validate_input_tools(tools) + + extra_headers = { + "X-Stainless-Helper-Method": "chat.completions.parse", + **(extra_headers or {}), + } + + def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseFormatT]: + return _parse_chat_completion( + response_format=response_format, + chat_completion=raw_completion, + input_tools=chat_completion_tools, + ) + + return await self._post( + "/chat/completions", + body=await async_maybe_transform( + { + "messages": messages, + "model": model, + "audio": audio, + "frequency_penalty": frequency_penalty, + "function_call": function_call, + "functions": functions, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_completion_tokens": max_completion_tokens, + "max_tokens": max_tokens, + "metadata": metadata, + "modalities": modalities, + "moderation": moderation, + "n": n, + "parallel_tool_calls": parallel_tool_calls, + "prediction": prediction, + "presence_penalty": presence_penalty, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning_effort": reasoning_effort, + "response_format": _type_to_response_format(response_format), + "safety_identifier": safety_identifier, + "seed": seed, + "service_tier": service_tier, + "store": store, + "stop": stop, + "stream": False, + "stream_options": stream_options, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": chat_completion_tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "user": user, + "verbosity": verbosity, + "web_search_options": web_search_options, + }, + completion_create_params.CompletionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=parser, + security={"bearer_auth": True}, + ), + # we turn the `ChatCompletion` instance into a `ParsedChatCompletion` + # in the `parser` function above + cast_to=cast(Type[ParsedChatCompletion[ResponseFormatT]], ChatCompletion), + stream=False, + ) + + @overload + async def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatCompletion: + """ + **Starting a new project?** We recommend trying + [Responses](https://developers.openai.com/api/reference/resources/responses) to + take advantage of the latest OpenAI platform features. Compare + [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses). + + --- + + Creates a model response for the given chat conversation. Learn more in the + [text generation](https://developers.openai.com/api/docs/guides/text), + [vision](https://developers.openai.com/api/docs/guides/images-vision), and + [audio](https://developers.openai.com/api/docs/guides/audio) guides. + + Parameter support can differ depending on the model used to generate the + response, particularly for newer reasoning models. Parameters that are only + supported for reasoning models are noted below. For the current state of + unsupported parameters in reasoning models, + [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning). + + Returns a chat completion object, or a streamed sequence of chat completion + chunk objects if the request is streamed. + + Args: + messages: A list of messages comprising the conversation so far. Depending on the + [model](https://developers.openai.com/api/docs/models) you use, different + message types (modalities) are supported, like + [text](https://developers.openai.com/api/docs/guides/text), + [images](https://developers.openai.com/api/docs/guides/images-vision), and + [audio](https://developers.openai.com/api/docs/guides/audio). + + model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI + offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + audio: Parameters for audio output. Required when audio output is requested with + `modalities: ["audio"]`. + [Learn more](https://developers.openai.com/api/docs/guides/audio). + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + function_call: Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a + function. + + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + + functions: Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the chat completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. + + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o-series models](https://developers.openai.com/api/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + modalities: Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://developers.openai.com/api/docs/guides/audio). To + request that this model generate both text and audio responses, you can use: + + `["text", "audio"]` + + moderation: Configuration for running moderation on the request input and generated output. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + prediction: Configuration for a + [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs), + which can greatly improve response times when large parts of the model response + are known ahead of time. This is most common when you are regenerating a file + with only minor changes to most of the content. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: An object specifying the format that the model must output. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + seed: This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + store: Whether or not to store the output of this chat completion request for use in + our + [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model) + or [evals](https://developers.openai.com/api/docs/guides/evals) products. + + Supports text and image inputs. Note: image inputs over 8MB will be dropped. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events) + for more information, along with the + [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. You can provide either + [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) + or + [function tools](https://developers.openai.com/api/docs/guides/function-calling). + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. The + default is `medium`. + + web_search_options: This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + stream: Literal[True], + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[ChatCompletionChunk]: + """ + **Starting a new project?** We recommend trying + [Responses](https://developers.openai.com/api/reference/resources/responses) to + take advantage of the latest OpenAI platform features. Compare + [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses). + + --- + + Creates a model response for the given chat conversation. Learn more in the + [text generation](https://developers.openai.com/api/docs/guides/text), + [vision](https://developers.openai.com/api/docs/guides/images-vision), and + [audio](https://developers.openai.com/api/docs/guides/audio) guides. + + Parameter support can differ depending on the model used to generate the + response, particularly for newer reasoning models. Parameters that are only + supported for reasoning models are noted below. For the current state of + unsupported parameters in reasoning models, + [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning). + + Returns a chat completion object, or a streamed sequence of chat completion + chunk objects if the request is streamed. + + Args: + messages: A list of messages comprising the conversation so far. Depending on the + [model](https://developers.openai.com/api/docs/models) you use, different + message types (modalities) are supported, like + [text](https://developers.openai.com/api/docs/guides/text), + [images](https://developers.openai.com/api/docs/guides/images-vision), and + [audio](https://developers.openai.com/api/docs/guides/audio). + + model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI + offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events) + for more information, along with the + [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. + + audio: Parameters for audio output. Required when audio output is requested with + `modalities: ["audio"]`. + [Learn more](https://developers.openai.com/api/docs/guides/audio). + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + function_call: Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a + function. + + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + + functions: Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the chat completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. + + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o-series models](https://developers.openai.com/api/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + modalities: Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://developers.openai.com/api/docs/guides/audio). To + request that this model generate both text and audio responses, you can use: + + `["text", "audio"]` + + moderation: Configuration for running moderation on the request input and generated output. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + prediction: Configuration for a + [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs), + which can greatly improve response times when large parts of the model response + are known ahead of time. This is most common when you are regenerating a file + with only minor changes to most of the content. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: An object specifying the format that the model must output. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + seed: This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + store: Whether or not to store the output of this chat completion request for use in + our + [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model) + or [evals](https://developers.openai.com/api/docs/guides/evals) products. + + Supports text and image inputs. Note: image inputs over 8MB will be dropped. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. You can provide either + [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) + or + [function tools](https://developers.openai.com/api/docs/guides/function-calling). + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. The + default is `medium`. + + web_search_options: This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + stream: bool, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatCompletion | AsyncStream[ChatCompletionChunk]: + """ + **Starting a new project?** We recommend trying + [Responses](https://developers.openai.com/api/reference/resources/responses) to + take advantage of the latest OpenAI platform features. Compare + [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses). + + --- + + Creates a model response for the given chat conversation. Learn more in the + [text generation](https://developers.openai.com/api/docs/guides/text), + [vision](https://developers.openai.com/api/docs/guides/images-vision), and + [audio](https://developers.openai.com/api/docs/guides/audio) guides. + + Parameter support can differ depending on the model used to generate the + response, particularly for newer reasoning models. Parameters that are only + supported for reasoning models are noted below. For the current state of + unsupported parameters in reasoning models, + [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning). + + Returns a chat completion object, or a streamed sequence of chat completion + chunk objects if the request is streamed. + + Args: + messages: A list of messages comprising the conversation so far. Depending on the + [model](https://developers.openai.com/api/docs/models) you use, different + message types (modalities) are supported, like + [text](https://developers.openai.com/api/docs/guides/text), + [images](https://developers.openai.com/api/docs/guides/images-vision), and + [audio](https://developers.openai.com/api/docs/guides/audio). + + model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI + offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events) + for more information, along with the + [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. + + audio: Parameters for audio output. Required when audio output is requested with + `modalities: ["audio"]`. + [Learn more](https://developers.openai.com/api/docs/guides/audio). + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + function_call: Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a + function. + + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + + functions: Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. + + max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the chat completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. + + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o-series models](https://developers.openai.com/api/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + modalities: Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://developers.openai.com/api/docs/guides/audio). To + request that this model generate both text and audio responses, you can use: + + `["text", "audio"]` + + moderation: Configuration for running moderation on the request input and generated output. + + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. + + parallel_tool_calls: Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + + prediction: Configuration for a + [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs), + which can greatly improve response times when large parts of the model response + are known ahead of time. This is most common when you are regenerating a file + with only minor changes to most of the content. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + + response_format: An object specifying the format that the model must output. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + seed: This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + store: Whether or not to store the output of this chat completion request for use in + our + [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model) + or [evals](https://developers.openai.com/api/docs/guides/evals) products. + + Supports text and image inputs. Note: image inputs over 8MB will be dropped. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + + tools: A list of tools the model may call. You can provide either + [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) + or + [function tools](https://developers.openai.com/api/docs/guides/function-calling). + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + `logprobs` must be set to `true` if this parameter is used. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. The + default is `medium`. + + web_search_options: This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["messages", "model"], ["messages", "model", "stream"]) + async def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatCompletion | AsyncStream[ChatCompletionChunk]: + validate_response_format(response_format) + return await self._post( + "/chat/completions", + body=await async_maybe_transform( + { + "messages": messages, + "model": model, + "audio": audio, + "frequency_penalty": frequency_penalty, + "function_call": function_call, + "functions": functions, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_completion_tokens": max_completion_tokens, + "max_tokens": max_tokens, + "metadata": metadata, + "modalities": modalities, + "moderation": moderation, + "n": n, + "parallel_tool_calls": parallel_tool_calls, + "prediction": prediction, + "presence_penalty": presence_penalty, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning_effort": reasoning_effort, + "response_format": response_format, + "safety_identifier": safety_identifier, + "seed": seed, + "service_tier": service_tier, + "stop": stop, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "user": user, + "verbosity": verbosity, + "web_search_options": web_search_options, + }, + completion_create_params.CompletionCreateParamsStreaming + if stream + else completion_create_params.CompletionCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatCompletion, + stream=stream or False, + stream_cls=AsyncStream[ChatCompletionChunk], + ) + + async def retrieve( + self, + completion_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatCompletion: + """Get a stored chat completion. + + Only Chat Completions that have been created with + the `store` parameter set to `true` will be returned. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return await self._get( + path_template("/chat/completions/{completion_id}", completion_id=completion_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatCompletion, + ) + + async def update( + self, + completion_id: str, + *, + metadata: Optional[Metadata], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatCompletion: + """Modify a stored chat completion. + + Only Chat Completions that have been created + with the `store` parameter set to `true` can be modified. Currently, the only + supported modification is to update the `metadata` field. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return await self._post( + path_template("/chat/completions/{completion_id}", completion_id=completion_id), + body=await async_maybe_transform({"metadata": metadata}, completion_update_params.CompletionUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatCompletion, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: str | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ChatCompletion, AsyncCursorPage[ChatCompletion]]: + """List stored Chat Completions. + + Only Chat Completions that have been stored with + the `store` parameter set to `true` will be returned. + + Args: + after: Identifier for the last chat completion from the previous pagination request. + + limit: Number of Chat Completions to retrieve. + + metadata: + A list of metadata keys to filter the Chat Completions by. Example: + + `metadata[key1]=value1&metadata[key2]=value2` + + model: The model used to generate the Chat Completions. + + order: Sort order for Chat Completions by timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `asc`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/chat/completions", + page=AsyncCursorPage[ChatCompletion], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "metadata": metadata, + "model": model, + "order": order, + }, + completion_list_params.CompletionListParams, + ), + security={"bearer_auth": True}, + ), + model=ChatCompletion, + ) + + async def delete( + self, + completion_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ChatCompletionDeleted: + """Delete a stored chat completion. + + Only Chat Completions that have been created + with the `store` parameter set to `true` can be deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return await self._delete( + path_template("/chat/completions/{completion_id}", completion_id=completion_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ChatCompletionDeleted, + ) + + def stream( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + response_format: completion_create_params.ResponseFormat | type[ResponseFormatT] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncChatCompletionStreamManager[ResponseFormatT]: + """Wrapper over the `client.chat.completions.create(stream=True)` method that provides a more granular event API + and automatic accumulation of each delta. + + This also supports all of the parsing utilities that `.parse()` does. + + Unlike `.create(stream=True)`, the `.stream()` method requires usage within a context manager to prevent accidental leakage of the response: + + ```py + async with client.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[...], + ) as stream: + async for event in stream: + if event.type == "content.delta": + print(event.delta, flush=True, end="") + ``` + + When the context manager is entered, an `AsyncChatCompletionStream` instance is returned which, like `.create(stream=True)` is an async iterator. The full list of events that are yielded by the iterator are outlined in [these docs](https://github.com/openai/openai-python/blob/main/helpers.md#chat-completions-events). + + When the context manager exits, the response will be closed, however the `stream` instance is still available outside + the context manager. + """ + chat_completion_tools = _materialize_input_tools(tools) + + extra_headers = { + "X-Stainless-Helper-Method": "chat.completions.stream", + **(extra_headers or {}), + } + + api_request = self.create( + messages=messages, + model=model, + audio=audio, + stream=True, + response_format=_type_to_response_format(response_format), + frequency_penalty=frequency_penalty, + function_call=function_call, + functions=functions, + logit_bias=logit_bias, + logprobs=logprobs, + max_completion_tokens=max_completion_tokens, + max_tokens=max_tokens, + metadata=metadata, + modalities=modalities, + moderation=moderation, + n=n, + parallel_tool_calls=parallel_tool_calls, + prediction=prediction, + presence_penalty=presence_penalty, + prompt_cache_key=prompt_cache_key, + prompt_cache_options=prompt_cache_options, + prompt_cache_retention=prompt_cache_retention, + reasoning_effort=reasoning_effort, + safety_identifier=safety_identifier, + seed=seed, + service_tier=service_tier, + stop=stop, + store=store, + stream_options=stream_options, + temperature=temperature, + tool_choice=tool_choice, + tools=chat_completion_tools, + top_logprobs=top_logprobs, + top_p=top_p, + user=user, + verbosity=verbosity, + web_search_options=web_search_options, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return AsyncChatCompletionStreamManager( + api_request, + response_format=response_format, + input_tools=chat_completion_tools, + ) + + +class CompletionsWithRawResponse: + def __init__(self, completions: Completions) -> None: + self._completions = completions + + self.parse = _legacy_response.to_raw_response_wrapper( + completions.parse, + ) + self.create = _legacy_response.to_raw_response_wrapper( + completions.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + completions.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + completions.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + completions.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + completions.delete, + ) + + @cached_property + def messages(self) -> MessagesWithRawResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + return MessagesWithRawResponse(self._completions.messages) + + +class AsyncCompletionsWithRawResponse: + def __init__(self, completions: AsyncCompletions) -> None: + self._completions = completions + + self.parse = _legacy_response.async_to_raw_response_wrapper( + completions.parse, + ) + self.create = _legacy_response.async_to_raw_response_wrapper( + completions.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + completions.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + completions.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + completions.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + completions.delete, + ) + + @cached_property + def messages(self) -> AsyncMessagesWithRawResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + return AsyncMessagesWithRawResponse(self._completions.messages) + + +class CompletionsWithStreamingResponse: + def __init__(self, completions: Completions) -> None: + self._completions = completions + + self.parse = to_streamed_response_wrapper( + completions.parse, + ) + self.create = to_streamed_response_wrapper( + completions.create, + ) + self.retrieve = to_streamed_response_wrapper( + completions.retrieve, + ) + self.update = to_streamed_response_wrapper( + completions.update, + ) + self.list = to_streamed_response_wrapper( + completions.list, + ) + self.delete = to_streamed_response_wrapper( + completions.delete, + ) + + @cached_property + def messages(self) -> MessagesWithStreamingResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + return MessagesWithStreamingResponse(self._completions.messages) + + +class AsyncCompletionsWithStreamingResponse: + def __init__(self, completions: AsyncCompletions) -> None: + self._completions = completions + + self.parse = async_to_streamed_response_wrapper( + completions.parse, + ) + self.create = async_to_streamed_response_wrapper( + completions.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + completions.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + completions.update, + ) + self.list = async_to_streamed_response_wrapper( + completions.list, + ) + self.delete = async_to_streamed_response_wrapper( + completions.delete, + ) + + @cached_property + def messages(self) -> AsyncMessagesWithStreamingResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + return AsyncMessagesWithStreamingResponse(self._completions.messages) + + +def validate_response_format(response_format: object) -> None: + if inspect.isclass(response_format) and issubclass(response_format, pydantic.BaseModel): + raise TypeError( + "You tried to pass a `BaseModel` class to `chat.completions.create()`; You must use `chat.completions.parse()` instead" + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/chat/completions/messages.py b/venv/lib/python3.12/site-packages/openai/resources/chat/completions/messages.py new file mode 100644 index 0000000..a18c162 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/chat/completions/messages.py @@ -0,0 +1,222 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.chat.completions import message_list_params +from ....types.chat.chat_completion_store_message import ChatCompletionStoreMessage + +__all__ = ["Messages", "AsyncMessages"] + + +class Messages(SyncAPIResource): + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + + @cached_property + def with_raw_response(self) -> MessagesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return MessagesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> MessagesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return MessagesWithStreamingResponse(self) + + def list( + self, + completion_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[ChatCompletionStoreMessage]: + """Get the messages in a stored chat completion. + + Only Chat Completions that have + been created with the `store` parameter set to `true` will be returned. + + Args: + after: Identifier for the last message from the previous pagination request. + + limit: Number of messages to retrieve. + + order: Sort order for messages by timestamp. Use `asc` for ascending order or `desc` + for descending order. Defaults to `asc`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return self._get_api_list( + path_template("/chat/completions/{completion_id}/messages", completion_id=completion_id), + page=SyncCursorPage[ChatCompletionStoreMessage], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + message_list_params.MessageListParams, + ), + security={"bearer_auth": True}, + ), + model=ChatCompletionStoreMessage, + ) + + +class AsyncMessages(AsyncAPIResource): + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + + @cached_property + def with_raw_response(self) -> AsyncMessagesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncMessagesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncMessagesWithStreamingResponse(self) + + def list( + self, + completion_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ChatCompletionStoreMessage, AsyncCursorPage[ChatCompletionStoreMessage]]: + """Get the messages in a stored chat completion. + + Only Chat Completions that have + been created with the `store` parameter set to `true` will be returned. + + Args: + after: Identifier for the last message from the previous pagination request. + + limit: Number of messages to retrieve. + + order: Sort order for messages by timestamp. Use `asc` for ascending order or `desc` + for descending order. Defaults to `asc`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return self._get_api_list( + path_template("/chat/completions/{completion_id}/messages", completion_id=completion_id), + page=AsyncCursorPage[ChatCompletionStoreMessage], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + message_list_params.MessageListParams, + ), + security={"bearer_auth": True}, + ), + model=ChatCompletionStoreMessage, + ) + + +class MessagesWithRawResponse: + def __init__(self, messages: Messages) -> None: + self._messages = messages + + self.list = _legacy_response.to_raw_response_wrapper( + messages.list, + ) + + +class AsyncMessagesWithRawResponse: + def __init__(self, messages: AsyncMessages) -> None: + self._messages = messages + + self.list = _legacy_response.async_to_raw_response_wrapper( + messages.list, + ) + + +class MessagesWithStreamingResponse: + def __init__(self, messages: Messages) -> None: + self._messages = messages + + self.list = to_streamed_response_wrapper( + messages.list, + ) + + +class AsyncMessagesWithStreamingResponse: + def __init__(self, messages: AsyncMessages) -> None: + self._messages = messages + + self.list = async_to_streamed_response_wrapper( + messages.list, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/completions.py b/venv/lib/python3.12/site-packages/openai/resources/completions.py new file mode 100644 index 0000000..f15bde2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/completions.py @@ -0,0 +1,1198 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, overload + +import httpx2 + +from .. import _legacy_response +from ..types import completion_create_params +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from .._utils import required_args, maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .._streaming import Stream, AsyncStream +from .._base_client import make_request_options +from ..types.completion import Completion +from ..types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam + +__all__ = ["Completions", "AsyncCompletions"] + + +class Completions(SyncAPIResource): + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ + + @cached_property + def with_raw_response(self) -> CompletionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CompletionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CompletionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CompletionsWithStreamingResponse(self) + + @overload + def create( + self, + *, + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Completion: + """ + Creates a completion for the provided prompt and parameters. + + Returns a completion object, or a sequence of completion objects if the request + is streamed. + + Args: + model: ID of the model to use. You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + + prompt: The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + + best_of: Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + echo: Echo back the prompt in addition to the completion + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text + to token IDs. Mathematically, the bias is added to the logits generated by the + model prior to sampling. The exact effect will vary per model, but values + between -1 and 1 should decrease or increase likelihood of selection; values + like -100 or 100 should result in a ban or exclusive selection of the relevant + token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + + logprobs: Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + + max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + + seed: If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream: Whether to stream back partial progress. If set, tokens will be sent as + data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + suffix: The suffix that comes after a completion of inserted text. + + This parameter is only supported for `gpt-3.5-turbo-instruct`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], + stream: Literal[True], + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[Completion]: + """ + Creates a completion for the provided prompt and parameters. + + Returns a completion object, or a sequence of completion objects if the request + is streamed. + + Args: + model: ID of the model to use. You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + + prompt: The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + + stream: Whether to stream back partial progress. If set, tokens will be sent as + data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + best_of: Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + echo: Echo back the prompt in addition to the completion + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text + to token IDs. Mathematically, the bias is added to the logits generated by the + model prior to sampling. The exact effect will vary per model, but values + between -1 and 1 should decrease or increase likelihood of selection; values + like -100 or 100 should result in a ban or exclusive selection of the relevant + token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + + logprobs: Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + + max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + + seed: If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + suffix: The suffix that comes after a completion of inserted text. + + This parameter is only supported for `gpt-3.5-turbo-instruct`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], + stream: bool, + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Completion | Stream[Completion]: + """ + Creates a completion for the provided prompt and parameters. + + Returns a completion object, or a sequence of completion objects if the request + is streamed. + + Args: + model: ID of the model to use. You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + + prompt: The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + + stream: Whether to stream back partial progress. If set, tokens will be sent as + data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + best_of: Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + echo: Echo back the prompt in addition to the completion + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text + to token IDs. Mathematically, the bias is added to the logits generated by the + model prior to sampling. The exact effect will vary per model, but values + between -1 and 1 should decrease or increase likelihood of selection; values + like -100 or 100 should result in a ban or exclusive selection of the relevant + token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + + logprobs: Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + + max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + + seed: If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + suffix: The suffix that comes after a completion of inserted text. + + This parameter is only supported for `gpt-3.5-turbo-instruct`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["model", "prompt"], ["model", "prompt", "stream"]) + def create( + self, + *, + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Completion | Stream[Completion]: + return self._post( + "/completions", + body=maybe_transform( + { + "model": model, + "prompt": prompt, + "best_of": best_of, + "echo": echo, + "frequency_penalty": frequency_penalty, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_tokens": max_tokens, + "n": n, + "presence_penalty": presence_penalty, + "seed": seed, + "stop": stop, + "stream": stream, + "stream_options": stream_options, + "suffix": suffix, + "temperature": temperature, + "top_p": top_p, + "user": user, + }, + completion_create_params.CompletionCreateParamsStreaming + if stream + else completion_create_params.CompletionCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Completion, + stream=stream or False, + stream_cls=Stream[Completion], + ) + + +class AsyncCompletions(AsyncAPIResource): + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ + + @cached_property + def with_raw_response(self) -> AsyncCompletionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCompletionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCompletionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCompletionsWithStreamingResponse(self) + + @overload + async def create( + self, + *, + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Completion: + """ + Creates a completion for the provided prompt and parameters. + + Returns a completion object, or a sequence of completion objects if the request + is streamed. + + Args: + model: ID of the model to use. You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + + prompt: The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + + best_of: Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + echo: Echo back the prompt in addition to the completion + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text + to token IDs. Mathematically, the bias is added to the logits generated by the + model prior to sampling. The exact effect will vary per model, but values + between -1 and 1 should decrease or increase likelihood of selection; values + like -100 or 100 should result in a ban or exclusive selection of the relevant + token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + + logprobs: Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + + max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + + seed: If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream: Whether to stream back partial progress. If set, tokens will be sent as + data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + suffix: The suffix that comes after a completion of inserted text. + + This parameter is only supported for `gpt-3.5-turbo-instruct`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], + stream: Literal[True], + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[Completion]: + """ + Creates a completion for the provided prompt and parameters. + + Returns a completion object, or a sequence of completion objects if the request + is streamed. + + Args: + model: ID of the model to use. You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + + prompt: The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + + stream: Whether to stream back partial progress. If set, tokens will be sent as + data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + best_of: Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + echo: Echo back the prompt in addition to the completion + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text + to token IDs. Mathematically, the bias is added to the logits generated by the + model prior to sampling. The exact effect will vary per model, but values + between -1 and 1 should decrease or increase likelihood of selection; values + like -100 or 100 should result in a ban or exclusive selection of the relevant + token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + + logprobs: Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + + max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + + seed: If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + suffix: The suffix that comes after a completion of inserted text. + + This parameter is only supported for `gpt-3.5-turbo-instruct`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], + stream: bool, + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Completion | AsyncStream[Completion]: + """ + Creates a completion for the provided prompt and parameters. + + Returns a completion object, or a sequence of completion objects if the request + is streamed. + + Args: + model: ID of the model to use. You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + + prompt: The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + + stream: Whether to stream back partial progress. If set, tokens will be sent as + data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + best_of: Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + echo: Echo back the prompt in addition to the completion + + frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text + to token IDs. Mathematically, the bias is added to the logits generated by the + model prior to sampling. The exact effect will vary per model, but values + between -1 and 1 should decrease or increase likelihood of selection; values + like -100 or 100 should result in a ban or exclusive selection of the relevant + token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + + logprobs: Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + + max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood to + talk about new topics. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + + seed: If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream_options: Options for streaming response. Only set this when you set `stream: true`. + + suffix: The suffix that comes after a completion of inserted text. + + This parameter is only supported for `gpt-3.5-turbo-instruct`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["model", "prompt"], ["model", "prompt", "stream"]) + async def create( + self, + *, + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Completion | AsyncStream[Completion]: + return await self._post( + "/completions", + body=await async_maybe_transform( + { + "model": model, + "prompt": prompt, + "best_of": best_of, + "echo": echo, + "frequency_penalty": frequency_penalty, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_tokens": max_tokens, + "n": n, + "presence_penalty": presence_penalty, + "seed": seed, + "stop": stop, + "stream": stream, + "stream_options": stream_options, + "suffix": suffix, + "temperature": temperature, + "top_p": top_p, + "user": user, + }, + completion_create_params.CompletionCreateParamsStreaming + if stream + else completion_create_params.CompletionCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Completion, + stream=stream or False, + stream_cls=AsyncStream[Completion], + ) + + +class CompletionsWithRawResponse: + def __init__(self, completions: Completions) -> None: + self._completions = completions + + self.create = _legacy_response.to_raw_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsWithRawResponse: + def __init__(self, completions: AsyncCompletions) -> None: + self._completions = completions + + self.create = _legacy_response.async_to_raw_response_wrapper( + completions.create, + ) + + +class CompletionsWithStreamingResponse: + def __init__(self, completions: Completions) -> None: + self._completions = completions + + self.create = to_streamed_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsWithStreamingResponse: + def __init__(self, completions: AsyncCompletions) -> None: + self._completions = completions + + self.create = async_to_streamed_response_wrapper( + completions.create, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/containers/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/containers/__init__.py new file mode 100644 index 0000000..a4accc2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/containers/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .files import ( + Files as Files, + AsyncFiles as AsyncFiles, + FilesWithRawResponse as FilesWithRawResponse, + AsyncFilesWithRawResponse as AsyncFilesWithRawResponse, + FilesWithStreamingResponse as FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse as AsyncFilesWithStreamingResponse, + ) + from .containers import ( + Containers as Containers, + AsyncContainers as AsyncContainers, + ContainersWithRawResponse as ContainersWithRawResponse, + AsyncContainersWithRawResponse as AsyncContainersWithRawResponse, + ContainersWithStreamingResponse as ContainersWithStreamingResponse, + AsyncContainersWithStreamingResponse as AsyncContainersWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Files": (".files", "Files"), + "AsyncFiles": (".files", "AsyncFiles"), + "FilesWithRawResponse": (".files", "FilesWithRawResponse"), + "AsyncFilesWithRawResponse": (".files", "AsyncFilesWithRawResponse"), + "FilesWithStreamingResponse": (".files", "FilesWithStreamingResponse"), + "AsyncFilesWithStreamingResponse": (".files", "AsyncFilesWithStreamingResponse"), + "Containers": (".containers", "Containers"), + "AsyncContainers": (".containers", "AsyncContainers"), + "ContainersWithRawResponse": (".containers", "ContainersWithRawResponse"), + "AsyncContainersWithRawResponse": (".containers", "AsyncContainersWithRawResponse"), + "ContainersWithStreamingResponse": (".containers", "ContainersWithStreamingResponse"), + "AsyncContainersWithStreamingResponse": (".containers", "AsyncContainersWithStreamingResponse"), + } + _SUBMODULES = { + "containers", + "files", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Files", + "AsyncFiles", + "FilesWithRawResponse", + "AsyncFilesWithRawResponse", + "FilesWithStreamingResponse", + "AsyncFilesWithStreamingResponse", + "Containers", + "AsyncContainers", + "ContainersWithRawResponse", + "AsyncContainersWithRawResponse", + "ContainersWithStreamingResponse", + "AsyncContainersWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/containers/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/containers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7be31a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/containers/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/containers/__pycache__/containers.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/containers/__pycache__/containers.cpython-312.pyc new file mode 100644 index 0000000..af211f7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/containers/__pycache__/containers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/containers/containers.py b/venv/lib/python3.12/site-packages/openai/resources/containers/containers.py new file mode 100644 index 0000000..f0f0c2f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/containers/containers.py @@ -0,0 +1,569 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal + +import httpx2 + +from ... import _legacy_response +from ...types import container_list_params, container_create_params +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .files.files import ( + Files, + AsyncFiles, + FilesWithRawResponse, + AsyncFilesWithRawResponse, + FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse, +) +from ...pagination import SyncCursorPage, AsyncCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.container_list_response import ContainerListResponse +from ...types.container_create_response import ContainerCreateResponse +from ...types.container_retrieve_response import ContainerRetrieveResponse + +__all__ = ["Containers", "AsyncContainers"] + + +class Containers(SyncAPIResource): + @cached_property + def files(self) -> Files: + return Files(self._client) + + @cached_property + def with_raw_response(self) -> ContainersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ContainersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ContainersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ContainersWithStreamingResponse(self) + + def create( + self, + *, + name: str, + expires_after: container_create_params.ExpiresAfter | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + memory_limit: Literal["1g", "4g", "16g", "64g"] | Omit = omit, + network_policy: container_create_params.NetworkPolicy | Omit = omit, + skills: Iterable[container_create_params.Skill] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ContainerCreateResponse: + """ + Create Container + + Args: + name: Name of the container to create. + + expires_after: Container expiration time in seconds relative to the 'anchor' time. + + file_ids: IDs of files to copy to the container. + + memory_limit: Optional memory limit for the container. Defaults to "1g". + + network_policy: Network access policy for the container. + + skills: An optional list of skills referenced by id or inline data. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/containers", + body=maybe_transform( + { + "name": name, + "expires_after": expires_after, + "file_ids": file_ids, + "memory_limit": memory_limit, + "network_policy": network_policy, + "skills": skills, + }, + container_create_params.ContainerCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ContainerCreateResponse, + ) + + def retrieve( + self, + container_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ContainerRetrieveResponse: + """ + Retrieve Container + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + return self._get( + path_template("/containers/{container_id}", container_id=container_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ContainerRetrieveResponse, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + name: str | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[ContainerListResponse]: + """List Containers + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + name: Filter results by container name. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/containers", + page=SyncCursorPage[ContainerListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "name": name, + "order": order, + }, + container_list_params.ContainerListParams, + ), + security={"bearer_auth": True}, + ), + model=ContainerListResponse, + ) + + def delete( + self, + container_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Delete Container + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + path_template("/containers/{container_id}", container_id=container_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + +class AsyncContainers(AsyncAPIResource): + @cached_property + def files(self) -> AsyncFiles: + return AsyncFiles(self._client) + + @cached_property + def with_raw_response(self) -> AsyncContainersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncContainersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncContainersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncContainersWithStreamingResponse(self) + + async def create( + self, + *, + name: str, + expires_after: container_create_params.ExpiresAfter | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + memory_limit: Literal["1g", "4g", "16g", "64g"] | Omit = omit, + network_policy: container_create_params.NetworkPolicy | Omit = omit, + skills: Iterable[container_create_params.Skill] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ContainerCreateResponse: + """ + Create Container + + Args: + name: Name of the container to create. + + expires_after: Container expiration time in seconds relative to the 'anchor' time. + + file_ids: IDs of files to copy to the container. + + memory_limit: Optional memory limit for the container. Defaults to "1g". + + network_policy: Network access policy for the container. + + skills: An optional list of skills referenced by id or inline data. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/containers", + body=await async_maybe_transform( + { + "name": name, + "expires_after": expires_after, + "file_ids": file_ids, + "memory_limit": memory_limit, + "network_policy": network_policy, + "skills": skills, + }, + container_create_params.ContainerCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ContainerCreateResponse, + ) + + async def retrieve( + self, + container_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ContainerRetrieveResponse: + """ + Retrieve Container + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + return await self._get( + path_template("/containers/{container_id}", container_id=container_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ContainerRetrieveResponse, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + name: str | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ContainerListResponse, AsyncCursorPage[ContainerListResponse]]: + """List Containers + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + name: Filter results by container name. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/containers", + page=AsyncCursorPage[ContainerListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "name": name, + "order": order, + }, + container_list_params.ContainerListParams, + ), + security={"bearer_auth": True}, + ), + model=ContainerListResponse, + ) + + async def delete( + self, + container_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Delete Container + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + path_template("/containers/{container_id}", container_id=container_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + +class ContainersWithRawResponse: + def __init__(self, containers: Containers) -> None: + self._containers = containers + + self.create = _legacy_response.to_raw_response_wrapper( + containers.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + containers.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + containers.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + containers.delete, + ) + + @cached_property + def files(self) -> FilesWithRawResponse: + return FilesWithRawResponse(self._containers.files) + + +class AsyncContainersWithRawResponse: + def __init__(self, containers: AsyncContainers) -> None: + self._containers = containers + + self.create = _legacy_response.async_to_raw_response_wrapper( + containers.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + containers.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + containers.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + containers.delete, + ) + + @cached_property + def files(self) -> AsyncFilesWithRawResponse: + return AsyncFilesWithRawResponse(self._containers.files) + + +class ContainersWithStreamingResponse: + def __init__(self, containers: Containers) -> None: + self._containers = containers + + self.create = to_streamed_response_wrapper( + containers.create, + ) + self.retrieve = to_streamed_response_wrapper( + containers.retrieve, + ) + self.list = to_streamed_response_wrapper( + containers.list, + ) + self.delete = to_streamed_response_wrapper( + containers.delete, + ) + + @cached_property + def files(self) -> FilesWithStreamingResponse: + return FilesWithStreamingResponse(self._containers.files) + + +class AsyncContainersWithStreamingResponse: + def __init__(self, containers: AsyncContainers) -> None: + self._containers = containers + + self.create = async_to_streamed_response_wrapper( + containers.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + containers.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + containers.list, + ) + self.delete = async_to_streamed_response_wrapper( + containers.delete, + ) + + @cached_property + def files(self) -> AsyncFilesWithStreamingResponse: + return AsyncFilesWithStreamingResponse(self._containers.files) diff --git a/venv/lib/python3.12/site-packages/openai/resources/containers/files/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/containers/files/__init__.py new file mode 100644 index 0000000..1109911 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/containers/files/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .files import ( + Files as Files, + AsyncFiles as AsyncFiles, + FilesWithRawResponse as FilesWithRawResponse, + AsyncFilesWithRawResponse as AsyncFilesWithRawResponse, + FilesWithStreamingResponse as FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse as AsyncFilesWithStreamingResponse, + ) + from .content import ( + Content as Content, + AsyncContent as AsyncContent, + ContentWithRawResponse as ContentWithRawResponse, + AsyncContentWithRawResponse as AsyncContentWithRawResponse, + ContentWithStreamingResponse as ContentWithStreamingResponse, + AsyncContentWithStreamingResponse as AsyncContentWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Content": (".content", "Content"), + "AsyncContent": (".content", "AsyncContent"), + "ContentWithRawResponse": (".content", "ContentWithRawResponse"), + "AsyncContentWithRawResponse": (".content", "AsyncContentWithRawResponse"), + "ContentWithStreamingResponse": (".content", "ContentWithStreamingResponse"), + "AsyncContentWithStreamingResponse": (".content", "AsyncContentWithStreamingResponse"), + "Files": (".files", "Files"), + "AsyncFiles": (".files", "AsyncFiles"), + "FilesWithRawResponse": (".files", "FilesWithRawResponse"), + "AsyncFilesWithRawResponse": (".files", "AsyncFilesWithRawResponse"), + "FilesWithStreamingResponse": (".files", "FilesWithStreamingResponse"), + "AsyncFilesWithStreamingResponse": (".files", "AsyncFilesWithStreamingResponse"), + } + _SUBMODULES = { + "content", + "files", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Content", + "AsyncContent", + "ContentWithRawResponse", + "AsyncContentWithRawResponse", + "ContentWithStreamingResponse", + "AsyncContentWithStreamingResponse", + "Files", + "AsyncFiles", + "FilesWithRawResponse", + "AsyncFilesWithRawResponse", + "FilesWithStreamingResponse", + "AsyncFilesWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/containers/files/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/containers/files/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a3a7df1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/containers/files/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/containers/files/__pycache__/content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/containers/files/__pycache__/content.cpython-312.pyc new file mode 100644 index 0000000..3b939ea Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/containers/files/__pycache__/content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/containers/files/__pycache__/files.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/containers/files/__pycache__/files.cpython-312.pyc new file mode 100644 index 0000000..699571e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/containers/files/__pycache__/files.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/containers/files/content.py b/venv/lib/python3.12/site-packages/openai/resources/containers/files/content.py new file mode 100644 index 0000000..826aab8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/containers/files/content.py @@ -0,0 +1,186 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import path_template +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ...._base_client import make_request_options + +__all__ = ["Content", "AsyncContent"] + + +class Content(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ContentWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ContentWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ContentWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ContentWithStreamingResponse(self) + + def retrieve( + self, + file_id: str, + *, + container_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Retrieve Container File Content + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return self._get( + path_template( + "/containers/{container_id}/files/{file_id}/content", container_id=container_id, file_id=file_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class AsyncContent(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncContentWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncContentWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncContentWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncContentWithStreamingResponse(self) + + async def retrieve( + self, + file_id: str, + *, + container_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Retrieve Container File Content + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return await self._get( + path_template( + "/containers/{container_id}/files/{file_id}/content", container_id=container_id, file_id=file_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class ContentWithRawResponse: + def __init__(self, content: Content) -> None: + self._content = content + + self.retrieve = _legacy_response.to_raw_response_wrapper( + content.retrieve, + ) + + +class AsyncContentWithRawResponse: + def __init__(self, content: AsyncContent) -> None: + self._content = content + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + content.retrieve, + ) + + +class ContentWithStreamingResponse: + def __init__(self, content: Content) -> None: + self._content = content + + self.retrieve = to_custom_streamed_response_wrapper( + content.retrieve, + StreamedBinaryAPIResponse, + ) + + +class AsyncContentWithStreamingResponse: + def __init__(self, content: AsyncContent) -> None: + self._content = content + + self.retrieve = async_to_custom_streamed_response_wrapper( + content.retrieve, + AsyncStreamedBinaryAPIResponse, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/containers/files/files.py b/venv/lib/python3.12/site-packages/openai/resources/containers/files/files.py new file mode 100644 index 0000000..5511c44 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/containers/files/files.py @@ -0,0 +1,576 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Mapping, cast +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from .content import ( + Content, + AsyncContent, + ContentWithRawResponse, + AsyncContentWithRawResponse, + ContentWithStreamingResponse, + AsyncContentWithStreamingResponse, +) +from ...._files import deepcopy_with_paths +from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, FileTypes, omit, not_given +from ...._utils import extract_files, path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.containers import file_list_params, file_create_params +from ....types.containers.file_list_response import FileListResponse +from ....types.containers.file_create_response import FileCreateResponse +from ....types.containers.file_retrieve_response import FileRetrieveResponse + +__all__ = ["Files", "AsyncFiles"] + + +class Files(SyncAPIResource): + @cached_property + def content(self) -> Content: + return Content(self._client) + + @cached_property + def with_raw_response(self) -> FilesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return FilesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FilesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return FilesWithStreamingResponse(self) + + def create( + self, + container_id: str, + *, + file: FileTypes | Omit = omit, + file_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FileCreateResponse: + """ + Create a Container File + + You can send either a multipart/form-data request with the raw file content, or + a JSON request with a file ID. + + Args: + file: The File object (not file name) to be uploaded. + + file_id: Name of the file to create. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + body = deepcopy_with_paths( + { + "file": file, + "file_id": file_id, + }, + [["file"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + path_template("/containers/{container_id}/files", container_id=container_id), + body=maybe_transform(body, file_create_params.FileCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FileCreateResponse, + ) + + def retrieve( + self, + file_id: str, + *, + container_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FileRetrieveResponse: + """ + Retrieve Container File + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return self._get( + path_template("/containers/{container_id}/files/{file_id}", container_id=container_id, file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FileRetrieveResponse, + ) + + def list( + self, + container_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[FileListResponse]: + """List Container files + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + return self._get_api_list( + path_template("/containers/{container_id}/files", container_id=container_id), + page=SyncCursorPage[FileListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + file_list_params.FileListParams, + ), + security={"bearer_auth": True}, + ), + model=FileListResponse, + ) + + def delete( + self, + file_id: str, + *, + container_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Delete Container File + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + path_template("/containers/{container_id}/files/{file_id}", container_id=container_id, file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + +class AsyncFiles(AsyncAPIResource): + @cached_property + def content(self) -> AsyncContent: + return AsyncContent(self._client) + + @cached_property + def with_raw_response(self) -> AsyncFilesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncFilesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFilesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncFilesWithStreamingResponse(self) + + async def create( + self, + container_id: str, + *, + file: FileTypes | Omit = omit, + file_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FileCreateResponse: + """ + Create a Container File + + You can send either a multipart/form-data request with the raw file content, or + a JSON request with a file ID. + + Args: + file: The File object (not file name) to be uploaded. + + file_id: Name of the file to create. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + body = deepcopy_with_paths( + { + "file": file, + "file_id": file_id, + }, + [["file"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + path_template("/containers/{container_id}/files", container_id=container_id), + body=await async_maybe_transform(body, file_create_params.FileCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FileCreateResponse, + ) + + async def retrieve( + self, + file_id: str, + *, + container_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FileRetrieveResponse: + """ + Retrieve Container File + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return await self._get( + path_template("/containers/{container_id}/files/{file_id}", container_id=container_id, file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FileRetrieveResponse, + ) + + def list( + self, + container_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[FileListResponse, AsyncCursorPage[FileListResponse]]: + """List Container files + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + return self._get_api_list( + path_template("/containers/{container_id}/files", container_id=container_id), + page=AsyncCursorPage[FileListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + file_list_params.FileListParams, + ), + security={"bearer_auth": True}, + ), + model=FileListResponse, + ) + + async def delete( + self, + file_id: str, + *, + container_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Delete Container File + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not container_id: + raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + path_template("/containers/{container_id}/files/{file_id}", container_id=container_id, file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + +class FilesWithRawResponse: + def __init__(self, files: Files) -> None: + self._files = files + + self.create = _legacy_response.to_raw_response_wrapper( + files.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + files.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + files.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + files.delete, + ) + + @cached_property + def content(self) -> ContentWithRawResponse: + return ContentWithRawResponse(self._files.content) + + +class AsyncFilesWithRawResponse: + def __init__(self, files: AsyncFiles) -> None: + self._files = files + + self.create = _legacy_response.async_to_raw_response_wrapper( + files.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + files.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + files.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + files.delete, + ) + + @cached_property + def content(self) -> AsyncContentWithRawResponse: + return AsyncContentWithRawResponse(self._files.content) + + +class FilesWithStreamingResponse: + def __init__(self, files: Files) -> None: + self._files = files + + self.create = to_streamed_response_wrapper( + files.create, + ) + self.retrieve = to_streamed_response_wrapper( + files.retrieve, + ) + self.list = to_streamed_response_wrapper( + files.list, + ) + self.delete = to_streamed_response_wrapper( + files.delete, + ) + + @cached_property + def content(self) -> ContentWithStreamingResponse: + return ContentWithStreamingResponse(self._files.content) + + +class AsyncFilesWithStreamingResponse: + def __init__(self, files: AsyncFiles) -> None: + self._files = files + + self.create = async_to_streamed_response_wrapper( + files.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + files.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + files.list, + ) + self.delete = async_to_streamed_response_wrapper( + files.delete, + ) + + @cached_property + def content(self) -> AsyncContentWithStreamingResponse: + return AsyncContentWithStreamingResponse(self._files.content) diff --git a/venv/lib/python3.12/site-packages/openai/resources/content_provenance_checks.py b/venv/lib/python3.12/site-packages/openai/resources/content_provenance_checks.py new file mode 100644 index 0000000..93c35c1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/content_provenance_checks.py @@ -0,0 +1,206 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Mapping, cast + +import httpx2 + +from .. import _legacy_response +from ..types import content_provenance_check_create_params +from .._files import deepcopy_with_paths +from .._types import Body, Query, Headers, NotGiven, FileTypes, not_given +from .._utils import extract_files, maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .._base_client import make_request_options +from ..types.content_provenance_check import ContentProvenanceCheck + +__all__ = ["ContentProvenanceChecks", "AsyncContentProvenanceChecks"] + + +class ContentProvenanceChecks(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ContentProvenanceChecksWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ContentProvenanceChecksWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ContentProvenanceChecksWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ContentProvenanceChecksWithStreamingResponse(self) + + def create( + self, + *, + file: FileTypes, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ContentProvenanceCheck: + """ + Check whether an image or audio file contains known OpenAI provenance signals. + [Learn more about content provenance](https://developers.openai.com/api/docs/guides/content-provenance). + + If `not_detected`, it means the tool did not find supported signals in the + uploaded file. The content could still have been generated by OpenAI if the + metadata was stripped or has evidence of tampering, the watermark was degraded, + it comes from a legacy generation model, or it was created before provenance + signals were available. Content could also still be AI-generated by another + company's model, which the tool currently does not detect. + + Args: + file: The image or audio file to check for supported OpenAI provenance signals. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths({"file": file}, [["file"]]) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/content_provenance_checks", + body=maybe_transform(body, content_provenance_check_create_params.ContentProvenanceCheckCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ContentProvenanceCheck, + ) + + +class AsyncContentProvenanceChecks(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncContentProvenanceChecksWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncContentProvenanceChecksWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncContentProvenanceChecksWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncContentProvenanceChecksWithStreamingResponse(self) + + async def create( + self, + *, + file: FileTypes, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ContentProvenanceCheck: + """ + Check whether an image or audio file contains known OpenAI provenance signals. + [Learn more about content provenance](https://developers.openai.com/api/docs/guides/content-provenance). + + If `not_detected`, it means the tool did not find supported signals in the + uploaded file. The content could still have been generated by OpenAI if the + metadata was stripped or has evidence of tampering, the watermark was degraded, + it comes from a legacy generation model, or it was created before provenance + signals were available. Content could also still be AI-generated by another + company's model, which the tool currently does not detect. + + Args: + file: The image or audio file to check for supported OpenAI provenance signals. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths({"file": file}, [["file"]]) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/content_provenance_checks", + body=await async_maybe_transform( + body, content_provenance_check_create_params.ContentProvenanceCheckCreateParams + ), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ContentProvenanceCheck, + ) + + +class ContentProvenanceChecksWithRawResponse: + def __init__(self, content_provenance_checks: ContentProvenanceChecks) -> None: + self._content_provenance_checks = content_provenance_checks + + self.create = _legacy_response.to_raw_response_wrapper( + content_provenance_checks.create, + ) + + +class AsyncContentProvenanceChecksWithRawResponse: + def __init__(self, content_provenance_checks: AsyncContentProvenanceChecks) -> None: + self._content_provenance_checks = content_provenance_checks + + self.create = _legacy_response.async_to_raw_response_wrapper( + content_provenance_checks.create, + ) + + +class ContentProvenanceChecksWithStreamingResponse: + def __init__(self, content_provenance_checks: ContentProvenanceChecks) -> None: + self._content_provenance_checks = content_provenance_checks + + self.create = to_streamed_response_wrapper( + content_provenance_checks.create, + ) + + +class AsyncContentProvenanceChecksWithStreamingResponse: + def __init__(self, content_provenance_checks: AsyncContentProvenanceChecks) -> None: + self._content_provenance_checks = content_provenance_checks + + self.create = async_to_streamed_response_wrapper( + content_provenance_checks.create, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/conversations/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/conversations/__init__.py new file mode 100644 index 0000000..0ff8564 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/conversations/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .items import ( + Items as Items, + AsyncItems as AsyncItems, + ItemsWithRawResponse as ItemsWithRawResponse, + AsyncItemsWithRawResponse as AsyncItemsWithRawResponse, + ItemsWithStreamingResponse as ItemsWithStreamingResponse, + AsyncItemsWithStreamingResponse as AsyncItemsWithStreamingResponse, + ) + from .conversations import ( + Conversations as Conversations, + AsyncConversations as AsyncConversations, + ConversationsWithRawResponse as ConversationsWithRawResponse, + AsyncConversationsWithRawResponse as AsyncConversationsWithRawResponse, + ConversationsWithStreamingResponse as ConversationsWithStreamingResponse, + AsyncConversationsWithStreamingResponse as AsyncConversationsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Items": (".items", "Items"), + "AsyncItems": (".items", "AsyncItems"), + "ItemsWithRawResponse": (".items", "ItemsWithRawResponse"), + "AsyncItemsWithRawResponse": (".items", "AsyncItemsWithRawResponse"), + "ItemsWithStreamingResponse": (".items", "ItemsWithStreamingResponse"), + "AsyncItemsWithStreamingResponse": (".items", "AsyncItemsWithStreamingResponse"), + "Conversations": (".conversations", "Conversations"), + "AsyncConversations": (".conversations", "AsyncConversations"), + "ConversationsWithRawResponse": (".conversations", "ConversationsWithRawResponse"), + "AsyncConversationsWithRawResponse": (".conversations", "AsyncConversationsWithRawResponse"), + "ConversationsWithStreamingResponse": (".conversations", "ConversationsWithStreamingResponse"), + "AsyncConversationsWithStreamingResponse": (".conversations", "AsyncConversationsWithStreamingResponse"), + } + _SUBMODULES = { + "conversations", + "items", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Items", + "AsyncItems", + "ItemsWithRawResponse", + "AsyncItemsWithRawResponse", + "ItemsWithStreamingResponse", + "AsyncItemsWithStreamingResponse", + "Conversations", + "AsyncConversations", + "ConversationsWithRawResponse", + "AsyncConversationsWithRawResponse", + "ConversationsWithStreamingResponse", + "AsyncConversationsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/conversations/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/conversations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1179ff9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/conversations/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/conversations/__pycache__/conversations.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/conversations/__pycache__/conversations.cpython-312.pyc new file mode 100644 index 0000000..dc3ea94 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/conversations/__pycache__/conversations.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/conversations/__pycache__/items.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/conversations/__pycache__/items.cpython-312.pyc new file mode 100644 index 0000000..5599e27 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/conversations/__pycache__/items.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/conversations/api.md b/venv/lib/python3.12/site-packages/openai/resources/conversations/api.md new file mode 100644 index 0000000..9e9181a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/conversations/api.md @@ -0,0 +1,42 @@ +# Conversations + +Types: + +```python +from openai.types.conversations import ( + ComputerScreenshotContent, + Conversation, + ConversationDeleted, + ConversationDeletedResource, + Message, + SummaryTextContent, + TextContent, + InputTextContent, + OutputTextContent, + RefusalContent, + InputImageContent, + InputFileContent, +) +``` + +Methods: + +- client.conversations.create(\*\*params) -> Conversation +- client.conversations.retrieve(conversation_id) -> Conversation +- client.conversations.update(conversation_id, \*\*params) -> Conversation +- client.conversations.delete(conversation_id) -> ConversationDeletedResource + +## Items + +Types: + +```python +from openai.types.conversations import ConversationItem, ConversationItemList +``` + +Methods: + +- client.conversations.items.create(conversation_id, \*\*params) -> ConversationItemList +- client.conversations.items.retrieve(item_id, \*, conversation_id, \*\*params) -> ConversationItem +- client.conversations.items.list(conversation_id, \*\*params) -> SyncConversationCursorPage[ConversationItem] +- client.conversations.items.delete(item_id, \*, conversation_id) -> Conversation diff --git a/venv/lib/python3.12/site-packages/openai/resources/conversations/conversations.py b/venv/lib/python3.12/site-packages/openai/resources/conversations/conversations.py new file mode 100644 index 0000000..6d2b4a6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/conversations/conversations.py @@ -0,0 +1,526 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional + +import httpx2 + +from ... import _legacy_response +from .items import ( + Items, + AsyncItems, + ItemsWithRawResponse, + AsyncItemsWithRawResponse, + ItemsWithStreamingResponse, + AsyncItemsWithStreamingResponse, +) +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.conversations import conversation_create_params, conversation_update_params +from ...types.shared_params.metadata import Metadata +from ...types.conversations.conversation import Conversation +from ...types.responses.response_input_item_param import ResponseInputItemParam +from ...types.conversations.conversation_deleted_resource import ConversationDeletedResource + +__all__ = ["Conversations", "AsyncConversations"] + + +class Conversations(SyncAPIResource): + """Manage conversations and conversation items.""" + + @cached_property + def items(self) -> Items: + """Manage conversations and conversation items.""" + return Items(self._client) + + @cached_property + def with_raw_response(self) -> ConversationsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ConversationsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ConversationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ConversationsWithStreamingResponse(self) + + def create( + self, + *, + items: Optional[Iterable[ResponseInputItemParam]] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Conversation: + """ + Create a conversation. + + Args: + items: Initial items to include in the conversation context. You may add up to 20 items + at a time. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/conversations", + body=maybe_transform( + { + "items": items, + "metadata": metadata, + }, + conversation_create_params.ConversationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Conversation, + ) + + def retrieve( + self, + conversation_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Conversation: + """ + Get a conversation + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return self._get( + path_template("/conversations/{conversation_id}", conversation_id=conversation_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Conversation, + ) + + def update( + self, + conversation_id: str, + *, + metadata: Optional[Metadata], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Conversation: + """ + Update a conversation + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return self._post( + path_template("/conversations/{conversation_id}", conversation_id=conversation_id), + body=maybe_transform({"metadata": metadata}, conversation_update_params.ConversationUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Conversation, + ) + + def delete( + self, + conversation_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ConversationDeletedResource: + """Delete a conversation. + + Items in the conversation will not be deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return self._delete( + path_template("/conversations/{conversation_id}", conversation_id=conversation_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ConversationDeletedResource, + ) + + +class AsyncConversations(AsyncAPIResource): + """Manage conversations and conversation items.""" + + @cached_property + def items(self) -> AsyncItems: + """Manage conversations and conversation items.""" + return AsyncItems(self._client) + + @cached_property + def with_raw_response(self) -> AsyncConversationsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncConversationsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncConversationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncConversationsWithStreamingResponse(self) + + async def create( + self, + *, + items: Optional[Iterable[ResponseInputItemParam]] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Conversation: + """ + Create a conversation. + + Args: + items: Initial items to include in the conversation context. You may add up to 20 items + at a time. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/conversations", + body=await async_maybe_transform( + { + "items": items, + "metadata": metadata, + }, + conversation_create_params.ConversationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Conversation, + ) + + async def retrieve( + self, + conversation_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Conversation: + """ + Get a conversation + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return await self._get( + path_template("/conversations/{conversation_id}", conversation_id=conversation_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Conversation, + ) + + async def update( + self, + conversation_id: str, + *, + metadata: Optional[Metadata], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Conversation: + """ + Update a conversation + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return await self._post( + path_template("/conversations/{conversation_id}", conversation_id=conversation_id), + body=await async_maybe_transform( + {"metadata": metadata}, conversation_update_params.ConversationUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Conversation, + ) + + async def delete( + self, + conversation_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ConversationDeletedResource: + """Delete a conversation. + + Items in the conversation will not be deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return await self._delete( + path_template("/conversations/{conversation_id}", conversation_id=conversation_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ConversationDeletedResource, + ) + + +class ConversationsWithRawResponse: + def __init__(self, conversations: Conversations) -> None: + self._conversations = conversations + + self.create = _legacy_response.to_raw_response_wrapper( + conversations.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + conversations.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + conversations.update, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + conversations.delete, + ) + + @cached_property + def items(self) -> ItemsWithRawResponse: + """Manage conversations and conversation items.""" + return ItemsWithRawResponse(self._conversations.items) + + +class AsyncConversationsWithRawResponse: + def __init__(self, conversations: AsyncConversations) -> None: + self._conversations = conversations + + self.create = _legacy_response.async_to_raw_response_wrapper( + conversations.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + conversations.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + conversations.update, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + conversations.delete, + ) + + @cached_property + def items(self) -> AsyncItemsWithRawResponse: + """Manage conversations and conversation items.""" + return AsyncItemsWithRawResponse(self._conversations.items) + + +class ConversationsWithStreamingResponse: + def __init__(self, conversations: Conversations) -> None: + self._conversations = conversations + + self.create = to_streamed_response_wrapper( + conversations.create, + ) + self.retrieve = to_streamed_response_wrapper( + conversations.retrieve, + ) + self.update = to_streamed_response_wrapper( + conversations.update, + ) + self.delete = to_streamed_response_wrapper( + conversations.delete, + ) + + @cached_property + def items(self) -> ItemsWithStreamingResponse: + """Manage conversations and conversation items.""" + return ItemsWithStreamingResponse(self._conversations.items) + + +class AsyncConversationsWithStreamingResponse: + def __init__(self, conversations: AsyncConversations) -> None: + self._conversations = conversations + + self.create = async_to_streamed_response_wrapper( + conversations.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + conversations.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + conversations.update, + ) + self.delete = async_to_streamed_response_wrapper( + conversations.delete, + ) + + @cached_property + def items(self) -> AsyncItemsWithStreamingResponse: + """Manage conversations and conversation items.""" + return AsyncItemsWithStreamingResponse(self._conversations.items) diff --git a/venv/lib/python3.12/site-packages/openai/resources/conversations/items.py b/venv/lib/python3.12/site-packages/openai/resources/conversations/items.py new file mode 100644 index 0000000..f73161d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/conversations/items.py @@ -0,0 +1,583 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, List, Iterable, cast +from typing_extensions import Literal + +import httpx2 + +from ... import _legacy_response +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.conversations import item_list_params, item_create_params, item_retrieve_params +from ...types.conversations.conversation import Conversation +from ...types.responses.response_includable import ResponseIncludable +from ...types.conversations.conversation_item import ConversationItem +from ...types.responses.response_input_item_param import ResponseInputItemParam +from ...types.conversations.conversation_item_list import ConversationItemList + +__all__ = ["Items", "AsyncItems"] + + +class Items(SyncAPIResource): + """Manage conversations and conversation items.""" + + @cached_property + def with_raw_response(self) -> ItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ItemsWithStreamingResponse(self) + + def create( + self, + conversation_id: str, + *, + items: Iterable[ResponseInputItemParam], + include: List[ResponseIncludable] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ConversationItemList: + """ + Create items in a conversation with the given ID. + + Args: + items: The items to add to the conversation. You may add up to 20 items at a time. + + include: Additional fields to include in the response. See the `include` parameter for + [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return self._post( + path_template("/conversations/{conversation_id}/items", conversation_id=conversation_id), + body=maybe_transform({"items": items}, item_create_params.ItemCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, item_create_params.ItemCreateParams), + security={"bearer_auth": True}, + ), + cast_to=ConversationItemList, + ) + + def retrieve( + self, + item_id: str, + *, + conversation_id: str, + include: List[ResponseIncludable] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ConversationItem: + """ + Get a single item from a conversation with the given IDs. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + if not item_id: + raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") + return cast( + ConversationItem, + self._get( + path_template( + "/conversations/{conversation_id}/items/{item_id}", conversation_id=conversation_id, item_id=item_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, item_retrieve_params.ItemRetrieveParams), + security={"bearer_auth": True}, + ), + cast_to=cast(Any, ConversationItem), # Union types cannot be passed in as arguments in the type system + ), + ) + + def list( + self, + conversation_id: str, + *, + after: str | Omit = omit, + include: List[ResponseIncludable] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ConversationItem]: + """ + List all items for a conversation with the given ID. + + Args: + after: An item ID to list items after, used in pagination. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return self._get_api_list( + path_template("/conversations/{conversation_id}/items", conversation_id=conversation_id), + page=SyncConversationCursorPage[ConversationItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include": include, + "limit": limit, + "order": order, + }, + item_list_params.ItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, ConversationItem), # Union types cannot be passed in as arguments in the type system + ) + + def delete( + self, + item_id: str, + *, + conversation_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Conversation: + """ + Delete an item from a conversation with the given IDs. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + if not item_id: + raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") + return self._delete( + path_template( + "/conversations/{conversation_id}/items/{item_id}", conversation_id=conversation_id, item_id=item_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Conversation, + ) + + +class AsyncItems(AsyncAPIResource): + """Manage conversations and conversation items.""" + + @cached_property + def with_raw_response(self) -> AsyncItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncItemsWithStreamingResponse(self) + + async def create( + self, + conversation_id: str, + *, + items: Iterable[ResponseInputItemParam], + include: List[ResponseIncludable] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ConversationItemList: + """ + Create items in a conversation with the given ID. + + Args: + items: The items to add to the conversation. You may add up to 20 items at a time. + + include: Additional fields to include in the response. See the `include` parameter for + [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return await self._post( + path_template("/conversations/{conversation_id}/items", conversation_id=conversation_id), + body=await async_maybe_transform({"items": items}, item_create_params.ItemCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"include": include}, item_create_params.ItemCreateParams), + security={"bearer_auth": True}, + ), + cast_to=ConversationItemList, + ) + + async def retrieve( + self, + item_id: str, + *, + conversation_id: str, + include: List[ResponseIncludable] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ConversationItem: + """ + Get a single item from a conversation with the given IDs. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + if not item_id: + raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") + return cast( + ConversationItem, + await self._get( + path_template( + "/conversations/{conversation_id}/items/{item_id}", conversation_id=conversation_id, item_id=item_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"include": include}, item_retrieve_params.ItemRetrieveParams), + security={"bearer_auth": True}, + ), + cast_to=cast(Any, ConversationItem), # Union types cannot be passed in as arguments in the type system + ), + ) + + def list( + self, + conversation_id: str, + *, + after: str | Omit = omit, + include: List[ResponseIncludable] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ConversationItem, AsyncConversationCursorPage[ConversationItem]]: + """ + List all items for a conversation with the given ID. + + Args: + after: An item ID to list items after, used in pagination. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return self._get_api_list( + path_template("/conversations/{conversation_id}/items", conversation_id=conversation_id), + page=AsyncConversationCursorPage[ConversationItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include": include, + "limit": limit, + "order": order, + }, + item_list_params.ItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, ConversationItem), # Union types cannot be passed in as arguments in the type system + ) + + async def delete( + self, + item_id: str, + *, + conversation_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Conversation: + """ + Delete an item from a conversation with the given IDs. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + if not item_id: + raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") + return await self._delete( + path_template( + "/conversations/{conversation_id}/items/{item_id}", conversation_id=conversation_id, item_id=item_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Conversation, + ) + + +class ItemsWithRawResponse: + def __init__(self, items: Items) -> None: + self._items = items + + self.create = _legacy_response.to_raw_response_wrapper( + items.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + items.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + items.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + items.delete, + ) + + +class AsyncItemsWithRawResponse: + def __init__(self, items: AsyncItems) -> None: + self._items = items + + self.create = _legacy_response.async_to_raw_response_wrapper( + items.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + items.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + items.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + items.delete, + ) + + +class ItemsWithStreamingResponse: + def __init__(self, items: Items) -> None: + self._items = items + + self.create = to_streamed_response_wrapper( + items.create, + ) + self.retrieve = to_streamed_response_wrapper( + items.retrieve, + ) + self.list = to_streamed_response_wrapper( + items.list, + ) + self.delete = to_streamed_response_wrapper( + items.delete, + ) + + +class AsyncItemsWithStreamingResponse: + def __init__(self, items: AsyncItems) -> None: + self._items = items + + self.create = async_to_streamed_response_wrapper( + items.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + items.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + items.list, + ) + self.delete = async_to_streamed_response_wrapper( + items.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/embeddings.py b/venv/lib/python3.12/site-packages/openai/resources/embeddings.py new file mode 100644 index 0000000..75bf9b4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/embeddings.py @@ -0,0 +1,263 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from functools import partial +from typing_extensions import Literal + +import httpx2 + +from .. import _legacy_response +from ..types import embedding_create_params +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from .._utils import is_given, maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .._base_client import make_request_options +from ..types.embedding_model import EmbeddingModel +from ..lib._parsing._embeddings import parse_embedding_response as _parse_embedding_response +from ..types.create_embedding_response import CreateEmbeddingResponse + +__all__ = ["Embeddings", "AsyncEmbeddings"] + + +class Embeddings(SyncAPIResource): + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ + + @cached_property + def with_raw_response(self) -> EmbeddingsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return EmbeddingsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EmbeddingsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return EmbeddingsWithStreamingResponse(self) + + def create( + self, + *, + input: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]]], + model: Union[str, EmbeddingModel], + dimensions: int | Omit = omit, + encoding_format: Literal["float", "base64"] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> CreateEmbeddingResponse: + """ + Creates an embedding vector representing the input text. + + Args: + input: Input text to embed, encoded as a string or array of tokens. To embed multiple + inputs in a single request, pass an array of strings or array of token arrays. + The input must not exceed the max input tokens for the model (8192 tokens for + all embedding models), cannot be an empty string, and any array must be 2048 + dimensions or less. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. In addition to the per-input token limit, all embedding + models enforce a maximum of 300,000 tokens summed across all inputs in a single + request. + + model: ID of the model to use. You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + + dimensions: The number of dimensions the resulting output embeddings should have. Only + supported in `text-embedding-3` and later models. + + encoding_format: The format to return the embeddings in. Can be either `float` or + [`base64`](https://pypi.org/project/pybase64/). + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + params = { + "input": input, + "model": model, + "user": user, + "dimensions": dimensions, + "encoding_format": encoding_format, + } + if not is_given(encoding_format): + params["encoding_format"] = "base64" + + return self._post( + "/embeddings", + body=maybe_transform(params, embedding_create_params.EmbeddingCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=partial(_parse_embedding_response, encoding_format=encoding_format), + security={"bearer_auth": True}, + ), + cast_to=CreateEmbeddingResponse, + ) + + +class AsyncEmbeddings(AsyncAPIResource): + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ + + @cached_property + def with_raw_response(self) -> AsyncEmbeddingsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncEmbeddingsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEmbeddingsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncEmbeddingsWithStreamingResponse(self) + + async def create( + self, + *, + input: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]]], + model: Union[str, EmbeddingModel], + dimensions: int | Omit = omit, + encoding_format: Literal["float", "base64"] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> CreateEmbeddingResponse: + """ + Creates an embedding vector representing the input text. + + Args: + input: Input text to embed, encoded as a string or array of tokens. To embed multiple + inputs in a single request, pass an array of strings or array of token arrays. + The input must not exceed the max input tokens for the model (8192 tokens for + all embedding models), cannot be an empty string, and any array must be 2048 + dimensions or less. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. In addition to the per-input token limit, all embedding + models enforce a maximum of 300,000 tokens summed across all inputs in a single + request. + + model: ID of the model to use. You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + + dimensions: The number of dimensions the resulting output embeddings should have. Only + supported in `text-embedding-3` and later models. + + encoding_format: The format to return the embeddings in. Can be either `float` or + [`base64`](https://pypi.org/project/pybase64/). + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + params = { + "input": input, + "model": model, + "user": user, + "dimensions": dimensions, + "encoding_format": encoding_format, + } + if not is_given(encoding_format): + params["encoding_format"] = "base64" + + return await self._post( + "/embeddings", + body=maybe_transform(params, embedding_create_params.EmbeddingCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=partial(_parse_embedding_response, encoding_format=encoding_format), + security={"bearer_auth": True}, + ), + cast_to=CreateEmbeddingResponse, + ) + + +class EmbeddingsWithRawResponse: + def __init__(self, embeddings: Embeddings) -> None: + self._embeddings = embeddings + + self.create = _legacy_response.to_raw_response_wrapper( + embeddings.create, + ) + + +class AsyncEmbeddingsWithRawResponse: + def __init__(self, embeddings: AsyncEmbeddings) -> None: + self._embeddings = embeddings + + self.create = _legacy_response.async_to_raw_response_wrapper( + embeddings.create, + ) + + +class EmbeddingsWithStreamingResponse: + def __init__(self, embeddings: Embeddings) -> None: + self._embeddings = embeddings + + self.create = to_streamed_response_wrapper( + embeddings.create, + ) + + +class AsyncEmbeddingsWithStreamingResponse: + def __init__(self, embeddings: AsyncEmbeddings) -> None: + self._embeddings = embeddings + + self.create = async_to_streamed_response_wrapper( + embeddings.create, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/evals/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/evals/__init__.py new file mode 100644 index 0000000..2f35487 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/evals/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .runs import ( + Runs as Runs, + AsyncRuns as AsyncRuns, + RunsWithRawResponse as RunsWithRawResponse, + AsyncRunsWithRawResponse as AsyncRunsWithRawResponse, + RunsWithStreamingResponse as RunsWithStreamingResponse, + AsyncRunsWithStreamingResponse as AsyncRunsWithStreamingResponse, + ) + from .evals import ( + Evals as Evals, + AsyncEvals as AsyncEvals, + EvalsWithRawResponse as EvalsWithRawResponse, + AsyncEvalsWithRawResponse as AsyncEvalsWithRawResponse, + EvalsWithStreamingResponse as EvalsWithStreamingResponse, + AsyncEvalsWithStreamingResponse as AsyncEvalsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Runs": (".runs", "Runs"), + "AsyncRuns": (".runs", "AsyncRuns"), + "RunsWithRawResponse": (".runs", "RunsWithRawResponse"), + "AsyncRunsWithRawResponse": (".runs", "AsyncRunsWithRawResponse"), + "RunsWithStreamingResponse": (".runs", "RunsWithStreamingResponse"), + "AsyncRunsWithStreamingResponse": (".runs", "AsyncRunsWithStreamingResponse"), + "Evals": (".evals", "Evals"), + "AsyncEvals": (".evals", "AsyncEvals"), + "EvalsWithRawResponse": (".evals", "EvalsWithRawResponse"), + "AsyncEvalsWithRawResponse": (".evals", "AsyncEvalsWithRawResponse"), + "EvalsWithStreamingResponse": (".evals", "EvalsWithStreamingResponse"), + "AsyncEvalsWithStreamingResponse": (".evals", "AsyncEvalsWithStreamingResponse"), + } + _SUBMODULES = { + "evals", + "runs", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Runs", + "AsyncRuns", + "RunsWithRawResponse", + "AsyncRunsWithRawResponse", + "RunsWithStreamingResponse", + "AsyncRunsWithStreamingResponse", + "Evals", + "AsyncEvals", + "EvalsWithRawResponse", + "AsyncEvalsWithRawResponse", + "EvalsWithStreamingResponse", + "AsyncEvalsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/evals/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/evals/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..678bcce Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/evals/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/evals/__pycache__/evals.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/evals/__pycache__/evals.cpython-312.pyc new file mode 100644 index 0000000..f273fc6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/evals/__pycache__/evals.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/evals/evals.py b/venv/lib/python3.12/site-packages/openai/resources/evals/evals.py new file mode 100644 index 0000000..a6aa916 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/evals/evals.py @@ -0,0 +1,706 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal + +import httpx2 + +from ... import _legacy_response +from ...types import eval_list_params, eval_create_params, eval_update_params +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from .runs.runs import ( + Runs, + AsyncRuns, + RunsWithRawResponse, + AsyncRunsWithRawResponse, + RunsWithStreamingResponse, + AsyncRunsWithStreamingResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncCursorPage, AsyncCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.eval_list_response import EvalListResponse +from ...types.eval_create_response import EvalCreateResponse +from ...types.eval_delete_response import EvalDeleteResponse +from ...types.eval_update_response import EvalUpdateResponse +from ...types.eval_retrieve_response import EvalRetrieveResponse +from ...types.shared_params.metadata import Metadata + +__all__ = ["Evals", "AsyncEvals"] + + +class Evals(SyncAPIResource): + """Manage and run evals in the OpenAI platform.""" + + @cached_property + def runs(self) -> Runs: + """Manage and run evals in the OpenAI platform.""" + return Runs(self._client) + + @cached_property + def with_raw_response(self) -> EvalsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return EvalsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EvalsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return EvalsWithStreamingResponse(self) + + def create( + self, + *, + data_source_config: eval_create_params.DataSourceConfig, + testing_criteria: Iterable[eval_create_params.TestingCriterion], + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EvalCreateResponse: + """ + Create the structure of an evaluation that can be used to test a model's + performance. An evaluation is a set of testing criteria and the config for a + data source, which dictates the schema of the data used in the evaluation. After + creating an evaluation, you can run it on different models and model parameters. + We support several types of graders and datasources. For more information, see + the [Evals guide](https://developers.openai.com/api/docs/guides/evals). + + Args: + data_source_config: The configuration for the data source used for the evaluation runs. Dictates the + schema of the data used in the evaluation. + + testing_criteria: A list of graders for all eval runs in this group. Graders can reference + variables in the data source using double curly braces notation, like + `{{item.variable_name}}`. To reference the model's output, use the `sample` + namespace (ie, `{{sample.output_text}}`). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the evaluation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/evals", + body=maybe_transform( + { + "data_source_config": data_source_config, + "testing_criteria": testing_criteria, + "metadata": metadata, + "name": name, + }, + eval_create_params.EvalCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EvalCreateResponse, + ) + + def retrieve( + self, + eval_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EvalRetrieveResponse: + """ + Get an evaluation by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return self._get( + path_template("/evals/{eval_id}", eval_id=eval_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EvalRetrieveResponse, + ) + + def update( + self, + eval_id: str, + *, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EvalUpdateResponse: + """ + Update certain properties of an evaluation. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: Rename the evaluation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return self._post( + path_template("/evals/{eval_id}", eval_id=eval_id), + body=maybe_transform( + { + "metadata": metadata, + "name": name, + }, + eval_update_params.EvalUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EvalUpdateResponse, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + order_by: Literal["created_at", "updated_at"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[EvalListResponse]: + """ + List evaluations for a project. + + Args: + after: Identifier for the last eval from the previous pagination request. + + limit: Number of evals to retrieve. + + order: Sort order for evals by timestamp. Use `asc` for ascending order or `desc` for + descending order. + + order_by: Evals can be ordered by creation time or last updated time. Use `created_at` for + creation time or `updated_at` for last updated time. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/evals", + page=SyncCursorPage[EvalListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "order_by": order_by, + }, + eval_list_params.EvalListParams, + ), + security={"bearer_auth": True}, + ), + model=EvalListResponse, + ) + + def delete( + self, + eval_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EvalDeleteResponse: + """ + Delete an evaluation. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return self._delete( + path_template("/evals/{eval_id}", eval_id=eval_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EvalDeleteResponse, + ) + + +class AsyncEvals(AsyncAPIResource): + """Manage and run evals in the OpenAI platform.""" + + @cached_property + def runs(self) -> AsyncRuns: + """Manage and run evals in the OpenAI platform.""" + return AsyncRuns(self._client) + + @cached_property + def with_raw_response(self) -> AsyncEvalsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncEvalsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEvalsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncEvalsWithStreamingResponse(self) + + async def create( + self, + *, + data_source_config: eval_create_params.DataSourceConfig, + testing_criteria: Iterable[eval_create_params.TestingCriterion], + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EvalCreateResponse: + """ + Create the structure of an evaluation that can be used to test a model's + performance. An evaluation is a set of testing criteria and the config for a + data source, which dictates the schema of the data used in the evaluation. After + creating an evaluation, you can run it on different models and model parameters. + We support several types of graders and datasources. For more information, see + the [Evals guide](https://developers.openai.com/api/docs/guides/evals). + + Args: + data_source_config: The configuration for the data source used for the evaluation runs. Dictates the + schema of the data used in the evaluation. + + testing_criteria: A list of graders for all eval runs in this group. Graders can reference + variables in the data source using double curly braces notation, like + `{{item.variable_name}}`. To reference the model's output, use the `sample` + namespace (ie, `{{sample.output_text}}`). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the evaluation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/evals", + body=await async_maybe_transform( + { + "data_source_config": data_source_config, + "testing_criteria": testing_criteria, + "metadata": metadata, + "name": name, + }, + eval_create_params.EvalCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EvalCreateResponse, + ) + + async def retrieve( + self, + eval_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EvalRetrieveResponse: + """ + Get an evaluation by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return await self._get( + path_template("/evals/{eval_id}", eval_id=eval_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EvalRetrieveResponse, + ) + + async def update( + self, + eval_id: str, + *, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EvalUpdateResponse: + """ + Update certain properties of an evaluation. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: Rename the evaluation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return await self._post( + path_template("/evals/{eval_id}", eval_id=eval_id), + body=await async_maybe_transform( + { + "metadata": metadata, + "name": name, + }, + eval_update_params.EvalUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EvalUpdateResponse, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + order_by: Literal["created_at", "updated_at"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[EvalListResponse, AsyncCursorPage[EvalListResponse]]: + """ + List evaluations for a project. + + Args: + after: Identifier for the last eval from the previous pagination request. + + limit: Number of evals to retrieve. + + order: Sort order for evals by timestamp. Use `asc` for ascending order or `desc` for + descending order. + + order_by: Evals can be ordered by creation time or last updated time. Use `created_at` for + creation time or `updated_at` for last updated time. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/evals", + page=AsyncCursorPage[EvalListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "order_by": order_by, + }, + eval_list_params.EvalListParams, + ), + security={"bearer_auth": True}, + ), + model=EvalListResponse, + ) + + async def delete( + self, + eval_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> EvalDeleteResponse: + """ + Delete an evaluation. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return await self._delete( + path_template("/evals/{eval_id}", eval_id=eval_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=EvalDeleteResponse, + ) + + +class EvalsWithRawResponse: + def __init__(self, evals: Evals) -> None: + self._evals = evals + + self.create = _legacy_response.to_raw_response_wrapper( + evals.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + evals.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + evals.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + evals.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + evals.delete, + ) + + @cached_property + def runs(self) -> RunsWithRawResponse: + """Manage and run evals in the OpenAI platform.""" + return RunsWithRawResponse(self._evals.runs) + + +class AsyncEvalsWithRawResponse: + def __init__(self, evals: AsyncEvals) -> None: + self._evals = evals + + self.create = _legacy_response.async_to_raw_response_wrapper( + evals.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + evals.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + evals.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + evals.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + evals.delete, + ) + + @cached_property + def runs(self) -> AsyncRunsWithRawResponse: + """Manage and run evals in the OpenAI platform.""" + return AsyncRunsWithRawResponse(self._evals.runs) + + +class EvalsWithStreamingResponse: + def __init__(self, evals: Evals) -> None: + self._evals = evals + + self.create = to_streamed_response_wrapper( + evals.create, + ) + self.retrieve = to_streamed_response_wrapper( + evals.retrieve, + ) + self.update = to_streamed_response_wrapper( + evals.update, + ) + self.list = to_streamed_response_wrapper( + evals.list, + ) + self.delete = to_streamed_response_wrapper( + evals.delete, + ) + + @cached_property + def runs(self) -> RunsWithStreamingResponse: + """Manage and run evals in the OpenAI platform.""" + return RunsWithStreamingResponse(self._evals.runs) + + +class AsyncEvalsWithStreamingResponse: + def __init__(self, evals: AsyncEvals) -> None: + self._evals = evals + + self.create = async_to_streamed_response_wrapper( + evals.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + evals.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + evals.update, + ) + self.list = async_to_streamed_response_wrapper( + evals.list, + ) + self.delete = async_to_streamed_response_wrapper( + evals.delete, + ) + + @cached_property + def runs(self) -> AsyncRunsWithStreamingResponse: + """Manage and run evals in the OpenAI platform.""" + return AsyncRunsWithStreamingResponse(self._evals.runs) diff --git a/venv/lib/python3.12/site-packages/openai/resources/evals/runs/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/evals/runs/__init__.py new file mode 100644 index 0000000..653bf40 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/evals/runs/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .runs import ( + Runs as Runs, + AsyncRuns as AsyncRuns, + RunsWithRawResponse as RunsWithRawResponse, + AsyncRunsWithRawResponse as AsyncRunsWithRawResponse, + RunsWithStreamingResponse as RunsWithStreamingResponse, + AsyncRunsWithStreamingResponse as AsyncRunsWithStreamingResponse, + ) + from .output_items import ( + OutputItems as OutputItems, + AsyncOutputItems as AsyncOutputItems, + OutputItemsWithRawResponse as OutputItemsWithRawResponse, + AsyncOutputItemsWithRawResponse as AsyncOutputItemsWithRawResponse, + OutputItemsWithStreamingResponse as OutputItemsWithStreamingResponse, + AsyncOutputItemsWithStreamingResponse as AsyncOutputItemsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "OutputItems": (".output_items", "OutputItems"), + "AsyncOutputItems": (".output_items", "AsyncOutputItems"), + "OutputItemsWithRawResponse": (".output_items", "OutputItemsWithRawResponse"), + "AsyncOutputItemsWithRawResponse": (".output_items", "AsyncOutputItemsWithRawResponse"), + "OutputItemsWithStreamingResponse": (".output_items", "OutputItemsWithStreamingResponse"), + "AsyncOutputItemsWithStreamingResponse": (".output_items", "AsyncOutputItemsWithStreamingResponse"), + "Runs": (".runs", "Runs"), + "AsyncRuns": (".runs", "AsyncRuns"), + "RunsWithRawResponse": (".runs", "RunsWithRawResponse"), + "AsyncRunsWithRawResponse": (".runs", "AsyncRunsWithRawResponse"), + "RunsWithStreamingResponse": (".runs", "RunsWithStreamingResponse"), + "AsyncRunsWithStreamingResponse": (".runs", "AsyncRunsWithStreamingResponse"), + } + _SUBMODULES = { + "output_items", + "runs", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "OutputItems", + "AsyncOutputItems", + "OutputItemsWithRawResponse", + "AsyncOutputItemsWithRawResponse", + "OutputItemsWithStreamingResponse", + "AsyncOutputItemsWithStreamingResponse", + "Runs", + "AsyncRuns", + "RunsWithRawResponse", + "AsyncRunsWithRawResponse", + "RunsWithStreamingResponse", + "AsyncRunsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/evals/runs/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/evals/runs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..bdfd5cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/evals/runs/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/evals/runs/__pycache__/output_items.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/evals/runs/__pycache__/output_items.cpython-312.pyc new file mode 100644 index 0000000..c00456e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/evals/runs/__pycache__/output_items.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/evals/runs/__pycache__/runs.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/evals/runs/__pycache__/runs.cpython-312.pyc new file mode 100644 index 0000000..7b3f86d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/evals/runs/__pycache__/runs.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/evals/runs/output_items.py b/venv/lib/python3.12/site-packages/openai/resources/evals/runs/output_items.py new file mode 100644 index 0000000..4a26ccf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/evals/runs/output_items.py @@ -0,0 +1,339 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.evals.runs import output_item_list_params +from ....types.evals.runs.output_item_list_response import OutputItemListResponse +from ....types.evals.runs.output_item_retrieve_response import OutputItemRetrieveResponse + +__all__ = ["OutputItems", "AsyncOutputItems"] + + +class OutputItems(SyncAPIResource): + """Manage and run evals in the OpenAI platform.""" + + @cached_property + def with_raw_response(self) -> OutputItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return OutputItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> OutputItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return OutputItemsWithStreamingResponse(self) + + def retrieve( + self, + output_item_id: str, + *, + eval_id: str, + run_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OutputItemRetrieveResponse: + """ + Get an evaluation run output item by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + if not output_item_id: + raise ValueError(f"Expected a non-empty value for `output_item_id` but received {output_item_id!r}") + return self._get( + path_template( + "/evals/{eval_id}/runs/{run_id}/output_items/{output_item_id}", + eval_id=eval_id, + run_id=run_id, + output_item_id=output_item_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=OutputItemRetrieveResponse, + ) + + def list( + self, + run_id: str, + *, + eval_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: Literal["fail", "pass"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[OutputItemListResponse]: + """ + Get a list of output items for an evaluation run. + + Args: + after: Identifier for the last output item from the previous pagination request. + + limit: Number of output items to retrieve. + + order: Sort order for output items by timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `asc`. + + status: Filter output items by status. Use `failed` to filter by failed output items or + `pass` to filter by passed output items. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._get_api_list( + path_template("/evals/{eval_id}/runs/{run_id}/output_items", eval_id=eval_id, run_id=run_id), + page=SyncCursorPage[OutputItemListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "status": status, + }, + output_item_list_params.OutputItemListParams, + ), + security={"bearer_auth": True}, + ), + model=OutputItemListResponse, + ) + + +class AsyncOutputItems(AsyncAPIResource): + """Manage and run evals in the OpenAI platform.""" + + @cached_property + def with_raw_response(self) -> AsyncOutputItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncOutputItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncOutputItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncOutputItemsWithStreamingResponse(self) + + async def retrieve( + self, + output_item_id: str, + *, + eval_id: str, + run_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> OutputItemRetrieveResponse: + """ + Get an evaluation run output item by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + if not output_item_id: + raise ValueError(f"Expected a non-empty value for `output_item_id` but received {output_item_id!r}") + return await self._get( + path_template( + "/evals/{eval_id}/runs/{run_id}/output_items/{output_item_id}", + eval_id=eval_id, + run_id=run_id, + output_item_id=output_item_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=OutputItemRetrieveResponse, + ) + + def list( + self, + run_id: str, + *, + eval_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: Literal["fail", "pass"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[OutputItemListResponse, AsyncCursorPage[OutputItemListResponse]]: + """ + Get a list of output items for an evaluation run. + + Args: + after: Identifier for the last output item from the previous pagination request. + + limit: Number of output items to retrieve. + + order: Sort order for output items by timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `asc`. + + status: Filter output items by status. Use `failed` to filter by failed output items or + `pass` to filter by passed output items. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._get_api_list( + path_template("/evals/{eval_id}/runs/{run_id}/output_items", eval_id=eval_id, run_id=run_id), + page=AsyncCursorPage[OutputItemListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "status": status, + }, + output_item_list_params.OutputItemListParams, + ), + security={"bearer_auth": True}, + ), + model=OutputItemListResponse, + ) + + +class OutputItemsWithRawResponse: + def __init__(self, output_items: OutputItems) -> None: + self._output_items = output_items + + self.retrieve = _legacy_response.to_raw_response_wrapper( + output_items.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + output_items.list, + ) + + +class AsyncOutputItemsWithRawResponse: + def __init__(self, output_items: AsyncOutputItems) -> None: + self._output_items = output_items + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + output_items.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + output_items.list, + ) + + +class OutputItemsWithStreamingResponse: + def __init__(self, output_items: OutputItems) -> None: + self._output_items = output_items + + self.retrieve = to_streamed_response_wrapper( + output_items.retrieve, + ) + self.list = to_streamed_response_wrapper( + output_items.list, + ) + + +class AsyncOutputItemsWithStreamingResponse: + def __init__(self, output_items: AsyncOutputItems) -> None: + self._output_items = output_items + + self.retrieve = async_to_streamed_response_wrapper( + output_items.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + output_items.list, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/evals/runs/runs.py b/venv/lib/python3.12/site-packages/openai/resources/evals/runs/runs.py new file mode 100644 index 0000000..f57db15 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/evals/runs/runs.py @@ -0,0 +1,678 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .output_items import ( + OutputItems, + AsyncOutputItems, + OutputItemsWithRawResponse, + AsyncOutputItemsWithRawResponse, + OutputItemsWithStreamingResponse, + AsyncOutputItemsWithStreamingResponse, +) +from ....pagination import SyncCursorPage, AsyncCursorPage +from ....types.evals import run_list_params, run_create_params +from ...._base_client import AsyncPaginator, make_request_options +from ....types.shared_params.metadata import Metadata +from ....types.evals.run_list_response import RunListResponse +from ....types.evals.run_cancel_response import RunCancelResponse +from ....types.evals.run_create_response import RunCreateResponse +from ....types.evals.run_delete_response import RunDeleteResponse +from ....types.evals.run_retrieve_response import RunRetrieveResponse + +__all__ = ["Runs", "AsyncRuns"] + + +class Runs(SyncAPIResource): + """Manage and run evals in the OpenAI platform.""" + + @cached_property + def output_items(self) -> OutputItems: + """Manage and run evals in the OpenAI platform.""" + return OutputItems(self._client) + + @cached_property + def with_raw_response(self) -> RunsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RunsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RunsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RunsWithStreamingResponse(self) + + def create( + self, + eval_id: str, + *, + data_source: run_create_params.DataSource, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RunCreateResponse: + """ + Kicks off a new run for a given evaluation, specifying the data source, and what + model configuration to use to test. The datasource will be validated against the + schema specified in the config of the evaluation. + + Args: + data_source: Details about the run's data source. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return self._post( + path_template("/evals/{eval_id}/runs", eval_id=eval_id), + body=maybe_transform( + { + "data_source": data_source, + "metadata": metadata, + "name": name, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=RunCreateResponse, + ) + + def retrieve( + self, + run_id: str, + *, + eval_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RunRetrieveResponse: + """ + Get an evaluation run by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._get( + path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=RunRetrieveResponse, + ) + + def list( + self, + eval_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: Literal["queued", "in_progress", "completed", "canceled", "failed"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[RunListResponse]: + """ + Get a list of runs for an evaluation. + + Args: + after: Identifier for the last run from the previous pagination request. + + limit: Number of runs to retrieve. + + order: Sort order for runs by timestamp. Use `asc` for ascending order or `desc` for + descending order. Defaults to `asc`. + + status: Filter runs by status. One of `queued` | `in_progress` | `failed` | `completed` + | `canceled`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return self._get_api_list( + path_template("/evals/{eval_id}/runs", eval_id=eval_id), + page=SyncCursorPage[RunListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "status": status, + }, + run_list_params.RunListParams, + ), + security={"bearer_auth": True}, + ), + model=RunListResponse, + ) + + def delete( + self, + run_id: str, + *, + eval_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RunDeleteResponse: + """ + Delete an eval run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._delete( + path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=RunDeleteResponse, + ) + + def cancel( + self, + run_id: str, + *, + eval_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RunCancelResponse: + """ + Cancel an ongoing evaluation run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._post( + path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=RunCancelResponse, + ) + + +class AsyncRuns(AsyncAPIResource): + """Manage and run evals in the OpenAI platform.""" + + @cached_property + def output_items(self) -> AsyncOutputItems: + """Manage and run evals in the OpenAI platform.""" + return AsyncOutputItems(self._client) + + @cached_property + def with_raw_response(self) -> AsyncRunsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRunsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRunsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRunsWithStreamingResponse(self) + + async def create( + self, + eval_id: str, + *, + data_source: run_create_params.DataSource, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RunCreateResponse: + """ + Kicks off a new run for a given evaluation, specifying the data source, and what + model configuration to use to test. The datasource will be validated against the + schema specified in the config of the evaluation. + + Args: + data_source: Details about the run's data source. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return await self._post( + path_template("/evals/{eval_id}/runs", eval_id=eval_id), + body=await async_maybe_transform( + { + "data_source": data_source, + "metadata": metadata, + "name": name, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=RunCreateResponse, + ) + + async def retrieve( + self, + run_id: str, + *, + eval_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RunRetrieveResponse: + """ + Get an evaluation run by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return await self._get( + path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=RunRetrieveResponse, + ) + + def list( + self, + eval_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: Literal["queued", "in_progress", "completed", "canceled", "failed"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[RunListResponse, AsyncCursorPage[RunListResponse]]: + """ + Get a list of runs for an evaluation. + + Args: + after: Identifier for the last run from the previous pagination request. + + limit: Number of runs to retrieve. + + order: Sort order for runs by timestamp. Use `asc` for ascending order or `desc` for + descending order. Defaults to `asc`. + + status: Filter runs by status. One of `queued` | `in_progress` | `failed` | `completed` + | `canceled`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return self._get_api_list( + path_template("/evals/{eval_id}/runs", eval_id=eval_id), + page=AsyncCursorPage[RunListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "status": status, + }, + run_list_params.RunListParams, + ), + security={"bearer_auth": True}, + ), + model=RunListResponse, + ) + + async def delete( + self, + run_id: str, + *, + eval_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RunDeleteResponse: + """ + Delete an eval run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return await self._delete( + path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=RunDeleteResponse, + ) + + async def cancel( + self, + run_id: str, + *, + eval_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> RunCancelResponse: + """ + Cancel an ongoing evaluation run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return await self._post( + path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=RunCancelResponse, + ) + + +class RunsWithRawResponse: + def __init__(self, runs: Runs) -> None: + self._runs = runs + + self.create = _legacy_response.to_raw_response_wrapper( + runs.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + runs.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + runs.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + runs.delete, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + runs.cancel, + ) + + @cached_property + def output_items(self) -> OutputItemsWithRawResponse: + """Manage and run evals in the OpenAI platform.""" + return OutputItemsWithRawResponse(self._runs.output_items) + + +class AsyncRunsWithRawResponse: + def __init__(self, runs: AsyncRuns) -> None: + self._runs = runs + + self.create = _legacy_response.async_to_raw_response_wrapper( + runs.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + runs.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + runs.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + runs.delete, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + runs.cancel, + ) + + @cached_property + def output_items(self) -> AsyncOutputItemsWithRawResponse: + """Manage and run evals in the OpenAI platform.""" + return AsyncOutputItemsWithRawResponse(self._runs.output_items) + + +class RunsWithStreamingResponse: + def __init__(self, runs: Runs) -> None: + self._runs = runs + + self.create = to_streamed_response_wrapper( + runs.create, + ) + self.retrieve = to_streamed_response_wrapper( + runs.retrieve, + ) + self.list = to_streamed_response_wrapper( + runs.list, + ) + self.delete = to_streamed_response_wrapper( + runs.delete, + ) + self.cancel = to_streamed_response_wrapper( + runs.cancel, + ) + + @cached_property + def output_items(self) -> OutputItemsWithStreamingResponse: + """Manage and run evals in the OpenAI platform.""" + return OutputItemsWithStreamingResponse(self._runs.output_items) + + +class AsyncRunsWithStreamingResponse: + def __init__(self, runs: AsyncRuns) -> None: + self._runs = runs + + self.create = async_to_streamed_response_wrapper( + runs.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + runs.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + runs.list, + ) + self.delete = async_to_streamed_response_wrapper( + runs.delete, + ) + self.cancel = async_to_streamed_response_wrapper( + runs.cancel, + ) + + @cached_property + def output_items(self) -> AsyncOutputItemsWithStreamingResponse: + """Manage and run evals in the OpenAI platform.""" + return AsyncOutputItemsWithStreamingResponse(self._runs.output_items) diff --git a/venv/lib/python3.12/site-packages/openai/resources/files.py b/venv/lib/python3.12/site-packages/openai/resources/files.py new file mode 100644 index 0000000..db2e77c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/files.py @@ -0,0 +1,826 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import typing_extensions +from typing import Mapping, cast +from typing_extensions import Literal + +import httpx2 + +from .. import _legacy_response +from ..types import FilePurpose, file_list_params, file_create_params +from .._files import deepcopy_with_paths +from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given +from .._utils import extract_files, path_template, maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_streamed_response_wrapper, + async_to_streamed_response_wrapper, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ..lib._files import ( + wait_for_file_processing as _wait_for_file_processing, + async_wait_for_file_processing as _async_wait_for_file_processing, +) +from ..pagination import SyncCursorPage, AsyncCursorPage +from .._base_client import AsyncPaginator, make_request_options +from ..types.file_object import FileObject +from ..types.file_deleted import FileDeleted +from ..types.file_purpose import FilePurpose + +__all__ = ["Files", "AsyncFiles"] + + +class Files(SyncAPIResource): + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ + + @cached_property + def with_raw_response(self) -> FilesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return FilesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FilesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return FilesWithStreamingResponse(self) + + def create( + self, + *, + file: FileTypes, + purpose: FilePurpose, + expires_after: file_create_params.ExpiresAfter | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FileObject: + """Upload a file that can be used across various endpoints. + + Individual files can be + up to 512 MB, and each project can store up to 2.5 TB of files in total. There + is no organization-wide storage limit. Uploads to this endpoint are rate-limited + to 1,000 requests per minute per authenticated user. + + - The Assistants API supports files up to 2 million tokens and of specific file + types. See the + [Assistants Tools guide](https://developers.openai.com/api/docs/guides/tools) + for details. + - The Fine-tuning API only supports `.jsonl` files. The input also has certain + required formats for fine-tuning + [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) + or + [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) + models. + - The Batch API only supports `.jsonl` files up to 200 MB in size. The input + also has a specific required + [format](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file). + - For Retrieval or `file_search` ingestion, upload files here first. If you need + to attach multiple uploaded files to the same vector store, use + [`/vector_stores/{vector_store_id}/file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create) + instead of attaching them one by one. Vector store attachment has separate + limits from file upload, including 2,000 attached files per minute per + organization. + + Please [contact us](https://help.openai.com/) if you need to increase these + storage limits. + + Args: + file: The File object (not file name) to be uploaded. + + purpose: + The intended purpose of the uploaded file. One of: + + - `assistants`: Used in the Assistants API + - `batch`: Used in the Batch API + - `fine-tune`: Used for fine-tuning + - `vision`: Images used for vision fine-tuning + - `user_data`: Flexible file type for any purpose + - `evals`: Used for eval data sets + + expires_after: The expiration policy for a file. By default, files with `purpose=batch` expire + after 30 days and all other files are persisted until they are manually deleted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "file": file, + "purpose": purpose, + "expires_after": expires_after, + }, + [["file"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/files", + body=maybe_transform(body, file_create_params.FileCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FileObject, + ) + + def retrieve( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FileObject: + """ + Returns information about a specific file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return self._get( + path_template("/files/{file_id}", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FileObject, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + purpose: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[FileObject]: + """Returns a list of files. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 10,000, and the default is 10,000. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + purpose: Only return files with the given purpose. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/files", + page=SyncCursorPage[FileObject], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "purpose": purpose, + }, + file_list_params.FileListParams, + ), + security={"bearer_auth": True}, + ), + model=FileObject, + ) + + def delete( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FileDeleted: + """ + Delete a file and remove it from all vector stores. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return self._delete( + path_template("/files/{file_id}", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FileDeleted, + ) + + def content( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Returns a response containing the contents of the specified file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return self._get( + path_template("/files/{file_id}/content", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + @typing_extensions.deprecated("The `.content()` method should be used instead") + def retrieve_content( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> str: + """ + Returns a response containing the contents of the specified file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return self._get( + path_template("/files/{file_id}/content", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=str, + ) + + def wait_for_processing( + self, + id: str, + *, + poll_interval: float = 5.0, + max_wait_seconds: float = 30 * 60, + ) -> FileObject: + """Waits for the given file to be processed, default timeout is 30 mins.""" + return _wait_for_file_processing(self, id, poll_interval=poll_interval, max_wait_seconds=max_wait_seconds) + + +class AsyncFiles(AsyncAPIResource): + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ + + @cached_property + def with_raw_response(self) -> AsyncFilesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncFilesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFilesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncFilesWithStreamingResponse(self) + + async def create( + self, + *, + file: FileTypes, + purpose: FilePurpose, + expires_after: file_create_params.ExpiresAfter | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FileObject: + """Upload a file that can be used across various endpoints. + + Individual files can be + up to 512 MB, and each project can store up to 2.5 TB of files in total. There + is no organization-wide storage limit. Uploads to this endpoint are rate-limited + to 1,000 requests per minute per authenticated user. + + - The Assistants API supports files up to 2 million tokens and of specific file + types. See the + [Assistants Tools guide](https://developers.openai.com/api/docs/guides/tools) + for details. + - The Fine-tuning API only supports `.jsonl` files. The input also has certain + required formats for fine-tuning + [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) + or + [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) + models. + - The Batch API only supports `.jsonl` files up to 200 MB in size. The input + also has a specific required + [format](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file). + - For Retrieval or `file_search` ingestion, upload files here first. If you need + to attach multiple uploaded files to the same vector store, use + [`/vector_stores/{vector_store_id}/file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create) + instead of attaching them one by one. Vector store attachment has separate + limits from file upload, including 2,000 attached files per minute per + organization. + + Please [contact us](https://help.openai.com/) if you need to increase these + storage limits. + + Args: + file: The File object (not file name) to be uploaded. + + purpose: + The intended purpose of the uploaded file. One of: + + - `assistants`: Used in the Assistants API + - `batch`: Used in the Batch API + - `fine-tune`: Used for fine-tuning + - `vision`: Images used for vision fine-tuning + - `user_data`: Flexible file type for any purpose + - `evals`: Used for eval data sets + + expires_after: The expiration policy for a file. By default, files with `purpose=batch` expire + after 30 days and all other files are persisted until they are manually deleted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "file": file, + "purpose": purpose, + "expires_after": expires_after, + }, + [["file"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/files", + body=await async_maybe_transform(body, file_create_params.FileCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FileObject, + ) + + async def retrieve( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FileObject: + """ + Returns information about a specific file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return await self._get( + path_template("/files/{file_id}", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FileObject, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + purpose: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[FileObject, AsyncCursorPage[FileObject]]: + """Returns a list of files. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 10,000, and the default is 10,000. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + purpose: Only return files with the given purpose. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/files", + page=AsyncCursorPage[FileObject], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "purpose": purpose, + }, + file_list_params.FileListParams, + ), + security={"bearer_auth": True}, + ), + model=FileObject, + ) + + async def delete( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FileDeleted: + """ + Delete a file and remove it from all vector stores. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return await self._delete( + path_template("/files/{file_id}", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FileDeleted, + ) + + async def content( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Returns a response containing the contents of the specified file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return await self._get( + path_template("/files/{file_id}/content", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + @typing_extensions.deprecated("The `.content()` method should be used instead") + async def retrieve_content( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> str: + """ + Returns a response containing the contents of the specified file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return await self._get( + path_template("/files/{file_id}/content", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=str, + ) + + async def wait_for_processing( + self, + id: str, + *, + poll_interval: float = 5.0, + max_wait_seconds: float = 30 * 60, + ) -> FileObject: + """Waits for the given file to be processed, default timeout is 30 mins.""" + return await _async_wait_for_file_processing( + self, id, poll_interval=poll_interval, max_wait_seconds=max_wait_seconds + ) + + +class FilesWithRawResponse: + def __init__(self, files: Files) -> None: + self._files = files + + self.create = _legacy_response.to_raw_response_wrapper( + files.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + files.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + files.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + files.delete, + ) + self.content = _legacy_response.to_raw_response_wrapper( + files.content, + ) + self.retrieve_content = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + files.retrieve_content, # pyright: ignore[reportDeprecated], + ) + ) + + +class AsyncFilesWithRawResponse: + def __init__(self, files: AsyncFiles) -> None: + self._files = files + + self.create = _legacy_response.async_to_raw_response_wrapper( + files.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + files.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + files.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + files.delete, + ) + self.content = _legacy_response.async_to_raw_response_wrapper( + files.content, + ) + self.retrieve_content = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + files.retrieve_content, # pyright: ignore[reportDeprecated], + ) + ) + + +class FilesWithStreamingResponse: + def __init__(self, files: Files) -> None: + self._files = files + + self.create = to_streamed_response_wrapper( + files.create, + ) + self.retrieve = to_streamed_response_wrapper( + files.retrieve, + ) + self.list = to_streamed_response_wrapper( + files.list, + ) + self.delete = to_streamed_response_wrapper( + files.delete, + ) + self.content = to_custom_streamed_response_wrapper( + files.content, + StreamedBinaryAPIResponse, + ) + self.retrieve_content = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + files.retrieve_content, # pyright: ignore[reportDeprecated], + ) + ) + + +class AsyncFilesWithStreamingResponse: + def __init__(self, files: AsyncFiles) -> None: + self._files = files + + self.create = async_to_streamed_response_wrapper( + files.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + files.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + files.list, + ) + self.delete = async_to_streamed_response_wrapper( + files.delete, + ) + self.content = async_to_custom_streamed_response_wrapper( + files.content, + AsyncStreamedBinaryAPIResponse, + ) + self.retrieve_content = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + files.retrieve_content, # pyright: ignore[reportDeprecated], + ) + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/__init__.py new file mode 100644 index 0000000..f109da5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/__init__.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .jobs import ( + Jobs as Jobs, + AsyncJobs as AsyncJobs, + JobsWithRawResponse as JobsWithRawResponse, + AsyncJobsWithRawResponse as AsyncJobsWithRawResponse, + JobsWithStreamingResponse as JobsWithStreamingResponse, + AsyncJobsWithStreamingResponse as AsyncJobsWithStreamingResponse, + ) + from .alpha import ( + Alpha as Alpha, + AsyncAlpha as AsyncAlpha, + AlphaWithRawResponse as AlphaWithRawResponse, + AsyncAlphaWithRawResponse as AsyncAlphaWithRawResponse, + AlphaWithStreamingResponse as AlphaWithStreamingResponse, + AsyncAlphaWithStreamingResponse as AsyncAlphaWithStreamingResponse, + ) + from .checkpoints import ( + Checkpoints as Checkpoints, + AsyncCheckpoints as AsyncCheckpoints, + CheckpointsWithRawResponse as CheckpointsWithRawResponse, + AsyncCheckpointsWithRawResponse as AsyncCheckpointsWithRawResponse, + CheckpointsWithStreamingResponse as CheckpointsWithStreamingResponse, + AsyncCheckpointsWithStreamingResponse as AsyncCheckpointsWithStreamingResponse, + ) + from .fine_tuning import ( + FineTuning as FineTuning, + AsyncFineTuning as AsyncFineTuning, + FineTuningWithRawResponse as FineTuningWithRawResponse, + AsyncFineTuningWithRawResponse as AsyncFineTuningWithRawResponse, + FineTuningWithStreamingResponse as FineTuningWithStreamingResponse, + AsyncFineTuningWithStreamingResponse as AsyncFineTuningWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Jobs": (".jobs", "Jobs"), + "AsyncJobs": (".jobs", "AsyncJobs"), + "JobsWithRawResponse": (".jobs", "JobsWithRawResponse"), + "AsyncJobsWithRawResponse": (".jobs", "AsyncJobsWithRawResponse"), + "JobsWithStreamingResponse": (".jobs", "JobsWithStreamingResponse"), + "AsyncJobsWithStreamingResponse": (".jobs", "AsyncJobsWithStreamingResponse"), + "Checkpoints": (".checkpoints", "Checkpoints"), + "AsyncCheckpoints": (".checkpoints", "AsyncCheckpoints"), + "CheckpointsWithRawResponse": (".checkpoints", "CheckpointsWithRawResponse"), + "AsyncCheckpointsWithRawResponse": (".checkpoints", "AsyncCheckpointsWithRawResponse"), + "CheckpointsWithStreamingResponse": (".checkpoints", "CheckpointsWithStreamingResponse"), + "AsyncCheckpointsWithStreamingResponse": (".checkpoints", "AsyncCheckpointsWithStreamingResponse"), + "Alpha": (".alpha", "Alpha"), + "AsyncAlpha": (".alpha", "AsyncAlpha"), + "AlphaWithRawResponse": (".alpha", "AlphaWithRawResponse"), + "AsyncAlphaWithRawResponse": (".alpha", "AsyncAlphaWithRawResponse"), + "AlphaWithStreamingResponse": (".alpha", "AlphaWithStreamingResponse"), + "AsyncAlphaWithStreamingResponse": (".alpha", "AsyncAlphaWithStreamingResponse"), + "FineTuning": (".fine_tuning", "FineTuning"), + "AsyncFineTuning": (".fine_tuning", "AsyncFineTuning"), + "FineTuningWithRawResponse": (".fine_tuning", "FineTuningWithRawResponse"), + "AsyncFineTuningWithRawResponse": (".fine_tuning", "AsyncFineTuningWithRawResponse"), + "FineTuningWithStreamingResponse": (".fine_tuning", "FineTuningWithStreamingResponse"), + "AsyncFineTuningWithStreamingResponse": (".fine_tuning", "AsyncFineTuningWithStreamingResponse"), + } + _SUBMODULES = { + "alpha", + "checkpoints", + "fine_tuning", + "jobs", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Jobs", + "AsyncJobs", + "JobsWithRawResponse", + "AsyncJobsWithRawResponse", + "JobsWithStreamingResponse", + "AsyncJobsWithStreamingResponse", + "Checkpoints", + "AsyncCheckpoints", + "CheckpointsWithRawResponse", + "AsyncCheckpointsWithRawResponse", + "CheckpointsWithStreamingResponse", + "AsyncCheckpointsWithStreamingResponse", + "Alpha", + "AsyncAlpha", + "AlphaWithRawResponse", + "AsyncAlphaWithRawResponse", + "AlphaWithStreamingResponse", + "AsyncAlphaWithStreamingResponse", + "FineTuning", + "AsyncFineTuning", + "FineTuningWithRawResponse", + "AsyncFineTuningWithRawResponse", + "FineTuningWithStreamingResponse", + "AsyncFineTuningWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..590ae2c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/__pycache__/fine_tuning.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/__pycache__/fine_tuning.cpython-312.pyc new file mode 100644 index 0000000..51717b7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/__pycache__/fine_tuning.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/__init__.py new file mode 100644 index 0000000..2600c0a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .alpha import ( + Alpha as Alpha, + AsyncAlpha as AsyncAlpha, + AlphaWithRawResponse as AlphaWithRawResponse, + AsyncAlphaWithRawResponse as AsyncAlphaWithRawResponse, + AlphaWithStreamingResponse as AlphaWithStreamingResponse, + AsyncAlphaWithStreamingResponse as AsyncAlphaWithStreamingResponse, + ) + from .graders import ( + Graders as Graders, + AsyncGraders as AsyncGraders, + GradersWithRawResponse as GradersWithRawResponse, + AsyncGradersWithRawResponse as AsyncGradersWithRawResponse, + GradersWithStreamingResponse as GradersWithStreamingResponse, + AsyncGradersWithStreamingResponse as AsyncGradersWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Graders": (".graders", "Graders"), + "AsyncGraders": (".graders", "AsyncGraders"), + "GradersWithRawResponse": (".graders", "GradersWithRawResponse"), + "AsyncGradersWithRawResponse": (".graders", "AsyncGradersWithRawResponse"), + "GradersWithStreamingResponse": (".graders", "GradersWithStreamingResponse"), + "AsyncGradersWithStreamingResponse": (".graders", "AsyncGradersWithStreamingResponse"), + "Alpha": (".alpha", "Alpha"), + "AsyncAlpha": (".alpha", "AsyncAlpha"), + "AlphaWithRawResponse": (".alpha", "AlphaWithRawResponse"), + "AsyncAlphaWithRawResponse": (".alpha", "AsyncAlphaWithRawResponse"), + "AlphaWithStreamingResponse": (".alpha", "AlphaWithStreamingResponse"), + "AsyncAlphaWithStreamingResponse": (".alpha", "AsyncAlphaWithStreamingResponse"), + } + _SUBMODULES = { + "alpha", + "graders", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Graders", + "AsyncGraders", + "GradersWithRawResponse", + "AsyncGradersWithRawResponse", + "GradersWithStreamingResponse", + "AsyncGradersWithStreamingResponse", + "Alpha", + "AsyncAlpha", + "AlphaWithRawResponse", + "AsyncAlphaWithRawResponse", + "AlphaWithStreamingResponse", + "AsyncAlphaWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2cc839e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/__pycache__/alpha.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/__pycache__/alpha.cpython-312.pyc new file mode 100644 index 0000000..7e0d42d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/__pycache__/alpha.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/__pycache__/graders.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/__pycache__/graders.cpython-312.pyc new file mode 100644 index 0000000..5e21721 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/__pycache__/graders.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/alpha.py b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/alpha.py new file mode 100644 index 0000000..237d701 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/alpha.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .graders import ( + Graders, + AsyncGraders, + GradersWithRawResponse, + AsyncGradersWithRawResponse, + GradersWithStreamingResponse, + AsyncGradersWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["Alpha", "AsyncAlpha"] + + +class Alpha(SyncAPIResource): + @cached_property + def graders(self) -> Graders: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return Graders(self._client) + + @cached_property + def with_raw_response(self) -> AlphaWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AlphaWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AlphaWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AlphaWithStreamingResponse(self) + + +class AsyncAlpha(AsyncAPIResource): + @cached_property + def graders(self) -> AsyncGraders: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return AsyncGraders(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAlphaWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAlphaWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAlphaWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAlphaWithStreamingResponse(self) + + +class AlphaWithRawResponse: + def __init__(self, alpha: Alpha) -> None: + self._alpha = alpha + + @cached_property + def graders(self) -> GradersWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return GradersWithRawResponse(self._alpha.graders) + + +class AsyncAlphaWithRawResponse: + def __init__(self, alpha: AsyncAlpha) -> None: + self._alpha = alpha + + @cached_property + def graders(self) -> AsyncGradersWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return AsyncGradersWithRawResponse(self._alpha.graders) + + +class AlphaWithStreamingResponse: + def __init__(self, alpha: Alpha) -> None: + self._alpha = alpha + + @cached_property + def graders(self) -> GradersWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return GradersWithStreamingResponse(self._alpha.graders) + + +class AsyncAlphaWithStreamingResponse: + def __init__(self, alpha: AsyncAlpha) -> None: + self._alpha = alpha + + @cached_property + def graders(self) -> AsyncGradersWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return AsyncGradersWithStreamingResponse(self._alpha.graders) diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/graders.py b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/graders.py new file mode 100644 index 0000000..cf70c5f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/alpha/graders.py @@ -0,0 +1,304 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.fine_tuning.alpha import grader_run_params, grader_validate_params +from ....types.fine_tuning.alpha.grader_run_response import GraderRunResponse +from ....types.fine_tuning.alpha.grader_validate_response import GraderValidateResponse + +__all__ = ["Graders", "AsyncGraders"] + + +class Graders(SyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + + @cached_property + def with_raw_response(self) -> GradersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return GradersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GradersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return GradersWithStreamingResponse(self) + + def run( + self, + *, + grader: grader_run_params.Grader, + model_sample: str, + item: object | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> GraderRunResponse: + """ + Run a grader. + + Args: + grader: The grader used for the fine-tuning job. + + model_sample: The model sample to be evaluated. This value will be used to populate the + `sample` namespace. See + [the guide](https://developers.openai.com/api/docs/guides/graders) for more + details. The `output_json` variable will be populated if the model sample is a + valid JSON string. + + item: The dataset item provided to the grader. This will be used to populate the + `item` namespace. See + [the guide](https://developers.openai.com/api/docs/guides/graders) for more + details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/fine_tuning/alpha/graders/run", + body=maybe_transform( + { + "grader": grader, + "model_sample": model_sample, + "item": item, + }, + grader_run_params.GraderRunParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=GraderRunResponse, + ) + + def validate( + self, + *, + grader: grader_validate_params.Grader, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> GraderValidateResponse: + """ + Validate a grader. + + Args: + grader: The grader used for the fine-tuning job. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/fine_tuning/alpha/graders/validate", + body=maybe_transform({"grader": grader}, grader_validate_params.GraderValidateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=GraderValidateResponse, + ) + + +class AsyncGraders(AsyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + + @cached_property + def with_raw_response(self) -> AsyncGradersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncGradersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGradersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncGradersWithStreamingResponse(self) + + async def run( + self, + *, + grader: grader_run_params.Grader, + model_sample: str, + item: object | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> GraderRunResponse: + """ + Run a grader. + + Args: + grader: The grader used for the fine-tuning job. + + model_sample: The model sample to be evaluated. This value will be used to populate the + `sample` namespace. See + [the guide](https://developers.openai.com/api/docs/guides/graders) for more + details. The `output_json` variable will be populated if the model sample is a + valid JSON string. + + item: The dataset item provided to the grader. This will be used to populate the + `item` namespace. See + [the guide](https://developers.openai.com/api/docs/guides/graders) for more + details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/fine_tuning/alpha/graders/run", + body=await async_maybe_transform( + { + "grader": grader, + "model_sample": model_sample, + "item": item, + }, + grader_run_params.GraderRunParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=GraderRunResponse, + ) + + async def validate( + self, + *, + grader: grader_validate_params.Grader, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> GraderValidateResponse: + """ + Validate a grader. + + Args: + grader: The grader used for the fine-tuning job. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/fine_tuning/alpha/graders/validate", + body=await async_maybe_transform({"grader": grader}, grader_validate_params.GraderValidateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=GraderValidateResponse, + ) + + +class GradersWithRawResponse: + def __init__(self, graders: Graders) -> None: + self._graders = graders + + self.run = _legacy_response.to_raw_response_wrapper( + graders.run, + ) + self.validate = _legacy_response.to_raw_response_wrapper( + graders.validate, + ) + + +class AsyncGradersWithRawResponse: + def __init__(self, graders: AsyncGraders) -> None: + self._graders = graders + + self.run = _legacy_response.async_to_raw_response_wrapper( + graders.run, + ) + self.validate = _legacy_response.async_to_raw_response_wrapper( + graders.validate, + ) + + +class GradersWithStreamingResponse: + def __init__(self, graders: Graders) -> None: + self._graders = graders + + self.run = to_streamed_response_wrapper( + graders.run, + ) + self.validate = to_streamed_response_wrapper( + graders.validate, + ) + + +class AsyncGradersWithStreamingResponse: + def __init__(self, graders: AsyncGraders) -> None: + self._graders = graders + + self.run = async_to_streamed_response_wrapper( + graders.run, + ) + self.validate = async_to_streamed_response_wrapper( + graders.validate, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/__init__.py new file mode 100644 index 0000000..b78a86c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .checkpoints import ( + Checkpoints as Checkpoints, + AsyncCheckpoints as AsyncCheckpoints, + CheckpointsWithRawResponse as CheckpointsWithRawResponse, + AsyncCheckpointsWithRawResponse as AsyncCheckpointsWithRawResponse, + CheckpointsWithStreamingResponse as CheckpointsWithStreamingResponse, + AsyncCheckpointsWithStreamingResponse as AsyncCheckpointsWithStreamingResponse, + ) + from .permissions import ( + Permissions as Permissions, + AsyncPermissions as AsyncPermissions, + PermissionsWithRawResponse as PermissionsWithRawResponse, + AsyncPermissionsWithRawResponse as AsyncPermissionsWithRawResponse, + PermissionsWithStreamingResponse as PermissionsWithStreamingResponse, + AsyncPermissionsWithStreamingResponse as AsyncPermissionsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Permissions": (".permissions", "Permissions"), + "AsyncPermissions": (".permissions", "AsyncPermissions"), + "PermissionsWithRawResponse": (".permissions", "PermissionsWithRawResponse"), + "AsyncPermissionsWithRawResponse": (".permissions", "AsyncPermissionsWithRawResponse"), + "PermissionsWithStreamingResponse": (".permissions", "PermissionsWithStreamingResponse"), + "AsyncPermissionsWithStreamingResponse": (".permissions", "AsyncPermissionsWithStreamingResponse"), + "Checkpoints": (".checkpoints", "Checkpoints"), + "AsyncCheckpoints": (".checkpoints", "AsyncCheckpoints"), + "CheckpointsWithRawResponse": (".checkpoints", "CheckpointsWithRawResponse"), + "AsyncCheckpointsWithRawResponse": (".checkpoints", "AsyncCheckpointsWithRawResponse"), + "CheckpointsWithStreamingResponse": (".checkpoints", "CheckpointsWithStreamingResponse"), + "AsyncCheckpointsWithStreamingResponse": (".checkpoints", "AsyncCheckpointsWithStreamingResponse"), + } + _SUBMODULES = { + "checkpoints", + "permissions", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Permissions", + "AsyncPermissions", + "PermissionsWithRawResponse", + "AsyncPermissionsWithRawResponse", + "PermissionsWithStreamingResponse", + "AsyncPermissionsWithStreamingResponse", + "Checkpoints", + "AsyncCheckpoints", + "CheckpointsWithRawResponse", + "AsyncCheckpointsWithRawResponse", + "CheckpointsWithStreamingResponse", + "AsyncCheckpointsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2145093 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/__pycache__/checkpoints.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/__pycache__/checkpoints.cpython-312.pyc new file mode 100644 index 0000000..96ceba0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/__pycache__/checkpoints.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/__pycache__/permissions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/__pycache__/permissions.cpython-312.pyc new file mode 100644 index 0000000..df82ed4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/__pycache__/permissions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/checkpoints.py b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/checkpoints.py new file mode 100644 index 0000000..a0d6251 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/checkpoints.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ...._compat import cached_property +from .permissions import ( + Permissions, + AsyncPermissions, + PermissionsWithRawResponse, + AsyncPermissionsWithRawResponse, + PermissionsWithStreamingResponse, + AsyncPermissionsWithStreamingResponse, +) +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["Checkpoints", "AsyncCheckpoints"] + + +class Checkpoints(SyncAPIResource): + @cached_property + def permissions(self) -> Permissions: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return Permissions(self._client) + + @cached_property + def with_raw_response(self) -> CheckpointsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CheckpointsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CheckpointsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CheckpointsWithStreamingResponse(self) + + +class AsyncCheckpoints(AsyncAPIResource): + @cached_property + def permissions(self) -> AsyncPermissions: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return AsyncPermissions(self._client) + + @cached_property + def with_raw_response(self) -> AsyncCheckpointsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCheckpointsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCheckpointsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCheckpointsWithStreamingResponse(self) + + +class CheckpointsWithRawResponse: + def __init__(self, checkpoints: Checkpoints) -> None: + self._checkpoints = checkpoints + + @cached_property + def permissions(self) -> PermissionsWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return PermissionsWithRawResponse(self._checkpoints.permissions) + + +class AsyncCheckpointsWithRawResponse: + def __init__(self, checkpoints: AsyncCheckpoints) -> None: + self._checkpoints = checkpoints + + @cached_property + def permissions(self) -> AsyncPermissionsWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return AsyncPermissionsWithRawResponse(self._checkpoints.permissions) + + +class CheckpointsWithStreamingResponse: + def __init__(self, checkpoints: Checkpoints) -> None: + self._checkpoints = checkpoints + + @cached_property + def permissions(self) -> PermissionsWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return PermissionsWithStreamingResponse(self._checkpoints.permissions) + + +class AsyncCheckpointsWithStreamingResponse: + def __init__(self, checkpoints: AsyncCheckpoints) -> None: + self._checkpoints = checkpoints + + @cached_property + def permissions(self) -> AsyncPermissionsWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return AsyncPermissionsWithStreamingResponse(self._checkpoints.permissions) diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/permissions.py b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/permissions.py new file mode 100644 index 0000000..7d2136f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/checkpoints/permissions.py @@ -0,0 +1,630 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import typing_extensions +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncPage, AsyncPage, SyncConversationCursorPage, AsyncConversationCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.fine_tuning.checkpoints import ( + permission_list_params, + permission_create_params, + permission_retrieve_params, +) +from ....types.fine_tuning.checkpoints.permission_list_response import PermissionListResponse +from ....types.fine_tuning.checkpoints.permission_create_response import PermissionCreateResponse +from ....types.fine_tuning.checkpoints.permission_delete_response import PermissionDeleteResponse +from ....types.fine_tuning.checkpoints.permission_retrieve_response import PermissionRetrieveResponse + +__all__ = ["Permissions", "AsyncPermissions"] + + +class Permissions(SyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + + @cached_property + def with_raw_response(self) -> PermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return PermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return PermissionsWithStreamingResponse(self) + + def create( + self, + fine_tuned_model_checkpoint: str, + *, + project_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncPage[PermissionCreateResponse]: + """ + **NOTE:** Calling this endpoint requires an + [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys). + + This enables organization owners to share fine-tuned models with other projects + in their organization. + + Args: + project_ids: The project identifiers to grant access to. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return self._get_api_list( + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + ), + page=SyncPage[PermissionCreateResponse], + body=maybe_transform({"project_ids": project_ids}, permission_create_params.PermissionCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + model=PermissionCreateResponse, + method="post", + ) + + @typing_extensions.deprecated("Retrieve is deprecated. Please swap to the paginated list method instead.") + def retrieve( + self, + fine_tuned_model_checkpoint: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["ascending", "descending"] | Omit = omit, + project_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> PermissionRetrieveResponse: + """ + **NOTE:** This endpoint requires an + [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys). + + Organization owners can use this endpoint to view all permissions for a + fine-tuned model checkpoint. + + Args: + after: Identifier for the last permission ID from the previous pagination request. + + limit: Number of permissions to retrieve. + + order: The order in which to retrieve permissions. + + project_id: The ID of the project to get permissions for. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return self._get( + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "project_id": project_id, + }, + permission_retrieve_params.PermissionRetrieveParams, + ), + security={"bearer_auth": True}, + ), + cast_to=PermissionRetrieveResponse, + ) + + def list( + self, + fine_tuned_model_checkpoint: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["ascending", "descending"] | Omit = omit, + project_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[PermissionListResponse]: + """ + **NOTE:** This endpoint requires an + [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys). + + Organization owners can use this endpoint to view all permissions for a + fine-tuned model checkpoint. + + Args: + after: Identifier for the last permission ID from the previous pagination request. + + limit: Number of permissions to retrieve. + + order: The order in which to retrieve permissions. + + project_id: The ID of the project to get permissions for. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return self._get_api_list( + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + ), + page=SyncConversationCursorPage[PermissionListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "project_id": project_id, + }, + permission_list_params.PermissionListParams, + ), + security={"bearer_auth": True}, + ), + model=PermissionListResponse, + ) + + def delete( + self, + permission_id: str, + *, + fine_tuned_model_checkpoint: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> PermissionDeleteResponse: + """ + **NOTE:** This endpoint requires an + [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys). + + Organization owners can use this endpoint to delete a permission for a + fine-tuned model checkpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + if not permission_id: + raise ValueError(f"Expected a non-empty value for `permission_id` but received {permission_id!r}") + return self._delete( + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + permission_id=permission_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=PermissionDeleteResponse, + ) + + +class AsyncPermissions(AsyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + + @cached_property + def with_raw_response(self) -> AsyncPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncPermissionsWithStreamingResponse(self) + + def create( + self, + fine_tuned_model_checkpoint: str, + *, + project_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[PermissionCreateResponse, AsyncPage[PermissionCreateResponse]]: + """ + **NOTE:** Calling this endpoint requires an + [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys). + + This enables organization owners to share fine-tuned models with other projects + in their organization. + + Args: + project_ids: The project identifiers to grant access to. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return self._get_api_list( + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + ), + page=AsyncPage[PermissionCreateResponse], + body=maybe_transform({"project_ids": project_ids}, permission_create_params.PermissionCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + model=PermissionCreateResponse, + method="post", + ) + + @typing_extensions.deprecated("Retrieve is deprecated. Please swap to the paginated list method instead.") + async def retrieve( + self, + fine_tuned_model_checkpoint: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["ascending", "descending"] | Omit = omit, + project_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> PermissionRetrieveResponse: + """ + **NOTE:** This endpoint requires an + [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys). + + Organization owners can use this endpoint to view all permissions for a + fine-tuned model checkpoint. + + Args: + after: Identifier for the last permission ID from the previous pagination request. + + limit: Number of permissions to retrieve. + + order: The order in which to retrieve permissions. + + project_id: The ID of the project to get permissions for. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return await self._get( + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "project_id": project_id, + }, + permission_retrieve_params.PermissionRetrieveParams, + ), + security={"bearer_auth": True}, + ), + cast_to=PermissionRetrieveResponse, + ) + + def list( + self, + fine_tuned_model_checkpoint: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["ascending", "descending"] | Omit = omit, + project_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[PermissionListResponse, AsyncConversationCursorPage[PermissionListResponse]]: + """ + **NOTE:** This endpoint requires an + [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys). + + Organization owners can use this endpoint to view all permissions for a + fine-tuned model checkpoint. + + Args: + after: Identifier for the last permission ID from the previous pagination request. + + limit: Number of permissions to retrieve. + + order: The order in which to retrieve permissions. + + project_id: The ID of the project to get permissions for. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return self._get_api_list( + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + ), + page=AsyncConversationCursorPage[PermissionListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "project_id": project_id, + }, + permission_list_params.PermissionListParams, + ), + security={"bearer_auth": True}, + ), + model=PermissionListResponse, + ) + + async def delete( + self, + permission_id: str, + *, + fine_tuned_model_checkpoint: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> PermissionDeleteResponse: + """ + **NOTE:** This endpoint requires an + [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys). + + Organization owners can use this endpoint to delete a permission for a + fine-tuned model checkpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + if not permission_id: + raise ValueError(f"Expected a non-empty value for `permission_id` but received {permission_id!r}") + return await self._delete( + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + permission_id=permission_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=PermissionDeleteResponse, + ) + + +class PermissionsWithRawResponse: + def __init__(self, permissions: Permissions) -> None: + self._permissions = permissions + + self.create = _legacy_response.to_raw_response_wrapper( + permissions.create, + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + permissions.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = _legacy_response.to_raw_response_wrapper( + permissions.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + permissions.delete, + ) + + +class AsyncPermissionsWithRawResponse: + def __init__(self, permissions: AsyncPermissions) -> None: + self._permissions = permissions + + self.create = _legacy_response.async_to_raw_response_wrapper( + permissions.create, + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + permissions.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + permissions.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + permissions.delete, + ) + + +class PermissionsWithStreamingResponse: + def __init__(self, permissions: Permissions) -> None: + self._permissions = permissions + + self.create = to_streamed_response_wrapper( + permissions.create, + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + permissions.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = to_streamed_response_wrapper( + permissions.list, + ) + self.delete = to_streamed_response_wrapper( + permissions.delete, + ) + + +class AsyncPermissionsWithStreamingResponse: + def __init__(self, permissions: AsyncPermissions) -> None: + self._permissions = permissions + + self.create = async_to_streamed_response_wrapper( + permissions.create, + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + permissions.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = async_to_streamed_response_wrapper( + permissions.list, + ) + self.delete = async_to_streamed_response_wrapper( + permissions.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/fine_tuning.py b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/fine_tuning.py new file mode 100644 index 0000000..2b6062d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/fine_tuning.py @@ -0,0 +1,172 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._compat import cached_property +from .jobs.jobs import ( + Jobs, + AsyncJobs, + JobsWithRawResponse, + AsyncJobsWithRawResponse, + JobsWithStreamingResponse, + AsyncJobsWithStreamingResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource +from .alpha.alpha import ( + Alpha, + AsyncAlpha, + AlphaWithRawResponse, + AsyncAlphaWithRawResponse, + AlphaWithStreamingResponse, + AsyncAlphaWithStreamingResponse, +) +from .checkpoints.checkpoints import ( + Checkpoints, + AsyncCheckpoints, + CheckpointsWithRawResponse, + AsyncCheckpointsWithRawResponse, + CheckpointsWithStreamingResponse, + AsyncCheckpointsWithStreamingResponse, +) + +__all__ = ["FineTuning", "AsyncFineTuning"] + + +class FineTuning(SyncAPIResource): + @cached_property + def jobs(self) -> Jobs: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return Jobs(self._client) + + @cached_property + def checkpoints(self) -> Checkpoints: + return Checkpoints(self._client) + + @cached_property + def alpha(self) -> Alpha: + return Alpha(self._client) + + @cached_property + def with_raw_response(self) -> FineTuningWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return FineTuningWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FineTuningWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return FineTuningWithStreamingResponse(self) + + +class AsyncFineTuning(AsyncAPIResource): + @cached_property + def jobs(self) -> AsyncJobs: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return AsyncJobs(self._client) + + @cached_property + def checkpoints(self) -> AsyncCheckpoints: + return AsyncCheckpoints(self._client) + + @cached_property + def alpha(self) -> AsyncAlpha: + return AsyncAlpha(self._client) + + @cached_property + def with_raw_response(self) -> AsyncFineTuningWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncFineTuningWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFineTuningWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncFineTuningWithStreamingResponse(self) + + +class FineTuningWithRawResponse: + def __init__(self, fine_tuning: FineTuning) -> None: + self._fine_tuning = fine_tuning + + @cached_property + def jobs(self) -> JobsWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return JobsWithRawResponse(self._fine_tuning.jobs) + + @cached_property + def checkpoints(self) -> CheckpointsWithRawResponse: + return CheckpointsWithRawResponse(self._fine_tuning.checkpoints) + + @cached_property + def alpha(self) -> AlphaWithRawResponse: + return AlphaWithRawResponse(self._fine_tuning.alpha) + + +class AsyncFineTuningWithRawResponse: + def __init__(self, fine_tuning: AsyncFineTuning) -> None: + self._fine_tuning = fine_tuning + + @cached_property + def jobs(self) -> AsyncJobsWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return AsyncJobsWithRawResponse(self._fine_tuning.jobs) + + @cached_property + def checkpoints(self) -> AsyncCheckpointsWithRawResponse: + return AsyncCheckpointsWithRawResponse(self._fine_tuning.checkpoints) + + @cached_property + def alpha(self) -> AsyncAlphaWithRawResponse: + return AsyncAlphaWithRawResponse(self._fine_tuning.alpha) + + +class FineTuningWithStreamingResponse: + def __init__(self, fine_tuning: FineTuning) -> None: + self._fine_tuning = fine_tuning + + @cached_property + def jobs(self) -> JobsWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return JobsWithStreamingResponse(self._fine_tuning.jobs) + + @cached_property + def checkpoints(self) -> CheckpointsWithStreamingResponse: + return CheckpointsWithStreamingResponse(self._fine_tuning.checkpoints) + + @cached_property + def alpha(self) -> AlphaWithStreamingResponse: + return AlphaWithStreamingResponse(self._fine_tuning.alpha) + + +class AsyncFineTuningWithStreamingResponse: + def __init__(self, fine_tuning: AsyncFineTuning) -> None: + self._fine_tuning = fine_tuning + + @cached_property + def jobs(self) -> AsyncJobsWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return AsyncJobsWithStreamingResponse(self._fine_tuning.jobs) + + @cached_property + def checkpoints(self) -> AsyncCheckpointsWithStreamingResponse: + return AsyncCheckpointsWithStreamingResponse(self._fine_tuning.checkpoints) + + @cached_property + def alpha(self) -> AsyncAlphaWithStreamingResponse: + return AsyncAlphaWithStreamingResponse(self._fine_tuning.alpha) diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/__init__.py new file mode 100644 index 0000000..06b7c3f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .jobs import ( + Jobs as Jobs, + AsyncJobs as AsyncJobs, + JobsWithRawResponse as JobsWithRawResponse, + AsyncJobsWithRawResponse as AsyncJobsWithRawResponse, + JobsWithStreamingResponse as JobsWithStreamingResponse, + AsyncJobsWithStreamingResponse as AsyncJobsWithStreamingResponse, + ) + from .checkpoints import ( + Checkpoints as Checkpoints, + AsyncCheckpoints as AsyncCheckpoints, + CheckpointsWithRawResponse as CheckpointsWithRawResponse, + AsyncCheckpointsWithRawResponse as AsyncCheckpointsWithRawResponse, + CheckpointsWithStreamingResponse as CheckpointsWithStreamingResponse, + AsyncCheckpointsWithStreamingResponse as AsyncCheckpointsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Checkpoints": (".checkpoints", "Checkpoints"), + "AsyncCheckpoints": (".checkpoints", "AsyncCheckpoints"), + "CheckpointsWithRawResponse": (".checkpoints", "CheckpointsWithRawResponse"), + "AsyncCheckpointsWithRawResponse": (".checkpoints", "AsyncCheckpointsWithRawResponse"), + "CheckpointsWithStreamingResponse": (".checkpoints", "CheckpointsWithStreamingResponse"), + "AsyncCheckpointsWithStreamingResponse": (".checkpoints", "AsyncCheckpointsWithStreamingResponse"), + "Jobs": (".jobs", "Jobs"), + "AsyncJobs": (".jobs", "AsyncJobs"), + "JobsWithRawResponse": (".jobs", "JobsWithRawResponse"), + "AsyncJobsWithRawResponse": (".jobs", "AsyncJobsWithRawResponse"), + "JobsWithStreamingResponse": (".jobs", "JobsWithStreamingResponse"), + "AsyncJobsWithStreamingResponse": (".jobs", "AsyncJobsWithStreamingResponse"), + } + _SUBMODULES = { + "checkpoints", + "jobs", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Checkpoints", + "AsyncCheckpoints", + "CheckpointsWithRawResponse", + "AsyncCheckpointsWithRawResponse", + "CheckpointsWithStreamingResponse", + "AsyncCheckpointsWithStreamingResponse", + "Jobs", + "AsyncJobs", + "JobsWithRawResponse", + "AsyncJobsWithRawResponse", + "JobsWithStreamingResponse", + "AsyncJobsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7026c92 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/__pycache__/checkpoints.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/__pycache__/checkpoints.cpython-312.pyc new file mode 100644 index 0000000..2b59b0a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/__pycache__/checkpoints.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/__pycache__/jobs.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/__pycache__/jobs.cpython-312.pyc new file mode 100644 index 0000000..5cbc308 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/__pycache__/jobs.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/checkpoints.py b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/checkpoints.py new file mode 100644 index 0000000..1768305 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/checkpoints.py @@ -0,0 +1,202 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.fine_tuning.jobs import checkpoint_list_params +from ....types.fine_tuning.jobs.fine_tuning_job_checkpoint import FineTuningJobCheckpoint + +__all__ = ["Checkpoints", "AsyncCheckpoints"] + + +class Checkpoints(SyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + + @cached_property + def with_raw_response(self) -> CheckpointsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CheckpointsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CheckpointsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CheckpointsWithStreamingResponse(self) + + def list( + self, + fine_tuning_job_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[FineTuningJobCheckpoint]: + """ + List checkpoints for a fine-tuning job. + + Args: + after: Identifier for the last checkpoint ID from the previous pagination request. + + limit: Number of checkpoints to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._get_api_list( + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints", fine_tuning_job_id=fine_tuning_job_id), + page=SyncCursorPage[FineTuningJobCheckpoint], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + checkpoint_list_params.CheckpointListParams, + ), + security={"bearer_auth": True}, + ), + model=FineTuningJobCheckpoint, + ) + + +class AsyncCheckpoints(AsyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + + @cached_property + def with_raw_response(self) -> AsyncCheckpointsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCheckpointsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCheckpointsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCheckpointsWithStreamingResponse(self) + + def list( + self, + fine_tuning_job_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[FineTuningJobCheckpoint, AsyncCursorPage[FineTuningJobCheckpoint]]: + """ + List checkpoints for a fine-tuning job. + + Args: + after: Identifier for the last checkpoint ID from the previous pagination request. + + limit: Number of checkpoints to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._get_api_list( + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints", fine_tuning_job_id=fine_tuning_job_id), + page=AsyncCursorPage[FineTuningJobCheckpoint], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + checkpoint_list_params.CheckpointListParams, + ), + security={"bearer_auth": True}, + ), + model=FineTuningJobCheckpoint, + ) + + +class CheckpointsWithRawResponse: + def __init__(self, checkpoints: Checkpoints) -> None: + self._checkpoints = checkpoints + + self.list = _legacy_response.to_raw_response_wrapper( + checkpoints.list, + ) + + +class AsyncCheckpointsWithRawResponse: + def __init__(self, checkpoints: AsyncCheckpoints) -> None: + self._checkpoints = checkpoints + + self.list = _legacy_response.async_to_raw_response_wrapper( + checkpoints.list, + ) + + +class CheckpointsWithStreamingResponse: + def __init__(self, checkpoints: Checkpoints) -> None: + self._checkpoints = checkpoints + + self.list = to_streamed_response_wrapper( + checkpoints.list, + ) + + +class AsyncCheckpointsWithStreamingResponse: + def __init__(self, checkpoints: AsyncCheckpoints) -> None: + self._checkpoints = checkpoints + + self.list = async_to_streamed_response_wrapper( + checkpoints.list, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/jobs.py b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/jobs.py new file mode 100644 index 0000000..2f708a6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/fine_tuning/jobs/jobs.py @@ -0,0 +1,971 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from .checkpoints import ( + Checkpoints, + AsyncCheckpoints, + CheckpointsWithRawResponse, + AsyncCheckpointsWithRawResponse, + CheckpointsWithStreamingResponse, + AsyncCheckpointsWithStreamingResponse, +) +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.fine_tuning import job_list_params, job_create_params, job_list_events_params +from ....types.shared_params.metadata import Metadata +from ....types.fine_tuning.fine_tuning_job import FineTuningJob +from ....types.fine_tuning.fine_tuning_job_event import FineTuningJobEvent + +__all__ = ["Jobs", "AsyncJobs"] + + +class Jobs(SyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + + @cached_property + def checkpoints(self) -> Checkpoints: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return Checkpoints(self._client) + + @cached_property + def with_raw_response(self) -> JobsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return JobsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> JobsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return JobsWithStreamingResponse(self) + + def create( + self, + *, + model: Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo", "gpt-4o-mini"]], + training_file: str, + hyperparameters: job_create_params.Hyperparameters | Omit = omit, + integrations: Optional[Iterable[job_create_params.Integration]] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + method: job_create_params.Method | Omit = omit, + seed: Optional[int] | Omit = omit, + suffix: Optional[str] | Omit = omit, + validation_file: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FineTuningJob: + """ + Creates a fine-tuning job which begins the process of creating a new model from + a given dataset. + + Response includes details of the enqueued job including job status and the name + of the fine-tuned models once complete. + + [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization) + + Args: + model: The name of the model to fine-tune. You can select one of the + [supported models](https://developers.openai.com/api/docs/guides/model-optimization#fine-tuning-methods). + + training_file: The ID of an uploaded file that contains training data. + + See + [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) + for how to upload a file. + + Your dataset must be formatted as a JSONL file. Additionally, you must upload + your file with the purpose `fine-tune`. + + The contents of the file should differ depending on if the model uses the + [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data), + [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) + format, or if the fine-tuning method uses the + [preference](https://developers.openai.com/api/docs/guides/direct-preference-optimization) + format. + + See the + [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) + for more details. + + hyperparameters: The hyperparameters used for the fine-tuning job. This value is now deprecated + in favor of `method`, and should be passed in under the `method` parameter. + + integrations: A list of integrations to enable for your fine-tuning job. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + method: The method used for fine-tuning. + + seed: The seed controls the reproducibility of the job. Passing in the same seed and + job parameters should produce the same results, but may differ in rare cases. If + a seed is not specified, one will be generated for you. + + suffix: A string of up to 64 characters that will be added to your fine-tuned model + name. + + For example, a `suffix` of "custom-model-name" would produce a model name like + `ft:gpt-4o-mini:openai:custom-model-name:7p4lURel`. + + validation_file: The ID of an uploaded file that contains validation data. + + If you provide this file, the data is used to generate validation metrics + periodically during fine-tuning. These metrics can be viewed in the fine-tuning + results file. The same data should not be present in both train and validation + files. + + Your dataset must be formatted as a JSONL file. You must upload your file with + the purpose `fine-tune`. + + See the + [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) + for more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/fine_tuning/jobs", + body=maybe_transform( + { + "model": model, + "training_file": training_file, + "hyperparameters": hyperparameters, + "integrations": integrations, + "metadata": metadata, + "method": method, + "seed": seed, + "suffix": suffix, + "validation_file": validation_file, + }, + job_create_params.JobCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FineTuningJob, + ) + + def retrieve( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FineTuningJob: + """ + Get info about a fine-tuning job. + + [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization) + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._get( + path_template("/fine_tuning/jobs/{fine_tuning_job_id}", fine_tuning_job_id=fine_tuning_job_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FineTuningJob, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[FineTuningJob]: + """ + List your organization's fine-tuning jobs + + Args: + after: Identifier for the last job from the previous pagination request. + + limit: Number of fine-tuning jobs to retrieve. + + metadata: Optional metadata filter. To filter, use the syntax `metadata[k]=v`. + Alternatively, set `metadata=null` to indicate no metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/fine_tuning/jobs", + page=SyncCursorPage[FineTuningJob], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "metadata": metadata, + }, + job_list_params.JobListParams, + ), + security={"bearer_auth": True}, + ), + model=FineTuningJob, + ) + + def cancel( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FineTuningJob: + """ + Immediately cancel a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._post( + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/cancel", fine_tuning_job_id=fine_tuning_job_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FineTuningJob, + ) + + def list_events( + self, + fine_tuning_job_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[FineTuningJobEvent]: + """ + Get status updates for a fine-tuning job. + + Args: + after: Identifier for the last event from the previous pagination request. + + limit: Number of events to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._get_api_list( + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/events", fine_tuning_job_id=fine_tuning_job_id), + page=SyncCursorPage[FineTuningJobEvent], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + job_list_events_params.JobListEventsParams, + ), + security={"bearer_auth": True}, + ), + model=FineTuningJobEvent, + ) + + def pause( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FineTuningJob: + """ + Pause a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._post( + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/pause", fine_tuning_job_id=fine_tuning_job_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FineTuningJob, + ) + + def resume( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FineTuningJob: + """ + Resume a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._post( + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/resume", fine_tuning_job_id=fine_tuning_job_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FineTuningJob, + ) + + +class AsyncJobs(AsyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + + @cached_property + def checkpoints(self) -> AsyncCheckpoints: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return AsyncCheckpoints(self._client) + + @cached_property + def with_raw_response(self) -> AsyncJobsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncJobsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncJobsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncJobsWithStreamingResponse(self) + + async def create( + self, + *, + model: Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo", "gpt-4o-mini"]], + training_file: str, + hyperparameters: job_create_params.Hyperparameters | Omit = omit, + integrations: Optional[Iterable[job_create_params.Integration]] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + method: job_create_params.Method | Omit = omit, + seed: Optional[int] | Omit = omit, + suffix: Optional[str] | Omit = omit, + validation_file: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FineTuningJob: + """ + Creates a fine-tuning job which begins the process of creating a new model from + a given dataset. + + Response includes details of the enqueued job including job status and the name + of the fine-tuned models once complete. + + [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization) + + Args: + model: The name of the model to fine-tune. You can select one of the + [supported models](https://developers.openai.com/api/docs/guides/model-optimization#fine-tuning-methods). + + training_file: The ID of an uploaded file that contains training data. + + See + [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) + for how to upload a file. + + Your dataset must be formatted as a JSONL file. Additionally, you must upload + your file with the purpose `fine-tune`. + + The contents of the file should differ depending on if the model uses the + [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data), + [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) + format, or if the fine-tuning method uses the + [preference](https://developers.openai.com/api/docs/guides/direct-preference-optimization) + format. + + See the + [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) + for more details. + + hyperparameters: The hyperparameters used for the fine-tuning job. This value is now deprecated + in favor of `method`, and should be passed in under the `method` parameter. + + integrations: A list of integrations to enable for your fine-tuning job. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + method: The method used for fine-tuning. + + seed: The seed controls the reproducibility of the job. Passing in the same seed and + job parameters should produce the same results, but may differ in rare cases. If + a seed is not specified, one will be generated for you. + + suffix: A string of up to 64 characters that will be added to your fine-tuned model + name. + + For example, a `suffix` of "custom-model-name" would produce a model name like + `ft:gpt-4o-mini:openai:custom-model-name:7p4lURel`. + + validation_file: The ID of an uploaded file that contains validation data. + + If you provide this file, the data is used to generate validation metrics + periodically during fine-tuning. These metrics can be viewed in the fine-tuning + results file. The same data should not be present in both train and validation + files. + + Your dataset must be formatted as a JSONL file. You must upload your file with + the purpose `fine-tune`. + + See the + [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) + for more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/fine_tuning/jobs", + body=await async_maybe_transform( + { + "model": model, + "training_file": training_file, + "hyperparameters": hyperparameters, + "integrations": integrations, + "metadata": metadata, + "method": method, + "seed": seed, + "suffix": suffix, + "validation_file": validation_file, + }, + job_create_params.JobCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FineTuningJob, + ) + + async def retrieve( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FineTuningJob: + """ + Get info about a fine-tuning job. + + [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization) + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return await self._get( + path_template("/fine_tuning/jobs/{fine_tuning_job_id}", fine_tuning_job_id=fine_tuning_job_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FineTuningJob, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[FineTuningJob, AsyncCursorPage[FineTuningJob]]: + """ + List your organization's fine-tuning jobs + + Args: + after: Identifier for the last job from the previous pagination request. + + limit: Number of fine-tuning jobs to retrieve. + + metadata: Optional metadata filter. To filter, use the syntax `metadata[k]=v`. + Alternatively, set `metadata=null` to indicate no metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/fine_tuning/jobs", + page=AsyncCursorPage[FineTuningJob], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "metadata": metadata, + }, + job_list_params.JobListParams, + ), + security={"bearer_auth": True}, + ), + model=FineTuningJob, + ) + + async def cancel( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FineTuningJob: + """ + Immediately cancel a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return await self._post( + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/cancel", fine_tuning_job_id=fine_tuning_job_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FineTuningJob, + ) + + def list_events( + self, + fine_tuning_job_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[FineTuningJobEvent, AsyncCursorPage[FineTuningJobEvent]]: + """ + Get status updates for a fine-tuning job. + + Args: + after: Identifier for the last event from the previous pagination request. + + limit: Number of events to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._get_api_list( + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/events", fine_tuning_job_id=fine_tuning_job_id), + page=AsyncCursorPage[FineTuningJobEvent], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + job_list_events_params.JobListEventsParams, + ), + security={"bearer_auth": True}, + ), + model=FineTuningJobEvent, + ) + + async def pause( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FineTuningJob: + """ + Pause a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return await self._post( + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/pause", fine_tuning_job_id=fine_tuning_job_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FineTuningJob, + ) + + async def resume( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> FineTuningJob: + """ + Resume a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return await self._post( + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/resume", fine_tuning_job_id=fine_tuning_job_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=FineTuningJob, + ) + + +class JobsWithRawResponse: + def __init__(self, jobs: Jobs) -> None: + self._jobs = jobs + + self.create = _legacy_response.to_raw_response_wrapper( + jobs.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + jobs.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + jobs.list, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + jobs.cancel, + ) + self.list_events = _legacy_response.to_raw_response_wrapper( + jobs.list_events, + ) + self.pause = _legacy_response.to_raw_response_wrapper( + jobs.pause, + ) + self.resume = _legacy_response.to_raw_response_wrapper( + jobs.resume, + ) + + @cached_property + def checkpoints(self) -> CheckpointsWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return CheckpointsWithRawResponse(self._jobs.checkpoints) + + +class AsyncJobsWithRawResponse: + def __init__(self, jobs: AsyncJobs) -> None: + self._jobs = jobs + + self.create = _legacy_response.async_to_raw_response_wrapper( + jobs.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + jobs.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + jobs.list, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + jobs.cancel, + ) + self.list_events = _legacy_response.async_to_raw_response_wrapper( + jobs.list_events, + ) + self.pause = _legacy_response.async_to_raw_response_wrapper( + jobs.pause, + ) + self.resume = _legacy_response.async_to_raw_response_wrapper( + jobs.resume, + ) + + @cached_property + def checkpoints(self) -> AsyncCheckpointsWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return AsyncCheckpointsWithRawResponse(self._jobs.checkpoints) + + +class JobsWithStreamingResponse: + def __init__(self, jobs: Jobs) -> None: + self._jobs = jobs + + self.create = to_streamed_response_wrapper( + jobs.create, + ) + self.retrieve = to_streamed_response_wrapper( + jobs.retrieve, + ) + self.list = to_streamed_response_wrapper( + jobs.list, + ) + self.cancel = to_streamed_response_wrapper( + jobs.cancel, + ) + self.list_events = to_streamed_response_wrapper( + jobs.list_events, + ) + self.pause = to_streamed_response_wrapper( + jobs.pause, + ) + self.resume = to_streamed_response_wrapper( + jobs.resume, + ) + + @cached_property + def checkpoints(self) -> CheckpointsWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return CheckpointsWithStreamingResponse(self._jobs.checkpoints) + + +class AsyncJobsWithStreamingResponse: + def __init__(self, jobs: AsyncJobs) -> None: + self._jobs = jobs + + self.create = async_to_streamed_response_wrapper( + jobs.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + jobs.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + jobs.list, + ) + self.cancel = async_to_streamed_response_wrapper( + jobs.cancel, + ) + self.list_events = async_to_streamed_response_wrapper( + jobs.list_events, + ) + self.pause = async_to_streamed_response_wrapper( + jobs.pause, + ) + self.resume = async_to_streamed_response_wrapper( + jobs.resume, + ) + + @cached_property + def checkpoints(self) -> AsyncCheckpointsWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + return AsyncCheckpointsWithStreamingResponse(self._jobs.checkpoints) diff --git a/venv/lib/python3.12/site-packages/openai/resources/images.py b/venv/lib/python3.12/site-packages/openai/resources/images.py new file mode 100644 index 0000000..7c3df38 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/images.py @@ -0,0 +1,2119 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Mapping, Optional, cast +from typing_extensions import Literal, overload + +import httpx2 + +from .. import _legacy_response +from ..types import image_edit_params, image_generate_params, image_create_variation_params +from .._files import deepcopy_with_paths +from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, SequenceNotStr, omit, not_given +from .._utils import extract_files, required_args, maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .._streaming import Stream, AsyncStream +from .._base_client import make_request_options +from ..types.image_model import ImageModel +from ..types.images_response import ImagesResponse +from ..types.image_gen_stream_event import ImageGenStreamEvent +from ..types.image_edit_stream_event import ImageEditStreamEvent + +__all__ = ["Images", "AsyncImages"] + + +class Images(SyncAPIResource): + """Given a prompt and/or an input image, the model will generate a new image.""" + + @cached_property + def with_raw_response(self) -> ImagesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ImagesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ImagesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ImagesWithStreamingResponse(self) + + def create_variation( + self, + *, + image: FileTypes, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Optional[Literal["256x256", "512x512", "1024x1024"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse: + """Creates a variation of a given image. + + This endpoint only supports `dall-e-2`. + + Args: + image: The image to use as the basis for the variation(s). Must be a valid PNG file, + less than 4MB, and square. + + model: The model to use for image generation. Only `dall-e-2` is supported at this + time. + + n: The number of images to generate. Must be between 1 and 10. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. + + size: The size of the generated images. Must be one of `256x256`, `512x512`, or + `1024x1024`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "image": image, + "model": model, + "n": n, + "response_format": response_format, + "size": size, + "user": user, + }, + [["image"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["image"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/images/variations", + body=maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ImagesResponse, + ) + + @overload + def edit( + self, + *, + image: Union[FileTypes, SequenceNotStr[FileTypes]], + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse: + """Creates an edited or extended image given one or more source images and a + prompt. + + This endpoint supports GPT Image models and `dall-e-2`. + + Args: + image: The image(s) to edit. Must be a supported image file or an array of images. + + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should + be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 + images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. + + prompt: A text description of the desired image(s). The maximum length is 1000 + characters for `dall-e-2`, and 32000 characters for the GPT image models. + + background: Allows to set transparency for the background of the generated image(s). Must be + one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used, + the model will automatically determine the best background for the image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + + input_fidelity: Controls fidelity to the original input image(s). This parameter is supported + for GPT image models that support input fidelity. `gpt-image-2` and + `gpt-image-2-2026-04-21` ignore this parameter. + + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) + indicate where `image` should be edited. If there are multiple images provided, + the mask will be applied on the first image. Must be a valid PNG file, less than + 4MB, and have the same dimensions as `image`. + + model: The model to use for image generation. One of `dall-e-2` or a GPT image model + (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to + `gpt-image-1.5`. + + n: The number of images to generate. Must be between 1 and 10. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for the GPT image models with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. The + default value is `png`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + + quality: The quality of the image that will be generated for GPT image models. The GPT + image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and + `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support + `xhigh` and `max`. Defaults to `auto`. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. This parameter is only supported for `dall-e-2` (default is `url` for + `dall-e-2`), as GPT image models always return base64-encoded images. + + size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary + resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. + Width and height must both be divisible by 16 and the requested aspect ratio + must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and + the maximum supported resolution is `3840x2160`. The requested size must also + satisfy the model's current pixel and edge limits. The standard sizes + `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; + `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use + one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of + `1024x1024`, `1792x1024`, or `1024x1792`. + + stream: Edit the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def edit( + self, + *, + image: Union[FileTypes, SequenceNotStr[FileTypes]], + prompt: str, + stream: Literal[True], + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[ImageEditStreamEvent]: + """Creates an edited or extended image given one or more source images and a + prompt. + + This endpoint supports GPT Image models and `dall-e-2`. + + Args: + image: The image(s) to edit. Must be a supported image file or an array of images. + + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should + be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 + images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. + + prompt: A text description of the desired image(s). The maximum length is 1000 + characters for `dall-e-2`, and 32000 characters for the GPT image models. + + stream: Edit the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. + + background: Allows to set transparency for the background of the generated image(s). Must be + one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used, + the model will automatically determine the best background for the image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + + input_fidelity: Controls fidelity to the original input image(s). This parameter is supported + for GPT image models that support input fidelity. `gpt-image-2` and + `gpt-image-2-2026-04-21` ignore this parameter. + + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) + indicate where `image` should be edited. If there are multiple images provided, + the mask will be applied on the first image. Must be a valid PNG file, less than + 4MB, and have the same dimensions as `image`. + + model: The model to use for image generation. One of `dall-e-2` or a GPT image model + (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to + `gpt-image-1.5`. + + n: The number of images to generate. Must be between 1 and 10. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for the GPT image models with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. The + default value is `png`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + + quality: The quality of the image that will be generated for GPT image models. The GPT + image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and + `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support + `xhigh` and `max`. Defaults to `auto`. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. This parameter is only supported for `dall-e-2` (default is `url` for + `dall-e-2`), as GPT image models always return base64-encoded images. + + size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary + resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. + Width and height must both be divisible by 16 and the requested aspect ratio + must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and + the maximum supported resolution is `3840x2160`. The requested size must also + satisfy the model's current pixel and edge limits. The standard sizes + `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; + `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use + one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of + `1024x1024`, `1792x1024`, or `1024x1792`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def edit( + self, + *, + image: Union[FileTypes, SequenceNotStr[FileTypes]], + prompt: str, + stream: bool, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse | Stream[ImageEditStreamEvent]: + """Creates an edited or extended image given one or more source images and a + prompt. + + This endpoint supports GPT Image models and `dall-e-2`. + + Args: + image: The image(s) to edit. Must be a supported image file or an array of images. + + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should + be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 + images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. + + prompt: A text description of the desired image(s). The maximum length is 1000 + characters for `dall-e-2`, and 32000 characters for the GPT image models. + + stream: Edit the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. + + background: Allows to set transparency for the background of the generated image(s). Must be + one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used, + the model will automatically determine the best background for the image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + + input_fidelity: Controls fidelity to the original input image(s). This parameter is supported + for GPT image models that support input fidelity. `gpt-image-2` and + `gpt-image-2-2026-04-21` ignore this parameter. + + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) + indicate where `image` should be edited. If there are multiple images provided, + the mask will be applied on the first image. Must be a valid PNG file, less than + 4MB, and have the same dimensions as `image`. + + model: The model to use for image generation. One of `dall-e-2` or a GPT image model + (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to + `gpt-image-1.5`. + + n: The number of images to generate. Must be between 1 and 10. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for the GPT image models with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. The + default value is `png`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + + quality: The quality of the image that will be generated for GPT image models. The GPT + image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and + `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support + `xhigh` and `max`. Defaults to `auto`. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. This parameter is only supported for `dall-e-2` (default is `url` for + `dall-e-2`), as GPT image models always return base64-encoded images. + + size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary + resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. + Width and height must both be divisible by 16 and the requested aspect ratio + must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and + the maximum supported resolution is `3840x2160`. The requested size must also + satisfy the model's current pixel and edge limits. The standard sizes + `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; + `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use + one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of + `1024x1024`, `1792x1024`, or `1024x1792`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["image", "prompt"], ["image", "prompt", "stream"]) + def edit( + self, + *, + image: Union[FileTypes, SequenceNotStr[FileTypes]], + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse | Stream[ImageEditStreamEvent]: + body = deepcopy_with_paths( + { + "image": image, + "prompt": prompt, + "background": background, + "input_fidelity": input_fidelity, + "mask": mask, + "model": model, + "n": n, + "output_compression": output_compression, + "output_format": output_format, + "partial_images": partial_images, + "quality": quality, + "response_format": response_format, + "size": size, + "stream": stream, + "user": user, + }, + [["image"], ["image", ""], ["mask"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["image", ""], ["mask"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/images/edits", + body=maybe_transform( + body, + image_edit_params.ImageEditParamsStreaming if stream else image_edit_params.ImageEditParamsNonStreaming, + ), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ImagesResponse, + stream=stream or False, + stream_cls=Stream[ImageEditStreamEvent], + ) + + @overload + def generate( + self, + *, + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse: + """ + Creates an image given a prompt. + [Learn more](https://developers.openai.com/api/docs/guides/images-vision). + + Args: + prompt: A text description of the desired image(s). The maximum length is 32000 + characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 + characters for `dall-e-3`. + + background: Allows to set transparency for the background of the generated image(s). Must be + one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used, + the model will automatically determine the best background for the image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter + specific to the GPT image models is used. + + moderation: Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). + + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for the GPT image models with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + + quality: The quality of the image that will be generated. + + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for the GPT image models. + - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their + `2026-09-08` snapshots, also support `xhigh` and `max`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. + + response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes + after the image has been generated. This parameter isn't supported for the GPT + image models, which always return base64-encoded images. + + size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary + resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. + Width and height must both be divisible by 16 and the requested aspect ratio + must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and + the maximum supported resolution is `3840x2160`. The requested size must also + satisfy the model's current pixel and edge limits. The standard sizes + `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; + `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use + one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of + `1024x1024`, `1792x1024`, or `1024x1792`. + + stream: Generate the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. This parameter is only supported for the GPT image models. + + style: The style of the generated images. This parameter is only supported for + `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean + towards generating hyper-real and dramatic images. Natural causes the model to + produce more natural, less hyper-real looking images. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def generate( + self, + *, + prompt: str, + stream: Literal[True], + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[ImageGenStreamEvent]: + """ + Creates an image given a prompt. + [Learn more](https://developers.openai.com/api/docs/guides/images-vision). + + Args: + prompt: A text description of the desired image(s). The maximum length is 32000 + characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 + characters for `dall-e-3`. + + stream: Generate the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. This parameter is only supported for the GPT image models. + + background: Allows to set transparency for the background of the generated image(s). Must be + one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used, + the model will automatically determine the best background for the image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter + specific to the GPT image models is used. + + moderation: Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). + + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for the GPT image models with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + + quality: The quality of the image that will be generated. + + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for the GPT image models. + - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their + `2026-09-08` snapshots, also support `xhigh` and `max`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. + + response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes + after the image has been generated. This parameter isn't supported for the GPT + image models, which always return base64-encoded images. + + size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary + resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. + Width and height must both be divisible by 16 and the requested aspect ratio + must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and + the maximum supported resolution is `3840x2160`. The requested size must also + satisfy the model's current pixel and edge limits. The standard sizes + `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; + `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use + one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of + `1024x1024`, `1792x1024`, or `1024x1792`. + + style: The style of the generated images. This parameter is only supported for + `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean + towards generating hyper-real and dramatic images. Natural causes the model to + produce more natural, less hyper-real looking images. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def generate( + self, + *, + prompt: str, + stream: bool, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse | Stream[ImageGenStreamEvent]: + """ + Creates an image given a prompt. + [Learn more](https://developers.openai.com/api/docs/guides/images-vision). + + Args: + prompt: A text description of the desired image(s). The maximum length is 32000 + characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 + characters for `dall-e-3`. + + stream: Generate the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. This parameter is only supported for the GPT image models. + + background: Allows to set transparency for the background of the generated image(s). Must be + one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used, + the model will automatically determine the best background for the image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter + specific to the GPT image models is used. + + moderation: Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). + + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for the GPT image models with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + + quality: The quality of the image that will be generated. + + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for the GPT image models. + - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their + `2026-09-08` snapshots, also support `xhigh` and `max`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. + + response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes + after the image has been generated. This parameter isn't supported for the GPT + image models, which always return base64-encoded images. + + size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary + resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. + Width and height must both be divisible by 16 and the requested aspect ratio + must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and + the maximum supported resolution is `3840x2160`. The requested size must also + satisfy the model's current pixel and edge limits. The standard sizes + `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; + `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use + one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of + `1024x1024`, `1792x1024`, or `1024x1792`. + + style: The style of the generated images. This parameter is only supported for + `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean + towards generating hyper-real and dramatic images. Natural causes the model to + produce more natural, less hyper-real looking images. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["prompt"], ["prompt", "stream"]) + def generate( + self, + *, + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse | Stream[ImageGenStreamEvent]: + return self._post( + "/images/generations", + body=maybe_transform( + { + "prompt": prompt, + "background": background, + "model": model, + "moderation": moderation, + "n": n, + "output_compression": output_compression, + "output_format": output_format, + "partial_images": partial_images, + "quality": quality, + "response_format": response_format, + "size": size, + "stream": stream, + "style": style, + "user": user, + }, + image_generate_params.ImageGenerateParamsStreaming + if stream + else image_generate_params.ImageGenerateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ImagesResponse, + stream=stream or False, + stream_cls=Stream[ImageGenStreamEvent], + ) + + +class AsyncImages(AsyncAPIResource): + """Given a prompt and/or an input image, the model will generate a new image.""" + + @cached_property + def with_raw_response(self) -> AsyncImagesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncImagesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncImagesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncImagesWithStreamingResponse(self) + + async def create_variation( + self, + *, + image: FileTypes, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Optional[Literal["256x256", "512x512", "1024x1024"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse: + """Creates a variation of a given image. + + This endpoint only supports `dall-e-2`. + + Args: + image: The image to use as the basis for the variation(s). Must be a valid PNG file, + less than 4MB, and square. + + model: The model to use for image generation. Only `dall-e-2` is supported at this + time. + + n: The number of images to generate. Must be between 1 and 10. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. + + size: The size of the generated images. Must be one of `256x256`, `512x512`, or + `1024x1024`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "image": image, + "model": model, + "n": n, + "response_format": response_format, + "size": size, + "user": user, + }, + [["image"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["image"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/images/variations", + body=await async_maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ImagesResponse, + ) + + @overload + async def edit( + self, + *, + image: Union[FileTypes, SequenceNotStr[FileTypes]], + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse: + """Creates an edited or extended image given one or more source images and a + prompt. + + This endpoint supports GPT Image models and `dall-e-2`. + + Args: + image: The image(s) to edit. Must be a supported image file or an array of images. + + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should + be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 + images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. + + prompt: A text description of the desired image(s). The maximum length is 1000 + characters for `dall-e-2`, and 32000 characters for the GPT image models. + + background: Allows to set transparency for the background of the generated image(s). Must be + one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used, + the model will automatically determine the best background for the image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + + input_fidelity: Controls fidelity to the original input image(s). This parameter is supported + for GPT image models that support input fidelity. `gpt-image-2` and + `gpt-image-2-2026-04-21` ignore this parameter. + + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) + indicate where `image` should be edited. If there are multiple images provided, + the mask will be applied on the first image. Must be a valid PNG file, less than + 4MB, and have the same dimensions as `image`. + + model: The model to use for image generation. One of `dall-e-2` or a GPT image model + (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to + `gpt-image-1.5`. + + n: The number of images to generate. Must be between 1 and 10. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for the GPT image models with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. The + default value is `png`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + + quality: The quality of the image that will be generated for GPT image models. The GPT + image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and + `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support + `xhigh` and `max`. Defaults to `auto`. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. This parameter is only supported for `dall-e-2` (default is `url` for + `dall-e-2`), as GPT image models always return base64-encoded images. + + size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary + resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. + Width and height must both be divisible by 16 and the requested aspect ratio + must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and + the maximum supported resolution is `3840x2160`. The requested size must also + satisfy the model's current pixel and edge limits. The standard sizes + `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; + `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use + one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of + `1024x1024`, `1792x1024`, or `1024x1792`. + + stream: Edit the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def edit( + self, + *, + image: Union[FileTypes, SequenceNotStr[FileTypes]], + prompt: str, + stream: Literal[True], + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[ImageEditStreamEvent]: + """Creates an edited or extended image given one or more source images and a + prompt. + + This endpoint supports GPT Image models and `dall-e-2`. + + Args: + image: The image(s) to edit. Must be a supported image file or an array of images. + + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should + be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 + images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. + + prompt: A text description of the desired image(s). The maximum length is 1000 + characters for `dall-e-2`, and 32000 characters for the GPT image models. + + stream: Edit the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. + + background: Allows to set transparency for the background of the generated image(s). Must be + one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used, + the model will automatically determine the best background for the image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + + input_fidelity: Controls fidelity to the original input image(s). This parameter is supported + for GPT image models that support input fidelity. `gpt-image-2` and + `gpt-image-2-2026-04-21` ignore this parameter. + + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) + indicate where `image` should be edited. If there are multiple images provided, + the mask will be applied on the first image. Must be a valid PNG file, less than + 4MB, and have the same dimensions as `image`. + + model: The model to use for image generation. One of `dall-e-2` or a GPT image model + (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to + `gpt-image-1.5`. + + n: The number of images to generate. Must be between 1 and 10. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for the GPT image models with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. The + default value is `png`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + + quality: The quality of the image that will be generated for GPT image models. The GPT + image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and + `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support + `xhigh` and `max`. Defaults to `auto`. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. This parameter is only supported for `dall-e-2` (default is `url` for + `dall-e-2`), as GPT image models always return base64-encoded images. + + size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary + resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. + Width and height must both be divisible by 16 and the requested aspect ratio + must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and + the maximum supported resolution is `3840x2160`. The requested size must also + satisfy the model's current pixel and edge limits. The standard sizes + `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; + `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use + one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of + `1024x1024`, `1792x1024`, or `1024x1792`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def edit( + self, + *, + image: Union[FileTypes, SequenceNotStr[FileTypes]], + prompt: str, + stream: bool, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse | AsyncStream[ImageEditStreamEvent]: + """Creates an edited or extended image given one or more source images and a + prompt. + + This endpoint supports GPT Image models and `dall-e-2`. + + Args: + image: The image(s) to edit. Must be a supported image file or an array of images. + + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should + be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 + images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. + + prompt: A text description of the desired image(s). The maximum length is 1000 + characters for `dall-e-2`, and 32000 characters for the GPT image models. + + stream: Edit the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. + + background: Allows to set transparency for the background of the generated image(s). Must be + one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used, + the model will automatically determine the best background for the image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + + input_fidelity: Controls fidelity to the original input image(s). This parameter is supported + for GPT image models that support input fidelity. `gpt-image-2` and + `gpt-image-2-2026-04-21` ignore this parameter. + + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) + indicate where `image` should be edited. If there are multiple images provided, + the mask will be applied on the first image. Must be a valid PNG file, less than + 4MB, and have the same dimensions as `image`. + + model: The model to use for image generation. One of `dall-e-2` or a GPT image model + (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to + `gpt-image-1.5`. + + n: The number of images to generate. Must be between 1 and 10. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for the GPT image models with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. The + default value is `png`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + + quality: The quality of the image that will be generated for GPT image models. The GPT + image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and + `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support + `xhigh` and `max`. Defaults to `auto`. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. This parameter is only supported for `dall-e-2` (default is `url` for + `dall-e-2`), as GPT image models always return base64-encoded images. + + size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary + resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. + Width and height must both be divisible by 16 and the requested aspect ratio + must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and + the maximum supported resolution is `3840x2160`. The requested size must also + satisfy the model's current pixel and edge limits. The standard sizes + `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; + `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use + one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of + `1024x1024`, `1792x1024`, or `1024x1792`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["image", "prompt"], ["image", "prompt", "stream"]) + async def edit( + self, + *, + image: Union[FileTypes, SequenceNotStr[FileTypes]], + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse | AsyncStream[ImageEditStreamEvent]: + body = deepcopy_with_paths( + { + "image": image, + "prompt": prompt, + "background": background, + "input_fidelity": input_fidelity, + "mask": mask, + "model": model, + "n": n, + "output_compression": output_compression, + "output_format": output_format, + "partial_images": partial_images, + "quality": quality, + "response_format": response_format, + "size": size, + "stream": stream, + "user": user, + }, + [["image"], ["image", ""], ["mask"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["image", ""], ["mask"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/images/edits", + body=await async_maybe_transform( + body, + image_edit_params.ImageEditParamsStreaming if stream else image_edit_params.ImageEditParamsNonStreaming, + ), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ImagesResponse, + stream=stream or False, + stream_cls=AsyncStream[ImageEditStreamEvent], + ) + + @overload + async def generate( + self, + *, + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse: + """ + Creates an image given a prompt. + [Learn more](https://developers.openai.com/api/docs/guides/images-vision). + + Args: + prompt: A text description of the desired image(s). The maximum length is 32000 + characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 + characters for `dall-e-3`. + + background: Allows to set transparency for the background of the generated image(s). Must be + one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used, + the model will automatically determine the best background for the image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter + specific to the GPT image models is used. + + moderation: Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). + + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for the GPT image models with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + + quality: The quality of the image that will be generated. + + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for the GPT image models. + - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their + `2026-09-08` snapshots, also support `xhigh` and `max`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. + + response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes + after the image has been generated. This parameter isn't supported for the GPT + image models, which always return base64-encoded images. + + size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary + resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. + Width and height must both be divisible by 16 and the requested aspect ratio + must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and + the maximum supported resolution is `3840x2160`. The requested size must also + satisfy the model's current pixel and edge limits. The standard sizes + `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; + `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use + one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of + `1024x1024`, `1792x1024`, or `1024x1792`. + + stream: Generate the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. This parameter is only supported for the GPT image models. + + style: The style of the generated images. This parameter is only supported for + `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean + towards generating hyper-real and dramatic images. Natural causes the model to + produce more natural, less hyper-real looking images. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def generate( + self, + *, + prompt: str, + stream: Literal[True], + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[ImageGenStreamEvent]: + """ + Creates an image given a prompt. + [Learn more](https://developers.openai.com/api/docs/guides/images-vision). + + Args: + prompt: A text description of the desired image(s). The maximum length is 32000 + characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 + characters for `dall-e-3`. + + stream: Generate the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. This parameter is only supported for the GPT image models. + + background: Allows to set transparency for the background of the generated image(s). Must be + one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used, + the model will automatically determine the best background for the image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter + specific to the GPT image models is used. + + moderation: Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). + + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for the GPT image models with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + + quality: The quality of the image that will be generated. + + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for the GPT image models. + - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their + `2026-09-08` snapshots, also support `xhigh` and `max`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. + + response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes + after the image has been generated. This parameter isn't supported for the GPT + image models, which always return base64-encoded images. + + size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary + resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. + Width and height must both be divisible by 16 and the requested aspect ratio + must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and + the maximum supported resolution is `3840x2160`. The requested size must also + satisfy the model's current pixel and edge limits. The standard sizes + `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; + `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use + one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of + `1024x1024`, `1792x1024`, or `1024x1792`. + + style: The style of the generated images. This parameter is only supported for + `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean + towards generating hyper-real and dramatic images. Natural causes the model to + produce more natural, less hyper-real looking images. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def generate( + self, + *, + prompt: str, + stream: bool, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse | AsyncStream[ImageGenStreamEvent]: + """ + Creates an image given a prompt. + [Learn more](https://developers.openai.com/api/docs/guides/images-vision). + + Args: + prompt: A text description of the desired image(s). The maximum length is 32000 + characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 + characters for `dall-e-3`. + + stream: Generate the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. This parameter is only supported for the GPT image models. + + background: Allows to set transparency for the background of the generated image(s). Must be + one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used, + the model will automatically determine the best background for the image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter + specific to the GPT image models is used. + + moderation: Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). + + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for the GPT image models with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + + quality: The quality of the image that will be generated. + + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for the GPT image models. + - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their + `2026-09-08` snapshots, also support `xhigh` and `max`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. + + response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes + after the image has been generated. This parameter isn't supported for the GPT + image models, which always return base64-encoded images. + + size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary + resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. + Width and height must both be divisible by 16 and the requested aspect ratio + must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and + the maximum supported resolution is `3840x2160`. The requested size must also + satisfy the model's current pixel and edge limits. The standard sizes + `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; + `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use + one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of + `1024x1024`, `1792x1024`, or `1024x1792`. + + style: The style of the generated images. This parameter is only supported for + `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean + towards generating hyper-real and dramatic images. Natural causes the model to + produce more natural, less hyper-real looking images. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["prompt"], ["prompt", "stream"]) + async def generate( + self, + *, + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ImagesResponse | AsyncStream[ImageGenStreamEvent]: + return await self._post( + "/images/generations", + body=await async_maybe_transform( + { + "prompt": prompt, + "background": background, + "model": model, + "moderation": moderation, + "n": n, + "output_compression": output_compression, + "output_format": output_format, + "partial_images": partial_images, + "quality": quality, + "response_format": response_format, + "size": size, + "stream": stream, + "style": style, + "user": user, + }, + image_generate_params.ImageGenerateParamsStreaming + if stream + else image_generate_params.ImageGenerateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ImagesResponse, + stream=stream or False, + stream_cls=AsyncStream[ImageGenStreamEvent], + ) + + +class ImagesWithRawResponse: + def __init__(self, images: Images) -> None: + self._images = images + + self.create_variation = _legacy_response.to_raw_response_wrapper( + images.create_variation, + ) + self.edit = _legacy_response.to_raw_response_wrapper( + images.edit, + ) + self.generate = _legacy_response.to_raw_response_wrapper( + images.generate, + ) + + +class AsyncImagesWithRawResponse: + def __init__(self, images: AsyncImages) -> None: + self._images = images + + self.create_variation = _legacy_response.async_to_raw_response_wrapper( + images.create_variation, + ) + self.edit = _legacy_response.async_to_raw_response_wrapper( + images.edit, + ) + self.generate = _legacy_response.async_to_raw_response_wrapper( + images.generate, + ) + + +class ImagesWithStreamingResponse: + def __init__(self, images: Images) -> None: + self._images = images + + self.create_variation = to_streamed_response_wrapper( + images.create_variation, + ) + self.edit = to_streamed_response_wrapper( + images.edit, + ) + self.generate = to_streamed_response_wrapper( + images.generate, + ) + + +class AsyncImagesWithStreamingResponse: + def __init__(self, images: AsyncImages) -> None: + self._images = images + + self.create_variation = async_to_streamed_response_wrapper( + images.create_variation, + ) + self.edit = async_to_streamed_response_wrapper( + images.edit, + ) + self.generate = async_to_streamed_response_wrapper( + images.generate, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/live/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/live/__init__.py new file mode 100644 index 0000000..99571a6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/live/__init__.py @@ -0,0 +1,91 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .live import ( + Live as Live, + AsyncLive as AsyncLive, + LiveWithRawResponse as LiveWithRawResponse, + AsyncLiveWithRawResponse as AsyncLiveWithRawResponse, + LiveWithStreamingResponse as LiveWithStreamingResponse, + AsyncLiveWithStreamingResponse as AsyncLiveWithStreamingResponse, + ) + from .forks import ( + Forks as Forks, + AsyncForks as AsyncForks, + ) + from .sessions import ( + Sessions as Sessions, + AsyncSessions as AsyncSessions, + SessionsWithRawResponse as SessionsWithRawResponse, + AsyncSessionsWithRawResponse as AsyncSessionsWithRawResponse, + SessionsWithStreamingResponse as SessionsWithStreamingResponse, + AsyncSessionsWithStreamingResponse as AsyncSessionsWithStreamingResponse, + ) + from .sideband import ( + Sideband as Sideband, + AsyncSideband as AsyncSideband, + ) + +else: + _EXPORTS = { + "Sideband": (".sideband", "Sideband"), + "AsyncSideband": (".sideband", "AsyncSideband"), + "Forks": (".forks", "Forks"), + "AsyncForks": (".forks", "AsyncForks"), + "Sessions": (".sessions", "Sessions"), + "AsyncSessions": (".sessions", "AsyncSessions"), + "SessionsWithRawResponse": (".sessions", "SessionsWithRawResponse"), + "AsyncSessionsWithRawResponse": (".sessions", "AsyncSessionsWithRawResponse"), + "SessionsWithStreamingResponse": (".sessions", "SessionsWithStreamingResponse"), + "AsyncSessionsWithStreamingResponse": (".sessions", "AsyncSessionsWithStreamingResponse"), + "Live": (".live", "Live"), + "AsyncLive": (".live", "AsyncLive"), + "LiveWithRawResponse": (".live", "LiveWithRawResponse"), + "AsyncLiveWithRawResponse": (".live", "AsyncLiveWithRawResponse"), + "LiveWithStreamingResponse": (".live", "LiveWithStreamingResponse"), + "AsyncLiveWithStreamingResponse": (".live", "AsyncLiveWithStreamingResponse"), + } + _SUBMODULES = { + "forks", + "live", + "sessions", + "sideband", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Sideband", + "AsyncSideband", + "Forks", + "AsyncForks", + "Sessions", + "AsyncSessions", + "SessionsWithRawResponse", + "AsyncSessionsWithRawResponse", + "SessionsWithStreamingResponse", + "AsyncSessionsWithStreamingResponse", + "Live", + "AsyncLive", + "LiveWithRawResponse", + "AsyncLiveWithRawResponse", + "LiveWithStreamingResponse", + "AsyncLiveWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..87f34e8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/forks.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/forks.cpython-312.pyc new file mode 100644 index 0000000..8110a34 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/forks.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/live.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/live.cpython-312.pyc new file mode 100644 index 0000000..3d17c92 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/live.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/sessions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/sessions.cpython-312.pyc new file mode 100644 index 0000000..8d77ee7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/sessions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/sideband.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/sideband.cpython-312.pyc new file mode 100644 index 0000000..f50ca3b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/live/__pycache__/sideband.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/live/api.md b/venv/lib/python3.12/site-packages/openai/resources/live/api.md new file mode 100644 index 0000000..6cd5c7e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/live/api.md @@ -0,0 +1,95 @@ +# Live + +Types: + +```python +from openai.types.live import ( + AudioFormat, + BuiltInVoice, + ClientConfig, + ClientDelegation, + ClientEvent, + CommentaryAppendEvent, + CommentaryAppendedEvent, + CustomVoice, + DataChannelConfig, + DelegationCreatedEvent, + Error, + ErrorEvent, + ForkSessionConfig, + ForkSessionStartEvent, + FunctionTool, + InfoEvent, + InitialItem, + InputAudioAppendEvent, + InputAudioMuteEvent, + InputAudioMutedEvent, + InputAudioUnmuteEvent, + InputAudioUnmutedEvent, + InputTranscriptDeltaEvent, + InstructionsAppendEvent, + InstructionsAppendedEvent, + MediaSessionConfig, + MediaSessionForkConfig, + OutputAudioDeltaEvent, + OutputTranscriptDeltaEvent, + ResponseCreateEvent, + ResponseEvent, + ResponseItemCreateEvent, + ResponsesDelegationConfig, + ResponsesDelegationUpdateConfig, + ServerEvent, + ServerEventSelector, + SessionCloseEvent, + SessionClosedEvent, + SessionConfig, + SessionResource, + SessionStartEvent, + SessionStartedEvent, + SessionUpdateConfig, + SessionUpdateEvent, + SessionUpdatedEvent, + SessionUsage, + SessionUsageUpdatedEvent, + ThinkingAppendEvent, + ThinkingAppendedEvent, + LiveCreateResponse, +) +``` + +Methods: + +- client.live.create(\*\*params) -> LiveCreateResponse + +## Sideband + +Types: + +```python +from openai.types.live import ConnectClientEvent, ConnectServerEvent +``` + +## Forks + +Types: + +```python +from openai.types.live import ForkClientEvent, ForkServerEvent +``` + +## Sessions + +Types: + +```python +from openai.types.live import SessionForkResponse +``` + +Methods: + +- client.live.sessions.accept(session_id, \*\*params) -> None +- client.live.sessions.download_recording(session_id) -> HttpxBinaryResponseContent +- client.live.sessions.fork(session_id, \*\*params) -> SessionForkResponse +- client.live.sessions.hangup(session_id) -> None +- client.live.sessions.refer(session_id, \*\*params) -> None +- client.live.sessions.reject(session_id, \*\*params) -> None diff --git a/venv/lib/python3.12/site-packages/openai/resources/live/forks.py b/venv/lib/python3.12/site-packages/openai/resources/live/forks.py new file mode 100644 index 0000000..8deb5fb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/live/forks.py @@ -0,0 +1,1451 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import json +import time +import random +import logging +from types import TracebackType +from typing import TYPE_CHECKING, Any, Union, Callable, Iterator, Optional, Awaitable, cast +from typing_extensions import AsyncIterator + +import httpx2 +from pydantic import BaseModel + +from ..._types import Omit, Query, Headers, omit +from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform +from ..._compat import cached_property +from ..._models import FinalRequestOptions, construct_type_unchecked +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._exceptions import OpenAIError, WebSocketConnectionClosedError +from ..._send_queue import SendQueue +from ..._base_client import _merge_mappings +from ..._event_handler import EventHandlerRegistry +from ...types.live.error_event import ErrorEvent +from ...types.live.fork_client_event import ForkClientEvent +from ...types.live.fork_server_event import ForkServerEvent +from ...types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close +from ...types.live.fork_client_event_param import ForkClientEventParam +from ...types.websocket_connection_options import WebSocketConnectionOptions +from ...types.live.fork_session_config_param import ForkSessionConfigParam +from ...types.live.session_update_config_param import SessionUpdateConfigParam +from ...types.responses.response_input_item_param import ResponseInputItemParam + +if TYPE_CHECKING: + from websockets.sync.client import ClientConnection as WebSocketConnection + from websockets.asyncio.client import ClientConnection as AsyncWebSocketConnection + + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Forks", "AsyncForks"] + +log: logging.Logger = logging.getLogger(__name__) + + +class Forks(SyncAPIResource): + def connect( + self, + *, + session_id: str, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> ForksConnectionManager: + """Fork a stored Live session over WebSocket. + + Send session.start with an overrides object (which may be empty), then wait for session.started. The model is inherited. + """ + return ForksConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + session_id=session_id, + ) + + +class AsyncForks(AsyncAPIResource): + def connect( + self, + *, + session_id: str, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> AsyncForksConnectionManager: + """Fork a stored Live session over WebSocket. + + Send session.start with an overrides object (which may be empty), then wait for session.started. The model is inherited. + """ + return AsyncForksConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + session_id=session_id, + ) + + +class AsyncForksConnection: + """Represents a live WebSocket connection to the Forks API""" + + session: AsyncForksSessionResource + response: AsyncForksResponseResource + + _connection: AsyncWebSocketConnection + + def __init__( + self, + connection: AsyncWebSocketConnection, + *, + make_ws: Callable[[Query, Headers], Awaitable[AsyncWebSocketConnection]] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._reconnect_attempt = 0 + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue if send_queue is not None else SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=False) + + self.session = AsyncForksSessionResource(self) + self.response = AsyncForksResponseResource(self) + + async def __aiter__(self) -> AsyncIterator[ForkServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield await self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not await self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + async def recv(self) -> ForkServerEvent: + """ + Receive the next message from the connection and parses it into a `ForkServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + event = self.parse_event(await self.recv_bytes()) + event_type = ( + cast("dict[str, object]", event).get("type") + if isinstance(cast(object, event), dict) + else getattr(event, "type", None) + ) + # A successful upgrade can still be followed by an admission error. + # Reset the budget only after receiving a non-error application event. + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return event + + async def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `ForkServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = await self._connection.recv(decode=False) + log.debug("Received WebSocket message: %i bytes", len(message)) + if self._reconnect_attempt: + # Account for raw application progress without changing frame delivery. + try: + event_data: object = json.loads(message) + except (ValueError, RecursionError): + return message + event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return message + + async def send(self, event: ForkClientEvent | ForkClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(await async_maybe_transform(event, ForkClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + await self._connection.send(data) + + async def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + await self._connection.send(data) + + async def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + await self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> ForkServerEvent: + """ + Converts a raw `str` or `bytes` message into a `ForkServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast(ForkServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, ForkServerEvent))) + + async def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + import asyncio + + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1): + self._reconnect_attempt = attempt + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + await asyncio.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = await self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + await self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + async def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + + async def _send(data: str) -> None: + await self._connection.send(data) + + try: + await self._send_queue.flush_async(_send, requeue_failed=False) + except Exception: + log.warning("Failed to flush send queue after reconnect") + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncForksConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("session.started", my_handler) + + Or as a decorator:: + + @connection.on("session.started") + async def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncForksConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncForksConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks until the connection is closed. This is the push-based + alternative to iterating with ``async for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + import asyncio + + async for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, ErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + for handler in generic: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + +class AsyncForksConnectionManager: + """ + Context manager over a `AsyncForksConnection` that is returned by `live.forks.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.live.forks.connect(...).enter() + # ... + await connection.close() + ``` + """ + + def __init__( + self, + *, + client: AsyncOpenAI, + session_id: str, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__session_id = session_id + self.__connection: AsyncForksConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=False) + + def send(self, event: ForkClientEvent | ForkClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, ForkClientEventParam)) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncForksConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``AsyncForksConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncForksConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncForksConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def __aenter__(self) -> AsyncForksConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.live.forks.connect(...).enter() + # ... + await connection.close() + ``` + """ + ws = await self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = AsyncForksConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + await self.__connection._flush_send_queue() + + return self.__connection + + enter = __aenter__ + + async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection: + try: + from ...lib._websocket import _WebSocketConnect as connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **extra_query, + }, + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + options = await self.__client._prepare_options( + FinalRequestOptions.construct( + method="get", + url=str(url), + headers=dict(extra_headers), + security={"bearer_auth": True}, + ) + ) + url = self.__client._prepare_url(options.url).copy_merge_params( + self.__client.qs.stringify(cast(Any, options.params)) + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + headers = { + key.lower(): (key, value) + for header_set in ( + self.__client.auth_headers, + {}, + self.__client.default_headers, + options.headers if is_given(options.headers) else {}, + ) + for key, value in header_set.items() + } + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + return await connect( + str(url), + user_agent_header=None, + additional_headers=_merge_mappings(dict(headers.values()), {}), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx2.URL(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + path, separator, query = base_url.raw_path.partition(b"?") + endpoint = path_template("/live/sessions/{session_id}/fork", session_id=self.__session_id).encode("utf-8") + merge_raw_path = path.rstrip(b"/") + endpoint + separator + query + return base_url.copy_with(raw_path=merge_raw_path) + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + await self.__connection.close() + + +class ForksConnection: + """Represents a live WebSocket connection to the Forks API""" + + session: ForksSessionResource + response: ForksResponseResource + + _connection: WebSocketConnection + + def __init__( + self, + connection: WebSocketConnection, + *, + make_ws: Callable[[Query, Headers], WebSocketConnection] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._reconnect_attempt = 0 + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue if send_queue is not None else SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=True) + + self.session = ForksSessionResource(self) + self.response = ForksResponseResource(self) + + def __iter__(self) -> Iterator[ForkServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + def recv(self) -> ForkServerEvent: + """ + Receive the next message from the connection and parses it into a `ForkServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + event = self.parse_event(self.recv_bytes()) + event_type = ( + cast("dict[str, object]", event).get("type") + if isinstance(cast(object, event), dict) + else getattr(event, "type", None) + ) + # A successful upgrade can still be followed by an admission error. + # Reset the budget only after receiving a non-error application event. + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return event + + def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `ForkServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = self._connection.recv(decode=False) + log.debug("Received WebSocket message: %i bytes", len(message)) + if self._reconnect_attempt: + # Account for raw application progress without changing frame delivery. + try: + event_data: object = json.loads(message) + except (ValueError, RecursionError): + return message + event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return message + + def send(self, event: ForkClientEvent | ForkClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, ForkClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + self._connection.send(data) + + def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + self._connection.send(data) + + def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> ForkServerEvent: + """ + Converts a raw `str` or `bytes` message into a `ForkServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast(ForkServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, ForkServerEvent))) + + def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1): + self._reconnect_attempt = attempt + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + time.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + try: + self._send_queue.flush_sync(lambda data: self._connection.send(data), requeue_failed=False) + except Exception: + log.warning("Failed to flush send queue after reconnect") + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ForksConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("session.started", my_handler) + + Or as a decorator:: + + @connection.on("session.started") + def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> ForksConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ForksConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks the current thread until the connection is closed. This is the push-based + alternative to iterating with ``for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, ErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + handler(event) + + for handler in generic: + handler(event) + + +class ForksConnectionManager: + """ + Context manager over a `ForksConnection` that is returned by `live.forks.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.live.forks.connect(...).enter() + # ... + connection.close() + ``` + """ + + def __init__( + self, + *, + client: OpenAI, + session_id: str, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__session_id = session_id + self.__connection: ForksConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=True) + + def send(self, event: ForkClientEvent | ForkClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, ForkClientEventParam)) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ForksConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``ForksConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> ForksConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ForksConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def __enter__(self) -> ForksConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.live.forks.connect(...).enter() + # ... + connection.close() + ``` + """ + ws = self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = ForksConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + self.__connection._flush_send_queue() + + return self.__connection + + enter = __enter__ + + def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketConnection: + try: + from websockets.sync.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **extra_query, + }, + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + options = self.__client._prepare_options( + FinalRequestOptions.construct( + method="get", + url=str(url), + headers=dict(extra_headers), + security={"bearer_auth": True}, + ) + ) + url = self.__client._prepare_url(options.url).copy_merge_params( + self.__client.qs.stringify(cast(Any, options.params)) + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + headers = { + key.lower(): (key, value) + for header_set in ( + self.__client.auth_headers, + {}, + self.__client.default_headers, + options.headers if is_given(options.headers) else {}, + ) + for key, value in header_set.items() + } + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + return connect( + str(url), + user_agent_header=None, + additional_headers=_merge_mappings(dict(headers.values()), {}), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx2.URL(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + path, separator, query = base_url.raw_path.partition(b"?") + endpoint = path_template("/live/sessions/{session_id}/fork", session_id=self.__session_id).encode("utf-8") + merge_raw_path = path.rstrip(b"/") + endpoint + separator + query + return base_url.copy_with(raw_path=merge_raw_path) + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + self.__connection.close() + + +class BaseForksConnectionResource: + def __init__(self, connection: ForksConnection) -> None: + self._connection = connection + + +class ForksSessionResource(BaseForksConnectionResource): + @cached_property + def commentary(self) -> ForksSessionCommentaryResource: + return ForksSessionCommentaryResource(self._connection) + + @cached_property + def input_audio(self) -> ForksSessionInputAudioResource: + return ForksSessionInputAudioResource(self._connection) + + @cached_property + def instructions(self) -> ForksSessionInstructionsResource: + return ForksSessionInstructionsResource(self._connection) + + @cached_property + def thinking(self) -> ForksSessionThinkingResource: + return ForksSessionThinkingResource(self._connection) + + def start(self, *, session: ForkSessionConfigParam, event_id: Optional[str] | Omit = omit) -> None: + """ + Start a Live session after connecting to a stored session’s fork WebSocket. + + Send an empty `session` object to use the stored configuration. + """ + self._connection.send( + cast( + ForkClientEventParam, + strip_not_given({"type": "session.start", "session": session, "event_id": event_id}), + ) + ) + + def close(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Request that the Live session close. + + The terminal `session.closed` event contains the close reason and final usage. + """ + self._connection.send( + cast(ForkClientEventParam, strip_not_given({"type": "session.close", "event_id": event_id})) + ) + + def update(self, *, session: SessionUpdateConfigParam, event_id: Optional[str] | Omit = omit) -> None: + """Update the delegation settings of an active Live session. + + The server acknowledges accepted changes with `session.updated`. + """ + self._connection.send( + cast( + ForkClientEventParam, + strip_not_given({"type": "session.update", "session": session, "event_id": event_id}), + ) + ) + + +class ForksSessionCommentaryResource(BaseForksConnectionResource): + def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None: + """ + Provide context the Live model can communicate to the user, optionally for an existing client delegation. + """ + self._connection.send( + cast( + ForkClientEventParam, + strip_not_given( + { + "type": "session.commentary.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class ForksSessionInputAudioResource(BaseForksConnectionResource): + def append(self, *, audio: str, event_id: Optional[str] | Omit = omit) -> None: + """Send audio to a Live session over its primary WebSocket. + + WebRTC and SIP sessions send audio over their media transport. + """ + self._connection.send( + cast( + ForkClientEventParam, + strip_not_given({"type": "session.input_audio.append", "audio": audio, "event_id": event_id}), + ) + ) + + def mute(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Mute audio input to the Live model without closing the session. + + The server acknowledges with `session.input_audio.muted`. + """ + self._connection.send( + cast(ForkClientEventParam, strip_not_given({"type": "session.input_audio.mute", "event_id": event_id})) + ) + + def unmute(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Resume audio input to a Live model after muting it. + + The server acknowledges with `session.input_audio.unmuted`. + """ + self._connection.send( + cast(ForkClientEventParam, strip_not_given({"type": "session.input_audio.unmute", "event_id": event_id})) + ) + + +class ForksSessionInstructionsResource(BaseForksConnectionResource): + def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None: + """ + Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation. + """ + self._connection.send( + cast( + ForkClientEventParam, + strip_not_given( + { + "type": "session.instructions.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class ForksSessionThinkingResource(BaseForksConnectionResource): + def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None: + """ + Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation. + """ + self._connection.send( + cast( + ForkClientEventParam, + strip_not_given( + { + "type": "session.thinking.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class ForksResponseResource(BaseForksConnectionResource): + @cached_property + def item(self) -> ForksResponseItemResource: + return ForksResponseItemResource(self._connection) + + def create(self, *, event_id: Optional[str] | Omit = omit) -> None: + """ + Request a response from the Live session’s Responses backend, or continue a + delegated response waiting for tool results. + + Requires Responses delegation. + """ + self._connection.send( + cast(ForkClientEventParam, strip_not_given({"type": "response.create", "event_id": event_id})) + ) + + +class ForksResponseItemResource(BaseForksConnectionResource): + def create(self, *, item: ResponseInputItemParam, event_id: Optional[str] | Omit = omit) -> None: + """Add an input item to the Live session’s Responses backend. + + Requires Responses delegation; use `response.create` to request a response. + """ + self._connection.send( + cast( + ForkClientEventParam, + strip_not_given({"type": "response.item.create", "item": item, "event_id": event_id}), + ) + ) + + +class BaseAsyncForksConnectionResource: + def __init__(self, connection: AsyncForksConnection) -> None: + self._connection = connection + + +class AsyncForksSessionResource(BaseAsyncForksConnectionResource): + @cached_property + def commentary(self) -> AsyncForksSessionCommentaryResource: + return AsyncForksSessionCommentaryResource(self._connection) + + @cached_property + def input_audio(self) -> AsyncForksSessionInputAudioResource: + return AsyncForksSessionInputAudioResource(self._connection) + + @cached_property + def instructions(self) -> AsyncForksSessionInstructionsResource: + return AsyncForksSessionInstructionsResource(self._connection) + + @cached_property + def thinking(self) -> AsyncForksSessionThinkingResource: + return AsyncForksSessionThinkingResource(self._connection) + + async def start(self, *, session: ForkSessionConfigParam, event_id: Optional[str] | Omit = omit) -> None: + """ + Start a Live session after connecting to a stored session’s fork WebSocket. + + Send an empty `session` object to use the stored configuration. + """ + await self._connection.send( + cast( + ForkClientEventParam, + strip_not_given({"type": "session.start", "session": session, "event_id": event_id}), + ) + ) + + async def close(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Request that the Live session close. + + The terminal `session.closed` event contains the close reason and final usage. + """ + await self._connection.send( + cast(ForkClientEventParam, strip_not_given({"type": "session.close", "event_id": event_id})) + ) + + async def update(self, *, session: SessionUpdateConfigParam, event_id: Optional[str] | Omit = omit) -> None: + """Update the delegation settings of an active Live session. + + The server acknowledges accepted changes with `session.updated`. + """ + await self._connection.send( + cast( + ForkClientEventParam, + strip_not_given({"type": "session.update", "session": session, "event_id": event_id}), + ) + ) + + +class AsyncForksSessionCommentaryResource(BaseAsyncForksConnectionResource): + async def append( + self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit + ) -> None: + """ + Provide context the Live model can communicate to the user, optionally for an existing client delegation. + """ + await self._connection.send( + cast( + ForkClientEventParam, + strip_not_given( + { + "type": "session.commentary.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class AsyncForksSessionInputAudioResource(BaseAsyncForksConnectionResource): + async def append(self, *, audio: str, event_id: Optional[str] | Omit = omit) -> None: + """Send audio to a Live session over its primary WebSocket. + + WebRTC and SIP sessions send audio over their media transport. + """ + await self._connection.send( + cast( + ForkClientEventParam, + strip_not_given({"type": "session.input_audio.append", "audio": audio, "event_id": event_id}), + ) + ) + + async def mute(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Mute audio input to the Live model without closing the session. + + The server acknowledges with `session.input_audio.muted`. + """ + await self._connection.send( + cast(ForkClientEventParam, strip_not_given({"type": "session.input_audio.mute", "event_id": event_id})) + ) + + async def unmute(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Resume audio input to a Live model after muting it. + + The server acknowledges with `session.input_audio.unmuted`. + """ + await self._connection.send( + cast(ForkClientEventParam, strip_not_given({"type": "session.input_audio.unmute", "event_id": event_id})) + ) + + +class AsyncForksSessionInstructionsResource(BaseAsyncForksConnectionResource): + async def append( + self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit + ) -> None: + """ + Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation. + """ + await self._connection.send( + cast( + ForkClientEventParam, + strip_not_given( + { + "type": "session.instructions.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class AsyncForksSessionThinkingResource(BaseAsyncForksConnectionResource): + async def append( + self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit + ) -> None: + """ + Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation. + """ + await self._connection.send( + cast( + ForkClientEventParam, + strip_not_given( + { + "type": "session.thinking.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class AsyncForksResponseResource(BaseAsyncForksConnectionResource): + @cached_property + def item(self) -> AsyncForksResponseItemResource: + return AsyncForksResponseItemResource(self._connection) + + async def create(self, *, event_id: Optional[str] | Omit = omit) -> None: + """ + Request a response from the Live session’s Responses backend, or continue a + delegated response waiting for tool results. + + Requires Responses delegation. + """ + await self._connection.send( + cast(ForkClientEventParam, strip_not_given({"type": "response.create", "event_id": event_id})) + ) + + +class AsyncForksResponseItemResource(BaseAsyncForksConnectionResource): + async def create(self, *, item: ResponseInputItemParam, event_id: Optional[str] | Omit = omit) -> None: + """Add an input item to the Live session’s Responses backend. + + Requires Responses delegation; use `response.create` to request a response. + """ + await self._connection.send( + cast( + ForkClientEventParam, + strip_not_given({"type": "response.item.create", "item": item, "event_id": event_id}), + ) + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/live/live.py b/venv/lib/python3.12/site-packages/openai/resources/live/live.py new file mode 100644 index 0000000..19a7879 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/live/live.py @@ -0,0 +1,1651 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import json +import time +import random +import logging +from types import TracebackType +from typing import TYPE_CHECKING, Any, Union, Callable, Iterator, Optional, Awaitable, cast +from typing_extensions import AsyncIterator + +import httpx2 +from pydantic import BaseModel + +from ... import _legacy_response +from .forks import Forks, AsyncForks +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import is_given, maybe_transform, strip_not_given, async_maybe_transform +from .sessions import ( + Sessions, + AsyncSessions, + SessionsWithRawResponse, + AsyncSessionsWithRawResponse, + SessionsWithStreamingResponse, + AsyncSessionsWithStreamingResponse, +) +from .sideband import Sideband, AsyncSideband +from ..._compat import cached_property +from ..._models import FinalRequestOptions, construct_type_unchecked +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...types.live import live_create_params +from ..._exceptions import OpenAIError, WebSocketConnectionClosedError +from ..._send_queue import SendQueue +from ..._base_client import _merge_mappings, make_request_options +from ..._event_handler import EventHandlerRegistry +from ...types.live.error_event import ErrorEvent +from ...types.live.client_event import ClientEvent +from ...types.live.server_event import ServerEvent +from ...types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close +from ...types.live.client_event_param import ClientEventParam +from ...types.live.live_create_response import LiveCreateResponse +from ...types.live.session_config_param import SessionConfigParam +from ...types.websocket_connection_options import WebSocketConnectionOptions +from ...types.live.media_session_config_param import MediaSessionConfigParam +from ...types.live.session_update_config_param import SessionUpdateConfigParam +from ...types.responses.response_input_item_param import ResponseInputItemParam + +if TYPE_CHECKING: + from websockets.sync.client import ClientConnection as WebSocketConnection + from websockets.asyncio.client import ClientConnection as AsyncWebSocketConnection + + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Live", "AsyncLive"] + +log: logging.Logger = logging.getLogger(__name__) + + +class Live(SyncAPIResource): + @cached_property + def sideband(self) -> Sideband: + return Sideband(self._client) + + @cached_property + def forks(self) -> Forks: + return Forks(self._client) + + @cached_property + def sessions(self) -> Sessions: + return Sessions(self._client) + + @cached_property + def with_raw_response(self) -> LiveWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return LiveWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> LiveWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return LiveWithStreamingResponse(self) + + def create( + self, + *, + session: MediaSessionConfigParam, + transport: live_create_params.Transport, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> LiveCreateResponse: + """Create a Live WebRTC session. + + Start with the + [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting). + + Args: + session: Startup configuration for the Live session. + + transport: WebRTC transport with the browser's SDP offer. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/live/sessions", + body=maybe_transform( + { + "session": session, + "transport": transport, + }, + live_create_params.LiveCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=LiveCreateResponse, + ) + + def connect( + self, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> LiveConnectionManager: + """Connect to Live without query parameters. + + Send session.start with model and session configuration, then wait for session.started. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) before writing instructions. + """ + return LiveConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + ) + + +class AsyncLive(AsyncAPIResource): + @cached_property + def sideband(self) -> AsyncSideband: + return AsyncSideband(self._client) + + @cached_property + def forks(self) -> AsyncForks: + return AsyncForks(self._client) + + @cached_property + def sessions(self) -> AsyncSessions: + return AsyncSessions(self._client) + + @cached_property + def with_raw_response(self) -> AsyncLiveWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncLiveWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncLiveWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncLiveWithStreamingResponse(self) + + async def create( + self, + *, + session: MediaSessionConfigParam, + transport: live_create_params.Transport, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> LiveCreateResponse: + """Create a Live WebRTC session. + + Start with the + [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting). + + Args: + session: Startup configuration for the Live session. + + transport: WebRTC transport with the browser's SDP offer. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/live/sessions", + body=await async_maybe_transform( + { + "session": session, + "transport": transport, + }, + live_create_params.LiveCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=LiveCreateResponse, + ) + + def connect( + self, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> AsyncLiveConnectionManager: + """Connect to Live without query parameters. + + Send session.start with model and session configuration, then wait for session.started. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) before writing instructions. + """ + return AsyncLiveConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + ) + + +class LiveWithRawResponse: + def __init__(self, live: Live) -> None: + self._live = live + + self.create = _legacy_response.to_raw_response_wrapper( + live.create, + ) + + @cached_property + def sessions(self) -> SessionsWithRawResponse: + return SessionsWithRawResponse(self._live.sessions) + + +class AsyncLiveWithRawResponse: + def __init__(self, live: AsyncLive) -> None: + self._live = live + + self.create = _legacy_response.async_to_raw_response_wrapper( + live.create, + ) + + @cached_property + def sessions(self) -> AsyncSessionsWithRawResponse: + return AsyncSessionsWithRawResponse(self._live.sessions) + + +class LiveWithStreamingResponse: + def __init__(self, live: Live) -> None: + self._live = live + + self.create = to_streamed_response_wrapper( + live.create, + ) + + @cached_property + def sessions(self) -> SessionsWithStreamingResponse: + return SessionsWithStreamingResponse(self._live.sessions) + + +class AsyncLiveWithStreamingResponse: + def __init__(self, live: AsyncLive) -> None: + self._live = live + + self.create = async_to_streamed_response_wrapper( + live.create, + ) + + @cached_property + def sessions(self) -> AsyncSessionsWithStreamingResponse: + return AsyncSessionsWithStreamingResponse(self._live.sessions) + + +class AsyncLiveConnection: + """Represents a live WebSocket connection to the Live API""" + + session: AsyncLiveSessionResource + response: AsyncLiveResponseResource + + _connection: AsyncWebSocketConnection + + def __init__( + self, + connection: AsyncWebSocketConnection, + *, + make_ws: Callable[[Query, Headers], Awaitable[AsyncWebSocketConnection]] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._reconnect_attempt = 0 + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue if send_queue is not None else SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=False) + + self.session = AsyncLiveSessionResource(self) + self.response = AsyncLiveResponseResource(self) + + async def __aiter__(self) -> AsyncIterator[ServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield await self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not await self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + async def recv(self) -> ServerEvent: + """ + Receive the next message from the connection and parses it into a `ServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + event = self.parse_event(await self.recv_bytes()) + event_type = ( + cast("dict[str, object]", event).get("type") + if isinstance(cast(object, event), dict) + else getattr(event, "type", None) + ) + # A successful upgrade can still be followed by an admission error. + # Reset the budget only after receiving a non-error application event. + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return event + + async def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `ServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = await self._connection.recv(decode=False) + log.debug("Received WebSocket message: %i bytes", len(message)) + if self._reconnect_attempt: + # Account for raw application progress without changing frame delivery. + try: + event_data: object = json.loads(message) + except (ValueError, RecursionError): + return message + event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return message + + async def send(self, event: ClientEvent | ClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(await async_maybe_transform(event, ClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + await self._connection.send(data) + + async def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + await self._connection.send(data) + + async def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + await self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> ServerEvent: + """ + Converts a raw `str` or `bytes` message into a `ServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast(ServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, ServerEvent))) + + async def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + import asyncio + + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1): + self._reconnect_attempt = attempt + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + await asyncio.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = await self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + await self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + async def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + + async def _send(data: str) -> None: + await self._connection.send(data) + + try: + await self._send_queue.flush_async(_send, requeue_failed=False) + except Exception: + log.warning("Failed to flush send queue after reconnect") + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncLiveConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("session.started", my_handler) + + Or as a decorator:: + + @connection.on("session.started") + async def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncLiveConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncLiveConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks until the connection is closed. This is the push-based + alternative to iterating with ``async for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + import asyncio + + async for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, ErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + for handler in generic: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + +class AsyncLiveConnectionManager: + """ + Context manager over a `AsyncLiveConnection` that is returned by `live.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.live.connect(...).enter() + # ... + await connection.close() + ``` + """ + + def __init__( + self, + *, + client: AsyncOpenAI, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__connection: AsyncLiveConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=False) + + def send(self, event: ClientEvent | ClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, ClientEventParam)) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncLiveConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``AsyncLiveConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncLiveConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncLiveConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def __aenter__(self) -> AsyncLiveConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.live.connect(...).enter() + # ... + await connection.close() + ``` + """ + ws = await self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = AsyncLiveConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + await self.__connection._flush_send_queue() + + return self.__connection + + enter = __aenter__ + + async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection: + try: + from ...lib._websocket import _WebSocketConnect as connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **extra_query, + }, + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + options = await self.__client._prepare_options( + FinalRequestOptions.construct( + method="get", + url=str(url), + headers=dict(extra_headers), + security={"bearer_auth": True}, + ) + ) + url = self.__client._prepare_url(options.url).copy_merge_params( + self.__client.qs.stringify(cast(Any, options.params)) + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + headers = { + key.lower(): (key, value) + for header_set in ( + self.__client.auth_headers, + {}, + self.__client.default_headers, + options.headers if is_given(options.headers) else {}, + ) + for key, value in header_set.items() + } + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + return await connect( + str(url), + user_agent_header=None, + additional_headers=_merge_mappings(dict(headers.values()), {}), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx2.URL(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + path, separator, query = base_url.raw_path.partition(b"?") + merge_raw_path = path.rstrip(b"/") + b"/live/sessions" + separator + query + return base_url.copy_with(raw_path=merge_raw_path) + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + await self.__connection.close() + + +class LiveConnection: + """Represents a live WebSocket connection to the Live API""" + + session: LiveSessionResource + response: LiveResponseResource + + _connection: WebSocketConnection + + def __init__( + self, + connection: WebSocketConnection, + *, + make_ws: Callable[[Query, Headers], WebSocketConnection] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._reconnect_attempt = 0 + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue if send_queue is not None else SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=True) + + self.session = LiveSessionResource(self) + self.response = LiveResponseResource(self) + + def __iter__(self) -> Iterator[ServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + def recv(self) -> ServerEvent: + """ + Receive the next message from the connection and parses it into a `ServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + event = self.parse_event(self.recv_bytes()) + event_type = ( + cast("dict[str, object]", event).get("type") + if isinstance(cast(object, event), dict) + else getattr(event, "type", None) + ) + # A successful upgrade can still be followed by an admission error. + # Reset the budget only after receiving a non-error application event. + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return event + + def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `ServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = self._connection.recv(decode=False) + log.debug("Received WebSocket message: %i bytes", len(message)) + if self._reconnect_attempt: + # Account for raw application progress without changing frame delivery. + try: + event_data: object = json.loads(message) + except (ValueError, RecursionError): + return message + event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return message + + def send(self, event: ClientEvent | ClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, ClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + self._connection.send(data) + + def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + self._connection.send(data) + + def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> ServerEvent: + """ + Converts a raw `str` or `bytes` message into a `ServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast(ServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, ServerEvent))) + + def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1): + self._reconnect_attempt = attempt + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + time.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + try: + self._send_queue.flush_sync(lambda data: self._connection.send(data), requeue_failed=False) + except Exception: + log.warning("Failed to flush send queue after reconnect") + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[LiveConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("session.started", my_handler) + + Or as a decorator:: + + @connection.on("session.started") + def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> LiveConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[LiveConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks the current thread until the connection is closed. This is the push-based + alternative to iterating with ``for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, ErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + handler(event) + + for handler in generic: + handler(event) + + +class LiveConnectionManager: + """Context manager over a `LiveConnection` that is returned by `live.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.live.connect(...).enter() + # ... + connection.close() + ``` + """ + + def __init__( + self, + *, + client: OpenAI, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__connection: LiveConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=True) + + def send(self, event: ClientEvent | ClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, ClientEventParam)) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[LiveConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``LiveConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> LiveConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[LiveConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def __enter__(self) -> LiveConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.live.connect(...).enter() + # ... + connection.close() + ``` + """ + ws = self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = LiveConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + self.__connection._flush_send_queue() + + return self.__connection + + enter = __enter__ + + def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketConnection: + try: + from websockets.sync.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **extra_query, + }, + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + options = self.__client._prepare_options( + FinalRequestOptions.construct( + method="get", + url=str(url), + headers=dict(extra_headers), + security={"bearer_auth": True}, + ) + ) + url = self.__client._prepare_url(options.url).copy_merge_params( + self.__client.qs.stringify(cast(Any, options.params)) + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + headers = { + key.lower(): (key, value) + for header_set in ( + self.__client.auth_headers, + {}, + self.__client.default_headers, + options.headers if is_given(options.headers) else {}, + ) + for key, value in header_set.items() + } + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + return connect( + str(url), + user_agent_header=None, + additional_headers=_merge_mappings(dict(headers.values()), {}), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx2.URL(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + path, separator, query = base_url.raw_path.partition(b"?") + merge_raw_path = path.rstrip(b"/") + b"/live/sessions" + separator + query + return base_url.copy_with(raw_path=merge_raw_path) + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + self.__connection.close() + + +class BaseLiveConnectionResource: + def __init__(self, connection: LiveConnection) -> None: + self._connection = connection + + +class LiveSessionResource(BaseLiveConnectionResource): + @cached_property + def commentary(self) -> LiveSessionCommentaryResource: + return LiveSessionCommentaryResource(self._connection) + + @cached_property + def input_audio(self) -> LiveSessionInputAudioResource: + return LiveSessionInputAudioResource(self._connection) + + @cached_property + def instructions(self) -> LiveSessionInstructionsResource: + return LiveSessionInstructionsResource(self._connection) + + @cached_property + def thinking(self) -> LiveSessionThinkingResource: + return LiveSessionThinkingResource(self._connection) + + def close(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Request that the Live session close. + + The terminal `session.closed` event contains the close reason and final usage. + """ + self._connection.send(cast(ClientEventParam, strip_not_given({"type": "session.close", "event_id": event_id}))) + + def update(self, *, session: SessionUpdateConfigParam, event_id: Optional[str] | Omit = omit) -> None: + """Update the delegation settings of an active Live session. + + The server acknowledges accepted changes with `session.updated`. + """ + self._connection.send( + cast( + ClientEventParam, strip_not_given({"type": "session.update", "session": session, "event_id": event_id}) + ) + ) + + def start(self, *, session: SessionConfigParam, event_id: Optional[str] | Omit = omit) -> None: + """Start a Live session on a primary WebSocket. + + Send this event before other commands and wait for `session.started`. + """ + self._connection.send( + cast(ClientEventParam, strip_not_given({"type": "session.start", "session": session, "event_id": event_id})) + ) + + +class LiveSessionCommentaryResource(BaseLiveConnectionResource): + def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None: + """ + Provide context the Live model can communicate to the user, optionally for an existing client delegation. + """ + self._connection.send( + cast( + ClientEventParam, + strip_not_given( + { + "type": "session.commentary.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class LiveSessionInputAudioResource(BaseLiveConnectionResource): + def append(self, *, audio: str, event_id: Optional[str] | Omit = omit) -> None: + """Send audio to a Live session over its primary WebSocket. + + WebRTC and SIP sessions send audio over their media transport. + """ + self._connection.send( + cast( + ClientEventParam, + strip_not_given({"type": "session.input_audio.append", "audio": audio, "event_id": event_id}), + ) + ) + + def mute(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Mute audio input to the Live model without closing the session. + + The server acknowledges with `session.input_audio.muted`. + """ + self._connection.send( + cast(ClientEventParam, strip_not_given({"type": "session.input_audio.mute", "event_id": event_id})) + ) + + def unmute(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Resume audio input to a Live model after muting it. + + The server acknowledges with `session.input_audio.unmuted`. + """ + self._connection.send( + cast(ClientEventParam, strip_not_given({"type": "session.input_audio.unmute", "event_id": event_id})) + ) + + +class LiveSessionInstructionsResource(BaseLiveConnectionResource): + def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None: + """ + Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation. + """ + self._connection.send( + cast( + ClientEventParam, + strip_not_given( + { + "type": "session.instructions.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class LiveSessionThinkingResource(BaseLiveConnectionResource): + def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None: + """ + Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation. + """ + self._connection.send( + cast( + ClientEventParam, + strip_not_given( + { + "type": "session.thinking.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class LiveResponseResource(BaseLiveConnectionResource): + @cached_property + def item(self) -> LiveResponseItemResource: + return LiveResponseItemResource(self._connection) + + def create(self, *, event_id: Optional[str] | Omit = omit) -> None: + """ + Request a response from the Live session’s Responses backend, or continue a + delegated response waiting for tool results. + + Requires Responses delegation. + """ + self._connection.send( + cast(ClientEventParam, strip_not_given({"type": "response.create", "event_id": event_id})) + ) + + +class LiveResponseItemResource(BaseLiveConnectionResource): + def create(self, *, item: ResponseInputItemParam, event_id: Optional[str] | Omit = omit) -> None: + """Add an input item to the Live session’s Responses backend. + + Requires Responses delegation; use `response.create` to request a response. + """ + self._connection.send( + cast( + ClientEventParam, strip_not_given({"type": "response.item.create", "item": item, "event_id": event_id}) + ) + ) + + +class BaseAsyncLiveConnectionResource: + def __init__(self, connection: AsyncLiveConnection) -> None: + self._connection = connection + + +class AsyncLiveSessionResource(BaseAsyncLiveConnectionResource): + @cached_property + def commentary(self) -> AsyncLiveSessionCommentaryResource: + return AsyncLiveSessionCommentaryResource(self._connection) + + @cached_property + def input_audio(self) -> AsyncLiveSessionInputAudioResource: + return AsyncLiveSessionInputAudioResource(self._connection) + + @cached_property + def instructions(self) -> AsyncLiveSessionInstructionsResource: + return AsyncLiveSessionInstructionsResource(self._connection) + + @cached_property + def thinking(self) -> AsyncLiveSessionThinkingResource: + return AsyncLiveSessionThinkingResource(self._connection) + + async def close(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Request that the Live session close. + + The terminal `session.closed` event contains the close reason and final usage. + """ + await self._connection.send( + cast(ClientEventParam, strip_not_given({"type": "session.close", "event_id": event_id})) + ) + + async def update(self, *, session: SessionUpdateConfigParam, event_id: Optional[str] | Omit = omit) -> None: + """Update the delegation settings of an active Live session. + + The server acknowledges accepted changes with `session.updated`. + """ + await self._connection.send( + cast( + ClientEventParam, strip_not_given({"type": "session.update", "session": session, "event_id": event_id}) + ) + ) + + async def start(self, *, session: SessionConfigParam, event_id: Optional[str] | Omit = omit) -> None: + """Start a Live session on a primary WebSocket. + + Send this event before other commands and wait for `session.started`. + """ + await self._connection.send( + cast(ClientEventParam, strip_not_given({"type": "session.start", "session": session, "event_id": event_id})) + ) + + +class AsyncLiveSessionCommentaryResource(BaseAsyncLiveConnectionResource): + async def append( + self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit + ) -> None: + """ + Provide context the Live model can communicate to the user, optionally for an existing client delegation. + """ + await self._connection.send( + cast( + ClientEventParam, + strip_not_given( + { + "type": "session.commentary.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class AsyncLiveSessionInputAudioResource(BaseAsyncLiveConnectionResource): + async def append(self, *, audio: str, event_id: Optional[str] | Omit = omit) -> None: + """Send audio to a Live session over its primary WebSocket. + + WebRTC and SIP sessions send audio over their media transport. + """ + await self._connection.send( + cast( + ClientEventParam, + strip_not_given({"type": "session.input_audio.append", "audio": audio, "event_id": event_id}), + ) + ) + + async def mute(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Mute audio input to the Live model without closing the session. + + The server acknowledges with `session.input_audio.muted`. + """ + await self._connection.send( + cast(ClientEventParam, strip_not_given({"type": "session.input_audio.mute", "event_id": event_id})) + ) + + async def unmute(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Resume audio input to a Live model after muting it. + + The server acknowledges with `session.input_audio.unmuted`. + """ + await self._connection.send( + cast(ClientEventParam, strip_not_given({"type": "session.input_audio.unmute", "event_id": event_id})) + ) + + +class AsyncLiveSessionInstructionsResource(BaseAsyncLiveConnectionResource): + async def append( + self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit + ) -> None: + """ + Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation. + """ + await self._connection.send( + cast( + ClientEventParam, + strip_not_given( + { + "type": "session.instructions.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class AsyncLiveSessionThinkingResource(BaseAsyncLiveConnectionResource): + async def append( + self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit + ) -> None: + """ + Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation. + """ + await self._connection.send( + cast( + ClientEventParam, + strip_not_given( + { + "type": "session.thinking.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class AsyncLiveResponseResource(BaseAsyncLiveConnectionResource): + @cached_property + def item(self) -> AsyncLiveResponseItemResource: + return AsyncLiveResponseItemResource(self._connection) + + async def create(self, *, event_id: Optional[str] | Omit = omit) -> None: + """ + Request a response from the Live session’s Responses backend, or continue a + delegated response waiting for tool results. + + Requires Responses delegation. + """ + await self._connection.send( + cast(ClientEventParam, strip_not_given({"type": "response.create", "event_id": event_id})) + ) + + +class AsyncLiveResponseItemResource(BaseAsyncLiveConnectionResource): + async def create(self, *, item: ResponseInputItemParam, event_id: Optional[str] | Omit = omit) -> None: + """Add an input item to the Live session’s Responses backend. + + Requires Responses delegation; use `response.create` to request a response. + """ + await self._connection.send( + cast( + ClientEventParam, strip_not_given({"type": "response.item.create", "item": item, "event_id": event_id}) + ) + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/live/sessions.py b/venv/lib/python3.12/site-packages/openai/resources/live/sessions.py new file mode 100644 index 0000000..5fff8c3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/live/sessions.py @@ -0,0 +1,704 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from ... import _legacy_response +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_streamed_response_wrapper, + async_to_streamed_response_wrapper, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ...types.live import ( + session_fork_params, + session_refer_params, + session_accept_params, + session_reject_params, +) +from ..._base_client import make_request_options +from ...types.live.session_fork_response import SessionForkResponse +from ...types.live.media_session_fork_config_param import MediaSessionForkConfigParam + +__all__ = ["Sessions", "AsyncSessions"] + + +class Sessions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SessionsWithStreamingResponse(self) + + def accept( + self, + session_id: str, + *, + session: session_accept_params.Session, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """Accept an incoming SIP call. + + Supply session with type live, the model, and + startup configuration. Before accepting calls, follow the + [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) + to write frontend conversation instructions and a separate backend prompt. SIP + media format is negotiated; omit audio.format. + + Args: + session: Model and startup configuration for the Live session that answers the incoming + SIP call. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + path_template("/live/sessions/{session_id}/accept", session_id=session_id), + body=maybe_transform({"session": session}, session_accept_params.SessionAcceptParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + def download_recording( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Get Live session content + + Args: + session_id: The ID of the stored Live session to download. Use the session ID returned when + the session started with storage enabled. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return self._get( + path_template("/live/sessions/{session_id}/content", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + def fork( + self, + session_id: str, + *, + transport: session_fork_params.Transport, + session: MediaSessionForkConfigParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SessionForkResponse: + """ + Fork a stored Live session onto a new WebRTC connection. + + Args: + transport: WebRTC transport with an SDP offer for the new connection to the forked session. + + session: Optional configuration overrides for the new Live session. Omit this object or + send an empty object to inherit the stored session's settings. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + return self._post( + path_template("/live/sessions/{session_id}/fork", session_id=session_id), + body=maybe_transform( + { + "transport": transport, + "session": session, + }, + session_fork_params.SessionForkParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SessionForkResponse, + ) + + def hangup( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + End a SIP call identified by session_id. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + path_template("/live/sessions/{session_id}/hangup", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + def refer( + self, + session_id: str, + *, + target_uri: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """Transfer a SIP call to another destination. + + Supply a nonblank target_uri for the + SIP Refer-To header. + + Args: + target_uri: Nonblank URI for the SIP Refer-To header, such as tel:+14155550123 or + sip:agent@example.com. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + path_template("/live/sessions/{session_id}/refer", session_id=session_id), + body=maybe_transform({"target_uri": target_uri}, session_refer_params.SessionReferParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + def reject( + self, + session_id: str, + *, + status_code: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """Reject an incoming SIP call. + + Send a required SIP rejection status_code between + 300 and 699. + + Args: + status_code: SIP rejection status sent to the caller. This field is required. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + path_template("/live/sessions/{session_id}/reject", session_id=session_id), + body=maybe_transform({"status_code": status_code}, session_reject_params.SessionRejectParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + +class AsyncSessions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSessionsWithStreamingResponse(self) + + async def accept( + self, + session_id: str, + *, + session: session_accept_params.Session, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """Accept an incoming SIP call. + + Supply session with type live, the model, and + startup configuration. Before accepting calls, follow the + [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) + to write frontend conversation instructions and a separate backend prompt. SIP + media format is negotiated; omit audio.format. + + Args: + session: Model and startup configuration for the Live session that answers the incoming + SIP call. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + path_template("/live/sessions/{session_id}/accept", session_id=session_id), + body=await async_maybe_transform({"session": session}, session_accept_params.SessionAcceptParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + async def download_recording( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Get Live session content + + Args: + session_id: The ID of the stored Live session to download. Use the session ID returned when + the session started with storage enabled. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return await self._get( + path_template("/live/sessions/{session_id}/content", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + async def fork( + self, + session_id: str, + *, + transport: session_fork_params.Transport, + session: MediaSessionForkConfigParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SessionForkResponse: + """ + Fork a stored Live session onto a new WebRTC connection. + + Args: + transport: WebRTC transport with an SDP offer for the new connection to the forked session. + + session: Optional configuration overrides for the new Live session. Omit this object or + send an empty object to inherit the stored session's settings. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + return await self._post( + path_template("/live/sessions/{session_id}/fork", session_id=session_id), + body=await async_maybe_transform( + { + "transport": transport, + "session": session, + }, + session_fork_params.SessionForkParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SessionForkResponse, + ) + + async def hangup( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + End a SIP call identified by session_id. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + path_template("/live/sessions/{session_id}/hangup", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + async def refer( + self, + session_id: str, + *, + target_uri: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """Transfer a SIP call to another destination. + + Supply a nonblank target_uri for the + SIP Refer-To header. + + Args: + target_uri: Nonblank URI for the SIP Refer-To header, such as tel:+14155550123 or + sip:agent@example.com. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + path_template("/live/sessions/{session_id}/refer", session_id=session_id), + body=await async_maybe_transform({"target_uri": target_uri}, session_refer_params.SessionReferParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + async def reject( + self, + session_id: str, + *, + status_code: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """Reject an incoming SIP call. + + Send a required SIP rejection status_code between + 300 and 699. + + Args: + status_code: SIP rejection status sent to the caller. This field is required. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + path_template("/live/sessions/{session_id}/reject", session_id=session_id), + body=await async_maybe_transform({"status_code": status_code}, session_reject_params.SessionRejectParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + +class SessionsWithRawResponse: + def __init__(self, sessions: Sessions) -> None: + self._sessions = sessions + + self.accept = _legacy_response.to_raw_response_wrapper( + sessions.accept, + ) + self.download_recording = _legacy_response.to_raw_response_wrapper( + sessions.download_recording, + ) + self.fork = _legacy_response.to_raw_response_wrapper( + sessions.fork, + ) + self.hangup = _legacy_response.to_raw_response_wrapper( + sessions.hangup, + ) + self.refer = _legacy_response.to_raw_response_wrapper( + sessions.refer, + ) + self.reject = _legacy_response.to_raw_response_wrapper( + sessions.reject, + ) + + +class AsyncSessionsWithRawResponse: + def __init__(self, sessions: AsyncSessions) -> None: + self._sessions = sessions + + self.accept = _legacy_response.async_to_raw_response_wrapper( + sessions.accept, + ) + self.download_recording = _legacy_response.async_to_raw_response_wrapper( + sessions.download_recording, + ) + self.fork = _legacy_response.async_to_raw_response_wrapper( + sessions.fork, + ) + self.hangup = _legacy_response.async_to_raw_response_wrapper( + sessions.hangup, + ) + self.refer = _legacy_response.async_to_raw_response_wrapper( + sessions.refer, + ) + self.reject = _legacy_response.async_to_raw_response_wrapper( + sessions.reject, + ) + + +class SessionsWithStreamingResponse: + def __init__(self, sessions: Sessions) -> None: + self._sessions = sessions + + self.accept = to_streamed_response_wrapper( + sessions.accept, + ) + self.download_recording = to_custom_streamed_response_wrapper( + sessions.download_recording, + StreamedBinaryAPIResponse, + ) + self.fork = to_streamed_response_wrapper( + sessions.fork, + ) + self.hangup = to_streamed_response_wrapper( + sessions.hangup, + ) + self.refer = to_streamed_response_wrapper( + sessions.refer, + ) + self.reject = to_streamed_response_wrapper( + sessions.reject, + ) + + +class AsyncSessionsWithStreamingResponse: + def __init__(self, sessions: AsyncSessions) -> None: + self._sessions = sessions + + self.accept = async_to_streamed_response_wrapper( + sessions.accept, + ) + self.download_recording = async_to_custom_streamed_response_wrapper( + sessions.download_recording, + AsyncStreamedBinaryAPIResponse, + ) + self.fork = async_to_streamed_response_wrapper( + sessions.fork, + ) + self.hangup = async_to_streamed_response_wrapper( + sessions.hangup, + ) + self.refer = async_to_streamed_response_wrapper( + sessions.refer, + ) + self.reject = async_to_streamed_response_wrapper( + sessions.reject, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/live/sideband.py b/venv/lib/python3.12/site-packages/openai/resources/live/sideband.py new file mode 100644 index 0000000..83256c2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/live/sideband.py @@ -0,0 +1,1414 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import json +import time +import random +import logging +from types import TracebackType +from typing import TYPE_CHECKING, Any, Union, Callable, Iterator, Optional, Awaitable, cast +from typing_extensions import AsyncIterator + +import httpx2 +from pydantic import BaseModel + +from ..._types import Omit, Query, Headers, omit +from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform +from ..._compat import cached_property +from ..._models import FinalRequestOptions, construct_type_unchecked +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._exceptions import OpenAIError, WebSocketConnectionClosedError +from ..._send_queue import SendQueue +from ..._base_client import _merge_mappings +from ..._event_handler import EventHandlerRegistry +from ...types.live.error_event import ErrorEvent +from ...types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close +from ...types.live.connect_client_event import ConnectClientEvent +from ...types.live.connect_server_event import ConnectServerEvent +from ...types.websocket_connection_options import WebSocketConnectionOptions +from ...types.live.connect_client_event_param import ConnectClientEventParam +from ...types.live.session_update_config_param import SessionUpdateConfigParam +from ...types.responses.response_input_item_param import ResponseInputItemParam + +if TYPE_CHECKING: + from websockets.sync.client import ClientConnection as WebSocketConnection + from websockets.asyncio.client import ClientConnection as AsyncWebSocketConnection + + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Sideband", "AsyncSideband"] + +log: logging.Logger = logging.getLogger(__name__) + + +class Sideband(SyncAPIResource): + def connect( + self, + *, + session_id: str, + graceful_close: bool | Omit = omit, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> SidebandConnectionManager: + """Attach to an existing Live session. + + Do not send session.start again. + """ + return SidebandConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + session_id=session_id, + graceful_close=graceful_close, + ) + + +class AsyncSideband(AsyncAPIResource): + def connect( + self, + *, + session_id: str, + graceful_close: bool | Omit = omit, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> AsyncSidebandConnectionManager: + """Attach to an existing Live session. + + Do not send session.start again. + """ + return AsyncSidebandConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + session_id=session_id, + graceful_close=graceful_close, + ) + + +class AsyncSidebandConnection: + """Represents a live WebSocket connection to the Sideband API""" + + session: AsyncSidebandSessionResource + response: AsyncSidebandResponseResource + + _connection: AsyncWebSocketConnection + + def __init__( + self, + connection: AsyncWebSocketConnection, + *, + make_ws: Callable[[Query, Headers], Awaitable[AsyncWebSocketConnection]] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._reconnect_attempt = 0 + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue if send_queue is not None else SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=False) + + self.session = AsyncSidebandSessionResource(self) + self.response = AsyncSidebandResponseResource(self) + + async def __aiter__(self) -> AsyncIterator[ConnectServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield await self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not await self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + async def recv(self) -> ConnectServerEvent: + """ + Receive the next message from the connection and parses it into a `ConnectServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + event = self.parse_event(await self.recv_bytes()) + event_type = ( + cast("dict[str, object]", event).get("type") + if isinstance(cast(object, event), dict) + else getattr(event, "type", None) + ) + # A successful upgrade can still be followed by an admission error. + # Reset the budget only after receiving a non-error application event. + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return event + + async def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `ConnectServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = await self._connection.recv(decode=False) + log.debug("Received WebSocket message: %i bytes", len(message)) + if self._reconnect_attempt: + # Account for raw application progress without changing frame delivery. + try: + event_data: object = json.loads(message) + except (ValueError, RecursionError): + return message + event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return message + + async def send(self, event: ConnectClientEvent | ConnectClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(await async_maybe_transform(event, ConnectClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + await self._connection.send(data) + + async def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + await self._connection.send(data) + + async def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + await self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> ConnectServerEvent: + """ + Converts a raw `str` or `bytes` message into a `ConnectServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + ConnectServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, ConnectServerEvent)) + ) + + async def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + import asyncio + + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1): + self._reconnect_attempt = attempt + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + await asyncio.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = await self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + await self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + async def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + + async def _send(data: str) -> None: + await self._connection.send(data) + + try: + await self._send_queue.flush_async(_send, requeue_failed=False) + except Exception: + log.warning("Failed to flush send queue after reconnect") + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncSidebandConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("session.started", my_handler) + + Or as a decorator:: + + @connection.on("session.started") + async def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncSidebandConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncSidebandConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks until the connection is closed. This is the push-based + alternative to iterating with ``async for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + import asyncio + + async for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, ErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + for handler in generic: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + +class AsyncSidebandConnectionManager: + """ + Context manager over a `AsyncSidebandConnection` that is returned by `live.sideband.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.live.sideband.connect(...).enter() + # ... + await connection.close() + ``` + """ + + def __init__( + self, + *, + client: AsyncOpenAI, + session_id: str, + graceful_close: bool | Omit = omit, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__session_id = session_id + self.__graceful_close = graceful_close + self.__connection: AsyncSidebandConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=False) + + def send(self, event: ConnectClientEvent | ConnectClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, ConnectClientEventParam)) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncSidebandConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``AsyncSidebandConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncSidebandConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncSidebandConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def __aenter__(self) -> AsyncSidebandConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.live.sideband.connect(...).enter() + # ... + await connection.close() + ``` + """ + ws = await self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = AsyncSidebandConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + await self.__connection._flush_send_queue() + + return self.__connection + + enter = __aenter__ + + async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection: + try: + from ...lib._websocket import _WebSocketConnect as connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **({"graceful_close": self.__graceful_close} if self.__graceful_close is not omit else {}), + **extra_query, + }, + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + options = await self.__client._prepare_options( + FinalRequestOptions.construct( + method="get", + url=str(url), + headers=dict(extra_headers), + security={"bearer_auth": True}, + ) + ) + url = self.__client._prepare_url(options.url).copy_merge_params( + self.__client.qs.stringify(cast(Any, options.params)) + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + headers = { + key.lower(): (key, value) + for header_set in ( + self.__client.auth_headers, + {}, + self.__client.default_headers, + options.headers if is_given(options.headers) else {}, + ) + for key, value in header_set.items() + } + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + return await connect( + str(url), + user_agent_header=None, + additional_headers=_merge_mappings(dict(headers.values()), {}), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx2.URL(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + path, separator, query = base_url.raw_path.partition(b"?") + endpoint = path_template("/live/sessions/{session_id}/attach", session_id=self.__session_id).encode("utf-8") + merge_raw_path = path.rstrip(b"/") + endpoint + separator + query + return base_url.copy_with(raw_path=merge_raw_path) + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + await self.__connection.close() + + +class SidebandConnection: + """Represents a live WebSocket connection to the Sideband API""" + + session: SidebandSessionResource + response: SidebandResponseResource + + _connection: WebSocketConnection + + def __init__( + self, + connection: WebSocketConnection, + *, + make_ws: Callable[[Query, Headers], WebSocketConnection] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._reconnect_attempt = 0 + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue if send_queue is not None else SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=True) + + self.session = SidebandSessionResource(self) + self.response = SidebandResponseResource(self) + + def __iter__(self) -> Iterator[ConnectServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + def recv(self) -> ConnectServerEvent: + """ + Receive the next message from the connection and parses it into a `ConnectServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + event = self.parse_event(self.recv_bytes()) + event_type = ( + cast("dict[str, object]", event).get("type") + if isinstance(cast(object, event), dict) + else getattr(event, "type", None) + ) + # A successful upgrade can still be followed by an admission error. + # Reset the budget only after receiving a non-error application event. + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return event + + def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `ConnectServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = self._connection.recv(decode=False) + log.debug("Received WebSocket message: %i bytes", len(message)) + if self._reconnect_attempt: + # Account for raw application progress without changing frame delivery. + try: + event_data: object = json.loads(message) + except (ValueError, RecursionError): + return message + event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return message + + def send(self, event: ConnectClientEvent | ConnectClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, ConnectClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + self._connection.send(data) + + def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + self._connection.send(data) + + def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> ConnectServerEvent: + """ + Converts a raw `str` or `bytes` message into a `ConnectServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + ConnectServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, ConnectServerEvent)) + ) + + def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1): + self._reconnect_attempt = attempt + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + time.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + try: + self._send_queue.flush_sync(lambda data: self._connection.send(data), requeue_failed=False) + except Exception: + log.warning("Failed to flush send queue after reconnect") + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[SidebandConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("session.started", my_handler) + + Or as a decorator:: + + @connection.on("session.started") + def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> SidebandConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[SidebandConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks the current thread until the connection is closed. This is the push-based + alternative to iterating with ``for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, ErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + handler(event) + + for handler in generic: + handler(event) + + +class SidebandConnectionManager: + """ + Context manager over a `SidebandConnection` that is returned by `live.sideband.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.live.sideband.connect(...).enter() + # ... + connection.close() + ``` + """ + + def __init__( + self, + *, + client: OpenAI, + session_id: str, + graceful_close: bool | Omit = omit, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__session_id = session_id + self.__graceful_close = graceful_close + self.__connection: SidebandConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=True) + + def send(self, event: ConnectClientEvent | ConnectClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, ConnectClientEventParam)) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[SidebandConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``SidebandConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> SidebandConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[SidebandConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def __enter__(self) -> SidebandConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.live.sideband.connect(...).enter() + # ... + connection.close() + ``` + """ + ws = self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = SidebandConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + self.__connection._flush_send_queue() + + return self.__connection + + enter = __enter__ + + def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketConnection: + try: + from websockets.sync.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **({"graceful_close": self.__graceful_close} if self.__graceful_close is not omit else {}), + **extra_query, + }, + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + options = self.__client._prepare_options( + FinalRequestOptions.construct( + method="get", + url=str(url), + headers=dict(extra_headers), + security={"bearer_auth": True}, + ) + ) + url = self.__client._prepare_url(options.url).copy_merge_params( + self.__client.qs.stringify(cast(Any, options.params)) + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + headers = { + key.lower(): (key, value) + for header_set in ( + self.__client.auth_headers, + {}, + self.__client.default_headers, + options.headers if is_given(options.headers) else {}, + ) + for key, value in header_set.items() + } + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + return connect( + str(url), + user_agent_header=None, + additional_headers=_merge_mappings(dict(headers.values()), {}), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx2.URL(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + path, separator, query = base_url.raw_path.partition(b"?") + endpoint = path_template("/live/sessions/{session_id}/attach", session_id=self.__session_id).encode("utf-8") + merge_raw_path = path.rstrip(b"/") + endpoint + separator + query + return base_url.copy_with(raw_path=merge_raw_path) + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + self.__connection.close() + + +class BaseSidebandConnectionResource: + def __init__(self, connection: SidebandConnection) -> None: + self._connection = connection + + +class SidebandSessionResource(BaseSidebandConnectionResource): + @cached_property + def commentary(self) -> SidebandSessionCommentaryResource: + return SidebandSessionCommentaryResource(self._connection) + + @cached_property + def input_audio(self) -> SidebandSessionInputAudioResource: + return SidebandSessionInputAudioResource(self._connection) + + @cached_property + def instructions(self) -> SidebandSessionInstructionsResource: + return SidebandSessionInstructionsResource(self._connection) + + @cached_property + def thinking(self) -> SidebandSessionThinkingResource: + return SidebandSessionThinkingResource(self._connection) + + def close(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Request that the Live session close. + + The terminal `session.closed` event contains the close reason and final usage. + """ + self._connection.send( + cast(ConnectClientEventParam, strip_not_given({"type": "session.close", "event_id": event_id})) + ) + + def update(self, *, session: SessionUpdateConfigParam, event_id: Optional[str] | Omit = omit) -> None: + """Update the delegation settings of an active Live session. + + The server acknowledges accepted changes with `session.updated`. + """ + self._connection.send( + cast( + ConnectClientEventParam, + strip_not_given({"type": "session.update", "session": session, "event_id": event_id}), + ) + ) + + +class SidebandSessionCommentaryResource(BaseSidebandConnectionResource): + def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None: + """ + Provide context the Live model can communicate to the user, optionally for an existing client delegation. + """ + self._connection.send( + cast( + ConnectClientEventParam, + strip_not_given( + { + "type": "session.commentary.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class SidebandSessionInputAudioResource(BaseSidebandConnectionResource): + def mute(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Mute audio input to the Live model without closing the session. + + The server acknowledges with `session.input_audio.muted`. + """ + self._connection.send( + cast(ConnectClientEventParam, strip_not_given({"type": "session.input_audio.mute", "event_id": event_id})) + ) + + def unmute(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Resume audio input to a Live model after muting it. + + The server acknowledges with `session.input_audio.unmuted`. + """ + self._connection.send( + cast(ConnectClientEventParam, strip_not_given({"type": "session.input_audio.unmute", "event_id": event_id})) + ) + + +class SidebandSessionInstructionsResource(BaseSidebandConnectionResource): + def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None: + """ + Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation. + """ + self._connection.send( + cast( + ConnectClientEventParam, + strip_not_given( + { + "type": "session.instructions.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class SidebandSessionThinkingResource(BaseSidebandConnectionResource): + def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None: + """ + Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation. + """ + self._connection.send( + cast( + ConnectClientEventParam, + strip_not_given( + { + "type": "session.thinking.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class SidebandResponseResource(BaseSidebandConnectionResource): + @cached_property + def item(self) -> SidebandResponseItemResource: + return SidebandResponseItemResource(self._connection) + + def create(self, *, event_id: Optional[str] | Omit = omit) -> None: + """ + Request a response from the Live session’s Responses backend, or continue a + delegated response waiting for tool results. + + Requires Responses delegation. + """ + self._connection.send( + cast(ConnectClientEventParam, strip_not_given({"type": "response.create", "event_id": event_id})) + ) + + +class SidebandResponseItemResource(BaseSidebandConnectionResource): + def create(self, *, item: ResponseInputItemParam, event_id: Optional[str] | Omit = omit) -> None: + """Add an input item to the Live session’s Responses backend. + + Requires Responses delegation; use `response.create` to request a response. + """ + self._connection.send( + cast( + ConnectClientEventParam, + strip_not_given({"type": "response.item.create", "item": item, "event_id": event_id}), + ) + ) + + +class BaseAsyncSidebandConnectionResource: + def __init__(self, connection: AsyncSidebandConnection) -> None: + self._connection = connection + + +class AsyncSidebandSessionResource(BaseAsyncSidebandConnectionResource): + @cached_property + def commentary(self) -> AsyncSidebandSessionCommentaryResource: + return AsyncSidebandSessionCommentaryResource(self._connection) + + @cached_property + def input_audio(self) -> AsyncSidebandSessionInputAudioResource: + return AsyncSidebandSessionInputAudioResource(self._connection) + + @cached_property + def instructions(self) -> AsyncSidebandSessionInstructionsResource: + return AsyncSidebandSessionInstructionsResource(self._connection) + + @cached_property + def thinking(self) -> AsyncSidebandSessionThinkingResource: + return AsyncSidebandSessionThinkingResource(self._connection) + + async def close(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Request that the Live session close. + + The terminal `session.closed` event contains the close reason and final usage. + """ + await self._connection.send( + cast(ConnectClientEventParam, strip_not_given({"type": "session.close", "event_id": event_id})) + ) + + async def update(self, *, session: SessionUpdateConfigParam, event_id: Optional[str] | Omit = omit) -> None: + """Update the delegation settings of an active Live session. + + The server acknowledges accepted changes with `session.updated`. + """ + await self._connection.send( + cast( + ConnectClientEventParam, + strip_not_given({"type": "session.update", "session": session, "event_id": event_id}), + ) + ) + + +class AsyncSidebandSessionCommentaryResource(BaseAsyncSidebandConnectionResource): + async def append( + self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit + ) -> None: + """ + Provide context the Live model can communicate to the user, optionally for an existing client delegation. + """ + await self._connection.send( + cast( + ConnectClientEventParam, + strip_not_given( + { + "type": "session.commentary.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class AsyncSidebandSessionInputAudioResource(BaseAsyncSidebandConnectionResource): + async def mute(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Mute audio input to the Live model without closing the session. + + The server acknowledges with `session.input_audio.muted`. + """ + await self._connection.send( + cast(ConnectClientEventParam, strip_not_given({"type": "session.input_audio.mute", "event_id": event_id})) + ) + + async def unmute(self, *, event_id: Optional[str] | Omit = omit) -> None: + """Resume audio input to a Live model after muting it. + + The server acknowledges with `session.input_audio.unmuted`. + """ + await self._connection.send( + cast(ConnectClientEventParam, strip_not_given({"type": "session.input_audio.unmute", "event_id": event_id})) + ) + + +class AsyncSidebandSessionInstructionsResource(BaseAsyncSidebandConnectionResource): + async def append( + self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit + ) -> None: + """ + Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation. + """ + await self._connection.send( + cast( + ConnectClientEventParam, + strip_not_given( + { + "type": "session.instructions.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class AsyncSidebandSessionThinkingResource(BaseAsyncSidebandConnectionResource): + async def append( + self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit + ) -> None: + """ + Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation. + """ + await self._connection.send( + cast( + ConnectClientEventParam, + strip_not_given( + { + "type": "session.thinking.append", + "content": content, + "delegation_id": delegation_id, + "event_id": event_id, + } + ), + ) + ) + + +class AsyncSidebandResponseResource(BaseAsyncSidebandConnectionResource): + @cached_property + def item(self) -> AsyncSidebandResponseItemResource: + return AsyncSidebandResponseItemResource(self._connection) + + async def create(self, *, event_id: Optional[str] | Omit = omit) -> None: + """ + Request a response from the Live session’s Responses backend, or continue a + delegated response waiting for tool results. + + Requires Responses delegation. + """ + await self._connection.send( + cast(ConnectClientEventParam, strip_not_given({"type": "response.create", "event_id": event_id})) + ) + + +class AsyncSidebandResponseItemResource(BaseAsyncSidebandConnectionResource): + async def create(self, *, item: ResponseInputItemParam, event_id: Optional[str] | Omit = omit) -> None: + """Add an input item to the Live session’s Responses backend. + + Requires Responses delegation; use `response.create` to request a response. + """ + await self._connection.send( + cast( + ConnectClientEventParam, + strip_not_given({"type": "response.item.create", "item": item, "event_id": event_id}), + ) + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/models.py b/venv/lib/python3.12/site-packages/openai/resources/models.py new file mode 100644 index 0000000..209505f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/models.py @@ -0,0 +1,332 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from .. import _legacy_response +from .._types import Body, Query, Headers, NotGiven, not_given +from .._utils import path_template +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..pagination import SyncPage, AsyncPage +from ..types.model import Model +from .._base_client import AsyncPaginator, make_request_options +from ..types.model_deleted import ModelDeleted + +__all__ = ["Models", "AsyncModels"] + + +class Models(SyncAPIResource): + """List and describe the various models available in the API.""" + + @cached_property + def with_raw_response(self) -> ModelsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ModelsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ModelsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ModelsWithStreamingResponse(self) + + def retrieve( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Model: + """ + Retrieves a model instance, providing basic information about the model such as + the owner and permissioning. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") + return self._get( + path_template("/models/{model}", model=model), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Model, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncPage[Model]: + """ + Lists the currently available models, and provides basic information about each + one such as the owner and availability. + """ + return self._get_api_list( + "/models", + page=SyncPage[Model], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + model=Model, + ) + + def delete( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ModelDeleted: + """Delete a fine-tuned model. + + You must have the Owner role in your organization to + delete a model. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") + return self._delete( + path_template("/models/{model}", model=model), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ModelDeleted, + ) + + +class AsyncModels(AsyncAPIResource): + """List and describe the various models available in the API.""" + + @cached_property + def with_raw_response(self) -> AsyncModelsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncModelsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncModelsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncModelsWithStreamingResponse(self) + + async def retrieve( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Model: + """ + Retrieves a model instance, providing basic information about the model such as + the owner and permissioning. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") + return await self._get( + path_template("/models/{model}", model=model), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Model, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Model, AsyncPage[Model]]: + """ + Lists the currently available models, and provides basic information about each + one such as the owner and availability. + """ + return self._get_api_list( + "/models", + page=AsyncPage[Model], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + model=Model, + ) + + async def delete( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ModelDeleted: + """Delete a fine-tuned model. + + You must have the Owner role in your organization to + delete a model. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") + return await self._delete( + path_template("/models/{model}", model=model), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ModelDeleted, + ) + + +class ModelsWithRawResponse: + def __init__(self, models: Models) -> None: + self._models = models + + self.retrieve = _legacy_response.to_raw_response_wrapper( + models.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + models.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + models.delete, + ) + + +class AsyncModelsWithRawResponse: + def __init__(self, models: AsyncModels) -> None: + self._models = models + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + models.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + models.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + models.delete, + ) + + +class ModelsWithStreamingResponse: + def __init__(self, models: Models) -> None: + self._models = models + + self.retrieve = to_streamed_response_wrapper( + models.retrieve, + ) + self.list = to_streamed_response_wrapper( + models.list, + ) + self.delete = to_streamed_response_wrapper( + models.delete, + ) + + +class AsyncModelsWithStreamingResponse: + def __init__(self, models: AsyncModels) -> None: + self._models = models + + self.retrieve = async_to_streamed_response_wrapper( + models.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + models.list, + ) + self.delete = async_to_streamed_response_wrapper( + models.delete, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/moderations.py b/venv/lib/python3.12/site-packages/openai/resources/moderations.py new file mode 100644 index 0000000..763ac69 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/moderations.py @@ -0,0 +1,215 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable + +import httpx2 + +from .. import _legacy_response +from ..types import moderation_create_params +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .._base_client import make_request_options +from ..types.moderation_model import ModerationModel +from ..types.moderation_create_response import ModerationCreateResponse +from ..types.moderation_multi_modal_input_param import ModerationMultiModalInputParam + +__all__ = ["Moderations", "AsyncModerations"] + + +class Moderations(SyncAPIResource): + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ + + @cached_property + def with_raw_response(self) -> ModerationsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ModerationsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ModerationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ModerationsWithStreamingResponse(self) + + def create( + self, + *, + input: Union[str, SequenceNotStr[str], Iterable[ModerationMultiModalInputParam]], + model: Union[str, ModerationModel] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ModerationCreateResponse: + """Classifies if text and/or image inputs are potentially harmful. + + Learn more in + the + [moderation guide](https://developers.openai.com/api/docs/guides/moderation). + + Args: + input: Input (or inputs) to classify. Can be a single string, an array of strings, or + an array of multi-modal input objects similar to other models. + + model: The content moderation model you would like to use. Learn more in + [the moderation guide](https://developers.openai.com/api/docs/guides/moderation), + and learn about available models + [here](https://developers.openai.com/api/docs/guides/moderation). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/moderations", + body=maybe_transform( + { + "input": input, + "model": model, + }, + moderation_create_params.ModerationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ModerationCreateResponse, + ) + + +class AsyncModerations(AsyncAPIResource): + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ + + @cached_property + def with_raw_response(self) -> AsyncModerationsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncModerationsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncModerationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncModerationsWithStreamingResponse(self) + + async def create( + self, + *, + input: Union[str, SequenceNotStr[str], Iterable[ModerationMultiModalInputParam]], + model: Union[str, ModerationModel] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ModerationCreateResponse: + """Classifies if text and/or image inputs are potentially harmful. + + Learn more in + the + [moderation guide](https://developers.openai.com/api/docs/guides/moderation). + + Args: + input: Input (or inputs) to classify. Can be a single string, an array of strings, or + an array of multi-modal input objects similar to other models. + + model: The content moderation model you would like to use. Learn more in + [the moderation guide](https://developers.openai.com/api/docs/guides/moderation), + and learn about available models + [here](https://developers.openai.com/api/docs/guides/moderation). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/moderations", + body=await async_maybe_transform( + { + "input": input, + "model": model, + }, + moderation_create_params.ModerationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ModerationCreateResponse, + ) + + +class ModerationsWithRawResponse: + def __init__(self, moderations: Moderations) -> None: + self._moderations = moderations + + self.create = _legacy_response.to_raw_response_wrapper( + moderations.create, + ) + + +class AsyncModerationsWithRawResponse: + def __init__(self, moderations: AsyncModerations) -> None: + self._moderations = moderations + + self.create = _legacy_response.async_to_raw_response_wrapper( + moderations.create, + ) + + +class ModerationsWithStreamingResponse: + def __init__(self, moderations: Moderations) -> None: + self._moderations = moderations + + self.create = to_streamed_response_wrapper( + moderations.create, + ) + + +class AsyncModerationsWithStreamingResponse: + def __init__(self, moderations: AsyncModerations) -> None: + self._moderations = moderations + + self.create = async_to_streamed_response_wrapper( + moderations.create, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/realtime/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/realtime/__init__.py new file mode 100644 index 0000000..0b5722e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/realtime/__init__.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .calls import ( + Calls as Calls, + AsyncCalls as AsyncCalls, + CallsWithRawResponse as CallsWithRawResponse, + AsyncCallsWithRawResponse as AsyncCallsWithRawResponse, + CallsWithStreamingResponse as CallsWithStreamingResponse, + AsyncCallsWithStreamingResponse as AsyncCallsWithStreamingResponse, + ) + from .realtime import ( + Realtime as Realtime, + AsyncRealtime as AsyncRealtime, + RealtimeWithRawResponse as RealtimeWithRawResponse, + AsyncRealtimeWithRawResponse as AsyncRealtimeWithRawResponse, + RealtimeWithStreamingResponse as RealtimeWithStreamingResponse, + AsyncRealtimeWithStreamingResponse as AsyncRealtimeWithStreamingResponse, + ) + from .client_secrets import ( + ClientSecrets as ClientSecrets, + AsyncClientSecrets as AsyncClientSecrets, + ClientSecretsWithRawResponse as ClientSecretsWithRawResponse, + AsyncClientSecretsWithRawResponse as AsyncClientSecretsWithRawResponse, + ClientSecretsWithStreamingResponse as ClientSecretsWithStreamingResponse, + AsyncClientSecretsWithStreamingResponse as AsyncClientSecretsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "ClientSecrets": (".client_secrets", "ClientSecrets"), + "AsyncClientSecrets": (".client_secrets", "AsyncClientSecrets"), + "ClientSecretsWithRawResponse": (".client_secrets", "ClientSecretsWithRawResponse"), + "AsyncClientSecretsWithRawResponse": (".client_secrets", "AsyncClientSecretsWithRawResponse"), + "ClientSecretsWithStreamingResponse": (".client_secrets", "ClientSecretsWithStreamingResponse"), + "AsyncClientSecretsWithStreamingResponse": (".client_secrets", "AsyncClientSecretsWithStreamingResponse"), + "Calls": (".calls", "Calls"), + "AsyncCalls": (".calls", "AsyncCalls"), + "CallsWithRawResponse": (".calls", "CallsWithRawResponse"), + "AsyncCallsWithRawResponse": (".calls", "AsyncCallsWithRawResponse"), + "CallsWithStreamingResponse": (".calls", "CallsWithStreamingResponse"), + "AsyncCallsWithStreamingResponse": (".calls", "AsyncCallsWithStreamingResponse"), + "Realtime": (".realtime", "Realtime"), + "AsyncRealtime": (".realtime", "AsyncRealtime"), + "RealtimeWithRawResponse": (".realtime", "RealtimeWithRawResponse"), + "AsyncRealtimeWithRawResponse": (".realtime", "AsyncRealtimeWithRawResponse"), + "RealtimeWithStreamingResponse": (".realtime", "RealtimeWithStreamingResponse"), + "AsyncRealtimeWithStreamingResponse": (".realtime", "AsyncRealtimeWithStreamingResponse"), + } + _SUBMODULES = { + "calls", + "client_secrets", + "realtime", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "ClientSecrets", + "AsyncClientSecrets", + "ClientSecretsWithRawResponse", + "AsyncClientSecretsWithRawResponse", + "ClientSecretsWithStreamingResponse", + "AsyncClientSecretsWithStreamingResponse", + "Calls", + "AsyncCalls", + "CallsWithRawResponse", + "AsyncCallsWithRawResponse", + "CallsWithStreamingResponse", + "AsyncCallsWithStreamingResponse", + "Realtime", + "AsyncRealtime", + "RealtimeWithRawResponse", + "AsyncRealtimeWithRawResponse", + "RealtimeWithStreamingResponse", + "AsyncRealtimeWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/realtime/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/realtime/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..db6e0c6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/realtime/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/realtime/__pycache__/calls.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/realtime/__pycache__/calls.cpython-312.pyc new file mode 100644 index 0000000..e17d88a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/realtime/__pycache__/calls.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/realtime/__pycache__/client_secrets.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/realtime/__pycache__/client_secrets.cpython-312.pyc new file mode 100644 index 0000000..58a42c2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/realtime/__pycache__/client_secrets.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/realtime/__pycache__/realtime.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/realtime/__pycache__/realtime.cpython-312.pyc new file mode 100644 index 0000000..e11c564 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/realtime/__pycache__/realtime.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/realtime/api.md b/venv/lib/python3.12/site-packages/openai/resources/realtime/api.md new file mode 100644 index 0000000..01ddaa4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/realtime/api.md @@ -0,0 +1,134 @@ +# Realtime + +Types: + +```python +from openai.types.realtime import ( + AudioTranscription, + ConversationCreatedEvent, + ConversationItem, + ConversationItemAdded, + ConversationItemCreateEvent, + ConversationItemCreatedEvent, + ConversationItemDeleteEvent, + ConversationItemDeletedEvent, + ConversationItemDone, + ConversationItemInputAudioTranscriptionCompletedEvent, + ConversationItemInputAudioTranscriptionDeltaEvent, + ConversationItemInputAudioTranscriptionFailedEvent, + ConversationItemInputAudioTranscriptionSegment, + ConversationItemRetrieveEvent, + ConversationItemTruncateEvent, + ConversationItemTruncatedEvent, + InputAudioBufferAppendEvent, + InputAudioBufferClearEvent, + InputAudioBufferClearedEvent, + InputAudioBufferCommitEvent, + InputAudioBufferCommittedEvent, + InputAudioBufferDtmfEventReceivedEvent, + InputAudioBufferSpeechStartedEvent, + InputAudioBufferSpeechStoppedEvent, + InputAudioBufferTimeoutTriggered, + LogProbProperties, + McpListToolsCompleted, + McpListToolsFailed, + McpListToolsInProgress, + NoiseReductionType, + OutputAudioBufferClearEvent, + RateLimitsUpdatedEvent, + RealtimeAudioConfig, + RealtimeAudioConfigInput, + RealtimeAudioConfigOutput, + RealtimeAudioFormats, + RealtimeAudioInputTurnDetection, + RealtimeClientEvent, + RealtimeConversationItemAssistantMessage, + RealtimeConversationItemFunctionCall, + RealtimeConversationItemFunctionCallOutput, + RealtimeConversationItemSystemMessage, + RealtimeConversationItemUserMessage, + RealtimeError, + RealtimeErrorEvent, + RealtimeFunctionTool, + RealtimeMcpApprovalRequest, + RealtimeMcpApprovalResponse, + RealtimeMcpListTools, + RealtimeMcpProtocolError, + RealtimeMcpToolCall, + RealtimeMcpToolExecutionError, + RealtimeMcphttpError, + RealtimeReasoning, + RealtimeReasoningEffort, + RealtimeResponse, + RealtimeResponseCreateAudioOutput, + RealtimeResponseCreateMcpTool, + RealtimeResponseCreateParams, + RealtimeResponseStatus, + RealtimeResponseUsage, + RealtimeResponseUsageInputTokenDetails, + RealtimeResponseUsageOutputTokenDetails, + RealtimeServerEvent, + RealtimeSessionCreateRequest, + RealtimeToolChoiceConfig, + RealtimeToolsConfig, + RealtimeToolsConfigUnion, + RealtimeTracingConfig, + RealtimeTranscriptionSessionAudio, + RealtimeTranscriptionSessionAudioInput, + RealtimeTranscriptionSessionAudioInputTurnDetection, + RealtimeTranscriptionSessionCreateRequest, + RealtimeTruncation, + RealtimeTruncationRetentionRatio, + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseCancelEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreateEvent, + ResponseCreatedEvent, + ResponseDoneEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseMcpCallArgumentsDelta, + ResponseMcpCallArgumentsDone, + ResponseMcpCallCompleted, + ResponseMcpCallFailed, + ResponseMcpCallInProgress, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + SessionCreatedEvent, + SessionUpdateEvent, + SessionUpdatedEvent, +) +``` + +## ClientSecrets + +Types: + +```python +from openai.types.realtime import ( + RealtimeSessionCreateResponse, + RealtimeTranscriptionSessionCreateResponse, + RealtimeTranscriptionSessionTurnDetection, + ClientSecretCreateResponse, +) +``` + +Methods: + +- client.realtime.client_secrets.create(\*\*params) -> ClientSecretCreateResponse + +## Calls + +Methods: + +- client.realtime.calls.create(\*\*params) -> HttpxBinaryResponseContent +- client.realtime.calls.accept(call_id, \*\*params) -> None +- client.realtime.calls.hangup(call_id) -> None +- client.realtime.calls.refer(call_id, \*\*params) -> None +- client.realtime.calls.reject(call_id, \*\*params) -> None diff --git a/venv/lib/python3.12/site-packages/openai/resources/realtime/calls.py b/venv/lib/python3.12/site-packages/openai/resources/realtime/calls.py new file mode 100644 index 0000000..b008491 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/realtime/calls.py @@ -0,0 +1,886 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal + +import httpx2 + +from ... import _legacy_response +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_streamed_response_wrapper, + async_to_streamed_response_wrapper, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ..._multipart import encode_multipart +from ..._base_client import make_request_options +from ...types.realtime import ( + call_refer_params, + call_accept_params, + call_create_params, + call_reject_params, +) +from ...types.responses.response_prompt_param import ResponsePromptParam +from ...types.realtime.realtime_reasoning_param import RealtimeReasoningParam +from ...types.realtime.realtime_truncation_param import RealtimeTruncationParam +from ...types.realtime.realtime_audio_config_param import RealtimeAudioConfigParam +from ...types.realtime.realtime_tools_config_param import RealtimeToolsConfigParam +from ...types.realtime.realtime_tracing_config_param import RealtimeTracingConfigParam +from ...types.realtime.realtime_tool_choice_config_param import RealtimeToolChoiceConfigParam +from ...types.realtime.realtime_session_create_request_param import RealtimeSessionCreateRequestParam + +__all__ = ["Calls", "AsyncCalls"] + + +class Calls(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CallsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CallsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CallsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CallsWithStreamingResponse(self) + + def create( + self, + *, + sdp: str, + session: RealtimeSessionCreateRequestParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """Create a new Realtime API call over WebRTC and receive the SDP answer needed + to complete the peer connection. + + Args: + sdp: WebRTC Session Description Protocol (SDP) offer generated by the caller. + + session: Realtime session object configuration. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "application/sdp", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + multipart_body = maybe_transform( + { + "sdp": sdp, + "session": session, + }, + call_create_params.CallCreateParams, + ) + multipart_body, multipart_files, multipart_content, content_type = encode_multipart( + multipart_body, + extra_body, + {"sdp": ("application/sdp", False), "session": ("application/json", True)}, + "sdp", + None, + ) + extra_body = None + extra_headers = { + **{key: value for key, value in (extra_headers or {}).items() if key.lower() != "content-type"}, + "Content-Type": content_type, + } + return self._post( + "/realtime/calls", + body=multipart_body, + files=multipart_files, + content=multipart_content, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + def accept( + self, + call_id: str, + *, + type: Literal["realtime"], + audio: RealtimeAudioConfigParam | Omit = omit, + include: List[Literal["item.input_audio_transcription.logprobs"]] | Omit = omit, + instructions: str | Omit = omit, + max_output_tokens: Union[int, Literal["inf"]] | Omit = omit, + model: Union[ + str, + Literal[ + "gpt-realtime", + "gpt-realtime-1.5", + "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15", + ], + ] + | Omit = omit, + output_modalities: List[Literal["text", "audio"]] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + reasoning: RealtimeReasoningParam | Omit = omit, + tool_choice: RealtimeToolChoiceConfigParam | Omit = omit, + tools: RealtimeToolsConfigParam | Omit = omit, + tracing: Optional[RealtimeTracingConfigParam] | Omit = omit, + truncation: RealtimeTruncationParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Accept an incoming SIP call and configure the realtime session that will handle + it. + + Args: + type: The type of session to create. Always `realtime` for the Realtime API. + + audio: Configuration for input and output audio. + + include: Additional fields to include in server outputs. + + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + + instructions: The default system instructions (i.e. system message) prepended to model calls. + This field allows the client to guide the model on desired responses. The model + can be instructed on response content and format, (e.g. "be extremely succinct", + "act friendly", "here are examples of good responses") and on audio behavior + (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The + instructions are not guaranteed to be followed by the model, but they provide + guidance to the model on the desired behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + + max_output_tokens: Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + + model: The Realtime model used for this session. + + output_modalities: The set of modalities the model can respond with. It defaults to `["audio"]`, + indicating that the model will respond with audio plus a transcript. `["text"]` + can be used to make the model respond with text only. It is not possible to + request both `text` and `audio` at the same time. + + parallel_tool_calls: Whether the model may call multiple tools in parallel. Only supported by + reasoning Realtime models such as `gpt-realtime-2`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + reasoning: Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`. + + tool_choice: How the model chooses tools. Provide one of the string modes or force a specific + function/MCP tool. + + tools: Tools available to the model. + + tracing: Realtime API can write session traces to the + [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to + disable tracing. Once tracing is enabled for a session, the configuration cannot + be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + + truncation: When the number of tokens in a conversation exceeds the model's input token + limit, the conversation be truncated, meaning messages (starting from the + oldest) will not be included in the model's context. A 32k context model with + 4,096 max output tokens can only include 28,224 tokens in the context before + truncation occurs. + + Clients can configure truncation behavior to truncate with a lower max token + limit, which is an effective way to control token usage and cost. + + Truncation will reduce the number of cached tokens on the next turn (busting the + cache), since messages are dropped from the beginning of the context. However, + clients can also configure truncation to retain messages up to a fraction of the + maximum context size, which will reduce the need for future truncations and thus + improve the cache rate. + + Truncation can be disabled entirely, which means the server will never truncate + but would instead return an error if the conversation exceeds the model's input + token limit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + path_template("/realtime/calls/{call_id}/accept", call_id=call_id), + body=maybe_transform( + { + "type": type, + "audio": audio, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "model": model, + "output_modalities": output_modalities, + "parallel_tool_calls": parallel_tool_calls, + "prompt": prompt, + "reasoning": reasoning, + "tool_choice": tool_choice, + "tools": tools, + "tracing": tracing, + "truncation": truncation, + }, + call_accept_params.CallAcceptParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + def hangup( + self, + call_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + End an active Realtime API call, whether it was initiated over SIP or WebRTC. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + path_template("/realtime/calls/{call_id}/hangup", call_id=call_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + def refer( + self, + call_id: str, + *, + target_uri: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Transfer an active SIP call to a new destination using the SIP REFER verb. + + Args: + target_uri: URI that should appear in the SIP Refer-To header. Supports values like + `tel:+14155550123` or `sip:agent@example.com`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + path_template("/realtime/calls/{call_id}/refer", call_id=call_id), + body=maybe_transform({"target_uri": target_uri}, call_refer_params.CallReferParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + def reject( + self, + call_id: str, + *, + status_code: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Decline an incoming SIP call by returning a SIP status code to the caller. + + Args: + status_code: SIP response code to send back to the caller. Defaults to `603` (Decline) when + omitted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + path_template("/realtime/calls/{call_id}/reject", call_id=call_id), + body=maybe_transform({"status_code": status_code}, call_reject_params.CallRejectParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + +class AsyncCalls(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCallsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCallsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCallsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCallsWithStreamingResponse(self) + + async def create( + self, + *, + sdp: str, + session: RealtimeSessionCreateRequestParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """Create a new Realtime API call over WebRTC and receive the SDP answer needed + to complete the peer connection. + + Args: + sdp: WebRTC Session Description Protocol (SDP) offer generated by the caller. + + session: Realtime session object configuration. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "application/sdp", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + multipart_body = await async_maybe_transform( + { + "sdp": sdp, + "session": session, + }, + call_create_params.CallCreateParams, + ) + multipart_body, multipart_files, multipart_content, content_type = encode_multipart( + multipart_body, + extra_body, + {"sdp": ("application/sdp", False), "session": ("application/json", True)}, + "sdp", + None, + ) + extra_body = None + extra_headers = { + **{key: value for key, value in (extra_headers or {}).items() if key.lower() != "content-type"}, + "Content-Type": content_type, + } + return await self._post( + "/realtime/calls", + body=multipart_body, + files=multipart_files, + content=multipart_content, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + async def accept( + self, + call_id: str, + *, + type: Literal["realtime"], + audio: RealtimeAudioConfigParam | Omit = omit, + include: List[Literal["item.input_audio_transcription.logprobs"]] | Omit = omit, + instructions: str | Omit = omit, + max_output_tokens: Union[int, Literal["inf"]] | Omit = omit, + model: Union[ + str, + Literal[ + "gpt-realtime", + "gpt-realtime-1.5", + "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15", + ], + ] + | Omit = omit, + output_modalities: List[Literal["text", "audio"]] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + reasoning: RealtimeReasoningParam | Omit = omit, + tool_choice: RealtimeToolChoiceConfigParam | Omit = omit, + tools: RealtimeToolsConfigParam | Omit = omit, + tracing: Optional[RealtimeTracingConfigParam] | Omit = omit, + truncation: RealtimeTruncationParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Accept an incoming SIP call and configure the realtime session that will handle + it. + + Args: + type: The type of session to create. Always `realtime` for the Realtime API. + + audio: Configuration for input and output audio. + + include: Additional fields to include in server outputs. + + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + + instructions: The default system instructions (i.e. system message) prepended to model calls. + This field allows the client to guide the model on desired responses. The model + can be instructed on response content and format, (e.g. "be extremely succinct", + "act friendly", "here are examples of good responses") and on audio behavior + (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The + instructions are not guaranteed to be followed by the model, but they provide + guidance to the model on the desired behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + + max_output_tokens: Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + + model: The Realtime model used for this session. + + output_modalities: The set of modalities the model can respond with. It defaults to `["audio"]`, + indicating that the model will respond with audio plus a transcript. `["text"]` + can be used to make the model respond with text only. It is not possible to + request both `text` and `audio` at the same time. + + parallel_tool_calls: Whether the model may call multiple tools in parallel. Only supported by + reasoning Realtime models such as `gpt-realtime-2`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + reasoning: Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`. + + tool_choice: How the model chooses tools. Provide one of the string modes or force a specific + function/MCP tool. + + tools: Tools available to the model. + + tracing: Realtime API can write session traces to the + [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to + disable tracing. Once tracing is enabled for a session, the configuration cannot + be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + + truncation: When the number of tokens in a conversation exceeds the model's input token + limit, the conversation be truncated, meaning messages (starting from the + oldest) will not be included in the model's context. A 32k context model with + 4,096 max output tokens can only include 28,224 tokens in the context before + truncation occurs. + + Clients can configure truncation behavior to truncate with a lower max token + limit, which is an effective way to control token usage and cost. + + Truncation will reduce the number of cached tokens on the next turn (busting the + cache), since messages are dropped from the beginning of the context. However, + clients can also configure truncation to retain messages up to a fraction of the + maximum context size, which will reduce the need for future truncations and thus + improve the cache rate. + + Truncation can be disabled entirely, which means the server will never truncate + but would instead return an error if the conversation exceeds the model's input + token limit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + path_template("/realtime/calls/{call_id}/accept", call_id=call_id), + body=await async_maybe_transform( + { + "type": type, + "audio": audio, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "model": model, + "output_modalities": output_modalities, + "parallel_tool_calls": parallel_tool_calls, + "prompt": prompt, + "reasoning": reasoning, + "tool_choice": tool_choice, + "tools": tools, + "tracing": tracing, + "truncation": truncation, + }, + call_accept_params.CallAcceptParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + async def hangup( + self, + call_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + End an active Realtime API call, whether it was initiated over SIP or WebRTC. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + path_template("/realtime/calls/{call_id}/hangup", call_id=call_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + async def refer( + self, + call_id: str, + *, + target_uri: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Transfer an active SIP call to a new destination using the SIP REFER verb. + + Args: + target_uri: URI that should appear in the SIP Refer-To header. Supports values like + `tel:+14155550123` or `sip:agent@example.com`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + path_template("/realtime/calls/{call_id}/refer", call_id=call_id), + body=await async_maybe_transform({"target_uri": target_uri}, call_refer_params.CallReferParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + async def reject( + self, + call_id: str, + *, + status_code: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Decline an incoming SIP call by returning a SIP status code to the caller. + + Args: + status_code: SIP response code to send back to the caller. Defaults to `603` (Decline) when + omitted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + path_template("/realtime/calls/{call_id}/reject", call_id=call_id), + body=await async_maybe_transform({"status_code": status_code}, call_reject_params.CallRejectParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + +class CallsWithRawResponse: + def __init__(self, calls: Calls) -> None: + self._calls = calls + + self.create = _legacy_response.to_raw_response_wrapper( + calls.create, + ) + self.accept = _legacy_response.to_raw_response_wrapper( + calls.accept, + ) + self.hangup = _legacy_response.to_raw_response_wrapper( + calls.hangup, + ) + self.refer = _legacy_response.to_raw_response_wrapper( + calls.refer, + ) + self.reject = _legacy_response.to_raw_response_wrapper( + calls.reject, + ) + + +class AsyncCallsWithRawResponse: + def __init__(self, calls: AsyncCalls) -> None: + self._calls = calls + + self.create = _legacy_response.async_to_raw_response_wrapper( + calls.create, + ) + self.accept = _legacy_response.async_to_raw_response_wrapper( + calls.accept, + ) + self.hangup = _legacy_response.async_to_raw_response_wrapper( + calls.hangup, + ) + self.refer = _legacy_response.async_to_raw_response_wrapper( + calls.refer, + ) + self.reject = _legacy_response.async_to_raw_response_wrapper( + calls.reject, + ) + + +class CallsWithStreamingResponse: + def __init__(self, calls: Calls) -> None: + self._calls = calls + + self.create = to_custom_streamed_response_wrapper( + calls.create, + StreamedBinaryAPIResponse, + ) + self.accept = to_streamed_response_wrapper( + calls.accept, + ) + self.hangup = to_streamed_response_wrapper( + calls.hangup, + ) + self.refer = to_streamed_response_wrapper( + calls.refer, + ) + self.reject = to_streamed_response_wrapper( + calls.reject, + ) + + +class AsyncCallsWithStreamingResponse: + def __init__(self, calls: AsyncCalls) -> None: + self._calls = calls + + self.create = async_to_custom_streamed_response_wrapper( + calls.create, + AsyncStreamedBinaryAPIResponse, + ) + self.accept = async_to_streamed_response_wrapper( + calls.accept, + ) + self.hangup = async_to_streamed_response_wrapper( + calls.hangup, + ) + self.refer = async_to_streamed_response_wrapper( + calls.refer, + ) + self.reject = async_to_streamed_response_wrapper( + calls.reject, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/realtime/client_secrets.py b/venv/lib/python3.12/site-packages/openai/resources/realtime/client_secrets.py new file mode 100644 index 0000000..cca15b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/realtime/client_secrets.py @@ -0,0 +1,225 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from ... import _legacy_response +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.realtime import client_secret_create_params +from ...types.realtime.client_secret_create_response import ClientSecretCreateResponse + +__all__ = ["ClientSecrets", "AsyncClientSecrets"] + + +class ClientSecrets(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ClientSecretsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ClientSecretsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ClientSecretsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ClientSecretsWithStreamingResponse(self) + + def create( + self, + *, + expires_after: client_secret_create_params.ExpiresAfter | Omit = omit, + session: client_secret_create_params.Session | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ClientSecretCreateResponse: + """ + Create a Realtime client secret with an associated session configuration. + + Client secrets are short-lived tokens that can be passed to a client app, such + as a web frontend or mobile client, which grants access to the Realtime API + without leaking your main API key. You can configure a custom TTL for each + client secret. + + You can also attach session configuration options to the client secret, which + will be applied to any sessions created using that client secret, but these can + also be overridden by the client connection. + + [Learn more about authentication with client secrets over WebRTC](https://developers.openai.com/api/docs/guides/realtime-webrtc). + + Returns the created client secret and the effective session object. The client + secret is a string that looks like `ek_1234`. + + Args: + expires_after: Configuration for the client secret expiration. Expiration refers to the time + after which a client secret will no longer be valid for creating sessions. The + session itself may continue after that time once started. A secret can be used + to create multiple sessions until it expires. + + session: Session configuration to use for the client secret. Choose either a realtime + session or a transcription session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/realtime/client_secrets", + body=maybe_transform( + { + "expires_after": expires_after, + "session": session, + }, + client_secret_create_params.ClientSecretCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ClientSecretCreateResponse, + ) + + +class AsyncClientSecrets(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncClientSecretsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncClientSecretsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncClientSecretsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncClientSecretsWithStreamingResponse(self) + + async def create( + self, + *, + expires_after: client_secret_create_params.ExpiresAfter | Omit = omit, + session: client_secret_create_params.Session | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> ClientSecretCreateResponse: + """ + Create a Realtime client secret with an associated session configuration. + + Client secrets are short-lived tokens that can be passed to a client app, such + as a web frontend or mobile client, which grants access to the Realtime API + without leaking your main API key. You can configure a custom TTL for each + client secret. + + You can also attach session configuration options to the client secret, which + will be applied to any sessions created using that client secret, but these can + also be overridden by the client connection. + + [Learn more about authentication with client secrets over WebRTC](https://developers.openai.com/api/docs/guides/realtime-webrtc). + + Returns the created client secret and the effective session object. The client + secret is a string that looks like `ek_1234`. + + Args: + expires_after: Configuration for the client secret expiration. Expiration refers to the time + after which a client secret will no longer be valid for creating sessions. The + session itself may continue after that time once started. A secret can be used + to create multiple sessions until it expires. + + session: Session configuration to use for the client secret. Choose either a realtime + session or a transcription session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/realtime/client_secrets", + body=await async_maybe_transform( + { + "expires_after": expires_after, + "session": session, + }, + client_secret_create_params.ClientSecretCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=ClientSecretCreateResponse, + ) + + +class ClientSecretsWithRawResponse: + def __init__(self, client_secrets: ClientSecrets) -> None: + self._client_secrets = client_secrets + + self.create = _legacy_response.to_raw_response_wrapper( + client_secrets.create, + ) + + +class AsyncClientSecretsWithRawResponse: + def __init__(self, client_secrets: AsyncClientSecrets) -> None: + self._client_secrets = client_secrets + + self.create = _legacy_response.async_to_raw_response_wrapper( + client_secrets.create, + ) + + +class ClientSecretsWithStreamingResponse: + def __init__(self, client_secrets: ClientSecrets) -> None: + self._client_secrets = client_secrets + + self.create = to_streamed_response_wrapper( + client_secrets.create, + ) + + +class AsyncClientSecretsWithStreamingResponse: + def __init__(self, client_secrets: AsyncClientSecrets) -> None: + self._client_secrets = client_secrets + + self.create = async_to_streamed_response_wrapper( + client_secrets.create, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/realtime/realtime.py b/venv/lib/python3.12/site-packages/openai/resources/realtime/realtime.py new file mode 100644 index 0000000..f541bdd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/realtime/realtime.py @@ -0,0 +1,1757 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import json +import time +import random +import logging +from types import TracebackType +from typing import TYPE_CHECKING, Any, Union, Callable, Iterator, Awaitable, cast +from typing_extensions import AsyncIterator + +import httpx2 +from pydantic import BaseModel + +from .calls import ( + Calls, + AsyncCalls, + CallsWithRawResponse, + AsyncCallsWithRawResponse, + CallsWithStreamingResponse, + AsyncCallsWithStreamingResponse, +) +from ..._types import Omit, Query, Headers, omit +from ..._utils import ( + is_given, + is_azure_client, + maybe_transform, + strip_not_given, + async_maybe_transform, + is_async_azure_client, +) +from ..._compat import cached_property +from ..._httpx2 import normalize_httpx_url +from ..._models import FinalRequestOptions, construct_type_unchecked +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._exceptions import OpenAIError, WebSocketConnectionClosedError +from ..._send_queue import SendQueue +from ..._base_client import _merge_mappings +from .client_secrets import ( + ClientSecrets, + AsyncClientSecrets, + ClientSecretsWithRawResponse, + AsyncClientSecretsWithRawResponse, + ClientSecretsWithStreamingResponse, + AsyncClientSecretsWithStreamingResponse, +) +from ..._event_handler import EventHandlerRegistry +from ...types.realtime import session_update_event_param +from ...types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close +from ...types.websocket_connection_options import WebSocketConnectionOptions +from ...types.realtime.realtime_error_event import RealtimeErrorEvent +from ...types.realtime.realtime_client_event import RealtimeClientEvent +from ...types.realtime.realtime_server_event import RealtimeServerEvent +from ...types.realtime.conversation_item_param import ConversationItemParam +from ...types.realtime.realtime_client_event_param import RealtimeClientEventParam +from ...types.realtime.realtime_response_create_params_param import RealtimeResponseCreateParamsParam + +if TYPE_CHECKING: + from websockets.sync.client import ClientConnection as WebSocketConnection + from websockets.asyncio.client import ClientConnection as AsyncWebSocketConnection + + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Realtime", "AsyncRealtime"] + +log: logging.Logger = logging.getLogger(__name__) + + +class Realtime(SyncAPIResource): + @cached_property + def client_secrets(self) -> ClientSecrets: + return ClientSecrets(self._client) + + @cached_property + def calls(self) -> Calls: + from ...lib._realtime import _Calls + + return _Calls(self._client) + + @cached_property + def with_raw_response(self) -> RealtimeWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RealtimeWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RealtimeWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RealtimeWithStreamingResponse(self) + + def connect( + self, + *, + call_id: str | Omit = omit, + model: str | Omit = omit, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> RealtimeConnectionManager: + """ + The Realtime API enables you to build low-latency, multi-modal conversational experiences. It currently supports text and audio as both input and output, as well as function calling. + + Some notable benefits of the API include: + + - Native speech-to-speech: Skipping an intermediate text format means low latency and nuanced output. + - Natural, steerable voices: The models have natural inflection and can laugh, whisper, and adhere to tone direction. + - Simultaneous multimodal output: Text is useful for moderation; faster-than-realtime audio ensures stable playback. + + The Realtime API is a stateful, event-based API that communicates over a WebSocket. + """ + return RealtimeConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + call_id=call_id, + model=model, + ) + + +class AsyncRealtime(AsyncAPIResource): + @cached_property + def client_secrets(self) -> AsyncClientSecrets: + return AsyncClientSecrets(self._client) + + @cached_property + def calls(self) -> AsyncCalls: + from ...lib._realtime import _AsyncCalls + + return _AsyncCalls(self._client) + + @cached_property + def with_raw_response(self) -> AsyncRealtimeWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRealtimeWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRealtimeWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRealtimeWithStreamingResponse(self) + + def connect( + self, + *, + call_id: str | Omit = omit, + model: str | Omit = omit, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> AsyncRealtimeConnectionManager: + """ + The Realtime API enables you to build low-latency, multi-modal conversational experiences. It currently supports text and audio as both input and output, as well as function calling. + + Some notable benefits of the API include: + + - Native speech-to-speech: Skipping an intermediate text format means low latency and nuanced output. + - Natural, steerable voices: The models have natural inflection and can laugh, whisper, and adhere to tone direction. + - Simultaneous multimodal output: Text is useful for moderation; faster-than-realtime audio ensures stable playback. + + The Realtime API is a stateful, event-based API that communicates over a WebSocket. + """ + return AsyncRealtimeConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + call_id=call_id, + model=model, + ) + + +class RealtimeWithRawResponse: + def __init__(self, realtime: Realtime) -> None: + self._realtime = realtime + + @cached_property + def client_secrets(self) -> ClientSecretsWithRawResponse: + return ClientSecretsWithRawResponse(self._realtime.client_secrets) + + @cached_property + def calls(self) -> CallsWithRawResponse: + return CallsWithRawResponse(self._realtime.calls) + + +class AsyncRealtimeWithRawResponse: + def __init__(self, realtime: AsyncRealtime) -> None: + self._realtime = realtime + + @cached_property + def client_secrets(self) -> AsyncClientSecretsWithRawResponse: + return AsyncClientSecretsWithRawResponse(self._realtime.client_secrets) + + @cached_property + def calls(self) -> AsyncCallsWithRawResponse: + return AsyncCallsWithRawResponse(self._realtime.calls) + + +class RealtimeWithStreamingResponse: + def __init__(self, realtime: Realtime) -> None: + self._realtime = realtime + + @cached_property + def client_secrets(self) -> ClientSecretsWithStreamingResponse: + return ClientSecretsWithStreamingResponse(self._realtime.client_secrets) + + @cached_property + def calls(self) -> CallsWithStreamingResponse: + return CallsWithStreamingResponse(self._realtime.calls) + + +class AsyncRealtimeWithStreamingResponse: + def __init__(self, realtime: AsyncRealtime) -> None: + self._realtime = realtime + + @cached_property + def client_secrets(self) -> AsyncClientSecretsWithStreamingResponse: + return AsyncClientSecretsWithStreamingResponse(self._realtime.client_secrets) + + @cached_property + def calls(self) -> AsyncCallsWithStreamingResponse: + return AsyncCallsWithStreamingResponse(self._realtime.calls) + + +class AsyncRealtimeConnection: + """Represents a live WebSocket connection to the Realtime API""" + + session: AsyncRealtimeSessionResource + response: AsyncRealtimeResponseResource + input_audio_buffer: AsyncRealtimeInputAudioBufferResource + conversation: AsyncRealtimeConversationResource + output_audio_buffer: AsyncRealtimeOutputAudioBufferResource + + _connection: AsyncWebSocketConnection + + def __init__( + self, + connection: AsyncWebSocketConnection, + *, + make_ws: Callable[[Query, Headers], Awaitable[AsyncWebSocketConnection]] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._reconnect_attempt = 0 + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue if send_queue is not None else SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=False) + + self.session = AsyncRealtimeSessionResource(self) + self.response = AsyncRealtimeResponseResource(self) + self.input_audio_buffer = AsyncRealtimeInputAudioBufferResource(self) + self.conversation = AsyncRealtimeConversationResource(self) + self.output_audio_buffer = AsyncRealtimeOutputAudioBufferResource(self) + + async def __aiter__(self) -> AsyncIterator[RealtimeServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield await self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not await self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + async def recv(self) -> RealtimeServerEvent: + """ + Receive the next message from the connection and parses it into a `RealtimeServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + event = self.parse_event(await self.recv_bytes()) + event_type = ( + cast("dict[str, object]", event).get("type") + if isinstance(cast(object, event), dict) + else getattr(event, "type", None) + ) + # A successful upgrade can still be followed by an admission error. + # Reset the budget only after receiving a non-error application event. + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return event + + async def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `RealtimeServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = await self._connection.recv(decode=False) + log.debug("Received WebSocket message: %i bytes", len(message)) + if self._reconnect_attempt: + # Account for raw application progress without changing frame delivery. + try: + event_data: object = json.loads(message) + except (ValueError, RecursionError): + return message + event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return message + + async def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(await async_maybe_transform(event, RealtimeClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + await self._connection.send(data) + + async def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + await self._connection.send(data) + + async def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + await self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> RealtimeServerEvent: + """ + Converts a raw `str` or `bytes` message into a `RealtimeServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + RealtimeServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, RealtimeServerEvent)) + ) + + async def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + import asyncio + + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1): + self._reconnect_attempt = attempt + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + await asyncio.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = await self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + await self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + async def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + + async def _send(data: str) -> None: + await self._connection.send(data) + + try: + await self._send_queue.flush_async(_send, requeue_failed=False) + except Exception: + log.warning("Failed to flush send queue after reconnect") + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncRealtimeConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("conversation.created", my_handler) + + Or as a decorator:: + + @connection.on("conversation.created") + async def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncRealtimeConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncRealtimeConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks until the connection is closed. This is the push-based + alternative to iterating with ``async for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + import asyncio + + async for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, RealtimeErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + for handler in generic: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + +class AsyncRealtimeConnectionManager: + """ + Context manager over a `AsyncRealtimeConnection` that is returned by `realtime.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.realtime.connect(...).enter() + # ... + await connection.close() + ``` + """ + + def __init__( + self, + *, + client: AsyncOpenAI, + call_id: str | Omit = omit, + model: str | Omit = omit, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__call_id = call_id + self.__model = model + self.__connection: AsyncRealtimeConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=False) + + def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, RealtimeClientEventParam)) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncRealtimeConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``AsyncRealtimeConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncRealtimeConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncRealtimeConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def __aenter__(self) -> AsyncRealtimeConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.realtime.connect(...).enter() + # ... + await connection.close() + ``` + """ + ws = await self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = AsyncRealtimeConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + await self.__connection._flush_send_queue() + + return self.__connection + + enter = __aenter__ + + async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection: + try: + from ...lib._websocket import _WebSocketConnect as connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + if self.__call_id is not omit: + extra_query = {**extra_query, "call_id": self.__call_id} + if is_async_azure_client(self.__client): + await self.__client._refresh_api_key() + from ...lib._azure_websocket import _AzureWebSocketConnect as connect + + model = self.__model + if not model: + raise OpenAIError("`model` is required for Azure Realtime API") + else: + url, auth_headers = await self.__client._configure_realtime(model, extra_query) + prepared_headers: Headers = extra_headers + else: + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **({"model": self.__model} if self.__model is not omit else {}), + **extra_query, + }, + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + options = await self.__client._prepare_options( + FinalRequestOptions.construct( + method="get", + url=str(url), + headers=dict(extra_headers), + security={"bearer_auth": True}, + ) + ) + url = self.__client._prepare_url(options.url).copy_merge_params( + self.__client.qs.stringify(cast(Any, options.params)) + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + auth_headers = self.__client.auth_headers + prepared_headers = options.headers if is_given(options.headers) else {} + headers = { + key.lower(): (key, value) + for header_set in ( + auth_headers, + {}, + self.__client.default_headers, + prepared_headers, + ) + for key, value in header_set.items() + } + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + return await connect( + str(url), + user_agent_header=None, + additional_headers=_merge_mappings(dict(headers.values()), {}), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = normalize_httpx_url(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + path, separator, query = base_url.raw_path.partition(b"?") + merge_raw_path = path.rstrip(b"/") + b"/realtime" + separator + query + return base_url.copy_with(raw_path=merge_raw_path) + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + await self.__connection.close() + + +class RealtimeConnection: + """Represents a live WebSocket connection to the Realtime API""" + + session: RealtimeSessionResource + response: RealtimeResponseResource + input_audio_buffer: RealtimeInputAudioBufferResource + conversation: RealtimeConversationResource + output_audio_buffer: RealtimeOutputAudioBufferResource + + _connection: WebSocketConnection + + def __init__( + self, + connection: WebSocketConnection, + *, + make_ws: Callable[[Query, Headers], WebSocketConnection] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._reconnect_attempt = 0 + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue if send_queue is not None else SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=True) + + self.session = RealtimeSessionResource(self) + self.response = RealtimeResponseResource(self) + self.input_audio_buffer = RealtimeInputAudioBufferResource(self) + self.conversation = RealtimeConversationResource(self) + self.output_audio_buffer = RealtimeOutputAudioBufferResource(self) + + def __iter__(self) -> Iterator[RealtimeServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + def recv(self) -> RealtimeServerEvent: + """ + Receive the next message from the connection and parses it into a `RealtimeServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + event = self.parse_event(self.recv_bytes()) + event_type = ( + cast("dict[str, object]", event).get("type") + if isinstance(cast(object, event), dict) + else getattr(event, "type", None) + ) + # A successful upgrade can still be followed by an admission error. + # Reset the budget only after receiving a non-error application event. + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return event + + def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `RealtimeServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = self._connection.recv(decode=False) + log.debug("Received WebSocket message: %i bytes", len(message)) + if self._reconnect_attempt: + # Account for raw application progress without changing frame delivery. + try: + event_data: object = json.loads(message) + except (ValueError, RecursionError): + return message + event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return message + + def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, RealtimeClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + self._connection.send(data) + + def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + self._connection.send(data) + + def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> RealtimeServerEvent: + """ + Converts a raw `str` or `bytes` message into a `RealtimeServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + RealtimeServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, RealtimeServerEvent)) + ) + + def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1): + self._reconnect_attempt = attempt + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + time.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + try: + self._send_queue.flush_sync(lambda data: self._connection.send(data), requeue_failed=False) + except Exception: + log.warning("Failed to flush send queue after reconnect") + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[RealtimeConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("conversation.created", my_handler) + + Or as a decorator:: + + @connection.on("conversation.created") + def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> RealtimeConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[RealtimeConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks the current thread until the connection is closed. This is the push-based + alternative to iterating with ``for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, RealtimeErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + handler(event) + + for handler in generic: + handler(event) + + +class RealtimeConnectionManager: + """ + Context manager over a `RealtimeConnection` that is returned by `realtime.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.realtime.connect(...).enter() + # ... + connection.close() + ``` + """ + + def __init__( + self, + *, + client: OpenAI, + call_id: str | Omit = omit, + model: str | Omit = omit, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__call_id = call_id + self.__model = model + self.__connection: RealtimeConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=True) + + def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, RealtimeClientEventParam)) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[RealtimeConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``RealtimeConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> RealtimeConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[RealtimeConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def __enter__(self) -> RealtimeConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.realtime.connect(...).enter() + # ... + connection.close() + ``` + """ + ws = self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = RealtimeConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + self.__connection._flush_send_queue() + + return self.__connection + + enter = __enter__ + + def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketConnection: + try: + from websockets.sync.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + if self.__call_id is not omit: + extra_query = {**extra_query, "call_id": self.__call_id} + if is_azure_client(self.__client): + self.__client._refresh_api_key() + model = self.__model + if not model: + raise OpenAIError("`model` is required for Azure Realtime API") + else: + url, auth_headers = self.__client._configure_realtime(model, extra_query) + prepared_headers: Headers = extra_headers + else: + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **({"model": self.__model} if self.__model is not omit else {}), + **extra_query, + }, + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + options = self.__client._prepare_options( + FinalRequestOptions.construct( + method="get", + url=str(url), + headers=dict(extra_headers), + security={"bearer_auth": True}, + ) + ) + url = self.__client._prepare_url(options.url).copy_merge_params( + self.__client.qs.stringify(cast(Any, options.params)) + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + auth_headers = self.__client.auth_headers + prepared_headers = options.headers if is_given(options.headers) else {} + headers = { + key.lower(): (key, value) + for header_set in ( + auth_headers, + {}, + self.__client.default_headers, + prepared_headers, + ) + for key, value in header_set.items() + } + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + return connect( + str(url), + user_agent_header=None, + additional_headers=_merge_mappings(dict(headers.values()), {}), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = normalize_httpx_url(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + path, separator, query = base_url.raw_path.partition(b"?") + merge_raw_path = path.rstrip(b"/") + b"/realtime" + separator + query + return base_url.copy_with(raw_path=merge_raw_path) + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + self.__connection.close() + + +class BaseRealtimeConnectionResource: + def __init__(self, connection: RealtimeConnection) -> None: + self._connection = connection + + +class RealtimeSessionResource(BaseRealtimeConnectionResource): + def update(self, *, session: session_update_event_param.Session, event_id: str | Omit = omit) -> None: + """ + Send this event to update the session’s configuration. + The client may send this event at any time to update any field + except for `voice` and `model`. `voice` can be updated only if there have been no other audio outputs yet. + + When the server receives a `session.update`, it will respond + with a `session.updated` event showing the full, effective configuration. + Only the fields that are present in the `session.update` are updated. To clear a field like + `instructions`, pass an empty string. To clear a field like `tools`, pass an empty array. + To clear a field like `turn_detection`, pass `null`. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "session.update", "session": session, "event_id": event_id}), + ) + ) + + +class RealtimeResponseResource(BaseRealtimeConnectionResource): + def create(self, *, event_id: str | Omit = omit, response: RealtimeResponseCreateParamsParam | Omit = omit) -> None: + """ + This event instructs the server to create a Response, which means triggering + model inference. When in Server VAD mode, the server will create Responses + automatically. + + A Response will include at least one Item, and may have two, in which case + the second will be a function call. These Items will be appended to the + conversation history by default. + + The server will respond with a `response.created` event, events for Items + and content created, and finally a `response.done` event to indicate the + Response is complete. + + The `response.create` event includes inference configuration like + `instructions` and `tools`. If these are set, they will override the Session's + configuration for this Response only. + + Responses can be created out-of-band of the default Conversation, meaning that they can + have arbitrary input, and it's possible to disable writing the output to the Conversation. + Only one Response can write to the default Conversation at a time, but otherwise multiple + Responses can be created in parallel. The `metadata` field is a good way to disambiguate + multiple simultaneous Responses. + + Clients can set `conversation` to `none` to create a Response that does not write to the default + Conversation. Arbitrary input can be provided with the `input` field, which is an array accepting + raw Items and references to existing Items. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.create", "event_id": event_id, "response": response}), + ) + ) + + def cancel(self, *, event_id: str | Omit = omit, response_id: str | Omit = omit) -> None: + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.done` event with a status of `response.status=cancelled`. If + there is no response to cancel, the server will respond with an error. It's safe + to call `response.cancel` even if no response is in progress, an error will be + returned the session will remain unaffected. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.cancel", "event_id": event_id, "response_id": response_id}), + ) + ) + + +class RealtimeInputAudioBufferResource(BaseRealtimeConnectionResource): + def clear(self, *, event_id: str | Omit = omit) -> None: + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) + ) + + def commit(self, *, event_id: str | Omit = omit) -> None: + """ + Send this event to commit the user input audio buffer, which will create a new user message item in the conversation. This event will produce an error if the input audio buffer is empty. When in Server VAD mode, the client does not need to send this event, the server will commit the audio buffer automatically. + + Committing the input audio buffer will trigger input audio transcription (if enabled in session configuration), but it will not create a response from the model. The server will respond with an `input_audio_buffer.committed` event. + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) + ) + + def append(self, *, audio: str, event_id: str | Omit = omit) -> None: + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. A "commit" will create a new + user message item in the conversation history from the buffer content and clear the buffer. + Input audio transcription (if enabled) will be generated when the buffer is committed. + + If VAD is enabled the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. Input audio noise reduction operates on writes to the audio buffer. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike most other client events, the server will + not send a confirmation response to this event. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "input_audio_buffer.append", "audio": audio, "event_id": event_id}), + ) + ) + + +class RealtimeConversationResource(BaseRealtimeConnectionResource): + @cached_property + def item(self) -> RealtimeConversationItemResource: + return RealtimeConversationItemResource(self._connection) + + +class RealtimeConversationItemResource(BaseRealtimeConnectionResource): + def delete(self, *, item_id: str, event_id: str | Omit = omit) -> None: + """Send this event when you want to remove any item from the conversation + history. + + The server will respond with a `conversation.item.deleted` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.delete", "item_id": item_id, "event_id": event_id}), + ) + ) + + def create( + self, *, item: ConversationItemParam, event_id: str | Omit = omit, previous_item_id: str | Omit = omit + ) -> None: + """ + Add a new Item to the Conversation's context, including messages, function + calls, and function call responses. This event can be used both to populate a + "history" of the conversation and to add new items mid-stream, but has the + current limitation that it cannot populate assistant audio messages. + + If successful, the server will emit a `conversation.item.added` event and, + when the item is finalized, a `conversation.item.done` event. Otherwise, an + `error` event will be sent. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.create", + "item": item, + "event_id": event_id, + "previous_item_id": previous_item_id, + } + ), + ) + ) + + def truncate(self, *, audio_end_ms: int, content_index: int, item_id: str, event_id: str | Omit = omit) -> None: + """Send this event to truncate a previous assistant message’s audio. + + The server + will produce audio faster than realtime, so this event is useful when the user + interrupts to truncate audio that has already been sent to the client but not + yet played. This will synchronize the server's understanding of the audio with + the client's playback. + + Truncating audio will delete the server-side text transcript to ensure there + is not text in the context that hasn't been heard by the user. + + If successful, the server will respond with a `conversation.item.truncated` + event. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.truncate", + "audio_end_ms": audio_end_ms, + "content_index": content_index, + "item_id": item_id, + "event_id": event_id, + } + ), + ) + ) + + def retrieve(self, *, item_id: str, event_id: str | Omit = omit) -> None: + """ + Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. + The server will respond with a `conversation.item.retrieved` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.retrieve", "item_id": item_id, "event_id": event_id}), + ) + ) + + +class RealtimeOutputAudioBufferResource(BaseRealtimeConnectionResource): + def clear(self, *, event_id: str | Omit = omit) -> None: + """**WebRTC/SIP Only:** Emit to cut off the current audio response. + + This will trigger the server to + stop generating audio and emit a `output_audio_buffer.cleared` event. This + event should be preceded by a `response.cancel` client event to stop the + generation of the current response. + [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "output_audio_buffer.clear", "event_id": event_id})) + ) + + +class BaseAsyncRealtimeConnectionResource: + def __init__(self, connection: AsyncRealtimeConnection) -> None: + self._connection = connection + + +class AsyncRealtimeSessionResource(BaseAsyncRealtimeConnectionResource): + async def update(self, *, session: session_update_event_param.Session, event_id: str | Omit = omit) -> None: + """ + Send this event to update the session’s configuration. + The client may send this event at any time to update any field + except for `voice` and `model`. `voice` can be updated only if there have been no other audio outputs yet. + + When the server receives a `session.update`, it will respond + with a `session.updated` event showing the full, effective configuration. + Only the fields that are present in the `session.update` are updated. To clear a field like + `instructions`, pass an empty string. To clear a field like `tools`, pass an empty array. + To clear a field like `turn_detection`, pass `null`. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "session.update", "session": session, "event_id": event_id}), + ) + ) + + +class AsyncRealtimeResponseResource(BaseAsyncRealtimeConnectionResource): + async def create( + self, *, event_id: str | Omit = omit, response: RealtimeResponseCreateParamsParam | Omit = omit + ) -> None: + """ + This event instructs the server to create a Response, which means triggering + model inference. When in Server VAD mode, the server will create Responses + automatically. + + A Response will include at least one Item, and may have two, in which case + the second will be a function call. These Items will be appended to the + conversation history by default. + + The server will respond with a `response.created` event, events for Items + and content created, and finally a `response.done` event to indicate the + Response is complete. + + The `response.create` event includes inference configuration like + `instructions` and `tools`. If these are set, they will override the Session's + configuration for this Response only. + + Responses can be created out-of-band of the default Conversation, meaning that they can + have arbitrary input, and it's possible to disable writing the output to the Conversation. + Only one Response can write to the default Conversation at a time, but otherwise multiple + Responses can be created in parallel. The `metadata` field is a good way to disambiguate + multiple simultaneous Responses. + + Clients can set `conversation` to `none` to create a Response that does not write to the default + Conversation. Arbitrary input can be provided with the `input` field, which is an array accepting + raw Items and references to existing Items. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.create", "event_id": event_id, "response": response}), + ) + ) + + async def cancel(self, *, event_id: str | Omit = omit, response_id: str | Omit = omit) -> None: + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.done` event with a status of `response.status=cancelled`. If + there is no response to cancel, the server will respond with an error. It's safe + to call `response.cancel` even if no response is in progress, an error will be + returned the session will remain unaffected. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.cancel", "event_id": event_id, "response_id": response_id}), + ) + ) + + +class AsyncRealtimeInputAudioBufferResource(BaseAsyncRealtimeConnectionResource): + async def clear(self, *, event_id: str | Omit = omit) -> None: + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) + ) + + async def commit(self, *, event_id: str | Omit = omit) -> None: + """ + Send this event to commit the user input audio buffer, which will create a new user message item in the conversation. This event will produce an error if the input audio buffer is empty. When in Server VAD mode, the client does not need to send this event, the server will commit the audio buffer automatically. + + Committing the input audio buffer will trigger input audio transcription (if enabled in session configuration), but it will not create a response from the model. The server will respond with an `input_audio_buffer.committed` event. + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) + ) + + async def append(self, *, audio: str, event_id: str | Omit = omit) -> None: + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. A "commit" will create a new + user message item in the conversation history from the buffer content and clear the buffer. + Input audio transcription (if enabled) will be generated when the buffer is committed. + + If VAD is enabled the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. Input audio noise reduction operates on writes to the audio buffer. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike most other client events, the server will + not send a confirmation response to this event. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "input_audio_buffer.append", "audio": audio, "event_id": event_id}), + ) + ) + + +class AsyncRealtimeConversationResource(BaseAsyncRealtimeConnectionResource): + @cached_property + def item(self) -> AsyncRealtimeConversationItemResource: + return AsyncRealtimeConversationItemResource(self._connection) + + +class AsyncRealtimeConversationItemResource(BaseAsyncRealtimeConnectionResource): + async def delete(self, *, item_id: str, event_id: str | Omit = omit) -> None: + """Send this event when you want to remove any item from the conversation + history. + + The server will respond with a `conversation.item.deleted` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.delete", "item_id": item_id, "event_id": event_id}), + ) + ) + + async def create( + self, *, item: ConversationItemParam, event_id: str | Omit = omit, previous_item_id: str | Omit = omit + ) -> None: + """ + Add a new Item to the Conversation's context, including messages, function + calls, and function call responses. This event can be used both to populate a + "history" of the conversation and to add new items mid-stream, but has the + current limitation that it cannot populate assistant audio messages. + + If successful, the server will emit a `conversation.item.added` event and, + when the item is finalized, a `conversation.item.done` event. Otherwise, an + `error` event will be sent. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.create", + "item": item, + "event_id": event_id, + "previous_item_id": previous_item_id, + } + ), + ) + ) + + async def truncate( + self, *, audio_end_ms: int, content_index: int, item_id: str, event_id: str | Omit = omit + ) -> None: + """Send this event to truncate a previous assistant message’s audio. + + The server + will produce audio faster than realtime, so this event is useful when the user + interrupts to truncate audio that has already been sent to the client but not + yet played. This will synchronize the server's understanding of the audio with + the client's playback. + + Truncating audio will delete the server-side text transcript to ensure there + is not text in the context that hasn't been heard by the user. + + If successful, the server will respond with a `conversation.item.truncated` + event. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.truncate", + "audio_end_ms": audio_end_ms, + "content_index": content_index, + "item_id": item_id, + "event_id": event_id, + } + ), + ) + ) + + async def retrieve(self, *, item_id: str, event_id: str | Omit = omit) -> None: + """ + Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. + The server will respond with a `conversation.item.retrieved` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.retrieve", "item_id": item_id, "event_id": event_id}), + ) + ) + + +class AsyncRealtimeOutputAudioBufferResource(BaseAsyncRealtimeConnectionResource): + async def clear(self, *, event_id: str | Omit = omit) -> None: + """**WebRTC/SIP Only:** Emit to cut off the current audio response. + + This will trigger the server to + stop generating audio and emit a `output_audio_buffer.cleared` event. This + event should be preceded by a `response.cancel` client event to stop the + generation of the current response. + [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "output_audio_buffer.clear", "event_id": event_id})) + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/responses/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/responses/__init__.py new file mode 100644 index 0000000..d8eb8df --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/responses/__init__.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .responses import ( + Responses as Responses, + AsyncResponses as AsyncResponses, + ResponsesWithRawResponse as ResponsesWithRawResponse, + AsyncResponsesWithRawResponse as AsyncResponsesWithRawResponse, + ResponsesWithStreamingResponse as ResponsesWithStreamingResponse, + AsyncResponsesWithStreamingResponse as AsyncResponsesWithStreamingResponse, + ) + from .input_items import ( + InputItems as InputItems, + AsyncInputItems as AsyncInputItems, + InputItemsWithRawResponse as InputItemsWithRawResponse, + AsyncInputItemsWithRawResponse as AsyncInputItemsWithRawResponse, + InputItemsWithStreamingResponse as InputItemsWithStreamingResponse, + AsyncInputItemsWithStreamingResponse as AsyncInputItemsWithStreamingResponse, + ) + from .input_tokens import ( + InputTokens as InputTokens, + AsyncInputTokens as AsyncInputTokens, + InputTokensWithRawResponse as InputTokensWithRawResponse, + AsyncInputTokensWithRawResponse as AsyncInputTokensWithRawResponse, + InputTokensWithStreamingResponse as InputTokensWithStreamingResponse, + AsyncInputTokensWithStreamingResponse as AsyncInputTokensWithStreamingResponse, + ) + +else: + _EXPORTS = { + "InputItems": (".input_items", "InputItems"), + "AsyncInputItems": (".input_items", "AsyncInputItems"), + "InputItemsWithRawResponse": (".input_items", "InputItemsWithRawResponse"), + "AsyncInputItemsWithRawResponse": (".input_items", "AsyncInputItemsWithRawResponse"), + "InputItemsWithStreamingResponse": (".input_items", "InputItemsWithStreamingResponse"), + "AsyncInputItemsWithStreamingResponse": (".input_items", "AsyncInputItemsWithStreamingResponse"), + "InputTokens": (".input_tokens", "InputTokens"), + "AsyncInputTokens": (".input_tokens", "AsyncInputTokens"), + "InputTokensWithRawResponse": (".input_tokens", "InputTokensWithRawResponse"), + "AsyncInputTokensWithRawResponse": (".input_tokens", "AsyncInputTokensWithRawResponse"), + "InputTokensWithStreamingResponse": (".input_tokens", "InputTokensWithStreamingResponse"), + "AsyncInputTokensWithStreamingResponse": (".input_tokens", "AsyncInputTokensWithStreamingResponse"), + "Responses": (".responses", "Responses"), + "AsyncResponses": (".responses", "AsyncResponses"), + "ResponsesWithRawResponse": (".responses", "ResponsesWithRawResponse"), + "AsyncResponsesWithRawResponse": (".responses", "AsyncResponsesWithRawResponse"), + "ResponsesWithStreamingResponse": (".responses", "ResponsesWithStreamingResponse"), + "AsyncResponsesWithStreamingResponse": (".responses", "AsyncResponsesWithStreamingResponse"), + } + _SUBMODULES = { + "input_items", + "input_tokens", + "responses", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "InputItems", + "AsyncInputItems", + "InputItemsWithRawResponse", + "AsyncInputItemsWithRawResponse", + "InputItemsWithStreamingResponse", + "AsyncInputItemsWithStreamingResponse", + "InputTokens", + "AsyncInputTokens", + "InputTokensWithRawResponse", + "AsyncInputTokensWithRawResponse", + "InputTokensWithStreamingResponse", + "AsyncInputTokensWithStreamingResponse", + "Responses", + "AsyncResponses", + "ResponsesWithRawResponse", + "AsyncResponsesWithRawResponse", + "ResponsesWithStreamingResponse", + "AsyncResponsesWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/responses/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/responses/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b5736cc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/responses/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/responses/__pycache__/input_items.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/responses/__pycache__/input_items.cpython-312.pyc new file mode 100644 index 0000000..8e32c38 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/responses/__pycache__/input_items.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/responses/__pycache__/input_tokens.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/responses/__pycache__/input_tokens.cpython-312.pyc new file mode 100644 index 0000000..0d66ca5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/responses/__pycache__/input_tokens.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/responses/__pycache__/responses.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/responses/__pycache__/responses.cpython-312.pyc new file mode 100644 index 0000000..10725fb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/responses/__pycache__/responses.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/responses/api.md b/venv/lib/python3.12/site-packages/openai/resources/responses/api.md new file mode 100644 index 0000000..5a771fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/responses/api.md @@ -0,0 +1,207 @@ +# Responses + +Types: + +```python +from openai.types.responses import ( + ApplyPatchTool, + CompactedResponse, + ComputerAction, + ComputerActionList, + ComputerTool, + ComputerUsePreviewTool, + ContainerAuto, + ContainerNetworkPolicyAllowlist, + ContainerNetworkPolicyDisabled, + ContainerNetworkPolicyDomainSecret, + ContainerReference, + CustomTool, + EasyInputMessage, + FileSearchTool, + FunctionShellTool, + FunctionTool, + ImageDetail, + InlineSkill, + InlineSkillSource, + LocalEnvironment, + LocalSkill, + McpToolCallError, + NamespaceTool, + Response, + ResponseApplyPatchToolCall, + ResponseApplyPatchToolCallOutput, + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseCodeInterpreterCallCodeDeltaEvent, + ResponseCodeInterpreterCallCodeDoneEvent, + ResponseCodeInterpreterCallCompletedEvent, + ResponseCodeInterpreterCallInProgressEvent, + ResponseCodeInterpreterCallInterpretingEvent, + ResponseCodeInterpreterToolCall, + ResponseCompactionCompactingEvent, + ResponseCompactionItem, + ResponseCompactionItemParam, + ResponseCompletedEvent, + ResponseComputerToolCall, + ResponseComputerToolCallOutputItem, + ResponseComputerToolCallOutputScreenshot, + ResponseConfigurationUpdateItem, + ResponseConfigurationUpdateItemParam, + ResponseContainerReference, + ResponseContent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseConversationParam, + ResponseCreatedEvent, + ResponseCustomToolCall, + ResponseCustomToolCallInputDeltaEvent, + ResponseCustomToolCallInputDoneEvent, + ResponseCustomToolCallItem, + ResponseCustomToolCallOutput, + ResponseCustomToolCallOutputItem, + ResponseError, + ResponseErrorEvent, + ResponseFailedEvent, + ResponseFileSearchCallCompletedEvent, + ResponseFileSearchCallInProgressEvent, + ResponseFileSearchCallSearchingEvent, + ResponseFileSearchToolCall, + ResponseFormatTextConfig, + ResponseFormatTextJSONSchemaConfig, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseFunctionCallOutputItem, + ResponseFunctionCallOutputItemList, + ResponseFunctionShellCallOutputContent, + ResponseFunctionShellToolCall, + ResponseFunctionShellToolCallOutput, + ResponseFunctionToolCall, + ResponseFunctionToolCallItem, + ResponseFunctionToolCallOutputItem, + ResponseFunctionWebSearch, + ResponseImageGenCallCompletedEvent, + ResponseImageGenCallGeneratingEvent, + ResponseImageGenCallInProgressEvent, + ResponseImageGenCallPartialImageEvent, + ResponseInProgressEvent, + ResponseIncludable, + ResponseIncompleteEvent, + ResponseInput, + ResponseInputAudio, + ResponseInputContent, + ResponseInputFile, + ResponseInputFileContent, + ResponseInputImage, + ResponseInputImageContent, + ResponseInputItem, + ResponseInputMessageContentList, + ResponseInputMessageItem, + ResponseInputText, + ResponseInputTextContent, + ResponseItem, + ResponseLocalEnvironment, + ResponseMcpCallArgumentsDeltaEvent, + ResponseMcpCallArgumentsDoneEvent, + ResponseMcpCallCompletedEvent, + ResponseMcpCallFailedEvent, + ResponseMcpCallInProgressEvent, + ResponseMcpListToolsCompletedEvent, + ResponseMcpListToolsFailedEvent, + ResponseMcpListToolsInProgressEvent, + ResponseOutputAudio, + ResponseOutputItem, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseOutputMessage, + ResponseOutputRefusal, + ResponseOutputText, + ResponseOutputTextAnnotationAddedEvent, + ResponsePrompt, + ResponseQueuedEvent, + ResponseReasoningItem, + ResponseReasoningSummaryPartAddedEvent, + ResponseReasoningSummaryPartDoneEvent, + ResponseReasoningSummaryTextDeltaEvent, + ResponseReasoningSummaryTextDoneEvent, + ResponseReasoningTextDeltaEvent, + ResponseReasoningTextDoneEvent, + ResponseRefusalDeltaEvent, + ResponseRefusalDoneEvent, + ResponseShellCallCommandAddedEvent, + ResponseShellCallCommandDeltaEvent, + ResponseShellCallCommandDoneEvent, + ResponseShellCallOutputContentDeltaEvent, + ResponseShellCallOutputContentDoneEvent, + ResponseStatus, + ResponseSteerAcceptedEvent, + ResponseSteerErrorCode, + ResponseSteerEvent, + ResponseSteerFailedEvent, + ResponseSteerInput, + ResponseSteerInputContent, + ResponseSteerPendingEvent, + ResponseSteerPendingReason, + ResponseSteerRequiredInput, + ResponseStreamEvent, + ResponseTextConfig, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + ResponseToolSearchCall, + ResponseToolSearchOutputItem, + ResponseToolSearchOutputItemParam, + ResponseUsage, + ResponseWebSearchCallCompletedEvent, + ResponseWebSearchCallInProgressEvent, + ResponseWebSearchCallSearchingEvent, + ResponsesClientEvent, + ResponsesServerEvent, + ServiceTier, + SkillReference, + Tool, + ToolChoiceAllowed, + ToolChoiceApplyPatch, + ToolChoiceCustom, + ToolChoiceFunction, + ToolChoiceMcp, + ToolChoiceOptions, + ToolChoiceShell, + ToolChoiceTypes, + ToolSearchTool, + WebSearchPreviewTool, + WebSearchTool, +) +``` + +Methods: + +- client.responses.create(\*\*params) -> Response +- client.responses.retrieve(response_id, \*\*params) -> Response +- client.responses.delete(response_id) -> None +- client.responses.cancel(response_id) -> Response +- client.responses.compact(\*\*params) -> CompactedResponse + +## InputItems + +Types: + +```python +from openai.types.responses import ResponseItemList +``` + +Methods: + +- client.responses.input_items.list(response_id, \*\*params) -> SyncCursorPage[ResponseItem] + +## InputTokens + +Types: + +```python +from openai.types.responses import InputTokenCountResponse +``` + +Methods: + +- client.responses.input_tokens.count(\*\*params) -> InputTokenCountResponse diff --git a/venv/lib/python3.12/site-packages/openai/resources/responses/input_items.py b/venv/lib/python3.12/site-packages/openai/resources/responses/input_items.py new file mode 100644 index 0000000..e421a64 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/responses/input_items.py @@ -0,0 +1,232 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, List, cast +from typing_extensions import Literal + +import httpx2 + +from ... import _legacy_response +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncCursorPage, AsyncCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.responses import input_item_list_params +from ...types.responses.response_item import ResponseItem +from ...types.responses.response_includable import ResponseIncludable + +__all__ = ["InputItems", "AsyncInputItems"] + + +class InputItems(SyncAPIResource): + """Create and manage model responses.""" + + @cached_property + def with_raw_response(self) -> InputItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return InputItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InputItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return InputItemsWithStreamingResponse(self) + + def list( + self, + response_id: str, + *, + after: str | Omit = omit, + include: List[ResponseIncludable] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[ResponseItem]: + """ + Returns a list of input items for a given response. + + Args: + after: An item ID to list items after, used in pagination. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return self._get_api_list( + path_template("/responses/{response_id}/input_items", response_id=response_id), + page=SyncCursorPage[ResponseItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include": include, + "limit": limit, + "order": order, + }, + input_item_list_params.InputItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, ResponseItem), # Union types cannot be passed in as arguments in the type system + ) + + +class AsyncInputItems(AsyncAPIResource): + """Create and manage model responses.""" + + @cached_property + def with_raw_response(self) -> AsyncInputItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncInputItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInputItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncInputItemsWithStreamingResponse(self) + + def list( + self, + response_id: str, + *, + after: str | Omit = omit, + include: List[ResponseIncludable] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ResponseItem, AsyncCursorPage[ResponseItem]]: + """ + Returns a list of input items for a given response. + + Args: + after: An item ID to list items after, used in pagination. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return self._get_api_list( + path_template("/responses/{response_id}/input_items", response_id=response_id), + page=AsyncCursorPage[ResponseItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include": include, + "limit": limit, + "order": order, + }, + input_item_list_params.InputItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, ResponseItem), # Union types cannot be passed in as arguments in the type system + ) + + +class InputItemsWithRawResponse: + def __init__(self, input_items: InputItems) -> None: + self._input_items = input_items + + self.list = _legacy_response.to_raw_response_wrapper( + input_items.list, + ) + + +class AsyncInputItemsWithRawResponse: + def __init__(self, input_items: AsyncInputItems) -> None: + self._input_items = input_items + + self.list = _legacy_response.async_to_raw_response_wrapper( + input_items.list, + ) + + +class InputItemsWithStreamingResponse: + def __init__(self, input_items: InputItems) -> None: + self._input_items = input_items + + self.list = to_streamed_response_wrapper( + input_items.list, + ) + + +class AsyncInputItemsWithStreamingResponse: + def __init__(self, input_items: AsyncInputItems) -> None: + self._input_items = input_items + + self.list = async_to_streamed_response_wrapper( + input_items.list, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/responses/input_tokens.py b/venv/lib/python3.12/site-packages/openai/resources/responses/input_tokens.py new file mode 100644 index 0000000..2748ce7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/responses/input_tokens.py @@ -0,0 +1,335 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal + +import httpx2 + +from ... import _legacy_response +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.responses import input_token_count_params +from ...types.responses.tool_param import ToolParam +from ...types.shared_params.reasoning import Reasoning +from ...types.responses.response_input_item_param import ResponseInputItemParam +from ...types.responses.input_token_count_response import InputTokenCountResponse + +__all__ = ["InputTokens", "AsyncInputTokens"] + + +class InputTokens(SyncAPIResource): + """Create and manage model responses.""" + + @cached_property + def with_raw_response(self) -> InputTokensWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return InputTokensWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InputTokensWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return InputTokensWithStreamingResponse(self) + + def count( + self, + *, + conversation: Optional[input_token_count_params.Conversation] | Omit = omit, + input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + model: Optional[str] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + personality: Union[str, Literal["friendly", "pragmatic"]] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + text: Optional[input_token_count_params.Text] | Omit = omit, + tool_choice: Optional[input_token_count_params.ToolChoice] | Omit = omit, + tools: Optional[Iterable[ToolParam]] | Omit = omit, + truncation: Literal["auto", "disabled"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> InputTokenCountResponse: + """ + Returns input token counts of the request. + + Returns an object with `object` set to `response.input_tokens` and an + `input_tokens` count. + + Args: + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + personality: A model-owned style preset to apply to this request. Omit this parameter to use + the model's default style. Supported values may expand over time. Values must be + at most 64 characters. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + reasoning: **gpt-5 and o-series models only** Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: Controls which tool the model should use, if any. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + truncation: The truncation strategy to use for the model response. - `auto`: If the input to + this Response exceeds the model's context window size, the model will truncate + the response to fit the context window by dropping items from the beginning of + the conversation. - `disabled` (default): If the input size will exceed the + context window size for a model, the request will fail with a 400 error. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/responses/input_tokens", + body=maybe_transform( + { + "conversation": conversation, + "input": input, + "instructions": instructions, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "personality": personality, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "truncation": truncation, + }, + input_token_count_params.InputTokenCountParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=InputTokenCountResponse, + ) + + +class AsyncInputTokens(AsyncAPIResource): + """Create and manage model responses.""" + + @cached_property + def with_raw_response(self) -> AsyncInputTokensWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncInputTokensWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInputTokensWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncInputTokensWithStreamingResponse(self) + + async def count( + self, + *, + conversation: Optional[input_token_count_params.Conversation] | Omit = omit, + input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + model: Optional[str] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + personality: Union[str, Literal["friendly", "pragmatic"]] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + text: Optional[input_token_count_params.Text] | Omit = omit, + tool_choice: Optional[input_token_count_params.ToolChoice] | Omit = omit, + tools: Optional[Iterable[ToolParam]] | Omit = omit, + truncation: Literal["auto", "disabled"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> InputTokenCountResponse: + """ + Returns input token counts of the request. + + Returns an object with `object` set to `response.input_tokens` and an + `input_tokens` count. + + Args: + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + personality: A model-owned style preset to apply to this request. Omit this parameter to use + the model's default style. Supported values may expand over time. Values must be + at most 64 characters. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + reasoning: **gpt-5 and o-series models only** Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: Controls which tool the model should use, if any. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + truncation: The truncation strategy to use for the model response. - `auto`: If the input to + this Response exceeds the model's context window size, the model will truncate + the response to fit the context window by dropping items from the beginning of + the conversation. - `disabled` (default): If the input size will exceed the + context window size for a model, the request will fail with a 400 error. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/responses/input_tokens", + body=await async_maybe_transform( + { + "conversation": conversation, + "input": input, + "instructions": instructions, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "personality": personality, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "truncation": truncation, + }, + input_token_count_params.InputTokenCountParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=InputTokenCountResponse, + ) + + +class InputTokensWithRawResponse: + def __init__(self, input_tokens: InputTokens) -> None: + self._input_tokens = input_tokens + + self.count = _legacy_response.to_raw_response_wrapper( + input_tokens.count, + ) + + +class AsyncInputTokensWithRawResponse: + def __init__(self, input_tokens: AsyncInputTokens) -> None: + self._input_tokens = input_tokens + + self.count = _legacy_response.async_to_raw_response_wrapper( + input_tokens.count, + ) + + +class InputTokensWithStreamingResponse: + def __init__(self, input_tokens: InputTokens) -> None: + self._input_tokens = input_tokens + + self.count = to_streamed_response_wrapper( + input_tokens.count, + ) + + +class AsyncInputTokensWithStreamingResponse: + def __init__(self, input_tokens: AsyncInputTokens) -> None: + self._input_tokens = input_tokens + + self.count = async_to_streamed_response_wrapper( + input_tokens.count, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/responses/responses.py b/venv/lib/python3.12/site-packages/openai/resources/responses/responses.py new file mode 100644 index 0000000..7760ae9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/responses/responses.py @@ -0,0 +1,5347 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import json +import random +import logging +import threading +from copy import copy +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + List, + Type, + Union, + Callable, + Iterable, + Iterator, + Optional, + Awaitable, + AsyncIterator, + cast, +) +from functools import partial +from typing_extensions import Literal, overload + +import httpx2 +from pydantic import BaseModel + +from ... import _legacy_response +from ..._types import NOT_GIVEN, Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform +from ..._compat import cached_property +from ..._httpx2 import normalize_httpx_url +from ..._models import FinalRequestOptions, construct_type_unchecked +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .input_items import ( + InputItems, + AsyncInputItems, + InputItemsWithRawResponse, + AsyncInputItemsWithRawResponse, + InputItemsWithStreamingResponse, + AsyncInputItemsWithStreamingResponse, +) +from ..._streaming import Stream, AsyncStream +from ...lib._tools import PydanticFunctionTool, ResponsesPydanticFunctionTool +from .input_tokens import ( + InputTokens, + AsyncInputTokens, + InputTokensWithRawResponse, + AsyncInputTokensWithRawResponse, + InputTokensWithStreamingResponse, + AsyncInputTokensWithStreamingResponse, +) +from ..._exceptions import OpenAIError, WebSocketConnectionClosedError +from ..._send_queue import SendQueue +from ..._base_client import _merge_mappings, make_request_options +from ..._event_handler import EventHandlerRegistry +from ...types.responses import ( + response_create_params, + response_compact_params, + response_retrieve_params, + responses_client_event_param, +) +from ...lib._parsing._responses import ( + TextFormatT, + parse_response, + type_to_text_format_param as _type_to_text_format_param, +) +from ...types.responses.response import Response +from ...types.responses.tool_param import ToolParam, ParseableToolParam +from ...types.responses.service_tier import ServiceTier +from ...types.shared_params.metadata import Metadata +from ...types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close +from ...types.shared_params.reasoning import Reasoning +from ...types.responses.parsed_response import ParsedResponse +from ...lib.streaming.responses._responses import ResponseStreamManager, AsyncResponseStreamManager +from ...types.responses.compacted_response import CompactedResponse +from ...types.websocket_connection_options import WebSocketConnectionOptions +from ...types.responses.response_includable import ResponseIncludable +from ...types.shared_params.responses_model import ResponsesModel +from ...types.responses.response_input_param import ResponseInputParam +from ...types.responses.response_prompt_param import ResponsePromptParam +from ...types.responses.response_stream_event import ResponseStreamEvent +from ...types.responses.responses_client_event import ResponsesClientEvent +from ...types.responses.responses_server_event import ResponseWsError, ResponsesServerEvent +from ...types.responses.response_input_item_param import ResponseInputItemParam +from ...types.responses.response_steer_input_param import ResponseSteerInputParam +from ...types.responses.response_text_config_param import ResponseTextConfigParam +from ...types.responses.responses_client_event_param import ResponsesClientEventParam + +if TYPE_CHECKING: + from websockets.sync.client import ClientConnection as WebSocketConnection + from websockets.asyncio.client import ClientConnection as AsyncWebSocketConnection + + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Responses", "AsyncResponses"] + +log: logging.Logger = logging.getLogger(__name__) + + +class Responses(SyncAPIResource): + """Create and manage model responses.""" + + @cached_property + def input_items(self) -> InputItems: + """Create and manage model responses.""" + return InputItems(self._client) + + @cached_property + def input_tokens(self) -> InputTokens: + """Create and manage model responses.""" + return InputTokens(self._client) + + @cached_property + def with_raw_response(self) -> ResponsesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ResponsesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ResponsesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ResponsesWithStreamingResponse(self) + + @overload + def create( + self, + *, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response: + """Creates a model response. + + Provide + [text](https://developers.openai.com/api/docs/guides/text) or + [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to + generate [text](https://developers.openai.com/api/docs/guides/text) or + [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) + outputs. Have the model call your own + [custom code](https://developers.openai.com/api/docs/guides/function-calling) or + use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search) + to use your own data as input for the model's response. + + Args: + access_programs: Domain-specific access programs to use for this request. + + background: Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + moderation: Configuration for running moderation on the input and output of this response. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + stream: Literal[True], + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[ResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://developers.openai.com/api/docs/guides/text) or + [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to + generate [text](https://developers.openai.com/api/docs/guides/text) or + [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) + outputs. Have the model call your own + [custom code](https://developers.openai.com/api/docs/guides/function-calling) or + use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search) + to use your own data as input for the model's response. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + access_programs: Domain-specific access programs to use for this request. + + background: Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + moderation: Configuration for running moderation on the input and output of this response. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + stream: bool, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response | Stream[ResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://developers.openai.com/api/docs/guides/text) or + [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to + generate [text](https://developers.openai.com/api/docs/guides/text) or + [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) + outputs. Have the model call your own + [custom code](https://developers.openai.com/api/docs/guides/function-calling) or + use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search) + to use your own data as input for the model's response. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + access_programs: Domain-specific access programs to use for this request. + + background: Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + moderation: Configuration for running moderation on the input and output of this response. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + def create( + self, + *, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response | Stream[ResponseStreamEvent]: + return self._post( + "/responses", + body=maybe_transform( + { + "access_programs": access_programs, + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParamsStreaming + if stream + else response_create_params.ResponseCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Response, + stream=stream or False, + stream_cls=Stream[ResponseStreamEvent], + ) + + @overload + def stream( + self, + *, + response_id: str, + text_format: type[TextFormatT] | Omit = omit, + starting_after: int | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ResponseStreamManager[TextFormatT]: ... + + @overload + def stream( + self, + *, + input: Union[str, ResponseInputParam], + model: ResponsesModel, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + text_format: type[TextFormatT] | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ResponseStreamManager[TextFormatT]: ... + + def stream( + self, + *, + response_id: str | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + model: ResponsesModel | Omit = omit, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + text_format: type[TextFormatT] | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + starting_after: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ResponseStreamManager[TextFormatT]: + new_response_args = { + "access_programs": access_programs, + "input": input, + "model": model, + "context_management": context_management, + "conversation": conversation, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "moderation": moderation, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + "background": background, + } + new_response_args_names = [k for k, v in new_response_args.items() if is_given(v)] + + if (is_given(response_id) or is_given(starting_after)) and len(new_response_args_names) > 0: + raise ValueError( + "Cannot provide both response_id/starting_after can't be provided together with " + + ", ".join(new_response_args_names) + ) + tools = _make_tools(tools) + if len(new_response_args_names) > 0: + if not is_given(input): + raise ValueError("input must be provided when creating a new response") + + if not is_given(model): + raise ValueError("model must be provided when creating a new response") + + if is_given(text_format): + if not text: + text = {} + + if "format" in text: + raise TypeError("Cannot mix and match text.format with text_format") + + text = copy(text) + text["format"] = _type_to_text_format_param(text_format) + + api_request: partial[Stream[ResponseStreamEvent]] = partial( + self.create, + access_programs=access_programs, + input=input, + model=model, + tools=tools, + context_management=context_management, + conversation=conversation, + include=include, + instructions=instructions, + max_output_tokens=max_output_tokens, + max_tool_calls=max_tool_calls, + metadata=metadata, + moderation=moderation, + parallel_tool_calls=parallel_tool_calls, + previous_response_id=previous_response_id, + prompt=prompt, + prompt_cache_key=prompt_cache_key, + prompt_cache_options=prompt_cache_options, + prompt_cache_retention=prompt_cache_retention, + store=store, + stream_options=stream_options, + stream=True, + temperature=temperature, + text=text, + tool_choice=tool_choice, + reasoning=reasoning, + safety_identifier=safety_identifier, + service_tier=service_tier, + top_logprobs=top_logprobs, + top_p=top_p, + truncation=truncation, + user=user, + background=background, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + return ResponseStreamManager( + api_request, + text_format=text_format, + input_tools=tools, + starting_after=None, + ) + else: + if not is_given(response_id): + raise ValueError("id must be provided when streaming an existing response") + + return ResponseStreamManager( + lambda: self.retrieve( + response_id=response_id, + stream=True, + include=include or [], + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + starting_after=omit, + timeout=timeout, + ), + text_format=text_format, + input_tools=tools, + starting_after=starting_after if is_given(starting_after) else None, + ) + + def parse( + self, + *, + text_format: type[TextFormatT] | Omit = omit, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedResponse[TextFormatT]: + if is_given(text_format): + if not text: + text = {} + + if "format" in text: + raise TypeError("Cannot mix and match text.format with text_format") + text = copy(text) + text["format"] = _type_to_text_format_param(text_format) + + tools = _make_tools(tools) + + def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: + return parse_response( + input_tools=tools, + text_format=text_format, + response=raw_response, + ) + + return self._post( + "/responses", + body=maybe_transform( + { + "access_programs": access_programs, + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + "verbosity": verbosity, + }, + response_create_params.ResponseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=parser, + security={"bearer_auth": True}, + ), + # we turn the `Response` instance into a `ParsedResponse` + # in the `parser` function above + cast_to=cast("Type[ParsedResponse[TextFormatT]]", Response), + ) + + @overload + def retrieve( + self, + response_id: str, + *, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response: ... + + @overload + def retrieve( + self, + response_id: str, + *, + stream: Literal[True], + include: List[ResponseIncludable] | Omit = omit, + starting_after: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Stream[ResponseStreamEvent]: ... + + @overload + def retrieve( + self, + response_id: str, + *, + stream: bool, + include: List[ResponseIncludable] | Omit = omit, + starting_after: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response | Stream[ResponseStreamEvent]: ... + + @overload + def retrieve( + self, + response_id: str, + *, + stream: bool = False, + include: List[ResponseIncludable] | Omit = omit, + starting_after: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response | Stream[ResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def retrieve( + self, + response_id: str, + *, + stream: Literal[True], + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Stream[ResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def retrieve( + self, + response_id: str, + *, + stream: bool, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response | Stream[ResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + def retrieve( + self, + response_id: str, + *, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response | Stream[ResponseStreamEvent]: + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return self._get( + path_template("/responses/{response_id}", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "include": include, + "include_obfuscation": include_obfuscation, + "starting_after": starting_after, + "stream": stream, + }, + response_retrieve_params.ResponseRetrieveParams, + ), + security={"bearer_auth": True}, + ), + cast_to=Response, + stream=stream or False, + stream_cls=Stream[ResponseStreamEvent], + ) + + def delete( + self, + response_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Deletes a model response with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + path_template("/responses/{response_id}", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + def cancel( + self, + response_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response: + """Cancels a model response with the given ID. + + Only responses created with the + `background` parameter set to `true` can be cancelled. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return self._post( + path_template("/responses/{response_id}/cancel", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Response, + ) + + def compact( + self, + *, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + None, + ], + input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: Optional[response_compact_params.PromptCacheOptions] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "fast", "flex", "priority"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> CompactedResponse: + """Compact a conversation. + + Returns a compacted response object. + + Learn when and how to compact long-running conversations in the + [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window). + For ZDR-compatible compaction details, see + [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced). + + Args: + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt_cache_key: A key to use when reading from or writing to the prompt cache. + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: How long to retain a prompt cache entry created by this request. + + service_tier: Specifies the processing type used for serving the request. - If set to 'auto', + then the request will be processed with the service tier configured in the + Project settings. Unless otherwise configured, the Project will use 'default'. - + If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. - To opt-in + to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. For models with a dedicated Fast + tier, either value resolves to `service_tier=fast`; for other models, either + value resolves to `service_tier=priority`. - When not set, the default behavior + is 'auto'. When the `service_tier` parameter is set, the response body will + include the `service_tier` value based on the processing mode actually used to + serve the request. This response value may be different from the value set in + the parameter. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/responses/compact", + body=maybe_transform( + { + "model": model, + "input": input, + "instructions": instructions, + "previous_response_id": previous_response_id, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "service_tier": service_tier, + }, + response_compact_params.ResponseCompactParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=CompactedResponse, + ) + + def connect( + self, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> ResponsesConnectionManager: + """Connect to a persistent Responses API WebSocket. + + Send `response.create` events to create responses and `response.steer` events to queue user input containing text, images, or files for a continuation. Receive response stream events and steering acceptance, pending, or failure events over the socket. + """ + return ResponsesConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + ) + + +class AsyncResponses(AsyncAPIResource): + """Create and manage model responses.""" + + @cached_property + def input_items(self) -> AsyncInputItems: + """Create and manage model responses.""" + return AsyncInputItems(self._client) + + @cached_property + def input_tokens(self) -> AsyncInputTokens: + """Create and manage model responses.""" + return AsyncInputTokens(self._client) + + @cached_property + def with_raw_response(self) -> AsyncResponsesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncResponsesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncResponsesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncResponsesWithStreamingResponse(self) + + @overload + async def create( + self, + *, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response: + """Creates a model response. + + Provide + [text](https://developers.openai.com/api/docs/guides/text) or + [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to + generate [text](https://developers.openai.com/api/docs/guides/text) or + [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) + outputs. Have the model call your own + [custom code](https://developers.openai.com/api/docs/guides/function-calling) or + use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search) + to use your own data as input for the model's response. + + Args: + access_programs: Domain-specific access programs to use for this request. + + background: Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + moderation: Configuration for running moderation on the input and output of this response. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + stream: Literal[True], + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[ResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://developers.openai.com/api/docs/guides/text) or + [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to + generate [text](https://developers.openai.com/api/docs/guides/text) or + [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) + outputs. Have the model call your own + [custom code](https://developers.openai.com/api/docs/guides/function-calling) or + use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search) + to use your own data as input for the model's response. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + access_programs: Domain-specific access programs to use for this request. + + background: Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + moderation: Configuration for running moderation on the input and output of this response. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + stream: bool, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response | AsyncStream[ResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://developers.openai.com/api/docs/guides/text) or + [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to + generate [text](https://developers.openai.com/api/docs/guides/text) or + [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) + outputs. Have the model call your own + [custom code](https://developers.openai.com/api/docs/guides/function-calling) or + use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search) + to use your own data as input for the model's response. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + access_programs: Domain-specific access programs to use for this request. + + background: Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + moderation: Configuration for running moderation on the input and output of this response. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + async def create( + self, + *, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response | AsyncStream[ResponseStreamEvent]: + return await self._post( + "/responses", + body=await async_maybe_transform( + { + "access_programs": access_programs, + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParamsStreaming + if stream + else response_create_params.ResponseCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Response, + stream=stream or False, + stream_cls=AsyncStream[ResponseStreamEvent], + ) + + @overload + def stream( + self, + *, + response_id: str, + text_format: type[TextFormatT] | Omit = omit, + starting_after: int | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncResponseStreamManager[TextFormatT]: ... + + @overload + def stream( + self, + *, + input: Union[str, ResponseInputParam], + model: ResponsesModel, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + text_format: type[TextFormatT] | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncResponseStreamManager[TextFormatT]: ... + + def stream( + self, + *, + response_id: str | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + model: ResponsesModel | Omit = omit, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + text_format: type[TextFormatT] | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + starting_after: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncResponseStreamManager[TextFormatT]: + new_response_args = { + "access_programs": access_programs, + "input": input, + "model": model, + "context_management": context_management, + "conversation": conversation, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "moderation": moderation, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + "background": background, + } + new_response_args_names = [k for k, v in new_response_args.items() if is_given(v)] + + if (is_given(response_id) or is_given(starting_after)) and len(new_response_args_names) > 0: + raise ValueError( + "Cannot provide both response_id/starting_after can't be provided together with " + + ", ".join(new_response_args_names) + ) + + tools = _make_tools(tools) + if len(new_response_args_names) > 0: + if isinstance(input, NotGiven): + raise ValueError("input must be provided when creating a new response") + + if not is_given(model): + raise ValueError("model must be provided when creating a new response") + + if is_given(text_format): + if not text: + text = {} + + if "format" in text: + raise TypeError("Cannot mix and match text.format with text_format") + text = copy(text) + text["format"] = _type_to_text_format_param(text_format) + + api_request = self.create( + access_programs=access_programs, + input=input, + model=model, + stream=True, + tools=tools, + context_management=context_management, + conversation=conversation, + include=include, + instructions=instructions, + max_output_tokens=max_output_tokens, + max_tool_calls=max_tool_calls, + metadata=metadata, + moderation=moderation, + parallel_tool_calls=parallel_tool_calls, + previous_response_id=previous_response_id, + prompt=prompt, + prompt_cache_key=prompt_cache_key, + prompt_cache_options=prompt_cache_options, + prompt_cache_retention=prompt_cache_retention, + store=store, + stream_options=stream_options, + temperature=temperature, + text=text, + tool_choice=tool_choice, + reasoning=reasoning, + safety_identifier=safety_identifier, + service_tier=service_tier, + top_logprobs=top_logprobs, + top_p=top_p, + truncation=truncation, + user=user, + background=background, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + return AsyncResponseStreamManager( + api_request, + text_format=text_format, + input_tools=tools, + starting_after=None, + ) + else: + if isinstance(response_id, Omit): + raise ValueError("response_id must be provided when streaming an existing response") + + api_request = self.retrieve( + response_id, + stream=True, + include=include or [], + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return AsyncResponseStreamManager( + api_request, + text_format=text_format, + input_tools=tools, + starting_after=starting_after if is_given(starting_after) else None, + ) + + async def parse( + self, + *, + text_format: type[TextFormatT] | Omit = omit, + access_programs: response_create_params.AccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedResponse[TextFormatT]: + if is_given(text_format): + if not text: + text = {} + + if "format" in text: + raise TypeError("Cannot mix and match text.format with text_format") + text = copy(text) + text["format"] = _type_to_text_format_param(text_format) + + tools = _make_tools(tools) + + def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: + return parse_response( + input_tools=tools, + text_format=text_format, + response=raw_response, + ) + + return await self._post( + "/responses", + body=maybe_transform( + { + "access_programs": access_programs, + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + "verbosity": verbosity, + }, + response_create_params.ResponseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=parser, + security={"bearer_auth": True}, + ), + # we turn the `Response` instance into a `ParsedResponse` + # in the `parser` function above + cast_to=cast("Type[ParsedResponse[TextFormatT]]", Response), + ) + + @overload + async def retrieve( + self, + response_id: str, + *, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response: ... + + @overload + async def retrieve( + self, + response_id: str, + *, + stream: Literal[True], + include: List[ResponseIncludable] | Omit = omit, + starting_after: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncStream[ResponseStreamEvent]: ... + + @overload + async def retrieve( + self, + response_id: str, + *, + stream: bool, + include: List[ResponseIncludable] | Omit = omit, + starting_after: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response | AsyncStream[ResponseStreamEvent]: ... + + @overload + async def retrieve( + self, + response_id: str, + *, + stream: bool = False, + include: List[ResponseIncludable] | Omit = omit, + starting_after: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response | AsyncStream[ResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def retrieve( + self, + response_id: str, + *, + stream: Literal[True], + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[ResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def retrieve( + self, + response_id: str, + *, + stream: bool, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response | AsyncStream[ResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + async def retrieve( + self, + response_id: str, + *, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response | AsyncStream[ResponseStreamEvent]: + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return await self._get( + path_template("/responses/{response_id}", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "include": include, + "include_obfuscation": include_obfuscation, + "starting_after": starting_after, + "stream": stream, + }, + response_retrieve_params.ResponseRetrieveParams, + ), + security={"bearer_auth": True}, + ), + cast_to=Response, + stream=stream or False, + stream_cls=AsyncStream[ResponseStreamEvent], + ) + + async def delete( + self, + response_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Deletes a model response with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + path_template("/responses/{response_id}", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + async def cancel( + self, + response_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Response: + """Cancels a model response with the given ID. + + Only responses created with the + `background` parameter set to `true` can be cancelled. + [Learn more](https://developers.openai.com/api/docs/guides/background). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return await self._post( + path_template("/responses/{response_id}/cancel", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Response, + ) + + async def compact( + self, + *, + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + None, + ], + input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: Optional[response_compact_params.PromptCacheOptions] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "fast", "flex", "priority"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> CompactedResponse: + """Compact a conversation. + + Returns a compacted response object. + + Learn when and how to compact long-running conversations in the + [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window). + For ZDR-compatible compaction details, see + [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced). + + Args: + model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide + range of models with different capabilities, performance characteristics, and + price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt_cache_key: A key to use when reading from or writing to the prompt cache. + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: How long to retain a prompt cache entry created by this request. + + service_tier: Specifies the processing type used for serving the request. - If set to 'auto', + then the request will be processed with the service tier configured in the + Project settings. Unless otherwise configured, the Project will use 'default'. - + If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. - To opt-in + to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. For models with a dedicated Fast + tier, either value resolves to `service_tier=fast`; for other models, either + value resolves to `service_tier=priority`. - When not set, the default behavior + is 'auto'. When the `service_tier` parameter is set, the response body will + include the `service_tier` value based on the processing mode actually used to + serve the request. This response value may be different from the value set in + the parameter. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/responses/compact", + body=await async_maybe_transform( + { + "model": model, + "input": input, + "instructions": instructions, + "previous_response_id": previous_response_id, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "service_tier": service_tier, + }, + response_compact_params.ResponseCompactParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=CompactedResponse, + ) + + def connect( + self, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> AsyncResponsesConnectionManager: + """Connect to a persistent Responses API WebSocket. + + Send `response.create` events to create responses and `response.steer` events to queue user input containing text, images, or files for a continuation. Receive response stream events and steering acceptance, pending, or failure events over the socket. + """ + return AsyncResponsesConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + ) + + +class ResponsesWithRawResponse: + def __init__(self, responses: Responses) -> None: + self._responses = responses + + self.create = _legacy_response.to_raw_response_wrapper( + responses.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + responses.retrieve, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + responses.delete, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + responses.cancel, + ) + self.compact = _legacy_response.to_raw_response_wrapper( + responses.compact, + ) + self.parse = _legacy_response.to_raw_response_wrapper( + responses.parse, + ) + + @cached_property + def input_items(self) -> InputItemsWithRawResponse: + """Create and manage model responses.""" + return InputItemsWithRawResponse(self._responses.input_items) + + @cached_property + def input_tokens(self) -> InputTokensWithRawResponse: + """Create and manage model responses.""" + return InputTokensWithRawResponse(self._responses.input_tokens) + + +class AsyncResponsesWithRawResponse: + def __init__(self, responses: AsyncResponses) -> None: + self._responses = responses + + self.create = _legacy_response.async_to_raw_response_wrapper( + responses.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + responses.retrieve, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + responses.delete, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + responses.cancel, + ) + self.compact = _legacy_response.async_to_raw_response_wrapper( + responses.compact, + ) + self.parse = _legacy_response.async_to_raw_response_wrapper( + responses.parse, + ) + + @cached_property + def input_items(self) -> AsyncInputItemsWithRawResponse: + """Create and manage model responses.""" + return AsyncInputItemsWithRawResponse(self._responses.input_items) + + @cached_property + def input_tokens(self) -> AsyncInputTokensWithRawResponse: + """Create and manage model responses.""" + return AsyncInputTokensWithRawResponse(self._responses.input_tokens) + + +class ResponsesWithStreamingResponse: + def __init__(self, responses: Responses) -> None: + self._responses = responses + + self.create = to_streamed_response_wrapper( + responses.create, + ) + self.retrieve = to_streamed_response_wrapper( + responses.retrieve, + ) + self.delete = to_streamed_response_wrapper( + responses.delete, + ) + self.cancel = to_streamed_response_wrapper( + responses.cancel, + ) + self.compact = to_streamed_response_wrapper( + responses.compact, + ) + + @cached_property + def input_items(self) -> InputItemsWithStreamingResponse: + """Create and manage model responses.""" + return InputItemsWithStreamingResponse(self._responses.input_items) + + @cached_property + def input_tokens(self) -> InputTokensWithStreamingResponse: + """Create and manage model responses.""" + return InputTokensWithStreamingResponse(self._responses.input_tokens) + + +class AsyncResponsesWithStreamingResponse: + def __init__(self, responses: AsyncResponses) -> None: + self._responses = responses + + self.create = async_to_streamed_response_wrapper( + responses.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + responses.retrieve, + ) + self.delete = async_to_streamed_response_wrapper( + responses.delete, + ) + self.cancel = async_to_streamed_response_wrapper( + responses.cancel, + ) + self.compact = async_to_streamed_response_wrapper( + responses.compact, + ) + + @cached_property + def input_items(self) -> AsyncInputItemsWithStreamingResponse: + """Create and manage model responses.""" + return AsyncInputItemsWithStreamingResponse(self._responses.input_items) + + @cached_property + def input_tokens(self) -> AsyncInputTokensWithStreamingResponse: + """Create and manage model responses.""" + return AsyncInputTokensWithStreamingResponse(self._responses.input_tokens) + + +def _make_tools(tools: Iterable[ParseableToolParam] | Omit) -> List[ToolParam] | Omit: + if not is_given(tools): + return omit + + converted_tools: List[ToolParam] = [] + for tool in tools: + if tool["type"] != "function": + converted_tools.append(tool) + continue + + if "function" not in tool: + # standard Responses API case + converted_tools.append(tool) + continue + + function = cast(Any, tool)["function"] # pyright: ignore[reportUnnecessaryCast] + if not isinstance(function, PydanticFunctionTool): + raise Exception( + "Expected Chat Completions function tool shape to be created using `openai.pydantic_function_tool()`" + ) + + assert "parameters" in function + new_tool = ResponsesPydanticFunctionTool( + { + "type": "function", + "name": function["name"], + "description": function.get("description"), + "parameters": function["parameters"], + "strict": function.get("strict") or False, + }, + function.model, + ) + + converted_tools.append(new_tool.cast()) + + return converted_tools + + +class AsyncResponsesConnection: + """Represents a live WebSocket connection to the Responses API""" + + response: AsyncResponsesResponseResource + + _connection: AsyncWebSocketConnection + + def __init__( + self, + connection: AsyncWebSocketConnection, + *, + make_ws: Callable[[Query, Headers], Awaitable[AsyncWebSocketConnection]] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._reconnect_attempt = 0 + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue or SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=False) + + self.response = AsyncResponsesResponseResource(self) + + async def __aiter__(self) -> AsyncIterator[ResponsesServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield await self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not await self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + async def recv(self) -> ResponsesServerEvent: + """ + Receive the next message from the connection and parses it into a `ResponsesServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + event = self.parse_event(await self.recv_bytes()) + event_type = ( + cast("dict[str, object]", event).get("type") + if isinstance(cast(object, event), dict) + else getattr(event, "type", None) + ) + # A successful upgrade can still be followed by an admission error. + # Reset the budget only after receiving a non-error application event. + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return event + + async def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `ResponsesServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = await self._connection.recv(decode=False) + log.debug("Received WebSocket message: %i bytes", len(message)) + if self._reconnect_attempt: + # Account for raw application progress without changing frame delivery. + try: + event_data: object = json.loads(message) + except (ValueError, RecursionError): + return message + event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return message + + async def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(await async_maybe_transform(event, ResponsesClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + try: + await self._connection.send(data) + except Exception: + self._send_queue.enqueue(data) + raise + + async def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + await self._connection.send(data) + + async def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + await self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> ResponsesServerEvent: + """ + Converts a raw `str` or `bytes` message into a `ResponsesServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + ResponsesServerEvent, + construct_type_unchecked(value=json.loads(data), type_=cast(Any, ResponsesServerEvent)), + ) + + async def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + import asyncio + + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1): + self._reconnect_attempt = attempt + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + await asyncio.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = await self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + await self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + async def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + + async def _send(data: str) -> None: + await self._connection.send(data) + + try: + await self._send_queue.flush_async(_send) + except Exception: + log.warning("Failed to flush send queue after reconnect") + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("response.steer.accepted", my_handler) + + Or as a decorator:: + + @connection.on("response.steer.accepted") + async def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncResponsesConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks until the connection is closed. This is the push-based + alternative to iterating with ``async for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + import asyncio + + async for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, ResponseWsError): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + for handler in generic: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + +class AsyncResponsesConnectionManager: + """ + Context manager over a `AsyncResponsesConnection` that is returned by `responses.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.responses.connect(...).enter() + # ... + await connection.close() + ``` + """ + + def __init__( + self, + *, + client: AsyncOpenAI, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__connection: AsyncResponsesConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=False) + + def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, ResponsesClientEventParam)) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``AsyncResponsesConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncResponsesConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def __aenter__(self) -> AsyncResponsesConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.responses.connect(...).enter() + # ... + await connection.close() + ``` + """ + ws = await self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = AsyncResponsesConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + await self.__connection._flush_send_queue() + + return self.__connection + + enter = __aenter__ + + async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection: + try: + from ...lib._websocket import _WebSocketConnect as connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **extra_query, + }, + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + options = await self.__client._prepare_options( + FinalRequestOptions.construct( + method="get", + url=str(url), + headers=dict(extra_headers), + security={"bearer_auth": True}, + ) + ) + url = self.__client._prepare_url(options.url).copy_merge_params( + self.__client.qs.stringify(cast(Any, options.params)) + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + headers = { + key.lower(): (key, value) + for header_set in ( + self.__client.auth_headers, + {}, + self.__client.default_headers, + options.headers if is_given(options.headers) else {}, + ) + for key, value in header_set.items() + } + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + return await connect( + str(url), + user_agent_header=None, + additional_headers=_merge_mappings(dict(headers.values()), {}), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = normalize_httpx_url(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses" + return base_url.copy_with(raw_path=merge_raw_path) + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + await self.__connection.close() + + +class ResponsesConnection: + """Represents a live WebSocket connection to the Responses API""" + + response: ResponsesResponseResource + + _connection: WebSocketConnection + + def __init__( + self, + connection: WebSocketConnection, + *, + make_ws: Callable[[Query, Headers], WebSocketConnection] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._reconnect_attempt = 0 + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._close_event = threading.Event() + self._connection_lock = threading.Lock() + self._is_reconnecting = False + self._send_queue = send_queue or SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=True) + + self.response = ResponsesResponseResource(self) + + def __iter__(self) -> Iterator[ResponsesServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + def recv(self) -> ResponsesServerEvent: + """ + Receive the next message from the connection and parses it into a `ResponsesServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + event = self.parse_event(self.recv_bytes()) + event_type = ( + cast("dict[str, object]", event).get("type") + if isinstance(cast(object, event), dict) + else getattr(event, "type", None) + ) + # A successful upgrade can still be followed by an admission error. + # Reset the budget only after receiving a non-error application event. + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return event + + def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `ResponsesServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + from ...lib._websocket import _recv_bytes + + message = _recv_bytes(self._connection) + log.debug("Received WebSocket message: %i bytes", len(message)) + if self._reconnect_attempt: + # Account for raw application progress without changing frame delivery. + try: + event_data: object = json.loads(message) + except (ValueError, RecursionError): + return message + event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None + if isinstance(event_type, str) and event_type and event_type != "error": + self._reconnect_attempt = 0 + return message + + def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, ResponsesClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + try: + self._connection.send(data) + except Exception: + self._send_queue.enqueue(data) + raise + + def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + self._connection.send(data) + + def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + self._close_event.set() + with self._connection_lock: + connection = self._connection + connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> ResponsesServerEvent: + """ + Converts a raw `str` or `bytes` message into a `ResponsesServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + ResponsesServerEvent, + construct_type_unchecked(value=json.loads(data), type_=cast(Any, ResponsesServerEvent)), + ) + + def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + if self._on_reconnecting is None or self._make_ws is None or self._close_event.is_set(): + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + with self._connection_lock: + self._is_reconnecting = True + + for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1): + self._reconnect_attempt = attempt + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + if self._close_event.wait(delay): + self._is_reconnecting = False + return False + + try: + connection = self._make_ws(self._extra_query, self._extra_headers) + with self._connection_lock: + closed = self._intentionally_closed + if not closed: + self._connection = connection + if closed: + connection.close() + self._is_reconnecting = False + return False + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + try: + self._send_queue.flush_sync(lambda data: self._connection.send(data)) + except Exception: + log.warning("Failed to flush send queue after reconnect") + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("response.steer.accepted", my_handler) + + Or as a decorator:: + + @connection.on("response.steer.accepted") + def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> ResponsesConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks the current thread until the connection is closed. This is the push-based + alternative to iterating with ``for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, ResponseWsError): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + handler(event) + + for handler in generic: + handler(event) + + +class ResponsesConnectionManager: + """ + Context manager over a `ResponsesConnection` that is returned by `responses.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.responses.connect(...).enter() + # ... + connection.close() + ``` + """ + + def __init__( + self, + *, + client: OpenAI, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__connection: ResponsesConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=True) + + def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, ResponsesClientEventParam)) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``ResponsesConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> ResponsesConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def __enter__(self) -> ResponsesConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.responses.connect(...).enter() + # ... + connection.close() + ``` + """ + ws = self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = ResponsesConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + self.__connection._flush_send_queue() + + return self.__connection + + enter = __enter__ + + def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketConnection: + try: + from websockets.sync.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **extra_query, + }, + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + options = self.__client._prepare_options( + FinalRequestOptions.construct( + method="get", + url=str(url), + headers=dict(extra_headers), + security={"bearer_auth": True}, + ) + ) + url = self.__client._prepare_url(options.url).copy_merge_params( + self.__client.qs.stringify(cast(Any, options.params)) + ) + url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme)) + headers = { + key.lower(): (key, value) + for header_set in ( + self.__client.auth_headers, + {}, + self.__client.default_headers, + options.headers if is_given(options.headers) else {}, + ) + for key, value in header_set.items() + } + log.debug("Connecting to WebSocket API") + if self.__websocket_connection_options: + log.debug("Custom WebSocket connection options provided") + + return connect( + str(url), + user_agent_header=None, + additional_headers=_merge_mappings(dict(headers.values()), {}), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx2.URL: + if self.__client.websocket_base_url is not None: + base_url = normalize_httpx_url(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses" + return base_url.copy_with(raw_path=merge_raw_path) + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + self.__connection.close() + + +class BaseResponsesConnectionResource: + def __init__(self, connection: ResponsesConnection) -> None: + self._connection = connection + + +class ResponsesResponseResource(BaseResponsesConnectionResource): + def create( + self, + *, + access_programs: responses_client_event_param.ResponseCreateAccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[responses_client_event_param.ResponseCreateContextManagement]] + | Omit = omit, + conversation: Optional[responses_client_event_param.ResponseCreateConversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + moderation: Optional[responses_client_event_param.ResponseCreateModeration] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: responses_client_event_param.ResponseCreatePromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[bool] | Omit = omit, + stream_id: str | Omit = omit, + stream_options: Optional[responses_client_event_param.ResponseCreateStreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: responses_client_event_param.ResponseCreateToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + ) -> None: + """ + Client event for creating a response over a persistent WebSocket connection. + This payload uses the same top-level fields as `POST /v1/responses`, plus + WebSocket-only envelope metadata. + + Notes: + - `stream` is implicit over WebSocket and should not be sent. + - `background` is not supported over WebSocket. + - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`. + """ + self._connection.send( + cast( + ResponsesClientEventParam, + strip_not_given( + { + "type": "response.create", + "access_programs": access_programs, + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_id": stream_id, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + } + ), + ) + ) + + def steer(self, *, input: ResponseSteerInputParam, previous_response_id: str) -> None: + """Queues user input to steer a response on this WebSocket connection. + + Input + can contain text, images, and files. Steering is supported only for + single-agent responses on models and execution modes that support steering. + Responses bound to a conversation or using automatic compaction do not + support steering. + + A `response.steer.accepted` event acknowledges that the server owns the + queued input, not that it has been applied. The successor's `response.created` + event is the commit point. Input that cannot be committed is returned in + `response.steer.failed`. + + Steering may cause the active response to finish at a safe output boundary + with `response.incomplete` and `incomplete_details.reason` set to `steered`, + followed automatically by a successor `response.created`. Normal completion + can also be followed by an automatic successor. Automatic successors inherit + the previous response's settings and continue from it with the queued input. + + If the response stops for client-owned tool output or approval, accepted + steering input remains queued and `response.steer.pending` is emitted after + `response.completed`. Fill the `required_input` stubs from that event with + saved tool results or approval decisions, and send one explicit + `response.create` per parent with the same `previous_response_id` and + WebSocket lane. Do not rerun tools or resend accepted steering input. The + queued input is prepended in submission order to that request's input, and + the explicit request retains its own settings. + + This event accepts only `type`, `previous_response_id`, and `input`. Do not + send `stream_id`; the target response determines the WebSocket lane. + """ + self._connection.send({"type": "response.steer", "input": input, "previous_response_id": previous_response_id}) + + +class BaseAsyncResponsesConnectionResource: + def __init__(self, connection: AsyncResponsesConnection) -> None: + self._connection = connection + + +class AsyncResponsesResponseResource(BaseAsyncResponsesConnectionResource): + async def create( + self, + *, + access_programs: responses_client_event_param.ResponseCreateAccessPrograms | Omit = omit, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[responses_client_event_param.ResponseCreateContextManagement]] + | Omit = omit, + conversation: Optional[responses_client_event_param.ResponseCreateConversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + moderation: Optional[responses_client_event_param.ResponseCreateModeration] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: responses_client_event_param.ResponseCreatePromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, + service_tier: Optional[ServiceTier] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[bool] | Omit = omit, + stream_id: str | Omit = omit, + stream_options: Optional[responses_client_event_param.ResponseCreateStreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: responses_client_event_param.ResponseCreateToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + ) -> None: + """ + Client event for creating a response over a persistent WebSocket connection. + This payload uses the same top-level fields as `POST /v1/responses`, plus + WebSocket-only envelope metadata. + + Notes: + - `stream` is implicit over WebSocket and should not be sent. + - `background` is not supported over WebSocket. + - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`. + """ + await self._connection.send( + cast( + ResponsesClientEventParam, + strip_not_given( + { + "type": "response.create", + "access_programs": access_programs, + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_id": stream_id, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + } + ), + ) + ) + + async def steer(self, *, input: ResponseSteerInputParam, previous_response_id: str) -> None: + """Queues user input to steer a response on this WebSocket connection. + + Input + can contain text, images, and files. Steering is supported only for + single-agent responses on models and execution modes that support steering. + Responses bound to a conversation or using automatic compaction do not + support steering. + + A `response.steer.accepted` event acknowledges that the server owns the + queued input, not that it has been applied. The successor's `response.created` + event is the commit point. Input that cannot be committed is returned in + `response.steer.failed`. + + Steering may cause the active response to finish at a safe output boundary + with `response.incomplete` and `incomplete_details.reason` set to `steered`, + followed automatically by a successor `response.created`. Normal completion + can also be followed by an automatic successor. Automatic successors inherit + the previous response's settings and continue from it with the queued input. + + If the response stops for client-owned tool output or approval, accepted + steering input remains queued and `response.steer.pending` is emitted after + `response.completed`. Fill the `required_input` stubs from that event with + saved tool results or approval decisions, and send one explicit + `response.create` per parent with the same `previous_response_id` and + WebSocket lane. Do not rerun tools or resend accepted steering input. The + queued input is prepended in submission order to that request's input, and + the explicit request retains its own settings. + + This event accepts only `type`, `previous_response_id`, and `input`. Do not + send `stream_id`; the target response determines the WebSocket lane. + """ + await self._connection.send( + {"type": "response.steer", "input": input, "previous_response_id": previous_response_id} + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/safety/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/safety/__init__.py new file mode 100644 index 0000000..c25334f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/safety/__init__.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .cases import ( + Cases as Cases, + AsyncCases as AsyncCases, + CasesWithRawResponse as CasesWithRawResponse, + AsyncCasesWithRawResponse as AsyncCasesWithRawResponse, + CasesWithStreamingResponse as CasesWithStreamingResponse, + AsyncCasesWithStreamingResponse as AsyncCasesWithStreamingResponse, + ) + from .alerts import ( + Alerts as Alerts, + AsyncAlerts as AsyncAlerts, + AlertsWithRawResponse as AlertsWithRawResponse, + AsyncAlertsWithRawResponse as AsyncAlertsWithRawResponse, + AlertsWithStreamingResponse as AlertsWithStreamingResponse, + AsyncAlertsWithStreamingResponse as AsyncAlertsWithStreamingResponse, + ) + from .safety import ( + Safety as Safety, + AsyncSafety as AsyncSafety, + SafetyWithRawResponse as SafetyWithRawResponse, + AsyncSafetyWithRawResponse as AsyncSafetyWithRawResponse, + SafetyWithStreamingResponse as SafetyWithStreamingResponse, + AsyncSafetyWithStreamingResponse as AsyncSafetyWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Cases": (".cases", "Cases"), + "AsyncCases": (".cases", "AsyncCases"), + "CasesWithRawResponse": (".cases", "CasesWithRawResponse"), + "AsyncCasesWithRawResponse": (".cases", "AsyncCasesWithRawResponse"), + "CasesWithStreamingResponse": (".cases", "CasesWithStreamingResponse"), + "AsyncCasesWithStreamingResponse": (".cases", "AsyncCasesWithStreamingResponse"), + "Alerts": (".alerts", "Alerts"), + "AsyncAlerts": (".alerts", "AsyncAlerts"), + "AlertsWithRawResponse": (".alerts", "AlertsWithRawResponse"), + "AsyncAlertsWithRawResponse": (".alerts", "AsyncAlertsWithRawResponse"), + "AlertsWithStreamingResponse": (".alerts", "AlertsWithStreamingResponse"), + "AsyncAlertsWithStreamingResponse": (".alerts", "AsyncAlertsWithStreamingResponse"), + "Safety": (".safety", "Safety"), + "AsyncSafety": (".safety", "AsyncSafety"), + "SafetyWithRawResponse": (".safety", "SafetyWithRawResponse"), + "AsyncSafetyWithRawResponse": (".safety", "AsyncSafetyWithRawResponse"), + "SafetyWithStreamingResponse": (".safety", "SafetyWithStreamingResponse"), + "AsyncSafetyWithStreamingResponse": (".safety", "AsyncSafetyWithStreamingResponse"), + } + _SUBMODULES = { + "alerts", + "cases", + "safety", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Cases", + "AsyncCases", + "CasesWithRawResponse", + "AsyncCasesWithRawResponse", + "CasesWithStreamingResponse", + "AsyncCasesWithStreamingResponse", + "Alerts", + "AsyncAlerts", + "AlertsWithRawResponse", + "AsyncAlertsWithRawResponse", + "AlertsWithStreamingResponse", + "AsyncAlertsWithStreamingResponse", + "Safety", + "AsyncSafety", + "SafetyWithRawResponse", + "AsyncSafetyWithRawResponse", + "SafetyWithStreamingResponse", + "AsyncSafetyWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/safety/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/safety/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..09cf3d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/safety/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/safety/__pycache__/alerts.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/safety/__pycache__/alerts.cpython-312.pyc new file mode 100644 index 0000000..32f3a74 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/safety/__pycache__/alerts.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/safety/__pycache__/cases.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/safety/__pycache__/cases.cpython-312.pyc new file mode 100644 index 0000000..bef7537 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/safety/__pycache__/cases.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/safety/__pycache__/safety.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/safety/__pycache__/safety.cpython-312.pyc new file mode 100644 index 0000000..27c70d5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/safety/__pycache__/safety.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/safety/alerts.py b/venv/lib/python3.12/site-packages/openai/resources/safety/alerts.py new file mode 100644 index 0000000..eb3ee7e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/safety/alerts.py @@ -0,0 +1,172 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from ... import _legacy_response +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import path_template +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.safety.safety_alert import SafetyAlert + +__all__ = ["Alerts", "AsyncAlerts"] + + +class Alerts(SyncAPIResource): + @cached_property + def with_raw_response(self) -> AlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AlertsWithStreamingResponse(self) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SafetyAlert: + """ + Get a safety alert belonging to the authenticated API project. + + Args: + id: Project safety alert ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + path_template("/safety/alerts/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SafetyAlert, + ) + + +class AsyncAlerts(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAlertsWithStreamingResponse(self) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SafetyAlert: + """ + Get a safety alert belonging to the authenticated API project. + + Args: + id: Project safety alert ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + path_template("/safety/alerts/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SafetyAlert, + ) + + +class AlertsWithRawResponse: + def __init__(self, alerts: Alerts) -> None: + self._alerts = alerts + + self.retrieve = _legacy_response.to_raw_response_wrapper( + alerts.retrieve, + ) + + +class AsyncAlertsWithRawResponse: + def __init__(self, alerts: AsyncAlerts) -> None: + self._alerts = alerts + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + alerts.retrieve, + ) + + +class AlertsWithStreamingResponse: + def __init__(self, alerts: Alerts) -> None: + self._alerts = alerts + + self.retrieve = to_streamed_response_wrapper( + alerts.retrieve, + ) + + +class AsyncAlertsWithStreamingResponse: + def __init__(self, alerts: AsyncAlerts) -> None: + self._alerts = alerts + + self.retrieve = async_to_streamed_response_wrapper( + alerts.retrieve, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/safety/cases.py b/venv/lib/python3.12/site-packages/openai/resources/safety/cases.py new file mode 100644 index 0000000..7c59f4d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/safety/cases.py @@ -0,0 +1,172 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from ... import _legacy_response +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import path_template +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.safety.safety_case import SafetyCase + +__all__ = ["Cases", "AsyncCases"] + + +class Cases(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CasesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CasesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CasesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CasesWithStreamingResponse(self) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SafetyCase: + """ + Get a safety case by ID. + + Args: + id: Safety case ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + path_template("/safety/cases/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SafetyCase, + ) + + +class AsyncCases(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCasesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCasesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCasesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCasesWithStreamingResponse(self) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SafetyCase: + """ + Get a safety case by ID. + + Args: + id: Safety case ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + path_template("/safety/cases/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SafetyCase, + ) + + +class CasesWithRawResponse: + def __init__(self, cases: Cases) -> None: + self._cases = cases + + self.retrieve = _legacy_response.to_raw_response_wrapper( + cases.retrieve, + ) + + +class AsyncCasesWithRawResponse: + def __init__(self, cases: AsyncCases) -> None: + self._cases = cases + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + cases.retrieve, + ) + + +class CasesWithStreamingResponse: + def __init__(self, cases: Cases) -> None: + self._cases = cases + + self.retrieve = to_streamed_response_wrapper( + cases.retrieve, + ) + + +class AsyncCasesWithStreamingResponse: + def __init__(self, cases: AsyncCases) -> None: + self._cases = cases + + self.retrieve = async_to_streamed_response_wrapper( + cases.retrieve, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/safety/safety.py b/venv/lib/python3.12/site-packages/openai/resources/safety/safety.py new file mode 100644 index 0000000..ae72977 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/safety/safety.py @@ -0,0 +1,134 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .cases import ( + Cases, + AsyncCases, + CasesWithRawResponse, + AsyncCasesWithRawResponse, + CasesWithStreamingResponse, + AsyncCasesWithStreamingResponse, +) +from .alerts import ( + Alerts, + AsyncAlerts, + AlertsWithRawResponse, + AsyncAlertsWithRawResponse, + AlertsWithStreamingResponse, + AsyncAlertsWithStreamingResponse, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["Safety", "AsyncSafety"] + + +class Safety(SyncAPIResource): + @cached_property + def cases(self) -> Cases: + return Cases(self._client) + + @cached_property + def alerts(self) -> Alerts: + return Alerts(self._client) + + @cached_property + def with_raw_response(self) -> SafetyWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SafetyWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SafetyWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SafetyWithStreamingResponse(self) + + +class AsyncSafety(AsyncAPIResource): + @cached_property + def cases(self) -> AsyncCases: + return AsyncCases(self._client) + + @cached_property + def alerts(self) -> AsyncAlerts: + return AsyncAlerts(self._client) + + @cached_property + def with_raw_response(self) -> AsyncSafetyWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSafetyWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSafetyWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSafetyWithStreamingResponse(self) + + +class SafetyWithRawResponse: + def __init__(self, safety: Safety) -> None: + self._safety = safety + + @cached_property + def cases(self) -> CasesWithRawResponse: + return CasesWithRawResponse(self._safety.cases) + + @cached_property + def alerts(self) -> AlertsWithRawResponse: + return AlertsWithRawResponse(self._safety.alerts) + + +class AsyncSafetyWithRawResponse: + def __init__(self, safety: AsyncSafety) -> None: + self._safety = safety + + @cached_property + def cases(self) -> AsyncCasesWithRawResponse: + return AsyncCasesWithRawResponse(self._safety.cases) + + @cached_property + def alerts(self) -> AsyncAlertsWithRawResponse: + return AsyncAlertsWithRawResponse(self._safety.alerts) + + +class SafetyWithStreamingResponse: + def __init__(self, safety: Safety) -> None: + self._safety = safety + + @cached_property + def cases(self) -> CasesWithStreamingResponse: + return CasesWithStreamingResponse(self._safety.cases) + + @cached_property + def alerts(self) -> AlertsWithStreamingResponse: + return AlertsWithStreamingResponse(self._safety.alerts) + + +class AsyncSafetyWithStreamingResponse: + def __init__(self, safety: AsyncSafety) -> None: + self._safety = safety + + @cached_property + def cases(self) -> AsyncCasesWithStreamingResponse: + return AsyncCasesWithStreamingResponse(self._safety.cases) + + @cached_property + def alerts(self) -> AsyncAlertsWithStreamingResponse: + return AsyncAlertsWithStreamingResponse(self._safety.alerts) diff --git a/venv/lib/python3.12/site-packages/openai/resources/skills/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/skills/__init__.py new file mode 100644 index 0000000..84b69f3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/skills/__init__.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .skills import ( + Skills as Skills, + AsyncSkills as AsyncSkills, + SkillsWithRawResponse as SkillsWithRawResponse, + AsyncSkillsWithRawResponse as AsyncSkillsWithRawResponse, + SkillsWithStreamingResponse as SkillsWithStreamingResponse, + AsyncSkillsWithStreamingResponse as AsyncSkillsWithStreamingResponse, + ) + from .content import ( + Content as Content, + AsyncContent as AsyncContent, + ContentWithRawResponse as ContentWithRawResponse, + AsyncContentWithRawResponse as AsyncContentWithRawResponse, + ContentWithStreamingResponse as ContentWithStreamingResponse, + AsyncContentWithStreamingResponse as AsyncContentWithStreamingResponse, + ) + from .versions import ( + Versions as Versions, + AsyncVersions as AsyncVersions, + VersionsWithRawResponse as VersionsWithRawResponse, + AsyncVersionsWithRawResponse as AsyncVersionsWithRawResponse, + VersionsWithStreamingResponse as VersionsWithStreamingResponse, + AsyncVersionsWithStreamingResponse as AsyncVersionsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Content": (".content", "Content"), + "AsyncContent": (".content", "AsyncContent"), + "ContentWithRawResponse": (".content", "ContentWithRawResponse"), + "AsyncContentWithRawResponse": (".content", "AsyncContentWithRawResponse"), + "ContentWithStreamingResponse": (".content", "ContentWithStreamingResponse"), + "AsyncContentWithStreamingResponse": (".content", "AsyncContentWithStreamingResponse"), + "Versions": (".versions", "Versions"), + "AsyncVersions": (".versions", "AsyncVersions"), + "VersionsWithRawResponse": (".versions", "VersionsWithRawResponse"), + "AsyncVersionsWithRawResponse": (".versions", "AsyncVersionsWithRawResponse"), + "VersionsWithStreamingResponse": (".versions", "VersionsWithStreamingResponse"), + "AsyncVersionsWithStreamingResponse": (".versions", "AsyncVersionsWithStreamingResponse"), + "Skills": (".skills", "Skills"), + "AsyncSkills": (".skills", "AsyncSkills"), + "SkillsWithRawResponse": (".skills", "SkillsWithRawResponse"), + "AsyncSkillsWithRawResponse": (".skills", "AsyncSkillsWithRawResponse"), + "SkillsWithStreamingResponse": (".skills", "SkillsWithStreamingResponse"), + "AsyncSkillsWithStreamingResponse": (".skills", "AsyncSkillsWithStreamingResponse"), + } + _SUBMODULES = { + "content", + "skills", + "versions", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Content", + "AsyncContent", + "ContentWithRawResponse", + "AsyncContentWithRawResponse", + "ContentWithStreamingResponse", + "AsyncContentWithStreamingResponse", + "Versions", + "AsyncVersions", + "VersionsWithRawResponse", + "AsyncVersionsWithRawResponse", + "VersionsWithStreamingResponse", + "AsyncVersionsWithStreamingResponse", + "Skills", + "AsyncSkills", + "SkillsWithRawResponse", + "AsyncSkillsWithRawResponse", + "SkillsWithStreamingResponse", + "AsyncSkillsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/skills/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/skills/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..dd5ad30 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/skills/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/skills/__pycache__/content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/skills/__pycache__/content.cpython-312.pyc new file mode 100644 index 0000000..0ab0a2e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/skills/__pycache__/content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/skills/__pycache__/skills.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/skills/__pycache__/skills.cpython-312.pyc new file mode 100644 index 0000000..ebfcac5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/skills/__pycache__/skills.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/skills/content.py b/venv/lib/python3.12/site-packages/openai/resources/skills/content.py new file mode 100644 index 0000000..91ac17d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/skills/content.py @@ -0,0 +1,176 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from ... import _legacy_response +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import path_template +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ..._base_client import make_request_options + +__all__ = ["Content", "AsyncContent"] + + +class Content(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ContentWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ContentWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ContentWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ContentWithStreamingResponse(self) + + def retrieve( + self, + skill_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Download a skill zip bundle by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return self._get( + path_template("/skills/{skill_id}/content", skill_id=skill_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class AsyncContent(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncContentWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncContentWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncContentWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncContentWithStreamingResponse(self) + + async def retrieve( + self, + skill_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Download a skill zip bundle by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return await self._get( + path_template("/skills/{skill_id}/content", skill_id=skill_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class ContentWithRawResponse: + def __init__(self, content: Content) -> None: + self._content = content + + self.retrieve = _legacy_response.to_raw_response_wrapper( + content.retrieve, + ) + + +class AsyncContentWithRawResponse: + def __init__(self, content: AsyncContent) -> None: + self._content = content + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + content.retrieve, + ) + + +class ContentWithStreamingResponse: + def __init__(self, content: Content) -> None: + self._content = content + + self.retrieve = to_custom_streamed_response_wrapper( + content.retrieve, + StreamedBinaryAPIResponse, + ) + + +class AsyncContentWithStreamingResponse: + def __init__(self, content: AsyncContent) -> None: + self._content = content + + self.retrieve = async_to_custom_streamed_response_wrapper( + content.retrieve, + AsyncStreamedBinaryAPIResponse, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/skills/skills.py b/venv/lib/python3.12/site-packages/openai/resources/skills/skills.py new file mode 100644 index 0000000..7c840a9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/skills/skills.py @@ -0,0 +1,653 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Mapping, cast +from typing_extensions import Literal + +import httpx2 + +from ... import _legacy_response +from ...types import skill_list_params, skill_create_params, skill_update_params +from .content import ( + Content, + AsyncContent, + ContentWithRawResponse, + AsyncContentWithRawResponse, + ContentWithStreamingResponse, + AsyncContentWithStreamingResponse, +) +from ..._files import deepcopy_with_paths +from ..._types import ( + Body, + Omit, + Query, + Headers, + NotGiven, + FileTypes, + SequenceNotStr, + omit, + not_given, +) +from ..._utils import extract_files, path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncCursorPage, AsyncCursorPage +from ...types.skill import Skill +from ..._base_client import AsyncPaginator, make_request_options +from .versions.versions import ( + Versions, + AsyncVersions, + VersionsWithRawResponse, + AsyncVersionsWithRawResponse, + VersionsWithStreamingResponse, + AsyncVersionsWithStreamingResponse, +) +from ...types.deleted_skill import DeletedSkill + +__all__ = ["Skills", "AsyncSkills"] + + +class Skills(SyncAPIResource): + @cached_property + def content(self) -> Content: + return Content(self._client) + + @cached_property + def versions(self) -> Versions: + return Versions(self._client) + + @cached_property + def with_raw_response(self) -> SkillsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SkillsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SkillsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SkillsWithStreamingResponse(self) + + def create( + self, + *, + files: Union[SequenceNotStr[FileTypes], FileTypes] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Skill: + """ + Create a new skill. + + Args: + files: Skill files to upload (directory upload) or a single zip file. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths({"files": files}, [["files", ""], ["files"]]) + extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""], ["files"]]) + if extracted_files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/skills", + body=maybe_transform(body, skill_create_params.SkillCreateParams), + files=extracted_files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Skill, + ) + + def retrieve( + self, + skill_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Skill: + """ + Get a skill by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return self._get( + path_template("/skills/{skill_id}", skill_id=skill_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Skill, + ) + + def update( + self, + skill_id: str, + *, + default_version: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Skill: + """ + Update the default version pointer for a skill. + + Args: + default_version: The skill version number to set as default. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return self._post( + path_template("/skills/{skill_id}", skill_id=skill_id), + body=maybe_transform({"default_version": default_version}, skill_update_params.SkillUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Skill, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Skill]: + """ + List all skills for the current project. + + Args: + after: Identifier for the last item from the previous pagination request + + limit: Number of items to retrieve + + order: Sort order of results by timestamp. Use `asc` for ascending order or `desc` for + descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/skills", + page=SyncCursorPage[Skill], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + skill_list_params.SkillListParams, + ), + security={"bearer_auth": True}, + ), + model=Skill, + ) + + def delete( + self, + skill_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> DeletedSkill: + """ + Delete a skill by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return self._delete( + path_template("/skills/{skill_id}", skill_id=skill_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=DeletedSkill, + ) + + +class AsyncSkills(AsyncAPIResource): + @cached_property + def content(self) -> AsyncContent: + return AsyncContent(self._client) + + @cached_property + def versions(self) -> AsyncVersions: + return AsyncVersions(self._client) + + @cached_property + def with_raw_response(self) -> AsyncSkillsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSkillsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSkillsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSkillsWithStreamingResponse(self) + + async def create( + self, + *, + files: Union[SequenceNotStr[FileTypes], FileTypes] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Skill: + """ + Create a new skill. + + Args: + files: Skill files to upload (directory upload) or a single zip file. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths({"files": files}, [["files", ""], ["files"]]) + extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""], ["files"]]) + if extracted_files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/skills", + body=await async_maybe_transform(body, skill_create_params.SkillCreateParams), + files=extracted_files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Skill, + ) + + async def retrieve( + self, + skill_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Skill: + """ + Get a skill by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return await self._get( + path_template("/skills/{skill_id}", skill_id=skill_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Skill, + ) + + async def update( + self, + skill_id: str, + *, + default_version: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Skill: + """ + Update the default version pointer for a skill. + + Args: + default_version: The skill version number to set as default. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return await self._post( + path_template("/skills/{skill_id}", skill_id=skill_id), + body=await async_maybe_transform( + {"default_version": default_version}, skill_update_params.SkillUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Skill, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Skill, AsyncCursorPage[Skill]]: + """ + List all skills for the current project. + + Args: + after: Identifier for the last item from the previous pagination request + + limit: Number of items to retrieve + + order: Sort order of results by timestamp. Use `asc` for ascending order or `desc` for + descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/skills", + page=AsyncCursorPage[Skill], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + skill_list_params.SkillListParams, + ), + security={"bearer_auth": True}, + ), + model=Skill, + ) + + async def delete( + self, + skill_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> DeletedSkill: + """ + Delete a skill by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return await self._delete( + path_template("/skills/{skill_id}", skill_id=skill_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=DeletedSkill, + ) + + +class SkillsWithRawResponse: + def __init__(self, skills: Skills) -> None: + self._skills = skills + + self.create = _legacy_response.to_raw_response_wrapper( + skills.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + skills.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + skills.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + skills.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + skills.delete, + ) + + @cached_property + def content(self) -> ContentWithRawResponse: + return ContentWithRawResponse(self._skills.content) + + @cached_property + def versions(self) -> VersionsWithRawResponse: + return VersionsWithRawResponse(self._skills.versions) + + +class AsyncSkillsWithRawResponse: + def __init__(self, skills: AsyncSkills) -> None: + self._skills = skills + + self.create = _legacy_response.async_to_raw_response_wrapper( + skills.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + skills.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + skills.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + skills.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + skills.delete, + ) + + @cached_property + def content(self) -> AsyncContentWithRawResponse: + return AsyncContentWithRawResponse(self._skills.content) + + @cached_property + def versions(self) -> AsyncVersionsWithRawResponse: + return AsyncVersionsWithRawResponse(self._skills.versions) + + +class SkillsWithStreamingResponse: + def __init__(self, skills: Skills) -> None: + self._skills = skills + + self.create = to_streamed_response_wrapper( + skills.create, + ) + self.retrieve = to_streamed_response_wrapper( + skills.retrieve, + ) + self.update = to_streamed_response_wrapper( + skills.update, + ) + self.list = to_streamed_response_wrapper( + skills.list, + ) + self.delete = to_streamed_response_wrapper( + skills.delete, + ) + + @cached_property + def content(self) -> ContentWithStreamingResponse: + return ContentWithStreamingResponse(self._skills.content) + + @cached_property + def versions(self) -> VersionsWithStreamingResponse: + return VersionsWithStreamingResponse(self._skills.versions) + + +class AsyncSkillsWithStreamingResponse: + def __init__(self, skills: AsyncSkills) -> None: + self._skills = skills + + self.create = async_to_streamed_response_wrapper( + skills.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + skills.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + skills.update, + ) + self.list = async_to_streamed_response_wrapper( + skills.list, + ) + self.delete = async_to_streamed_response_wrapper( + skills.delete, + ) + + @cached_property + def content(self) -> AsyncContentWithStreamingResponse: + return AsyncContentWithStreamingResponse(self._skills.content) + + @cached_property + def versions(self) -> AsyncVersionsWithStreamingResponse: + return AsyncVersionsWithStreamingResponse(self._skills.versions) diff --git a/venv/lib/python3.12/site-packages/openai/resources/skills/versions/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/skills/versions/__init__.py new file mode 100644 index 0000000..7bde33e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/skills/versions/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .content import ( + Content as Content, + AsyncContent as AsyncContent, + ContentWithRawResponse as ContentWithRawResponse, + AsyncContentWithRawResponse as AsyncContentWithRawResponse, + ContentWithStreamingResponse as ContentWithStreamingResponse, + AsyncContentWithStreamingResponse as AsyncContentWithStreamingResponse, + ) + from .versions import ( + Versions as Versions, + AsyncVersions as AsyncVersions, + VersionsWithRawResponse as VersionsWithRawResponse, + AsyncVersionsWithRawResponse as AsyncVersionsWithRawResponse, + VersionsWithStreamingResponse as VersionsWithStreamingResponse, + AsyncVersionsWithStreamingResponse as AsyncVersionsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Content": (".content", "Content"), + "AsyncContent": (".content", "AsyncContent"), + "ContentWithRawResponse": (".content", "ContentWithRawResponse"), + "AsyncContentWithRawResponse": (".content", "AsyncContentWithRawResponse"), + "ContentWithStreamingResponse": (".content", "ContentWithStreamingResponse"), + "AsyncContentWithStreamingResponse": (".content", "AsyncContentWithStreamingResponse"), + "Versions": (".versions", "Versions"), + "AsyncVersions": (".versions", "AsyncVersions"), + "VersionsWithRawResponse": (".versions", "VersionsWithRawResponse"), + "AsyncVersionsWithRawResponse": (".versions", "AsyncVersionsWithRawResponse"), + "VersionsWithStreamingResponse": (".versions", "VersionsWithStreamingResponse"), + "AsyncVersionsWithStreamingResponse": (".versions", "AsyncVersionsWithStreamingResponse"), + } + _SUBMODULES = { + "content", + "versions", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Content", + "AsyncContent", + "ContentWithRawResponse", + "AsyncContentWithRawResponse", + "ContentWithStreamingResponse", + "AsyncContentWithStreamingResponse", + "Versions", + "AsyncVersions", + "VersionsWithRawResponse", + "AsyncVersionsWithRawResponse", + "VersionsWithStreamingResponse", + "AsyncVersionsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/skills/versions/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/skills/versions/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..8a46dea Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/skills/versions/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/skills/versions/__pycache__/content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/skills/versions/__pycache__/content.cpython-312.pyc new file mode 100644 index 0000000..ad41084 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/skills/versions/__pycache__/content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/skills/versions/__pycache__/versions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/skills/versions/__pycache__/versions.cpython-312.pyc new file mode 100644 index 0000000..1e3e735 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/skills/versions/__pycache__/versions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/skills/versions/content.py b/venv/lib/python3.12/site-packages/openai/resources/skills/versions/content.py new file mode 100644 index 0000000..241b3f6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/skills/versions/content.py @@ -0,0 +1,186 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from .... import _legacy_response +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import path_template +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ...._base_client import make_request_options + +__all__ = ["Content", "AsyncContent"] + + +class Content(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ContentWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ContentWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ContentWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ContentWithStreamingResponse(self) + + def retrieve( + self, + version: str, + *, + skill_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Download a skill version zip bundle. + + Args: + version: The skill version number. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + if not version: + raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return self._get( + path_template("/skills/{skill_id}/versions/{version}/content", skill_id=skill_id, version=version), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class AsyncContent(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncContentWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncContentWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncContentWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncContentWithStreamingResponse(self) + + async def retrieve( + self, + version: str, + *, + skill_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Download a skill version zip bundle. + + Args: + version: The skill version number. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + if not version: + raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return await self._get( + path_template("/skills/{skill_id}/versions/{version}/content", skill_id=skill_id, version=version), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class ContentWithRawResponse: + def __init__(self, content: Content) -> None: + self._content = content + + self.retrieve = _legacy_response.to_raw_response_wrapper( + content.retrieve, + ) + + +class AsyncContentWithRawResponse: + def __init__(self, content: AsyncContent) -> None: + self._content = content + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + content.retrieve, + ) + + +class ContentWithStreamingResponse: + def __init__(self, content: Content) -> None: + self._content = content + + self.retrieve = to_custom_streamed_response_wrapper( + content.retrieve, + StreamedBinaryAPIResponse, + ) + + +class AsyncContentWithStreamingResponse: + def __init__(self, content: AsyncContent) -> None: + self._content = content + + self.retrieve = async_to_custom_streamed_response_wrapper( + content.retrieve, + AsyncStreamedBinaryAPIResponse, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/skills/versions/versions.py b/venv/lib/python3.12/site-packages/openai/resources/skills/versions/versions.py new file mode 100644 index 0000000..b4efd79 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/skills/versions/versions.py @@ -0,0 +1,573 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Mapping, cast +from typing_extensions import Literal + +import httpx2 + +from .... import _legacy_response +from .content import ( + Content, + AsyncContent, + ContentWithRawResponse, + AsyncContentWithRawResponse, + ContentWithStreamingResponse, + AsyncContentWithStreamingResponse, +) +from ...._files import deepcopy_with_paths +from ...._types import ( + Body, + Omit, + Query, + Headers, + NotGiven, + FileTypes, + SequenceNotStr, + omit, + not_given, +) +from ...._utils import extract_files, path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.skills import version_list_params, version_create_params +from ....types.skills.skill_version import SkillVersion +from ....types.skills.deleted_skill_version import DeletedSkillVersion + +__all__ = ["Versions", "AsyncVersions"] + + +class Versions(SyncAPIResource): + @cached_property + def content(self) -> Content: + return Content(self._client) + + @cached_property + def with_raw_response(self) -> VersionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return VersionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> VersionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return VersionsWithStreamingResponse(self) + + def create( + self, + skill_id: str, + *, + default: bool | Omit = omit, + files: Union[SequenceNotStr[FileTypes], FileTypes] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SkillVersion: + """ + Create a new immutable skill version. + + Args: + default: Whether to set this version as the default. + + files: Skill files to upload (directory upload) or a single zip file. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + body = deepcopy_with_paths( + { + "default": default, + "files": files, + }, + [["files", ""], ["files"]], + ) + extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""], ["files"]]) + if extracted_files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + path_template("/skills/{skill_id}/versions", skill_id=skill_id), + body=maybe_transform(body, version_create_params.VersionCreateParams), + files=extracted_files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SkillVersion, + ) + + def retrieve( + self, + version: str, + *, + skill_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SkillVersion: + """ + Get a specific skill version. + + Args: + version: The version number to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + if not version: + raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") + return self._get( + path_template("/skills/{skill_id}/versions/{version}", skill_id=skill_id, version=version), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SkillVersion, + ) + + def list( + self, + skill_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[SkillVersion]: + """ + List skill versions for a skill. + + Args: + after: The skill version ID to start after. + + limit: Number of versions to retrieve. + + order: Sort order of results by version number. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return self._get_api_list( + path_template("/skills/{skill_id}/versions", skill_id=skill_id), + page=SyncCursorPage[SkillVersion], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + version_list_params.VersionListParams, + ), + security={"bearer_auth": True}, + ), + model=SkillVersion, + ) + + def delete( + self, + version: str, + *, + skill_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> DeletedSkillVersion: + """ + Delete a skill version. + + Args: + version: The skill version number. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + if not version: + raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") + return self._delete( + path_template("/skills/{skill_id}/versions/{version}", skill_id=skill_id, version=version), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=DeletedSkillVersion, + ) + + +class AsyncVersions(AsyncAPIResource): + @cached_property + def content(self) -> AsyncContent: + return AsyncContent(self._client) + + @cached_property + def with_raw_response(self) -> AsyncVersionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncVersionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncVersionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncVersionsWithStreamingResponse(self) + + async def create( + self, + skill_id: str, + *, + default: bool | Omit = omit, + files: Union[SequenceNotStr[FileTypes], FileTypes] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SkillVersion: + """ + Create a new immutable skill version. + + Args: + default: Whether to set this version as the default. + + files: Skill files to upload (directory upload) or a single zip file. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + body = deepcopy_with_paths( + { + "default": default, + "files": files, + }, + [["files", ""], ["files"]], + ) + extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""], ["files"]]) + if extracted_files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + path_template("/skills/{skill_id}/versions", skill_id=skill_id), + body=await async_maybe_transform(body, version_create_params.VersionCreateParams), + files=extracted_files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SkillVersion, + ) + + async def retrieve( + self, + version: str, + *, + skill_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SkillVersion: + """ + Get a specific skill version. + + Args: + version: The version number to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + if not version: + raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") + return await self._get( + path_template("/skills/{skill_id}/versions/{version}", skill_id=skill_id, version=version), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=SkillVersion, + ) + + def list( + self, + skill_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[SkillVersion, AsyncCursorPage[SkillVersion]]: + """ + List skill versions for a skill. + + Args: + after: The skill version ID to start after. + + limit: Number of versions to retrieve. + + order: Sort order of results by version number. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return self._get_api_list( + path_template("/skills/{skill_id}/versions", skill_id=skill_id), + page=AsyncCursorPage[SkillVersion], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + version_list_params.VersionListParams, + ), + security={"bearer_auth": True}, + ), + model=SkillVersion, + ) + + async def delete( + self, + version: str, + *, + skill_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> DeletedSkillVersion: + """ + Delete a skill version. + + Args: + version: The skill version number. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + if not version: + raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") + return await self._delete( + path_template("/skills/{skill_id}/versions/{version}", skill_id=skill_id, version=version), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=DeletedSkillVersion, + ) + + +class VersionsWithRawResponse: + def __init__(self, versions: Versions) -> None: + self._versions = versions + + self.create = _legacy_response.to_raw_response_wrapper( + versions.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + versions.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + versions.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + versions.delete, + ) + + @cached_property + def content(self) -> ContentWithRawResponse: + return ContentWithRawResponse(self._versions.content) + + +class AsyncVersionsWithRawResponse: + def __init__(self, versions: AsyncVersions) -> None: + self._versions = versions + + self.create = _legacy_response.async_to_raw_response_wrapper( + versions.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + versions.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + versions.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + versions.delete, + ) + + @cached_property + def content(self) -> AsyncContentWithRawResponse: + return AsyncContentWithRawResponse(self._versions.content) + + +class VersionsWithStreamingResponse: + def __init__(self, versions: Versions) -> None: + self._versions = versions + + self.create = to_streamed_response_wrapper( + versions.create, + ) + self.retrieve = to_streamed_response_wrapper( + versions.retrieve, + ) + self.list = to_streamed_response_wrapper( + versions.list, + ) + self.delete = to_streamed_response_wrapper( + versions.delete, + ) + + @cached_property + def content(self) -> ContentWithStreamingResponse: + return ContentWithStreamingResponse(self._versions.content) + + +class AsyncVersionsWithStreamingResponse: + def __init__(self, versions: AsyncVersions) -> None: + self._versions = versions + + self.create = async_to_streamed_response_wrapper( + versions.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + versions.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + versions.list, + ) + self.delete = async_to_streamed_response_wrapper( + versions.delete, + ) + + @cached_property + def content(self) -> AsyncContentWithStreamingResponse: + return AsyncContentWithStreamingResponse(self._versions.content) diff --git a/venv/lib/python3.12/site-packages/openai/resources/uploads/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/uploads/__init__.py new file mode 100644 index 0000000..2685efc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/uploads/__init__.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .parts import ( + Parts as Parts, + AsyncParts as AsyncParts, + PartsWithRawResponse as PartsWithRawResponse, + AsyncPartsWithRawResponse as AsyncPartsWithRawResponse, + PartsWithStreamingResponse as PartsWithStreamingResponse, + AsyncPartsWithStreamingResponse as AsyncPartsWithStreamingResponse, + ) + from .uploads import ( + Uploads as Uploads, + AsyncUploads as AsyncUploads, + UploadsWithRawResponse as UploadsWithRawResponse, + AsyncUploadsWithRawResponse as AsyncUploadsWithRawResponse, + UploadsWithStreamingResponse as UploadsWithStreamingResponse, + AsyncUploadsWithStreamingResponse as AsyncUploadsWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Parts": (".parts", "Parts"), + "AsyncParts": (".parts", "AsyncParts"), + "PartsWithRawResponse": (".parts", "PartsWithRawResponse"), + "AsyncPartsWithRawResponse": (".parts", "AsyncPartsWithRawResponse"), + "PartsWithStreamingResponse": (".parts", "PartsWithStreamingResponse"), + "AsyncPartsWithStreamingResponse": (".parts", "AsyncPartsWithStreamingResponse"), + "Uploads": (".uploads", "Uploads"), + "AsyncUploads": (".uploads", "AsyncUploads"), + "UploadsWithRawResponse": (".uploads", "UploadsWithRawResponse"), + "AsyncUploadsWithRawResponse": (".uploads", "AsyncUploadsWithRawResponse"), + "UploadsWithStreamingResponse": (".uploads", "UploadsWithStreamingResponse"), + "AsyncUploadsWithStreamingResponse": (".uploads", "AsyncUploadsWithStreamingResponse"), + } + _SUBMODULES = { + "parts", + "uploads", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Parts", + "AsyncParts", + "PartsWithRawResponse", + "AsyncPartsWithRawResponse", + "PartsWithStreamingResponse", + "AsyncPartsWithStreamingResponse", + "Uploads", + "AsyncUploads", + "UploadsWithRawResponse", + "AsyncUploadsWithRawResponse", + "UploadsWithStreamingResponse", + "AsyncUploadsWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/uploads/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/uploads/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6702830 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/uploads/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/uploads/__pycache__/parts.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/uploads/__pycache__/parts.cpython-312.pyc new file mode 100644 index 0000000..ff45751 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/uploads/__pycache__/parts.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/uploads/__pycache__/uploads.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/uploads/__pycache__/uploads.cpython-312.pyc new file mode 100644 index 0000000..d2358b7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/uploads/__pycache__/uploads.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/uploads/parts.py b/venv/lib/python3.12/site-packages/openai/resources/uploads/parts.py new file mode 100644 index 0000000..5dbfcdd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/uploads/parts.py @@ -0,0 +1,220 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Mapping, cast + +import httpx2 + +from ... import _legacy_response +from ..._files import deepcopy_with_paths +from ..._types import Body, Query, Headers, NotGiven, FileTypes, not_given +from ..._utils import extract_files, path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.uploads import part_create_params +from ...types.uploads.upload_part import UploadPart + +__all__ = ["Parts", "AsyncParts"] + + +class Parts(SyncAPIResource): + """Use Uploads to upload large files in multiple parts.""" + + @cached_property + def with_raw_response(self) -> PartsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return PartsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PartsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return PartsWithStreamingResponse(self) + + def create( + self, + upload_id: str, + *, + data: FileTypes, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UploadPart: + """ + Adds a + [Part](https://developers.openai.com/api/reference/resources/uploads/subresources/parts) + to an [Upload](https://developers.openai.com/api/reference/resources/uploads) + object. A Part represents a chunk of bytes from the file you are trying to + upload. + + Each Part can be at most 64 MB, and you can add Parts until you hit the Upload + maximum of 8 GB. + + It is possible to add multiple Parts in parallel. You can decide the intended + order of the Parts when you + [complete the Upload](https://developers.openai.com/api/reference/resources/uploads/methods/complete). + + Args: + data: The chunk of bytes for this Part. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not upload_id: + raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") + body = deepcopy_with_paths({"data": data}, [["data"]]) + files = extract_files(cast(Mapping[str, object], body), paths=[["data"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + path_template("/uploads/{upload_id}/parts", upload_id=upload_id), + body=maybe_transform(body, part_create_params.PartCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=UploadPart, + ) + + +class AsyncParts(AsyncAPIResource): + """Use Uploads to upload large files in multiple parts.""" + + @cached_property + def with_raw_response(self) -> AsyncPartsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncPartsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPartsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncPartsWithStreamingResponse(self) + + async def create( + self, + upload_id: str, + *, + data: FileTypes, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> UploadPart: + """ + Adds a + [Part](https://developers.openai.com/api/reference/resources/uploads/subresources/parts) + to an [Upload](https://developers.openai.com/api/reference/resources/uploads) + object. A Part represents a chunk of bytes from the file you are trying to + upload. + + Each Part can be at most 64 MB, and you can add Parts until you hit the Upload + maximum of 8 GB. + + It is possible to add multiple Parts in parallel. You can decide the intended + order of the Parts when you + [complete the Upload](https://developers.openai.com/api/reference/resources/uploads/methods/complete). + + Args: + data: The chunk of bytes for this Part. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not upload_id: + raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") + body = deepcopy_with_paths({"data": data}, [["data"]]) + files = extract_files(cast(Mapping[str, object], body), paths=[["data"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + path_template("/uploads/{upload_id}/parts", upload_id=upload_id), + body=await async_maybe_transform(body, part_create_params.PartCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=UploadPart, + ) + + +class PartsWithRawResponse: + def __init__(self, parts: Parts) -> None: + self._parts = parts + + self.create = _legacy_response.to_raw_response_wrapper( + parts.create, + ) + + +class AsyncPartsWithRawResponse: + def __init__(self, parts: AsyncParts) -> None: + self._parts = parts + + self.create = _legacy_response.async_to_raw_response_wrapper( + parts.create, + ) + + +class PartsWithStreamingResponse: + def __init__(self, parts: Parts) -> None: + self._parts = parts + + self.create = to_streamed_response_wrapper( + parts.create, + ) + + +class AsyncPartsWithStreamingResponse: + def __init__(self, parts: AsyncParts) -> None: + self._parts = parts + + self.create = async_to_streamed_response_wrapper( + parts.create, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/uploads/uploads.py b/venv/lib/python3.12/site-packages/openai/resources/uploads/uploads.py new file mode 100644 index 0000000..6715388 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/uploads/uploads.py @@ -0,0 +1,765 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import io +import os +import logging +import builtins +from typing import overload +from pathlib import Path + +import anyio +import httpx2 + +from ... import _legacy_response +from .parts import ( + Parts, + AsyncParts, + PartsWithRawResponse, + AsyncPartsWithRawResponse, + PartsWithStreamingResponse, + AsyncPartsWithStreamingResponse, +) +from ...types import FilePurpose, upload_create_params, upload_complete_params +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.upload import Upload +from ...types.file_purpose import FilePurpose + +__all__ = ["Uploads", "AsyncUploads"] + + +# 64MB +DEFAULT_PART_SIZE = 64 * 1024 * 1024 + +log: logging.Logger = logging.getLogger(__name__) + + +class Uploads(SyncAPIResource): + """Use Uploads to upload large files in multiple parts.""" + + @cached_property + def parts(self) -> Parts: + """Use Uploads to upload large files in multiple parts.""" + return Parts(self._client) + + @cached_property + def with_raw_response(self) -> UploadsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return UploadsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UploadsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return UploadsWithStreamingResponse(self) + + @overload + def upload_file_chunked( + self, + *, + file: os.PathLike[str], + mime_type: str, + purpose: FilePurpose, + bytes: int | None = None, + part_size: int | None = None, + md5: str | Omit = omit, + ) -> Upload: + """Splits a file into multiple 64MB parts and uploads them sequentially.""" + + @overload + def upload_file_chunked( + self, + *, + file: bytes, + filename: str, + bytes: int, + mime_type: str, + purpose: FilePurpose, + part_size: int | None = None, + md5: str | Omit = omit, + ) -> Upload: + """Splits an in-memory file into multiple 64MB parts and uploads them sequentially.""" + + def upload_file_chunked( + self, + *, + file: os.PathLike[str] | bytes, + mime_type: str, + purpose: FilePurpose, + filename: str | None = None, + bytes: int | None = None, + part_size: int | None = None, + md5: str | Omit = omit, + ) -> Upload: + """Splits the given file into multiple parts and uploads them sequentially. + + ```py + from pathlib import Path + + client.uploads.upload_file( + file=Path("my-paper.pdf"), + mime_type="pdf", + purpose="assistants", + ) + ``` + """ + if isinstance(file, builtins.bytes): + if filename is None: + raise TypeError("The `filename` argument must be given for in-memory files") + + if bytes is None: + raise TypeError("The `bytes` argument must be given for in-memory files") + else: + if not isinstance(file, Path): + file = Path(file) + + if not filename: + filename = file.name + + if bytes is None: + bytes = file.stat().st_size + + upload = self.create( + bytes=bytes, + filename=filename, + mime_type=mime_type, + purpose=purpose, + ) + + part_ids: list[str] = [] + + if part_size is None: + part_size = DEFAULT_PART_SIZE + + if isinstance(file, builtins.bytes): + buf: io.FileIO | io.BytesIO = io.BytesIO(file) + else: + buf = io.FileIO(file) + + try: + while True: + data = buf.read(part_size) + if not data: + # EOF + break + + part = self.parts.create(upload_id=upload.id, data=data) + log.info("Uploaded part %s for upload %s", part.id, upload.id) + part_ids.append(part.id) + finally: + buf.close() + + return self.complete(upload_id=upload.id, part_ids=part_ids, md5=md5) + + def create( + self, + *, + bytes: int, + filename: str, + mime_type: str, + purpose: FilePurpose, + expires_after: upload_create_params.ExpiresAfter | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Upload: + """ + Creates an intermediate + [Upload](https://developers.openai.com/api/reference/resources/uploads) object + that you can add + [Parts](https://developers.openai.com/api/reference/resources/uploads/subresources/parts) + to. Currently, an Upload can accept at most 8 GB in total and expires after an + hour after you create it. + + Once you complete the Upload, we will create a + [File](https://developers.openai.com/api/reference/resources/files) object that + contains all the parts you uploaded. This File is usable in the rest of our + platform as a regular File object. + + For certain `purpose` values, the correct `mime_type` must be specified. Please + refer to documentation for the + [supported MIME types for your use case](https://developers.openai.com/api/docs/guides/tools-file-search#supported-files). + + For guidance on the proper filename extensions for each purpose, please follow + the documentation on + [creating a File](https://developers.openai.com/api/reference/resources/files/methods/create). + + Returns the Upload object with status `pending`. + + Args: + bytes: The number of bytes in the file you are uploading. + + filename: The name of the file to upload. + + mime_type: The MIME type of the file. + + This must fall within the supported MIME types for your file purpose. See the + supported MIME types for assistants and vision. + + purpose: The intended purpose of the uploaded file. + + See the + [documentation on File purposes](https://developers.openai.com/api/reference/resources/files/methods/create#%28resource%29%20files%20%3E%20%28method%29%20create%20%3E%20%28params%29%200%20%3E%20%28param%29%20purpose%20%3E%20%28schema%29). + + expires_after: The expiration policy for a file. By default, files with `purpose=batch` expire + after 30 days and all other files are persisted until they are manually deleted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/uploads", + body=maybe_transform( + { + "bytes": bytes, + "filename": filename, + "mime_type": mime_type, + "purpose": purpose, + "expires_after": expires_after, + }, + upload_create_params.UploadCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Upload, + ) + + def cancel( + self, + upload_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Upload: + """Cancels the Upload. + + No Parts may be added after an Upload is cancelled. + + Returns the Upload object with status `cancelled`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not upload_id: + raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") + return self._post( + path_template("/uploads/{upload_id}/cancel", upload_id=upload_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Upload, + ) + + def complete( + self, + upload_id: str, + *, + part_ids: SequenceNotStr[str], + md5: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Upload: + """ + Completes the + [Upload](https://developers.openai.com/api/reference/resources/uploads). + + Within the returned Upload object, there is a nested + [File](https://developers.openai.com/api/reference/resources/files) object that + is ready to use in the rest of the platform. + + You can specify the order of the Parts by passing in an ordered list of the Part + IDs. + + The number of bytes uploaded upon completion must match the number of bytes + initially specified when creating the Upload object. No Parts may be added after + an Upload is completed. Returns the Upload object with status `completed`, + including an additional `file` property containing the created usable File + object. + + Args: + part_ids: The ordered list of Part IDs. + + md5: The optional md5 checksum for the file contents to verify if the bytes uploaded + matches what you expect. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not upload_id: + raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") + return self._post( + path_template("/uploads/{upload_id}/complete", upload_id=upload_id), + body=maybe_transform( + { + "part_ids": part_ids, + "md5": md5, + }, + upload_complete_params.UploadCompleteParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Upload, + ) + + +class AsyncUploads(AsyncAPIResource): + """Use Uploads to upload large files in multiple parts.""" + + @cached_property + def parts(self) -> AsyncParts: + """Use Uploads to upload large files in multiple parts.""" + return AsyncParts(self._client) + + @cached_property + def with_raw_response(self) -> AsyncUploadsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncUploadsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUploadsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncUploadsWithStreamingResponse(self) + + @overload + async def upload_file_chunked( + self, + *, + file: os.PathLike[str], + mime_type: str, + purpose: FilePurpose, + bytes: int | None = None, + part_size: int | None = None, + md5: str | Omit = omit, + ) -> Upload: + """Splits a file into multiple 64MB parts and uploads them sequentially.""" + + @overload + async def upload_file_chunked( + self, + *, + file: bytes, + filename: str, + bytes: int, + mime_type: str, + purpose: FilePurpose, + part_size: int | None = None, + md5: str | Omit = omit, + ) -> Upload: + """Splits an in-memory file into multiple 64MB parts and uploads them sequentially.""" + + async def upload_file_chunked( + self, + *, + file: os.PathLike[str] | bytes, + mime_type: str, + purpose: FilePurpose, + filename: str | None = None, + bytes: int | None = None, + part_size: int | None = None, + md5: str | Omit = omit, + ) -> Upload: + """Splits the given file into multiple parts and uploads them sequentially. + + ```py + from pathlib import Path + + client.uploads.upload_file( + file=Path("my-paper.pdf"), + mime_type="pdf", + purpose="assistants", + ) + ``` + """ + if isinstance(file, builtins.bytes): + if filename is None: + raise TypeError("The `filename` argument must be given for in-memory files") + + if bytes is None: + raise TypeError("The `bytes` argument must be given for in-memory files") + else: + if not isinstance(file, anyio.Path): + file = anyio.Path(file) + + if not filename: + filename = file.name + + if bytes is None: + stat = await file.stat() + bytes = stat.st_size + + upload = await self.create( + bytes=bytes, + filename=filename, + mime_type=mime_type, + purpose=purpose, + ) + + part_ids: list[str] = [] + + if part_size is None: + part_size = DEFAULT_PART_SIZE + + if isinstance(file, anyio.Path): + fd = await file.open("rb") + async with fd: + while True: + data = await fd.read(part_size) + if not data: + # EOF + break + + part = await self.parts.create(upload_id=upload.id, data=data) + log.info("Uploaded part %s for upload %s", part.id, upload.id) + part_ids.append(part.id) + else: + buf = io.BytesIO(file) + + try: + while True: + data = buf.read(part_size) + if not data: + # EOF + break + + part = await self.parts.create(upload_id=upload.id, data=data) + log.info("Uploaded part %s for upload %s", part.id, upload.id) + part_ids.append(part.id) + finally: + buf.close() + + return await self.complete(upload_id=upload.id, part_ids=part_ids, md5=md5) + + async def create( + self, + *, + bytes: int, + filename: str, + mime_type: str, + purpose: FilePurpose, + expires_after: upload_create_params.ExpiresAfter | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Upload: + """ + Creates an intermediate + [Upload](https://developers.openai.com/api/reference/resources/uploads) object + that you can add + [Parts](https://developers.openai.com/api/reference/resources/uploads/subresources/parts) + to. Currently, an Upload can accept at most 8 GB in total and expires after an + hour after you create it. + + Once you complete the Upload, we will create a + [File](https://developers.openai.com/api/reference/resources/files) object that + contains all the parts you uploaded. This File is usable in the rest of our + platform as a regular File object. + + For certain `purpose` values, the correct `mime_type` must be specified. Please + refer to documentation for the + [supported MIME types for your use case](https://developers.openai.com/api/docs/guides/tools-file-search#supported-files). + + For guidance on the proper filename extensions for each purpose, please follow + the documentation on + [creating a File](https://developers.openai.com/api/reference/resources/files/methods/create). + + Returns the Upload object with status `pending`. + + Args: + bytes: The number of bytes in the file you are uploading. + + filename: The name of the file to upload. + + mime_type: The MIME type of the file. + + This must fall within the supported MIME types for your file purpose. See the + supported MIME types for assistants and vision. + + purpose: The intended purpose of the uploaded file. + + See the + [documentation on File purposes](https://developers.openai.com/api/reference/resources/files/methods/create#%28resource%29%20files%20%3E%20%28method%29%20create%20%3E%20%28params%29%200%20%3E%20%28param%29%20purpose%20%3E%20%28schema%29). + + expires_after: The expiration policy for a file. By default, files with `purpose=batch` expire + after 30 days and all other files are persisted until they are manually deleted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/uploads", + body=await async_maybe_transform( + { + "bytes": bytes, + "filename": filename, + "mime_type": mime_type, + "purpose": purpose, + "expires_after": expires_after, + }, + upload_create_params.UploadCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Upload, + ) + + async def cancel( + self, + upload_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Upload: + """Cancels the Upload. + + No Parts may be added after an Upload is cancelled. + + Returns the Upload object with status `cancelled`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not upload_id: + raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") + return await self._post( + path_template("/uploads/{upload_id}/cancel", upload_id=upload_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Upload, + ) + + async def complete( + self, + upload_id: str, + *, + part_ids: SequenceNotStr[str], + md5: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Upload: + """ + Completes the + [Upload](https://developers.openai.com/api/reference/resources/uploads). + + Within the returned Upload object, there is a nested + [File](https://developers.openai.com/api/reference/resources/files) object that + is ready to use in the rest of the platform. + + You can specify the order of the Parts by passing in an ordered list of the Part + IDs. + + The number of bytes uploaded upon completion must match the number of bytes + initially specified when creating the Upload object. No Parts may be added after + an Upload is completed. Returns the Upload object with status `completed`, + including an additional `file` property containing the created usable File + object. + + Args: + part_ids: The ordered list of Part IDs. + + md5: The optional md5 checksum for the file contents to verify if the bytes uploaded + matches what you expect. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not upload_id: + raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") + return await self._post( + path_template("/uploads/{upload_id}/complete", upload_id=upload_id), + body=await async_maybe_transform( + { + "part_ids": part_ids, + "md5": md5, + }, + upload_complete_params.UploadCompleteParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Upload, + ) + + +class UploadsWithRawResponse: + def __init__(self, uploads: Uploads) -> None: + self._uploads = uploads + + self.create = _legacy_response.to_raw_response_wrapper( + uploads.create, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + uploads.cancel, + ) + self.complete = _legacy_response.to_raw_response_wrapper( + uploads.complete, + ) + + @cached_property + def parts(self) -> PartsWithRawResponse: + """Use Uploads to upload large files in multiple parts.""" + return PartsWithRawResponse(self._uploads.parts) + + +class AsyncUploadsWithRawResponse: + def __init__(self, uploads: AsyncUploads) -> None: + self._uploads = uploads + + self.create = _legacy_response.async_to_raw_response_wrapper( + uploads.create, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + uploads.cancel, + ) + self.complete = _legacy_response.async_to_raw_response_wrapper( + uploads.complete, + ) + + @cached_property + def parts(self) -> AsyncPartsWithRawResponse: + """Use Uploads to upload large files in multiple parts.""" + return AsyncPartsWithRawResponse(self._uploads.parts) + + +class UploadsWithStreamingResponse: + def __init__(self, uploads: Uploads) -> None: + self._uploads = uploads + + self.create = to_streamed_response_wrapper( + uploads.create, + ) + self.cancel = to_streamed_response_wrapper( + uploads.cancel, + ) + self.complete = to_streamed_response_wrapper( + uploads.complete, + ) + + @cached_property + def parts(self) -> PartsWithStreamingResponse: + """Use Uploads to upload large files in multiple parts.""" + return PartsWithStreamingResponse(self._uploads.parts) + + +class AsyncUploadsWithStreamingResponse: + def __init__(self, uploads: AsyncUploads) -> None: + self._uploads = uploads + + self.create = async_to_streamed_response_wrapper( + uploads.create, + ) + self.cancel = async_to_streamed_response_wrapper( + uploads.cancel, + ) + self.complete = async_to_streamed_response_wrapper( + uploads.complete, + ) + + @cached_property + def parts(self) -> AsyncPartsWithStreamingResponse: + """Use Uploads to upload large files in multiple parts.""" + return AsyncPartsWithStreamingResponse(self._uploads.parts) diff --git a/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__init__.py new file mode 100644 index 0000000..f371aaf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__init__.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import typing as _t + +if _t.TYPE_CHECKING: + from .files import ( + Files as Files, + AsyncFiles as AsyncFiles, + FilesWithRawResponse as FilesWithRawResponse, + AsyncFilesWithRawResponse as AsyncFilesWithRawResponse, + FilesWithStreamingResponse as FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse as AsyncFilesWithStreamingResponse, + ) + from .file_batches import ( + FileBatches as FileBatches, + AsyncFileBatches as AsyncFileBatches, + FileBatchesWithRawResponse as FileBatchesWithRawResponse, + AsyncFileBatchesWithRawResponse as AsyncFileBatchesWithRawResponse, + FileBatchesWithStreamingResponse as FileBatchesWithStreamingResponse, + AsyncFileBatchesWithStreamingResponse as AsyncFileBatchesWithStreamingResponse, + ) + from .vector_stores import ( + VectorStores as VectorStores, + AsyncVectorStores as AsyncVectorStores, + VectorStoresWithRawResponse as VectorStoresWithRawResponse, + AsyncVectorStoresWithRawResponse as AsyncVectorStoresWithRawResponse, + VectorStoresWithStreamingResponse as VectorStoresWithStreamingResponse, + AsyncVectorStoresWithStreamingResponse as AsyncVectorStoresWithStreamingResponse, + ) + +else: + _EXPORTS = { + "Files": (".files", "Files"), + "AsyncFiles": (".files", "AsyncFiles"), + "FilesWithRawResponse": (".files", "FilesWithRawResponse"), + "AsyncFilesWithRawResponse": (".files", "AsyncFilesWithRawResponse"), + "FilesWithStreamingResponse": (".files", "FilesWithStreamingResponse"), + "AsyncFilesWithStreamingResponse": (".files", "AsyncFilesWithStreamingResponse"), + "FileBatches": (".file_batches", "FileBatches"), + "AsyncFileBatches": (".file_batches", "AsyncFileBatches"), + "FileBatchesWithRawResponse": (".file_batches", "FileBatchesWithRawResponse"), + "AsyncFileBatchesWithRawResponse": (".file_batches", "AsyncFileBatchesWithRawResponse"), + "FileBatchesWithStreamingResponse": (".file_batches", "FileBatchesWithStreamingResponse"), + "AsyncFileBatchesWithStreamingResponse": (".file_batches", "AsyncFileBatchesWithStreamingResponse"), + "VectorStores": (".vector_stores", "VectorStores"), + "AsyncVectorStores": (".vector_stores", "AsyncVectorStores"), + "VectorStoresWithRawResponse": (".vector_stores", "VectorStoresWithRawResponse"), + "AsyncVectorStoresWithRawResponse": (".vector_stores", "AsyncVectorStoresWithRawResponse"), + "VectorStoresWithStreamingResponse": (".vector_stores", "VectorStoresWithStreamingResponse"), + "AsyncVectorStoresWithStreamingResponse": (".vector_stores", "AsyncVectorStoresWithStreamingResponse"), + } + _SUBMODULES = { + "file_batches", + "files", + "vector_stores", + } + + def __getattr__(name: str) -> _t.Any: + import importlib + + if name in _EXPORTS: + module_name, symbol_name = _EXPORTS[name] + value = getattr(importlib.import_module(module_name, __name__), symbol_name) + elif name in _SUBMODULES: + value = importlib.import_module(f".{name}", __name__) + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + globals()[name] = value + return value + + def __dir__() -> list[str]: + return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES) + + +__all__ = [ + "Files", + "AsyncFiles", + "FilesWithRawResponse", + "AsyncFilesWithRawResponse", + "FilesWithStreamingResponse", + "AsyncFilesWithStreamingResponse", + "FileBatches", + "AsyncFileBatches", + "FileBatchesWithRawResponse", + "AsyncFileBatchesWithRawResponse", + "FileBatchesWithStreamingResponse", + "AsyncFileBatchesWithStreamingResponse", + "VectorStores", + "AsyncVectorStores", + "VectorStoresWithRawResponse", + "AsyncVectorStoresWithRawResponse", + "VectorStoresWithStreamingResponse", + "AsyncVectorStoresWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1e2deb8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__pycache__/file_batches.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__pycache__/file_batches.cpython-312.pyc new file mode 100644 index 0000000..03c240d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__pycache__/file_batches.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__pycache__/files.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__pycache__/files.cpython-312.pyc new file mode 100644 index 0000000..015cbfe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__pycache__/files.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__pycache__/vector_stores.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__pycache__/vector_stores.cpython-312.pyc new file mode 100644 index 0000000..e6dde76 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/__pycache__/vector_stores.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/vector_stores/file_batches.py b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/file_batches.py new file mode 100644 index 0000000..8ba0201 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/file_batches.py @@ -0,0 +1,867 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import asyncio +from typing import Dict, Iterable, Optional +from typing_extensions import Union, Literal +from concurrent.futures import Future, ThreadPoolExecutor, as_completed + +import httpx2 +import sniffio + +from ... import _legacy_response +from ...types import FileChunkingStrategyParam +from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncCursorPage, AsyncCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.file_object import FileObject +from ...lib._vector_stores import ( + poll_vector_store_file_batch as _poll_vector_store_file_batch, + async_poll_vector_store_file_batch as _async_poll_vector_store_file_batch, +) +from ...types.vector_stores import file_batch_create_params, file_batch_list_files_params +from ...types.file_chunking_strategy_param import FileChunkingStrategyParam +from ...types.vector_stores.vector_store_file import VectorStoreFile +from ...types.vector_stores.vector_store_file_batch import VectorStoreFileBatch + +__all__ = ["FileBatches", "AsyncFileBatches"] + + +class FileBatches(SyncAPIResource): + @cached_property + def with_raw_response(self) -> FileBatchesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return FileBatchesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FileBatchesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return FileBatchesWithStreamingResponse(self) + + def create( + self, + vector_store_id: str, + *, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + files: Iterable[file_batch_create_params.File] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFileBatch: + """ + Create a vector store file batch. + + Args: + attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters, booleans, or numbers. + + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` + strategy. Only applicable if `file_ids` is non-empty. + + file_ids: A list of [File](https://developers.openai.com/api/reference/resources/files) + IDs that the vector store should use. Useful for tools like `file_search` that + can access files. If `attributes` or `chunking_strategy` are provided, they will + be applied to all files in the batch. The maximum batch size is 2000 files. This + endpoint is recommended for multi-file ingestion and helps reduce + per-vector-store write request pressure. Mutually exclusive with `files`. + + files: A list of objects that each include a `file_id` plus optional `attributes` or + `chunking_strategy`. Use this when you need to override metadata for specific + files. The global `attributes` or `chunking_strategy` will be ignored and must + be specified for each file. The maximum batch size is 2000 files. This endpoint + is recommended for multi-file ingestion and helps reduce per-vector-store write + request pressure. Mutually exclusive with `file_ids`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + path_template("/vector_stores/{vector_store_id}/file_batches", vector_store_id=vector_store_id), + body=maybe_transform( + { + "attributes": attributes, + "chunking_strategy": chunking_strategy, + "file_ids": file_ids, + "files": files, + }, + file_batch_create_params.FileBatchCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFileBatch, + ) + + def retrieve( + self, + batch_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFileBatch: + """ + Retrieves a vector store file batch. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get( + path_template( + "/vector_stores/{vector_store_id}/file_batches/{batch_id}", + vector_store_id=vector_store_id, + batch_id=batch_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFileBatch, + ) + + def cancel( + self, + batch_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFileBatch: + """Cancel a vector store file batch. + + This attempts to cancel the processing of + files in this batch as soon as possible. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + path_template( + "/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel", + vector_store_id=vector_store_id, + batch_id=batch_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFileBatch, + ) + + def create_and_poll( + self, + vector_store_id: str, + *, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + files: Iterable[file_batch_create_params.File] | Omit = omit, + poll_interval_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFileBatch: + """Create a vector store batch and poll until all files have been processed.""" + batch = self.create( + vector_store_id=vector_store_id, + attributes=attributes, + chunking_strategy=chunking_strategy, + file_ids=file_ids, + files=files, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + # TODO: don't poll unless necessary?? + return self.poll( + batch.id, + vector_store_id=vector_store_id, + poll_interval_ms=poll_interval_ms, + ) + + def list_files( + self, + batch_id: str, + *, + vector_store_id: str, + after: str | Omit = omit, + before: str | Omit = omit, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[VectorStoreFile]: + """ + Returns a list of vector store files in a batch. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + filter: Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template( + "/vector_stores/{vector_store_id}/file_batches/{batch_id}/files", + vector_store_id=vector_store_id, + batch_id=batch_id, + ), + page=SyncCursorPage[VectorStoreFile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "filter": filter, + "limit": limit, + "order": order, + }, + file_batch_list_files_params.FileBatchListFilesParams, + ), + security={"bearer_auth": True}, + ), + model=VectorStoreFile, + ) + + def poll( + self, + batch_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | Omit = omit, + ) -> VectorStoreFileBatch: + """Wait for the given file batch to be processed. + + Note: this will return even if one of the files failed to process, you need to + check batch.file_counts.failed_count to handle this case. + """ + return _poll_vector_store_file_batch( + self, + batch_id, + vector_store_id=vector_store_id, + poll_interval_ms=poll_interval_ms, + ) + + def upload_and_poll( + self, + vector_store_id: str, + *, + files: Iterable[FileTypes], + max_concurrency: int = 5, + file_ids: SequenceNotStr[str] = [], + poll_interval_ms: int | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + ) -> VectorStoreFileBatch: + """Uploads the given files concurrently and then creates a vector store file batch. + + If you've already uploaded certain files that you want to include in this batch + then you can pass their IDs through the `file_ids` argument. + + By default, if any file upload fails then an exception will be eagerly raised. + + The number of concurrency uploads is configurable using the `max_concurrency` + parameter. + + Note: this method only supports `asyncio` or `trio` as the backing async + runtime. + """ + results: list[FileObject] = [] + + with ThreadPoolExecutor(max_workers=max_concurrency) as executor: + futures: list[Future[FileObject]] = [ + executor.submit( + self._client.files.create, + file=file, + purpose="assistants", + ) + for file in files + ] + + for future in as_completed(futures): + exc = future.exception() + if exc: + raise exc + + results.append(future.result()) + + batch = self.create_and_poll( + vector_store_id=vector_store_id, + file_ids=[*file_ids, *(f.id for f in results)], + poll_interval_ms=poll_interval_ms, + chunking_strategy=chunking_strategy, + ) + return batch + + +class AsyncFileBatches(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncFileBatchesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncFileBatchesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFileBatchesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncFileBatchesWithStreamingResponse(self) + + async def create( + self, + vector_store_id: str, + *, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + files: Iterable[file_batch_create_params.File] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFileBatch: + """ + Create a vector store file batch. + + Args: + attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters, booleans, or numbers. + + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` + strategy. Only applicable if `file_ids` is non-empty. + + file_ids: A list of [File](https://developers.openai.com/api/reference/resources/files) + IDs that the vector store should use. Useful for tools like `file_search` that + can access files. If `attributes` or `chunking_strategy` are provided, they will + be applied to all files in the batch. The maximum batch size is 2000 files. This + endpoint is recommended for multi-file ingestion and helps reduce + per-vector-store write request pressure. Mutually exclusive with `files`. + + files: A list of objects that each include a `file_id` plus optional `attributes` or + `chunking_strategy`. Use this when you need to override metadata for specific + files. The global `attributes` or `chunking_strategy` will be ignored and must + be specified for each file. The maximum batch size is 2000 files. This endpoint + is recommended for multi-file ingestion and helps reduce per-vector-store write + request pressure. Mutually exclusive with `file_ids`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + path_template("/vector_stores/{vector_store_id}/file_batches", vector_store_id=vector_store_id), + body=await async_maybe_transform( + { + "attributes": attributes, + "chunking_strategy": chunking_strategy, + "file_ids": file_ids, + "files": files, + }, + file_batch_create_params.FileBatchCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFileBatch, + ) + + async def retrieve( + self, + batch_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFileBatch: + """ + Retrieves a vector store file batch. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._get( + path_template( + "/vector_stores/{vector_store_id}/file_batches/{batch_id}", + vector_store_id=vector_store_id, + batch_id=batch_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFileBatch, + ) + + async def cancel( + self, + batch_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFileBatch: + """Cancel a vector store file batch. + + This attempts to cancel the processing of + files in this batch as soon as possible. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + path_template( + "/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel", + vector_store_id=vector_store_id, + batch_id=batch_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFileBatch, + ) + + async def create_and_poll( + self, + vector_store_id: str, + *, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + files: Iterable[file_batch_create_params.File] | Omit = omit, + poll_interval_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFileBatch: + """Create a vector store batch and poll until all files have been processed.""" + batch = await self.create( + vector_store_id=vector_store_id, + attributes=attributes, + chunking_strategy=chunking_strategy, + file_ids=file_ids, + files=files, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + # TODO: don't poll unless necessary?? + return await self.poll( + batch.id, + vector_store_id=vector_store_id, + poll_interval_ms=poll_interval_ms, + ) + + def list_files( + self, + batch_id: str, + *, + vector_store_id: str, + after: str | Omit = omit, + before: str | Omit = omit, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[VectorStoreFile, AsyncCursorPage[VectorStoreFile]]: + """ + Returns a list of vector store files in a batch. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + filter: Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template( + "/vector_stores/{vector_store_id}/file_batches/{batch_id}/files", + vector_store_id=vector_store_id, + batch_id=batch_id, + ), + page=AsyncCursorPage[VectorStoreFile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "filter": filter, + "limit": limit, + "order": order, + }, + file_batch_list_files_params.FileBatchListFilesParams, + ), + security={"bearer_auth": True}, + ), + model=VectorStoreFile, + ) + + async def poll( + self, + batch_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | Omit = omit, + ) -> VectorStoreFileBatch: + """Wait for the given file batch to be processed. + + Note: this will return even if one of the files failed to process, you need to + check batch.file_counts.failed_count to handle this case. + """ + return await _async_poll_vector_store_file_batch( + self, + batch_id, + vector_store_id=vector_store_id, + poll_interval_ms=poll_interval_ms, + ) + + async def upload_and_poll( + self, + vector_store_id: str, + *, + files: Iterable[FileTypes], + max_concurrency: int = 5, + file_ids: SequenceNotStr[str] = [], + poll_interval_ms: int | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + ) -> VectorStoreFileBatch: + """Uploads the given files concurrently and then creates a vector store file batch. + + If you've already uploaded certain files that you want to include in this batch + then you can pass their IDs through the `file_ids` argument. + + By default, if any file upload fails then an exception will be eagerly raised. + + The number of concurrency uploads is configurable using the `max_concurrency` + parameter. + + Note: this method only supports `asyncio` or `trio` as the backing async + runtime. + """ + uploaded_files: list[FileObject] = [] + + async_library = sniffio.current_async_library() + + if async_library == "asyncio": + + async def asyncio_upload_file(semaphore: asyncio.Semaphore, file: FileTypes) -> None: + async with semaphore: + file_obj = await self._client.files.create( + file=file, + purpose="assistants", + ) + uploaded_files.append(file_obj) + + semaphore = asyncio.Semaphore(max_concurrency) + + tasks = [asyncio_upload_file(semaphore, file) for file in files] + + await asyncio.gather(*tasks) + elif async_library == "trio": + # We only import if the library is being used. + # We support Python 3.7 so are using an older version of trio that does not have type information + import trio # type: ignore # pyright: ignore[reportMissingTypeStubs] + + async def trio_upload_file(limiter: trio.CapacityLimiter, file: FileTypes) -> None: + async with limiter: + file_obj = await self._client.files.create( + file=file, + purpose="assistants", + ) + uploaded_files.append(file_obj) + + limiter = trio.CapacityLimiter(max_concurrency) + + async with trio.open_nursery() as nursery: + for file in files: + nursery.start_soon(trio_upload_file, limiter, file) # pyright: ignore [reportUnknownMemberType] + else: + raise RuntimeError( + f"Async runtime {async_library} is not supported yet. Only asyncio or trio is supported", + ) + + batch = await self.create_and_poll( + vector_store_id=vector_store_id, + file_ids=[*file_ids, *(f.id for f in uploaded_files)], + poll_interval_ms=poll_interval_ms, + chunking_strategy=chunking_strategy, + ) + return batch + + +class FileBatchesWithRawResponse: + def __init__(self, file_batches: FileBatches) -> None: + self._file_batches = file_batches + + self.create = _legacy_response.to_raw_response_wrapper( + file_batches.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + file_batches.retrieve, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + file_batches.cancel, + ) + self.list_files = _legacy_response.to_raw_response_wrapper( + file_batches.list_files, + ) + + +class AsyncFileBatchesWithRawResponse: + def __init__(self, file_batches: AsyncFileBatches) -> None: + self._file_batches = file_batches + + self.create = _legacy_response.async_to_raw_response_wrapper( + file_batches.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + file_batches.retrieve, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + file_batches.cancel, + ) + self.list_files = _legacy_response.async_to_raw_response_wrapper( + file_batches.list_files, + ) + + +class FileBatchesWithStreamingResponse: + def __init__(self, file_batches: FileBatches) -> None: + self._file_batches = file_batches + + self.create = to_streamed_response_wrapper( + file_batches.create, + ) + self.retrieve = to_streamed_response_wrapper( + file_batches.retrieve, + ) + self.cancel = to_streamed_response_wrapper( + file_batches.cancel, + ) + self.list_files = to_streamed_response_wrapper( + file_batches.list_files, + ) + + +class AsyncFileBatchesWithStreamingResponse: + def __init__(self, file_batches: AsyncFileBatches) -> None: + self._file_batches = file_batches + + self.create = async_to_streamed_response_wrapper( + file_batches.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + file_batches.retrieve, + ) + self.cancel = async_to_streamed_response_wrapper( + file_batches.cancel, + ) + self.list_files = async_to_streamed_response_wrapper( + file_batches.list_files, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/vector_stores/files.py b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/files.py new file mode 100644 index 0000000..4f18f96 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/files.py @@ -0,0 +1,1032 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Literal + +import httpx2 + +from ... import _legacy_response +from ...types import FileChunkingStrategyParam +from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncPage, AsyncPage, SyncCursorPage, AsyncCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...lib._vector_stores import ( + poll_vector_store_file as _poll_vector_store_file, + validate_max_wait_seconds as _validate_max_wait_seconds, + async_poll_vector_store_file as _async_poll_vector_store_file, +) +from ...types.vector_stores import file_list_params, file_create_params, file_update_params +from ...types.file_chunking_strategy_param import FileChunkingStrategyParam +from ...types.vector_stores.vector_store_file import VectorStoreFile +from ...types.vector_stores.file_content_response import FileContentResponse +from ...types.vector_stores.vector_store_file_deleted import VectorStoreFileDeleted + +__all__ = ["Files", "AsyncFiles"] + + +class Files(SyncAPIResource): + @cached_property + def with_raw_response(self) -> FilesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return FilesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FilesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return FilesWithStreamingResponse(self) + + def create( + self, + vector_store_id: str, + *, + file_id: str, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFile: + """ + Create a vector store file by attaching a + [File](https://developers.openai.com/api/reference/resources/files) to a + [vector store](https://developers.openai.com/api/reference/resources/vector_stores). + + Args: + file_id: A [File](https://developers.openai.com/api/reference/resources/files) ID that + the vector store should use. Useful for tools like `file_search` that can access + files. For multi-file ingestion, we recommend + [`file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create) + to minimize per-vector-store write requests. + + attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters, booleans, or numbers. + + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` + strategy. Only applicable if `file_ids` is non-empty. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + path_template("/vector_stores/{vector_store_id}/files", vector_store_id=vector_store_id), + body=maybe_transform( + { + "file_id": file_id, + "attributes": attributes, + "chunking_strategy": chunking_strategy, + }, + file_create_params.FileCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFile, + ) + + def retrieve( + self, + file_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFile: + """ + Retrieves a vector store file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get( + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFile, + ) + + def update( + self, + file_id: str, + *, + vector_store_id: str, + attributes: Optional[Dict[str, Union[str, float, bool]]], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFile: + """ + Update attributes on a vector store file. + + Args: + attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters, booleans, or numbers. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id + ), + body=maybe_transform({"attributes": attributes}, file_update_params.FileUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFile, + ) + + def list( + self, + vector_store_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[VectorStoreFile]: + """ + Returns a list of vector store files. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + filter: Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template("/vector_stores/{vector_store_id}/files", vector_store_id=vector_store_id), + page=SyncCursorPage[VectorStoreFile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "filter": filter, + "limit": limit, + "order": order, + }, + file_list_params.FileListParams, + ), + security={"bearer_auth": True}, + ), + model=VectorStoreFile, + ) + + def delete( + self, + file_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFileDeleted: + """Delete a vector store file. + + This will remove the file from the vector store but + the file itself will not be deleted. To delete the file, use the + [delete file](https://developers.openai.com/api/reference/resources/files/methods/delete) + endpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._delete( + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFileDeleted, + ) + + def create_and_poll( + self, + file_id: str, + *, + vector_store_id: str, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + poll_interval_ms: int | Omit = omit, + max_wait_seconds: float | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFile: + """Attach a file to the given vector store and wait for it to be processed. + + `max_wait_seconds` limits polling after attachment; see `poll` for details. + """ + _validate_max_wait_seconds(max_wait_seconds) + self.create( + vector_store_id=vector_store_id, + file_id=file_id, + chunking_strategy=chunking_strategy, + attributes=attributes, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + return self.poll( + file_id, + vector_store_id=vector_store_id, + poll_interval_ms=poll_interval_ms, + max_wait_seconds=max_wait_seconds, + ) + + def poll( + self, + file_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | Omit = omit, + max_wait_seconds: float | Omit = omit, + ) -> VectorStoreFile: + """Wait for the vector store file to finish processing. + + `max_wait_seconds` must be finite and non-negative. When provided, it limits + polling and raises `TimeoutError` on expiry; zero prevents any polling requests. + Omit it to wait indefinitely. In-flight requests and retries use the client's + HTTP timeout settings and may finish after the polling deadline. Timing out + does not cancel processing on the server. + + Note: this will return even if the file failed to process, you need to check + file.last_error and file.status to handle these cases + """ + return _poll_vector_store_file( + self, + file_id, + vector_store_id=vector_store_id, + poll_interval_ms=poll_interval_ms, + max_wait_seconds=max_wait_seconds, + ) + + def upload( + self, + *, + vector_store_id: str, + file: FileTypes, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + ) -> VectorStoreFile: + """Upload a file to the `files` API and then attach it to the given vector store. + + Note the file will be asynchronously processed (you can use the alternative + polling helper method to wait for processing to complete). + """ + file_obj = self._client.files.create(file=file, purpose="assistants") + return self.create(vector_store_id=vector_store_id, file_id=file_obj.id, chunking_strategy=chunking_strategy) + + def upload_and_poll( + self, + *, + vector_store_id: str, + file: FileTypes, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + poll_interval_ms: int | Omit = omit, + max_wait_seconds: float | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + ) -> VectorStoreFile: + """Add a file to a vector store and poll until processing is complete. + + `max_wait_seconds` limits polling after upload and attachment; see `poll` for details. + """ + _validate_max_wait_seconds(max_wait_seconds) + file_obj = self._client.files.create(file=file, purpose="assistants") + return self.create_and_poll( + vector_store_id=vector_store_id, + file_id=file_obj.id, + chunking_strategy=chunking_strategy, + poll_interval_ms=poll_interval_ms, + max_wait_seconds=max_wait_seconds, + attributes=attributes, + ) + + def content( + self, + file_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncPage[FileContentResponse]: + """ + Retrieve the parsed contents of a vector store file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}/content", + vector_store_id=vector_store_id, + file_id=file_id, + ), + page=SyncPage[FileContentResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + model=FileContentResponse, + ) + + +class AsyncFiles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncFilesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncFilesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFilesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncFilesWithStreamingResponse(self) + + async def create( + self, + vector_store_id: str, + *, + file_id: str, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFile: + """ + Create a vector store file by attaching a + [File](https://developers.openai.com/api/reference/resources/files) to a + [vector store](https://developers.openai.com/api/reference/resources/vector_stores). + + Args: + file_id: A [File](https://developers.openai.com/api/reference/resources/files) ID that + the vector store should use. Useful for tools like `file_search` that can access + files. For multi-file ingestion, we recommend + [`file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create) + to minimize per-vector-store write requests. + + attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters, booleans, or numbers. + + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` + strategy. Only applicable if `file_ids` is non-empty. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + path_template("/vector_stores/{vector_store_id}/files", vector_store_id=vector_store_id), + body=await async_maybe_transform( + { + "file_id": file_id, + "attributes": attributes, + "chunking_strategy": chunking_strategy, + }, + file_create_params.FileCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFile, + ) + + async def retrieve( + self, + file_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFile: + """ + Retrieves a vector store file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._get( + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFile, + ) + + async def update( + self, + file_id: str, + *, + vector_store_id: str, + attributes: Optional[Dict[str, Union[str, float, bool]]], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFile: + """ + Update attributes on a vector store file. + + Args: + attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters, booleans, or numbers. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id + ), + body=await async_maybe_transform({"attributes": attributes}, file_update_params.FileUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFile, + ) + + def list( + self, + vector_store_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[VectorStoreFile, AsyncCursorPage[VectorStoreFile]]: + """ + Returns a list of vector store files. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + filter: Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template("/vector_stores/{vector_store_id}/files", vector_store_id=vector_store_id), + page=AsyncCursorPage[VectorStoreFile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "filter": filter, + "limit": limit, + "order": order, + }, + file_list_params.FileListParams, + ), + security={"bearer_auth": True}, + ), + model=VectorStoreFile, + ) + + async def delete( + self, + file_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFileDeleted: + """Delete a vector store file. + + This will remove the file from the vector store but + the file itself will not be deleted. To delete the file, use the + [delete file](https://developers.openai.com/api/reference/resources/files/methods/delete) + endpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._delete( + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreFileDeleted, + ) + + async def create_and_poll( + self, + file_id: str, + *, + vector_store_id: str, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + poll_interval_ms: int | Omit = omit, + max_wait_seconds: float | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreFile: + """Attach a file to the given vector store and wait for it to be processed. + + `max_wait_seconds` limits polling after attachment; see `poll` for details. + """ + _validate_max_wait_seconds(max_wait_seconds) + await self.create( + vector_store_id=vector_store_id, + file_id=file_id, + chunking_strategy=chunking_strategy, + attributes=attributes, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + return await self.poll( + file_id, + vector_store_id=vector_store_id, + poll_interval_ms=poll_interval_ms, + max_wait_seconds=max_wait_seconds, + ) + + async def poll( + self, + file_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | Omit = omit, + max_wait_seconds: float | Omit = omit, + ) -> VectorStoreFile: + """Wait for the vector store file to finish processing. + + `max_wait_seconds` must be finite and non-negative. When provided, it limits + polling and raises `TimeoutError` on expiry; zero prevents any polling requests. + Omit it to wait indefinitely. In-flight requests and retries use the client's + HTTP timeout settings and may finish after the polling deadline. Timing out + does not cancel processing on the server. + + Note: this will return even if the file failed to process, you need to check + file.last_error and file.status to handle these cases + """ + return await _async_poll_vector_store_file( + self, + file_id, + vector_store_id=vector_store_id, + poll_interval_ms=poll_interval_ms, + max_wait_seconds=max_wait_seconds, + ) + + async def upload( + self, + *, + vector_store_id: str, + file: FileTypes, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + ) -> VectorStoreFile: + """Upload a file to the `files` API and then attach it to the given vector store. + + Note the file will be asynchronously processed (you can use the alternative + polling helper method to wait for processing to complete). + """ + file_obj = await self._client.files.create(file=file, purpose="assistants") + return await self.create( + vector_store_id=vector_store_id, file_id=file_obj.id, chunking_strategy=chunking_strategy + ) + + async def upload_and_poll( + self, + *, + vector_store_id: str, + file: FileTypes, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + poll_interval_ms: int | Omit = omit, + max_wait_seconds: float | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + ) -> VectorStoreFile: + """Add a file to a vector store and poll until processing is complete. + + `max_wait_seconds` limits polling after upload and attachment; see `poll` for details. + """ + _validate_max_wait_seconds(max_wait_seconds) + file_obj = await self._client.files.create(file=file, purpose="assistants") + return await self.create_and_poll( + vector_store_id=vector_store_id, + file_id=file_obj.id, + poll_interval_ms=poll_interval_ms, + max_wait_seconds=max_wait_seconds, + chunking_strategy=chunking_strategy, + attributes=attributes, + ) + + def content( + self, + file_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[FileContentResponse, AsyncPage[FileContentResponse]]: + """ + Retrieve the parsed contents of a vector store file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}/content", + vector_store_id=vector_store_id, + file_id=file_id, + ), + page=AsyncPage[FileContentResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + model=FileContentResponse, + ) + + +class FilesWithRawResponse: + def __init__(self, files: Files) -> None: + self._files = files + + self.create = _legacy_response.to_raw_response_wrapper( + files.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + files.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + files.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + files.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + files.delete, + ) + self.content = _legacy_response.to_raw_response_wrapper( + files.content, + ) + + +class AsyncFilesWithRawResponse: + def __init__(self, files: AsyncFiles) -> None: + self._files = files + + self.create = _legacy_response.async_to_raw_response_wrapper( + files.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + files.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + files.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + files.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + files.delete, + ) + self.content = _legacy_response.async_to_raw_response_wrapper( + files.content, + ) + + +class FilesWithStreamingResponse: + def __init__(self, files: Files) -> None: + self._files = files + + self.create = to_streamed_response_wrapper( + files.create, + ) + self.retrieve = to_streamed_response_wrapper( + files.retrieve, + ) + self.update = to_streamed_response_wrapper( + files.update, + ) + self.list = to_streamed_response_wrapper( + files.list, + ) + self.delete = to_streamed_response_wrapper( + files.delete, + ) + self.content = to_streamed_response_wrapper( + files.content, + ) + + +class AsyncFilesWithStreamingResponse: + def __init__(self, files: AsyncFiles) -> None: + self._files = files + + self.create = async_to_streamed_response_wrapper( + files.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + files.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + files.update, + ) + self.list = async_to_streamed_response_wrapper( + files.list, + ) + self.delete = async_to_streamed_response_wrapper( + files.delete, + ) + self.content = async_to_streamed_response_wrapper( + files.content, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/vector_stores/vector_stores.py b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/vector_stores.py new file mode 100644 index 0000000..8408af7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/vector_stores/vector_stores.py @@ -0,0 +1,917 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal + +import httpx2 + +from ... import _legacy_response +from .files import ( + Files, + AsyncFiles, + FilesWithRawResponse, + AsyncFilesWithRawResponse, + FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse, +) +from ...types import ( + FileChunkingStrategyParam, + vector_store_list_params, + vector_store_create_params, + vector_store_search_params, + vector_store_update_params, +) +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncPage, AsyncPage, SyncCursorPage, AsyncCursorPage +from .file_batches import ( + FileBatches, + AsyncFileBatches, + FileBatchesWithRawResponse, + AsyncFileBatchesWithRawResponse, + FileBatchesWithStreamingResponse, + AsyncFileBatchesWithStreamingResponse, +) +from ..._base_client import AsyncPaginator, make_request_options +from ...types.vector_store import VectorStore +from ...types.vector_store_deleted import VectorStoreDeleted +from ...types.shared_params.metadata import Metadata +from ...types.file_chunking_strategy_param import FileChunkingStrategyParam +from ...types.vector_store_search_response import VectorStoreSearchResponse + +__all__ = ["VectorStores", "AsyncVectorStores"] + + +class VectorStores(SyncAPIResource): + @cached_property + def files(self) -> Files: + return Files(self._client) + + @cached_property + def file_batches(self) -> FileBatches: + return FileBatches(self._client) + + @cached_property + def with_raw_response(self) -> VectorStoresWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return VectorStoresWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> VectorStoresWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return VectorStoresWithStreamingResponse(self) + + def create( + self, + *, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + description: str | Omit = omit, + expires_after: vector_store_create_params.ExpiresAfter | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStore: + """ + Create a vector store. + + Args: + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` + strategy. Only applicable if `file_ids` is non-empty. + + description: A description for the vector store. Can be used to describe the vector store's + purpose. + + expires_after: The expiration policy for a vector store. + + file_ids: A list of [File](https://developers.openai.com/api/reference/resources/files) + IDs that the vector store should use. Useful for tools like `file_search` that + can access files. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the vector store. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + "/vector_stores", + body=maybe_transform( + { + "chunking_strategy": chunking_strategy, + "description": description, + "expires_after": expires_after, + "file_ids": file_ids, + "metadata": metadata, + "name": name, + }, + vector_store_create_params.VectorStoreCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStore, + ) + + def retrieve( + self, + vector_store_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStore: + """ + Retrieves a vector store. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get( + path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStore, + ) + + def update( + self, + vector_store_id: str, + *, + expires_after: Optional[vector_store_update_params.ExpiresAfter] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStore: + """ + Modifies a vector store. + + Args: + expires_after: The expiration policy for a vector store. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the vector store. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), + body=maybe_transform( + { + "expires_after": expires_after, + "metadata": metadata, + "name": name, + }, + vector_store_update_params.VectorStoreUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStore, + ) + + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[VectorStore]: + """Returns a list of vector stores. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + "/vector_stores", + page=SyncCursorPage[VectorStore], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + vector_store_list_params.VectorStoreListParams, + ), + security={"bearer_auth": True}, + ), + model=VectorStore, + ) + + def delete( + self, + vector_store_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreDeleted: + """ + Delete a vector store. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._delete( + path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreDeleted, + ) + + def search( + self, + vector_store_id: str, + *, + query: Union[str, SequenceNotStr[str]], + filters: vector_store_search_params.Filters | Omit = omit, + max_num_results: int | Omit = omit, + ranking_options: vector_store_search_params.RankingOptions | Omit = omit, + rewrite_query: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncPage[VectorStoreSearchResponse]: + """ + Search a vector store for relevant chunks based on a query and file attributes + filter. + + Args: + query: A query string for a search + + filters: A filter to apply based on file attributes. + + max_num_results: The maximum number of results to return. This number should be between 1 and 50 + inclusive. + + ranking_options: Ranking options for search. + + rewrite_query: Whether to rewrite the natural language query for vector search. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template("/vector_stores/{vector_store_id}/search", vector_store_id=vector_store_id), + page=SyncPage[VectorStoreSearchResponse], + body=maybe_transform( + { + "query": query, + "filters": filters, + "max_num_results": max_num_results, + "ranking_options": ranking_options, + "rewrite_query": rewrite_query, + }, + vector_store_search_params.VectorStoreSearchParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + model=VectorStoreSearchResponse, + method="post", + ) + + +class AsyncVectorStores(AsyncAPIResource): + @cached_property + def files(self) -> AsyncFiles: + return AsyncFiles(self._client) + + @cached_property + def file_batches(self) -> AsyncFileBatches: + return AsyncFileBatches(self._client) + + @cached_property + def with_raw_response(self) -> AsyncVectorStoresWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncVectorStoresWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncVectorStoresWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncVectorStoresWithStreamingResponse(self) + + async def create( + self, + *, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + description: str | Omit = omit, + expires_after: vector_store_create_params.ExpiresAfter | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStore: + """ + Create a vector store. + + Args: + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` + strategy. Only applicable if `file_ids` is non-empty. + + description: A description for the vector store. Can be used to describe the vector store's + purpose. + + expires_after: The expiration policy for a vector store. + + file_ids: A list of [File](https://developers.openai.com/api/reference/resources/files) + IDs that the vector store should use. Useful for tools like `file_search` that + can access files. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the vector store. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + "/vector_stores", + body=await async_maybe_transform( + { + "chunking_strategy": chunking_strategy, + "description": description, + "expires_after": expires_after, + "file_ids": file_ids, + "metadata": metadata, + "name": name, + }, + vector_store_create_params.VectorStoreCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStore, + ) + + async def retrieve( + self, + vector_store_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStore: + """ + Retrieves a vector store. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._get( + path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStore, + ) + + async def update( + self, + vector_store_id: str, + *, + expires_after: Optional[vector_store_update_params.ExpiresAfter] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStore: + """ + Modifies a vector store. + + Args: + expires_after: The expiration policy for a vector store. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the vector store. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), + body=await async_maybe_transform( + { + "expires_after": expires_after, + "metadata": metadata, + "name": name, + }, + vector_store_update_params.VectorStoreUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStore, + ) + + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[VectorStore, AsyncCursorPage[VectorStore]]: + """Returns a list of vector stores. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + "/vector_stores", + page=AsyncCursorPage[VectorStore], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + vector_store_list_params.VectorStoreListParams, + ), + security={"bearer_auth": True}, + ), + model=VectorStore, + ) + + async def delete( + self, + vector_store_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VectorStoreDeleted: + """ + Delete a vector store. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._delete( + path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VectorStoreDeleted, + ) + + def search( + self, + vector_store_id: str, + *, + query: Union[str, SequenceNotStr[str]], + filters: vector_store_search_params.Filters | Omit = omit, + max_num_results: int | Omit = omit, + ranking_options: vector_store_search_params.RankingOptions | Omit = omit, + rewrite_query: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[VectorStoreSearchResponse, AsyncPage[VectorStoreSearchResponse]]: + """ + Search a vector store for relevant chunks based on a query and file attributes + filter. + + Args: + query: A query string for a search + + filters: A filter to apply based on file attributes. + + max_num_results: The maximum number of results to return. This number should be between 1 and 50 + inclusive. + + ranking_options: Ranking options for search. + + rewrite_query: Whether to rewrite the natural language query for vector search. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + path_template("/vector_stores/{vector_store_id}/search", vector_store_id=vector_store_id), + page=AsyncPage[VectorStoreSearchResponse], + body=maybe_transform( + { + "query": query, + "filters": filters, + "max_num_results": max_num_results, + "ranking_options": ranking_options, + "rewrite_query": rewrite_query, + }, + vector_store_search_params.VectorStoreSearchParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + model=VectorStoreSearchResponse, + method="post", + ) + + +class VectorStoresWithRawResponse: + def __init__(self, vector_stores: VectorStores) -> None: + self._vector_stores = vector_stores + + self.create = _legacy_response.to_raw_response_wrapper( + vector_stores.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + vector_stores.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + vector_stores.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + vector_stores.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + vector_stores.delete, + ) + self.search = _legacy_response.to_raw_response_wrapper( + vector_stores.search, + ) + + @cached_property + def files(self) -> FilesWithRawResponse: + return FilesWithRawResponse(self._vector_stores.files) + + @cached_property + def file_batches(self) -> FileBatchesWithRawResponse: + return FileBatchesWithRawResponse(self._vector_stores.file_batches) + + +class AsyncVectorStoresWithRawResponse: + def __init__(self, vector_stores: AsyncVectorStores) -> None: + self._vector_stores = vector_stores + + self.create = _legacy_response.async_to_raw_response_wrapper( + vector_stores.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + vector_stores.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + vector_stores.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + vector_stores.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + vector_stores.delete, + ) + self.search = _legacy_response.async_to_raw_response_wrapper( + vector_stores.search, + ) + + @cached_property + def files(self) -> AsyncFilesWithRawResponse: + return AsyncFilesWithRawResponse(self._vector_stores.files) + + @cached_property + def file_batches(self) -> AsyncFileBatchesWithRawResponse: + return AsyncFileBatchesWithRawResponse(self._vector_stores.file_batches) + + +class VectorStoresWithStreamingResponse: + def __init__(self, vector_stores: VectorStores) -> None: + self._vector_stores = vector_stores + + self.create = to_streamed_response_wrapper( + vector_stores.create, + ) + self.retrieve = to_streamed_response_wrapper( + vector_stores.retrieve, + ) + self.update = to_streamed_response_wrapper( + vector_stores.update, + ) + self.list = to_streamed_response_wrapper( + vector_stores.list, + ) + self.delete = to_streamed_response_wrapper( + vector_stores.delete, + ) + self.search = to_streamed_response_wrapper( + vector_stores.search, + ) + + @cached_property + def files(self) -> FilesWithStreamingResponse: + return FilesWithStreamingResponse(self._vector_stores.files) + + @cached_property + def file_batches(self) -> FileBatchesWithStreamingResponse: + return FileBatchesWithStreamingResponse(self._vector_stores.file_batches) + + +class AsyncVectorStoresWithStreamingResponse: + def __init__(self, vector_stores: AsyncVectorStores) -> None: + self._vector_stores = vector_stores + + self.create = async_to_streamed_response_wrapper( + vector_stores.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + vector_stores.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + vector_stores.update, + ) + self.list = async_to_streamed_response_wrapper( + vector_stores.list, + ) + self.delete = async_to_streamed_response_wrapper( + vector_stores.delete, + ) + self.search = async_to_streamed_response_wrapper( + vector_stores.search, + ) + + @cached_property + def files(self) -> AsyncFilesWithStreamingResponse: + return AsyncFilesWithStreamingResponse(self._vector_stores.files) + + @cached_property + def file_batches(self) -> AsyncFileBatchesWithStreamingResponse: + return AsyncFileBatchesWithStreamingResponse(self._vector_stores.file_batches) diff --git a/venv/lib/python3.12/site-packages/openai/resources/videos.py b/venv/lib/python3.12/site-packages/openai/resources/videos.py new file mode 100644 index 0000000..d4c84ea --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/videos.py @@ -0,0 +1,1457 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import typing_extensions +from typing import TYPE_CHECKING, Mapping, cast +from typing_extensions import Literal, assert_never + +import httpx2 + +from .. import _legacy_response +from ..types import ( + VideoSize, + VideoSeconds, + video_edit_params, + video_list_params, + video_remix_params, + video_create_params, + video_extend_params, + video_create_character_params, + video_download_content_params, +) +from .._files import deepcopy_with_paths +from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given +from .._utils import extract_files, path_template, maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_streamed_response_wrapper, + async_to_streamed_response_wrapper, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ..pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ..types.video import Video +from .._base_client import AsyncPaginator, make_request_options +from .._utils._utils import is_given +from ..types.video_size import VideoSize +from ..types.video_seconds import VideoSeconds +from ..types.video_model_param import VideoModelParam +from ..types.video_delete_response import VideoDeleteResponse +from ..types.video_get_character_response import VideoGetCharacterResponse +from ..types.video_create_character_response import VideoCreateCharacterResponse + +__all__ = ["Videos", "AsyncVideos"] + + +class Videos(SyncAPIResource): + @cached_property + def with_raw_response(self) -> VideosWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return VideosWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> VideosWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return VideosWithStreamingResponse(self) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + def create( + self, + *, + prompt: str, + input_reference: video_create_params.InputReference | Omit = omit, + model: VideoModelParam | Omit = omit, + seconds: VideoSeconds | Omit = omit, + size: VideoSize | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create a new video generation job from a prompt and optional reference assets. + + Args: + prompt: Text prompt that describes the video to generate. + + input_reference: Optional reference asset upload or reference object that guides generation. + + model: The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults + to `sora-2`. + + seconds: Clip duration in seconds (allowed values: 4, 8, 12). Defaults to 4 seconds. + + size: Output resolution formatted as width x height (allowed values: 720x1280, + 1280x720, 1024x1792, 1792x1024). Defaults to 720x1280. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "prompt": prompt, + "input_reference": input_reference, + "model": model, + "seconds": seconds, + "size": size, + }, + [["input_reference"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["input_reference"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/videos", + body=maybe_transform(body, video_create_params.VideoCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Video, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + def create_and_poll( + self, + *, + prompt: str, + input_reference: video_create_params.InputReference | Omit = omit, + model: VideoModelParam | Omit = omit, + seconds: VideoSeconds | Omit = omit, + size: VideoSize | Omit = omit, + poll_interval_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Video: + """Create a video and wait for it to be processed.""" + video = self.create( # pyright: ignore[reportDeprecated] + model=model, + prompt=prompt, + input_reference=input_reference, + seconds=seconds, + size=size, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + return self.poll( # pyright: ignore[reportDeprecated] + video.id, + poll_interval_ms=poll_interval_ms, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + def poll( + self, + video_id: str, + *, + poll_interval_ms: int | Omit = omit, + ) -> Video: + """Wait for the vector store file to finish processing. + + Note: this will return even if the file failed to process, you need to check + file.last_error and file.status to handle these cases + """ + headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"} + if is_given(poll_interval_ms): + headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + while True: + response = self.with_raw_response.retrieve( # pyright: ignore[reportDeprecated] + video_id, + extra_headers=headers, + ) + + video = response.parse() + if video.status == "in_progress" or video.status == "queued": + if not is_given(poll_interval_ms): + from_header = response.headers.get("openai-poll-after-ms") + if from_header is not None: + poll_interval_ms = int(from_header) + else: + poll_interval_ms = 1000 + + self._sleep(poll_interval_ms / 1000) + elif video.status == "completed" or video.status == "failed": + return video + else: + if TYPE_CHECKING: # type: ignore[unreachable] + assert_never(video.status) + else: + return video + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + def retrieve( + self, + video_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Fetch the latest metadata for a generated video. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + return self._get( + path_template("/videos/{video_id}", video_id=video_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Video, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[Video]: + """ + List recently generated videos for the current project. + + Args: + after: Identifier for the last item from the previous pagination request + + limit: Number of items to retrieve + + order: Sort order of results by timestamp. Use `asc` for ascending order or `desc` for + descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/videos", + page=SyncConversationCursorPage[Video], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + video_list_params.VideoListParams, + ), + security={"bearer_auth": True}, + ), + model=Video, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + def delete( + self, + video_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VideoDeleteResponse: + """ + Permanently delete a completed or failed video and its stored assets. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + return self._delete( + path_template("/videos/{video_id}", video_id=video_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VideoDeleteResponse, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + def create_character( + self, + *, + name: str, + video: FileTypes, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VideoCreateCharacterResponse: + """ + Create a character from an uploaded video. + + Args: + name: Display name for this API character. + + video: Video file used to create a character. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "name": name, + "video": video, + }, + [["video"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/videos/characters", + body=maybe_transform(body, video_create_character_params.VideoCreateCharacterParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VideoCreateCharacterResponse, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + def download_content( + self, + video_id: str, + *, + variant: Literal["video", "thumbnail", "spritesheet"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Download the generated video bytes or a derived preview asset. + + Streams the rendered video content for the specified video job. + + Args: + variant: Which downloadable asset to return. Defaults to the MP4 video. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return self._get( + path_template("/videos/{video_id}/content", video_id=video_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"variant": variant}, video_download_content_params.VideoDownloadContentParams), + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + def edit( + self, + *, + prompt: str, + video: video_edit_params.Video, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create a new video generation job by editing a source video or existing + generated video. + + Args: + prompt: Text prompt that describes how to edit the source video. + + video: Reference to the completed video to edit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "prompt": prompt, + "video": video, + }, + [["video"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/videos/edits", + body=maybe_transform(body, video_edit_params.VideoEditParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Video, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + def extend( + self, + *, + prompt: str, + seconds: VideoSeconds, + video: video_extend_params.Video, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create an extension of a completed video. + + Args: + prompt: Updated text prompt that directs the extension generation. + + seconds: Length of the newly generated extension segment in seconds (allowed values: 4, + 8, 12, 16, 20). + + video: Reference to the completed video to extend. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "prompt": prompt, + "seconds": seconds, + "video": video, + }, + [["video"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/videos/extensions", + body=maybe_transform(body, video_extend_params.VideoExtendParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Video, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + def get_character( + self, + character_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VideoGetCharacterResponse: + """ + Fetch a character. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not character_id: + raise ValueError(f"Expected a non-empty value for `character_id` but received {character_id!r}") + return self._get( + path_template("/videos/characters/{character_id}", character_id=character_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VideoGetCharacterResponse, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + def remix( + self, + video_id: str, + *, + prompt: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create a remix of a completed video using a refreshed prompt. + + Args: + prompt: Updated text prompt that directs the remix generation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + return self._post( + path_template("/videos/{video_id}/remix", video_id=video_id), + body=maybe_transform({"prompt": prompt}, video_remix_params.VideoRemixParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Video, + ) + + +class AsyncVideos(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncVideosWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncVideosWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncVideosWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncVideosWithStreamingResponse(self) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + async def create( + self, + *, + prompt: str, + input_reference: video_create_params.InputReference | Omit = omit, + model: VideoModelParam | Omit = omit, + seconds: VideoSeconds | Omit = omit, + size: VideoSize | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create a new video generation job from a prompt and optional reference assets. + + Args: + prompt: Text prompt that describes the video to generate. + + input_reference: Optional reference asset upload or reference object that guides generation. + + model: The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults + to `sora-2`. + + seconds: Clip duration in seconds (allowed values: 4, 8, 12). Defaults to 4 seconds. + + size: Output resolution formatted as width x height (allowed values: 720x1280, + 1280x720, 1024x1792, 1792x1024). Defaults to 720x1280. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "prompt": prompt, + "input_reference": input_reference, + "model": model, + "seconds": seconds, + "size": size, + }, + [["input_reference"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["input_reference"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/videos", + body=await async_maybe_transform(body, video_create_params.VideoCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Video, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + async def create_and_poll( + self, + *, + prompt: str, + input_reference: video_create_params.InputReference | Omit = omit, + model: VideoModelParam | Omit = omit, + seconds: VideoSeconds | Omit = omit, + size: VideoSize | Omit = omit, + poll_interval_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Video: + """Create a video and wait for it to be processed.""" + video = await self.create( # pyright: ignore[reportDeprecated] + model=model, + prompt=prompt, + input_reference=input_reference, + seconds=seconds, + size=size, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + return await self.poll( # pyright: ignore[reportDeprecated] + video.id, + poll_interval_ms=poll_interval_ms, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + async def poll( + self, + video_id: str, + *, + poll_interval_ms: int | Omit = omit, + ) -> Video: + """Wait for the vector store file to finish processing. + + Note: this will return even if the file failed to process, you need to check + file.last_error and file.status to handle these cases + """ + headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"} + if is_given(poll_interval_ms): + headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + while True: + response = await self.with_raw_response.retrieve( # pyright: ignore[reportDeprecated] + video_id, + extra_headers=headers, + ) + + video = response.parse() + if video.status == "in_progress" or video.status == "queued": + if not is_given(poll_interval_ms): + from_header = response.headers.get("openai-poll-after-ms") + if from_header is not None: + poll_interval_ms = int(from_header) + else: + poll_interval_ms = 1000 + + await self._sleep(poll_interval_ms / 1000) + elif video.status == "completed" or video.status == "failed": + return video + else: + if TYPE_CHECKING: # type: ignore[unreachable] + assert_never(video.status) + else: + return video + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + async def retrieve( + self, + video_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Fetch the latest metadata for a generated video. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + return await self._get( + path_template("/videos/{video_id}", video_id=video_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Video, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Video, AsyncConversationCursorPage[Video]]: + """ + List recently generated videos for the current project. + + Args: + after: Identifier for the last item from the previous pagination request + + limit: Number of items to retrieve + + order: Sort order of results by timestamp. Use `asc` for ascending order or `desc` for + descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/videos", + page=AsyncConversationCursorPage[Video], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + video_list_params.VideoListParams, + ), + security={"bearer_auth": True}, + ), + model=Video, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + async def delete( + self, + video_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VideoDeleteResponse: + """ + Permanently delete a completed or failed video and its stored assets. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + return await self._delete( + path_template("/videos/{video_id}", video_id=video_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VideoDeleteResponse, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + async def create_character( + self, + *, + name: str, + video: FileTypes, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VideoCreateCharacterResponse: + """ + Create a character from an uploaded video. + + Args: + name: Display name for this API character. + + video: Video file used to create a character. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "name": name, + "video": video, + }, + [["video"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/videos/characters", + body=await async_maybe_transform(body, video_create_character_params.VideoCreateCharacterParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VideoCreateCharacterResponse, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + async def download_content( + self, + video_id: str, + *, + variant: Literal["video", "thumbnail", "spritesheet"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Download the generated video bytes or a derived preview asset. + + Streams the rendered video content for the specified video job. + + Args: + variant: Which downloadable asset to return. Defaults to the MP4 video. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return await self._get( + path_template("/videos/{video_id}/content", video_id=video_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"variant": variant}, video_download_content_params.VideoDownloadContentParams + ), + security={"bearer_auth": True}, + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + async def edit( + self, + *, + prompt: str, + video: video_edit_params.Video, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create a new video generation job by editing a source video or existing + generated video. + + Args: + prompt: Text prompt that describes how to edit the source video. + + video: Reference to the completed video to edit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "prompt": prompt, + "video": video, + }, + [["video"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/videos/edits", + body=await async_maybe_transform(body, video_edit_params.VideoEditParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Video, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + async def extend( + self, + *, + prompt: str, + seconds: VideoSeconds, + video: video_extend_params.Video, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create an extension of a completed video. + + Args: + prompt: Updated text prompt that directs the extension generation. + + seconds: Length of the newly generated extension segment in seconds (allowed values: 4, + 8, 12, 16, 20). + + video: Reference to the completed video to extend. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "prompt": prompt, + "seconds": seconds, + "video": video, + }, + [["video"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/videos/extensions", + body=await async_maybe_transform(body, video_extend_params.VideoExtendParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Video, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + async def get_character( + self, + character_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> VideoGetCharacterResponse: + """ + Fetch a character. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not character_id: + raise ValueError(f"Expected a non-empty value for `character_id` but received {character_id!r}") + return await self._get( + path_template("/videos/characters/{character_id}", character_id=character_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=VideoGetCharacterResponse, + ) + + @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.") + async def remix( + self, + video_id: str, + *, + prompt: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create a remix of a completed video using a refreshed prompt. + + Args: + prompt: Updated text prompt that directs the remix generation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + return await self._post( + path_template("/videos/{video_id}/remix", video_id=video_id), + body=await async_maybe_transform({"prompt": prompt}, video_remix_params.VideoRemixParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=Video, + ) + + +class VideosWithRawResponse: + def __init__(self, videos: Videos) -> None: + self._videos = videos + + self.create = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + videos.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + videos.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + videos.list, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + videos.delete, # pyright: ignore[reportDeprecated], + ) + ) + self.create_character = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + videos.create_character, # pyright: ignore[reportDeprecated], + ) + ) + self.download_content = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + videos.download_content, # pyright: ignore[reportDeprecated], + ) + ) + self.edit = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + videos.edit, # pyright: ignore[reportDeprecated], + ) + ) + self.extend = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + videos.extend, # pyright: ignore[reportDeprecated], + ) + ) + self.get_character = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + videos.get_character, # pyright: ignore[reportDeprecated], + ) + ) + self.remix = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + videos.remix, # pyright: ignore[reportDeprecated], + ) + ) + + +class AsyncVideosWithRawResponse: + def __init__(self, videos: AsyncVideos) -> None: + self._videos = videos + + self.create = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + videos.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + videos.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + videos.list, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + videos.delete, # pyright: ignore[reportDeprecated], + ) + ) + self.create_character = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + videos.create_character, # pyright: ignore[reportDeprecated], + ) + ) + self.download_content = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + videos.download_content, # pyright: ignore[reportDeprecated], + ) + ) + self.edit = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + videos.edit, # pyright: ignore[reportDeprecated], + ) + ) + self.extend = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + videos.extend, # pyright: ignore[reportDeprecated], + ) + ) + self.get_character = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + videos.get_character, # pyright: ignore[reportDeprecated], + ) + ) + self.remix = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + videos.remix, # pyright: ignore[reportDeprecated], + ) + ) + + +class VideosWithStreamingResponse: + def __init__(self, videos: Videos) -> None: + self._videos = videos + + self.create = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + videos.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + videos.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + videos.list, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + videos.delete, # pyright: ignore[reportDeprecated], + ) + ) + self.create_character = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + videos.create_character, # pyright: ignore[reportDeprecated], + ) + ) + self.download_content = ( # pyright: ignore[reportDeprecated] + to_custom_streamed_response_wrapper( + videos.download_content, # pyright: ignore[reportDeprecated], + StreamedBinaryAPIResponse, + ) + ) + self.edit = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + videos.edit, # pyright: ignore[reportDeprecated], + ) + ) + self.extend = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + videos.extend, # pyright: ignore[reportDeprecated], + ) + ) + self.get_character = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + videos.get_character, # pyright: ignore[reportDeprecated], + ) + ) + self.remix = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + videos.remix, # pyright: ignore[reportDeprecated], + ) + ) + + +class AsyncVideosWithStreamingResponse: + def __init__(self, videos: AsyncVideos) -> None: + self._videos = videos + + self.create = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + videos.create, # pyright: ignore[reportDeprecated], + ) + ) + self.retrieve = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + videos.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + videos.list, # pyright: ignore[reportDeprecated], + ) + ) + self.delete = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + videos.delete, # pyright: ignore[reportDeprecated], + ) + ) + self.create_character = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + videos.create_character, # pyright: ignore[reportDeprecated], + ) + ) + self.download_content = ( # pyright: ignore[reportDeprecated] + async_to_custom_streamed_response_wrapper( + videos.download_content, # pyright: ignore[reportDeprecated], + AsyncStreamedBinaryAPIResponse, + ) + ) + self.edit = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + videos.edit, # pyright: ignore[reportDeprecated], + ) + ) + self.extend = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + videos.extend, # pyright: ignore[reportDeprecated], + ) + ) + self.get_character = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + videos.get_character, # pyright: ignore[reportDeprecated], + ) + ) + self.remix = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + videos.remix, # pyright: ignore[reportDeprecated], + ) + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/webhooks/__init__.py b/venv/lib/python3.12/site-packages/openai/resources/webhooks/__init__.py new file mode 100644 index 0000000..296bdb3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/webhooks/__init__.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from .webhooks import ( + Webhooks as _Webhooks, + AsyncWebhooks as _AsyncWebhooks, + WebhooksWithRawResponse, + AsyncWebhooksWithRawResponse, + WebhooksWithStreamingResponse, + AsyncWebhooksWithStreamingResponse, +) +from .event_types import ( + EventTypes, + AsyncEventTypes, + EventTypesWithRawResponse, + AsyncEventTypesWithRawResponse, + EventTypesWithStreamingResponse, + AsyncEventTypesWithStreamingResponse, +) + + +class Webhooks(_Webhooks): + pass + + +class AsyncWebhooks(_AsyncWebhooks): + pass + + +__all__ = [ + "EventTypes", + "AsyncEventTypes", + "EventTypesWithRawResponse", + "AsyncEventTypesWithRawResponse", + "EventTypesWithStreamingResponse", + "AsyncEventTypesWithStreamingResponse", + "Webhooks", + "AsyncWebhooks", + "WebhooksWithRawResponse", + "AsyncWebhooksWithRawResponse", + "WebhooksWithStreamingResponse", + "AsyncWebhooksWithStreamingResponse", +] diff --git a/venv/lib/python3.12/site-packages/openai/resources/webhooks/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/webhooks/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c17fbc9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/webhooks/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/webhooks/__pycache__/event_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/webhooks/__pycache__/event_types.cpython-312.pyc new file mode 100644 index 0000000..7df2f4a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/webhooks/__pycache__/event_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/webhooks/__pycache__/webhooks.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/resources/webhooks/__pycache__/webhooks.cpython-312.pyc new file mode 100644 index 0000000..cea2843 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/resources/webhooks/__pycache__/webhooks.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/resources/webhooks/api.md b/venv/lib/python3.12/site-packages/openai/resources/webhooks/api.md new file mode 100644 index 0000000..d060b41 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/webhooks/api.md @@ -0,0 +1,52 @@ +# Webhooks + +Types: + +```python +from openai.types.webhooks import ( + BatchCancelledWebhookEvent, + BatchCompletedWebhookEvent, + BatchExpiredWebhookEvent, + BatchFailedWebhookEvent, + DeletedWebhookEndpoint, + EvalRunCanceledWebhookEvent, + EvalRunFailedWebhookEvent, + EvalRunSucceededWebhookEvent, + FineTuningJobCancelledWebhookEvent, + FineTuningJobFailedWebhookEvent, + FineTuningJobSucceededWebhookEvent, + LiveCallIncomingWebhookEvent, + LiveTransportIncomingWebhookEvent, + RealtimeCallIncomingWebhookEvent, + ResponseCancelledWebhookEvent, + ResponseCompletedWebhookEvent, + ResponseFailedWebhookEvent, + ResponseIncompleteWebhookEvent, + SafetyAlertCreatedWebhookEvent, + SafetyDeactivationIssuedWebhookEvent, + SafetyOrgAlertCreatedWebhookEvent, + SafetyWarningIssuedWebhookEvent, + UnwrapWebhookEvent, + WebhookEndpoint, + WebhookEndpointList, + WebhookEndpointTestResult, + WebhookEndpointWithSecret, + WebhookEventTypeList, +) +``` + +Methods: + +- client.webhooks.create(\*\*params) -> WebhookEndpointWithSecret +- client.webhooks.retrieve(webhook_endpoint_id) -> WebhookEndpoint +- client.webhooks.update(webhook_endpoint_id, \*\*params) -> WebhookEndpoint +- client.webhooks.list(\*\*params) -> SyncCursorPage[WebhookEndpoint] +- client.webhooks.delete(webhook_endpoint_id) -> DeletedWebhookEndpoint +- client.webhooks.rotate_secret(webhook_endpoint_id, \*\*params) -> WebhookEndpointWithSecret +- client.webhooks.test(webhook_endpoint_id, \*\*params) -> WebhookEndpointTestResult + +## EventTypes + +Methods: + +- client.webhooks.event_types.list() -> WebhookEventTypeList diff --git a/venv/lib/python3.12/site-packages/openai/resources/webhooks/event_types.py b/venv/lib/python3.12/site-packages/openai/resources/webhooks/event_types.py new file mode 100644 index 0000000..44e074e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/webhooks/event_types.py @@ -0,0 +1,139 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx2 + +from ... import _legacy_response +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.webhooks.webhook_event_type_list import WebhookEventTypeList + +__all__ = ["EventTypes", "AsyncEventTypes"] + + +class EventTypes(SyncAPIResource): + @cached_property + def with_raw_response(self) -> EventTypesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return EventTypesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EventTypesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return EventTypesWithStreamingResponse(self) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> WebhookEventTypeList: + """Returns webhook event types visible to the authenticated project.""" + return self._get( + "/webhook_event_types", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=WebhookEventTypeList, + ) + + +class AsyncEventTypes(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncEventTypesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncEventTypesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEventTypesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncEventTypesWithStreamingResponse(self) + + async def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> WebhookEventTypeList: + """Returns webhook event types visible to the authenticated project.""" + return await self._get( + "/webhook_event_types", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=WebhookEventTypeList, + ) + + +class EventTypesWithRawResponse: + def __init__(self, event_types: EventTypes) -> None: + self._event_types = event_types + + self.list = _legacy_response.to_raw_response_wrapper( + event_types.list, + ) + + +class AsyncEventTypesWithRawResponse: + def __init__(self, event_types: AsyncEventTypes) -> None: + self._event_types = event_types + + self.list = _legacy_response.async_to_raw_response_wrapper( + event_types.list, + ) + + +class EventTypesWithStreamingResponse: + def __init__(self, event_types: EventTypes) -> None: + self._event_types = event_types + + self.list = to_streamed_response_wrapper( + event_types.list, + ) + + +class AsyncEventTypesWithStreamingResponse: + def __init__(self, event_types: AsyncEventTypes) -> None: + self._event_types = event_types + + self.list = async_to_streamed_response_wrapper( + event_types.list, + ) diff --git a/venv/lib/python3.12/site-packages/openai/resources/webhooks/webhooks.py b/venv/lib/python3.12/site-packages/openai/resources/webhooks/webhooks.py new file mode 100644 index 0000000..fbc3145 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/resources/webhooks/webhooks.py @@ -0,0 +1,1092 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import json +from typing import List, Optional, cast +from typing_extensions import Literal + +import httpx2 + +from ... import _legacy_response +from ..._types import Body, Omit, Query, Headers, NotGiven, HeadersLike, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._models import construct_type +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .event_types import ( + EventTypes, + AsyncEventTypes, + EventTypesWithRawResponse, + AsyncEventTypesWithRawResponse, + EventTypesWithStreamingResponse, + AsyncEventTypesWithStreamingResponse, +) +from ...pagination import SyncCursorPage, AsyncCursorPage +from ..._exceptions import InvalidWebhookSignatureError +from ..._base_client import AsyncPaginator, make_request_options +from ...lib._webhooks import webhook_signature_matches as _webhook_signature_matches +from ...types.webhooks import ( + webhook_list_params, + webhook_test_params, + webhook_create_params, + webhook_update_params, + webhook_rotate_secret_params, +) +from ...types.webhooks.webhook_endpoint import WebhookEndpoint +from ...types.webhooks.unwrap_webhook_event import UnwrapWebhookEvent +from ...types.webhooks.deleted_webhook_endpoint import DeletedWebhookEndpoint +from ...types.webhooks.webhook_endpoint_test_result import WebhookEndpointTestResult +from ...types.webhooks.webhook_endpoint_with_secret import WebhookEndpointWithSecret + +__all__ = ["Webhooks", "AsyncWebhooks"] + + +class Webhooks(SyncAPIResource): + @cached_property + def event_types(self) -> EventTypes: + return EventTypes(self._client) + + @cached_property + def with_raw_response(self) -> WebhooksWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return WebhooksWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> WebhooksWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return WebhooksWithStreamingResponse(self) + + def create( + self, + *, + event_types: List[ + Literal[ + "batch.completed", + "batch.failed", + "batch.expired", + "batch.cancelled", + "response.completed", + "response.failed", + "response.cancelled", + "response.incomplete", + "eval.run.succeeded", + "eval.run.failed", + "eval.run.canceled", + "fine_tuning.job.succeeded", + "fine_tuning.job.failed", + "fine_tuning.job.cancelled", + "realtime.call.incoming", + "video.completed", + "video.failed", + "safety.alert.created", + ] + ], + name: str, + url: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> WebhookEndpointWithSecret: + """ + Creates a webhook endpoint for the authenticated project. + + Args: + event_types: The event types that trigger deliveries to this endpoint. + + name: A human-readable name for the webhook endpoint. + + url: The HTTPS URL that receives webhook deliveries. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/webhook_endpoints", + body=maybe_transform( + { + "event_types": event_types, + "name": name, + "url": url, + }, + webhook_create_params.WebhookCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=WebhookEndpointWithSecret, + ) + + def retrieve( + self, + webhook_endpoint_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> WebhookEndpoint: + """ + Retrieves a webhook endpoint for the authenticated project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not webhook_endpoint_id: + raise ValueError( + f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}" + ) + return self._get( + path_template("/webhook_endpoints/{webhook_endpoint_id}", webhook_endpoint_id=webhook_endpoint_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=WebhookEndpoint, + ) + + def update( + self, + webhook_endpoint_id: str, + *, + event_types: List[ + Literal[ + "batch.completed", + "batch.failed", + "batch.expired", + "batch.cancelled", + "response.completed", + "response.failed", + "response.cancelled", + "response.incomplete", + "eval.run.succeeded", + "eval.run.failed", + "eval.run.canceled", + "fine_tuning.job.succeeded", + "fine_tuning.job.failed", + "fine_tuning.job.cancelled", + "realtime.call.incoming", + "video.completed", + "video.failed", + "safety.alert.created", + ] + ] + | Omit = omit, + name: str | Omit = omit, + url: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> WebhookEndpoint: + """ + Updates a webhook endpoint for the authenticated project. + + Args: + event_types: The complete set of event types that should trigger deliveries. + + name: A new human-readable name for the webhook endpoint. + + url: A new HTTPS URL that receives webhook deliveries. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not webhook_endpoint_id: + raise ValueError( + f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}" + ) + return self._post( + path_template("/webhook_endpoints/{webhook_endpoint_id}", webhook_endpoint_id=webhook_endpoint_id), + body=maybe_transform( + { + "event_types": event_types, + "name": name, + "url": url, + }, + webhook_update_params.WebhookUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=WebhookEndpoint, + ) + + def list( + self, + *, + after: Optional[str] | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[WebhookEndpoint]: + """ + Returns webhook endpoints for the authenticated project in newest-first order. + + Args: + after: ID of the last webhook endpoint from the previous page. + + limit: Maximum number of webhook endpoints to return. Defaults to 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/webhook_endpoints", + page=SyncCursorPage[WebhookEndpoint], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + webhook_list_params.WebhookListParams, + ), + security={"bearer_auth": True}, + ), + model=WebhookEndpoint, + ) + + def delete( + self, + webhook_endpoint_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> DeletedWebhookEndpoint: + """ + Deletes a webhook endpoint for the authenticated project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not webhook_endpoint_id: + raise ValueError( + f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}" + ) + return self._delete( + path_template("/webhook_endpoints/{webhook_endpoint_id}", webhook_endpoint_id=webhook_endpoint_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=DeletedWebhookEndpoint, + ) + + def rotate_secret( + self, + webhook_endpoint_id: str, + *, + keep_old_secret_active_for_24_hours: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> WebhookEndpointWithSecret: + """ + Rotates the signing secret for a webhook endpoint in the authenticated project. + + Args: + keep_old_secret_active_for_24_hours: Whether to keep the previous signing secret valid for 24 hours after rotation. + Defaults to false, which invalidates the previous secret immediately. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not webhook_endpoint_id: + raise ValueError( + f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}" + ) + return self._post( + path_template( + "/webhook_endpoints/{webhook_endpoint_id}/rotate_secret", webhook_endpoint_id=webhook_endpoint_id + ), + body=maybe_transform( + {"keep_old_secret_active_for_24_hours": keep_old_secret_active_for_24_hours}, + webhook_rotate_secret_params.WebhookRotateSecretParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=WebhookEndpointWithSecret, + ) + + def test( + self, + webhook_endpoint_id: str, + *, + event_type: Literal[ + "batch.completed", + "batch.failed", + "batch.expired", + "batch.cancelled", + "response.completed", + "response.failed", + "response.cancelled", + "response.incomplete", + "eval.run.succeeded", + "eval.run.failed", + "eval.run.canceled", + "fine_tuning.job.succeeded", + "fine_tuning.job.failed", + "fine_tuning.job.cancelled", + "realtime.call.incoming", + "video.completed", + "video.failed", + "safety.alert.created", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> WebhookEndpointTestResult: + """ + Sends a sample event to a webhook endpoint for the authenticated project. + + Args: + event_type: The event type to send as a sample delivery. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not webhook_endpoint_id: + raise ValueError( + f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}" + ) + return self._post( + path_template("/webhook_endpoints/{webhook_endpoint_id}/test", webhook_endpoint_id=webhook_endpoint_id), + body=maybe_transform({"event_type": event_type}, webhook_test_params.WebhookTestParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=WebhookEndpointTestResult, + ) + + def unwrap( + self, + payload: str | bytes, + headers: HeadersLike, + *, + secret: str | None = None, + ) -> UnwrapWebhookEvent: + """Validates that the given payload was sent by OpenAI and parses the payload.""" + if secret is None: + secret = self._client.webhook_secret + + self.verify_signature(payload=payload, headers=headers, secret=secret) + + return cast( + UnwrapWebhookEvent, + construct_type( + type_=UnwrapWebhookEvent, + value=json.loads(payload), + ), + ) + + def verify_signature( + self, + payload: str | bytes, + headers: HeadersLike, + *, + secret: str | None = None, + tolerance: int = 300, + ) -> None: + """Validates whether or not the webhook payload was sent by OpenAI. + + Args: + payload: The webhook payload + headers: The webhook headers + secret: The webhook secret (optional, will use client secret if not provided) + tolerance: Maximum age of the webhook in seconds (default: 300 = 5 minutes) + """ + if secret is None: + secret = self._client.webhook_secret + + if secret is None: + raise ValueError( + "The webhook secret must either be set using the env var, OPENAI_WEBHOOK_SECRET, " + "on the client class, OpenAI(webhook_secret='123'), or passed to this function" + ) + + if not _webhook_signature_matches(payload, headers, secret=secret, tolerance=tolerance): + raise InvalidWebhookSignatureError( + "The given webhook signature does not match the expected signature" + ) from None + + +class AsyncWebhooks(AsyncAPIResource): + @cached_property + def event_types(self) -> AsyncEventTypes: + return AsyncEventTypes(self._client) + + @cached_property + def with_raw_response(self) -> AsyncWebhooksWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncWebhooksWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncWebhooksWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncWebhooksWithStreamingResponse(self) + + async def create( + self, + *, + event_types: List[ + Literal[ + "batch.completed", + "batch.failed", + "batch.expired", + "batch.cancelled", + "response.completed", + "response.failed", + "response.cancelled", + "response.incomplete", + "eval.run.succeeded", + "eval.run.failed", + "eval.run.canceled", + "fine_tuning.job.succeeded", + "fine_tuning.job.failed", + "fine_tuning.job.cancelled", + "realtime.call.incoming", + "video.completed", + "video.failed", + "safety.alert.created", + ] + ], + name: str, + url: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> WebhookEndpointWithSecret: + """ + Creates a webhook endpoint for the authenticated project. + + Args: + event_types: The event types that trigger deliveries to this endpoint. + + name: A human-readable name for the webhook endpoint. + + url: The HTTPS URL that receives webhook deliveries. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/webhook_endpoints", + body=await async_maybe_transform( + { + "event_types": event_types, + "name": name, + "url": url, + }, + webhook_create_params.WebhookCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=WebhookEndpointWithSecret, + ) + + async def retrieve( + self, + webhook_endpoint_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> WebhookEndpoint: + """ + Retrieves a webhook endpoint for the authenticated project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not webhook_endpoint_id: + raise ValueError( + f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}" + ) + return await self._get( + path_template("/webhook_endpoints/{webhook_endpoint_id}", webhook_endpoint_id=webhook_endpoint_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=WebhookEndpoint, + ) + + async def update( + self, + webhook_endpoint_id: str, + *, + event_types: List[ + Literal[ + "batch.completed", + "batch.failed", + "batch.expired", + "batch.cancelled", + "response.completed", + "response.failed", + "response.cancelled", + "response.incomplete", + "eval.run.succeeded", + "eval.run.failed", + "eval.run.canceled", + "fine_tuning.job.succeeded", + "fine_tuning.job.failed", + "fine_tuning.job.cancelled", + "realtime.call.incoming", + "video.completed", + "video.failed", + "safety.alert.created", + ] + ] + | Omit = omit, + name: str | Omit = omit, + url: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> WebhookEndpoint: + """ + Updates a webhook endpoint for the authenticated project. + + Args: + event_types: The complete set of event types that should trigger deliveries. + + name: A new human-readable name for the webhook endpoint. + + url: A new HTTPS URL that receives webhook deliveries. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not webhook_endpoint_id: + raise ValueError( + f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}" + ) + return await self._post( + path_template("/webhook_endpoints/{webhook_endpoint_id}", webhook_endpoint_id=webhook_endpoint_id), + body=await async_maybe_transform( + { + "event_types": event_types, + "name": name, + "url": url, + }, + webhook_update_params.WebhookUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=WebhookEndpoint, + ) + + def list( + self, + *, + after: Optional[str] | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[WebhookEndpoint, AsyncCursorPage[WebhookEndpoint]]: + """ + Returns webhook endpoints for the authenticated project in newest-first order. + + Args: + after: ID of the last webhook endpoint from the previous page. + + limit: Maximum number of webhook endpoints to return. Defaults to 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/webhook_endpoints", + page=AsyncCursorPage[WebhookEndpoint], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + webhook_list_params.WebhookListParams, + ), + security={"bearer_auth": True}, + ), + model=WebhookEndpoint, + ) + + async def delete( + self, + webhook_endpoint_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> DeletedWebhookEndpoint: + """ + Deletes a webhook endpoint for the authenticated project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not webhook_endpoint_id: + raise ValueError( + f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}" + ) + return await self._delete( + path_template("/webhook_endpoints/{webhook_endpoint_id}", webhook_endpoint_id=webhook_endpoint_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=DeletedWebhookEndpoint, + ) + + async def rotate_secret( + self, + webhook_endpoint_id: str, + *, + keep_old_secret_active_for_24_hours: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> WebhookEndpointWithSecret: + """ + Rotates the signing secret for a webhook endpoint in the authenticated project. + + Args: + keep_old_secret_active_for_24_hours: Whether to keep the previous signing secret valid for 24 hours after rotation. + Defaults to false, which invalidates the previous secret immediately. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not webhook_endpoint_id: + raise ValueError( + f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}" + ) + return await self._post( + path_template( + "/webhook_endpoints/{webhook_endpoint_id}/rotate_secret", webhook_endpoint_id=webhook_endpoint_id + ), + body=await async_maybe_transform( + {"keep_old_secret_active_for_24_hours": keep_old_secret_active_for_24_hours}, + webhook_rotate_secret_params.WebhookRotateSecretParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=WebhookEndpointWithSecret, + ) + + async def test( + self, + webhook_endpoint_id: str, + *, + event_type: Literal[ + "batch.completed", + "batch.failed", + "batch.expired", + "batch.cancelled", + "response.completed", + "response.failed", + "response.cancelled", + "response.incomplete", + "eval.run.succeeded", + "eval.run.failed", + "eval.run.canceled", + "fine_tuning.job.succeeded", + "fine_tuning.job.failed", + "fine_tuning.job.cancelled", + "realtime.call.incoming", + "video.completed", + "video.failed", + "safety.alert.created", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx2.Timeout | None | NotGiven = not_given, + ) -> WebhookEndpointTestResult: + """ + Sends a sample event to a webhook endpoint for the authenticated project. + + Args: + event_type: The event type to send as a sample delivery. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not webhook_endpoint_id: + raise ValueError( + f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}" + ) + return await self._post( + path_template("/webhook_endpoints/{webhook_endpoint_id}/test", webhook_endpoint_id=webhook_endpoint_id), + body=await async_maybe_transform({"event_type": event_type}, webhook_test_params.WebhookTestParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=WebhookEndpointTestResult, + ) + + def unwrap( + self, + payload: str | bytes, + headers: HeadersLike, + *, + secret: str | None = None, + ) -> UnwrapWebhookEvent: + """Validates that the given payload was sent by OpenAI and parses the payload.""" + if secret is None: + secret = self._client.webhook_secret + + self.verify_signature(payload=payload, headers=headers, secret=secret) + + body = payload.decode("utf-8") if isinstance(payload, bytes) else payload + return cast( + UnwrapWebhookEvent, + construct_type( + type_=UnwrapWebhookEvent, + value=json.loads(body), + ), + ) + + def verify_signature( + self, + payload: str | bytes, + headers: HeadersLike, + *, + secret: str | None = None, + tolerance: int = 300, + ) -> None: + """Validates whether or not the webhook payload was sent by OpenAI. + + Args: + payload: The webhook payload + headers: The webhook headers + secret: The webhook secret (optional, will use client secret if not provided) + tolerance: Maximum age of the webhook in seconds (default: 300 = 5 minutes) + """ + if secret is None: + secret = self._client.webhook_secret + + if secret is None: + raise ValueError( + "The webhook secret must either be set using the env var, OPENAI_WEBHOOK_SECRET, " + "on the client class, OpenAI(webhook_secret='123'), or passed to this function" + ) from None + + if not _webhook_signature_matches(payload, headers, secret=secret, tolerance=tolerance): + raise InvalidWebhookSignatureError("The given webhook signature does not match the expected signature") + + +class WebhooksWithRawResponse: + def __init__(self, webhooks: Webhooks) -> None: + self._webhooks = webhooks + + self.create = _legacy_response.to_raw_response_wrapper( + webhooks.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + webhooks.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + webhooks.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + webhooks.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + webhooks.delete, + ) + self.rotate_secret = _legacy_response.to_raw_response_wrapper( + webhooks.rotate_secret, + ) + self.test = _legacy_response.to_raw_response_wrapper( + webhooks.test, + ) + + @cached_property + def event_types(self) -> EventTypesWithRawResponse: + return EventTypesWithRawResponse(self._webhooks.event_types) + + +class AsyncWebhooksWithRawResponse: + def __init__(self, webhooks: AsyncWebhooks) -> None: + self._webhooks = webhooks + + self.create = _legacy_response.async_to_raw_response_wrapper( + webhooks.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + webhooks.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + webhooks.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + webhooks.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + webhooks.delete, + ) + self.rotate_secret = _legacy_response.async_to_raw_response_wrapper( + webhooks.rotate_secret, + ) + self.test = _legacy_response.async_to_raw_response_wrapper( + webhooks.test, + ) + + @cached_property + def event_types(self) -> AsyncEventTypesWithRawResponse: + return AsyncEventTypesWithRawResponse(self._webhooks.event_types) + + +class WebhooksWithStreamingResponse: + def __init__(self, webhooks: Webhooks) -> None: + self._webhooks = webhooks + + self.create = to_streamed_response_wrapper( + webhooks.create, + ) + self.retrieve = to_streamed_response_wrapper( + webhooks.retrieve, + ) + self.update = to_streamed_response_wrapper( + webhooks.update, + ) + self.list = to_streamed_response_wrapper( + webhooks.list, + ) + self.delete = to_streamed_response_wrapper( + webhooks.delete, + ) + self.rotate_secret = to_streamed_response_wrapper( + webhooks.rotate_secret, + ) + self.test = to_streamed_response_wrapper( + webhooks.test, + ) + + @cached_property + def event_types(self) -> EventTypesWithStreamingResponse: + return EventTypesWithStreamingResponse(self._webhooks.event_types) + + +class AsyncWebhooksWithStreamingResponse: + def __init__(self, webhooks: AsyncWebhooks) -> None: + self._webhooks = webhooks + + self.create = async_to_streamed_response_wrapper( + webhooks.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + webhooks.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + webhooks.update, + ) + self.list = async_to_streamed_response_wrapper( + webhooks.list, + ) + self.delete = async_to_streamed_response_wrapper( + webhooks.delete, + ) + self.rotate_secret = async_to_streamed_response_wrapper( + webhooks.rotate_secret, + ) + self.test = async_to_streamed_response_wrapper( + webhooks.test, + ) + + @cached_property + def event_types(self) -> AsyncEventTypesWithStreamingResponse: + return AsyncEventTypesWithStreamingResponse(self._webhooks.event_types) diff --git a/venv/lib/python3.12/site-packages/openai/types/__init__.py b/venv/lib/python3.12/site-packages/openai/types/__init__.py new file mode 100644 index 0000000..a27bb84 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/__init__.py @@ -0,0 +1,140 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .batch import Batch as Batch +from .image import Image as Image +from .model import Model as Model +from .skill import Skill as Skill +from .video import Video as Video +from .shared import ( + Metadata as Metadata, + AllModels as AllModels, + ChatModel as ChatModel, + Reasoning as Reasoning, + ErrorObject as ErrorObject, + CompoundFilter as CompoundFilter, + OAuthErrorCode as OAuthErrorCode, + ResponsesModel as ResponsesModel, + ReasoningEffort as ReasoningEffort, + ComparisonFilter as ComparisonFilter, + FunctionDefinition as FunctionDefinition, + FunctionParameters as FunctionParameters, + ResponseFormatText as ResponseFormatText, + CustomToolInputFormat as CustomToolInputFormat, + ResponseFormatJSONObject as ResponseFormatJSONObject, + ResponseFormatJSONSchema as ResponseFormatJSONSchema, + ResponseFormatTextPython as ResponseFormatTextPython, + ResponseFormatTextGrammar as ResponseFormatTextGrammar, +) +from .upload import Upload as Upload +from .embedding import Embedding as Embedding +from .chat_model import ChatModel as ChatModel +from .completion import Completion as Completion +from .moderation import Moderation as Moderation +from .skill_list import SkillList as SkillList +from .video_size import VideoSize as VideoSize +from .audio_model import AudioModel as AudioModel +from .batch_error import BatchError as BatchError +from .batch_usage import BatchUsage as BatchUsage +from .file_object import FileObject as FileObject +from .image_model import ImageModel as ImageModel +from .video_model import VideoModel as VideoModel +from .file_content import FileContent as FileContent +from .file_deleted import FileDeleted as FileDeleted +from .file_purpose import FilePurpose as FilePurpose +from .vector_store import VectorStore as VectorStore +from .deleted_skill import DeletedSkill as DeletedSkill +from .model_deleted import ModelDeleted as ModelDeleted +from .video_seconds import VideoSeconds as VideoSeconds +from .embedding_model import EmbeddingModel as EmbeddingModel +from .images_response import ImagesResponse as ImagesResponse +from .completion_usage import CompletionUsage as CompletionUsage +from .eval_list_params import EvalListParams as EvalListParams +from .file_list_params import FileListParams as FileListParams +from .moderation_model import ModerationModel as ModerationModel +from .batch_list_params import BatchListParams as BatchListParams +from .completion_choice import CompletionChoice as CompletionChoice +from .image_edit_params import ImageEditParams as ImageEditParams +from .skill_list_params import SkillListParams as SkillListParams +from .video_edit_params import VideoEditParams as VideoEditParams +from .video_list_params import VideoListParams as VideoListParams +from .video_model_param import VideoModelParam as VideoModelParam +from .eval_create_params import EvalCreateParams as EvalCreateParams +from .eval_list_response import EvalListResponse as EvalListResponse +from .eval_update_params import EvalUpdateParams as EvalUpdateParams +from .file_create_params import FileCreateParams as FileCreateParams +from .video_create_error import VideoCreateError as VideoCreateError +from .video_remix_params import VideoRemixParams as VideoRemixParams +from .batch_create_params import BatchCreateParams as BatchCreateParams +from .skill_create_params import SkillCreateParams as SkillCreateParams +from .skill_update_params import SkillUpdateParams as SkillUpdateParams +from .video_create_params import VideoCreateParams as VideoCreateParams +from .video_extend_params import VideoExtendParams as VideoExtendParams +from .batch_request_counts import BatchRequestCounts as BatchRequestCounts +from .eval_create_response import EvalCreateResponse as EvalCreateResponse +from .eval_delete_response import EvalDeleteResponse as EvalDeleteResponse +from .eval_update_response import EvalUpdateResponse as EvalUpdateResponse +from .upload_create_params import UploadCreateParams as UploadCreateParams +from .vector_store_deleted import VectorStoreDeleted as VectorStoreDeleted +from .audio_response_format import AudioResponseFormat as AudioResponseFormat +from .container_list_params import ContainerListParams as ContainerListParams +from .image_generate_params import ImageGenerateParams as ImageGenerateParams +from .video_delete_response import VideoDeleteResponse as VideoDeleteResponse +from .eval_retrieve_response import EvalRetrieveResponse as EvalRetrieveResponse +from .file_chunking_strategy import FileChunkingStrategy as FileChunkingStrategy +from .image_gen_stream_event import ImageGenStreamEvent as ImageGenStreamEvent +from .upload_complete_params import UploadCompleteParams as UploadCompleteParams +from .websocket_reconnection import ( + ReconnectingEvent as ReconnectingEvent, + ReconnectingOverrides as ReconnectingOverrides, +) +from .container_create_params import ContainerCreateParams as ContainerCreateParams +from .container_list_response import ContainerListResponse as ContainerListResponse +from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams +from .image_edit_stream_event import ImageEditStreamEvent as ImageEditStreamEvent +from .completion_create_params import CompletionCreateParams as CompletionCreateParams +from .content_provenance_check import ContentProvenanceCheck as ContentProvenanceCheck +from .moderation_create_params import ModerationCreateParams as ModerationCreateParams +from .vector_store_list_params import VectorStoreListParams as VectorStoreListParams +from .container_create_response import ContainerCreateResponse as ContainerCreateResponse +from .create_embedding_response import CreateEmbeddingResponse as CreateEmbeddingResponse +from .image_gen_completed_event import ImageGenCompletedEvent as ImageGenCompletedEvent +from .image_edit_completed_event import ImageEditCompletedEvent as ImageEditCompletedEvent +from .moderation_create_response import ModerationCreateResponse as ModerationCreateResponse +from .vector_store_create_params import VectorStoreCreateParams as VectorStoreCreateParams +from .vector_store_search_params import VectorStoreSearchParams as VectorStoreSearchParams +from .vector_store_update_params import VectorStoreUpdateParams as VectorStoreUpdateParams +from .container_retrieve_response import ContainerRetrieveResponse as ContainerRetrieveResponse +from .image_input_reference_param import ImageInputReferenceParam as ImageInputReferenceParam +from .moderation_text_input_param import ModerationTextInputParam as ModerationTextInputParam +from .file_chunking_strategy_param import FileChunkingStrategyParam as FileChunkingStrategyParam +from .vector_store_search_response import VectorStoreSearchResponse as VectorStoreSearchResponse +from .video_get_character_response import VideoGetCharacterResponse as VideoGetCharacterResponse +from .websocket_connection_options import ( + WebSocketConnectionOptions as WebSocketConnectionOptions, + WebsocketConnectionOptions as WebsocketConnectionOptions, +) +from .image_create_variation_params import ImageCreateVariationParams as ImageCreateVariationParams +from .image_gen_partial_image_event import ImageGenPartialImageEvent as ImageGenPartialImageEvent +from .static_file_chunking_strategy import StaticFileChunkingStrategy as StaticFileChunkingStrategy +from .video_create_character_params import VideoCreateCharacterParams as VideoCreateCharacterParams +from .video_download_content_params import VideoDownloadContentParams as VideoDownloadContentParams +from .eval_custom_data_source_config import EvalCustomDataSourceConfig as EvalCustomDataSourceConfig +from .image_edit_partial_image_event import ImageEditPartialImageEvent as ImageEditPartialImageEvent +from .video_create_character_response import VideoCreateCharacterResponse as VideoCreateCharacterResponse +from .moderation_image_url_input_param import ModerationImageURLInputParam as ModerationImageURLInputParam +from .auto_file_chunking_strategy_param import AutoFileChunkingStrategyParam as AutoFileChunkingStrategyParam +from .moderation_multi_modal_input_param import ModerationMultiModalInputParam as ModerationMultiModalInputParam +from .other_file_chunking_strategy_object import OtherFileChunkingStrategyObject as OtherFileChunkingStrategyObject +from .static_file_chunking_strategy_param import StaticFileChunkingStrategyParam as StaticFileChunkingStrategyParam +from .static_file_chunking_strategy_object import StaticFileChunkingStrategyObject as StaticFileChunkingStrategyObject +from .content_provenance_check_create_params import ( + ContentProvenanceCheckCreateParams as ContentProvenanceCheckCreateParams, +) +from .eval_stored_completions_data_source_config import ( + EvalStoredCompletionsDataSourceConfig as EvalStoredCompletionsDataSourceConfig, +) +from .static_file_chunking_strategy_object_param import ( + StaticFileChunkingStrategyObjectParam as StaticFileChunkingStrategyObjectParam, +) diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..dc7b82e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/audio_model.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/audio_model.cpython-312.pyc new file mode 100644 index 0000000..bed662c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/audio_model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/audio_response_format.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/audio_response_format.cpython-312.pyc new file mode 100644 index 0000000..4e4f69f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/audio_response_format.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/auto_file_chunking_strategy_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/auto_file_chunking_strategy_param.cpython-312.pyc new file mode 100644 index 0000000..61577eb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/auto_file_chunking_strategy_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch.cpython-312.pyc new file mode 100644 index 0000000..6fbaa55 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_create_params.cpython-312.pyc new file mode 100644 index 0000000..bf84b13 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_error.cpython-312.pyc new file mode 100644 index 0000000..bc98efa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_list_params.cpython-312.pyc new file mode 100644 index 0000000..118da06 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_request_counts.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_request_counts.cpython-312.pyc new file mode 100644 index 0000000..60d4a08 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_request_counts.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_usage.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_usage.cpython-312.pyc new file mode 100644 index 0000000..f0a2fa6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/batch_usage.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/chat_model.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/chat_model.cpython-312.pyc new file mode 100644 index 0000000..11dd691 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/chat_model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/completion.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/completion.cpython-312.pyc new file mode 100644 index 0000000..eeddff2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/completion.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/completion_choice.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/completion_choice.cpython-312.pyc new file mode 100644 index 0000000..6936ce9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/completion_choice.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/completion_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/completion_create_params.cpython-312.pyc new file mode 100644 index 0000000..9f5152b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/completion_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/completion_usage.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/completion_usage.cpython-312.pyc new file mode 100644 index 0000000..76c8205 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/completion_usage.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_create_params.cpython-312.pyc new file mode 100644 index 0000000..ccf67fb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_create_response.cpython-312.pyc new file mode 100644 index 0000000..94b77e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_list_params.cpython-312.pyc new file mode 100644 index 0000000..251d21d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_list_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_list_response.cpython-312.pyc new file mode 100644 index 0000000..684fba0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_list_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_retrieve_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_retrieve_response.cpython-312.pyc new file mode 100644 index 0000000..e297cd2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/container_retrieve_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/content_provenance_check.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/content_provenance_check.cpython-312.pyc new file mode 100644 index 0000000..9518b8a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/content_provenance_check.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/content_provenance_check_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/content_provenance_check_create_params.cpython-312.pyc new file mode 100644 index 0000000..ed74bdf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/content_provenance_check_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/create_embedding_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/create_embedding_response.cpython-312.pyc new file mode 100644 index 0000000..7fb045a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/create_embedding_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/deleted_skill.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/deleted_skill.cpython-312.pyc new file mode 100644 index 0000000..2e22370 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/deleted_skill.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/embedding.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/embedding.cpython-312.pyc new file mode 100644 index 0000000..28ec0a8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/embedding.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/embedding_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/embedding_create_params.cpython-312.pyc new file mode 100644 index 0000000..ce40ba3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/embedding_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/embedding_model.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/embedding_model.cpython-312.pyc new file mode 100644 index 0000000..fd9bdbd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/embedding_model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_create_params.cpython-312.pyc new file mode 100644 index 0000000..4054079 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_create_response.cpython-312.pyc new file mode 100644 index 0000000..92c2209 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_custom_data_source_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_custom_data_source_config.cpython-312.pyc new file mode 100644 index 0000000..2e62c77 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_custom_data_source_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_delete_response.cpython-312.pyc new file mode 100644 index 0000000..7b835f0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_list_params.cpython-312.pyc new file mode 100644 index 0000000..2b14a26 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_list_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_list_response.cpython-312.pyc new file mode 100644 index 0000000..d291c12 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_list_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_retrieve_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_retrieve_response.cpython-312.pyc new file mode 100644 index 0000000..a9071ac Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_retrieve_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_stored_completions_data_source_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_stored_completions_data_source_config.cpython-312.pyc new file mode 100644 index 0000000..741fb1c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_stored_completions_data_source_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_update_params.cpython-312.pyc new file mode 100644 index 0000000..696da3b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_update_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_update_response.cpython-312.pyc new file mode 100644 index 0000000..bb93917 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/eval_update_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_chunking_strategy.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_chunking_strategy.cpython-312.pyc new file mode 100644 index 0000000..9ab382f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_chunking_strategy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_chunking_strategy_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_chunking_strategy_param.cpython-312.pyc new file mode 100644 index 0000000..324ca6b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_chunking_strategy_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_content.cpython-312.pyc new file mode 100644 index 0000000..86bd57d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_create_params.cpython-312.pyc new file mode 100644 index 0000000..2efa4d3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_deleted.cpython-312.pyc new file mode 100644 index 0000000..a305eee Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_list_params.cpython-312.pyc new file mode 100644 index 0000000..cf7a55b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_object.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_object.cpython-312.pyc new file mode 100644 index 0000000..87f5bb1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_object.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_purpose.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_purpose.cpython-312.pyc new file mode 100644 index 0000000..28f0e81 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/file_purpose.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/image.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image.cpython-312.pyc new file mode 100644 index 0000000..ae20beb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_create_variation_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_create_variation_params.cpython-312.pyc new file mode 100644 index 0000000..dbe0036 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_create_variation_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_edit_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_edit_completed_event.cpython-312.pyc new file mode 100644 index 0000000..9e41164 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_edit_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_edit_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_edit_params.cpython-312.pyc new file mode 100644 index 0000000..80e9aba Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_edit_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_edit_partial_image_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_edit_partial_image_event.cpython-312.pyc new file mode 100644 index 0000000..0da0d1a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_edit_partial_image_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_edit_stream_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_edit_stream_event.cpython-312.pyc new file mode 100644 index 0000000..5d03689 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_edit_stream_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_gen_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_gen_completed_event.cpython-312.pyc new file mode 100644 index 0000000..b6cf103 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_gen_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_gen_partial_image_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_gen_partial_image_event.cpython-312.pyc new file mode 100644 index 0000000..3701f98 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_gen_partial_image_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_gen_stream_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_gen_stream_event.cpython-312.pyc new file mode 100644 index 0000000..e2f4c9d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_gen_stream_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_generate_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_generate_params.cpython-312.pyc new file mode 100644 index 0000000..7d60fa7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_generate_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_input_reference_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_input_reference_param.cpython-312.pyc new file mode 100644 index 0000000..b69d7ff Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_input_reference_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_model.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_model.cpython-312.pyc new file mode 100644 index 0000000..c069d87 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/image_model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/images_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/images_response.cpython-312.pyc new file mode 100644 index 0000000..a7d375f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/images_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/model.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/model.cpython-312.pyc new file mode 100644 index 0000000..dd263e3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/model_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/model_deleted.cpython-312.pyc new file mode 100644 index 0000000..ff28f3a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/model_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation.cpython-312.pyc new file mode 100644 index 0000000..b8171e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_create_params.cpython-312.pyc new file mode 100644 index 0000000..0d77a2b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_create_response.cpython-312.pyc new file mode 100644 index 0000000..b9d631c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_image_url_input_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_image_url_input_param.cpython-312.pyc new file mode 100644 index 0000000..4f1ce1b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_image_url_input_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_model.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_model.cpython-312.pyc new file mode 100644 index 0000000..7e454d4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_multi_modal_input_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_multi_modal_input_param.cpython-312.pyc new file mode 100644 index 0000000..2b39c66 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_multi_modal_input_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_text_input_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_text_input_param.cpython-312.pyc new file mode 100644 index 0000000..ebc7132 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/moderation_text_input_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/other_file_chunking_strategy_object.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/other_file_chunking_strategy_object.cpython-312.pyc new file mode 100644 index 0000000..32e995a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/other_file_chunking_strategy_object.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill.cpython-312.pyc new file mode 100644 index 0000000..1eb8895 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill_create_params.cpython-312.pyc new file mode 100644 index 0000000..0dc0d35 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill_list.cpython-312.pyc new file mode 100644 index 0000000..34a7301 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill_list_params.cpython-312.pyc new file mode 100644 index 0000000..8383cce Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill_update_params.cpython-312.pyc new file mode 100644 index 0000000..5762382 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/skill_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/static_file_chunking_strategy.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/static_file_chunking_strategy.cpython-312.pyc new file mode 100644 index 0000000..feec696 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/static_file_chunking_strategy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/static_file_chunking_strategy_object.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/static_file_chunking_strategy_object.cpython-312.pyc new file mode 100644 index 0000000..001feac Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/static_file_chunking_strategy_object.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/static_file_chunking_strategy_object_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/static_file_chunking_strategy_object_param.cpython-312.pyc new file mode 100644 index 0000000..b608a19 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/static_file_chunking_strategy_object_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/static_file_chunking_strategy_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/static_file_chunking_strategy_param.cpython-312.pyc new file mode 100644 index 0000000..f85d315 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/static_file_chunking_strategy_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/upload.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/upload.cpython-312.pyc new file mode 100644 index 0000000..25a5341 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/upload.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/upload_complete_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/upload_complete_params.cpython-312.pyc new file mode 100644 index 0000000..cafd464 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/upload_complete_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/upload_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/upload_create_params.cpython-312.pyc new file mode 100644 index 0000000..e1a6e59 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/upload_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store.cpython-312.pyc new file mode 100644 index 0000000..9298c6a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_create_params.cpython-312.pyc new file mode 100644 index 0000000..e0740b1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_deleted.cpython-312.pyc new file mode 100644 index 0000000..9225f56 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_list_params.cpython-312.pyc new file mode 100644 index 0000000..9f27515 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_search_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_search_params.cpython-312.pyc new file mode 100644 index 0000000..a4b192e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_search_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_search_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_search_response.cpython-312.pyc new file mode 100644 index 0000000..9ff6c8c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_search_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_update_params.cpython-312.pyc new file mode 100644 index 0000000..5fd2132 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/vector_store_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video.cpython-312.pyc new file mode 100644 index 0000000..c989cde Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_create_character_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_create_character_params.cpython-312.pyc new file mode 100644 index 0000000..eada996 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_create_character_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_create_character_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_create_character_response.cpython-312.pyc new file mode 100644 index 0000000..02d140a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_create_character_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_create_error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_create_error.cpython-312.pyc new file mode 100644 index 0000000..bd21f50 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_create_error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_create_params.cpython-312.pyc new file mode 100644 index 0000000..6bbb008 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_delete_response.cpython-312.pyc new file mode 100644 index 0000000..c22727c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_download_content_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_download_content_params.cpython-312.pyc new file mode 100644 index 0000000..d897958 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_download_content_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_edit_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_edit_params.cpython-312.pyc new file mode 100644 index 0000000..7487faa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_edit_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_extend_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_extend_params.cpython-312.pyc new file mode 100644 index 0000000..a156fa0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_extend_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_get_character_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_get_character_response.cpython-312.pyc new file mode 100644 index 0000000..15b2303 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_get_character_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_list_params.cpython-312.pyc new file mode 100644 index 0000000..e1a6bba Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_model.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_model.cpython-312.pyc new file mode 100644 index 0000000..5d3b16a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_model_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_model_param.cpython-312.pyc new file mode 100644 index 0000000..d33855e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_model_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_remix_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_remix_params.cpython-312.pyc new file mode 100644 index 0000000..c2e3b09 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_remix_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_seconds.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_seconds.cpython-312.pyc new file mode 100644 index 0000000..1412776 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_seconds.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_size.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_size.cpython-312.pyc new file mode 100644 index 0000000..0719938 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/video_size.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/websocket_connection_options.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/websocket_connection_options.cpython-312.pyc new file mode 100644 index 0000000..0446885 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/websocket_connection_options.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/__pycache__/websocket_reconnection.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/__pycache__/websocket_reconnection.cpython-312.pyc new file mode 100644 index 0000000..f5ab3a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/__pycache__/websocket_reconnection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/__init__.py b/venv/lib/python3.12/site-packages/openai/types/admin/__init__.py new file mode 100644 index 0000000..273c8b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f3eab22 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__init__.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__init__.py new file mode 100644 index 0000000..d2be718 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__init__.py @@ -0,0 +1,90 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .role import Role as Role +from .group import Group as Group +from .invite import Invite as Invite +from .project import Project as Project +from .certificate import Certificate as Certificate +from .admin_api_key import AdminAPIKey as AdminAPIKey +from .role_list_params import RoleListParams as RoleListParams +from .user_list_params import UserListParams as UserListParams +from .group_list_params import GroupListParams as GroupListParams +from .organization_user import OrganizationUser as OrganizationUser +from .project_residency import ProjectResidency as ProjectResidency +from .cost_quantity_unit import CostQuantityUnit as CostQuantityUnit +from .invite_list_params import InviteListParams as InviteListParams +from .role_create_params import RoleCreateParams as RoleCreateParams +from .role_update_params import RoleUpdateParams as RoleUpdateParams +from .usage_costs_params import UsageCostsParams as UsageCostsParams +from .user_update_params import UserUpdateParams as UserUpdateParams +from .group_create_params import GroupCreateParams as GroupCreateParams +from .group_update_params import GroupUpdateParams as GroupUpdateParams +from .project_list_params import ProjectListParams as ProjectListParams +from .usage_images_params import UsageImagesParams as UsageImagesParams +from .invite_create_params import InviteCreateParams as InviteCreateParams +from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .usage_costs_response import UsageCostsResponse as UsageCostsResponse +from .user_delete_response import UserDeleteResponse as UserDeleteResponse +from .audit_log_list_params import AuditLogListParams as AuditLogListParams +from .group_delete_response import GroupDeleteResponse as GroupDeleteResponse +from .group_update_response import GroupUpdateResponse as GroupUpdateResponse +from .project_create_params import ProjectCreateParams as ProjectCreateParams +from .project_update_params import ProjectUpdateParams as ProjectUpdateParams +from .usage_images_response import UsageImagesResponse as UsageImagesResponse +from .invite_delete_response import InviteDeleteResponse as InviteDeleteResponse +from .audit_log_list_response import AuditLogListResponse as AuditLogListResponse +from .certificate_list_params import CertificateListParams as CertificateListParams +from .spend_alert_list_params import SpendAlertListParams as SpendAlertListParams +from .usage_embeddings_params import UsageEmbeddingsParams as UsageEmbeddingsParams +from .external_storage_deleted import ExternalStorageDeleted as ExternalStorageDeleted +from .organization_spend_alert import OrganizationSpendAlert as OrganizationSpendAlert +from .organization_spend_limit import OrganizationSpendLimit as OrganizationSpendLimit +from .usage_completions_params import UsageCompletionsParams as UsageCompletionsParams +from .usage_moderations_params import UsageModerationsParams as UsageModerationsParams +from .admin_api_key_list_params import AdminAPIKeyListParams as AdminAPIKeyListParams +from .certificate_create_params import CertificateCreateParams as CertificateCreateParams +from .certificate_list_response import CertificateListResponse as CertificateListResponse +from .certificate_update_params import CertificateUpdateParams as CertificateUpdateParams +from .spend_alert_create_params import SpendAlertCreateParams as SpendAlertCreateParams +from .spend_alert_update_params import SpendAlertUpdateParams as SpendAlertUpdateParams +from .spend_limit_update_params import SpendLimitUpdateParams as SpendLimitUpdateParams +from .usage_embeddings_response import UsageEmbeddingsResponse as UsageEmbeddingsResponse +from .usage_completions_response import UsageCompletionsResponse as UsageCompletionsResponse +from .usage_moderations_response import UsageModerationsResponse as UsageModerationsResponse +from .usage_vector_stores_params import UsageVectorStoresParams as UsageVectorStoresParams +from .admin_api_key_create_params import AdminAPIKeyCreateParams as AdminAPIKeyCreateParams +from .certificate_activate_params import CertificateActivateParams as CertificateActivateParams +from .certificate_delete_response import CertificateDeleteResponse as CertificateDeleteResponse +from .certificate_retrieve_params import CertificateRetrieveParams as CertificateRetrieveParams +from .organization_data_retention import OrganizationDataRetention as OrganizationDataRetention +from .usage_audio_speeches_params import UsageAudioSpeechesParams as UsageAudioSpeechesParams +from .data_retention_update_params import DataRetentionUpdateParams as DataRetentionUpdateParams +from .external_storage_list_params import ExternalStorageListParams as ExternalStorageListParams +from .usage_vector_stores_response import UsageVectorStoresResponse as UsageVectorStoresResponse +from .admin_api_key_create_response import AdminAPIKeyCreateResponse as AdminAPIKeyCreateResponse +from .admin_api_key_delete_response import AdminAPIKeyDeleteResponse as AdminAPIKeyDeleteResponse +from .aws_external_storage_provider import AwsExternalStorageProvider as AwsExternalStorageProvider +from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse +from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams +from .gcp_external_storage_provider import GcpExternalStorageProvider as GcpExternalStorageProvider +from .usage_audio_speeches_response import UsageAudioSpeechesResponse as UsageAudioSpeechesResponse +from .usage_web_search_calls_params import UsageWebSearchCallsParams as UsageWebSearchCallsParams +from .external_storage_configuration import ExternalStorageConfiguration as ExternalStorageConfiguration +from .external_storage_create_params import ExternalStorageCreateParams as ExternalStorageCreateParams +from .usage_file_search_calls_params import UsageFileSearchCallsParams as UsageFileSearchCallsParams +from .azure_external_storage_provider import AzureExternalStorageProvider as AzureExternalStorageProvider +from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse +from .usage_web_search_calls_response import UsageWebSearchCallsResponse as UsageWebSearchCallsResponse +from .organization_spend_alert_deleted import OrganizationSpendAlertDeleted as OrganizationSpendAlertDeleted +from .organization_spend_limit_deleted import OrganizationSpendLimitDeleted as OrganizationSpendLimitDeleted +from .usage_file_search_calls_response import UsageFileSearchCallsResponse as UsageFileSearchCallsResponse +from .usage_audio_transcriptions_params import UsageAudioTranscriptionsParams as UsageAudioTranscriptionsParams +from .usage_audio_transcriptions_response import UsageAudioTranscriptionsResponse as UsageAudioTranscriptionsResponse +from .usage_code_interpreter_sessions_params import ( + UsageCodeInterpreterSessionsParams as UsageCodeInterpreterSessionsParams, +) +from .usage_code_interpreter_sessions_response import ( + UsageCodeInterpreterSessionsResponse as UsageCodeInterpreterSessionsResponse, +) diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..14d9107 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key.cpython-312.pyc new file mode 100644 index 0000000..070983e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key_create_params.cpython-312.pyc new file mode 100644 index 0000000..5504f98 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key_create_response.cpython-312.pyc new file mode 100644 index 0000000..9606039 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key_delete_response.cpython-312.pyc new file mode 100644 index 0000000..29f77db Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key_list_params.cpython-312.pyc new file mode 100644 index 0000000..4299664 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/admin_api_key_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/audit_log_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/audit_log_list_params.cpython-312.pyc new file mode 100644 index 0000000..6168e50 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/audit_log_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/audit_log_list_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/audit_log_list_response.cpython-312.pyc new file mode 100644 index 0000000..e342d6f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/audit_log_list_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/aws_external_storage_provider.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/aws_external_storage_provider.cpython-312.pyc new file mode 100644 index 0000000..2064b8a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/aws_external_storage_provider.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/azure_external_storage_provider.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/azure_external_storage_provider.cpython-312.pyc new file mode 100644 index 0000000..efc9c5b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/azure_external_storage_provider.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate.cpython-312.pyc new file mode 100644 index 0000000..133bcdb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_activate_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_activate_params.cpython-312.pyc new file mode 100644 index 0000000..93789d1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_activate_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_activate_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_activate_response.cpython-312.pyc new file mode 100644 index 0000000..4ccdbd0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_activate_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_create_params.cpython-312.pyc new file mode 100644 index 0000000..1461521 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_deactivate_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_deactivate_params.cpython-312.pyc new file mode 100644 index 0000000..bac722e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_deactivate_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_deactivate_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_deactivate_response.cpython-312.pyc new file mode 100644 index 0000000..ba81718 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_deactivate_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_delete_response.cpython-312.pyc new file mode 100644 index 0000000..3898535 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_list_params.cpython-312.pyc new file mode 100644 index 0000000..fe956c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_list_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_list_response.cpython-312.pyc new file mode 100644 index 0000000..c0ac6ef Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_list_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_retrieve_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_retrieve_params.cpython-312.pyc new file mode 100644 index 0000000..e73712d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_retrieve_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_update_params.cpython-312.pyc new file mode 100644 index 0000000..e259bf8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/certificate_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/cost_quantity_unit.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/cost_quantity_unit.cpython-312.pyc new file mode 100644 index 0000000..935154e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/cost_quantity_unit.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/data_retention_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/data_retention_update_params.cpython-312.pyc new file mode 100644 index 0000000..6119ba9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/data_retention_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/external_storage_configuration.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/external_storage_configuration.cpython-312.pyc new file mode 100644 index 0000000..4707248 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/external_storage_configuration.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/external_storage_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/external_storage_create_params.cpython-312.pyc new file mode 100644 index 0000000..d82dc5a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/external_storage_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/external_storage_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/external_storage_deleted.cpython-312.pyc new file mode 100644 index 0000000..fd031c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/external_storage_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/external_storage_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/external_storage_list_params.cpython-312.pyc new file mode 100644 index 0000000..4b39c0a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/external_storage_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/gcp_external_storage_provider.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/gcp_external_storage_provider.cpython-312.pyc new file mode 100644 index 0000000..6145f32 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/gcp_external_storage_provider.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group.cpython-312.pyc new file mode 100644 index 0000000..19e5694 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_create_params.cpython-312.pyc new file mode 100644 index 0000000..3c59095 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_delete_response.cpython-312.pyc new file mode 100644 index 0000000..dc5e7ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_list_params.cpython-312.pyc new file mode 100644 index 0000000..126df9a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_update_params.cpython-312.pyc new file mode 100644 index 0000000..36a93bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_update_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_update_response.cpython-312.pyc new file mode 100644 index 0000000..8953f0d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/group_update_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/invite.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/invite.cpython-312.pyc new file mode 100644 index 0000000..9656025 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/invite.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/invite_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/invite_create_params.cpython-312.pyc new file mode 100644 index 0000000..8d3eaf4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/invite_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/invite_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/invite_delete_response.cpython-312.pyc new file mode 100644 index 0000000..3d7b937 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/invite_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/invite_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/invite_list_params.cpython-312.pyc new file mode 100644 index 0000000..88406dd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/invite_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_data_retention.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_data_retention.cpython-312.pyc new file mode 100644 index 0000000..bd0c271 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_data_retention.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_spend_alert.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_spend_alert.cpython-312.pyc new file mode 100644 index 0000000..dfe16fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_spend_alert.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_spend_alert_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_spend_alert_deleted.cpython-312.pyc new file mode 100644 index 0000000..caba2ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_spend_alert_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_spend_limit.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_spend_limit.cpython-312.pyc new file mode 100644 index 0000000..be1ad9d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_spend_limit.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_spend_limit_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_spend_limit_deleted.cpython-312.pyc new file mode 100644 index 0000000..0166647 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_spend_limit_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_user.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_user.cpython-312.pyc new file mode 100644 index 0000000..f8a5f89 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/organization_user.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project.cpython-312.pyc new file mode 100644 index 0000000..3214e36 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project_create_params.cpython-312.pyc new file mode 100644 index 0000000..5b8530b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project_list_params.cpython-312.pyc new file mode 100644 index 0000000..b2d0fe9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project_residency.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project_residency.cpython-312.pyc new file mode 100644 index 0000000..33a4e8f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project_residency.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project_update_params.cpython-312.pyc new file mode 100644 index 0000000..f62089d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/project_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role.cpython-312.pyc new file mode 100644 index 0000000..7f829c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role_create_params.cpython-312.pyc new file mode 100644 index 0000000..cb00e25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role_delete_response.cpython-312.pyc new file mode 100644 index 0000000..e7776ff Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role_list_params.cpython-312.pyc new file mode 100644 index 0000000..b717860 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role_update_params.cpython-312.pyc new file mode 100644 index 0000000..feda142 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/role_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/spend_alert_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/spend_alert_create_params.cpython-312.pyc new file mode 100644 index 0000000..754a4b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/spend_alert_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/spend_alert_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/spend_alert_list_params.cpython-312.pyc new file mode 100644 index 0000000..16622cf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/spend_alert_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/spend_alert_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/spend_alert_update_params.cpython-312.pyc new file mode 100644 index 0000000..796b13e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/spend_alert_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/spend_limit_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/spend_limit_update_params.cpython-312.pyc new file mode 100644 index 0000000..1f55cf6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/spend_limit_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_audio_speeches_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_audio_speeches_params.cpython-312.pyc new file mode 100644 index 0000000..6375bbd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_audio_speeches_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_audio_speeches_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_audio_speeches_response.cpython-312.pyc new file mode 100644 index 0000000..fef087f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_audio_speeches_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_audio_transcriptions_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_audio_transcriptions_params.cpython-312.pyc new file mode 100644 index 0000000..4a08028 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_audio_transcriptions_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_audio_transcriptions_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_audio_transcriptions_response.cpython-312.pyc new file mode 100644 index 0000000..d72d406 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_audio_transcriptions_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_code_interpreter_sessions_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_code_interpreter_sessions_params.cpython-312.pyc new file mode 100644 index 0000000..107b73c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_code_interpreter_sessions_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_code_interpreter_sessions_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_code_interpreter_sessions_response.cpython-312.pyc new file mode 100644 index 0000000..ca82183 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_code_interpreter_sessions_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_completions_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_completions_params.cpython-312.pyc new file mode 100644 index 0000000..672aa70 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_completions_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_completions_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_completions_response.cpython-312.pyc new file mode 100644 index 0000000..eecc8fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_completions_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_costs_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_costs_params.cpython-312.pyc new file mode 100644 index 0000000..a730868 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_costs_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_costs_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_costs_response.cpython-312.pyc new file mode 100644 index 0000000..77916f8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_costs_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_embeddings_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_embeddings_params.cpython-312.pyc new file mode 100644 index 0000000..7e6d761 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_embeddings_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_embeddings_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_embeddings_response.cpython-312.pyc new file mode 100644 index 0000000..c26557c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_embeddings_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_file_search_calls_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_file_search_calls_params.cpython-312.pyc new file mode 100644 index 0000000..d107648 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_file_search_calls_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_file_search_calls_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_file_search_calls_response.cpython-312.pyc new file mode 100644 index 0000000..ea207fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_file_search_calls_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_images_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_images_params.cpython-312.pyc new file mode 100644 index 0000000..9609d9b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_images_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_images_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_images_response.cpython-312.pyc new file mode 100644 index 0000000..862aaed Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_images_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_moderations_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_moderations_params.cpython-312.pyc new file mode 100644 index 0000000..48f0d75 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_moderations_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_moderations_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_moderations_response.cpython-312.pyc new file mode 100644 index 0000000..28863d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_moderations_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_vector_stores_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_vector_stores_params.cpython-312.pyc new file mode 100644 index 0000000..fcae648 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_vector_stores_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_vector_stores_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_vector_stores_response.cpython-312.pyc new file mode 100644 index 0000000..5b935a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_vector_stores_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_web_search_calls_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_web_search_calls_params.cpython-312.pyc new file mode 100644 index 0000000..538a4bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_web_search_calls_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_web_search_calls_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_web_search_calls_response.cpython-312.pyc new file mode 100644 index 0000000..ba4cb79 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/usage_web_search_calls_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/user_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/user_delete_response.cpython-312.pyc new file mode 100644 index 0000000..ad6e2a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/user_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/user_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/user_list_params.cpython-312.pyc new file mode 100644 index 0000000..955190a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/user_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/user_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/user_update_params.cpython-312.pyc new file mode 100644 index 0000000..842ca97 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/__pycache__/user_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key.py new file mode 100644 index 0000000..945236c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["AdminAPIKey", "Owner"] + + +class Owner(BaseModel): + id: Optional[str] = None + """The identifier, which can be referenced in API endpoints""" + + created_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the user was created""" + + name: Optional[str] = None + """The name of the user""" + + object: Optional[str] = None + """The object type, which is always organization.user""" + + role: Optional[str] = None + """Always `owner`""" + + type: Optional[str] = None + """Always `user`""" + + +class AdminAPIKey(BaseModel): + """Represents an individual Admin API key in an org.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + created_at: int + """The Unix timestamp (in seconds) of when the API key was created""" + + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the API key expires""" + + object: Literal["organization.admin_api_key"] + """The object type, which is always `organization.admin_api_key`""" + + owner: Owner + + redacted_value: str + """The redacted value of the API key""" + + last_used_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the API key was last used""" + + name: Optional[str] = None + """The name of the API key""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key_create_params.py new file mode 100644 index 0000000..3766a7f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["AdminAPIKeyCreateParams"] + + +class AdminAPIKeyCreateParams(TypedDict, total=False): + name: Required[str] + + expires_in_seconds: int + """The number of seconds until the API key expires. + + Omit this field for a key that does not expire. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key_create_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key_create_response.py new file mode 100644 index 0000000..1f56c43 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from .admin_api_key import AdminAPIKey + +__all__ = ["AdminAPIKeyCreateResponse"] + + +class AdminAPIKeyCreateResponse(AdminAPIKey): + """Represents an individual Admin API key in an org.""" + + value: str + """The value of the API key. Only shown on create.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key_delete_response.py new file mode 100644 index 0000000..60016a0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["AdminAPIKeyDeleteResponse"] + + +class AdminAPIKeyDeleteResponse(BaseModel): + id: str + + deleted: bool + + object: Literal["organization.admin_api_key.deleted"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key_list_params.py new file mode 100644 index 0000000..a09b7f0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/admin_api_key_list_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["AdminAPIKeyListParams"] + + +class AdminAPIKeyListParams(TypedDict, total=False): + after: Optional[str] + """Return keys with IDs that come after this ID in the pagination order.""" + + limit: int + """Maximum number of keys to return.""" + + order: Literal["asc", "desc"] + """Order results by creation time, ascending or descending.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/audit_log_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/audit_log_list_params.py new file mode 100644 index 0000000..260d8da --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/audit_log_list_params.py @@ -0,0 +1,251 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["AuditLogListParams", "EffectiveAt"] + + +class AuditLogListParams(TypedDict, total=False): + actor_emails: SequenceNotStr[str] + """Return only events performed by users with these emails.""" + + actor_ids: SequenceNotStr[str] + """Return only events performed by these actors. + + Can be a user ID, a service account ID, or an api key tracking ID. + """ + + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, starting with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + effective_at: EffectiveAt + """Return only events whose `effective_at` (Unix seconds) is in this range.""" + + event_types: List[ + Literal[ + "api_key.created", + "api_key.updated", + "api_key.deleted", + "certificate.created", + "certificate.updated", + "certificate.deleted", + "certificates.activated", + "certificates.deactivated", + "checkpoint.permission.created", + "checkpoint.permission.deleted", + "external_key.registered", + "external_key.removed", + "external_storage.registered", + "external_storage.removed", + "group.created", + "group.updated", + "group.deleted", + "invite.sent", + "invite.accepted", + "invite.deleted", + "ip_allowlist.created", + "ip_allowlist.updated", + "ip_allowlist.deleted", + "ip_allowlist.config.activated", + "ip_allowlist.config.deactivated", + "login.succeeded", + "login.failed", + "logout.succeeded", + "logout.failed", + "organization.updated", + "project.created", + "project.updated", + "project.archived", + "project.deleted", + "rate_limit.updated", + "rate_limit.deleted", + "resource.deleted", + "tunnel.created", + "tunnel.updated", + "tunnel.deleted", + "workload_identity_provider.created", + "workload_identity_provider.updated", + "workload_identity_provider.deleted", + "workload_identity_provider_mapping.created", + "workload_identity_provider_mapping.updated", + "workload_identity_provider_mapping.deleted", + "role.created", + "role.updated", + "role.deleted", + "role.assignment.created", + "role.assignment.deleted", + "role.bound_to_resource", + "role.unbound_from_resource", + "scim.enabled", + "scim.disabled", + "service_account.created", + "service_account.updated", + "service_account.deleted", + "user.added", + "user.updated", + "user.deleted", + "tenant.metadata.updated", + "tenant.microsoft_entra_mapping.upserted", + "tenant.microsoft_entra_mapping.deleted", + "tenant.workload_identity.provider.created", + "tenant.workload_identity.provider.updated", + "tenant.workload_identity.provider.archived", + "tenant.workload_identity.mapping.created", + "tenant.workload_identity.mapping.updated", + "tenant.workload_identity.mapping.archived", + "tenant.workload_identity.binding.created", + "tenant.workload_identity.principal.provisioned", + "tenant.workload_identity.access_token.issued", + "tenant.admin_api_key.created", + "tenant.admin_api_key.updated", + "tenant.admin_api_key.deleted", + "tenant.project_api_key.created", + "tenant.trusted_access.business_verification.started", + "tenant.trusted_access.application.submitted", + "tenant.chatgpt_access_token.revoked", + "tenant.migration.completed", + "tenant.sso.migrated", + "tenant.domains.migrated", + "tenant.sso_connection.created", + "tenant.sso_connection.updated", + "tenant.sso_connection.deleted", + "tenant.sso_connection.setup.started", + "tenant.policy.created", + "tenant.policy.updated", + "tenant.policy.deleted", + "tenant.policy.attached", + "tenant.policy.detached", + "tenant.principal_authentication_policy.resolved", + "tenant.scim.setup.started", + "tenant.scim.deletion.requested", + "tenant.scim.directory.created", + "tenant.product_access_policy.updated", + "tenant.resource_share_grant.created", + "tenant.resource_share_grant.updated", + "tenant.resource_share_grant.accepted", + "tenant.resource_share_grant.declined", + "tenant.resource_share_grant.revoked", + "tenant.resource_share_grant.deleted", + "tenant.service_account.updated", + "tenant.service_account.deleted", + "tenant.service_account.token.revoked", + "tenant.billing.overage_limit.updated", + "tenant.billing.alerts.updated", + "tenant.billing.info.updated", + "tenant.usage_limit.workspace.updated", + "tenant.usage_limit.group.updated", + "tenant.usage_limit.user.updated", + "tenant.usage_limit.increase_request.updated", + "tenant.usage_limit.increase_request.resolved", + "tenant.group.created", + "tenant.group.updated", + "tenant.group.deleted", + "tenant.group.member.added", + "tenant.group.member.removed", + "tenant.migration_rollout.status.updated", + "tenant.migration_rollout.tier.updated", + "tenant.role.metadata.updated", + "tenant.custom_role.created", + "tenant.custom_role.updated", + "tenant.custom_role.deleted", + "tenant.role_assignment.created", + "tenant.role_assignment.deleted", + "tenant.resource_role_assignment.created", + "tenant.resource_role_assignment.deleted", + "tenant.resource_access.updated", + "tenant.resource_access.deleted", + "tenant.ads_account.onboarding.redemption", + "tenant.session_policy.created", + "tenant.session_policy.updated", + "tenant.session_policy.deleted", + "tenant.session_revocation.started", + "tenant.third_party_app_policy.updated", + "tenant.user.added", + "tenant.user.updated", + "tenant.user.removed", + "tenant.user.looked_up", + "tenant.user.invited", + "tenant.membership.revoked", + "tenant.api_organization_invite.upserted", + "tenant.api_organization_invite.deleted", + "tenant.chatgpt_workspace_invite.upserted", + "tenant.membership.accepted", + "tenant.membership.declined", + "tenant.workspace_invite_email_settings.updated", + ] + ] + """Return only events with a `type` in one of these values. + + For example, `project.created`. For all options, see the documentation for the + [audit log object](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/audit_logs). + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + project_ids: SequenceNotStr[str] + """Return only events for these projects.""" + + resource_ids: SequenceNotStr[str] + """Return only events performed on these targets. + + For example, a project ID updated. For ChatGPT connector role events, use the + workspace connector resource ID shown in `details.id`, such as + `__`. + """ + + tenant_only: bool + """Return only tenant-scoped events associated with this organization. + + Required for tenant-scoped events such as `role.bound_to_resource` and + `role.unbound_from_resource`. When `true`, all supplied event types must be + tenant-scoped. + """ + + +class EffectiveAt(TypedDict, total=False): + """Return only events whose `effective_at` (Unix seconds) is in this range.""" + + gt: int + """ + Return only events whose `effective_at` (Unix seconds) is greater than this + value. + """ + + gte: int + """ + Return only events whose `effective_at` (Unix seconds) is greater than or equal + to this value. + """ + + lt: int + """Return only events whose `effective_at` (Unix seconds) is less than this value.""" + + lte: int + """ + Return only events whose `effective_at` (Unix seconds) is less than or equal to + this value. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/audit_log_list_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/audit_log_list_response.py new file mode 100644 index 0000000..a0686e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/audit_log_list_response.py @@ -0,0 +1,1384 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .aws_external_storage_provider import AwsExternalStorageProvider +from .gcp_external_storage_provider import GcpExternalStorageProvider +from .azure_external_storage_provider import AzureExternalStorageProvider + +__all__ = [ + "AuditLogListResponse", + "Actor", + "ActorAPIKey", + "ActorAPIKeyServiceAccount", + "ActorAPIKeyUser", + "ActorSession", + "ActorSessionUser", + "APIKeyCreated", + "APIKeyCreatedData", + "APIKeyDeleted", + "APIKeyUpdated", + "APIKeyUpdatedChangesRequested", + "CertificateCreated", + "CertificateDeleted", + "CertificateUpdated", + "CertificatesActivated", + "CertificatesActivatedCertificate", + "CertificatesDeactivated", + "CertificatesDeactivatedCertificate", + "CheckpointPermissionCreated", + "CheckpointPermissionCreatedData", + "CheckpointPermissionDeleted", + "ExternalKeyRegistered", + "ExternalKeyRemoved", + "ExternalStorageRegistered", + "ExternalStorageRegisteredData", + "ExternalStorageRegisteredDataProvider", + "ExternalStorageRemoved", + "GroupCreated", + "GroupCreatedData", + "GroupDeleted", + "GroupUpdated", + "GroupUpdatedChangesRequested", + "InviteAccepted", + "InviteDeleted", + "InviteSent", + "InviteSentData", + "IPAllowlistConfigActivated", + "IPAllowlistConfigActivatedConfig", + "IPAllowlistConfigDeactivated", + "IPAllowlistConfigDeactivatedConfig", + "IPAllowlistCreated", + "IPAllowlistDeleted", + "IPAllowlistUpdated", + "LoginFailed", + "LogoutFailed", + "OrganizationUpdated", + "OrganizationUpdatedChangesRequested", + "Project", + "ProjectArchived", + "ProjectCreated", + "ProjectCreatedData", + "ProjectDeleted", + "ProjectUpdated", + "ProjectUpdatedChangesRequested", + "RateLimitDeleted", + "RateLimitUpdated", + "RateLimitUpdatedChangesRequested", + "RoleAssignmentCreated", + "RoleAssignmentDeleted", + "RoleBoundToResource", + "RoleCreated", + "RoleDeleted", + "RoleUnboundFromResource", + "RoleUpdated", + "RoleUpdatedChangesRequested", + "ScimDisabled", + "ScimEnabled", + "ServiceAccountCreated", + "ServiceAccountCreatedData", + "ServiceAccountDeleted", + "ServiceAccountUpdated", + "ServiceAccountUpdatedChangesRequested", + "UserAdded", + "UserAddedData", + "UserDeleted", + "UserUpdated", + "UserUpdatedChangesRequested", + "WorkloadIdentityProviderMappingCreated", + "WorkloadIdentityProviderMappingDeleted", + "WorkloadIdentityProviderMappingUpdated", + "WorkloadIdentityProviderCreated", + "WorkloadIdentityProviderDeleted", + "WorkloadIdentityProviderUpdated", +] + + +class ActorAPIKeyServiceAccount(BaseModel): + """The service account that performed the audit logged action.""" + + id: Optional[str] = None + """The service account id.""" + + +class ActorAPIKeyUser(BaseModel): + """The user who performed the audit logged action.""" + + id: Optional[str] = None + """The user id.""" + + email: Optional[str] = None + """The user email.""" + + +class ActorAPIKey(BaseModel): + """The API Key used to perform the audit logged action.""" + + id: Optional[str] = None + """The tracking id of the API key.""" + + service_account: Optional[ActorAPIKeyServiceAccount] = None + """The service account that performed the audit logged action.""" + + type: Optional[Literal["user", "service_account"]] = None + """The type of API key. Can be either `user` or `service_account`.""" + + user: Optional[ActorAPIKeyUser] = None + """The user who performed the audit logged action.""" + + +class ActorSessionUser(BaseModel): + """The user who performed the audit logged action.""" + + id: Optional[str] = None + """The user id.""" + + email: Optional[str] = None + """The user email.""" + + +class ActorSession(BaseModel): + """The session in which the audit logged action was performed.""" + + ip_address: Optional[str] = None + """The IP address from which the action was performed.""" + + user: Optional[ActorSessionUser] = None + """The user who performed the audit logged action.""" + + +class Actor(BaseModel): + """The actor who performed the audit logged action.""" + + api_key: Optional[ActorAPIKey] = None + """The API Key used to perform the audit logged action.""" + + session: Optional[ActorSession] = None + """The session in which the audit logged action was performed.""" + + type: Optional[Literal["session", "api_key"]] = None + """The type of actor. Is either `session` or `api_key`.""" + + +class APIKeyCreatedData(BaseModel): + """The payload used to create the API key.""" + + scopes: Optional[List[str]] = None + """A list of scopes allowed for the API key, e.g. `["api.model.request"]`""" + + +class APIKeyCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The tracking ID of the API key.""" + + data: Optional[APIKeyCreatedData] = None + """The payload used to create the API key.""" + + +class APIKeyDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The tracking ID of the API key.""" + + +class APIKeyUpdatedChangesRequested(BaseModel): + """The payload used to update the API key.""" + + scopes: Optional[List[str]] = None + """A list of scopes allowed for the API key, e.g. `["api.model.request"]`""" + + +class APIKeyUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The tracking ID of the API key.""" + + changes_requested: Optional[APIKeyUpdatedChangesRequested] = None + """The payload used to update the API key.""" + + +class CertificateCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The certificate ID.""" + + name: Optional[str] = None + """The name of the certificate.""" + + +class CertificateDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The certificate ID.""" + + certificate: Optional[str] = None + """The certificate content in PEM format.""" + + name: Optional[str] = None + """The name of the certificate.""" + + +class CertificateUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The certificate ID.""" + + name: Optional[str] = None + """The name of the certificate.""" + + +class CertificatesActivatedCertificate(BaseModel): + id: Optional[str] = None + """The certificate ID.""" + + name: Optional[str] = None + """The name of the certificate.""" + + +class CertificatesActivated(BaseModel): + """The details for events with this `type`.""" + + certificates: Optional[List[CertificatesActivatedCertificate]] = None + + +class CertificatesDeactivatedCertificate(BaseModel): + id: Optional[str] = None + """The certificate ID.""" + + name: Optional[str] = None + """The name of the certificate.""" + + +class CertificatesDeactivated(BaseModel): + """The details for events with this `type`.""" + + certificates: Optional[List[CertificatesDeactivatedCertificate]] = None + + +class CheckpointPermissionCreatedData(BaseModel): + """The payload used to create the checkpoint permission.""" + + fine_tuned_model_checkpoint: Optional[str] = None + """The ID of the fine-tuned model checkpoint.""" + + project_id: Optional[str] = None + """The ID of the project that the checkpoint permission was created for.""" + + +class CheckpointPermissionCreated(BaseModel): + """ + The project and fine-tuned model checkpoint that the checkpoint permission was created for. + """ + + id: Optional[str] = None + """The ID of the checkpoint permission.""" + + data: Optional[CheckpointPermissionCreatedData] = None + """The payload used to create the checkpoint permission.""" + + +class CheckpointPermissionDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the checkpoint permission.""" + + +class ExternalKeyRegistered(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the external key configuration.""" + + data: Optional[object] = None + """The configuration for the external key.""" + + +class ExternalKeyRemoved(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the external key configuration.""" + + +ExternalStorageRegisteredDataProvider: TypeAlias = Annotated[ + Union[AwsExternalStorageProvider, AzureExternalStorageProvider, GcpExternalStorageProvider], + PropertyInfo(discriminator="type"), +] + + +class ExternalStorageRegisteredData(BaseModel): + """The configuration for the external storage.""" + + geography: Optional[str] = None + """The OpenAI geography derived from the storage region.""" + + provider: Optional[ExternalStorageRegisteredDataProvider] = None + """The external storage provider configuration.""" + + +class ExternalStorageRegistered(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the external storage configuration.""" + + data: Optional[ExternalStorageRegisteredData] = None + """The configuration for the external storage.""" + + +class ExternalStorageRemoved(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the external storage configuration.""" + + +class GroupCreatedData(BaseModel): + """Information about the created group.""" + + group_name: Optional[str] = None + """The group name.""" + + +class GroupCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the group.""" + + data: Optional[GroupCreatedData] = None + """Information about the created group.""" + + +class GroupDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the group.""" + + +class GroupUpdatedChangesRequested(BaseModel): + """The payload used to update the group.""" + + group_name: Optional[str] = None + """The updated group name.""" + + +class GroupUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the group.""" + + changes_requested: Optional[GroupUpdatedChangesRequested] = None + """The payload used to update the group.""" + + +class InviteAccepted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the invite.""" + + +class InviteDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the invite.""" + + +class InviteSentData(BaseModel): + """The payload used to create the invite.""" + + email: Optional[str] = None + """The email invited to the organization.""" + + role: Optional[str] = None + """The role the email was invited to be. Is either `owner` or `member`.""" + + +class InviteSent(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the invite.""" + + data: Optional[InviteSentData] = None + """The payload used to create the invite.""" + + +class IPAllowlistConfigActivatedConfig(BaseModel): + id: Optional[str] = None + """The ID of the IP allowlist configuration.""" + + name: Optional[str] = None + """The name of the IP allowlist configuration.""" + + +class IPAllowlistConfigActivated(BaseModel): + """The details for events with this `type`.""" + + configs: Optional[List[IPAllowlistConfigActivatedConfig]] = None + """The configurations that were activated.""" + + +class IPAllowlistConfigDeactivatedConfig(BaseModel): + id: Optional[str] = None + """The ID of the IP allowlist configuration.""" + + name: Optional[str] = None + """The name of the IP allowlist configuration.""" + + +class IPAllowlistConfigDeactivated(BaseModel): + """The details for events with this `type`.""" + + configs: Optional[List[IPAllowlistConfigDeactivatedConfig]] = None + """The configurations that were deactivated.""" + + +class IPAllowlistCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the IP allowlist configuration.""" + + allowed_ips: Optional[List[str]] = None + """The IP addresses or CIDR ranges included in the configuration.""" + + name: Optional[str] = None + """The name of the IP allowlist configuration.""" + + +class IPAllowlistDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the IP allowlist configuration.""" + + allowed_ips: Optional[List[str]] = None + """The IP addresses or CIDR ranges that were in the configuration.""" + + name: Optional[str] = None + """The name of the IP allowlist configuration.""" + + +class IPAllowlistUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the IP allowlist configuration.""" + + allowed_ips: Optional[List[str]] = None + """The updated set of IP addresses or CIDR ranges in the configuration.""" + + +class LoginFailed(BaseModel): + """The details for events with this `type`.""" + + error_code: Optional[str] = None + """The error code of the failure.""" + + error_message: Optional[str] = None + """The error message of the failure.""" + + +class LogoutFailed(BaseModel): + """The details for events with this `type`.""" + + error_code: Optional[str] = None + """The error code of the failure.""" + + error_message: Optional[str] = None + """The error message of the failure.""" + + +class OrganizationUpdatedChangesRequested(BaseModel): + """The payload used to update the organization settings.""" + + api_call_logging: Optional[str] = None + """How your organization logs data from supported API calls. + + One of `disabled`, `enabled_per_call`, `enabled_for_all_projects`, or + `enabled_for_selected_projects` + """ + + api_call_logging_project_ids: Optional[str] = None + """ + The list of project ids if api_call_logging is set to + `enabled_for_selected_projects` + """ + + description: Optional[str] = None + """The organization description.""" + + name: Optional[str] = None + """The organization name.""" + + threads_ui_visibility: Optional[str] = None + """ + Visibility of the threads page which shows messages created with the Assistants + API and Playground. One of `ANY_ROLE`, `OWNERS`, or `NONE`. + """ + + title: Optional[str] = None + """The organization title.""" + + usage_dashboard_visibility: Optional[str] = None + """ + Visibility of the usage dashboard which shows activity and costs for your + organization. One of `ANY_ROLE` or `OWNERS`. + """ + + +class OrganizationUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The organization ID.""" + + changes_requested: Optional[OrganizationUpdatedChangesRequested] = None + """The payload used to update the organization settings.""" + + +class Project(BaseModel): + """The project that the action was scoped to. + + Absent for actions not scoped to projects. Note that any admin actions taken via Admin API keys are associated with the default project. + """ + + id: Optional[str] = None + """The project ID.""" + + name: Optional[str] = None + """The project title.""" + + +class ProjectArchived(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The project ID.""" + + +class ProjectCreatedData(BaseModel): + """The payload used to create the project.""" + + name: Optional[str] = None + """The project name.""" + + title: Optional[str] = None + """The title of the project as seen on the dashboard.""" + + +class ProjectCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The project ID.""" + + data: Optional[ProjectCreatedData] = None + """The payload used to create the project.""" + + +class ProjectDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The project ID.""" + + +class ProjectUpdatedChangesRequested(BaseModel): + """The payload used to update the project.""" + + title: Optional[str] = None + """The title of the project as seen on the dashboard.""" + + +class ProjectUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The project ID.""" + + changes_requested: Optional[ProjectUpdatedChangesRequested] = None + """The payload used to update the project.""" + + +class RateLimitDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The rate limit ID""" + + +class RateLimitUpdatedChangesRequested(BaseModel): + """The payload used to update the rate limits.""" + + batch_1_day_max_input_tokens: Optional[int] = None + """The maximum batch input tokens per day. Only relevant for certain models.""" + + max_audio_megabytes_per_1_minute: Optional[int] = None + """The maximum audio megabytes per minute. Only relevant for certain models.""" + + max_images_per_1_minute: Optional[int] = None + """The maximum images per minute. Only relevant for certain models.""" + + max_requests_per_1_day: Optional[int] = None + """The maximum requests per day. Only relevant for certain models.""" + + max_requests_per_1_minute: Optional[int] = None + """The maximum requests per minute.""" + + max_tokens_per_1_minute: Optional[int] = None + """The maximum tokens per minute.""" + + +class RateLimitUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The rate limit ID""" + + changes_requested: Optional[RateLimitUpdatedChangesRequested] = None + """The payload used to update the rate limits.""" + + +class RoleAssignmentCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The identifier of the role assignment.""" + + principal_id: Optional[str] = None + """The principal (user or group) that received the role.""" + + principal_type: Optional[str] = None + """The type of principal (user or group) that received the role.""" + + resource_id: Optional[str] = None + """The resource the role assignment is scoped to.""" + + resource_type: Optional[str] = None + """The type of resource the role assignment is scoped to.""" + + +class RoleAssignmentDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The identifier of the role assignment.""" + + principal_id: Optional[str] = None + """The principal (user or group) that had the role removed.""" + + principal_type: Optional[str] = None + """The type of principal (user or group) that had the role removed.""" + + resource_id: Optional[str] = None + """The resource the role assignment was scoped to.""" + + resource_type: Optional[str] = None + """The type of resource the role assignment was scoped to.""" + + +class RoleBoundToResource(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the resource the role was bound to. + + ChatGPT workspace connector resources use `__`. + """ + + connector_id: Optional[str] = None + """The connector ID for a ChatGPT workspace connector resource.""" + + connector_name: Optional[str] = None + """ + The connector display name for a ChatGPT workspace connector resource, or the + connector ID when the display name could not be resolved. + """ + + enabled: Optional[bool] = None + """Whether the connector is enabled for the role.""" + + permissions: Optional[List[str]] = None + """The permissions granted to the role for the resource.""" + + resource_id: Optional[str] = None + """The ID of the resource the role was bound to.""" + + resource_type: Optional[str] = None + """The type of resource the role was bound to.""" + + role_id: Optional[str] = None + """The ID of the role that was bound to the resource.""" + + source: Optional[ + Literal["role_toggle", "role_connector_update", "role_delete", "workspace_permissions", "connector_publish"] + ] = None + """The connector role mutation path that produced the event.""" + + workspace_id: Optional[str] = None + """The workspace ID for a ChatGPT workspace connector resource.""" + + +class RoleCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The role ID.""" + + permissions: Optional[List[str]] = None + """The permissions granted by the role.""" + + resource_id: Optional[str] = None + """The resource the role is scoped to.""" + + resource_type: Optional[str] = None + """The type of resource the role belongs to.""" + + role_name: Optional[str] = None + """The name of the role.""" + + +class RoleDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The role ID.""" + + +class RoleUnboundFromResource(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the resource the role was unbound from. + + ChatGPT workspace connector resources use `__`. + """ + + connector_id: Optional[str] = None + """The connector ID for a ChatGPT workspace connector resource.""" + + connector_name: Optional[str] = None + """ + The connector display name for a ChatGPT workspace connector resource, or the + connector ID when the display name could not be resolved. + """ + + enabled: Optional[bool] = None + """Whether the connector is enabled for the role.""" + + permissions: Optional[List[str]] = None + """The permissions remaining for the role after the change.""" + + resource_id: Optional[str] = None + """The ID of the resource the role was unbound from.""" + + resource_type: Optional[str] = None + """The type of resource the role was unbound from.""" + + role_id: Optional[str] = None + """The ID of the role that was unbound from the resource.""" + + source: Optional[ + Literal["role_toggle", "role_connector_update", "role_delete", "workspace_permissions", "connector_publish"] + ] = None + """The connector role mutation path that produced the event.""" + + workspace_id: Optional[str] = None + """The workspace ID for a ChatGPT workspace connector resource.""" + + +class RoleUpdatedChangesRequested(BaseModel): + """The payload used to update the role.""" + + description: Optional[str] = None + """The updated role description, when provided.""" + + metadata: Optional[object] = None + """Additional metadata stored on the role.""" + + permissions_added: Optional[List[str]] = None + """The permissions added to the role.""" + + permissions_removed: Optional[List[str]] = None + """The permissions removed from the role.""" + + resource_id: Optional[str] = None + """The resource the role is scoped to.""" + + resource_type: Optional[str] = None + """The type of resource the role belongs to.""" + + role_name: Optional[str] = None + """The updated role name, when provided.""" + + +class RoleUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The role ID.""" + + changes_requested: Optional[RoleUpdatedChangesRequested] = None + """The payload used to update the role.""" + + +class ScimDisabled(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the SCIM was disabled for.""" + + +class ScimEnabled(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the SCIM was enabled for.""" + + +class ServiceAccountCreatedData(BaseModel): + """The payload used to create the service account.""" + + role: Optional[str] = None + """The role of the service account. Is either `owner` or `member`.""" + + +class ServiceAccountCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The service account ID.""" + + data: Optional[ServiceAccountCreatedData] = None + """The payload used to create the service account.""" + + +class ServiceAccountDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The service account ID.""" + + +class ServiceAccountUpdatedChangesRequested(BaseModel): + """The payload used to updated the service account.""" + + role: Optional[str] = None + """The role of the service account. Is either `owner` or `member`.""" + + +class ServiceAccountUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The service account ID.""" + + changes_requested: Optional[ServiceAccountUpdatedChangesRequested] = None + """The payload used to updated the service account.""" + + +class UserAddedData(BaseModel): + """The payload used to add the user to the project.""" + + role: Optional[str] = None + """The role of the user. Is either `owner` or `member`.""" + + +class UserAdded(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The user ID.""" + + data: Optional[UserAddedData] = None + """The payload used to add the user to the project.""" + + +class UserDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The user ID.""" + + +class UserUpdatedChangesRequested(BaseModel): + """The payload used to update the user.""" + + role: Optional[str] = None + """The role of the user. Is either `owner` or `member`.""" + + +class UserUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The project ID.""" + + changes_requested: Optional[UserUpdatedChangesRequested] = None + """The payload used to update the user.""" + + +class WorkloadIdentityProviderMappingCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The workload identity provider mapping ID.""" + + data: Optional[object] = None + """The payload used to create the workload identity provider mapping.""" + + identity_provider_id: Optional[str] = None + """The workload identity provider ID.""" + + +class WorkloadIdentityProviderMappingDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The workload identity provider mapping ID.""" + + identity_provider_id: Optional[str] = None + """The workload identity provider ID.""" + + project_id: Optional[str] = None + """The project ID.""" + + service_account_id: Optional[str] = None + """The mapped service account ID.""" + + +class WorkloadIdentityProviderMappingUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The workload identity provider mapping ID.""" + + changes_requested: Optional[object] = None + """The payload used to update the workload identity provider mapping.""" + + identity_provider_id: Optional[str] = None + """The workload identity provider ID.""" + + +class WorkloadIdentityProviderCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The workload identity provider ID.""" + + data: Optional[object] = None + """The payload used to create the workload identity provider.""" + + +class WorkloadIdentityProviderDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The workload identity provider ID.""" + + name: Optional[str] = None + """The workload identity provider name.""" + + +class WorkloadIdentityProviderUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The workload identity provider ID.""" + + changes_requested: Optional[object] = None + """The payload used to update the workload identity provider.""" + + +class AuditLogListResponse(BaseModel): + """A log of a user action or configuration change within this organization.""" + + id: str + """The ID of this log.""" + + effective_at: int + """The Unix timestamp (in seconds) of the event.""" + + type: Literal[ + "api_key.created", + "api_key.updated", + "api_key.deleted", + "certificate.created", + "certificate.updated", + "certificate.deleted", + "certificates.activated", + "certificates.deactivated", + "checkpoint.permission.created", + "checkpoint.permission.deleted", + "external_key.registered", + "external_key.removed", + "external_storage.registered", + "external_storage.removed", + "group.created", + "group.updated", + "group.deleted", + "invite.sent", + "invite.accepted", + "invite.deleted", + "ip_allowlist.created", + "ip_allowlist.updated", + "ip_allowlist.deleted", + "ip_allowlist.config.activated", + "ip_allowlist.config.deactivated", + "login.succeeded", + "login.failed", + "logout.succeeded", + "logout.failed", + "organization.updated", + "project.created", + "project.updated", + "project.archived", + "project.deleted", + "rate_limit.updated", + "rate_limit.deleted", + "resource.deleted", + "tunnel.created", + "tunnel.updated", + "tunnel.deleted", + "workload_identity_provider.created", + "workload_identity_provider.updated", + "workload_identity_provider.deleted", + "workload_identity_provider_mapping.created", + "workload_identity_provider_mapping.updated", + "workload_identity_provider_mapping.deleted", + "role.created", + "role.updated", + "role.deleted", + "role.assignment.created", + "role.assignment.deleted", + "role.bound_to_resource", + "role.unbound_from_resource", + "scim.enabled", + "scim.disabled", + "service_account.created", + "service_account.updated", + "service_account.deleted", + "user.added", + "user.updated", + "user.deleted", + "tenant.metadata.updated", + "tenant.microsoft_entra_mapping.upserted", + "tenant.microsoft_entra_mapping.deleted", + "tenant.workload_identity.provider.created", + "tenant.workload_identity.provider.updated", + "tenant.workload_identity.provider.archived", + "tenant.workload_identity.mapping.created", + "tenant.workload_identity.mapping.updated", + "tenant.workload_identity.mapping.archived", + "tenant.workload_identity.binding.created", + "tenant.workload_identity.principal.provisioned", + "tenant.workload_identity.access_token.issued", + "tenant.admin_api_key.created", + "tenant.admin_api_key.updated", + "tenant.admin_api_key.deleted", + "tenant.project_api_key.created", + "tenant.trusted_access.business_verification.started", + "tenant.trusted_access.application.submitted", + "tenant.chatgpt_access_token.revoked", + "tenant.migration.completed", + "tenant.sso.migrated", + "tenant.domains.migrated", + "tenant.sso_connection.created", + "tenant.sso_connection.updated", + "tenant.sso_connection.deleted", + "tenant.sso_connection.setup.started", + "tenant.policy.created", + "tenant.policy.updated", + "tenant.policy.deleted", + "tenant.policy.attached", + "tenant.policy.detached", + "tenant.principal_authentication_policy.resolved", + "tenant.scim.setup.started", + "tenant.scim.deletion.requested", + "tenant.scim.directory.created", + "tenant.product_access_policy.updated", + "tenant.resource_share_grant.created", + "tenant.resource_share_grant.updated", + "tenant.resource_share_grant.accepted", + "tenant.resource_share_grant.declined", + "tenant.resource_share_grant.revoked", + "tenant.resource_share_grant.deleted", + "tenant.service_account.updated", + "tenant.service_account.deleted", + "tenant.service_account.token.revoked", + "tenant.billing.overage_limit.updated", + "tenant.billing.alerts.updated", + "tenant.billing.info.updated", + "tenant.usage_limit.workspace.updated", + "tenant.usage_limit.group.updated", + "tenant.usage_limit.user.updated", + "tenant.usage_limit.increase_request.updated", + "tenant.usage_limit.increase_request.resolved", + "tenant.group.created", + "tenant.group.updated", + "tenant.group.deleted", + "tenant.group.member.added", + "tenant.group.member.removed", + "tenant.migration_rollout.status.updated", + "tenant.migration_rollout.tier.updated", + "tenant.role.metadata.updated", + "tenant.custom_role.created", + "tenant.custom_role.updated", + "tenant.custom_role.deleted", + "tenant.role_assignment.created", + "tenant.role_assignment.deleted", + "tenant.resource_role_assignment.created", + "tenant.resource_role_assignment.deleted", + "tenant.resource_access.updated", + "tenant.resource_access.deleted", + "tenant.ads_account.onboarding.redemption", + "tenant.session_policy.created", + "tenant.session_policy.updated", + "tenant.session_policy.deleted", + "tenant.session_revocation.started", + "tenant.third_party_app_policy.updated", + "tenant.user.added", + "tenant.user.updated", + "tenant.user.removed", + "tenant.user.looked_up", + "tenant.user.invited", + "tenant.membership.revoked", + "tenant.api_organization_invite.upserted", + "tenant.api_organization_invite.deleted", + "tenant.chatgpt_workspace_invite.upserted", + "tenant.membership.accepted", + "tenant.membership.declined", + "tenant.workspace_invite_email_settings.updated", + ] + """The event type.""" + + actor: Optional[Actor] = None + """The actor who performed the audit logged action.""" + + api_key_created: Optional[APIKeyCreated] = FieldInfo(alias="api_key.created", default=None) + """The details for events with this `type`.""" + + api_key_deleted: Optional[APIKeyDeleted] = FieldInfo(alias="api_key.deleted", default=None) + """The details for events with this `type`.""" + + api_key_updated: Optional[APIKeyUpdated] = FieldInfo(alias="api_key.updated", default=None) + """The details for events with this `type`.""" + + certificate_created: Optional[CertificateCreated] = FieldInfo(alias="certificate.created", default=None) + """The details for events with this `type`.""" + + certificate_deleted: Optional[CertificateDeleted] = FieldInfo(alias="certificate.deleted", default=None) + """The details for events with this `type`.""" + + certificate_updated: Optional[CertificateUpdated] = FieldInfo(alias="certificate.updated", default=None) + """The details for events with this `type`.""" + + certificates_activated: Optional[CertificatesActivated] = FieldInfo(alias="certificates.activated", default=None) + """The details for events with this `type`.""" + + certificates_deactivated: Optional[CertificatesDeactivated] = FieldInfo( + alias="certificates.deactivated", default=None + ) + """The details for events with this `type`.""" + + checkpoint_permission_created: Optional[CheckpointPermissionCreated] = FieldInfo( + alias="checkpoint.permission.created", default=None + ) + """ + The project and fine-tuned model checkpoint that the checkpoint permission was + created for. + """ + + checkpoint_permission_deleted: Optional[CheckpointPermissionDeleted] = FieldInfo( + alias="checkpoint.permission.deleted", default=None + ) + """The details for events with this `type`.""" + + external_key_registered: Optional[ExternalKeyRegistered] = FieldInfo(alias="external_key.registered", default=None) + """The details for events with this `type`.""" + + external_key_removed: Optional[ExternalKeyRemoved] = FieldInfo(alias="external_key.removed", default=None) + """The details for events with this `type`.""" + + external_storage_registered: Optional[ExternalStorageRegistered] = FieldInfo( + alias="external_storage.registered", default=None + ) + """The details for events with this `type`.""" + + external_storage_removed: Optional[ExternalStorageRemoved] = FieldInfo( + alias="external_storage.removed", default=None + ) + """The details for events with this `type`.""" + + group_created: Optional[GroupCreated] = FieldInfo(alias="group.created", default=None) + """The details for events with this `type`.""" + + group_deleted: Optional[GroupDeleted] = FieldInfo(alias="group.deleted", default=None) + """The details for events with this `type`.""" + + group_updated: Optional[GroupUpdated] = FieldInfo(alias="group.updated", default=None) + """The details for events with this `type`.""" + + invite_accepted: Optional[InviteAccepted] = FieldInfo(alias="invite.accepted", default=None) + """The details for events with this `type`.""" + + invite_deleted: Optional[InviteDeleted] = FieldInfo(alias="invite.deleted", default=None) + """The details for events with this `type`.""" + + invite_sent: Optional[InviteSent] = FieldInfo(alias="invite.sent", default=None) + """The details for events with this `type`.""" + + ip_allowlist_config_activated: Optional[IPAllowlistConfigActivated] = FieldInfo( + alias="ip_allowlist.config.activated", default=None + ) + """The details for events with this `type`.""" + + ip_allowlist_config_deactivated: Optional[IPAllowlistConfigDeactivated] = FieldInfo( + alias="ip_allowlist.config.deactivated", default=None + ) + """The details for events with this `type`.""" + + ip_allowlist_created: Optional[IPAllowlistCreated] = FieldInfo(alias="ip_allowlist.created", default=None) + """The details for events with this `type`.""" + + ip_allowlist_deleted: Optional[IPAllowlistDeleted] = FieldInfo(alias="ip_allowlist.deleted", default=None) + """The details for events with this `type`.""" + + ip_allowlist_updated: Optional[IPAllowlistUpdated] = FieldInfo(alias="ip_allowlist.updated", default=None) + """The details for events with this `type`.""" + + login_failed: Optional[LoginFailed] = FieldInfo(alias="login.failed", default=None) + """The details for events with this `type`.""" + + login_succeeded: Optional[object] = FieldInfo(alias="login.succeeded", default=None) + """This event has no additional fields beyond the standard audit log attributes.""" + + logout_failed: Optional[LogoutFailed] = FieldInfo(alias="logout.failed", default=None) + """The details for events with this `type`.""" + + logout_succeeded: Optional[object] = FieldInfo(alias="logout.succeeded", default=None) + """This event has no additional fields beyond the standard audit log attributes.""" + + organization_updated: Optional[OrganizationUpdated] = FieldInfo(alias="organization.updated", default=None) + """The details for events with this `type`.""" + + project: Optional[Project] = None + """The project that the action was scoped to. + + Absent for actions not scoped to projects. Note that any admin actions taken via + Admin API keys are associated with the default project. + """ + + project_archived: Optional[ProjectArchived] = FieldInfo(alias="project.archived", default=None) + """The details for events with this `type`.""" + + project_created: Optional[ProjectCreated] = FieldInfo(alias="project.created", default=None) + """The details for events with this `type`.""" + + project_deleted: Optional[ProjectDeleted] = FieldInfo(alias="project.deleted", default=None) + """The details for events with this `type`.""" + + project_updated: Optional[ProjectUpdated] = FieldInfo(alias="project.updated", default=None) + """The details for events with this `type`.""" + + rate_limit_deleted: Optional[RateLimitDeleted] = FieldInfo(alias="rate_limit.deleted", default=None) + """The details for events with this `type`.""" + + rate_limit_updated: Optional[RateLimitUpdated] = FieldInfo(alias="rate_limit.updated", default=None) + """The details for events with this `type`.""" + + role_assignment_created: Optional[RoleAssignmentCreated] = FieldInfo(alias="role.assignment.created", default=None) + """The details for events with this `type`.""" + + role_assignment_deleted: Optional[RoleAssignmentDeleted] = FieldInfo(alias="role.assignment.deleted", default=None) + """The details for events with this `type`.""" + + role_bound_to_resource: Optional[RoleBoundToResource] = FieldInfo(alias="role.bound_to_resource", default=None) + """The details for events with this `type`.""" + + role_created: Optional[RoleCreated] = FieldInfo(alias="role.created", default=None) + """The details for events with this `type`.""" + + role_deleted: Optional[RoleDeleted] = FieldInfo(alias="role.deleted", default=None) + """The details for events with this `type`.""" + + role_unbound_from_resource: Optional[RoleUnboundFromResource] = FieldInfo( + alias="role.unbound_from_resource", default=None + ) + """The details for events with this `type`.""" + + role_updated: Optional[RoleUpdated] = FieldInfo(alias="role.updated", default=None) + """The details for events with this `type`.""" + + scim_disabled: Optional[ScimDisabled] = FieldInfo(alias="scim.disabled", default=None) + """The details for events with this `type`.""" + + scim_enabled: Optional[ScimEnabled] = FieldInfo(alias="scim.enabled", default=None) + """The details for events with this `type`.""" + + service_account_created: Optional[ServiceAccountCreated] = FieldInfo(alias="service_account.created", default=None) + """The details for events with this `type`.""" + + service_account_deleted: Optional[ServiceAccountDeleted] = FieldInfo(alias="service_account.deleted", default=None) + """The details for events with this `type`.""" + + service_account_updated: Optional[ServiceAccountUpdated] = FieldInfo(alias="service_account.updated", default=None) + """The details for events with this `type`.""" + + user_added: Optional[UserAdded] = FieldInfo(alias="user.added", default=None) + """The details for events with this `type`.""" + + user_deleted: Optional[UserDeleted] = FieldInfo(alias="user.deleted", default=None) + """The details for events with this `type`.""" + + user_updated: Optional[UserUpdated] = FieldInfo(alias="user.updated", default=None) + """The details for events with this `type`.""" + + workload_identity_provider_mapping_created: Optional[WorkloadIdentityProviderMappingCreated] = FieldInfo( + alias="workload_identity_provider_mapping.created", default=None + ) + """The details for events with this `type`.""" + + workload_identity_provider_mapping_deleted: Optional[WorkloadIdentityProviderMappingDeleted] = FieldInfo( + alias="workload_identity_provider_mapping.deleted", default=None + ) + """The details for events with this `type`.""" + + workload_identity_provider_mapping_updated: Optional[WorkloadIdentityProviderMappingUpdated] = FieldInfo( + alias="workload_identity_provider_mapping.updated", default=None + ) + """The details for events with this `type`.""" + + workload_identity_provider_created: Optional[WorkloadIdentityProviderCreated] = FieldInfo( + alias="workload_identity_provider.created", default=None + ) + """The details for events with this `type`.""" + + workload_identity_provider_deleted: Optional[WorkloadIdentityProviderDeleted] = FieldInfo( + alias="workload_identity_provider.deleted", default=None + ) + """The details for events with this `type`.""" + + workload_identity_provider_updated: Optional[WorkloadIdentityProviderUpdated] = FieldInfo( + alias="workload_identity_provider.updated", default=None + ) + """The details for events with this `type`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/aws_external_storage_provider.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/aws_external_storage_provider.py new file mode 100644 index 0000000..c034d0b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/aws_external_storage_provider.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["AwsExternalStorageProvider"] + + +class AwsExternalStorageProvider(BaseModel): + account_id: str + + bucket: str + + external_id: str + + region: str + + role_arn: str + + type: Literal["aws"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/azure_external_storage_provider.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/azure_external_storage_provider.py new file mode 100644 index 0000000..3d81415 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/azure_external_storage_provider.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["AzureExternalStorageProvider"] + + +class AzureExternalStorageProvider(BaseModel): + account_name: str + + container: str + + region: str + + resource_group: str + + subscription_id: str + + tenant_id: str + + type: Literal["azure"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate.py new file mode 100644 index 0000000..beff324 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["Certificate", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + content: Optional[str] = None + """The content of the certificate in PEM format.""" + + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class Certificate(BaseModel): + """Represents an individual `certificate` uploaded to the organization.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: Optional[str] = None + """The name of the certificate.""" + + object: Literal["certificate", "organization.certificate", "organization.project.certificate"] + """The object type. + + - If creating, updating, or getting a specific certificate, the object type is + `certificate`. + - If listing, activating, or deactivating certificates for the organization, the + object type is `organization.certificate`. + - If listing, activating, or deactivating certificates for a project, the object + type is `organization.project.certificate`. + """ + + active: Optional[bool] = None + """Whether the certificate is currently active at the specified scope. + + Not returned when getting details for a specific certificate. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_activate_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_activate_params.py new file mode 100644 index 0000000..730afd3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_activate_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["CertificateActivateParams"] + + +class CertificateActivateParams(TypedDict, total=False): + certificate_ids: Required[SequenceNotStr[str]] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_activate_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_activate_response.py new file mode 100644 index 0000000..9785131 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_activate_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["CertificateActivateResponse", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class CertificateActivateResponse(BaseModel): + """Represents an individual certificate configured at the organization level.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + active: bool + """Whether the certificate is currently active at the organization level.""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: Optional[str] = None + """The name of the certificate.""" + + object: Literal["organization.certificate"] + """The object type, which is always `organization.certificate`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_create_params.py new file mode 100644 index 0000000..8527fde --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_create_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["CertificateCreateParams"] + + +class CertificateCreateParams(TypedDict, total=False): + certificate: Required[str] + """The certificate content in PEM format""" + + name: str + """An optional name for the certificate""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_deactivate_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_deactivate_params.py new file mode 100644 index 0000000..a5e1041 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_deactivate_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["CertificateDeactivateParams"] + + +class CertificateDeactivateParams(TypedDict, total=False): + certificate_ids: Required[SequenceNotStr[str]] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_deactivate_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_deactivate_response.py new file mode 100644 index 0000000..73015b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_deactivate_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["CertificateDeactivateResponse", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class CertificateDeactivateResponse(BaseModel): + """Represents an individual certificate configured at the organization level.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + active: bool + """Whether the certificate is currently active at the organization level.""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: Optional[str] = None + """The name of the certificate.""" + + object: Literal["organization.certificate"] + """The object type, which is always `organization.certificate`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_delete_response.py new file mode 100644 index 0000000..58d46e5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["CertificateDeleteResponse"] + + +class CertificateDeleteResponse(BaseModel): + id: str + """The ID of the certificate that was deleted.""" + + object: Literal["certificate.deleted"] + """The object type, must be `certificate.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_list_params.py new file mode 100644 index 0000000..b215769 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_list_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["CertificateListParams"] + + +class CertificateListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_list_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_list_response.py new file mode 100644 index 0000000..6f61def --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_list_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["CertificateListResponse", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class CertificateListResponse(BaseModel): + """Represents an individual certificate configured at the organization level.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + active: bool + """Whether the certificate is currently active at the organization level.""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: Optional[str] = None + """The name of the certificate.""" + + object: Literal["organization.certificate"] + """The object type, which is always `organization.certificate`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_retrieve_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_retrieve_params.py new file mode 100644 index 0000000..2bef2d0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_retrieve_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +__all__ = ["CertificateRetrieveParams"] + + +class CertificateRetrieveParams(TypedDict, total=False): + include: List[Literal["content"]] + """A list of additional fields to include in the response. + + Currently the only supported value is `content` to fetch the PEM content of the + certificate. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_update_params.py new file mode 100644 index 0000000..c40c8b8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/certificate_update_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["CertificateUpdateParams"] + + +class CertificateUpdateParams(TypedDict, total=False): + name: str + """The updated name for the certificate""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/cost_quantity_unit.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/cost_quantity_unit.py new file mode 100644 index 0000000..311c542 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/cost_quantity_unit.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +__all__ = ["CostQuantityUnit"] + +CostQuantityUnit: TypeAlias = Union[ + str, + Literal[ + "tokens", + "1000_tokens", + "duration_seconds", + "duration_minutes", + "duration_hours", + "gibibyte_hours", + "images", + "characters", + ], +] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/data_retention_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/data_retention_update_params.py new file mode 100644 index 0000000..47b948e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/data_retention_update_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["DataRetentionUpdateParams"] + + +class DataRetentionUpdateParams(TypedDict, total=False): + retention_type: Required[ + Literal[ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ] + ] + """The desired organization data retention type.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/external_storage_configuration.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/external_storage_configuration.py new file mode 100644 index 0000000..afb027b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/external_storage_configuration.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .aws_external_storage_provider import AwsExternalStorageProvider +from .gcp_external_storage_provider import GcpExternalStorageProvider +from .azure_external_storage_provider import AzureExternalStorageProvider + +__all__ = ["ExternalStorageConfiguration", "Provider"] + +Provider: TypeAlias = Annotated[ + Union[AwsExternalStorageProvider, AzureExternalStorageProvider, GcpExternalStorageProvider], + PropertyInfo(discriminator="type"), +] + + +class ExternalStorageConfiguration(BaseModel): + id: str + + created_at: int + + geography: str + + object: Literal["organization.external_storage"] + + project_id: str + + provider: Provider + + status: Literal["pending", "validated", "unhealthy"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/external_storage_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/external_storage_create_params.py new file mode 100644 index 0000000..41b0514 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/external_storage_create_params.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["ExternalStorageCreateParams", "Provider", "ProviderAws", "ProviderAzure", "ProviderGcp"] + + +class ExternalStorageCreateParams(TypedDict, total=False): + project_id: Required[str] + + provider: Required[Provider] + + +class ProviderAws(TypedDict, total=False): + bucket: Required[str] + + role_arn: Required[str] + + type: Required[Literal["aws"]] + + +class ProviderAzure(TypedDict, total=False): + account_name: Required[str] + + container: Required[str] + + resource_group: Required[str] + + subscription_id: Required[str] + + tenant_id: Required[str] + + type: Required[Literal["azure"]] + + +class ProviderGcp(TypedDict, total=False): + bucket: Required[str] + + type: Required[Literal["gcp"]] + + workload_identity_pool_id: Required[str] + + workload_identity_project_number: Required[str] + + workload_identity_provider_id: Required[str] + + +Provider: TypeAlias = Union[ProviderAws, ProviderAzure, ProviderGcp] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/external_storage_deleted.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/external_storage_deleted.py new file mode 100644 index 0000000..fe73284 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/external_storage_deleted.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ExternalStorageDeleted"] + + +class ExternalStorageDeleted(BaseModel): + id: str + + deleted: bool + + object: Literal["organization.external_storage.deleted"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/external_storage_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/external_storage_list_params.py new file mode 100644 index 0000000..05a0b36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/external_storage_list_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["ExternalStorageListParams"] + + +class ExternalStorageListParams(TypedDict, total=False): + after: Optional[str] + """Return external storage configurations after this ID.""" + + limit: int + + order: Literal["asc", "desc"] + + project_id: Optional[str] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/gcp_external_storage_provider.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/gcp_external_storage_provider.py new file mode 100644 index 0000000..0b6c5ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/gcp_external_storage_provider.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["GcpExternalStorageProvider"] + + +class GcpExternalStorageProvider(BaseModel): + audience: str + + bucket: str + + region: str + + type: Literal["gcp"] + + workload_identity_pool_id: str + + workload_identity_project_number: str + + workload_identity_provider_id: str diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/group.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/group.py new file mode 100644 index 0000000..7b066bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/group.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["Group"] + + +class Group(BaseModel): + """Details about an organization group.""" + + id: str + """Identifier for the group.""" + + created_at: int + """Unix timestamp (in seconds) when the group was created.""" + + group_type: Literal["group", "tenant_group"] + """The type of the group.""" + + is_scim_managed: bool + """ + Whether the group is managed through SCIM and controlled by your identity + provider. + """ + + name: str + """Display name of the group.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_create_params.py new file mode 100644 index 0000000..80d3930 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["GroupCreateParams"] + + +class GroupCreateParams(TypedDict, total=False): + name: Required[str] + """Human readable name for the group.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_delete_response.py new file mode 100644 index 0000000..d8f303c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_delete_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["GroupDeleteResponse"] + + +class GroupDeleteResponse(BaseModel): + """Confirmation payload returned after deleting a group.""" + + id: str + """Identifier of the deleted group.""" + + deleted: bool + """Whether the group was deleted.""" + + object: Literal["group.deleted"] + """Always `group.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_list_params.py new file mode 100644 index 0000000..6dcece1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_list_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["GroupListParams"] + + +class GroupListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is a group ID that defines your place in the list. For instance, if you + make a list request and receive 100 objects, ending with group_abc, your + subsequent call can include `after=group_abc` in order to fetch the next page of + the list. + """ + + limit: int + """A limit on the number of groups to be returned. + + Limit can range between 0 and 1000, and the default is 100. + """ + + order: Literal["asc", "desc"] + """Specifies the sort order of the returned groups.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_update_params.py new file mode 100644 index 0000000..f678602 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_update_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["GroupUpdateParams"] + + +class GroupUpdateParams(TypedDict, total=False): + name: Required[str] + """New display name for the group.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_update_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_update_response.py new file mode 100644 index 0000000..6a576bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/group_update_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ...._models import BaseModel + +__all__ = ["GroupUpdateResponse"] + + +class GroupUpdateResponse(BaseModel): + """Response returned after updating a group.""" + + id: str + """Identifier for the group.""" + + created_at: int + """Unix timestamp (in seconds) when the group was created.""" + + is_scim_managed: bool + """ + Whether the group is managed through SCIM and controlled by your identity + provider. + """ + + name: str + """Updated display name for the group.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__init__.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__init__.py new file mode 100644 index 0000000..9364b8c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__init__.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .role_list_params import RoleListParams as RoleListParams +from .user_list_params import UserListParams as UserListParams +from .role_create_params import RoleCreateParams as RoleCreateParams +from .role_list_response import RoleListResponse as RoleListResponse +from .user_create_params import UserCreateParams as UserCreateParams +from .role_create_response import RoleCreateResponse as RoleCreateResponse +from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .user_create_response import UserCreateResponse as UserCreateResponse +from .user_delete_response import UserDeleteResponse as UserDeleteResponse +from .role_retrieve_response import RoleRetrieveResponse as RoleRetrieveResponse +from .user_retrieve_response import UserRetrieveResponse as UserRetrieveResponse +from .organization_group_user import OrganizationGroupUser as OrganizationGroupUser diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1f3befa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/organization_group_user.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/organization_group_user.cpython-312.pyc new file mode 100644 index 0000000..932f75a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/organization_group_user.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_create_params.cpython-312.pyc new file mode 100644 index 0000000..fb45f56 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_create_response.cpython-312.pyc new file mode 100644 index 0000000..f0ee62f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_delete_response.cpython-312.pyc new file mode 100644 index 0000000..334d202 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_list_params.cpython-312.pyc new file mode 100644 index 0000000..7e9885c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_list_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_list_response.cpython-312.pyc new file mode 100644 index 0000000..32ce511 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_list_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_retrieve_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_retrieve_response.cpython-312.pyc new file mode 100644 index 0000000..9a2ed9f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/role_retrieve_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_create_params.cpython-312.pyc new file mode 100644 index 0000000..6aef157 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_create_response.cpython-312.pyc new file mode 100644 index 0000000..31c9e9e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_delete_response.cpython-312.pyc new file mode 100644 index 0000000..142ecaa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_list_params.cpython-312.pyc new file mode 100644 index 0000000..b9580e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_retrieve_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_retrieve_response.cpython-312.pyc new file mode 100644 index 0000000..725c80f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/__pycache__/user_retrieve_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/organization_group_user.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/organization_group_user.py new file mode 100644 index 0000000..7b0f4e8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/organization_group_user.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ....._models import BaseModel + +__all__ = ["OrganizationGroupUser"] + + +class OrganizationGroupUser(BaseModel): + """Represents an individual user returned when inspecting group membership.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + email: Optional[str] = None + """The email address of the user.""" + + name: str + """The name of the user.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_create_params.py new file mode 100644 index 0000000..5c676ce --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RoleCreateParams"] + + +class RoleCreateParams(TypedDict, total=False): + role_id: Required[str] + """Identifier of the role to assign.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_create_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_create_response.py new file mode 100644 index 0000000..5bcc888 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_create_response.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..role import Role +from ....._models import BaseModel + +__all__ = ["RoleCreateResponse", "Group"] + + +class Group(BaseModel): + """Summary information about a group returned in role assignment responses.""" + + id: str + """Identifier for the group.""" + + created_at: int + """Unix timestamp (in seconds) when the group was created.""" + + name: str + """Display name of the group.""" + + object: Literal["group"] + """Always `group`.""" + + scim_managed: bool + """Whether the group is managed through SCIM.""" + + +class RoleCreateResponse(BaseModel): + """Role assignment linking a group to a role.""" + + group: Group + """Summary information about a group returned in role assignment responses.""" + + object: Literal["group.role"] + """Always `group.role`.""" + + role: Role + """Details about a role that can be assigned through the public Roles API.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_delete_response.py new file mode 100644 index 0000000..7368e73 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_delete_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ....._models import BaseModel + +__all__ = ["RoleDeleteResponse"] + + +class RoleDeleteResponse(BaseModel): + """Confirmation payload returned after unassigning a role.""" + + deleted: bool + """Whether the assignment was removed.""" + + object: str + """ + Identifier for the deleted assignment, such as `group.role.deleted` or + `user.role.deleted`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_list_params.py new file mode 100644 index 0000000..a40ea7d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["RoleListParams"] + + +class RoleListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the value from the previous response's `next` field to continue listing + organization roles. + """ + + limit: int + """A limit on the number of organization role assignments to return.""" + + order: Literal["asc", "desc"] + """Sort order for the returned organization roles.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_list_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_list_response.py new file mode 100644 index 0000000..3d3096f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_list_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ....._models import BaseModel + +__all__ = ["RoleListResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleListResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_retrieve_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_retrieve_response.py new file mode 100644 index 0000000..6b1638a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/role_retrieve_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ....._models import BaseModel + +__all__ = ["RoleRetrieveResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleRetrieveResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_create_params.py new file mode 100644 index 0000000..1ad4d91 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["UserCreateParams"] + + +class UserCreateParams(TypedDict, total=False): + user_id: Required[str] + """Identifier of the user to add to the group.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_create_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_create_response.py new file mode 100644 index 0000000..893ca15 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_create_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["UserCreateResponse"] + + +class UserCreateResponse(BaseModel): + """Confirmation payload returned after adding a user to a group.""" + + group_id: str + """Identifier of the group the user was added to.""" + + object: Literal["group.user"] + """Always `group.user`.""" + + user_id: str + """Identifier of the user that was added.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_delete_response.py new file mode 100644 index 0000000..3416a4c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_delete_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["UserDeleteResponse"] + + +class UserDeleteResponse(BaseModel): + """Confirmation payload returned after removing a user from a group.""" + + deleted: bool + """Whether the group membership was removed.""" + + object: Literal["group.user.deleted"] + """Always `group.user.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_list_params.py new file mode 100644 index 0000000..824bdd7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_list_params.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["UserListParams"] + + +class UserListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + Provide the ID of the last user from the previous list response to retrieve the + next page. + """ + + limit: int + """A limit on the number of users to be returned. + + Limit can range between 0 and 1000, and the default is 100. + """ + + order: Literal["asc", "desc"] + """Specifies the sort order of users in the list.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_retrieve_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_retrieve_response.py new file mode 100644 index 0000000..94acfd3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/groups/user_retrieve_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["UserRetrieveResponse"] + + +class UserRetrieveResponse(BaseModel): + """Details about a user returned from an organization group membership lookup.""" + + id: str + """Identifier for the user.""" + + email: Optional[str] = None + """Email address of the user, or `null` for users without an email.""" + + is_service_account: Optional[bool] = None + """Whether the user is a service account.""" + + name: str + """Display name of the user.""" + + picture: Optional[str] = None + """URL of the user's profile picture, if available.""" + + user_type: Literal["user", "tenant_user"] + """The type of user.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/invite.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/invite.py new file mode 100644 index 0000000..31eca26 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/invite.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["Invite", "Project"] + + +class Project(BaseModel): + id: str + """Project's public ID""" + + role: Literal["member", "owner"] + """Project membership role""" + + +class Invite(BaseModel): + """Represents an individual `invite` to the organization.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + created_at: int + """The Unix timestamp (in seconds) of when the invite was sent.""" + + email: str + """The email address of the individual to whom the invite was sent""" + + object: Literal["organization.invite"] + """The object type, which is always `organization.invite`""" + + projects: List[Project] + """The projects that were granted membership upon acceptance of the invite.""" + + role: Literal["owner", "reader"] + """`owner` or `reader`""" + + status: Literal["accepted", "expired", "pending"] + """`accepted`,`expired`, or `pending`""" + + accepted_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the invite was accepted.""" + + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the invite expires.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/invite_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/invite_create_params.py new file mode 100644 index 0000000..b06320f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/invite_create_params.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InviteCreateParams", "Project"] + + +class InviteCreateParams(TypedDict, total=False): + email: Required[str] + """Send an email to this address""" + + role: Required[Literal["reader", "owner"]] + """`owner` or `reader`""" + + projects: Iterable[Project] + """ + An array of projects to which membership is granted at the same time the org + invite is accepted. If omitted, the user will be invited to the default project + for compatibility with legacy behavior. If empty list is passed, the user will + not be invited to any projects, including the default one. + """ + + +class Project(TypedDict, total=False): + id: Required[str] + """Project's public ID""" + + role: Required[Literal["member", "owner"]] + """Project membership role""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/invite_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/invite_delete_response.py new file mode 100644 index 0000000..47f5aa2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/invite_delete_response.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InviteDeleteResponse"] + + +class InviteDeleteResponse(BaseModel): + id: str + + deleted: bool + + object: Literal["organization.invite.deleted"] + """The object type, which is always `organization.invite.deleted`""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/invite_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/invite_list_params.py new file mode 100644 index 0000000..7be8e8b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/invite_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["InviteListParams"] + + +class InviteListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_data_retention.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_data_retention.py new file mode 100644 index 0000000..e7fe5da --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_data_retention.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationDataRetention"] + + +class OrganizationDataRetention(BaseModel): + """Represents the organization's data retention control setting.""" + + object: Literal["organization.data_retention"] + """The object type, which is always `organization.data_retention`.""" + + type: Literal[ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ] + """The configured organization data retention type.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_spend_alert.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_spend_alert.py new file mode 100644 index 0000000..5077243 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_spend_alert.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationSpendAlert", "NotificationChannel"] + + +class NotificationChannel(BaseModel): + """Email notification settings for a spend alert.""" + + recipients: List[str] + """Email addresses that receive the spend alert notification.""" + + type: Literal["email"] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] = None + """Optional subject prefix for alert emails.""" + + +class OrganizationSpendAlert(BaseModel): + """Represents a spend alert configured at the organization level.""" + + id: str + """The identifier, which can be referenced in API endpoints.""" + + currency: Literal["USD"] + """The currency for the threshold amount.""" + + interval: Literal["month"] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: NotificationChannel + """Email notification settings for a spend alert.""" + + object: Literal["organization.spend_alert"] + """The object type, which is always `organization.spend_alert`.""" + + threshold_amount: int + """The alert threshold amount, in cents.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_spend_alert_deleted.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_spend_alert_deleted.py new file mode 100644 index 0000000..04ab4e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_spend_alert_deleted.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationSpendAlertDeleted"] + + +class OrganizationSpendAlertDeleted(BaseModel): + """Confirmation payload returned after deleting an organization spend alert.""" + + id: str + """The deleted spend alert ID.""" + + deleted: bool + """Whether the spend alert was deleted.""" + + object: Literal["organization.spend_alert.deleted"] + """Always `organization.spend_alert.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_spend_limit.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_spend_limit.py new file mode 100644 index 0000000..3c4197b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_spend_limit.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationSpendLimit", "Enforcement"] + + +class Enforcement(BaseModel): + """The current enforcement state of the hard spend limit.""" + + status: Union[str, Literal["inactive", "enforcing"]] + """Whether the hard spend limit is currently enforcing.""" + + +class OrganizationSpendLimit(BaseModel): + """Represents a hard spend limit configured at the organization level.""" + + currency: Union[str, Literal["USD"]] + """The currency for the threshold amount. Currently, only `USD` is supported.""" + + enforcement: Enforcement + """The current enforcement state of the hard spend limit.""" + + interval: Union[str, Literal["month"]] + """The time interval for evaluating spend against the threshold. + + Currently, only `month` is supported. + """ + + object: Literal["organization.spend_limit"] + """The object type, which is always `organization.spend_limit`.""" + + threshold_amount: int + """The hard spend limit amount, in cents.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_spend_limit_deleted.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_spend_limit_deleted.py new file mode 100644 index 0000000..6bd77d2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_spend_limit_deleted.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationSpendLimitDeleted"] + + +class OrganizationSpendLimitDeleted(BaseModel): + """Confirmation payload returned after deleting an organization hard spend limit.""" + + deleted: bool + """Whether the hard spend limit was deleted.""" + + object: Literal["organization.spend_limit.deleted"] + """The object type, which is always `organization.spend_limit.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_user.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_user.py new file mode 100644 index 0000000..8452371 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/organization_user.py @@ -0,0 +1,96 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationUser", "Projects", "ProjectsData", "User"] + + +class ProjectsData(BaseModel): + id: Optional[str] = None + + name: Optional[str] = None + + role: Optional[str] = None + + +class Projects(BaseModel): + """Projects associated with the user, if included.""" + + data: List[ProjectsData] + + object: Literal["list"] + + +class User(BaseModel): + """Nested user details.""" + + id: str + + object: Literal["user"] + + banned: Optional[bool] = None + + banned_at: Optional[int] = None + + email: Optional[str] = None + + enabled: Optional[bool] = None + + name: Optional[str] = None + + picture: Optional[str] = None + + +class OrganizationUser(BaseModel): + """Represents an individual `user` within an organization.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + added_at: int + """The Unix timestamp (in seconds) of when the user was added.""" + + object: Literal["organization.user"] + """The object type, which is always `organization.user`""" + + api_key_last_used_at: Optional[int] = None + """The Unix timestamp (in seconds) of the user's last API key usage.""" + + created: Optional[int] = None + """The Unix timestamp (in seconds) of when the user was created.""" + + developer_persona: Optional[str] = None + """The developer persona metadata for the user.""" + + email: Optional[str] = None + """The email address of the user""" + + is_default: Optional[bool] = None + """Whether this is the organization's default user.""" + + is_scale_tier_authorized_purchaser: Optional[bool] = None + """Whether the user is an authorized purchaser for Scale Tier.""" + + is_scim_managed: Optional[bool] = None + """Whether the user is managed through SCIM.""" + + is_service_account: Optional[bool] = None + """Whether the user is a service account.""" + + name: Optional[str] = None + """The name of the user""" + + projects: Optional[Projects] = None + """Projects associated with the user, if included.""" + + role: Optional[str] = None + """`owner` or `reader`""" + + technical_level: Optional[str] = None + """The technical level metadata for the user.""" + + user: Optional[User] = None + """Nested user details.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/project.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/project.py new file mode 100644 index 0000000..a0f5116 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/project.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .project_residency import ProjectResidency + +__all__ = ["Project"] + + +class Project(BaseModel): + """Represents an individual project.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + created_at: int + """The Unix timestamp (in seconds) of when the project was created.""" + + object: Literal["organization.project"] + """The object type, which is always `organization.project`""" + + archived_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the project was archived or `null`.""" + + external_key_id: Optional[str] = None + """The external key associated with the project.""" + + name: Optional[str] = None + """The name of the project. This appears in reporting.""" + + residency: Optional[ProjectResidency] = None + """The residency configuration for the project.""" + + status: Optional[str] = None + """`active` or `archived`""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/project_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/project_create_params.py new file mode 100644 index 0000000..4893a8e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/project_create_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from .project_residency import ProjectResidency + +__all__ = ["ProjectCreateParams"] + + +class ProjectCreateParams(TypedDict, total=False): + name: Required[str] + """The friendly name of the project, this name appears in reports.""" + + external_key_id: Optional[str] + """External key ID to associate with the project.""" + + geography: Optional[str] + """Create the project with the specified data residency region. + + Your organization must have access to Data residency functionality in order to + use. See + [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) + to review the functionality and limitations of setting this field. Deprecated: + use `residency` instead. Do not provide both `geography` and `residency`. + """ + + residency: Optional[ProjectResidency] + """Create the project with the specified residency configuration. + + Your organization must have access to the requested residency configuration in + order to use it. See + [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) + to review the functionality and limitations of setting this field. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/project_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/project_list_params.py new file mode 100644 index 0000000..3f4c965 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/project_list_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ProjectListParams"] + + +class ProjectListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + include_archived: bool + """If `true` returns all projects including those that have been `archived`. + + Archived projects are not included by default. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/project_residency.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/project_residency.py new file mode 100644 index 0000000..93b506d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/project_residency.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ProjectResidency"] + +ProjectResidency: TypeAlias = Literal[ + "GLOBAL", + "US_STORAGE_PROCESSING", + "EU_STORAGE_PROCESSING", + "JP_STORAGE", + "KR_STORAGE", + "CA_STORAGE", + "SG_STORAGE", + "IN_STORAGE", + "AU_STORAGE", + "GB_STORAGE", + "AE_STORAGE", + "AE_STORAGE_PROCESSING", +] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/project_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/project_update_params.py new file mode 100644 index 0000000..a16d25c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/project_update_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["ProjectUpdateParams"] + + +class ProjectUpdateParams(TypedDict, total=False): + external_key_id: Optional[str] + """External key ID to associate with the project.""" + + geography: Optional[str] + """ + Geography for the project. Deprecated: use `residency` when creating a project + to configure data residency. This field is retained for backward compatibility. + """ + + name: Optional[str] + """The updated name of the project, this name appears in reports.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__init__.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__init__.py new file mode 100644 index 0000000..a067cf6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__init__.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .project_user import ProjectUser as ProjectUser +from .project_group import ProjectGroup as ProjectGroup +from .project_api_key import ProjectAPIKey as ProjectAPIKey +from .role_list_params import RoleListParams as RoleListParams +from .user_list_params import UserListParams as UserListParams +from .group_list_params import GroupListParams as GroupListParams +from .project_rate_limit import ProjectRateLimit as ProjectRateLimit +from .role_create_params import RoleCreateParams as RoleCreateParams +from .role_update_params import RoleUpdateParams as RoleUpdateParams +from .user_create_params import UserCreateParams as UserCreateParams +from .user_update_params import UserUpdateParams as UserUpdateParams +from .api_key_list_params import APIKeyListParams as APIKeyListParams +from .group_create_params import GroupCreateParams as GroupCreateParams +from .project_spend_alert import ProjectSpendAlert as ProjectSpendAlert +from .project_spend_limit import ProjectSpendLimit as ProjectSpendLimit +from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .user_delete_response import UserDeleteResponse as UserDeleteResponse +from .group_delete_response import GroupDeleteResponse as GroupDeleteResponse +from .group_retrieve_params import GroupRetrieveParams as GroupRetrieveParams +from .project_data_retention import ProjectDataRetention as ProjectDataRetention +from .api_key_delete_response import APIKeyDeleteResponse as APIKeyDeleteResponse +from .certificate_list_params import CertificateListParams as CertificateListParams +from .project_service_account import ProjectServiceAccount as ProjectServiceAccount +from .spend_alert_list_params import SpendAlertListParams as SpendAlertListParams +from .certificate_list_response import CertificateListResponse as CertificateListResponse +from .project_model_permissions import ProjectModelPermissions as ProjectModelPermissions +from .spend_alert_create_params import SpendAlertCreateParams as SpendAlertCreateParams +from .spend_alert_update_params import SpendAlertUpdateParams as SpendAlertUpdateParams +from .spend_limit_update_params import SpendLimitUpdateParams as SpendLimitUpdateParams +from .certificate_activate_params import CertificateActivateParams as CertificateActivateParams +from .project_spend_alert_deleted import ProjectSpendAlertDeleted as ProjectSpendAlertDeleted +from .project_spend_limit_deleted import ProjectSpendLimitDeleted as ProjectSpendLimitDeleted +from .service_account_list_params import ServiceAccountListParams as ServiceAccountListParams +from .data_retention_update_params import DataRetentionUpdateParams as DataRetentionUpdateParams +from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse +from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams +from .service_account_create_params import ServiceAccountCreateParams as ServiceAccountCreateParams +from .service_account_update_params import ServiceAccountUpdateParams as ServiceAccountUpdateParams +from .model_permission_update_params import ModelPermissionUpdateParams as ModelPermissionUpdateParams +from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse +from .project_hosted_tool_permissions import ProjectHostedToolPermissions as ProjectHostedToolPermissions +from .service_account_create_response import ServiceAccountCreateResponse as ServiceAccountCreateResponse +from .service_account_delete_response import ServiceAccountDeleteResponse as ServiceAccountDeleteResponse +from .project_model_permissions_deleted import ProjectModelPermissionsDeleted as ProjectModelPermissionsDeleted +from .rate_limit_list_rate_limits_params import RateLimitListRateLimitsParams as RateLimitListRateLimitsParams +from .rate_limit_update_rate_limit_params import RateLimitUpdateRateLimitParams as RateLimitUpdateRateLimitParams +from .hosted_tool_permission_update_params import HostedToolPermissionUpdateParams as HostedToolPermissionUpdateParams diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4c133c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/api_key_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/api_key_delete_response.cpython-312.pyc new file mode 100644 index 0000000..5178ae2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/api_key_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/api_key_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/api_key_list_params.cpython-312.pyc new file mode 100644 index 0000000..3a7b31e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/api_key_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_activate_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_activate_params.cpython-312.pyc new file mode 100644 index 0000000..51ff175 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_activate_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_activate_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_activate_response.cpython-312.pyc new file mode 100644 index 0000000..631700a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_activate_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_deactivate_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_deactivate_params.cpython-312.pyc new file mode 100644 index 0000000..9829983 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_deactivate_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_deactivate_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_deactivate_response.cpython-312.pyc new file mode 100644 index 0000000..f84bad4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_deactivate_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_list_params.cpython-312.pyc new file mode 100644 index 0000000..3908f04 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_list_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_list_response.cpython-312.pyc new file mode 100644 index 0000000..348fdf0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/certificate_list_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/data_retention_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/data_retention_update_params.cpython-312.pyc new file mode 100644 index 0000000..15d8de1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/data_retention_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/group_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/group_create_params.cpython-312.pyc new file mode 100644 index 0000000..84fd0b1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/group_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/group_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/group_delete_response.cpython-312.pyc new file mode 100644 index 0000000..fafc7b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/group_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/group_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/group_list_params.cpython-312.pyc new file mode 100644 index 0000000..b37d361 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/group_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/group_retrieve_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/group_retrieve_params.cpython-312.pyc new file mode 100644 index 0000000..121aef1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/group_retrieve_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/hosted_tool_permission_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/hosted_tool_permission_update_params.cpython-312.pyc new file mode 100644 index 0000000..32822fb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/hosted_tool_permission_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/model_permission_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/model_permission_update_params.cpython-312.pyc new file mode 100644 index 0000000..edb7c19 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/model_permission_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_api_key.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_api_key.cpython-312.pyc new file mode 100644 index 0000000..4358861 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_api_key.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_data_retention.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_data_retention.cpython-312.pyc new file mode 100644 index 0000000..544ab97 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_data_retention.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_group.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_group.cpython-312.pyc new file mode 100644 index 0000000..b323d17 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_group.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_hosted_tool_permissions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_hosted_tool_permissions.cpython-312.pyc new file mode 100644 index 0000000..4313b13 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_hosted_tool_permissions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_model_permissions.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_model_permissions.cpython-312.pyc new file mode 100644 index 0000000..e5f346b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_model_permissions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_model_permissions_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_model_permissions_deleted.cpython-312.pyc new file mode 100644 index 0000000..a8ac2e5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_model_permissions_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_rate_limit.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_rate_limit.cpython-312.pyc new file mode 100644 index 0000000..96d6ba9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_rate_limit.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_service_account.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_service_account.cpython-312.pyc new file mode 100644 index 0000000..6ccf855 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_service_account.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_spend_alert.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_spend_alert.cpython-312.pyc new file mode 100644 index 0000000..7e880a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_spend_alert.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_spend_alert_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_spend_alert_deleted.cpython-312.pyc new file mode 100644 index 0000000..a10d6f7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_spend_alert_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_spend_limit.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_spend_limit.cpython-312.pyc new file mode 100644 index 0000000..a51694a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_spend_limit.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_spend_limit_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_spend_limit_deleted.cpython-312.pyc new file mode 100644 index 0000000..12c5645 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_spend_limit_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_user.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_user.cpython-312.pyc new file mode 100644 index 0000000..c3ff7bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/project_user.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/rate_limit_list_rate_limits_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/rate_limit_list_rate_limits_params.cpython-312.pyc new file mode 100644 index 0000000..91c9879 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/rate_limit_list_rate_limits_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/rate_limit_update_rate_limit_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/rate_limit_update_rate_limit_params.cpython-312.pyc new file mode 100644 index 0000000..64aea0b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/rate_limit_update_rate_limit_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/role_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/role_create_params.cpython-312.pyc new file mode 100644 index 0000000..2d79665 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/role_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/role_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/role_delete_response.cpython-312.pyc new file mode 100644 index 0000000..31ea892 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/role_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/role_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/role_list_params.cpython-312.pyc new file mode 100644 index 0000000..69a64cf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/role_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/role_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/role_update_params.cpython-312.pyc new file mode 100644 index 0000000..5cfe331 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/role_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_create_params.cpython-312.pyc new file mode 100644 index 0000000..a501620 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_create_response.cpython-312.pyc new file mode 100644 index 0000000..e51487b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_delete_response.cpython-312.pyc new file mode 100644 index 0000000..c183bfc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_list_params.cpython-312.pyc new file mode 100644 index 0000000..0239643 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_update_params.cpython-312.pyc new file mode 100644 index 0000000..7d2af37 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/service_account_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/spend_alert_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/spend_alert_create_params.cpython-312.pyc new file mode 100644 index 0000000..2630a2d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/spend_alert_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/spend_alert_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/spend_alert_list_params.cpython-312.pyc new file mode 100644 index 0000000..851cdfd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/spend_alert_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/spend_alert_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/spend_alert_update_params.cpython-312.pyc new file mode 100644 index 0000000..07bb30e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/spend_alert_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/spend_limit_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/spend_limit_update_params.cpython-312.pyc new file mode 100644 index 0000000..69c2b6a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/spend_limit_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/user_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/user_create_params.cpython-312.pyc new file mode 100644 index 0000000..22a44fd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/user_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/user_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/user_delete_response.cpython-312.pyc new file mode 100644 index 0000000..a685cfc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/user_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/user_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/user_list_params.cpython-312.pyc new file mode 100644 index 0000000..9292f0c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/user_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/user_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/user_update_params.cpython-312.pyc new file mode 100644 index 0000000..2b1cd47 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/__pycache__/user_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/api_key_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/api_key_delete_response.py new file mode 100644 index 0000000..59e3347 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/api_key_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["APIKeyDeleteResponse"] + + +class APIKeyDeleteResponse(BaseModel): + id: str + + deleted: bool + + object: Literal["organization.project.api_key.deleted"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/api_key_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/api_key_list_params.py new file mode 100644 index 0000000..a4f49e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/api_key_list_params.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["APIKeyListParams"] + + +class APIKeyListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + owner_project_access: Literal["active", "inactive", "any"] + """ + Filter API keys by whether the owner currently has effective access to the + project. Use `active` for owners with access, `inactive` for owners without + access, or `any` for all enabled project API keys. If omitted, the endpoint + applies its existing membership-based visibility rules, which may exclude some + enabled keys. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_activate_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_activate_params.py new file mode 100644 index 0000000..22d08c1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_activate_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["CertificateActivateParams"] + + +class CertificateActivateParams(TypedDict, total=False): + certificate_ids: Required[SequenceNotStr[str]] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_activate_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_activate_response.py new file mode 100644 index 0000000..140fa8b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_activate_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["CertificateActivateResponse", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class CertificateActivateResponse(BaseModel): + """Represents an individual certificate configured at the project level.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + active: bool + """Whether the certificate is currently active at the project level.""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: Optional[str] = None + """The name of the certificate.""" + + object: Literal["organization.project.certificate"] + """The object type, which is always `organization.project.certificate`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_deactivate_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_deactivate_params.py new file mode 100644 index 0000000..fc18d25 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_deactivate_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["CertificateDeactivateParams"] + + +class CertificateDeactivateParams(TypedDict, total=False): + certificate_ids: Required[SequenceNotStr[str]] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_deactivate_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_deactivate_response.py new file mode 100644 index 0000000..2734111 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_deactivate_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["CertificateDeactivateResponse", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class CertificateDeactivateResponse(BaseModel): + """Represents an individual certificate configured at the project level.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + active: bool + """Whether the certificate is currently active at the project level.""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: Optional[str] = None + """The name of the certificate.""" + + object: Literal["organization.project.certificate"] + """The object type, which is always `organization.project.certificate`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_list_params.py new file mode 100644 index 0000000..b215769 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_list_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["CertificateListParams"] + + +class CertificateListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_list_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_list_response.py new file mode 100644 index 0000000..497cdf1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/certificate_list_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["CertificateListResponse", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class CertificateListResponse(BaseModel): + """Represents an individual certificate configured at the project level.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + active: bool + """Whether the certificate is currently active at the project level.""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: Optional[str] = None + """The name of the certificate.""" + + object: Literal["organization.project.certificate"] + """The object type, which is always `organization.project.certificate`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/data_retention_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/data_retention_update_params.py new file mode 100644 index 0000000..6c42f94 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/data_retention_update_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["DataRetentionUpdateParams"] + + +class DataRetentionUpdateParams(TypedDict, total=False): + retention_type: Required[ + Literal[ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ] + ] + """The desired project data retention type.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/group_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/group_create_params.py new file mode 100644 index 0000000..fb1a3bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/group_create_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["GroupCreateParams"] + + +class GroupCreateParams(TypedDict, total=False): + group_id: Required[str] + """Identifier of the group to add to the project.""" + + role: Required[str] + """Identifier of the project role to grant to the group.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/group_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/group_delete_response.py new file mode 100644 index 0000000..d903b77 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/group_delete_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["GroupDeleteResponse"] + + +class GroupDeleteResponse(BaseModel): + """Confirmation payload returned after removing a group from a project.""" + + deleted: bool + """Whether the group membership in the project was removed.""" + + object: Literal["project.group.deleted"] + """Always `project.group.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/group_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/group_list_params.py new file mode 100644 index 0000000..be74bbe --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/group_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["GroupListParams"] + + +class GroupListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the ID of the last group from the previous response to fetch the next + page. + """ + + limit: int + """A limit on the number of project groups to return. Defaults to 20.""" + + order: Literal["asc", "desc"] + """Sort order for the returned groups.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/group_retrieve_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/group_retrieve_params.py new file mode 100644 index 0000000..342ed2f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/group_retrieve_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["GroupRetrieveParams"] + + +class GroupRetrieveParams(TypedDict, total=False): + project_id: Required[str] + + group_type: Literal["group", "tenant_group"] + """The type of group to retrieve.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__init__.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__init__.py new file mode 100644 index 0000000..8e592ec --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__init__.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .role_list_params import RoleListParams as RoleListParams +from .role_create_params import RoleCreateParams as RoleCreateParams +from .role_list_response import RoleListResponse as RoleListResponse +from .role_create_response import RoleCreateResponse as RoleCreateResponse +from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .role_retrieve_response import RoleRetrieveResponse as RoleRetrieveResponse diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..92c548d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_create_params.cpython-312.pyc new file mode 100644 index 0000000..5710623 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_create_response.cpython-312.pyc new file mode 100644 index 0000000..719c05f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_delete_response.cpython-312.pyc new file mode 100644 index 0000000..dbc3b0f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_list_params.cpython-312.pyc new file mode 100644 index 0000000..89818b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_list_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_list_response.cpython-312.pyc new file mode 100644 index 0000000..d4450f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_list_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_retrieve_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_retrieve_response.cpython-312.pyc new file mode 100644 index 0000000..aca524d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/__pycache__/role_retrieve_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_create_params.py new file mode 100644 index 0000000..df4a8ed --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_create_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RoleCreateParams"] + + +class RoleCreateParams(TypedDict, total=False): + project_id: Required[str] + + role_id: Required[str] + """Identifier of the role to assign.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_create_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_create_response.py new file mode 100644 index 0000000..2e7729a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_create_response.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...role import Role +from ......_models import BaseModel + +__all__ = ["RoleCreateResponse", "Group"] + + +class Group(BaseModel): + """Summary information about a group returned in role assignment responses.""" + + id: str + """Identifier for the group.""" + + created_at: int + """Unix timestamp (in seconds) when the group was created.""" + + name: str + """Display name of the group.""" + + object: Literal["group"] + """Always `group`.""" + + scim_managed: bool + """Whether the group is managed through SCIM.""" + + +class RoleCreateResponse(BaseModel): + """Role assignment linking a group to a role.""" + + group: Group + """Summary information about a group returned in role assignment responses.""" + + object: Literal["group.role"] + """Always `group.role`.""" + + role: Role + """Details about a role that can be assigned through the public Roles API.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_delete_response.py new file mode 100644 index 0000000..66c7bea --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_delete_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ......_models import BaseModel + +__all__ = ["RoleDeleteResponse"] + + +class RoleDeleteResponse(BaseModel): + """Confirmation payload returned after unassigning a role.""" + + deleted: bool + """Whether the assignment was removed.""" + + object: str + """ + Identifier for the deleted assignment, such as `group.role.deleted` or + `user.role.deleted`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_list_params.py new file mode 100644 index 0000000..9681c93 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RoleListParams"] + + +class RoleListParams(TypedDict, total=False): + project_id: Required[str] + + after: str + """Cursor for pagination. + + Provide the value from the previous response's `next` field to continue listing + project roles. + """ + + limit: int + """A limit on the number of project role assignments to return.""" + + order: Literal["asc", "desc"] + """Sort order for the returned project roles.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_list_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_list_response.py new file mode 100644 index 0000000..f5ed0b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_list_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ......_models import BaseModel + +__all__ = ["RoleListResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleListResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_retrieve_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_retrieve_response.py new file mode 100644 index 0000000..8f7e6f0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/groups/role_retrieve_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ......_models import BaseModel + +__all__ = ["RoleRetrieveResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleRetrieveResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py new file mode 100644 index 0000000..93e71dd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["HostedToolPermissionUpdateParams", "CodeInterpreter", "FileSearch", "ImageGeneration", "Mcp", "WebSearch"] + + +class HostedToolPermissionUpdateParams(TypedDict, total=False): + code_interpreter: Optional[CodeInterpreter] + """The code interpreter permission update.""" + + file_search: Optional[FileSearch] + """The file search permission update.""" + + image_generation: Optional[ImageGeneration] + """The image generation permission update.""" + + mcp: Optional[Mcp] + """The MCP permission update.""" + + web_search: Optional[WebSearch] + """The web search permission update.""" + + +class CodeInterpreter(TypedDict, total=False): + """The code interpreter permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" + + +class FileSearch(TypedDict, total=False): + """The file search permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" + + +class ImageGeneration(TypedDict, total=False): + """The image generation permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" + + +class Mcp(TypedDict, total=False): + """The MCP permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" + + +class WebSearch(TypedDict, total=False): + """The web search permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/model_permission_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/model_permission_update_params.py new file mode 100644 index 0000000..f1dc25f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/model_permission_update_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["ModelPermissionUpdateParams"] + + +class ModelPermissionUpdateParams(TypedDict, total=False): + mode: Required[Literal["allow_list", "deny_list"]] + """The model permissions mode to apply.""" + + model_ids: Required[SequenceNotStr[str]] + """The model IDs included in this permissions policy.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_api_key.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_api_key.py new file mode 100644 index 0000000..15f8544 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_api_key.py @@ -0,0 +1,87 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectAPIKey", "Owner", "OwnerServiceAccount", "OwnerUser"] + + +class OwnerServiceAccount(BaseModel): + """The service account that owns a project API key.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + created_at: int + """The Unix timestamp (in seconds) of when the service account was created.""" + + name: str + """The name of the service account.""" + + role: str + """The service account's project role.""" + + +class OwnerUser(BaseModel): + """The user that owns a project API key.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + created_at: int + """The Unix timestamp (in seconds) of when the user was created.""" + + email: str + """The email address of the user.""" + + name: str + """The name of the user.""" + + role: str + """The user's project role.""" + + +class Owner(BaseModel): + service_account: Optional[OwnerServiceAccount] = None + """The service account that owns a project API key.""" + + type: Optional[Literal["user", "service_account"]] = None + """`user` or `service_account`""" + + user: Optional[OwnerUser] = None + """The user that owns a project API key.""" + + +class ProjectAPIKey(BaseModel): + """Represents an individual API key in a project.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + created_at: int + """The Unix timestamp (in seconds) of when the API key was created""" + + last_used_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the API key was last used.""" + + name: str + """The name of the API key""" + + object: Literal["organization.project.api_key"] + """The object type, which is always `organization.project.api_key`""" + + owner: Owner + + owner_project_access: Literal["active", "inactive"] + """Whether the API key's owner currently has effective access to the project.""" + + redacted_value: str + """The redacted value of the API key""" + + expires_at: Optional[int] = None + """ + The Unix timestamp (in seconds) when the API key expires, or null if it does not + expire. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_data_retention.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_data_retention.py new file mode 100644 index 0000000..fbc5904 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_data_retention.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectDataRetention"] + + +class ProjectDataRetention(BaseModel): + """Represents a project's data retention control setting.""" + + object: Literal["project.data_retention"] + """The object type, which is always `project.data_retention`.""" + + type: Literal[ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ] + """The configured project data retention type.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_group.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_group.py new file mode 100644 index 0000000..c5c2d74 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_group.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectGroup"] + + +class ProjectGroup(BaseModel): + """Details about a group's membership in a project.""" + + created_at: int + """Unix timestamp (in seconds) when the group was granted project access.""" + + group_id: str + """Identifier of the group that has access to the project.""" + + group_name: str + """Display name of the group.""" + + group_type: Literal["group", "tenant_group"] + """The type of the group.""" + + object: Literal["project.group"] + """Always `project.group`.""" + + project_id: str + """Identifier of the project.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_hosted_tool_permissions.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_hosted_tool_permissions.py new file mode 100644 index 0000000..4488a84 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_hosted_tool_permissions.py @@ -0,0 +1,59 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ....._models import BaseModel + +__all__ = ["ProjectHostedToolPermissions", "CodeInterpreter", "FileSearch", "ImageGeneration", "Mcp", "WebSearch"] + + +class CodeInterpreter(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class FileSearch(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class ImageGeneration(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class Mcp(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class WebSearch(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class ProjectHostedToolPermissions(BaseModel): + """Represents hosted tool permissions for a project.""" + + code_interpreter: CodeInterpreter + """Permission state for a single hosted tool on a project.""" + + file_search: FileSearch + """Permission state for a single hosted tool on a project.""" + + image_generation: ImageGeneration + """Permission state for a single hosted tool on a project.""" + + mcp: Mcp + """Permission state for a single hosted tool on a project.""" + + web_search: WebSearch + """Permission state for a single hosted tool on a project.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_model_permissions.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_model_permissions.py new file mode 100644 index 0000000..b2c4ab1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_model_permissions.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ....._models import BaseModel + +__all__ = ["ProjectModelPermissions"] + + +class ProjectModelPermissions(BaseModel): + """Represents the model allowlist or denylist policy for a project.""" + + mode: Literal["allow_list", "deny_list"] + """Whether the project uses an allowlist or a denylist.""" + + api_model_ids: List[str] = FieldInfo(alias="model_ids") + """The model IDs included in the model permissions policy.""" + + object: Literal["project.model_permissions"] + """The object type, which is always `project.model_permissions`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_model_permissions_deleted.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_model_permissions_deleted.py new file mode 100644 index 0000000..34f3afc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_model_permissions_deleted.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectModelPermissionsDeleted"] + + +class ProjectModelPermissionsDeleted(BaseModel): + """Confirmation payload returned after deleting project model permissions.""" + + deleted: bool + """Whether the project model permissions were deleted.""" + + object: Literal["project.model_permissions.deleted"] + """The object type, which is always `project.model_permissions.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_rate_limit.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_rate_limit.py new file mode 100644 index 0000000..50fb117 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_rate_limit.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectRateLimit"] + + +class ProjectRateLimit(BaseModel): + """Represents a project rate limit config.""" + + id: str + """The identifier, which can be referenced in API endpoints.""" + + max_requests_per_1_minute: int + """The maximum requests per minute.""" + + max_tokens_per_1_minute: int + """The maximum tokens per minute.""" + + model: str + """The model this rate limit applies to.""" + + object: Literal["project.rate_limit"] + """The object type, which is always `project.rate_limit`""" + + batch_1_day_max_input_tokens: Optional[int] = None + """The maximum batch input tokens per day. Only present for relevant models.""" + + max_audio_megabytes_per_1_minute: Optional[int] = None + """The maximum audio megabytes per minute. Only present for relevant models.""" + + max_images_per_1_minute: Optional[int] = None + """The maximum images per minute. Only present for relevant models.""" + + max_requests_per_1_day: Optional[int] = None + """The maximum requests per day. Only present for relevant models.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_service_account.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_service_account.py new file mode 100644 index 0000000..ea66af4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_service_account.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectServiceAccount"] + + +class ProjectServiceAccount(BaseModel): + """Represents an individual service account in a project.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + created_at: int + """The Unix timestamp (in seconds) of when the service account was created""" + + name: str + """The name of the service account""" + + object: Literal["organization.project.service_account"] + """The object type, which is always `organization.project.service_account`""" + + role: Literal["owner", "member", "none"] + """`owner`, `member`, or `none`""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_spend_alert.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_spend_alert.py new file mode 100644 index 0000000..e579f4c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_spend_alert.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectSpendAlert", "NotificationChannel"] + + +class NotificationChannel(BaseModel): + """Email notification settings for a spend alert.""" + + recipients: List[str] + """Email addresses that receive the spend alert notification.""" + + type: Literal["email"] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] = None + """Optional subject prefix for alert emails.""" + + +class ProjectSpendAlert(BaseModel): + """Represents a spend alert configured at the project level.""" + + id: str + """The identifier, which can be referenced in API endpoints.""" + + currency: Literal["USD"] + """The currency for the threshold amount.""" + + interval: Literal["month"] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: NotificationChannel + """Email notification settings for a spend alert.""" + + object: Literal["project.spend_alert"] + """The object type, which is always `project.spend_alert`.""" + + threshold_amount: int + """The alert threshold amount, in cents.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_spend_alert_deleted.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_spend_alert_deleted.py new file mode 100644 index 0000000..76f1d86 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_spend_alert_deleted.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectSpendAlertDeleted"] + + +class ProjectSpendAlertDeleted(BaseModel): + """Confirmation payload returned after deleting a project spend alert.""" + + id: str + """The deleted spend alert ID.""" + + deleted: bool + """Whether the spend alert was deleted.""" + + object: Literal["project.spend_alert.deleted"] + """Always `project.spend_alert.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_spend_limit.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_spend_limit.py new file mode 100644 index 0000000..cc69598 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_spend_limit.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectSpendLimit", "Enforcement"] + + +class Enforcement(BaseModel): + """The current enforcement state of the hard spend limit.""" + + status: Union[str, Literal["inactive", "enforcing"]] + """Whether the hard spend limit is currently enforcing.""" + + +class ProjectSpendLimit(BaseModel): + """Represents a hard spend limit configured at the project level.""" + + currency: Union[str, Literal["USD"]] + """The currency for the threshold amount. Currently, only `USD` is supported.""" + + enforcement: Enforcement + """The current enforcement state of the hard spend limit.""" + + interval: Union[str, Literal["month"]] + """The time interval for evaluating spend against the threshold. + + Currently, only `month` is supported. + """ + + object: Literal["project.spend_limit"] + """The object type, which is always `project.spend_limit`.""" + + threshold_amount: int + """The hard spend limit amount, in cents.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_spend_limit_deleted.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_spend_limit_deleted.py new file mode 100644 index 0000000..3944756 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_spend_limit_deleted.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectSpendLimitDeleted"] + + +class ProjectSpendLimitDeleted(BaseModel): + """Confirmation payload returned after deleting a project hard spend limit.""" + + deleted: bool + """Whether the hard spend limit was deleted.""" + + object: Literal["project.spend_limit.deleted"] + """The object type, which is always `project.spend_limit.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_user.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_user.py new file mode 100644 index 0000000..199b387 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/project_user.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectUser"] + + +class ProjectUser(BaseModel): + """Represents an individual user in a project.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + added_at: int + """The Unix timestamp (in seconds) of when the project was added.""" + + object: Literal["organization.project.user"] + """The object type, which is always `organization.project.user`""" + + role: str + """`owner` or `member`""" + + email: Optional[str] = None + """The email address of the user""" + + name: Optional[str] = None + """The name of the user""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/rate_limit_list_rate_limits_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/rate_limit_list_rate_limits_params.py new file mode 100644 index 0000000..dfb73d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/rate_limit_list_rate_limits_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["RateLimitListRateLimitsParams"] + + +class RateLimitListRateLimitsParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, beginning with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + limit: int + """A limit on the number of objects to be returned. The default is 100.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/rate_limit_update_rate_limit_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/rate_limit_update_rate_limit_params.py new file mode 100644 index 0000000..058b6bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/rate_limit_update_rate_limit_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RateLimitUpdateRateLimitParams"] + + +class RateLimitUpdateRateLimitParams(TypedDict, total=False): + project_id: Required[str] + + batch_1_day_max_input_tokens: int + """The maximum batch input tokens per day. Only relevant for certain models.""" + + max_audio_megabytes_per_1_minute: int + """The maximum audio megabytes per minute. Only relevant for certain models.""" + + max_images_per_1_minute: int + """The maximum images per minute. Only relevant for certain models.""" + + max_requests_per_1_day: int + """The maximum requests per day. Only relevant for certain models.""" + + max_requests_per_1_minute: int + """The maximum requests per minute.""" + + max_tokens_per_1_minute: int + """The maximum tokens per minute.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/role_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/role_create_params.py new file mode 100644 index 0000000..bb1178b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/role_create_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["RoleCreateParams"] + + +class RoleCreateParams(TypedDict, total=False): + permissions: Required[SequenceNotStr[str]] + """Permissions to grant to the role.""" + + role_name: Required[str] + """Unique name for the role.""" + + description: Optional[str] + """Optional description of the role.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/role_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/role_delete_response.py new file mode 100644 index 0000000..9600be3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/role_delete_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["RoleDeleteResponse"] + + +class RoleDeleteResponse(BaseModel): + """Confirmation payload returned after deleting a role.""" + + id: str + """Identifier of the deleted role.""" + + deleted: bool + """Whether the role was deleted.""" + + object: Literal["role.deleted"] + """Always `role.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/role_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/role_list_params.py new file mode 100644 index 0000000..2257ed3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/role_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["RoleListParams"] + + +class RoleListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the value from the previous response's `next` field to continue listing + roles. + """ + + limit: int + """A limit on the number of roles to return. Defaults to 1000.""" + + order: Literal["asc", "desc"] + """Sort order for the returned roles.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/role_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/role_update_params.py new file mode 100644 index 0000000..057c9c9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/role_update_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["RoleUpdateParams"] + + +class RoleUpdateParams(TypedDict, total=False): + project_id: Required[str] + + description: Optional[str] + """New description for the role.""" + + permissions: Optional[SequenceNotStr[str]] + """Updated set of permissions for the role.""" + + role_name: Optional[str] + """New name for the role.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_create_params.py new file mode 100644 index 0000000..795a2ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_create_params.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["ServiceAccountCreateParams"] + + +class ServiceAccountCreateParams(TypedDict, total=False): + name: Required[str] + """The name of the service account being created.""" + + create_service_account_only: Optional[bool] + """Create the service account without default roles or an API key.""" + + expires_in_seconds: Optional[int] + """Number of seconds until the initial API key expires. + + If omitted or null, the key does not expire unless the effective organization or + project policy requires an expiration. When a policy sets a maximum lifetime, + this value must be provided and must not exceed that limit. A non-null value + cannot be used when `create_service_account_only` is true. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_create_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_create_response.py new file mode 100644 index 0000000..8a3fb36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_create_response.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ServiceAccountCreateResponse", "APIKey"] + + +class APIKey(BaseModel): + id: str + + created_at: int + + name: str + + object: Literal["organization.project.service_account.api_key"] + """The object type, which is always `organization.project.service_account.api_key`""" + + value: str + + expires_at: Optional[int] = None + """ + The Unix timestamp (in seconds) when the API key expires, or null if it does not + expire. + """ + + +class ServiceAccountCreateResponse(BaseModel): + id: str + + api_key: Optional[APIKey] = None + + created_at: int + + name: str + + object: Literal["organization.project.service_account"] + + role: Literal["member", "none"] + """Service accounts created with default project membership have role `member`. + + Accounts created with `create_service_account_only` have role `none`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_delete_response.py new file mode 100644 index 0000000..b3d88f5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ServiceAccountDeleteResponse"] + + +class ServiceAccountDeleteResponse(BaseModel): + id: str + + deleted: bool + + object: Literal["organization.project.service_account.deleted"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_list_params.py new file mode 100644 index 0000000..c8e400c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ServiceAccountListParams"] + + +class ServiceAccountListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_update_params.py new file mode 100644 index 0000000..2d0c38c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_account_update_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ServiceAccountUpdateParams"] + + +class ServiceAccountUpdateParams(TypedDict, total=False): + project_id: Required[str] + + name: str + """The updated service account name.""" + + role: Literal["member", "owner"] + """The updated service account role.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/__init__.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/__init__.py new file mode 100644 index 0000000..0433f31 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .api_key_create_params import APIKeyCreateParams as APIKeyCreateParams +from .api_key_create_response import APIKeyCreateResponse as APIKeyCreateResponse diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5ccb870 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/__pycache__/api_key_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/__pycache__/api_key_create_params.cpython-312.pyc new file mode 100644 index 0000000..59ea8aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/__pycache__/api_key_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/__pycache__/api_key_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/__pycache__/api_key_create_response.cpython-312.pyc new file mode 100644 index 0000000..0ce4e08 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/__pycache__/api_key_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py new file mode 100644 index 0000000..4964231 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ......_types import SequenceNotStr + +__all__ = ["APIKeyCreateParams"] + + +class APIKeyCreateParams(TypedDict, total=False): + project_id: Required[str] + """The ID of the project.""" + + expires_in_seconds: Optional[int] + """Number of seconds until the API key expires.""" + + name: str + """API key name.""" + + scopes: SequenceNotStr[str] + """API key scopes.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py new file mode 100644 index 0000000..cefcf98 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ......_models import BaseModel + +__all__ = ["APIKeyCreateResponse"] + + +class APIKeyCreateResponse(BaseModel): + id: str + """The identifier of the API key.""" + + created_at: int + """The Unix timestamp (in seconds) when the API key was created.""" + + name: str + """The name of the API key.""" + + object: Literal["organization.project.service_account.api_key"] + """The object type, which is always `organization.project.service_account.api_key`""" + + value: str + """The unredacted API key value.""" + + expires_at: Optional[int] = None + """ + The Unix timestamp (in seconds) when the API key expires, or null if it does not + expire. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/spend_alert_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/spend_alert_create_params.py new file mode 100644 index 0000000..11fb371 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/spend_alert_create_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["SpendAlertCreateParams", "NotificationChannel"] + + +class SpendAlertCreateParams(TypedDict, total=False): + currency: Required[Literal["USD"]] + """The currency for the threshold amount.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: Required[NotificationChannel] + """Email notification settings for a spend alert.""" + + threshold_amount: Required[int] + """The alert threshold amount, in cents.""" + + +class NotificationChannel(TypedDict, total=False): + """Email notification settings for a spend alert.""" + + recipients: Required[SequenceNotStr[str]] + """Email addresses that receive the spend alert notification.""" + + type: Required[Literal["email"]] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] + """Optional subject prefix for alert emails.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/spend_alert_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/spend_alert_list_params.py new file mode 100644 index 0000000..865ae35 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/spend_alert_list_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["SpendAlertListParams"] + + +class SpendAlertListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the ID of the last spend alert from the previous response to fetch the + next page. + """ + + before: str + """Cursor for pagination. + + Provide the ID of the first spend alert from the previous response to fetch the + previous page. + """ + + limit: int + """A limit on the number of spend alerts to return. Defaults to 20.""" + + order: Literal["asc", "desc"] + """Sort order for the returned spend alerts.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/spend_alert_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/spend_alert_update_params.py new file mode 100644 index 0000000..c4e87a9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/spend_alert_update_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["SpendAlertUpdateParams", "NotificationChannel"] + + +class SpendAlertUpdateParams(TypedDict, total=False): + project_id: Required[str] + + currency: Required[Literal["USD"]] + """The currency for the threshold amount.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: Required[NotificationChannel] + """Email notification settings for a spend alert.""" + + threshold_amount: Required[int] + """The alert threshold amount, in cents.""" + + +class NotificationChannel(TypedDict, total=False): + """Email notification settings for a spend alert.""" + + recipients: Required[SequenceNotStr[str]] + """Email addresses that receive the spend alert notification.""" + + type: Required[Literal["email"]] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] + """Optional subject prefix for alert emails.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/spend_limit_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/spend_limit_update_params.py new file mode 100644 index 0000000..826c8d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/spend_limit_update_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["SpendLimitUpdateParams"] + + +class SpendLimitUpdateParams(TypedDict, total=False): + currency: Required[Literal["USD"]] + """The currency for the threshold amount. Currently, only `USD` is supported.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold. + + Currently, only `month` is supported. + """ + + threshold_amount: Required[int] + """The hard spend limit amount, in cents.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/user_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/user_create_params.py new file mode 100644 index 0000000..c2d32a4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/user_create_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["UserCreateParams"] + + +class UserCreateParams(TypedDict, total=False): + role: Required[str] + """`owner` or `member`""" + + email: Optional[str] + """Email of the user to add.""" + + user_id: Optional[str] + """The ID of the user.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/user_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/user_delete_response.py new file mode 100644 index 0000000..a9af53e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/user_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["UserDeleteResponse"] + + +class UserDeleteResponse(BaseModel): + id: str + + deleted: bool + + object: Literal["organization.project.user.deleted"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/user_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/user_list_params.py new file mode 100644 index 0000000..b1db15f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/user_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["UserListParams"] + + +class UserListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/user_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/user_update_params.py new file mode 100644 index 0000000..a97ee03 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/user_update_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["UserUpdateParams"] + + +class UserUpdateParams(TypedDict, total=False): + project_id: Required[str] + + role: Optional[str] + """`owner` or `member`""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__init__.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__init__.py new file mode 100644 index 0000000..8e592ec --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__init__.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .role_list_params import RoleListParams as RoleListParams +from .role_create_params import RoleCreateParams as RoleCreateParams +from .role_list_response import RoleListResponse as RoleListResponse +from .role_create_response import RoleCreateResponse as RoleCreateResponse +from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .role_retrieve_response import RoleRetrieveResponse as RoleRetrieveResponse diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..fb96c94 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_create_params.cpython-312.pyc new file mode 100644 index 0000000..796cbef Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_create_response.cpython-312.pyc new file mode 100644 index 0000000..c7aff82 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_delete_response.cpython-312.pyc new file mode 100644 index 0000000..6ad257f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_list_params.cpython-312.pyc new file mode 100644 index 0000000..6c84808 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_list_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_list_response.cpython-312.pyc new file mode 100644 index 0000000..5ba5109 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_list_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_retrieve_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_retrieve_response.cpython-312.pyc new file mode 100644 index 0000000..389c730 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/__pycache__/role_retrieve_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_create_params.py new file mode 100644 index 0000000..df4a8ed --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_create_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RoleCreateParams"] + + +class RoleCreateParams(TypedDict, total=False): + project_id: Required[str] + + role_id: Required[str] + """Identifier of the role to assign.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_create_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_create_response.py new file mode 100644 index 0000000..3ef8b95 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_create_response.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...role import Role +from ......_models import BaseModel +from ...organization_user import OrganizationUser + +__all__ = ["RoleCreateResponse"] + + +class RoleCreateResponse(BaseModel): + """Role assignment linking a user to a role.""" + + object: Literal["user.role"] + """Always `user.role`.""" + + role: Role + """Details about a role that can be assigned through the public Roles API.""" + + user: OrganizationUser + """Represents an individual `user` within an organization.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_delete_response.py new file mode 100644 index 0000000..66c7bea --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_delete_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ......_models import BaseModel + +__all__ = ["RoleDeleteResponse"] + + +class RoleDeleteResponse(BaseModel): + """Confirmation payload returned after unassigning a role.""" + + deleted: bool + """Whether the assignment was removed.""" + + object: str + """ + Identifier for the deleted assignment, such as `group.role.deleted` or + `user.role.deleted`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_list_params.py new file mode 100644 index 0000000..9681c93 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RoleListParams"] + + +class RoleListParams(TypedDict, total=False): + project_id: Required[str] + + after: str + """Cursor for pagination. + + Provide the value from the previous response's `next` field to continue listing + project roles. + """ + + limit: int + """A limit on the number of project role assignments to return.""" + + order: Literal["asc", "desc"] + """Sort order for the returned project roles.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_list_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_list_response.py new file mode 100644 index 0000000..f5ed0b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_list_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ......_models import BaseModel + +__all__ = ["RoleListResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleListResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_retrieve_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_retrieve_response.py new file mode 100644 index 0000000..8f7e6f0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/projects/users/role_retrieve_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ......_models import BaseModel + +__all__ = ["RoleRetrieveResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleRetrieveResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/role.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/role.py new file mode 100644 index 0000000..1be5513 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/role.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["Role"] + + +class Role(BaseModel): + """Details about a role that can be assigned through the public Roles API.""" + + id: str + """Identifier for the role.""" + + description: Optional[str] = None + """Optional description of the role.""" + + name: str + """Unique name for the role.""" + + object: Literal["role"] + """Always `role`.""" + + permissions: List[str] + """Permissions granted by the role.""" + + predefined_role: bool + """Whether the role is predefined and managed by OpenAI.""" + + resource_type: str + """ + Resource type the role is bound to (for example `api.organization` or + `api.project`). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/role_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/role_create_params.py new file mode 100644 index 0000000..4d7315c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/role_create_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["RoleCreateParams"] + + +class RoleCreateParams(TypedDict, total=False): + permissions: Required[SequenceNotStr[str]] + """Permissions to grant to the role.""" + + role_name: Required[str] + """Unique name for the role.""" + + description: Optional[str] + """Optional description of the role.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/role_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/role_delete_response.py new file mode 100644 index 0000000..e4383cb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/role_delete_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["RoleDeleteResponse"] + + +class RoleDeleteResponse(BaseModel): + """Confirmation payload returned after deleting a role.""" + + id: str + """Identifier of the deleted role.""" + + deleted: bool + """Whether the role was deleted.""" + + object: Literal["role.deleted"] + """Always `role.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/role_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/role_list_params.py new file mode 100644 index 0000000..2257ed3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/role_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["RoleListParams"] + + +class RoleListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the value from the previous response's `next` field to continue listing + roles. + """ + + limit: int + """A limit on the number of roles to return. Defaults to 1000.""" + + order: Literal["asc", "desc"] + """Sort order for the returned roles.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/role_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/role_update_params.py new file mode 100644 index 0000000..6cd125a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/role_update_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["RoleUpdateParams"] + + +class RoleUpdateParams(TypedDict, total=False): + description: Optional[str] + """New description for the role.""" + + permissions: Optional[SequenceNotStr[str]] + """Updated set of permissions for the role.""" + + role_name: Optional[str] + """New name for the role.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/spend_alert_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/spend_alert_create_params.py new file mode 100644 index 0000000..0c087a6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/spend_alert_create_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["SpendAlertCreateParams", "NotificationChannel"] + + +class SpendAlertCreateParams(TypedDict, total=False): + currency: Required[Literal["USD"]] + """The currency for the threshold amount.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: Required[NotificationChannel] + """Email notification settings for a spend alert.""" + + threshold_amount: Required[int] + """The alert threshold amount, in cents.""" + + +class NotificationChannel(TypedDict, total=False): + """Email notification settings for a spend alert.""" + + recipients: Required[SequenceNotStr[str]] + """Email addresses that receive the spend alert notification.""" + + type: Required[Literal["email"]] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] + """Optional subject prefix for alert emails.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/spend_alert_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/spend_alert_list_params.py new file mode 100644 index 0000000..865ae35 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/spend_alert_list_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["SpendAlertListParams"] + + +class SpendAlertListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the ID of the last spend alert from the previous response to fetch the + next page. + """ + + before: str + """Cursor for pagination. + + Provide the ID of the first spend alert from the previous response to fetch the + previous page. + """ + + limit: int + """A limit on the number of spend alerts to return. Defaults to 20.""" + + order: Literal["asc", "desc"] + """Sort order for the returned spend alerts.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/spend_alert_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/spend_alert_update_params.py new file mode 100644 index 0000000..0ba6920 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/spend_alert_update_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["SpendAlertUpdateParams", "NotificationChannel"] + + +class SpendAlertUpdateParams(TypedDict, total=False): + currency: Required[Literal["USD"]] + """The currency for the threshold amount.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: Required[NotificationChannel] + """Email notification settings for a spend alert.""" + + threshold_amount: Required[int] + """The alert threshold amount, in cents.""" + + +class NotificationChannel(TypedDict, total=False): + """Email notification settings for a spend alert.""" + + recipients: Required[SequenceNotStr[str]] + """Email addresses that receive the spend alert notification.""" + + type: Required[Literal["email"]] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] + """Optional subject prefix for alert emails.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/spend_limit_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/spend_limit_update_params.py new file mode 100644 index 0000000..826c8d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/spend_limit_update_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["SpendLimitUpdateParams"] + + +class SpendLimitUpdateParams(TypedDict, total=False): + currency: Required[Literal["USD"]] + """The currency for the threshold amount. Currently, only `USD` is supported.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold. + + Currently, only `month` is supported. + """ + + threshold_amount: Required[int] + """The hard spend limit amount, in cents.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_audio_speeches_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_audio_speeches_params.py new file mode 100644 index 0000000..ccde991 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_audio_speeches_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageAudioSpeechesParams"] + + +class UsageAudioSpeechesParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model` or any + combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_audio_speeches_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_audio_speeches_response.py new file mode 100644 index 0000000..4ce555d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_audio_speeches_response.py @@ -0,0 +1,516 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .cost_quantity_unit import CostQuantityUnit + +__all__ = [ + "UsageAudioSpeechesResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write + tokens. + """ + + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + quantity_unit: Optional[CostQuantityUnit] = None + """The unit of the `quantity` value. + + If no single supported unit applies to the result, this field is `null`. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageAudioSpeechesResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_audio_transcriptions_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_audio_transcriptions_params.py new file mode 100644 index 0000000..1b50182 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_audio_transcriptions_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageAudioTranscriptionsParams"] + + +class UsageAudioTranscriptionsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model` or any + combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_audio_transcriptions_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_audio_transcriptions_response.py new file mode 100644 index 0000000..65d0ef1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_audio_transcriptions_response.py @@ -0,0 +1,516 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .cost_quantity_unit import CostQuantityUnit + +__all__ = [ + "UsageAudioTranscriptionsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write + tokens. + """ + + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + quantity_unit: Optional[CostQuantityUnit] = None + """The unit of the `quantity` value. + + If no single supported unit applies to the result, this field is `null`. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageAudioTranscriptionsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_code_interpreter_sessions_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_code_interpreter_sessions_params.py new file mode 100644 index 0000000..5444adf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_code_interpreter_sessions_params.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageCodeInterpreterSessionsParams"] + + +class UsageCodeInterpreterSessionsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_code_interpreter_sessions_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_code_interpreter_sessions_response.py new file mode 100644 index 0000000..6dd529a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_code_interpreter_sessions_response.py @@ -0,0 +1,516 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .cost_quantity_unit import CostQuantityUnit + +__all__ = [ + "UsageCodeInterpreterSessionsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write + tokens. + """ + + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + quantity_unit: Optional[CostQuantityUnit] = None + """The unit of the `quantity` value. + + If no single supported unit applies to the result, this field is `null`. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageCodeInterpreterSessionsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_completions_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_completions_params.py new file mode 100644 index 0000000..341e27b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_completions_params.py @@ -0,0 +1,63 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageCompletionsParams"] + + +class UsageCompletionsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + batch: bool + """If `true`, return batch jobs only. + + If `false`, return non-batch jobs only. By default, return both. + """ + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "batch", "service_tier"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model`, `batch`, + `service_tier` or any combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_completions_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_completions_response.py new file mode 100644 index 0000000..d7c116e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_completions_response.py @@ -0,0 +1,516 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .cost_quantity_unit import CostQuantityUnit + +__all__ = [ + "UsageCompletionsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write + tokens. + """ + + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + quantity_unit: Optional[CostQuantityUnit] = None + """The unit of the `quantity` value. + + If no single supported unit applies to the result, this field is `null`. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageCompletionsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_costs_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_costs_params.py new file mode 100644 index 0000000..a547069 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_costs_params.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageCostsParams"] + + +class UsageCostsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only costs for these API keys.""" + + bucket_width: Literal["1d"] + """Width of each time bucket in response. + + Currently only `1d` is supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "line_item", "api_key_id"]] + """Group the costs by the specified fields. + + Support fields include `project_id`, `line_item`, `api_key_id` and any + combination of them. + """ + + limit: int + """A limit on the number of buckets to be returned. + + Limit can range between 1 and 180, and the default is 7. + """ + + line_items: SequenceNotStr[str] + """Return only costs for these exact line item names. + + Each value must match the complete `line_item` value, for example + `gpt-6-astra, input_tokens`. + """ + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only costs for these projects.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_costs_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_costs_response.py new file mode 100644 index 0000000..c77c34d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_costs_response.py @@ -0,0 +1,516 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .cost_quantity_unit import CostQuantityUnit + +__all__ = [ + "UsageCostsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write + tokens. + """ + + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + quantity_unit: Optional[CostQuantityUnit] = None + """The unit of the `quantity` value. + + If no single supported unit applies to the result, this field is `null`. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageCostsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_embeddings_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_embeddings_params.py new file mode 100644 index 0000000..fe7407a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_embeddings_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageEmbeddingsParams"] + + +class UsageEmbeddingsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model` or any + combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_embeddings_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_embeddings_response.py new file mode 100644 index 0000000..fe70897 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_embeddings_response.py @@ -0,0 +1,516 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .cost_quantity_unit import CostQuantityUnit + +__all__ = [ + "UsageEmbeddingsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write + tokens. + """ + + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + quantity_unit: Optional[CostQuantityUnit] = None + """The unit of the `quantity` value. + + If no single supported unit applies to the result, this field is `null`. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageEmbeddingsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_file_search_calls_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_file_search_calls_params.py new file mode 100644 index 0000000..57d29e5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_file_search_calls_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageFileSearchCallsParams"] + + +class UsageFileSearchCallsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "vector_store_id"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `vector_store_id` + or any combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" + + vector_store_ids: SequenceNotStr[str] + """Return only usage for these vector stores.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_file_search_calls_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_file_search_calls_response.py new file mode 100644 index 0000000..e1d8d91 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_file_search_calls_response.py @@ -0,0 +1,516 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .cost_quantity_unit import CostQuantityUnit + +__all__ = [ + "UsageFileSearchCallsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write + tokens. + """ + + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + quantity_unit: Optional[CostQuantityUnit] = None + """The unit of the `quantity` value. + + If no single supported unit applies to the result, this field is `null`. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageFileSearchCallsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_images_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_images_params.py new file mode 100644 index 0000000..7be83dc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_images_params.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageImagesParams"] + + +class UsageImagesParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "size", "source"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model`, `size`, + `source` or any combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + sizes: List[Literal["256x256", "512x512", "1024x1024", "1792x1792", "1024x1792"]] + """Return only usages for these image sizes. + + Possible values are `256x256`, `512x512`, `1024x1024`, `1792x1792`, `1024x1792` + or any combination of them. + """ + + sources: List[Literal["image.generation", "image.edit", "image.variation"]] + """Return only usages for these sources. + + Possible values are `image.generation`, `image.edit`, `image.variation` or any + combination of them. + """ + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_images_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_images_response.py new file mode 100644 index 0000000..32f88b8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_images_response.py @@ -0,0 +1,516 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .cost_quantity_unit import CostQuantityUnit + +__all__ = [ + "UsageImagesResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write + tokens. + """ + + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + quantity_unit: Optional[CostQuantityUnit] = None + """The unit of the `quantity` value. + + If no single supported unit applies to the result, this field is `null`. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageImagesResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_moderations_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_moderations_params.py new file mode 100644 index 0000000..4ff6869 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_moderations_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageModerationsParams"] + + +class UsageModerationsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model` or any + combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_moderations_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_moderations_response.py new file mode 100644 index 0000000..8c71218 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_moderations_response.py @@ -0,0 +1,516 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .cost_quantity_unit import CostQuantityUnit + +__all__ = [ + "UsageModerationsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write + tokens. + """ + + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + quantity_unit: Optional[CostQuantityUnit] = None + """The unit of the `quantity` value. + + If no single supported unit applies to the result, this field is `null`. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageModerationsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_vector_stores_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_vector_stores_params.py new file mode 100644 index 0000000..5fc7265 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_vector_stores_params.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageVectorStoresParams"] + + +class UsageVectorStoresParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_vector_stores_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_vector_stores_response.py new file mode 100644 index 0000000..1480503 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_vector_stores_response.py @@ -0,0 +1,516 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .cost_quantity_unit import CostQuantityUnit + +__all__ = [ + "UsageVectorStoresResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write + tokens. + """ + + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + quantity_unit: Optional[CostQuantityUnit] = None + """The unit of the `quantity` value. + + If no single supported unit applies to the result, this field is `null`. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageVectorStoresResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_web_search_calls_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_web_search_calls_params.py new file mode 100644 index 0000000..650efd3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_web_search_calls_params.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageWebSearchCallsParams"] + + +class UsageWebSearchCallsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + context_levels: List[Literal["low", "medium", "high"]] + """Return only web search usage for these context levels.""" + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "context_level"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model`, + `context_level` or any combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_web_search_calls_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_web_search_calls_response.py new file mode 100644 index 0000000..f38fa0b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/usage_web_search_calls_response.py @@ -0,0 +1,516 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .cost_quantity_unit import CostQuantityUnit + +__all__ = [ + "UsageWebSearchCallsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write + tokens. + """ + + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + quantity_unit: Optional[CostQuantityUnit] = None + """The unit of the `quantity` value. + + If no single supported unit applies to the result, this field is `null`. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageWebSearchCallsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/user_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/user_delete_response.py new file mode 100644 index 0000000..af26c8e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/user_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["UserDeleteResponse"] + + +class UserDeleteResponse(BaseModel): + id: str + + deleted: bool + + object: Literal["organization.user.deleted"] diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/user_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/user_list_params.py new file mode 100644 index 0000000..8c5ad56 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/user_list_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UserListParams"] + + +class UserListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + emails: SequenceNotStr[str] + """Filter by the email address of users.""" + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/user_update_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/user_update_params.py new file mode 100644 index 0000000..358e86a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/user_update_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["UserUpdateParams"] + + +class UserUpdateParams(TypedDict, total=False): + developer_persona: Optional[str] + """Developer persona metadata.""" + + role: Optional[str] + """`owner` or `reader`""" + + role_id: Optional[str] + """Role ID to assign to the user.""" + + technical_level: Optional[str] + """Technical level metadata.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__init__.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__init__.py new file mode 100644 index 0000000..8e592ec --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__init__.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .role_list_params import RoleListParams as RoleListParams +from .role_create_params import RoleCreateParams as RoleCreateParams +from .role_list_response import RoleListResponse as RoleListResponse +from .role_create_response import RoleCreateResponse as RoleCreateResponse +from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .role_retrieve_response import RoleRetrieveResponse as RoleRetrieveResponse diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..8a4acdf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_create_params.cpython-312.pyc new file mode 100644 index 0000000..6545f4f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_create_response.cpython-312.pyc new file mode 100644 index 0000000..656427d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_delete_response.cpython-312.pyc new file mode 100644 index 0000000..08c80f2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_list_params.cpython-312.pyc new file mode 100644 index 0000000..9c58f0b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_list_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_list_response.cpython-312.pyc new file mode 100644 index 0000000..3373663 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_list_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_retrieve_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_retrieve_response.cpython-312.pyc new file mode 100644 index 0000000..b010820 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/__pycache__/role_retrieve_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_create_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_create_params.py new file mode 100644 index 0000000..5c676ce --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RoleCreateParams"] + + +class RoleCreateParams(TypedDict, total=False): + role_id: Required[str] + """Identifier of the role to assign.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_create_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_create_response.py new file mode 100644 index 0000000..e74242c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_create_response.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..role import Role +from ....._models import BaseModel +from ..organization_user import OrganizationUser + +__all__ = ["RoleCreateResponse"] + + +class RoleCreateResponse(BaseModel): + """Role assignment linking a user to a role.""" + + object: Literal["user.role"] + """Always `user.role`.""" + + role: Role + """Details about a role that can be assigned through the public Roles API.""" + + user: OrganizationUser + """Represents an individual `user` within an organization.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_delete_response.py new file mode 100644 index 0000000..7368e73 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_delete_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ....._models import BaseModel + +__all__ = ["RoleDeleteResponse"] + + +class RoleDeleteResponse(BaseModel): + """Confirmation payload returned after unassigning a role.""" + + deleted: bool + """Whether the assignment was removed.""" + + object: str + """ + Identifier for the deleted assignment, such as `group.role.deleted` or + `user.role.deleted`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_list_params.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_list_params.py new file mode 100644 index 0000000..a40ea7d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["RoleListParams"] + + +class RoleListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the value from the previous response's `next` field to continue listing + organization roles. + """ + + limit: int + """A limit on the number of organization role assignments to return.""" + + order: Literal["asc", "desc"] + """Sort order for the returned organization roles.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_list_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_list_response.py new file mode 100644 index 0000000..3d3096f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_list_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ....._models import BaseModel + +__all__ = ["RoleListResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleListResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_retrieve_response.py b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_retrieve_response.py new file mode 100644 index 0000000..6b1638a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/admin/organization/users/role_retrieve_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ....._models import BaseModel + +__all__ = ["RoleRetrieveResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleRetrieveResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__init__.py b/venv/lib/python3.12/site-packages/openai/types/audio/__init__.py new file mode 100644 index 0000000..68a5f9e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/__init__.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .translation import Translation as Translation +from .speech_model import SpeechModel as SpeechModel +from .transcription import Transcription as Transcription +from .transcription_word import TranscriptionWord as TranscriptionWord +from .translation_verbose import TranslationVerbose as TranslationVerbose +from .speech_create_params import SpeechCreateParams as SpeechCreateParams +from .transcription_include import TranscriptionInclude as TranscriptionInclude +from .transcription_segment import TranscriptionSegment as TranscriptionSegment +from .transcription_verbose import TranscriptionVerbose as TranscriptionVerbose +from .transcription_diarized import TranscriptionDiarized as TranscriptionDiarized +from .transcription_language import TranscriptionLanguage as TranscriptionLanguage +from .translation_create_params import TranslationCreateParams as TranslationCreateParams +from .transcription_stream_event import TranscriptionStreamEvent as TranscriptionStreamEvent +from .transcription_create_params import TranscriptionCreateParams as TranscriptionCreateParams +from .translation_create_response import TranslationCreateResponse as TranslationCreateResponse +from .transcription_create_response import TranscriptionCreateResponse as TranscriptionCreateResponse +from .transcription_text_done_event import TranscriptionTextDoneEvent as TranscriptionTextDoneEvent +from .transcription_diarized_segment import TranscriptionDiarizedSegment as TranscriptionDiarizedSegment +from .transcription_text_delta_event import TranscriptionTextDeltaEvent as TranscriptionTextDeltaEvent +from .transcription_text_segment_event import TranscriptionTextSegmentEvent as TranscriptionTextSegmentEvent diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6a6250a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/speech_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/speech_create_params.cpython-312.pyc new file mode 100644 index 0000000..c2e9be5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/speech_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/speech_model.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/speech_model.cpython-312.pyc new file mode 100644 index 0000000..a51bdae Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/speech_model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription.cpython-312.pyc new file mode 100644 index 0000000..5808e2b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_create_params.cpython-312.pyc new file mode 100644 index 0000000..8be3d2c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_create_response.cpython-312.pyc new file mode 100644 index 0000000..a0e66c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_diarized.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_diarized.cpython-312.pyc new file mode 100644 index 0000000..58be265 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_diarized.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_diarized_segment.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_diarized_segment.cpython-312.pyc new file mode 100644 index 0000000..8037f17 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_diarized_segment.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_include.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_include.cpython-312.pyc new file mode 100644 index 0000000..c9ffd09 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_include.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_language.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_language.cpython-312.pyc new file mode 100644 index 0000000..b8418e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_language.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_segment.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_segment.cpython-312.pyc new file mode 100644 index 0000000..8c6099a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_segment.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_stream_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_stream_event.cpython-312.pyc new file mode 100644 index 0000000..29a62fd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_stream_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_text_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_text_delta_event.cpython-312.pyc new file mode 100644 index 0000000..b478015 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_text_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_text_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_text_done_event.cpython-312.pyc new file mode 100644 index 0000000..37f2be8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_text_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_text_segment_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_text_segment_event.cpython-312.pyc new file mode 100644 index 0000000..8f8cfed Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_text_segment_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_verbose.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_verbose.cpython-312.pyc new file mode 100644 index 0000000..342e218 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_verbose.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_word.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_word.cpython-312.pyc new file mode 100644 index 0000000..322a189 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/transcription_word.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/translation.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/translation.cpython-312.pyc new file mode 100644 index 0000000..7b0341c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/translation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/translation_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/translation_create_params.cpython-312.pyc new file mode 100644 index 0000000..109eac2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/translation_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/translation_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/translation_create_response.cpython-312.pyc new file mode 100644 index 0000000..b2b9e8d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/translation_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/translation_verbose.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/translation_verbose.cpython-312.pyc new file mode 100644 index 0000000..e8fdfcf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/audio/__pycache__/translation_verbose.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/speech_create_params.py b/venv/lib/python3.12/site-packages/openai/types/audio/speech_create_params.py new file mode 100644 index 0000000..0948e37 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/speech_create_params.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .speech_model import SpeechModel + +__all__ = ["SpeechCreateParams", "Voice", "VoiceID"] + + +class SpeechCreateParams(TypedDict, total=False): + input: Required[str] + """The text to generate audio for. The maximum length is 4096 characters.""" + + model: Required[Union[str, SpeechModel]] + """ + One of the available + [TTS models](https://developers.openai.com/api/docs/guides/text-to-speech): + `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`. + """ + + voice: Required[Voice] + """The voice to use when generating the audio. + + Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, + `fable`, `onyx`, `nova`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. You + may also provide a custom voice object with an `id`, for example + `{ "id": "voice_1234" }`. Previews of the voices are available in the + [Text to speech guide](https://developers.openai.com/api/docs/guides/text-to-speech#voice-options). + """ + + instructions: str + """Control the voice of your generated audio with additional instructions. + + Does not work with `tts-1` or `tts-1-hd`. + """ + + response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] + """The format to audio in. + + Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`. + """ + + speed: float + """The speed of the generated audio. + + Select a value from `0.25` to `4.0`. `1.0` is the default. + """ + + stream_format: Literal["sse", "audio"] + """The format to stream the audio in. + + Supported formats are `sse` and `audio`. `sse` is not supported for `tts-1` or + `tts-1-hd`. + """ + + +class VoiceID(TypedDict, total=False): + """Custom voice reference.""" + + id: Required[str] + """The custom voice ID, e.g. `voice_1234`.""" + + +Voice: TypeAlias = Union[ + str, + Literal[ + "alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar", "fable", "onyx", "nova" + ], + VoiceID, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/speech_model.py b/venv/lib/python3.12/site-packages/openai/types/audio/speech_model.py new file mode 100644 index 0000000..bf9b4cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/speech_model.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["SpeechModel"] + +SpeechModel: TypeAlias = Literal["tts-1", "tts-1-hd", "gpt-4o-mini-tts", "gpt-4o-mini-tts-2025-12-15"] diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription.py new file mode 100644 index 0000000..545d596 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription.py @@ -0,0 +1,89 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .transcription_language import TranscriptionLanguage + +__all__ = ["Transcription", "Logprob", "Usage", "UsageTokens", "UsageTokensInputTokenDetails", "UsageDuration"] + + +class Logprob(BaseModel): + token: Optional[str] = None + """The token in the transcription.""" + + bytes: Optional[List[float]] = None + """The bytes of the token.""" + + logprob: Optional[float] = None + """The log probability of the token.""" + + +class UsageTokensInputTokenDetails(BaseModel): + """Details about the input tokens billed for this request.""" + + audio_tokens: Optional[int] = None + """Number of audio tokens billed for this request.""" + + text_tokens: Optional[int] = None + """Number of text tokens billed for this request.""" + + +class UsageTokens(BaseModel): + """Usage statistics for models billed by token usage.""" + + input_tokens: int + """Number of input tokens billed for this request.""" + + output_tokens: int + """Number of output tokens generated.""" + + total_tokens: int + """Total number of tokens used (input + output).""" + + type: Literal["tokens"] + """The type of the usage object. Always `tokens` for this variant.""" + + input_token_details: Optional[UsageTokensInputTokenDetails] = None + """Details about the input tokens billed for this request.""" + + +class UsageDuration(BaseModel): + """Usage statistics for models billed by audio input duration.""" + + seconds: float + """Duration of the input audio in seconds.""" + + type: Literal["duration"] + """The type of the usage object. Always `duration` for this variant.""" + + +Usage: TypeAlias = Annotated[Union[UsageTokens, UsageDuration], PropertyInfo(discriminator="type")] + + +class Transcription(BaseModel): + """ + Represents a transcription response returned by model, based on the provided input. + """ + + text: str + """The transcribed text.""" + + languages: Optional[List[TranscriptionLanguage]] = None + """The languages detected in the audio. + + Returned by `gpt-transcribe`. An empty array indicates that no language could be + reliably detected. + """ + + logprobs: Optional[List[Logprob]] = None + """The log probabilities of the tokens in the transcription. + + Only returned with the models `gpt-4o-transcribe` and `gpt-4o-mini-transcribe` + if `logprobs` is added to the `include` array. + """ + + usage: Optional[Usage] = None + """Token usage statistics for the request.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription_create_params.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_create_params.py new file mode 100644 index 0000000..e1c29e5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_create_params.py @@ -0,0 +1,188 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import FileTypes, SequenceNotStr +from ..audio_model import AudioModel +from .transcription_include import TranscriptionInclude +from ..audio_response_format import AudioResponseFormat + +__all__ = [ + "TranscriptionCreateParamsBase", + "ChunkingStrategy", + "ChunkingStrategyVadConfig", + "TranscriptionCreateParamsNonStreaming", + "TranscriptionCreateParamsStreaming", +] + + +class TranscriptionCreateParamsBase(TypedDict, total=False): + file: Required[FileTypes] + """ + The audio file object (not file name) to transcribe, in one of these formats: + flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include + enough format metadata for the file to be identified. We recommend an + extension-bearing filename and an appropriate content type. + """ + + model: Required[Union[str, AudioModel]] + """ID of the model to use. + + The options are `gpt-transcribe`, `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, + `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` (which is powered by our open + source Whisper V2 model), and `gpt-4o-transcribe-diarize`. + """ + + chunking_strategy: Optional[ChunkingStrategy] + """Controls how the audio is cut into chunks. + + When set to `"auto"`, the server first normalizes loudness and then uses voice + activity detection (VAD) to choose boundaries. `server_vad` object can be + provided to tweak VAD detection parameters manually. If unset, the audio is + transcribed as a single block. Required when using `gpt-4o-transcribe-diarize` + for inputs longer than 30 seconds. + """ + + include: List[TranscriptionInclude] + """ + Additional information to include in the transcription response. `logprobs` will + return the log probabilities of the tokens in the response to understand the + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. + """ + + keywords: SequenceNotStr[str] + """Words or phrases to guide transcription of the input audio. + + Supported by `gpt-transcribe`. + """ + + known_speaker_names: SequenceNotStr[str] + """ + Optional list of speaker names that correspond to the audio samples provided in + `known_speaker_references[]`. Each entry should be a short identifier (for + example `customer` or `agent`). Up to 4 speakers are supported. + """ + + known_speaker_references: SequenceNotStr[str] + """ + Optional list of audio samples (as + [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) + that contain known speaker references matching `known_speaker_names[]`. Each + sample must be between 2 and 10 seconds, and can use any of the same input audio + formats supported by `file`. + """ + + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + languages: SequenceNotStr[str] + """ + Possible languages of the input audio, in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format. + Supported by `gpt-transcribe`. + """ + + prompt: str + """An optional text to guide the model's style or continue a previous audio + segment. + + The + [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. + """ + + response_format: AudioResponseFormat + """ + The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`, the only supported format is `json`. For + `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and + `diarized_json`, with `diarized_json` required to receive speaker annotations. + """ + + temperature: float + """The sampling temperature, between 0 and 1. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + """ + + timestamp_granularities: List[Literal["word", "segment"]] + """The timestamp granularities to populate for this transcription. + + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. This option is not available for + `gpt-4o-transcribe-diarize`. + """ + + +class ChunkingStrategyVadConfig(TypedDict, total=False): + type: Required[Literal["server_vad"]] + """Must be set to `server_vad` to enable manual chunking using server side VAD.""" + + prefix_padding_ms: int + """Amount of audio to include before the VAD detected speech (in milliseconds).""" + + silence_duration_ms: int + """ + Duration of silence to detect speech stop (in milliseconds). With shorter values + the model will respond more quickly, but may jump in on short pauses from the + user. + """ + + threshold: float + """Sensitivity threshold (0.0 to 1.0) for voice activity detection. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + +ChunkingStrategy: TypeAlias = Union[Literal["auto"], ChunkingStrategyVadConfig] + + +class TranscriptionCreateParamsNonStreaming(TranscriptionCreateParamsBase, total=False): + stream: Optional[Literal[False]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + """ + + +class TranscriptionCreateParamsStreaming(TranscriptionCreateParamsBase): + stream: Required[Literal[True]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + """ + + +TranscriptionCreateParams = Union[TranscriptionCreateParamsNonStreaming, TranscriptionCreateParamsStreaming] diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription_create_response.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_create_response.py new file mode 100644 index 0000000..0e9cc74 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import TypeAlias + +from .transcription import Transcription +from .transcription_verbose import TranscriptionVerbose +from .transcription_diarized import TranscriptionDiarized + +__all__ = ["TranscriptionCreateResponse"] + +TranscriptionCreateResponse: TypeAlias = Union[Transcription, TranscriptionDiarized, TranscriptionVerbose] diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription_diarized.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_diarized.py new file mode 100644 index 0000000..ff4e150 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_diarized.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .transcription_diarized_segment import TranscriptionDiarizedSegment + +__all__ = ["TranscriptionDiarized", "Usage", "UsageTokens", "UsageTokensInputTokenDetails", "UsageDuration"] + + +class UsageTokensInputTokenDetails(BaseModel): + """Details about the input tokens billed for this request.""" + + audio_tokens: Optional[int] = None + """Number of audio tokens billed for this request.""" + + text_tokens: Optional[int] = None + """Number of text tokens billed for this request.""" + + +class UsageTokens(BaseModel): + """Usage statistics for models billed by token usage.""" + + input_tokens: int + """Number of input tokens billed for this request.""" + + output_tokens: int + """Number of output tokens generated.""" + + total_tokens: int + """Total number of tokens used (input + output).""" + + type: Literal["tokens"] + """The type of the usage object. Always `tokens` for this variant.""" + + input_token_details: Optional[UsageTokensInputTokenDetails] = None + """Details about the input tokens billed for this request.""" + + +class UsageDuration(BaseModel): + """Usage statistics for models billed by audio input duration.""" + + seconds: float + """Duration of the input audio in seconds.""" + + type: Literal["duration"] + """The type of the usage object. Always `duration` for this variant.""" + + +Usage: TypeAlias = Annotated[Union[UsageTokens, UsageDuration], PropertyInfo(discriminator="type")] + + +class TranscriptionDiarized(BaseModel): + """ + Represents a diarized transcription response returned by the model, including the combined transcript and speaker-segment annotations. + """ + + duration: float + """Duration of the input audio in seconds.""" + + segments: List[TranscriptionDiarizedSegment] + """Segments of the transcript annotated with timestamps and speaker labels.""" + + task: Literal["transcribe"] + """The type of task that was run. Always `transcribe`.""" + + text: str + """The concatenated transcript text for the entire audio input.""" + + usage: Optional[Usage] = None + """Token or duration usage statistics for the request.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription_diarized_segment.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_diarized_segment.py new file mode 100644 index 0000000..d60d007 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_diarized_segment.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["TranscriptionDiarizedSegment"] + + +class TranscriptionDiarizedSegment(BaseModel): + """A segment of diarized transcript text with speaker metadata.""" + + id: str + """Unique identifier for the segment.""" + + end: float + """End timestamp of the segment in seconds.""" + + speaker: str + """Speaker label for this segment. + + When known speakers are provided, the label matches `known_speaker_names[]`. + Otherwise speakers are labeled sequentially using capital letters (`A`, `B`, + ...). + """ + + start: float + """Start timestamp of the segment in seconds.""" + + text: str + """Transcript text for this segment.""" + + type: Literal["transcript.text.segment"] + """The type of the segment. Always `transcript.text.segment`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription_include.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_include.py new file mode 100644 index 0000000..1ec7562 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_include.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["TranscriptionInclude"] + +TranscriptionInclude: TypeAlias = Literal["logprobs"] diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription_language.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_language.py new file mode 100644 index 0000000..01c3d4b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_language.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["TranscriptionLanguage"] + + +class TranscriptionLanguage(BaseModel): + """A language detected in transcribed audio.""" + + code: str + """The code of a language detected in the audio.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription_segment.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_segment.py new file mode 100644 index 0000000..a9c28bb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_segment.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel + +__all__ = ["TranscriptionSegment"] + + +class TranscriptionSegment(BaseModel): + id: int + """Unique identifier of the segment.""" + + avg_logprob: float + """Average logprob of the segment. + + If the value is lower than -1, consider the logprobs failed. + """ + + compression_ratio: float + """Compression ratio of the segment. + + If the value is greater than 2.4, consider the compression failed. + """ + + end: float + """End time of the segment in seconds.""" + + no_speech_prob: float + """Probability of no speech in the segment. + + If the value is higher than 1.0 and the `avg_logprob` is below -1, consider this + segment silent. + """ + + seek: int + """Seek offset of the segment.""" + + start: float + """Start time of the segment in seconds.""" + + temperature: float + """Temperature parameter used for generating the segment.""" + + text: str + """Text content of the segment.""" + + tokens: List[int] + """Array of token IDs for the text content.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription_stream_event.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_stream_event.py new file mode 100644 index 0000000..7ef93e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_stream_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .transcription_text_done_event import TranscriptionTextDoneEvent +from .transcription_text_delta_event import TranscriptionTextDeltaEvent +from .transcription_text_segment_event import TranscriptionTextSegmentEvent + +__all__ = ["TranscriptionStreamEvent"] + +TranscriptionStreamEvent: TypeAlias = Annotated[ + Union[TranscriptionTextSegmentEvent, TranscriptionTextDeltaEvent, TranscriptionTextDoneEvent], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription_text_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_text_delta_event.py new file mode 100644 index 0000000..dd7827c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_text_delta_event.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["TranscriptionTextDeltaEvent", "Logprob"] + + +class Logprob(BaseModel): + token: Optional[str] = None + """The token that was used to generate the log probability.""" + + bytes: Optional[List[int]] = None + """The bytes that were used to generate the log probability.""" + + logprob: Optional[float] = None + """The log probability of the token.""" + + +class TranscriptionTextDeltaEvent(BaseModel): + """Emitted when there is an additional text delta. + + This is also the first event emitted when the transcription starts. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `Stream` parameter set to `true`. + """ + + delta: str + """The text delta that was additionally transcribed.""" + + type: Literal["transcript.text.delta"] + """The type of the event. Always `transcript.text.delta`.""" + + logprobs: Optional[List[Logprob]] = None + """The log probabilities of the delta. + + Only included if you + [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) + with the `include[]` parameter set to `logprobs`. + """ + + segment_id: Optional[str] = None + """Identifier of the diarized segment that this delta belongs to. + + Only present when using `gpt-4o-transcribe-diarize`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription_text_done_event.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_text_done_event.py new file mode 100644 index 0000000..86a4910 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_text_done_event.py @@ -0,0 +1,80 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .transcription_language import TranscriptionLanguage + +__all__ = ["TranscriptionTextDoneEvent", "Logprob", "Usage", "UsageInputTokenDetails"] + + +class Logprob(BaseModel): + token: Optional[str] = None + """The token that was used to generate the log probability.""" + + bytes: Optional[List[int]] = None + """The bytes that were used to generate the log probability.""" + + logprob: Optional[float] = None + """The log probability of the token.""" + + +class UsageInputTokenDetails(BaseModel): + """Details about the input tokens billed for this request.""" + + audio_tokens: Optional[int] = None + """Number of audio tokens billed for this request.""" + + text_tokens: Optional[int] = None + """Number of text tokens billed for this request.""" + + +class Usage(BaseModel): + """Usage statistics for models billed by token usage.""" + + input_tokens: int + """Number of input tokens billed for this request.""" + + output_tokens: int + """Number of output tokens generated.""" + + total_tokens: int + """Total number of tokens used (input + output).""" + + type: Literal["tokens"] + """The type of the usage object. Always `tokens` for this variant.""" + + input_token_details: Optional[UsageInputTokenDetails] = None + """Details about the input tokens billed for this request.""" + + +class TranscriptionTextDoneEvent(BaseModel): + """Emitted when the transcription is complete. + + Contains the complete transcription text. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `Stream` parameter set to `true`. + """ + + text: str + """The text that was transcribed.""" + + type: Literal["transcript.text.done"] + """The type of the event. Always `transcript.text.done`.""" + + languages: Optional[List[TranscriptionLanguage]] = None + """The languages detected in the audio. + + Returned by `gpt-transcribe`. An empty array indicates that no language could be + reliably detected. + """ + + logprobs: Optional[List[Logprob]] = None + """The log probabilities of the individual tokens in the transcription. + + Only included if you + [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) + with the `include[]` parameter set to `logprobs`. + """ + + usage: Optional[Usage] = None + """Usage statistics for models billed by token usage.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription_text_segment_event.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_text_segment_event.py new file mode 100644 index 0000000..badeb50 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_text_segment_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["TranscriptionTextSegmentEvent"] + + +class TranscriptionTextSegmentEvent(BaseModel): + """ + Emitted when a diarized transcription returns a completed segment with speaker information. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with `stream` set to `true` and `response_format` set to `diarized_json`. + """ + + id: str + """Unique identifier for the segment.""" + + end: float + """End timestamp of the segment in seconds.""" + + speaker: str + """Speaker label for this segment.""" + + start: float + """Start timestamp of the segment in seconds.""" + + text: str + """Transcript text for this segment.""" + + type: Literal["transcript.text.segment"] + """The type of the event. Always `transcript.text.segment`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription_verbose.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_verbose.py new file mode 100644 index 0000000..bcae442 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_verbose.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .transcription_word import TranscriptionWord +from .transcription_segment import TranscriptionSegment + +__all__ = ["TranscriptionVerbose", "Usage"] + + +class Usage(BaseModel): + """Usage statistics for models billed by audio input duration.""" + + seconds: float + """Duration of the input audio in seconds.""" + + type: Literal["duration"] + """The type of the usage object. Always `duration` for this variant.""" + + +class TranscriptionVerbose(BaseModel): + """ + Represents a verbose json transcription response returned by model, based on the provided input. + """ + + duration: float + """The duration of the input audio.""" + + language: str + """The language of the input audio.""" + + text: str + """The transcribed text.""" + + segments: Optional[List[TranscriptionSegment]] = None + """Segments of the transcribed text and their corresponding details.""" + + usage: Optional[Usage] = None + """Usage statistics for models billed by audio input duration.""" + + words: Optional[List[TranscriptionWord]] = None + """Extracted words and their corresponding timestamps.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/transcription_word.py b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_word.py new file mode 100644 index 0000000..1dbf0c4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/transcription_word.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["TranscriptionWord"] + + +class TranscriptionWord(BaseModel): + end: float + """End time of the word in seconds.""" + + start: float + """Start time of the word in seconds.""" + + word: str + """The text content of the word.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/translation.py b/venv/lib/python3.12/site-packages/openai/types/audio/translation.py new file mode 100644 index 0000000..2eea7af --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/translation.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["Translation"] + + +class Translation(BaseModel): + text: str diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/translation_create_params.py b/venv/lib/python3.12/site-packages/openai/types/audio/translation_create_params.py new file mode 100644 index 0000000..c06c9ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/translation_create_params.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypedDict + +from ..._types import FileTypes +from ..audio_model import AudioModel + +__all__ = ["TranslationCreateParams"] + + +class TranslationCreateParams(TypedDict, total=False): + file: Required[FileTypes] + """ + The audio file object (not file name) translate, in one of these formats: flac, + mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include enough + format metadata for the file to be identified. We recommend an extension-bearing + filename and an appropriate content type. + """ + + model: Required[Union[str, AudioModel]] + """ID of the model to use. + + Only `whisper-1` (which is powered by our open source Whisper V2 model) is + currently available. + """ + + prompt: str + """An optional text to guide the model's style or continue a previous audio + segment. + + The + [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) + should be in English. + """ + + response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] + """ + The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, or `vtt`. + """ + + temperature: float + """The sampling temperature, between 0 and 1. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/translation_create_response.py b/venv/lib/python3.12/site-packages/openai/types/audio/translation_create_response.py new file mode 100644 index 0000000..983dcbf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/translation_create_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import TypeAlias + +from .translation import Translation +from .translation_verbose import TranslationVerbose + +__all__ = ["TranslationCreateResponse"] + +TranslationCreateResponse: TypeAlias = Union[Translation, TranslationVerbose] diff --git a/venv/lib/python3.12/site-packages/openai/types/audio/translation_verbose.py b/venv/lib/python3.12/site-packages/openai/types/audio/translation_verbose.py new file mode 100644 index 0000000..69f627d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio/translation_verbose.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from .transcription_segment import TranscriptionSegment + +__all__ = ["TranslationVerbose"] + + +class TranslationVerbose(BaseModel): + duration: float + """The duration of the input audio.""" + + language: str + """The language of the output translation (always `english`).""" + + text: str + """The translated text.""" + + segments: Optional[List[TranscriptionSegment]] = None + """Segments of the translated text and their corresponding details.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/audio_model.py b/venv/lib/python3.12/site-packages/openai/types/audio_model.py new file mode 100644 index 0000000..ac6220d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio_model.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["AudioModel"] + +AudioModel: TypeAlias = Literal[ + "whisper-1", + "gpt-transcribe", + "gpt-4o-transcribe", + "gpt-4o-mini-transcribe", + "gpt-4o-mini-transcribe-2025-12-15", + "gpt-4o-transcribe-diarize", +] diff --git a/venv/lib/python3.12/site-packages/openai/types/audio_response_format.py b/venv/lib/python3.12/site-packages/openai/types/audio_response_format.py new file mode 100644 index 0000000..332b79e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/audio_response_format.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["AudioResponseFormat"] + +AudioResponseFormat: TypeAlias = Literal["json", "text", "srt", "verbose_json", "vtt", "diarized_json"] diff --git a/venv/lib/python3.12/site-packages/openai/types/auto_file_chunking_strategy_param.py b/venv/lib/python3.12/site-packages/openai/types/auto_file_chunking_strategy_param.py new file mode 100644 index 0000000..b4d6fa2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/auto_file_chunking_strategy_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["AutoFileChunkingStrategyParam"] + + +class AutoFileChunkingStrategyParam(TypedDict, total=False): + """The default strategy. + + This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`. + """ + + type: Required[Literal["auto"]] + """Always `auto`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/batch.py b/venv/lib/python3.12/site-packages/openai/types/batch.py new file mode 100644 index 0000000..9c7ad2f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/batch.py @@ -0,0 +1,104 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .._models import BaseModel +from .batch_error import BatchError +from .batch_usage import BatchUsage +from .shared.metadata import Metadata +from .batch_request_counts import BatchRequestCounts + +__all__ = ["Batch", "Errors"] + + +class Errors(BaseModel): + data: Optional[List[BatchError]] = None + + object: Optional[str] = None + """The object type, which is always `list`.""" + + +class Batch(BaseModel): + id: str + + completion_window: str + """The time frame within which the batch should be processed.""" + + created_at: int + """The Unix timestamp (in seconds) for when the batch was created.""" + + endpoint: str + """The OpenAI API endpoint used by the batch.""" + + input_file_id: str + """The ID of the input file for the batch.""" + + object: Literal["batch"] + """The object type, which is always `batch`.""" + + status: Literal[ + "validating", "failed", "in_progress", "finalizing", "completed", "expired", "cancelling", "cancelled" + ] + """The current status of the batch.""" + + cancelled_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the batch was cancelled.""" + + cancelling_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the batch started cancelling.""" + + completed_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the batch was completed.""" + + error_file_id: Optional[str] = None + """The ID of the file containing the outputs of requests with errors.""" + + errors: Optional[Errors] = None + + expired_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the batch expired.""" + + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the batch will expire.""" + + failed_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the batch failed.""" + + finalizing_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the batch started finalizing.""" + + in_progress_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the batch started processing.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Optional[str] = None + """Model ID used to process the batch, like `gpt-6-astra`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + output_file_id: Optional[str] = None + """The ID of the file containing the outputs of successfully executed requests.""" + + request_counts: Optional[BatchRequestCounts] = None + """The request counts for different statuses within the batch.""" + + usage: Optional[BatchUsage] = None + """ + Represents token usage details including input tokens, output tokens, a + breakdown of output tokens, and the total tokens used. Only populated on batches + created after September 7, 2025. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/batch_create_params.py b/venv/lib/python3.12/site-packages/openai/types/batch_create_params.py new file mode 100644 index 0000000..ed55c0d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/batch_create_params.py @@ -0,0 +1,87 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from .shared_params.metadata import Metadata + +__all__ = ["BatchCreateParams", "OutputExpiresAfter"] + + +class BatchCreateParams(TypedDict, total=False): + completion_window: Required[Literal["24h"]] + """The time frame within which the batch should be processed. + + Currently only `24h` is supported. + """ + + endpoint: Required[ + Literal[ + "/v1/responses", + "/v1/chat/completions", + "/v1/embeddings", + "/v1/completions", + "/v1/moderations", + "/v1/images/generations", + "/v1/images/edits", + "/v1/videos", + ] + ] + """The endpoint to be used for all requests in the batch. + + Currently `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, + `/v1/completions`, `/v1/moderations`, `/v1/images/generations`, + `/v1/images/edits`, and `/v1/videos` are supported. Note that `/v1/embeddings` + batches are also restricted to a maximum of 50,000 embedding inputs across all + requests in the batch. + """ + + input_file_id: Required[str] + """The ID of an uploaded file that contains requests for the new batch. + + See + [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) + for how to upload a file. + + Your input file must be formatted as a + [JSONL file](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file), + and must be uploaded with the purpose `batch`. The file can contain up to 50,000 + requests, and can be up to 200 MB in size. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + output_expires_after: OutputExpiresAfter + """ + The expiration policy for the output and/or error file that are generated for a + batch. + """ + + +class OutputExpiresAfter(TypedDict, total=False): + """ + The expiration policy for the output and/or error file that are generated for a batch. + """ + + anchor: Required[Literal["created_at"]] + """Anchor timestamp after which the expiration policy applies. + + Supported anchors: `created_at`. Note that the anchor is the file creation time, + not the time the batch is created. + """ + + seconds: Required[int] + """The number of seconds after the anchor time that the file will expire. + + Must be between 3600 (1 hour) and 2592000 (30 days). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/batch_error.py b/venv/lib/python3.12/site-packages/openai/types/batch_error.py new file mode 100644 index 0000000..33aa64d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/batch_error.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["BatchError"] + + +class BatchError(BaseModel): + code: Optional[str] = None + """An error code identifying the error type.""" + + line: Optional[int] = None + """The line number of the input file where the error occurred, if applicable.""" + + message: Optional[str] = None + """A human-readable message providing more details about the error.""" + + param: Optional[str] = None + """The name of the parameter that caused the error, if applicable.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/batch_list_params.py b/venv/lib/python3.12/site-packages/openai/types/batch_list_params.py new file mode 100644 index 0000000..172da29 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/batch_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["BatchListParams"] + + +class BatchListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/batch_request_counts.py b/venv/lib/python3.12/site-packages/openai/types/batch_request_counts.py new file mode 100644 index 0000000..97e3977 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/batch_request_counts.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["BatchRequestCounts"] + + +class BatchRequestCounts(BaseModel): + """The request counts for different statuses within the batch.""" + + completed: int + """Number of requests that have been completed successfully.""" + + failed: int + """Number of requests that have failed.""" + + total: int + """Total number of requests in the batch.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/batch_usage.py b/venv/lib/python3.12/site-packages/openai/types/batch_usage.py new file mode 100644 index 0000000..e484522 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/batch_usage.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["BatchUsage", "InputTokensDetails", "OutputTokensDetails"] + + +class InputTokensDetails(BaseModel): + """A detailed breakdown of the input tokens.""" + + cached_tokens: int + """The number of tokens that were retrieved from the cache. + + [More on prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching). + """ + + +class OutputTokensDetails(BaseModel): + """A detailed breakdown of the output tokens.""" + + reasoning_tokens: int + """The number of reasoning tokens.""" + + +class BatchUsage(BaseModel): + """ + Represents token usage details including input tokens, output tokens, a + breakdown of output tokens, and the total tokens used. Only populated on + batches created after September 7, 2025. + """ + + input_tokens: int + """The number of input tokens.""" + + input_tokens_details: InputTokensDetails + """A detailed breakdown of the input tokens.""" + + output_tokens: int + """The number of output tokens.""" + + output_tokens_details: OutputTokensDetails + """A detailed breakdown of the output tokens.""" + + total_tokens: int + """The total number of tokens used.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__init__.py b/venv/lib/python3.12/site-packages/openai/types/beta/__init__.py new file mode 100644 index 0000000..dc935b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/__init__.py @@ -0,0 +1,575 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .agent import Agent as Agent +from .thread import Thread as Thread +from .subagent import Subagent as Subagent +from .assistant import Assistant as Assistant +from .beta_tool import BetaTool as BetaTool +from .agent_text import AgentText as AgentText +from .agent_tool import AgentTool as AgentTool +from .environment import Environment as Environment +from .output_text import OutputText as OutputText +from .text_format import TextFormat as TextFormat +from .token_usage import TokenUsage as TokenUsage +from .hosted_skill import HostedSkill as HostedSkill +from .summary_text import SummaryText as SummaryText +from .agent_content import AgentContent as AgentContent +from .agent_deleted import AgentDeleted as AgentDeleted +from .agent_session import AgentSession as AgentSession +from .beta_response import BetaResponse as BetaResponse +from .function_tool import FunctionTool as FunctionTool +from .hosted_plugin import HostedPlugin as HostedPlugin +from .input_content import InputContent as InputContent +from .mcp_transport import McpTransport as McpTransport +from .session_error import SessionError as SessionError +from .assistant_tool import AssistantTool as AssistantTool +from .thread_deleted import ThreadDeleted as ThreadDeleted +from .agent_reasoning import AgentReasoning as AgentReasoning +from .beta_tool_param import BetaToolParam as BetaToolParam +from .agent_text_param import AgentTextParam as AgentTextParam +from .agent_tool_param import AgentToolParam as AgentToolParam +from .beta_custom_tool import BetaCustomTool as BetaCustomTool +from .beta_local_skill import BetaLocalSkill as BetaLocalSkill +from .chatkit_workflow import ChatKitWorkflow as ChatKitWorkflow +from .file_search_tool import FileSearchTool as FileSearchTool +from .agent_list_params import AgentListParams as AgentListParams +from .agent_output_item import AgentOutputItem as AgentOutputItem +from .assistant_deleted import AssistantDeleted as AssistantDeleted +from .beta_image_detail import BetaImageDetail as BetaImageDetail +from .beta_inline_skill import BetaInlineSkill as BetaInlineSkill +from .beta_service_tier import BetaServiceTier as BetaServiceTier +from .environment_param import EnvironmentParam as EnvironmentParam +from .text_format_param import TextFormatParam as TextFormatParam +from .web_search_action import WebSearchAction as WebSearchAction +from .agent_session_item import AgentSessionItem as AgentSessionItem +from .beta_computer_tool import BetaComputerTool as BetaComputerTool +from .beta_function_tool import BetaFunctionTool as BetaFunctionTool +from .beta_response_item import BetaResponseItem as BetaResponseItem +from .hosted_skill_param import HostedSkillParam as HostedSkillParam +from .multi_agent_config import MultiAgentConfig as MultiAgentConfig +from .session_turn_error import SessionTurnError as SessionTurnError +from .agent_create_params import AgentCreateParams as AgentCreateParams +from .agent_mcp_call_item import AgentMcpCallItem as AgentMcpCallItem +from .agent_session_event import AgentSessionEvent as AgentSessionEvent +from .agent_update_params import AgentUpdateParams as AgentUpdateParams +from .beta_container_auto import BetaContainerAuto as BetaContainerAuto +from .beta_namespace_tool import BetaNamespaceTool as BetaNamespaceTool +from .beta_response_error import BetaResponseError as BetaResponseError +from .beta_response_input import BetaResponseInput as BetaResponseInput +from .beta_response_usage import BetaResponseUsage as BetaResponseUsage +from .function_tool_param import FunctionToolParam as FunctionToolParam +from .hosted_plugin_param import HostedPluginParam as HostedPluginParam +from .input_content_param import InputContentParam as InputContentParam +from .mcp_transport_param import McpTransportParam as McpTransportParam +from .setup_command_param import SetupCommandParam as SetupCommandParam +from .agent_reasoning_item import AgentReasoningItem as AgentReasoningItem +from .assistant_tool_param import AssistantToolParam as AssistantToolParam +from .beta_computer_action import BetaComputerAction as BetaComputerAction +from .beta_response_prompt import BetaResponsePrompt as BetaResponsePrompt +from .beta_response_status import BetaResponseStatus as BetaResponseStatus +from .beta_skill_reference import BetaSkillReference as BetaSkillReference +from .beta_tool_choice_mcp import BetaToolChoiceMcp as BetaToolChoiceMcp +from .beta_web_search_tool import BetaWebSearchTool as BetaWebSearchTool +from .persisted_agent_tool import PersistedAgentTool as PersistedAgentTool +from .thread_create_params import ThreadCreateParams as ThreadCreateParams +from .thread_update_params import ThreadUpdateParams as ThreadUpdateParams +from .agent_reasoning_param import AgentReasoningParam as AgentReasoningParam +from .agent_session_deleted import AgentSessionDeleted as AgentSessionDeleted +from .agent_session_message import AgentSessionMessage as AgentSessionMessage +from .assistant_list_params import AssistantListParams as AssistantListParams +from .assistant_tool_choice import AssistantToolChoice as AssistantToolChoice +from .beta_apply_patch_tool import BetaApplyPatchTool as BetaApplyPatchTool +from .beta_file_search_tool import BetaFileSearchTool as BetaFileSearchTool +from .beta_tool_search_tool import BetaToolSearchTool as BetaToolSearchTool +from .code_interpreter_tool import CodeInterpreterTool as CodeInterpreterTool +from .assistant_stream_event import AssistantStreamEvent as AssistantStreamEvent +from .beta_custom_tool_param import BetaCustomToolParam as BetaCustomToolParam +from .beta_local_environment import BetaLocalEnvironment as BetaLocalEnvironment +from .beta_local_skill_param import BetaLocalSkillParam as BetaLocalSkillParam +from .beta_tool_choice_shell import BetaToolChoiceShell as BetaToolChoiceShell +from .beta_tool_choice_types import BetaToolChoiceTypes as BetaToolChoiceTypes +from .file_search_tool_param import FileSearchToolParam as FileSearchToolParam +from .hosted_skill_reference import HostedSkillReference as HostedSkillReference +from .response_create_params import ResponseCreateParams as ResponseCreateParams +from .assistant_create_params import AssistantCreateParams as AssistantCreateParams +from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams +from .beta_compacted_response import BetaCompactedResponse as BetaCompactedResponse +from .beta_easy_input_message import BetaEasyInputMessage as BetaEasyInputMessage +from .beta_inline_skill_param import BetaInlineSkillParam as BetaInlineSkillParam +from .beta_tool_choice_custom import BetaToolChoiceCustom as BetaToolChoiceCustom +from .hosted_environment_file import HostedEnvironmentFile as HostedEnvironmentFile +from .persisted_mcp_transport import PersistedMcpTransport as PersistedMcpTransport +from .response_compact_params import ResponseCompactParams as ResponseCompactParams +from .agent_function_call_item import AgentFunctionCallItem as AgentFunctionCallItem +from .agent_output_item_status import AgentOutputItemStatus as AgentOutputItemStatus +from .agent_session_idle_event import AgentSessionIdleEvent as AgentSessionIdleEvent +from .beta_computer_tool_param import BetaComputerToolParam as BetaComputerToolParam +from .beta_container_reference import BetaContainerReference as BetaContainerReference +from .beta_function_shell_tool import BetaFunctionShellTool as BetaFunctionShellTool +from .beta_function_tool_param import BetaFunctionToolParam as BetaFunctionToolParam +from .beta_inline_skill_source import BetaInlineSkillSource as BetaInlineSkillSource +from .beta_mcp_tool_call_error import BetaMcpToolCallError as BetaMcpToolCallError +from .beta_response_includable import BetaResponseIncludable as BetaResponseIncludable +from .beta_response_input_file import BetaResponseInputFile as BetaResponseInputFile +from .beta_response_input_item import BetaResponseInputItem as BetaResponseInputItem +from .beta_response_input_text import BetaResponseInputText as BetaResponseInputText +from .beta_tool_choice_allowed import BetaToolChoiceAllowed as BetaToolChoiceAllowed +from .beta_tool_choice_options import BetaToolChoiceOptions as BetaToolChoiceOptions +from .multi_agent_config_param import MultiAgentConfigParam as MultiAgentConfigParam +from .response_retrieve_params import ResponseRetrieveParams as ResponseRetrieveParams +from .agent_session_error_event import AgentSessionErrorEvent as AgentSessionErrorEvent +from .agent_session_input_param import AgentSessionInputParam as AgentSessionInputParam +from .beta_computer_action_list import BetaComputerActionList as BetaComputerActionList +from .beta_container_auto_param import BetaContainerAutoParam as BetaContainerAutoParam +from .beta_namespace_tool_param import BetaNamespaceToolParam as BetaNamespaceToolParam +from .beta_response_error_event import BetaResponseErrorEvent as BetaResponseErrorEvent +from .beta_response_input_image import BetaResponseInputImage as BetaResponseInputImage +from .beta_response_input_param import BetaResponseInputParam as BetaResponseInputParam +from .beta_response_output_item import BetaResponseOutputItem as BetaResponseOutputItem +from .beta_response_output_text import BetaResponseOutputText as BetaResponseOutputText +from .beta_response_steer_event import BetaResponseSteerEvent as BetaResponseSteerEvent +from .beta_response_steer_input import BetaResponseSteerInput as BetaResponseSteerInput +from .beta_response_text_config import BetaResponseTextConfig as BetaResponseTextConfig +from .beta_tool_choice_function import BetaToolChoiceFunction as BetaToolChoiceFunction +from .agent_function_call_output import AgentFunctionCallOutput as AgentFunctionCallOutput +from .agent_function_call_status import AgentFunctionCallStatus as AgentFunctionCallStatus +from .agent_session_failed_event import AgentSessionFailedEvent as AgentSessionFailedEvent +from .agent_web_search_call_item import AgentWebSearchCallItem as AgentWebSearchCallItem +from .beta_computer_action_param import BetaComputerActionParam as BetaComputerActionParam +from .beta_response_failed_event import BetaResponseFailedEvent as BetaResponseFailedEvent +from .beta_response_inject_event import BetaResponseInjectEvent as BetaResponseInjectEvent +from .beta_response_prompt_param import BetaResponsePromptParam as BetaResponsePromptParam +from .beta_response_queued_event import BetaResponseQueuedEvent as BetaResponseQueuedEvent +from .beta_response_stream_event import BetaResponseStreamEvent as BetaResponseStreamEvent +from .beta_skill_reference_param import BetaSkillReferenceParam as BetaSkillReferenceParam +from .beta_tool_choice_mcp_param import BetaToolChoiceMcpParam as BetaToolChoiceMcpParam +from .beta_web_search_tool_param import BetaWebSearchToolParam as BetaWebSearchToolParam +from .hosted_environment_file_id import HostedEnvironmentFileID as HostedEnvironmentFileID +from .persisted_agent_tool_param import PersistedAgentToolParam as PersistedAgentToolParam +from .agent_session_created_event import AgentSessionCreatedEvent as AgentSessionCreatedEvent +from .assistant_tool_choice_param import AssistantToolChoiceParam as AssistantToolChoiceParam +from .beta_apply_patch_tool_param import BetaApplyPatchToolParam as BetaApplyPatchToolParam +from .beta_file_search_tool_param import BetaFileSearchToolParam as BetaFileSearchToolParam +from .beta_response_created_event import BetaResponseCreatedEvent as BetaResponseCreatedEvent +from .beta_response_input_content import BetaResponseInputContent as BetaResponseInputContent +from .beta_responses_client_event import BetaResponsesClientEvent as BetaResponsesClientEvent +from .beta_responses_server_event import BetaResponsesServerEvent as BetaResponsesServerEvent +from .beta_tool_search_tool_param import BetaToolSearchToolParam as BetaToolSearchToolParam +from .code_interpreter_tool_param import CodeInterpreterToolParam as CodeInterpreterToolParam +from .agent_command_execution_item import AgentCommandExecutionItem as AgentCommandExecutionItem +from .assistant_tool_choice_option import AssistantToolChoiceOption as AssistantToolChoiceOption +from .beta_local_environment_param import BetaLocalEnvironmentParam as BetaLocalEnvironmentParam +from .beta_response_output_message import BetaResponseOutputMessage as BetaResponseOutputMessage +from .beta_response_output_refusal import BetaResponseOutputRefusal as BetaResponseOutputRefusal +from .beta_response_reasoning_item import BetaResponseReasoningItem as BetaResponseReasoningItem +from .beta_tool_choice_apply_patch import BetaToolChoiceApplyPatch as BetaToolChoiceApplyPatch +from .beta_tool_choice_shell_param import BetaToolChoiceShellParam as BetaToolChoiceShellParam +from .beta_tool_choice_types_param import BetaToolChoiceTypesParam as BetaToolChoiceTypesParam +from .beta_web_search_preview_tool import BetaWebSearchPreviewTool as BetaWebSearchPreviewTool +from .thread_create_and_run_params import ThreadCreateAndRunParams as ThreadCreateAndRunParams +from .agent_session_message_content import AgentSessionMessageContent as AgentSessionMessageContent +from .beta_easy_input_message_param import BetaEasyInputMessageParam as BetaEasyInputMessageParam +from .beta_response_compaction_item import BetaResponseCompactionItem as BetaResponseCompactionItem +from .beta_response_completed_event import BetaResponseCompletedEvent as BetaResponseCompletedEvent +from .beta_response_text_done_event import BetaResponseTextDoneEvent as BetaResponseTextDoneEvent +from .beta_tool_choice_custom_param import BetaToolChoiceCustomParam as BetaToolChoiceCustomParam +from .hosted_environment_file_param import HostedEnvironmentFileParam as HostedEnvironmentFileParam +from .persisted_mcp_transport_param import PersistedMcpTransportParam as PersistedMcpTransportParam +from .agent_close_subagent_call_item import AgentCloseSubagentCallItem as AgentCloseSubagentCallItem +from .assistant_tool_choice_function import AssistantToolChoiceFunction as AssistantToolChoiceFunction +from .beta_computer_use_preview_tool import BetaComputerUsePreviewTool as BetaComputerUsePreviewTool +from .beta_container_reference_param import BetaContainerReferenceParam as BetaContainerReferenceParam +from .beta_function_shell_tool_param import BetaFunctionShellToolParam as BetaFunctionShellToolParam +from .beta_inline_skill_source_param import BetaInlineSkillSourceParam as BetaInlineSkillSourceParam +from .beta_mcp_tool_call_error_param import BetaMcpToolCallErrorParam as BetaMcpToolCallErrorParam +from .beta_response_audio_done_event import BetaResponseAudioDoneEvent as BetaResponseAudioDoneEvent +from .beta_response_custom_tool_call import BetaResponseCustomToolCall as BetaResponseCustomToolCall +from .beta_response_incomplete_event import BetaResponseIncompleteEvent as BetaResponseIncompleteEvent +from .beta_response_input_file_param import BetaResponseInputFileParam as BetaResponseInputFileParam +from .beta_response_input_item_param import BetaResponseInputItemParam as BetaResponseInputItemParam +from .beta_response_input_text_param import BetaResponseInputTextParam as BetaResponseInputTextParam +from .beta_response_steer_error_code import BetaResponseSteerErrorCode as BetaResponseSteerErrorCode +from .beta_response_text_delta_event import BetaResponseTextDeltaEvent as BetaResponseTextDeltaEvent +from .beta_response_tool_search_call import BetaResponseToolSearchCall as BetaResponseToolSearchCall +from .beta_tool_choice_allowed_param import BetaToolChoiceAllowedParam as BetaToolChoiceAllowedParam +from .inline_capability_source_param import InlineCapabilitySourceParam as InlineCapabilitySourceParam +from .agent_create_subagent_call_item import AgentCreateSubagentCallItem as AgentCreateSubagentCallItem +from .agent_resume_subagent_call_item import AgentResumeSubagentCallItem as AgentResumeSubagentCallItem +from .agent_session_assistant_message import AgentSessionAssistantMessage as AgentSessionAssistantMessage +from .agent_session_environment_state import AgentSessionEnvironmentState as AgentSessionEnvironmentState +from .agent_session_in_progress_event import AgentSessionInProgressEvent as AgentSessionInProgressEvent +from .agent_session_turn_failed_event import AgentSessionTurnFailedEvent as AgentSessionTurnFailedEvent +from .beta_computer_action_list_param import BetaComputerActionListParam as BetaComputerActionListParam +from .beta_response_audio_delta_event import BetaResponseAudioDeltaEvent as BetaResponseAudioDeltaEvent +from .beta_response_in_progress_event import BetaResponseInProgressEvent as BetaResponseInProgressEvent +from .beta_response_input_image_param import BetaResponseInputImageParam as BetaResponseInputImageParam +from .beta_response_local_environment import BetaResponseLocalEnvironment as BetaResponseLocalEnvironment +from .beta_response_output_text_param import BetaResponseOutputTextParam as BetaResponseOutputTextParam +from .beta_response_steer_event_param import BetaResponseSteerEventParam as BetaResponseSteerEventParam +from .beta_response_steer_input_param import BetaResponseSteerInputParam as BetaResponseSteerInputParam +from .beta_response_text_config_param import BetaResponseTextConfigParam as BetaResponseTextConfigParam +from .beta_tool_choice_function_param import BetaToolChoiceFunctionParam as BetaToolChoiceFunctionParam +from .agent_function_call_output_param import AgentFunctionCallOutputParam as AgentFunctionCallOutputParam +from .agent_session_turn_created_event import AgentSessionTurnCreatedEvent as AgentSessionTurnCreatedEvent +from .assistant_response_format_option import AssistantResponseFormatOption as AssistantResponseFormatOption +from .beta_response_computer_tool_call import BetaResponseComputerToolCall as BetaResponseComputerToolCall +from .beta_response_conversation_param import BetaResponseConversationParam as BetaResponseConversationParam +from .beta_response_format_text_config import BetaResponseFormatTextConfig as BetaResponseFormatTextConfig +from .beta_response_function_tool_call import BetaResponseFunctionToolCall as BetaResponseFunctionToolCall +from .beta_response_inject_event_param import BetaResponseInjectEventParam as BetaResponseInjectEventParam +from .beta_response_input_file_content import BetaResponseInputFileContent as BetaResponseInputFileContent +from .beta_response_input_message_item import BetaResponseInputMessageItem as BetaResponseInputMessageItem +from .beta_response_input_text_content import BetaResponseInputTextContent as BetaResponseInputTextContent +from .beta_response_refusal_done_event import BetaResponseRefusalDoneEvent as BetaResponseRefusalDoneEvent +from .beta_response_steer_failed_event import BetaResponseSteerFailedEvent as BetaResponseSteerFailedEvent +from .agent_session_input_message_param import AgentSessionInputMessageParam as AgentSessionInputMessageParam +from .beta_response_container_reference import BetaResponseContainerReference as BetaResponseContainerReference +from .beta_response_function_web_search import BetaResponseFunctionWebSearch as BetaResponseFunctionWebSearch +from .beta_response_inject_failed_event import BetaResponseInjectFailedEvent as BetaResponseInjectFailedEvent +from .beta_response_input_content_param import BetaResponseInputContentParam as BetaResponseInputContentParam +from .beta_response_input_image_content import BetaResponseInputImageContent as BetaResponseInputImageContent +from .beta_response_refusal_delta_event import BetaResponseRefusalDeltaEvent as BetaResponseRefusalDeltaEvent +from .beta_response_steer_input_content import BetaResponseSteerInputContent as BetaResponseSteerInputContent +from .beta_response_steer_pending_event import BetaResponseSteerPendingEvent as BetaResponseSteerPendingEvent +from .beta_responses_client_event_param import BetaResponsesClientEventParam as BetaResponsesClientEventParam +from .agent_interrupt_subagent_call_item import AgentInterruptSubagentCallItem as AgentInterruptSubagentCallItem +from .agent_session_turn_cancelled_event import AgentSessionTurnCancelledEvent as AgentSessionTurnCancelledEvent +from .agent_session_turn_completed_event import AgentSessionTurnCompletedEvent as AgentSessionTurnCompletedEvent +from .agent_session_turn_item_done_event import AgentSessionTurnItemDoneEvent as AgentSessionTurnItemDoneEvent +from .agent_wait_for_subagents_call_item import AgentWaitForSubagentsCallItem as AgentWaitForSubagentsCallItem +from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam as AssistantToolChoiceOptionParam +from .beta_response_inject_created_event import BetaResponseInjectCreatedEvent as BetaResponseInjectCreatedEvent +from .beta_response_output_message_param import BetaResponseOutputMessageParam as BetaResponseOutputMessageParam +from .beta_response_output_refusal_param import BetaResponseOutputRefusalParam as BetaResponseOutputRefusalParam +from .beta_response_reasoning_item_param import BetaResponseReasoningItemParam as BetaResponseReasoningItemParam +from .beta_response_steer_accepted_event import BetaResponseSteerAcceptedEvent as BetaResponseSteerAcceptedEvent +from .beta_response_steer_pending_reason import BetaResponseSteerPendingReason as BetaResponseSteerPendingReason +from .beta_response_steer_required_input import BetaResponseSteerRequiredInput as BetaResponseSteerRequiredInput +from .beta_tool_choice_apply_patch_param import BetaToolChoiceApplyPatchParam as BetaToolChoiceApplyPatchParam +from .beta_web_search_preview_tool_param import BetaWebSearchPreviewToolParam as BetaWebSearchPreviewToolParam +from .agent_send_subagent_input_call_item import AgentSendSubagentInputCallItem as AgentSendSubagentInputCallItem +from .agent_session_requires_action_event import AgentSessionRequiresActionEvent as AgentSessionRequiresActionEvent +from .agent_session_subagent_active_event import AgentSessionSubagentActiveEvent as AgentSessionSubagentActiveEvent +from .agent_session_subagent_closed_event import AgentSessionSubagentClosedEvent as AgentSessionSubagentClosedEvent +from .agent_session_turn_item_added_event import AgentSessionTurnItemAddedEvent as AgentSessionTurnItemAddedEvent +from .beta_response_apply_patch_tool_call import BetaResponseApplyPatchToolCall as BetaResponseApplyPatchToolCall +from .beta_response_compaction_item_param import BetaResponseCompactionItemParam as BetaResponseCompactionItemParam +from .beta_response_custom_tool_call_item import BetaResponseCustomToolCallItem as BetaResponseCustomToolCallItem +from .beta_response_file_search_tool_call import BetaResponseFileSearchToolCall as BetaResponseFileSearchToolCall +from .beta_response_mcp_call_failed_event import BetaResponseMcpCallFailedEvent as BetaResponseMcpCallFailedEvent +from .agent_session_subagent_created_event import AgentSessionSubagentCreatedEvent as AgentSessionSubagentCreatedEvent +from .agent_session_turn_in_progress_event import AgentSessionTurnInProgressEvent as AgentSessionTurnInProgressEvent +from .assistant_tool_choice_function_param import AssistantToolChoiceFunctionParam as AssistantToolChoiceFunctionParam +from .beta_computer_use_preview_tool_param import BetaComputerUsePreviewToolParam as BetaComputerUsePreviewToolParam +from .beta_response_custom_tool_call_param import BetaResponseCustomToolCallParam as BetaResponseCustomToolCallParam +from .beta_response_output_item_done_event import BetaResponseOutputItemDoneEvent as BetaResponseOutputItemDoneEvent +from .agent_session_environment_ready_event import ( + AgentSessionEnvironmentReadyEvent as AgentSessionEnvironmentReadyEvent, +) +from .agent_session_environment_reset_event import ( + AgentSessionEnvironmentResetEvent as AgentSessionEnvironmentResetEvent, +) +from .beta_response_content_part_done_event import BetaResponseContentPartDoneEvent as BetaResponseContentPartDoneEvent +from .beta_response_custom_tool_call_output import BetaResponseCustomToolCallOutput as BetaResponseCustomToolCallOutput +from .beta_response_function_tool_call_item import BetaResponseFunctionToolCallItem as BetaResponseFunctionToolCallItem +from .beta_response_output_item_added_event import BetaResponseOutputItemAddedEvent as BetaResponseOutputItemAddedEvent +from .beta_response_tool_search_output_item import BetaResponseToolSearchOutputItem as BetaResponseToolSearchOutputItem +from .agent_session_environment_failed_event import ( + AgentSessionEnvironmentFailedEvent as AgentSessionEnvironmentFailedEvent, +) +from .assistant_response_format_option_param import ( + AssistantResponseFormatOptionParam as AssistantResponseFormatOptionParam, +) +from .beta_container_network_policy_disabled import ( + BetaContainerNetworkPolicyDisabled as BetaContainerNetworkPolicyDisabled, +) +from .beta_response_computer_tool_call_param import ( + BetaResponseComputerToolCallParam as BetaResponseComputerToolCallParam, +) +from .beta_response_content_part_added_event import ( + BetaResponseContentPartAddedEvent as BetaResponseContentPartAddedEvent, +) +from .beta_response_conversation_param_param import ( + BetaResponseConversationParamParam as BetaResponseConversationParamParam, +) +from .beta_response_format_text_config_param import ( + BetaResponseFormatTextConfigParam as BetaResponseFormatTextConfigParam, +) +from .beta_response_function_shell_tool_call import ( + BetaResponseFunctionShellToolCall as BetaResponseFunctionShellToolCall, +) +from .beta_response_function_tool_call_param import ( + BetaResponseFunctionToolCallParam as BetaResponseFunctionToolCallParam, +) +from .beta_response_input_file_content_param import ( + BetaResponseInputFileContentParam as BetaResponseInputFileContentParam, +) +from .beta_response_input_text_content_param import ( + BetaResponseInputTextContentParam as BetaResponseInputTextContentParam, +) +from .beta_response_mcp_call_completed_event import ( + BetaResponseMcpCallCompletedEvent as BetaResponseMcpCallCompletedEvent, +) +from .agent_session_environment_pending_event import ( + AgentSessionEnvironmentPendingEvent as AgentSessionEnvironmentPendingEvent, +) +from .beta_container_network_policy_allowlist import ( + BetaContainerNetworkPolicyAllowlist as BetaContainerNetworkPolicyAllowlist, +) +from .beta_response_configuration_update_item import ( + BetaResponseConfigurationUpdateItem as BetaResponseConfigurationUpdateItem, +) +from .beta_response_function_call_output_item import ( + BetaResponseFunctionCallOutputItem as BetaResponseFunctionCallOutputItem, +) +from .beta_response_function_web_search_param import ( + BetaResponseFunctionWebSearchParam as BetaResponseFunctionWebSearchParam, +) +from .beta_response_input_image_content_param import ( + BetaResponseInputImageContentParam as BetaResponseInputImageContentParam, +) +from .beta_response_reasoning_text_done_event import ( + BetaResponseReasoningTextDoneEvent as BetaResponseReasoningTextDoneEvent, +) +from .beta_response_steer_input_content_param import ( + BetaResponseSteerInputContentParam as BetaResponseSteerInputContentParam, +) +from .beta_response_code_interpreter_tool_call import ( + BetaResponseCodeInterpreterToolCall as BetaResponseCodeInterpreterToolCall, +) +from .beta_response_input_message_content_list import ( + BetaResponseInputMessageContentList as BetaResponseInputMessageContentList, +) +from .beta_response_mcp_call_in_progress_event import ( + BetaResponseMcpCallInProgressEvent as BetaResponseMcpCallInProgressEvent, +) +from .beta_response_reasoning_text_delta_event import ( + BetaResponseReasoningTextDeltaEvent as BetaResponseReasoningTextDeltaEvent, +) +from .agent_session_environment_connected_event import ( + AgentSessionEnvironmentConnectedEvent as AgentSessionEnvironmentConnectedEvent, +) +from .agent_session_turn_output_text_done_event import ( + AgentSessionTurnOutputTextDoneEvent as AgentSessionTurnOutputTextDoneEvent, +) +from .beta_response_audio_transcript_done_event import ( + BetaResponseAudioTranscriptDoneEvent as BetaResponseAudioTranscriptDoneEvent, +) +from .beta_response_compaction_compacting_event import ( + BetaResponseCompactionCompactingEvent as BetaResponseCompactionCompactingEvent, +) +from .beta_response_compaction_item_param_param import ( + BetaResponseCompactionItemParamParam as BetaResponseCompactionItemParamParam, +) +from .beta_response_file_search_tool_call_param import ( + BetaResponseFileSearchToolCallParam as BetaResponseFileSearchToolCallParam, +) +from .beta_response_mcp_list_tools_failed_event import ( + BetaResponseMcpListToolsFailedEvent as BetaResponseMcpListToolsFailedEvent, +) +from .agent_session_turn_content_part_done_event import ( + AgentSessionTurnContentPartDoneEvent as AgentSessionTurnContentPartDoneEvent, +) +from .agent_session_turn_output_text_delta_event import ( + AgentSessionTurnOutputTextDeltaEvent as AgentSessionTurnOutputTextDeltaEvent, +) +from .beta_response_apply_patch_tool_call_output import ( + BetaResponseApplyPatchToolCallOutput as BetaResponseApplyPatchToolCallOutput, +) +from .beta_response_audio_transcript_delta_event import ( + BetaResponseAudioTranscriptDeltaEvent as BetaResponseAudioTranscriptDeltaEvent, +) +from .beta_response_custom_tool_call_output_item import ( + BetaResponseCustomToolCallOutputItem as BetaResponseCustomToolCallOutputItem, +) +from .agent_session_turn_content_part_added_event import ( + AgentSessionTurnContentPartAddedEvent as AgentSessionTurnContentPartAddedEvent, +) +from .beta_container_network_policy_domain_secret import ( + BetaContainerNetworkPolicyDomainSecret as BetaContainerNetworkPolicyDomainSecret, +) +from .beta_response_custom_tool_call_output_param import ( + BetaResponseCustomToolCallOutputParam as BetaResponseCustomToolCallOutputParam, +) +from .beta_response_mcp_call_arguments_done_event import ( + BetaResponseMcpCallArgumentsDoneEvent as BetaResponseMcpCallArgumentsDoneEvent, +) +from .beta_response_shell_call_command_done_event import ( + BetaResponseShellCallCommandDoneEvent as BetaResponseShellCallCommandDoneEvent, +) +from .beta_response_tool_search_output_item_param import ( + BetaResponseToolSearchOutputItemParam as BetaResponseToolSearchOutputItemParam, +) +from .agent_session_environment_disconnected_event import ( + AgentSessionEnvironmentDisconnectedEvent as AgentSessionEnvironmentDisconnectedEvent, +) +from .beta_container_network_policy_disabled_param import ( + BetaContainerNetworkPolicyDisabledParam as BetaContainerNetworkPolicyDisabledParam, +) +from .beta_response_computer_tool_call_output_item import ( + BetaResponseComputerToolCallOutputItem as BetaResponseComputerToolCallOutputItem, +) +from .beta_response_format_text_json_schema_config import ( + BetaResponseFormatTextJSONSchemaConfig as BetaResponseFormatTextJSONSchemaConfig, +) +from .beta_response_function_call_output_item_list import ( + BetaResponseFunctionCallOutputItemList as BetaResponseFunctionCallOutputItemList, +) +from .beta_response_function_tool_call_output_item import ( + BetaResponseFunctionToolCallOutputItem as BetaResponseFunctionToolCallOutputItem, +) +from .beta_response_image_gen_call_completed_event import ( + BetaResponseImageGenCallCompletedEvent as BetaResponseImageGenCallCompletedEvent, +) +from .beta_response_mcp_call_arguments_delta_event import ( + BetaResponseMcpCallArgumentsDeltaEvent as BetaResponseMcpCallArgumentsDeltaEvent, +) +from .beta_response_mcp_list_tools_completed_event import ( + BetaResponseMcpListToolsCompletedEvent as BetaResponseMcpListToolsCompletedEvent, +) +from .beta_response_shell_call_command_added_event import ( + BetaResponseShellCallCommandAddedEvent as BetaResponseShellCallCommandAddedEvent, +) +from .beta_response_shell_call_command_delta_event import ( + BetaResponseShellCallCommandDeltaEvent as BetaResponseShellCallCommandDeltaEvent, +) +from .beta_container_network_policy_allowlist_param import ( + BetaContainerNetworkPolicyAllowlistParam as BetaContainerNetworkPolicyAllowlistParam, +) +from .beta_response_configuration_update_item_param import ( + BetaResponseConfigurationUpdateItemParam as BetaResponseConfigurationUpdateItemParam, +) +from .beta_response_function_call_output_item_param import ( + BetaResponseFunctionCallOutputItemParam as BetaResponseFunctionCallOutputItemParam, +) +from .beta_response_function_shell_tool_call_output import ( + BetaResponseFunctionShellToolCallOutput as BetaResponseFunctionShellToolCallOutput, +) +from .beta_response_image_gen_call_generating_event import ( + BetaResponseImageGenCallGeneratingEvent as BetaResponseImageGenCallGeneratingEvent, +) +from .beta_response_web_search_call_completed_event import ( + BetaResponseWebSearchCallCompletedEvent as BetaResponseWebSearchCallCompletedEvent, +) +from .beta_response_web_search_call_searching_event import ( + BetaResponseWebSearchCallSearchingEvent as BetaResponseWebSearchCallSearchingEvent, +) +from .beta_response_code_interpreter_tool_call_param import ( + BetaResponseCodeInterpreterToolCallParam as BetaResponseCodeInterpreterToolCallParam, +) +from .beta_response_file_search_call_completed_event import ( + BetaResponseFileSearchCallCompletedEvent as BetaResponseFileSearchCallCompletedEvent, +) +from .beta_response_file_search_call_searching_event import ( + BetaResponseFileSearchCallSearchingEvent as BetaResponseFileSearchCallSearchingEvent, +) +from .beta_response_image_gen_call_in_progress_event import ( + BetaResponseImageGenCallInProgressEvent as BetaResponseImageGenCallInProgressEvent, +) +from .beta_response_input_message_content_list_param import ( + BetaResponseInputMessageContentListParam as BetaResponseInputMessageContentListParam, +) +from .beta_response_mcp_list_tools_in_progress_event import ( + BetaResponseMcpListToolsInProgressEvent as BetaResponseMcpListToolsInProgressEvent, +) +from .beta_response_custom_tool_call_input_done_event import ( + BetaResponseCustomToolCallInputDoneEvent as BetaResponseCustomToolCallInputDoneEvent, +) +from .beta_response_reasoning_summary_part_done_event import ( + BetaResponseReasoningSummaryPartDoneEvent as BetaResponseReasoningSummaryPartDoneEvent, +) +from .beta_response_reasoning_summary_text_done_event import ( + BetaResponseReasoningSummaryTextDoneEvent as BetaResponseReasoningSummaryTextDoneEvent, +) +from .beta_response_web_search_call_in_progress_event import ( + BetaResponseWebSearchCallInProgressEvent as BetaResponseWebSearchCallInProgressEvent, +) +from .beta_response_custom_tool_call_input_delta_event import ( + BetaResponseCustomToolCallInputDeltaEvent as BetaResponseCustomToolCallInputDeltaEvent, +) +from .beta_response_file_search_call_in_progress_event import ( + BetaResponseFileSearchCallInProgressEvent as BetaResponseFileSearchCallInProgressEvent, +) +from .beta_response_function_call_arguments_done_event import ( + BetaResponseFunctionCallArgumentsDoneEvent as BetaResponseFunctionCallArgumentsDoneEvent, +) +from .beta_response_function_shell_call_output_content import ( + BetaResponseFunctionShellCallOutputContent as BetaResponseFunctionShellCallOutputContent, +) +from .beta_response_image_gen_call_partial_image_event import ( + BetaResponseImageGenCallPartialImageEvent as BetaResponseImageGenCallPartialImageEvent, +) +from .beta_response_output_text_annotation_added_event import ( + BetaResponseOutputTextAnnotationAddedEvent as BetaResponseOutputTextAnnotationAddedEvent, +) +from .beta_response_reasoning_summary_part_added_event import ( + BetaResponseReasoningSummaryPartAddedEvent as BetaResponseReasoningSummaryPartAddedEvent, +) +from .beta_response_reasoning_summary_text_delta_event import ( + BetaResponseReasoningSummaryTextDeltaEvent as BetaResponseReasoningSummaryTextDeltaEvent, +) +from .agent_output_command_execution_output_delta_event import ( + AgentOutputCommandExecutionOutputDeltaEvent as AgentOutputCommandExecutionOutputDeltaEvent, +) +from .beta_container_network_policy_domain_secret_param import ( + BetaContainerNetworkPolicyDomainSecretParam as BetaContainerNetworkPolicyDomainSecretParam, +) +from .beta_response_function_call_arguments_delta_event import ( + BetaResponseFunctionCallArgumentsDeltaEvent as BetaResponseFunctionCallArgumentsDeltaEvent, +) +from .beta_response_tool_search_output_item_param_param import ( + BetaResponseToolSearchOutputItemParamParam as BetaResponseToolSearchOutputItemParamParam, +) +from .beta_response_computer_tool_call_output_screenshot import ( + BetaResponseComputerToolCallOutputScreenshot as BetaResponseComputerToolCallOutputScreenshot, +) +from .beta_response_format_text_json_schema_config_param import ( + BetaResponseFormatTextJSONSchemaConfigParam as BetaResponseFormatTextJSONSchemaConfigParam, +) +from .beta_response_function_call_output_item_list_param import ( + BetaResponseFunctionCallOutputItemListParam as BetaResponseFunctionCallOutputItemListParam, +) +from .beta_response_shell_call_output_content_done_event import ( + BetaResponseShellCallOutputContentDoneEvent as BetaResponseShellCallOutputContentDoneEvent, +) +from .beta_response_code_interpreter_call_code_done_event import ( + BetaResponseCodeInterpreterCallCodeDoneEvent as BetaResponseCodeInterpreterCallCodeDoneEvent, +) +from .beta_response_code_interpreter_call_completed_event import ( + BetaResponseCodeInterpreterCallCompletedEvent as BetaResponseCodeInterpreterCallCompletedEvent, +) +from .beta_response_configuration_update_item_param_param import ( + BetaResponseConfigurationUpdateItemParamParam as BetaResponseConfigurationUpdateItemParamParam, +) +from .beta_response_shell_call_output_content_delta_event import ( + BetaResponseShellCallOutputContentDeltaEvent as BetaResponseShellCallOutputContentDeltaEvent, +) +from .agent_session_turn_reasoning_summary_part_done_event import ( + AgentSessionTurnReasoningSummaryPartDoneEvent as AgentSessionTurnReasoningSummaryPartDoneEvent, +) +from .agent_session_turn_reasoning_summary_text_done_event import ( + AgentSessionTurnReasoningSummaryTextDoneEvent as AgentSessionTurnReasoningSummaryTextDoneEvent, +) +from .beta_response_code_interpreter_call_code_delta_event import ( + BetaResponseCodeInterpreterCallCodeDeltaEvent as BetaResponseCodeInterpreterCallCodeDeltaEvent, +) +from .agent_session_turn_reasoning_summary_part_added_event import ( + AgentSessionTurnReasoningSummaryPartAddedEvent as AgentSessionTurnReasoningSummaryPartAddedEvent, +) +from .agent_session_turn_reasoning_summary_text_delta_event import ( + AgentSessionTurnReasoningSummaryTextDeltaEvent as AgentSessionTurnReasoningSummaryTextDeltaEvent, +) +from .beta_response_code_interpreter_call_in_progress_event import ( + BetaResponseCodeInterpreterCallInProgressEvent as BetaResponseCodeInterpreterCallInProgressEvent, +) +from .beta_response_code_interpreter_call_interpreting_event import ( + BetaResponseCodeInterpreterCallInterpretingEvent as BetaResponseCodeInterpreterCallInterpretingEvent, +) +from .beta_response_function_shell_call_output_content_param import ( + BetaResponseFunctionShellCallOutputContentParam as BetaResponseFunctionShellCallOutputContentParam, +) +from .beta_response_computer_tool_call_output_screenshot_param import ( + BetaResponseComputerToolCallOutputScreenshotParam as BetaResponseComputerToolCallOutputScreenshotParam, +) diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..261e5dd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent.cpython-312.pyc new file mode 100644 index 0000000..d70e9b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_close_subagent_call_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_close_subagent_call_item.cpython-312.pyc new file mode 100644 index 0000000..048f918 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_close_subagent_call_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_command_execution_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_command_execution_item.cpython-312.pyc new file mode 100644 index 0000000..fc85849 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_command_execution_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_content.cpython-312.pyc new file mode 100644 index 0000000..5e40001 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_create_params.cpython-312.pyc new file mode 100644 index 0000000..8a241ca Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_create_subagent_call_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_create_subagent_call_item.cpython-312.pyc new file mode 100644 index 0000000..57bf90c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_create_subagent_call_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_deleted.cpython-312.pyc new file mode 100644 index 0000000..a31a8de Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_function_call_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_function_call_item.cpython-312.pyc new file mode 100644 index 0000000..46f20d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_function_call_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_function_call_output.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_function_call_output.cpython-312.pyc new file mode 100644 index 0000000..e6ac991 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_function_call_output.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_function_call_output_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_function_call_output_param.cpython-312.pyc new file mode 100644 index 0000000..5ec9600 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_function_call_output_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_function_call_status.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_function_call_status.cpython-312.pyc new file mode 100644 index 0000000..8767acf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_function_call_status.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_interrupt_subagent_call_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_interrupt_subagent_call_item.cpython-312.pyc new file mode 100644 index 0000000..661b4a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_interrupt_subagent_call_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_list_params.cpython-312.pyc new file mode 100644 index 0000000..70ff771 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_mcp_call_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_mcp_call_item.cpython-312.pyc new file mode 100644 index 0000000..c546ef9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_mcp_call_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_output_command_execution_output_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_output_command_execution_output_delta_event.cpython-312.pyc new file mode 100644 index 0000000..acfea4d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_output_command_execution_output_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_output_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_output_item.cpython-312.pyc new file mode 100644 index 0000000..f3002e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_output_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_output_item_status.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_output_item_status.cpython-312.pyc new file mode 100644 index 0000000..7a8b7ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_output_item_status.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_reasoning.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_reasoning.cpython-312.pyc new file mode 100644 index 0000000..0edeaa8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_reasoning.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_reasoning_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_reasoning_item.cpython-312.pyc new file mode 100644 index 0000000..22c67f0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_reasoning_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_reasoning_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_reasoning_param.cpython-312.pyc new file mode 100644 index 0000000..8fec591 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_reasoning_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_resume_subagent_call_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_resume_subagent_call_item.cpython-312.pyc new file mode 100644 index 0000000..aa70415 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_resume_subagent_call_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_send_subagent_input_call_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_send_subagent_input_call_item.cpython-312.pyc new file mode 100644 index 0000000..6ba9d5c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_send_subagent_input_call_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session.cpython-312.pyc new file mode 100644 index 0000000..859646e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_assistant_message.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_assistant_message.cpython-312.pyc new file mode 100644 index 0000000..13fa08f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_assistant_message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_created_event.cpython-312.pyc new file mode 100644 index 0000000..829c28b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_deleted.cpython-312.pyc new file mode 100644 index 0000000..6ec6909 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_connected_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_connected_event.cpython-312.pyc new file mode 100644 index 0000000..e9be132 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_connected_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_disconnected_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_disconnected_event.cpython-312.pyc new file mode 100644 index 0000000..a13cfbe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_disconnected_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_failed_event.cpython-312.pyc new file mode 100644 index 0000000..9e34ce0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_pending_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_pending_event.cpython-312.pyc new file mode 100644 index 0000000..6d8b1b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_pending_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_ready_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_ready_event.cpython-312.pyc new file mode 100644 index 0000000..1f96b78 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_ready_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_reset_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_reset_event.cpython-312.pyc new file mode 100644 index 0000000..ac21fbc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_reset_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_state.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_state.cpython-312.pyc new file mode 100644 index 0000000..c5f2bb2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_environment_state.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_error_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_error_event.cpython-312.pyc new file mode 100644 index 0000000..03dd591 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_error_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_event.cpython-312.pyc new file mode 100644 index 0000000..b1895b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_failed_event.cpython-312.pyc new file mode 100644 index 0000000..90eb6a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_idle_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_idle_event.cpython-312.pyc new file mode 100644 index 0000000..dc39a53 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_idle_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..5702df6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_input_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_input_message_param.cpython-312.pyc new file mode 100644 index 0000000..8e93a25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_input_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_input_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_input_param.cpython-312.pyc new file mode 100644 index 0000000..af8573d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_input_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_item.cpython-312.pyc new file mode 100644 index 0000000..fe76804 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_message.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_message.cpython-312.pyc new file mode 100644 index 0000000..9cf7fb2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_message_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_message_content.cpython-312.pyc new file mode 100644 index 0000000..5a0ae06 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_message_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_requires_action_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_requires_action_event.cpython-312.pyc new file mode 100644 index 0000000..55f78c2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_requires_action_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_subagent_active_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_subagent_active_event.cpython-312.pyc new file mode 100644 index 0000000..34f5241 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_subagent_active_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_subagent_closed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_subagent_closed_event.cpython-312.pyc new file mode 100644 index 0000000..3da5d6c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_subagent_closed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_subagent_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_subagent_created_event.cpython-312.pyc new file mode 100644 index 0000000..268569f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_subagent_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_cancelled_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_cancelled_event.cpython-312.pyc new file mode 100644 index 0000000..83a21c3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_cancelled_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_completed_event.cpython-312.pyc new file mode 100644 index 0000000..ef0cf27 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_content_part_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_content_part_added_event.cpython-312.pyc new file mode 100644 index 0000000..4332868 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_content_part_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_content_part_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_content_part_done_event.cpython-312.pyc new file mode 100644 index 0000000..77e1279 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_content_part_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_created_event.cpython-312.pyc new file mode 100644 index 0000000..faa4648 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_failed_event.cpython-312.pyc new file mode 100644 index 0000000..c13b285 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..ac5aedc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_item_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_item_added_event.cpython-312.pyc new file mode 100644 index 0000000..d3cd84d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_item_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_item_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_item_done_event.cpython-312.pyc new file mode 100644 index 0000000..3353225 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_item_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_output_text_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_output_text_delta_event.cpython-312.pyc new file mode 100644 index 0000000..b4c9496 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_output_text_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_output_text_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_output_text_done_event.cpython-312.pyc new file mode 100644 index 0000000..abfbdde Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_output_text_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_part_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_part_added_event.cpython-312.pyc new file mode 100644 index 0000000..c2982cc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_part_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_part_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_part_done_event.cpython-312.pyc new file mode 100644 index 0000000..4c9f614 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_part_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_text_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_text_delta_event.cpython-312.pyc new file mode 100644 index 0000000..e217d07 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_text_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_text_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_text_done_event.cpython-312.pyc new file mode 100644 index 0000000..b4080b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_session_turn_reasoning_summary_text_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_text.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_text.cpython-312.pyc new file mode 100644 index 0000000..5be5d5b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_text_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_text_param.cpython-312.pyc new file mode 100644 index 0000000..d49c07d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_text_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_tool.cpython-312.pyc new file mode 100644 index 0000000..0da678c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_tool_param.cpython-312.pyc new file mode 100644 index 0000000..988cdc0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_update_params.cpython-312.pyc new file mode 100644 index 0000000..1e2b17a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_wait_for_subagents_call_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_wait_for_subagents_call_item.cpython-312.pyc new file mode 100644 index 0000000..93bfec0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_wait_for_subagents_call_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_web_search_call_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_web_search_call_item.cpython-312.pyc new file mode 100644 index 0000000..365d6cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/agent_web_search_call_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant.cpython-312.pyc new file mode 100644 index 0000000..514a2be Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_create_params.cpython-312.pyc new file mode 100644 index 0000000..1865590 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_deleted.cpython-312.pyc new file mode 100644 index 0000000..2c0dbdc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_list_params.cpython-312.pyc new file mode 100644 index 0000000..683ea69 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_response_format_option.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_response_format_option.cpython-312.pyc new file mode 100644 index 0000000..e88ce7b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_response_format_option.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_response_format_option_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_response_format_option_param.cpython-312.pyc new file mode 100644 index 0000000..8d05bf2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_response_format_option_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_stream_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_stream_event.cpython-312.pyc new file mode 100644 index 0000000..f637ce2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_stream_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool.cpython-312.pyc new file mode 100644 index 0000000..3a06e34 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice.cpython-312.pyc new file mode 100644 index 0000000..54d4a23 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function.cpython-312.pyc new file mode 100644 index 0000000..25b68eb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function_param.cpython-312.pyc new file mode 100644 index 0000000..503bc36 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option.cpython-312.pyc new file mode 100644 index 0000000..80a1127 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option_param.cpython-312.pyc new file mode 100644 index 0000000..4ee110c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_param.cpython-312.pyc new file mode 100644 index 0000000..256fdd9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_param.cpython-312.pyc new file mode 100644 index 0000000..05c4cc0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_update_params.cpython-312.pyc new file mode 100644 index 0000000..5dc0b57 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/assistant_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_apply_patch_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_apply_patch_tool.cpython-312.pyc new file mode 100644 index 0000000..3e770b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_apply_patch_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_apply_patch_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_apply_patch_tool_param.cpython-312.pyc new file mode 100644 index 0000000..349c17c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_apply_patch_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_compacted_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_compacted_response.cpython-312.pyc new file mode 100644 index 0000000..ed143cc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_compacted_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_action.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_action.cpython-312.pyc new file mode 100644 index 0000000..8c0124b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_action.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_action_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_action_list.cpython-312.pyc new file mode 100644 index 0000000..3334834 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_action_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_action_list_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_action_list_param.cpython-312.pyc new file mode 100644 index 0000000..91a3e3d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_action_list_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_action_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_action_param.cpython-312.pyc new file mode 100644 index 0000000..596d09b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_action_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_tool.cpython-312.pyc new file mode 100644 index 0000000..2f8eb82 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_tool_param.cpython-312.pyc new file mode 100644 index 0000000..09a653a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_use_preview_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_use_preview_tool.cpython-312.pyc new file mode 100644 index 0000000..15a0efd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_use_preview_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_use_preview_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_use_preview_tool_param.cpython-312.pyc new file mode 100644 index 0000000..3d28d1e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_computer_use_preview_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_auto.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_auto.cpython-312.pyc new file mode 100644 index 0000000..ef8c08c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_auto.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_auto_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_auto_param.cpython-312.pyc new file mode 100644 index 0000000..66243ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_auto_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_allowlist.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_allowlist.cpython-312.pyc new file mode 100644 index 0000000..1550b02 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_allowlist.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_allowlist_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_allowlist_param.cpython-312.pyc new file mode 100644 index 0000000..fc2f1d2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_allowlist_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_disabled.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_disabled.cpython-312.pyc new file mode 100644 index 0000000..c115e75 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_disabled.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_disabled_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_disabled_param.cpython-312.pyc new file mode 100644 index 0000000..cec94cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_disabled_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_domain_secret.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_domain_secret.cpython-312.pyc new file mode 100644 index 0000000..42153b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_domain_secret.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_domain_secret_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_domain_secret_param.cpython-312.pyc new file mode 100644 index 0000000..caa468e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_network_policy_domain_secret_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_reference.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_reference.cpython-312.pyc new file mode 100644 index 0000000..59467d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_reference.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_reference_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_reference_param.cpython-312.pyc new file mode 100644 index 0000000..23dc508 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_container_reference_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_custom_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_custom_tool.cpython-312.pyc new file mode 100644 index 0000000..c63db8f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_custom_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_custom_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_custom_tool_param.cpython-312.pyc new file mode 100644 index 0000000..a52bb25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_custom_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_easy_input_message.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_easy_input_message.cpython-312.pyc new file mode 100644 index 0000000..e047d36 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_easy_input_message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_easy_input_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_easy_input_message_param.cpython-312.pyc new file mode 100644 index 0000000..bb1f3ca Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_easy_input_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_file_search_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_file_search_tool.cpython-312.pyc new file mode 100644 index 0000000..29254b9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_file_search_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_file_search_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_file_search_tool_param.cpython-312.pyc new file mode 100644 index 0000000..1b8983f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_file_search_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_function_shell_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_function_shell_tool.cpython-312.pyc new file mode 100644 index 0000000..308574f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_function_shell_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_function_shell_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_function_shell_tool_param.cpython-312.pyc new file mode 100644 index 0000000..1f1d84f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_function_shell_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_function_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_function_tool.cpython-312.pyc new file mode 100644 index 0000000..435ea7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_function_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_function_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_function_tool_param.cpython-312.pyc new file mode 100644 index 0000000..2e22bba Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_function_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_image_detail.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_image_detail.cpython-312.pyc new file mode 100644 index 0000000..7d81771 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_image_detail.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_inline_skill.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_inline_skill.cpython-312.pyc new file mode 100644 index 0000000..2131dd6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_inline_skill.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_inline_skill_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_inline_skill_param.cpython-312.pyc new file mode 100644 index 0000000..4b45e3b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_inline_skill_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_inline_skill_source.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_inline_skill_source.cpython-312.pyc new file mode 100644 index 0000000..c349f75 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_inline_skill_source.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_inline_skill_source_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_inline_skill_source_param.cpython-312.pyc new file mode 100644 index 0000000..2759f43 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_inline_skill_source_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_local_environment.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_local_environment.cpython-312.pyc new file mode 100644 index 0000000..5c4dbd9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_local_environment.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_local_environment_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_local_environment_param.cpython-312.pyc new file mode 100644 index 0000000..c313995 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_local_environment_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_local_skill.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_local_skill.cpython-312.pyc new file mode 100644 index 0000000..d9ef7a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_local_skill.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_local_skill_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_local_skill_param.cpython-312.pyc new file mode 100644 index 0000000..1ea4e3f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_local_skill_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_mcp_tool_call_error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_mcp_tool_call_error.cpython-312.pyc new file mode 100644 index 0000000..849ebf1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_mcp_tool_call_error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_mcp_tool_call_error_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_mcp_tool_call_error_param.cpython-312.pyc new file mode 100644 index 0000000..89e0f5b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_mcp_tool_call_error_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_namespace_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_namespace_tool.cpython-312.pyc new file mode 100644 index 0000000..5c6487f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_namespace_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_namespace_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_namespace_tool_param.cpython-312.pyc new file mode 100644 index 0000000..29305f8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_namespace_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response.cpython-312.pyc new file mode 100644 index 0000000..8455298 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_apply_patch_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_apply_patch_tool_call.cpython-312.pyc new file mode 100644 index 0000000..f333a4f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_apply_patch_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_apply_patch_tool_call_output.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_apply_patch_tool_call_output.cpython-312.pyc new file mode 100644 index 0000000..00be24f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_apply_patch_tool_call_output.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_audio_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_audio_delta_event.cpython-312.pyc new file mode 100644 index 0000000..5f969a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_audio_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_audio_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_audio_done_event.cpython-312.pyc new file mode 100644 index 0000000..7565e21 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_audio_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_audio_transcript_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_audio_transcript_delta_event.cpython-312.pyc new file mode 100644 index 0000000..49aa824 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_audio_transcript_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_audio_transcript_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_audio_transcript_done_event.cpython-312.pyc new file mode 100644 index 0000000..569432b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_audio_transcript_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_code_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_code_delta_event.cpython-312.pyc new file mode 100644 index 0000000..1799415 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_code_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_code_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_code_done_event.cpython-312.pyc new file mode 100644 index 0000000..c841b29 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_code_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_completed_event.cpython-312.pyc new file mode 100644 index 0000000..b602603 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..683da58 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_interpreting_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_interpreting_event.cpython-312.pyc new file mode 100644 index 0000000..03c94bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_call_interpreting_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_tool_call.cpython-312.pyc new file mode 100644 index 0000000..efdc897 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..a762502 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_code_interpreter_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_compaction_compacting_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_compaction_compacting_event.cpython-312.pyc new file mode 100644 index 0000000..61ebff3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_compaction_compacting_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_compaction_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_compaction_item.cpython-312.pyc new file mode 100644 index 0000000..6e61d57 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_compaction_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_compaction_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_compaction_item_param.cpython-312.pyc new file mode 100644 index 0000000..c5e341f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_compaction_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_compaction_item_param_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_compaction_item_param_param.cpython-312.pyc new file mode 100644 index 0000000..f8a7bcc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_compaction_item_param_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_completed_event.cpython-312.pyc new file mode 100644 index 0000000..063ab5b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call.cpython-312.pyc new file mode 100644 index 0000000..994e185 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call_output_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call_output_item.cpython-312.pyc new file mode 100644 index 0000000..40afa27 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call_output_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call_output_screenshot.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call_output_screenshot.cpython-312.pyc new file mode 100644 index 0000000..ca33ca2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call_output_screenshot.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call_output_screenshot_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call_output_screenshot_param.cpython-312.pyc new file mode 100644 index 0000000..643d83b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call_output_screenshot_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..d0ae254 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_computer_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_configuration_update_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_configuration_update_item.cpython-312.pyc new file mode 100644 index 0000000..ae2d054 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_configuration_update_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_configuration_update_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_configuration_update_item_param.cpython-312.pyc new file mode 100644 index 0000000..890538d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_configuration_update_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_configuration_update_item_param_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_configuration_update_item_param_param.cpython-312.pyc new file mode 100644 index 0000000..4281e04 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_configuration_update_item_param_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_container_reference.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_container_reference.cpython-312.pyc new file mode 100644 index 0000000..dcffb6e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_container_reference.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_content_part_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_content_part_added_event.cpython-312.pyc new file mode 100644 index 0000000..24cc01f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_content_part_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_content_part_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_content_part_done_event.cpython-312.pyc new file mode 100644 index 0000000..a261d4d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_content_part_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_conversation_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_conversation_param.cpython-312.pyc new file mode 100644 index 0000000..60cd5f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_conversation_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_conversation_param_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_conversation_param_param.cpython-312.pyc new file mode 100644 index 0000000..75cc4ff Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_conversation_param_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_created_event.cpython-312.pyc new file mode 100644 index 0000000..bac421c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call.cpython-312.pyc new file mode 100644 index 0000000..9e80ff5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_input_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_input_delta_event.cpython-312.pyc new file mode 100644 index 0000000..3bdf97c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_input_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_input_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_input_done_event.cpython-312.pyc new file mode 100644 index 0000000..0765ecd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_input_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_item.cpython-312.pyc new file mode 100644 index 0000000..d751dd0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_output.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_output.cpython-312.pyc new file mode 100644 index 0000000..0b19ae2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_output.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_output_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_output_item.cpython-312.pyc new file mode 100644 index 0000000..32dc143 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_output_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_output_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_output_param.cpython-312.pyc new file mode 100644 index 0000000..bc5f4d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_output_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..65fa894 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_custom_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_error.cpython-312.pyc new file mode 100644 index 0000000..676bc8f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_error_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_error_event.cpython-312.pyc new file mode 100644 index 0000000..ceeabbc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_error_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_failed_event.cpython-312.pyc new file mode 100644 index 0000000..97cd28b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_call_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_call_completed_event.cpython-312.pyc new file mode 100644 index 0000000..7f1a70b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_call_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_call_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_call_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..d08ae9e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_call_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_call_searching_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_call_searching_event.cpython-312.pyc new file mode 100644 index 0000000..c125eac Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_call_searching_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_tool_call.cpython-312.pyc new file mode 100644 index 0000000..b763ade Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..2865052 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_file_search_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_format_text_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_format_text_config.cpython-312.pyc new file mode 100644 index 0000000..39c6d1d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_format_text_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_format_text_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_format_text_config_param.cpython-312.pyc new file mode 100644 index 0000000..aef9b76 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_format_text_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_format_text_json_schema_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_format_text_json_schema_config.cpython-312.pyc new file mode 100644 index 0000000..202fea9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_format_text_json_schema_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_format_text_json_schema_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_format_text_json_schema_config_param.cpython-312.pyc new file mode 100644 index 0000000..49ee0a4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_format_text_json_schema_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_arguments_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_arguments_delta_event.cpython-312.pyc new file mode 100644 index 0000000..c197d06 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_arguments_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_arguments_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_arguments_done_event.cpython-312.pyc new file mode 100644 index 0000000..4fbff6b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_arguments_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_output_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_output_item.cpython-312.pyc new file mode 100644 index 0000000..b45366c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_output_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_output_item_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_output_item_list.cpython-312.pyc new file mode 100644 index 0000000..aa62fe0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_output_item_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_output_item_list_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_output_item_list_param.cpython-312.pyc new file mode 100644 index 0000000..245eb60 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_output_item_list_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_output_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_output_item_param.cpython-312.pyc new file mode 100644 index 0000000..41b2e55 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_call_output_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_shell_call_output_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_shell_call_output_content.cpython-312.pyc new file mode 100644 index 0000000..39d4d72 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_shell_call_output_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_shell_call_output_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_shell_call_output_content_param.cpython-312.pyc new file mode 100644 index 0000000..d3a4ce9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_shell_call_output_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_shell_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_shell_tool_call.cpython-312.pyc new file mode 100644 index 0000000..c5b720d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_shell_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_shell_tool_call_output.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_shell_tool_call_output.cpython-312.pyc new file mode 100644 index 0000000..860339f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_shell_tool_call_output.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_tool_call.cpython-312.pyc new file mode 100644 index 0000000..4dacbb4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_tool_call_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_tool_call_item.cpython-312.pyc new file mode 100644 index 0000000..c7e340b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_tool_call_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_tool_call_output_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_tool_call_output_item.cpython-312.pyc new file mode 100644 index 0000000..41ee6d2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_tool_call_output_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..e3841f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_web_search.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_web_search.cpython-312.pyc new file mode 100644 index 0000000..4eff4e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_web_search.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_web_search_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_web_search_param.cpython-312.pyc new file mode 100644 index 0000000..8810e4c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_function_web_search_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_image_gen_call_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_image_gen_call_completed_event.cpython-312.pyc new file mode 100644 index 0000000..7ac70bf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_image_gen_call_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_image_gen_call_generating_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_image_gen_call_generating_event.cpython-312.pyc new file mode 100644 index 0000000..cc86e20 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_image_gen_call_generating_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_image_gen_call_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_image_gen_call_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..c1e256d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_image_gen_call_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_image_gen_call_partial_image_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_image_gen_call_partial_image_event.cpython-312.pyc new file mode 100644 index 0000000..38c5656 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_image_gen_call_partial_image_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..b186233 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_includable.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_includable.cpython-312.pyc new file mode 100644 index 0000000..3239f25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_includable.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_incomplete_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_incomplete_event.cpython-312.pyc new file mode 100644 index 0000000..2ba90a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_incomplete_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_inject_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_inject_created_event.cpython-312.pyc new file mode 100644 index 0000000..99ac3a8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_inject_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_inject_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_inject_event.cpython-312.pyc new file mode 100644 index 0000000..789416e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_inject_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_inject_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_inject_event_param.cpython-312.pyc new file mode 100644 index 0000000..950e3a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_inject_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_inject_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_inject_failed_event.cpython-312.pyc new file mode 100644 index 0000000..d4f9cb6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_inject_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input.cpython-312.pyc new file mode 100644 index 0000000..daff3ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_content.cpython-312.pyc new file mode 100644 index 0000000..ce559fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_content_param.cpython-312.pyc new file mode 100644 index 0000000..36f6080 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_file.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_file.cpython-312.pyc new file mode 100644 index 0000000..af4a89b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_file.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_file_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_file_content.cpython-312.pyc new file mode 100644 index 0000000..cf5ec2d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_file_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_file_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_file_content_param.cpython-312.pyc new file mode 100644 index 0000000..805d877 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_file_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_file_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_file_param.cpython-312.pyc new file mode 100644 index 0000000..0fab463 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_file_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_image.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_image.cpython-312.pyc new file mode 100644 index 0000000..9b051ca Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_image.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_image_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_image_content.cpython-312.pyc new file mode 100644 index 0000000..56941e8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_image_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_image_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_image_content_param.cpython-312.pyc new file mode 100644 index 0000000..32cef7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_image_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_image_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_image_param.cpython-312.pyc new file mode 100644 index 0000000..aa3ce0b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_image_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_item.cpython-312.pyc new file mode 100644 index 0000000..1fbe4ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_item_param.cpython-312.pyc new file mode 100644 index 0000000..31c9629 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_message_content_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_message_content_list.cpython-312.pyc new file mode 100644 index 0000000..7e2b40c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_message_content_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_message_content_list_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_message_content_list_param.cpython-312.pyc new file mode 100644 index 0000000..0bb01db Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_message_content_list_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_message_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_message_item.cpython-312.pyc new file mode 100644 index 0000000..e4433d4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_message_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_param.cpython-312.pyc new file mode 100644 index 0000000..48b382c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_text.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_text.cpython-312.pyc new file mode 100644 index 0000000..941bce7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_text_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_text_content.cpython-312.pyc new file mode 100644 index 0000000..01da24e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_text_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_text_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_text_content_param.cpython-312.pyc new file mode 100644 index 0000000..17bf1a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_text_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_text_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_text_param.cpython-312.pyc new file mode 100644 index 0000000..6ec2a10 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_input_text_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_item.cpython-312.pyc new file mode 100644 index 0000000..c3b403a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_local_environment.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_local_environment.cpython-312.pyc new file mode 100644 index 0000000..ca9e2a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_local_environment.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_arguments_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_arguments_delta_event.cpython-312.pyc new file mode 100644 index 0000000..f7014df Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_arguments_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_arguments_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_arguments_done_event.cpython-312.pyc new file mode 100644 index 0000000..be4129c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_arguments_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_completed_event.cpython-312.pyc new file mode 100644 index 0000000..a8e231b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_failed_event.cpython-312.pyc new file mode 100644 index 0000000..826286a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..e0c3401 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_call_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_list_tools_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_list_tools_completed_event.cpython-312.pyc new file mode 100644 index 0000000..9dd31e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_list_tools_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_list_tools_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_list_tools_failed_event.cpython-312.pyc new file mode 100644 index 0000000..8e4464e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_list_tools_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_list_tools_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_list_tools_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..afd47bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_mcp_list_tools_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_item.cpython-312.pyc new file mode 100644 index 0000000..f4fd8db Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_item_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_item_added_event.cpython-312.pyc new file mode 100644 index 0000000..c5217fb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_item_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_item_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_item_done_event.cpython-312.pyc new file mode 100644 index 0000000..ea98407 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_item_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_message.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_message.cpython-312.pyc new file mode 100644 index 0000000..bf0282a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_message_param.cpython-312.pyc new file mode 100644 index 0000000..bfbaaff Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_refusal.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_refusal.cpython-312.pyc new file mode 100644 index 0000000..c0e629c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_refusal.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_refusal_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_refusal_param.cpython-312.pyc new file mode 100644 index 0000000..760b400 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_refusal_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_text.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_text.cpython-312.pyc new file mode 100644 index 0000000..cda91da Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_text_annotation_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_text_annotation_added_event.cpython-312.pyc new file mode 100644 index 0000000..8b836af Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_text_annotation_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_text_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_text_param.cpython-312.pyc new file mode 100644 index 0000000..9f1b6e5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_output_text_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_prompt.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_prompt.cpython-312.pyc new file mode 100644 index 0000000..01884b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_prompt.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_prompt_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_prompt_param.cpython-312.pyc new file mode 100644 index 0000000..ac2ff6f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_prompt_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_queued_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_queued_event.cpython-312.pyc new file mode 100644 index 0000000..102ae28 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_queued_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_item.cpython-312.pyc new file mode 100644 index 0000000..6919e3c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_item_param.cpython-312.pyc new file mode 100644 index 0000000..ee0cd14 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_summary_part_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_summary_part_added_event.cpython-312.pyc new file mode 100644 index 0000000..79650df Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_summary_part_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_summary_part_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_summary_part_done_event.cpython-312.pyc new file mode 100644 index 0000000..bd7e649 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_summary_part_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_summary_text_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_summary_text_delta_event.cpython-312.pyc new file mode 100644 index 0000000..4504334 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_summary_text_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_summary_text_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_summary_text_done_event.cpython-312.pyc new file mode 100644 index 0000000..466bf7c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_summary_text_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_text_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_text_delta_event.cpython-312.pyc new file mode 100644 index 0000000..c5d937e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_text_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_text_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_text_done_event.cpython-312.pyc new file mode 100644 index 0000000..c9bf399 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_reasoning_text_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_refusal_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_refusal_delta_event.cpython-312.pyc new file mode 100644 index 0000000..0c3f075 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_refusal_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_refusal_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_refusal_done_event.cpython-312.pyc new file mode 100644 index 0000000..06c9b23 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_refusal_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_command_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_command_added_event.cpython-312.pyc new file mode 100644 index 0000000..e640bfb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_command_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_command_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_command_delta_event.cpython-312.pyc new file mode 100644 index 0000000..87e924e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_command_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_command_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_command_done_event.cpython-312.pyc new file mode 100644 index 0000000..4db8bee Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_command_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_output_content_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_output_content_delta_event.cpython-312.pyc new file mode 100644 index 0000000..a25daf4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_output_content_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_output_content_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_output_content_done_event.cpython-312.pyc new file mode 100644 index 0000000..63dd834 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_shell_call_output_content_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_status.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_status.cpython-312.pyc new file mode 100644 index 0000000..133cb08 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_status.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_accepted_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_accepted_event.cpython-312.pyc new file mode 100644 index 0000000..d23b215 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_accepted_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_error_code.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_error_code.cpython-312.pyc new file mode 100644 index 0000000..74d9575 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_error_code.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_event.cpython-312.pyc new file mode 100644 index 0000000..7f80399 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_event_param.cpython-312.pyc new file mode 100644 index 0000000..a20a31b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_failed_event.cpython-312.pyc new file mode 100644 index 0000000..70ca6d1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_input.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_input.cpython-312.pyc new file mode 100644 index 0000000..602ba5a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_input.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_input_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_input_content.cpython-312.pyc new file mode 100644 index 0000000..15b1162 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_input_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_input_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_input_content_param.cpython-312.pyc new file mode 100644 index 0000000..02dbfd0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_input_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_input_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_input_param.cpython-312.pyc new file mode 100644 index 0000000..1bde3d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_input_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_pending_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_pending_event.cpython-312.pyc new file mode 100644 index 0000000..4ef7860 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_pending_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_pending_reason.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_pending_reason.cpython-312.pyc new file mode 100644 index 0000000..6a2ddd1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_pending_reason.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_required_input.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_required_input.cpython-312.pyc new file mode 100644 index 0000000..1337b6e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_steer_required_input.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_stream_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_stream_event.cpython-312.pyc new file mode 100644 index 0000000..7b797f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_stream_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_text_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_text_config.cpython-312.pyc new file mode 100644 index 0000000..02c4797 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_text_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_text_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_text_config_param.cpython-312.pyc new file mode 100644 index 0000000..acbb4ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_text_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_text_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_text_delta_event.cpython-312.pyc new file mode 100644 index 0000000..43ee611 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_text_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_text_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_text_done_event.cpython-312.pyc new file mode 100644 index 0000000..973b691 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_text_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_tool_search_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_tool_search_call.cpython-312.pyc new file mode 100644 index 0000000..2eafda3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_tool_search_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_tool_search_output_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_tool_search_output_item.cpython-312.pyc new file mode 100644 index 0000000..098abb6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_tool_search_output_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_tool_search_output_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_tool_search_output_item_param.cpython-312.pyc new file mode 100644 index 0000000..3ed69df Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_tool_search_output_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_tool_search_output_item_param_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_tool_search_output_item_param_param.cpython-312.pyc new file mode 100644 index 0000000..8cef89c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_tool_search_output_item_param_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_usage.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_usage.cpython-312.pyc new file mode 100644 index 0000000..4a16cce Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_usage.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_web_search_call_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_web_search_call_completed_event.cpython-312.pyc new file mode 100644 index 0000000..3b6fcf6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_web_search_call_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_web_search_call_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_web_search_call_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..cb86de6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_web_search_call_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_web_search_call_searching_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_web_search_call_searching_event.cpython-312.pyc new file mode 100644 index 0000000..d1e9c96 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_response_web_search_call_searching_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_responses_client_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_responses_client_event.cpython-312.pyc new file mode 100644 index 0000000..5fe4554 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_responses_client_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_responses_client_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_responses_client_event_param.cpython-312.pyc new file mode 100644 index 0000000..ed6d4dd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_responses_client_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_responses_server_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_responses_server_event.cpython-312.pyc new file mode 100644 index 0000000..bfa5cc4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_responses_server_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_service_tier.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_service_tier.cpython-312.pyc new file mode 100644 index 0000000..648e706 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_service_tier.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_skill_reference.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_skill_reference.cpython-312.pyc new file mode 100644 index 0000000..ea0752f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_skill_reference.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_skill_reference_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_skill_reference_param.cpython-312.pyc new file mode 100644 index 0000000..c848d1d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_skill_reference_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool.cpython-312.pyc new file mode 100644 index 0000000..2e8cb0a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_allowed.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_allowed.cpython-312.pyc new file mode 100644 index 0000000..b2eb6ac Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_allowed.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_allowed_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_allowed_param.cpython-312.pyc new file mode 100644 index 0000000..0d6d466 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_allowed_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_apply_patch.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_apply_patch.cpython-312.pyc new file mode 100644 index 0000000..aef3560 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_apply_patch.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_apply_patch_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_apply_patch_param.cpython-312.pyc new file mode 100644 index 0000000..7d03256 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_apply_patch_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_custom.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_custom.cpython-312.pyc new file mode 100644 index 0000000..0e9c7c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_custom.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_custom_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_custom_param.cpython-312.pyc new file mode 100644 index 0000000..3cd1aa1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_custom_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_function.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_function.cpython-312.pyc new file mode 100644 index 0000000..d79b721 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_function.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_function_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_function_param.cpython-312.pyc new file mode 100644 index 0000000..0d59511 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_function_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_mcp.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_mcp.cpython-312.pyc new file mode 100644 index 0000000..cc9618d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_mcp.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_mcp_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_mcp_param.cpython-312.pyc new file mode 100644 index 0000000..5bd730a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_mcp_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_options.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_options.cpython-312.pyc new file mode 100644 index 0000000..fe8b57e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_options.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_shell.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_shell.cpython-312.pyc new file mode 100644 index 0000000..f60d832 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_shell.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_shell_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_shell_param.cpython-312.pyc new file mode 100644 index 0000000..2560994 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_shell_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_types.cpython-312.pyc new file mode 100644 index 0000000..ad792b8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_types_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_types_param.cpython-312.pyc new file mode 100644 index 0000000..2e37bd1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_choice_types_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_param.cpython-312.pyc new file mode 100644 index 0000000..9d2b1a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_search_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_search_tool.cpython-312.pyc new file mode 100644 index 0000000..0e39134 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_search_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_search_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_search_tool_param.cpython-312.pyc new file mode 100644 index 0000000..cb95b21 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_tool_search_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_web_search_preview_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_web_search_preview_tool.cpython-312.pyc new file mode 100644 index 0000000..9f1db0f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_web_search_preview_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_web_search_preview_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_web_search_preview_tool_param.cpython-312.pyc new file mode 100644 index 0000000..3feee6c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_web_search_preview_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_web_search_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_web_search_tool.cpython-312.pyc new file mode 100644 index 0000000..f010506 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_web_search_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_web_search_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_web_search_tool_param.cpython-312.pyc new file mode 100644 index 0000000..9407352 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/beta_web_search_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/chatkit_workflow.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/chatkit_workflow.cpython-312.pyc new file mode 100644 index 0000000..e53997c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/chatkit_workflow.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/code_interpreter_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/code_interpreter_tool.cpython-312.pyc new file mode 100644 index 0000000..ca62b31 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/code_interpreter_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/code_interpreter_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/code_interpreter_tool_param.cpython-312.pyc new file mode 100644 index 0000000..283d1ac Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/code_interpreter_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/environment.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/environment.cpython-312.pyc new file mode 100644 index 0000000..61ec571 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/environment.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/environment_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/environment_param.cpython-312.pyc new file mode 100644 index 0000000..cd17619 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/environment_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/file_search_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/file_search_tool.cpython-312.pyc new file mode 100644 index 0000000..7b6dbe8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/file_search_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/file_search_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/file_search_tool_param.cpython-312.pyc new file mode 100644 index 0000000..fc9d478 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/file_search_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/function_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/function_tool.cpython-312.pyc new file mode 100644 index 0000000..d159e76 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/function_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/function_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/function_tool_param.cpython-312.pyc new file mode 100644 index 0000000..94daa0a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/function_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_environment_file.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_environment_file.cpython-312.pyc new file mode 100644 index 0000000..cd4f19b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_environment_file.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_environment_file_id.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_environment_file_id.cpython-312.pyc new file mode 100644 index 0000000..8bbc52c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_environment_file_id.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_environment_file_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_environment_file_param.cpython-312.pyc new file mode 100644 index 0000000..780735b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_environment_file_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_plugin.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_plugin.cpython-312.pyc new file mode 100644 index 0000000..c32df26 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_plugin.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_plugin_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_plugin_param.cpython-312.pyc new file mode 100644 index 0000000..bdf35c3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_plugin_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_skill.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_skill.cpython-312.pyc new file mode 100644 index 0000000..9f0320e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_skill.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_skill_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_skill_param.cpython-312.pyc new file mode 100644 index 0000000..21347c6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_skill_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_skill_reference.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_skill_reference.cpython-312.pyc new file mode 100644 index 0000000..fc223bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/hosted_skill_reference.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/inline_capability_source_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/inline_capability_source_param.cpython-312.pyc new file mode 100644 index 0000000..e09322b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/inline_capability_source_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/input_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/input_content.cpython-312.pyc new file mode 100644 index 0000000..63bfd7e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/input_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/input_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/input_content_param.cpython-312.pyc new file mode 100644 index 0000000..bc8b033 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/input_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/mcp_transport.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/mcp_transport.cpython-312.pyc new file mode 100644 index 0000000..7a963de Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/mcp_transport.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/mcp_transport_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/mcp_transport_param.cpython-312.pyc new file mode 100644 index 0000000..3dc5894 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/mcp_transport_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/multi_agent_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/multi_agent_config.cpython-312.pyc new file mode 100644 index 0000000..91c04db Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/multi_agent_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/multi_agent_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/multi_agent_config_param.cpython-312.pyc new file mode 100644 index 0000000..a92dd8e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/multi_agent_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/output_text.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/output_text.cpython-312.pyc new file mode 100644 index 0000000..37b53d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/output_text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/persisted_agent_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/persisted_agent_tool.cpython-312.pyc new file mode 100644 index 0000000..07c7abb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/persisted_agent_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/persisted_agent_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/persisted_agent_tool_param.cpython-312.pyc new file mode 100644 index 0000000..406590c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/persisted_agent_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/persisted_mcp_transport.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/persisted_mcp_transport.cpython-312.pyc new file mode 100644 index 0000000..e14eaff Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/persisted_mcp_transport.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/persisted_mcp_transport_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/persisted_mcp_transport_param.cpython-312.pyc new file mode 100644 index 0000000..20444c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/persisted_mcp_transport_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/response_compact_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/response_compact_params.cpython-312.pyc new file mode 100644 index 0000000..a888ba3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/response_compact_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/response_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/response_create_params.cpython-312.pyc new file mode 100644 index 0000000..3f4609d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/response_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/response_retrieve_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/response_retrieve_params.cpython-312.pyc new file mode 100644 index 0000000..556dffe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/response_retrieve_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/session_error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/session_error.cpython-312.pyc new file mode 100644 index 0000000..28db7f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/session_error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/session_turn_error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/session_turn_error.cpython-312.pyc new file mode 100644 index 0000000..dba5e24 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/session_turn_error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/setup_command_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/setup_command_param.cpython-312.pyc new file mode 100644 index 0000000..fd3cb95 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/setup_command_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/subagent.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/subagent.cpython-312.pyc new file mode 100644 index 0000000..3ae9151 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/subagent.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/summary_text.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/summary_text.cpython-312.pyc new file mode 100644 index 0000000..4176645 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/summary_text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/text_format.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/text_format.cpython-312.pyc new file mode 100644 index 0000000..02e1766 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/text_format.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/text_format_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/text_format_param.cpython-312.pyc new file mode 100644 index 0000000..91c35bb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/text_format_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread.cpython-312.pyc new file mode 100644 index 0000000..c23c36d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread_create_and_run_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread_create_and_run_params.cpython-312.pyc new file mode 100644 index 0000000..ef22bbb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread_create_and_run_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread_create_params.cpython-312.pyc new file mode 100644 index 0000000..3fa56f7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread_deleted.cpython-312.pyc new file mode 100644 index 0000000..02dc3b9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread_update_params.cpython-312.pyc new file mode 100644 index 0000000..0700691 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/thread_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/token_usage.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/token_usage.cpython-312.pyc new file mode 100644 index 0000000..125def4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/token_usage.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/web_search_action.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/web_search_action.cpython-312.pyc new file mode 100644 index 0000000..47e1663 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/__pycache__/web_search_action.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent.py new file mode 100644 index 0000000..2c2f5ad --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_text import AgentText +from .agent_reasoning import AgentReasoning +from .multi_agent_config import MultiAgentConfig +from .persisted_agent_tool import PersistedAgentTool + +__all__ = ["Agent"] + + +class Agent(BaseModel): + """A reusable agent scoped to the caller's project.""" + + id: str + """The ID of the reusable agent.""" + + created_at: int + """The Unix timestamp, in seconds, when the agent was created.""" + + instructions: Optional[str] = None + """Custom instructions appended to the agent's default base instructions.""" + + metadata: Dict[str, str] + """Custom string key-value pairs attached to the agent.""" + + model: str + """The requested model name used for inference.""" + + multi_agent: MultiAgentConfig + """The resolved configuration for creating and coordinating subagents.""" + + name: Optional[str] = None + """A human-readable name for the agent, or null if it is unnamed.""" + + object: Literal["agent"] + """The object type. Always `agent`.""" + + reasoning: AgentReasoning + """ + The resolved reasoning configuration, including the model default for an omitted + effort. + """ + + service_tier: Literal["auto", "default", "flex", "priority", "fast", "ultrafast"] + """The resolved service-tier policy used for model requests.""" + + text: AgentText + """The resolved configuration for text generated by the agent.""" + + tools: List[PersistedAgentTool] + """Tools available to the agent.""" + + updated_at: int + """The Unix timestamp, in seconds, when the agent was last updated.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_close_subagent_call_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_close_subagent_call_item.py new file mode 100644 index 0000000..ca16133 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_close_subagent_call_item.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_function_call_status import AgentFunctionCallStatus + +__all__ = ["AgentCloseSubagentCallItem"] + + +class AgentCloseSubagentCallItem(BaseModel): + """A request to close a subagent.""" + + id: str + """The ID of the tool call item.""" + + recipient_agent_id: str + """The ID of the agent to close.""" + + sender_agent_id: str + """The ID of the agent requesting the close.""" + + status: AgentFunctionCallStatus + """The status of the tool call.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["close_subagent_call"] + """The item type. Always `close_subagent_call`. + + - `close_subagent_call` - The current public item type. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_command_execution_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_command_execution_item.py new file mode 100644 index 0000000..de182d0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_command_execution_item.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_function_call_status import AgentFunctionCallStatus + +__all__ = ["AgentCommandExecutionItem"] + + +class AgentCommandExecutionItem(BaseModel): + """A command execution produced by the agent.""" + + id: str + """The ID of the command execution item.""" + + command: str + """The command that was executed.""" + + cwd: Optional[str] = None + """The working directory used to execute the command.""" + + duration_ms: Optional[int] = None + """The command duration in milliseconds.""" + + exit_code: Optional[int] = None + """The process exit code, if the command completed.""" + + output: Optional[str] = None + """The command output, if available.""" + + status: AgentFunctionCallStatus + """The status of the command execution.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["command_execution"] + """The item type. Always `command_execution`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_content.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_content.py new file mode 100644 index 0000000..948afe7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_content.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .output_text import OutputText + +__all__ = ["AgentContent", "EncryptedContentResource"] + + +class EncryptedContentResource(BaseModel): + """Encrypted content exchanged between agents.""" + + encrypted_content: str + """The encrypted content payload.""" + + type: Literal["encrypted_content"] + """The content type. Always `encrypted_content`.""" + + +AgentContent: TypeAlias = Annotated[Union[OutputText, EncryptedContentResource], PropertyInfo(discriminator="type")] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_create_params.py new file mode 100644 index 0000000..a2d91fc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_create_params.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from .agent_text_param import AgentTextParam +from .agent_reasoning_param import AgentReasoningParam +from .multi_agent_config_param import MultiAgentConfigParam +from .persisted_agent_tool_param import PersistedAgentToolParam + +__all__ = ["AgentCreateParams"] + + +class AgentCreateParams(TypedDict, total=False): + model: Required[str] + """The model to use for the agent. The requested model name is preserved.""" + + instructions: Optional[str] + """Additional instructions appended to the agent's default base instructions. + + Omit or set to null to add no custom instructions. + """ + + metadata: Optional[Dict[str, str]] + """ + Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. Omission or null defaults to an empty map. + """ + + multi_agent: Optional[MultiAgentConfigParam] + """Configuration for creating and coordinating subagents. + + Subagent tools are disabled by default. + """ + + name: Optional[str] + """A human-readable name for the agent. Omission or null leaves the agent unnamed.""" + + reasoning: Optional[AgentReasoningParam] + """Configuration for model reasoning. Omission uses the model's default effort.""" + + service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast", "ultrafast"]] + """The service tier used for model requests. Defaults to `auto`. + + - `auto` - Selects the service tier automatically. + - `default` - Uses the default service tier. + - `flex` - Uses the flex service tier. + - `priority` - Uses the priority service tier. + - `fast` - Uses the fast service tier. + - `ultrafast` - Uses the ultrafast service tier. + """ + + text: Optional[AgentTextParam] + """Configuration for generated text. + + Defaults to the `text` format and medium verbosity. + """ + + tools: Optional[Iterable[PersistedAgentToolParam]] + """Tools available to the agent. Defaults to an empty list.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_create_subagent_call_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_create_subagent_call_item.py new file mode 100644 index 0000000..9f6097b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_create_subagent_call_item.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_content import AgentContent +from .agent_function_call_status import AgentFunctionCallStatus + +__all__ = ["AgentCreateSubagentCallItem"] + + +class AgentCreateSubagentCallItem(BaseModel): + """A request to spawn a subagent.""" + + id: str + """The ID of the tool call item.""" + + agent_id: str + """The ID of the agent that requested the subagent.""" + + content: List[AgentContent] + """The task given to the spawned agent.""" + + model: Optional[str] = None + """The model requested for the spawned agent.""" + + reasoning_effort: Optional[str] = None + """The reasoning effort requested for the spawned agent.""" + + status: AgentFunctionCallStatus + """The status of the tool call.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["create_subagent_call"] + """The item type. Always `create_subagent_call`. + + - `create_subagent_call` - The current public item type. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_deleted.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_deleted.py new file mode 100644 index 0000000..0b4b03f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_deleted.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AgentDeleted"] + + +class AgentDeleted(BaseModel): + """A deleted reusable agent.""" + + id: str + """The ID of the deleted agent.""" + + deleted: bool + """Whether the agent was deleted. Always `true`.""" + + object: Literal["agent.deleted"] + """The object type. Always `agent.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_function_call_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_function_call_item.py new file mode 100644 index 0000000..9b81386 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_function_call_item.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_function_call_status import AgentFunctionCallStatus + +__all__ = ["AgentFunctionCallItem"] + + +class AgentFunctionCallItem(BaseModel): + """A function call produced by the agent.""" + + id: str + """The ID of the function call item.""" + + arguments: object + """The arguments to pass to the function.""" + + call_id: str + """The ID used to submit the function result.""" + + name: str + """The name of the function to call.""" + + status: AgentFunctionCallStatus + """The status of the function call.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["function_call"] + """The item type. Always `function_call`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_function_call_output.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_function_call_output.py new file mode 100644 index 0000000..c450daa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_function_call_output.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import TypeAlias + +from .input_content import InputContent + +__all__ = ["AgentFunctionCallOutput"] + +AgentFunctionCallOutput: TypeAlias = Union[str, List[InputContent]] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_function_call_output_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_function_call_output_param.py new file mode 100644 index 0000000..57e6156 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_function_call_output_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .input_content_param import InputContentParam + +__all__ = ["AgentFunctionCallOutputParam", "InputContentParamInputText", "InputContentParamInputImage"] + + +class InputContentParamInputText(TypedDict, total=False): + """Text input to the model.""" + + text: Required[str] + """The text sent to the model.""" + + type: Required[Literal["input_text"]] + """The type of the object. Always `input_text`.""" + + +class InputContentParamInputImage(TypedDict, total=False): + """Image input to the model.""" + + image_url: Required[str] + """The URL of the image sent to the model.""" + + type: Required[Literal["input_image"]] + """The type of the object. Always `input_image`.""" + + +AgentFunctionCallOutputParam: TypeAlias = Union[str, Iterable[InputContentParam]] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_function_call_status.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_function_call_status.py new file mode 100644 index 0000000..54fb74c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_function_call_status.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["AgentFunctionCallStatus"] + +AgentFunctionCallStatus: TypeAlias = Literal["in_progress", "completed", "failed", "incomplete"] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_interrupt_subagent_call_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_interrupt_subagent_call_item.py new file mode 100644 index 0000000..e064d0e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_interrupt_subagent_call_item.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_function_call_status import AgentFunctionCallStatus + +__all__ = ["AgentInterruptSubagentCallItem"] + + +class AgentInterruptSubagentCallItem(BaseModel): + """A request to interrupt a subagent's current turn. + + The subagent remains available. + """ + + id: str + """The ID of the tool call item.""" + + recipient_agent_id: str + """The ID of the agent to interrupt.""" + + sender_agent_id: str + """The ID of the agent requesting the interrupt.""" + + status: AgentFunctionCallStatus + """The status of the tool call.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["interrupt_subagent_call"] + """The item type. Always `interrupt_subagent_call`. + + - `interrupt_subagent_call` - The current public item type. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_list_params.py new file mode 100644 index 0000000..a8c1294 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_list_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["AgentListParams"] + + +class AgentListParams(TypedDict, total=False): + after: str + """Return resources after this resource ID in the selected order.""" + + limit: Optional[int] + """The maximum number of resources to return.""" + + order: Literal["asc", "desc"] + """The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_mcp_call_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_mcp_call_item.py new file mode 100644 index 0000000..e2e0ac8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_mcp_call_item.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_function_call_status import AgentFunctionCallStatus + +__all__ = ["AgentMcpCallItem"] + + +class AgentMcpCallItem(BaseModel): + """A call to a tool on an MCP server.""" + + id: str + """The ID of the MCP call item.""" + + arguments: object + """The arguments passed to the MCP tool.""" + + error: object + """The error returned by the MCP tool, if any.""" + + name: str + """The name of the MCP tool.""" + + output: object + """The output returned by the MCP tool, if any.""" + + server_label: str + """The label of the MCP server.""" + + status: AgentFunctionCallStatus + """The status of the MCP tool call.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["mcp_call"] + """The item type. Always `mcp_call`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_output_command_execution_output_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_output_command_execution_output_delta_event.py new file mode 100644 index 0000000..34460d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_output_command_execution_output_delta_event.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AgentOutputCommandExecutionOutputDeltaEvent"] + + +class AgentOutputCommandExecutionOutputDeltaEvent(BaseModel): + """Emitted when command execution produces an output delta.""" + + delta: str + """The output text that was appended.""" + + event_id: str + """The unique ID of the event.""" + + item_id: str + """The ID of the command execution item.""" + + output_index: int + """The index of the item in the turn output.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.output.command_execution_output.delta"] + """The type of the object. Always `agent.output.command_execution_output.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_output_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_output_item.py new file mode 100644 index 0000000..cfa5d6a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_output_item.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .agent_mcp_call_item import AgentMcpCallItem +from .agent_reasoning_item import AgentReasoningItem +from .agent_function_call_item import AgentFunctionCallItem +from .agent_web_search_call_item import AgentWebSearchCallItem +from .agent_command_execution_item import AgentCommandExecutionItem +from .agent_close_subagent_call_item import AgentCloseSubagentCallItem +from .agent_create_subagent_call_item import AgentCreateSubagentCallItem +from .agent_resume_subagent_call_item import AgentResumeSubagentCallItem +from .agent_session_assistant_message import AgentSessionAssistantMessage +from .agent_interrupt_subagent_call_item import AgentInterruptSubagentCallItem +from .agent_wait_for_subagents_call_item import AgentWaitForSubagentsCallItem +from .agent_send_subagent_input_call_item import AgentSendSubagentInputCallItem + +__all__ = ["AgentOutputItem"] + +AgentOutputItem: TypeAlias = Annotated[ + Union[ + AgentSessionAssistantMessage, + AgentReasoningItem, + AgentFunctionCallItem, + AgentMcpCallItem, + AgentWebSearchCallItem, + AgentCommandExecutionItem, + AgentCreateSubagentCallItem, + AgentSendSubagentInputCallItem, + AgentResumeSubagentCallItem, + AgentWaitForSubagentsCallItem, + AgentInterruptSubagentCallItem, + AgentCloseSubagentCallItem, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_output_item_status.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_output_item_status.py new file mode 100644 index 0000000..5c25fc9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_output_item_status.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["AgentOutputItemStatus"] + +AgentOutputItemStatus: TypeAlias = Literal["in_progress", "completed", "incomplete"] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_reasoning.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_reasoning.py new file mode 100644 index 0000000..09b6157 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_reasoning.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AgentReasoning"] + + +class AgentReasoning(BaseModel): + """The reasoning configuration used by an agent.""" + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] = None + """ + The requested reasoning effort, or `null` when the model selects its own + default. + """ + + summary: Optional[Literal["concise", "detailed", "auto"]] = None + """The requested reasoning summary format, or `null` when summaries are disabled. + + - `concise` - Returns a concise reasoning summary when supported. + - `detailed` - Returns a detailed reasoning summary when supported. + - `auto` - Automatically selects the most detailed summary supported by the + model. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_reasoning_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_reasoning_item.py new file mode 100644 index 0000000..5f062ef --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_reasoning_item.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .summary_text import SummaryText +from .agent_output_item_status import AgentOutputItemStatus + +__all__ = ["AgentReasoningItem"] + + +class AgentReasoningItem(BaseModel): + """A reasoning item produced by the agent.""" + + id: str + """The ID of the reasoning item.""" + + status: Optional[AgentOutputItemStatus] = None + """The status of the reasoning item.""" + + summary: List[SummaryText] + """The reasoning summaries produced by the agent.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["reasoning"] + """The item type. Always `reasoning`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_reasoning_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_reasoning_param.py new file mode 100644 index 0000000..3b0e319 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_reasoning_param.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["AgentReasoningParam"] + + +class AgentReasoningParam(TypedDict, total=False): + """Reasoning configuration for the agent.""" + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] + """The amount of reasoning effort the model should use. + + Omission lets the model select it. + """ + + summary: Optional[Literal["concise", "detailed", "auto"]] + """Controls whether the response includes a reasoning summary. + + - `concise` - Returns a concise reasoning summary when supported. + - `detailed` - Returns a detailed reasoning summary when supported. + - `auto` - Automatically selects the most detailed summary supported by the + model. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_resume_subagent_call_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_resume_subagent_call_item.py new file mode 100644 index 0000000..4623064 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_resume_subagent_call_item.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_function_call_status import AgentFunctionCallStatus + +__all__ = ["AgentResumeSubagentCallItem"] + + +class AgentResumeSubagentCallItem(BaseModel): + """A request to resume a subagent.""" + + id: str + """The ID of the tool call item.""" + + recipient_agent_id: str + """The ID of the agent to resume.""" + + sender_agent_id: str + """The ID of the agent requesting the resume.""" + + status: AgentFunctionCallStatus + """The status of the tool call.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["resume_subagent_call"] + """The item type. Always `resume_subagent_call`. + + - `resume_subagent_call` - The current public item type. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_send_subagent_input_call_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_send_subagent_input_call_item.py new file mode 100644 index 0000000..f7ae580 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_send_subagent_input_call_item.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_content import AgentContent +from .agent_function_call_status import AgentFunctionCallStatus + +__all__ = ["AgentSendSubagentInputCallItem"] + + +class AgentSendSubagentInputCallItem(BaseModel): + """A request to send input to another agent.""" + + id: str + """The ID of the tool call item.""" + + content: List[AgentContent] + """The input sent to the receiving agent.""" + + recipient_agent_id: str + """The ID of the agent receiving the input.""" + + sender_agent_id: str + """The ID of the agent sending the input.""" + + status: AgentFunctionCallStatus + """The status of the tool call.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["send_subagent_input_call"] + """The item type. Always `send_subagent_input_call`. + + - `send_subagent_input_call` - The current public item type. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session.py new file mode 100644 index 0000000..3cdce02 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session.py @@ -0,0 +1,143 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .agent_text import AgentText +from .agent_tool import AgentTool +from .environment import Environment +from .token_usage import TokenUsage +from .agent_reasoning import AgentReasoning +from .multi_agent_config import MultiAgentConfig + +__all__ = [ + "AgentSession", + "Agent", + "RequiredAction", + "RequiredActionSessionRequiredActionResourceFunctionCall", + "RequiredActionSessionRequiredActionResourceEnvironmentConnection", +] + + +class Agent(BaseModel): + """The agent running in the session.""" + + id: str + """The ID of the agent.""" + + instructions: Optional[str] = None + """Custom instructions appended to the agent's default base instructions.""" + + model: str + """The model used by the agent.""" + + multi_agent: MultiAgentConfig + """Configuration for creating and coordinating subagents.""" + + name: Optional[str] = None + """ + The reusable agent's name when the session was created, or null if no name was + saved. Later changes to the agent's name do not affect this value. + """ + + reasoning: AgentReasoning + """The agent's reasoning configuration.""" + + service_tier: Literal["auto", "default", "flex", "priority", "fast", "ultrafast"] + """The effective service-tier policy for model requests. Defaults to `auto`.""" + + text: AgentText + """Configuration for text generated by the agent.""" + + tools: List[AgentTool] + """Tools available to the agent.""" + + +class RequiredActionSessionRequiredActionResourceFunctionCall(BaseModel): + """Run a function tool and submit its result.""" + + arguments: object + """The arguments supplied by the model.""" + + call_id: str + """The ID to include when submitting the function result.""" + + name: str + """The function name.""" + + turn_id: str + """The ID of the turn that requested the function call.""" + + type: Literal["function_call"] + """The type of the object. Always `function_call`.""" + + +class RequiredActionSessionRequiredActionResourceEnvironmentConnection(BaseModel): + """Reconnect a session environment.""" + + environment_id: str + """The ID of the environment to reconnect.""" + + type: Literal["environment_connection"] + """The type of the object. Always `environment_connection`.""" + + +RequiredAction: TypeAlias = Annotated[ + Union[ + RequiredActionSessionRequiredActionResourceFunctionCall, + RequiredActionSessionRequiredActionResourceEnvironmentConnection, + ], + PropertyInfo(discriminator="type"), +] + + +class AgentSession(BaseModel): + """A Managed Agents session.""" + + id: str + """The ID of the session.""" + + agent: Agent + """The agent running in the session.""" + + created_at: int + """The Unix timestamp, in seconds, when the session was created.""" + + environment: Environment + """The execution environment for the session.""" + + error: Optional[str] = None + """The error that caused the session to fail, if any.""" + + last_active_at: int + """The Unix timestamp, in seconds, when the session was last active.""" + + metadata: Dict[str, str] + """Custom string key-value pairs attached to the session.""" + + object: Literal["agent.session"] + """The object type. Always `agent.session`.""" + + required_actions: List[RequiredAction] + """Actions that must be completed before the session can continue.""" + + status: Literal["idle", "in_progress", "requires_action", "failed"] + """The current status of the session. + + - `idle` - The session has no turn in progress and is ready for input. A hosted + environment may still be provisioning. + - `in_progress` - The session is processing a turn. + - `requires_action` - The session is waiting for one or more required actions. + - `failed` - The session failed. + """ + + usage: Optional[TokenUsage] = None + """Best-effort token usage for the session, or null if unknown. + + Recorded usage may change. + """ + + vault_ids: List[str] + """The IDs of vaults made available to the session.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_assistant_message.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_assistant_message.py new file mode 100644 index 0000000..c817c6a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_assistant_message.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .output_text import OutputText +from .agent_output_item_status import AgentOutputItemStatus + +__all__ = ["AgentSessionAssistantMessage"] + + +class AgentSessionAssistantMessage(BaseModel): + """An assistant message produced by the agent.""" + + id: str + """The ID of the message.""" + + content: List[OutputText] + """The content of the message.""" + + phase: Optional[Literal["commentary", "final_answer"]] = None + """The phase of the assistant message. + + - `commentary` - Commentary produced while the agent works. + - `final_answer` - The agent's final answer. + """ + + role: Literal["assistant"] + """The role of the message author. Always `assistant`.""" + + status: AgentOutputItemStatus + """The status of the message.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["message"] + """The item type. Always `message`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_created_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_created_event.py new file mode 100644 index 0000000..9d1cd62 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_created_event.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_session import AgentSession + +__all__ = ["AgentSessionCreatedEvent"] + + +class AgentSessionCreatedEvent(BaseModel): + """Emitted when a session is created.""" + + event_id: str + """The unique ID of the event.""" + + session: AgentSession + """The session that was created.""" + + type: Literal["agent.session.created"] + """The type of the object. Always `agent.session.created`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_deleted.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_deleted.py new file mode 100644 index 0000000..a0ad687 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_deleted.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AgentSessionDeleted"] + + +class AgentSessionDeleted(BaseModel): + """A Managed Agents session removed from the public API. + + Physical cleanup may continue asynchronously. + """ + + id: str + """The ID of the deleted session.""" + + deleted: bool + """Whether the session has been removed from the public API. + + Always `true`. Physical cleanup may still be in progress. + """ + + object: Literal["agent.session.deleted"] + """The object type. Always `agent.session.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_connected_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_connected_event.py new file mode 100644 index 0000000..4257f16 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_connected_event.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_session_environment_state import AgentSessionEnvironmentState + +__all__ = ["AgentSessionEnvironmentConnectedEvent"] + + +class AgentSessionEnvironmentConnectedEvent(BaseModel): + """Emitted when a session environment connects.""" + + environment: AgentSessionEnvironmentState + """The current environment state.""" + + event_id: str + """The unique ID of the event.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.environment.connected"] + """The type of the object. Always `agent.session.environment.connected`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_disconnected_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_disconnected_event.py new file mode 100644 index 0000000..1921e8d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_disconnected_event.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_session_environment_state import AgentSessionEnvironmentState + +__all__ = ["AgentSessionEnvironmentDisconnectedEvent"] + + +class AgentSessionEnvironmentDisconnectedEvent(BaseModel): + """Emitted when a session environment disconnects.""" + + environment: AgentSessionEnvironmentState + """The current environment state.""" + + event_id: str + """The unique ID of the event.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.environment.disconnected"] + """The type of the object. Always `agent.session.environment.disconnected`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_failed_event.py new file mode 100644 index 0000000..d306e36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_failed_event.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_session_environment_state import AgentSessionEnvironmentState + +__all__ = ["AgentSessionEnvironmentFailedEvent"] + + +class AgentSessionEnvironmentFailedEvent(BaseModel): + """Emitted when a session environment fails.""" + + environment: AgentSessionEnvironmentState + """The current environment state.""" + + event_id: str + """The unique ID of the event.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.environment.failed"] + """The type of the object. Always `agent.session.environment.failed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_pending_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_pending_event.py new file mode 100644 index 0000000..d229010 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_pending_event.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_session_environment_state import AgentSessionEnvironmentState + +__all__ = ["AgentSessionEnvironmentPendingEvent"] + + +class AgentSessionEnvironmentPendingEvent(BaseModel): + """Emitted while a session environment is being prepared.""" + + environment: AgentSessionEnvironmentState + """The current environment state.""" + + event_id: str + """The unique ID of the event.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.environment.pending"] + """The type of the object. Always `agent.session.environment.pending`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_ready_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_ready_event.py new file mode 100644 index 0000000..2b40432 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_ready_event.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_session_environment_state import AgentSessionEnvironmentState + +__all__ = ["AgentSessionEnvironmentReadyEvent"] + + +class AgentSessionEnvironmentReadyEvent(BaseModel): + """Emitted when a hosted session environment is ready to connect.""" + + environment: AgentSessionEnvironmentState + """The current environment state.""" + + event_id: str + """The unique ID of the event.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.environment.ready"] + """The type of the object. Always `agent.session.environment.ready`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_reset_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_reset_event.py new file mode 100644 index 0000000..aacfd50 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_reset_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AgentSessionEnvironmentResetEvent"] + + +class AgentSessionEnvironmentResetEvent(BaseModel): + """Emitted after a hosted sandbox is replaced. + + Conversation history survives; changes to the previous sandbox's files and processes do not. + """ + + environment_id: str + """The stable environment ID, retained across sandbox replacements.""" + + event_id: str + """The unique ID of the event.""" + + reset_count: int + """Monotonically increasing reset number. + + Repeated notifications share this number. + """ + + session_id: str + """The ID of the session associated with the event.""" + + turn_id: Optional[str] = None + """The associated turn, when applicable.""" + + type: Literal["agent.session.environment.reset"] + """The type of the object. Always `agent.session.environment.reset`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_state.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_state.py new file mode 100644 index 0000000..eaa07b4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_environment_state.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AgentSessionEnvironmentState", "Error"] + + +class Error(BaseModel): + """The error reported while preparing the environment, if any.""" + + code: str + """A machine-readable error code.""" + + message: str + """A human-readable error message.""" + + type: str + """The error type.""" + + +class AgentSessionEnvironmentState(BaseModel): + """The current state of a session environment.""" + + id: str + """The public ID of the environment.""" + + error: Optional[Error] = None + """The error reported while preparing the environment, if any.""" + + status: Literal["pending", "ready", "connected", "disconnected", "failed"] + """The environment's connection status. + + - `pending` - The environment is being prepared. + - `ready` - The environment is ready to connect. + - `connected` - The environment is connected. + - `disconnected` - The environment is disconnected. + - `failed` - The environment failed to connect. + """ + + type: str + """The environment type.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_error_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_error_event.py new file mode 100644 index 0000000..0f72f96 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_error_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .session_error import SessionError + +__all__ = ["AgentSessionErrorEvent"] + + +class AgentSessionErrorEvent(BaseModel): + """Emitted when a turn or session fails.""" + + error: SessionError + """The error that occurred.""" + + event_id: str + """The unique ID of the event.""" + + session_id: str + """The ID of the session associated with the event.""" + + type: Literal["error"] + """The type of the object. Always `error`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_event.py new file mode 100644 index 0000000..ce0e2de --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_event.py @@ -0,0 +1,76 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .agent_session_idle_event import AgentSessionIdleEvent +from .agent_session_error_event import AgentSessionErrorEvent +from .agent_session_failed_event import AgentSessionFailedEvent +from .agent_session_created_event import AgentSessionCreatedEvent +from .agent_session_in_progress_event import AgentSessionInProgressEvent +from .agent_session_turn_failed_event import AgentSessionTurnFailedEvent +from .agent_session_turn_created_event import AgentSessionTurnCreatedEvent +from .agent_session_turn_cancelled_event import AgentSessionTurnCancelledEvent +from .agent_session_turn_completed_event import AgentSessionTurnCompletedEvent +from .agent_session_turn_item_done_event import AgentSessionTurnItemDoneEvent +from .agent_session_requires_action_event import AgentSessionRequiresActionEvent +from .agent_session_subagent_active_event import AgentSessionSubagentActiveEvent +from .agent_session_subagent_closed_event import AgentSessionSubagentClosedEvent +from .agent_session_turn_item_added_event import AgentSessionTurnItemAddedEvent +from .agent_session_subagent_created_event import AgentSessionSubagentCreatedEvent +from .agent_session_turn_in_progress_event import AgentSessionTurnInProgressEvent +from .agent_session_environment_ready_event import AgentSessionEnvironmentReadyEvent +from .agent_session_environment_reset_event import AgentSessionEnvironmentResetEvent +from .agent_session_environment_failed_event import AgentSessionEnvironmentFailedEvent +from .agent_session_environment_pending_event import AgentSessionEnvironmentPendingEvent +from .agent_session_environment_connected_event import AgentSessionEnvironmentConnectedEvent +from .agent_session_turn_output_text_done_event import AgentSessionTurnOutputTextDoneEvent +from .agent_session_turn_content_part_done_event import AgentSessionTurnContentPartDoneEvent +from .agent_session_turn_output_text_delta_event import AgentSessionTurnOutputTextDeltaEvent +from .agent_session_turn_content_part_added_event import AgentSessionTurnContentPartAddedEvent +from .agent_session_environment_disconnected_event import AgentSessionEnvironmentDisconnectedEvent +from .agent_output_command_execution_output_delta_event import AgentOutputCommandExecutionOutputDeltaEvent +from .agent_session_turn_reasoning_summary_part_done_event import AgentSessionTurnReasoningSummaryPartDoneEvent +from .agent_session_turn_reasoning_summary_text_done_event import AgentSessionTurnReasoningSummaryTextDoneEvent +from .agent_session_turn_reasoning_summary_part_added_event import AgentSessionTurnReasoningSummaryPartAddedEvent +from .agent_session_turn_reasoning_summary_text_delta_event import AgentSessionTurnReasoningSummaryTextDeltaEvent + +__all__ = ["AgentSessionEvent"] + +AgentSessionEvent: TypeAlias = Annotated[ + Union[ + AgentSessionErrorEvent, + AgentSessionEnvironmentReadyEvent, + AgentSessionEnvironmentResetEvent, + AgentOutputCommandExecutionOutputDeltaEvent, + AgentSessionCreatedEvent, + AgentSessionTurnCreatedEvent, + AgentSessionTurnInProgressEvent, + AgentSessionTurnCompletedEvent, + AgentSessionTurnFailedEvent, + AgentSessionTurnCancelledEvent, + AgentSessionTurnItemAddedEvent, + AgentSessionIdleEvent, + AgentSessionInProgressEvent, + AgentSessionRequiresActionEvent, + AgentSessionFailedEvent, + AgentSessionEnvironmentPendingEvent, + AgentSessionEnvironmentConnectedEvent, + AgentSessionEnvironmentDisconnectedEvent, + AgentSessionEnvironmentFailedEvent, + AgentSessionSubagentCreatedEvent, + AgentSessionSubagentActiveEvent, + AgentSessionSubagentClosedEvent, + AgentSessionTurnItemDoneEvent, + AgentSessionTurnContentPartAddedEvent, + AgentSessionTurnContentPartDoneEvent, + AgentSessionTurnOutputTextDeltaEvent, + AgentSessionTurnOutputTextDoneEvent, + AgentSessionTurnReasoningSummaryPartAddedEvent, + AgentSessionTurnReasoningSummaryPartDoneEvent, + AgentSessionTurnReasoningSummaryTextDeltaEvent, + AgentSessionTurnReasoningSummaryTextDoneEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_failed_event.py new file mode 100644 index 0000000..61e46b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_failed_event.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_session import AgentSession + +__all__ = ["AgentSessionFailedEvent"] + + +class AgentSessionFailedEvent(BaseModel): + """Emitted when a session fails.""" + + event_id: str + """The unique ID of the event.""" + + session: AgentSession + """The failed session.""" + + type: Literal["agent.session.failed"] + """The type of the object. Always `agent.session.failed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_idle_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_idle_event.py new file mode 100644 index 0000000..dba78f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_idle_event.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_session import AgentSession + +__all__ = ["AgentSessionIdleEvent"] + + +class AgentSessionIdleEvent(BaseModel): + """Emitted when a session becomes idle.""" + + event_id: str + """The unique ID of the event.""" + + session: AgentSession + """The session that became idle.""" + + type: Literal["agent.session.idle"] + """The type of the object. Always `agent.session.idle`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_in_progress_event.py new file mode 100644 index 0000000..ac2c49c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_in_progress_event.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_session import AgentSession + +__all__ = ["AgentSessionInProgressEvent"] + + +class AgentSessionInProgressEvent(BaseModel): + """Emitted when a session starts processing a turn.""" + + event_id: str + """The unique ID of the event.""" + + session: AgentSession + """The session that started processing.""" + + type: Literal["agent.session.in_progress"] + """The type of the object. Always `agent.session.in_progress`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_input_message_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_input_message_param.py new file mode 100644 index 0000000..f8b1cf1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_input_message_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +from .input_content_param import InputContentParam + +__all__ = ["AgentSessionInputMessageParam"] + + +class AgentSessionInputMessageParam(TypedDict, total=False): + """A user message submitted to a session.""" + + content: Required[Iterable[InputContentParam]] + """The content of the message.""" + + role: Required[Literal["user"]] + """The role of the message author. Always `user`.""" + + type: Literal["message"] + """The type of the input item. Always `message`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_input_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_input_param.py new file mode 100644 index 0000000..b7a49bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_input_param.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .agent_function_call_output_param import AgentFunctionCallOutputParam +from .agent_session_input_message_param import AgentSessionInputMessageParam + +__all__ = [ + "AgentSessionInputParam", + "SessionInputParamAgentSessionInputMessage", + "SessionInputParamAgentSessionInputCancel", + "SessionInputParamAgentSessionInputToolResult", +] + + +class SessionInputParamAgentSessionInputMessage(TypedDict, total=False): + """Adds one or more user messages and starts a turn.""" + + input: Required[Iterable[AgentSessionInputMessageParam]] + """The user messages to add to the session.""" + + type: Required[Literal["agent.session.input.message"]] + """The type of the object. Always `agent.session.input.message`.""" + + +class SessionInputParamAgentSessionInputCancel(TypedDict, total=False): + """Cancels the session's active turn.""" + + type: Required[Literal["agent.session.input.cancel"]] + """The type of the object. Always `agent.session.input.cancel`.""" + + +class SessionInputParamAgentSessionInputToolResult(TypedDict, total=False): + """Submits the result of a function call.""" + + call_id: Required[str] + """The ID of the function call.""" + + success: Required[bool] + """Whether the function call succeeded.""" + + turn_id: Required[str] + """The ID of the turn that requested the function call.""" + + type: Required[Literal["agent.session.input.tool_result"]] + """The type of the object. Always `agent.session.input.tool_result`.""" + + error: Optional[str] + """The error message when the call failed.""" + + output: Optional[AgentFunctionCallOutputParam] + """The function result when the call succeeded.""" + + +AgentSessionInputParam: TypeAlias = Union[ + SessionInputParamAgentSessionInputMessage, + SessionInputParamAgentSessionInputCancel, + SessionInputParamAgentSessionInputToolResult, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_item.py new file mode 100644 index 0000000..75e7df8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_item.py @@ -0,0 +1,92 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .agent_content import AgentContent +from .agent_mcp_call_item import AgentMcpCallItem +from .agent_reasoning_item import AgentReasoningItem +from .agent_session_message import AgentSessionMessage +from .agent_function_call_item import AgentFunctionCallItem +from .agent_function_call_output import AgentFunctionCallOutput +from .agent_function_call_status import AgentFunctionCallStatus +from .agent_web_search_call_item import AgentWebSearchCallItem +from .agent_command_execution_item import AgentCommandExecutionItem +from .agent_close_subagent_call_item import AgentCloseSubagentCallItem +from .agent_create_subagent_call_item import AgentCreateSubagentCallItem +from .agent_resume_subagent_call_item import AgentResumeSubagentCallItem +from .agent_interrupt_subagent_call_item import AgentInterruptSubagentCallItem +from .agent_wait_for_subagents_call_item import AgentWaitForSubagentsCallItem +from .agent_send_subagent_input_call_item import AgentSendSubagentInputCallItem + +__all__ = ["AgentSessionItem", "FunctionCallOutputItemResource", "AgentMessageItemResource"] + + +class FunctionCallOutputItemResource(BaseModel): + """The result supplied for a function call.""" + + id: str + """The ID of the function call output item.""" + + call_id: str + """The ID of the function call that produced this output.""" + + error: Optional[str] = None + """The error message, if the call failed.""" + + output: Optional[AgentFunctionCallOutput] = None + """The function result, if the call succeeded.""" + + status: AgentFunctionCallStatus + """The status of the function call.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["function_call_output"] + """The item type. Always `function_call_output`.""" + + +class AgentMessageItemResource(BaseModel): + """A message exchanged between agent threads.""" + + id: str + """The ID of the message.""" + + content: List[AgentContent] + """The content exchanged between the agents.""" + + recipient_agent_id: str + """The ID or name of the receiving agent.""" + + sender_agent_id: str + """The ID or name of the sending agent.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["agent_message"] + """The item type. Always `agent_message`.""" + + +AgentSessionItem: TypeAlias = Annotated[ + Union[ + AgentSessionMessage, + AgentReasoningItem, + AgentFunctionCallItem, + FunctionCallOutputItemResource, + AgentMessageItemResource, + AgentMcpCallItem, + AgentWebSearchCallItem, + AgentCommandExecutionItem, + AgentCreateSubagentCallItem, + AgentSendSubagentInputCallItem, + AgentResumeSubagentCallItem, + AgentWaitForSubagentsCallItem, + AgentInterruptSubagentCallItem, + AgentCloseSubagentCallItem, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_message.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_message.py new file mode 100644 index 0000000..b0e0a95 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_message.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_output_item_status import AgentOutputItemStatus +from .agent_session_message_content import AgentSessionMessageContent + +__all__ = ["AgentSessionMessage"] + + +class AgentSessionMessage(BaseModel): + """A user or assistant message recorded in a session.""" + + id: Optional[str] = None + """ + The ID of this item, or null for legacy user messages whose ID was not recorded. + """ + + content: List[AgentSessionMessageContent] + """The content of the message. + + User messages contain input text or images; assistant messages contain output + text. + """ + + phase: Optional[Literal["commentary", "final_answer"]] = None + """The phase of an assistant message. Null for user messages. + + - `commentary` - Commentary produced while the agent works. + - `final_answer` - The agent's final answer. + """ + + role: Literal["user", "assistant"] + """The role of the message author.""" + + status: AgentOutputItemStatus + """The status of the message. User messages are always `completed`.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["message"] + """The item type. Always `message`.""" + + @property + def output_text(self) -> str: + """Join all `output_text` content blocks, returning an empty string if none exist.""" + return "".join(content.text for content in self.content if content.type == "output_text") diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_message_content.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_message_content.py new file mode 100644 index 0000000..48b3763 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_message_content.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "AgentSessionMessageContent", + "MessageContentResourceInputText", + "MessageContentResourceInputImage", + "MessageContentResourceOutputText", +] + + +class MessageContentResourceInputText(BaseModel): + """Text supplied by the user.""" + + text: str + """The text supplied by the user.""" + + type: Literal["input_text"] + """The type of the object. Always `input_text`.""" + + +class MessageContentResourceInputImage(BaseModel): + """An image supplied by the user.""" + + image_url: str + """ + The URL of the image supplied by the user, which may be a base64-encoded data + URL. + """ + + type: Literal["input_image"] + """The type of the object. Always `input_image`.""" + + +class MessageContentResourceOutputText(BaseModel): + """Text produced by the assistant.""" + + text: str + """The text produced by the assistant.""" + + type: Literal["output_text"] + """The type of the object. Always `output_text`.""" + + +AgentSessionMessageContent: TypeAlias = Annotated[ + Union[MessageContentResourceInputText, MessageContentResourceInputImage, MessageContentResourceOutputText], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_requires_action_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_requires_action_event.py new file mode 100644 index 0000000..b80e4d9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_requires_action_event.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_session import AgentSession + +__all__ = ["AgentSessionRequiresActionEvent"] + + +class AgentSessionRequiresActionEvent(BaseModel): + """Emitted when a session is waiting for one or more required actions.""" + + event_id: str + """The unique ID of the event.""" + + session: AgentSession + """The session and its current required actions.""" + + type: Literal["agent.session.requires_action"] + """The type of the object. Always `agent.session.requires_action`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_subagent_active_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_subagent_active_event.py new file mode 100644 index 0000000..09d55d1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_subagent_active_event.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .subagent import Subagent +from ..._models import BaseModel + +__all__ = ["AgentSessionSubagentActiveEvent"] + + +class AgentSessionSubagentActiveEvent(BaseModel): + """Emitted when a closed subagent successfully resumes.""" + + event_id: str + """The unique ID of the event.""" + + subagent: Subagent + """The subagent that resumed.""" + + type: Literal["agent.session.subagent.active"] + """The type of the object. Always `agent.session.subagent.active`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_subagent_closed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_subagent_closed_event.py new file mode 100644 index 0000000..d55594c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_subagent_closed_event.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .subagent import Subagent +from ..._models import BaseModel + +__all__ = ["AgentSessionSubagentClosedEvent"] + + +class AgentSessionSubagentClosedEvent(BaseModel): + """Emitted when a subagent is closed.""" + + event_id: str + """The unique ID of the event.""" + + subagent: Subagent + """The subagent that was closed.""" + + type: Literal["agent.session.subagent.closed"] + """The type of the object. Always `agent.session.subagent.closed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_subagent_created_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_subagent_created_event.py new file mode 100644 index 0000000..50fd724 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_subagent_created_event.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .subagent import Subagent +from ..._models import BaseModel + +__all__ = ["AgentSessionSubagentCreatedEvent"] + + +class AgentSessionSubagentCreatedEvent(BaseModel): + """Emitted when a subagent is created.""" + + event_id: str + """The unique ID of the event.""" + + subagent: Subagent + """The subagent that was created.""" + + type: Literal["agent.session.subagent.created"] + """The type of the object. Always `agent.session.subagent.created`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_cancelled_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_cancelled_event.py new file mode 100644 index 0000000..0b8a341 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_cancelled_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .token_usage import TokenUsage +from .agents.sessions.turn import Turn + +__all__ = ["AgentSessionTurnCancelledEvent"] + + +class AgentSessionTurnCancelledEvent(BaseModel): + """Emitted when a turn is cancelled.""" + + event_id: str + """The unique ID of the event.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn: Turn + """The cancelled turn.""" + + turn_id: str + """The ID of the turn associated with the event.""" + + type: Literal["agent.session.turn.cancelled"] + """The type of the object. Always `agent.session.turn.cancelled`.""" + + usage: Optional[TokenUsage] = None + """Token usage by the root agent during the turn, when available.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_completed_event.py new file mode 100644 index 0000000..1b76925 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_completed_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .token_usage import TokenUsage +from .agents.sessions.turn import Turn + +__all__ = ["AgentSessionTurnCompletedEvent"] + + +class AgentSessionTurnCompletedEvent(BaseModel): + """Emitted when a turn completes.""" + + event_id: str + """The unique ID of the event.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn: Turn + """The completed turn.""" + + turn_id: str + """The ID of the turn associated with the event.""" + + type: Literal["agent.session.turn.completed"] + """The type of the object. Always `agent.session.turn.completed`.""" + + usage: Optional[TokenUsage] = None + """Token usage by the root agent during the turn, when available.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_content_part_added_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_content_part_added_event.py new file mode 100644 index 0000000..7436c49 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_content_part_added_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .output_text import OutputText + +__all__ = ["AgentSessionTurnContentPartAddedEvent"] + + +class AgentSessionTurnContentPartAddedEvent(BaseModel): + """Emitted when an output text content part is added.""" + + content_index: int + """The index of the content part in the message.""" + + event_id: str + """The unique ID of the event.""" + + item_id: str + """The ID of the message item.""" + + output_index: int + """The index of the item in the turn output.""" + + part: OutputText + """The initial content part.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.turn.content_part.added"] + """The type of the object. Always `agent.session.turn.content_part.added`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_content_part_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_content_part_done_event.py new file mode 100644 index 0000000..79e9e95 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_content_part_done_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .output_text import OutputText + +__all__ = ["AgentSessionTurnContentPartDoneEvent"] + + +class AgentSessionTurnContentPartDoneEvent(BaseModel): + """Emitted when an output content part is complete.""" + + content_index: int + """The index of the content part in the message.""" + + event_id: str + """The unique ID of the event.""" + + item_id: str + """The ID of the message item.""" + + output_index: int + """The index of the item in the turn output.""" + + part: OutputText + """The completed content part.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.turn.content_part.done"] + """The type of the object. Always `agent.session.turn.content_part.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_created_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_created_event.py new file mode 100644 index 0000000..f7b515f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_created_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .agents.sessions.turn import Turn + +__all__ = ["AgentSessionTurnCreatedEvent"] + + +class AgentSessionTurnCreatedEvent(BaseModel): + """Emitted when a turn is created.""" + + event_id: str + """The unique ID of the event.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn: Turn + """The turn at the time it was created.""" + + turn_id: str + """The ID of the turn associated with the event.""" + + type: Literal["agent.session.turn.created"] + """The type of the object. Always `agent.session.turn.created`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_failed_event.py new file mode 100644 index 0000000..a6bdd6e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_failed_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .token_usage import TokenUsage +from .agents.sessions.turn import Turn + +__all__ = ["AgentSessionTurnFailedEvent"] + + +class AgentSessionTurnFailedEvent(BaseModel): + """Emitted when a turn fails.""" + + event_id: str + """The unique ID of the event.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn: Turn + """The failed turn.""" + + turn_id: str + """The ID of the turn associated with the event.""" + + type: Literal["agent.session.turn.failed"] + """The type of the object. Always `agent.session.turn.failed`.""" + + usage: Optional[TokenUsage] = None + """Token usage by the root agent during the turn, when available.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_in_progress_event.py new file mode 100644 index 0000000..9d5f3c8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_in_progress_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .agents.sessions.turn import Turn + +__all__ = ["AgentSessionTurnInProgressEvent"] + + +class AgentSessionTurnInProgressEvent(BaseModel): + """Emitted when a turn starts running.""" + + event_id: str + """The unique ID of the event.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn: Turn + """The turn at the time it started running.""" + + turn_id: str + """The ID of the turn associated with the event.""" + + type: Literal["agent.session.turn.in_progress"] + """The type of the object. Always `agent.session.turn.in_progress`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_item_added_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_item_added_event.py new file mode 100644 index 0000000..11ac886 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_item_added_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_session_item import AgentSessionItem + +__all__ = ["AgentSessionTurnItemAddedEvent"] + + +class AgentSessionTurnItemAddedEvent(BaseModel): + """Emitted when an item is added to a turn.""" + + event_id: str + """The unique ID of the event.""" + + item: AgentSessionItem + """The item that was added.""" + + output_index: Optional[int] = None + """The index of the item in the turn output, when the item is agent output.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.turn.item.added"] + """The type of the object. Always `agent.session.turn.item.added`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_item_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_item_done_event.py new file mode 100644 index 0000000..3456991 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_item_done_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_output_item import AgentOutputItem + +__all__ = ["AgentSessionTurnItemDoneEvent"] + + +class AgentSessionTurnItemDoneEvent(BaseModel): + """Emitted when an output item is complete.""" + + event_id: str + """The unique ID of the event.""" + + item: AgentOutputItem + """The completed output item.""" + + output_index: int + """The index of the output item in the turn output.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.turn.item.done"] + """The type of the object. Always `agent.session.turn.item.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_output_text_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_output_text_delta_event.py new file mode 100644 index 0000000..cbdaf40 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_output_text_delta_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AgentSessionTurnOutputTextDeltaEvent"] + + +class AgentSessionTurnOutputTextDeltaEvent(BaseModel): + """Emitted when text is appended to an output text content part.""" + + content_index: int + """The index of the content part in the message.""" + + delta: str + """The text that was appended.""" + + event_id: str + """The unique ID of the event.""" + + item_id: str + """The ID of the message item.""" + + output_index: int + """The index of the item in the turn output.""" + + session_id: str + """The ID of the session associated with the event.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.turn.output_text.delta"] + """The type of the object. Always `agent.session.turn.output_text.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_output_text_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_output_text_done_event.py new file mode 100644 index 0000000..51813a0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_output_text_done_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AgentSessionTurnOutputTextDoneEvent"] + + +class AgentSessionTurnOutputTextDoneEvent(BaseModel): + """Emitted when an output text content part is complete.""" + + content_index: int + """The index of the content part in the message.""" + + event_id: str + """The unique ID of the event.""" + + item_id: str + """The ID of the message item.""" + + output_index: int + """The index of the item in the turn output.""" + + session_id: str + """The ID of the session associated with the event.""" + + text: str + """The complete output text.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.turn.output_text.done"] + """The type of the object. Always `agent.session.turn.output_text.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_reasoning_summary_part_added_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_reasoning_summary_part_added_event.py new file mode 100644 index 0000000..5d1a58f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_reasoning_summary_part_added_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .summary_text import SummaryText + +__all__ = ["AgentSessionTurnReasoningSummaryPartAddedEvent"] + + +class AgentSessionTurnReasoningSummaryPartAddedEvent(BaseModel): + """Emitted when a reasoning summary content part is added.""" + + event_id: str + """The unique ID of the event.""" + + item_id: str + """The ID of the reasoning item.""" + + output_index: int + """The index of the item in the turn output.""" + + part: SummaryText + """The initial summary part.""" + + session_id: str + """The ID of the session associated with the event.""" + + summary_index: int + """The index of the summary content part.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.turn.reasoning_summary_part.added"] + """The type of the object. + + Always `agent.session.turn.reasoning_summary_part.added`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_reasoning_summary_part_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_reasoning_summary_part_done_event.py new file mode 100644 index 0000000..ca64026 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_reasoning_summary_part_done_event.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .summary_text import SummaryText + +__all__ = ["AgentSessionTurnReasoningSummaryPartDoneEvent"] + + +class AgentSessionTurnReasoningSummaryPartDoneEvent(BaseModel): + """Emitted when a reasoning summary part is complete.""" + + event_id: str + """The unique ID of the event.""" + + item_id: str + """The ID of the reasoning item.""" + + output_index: int + """The index of the item in the turn output.""" + + part: SummaryText + """The completed summary part.""" + + session_id: str + """The ID of the session associated with the event.""" + + status: Optional[Literal["incomplete"]] = None + """Present as `incomplete` when summary generation was interrupted.""" + + summary_index: int + """The index of the summary part.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.turn.reasoning_summary_part.done"] + """The type of the object. + + Always `agent.session.turn.reasoning_summary_part.done`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_reasoning_summary_text_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_reasoning_summary_text_delta_event.py new file mode 100644 index 0000000..d92698c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_reasoning_summary_text_delta_event.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AgentSessionTurnReasoningSummaryTextDeltaEvent"] + + +class AgentSessionTurnReasoningSummaryTextDeltaEvent(BaseModel): + """Emitted when text is appended to a reasoning summary.""" + + delta: str + """The summary text that was appended.""" + + event_id: str + """The unique ID of the event.""" + + item_id: str + """The ID of the reasoning item.""" + + output_index: int + """The index of the item in the turn output.""" + + session_id: str + """The ID of the session associated with the event.""" + + summary_index: int + """The index of the summary content part.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.turn.reasoning_summary_text.delta"] + """The type of the object. + + Always `agent.session.turn.reasoning_summary_text.delta`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_reasoning_summary_text_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_reasoning_summary_text_done_event.py new file mode 100644 index 0000000..82c75e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_session_turn_reasoning_summary_text_done_event.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AgentSessionTurnReasoningSummaryTextDoneEvent"] + + +class AgentSessionTurnReasoningSummaryTextDoneEvent(BaseModel): + """Emitted when a reasoning summary content part is complete.""" + + event_id: str + """The unique ID of the event.""" + + item_id: str + """The ID of the reasoning item.""" + + output_index: int + """The index of the item in the turn output.""" + + session_id: str + """The ID of the session associated with the event.""" + + summary_index: int + """The index of the summary content part.""" + + text: str + """The complete reasoning summary text.""" + + turn_id: Optional[str] = None + """The ID of the turn associated with the event, when applicable.""" + + type: Literal["agent.session.turn.reasoning_summary_text.done"] + """The type of the object. + + Always `agent.session.turn.reasoning_summary_text.done`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_text.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_text.py new file mode 100644 index 0000000..5c0758f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_text.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .text_format import TextFormat + +__all__ = ["AgentText"] + + +class AgentText(BaseModel): + """The text configuration used by an agent.""" + + format: TextFormat + """The effective output format. Defaults to ordinary text.""" + + verbosity: Literal["low", "medium", "high"] + """The amount of text produced by the agent. Defaults to `medium`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_text_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_text_param.py new file mode 100644 index 0000000..c52df0e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_text_param.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +from .text_format_param import TextFormatParam + +__all__ = ["AgentTextParam"] + + +class AgentTextParam(TypedDict, total=False): + """Configuration for text generated by the agent.""" + + format: Optional[TextFormatParam] + """The output format. Omission uses ordinary text (`{"type": "text"}`).""" + + verbosity: Optional[Literal["low", "medium", "high"]] + """The amount of text the model should produce. + + Defaults to `medium`, matching Responses. + + - `low` - Produces less text. + - `medium` - Uses the default amount of text. + - `high` - Produces more text. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_tool.py new file mode 100644 index 0000000..023fe89 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_tool.py @@ -0,0 +1,123 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .mcp_transport import McpTransport + +__all__ = [ + "AgentTool", + "AgentToolResourceFunction", + "AgentToolResourceProgrammaticToolCalling", + "AgentToolResourceMcp", + "AgentToolResourceWebSearch", + "AgentToolResourceWebSearchLocation", +] + + +class AgentToolResourceFunction(BaseModel): + """A function defined by the application.""" + + defer_loading: bool + """Whether the function is deferred and discovered through tool search.""" + + description: str + """A description of what the function does.""" + + name: str + """The name of the function.""" + + parameters: Dict[str, object] + """A JSON Schema object describing the function's arguments.""" + + type: Literal["function"] + """The type of the object. Always `function`.""" + + +class AgentToolResourceProgrammaticToolCalling(BaseModel): + """Enables calling tools from model-generated code.""" + + enabled: bool + """Whether tools can be called from model-generated code.""" + + type: Literal["programmatic_tool_calling"] + """The type of the object. Always `programmatic_tool_calling`.""" + + +class AgentToolResourceMcp(BaseModel): + """Tools provided by a remote MCP server.""" + + allowed_tools: Optional[List[str]] = None + """The MCP tools the agent may call.""" + + connection_origin: Literal["service", "environment"] + """Where outbound MCP HTTP connections originate.""" + + credential_id: Optional[str] = None + """The attached vault credential selected for this MCP server, if any. + + Optional when exactly one attached credential matches the server URL. + """ + + request_metadata: Dict[str, object] + """Metadata included with requests to this MCP server.""" + + required: bool + """Whether this MCP server must initialize before the first turn.""" + + server_label: str + """A label used to identify the MCP server in tool calls.""" + + transport: McpTransport + """The transport used to connect to the MCP server.""" + + type: Literal["mcp"] + """The type of the object. Always `mcp`.""" + + +class AgentToolResourceWebSearchLocation(BaseModel): + """Approximate location used to localize search results, if provided.""" + + city: Optional[str] = None + """The city name.""" + + country: Optional[str] = None + """The two-letter ISO country code, such as `US`.""" + + region: Optional[str] = None + """The region or state name.""" + + timezone: Optional[str] = None + """The IANA timezone, such as `America/Los_Angeles`.""" + + +class AgentToolResourceWebSearch(BaseModel): + """Web search.""" + + allowed_domains: Optional[List[str]] = None + """Allowed search domains, or `null` when the search is unrestricted.""" + + context_size: Literal["low", "medium", "high"] + """The amount of search context made available to the model. Defaults to `medium`.""" + + location: Optional[AgentToolResourceWebSearchLocation] = None + """Approximate location used to localize search results, if provided.""" + + mode: Literal["disabled", "cached", "live"] + """The source used for web search results.""" + + type: Literal["web_search"] + """The type of the object. Always `web_search`.""" + + +AgentTool: TypeAlias = Annotated[ + Union[ + AgentToolResourceFunction, + AgentToolResourceProgrammaticToolCalling, + AgentToolResourceMcp, + AgentToolResourceWebSearch, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_tool_param.py new file mode 100644 index 0000000..a9e3da4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_tool_param.py @@ -0,0 +1,148 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .mcp_transport_param import McpTransportParam + +__all__ = [ + "AgentToolParam", + "AgentToolConfigParamFunction", + "AgentToolConfigParamToolSearch", + "AgentToolConfigParamProgrammaticToolCalling", + "AgentToolConfigParamMcp", + "AgentToolConfigParamWebSearch", + "AgentToolConfigParamWebSearchLocation", +] + + +class AgentToolConfigParamFunction(TypedDict, total=False): + """A function defined by the application.""" + + description: Required[str] + """A description of what the function does.""" + + name: Required[str] + """The name of the function.""" + + parameters: Required[Dict[str, object]] + """A JSON Schema object describing the function's arguments.""" + + type: Required[Literal["function"]] + """The type of the object. Always `function`.""" + + defer_loading: bool + """Whether this function is deferred and discovered through tool search. + + Defaults to `false`. + """ + + +class AgentToolConfigParamToolSearch(TypedDict, total=False): + """Discovers deferred function tools and loads them into the model context.""" + + type: Required[Literal["tool_search"]] + """The type of the object. Always `tool_search`.""" + + +class AgentToolConfigParamProgrammaticToolCalling(TypedDict, total=False): + """Enables calling tools from model-generated code.""" + + type: Required[Literal["programmatic_tool_calling"]] + """The type of the object. Always `programmatic_tool_calling`.""" + + enabled: bool + """Whether tools can be called from model-generated code. Defaults to `true`.""" + + +class AgentToolConfigParamMcp(TypedDict, total=False): + """Tools provided by a remote MCP server.""" + + server_label: Required[str] + """A label used to identify the MCP server in tool calls.""" + + transport: Required[McpTransportParam] + """The transport used to connect to the MCP server.""" + + type: Required[Literal["mcp"]] + """The type of the object. Always `mcp`.""" + + allowed_tools: Optional[SequenceNotStr[str]] + """The MCP tools the agent may call. All server tools are allowed when omitted.""" + + connection_origin: Optional[Literal["service", "environment"]] + """Selects where outbound MCP HTTP connections originate. + + Omitted or `service` uses the Managed Agents service network; `environment` uses + the session's selected environment. + + - `service` - Uses the Managed Agents service network. + - `environment` - Uses the session's execution environment. + """ + + credential_id: Optional[str] + """The attached vault credential used to authenticate this MCP server. + + Optional when exactly one attached credential matches the server URL. + """ + + request_metadata: Optional[Dict[str, object]] + """Metadata included with requests to this MCP server.""" + + required: bool + """Whether this MCP server must initialize before the first turn. + + Defaults to `false`. + """ + + +class AgentToolConfigParamWebSearchLocation(TypedDict, total=False): + """Approximate location used to localize search results.""" + + city: Optional[str] + """The city name.""" + + country: Optional[str] + """The two-letter ISO country code, such as `US`.""" + + region: Optional[str] + """The region or state name.""" + + timezone: Optional[str] + """The IANA timezone, such as `America/Los_Angeles`.""" + + +class AgentToolConfigParamWebSearch(TypedDict, total=False): + """Web search.""" + + type: Required[Literal["web_search"]] + """The type of the object. Always `web_search`.""" + + allowed_domains: Optional[SequenceNotStr[str]] + """Domains the search may include.""" + + context_size: Optional[Literal["low", "medium", "high"]] + """The amount of search context made available to the model. Defaults to `medium`.""" + + location: Optional[AgentToolConfigParamWebSearchLocation] + """Approximate location used to localize search results.""" + + mode: Optional[Literal["disabled", "cached", "live"]] + """The source used for web search results. Defaults to `live`. + + - `disabled` - Disables web search. + - `cached` - Uses cached search results. + - `live` - Searches the live web. + """ + + +AgentToolParam: TypeAlias = Union[ + AgentToolConfigParamFunction, + AgentToolConfigParamToolSearch, + AgentToolConfigParamProgrammaticToolCalling, + AgentToolConfigParamMcp, + AgentToolConfigParamWebSearch, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_update_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_update_params.py new file mode 100644 index 0000000..7a888da --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_update_params.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable, Optional +from typing_extensions import Literal, TypedDict + +from .agent_text_param import AgentTextParam +from .agent_reasoning_param import AgentReasoningParam +from .multi_agent_config_param import MultiAgentConfigParam +from .persisted_agent_tool_param import PersistedAgentToolParam + +__all__ = ["AgentUpdateParams"] + + +class AgentUpdateParams(TypedDict, total=False): + instructions: Optional[str] + """Additional instructions appended to the agent's default base instructions. + + Omit to leave unchanged. + """ + + metadata: Optional[Dict[str, str]] + """Replaces all metadata. + + Omit to leave unchanged, or pass null or {} to clear it. Up to 16 string + key-value pairs, with keys up to 64 and values up to 512 characters. + """ + + model: str + """The model to use for the agent. The requested model name is preserved.""" + + multi_agent: Optional[MultiAgentConfigParam] + """Configuration for creating and coordinating subagents.""" + + name: Optional[str] + """A replacement name. Omit to leave unchanged, or pass null to clear it.""" + + reasoning: Optional[AgentReasoningParam] + """Configuration for model reasoning. + + Omit to keep the current settings; pass `null` to reset to the model's default + effort. + """ + + service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast", "ultrafast"]] + """The service tier used for model requests. + + - `auto` - Selects the service tier automatically. + - `default` - Uses the default service tier. + - `flex` - Uses the flex service tier. + - `priority` - Uses the priority service tier. + - `fast` - Uses the fast service tier. + - `ultrafast` - Uses the ultrafast service tier. + """ + + text: Optional[AgentTextParam] + """Configuration for text generated by the agent.""" + + tools: Optional[Iterable[PersistedAgentToolParam]] + """Tools available to the agent.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_wait_for_subagents_call_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_wait_for_subagents_call_item.py new file mode 100644 index 0000000..5dc67de --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_wait_for_subagents_call_item.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_function_call_status import AgentFunctionCallStatus + +__all__ = ["AgentWaitForSubagentsCallItem"] + + +class AgentWaitForSubagentsCallItem(BaseModel): + """A request to wait for one or more subagents.""" + + id: str + """The ID of the tool call item.""" + + recipient_agent_ids: List[str] + """The IDs of the agents to wait for.""" + + sender_agent_id: str + """The ID of the agent waiting for results.""" + + status: AgentFunctionCallStatus + """The status of the tool call.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["wait_for_subagents_call"] + """The item type. Always `wait_for_subagents_call`. + + - `wait_for_subagents_call` - The current public item type. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agent_web_search_call_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/agent_web_search_call_item.py new file mode 100644 index 0000000..48a18e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agent_web_search_call_item.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .web_search_action import WebSearchAction +from .agent_output_item_status import AgentOutputItemStatus + +__all__ = ["AgentWebSearchCallItem"] + + +class AgentWebSearchCallItem(BaseModel): + """A web search call produced by the agent.""" + + id: str + """The ID of the web search call.""" + + action: Optional[WebSearchAction] = None + """The action performed by the web search tool.""" + + status: AgentOutputItemStatus + """The status of the web search call.""" + + turn_id: str + """The ID of the turn that contains this item.""" + + type: Literal["web_search_call"] + """The item type. Always `web_search_call`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/__init__.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__init__.py new file mode 100644 index 0000000..376bc76 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__init__.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .vault import Vault as Vault +from .vault_status import VaultStatus as VaultStatus +from .vault_deleted import VaultDeleted as VaultDeleted +from .environment_info import EnvironmentInfo as EnvironmentInfo +from .vault_list_params import VaultListParams as VaultListParams +from .session_list_params import SessionListParams as SessionListParams +from .vault_create_params import VaultCreateParams as VaultCreateParams +from .session_create_params import SessionCreateParams as SessionCreateParams +from .session_update_params import SessionUpdateParams as SessionUpdateParams +from .vault_status_filter_param import VaultStatusFilterParam as VaultStatusFilterParam diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d269fe1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/environment_info.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/environment_info.cpython-312.pyc new file mode 100644 index 0000000..42a8ba1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/environment_info.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/session_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/session_create_params.cpython-312.pyc new file mode 100644 index 0000000..f59037d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/session_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/session_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/session_list_params.cpython-312.pyc new file mode 100644 index 0000000..673905f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/session_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/session_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/session_update_params.cpython-312.pyc new file mode 100644 index 0000000..f422d2d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/session_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault.cpython-312.pyc new file mode 100644 index 0000000..d2279ba Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_create_params.cpython-312.pyc new file mode 100644 index 0000000..9dab567 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_deleted.cpython-312.pyc new file mode 100644 index 0000000..1591470 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_list_params.cpython-312.pyc new file mode 100644 index 0000000..e551472 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_status.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_status.cpython-312.pyc new file mode 100644 index 0000000..2a6c7df Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_status.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_status_filter_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_status_filter_param.cpython-312.pyc new file mode 100644 index 0000000..99fb1ac Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/__pycache__/vault_status_filter_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environment_info.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environment_info.py new file mode 100644 index 0000000..0362c5c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environment_info.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ...._models import BaseModel +from ..hosted_skill import HostedSkill +from ..hosted_plugin import HostedPlugin +from ..hosted_environment_file import HostedEnvironmentFile + +__all__ = ["EnvironmentInfo"] + + +class EnvironmentInfo(BaseModel): + """Safe metadata for a first-class execution environment.""" + + id: str + """The ID of the environment.""" + + files: List[HostedEnvironmentFile] + """Files installed in the environment, without their contents.""" + + object: Literal["agent.environment"] + """The object type. Always `agent.environment`.""" + + plugins: List[HostedPlugin] + """Plugins installed in the environment, without their archive contents.""" + + skills: List[HostedSkill] + """Skills installed in the environment, without their archive contents.""" + + status: Literal["pending", "connected", "disconnected", "expired", "failed"] + """The current environment connection status.""" + + type: Literal["openai_hosted", "self_hosted"] + """Whether the environment is hosted by OpenAI or by the application.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__init__.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__init__.py new file mode 100644 index 0000000..f10e693 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__init__.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .environment_file import EnvironmentFile as EnvironmentFile +from .file_list_params import FileListParams as FileListParams +from .file_create_params import FileCreateParams as FileCreateParams +from .environment_template import EnvironmentTemplate as EnvironmentTemplate +from .template_list_params import TemplateListParams as TemplateListParams +from .template_create_params import TemplateCreateParams as TemplateCreateParams +from .template_update_params import TemplateUpdateParams as TemplateUpdateParams +from .environment_template_deleted import EnvironmentTemplateDeleted as EnvironmentTemplateDeleted diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..13b359d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/environment_file.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/environment_file.cpython-312.pyc new file mode 100644 index 0000000..4b9d9e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/environment_file.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/environment_template.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/environment_template.cpython-312.pyc new file mode 100644 index 0000000..c3e0c79 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/environment_template.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/environment_template_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/environment_template_deleted.cpython-312.pyc new file mode 100644 index 0000000..63169a8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/environment_template_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/file_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/file_create_params.cpython-312.pyc new file mode 100644 index 0000000..80808b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/file_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/file_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/file_list_params.cpython-312.pyc new file mode 100644 index 0000000..a85145e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/file_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/template_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/template_create_params.cpython-312.pyc new file mode 100644 index 0000000..6332db7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/template_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/template_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/template_list_params.cpython-312.pyc new file mode 100644 index 0000000..a8399f5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/template_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/template_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/template_update_params.cpython-312.pyc new file mode 100644 index 0000000..e0a9fc3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/__pycache__/template_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/environment_file.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/environment_file.py new file mode 100644 index 0000000..bdff13b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/environment_file.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["EnvironmentFile"] + + +class EnvironmentFile(BaseModel): + """A live file in an execution environment.""" + + environment_id: str + """The ID of the environment containing this file.""" + + object: Literal["agent.environment.file"] + """The object type. Always `agent.environment.file`.""" + + path: str + """The absolute file path inside the environment's workspace.""" + + size_bytes: int + """The file size in bytes.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/environment_template.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/environment_template.py new file mode 100644 index 0000000..5779f05 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/environment_template.py @@ -0,0 +1,151 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ....._utils import PropertyInfo +from ....._models import BaseModel +from ...hosted_plugin import HostedPlugin + +__all__ = [ + "EnvironmentTemplate", + "File", + "FileHostedTemplateFileResourceFileID", + "FileHostedTemplateFileResourceInline", + "Network", + "Packages", + "Skill", + "SkillHostedTemplateSkillResourceSkillReference", + "SkillHostedTemplateSkillResourceInline", +] + + +class FileHostedTemplateFileResourceFileID(BaseModel): + """A project-scoped Files API reference resolved separately for each session.""" + + file_id: str + """The ID of the uploaded file.""" + + path: str + """The file's absolute path inside the environment.""" + + type: Literal["file_id"] + """The type of the object. Always `file_id`.""" + + +class FileHostedTemplateFileResourceInline(BaseModel): + """Metadata for confidential inline file contents.""" + + path: str + """The file's absolute path inside the environment.""" + + size_bytes: int + """The decoded size of the inline file in bytes.""" + + type: Literal["inline"] + """The type of the object. Always `inline`.""" + + +File: TypeAlias = Annotated[ + Union[FileHostedTemplateFileResourceFileID, FileHostedTemplateFileResourceInline], + PropertyInfo(discriminator="type"), +] + + +class Network(BaseModel): + """Runtime network access for each OpenAI-hosted environment.""" + + access: Literal["enabled", "disabled", "restricted"] + """The environment's network access mode. + + - `enabled` - Allows unrestricted network access. + - `disabled` - Disables network access. + - `restricted` - Allows access only to configured domains. + """ + + allowed_domains: List[str] + """Domains the environment may access when network access is restricted.""" + + +class Packages(BaseModel): + """Packages installed in each fresh OpenAI-hosted environment.""" + + npm: List[str] + """npm packages installed globally in the environment.""" + + python: List[str] + """Python packages installed in the environment.""" + + system: List[str] + """System packages installed in the environment.""" + + +class SkillHostedTemplateSkillResourceSkillReference(BaseModel): + """A skill resolved afresh from the Skills API whenever a session starts.""" + + skill_id: str + """The referenced skill ID.""" + + type: Literal["skill_reference"] + """The type of the object. Always `skill_reference`.""" + + version: Optional[str] = None + """The requested version selector, including `latest`.""" + + +class SkillHostedTemplateSkillResourceInline(BaseModel): + """Safe metadata for an inline skill archive.""" + + description: str + """The skill description declared in `SKILL.md`.""" + + name: str + """The skill name declared in `SKILL.md`.""" + + type: Literal["inline"] + """The type of the object. Always `inline`.""" + + +Skill: TypeAlias = Annotated[ + Union[SkillHostedTemplateSkillResourceSkillReference, SkillHostedTemplateSkillResourceInline], + PropertyInfo(discriminator="type"), +] + + +class EnvironmentTemplate(BaseModel): + """ + Reusable configuration that provisions a fresh OpenAI-hosted environment for each session. + """ + + id: str + """The ID of the reusable environment template.""" + + capability_directories: List[str] + """Directories that expose capabilities to the agent.""" + + created_at: int + """The Unix timestamp, in seconds, when the template was created.""" + + files: List[File] + """Safe file metadata, excluding contents and session-scoped file IDs.""" + + name: Optional[str] = None + """An optional human-readable display name for the template.""" + + network: Network + """Runtime network access for each OpenAI-hosted environment.""" + + object: Literal["agent.environment.template"] + """The object type. Always `agent.environment.template`.""" + + packages: Packages + """Packages installed in each fresh OpenAI-hosted environment.""" + + plugins: List[HostedPlugin] + """Safe plugin metadata, excluding inline archive contents.""" + + skills: List[Skill] + """Safe skill metadata, preserving unresolved version selectors.""" + + updated_at: int + """The Unix timestamp, in seconds, when the template was last updated.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/environment_template_deleted.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/environment_template_deleted.py new file mode 100644 index 0000000..5c55846 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/environment_template_deleted.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["EnvironmentTemplateDeleted"] + + +class EnvironmentTemplateDeleted(BaseModel): + """A deleted reusable environment template.""" + + id: str + """The ID of the deleted environment template.""" + + deleted: bool + """Whether the environment template was deleted. Always `true`.""" + + object: Literal["agent.environment.template.deleted"] + """The object type. Always `agent.environment.template.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/file_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/file_create_params.py new file mode 100644 index 0000000..b028235 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/file_create_params.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["FileCreateParams", "HostedEnvironmentFileParamFileID", "HostedEnvironmentFileParamInline"] + + +class HostedEnvironmentFileParamFileID(TypedDict, total=False): + file_id: Required[str] + """The ID of the uploaded file.""" + + path: Required[str] + """The absolute destination path inside `/workspace`.""" + + type: Required[Literal["file_id"]] + """The type of the object. Always `file_id`.""" + + +class HostedEnvironmentFileParamInline(TypedDict, total=False): + data: Required[str] + """The standard-base64-encoded file contents.""" + + path: Required[str] + """The absolute destination path inside `/workspace`.""" + + type: Required[Literal["inline"]] + """The type of the object. Always `inline`.""" + + +FileCreateParams: TypeAlias = Union[HostedEnvironmentFileParamFileID, HostedEnvironmentFileParamInline] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/file_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/file_list_params.py new file mode 100644 index 0000000..8cad4e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/file_list_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["FileListParams"] + + +class FileListParams(TypedDict, total=False): + limit: Optional[int] + """The maximum number of files to return, between 1 and 100.""" + + order: Literal["asc", "desc"] + """Sort by case-sensitive path components. Defaults to descending. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + """ + + page: str + """The opaque token from the previous page. Keep the same path, order, and limit.""" + + path: Optional[str] + """Restrict the listing to this absolute workspace directory.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/template_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/template_create_params.py new file mode 100644 index 0000000..9fc8581 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/template_create_params.py @@ -0,0 +1,83 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from ....._types import SequenceNotStr +from ...hosted_skill_param import HostedSkillParam +from ...hosted_plugin_param import HostedPluginParam +from ...setup_command_param import SetupCommandParam +from ...hosted_environment_file_param import HostedEnvironmentFileParam + +__all__ = ["TemplateCreateParams", "Network", "Packages"] + + +class TemplateCreateParams(TypedDict, total=False): + capability_directories: Optional[SequenceNotStr[str]] + """Directories that contain capabilities exposed to the agent. + + Defaults to an empty list. + """ + + env: Optional[Dict[str, str]] + """Environment variables made available to the agent.""" + + files: Optional[Iterable[HostedEnvironmentFileParam]] + """Files available before the agent starts. Defaults to an empty list.""" + + name: Optional[str] + """An optional human-readable display name for the template.""" + + network: Optional[Network] + """Network access policy for the environment. + + Defaults to disabled for GA requests and enabled for beta requests. + """ + + packages: Optional[Packages] + """Packages to install in the environment. Defaults to empty package lists.""" + + plugins: Optional[Iterable[HostedPluginParam]] + """Plugins provided as inline ZIP archives. Defaults to an empty list.""" + + setup_commands: Optional[Iterable[SetupCommandParam]] + """Ordered, confidential setup commands. Command bodies are never returned.""" + + skills: Optional[Iterable[HostedSkillParam]] + """Skills referenced by ID or provided as inline ZIP archives. + + Defaults to an empty list. + """ + + +class Network(TypedDict, total=False): + """Network access policy for the environment. + + Defaults to disabled for GA requests and enabled for beta requests. + """ + + access: Required[Literal["enabled", "disabled", "restricted"]] + """The environment's network access mode. + + - `enabled` - Allows unrestricted network access. + - `disabled` - Disables network access. + - `restricted` - Allows access only to configured domains. + """ + + allowed_domains: Optional[SequenceNotStr[str]] + """Domains the environment may access when network access is restricted.""" + + +class Packages(TypedDict, total=False): + """Packages to install in the environment. Defaults to empty package lists.""" + + npm: Optional[SequenceNotStr[str]] + """npm packages to install globally. Defaults to an empty list.""" + + python: Optional[SequenceNotStr[str]] + """Python packages to install. Defaults to an empty list.""" + + system: Optional[SequenceNotStr[str]] + """System packages to install. Defaults to an empty list.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/template_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/template_list_params.py new file mode 100644 index 0000000..c20e70a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/template_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["TemplateListParams"] + + +class TemplateListParams(TypedDict, total=False): + after: str + """Return resources after this resource ID in the selected order.""" + + limit: int + """The maximum number of resources to return, between 1 and 100. Defaults to 20.""" + + order: Literal["asc", "desc"] + """The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/template_update_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/template_update_params.py new file mode 100644 index 0000000..a3a0bed --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/environments/template_update_params.py @@ -0,0 +1,78 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from ....._types import SequenceNotStr +from ...hosted_skill_param import HostedSkillParam +from ...hosted_plugin_param import HostedPluginParam +from ...setup_command_param import SetupCommandParam +from ...hosted_environment_file_param import HostedEnvironmentFileParam + +__all__ = ["TemplateUpdateParams", "Network", "Packages"] + + +class TemplateUpdateParams(TypedDict, total=False): + capability_directories: Optional[SequenceNotStr[str]] + """Directories that expose capabilities to the agent.""" + + env: Optional[Dict[str, str]] + """Replacement confidential environment values.""" + + files: Optional[Iterable[HostedEnvironmentFileParam]] + """Replacement file configuration materialized for each new session.""" + + name: Optional[str] + """A replacement human-readable display name, or `null` to clear the name.""" + + network: Optional[Network] + """Network access available after setup completes. + + Omit to preserve the current policy, or pass `null` to reset to disabled for GA + requests or enabled for beta requests. + """ + + packages: Optional[Packages] + """Packages installed before the runtime network policy applies.""" + + plugins: Optional[Iterable[HostedPluginParam]] + """Replacement plugin configuration installed for each new session.""" + + setup_commands: Optional[Iterable[SetupCommandParam]] + """Replacement confidential setup commands, never included in returned resources.""" + + skills: Optional[Iterable[HostedSkillParam]] + """Replacement skill configuration installed for each new session.""" + + +class Network(TypedDict, total=False): + """Network access available after setup completes. + + Omit to preserve the current policy, or pass `null` to reset to disabled for GA requests or enabled for beta requests. + """ + + access: Required[Literal["enabled", "disabled", "restricted"]] + """The environment's network access mode. + + - `enabled` - Allows unrestricted network access. + - `disabled` - Disables network access. + - `restricted` - Allows access only to configured domains. + """ + + allowed_domains: Optional[SequenceNotStr[str]] + """Domains the environment may access when network access is restricted.""" + + +class Packages(TypedDict, total=False): + """Packages installed before the runtime network policy applies.""" + + npm: Optional[SequenceNotStr[str]] + """npm packages to install globally. Defaults to an empty list.""" + + python: Optional[SequenceNotStr[str]] + """Python packages to install. Defaults to an empty list.""" + + system: Optional[SequenceNotStr[str]] + """System packages to install. Defaults to an empty list.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/session_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/session_create_params.py new file mode 100644 index 0000000..11d5391 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/session_create_params.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr +from ..agent_text_param import AgentTextParam +from ..agent_tool_param import AgentToolParam +from ..environment_param import EnvironmentParam +from ..agent_reasoning_param import AgentReasoningParam +from ..multi_agent_config_param import MultiAgentConfigParam +from ..agent_session_input_message_param import AgentSessionInputMessageParam + +__all__ = ["SessionCreateParamsBase", "Agent", "SessionCreateParamsNonStreaming", "SessionCreateParamsStreaming"] + + +class SessionCreateParamsBase(TypedDict, total=False): + environment: Required[EnvironmentParam] + """An inline execution environment or a reference to an environment template.""" + + agent: Agent + """Agent configuration. + + With `agent_id`, supplied fields override the saved agent for this session. + Without `agent_id`, `model` is required. + """ + + agent_id: str + """The ID of a saved reusable agent. + + Omit `agent` to use its configuration unchanged. + """ + + input: Union[str, Iterable[AgentSessionInputMessageParam], None] + """Initial input to submit when the session is created. + + A string is shorthand for a single user message. Required when + `environment.type` is `none`, or when `stream` is `true` for an environment that + is not `self_hosted`; optional for self-hosted and non-streaming execution + environments. + """ + + metadata: Optional[Dict[str, str]] + """ + Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. Omission or null defaults to an empty map. + """ + + vault_ids: Optional[SequenceNotStr[str]] + """The IDs of vaults made available to the session.""" + + +class Agent(TypedDict, total=False): + """Agent configuration. + + With `agent_id`, supplied fields override the saved agent for this session. Without `agent_id`, `model` is required. + """ + + instructions: Optional[str] + """Additional instructions appended to the agent's default base instructions. + + Omit to leave unchanged. + """ + + model: str + """The model to use for the agent. The requested model name is preserved.""" + + multi_agent: Optional[MultiAgentConfigParam] + """Configuration for creating and coordinating subagents.""" + + reasoning: Optional[AgentReasoningParam] + """Configuration for model reasoning. + + Omit to keep the current settings; pass `null` to reset to the model's default + effort. + """ + + service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast", "ultrafast"]] + """The service tier used for model requests. + + - `auto` - Selects the service tier automatically. + - `default` - Uses the default service tier. + - `flex` - Uses the flex service tier. + - `priority` - Uses the priority service tier. + - `fast` - Uses the fast service tier. + - `ultrafast` - Uses the ultrafast service tier. + """ + + text: Optional[AgentTextParam] + """Configuration for text generated by the agent.""" + + tools: Optional[Iterable[AgentToolParam]] + """Tools available to the agent. Omit to inherit, or pass null to clear them.""" + + +class SessionCreateParamsNonStreaming(SessionCreateParamsBase, total=False): + stream: Literal[False] + """Whether to stream session events as server-sent events. Defaults to `false`.""" + + +class SessionCreateParamsStreaming(SessionCreateParamsBase): + stream: Required[Literal[True]] + """Whether to stream session events as server-sent events. Defaults to `false`.""" + + +SessionCreateParams = Union[SessionCreateParamsNonStreaming, SessionCreateParamsStreaming] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/session_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/session_list_params.py new file mode 100644 index 0000000..14579b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/session_list_params.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["SessionListParams"] + + +class SessionListParams(TypedDict, total=False): + after: str + """Return resources after this resource ID in the selected order.""" + + agent_id: str + """Only return sessions whose root agent has this ID. + + Omit to return sessions for all agents. + """ + + limit: Optional[int] + """The maximum number of resources to return.""" + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp. + + Use `asc` for ascending order or `desc` for descending order. Defaults to + `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/session_update_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/session_update_params.py new file mode 100644 index 0000000..d10cd29 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/session_update_params.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["SessionUpdateParams", "Agent", "AgentReasoning"] + + +class SessionUpdateParams(TypedDict, total=False): + agent: Agent + """Model settings for subsequent turns. Omitted fields stay unchanged.""" + + metadata: Optional[Dict[str, str]] + """Replaces all metadata. + + Omit to leave unchanged, or pass null or {} to clear it. Up to 16 string + key-value pairs, with keys up to 64 and values up to 512 characters. + """ + + +class AgentReasoning(TypedDict, total=False): + """Reasoning settings to update. Omit to keep the current effort.""" + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] + """Omit to keep the current effort. Null selects the model's default effort.""" + + +class Agent(TypedDict, total=False): + """Model settings for subsequent turns. Omitted fields stay unchanged.""" + + model: str + """The model for subsequent turns. Omit to keep the current model.""" + + reasoning: AgentReasoning + """Reasoning settings to update. Omit to keep the current effort.""" + + service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast", "ultrafast"]] + """Omit to keep the current tier. Null resets it to auto. + + - `auto` - Selects the service tier automatically. + - `default` - Uses the default service tier. + - `flex` - Uses the flex service tier. + - `priority` - Uses the priority service tier. + - `fast` - Uses the fast service tier. + - `ultrafast` - Uses the ultrafast service tier. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__init__.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__init__.py new file mode 100644 index 0000000..ae2fda1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__init__.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .turn import Turn as Turn +from .item_list_params import ItemListParams as ItemListParams +from .session_artifact import SessionArtifact as SessionArtifact +from .turn_list_params import TurnListParams as TurnListParams +from .event_create_params import EventCreateParams as EventCreateParams +from .artifact_list_params import ArtifactListParams as ArtifactListParams +from .subagent_list_params import SubagentListParams as SubagentListParams +from .session_artifact_deleted import SessionArtifactDeleted as SessionArtifactDeleted diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f45821f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/artifact_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/artifact_list_params.cpython-312.pyc new file mode 100644 index 0000000..6233c71 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/artifact_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/event_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/event_create_params.cpython-312.pyc new file mode 100644 index 0000000..5b9044a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/event_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/item_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/item_list_params.cpython-312.pyc new file mode 100644 index 0000000..a055d8e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/item_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/session_artifact.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/session_artifact.cpython-312.pyc new file mode 100644 index 0000000..c7011ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/session_artifact.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/session_artifact_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/session_artifact_deleted.cpython-312.pyc new file mode 100644 index 0000000..282c944 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/session_artifact_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/subagent_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/subagent_list_params.cpython-312.pyc new file mode 100644 index 0000000..ace1697 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/subagent_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/turn.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/turn.cpython-312.pyc new file mode 100644 index 0000000..80e6581 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/turn.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/turn_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/turn_list_params.cpython-312.pyc new file mode 100644 index 0000000..e693a4a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/__pycache__/turn_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/artifact_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/artifact_list_params.py new file mode 100644 index 0000000..e7cc236 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/artifact_list_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["ArtifactListParams"] + + +class ArtifactListParams(TypedDict, total=False): + after: Optional[str] + """Return artifacts after this immutable artifact ID.""" + + environment_id: Optional[str] + """Restrict the listing to artifacts produced by this environment.""" + + limit: Optional[int] + """The maximum number of artifacts to return, between 1 and 100.""" + + order: Literal["asc", "desc"] + """Sort by creation time and ID. Defaults to descending. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/event_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/event_create_params.py new file mode 100644 index 0000000..11b087c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/event_create_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Required, Annotated, TypedDict + +from ....._utils import PropertyInfo +from ...agent_session_input_param import AgentSessionInputParam + +__all__ = ["EventCreateParams"] + + +class EventCreateParams(TypedDict, total=False): + events: Required[Iterable[AgentSessionInputParam]] + """The input events to submit to the session.""" + + idempotency_key: Annotated[str, PropertyInfo(alias="Idempotency-Key")] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/item_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/item_list_params.py new file mode 100644 index 0000000..26b461d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/item_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ItemListParams"] + + +class ItemListParams(TypedDict, total=False): + after: str + """Return resources after this resource ID in the selected order.""" + + limit: int + """The maximum number of resources to return, between 1 and 100. Defaults to 20.""" + + order: Literal["asc", "desc"] + """The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/session_artifact.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/session_artifact.py new file mode 100644 index 0000000..58442a4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/session_artifact.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["SessionArtifact"] + + +class SessionArtifact(BaseModel): + """An immutable file published by a completed hosted session turn.""" + + id: str + """The immutable artifact ID.""" + + created_at: int + """The Unix timestamp, in seconds, when the artifact was published.""" + + environment_id: str + """The ID of the environment that produced the artifact.""" + + object: Literal["agent.session.artifact"] + """The object type. Always `agent.session.artifact`.""" + + path: str + """The original absolute file path in the execution environment.""" + + session_id: str + """The ID of the session that owns the artifact.""" + + size_bytes: int + """The immutable artifact size in bytes.""" + + turn_id: str + """The ID of the completed turn that published the artifact.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/session_artifact_deleted.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/session_artifact_deleted.py new file mode 100644 index 0000000..57d744b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/session_artifact_deleted.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["SessionArtifactDeleted"] + + +class SessionArtifactDeleted(BaseModel): + """Confirmation that an immutable session artifact was deleted.""" + + id: str + """The ID of the deleted session artifact.""" + + deleted: bool + """Whether the session artifact was deleted. Always `true`.""" + + object: Literal["agent.session.artifact.deleted"] + """The object type. Always `agent.session.artifact.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagent_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagent_list_params.py new file mode 100644 index 0000000..8c77744 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagent_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["SubagentListParams"] + + +class SubagentListParams(TypedDict, total=False): + after: str + """Return resources after this resource ID in the selected order.""" + + limit: int + """The maximum number of resources to return, between 1 and 100. Defaults to 20.""" + + order: Literal["asc", "desc"] + """The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/__init__.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/__init__.py new file mode 100644 index 0000000..dc5573f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .item_list_params import ItemListParams as ItemListParams +from .turn_list_params import TurnListParams as TurnListParams diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c90040f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/__pycache__/item_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/__pycache__/item_list_params.cpython-312.pyc new file mode 100644 index 0000000..5c6024d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/__pycache__/item_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/__pycache__/turn_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/__pycache__/turn_list_params.cpython-312.pyc new file mode 100644 index 0000000..918d304 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/__pycache__/turn_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/item_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/item_list_params.py new file mode 100644 index 0000000..cd1a5fb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/item_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ItemListParams"] + + +class ItemListParams(TypedDict, total=False): + session_id: Required[str] + + after: str + """Return resources after this resource ID in the selected order.""" + + limit: int + """The maximum number of resources to return, between 1 and 100. Defaults to 20.""" + + order: Literal["asc", "desc"] + """The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turn_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turn_list_params.py new file mode 100644 index 0000000..e49581f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turn_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["TurnListParams"] + + +class TurnListParams(TypedDict, total=False): + session_id: Required[str] + + after: str + """Return resources after this resource ID in the selected order.""" + + limit: int + """The maximum number of resources to return, between 1 and 100. Defaults to 20.""" + + order: Literal["asc", "desc"] + """The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turns/__init__.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turns/__init__.py new file mode 100644 index 0000000..d630e30 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turns/__init__.py @@ -0,0 +1,5 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .item_list_params import ItemListParams as ItemListParams diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turns/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turns/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..27bc549 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turns/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turns/__pycache__/item_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turns/__pycache__/item_list_params.cpython-312.pyc new file mode 100644 index 0000000..5e91b45 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turns/__pycache__/item_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turns/item_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turns/item_list_params.py new file mode 100644 index 0000000..d7522d7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/subagents/turns/item_list_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ItemListParams"] + + +class ItemListParams(TypedDict, total=False): + session_id: Required[str] + + subagent_id: Required[str] + + after: str + """Return resources after this resource ID in the selected order.""" + + limit: int + """The maximum number of resources to return, between 1 and 100. Defaults to 20.""" + + order: Literal["asc", "desc"] + """The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/turn.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/turn.py new file mode 100644 index 0000000..29e119e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/turn.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel +from ...token_usage import TokenUsage +from ...session_turn_error import SessionTurnError + +__all__ = ["Turn"] + + +class Turn(BaseModel): + """The canonical public representation of a session turn.""" + + id: str + """The ID of the turn.""" + + agent_id: str + """The ID of the agent that ran the turn.""" + + completed_at: Optional[int] = None + """The Unix timestamp, in seconds, when the turn reached a terminal state.""" + + created_at: int + """The Unix timestamp, in seconds, used to order the turn by creation time. + + Subagent turns use their start time, falling back to completion time or the + subagent opening time when the preceding timestamps are unavailable. + """ + + error: Optional[SessionTurnError] = None + """A customer-safe error. Non-null only for a failed turn.""" + + object: Literal["agent.session.turn"] + """The object type. Always `agent.session.turn`.""" + + session_id: str + """The ID of the session that owns the turn.""" + + started_at: Optional[int] = None + """The Unix timestamp, in seconds, when the turn started.""" + + status: Literal["queued", "in_progress", "waiting", "completed", "failed", "cancelled"] + """The current status of the turn. + + - `queued` - The turn is waiting to start. + - `in_progress` - The turn is in progress. + - `waiting` - The turn is waiting for external input. + - `completed` - The turn completed successfully. + - `failed` - The turn failed. + - `cancelled` - The turn was cancelled. + """ + + subagent_id: Optional[str] = None + """The ID of the subagent that ran the turn, if applicable.""" + + usage: Optional[TokenUsage] = None + """Best-effort token usage for the turn, or null if unknown. + + Recorded usage may change. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/turn_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/turn_list_params.py new file mode 100644 index 0000000..be51516 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/sessions/turn_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["TurnListParams"] + + +class TurnListParams(TypedDict, total=False): + after: str + """Return resources after this resource ID in the selected order.""" + + limit: int + """The maximum number of resources to return, between 1 and 100. Defaults to 20.""" + + order: Literal["asc", "desc"] + """The order in which resources are returned. Defaults to `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault.py new file mode 100644 index 0000000..da4ab9f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["Vault"] + + +class Vault(BaseModel): + """A collection of credentials for MCP servers and OpenAI-hosted environments.""" + + id: str + """The ID of the vault.""" + + created_at: int + """The Unix timestamp, in seconds, when the vault was created.""" + + metadata: Dict[str, str] + """ + Key-value pairs associated with the vault, such as an application or team + identifier. + """ + + name: Optional[str] = None + """The human-readable name of the vault, if set.""" + + object: Literal["vault"] + """The object type. Always `vault`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_create_params.py new file mode 100644 index 0000000..3bd66a9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_create_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import TypedDict + +__all__ = ["VaultCreateParams"] + + +class VaultCreateParams(TypedDict, total=False): + metadata: Optional[Dict[str, str]] + """ + Key-value pairs to associate with the vault, such as an application or team + identifier. + """ + + name: str + """The name is trimmed before storage. + + It must contain 1 to 256 UTF-8 bytes after trimming. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_deleted.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_deleted.py new file mode 100644 index 0000000..9beb82a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_deleted.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["VaultDeleted"] + + +class VaultDeleted(BaseModel): + """Confirmation that a vault was deleted.""" + + id: str + """The ID of the deleted vault.""" + + deleted: bool + """Whether the resource was deleted. Always `true`.""" + + object: Literal["vault.deleted"] + """The object type. Always `vault.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_list_params.py new file mode 100644 index 0000000..72bb8c5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_list_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +from .vault_status_filter_param import VaultStatusFilterParam + +__all__ = ["VaultListParams"] + + +class VaultListParams(TypedDict, total=False): + after: str + """Return resources after this resource ID in the selected order.""" + + limit: Optional[int] + """The maximum number of resources to return. + + Defaults to 20. Values are clamped between 1 and 100. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp. + + Use `asc` for ascending order or `desc` for descending order. Defaults to + `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + """ + + status: VaultStatusFilterParam + """ + Filter by one status or a list, such as `status=active` or + `status[]=active&status[]=archived`. Both statuses are included by default. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_status.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_status.py new file mode 100644 index 0000000..f594473 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_status.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["VaultStatus"] + +VaultStatus: TypeAlias = Literal["active", "archived"] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_status_filter_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_status_filter_param.py new file mode 100644 index 0000000..a0017ef --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vault_status_filter_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import TypeAlias + +from .vault_status import VaultStatus + +__all__ = ["VaultStatusFilterParam"] + +VaultStatusFilterParam: TypeAlias = Union[VaultStatus, Iterable[VaultStatus]] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__init__.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__init__.py new file mode 100644 index 0000000..056a55e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__init__.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .credential import Credential as Credential +from .credential_auth import CredentialAuth as CredentialAuth +from .credential_deleted import CredentialDeleted as CredentialDeleted +from .credential_networking import CredentialNetworking as CredentialNetworking +from .credential_list_params import CredentialListParams as CredentialListParams +from .credential_create_params import CredentialCreateParams as CredentialCreateParams +from .credential_update_params import CredentialUpdateParams as CredentialUpdateParams +from .credential_networking_param import CredentialNetworkingParam as CredentialNetworkingParam +from .credential_auth_create_param import CredentialAuthCreateParam as CredentialAuthCreateParam +from .credential_auth_rotate_param import CredentialAuthRotateParam as CredentialAuthRotateParam +from .mcp_oauth_token_endpoint_auth import McpOauthTokenEndpointAuth as McpOauthTokenEndpointAuth +from .mcp_oauth_token_endpoint_auth_create_param import ( + McpOauthTokenEndpointAuthCreateParam as McpOauthTokenEndpointAuthCreateParam, +) +from .mcp_oauth_token_endpoint_auth_rotate_param import ( + McpOauthTokenEndpointAuthRotateParam as McpOauthTokenEndpointAuthRotateParam, +) diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b9542ea Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential.cpython-312.pyc new file mode 100644 index 0000000..752278f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_auth.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_auth.cpython-312.pyc new file mode 100644 index 0000000..e3b2597 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_auth.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_auth_create_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_auth_create_param.cpython-312.pyc new file mode 100644 index 0000000..ead2938 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_auth_create_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_auth_rotate_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_auth_rotate_param.cpython-312.pyc new file mode 100644 index 0000000..390a2ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_auth_rotate_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_create_params.cpython-312.pyc new file mode 100644 index 0000000..a7db16c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_deleted.cpython-312.pyc new file mode 100644 index 0000000..446724d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_list_params.cpython-312.pyc new file mode 100644 index 0000000..154c0c2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_networking.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_networking.cpython-312.pyc new file mode 100644 index 0000000..e143a72 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_networking.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_networking_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_networking_param.cpython-312.pyc new file mode 100644 index 0000000..2f2fd09 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_networking_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_update_params.cpython-312.pyc new file mode 100644 index 0000000..81c0271 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/credential_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/mcp_oauth_token_endpoint_auth.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/mcp_oauth_token_endpoint_auth.cpython-312.pyc new file mode 100644 index 0000000..5cd42c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/mcp_oauth_token_endpoint_auth.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/mcp_oauth_token_endpoint_auth_create_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/mcp_oauth_token_endpoint_auth_create_param.cpython-312.pyc new file mode 100644 index 0000000..6448ea2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/mcp_oauth_token_endpoint_auth_create_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/mcp_oauth_token_endpoint_auth_rotate_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/mcp_oauth_token_endpoint_auth_rotate_param.cpython-312.pyc new file mode 100644 index 0000000..7cd085f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/__pycache__/mcp_oauth_token_endpoint_auth_rotate_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential.py new file mode 100644 index 0000000..903072e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict +from typing_extensions import Literal + +from ....._models import BaseModel +from .credential_auth import CredentialAuth + +__all__ = ["Credential"] + + +class Credential(BaseModel): + """Metadata for a stored credential. Secret values are never returned.""" + + id: str + """The ID of the credential.""" + + auth: CredentialAuth + """The authentication method and non-secret configuration of the credential.""" + + created_at: int + """The Unix timestamp, in seconds, when the credential was created.""" + + metadata: Dict[str, str] + """Application-defined key-value pairs associated with this credential.""" + + name: str + """The human-readable name of the credential.""" + + object: Literal["vault.credential"] + """The object type. Always `vault.credential`.""" + + updated_at: int + """The Unix timestamp, in seconds, when the credential was last updated.""" + + vault_id: str + """The ID of the vault containing this credential.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_auth.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_auth.py new file mode 100644 index 0000000..2ee2f12 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_auth.py @@ -0,0 +1,98 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ....._utils import PropertyInfo +from ....._models import BaseModel +from .credential_networking import CredentialNetworking +from .mcp_oauth_token_endpoint_auth import McpOauthTokenEndpointAuth + +__all__ = [ + "CredentialAuth", + "VaultCredentialAuthResourceMcpOauth", + "VaultCredentialAuthResourceMcpOauthRefresh", + "VaultCredentialAuthResourceStaticBearer", + "VaultCredentialAuthResourceEnvironmentVariable", +] + + +class VaultCredentialAuthResourceMcpOauthRefresh(BaseModel): + """Public refresh metadata without refresh tokens or OAuth client secrets.""" + + client_id: str + """The OAuth client ID used when requesting a new access token.""" + + resource: Optional[str] = None + """ + The resource URI sent to the OAuth token endpoint during refresh, if configured. + """ + + scope: Optional[str] = None + """Space-separated OAuth scopes requested during refresh, if configured.""" + + token_endpoint: str + """The HTTPS OAuth token endpoint used for refresh.""" + + token_endpoint_auth: McpOauthTokenEndpointAuth + """ + How the OAuth client authenticates to the token endpoint, excluding its client + secret. + """ + + +class VaultCredentialAuthResourceMcpOauth(BaseModel): + """ + Public metadata for an OAuth credential; tokens and client secrets are never returned. + """ + + expires_at: Optional[str] = None + """When the OAuth access token expires, as an RFC 3339 timestamp, if known.""" + + mcp_server_url: str + """The HTTPS MCP server URL authorized by this credential.""" + + refresh: Optional[VaultCredentialAuthResourceMcpOauthRefresh] = None + """Public refresh metadata without refresh tokens or OAuth client secrets.""" + + type: Literal["mcp_oauth"] + """The type of the object. Always `mcp_oauth`.""" + + +class VaultCredentialAuthResourceStaticBearer(BaseModel): + """Metadata for a bearer-token credential, without automatic OAuth refresh.""" + + mcp_server_url: str + """The HTTPS MCP server URL authorized by this credential.""" + + type: Literal["static_bearer"] + """The type of the object. Always `static_bearer`.""" + + +class VaultCredentialAuthResourceEnvironmentVariable(BaseModel): + """Metadata for an HTTP credential used only in OpenAI-hosted environments. + + Sandbox code receives a placeholder. The proxy substitutes the secret for allowed HTTPS destinations on ports 443 and 8443. The real secret is not available to sandbox code for local computation and is never returned in this resource. + """ + + networking: CredentialNetworking + """ + The destinations where the proxy can substitute the secret, subject to the + environment network policy. + """ + + secret_name: str + """The environment variable name that receives the placeholder in the sandbox.""" + + type: Literal["environment_variable"] + """The type of the object. Always `environment_variable`.""" + + +CredentialAuth: TypeAlias = Annotated[ + Union[ + VaultCredentialAuthResourceMcpOauth, + VaultCredentialAuthResourceStaticBearer, + VaultCredentialAuthResourceEnvironmentVariable, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_auth_create_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_auth_create_param.py new file mode 100644 index 0000000..5b0497c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_auth_create_param.py @@ -0,0 +1,121 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .credential_networking_param import CredentialNetworkingParam +from .mcp_oauth_token_endpoint_auth_create_param import McpOauthTokenEndpointAuthCreateParam + +__all__ = [ + "CredentialAuthCreateParam", + "CreateVaultCredentialAuthParamMcpOauth", + "CreateVaultCredentialAuthParamMcpOauthRefresh", + "CreateVaultCredentialAuthParamStaticBearer", + "CreateVaultCredentialAuthParamEnvironmentVariable", +] + + +class CreateVaultCredentialAuthParamMcpOauthRefresh(TypedDict, total=False): + """Optional refresh configuration for an HTTPS OAuth token endpoint.""" + + client_id: Required[str] + """The OAuth client ID used when requesting a new access token.""" + + refresh_token: Required[str] + """The refresh token to store. + + This secret is never returned in credential resources. + """ + + token_endpoint: Required[str] + """ + The HTTPS OAuth token endpoint used to exchange the refresh token for a new + access token. + """ + + token_endpoint_auth: Required[McpOauthTokenEndpointAuthCreateParam] + """How the OAuth client authenticates to the token endpoint.""" + + resource: Optional[str] + """ + The resource URI to send to the OAuth token endpoint during refresh, if + required. + """ + + scope: Optional[str] + """Space-separated OAuth scopes to request during refresh, if required.""" + + +class CreateVaultCredentialAuthParamMcpOauth(TypedDict, total=False): + """An OAuth credential for an HTTPS MCP destination.""" + + access_token: Required[str] + """A write-only OAuth access token; never returned by credential resources.""" + + mcp_server_url: Required[str] + """The HTTPS MCP server URL authorized by this credential.""" + + type: Required[Literal["mcp_oauth"]] + """The type of the object. Always `mcp_oauth`.""" + + expires_at: Optional[str] + """When the OAuth access token expires, as an RFC 3339 timestamp, if known.""" + + refresh: Optional[CreateVaultCredentialAuthParamMcpOauthRefresh] + """Optional refresh configuration for an HTTPS OAuth token endpoint.""" + + +class CreateVaultCredentialAuthParamStaticBearer(TypedDict, total=False): + """A bearer token for an MCP server, without automatic OAuth refresh.""" + + token: Required[str] + """The bearer token to store. + + This secret is never returned in credential resources. + """ + + mcp_server_url: Required[str] + """The HTTPS MCP server URL authorized by this credential.""" + + type: Required[Literal["static_bearer"]] + """The type of the object. Always `static_bearer`.""" + + +class CreateVaultCredentialAuthParamEnvironmentVariable(TypedDict, total=False): + """An HTTP credential for OpenAI-hosted environments only. + + The sandbox receives an environment variable containing a placeholder, not the secret. Use the placeholder unchanged in outgoing requests. The egress proxy replaces the placeholder with the secret for allowed HTTPS destinations on ports 443 and 8443. Sandbox code cannot read the real secret or use it for local computation, such as signing a request. + """ + + networking: Required[CredentialNetworkingParam] + """The destinations where the proxy can substitute this secret. + + The environment network policy must also allow them. + """ + + secret_name: Required[str] + """ + The environment variable name that receives the placeholder, such as + `SERVICE_API_KEY`. Use ASCII letters, digits, and underscores, starting with a + letter or underscore. Names starting with `CODEX_` and managed proxy or + certificate variable names are reserved. + """ + + secret_value: Required[str] + """The write-only secret to store. + + Never returned in credential resources or supplied directly to sandbox code. + Must be nonempty and must not contain carriage returns, newlines, or NUL bytes. + """ + + type: Required[Literal["environment_variable"]] + """The type of the object. Always `environment_variable`.""" + + +CredentialAuthCreateParam: TypeAlias = Union[ + CreateVaultCredentialAuthParamMcpOauth, + CreateVaultCredentialAuthParamStaticBearer, + CreateVaultCredentialAuthParamEnvironmentVariable, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_auth_rotate_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_auth_rotate_param.py new file mode 100644 index 0000000..16e02ea --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_auth_rotate_param.py @@ -0,0 +1,93 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .mcp_oauth_token_endpoint_auth_rotate_param import McpOauthTokenEndpointAuthRotateParam + +__all__ = [ + "CredentialAuthRotateParam", + "RotateVaultCredentialAuthParamMcpOauth", + "RotateVaultCredentialAuthParamMcpOauthRefresh", + "RotateVaultCredentialAuthParamStaticBearer", + "RotateVaultCredentialAuthParamEnvironmentVariable", +] + + +class RotateVaultCredentialAuthParamMcpOauthRefresh(TypedDict, total=False): + """Optional write-only refresh-token and client-secret updates.""" + + refresh_token: Optional[str] + """The replacement refresh token. + + Omit or pass `null` to keep the stored token. This secret is never returned in + resources. + """ + + scope: Optional[str] + """Replacement space-separated OAuth scopes for refresh requests. + + Omit to keep the scopes, or pass `null` to stop sending a scope parameter. + """ + + token_endpoint_auth: Optional[McpOauthTokenEndpointAuthRotateParam] + """Client-secret updates for the existing token endpoint authentication method.""" + + +class RotateVaultCredentialAuthParamMcpOauth(TypedDict, total=False): + """Rotate an OAuth credential for an HTTPS MCP destination.""" + + type: Required[Literal["mcp_oauth"]] + """The type of the object. Always `mcp_oauth`.""" + + access_token: Optional[str] + """A write-only replacement OAuth access token.""" + + expires_at: Optional[str] + """The replacement expiry as an RFC 3339 timestamp, or `null` to clear it. + + Omitting this field preserves the expiry unless a new access token is supplied, + in which case the expiry is cleared. + """ + + refresh: Optional[RotateVaultCredentialAuthParamMcpOauthRefresh] + """Optional write-only refresh-token and client-secret updates.""" + + +class RotateVaultCredentialAuthParamStaticBearer(TypedDict, total=False): + """Replace the bearer token for the credential's MCP server.""" + + token: Required[str] + """The replacement bearer token. + + This secret is never returned in credential resources. + """ + + type: Required[Literal["static_bearer"]] + """The type of the object. Always `static_bearer`.""" + + +class RotateVaultCredentialAuthParamEnvironmentVariable(TypedDict, total=False): + """Replace the secret for an OpenAI-hosted environment credential. + + The environment variable name and networking configuration remain unchanged. + """ + + secret_value: Required[str] + """The write-only replacement secret. + + Never returned in credential resources or supplied directly to sandbox code. + Must be nonempty and must not contain carriage returns, newlines, or NUL bytes. + """ + + type: Required[Literal["environment_variable"]] + """The type of the object. Always `environment_variable`.""" + + +CredentialAuthRotateParam: TypeAlias = Union[ + RotateVaultCredentialAuthParamMcpOauth, + RotateVaultCredentialAuthParamStaticBearer, + RotateVaultCredentialAuthParamEnvironmentVariable, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_create_params.py new file mode 100644 index 0000000..e37ab7a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_create_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, TypedDict + +from .credential_auth_create_param import CredentialAuthCreateParam + +__all__ = ["CredentialCreateParams"] + + +class CredentialCreateParams(TypedDict, total=False): + auth: Required[CredentialAuthCreateParam] + """The authentication method and write-only secret values to store.""" + + name: Required[str] + """The name is trimmed before storage. + + It must contain 1 to 256 UTF-8 bytes after trimming. + """ + + metadata: Dict[str, str] + """ + Up to 16 string key-value pairs, with keys up to 64 and values up to 512 + characters. Defaults to an empty map. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_deleted.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_deleted.py new file mode 100644 index 0000000..76e4635 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_deleted.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["CredentialDeleted"] + + +class CredentialDeleted(BaseModel): + """Confirmation that a vault credential was deleted.""" + + id: str + """The ID of the deleted credential.""" + + deleted: bool + """Whether the resource was deleted. Always `true`.""" + + object: Literal["vault.credential.deleted"] + """The object type. Always `vault.credential.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_list_params.py new file mode 100644 index 0000000..d453fe6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_list_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +from ..vault_status_filter_param import VaultStatusFilterParam + +__all__ = ["CredentialListParams"] + + +class CredentialListParams(TypedDict, total=False): + after: str + """Return resources after this resource ID in the selected order.""" + + limit: Optional[int] + """The maximum number of resources to return. + + Defaults to 20. Values are clamped between 1 and 100. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp. + + Use `asc` for ascending order or `desc` for descending order. Defaults to + `desc`. + + - `asc` - Returns resources in ascending order. + - `desc` - Returns resources in descending order. + """ + + status: VaultStatusFilterParam + """ + Filter by one status or a list, such as `status=active` or + `status[]=active&status[]=archived`. Both statuses are included by default. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_networking.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_networking.py new file mode 100644 index 0000000..d3e7576 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_networking.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ....._utils import PropertyInfo +from ....._models import BaseModel + +__all__ = [ + "CredentialNetworking", + "VaultCredentialNetworkingResourceUnrestricted", + "VaultCredentialNetworkingResourceLimited", +] + + +class VaultCredentialNetworkingResourceUnrestricted(BaseModel): + """Allows substitution for destinations permitted by the environment network policy. + + Requires `environment.network.access` to be `restricted`, with explicit `allowed_domains`. + """ + + type: Literal["unrestricted"] + """The type of the object. Always `unrestricted`.""" + + +class VaultCredentialNetworkingResourceLimited(BaseModel): + """Allows substitution only for the listed hosts. + + The environment network policy must also allow these hosts. + """ + + allowed_hosts: List[str] + """ + The 1 to 16 distinct allowed hostnames or IPv4 addresses, normalized to + lowercase. Entries contain no scheme, path, port, or wildcard. IPv6 addresses + are not supported. + """ + + type: Literal["limited"] + """The type of the object. Always `limited`.""" + + +CredentialNetworking: TypeAlias = Annotated[ + Union[VaultCredentialNetworkingResourceUnrestricted, VaultCredentialNetworkingResourceLimited], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_networking_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_networking_param.py new file mode 100644 index 0000000..55a537a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_networking_param.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ....._types import SequenceNotStr + +__all__ = [ + "CredentialNetworkingParam", + "VaultCredentialNetworkingParamUnrestricted", + "VaultCredentialNetworkingParamLimited", +] + + +class VaultCredentialNetworkingParamUnrestricted(TypedDict, total=False): + """Allows substitution for destinations permitted by the environment network policy. + + Requires `environment.network.access` to be `restricted`, with explicit `allowed_domains`. + """ + + type: Required[Literal["unrestricted"]] + """The type of the object. Always `unrestricted`.""" + + +class VaultCredentialNetworkingParamLimited(TypedDict, total=False): + """Allows substitution only for the listed hosts. + + The environment network policy must also allow these hosts. + """ + + allowed_hosts: Required[SequenceNotStr[str]] + """ + The 1 to 16 distinct allowed hostnames or IPv4 addresses, normalized to + lowercase. Entries contain no scheme, path, port, or wildcard. IPv6 addresses + are not supported. + """ + + type: Required[Literal["limited"]] + """The type of the object. Always `limited`.""" + + +CredentialNetworkingParam: TypeAlias = Union[ + VaultCredentialNetworkingParamUnrestricted, VaultCredentialNetworkingParamLimited +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_update_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_update_params.py new file mode 100644 index 0000000..31aabcf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/credential_update_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, TypedDict + +from .credential_auth_rotate_param import CredentialAuthRotateParam + +__all__ = ["CredentialUpdateParams"] + + +class CredentialUpdateParams(TypedDict, total=False): + vault_id: Required[str] + + auth: CredentialAuthRotateParam + """Replacement values for the credential's existing authentication method.""" + + metadata: Dict[str, str] + """Replaces all metadata. + + Omit to preserve it, or pass {} to clear it. Up to 16 string key-value pairs, + with keys up to 64 and values up to 512 characters. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth.py new file mode 100644 index 0000000..b2d6132 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ....._utils import PropertyInfo +from ....._models import BaseModel + +__all__ = [ + "McpOauthTokenEndpointAuth", + "McpOauthTokenEndpointAuthResourceNone", + "McpOauthTokenEndpointAuthResourceClientSecretBasic", + "McpOauthTokenEndpointAuthResourceClientSecretPost", +] + + +class McpOauthTokenEndpointAuthResourceNone(BaseModel): + """Sends the client ID without a client secret.""" + + type: Literal["none"] + """The type of the object. Always `none`.""" + + +class McpOauthTokenEndpointAuthResourceClientSecretBasic(BaseModel): + """Sends the client ID and secret using HTTP Basic authentication.""" + + type: Literal["client_secret_basic"] + """The type of the object. Always `client_secret_basic`.""" + + +class McpOauthTokenEndpointAuthResourceClientSecretPost(BaseModel): + """Sends the client ID and secret in the token request body.""" + + type: Literal["client_secret_post"] + """The type of the object. Always `client_secret_post`.""" + + +McpOauthTokenEndpointAuth: TypeAlias = Annotated[ + Union[ + McpOauthTokenEndpointAuthResourceNone, + McpOauthTokenEndpointAuthResourceClientSecretBasic, + McpOauthTokenEndpointAuthResourceClientSecretPost, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_create_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_create_param.py new file mode 100644 index 0000000..ef1218e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_create_param.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "McpOauthTokenEndpointAuthCreateParam", + "CreateMcpOauthTokenEndpointAuthParamNone", + "CreateMcpOauthTokenEndpointAuthParamClientSecretBasic", + "CreateMcpOauthTokenEndpointAuthParamClientSecretPost", +] + + +class CreateMcpOauthTokenEndpointAuthParamNone(TypedDict, total=False): + """Sends the client ID without a client secret.""" + + type: Required[Literal["none"]] + """The type of the object. Always `none`.""" + + +class CreateMcpOauthTokenEndpointAuthParamClientSecretBasic(TypedDict, total=False): + """Sends the client ID and secret using HTTP Basic authentication.""" + + client_secret: Required[str] + """The OAuth client secret to store. Never returned in credential resources.""" + + type: Required[Literal["client_secret_basic"]] + """The type of the object. Always `client_secret_basic`.""" + + +class CreateMcpOauthTokenEndpointAuthParamClientSecretPost(TypedDict, total=False): + """Sends the client ID and secret in the token request body.""" + + client_secret: Required[str] + """The OAuth client secret to store. Never returned in credential resources.""" + + type: Required[Literal["client_secret_post"]] + """The type of the object. Always `client_secret_post`.""" + + +McpOauthTokenEndpointAuthCreateParam: TypeAlias = Union[ + CreateMcpOauthTokenEndpointAuthParamNone, + CreateMcpOauthTokenEndpointAuthParamClientSecretBasic, + CreateMcpOauthTokenEndpointAuthParamClientSecretPost, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_rotate_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_rotate_param.py new file mode 100644 index 0000000..35eb3d9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_rotate_param.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "McpOauthTokenEndpointAuthRotateParam", + "RotateMcpOauthTokenEndpointAuthParamClientSecretBasic", + "RotateMcpOauthTokenEndpointAuthParamClientSecretPost", +] + + +class RotateMcpOauthTokenEndpointAuthParamClientSecretBasic(TypedDict, total=False): + """Updates credentials sent using HTTP Basic authentication.""" + + type: Required[Literal["client_secret_basic"]] + """The type of the object. Always `client_secret_basic`.""" + + client_secret: Optional[str] + """The replacement OAuth client secret. + + Omit or pass `null` to keep the stored secret. This secret is never returned in + resources. + """ + + +class RotateMcpOauthTokenEndpointAuthParamClientSecretPost(TypedDict, total=False): + """Updates credentials sent in the token request body.""" + + type: Required[Literal["client_secret_post"]] + """The type of the object. Always `client_secret_post`.""" + + client_secret: Optional[str] + """The replacement OAuth client secret. + + Omit or pass `null` to keep the stored secret. This secret is never returned in + resources. + """ + + +McpOauthTokenEndpointAuthRotateParam: TypeAlias = Union[ + RotateMcpOauthTokenEndpointAuthParamClientSecretBasic, RotateMcpOauthTokenEndpointAuthParamClientSecretPost +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant.py new file mode 100644 index 0000000..de28df1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant.py @@ -0,0 +1,141 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .assistant_tool import AssistantTool +from ..shared.metadata import Metadata +from .assistant_response_format_option import AssistantResponseFormatOption + +__all__ = ["Assistant", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] + + +class ToolResourcesCodeInterpreter(BaseModel): + file_ids: Optional[List[str]] = None + """ + A list of [file](https://developers.openai.com/api/reference/resources/files) + IDs made available to the `code_interpreter`` tool. There can be a maximum of 20 + files associated with the tool. + """ + + +class ToolResourcesFileSearch(BaseModel): + vector_store_ids: Optional[List[str]] = None + """ + The ID of the + [vector store](https://developers.openai.com/api/reference/resources/vector_stores) + attached to this assistant. There can be a maximum of 1 vector store attached to + the assistant. + """ + + +class ToolResources(BaseModel): + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + + code_interpreter: Optional[ToolResourcesCodeInterpreter] = None + + file_search: Optional[ToolResourcesFileSearch] = None + + +class Assistant(BaseModel): + """Represents an `assistant` that can call the model and use tools.""" + + id: str + """The identifier, which can be referenced in API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the assistant was created.""" + + description: Optional[str] = None + """The description of the assistant. The maximum length is 512 characters.""" + + instructions: Optional[str] = None + """The system instructions that the assistant uses. + + The maximum length is 256,000 characters. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: str + """ID of the model to use. + + You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + """ + + name: Optional[str] = None + """The name of the assistant. The maximum length is 256 characters.""" + + object: Literal["assistant"] + """The object type, which is always `assistant`.""" + + tools: List[AssistantTool] + """A list of tool enabled on the assistant. + + There can be a maximum of 128 tools per assistant. Tools can be of types + `code_interpreter`, `file_search`, or `function`. + """ + + response_format: Optional[AssistantResponseFormatOption] = None + """Specifies the format that the model must output. + + Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + """ + + temperature: Optional[float] = None + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + """ + + tool_resources: Optional[ToolResources] = None + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ + + top_p: Optional[float] = None + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_create_params.py new file mode 100644 index 0000000..7e86357 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_create_params.py @@ -0,0 +1,226 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from ..shared.chat_model import ChatModel +from .assistant_tool_param import AssistantToolParam +from ..shared_params.metadata import Metadata +from ..shared.reasoning_effort import ReasoningEffort +from .assistant_response_format_option_param import AssistantResponseFormatOptionParam + +__all__ = [ + "AssistantCreateParams", + "ToolResources", + "ToolResourcesCodeInterpreter", + "ToolResourcesFileSearch", + "ToolResourcesFileSearchVectorStore", + "ToolResourcesFileSearchVectorStoreChunkingStrategy", + "ToolResourcesFileSearchVectorStoreChunkingStrategyAuto", + "ToolResourcesFileSearchVectorStoreChunkingStrategyStatic", + "ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic", +] + + +class AssistantCreateParams(TypedDict, total=False): + model: Required[Union[str, ChatModel]] + """ID of the model to use. + + You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + """ + + description: Optional[str] + """The description of the assistant. The maximum length is 512 characters.""" + + instructions: Optional[str] + """The system instructions that the assistant uses. + + The maximum length is 256,000 characters. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: Optional[str] + """The name of the assistant. The maximum length is 256 characters.""" + + reasoning_effort: Optional[ReasoningEffort] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + response_format: Optional[AssistantResponseFormatOptionParam] + """Specifies the format that the model must output. + + Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + """ + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + """ + + tool_resources: Optional[ToolResources] + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ + + tools: Iterable[AssistantToolParam] + """A list of tool enabled on the assistant. + + There can be a maximum of 128 tools per assistant. Tools can be of types + `code_interpreter`, `file_search`, or `function`. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + """ + + +class ToolResourcesCodeInterpreter(TypedDict, total=False): + file_ids: SequenceNotStr[str] + """ + A list of [file](https://developers.openai.com/api/reference/resources/files) + IDs made available to the `code_interpreter` tool. There can be a maximum of 20 + files associated with the tool. + """ + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): + """The default strategy. + + This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`. + """ + + type: Required[Literal["auto"]] + """Always `auto`.""" + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total=False): + static: Required[ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic] + + type: Required[Literal["static"]] + """Always `static`.""" + + +ToolResourcesFileSearchVectorStoreChunkingStrategy: TypeAlias = Union[ + ToolResourcesFileSearchVectorStoreChunkingStrategyAuto, ToolResourcesFileSearchVectorStoreChunkingStrategyStatic +] + + +class ToolResourcesFileSearchVectorStore(TypedDict, total=False): + chunking_strategy: ToolResourcesFileSearchVectorStoreChunkingStrategy + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. + """ + + file_ids: SequenceNotStr[str] + """ + A list of [file](https://developers.openai.com/api/reference/resources/files) + IDs to add to the vector store. For vector stores created before Nov 2025, there + can be a maximum of 10,000 files in a vector store. For vector stores created + starting in Nov 2025, the limit is 100,000,000 files. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + +class ToolResourcesFileSearch(TypedDict, total=False): + vector_store_ids: SequenceNotStr[str] + """ + The + [vector store](https://developers.openai.com/api/reference/resources/vector_stores) + attached to this assistant. There can be a maximum of 1 vector store attached to + the assistant. + """ + + vector_stores: Iterable[ToolResourcesFileSearchVectorStore] + """ + A helper to create a + [vector store](https://developers.openai.com/api/reference/resources/vector_stores) + with file_ids and attach it to this assistant. There can be a maximum of 1 + vector store attached to the assistant. + """ + + +class ToolResources(TypedDict, total=False): + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + + code_interpreter: ToolResourcesCodeInterpreter + + file_search: ToolResourcesFileSearch diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_deleted.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_deleted.py new file mode 100644 index 0000000..4792650 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_deleted.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AssistantDeleted"] + + +class AssistantDeleted(BaseModel): + id: str + + deleted: bool + + object: Literal["assistant.deleted"] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_list_params.py new file mode 100644 index 0000000..26f5191 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_list_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["AssistantListParams"] + + +class AssistantListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, starting with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_response_format_option.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_response_format_option.py new file mode 100644 index 0000000..da20311 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_response_format_option.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from ..shared.response_format_text import ResponseFormatText +from ..shared.response_format_json_object import ResponseFormatJSONObject +from ..shared.response_format_json_schema import ResponseFormatJSONSchema + +__all__ = ["AssistantResponseFormatOption"] + +AssistantResponseFormatOption: TypeAlias = Union[ + Literal["auto"], ResponseFormatText, ResponseFormatJSONObject, ResponseFormatJSONSchema +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_response_format_option_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_response_format_option_param.py new file mode 100644 index 0000000..c2b2c16 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_response_format_option_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from ..shared_params.response_format_text import ResponseFormatText +from ..shared_params.response_format_json_object import ResponseFormatJSONObject +from ..shared_params.response_format_json_schema import ResponseFormatJSONSchema + +__all__ = ["AssistantResponseFormatOptionParam"] + +AssistantResponseFormatOptionParam: TypeAlias = Union[ + Literal["auto"], ResponseFormatText, ResponseFormatJSONObject, ResponseFormatJSONSchema +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_stream_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_stream_event.py new file mode 100644 index 0000000..94a8a15 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_stream_event.py @@ -0,0 +1,390 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from .thread import Thread +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .threads.run import Run +from .threads.message import Message +from ..shared.error_object import ErrorObject +from .threads.runs.run_step import RunStep +from .threads.message_delta_event import MessageDeltaEvent +from .threads.runs.run_step_delta_event import RunStepDeltaEvent + +__all__ = [ + "AssistantStreamEvent", + "ThreadCreated", + "ThreadRunCreated", + "ThreadRunQueued", + "ThreadRunInProgress", + "ThreadRunRequiresAction", + "ThreadRunCompleted", + "ThreadRunIncomplete", + "ThreadRunFailed", + "ThreadRunCancelling", + "ThreadRunCancelled", + "ThreadRunExpired", + "ThreadRunStepCreated", + "ThreadRunStepInProgress", + "ThreadRunStepDelta", + "ThreadRunStepCompleted", + "ThreadRunStepFailed", + "ThreadRunStepCancelled", + "ThreadRunStepExpired", + "ThreadMessageCreated", + "ThreadMessageInProgress", + "ThreadMessageDelta", + "ThreadMessageCompleted", + "ThreadMessageIncomplete", + "ErrorEvent", +] + + +class ThreadCreated(BaseModel): + """ + Occurs when a new [thread](https://developers.openai.com/api/docs/assistants/migration) is created. + """ + + data: Thread + """ + Represents a thread that contains + [messages](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.created"] + + enabled: Optional[bool] = None + """Whether to enable input audio transcription.""" + + +class ThreadRunCreated(BaseModel): + """ + Occurs when a new [run](https://developers.openai.com/api/docs/assistants/migration) is created. + """ + + data: Run + """ + Represents an execution run on a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.run.created"] + + +class ThreadRunQueued(BaseModel): + """ + Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `queued` status. + """ + + data: Run + """ + Represents an execution run on a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.run.queued"] + + +class ThreadRunInProgress(BaseModel): + """ + Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` status. + """ + + data: Run + """ + Represents an execution run on a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.run.in_progress"] + + +class ThreadRunRequiresAction(BaseModel): + """ + Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `requires_action` status. + """ + + data: Run + """ + Represents an execution run on a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.run.requires_action"] + + +class ThreadRunCompleted(BaseModel): + """ + Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) is completed. + """ + + data: Run + """ + Represents an execution run on a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.run.completed"] + + +class ThreadRunIncomplete(BaseModel): + """ + Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) ends with status `incomplete`. + """ + + data: Run + """ + Represents an execution run on a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.run.incomplete"] + + +class ThreadRunFailed(BaseModel): + """ + Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) fails. + """ + + data: Run + """ + Represents an execution run on a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.run.failed"] + + +class ThreadRunCancelling(BaseModel): + """ + Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `cancelling` status. + """ + + data: Run + """ + Represents an execution run on a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.run.cancelling"] + + +class ThreadRunCancelled(BaseModel): + """ + Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) is cancelled. + """ + + data: Run + """ + Represents an execution run on a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.run.cancelled"] + + +class ThreadRunExpired(BaseModel): + """ + Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) expires. + """ + + data: Run + """ + Represents an execution run on a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.run.expired"] + + +class ThreadRunStepCreated(BaseModel): + """ + Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is created. + """ + + data: RunStep + """Represents a step in execution of a run.""" + + event: Literal["thread.run.step.created"] + + +class ThreadRunStepInProgress(BaseModel): + """ + Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` state. + """ + + data: RunStep + """Represents a step in execution of a run.""" + + event: Literal["thread.run.step.in_progress"] + + +class ThreadRunStepDelta(BaseModel): + """ + Occurs when parts of a [run step](https://developers.openai.com/api/docs/assistants/migration) are being streamed. + """ + + data: RunStepDeltaEvent + """Represents a run step delta i.e. + + any changed fields on a run step during streaming. + """ + + event: Literal["thread.run.step.delta"] + + +class ThreadRunStepCompleted(BaseModel): + """ + Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is completed. + """ + + data: RunStep + """Represents a step in execution of a run.""" + + event: Literal["thread.run.step.completed"] + + +class ThreadRunStepFailed(BaseModel): + """ + Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) fails. + """ + + data: RunStep + """Represents a step in execution of a run.""" + + event: Literal["thread.run.step.failed"] + + +class ThreadRunStepCancelled(BaseModel): + """ + Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is cancelled. + """ + + data: RunStep + """Represents a step in execution of a run.""" + + event: Literal["thread.run.step.cancelled"] + + +class ThreadRunStepExpired(BaseModel): + """ + Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) expires. + """ + + data: RunStep + """Represents a step in execution of a run.""" + + event: Literal["thread.run.step.expired"] + + +class ThreadMessageCreated(BaseModel): + """ + Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) is created. + """ + + data: Message + """ + Represents a message within a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.message.created"] + + +class ThreadMessageInProgress(BaseModel): + """ + Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` state. + """ + + data: Message + """ + Represents a message within a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.message.in_progress"] + + +class ThreadMessageDelta(BaseModel): + """ + Occurs when parts of a [Message](https://developers.openai.com/api/docs/assistants/migration) are being streamed. + """ + + data: MessageDeltaEvent + """Represents a message delta i.e. + + any changed fields on a message during streaming. + """ + + event: Literal["thread.message.delta"] + + +class ThreadMessageCompleted(BaseModel): + """ + Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) is completed. + """ + + data: Message + """ + Represents a message within a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.message.completed"] + + +class ThreadMessageIncomplete(BaseModel): + """ + Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) ends before it is completed. + """ + + data: Message + """ + Represents a message within a + [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + event: Literal["thread.message.incomplete"] + + +class ErrorEvent(BaseModel): + """ + Occurs when an [error](https://developers.openai.com/api/docs/guides/error-codes#api-errors) occurs. This can happen due to an internal server error or a timeout. + """ + + data: ErrorObject + + event: Literal["error"] + + +AssistantStreamEvent: TypeAlias = Annotated[ + Union[ + ThreadCreated, + ThreadRunCreated, + ThreadRunQueued, + ThreadRunInProgress, + ThreadRunRequiresAction, + ThreadRunCompleted, + ThreadRunIncomplete, + ThreadRunFailed, + ThreadRunCancelling, + ThreadRunCancelled, + ThreadRunExpired, + ThreadRunStepCreated, + ThreadRunStepInProgress, + ThreadRunStepDelta, + ThreadRunStepCompleted, + ThreadRunStepFailed, + ThreadRunStepCancelled, + ThreadRunStepExpired, + ThreadMessageCreated, + ThreadMessageInProgress, + ThreadMessageDelta, + ThreadMessageCompleted, + ThreadMessageIncomplete, + ErrorEvent, + ], + PropertyInfo(discriminator="event"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool.py new file mode 100644 index 0000000..22890a1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .function_tool import FunctionTool +from .file_search_tool import FileSearchTool +from .code_interpreter_tool import CodeInterpreterTool + +__all__ = ["AssistantTool"] + +AssistantTool: TypeAlias = Annotated[ + Union[CodeInterpreterTool, FileSearchTool, FunctionTool], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice.py new file mode 100644 index 0000000..7a0f289 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .assistant_tool_choice_function import AssistantToolChoiceFunction + +__all__ = ["AssistantToolChoice"] + + +class AssistantToolChoice(BaseModel): + """Specifies a tool the model should use. + + Use to force the model to call a specific tool. + """ + + type: Literal["function", "code_interpreter", "file_search"] + """The type of the tool. If type is `function`, the function name must be set""" + + function: Optional[AssistantToolChoiceFunction] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_function.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_function.py new file mode 100644 index 0000000..5210345 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_function.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["AssistantToolChoiceFunction"] + + +class AssistantToolChoiceFunction(BaseModel): + name: str + """The name of the function to call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_function_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_function_param.py new file mode 100644 index 0000000..51d11fa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_function_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["AssistantToolChoiceFunctionParam"] + + +class AssistantToolChoiceFunctionParam(TypedDict, total=False): + name: Required[str] + """The name of the function to call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_option.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_option.py new file mode 100644 index 0000000..75c3a06 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_option.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from .assistant_tool_choice import AssistantToolChoice + +__all__ = ["AssistantToolChoiceOption"] + +AssistantToolChoiceOption: TypeAlias = Union[Literal["none", "auto", "required"], AssistantToolChoice] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_option_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_option_param.py new file mode 100644 index 0000000..f0542b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_option_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from .assistant_tool_choice_param import AssistantToolChoiceParam + +__all__ = ["AssistantToolChoiceOptionParam"] + +AssistantToolChoiceOptionParam: TypeAlias = Union[Literal["none", "auto", "required"], AssistantToolChoiceParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_param.py new file mode 100644 index 0000000..28cd3fa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_choice_param.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .assistant_tool_choice_function_param import AssistantToolChoiceFunctionParam + +__all__ = ["AssistantToolChoiceParam"] + + +class AssistantToolChoiceParam(TypedDict, total=False): + """Specifies a tool the model should use. + + Use to force the model to call a specific tool. + """ + + type: Required[Literal["function", "code_interpreter", "file_search"]] + """The type of the tool. If type is `function`, the function name must be set""" + + function: AssistantToolChoiceFunctionParam diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_param.py new file mode 100644 index 0000000..039123e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_tool_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .function_tool_param import FunctionToolParam +from .file_search_tool_param import FileSearchToolParam +from .code_interpreter_tool_param import CodeInterpreterToolParam + +__all__ = ["AssistantToolParam"] + +AssistantToolParam: TypeAlias = Union[CodeInterpreterToolParam, FileSearchToolParam, FunctionToolParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/assistant_update_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_update_params.py new file mode 100644 index 0000000..2b28c10 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/assistant_update_params.py @@ -0,0 +1,191 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, TypedDict + +from ..._types import SequenceNotStr +from .assistant_tool_param import AssistantToolParam +from ..shared_params.metadata import Metadata +from ..shared.reasoning_effort import ReasoningEffort +from .assistant_response_format_option_param import AssistantResponseFormatOptionParam + +__all__ = ["AssistantUpdateParams", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] + + +class AssistantUpdateParams(TypedDict, total=False): + description: Optional[str] + """The description of the assistant. The maximum length is 512 characters.""" + + instructions: Optional[str] + """The system instructions that the assistant uses. + + The maximum length is 256,000 characters. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Union[ + str, + Literal[ + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4.5-preview", + "gpt-4.5-preview-2025-02-27", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + ] + """ID of the model to use. + + You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + """ + + name: Optional[str] + """The name of the assistant. The maximum length is 256 characters.""" + + reasoning_effort: Optional[ReasoningEffort] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + response_format: Optional[AssistantResponseFormatOptionParam] + """Specifies the format that the model must output. + + Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + """ + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + """ + + tool_resources: Optional[ToolResources] + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ + + tools: Iterable[AssistantToolParam] + """A list of tool enabled on the assistant. + + There can be a maximum of 128 tools per assistant. Tools can be of types + `code_interpreter`, `file_search`, or `function`. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + """ + + +class ToolResourcesCodeInterpreter(TypedDict, total=False): + file_ids: SequenceNotStr[str] + """ + Overrides the list of + [file](https://developers.openai.com/api/reference/resources/files) IDs made + available to the `code_interpreter` tool. There can be a maximum of 20 files + associated with the tool. + """ + + +class ToolResourcesFileSearch(TypedDict, total=False): + vector_store_ids: SequenceNotStr[str] + """ + Overrides the + [vector store](https://developers.openai.com/api/reference/resources/vector_stores) + attached to this assistant. There can be a maximum of 1 vector store attached to + the assistant. + """ + + +class ToolResources(TypedDict, total=False): + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + + code_interpreter: ToolResourcesCodeInterpreter + + file_search: ToolResourcesFileSearch diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_apply_patch_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_apply_patch_tool.py new file mode 100644 index 0000000..a9bf206 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_apply_patch_tool.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaApplyPatchTool"] + + +class BetaApplyPatchTool(BaseModel): + """Allows the assistant to create, delete, or update files using unified diffs.""" + + type: Literal["apply_patch"] + """The type of the tool. Always `apply_patch`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_apply_patch_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_apply_patch_tool_param.py new file mode 100644 index 0000000..569bc3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_apply_patch_tool_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaApplyPatchToolParam"] + + +class BetaApplyPatchToolParam(TypedDict, total=False): + """Allows the assistant to create, delete, or update files using unified diffs.""" + + type: Required[Literal["apply_patch"]] + """The type of the tool. Always `apply_patch`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_compacted_response.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_compacted_response.py new file mode 100644 index 0000000..fc83301 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_compacted_response.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_usage import BetaResponseUsage +from .beta_response_output_item import BetaResponseOutputItem + +__all__ = ["BetaCompactedResponse"] + + +class BetaCompactedResponse(BaseModel): + id: str + """The unique identifier for the compacted response.""" + + created_at: int + """Unix timestamp (in seconds) when the compacted conversation was created.""" + + object: Literal["response.compaction"] + """The object type. Always `response.compaction`.""" + + output: List[BetaResponseOutputItem] + """The compacted list of output items. + + This is a list of all user messages, followed by a single compaction item. + """ + + usage: BetaResponseUsage + """ + Token accounting for the compaction pass, including cached, reasoning, and total + tokens. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_action.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_action.py new file mode 100644 index 0000000..7ca06f5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_action.py @@ -0,0 +1,196 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "BetaComputerAction", + "Click", + "DoubleClick", + "Drag", + "DragPath", + "Keypress", + "Move", + "Screenshot", + "Scroll", + "Type", + "Wait", +] + + +class Click(BaseModel): + """A click action.""" + + button: Literal["left", "right", "wheel", "back", "forward"] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Literal["click"] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: int + """The x-coordinate where the click occurred.""" + + y: int + """The y-coordinate where the click occurred.""" + + keys: Optional[List[str]] = None + """The keys being held while clicking.""" + + +class DoubleClick(BaseModel): + """A double click action.""" + + keys: Optional[List[str]] = None + """The keys being held while double-clicking.""" + + type: Literal["double_click"] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: int + """The x-coordinate where the double click occurred.""" + + y: int + """The y-coordinate where the double click occurred.""" + + +class DragPath(BaseModel): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: int + """The x-coordinate.""" + + y: int + """The y-coordinate.""" + + +class Drag(BaseModel): + """A drag action.""" + + path: List[DragPath] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Literal["drag"] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + keys: Optional[List[str]] = None + """The keys being held while dragging the mouse.""" + + +class Keypress(BaseModel): + """A collection of keypresses the model would like to perform.""" + + keys: List[str] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Literal["keypress"] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class Move(BaseModel): + """A mouse move action.""" + + type: Literal["move"] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: int + """The x-coordinate to move to.""" + + y: int + """The y-coordinate to move to.""" + + keys: Optional[List[str]] = None + """The keys being held while moving the mouse.""" + + +class Screenshot(BaseModel): + """A screenshot action.""" + + type: Literal["screenshot"] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class Scroll(BaseModel): + """A scroll action.""" + + scroll_x: int + """The horizontal scroll distance.""" + + scroll_y: int + """The vertical scroll distance.""" + + type: Literal["scroll"] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: int + """The x-coordinate where the scroll occurred.""" + + y: int + """The y-coordinate where the scroll occurred.""" + + keys: Optional[List[str]] = None + """The keys being held while scrolling.""" + + +class Type(BaseModel): + """An action to type in text.""" + + text: str + """The text to type.""" + + type: Literal["type"] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class Wait(BaseModel): + """A wait action.""" + + type: Literal["wait"] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +BetaComputerAction: TypeAlias = Annotated[ + Union[Click, DoubleClick, Drag, Keypress, Move, Screenshot, Scroll, Type, Wait], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_action_list.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_action_list.py new file mode 100644 index 0000000..7522aad --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_action_list.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .beta_computer_action import BetaComputerAction + +__all__ = ["BetaComputerActionList"] + +BetaComputerActionList: TypeAlias = List[BetaComputerAction] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_action_list_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_action_list_param.py new file mode 100644 index 0000000..9914a30 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_action_list_param.py @@ -0,0 +1,198 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "BetaComputerActionListParam", + "BetaComputerActionParam", + "Click", + "DoubleClick", + "Drag", + "DragPath", + "Keypress", + "Move", + "Screenshot", + "Scroll", + "Type", + "Wait", +] + + +class Click(TypedDict, total=False): + """A click action.""" + + button: Required[Literal["left", "right", "wheel", "back", "forward"]] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Required[Literal["click"]] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: Required[int] + """The x-coordinate where the click occurred.""" + + y: Required[int] + """The y-coordinate where the click occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while clicking.""" + + +class DoubleClick(TypedDict, total=False): + """A double click action.""" + + keys: Required[Optional[SequenceNotStr[str]]] + """The keys being held while double-clicking.""" + + type: Required[Literal["double_click"]] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: Required[int] + """The x-coordinate where the double click occurred.""" + + y: Required[int] + """The y-coordinate where the double click occurred.""" + + +class DragPath(TypedDict, total=False): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: Required[int] + """The x-coordinate.""" + + y: Required[int] + """The y-coordinate.""" + + +class Drag(TypedDict, total=False): + """A drag action.""" + + path: Required[Iterable[DragPath]] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Required[Literal["drag"]] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + keys: Optional[SequenceNotStr[str]] + """The keys being held while dragging the mouse.""" + + +class Keypress(TypedDict, total=False): + """A collection of keypresses the model would like to perform.""" + + keys: Required[SequenceNotStr[str]] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Required[Literal["keypress"]] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class Move(TypedDict, total=False): + """A mouse move action.""" + + type: Required[Literal["move"]] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: Required[int] + """The x-coordinate to move to.""" + + y: Required[int] + """The y-coordinate to move to.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while moving the mouse.""" + + +class Screenshot(TypedDict, total=False): + """A screenshot action.""" + + type: Required[Literal["screenshot"]] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class Scroll(TypedDict, total=False): + """A scroll action.""" + + scroll_x: Required[int] + """The horizontal scroll distance.""" + + scroll_y: Required[int] + """The vertical scroll distance.""" + + type: Required[Literal["scroll"]] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: Required[int] + """The x-coordinate where the scroll occurred.""" + + y: Required[int] + """The y-coordinate where the scroll occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while scrolling.""" + + +class Type(TypedDict, total=False): + """An action to type in text.""" + + text: Required[str] + """The text to type.""" + + type: Required[Literal["type"]] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class Wait(TypedDict, total=False): + """A wait action.""" + + type: Required[Literal["wait"]] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +BetaComputerActionParam: TypeAlias = Union[Click, DoubleClick, Drag, Keypress, Move, Screenshot, Scroll, Type, Wait] + +BetaComputerActionListParam: TypeAlias = List[BetaComputerActionParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_action_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_action_param.py new file mode 100644 index 0000000..c502e7b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_action_param.py @@ -0,0 +1,195 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "BetaComputerActionParam", + "Click", + "DoubleClick", + "Drag", + "DragPath", + "Keypress", + "Move", + "Screenshot", + "Scroll", + "Type", + "Wait", +] + + +class Click(TypedDict, total=False): + """A click action.""" + + button: Required[Literal["left", "right", "wheel", "back", "forward"]] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Required[Literal["click"]] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: Required[int] + """The x-coordinate where the click occurred.""" + + y: Required[int] + """The y-coordinate where the click occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while clicking.""" + + +class DoubleClick(TypedDict, total=False): + """A double click action.""" + + keys: Required[Optional[SequenceNotStr[str]]] + """The keys being held while double-clicking.""" + + type: Required[Literal["double_click"]] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: Required[int] + """The x-coordinate where the double click occurred.""" + + y: Required[int] + """The y-coordinate where the double click occurred.""" + + +class DragPath(TypedDict, total=False): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: Required[int] + """The x-coordinate.""" + + y: Required[int] + """The y-coordinate.""" + + +class Drag(TypedDict, total=False): + """A drag action.""" + + path: Required[Iterable[DragPath]] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Required[Literal["drag"]] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + keys: Optional[SequenceNotStr[str]] + """The keys being held while dragging the mouse.""" + + +class Keypress(TypedDict, total=False): + """A collection of keypresses the model would like to perform.""" + + keys: Required[SequenceNotStr[str]] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Required[Literal["keypress"]] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class Move(TypedDict, total=False): + """A mouse move action.""" + + type: Required[Literal["move"]] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: Required[int] + """The x-coordinate to move to.""" + + y: Required[int] + """The y-coordinate to move to.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while moving the mouse.""" + + +class Screenshot(TypedDict, total=False): + """A screenshot action.""" + + type: Required[Literal["screenshot"]] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class Scroll(TypedDict, total=False): + """A scroll action.""" + + scroll_x: Required[int] + """The horizontal scroll distance.""" + + scroll_y: Required[int] + """The vertical scroll distance.""" + + type: Required[Literal["scroll"]] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: Required[int] + """The x-coordinate where the scroll occurred.""" + + y: Required[int] + """The y-coordinate where the scroll occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while scrolling.""" + + +class Type(TypedDict, total=False): + """An action to type in text.""" + + text: Required[str] + """The text to type.""" + + type: Required[Literal["type"]] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class Wait(TypedDict, total=False): + """A wait action.""" + + type: Required[Literal["wait"]] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +BetaComputerActionParam: TypeAlias = Union[Click, DoubleClick, Drag, Keypress, Move, Screenshot, Scroll, Type, Wait] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_tool.py new file mode 100644 index 0000000..b8566fc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_tool.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaComputerTool"] + + +class BetaComputerTool(BaseModel): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use). + """ + + type: Literal["computer"] + """The type of the computer tool. Always `computer`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_tool_param.py new file mode 100644 index 0000000..2ae661f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_tool_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaComputerToolParam"] + + +class BetaComputerToolParam(TypedDict, total=False): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use). + """ + + type: Required[Literal["computer"]] + """The type of the computer tool. Always `computer`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_use_preview_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_use_preview_tool.py new file mode 100644 index 0000000..5ea08d2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_use_preview_tool.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaComputerUsePreviewTool"] + + +class BetaComputerUsePreviewTool(BaseModel): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use). + """ + + display_height: int + """The height of the computer display.""" + + display_width: int + """The width of the computer display.""" + + environment: Literal["windows", "mac", "linux", "ubuntu", "browser"] + """The type of computer environment to control.""" + + type: Literal["computer_use_preview"] + """The type of the computer use tool. Always `computer_use_preview`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_use_preview_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_use_preview_tool_param.py new file mode 100644 index 0000000..f99ac13 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_computer_use_preview_tool_param.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaComputerUsePreviewToolParam"] + + +class BetaComputerUsePreviewToolParam(TypedDict, total=False): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use). + """ + + display_height: Required[int] + """The height of the computer display.""" + + display_width: Required[int] + """The width of the computer display.""" + + environment: Required[Literal["windows", "mac", "linux", "ubuntu", "browser"]] + """The type of computer environment to control.""" + + type: Required[Literal["computer_use_preview"]] + """The type of the computer use tool. Always `computer_use_preview`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_auto.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_auto.py new file mode 100644 index 0000000..ec4cc29 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_auto.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_inline_skill import BetaInlineSkill +from .beta_skill_reference import BetaSkillReference +from .beta_container_network_policy_disabled import BetaContainerNetworkPolicyDisabled +from .beta_container_network_policy_allowlist import BetaContainerNetworkPolicyAllowlist + +__all__ = ["BetaContainerAuto", "NetworkPolicy", "Skill"] + +NetworkPolicy: TypeAlias = Annotated[ + Union[BetaContainerNetworkPolicyDisabled, BetaContainerNetworkPolicyAllowlist], PropertyInfo(discriminator="type") +] + +Skill: TypeAlias = Annotated[Union[BetaSkillReference, BetaInlineSkill], PropertyInfo(discriminator="type")] + + +class BetaContainerAuto(BaseModel): + type: Literal["container_auto"] + """Automatically creates a container for this request""" + + file_ids: Optional[List[str]] = None + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit for the container.""" + + network_policy: Optional[NetworkPolicy] = None + """Network access policy for the container.""" + + skills: Optional[List[Skill]] = None + """An optional list of skills referenced by id or inline data.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_auto_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_auto_param.py new file mode 100644 index 0000000..b1961eb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_auto_param.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .beta_inline_skill_param import BetaInlineSkillParam +from .beta_skill_reference_param import BetaSkillReferenceParam +from .beta_container_network_policy_disabled_param import BetaContainerNetworkPolicyDisabledParam +from .beta_container_network_policy_allowlist_param import BetaContainerNetworkPolicyAllowlistParam + +__all__ = ["BetaContainerAutoParam", "NetworkPolicy", "Skill"] + +NetworkPolicy: TypeAlias = Union[BetaContainerNetworkPolicyDisabledParam, BetaContainerNetworkPolicyAllowlistParam] + +Skill: TypeAlias = Union[BetaSkillReferenceParam, BetaInlineSkillParam] + + +class BetaContainerAutoParam(TypedDict, total=False): + type: Required[Literal["container_auto"]] + """Automatically creates a container for this request""" + + file_ids: SequenceNotStr[str] + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] + """The memory limit for the container.""" + + network_policy: NetworkPolicy + """Network access policy for the container.""" + + skills: Iterable[Skill] + """An optional list of skills referenced by id or inline data.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_allowlist.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_allowlist.py new file mode 100644 index 0000000..dafdc57 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_allowlist.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_container_network_policy_domain_secret import BetaContainerNetworkPolicyDomainSecret + +__all__ = ["BetaContainerNetworkPolicyAllowlist"] + + +class BetaContainerNetworkPolicyAllowlist(BaseModel): + allowed_domains: List[str] + """A list of allowed domains when type is `allowlist`.""" + + type: Literal["allowlist"] + """Allow outbound network access only to specified domains. Always `allowlist`.""" + + domain_secrets: Optional[List[BetaContainerNetworkPolicyDomainSecret]] = None + """Optional domain-scoped secrets for allowlisted domains.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_allowlist_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_allowlist_param.py new file mode 100644 index 0000000..7ce0985 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_allowlist_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr +from .beta_container_network_policy_domain_secret_param import BetaContainerNetworkPolicyDomainSecretParam + +__all__ = ["BetaContainerNetworkPolicyAllowlistParam"] + + +class BetaContainerNetworkPolicyAllowlistParam(TypedDict, total=False): + allowed_domains: Required[SequenceNotStr[str]] + """A list of allowed domains when type is `allowlist`.""" + + type: Required[Literal["allowlist"]] + """Allow outbound network access only to specified domains. Always `allowlist`.""" + + domain_secrets: Iterable[BetaContainerNetworkPolicyDomainSecretParam] + """Optional domain-scoped secrets for allowlisted domains.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_disabled.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_disabled.py new file mode 100644 index 0000000..e5571f1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_disabled.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaContainerNetworkPolicyDisabled"] + + +class BetaContainerNetworkPolicyDisabled(BaseModel): + type: Literal["disabled"] + """Disable outbound network access. Always `disabled`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_disabled_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_disabled_param.py new file mode 100644 index 0000000..7fe8221 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_disabled_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaContainerNetworkPolicyDisabledParam"] + + +class BetaContainerNetworkPolicyDisabledParam(TypedDict, total=False): + type: Required[Literal["disabled"]] + """Disable outbound network access. Always `disabled`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_domain_secret.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_domain_secret.py new file mode 100644 index 0000000..bcbc9df --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_domain_secret.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["BetaContainerNetworkPolicyDomainSecret"] + + +class BetaContainerNetworkPolicyDomainSecret(BaseModel): + domain: str + """The domain associated with the secret.""" + + name: str + """The name of the secret to inject for the domain.""" + + value: str + """The secret value to inject for the domain.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_domain_secret_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_domain_secret_param.py new file mode 100644 index 0000000..83defdb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_network_policy_domain_secret_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["BetaContainerNetworkPolicyDomainSecretParam"] + + +class BetaContainerNetworkPolicyDomainSecretParam(TypedDict, total=False): + domain: Required[str] + """The domain associated with the secret.""" + + name: Required[str] + """The name of the secret to inject for the domain.""" + + value: Required[str] + """The secret value to inject for the domain.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_reference.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_reference.py new file mode 100644 index 0000000..e181985 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_reference.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaContainerReference"] + + +class BetaContainerReference(BaseModel): + container_id: str + """The ID of the referenced container.""" + + type: Literal["container_reference"] + """References a container created with the /v1/containers endpoint""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_reference_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_reference_param.py new file mode 100644 index 0000000..fd7d046 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_container_reference_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaContainerReferenceParam"] + + +class BetaContainerReferenceParam(TypedDict, total=False): + container_id: Required[str] + """The ID of the referenced container.""" + + type: Required[Literal["container_reference"]] + """References a container created with the /v1/containers endpoint""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_custom_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_custom_tool.py new file mode 100644 index 0000000..5521d24 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_custom_tool.py @@ -0,0 +1,65 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["BetaCustomTool", "Format", "FormatText", "FormatGrammar"] + + +class FormatText(BaseModel): + """Unconstrained free-form text.""" + + type: Literal["text"] + """Unconstrained text format. Always `text`.""" + + +class FormatGrammar(BaseModel): + """A grammar defined by the user.""" + + definition: str + """The grammar definition.""" + + syntax: Literal["lark", "regex"] + """The syntax of the grammar definition. One of `lark` or `regex`.""" + + type: Literal["grammar"] + """Grammar format. Always `grammar`.""" + + +Format: TypeAlias = Annotated[Union[FormatText, FormatGrammar], PropertyInfo(discriminator="type")] + + +class BetaCustomTool(BaseModel): + """A custom tool that processes input using a specified format. + + Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) + """ + + name: str + """The name of the custom tool, used to identify it in tool calls.""" + + type: Literal["custom"] + """The type of the custom tool. Always `custom`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + async_: Optional[bool] = FieldInfo(alias="async", default=None) + """ + Whether the tool response can be returned asynchronously versus immediately + returned on next response creation. + """ + + defer_loading: Optional[bool] = None + """Whether this tool should be deferred and discovered via tool search.""" + + description: Optional[str] = None + """Optional description of the custom tool, used to provide more context.""" + + format: Optional[Format] = None + """The input format for the custom tool. Default is unconstrained text.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_custom_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_custom_tool_param.py new file mode 100644 index 0000000..e375511 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_custom_tool_param.py @@ -0,0 +1,65 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["BetaCustomToolParam", "Format", "FormatText", "FormatGrammar"] + + +class FormatText(TypedDict, total=False): + """Unconstrained free-form text.""" + + type: Required[Literal["text"]] + """Unconstrained text format. Always `text`.""" + + +class FormatGrammar(TypedDict, total=False): + """A grammar defined by the user.""" + + definition: Required[str] + """The grammar definition.""" + + syntax: Required[Literal["lark", "regex"]] + """The syntax of the grammar definition. One of `lark` or `regex`.""" + + type: Required[Literal["grammar"]] + """Grammar format. Always `grammar`.""" + + +Format: TypeAlias = Union[FormatText, FormatGrammar] + + +_BetaCustomToolParamReservedKeywords = TypedDict( + "_BetaCustomToolParamReservedKeywords", + { + "async": bool, + }, + total=False, +) + + +class BetaCustomToolParam(_BetaCustomToolParamReservedKeywords, total=False): + """A custom tool that processes input using a specified format. + + Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) + """ + + name: Required[str] + """The name of the custom tool, used to identify it in tool calls.""" + + type: Required[Literal["custom"]] + """The type of the custom tool. Always `custom`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + defer_loading: bool + """Whether this tool should be deferred and discovered via tool search.""" + + description: str + """Optional description of the custom tool, used to provide more context.""" + + format: Format + """The input format for the custom tool. Default is unconstrained text.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_easy_input_message.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_easy_input_message.py new file mode 100644 index 0000000..095351a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_easy_input_message.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_input_message_content_list import BetaResponseInputMessageContentList + +__all__ = ["BetaEasyInputMessage"] + + +class BetaEasyInputMessage(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: Union[str, BetaResponseInputMessageContentList] + """ + Text, image, or audio input to the model, used to generate a response. Can also + contain previous assistant responses. + """ + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + phase: Optional[Literal["commentary", "final_answer"]] = None + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_easy_input_message_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_easy_input_message_param.py new file mode 100644 index 0000000..d4ab24b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_easy_input_message_param.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypedDict + +from .beta_response_input_message_content_list_param import BetaResponseInputMessageContentListParam + +__all__ = ["BetaEasyInputMessageParam"] + + +class BetaEasyInputMessageParam(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: Required[Union[str, BetaResponseInputMessageContentListParam]] + """ + Text, image, or audio input to the model, used to generate a response. Can also + contain previous assistant responses. + """ + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + phase: Optional[Literal["commentary", "final_answer"]] + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_file_search_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_file_search_tool.py new file mode 100644 index 0000000..fe3ccd5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_file_search_tool.py @@ -0,0 +1,153 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel + +__all__ = [ + "BetaFileSearchTool", + "Filters", + "FiltersComparisonFilter", + "FiltersCompoundFilter", + "FiltersCompoundFilterFilter", + "FiltersCompoundFilterFilterComparisonFilter", + "RankingOptions", + "RankingOptionsHybridSearch", +] + + +class FiltersComparisonFilter(BaseModel): + """ + A filter used to compare a specified attribute key to a given value using a defined comparison operation. + """ + + key: str + """The key to compare against the value.""" + + type: Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"] + """ + Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, + `nin`. + + - `eq`: equals + - `ne`: not equal + - `gt`: greater than + - `gte`: greater than or equal + - `lt`: less than + - `lte`: less than or equal + - `in`: in + - `nin`: not in + """ + + value: Union[str, float, bool, List[Union[str, float]]] + """ + The value to compare against the attribute key; supports string, number, or + boolean types. + """ + + +class FiltersCompoundFilterFilterComparisonFilter(BaseModel): + """ + A filter used to compare a specified attribute key to a given value using a defined comparison operation. + """ + + key: str + """The key to compare against the value.""" + + type: Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"] + """ + Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, + `nin`. + + - `eq`: equals + - `ne`: not equal + - `gt`: greater than + - `gte`: greater than or equal + - `lt`: less than + - `lte`: less than or equal + - `in`: in + - `nin`: not in + """ + + value: Union[str, float, bool, List[Union[str, float]]] + """ + The value to compare against the attribute key; supports string, number, or + boolean types. + """ + + +FiltersCompoundFilterFilter: TypeAlias = Union[FiltersCompoundFilterFilterComparisonFilter, object] + + +class FiltersCompoundFilter(BaseModel): + """Combine multiple filters using `and` or `or`.""" + + filters: List[FiltersCompoundFilterFilter] + """Array of filters to combine. + + Items can be `ComparisonFilter` or `CompoundFilter`. + """ + + type: Literal["and", "or"] + """Type of operation: `and` or `or`.""" + + +Filters: TypeAlias = Union[FiltersComparisonFilter, FiltersCompoundFilter, None] + + +class RankingOptionsHybridSearch(BaseModel): + """ + Weights that control how reciprocal rank fusion balances semantic embedding matches versus sparse keyword matches when hybrid search is enabled. + """ + + embedding_weight: float + """The weight of the embedding in the reciprocal ranking fusion.""" + + text_weight: float + """The weight of the text in the reciprocal ranking fusion.""" + + +class RankingOptions(BaseModel): + """Ranking options for search.""" + + hybrid_search: Optional[RankingOptionsHybridSearch] = None + """ + Weights that control how reciprocal rank fusion balances semantic embedding + matches versus sparse keyword matches when hybrid search is enabled. + """ + + ranker: Optional[Literal["auto", "default-2024-11-15"]] = None + """The ranker to use for the file search.""" + + score_threshold: Optional[float] = None + """The score threshold for the file search, a number between 0 and 1. + + Numbers closer to 1 will attempt to return only the most relevant results, but + may return fewer results. + """ + + +class BetaFileSearchTool(BaseModel): + """A tool that searches for relevant content from uploaded files. + + Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search). + """ + + type: Literal["file_search"] + """The type of the file search tool. Always `file_search`.""" + + vector_store_ids: List[str] + """The IDs of the vector stores to search.""" + + filters: Optional[Filters] = None + """A filter to apply.""" + + max_num_results: Optional[int] = None + """The maximum number of results to return. + + This number should be between 1 and 50 inclusive. + """ + + ranking_options: Optional[RankingOptions] = None + """Ranking options for search.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_file_search_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_file_search_tool_param.py new file mode 100644 index 0000000..8aa63b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_file_search_tool_param.py @@ -0,0 +1,155 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "BetaFileSearchToolParam", + "Filters", + "FiltersComparisonFilter", + "FiltersCompoundFilter", + "FiltersCompoundFilterFilter", + "FiltersCompoundFilterFilterComparisonFilter", + "RankingOptions", + "RankingOptionsHybridSearch", +] + + +class FiltersComparisonFilter(TypedDict, total=False): + """ + A filter used to compare a specified attribute key to a given value using a defined comparison operation. + """ + + key: Required[str] + """The key to compare against the value.""" + + type: Required[Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"]] + """ + Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, + `nin`. + + - `eq`: equals + - `ne`: not equal + - `gt`: greater than + - `gte`: greater than or equal + - `lt`: less than + - `lte`: less than or equal + - `in`: in + - `nin`: not in + """ + + value: Required[Union[str, float, bool, SequenceNotStr[Union[str, float]]]] + """ + The value to compare against the attribute key; supports string, number, or + boolean types. + """ + + +class FiltersCompoundFilterFilterComparisonFilter(TypedDict, total=False): + """ + A filter used to compare a specified attribute key to a given value using a defined comparison operation. + """ + + key: Required[str] + """The key to compare against the value.""" + + type: Required[Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"]] + """ + Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, + `nin`. + + - `eq`: equals + - `ne`: not equal + - `gt`: greater than + - `gte`: greater than or equal + - `lt`: less than + - `lte`: less than or equal + - `in`: in + - `nin`: not in + """ + + value: Required[Union[str, float, bool, SequenceNotStr[Union[str, float]]]] + """ + The value to compare against the attribute key; supports string, number, or + boolean types. + """ + + +FiltersCompoundFilterFilter: TypeAlias = Union[FiltersCompoundFilterFilterComparisonFilter, object] + + +class FiltersCompoundFilter(TypedDict, total=False): + """Combine multiple filters using `and` or `or`.""" + + filters: Required[Iterable[FiltersCompoundFilterFilter]] + """Array of filters to combine. + + Items can be `ComparisonFilter` or `CompoundFilter`. + """ + + type: Required[Literal["and", "or"]] + """Type of operation: `and` or `or`.""" + + +Filters: TypeAlias = Union[FiltersComparisonFilter, FiltersCompoundFilter] + + +class RankingOptionsHybridSearch(TypedDict, total=False): + """ + Weights that control how reciprocal rank fusion balances semantic embedding matches versus sparse keyword matches when hybrid search is enabled. + """ + + embedding_weight: Required[float] + """The weight of the embedding in the reciprocal ranking fusion.""" + + text_weight: Required[float] + """The weight of the text in the reciprocal ranking fusion.""" + + +class RankingOptions(TypedDict, total=False): + """Ranking options for search.""" + + hybrid_search: RankingOptionsHybridSearch + """ + Weights that control how reciprocal rank fusion balances semantic embedding + matches versus sparse keyword matches when hybrid search is enabled. + """ + + ranker: Literal["auto", "default-2024-11-15"] + """The ranker to use for the file search.""" + + score_threshold: float + """The score threshold for the file search, a number between 0 and 1. + + Numbers closer to 1 will attempt to return only the most relevant results, but + may return fewer results. + """ + + +class BetaFileSearchToolParam(TypedDict, total=False): + """A tool that searches for relevant content from uploaded files. + + Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search). + """ + + type: Required[Literal["file_search"]] + """The type of the file search tool. Always `file_search`.""" + + vector_store_ids: Required[SequenceNotStr[str]] + """The IDs of the vector stores to search.""" + + filters: Optional[Filters] + """A filter to apply.""" + + max_num_results: int + """The maximum number of results to return. + + This number should be between 1 and 50 inclusive. + """ + + ranking_options: RankingOptions + """Ranking options for search.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_function_shell_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_function_shell_tool.py new file mode 100644 index 0000000..2f5d958 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_function_shell_tool.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_container_auto import BetaContainerAuto +from .beta_local_environment import BetaLocalEnvironment +from .beta_container_reference import BetaContainerReference + +__all__ = ["BetaFunctionShellTool", "Environment"] + +Environment: TypeAlias = Annotated[ + Union[BetaContainerAuto, BetaLocalEnvironment, BetaContainerReference, None], PropertyInfo(discriminator="type") +] + + +class BetaFunctionShellTool(BaseModel): + """A tool that allows the model to execute shell commands.""" + + type: Literal["shell"] + """The type of the shell tool. Always `shell`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + environment: Optional[Environment] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_function_shell_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_function_shell_tool_param.py new file mode 100644 index 0000000..5879d42 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_function_shell_tool_param.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .beta_container_auto_param import BetaContainerAutoParam +from .beta_local_environment_param import BetaLocalEnvironmentParam +from .beta_container_reference_param import BetaContainerReferenceParam + +__all__ = ["BetaFunctionShellToolParam", "Environment"] + +Environment: TypeAlias = Union[BetaContainerAutoParam, BetaLocalEnvironmentParam, BetaContainerReferenceParam] + + +class BetaFunctionShellToolParam(TypedDict, total=False): + """A tool that allows the model to execute shell commands.""" + + type: Required[Literal["shell"]] + """The type of the shell tool. Always `shell`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + environment: Optional[Environment] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_function_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_function_tool.py new file mode 100644 index 0000000..8238e31 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_function_tool.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["BetaFunctionTool"] + + +class BetaFunctionTool(BaseModel): + """Defines a function in your own code the model can choose to call. + + Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling). + """ + + name: str + """The name of the function to call.""" + + parameters: Optional[Dict[str, object]] = None + """A JSON schema object describing the parameters of the function.""" + + strict: Optional[bool] = None + """Whether strict parameter validation is enforced for this function tool.""" + + type: Literal["function"] + """The type of the function tool. Always `function`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + async_: Optional[bool] = FieldInfo(alias="async", default=None) + + defer_loading: Optional[bool] = None + """Whether this function is deferred and loaded via tool search.""" + + description: Optional[str] = None + """A description of the function. + + Used by the model to determine whether or not to call the function. + """ + + output_schema: Optional[Dict[str, object]] = None + """ + A JSON schema object describing the JSON value encoded in string outputs for + this function. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_function_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_function_tool_param.py new file mode 100644 index 0000000..7853575 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_function_tool_param.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaFunctionToolParam"] + +_BetaFunctionToolParamReservedKeywords = TypedDict( + "_BetaFunctionToolParamReservedKeywords", + { + "async": bool, + }, + total=False, +) + + +class BetaFunctionToolParam(_BetaFunctionToolParamReservedKeywords, total=False): + """Defines a function in your own code the model can choose to call. + + Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling). + """ + + name: Required[str] + """The name of the function to call.""" + + parameters: Required[Optional[Dict[str, object]]] + """A JSON schema object describing the parameters of the function.""" + + strict: Required[Optional[bool]] + """Whether strict parameter validation is enforced for this function tool.""" + + type: Required[Literal["function"]] + """The type of the function tool. Always `function`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + defer_loading: bool + """Whether this function is deferred and loaded via tool search.""" + + description: Optional[str] + """A description of the function. + + Used by the model to determine whether or not to call the function. + """ + + output_schema: Optional[Dict[str, object]] + """ + A JSON schema object describing the JSON value encoded in string outputs for + this function. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_image_detail.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_image_detail.py new file mode 100644 index 0000000..6402b09 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_image_detail.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["BetaImageDetail"] + +BetaImageDetail: TypeAlias = Literal["low", "high", "auto", "original"] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_inline_skill.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_inline_skill.py new file mode 100644 index 0000000..e5e6378 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_inline_skill.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_inline_skill_source import BetaInlineSkillSource + +__all__ = ["BetaInlineSkill"] + + +class BetaInlineSkill(BaseModel): + description: str + """The description of the skill.""" + + name: str + """The name of the skill.""" + + source: BetaInlineSkillSource + """Inline skill payload""" + + type: Literal["inline"] + """Defines an inline skill for this request.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_inline_skill_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_inline_skill_param.py new file mode 100644 index 0000000..60f5d2e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_inline_skill_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .beta_inline_skill_source_param import BetaInlineSkillSourceParam + +__all__ = ["BetaInlineSkillParam"] + + +class BetaInlineSkillParam(TypedDict, total=False): + description: Required[str] + """The description of the skill.""" + + name: Required[str] + """The name of the skill.""" + + source: Required[BetaInlineSkillSourceParam] + """Inline skill payload""" + + type: Required[Literal["inline"]] + """Defines an inline skill for this request.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_inline_skill_source.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_inline_skill_source.py new file mode 100644 index 0000000..283c29c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_inline_skill_source.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaInlineSkillSource"] + + +class BetaInlineSkillSource(BaseModel): + """Inline skill payload""" + + data: str + """Base64-encoded skill zip bundle.""" + + media_type: Literal["application/zip"] + """The media type of the inline skill payload. Must be `application/zip`.""" + + type: Literal["base64"] + """The type of the inline skill source. Must be `base64`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_inline_skill_source_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_inline_skill_source_param.py new file mode 100644 index 0000000..d34abde --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_inline_skill_source_param.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaInlineSkillSourceParam"] + + +class BetaInlineSkillSourceParam(TypedDict, total=False): + """Inline skill payload""" + + data: Required[str] + """Base64-encoded skill zip bundle.""" + + media_type: Required[Literal["application/zip"]] + """The media type of the inline skill payload. Must be `application/zip`.""" + + type: Required[Literal["base64"]] + """The type of the inline skill source. Must be `base64`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_local_environment.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_local_environment.py new file mode 100644 index 0000000..3686133 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_local_environment.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_local_skill import BetaLocalSkill + +__all__ = ["BetaLocalEnvironment"] + + +class BetaLocalEnvironment(BaseModel): + type: Literal["local"] + """Use a local computer environment.""" + + skills: Optional[List[BetaLocalSkill]] = None + """An optional list of skills.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_local_environment_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_local_environment_param.py new file mode 100644 index 0000000..1955051 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_local_environment_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +from .beta_local_skill_param import BetaLocalSkillParam + +__all__ = ["BetaLocalEnvironmentParam"] + + +class BetaLocalEnvironmentParam(TypedDict, total=False): + type: Required[Literal["local"]] + """Use a local computer environment.""" + + skills: Iterable[BetaLocalSkillParam] + """An optional list of skills.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_local_skill.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_local_skill.py new file mode 100644 index 0000000..b1db823 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_local_skill.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["BetaLocalSkill"] + + +class BetaLocalSkill(BaseModel): + description: str + """The description of the skill.""" + + name: str + """The name of the skill.""" + + path: str + """The path to the directory containing the skill.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_local_skill_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_local_skill_param.py new file mode 100644 index 0000000..4c405d8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_local_skill_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["BetaLocalSkillParam"] + + +class BetaLocalSkillParam(TypedDict, total=False): + description: Required[str] + """The description of the skill.""" + + name: Required[str] + """The name of the skill.""" + + path: Required[str] + """The path to the directory containing the skill.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_mcp_tool_call_error.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_mcp_tool_call_error.py new file mode 100644 index 0000000..a24fc13 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_mcp_tool_call_error.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["BetaMcpToolCallError", "McpProtocolError", "McpToolExecutionError", "HTTPError"] + + +class McpProtocolError(BaseModel): + code: int + + message: str + + type: Literal["mcp_protocol_error"] + + +class McpToolExecutionError(BaseModel): + content: object + + type: Literal["mcp_tool_execution_error"] + + +class HTTPError(BaseModel): + code: int + + message: str + + type: Literal["http_error"] + + +BetaMcpToolCallError: TypeAlias = Annotated[ + Union[McpProtocolError, McpToolExecutionError, HTTPError], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_mcp_tool_call_error_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_mcp_tool_call_error_param.py new file mode 100644 index 0000000..a81608e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_mcp_tool_call_error_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["BetaMcpToolCallErrorParam", "McpProtocolError", "McpToolExecutionError", "HTTPError"] + + +class McpProtocolError(TypedDict, total=False): + code: Required[int] + + message: Required[str] + + type: Required[Literal["mcp_protocol_error"]] + + +class McpToolExecutionError(TypedDict, total=False): + content: Required[object] + + type: Required[Literal["mcp_tool_execution_error"]] + + +class HTTPError(TypedDict, total=False): + code: Required[int] + + message: Required[str] + + type: Required[Literal["http_error"]] + + +BetaMcpToolCallErrorParam: TypeAlias = Union[McpProtocolError, McpToolExecutionError, HTTPError] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_namespace_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_namespace_tool.py new file mode 100644 index 0000000..0df50d1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_namespace_tool.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_custom_tool import BetaCustomTool + +__all__ = ["BetaNamespaceTool", "Tool", "ToolFunction"] + + +class ToolFunction(BaseModel): + name: str + + type: Literal["function"] + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + async_: Optional[bool] = FieldInfo(alias="async", default=None) + """ + Whether the tool response can be returned asynchronously versus immediately + returned on next response creation. + """ + + defer_loading: Optional[bool] = None + """Whether this function should be deferred and discovered via tool search.""" + + description: Optional[str] = None + + output_schema: Optional[Dict[str, object]] = None + """ + A JSON Schema describing the JSON value encoded in string outputs for this + function tool. This does not describe content-array outputs. + """ + + parameters: Optional[object] = None + + strict: Optional[bool] = None + """Whether to enforce strict parameter validation. + + If omitted, Responses attempts to use strict validation when the schema is + compatible, and falls back to non-strict validation otherwise. + """ + + +Tool: TypeAlias = Annotated[Union[ToolFunction, BetaCustomTool], PropertyInfo(discriminator="type")] + + +class BetaNamespaceTool(BaseModel): + """Groups function/custom tools under a shared namespace.""" + + description: str + """A description of the namespace shown to the model.""" + + name: str + """The namespace name used in tool calls (for example, `crm`).""" + + tools: List[Tool] + """The function/custom tools available inside this namespace.""" + + type: Literal["namespace"] + """The type of the tool. Always `namespace`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_namespace_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_namespace_tool_param.py new file mode 100644 index 0000000..c351cbd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_namespace_tool_param.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .beta_custom_tool_param import BetaCustomToolParam + +__all__ = ["BetaNamespaceToolParam", "Tool", "ToolFunction"] + +_ToolFunctionReservedKeywords = TypedDict( + "_ToolFunctionReservedKeywords", + { + "async": bool, + }, + total=False, +) + + +class ToolFunction(_ToolFunctionReservedKeywords, total=False): + name: Required[str] + + type: Required[Literal["function"]] + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + defer_loading: bool + """Whether this function should be deferred and discovered via tool search.""" + + description: Optional[str] + + output_schema: Optional[Dict[str, object]] + """ + A JSON Schema describing the JSON value encoded in string outputs for this + function tool. This does not describe content-array outputs. + """ + + parameters: Optional[object] + + strict: Optional[bool] + """Whether to enforce strict parameter validation. + + If omitted, Responses attempts to use strict validation when the schema is + compatible, and falls back to non-strict validation otherwise. + """ + + +Tool: TypeAlias = Union[ToolFunction, BetaCustomToolParam] + + +class BetaNamespaceToolParam(TypedDict, total=False): + """Groups function/custom tools under a shared namespace.""" + + description: Required[str] + """A description of the namespace shown to the model.""" + + name: Required[str] + """The namespace name used in tool calls (for example, `crm`).""" + + tools: Required[Iterable[Tool]] + """The function/custom tools available inside this namespace.""" + + type: Required[Literal["namespace"]] + """The type of the tool. Always `namespace`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response.py new file mode 100644 index 0000000..cd2514f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response.py @@ -0,0 +1,715 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_tool import BetaTool +from .beta_service_tier import BetaServiceTier +from .beta_response_error import BetaResponseError +from .beta_response_usage import BetaResponseUsage +from .beta_response_prompt import BetaResponsePrompt +from .beta_response_status import BetaResponseStatus +from .beta_tool_choice_mcp import BetaToolChoiceMcp +from .beta_tool_choice_shell import BetaToolChoiceShell +from .beta_tool_choice_types import BetaToolChoiceTypes +from .beta_tool_choice_custom import BetaToolChoiceCustom +from .beta_response_input_item import BetaResponseInputItem +from .beta_tool_choice_allowed import BetaToolChoiceAllowed +from .beta_tool_choice_options import BetaToolChoiceOptions +from .beta_response_output_item import BetaResponseOutputItem +from .beta_response_text_config import BetaResponseTextConfig +from .beta_tool_choice_function import BetaToolChoiceFunction +from .beta_tool_choice_apply_patch import BetaToolChoiceApplyPatch + +__all__ = [ + "BetaResponse", + "AccessPrograms", + "IncompleteDetails", + "ToolChoice", + "ToolChoiceBetaSpecificProgrammaticToolCallingParam", + "Conversation", + "Moderation", + "ModerationInput", + "ModerationInputModerationResult", + "ModerationInputError", + "ModerationOutput", + "ModerationOutputModerationResult", + "ModerationOutputError", + "PromptCacheDiagnostics", + "PromptCacheDiagnosticsCacheMiss", + "PromptCacheDiagnosticsCacheHit", + "PromptCacheDiagnosticsComparisonResponseNotFound", + "PromptCacheDiagnosticsUnavailable", + "PromptCacheOptions", + "Reasoning", +] + + +class AccessPrograms(BaseModel): + cyber: Literal["standard", "daybreak_blue", "daybreak_red"] + """The effective Cyber access program used for this response.""" + + +class IncompleteDetails(BaseModel): + """Details about why the response is incomplete.""" + + reason: Optional[Literal["max_output_tokens", "max_messages", "content_filter", "steered"]] = None + """The reason why the response is incomplete. + + `steered` means the response stopped at a safe output boundary after a WebSocket + `response.steer` event. The server can then create a successor response + automatically with the queued input. + """ + + +class ToolChoiceBetaSpecificProgrammaticToolCallingParam(BaseModel): + type: Literal["programmatic_tool_calling"] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ToolChoice: TypeAlias = Union[ + BetaToolChoiceOptions, + BetaToolChoiceAllowed, + BetaToolChoiceTypes, + BetaToolChoiceFunction, + BetaToolChoiceMcp, + BetaToolChoiceCustom, + ToolChoiceBetaSpecificProgrammaticToolCallingParam, + BetaToolChoiceApplyPatch, + BetaToolChoiceShell, +] + + +class Conversation(BaseModel): + """The conversation that this response belonged to. + + Input items and output items from this response were automatically added to this conversation. + """ + + id: str + """The unique ID of the conversation that this response was associated with.""" + + +class ModerationInputModerationResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationInputError(BaseModel): + """An error produced while attempting moderation for the response input or output.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which was always `error` for moderation failures.""" + + +ModerationInput: TypeAlias = Annotated[ + Union[ModerationInputModerationResult, ModerationInputError], PropertyInfo(discriminator="type") +] + + +class ModerationOutputModerationResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationOutputError(BaseModel): + """An error produced while attempting moderation for the response input or output.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which was always `error` for moderation failures.""" + + +ModerationOutput: TypeAlias = Annotated[ + Union[ModerationOutputModerationResult, ModerationOutputError], PropertyInfo(discriminator="type") +] + + +class Moderation(BaseModel): + """ + Moderation results for the response input and output, if moderated completions were requested. + """ + + input: ModerationInput + """Moderation for the response input.""" + + output: ModerationOutput + """Moderation for the response output.""" + + +class PromptCacheDiagnosticsCacheMiss(BaseModel): + cache_missed_tokens: int + """ + The estimated number of input tokens affected after the first detected + divergence. + """ + + reason: Literal[ + "model_changed", + "prompt_cache_key_changed", + "tools_changed", + "text_format_changed", + "reasoning_effort_changed", + "verbosity_changed", + "context_compacted", + "input_changed", + "service_tier_changed", + ] + """The reason prompt cache reuse did not occur.""" + + type: Literal["cache_miss"] + + comparison_reusable_tokens: Optional[int] = None + """The raw token count of the reusable prefix in the compared response.""" + + +class PromptCacheDiagnosticsCacheHit(BaseModel): + type: Literal["cache_hit"] + + +class PromptCacheDiagnosticsComparisonResponseNotFound(BaseModel): + type: Literal["comparison_response_not_found"] + + +class PromptCacheDiagnosticsUnavailable(BaseModel): + type: Literal["unavailable"] + + +PromptCacheDiagnostics: TypeAlias = Annotated[ + Union[ + PromptCacheDiagnosticsCacheMiss, + PromptCacheDiagnosticsCacheHit, + PromptCacheDiagnosticsComparisonResponseNotFound, + PromptCacheDiagnosticsUnavailable, + ], + PropertyInfo(discriminator="type"), +] + + +class PromptCacheOptions(BaseModel): + """The prompt-caching options that were applied to the response. + + Supported for `gpt-5.6` and later models. + """ + + mode: Literal["implicit", "explicit"] + """Whether implicit prompt-cache breakpoints were enabled.""" + + ttl: Literal["30m"] + """The minimum lifetime applied to each cache breakpoint.""" + + comparison_response_id: Optional[str] = None + """The response ID supplied as the prompt cache diagnostics comparison.""" + + +class Reasoning(BaseModel): + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + context: Optional[Literal["auto", "current_turn", "all_turns"]] = None + """ + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + generate_summary: Optional[Literal["auto", "concise", "detailed"]] = None + """**Deprecated:** use `summary` instead. + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + mode: Union[str, Literal["standard", "pro"], None] = None + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] = None + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. + + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. + """ + + +class BetaResponse(BaseModel): + id: str + """Unique identifier for this Response.""" + + access_programs: Optional[AccessPrograms] = None + + created_at: float + """Unix timestamp (in seconds) of when this Response was created.""" + + error: Optional[BetaResponseError] = None + """An error object returned when the model fails to generate a Response.""" + + incomplete_details: Optional[IncompleteDetails] = None + """Details about why the response is incomplete.""" + + instructions: Union[str, List[BetaResponseInputItem], None] = None + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + metadata: Optional[Dict[str, str]] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + ] + """Model ID used to generate the response, like `gpt-6-astra`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + object: Literal["response"] + """The object type of this resource - always set to `response`.""" + + output: List[BetaResponseOutputItem] + """An array of content items generated by the model. + + - The length and order of items in the `output` array is dependent on the + model's response. + - Rather than accessing the first item in the `output` array and assuming it's + an `assistant` message with the content generated by the model, you might + consider using the `output_text` property where supported in SDKs. + """ + + parallel_tool_calls: bool + """Whether to allow the model to run tool calls in parallel.""" + + temperature: Optional[float] = None + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + tool_choice: ToolChoice + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: List[BetaTool] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_p: Optional[float] = None + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + background: Optional[bool] = None + """ + Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + """ + + completed_at: Optional[float] = None + """ + Unix timestamp (in seconds) of when this Response was completed. Only present + when the status is `completed`. + """ + + conversation: Optional[Conversation] = None + """The conversation that this response belonged to. + + Input items and output items from this response were automatically added to this + conversation. + """ + + max_output_tokens: Optional[int] = None + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] = None + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + moderation: Optional[Moderation] = None + """ + Moderation results for the response input and output, if moderated completions + were requested. + """ + + previous_response_id: Optional[str] = None + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[BetaResponsePrompt] = None + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + prompt_cache_diagnostics: Optional[PromptCacheDiagnostics] = None + """Prompt cache diagnostics requested for this response.""" + + prompt_cache_key: Optional[str] = None + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + """ + + prompt_cache_options: Optional[PromptCacheOptions] = None + """The prompt-caching options that were applied to the response. + + Supported for `gpt-5.6` and later models. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] = None + """Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + """ + + reasoning: Optional[Reasoning] = None + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + safety_identifier: Optional[str] = None + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + service_tier: Optional[BetaServiceTier] = None + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + status: Optional[BetaResponseStatus] = None + """The status of the response generation. + + One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or + `incomplete`. + """ + + text: Optional[BetaResponseTextConfig] = None + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + top_logprobs: Optional[int] = None + """ + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + """ + + truncation: Optional[Literal["auto", "disabled"]] = None + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + usage: Optional[BetaResponseUsage] = None + """ + Represents token usage details including input tokens, output tokens, a + breakdown of output tokens, and the total tokens used. + """ + + user: Optional[str] = None + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_apply_patch_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_apply_patch_tool_call.py new file mode 100644 index 0000000..ed3fe57 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_apply_patch_tool_call.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "BetaResponseApplyPatchToolCall", + "Operation", + "OperationCreateFile", + "OperationDeleteFile", + "OperationUpdateFile", + "Agent", + "Caller", + "CallerDirect", + "CallerProgram", +] + + +class OperationCreateFile(BaseModel): + """Instruction describing how to create a file via the apply_patch tool.""" + + diff: str + """Diff to apply.""" + + path: str + """Path of the file to create.""" + + type: Literal["create_file"] + """Create a new file with the provided diff.""" + + +class OperationDeleteFile(BaseModel): + """Instruction describing how to delete a file via the apply_patch tool.""" + + path: str + """Path of the file to delete.""" + + type: Literal["delete_file"] + """Delete the specified file.""" + + +class OperationUpdateFile(BaseModel): + """Instruction describing how to update a file via the apply_patch tool.""" + + diff: str + """Diff to apply.""" + + path: str + """Path of the file to update.""" + + type: Literal["update_file"] + """Update an existing file with the provided diff.""" + + +Operation: TypeAlias = Annotated[ + Union[OperationCreateFile, OperationDeleteFile, OperationUpdateFile], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseApplyPatchToolCall(BaseModel): + """A tool call that applies file diffs by creating, deleting, or updating files.""" + + id: str + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + operation: Operation + """ + One of the create_file, delete_file, or update_file operations applied via + apply_patch. + """ + + status: Literal["in_progress", "completed"] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Literal["apply_patch_call"] + """The type of the item. Always `apply_patch_call`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The ID of the entity that created this tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_apply_patch_tool_call_output.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_apply_patch_tool_call_output.py new file mode 100644 index 0000000..2c7acdd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_apply_patch_tool_call_output.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["BetaResponseApplyPatchToolCallOutput", "Agent", "Caller", "CallerDirect", "CallerProgram"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseApplyPatchToolCallOutput(BaseModel): + """The output emitted by an apply patch tool call.""" + + id: str + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + status: Literal["completed", "failed"] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Literal["apply_patch_call_output"] + """The type of the item. Always `apply_patch_call_output`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The ID of the entity that created this tool call output.""" + + output: Optional[str] = None + """Optional textual output returned by the apply patch tool.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_audio_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_audio_delta_event.py new file mode 100644 index 0000000..f08e6f2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_audio_delta_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseAudioDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseAudioDeltaEvent(BaseModel): + """Emitted when there is a partial audio response.""" + + delta: str + """A chunk of Base64 encoded response audio bytes.""" + + sequence_number: int + """A sequence number for this chunk of the stream response.""" + + type: Literal["response.audio.delta"] + """The type of the event. Always `response.audio.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_audio_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_audio_done_event.py new file mode 100644 index 0000000..b1a5a39 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_audio_done_event.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseAudioDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseAudioDoneEvent(BaseModel): + """Emitted when the audio response is complete.""" + + sequence_number: int + """The sequence number of the delta.""" + + type: Literal["response.audio.done"] + """The type of the event. Always `response.audio.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_audio_transcript_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_audio_transcript_delta_event.py new file mode 100644 index 0000000..788617f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_audio_transcript_delta_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseAudioTranscriptDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseAudioTranscriptDeltaEvent(BaseModel): + """Emitted when there is a partial transcript of audio.""" + + delta: str + """The partial transcript of the audio response.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.audio.transcript.delta"] + """The type of the event. Always `response.audio.transcript.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_audio_transcript_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_audio_transcript_done_event.py new file mode 100644 index 0000000..12b4f51 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_audio_transcript_done_event.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseAudioTranscriptDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseAudioTranscriptDoneEvent(BaseModel): + """Emitted when the full audio transcript is completed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.audio.transcript.done"] + """The type of the event. Always `response.audio.transcript.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_code_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_code_delta_event.py new file mode 100644 index 0000000..64fc57b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_code_delta_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCodeInterpreterCallCodeDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterCallCodeDeltaEvent(BaseModel): + """Emitted when a partial code snippet is streamed by the code interpreter.""" + + delta: str + """The partial code snippet being streamed by the code interpreter.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """ + The index of the output item in the response for which the code is being + streamed. + """ + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call_code.delta"] + """The type of the event. Always `response.code_interpreter_call_code.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_code_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_code_done_event.py new file mode 100644 index 0000000..4ffb261 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_code_done_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCodeInterpreterCallCodeDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterCallCodeDoneEvent(BaseModel): + """Emitted when the code snippet is finalized by the code interpreter.""" + + code: str + """The final code snippet output by the code interpreter.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """The index of the output item in the response for which the code is finalized.""" + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call_code.done"] + """The type of the event. Always `response.code_interpreter_call_code.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_completed_event.py new file mode 100644 index 0000000..cb43c5f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_completed_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCodeInterpreterCallCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterCallCompletedEvent(BaseModel): + """Emitted when the code interpreter call is completed.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """ + The index of the output item in the response for which the code interpreter call + is completed. + """ + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call.completed"] + """The type of the event. Always `response.code_interpreter_call.completed`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_in_progress_event.py new file mode 100644 index 0000000..cf7f966 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_in_progress_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCodeInterpreterCallInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterCallInProgressEvent(BaseModel): + """Emitted when a code interpreter call is in progress.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """ + The index of the output item in the response for which the code interpreter call + is in progress. + """ + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call.in_progress"] + """The type of the event. Always `response.code_interpreter_call.in_progress`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_interpreting_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_interpreting_event.py new file mode 100644 index 0000000..1716021 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_call_interpreting_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCodeInterpreterCallInterpretingEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterCallInterpretingEvent(BaseModel): + """Emitted when the code interpreter is actively interpreting the code snippet.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """ + The index of the output item in the response for which the code interpreter is + interpreting code. + """ + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call.interpreting"] + """The type of the event. Always `response.code_interpreter_call.interpreting`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_tool_call.py new file mode 100644 index 0000000..eeb0fc5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_tool_call.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["BetaResponseCodeInterpreterToolCall", "Output", "OutputLogs", "OutputImage", "Agent"] + + +class OutputLogs(BaseModel): + """The logs output from the code interpreter.""" + + logs: str + """The logs output from the code interpreter.""" + + type: Literal["logs"] + """The type of the output. Always `logs`.""" + + +class OutputImage(BaseModel): + """The image output from the code interpreter.""" + + type: Literal["image"] + """The type of the output. Always `image`.""" + + url: str + """The URL of the image output from the code interpreter.""" + + +Output: TypeAlias = Annotated[Union[OutputLogs, OutputImage], PropertyInfo(discriminator="type")] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterToolCall(BaseModel): + """A tool call to run code.""" + + id: str + """The unique ID of the code interpreter tool call.""" + + code: Optional[str] = None + """The code to run, or null if not available.""" + + container_id: str + """The ID of the container used to run the code.""" + + outputs: Optional[List[Output]] = None + """ + The outputs generated by the code interpreter, such as logs or images. Can be + null if no outputs are available. + """ + + status: Literal["in_progress", "completed", "incomplete", "interpreting", "failed"] + """The status of the code interpreter tool call. + + Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and + `failed`. + """ + + type: Literal["code_interpreter_call"] + """The type of the code interpreter tool call. Always `code_interpreter_call`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_tool_call_param.py new file mode 100644 index 0000000..09ff992 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_code_interpreter_tool_call_param.py @@ -0,0 +1,70 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["BetaResponseCodeInterpreterToolCallParam", "Output", "OutputLogs", "OutputImage", "Agent"] + + +class OutputLogs(TypedDict, total=False): + """The logs output from the code interpreter.""" + + logs: Required[str] + """The logs output from the code interpreter.""" + + type: Required[Literal["logs"]] + """The type of the output. Always `logs`.""" + + +class OutputImage(TypedDict, total=False): + """The image output from the code interpreter.""" + + type: Required[Literal["image"]] + """The type of the output. Always `image`.""" + + url: Required[str] + """The URL of the image output from the code interpreter.""" + + +Output: TypeAlias = Union[OutputLogs, OutputImage] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterToolCallParam(TypedDict, total=False): + """A tool call to run code.""" + + id: Required[str] + """The unique ID of the code interpreter tool call.""" + + code: Required[Optional[str]] + """The code to run, or null if not available.""" + + container_id: Required[str] + """The ID of the container used to run the code.""" + + outputs: Required[Optional[Iterable[Output]]] + """ + The outputs generated by the code interpreter, such as logs or images. Can be + null if no outputs are available. + """ + + status: Required[Literal["in_progress", "completed", "incomplete", "interpreting", "failed"]] + """The status of the code interpreter tool call. + + Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and + `failed`. + """ + + type: Required[Literal["code_interpreter_call"]] + """The type of the code interpreter tool call. Always `code_interpreter_call`.""" + + agent: Optional[Agent] + """The agent that produced this item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_compaction_compacting_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_compaction_compacting_event.py new file mode 100644 index 0000000..1307b4e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_compaction_compacting_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCompactionCompactingEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCompactionCompactingEvent(BaseModel): + """Emitted when new summary content is sampled for a compaction trigger. + + Contains no summary content. + """ + + item_id: str + """The ID of the compaction output item.""" + + output_index: int + """The index of the compaction output item.""" + + sequence_number: int + """The sequence number of the event that was emitted.""" + + type: Literal["response.compaction.compacting"] + """The type of the event, always `response.compaction.compacting`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_compaction_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_compaction_item.py new file mode 100644 index 0000000..d6772a8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_compaction_item.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCompactionItem", "Agent"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCompactionItem(BaseModel): + """ + A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact). + """ + + id: str + """The unique ID of the compaction item.""" + + encrypted_content: str + """The encrypted content that was produced by compaction.""" + + type: Literal["compaction"] + """The type of the item. Always `compaction`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_compaction_item_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_compaction_item_param.py new file mode 100644 index 0000000..6c12376 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_compaction_item_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCompactionItemParam", "Agent"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCompactionItemParam(BaseModel): + """ + A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact). + """ + + encrypted_content: str + """The encrypted content of the compaction summary.""" + + type: Literal["compaction"] + """The type of the item. Always `compaction`.""" + + id: Optional[str] = None + """The ID of the compaction item.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_compaction_item_param_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_compaction_item_param_param.py new file mode 100644 index 0000000..beb9d64 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_compaction_item_param_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseCompactionItemParamParam", "Agent"] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCompactionItemParamParam(TypedDict, total=False): + """ + A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact). + """ + + encrypted_content: Required[str] + """The encrypted content of the compaction summary.""" + + type: Required[Literal["compaction"]] + """The type of the item. Always `compaction`.""" + + id: Optional[str] + """The ID of the compaction item.""" + + agent: Optional[Agent] + """The agent that produced this item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_completed_event.py new file mode 100644 index 0000000..b701e54 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_completed_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response import BetaResponse + +__all__ = ["BetaResponseCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCompletedEvent(BaseModel): + """Emitted when the model response is complete.""" + + response: BetaResponse + """Properties of the completed response.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.completed"] + """The type of the event. Always `response.completed`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call.py new file mode 100644 index 0000000..fefce55 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_computer_action import BetaComputerAction +from .beta_computer_action_list import BetaComputerActionList + +__all__ = ["BetaResponseComputerToolCall", "PendingSafetyCheck", "Agent"] + + +class PendingSafetyCheck(BaseModel): + """A pending safety check for the computer call.""" + + id: str + """The ID of the pending safety check.""" + + code: Optional[str] = None + """The type of the pending safety check.""" + + message: Optional[str] = None + """Details about the pending safety check.""" + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseComputerToolCall(BaseModel): + """A tool call to a computer use tool. + + See the + [computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information. + """ + + id: str + """The unique ID of the computer call.""" + + call_id: str + """An identifier used when responding to the tool call with output.""" + + pending_safety_checks: List[PendingSafetyCheck] + """The pending safety checks for the computer call.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["computer_call"] + """The type of the computer call. Always `computer_call`.""" + + action: Optional[BetaComputerAction] = None + """A click action.""" + + actions: Optional[BetaComputerActionList] = None + """Flattened batched actions for `computer_use`. + + Each action includes an `type` discriminator and action-specific fields. + """ + + agent: Optional[Agent] = None + """The agent that produced this item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call_output_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call_output_item.py new file mode 100644 index 0000000..b9e65cd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call_output_item.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_computer_tool_call_output_screenshot import BetaResponseComputerToolCallOutputScreenshot + +__all__ = ["BetaResponseComputerToolCallOutputItem", "AcknowledgedSafetyCheck", "Agent"] + + +class AcknowledgedSafetyCheck(BaseModel): + """A pending safety check for the computer call.""" + + id: str + """The ID of the pending safety check.""" + + code: Optional[str] = None + """The type of the pending safety check.""" + + message: Optional[str] = None + """Details about the pending safety check.""" + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseComputerToolCallOutputItem(BaseModel): + id: str + """The unique ID of the computer call tool output.""" + + call_id: str + """The ID of the computer tool call that produced the output.""" + + output: BetaResponseComputerToolCallOutputScreenshot + """A computer screenshot image used with the computer use tool.""" + + status: Literal["completed", "incomplete", "failed", "in_progress"] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + type: Literal["computer_call_output"] + """The type of the computer tool call output. Always `computer_call_output`.""" + + acknowledged_safety_checks: Optional[List[AcknowledgedSafetyCheck]] = None + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call_output_screenshot.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call_output_screenshot.py new file mode 100644 index 0000000..96bcadd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call_output_screenshot.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseComputerToolCallOutputScreenshot"] + + +class BetaResponseComputerToolCallOutputScreenshot(BaseModel): + """A computer screenshot image used with the computer use tool.""" + + type: Literal["computer_screenshot"] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + file_id: Optional[str] = None + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: Optional[str] = None + """The URL of the screenshot image.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call_output_screenshot_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call_output_screenshot_param.py new file mode 100644 index 0000000..0f852ad --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call_output_screenshot_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseComputerToolCallOutputScreenshotParam"] + + +class BetaResponseComputerToolCallOutputScreenshotParam(TypedDict, total=False): + """A computer screenshot image used with the computer use tool.""" + + type: Required[Literal["computer_screenshot"]] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + file_id: str + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: str + """The URL of the screenshot image.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call_param.py new file mode 100644 index 0000000..d46b234 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_computer_tool_call_param.py @@ -0,0 +1,70 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from .beta_computer_action_param import BetaComputerActionParam +from .beta_computer_action_list_param import BetaComputerActionListParam + +__all__ = ["BetaResponseComputerToolCallParam", "PendingSafetyCheck", "Agent"] + + +class PendingSafetyCheck(TypedDict, total=False): + """A pending safety check for the computer call.""" + + id: Required[str] + """The ID of the pending safety check.""" + + code: Optional[str] + """The type of the pending safety check.""" + + message: Optional[str] + """Details about the pending safety check.""" + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class BetaResponseComputerToolCallParam(TypedDict, total=False): + """A tool call to a computer use tool. + + See the + [computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information. + """ + + id: Required[str] + """The unique ID of the computer call.""" + + call_id: Required[str] + """An identifier used when responding to the tool call with output.""" + + pending_safety_checks: Required[Iterable[PendingSafetyCheck]] + """The pending safety checks for the computer call.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Required[Literal["computer_call"]] + """The type of the computer call. Always `computer_call`.""" + + action: BetaComputerActionParam + """A click action.""" + + actions: BetaComputerActionListParam + """Flattened batched actions for `computer_use`. + + Each action includes an `type` discriminator and action-specific fields. + """ + + agent: Optional[Agent] + """The agent that produced this item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_configuration_update_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_configuration_update_item.py new file mode 100644 index 0000000..a6d1780 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_configuration_update_item.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseConfigurationUpdateItem", "Agent", "Reasoning"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Reasoning(BaseModel): + """The reasoning configuration applied by this update.""" + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] = None + """ + The reasoning effort used for subsequent responses until another configuration + update replaces it. + """ + + +class BetaResponseConfigurationUpdateItem(BaseModel): + """ + A configuration update that applies to subsequent responses until it is + replaced by another configuration update. + """ + + id: str + """The unique ID of the configuration update item.""" + + type: Literal["configuration_update"] + """The item type. Always `configuration_update`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + reasoning: Optional[Reasoning] = None + """The reasoning configuration applied by this update.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_configuration_update_item_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_configuration_update_item_param.py new file mode 100644 index 0000000..36090be --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_configuration_update_item_param.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseConfigurationUpdateItemParam", "Agent", "Reasoning"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Reasoning(BaseModel): + """Updates to reasoning configuration. Only effort is supported.""" + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] = None + """ + The reasoning effort to use for subsequent responses until another configuration + update replaces it. + """ + + +class BetaResponseConfigurationUpdateItemParam(BaseModel): + """An update to the conversation's response configuration. + + The configuration + remains in effect for subsequent responses until it is replaced by another + configuration update. + """ + + type: Literal["configuration_update"] + """The item type. Always `configuration_update`.""" + + id: Optional[str] = None + """The unique ID of the configuration update item.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + reasoning: Optional[Reasoning] = None + """Updates to reasoning configuration. Only effort is supported.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_configuration_update_item_param_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_configuration_update_item_param_param.py new file mode 100644 index 0000000..1c9b060 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_configuration_update_item_param_param.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseConfigurationUpdateItemParamParam", "Agent", "Reasoning"] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class Reasoning(TypedDict, total=False): + """Updates to reasoning configuration. Only effort is supported.""" + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] + """ + The reasoning effort to use for subsequent responses until another configuration + update replaces it. + """ + + +class BetaResponseConfigurationUpdateItemParamParam(TypedDict, total=False): + """An update to the conversation's response configuration. + + The configuration + remains in effect for subsequent responses until it is replaced by another + configuration update. + """ + + type: Required[Literal["configuration_update"]] + """The item type. Always `configuration_update`.""" + + id: Optional[str] + """The unique ID of the configuration update item.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + reasoning: Reasoning + """Updates to reasoning configuration. Only effort is supported.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_container_reference.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_container_reference.py new file mode 100644 index 0000000..cc2a3cb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_container_reference.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseContainerReference"] + + +class BetaResponseContainerReference(BaseModel): + """Represents a container created with /v1/containers.""" + + container_id: str + + type: Literal["container_reference"] + """The environment type. Always `container_reference`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_content_part_added_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_content_part_added_event.py new file mode 100644 index 0000000..5dc7cbb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_content_part_added_event.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_output_text import BetaResponseOutputText +from .beta_response_output_refusal import BetaResponseOutputRefusal + +__all__ = ["BetaResponseContentPartAddedEvent", "Part", "PartReasoningText", "Agent"] + + +class PartReasoningText(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +Part: TypeAlias = Annotated[ + Union[BetaResponseOutputText, BetaResponseOutputRefusal, PartReasoningText], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseContentPartAddedEvent(BaseModel): + """Emitted when a new content part is added.""" + + content_index: int + """The index of the content part that was added.""" + + item_id: str + """The ID of the output item that the content part was added to.""" + + output_index: int + """The index of the output item that the content part was added to.""" + + part: Part + """The content part that was added.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.content_part.added"] + """The type of the event. Always `response.content_part.added`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_content_part_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_content_part_done_event.py new file mode 100644 index 0000000..daf61a0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_content_part_done_event.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_output_text import BetaResponseOutputText +from .beta_response_output_refusal import BetaResponseOutputRefusal + +__all__ = ["BetaResponseContentPartDoneEvent", "Part", "PartReasoningText", "Agent"] + + +class PartReasoningText(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +Part: TypeAlias = Annotated[ + Union[BetaResponseOutputText, BetaResponseOutputRefusal, PartReasoningText], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseContentPartDoneEvent(BaseModel): + """Emitted when a content part is done.""" + + content_index: int + """The index of the content part that is done.""" + + item_id: str + """The ID of the output item that the content part was added to.""" + + output_index: int + """The index of the output item that the content part was added to.""" + + part: Part + """The content part that is done.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.content_part.done"] + """The type of the event. Always `response.content_part.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_conversation_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_conversation_param.py new file mode 100644 index 0000000..6eb6a31 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_conversation_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["BetaResponseConversationParam"] + + +class BetaResponseConversationParam(BaseModel): + """The conversation that this response belongs to.""" + + id: str + """The unique ID of the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_conversation_param_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_conversation_param_param.py new file mode 100644 index 0000000..31c17da --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_conversation_param_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["BetaResponseConversationParamParam"] + + +class BetaResponseConversationParamParam(TypedDict, total=False): + """The conversation that this response belongs to.""" + + id: Required[str] + """The unique ID of the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_created_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_created_event.py new file mode 100644 index 0000000..66da68d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_created_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response import BetaResponse + +__all__ = ["BetaResponseCreatedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCreatedEvent(BaseModel): + """An event that is emitted when a response is created.""" + + response: BetaResponse + """The response that was created.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.created"] + """The type of the event. Always `response.created`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call.py new file mode 100644 index 0000000..3f1d22e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call.py @@ -0,0 +1,63 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["BetaResponseCustomToolCall", "Agent", "Caller", "CallerDirect", "CallerProgram"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseCustomToolCall(BaseModel): + """A call to a custom tool created by the model.""" + + call_id: str + """An identifier used to map this custom tool call to a tool call output.""" + + input: str + """The input for the custom tool call generated by the model.""" + + name: str + """The name of the custom tool being called.""" + + type: Literal["custom_tool_call"] + """The type of the custom tool call. Always `custom_tool_call`.""" + + id: Optional[str] = None + """The unique ID of the custom tool call in the OpenAI platform.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + async_: Optional[bool] = FieldInfo(alias="async", default=None) + """Whether the custom tool call runs asynchronously.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + namespace: Optional[str] = None + """The namespace of the custom tool being called.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_input_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_input_delta_event.py new file mode 100644 index 0000000..b3142e5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_input_delta_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCustomToolCallInputDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCustomToolCallInputDeltaEvent(BaseModel): + """Event representing a delta (partial update) to the input of a custom tool call.""" + + delta: str + """The incremental input data (delta) for the custom tool call.""" + + item_id: str + """Unique identifier for the API item associated with this event.""" + + output_index: int + """The index of the output this delta applies to.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.custom_tool_call_input.delta"] + """The event type identifier.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_input_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_input_done_event.py new file mode 100644 index 0000000..7ff33bb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_input_done_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCustomToolCallInputDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCustomToolCallInputDoneEvent(BaseModel): + """Event indicating that input for a custom tool call is complete.""" + + input: str + """The complete input data for the custom tool call.""" + + item_id: str + """Unique identifier for the API item associated with this event.""" + + output_index: int + """The index of the output this event applies to.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.custom_tool_call_input.done"] + """The event type identifier.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_item.py new file mode 100644 index 0000000..9509ee3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_item.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .beta_response_custom_tool_call import BetaResponseCustomToolCall + +__all__ = ["BetaResponseCustomToolCallItem"] + + +class BetaResponseCustomToolCallItem(BetaResponseCustomToolCall): + """A call to a custom tool created by the model.""" + + id: str # type: ignore + """The unique ID of the custom tool call item.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_output.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_output.py new file mode 100644 index 0000000..119cd48 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_output.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_input_file import BetaResponseInputFile +from .beta_response_input_text import BetaResponseInputText +from .beta_response_input_image import BetaResponseInputImage + +__all__ = [ + "BetaResponseCustomToolCallOutput", + "OutputOutputContentList", + "Agent", + "Caller", + "CallerDirect", + "CallerProgram", +] + +OutputOutputContentList: TypeAlias = Annotated[ + Union[BetaResponseInputText, BetaResponseInputImage, BetaResponseInputFile], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseCustomToolCallOutput(BaseModel): + """The output of a custom tool call from your code, being sent back to the model.""" + + call_id: str + """The call ID, used to map this custom tool call output to a custom tool call.""" + + output: Union[str, List[OutputOutputContentList]] + """ + The output from the custom tool call generated by your code. Can be a string or + an list of output content. + """ + + type: Literal["custom_tool_call_output"] + """The type of the custom tool call output. Always `custom_tool_call_output`.""" + + id: Optional[str] = None + """The unique ID of the custom tool call output in the OpenAI platform.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_output_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_output_item.py new file mode 100644 index 0000000..fe1cd96 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_output_item.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .beta_response_custom_tool_call_output import BetaResponseCustomToolCallOutput + +__all__ = ["BetaResponseCustomToolCallOutputItem"] + + +class BetaResponseCustomToolCallOutputItem(BetaResponseCustomToolCallOutput): + """The output of a custom tool call from your code, being sent back to the model.""" + + id: str # type: ignore + """The unique ID of the custom tool call output item.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_output_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_output_param.py new file mode 100644 index 0000000..1b925a7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_output_param.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .beta_response_input_file_param import BetaResponseInputFileParam +from .beta_response_input_text_param import BetaResponseInputTextParam +from .beta_response_input_image_param import BetaResponseInputImageParam + +__all__ = [ + "BetaResponseCustomToolCallOutputParam", + "OutputOutputContentList", + "Agent", + "Caller", + "CallerDirect", + "CallerProgram", +] + +OutputOutputContentList: TypeAlias = Union[ + BetaResponseInputTextParam, BetaResponseInputImageParam, BetaResponseInputFileParam +] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class CallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +Caller: TypeAlias = Union[CallerDirect, CallerProgram] + + +class BetaResponseCustomToolCallOutputParam(TypedDict, total=False): + """The output of a custom tool call from your code, being sent back to the model.""" + + call_id: Required[str] + """The call ID, used to map this custom tool call output to a custom tool call.""" + + output: Required[Union[str, Iterable[OutputOutputContentList]]] + """ + The output from the custom tool call generated by your code. Can be a string or + an list of output content. + """ + + type: Required[Literal["custom_tool_call_output"]] + """The type of the custom tool call output. Always `custom_tool_call_output`.""" + + id: str + """The unique ID of the custom tool call output in the OpenAI platform.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + caller: Optional[Caller] + """The execution context that produced this tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_param.py new file mode 100644 index 0000000..8052d1d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_custom_tool_call_param.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["BetaResponseCustomToolCallParam", "Agent", "Caller", "CallerDirect", "CallerProgram"] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + + +class CallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + + +Caller: TypeAlias = Union[CallerDirect, CallerProgram] + + +_BetaResponseCustomToolCallParamReservedKeywords = TypedDict( + "_BetaResponseCustomToolCallParamReservedKeywords", + { + "async": bool, + }, + total=False, +) + + +class BetaResponseCustomToolCallParam(_BetaResponseCustomToolCallParamReservedKeywords, total=False): + """A call to a custom tool created by the model.""" + + call_id: Required[str] + """An identifier used to map this custom tool call to a tool call output.""" + + input: Required[str] + """The input for the custom tool call generated by the model.""" + + name: Required[str] + """The name of the custom tool being called.""" + + type: Required[Literal["custom_tool_call"]] + """The type of the custom tool call. Always `custom_tool_call`.""" + + id: str + """The unique ID of the custom tool call in the OpenAI platform.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + caller: Optional[Caller] + """The execution context that produced this tool call.""" + + namespace: str + """The namespace of the custom tool being called.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_error.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_error.py new file mode 100644 index 0000000..25974b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_error.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseError", "Misalignment", "MisalignmentSteer"] + + +class MisalignmentSteer(BaseModel): + """An optional public continuation instruction.""" + + message: str + """The public continuation instruction.""" + + +class Misalignment(BaseModel): + detailed_explanation: Optional[str] = None + """The public explanation for this block.""" + + error_type: Union[ + str, + Literal[ + "potentially_unintended_data_transfer", + "potentially_unintended_data_access", + "potentially_unintended_destructive_activity", + "other", + ], + None, + ] = None + """An optional classification; clients must accept additional values.""" + + steer: Optional[MisalignmentSteer] = None + """An optional public continuation instruction.""" + + +class BetaResponseError(BaseModel): + """An error object returned when the model fails to generate a Response.""" + + code: Literal[ + "server_error", + "rate_limit_exceeded", + "invalid_prompt", + "data_residency_mismatch", + "bio_policy", + "misalignment_policy_violation", + "vector_store_timeout", + "invalid_image", + "invalid_image_format", + "invalid_base64_image", + "invalid_image_url", + "image_too_large", + "image_too_small", + "image_parse_error", + "image_content_policy_violation", + "invalid_image_mode", + "image_file_too_large", + "unsupported_image_media_type", + "empty_image_file", + "failed_to_download_image", + "image_file_not_found", + ] + """The error code for the response.""" + + message: str + """A human-readable description of the error.""" + + misalignment: Optional[Misalignment] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_error_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_error_event.py new file mode 100644 index 0000000..6d0b0a7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_error_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseErrorEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseErrorEvent(BaseModel): + """Emitted when an error occurs.""" + + code: Optional[str] = None + """The error code.""" + + message: str + """The error message.""" + + param: Optional[str] = None + """The error parameter.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["error"] + """The type of the event. Always `error`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_failed_event.py new file mode 100644 index 0000000..1ae1a77 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_failed_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response import BetaResponse + +__all__ = ["BetaResponseFailedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFailedEvent(BaseModel): + """An event that is emitted when a response fails.""" + + response: BetaResponse + """The response that failed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.failed"] + """The type of the event. Always `response.failed`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_call_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_call_completed_event.py new file mode 100644 index 0000000..b11587a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_call_completed_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseFileSearchCallCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFileSearchCallCompletedEvent(BaseModel): + """Emitted when a file search call is completed (results found).""" + + item_id: str + """The ID of the output item that the file search call is initiated.""" + + output_index: int + """The index of the output item that the file search call is initiated.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.file_search_call.completed"] + """The type of the event. Always `response.file_search_call.completed`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_call_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_call_in_progress_event.py new file mode 100644 index 0000000..db26f9c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_call_in_progress_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseFileSearchCallInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFileSearchCallInProgressEvent(BaseModel): + """Emitted when a file search call is initiated.""" + + item_id: str + """The ID of the output item that the file search call is initiated.""" + + output_index: int + """The index of the output item that the file search call is initiated.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.file_search_call.in_progress"] + """The type of the event. Always `response.file_search_call.in_progress`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_call_searching_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_call_searching_event.py new file mode 100644 index 0000000..59d721f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_call_searching_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseFileSearchCallSearchingEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFileSearchCallSearchingEvent(BaseModel): + """Emitted when a file search is currently searching.""" + + item_id: str + """The ID of the output item that the file search call is initiated.""" + + output_index: int + """The index of the output item that the file search call is searching.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.file_search_call.searching"] + """The type of the event. Always `response.file_search_call.searching`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_tool_call.py new file mode 100644 index 0000000..baf576c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_tool_call.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseFileSearchToolCall", "Agent", "Result"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Result(BaseModel): + attributes: Optional[Dict[str, Union[str, float, bool]]] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + file_id: Optional[str] = None + """The unique ID of the file.""" + + filename: Optional[str] = None + """The name of the file.""" + + score: Optional[float] = None + """The relevance score of the file - a value between 0 and 1.""" + + text: Optional[str] = None + """The text that was retrieved from the file.""" + + +class BetaResponseFileSearchToolCall(BaseModel): + """The results of a file search tool call. + + See the + [file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information. + """ + + id: str + """The unique ID of the file search tool call.""" + + queries: List[str] + """The queries used to search for files.""" + + status: Literal["in_progress", "searching", "completed", "incomplete", "failed"] + """The status of the file search tool call. + + One of `in_progress`, `searching`, `incomplete` or `failed`, + """ + + type: Literal["file_search_call"] + """The type of the file search tool call. Always `file_search_call`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + results: Optional[List[Result]] = None + """The results of the file search tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_tool_call_param.py new file mode 100644 index 0000000..0c30e63 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_file_search_tool_call_param.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["BetaResponseFileSearchToolCallParam", "Agent", "Result"] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class Result(TypedDict, total=False): + attributes: Optional[Dict[str, Union[str, float, bool]]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + file_id: str + """The unique ID of the file.""" + + filename: str + """The name of the file.""" + + score: float + """The relevance score of the file - a value between 0 and 1.""" + + text: str + """The text that was retrieved from the file.""" + + +class BetaResponseFileSearchToolCallParam(TypedDict, total=False): + """The results of a file search tool call. + + See the + [file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information. + """ + + id: Required[str] + """The unique ID of the file search tool call.""" + + queries: Required[SequenceNotStr[str]] + """The queries used to search for files.""" + + status: Required[Literal["in_progress", "searching", "completed", "incomplete", "failed"]] + """The status of the file search tool call. + + One of `in_progress`, `searching`, `incomplete` or `failed`, + """ + + type: Required[Literal["file_search_call"]] + """The type of the file search tool call. Always `file_search_call`.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + results: Optional[Iterable[Result]] + """The results of the file search tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_format_text_config.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_format_text_config.py new file mode 100644 index 0000000..f5f6f0c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_format_text_config.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_format_text_json_schema_config import BetaResponseFormatTextJSONSchemaConfig + +__all__ = ["BetaResponseFormatTextConfig", "Text", "JSONObject"] + + +class Text(BaseModel): + """Default response format. Used to generate text responses.""" + + type: Literal["text"] + """The type of response format being defined. Always `text`.""" + + +class JSONObject(BaseModel): + """JSON object response format. + + An older method of generating JSON responses. + Using `json_schema` is recommended for models that support it. Note that the + model will not generate JSON without a system or user message instructing it + to do so. + """ + + type: Literal["json_object"] + """The type of response format being defined. Always `json_object`.""" + + +BetaResponseFormatTextConfig: TypeAlias = Annotated[ + Union[Text, BetaResponseFormatTextJSONSchemaConfig, JSONObject], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_format_text_config_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_format_text_config_param.py new file mode 100644 index 0000000..c10399f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_format_text_config_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .beta_response_format_text_json_schema_config_param import BetaResponseFormatTextJSONSchemaConfigParam + +__all__ = ["BetaResponseFormatTextConfigParam", "Text", "JSONObject"] + + +class Text(TypedDict, total=False): + """Default response format. Used to generate text responses.""" + + type: Required[Literal["text"]] + """The type of response format being defined. Always `text`.""" + + +class JSONObject(TypedDict, total=False): + """JSON object response format. + + An older method of generating JSON responses. + Using `json_schema` is recommended for models that support it. Note that the + model will not generate JSON without a system or user message instructing it + to do so. + """ + + type: Required[Literal["json_object"]] + """The type of response format being defined. Always `json_object`.""" + + +BetaResponseFormatTextConfigParam: TypeAlias = Union[Text, BetaResponseFormatTextJSONSchemaConfigParam, JSONObject] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_format_text_json_schema_config.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_format_text_json_schema_config.py new file mode 100644 index 0000000..d4c141f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_format_text_json_schema_config.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["BetaResponseFormatTextJSONSchemaConfig"] + + +class BetaResponseFormatTextJSONSchemaConfig(BaseModel): + """JSON Schema response format. + + Used to generate structured JSON responses. + Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs). + """ + + name: str + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + schema_: Dict[str, object] = FieldInfo(alias="schema") + """ + The schema for the response format, described as a JSON Schema object. Learn how + to build JSON schemas [here](https://json-schema.org/). + """ + + type: Literal["json_schema"] + """The type of response format being defined. Always `json_schema`.""" + + description: Optional[str] = None + """ + A description of what the response format is for, used by the model to determine + how to respond in the format. + """ + + strict: Optional[bool] = None + """ + Whether to enable strict schema adherence when generating the output. If set to + true, the model will always follow the exact schema defined in the `schema` + field. Only a subset of JSON Schema is supported when `strict` is `true`. To + learn more, read the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_format_text_json_schema_config_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_format_text_json_schema_config_param.py new file mode 100644 index 0000000..6fc894c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_format_text_json_schema_config_param.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseFormatTextJSONSchemaConfigParam"] + + +class BetaResponseFormatTextJSONSchemaConfigParam(TypedDict, total=False): + """JSON Schema response format. + + Used to generate structured JSON responses. + Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs). + """ + + name: Required[str] + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + schema: Required[Dict[str, object]] + """ + The schema for the response format, described as a JSON Schema object. Learn how + to build JSON schemas [here](https://json-schema.org/). + """ + + type: Required[Literal["json_schema"]] + """The type of response format being defined. Always `json_schema`.""" + + description: str + """ + A description of what the response format is for, used by the model to determine + how to respond in the format. + """ + + strict: Optional[bool] + """ + Whether to enable strict schema adherence when generating the output. If set to + true, the model will always follow the exact schema defined in the `schema` + field. Only a subset of JSON Schema is supported when `strict` is `true`. To + learn more, read the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_arguments_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_arguments_delta_event.py new file mode 100644 index 0000000..278420d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_arguments_delta_event.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseFunctionCallArgumentsDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFunctionCallArgumentsDeltaEvent(BaseModel): + """Emitted when there is a partial function-call arguments delta.""" + + delta: str + """The function-call arguments delta that is added.""" + + item_id: str + """The ID of the output item that the function-call arguments delta is added to.""" + + output_index: int + """ + The index of the output item that the function-call arguments delta is added to. + """ + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.function_call_arguments.delta"] + """The type of the event. Always `response.function_call_arguments.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_arguments_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_arguments_done_event.py new file mode 100644 index 0000000..0c50ee1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_arguments_done_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseFunctionCallArgumentsDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFunctionCallArgumentsDoneEvent(BaseModel): + """Emitted when function-call arguments are finalized.""" + + arguments: str + """The function-call arguments.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.function_call_arguments.done"] + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_output_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_output_item.py new file mode 100644 index 0000000..90ca007 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_output_item.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .beta_response_input_file_content import BetaResponseInputFileContent +from .beta_response_input_text_content import BetaResponseInputTextContent +from .beta_response_input_image_content import BetaResponseInputImageContent + +__all__ = ["BetaResponseFunctionCallOutputItem"] + +BetaResponseFunctionCallOutputItem: TypeAlias = Annotated[ + Union[BetaResponseInputTextContent, BetaResponseInputImageContent, BetaResponseInputFileContent], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_output_item_list.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_output_item_list.py new file mode 100644 index 0000000..dd3b378 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_output_item_list.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .beta_response_function_call_output_item import BetaResponseFunctionCallOutputItem + +__all__ = ["BetaResponseFunctionCallOutputItemList"] + +BetaResponseFunctionCallOutputItemList: TypeAlias = List[BetaResponseFunctionCallOutputItem] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_output_item_list_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_output_item_list_param.py new file mode 100644 index 0000000..e168faa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_output_item_list_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import TypeAlias + +from .beta_response_input_file_content_param import BetaResponseInputFileContentParam +from .beta_response_input_text_content_param import BetaResponseInputTextContentParam +from .beta_response_input_image_content_param import BetaResponseInputImageContentParam + +__all__ = ["BetaResponseFunctionCallOutputItemListParam", "BetaResponseFunctionCallOutputItemParam"] + +BetaResponseFunctionCallOutputItemParam: TypeAlias = Union[ + BetaResponseInputTextContentParam, BetaResponseInputImageContentParam, BetaResponseInputFileContentParam +] + +BetaResponseFunctionCallOutputItemListParam: TypeAlias = List[BetaResponseFunctionCallOutputItemParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_output_item_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_output_item_param.py new file mode 100644 index 0000000..550bcc4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_call_output_item_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .beta_response_input_file_content_param import BetaResponseInputFileContentParam +from .beta_response_input_text_content_param import BetaResponseInputTextContentParam +from .beta_response_input_image_content_param import BetaResponseInputImageContentParam + +__all__ = ["BetaResponseFunctionCallOutputItemParam"] + +BetaResponseFunctionCallOutputItemParam: TypeAlias = Union[ + BetaResponseInputTextContentParam, BetaResponseInputImageContentParam, BetaResponseInputFileContentParam +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_shell_call_output_content.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_shell_call_output_content.py new file mode 100644 index 0000000..146087b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_shell_call_output_content.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["BetaResponseFunctionShellCallOutputContent", "Outcome", "OutcomeTimeout", "OutcomeExit"] + + +class OutcomeTimeout(BaseModel): + """Indicates that the shell call exceeded its configured time limit.""" + + type: Literal["timeout"] + """The outcome type. Always `timeout`.""" + + +class OutcomeExit(BaseModel): + """Indicates that the shell commands finished and returned an exit code.""" + + exit_code: int + """The exit code returned by the shell process.""" + + type: Literal["exit"] + """The outcome type. Always `exit`.""" + + +Outcome: TypeAlias = Annotated[Union[OutcomeTimeout, OutcomeExit], PropertyInfo(discriminator="type")] + + +class BetaResponseFunctionShellCallOutputContent(BaseModel): + """Captured stdout and stderr for a portion of a shell tool call output.""" + + outcome: Outcome + """The exit or timeout outcome associated with this shell call.""" + + stderr: str + """Captured stderr output for the shell call.""" + + stdout: str + """Captured stdout output for the shell call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_shell_call_output_content_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_shell_call_output_content_param.py new file mode 100644 index 0000000..bcc502c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_shell_call_output_content_param.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["BetaResponseFunctionShellCallOutputContentParam", "Outcome", "OutcomeTimeout", "OutcomeExit"] + + +class OutcomeTimeout(TypedDict, total=False): + """Indicates that the shell call exceeded its configured time limit.""" + + type: Required[Literal["timeout"]] + """The outcome type. Always `timeout`.""" + + +class OutcomeExit(TypedDict, total=False): + """Indicates that the shell commands finished and returned an exit code.""" + + exit_code: Required[int] + """The exit code returned by the shell process.""" + + type: Required[Literal["exit"]] + """The outcome type. Always `exit`.""" + + +Outcome: TypeAlias = Union[OutcomeTimeout, OutcomeExit] + + +class BetaResponseFunctionShellCallOutputContentParam(TypedDict, total=False): + """Captured stdout and stderr for a portion of a shell tool call output.""" + + outcome: Required[Outcome] + """The exit or timeout outcome associated with this shell call.""" + + stderr: Required[str] + """Captured stderr output for the shell call.""" + + stdout: Required[str] + """Captured stdout output for the shell call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_shell_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_shell_tool_call.py new file mode 100644 index 0000000..f6cdccf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_shell_tool_call.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_local_environment import BetaResponseLocalEnvironment +from .beta_response_container_reference import BetaResponseContainerReference + +__all__ = [ + "BetaResponseFunctionShellToolCall", + "Action", + "Environment", + "Agent", + "Caller", + "CallerDirect", + "CallerProgram", +] + + +class Action(BaseModel): + """The shell commands and limits that describe how to run the tool call.""" + + commands: List[str] + + max_output_length: Optional[int] = None + """Optional maximum number of characters to return from each command.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the commands.""" + + +Environment: TypeAlias = Annotated[ + Union[BetaResponseLocalEnvironment, BetaResponseContainerReference, None], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseFunctionShellToolCall(BaseModel): + """A tool call that executes one or more shell commands in a managed environment.""" + + id: str + """The unique ID of the shell tool call. + + Populated when this item is returned via API. + """ + + action: Action + """The shell commands and limits that describe how to run the tool call.""" + + call_id: str + """The unique ID of the shell tool call generated by the model.""" + + environment: Optional[Environment] = None + """Represents the use of a local environment to perform shell actions.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + type: Literal["shell_call"] + """The type of the item. Always `shell_call`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The ID of the entity that created this tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_shell_tool_call_output.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_shell_tool_call_output.py new file mode 100644 index 0000000..2ab6a88 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_shell_tool_call_output.py @@ -0,0 +1,119 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "BetaResponseFunctionShellToolCallOutput", + "Output", + "OutputOutcome", + "OutputOutcomeTimeout", + "OutputOutcomeExit", + "Agent", + "Caller", + "CallerDirect", + "CallerProgram", +] + + +class OutputOutcomeTimeout(BaseModel): + """Indicates that the shell call exceeded its configured time limit.""" + + type: Literal["timeout"] + """The outcome type. Always `timeout`.""" + + +class OutputOutcomeExit(BaseModel): + """Indicates that the shell commands finished and returned an exit code.""" + + exit_code: int + """Exit code from the shell process.""" + + type: Literal["exit"] + """The outcome type. Always `exit`.""" + + +OutputOutcome: TypeAlias = Annotated[Union[OutputOutcomeTimeout, OutputOutcomeExit], PropertyInfo(discriminator="type")] + + +class Output(BaseModel): + """The content of a shell tool call output that was emitted.""" + + outcome: OutputOutcome + """ + Represents either an exit outcome (with an exit code) or a timeout outcome for a + shell call output chunk. + """ + + stderr: str + """The standard error output that was captured.""" + + stdout: str + """The standard output that was captured.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseFunctionShellToolCallOutput(BaseModel): + """The output of a shell tool call that was emitted.""" + + id: str + """The unique ID of the shell call output. + + Populated when this item is returned via API. + """ + + call_id: str + """The unique ID of the shell tool call generated by the model.""" + + max_output_length: Optional[int] = None + """The maximum length of the shell command output. + + This is generated by the model and should be passed back with the raw output. + """ + + output: List[Output] + """An array of shell call output contents""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the shell call output. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + type: Literal["shell_call_output"] + """The type of the shell call output. Always `shell_call_output`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_tool_call.py new file mode 100644 index 0000000..a71c812 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_tool_call.py @@ -0,0 +1,74 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["BetaResponseFunctionToolCall", "Agent", "Caller", "CallerDirect", "CallerProgram"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseFunctionToolCall(BaseModel): + """A tool call to run a function. + + See the + [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information. + """ + + arguments: str + """A JSON string of the arguments to pass to the function.""" + + call_id: str + """The unique ID of the function tool call generated by the model.""" + + name: str + """The name of the function to run.""" + + type: Literal["function_call"] + """The type of the function tool call. Always `function_call`.""" + + id: Optional[str] = None + """The unique ID of the function tool call.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + async_: Optional[bool] = FieldInfo(alias="async", default=None) + """Whether the function tool call runs asynchronously.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + namespace: Optional[str] = None + """The namespace of the function to run.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_tool_call_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_tool_call_item.py new file mode 100644 index 0000000..0c16583 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_tool_call_item.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .beta_response_function_tool_call import BetaResponseFunctionToolCall + +__all__ = ["BetaResponseFunctionToolCallItem"] + + +class BetaResponseFunctionToolCallItem(BetaResponseFunctionToolCall): + """A tool call to run a function. + + See the + [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information. + """ + + id: str # type: ignore + """The unique ID of the function tool call.""" + + status: Literal["in_progress", "completed", "incomplete"] # type: ignore + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_tool_call_output_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_tool_call_output_item.py new file mode 100644 index 0000000..cfe223d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_tool_call_output_item.py @@ -0,0 +1,85 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_input_file import BetaResponseInputFile +from .beta_response_input_text import BetaResponseInputText +from .beta_response_input_image import BetaResponseInputImage + +__all__ = [ + "BetaResponseFunctionToolCallOutputItem", + "OutputOutputContentList", + "Agent", + "Caller", + "CallerDirect", + "CallerProgram", +] + +OutputOutputContentList: TypeAlias = Annotated[ + Union[BetaResponseInputText, BetaResponseInputImage, BetaResponseInputFile], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseFunctionToolCallOutputItem(BaseModel): + id: str + """The unique ID of the function call tool output.""" + + output: Union[str, List[OutputOutputContentList]] + """ + The output from the function call generated by your code. Can be a string or an + list of output content. + """ + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["function_call_output"] + """The type of the function tool call output. Always `function_call_output`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + call_id: Optional[str] = None + """The unique ID of the function tool call generated by the model.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" + + name: Optional[str] = None + """The name of the tool that produced the output.""" + + namespace: Optional[str] = None + """The namespace of the tool that produced the output.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_tool_call_param.py new file mode 100644 index 0000000..775f563 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_tool_call_param.py @@ -0,0 +1,77 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["BetaResponseFunctionToolCallParam", "Agent", "Caller", "CallerDirect", "CallerProgram"] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + + +class CallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + + +Caller: TypeAlias = Union[CallerDirect, CallerProgram] + + +_BetaResponseFunctionToolCallParamReservedKeywords = TypedDict( + "_BetaResponseFunctionToolCallParamReservedKeywords", + { + "async": bool, + }, + total=False, +) + + +class BetaResponseFunctionToolCallParam(_BetaResponseFunctionToolCallParamReservedKeywords, total=False): + """A tool call to run a function. + + See the + [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information. + """ + + arguments: Required[str] + """A JSON string of the arguments to pass to the function.""" + + call_id: Required[str] + """The unique ID of the function tool call generated by the model.""" + + name: Required[str] + """The name of the function to run.""" + + type: Required[Literal["function_call"]] + """The type of the function tool call. Always `function_call`.""" + + id: str + """The unique ID of the function tool call.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + caller: Optional[Caller] + """The execution context that produced this tool call.""" + + namespace: str + """The namespace of the function to run.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_web_search.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_web_search.py new file mode 100644 index 0000000..9da960e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_web_search.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "BetaResponseFunctionWebSearch", + "Action", + "ActionSearch", + "ActionSearchSource", + "ActionOpenPage", + "ActionFindInPage", + "Agent", +] + + +class ActionSearchSource(BaseModel): + """A source used in the search.""" + + type: Literal["url"] + """The type of source. Always `url`.""" + + url: str + """The URL of the source.""" + + +class ActionSearch(BaseModel): + """Action type "search" - Performs a web search query.""" + + type: Literal["search"] + """The action type.""" + + queries: Optional[List[str]] = None + """The search queries.""" + + query: Optional[str] = None + """The search query.""" + + sources: Optional[List[ActionSearchSource]] = None + """The sources used in the search.""" + + +class ActionOpenPage(BaseModel): + """Action type "open_page" - Opens a specific URL from search results.""" + + type: Literal["open_page"] + """The action type.""" + + url: Optional[str] = None + """The URL opened by the model.""" + + +class ActionFindInPage(BaseModel): + """Action type "find_in_page": Searches for a pattern within a loaded page.""" + + pattern: str + """The pattern or text to search for within the page.""" + + type: Literal["find_in_page"] + """The action type.""" + + url: str + """The URL of the page searched for the pattern.""" + + +Action: TypeAlias = Annotated[Union[ActionSearch, ActionOpenPage, ActionFindInPage], PropertyInfo(discriminator="type")] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFunctionWebSearch(BaseModel): + """The results of a web search tool call. + + See the + [web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information. + """ + + id: str + """The unique ID of the web search tool call.""" + + action: Action + """ + An object describing the specific action taken in this web search call. Includes + details on how the model used the web (search, open_page, find_in_page). + """ + + status: Literal["in_progress", "searching", "completed", "failed", "incomplete"] + """The status of the web search tool call.""" + + type: Literal["web_search_call"] + """The type of the web search tool call. Always `web_search_call`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_web_search_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_web_search_param.py new file mode 100644 index 0000000..04a920e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_function_web_search_param.py @@ -0,0 +1,103 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "BetaResponseFunctionWebSearchParam", + "Action", + "ActionSearch", + "ActionSearchSource", + "ActionOpenPage", + "ActionFindInPage", + "Agent", +] + + +class ActionSearchSource(TypedDict, total=False): + """A source used in the search.""" + + type: Required[Literal["url"]] + """The type of source. Always `url`.""" + + url: Required[str] + """The URL of the source.""" + + +class ActionSearch(TypedDict, total=False): + """Action type "search" - Performs a web search query.""" + + type: Required[Literal["search"]] + """The action type.""" + + queries: SequenceNotStr[str] + """The search queries.""" + + query: str + """The search query.""" + + sources: Iterable[ActionSearchSource] + """The sources used in the search.""" + + +class ActionOpenPage(TypedDict, total=False): + """Action type "open_page" - Opens a specific URL from search results.""" + + type: Required[Literal["open_page"]] + """The action type.""" + + url: Optional[str] + """The URL opened by the model.""" + + +class ActionFindInPage(TypedDict, total=False): + """Action type "find_in_page": Searches for a pattern within a loaded page.""" + + pattern: Required[str] + """The pattern or text to search for within the page.""" + + type: Required[Literal["find_in_page"]] + """The action type.""" + + url: Required[str] + """The URL of the page searched for the pattern.""" + + +Action: TypeAlias = Union[ActionSearch, ActionOpenPage, ActionFindInPage] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFunctionWebSearchParam(TypedDict, total=False): + """The results of a web search tool call. + + See the + [web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information. + """ + + id: Required[str] + """The unique ID of the web search tool call.""" + + action: Required[Action] + """ + An object describing the specific action taken in this web search call. Includes + details on how the model used the web (search, open_page, find_in_page). + """ + + status: Required[Literal["in_progress", "searching", "completed", "failed", "incomplete"]] + """The status of the web search tool call.""" + + type: Required[Literal["web_search_call"]] + """The type of the web search tool call. Always `web_search_call`.""" + + agent: Optional[Agent] + """The agent that produced this item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_image_gen_call_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_image_gen_call_completed_event.py new file mode 100644 index 0000000..6f3aadd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_image_gen_call_completed_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseImageGenCallCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseImageGenCallCompletedEvent(BaseModel): + """ + Emitted when an image generation tool call has completed and the final image is available. + """ + + item_id: str + """The unique identifier of the image generation item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.image_generation_call.completed"] + """The type of the event. Always 'response.image_generation_call.completed'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_image_gen_call_generating_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_image_gen_call_generating_event.py new file mode 100644 index 0000000..415ea84 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_image_gen_call_generating_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseImageGenCallGeneratingEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseImageGenCallGeneratingEvent(BaseModel): + """ + Emitted when an image generation tool call is actively generating an image (intermediate state). + """ + + item_id: str + """The unique identifier of the image generation item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of the image generation item being processed.""" + + type: Literal["response.image_generation_call.generating"] + """The type of the event. Always 'response.image_generation_call.generating'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_image_gen_call_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_image_gen_call_in_progress_event.py new file mode 100644 index 0000000..b2c3381 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_image_gen_call_in_progress_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseImageGenCallInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseImageGenCallInProgressEvent(BaseModel): + """Emitted when an image generation tool call is in progress.""" + + item_id: str + """The unique identifier of the image generation item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of the image generation item being processed.""" + + type: Literal["response.image_generation_call.in_progress"] + """The type of the event. Always 'response.image_generation_call.in_progress'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_image_gen_call_partial_image_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_image_gen_call_partial_image_event.py new file mode 100644 index 0000000..79ef0bb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_image_gen_call_partial_image_event.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseImageGenCallPartialImageEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseImageGenCallPartialImageEvent(BaseModel): + """Emitted when a partial image is available during image generation streaming.""" + + item_id: str + """The unique identifier of the image generation item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + partial_image_b64: str + """Base64-encoded partial image data, suitable for rendering as an image.""" + + partial_image_index: int + """ + 0-based index for the partial image (backend is 1-based, but this is 0-based for + the user). + """ + + sequence_number: int + """The sequence number of the image generation item being processed.""" + + type: Literal["response.image_generation_call.partial_image"] + """The type of the event. Always 'response.image_generation_call.partial_image'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" + + background: Optional[str] = None + """The background setting that was used.""" + + output_format: Optional[str] = None + """The output format that was used.""" + + quality: Optional[str] = None + """The image quality that was used.""" + + size: Optional[str] = None + """The image size that was used.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_in_progress_event.py new file mode 100644 index 0000000..0f9affe --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_in_progress_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response import BetaResponse + +__all__ = ["BetaResponseInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseInProgressEvent(BaseModel): + """Emitted when the response is in progress.""" + + response: BetaResponse + """The response that is in progress.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.in_progress"] + """The type of the event. Always `response.in_progress`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_includable.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_includable.py new file mode 100644 index 0000000..1d154e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_includable.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["BetaResponseIncludable"] + +BetaResponseIncludable: TypeAlias = Literal[ + "file_search_call.results", + "web_search_call.results", + "web_search_call.action.sources", + "message.input_image.image_url", + "computer_call_output.output.image_url", + "code_interpreter_call.outputs", + "reasoning.encrypted_content", + "message.output_text.logprobs", +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_incomplete_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_incomplete_event.py new file mode 100644 index 0000000..1f8f2f6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_incomplete_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response import BetaResponse + +__all__ = ["BetaResponseIncompleteEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseIncompleteEvent(BaseModel): + """An event that is emitted when a response finishes as incomplete. + + Over WebSocket, steering can finish a response with + `response.incomplete_details.reason` set to `steered`, followed automatically + by a successor `response.created` that commits the queued steering input. + """ + + response: BetaResponse + """The response that was incomplete.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.incomplete"] + """The type of the event. Always `response.incomplete`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_inject_created_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_inject_created_event.py new file mode 100644 index 0000000..1deb40e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_inject_created_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseInjectCreatedEvent"] + + +class BetaResponseInjectCreatedEvent(BaseModel): + """ + Emitted when all injected input items were validated and committed to the + active response. + """ + + response_id: str + """The ID of the response that accepted the input.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.inject.created"] + """The event discriminator. Always `response.inject.created`.""" + + stream_id: Optional[str] = None + """The multiplexed WebSocket stream that emitted the event. + + This field is present only when WebSocket multiplexing is enabled separately. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_inject_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_inject_event.py new file mode 100644 index 0000000..9499939 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_inject_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_input_item import BetaResponseInputItem + +__all__ = ["BetaResponseInjectEvent"] + + +class BetaResponseInjectEvent(BaseModel): + """ + Injects input items into an active response over a WebSocket connection. + The items are validated and committed atomically. Currently, the server + accepts client-owned tool outputs that resume a waiting agent. + """ + + input: List[BetaResponseInputItem] + """Input items to inject into the active response.""" + + response_id: str + """The ID of the active response that should receive the input.""" + + type: Literal["response.inject"] + """The event discriminator. Always `response.inject`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_inject_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_inject_event_param.py new file mode 100644 index 0000000..3bb9a62 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_inject_event_param.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +from .beta_response_input_item_param import BetaResponseInputItemParam + +__all__ = ["BetaResponseInjectEventParam"] + + +class BetaResponseInjectEventParam(TypedDict, total=False): + """ + Injects input items into an active response over a WebSocket connection. + The items are validated and committed atomically. Currently, the server + accepts client-owned tool outputs that resume a waiting agent. + """ + + input: Required[Iterable[BetaResponseInputItemParam]] + """Input items to inject into the active response.""" + + response_id: Required[str] + """The ID of the active response that should receive the input.""" + + type: Required[Literal["response.inject"]] + """The event discriminator. Always `response.inject`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_inject_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_inject_failed_event.py new file mode 100644 index 0000000..f6c983f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_inject_failed_event.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_input_item import BetaResponseInputItem + +__all__ = ["BetaResponseInjectFailedEvent", "Error"] + + +class Error(BaseModel): + """Information about why the input was not committed.""" + + code: Literal["response_already_completed", "response_not_found"] + """A machine-readable error code.""" + + message: str + """A human-readable description of the error.""" + + +class BetaResponseInjectFailedEvent(BaseModel): + """Emitted when injected input could not be committed to a response. + + The event + returns the uncommitted raw input so the client can retry it in another + response when appropriate. + """ + + error: Error + """Information about why the input was not committed.""" + + input: List[BetaResponseInputItem] + """The raw input items that were not committed.""" + + response_id: str + """The ID of the response that rejected the input.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.inject.failed"] + """The event discriminator. Always `response.inject.failed`.""" + + stream_id: Optional[str] = None + """The multiplexed WebSocket stream that emitted the event. + + This field is present only when WebSocket multiplexing is enabled separately. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input.py new file mode 100644 index 0000000..adb42a5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .beta_response_input_item import BetaResponseInputItem + +__all__ = ["BetaResponseInput"] + +BetaResponseInput: TypeAlias = List[BetaResponseInputItem] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_content.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_content.py new file mode 100644 index 0000000..705d216 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_content.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .beta_response_input_file import BetaResponseInputFile +from .beta_response_input_text import BetaResponseInputText +from .beta_response_input_image import BetaResponseInputImage + +__all__ = ["BetaResponseInputContent"] + +BetaResponseInputContent: TypeAlias = Annotated[ + Union[BetaResponseInputText, BetaResponseInputImage, BetaResponseInputFile], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_content_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_content_param.py new file mode 100644 index 0000000..dc405ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_content_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .beta_response_input_file_param import BetaResponseInputFileParam +from .beta_response_input_text_param import BetaResponseInputTextParam +from .beta_response_input_image_param import BetaResponseInputImageParam + +__all__ = ["BetaResponseInputContentParam"] + +BetaResponseInputContentParam: TypeAlias = Union[ + BetaResponseInputTextParam, BetaResponseInputImageParam, BetaResponseInputFileParam +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_file.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_file.py new file mode 100644 index 0000000..37661b6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_file.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseInputFile", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputFile(BaseModel): + """A file input to the model.""" + + type: Literal["input_file"] + """The type of the input item. Always `input_file`.""" + + detail: Optional[Literal["auto", "low", "high"]] = None + """The detail level of the file to be sent to the model. + + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. + """ + + file_data: Optional[str] = None + """The content of the file to be sent to the model.""" + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + file_url: Optional[str] = None + """The URL of the file to be sent to the model.""" + + filename: Optional[str] = None + """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_file_content.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_file_content.py new file mode 100644 index 0000000..5f2f96d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_file_content.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseInputFileContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputFileContent(BaseModel): + """A file input to the model.""" + + type: Literal["input_file"] + """The type of the input item. Always `input_file`.""" + + detail: Optional[Literal["auto", "low", "high"]] = None + """The detail level of the file to be sent to the model. + + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. + """ + + file_data: Optional[str] = None + """The base64-encoded data of the file to be sent to the model.""" + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + file_url: Optional[str] = None + """The URL of the file to be sent to the model.""" + + filename: Optional[str] = None + """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_file_content_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_file_content_param.py new file mode 100644 index 0000000..ccc9a74 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_file_content_param.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseInputFileContentParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputFileContentParam(TypedDict, total=False): + """A file input to the model.""" + + type: Required[Literal["input_file"]] + """The type of the input item. Always `input_file`.""" + + detail: Literal["auto", "low", "high"] + """The detail level of the file to be sent to the model. + + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. + """ + + file_data: Optional[str] + """The base64-encoded data of the file to be sent to the model.""" + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + file_url: Optional[str] + """The URL of the file to be sent to the model.""" + + filename: Optional[str] + """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_file_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_file_param.py new file mode 100644 index 0000000..12d226e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_file_param.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseInputFileParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputFileParam(TypedDict, total=False): + """A file input to the model.""" + + type: Required[Literal["input_file"]] + """The type of the input item. Always `input_file`.""" + + detail: Literal["auto", "low", "high"] + """The detail level of the file to be sent to the model. + + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. + """ + + file_data: str + """The content of the file to be sent to the model.""" + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + file_url: str + """The URL of the file to be sent to the model.""" + + filename: str + """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_image.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_image.py new file mode 100644 index 0000000..81da7d6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_image.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_image_detail import BetaImageDetail + +__all__ = ["BetaResponseInputImage", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputImage(BaseModel): + """An image input to the model. + + Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision). + """ + + detail: BetaImageDetail + """The detail level of the image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + type: Literal["input_image"] + """The type of the input item. Always `input_image`.""" + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] = None + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_image_content.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_image_content.py new file mode 100644 index 0000000..532c65d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_image_content.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_image_detail import BetaImageDetail + +__all__ = ["BetaResponseInputImageContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputImageContent(BaseModel): + """An image input to the model. + + Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision) + """ + + type: Literal["input_image"] + """The type of the input item. Always `input_image`.""" + + detail: Optional[BetaImageDetail] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] = None + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_image_content_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_image_content_param.py new file mode 100644 index 0000000..03e343c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_image_content_param.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from .beta_image_detail import BetaImageDetail + +__all__ = ["BetaResponseInputImageContentParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputImageContentParam(TypedDict, total=False): + """An image input to the model. + + Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision) + """ + + type: Required[Literal["input_image"]] + """The type of the input item. Always `input_image`.""" + + detail: Optional[BetaImageDetail] + """The detail level of the image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_image_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_image_param.py new file mode 100644 index 0000000..cabe60f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_image_param.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from .beta_image_detail import BetaImageDetail + +__all__ = ["BetaResponseInputImageParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputImageParam(TypedDict, total=False): + """An image input to the model. + + Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision). + """ + + detail: Required[BetaImageDetail] + """The detail level of the image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + type: Required[Literal["input_image"]] + """The type of the input item. Always `input_image`.""" + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_item.py new file mode 100644 index 0000000..51d12ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_item.py @@ -0,0 +1,1196 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_tool import BetaTool +from .beta_local_environment import BetaLocalEnvironment +from .beta_easy_input_message import BetaEasyInputMessage +from .beta_container_reference import BetaContainerReference +from .beta_mcp_tool_call_error import BetaMcpToolCallError +from .beta_response_output_message import BetaResponseOutputMessage +from .beta_response_reasoning_item import BetaResponseReasoningItem +from .beta_response_custom_tool_call import BetaResponseCustomToolCall +from .beta_response_computer_tool_call import BetaResponseComputerToolCall +from .beta_response_function_tool_call import BetaResponseFunctionToolCall +from .beta_response_input_text_content import BetaResponseInputTextContent +from .beta_response_function_web_search import BetaResponseFunctionWebSearch +from .beta_response_input_image_content import BetaResponseInputImageContent +from .beta_response_compaction_item_param import BetaResponseCompactionItemParam +from .beta_response_file_search_tool_call import BetaResponseFileSearchToolCall +from .beta_response_custom_tool_call_output import BetaResponseCustomToolCallOutput +from .beta_response_code_interpreter_tool_call import BetaResponseCodeInterpreterToolCall +from .beta_response_input_message_content_list import BetaResponseInputMessageContentList +from .beta_response_tool_search_output_item_param import BetaResponseToolSearchOutputItemParam +from .beta_response_function_call_output_item_list import BetaResponseFunctionCallOutputItemList +from .beta_response_configuration_update_item_param import BetaResponseConfigurationUpdateItemParam +from .beta_response_function_shell_call_output_content import BetaResponseFunctionShellCallOutputContent +from .beta_response_computer_tool_call_output_screenshot import BetaResponseComputerToolCallOutputScreenshot + +__all__ = [ + "BetaResponseInputItem", + "Message", + "MessageAgent", + "ComputerCallOutput", + "ComputerCallOutputAcknowledgedSafetyCheck", + "ComputerCallOutputAgent", + "FunctionCallOutput", + "FunctionCallOutputAgent", + "FunctionCallOutputCaller", + "FunctionCallOutputCallerDirect", + "FunctionCallOutputCallerProgram", + "AgentMessage", + "AgentMessageContent", + "AgentMessageContentEncryptedContent", + "AgentMessageAgent", + "MultiAgentCall", + "MultiAgentCallAgent", + "MultiAgentCallOutput", + "MultiAgentCallOutputOutput", + "MultiAgentCallOutputOutputAnnotation", + "MultiAgentCallOutputOutputAnnotationFileCitation", + "MultiAgentCallOutputOutputAnnotationURLCitation", + "MultiAgentCallOutputOutputAnnotationContainerFileCitation", + "MultiAgentCallOutputAgent", + "ToolSearchCall", + "ToolSearchCallAgent", + "AdditionalTools", + "AdditionalToolsAgent", + "ImageGenerationCall", + "ImageGenerationCallAgent", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallAgent", + "LocalShellCallOutput", + "LocalShellCallOutputAgent", + "ShellCall", + "ShellCallAction", + "ShellCallAgent", + "ShellCallCaller", + "ShellCallCallerDirect", + "ShellCallCallerProgram", + "ShellCallEnvironment", + "ShellCallOutput", + "ShellCallOutputAgent", + "ShellCallOutputCaller", + "ShellCallOutputCallerDirect", + "ShellCallOutputCallerProgram", + "ApplyPatchCall", + "ApplyPatchCallOperation", + "ApplyPatchCallOperationCreateFile", + "ApplyPatchCallOperationDeleteFile", + "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallAgent", + "ApplyPatchCallCaller", + "ApplyPatchCallCallerDirect", + "ApplyPatchCallCallerProgram", + "ApplyPatchCallOutput", + "ApplyPatchCallOutputAgent", + "ApplyPatchCallOutputCaller", + "ApplyPatchCallOutputCallerDirect", + "ApplyPatchCallOutputCallerProgram", + "McpListTools", + "McpListToolsTool", + "McpListToolsAgent", + "McpApprovalRequest", + "McpApprovalRequestAgent", + "McpApprovalResponse", + "McpApprovalResponseAgent", + "McpCall", + "McpCallAgent", + "CompactionTrigger", + "CompactionTriggerAgent", + "ItemReference", + "ItemReferenceAgent", + "Program", + "ProgramAgent", + "ProgramOutput", + "ProgramOutputAgent", +] + + +class MessageAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Message(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. + """ + + content: BetaResponseInputMessageContentList + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Literal["user", "system", "developer"] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + agent: Optional[MessageAgent] = None + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always set to `message`.""" + + +class ComputerCallOutputAcknowledgedSafetyCheck(BaseModel): + """A pending safety check for the computer call.""" + + id: str + """The ID of the pending safety check.""" + + code: Optional[str] = None + """The type of the pending safety check.""" + + message: Optional[str] = None + """Details about the pending safety check.""" + + +class ComputerCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ComputerCallOutput(BaseModel): + """The output of a computer tool call.""" + + call_id: str + """The ID of the computer tool call that produced the output.""" + + output: BetaResponseComputerToolCallOutputScreenshot + """A computer screenshot image used with the computer use tool.""" + + type: Literal["computer_call_output"] + """The type of the computer tool call output. Always `computer_call_output`.""" + + id: Optional[str] = None + """The ID of the computer tool call output.""" + + acknowledged_safety_checks: Optional[List[ComputerCallOutputAcknowledgedSafetyCheck]] = None + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + agent: Optional[ComputerCallOutputAgent] = None + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + +class FunctionCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class FunctionCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class FunctionCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +FunctionCallOutputCaller: TypeAlias = Annotated[ + Union[FunctionCallOutputCallerDirect, FunctionCallOutputCallerProgram, None], PropertyInfo(discriminator="type") +] + + +class FunctionCallOutput(BaseModel): + """The output of a function tool call.""" + + output: Union[str, BetaResponseFunctionCallOutputItemList] + """Text, image, or file output of the function tool call.""" + + type: Literal["function_call_output"] + """The type of the function tool call output. Always `function_call_output`.""" + + id: Optional[str] = None + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[FunctionCallOutputAgent] = None + """The agent that produced this item.""" + + call_id: Optional[str] = None + """The unique ID of the function tool call generated by the model.""" + + caller: Optional[FunctionCallOutputCaller] = None + """The execution context that produced this tool call.""" + + name: Optional[str] = None + """The name of the tool that produced the output.""" + + namespace: Optional[str] = None + """The namespace of the tool that produced the output.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +class AgentMessageContentEncryptedContent(BaseModel): + """ + Opaque encrypted content that Responses API decrypts inside trusted model execution. + """ + + encrypted_content: str + """Opaque encrypted content.""" + + type: Literal["encrypted_content"] + """The type of the input item. Always `encrypted_content`.""" + + +AgentMessageContent: TypeAlias = Annotated[ + Union[BetaResponseInputTextContent, BetaResponseInputImageContent, AgentMessageContentEncryptedContent], + PropertyInfo(discriminator="type"), +] + + +class AgentMessageAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class AgentMessage(BaseModel): + """A message routed between agents.""" + + author: str + """The sending agent identity.""" + + content: List[AgentMessageContent] + """Plaintext, image, or encrypted content sent between agents.""" + + recipient: str + """The destination agent identity.""" + + type: Literal["agent_message"] + """The item type. Always `agent_message`.""" + + id: Optional[str] = None + """The unique ID of this agent message item.""" + + agent: Optional[AgentMessageAgent] = None + """The agent that produced this item.""" + + +class MultiAgentCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCall(BaseModel): + action: Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + """The multi-agent action that was executed.""" + + arguments: str + """The action arguments as a JSON string.""" + + call_id: str + """The unique ID linking this call to its output.""" + + type: Literal["multi_agent_call"] + """The item type. Always `multi_agent_call`.""" + + id: Optional[str] = None + """The unique ID of this multi-agent call.""" + + agent: Optional[MultiAgentCallAgent] = None + """The agent that produced this item.""" + + +class MultiAgentCallOutputOutputAnnotationFileCitation(BaseModel): + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the file cited.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_citation"] + """The citation type. Always `file_citation`.""" + + +class MultiAgentCallOutputOutputAnnotationURLCitation(BaseModel): + end_index: int + """The index of the last character of the citation in the message.""" + + start_index: int + """The index of the first character of the citation in the message.""" + + title: str + """The title of the cited resource.""" + + type: Literal["url_citation"] + """The citation type. Always `url_citation`.""" + + url: str + """The URL of the cited resource.""" + + +class MultiAgentCallOutputOutputAnnotationContainerFileCitation(BaseModel): + container_id: str + """The ID of the container.""" + + end_index: int + """The index of the last character of the citation in the message.""" + + file_id: str + """The ID of the container file.""" + + filename: str + """The filename of the container file cited.""" + + start_index: int + """The index of the first character of the citation in the message.""" + + type: Literal["container_file_citation"] + """The citation type. Always `container_file_citation`.""" + + +MultiAgentCallOutputOutputAnnotation: TypeAlias = Annotated[ + Union[ + MultiAgentCallOutputOutputAnnotationFileCitation, + MultiAgentCallOutputOutputAnnotationURLCitation, + MultiAgentCallOutputOutputAnnotationContainerFileCitation, + ], + PropertyInfo(discriminator="type"), +] + + +class MultiAgentCallOutputOutput(BaseModel): + text: str + """The text content.""" + + type: Literal["output_text"] + """The content type. Always `output_text`.""" + + annotations: Optional[List[MultiAgentCallOutputOutputAnnotation]] = None + """Citations associated with the text content.""" + + +class MultiAgentCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCallOutput(BaseModel): + action: Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + """The multi-agent action that produced this result.""" + + call_id: str + """The unique ID of the multi-agent call.""" + + output: List[MultiAgentCallOutputOutput] + """Text output returned by the multi-agent action.""" + + type: Literal["multi_agent_call_output"] + """The item type. Always `multi_agent_call_output`.""" + + id: Optional[str] = None + """The unique ID of this multi-agent call output.""" + + agent: Optional[MultiAgentCallOutputAgent] = None + """The agent that produced this item.""" + + +class ToolSearchCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ToolSearchCall(BaseModel): + arguments: object + """The arguments supplied to the tool search call.""" + + type: Literal["tool_search_call"] + """The item type. Always `tool_search_call`.""" + + id: Optional[str] = None + """The unique ID of this tool search call.""" + + agent: Optional[ToolSearchCallAgent] = None + """The agent that produced this item.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Optional[Literal["server", "client"]] = None + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the tool search call.""" + + +class AdditionalToolsAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class AdditionalTools(BaseModel): + role: Literal["developer"] + """The role that provided the additional tools. Only `developer` is supported.""" + + tools: List[BetaTool] + """A list of additional tools made available at this item.""" + + type: Literal["additional_tools"] + """The item type. Always `additional_tools`.""" + + id: Optional[str] = None + """The unique ID of this additional tools item.""" + + agent: Optional[AdditionalToolsAgent] = None + """The agent that produced this item.""" + + +class ImageGenerationCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + + id: str + """The unique ID of the image generation call.""" + + result: Optional[str] = None + """The generated image encoded in base64.""" + + status: Literal["in_progress", "completed", "generating", "failed"] + """The status of the image generation call.""" + + type: Literal["image_generation_call"] + """The type of the image generation call. Always `image_generation_call`.""" + + action: Optional[Literal["generate", "edit", "auto"]] = None + """The action used for image generation.""" + + agent: Optional[ImageGenerationCallAgent] = None + """The agent that produced this item.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] = None + """The background setting used for generation.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] = None + """The output format used for generation.""" + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None + """The quality of the image generated by the image generation tool call. + + One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`. + """ + + revised_prompt: Optional[str] = None + """The prompt that was used after any model prompt rewriting.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + +class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + + command: List[str] + """The command to run.""" + + env: Dict[str, str] + """Environment variables to set for the command.""" + + type: Literal["exec"] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] = None + """Optional user to run the command as.""" + + working_directory: Optional[str] = None + """Optional working directory to run the command in.""" + + +class LocalShellCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + + id: str + """The unique ID of the local shell call.""" + + action: LocalShellCallAction + """Execute a shell command on the server.""" + + call_id: str + """The unique ID of the local shell tool call generated by the model.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the local shell call.""" + + type: Literal["local_shell_call"] + """The type of the local shell call. Always `local_shell_call`.""" + + agent: Optional[LocalShellCallAgent] = None + """The agent that produced this item.""" + + +class LocalShellCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + + id: str + """The unique ID of the local shell tool call generated by the model.""" + + output: str + """A JSON string of the output of the local shell tool call.""" + + type: Literal["local_shell_call_output"] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + agent: Optional[LocalShellCallOutputAgent] = None + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class ShellCallAction(BaseModel): + """The shell commands and limits that describe how to run the tool call.""" + + commands: List[str] + """Ordered shell commands for the execution environment to run.""" + + max_output_length: Optional[int] = None + """ + Maximum number of UTF-8 characters to capture from combined stdout and stderr + output. + """ + + timeout_ms: Optional[int] = None + """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" + + +class ShellCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ShellCallCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ShellCallCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ShellCallCaller: TypeAlias = Annotated[ + Union[ShellCallCallerDirect, ShellCallCallerProgram, None], PropertyInfo(discriminator="type") +] + +ShellCallEnvironment: TypeAlias = Annotated[ + Union[BetaLocalEnvironment, BetaContainerReference, None], PropertyInfo(discriminator="type") +] + + +class ShellCall(BaseModel): + """A tool representing a request to execute one or more shell commands.""" + + action: ShellCallAction + """The shell commands and limits that describe how to run the tool call.""" + + call_id: str + """The unique ID of the shell tool call generated by the model.""" + + type: Literal["shell_call"] + """The type of the item. Always `shell_call`.""" + + id: Optional[str] = None + """The unique ID of the shell tool call. + + Populated when this item is returned via API. + """ + + agent: Optional[ShellCallAgent] = None + """The agent that produced this item.""" + + caller: Optional[ShellCallCaller] = None + """The execution context that produced this tool call.""" + + environment: Optional[ShellCallEnvironment] = None + """The environment to execute the shell commands in.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + +class ShellCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ShellCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ShellCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ShellCallOutputCaller: TypeAlias = Annotated[ + Union[ShellCallOutputCallerDirect, ShellCallOutputCallerProgram, None], PropertyInfo(discriminator="type") +] + + +class ShellCallOutput(BaseModel): + """The streamed output items emitted by a shell tool call.""" + + call_id: str + """The unique ID of the shell tool call generated by the model.""" + + output: List[BetaResponseFunctionShellCallOutputContent] + """ + Captured chunks of stdout and stderr output, along with their associated + outcomes. + """ + + type: Literal["shell_call_output"] + """The type of the item. Always `shell_call_output`.""" + + id: Optional[str] = None + """The unique ID of the shell tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ShellCallOutputAgent] = None + """The agent that produced this item.""" + + caller: Optional[ShellCallOutputCaller] = None + """The execution context that produced this tool call.""" + + max_output_length: Optional[int] = None + """ + The maximum number of UTF-8 characters captured for this shell call's combined + output. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the shell call output.""" + + +class ApplyPatchCallOperationCreateFile(BaseModel): + """Instruction for creating a new file via the apply_patch tool.""" + + diff: str + """Unified diff content to apply when creating the file.""" + + path: str + """Path of the file to create relative to the workspace root.""" + + type: Literal["create_file"] + """The operation type. Always `create_file`.""" + + +class ApplyPatchCallOperationDeleteFile(BaseModel): + """Instruction for deleting an existing file via the apply_patch tool.""" + + path: str + """Path of the file to delete relative to the workspace root.""" + + type: Literal["delete_file"] + """The operation type. Always `delete_file`.""" + + +class ApplyPatchCallOperationUpdateFile(BaseModel): + """Instruction for updating an existing file via the apply_patch tool.""" + + diff: str + """Unified diff content to apply to the existing file.""" + + path: str + """Path of the file to update relative to the workspace root.""" + + type: Literal["update_file"] + """The operation type. Always `update_file`.""" + + +ApplyPatchCallOperation: TypeAlias = Annotated[ + Union[ApplyPatchCallOperationCreateFile, ApplyPatchCallOperationDeleteFile, ApplyPatchCallOperationUpdateFile], + PropertyInfo(discriminator="type"), +] + + +class ApplyPatchCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ApplyPatchCallCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ApplyPatchCallCaller: TypeAlias = Annotated[ + Union[ApplyPatchCallCallerDirect, ApplyPatchCallCallerProgram, None], PropertyInfo(discriminator="type") +] + + +class ApplyPatchCall(BaseModel): + """ + A tool call representing a request to create, delete, or update files using diff patches. + """ + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + operation: ApplyPatchCallOperation + """ + The specific create, delete, or update instruction for the apply_patch tool + call. + """ + + status: Literal["in_progress", "completed"] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Literal["apply_patch_call"] + """The type of the item. Always `apply_patch_call`.""" + + id: Optional[str] = None + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + agent: Optional[ApplyPatchCallAgent] = None + """The agent that produced this item.""" + + caller: Optional[ApplyPatchCallCaller] = None + """The execution context that produced this tool call.""" + + +class ApplyPatchCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ApplyPatchCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ApplyPatchCallOutputCaller: TypeAlias = Annotated[ + Union[ApplyPatchCallOutputCallerDirect, ApplyPatchCallOutputCallerProgram, None], PropertyInfo(discriminator="type") +] + + +class ApplyPatchCallOutput(BaseModel): + """The streamed output emitted by an apply patch tool call.""" + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + status: Literal["completed", "failed"] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Literal["apply_patch_call_output"] + """The type of the item. Always `apply_patch_call_output`.""" + + id: Optional[str] = None + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ApplyPatchCallOutputAgent] = None + """The agent that produced this item.""" + + caller: Optional[ApplyPatchCallOutputCaller] = None + """The execution context that produced this tool call.""" + + output: Optional[str] = None + """ + Optional human-readable log text from the apply patch tool (e.g., patch results + or errors). + """ + + +class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + + input_schema: object + """The JSON schema describing the tool's input.""" + + name: str + """The name of the tool.""" + + annotations: Optional[object] = None + """Additional annotations about the tool.""" + + description: Optional[str] = None + """The description of the tool.""" + + +class McpListToolsAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + + id: str + """The unique ID of the list.""" + + server_label: str + """The label of the MCP server.""" + + tools: List[McpListToolsTool] + """The tools available on the server.""" + + type: Literal["mcp_list_tools"] + """The type of the item. Always `mcp_list_tools`.""" + + agent: Optional[McpListToolsAgent] = None + """The agent that produced this item.""" + + error: Optional[str] = None + """Error message if the server could not list tools.""" + + +class McpApprovalRequestAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + + id: str + """The unique ID of the approval request.""" + + arguments: str + """A JSON string of arguments for the tool.""" + + name: str + """The name of the tool to run.""" + + server_label: str + """The label of the MCP server making the request.""" + + type: Literal["mcp_approval_request"] + """The type of the item. Always `mcp_approval_request`.""" + + agent: Optional[McpApprovalRequestAgent] = None + """The agent that produced this item.""" + + +class McpApprovalResponseAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + id: Optional[str] = None + """The unique ID of the approval response""" + + agent: Optional[McpApprovalResponseAgent] = None + """The agent that produced this item.""" + + reason: Optional[str] = None + """Optional reason for the decision.""" + + +class McpCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + + id: str + """The unique ID of the tool call.""" + + arguments: str + """A JSON string of the arguments passed to the tool.""" + + name: str + """The name of the tool that was run.""" + + server_label: str + """The label of the MCP server running the tool.""" + + type: Literal["mcp_call"] + """The type of the item. Always `mcp_call`.""" + + agent: Optional[McpCallAgent] = None + """The agent that produced this item.""" + + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[BetaMcpToolCallError] = None + """The error from the tool call, if any.""" + + output: Optional[str] = None + """The output from the tool call.""" + + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +class CompactionTriggerAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CompactionTrigger(BaseModel): + """Compacts the current context. Must be the final input item.""" + + type: Literal["compaction_trigger"] + """The type of the item. Always `compaction_trigger`.""" + + agent: Optional[CompactionTriggerAgent] = None + """The agent that produced this item.""" + + +class ItemReferenceAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ItemReference(BaseModel): + """An internal identifier for an item to reference.""" + + id: str + """The ID of the item to reference.""" + + agent: Optional[ItemReferenceAgent] = None + """The agent that produced this item.""" + + type: Optional[Literal["item_reference"]] = None + """The type of item to reference. Always `item_reference`.""" + + +class ProgramAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Program(BaseModel): + id: str + """The unique ID of this program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The item type. Always `program`.""" + + agent: Optional[ProgramAgent] = None + """The agent that produced this item.""" + + +class ProgramOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of this program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output.""" + + type: Literal["program_output"] + """The item type. Always `program_output`.""" + + agent: Optional[ProgramOutputAgent] = None + """The agent that produced this item.""" + + +BetaResponseInputItem: TypeAlias = Annotated[ + Union[ + BetaEasyInputMessage, + Message, + BetaResponseOutputMessage, + BetaResponseFileSearchToolCall, + BetaResponseComputerToolCall, + ComputerCallOutput, + BetaResponseFunctionWebSearch, + BetaResponseFunctionToolCall, + FunctionCallOutput, + AgentMessage, + MultiAgentCall, + MultiAgentCallOutput, + ToolSearchCall, + BetaResponseToolSearchOutputItemParam, + AdditionalTools, + BetaResponseConfigurationUpdateItemParam, + BetaResponseReasoningItem, + BetaResponseCompactionItemParam, + ImageGenerationCall, + BetaResponseCodeInterpreterToolCall, + LocalShellCall, + LocalShellCallOutput, + ShellCall, + ShellCallOutput, + ApplyPatchCall, + ApplyPatchCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + BetaResponseCustomToolCallOutput, + BetaResponseCustomToolCall, + CompactionTrigger, + ItemReference, + Program, + ProgramOutput, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_item_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_item_param.py new file mode 100644 index 0000000..f727063 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_item_param.py @@ -0,0 +1,1181 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .beta_tool_param import BetaToolParam +from .beta_local_environment_param import BetaLocalEnvironmentParam +from .beta_easy_input_message_param import BetaEasyInputMessageParam +from .beta_container_reference_param import BetaContainerReferenceParam +from .beta_mcp_tool_call_error_param import BetaMcpToolCallErrorParam +from .beta_response_output_message_param import BetaResponseOutputMessageParam +from .beta_response_reasoning_item_param import BetaResponseReasoningItemParam +from .beta_response_custom_tool_call_param import BetaResponseCustomToolCallParam +from .beta_response_computer_tool_call_param import BetaResponseComputerToolCallParam +from .beta_response_function_tool_call_param import BetaResponseFunctionToolCallParam +from .beta_response_input_text_content_param import BetaResponseInputTextContentParam +from .beta_response_function_web_search_param import BetaResponseFunctionWebSearchParam +from .beta_response_input_image_content_param import BetaResponseInputImageContentParam +from .beta_response_compaction_item_param_param import BetaResponseCompactionItemParamParam +from .beta_response_file_search_tool_call_param import BetaResponseFileSearchToolCallParam +from .beta_response_custom_tool_call_output_param import BetaResponseCustomToolCallOutputParam +from .beta_response_code_interpreter_tool_call_param import BetaResponseCodeInterpreterToolCallParam +from .beta_response_input_message_content_list_param import BetaResponseInputMessageContentListParam +from .beta_response_tool_search_output_item_param_param import BetaResponseToolSearchOutputItemParamParam +from .beta_response_function_call_output_item_list_param import BetaResponseFunctionCallOutputItemListParam +from .beta_response_configuration_update_item_param_param import BetaResponseConfigurationUpdateItemParamParam +from .beta_response_function_shell_call_output_content_param import BetaResponseFunctionShellCallOutputContentParam +from .beta_response_computer_tool_call_output_screenshot_param import BetaResponseComputerToolCallOutputScreenshotParam + +__all__ = [ + "BetaResponseInputItemParam", + "Message", + "MessageAgent", + "ComputerCallOutput", + "ComputerCallOutputAcknowledgedSafetyCheck", + "ComputerCallOutputAgent", + "FunctionCallOutput", + "FunctionCallOutputAgent", + "FunctionCallOutputCaller", + "FunctionCallOutputCallerDirect", + "FunctionCallOutputCallerProgram", + "AgentMessage", + "AgentMessageContent", + "AgentMessageContentEncryptedContent", + "AgentMessageAgent", + "MultiAgentCall", + "MultiAgentCallAgent", + "MultiAgentCallOutput", + "MultiAgentCallOutputOutput", + "MultiAgentCallOutputOutputAnnotation", + "MultiAgentCallOutputOutputAnnotationFileCitation", + "MultiAgentCallOutputOutputAnnotationURLCitation", + "MultiAgentCallOutputOutputAnnotationContainerFileCitation", + "MultiAgentCallOutputAgent", + "ToolSearchCall", + "ToolSearchCallAgent", + "AdditionalTools", + "AdditionalToolsAgent", + "ImageGenerationCall", + "ImageGenerationCallAgent", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallAgent", + "LocalShellCallOutput", + "LocalShellCallOutputAgent", + "ShellCall", + "ShellCallAction", + "ShellCallAgent", + "ShellCallCaller", + "ShellCallCallerDirect", + "ShellCallCallerProgram", + "ShellCallEnvironment", + "ShellCallOutput", + "ShellCallOutputAgent", + "ShellCallOutputCaller", + "ShellCallOutputCallerDirect", + "ShellCallOutputCallerProgram", + "ApplyPatchCall", + "ApplyPatchCallOperation", + "ApplyPatchCallOperationCreateFile", + "ApplyPatchCallOperationDeleteFile", + "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallAgent", + "ApplyPatchCallCaller", + "ApplyPatchCallCallerDirect", + "ApplyPatchCallCallerProgram", + "ApplyPatchCallOutput", + "ApplyPatchCallOutputAgent", + "ApplyPatchCallOutputCaller", + "ApplyPatchCallOutputCallerDirect", + "ApplyPatchCallOutputCallerProgram", + "McpListTools", + "McpListToolsTool", + "McpListToolsAgent", + "McpApprovalRequest", + "McpApprovalRequestAgent", + "McpApprovalResponse", + "McpApprovalResponseAgent", + "McpCall", + "McpCallAgent", + "CompactionTrigger", + "CompactionTriggerAgent", + "ItemReference", + "ItemReferenceAgent", + "Program", + "ProgramAgent", + "ProgramOutput", + "ProgramOutputAgent", +] + + +class MessageAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class Message(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. + """ + + content: Required[BetaResponseInputMessageContentListParam] + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Required[Literal["user", "system", "developer"]] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + agent: Optional[MessageAgent] + """The agent that produced this item.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["message"] + """The type of the message input. Always set to `message`.""" + + +class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): + """A pending safety check for the computer call.""" + + id: Required[str] + """The ID of the pending safety check.""" + + code: Optional[str] + """The type of the pending safety check.""" + + message: Optional[str] + """Details about the pending safety check.""" + + +class ComputerCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ComputerCallOutput(TypedDict, total=False): + """The output of a computer tool call.""" + + call_id: Required[str] + """The ID of the computer tool call that produced the output.""" + + output: Required[BetaResponseComputerToolCallOutputScreenshotParam] + """A computer screenshot image used with the computer use tool.""" + + type: Required[Literal["computer_call_output"]] + """The type of the computer tool call output. Always `computer_call_output`.""" + + id: Optional[str] + """The ID of the computer tool call output.""" + + acknowledged_safety_checks: Optional[Iterable[ComputerCallOutputAcknowledgedSafetyCheck]] + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + agent: Optional[ComputerCallOutputAgent] + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + +class FunctionCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class FunctionCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class FunctionCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +FunctionCallOutputCaller: TypeAlias = Union[FunctionCallOutputCallerDirect, FunctionCallOutputCallerProgram] + + +class FunctionCallOutput(TypedDict, total=False): + """The output of a function tool call.""" + + output: Required[Union[str, BetaResponseFunctionCallOutputItemListParam]] + """Text, image, or file output of the function tool call.""" + + type: Required[Literal["function_call_output"]] + """The type of the function tool call output. Always `function_call_output`.""" + + id: Optional[str] + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[FunctionCallOutputAgent] + """The agent that produced this item.""" + + call_id: Optional[str] + """The unique ID of the function tool call generated by the model.""" + + caller: Optional[FunctionCallOutputCaller] + """The execution context that produced this tool call.""" + + name: Optional[str] + """The name of the tool that produced the output.""" + + namespace: Optional[str] + """The namespace of the tool that produced the output.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +class AgentMessageContentEncryptedContent(TypedDict, total=False): + """ + Opaque encrypted content that Responses API decrypts inside trusted model execution. + """ + + encrypted_content: Required[str] + """Opaque encrypted content.""" + + type: Required[Literal["encrypted_content"]] + """The type of the input item. Always `encrypted_content`.""" + + +AgentMessageContent: TypeAlias = Union[ + BetaResponseInputTextContentParam, BetaResponseInputImageContentParam, AgentMessageContentEncryptedContent +] + + +class AgentMessageAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class AgentMessage(TypedDict, total=False): + """A message routed between agents.""" + + author: Required[str] + """The sending agent identity.""" + + content: Required[Iterable[AgentMessageContent]] + """Plaintext, image, or encrypted content sent between agents.""" + + recipient: Required[str] + """The destination agent identity.""" + + type: Required[Literal["agent_message"]] + """The item type. Always `agent_message`.""" + + id: Optional[str] + """The unique ID of this agent message item.""" + + agent: Optional[AgentMessageAgent] + """The agent that produced this item.""" + + +class MultiAgentCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCall(TypedDict, total=False): + action: Required[ + Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + ] + """The multi-agent action that was executed.""" + + arguments: Required[str] + """The action arguments as a JSON string.""" + + call_id: Required[str] + """The unique ID linking this call to its output.""" + + type: Required[Literal["multi_agent_call"]] + """The item type. Always `multi_agent_call`.""" + + id: Optional[str] + """The unique ID of this multi-agent call.""" + + agent: Optional[MultiAgentCallAgent] + """The agent that produced this item.""" + + +class MultiAgentCallOutputOutputAnnotationFileCitation(TypedDict, total=False): + file_id: Required[str] + """The ID of the file.""" + + filename: Required[str] + """The filename of the file cited.""" + + index: Required[int] + """The index of the file in the list of files.""" + + type: Required[Literal["file_citation"]] + """The citation type. Always `file_citation`.""" + + +class MultiAgentCallOutputOutputAnnotationURLCitation(TypedDict, total=False): + end_index: Required[int] + """The index of the last character of the citation in the message.""" + + start_index: Required[int] + """The index of the first character of the citation in the message.""" + + title: Required[str] + """The title of the cited resource.""" + + type: Required[Literal["url_citation"]] + """The citation type. Always `url_citation`.""" + + url: Required[str] + """The URL of the cited resource.""" + + +class MultiAgentCallOutputOutputAnnotationContainerFileCitation(TypedDict, total=False): + container_id: Required[str] + """The ID of the container.""" + + end_index: Required[int] + """The index of the last character of the citation in the message.""" + + file_id: Required[str] + """The ID of the container file.""" + + filename: Required[str] + """The filename of the container file cited.""" + + start_index: Required[int] + """The index of the first character of the citation in the message.""" + + type: Required[Literal["container_file_citation"]] + """The citation type. Always `container_file_citation`.""" + + +MultiAgentCallOutputOutputAnnotation: TypeAlias = Union[ + MultiAgentCallOutputOutputAnnotationFileCitation, + MultiAgentCallOutputOutputAnnotationURLCitation, + MultiAgentCallOutputOutputAnnotationContainerFileCitation, +] + + +class MultiAgentCallOutputOutput(TypedDict, total=False): + text: Required[str] + """The text content.""" + + type: Required[Literal["output_text"]] + """The content type. Always `output_text`.""" + + annotations: Iterable[MultiAgentCallOutputOutputAnnotation] + """Citations associated with the text content.""" + + +class MultiAgentCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCallOutput(TypedDict, total=False): + action: Required[ + Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + ] + """The multi-agent action that produced this result.""" + + call_id: Required[str] + """The unique ID of the multi-agent call.""" + + output: Required[Iterable[MultiAgentCallOutputOutput]] + """Text output returned by the multi-agent action.""" + + type: Required[Literal["multi_agent_call_output"]] + """The item type. Always `multi_agent_call_output`.""" + + id: Optional[str] + """The unique ID of this multi-agent call output.""" + + agent: Optional[MultiAgentCallOutputAgent] + """The agent that produced this item.""" + + +class ToolSearchCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ToolSearchCall(TypedDict, total=False): + arguments: Required[object] + """The arguments supplied to the tool search call.""" + + type: Required[Literal["tool_search_call"]] + """The item type. Always `tool_search_call`.""" + + id: Optional[str] + """The unique ID of this tool search call.""" + + agent: Optional[ToolSearchCallAgent] + """The agent that produced this item.""" + + call_id: Optional[str] + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the tool search call.""" + + +class AdditionalToolsAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class AdditionalTools(TypedDict, total=False): + role: Required[Literal["developer"]] + """The role that provided the additional tools. Only `developer` is supported.""" + + tools: Required[Iterable[BetaToolParam]] + """A list of additional tools made available at this item.""" + + type: Required[Literal["additional_tools"]] + """The item type. Always `additional_tools`.""" + + id: Optional[str] + """The unique ID of this additional tools item.""" + + agent: Optional[AdditionalToolsAgent] + """The agent that produced this item.""" + + +class ImageGenerationCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ImageGenerationCall(TypedDict, total=False): + """An image generation request made by the model.""" + + id: Required[str] + """The unique ID of the image generation call.""" + + result: Required[Optional[str]] + """The generated image encoded in base64.""" + + status: Required[Literal["in_progress", "completed", "generating", "failed"]] + """The status of the image generation call.""" + + type: Required[Literal["image_generation_call"]] + """The type of the image generation call. Always `image_generation_call`.""" + + action: Optional[Literal["generate", "edit", "auto"]] + """The action used for image generation.""" + + agent: Optional[ImageGenerationCallAgent] + """The agent that produced this item.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] + """The background setting used for generation.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] + """The output format used for generation.""" + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] + """The quality of the image generated by the image generation tool call. + + One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`. + """ + + revised_prompt: Optional[str] + """The prompt that was used after any model prompt rewriting.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + +class LocalShellCallAction(TypedDict, total=False): + """Execute a shell command on the server.""" + + command: Required[SequenceNotStr[str]] + """The command to run.""" + + env: Required[Dict[str, str]] + """Environment variables to set for the command.""" + + type: Required[Literal["exec"]] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] + """Optional user to run the command as.""" + + working_directory: Optional[str] + """Optional working directory to run the command in.""" + + +class LocalShellCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class LocalShellCall(TypedDict, total=False): + """A tool call to run a command on the local shell.""" + + id: Required[str] + """The unique ID of the local shell call.""" + + action: Required[LocalShellCallAction] + """Execute a shell command on the server.""" + + call_id: Required[str] + """The unique ID of the local shell tool call generated by the model.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the local shell call.""" + + type: Required[Literal["local_shell_call"]] + """The type of the local shell call. Always `local_shell_call`.""" + + agent: Optional[LocalShellCallAgent] + """The agent that produced this item.""" + + +class LocalShellCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class LocalShellCallOutput(TypedDict, total=False): + """The output of a local shell tool call.""" + + id: Required[str] + """The unique ID of the local shell tool call generated by the model.""" + + output: Required[str] + """A JSON string of the output of the local shell tool call.""" + + type: Required[Literal["local_shell_call_output"]] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + agent: Optional[LocalShellCallOutputAgent] + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class ShellCallAction(TypedDict, total=False): + """The shell commands and limits that describe how to run the tool call.""" + + commands: Required[SequenceNotStr[str]] + """Ordered shell commands for the execution environment to run.""" + + max_output_length: Optional[int] + """ + Maximum number of UTF-8 characters to capture from combined stdout and stderr + output. + """ + + timeout_ms: Optional[int] + """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" + + +class ShellCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ShellCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallCaller: TypeAlias = Union[ShellCallCallerDirect, ShellCallCallerProgram] + +ShellCallEnvironment: TypeAlias = Union[BetaLocalEnvironmentParam, BetaContainerReferenceParam] + + +class ShellCall(TypedDict, total=False): + """A tool representing a request to execute one or more shell commands.""" + + action: Required[ShellCallAction] + """The shell commands and limits that describe how to run the tool call.""" + + call_id: Required[str] + """The unique ID of the shell tool call generated by the model.""" + + type: Required[Literal["shell_call"]] + """The type of the item. Always `shell_call`.""" + + id: Optional[str] + """The unique ID of the shell tool call. + + Populated when this item is returned via API. + """ + + agent: Optional[ShellCallAgent] + """The agent that produced this item.""" + + caller: Optional[ShellCallCaller] + """The execution context that produced this tool call.""" + + environment: Optional[ShellCallEnvironment] + """The environment to execute the shell commands in.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + +class ShellCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ShellCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallOutputCaller: TypeAlias = Union[ShellCallOutputCallerDirect, ShellCallOutputCallerProgram] + + +class ShellCallOutput(TypedDict, total=False): + """The streamed output items emitted by a shell tool call.""" + + call_id: Required[str] + """The unique ID of the shell tool call generated by the model.""" + + output: Required[Iterable[BetaResponseFunctionShellCallOutputContentParam]] + """ + Captured chunks of stdout and stderr output, along with their associated + outcomes. + """ + + type: Required[Literal["shell_call_output"]] + """The type of the item. Always `shell_call_output`.""" + + id: Optional[str] + """The unique ID of the shell tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ShellCallOutputAgent] + """The agent that produced this item.""" + + caller: Optional[ShellCallOutputCaller] + """The execution context that produced this tool call.""" + + max_output_length: Optional[int] + """ + The maximum number of UTF-8 characters captured for this shell call's combined + output. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call output.""" + + +class ApplyPatchCallOperationCreateFile(TypedDict, total=False): + """Instruction for creating a new file via the apply_patch tool.""" + + diff: Required[str] + """Unified diff content to apply when creating the file.""" + + path: Required[str] + """Path of the file to create relative to the workspace root.""" + + type: Required[Literal["create_file"]] + """The operation type. Always `create_file`.""" + + +class ApplyPatchCallOperationDeleteFile(TypedDict, total=False): + """Instruction for deleting an existing file via the apply_patch tool.""" + + path: Required[str] + """Path of the file to delete relative to the workspace root.""" + + type: Required[Literal["delete_file"]] + """The operation type. Always `delete_file`.""" + + +class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): + """Instruction for updating an existing file via the apply_patch tool.""" + + diff: Required[str] + """Unified diff content to apply to the existing file.""" + + path: Required[str] + """Path of the file to update relative to the workspace root.""" + + type: Required[Literal["update_file"]] + """The operation type. Always `update_file`.""" + + +ApplyPatchCallOperation: TypeAlias = Union[ + ApplyPatchCallOperationCreateFile, ApplyPatchCallOperationDeleteFile, ApplyPatchCallOperationUpdateFile +] + + +class ApplyPatchCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ApplyPatchCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallCaller: TypeAlias = Union[ApplyPatchCallCallerDirect, ApplyPatchCallCallerProgram] + + +class ApplyPatchCall(TypedDict, total=False): + """ + A tool call representing a request to create, delete, or update files using diff patches. + """ + + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + operation: Required[ApplyPatchCallOperation] + """ + The specific create, delete, or update instruction for the apply_patch tool + call. + """ + + status: Required[Literal["in_progress", "completed"]] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Required[Literal["apply_patch_call"]] + """The type of the item. Always `apply_patch_call`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + agent: Optional[ApplyPatchCallAgent] + """The agent that produced this item.""" + + caller: Optional[ApplyPatchCallCaller] + """The execution context that produced this tool call.""" + + +class ApplyPatchCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ApplyPatchCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallOutputCaller: TypeAlias = Union[ApplyPatchCallOutputCallerDirect, ApplyPatchCallOutputCallerProgram] + + +class ApplyPatchCallOutput(TypedDict, total=False): + """The streamed output emitted by an apply patch tool call.""" + + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + status: Required[Literal["completed", "failed"]] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Required[Literal["apply_patch_call_output"]] + """The type of the item. Always `apply_patch_call_output`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ApplyPatchCallOutputAgent] + """The agent that produced this item.""" + + caller: Optional[ApplyPatchCallOutputCaller] + """The execution context that produced this tool call.""" + + output: Optional[str] + """ + Optional human-readable log text from the apply patch tool (e.g., patch results + or errors). + """ + + +class McpListToolsTool(TypedDict, total=False): + """A tool available on an MCP server.""" + + input_schema: Required[object] + """The JSON schema describing the tool's input.""" + + name: Required[str] + """The name of the tool.""" + + annotations: Optional[object] + """Additional annotations about the tool.""" + + description: Optional[str] + """The description of the tool.""" + + +class McpListToolsAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpListTools(TypedDict, total=False): + """A list of tools available on an MCP server.""" + + id: Required[str] + """The unique ID of the list.""" + + server_label: Required[str] + """The label of the MCP server.""" + + tools: Required[Iterable[McpListToolsTool]] + """The tools available on the server.""" + + type: Required[Literal["mcp_list_tools"]] + """The type of the item. Always `mcp_list_tools`.""" + + agent: Optional[McpListToolsAgent] + """The agent that produced this item.""" + + error: Optional[str] + """Error message if the server could not list tools.""" + + +class McpApprovalRequestAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpApprovalRequest(TypedDict, total=False): + """A request for human approval of a tool invocation.""" + + id: Required[str] + """The unique ID of the approval request.""" + + arguments: Required[str] + """A JSON string of arguments for the tool.""" + + name: Required[str] + """The name of the tool to run.""" + + server_label: Required[str] + """The label of the MCP server making the request.""" + + type: Required[Literal["mcp_approval_request"]] + """The type of the item. Always `mcp_approval_request`.""" + + agent: Optional[McpApprovalRequestAgent] + """The agent that produced this item.""" + + +class McpApprovalResponseAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpApprovalResponse(TypedDict, total=False): + """A response to an MCP approval request.""" + + approval_request_id: Required[str] + """The ID of the approval request being answered.""" + + approve: Required[bool] + """Whether the request was approved.""" + + type: Required[Literal["mcp_approval_response"]] + """The type of the item. Always `mcp_approval_response`.""" + + id: Optional[str] + """The unique ID of the approval response""" + + agent: Optional[McpApprovalResponseAgent] + """The agent that produced this item.""" + + reason: Optional[str] + """Optional reason for the decision.""" + + +class McpCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpCall(TypedDict, total=False): + """An invocation of a tool on an MCP server.""" + + id: Required[str] + """The unique ID of the tool call.""" + + arguments: Required[str] + """A JSON string of the arguments passed to the tool.""" + + name: Required[str] + """The name of the tool that was run.""" + + server_label: Required[str] + """The label of the MCP server running the tool.""" + + type: Required[Literal["mcp_call"]] + """The type of the item. Always `mcp_call`.""" + + agent: Optional[McpCallAgent] + """The agent that produced this item.""" + + approval_request_id: Optional[str] + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[BetaMcpToolCallErrorParam] + """The error from the tool call, if any.""" + + output: Optional[str] + """The output from the tool call.""" + + status: Literal["in_progress", "completed", "incomplete", "calling", "failed"] + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +class CompactionTriggerAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class CompactionTrigger(TypedDict, total=False): + """Compacts the current context. Must be the final input item.""" + + type: Required[Literal["compaction_trigger"]] + """The type of the item. Always `compaction_trigger`.""" + + agent: Optional[CompactionTriggerAgent] + """The agent that produced this item.""" + + +class ItemReferenceAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ItemReference(TypedDict, total=False): + """An internal identifier for an item to reference.""" + + id: Required[str] + """The ID of the item to reference.""" + + agent: Optional[ItemReferenceAgent] + """The agent that produced this item.""" + + type: Optional[Literal["item_reference"]] + """The type of item to reference. Always `item_reference`.""" + + +class ProgramAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class Program(TypedDict, total=False): + id: Required[str] + """The unique ID of this program item.""" + + call_id: Required[str] + """The stable call ID of the program item.""" + + code: Required[str] + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: Required[str] + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Required[Literal["program"]] + """The item type. Always `program`.""" + + agent: Optional[ProgramAgent] + """The agent that produced this item.""" + + +class ProgramOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ProgramOutput(TypedDict, total=False): + id: Required[str] + """The unique ID of this program output item.""" + + call_id: Required[str] + """The call ID of the program item.""" + + result: Required[str] + """The result produced by the program item.""" + + status: Required[Literal["completed", "incomplete"]] + """The terminal status of the program output.""" + + type: Required[Literal["program_output"]] + """The item type. Always `program_output`.""" + + agent: Optional[ProgramOutputAgent] + """The agent that produced this item.""" + + +BetaResponseInputItemParam: TypeAlias = Union[ + BetaEasyInputMessageParam, + Message, + BetaResponseOutputMessageParam, + BetaResponseFileSearchToolCallParam, + BetaResponseComputerToolCallParam, + ComputerCallOutput, + BetaResponseFunctionWebSearchParam, + BetaResponseFunctionToolCallParam, + FunctionCallOutput, + AgentMessage, + MultiAgentCall, + MultiAgentCallOutput, + ToolSearchCall, + BetaResponseToolSearchOutputItemParamParam, + AdditionalTools, + BetaResponseConfigurationUpdateItemParamParam, + BetaResponseReasoningItemParam, + BetaResponseCompactionItemParamParam, + ImageGenerationCall, + BetaResponseCodeInterpreterToolCallParam, + LocalShellCall, + LocalShellCallOutput, + ShellCall, + ShellCallOutput, + ApplyPatchCall, + ApplyPatchCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + BetaResponseCustomToolCallOutputParam, + BetaResponseCustomToolCallParam, + CompactionTrigger, + ItemReference, + Program, + ProgramOutput, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_message_content_list.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_message_content_list.py new file mode 100644 index 0000000..ad67484 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_message_content_list.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .beta_response_input_content import BetaResponseInputContent + +__all__ = ["BetaResponseInputMessageContentList"] + +BetaResponseInputMessageContentList: TypeAlias = List[BetaResponseInputContent] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_message_content_list_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_message_content_list_param.py new file mode 100644 index 0000000..b6949e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_message_content_list_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import TypeAlias + +from .beta_response_input_file_param import BetaResponseInputFileParam +from .beta_response_input_text_param import BetaResponseInputTextParam +from .beta_response_input_image_param import BetaResponseInputImageParam + +__all__ = ["BetaResponseInputMessageContentListParam", "BetaResponseInputContentParam"] + +BetaResponseInputContentParam: TypeAlias = Union[ + BetaResponseInputTextParam, BetaResponseInputImageParam, BetaResponseInputFileParam +] + +BetaResponseInputMessageContentListParam: TypeAlias = List[BetaResponseInputContentParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_message_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_message_item.py new file mode 100644 index 0000000..175ebba --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_message_item.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_input_message_content_list import BetaResponseInputMessageContentList + +__all__ = ["BetaResponseInputMessageItem", "Agent"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseInputMessageItem(BaseModel): + id: str + """The unique ID of the message input.""" + + content: BetaResponseInputMessageContentList + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Literal["user", "system", "developer"] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + type: Literal["message"] + """The type of the message input. Always set to `message`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_param.py new file mode 100644 index 0000000..2f640b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_param.py @@ -0,0 +1,1184 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .beta_tool_param import BetaToolParam +from .beta_local_environment_param import BetaLocalEnvironmentParam +from .beta_easy_input_message_param import BetaEasyInputMessageParam +from .beta_container_reference_param import BetaContainerReferenceParam +from .beta_mcp_tool_call_error_param import BetaMcpToolCallErrorParam +from .beta_response_output_message_param import BetaResponseOutputMessageParam +from .beta_response_reasoning_item_param import BetaResponseReasoningItemParam +from .beta_response_custom_tool_call_param import BetaResponseCustomToolCallParam +from .beta_response_computer_tool_call_param import BetaResponseComputerToolCallParam +from .beta_response_function_tool_call_param import BetaResponseFunctionToolCallParam +from .beta_response_input_text_content_param import BetaResponseInputTextContentParam +from .beta_response_function_web_search_param import BetaResponseFunctionWebSearchParam +from .beta_response_input_image_content_param import BetaResponseInputImageContentParam +from .beta_response_compaction_item_param_param import BetaResponseCompactionItemParamParam +from .beta_response_file_search_tool_call_param import BetaResponseFileSearchToolCallParam +from .beta_response_custom_tool_call_output_param import BetaResponseCustomToolCallOutputParam +from .beta_response_code_interpreter_tool_call_param import BetaResponseCodeInterpreterToolCallParam +from .beta_response_input_message_content_list_param import BetaResponseInputMessageContentListParam +from .beta_response_tool_search_output_item_param_param import BetaResponseToolSearchOutputItemParamParam +from .beta_response_function_call_output_item_list_param import BetaResponseFunctionCallOutputItemListParam +from .beta_response_configuration_update_item_param_param import BetaResponseConfigurationUpdateItemParamParam +from .beta_response_function_shell_call_output_content_param import BetaResponseFunctionShellCallOutputContentParam +from .beta_response_computer_tool_call_output_screenshot_param import BetaResponseComputerToolCallOutputScreenshotParam + +__all__ = [ + "BetaResponseInputParam", + "BetaResponseInputItemParam", + "Message", + "MessageAgent", + "ComputerCallOutput", + "ComputerCallOutputAcknowledgedSafetyCheck", + "ComputerCallOutputAgent", + "FunctionCallOutput", + "FunctionCallOutputAgent", + "FunctionCallOutputCaller", + "FunctionCallOutputCallerDirect", + "FunctionCallOutputCallerProgram", + "AgentMessage", + "AgentMessageContent", + "AgentMessageContentEncryptedContent", + "AgentMessageAgent", + "MultiAgentCall", + "MultiAgentCallAgent", + "MultiAgentCallOutput", + "MultiAgentCallOutputOutput", + "MultiAgentCallOutputOutputAnnotation", + "MultiAgentCallOutputOutputAnnotationFileCitation", + "MultiAgentCallOutputOutputAnnotationURLCitation", + "MultiAgentCallOutputOutputAnnotationContainerFileCitation", + "MultiAgentCallOutputAgent", + "ToolSearchCall", + "ToolSearchCallAgent", + "AdditionalTools", + "AdditionalToolsAgent", + "ImageGenerationCall", + "ImageGenerationCallAgent", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallAgent", + "LocalShellCallOutput", + "LocalShellCallOutputAgent", + "ShellCall", + "ShellCallAction", + "ShellCallAgent", + "ShellCallCaller", + "ShellCallCallerDirect", + "ShellCallCallerProgram", + "ShellCallEnvironment", + "ShellCallOutput", + "ShellCallOutputAgent", + "ShellCallOutputCaller", + "ShellCallOutputCallerDirect", + "ShellCallOutputCallerProgram", + "ApplyPatchCall", + "ApplyPatchCallOperation", + "ApplyPatchCallOperationCreateFile", + "ApplyPatchCallOperationDeleteFile", + "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallAgent", + "ApplyPatchCallCaller", + "ApplyPatchCallCallerDirect", + "ApplyPatchCallCallerProgram", + "ApplyPatchCallOutput", + "ApplyPatchCallOutputAgent", + "ApplyPatchCallOutputCaller", + "ApplyPatchCallOutputCallerDirect", + "ApplyPatchCallOutputCallerProgram", + "McpListTools", + "McpListToolsTool", + "McpListToolsAgent", + "McpApprovalRequest", + "McpApprovalRequestAgent", + "McpApprovalResponse", + "McpApprovalResponseAgent", + "McpCall", + "McpCallAgent", + "CompactionTrigger", + "CompactionTriggerAgent", + "ItemReference", + "ItemReferenceAgent", + "Program", + "ProgramAgent", + "ProgramOutput", + "ProgramOutputAgent", +] + + +class MessageAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class Message(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. + """ + + content: Required[BetaResponseInputMessageContentListParam] + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Required[Literal["user", "system", "developer"]] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + agent: Optional[MessageAgent] + """The agent that produced this item.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["message"] + """The type of the message input. Always set to `message`.""" + + +class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): + """A pending safety check for the computer call.""" + + id: Required[str] + """The ID of the pending safety check.""" + + code: Optional[str] + """The type of the pending safety check.""" + + message: Optional[str] + """Details about the pending safety check.""" + + +class ComputerCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ComputerCallOutput(TypedDict, total=False): + """The output of a computer tool call.""" + + call_id: Required[str] + """The ID of the computer tool call that produced the output.""" + + output: Required[BetaResponseComputerToolCallOutputScreenshotParam] + """A computer screenshot image used with the computer use tool.""" + + type: Required[Literal["computer_call_output"]] + """The type of the computer tool call output. Always `computer_call_output`.""" + + id: Optional[str] + """The ID of the computer tool call output.""" + + acknowledged_safety_checks: Optional[Iterable[ComputerCallOutputAcknowledgedSafetyCheck]] + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + agent: Optional[ComputerCallOutputAgent] + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + +class FunctionCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class FunctionCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class FunctionCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +FunctionCallOutputCaller: TypeAlias = Union[FunctionCallOutputCallerDirect, FunctionCallOutputCallerProgram] + + +class FunctionCallOutput(TypedDict, total=False): + """The output of a function tool call.""" + + output: Required[Union[str, BetaResponseFunctionCallOutputItemListParam]] + """Text, image, or file output of the function tool call.""" + + type: Required[Literal["function_call_output"]] + """The type of the function tool call output. Always `function_call_output`.""" + + id: Optional[str] + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[FunctionCallOutputAgent] + """The agent that produced this item.""" + + call_id: Optional[str] + """The unique ID of the function tool call generated by the model.""" + + caller: Optional[FunctionCallOutputCaller] + """The execution context that produced this tool call.""" + + name: Optional[str] + """The name of the tool that produced the output.""" + + namespace: Optional[str] + """The namespace of the tool that produced the output.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +class AgentMessageContentEncryptedContent(TypedDict, total=False): + """ + Opaque encrypted content that Responses API decrypts inside trusted model execution. + """ + + encrypted_content: Required[str] + """Opaque encrypted content.""" + + type: Required[Literal["encrypted_content"]] + """The type of the input item. Always `encrypted_content`.""" + + +AgentMessageContent: TypeAlias = Union[ + BetaResponseInputTextContentParam, BetaResponseInputImageContentParam, AgentMessageContentEncryptedContent +] + + +class AgentMessageAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class AgentMessage(TypedDict, total=False): + """A message routed between agents.""" + + author: Required[str] + """The sending agent identity.""" + + content: Required[Iterable[AgentMessageContent]] + """Plaintext, image, or encrypted content sent between agents.""" + + recipient: Required[str] + """The destination agent identity.""" + + type: Required[Literal["agent_message"]] + """The item type. Always `agent_message`.""" + + id: Optional[str] + """The unique ID of this agent message item.""" + + agent: Optional[AgentMessageAgent] + """The agent that produced this item.""" + + +class MultiAgentCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCall(TypedDict, total=False): + action: Required[ + Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + ] + """The multi-agent action that was executed.""" + + arguments: Required[str] + """The action arguments as a JSON string.""" + + call_id: Required[str] + """The unique ID linking this call to its output.""" + + type: Required[Literal["multi_agent_call"]] + """The item type. Always `multi_agent_call`.""" + + id: Optional[str] + """The unique ID of this multi-agent call.""" + + agent: Optional[MultiAgentCallAgent] + """The agent that produced this item.""" + + +class MultiAgentCallOutputOutputAnnotationFileCitation(TypedDict, total=False): + file_id: Required[str] + """The ID of the file.""" + + filename: Required[str] + """The filename of the file cited.""" + + index: Required[int] + """The index of the file in the list of files.""" + + type: Required[Literal["file_citation"]] + """The citation type. Always `file_citation`.""" + + +class MultiAgentCallOutputOutputAnnotationURLCitation(TypedDict, total=False): + end_index: Required[int] + """The index of the last character of the citation in the message.""" + + start_index: Required[int] + """The index of the first character of the citation in the message.""" + + title: Required[str] + """The title of the cited resource.""" + + type: Required[Literal["url_citation"]] + """The citation type. Always `url_citation`.""" + + url: Required[str] + """The URL of the cited resource.""" + + +class MultiAgentCallOutputOutputAnnotationContainerFileCitation(TypedDict, total=False): + container_id: Required[str] + """The ID of the container.""" + + end_index: Required[int] + """The index of the last character of the citation in the message.""" + + file_id: Required[str] + """The ID of the container file.""" + + filename: Required[str] + """The filename of the container file cited.""" + + start_index: Required[int] + """The index of the first character of the citation in the message.""" + + type: Required[Literal["container_file_citation"]] + """The citation type. Always `container_file_citation`.""" + + +MultiAgentCallOutputOutputAnnotation: TypeAlias = Union[ + MultiAgentCallOutputOutputAnnotationFileCitation, + MultiAgentCallOutputOutputAnnotationURLCitation, + MultiAgentCallOutputOutputAnnotationContainerFileCitation, +] + + +class MultiAgentCallOutputOutput(TypedDict, total=False): + text: Required[str] + """The text content.""" + + type: Required[Literal["output_text"]] + """The content type. Always `output_text`.""" + + annotations: Iterable[MultiAgentCallOutputOutputAnnotation] + """Citations associated with the text content.""" + + +class MultiAgentCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCallOutput(TypedDict, total=False): + action: Required[ + Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + ] + """The multi-agent action that produced this result.""" + + call_id: Required[str] + """The unique ID of the multi-agent call.""" + + output: Required[Iterable[MultiAgentCallOutputOutput]] + """Text output returned by the multi-agent action.""" + + type: Required[Literal["multi_agent_call_output"]] + """The item type. Always `multi_agent_call_output`.""" + + id: Optional[str] + """The unique ID of this multi-agent call output.""" + + agent: Optional[MultiAgentCallOutputAgent] + """The agent that produced this item.""" + + +class ToolSearchCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ToolSearchCall(TypedDict, total=False): + arguments: Required[object] + """The arguments supplied to the tool search call.""" + + type: Required[Literal["tool_search_call"]] + """The item type. Always `tool_search_call`.""" + + id: Optional[str] + """The unique ID of this tool search call.""" + + agent: Optional[ToolSearchCallAgent] + """The agent that produced this item.""" + + call_id: Optional[str] + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the tool search call.""" + + +class AdditionalToolsAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class AdditionalTools(TypedDict, total=False): + role: Required[Literal["developer"]] + """The role that provided the additional tools. Only `developer` is supported.""" + + tools: Required[Iterable[BetaToolParam]] + """A list of additional tools made available at this item.""" + + type: Required[Literal["additional_tools"]] + """The item type. Always `additional_tools`.""" + + id: Optional[str] + """The unique ID of this additional tools item.""" + + agent: Optional[AdditionalToolsAgent] + """The agent that produced this item.""" + + +class ImageGenerationCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ImageGenerationCall(TypedDict, total=False): + """An image generation request made by the model.""" + + id: Required[str] + """The unique ID of the image generation call.""" + + result: Required[Optional[str]] + """The generated image encoded in base64.""" + + status: Required[Literal["in_progress", "completed", "generating", "failed"]] + """The status of the image generation call.""" + + type: Required[Literal["image_generation_call"]] + """The type of the image generation call. Always `image_generation_call`.""" + + action: Optional[Literal["generate", "edit", "auto"]] + """The action used for image generation.""" + + agent: Optional[ImageGenerationCallAgent] + """The agent that produced this item.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] + """The background setting used for generation.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] + """The output format used for generation.""" + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] + """The quality of the image generated by the image generation tool call. + + One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`. + """ + + revised_prompt: Optional[str] + """The prompt that was used after any model prompt rewriting.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + +class LocalShellCallAction(TypedDict, total=False): + """Execute a shell command on the server.""" + + command: Required[SequenceNotStr[str]] + """The command to run.""" + + env: Required[Dict[str, str]] + """Environment variables to set for the command.""" + + type: Required[Literal["exec"]] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] + """Optional user to run the command as.""" + + working_directory: Optional[str] + """Optional working directory to run the command in.""" + + +class LocalShellCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class LocalShellCall(TypedDict, total=False): + """A tool call to run a command on the local shell.""" + + id: Required[str] + """The unique ID of the local shell call.""" + + action: Required[LocalShellCallAction] + """Execute a shell command on the server.""" + + call_id: Required[str] + """The unique ID of the local shell tool call generated by the model.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the local shell call.""" + + type: Required[Literal["local_shell_call"]] + """The type of the local shell call. Always `local_shell_call`.""" + + agent: Optional[LocalShellCallAgent] + """The agent that produced this item.""" + + +class LocalShellCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class LocalShellCallOutput(TypedDict, total=False): + """The output of a local shell tool call.""" + + id: Required[str] + """The unique ID of the local shell tool call generated by the model.""" + + output: Required[str] + """A JSON string of the output of the local shell tool call.""" + + type: Required[Literal["local_shell_call_output"]] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + agent: Optional[LocalShellCallOutputAgent] + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class ShellCallAction(TypedDict, total=False): + """The shell commands and limits that describe how to run the tool call.""" + + commands: Required[SequenceNotStr[str]] + """Ordered shell commands for the execution environment to run.""" + + max_output_length: Optional[int] + """ + Maximum number of UTF-8 characters to capture from combined stdout and stderr + output. + """ + + timeout_ms: Optional[int] + """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" + + +class ShellCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ShellCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallCaller: TypeAlias = Union[ShellCallCallerDirect, ShellCallCallerProgram] + +ShellCallEnvironment: TypeAlias = Union[BetaLocalEnvironmentParam, BetaContainerReferenceParam] + + +class ShellCall(TypedDict, total=False): + """A tool representing a request to execute one or more shell commands.""" + + action: Required[ShellCallAction] + """The shell commands and limits that describe how to run the tool call.""" + + call_id: Required[str] + """The unique ID of the shell tool call generated by the model.""" + + type: Required[Literal["shell_call"]] + """The type of the item. Always `shell_call`.""" + + id: Optional[str] + """The unique ID of the shell tool call. + + Populated when this item is returned via API. + """ + + agent: Optional[ShellCallAgent] + """The agent that produced this item.""" + + caller: Optional[ShellCallCaller] + """The execution context that produced this tool call.""" + + environment: Optional[ShellCallEnvironment] + """The environment to execute the shell commands in.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + +class ShellCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ShellCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallOutputCaller: TypeAlias = Union[ShellCallOutputCallerDirect, ShellCallOutputCallerProgram] + + +class ShellCallOutput(TypedDict, total=False): + """The streamed output items emitted by a shell tool call.""" + + call_id: Required[str] + """The unique ID of the shell tool call generated by the model.""" + + output: Required[Iterable[BetaResponseFunctionShellCallOutputContentParam]] + """ + Captured chunks of stdout and stderr output, along with their associated + outcomes. + """ + + type: Required[Literal["shell_call_output"]] + """The type of the item. Always `shell_call_output`.""" + + id: Optional[str] + """The unique ID of the shell tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ShellCallOutputAgent] + """The agent that produced this item.""" + + caller: Optional[ShellCallOutputCaller] + """The execution context that produced this tool call.""" + + max_output_length: Optional[int] + """ + The maximum number of UTF-8 characters captured for this shell call's combined + output. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call output.""" + + +class ApplyPatchCallOperationCreateFile(TypedDict, total=False): + """Instruction for creating a new file via the apply_patch tool.""" + + diff: Required[str] + """Unified diff content to apply when creating the file.""" + + path: Required[str] + """Path of the file to create relative to the workspace root.""" + + type: Required[Literal["create_file"]] + """The operation type. Always `create_file`.""" + + +class ApplyPatchCallOperationDeleteFile(TypedDict, total=False): + """Instruction for deleting an existing file via the apply_patch tool.""" + + path: Required[str] + """Path of the file to delete relative to the workspace root.""" + + type: Required[Literal["delete_file"]] + """The operation type. Always `delete_file`.""" + + +class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): + """Instruction for updating an existing file via the apply_patch tool.""" + + diff: Required[str] + """Unified diff content to apply to the existing file.""" + + path: Required[str] + """Path of the file to update relative to the workspace root.""" + + type: Required[Literal["update_file"]] + """The operation type. Always `update_file`.""" + + +ApplyPatchCallOperation: TypeAlias = Union[ + ApplyPatchCallOperationCreateFile, ApplyPatchCallOperationDeleteFile, ApplyPatchCallOperationUpdateFile +] + + +class ApplyPatchCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ApplyPatchCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallCaller: TypeAlias = Union[ApplyPatchCallCallerDirect, ApplyPatchCallCallerProgram] + + +class ApplyPatchCall(TypedDict, total=False): + """ + A tool call representing a request to create, delete, or update files using diff patches. + """ + + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + operation: Required[ApplyPatchCallOperation] + """ + The specific create, delete, or update instruction for the apply_patch tool + call. + """ + + status: Required[Literal["in_progress", "completed"]] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Required[Literal["apply_patch_call"]] + """The type of the item. Always `apply_patch_call`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + agent: Optional[ApplyPatchCallAgent] + """The agent that produced this item.""" + + caller: Optional[ApplyPatchCallCaller] + """The execution context that produced this tool call.""" + + +class ApplyPatchCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ApplyPatchCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallOutputCaller: TypeAlias = Union[ApplyPatchCallOutputCallerDirect, ApplyPatchCallOutputCallerProgram] + + +class ApplyPatchCallOutput(TypedDict, total=False): + """The streamed output emitted by an apply patch tool call.""" + + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + status: Required[Literal["completed", "failed"]] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Required[Literal["apply_patch_call_output"]] + """The type of the item. Always `apply_patch_call_output`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ApplyPatchCallOutputAgent] + """The agent that produced this item.""" + + caller: Optional[ApplyPatchCallOutputCaller] + """The execution context that produced this tool call.""" + + output: Optional[str] + """ + Optional human-readable log text from the apply patch tool (e.g., patch results + or errors). + """ + + +class McpListToolsTool(TypedDict, total=False): + """A tool available on an MCP server.""" + + input_schema: Required[object] + """The JSON schema describing the tool's input.""" + + name: Required[str] + """The name of the tool.""" + + annotations: Optional[object] + """Additional annotations about the tool.""" + + description: Optional[str] + """The description of the tool.""" + + +class McpListToolsAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpListTools(TypedDict, total=False): + """A list of tools available on an MCP server.""" + + id: Required[str] + """The unique ID of the list.""" + + server_label: Required[str] + """The label of the MCP server.""" + + tools: Required[Iterable[McpListToolsTool]] + """The tools available on the server.""" + + type: Required[Literal["mcp_list_tools"]] + """The type of the item. Always `mcp_list_tools`.""" + + agent: Optional[McpListToolsAgent] + """The agent that produced this item.""" + + error: Optional[str] + """Error message if the server could not list tools.""" + + +class McpApprovalRequestAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpApprovalRequest(TypedDict, total=False): + """A request for human approval of a tool invocation.""" + + id: Required[str] + """The unique ID of the approval request.""" + + arguments: Required[str] + """A JSON string of arguments for the tool.""" + + name: Required[str] + """The name of the tool to run.""" + + server_label: Required[str] + """The label of the MCP server making the request.""" + + type: Required[Literal["mcp_approval_request"]] + """The type of the item. Always `mcp_approval_request`.""" + + agent: Optional[McpApprovalRequestAgent] + """The agent that produced this item.""" + + +class McpApprovalResponseAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpApprovalResponse(TypedDict, total=False): + """A response to an MCP approval request.""" + + approval_request_id: Required[str] + """The ID of the approval request being answered.""" + + approve: Required[bool] + """Whether the request was approved.""" + + type: Required[Literal["mcp_approval_response"]] + """The type of the item. Always `mcp_approval_response`.""" + + id: Optional[str] + """The unique ID of the approval response""" + + agent: Optional[McpApprovalResponseAgent] + """The agent that produced this item.""" + + reason: Optional[str] + """Optional reason for the decision.""" + + +class McpCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpCall(TypedDict, total=False): + """An invocation of a tool on an MCP server.""" + + id: Required[str] + """The unique ID of the tool call.""" + + arguments: Required[str] + """A JSON string of the arguments passed to the tool.""" + + name: Required[str] + """The name of the tool that was run.""" + + server_label: Required[str] + """The label of the MCP server running the tool.""" + + type: Required[Literal["mcp_call"]] + """The type of the item. Always `mcp_call`.""" + + agent: Optional[McpCallAgent] + """The agent that produced this item.""" + + approval_request_id: Optional[str] + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[BetaMcpToolCallErrorParam] + """The error from the tool call, if any.""" + + output: Optional[str] + """The output from the tool call.""" + + status: Literal["in_progress", "completed", "incomplete", "calling", "failed"] + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +class CompactionTriggerAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class CompactionTrigger(TypedDict, total=False): + """Compacts the current context. Must be the final input item.""" + + type: Required[Literal["compaction_trigger"]] + """The type of the item. Always `compaction_trigger`.""" + + agent: Optional[CompactionTriggerAgent] + """The agent that produced this item.""" + + +class ItemReferenceAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ItemReference(TypedDict, total=False): + """An internal identifier for an item to reference.""" + + id: Required[str] + """The ID of the item to reference.""" + + agent: Optional[ItemReferenceAgent] + """The agent that produced this item.""" + + type: Optional[Literal["item_reference"]] + """The type of item to reference. Always `item_reference`.""" + + +class ProgramAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class Program(TypedDict, total=False): + id: Required[str] + """The unique ID of this program item.""" + + call_id: Required[str] + """The stable call ID of the program item.""" + + code: Required[str] + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: Required[str] + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Required[Literal["program"]] + """The item type. Always `program`.""" + + agent: Optional[ProgramAgent] + """The agent that produced this item.""" + + +class ProgramOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ProgramOutput(TypedDict, total=False): + id: Required[str] + """The unique ID of this program output item.""" + + call_id: Required[str] + """The call ID of the program item.""" + + result: Required[str] + """The result produced by the program item.""" + + status: Required[Literal["completed", "incomplete"]] + """The terminal status of the program output.""" + + type: Required[Literal["program_output"]] + """The item type. Always `program_output`.""" + + agent: Optional[ProgramOutputAgent] + """The agent that produced this item.""" + + +BetaResponseInputItemParam: TypeAlias = Union[ + BetaEasyInputMessageParam, + Message, + BetaResponseOutputMessageParam, + BetaResponseFileSearchToolCallParam, + BetaResponseComputerToolCallParam, + ComputerCallOutput, + BetaResponseFunctionWebSearchParam, + BetaResponseFunctionToolCallParam, + FunctionCallOutput, + AgentMessage, + MultiAgentCall, + MultiAgentCallOutput, + ToolSearchCall, + BetaResponseToolSearchOutputItemParamParam, + AdditionalTools, + BetaResponseConfigurationUpdateItemParamParam, + BetaResponseReasoningItemParam, + BetaResponseCompactionItemParamParam, + ImageGenerationCall, + BetaResponseCodeInterpreterToolCallParam, + LocalShellCall, + LocalShellCallOutput, + ShellCall, + ShellCallOutput, + ApplyPatchCall, + ApplyPatchCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + BetaResponseCustomToolCallOutputParam, + BetaResponseCustomToolCallParam, + CompactionTrigger, + ItemReference, + Program, + ProgramOutput, +] + +BetaResponseInputParam: TypeAlias = List[BetaResponseInputItemParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_text.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_text.py new file mode 100644 index 0000000..51b1a5e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_text.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseInputText", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputText(BaseModel): + """A text input to the model.""" + + text: str + """The text input to the model.""" + + type: Literal["input_text"] + """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_text_content.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_text_content.py new file mode 100644 index 0000000..9b8e5a6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_text_content.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseInputTextContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputTextContent(BaseModel): + """A text input to the model.""" + + text: str + """The text input to the model.""" + + type: Literal["input_text"] + """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_text_content_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_text_content_param.py new file mode 100644 index 0000000..b1003c3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_text_content_param.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseInputTextContentParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputTextContentParam(TypedDict, total=False): + """A text input to the model.""" + + text: Required[str] + """The text input to the model.""" + + type: Required[Literal["input_text"]] + """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_text_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_text_param.py new file mode 100644 index 0000000..e9ceb88 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_input_text_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseInputTextParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputTextParam(TypedDict, total=False): + """A text input to the model.""" + + text: Required[str] + """The text input to the model.""" + + type: Required[Literal["input_text"]] + """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_item.py new file mode 100644 index 0000000..875a51e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_item.py @@ -0,0 +1,644 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_tool import BetaTool +from .beta_image_detail import BetaImageDetail +from .beta_mcp_tool_call_error import BetaMcpToolCallError +from .beta_response_input_file import BetaResponseInputFile +from .beta_response_input_text import BetaResponseInputText +from .beta_response_input_image import BetaResponseInputImage +from .beta_response_output_text import BetaResponseOutputText +from .beta_response_output_message import BetaResponseOutputMessage +from .beta_response_output_refusal import BetaResponseOutputRefusal +from .beta_response_reasoning_item import BetaResponseReasoningItem +from .beta_response_compaction_item import BetaResponseCompactionItem +from .beta_response_tool_search_call import BetaResponseToolSearchCall +from .beta_response_computer_tool_call import BetaResponseComputerToolCall +from .beta_response_input_message_item import BetaResponseInputMessageItem +from .beta_response_function_web_search import BetaResponseFunctionWebSearch +from .beta_response_apply_patch_tool_call import BetaResponseApplyPatchToolCall +from .beta_response_custom_tool_call_item import BetaResponseCustomToolCallItem +from .beta_response_file_search_tool_call import BetaResponseFileSearchToolCall +from .beta_response_function_tool_call_item import BetaResponseFunctionToolCallItem +from .beta_response_tool_search_output_item import BetaResponseToolSearchOutputItem +from .beta_response_function_shell_tool_call import BetaResponseFunctionShellToolCall +from .beta_response_configuration_update_item import BetaResponseConfigurationUpdateItem +from .beta_response_code_interpreter_tool_call import BetaResponseCodeInterpreterToolCall +from .beta_response_apply_patch_tool_call_output import BetaResponseApplyPatchToolCallOutput +from .beta_response_custom_tool_call_output_item import BetaResponseCustomToolCallOutputItem +from .beta_response_computer_tool_call_output_item import BetaResponseComputerToolCallOutputItem +from .beta_response_function_tool_call_output_item import BetaResponseFunctionToolCallOutputItem +from .beta_response_function_shell_tool_call_output import BetaResponseFunctionShellToolCallOutput + +__all__ = [ + "BetaResponseItem", + "AgentMessage", + "AgentMessageContent", + "AgentMessageContentText", + "AgentMessageContentSummaryText", + "AgentMessageContentReasoningText", + "AgentMessageContentComputerScreenshot", + "AgentMessageContentComputerScreenshotPromptCacheBreakpoint", + "AgentMessageContentEncryptedContent", + "AgentMessageAgent", + "MultiAgentCall", + "MultiAgentCallAgent", + "MultiAgentCallOutput", + "MultiAgentCallOutputAgent", + "AdditionalTools", + "AdditionalToolsAgent", + "Program", + "ProgramAgent", + "ProgramOutput", + "ProgramOutputAgent", + "ImageGenerationCall", + "ImageGenerationCallAgent", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallAgent", + "LocalShellCallOutput", + "LocalShellCallOutputAgent", + "McpListTools", + "McpListToolsTool", + "McpListToolsAgent", + "McpApprovalRequest", + "McpApprovalRequestAgent", + "McpApprovalResponse", + "McpApprovalResponseAgent", + "McpCall", + "McpCallAgent", +] + + +class AgentMessageContentText(BaseModel): + """A text content.""" + + text: str + + type: Literal["text"] + + +class AgentMessageContentSummaryText(BaseModel): + """A summary text from the model.""" + + text: str + """A summary of the reasoning output from the model so far.""" + + type: Literal["summary_text"] + """The type of the object. Always `summary_text`.""" + + +class AgentMessageContentReasoningText(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +class AgentMessageContentComputerScreenshotPromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class AgentMessageContentComputerScreenshot(BaseModel): + """A screenshot of a computer.""" + + detail: BetaImageDetail + """The detail level of the screenshot image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + file_id: Optional[str] = None + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: Optional[str] = None + """The URL of the screenshot image.""" + + type: Literal["computer_screenshot"] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + prompt_cache_breakpoint: Optional[AgentMessageContentComputerScreenshotPromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ + + +class AgentMessageContentEncryptedContent(BaseModel): + """ + Opaque encrypted content that Responses API decrypts inside trusted model execution. + """ + + encrypted_content: str + """Opaque encrypted content.""" + + type: Literal["encrypted_content"] + """The type of the input item. Always `encrypted_content`.""" + + +AgentMessageContent: TypeAlias = Annotated[ + Union[ + BetaResponseInputText, + BetaResponseOutputText, + AgentMessageContentText, + AgentMessageContentSummaryText, + AgentMessageContentReasoningText, + BetaResponseOutputRefusal, + BetaResponseInputImage, + AgentMessageContentComputerScreenshot, + BetaResponseInputFile, + AgentMessageContentEncryptedContent, + ], + PropertyInfo(discriminator="type"), +] + + +class AgentMessageAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class AgentMessage(BaseModel): + id: str + """The unique ID of the agent message.""" + + author: str + """The sending agent identity.""" + + content: List[AgentMessageContent] + """Encrypted content sent between agents.""" + + recipient: str + """The destination agent identity.""" + + type: Literal["agent_message"] + """The type of the item. Always `agent_message`.""" + + agent: Optional[AgentMessageAgent] = None + """The agent that produced this item.""" + + +class MultiAgentCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCall(BaseModel): + id: str + """The unique ID of the multi-agent call item.""" + + action: Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + """The multi-agent action to execute.""" + + arguments: str + """The JSON string of arguments generated for the action.""" + + call_id: str + """The unique ID linking this call to its output.""" + + type: Literal["multi_agent_call"] + """The type of the multi-agent call. Always `multi_agent_call`.""" + + agent: Optional[MultiAgentCallAgent] = None + """The agent that produced this item.""" + + +class MultiAgentCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCallOutput(BaseModel): + id: str + """The unique ID of the multi-agent call output item.""" + + action: Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + """The multi-agent action that produced this result.""" + + call_id: str + """The unique ID of the multi-agent call.""" + + output: List[BetaResponseOutputText] + """Text output returned by the multi-agent action.""" + + type: Literal["multi_agent_call_output"] + """The type of the multi-agent result. Always `multi_agent_call_output`.""" + + agent: Optional[MultiAgentCallOutputAgent] = None + """The agent that produced this item.""" + + +class AdditionalToolsAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class AdditionalTools(BaseModel): + id: str + """The unique ID of the additional tools item.""" + + role: Literal["unknown", "user", "assistant", "system", "critic", "discriminator", "developer", "tool"] + """The role that provided the additional tools.""" + + tools: List[BetaTool] + """The additional tool definitions made available at this item.""" + + type: Literal["additional_tools"] + """The type of the item. Always `additional_tools`.""" + + agent: Optional[AdditionalToolsAgent] = None + """The agent that produced this item.""" + + +class ProgramAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Program(BaseModel): + id: str + """The unique ID of the program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The type of the item. Always `program`.""" + + agent: Optional[ProgramAgent] = None + """The agent that produced this item.""" + + +class ProgramOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of the program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output item.""" + + type: Literal["program_output"] + """The type of the item. Always `program_output`.""" + + agent: Optional[ProgramOutputAgent] = None + """The agent that produced this item.""" + + +class ImageGenerationCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + + id: str + """The unique ID of the image generation call.""" + + result: Optional[str] = None + """The generated image encoded in base64.""" + + status: Literal["in_progress", "completed", "generating", "failed"] + """The status of the image generation call.""" + + type: Literal["image_generation_call"] + """The type of the image generation call. Always `image_generation_call`.""" + + action: Optional[Literal["generate", "edit", "auto"]] = None + """The action used for image generation.""" + + agent: Optional[ImageGenerationCallAgent] = None + """The agent that produced this item.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] = None + """The background setting used for generation.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] = None + """The output format used for generation.""" + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None + """The quality of the image generated by the image generation tool call. + + One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`. + """ + + revised_prompt: Optional[str] = None + """The prompt that was used after any model prompt rewriting.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + +class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + + command: List[str] + """The command to run.""" + + env: Dict[str, str] + """Environment variables to set for the command.""" + + type: Literal["exec"] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] = None + """Optional user to run the command as.""" + + working_directory: Optional[str] = None + """Optional working directory to run the command in.""" + + +class LocalShellCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + + id: str + """The unique ID of the local shell call.""" + + action: LocalShellCallAction + """Execute a shell command on the server.""" + + call_id: str + """The unique ID of the local shell tool call generated by the model.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the local shell call.""" + + type: Literal["local_shell_call"] + """The type of the local shell call. Always `local_shell_call`.""" + + agent: Optional[LocalShellCallAgent] = None + """The agent that produced this item.""" + + +class LocalShellCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + + id: str + """The unique ID of the local shell tool call generated by the model.""" + + output: str + """A JSON string of the output of the local shell tool call.""" + + type: Literal["local_shell_call_output"] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + agent: Optional[LocalShellCallOutputAgent] = None + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + + input_schema: object + """The JSON schema describing the tool's input.""" + + name: str + """The name of the tool.""" + + annotations: Optional[object] = None + """Additional annotations about the tool.""" + + description: Optional[str] = None + """The description of the tool.""" + + +class McpListToolsAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + + id: str + """The unique ID of the list.""" + + server_label: str + """The label of the MCP server.""" + + tools: List[McpListToolsTool] + """The tools available on the server.""" + + type: Literal["mcp_list_tools"] + """The type of the item. Always `mcp_list_tools`.""" + + agent: Optional[McpListToolsAgent] = None + """The agent that produced this item.""" + + error: Optional[str] = None + """Error message if the server could not list tools.""" + + +class McpApprovalRequestAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + + id: str + """The unique ID of the approval request.""" + + arguments: str + """A JSON string of arguments for the tool.""" + + name: str + """The name of the tool to run.""" + + server_label: str + """The label of the MCP server making the request.""" + + type: Literal["mcp_approval_request"] + """The type of the item. Always `mcp_approval_request`.""" + + agent: Optional[McpApprovalRequestAgent] = None + """The agent that produced this item.""" + + +class McpApprovalResponseAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + + id: str + """The unique ID of the approval response""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + agent: Optional[McpApprovalResponseAgent] = None + """The agent that produced this item.""" + + reason: Optional[str] = None + """Optional reason for the decision.""" + + +class McpCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + + id: str + """The unique ID of the tool call.""" + + arguments: str + """A JSON string of the arguments passed to the tool.""" + + name: str + """The name of the tool that was run.""" + + server_label: str + """The label of the MCP server running the tool.""" + + type: Literal["mcp_call"] + """The type of the item. Always `mcp_call`.""" + + agent: Optional[McpCallAgent] = None + """The agent that produced this item.""" + + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[BetaMcpToolCallError] = None + """The error from the tool call, if any.""" + + output: Optional[str] = None + """The output from the tool call.""" + + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +BetaResponseItem: TypeAlias = Annotated[ + Union[ + BetaResponseInputMessageItem, + BetaResponseOutputMessage, + BetaResponseFileSearchToolCall, + BetaResponseComputerToolCall, + BetaResponseComputerToolCallOutputItem, + BetaResponseFunctionWebSearch, + BetaResponseFunctionToolCallItem, + BetaResponseFunctionToolCallOutputItem, + AgentMessage, + MultiAgentCall, + MultiAgentCallOutput, + BetaResponseToolSearchCall, + BetaResponseToolSearchOutputItem, + AdditionalTools, + BetaResponseConfigurationUpdateItem, + BetaResponseReasoningItem, + Program, + ProgramOutput, + BetaResponseCompactionItem, + ImageGenerationCall, + BetaResponseCodeInterpreterToolCall, + LocalShellCall, + LocalShellCallOutput, + BetaResponseFunctionShellToolCall, + BetaResponseFunctionShellToolCallOutput, + BetaResponseApplyPatchToolCall, + BetaResponseApplyPatchToolCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + BetaResponseCustomToolCallItem, + BetaResponseCustomToolCallOutputItem, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_local_environment.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_local_environment.py new file mode 100644 index 0000000..0f3e0d9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_local_environment.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseLocalEnvironment"] + + +class BetaResponseLocalEnvironment(BaseModel): + """Represents the use of a local environment to perform shell actions.""" + + type: Literal["local"] + """The environment type. Always `local`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_arguments_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_arguments_delta_event.py new file mode 100644 index 0000000..35575c7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_arguments_delta_event.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpCallArgumentsDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpCallArgumentsDeltaEvent(BaseModel): + """ + Emitted when there is a delta (partial update) to the arguments of an MCP tool call. + """ + + delta: str + """ + A JSON string containing the partial update to the arguments for the MCP tool + call. + """ + + item_id: str + """The unique identifier of the MCP tool call item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call_arguments.delta"] + """The type of the event. Always 'response.mcp_call_arguments.delta'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_arguments_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_arguments_done_event.py new file mode 100644 index 0000000..f667517 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_arguments_done_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpCallArgumentsDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpCallArgumentsDoneEvent(BaseModel): + """Emitted when the arguments for an MCP tool call are finalized.""" + + arguments: str + """A JSON string containing the finalized arguments for the MCP tool call.""" + + item_id: str + """The unique identifier of the MCP tool call item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call_arguments.done"] + """The type of the event. Always 'response.mcp_call_arguments.done'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_completed_event.py new file mode 100644 index 0000000..d6dcaba --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_completed_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpCallCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpCallCompletedEvent(BaseModel): + """Emitted when an MCP tool call has completed successfully.""" + + item_id: str + """The ID of the MCP tool call item that completed.""" + + output_index: int + """The index of the output item that completed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call.completed"] + """The type of the event. Always 'response.mcp_call.completed'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_failed_event.py new file mode 100644 index 0000000..cd557d0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_failed_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpCallFailedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpCallFailedEvent(BaseModel): + """Emitted when an MCP tool call has failed.""" + + item_id: str + """The ID of the MCP tool call item that failed.""" + + output_index: int + """The index of the output item that failed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call.failed"] + """The type of the event. Always 'response.mcp_call.failed'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_in_progress_event.py new file mode 100644 index 0000000..34d82cb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_call_in_progress_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpCallInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpCallInProgressEvent(BaseModel): + """Emitted when an MCP tool call is in progress.""" + + item_id: str + """The unique identifier of the MCP tool call item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call.in_progress"] + """The type of the event. Always 'response.mcp_call.in_progress'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_list_tools_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_list_tools_completed_event.py new file mode 100644 index 0000000..ff23246 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_list_tools_completed_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpListToolsCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpListToolsCompletedEvent(BaseModel): + """Emitted when the list of available MCP tools has been successfully retrieved.""" + + item_id: str + """The ID of the MCP tool call item that produced this output.""" + + output_index: int + """The index of the output item that was processed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_list_tools.completed"] + """The type of the event. Always 'response.mcp_list_tools.completed'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_list_tools_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_list_tools_failed_event.py new file mode 100644 index 0000000..2f6fc30 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_list_tools_failed_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpListToolsFailedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpListToolsFailedEvent(BaseModel): + """Emitted when the attempt to list available MCP tools has failed.""" + + item_id: str + """The ID of the MCP tool call item that failed.""" + + output_index: int + """The index of the output item that failed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_list_tools.failed"] + """The type of the event. Always 'response.mcp_list_tools.failed'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_list_tools_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_list_tools_in_progress_event.py new file mode 100644 index 0000000..c94513d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_mcp_list_tools_in_progress_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpListToolsInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpListToolsInProgressEvent(BaseModel): + """ + Emitted when the system is in the process of retrieving the list of available MCP tools. + """ + + item_id: str + """The ID of the MCP tool call item that is being processed.""" + + output_index: int + """The index of the output item that is being processed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_list_tools.in_progress"] + """The type of the event. Always 'response.mcp_list_tools.in_progress'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_item.py new file mode 100644 index 0000000..7c2a74c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_item.py @@ -0,0 +1,640 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_tool import BetaTool +from .beta_image_detail import BetaImageDetail +from .beta_mcp_tool_call_error import BetaMcpToolCallError +from .beta_response_input_file import BetaResponseInputFile +from .beta_response_input_text import BetaResponseInputText +from .beta_response_input_image import BetaResponseInputImage +from .beta_response_output_text import BetaResponseOutputText +from .beta_response_output_message import BetaResponseOutputMessage +from .beta_response_output_refusal import BetaResponseOutputRefusal +from .beta_response_reasoning_item import BetaResponseReasoningItem +from .beta_response_compaction_item import BetaResponseCompactionItem +from .beta_response_custom_tool_call import BetaResponseCustomToolCall +from .beta_response_tool_search_call import BetaResponseToolSearchCall +from .beta_response_computer_tool_call import BetaResponseComputerToolCall +from .beta_response_function_tool_call import BetaResponseFunctionToolCall +from .beta_response_function_web_search import BetaResponseFunctionWebSearch +from .beta_response_apply_patch_tool_call import BetaResponseApplyPatchToolCall +from .beta_response_file_search_tool_call import BetaResponseFileSearchToolCall +from .beta_response_tool_search_output_item import BetaResponseToolSearchOutputItem +from .beta_response_function_shell_tool_call import BetaResponseFunctionShellToolCall +from .beta_response_code_interpreter_tool_call import BetaResponseCodeInterpreterToolCall +from .beta_response_apply_patch_tool_call_output import BetaResponseApplyPatchToolCallOutput +from .beta_response_custom_tool_call_output_item import BetaResponseCustomToolCallOutputItem +from .beta_response_computer_tool_call_output_item import BetaResponseComputerToolCallOutputItem +from .beta_response_function_tool_call_output_item import BetaResponseFunctionToolCallOutputItem +from .beta_response_function_shell_tool_call_output import BetaResponseFunctionShellToolCallOutput + +__all__ = [ + "BetaResponseOutputItem", + "AgentMessage", + "AgentMessageContent", + "AgentMessageContentText", + "AgentMessageContentSummaryText", + "AgentMessageContentReasoningText", + "AgentMessageContentComputerScreenshot", + "AgentMessageContentComputerScreenshotPromptCacheBreakpoint", + "AgentMessageContentEncryptedContent", + "AgentMessageAgent", + "MultiAgentCall", + "MultiAgentCallAgent", + "MultiAgentCallOutput", + "MultiAgentCallOutputAgent", + "Program", + "ProgramAgent", + "ProgramOutput", + "ProgramOutputAgent", + "AdditionalTools", + "AdditionalToolsAgent", + "ImageGenerationCall", + "ImageGenerationCallAgent", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallAgent", + "LocalShellCallOutput", + "LocalShellCallOutputAgent", + "McpCall", + "McpCallAgent", + "McpListTools", + "McpListToolsTool", + "McpListToolsAgent", + "McpApprovalRequest", + "McpApprovalRequestAgent", + "McpApprovalResponse", + "McpApprovalResponseAgent", +] + + +class AgentMessageContentText(BaseModel): + """A text content.""" + + text: str + + type: Literal["text"] + + +class AgentMessageContentSummaryText(BaseModel): + """A summary text from the model.""" + + text: str + """A summary of the reasoning output from the model so far.""" + + type: Literal["summary_text"] + """The type of the object. Always `summary_text`.""" + + +class AgentMessageContentReasoningText(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +class AgentMessageContentComputerScreenshotPromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class AgentMessageContentComputerScreenshot(BaseModel): + """A screenshot of a computer.""" + + detail: BetaImageDetail + """The detail level of the screenshot image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + file_id: Optional[str] = None + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: Optional[str] = None + """The URL of the screenshot image.""" + + type: Literal["computer_screenshot"] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + prompt_cache_breakpoint: Optional[AgentMessageContentComputerScreenshotPromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ + + +class AgentMessageContentEncryptedContent(BaseModel): + """ + Opaque encrypted content that Responses API decrypts inside trusted model execution. + """ + + encrypted_content: str + """Opaque encrypted content.""" + + type: Literal["encrypted_content"] + """The type of the input item. Always `encrypted_content`.""" + + +AgentMessageContent: TypeAlias = Annotated[ + Union[ + BetaResponseInputText, + BetaResponseOutputText, + AgentMessageContentText, + AgentMessageContentSummaryText, + AgentMessageContentReasoningText, + BetaResponseOutputRefusal, + BetaResponseInputImage, + AgentMessageContentComputerScreenshot, + BetaResponseInputFile, + AgentMessageContentEncryptedContent, + ], + PropertyInfo(discriminator="type"), +] + + +class AgentMessageAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class AgentMessage(BaseModel): + id: str + """The unique ID of the agent message.""" + + author: str + """The sending agent identity.""" + + content: List[AgentMessageContent] + """Encrypted content sent between agents.""" + + recipient: str + """The destination agent identity.""" + + type: Literal["agent_message"] + """The type of the item. Always `agent_message`.""" + + agent: Optional[AgentMessageAgent] = None + """The agent that produced this item.""" + + +class MultiAgentCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCall(BaseModel): + id: str + """The unique ID of the multi-agent call item.""" + + action: Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + """The multi-agent action to execute.""" + + arguments: str + """The JSON string of arguments generated for the action.""" + + call_id: str + """The unique ID linking this call to its output.""" + + type: Literal["multi_agent_call"] + """The type of the multi-agent call. Always `multi_agent_call`.""" + + agent: Optional[MultiAgentCallAgent] = None + """The agent that produced this item.""" + + +class MultiAgentCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCallOutput(BaseModel): + id: str + """The unique ID of the multi-agent call output item.""" + + action: Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + """The multi-agent action that produced this result.""" + + call_id: str + """The unique ID of the multi-agent call.""" + + output: List[BetaResponseOutputText] + """Text output returned by the multi-agent action.""" + + type: Literal["multi_agent_call_output"] + """The type of the multi-agent result. Always `multi_agent_call_output`.""" + + agent: Optional[MultiAgentCallOutputAgent] = None + """The agent that produced this item.""" + + +class ProgramAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Program(BaseModel): + id: str + """The unique ID of the program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The type of the item. Always `program`.""" + + agent: Optional[ProgramAgent] = None + """The agent that produced this item.""" + + +class ProgramOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of the program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output item.""" + + type: Literal["program_output"] + """The type of the item. Always `program_output`.""" + + agent: Optional[ProgramOutputAgent] = None + """The agent that produced this item.""" + + +class AdditionalToolsAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class AdditionalTools(BaseModel): + id: str + """The unique ID of the additional tools item.""" + + role: Literal["unknown", "user", "assistant", "system", "critic", "discriminator", "developer", "tool"] + """The role that provided the additional tools.""" + + tools: List[BetaTool] + """The additional tool definitions made available at this item.""" + + type: Literal["additional_tools"] + """The type of the item. Always `additional_tools`.""" + + agent: Optional[AdditionalToolsAgent] = None + """The agent that produced this item.""" + + +class ImageGenerationCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + + id: str + """The unique ID of the image generation call.""" + + result: Optional[str] = None + """The generated image encoded in base64.""" + + status: Literal["in_progress", "completed", "generating", "failed"] + """The status of the image generation call.""" + + type: Literal["image_generation_call"] + """The type of the image generation call. Always `image_generation_call`.""" + + action: Optional[Literal["generate", "edit", "auto"]] = None + """The action used for image generation.""" + + agent: Optional[ImageGenerationCallAgent] = None + """The agent that produced this item.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] = None + """The background setting used for generation.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] = None + """The output format used for generation.""" + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None + """The quality of the image generated by the image generation tool call. + + One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`. + """ + + revised_prompt: Optional[str] = None + """The prompt that was used after any model prompt rewriting.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + +class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + + command: List[str] + """The command to run.""" + + env: Dict[str, str] + """Environment variables to set for the command.""" + + type: Literal["exec"] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] = None + """Optional user to run the command as.""" + + working_directory: Optional[str] = None + """Optional working directory to run the command in.""" + + +class LocalShellCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + + id: str + """The unique ID of the local shell call.""" + + action: LocalShellCallAction + """Execute a shell command on the server.""" + + call_id: str + """The unique ID of the local shell tool call generated by the model.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the local shell call.""" + + type: Literal["local_shell_call"] + """The type of the local shell call. Always `local_shell_call`.""" + + agent: Optional[LocalShellCallAgent] = None + """The agent that produced this item.""" + + +class LocalShellCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + + id: str + """The unique ID of the local shell tool call generated by the model.""" + + output: str + """A JSON string of the output of the local shell tool call.""" + + type: Literal["local_shell_call_output"] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + agent: Optional[LocalShellCallOutputAgent] = None + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class McpCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + + id: str + """The unique ID of the tool call.""" + + arguments: str + """A JSON string of the arguments passed to the tool.""" + + name: str + """The name of the tool that was run.""" + + server_label: str + """The label of the MCP server running the tool.""" + + type: Literal["mcp_call"] + """The type of the item. Always `mcp_call`.""" + + agent: Optional[McpCallAgent] = None + """The agent that produced this item.""" + + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[BetaMcpToolCallError] = None + """The error from the tool call, if any.""" + + output: Optional[str] = None + """The output from the tool call.""" + + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + + input_schema: object + """The JSON schema describing the tool's input.""" + + name: str + """The name of the tool.""" + + annotations: Optional[object] = None + """Additional annotations about the tool.""" + + description: Optional[str] = None + """The description of the tool.""" + + +class McpListToolsAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + + id: str + """The unique ID of the list.""" + + server_label: str + """The label of the MCP server.""" + + tools: List[McpListToolsTool] + """The tools available on the server.""" + + type: Literal["mcp_list_tools"] + """The type of the item. Always `mcp_list_tools`.""" + + agent: Optional[McpListToolsAgent] = None + """The agent that produced this item.""" + + error: Optional[str] = None + """Error message if the server could not list tools.""" + + +class McpApprovalRequestAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + + id: str + """The unique ID of the approval request.""" + + arguments: str + """A JSON string of arguments for the tool.""" + + name: str + """The name of the tool to run.""" + + server_label: str + """The label of the MCP server making the request.""" + + type: Literal["mcp_approval_request"] + """The type of the item. Always `mcp_approval_request`.""" + + agent: Optional[McpApprovalRequestAgent] = None + """The agent that produced this item.""" + + +class McpApprovalResponseAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + + id: str + """The unique ID of the approval response""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + agent: Optional[McpApprovalResponseAgent] = None + """The agent that produced this item.""" + + reason: Optional[str] = None + """Optional reason for the decision.""" + + +BetaResponseOutputItem: TypeAlias = Annotated[ + Union[ + BetaResponseOutputMessage, + BetaResponseFileSearchToolCall, + BetaResponseFunctionToolCall, + BetaResponseFunctionToolCallOutputItem, + AgentMessage, + MultiAgentCall, + MultiAgentCallOutput, + BetaResponseFunctionWebSearch, + BetaResponseComputerToolCall, + BetaResponseComputerToolCallOutputItem, + BetaResponseReasoningItem, + Program, + ProgramOutput, + BetaResponseToolSearchCall, + BetaResponseToolSearchOutputItem, + AdditionalTools, + BetaResponseCompactionItem, + ImageGenerationCall, + BetaResponseCodeInterpreterToolCall, + LocalShellCall, + LocalShellCallOutput, + BetaResponseFunctionShellToolCall, + BetaResponseFunctionShellToolCallOutput, + BetaResponseApplyPatchToolCall, + BetaResponseApplyPatchToolCallOutput, + McpCall, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + BetaResponseCustomToolCall, + BetaResponseCustomToolCallOutputItem, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_item_added_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_item_added_event.py new file mode 100644 index 0000000..4e12b9d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_item_added_event.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_output_item import BetaResponseOutputItem + +__all__ = ["BetaResponseOutputItemAddedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseOutputItemAddedEvent(BaseModel): + """Emitted when a new output item is added.""" + + item: BetaResponseOutputItem + """The output item that was added. + + For reasoning items, `encrypted_content` may be incomplete while the item is in + progress. Use the reasoning item from the corresponding + `response.output_item.done` event when passing it as input to a subsequent + request. + """ + + output_index: int + """The index of the output item that was added.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.output_item.added"] + """The type of the event. Always `response.output_item.added`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_item_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_item_done_event.py new file mode 100644 index 0000000..293eb94 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_item_done_event.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_output_item import BetaResponseOutputItem + +__all__ = ["BetaResponseOutputItemDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseOutputItemDoneEvent(BaseModel): + """Emitted when an output item is marked done.""" + + item: BetaResponseOutputItem + """The output item that was marked done.""" + + output_index: int + """The index of the output item that was marked done.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.output_item.done"] + """The type of the event. Always `response.output_item.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_message.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_message.py new file mode 100644 index 0000000..9abf376 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_message.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_output_text import BetaResponseOutputText +from .beta_response_output_refusal import BetaResponseOutputRefusal + +__all__ = ["BetaResponseOutputMessage", "Content", "Agent"] + +Content: TypeAlias = Annotated[ + Union[BetaResponseOutputText, BetaResponseOutputRefusal], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseOutputMessage(BaseModel): + """An output message from the model.""" + + id: str + """The unique ID of the output message.""" + + content: List[Content] + """The content of the output message.""" + + role: Literal["assistant"] + """The role of the output message. Always `assistant`.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + type: Literal["message"] + """The type of the output message. Always `message`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + phase: Optional[Literal["commentary", "final_answer"]] = None + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_message_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_message_param.py new file mode 100644 index 0000000..0ac8a9c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_message_param.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .beta_response_output_text_param import BetaResponseOutputTextParam +from .beta_response_output_refusal_param import BetaResponseOutputRefusalParam + +__all__ = ["BetaResponseOutputMessageParam", "Content", "Agent"] + +Content: TypeAlias = Union[BetaResponseOutputTextParam, BetaResponseOutputRefusalParam] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class BetaResponseOutputMessageParam(TypedDict, total=False): + """An output message from the model.""" + + id: Required[str] + """The unique ID of the output message.""" + + content: Required[Iterable[Content]] + """The content of the output message.""" + + role: Required[Literal["assistant"]] + """The role of the output message. Always `assistant`.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + type: Required[Literal["message"]] + """The type of the output message. Always `message`.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + phase: Optional[Literal["commentary", "final_answer"]] + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_refusal.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_refusal.py new file mode 100644 index 0000000..a2c534c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_refusal.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseOutputRefusal"] + + +class BetaResponseOutputRefusal(BaseModel): + """A refusal from the model.""" + + refusal: str + """The refusal explanation from the model.""" + + type: Literal["refusal"] + """The type of the refusal. Always `refusal`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_refusal_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_refusal_param.py new file mode 100644 index 0000000..3aa8cb4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_refusal_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseOutputRefusalParam"] + + +class BetaResponseOutputRefusalParam(TypedDict, total=False): + """A refusal from the model.""" + + refusal: Required[str] + """The refusal explanation from the model.""" + + type: Required[Literal["refusal"]] + """The type of the refusal. Always `refusal`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_text.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_text.py new file mode 100644 index 0000000..b17471e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_text.py @@ -0,0 +1,131 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "BetaResponseOutputText", + "Annotation", + "AnnotationFileCitation", + "AnnotationURLCitation", + "AnnotationContainerFileCitation", + "AnnotationFilePath", + "Logprob", + "LogprobTopLogprob", +] + + +class AnnotationFileCitation(BaseModel): + """A citation to a file.""" + + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the file cited.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_citation"] + """The type of the file citation. Always `file_citation`.""" + + +class AnnotationURLCitation(BaseModel): + """A citation for a web resource used to generate a model response.""" + + end_index: int + """The index of the last character of the URL citation in the message.""" + + start_index: int + """The index of the first character of the URL citation in the message.""" + + title: str + """The title of the web resource.""" + + type: Literal["url_citation"] + """The type of the URL citation. Always `url_citation`.""" + + url: str + """The URL of the web resource.""" + + +class AnnotationContainerFileCitation(BaseModel): + """A citation for a container file used to generate a model response.""" + + container_id: str + """The ID of the container file.""" + + end_index: int + """The index of the last character of the container file citation in the message.""" + + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the container file cited.""" + + start_index: int + """The index of the first character of the container file citation in the message.""" + + type: Literal["container_file_citation"] + """The type of the container file citation. Always `container_file_citation`.""" + + +class AnnotationFilePath(BaseModel): + """A path to a file.""" + + file_id: str + """The ID of the file.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_path"] + """The type of the file path. Always `file_path`.""" + + +Annotation: TypeAlias = Annotated[ + Union[AnnotationFileCitation, AnnotationURLCitation, AnnotationContainerFileCitation, AnnotationFilePath], + PropertyInfo(discriminator="type"), +] + + +class LogprobTopLogprob(BaseModel): + """The top log probability of a token.""" + + token: str + + bytes: List[int] + + logprob: float + + +class Logprob(BaseModel): + """The log probability of a token.""" + + token: str + + bytes: List[int] + + logprob: float + + top_logprobs: List[LogprobTopLogprob] + + +class BetaResponseOutputText(BaseModel): + """A text output from the model.""" + + annotations: List[Annotation] + """The annotations of the text output.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + logprobs: Optional[List[Logprob]] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_text_annotation_added_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_text_annotation_added_event.py new file mode 100644 index 0000000..a1251f1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_text_annotation_added_event.py @@ -0,0 +1,128 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "BetaResponseOutputTextAnnotationAddedEvent", + "Annotation", + "AnnotationFileCitation", + "AnnotationURLCitation", + "AnnotationContainerFileCitation", + "AnnotationFilePath", + "Agent", +] + + +class AnnotationFileCitation(BaseModel): + """A citation to a file.""" + + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the file cited.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_citation"] + """The type of the file citation. Always `file_citation`.""" + + +class AnnotationURLCitation(BaseModel): + """A citation for a web resource used to generate a model response.""" + + end_index: int + """The index of the last character of the URL citation in the message.""" + + start_index: int + """The index of the first character of the URL citation in the message.""" + + title: str + """The title of the web resource.""" + + type: Literal["url_citation"] + """The type of the URL citation. Always `url_citation`.""" + + url: str + """The URL of the web resource.""" + + +class AnnotationContainerFileCitation(BaseModel): + """A citation for a container file used to generate a model response.""" + + container_id: str + """The ID of the container file.""" + + end_index: int + """The index of the last character of the container file citation in the message.""" + + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the container file cited.""" + + start_index: int + """The index of the first character of the container file citation in the message.""" + + type: Literal["container_file_citation"] + """The type of the container file citation. Always `container_file_citation`.""" + + +class AnnotationFilePath(BaseModel): + """A path to a file.""" + + file_id: str + """The ID of the file.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_path"] + """The type of the file path. Always `file_path`.""" + + +Annotation: TypeAlias = Annotated[ + Union[AnnotationFileCitation, AnnotationURLCitation, AnnotationContainerFileCitation, AnnotationFilePath, None], + PropertyInfo(discriminator="type"), +] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseOutputTextAnnotationAddedEvent(BaseModel): + """Emitted when an annotation is added to output text content.""" + + annotation: Optional[Annotation] = None + """The annotation object being added. (See annotation schema for details.)""" + + annotation_index: int + """The index of the annotation within the content part.""" + + content_index: int + """The index of the content part within the output item.""" + + item_id: str + """The unique identifier of the item to which the annotation is being added.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.output_text.annotation.added"] + """The type of the event. Always 'response.output_text.annotation.added'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_text_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_text_param.py new file mode 100644 index 0000000..3dff0da --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_output_text_param.py @@ -0,0 +1,129 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "BetaResponseOutputTextParam", + "Annotation", + "AnnotationFileCitation", + "AnnotationURLCitation", + "AnnotationContainerFileCitation", + "AnnotationFilePath", + "Logprob", + "LogprobTopLogprob", +] + + +class AnnotationFileCitation(TypedDict, total=False): + """A citation to a file.""" + + file_id: Required[str] + """The ID of the file.""" + + filename: Required[str] + """The filename of the file cited.""" + + index: Required[int] + """The index of the file in the list of files.""" + + type: Required[Literal["file_citation"]] + """The type of the file citation. Always `file_citation`.""" + + +class AnnotationURLCitation(TypedDict, total=False): + """A citation for a web resource used to generate a model response.""" + + end_index: Required[int] + """The index of the last character of the URL citation in the message.""" + + start_index: Required[int] + """The index of the first character of the URL citation in the message.""" + + title: Required[str] + """The title of the web resource.""" + + type: Required[Literal["url_citation"]] + """The type of the URL citation. Always `url_citation`.""" + + url: Required[str] + """The URL of the web resource.""" + + +class AnnotationContainerFileCitation(TypedDict, total=False): + """A citation for a container file used to generate a model response.""" + + container_id: Required[str] + """The ID of the container file.""" + + end_index: Required[int] + """The index of the last character of the container file citation in the message.""" + + file_id: Required[str] + """The ID of the file.""" + + filename: Required[str] + """The filename of the container file cited.""" + + start_index: Required[int] + """The index of the first character of the container file citation in the message.""" + + type: Required[Literal["container_file_citation"]] + """The type of the container file citation. Always `container_file_citation`.""" + + +class AnnotationFilePath(TypedDict, total=False): + """A path to a file.""" + + file_id: Required[str] + """The ID of the file.""" + + index: Required[int] + """The index of the file in the list of files.""" + + type: Required[Literal["file_path"]] + """The type of the file path. Always `file_path`.""" + + +Annotation: TypeAlias = Union[ + AnnotationFileCitation, AnnotationURLCitation, AnnotationContainerFileCitation, AnnotationFilePath +] + + +class LogprobTopLogprob(TypedDict, total=False): + """The top log probability of a token.""" + + token: Required[str] + + bytes: Required[Iterable[int]] + + logprob: Required[float] + + +class Logprob(TypedDict, total=False): + """The log probability of a token.""" + + token: Required[str] + + bytes: Required[Iterable[int]] + + logprob: Required[float] + + top_logprobs: Required[Iterable[LogprobTopLogprob]] + + +class BetaResponseOutputTextParam(TypedDict, total=False): + """A text output from the model.""" + + annotations: Required[Iterable[Annotation]] + """The annotations of the text output.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + logprobs: Iterable[Logprob] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_prompt.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_prompt.py new file mode 100644 index 0000000..c8fb425 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_prompt.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Union, Optional +from typing_extensions import TypeAlias + +from ..._models import BaseModel +from .beta_response_input_file import BetaResponseInputFile +from .beta_response_input_text import BetaResponseInputText +from .beta_response_input_image import BetaResponseInputImage + +__all__ = ["BetaResponsePrompt", "Variables"] + +Variables: TypeAlias = Union[str, BetaResponseInputText, BetaResponseInputImage, BetaResponseInputFile] + + +class BetaResponsePrompt(BaseModel): + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + id: str + """The unique identifier of the prompt template to use.""" + + variables: Optional[Dict[str, Variables]] = None + """Optional map of values to substitute in for variables in your prompt. + + The substitution values can either be strings, or other Response input types + like images or files. + """ + + version: Optional[str] = None + """Optional version of the prompt template.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_prompt_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_prompt_param.py new file mode 100644 index 0000000..e024eb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_prompt_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Required, TypeAlias, TypedDict + +from .beta_response_input_file_param import BetaResponseInputFileParam +from .beta_response_input_text_param import BetaResponseInputTextParam +from .beta_response_input_image_param import BetaResponseInputImageParam + +__all__ = ["BetaResponsePromptParam", "Variables"] + +Variables: TypeAlias = Union[str, BetaResponseInputTextParam, BetaResponseInputImageParam, BetaResponseInputFileParam] + + +class BetaResponsePromptParam(TypedDict, total=False): + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + id: Required[str] + """The unique identifier of the prompt template to use.""" + + variables: Optional[Dict[str, Variables]] + """Optional map of values to substitute in for variables in your prompt. + + The substitution values can either be strings, or other Response input types + like images or files. + """ + + version: Optional[str] + """Optional version of the prompt template.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_queued_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_queued_event.py new file mode 100644 index 0000000..dabe9f5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_queued_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response import BetaResponse + +__all__ = ["BetaResponseQueuedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseQueuedEvent(BaseModel): + """Emitted when a response is queued and waiting to be processed.""" + + response: BetaResponse + """The full response object that is queued.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.queued"] + """The type of the event. Always 'response.queued'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_item.py new file mode 100644 index 0000000..c7b1e0f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_item.py @@ -0,0 +1,78 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningItem", "Summary", "Agent", "Content"] + + +class Summary(BaseModel): + """A summary text from the model.""" + + text: str + """A summary of the reasoning output from the model so far.""" + + type: Literal["summary_text"] + """The type of the object. Always `summary_text`.""" + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Content(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +class BetaResponseReasoningItem(BaseModel): + """ + A description of the chain of thought used by a reasoning model while generating + a response. Be sure to include these items in your `input` to the Responses API + for subsequent turns of a conversation if you are manually + [managing context](https://developers.openai.com/api/docs/guides/conversation-state). + """ + + id: str + """The unique identifier of the reasoning content.""" + + summary: List[Summary] + """Reasoning summary content.""" + + type: Literal["reasoning"] + """The type of the object. Always `reasoning`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + content: Optional[List[Content]] = None + """Reasoning text content.""" + + encrypted_content: Optional[str] = None + """The encrypted content of the reasoning item. + + This is populated by default for reasoning items returned by + `POST /v1/responses` and WebSocket `response.create` requests. + + When streaming, use the completed reasoning item and its `encrypted_content` + from the `response.output_item.done` event in subsequent requests. The + `encrypted_content` in `response.output_item.added` may be incomplete. This is + especially important when `store` is `false` or when using Zero Data Retention. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_item_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_item_param.py new file mode 100644 index 0000000..c67348e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_item_param.py @@ -0,0 +1,78 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseReasoningItemParam", "Summary", "Agent", "Content"] + + +class Summary(TypedDict, total=False): + """A summary text from the model.""" + + text: Required[str] + """A summary of the reasoning output from the model so far.""" + + type: Required[Literal["summary_text"]] + """The type of the object. Always `summary_text`.""" + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class Content(TypedDict, total=False): + """Reasoning text from the model.""" + + text: Required[str] + """The reasoning text from the model.""" + + type: Required[Literal["reasoning_text"]] + """The type of the reasoning text. Always `reasoning_text`.""" + + +class BetaResponseReasoningItemParam(TypedDict, total=False): + """ + A description of the chain of thought used by a reasoning model while generating + a response. Be sure to include these items in your `input` to the Responses API + for subsequent turns of a conversation if you are manually + [managing context](https://developers.openai.com/api/docs/guides/conversation-state). + """ + + id: Required[str] + """The unique identifier of the reasoning content.""" + + summary: Required[Iterable[Summary]] + """Reasoning summary content.""" + + type: Required[Literal["reasoning"]] + """The type of the object. Always `reasoning`.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + content: Iterable[Content] + """Reasoning text content.""" + + encrypted_content: Optional[str] + """The encrypted content of the reasoning item. + + This is populated by default for reasoning items returned by + `POST /v1/responses` and WebSocket `response.create` requests. + + When streaming, use the completed reasoning item and its `encrypted_content` + from the `response.output_item.done` event in subsequent requests. The + `encrypted_content` in `response.output_item.added` may be incomplete. This is + especially important when `store` is `false` or when using Zero Data Retention. + """ + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_summary_part_added_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_summary_part_added_event.py new file mode 100644 index 0000000..48578e1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_summary_part_added_event.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningSummaryPartAddedEvent", "Part", "Agent"] + + +class Part(BaseModel): + """The summary part that was added.""" + + text: str + """The text of the summary part.""" + + type: Literal["summary_text"] + """The type of the summary part. Always `summary_text`.""" + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseReasoningSummaryPartAddedEvent(BaseModel): + """Emitted when a new reasoning summary part is added.""" + + item_id: str + """The ID of the item this summary part is associated with.""" + + output_index: int + """The index of the output item this summary part is associated with.""" + + part: Part + """The summary part that was added.""" + + sequence_number: int + """The sequence number of this event.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + type: Literal["response.reasoning_summary_part.added"] + """The type of the event. Always `response.reasoning_summary_part.added`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_summary_part_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_summary_part_done_event.py new file mode 100644 index 0000000..6307fe4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_summary_part_done_event.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningSummaryPartDoneEvent", "Part", "Agent"] + + +class Part(BaseModel): + """The completed summary part.""" + + text: str + """The text of the summary part.""" + + type: Literal["summary_text"] + """The type of the summary part. Always `summary_text`.""" + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseReasoningSummaryPartDoneEvent(BaseModel): + """Emitted when a reasoning summary part is completed.""" + + item_id: str + """The ID of the item this summary part is associated with.""" + + output_index: int + """The index of the output item this summary part is associated with.""" + + part: Part + """The completed summary part.""" + + sequence_number: int + """The sequence number of this event.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + type: Literal["response.reasoning_summary_part.done"] + """The type of the event. Always `response.reasoning_summary_part.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" + + status: Optional[Literal["incomplete"]] = None + """The completion status of the summary part. + + Omitted when the part completed normally and set to `incomplete` when generation + was interrupted. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_summary_text_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_summary_text_delta_event.py new file mode 100644 index 0000000..7272573 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_summary_text_delta_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningSummaryTextDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseReasoningSummaryTextDeltaEvent(BaseModel): + """Emitted when a delta is added to a reasoning summary text.""" + + delta: str + """The text delta that was added to the summary.""" + + item_id: str + """The ID of the item this summary text delta is associated with.""" + + output_index: int + """The index of the output item this summary text delta is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + type: Literal["response.reasoning_summary_text.delta"] + """The type of the event. Always `response.reasoning_summary_text.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_summary_text_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_summary_text_done_event.py new file mode 100644 index 0000000..ba02b9f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_summary_text_done_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningSummaryTextDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseReasoningSummaryTextDoneEvent(BaseModel): + """Emitted when a reasoning summary text is completed.""" + + item_id: str + """The ID of the item this summary text is associated with.""" + + output_index: int + """The index of the output item this summary text is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + text: str + """The full text of the completed reasoning summary.""" + + type: Literal["response.reasoning_summary_text.done"] + """The type of the event. Always `response.reasoning_summary_text.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_text_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_text_delta_event.py new file mode 100644 index 0000000..f41bf0a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_text_delta_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningTextDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseReasoningTextDeltaEvent(BaseModel): + """Emitted when a delta is added to a reasoning text.""" + + content_index: int + """The index of the reasoning content part this delta is associated with.""" + + delta: str + """The text delta that was added to the reasoning content.""" + + item_id: str + """The ID of the item this reasoning text delta is associated with.""" + + output_index: int + """The index of the output item this reasoning text delta is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.reasoning_text.delta"] + """The type of the event. Always `response.reasoning_text.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_text_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_text_done_event.py new file mode 100644 index 0000000..c5a0c46 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_reasoning_text_done_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningTextDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseReasoningTextDoneEvent(BaseModel): + """Emitted when a reasoning text is completed.""" + + content_index: int + """The index of the reasoning content part.""" + + item_id: str + """The ID of the item this reasoning text is associated with.""" + + output_index: int + """The index of the output item this reasoning text is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + text: str + """The full text of the completed reasoning content.""" + + type: Literal["response.reasoning_text.done"] + """The type of the event. Always `response.reasoning_text.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_refusal_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_refusal_delta_event.py new file mode 100644 index 0000000..254809e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_refusal_delta_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseRefusalDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseRefusalDeltaEvent(BaseModel): + """Emitted when there is a partial refusal text.""" + + content_index: int + """The index of the content part that the refusal text is added to.""" + + delta: str + """The refusal text that is added.""" + + item_id: str + """The ID of the output item that the refusal text is added to.""" + + output_index: int + """The index of the output item that the refusal text is added to.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.refusal.delta"] + """The type of the event. Always `response.refusal.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_refusal_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_refusal_done_event.py new file mode 100644 index 0000000..049024a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_refusal_done_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseRefusalDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseRefusalDoneEvent(BaseModel): + """Emitted when refusal text is finalized.""" + + content_index: int + """The index of the content part that the refusal text is finalized.""" + + item_id: str + """The ID of the output item that the refusal text is finalized.""" + + output_index: int + """The index of the output item that the refusal text is finalized.""" + + refusal: str + """The refusal text that is finalized.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.refusal.done"] + """The type of the event. Always `response.refusal.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_command_added_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_command_added_event.py new file mode 100644 index 0000000..cc62fd8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_command_added_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseShellCallCommandAddedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseShellCallCommandAddedEvent(BaseModel): + """A streaming event that indicated a shell command was added to a tool call.""" + + command: str + """The shell command that was added.""" + + command_index: int + """The index of the shell command that was added.""" + + output_index: int + """The index of the output item that was updated.""" + + sequence_number: int + """The sequence number of the event that was emitted.""" + + type: Literal["response.shell_call_command.added"] + """The type of the event, always `response.shell_call_command.added`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_command_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_command_delta_event.py new file mode 100644 index 0000000..51a1667 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_command_delta_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseShellCallCommandDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseShellCallCommandDeltaEvent(BaseModel): + """A streaming event that indicated a shell command was incrementally updated.""" + + command_index: int + """The index of the shell command that was updated.""" + + delta: str + """The shell command delta that was appended.""" + + output_index: int + """The index of the output item that was updated.""" + + sequence_number: int + """The sequence number of the event that was emitted.""" + + type: Literal["response.shell_call_command.delta"] + """The type of the event, always `response.shell_call_command.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" + + obfuscation: Optional[str] = None + """An obfuscation string that was added to pad the event payload.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_command_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_command_done_event.py new file mode 100644 index 0000000..1dba069 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_command_done_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseShellCallCommandDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseShellCallCommandDoneEvent(BaseModel): + """A streaming event that indicated a shell command was completed.""" + + command: str + """The final shell command that was emitted.""" + + command_index: int + """The index of the shell command that was completed.""" + + output_index: int + """The index of the output item that was updated.""" + + sequence_number: int + """The sequence number of the event that was emitted.""" + + type: Literal["response.shell_call_command.done"] + """The type of the event, always `response.shell_call_command.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_output_content_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_output_content_delta_event.py new file mode 100644 index 0000000..e4190eb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_output_content_delta_event.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseShellCallOutputContentDeltaEvent", "Delta", "Agent"] + + +class Delta(BaseModel): + """The stdout/stderr delta that was emitted.""" + + stderr: Optional[str] = None + """The stderr delta that was emitted.""" + + stdout: Optional[str] = None + """The stdout delta that was emitted.""" + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseShellCallOutputContentDeltaEvent(BaseModel): + """A streaming event that indicated shell call output was incrementally added.""" + + command_index: int + """The index of the shell command that produced output.""" + + delta: Delta + """The stdout/stderr delta that was emitted.""" + + item_id: str + """The ID of the output item that was updated.""" + + output_index: int + """The index of the output item that was updated.""" + + sequence_number: int + """The sequence number of the event that was emitted.""" + + type: Literal["response.shell_call_output_content.delta"] + """The type of the event, always `response.shell_call_output_content.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_output_content_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_output_content_done_event.py new file mode 100644 index 0000000..2d00823 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_shell_call_output_content_done_event.py @@ -0,0 +1,87 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "BetaResponseShellCallOutputContentDoneEvent", + "Output", + "OutputOutcome", + "OutputOutcomeTimeout", + "OutputOutcomeExit", + "Agent", +] + + +class OutputOutcomeTimeout(BaseModel): + """Indicates that the shell call exceeded its configured time limit.""" + + type: Literal["timeout"] + """The outcome type. Always `timeout`.""" + + +class OutputOutcomeExit(BaseModel): + """Indicates that the shell commands finished and returned an exit code.""" + + exit_code: int + """Exit code from the shell process.""" + + type: Literal["exit"] + """The outcome type. Always `exit`.""" + + +OutputOutcome: TypeAlias = Annotated[Union[OutputOutcomeTimeout, OutputOutcomeExit], PropertyInfo(discriminator="type")] + + +class Output(BaseModel): + """The content of a shell tool call output that was emitted.""" + + outcome: OutputOutcome + """ + Represents either an exit outcome (with an exit code) or a timeout outcome for a + shell call output chunk. + """ + + stderr: str + """The standard error output that was captured.""" + + stdout: str + """The standard output that was captured.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseShellCallOutputContentDoneEvent(BaseModel): + """A streaming event that indicated shell call output was completed.""" + + command_index: int + """The index of the shell command that produced output.""" + + item_id: str + """The ID of the output item that was updated.""" + + output: List[Output] + """The output contents emitted for the shell command.""" + + output_index: int + """The index of the output item that was updated.""" + + sequence_number: int + """The sequence number of the event that was emitted.""" + + type: Literal["response.shell_call_output_content.done"] + """The type of the event, always `response.shell_call_output_content.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_status.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_status.py new file mode 100644 index 0000000..06498b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_status.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["BetaResponseStatus"] + +BetaResponseStatus: TypeAlias = Literal["completed", "failed", "in_progress", "cancelled", "queued", "incomplete"] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_accepted_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_accepted_event.py new file mode 100644 index 0000000..f12b2d3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_accepted_event.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseSteerAcceptedEvent", "Steer"] + + +class Steer(BaseModel): + """The accepted steering submission.""" + + id: str + """The ID assigned to the steering submission.""" + + previous_response_id: str + """The ID of the response being steered.""" + + +class BetaResponseSteerAcceptedEvent(BaseModel): + """Emitted when steering input has been validated and queued. + + Acceptance means + the server owns the input, not that it has been applied. The successor's + `response.created` event is the commit point. If accepted input cannot be + committed, `response.steer.failed` returns it with the same steering ID. + + When the response stops for client-owned tool output or approval, the input + remains queued and `response.steer.pending` is emitted after + `response.completed`. Fill the pending event's `required_input` stubs with + saved results and send one matching explicit `response.create` per parent. + Do not resend accepted input while it is still queued. + """ + + sequence_number: int + """The sequence number for this event.""" + + steer: Steer + """The accepted steering submission.""" + + type: Literal["response.steer.accepted"] + """The event discriminator. Always `response.steer.accepted`.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the target response's `response.create` event + supplied a `stream_id`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_error_code.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_error_code.py new file mode 100644 index 0000000..080d947 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_error_code.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +__all__ = ["BetaResponseSteerErrorCode"] + +BetaResponseSteerErrorCode: TypeAlias = Union[ + Literal[ + "response_not_found", + "invalid_input", + "steering_not_supported", + "too_many_pending_steers", + "response_already_completed", + "response_not_active", + "successor_creation_failed", + ], + str, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_event.py new file mode 100644 index 0000000..9901d25 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_event.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_steer_input import BetaResponseSteerInput + +__all__ = ["BetaResponseSteerEvent"] + + +class BetaResponseSteerEvent(BaseModel): + """Queues user input to steer a response on this WebSocket connection. + + Input + can contain text, images, and files. Steering is supported only for + single-agent responses on models and execution modes that support steering. + Responses bound to a conversation or using automatic compaction do not + support steering. + + A `response.steer.accepted` event acknowledges that the server owns the + queued input, not that it has been applied. The successor's `response.created` + event is the commit point. Input that cannot be committed is returned in + `response.steer.failed`. + + Steering may cause the active response to finish at a safe output boundary + with `response.incomplete` and `incomplete_details.reason` set to `steered`, + followed automatically by a successor `response.created`. Normal completion + can also be followed by an automatic successor. Automatic successors inherit + the previous response's settings and continue from it with the queued input. + + If the response stops for client-owned tool output or approval, accepted + steering input remains queued and `response.steer.pending` is emitted after + `response.completed`. Fill the `required_input` stubs from that event with + saved tool results or approval decisions, and send one explicit + `response.create` per parent with the same `previous_response_id` and + WebSocket lane. Do not rerun tools or resend accepted steering input. The + queued input is prepended in submission order to that request's input, and + the explicit request retains its own settings. + + This event accepts only `type`, `previous_response_id`, and `input`. Do not + send `stream_id`; the target response determines the WebSocket lane. + """ + + input: BetaResponseSteerInput + """Input to queue for a continuation of the response. + + Uses the same string or input-item shape as `response.create.input`, with a + non-empty array when supplying input items. + + Steering accepts only messages with the `user` role. Each message may contain + only `type`, `role`, and `content`, with `content` as a string or an array of + `input_text`, `input_image`, and `input_file` parts. The optional `type` must be + `message`. Other roles, tool outputs, and item types are not supported for + steering. + """ + + previous_response_id: str + """The ID of the response to steer on this WebSocket connection.""" + + type: Literal["response.steer"] + """The event discriminator. Always `response.steer`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_event_param.py new file mode 100644 index 0000000..002e704 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_event_param.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .beta_response_steer_input_param import BetaResponseSteerInputParam + +__all__ = ["BetaResponseSteerEventParam"] + + +class BetaResponseSteerEventParam(TypedDict, total=False): + """Queues user input to steer a response on this WebSocket connection. + + Input + can contain text, images, and files. Steering is supported only for + single-agent responses on models and execution modes that support steering. + Responses bound to a conversation or using automatic compaction do not + support steering. + + A `response.steer.accepted` event acknowledges that the server owns the + queued input, not that it has been applied. The successor's `response.created` + event is the commit point. Input that cannot be committed is returned in + `response.steer.failed`. + + Steering may cause the active response to finish at a safe output boundary + with `response.incomplete` and `incomplete_details.reason` set to `steered`, + followed automatically by a successor `response.created`. Normal completion + can also be followed by an automatic successor. Automatic successors inherit + the previous response's settings and continue from it with the queued input. + + If the response stops for client-owned tool output or approval, accepted + steering input remains queued and `response.steer.pending` is emitted after + `response.completed`. Fill the `required_input` stubs from that event with + saved tool results or approval decisions, and send one explicit + `response.create` per parent with the same `previous_response_id` and + WebSocket lane. Do not rerun tools or resend accepted steering input. The + queued input is prepended in submission order to that request's input, and + the explicit request retains its own settings. + + This event accepts only `type`, `previous_response_id`, and `input`. Do not + send `stream_id`; the target response determines the WebSocket lane. + """ + + input: Required[BetaResponseSteerInputParam] + """Input to queue for a continuation of the response. + + Uses the same string or input-item shape as `response.create.input`, with a + non-empty array when supplying input items. + + Steering accepts only messages with the `user` role. Each message may contain + only `type`, `role`, and `content`, with `content` as a string or an array of + `input_text`, `input_image`, and `input_file` parts. The optional `type` must be + `message`. Other roles, tool outputs, and item types are not supported for + steering. + """ + + previous_response_id: Required[str] + """The ID of the response to steer on this WebSocket connection.""" + + type: Required[Literal["response.steer"]] + """The event discriminator. Always `response.steer`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_failed_event.py new file mode 100644 index 0000000..513a4bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_failed_event.py @@ -0,0 +1,92 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_steer_input import BetaResponseSteerInput +from .beta_response_steer_error_code import BetaResponseSteerErrorCode + +__all__ = ["BetaResponseSteerFailedEvent", "Error", "Steer"] + + +class Error(BaseModel): + """Information about why the input could not be committed.""" + + code: BetaResponseSteerErrorCode + """A machine-readable steering error code. + + Clients should handle unknown values because additional codes may be introduced. + Known values include: + + - `response_not_found`: The target response is not available on this connection. + - `invalid_input`: The event or input failed validation. + - `steering_not_supported`: The model or response execution mode does not + support steering. + - `too_many_pending_steers`: Too much steering input is pending for the + response. + - `response_already_completed`: The response completed and is no longer + accepting steering input. + - `response_not_active`: The response is no longer accepting steering input. + - `successor_creation_failed`: The successor response could not be created. + """ + + message: str + """A human-readable description of the error.""" + + type: Literal["invalid_request_error"] + """The error type. Always `invalid_request_error`.""" + + +class Steer(BaseModel): + """The steering submission that could not be committed.""" + + input: BetaResponseSteerInput + """Input to queue for a continuation of the response. + + Uses the same string or input-item shape as `response.create.input`, with a + non-empty array when supplying input items. + + Steering accepts only messages with the `user` role. Each message may contain + only `type`, `role`, and `content`, with `content` as a string or an array of + `input_text`, `input_image`, and `input_file` parts. The optional `type` must be + `message`. Other roles, tool outputs, and item types are not supported for + steering. + """ + + previous_response_id: str + """The ID of the response that was targeted for steering.""" + + id: Optional[str] = None + """The ID assigned to the steering submission, if one was allocated.""" + + +class BetaResponseSteerFailedEvent(BaseModel): + """ + Emitted when steering input is rejected or cannot be committed to a + successor response. Returns the original, uncommitted input so the client + can carry it into `response.create` when appropriate. Invalid input must + be corrected before retrying. + + Failures after acceptance include the same steering ID. Failures before an + ID is allocated omit `steer.id`. A lost connection or missing acknowledgement + leaves the outcome unknown; it is not proof that the input was rejected. + """ + + error: Error + """Information about why the input could not be committed.""" + + sequence_number: int + """The sequence number for this event.""" + + steer: Steer + """The steering submission that could not be committed.""" + + type: Literal["response.steer.failed"] + """The event discriminator. Always `response.steer.failed`.""" + + stream_id: Optional[str] = None + """ + The WebSocket lane that emitted this event, when the target response is + available and its `response.create` event supplied a `stream_id`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_input.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_input.py new file mode 100644 index 0000000..aac528e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_input.py @@ -0,0 +1,124 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_steer_input_content import BetaResponseSteerInputContent +from .beta_response_function_call_output_item_list import BetaResponseFunctionCallOutputItemList + +__all__ = [ + "BetaResponseSteerInput", + "ResponseSteerInputItemList", + "ResponseSteerInputItemListMessage", + "ResponseSteerInputItemListMessageAgent", + "ResponseSteerInputItemListFunctionCallOutput", + "ResponseSteerInputItemListFunctionCallOutputAgent", + "ResponseSteerInputItemListFunctionCallOutputCaller", + "ResponseSteerInputItemListFunctionCallOutputCallerDirect", + "ResponseSteerInputItemListFunctionCallOutputCallerProgram", +] + + +class ResponseSteerInputItemListMessageAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ResponseSteerInputItemListMessage(BaseModel): + content: Union[List[BetaResponseSteerInputContent], str] + """The message content, as an array of content parts.""" + + role: Literal["user"] + """The message role. Always `user`.""" + + type: Literal["message"] + """The item type. Always `message`.""" + + id: Optional[str] = None + """The unique ID of this message item.""" + + agent: Optional[ResponseSteerInputItemListMessageAgent] = None + """The agent that produced this item.""" + + status: Optional[str] = None + """The status of the message item.""" + + +class ResponseSteerInputItemListFunctionCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ResponseSteerInputItemListFunctionCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ResponseSteerInputItemListFunctionCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ResponseSteerInputItemListFunctionCallOutputCaller: TypeAlias = Annotated[ + Union[ + ResponseSteerInputItemListFunctionCallOutputCallerDirect, + ResponseSteerInputItemListFunctionCallOutputCallerProgram, + None, + ], + PropertyInfo(discriminator="type"), +] + + +class ResponseSteerInputItemListFunctionCallOutput(BaseModel): + """The output of a function tool call.""" + + output: Union[str, BetaResponseFunctionCallOutputItemList] + """Text, image, or file output of the function tool call.""" + + type: Literal["function_call_output"] + """The type of the function tool call output. Always `function_call_output`.""" + + id: Optional[str] = None + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ResponseSteerInputItemListFunctionCallOutputAgent] = None + """The agent that produced this item.""" + + call_id: Optional[str] = None + """The unique ID of the function tool call generated by the model.""" + + caller: Optional[ResponseSteerInputItemListFunctionCallOutputCaller] = None + """The execution context that produced this tool call.""" + + name: Optional[str] = None + """The name of the tool that produced the output.""" + + namespace: Optional[str] = None + """The namespace of the tool that produced the output.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +ResponseSteerInputItemList: TypeAlias = Annotated[ + Union[ResponseSteerInputItemListMessage, ResponseSteerInputItemListFunctionCallOutput], + PropertyInfo(discriminator="type"), +] + +BetaResponseSteerInput: TypeAlias = Union[str, List[ResponseSteerInputItemList]] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_input_content.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_input_content.py new file mode 100644 index 0000000..b2feca2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_input_content.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .beta_response_input_file_content import BetaResponseInputFileContent +from .beta_response_input_text_content import BetaResponseInputTextContent +from .beta_response_input_image_content import BetaResponseInputImageContent + +__all__ = ["BetaResponseSteerInputContent"] + +BetaResponseSteerInputContent: TypeAlias = Annotated[ + Union[BetaResponseInputTextContent, BetaResponseInputImageContent, BetaResponseInputFileContent], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_input_content_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_input_content_param.py new file mode 100644 index 0000000..e97d067 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_input_content_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .beta_response_input_file_content_param import BetaResponseInputFileContentParam +from .beta_response_input_text_content_param import BetaResponseInputTextContentParam +from .beta_response_input_image_content_param import BetaResponseInputImageContentParam + +__all__ = ["BetaResponseSteerInputContentParam"] + +BetaResponseSteerInputContentParam: TypeAlias = Union[ + BetaResponseInputTextContentParam, BetaResponseInputImageContentParam, BetaResponseInputFileContentParam +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_input_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_input_param.py new file mode 100644 index 0000000..0023695 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_input_param.py @@ -0,0 +1,118 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .beta_response_steer_input_content_param import BetaResponseSteerInputContentParam +from .beta_response_function_call_output_item_list_param import BetaResponseFunctionCallOutputItemListParam + +__all__ = [ + "BetaResponseSteerInputParam", + "ResponseSteerInputItemList", + "ResponseSteerInputItemListMessage", + "ResponseSteerInputItemListMessageAgent", + "ResponseSteerInputItemListFunctionCallOutput", + "ResponseSteerInputItemListFunctionCallOutputAgent", + "ResponseSteerInputItemListFunctionCallOutputCaller", + "ResponseSteerInputItemListFunctionCallOutputCallerDirect", + "ResponseSteerInputItemListFunctionCallOutputCallerProgram", +] + + +class ResponseSteerInputItemListMessageAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ResponseSteerInputItemListMessage(TypedDict, total=False): + content: Required[Union[Iterable[BetaResponseSteerInputContentParam], str]] + """The message content, as an array of content parts.""" + + role: Required[Literal["user"]] + """The message role. Always `user`.""" + + type: Required[Literal["message"]] + """The item type. Always `message`.""" + + id: Optional[str] + """The unique ID of this message item.""" + + agent: Optional[ResponseSteerInputItemListMessageAgent] + """The agent that produced this item.""" + + status: Optional[str] + """The status of the message item.""" + + +class ResponseSteerInputItemListFunctionCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ResponseSteerInputItemListFunctionCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ResponseSteerInputItemListFunctionCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ResponseSteerInputItemListFunctionCallOutputCaller: TypeAlias = Union[ + ResponseSteerInputItemListFunctionCallOutputCallerDirect, ResponseSteerInputItemListFunctionCallOutputCallerProgram +] + + +class ResponseSteerInputItemListFunctionCallOutput(TypedDict, total=False): + """The output of a function tool call.""" + + output: Required[Union[str, BetaResponseFunctionCallOutputItemListParam]] + """Text, image, or file output of the function tool call.""" + + type: Required[Literal["function_call_output"]] + """The type of the function tool call output. Always `function_call_output`.""" + + id: Optional[str] + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ResponseSteerInputItemListFunctionCallOutputAgent] + """The agent that produced this item.""" + + call_id: Optional[str] + """The unique ID of the function tool call generated by the model.""" + + caller: Optional[ResponseSteerInputItemListFunctionCallOutputCaller] + """The execution context that produced this tool call.""" + + name: Optional[str] + """The name of the tool that produced the output.""" + + namespace: Optional[str] + """The namespace of the tool that produced the output.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +ResponseSteerInputItemList: TypeAlias = Union[ + ResponseSteerInputItemListMessage, ResponseSteerInputItemListFunctionCallOutput +] + +BetaResponseSteerInputParam: TypeAlias = Union[str, Iterable[ResponseSteerInputItemList]] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_pending_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_pending_event.py new file mode 100644 index 0000000..491a08c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_pending_event.py @@ -0,0 +1,74 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_steer_pending_reason import BetaResponseSteerPendingReason +from .beta_response_steer_required_input import BetaResponseSteerRequiredInput + +__all__ = ["BetaResponseSteerPendingEvent", "Steer"] + + +class Steer(BaseModel): + """The steering submission that remains queued.""" + + id: str + """The ID assigned to the steering submission.""" + + previous_response_id: str + """The ID of the response being steered.""" + + +class BetaResponseSteerPendingEvent(BaseModel): + """ + Emitted when accepted steering input remains queued after the target + response completes. The server still owns the input. Do not resend it. + The successor's `response.created` event is the commit point. + + When `reason` is `waiting_for_required_input`, this event follows + `response.completed` while the response waits for the tool results or + approval decisions identified by `required_input`. Copy those stubs, fill + their result fields using the ordinary `response.create` input schemas, + and submit one continuation per parent with the same `previous_response_id` + and WebSocket lane. Use saved results without rerunning tools. The queued + steering input is prepended in submission order to the continuation's + input. That explicit request retains its own settings. + + This notification is emitted at most once per steering submission. Multiple + submissions for the same parent can report the same required inputs; they + do not each require a separate continuation. + """ + + reason: BetaResponseSteerPendingReason + """ + An extensible enum describing why accepted steering input is still queued. + Clients should handle unknown values because additional reasons may be + introduced. Known values include: + + - `waiting_for_required_input`: The response is waiting for the tool results or + approval decisions identified by `required_input`. + """ + + required_input: List[BetaResponseSteerRequiredInput] + """ + Input stubs identifying outstanding client-owned tool results or approval + decisions. Each stub contains identifying fields only; the client supplies the + result before including it in `response.create`. + """ + + sequence_number: int + """The sequence number for this event.""" + + steer: Steer + """The steering submission that remains queued.""" + + type: Literal["response.steer.pending"] + """The event discriminator. Always `response.steer.pending`.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the target response's `response.create` event + supplied a `stream_id`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_pending_reason.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_pending_reason.py new file mode 100644 index 0000000..e9d5232 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_pending_reason.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +__all__ = ["BetaResponseSteerPendingReason"] + +BetaResponseSteerPendingReason: TypeAlias = Union[Literal["waiting_for_required_input"], str] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_required_input.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_required_input.py new file mode 100644 index 0000000..f179054 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_steer_required_input.py @@ -0,0 +1,114 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "BetaResponseSteerRequiredInput", + "FunctionCallOutput", + "CustomToolCallOutput", + "ComputerCallOutput", + "ShellCallOutput", + "ApplyPatchCallOutput", + "ToolSearchOutput", + "McpApprovalResponse", +] + + +class FunctionCallOutput(BaseModel): + """Supply `output` using the function tool call output input schema.""" + + call_id: str + + name: str + + type: Literal["function_call_output"] + + +class CustomToolCallOutput(BaseModel): + """Supply `output` using the custom tool call output input schema. + + The + original custom tool call supplies the tool's name. + """ + + call_id: str + + type: Literal["custom_tool_call_output"] + + +class ComputerCallOutput(BaseModel): + """ + Supply `output` using the computer tool call output input schema, + including any required `acknowledged_safety_checks`. + """ + + call_id: str + + type: Literal["computer_call_output"] + + +class ShellCallOutput(BaseModel): + """Supply `output` using the shell tool call output input schema. + + Each + output entry includes `stdout`, `stderr`, and `outcome`. + """ + + call_id: str + + type: Literal["shell_call_output"] + + +class ApplyPatchCallOutput(BaseModel): + """ + Supply `status` and optional `output` using the apply patch tool call + output input schema. + """ + + call_id: str + + type: Literal["apply_patch_call_output"] + + +class ToolSearchOutput(BaseModel): + """ + Supply `tools` using the tool search output input schema, retaining + `execution: "client"`. + """ + + call_id: str + + execution: Literal["client"] + + type: Literal["tool_search_output"] + + +class McpApprovalResponse(BaseModel): + """Supply `approve` using the MCP approval response input schema. + + An + optional `reason` can be supplied when denying the request. The original + approval request identifies the tool and server. + """ + + approval_request_id: str + + type: Literal["mcp_approval_response"] + + +BetaResponseSteerRequiredInput: TypeAlias = Annotated[ + Union[ + FunctionCallOutput, + CustomToolCallOutput, + ComputerCallOutput, + ShellCallOutput, + ApplyPatchCallOutput, + ToolSearchOutput, + McpApprovalResponse, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_stream_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_stream_event.py new file mode 100644 index 0000000..cd3db2c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_stream_event.py @@ -0,0 +1,132 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .beta_response_error_event import BetaResponseErrorEvent +from .beta_response_failed_event import BetaResponseFailedEvent +from .beta_response_queued_event import BetaResponseQueuedEvent +from .beta_response_created_event import BetaResponseCreatedEvent +from .beta_response_completed_event import BetaResponseCompletedEvent +from .beta_response_text_done_event import BetaResponseTextDoneEvent +from .beta_response_audio_done_event import BetaResponseAudioDoneEvent +from .beta_response_incomplete_event import BetaResponseIncompleteEvent +from .beta_response_text_delta_event import BetaResponseTextDeltaEvent +from .beta_response_audio_delta_event import BetaResponseAudioDeltaEvent +from .beta_response_in_progress_event import BetaResponseInProgressEvent +from .beta_response_refusal_done_event import BetaResponseRefusalDoneEvent +from .beta_response_refusal_delta_event import BetaResponseRefusalDeltaEvent +from .beta_response_mcp_call_failed_event import BetaResponseMcpCallFailedEvent +from .beta_response_output_item_done_event import BetaResponseOutputItemDoneEvent +from .beta_response_content_part_done_event import BetaResponseContentPartDoneEvent +from .beta_response_output_item_added_event import BetaResponseOutputItemAddedEvent +from .beta_response_content_part_added_event import BetaResponseContentPartAddedEvent +from .beta_response_mcp_call_completed_event import BetaResponseMcpCallCompletedEvent +from .beta_response_reasoning_text_done_event import BetaResponseReasoningTextDoneEvent +from .beta_response_mcp_call_in_progress_event import BetaResponseMcpCallInProgressEvent +from .beta_response_reasoning_text_delta_event import BetaResponseReasoningTextDeltaEvent +from .beta_response_audio_transcript_done_event import BetaResponseAudioTranscriptDoneEvent +from .beta_response_compaction_compacting_event import BetaResponseCompactionCompactingEvent +from .beta_response_mcp_list_tools_failed_event import BetaResponseMcpListToolsFailedEvent +from .beta_response_audio_transcript_delta_event import BetaResponseAudioTranscriptDeltaEvent +from .beta_response_mcp_call_arguments_done_event import BetaResponseMcpCallArgumentsDoneEvent +from .beta_response_shell_call_command_done_event import BetaResponseShellCallCommandDoneEvent +from .beta_response_image_gen_call_completed_event import BetaResponseImageGenCallCompletedEvent +from .beta_response_mcp_call_arguments_delta_event import BetaResponseMcpCallArgumentsDeltaEvent +from .beta_response_mcp_list_tools_completed_event import BetaResponseMcpListToolsCompletedEvent +from .beta_response_shell_call_command_added_event import BetaResponseShellCallCommandAddedEvent +from .beta_response_shell_call_command_delta_event import BetaResponseShellCallCommandDeltaEvent +from .beta_response_image_gen_call_generating_event import BetaResponseImageGenCallGeneratingEvent +from .beta_response_web_search_call_completed_event import BetaResponseWebSearchCallCompletedEvent +from .beta_response_web_search_call_searching_event import BetaResponseWebSearchCallSearchingEvent +from .beta_response_file_search_call_completed_event import BetaResponseFileSearchCallCompletedEvent +from .beta_response_file_search_call_searching_event import BetaResponseFileSearchCallSearchingEvent +from .beta_response_image_gen_call_in_progress_event import BetaResponseImageGenCallInProgressEvent +from .beta_response_mcp_list_tools_in_progress_event import BetaResponseMcpListToolsInProgressEvent +from .beta_response_custom_tool_call_input_done_event import BetaResponseCustomToolCallInputDoneEvent +from .beta_response_reasoning_summary_part_done_event import BetaResponseReasoningSummaryPartDoneEvent +from .beta_response_reasoning_summary_text_done_event import BetaResponseReasoningSummaryTextDoneEvent +from .beta_response_web_search_call_in_progress_event import BetaResponseWebSearchCallInProgressEvent +from .beta_response_custom_tool_call_input_delta_event import BetaResponseCustomToolCallInputDeltaEvent +from .beta_response_file_search_call_in_progress_event import BetaResponseFileSearchCallInProgressEvent +from .beta_response_function_call_arguments_done_event import BetaResponseFunctionCallArgumentsDoneEvent +from .beta_response_image_gen_call_partial_image_event import BetaResponseImageGenCallPartialImageEvent +from .beta_response_output_text_annotation_added_event import BetaResponseOutputTextAnnotationAddedEvent +from .beta_response_reasoning_summary_part_added_event import BetaResponseReasoningSummaryPartAddedEvent +from .beta_response_reasoning_summary_text_delta_event import BetaResponseReasoningSummaryTextDeltaEvent +from .beta_response_function_call_arguments_delta_event import BetaResponseFunctionCallArgumentsDeltaEvent +from .beta_response_shell_call_output_content_done_event import BetaResponseShellCallOutputContentDoneEvent +from .beta_response_code_interpreter_call_code_done_event import BetaResponseCodeInterpreterCallCodeDoneEvent +from .beta_response_code_interpreter_call_completed_event import BetaResponseCodeInterpreterCallCompletedEvent +from .beta_response_shell_call_output_content_delta_event import BetaResponseShellCallOutputContentDeltaEvent +from .beta_response_code_interpreter_call_code_delta_event import BetaResponseCodeInterpreterCallCodeDeltaEvent +from .beta_response_code_interpreter_call_in_progress_event import BetaResponseCodeInterpreterCallInProgressEvent +from .beta_response_code_interpreter_call_interpreting_event import BetaResponseCodeInterpreterCallInterpretingEvent + +__all__ = ["BetaResponseStreamEvent"] + +BetaResponseStreamEvent: TypeAlias = Annotated[ + Union[ + BetaResponseAudioDeltaEvent, + BetaResponseAudioDoneEvent, + BetaResponseAudioTranscriptDeltaEvent, + BetaResponseAudioTranscriptDoneEvent, + BetaResponseCodeInterpreterCallCodeDeltaEvent, + BetaResponseCodeInterpreterCallCodeDoneEvent, + BetaResponseCodeInterpreterCallCompletedEvent, + BetaResponseCodeInterpreterCallInProgressEvent, + BetaResponseCodeInterpreterCallInterpretingEvent, + BetaResponseCompactionCompactingEvent, + BetaResponseCompletedEvent, + BetaResponseContentPartAddedEvent, + BetaResponseContentPartDoneEvent, + BetaResponseCreatedEvent, + BetaResponseErrorEvent, + BetaResponseFileSearchCallCompletedEvent, + BetaResponseFileSearchCallInProgressEvent, + BetaResponseFileSearchCallSearchingEvent, + BetaResponseFunctionCallArgumentsDeltaEvent, + BetaResponseFunctionCallArgumentsDoneEvent, + BetaResponseShellCallCommandAddedEvent, + BetaResponseShellCallCommandDeltaEvent, + BetaResponseShellCallCommandDoneEvent, + BetaResponseShellCallOutputContentDeltaEvent, + BetaResponseShellCallOutputContentDoneEvent, + BetaResponseInProgressEvent, + BetaResponseFailedEvent, + BetaResponseIncompleteEvent, + BetaResponseOutputItemAddedEvent, + BetaResponseOutputItemDoneEvent, + BetaResponseReasoningSummaryPartAddedEvent, + BetaResponseReasoningSummaryPartDoneEvent, + BetaResponseReasoningSummaryTextDeltaEvent, + BetaResponseReasoningSummaryTextDoneEvent, + BetaResponseReasoningTextDeltaEvent, + BetaResponseReasoningTextDoneEvent, + BetaResponseRefusalDeltaEvent, + BetaResponseRefusalDoneEvent, + BetaResponseTextDeltaEvent, + BetaResponseTextDoneEvent, + BetaResponseWebSearchCallCompletedEvent, + BetaResponseWebSearchCallInProgressEvent, + BetaResponseWebSearchCallSearchingEvent, + BetaResponseImageGenCallCompletedEvent, + BetaResponseImageGenCallGeneratingEvent, + BetaResponseImageGenCallInProgressEvent, + BetaResponseImageGenCallPartialImageEvent, + BetaResponseMcpCallArgumentsDeltaEvent, + BetaResponseMcpCallArgumentsDoneEvent, + BetaResponseMcpCallCompletedEvent, + BetaResponseMcpCallFailedEvent, + BetaResponseMcpCallInProgressEvent, + BetaResponseMcpListToolsCompletedEvent, + BetaResponseMcpListToolsFailedEvent, + BetaResponseMcpListToolsInProgressEvent, + BetaResponseOutputTextAnnotationAddedEvent, + BetaResponseQueuedEvent, + BetaResponseCustomToolCallInputDeltaEvent, + BetaResponseCustomToolCallInputDoneEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_text_config.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_text_config.py new file mode 100644 index 0000000..d0a471a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_text_config.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_format_text_config import BetaResponseFormatTextConfig + +__all__ = ["BetaResponseTextConfig"] + + +class BetaResponseTextConfig(BaseModel): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + format: Optional[BetaResponseFormatTextConfig] = None + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] = None + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. The default is `medium`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_text_config_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_text_config_param.py new file mode 100644 index 0000000..8372c9e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_text_config_param.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +from .beta_response_format_text_config_param import BetaResponseFormatTextConfigParam + +__all__ = ["BetaResponseTextConfigParam"] + + +class BetaResponseTextConfigParam(TypedDict, total=False): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + format: BetaResponseFormatTextConfigParam + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. The default is `medium`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_text_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_text_delta_event.py new file mode 100644 index 0000000..86a378e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_text_delta_event.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseTextDeltaEvent", "Logprob", "LogprobTopLogprob", "Agent"] + + +class LogprobTopLogprob(BaseModel): + token: Optional[str] = None + """A possible text token.""" + + logprob: Optional[float] = None + """The log probability of this token.""" + + +class Logprob(BaseModel): + """ + A logprob is the logarithmic probability that the model assigns to producing + a particular token at a given position in the sequence. Less-negative (higher) + logprob values indicate greater model confidence in that token choice. + """ + + token: str + """A possible text token.""" + + logprob: float + """The log probability of this token.""" + + top_logprobs: Optional[List[LogprobTopLogprob]] = None + """The log probabilities of up to 20 of the most likely tokens.""" + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseTextDeltaEvent(BaseModel): + """Emitted when there is an additional text delta.""" + + content_index: int + """The index of the content part that the text delta was added to.""" + + delta: str + """The text delta that was added.""" + + item_id: str + """The ID of the output item that the text delta was added to.""" + + logprobs: List[Logprob] + """The log probabilities of the tokens in the delta.""" + + output_index: int + """The index of the output item that the text delta was added to.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.output_text.delta"] + """The type of the event. Always `response.output_text.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_text_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_text_done_event.py new file mode 100644 index 0000000..afd2dd3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_text_done_event.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseTextDoneEvent", "Logprob", "LogprobTopLogprob", "Agent"] + + +class LogprobTopLogprob(BaseModel): + token: Optional[str] = None + """A possible text token.""" + + logprob: Optional[float] = None + """The log probability of this token.""" + + +class Logprob(BaseModel): + """ + A logprob is the logarithmic probability that the model assigns to producing + a particular token at a given position in the sequence. Less-negative (higher) + logprob values indicate greater model confidence in that token choice. + """ + + token: str + """A possible text token.""" + + logprob: float + """The log probability of this token.""" + + top_logprobs: Optional[List[LogprobTopLogprob]] = None + """The log probabilities of up to 20 of the most likely tokens.""" + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseTextDoneEvent(BaseModel): + """Emitted when text content is finalized.""" + + content_index: int + """The index of the content part that the text content is finalized.""" + + item_id: str + """The ID of the output item that the text content is finalized.""" + + logprobs: List[Logprob] + """The log probabilities of the tokens in the delta.""" + + output_index: int + """The index of the output item that the text content is finalized.""" + + sequence_number: int + """The sequence number for this event.""" + + text: str + """The text content that is finalized.""" + + type: Literal["response.output_text.done"] + """The type of the event. Always `response.output_text.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_tool_search_call.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_tool_search_call.py new file mode 100644 index 0000000..3f73c89 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_tool_search_call.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseToolSearchCall", "Agent"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseToolSearchCall(BaseModel): + id: str + """The unique ID of the tool search call item.""" + + arguments: object + """Arguments used for the tool search call.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the tool search call item that was recorded.""" + + type: Literal["tool_search_call"] + """The type of the item. Always `tool_search_call`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_tool_search_output_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_tool_search_output_item.py new file mode 100644 index 0000000..2baf973 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_tool_search_output_item.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_tool import BetaTool + +__all__ = ["BetaResponseToolSearchOutputItem", "Agent"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseToolSearchOutputItem(BaseModel): + id: str + """The unique ID of the tool search output item.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the tool search output item that was recorded.""" + + tools: List[BetaTool] + """The loaded tool definitions returned by tool search.""" + + type: Literal["tool_search_output"] + """The type of the item. Always `tool_search_output`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_tool_search_output_item_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_tool_search_output_item_param.py new file mode 100644 index 0000000..e718968 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_tool_search_output_item_param.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_tool import BetaTool + +__all__ = ["BetaResponseToolSearchOutputItemParam", "Agent"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseToolSearchOutputItemParam(BaseModel): + tools: List[BetaTool] + """The loaded tool definitions returned by the tool search output.""" + + type: Literal["tool_search_output"] + """The item type. Always `tool_search_output`.""" + + id: Optional[str] = None + """The unique ID of this tool search output.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Optional[Literal["server", "client"]] = None + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the tool search output.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_tool_search_output_item_param_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_tool_search_output_item_param_param.py new file mode 100644 index 0000000..1862221 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_tool_search_output_item_param_param.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from .beta_tool_param import BetaToolParam + +__all__ = ["BetaResponseToolSearchOutputItemParamParam", "Agent"] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class BetaResponseToolSearchOutputItemParamParam(TypedDict, total=False): + tools: Required[Iterable[BetaToolParam]] + """The loaded tool definitions returned by the tool search output.""" + + type: Required[Literal["tool_search_output"]] + """The item type. Always `tool_search_output`.""" + + id: Optional[str] + """The unique ID of this tool search output.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + call_id: Optional[str] + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the tool search output.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_usage.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_usage.py new file mode 100644 index 0000000..56182da --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_usage.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["BetaResponseUsage", "InputTokensDetails", "OutputTokensDetails"] + + +class InputTokensDetails(BaseModel): + """A detailed breakdown of the input tokens.""" + + cache_write_tokens: int + """The number of input tokens that were written to the cache.""" + + cached_tokens: int + """ + The number of tokens that were retrieved from the cache. + [More on prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching). + """ + + +class OutputTokensDetails(BaseModel): + """A detailed breakdown of the output tokens.""" + + reasoning_tokens: int + """The number of reasoning tokens.""" + + +class BetaResponseUsage(BaseModel): + """ + Represents token usage details including input tokens, output tokens, + a breakdown of output tokens, and the total tokens used. + """ + + input_tokens: int + """The number of input tokens.""" + + input_tokens_details: InputTokensDetails + """A detailed breakdown of the input tokens.""" + + output_tokens: int + """The number of output tokens.""" + + output_tokens_details: OutputTokensDetails + """A detailed breakdown of the output tokens.""" + + total_tokens: int + """The total number of tokens used.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_web_search_call_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_web_search_call_completed_event.py new file mode 100644 index 0000000..cc3a81a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_web_search_call_completed_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseWebSearchCallCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseWebSearchCallCompletedEvent(BaseModel): + """Emitted when a web search call is completed.""" + + item_id: str + """Unique ID for the output item associated with the web search call.""" + + output_index: int + """The index of the output item that the web search call is associated with.""" + + sequence_number: int + """The sequence number of the web search call being processed.""" + + type: Literal["response.web_search_call.completed"] + """The type of the event. Always `response.web_search_call.completed`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_web_search_call_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_web_search_call_in_progress_event.py new file mode 100644 index 0000000..e58923f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_web_search_call_in_progress_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseWebSearchCallInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseWebSearchCallInProgressEvent(BaseModel): + """Emitted when a web search call is initiated.""" + + item_id: str + """Unique ID for the output item associated with the web search call.""" + + output_index: int + """The index of the output item that the web search call is associated with.""" + + sequence_number: int + """The sequence number of the web search call being processed.""" + + type: Literal["response.web_search_call.in_progress"] + """The type of the event. Always `response.web_search_call.in_progress`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_web_search_call_searching_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_web_search_call_searching_event.py new file mode 100644 index 0000000..ce49cc8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_response_web_search_call_searching_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseWebSearchCallSearchingEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseWebSearchCallSearchingEvent(BaseModel): + """Emitted when a web search call is executing.""" + + item_id: str + """Unique ID for the output item associated with the web search call.""" + + output_index: int + """The index of the output item that the web search call is associated with.""" + + sequence_number: int + """The sequence number of the web search call being processed.""" + + type: Literal["response.web_search_call.searching"] + """The type of the event. Always `response.web_search_call.searching`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_responses_client_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_responses_client_event.py new file mode 100644 index 0000000..062ed28 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_responses_client_event.py @@ -0,0 +1,699 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_tool import BetaTool +from .beta_service_tier import BetaServiceTier +from .beta_response_input import BetaResponseInput +from .beta_response_prompt import BetaResponsePrompt +from .beta_tool_choice_mcp import BetaToolChoiceMcp +from .beta_tool_choice_shell import BetaToolChoiceShell +from .beta_tool_choice_types import BetaToolChoiceTypes +from .beta_tool_choice_custom import BetaToolChoiceCustom +from .beta_response_includable import BetaResponseIncludable +from .beta_tool_choice_allowed import BetaToolChoiceAllowed +from .beta_tool_choice_options import BetaToolChoiceOptions +from .beta_response_steer_event import BetaResponseSteerEvent +from .beta_response_text_config import BetaResponseTextConfig +from .beta_tool_choice_function import BetaToolChoiceFunction +from .beta_response_inject_event import BetaResponseInjectEvent +from .beta_tool_choice_apply_patch import BetaToolChoiceApplyPatch +from .beta_response_conversation_param import BetaResponseConversationParam + +__all__ = [ + "BetaResponsesClientEvent", + "ResponseCreate", + "ResponseCreateAccessPrograms", + "ResponseCreateContextManagement", + "ResponseCreateConversation", + "ResponseCreateModeration", + "ResponseCreateModerationPolicy", + "ResponseCreateModerationPolicyInput", + "ResponseCreateModerationPolicyOutput", + "ResponseCreateMultiAgent", + "ResponseCreatePromptCacheOptions", + "ResponseCreateReasoning", + "ResponseCreateStreamOptions", + "ResponseCreateToolChoice", + "ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam", +] + + +class ResponseCreateAccessPrograms(BaseModel): + """Domain-specific access programs to use for this request.""" + + cyber: Optional[Literal["standard", "daybreak_blue", "daybreak_red"]] = None + """The Cyber access program to use for this request. + + Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If + omitted, the API resolves the program from the model's Cyber tier and your + organization and project access, subject to model-specific eligibility + restrictions. By default, models without a Cyber tier use Standard. Blue-tier + models use Daybreak Blue when authorized; otherwise they fall back to Standard + unless the model requires Daybreak access. Red-tier models use Daybreak Red and + require authorization. Requests that require unavailable Daybreak access + return 403. An implicit Standard fallback is represented by null in the + response's access_programs field, rather than an explicit Standard selection. + """ + + +class ResponseCreateContextManagement(BaseModel): + type: str + """The context management entry type. Currently only 'compaction' is supported.""" + + compact_threshold: Optional[int] = None + """Token threshold at which compaction should be triggered for this entry.""" + + +ResponseCreateConversation: TypeAlias = Union[str, BetaResponseConversationParam, None] + + +class ResponseCreateModerationPolicyInput(BaseModel): + """The moderation policy for the response input.""" + + mode: Literal["score", "block"] + + +class ResponseCreateModerationPolicyOutput(BaseModel): + """The moderation policy for the response output.""" + + mode: Literal["score", "block"] + + +class ResponseCreateModerationPolicy(BaseModel): + """The policy to apply to moderated response input and output.""" + + input: Optional[ResponseCreateModerationPolicyInput] = None + """The moderation policy for the response input.""" + + output: Optional[ResponseCreateModerationPolicyOutput] = None + """The moderation policy for the response output.""" + + +class ResponseCreateModeration(BaseModel): + """Configuration for running moderation on the input and output of this response.""" + + model: str + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + policy: Optional[ResponseCreateModerationPolicy] = None + """The policy to apply to moderated response input and output.""" + + +class ResponseCreateMultiAgent(BaseModel): + """Configuration for server-hosted multi-agent execution.""" + + enabled: bool + """Whether to enable server-hosted multi-agent execution for this response.""" + + max_concurrent_subagents: Optional[int] = None + """ + `max_concurrent_subagents` sets the maximum number of subagents that can be + active simultaneously across the entire agent tree. It includes all + descendants—children, grandchildren, and deeper subagents—but excludes the root + agent. The API does not impose a fixed upper bound on this setting. The default + is `3`, which is recommended for most workloads. Multi-agent runs also have no + fixed limit on tree depth or the total number of subagents created during a run. + """ + + +class ResponseCreatePromptCacheOptions(BaseModel): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details. + """ + + comparison_response_id: Optional[str] = None + """The ID of a response to compare when diagnosing prompt cache reuse. + + Supplying this field requests prompt cache diagnostics when the feature is + enabled. + """ + + mode: Optional[Literal["implicit", "explicit"]] = None + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + prewarm: Optional[bool] = None + """Prepares the prompt cache without generating output. + + Defaults to `false`. When set to `true`, overrides the `generate` field to + `false`. + """ + + ttl: Optional[Literal["30m"]] = None + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + + +class ResponseCreateReasoning(BaseModel): + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + context: Optional[Literal["auto", "current_turn", "all_turns"]] = None + """ + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + generate_summary: Optional[Literal["auto", "concise", "detailed"]] = None + """**Deprecated:** use `summary` instead. + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + mode: Union[str, Literal["standard", "pro"], None] = None + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] = None + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. + + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. + """ + + +class ResponseCreateStreamOptions(BaseModel): + """Options for streaming responses. Only set this when you set `stream: true`.""" + + include_obfuscation: Optional[bool] = None + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + +class ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam(BaseModel): + type: Literal["programmatic_tool_calling"] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ResponseCreateToolChoice: TypeAlias = Union[ + BetaToolChoiceOptions, + BetaToolChoiceAllowed, + BetaToolChoiceTypes, + BetaToolChoiceFunction, + BetaToolChoiceMcp, + BetaToolChoiceCustom, + ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam, + BetaToolChoiceApplyPatch, + BetaToolChoiceShell, +] + + +class ResponseCreate(BaseModel): + """ + Client event for creating a response over a persistent WebSocket connection. + This payload uses the same top-level fields as `POST /v1/responses`, plus + WebSocket-only envelope metadata. + + Notes: + - `stream` is implicit over WebSocket and should not be sent. + - `background` is not supported over WebSocket. + - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`. + """ + + type: Literal["response.create"] + """The type of the client event. Always `response.create`.""" + + access_programs: Optional[ResponseCreateAccessPrograms] = None + """Domain-specific access programs to use for this request.""" + + background: Optional[bool] = None + """ + Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + """ + + context_management: Optional[List[ResponseCreateContextManagement]] = None + """Context management configuration for this request.""" + + conversation: Optional[ResponseCreateConversation] = None + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + include: Optional[List[BetaResponseIncludable]] = None + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + """ + + input: Union[str, BetaResponseInput, None] = None + """Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + """ + + instructions: Optional[str] = None + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + max_output_tokens: Optional[int] = None + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] = None + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + metadata: Optional[Dict[str, str]] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + None, + ] = None + """Model ID used to generate the response, like `gpt-6-astra`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + moderation: Optional[ResponseCreateModeration] = None + """Configuration for running moderation on the input and output of this response.""" + + multi_agent: Optional[ResponseCreateMultiAgent] = None + """Configuration for server-hosted multi-agent execution.""" + + parallel_tool_calls: Optional[bool] = None + """Whether to allow the model to run tool calls in parallel.""" + + previous_response_id: Optional[str] = None + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[BetaResponsePrompt] = None + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + prompt_cache_key: Optional[str] = None + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + """ + + prompt_cache_options: Optional[ResponseCreatePromptCacheOptions] = None + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] = None + """Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + """ + + reasoning: Optional[ResponseCreateReasoning] = None + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + safety_identifier: Optional[str] = None + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + service_tier: Optional[BetaServiceTier] = None + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + store: Optional[bool] = None + """Whether to store the generated model response for later retrieval via API. + + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + """ + + stream: Optional[bool] = None + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + """ + + stream_id: Optional[str] = None + """The WebSocket lane for this response. + + Requests with the same `stream_id` are processed FIFO, and events for the + response echo the same `stream_id`. + + `stream_id` controls routing; `previous_response_id` controls conversation + lineage, so a new lane can fork from a response created on another lane. + """ + + stream_options: Optional[ResponseCreateStreamOptions] = None + """Options for streaming responses. Only set this when you set `stream: true`.""" + + temperature: Optional[float] = None + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + text: Optional[BetaResponseTextConfig] = None + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + tool_choice: Optional[ResponseCreateToolChoice] = None + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: Optional[List[BetaTool]] = None + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_logprobs: Optional[int] = None + """ + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + """ + + top_p: Optional[float] = None + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + truncation: Optional[Literal["auto", "disabled"]] = None + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + user: Optional[str] = None + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + +BetaResponsesClientEvent: TypeAlias = Annotated[ + Union[ResponseCreate, BetaResponseSteerEvent, BetaResponseInjectEvent], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_responses_client_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_responses_client_event_param.py new file mode 100644 index 0000000..e875975 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_responses_client_event_param.py @@ -0,0 +1,698 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .beta_tool_param import BetaToolParam +from .beta_service_tier import BetaServiceTier +from .beta_response_includable import BetaResponseIncludable +from .beta_tool_choice_options import BetaToolChoiceOptions +from .beta_response_input_param import BetaResponseInputParam +from .beta_response_prompt_param import BetaResponsePromptParam +from .beta_tool_choice_mcp_param import BetaToolChoiceMcpParam +from .beta_tool_choice_shell_param import BetaToolChoiceShellParam +from .beta_tool_choice_types_param import BetaToolChoiceTypesParam +from .beta_tool_choice_custom_param import BetaToolChoiceCustomParam +from .beta_tool_choice_allowed_param import BetaToolChoiceAllowedParam +from .beta_response_steer_event_param import BetaResponseSteerEventParam +from .beta_response_text_config_param import BetaResponseTextConfigParam +from .beta_tool_choice_function_param import BetaToolChoiceFunctionParam +from .beta_response_inject_event_param import BetaResponseInjectEventParam +from .beta_tool_choice_apply_patch_param import BetaToolChoiceApplyPatchParam +from .beta_response_conversation_param_param import BetaResponseConversationParamParam + +__all__ = [ + "BetaResponsesClientEventParam", + "ResponseCreate", + "ResponseCreateAccessPrograms", + "ResponseCreateContextManagement", + "ResponseCreateConversation", + "ResponseCreateModeration", + "ResponseCreateModerationPolicy", + "ResponseCreateModerationPolicyInput", + "ResponseCreateModerationPolicyOutput", + "ResponseCreateMultiAgent", + "ResponseCreatePromptCacheOptions", + "ResponseCreateReasoning", + "ResponseCreateStreamOptions", + "ResponseCreateToolChoice", + "ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam", +] + + +class ResponseCreateAccessPrograms(TypedDict, total=False): + """Domain-specific access programs to use for this request.""" + + cyber: Literal["standard", "daybreak_blue", "daybreak_red"] + """The Cyber access program to use for this request. + + Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If + omitted, the API resolves the program from the model's Cyber tier and your + organization and project access, subject to model-specific eligibility + restrictions. By default, models without a Cyber tier use Standard. Blue-tier + models use Daybreak Blue when authorized; otherwise they fall back to Standard + unless the model requires Daybreak access. Red-tier models use Daybreak Red and + require authorization. Requests that require unavailable Daybreak access + return 403. An implicit Standard fallback is represented by null in the + response's access_programs field, rather than an explicit Standard selection. + """ + + +class ResponseCreateContextManagement(TypedDict, total=False): + type: Required[str] + """The context management entry type. Currently only 'compaction' is supported.""" + + compact_threshold: Optional[int] + """Token threshold at which compaction should be triggered for this entry.""" + + +ResponseCreateConversation: TypeAlias = Union[str, BetaResponseConversationParamParam] + + +class ResponseCreateModerationPolicyInput(TypedDict, total=False): + """The moderation policy for the response input.""" + + mode: Required[Literal["score", "block"]] + + +class ResponseCreateModerationPolicyOutput(TypedDict, total=False): + """The moderation policy for the response output.""" + + mode: Required[Literal["score", "block"]] + + +class ResponseCreateModerationPolicy(TypedDict, total=False): + """The policy to apply to moderated response input and output.""" + + input: Optional[ResponseCreateModerationPolicyInput] + """The moderation policy for the response input.""" + + output: Optional[ResponseCreateModerationPolicyOutput] + """The moderation policy for the response output.""" + + +class ResponseCreateModeration(TypedDict, total=False): + """Configuration for running moderation on the input and output of this response.""" + + model: Required[str] + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + policy: Optional[ResponseCreateModerationPolicy] + """The policy to apply to moderated response input and output.""" + + +class ResponseCreateMultiAgent(TypedDict, total=False): + """Configuration for server-hosted multi-agent execution.""" + + enabled: Required[bool] + """Whether to enable server-hosted multi-agent execution for this response.""" + + max_concurrent_subagents: int + """ + `max_concurrent_subagents` sets the maximum number of subagents that can be + active simultaneously across the entire agent tree. It includes all + descendants—children, grandchildren, and deeper subagents—but excludes the root + agent. The API does not impose a fixed upper bound on this setting. The default + is `3`, which is recommended for most workloads. Multi-agent runs also have no + fixed limit on tree depth or the total number of subagents created during a run. + """ + + +class ResponseCreatePromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details. + """ + + comparison_response_id: Optional[str] + """The ID of a response to compare when diagnosing prompt cache reuse. + + Supplying this field requests prompt cache diagnostics when the feature is + enabled. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + prewarm: bool + """Prepares the prompt cache without generating output. + + Defaults to `false`. When set to `true`, overrides the `generate` field to + `false`. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + + +class ResponseCreateReasoning(TypedDict, total=False): + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + context: Optional[Literal["auto", "current_turn", "all_turns"]] + """ + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + generate_summary: Optional[Literal["auto", "concise", "detailed"]] + """**Deprecated:** use `summary` instead. + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + mode: Union[str, Literal["standard", "pro"]] + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. + + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. + """ + + +class ResponseCreateStreamOptions(TypedDict, total=False): + """Options for streaming responses. Only set this when you set `stream: true`.""" + + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + +class ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ResponseCreateToolChoice: TypeAlias = Union[ + BetaToolChoiceOptions, + BetaToolChoiceAllowedParam, + BetaToolChoiceTypesParam, + BetaToolChoiceFunctionParam, + BetaToolChoiceMcpParam, + BetaToolChoiceCustomParam, + ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam, + BetaToolChoiceApplyPatchParam, + BetaToolChoiceShellParam, +] + + +class ResponseCreate(TypedDict, total=False): + """ + Client event for creating a response over a persistent WebSocket connection. + This payload uses the same top-level fields as `POST /v1/responses`, plus + WebSocket-only envelope metadata. + + Notes: + - `stream` is implicit over WebSocket and should not be sent. + - `background` is not supported over WebSocket. + - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`. + """ + + type: Required[Literal["response.create"]] + """The type of the client event. Always `response.create`.""" + + access_programs: ResponseCreateAccessPrograms + """Domain-specific access programs to use for this request.""" + + background: Optional[bool] + """ + Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + """ + + context_management: Optional[Iterable[ResponseCreateContextManagement]] + """Context management configuration for this request.""" + + conversation: Optional[ResponseCreateConversation] + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + include: Optional[List[BetaResponseIncludable]] + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + """ + + input: Union[str, BetaResponseInputParam] + """Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + """ + + instructions: Optional[str] + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + max_output_tokens: Optional[int] + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + metadata: Optional[Dict[str, str]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + ] + """Model ID used to generate the response, like `gpt-6-astra`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + moderation: Optional[ResponseCreateModeration] + """Configuration for running moderation on the input and output of this response.""" + + multi_agent: Optional[ResponseCreateMultiAgent] + """Configuration for server-hosted multi-agent execution.""" + + parallel_tool_calls: Optional[bool] + """Whether to allow the model to run tool calls in parallel.""" + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[BetaResponsePromptParam] + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + prompt_cache_key: Optional[str] + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + """ + + prompt_cache_options: ResponseCreatePromptCacheOptions + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] + """Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + """ + + reasoning: Optional[ResponseCreateReasoning] + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + safety_identifier: Optional[str] + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + service_tier: Optional[BetaServiceTier] + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + store: Optional[bool] + """Whether to store the generated model response for later retrieval via API. + + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + """ + + stream: Optional[bool] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + """ + + stream_id: str + """The WebSocket lane for this response. + + Requests with the same `stream_id` are processed FIFO, and events for the + response echo the same `stream_id`. + + `stream_id` controls routing; `previous_response_id` controls conversation + lineage, so a new lane can fork from a response created on another lane. + """ + + stream_options: Optional[ResponseCreateStreamOptions] + """Options for streaming responses. Only set this when you set `stream: true`.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + text: BetaResponseTextConfigParam + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + tool_choice: ResponseCreateToolChoice + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: Iterable[BetaToolParam] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_logprobs: Optional[int] + """ + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + truncation: Optional[Literal["auto", "disabled"]] + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + user: str + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + +BetaResponsesClientEventParam: TypeAlias = Union[ + ResponseCreate, BetaResponseSteerEventParam, BetaResponseInjectEventParam +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_responses_server_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_responses_server_event.py new file mode 100644 index 0000000..eb980bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_responses_server_event.py @@ -0,0 +1,943 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_failed_event import BetaResponseFailedEvent +from .beta_response_queued_event import BetaResponseQueuedEvent +from .beta_response_created_event import BetaResponseCreatedEvent +from .beta_response_completed_event import BetaResponseCompletedEvent +from .beta_response_text_done_event import BetaResponseTextDoneEvent +from .beta_response_audio_done_event import BetaResponseAudioDoneEvent +from .beta_response_incomplete_event import BetaResponseIncompleteEvent +from .beta_response_text_delta_event import BetaResponseTextDeltaEvent +from .beta_response_audio_delta_event import BetaResponseAudioDeltaEvent +from .beta_response_in_progress_event import BetaResponseInProgressEvent +from .beta_response_refusal_done_event import BetaResponseRefusalDoneEvent +from .beta_response_steer_failed_event import BetaResponseSteerFailedEvent +from .beta_response_inject_failed_event import BetaResponseInjectFailedEvent +from .beta_response_refusal_delta_event import BetaResponseRefusalDeltaEvent +from .beta_response_steer_pending_event import BetaResponseSteerPendingEvent +from .beta_response_inject_created_event import BetaResponseInjectCreatedEvent +from .beta_response_steer_accepted_event import BetaResponseSteerAcceptedEvent +from .beta_response_mcp_call_failed_event import BetaResponseMcpCallFailedEvent +from .beta_response_output_item_done_event import BetaResponseOutputItemDoneEvent +from .beta_response_content_part_done_event import BetaResponseContentPartDoneEvent +from .beta_response_output_item_added_event import BetaResponseOutputItemAddedEvent +from .beta_response_content_part_added_event import BetaResponseContentPartAddedEvent +from .beta_response_mcp_call_completed_event import BetaResponseMcpCallCompletedEvent +from .beta_response_reasoning_text_done_event import BetaResponseReasoningTextDoneEvent +from .beta_response_mcp_call_in_progress_event import BetaResponseMcpCallInProgressEvent +from .beta_response_reasoning_text_delta_event import BetaResponseReasoningTextDeltaEvent +from .beta_response_audio_transcript_done_event import BetaResponseAudioTranscriptDoneEvent +from .beta_response_compaction_compacting_event import BetaResponseCompactionCompactingEvent +from .beta_response_mcp_list_tools_failed_event import BetaResponseMcpListToolsFailedEvent +from .beta_response_audio_transcript_delta_event import BetaResponseAudioTranscriptDeltaEvent +from .beta_response_mcp_call_arguments_done_event import BetaResponseMcpCallArgumentsDoneEvent +from .beta_response_shell_call_command_done_event import BetaResponseShellCallCommandDoneEvent +from .beta_response_image_gen_call_completed_event import BetaResponseImageGenCallCompletedEvent +from .beta_response_mcp_call_arguments_delta_event import BetaResponseMcpCallArgumentsDeltaEvent +from .beta_response_mcp_list_tools_completed_event import BetaResponseMcpListToolsCompletedEvent +from .beta_response_shell_call_command_added_event import BetaResponseShellCallCommandAddedEvent +from .beta_response_shell_call_command_delta_event import BetaResponseShellCallCommandDeltaEvent +from .beta_response_image_gen_call_generating_event import BetaResponseImageGenCallGeneratingEvent +from .beta_response_web_search_call_completed_event import BetaResponseWebSearchCallCompletedEvent +from .beta_response_web_search_call_searching_event import BetaResponseWebSearchCallSearchingEvent +from .beta_response_file_search_call_completed_event import BetaResponseFileSearchCallCompletedEvent +from .beta_response_file_search_call_searching_event import BetaResponseFileSearchCallSearchingEvent +from .beta_response_image_gen_call_in_progress_event import BetaResponseImageGenCallInProgressEvent +from .beta_response_mcp_list_tools_in_progress_event import BetaResponseMcpListToolsInProgressEvent +from .beta_response_custom_tool_call_input_done_event import BetaResponseCustomToolCallInputDoneEvent +from .beta_response_reasoning_summary_part_done_event import BetaResponseReasoningSummaryPartDoneEvent +from .beta_response_reasoning_summary_text_done_event import BetaResponseReasoningSummaryTextDoneEvent +from .beta_response_web_search_call_in_progress_event import BetaResponseWebSearchCallInProgressEvent +from .beta_response_custom_tool_call_input_delta_event import BetaResponseCustomToolCallInputDeltaEvent +from .beta_response_file_search_call_in_progress_event import BetaResponseFileSearchCallInProgressEvent +from .beta_response_function_call_arguments_done_event import BetaResponseFunctionCallArgumentsDoneEvent +from .beta_response_image_gen_call_partial_image_event import BetaResponseImageGenCallPartialImageEvent +from .beta_response_output_text_annotation_added_event import BetaResponseOutputTextAnnotationAddedEvent +from .beta_response_reasoning_summary_part_added_event import BetaResponseReasoningSummaryPartAddedEvent +from .beta_response_reasoning_summary_text_delta_event import BetaResponseReasoningSummaryTextDeltaEvent +from .beta_response_function_call_arguments_delta_event import BetaResponseFunctionCallArgumentsDeltaEvent +from .beta_response_shell_call_output_content_done_event import BetaResponseShellCallOutputContentDoneEvent +from .beta_response_code_interpreter_call_code_done_event import BetaResponseCodeInterpreterCallCodeDoneEvent +from .beta_response_code_interpreter_call_completed_event import BetaResponseCodeInterpreterCallCompletedEvent +from .beta_response_shell_call_output_content_delta_event import BetaResponseShellCallOutputContentDeltaEvent +from .beta_response_code_interpreter_call_code_delta_event import BetaResponseCodeInterpreterCallCodeDeltaEvent +from .beta_response_code_interpreter_call_in_progress_event import BetaResponseCodeInterpreterCallInProgressEvent +from .beta_response_code_interpreter_call_interpreting_event import BetaResponseCodeInterpreterCallInterpretingEvent + +__all__ = [ + "BetaResponsesServerEvent", + "BetaResponseAudioWsDelta", + "BetaResponseAudioWsDone", + "BetaResponseAudioTranscriptWsDelta", + "BetaResponseAudioTranscriptWsDone", + "BetaResponseCodeInterpreterCallCodeWsDelta", + "BetaResponseCodeInterpreterCallCodeWsDone", + "BetaResponseCodeInterpreterCallWsCompleted", + "BetaResponseCodeInterpreterCallInWsProgress", + "BetaResponseCodeInterpreterCallWsInterpreting", + "BetaResponseCompactionWsCompacting", + "BetaResponseWsCompleted", + "BetaResponseContentPartWsAdded", + "BetaResponseContentPartWsDone", + "BetaResponseWsCreated", + "BetaResponseFileSearchCallWsCompleted", + "BetaResponseFileSearchCallInWsProgress", + "BetaResponseFileSearchCallWsSearching", + "BetaResponseFunctionCallArgumentsWsDelta", + "BetaResponseFunctionCallArgumentsWsDone", + "BetaResponseShellCallCommandWsAdded", + "BetaResponseShellCallCommandWsDelta", + "BetaResponseShellCallCommandWsDone", + "BetaResponseShellCallOutputContentWsDelta", + "BetaResponseShellCallOutputContentWsDone", + "BetaResponseInWsProgress", + "BetaResponseWsFailed", + "BetaResponseWsIncomplete", + "BetaResponseOutputItemWsAdded", + "BetaResponseOutputItemWsDone", + "BetaResponseReasoningSummaryPartWsAdded", + "BetaResponseReasoningSummaryPartWsDone", + "BetaResponseReasoningSummaryTextWsDelta", + "BetaResponseReasoningSummaryTextWsDone", + "BetaResponseReasoningTextWsDelta", + "BetaResponseReasoningTextWsDone", + "BetaResponseRefusalWsDelta", + "BetaResponseRefusalWsDone", + "BetaResponseTextWsDelta", + "BetaResponseTextWsDone", + "BetaResponseWebSearchCallWsCompleted", + "BetaResponseWebSearchCallInWsProgress", + "BetaResponseWebSearchCallWsSearching", + "BetaResponseImageGenCallWsCompleted", + "BetaResponseImageGenCallWsGenerating", + "BetaResponseImageGenCallInWsProgress", + "BetaResponseImageGenCallPartialWsImage", + "BetaResponseMcpCallArgumentsWsDelta", + "BetaResponseMcpCallArgumentsWsDone", + "BetaResponseMcpCallWsCompleted", + "BetaResponseMcpCallWsFailed", + "BetaResponseMcpCallInWsProgress", + "BetaResponseMcpListToolsWsCompleted", + "BetaResponseMcpListToolsWsFailed", + "BetaResponseMcpListToolsInWsProgress", + "BetaResponseOutputTextAnnotationWsAdded", + "BetaResponseWsQueued", + "BetaResponseCustomToolCallInputWsDelta", + "BetaResponseCustomToolCallInputWsDone", + "BetaResponseWsError", + "BetaResponseWsErrorError", + "BetaResponseWsErrorErrorMisalignment", + "BetaResponseWsErrorErrorMisalignmentSteer", + "BetaResponseWsErrorAgent", +] + + +class BetaResponseAudioWsDelta(BetaResponseAudioDeltaEvent): + """Emitted when there is a partial audio response.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseAudioWsDone(BetaResponseAudioDoneEvent): + """Emitted when the audio response is complete.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseAudioTranscriptWsDelta(BetaResponseAudioTranscriptDeltaEvent): + """Emitted when there is a partial transcript of audio.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseAudioTranscriptWsDone(BetaResponseAudioTranscriptDoneEvent): + """Emitted when the full audio transcript is completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseCodeInterpreterCallCodeWsDelta(BetaResponseCodeInterpreterCallCodeDeltaEvent): + """Emitted when a partial code snippet is streamed by the code interpreter.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseCodeInterpreterCallCodeWsDone(BetaResponseCodeInterpreterCallCodeDoneEvent): + """Emitted when the code snippet is finalized by the code interpreter.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseCodeInterpreterCallWsCompleted(BetaResponseCodeInterpreterCallCompletedEvent): + """Emitted when the code interpreter call is completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseCodeInterpreterCallInWsProgress(BetaResponseCodeInterpreterCallInProgressEvent): + """Emitted when a code interpreter call is in progress.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseCodeInterpreterCallWsInterpreting(BetaResponseCodeInterpreterCallInterpretingEvent): + """Emitted when the code interpreter is actively interpreting the code snippet.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseCompactionWsCompacting(BetaResponseCompactionCompactingEvent): + """Emitted when new summary content is sampled for a compaction trigger. + + Contains no summary content. + """ + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseWsCompleted(BetaResponseCompletedEvent): + """Emitted when the model response is complete.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseContentPartWsAdded(BetaResponseContentPartAddedEvent): + """Emitted when a new content part is added.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseContentPartWsDone(BetaResponseContentPartDoneEvent): + """Emitted when a content part is done.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseWsCreated(BetaResponseCreatedEvent): + """An event that is emitted when a response is created.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseFileSearchCallWsCompleted(BetaResponseFileSearchCallCompletedEvent): + """Emitted when a file search call is completed (results found).""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseFileSearchCallInWsProgress(BetaResponseFileSearchCallInProgressEvent): + """Emitted when a file search call is initiated.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseFileSearchCallWsSearching(BetaResponseFileSearchCallSearchingEvent): + """Emitted when a file search is currently searching.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseFunctionCallArgumentsWsDelta(BetaResponseFunctionCallArgumentsDeltaEvent): + """Emitted when there is a partial function-call arguments delta.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseFunctionCallArgumentsWsDone(BetaResponseFunctionCallArgumentsDoneEvent): + """Emitted when function-call arguments are finalized.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseShellCallCommandWsAdded(BetaResponseShellCallCommandAddedEvent): + """A streaming event that indicated a shell command was added to a tool call.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseShellCallCommandWsDelta(BetaResponseShellCallCommandDeltaEvent): + """A streaming event that indicated a shell command was incrementally updated.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseShellCallCommandWsDone(BetaResponseShellCallCommandDoneEvent): + """A streaming event that indicated a shell command was completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseShellCallOutputContentWsDelta(BetaResponseShellCallOutputContentDeltaEvent): + """A streaming event that indicated shell call output was incrementally added.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseShellCallOutputContentWsDone(BetaResponseShellCallOutputContentDoneEvent): + """A streaming event that indicated shell call output was completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseInWsProgress(BetaResponseInProgressEvent): + """Emitted when the response is in progress.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseWsFailed(BetaResponseFailedEvent): + """An event that is emitted when a response fails.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseWsIncomplete(BetaResponseIncompleteEvent): + """An event that is emitted when a response finishes as incomplete. + + Over WebSocket, steering can finish a response with + `response.incomplete_details.reason` set to `steered`, followed automatically + by a successor `response.created` that commits the queued steering input. + """ + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseOutputItemWsAdded(BetaResponseOutputItemAddedEvent): + """Emitted when a new output item is added.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseOutputItemWsDone(BetaResponseOutputItemDoneEvent): + """Emitted when an output item is marked done.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseReasoningSummaryPartWsAdded(BetaResponseReasoningSummaryPartAddedEvent): + """Emitted when a new reasoning summary part is added.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseReasoningSummaryPartWsDone(BetaResponseReasoningSummaryPartDoneEvent): + """Emitted when a reasoning summary part is completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseReasoningSummaryTextWsDelta(BetaResponseReasoningSummaryTextDeltaEvent): + """Emitted when a delta is added to a reasoning summary text.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseReasoningSummaryTextWsDone(BetaResponseReasoningSummaryTextDoneEvent): + """Emitted when a reasoning summary text is completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseReasoningTextWsDelta(BetaResponseReasoningTextDeltaEvent): + """Emitted when a delta is added to a reasoning text.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseReasoningTextWsDone(BetaResponseReasoningTextDoneEvent): + """Emitted when a reasoning text is completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseRefusalWsDelta(BetaResponseRefusalDeltaEvent): + """Emitted when there is a partial refusal text.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseRefusalWsDone(BetaResponseRefusalDoneEvent): + """Emitted when refusal text is finalized.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseTextWsDelta(BetaResponseTextDeltaEvent): + """Emitted when there is an additional text delta.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseTextWsDone(BetaResponseTextDoneEvent): + """Emitted when text content is finalized.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseWebSearchCallWsCompleted(BetaResponseWebSearchCallCompletedEvent): + """Emitted when a web search call is completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseWebSearchCallInWsProgress(BetaResponseWebSearchCallInProgressEvent): + """Emitted when a web search call is initiated.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseWebSearchCallWsSearching(BetaResponseWebSearchCallSearchingEvent): + """Emitted when a web search call is executing.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseImageGenCallWsCompleted(BetaResponseImageGenCallCompletedEvent): + """ + Emitted when an image generation tool call has completed and the final image is available. + """ + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseImageGenCallWsGenerating(BetaResponseImageGenCallGeneratingEvent): + """ + Emitted when an image generation tool call is actively generating an image (intermediate state). + """ + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseImageGenCallInWsProgress(BetaResponseImageGenCallInProgressEvent): + """Emitted when an image generation tool call is in progress.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseImageGenCallPartialWsImage(BetaResponseImageGenCallPartialImageEvent): + """Emitted when a partial image is available during image generation streaming.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseMcpCallArgumentsWsDelta(BetaResponseMcpCallArgumentsDeltaEvent): + """ + Emitted when there is a delta (partial update) to the arguments of an MCP tool call. + """ + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseMcpCallArgumentsWsDone(BetaResponseMcpCallArgumentsDoneEvent): + """Emitted when the arguments for an MCP tool call are finalized.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseMcpCallWsCompleted(BetaResponseMcpCallCompletedEvent): + """Emitted when an MCP tool call has completed successfully.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseMcpCallWsFailed(BetaResponseMcpCallFailedEvent): + """Emitted when an MCP tool call has failed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseMcpCallInWsProgress(BetaResponseMcpCallInProgressEvent): + """Emitted when an MCP tool call is in progress.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseMcpListToolsWsCompleted(BetaResponseMcpListToolsCompletedEvent): + """Emitted when the list of available MCP tools has been successfully retrieved.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseMcpListToolsWsFailed(BetaResponseMcpListToolsFailedEvent): + """Emitted when the attempt to list available MCP tools has failed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseMcpListToolsInWsProgress(BetaResponseMcpListToolsInProgressEvent): + """ + Emitted when the system is in the process of retrieving the list of available MCP tools. + """ + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseOutputTextAnnotationWsAdded(BetaResponseOutputTextAnnotationAddedEvent): + """Emitted when an annotation is added to output text content.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseWsQueued(BetaResponseQueuedEvent): + """Emitted when a response is queued and waiting to be processed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseCustomToolCallInputWsDelta(BetaResponseCustomToolCallInputDeltaEvent): + """Event representing a delta (partial update) to the input of a custom tool call.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseCustomToolCallInputWsDone(BetaResponseCustomToolCallInputDoneEvent): + """Event indicating that input for a custom tool call is complete.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class BetaResponseWsErrorErrorMisalignmentSteer(BaseModel): + """An optional public continuation instruction.""" + + message: str + """The public continuation instruction.""" + + +class BetaResponseWsErrorErrorMisalignment(BaseModel): + detailed_explanation: Optional[str] = None + """The public explanation for this block.""" + + error_type: Union[ + str, + Literal[ + "potentially_unintended_data_transfer", + "potentially_unintended_data_access", + "potentially_unintended_destructive_activity", + "other", + ], + None, + ] = None + """An optional classification; clients must accept additional values.""" + + steer: Optional[BetaResponseWsErrorErrorMisalignmentSteer] = None + """An optional public continuation instruction.""" + + +class BetaResponseWsErrorError(BaseModel): + """Details about the error.""" + + code: Optional[str] = None + """The error code that was emitted, if any.""" + + message: str + """The human-readable error message that was emitted.""" + + param: Optional[str] = None + """The parameter name that was associated with the error, if any.""" + + type: str + """The error type that was emitted.""" + + headers: Optional[Dict[str, str]] = None + """The response headers that were emitted with the error, if any.""" + + misalignment: Optional[BetaResponseWsErrorErrorMisalignment] = None + + +class BetaResponseWsErrorAgent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseWsError(BaseModel): + """Emitted when an error occurs while processing a Responses WebSocket request.""" + + error: BetaResponseWsErrorError + """Details about the error.""" + + type: Literal["error"] + """The type of the event. Always `error`.""" + + agent: Optional[BetaResponseWsErrorAgent] = None + """The agent that owns this multi-agent streaming event.""" + + sequence_number: Optional[int] = None + """The sequence number of an error emitted by the response stream.""" + + status: Optional[int] = None + """The HTTP status code associated with a WebSocket protocol error.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +BetaResponsesServerEvent: TypeAlias = Annotated[ + Union[ + BetaResponseAudioWsDelta, + BetaResponseAudioWsDone, + BetaResponseAudioTranscriptWsDelta, + BetaResponseAudioTranscriptWsDone, + BetaResponseCodeInterpreterCallCodeWsDelta, + BetaResponseCodeInterpreterCallCodeWsDone, + BetaResponseCodeInterpreterCallWsCompleted, + BetaResponseCodeInterpreterCallInWsProgress, + BetaResponseCodeInterpreterCallWsInterpreting, + BetaResponseCompactionWsCompacting, + BetaResponseWsCompleted, + BetaResponseContentPartWsAdded, + BetaResponseContentPartWsDone, + BetaResponseWsCreated, + BetaResponseFileSearchCallWsCompleted, + BetaResponseFileSearchCallInWsProgress, + BetaResponseFileSearchCallWsSearching, + BetaResponseFunctionCallArgumentsWsDelta, + BetaResponseFunctionCallArgumentsWsDone, + BetaResponseShellCallCommandWsAdded, + BetaResponseShellCallCommandWsDelta, + BetaResponseShellCallCommandWsDone, + BetaResponseShellCallOutputContentWsDelta, + BetaResponseShellCallOutputContentWsDone, + BetaResponseInWsProgress, + BetaResponseWsFailed, + BetaResponseWsIncomplete, + BetaResponseOutputItemWsAdded, + BetaResponseOutputItemWsDone, + BetaResponseReasoningSummaryPartWsAdded, + BetaResponseReasoningSummaryPartWsDone, + BetaResponseReasoningSummaryTextWsDelta, + BetaResponseReasoningSummaryTextWsDone, + BetaResponseReasoningTextWsDelta, + BetaResponseReasoningTextWsDone, + BetaResponseRefusalWsDelta, + BetaResponseRefusalWsDone, + BetaResponseTextWsDelta, + BetaResponseTextWsDone, + BetaResponseWebSearchCallWsCompleted, + BetaResponseWebSearchCallInWsProgress, + BetaResponseWebSearchCallWsSearching, + BetaResponseImageGenCallWsCompleted, + BetaResponseImageGenCallWsGenerating, + BetaResponseImageGenCallInWsProgress, + BetaResponseImageGenCallPartialWsImage, + BetaResponseMcpCallArgumentsWsDelta, + BetaResponseMcpCallArgumentsWsDone, + BetaResponseMcpCallWsCompleted, + BetaResponseMcpCallWsFailed, + BetaResponseMcpCallInWsProgress, + BetaResponseMcpListToolsWsCompleted, + BetaResponseMcpListToolsWsFailed, + BetaResponseMcpListToolsInWsProgress, + BetaResponseOutputTextAnnotationWsAdded, + BetaResponseWsQueued, + BetaResponseCustomToolCallInputWsDelta, + BetaResponseCustomToolCallInputWsDone, + BetaResponseWsError, + BetaResponseSteerAcceptedEvent, + BetaResponseSteerPendingEvent, + BetaResponseSteerFailedEvent, + BetaResponseInjectCreatedEvent, + BetaResponseInjectFailedEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_service_tier.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_service_tier.py new file mode 100644 index 0000000..fe6d349 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_service_tier.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal, TypeAlias + +__all__ = ["BetaServiceTier"] + +BetaServiceTier: TypeAlias = Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_skill_reference.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_skill_reference.py new file mode 100644 index 0000000..40337b8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_skill_reference.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaSkillReference"] + + +class BetaSkillReference(BaseModel): + skill_id: str + """The ID of the referenced skill.""" + + type: Literal["skill_reference"] + """References a skill created with the /v1/skills endpoint.""" + + version: Optional[str] = None + """Optional skill version. Use a positive integer or 'latest'. Omit for default.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_skill_reference_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_skill_reference_param.py new file mode 100644 index 0000000..fa87d81 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_skill_reference_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaSkillReferenceParam"] + + +class BetaSkillReferenceParam(TypedDict, total=False): + skill_id: Required[str] + """The ID of the referenced skill.""" + + type: Required[Literal["skill_reference"]] + """References a skill created with the /v1/skills endpoint.""" + + version: str + """Optional skill version. Use a positive integer or 'latest'. Omit for default.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool.py new file mode 100644 index 0000000..942b2b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool.py @@ -0,0 +1,390 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_custom_tool import BetaCustomTool +from .beta_computer_tool import BetaComputerTool +from .beta_function_tool import BetaFunctionTool +from .beta_namespace_tool import BetaNamespaceTool +from .beta_web_search_tool import BetaWebSearchTool +from .beta_apply_patch_tool import BetaApplyPatchTool +from .beta_file_search_tool import BetaFileSearchTool +from .beta_tool_search_tool import BetaToolSearchTool +from .beta_function_shell_tool import BetaFunctionShellTool +from .beta_web_search_preview_tool import BetaWebSearchPreviewTool +from .beta_computer_use_preview_tool import BetaComputerUsePreviewTool +from .beta_container_network_policy_disabled import BetaContainerNetworkPolicyDisabled +from .beta_container_network_policy_allowlist import BetaContainerNetworkPolicyAllowlist + +__all__ = [ + "BetaTool", + "Mcp", + "McpAllowedTools", + "McpAllowedToolsMcpToolFilter", + "McpRequireApproval", + "McpRequireApprovalMcpToolApprovalFilter", + "McpRequireApprovalMcpToolApprovalFilterAlways", + "McpRequireApprovalMcpToolApprovalFilterNever", + "CodeInterpreter", + "CodeInterpreterContainer", + "CodeInterpreterContainerCodeInterpreterToolAuto", + "CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy", + "ProgrammaticToolCalling", + "ImageGeneration", + "ImageGenerationInputImageMask", + "LocalShell", +] + + +class McpAllowedToolsMcpToolFilter(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpToolFilter, None] + + +class McpRequireApprovalMcpToolApprovalFilterAlways(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilterNever(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilter(BaseModel): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + + always: Optional[McpRequireApprovalMcpToolApprovalFilterAlways] = None + """A filter object to specify which tools are allowed.""" + + never: Optional[McpRequireApprovalMcpToolApprovalFilterNever] = None + """A filter object to specify which tools are allowed.""" + + +McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"], None] + + +class Mcp(BaseModel): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + """ + + server_label: str + """A label for this MCP server, used to identify it in tool calls.""" + + type: Literal["mcp"] + """The type of the MCP tool. Always `mcp`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + allowed_tools: Optional[McpAllowedTools] = None + """List of allowed tool names or a filter object.""" + + authorization: Optional[str] = None + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Optional[ + Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + ] = None + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors + [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors). + + This field is deprecated for models released after September 1, 2026. Use + `server_url` to connect to a remote MCP server, or `tunnel_id` to connect + through a Secure MCP Tunnel. + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + defer_loading: Optional[bool] = None + """Whether this MCP tool is deferred and discovered via tool search.""" + + headers: Optional[Dict[str, str]] = None + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[McpRequireApproval] = None + """Specify which of the MCP server's tools require approval.""" + + server_description: Optional[str] = None + """Optional description of the MCP server, used to provide more context.""" + + server_url: Optional[str] = None + """The URL for the MCP server. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: Optional[str] = None + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + +CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy: TypeAlias = Annotated[ + Union[BetaContainerNetworkPolicyDisabled, BetaContainerNetworkPolicyAllowlist], PropertyInfo(discriminator="type") +] + + +class CodeInterpreterContainerCodeInterpreterToolAuto(BaseModel): + """Configuration for a code interpreter container. + + Optionally specify the IDs of the files to run the code on. + """ + + type: Literal["auto"] + """Always `auto`.""" + + file_ids: Optional[List[str]] = None + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit for the code interpreter container.""" + + network_policy: Optional[CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy] = None + """Network access policy for the container.""" + + +CodeInterpreterContainer: TypeAlias = Union[str, CodeInterpreterContainerCodeInterpreterToolAuto] + + +class CodeInterpreter(BaseModel): + """A tool that runs Python code to help generate a response to a prompt.""" + + container: CodeInterpreterContainer + """The code interpreter container. + + Can be a container ID or an object that specifies uploaded file IDs to make + available to your code, along with an optional `memory_limit` setting. + """ + + type: Literal["code_interpreter"] + """The type of the code interpreter tool. Always `code_interpreter`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + +class ProgrammaticToolCalling(BaseModel): + type: Literal["programmatic_tool_calling"] + """The type of the tool. Always `programmatic_tool_calling`.""" + + +class ImageGenerationInputImageMask(BaseModel): + """Optional mask for inpainting. + + Contains `image_url` + (string, optional) and `file_id` (string, optional). + """ + + file_id: Optional[str] = None + """File ID for the mask image.""" + + image_url: Optional[str] = None + """Base64-encoded mask image.""" + + +class ImageGeneration(BaseModel): + """A tool that generates images using the GPT image models.""" + + type: Literal["image_generation"] + """The type of the image generation tool. Always `image_generation`.""" + + action: Optional[Literal["generate", "edit", "auto"]] = None + """Whether to generate a new image or edit an existing image. Default: `auto`.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] = None + """Allows to set transparency for the background of the generated image(s). + + Must be one of `transparent`, `opaque`, or `auto` (default value). When `auto` + is used, the model will automatically determine the best background for the + image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + """ + + input_fidelity: Optional[Literal["high", "low"]] = None + """Controls fidelity to the original input image(s). + + This parameter is supported for GPT image models that support input fidelity. + `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter. + """ + + input_image_mask: Optional[ImageGenerationInputImageMask] = None + """Optional mask for inpainting. + + Contains `image_url` (string, optional) and `file_id` (string, optional). + """ + + model: Union[ + str, + Literal[ + "gpt-image-1", + "gpt-image-1-mini", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-2.5-sunburst", + "gpt-image-2.5-sunburst-2026-09-08", + "gpt-image-2.5-flare", + "gpt-image-2.5-flare-2026-09-08", + "gpt-image-1.5", + "chatgpt-image-latest", + ], + None, + ] = None + """The image generation model to use. + + One of `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default: + `gpt-image-1`. + """ + + moderation: Optional[Literal["auto", "low"]] = None + """Moderation level for the generated image. Default: `auto`.""" + + output_compression: Optional[int] = None + """Compression level for the output image. Default: 100.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] = None + """The output format of the generated image. + + One of `png`, `webp`, or `jpeg`. Default: `png`. + """ + + partial_images: Optional[int] = None + """ + Number of partial images to generate in streaming mode, from 0 (default value) + to 3. + """ + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None + """The quality of the generated image. + + The GPT image models support `low`, `medium`, and `high`. + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, also support `xhigh` and `max`. Default: `auto`. + """ + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"], None] = None + """The size of the generated images. + + For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and + `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as + `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be + divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. + Resolutions above `2560x1440` are experimental, and the maximum supported + resolution is `3840x2160`. The requested size must also satisfy the model's + current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and + `1024x1536` are supported by the GPT image models; `auto` is supported for + models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, + `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, + or `1024x1792`. + """ + + +class LocalShell(BaseModel): + """A tool that allows the model to execute shell commands in a local environment.""" + + type: Literal["local_shell"] + """The type of the local shell tool. Always `local_shell`.""" + + +BetaTool: TypeAlias = Annotated[ + Union[ + BetaFunctionTool, + BetaFileSearchTool, + BetaComputerTool, + BetaComputerUsePreviewTool, + BetaWebSearchTool, + Mcp, + CodeInterpreter, + ProgrammaticToolCalling, + ImageGeneration, + LocalShell, + BetaFunctionShellTool, + BetaCustomTool, + BetaNamespaceTool, + BetaToolSearchTool, + BetaWebSearchPreviewTool, + BetaApplyPatchTool, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_allowed.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_allowed.py new file mode 100644 index 0000000..fd162ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_allowed.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceAllowed"] + + +class BetaToolChoiceAllowed(BaseModel): + """Constrains the tools available to the model to a pre-defined set.""" + + mode: Literal["auto", "required"] + """Constrains the tools available to the model to a pre-defined set. + + `auto` allows the model to pick from among the allowed tools and generate a + message. + + `required` requires the model to call one or more of the allowed tools. + """ + + tools: List[Dict[str, object]] + """A list of tool definitions that the model should be allowed to call. + + For the Responses API, the list of tool definitions might look like: + + ```json + [ + { "type": "function", "name": "get_weather" }, + { "type": "mcp", "server_label": "deepwiki" }, + { "type": "image_generation" } + ] + ``` + """ + + type: Literal["allowed_tools"] + """Allowed tool configuration type. Always `allowed_tools`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_allowed_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_allowed_param.py new file mode 100644 index 0000000..293c0a3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_allowed_param.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceAllowedParam"] + + +class BetaToolChoiceAllowedParam(TypedDict, total=False): + """Constrains the tools available to the model to a pre-defined set.""" + + mode: Required[Literal["auto", "required"]] + """Constrains the tools available to the model to a pre-defined set. + + `auto` allows the model to pick from among the allowed tools and generate a + message. + + `required` requires the model to call one or more of the allowed tools. + """ + + tools: Required[Iterable[Dict[str, object]]] + """A list of tool definitions that the model should be allowed to call. + + For the Responses API, the list of tool definitions might look like: + + ```json + [ + { "type": "function", "name": "get_weather" }, + { "type": "mcp", "server_label": "deepwiki" }, + { "type": "image_generation" } + ] + ``` + """ + + type: Required[Literal["allowed_tools"]] + """Allowed tool configuration type. Always `allowed_tools`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_apply_patch.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_apply_patch.py new file mode 100644 index 0000000..b3bcede --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_apply_patch.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceApplyPatch"] + + +class BetaToolChoiceApplyPatch(BaseModel): + """Forces the model to call the apply_patch tool when executing a tool call.""" + + type: Literal["apply_patch"] + """The tool to call. Always `apply_patch`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_apply_patch_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_apply_patch_param.py new file mode 100644 index 0000000..3b26b0c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_apply_patch_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceApplyPatchParam"] + + +class BetaToolChoiceApplyPatchParam(TypedDict, total=False): + """Forces the model to call the apply_patch tool when executing a tool call.""" + + type: Required[Literal["apply_patch"]] + """The tool to call. Always `apply_patch`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_custom.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_custom.py new file mode 100644 index 0000000..4f41f38 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_custom.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceCustom"] + + +class BetaToolChoiceCustom(BaseModel): + """Use this option to force the model to call a specific custom tool.""" + + name: str + """The name of the custom tool to call.""" + + type: Literal["custom"] + """For custom tool calling, the type is always `custom`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_custom_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_custom_param.py new file mode 100644 index 0000000..83d8d88 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_custom_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceCustomParam"] + + +class BetaToolChoiceCustomParam(TypedDict, total=False): + """Use this option to force the model to call a specific custom tool.""" + + name: Required[str] + """The name of the custom tool to call.""" + + type: Required[Literal["custom"]] + """For custom tool calling, the type is always `custom`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_function.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_function.py new file mode 100644 index 0000000..883fcb8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_function.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceFunction"] + + +class BetaToolChoiceFunction(BaseModel): + """Use this option to force the model to call a specific function.""" + + name: str + """The name of the function to call.""" + + type: Literal["function"] + """For function calling, the type is always `function`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_function_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_function_param.py new file mode 100644 index 0000000..faf08d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_function_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceFunctionParam"] + + +class BetaToolChoiceFunctionParam(TypedDict, total=False): + """Use this option to force the model to call a specific function.""" + + name: Required[str] + """The name of the function to call.""" + + type: Required[Literal["function"]] + """For function calling, the type is always `function`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_mcp.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_mcp.py new file mode 100644 index 0000000..41f660a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_mcp.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceMcp"] + + +class BetaToolChoiceMcp(BaseModel): + """ + Use this option to force the model to call a specific tool on a remote MCP server. + """ + + server_label: str + """The label of the MCP server to use.""" + + type: Literal["mcp"] + """For MCP tools, the type is always `mcp`.""" + + name: Optional[str] = None + """The name of the tool to call on the server.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_mcp_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_mcp_param.py new file mode 100644 index 0000000..5cd07fc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_mcp_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceMcpParam"] + + +class BetaToolChoiceMcpParam(TypedDict, total=False): + """ + Use this option to force the model to call a specific tool on a remote MCP server. + """ + + server_label: Required[str] + """The label of the MCP server to use.""" + + type: Required[Literal["mcp"]] + """For MCP tools, the type is always `mcp`.""" + + name: Optional[str] + """The name of the tool to call on the server.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_options.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_options.py new file mode 100644 index 0000000..43dfcb1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_options.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["BetaToolChoiceOptions"] + +BetaToolChoiceOptions: TypeAlias = Literal["none", "auto", "required"] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_shell.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_shell.py new file mode 100644 index 0000000..6710b6c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_shell.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceShell"] + + +class BetaToolChoiceShell(BaseModel): + """Forces the model to call the shell tool when a tool call is required.""" + + type: Literal["shell"] + """The tool to call. Always `shell`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_shell_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_shell_param.py new file mode 100644 index 0000000..b72913a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_shell_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceShellParam"] + + +class BetaToolChoiceShellParam(TypedDict, total=False): + """Forces the model to call the shell tool when a tool call is required.""" + + type: Required[Literal["shell"]] + """The tool to call. Always `shell`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_types.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_types.py new file mode 100644 index 0000000..82d9d17 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_types.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceTypes"] + + +class BetaToolChoiceTypes(BaseModel): + """ + Indicates that the model should use a built-in tool to generate a response. + [Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools). + """ + + type: Literal[ + "file_search", + "web_search_preview", + "computer", + "computer_use_preview", + "computer_use", + "web_search_preview_2025_03_11", + "image_generation", + "code_interpreter", + ] + """The type of hosted tool the model should to use. + + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + + Allowed values are: + + - `file_search` + - `web_search_preview` + - `computer` + - `computer_use_preview` + - `computer_use` + - `code_interpreter` + - `image_generation` + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_types_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_types_param.py new file mode 100644 index 0000000..e0eeff4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_choice_types_param.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceTypesParam"] + + +class BetaToolChoiceTypesParam(TypedDict, total=False): + """ + Indicates that the model should use a built-in tool to generate a response. + [Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools). + """ + + type: Required[ + Literal[ + "file_search", + "web_search_preview", + "computer", + "computer_use_preview", + "computer_use", + "web_search_preview_2025_03_11", + "image_generation", + "code_interpreter", + ] + ] + """The type of hosted tool the model should to use. + + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + + Allowed values are: + + - `file_search` + - `web_search_preview` + - `computer` + - `computer_use_preview` + - `computer_use` + - `code_interpreter` + - `image_generation` + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_param.py new file mode 100644 index 0000000..2155895 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_param.py @@ -0,0 +1,385 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .beta_custom_tool_param import BetaCustomToolParam +from .beta_computer_tool_param import BetaComputerToolParam +from .beta_function_tool_param import BetaFunctionToolParam +from .beta_namespace_tool_param import BetaNamespaceToolParam +from .beta_web_search_tool_param import BetaWebSearchToolParam +from .beta_apply_patch_tool_param import BetaApplyPatchToolParam +from .beta_file_search_tool_param import BetaFileSearchToolParam +from .beta_tool_search_tool_param import BetaToolSearchToolParam +from .beta_function_shell_tool_param import BetaFunctionShellToolParam +from .beta_web_search_preview_tool_param import BetaWebSearchPreviewToolParam +from .beta_computer_use_preview_tool_param import BetaComputerUsePreviewToolParam +from .beta_container_network_policy_disabled_param import BetaContainerNetworkPolicyDisabledParam +from .beta_container_network_policy_allowlist_param import BetaContainerNetworkPolicyAllowlistParam + +__all__ = [ + "BetaToolParam", + "Mcp", + "McpAllowedTools", + "McpAllowedToolsMcpToolFilter", + "McpRequireApproval", + "McpRequireApprovalMcpToolApprovalFilter", + "McpRequireApprovalMcpToolApprovalFilterAlways", + "McpRequireApprovalMcpToolApprovalFilterNever", + "CodeInterpreter", + "CodeInterpreterContainer", + "CodeInterpreterContainerCodeInterpreterToolAuto", + "CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy", + "ProgrammaticToolCalling", + "ImageGeneration", + "ImageGenerationInputImageMask", + "LocalShell", +] + + +class McpAllowedToolsMcpToolFilter(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +McpAllowedTools: TypeAlias = Union[SequenceNotStr[str], McpAllowedToolsMcpToolFilter] + + +class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + + always: McpRequireApprovalMcpToolApprovalFilterAlways + """A filter object to specify which tools are allowed.""" + + never: McpRequireApprovalMcpToolApprovalFilterNever + """A filter object to specify which tools are allowed.""" + + +McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"]] + + +class Mcp(TypedDict, total=False): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + """ + + server_label: Required[str] + """A label for this MCP server, used to identify it in tool calls.""" + + type: Required[Literal["mcp"]] + """The type of the MCP tool. Always `mcp`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + allowed_tools: Optional[McpAllowedTools] + """List of allowed tool names or a filter object.""" + + authorization: str + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors + [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors). + + This field is deprecated for models released after September 1, 2026. Use + `server_url` to connect to a remote MCP server, or `tunnel_id` to connect + through a Secure MCP Tunnel. + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + defer_loading: bool + """Whether this MCP tool is deferred and discovered via tool search.""" + + headers: Optional[Dict[str, str]] + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[McpRequireApproval] + """Specify which of the MCP server's tools require approval.""" + + server_description: str + """Optional description of the MCP server, used to provide more context.""" + + server_url: str + """The URL for the MCP server. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: str + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + +CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy: TypeAlias = Union[ + BetaContainerNetworkPolicyDisabledParam, BetaContainerNetworkPolicyAllowlistParam +] + + +class CodeInterpreterContainerCodeInterpreterToolAuto(TypedDict, total=False): + """Configuration for a code interpreter container. + + Optionally specify the IDs of the files to run the code on. + """ + + type: Required[Literal["auto"]] + """Always `auto`.""" + + file_ids: SequenceNotStr[str] + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] + """The memory limit for the code interpreter container.""" + + network_policy: CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy + """Network access policy for the container.""" + + +CodeInterpreterContainer: TypeAlias = Union[str, CodeInterpreterContainerCodeInterpreterToolAuto] + + +class CodeInterpreter(TypedDict, total=False): + """A tool that runs Python code to help generate a response to a prompt.""" + + container: Required[CodeInterpreterContainer] + """The code interpreter container. + + Can be a container ID or an object that specifies uploaded file IDs to make + available to your code, along with an optional `memory_limit` setting. + """ + + type: Required[Literal["code_interpreter"]] + """The type of the code interpreter tool. Always `code_interpreter`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + +class ProgrammaticToolCalling(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The type of the tool. Always `programmatic_tool_calling`.""" + + +class ImageGenerationInputImageMask(TypedDict, total=False): + """Optional mask for inpainting. + + Contains `image_url` + (string, optional) and `file_id` (string, optional). + """ + + file_id: str + """File ID for the mask image.""" + + image_url: str + """Base64-encoded mask image.""" + + +class ImageGeneration(TypedDict, total=False): + """A tool that generates images using the GPT image models.""" + + type: Required[Literal["image_generation"]] + """The type of the image generation tool. Always `image_generation`.""" + + action: Literal["generate", "edit", "auto"] + """Whether to generate a new image or edit an existing image. Default: `auto`.""" + + background: Literal["transparent", "opaque", "auto"] + """Allows to set transparency for the background of the generated image(s). + + Must be one of `transparent`, `opaque`, or `auto` (default value). When `auto` + is used, the model will automatically determine the best background for the + image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + """ + + input_fidelity: Optional[Literal["high", "low"]] + """Controls fidelity to the original input image(s). + + This parameter is supported for GPT image models that support input fidelity. + `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter. + """ + + input_image_mask: ImageGenerationInputImageMask + """Optional mask for inpainting. + + Contains `image_url` (string, optional) and `file_id` (string, optional). + """ + + model: Union[ + str, + Literal[ + "gpt-image-1", + "gpt-image-1-mini", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-2.5-sunburst", + "gpt-image-2.5-sunburst-2026-09-08", + "gpt-image-2.5-flare", + "gpt-image-2.5-flare-2026-09-08", + "gpt-image-1.5", + "chatgpt-image-latest", + ], + ] + """The image generation model to use. + + One of `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default: + `gpt-image-1`. + """ + + moderation: Literal["auto", "low"] + """Moderation level for the generated image. Default: `auto`.""" + + output_compression: int + """Compression level for the output image. Default: 100.""" + + output_format: Literal["png", "webp", "jpeg"] + """The output format of the generated image. + + One of `png`, `webp`, or `jpeg`. Default: `png`. + """ + + partial_images: int + """ + Number of partial images to generate in streaming mode, from 0 (default value) + to 3. + """ + + quality: Literal["low", "medium", "high", "xhigh", "max", "auto"] + """The quality of the generated image. + + The GPT image models support `low`, `medium`, and `high`. + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, also support `xhigh` and `max`. Default: `auto`. + """ + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]] + """The size of the generated images. + + For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and + `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as + `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be + divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. + Resolutions above `2560x1440` are experimental, and the maximum supported + resolution is `3840x2160`. The requested size must also satisfy the model's + current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and + `1024x1536` are supported by the GPT image models; `auto` is supported for + models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, + `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, + or `1024x1792`. + """ + + +class LocalShell(TypedDict, total=False): + """A tool that allows the model to execute shell commands in a local environment.""" + + type: Required[Literal["local_shell"]] + """The type of the local shell tool. Always `local_shell`.""" + + +BetaToolParam: TypeAlias = Union[ + BetaFunctionToolParam, + BetaFileSearchToolParam, + BetaComputerToolParam, + BetaComputerUsePreviewToolParam, + BetaWebSearchToolParam, + Mcp, + CodeInterpreter, + ProgrammaticToolCalling, + ImageGeneration, + LocalShell, + BetaFunctionShellToolParam, + BetaCustomToolParam, + BetaNamespaceToolParam, + BetaToolSearchToolParam, + BetaWebSearchPreviewToolParam, + BetaApplyPatchToolParam, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_search_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_search_tool.py new file mode 100644 index 0000000..a949bb7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_search_tool.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolSearchTool"] + + +class BetaToolSearchTool(BaseModel): + """Hosted or BYOT tool search configuration for deferred tools.""" + + type: Literal["tool_search"] + """The type of the tool. Always `tool_search`.""" + + description: Optional[str] = None + """Description shown to the model for a client-executed tool search tool.""" + + execution: Optional[Literal["server", "client"]] = None + """Whether tool search is executed by the server or by the client.""" + + parameters: Optional[object] = None + """Parameter schema for a client-executed tool search tool.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_search_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_search_tool_param.py new file mode 100644 index 0000000..71febf2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_tool_search_tool_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolSearchToolParam"] + + +class BetaToolSearchToolParam(TypedDict, total=False): + """Hosted or BYOT tool search configuration for deferred tools.""" + + type: Required[Literal["tool_search"]] + """The type of the tool. Always `tool_search`.""" + + description: Optional[str] + """Description shown to the model for a client-executed tool search tool.""" + + execution: Literal["server", "client"] + """Whether tool search is executed by the server or by the client.""" + + parameters: Optional[object] + """Parameter schema for a client-executed tool search tool.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_web_search_preview_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_web_search_preview_tool.py new file mode 100644 index 0000000..967d7b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_web_search_preview_tool.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaWebSearchPreviewTool", "UserLocation"] + + +class UserLocation(BaseModel): + """The approximate location of the user. + + If omitted or null, defaults to the United States. To avoid this fallback, pass `{"type": "approximate"}` without location fields. To localize results, provide the relevant location fields. + """ + + type: Literal["approximate"] + """The type of location approximation. Always `approximate`.""" + + city: Optional[str] = None + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] = None + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] = None + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] = None + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + +class BetaWebSearchPreviewTool(BaseModel): + """This tool searches the web for relevant results to use in a response. + + Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + """ + + type: Literal["web_search_preview", "web_search_preview_2025_03_11"] + """The type of the web search tool. + + One of `web_search_preview` or `web_search_preview_2025_03_11`. + """ + + search_content_types: Optional[List[Literal["text", "image"]]] = None + + search_context_size: Optional[Literal["low", "medium", "high"]] = None + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] = None + """The approximate location of the user. + + If omitted or null, defaults to the United States. To avoid this fallback, pass + `{"type": "approximate"}` without location fields. To localize results, provide + the relevant location fields. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_web_search_preview_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_web_search_preview_tool_param.py new file mode 100644 index 0000000..c5fae46 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_web_search_preview_tool_param.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaWebSearchPreviewToolParam", "UserLocation"] + + +class UserLocation(TypedDict, total=False): + """The approximate location of the user. + + If omitted or null, defaults to the United States. To avoid this fallback, pass `{"type": "approximate"}` without location fields. To localize results, provide the relevant location fields. + """ + + type: Required[Literal["approximate"]] + """The type of location approximation. Always `approximate`.""" + + city: Optional[str] + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + +class BetaWebSearchPreviewToolParam(TypedDict, total=False): + """This tool searches the web for relevant results to use in a response. + + Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + """ + + type: Required[Literal["web_search_preview", "web_search_preview_2025_03_11"]] + """The type of the web search tool. + + One of `web_search_preview` or `web_search_preview_2025_03_11`. + """ + + search_content_types: List[Literal["text", "image"]] + + search_context_size: Literal["low", "medium", "high"] + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] + """The approximate location of the user. + + If omitted or null, defaults to the United States. To avoid this fallback, pass + `{"type": "approximate"}` without location fields. To localize results, provide + the relevant location fields. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_web_search_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_web_search_tool.py new file mode 100644 index 0000000..6adcf29 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_web_search_tool.py @@ -0,0 +1,90 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaWebSearchTool", "Filters", "UserLocation"] + + +class Filters(BaseModel): + """Filters for the search.""" + + allowed_domains: Optional[List[str]] = None + """Allowed domains for the search. + + If not provided, all domains are allowed. Subdomains of the provided domains are + allowed as well. + + Example: `["pubmed.ncbi.nlm.nih.gov"]` + """ + + +class UserLocation(BaseModel): + """The approximate location of the user. + + If omitted or null, defaults to the + United States. To avoid this fallback, pass `{"type": "approximate"}` without + location fields. To localize results, provide the relevant location fields. + """ + + city: Optional[str] = None + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] = None + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] = None + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] = None + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + type: Optional[Literal["approximate"]] = None + """The type of location approximation. Always `approximate`.""" + + +class BetaWebSearchTool(BaseModel): + """Search the Internet for sources related to the prompt. + + Learn more about the + [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + """ + + type: Literal["web_search", "web_search_2025_08_26"] + """The type of the web search tool. + + One of `web_search` or `web_search_2025_08_26`. + """ + + external_web_access: Optional[bool] = None + """Allow live internet access for web search. + + Defaults to true when omitted. When false, the web search tool runs in + offline/cache-only mode and will not fetch new external content. + """ + + filters: Optional[Filters] = None + """Filters for the search.""" + + search_context_size: Optional[Literal["low", "medium", "high"]] = None + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] = None + """The approximate location of the user. + + If omitted or null, defaults to the United States. To avoid this fallback, pass + `{"type": "approximate"}` without location fields. To localize results, provide + the relevant location fields. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/beta_web_search_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/beta_web_search_tool_param.py new file mode 100644 index 0000000..1d6cdf5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/beta_web_search_tool_param.py @@ -0,0 +1,92 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["BetaWebSearchToolParam", "Filters", "UserLocation"] + + +class Filters(TypedDict, total=False): + """Filters for the search.""" + + allowed_domains: Optional[SequenceNotStr[str]] + """Allowed domains for the search. + + If not provided, all domains are allowed. Subdomains of the provided domains are + allowed as well. + + Example: `["pubmed.ncbi.nlm.nih.gov"]` + """ + + +class UserLocation(TypedDict, total=False): + """The approximate location of the user. + + If omitted or null, defaults to the + United States. To avoid this fallback, pass `{"type": "approximate"}` without + location fields. To localize results, provide the relevant location fields. + """ + + city: Optional[str] + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + type: Literal["approximate"] + """The type of location approximation. Always `approximate`.""" + + +class BetaWebSearchToolParam(TypedDict, total=False): + """Search the Internet for sources related to the prompt. + + Learn more about the + [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + """ + + type: Required[Literal["web_search", "web_search_2025_08_26"]] + """The type of the web search tool. + + One of `web_search` or `web_search_2025_08_26`. + """ + + external_web_access: bool + """Allow live internet access for web search. + + Defaults to true when omitted. When false, the web search tool runs in + offline/cache-only mode and will not fetch new external content. + """ + + filters: Optional[Filters] + """Filters for the search.""" + + search_context_size: Literal["low", "medium", "high"] + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] + """The approximate location of the user. + + If omitted or null, defaults to the United States. To avoid this fallback, pass + `{"type": "approximate"}` without location fields. To localize results, provide + the relevant location fields. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chat/__init__.py b/venv/lib/python3.12/site-packages/openai/types/beta/chat/__init__.py new file mode 100644 index 0000000..9d48db4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chat/__init__.py @@ -0,0 +1 @@ +from __future__ import annotations diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chat/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chat/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..eb4170f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chat/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__init__.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__init__.py new file mode 100644 index 0000000..1622518 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__init__.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .chat_session import ChatSession as ChatSession +from .chatkit_thread import ChatKitThread as ChatKitThread +from .chatkit_attachment import ChatKitAttachment as ChatKitAttachment +from .thread_list_params import ThreadListParams as ThreadListParams +from .chat_session_status import ChatSessionStatus as ChatSessionStatus +from .chatkit_widget_item import ChatKitWidgetItem as ChatKitWidgetItem +from .chat_session_history import ChatSessionHistory as ChatSessionHistory +from .session_create_params import SessionCreateParams as SessionCreateParams +from .thread_delete_response import ThreadDeleteResponse as ThreadDeleteResponse +from .chat_session_file_upload import ChatSessionFileUpload as ChatSessionFileUpload +from .chat_session_rate_limits import ChatSessionRateLimits as ChatSessionRateLimits +from .chatkit_thread_item_list import ChatKitThreadItemList as ChatKitThreadItemList +from .thread_list_items_params import ThreadListItemsParams as ThreadListItemsParams +from .chat_session_workflow_param import ChatSessionWorkflowParam as ChatSessionWorkflowParam +from .chatkit_response_output_text import ChatKitResponseOutputText as ChatKitResponseOutputText +from .chat_session_rate_limits_param import ChatSessionRateLimitsParam as ChatSessionRateLimitsParam +from .chat_session_expires_after_param import ChatSessionExpiresAfterParam as ChatSessionExpiresAfterParam +from .chatkit_thread_user_message_item import ChatKitThreadUserMessageItem as ChatKitThreadUserMessageItem +from .chat_session_chatkit_configuration import ChatSessionChatKitConfiguration as ChatSessionChatKitConfiguration +from .chat_session_automatic_thread_titling import ( + ChatSessionAutomaticThreadTitling as ChatSessionAutomaticThreadTitling, +) +from .chatkit_thread_assistant_message_item import ( + ChatKitThreadAssistantMessageItem as ChatKitThreadAssistantMessageItem, +) +from .chat_session_chatkit_configuration_param import ( + ChatSessionChatKitConfigurationParam as ChatSessionChatKitConfigurationParam, +) diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..21df3de Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session.cpython-312.pyc new file mode 100644 index 0000000..44ad22e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_automatic_thread_titling.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_automatic_thread_titling.cpython-312.pyc new file mode 100644 index 0000000..675a968 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_automatic_thread_titling.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_chatkit_configuration.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_chatkit_configuration.cpython-312.pyc new file mode 100644 index 0000000..c18af6e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_chatkit_configuration.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_chatkit_configuration_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_chatkit_configuration_param.cpython-312.pyc new file mode 100644 index 0000000..4ed2218 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_chatkit_configuration_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_expires_after_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_expires_after_param.cpython-312.pyc new file mode 100644 index 0000000..2b46dc9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_expires_after_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_file_upload.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_file_upload.cpython-312.pyc new file mode 100644 index 0000000..eff2619 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_file_upload.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_history.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_history.cpython-312.pyc new file mode 100644 index 0000000..acbacc9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_history.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_rate_limits.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_rate_limits.cpython-312.pyc new file mode 100644 index 0000000..8ccf4ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_rate_limits.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_rate_limits_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_rate_limits_param.cpython-312.pyc new file mode 100644 index 0000000..a2b5e3c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_rate_limits_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_status.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_status.cpython-312.pyc new file mode 100644 index 0000000..735962d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_status.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_workflow_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_workflow_param.cpython-312.pyc new file mode 100644 index 0000000..f2dff91 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chat_session_workflow_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_attachment.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_attachment.cpython-312.pyc new file mode 100644 index 0000000..1238d06 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_attachment.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_response_output_text.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_response_output_text.cpython-312.pyc new file mode 100644 index 0000000..981584e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_response_output_text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_thread.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_thread.cpython-312.pyc new file mode 100644 index 0000000..b9e2217 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_thread.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_thread_assistant_message_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_thread_assistant_message_item.cpython-312.pyc new file mode 100644 index 0000000..b053866 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_thread_assistant_message_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_thread_item_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_thread_item_list.cpython-312.pyc new file mode 100644 index 0000000..7753813 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_thread_item_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_thread_user_message_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_thread_user_message_item.cpython-312.pyc new file mode 100644 index 0000000..b55fba8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_thread_user_message_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_widget_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_widget_item.cpython-312.pyc new file mode 100644 index 0000000..b6fde76 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/chatkit_widget_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/session_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/session_create_params.cpython-312.pyc new file mode 100644 index 0000000..2087c98 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/session_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/thread_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/thread_delete_response.cpython-312.pyc new file mode 100644 index 0000000..5ba32ce Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/thread_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/thread_list_items_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/thread_list_items_params.cpython-312.pyc new file mode 100644 index 0000000..3a545e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/thread_list_items_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/thread_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/thread_list_params.cpython-312.pyc new file mode 100644 index 0000000..87312eb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/__pycache__/thread_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session.py new file mode 100644 index 0000000..ad3bf50 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel +from ..chatkit_workflow import ChatKitWorkflow +from .chat_session_status import ChatSessionStatus +from .chat_session_rate_limits import ChatSessionRateLimits +from .chat_session_chatkit_configuration import ChatSessionChatKitConfiguration + +__all__ = ["ChatSession"] + + +class ChatSession(BaseModel): + """Represents a ChatKit session and its resolved configuration.""" + + id: str + """Identifier for the ChatKit session.""" + + chatkit_configuration: ChatSessionChatKitConfiguration + """Resolved ChatKit feature configuration for the session.""" + + client_secret: str + """Ephemeral client secret that authenticates session requests.""" + + expires_at: int + """Unix timestamp (in seconds) for when the session expires.""" + + max_requests_per_1_minute: int + """Convenience copy of the per-minute request limit.""" + + object: Literal["chatkit.session"] + """Type discriminator that is always `chatkit.session`.""" + + rate_limits: ChatSessionRateLimits + """Resolved rate limit values.""" + + status: ChatSessionStatus + """Current lifecycle state of the session.""" + + user: str + """User identifier associated with the session.""" + + workflow: ChatKitWorkflow + """Workflow metadata for the session.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py new file mode 100644 index 0000000..353083b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ...._models import BaseModel + +__all__ = ["ChatSessionAutomaticThreadTitling"] + + +class ChatSessionAutomaticThreadTitling(BaseModel): + """Automatic thread title preferences for the session.""" + + enabled: bool + """Whether automatic thread titling is enabled.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_chatkit_configuration.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_chatkit_configuration.py new file mode 100644 index 0000000..e18ec5d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_chatkit_configuration.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ...._models import BaseModel +from .chat_session_history import ChatSessionHistory +from .chat_session_file_upload import ChatSessionFileUpload +from .chat_session_automatic_thread_titling import ChatSessionAutomaticThreadTitling + +__all__ = ["ChatSessionChatKitConfiguration"] + + +class ChatSessionChatKitConfiguration(BaseModel): + """ChatKit configuration for the session.""" + + automatic_thread_titling: ChatSessionAutomaticThreadTitling + """Automatic thread titling preferences.""" + + file_upload: ChatSessionFileUpload + """Upload settings for the session.""" + + history: ChatSessionHistory + """History retention configuration.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py new file mode 100644 index 0000000..d94b6b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py @@ -0,0 +1,76 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ChatSessionChatKitConfigurationParam", "AutomaticThreadTitling", "FileUpload", "History"] + + +class AutomaticThreadTitling(TypedDict, total=False): + """Configuration for automatic thread titling. + + When omitted, automatic thread titling is enabled by default. + """ + + enabled: bool + """Enable automatic thread title generation. Defaults to true.""" + + +class FileUpload(TypedDict, total=False): + """Configuration for upload enablement and limits. + + When omitted, uploads are disabled by default (max_files 10, max_file_size 512 MB). + """ + + enabled: bool + """Enable uploads for this session. Defaults to false.""" + + max_file_size: int + """Maximum size in megabytes for each uploaded file. + + Defaults to 512 MB, which is the maximum allowable size. + """ + + max_files: int + """Maximum number of files that can be uploaded to the session. Defaults to 10.""" + + +class History(TypedDict, total=False): + """Configuration for chat history retention. + + When omitted, history is enabled by default with no limit on recent_threads (null). + """ + + enabled: bool + """Enables chat users to access previous ChatKit threads. Defaults to true.""" + + recent_threads: int + """Number of recent ChatKit threads users have access to. + + Defaults to unlimited when unset. + """ + + +class ChatSessionChatKitConfigurationParam(TypedDict, total=False): + """Optional per-session configuration settings for ChatKit behavior.""" + + automatic_thread_titling: AutomaticThreadTitling + """Configuration for automatic thread titling. + + When omitted, automatic thread titling is enabled by default. + """ + + file_upload: FileUpload + """Configuration for upload enablement and limits. + + When omitted, uploads are disabled by default (max_files 10, max_file_size 512 + MB). + """ + + history: History + """Configuration for chat history retention. + + When omitted, history is enabled by default with no limit on recent_threads + (null). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_expires_after_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_expires_after_param.py new file mode 100644 index 0000000..b67f415 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_expires_after_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatSessionExpiresAfterParam"] + + +class ChatSessionExpiresAfterParam(TypedDict, total=False): + """Controls when the session expires relative to an anchor timestamp.""" + + anchor: Required[Literal["created_at"]] + """Base timestamp used to calculate expiration. Currently fixed to `created_at`.""" + + seconds: Required[int] + """Number of seconds after the anchor when the session expires.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_file_upload.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_file_upload.py new file mode 100644 index 0000000..fc13e88 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_file_upload.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["ChatSessionFileUpload"] + + +class ChatSessionFileUpload(BaseModel): + """Upload permissions and limits applied to the session.""" + + enabled: bool + """Indicates if uploads are enabled for the session.""" + + max_file_size: Optional[int] = None + """Maximum upload size in megabytes.""" + + max_files: Optional[int] = None + """Maximum number of uploads allowed during the session.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_history.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_history.py new file mode 100644 index 0000000..c54eb69 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_history.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["ChatSessionHistory"] + + +class ChatSessionHistory(BaseModel): + """History retention preferences returned for the session.""" + + enabled: bool + """Indicates if chat history is persisted for the session.""" + + recent_threads: Optional[int] = None + """Number of prior threads surfaced in history views. + + Defaults to null when all history is retained. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_rate_limits.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_rate_limits.py new file mode 100644 index 0000000..77feac4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_rate_limits.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ...._models import BaseModel + +__all__ = ["ChatSessionRateLimits"] + + +class ChatSessionRateLimits(BaseModel): + """Active per-minute request limit for the session.""" + + max_requests_per_1_minute: int + """Maximum allowed requests per one-minute window.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_rate_limits_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_rate_limits_param.py new file mode 100644 index 0000000..b7c58a4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_rate_limits_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ChatSessionRateLimitsParam"] + + +class ChatSessionRateLimitsParam(TypedDict, total=False): + """Controls request rate limits for the session.""" + + max_requests_per_1_minute: int + """Maximum number of requests allowed per minute for the session. Defaults to 10.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_status.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_status.py new file mode 100644 index 0000000..2b17aec --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_status.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ChatSessionStatus"] + +ChatSessionStatus: TypeAlias = Literal["active", "expired", "cancelled"] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_workflow_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_workflow_param.py new file mode 100644 index 0000000..44d113c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chat_session_workflow_param.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import Required, TypedDict + +__all__ = ["ChatSessionWorkflowParam", "Tracing"] + + +class Tracing(TypedDict, total=False): + """Optional tracing overrides for the workflow invocation. + + When omitted, tracing is enabled by default. + """ + + enabled: bool + """Whether tracing is enabled during the session. Defaults to true.""" + + +class ChatSessionWorkflowParam(TypedDict, total=False): + """Workflow reference and overrides applied to the chat session.""" + + id: Required[str] + """Identifier for the workflow invoked by the session.""" + + state_variables: Dict[str, Union[str, bool, float]] + """State variables forwarded to the workflow. + + Keys may be up to 64 characters, values must be primitive types, and the map + defaults to an empty object. + """ + + tracing: Tracing + """Optional tracing overrides for the workflow invocation. + + When omitted, tracing is enabled by default. + """ + + version: str + """Specific workflow version to run. Defaults to the latest deployed version.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_attachment.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_attachment.py new file mode 100644 index 0000000..d00098e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_attachment.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ChatKitAttachment"] + + +class ChatKitAttachment(BaseModel): + """Attachment metadata included on thread items.""" + + id: str + """Identifier for the attachment.""" + + mime_type: str + """MIME type of the attachment.""" + + name: str + """Original display name for the attachment.""" + + preview_url: Optional[str] = None + """Preview URL for rendering the attachment inline.""" + + type: Literal["image", "file"] + """Attachment discriminator.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_response_output_text.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_response_output_text.py new file mode 100644 index 0000000..3baa2f6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_response_output_text.py @@ -0,0 +1,72 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "ChatKitResponseOutputText", + "Annotation", + "AnnotationFile", + "AnnotationFileSource", + "AnnotationURL", + "AnnotationURLSource", +] + + +class AnnotationFileSource(BaseModel): + """File attachment referenced by the annotation.""" + + filename: str + """Filename referenced by the annotation.""" + + type: Literal["file"] + """Type discriminator that is always `file`.""" + + +class AnnotationFile(BaseModel): + """Annotation that references an uploaded file.""" + + source: AnnotationFileSource + """File attachment referenced by the annotation.""" + + type: Literal["file"] + """Type discriminator that is always `file` for this annotation.""" + + +class AnnotationURLSource(BaseModel): + """URL referenced by the annotation.""" + + type: Literal["url"] + """Type discriminator that is always `url`.""" + + url: str + """URL referenced by the annotation.""" + + +class AnnotationURL(BaseModel): + """Annotation that references a URL.""" + + source: AnnotationURLSource + """URL referenced by the annotation.""" + + type: Literal["url"] + """Type discriminator that is always `url` for this annotation.""" + + +Annotation: TypeAlias = Annotated[Union[AnnotationFile, AnnotationURL], PropertyInfo(discriminator="type")] + + +class ChatKitResponseOutputText(BaseModel): + """Assistant response text accompanied by optional annotations.""" + + annotations: List[Annotation] + """Ordered list of annotations attached to the response text.""" + + text: str + """Assistant generated text.""" + + type: Literal["output_text"] + """Type discriminator that is always `output_text`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_thread.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_thread.py new file mode 100644 index 0000000..b85ef63 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_thread.py @@ -0,0 +1,64 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = ["ChatKitThread", "Status", "StatusActive", "StatusLocked", "StatusClosed"] + + +class StatusActive(BaseModel): + """Indicates that a thread is active.""" + + type: Literal["active"] + """Status discriminator that is always `active`.""" + + +class StatusLocked(BaseModel): + """Indicates that a thread is locked and cannot accept new input.""" + + reason: Optional[str] = None + """Reason that the thread was locked. Defaults to null when no reason is recorded.""" + + type: Literal["locked"] + """Status discriminator that is always `locked`.""" + + +class StatusClosed(BaseModel): + """Indicates that a thread has been closed.""" + + reason: Optional[str] = None + """Reason that the thread was closed. Defaults to null when no reason is recorded.""" + + type: Literal["closed"] + """Status discriminator that is always `closed`.""" + + +Status: TypeAlias = Annotated[Union[StatusActive, StatusLocked, StatusClosed], PropertyInfo(discriminator="type")] + + +class ChatKitThread(BaseModel): + """Represents a ChatKit thread and its current status.""" + + id: str + """Identifier of the thread.""" + + created_at: int + """Unix timestamp (in seconds) for when the thread was created.""" + + object: Literal["chatkit.thread"] + """Type discriminator that is always `chatkit.thread`.""" + + status: Status + """Current status for the thread. Defaults to `active` for newly created threads.""" + + title: Optional[str] = None + """Optional human-readable title for the thread. + + Defaults to null when no title has been generated. + """ + + user: str + """Free-form string that identifies your end user who owns the thread.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py new file mode 100644 index 0000000..6966555 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ...._models import BaseModel +from .chatkit_response_output_text import ChatKitResponseOutputText + +__all__ = ["ChatKitThreadAssistantMessageItem"] + + +class ChatKitThreadAssistantMessageItem(BaseModel): + """Assistant-authored message within a thread.""" + + id: str + """Identifier of the thread item.""" + + content: List[ChatKitResponseOutputText] + """Ordered assistant response segments.""" + + created_at: int + """Unix timestamp (in seconds) for when the item was created.""" + + object: Literal["chatkit.thread_item"] + """Type discriminator that is always `chatkit.thread_item`.""" + + thread_id: str + """Identifier of the parent thread.""" + + type: Literal["chatkit.assistant_message"] + """Type discriminator that is always `chatkit.assistant_message`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_thread_item_list.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_thread_item_list.py new file mode 100644 index 0000000..3646dcc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_thread_item_list.py @@ -0,0 +1,154 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .chatkit_widget_item import ChatKitWidgetItem +from .chatkit_thread_user_message_item import ChatKitThreadUserMessageItem +from .chatkit_thread_assistant_message_item import ChatKitThreadAssistantMessageItem + +__all__ = [ + "ChatKitThreadItemList", + "Data", + "DataChatKitClientToolCall", + "DataChatKitTask", + "DataChatKitTaskGroup", + "DataChatKitTaskGroupTask", +] + + +class DataChatKitClientToolCall(BaseModel): + """Record of a client side tool invocation initiated by the assistant.""" + + id: str + """Identifier of the thread item.""" + + arguments: str + """JSON-encoded arguments that were sent to the tool.""" + + call_id: str + """Identifier for the client tool call.""" + + created_at: int + """Unix timestamp (in seconds) for when the item was created.""" + + name: str + """Tool name that was invoked.""" + + object: Literal["chatkit.thread_item"] + """Type discriminator that is always `chatkit.thread_item`.""" + + output: Optional[str] = None + """JSON-encoded output captured from the tool. + + Defaults to null while execution is in progress. + """ + + status: Literal["in_progress", "completed"] + """Execution status for the tool call.""" + + thread_id: str + """Identifier of the parent thread.""" + + type: Literal["chatkit.client_tool_call"] + """Type discriminator that is always `chatkit.client_tool_call`.""" + + +class DataChatKitTask(BaseModel): + """Task emitted by the workflow to show progress and status updates.""" + + id: str + """Identifier of the thread item.""" + + created_at: int + """Unix timestamp (in seconds) for when the item was created.""" + + heading: Optional[str] = None + """Optional heading for the task. Defaults to null when not provided.""" + + object: Literal["chatkit.thread_item"] + """Type discriminator that is always `chatkit.thread_item`.""" + + summary: Optional[str] = None + """Optional summary that describes the task. Defaults to null when omitted.""" + + task_type: Literal["custom", "thought"] + """Subtype for the task.""" + + thread_id: str + """Identifier of the parent thread.""" + + type: Literal["chatkit.task"] + """Type discriminator that is always `chatkit.task`.""" + + +class DataChatKitTaskGroupTask(BaseModel): + """Task entry that appears within a TaskGroup.""" + + heading: Optional[str] = None + """Optional heading for the grouped task. Defaults to null when not provided.""" + + summary: Optional[str] = None + """Optional summary that describes the grouped task. + + Defaults to null when omitted. + """ + + type: Literal["custom", "thought"] + """Subtype for the grouped task.""" + + +class DataChatKitTaskGroup(BaseModel): + """Collection of workflow tasks grouped together in the thread.""" + + id: str + """Identifier of the thread item.""" + + created_at: int + """Unix timestamp (in seconds) for when the item was created.""" + + object: Literal["chatkit.thread_item"] + """Type discriminator that is always `chatkit.thread_item`.""" + + tasks: List[DataChatKitTaskGroupTask] + """Tasks included in the group.""" + + thread_id: str + """Identifier of the parent thread.""" + + type: Literal["chatkit.task_group"] + """Type discriminator that is always `chatkit.task_group`.""" + + +Data: TypeAlias = Annotated[ + Union[ + ChatKitThreadUserMessageItem, + ChatKitThreadAssistantMessageItem, + ChatKitWidgetItem, + DataChatKitClientToolCall, + DataChatKitTask, + DataChatKitTaskGroup, + ], + PropertyInfo(discriminator="type"), +] + + +class ChatKitThreadItemList(BaseModel): + """A paginated list of thread items rendered for the ChatKit API.""" + + data: List[Data] + """A list of items""" + + first_id: Optional[str] = None + """The ID of the first item in the list.""" + + has_more: bool + """Whether there are more items available.""" + + last_id: Optional[str] = None + """The ID of the last item in the list.""" + + object: Literal["list"] + """The type of object returned, must be `list`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_thread_user_message_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_thread_user_message_item.py new file mode 100644 index 0000000..bb72894 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_thread_user_message_item.py @@ -0,0 +1,87 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .chatkit_attachment import ChatKitAttachment + +__all__ = [ + "ChatKitThreadUserMessageItem", + "Content", + "ContentInputText", + "ContentQuotedText", + "InferenceOptions", + "InferenceOptionsToolChoice", +] + + +class ContentInputText(BaseModel): + """Text block that a user contributed to the thread.""" + + text: str + """Plain-text content supplied by the user.""" + + type: Literal["input_text"] + """Type discriminator that is always `input_text`.""" + + +class ContentQuotedText(BaseModel): + """Quoted snippet that the user referenced in their message.""" + + text: str + """Quoted text content.""" + + type: Literal["quoted_text"] + """Type discriminator that is always `quoted_text`.""" + + +Content: TypeAlias = Annotated[Union[ContentInputText, ContentQuotedText], PropertyInfo(discriminator="type")] + + +class InferenceOptionsToolChoice(BaseModel): + """Preferred tool to invoke. Defaults to null when ChatKit should auto-select.""" + + id: str + """Identifier of the requested tool.""" + + +class InferenceOptions(BaseModel): + """Inference overrides applied to the message. Defaults to null when unset.""" + + model: Optional[str] = None + """Model name that generated the response. + + Defaults to null when using the session default. + """ + + tool_choice: Optional[InferenceOptionsToolChoice] = None + """Preferred tool to invoke. Defaults to null when ChatKit should auto-select.""" + + +class ChatKitThreadUserMessageItem(BaseModel): + """User-authored messages within a thread.""" + + id: str + """Identifier of the thread item.""" + + attachments: List[ChatKitAttachment] + """Attachments associated with the user message. Defaults to an empty list.""" + + content: List[Content] + """Ordered content elements supplied by the user.""" + + created_at: int + """Unix timestamp (in seconds) for when the item was created.""" + + inference_options: Optional[InferenceOptions] = None + """Inference overrides applied to the message. Defaults to null when unset.""" + + object: Literal["chatkit.thread_item"] + """Type discriminator that is always `chatkit.thread_item`.""" + + thread_id: str + """Identifier of the parent thread.""" + + type: Literal["chatkit.user_message"] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_widget_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_widget_item.py new file mode 100644 index 0000000..6aad952 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/chatkit_widget_item.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ChatKitWidgetItem"] + + +class ChatKitWidgetItem(BaseModel): + """Thread item that renders a widget payload.""" + + id: str + """Identifier of the thread item.""" + + created_at: int + """Unix timestamp (in seconds) for when the item was created.""" + + object: Literal["chatkit.thread_item"] + """Type discriminator that is always `chatkit.thread_item`.""" + + thread_id: str + """Identifier of the parent thread.""" + + type: Literal["chatkit.widget"] + """Type discriminator that is always `chatkit.widget`.""" + + widget: str + """Serialized widget payload rendered in the UI.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/session_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/session_create_params.py new file mode 100644 index 0000000..ba79d88 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/session_create_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .chat_session_workflow_param import ChatSessionWorkflowParam +from .chat_session_rate_limits_param import ChatSessionRateLimitsParam +from .chat_session_expires_after_param import ChatSessionExpiresAfterParam +from .chat_session_chatkit_configuration_param import ChatSessionChatKitConfigurationParam + +__all__ = ["SessionCreateParams"] + + +class SessionCreateParams(TypedDict, total=False): + user: Required[str] + """ + A free-form string that identifies your end user; ensures this Session can + access other objects that have the same `user` scope. + """ + + workflow: Required[ChatSessionWorkflowParam] + """Workflow that powers the session.""" + + chatkit_configuration: ChatSessionChatKitConfigurationParam + """Optional overrides for ChatKit runtime configuration features""" + + expires_after: ChatSessionExpiresAfterParam + """Optional override for session expiration timing in seconds from creation. + + Defaults to 10 minutes. + """ + + rate_limits: ChatSessionRateLimitsParam + """Optional override for per-minute request limits. When omitted, defaults to 10.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/thread_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/thread_delete_response.py new file mode 100644 index 0000000..a8e069a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/thread_delete_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ThreadDeleteResponse"] + + +class ThreadDeleteResponse(BaseModel): + """Confirmation payload returned after deleting a thread.""" + + id: str + """Identifier of the deleted thread.""" + + deleted: bool + """Indicates that the thread has been deleted.""" + + object: Literal["chatkit.thread.deleted"] + """Type discriminator that is always `chatkit.thread.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/thread_list_items_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/thread_list_items_params.py new file mode 100644 index 0000000..3f36caf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/thread_list_items_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ThreadListItemsParams"] + + +class ThreadListItemsParams(TypedDict, total=False): + after: str + """List items created after this thread item ID. + + Defaults to null for the first page. + """ + + before: str + """List items created before this thread item ID. + + Defaults to null for the newest results. + """ + + limit: int + """Maximum number of thread items to return. Defaults to 20.""" + + order: Literal["asc", "desc"] + """Sort order for results by creation time. Defaults to `desc`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/thread_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/thread_list_params.py new file mode 100644 index 0000000..2163c8c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit/thread_list_params.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ThreadListParams"] + + +class ThreadListParams(TypedDict, total=False): + after: str + """List items created after this thread item ID. + + Defaults to null for the first page. + """ + + before: str + """List items created before this thread item ID. + + Defaults to null for the newest results. + """ + + limit: int + """Maximum number of thread items to return. Defaults to 20.""" + + order: Literal["asc", "desc"] + """Sort order for results by creation time. Defaults to `desc`.""" + + user: str + """Filter threads that belong to this user identifier. + + Defaults to null to return all users. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/chatkit_workflow.py b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit_workflow.py new file mode 100644 index 0000000..214f6da --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/chatkit_workflow.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Union, Optional + +from ..._models import BaseModel + +__all__ = ["ChatKitWorkflow", "Tracing"] + + +class Tracing(BaseModel): + """Tracing settings applied to the workflow.""" + + enabled: bool + """Indicates whether tracing is enabled.""" + + +class ChatKitWorkflow(BaseModel): + """Workflow metadata and state returned for the session.""" + + id: str + """Identifier of the workflow backing the session.""" + + state_variables: Optional[Dict[str, Union[str, bool, float]]] = None + """State variable key-value pairs applied when invoking the workflow. + + Defaults to null when no overrides were provided. + """ + + tracing: Tracing + """Tracing settings applied to the workflow.""" + + version: Optional[str] = None + """Specific workflow version used for the session. + + Defaults to null when using the latest deployment. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/code_interpreter_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/code_interpreter_tool.py new file mode 100644 index 0000000..c02b9b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/code_interpreter_tool.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["CodeInterpreterTool"] + + +class CodeInterpreterTool(BaseModel): + type: Literal["code_interpreter"] + """The type of tool being defined: `code_interpreter`""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/code_interpreter_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/code_interpreter_tool_param.py new file mode 100644 index 0000000..9b5ded2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/code_interpreter_tool_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["CodeInterpreterToolParam"] + + +class CodeInterpreterToolParam(TypedDict, total=False): + type: Required[Literal["code_interpreter"]] + """The type of tool being defined: `code_interpreter`""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/environment.py b/venv/lib/python3.12/site-packages/openai/types/beta/environment.py new file mode 100644 index 0000000..5084574 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/environment.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .hosted_skill import HostedSkill +from .hosted_plugin import HostedPlugin +from .hosted_environment_file import HostedEnvironmentFile + +__all__ = [ + "Environment", + "EnvironmentResourceNone", + "EnvironmentResourceOpenAIHosted", + "EnvironmentResourceOpenAIHostedNetwork", + "EnvironmentResourceOpenAIHostedPackages", + "EnvironmentResourceSelfHosted", +] + + +class EnvironmentResourceNone(BaseModel): + """ + The session talks to CCA without selecting or provisioning an execution environment. + """ + + type: Literal["none"] + """The type of the object. Always `none`.""" + + +class EnvironmentResourceOpenAIHostedNetwork(BaseModel): + """The effective network access policy for the environment.""" + + access: Literal["enabled", "disabled", "restricted"] + """The environment's network access mode. + + - `enabled` - Allows unrestricted network access. + - `disabled` - Disables network access. + - `restricted` - Allows access only to configured domains. + """ + + allowed_domains: List[str] + """Domains the environment may access when network access is restricted.""" + + +class EnvironmentResourceOpenAIHostedPackages(BaseModel): + """Packages installed in the environment.""" + + npm: List[str] + """npm packages installed globally in the environment.""" + + python: List[str] + """Python packages installed in the environment.""" + + system: List[str] + """System packages installed in the environment.""" + + +class EnvironmentResourceOpenAIHosted(BaseModel): + """An environment hosted by OpenAI.""" + + id: str + """The public ID of the environment.""" + + capability_directories: List[str] + """Directories that contain capabilities exposed to the agent.""" + + files: List[HostedEnvironmentFile] + """Files available in the environment, excluding their contents.""" + + network: EnvironmentResourceOpenAIHostedNetwork + """The effective network access policy for the environment.""" + + packages: EnvironmentResourceOpenAIHostedPackages + """Packages installed in the environment.""" + + plugins: List[HostedPlugin] + """Plugins installed in the environment, excluding their archive contents.""" + + skills: List[HostedSkill] + """Skills installed in the environment, excluding their archive contents.""" + + type: Literal["openai_hosted"] + """The type of the object. Always `openai_hosted`.""" + + +class EnvironmentResourceSelfHosted(BaseModel): + """An environment hosted by the application.""" + + id: str + """The public ID of the environment.""" + + capability_directories: List[str] + """Directories that contain capabilities exposed to the agent.""" + + remote_url: str + """ + Pass this URL unchanged to `codex exec-server --remote` when connecting this + environment. + """ + + type: Literal["self_hosted"] + """The type of the object. Always `self_hosted`.""" + + workspace_directory: str + """The absolute project directory inside the environment. + + Defaults to `/workspace`. + """ + + +Environment: TypeAlias = Annotated[ + Union[EnvironmentResourceNone, EnvironmentResourceOpenAIHosted, EnvironmentResourceSelfHosted], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/environment_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/environment_param.py new file mode 100644 index 0000000..43101b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/environment_param.py @@ -0,0 +1,127 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .hosted_skill_param import HostedSkillParam +from .hosted_plugin_param import HostedPluginParam +from .setup_command_param import SetupCommandParam +from .hosted_environment_file_param import HostedEnvironmentFileParam + +__all__ = [ + "EnvironmentParam", + "EnvironmentParamNone", + "EnvironmentParamOpenAIHosted", + "EnvironmentParamOpenAIHostedNetwork", + "EnvironmentParamOpenAIHostedPackages", + "EnvironmentParamSelfHosted", +] + + +class EnvironmentParamNone(TypedDict, total=False): + """Runs the agent without an execution environment.""" + + type: Required[Literal["none"]] + """The type of the object. Always `none`.""" + + +class EnvironmentParamOpenAIHostedNetwork(TypedDict, total=False): + """Network access policy for the environment. + + Defaults to disabled for GA requests and enabled for beta requests. + """ + + access: Required[Literal["enabled", "disabled", "restricted"]] + """The environment's network access mode. + + - `enabled` - Allows unrestricted network access. + - `disabled` - Disables network access. + - `restricted` - Allows access only to configured domains. + """ + + allowed_domains: Optional[SequenceNotStr[str]] + """Domains the environment may access when network access is restricted.""" + + +class EnvironmentParamOpenAIHostedPackages(TypedDict, total=False): + """Packages to install in the environment. Defaults to empty package lists.""" + + npm: Optional[SequenceNotStr[str]] + """npm packages to install globally. Defaults to an empty list.""" + + python: Optional[SequenceNotStr[str]] + """Python packages to install. Defaults to an empty list.""" + + system: Optional[SequenceNotStr[str]] + """System packages to install. Defaults to an empty list.""" + + +class EnvironmentParamOpenAIHosted(TypedDict, total=False): + """ + An existing OpenAI-hosted environment or new inline/template-based hosted configuration. + """ + + type: Required[Literal["openai_hosted"]] + """The type of the object. Always `openai_hosted`.""" + + capability_directories: Optional[SequenceNotStr[str]] + """Directories that contain capabilities exposed to the agent. + + Defaults to an empty list. + """ + + env: Optional[Dict[str, str]] + """Environment variables made available to the agent.""" + + environment_template_id: str + """A reusable hosted template applied before inline session configuration. + + Omitted fields inherit the template; network overrides cannot broaden its + policy. + """ + + files: Optional[Iterable[HostedEnvironmentFileParam]] + """Files available before the agent starts. Defaults to an empty list.""" + + network: Optional[EnvironmentParamOpenAIHostedNetwork] + """Network access policy for the environment. + + Defaults to disabled for GA requests and enabled for beta requests. + """ + + packages: Optional[EnvironmentParamOpenAIHostedPackages] + """Packages to install in the environment. Defaults to empty package lists.""" + + plugins: Optional[Iterable[HostedPluginParam]] + """Plugins provided as inline ZIP archives. Defaults to an empty list.""" + + setup_commands: Optional[Iterable[SetupCommandParam]] + """Ordered, confidential setup commands. Command bodies are never returned.""" + + skills: Optional[Iterable[HostedSkillParam]] + """Skills referenced by ID or provided as inline ZIP archives. + + Defaults to an empty list. + """ + + +class EnvironmentParamSelfHosted(TypedDict, total=False): + """An application-hosted environment configured inline.""" + + type: Required[Literal["self_hosted"]] + """The type of the object. Always `self_hosted`.""" + + workspace_directory: Required[str] + """Absolute project directory inside the self-hosted environment.""" + + capability_directories: Optional[SequenceNotStr[str]] + """Directories that contain capabilities exposed to the agent. + + Defaults to an empty list. + """ + + +EnvironmentParam: TypeAlias = Union[EnvironmentParamNone, EnvironmentParamOpenAIHosted, EnvironmentParamSelfHosted] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/file_search_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/file_search_tool.py new file mode 100644 index 0000000..c431b75 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/file_search_tool.py @@ -0,0 +1,64 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FileSearchTool", "FileSearch", "FileSearchRankingOptions"] + + +class FileSearchRankingOptions(BaseModel): + """The ranking options for the file search. + + If not specified, the file search tool will use the `auto` ranker and a score_threshold of 0. + + See the [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) for more information. + """ + + score_threshold: float + """The score threshold for the file search. + + All values must be a floating point number between 0 and 1. + """ + + ranker: Optional[Literal["auto", "default_2024_08_21"]] = None + """The ranker to use for the file search. + + If not specified will use the `auto` ranker. + """ + + +class FileSearch(BaseModel): + """Overrides for the file search tool.""" + + max_num_results: Optional[int] = None + """The maximum number of results the file search tool should output. + + The default is 20 for `gpt-4*` models and 5 for `gpt-3.5-turbo`. This number + should be between 1 and 50 inclusive. + + Note that the file search tool may output fewer than `max_num_results` results. + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + """ + + ranking_options: Optional[FileSearchRankingOptions] = None + """The ranking options for the file search. + + If not specified, the file search tool will use the `auto` ranker and a + score_threshold of 0. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + """ + + +class FileSearchTool(BaseModel): + type: Literal["file_search"] + """The type of tool being defined: `file_search`""" + + file_search: Optional[FileSearch] = None + """Overrides for the file search tool.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/file_search_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/file_search_tool_param.py new file mode 100644 index 0000000..f74c004 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/file_search_tool_param.py @@ -0,0 +1,63 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["FileSearchToolParam", "FileSearch", "FileSearchRankingOptions"] + + +class FileSearchRankingOptions(TypedDict, total=False): + """The ranking options for the file search. + + If not specified, the file search tool will use the `auto` ranker and a score_threshold of 0. + + See the [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) for more information. + """ + + score_threshold: Required[float] + """The score threshold for the file search. + + All values must be a floating point number between 0 and 1. + """ + + ranker: Literal["auto", "default_2024_08_21"] + """The ranker to use for the file search. + + If not specified will use the `auto` ranker. + """ + + +class FileSearch(TypedDict, total=False): + """Overrides for the file search tool.""" + + max_num_results: int + """The maximum number of results the file search tool should output. + + The default is 20 for `gpt-4*` models and 5 for `gpt-3.5-turbo`. This number + should be between 1 and 50 inclusive. + + Note that the file search tool may output fewer than `max_num_results` results. + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + """ + + ranking_options: FileSearchRankingOptions + """The ranking options for the file search. + + If not specified, the file search tool will use the `auto` ranker and a + score_threshold of 0. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + """ + + +class FileSearchToolParam(TypedDict, total=False): + type: Required[Literal["file_search"]] + """The type of tool being defined: `file_search`""" + + file_search: FileSearch + """Overrides for the file search tool.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/function_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/function_tool.py new file mode 100644 index 0000000..172048c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/function_tool.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from ..shared.function_definition import FunctionDefinition + +__all__ = ["FunctionTool"] + + +class FunctionTool(BaseModel): + function: FunctionDefinition + + type: Literal["function"] + """The type of tool being defined: `function`""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/function_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/function_tool_param.py new file mode 100644 index 0000000..da09db4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/function_tool_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from ..shared_params.function_definition import FunctionDefinition + +__all__ = ["FunctionToolParam"] + + +class FunctionToolParam(TypedDict, total=False): + function: Required[FunctionDefinition] + + type: Required[Literal["function"]] + """The type of tool being defined: `function`""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/hosted_environment_file.py b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_environment_file.py new file mode 100644 index 0000000..e6c12ed --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_environment_file.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .hosted_environment_file_id import HostedEnvironmentFileID + +__all__ = ["HostedEnvironmentFile", "HostedEnvironmentFileResourceInline"] + + +class HostedEnvironmentFileResourceInline(BaseModel): + """A file supplied inline when the session was created.""" + + id: str + """The session-scoped ID of the file in the execution environment.""" + + path: str + """The file's absolute path inside the environment.""" + + size_bytes: int + """The decoded file size in bytes.""" + + type: Literal["inline"] + """The type of the object. Always `inline`.""" + + +HostedEnvironmentFile: TypeAlias = Annotated[ + Union[HostedEnvironmentFileID, HostedEnvironmentFileResourceInline], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/hosted_environment_file_id.py b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_environment_file_id.py new file mode 100644 index 0000000..c9cd7e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_environment_file_id.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["HostedEnvironmentFileID"] + + +class HostedEnvironmentFileID(BaseModel): + """A file copied from the OpenAI Files API.""" + + id: str + """The session-scoped ID of the file in the execution environment.""" + + file_id: str + """The ID of the uploaded file.""" + + path: str + """The file's absolute path inside the environment.""" + + size_bytes: int + """The decoded file size in bytes.""" + + type: Literal["file_id"] + """The type of the object. Always `file_id`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/hosted_environment_file_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_environment_file_param.py new file mode 100644 index 0000000..73b7213 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_environment_file_param.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["HostedEnvironmentFileParam", "HostedEnvironmentFileParamFileID", "HostedEnvironmentFileParamInline"] + + +class HostedEnvironmentFileParamFileID(TypedDict, total=False): + """A file previously uploaded through the OpenAI Files API.""" + + file_id: Required[str] + """The ID of the uploaded file.""" + + path: Required[str] + """The absolute destination path inside `/workspace`.""" + + type: Required[Literal["file_id"]] + """The type of the object. Always `file_id`.""" + + +class HostedEnvironmentFileParamInline(TypedDict, total=False): + """A file supplied directly as standard-base64 data.""" + + data: Required[str] + """The standard-base64-encoded file contents.""" + + path: Required[str] + """The absolute destination path inside `/workspace`.""" + + type: Required[Literal["inline"]] + """The type of the object. Always `inline`.""" + + +HostedEnvironmentFileParam: TypeAlias = Union[HostedEnvironmentFileParamFileID, HostedEnvironmentFileParamInline] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/hosted_plugin.py b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_plugin.py new file mode 100644 index 0000000..ef31cad --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_plugin.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["HostedPlugin"] + + +class HostedPlugin(BaseModel): + """A plugin installed from an inline ZIP archive.""" + + description: str + """The installed plugin description.""" + + name: str + """The installed plugin name.""" + + type: Literal["inline"] + """The type of the object. Always `inline`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/hosted_plugin_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_plugin_param.py new file mode 100644 index 0000000..5d8b5d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_plugin_param.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .inline_capability_source_param import InlineCapabilitySourceParam + +__all__ = ["HostedPluginParam"] + + +class HostedPluginParam(TypedDict, total=False): + """Supplies a plugin ZIP directly in the session request.""" + + description: Required[str] + """The plugin description declared in `.codex-plugin/plugin.json`.""" + + name: Required[str] + """The plugin name declared in `.codex-plugin/plugin.json`.""" + + source: Required[InlineCapabilitySourceParam] + """Provides ZIP bytes encoded with standard base64.""" + + type: Required[Literal["inline"]] + """The type of the object. Always `inline`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/hosted_skill.py b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_skill.py new file mode 100644 index 0000000..be82c4a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_skill.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .hosted_skill_reference import HostedSkillReference + +__all__ = ["HostedSkill", "HostedSkillResourceInline"] + + +class HostedSkillResourceInline(BaseModel): + """A skill installed from an inline ZIP archive.""" + + description: str + """The installed skill description.""" + + name: str + """The installed skill name.""" + + type: Literal["inline"] + """The type of the object. Always `inline`.""" + + +HostedSkill: TypeAlias = Annotated[ + Union[HostedSkillReference, HostedSkillResourceInline], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/hosted_skill_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_skill_param.py new file mode 100644 index 0000000..0effe8c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_skill_param.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .inline_capability_source_param import InlineCapabilitySourceParam + +__all__ = ["HostedSkillParam", "HostedSkillParamSkillReference", "HostedSkillParamInline"] + + +class HostedSkillParamSkillReference(TypedDict, total=False): + """References a skill uploaded through the Skills API.""" + + skill_id: Required[str] + """The ID of the skill created through `/v1/skills`.""" + + type: Required[Literal["skill_reference"]] + """The type of the object. Always `skill_reference`.""" + + version: Optional[str] + """ + The skill version, a positive integer or `latest`; omission selects the default. + """ + + +class HostedSkillParamInline(TypedDict, total=False): + """Supplies a skill ZIP directly in the session request.""" + + description: Required[str] + """The skill description declared in `SKILL.md`.""" + + name: Required[str] + """The skill name declared in `SKILL.md`.""" + + source: Required[InlineCapabilitySourceParam] + """Provides ZIP bytes encoded with standard base64.""" + + type: Required[Literal["inline"]] + """The type of the object. Always `inline`.""" + + +HostedSkillParam: TypeAlias = Union[HostedSkillParamSkillReference, HostedSkillParamInline] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/hosted_skill_reference.py b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_skill_reference.py new file mode 100644 index 0000000..bed8e03 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/hosted_skill_reference.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["HostedSkillReference"] + + +class HostedSkillReference(BaseModel): + """A skill installed from the Skills API.""" + + description: str + """The installed skill description.""" + + name: str + """The installed skill name.""" + + skill_id: str + """The referenced skill ID.""" + + type: Literal["skill_reference"] + """The type of the object. Always `skill_reference`.""" + + version: str + """The concrete skill version installed for this session.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/inline_capability_source_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/inline_capability_source_param.py new file mode 100644 index 0000000..5dff18a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/inline_capability_source_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InlineCapabilitySourceParam"] + + +class InlineCapabilitySourceParam(TypedDict, total=False): + """Provides ZIP bytes encoded with standard base64.""" + + data: Required[str] + """Standard-base64 encoded ZIP archive bytes.""" + + media_type: Required[Literal["application/zip"]] + """The archive media type, always `application/zip`. + + - `application/zip` - A ZIP archive. + """ + + type: Required[Literal["base64"]] + """The type of the object. Always `base64`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/input_content.py b/venv/lib/python3.12/site-packages/openai/types/beta/input_content.py new file mode 100644 index 0000000..61f1559 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/input_content.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["InputContent", "InputContentResourceInputText", "InputContentResourceInputImage"] + + +class InputContentResourceInputText(BaseModel): + """Text input recorded in a session item.""" + + text: str + """The text supplied to the agent.""" + + type: Literal["input_text"] + """The type of the object. Always `input_text`.""" + + +class InputContentResourceInputImage(BaseModel): + """Image input recorded in a session item.""" + + image_url: str + """ + The URL of the image supplied to the agent, which may be a base64-encoded data + URL. + """ + + type: Literal["input_image"] + """The type of the object. Always `input_image`.""" + + +InputContent: TypeAlias = Annotated[ + Union[InputContentResourceInputText, InputContentResourceInputImage], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/input_content_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/input_content_param.py new file mode 100644 index 0000000..c31232c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/input_content_param.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["InputContentParam", "InputContentParamInputText", "InputContentParamInputImage"] + + +class InputContentParamInputText(TypedDict, total=False): + """Text input to the model.""" + + text: Required[str] + """The text sent to the model.""" + + type: Required[Literal["input_text"]] + """The type of the object. Always `input_text`.""" + + +class InputContentParamInputImage(TypedDict, total=False): + """Image input to the model.""" + + image_url: Required[str] + """The URL of the image sent to the model.""" + + type: Required[Literal["input_image"]] + """The type of the object. Always `input_image`.""" + + +InputContentParam: TypeAlias = Union[InputContentParamInputText, InputContentParamInputImage] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/mcp_transport.py b/venv/lib/python3.12/site-packages/openai/types/beta/mcp_transport.py new file mode 100644 index 0000000..4e768fa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/mcp_transport.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["McpTransport", "McpTransportResourceHTTP", "McpTransportResourceStdio"] + + +class McpTransportResourceHTTP(BaseModel): + """Connects to an MCP server over HTTP.""" + + server_url: str + """The URL of the MCP server.""" + + type: Literal["http"] + """The type of the object. Always `http`.""" + + +class McpTransportResourceStdio(BaseModel): + """Starts an MCP server as a local process.""" + + args: List[str] + """Arguments passed to the MCP server command.""" + + command: str + """The command used to start the MCP server.""" + + cwd: str + """The working directory used to start the MCP server.""" + + env_vars: List[str] + """Environment variable names inherited from the execution environment.""" + + type: Literal["stdio"] + """The type of the object. Always `stdio`.""" + + +McpTransport: TypeAlias = Annotated[ + Union[McpTransportResourceHTTP, McpTransportResourceStdio], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/mcp_transport_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/mcp_transport_param.py new file mode 100644 index 0000000..d684bb8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/mcp_transport_param.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["McpTransportParam", "McpTransportConfigParamHTTP", "McpTransportConfigParamStdio"] + + +class McpTransportConfigParamHTTP(TypedDict, total=False): + """Connects to an MCP server over HTTP.""" + + server_url: Required[str] + """The URL of the MCP server.""" + + type: Required[Literal["http"]] + """The type of the object. Always `http`.""" + + authorization: Optional[str] + """The authorization value sent to the MCP server, if any.""" + + headers: Optional[Dict[str, str]] + """Additional HTTP headers sent to the MCP server.""" + + +class McpTransportConfigParamStdio(TypedDict, total=False): + """Starts an MCP server as a local process.""" + + command: Required[str] + """The command used to start the MCP server.""" + + cwd: Required[str] + """The working directory used to start the MCP server.""" + + type: Required[Literal["stdio"]] + """The type of the object. Always `stdio`.""" + + args: Optional[SequenceNotStr[str]] + """Arguments passed to the MCP server command.""" + + env: Optional[Dict[str, str]] + """Environment variables set for the MCP server process.""" + + env_vars: Optional[SequenceNotStr[str]] + """Environment variable names to inherit from the selected execution environment.""" + + +McpTransportParam: TypeAlias = Union[McpTransportConfigParamHTTP, McpTransportConfigParamStdio] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/multi_agent_config.py b/venv/lib/python3.12/site-packages/openai/types/beta/multi_agent_config.py new file mode 100644 index 0000000..dc4439c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/multi_agent_config.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["MultiAgentConfig"] + + +class MultiAgentConfig(BaseModel): + """The resolved configuration for creating and coordinating subagents.""" + + enabled: bool + """Whether subagent tools are enabled. Defaults to false.""" + + max_concurrent_subagents: Optional[int] = None + """Maximum number of subagents that may run concurrently, or null when disabled. + + Defaults to 6 when enabled. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/multi_agent_config_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/multi_agent_config_param.py new file mode 100644 index 0000000..b40a601 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/multi_agent_config_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["MultiAgentConfigParam"] + + +class MultiAgentConfigParam(TypedDict, total=False): + """Explicit configuration for creating and coordinating subagents.""" + + enabled: Required[bool] + """Whether subagent tools are enabled.""" + + max_concurrent_subagents: int + """Maximum number of subagents that may run concurrently. Defaults to 6.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/output_text.py b/venv/lib/python3.12/site-packages/openai/types/beta/output_text.py new file mode 100644 index 0000000..4cfa7c2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/output_text.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["OutputText"] + + +class OutputText(BaseModel): + """A text content part produced by the agent.""" + + text: str + """The text produced by the agent.""" + + type: Literal["output_text"] + """The content type. Always `output_text`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/persisted_agent_tool.py b/venv/lib/python3.12/site-packages/openai/types/beta/persisted_agent_tool.py new file mode 100644 index 0000000..69340fb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/persisted_agent_tool.py @@ -0,0 +1,129 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .persisted_mcp_transport import PersistedMcpTransport + +__all__ = [ + "PersistedAgentTool", + "PersistedAgentToolResourceFunction", + "PersistedAgentToolResourceToolSearch", + "PersistedAgentToolResourceProgrammaticToolCalling", + "PersistedAgentToolResourceMcp", + "PersistedAgentToolResourceWebSearch", + "PersistedAgentToolResourceWebSearchLocation", +] + + +class PersistedAgentToolResourceFunction(BaseModel): + """A function defined by the application.""" + + defer_loading: bool + """Whether the function is deferred and discovered through tool search.""" + + description: str + """A description of what the function does.""" + + name: str + """The name of the function.""" + + parameters: Dict[str, object] + """A JSON Schema object describing the function's arguments.""" + + type: Literal["function"] + """The type of the object. Always `function`.""" + + +class PersistedAgentToolResourceToolSearch(BaseModel): + """Discovers deferred function tools and loads them into the model context.""" + + type: Literal["tool_search"] + """The type of the object. Always `tool_search`.""" + + +class PersistedAgentToolResourceProgrammaticToolCalling(BaseModel): + """Enables calling tools from model-generated code.""" + + enabled: bool + """Whether tools can be called from model-generated code.""" + + type: Literal["programmatic_tool_calling"] + """The type of the object. Always `programmatic_tool_calling`.""" + + +class PersistedAgentToolResourceMcp(BaseModel): + """Tools provided by a remote MCP server without stored credentials.""" + + allowed_tools: Optional[List[str]] = None + """The MCP tools the agent may call, or null when all server tools are allowed.""" + + connection_origin: Literal["service", "environment"] + """Where outbound MCP HTTP connections originate.""" + + credential_id: Optional[str] = None + """The vault credential selected for this MCP server, if any.""" + + request_metadata: Dict[str, object] + """Metadata included with requests to this MCP server.""" + + required: bool + """Whether this MCP server must initialize before the first turn.""" + + server_label: str + """A label used to identify the MCP server in tool calls.""" + + transport: PersistedMcpTransport + """The credential-free transport used to connect to the MCP server.""" + + type: Literal["mcp"] + """The type of the object. Always `mcp`.""" + + +class PersistedAgentToolResourceWebSearchLocation(BaseModel): + """Approximate location used to localize search results, if provided.""" + + city: Optional[str] = None + """The city name.""" + + country: Optional[str] = None + """The two-letter ISO country code, such as `US`.""" + + region: Optional[str] = None + """The region or state name.""" + + timezone: Optional[str] = None + """The IANA timezone, such as `America/Los_Angeles`.""" + + +class PersistedAgentToolResourceWebSearch(BaseModel): + """Web search.""" + + allowed_domains: Optional[List[str]] = None + """Allowed search domains, or `null` when the search is unrestricted.""" + + context_size: Literal["low", "medium", "high"] + """The amount of search context made available to the model. Defaults to `medium`.""" + + location: Optional[PersistedAgentToolResourceWebSearchLocation] = None + """Approximate location used to localize search results, if provided.""" + + mode: Literal["disabled", "cached", "live"] + """The source used for web search results.""" + + type: Literal["web_search"] + """The type of the object. Always `web_search`.""" + + +PersistedAgentTool: TypeAlias = Annotated[ + Union[ + PersistedAgentToolResourceFunction, + PersistedAgentToolResourceToolSearch, + PersistedAgentToolResourceProgrammaticToolCalling, + PersistedAgentToolResourceMcp, + PersistedAgentToolResourceWebSearch, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/persisted_agent_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/persisted_agent_tool_param.py new file mode 100644 index 0000000..a30719d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/persisted_agent_tool_param.py @@ -0,0 +1,145 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .persisted_mcp_transport_param import PersistedMcpTransportParam + +__all__ = [ + "PersistedAgentToolParam", + "PersistedAgentToolConfigParamFunction", + "PersistedAgentToolConfigParamToolSearch", + "PersistedAgentToolConfigParamProgrammaticToolCalling", + "PersistedAgentToolConfigParamMcp", + "PersistedAgentToolConfigParamWebSearch", + "PersistedAgentToolConfigParamWebSearchLocation", +] + + +class PersistedAgentToolConfigParamFunction(TypedDict, total=False): + """A function defined by the application.""" + + description: Required[str] + """A description of what the function does.""" + + name: Required[str] + """The name of the function.""" + + parameters: Required[Dict[str, object]] + """A JSON Schema object describing the function's arguments.""" + + type: Required[Literal["function"]] + """The type of the object. Always `function`.""" + + defer_loading: bool + """Whether this function is deferred and discovered through tool search. + + Defaults to `false`. + """ + + +class PersistedAgentToolConfigParamToolSearch(TypedDict, total=False): + """Discovers deferred function tools and loads them into the model context.""" + + type: Required[Literal["tool_search"]] + """The type of the object. Always `tool_search`.""" + + +class PersistedAgentToolConfigParamProgrammaticToolCalling(TypedDict, total=False): + """Enables calling tools from model-generated code.""" + + type: Required[Literal["programmatic_tool_calling"]] + """The type of the object. Always `programmatic_tool_calling`.""" + + enabled: bool + """Whether tools can be called from model-generated code. Defaults to `true`.""" + + +class PersistedAgentToolConfigParamMcp(TypedDict, total=False): + """Tools provided by a remote MCP server without stored credentials.""" + + server_label: Required[str] + """A label used to identify the MCP server in tool calls.""" + + transport: Required[PersistedMcpTransportParam] + """The credential-free transport used to connect to the MCP server.""" + + type: Required[Literal["mcp"]] + """The type of the object. Always `mcp`.""" + + allowed_tools: Optional[SequenceNotStr[str]] + """The MCP tools the agent may call. All server tools are allowed when omitted.""" + + connection_origin: Optional[Literal["service", "environment"]] + """Selects where outbound MCP HTTP connections originate. + + - `service` - Uses the Managed Agents service network. + - `environment` - Uses the session's execution environment. + """ + + credential_id: Optional[str] + """The vault credential selected for this MCP server. + + Optional when exactly one attached credential matches the server URL. + """ + + request_metadata: Optional[Dict[str, object]] + """Metadata included with requests to this MCP server.""" + + required: bool + """Whether this MCP server must initialize before the first turn. + + Defaults to `false`. + """ + + +class PersistedAgentToolConfigParamWebSearchLocation(TypedDict, total=False): + """Approximate location used to localize search results.""" + + city: Optional[str] + """The city name.""" + + country: Optional[str] + """The two-letter ISO country code, such as `US`.""" + + region: Optional[str] + """The region or state name.""" + + timezone: Optional[str] + """The IANA timezone, such as `America/Los_Angeles`.""" + + +class PersistedAgentToolConfigParamWebSearch(TypedDict, total=False): + """Web search.""" + + type: Required[Literal["web_search"]] + """The type of the object. Always `web_search`.""" + + allowed_domains: Optional[SequenceNotStr[str]] + """Domains the search may include.""" + + context_size: Optional[Literal["low", "medium", "high"]] + """The amount of search context made available to the model. Defaults to `medium`.""" + + location: Optional[PersistedAgentToolConfigParamWebSearchLocation] + """Approximate location used to localize search results.""" + + mode: Optional[Literal["disabled", "cached", "live"]] + """The source used for web search results. Defaults to `live`. + + - `disabled` - Disables web search. + - `cached` - Uses cached search results. + - `live` - Searches the live web. + """ + + +PersistedAgentToolParam: TypeAlias = Union[ + PersistedAgentToolConfigParamFunction, + PersistedAgentToolConfigParamToolSearch, + PersistedAgentToolConfigParamProgrammaticToolCalling, + PersistedAgentToolConfigParamMcp, + PersistedAgentToolConfigParamWebSearch, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/persisted_mcp_transport.py b/venv/lib/python3.12/site-packages/openai/types/beta/persisted_mcp_transport.py new file mode 100644 index 0000000..cf5462e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/persisted_mcp_transport.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["PersistedMcpTransport", "PersistedMcpTransportResourceHTTP", "PersistedMcpTransportResourceStdio"] + + +class PersistedMcpTransportResourceHTTP(BaseModel): + """Connects to an MCP server over HTTP.""" + + headers: Dict[str, str] + """Non-secret HTTP headers sent to the MCP server.""" + + server_url: str + """The URL of the MCP server.""" + + type: Literal["http"] + """The type of the object. Always `http`.""" + + +class PersistedMcpTransportResourceStdio(BaseModel): + """Starts an MCP server as a local process.""" + + args: List[str] + """Arguments passed to the MCP server command.""" + + command: str + """The command used to start the MCP server.""" + + cwd: str + """The working directory used to start the MCP server.""" + + env_vars: List[str] + """Environment variable names inherited from the execution environment.""" + + type: Literal["stdio"] + """The type of the object. Always `stdio`.""" + + +PersistedMcpTransport: TypeAlias = Annotated[ + Union[PersistedMcpTransportResourceHTTP, PersistedMcpTransportResourceStdio], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/persisted_mcp_transport_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/persisted_mcp_transport_param.py new file mode 100644 index 0000000..76ec6ef --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/persisted_mcp_transport_param.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "PersistedMcpTransportParam", + "PersistedMcpTransportConfigParamHTTP", + "PersistedMcpTransportConfigParamStdio", +] + + +class PersistedMcpTransportConfigParamHTTP(TypedDict, total=False): + """Connects to an MCP server over HTTP.""" + + server_url: Required[str] + """The URL of the MCP server.""" + + type: Required[Literal["http"]] + """The type of the object. Always `http`.""" + + headers: Optional[Dict[str, str]] + """Non-secret HTTP headers sent to the MCP server.""" + + +class PersistedMcpTransportConfigParamStdio(TypedDict, total=False): + """Starts an MCP server as a local process.""" + + command: Required[str] + """The command used to start the MCP server.""" + + cwd: Required[str] + """The working directory used to start the MCP server.""" + + type: Required[Literal["stdio"]] + """The type of the object. Always `stdio`.""" + + args: Optional[SequenceNotStr[str]] + """Arguments passed to the MCP server command.""" + + env_vars: Optional[SequenceNotStr[str]] + """Environment variable names to inherit from the selected execution environment.""" + + +PersistedMcpTransportParam: TypeAlias = Union[ + PersistedMcpTransportConfigParamHTTP, PersistedMcpTransportConfigParamStdio +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__init__.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__init__.py new file mode 100644 index 0000000..7913aad --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__init__.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +from .session import Session as Session +from .error_event import ErrorEvent as ErrorEvent +from .conversation_item import ConversationItem as ConversationItem +from .realtime_response import RealtimeResponse as RealtimeResponse +from .response_done_event import ResponseDoneEvent as ResponseDoneEvent +from .session_update_event import SessionUpdateEvent as SessionUpdateEvent +from .realtime_client_event import RealtimeClientEvent as RealtimeClientEvent +from .realtime_server_event import RealtimeServerEvent as RealtimeServerEvent +from .response_cancel_event import ResponseCancelEvent as ResponseCancelEvent +from .response_create_event import ResponseCreateEvent as ResponseCreateEvent +from .session_create_params import SessionCreateParams as SessionCreateParams +from .session_created_event import SessionCreatedEvent as SessionCreatedEvent +from .session_updated_event import SessionUpdatedEvent as SessionUpdatedEvent +from .transcription_session import TranscriptionSession as TranscriptionSession +from .response_created_event import ResponseCreatedEvent as ResponseCreatedEvent +from .conversation_item_param import ConversationItemParam as ConversationItemParam +from .realtime_connect_params import RealtimeConnectParams as RealtimeConnectParams +from .realtime_response_usage import RealtimeResponseUsage as RealtimeResponseUsage +from .session_create_response import SessionCreateResponse as SessionCreateResponse +from .realtime_response_status import RealtimeResponseStatus as RealtimeResponseStatus +from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent +from .conversation_item_content import ConversationItemContent as ConversationItemContent +from .rate_limits_updated_event import RateLimitsUpdatedEvent as RateLimitsUpdatedEvent +from .response_audio_done_event import ResponseAudioDoneEvent as ResponseAudioDoneEvent +from .response_text_delta_event import ResponseTextDeltaEvent as ResponseTextDeltaEvent +from .conversation_created_event import ConversationCreatedEvent as ConversationCreatedEvent +from .response_audio_delta_event import ResponseAudioDeltaEvent as ResponseAudioDeltaEvent +from .session_update_event_param import SessionUpdateEventParam as SessionUpdateEventParam +from .realtime_client_event_param import RealtimeClientEventParam as RealtimeClientEventParam +from .response_cancel_event_param import ResponseCancelEventParam as ResponseCancelEventParam +from .response_create_event_param import ResponseCreateEventParam as ResponseCreateEventParam +from .transcription_session_update import TranscriptionSessionUpdate as TranscriptionSessionUpdate +from .conversation_item_create_event import ConversationItemCreateEvent as ConversationItemCreateEvent +from .conversation_item_delete_event import ConversationItemDeleteEvent as ConversationItemDeleteEvent +from .input_audio_buffer_clear_event import InputAudioBufferClearEvent as InputAudioBufferClearEvent +from .conversation_item_content_param import ConversationItemContentParam as ConversationItemContentParam +from .conversation_item_created_event import ConversationItemCreatedEvent as ConversationItemCreatedEvent +from .conversation_item_deleted_event import ConversationItemDeletedEvent as ConversationItemDeletedEvent +from .input_audio_buffer_append_event import InputAudioBufferAppendEvent as InputAudioBufferAppendEvent +from .input_audio_buffer_commit_event import InputAudioBufferCommitEvent as InputAudioBufferCommitEvent +from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent +from .conversation_item_retrieve_event import ConversationItemRetrieveEvent as ConversationItemRetrieveEvent +from .conversation_item_truncate_event import ConversationItemTruncateEvent as ConversationItemTruncateEvent +from .conversation_item_with_reference import ConversationItemWithReference as ConversationItemWithReference +from .input_audio_buffer_cleared_event import InputAudioBufferClearedEvent as InputAudioBufferClearedEvent +from .response_content_part_done_event import ResponseContentPartDoneEvent as ResponseContentPartDoneEvent +from .response_output_item_added_event import ResponseOutputItemAddedEvent as ResponseOutputItemAddedEvent +from .conversation_item_truncated_event import ConversationItemTruncatedEvent as ConversationItemTruncatedEvent +from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent +from .input_audio_buffer_committed_event import InputAudioBufferCommittedEvent as InputAudioBufferCommittedEvent +from .transcription_session_update_param import TranscriptionSessionUpdateParam as TranscriptionSessionUpdateParam +from .transcription_session_create_params import TranscriptionSessionCreateParams as TranscriptionSessionCreateParams +from .transcription_session_updated_event import TranscriptionSessionUpdatedEvent as TranscriptionSessionUpdatedEvent +from .conversation_item_create_event_param import ConversationItemCreateEventParam as ConversationItemCreateEventParam +from .conversation_item_delete_event_param import ConversationItemDeleteEventParam as ConversationItemDeleteEventParam +from .input_audio_buffer_clear_event_param import InputAudioBufferClearEventParam as InputAudioBufferClearEventParam +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent as ResponseAudioTranscriptDoneEvent +from .input_audio_buffer_append_event_param import InputAudioBufferAppendEventParam as InputAudioBufferAppendEventParam +from .input_audio_buffer_commit_event_param import InputAudioBufferCommitEventParam as InputAudioBufferCommitEventParam +from .response_audio_transcript_delta_event import ( + ResponseAudioTranscriptDeltaEvent as ResponseAudioTranscriptDeltaEvent, +) +from .conversation_item_retrieve_event_param import ( + ConversationItemRetrieveEventParam as ConversationItemRetrieveEventParam, +) +from .conversation_item_truncate_event_param import ( + ConversationItemTruncateEventParam as ConversationItemTruncateEventParam, +) +from .conversation_item_with_reference_param import ( + ConversationItemWithReferenceParam as ConversationItemWithReferenceParam, +) +from .input_audio_buffer_speech_started_event import ( + InputAudioBufferSpeechStartedEvent as InputAudioBufferSpeechStartedEvent, +) +from .input_audio_buffer_speech_stopped_event import ( + InputAudioBufferSpeechStoppedEvent as InputAudioBufferSpeechStoppedEvent, +) +from .response_function_call_arguments_done_event import ( + ResponseFunctionCallArgumentsDoneEvent as ResponseFunctionCallArgumentsDoneEvent, +) +from .response_function_call_arguments_delta_event import ( + ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, +) +from .conversation_item_input_audio_transcription_delta_event import ( + ConversationItemInputAudioTranscriptionDeltaEvent as ConversationItemInputAudioTranscriptionDeltaEvent, +) +from .conversation_item_input_audio_transcription_failed_event import ( + ConversationItemInputAudioTranscriptionFailedEvent as ConversationItemInputAudioTranscriptionFailedEvent, +) +from .conversation_item_input_audio_transcription_completed_event import ( + ConversationItemInputAudioTranscriptionCompletedEvent as ConversationItemInputAudioTranscriptionCompletedEvent, +) diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..20f3d60 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_created_event.cpython-312.pyc new file mode 100644 index 0000000..3027f2e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item.cpython-312.pyc new file mode 100644 index 0000000..dfcc227 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_content.cpython-312.pyc new file mode 100644 index 0000000..9f61093 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_content_param.cpython-312.pyc new file mode 100644 index 0000000..c4dd1d3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_create_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_create_event.cpython-312.pyc new file mode 100644 index 0000000..17e3103 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_create_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_create_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_create_event_param.cpython-312.pyc new file mode 100644 index 0000000..3d23e10 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_create_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_created_event.cpython-312.pyc new file mode 100644 index 0000000..491ed0d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_delete_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_delete_event.cpython-312.pyc new file mode 100644 index 0000000..70e29fd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_delete_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_delete_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_delete_event_param.cpython-312.pyc new file mode 100644 index 0000000..25d9d73 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_delete_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_deleted_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_deleted_event.cpython-312.pyc new file mode 100644 index 0000000..11df171 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_deleted_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_input_audio_transcription_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_input_audio_transcription_completed_event.cpython-312.pyc new file mode 100644 index 0000000..10140ea Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_input_audio_transcription_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_input_audio_transcription_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_input_audio_transcription_delta_event.cpython-312.pyc new file mode 100644 index 0000000..10a5a31 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_input_audio_transcription_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_input_audio_transcription_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_input_audio_transcription_failed_event.cpython-312.pyc new file mode 100644 index 0000000..2efa63f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_input_audio_transcription_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_param.cpython-312.pyc new file mode 100644 index 0000000..6197a7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_retrieve_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_retrieve_event.cpython-312.pyc new file mode 100644 index 0000000..be2fe83 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_retrieve_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_retrieve_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_retrieve_event_param.cpython-312.pyc new file mode 100644 index 0000000..f2fcf50 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_retrieve_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_truncate_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_truncate_event.cpython-312.pyc new file mode 100644 index 0000000..8376918 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_truncate_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_truncate_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_truncate_event_param.cpython-312.pyc new file mode 100644 index 0000000..fe23234 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_truncate_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_truncated_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_truncated_event.cpython-312.pyc new file mode 100644 index 0000000..9e918ca Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_truncated_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_with_reference.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_with_reference.cpython-312.pyc new file mode 100644 index 0000000..4da1050 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_with_reference.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_with_reference_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_with_reference_param.cpython-312.pyc new file mode 100644 index 0000000..7640bab Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/conversation_item_with_reference_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/error_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/error_event.cpython-312.pyc new file mode 100644 index 0000000..7507ecd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/error_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_append_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_append_event.cpython-312.pyc new file mode 100644 index 0000000..2194182 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_append_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_append_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_append_event_param.cpython-312.pyc new file mode 100644 index 0000000..36a983e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_append_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_clear_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_clear_event.cpython-312.pyc new file mode 100644 index 0000000..47385d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_clear_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_clear_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_clear_event_param.cpython-312.pyc new file mode 100644 index 0000000..c55723a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_clear_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_cleared_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_cleared_event.cpython-312.pyc new file mode 100644 index 0000000..9dac76d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_cleared_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_commit_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_commit_event.cpython-312.pyc new file mode 100644 index 0000000..0247b45 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_commit_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_commit_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_commit_event_param.cpython-312.pyc new file mode 100644 index 0000000..06c3540 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_commit_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_committed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_committed_event.cpython-312.pyc new file mode 100644 index 0000000..87da14d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_committed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_speech_started_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_speech_started_event.cpython-312.pyc new file mode 100644 index 0000000..ce00799 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_speech_started_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_speech_stopped_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_speech_stopped_event.cpython-312.pyc new file mode 100644 index 0000000..d06e379 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/input_audio_buffer_speech_stopped_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/rate_limits_updated_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/rate_limits_updated_event.cpython-312.pyc new file mode 100644 index 0000000..a0fe57b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/rate_limits_updated_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_client_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_client_event.cpython-312.pyc new file mode 100644 index 0000000..91c4130 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_client_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_client_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_client_event_param.cpython-312.pyc new file mode 100644 index 0000000..bddf77a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_client_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_connect_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_connect_params.cpython-312.pyc new file mode 100644 index 0000000..a3cd634 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_connect_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_response.cpython-312.pyc new file mode 100644 index 0000000..6363fe9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_response_status.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_response_status.cpython-312.pyc new file mode 100644 index 0000000..f52ee86 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_response_status.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_response_usage.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_response_usage.cpython-312.pyc new file mode 100644 index 0000000..22adf17 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_response_usage.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_server_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_server_event.cpython-312.pyc new file mode 100644 index 0000000..ba5ae2f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/realtime_server_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_audio_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_audio_delta_event.cpython-312.pyc new file mode 100644 index 0000000..2a9d6e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_audio_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_audio_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_audio_done_event.cpython-312.pyc new file mode 100644 index 0000000..3c990a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_audio_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_audio_transcript_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_audio_transcript_delta_event.cpython-312.pyc new file mode 100644 index 0000000..cd83f21 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_audio_transcript_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_audio_transcript_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_audio_transcript_done_event.cpython-312.pyc new file mode 100644 index 0000000..fe25cfa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_audio_transcript_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_cancel_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_cancel_event.cpython-312.pyc new file mode 100644 index 0000000..944b2af Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_cancel_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_cancel_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_cancel_event_param.cpython-312.pyc new file mode 100644 index 0000000..a871359 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_cancel_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_content_part_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_content_part_added_event.cpython-312.pyc new file mode 100644 index 0000000..ea84570 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_content_part_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_content_part_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_content_part_done_event.cpython-312.pyc new file mode 100644 index 0000000..01e7c2d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_content_part_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_create_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_create_event.cpython-312.pyc new file mode 100644 index 0000000..a4509df Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_create_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_create_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_create_event_param.cpython-312.pyc new file mode 100644 index 0000000..73808bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_create_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_created_event.cpython-312.pyc new file mode 100644 index 0000000..2813566 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_done_event.cpython-312.pyc new file mode 100644 index 0000000..97170b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_function_call_arguments_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_function_call_arguments_delta_event.cpython-312.pyc new file mode 100644 index 0000000..0bc13e7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_function_call_arguments_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_function_call_arguments_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_function_call_arguments_done_event.cpython-312.pyc new file mode 100644 index 0000000..3213b4b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_function_call_arguments_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_output_item_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_output_item_added_event.cpython-312.pyc new file mode 100644 index 0000000..03cf305 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_output_item_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_output_item_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_output_item_done_event.cpython-312.pyc new file mode 100644 index 0000000..57abc56 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_output_item_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_text_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_text_delta_event.cpython-312.pyc new file mode 100644 index 0000000..405d9b3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_text_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_text_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_text_done_event.cpython-312.pyc new file mode 100644 index 0000000..2b43ded Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/response_text_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session.cpython-312.pyc new file mode 100644 index 0000000..c986394 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_create_params.cpython-312.pyc new file mode 100644 index 0000000..a3f355b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_create_response.cpython-312.pyc new file mode 100644 index 0000000..9dfeda5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_created_event.cpython-312.pyc new file mode 100644 index 0000000..0875729 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_update_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_update_event.cpython-312.pyc new file mode 100644 index 0000000..c601ed3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_update_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_update_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_update_event_param.cpython-312.pyc new file mode 100644 index 0000000..a9430f5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_update_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_updated_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_updated_event.cpython-312.pyc new file mode 100644 index 0000000..c700248 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/session_updated_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session.cpython-312.pyc new file mode 100644 index 0000000..dd3e3b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session_create_params.cpython-312.pyc new file mode 100644 index 0000000..79ebed4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session_update.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session_update.cpython-312.pyc new file mode 100644 index 0000000..ad6a9c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session_update.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session_update_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session_update_param.cpython-312.pyc new file mode 100644 index 0000000..e9719b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session_update_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session_updated_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session_updated_event.cpython-312.pyc new file mode 100644 index 0000000..e27d378 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/__pycache__/transcription_session_updated_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_created_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_created_event.py new file mode 100644 index 0000000..e206126 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_created_event.py @@ -0,0 +1,25 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationCreatedEvent", "Conversation"] + + +class Conversation(BaseModel): + id: Optional[str] = None + """The unique ID of the conversation.""" + + object: Optional[Literal["realtime.conversation"]] = None + """The object type, must be `realtime.conversation`.""" + + +class ConversationCreatedEvent(BaseModel): + conversation: Conversation + """The conversation resource.""" + + event_id: str + """The unique ID of the server event.""" + + type: Literal["conversation.created"] + """The event type, must be `conversation.created`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item.py new file mode 100644 index 0000000..4cc60db --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item.py @@ -0,0 +1,59 @@ +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .conversation_item_content import ConversationItemContent + +__all__ = ["ConversationItem"] + + +class ConversationItem(BaseModel): + id: Optional[str] = None + """ + The unique ID of the item, this can be generated by the client to help manage + server-side context, but is not required because the server will generate one if + not provided. + """ + + arguments: Optional[str] = None + """The arguments of the function call (for `function_call` items).""" + + call_id: Optional[str] = None + """ + The ID of the function call (for `function_call` and `function_call_output` + items). If passed on a `function_call_output` item, the server will check that a + `function_call` item with the same ID exists in the conversation history. + """ + + content: Optional[List[ConversationItemContent]] = None + """The content of the message, applicable for `message` items. + + - Message items of role `system` support only `input_text` content + - Message items of role `user` support `input_text` and `input_audio` content + - Message items of role `assistant` support `text` content. + """ + + name: Optional[str] = None + """The name of the function being called (for `function_call` items).""" + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`.""" + + output: Optional[str] = None + """The output of the function call (for `function_call_output` items).""" + + role: Optional[Literal["user", "assistant", "system"]] = None + """ + The role of the message sender (`user`, `assistant`, `system`), only applicable + for `message` items. + """ + + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item (`completed`, `incomplete`, `in_progress`). + + These have no effect on the conversation, but are accepted for consistency with + the `conversation.item.created` event. + """ + + type: Optional[Literal["message", "function_call", "function_call_output"]] = None + """The type of the item (`message`, `function_call`, `function_call_output`).""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_content.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_content.py new file mode 100644 index 0000000..de2391a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_content.py @@ -0,0 +1,30 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemContent"] + + +class ConversationItemContent(BaseModel): + id: Optional[str] = None + """ + ID of a previous conversation item to reference (for `item_reference` content + types in `response.create` events). These can reference both client and server + created items. + """ + + audio: Optional[str] = None + """Base64-encoded audio bytes, used for `input_audio` content type.""" + + text: Optional[str] = None + """The text content, used for `input_text` and `text` content types.""" + + transcript: Optional[str] = None + """The transcript of the audio, used for `input_audio` and `audio` content types.""" + + type: Optional[Literal["input_text", "input_audio", "item_reference", "text", "audio"]] = None + """ + The content type (`input_text`, `input_audio`, `item_reference`, `text`, + `audio`). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_content_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_content_param.py new file mode 100644 index 0000000..509697d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_content_param.py @@ -0,0 +1,29 @@ +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ConversationItemContentParam"] + + +class ConversationItemContentParam(TypedDict, total=False): + id: str + """ + ID of a previous conversation item to reference (for `item_reference` content + types in `response.create` events). These can reference both client and server + created items. + """ + + audio: str + """Base64-encoded audio bytes, used for `input_audio` content type.""" + + text: str + """The text content, used for `input_text` and `text` content types.""" + + transcript: str + """The transcript of the audio, used for `input_audio` and `audio` content types.""" + + type: Literal["input_text", "input_audio", "item_reference", "text", "audio"] + """ + The content type (`input_text`, `input_audio`, `item_reference`, `text`, + `audio`). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_create_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_create_event.py new file mode 100644 index 0000000..6bdb7d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_create_event.py @@ -0,0 +1,27 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemCreateEvent"] + + +class ConversationItemCreateEvent(BaseModel): + item: ConversationItem + """The item to add to the conversation.""" + + type: Literal["conversation.item.create"] + """The event type, must be `conversation.item.create`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" + + previous_item_id: Optional[str] = None + """The ID of the preceding item after which the new item will be inserted. + + If not set, the new item will be appended to the end of the conversation. If set + to `root`, the new item will be added to the beginning of the conversation. If + set to an existing ID, it allows an item to be inserted mid-conversation. If the + ID cannot be found, an error will be returned and the item will not be added. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_create_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_create_event_param.py new file mode 100644 index 0000000..83c3a29 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_create_event_param.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .conversation_item_param import ConversationItemParam + +__all__ = ["ConversationItemCreateEventParam"] + + +class ConversationItemCreateEventParam(TypedDict, total=False): + item: Required[ConversationItemParam] + """The item to add to the conversation.""" + + type: Required[Literal["conversation.item.create"]] + """The event type, must be `conversation.item.create`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" + + previous_item_id: str + """The ID of the preceding item after which the new item will be inserted. + + If not set, the new item will be appended to the end of the conversation. If set + to `root`, the new item will be added to the beginning of the conversation. If + set to an existing ID, it allows an item to be inserted mid-conversation. If the + ID cannot be found, an error will be returned and the item will not be added. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_created_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_created_event.py new file mode 100644 index 0000000..15426e7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_created_event.py @@ -0,0 +1,25 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemCreatedEvent"] + + +class ConversationItemCreatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """The item to add to the conversation.""" + + type: Literal["conversation.item.created"] + """The event type, must be `conversation.item.created`.""" + + previous_item_id: Optional[str] = None + """ + The ID of the preceding item in the Conversation context, allows the client to + understand the order of the conversation. Can be `null` if the item has no + predecessor. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_delete_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_delete_event.py new file mode 100644 index 0000000..815512f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_delete_event.py @@ -0,0 +1,17 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemDeleteEvent"] + + +class ConversationItemDeleteEvent(BaseModel): + item_id: str + """The ID of the item to delete.""" + + type: Literal["conversation.item.delete"] + """The event type, must be `conversation.item.delete`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_delete_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_delete_event_param.py new file mode 100644 index 0000000..6e3cea0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_delete_event_param.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConversationItemDeleteEventParam"] + + +class ConversationItemDeleteEventParam(TypedDict, total=False): + item_id: Required[str] + """The ID of the item to delete.""" + + type: Required[Literal["conversation.item.delete"]] + """The event type, must be `conversation.item.delete`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_deleted_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_deleted_event.py new file mode 100644 index 0000000..f0da6aa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_deleted_event.py @@ -0,0 +1,16 @@ +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemDeletedEvent"] + + +class ConversationItemDeletedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item that was deleted.""" + + type: Literal["conversation.item.deleted"] + """The event type, must be `conversation.item.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py new file mode 100644 index 0000000..0d0a9cf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py @@ -0,0 +1,85 @@ +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ...._models import BaseModel + +__all__ = [ + "ConversationItemInputAudioTranscriptionCompletedEvent", + "Usage", + "UsageTranscriptTextUsageTokens", + "UsageTranscriptTextUsageTokensInputTokenDetails", + "UsageTranscriptTextUsageDuration", + "Logprob", +] + + +class UsageTranscriptTextUsageTokensInputTokenDetails(BaseModel): + audio_tokens: Optional[int] = None + """Number of audio tokens billed for this request.""" + + text_tokens: Optional[int] = None + """Number of text tokens billed for this request.""" + + +class UsageTranscriptTextUsageTokens(BaseModel): + input_tokens: int + """Number of input tokens billed for this request.""" + + output_tokens: int + """Number of output tokens generated.""" + + total_tokens: int + """Total number of tokens used (input + output).""" + + type: Literal["tokens"] + """The type of the usage object. Always `tokens` for this variant.""" + + input_token_details: Optional[UsageTranscriptTextUsageTokensInputTokenDetails] = None + """Details about the input tokens billed for this request.""" + + +class UsageTranscriptTextUsageDuration(BaseModel): + seconds: float + """Duration of the input audio in seconds.""" + + type: Literal["duration"] + """The type of the usage object. Always `duration` for this variant.""" + + +Usage: TypeAlias = Union[UsageTranscriptTextUsageTokens, UsageTranscriptTextUsageDuration] + + +class Logprob(BaseModel): + token: str + """The token that was used to generate the log probability.""" + + bytes: List[int] + """The bytes that were used to generate the log probability.""" + + logprob: float + """The log probability of the token.""" + + +class ConversationItemInputAudioTranscriptionCompletedEvent(BaseModel): + content_index: int + """The index of the content part containing the audio.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item containing the audio.""" + + transcript: str + """The transcribed text.""" + + type: Literal["conversation.item.input_audio_transcription.completed"] + """ + The event type, must be `conversation.item.input_audio_transcription.completed`. + """ + + usage: Usage + """Usage statistics for the transcription.""" + + logprobs: Optional[List[Logprob]] = None + """The log probabilities of the transcription.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_input_audio_transcription_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_input_audio_transcription_delta_event.py new file mode 100644 index 0000000..75592cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_input_audio_transcription_delta_event.py @@ -0,0 +1,37 @@ +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemInputAudioTranscriptionDeltaEvent", "Logprob"] + + +class Logprob(BaseModel): + token: str + """The token that was used to generate the log probability.""" + + bytes: List[int] + """The bytes that were used to generate the log probability.""" + + logprob: float + """The log probability of the token.""" + + +class ConversationItemInputAudioTranscriptionDeltaEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + type: Literal["conversation.item.input_audio_transcription.delta"] + """The event type, must be `conversation.item.input_audio_transcription.delta`.""" + + content_index: Optional[int] = None + """The index of the content part in the item's content array.""" + + delta: Optional[str] = None + """The text delta.""" + + logprobs: Optional[List[Logprob]] = None + """The log probabilities of the transcription.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_input_audio_transcription_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_input_audio_transcription_failed_event.py new file mode 100644 index 0000000..50772b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_input_audio_transcription_failed_event.py @@ -0,0 +1,37 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemInputAudioTranscriptionFailedEvent", "Error"] + + +class Error(BaseModel): + code: Optional[str] = None + """Error code, if any.""" + + message: Optional[str] = None + """A human-readable error message.""" + + param: Optional[str] = None + """Parameter related to the error, if any.""" + + type: Optional[str] = None + """The type of error.""" + + +class ConversationItemInputAudioTranscriptionFailedEvent(BaseModel): + content_index: int + """The index of the content part containing the audio.""" + + error: Error + """Details of the transcription error.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item.""" + + type: Literal["conversation.item.input_audio_transcription.failed"] + """The event type, must be `conversation.item.input_audio_transcription.failed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_param.py new file mode 100644 index 0000000..4ae4ed1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_param.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, TypedDict + +from .conversation_item_content_param import ConversationItemContentParam + +__all__ = ["ConversationItemParam"] + + +class ConversationItemParam(TypedDict, total=False): + id: str + """ + The unique ID of the item, this can be generated by the client to help manage + server-side context, but is not required because the server will generate one if + not provided. + """ + + arguments: str + """The arguments of the function call (for `function_call` items).""" + + call_id: str + """ + The ID of the function call (for `function_call` and `function_call_output` + items). If passed on a `function_call_output` item, the server will check that a + `function_call` item with the same ID exists in the conversation history. + """ + + content: Iterable[ConversationItemContentParam] + """The content of the message, applicable for `message` items. + + - Message items of role `system` support only `input_text` content + - Message items of role `user` support `input_text` and `input_audio` content + - Message items of role `assistant` support `text` content. + """ + + name: str + """The name of the function being called (for `function_call` items).""" + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`.""" + + output: str + """The output of the function call (for `function_call_output` items).""" + + role: Literal["user", "assistant", "system"] + """ + The role of the message sender (`user`, `assistant`, `system`), only applicable + for `message` items. + """ + + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item (`completed`, `incomplete`, `in_progress`). + + These have no effect on the conversation, but are accepted for consistency with + the `conversation.item.created` event. + """ + + type: Literal["message", "function_call", "function_call_output"] + """The type of the item (`message`, `function_call`, `function_call_output`).""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_retrieve_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_retrieve_event.py new file mode 100644 index 0000000..eb8fb3a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_retrieve_event.py @@ -0,0 +1,17 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemRetrieveEvent"] + + +class ConversationItemRetrieveEvent(BaseModel): + item_id: str + """The ID of the item to retrieve.""" + + type: Literal["conversation.item.retrieve"] + """The event type, must be `conversation.item.retrieve`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_retrieve_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_retrieve_event_param.py new file mode 100644 index 0000000..0a889b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_retrieve_event_param.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConversationItemRetrieveEventParam"] + + +class ConversationItemRetrieveEventParam(TypedDict, total=False): + item_id: Required[str] + """The ID of the item to retrieve.""" + + type: Required[Literal["conversation.item.retrieve"]] + """The event type, must be `conversation.item.retrieve`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_truncate_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_truncate_event.py new file mode 100644 index 0000000..ee25f51 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_truncate_event.py @@ -0,0 +1,30 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemTruncateEvent"] + + +class ConversationItemTruncateEvent(BaseModel): + audio_end_ms: int + """Inclusive duration up to which audio is truncated, in milliseconds. + + If the audio_end_ms is greater than the actual audio duration, the server will + respond with an error. + """ + + content_index: int + """The index of the content part to truncate. Set this to 0.""" + + item_id: str + """The ID of the assistant message item to truncate. + + Only assistant message items can be truncated. + """ + + type: Literal["conversation.item.truncate"] + """The event type, must be `conversation.item.truncate`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_truncate_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_truncate_event_param.py new file mode 100644 index 0000000..fe65493 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_truncate_event_param.py @@ -0,0 +1,29 @@ +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConversationItemTruncateEventParam"] + + +class ConversationItemTruncateEventParam(TypedDict, total=False): + audio_end_ms: Required[int] + """Inclusive duration up to which audio is truncated, in milliseconds. + + If the audio_end_ms is greater than the actual audio duration, the server will + respond with an error. + """ + + content_index: Required[int] + """The index of the content part to truncate. Set this to 0.""" + + item_id: Required[str] + """The ID of the assistant message item to truncate. + + Only assistant message items can be truncated. + """ + + type: Required[Literal["conversation.item.truncate"]] + """The event type, must be `conversation.item.truncate`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_truncated_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_truncated_event.py new file mode 100644 index 0000000..50afa12 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_truncated_event.py @@ -0,0 +1,22 @@ +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemTruncatedEvent"] + + +class ConversationItemTruncatedEvent(BaseModel): + audio_end_ms: int + """The duration up to which the audio was truncated, in milliseconds.""" + + content_index: int + """The index of the content part that was truncated.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the assistant message item that was truncated.""" + + type: Literal["conversation.item.truncated"] + """The event type, must be `conversation.item.truncated`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_with_reference.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_with_reference.py new file mode 100644 index 0000000..d1f4fe3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_with_reference.py @@ -0,0 +1,85 @@ +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemWithReference", "Content"] + + +class Content(BaseModel): + id: Optional[str] = None + """ + ID of a previous conversation item to reference (for `item_reference` content + types in `response.create` events). These can reference both client and server + created items. + """ + + audio: Optional[str] = None + """Base64-encoded audio bytes, used for `input_audio` content type.""" + + text: Optional[str] = None + """The text content, used for `input_text` and `text` content types.""" + + transcript: Optional[str] = None + """The transcript of the audio, used for `input_audio` content type.""" + + type: Optional[Literal["input_text", "input_audio", "item_reference", "text"]] = None + """The content type (`input_text`, `input_audio`, `item_reference`, `text`).""" + + +class ConversationItemWithReference(BaseModel): + id: Optional[str] = None + """ + For an item of type (`message` | `function_call` | `function_call_output`) this + field allows the client to assign the unique ID of the item. It is not required + because the server will generate one if not provided. + + For an item of type `item_reference`, this field is required and is a reference + to any item that has previously existed in the conversation. + """ + + arguments: Optional[str] = None + """The arguments of the function call (for `function_call` items).""" + + call_id: Optional[str] = None + """ + The ID of the function call (for `function_call` and `function_call_output` + items). If passed on a `function_call_output` item, the server will check that a + `function_call` item with the same ID exists in the conversation history. + """ + + content: Optional[List[Content]] = None + """The content of the message, applicable for `message` items. + + - Message items of role `system` support only `input_text` content + - Message items of role `user` support `input_text` and `input_audio` content + - Message items of role `assistant` support `text` content. + """ + + name: Optional[str] = None + """The name of the function being called (for `function_call` items).""" + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`.""" + + output: Optional[str] = None + """The output of the function call (for `function_call_output` items).""" + + role: Optional[Literal["user", "assistant", "system"]] = None + """ + The role of the message sender (`user`, `assistant`, `system`), only applicable + for `message` items. + """ + + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item (`completed`, `incomplete`, `in_progress`). + + These have no effect on the conversation, but are accepted for consistency with + the `conversation.item.created` event. + """ + + type: Optional[Literal["message", "function_call", "function_call_output", "item_reference"]] = None + """ + The type of the item (`message`, `function_call`, `function_call_output`, + `item_reference`). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_with_reference_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_with_reference_param.py new file mode 100644 index 0000000..426913f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/conversation_item_with_reference_param.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, TypedDict + +__all__ = ["ConversationItemWithReferenceParam", "Content"] + + +class Content(TypedDict, total=False): + id: str + """ + ID of a previous conversation item to reference (for `item_reference` content + types in `response.create` events). These can reference both client and server + created items. + """ + + audio: str + """Base64-encoded audio bytes, used for `input_audio` content type.""" + + text: str + """The text content, used for `input_text` and `text` content types.""" + + transcript: str + """The transcript of the audio, used for `input_audio` content type.""" + + type: Literal["input_text", "input_audio", "item_reference", "text"] + """The content type (`input_text`, `input_audio`, `item_reference`, `text`).""" + + +class ConversationItemWithReferenceParam(TypedDict, total=False): + id: str + """ + For an item of type (`message` | `function_call` | `function_call_output`) this + field allows the client to assign the unique ID of the item. It is not required + because the server will generate one if not provided. + + For an item of type `item_reference`, this field is required and is a reference + to any item that has previously existed in the conversation. + """ + + arguments: str + """The arguments of the function call (for `function_call` items).""" + + call_id: str + """ + The ID of the function call (for `function_call` and `function_call_output` + items). If passed on a `function_call_output` item, the server will check that a + `function_call` item with the same ID exists in the conversation history. + """ + + content: Iterable[Content] + """The content of the message, applicable for `message` items. + + - Message items of role `system` support only `input_text` content + - Message items of role `user` support `input_text` and `input_audio` content + - Message items of role `assistant` support `text` content. + """ + + name: str + """The name of the function being called (for `function_call` items).""" + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`.""" + + output: str + """The output of the function call (for `function_call_output` items).""" + + role: Literal["user", "assistant", "system"] + """ + The role of the message sender (`user`, `assistant`, `system`), only applicable + for `message` items. + """ + + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item (`completed`, `incomplete`, `in_progress`). + + These have no effect on the conversation, but are accepted for consistency with + the `conversation.item.created` event. + """ + + type: Literal["message", "function_call", "function_call_output", "item_reference"] + """ + The type of the item (`message`, `function_call`, `function_call_output`, + `item_reference`). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/error_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/error_event.py new file mode 100644 index 0000000..2438526 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/error_event.py @@ -0,0 +1,34 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ErrorEvent", "Error"] + + +class Error(BaseModel): + message: str + """A human-readable error message.""" + + type: str + """The type of error (e.g., "invalid_request_error", "server_error").""" + + code: Optional[str] = None + """Error code, if any.""" + + event_id: Optional[str] = None + """The event_id of the client event that caused the error, if applicable.""" + + param: Optional[str] = None + """Parameter related to the error, if any.""" + + +class ErrorEvent(BaseModel): + error: Error + """Details of the error.""" + + event_id: str + """The unique ID of the server event.""" + + type: Literal["error"] + """The event type, must be `error`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_append_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_append_event.py new file mode 100644 index 0000000..9cfc359 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_append_event.py @@ -0,0 +1,21 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputAudioBufferAppendEvent"] + + +class InputAudioBufferAppendEvent(BaseModel): + audio: str + """Base64-encoded audio bytes. + + This must be in the format specified by the `input_audio_format` field in the + session configuration. + """ + + type: Literal["input_audio_buffer.append"] + """The event type, must be `input_audio_buffer.append`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_append_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_append_event_param.py new file mode 100644 index 0000000..fc2065d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_append_event_param.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioBufferAppendEventParam"] + + +class InputAudioBufferAppendEventParam(TypedDict, total=False): + audio: Required[str] + """Base64-encoded audio bytes. + + This must be in the format specified by the `input_audio_format` field in the + session configuration. + """ + + type: Required[Literal["input_audio_buffer.append"]] + """The event type, must be `input_audio_buffer.append`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_clear_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_clear_event.py new file mode 100644 index 0000000..73a35d9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_clear_event.py @@ -0,0 +1,14 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputAudioBufferClearEvent"] + + +class InputAudioBufferClearEvent(BaseModel): + type: Literal["input_audio_buffer.clear"] + """The event type, must be `input_audio_buffer.clear`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_clear_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_clear_event_param.py new file mode 100644 index 0000000..d7c0023 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_clear_event_param.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioBufferClearEventParam"] + + +class InputAudioBufferClearEventParam(TypedDict, total=False): + type: Required[Literal["input_audio_buffer.clear"]] + """The event type, must be `input_audio_buffer.clear`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_cleared_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_cleared_event.py new file mode 100644 index 0000000..6ef3ee1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_cleared_event.py @@ -0,0 +1,13 @@ +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputAudioBufferClearedEvent"] + + +class InputAudioBufferClearedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + type: Literal["input_audio_buffer.cleared"] + """The event type, must be `input_audio_buffer.cleared`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_commit_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_commit_event.py new file mode 100644 index 0000000..17258b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_commit_event.py @@ -0,0 +1,14 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputAudioBufferCommitEvent"] + + +class InputAudioBufferCommitEvent(BaseModel): + type: Literal["input_audio_buffer.commit"] + """The event type, must be `input_audio_buffer.commit`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_commit_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_commit_event_param.py new file mode 100644 index 0000000..88ceef7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_commit_event_param.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioBufferCommitEventParam"] + + +class InputAudioBufferCommitEventParam(TypedDict, total=False): + type: Required[Literal["input_audio_buffer.commit"]] + """The event type, must be `input_audio_buffer.commit`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_committed_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_committed_event.py new file mode 100644 index 0000000..d632964 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_committed_event.py @@ -0,0 +1,23 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputAudioBufferCommittedEvent"] + + +class InputAudioBufferCommittedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item that will be created.""" + + type: Literal["input_audio_buffer.committed"] + """The event type, must be `input_audio_buffer.committed`.""" + + previous_item_id: Optional[str] = None + """ + The ID of the preceding item after which the new item will be inserted. Can be + `null` if the item has no predecessor. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_speech_started_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_speech_started_event.py new file mode 100644 index 0000000..ca85dd8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_speech_started_event.py @@ -0,0 +1,24 @@ +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputAudioBufferSpeechStartedEvent"] + + +class InputAudioBufferSpeechStartedEvent(BaseModel): + audio_start_ms: int + """ + Milliseconds from the start of all audio written to the buffer during the + session when speech was first detected. This will correspond to the beginning of + audio sent to the model, and thus includes the `prefix_padding_ms` configured in + the Session. + """ + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item that will be created when speech stops.""" + + type: Literal["input_audio_buffer.speech_started"] + """The event type, must be `input_audio_buffer.speech_started`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_speech_stopped_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_speech_stopped_event.py new file mode 100644 index 0000000..a3d48aa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/input_audio_buffer_speech_stopped_event.py @@ -0,0 +1,23 @@ +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputAudioBufferSpeechStoppedEvent"] + + +class InputAudioBufferSpeechStoppedEvent(BaseModel): + audio_end_ms: int + """Milliseconds since the session started when speech stopped. + + This will correspond to the end of audio sent to the model, and thus includes + the `min_silence_duration_ms` configured in the Session. + """ + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item that will be created.""" + + type: Literal["input_audio_buffer.speech_stopped"] + """The event type, must be `input_audio_buffer.speech_stopped`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/rate_limits_updated_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/rate_limits_updated_event.py new file mode 100644 index 0000000..03516cb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/rate_limits_updated_event.py @@ -0,0 +1,31 @@ +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["RateLimitsUpdatedEvent", "RateLimit"] + + +class RateLimit(BaseModel): + limit: Optional[int] = None + """The maximum allowed value for the rate limit.""" + + name: Optional[Literal["requests", "tokens"]] = None + """The name of the rate limit (`requests`, `tokens`).""" + + remaining: Optional[int] = None + """The remaining value before the limit is reached.""" + + reset_seconds: Optional[float] = None + """Seconds until the rate limit resets.""" + + +class RateLimitsUpdatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + rate_limits: List[RateLimit] + """List of rate limit information.""" + + type: Literal["rate_limits.updated"] + """The event type, must be `rate_limits.updated`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_client_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_client_event.py new file mode 100644 index 0000000..4cfbf24 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_client_event.py @@ -0,0 +1,45 @@ +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .session_update_event import SessionUpdateEvent +from .response_cancel_event import ResponseCancelEvent +from .response_create_event import ResponseCreateEvent +from .transcription_session_update import TranscriptionSessionUpdate +from .conversation_item_create_event import ConversationItemCreateEvent +from .conversation_item_delete_event import ConversationItemDeleteEvent +from .input_audio_buffer_clear_event import InputAudioBufferClearEvent +from .input_audio_buffer_append_event import InputAudioBufferAppendEvent +from .input_audio_buffer_commit_event import InputAudioBufferCommitEvent +from .conversation_item_retrieve_event import ConversationItemRetrieveEvent +from .conversation_item_truncate_event import ConversationItemTruncateEvent + +__all__ = ["RealtimeClientEvent", "OutputAudioBufferClear"] + + +class OutputAudioBufferClear(BaseModel): + type: Literal["output_audio_buffer.clear"] + """The event type, must be `output_audio_buffer.clear`.""" + + event_id: Optional[str] = None + """The unique ID of the client event used for error handling.""" + + +RealtimeClientEvent: TypeAlias = Annotated[ + Union[ + ConversationItemCreateEvent, + ConversationItemDeleteEvent, + ConversationItemRetrieveEvent, + ConversationItemTruncateEvent, + InputAudioBufferAppendEvent, + InputAudioBufferClearEvent, + OutputAudioBufferClear, + InputAudioBufferCommitEvent, + ResponseCancelEvent, + ResponseCreateEvent, + SessionUpdateEvent, + TranscriptionSessionUpdate, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_client_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_client_event_param.py new file mode 100644 index 0000000..86aa3be --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_client_event_param.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .session_update_event_param import SessionUpdateEventParam +from .response_cancel_event_param import ResponseCancelEventParam +from .response_create_event_param import ResponseCreateEventParam +from .transcription_session_update_param import TranscriptionSessionUpdateParam +from .conversation_item_create_event_param import ConversationItemCreateEventParam +from .conversation_item_delete_event_param import ConversationItemDeleteEventParam +from .input_audio_buffer_clear_event_param import InputAudioBufferClearEventParam +from .input_audio_buffer_append_event_param import InputAudioBufferAppendEventParam +from .input_audio_buffer_commit_event_param import InputAudioBufferCommitEventParam +from .conversation_item_retrieve_event_param import ConversationItemRetrieveEventParam +from .conversation_item_truncate_event_param import ConversationItemTruncateEventParam + +__all__ = ["RealtimeClientEventParam", "OutputAudioBufferClear"] + + +class OutputAudioBufferClear(TypedDict, total=False): + type: Required[Literal["output_audio_buffer.clear"]] + """The event type, must be `output_audio_buffer.clear`.""" + + event_id: str + """The unique ID of the client event used for error handling.""" + + +RealtimeClientEventParam: TypeAlias = Union[ + ConversationItemCreateEventParam, + ConversationItemDeleteEventParam, + ConversationItemRetrieveEventParam, + ConversationItemTruncateEventParam, + InputAudioBufferAppendEventParam, + InputAudioBufferClearEventParam, + OutputAudioBufferClear, + InputAudioBufferCommitEventParam, + ResponseCancelEventParam, + ResponseCreateEventParam, + SessionUpdateEventParam, + TranscriptionSessionUpdateParam, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_connect_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_connect_params.py new file mode 100644 index 0000000..aa21222 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_connect_params.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RealtimeConnectParams"] + + +class RealtimeConnectParams(TypedDict, total=False): + model: Required[str] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_response.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_response.py new file mode 100644 index 0000000..0b7e02c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_response.py @@ -0,0 +1,85 @@ +from typing import List, Union, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from ...shared.metadata import Metadata +from .conversation_item import ConversationItem +from .realtime_response_usage import RealtimeResponseUsage +from .realtime_response_status import RealtimeResponseStatus + +__all__ = ["RealtimeResponse"] + + +class RealtimeResponse(BaseModel): + id: Optional[str] = None + """The unique ID of the response.""" + + conversation_id: Optional[str] = None + """ + Which conversation the response is added to, determined by the `conversation` + field in the `response.create` event. If `auto`, the response will be added to + the default conversation and the value of `conversation_id` will be an id like + `conv_1234`. If `none`, the response will not be added to any conversation and + the value of `conversation_id` will be `null`. If responses are being triggered + by server VAD, the response will be added to the default conversation, thus the + `conversation_id` will be an id like `conv_1234`. + """ + + max_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls, that was used in this response. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model used to respond. + + If there are multiple modalities, the model will pick one, for example if + `modalities` is `["text", "audio"]`, the model could be responding in either + text or audio. + """ + + object: Optional[Literal["realtime.response"]] = None + """The object type, must be `realtime.response`.""" + + output: Optional[List[ConversationItem]] = None + """The list of output items generated by the response.""" + + output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + status: Optional[Literal["completed", "cancelled", "failed", "incomplete", "in_progress"]] = None + """ + The final status of the response (`completed`, `cancelled`, `failed`, or + `incomplete`, `in_progress`). + """ + + status_details: Optional[RealtimeResponseStatus] = None + """Additional details about the status.""" + + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + usage: Optional[RealtimeResponseUsage] = None + """Usage statistics for the Response, this will correspond to billing. + + A Realtime API session will maintain a conversation context and append new Items + to the Conversation, thus output from previous turns (text and audio tokens) + will become the input for later turns. + """ + + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"], None] = None + """ + The voice the model used to respond. Current voice options are `alloy`, `ash`, + `ballad`, `coral`, `echo`, `sage`, `shimmer`, and `verse`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_response_status.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_response_status.py new file mode 100644 index 0000000..c453b46 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_response_status.py @@ -0,0 +1,37 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["RealtimeResponseStatus", "Error"] + + +class Error(BaseModel): + code: Optional[str] = None + """Error code, if any.""" + + type: Optional[str] = None + """The type of error.""" + + +class RealtimeResponseStatus(BaseModel): + error: Optional[Error] = None + """ + A description of the error that caused the response to fail, populated when the + `status` is `failed`. + """ + + reason: Optional[Literal["turn_detected", "client_cancelled", "max_output_tokens", "content_filter"]] = None + """The reason the Response did not complete. + + For a `cancelled` Response, one of `turn_detected` (the server VAD detected a + new start of speech) or `client_cancelled` (the client sent a cancel event). For + an `incomplete` Response, one of `max_output_tokens` or `content_filter` (the + server-side safety filter activated and cut off the response). + """ + + type: Optional[Literal["completed", "cancelled", "incomplete", "failed"]] = None + """ + The type of error that caused the response to fail, corresponding with the + `status` field (`completed`, `cancelled`, `incomplete`, `failed`). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_response_usage.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_response_usage.py new file mode 100644 index 0000000..1e5ac7e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_response_usage.py @@ -0,0 +1,50 @@ +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["RealtimeResponseUsage", "InputTokenDetails", "OutputTokenDetails"] + + +class InputTokenDetails(BaseModel): + audio_tokens: Optional[int] = None + """The number of audio tokens used in the Response.""" + + cached_tokens: Optional[int] = None + """The number of cached tokens used in the Response.""" + + text_tokens: Optional[int] = None + """The number of text tokens used in the Response.""" + + +class OutputTokenDetails(BaseModel): + audio_tokens: Optional[int] = None + """The number of audio tokens used in the Response.""" + + text_tokens: Optional[int] = None + """The number of text tokens used in the Response.""" + + +class RealtimeResponseUsage(BaseModel): + input_token_details: Optional[InputTokenDetails] = None + """Details about the input tokens used in the Response.""" + + input_tokens: Optional[int] = None + """ + The number of input tokens used in the Response, including text and audio + tokens. + """ + + output_token_details: Optional[OutputTokenDetails] = None + """Details about the output tokens used in the Response.""" + + output_tokens: Optional[int] = None + """ + The number of output tokens sent in the Response, including text and audio + tokens. + """ + + total_tokens: Optional[int] = None + """ + The total number of tokens in the Response including input and output text and + audio tokens. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_server_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_server_event.py new file mode 100644 index 0000000..3a99a83 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/realtime_server_event.py @@ -0,0 +1,131 @@ +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .error_event import ErrorEvent +from .conversation_item import ConversationItem +from .response_done_event import ResponseDoneEvent +from .session_created_event import SessionCreatedEvent +from .session_updated_event import SessionUpdatedEvent +from .response_created_event import ResponseCreatedEvent +from .response_text_done_event import ResponseTextDoneEvent +from .rate_limits_updated_event import RateLimitsUpdatedEvent +from .response_audio_done_event import ResponseAudioDoneEvent +from .response_text_delta_event import ResponseTextDeltaEvent +from .conversation_created_event import ConversationCreatedEvent +from .response_audio_delta_event import ResponseAudioDeltaEvent +from .conversation_item_created_event import ConversationItemCreatedEvent +from .conversation_item_deleted_event import ConversationItemDeletedEvent +from .response_output_item_done_event import ResponseOutputItemDoneEvent +from .input_audio_buffer_cleared_event import InputAudioBufferClearedEvent +from .response_content_part_done_event import ResponseContentPartDoneEvent +from .response_output_item_added_event import ResponseOutputItemAddedEvent +from .conversation_item_truncated_event import ConversationItemTruncatedEvent +from .response_content_part_added_event import ResponseContentPartAddedEvent +from .input_audio_buffer_committed_event import InputAudioBufferCommittedEvent +from .transcription_session_updated_event import TranscriptionSessionUpdatedEvent +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent +from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent +from .input_audio_buffer_speech_started_event import InputAudioBufferSpeechStartedEvent +from .input_audio_buffer_speech_stopped_event import InputAudioBufferSpeechStoppedEvent +from .response_function_call_arguments_done_event import ResponseFunctionCallArgumentsDoneEvent +from .response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent +from .conversation_item_input_audio_transcription_delta_event import ConversationItemInputAudioTranscriptionDeltaEvent +from .conversation_item_input_audio_transcription_failed_event import ConversationItemInputAudioTranscriptionFailedEvent +from .conversation_item_input_audio_transcription_completed_event import ( + ConversationItemInputAudioTranscriptionCompletedEvent, +) + +__all__ = [ + "RealtimeServerEvent", + "ConversationItemRetrieved", + "OutputAudioBufferStarted", + "OutputAudioBufferStopped", + "OutputAudioBufferCleared", +] + + +class ConversationItemRetrieved(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """The item to add to the conversation.""" + + type: Literal["conversation.item.retrieved"] + """The event type, must be `conversation.item.retrieved`.""" + + +class OutputAudioBufferStarted(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response_id: str + """The unique ID of the response that produced the audio.""" + + type: Literal["output_audio_buffer.started"] + """The event type, must be `output_audio_buffer.started`.""" + + +class OutputAudioBufferStopped(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response_id: str + """The unique ID of the response that produced the audio.""" + + type: Literal["output_audio_buffer.stopped"] + """The event type, must be `output_audio_buffer.stopped`.""" + + +class OutputAudioBufferCleared(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response_id: str + """The unique ID of the response that produced the audio.""" + + type: Literal["output_audio_buffer.cleared"] + """The event type, must be `output_audio_buffer.cleared`.""" + + +RealtimeServerEvent: TypeAlias = Annotated[ + Union[ + ConversationCreatedEvent, + ConversationItemCreatedEvent, + ConversationItemDeletedEvent, + ConversationItemInputAudioTranscriptionCompletedEvent, + ConversationItemInputAudioTranscriptionDeltaEvent, + ConversationItemInputAudioTranscriptionFailedEvent, + ConversationItemRetrieved, + ConversationItemTruncatedEvent, + ErrorEvent, + InputAudioBufferClearedEvent, + InputAudioBufferCommittedEvent, + InputAudioBufferSpeechStartedEvent, + InputAudioBufferSpeechStoppedEvent, + RateLimitsUpdatedEvent, + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreatedEvent, + ResponseDoneEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + SessionCreatedEvent, + SessionUpdatedEvent, + TranscriptionSessionUpdatedEvent, + OutputAudioBufferStarted, + OutputAudioBufferStopped, + OutputAudioBufferCleared, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_audio_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_audio_delta_event.py new file mode 100644 index 0000000..c753114 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_audio_delta_event.py @@ -0,0 +1,28 @@ +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseAudioDeltaEvent"] + + +class ResponseAudioDeltaEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + delta: str + """Base64-encoded audio data delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.audio.delta"] + """The event type, must be `response.audio.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_audio_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_audio_done_event.py new file mode 100644 index 0000000..f4c0539 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_audio_done_event.py @@ -0,0 +1,25 @@ +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseAudioDoneEvent"] + + +class ResponseAudioDoneEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.audio.done"] + """The event type, must be `response.audio.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_audio_transcript_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_audio_transcript_delta_event.py new file mode 100644 index 0000000..f7331a3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_audio_transcript_delta_event.py @@ -0,0 +1,28 @@ +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseAudioTranscriptDeltaEvent"] + + +class ResponseAudioTranscriptDeltaEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + delta: str + """The transcript delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.audio_transcript.delta"] + """The event type, must be `response.audio_transcript.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_audio_transcript_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_audio_transcript_done_event.py new file mode 100644 index 0000000..d60ad73 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_audio_transcript_done_event.py @@ -0,0 +1,28 @@ +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseAudioTranscriptDoneEvent"] + + +class ResponseAudioTranscriptDoneEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + transcript: str + """The final transcript of the audio.""" + + type: Literal["response.audio_transcript.done"] + """The event type, must be `response.audio_transcript.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_cancel_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_cancel_event.py new file mode 100644 index 0000000..a6c6ed0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_cancel_event.py @@ -0,0 +1,20 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseCancelEvent"] + + +class ResponseCancelEvent(BaseModel): + type: Literal["response.cancel"] + """The event type, must be `response.cancel`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" + + response_id: Optional[str] = None + """ + A specific response ID to cancel - if not provided, will cancel an in-progress + response in the default conversation. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_cancel_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_cancel_event_param.py new file mode 100644 index 0000000..6c7863b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_cancel_event_param.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseCancelEventParam"] + + +class ResponseCancelEventParam(TypedDict, total=False): + type: Required[Literal["response.cancel"]] + """The event type, must be `response.cancel`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" + + response_id: str + """ + A specific response ID to cancel - if not provided, will cancel an in-progress + response in the default conversation. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_content_part_added_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_content_part_added_event.py new file mode 100644 index 0000000..24286be --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_content_part_added_event.py @@ -0,0 +1,43 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseContentPartAddedEvent", "Part"] + + +class Part(BaseModel): + audio: Optional[str] = None + """Base64-encoded audio data (if type is "audio").""" + + text: Optional[str] = None + """The text content (if type is "text").""" + + transcript: Optional[str] = None + """The transcript of the audio (if type is "audio").""" + + type: Optional[Literal["text", "audio"]] = None + """The content type ("text", "audio").""" + + +class ResponseContentPartAddedEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item to which the content part was added.""" + + output_index: int + """The index of the output item in the response.""" + + part: Part + """The content part that was added.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.content_part.added"] + """The event type, must be `response.content_part.added`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_content_part_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_content_part_done_event.py new file mode 100644 index 0000000..de8beaf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_content_part_done_event.py @@ -0,0 +1,43 @@ +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseContentPartDoneEvent", "Part"] + + +class Part(BaseModel): + audio: Optional[str] = None + """Base64-encoded audio data (if type is "audio").""" + + text: Optional[str] = None + """The text content (if type is "text").""" + + transcript: Optional[str] = None + """The transcript of the audio (if type is "audio").""" + + type: Optional[Literal["text", "audio"]] = None + """The content type ("text", "audio").""" + + +class ResponseContentPartDoneEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + part: Part + """The content part that is done.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.content_part.done"] + """The event type, must be `response.content_part.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_create_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_create_event.py new file mode 100644 index 0000000..4216972 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_create_event.py @@ -0,0 +1,119 @@ +from typing import List, Union, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from ...shared.metadata import Metadata +from .conversation_item_with_reference import ConversationItemWithReference + +__all__ = ["ResponseCreateEvent", "Response", "ResponseTool"] + + +class ResponseTool(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" + + +class Response(BaseModel): + conversation: Union[str, Literal["auto", "none"], None] = None + """Controls which conversation the response is added to. + + Currently supports `auto` and `none`, with `auto` as the default value. The + `auto` value means that the contents of the response will be added to the + default conversation. Set this to `none` to create an out-of-band response which + will not add items to default conversation. + """ + + input: Optional[List[ConversationItemWithReference]] = None + """Input items to include in the prompt for the model. + + Using this field creates a new context for this Response instead of using the + default conversation. An empty array `[]` will clear the context for this + Response. Note that this can include references to items from the default + conversation. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + tool_choice: Optional[str] = None + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function, like + `{"type": "function", "function": {"name": "my_function"}}`. + """ + + tools: Optional[List[ResponseTool]] = None + """Tools (functions) available to the model.""" + + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"], None] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, and `verse`. + """ + + +class ResponseCreateEvent(BaseModel): + type: Literal["response.create"] + """The event type, must be `response.create`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" + + response: Optional[Response] = None + """Create a new Realtime response with these parameters""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_create_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_create_event_param.py new file mode 100644 index 0000000..6d43f92 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_create_event_param.py @@ -0,0 +1,120 @@ +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from ...shared_params.metadata import Metadata +from .conversation_item_with_reference_param import ConversationItemWithReferenceParam + +__all__ = ["ResponseCreateEventParam", "Response", "ResponseTool"] + + +class ResponseTool(TypedDict, total=False): + description: str + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: str + """The name of the function.""" + + parameters: object + """Parameters of the function in JSON Schema.""" + + type: Literal["function"] + """The type of the tool, i.e. `function`.""" + + +class Response(TypedDict, total=False): + conversation: Union[str, Literal["auto", "none"]] + """Controls which conversation the response is added to. + + Currently supports `auto` and `none`, with `auto` as the default value. The + `auto` value means that the contents of the response will be added to the + default conversation. Set this to `none` to create an out-of-band response which + will not add items to default conversation. + """ + + input: Iterable[ConversationItemWithReferenceParam] + """Input items to include in the prompt for the model. + + Using this field creates a new context for this Response instead of using the + default conversation. An empty array `[]` will clear the context for this + Response. Note that this can include references to items from the default + conversation. + """ + + instructions: str + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"]] + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + temperature: float + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + tool_choice: str + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function, like + `{"type": "function", "function": {"name": "my_function"}}`. + """ + + tools: Iterable[ResponseTool] + """Tools (functions) available to the model.""" + + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, and `verse`. + """ + + +class ResponseCreateEventParam(TypedDict, total=False): + type: Required[Literal["response.create"]] + """The event type, must be `response.create`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" + + response: Response + """Create a new Realtime response with these parameters""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_created_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_created_event.py new file mode 100644 index 0000000..b0a33e7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_created_event.py @@ -0,0 +1,17 @@ +from typing_extensions import Literal + +from ...._models import BaseModel +from .realtime_response import RealtimeResponse + +__all__ = ["ResponseCreatedEvent"] + + +class ResponseCreatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response: RealtimeResponse + """The response resource.""" + + type: Literal["response.created"] + """The event type, must be `response.created`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_done_event.py new file mode 100644 index 0000000..73771bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_done_event.py @@ -0,0 +1,17 @@ +from typing_extensions import Literal + +from ...._models import BaseModel +from .realtime_response import RealtimeResponse + +__all__ = ["ResponseDoneEvent"] + + +class ResponseDoneEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response: RealtimeResponse + """The response resource.""" + + type: Literal["response.done"] + """The event type, must be `response.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_function_call_arguments_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_function_call_arguments_delta_event.py new file mode 100644 index 0000000..a5f364a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_function_call_arguments_delta_event.py @@ -0,0 +1,28 @@ +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseFunctionCallArgumentsDeltaEvent"] + + +class ResponseFunctionCallArgumentsDeltaEvent(BaseModel): + call_id: str + """The ID of the function call.""" + + delta: str + """The arguments delta as a JSON string.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the function call item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.function_call_arguments.delta"] + """The event type, must be `response.function_call_arguments.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_function_call_arguments_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_function_call_arguments_done_event.py new file mode 100644 index 0000000..03014dd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_function_call_arguments_done_event.py @@ -0,0 +1,28 @@ +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseFunctionCallArgumentsDoneEvent"] + + +class ResponseFunctionCallArgumentsDoneEvent(BaseModel): + arguments: str + """The final arguments as a JSON string.""" + + call_id: str + """The ID of the function call.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the function call item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.function_call_arguments.done"] + """The event type, must be `response.function_call_arguments.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_output_item_added_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_output_item_added_event.py new file mode 100644 index 0000000..d5fd1cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_output_item_added_event.py @@ -0,0 +1,23 @@ +from typing_extensions import Literal + +from ...._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ResponseOutputItemAddedEvent"] + + +class ResponseOutputItemAddedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """The item to add to the conversation.""" + + output_index: int + """The index of the output item in the Response.""" + + response_id: str + """The ID of the Response to which the item belongs.""" + + type: Literal["response.output_item.added"] + """The event type, must be `response.output_item.added`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_output_item_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_output_item_done_event.py new file mode 100644 index 0000000..4f9fbd4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_output_item_done_event.py @@ -0,0 +1,23 @@ +from typing_extensions import Literal + +from ...._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ResponseOutputItemDoneEvent"] + + +class ResponseOutputItemDoneEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """The item to add to the conversation.""" + + output_index: int + """The index of the output item in the Response.""" + + response_id: str + """The ID of the Response to which the item belongs.""" + + type: Literal["response.output_item.done"] + """The event type, must be `response.output_item.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_text_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_text_delta_event.py new file mode 100644 index 0000000..f61dcc8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_text_delta_event.py @@ -0,0 +1,28 @@ +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseTextDeltaEvent"] + + +class ResponseTextDeltaEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + delta: str + """The text delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.text.delta"] + """The event type, must be `response.text.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_text_done_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_text_done_event.py new file mode 100644 index 0000000..5b85e40 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/response_text_done_event.py @@ -0,0 +1,28 @@ +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseTextDoneEvent"] + + +class ResponseTextDoneEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + text: str + """The final text content.""" + + type: Literal["response.text.done"] + """The event type, must be `response.text.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session.py new file mode 100644 index 0000000..2694e87 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session.py @@ -0,0 +1,277 @@ +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ...._models import BaseModel + +__all__ = [ + "Session", + "InputAudioNoiseReduction", + "InputAudioTranscription", + "Tool", + "Tracing", + "TracingTracingConfiguration", + "TurnDetection", +] + + +class InputAudioNoiseReduction(BaseModel): + type: Optional[Literal["near_field", "far_field"]] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class InputAudioTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[str] = None + """ + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: Optional[str] = None + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class Tool(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" + + +class TracingTracingConfiguration(BaseModel): + group_id: Optional[str] = None + """ + The group id to attach to this trace to enable filtering and grouping in the + traces dashboard. + """ + + metadata: Optional[object] = None + """ + The arbitrary metadata to attach to this trace to enable filtering in the traces + dashboard. + """ + + workflow_name: Optional[str] = None + """The name of the workflow to attach to this trace. + + This is used to name the trace in the traces dashboard. + """ + + +Tracing: TypeAlias = Union[Literal["auto"], TracingTracingConfiguration] + + +class TurnDetection(BaseModel): + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: Optional[float] = None + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Optional[Literal["server_vad", "semantic_vad"]] = None + """Type of turn detection.""" + + +class Session(BaseModel): + id: Optional[str] = None + """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" + + input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: Optional[InputAudioNoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: Optional[InputAudioTranscription] = None + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + model: Optional[ + Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ] + ] = None + """The Realtime model used for this session.""" + + output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of output audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is + sampled at a rate of 24kHz. + """ + + speed: Optional[float] = None + """The speed of the model's spoken response. + + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + """ + + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. + + For audio models a temperature of 0.8 is highly recommended for best + performance. + """ + + tool_choice: Optional[str] = None + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Optional[List[Tool]] = None + """Tools (functions) available to the model.""" + + tracing: Optional[Tracing] = None + """Configuration options for tracing. + + Set to null to disable tracing. Once tracing is enabled for a session, the + configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + turn_detection: Optional[TurnDetection] = None + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ + + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"], None] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, and `verse`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_create_params.py new file mode 100644 index 0000000..5c1e5ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_create_params.py @@ -0,0 +1,296 @@ +from __future__ import annotations + +from typing import List, Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "SessionCreateParams", + "ClientSecret", + "ClientSecretExpiresAfter", + "InputAudioNoiseReduction", + "InputAudioTranscription", + "Tool", + "Tracing", + "TracingTracingConfiguration", + "TurnDetection", +] + + +class SessionCreateParams(TypedDict, total=False): + client_secret: ClientSecret + """Configuration options for the generated client secret.""" + + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: InputAudioNoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: InputAudioTranscription + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + instructions: str + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"]] + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + model: Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ] + """The Realtime model used for this session.""" + + output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of output audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is + sampled at a rate of 24kHz. + """ + + speed: float + """The speed of the model's spoken response. + + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + """ + + temperature: float + """Sampling temperature for the model, limited to [0.6, 1.2]. + + For audio models a temperature of 0.8 is highly recommended for best + performance. + """ + + tool_choice: str + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Iterable[Tool] + """Tools (functions) available to the model.""" + + tracing: Tracing + """Configuration options for tracing. + + Set to null to disable tracing. Once tracing is enabled for a session, the + configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + turn_detection: TurnDetection + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ + + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, and `verse`. + """ + + +class ClientSecretExpiresAfter(TypedDict, total=False): + anchor: Required[Literal["created_at"]] + """The anchor point for the ephemeral token expiration. + + Only `created_at` is currently supported. + """ + + seconds: int + """The number of seconds from the anchor point to the expiration. + + Select a value between `10` and `7200`. + """ + + +class ClientSecret(TypedDict, total=False): + expires_after: ClientSecretExpiresAfter + """Configuration for the ephemeral token expiration.""" + + +class InputAudioNoiseReduction(TypedDict, total=False): + type: Literal["near_field", "far_field"] + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class InputAudioTranscription(TypedDict, total=False): + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: str + """ + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: str + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class Tool(TypedDict, total=False): + description: str + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: str + """The name of the function.""" + + parameters: object + """Parameters of the function in JSON Schema.""" + + type: Literal["function"] + """The type of the tool, i.e. `function`.""" + + +class TracingTracingConfiguration(TypedDict, total=False): + group_id: str + """ + The group id to attach to this trace to enable filtering and grouping in the + traces dashboard. + """ + + metadata: object + """ + The arbitrary metadata to attach to this trace to enable filtering in the traces + dashboard. + """ + + workflow_name: str + """The name of the workflow to attach to this trace. + + This is used to name the trace in the traces dashboard. + """ + + +Tracing: TypeAlias = Union[Literal["auto"], TracingTracingConfiguration] + + +class TurnDetection(TypedDict, total=False): + create_response: bool + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: int + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: int + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: float + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Literal["server_vad", "semantic_vad"] + """Type of turn detection.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_create_response.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_create_response.py new file mode 100644 index 0000000..a777d80 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_create_response.py @@ -0,0 +1,194 @@ +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ...._models import BaseModel + +__all__ = [ + "SessionCreateResponse", + "ClientSecret", + "InputAudioTranscription", + "Tool", + "Tracing", + "TracingTracingConfiguration", + "TurnDetection", +] + + +class ClientSecret(BaseModel): + expires_at: int + """Timestamp for when the token expires. + + Currently, all tokens expire after one minute. + """ + + value: str + """ + Ephemeral key usable in client environments to authenticate connections to the + Realtime API. Use this in client-side environments rather than a standard API + token, which should only be used server-side. + """ + + +class InputAudioTranscription(BaseModel): + model: Optional[str] = None + """The model to use for transcription.""" + + +class Tool(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" + + +class TracingTracingConfiguration(BaseModel): + group_id: Optional[str] = None + """ + The group id to attach to this trace to enable filtering and grouping in the + traces dashboard. + """ + + metadata: Optional[object] = None + """ + The arbitrary metadata to attach to this trace to enable filtering in the traces + dashboard. + """ + + workflow_name: Optional[str] = None + """The name of the workflow to attach to this trace. + + This is used to name the trace in the traces dashboard. + """ + + +Tracing: TypeAlias = Union[Literal["auto"], TracingTracingConfiguration] + + +class TurnDetection(BaseModel): + prefix_padding_ms: Optional[int] = None + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: Optional[float] = None + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: Optional[str] = None + """Type of turn detection, only `server_vad` is currently supported.""" + + +class SessionCreateResponse(BaseModel): + client_secret: ClientSecret + """Ephemeral key returned by the API.""" + + input_audio_format: Optional[str] = None + """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + input_audio_transcription: Optional[InputAudioTranscription] = None + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously and should be treated as rough guidance rather than the + representation understood by the model. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + output_audio_format: Optional[str] = None + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + speed: Optional[float] = None + """The speed of the model's spoken response. + + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + """ + + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + tool_choice: Optional[str] = None + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Optional[List[Tool]] = None + """Tools (functions) available to the model.""" + + tracing: Optional[Tracing] = None + """Configuration options for tracing. + + Set to null to disable tracing. Once tracing is enabled for a session, the + configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + turn_detection: Optional[TurnDetection] = None + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ + + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"], None] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, and `verse`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_created_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_created_event.py new file mode 100644 index 0000000..395626f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_created_event.py @@ -0,0 +1,17 @@ +from typing_extensions import Literal + +from .session import Session +from ...._models import BaseModel + +__all__ = ["SessionCreatedEvent"] + + +class SessionCreatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + session: Session + """Realtime session object configuration.""" + + type: Literal["session.created"] + """The event type, must be `session.created`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_update_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_update_event.py new file mode 100644 index 0000000..251517f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_update_event.py @@ -0,0 +1,310 @@ +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ...._models import BaseModel + +__all__ = [ + "SessionUpdateEvent", + "Session", + "SessionClientSecret", + "SessionClientSecretExpiresAfter", + "SessionInputAudioNoiseReduction", + "SessionInputAudioTranscription", + "SessionTool", + "SessionTracing", + "SessionTracingTracingConfiguration", + "SessionTurnDetection", +] + + +class SessionClientSecretExpiresAfter(BaseModel): + anchor: Literal["created_at"] + """The anchor point for the ephemeral token expiration. + + Only `created_at` is currently supported. + """ + + seconds: Optional[int] = None + """The number of seconds from the anchor point to the expiration. + + Select a value between `10` and `7200`. + """ + + +class SessionClientSecret(BaseModel): + expires_after: Optional[SessionClientSecretExpiresAfter] = None + """Configuration for the ephemeral token expiration.""" + + +class SessionInputAudioNoiseReduction(BaseModel): + type: Optional[Literal["near_field", "far_field"]] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class SessionInputAudioTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[str] = None + """ + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: Optional[str] = None + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class SessionTool(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" + + +class SessionTracingTracingConfiguration(BaseModel): + group_id: Optional[str] = None + """ + The group id to attach to this trace to enable filtering and grouping in the + traces dashboard. + """ + + metadata: Optional[object] = None + """ + The arbitrary metadata to attach to this trace to enable filtering in the traces + dashboard. + """ + + workflow_name: Optional[str] = None + """The name of the workflow to attach to this trace. + + This is used to name the trace in the traces dashboard. + """ + + +SessionTracing: TypeAlias = Union[Literal["auto"], SessionTracingTracingConfiguration] + + +class SessionTurnDetection(BaseModel): + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: Optional[float] = None + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Optional[Literal["server_vad", "semantic_vad"]] = None + """Type of turn detection.""" + + +class Session(BaseModel): + client_secret: Optional[SessionClientSecret] = None + """Configuration options for the generated client secret.""" + + input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: Optional[SessionInputAudioNoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: Optional[SessionInputAudioTranscription] = None + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + model: Optional[ + Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ] + ] = None + """The Realtime model used for this session.""" + + output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of output audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is + sampled at a rate of 24kHz. + """ + + speed: Optional[float] = None + """The speed of the model's spoken response. + + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + """ + + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. + + For audio models a temperature of 0.8 is highly recommended for best + performance. + """ + + tool_choice: Optional[str] = None + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Optional[List[SessionTool]] = None + """Tools (functions) available to the model.""" + + tracing: Optional[SessionTracing] = None + """Configuration options for tracing. + + Set to null to disable tracing. Once tracing is enabled for a session, the + configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + turn_detection: Optional[SessionTurnDetection] = None + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ + + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"], None] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, and `verse`. + """ + + +class SessionUpdateEvent(BaseModel): + session: Session + """Realtime session object configuration.""" + + type: Literal["session.update"] + """The event type, must be `session.update`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_update_event_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_update_event_param.py new file mode 100644 index 0000000..3186592 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_update_event_param.py @@ -0,0 +1,308 @@ +from __future__ import annotations + +from typing import List, Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "SessionUpdateEventParam", + "Session", + "SessionClientSecret", + "SessionClientSecretExpiresAfter", + "SessionInputAudioNoiseReduction", + "SessionInputAudioTranscription", + "SessionTool", + "SessionTracing", + "SessionTracingTracingConfiguration", + "SessionTurnDetection", +] + + +class SessionClientSecretExpiresAfter(TypedDict, total=False): + anchor: Required[Literal["created_at"]] + """The anchor point for the ephemeral token expiration. + + Only `created_at` is currently supported. + """ + + seconds: int + """The number of seconds from the anchor point to the expiration. + + Select a value between `10` and `7200`. + """ + + +class SessionClientSecret(TypedDict, total=False): + expires_after: SessionClientSecretExpiresAfter + """Configuration for the ephemeral token expiration.""" + + +class SessionInputAudioNoiseReduction(TypedDict, total=False): + type: Literal["near_field", "far_field"] + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class SessionInputAudioTranscription(TypedDict, total=False): + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: str + """ + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: str + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class SessionTool(TypedDict, total=False): + description: str + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: str + """The name of the function.""" + + parameters: object + """Parameters of the function in JSON Schema.""" + + type: Literal["function"] + """The type of the tool, i.e. `function`.""" + + +class SessionTracingTracingConfiguration(TypedDict, total=False): + group_id: str + """ + The group id to attach to this trace to enable filtering and grouping in the + traces dashboard. + """ + + metadata: object + """ + The arbitrary metadata to attach to this trace to enable filtering in the traces + dashboard. + """ + + workflow_name: str + """The name of the workflow to attach to this trace. + + This is used to name the trace in the traces dashboard. + """ + + +SessionTracing: TypeAlias = Union[Literal["auto"], SessionTracingTracingConfiguration] + + +class SessionTurnDetection(TypedDict, total=False): + create_response: bool + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: int + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: int + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: float + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Literal["server_vad", "semantic_vad"] + """Type of turn detection.""" + + +class Session(TypedDict, total=False): + client_secret: SessionClientSecret + """Configuration options for the generated client secret.""" + + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: SessionInputAudioNoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: SessionInputAudioTranscription + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + instructions: str + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"]] + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + model: Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ] + """The Realtime model used for this session.""" + + output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of output audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is + sampled at a rate of 24kHz. + """ + + speed: float + """The speed of the model's spoken response. + + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + """ + + temperature: float + """Sampling temperature for the model, limited to [0.6, 1.2]. + + For audio models a temperature of 0.8 is highly recommended for best + performance. + """ + + tool_choice: str + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Iterable[SessionTool] + """Tools (functions) available to the model.""" + + tracing: SessionTracing + """Configuration options for tracing. + + Set to null to disable tracing. Once tracing is enabled for a session, the + configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + turn_detection: SessionTurnDetection + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ + + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, and `verse`. + """ + + +class SessionUpdateEventParam(TypedDict, total=False): + session: Required[Session] + """Realtime session object configuration.""" + + type: Required[Literal["session.update"]] + """The event type, must be `session.update`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_updated_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_updated_event.py new file mode 100644 index 0000000..d03876b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/session_updated_event.py @@ -0,0 +1,17 @@ +from typing_extensions import Literal + +from .session import Session +from ...._models import BaseModel + +__all__ = ["SessionUpdatedEvent"] + + +class SessionUpdatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + session: Session + """Realtime session object configuration.""" + + type: Literal["session.updated"] + """The event type, must be `session.updated`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session.py new file mode 100644 index 0000000..8c4a6d0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session.py @@ -0,0 +1,98 @@ +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["TranscriptionSession", "ClientSecret", "InputAudioTranscription", "TurnDetection"] + + +class ClientSecret(BaseModel): + expires_at: int + """Timestamp for when the token expires. + + Currently, all tokens expire after one minute. + """ + + value: str + """ + Ephemeral key usable in client environments to authenticate connections to the + Realtime API. Use this in client-side environments rather than a standard API + token, which should only be used server-side. + """ + + +class InputAudioTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"]] = None + """The model to use for transcription. + + Can be `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, or `whisper-1`. + """ + + prompt: Optional[str] = None + """An optional text to guide the model's style or continue a previous audio + segment. + + The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) + should match the audio language. + """ + + +class TurnDetection(BaseModel): + prefix_padding_ms: Optional[int] = None + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: Optional[float] = None + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: Optional[str] = None + """Type of turn detection, only `server_vad` is currently supported.""" + + +class TranscriptionSession(BaseModel): + client_secret: ClientSecret + """Ephemeral key returned by the API. + + Only present when the session is created on the server via REST API. + """ + + input_audio_format: Optional[str] = None + """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + input_audio_transcription: Optional[InputAudioTranscription] = None + """Configuration of the transcription model.""" + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + turn_detection: Optional[TurnDetection] = None + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session_create_params.py new file mode 100644 index 0000000..5573ab2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session_create_params.py @@ -0,0 +1,171 @@ +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +__all__ = [ + "TranscriptionSessionCreateParams", + "ClientSecret", + "ClientSecretExpiresAt", + "InputAudioNoiseReduction", + "InputAudioTranscription", + "TurnDetection", +] + + +class TranscriptionSessionCreateParams(TypedDict, total=False): + client_secret: ClientSecret + """Configuration options for the generated client secret.""" + + include: List[str] + """The set of items to include in the transcription. Current available items are: + + - `item.input_audio_transcription.logprobs` + """ + + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: InputAudioNoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: InputAudioTranscription + """Configuration for input audio transcription. + + The client can optionally set the language and prompt for transcription, these + offer additional guidance to the transcription service. + """ + + modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + turn_detection: TurnDetection + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ + + +class ClientSecretExpiresAt(TypedDict, total=False): + anchor: Literal["created_at"] + """The anchor point for the ephemeral token expiration. + + Only `created_at` is currently supported. + """ + + seconds: int + """The number of seconds from the anchor point to the expiration. + + Select a value between `10` and `7200`. + """ + + +class ClientSecret(TypedDict, total=False): + expires_at: ClientSecretExpiresAt + """Configuration for the ephemeral token expiration.""" + + +class InputAudioNoiseReduction(TypedDict, total=False): + type: Literal["near_field", "far_field"] + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class InputAudioTranscription(TypedDict, total=False): + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"] + """ + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: str + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class TurnDetection(TypedDict, total=False): + create_response: bool + """Whether or not to automatically generate a response when a VAD stop event + occurs. + + Not available for transcription sessions. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. Not available for transcription sessions. + """ + + prefix_padding_ms: int + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: int + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: float + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Literal["server_vad", "semantic_vad"] + """Type of turn detection.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session_update.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session_update.py new file mode 100644 index 0000000..3fe3033 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session_update.py @@ -0,0 +1,183 @@ +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = [ + "TranscriptionSessionUpdate", + "Session", + "SessionClientSecret", + "SessionClientSecretExpiresAt", + "SessionInputAudioNoiseReduction", + "SessionInputAudioTranscription", + "SessionTurnDetection", +] + + +class SessionClientSecretExpiresAt(BaseModel): + anchor: Optional[Literal["created_at"]] = None + """The anchor point for the ephemeral token expiration. + + Only `created_at` is currently supported. + """ + + seconds: Optional[int] = None + """The number of seconds from the anchor point to the expiration. + + Select a value between `10` and `7200`. + """ + + +class SessionClientSecret(BaseModel): + expires_at: Optional[SessionClientSecretExpiresAt] = None + """Configuration for the ephemeral token expiration.""" + + +class SessionInputAudioNoiseReduction(BaseModel): + type: Optional[Literal["near_field", "far_field"]] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class SessionInputAudioTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"]] = None + """ + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: Optional[str] = None + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class SessionTurnDetection(BaseModel): + create_response: Optional[bool] = None + """Whether or not to automatically generate a response when a VAD stop event + occurs. + + Not available for transcription sessions. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. Not available for transcription sessions. + """ + + prefix_padding_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: Optional[float] = None + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Optional[Literal["server_vad", "semantic_vad"]] = None + """Type of turn detection.""" + + +class Session(BaseModel): + client_secret: Optional[SessionClientSecret] = None + """Configuration options for the generated client secret.""" + + include: Optional[List[str]] = None + """The set of items to include in the transcription. Current available items are: + + - `item.input_audio_transcription.logprobs` + """ + + input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: Optional[SessionInputAudioNoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: Optional[SessionInputAudioTranscription] = None + """Configuration for input audio transcription. + + The client can optionally set the language and prompt for transcription, these + offer additional guidance to the transcription service. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + turn_detection: Optional[SessionTurnDetection] = None + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ + + +class TranscriptionSessionUpdate(BaseModel): + session: Session + """Realtime transcription session object configuration.""" + + type: Literal["transcription_session.update"] + """The event type, must be `transcription_session.update`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session_update_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session_update_param.py new file mode 100644 index 0000000..aa8719a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session_update_param.py @@ -0,0 +1,183 @@ +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +__all__ = [ + "TranscriptionSessionUpdateParam", + "Session", + "SessionClientSecret", + "SessionClientSecretExpiresAt", + "SessionInputAudioNoiseReduction", + "SessionInputAudioTranscription", + "SessionTurnDetection", +] + + +class SessionClientSecretExpiresAt(TypedDict, total=False): + anchor: Literal["created_at"] + """The anchor point for the ephemeral token expiration. + + Only `created_at` is currently supported. + """ + + seconds: int + """The number of seconds from the anchor point to the expiration. + + Select a value between `10` and `7200`. + """ + + +class SessionClientSecret(TypedDict, total=False): + expires_at: SessionClientSecretExpiresAt + """Configuration for the ephemeral token expiration.""" + + +class SessionInputAudioNoiseReduction(TypedDict, total=False): + type: Literal["near_field", "far_field"] + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class SessionInputAudioTranscription(TypedDict, total=False): + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"] + """ + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: str + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class SessionTurnDetection(TypedDict, total=False): + create_response: bool + """Whether or not to automatically generate a response when a VAD stop event + occurs. + + Not available for transcription sessions. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. Not available for transcription sessions. + """ + + prefix_padding_ms: int + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: int + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: float + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Literal["server_vad", "semantic_vad"] + """Type of turn detection.""" + + +class Session(TypedDict, total=False): + client_secret: SessionClientSecret + """Configuration options for the generated client secret.""" + + include: List[str] + """The set of items to include in the transcription. Current available items are: + + - `item.input_audio_transcription.logprobs` + """ + + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: SessionInputAudioNoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: SessionInputAudioTranscription + """Configuration for input audio transcription. + + The client can optionally set the language and prompt for transcription, these + offer additional guidance to the transcription service. + """ + + modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + turn_detection: SessionTurnDetection + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ + + +class TranscriptionSessionUpdateParam(TypedDict, total=False): + session: Required[Session] + """Realtime transcription session object configuration.""" + + type: Required[Literal["transcription_session.update"]] + """The event type, must be `transcription_session.update`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session_updated_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session_updated_event.py new file mode 100644 index 0000000..5028ed2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/realtime/transcription_session_updated_event.py @@ -0,0 +1,22 @@ +from typing_extensions import Literal + +from ...._models import BaseModel +from .transcription_session import TranscriptionSession + +__all__ = ["TranscriptionSessionUpdatedEvent"] + + +class TranscriptionSessionUpdatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + session: TranscriptionSession + """A new Realtime transcription session configuration. + + When a session is created on the server via REST API, the session object also + contains an ephemeral key. Default TTL for keys is 10 minutes. This property is + not present when a session is updated via the WebSocket API. + """ + + type: Literal["transcription_session.updated"] + """The event type, must be `transcription_session.updated`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/response_compact_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/response_compact_params.py new file mode 100644 index 0000000..583efef --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/response_compact_params.py @@ -0,0 +1,223 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ..._utils import PropertyInfo +from .beta_response_input_item_param import BetaResponseInputItemParam + +__all__ = ["ResponseCompactParams", "PromptCacheOptions"] + + +class ResponseCompactParams(TypedDict, total=False): + model: Required[ + Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + None, + ] + ] + """Model ID used to generate the response, like `gpt-6-astra`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + input: Union[str, Iterable[BetaResponseInputItemParam], None] + """Text, image, or file inputs to the model, used to generate a response""" + + instructions: Optional[str] + """ + A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + """ + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt_cache_key: Optional[str] + """A key to use when reading from or writing to the prompt cache.""" + + prompt_cache_options: Optional[PromptCacheOptions] + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] + """How long to retain a prompt cache entry created by this request.""" + + service_tier: Optional[Literal["auto", "default", "fast", "flex", "priority"]] + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. - If set to 'default', then the request will be processed + with the standard pricing and performance for the selected model. - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. - To + opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) + at the request level, include the `service_tier=fast` or + `service_tier=priority` parameter for Responses or Chat Completions. For + models with a dedicated Fast tier, either value resolves to + `service_tier=fast`; for other models, either value resolves to + `service_tier=priority`. - When not set, the default behavior is 'auto'. When + the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + betas: Annotated[List[Literal["responses_multi_agent=v1"]], PropertyInfo(alias="openai-beta")] + + +class PromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/response_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/response_create_params.py new file mode 100644 index 0000000..4247167 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/response_create_params.py @@ -0,0 +1,688 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from ..._utils import PropertyInfo +from .beta_tool_param import BetaToolParam +from .beta_service_tier import BetaServiceTier +from .beta_response_includable import BetaResponseIncludable +from .beta_tool_choice_options import BetaToolChoiceOptions +from .beta_response_input_param import BetaResponseInputParam +from .beta_response_prompt_param import BetaResponsePromptParam +from .beta_tool_choice_mcp_param import BetaToolChoiceMcpParam +from .beta_tool_choice_shell_param import BetaToolChoiceShellParam +from .beta_tool_choice_types_param import BetaToolChoiceTypesParam +from .beta_tool_choice_custom_param import BetaToolChoiceCustomParam +from .beta_tool_choice_allowed_param import BetaToolChoiceAllowedParam +from .beta_response_text_config_param import BetaResponseTextConfigParam +from .beta_tool_choice_function_param import BetaToolChoiceFunctionParam +from .beta_tool_choice_apply_patch_param import BetaToolChoiceApplyPatchParam +from .beta_response_conversation_param_param import BetaResponseConversationParamParam + +__all__ = [ + "ResponseCreateParamsBase", + "AccessPrograms", + "ContextManagement", + "Conversation", + "Moderation", + "ModerationPolicy", + "ModerationPolicyInput", + "ModerationPolicyOutput", + "MultiAgent", + "PromptCacheOptions", + "Reasoning", + "StreamOptions", + "ToolChoice", + "ToolChoiceBetaSpecificProgrammaticToolCallingParam", + "ResponseCreateParamsNonStreaming", + "ResponseCreateParamsStreaming", +] + + +class ResponseCreateParamsBase(TypedDict, total=False): + access_programs: AccessPrograms + """Domain-specific access programs to use for this request.""" + + background: Optional[bool] + """ + Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + """ + + context_management: Optional[Iterable[ContextManagement]] + """Context management configuration for this request.""" + + conversation: Optional[Conversation] + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + include: Optional[List[BetaResponseIncludable]] + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + """ + + input: Union[str, BetaResponseInputParam] + """Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + """ + + instructions: Optional[str] + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + max_output_tokens: Optional[int] + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + metadata: Optional[Dict[str, str]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + ] + """Model ID used to generate the response, like `gpt-6-astra`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + moderation: Optional[Moderation] + """Configuration for running moderation on the input and output of this response.""" + + multi_agent: Optional[MultiAgent] + """Configuration for server-hosted multi-agent execution.""" + + parallel_tool_calls: Optional[bool] + """Whether to allow the model to run tool calls in parallel.""" + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[BetaResponsePromptParam] + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + prompt_cache_key: Optional[str] + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + """ + + prompt_cache_options: PromptCacheOptions + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] + """Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + """ + + reasoning: Optional[Reasoning] + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + safety_identifier: Optional[str] + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + service_tier: Optional[BetaServiceTier] + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + store: Optional[bool] + """Whether to store the generated model response for later retrieval via API. + + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + """ + + stream_options: Optional[StreamOptions] + """Options for streaming responses. Only set this when you set `stream: true`.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + text: BetaResponseTextConfigParam + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + tool_choice: ToolChoice + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: Iterable[BetaToolParam] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_logprobs: Optional[int] + """ + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + truncation: Optional[Literal["auto", "disabled"]] + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + user: str + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + betas: Annotated[List[Literal["responses_multi_agent=v1"]], PropertyInfo(alias="openai-beta")] + + +class AccessPrograms(TypedDict, total=False): + """Domain-specific access programs to use for this request.""" + + cyber: Literal["standard", "daybreak_blue", "daybreak_red"] + """The Cyber access program to use for this request. + + Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If + omitted, the API resolves the program from the model's Cyber tier and your + organization and project access, subject to model-specific eligibility + restrictions. By default, models without a Cyber tier use Standard. Blue-tier + models use Daybreak Blue when authorized; otherwise they fall back to Standard + unless the model requires Daybreak access. Red-tier models use Daybreak Red and + require authorization. Requests that require unavailable Daybreak access + return 403. An implicit Standard fallback is represented by null in the + response's access_programs field, rather than an explicit Standard selection. + """ + + +class ContextManagement(TypedDict, total=False): + type: Required[str] + """The context management entry type. Currently only 'compaction' is supported.""" + + compact_threshold: Optional[int] + """Token threshold at which compaction should be triggered for this entry.""" + + +Conversation: TypeAlias = Union[str, BetaResponseConversationParamParam] + + +class ModerationPolicyInput(TypedDict, total=False): + """The moderation policy for the response input.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicyOutput(TypedDict, total=False): + """The moderation policy for the response output.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicy(TypedDict, total=False): + """The policy to apply to moderated response input and output.""" + + input: Optional[ModerationPolicyInput] + """The moderation policy for the response input.""" + + output: Optional[ModerationPolicyOutput] + """The moderation policy for the response output.""" + + +class Moderation(TypedDict, total=False): + """Configuration for running moderation on the input and output of this response.""" + + model: Required[str] + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + policy: Optional[ModerationPolicy] + """The policy to apply to moderated response input and output.""" + + +class MultiAgent(TypedDict, total=False): + """Configuration for server-hosted multi-agent execution.""" + + enabled: Required[bool] + """Whether to enable server-hosted multi-agent execution for this response.""" + + max_concurrent_subagents: int + """ + `max_concurrent_subagents` sets the maximum number of subagents that can be + active simultaneously across the entire agent tree. It includes all + descendants—children, grandchildren, and deeper subagents—but excludes the root + agent. The API does not impose a fixed upper bound on this setting. The default + is `3`, which is recommended for most workloads. Multi-agent runs also have no + fixed limit on tree depth or the total number of subagents created during a run. + """ + + +class PromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details. + """ + + comparison_response_id: Optional[str] + """The ID of a response to compare when diagnosing prompt cache reuse. + + Supplying this field requests prompt cache diagnostics when the feature is + enabled. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + prewarm: bool + """Prepares the prompt cache without generating output. + + Defaults to `false`. When set to `true`, overrides the `generate` field to + `false`. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + + +class Reasoning(TypedDict, total=False): + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + context: Optional[Literal["auto", "current_turn", "all_turns"]] + """ + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + generate_summary: Optional[Literal["auto", "concise", "detailed"]] + """**Deprecated:** use `summary` instead. + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + mode: Union[str, Literal["standard", "pro"]] + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. + + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. + """ + + +class StreamOptions(TypedDict, total=False): + """Options for streaming responses. Only set this when you set `stream: true`.""" + + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + +class ToolChoiceBetaSpecificProgrammaticToolCallingParam(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ToolChoice: TypeAlias = Union[ + BetaToolChoiceOptions, + BetaToolChoiceAllowedParam, + BetaToolChoiceTypesParam, + BetaToolChoiceFunctionParam, + BetaToolChoiceMcpParam, + BetaToolChoiceCustomParam, + ToolChoiceBetaSpecificProgrammaticToolCallingParam, + BetaToolChoiceApplyPatchParam, + BetaToolChoiceShellParam, +] + + +class ResponseCreateParamsNonStreaming(ResponseCreateParamsBase, total=False): + stream: Optional[Literal[False]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + """ + + +class ResponseCreateParamsStreaming(ResponseCreateParamsBase): + stream: Required[Literal[True]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + """ + + +ResponseCreateParams = Union[ResponseCreateParamsNonStreaming, ResponseCreateParamsStreaming] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/response_retrieve_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/response_retrieve_params.py new file mode 100644 index 0000000..bddae0f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/response_retrieve_params.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ..._utils import PropertyInfo +from .beta_response_includable import BetaResponseIncludable + +__all__ = ["ResponseRetrieveParamsBase", "ResponseRetrieveParamsNonStreaming", "ResponseRetrieveParamsStreaming"] + + +class ResponseRetrieveParamsBase(TypedDict, total=False): + include: List[BetaResponseIncludable] + """Additional fields to include in the response. + + See the `include` parameter for Response creation above for more information. + """ + + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + starting_after: int + """The sequence number of the event after which to start streaming.""" + + betas: Annotated[List[Literal["responses_multi_agent=v1"]], PropertyInfo(alias="openai-beta")] + + +class ResponseRetrieveParamsNonStreaming(ResponseRetrieveParamsBase, total=False): + stream: Literal[False] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + """ + + +class ResponseRetrieveParamsStreaming(ResponseRetrieveParamsBase): + stream: Required[Literal[True]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + """ + + +ResponseRetrieveParams = Union[ResponseRetrieveParamsNonStreaming, ResponseRetrieveParamsStreaming] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/responses/__init__.py b/venv/lib/python3.12/site-packages/openai/types/beta/responses/__init__.py new file mode 100644 index 0000000..ffc555c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/responses/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .input_item_list_params import InputItemListParams as InputItemListParams +from .beta_response_item_list import BetaResponseItemList as BetaResponseItemList +from .input_token_count_params import InputTokenCountParams as InputTokenCountParams +from .input_token_count_response import InputTokenCountResponse as InputTokenCountResponse diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b833f34 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/beta_response_item_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/beta_response_item_list.cpython-312.pyc new file mode 100644 index 0000000..ae101a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/beta_response_item_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/input_item_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/input_item_list_params.cpython-312.pyc new file mode 100644 index 0000000..64a0351 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/input_item_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/input_token_count_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/input_token_count_params.cpython-312.pyc new file mode 100644 index 0000000..15325d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/input_token_count_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/input_token_count_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/input_token_count_response.cpython-312.pyc new file mode 100644 index 0000000..313b8b1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/responses/__pycache__/input_token_count_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/responses/beta_response_item_list.py b/venv/lib/python3.12/site-packages/openai/types/beta/responses/beta_response_item_list.py new file mode 100644 index 0000000..b62baab --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/responses/beta_response_item_list.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ...._models import BaseModel +from ..beta_response_item import BetaResponseItem + +__all__ = ["BetaResponseItemList"] + + +class BetaResponseItemList(BaseModel): + """A list of Response items.""" + + data: List[BetaResponseItem] + """A list of items used to generate this response.""" + + first_id: str + """The ID of the first item in the list.""" + + has_more: bool + """Whether there are more items available.""" + + last_id: str + """The ID of the last item in the list.""" + + object: Literal["list"] + """The type of object returned, must be `list`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/responses/input_item_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/responses/input_item_list_params.py new file mode 100644 index 0000000..ec3da95 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/responses/input_item_list_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Annotated, TypedDict + +from ...._utils import PropertyInfo +from ..beta_response_includable import BetaResponseIncludable + +__all__ = ["InputItemListParams"] + + +class InputItemListParams(TypedDict, total=False): + after: str + """An item ID to list items after, used in pagination.""" + + include: List[BetaResponseIncludable] + """Additional fields to include in the response. + + See the `include` parameter for Response creation above for more information. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + """ + + betas: Annotated[List[Literal["responses_multi_agent=v1"]], PropertyInfo(alias="openai-beta")] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/responses/input_token_count_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/responses/input_token_count_params.py new file mode 100644 index 0000000..e4c2d3f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/responses/input_token_count_params.py @@ -0,0 +1,219 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from ...._utils import PropertyInfo +from ..beta_tool_param import BetaToolParam +from ..beta_tool_choice_options import BetaToolChoiceOptions +from ..beta_tool_choice_mcp_param import BetaToolChoiceMcpParam +from ..beta_tool_choice_shell_param import BetaToolChoiceShellParam +from ..beta_tool_choice_types_param import BetaToolChoiceTypesParam +from ..beta_tool_choice_custom_param import BetaToolChoiceCustomParam +from ..beta_response_input_item_param import BetaResponseInputItemParam +from ..beta_tool_choice_allowed_param import BetaToolChoiceAllowedParam +from ..beta_tool_choice_function_param import BetaToolChoiceFunctionParam +from ..beta_tool_choice_apply_patch_param import BetaToolChoiceApplyPatchParam +from ..beta_response_conversation_param_param import BetaResponseConversationParamParam +from ..beta_response_format_text_config_param import BetaResponseFormatTextConfigParam + +__all__ = [ + "InputTokenCountParams", + "Conversation", + "Reasoning", + "Text", + "ToolChoice", + "ToolChoiceBetaSpecificProgrammaticToolCallingParam", +] + + +class InputTokenCountParams(TypedDict, total=False): + conversation: Optional[Conversation] + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + input: Union[str, Iterable[BetaResponseInputItemParam], None] + """Text, image, or file inputs to the model, used to generate a response""" + + instructions: Optional[str] + """ + A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + """ + + model: Optional[str] + """Model ID used to generate the response, like `gpt-4o` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + parallel_tool_calls: Optional[bool] + """Whether to allow the model to run tool calls in parallel.""" + + personality: Union[str, Literal["friendly", "pragmatic"]] + """A model-owned style preset to apply to this request. + + Omit this parameter to use the model's default style. Supported values may + expand over time. Values must be at most 64 characters. + """ + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + reasoning: Optional[Reasoning] + """ + **gpt-5 and o-series models only** Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + text: Optional[Text] + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + tool_choice: Optional[ToolChoice] + """Controls which tool the model should use, if any.""" + + tools: Optional[Iterable[BetaToolParam]] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + """ + + truncation: Literal["auto", "disabled"] + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. - `disabled` (default): If the + input size will exceed the context window size for a model, the request will + fail with a 400 error. + """ + + betas: Annotated[List[Literal["responses_multi_agent=v1"]], PropertyInfo(alias="openai-beta")] + + +Conversation: TypeAlias = Union[str, BetaResponseConversationParamParam] + + +class Reasoning(TypedDict, total=False): + """ + **gpt-5 and o-series models only** Configuration options for [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + context: Optional[Literal["auto", "current_turn", "all_turns"]] + """ + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + generate_summary: Optional[Literal["auto", "concise", "detailed"]] + """**Deprecated:** use `summary` instead. + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + mode: Union[str, Literal["standard", "pro"]] + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. + + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. + """ + + +class Text(TypedDict, total=False): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + format: BetaResponseFormatTextConfigParam + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. The default is `medium`. + """ + + +class ToolChoiceBetaSpecificProgrammaticToolCallingParam(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ToolChoice: TypeAlias = Union[ + BetaToolChoiceOptions, + BetaToolChoiceAllowedParam, + BetaToolChoiceTypesParam, + BetaToolChoiceFunctionParam, + BetaToolChoiceMcpParam, + BetaToolChoiceCustomParam, + ToolChoiceBetaSpecificProgrammaticToolCallingParam, + BetaToolChoiceApplyPatchParam, + BetaToolChoiceShellParam, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/responses/input_token_count_response.py b/venv/lib/python3.12/site-packages/openai/types/beta/responses/input_token_count_response.py new file mode 100644 index 0000000..d21f197 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/responses/input_token_count_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputTokenCountResponse"] + + +class InputTokenCountResponse(BaseModel): + input_tokens: int + + object: Literal["response.input_tokens"] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/session_error.py b/venv/lib/python3.12/site-packages/openai/types/beta/session_error.py new file mode 100644 index 0000000..f4d963c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/session_error.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["SessionError"] + + +class SessionError(BaseModel): + """An error payload with the same public fields as Responses API streaming errors.""" + + code: Optional[str] = None + """The machine-readable error code, if any.""" + + message: str + """A customer-safe explanation of the error.""" + + param: Optional[str] = None + """The request parameter associated with the error, if any.""" + + type: str + """The error type.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/session_turn_error.py b/venv/lib/python3.12/site-packages/openai/types/beta/session_turn_error.py new file mode 100644 index 0000000..927d78f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/session_turn_error.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SessionTurnError"] + + +class SessionTurnError(BaseModel): + """A customer-safe error describing why a session request failed.""" + + code: Literal[ + "context_length_exceeded", + "session_budget_exceeded", + "usage_limit_exceeded", + "credit_balance_exhausted", + "rate_limit_exceeded", + "server_overloaded", + "cyber_policy", + "misalignment_policy_violation", + "connection_failed", + "server_error", + "authentication_error", + "invalid_request", + "resource_not_found", + "sandbox_error", + "executor_version_incompatible", + "active_turn_not_steerable", + "request_timeout", + "internal_error", + ] + """A stable, machine-readable failure category. + + - `context_length_exceeded` - The request exceeds the model's context window. + - `session_budget_exceeded` - The session has reached its usage budget. + - `usage_limit_exceeded` - The organization has reached a usage, plan, or + billing limit. + - `credit_balance_exhausted` - The organization has no API credits remaining. + - `rate_limit_exceeded` - The request exceeds the available rate limit. + - `server_overloaded` - The model service is temporarily overloaded. + - `cyber_policy` - The request was rejected by a safety policy. + - `misalignment_policy_violation` - The request was blocked by the safety + systems. + - `connection_failed` - The request could not connect to the model service. + - `server_error` - The model service encountered an unexpected error. + - `authentication_error` - The API credentials are invalid or lack the required + access. + - `invalid_request` - The request contains invalid input or configuration. + - `resource_not_found` - The requested model or resource is unavailable. + - `sandbox_error` - The request could not complete in its execution environment. + - `executor_version_incompatible` - The executor must be upgraded before it can + run this turn. + - `active_turn_not_steerable` - The session cannot accept additional input while + a request is running. + - `request_timeout` - The request timed out before the model service responded. + - `internal_error` - An unexpected internal error prevented the session request + from completing. + """ + + message: str + """A customer-safe explanation of the failure.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/setup_command_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/setup_command_param.py new file mode 100644 index 0000000..790accf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/setup_command_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["SetupCommandParam"] + + +class SetupCommandParam(TypedDict, total=False): + """A confidential setup command executed before the hosted agent starts.""" + + command: Required[str] + """The shell command to execute.""" + + cwd: Optional[str] + """The absolute working directory. Defaults to `/workspace`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/subagent.py b/venv/lib/python3.12/site-packages/openai/types/beta/subagent.py new file mode 100644 index 0000000..938f727 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/subagent.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .agent_content import AgentContent + +__all__ = ["Subagent"] + + +class Subagent(BaseModel): + """A subagent created within a session.""" + + id: str + """The ID of the subagent.""" + + closed_at: Optional[int] = None + """The Unix timestamp, in seconds, when the subagent was closed. + + Null while active, including after resume. + """ + + instructions: Optional[List[AgentContent]] = None + """Initial task content, or null when unavailable. + + Text may contain placeholders for images or audio when only a preview is + available. + """ + + name: Optional[str] = None + """The runner-assigned nickname, or null when unavailable.""" + + object: Literal["agent.session.subagent"] + """The object type. Always `agent.session.subagent`.""" + + opened_at: int + """The Unix timestamp, in seconds, when the subagent was first opened. + + Resuming does not change it. + """ + + parent_agent_id: str + """The ID of the agent that created this subagent.""" + + session_id: str + """The ID of the session that owns the subagent.""" + + status: Literal["active", "closed"] + """The current status of the subagent. + + - `active` - The subagent remains available, including while idle between turns. + - `closed` - The subagent is closed. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/summary_text.py b/venv/lib/python3.12/site-packages/openai/types/beta/summary_text.py new file mode 100644 index 0000000..40fd61d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/summary_text.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SummaryText"] + + +class SummaryText(BaseModel): + """A reasoning summary content part.""" + + text: str + """The reasoning summary text.""" + + type: Literal["summary_text"] + """The content type. Always `summary_text`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/text_format.py b/venv/lib/python3.12/site-packages/openai/types/beta/text_format.py new file mode 100644 index 0000000..7f8e27d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/text_format.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["TextFormat", "TextFormatResourceText", "TextFormatResourceJSONSchema"] + + +class TextFormatResourceText(BaseModel): + """Generates ordinary text without a structured-output constraint.""" + + type: Literal["text"] + """The type of the object. Always `text`.""" + + +class TextFormatResourceJSONSchema(BaseModel): + """Constrains generated text to a JSON Schema.""" + + schema_: Dict[str, object] = FieldInfo(alias="schema") + """The JSON Schema that generated text must match.""" + + type: Literal["json_schema"] + """The type of the object. Always `json_schema`.""" + + +TextFormat: TypeAlias = Annotated[ + Union[TextFormatResourceText, TextFormatResourceJSONSchema], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/text_format_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/text_format_param.py new file mode 100644 index 0000000..fdb23ce --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/text_format_param.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["TextFormatParam", "TextFormatParamText", "TextFormatParamJSONSchema"] + + +class TextFormatParamText(TypedDict, total=False): + """Generates ordinary text without a structured-output constraint.""" + + type: Required[Literal["text"]] + """The type of the object. Always `text`.""" + + +class TextFormatParamJSONSchema(TypedDict, total=False): + """Constrains generated text to a JSON Schema.""" + + schema: Required[Dict[str, object]] + """The JSON Schema that generated text must match.""" + + type: Required[Literal["json_schema"]] + """The type of the object. Always `json_schema`.""" + + +TextFormatParam: TypeAlias = Union[TextFormatParamText, TextFormatParamJSONSchema] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/thread.py b/venv/lib/python3.12/site-packages/openai/types/beta/thread.py new file mode 100644 index 0000000..9a0bbc8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/thread.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from ..shared.metadata import Metadata + +__all__ = ["Thread", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] + + +class ToolResourcesCodeInterpreter(BaseModel): + file_ids: Optional[List[str]] = None + """ + A list of [file](https://developers.openai.com/api/reference/resources/files) + IDs made available to the `code_interpreter` tool. There can be a maximum of 20 + files associated with the tool. + """ + + +class ToolResourcesFileSearch(BaseModel): + vector_store_ids: Optional[List[str]] = None + """ + The + [vector store](https://developers.openai.com/api/reference/resources/vector_stores) + attached to this thread. There can be a maximum of 1 vector store attached to + the thread. + """ + + +class ToolResources(BaseModel): + """ + A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + + code_interpreter: Optional[ToolResourcesCodeInterpreter] = None + + file_search: Optional[ToolResourcesFileSearch] = None + + +class Thread(BaseModel): + """ + Represents a thread that contains [messages](https://developers.openai.com/api/docs/assistants/migration). + """ + + id: str + """The identifier, which can be referenced in API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the thread was created.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + object: Literal["thread"] + """The object type, which is always `thread`.""" + + tool_resources: Optional[ToolResources] = None + """ + A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/thread_create_and_run_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/thread_create_and_run_params.py new file mode 100644 index 0000000..dbdc17e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/thread_create_and_run_params.py @@ -0,0 +1,425 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from ..shared.chat_model import ChatModel +from .assistant_tool_param import AssistantToolParam +from ..shared_params.metadata import Metadata +from .code_interpreter_tool_param import CodeInterpreterToolParam +from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam +from .threads.message_content_part_param import MessageContentPartParam +from .assistant_response_format_option_param import AssistantResponseFormatOptionParam + +__all__ = [ + "ThreadCreateAndRunParamsBase", + "Thread", + "ThreadMessage", + "ThreadMessageAttachment", + "ThreadMessageAttachmentTool", + "ThreadMessageAttachmentToolFileSearch", + "ThreadToolResources", + "ThreadToolResourcesCodeInterpreter", + "ThreadToolResourcesFileSearch", + "ThreadToolResourcesFileSearchVectorStore", + "ThreadToolResourcesFileSearchVectorStoreChunkingStrategy", + "ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto", + "ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic", + "ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic", + "ToolResources", + "ToolResourcesCodeInterpreter", + "ToolResourcesFileSearch", + "TruncationStrategy", + "ThreadCreateAndRunParamsNonStreaming", + "ThreadCreateAndRunParamsStreaming", +] + + +class ThreadCreateAndRunParamsBase(TypedDict, total=False): + assistant_id: Required[str] + """ + The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + """ + + instructions: Optional[str] + """Override the default system message of the assistant. + + This is useful for modifying the behavior on a per-run basis. + """ + + max_completion_tokens: Optional[int] + """ + The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + """ + + max_prompt_tokens: Optional[int] + """The maximum number of prompt tokens that may be used over the course of the run. + + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Union[str, ChatModel, None] + """ + The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + """ + + parallel_tool_calls: bool + """ + Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + """ + + response_format: Optional[AssistantResponseFormatOptionParam] + """Specifies the format that the model must output. + + Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + """ + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + """ + + thread: Thread + """Options to create a new thread. + + If no thread is provided when running a request, an empty thread will be + created. + """ + + tool_choice: Optional[AssistantToolChoiceOptionParam] + """ + Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + """ + + tool_resources: Optional[ToolResources] + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ + + tools: Optional[Iterable[AssistantToolParam]] + """Override the tools the assistant can use for this run. + + This is useful for modifying the behavior on a per-run basis. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + """ + + truncation_strategy: Optional[TruncationStrategy] + """Controls for how a thread will be truncated prior to the run. + + Use this to control the initial context window of the run. + """ + + +class ThreadMessageAttachmentToolFileSearch(TypedDict, total=False): + type: Required[Literal["file_search"]] + """The type of tool being defined: `file_search`""" + + +ThreadMessageAttachmentTool: TypeAlias = Union[CodeInterpreterToolParam, ThreadMessageAttachmentToolFileSearch] + + +class ThreadMessageAttachment(TypedDict, total=False): + file_id: str + """The ID of the file to attach to the message.""" + + tools: Iterable[ThreadMessageAttachmentTool] + """The tools to add this file to.""" + + +class ThreadMessage(TypedDict, total=False): + content: Required[Union[str, Iterable[MessageContentPartParam]]] + """The text contents of the message.""" + + role: Required[Literal["user", "assistant"]] + """The role of the entity that is creating the message. Allowed values include: + + - `user`: Indicates the message is sent by an actual user and should be used in + most cases to represent user-generated messages. + - `assistant`: Indicates the message is generated by the assistant. Use this + value to insert messages from the assistant into the conversation. + """ + + attachments: Optional[Iterable[ThreadMessageAttachment]] + """A list of files attached to the message, and the tools they should be added to.""" + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + +class ThreadToolResourcesCodeInterpreter(TypedDict, total=False): + file_ids: SequenceNotStr[str] + """ + A list of [file](https://developers.openai.com/api/reference/resources/files) + IDs made available to the `code_interpreter` tool. There can be a maximum of 20 + files associated with the tool. + """ + + +class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): + """The default strategy. + + This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`. + """ + + type: Required[Literal["auto"]] + """Always `auto`.""" + + +class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ + + +class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total=False): + static: Required[ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic] + + type: Required[Literal["static"]] + """Always `static`.""" + + +ThreadToolResourcesFileSearchVectorStoreChunkingStrategy: TypeAlias = Union[ + ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto, + ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic, +] + + +class ThreadToolResourcesFileSearchVectorStore(TypedDict, total=False): + chunking_strategy: ThreadToolResourcesFileSearchVectorStoreChunkingStrategy + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. + """ + + file_ids: SequenceNotStr[str] + """ + A list of [file](https://developers.openai.com/api/reference/resources/files) + IDs to add to the vector store. For vector stores created before Nov 2025, there + can be a maximum of 10,000 files in a vector store. For vector stores created + starting in Nov 2025, the limit is 100,000,000 files. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + +class ThreadToolResourcesFileSearch(TypedDict, total=False): + vector_store_ids: SequenceNotStr[str] + """ + The + [vector store](https://developers.openai.com/api/reference/resources/vector_stores) + attached to this thread. There can be a maximum of 1 vector store attached to + the thread. + """ + + vector_stores: Iterable[ThreadToolResourcesFileSearchVectorStore] + """ + A helper to create a + [vector store](https://developers.openai.com/api/reference/resources/vector_stores) + with file_ids and attach it to this thread. There can be a maximum of 1 vector + store attached to the thread. + """ + + +class ThreadToolResources(TypedDict, total=False): + """ + A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + + code_interpreter: ThreadToolResourcesCodeInterpreter + + file_search: ThreadToolResourcesFileSearch + + +class Thread(TypedDict, total=False): + """Options to create a new thread. + + If no thread is provided when running a + request, an empty thread will be created. + """ + + messages: Iterable[ThreadMessage] + """ + A list of + [messages](https://developers.openai.com/api/docs/assistants/migration) to start + the thread with. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + tool_resources: Optional[ThreadToolResources] + """ + A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ + + +class ToolResourcesCodeInterpreter(TypedDict, total=False): + file_ids: SequenceNotStr[str] + """ + A list of [file](https://developers.openai.com/api/reference/resources/files) + IDs made available to the `code_interpreter` tool. There can be a maximum of 20 + files associated with the tool. + """ + + +class ToolResourcesFileSearch(TypedDict, total=False): + vector_store_ids: SequenceNotStr[str] + """ + The ID of the + [vector store](https://developers.openai.com/api/reference/resources/vector_stores) + attached to this assistant. There can be a maximum of 1 vector store attached to + the assistant. + """ + + +class ToolResources(TypedDict, total=False): + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + + code_interpreter: ToolResourcesCodeInterpreter + + file_search: ToolResourcesFileSearch + + +class TruncationStrategy(TypedDict, total=False): + """Controls for how a thread will be truncated prior to the run. + + Use this to control the initial context window of the run. + """ + + type: Required[Literal["auto", "last_messages"]] + """The truncation strategy to use for the thread. + + The default is `auto`. If set to `last_messages`, the thread will be truncated + to the n most recent messages in the thread. When set to `auto`, messages in the + middle of the thread will be dropped to fit the context length of the model, + `max_prompt_tokens`. + """ + + last_messages: Optional[int] + """ + The number of most recent messages from the thread when constructing the context + for the run. + """ + + +class ThreadCreateAndRunParamsNonStreaming(ThreadCreateAndRunParamsBase, total=False): + stream: Optional[Literal[False]] + """ + If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + """ + + +class ThreadCreateAndRunParamsStreaming(ThreadCreateAndRunParamsBase): + stream: Required[Literal[True]] + """ + If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + """ + + +ThreadCreateAndRunParams = Union[ThreadCreateAndRunParamsNonStreaming, ThreadCreateAndRunParamsStreaming] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/thread_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/thread_create_params.py new file mode 100644 index 0000000..4524d3a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/thread_create_params.py @@ -0,0 +1,197 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from ..shared_params.metadata import Metadata +from .code_interpreter_tool_param import CodeInterpreterToolParam +from .threads.message_content_part_param import MessageContentPartParam + +__all__ = [ + "ThreadCreateParams", + "Message", + "MessageAttachment", + "MessageAttachmentTool", + "MessageAttachmentToolFileSearch", + "ToolResources", + "ToolResourcesCodeInterpreter", + "ToolResourcesFileSearch", + "ToolResourcesFileSearchVectorStore", + "ToolResourcesFileSearchVectorStoreChunkingStrategy", + "ToolResourcesFileSearchVectorStoreChunkingStrategyAuto", + "ToolResourcesFileSearchVectorStoreChunkingStrategyStatic", + "ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic", +] + + +class ThreadCreateParams(TypedDict, total=False): + messages: Iterable[Message] + """ + A list of + [messages](https://developers.openai.com/api/docs/assistants/migration) to start + the thread with. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + tool_resources: Optional[ToolResources] + """ + A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ + + +class MessageAttachmentToolFileSearch(TypedDict, total=False): + type: Required[Literal["file_search"]] + """The type of tool being defined: `file_search`""" + + +MessageAttachmentTool: TypeAlias = Union[CodeInterpreterToolParam, MessageAttachmentToolFileSearch] + + +class MessageAttachment(TypedDict, total=False): + file_id: str + """The ID of the file to attach to the message.""" + + tools: Iterable[MessageAttachmentTool] + """The tools to add this file to.""" + + +class Message(TypedDict, total=False): + content: Required[Union[str, Iterable[MessageContentPartParam]]] + """The text contents of the message.""" + + role: Required[Literal["user", "assistant"]] + """The role of the entity that is creating the message. Allowed values include: + + - `user`: Indicates the message is sent by an actual user and should be used in + most cases to represent user-generated messages. + - `assistant`: Indicates the message is generated by the assistant. Use this + value to insert messages from the assistant into the conversation. + """ + + attachments: Optional[Iterable[MessageAttachment]] + """A list of files attached to the message, and the tools they should be added to.""" + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + +class ToolResourcesCodeInterpreter(TypedDict, total=False): + file_ids: SequenceNotStr[str] + """ + A list of [file](https://developers.openai.com/api/reference/resources/files) + IDs made available to the `code_interpreter` tool. There can be a maximum of 20 + files associated with the tool. + """ + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): + """The default strategy. + + This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`. + """ + + type: Required[Literal["auto"]] + """Always `auto`.""" + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total=False): + static: Required[ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic] + + type: Required[Literal["static"]] + """Always `static`.""" + + +ToolResourcesFileSearchVectorStoreChunkingStrategy: TypeAlias = Union[ + ToolResourcesFileSearchVectorStoreChunkingStrategyAuto, ToolResourcesFileSearchVectorStoreChunkingStrategyStatic +] + + +class ToolResourcesFileSearchVectorStore(TypedDict, total=False): + chunking_strategy: ToolResourcesFileSearchVectorStoreChunkingStrategy + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. + """ + + file_ids: SequenceNotStr[str] + """ + A list of [file](https://developers.openai.com/api/reference/resources/files) + IDs to add to the vector store. For vector stores created before Nov 2025, there + can be a maximum of 10,000 files in a vector store. For vector stores created + starting in Nov 2025, the limit is 100,000,000 files. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + +class ToolResourcesFileSearch(TypedDict, total=False): + vector_store_ids: SequenceNotStr[str] + """ + The + [vector store](https://developers.openai.com/api/reference/resources/vector_stores) + attached to this thread. There can be a maximum of 1 vector store attached to + the thread. + """ + + vector_stores: Iterable[ToolResourcesFileSearchVectorStore] + """ + A helper to create a + [vector store](https://developers.openai.com/api/reference/resources/vector_stores) + with file_ids and attach it to this thread. There can be a maximum of 1 vector + store attached to the thread. + """ + + +class ToolResources(TypedDict, total=False): + """ + A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + + code_interpreter: ToolResourcesCodeInterpreter + + file_search: ToolResourcesFileSearch diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/thread_deleted.py b/venv/lib/python3.12/site-packages/openai/types/beta/thread_deleted.py new file mode 100644 index 0000000..e864c90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/thread_deleted.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ThreadDeleted"] + + +class ThreadDeleted(BaseModel): + id: str + + deleted: bool + + object: Literal["thread.deleted"] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/thread_update_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/thread_update_params.py new file mode 100644 index 0000000..d4db589 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/thread_update_params.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +from ..._types import SequenceNotStr +from ..shared_params.metadata import Metadata + +__all__ = ["ThreadUpdateParams", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] + + +class ThreadUpdateParams(TypedDict, total=False): + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + tool_resources: Optional[ToolResources] + """ + A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ + + +class ToolResourcesCodeInterpreter(TypedDict, total=False): + file_ids: SequenceNotStr[str] + """ + A list of [file](https://developers.openai.com/api/reference/resources/files) + IDs made available to the `code_interpreter` tool. There can be a maximum of 20 + files associated with the tool. + """ + + +class ToolResourcesFileSearch(TypedDict, total=False): + vector_store_ids: SequenceNotStr[str] + """ + The + [vector store](https://developers.openai.com/api/reference/resources/vector_stores) + attached to this thread. There can be a maximum of 1 vector store attached to + the thread. + """ + + +class ToolResources(TypedDict, total=False): + """ + A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + + code_interpreter: ToolResourcesCodeInterpreter + + file_search: ToolResourcesFileSearch diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__init__.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__init__.py new file mode 100644 index 0000000..334600a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__init__.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .run import Run as Run +from .text import Text as Text +from .message import Message as Message +from .image_url import ImageURL as ImageURL +from .annotation import Annotation as Annotation +from .image_file import ImageFile as ImageFile +from .run_status import RunStatus as RunStatus +from .text_delta import TextDelta as TextDelta +from .message_delta import MessageDelta as MessageDelta +from .image_url_delta import ImageURLDelta as ImageURLDelta +from .image_url_param import ImageURLParam as ImageURLParam +from .message_content import MessageContent as MessageContent +from .message_deleted import MessageDeleted as MessageDeleted +from .run_list_params import RunListParams as RunListParams +from .annotation_delta import AnnotationDelta as AnnotationDelta +from .image_file_delta import ImageFileDelta as ImageFileDelta +from .image_file_param import ImageFileParam as ImageFileParam +from .text_delta_block import TextDeltaBlock as TextDeltaBlock +from .run_create_params import RunCreateParams as RunCreateParams +from .run_update_params import RunUpdateParams as RunUpdateParams +from .text_content_block import TextContentBlock as TextContentBlock +from .message_delta_event import MessageDeltaEvent as MessageDeltaEvent +from .message_list_params import MessageListParams as MessageListParams +from .refusal_delta_block import RefusalDeltaBlock as RefusalDeltaBlock +from .file_path_annotation import FilePathAnnotation as FilePathAnnotation +from .image_url_delta_block import ImageURLDeltaBlock as ImageURLDeltaBlock +from .message_content_delta import MessageContentDelta as MessageContentDelta +from .message_create_params import MessageCreateParams as MessageCreateParams +from .message_update_params import MessageUpdateParams as MessageUpdateParams +from .refusal_content_block import RefusalContentBlock as RefusalContentBlock +from .image_file_delta_block import ImageFileDeltaBlock as ImageFileDeltaBlock +from .image_url_content_block import ImageURLContentBlock as ImageURLContentBlock +from .file_citation_annotation import FileCitationAnnotation as FileCitationAnnotation +from .image_file_content_block import ImageFileContentBlock as ImageFileContentBlock +from .text_content_block_param import TextContentBlockParam as TextContentBlockParam +from .file_path_delta_annotation import FilePathDeltaAnnotation as FilePathDeltaAnnotation +from .message_content_part_param import MessageContentPartParam as MessageContentPartParam +from .image_url_content_block_param import ImageURLContentBlockParam as ImageURLContentBlockParam +from .file_citation_delta_annotation import FileCitationDeltaAnnotation as FileCitationDeltaAnnotation +from .image_file_content_block_param import ImageFileContentBlockParam as ImageFileContentBlockParam +from .run_submit_tool_outputs_params import RunSubmitToolOutputsParams as RunSubmitToolOutputsParams +from .required_action_function_tool_call import RequiredActionFunctionToolCall as RequiredActionFunctionToolCall diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..cb67b13 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/annotation.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/annotation.cpython-312.pyc new file mode 100644 index 0000000..19f4ec6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/annotation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/annotation_delta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/annotation_delta.cpython-312.pyc new file mode 100644 index 0000000..2445a12 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/annotation_delta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/file_citation_annotation.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/file_citation_annotation.cpython-312.pyc new file mode 100644 index 0000000..242a6da Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/file_citation_annotation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/file_citation_delta_annotation.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/file_citation_delta_annotation.cpython-312.pyc new file mode 100644 index 0000000..af3ff54 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/file_citation_delta_annotation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/file_path_annotation.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/file_path_annotation.cpython-312.pyc new file mode 100644 index 0000000..4880fa9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/file_path_annotation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/file_path_delta_annotation.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/file_path_delta_annotation.cpython-312.pyc new file mode 100644 index 0000000..5d8c11c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/file_path_delta_annotation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file.cpython-312.pyc new file mode 100644 index 0000000..4d8dcf9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_content_block.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_content_block.cpython-312.pyc new file mode 100644 index 0000000..3aebbba Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_content_block.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_content_block_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_content_block_param.cpython-312.pyc new file mode 100644 index 0000000..9288af8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_content_block_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_delta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_delta.cpython-312.pyc new file mode 100644 index 0000000..6f04ef6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_delta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_delta_block.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_delta_block.cpython-312.pyc new file mode 100644 index 0000000..228f2a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_delta_block.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_param.cpython-312.pyc new file mode 100644 index 0000000..5b6f414 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_file_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url.cpython-312.pyc new file mode 100644 index 0000000..bf8b47b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_content_block.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_content_block.cpython-312.pyc new file mode 100644 index 0000000..66f35d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_content_block.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_content_block_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_content_block_param.cpython-312.pyc new file mode 100644 index 0000000..0ba15f3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_content_block_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_delta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_delta.cpython-312.pyc new file mode 100644 index 0000000..6371f45 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_delta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_delta_block.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_delta_block.cpython-312.pyc new file mode 100644 index 0000000..4369cac Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_delta_block.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_param.cpython-312.pyc new file mode 100644 index 0000000..dc5b7de Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/image_url_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message.cpython-312.pyc new file mode 100644 index 0000000..e0730b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_content.cpython-312.pyc new file mode 100644 index 0000000..7b9054f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_content_delta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_content_delta.cpython-312.pyc new file mode 100644 index 0000000..fadcda1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_content_delta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_content_part_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_content_part_param.cpython-312.pyc new file mode 100644 index 0000000..77c2448 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_content_part_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_create_params.cpython-312.pyc new file mode 100644 index 0000000..3a6991e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_deleted.cpython-312.pyc new file mode 100644 index 0000000..5e7ea35 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_delta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_delta.cpython-312.pyc new file mode 100644 index 0000000..f7933ab Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_delta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_delta_event.cpython-312.pyc new file mode 100644 index 0000000..ed83ea6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_list_params.cpython-312.pyc new file mode 100644 index 0000000..7c2e341 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_update_params.cpython-312.pyc new file mode 100644 index 0000000..7940ddd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/message_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/refusal_content_block.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/refusal_content_block.cpython-312.pyc new file mode 100644 index 0000000..94c64cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/refusal_content_block.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/refusal_delta_block.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/refusal_delta_block.cpython-312.pyc new file mode 100644 index 0000000..eabc6e3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/refusal_delta_block.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/required_action_function_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/required_action_function_tool_call.cpython-312.pyc new file mode 100644 index 0000000..2ca7a05 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/required_action_function_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run.cpython-312.pyc new file mode 100644 index 0000000..3950d17 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_create_params.cpython-312.pyc new file mode 100644 index 0000000..d4dd255 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_list_params.cpython-312.pyc new file mode 100644 index 0000000..10861ee Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_status.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_status.cpython-312.pyc new file mode 100644 index 0000000..1f1e2fa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_status.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_submit_tool_outputs_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_submit_tool_outputs_params.cpython-312.pyc new file mode 100644 index 0000000..8f114bf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_submit_tool_outputs_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_update_params.cpython-312.pyc new file mode 100644 index 0000000..0d825f3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/run_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text.cpython-312.pyc new file mode 100644 index 0000000..07e50ee Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text_content_block.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text_content_block.cpython-312.pyc new file mode 100644 index 0000000..6ba9828 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text_content_block.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text_content_block_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text_content_block_param.cpython-312.pyc new file mode 100644 index 0000000..82da3f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text_content_block_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text_delta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text_delta.cpython-312.pyc new file mode 100644 index 0000000..6b35e03 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text_delta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text_delta_block.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text_delta_block.cpython-312.pyc new file mode 100644 index 0000000..196a86a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/__pycache__/text_delta_block.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/annotation.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/annotation.py new file mode 100644 index 0000000..cd57b8b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/annotation.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ...._utils import PropertyInfo +from .file_path_annotation import FilePathAnnotation +from .file_citation_annotation import FileCitationAnnotation + +__all__ = ["Annotation"] + +Annotation: TypeAlias = Annotated[Union[FileCitationAnnotation, FilePathAnnotation], PropertyInfo(discriminator="type")] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/annotation_delta.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/annotation_delta.py new file mode 100644 index 0000000..a30953e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/annotation_delta.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ...._utils import PropertyInfo +from .file_path_delta_annotation import FilePathDeltaAnnotation +from .file_citation_delta_annotation import FileCitationDeltaAnnotation + +__all__ = ["AnnotationDelta"] + +AnnotationDelta: TypeAlias = Annotated[ + Union[FileCitationDeltaAnnotation, FilePathDeltaAnnotation], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/file_citation_annotation.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/file_citation_annotation.py new file mode 100644 index 0000000..68264fa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/file_citation_annotation.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["FileCitationAnnotation", "FileCitation"] + + +class FileCitation(BaseModel): + file_id: str + """The ID of the specific File the citation is from.""" + + +class FileCitationAnnotation(BaseModel): + """ + A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the "file_search" tool to search files. + """ + + end_index: int + + file_citation: FileCitation + + start_index: int + + text: str + """The text in the message content that needs to be replaced.""" + + type: Literal["file_citation"] + """Always `file_citation`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/file_citation_delta_annotation.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/file_citation_delta_annotation.py new file mode 100644 index 0000000..cc00e85 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/file_citation_delta_annotation.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["FileCitationDeltaAnnotation", "FileCitation"] + + +class FileCitation(BaseModel): + file_id: Optional[str] = None + """The ID of the specific File the citation is from.""" + + quote: Optional[str] = None + """The specific quote in the file.""" + + +class FileCitationDeltaAnnotation(BaseModel): + """ + A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the "file_search" tool to search files. + """ + + index: int + """The index of the annotation in the text content part.""" + + type: Literal["file_citation"] + """Always `file_citation`.""" + + end_index: Optional[int] = None + + file_citation: Optional[FileCitation] = None + + start_index: Optional[int] = None + + text: Optional[str] = None + """The text in the message content that needs to be replaced.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/file_path_annotation.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/file_path_annotation.py new file mode 100644 index 0000000..48b5bd9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/file_path_annotation.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["FilePathAnnotation", "FilePath"] + + +class FilePath(BaseModel): + file_id: str + """The ID of the file that was generated.""" + + +class FilePathAnnotation(BaseModel): + """ + A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file. + """ + + end_index: int + + file_path: FilePath + + start_index: int + + text: str + """The text in the message content that needs to be replaced.""" + + type: Literal["file_path"] + """Always `file_path`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/file_path_delta_annotation.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/file_path_delta_annotation.py new file mode 100644 index 0000000..0c9ad1e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/file_path_delta_annotation.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["FilePathDeltaAnnotation", "FilePath"] + + +class FilePath(BaseModel): + file_id: Optional[str] = None + """The ID of the file that was generated.""" + + +class FilePathDeltaAnnotation(BaseModel): + """ + A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file. + """ + + index: int + """The index of the annotation in the text content part.""" + + type: Literal["file_path"] + """Always `file_path`.""" + + end_index: Optional[int] = None + + file_path: Optional[FilePath] = None + + start_index: Optional[int] = None + + text: Optional[str] = None + """The text in the message content that needs to be replaced.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file.py new file mode 100644 index 0000000..87c9969 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ImageFile"] + + +class ImageFile(BaseModel): + file_id: str + """ + The [File](https://developers.openai.com/api/reference/resources/files) ID of + the image in the message content. Set `purpose="vision"` when uploading the File + if you need to later display the file content. + """ + + detail: Optional[Literal["auto", "low", "high"]] = None + """Specifies the detail level of the image if specified by the user. + + `low` uses fewer tokens, you can opt in to high resolution using `high`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_content_block.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_content_block.py new file mode 100644 index 0000000..8739286 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_content_block.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel +from .image_file import ImageFile + +__all__ = ["ImageFileContentBlock"] + + +class ImageFileContentBlock(BaseModel): + """ + References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message. + """ + + image_file: ImageFile + + type: Literal["image_file"] + """Always `image_file`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_content_block_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_content_block_param.py new file mode 100644 index 0000000..1eaa6d9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_content_block_param.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .image_file_param import ImageFileParam + +__all__ = ["ImageFileContentBlockParam"] + + +class ImageFileContentBlockParam(TypedDict, total=False): + """ + References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message. + """ + + image_file: Required[ImageFileParam] + + type: Required[Literal["image_file"]] + """Always `image_file`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_delta.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_delta.py new file mode 100644 index 0000000..63cb685 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_delta.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ImageFileDelta"] + + +class ImageFileDelta(BaseModel): + detail: Optional[Literal["auto", "low", "high"]] = None + """Specifies the detail level of the image if specified by the user. + + `low` uses fewer tokens, you can opt in to high resolution using `high`. + """ + + file_id: Optional[str] = None + """ + The [File](https://developers.openai.com/api/reference/resources/files) ID of + the image in the message content. Set `purpose="vision"` when uploading the File + if you need to later display the file content. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_delta_block.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_delta_block.py new file mode 100644 index 0000000..01a72c5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_delta_block.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .image_file_delta import ImageFileDelta + +__all__ = ["ImageFileDeltaBlock"] + + +class ImageFileDeltaBlock(BaseModel): + """ + References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message. + """ + + index: int + """The index of the content part in the message.""" + + type: Literal["image_file"] + """Always `image_file`.""" + + image_file: Optional[ImageFileDelta] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_param.py new file mode 100644 index 0000000..ac3c7dd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_file_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ImageFileParam"] + + +class ImageFileParam(TypedDict, total=False): + file_id: Required[str] + """ + The [File](https://developers.openai.com/api/reference/resources/files) ID of + the image in the message content. Set `purpose="vision"` when uploading the File + if you need to later display the file content. + """ + + detail: Literal["auto", "low", "high"] + """Specifies the detail level of the image if specified by the user. + + `low` uses fewer tokens, you can opt in to high resolution using `high`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url.py new file mode 100644 index 0000000..d55ffde --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ImageURL"] + + +class ImageURL(BaseModel): + url: str + """ + The external URL of the image, must be a supported image types: jpeg, jpg, png, + gif, webp. + """ + + detail: Optional[Literal["auto", "low", "high"]] = None + """Specifies the detail level of the image. + + `low` uses fewer tokens, you can opt in to high resolution using `high`. Default + value is `auto` + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_content_block.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_content_block.py new file mode 100644 index 0000000..a78ede9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_content_block.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .image_url import ImageURL +from ...._models import BaseModel + +__all__ = ["ImageURLContentBlock"] + + +class ImageURLContentBlock(BaseModel): + """References an image URL in the content of a message.""" + + image_url: ImageURL + + type: Literal["image_url"] + """The type of the content part.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_content_block_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_content_block_param.py new file mode 100644 index 0000000..02c5124 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_content_block_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .image_url_param import ImageURLParam + +__all__ = ["ImageURLContentBlockParam"] + + +class ImageURLContentBlockParam(TypedDict, total=False): + """References an image URL in the content of a message.""" + + image_url: Required[ImageURLParam] + + type: Required[Literal["image_url"]] + """The type of the content part.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_delta.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_delta.py new file mode 100644 index 0000000..f194f3a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_delta.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ImageURLDelta"] + + +class ImageURLDelta(BaseModel): + detail: Optional[Literal["auto", "low", "high"]] = None + """Specifies the detail level of the image. + + `low` uses fewer tokens, you can opt in to high resolution using `high`. + """ + + url: Optional[str] = None + """ + The URL of the image, must be a supported image types: jpeg, jpg, png, gif, + webp. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_delta_block.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_delta_block.py new file mode 100644 index 0000000..888764c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_delta_block.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .image_url_delta import ImageURLDelta + +__all__ = ["ImageURLDeltaBlock"] + + +class ImageURLDeltaBlock(BaseModel): + """References an image URL in the content of a message.""" + + index: int + """The index of the content part in the message.""" + + type: Literal["image_url"] + """Always `image_url`.""" + + image_url: Optional[ImageURLDelta] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_param.py new file mode 100644 index 0000000..ef8d90a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/image_url_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ImageURLParam"] + + +class ImageURLParam(TypedDict, total=False): + url: Required[str] + """ + The external URL of the image, must be a supported image types: jpeg, jpg, png, + gif, webp. + """ + + detail: Literal["auto", "low", "high"] + """Specifies the detail level of the image. + + `low` uses fewer tokens, you can opt in to high resolution using `high`. Default + value is `auto` + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/message.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message.py new file mode 100644 index 0000000..5aa80d9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message.py @@ -0,0 +1,109 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ...._models import BaseModel +from .message_content import MessageContent +from ...shared.metadata import Metadata +from ..code_interpreter_tool import CodeInterpreterTool + +__all__ = [ + "Message", + "Attachment", + "AttachmentTool", + "AttachmentToolAssistantToolsFileSearchTypeOnly", + "IncompleteDetails", +] + + +class AttachmentToolAssistantToolsFileSearchTypeOnly(BaseModel): + type: Literal["file_search"] + """The type of tool being defined: `file_search`""" + + +AttachmentTool: TypeAlias = Union[CodeInterpreterTool, AttachmentToolAssistantToolsFileSearchTypeOnly] + + +class Attachment(BaseModel): + file_id: Optional[str] = None + """The ID of the file to attach to the message.""" + + tools: Optional[List[AttachmentTool]] = None + """The tools to add this file to.""" + + +class IncompleteDetails(BaseModel): + """On an incomplete message, details about why the message is incomplete.""" + + reason: Literal["content_filter", "max_tokens", "run_cancelled", "run_expired", "run_failed"] + """The reason the message is incomplete.""" + + +class Message(BaseModel): + """ + Represents a message within a [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + id: str + """The identifier, which can be referenced in API endpoints.""" + + assistant_id: Optional[str] = None + """ + If applicable, the ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) that + authored this message. + """ + + attachments: Optional[List[Attachment]] = None + """A list of files attached to the message, and the tools they were added to.""" + + completed_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the message was completed.""" + + content: List[MessageContent] + """The content of the message in array of text and/or images.""" + + created_at: int + """The Unix timestamp (in seconds) for when the message was created.""" + + incomplete_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the message was marked as incomplete.""" + + incomplete_details: Optional[IncompleteDetails] = None + """On an incomplete message, details about why the message is incomplete.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + object: Literal["thread.message"] + """The object type, which is always `thread.message`.""" + + role: Literal["user", "assistant"] + """The entity that produced the message. One of `user` or `assistant`.""" + + run_id: Optional[str] = None + """ + The ID of the [run](https://developers.openai.com/api/docs/assistants/migration) + associated with the creation of this message. Value is `null` when messages are + created manually using the create message or create thread endpoints. + """ + + status: Literal["in_progress", "incomplete", "completed"] + """ + The status of the message, which can be either `in_progress`, `incomplete`, or + `completed`. + """ + + thread_id: str + """ + The [thread](https://developers.openai.com/api/docs/assistants/migration) ID + that this message belongs to. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_content.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_content.py new file mode 100644 index 0000000..ad88a99 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_content.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ...._utils import PropertyInfo +from .text_content_block import TextContentBlock +from .refusal_content_block import RefusalContentBlock +from .image_url_content_block import ImageURLContentBlock +from .image_file_content_block import ImageFileContentBlock + +__all__ = ["MessageContent"] + +MessageContent: TypeAlias = Annotated[ + Union[ImageFileContentBlock, ImageURLContentBlock, TextContentBlock, RefusalContentBlock], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_content_delta.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_content_delta.py new file mode 100644 index 0000000..d43d9e8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_content_delta.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ...._utils import PropertyInfo +from .text_delta_block import TextDeltaBlock +from .refusal_delta_block import RefusalDeltaBlock +from .image_url_delta_block import ImageURLDeltaBlock +from .image_file_delta_block import ImageFileDeltaBlock + +__all__ = ["MessageContentDelta"] + +MessageContentDelta: TypeAlias = Annotated[ + Union[ImageFileDeltaBlock, TextDeltaBlock, RefusalDeltaBlock, ImageURLDeltaBlock], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_content_part_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_content_part_param.py new file mode 100644 index 0000000..247ab1e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_content_part_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .text_content_block_param import TextContentBlockParam +from .image_url_content_block_param import ImageURLContentBlockParam +from .image_file_content_block_param import ImageFileContentBlockParam + +__all__ = ["MessageContentPartParam"] + +MessageContentPartParam: TypeAlias = Union[ImageFileContentBlockParam, ImageURLContentBlockParam, TextContentBlockParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_create_params.py new file mode 100644 index 0000000..30a04ea --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_create_params.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ...shared_params.metadata import Metadata +from .message_content_part_param import MessageContentPartParam +from ..code_interpreter_tool_param import CodeInterpreterToolParam + +__all__ = ["MessageCreateParams", "Attachment", "AttachmentTool", "AttachmentToolFileSearch"] + + +class MessageCreateParams(TypedDict, total=False): + content: Required[Union[str, Iterable[MessageContentPartParam]]] + """The text contents of the message.""" + + role: Required[Literal["user", "assistant"]] + """The role of the entity that is creating the message. Allowed values include: + + - `user`: Indicates the message is sent by an actual user and should be used in + most cases to represent user-generated messages. + - `assistant`: Indicates the message is generated by the assistant. Use this + value to insert messages from the assistant into the conversation. + """ + + attachments: Optional[Iterable[Attachment]] + """A list of files attached to the message, and the tools they should be added to.""" + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + +class AttachmentToolFileSearch(TypedDict, total=False): + type: Required[Literal["file_search"]] + """The type of tool being defined: `file_search`""" + + +AttachmentTool: TypeAlias = Union[CodeInterpreterToolParam, AttachmentToolFileSearch] + + +class Attachment(TypedDict, total=False): + file_id: str + """The ID of the file to attach to the message.""" + + tools: Iterable[AttachmentTool] + """The tools to add this file to.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_deleted.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_deleted.py new file mode 100644 index 0000000..c1e5b2b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_deleted.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["MessageDeleted"] + + +class MessageDeleted(BaseModel): + id: str + + deleted: bool + + object: Literal["thread.message.deleted"] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_delta.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_delta.py new file mode 100644 index 0000000..e8d9431 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_delta.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .message_content_delta import MessageContentDelta + +__all__ = ["MessageDelta"] + + +class MessageDelta(BaseModel): + """The delta containing the fields that have changed on the Message.""" + + content: Optional[List[MessageContentDelta]] = None + """The content of the message in array of text and/or images.""" + + role: Optional[Literal["user", "assistant"]] = None + """The entity that produced the message. One of `user` or `assistant`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_delta_event.py new file mode 100644 index 0000000..0d57dcd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_delta_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel +from .message_delta import MessageDelta + +__all__ = ["MessageDeltaEvent"] + + +class MessageDeltaEvent(BaseModel): + """Represents a message delta i.e. + + any changed fields on a message during streaming. + """ + + id: str + """The identifier of the message, which can be referenced in API endpoints.""" + + delta: MessageDelta + """The delta containing the fields that have changed on the Message.""" + + object: Literal["thread.message.delta"] + """The object type, which is always `thread.message.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_list_params.py new file mode 100644 index 0000000..6df603d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_list_params.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["MessageListParams"] + + +class MessageListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, starting with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ + + run_id: str + """Filter messages by the run ID that generated them.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_update_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_update_params.py new file mode 100644 index 0000000..3c2a0f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/message_update_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ...shared_params.metadata import Metadata + +__all__ = ["MessageUpdateParams"] + + +class MessageUpdateParams(TypedDict, total=False): + thread_id: Required[str] + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/refusal_content_block.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/refusal_content_block.py new file mode 100644 index 0000000..3a17946 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/refusal_content_block.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["RefusalContentBlock"] + + +class RefusalContentBlock(BaseModel): + """The refusal content generated by the assistant.""" + + refusal: str + + type: Literal["refusal"] + """Always `refusal`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/refusal_delta_block.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/refusal_delta_block.py new file mode 100644 index 0000000..ebc0b24 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/refusal_delta_block.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["RefusalDeltaBlock"] + + +class RefusalDeltaBlock(BaseModel): + """The refusal content that is part of a message.""" + + index: int + """The index of the refusal part in the message.""" + + type: Literal["refusal"] + """Always `refusal`.""" + + refusal: Optional[str] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/required_action_function_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/required_action_function_tool_call.py new file mode 100644 index 0000000..def9230 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/required_action_function_tool_call.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["RequiredActionFunctionToolCall", "Function"] + + +class Function(BaseModel): + """The function definition.""" + + arguments: str + """The arguments that the model expects you to pass to the function.""" + + name: str + """The name of the function.""" + + +class RequiredActionFunctionToolCall(BaseModel): + """Tool call objects""" + + id: str + """The ID of the tool call. + + This ID must be referenced when you submit the tool outputs in using the + [Submit tool outputs to run](https://developers.openai.com/api/docs/assistants/migration) + endpoint. + """ + + function: Function + """The function definition.""" + + type: Literal["function"] + """The type of tool call the output is required for. + + For now, this is always `function`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/run.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/run.py new file mode 100644 index 0000000..e1ba8be --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/run.py @@ -0,0 +1,274 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .run_status import RunStatus +from ..assistant_tool import AssistantTool +from ...shared.metadata import Metadata +from ..assistant_tool_choice_option import AssistantToolChoiceOption +from ..assistant_response_format_option import AssistantResponseFormatOption +from .required_action_function_tool_call import RequiredActionFunctionToolCall + +__all__ = [ + "Run", + "IncompleteDetails", + "LastError", + "RequiredAction", + "RequiredActionSubmitToolOutputs", + "TruncationStrategy", + "Usage", +] + + +class IncompleteDetails(BaseModel): + """Details on why the run is incomplete. + + Will be `null` if the run is not incomplete. + """ + + reason: Optional[Literal["max_completion_tokens", "max_prompt_tokens"]] = None + """The reason why the run is incomplete. + + This will point to which specific token limit was reached over the course of the + run. + """ + + +class LastError(BaseModel): + """The last error associated with this run. Will be `null` if there are no errors.""" + + code: Literal["server_error", "rate_limit_exceeded", "invalid_prompt"] + """One of `server_error`, `rate_limit_exceeded`, or `invalid_prompt`.""" + + message: str + """A human-readable description of the error.""" + + +class RequiredActionSubmitToolOutputs(BaseModel): + """Details on the tool outputs needed for this run to continue.""" + + tool_calls: List[RequiredActionFunctionToolCall] + """A list of the relevant tool calls.""" + + +class RequiredAction(BaseModel): + """Details on the action required to continue the run. + + Will be `null` if no action is required. + """ + + submit_tool_outputs: RequiredActionSubmitToolOutputs + """Details on the tool outputs needed for this run to continue.""" + + type: Literal["submit_tool_outputs"] + """For now, this is always `submit_tool_outputs`.""" + + +class TruncationStrategy(BaseModel): + """Controls for how a thread will be truncated prior to the run. + + Use this to control the initial context window of the run. + """ + + type: Literal["auto", "last_messages"] + """The truncation strategy to use for the thread. + + The default is `auto`. If set to `last_messages`, the thread will be truncated + to the n most recent messages in the thread. When set to `auto`, messages in the + middle of the thread will be dropped to fit the context length of the model, + `max_prompt_tokens`. + """ + + last_messages: Optional[int] = None + """ + The number of most recent messages from the thread when constructing the context + for the run. + """ + + +class Usage(BaseModel): + """Usage statistics related to the run. + + This value will be `null` if the run is not in a terminal state (i.e. `in_progress`, `queued`, etc.). + """ + + completion_tokens: int + """Number of completion tokens used over the course of the run.""" + + prompt_tokens: int + """Number of prompt tokens used over the course of the run.""" + + total_tokens: int + """Total number of tokens used (prompt + completion).""" + + +class Run(BaseModel): + """ + Represents an execution run on a [thread](https://developers.openai.com/api/docs/assistants/migration). + """ + + id: str + """The identifier, which can be referenced in API endpoints.""" + + assistant_id: str + """ + The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) used + for execution of this run. + """ + + cancelled_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the run was cancelled.""" + + completed_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the run was completed.""" + + created_at: int + """The Unix timestamp (in seconds) for when the run was created.""" + + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the run will expire.""" + + failed_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the run failed.""" + + incomplete_details: Optional[IncompleteDetails] = None + """Details on why the run is incomplete. + + Will be `null` if the run is not incomplete. + """ + + instructions: str + """ + The instructions that the + [assistant](https://developers.openai.com/api/docs/assistants/migration) used + for this run. + """ + + last_error: Optional[LastError] = None + """The last error associated with this run. Will be `null` if there are no errors.""" + + max_completion_tokens: Optional[int] = None + """ + The maximum number of completion tokens specified to have been used over the + course of the run. + """ + + max_prompt_tokens: Optional[int] = None + """ + The maximum number of prompt tokens specified to have been used over the course + of the run. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: str + """ + The model that the + [assistant](https://developers.openai.com/api/docs/assistants/migration) used + for this run. + """ + + object: Literal["thread.run"] + """The object type, which is always `thread.run`.""" + + parallel_tool_calls: bool + """ + Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + """ + + required_action: Optional[RequiredAction] = None + """Details on the action required to continue the run. + + Will be `null` if no action is required. + """ + + response_format: Optional[AssistantResponseFormatOption] = None + """Specifies the format that the model must output. + + Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + """ + + started_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the run was started.""" + + status: RunStatus + """ + The status of the run, which can be either `queued`, `in_progress`, + `requires_action`, `cancelling`, `cancelled`, `failed`, `completed`, + `incomplete`, or `expired`. + """ + + thread_id: str + """ + The ID of the + [thread](https://developers.openai.com/api/docs/assistants/migration) that was + executed on as a part of this run. + """ + + tool_choice: Optional[AssistantToolChoiceOption] = None + """ + Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + """ + + tools: List[AssistantTool] + """ + The list of tools that the + [assistant](https://developers.openai.com/api/docs/assistants/migration) used + for this run. + """ + + truncation_strategy: Optional[TruncationStrategy] = None + """Controls for how a thread will be truncated prior to the run. + + Use this to control the initial context window of the run. + """ + + usage: Optional[Usage] = None + """Usage statistics related to the run. + + This value will be `null` if the run is not in a terminal state (i.e. + `in_progress`, `queued`, etc.). + """ + + temperature: Optional[float] = None + """The sampling temperature used for this run. If not set, defaults to 1.""" + + top_p: Optional[float] = None + """The nucleus sampling value used for this run. If not set, defaults to 1.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_create_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_create_params.py new file mode 100644 index 0000000..d2be422 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_create_params.py @@ -0,0 +1,269 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ...shared.chat_model import ChatModel +from ..assistant_tool_param import AssistantToolParam +from .runs.run_step_include import RunStepInclude +from ...shared_params.metadata import Metadata +from ...shared.reasoning_effort import ReasoningEffort +from .message_content_part_param import MessageContentPartParam +from ..code_interpreter_tool_param import CodeInterpreterToolParam +from ..assistant_tool_choice_option_param import AssistantToolChoiceOptionParam +from ..assistant_response_format_option_param import AssistantResponseFormatOptionParam + +__all__ = [ + "RunCreateParamsBase", + "AdditionalMessage", + "AdditionalMessageAttachment", + "AdditionalMessageAttachmentTool", + "AdditionalMessageAttachmentToolFileSearch", + "TruncationStrategy", + "RunCreateParamsNonStreaming", + "RunCreateParamsStreaming", +] + + +class RunCreateParamsBase(TypedDict, total=False): + assistant_id: Required[str] + """ + The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) to use + to execute this run. + """ + + include: List[RunStepInclude] + """A list of additional fields to include in the response. + + Currently the only supported value is + `step_details.tool_calls[*].file_search.results[*].content` to fetch the file + search result content. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + """ + + additional_instructions: Optional[str] + """Appends additional instructions at the end of the instructions for the run. + + This is useful for modifying the behavior on a per-run basis without overriding + other instructions. + """ + + additional_messages: Optional[Iterable[AdditionalMessage]] + """Adds additional messages to the thread before creating the run.""" + + instructions: Optional[str] + """ + Overrides the + [instructions](https://developers.openai.com/api/docs/assistants/migration) of + the assistant. This is useful for modifying the behavior on a per-run basis. + """ + + max_completion_tokens: Optional[int] + """ + The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + """ + + max_prompt_tokens: Optional[int] + """The maximum number of prompt tokens that may be used over the course of the run. + + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `incomplete`. See + `incomplete_details` for more info. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Union[str, ChatModel, None] + """ + The ID of the + [Model](https://developers.openai.com/api/reference/resources/models) to be used + to execute this run. If a value is provided here, it will override the model + associated with the assistant. If not, the model associated with the assistant + will be used. + """ + + parallel_tool_calls: bool + """ + Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + """ + + reasoning_effort: Optional[ReasoningEffort] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + response_format: Optional[AssistantResponseFormatOptionParam] + """Specifies the format that the model must output. + + Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), + [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + """ + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + """ + + tool_choice: Optional[AssistantToolChoiceOptionParam] + """ + Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + """ + + tools: Optional[Iterable[AssistantToolParam]] + """Override the tools the assistant can use for this run. + + This is useful for modifying the behavior on a per-run basis. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or temperature but not both. + """ + + truncation_strategy: Optional[TruncationStrategy] + """Controls for how a thread will be truncated prior to the run. + + Use this to control the initial context window of the run. + """ + + +class AdditionalMessageAttachmentToolFileSearch(TypedDict, total=False): + type: Required[Literal["file_search"]] + """The type of tool being defined: `file_search`""" + + +AdditionalMessageAttachmentTool: TypeAlias = Union[CodeInterpreterToolParam, AdditionalMessageAttachmentToolFileSearch] + + +class AdditionalMessageAttachment(TypedDict, total=False): + file_id: str + """The ID of the file to attach to the message.""" + + tools: Iterable[AdditionalMessageAttachmentTool] + """The tools to add this file to.""" + + +class AdditionalMessage(TypedDict, total=False): + content: Required[Union[str, Iterable[MessageContentPartParam]]] + """The text contents of the message.""" + + role: Required[Literal["user", "assistant"]] + """The role of the entity that is creating the message. Allowed values include: + + - `user`: Indicates the message is sent by an actual user and should be used in + most cases to represent user-generated messages. + - `assistant`: Indicates the message is generated by the assistant. Use this + value to insert messages from the assistant into the conversation. + """ + + attachments: Optional[Iterable[AdditionalMessageAttachment]] + """A list of files attached to the message, and the tools they should be added to.""" + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + +class TruncationStrategy(TypedDict, total=False): + """Controls for how a thread will be truncated prior to the run. + + Use this to control the initial context window of the run. + """ + + type: Required[Literal["auto", "last_messages"]] + """The truncation strategy to use for the thread. + + The default is `auto`. If set to `last_messages`, the thread will be truncated + to the n most recent messages in the thread. When set to `auto`, messages in the + middle of the thread will be dropped to fit the context length of the model, + `max_prompt_tokens`. + """ + + last_messages: Optional[int] + """ + The number of most recent messages from the thread when constructing the context + for the run. + """ + + +class RunCreateParamsNonStreaming(RunCreateParamsBase, total=False): + stream: Optional[Literal[False]] + """ + If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + """ + + +class RunCreateParamsStreaming(RunCreateParamsBase): + stream: Required[Literal[True]] + """ + If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + """ + + +RunCreateParams = Union[RunCreateParamsNonStreaming, RunCreateParamsStreaming] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_list_params.py new file mode 100644 index 0000000..abc31c4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_list_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["RunListParams"] + + +class RunListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, starting with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_status.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_status.py new file mode 100644 index 0000000..1922290 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_status.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["RunStatus"] + +RunStatus: TypeAlias = Literal[ + "queued", + "in_progress", + "requires_action", + "cancelling", + "cancelled", + "failed", + "completed", + "incomplete", + "expired", +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_submit_tool_outputs_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_submit_tool_outputs_params.py new file mode 100644 index 0000000..a2e6f0f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_submit_tool_outputs_params.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = [ + "RunSubmitToolOutputsParamsBase", + "ToolOutput", + "RunSubmitToolOutputsParamsNonStreaming", + "RunSubmitToolOutputsParamsStreaming", +] + + +class RunSubmitToolOutputsParamsBase(TypedDict, total=False): + thread_id: Required[str] + + tool_outputs: Required[Iterable[ToolOutput]] + """A list of tools for which the outputs are being submitted.""" + + +class ToolOutput(TypedDict, total=False): + output: str + """The output of the tool call to be submitted to continue the run.""" + + tool_call_id: str + """ + The ID of the tool call in the `required_action` object within the run object + the output is being submitted for. + """ + + +class RunSubmitToolOutputsParamsNonStreaming(RunSubmitToolOutputsParamsBase, total=False): + stream: Optional[Literal[False]] + """ + If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + """ + + +class RunSubmitToolOutputsParamsStreaming(RunSubmitToolOutputsParamsBase): + stream: Required[Literal[True]] + """ + If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + """ + + +RunSubmitToolOutputsParams = Union[RunSubmitToolOutputsParamsNonStreaming, RunSubmitToolOutputsParamsStreaming] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_update_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_update_params.py new file mode 100644 index 0000000..2bae800 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/run_update_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ...shared_params.metadata import Metadata + +__all__ = ["RunUpdateParams"] + + +class RunUpdateParams(TypedDict, total=False): + thread_id: Required[str] + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__init__.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__init__.py new file mode 100644 index 0000000..6406f28 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__init__.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .run_step import RunStep as RunStep +from .tool_call import ToolCall as ToolCall +from .run_step_delta import RunStepDelta as RunStepDelta +from .tool_call_delta import ToolCallDelta as ToolCallDelta +from .run_step_include import RunStepInclude as RunStepInclude +from .step_list_params import StepListParams as StepListParams +from .function_tool_call import FunctionToolCall as FunctionToolCall +from .run_step_delta_event import RunStepDeltaEvent as RunStepDeltaEvent +from .step_retrieve_params import StepRetrieveParams as StepRetrieveParams +from .code_interpreter_logs import CodeInterpreterLogs as CodeInterpreterLogs +from .file_search_tool_call import FileSearchToolCall as FileSearchToolCall +from .tool_call_delta_object import ToolCallDeltaObject as ToolCallDeltaObject +from .tool_calls_step_details import ToolCallsStepDetails as ToolCallsStepDetails +from .function_tool_call_delta import FunctionToolCallDelta as FunctionToolCallDelta +from .code_interpreter_tool_call import CodeInterpreterToolCall as CodeInterpreterToolCall +from .file_search_tool_call_delta import FileSearchToolCallDelta as FileSearchToolCallDelta +from .run_step_delta_message_delta import RunStepDeltaMessageDelta as RunStepDeltaMessageDelta +from .code_interpreter_output_image import CodeInterpreterOutputImage as CodeInterpreterOutputImage +from .message_creation_step_details import MessageCreationStepDetails as MessageCreationStepDetails +from .code_interpreter_tool_call_delta import CodeInterpreterToolCallDelta as CodeInterpreterToolCallDelta diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a8d0a73 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_logs.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_logs.cpython-312.pyc new file mode 100644 index 0000000..7170dd3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_logs.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_output_image.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_output_image.cpython-312.pyc new file mode 100644 index 0000000..805bf2d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_output_image.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call.cpython-312.pyc new file mode 100644 index 0000000..c5ab8b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call_delta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call_delta.cpython-312.pyc new file mode 100644 index 0000000..7e07629 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call_delta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call.cpython-312.pyc new file mode 100644 index 0000000..8723cd9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call_delta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call_delta.cpython-312.pyc new file mode 100644 index 0000000..0d2f0bf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call_delta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call.cpython-312.pyc new file mode 100644 index 0000000..79a9648 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call_delta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call_delta.cpython-312.pyc new file mode 100644 index 0000000..67c74d4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call_delta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/message_creation_step_details.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/message_creation_step_details.cpython-312.pyc new file mode 100644 index 0000000..d8c9aae Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/message_creation_step_details.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step.cpython-312.pyc new file mode 100644 index 0000000..26c2e8e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta.cpython-312.pyc new file mode 100644 index 0000000..84e973f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_event.cpython-312.pyc new file mode 100644 index 0000000..ed4fa0f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_message_delta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_message_delta.cpython-312.pyc new file mode 100644 index 0000000..b737825 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_message_delta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_include.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_include.cpython-312.pyc new file mode 100644 index 0000000..ff83ae9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_include.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/step_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/step_list_params.cpython-312.pyc new file mode 100644 index 0000000..108cbe8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/step_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/step_retrieve_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/step_retrieve_params.cpython-312.pyc new file mode 100644 index 0000000..9ffef23 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/step_retrieve_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call.cpython-312.pyc new file mode 100644 index 0000000..6f79946 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta.cpython-312.pyc new file mode 100644 index 0000000..3e18137 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta_object.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta_object.cpython-312.pyc new file mode 100644 index 0000000..aab22b6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta_object.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/tool_calls_step_details.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/tool_calls_step_details.cpython-312.pyc new file mode 100644 index 0000000..03d4b67 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/__pycache__/tool_calls_step_details.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/code_interpreter_logs.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/code_interpreter_logs.py new file mode 100644 index 0000000..df73ff3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/code_interpreter_logs.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["CodeInterpreterLogs"] + + +class CodeInterpreterLogs(BaseModel): + """Text output from the Code Interpreter tool call as part of a run step.""" + + index: int + """The index of the output in the outputs array.""" + + type: Literal["logs"] + """Always `logs`.""" + + logs: Optional[str] = None + """The text output from the Code Interpreter tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/code_interpreter_output_image.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/code_interpreter_output_image.py new file mode 100644 index 0000000..5a3694f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/code_interpreter_output_image.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["CodeInterpreterOutputImage", "Image"] + + +class Image(BaseModel): + file_id: Optional[str] = None + """ + The [file](https://developers.openai.com/api/reference/resources/files) ID of + the image. + """ + + +class CodeInterpreterOutputImage(BaseModel): + index: int + """The index of the output in the outputs array.""" + + type: Literal["image"] + """Always `image`.""" + + image: Optional[Image] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/code_interpreter_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/code_interpreter_tool_call.py new file mode 100644 index 0000000..d67947f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/code_interpreter_tool_call.py @@ -0,0 +1,76 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ....._utils import PropertyInfo +from ....._models import BaseModel + +__all__ = [ + "CodeInterpreterToolCall", + "CodeInterpreter", + "CodeInterpreterOutput", + "CodeInterpreterOutputLogs", + "CodeInterpreterOutputImage", + "CodeInterpreterOutputImageImage", +] + + +class CodeInterpreterOutputLogs(BaseModel): + """Text output from the Code Interpreter tool call as part of a run step.""" + + logs: str + """The text output from the Code Interpreter tool call.""" + + type: Literal["logs"] + """Always `logs`.""" + + +class CodeInterpreterOutputImageImage(BaseModel): + file_id: str + """ + The [file](https://developers.openai.com/api/reference/resources/files) ID of + the image. + """ + + +class CodeInterpreterOutputImage(BaseModel): + image: CodeInterpreterOutputImageImage + + type: Literal["image"] + """Always `image`.""" + + +CodeInterpreterOutput: TypeAlias = Annotated[ + Union[CodeInterpreterOutputLogs, CodeInterpreterOutputImage], PropertyInfo(discriminator="type") +] + + +class CodeInterpreter(BaseModel): + """The Code Interpreter tool call definition.""" + + input: str + """The input to the Code Interpreter tool call.""" + + outputs: List[CodeInterpreterOutput] + """The outputs from the Code Interpreter tool call. + + Code Interpreter can output one or more items, including text (`logs`) or images + (`image`). Each of these are represented by a different object type. + """ + + +class CodeInterpreterToolCall(BaseModel): + """Details of the Code Interpreter tool call the run step was involved in.""" + + id: str + """The ID of the tool call.""" + + code_interpreter: CodeInterpreter + """The Code Interpreter tool call definition.""" + + type: Literal["code_interpreter"] + """The type of tool call. + + This is always going to be `code_interpreter` for this type of tool call. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py new file mode 100644 index 0000000..6383e60 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ....._utils import PropertyInfo +from ....._models import BaseModel +from .code_interpreter_logs import CodeInterpreterLogs +from .code_interpreter_output_image import CodeInterpreterOutputImage + +__all__ = ["CodeInterpreterToolCallDelta", "CodeInterpreter", "CodeInterpreterOutput"] + +CodeInterpreterOutput: TypeAlias = Annotated[ + Union[CodeInterpreterLogs, CodeInterpreterOutputImage], PropertyInfo(discriminator="type") +] + + +class CodeInterpreter(BaseModel): + """The Code Interpreter tool call definition.""" + + input: Optional[str] = None + """The input to the Code Interpreter tool call.""" + + outputs: Optional[List[CodeInterpreterOutput]] = None + """The outputs from the Code Interpreter tool call. + + Code Interpreter can output one or more items, including text (`logs`) or images + (`image`). Each of these are represented by a different object type. + """ + + +class CodeInterpreterToolCallDelta(BaseModel): + """Details of the Code Interpreter tool call the run step was involved in.""" + + index: int + """The index of the tool call in the tool calls array.""" + + type: Literal["code_interpreter"] + """The type of tool call. + + This is always going to be `code_interpreter` for this type of tool call. + """ + + id: Optional[str] = None + """The ID of the tool call.""" + + code_interpreter: Optional[CodeInterpreter] = None + """The Code Interpreter tool call definition.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/file_search_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/file_search_tool_call.py new file mode 100644 index 0000000..df4da65 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/file_search_tool_call.py @@ -0,0 +1,84 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = [ + "FileSearchToolCall", + "FileSearch", + "FileSearchRankingOptions", + "FileSearchResult", + "FileSearchResultContent", +] + + +class FileSearchRankingOptions(BaseModel): + """The ranking options for the file search.""" + + ranker: Literal["auto", "default_2024_08_21"] + """The ranker to use for the file search. + + If not specified will use the `auto` ranker. + """ + + score_threshold: float + """The score threshold for the file search. + + All values must be a floating point number between 0 and 1. + """ + + +class FileSearchResultContent(BaseModel): + text: Optional[str] = None + """The text content of the file.""" + + type: Optional[Literal["text"]] = None + """The type of the content.""" + + +class FileSearchResult(BaseModel): + """A result instance of the file search.""" + + file_id: str + """The ID of the file that result was found in.""" + + file_name: str + """The name of the file that result was found in.""" + + score: float + """The score of the result. + + All values must be a floating point number between 0 and 1. + """ + + content: Optional[List[FileSearchResultContent]] = None + """The content of the result that was found. + + The content is only included if requested via the include query parameter. + """ + + +class FileSearch(BaseModel): + """For now, this is always going to be an empty object.""" + + ranking_options: Optional[FileSearchRankingOptions] = None + """The ranking options for the file search.""" + + results: Optional[List[FileSearchResult]] = None + """The results of the file search.""" + + +class FileSearchToolCall(BaseModel): + id: str + """The ID of the tool call object.""" + + file_search: FileSearch + """For now, this is always going to be an empty object.""" + + type: Literal["file_search"] + """The type of tool call. + + This is always going to be `file_search` for this type of tool call. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/file_search_tool_call_delta.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/file_search_tool_call_delta.py new file mode 100644 index 0000000..04fa5ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/file_search_tool_call_delta.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["FileSearchToolCallDelta"] + + +class FileSearchToolCallDelta(BaseModel): + file_search: object + """For now, this is always going to be an empty object.""" + + index: int + """The index of the tool call in the tool calls array.""" + + type: Literal["file_search"] + """The type of tool call. + + This is always going to be `file_search` for this type of tool call. + """ + + id: Optional[str] = None + """The ID of the tool call object.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/function_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/function_tool_call.py new file mode 100644 index 0000000..b5d4e3e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/function_tool_call.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["FunctionToolCall", "Function"] + + +class Function(BaseModel): + """The definition of the function that was called.""" + + arguments: str + """The arguments passed to the function.""" + + name: str + """The name of the function.""" + + output: Optional[str] = None + """The output of the function. + + This will be `null` if the outputs have not been + [submitted](https://developers.openai.com/api/docs/assistants/migration) yet. + """ + + +class FunctionToolCall(BaseModel): + id: str + """The ID of the tool call object.""" + + function: Function + """The definition of the function that was called.""" + + type: Literal["function"] + """The type of tool call. + + This is always going to be `function` for this type of tool call. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/function_tool_call_delta.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/function_tool_call_delta.py new file mode 100644 index 0000000..59ceeb1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/function_tool_call_delta.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["FunctionToolCallDelta", "Function"] + + +class Function(BaseModel): + """The definition of the function that was called.""" + + arguments: Optional[str] = None + """The arguments passed to the function.""" + + name: Optional[str] = None + """The name of the function.""" + + output: Optional[str] = None + """The output of the function. + + This will be `null` if the outputs have not been + [submitted](https://developers.openai.com/api/docs/assistants/migration) yet. + """ + + +class FunctionToolCallDelta(BaseModel): + index: int + """The index of the tool call in the tool calls array.""" + + type: Literal["function"] + """The type of tool call. + + This is always going to be `function` for this type of tool call. + """ + + id: Optional[str] = None + """The ID of the tool call object.""" + + function: Optional[Function] = None + """The definition of the function that was called.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/message_creation_step_details.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/message_creation_step_details.py new file mode 100644 index 0000000..20e08cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/message_creation_step_details.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["MessageCreationStepDetails", "MessageCreation"] + + +class MessageCreation(BaseModel): + message_id: str + """The ID of the message that was created by this run step.""" + + +class MessageCreationStepDetails(BaseModel): + """Details of the message creation by the run step.""" + + message_creation: MessageCreation + + type: Literal["message_creation"] + """Always `message_creation`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step.py new file mode 100644 index 0000000..594d40e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step.py @@ -0,0 +1,128 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ....._utils import PropertyInfo +from ....._models import BaseModel +from ....shared.metadata import Metadata +from .tool_calls_step_details import ToolCallsStepDetails +from .message_creation_step_details import MessageCreationStepDetails + +__all__ = ["RunStep", "LastError", "StepDetails", "Usage"] + + +class LastError(BaseModel): + """The last error associated with this run step. + + Will be `null` if there are no errors. + """ + + code: Literal["server_error", "rate_limit_exceeded"] + """One of `server_error` or `rate_limit_exceeded`.""" + + message: str + """A human-readable description of the error.""" + + +StepDetails: TypeAlias = Annotated[ + Union[MessageCreationStepDetails, ToolCallsStepDetails], PropertyInfo(discriminator="type") +] + + +class Usage(BaseModel): + """Usage statistics related to the run step. + + This value will be `null` while the run step's status is `in_progress`. + """ + + completion_tokens: int + """Number of completion tokens used over the course of the run step.""" + + prompt_tokens: int + """Number of prompt tokens used over the course of the run step.""" + + total_tokens: int + """Total number of tokens used (prompt + completion).""" + + +class RunStep(BaseModel): + """Represents a step in execution of a run.""" + + id: str + """The identifier of the run step, which can be referenced in API endpoints.""" + + assistant_id: str + """ + The ID of the + [assistant](https://developers.openai.com/api/docs/assistants/migration) + associated with the run step. + """ + + cancelled_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the run step was cancelled.""" + + completed_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the run step completed.""" + + created_at: int + """The Unix timestamp (in seconds) for when the run step was created.""" + + expired_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the run step expired. + + A step is considered expired if the parent run is expired. + """ + + failed_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the run step failed.""" + + last_error: Optional[LastError] = None + """The last error associated with this run step. + + Will be `null` if there are no errors. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + object: Literal["thread.run.step"] + """The object type, which is always `thread.run.step`.""" + + run_id: str + """ + The ID of the [run](https://developers.openai.com/api/docs/assistants/migration) + that this run step is a part of. + """ + + status: Literal["in_progress", "cancelled", "failed", "completed", "expired"] + """ + The status of the run step, which can be either `in_progress`, `cancelled`, + `failed`, `completed`, or `expired`. + """ + + step_details: StepDetails + """The details of the run step.""" + + thread_id: str + """ + The ID of the + [thread](https://developers.openai.com/api/docs/assistants/migration) that was + run. + """ + + type: Literal["message_creation", "tool_calls"] + """The type of run step, which can be either `message_creation` or `tool_calls`.""" + + usage: Optional[Usage] = None + """Usage statistics related to the run step. + + This value will be `null` while the run step's status is `in_progress`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step_delta.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step_delta.py new file mode 100644 index 0000000..0094f59 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step_delta.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Annotated, TypeAlias + +from ....._utils import PropertyInfo +from ....._models import BaseModel +from .tool_call_delta_object import ToolCallDeltaObject +from .run_step_delta_message_delta import RunStepDeltaMessageDelta + +__all__ = ["RunStepDelta", "StepDetails"] + +StepDetails: TypeAlias = Annotated[ + Union[RunStepDeltaMessageDelta, ToolCallDeltaObject], PropertyInfo(discriminator="type") +] + + +class RunStepDelta(BaseModel): + """The delta containing the fields that have changed on the run step.""" + + step_details: Optional[StepDetails] = None + """The details of the run step.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step_delta_event.py new file mode 100644 index 0000000..bdc5f91 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step_delta_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel +from .run_step_delta import RunStepDelta + +__all__ = ["RunStepDeltaEvent"] + + +class RunStepDeltaEvent(BaseModel): + """Represents a run step delta i.e. + + any changed fields on a run step during streaming. + """ + + id: str + """The identifier of the run step, which can be referenced in API endpoints.""" + + delta: RunStepDelta + """The delta containing the fields that have changed on the run step.""" + + object: Literal["thread.run.step.delta"] + """The object type, which is always `thread.run.step.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step_delta_message_delta.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step_delta_message_delta.py new file mode 100644 index 0000000..21a997e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step_delta_message_delta.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["RunStepDeltaMessageDelta", "MessageCreation"] + + +class MessageCreation(BaseModel): + message_id: Optional[str] = None + """The ID of the message that was created by this run step.""" + + +class RunStepDeltaMessageDelta(BaseModel): + """Details of the message creation by the run step.""" + + type: Literal["message_creation"] + """Always `message_creation`.""" + + message_creation: Optional[MessageCreation] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step_include.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step_include.py new file mode 100644 index 0000000..452df58 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/run_step_include.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["RunStepInclude"] + +RunStepInclude: TypeAlias = Literal["step_details.tool_calls[*].file_search.results[*].content"] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/step_list_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/step_list_params.py new file mode 100644 index 0000000..e073ed7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/step_list_params.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from .run_step_include import RunStepInclude + +__all__ = ["StepListParams"] + + +class StepListParams(TypedDict, total=False): + thread_id: Required[str] + + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, starting with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + include: List[RunStepInclude] + """A list of additional fields to include in the response. + + Currently the only supported value is + `step_details.tool_calls[*].file_search.results[*].content` to fetch the file + search result content. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/step_retrieve_params.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/step_retrieve_params.py new file mode 100644 index 0000000..6e7f58a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/step_retrieve_params.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Required, TypedDict + +from .run_step_include import RunStepInclude + +__all__ = ["StepRetrieveParams"] + + +class StepRetrieveParams(TypedDict, total=False): + thread_id: Required[str] + + run_id: Required[str] + + include: List[RunStepInclude] + """A list of additional fields to include in the response. + + Currently the only supported value is + `step_details.tool_calls[*].file_search.results[*].content` to fetch the file + search result content. + + See the + [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) + for more information. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/tool_call.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/tool_call.py new file mode 100644 index 0000000..988113a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/tool_call.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ....._utils import PropertyInfo +from .function_tool_call import FunctionToolCall +from .file_search_tool_call import FileSearchToolCall +from .code_interpreter_tool_call import CodeInterpreterToolCall + +__all__ = ["ToolCall"] + +ToolCall: TypeAlias = Annotated[ + Union[CodeInterpreterToolCall, FileSearchToolCall, FunctionToolCall], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/tool_call_delta.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/tool_call_delta.py new file mode 100644 index 0000000..f4e2f97 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/tool_call_delta.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ....._utils import PropertyInfo +from .function_tool_call_delta import FunctionToolCallDelta +from .file_search_tool_call_delta import FileSearchToolCallDelta +from .code_interpreter_tool_call_delta import CodeInterpreterToolCallDelta + +__all__ = ["ToolCallDelta"] + +ToolCallDelta: TypeAlias = Annotated[ + Union[CodeInterpreterToolCallDelta, FileSearchToolCallDelta, FunctionToolCallDelta], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/tool_call_delta_object.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/tool_call_delta_object.py new file mode 100644 index 0000000..829319c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/tool_call_delta_object.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ....._models import BaseModel +from .tool_call_delta import ToolCallDelta + +__all__ = ["ToolCallDeltaObject"] + + +class ToolCallDeltaObject(BaseModel): + """Details of the tool call.""" + + type: Literal["tool_calls"] + """Always `tool_calls`.""" + + tool_calls: Optional[List[ToolCallDelta]] = None + """An array of tool calls the run step was involved in. + + These can be associated with one of three types of tools: `code_interpreter`, + `file_search`, or `function`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/tool_calls_step_details.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/tool_calls_step_details.py new file mode 100644 index 0000000..4c3ed0a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/runs/tool_calls_step_details.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from .tool_call import ToolCall +from ....._models import BaseModel + +__all__ = ["ToolCallsStepDetails"] + + +class ToolCallsStepDetails(BaseModel): + """Details of the tool call.""" + + tool_calls: List[ToolCall] + """An array of tool calls the run step was involved in. + + These can be associated with one of three types of tools: `code_interpreter`, + `file_search`, or `function`. + """ + + type: Literal["tool_calls"] + """Always `tool_calls`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/text.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/text.py new file mode 100644 index 0000000..ff2bead --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/text.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List + +from ...._models import BaseModel +from .annotation import Annotation + +__all__ = ["Text"] + + +class Text(BaseModel): + annotations: List[Annotation] + + value: str + """The data that makes up the text.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/text_content_block.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/text_content_block.py new file mode 100644 index 0000000..2b87d26 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/text_content_block.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .text import Text +from ...._models import BaseModel + +__all__ = ["TextContentBlock"] + + +class TextContentBlock(BaseModel): + """The text content that is part of a message.""" + + text: Text + + type: Literal["text"] + """Always `text`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/text_content_block_param.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/text_content_block_param.py new file mode 100644 index 0000000..7d8fcf3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/text_content_block_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["TextContentBlockParam"] + + +class TextContentBlockParam(TypedDict, total=False): + """The text content that is part of a message.""" + + text: Required[str] + """Text content to be sent to the model""" + + type: Required[Literal["text"]] + """Always `text`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/text_delta.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/text_delta.py new file mode 100644 index 0000000..ec100f8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/text_delta.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ...._models import BaseModel +from .annotation_delta import AnnotationDelta + +__all__ = ["TextDelta"] + + +class TextDelta(BaseModel): + annotations: Optional[List[AnnotationDelta]] = None + + value: Optional[str] = None + """The data that makes up the text.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/threads/text_delta_block.py b/venv/lib/python3.12/site-packages/openai/types/beta/threads/text_delta_block.py new file mode 100644 index 0000000..baa6cb1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/threads/text_delta_block.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .text_delta import TextDelta + +__all__ = ["TextDeltaBlock"] + + +class TextDeltaBlock(BaseModel): + """The text content that is part of a message.""" + + index: int + """The index of the content part in the message.""" + + type: Literal["text"] + """Always `text`.""" + + text: Optional[TextDelta] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/token_usage.py b/venv/lib/python3.12/site-packages/openai/types/beta/token_usage.py new file mode 100644 index 0000000..827963f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/token_usage.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["TokenUsage", "InputTokensDetails", "OutputTokensDetails"] + + +class InputTokensDetails(BaseModel): + """A breakdown of the agent's input token usage.""" + + cached_tokens: int + """The number of input tokens retrieved from the prompt cache.""" + + +class OutputTokensDetails(BaseModel): + """A breakdown of the agent's output token usage.""" + + reasoning_tokens: int + """The number of output tokens used for reasoning.""" + + +class TokenUsage(BaseModel): + """Recorded token usage for a session or turn. + + Usage is best effort and may change. + """ + + input_tokens: int + """The number of input tokens used by the agent.""" + + input_tokens_details: InputTokensDetails + """A breakdown of the agent's input token usage.""" + + output_tokens: int + """The number of output tokens generated by the agent.""" + + output_tokens_details: OutputTokensDetails + """A breakdown of the agent's output token usage.""" + + total_tokens: int + """The total number of input and output tokens used by the agent.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/beta/web_search_action.py b/venv/lib/python3.12/site-packages/openai/types/beta/web_search_action.py new file mode 100644 index 0000000..69ee877 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/beta/web_search_action.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "WebSearchAction", + "WebSearchActionResourceSearch", + "WebSearchActionResourceOpenPage", + "WebSearchActionResourceFindInPage", + "WebSearchActionResourceOther", +] + + +class WebSearchActionResourceSearch(BaseModel): + """A search query or group of search queries.""" + + queries: Optional[List[str]] = None + """The search queries, when multiple queries were used.""" + + query: Optional[str] = None + """The search query, when a single query was used.""" + + type: Literal["search"] + """The type of the object. Always `search`.""" + + +class WebSearchActionResourceOpenPage(BaseModel): + """Opens a web page.""" + + type: Literal["open_page"] + """The type of the object. Always `open_page`.""" + + url: Optional[str] = None + """The URL of the page that was opened.""" + + +class WebSearchActionResourceFindInPage(BaseModel): + """Finds text within a web page.""" + + pattern: Optional[str] = None + """The text pattern that was searched for.""" + + type: Literal["find_in_page"] + """The type of the object. Always `find_in_page`.""" + + url: Optional[str] = None + """The URL of the page that was searched.""" + + +class WebSearchActionResourceOther(BaseModel): + """Another web search action.""" + + type: Literal["other"] + """The type of the object. Always `other`.""" + + +WebSearchAction: TypeAlias = Annotated[ + Union[ + WebSearchActionResourceSearch, + WebSearchActionResourceOpenPage, + WebSearchActionResourceFindInPage, + WebSearchActionResourceOther, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__init__.py b/venv/lib/python3.12/site-packages/openai/types/chat/__init__.py new file mode 100644 index 0000000..4b64f36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/__init__.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .chat_completion import ChatCompletion as ChatCompletion +from .chat_completion_role import ChatCompletionRole as ChatCompletionRole +from .chat_completion_audio import ChatCompletionAudio as ChatCompletionAudio +from .chat_completion_chunk import ChatCompletionChunk as ChatCompletionChunk +from .completion_list_params import CompletionListParams as CompletionListParams +from .parsed_chat_completion import ( + ParsedChoice as ParsedChoice, + ParsedChatCompletion as ParsedChatCompletion, + ParsedChatCompletionMessage as ParsedChatCompletionMessage, +) +from .chat_completion_deleted import ChatCompletionDeleted as ChatCompletionDeleted +from .chat_completion_message import ChatCompletionMessage as ChatCompletionMessage +from .chat_completion_modality import ChatCompletionModality as ChatCompletionModality +from .completion_create_params import CompletionCreateParams as CompletionCreateParams +from .completion_update_params import CompletionUpdateParams as CompletionUpdateParams +from .parsed_function_tool_call import ( + ParsedFunction as ParsedFunction, + ParsedFunctionToolCall as ParsedFunctionToolCall, +) +from .chat_completion_tool_param import ChatCompletionToolParam as ChatCompletionToolParam +from .chat_completion_audio_param import ChatCompletionAudioParam as ChatCompletionAudioParam +from .chat_completion_function_tool import ChatCompletionFunctionTool as ChatCompletionFunctionTool +from .chat_completion_message_param import ChatCompletionMessageParam as ChatCompletionMessageParam +from .chat_completion_store_message import ChatCompletionStoreMessage as ChatCompletionStoreMessage +from .chat_completion_token_logprob import ChatCompletionTokenLogprob as ChatCompletionTokenLogprob +from .chat_completion_reasoning_effort import ChatCompletionReasoningEffort as ChatCompletionReasoningEffort +from .chat_completion_tool_union_param import ChatCompletionToolUnionParam as ChatCompletionToolUnionParam +from .chat_completion_content_part_text import ChatCompletionContentPartText as ChatCompletionContentPartText +from .chat_completion_custom_tool_param import ChatCompletionCustomToolParam as ChatCompletionCustomToolParam +from .chat_completion_message_tool_call import ( + ChatCompletionMessageToolCall as ChatCompletionMessageToolCall, + ChatCompletionMessageToolCallUnion as ChatCompletionMessageToolCallUnion, +) +from .chat_completion_content_part_image import ChatCompletionContentPartImage as ChatCompletionContentPartImage +from .chat_completion_content_part_param import ChatCompletionContentPartParam as ChatCompletionContentPartParam +from .chat_completion_tool_message_param import ChatCompletionToolMessageParam as ChatCompletionToolMessageParam +from .chat_completion_user_message_param import ChatCompletionUserMessageParam as ChatCompletionUserMessageParam +from .chat_completion_allowed_tools_param import ChatCompletionAllowedToolsParam as ChatCompletionAllowedToolsParam +from .chat_completion_function_tool_param import ChatCompletionFunctionToolParam as ChatCompletionFunctionToolParam +from .chat_completion_stream_options_param import ChatCompletionStreamOptionsParam as ChatCompletionStreamOptionsParam +from .chat_completion_system_message_param import ChatCompletionSystemMessageParam as ChatCompletionSystemMessageParam +from .chat_completion_function_message_param import ( + ChatCompletionFunctionMessageParam as ChatCompletionFunctionMessageParam, +) +from .chat_completion_assistant_message_param import ( + ChatCompletionAssistantMessageParam as ChatCompletionAssistantMessageParam, +) +from .chat_completion_content_part_text_param import ( + ChatCompletionContentPartTextParam as ChatCompletionContentPartTextParam, +) +from .chat_completion_developer_message_param import ( + ChatCompletionDeveloperMessageParam as ChatCompletionDeveloperMessageParam, +) +from .chat_completion_message_tool_call_param import ( + ChatCompletionMessageToolCallParam as ChatCompletionMessageToolCallParam, +) +from .chat_completion_named_tool_choice_param import ( + ChatCompletionNamedToolChoiceParam as ChatCompletionNamedToolChoiceParam, +) +from .chat_completion_content_part_image_param import ( + ChatCompletionContentPartImageParam as ChatCompletionContentPartImageParam, +) +from .chat_completion_message_custom_tool_call import ( + ChatCompletionMessageCustomToolCall as ChatCompletionMessageCustomToolCall, +) +from .chat_completion_prediction_content_param import ( + ChatCompletionPredictionContentParam as ChatCompletionPredictionContentParam, +) +from .chat_completion_tool_choice_option_param import ( + ChatCompletionToolChoiceOptionParam as ChatCompletionToolChoiceOptionParam, +) +from .chat_completion_allowed_tool_choice_param import ( + ChatCompletionAllowedToolChoiceParam as ChatCompletionAllowedToolChoiceParam, +) +from .chat_completion_content_part_refusal_param import ( + ChatCompletionContentPartRefusalParam as ChatCompletionContentPartRefusalParam, +) +from .chat_completion_function_call_option_param import ( + ChatCompletionFunctionCallOptionParam as ChatCompletionFunctionCallOptionParam, +) +from .chat_completion_message_function_tool_call import ( + ChatCompletionMessageFunctionToolCall as ChatCompletionMessageFunctionToolCall, +) +from .chat_completion_message_tool_call_union_param import ( + ChatCompletionMessageToolCallUnionParam as ChatCompletionMessageToolCallUnionParam, +) +from .chat_completion_content_part_input_audio_param import ( + ChatCompletionContentPartInputAudioParam as ChatCompletionContentPartInputAudioParam, +) +from .chat_completion_message_custom_tool_call_param import ( + ChatCompletionMessageCustomToolCallParam as ChatCompletionMessageCustomToolCallParam, +) +from .chat_completion_named_tool_choice_custom_param import ( + ChatCompletionNamedToolChoiceCustomParam as ChatCompletionNamedToolChoiceCustomParam, +) +from .chat_completion_message_function_tool_call_param import ( + ChatCompletionMessageFunctionToolCallParam as ChatCompletionMessageFunctionToolCallParam, +) diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2d98e49 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion.cpython-312.pyc new file mode 100644 index 0000000..f798dbd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_allowed_tool_choice_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_allowed_tool_choice_param.cpython-312.pyc new file mode 100644 index 0000000..76d07cb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_allowed_tool_choice_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_allowed_tools_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_allowed_tools_param.cpython-312.pyc new file mode 100644 index 0000000..382653c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_allowed_tools_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_assistant_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_assistant_message_param.cpython-312.pyc new file mode 100644 index 0000000..a34d54d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_assistant_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_audio.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_audio.cpython-312.pyc new file mode 100644 index 0000000..3b65f28 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_audio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_audio_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_audio_param.cpython-312.pyc new file mode 100644 index 0000000..57048c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_audio_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_chunk.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_chunk.cpython-312.pyc new file mode 100644 index 0000000..dd797eb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_chunk.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_image.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_image.cpython-312.pyc new file mode 100644 index 0000000..cb3250d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_image.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_image_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_image_param.cpython-312.pyc new file mode 100644 index 0000000..a77f781 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_image_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_input_audio_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_input_audio_param.cpython-312.pyc new file mode 100644 index 0000000..6c01e28 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_input_audio_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_param.cpython-312.pyc new file mode 100644 index 0000000..4056193 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_refusal_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_refusal_param.cpython-312.pyc new file mode 100644 index 0000000..667b00c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_refusal_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_text.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_text.cpython-312.pyc new file mode 100644 index 0000000..98b35c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_text_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_text_param.cpython-312.pyc new file mode 100644 index 0000000..9090f9f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_text_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_custom_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_custom_tool_param.cpython-312.pyc new file mode 100644 index 0000000..04c92d1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_custom_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_deleted.cpython-312.pyc new file mode 100644 index 0000000..c26050d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_developer_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_developer_message_param.cpython-312.pyc new file mode 100644 index 0000000..c0caaeb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_developer_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_function_call_option_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_function_call_option_param.cpython-312.pyc new file mode 100644 index 0000000..b1f7b7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_function_call_option_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_function_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_function_message_param.cpython-312.pyc new file mode 100644 index 0000000..17430df Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_function_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_function_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_function_tool.cpython-312.pyc new file mode 100644 index 0000000..3d58e25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_function_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_function_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_function_tool_param.cpython-312.pyc new file mode 100644 index 0000000..45ee134 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_function_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message.cpython-312.pyc new file mode 100644 index 0000000..985d94f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_custom_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_custom_tool_call.cpython-312.pyc new file mode 100644 index 0000000..5a2e593 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_custom_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_custom_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_custom_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..bfdcd59 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_custom_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_function_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_function_tool_call.cpython-312.pyc new file mode 100644 index 0000000..ac657f7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_function_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_function_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_function_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..e28744e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_function_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_param.cpython-312.pyc new file mode 100644 index 0000000..243fe3a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call.cpython-312.pyc new file mode 100644 index 0000000..92288f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..3721960 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call_union_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call_union_param.cpython-312.pyc new file mode 100644 index 0000000..3792b72 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call_union_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_modality.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_modality.cpython-312.pyc new file mode 100644 index 0000000..5de0ce8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_modality.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_named_tool_choice_custom_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_named_tool_choice_custom_param.cpython-312.pyc new file mode 100644 index 0000000..d4ac57c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_named_tool_choice_custom_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_named_tool_choice_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_named_tool_choice_param.cpython-312.pyc new file mode 100644 index 0000000..e07c0c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_named_tool_choice_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_prediction_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_prediction_content_param.cpython-312.pyc new file mode 100644 index 0000000..c600c48 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_prediction_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_reasoning_effort.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_reasoning_effort.cpython-312.pyc new file mode 100644 index 0000000..85a1481 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_reasoning_effort.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_role.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_role.cpython-312.pyc new file mode 100644 index 0000000..13acd73 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_role.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_store_message.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_store_message.cpython-312.pyc new file mode 100644 index 0000000..bed7f14 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_store_message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_stream_options_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_stream_options_param.cpython-312.pyc new file mode 100644 index 0000000..5528a1f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_stream_options_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_system_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_system_message_param.cpython-312.pyc new file mode 100644 index 0000000..016db49 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_system_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_token_logprob.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_token_logprob.cpython-312.pyc new file mode 100644 index 0000000..ad09200 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_token_logprob.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_tool_choice_option_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_tool_choice_option_param.cpython-312.pyc new file mode 100644 index 0000000..8af780e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_tool_choice_option_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_tool_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_tool_message_param.cpython-312.pyc new file mode 100644 index 0000000..df64ce2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_tool_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_tool_param.cpython-312.pyc new file mode 100644 index 0000000..2257e11 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_tool_union_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_tool_union_param.cpython-312.pyc new file mode 100644 index 0000000..a84f029 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_tool_union_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_user_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_user_message_param.cpython-312.pyc new file mode 100644 index 0000000..2a86df3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/chat_completion_user_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/completion_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/completion_create_params.cpython-312.pyc new file mode 100644 index 0000000..902ff5e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/completion_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/completion_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/completion_list_params.cpython-312.pyc new file mode 100644 index 0000000..2896a4a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/completion_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/completion_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/completion_update_params.cpython-312.pyc new file mode 100644 index 0000000..9fbf6a9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/completion_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/parsed_chat_completion.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/parsed_chat_completion.cpython-312.pyc new file mode 100644 index 0000000..c38feb1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/parsed_chat_completion.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/parsed_function_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/parsed_function_tool_call.cpython-312.pyc new file mode 100644 index 0000000..7f8d950 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/__pycache__/parsed_function_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion.py new file mode 100644 index 0000000..9372c4a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion.py @@ -0,0 +1,264 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from ..shared.metadata import Metadata +from ..completion_usage import CompletionUsage +from .chat_completion_message import ChatCompletionMessage +from .chat_completion_token_logprob import ChatCompletionTokenLogprob + +__all__ = [ + "ChatCompletion", + "Choice", + "ChoiceLogprobs", + "Moderation", + "ModerationInput", + "ModerationInputModerationResults", + "ModerationInputModerationResultsResult", + "ModerationInputError", + "ModerationOutput", + "ModerationOutputModerationResults", + "ModerationOutputModerationResultsResult", + "ModerationOutputError", +] + + +class ChoiceLogprobs(BaseModel): + """Log probability information for the choice.""" + + content: Optional[List[ChatCompletionTokenLogprob]] = None + """A list of message content tokens with log probability information.""" + + refusal: Optional[List[ChatCompletionTokenLogprob]] = None + """A list of message refusal tokens with log probability information.""" + + +class Choice(BaseModel): + finish_reason: Literal["stop", "length", "tool_calls", "content_filter", "function_call"] + """The reason the model stopped generating tokens. + + This will be `stop` if the model hit a natural stop point or a provided stop + sequence, `length` if the maximum number of tokens specified in the request was + reached, `content_filter` if content was omitted due to a flag from our content + filters, `tool_calls` if the model called a tool, or `function_call` + (deprecated) if the model called a function. Read the + [Model Spec](https://model-spec.openai.com/2025-12-18.html) for more. + """ + + index: int + """The index of the choice in the list of choices.""" + + logprobs: Optional[ChoiceLogprobs] = None + """Log probability information for the choice.""" + + message: ChatCompletionMessage + """A chat completion message generated by the model.""" + + +class ModerationInputModerationResultsResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationInputModerationResults(BaseModel): + """Successful moderation results for the request input or generated output.""" + + model: str + """The moderation model used to generate the results.""" + + results: List[ModerationInputModerationResultsResult] + """A list of moderation results.""" + + type: Literal["moderation_results"] + """The object type, which is always `moderation_results`.""" + + +class ModerationInputError(BaseModel): + """An error produced while attempting moderation.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which is always `error`.""" + + +ModerationInput: TypeAlias = Annotated[ + Union[ModerationInputModerationResults, ModerationInputError], PropertyInfo(discriminator="type") +] + + +class ModerationOutputModerationResultsResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationOutputModerationResults(BaseModel): + """Successful moderation results for the request input or generated output.""" + + model: str + """The moderation model used to generate the results.""" + + results: List[ModerationOutputModerationResultsResult] + """A list of moderation results.""" + + type: Literal["moderation_results"] + """The object type, which is always `moderation_results`.""" + + +class ModerationOutputError(BaseModel): + """An error produced while attempting moderation.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which is always `error`.""" + + +ModerationOutput: TypeAlias = Annotated[ + Union[ModerationOutputModerationResults, ModerationOutputError], PropertyInfo(discriminator="type") +] + + +class Moderation(BaseModel): + """ + Moderation results for the request input and generated output, if moderated + completions were requested. + """ + + input: ModerationInput + """Moderation for the request input.""" + + output: ModerationOutput + """Moderation for the generated output.""" + + +class ChatCompletion(BaseModel): + """ + Represents a chat completion response returned by model, based on the provided input. + """ + + id: str + """A unique identifier for the chat completion.""" + + choices: List[Choice] + """A list of chat completion choices. + + Can be more than one if `n` is greater than 1. + """ + + created: int + """The Unix timestamp (in seconds) of when the chat completion was created.""" + + model: str + """The model used for the chat completion.""" + + object: Literal["chat.completion"] + """The object type, which is always `chat.completion`.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + moderation: Optional[Moderation] = None + """ + Moderation results for the request input and generated output, if moderated + completions were requested. + """ + + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] = None + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + system_fingerprint: Optional[str] = None + """This fingerprint represents the backend configuration that the model runs with. + + Can be used in conjunction with the `seed` request parameter to understand when + backend changes have been made that might impact determinism. + """ + + usage: Optional[CompletionUsage] = None + """Usage statistics for the completion request.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_allowed_tool_choice_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_allowed_tool_choice_param.py new file mode 100644 index 0000000..6c963ec --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_allowed_tool_choice_param.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .chat_completion_allowed_tools_param import ChatCompletionAllowedToolsParam + +__all__ = ["ChatCompletionAllowedToolChoiceParam"] + + +class ChatCompletionAllowedToolChoiceParam(TypedDict, total=False): + """Constrains the tools available to the model to a pre-defined set.""" + + allowed_tools: Required[ChatCompletionAllowedToolsParam] + """Constrains the tools available to the model to a pre-defined set.""" + + type: Required[Literal["allowed_tools"]] + """Allowed tool configuration type. Always `allowed_tools`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_allowed_tools_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_allowed_tools_param.py new file mode 100644 index 0000000..24ddced --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_allowed_tools_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionAllowedToolsParam"] + + +class ChatCompletionAllowedToolsParam(TypedDict, total=False): + """Constrains the tools available to the model to a pre-defined set.""" + + mode: Required[Literal["auto", "required"]] + """Constrains the tools available to the model to a pre-defined set. + + `auto` allows the model to pick from among the allowed tools and generate a + message. + + `required` requires the model to call one or more of the allowed tools. + """ + + tools: Required[Iterable[Dict[str, object]]] + """A list of tool definitions that the model should be allowed to call. + + For the Chat Completions API, the list of tool definitions might look like: + + ```json + [ + { "type": "function", "function": { "name": "get_weather" } }, + { "type": "function", "function": { "name": "get_time" } } + ] + ``` + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_assistant_message_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_assistant_message_param.py new file mode 100644 index 0000000..0bf29fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_assistant_message_param.py @@ -0,0 +1,82 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam +from .chat_completion_content_part_refusal_param import ChatCompletionContentPartRefusalParam +from .chat_completion_message_tool_call_union_param import ChatCompletionMessageToolCallUnionParam + +__all__ = ["ChatCompletionAssistantMessageParam", "Audio", "ContentArrayOfContentPart", "FunctionCall"] + + +class Audio(TypedDict, total=False): + """ + Data about a previous audio response from the model. + [Learn more](https://developers.openai.com/api/docs/guides/audio). + """ + + id: Required[str] + """Unique identifier for a previous audio response from the model.""" + + +ContentArrayOfContentPart: TypeAlias = Union[ChatCompletionContentPartTextParam, ChatCompletionContentPartRefusalParam] + + +class FunctionCall(TypedDict, total=False): + """Deprecated and replaced by `tool_calls`. + + The name and arguments of a function that should be called, as generated by the model. + """ + + arguments: Required[str] + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: Required[str] + """The name of the function to call.""" + + +class ChatCompletionAssistantMessageParam(TypedDict, total=False): + """Messages sent by the model in response to user messages.""" + + role: Required[Literal["assistant"]] + """The role of the messages author, in this case `assistant`.""" + + audio: Optional[Audio] + """ + Data about a previous audio response from the model. + [Learn more](https://developers.openai.com/api/docs/guides/audio). + """ + + content: Union[str, Iterable[ContentArrayOfContentPart], None] + """The contents of the assistant message. + + Required unless `tool_calls` or `function_call` is specified. + """ + + function_call: Optional[FunctionCall] + """Deprecated and replaced by `tool_calls`. + + The name and arguments of a function that should be called, as generated by the + model. + """ + + name: str + """An optional name for the participant. + + Provides the model information to differentiate between participants of the same + role. + """ + + refusal: Optional[str] + """The refusal message by the assistant.""" + + tool_calls: Iterable[ChatCompletionMessageToolCallUnionParam] + """The tool calls generated by the model, such as function calls.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_audio.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_audio.py new file mode 100644 index 0000000..f15fdbc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_audio.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["ChatCompletionAudio"] + + +class ChatCompletionAudio(BaseModel): + """ + If the audio output modality is requested, this object contains data + about the audio response from the model. [Learn more](https://developers.openai.com/api/docs/guides/audio). + """ + + id: str + """Unique identifier for this audio response.""" + + data: str + """ + Base64 encoded audio bytes generated by the model, in the format specified in + the request. + """ + + expires_at: int + """ + The Unix timestamp (in seconds) for when this audio response will no longer be + accessible on the server for use in multi-turn conversations. + """ + + transcript: str + """Transcript of the audio generated by the model.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_audio_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_audio_param.py new file mode 100644 index 0000000..078e601 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_audio_param.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["ChatCompletionAudioParam", "Voice", "VoiceID"] + + +class VoiceID(TypedDict, total=False): + """Custom voice reference.""" + + id: Required[str] + """The custom voice ID, e.g. `voice_1234`.""" + + +Voice: TypeAlias = Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], VoiceID +] + + +class ChatCompletionAudioParam(TypedDict, total=False): + """Parameters for audio output. + + Required when audio output is requested with + `modalities: ["audio"]`. [Learn more](https://developers.openai.com/api/docs/guides/audio). + """ + + format: Required[Literal["wav", "aac", "mp3", "flac", "opus", "pcm16"]] + """Specifies the output audio format. + + Must be one of `wav`, `mp3`, `flac`, `opus`, or `pcm16`. + """ + + voice: Required[Voice] + """The voice the model uses to respond. + + Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, + `fable`, `nova`, `onyx`, `sage`, `shimmer`, `marin`, and `cedar`. You may also + provide a custom voice object with an `id`, for example + `{ "id": "voice_1234" }`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_chunk.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_chunk.py new file mode 100644 index 0000000..fd23828 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_chunk.py @@ -0,0 +1,342 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from ..completion_usage import CompletionUsage +from .chat_completion_token_logprob import ChatCompletionTokenLogprob + +__all__ = [ + "ChatCompletionChunk", + "Choice", + "ChoiceDelta", + "ChoiceDeltaFunctionCall", + "ChoiceDeltaToolCall", + "ChoiceDeltaToolCallFunction", + "ChoiceLogprobs", + "Moderation", + "ModerationInput", + "ModerationInputModerationResults", + "ModerationInputModerationResultsResult", + "ModerationInputError", + "ModerationOutput", + "ModerationOutputModerationResults", + "ModerationOutputModerationResultsResult", + "ModerationOutputError", +] + + +class ChoiceDeltaFunctionCall(BaseModel): + """Deprecated and replaced by `tool_calls`. + + The name and arguments of a function that should be called, as generated by the model. + """ + + arguments: Optional[str] = None + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: Optional[str] = None + """The name of the function to call.""" + + +class ChoiceDeltaToolCallFunction(BaseModel): + arguments: Optional[str] = None + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: Optional[str] = None + """The name of the function to call.""" + + +class ChoiceDeltaToolCall(BaseModel): + index: int + + id: Optional[str] = None + """The ID of the tool call.""" + + function: Optional[ChoiceDeltaToolCallFunction] = None + + type: Optional[Literal["function"]] = None + """The type of the tool. Currently, only `function` is supported.""" + + +class ChoiceDelta(BaseModel): + """A chat completion delta generated by streamed model responses.""" + + content: Optional[str] = None + """The contents of the chunk message.""" + + function_call: Optional[ChoiceDeltaFunctionCall] = None + """Deprecated and replaced by `tool_calls`. + + The name and arguments of a function that should be called, as generated by the + model. + """ + + refusal: Optional[str] = None + """The refusal message generated by the model.""" + + role: Optional[Literal["developer", "system", "user", "assistant", "tool"]] = None + """The role of the author of this message.""" + + tool_calls: Optional[List[ChoiceDeltaToolCall]] = None + + +class ChoiceLogprobs(BaseModel): + """Log probability information for the choice.""" + + content: Optional[List[ChatCompletionTokenLogprob]] = None + """A list of message content tokens with log probability information.""" + + refusal: Optional[List[ChatCompletionTokenLogprob]] = None + """A list of message refusal tokens with log probability information.""" + + +class Choice(BaseModel): + delta: ChoiceDelta + """A chat completion delta generated by streamed model responses.""" + + finish_reason: Optional[Literal["stop", "length", "tool_calls", "content_filter", "function_call"]] = None + """The reason the model stopped generating tokens. + + This will be `stop` if the model hit a natural stop point or a provided stop + sequence, `length` if the maximum number of tokens specified in the request was + reached, `content_filter` if content was omitted due to a flag from our content + filters, `tool_calls` if the model called a tool, or `function_call` + (deprecated) if the model called a function. + """ + + index: int + """The index of the choice in the list of choices.""" + + logprobs: Optional[ChoiceLogprobs] = None + """Log probability information for the choice.""" + + +class ModerationInputModerationResultsResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationInputModerationResults(BaseModel): + """Successful moderation results for the request input or generated output.""" + + model: str + """The moderation model used to generate the results.""" + + results: List[ModerationInputModerationResultsResult] + """A list of moderation results.""" + + type: Literal["moderation_results"] + """The object type, which is always `moderation_results`.""" + + +class ModerationInputError(BaseModel): + """An error produced while attempting moderation.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which is always `error`.""" + + +ModerationInput: TypeAlias = Annotated[ + Union[ModerationInputModerationResults, ModerationInputError], PropertyInfo(discriminator="type") +] + + +class ModerationOutputModerationResultsResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationOutputModerationResults(BaseModel): + """Successful moderation results for the request input or generated output.""" + + model: str + """The moderation model used to generate the results.""" + + results: List[ModerationOutputModerationResultsResult] + """A list of moderation results.""" + + type: Literal["moderation_results"] + """The object type, which is always `moderation_results`.""" + + +class ModerationOutputError(BaseModel): + """An error produced while attempting moderation.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which is always `error`.""" + + +ModerationOutput: TypeAlias = Annotated[ + Union[ModerationOutputModerationResults, ModerationOutputError], PropertyInfo(discriminator="type") +] + + +class Moderation(BaseModel): + """Moderation results for the request input and generated output. + + Present + on the moderation chunk when moderated completions are requested. + """ + + input: ModerationInput + """Moderation for the request input.""" + + output: ModerationOutput + """Moderation for the generated output.""" + + +class ChatCompletionChunk(BaseModel): + """ + Represents a streamed chunk of a chat completion response returned + by the model, based on the provided input. + [Learn more](https://developers.openai.com/api/docs/guides/streaming-responses). + """ + + id: str + """A unique identifier for the chat completion. Each chunk has the same ID.""" + + choices: List[Choice] + """A list of chat completion choices. + + Can contain more than one elements if `n` is greater than 1. Can also be empty + for the last chunk if you set `stream_options: {"include_usage": true}`. + """ + + created: int + """The Unix timestamp (in seconds) of when the chat completion was created. + + Each chunk has the same timestamp. + """ + + model: str + """The model to generate the completion.""" + + object: Literal["chat.completion.chunk"] + """The object type, which is always `chat.completion.chunk`.""" + + moderation: Optional[Moderation] = None + """Moderation results for the request input and generated output. + + Present on the moderation chunk when moderated completions are requested. + """ + + obfuscation: Optional[str] = None + """ + An obfuscation string added to normalize the size of streamed chunks as a + mitigation to certain side-channel attacks. The field is included by default and + omitted when `stream_options.include_obfuscation` is `false`. + """ + + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] = None + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + system_fingerprint: Optional[str] = None + """ + This fingerprint represents the backend configuration that the model runs with. + Can be used in conjunction with the `seed` request parameter to understand when + backend changes have been made that might impact determinism. + """ + + usage: Optional[CompletionUsage] = None + """ + An optional field that will only be present when you set + `stream_options: {"include_usage": true}` in your request. When present, it + contains a null value **except for the last chunk** which contains the token + usage statistics for the entire request. + + **NOTE:** If the stream is interrupted or cancelled, you may not receive the + final usage chunk which contains the total token usage for the request. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_image.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_image.py new file mode 100644 index 0000000..3560848 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_image.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ChatCompletionContentPartImage", "ImageURL", "PromptCacheBreakpoint"] + + +class ImageURL(BaseModel): + url: str + """Either a URL of the image or the base64 encoded image data.""" + + detail: Optional[Literal["auto", "low", "high"]] = None + """Specifies the detail level of the image. + + Learn more in the + [Vision guide](https://developers.openai.com/api/docs/guides/images-vision#choose-an-image-detail-level). + """ + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class ChatCompletionContentPartImage(BaseModel): + """ + Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision). + """ + + image_url: ImageURL + + type: Literal["image_url"] + """The type of the content part.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_image_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_image_param.py new file mode 100644 index 0000000..8baace4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_image_param.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionContentPartImageParam", "ImageURL", "PromptCacheBreakpoint"] + + +class ImageURL(TypedDict, total=False): + url: Required[str] + """Either a URL of the image or the base64 encoded image data.""" + + detail: Literal["auto", "low", "high"] + """Specifies the detail level of the image. + + Learn more in the + [Vision guide](https://developers.openai.com/api/docs/guides/images-vision#choose-an-image-detail-level). + """ + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class ChatCompletionContentPartImageParam(TypedDict, total=False): + """ + Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision). + """ + + image_url: Required[ImageURL] + + type: Required[Literal["image_url"]] + """The type of the content part.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_input_audio_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_input_audio_param.py new file mode 100644 index 0000000..e14bf35 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_input_audio_param.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionContentPartInputAudioParam", "InputAudio", "PromptCacheBreakpoint"] + + +class InputAudio(TypedDict, total=False): + data: Required[str] + """Base64 encoded audio data.""" + + format: Required[Literal["wav", "mp3"]] + """The format of the encoded audio data. Currently supports "wav" and "mp3".""" + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class ChatCompletionContentPartInputAudioParam(TypedDict, total=False): + """ + Learn about [audio inputs](https://developers.openai.com/api/docs/guides/audio). + """ + + input_audio: Required[InputAudio] + + type: Required[Literal["input_audio"]] + """The type of the content part. Always `input_audio`.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_param.py new file mode 100644 index 0000000..cc34b7c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_param.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam +from .chat_completion_content_part_image_param import ChatCompletionContentPartImageParam +from .chat_completion_content_part_input_audio_param import ChatCompletionContentPartInputAudioParam + +__all__ = ["ChatCompletionContentPartParam", "File", "FileFile", "FilePromptCacheBreakpoint"] + + +class FileFile(TypedDict, total=False): + file_data: str + """ + The base64 encoded file data, used when passing the file to the model as a + string. + """ + + file_id: str + """The ID of an uploaded file to use as input.""" + + filename: str + """The name of the file, used when passing the file to the model as a string.""" + + +class FilePromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class File(TypedDict, total=False): + """ + Learn about [file inputs](https://developers.openai.com/api/docs/guides/text) for text generation. + """ + + file: Required[FileFile] + + type: Required[Literal["file"]] + """The type of the content part. Always `file`.""" + + prompt_cache_breakpoint: FilePromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ + + +ChatCompletionContentPartParam: TypeAlias = Union[ + ChatCompletionContentPartTextParam, + ChatCompletionContentPartImageParam, + ChatCompletionContentPartInputAudioParam, + File, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_refusal_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_refusal_param.py new file mode 100644 index 0000000..b32561b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_refusal_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionContentPartRefusalParam"] + + +class ChatCompletionContentPartRefusalParam(TypedDict, total=False): + refusal: Required[str] + """The refusal message generated by the model.""" + + type: Required[Literal["refusal"]] + """The type of the content part.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_text.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_text.py new file mode 100644 index 0000000..590b95a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_text.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ChatCompletionContentPartText", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class ChatCompletionContentPartText(BaseModel): + """Learn about [text inputs](https://developers.openai.com/api/docs/guides/text).""" + + text: str + """The text content.""" + + type: Literal["text"] + """The type of the content part.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_text_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_text_param.py new file mode 100644 index 0000000..adee12e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_content_part_text_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionContentPartTextParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class ChatCompletionContentPartTextParam(TypedDict, total=False): + """Learn about [text inputs](https://developers.openai.com/api/docs/guides/text).""" + + text: Required[str] + """The text content.""" + + type: Required[Literal["text"]] + """The type of the content part.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_custom_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_custom_tool_param.py new file mode 100644 index 0000000..4ad9c1c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_custom_tool_param.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "ChatCompletionCustomToolParam", + "Custom", + "CustomFormat", + "CustomFormatText", + "CustomFormatGrammar", + "CustomFormatGrammarGrammar", +] + + +class CustomFormatText(TypedDict, total=False): + """Unconstrained free-form text.""" + + type: Required[Literal["text"]] + """Unconstrained text format. Always `text`.""" + + +class CustomFormatGrammarGrammar(TypedDict, total=False): + """Your chosen grammar.""" + + definition: Required[str] + """The grammar definition.""" + + syntax: Required[Literal["lark", "regex"]] + """The syntax of the grammar definition. One of `lark` or `regex`.""" + + +class CustomFormatGrammar(TypedDict, total=False): + """A grammar defined by the user.""" + + grammar: Required[CustomFormatGrammarGrammar] + """Your chosen grammar.""" + + type: Required[Literal["grammar"]] + """Grammar format. Always `grammar`.""" + + +CustomFormat: TypeAlias = Union[CustomFormatText, CustomFormatGrammar] + + +class Custom(TypedDict, total=False): + """Properties of the custom tool.""" + + name: Required[str] + """The name of the custom tool, used to identify it in tool calls.""" + + description: str + """Optional description of the custom tool, used to provide more context.""" + + format: CustomFormat + """The input format for the custom tool. Default is unconstrained text.""" + + +class ChatCompletionCustomToolParam(TypedDict, total=False): + """A custom tool that processes input using a specified format.""" + + custom: Required[Custom] + """Properties of the custom tool.""" + + type: Required[Literal["custom"]] + """The type of the custom tool. Always `custom`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_deleted.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_deleted.py new file mode 100644 index 0000000..66c23e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_deleted.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ChatCompletionDeleted"] + + +class ChatCompletionDeleted(BaseModel): + id: str + """The ID of the chat completion that was deleted.""" + + deleted: bool + """Whether the chat completion was deleted.""" + + object: Literal["chat.completion.deleted"] + """The type of object being deleted.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_developer_message_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_developer_message_param.py new file mode 100644 index 0000000..af8f754 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_developer_message_param.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypedDict + +from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam + +__all__ = ["ChatCompletionDeveloperMessageParam"] + + +class ChatCompletionDeveloperMessageParam(TypedDict, total=False): + """ + Developer-provided instructions that the model should follow, regardless of + messages sent by the user. With o1 models and newer, `developer` messages + replace the previous `system` messages. + """ + + content: Required[Union[str, Iterable[ChatCompletionContentPartTextParam]]] + """The contents of the developer message.""" + + role: Required[Literal["developer"]] + """The role of the messages author, in this case `developer`.""" + + name: str + """An optional name for the participant. + + Provides the model information to differentiate between participants of the same + role. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_function_call_option_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_function_call_option_param.py new file mode 100644 index 0000000..f23e4fc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_function_call_option_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ChatCompletionFunctionCallOptionParam"] + + +class ChatCompletionFunctionCallOptionParam(TypedDict, total=False): + """ + Specifying a particular function via `{"name": "my_function"}` forces the model to call that function. + """ + + name: Required[str] + """The name of the function to call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_function_message_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_function_message_param.py new file mode 100644 index 0000000..aead3c3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_function_message_param.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionFunctionMessageParam"] + + +class ChatCompletionFunctionMessageParam(TypedDict, total=False): + content: Required[Optional[str]] + """The contents of the function message.""" + + name: Required[str] + """The name of the function to call.""" + + role: Required[Literal["function"]] + """The role of the messages author, in this case `function`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_function_tool.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_function_tool.py new file mode 100644 index 0000000..1e76bf6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_function_tool.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from ..shared.function_definition import FunctionDefinition + +__all__ = ["ChatCompletionFunctionTool"] + + +class ChatCompletionFunctionTool(BaseModel): + """A function tool that can be used to generate a response.""" + + function: FunctionDefinition + + type: Literal["function"] + """The type of the tool. Currently, only `function` is supported.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_function_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_function_tool_param.py new file mode 100644 index 0000000..11bee79 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_function_tool_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from ..shared_params.function_definition import FunctionDefinition + +__all__ = ["ChatCompletionFunctionToolParam"] + + +class ChatCompletionFunctionToolParam(TypedDict, total=False): + """A function tool that can be used to generate a response.""" + + function: Required[FunctionDefinition] + + type: Required[Literal["function"]] + """The type of the tool. Currently, only `function` is supported.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message.py new file mode 100644 index 0000000..a3064da --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message.py @@ -0,0 +1,90 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .chat_completion_audio import ChatCompletionAudio +from .chat_completion_message_tool_call import ChatCompletionMessageToolCallUnion + +__all__ = ["ChatCompletionMessage", "Annotation", "AnnotationURLCitation", "FunctionCall"] + + +class AnnotationURLCitation(BaseModel): + """A URL citation when using web search.""" + + end_index: int + """The index of the last character of the URL citation in the message.""" + + start_index: int + """The index of the first character of the URL citation in the message.""" + + title: str + """The title of the web resource.""" + + url: str + """The URL of the web resource.""" + + +class Annotation(BaseModel): + """A URL citation when using web search.""" + + type: Literal["url_citation"] + """The type of the URL citation. Always `url_citation`.""" + + url_citation: AnnotationURLCitation + """A URL citation when using web search.""" + + +class FunctionCall(BaseModel): + """Deprecated and replaced by `tool_calls`. + + The name and arguments of a function that should be called, as generated by the model. + """ + + arguments: str + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: str + """The name of the function to call.""" + + +class ChatCompletionMessage(BaseModel): + """A chat completion message generated by the model.""" + + content: Optional[str] = None + """The contents of the message.""" + + refusal: Optional[str] = None + """The refusal message generated by the model.""" + + role: Literal["assistant"] + """The role of the author of this message.""" + + annotations: Optional[List[Annotation]] = None + """ + Annotations for the message, when applicable, as when using the + [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + """ + + audio: Optional[ChatCompletionAudio] = None + """ + If the audio output modality is requested, this object contains data about the + audio response from the model. + [Learn more](https://developers.openai.com/api/docs/guides/audio). + """ + + function_call: Optional[FunctionCall] = None + """Deprecated and replaced by `tool_calls`. + + The name and arguments of a function that should be called, as generated by the + model. + """ + + tool_calls: Optional[List[ChatCompletionMessageToolCallUnion]] = None + """The tool calls generated by the model, such as function calls.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_custom_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_custom_tool_call.py new file mode 100644 index 0000000..93bf679 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_custom_tool_call.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ChatCompletionMessageCustomToolCall", "Custom"] + + +class Custom(BaseModel): + """The custom tool that the model called.""" + + input: str + """The input for the custom tool call generated by the model.""" + + name: str + """The name of the custom tool to call.""" + + +class ChatCompletionMessageCustomToolCall(BaseModel): + """A call to a custom tool created by the model.""" + + id: str + """The ID of the tool call.""" + + custom: Custom + """The custom tool that the model called.""" + + type: Literal["custom"] + """The type of the tool. Always `custom`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_custom_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_custom_tool_call_param.py new file mode 100644 index 0000000..9c18d93 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_custom_tool_call_param.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionMessageCustomToolCallParam", "Custom"] + + +class Custom(TypedDict, total=False): + """The custom tool that the model called.""" + + input: Required[str] + """The input for the custom tool call generated by the model.""" + + name: Required[str] + """The name of the custom tool to call.""" + + +class ChatCompletionMessageCustomToolCallParam(TypedDict, total=False): + """A call to a custom tool created by the model.""" + + id: Required[str] + """The ID of the tool call.""" + + custom: Required[Custom] + """The custom tool that the model called.""" + + type: Required[Literal["custom"]] + """The type of the tool. Always `custom`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_function_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_function_tool_call.py new file mode 100644 index 0000000..6c4fba2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_function_tool_call.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ChatCompletionMessageFunctionToolCall", "Function"] + + +class Function(BaseModel): + """The function that the model called.""" + + arguments: str + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: str + """The name of the function to call.""" + + +class ChatCompletionMessageFunctionToolCall(BaseModel): + """A call to a function tool created by the model.""" + + id: str + """The ID of the tool call.""" + + function: Function + """The function that the model called.""" + + type: Literal["function"] + """The type of the tool. Currently, only `function` is supported.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_function_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_function_tool_call_param.py new file mode 100644 index 0000000..efaf9f9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_function_tool_call_param.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionMessageFunctionToolCallParam", "Function"] + + +class Function(TypedDict, total=False): + """The function that the model called.""" + + arguments: Required[str] + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: Required[str] + """The name of the function to call.""" + + +class ChatCompletionMessageFunctionToolCallParam(TypedDict, total=False): + """A call to a function tool created by the model.""" + + id: Required[str] + """The ID of the tool call.""" + + function: Required[Function] + """The function that the model called.""" + + type: Required[Literal["function"]] + """The type of the tool. Currently, only `function` is supported.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_param.py new file mode 100644 index 0000000..fc7a0b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .chat_completion_tool_message_param import ChatCompletionToolMessageParam +from .chat_completion_user_message_param import ChatCompletionUserMessageParam +from .chat_completion_system_message_param import ChatCompletionSystemMessageParam +from .chat_completion_function_message_param import ChatCompletionFunctionMessageParam +from .chat_completion_assistant_message_param import ChatCompletionAssistantMessageParam +from .chat_completion_developer_message_param import ChatCompletionDeveloperMessageParam + +__all__ = ["ChatCompletionMessageParam"] + +ChatCompletionMessageParam: TypeAlias = Union[ + ChatCompletionDeveloperMessageParam, + ChatCompletionSystemMessageParam, + ChatCompletionUserMessageParam, + ChatCompletionAssistantMessageParam, + ChatCompletionToolMessageParam, + ChatCompletionFunctionMessageParam, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_tool_call.py new file mode 100644 index 0000000..e876ac7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_tool_call.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .chat_completion_message_custom_tool_call import ChatCompletionMessageCustomToolCall +from .chat_completion_message_function_tool_call import Function as Function, ChatCompletionMessageFunctionToolCall + +__all__ = ["Function", "ChatCompletionMessageToolCallUnion"] + +ChatCompletionMessageToolCallUnion: TypeAlias = Annotated[ + Union[ChatCompletionMessageFunctionToolCall, ChatCompletionMessageCustomToolCall], + PropertyInfo(discriminator="type"), +] + +ChatCompletionMessageToolCall: TypeAlias = ChatCompletionMessageFunctionToolCall diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_tool_call_param.py new file mode 100644 index 0000000..fedf615 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_tool_call_param.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +from typing_extensions import TypeAlias + +from .chat_completion_message_function_tool_call_param import ( + Function as Function, + ChatCompletionMessageFunctionToolCallParam, +) + +__all__ = ["ChatCompletionMessageToolCallParam", "Function"] + +ChatCompletionMessageToolCallParam: TypeAlias = ChatCompletionMessageFunctionToolCallParam diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_tool_call_union_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_tool_call_union_param.py new file mode 100644 index 0000000..7baef8e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_message_tool_call_union_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .chat_completion_message_custom_tool_call_param import ChatCompletionMessageCustomToolCallParam +from .chat_completion_message_function_tool_call_param import ChatCompletionMessageFunctionToolCallParam + +__all__ = ["ChatCompletionMessageToolCallUnionParam"] + +ChatCompletionMessageToolCallUnionParam: TypeAlias = Union[ + ChatCompletionMessageFunctionToolCallParam, ChatCompletionMessageCustomToolCallParam +] diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_modality.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_modality.py new file mode 100644 index 0000000..754f810 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_modality.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ChatCompletionModality"] + +ChatCompletionModality: TypeAlias = Literal["text", "audio"] diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_named_tool_choice_custom_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_named_tool_choice_custom_param.py new file mode 100644 index 0000000..d3b174e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_named_tool_choice_custom_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionNamedToolChoiceCustomParam", "Custom"] + + +class Custom(TypedDict, total=False): + name: Required[str] + """The name of the custom tool to call.""" + + +class ChatCompletionNamedToolChoiceCustomParam(TypedDict, total=False): + """Specifies a tool the model should use. + + Use to force the model to call a specific custom tool. + """ + + custom: Required[Custom] + + type: Required[Literal["custom"]] + """For custom tool calling, the type is always `custom`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_named_tool_choice_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_named_tool_choice_param.py new file mode 100644 index 0000000..ebe8a01 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_named_tool_choice_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionNamedToolChoiceParam", "Function"] + + +class Function(TypedDict, total=False): + name: Required[str] + """The name of the function to call.""" + + +class ChatCompletionNamedToolChoiceParam(TypedDict, total=False): + """Specifies a tool the model should use. + + Use to force the model to call a specific function. + """ + + function: Required[Function] + + type: Required[Literal["function"]] + """For function calling, the type is always `function`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_prediction_content_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_prediction_content_param.py new file mode 100644 index 0000000..94d3c39 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_prediction_content_param.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypedDict + +from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam + +__all__ = ["ChatCompletionPredictionContentParam"] + + +class ChatCompletionPredictionContentParam(TypedDict, total=False): + """ + Static predicted output content, such as the content of a text file that is + being regenerated. + """ + + content: Required[Union[str, Iterable[ChatCompletionContentPartTextParam]]] + """ + The content that should be matched when generating a model response. If + generated tokens would match this content, the entire model response can be + returned much more quickly. + """ + + type: Required[Literal["content"]] + """The type of the predicted content you want to provide. + + This type is currently always `content`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_reasoning_effort.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_reasoning_effort.py new file mode 100644 index 0000000..4c504d1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_reasoning_effort.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..shared.reasoning_effort import ReasoningEffort + +__all__ = ["ChatCompletionReasoningEffort"] + +ChatCompletionReasoningEffort = ReasoningEffort diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_role.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_role.py new file mode 100644 index 0000000..4acbf66 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_role.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ChatCompletionRole"] + +ChatCompletionRole: TypeAlias = Literal["developer", "system", "user", "assistant", "tool", "function"] diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_store_message.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_store_message.py new file mode 100644 index 0000000..66eea9d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_store_message.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import TypeAlias + +from .chat_completion_message import ChatCompletionMessage +from .chat_completion_content_part_text import ChatCompletionContentPartText +from .chat_completion_content_part_image import ChatCompletionContentPartImage + +__all__ = ["ChatCompletionStoreMessage", "ChatCompletionStoreMessageContentPart"] + +ChatCompletionStoreMessageContentPart: TypeAlias = Union[ChatCompletionContentPartText, ChatCompletionContentPartImage] + + +class ChatCompletionStoreMessage(ChatCompletionMessage): + """A chat completion message generated by the model.""" + + id: str + """The identifier of the chat message.""" + + content_parts: Optional[List[ChatCompletionStoreMessageContentPart]] = None + """ + If a content parts array was provided, this is an array of `text` and + `image_url` parts. Otherwise, null. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_stream_options_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_stream_options_param.py new file mode 100644 index 0000000..3cd9441 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_stream_options_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ChatCompletionStreamOptionsParam"] + + +class ChatCompletionStreamOptionsParam(TypedDict, total=False): + """Options for streaming response. Only set this when you set `stream: true`.""" + + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + include_usage: bool + """If set, an additional chunk will be streamed before the `data: [DONE]` message. + + The `usage` field on this chunk shows the token usage statistics for the entire + request, and the `choices` field will always be an empty array. + + All other chunks will also include a `usage` field, but with a null value. + **NOTE:** If the stream is interrupted, you may not receive the final usage + chunk which contains the total token usage for the request. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_system_message_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_system_message_param.py new file mode 100644 index 0000000..cbdc841 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_system_message_param.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypedDict + +from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam + +__all__ = ["ChatCompletionSystemMessageParam"] + + +class ChatCompletionSystemMessageParam(TypedDict, total=False): + """ + Developer-provided instructions that the model should follow, regardless of + messages sent by the user. With o1 models and newer, use `developer` messages + for this purpose instead. + """ + + content: Required[Union[str, Iterable[ChatCompletionContentPartTextParam]]] + """The contents of the system message.""" + + role: Required[Literal["system"]] + """The role of the messages author, in this case `system`.""" + + name: str + """An optional name for the participant. + + Provides the model information to differentiate between participants of the same + role. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_token_logprob.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_token_logprob.py new file mode 100644 index 0000000..80352a4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_token_logprob.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel + +__all__ = ["ChatCompletionTokenLogprob", "TopLogprob"] + + +class TopLogprob(BaseModel): + token: str + """The token.""" + + bytes: Optional[List[int]] = None + """A list of integers representing the UTF-8 bytes representation of the token. + + Useful in instances where characters are represented by multiple tokens and + their byte representations must be combined to generate the correct text + representation. Can be `null` if there is no bytes representation for the token. + """ + + logprob: float + """The log probability of this token, if it is within the top 20 most likely + tokens. + + Otherwise, the value `-9999.0` is used to signify that the token is very + unlikely. + """ + + +class ChatCompletionTokenLogprob(BaseModel): + token: str + """The token.""" + + bytes: Optional[List[int]] = None + """A list of integers representing the UTF-8 bytes representation of the token. + + Useful in instances where characters are represented by multiple tokens and + their byte representations must be combined to generate the correct text + representation. Can be `null` if there is no bytes representation for the token. + """ + + logprob: float + """The log probability of this token, if it is within the top 20 most likely + tokens. + + Otherwise, the value `-9999.0` is used to signify that the token is very + unlikely. + """ + + top_logprobs: List[TopLogprob] + """List of the most likely tokens and their log probability, at this token + position. + + The number of entries may be fewer than the requested `top_logprobs`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_tool_choice_option_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_tool_choice_option_param.py new file mode 100644 index 0000000..93f2153 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_tool_choice_option_param.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from .chat_completion_named_tool_choice_param import ChatCompletionNamedToolChoiceParam +from .chat_completion_allowed_tool_choice_param import ChatCompletionAllowedToolChoiceParam +from .chat_completion_named_tool_choice_custom_param import ChatCompletionNamedToolChoiceCustomParam + +__all__ = ["ChatCompletionToolChoiceOptionParam"] + +ChatCompletionToolChoiceOptionParam: TypeAlias = Union[ + Literal["none", "auto", "required"], + ChatCompletionAllowedToolChoiceParam, + ChatCompletionNamedToolChoiceParam, + ChatCompletionNamedToolChoiceCustomParam, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_tool_message_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_tool_message_param.py new file mode 100644 index 0000000..2a9427e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_tool_message_param.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypedDict + +from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam + +__all__ = ["ChatCompletionToolMessageParam"] + + +class ChatCompletionToolMessageParam(TypedDict, total=False): + content: Required[Union[str, Iterable[ChatCompletionContentPartTextParam]]] + """The contents of the tool message.""" + + role: Required[Literal["tool"]] + """The role of the messages author, in this case `tool`.""" + + tool_call_id: Required[str] + """Tool call that this message is responding to.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_tool_param.py new file mode 100644 index 0000000..9f0abf2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_tool_param.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +from typing_extensions import TypeAlias + +from .chat_completion_function_tool_param import ( + FunctionDefinition as FunctionDefinition, + ChatCompletionFunctionToolParam, +) + +__all__ = ["ChatCompletionToolParam", "FunctionDefinition"] + +ChatCompletionToolParam: TypeAlias = ChatCompletionFunctionToolParam diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_tool_union_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_tool_union_param.py new file mode 100644 index 0000000..94cc1ea --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_tool_union_param.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .chat_completion_custom_tool_param import ChatCompletionCustomToolParam +from .chat_completion_function_tool_param import ChatCompletionFunctionToolParam + +__all__ = ["ChatCompletionToolUnionParam"] + +ChatCompletionToolUnionParam: TypeAlias = Union[ChatCompletionFunctionToolParam, ChatCompletionCustomToolParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_user_message_param.py b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_user_message_param.py new file mode 100644 index 0000000..bf9a5b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/chat_completion_user_message_param.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypedDict + +from .chat_completion_content_part_param import ChatCompletionContentPartParam + +__all__ = ["ChatCompletionUserMessageParam"] + + +class ChatCompletionUserMessageParam(TypedDict, total=False): + """ + Messages sent by an end user, containing prompts or additional context + information. + """ + + content: Required[Union[str, Iterable[ChatCompletionContentPartParam]]] + """The contents of the user message.""" + + role: Required[Literal["user"]] + """The role of the messages author, in this case `user`.""" + + name: str + """An optional name for the participant. + + Provides the model information to differentiate between participants of the same + role. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/completion_create_params.py b/venv/lib/python3.12/site-packages/openai/types/chat/completion_create_params.py new file mode 100644 index 0000000..dacf217 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/completion_create_params.py @@ -0,0 +1,560 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from ..shared.chat_model import ChatModel +from ..shared_params.metadata import Metadata +from ..shared.reasoning_effort import ReasoningEffort +from .chat_completion_audio_param import ChatCompletionAudioParam +from .chat_completion_message_param import ChatCompletionMessageParam +from .chat_completion_tool_union_param import ChatCompletionToolUnionParam +from ..shared_params.function_parameters import FunctionParameters +from ..shared_params.response_format_text import ResponseFormatText +from .chat_completion_stream_options_param import ChatCompletionStreamOptionsParam +from .chat_completion_prediction_content_param import ChatCompletionPredictionContentParam +from .chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam +from ..shared_params.response_format_json_object import ResponseFormatJSONObject +from ..shared_params.response_format_json_schema import ResponseFormatJSONSchema +from .chat_completion_function_call_option_param import ChatCompletionFunctionCallOptionParam + +__all__ = [ + "CompletionCreateParamsBase", + "FunctionCall", + "Function", + "Moderation", + "ModerationPolicy", + "ModerationPolicyInput", + "ModerationPolicyOutput", + "PromptCacheOptions", + "ResponseFormat", + "WebSearchOptions", + "WebSearchOptionsUserLocation", + "WebSearchOptionsUserLocationApproximate", + "CompletionCreateParamsNonStreaming", + "CompletionCreateParamsStreaming", +] + + +class CompletionCreateParamsBase(TypedDict, total=False): + messages: Required[Iterable[ChatCompletionMessageParam]] + """A list of messages comprising the conversation so far. + + Depending on the [model](https://developers.openai.com/api/docs/models) you use, + different message types (modalities) are supported, like + [text](https://developers.openai.com/api/docs/guides/text), + [images](https://developers.openai.com/api/docs/guides/images-vision), and + [audio](https://developers.openai.com/api/docs/guides/audio). + """ + + model: Required[Union[str, ChatModel]] + """Model ID used to generate the response, like `gpt-6-astra` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + audio: Optional[ChatCompletionAudioParam] + """Parameters for audio output. + + Required when audio output is requested with `modalities: ["audio"]`. + [Learn more](https://developers.openai.com/api/docs/guides/audio). + """ + + frequency_penalty: Optional[float] + """Number between -2.0 and 2.0. + + Positive values penalize new tokens based on their existing frequency in the + text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + + function_call: FunctionCall + """Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a + function. + + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + """ + + functions: Iterable[Function] + """Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. + """ + + logit_bias: Optional[Dict[str, int]] + """Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + """ + + logprobs: Optional[bool] + """Whether to return log probabilities of the output tokens or not. + + If true, returns the log probabilities of each output token returned in the + `content` of `message`. + """ + + max_completion_tokens: Optional[int] + """ + An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + """ + + max_tokens: Optional[int] + """ + The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the chat completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. + + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o-series models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + modalities: Optional[List[Literal["text", "audio"]]] + """ + Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://developers.openai.com/api/docs/guides/audio). To + request that this model generate both text and audio responses, you can use: + + `["text", "audio"]` + """ + + moderation: Optional[Moderation] + """Configuration for running moderation on the request input and generated output.""" + + n: Optional[int] + """How many chat completion choices to generate for each input message. + + Note that you will be charged based on the number of generated tokens across all + of the choices. Keep `n` as `1` to minimize costs. + """ + + parallel_tool_calls: bool + """ + Whether to enable + [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) + during tool use. + """ + + prediction: Optional[ChatCompletionPredictionContentParam] + """ + Configuration for a + [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs), + which can greatly improve response times when large parts of the model response + are known ahead of time. This is most common when you are regenerating a file + with only minor changes to most of the content. + """ + + presence_penalty: Optional[float] + """Number between -2.0 and 2.0. + + Positive values penalize new tokens based on whether they appear in the text so + far, increasing the model's likelihood to talk about new topics. + """ + + prompt_cache_key: Optional[str] + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + """ + + prompt_cache_options: PromptCacheOptions + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] + """Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + """ + + reasoning_effort: Optional[ReasoningEffort] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + response_format: ResponseFormat + """An object specifying the format that the model must output. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + safety_identifier: Optional[str] + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + seed: Optional[int] + """ + This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. + """ + + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + stop: Union[Optional[str], SequenceNotStr[str], None] + """Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + """ + + store: Optional[bool] + """ + Whether or not to store the output of this chat completion request for use in + our + [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model) + or [evals](https://developers.openai.com/api/docs/guides/evals) products. + + Supports text and image inputs. Note: image inputs over 8MB will be dropped. + """ + + stream_options: Optional[ChatCompletionStreamOptionsParam] + """Options for streaming response. Only set this when you set `stream: true`.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + tool_choice: ChatCompletionToolChoiceOptionParam + """ + Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + + `none` is the default when no tools are present. `auto` is the default if tools + are present. + """ + + tools: Iterable[ChatCompletionToolUnionParam] + """A list of tools the model may call. + + You can provide either + [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) + or + [function tools](https://developers.openai.com/api/docs/guides/function-calling). + """ + + top_logprobs: Optional[int] + """ + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + `logprobs` must be set to `true` if this parameter is used. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + user: str + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. The default is `medium`. + """ + + web_search_options: WebSearchOptions + """ + This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + """ + + +FunctionCall: TypeAlias = Union[Literal["none", "auto"], ChatCompletionFunctionCallOptionParam] + + +class Function(TypedDict, total=False): + name: Required[str] + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + description: str + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + parameters: FunctionParameters + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](https://developers.openai.com/api/docs/guides/function-calling) + for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + Omitting `parameters` defines a function with an empty parameter list. + """ + + +class ModerationPolicyInput(TypedDict, total=False): + """The moderation policy for the response input.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicyOutput(TypedDict, total=False): + """The moderation policy for the response output.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicy(TypedDict, total=False): + """The policy to apply to moderated response input and output.""" + + input: Optional[ModerationPolicyInput] + """The moderation policy for the response input.""" + + output: Optional[ModerationPolicyOutput] + """The moderation policy for the response output.""" + + +class Moderation(TypedDict, total=False): + """Configuration for running moderation on the request input and generated output.""" + + model: Required[str] + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + policy: Optional[ModerationPolicy] + """The policy to apply to moderated response input and output.""" + + +class PromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + + +ResponseFormat: TypeAlias = Union[ResponseFormatText, ResponseFormatJSONSchema, ResponseFormatJSONObject] + + +class WebSearchOptionsUserLocationApproximate(TypedDict, total=False): + """Approximate location parameters for the search.""" + + city: str + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: str + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: str + """Free text input for the region of the user, e.g. `California`.""" + + timezone: str + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + +class WebSearchOptionsUserLocation(TypedDict, total=False): + """Approximate location parameters for the search.""" + + approximate: Required[WebSearchOptionsUserLocationApproximate] + """Approximate location parameters for the search.""" + + type: Required[Literal["approximate"]] + """The type of location approximation. Always `approximate`.""" + + +class WebSearchOptions(TypedDict, total=False): + """ + This tool searches the web for relevant results to use in a response. + Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + """ + + search_context_size: Literal["low", "medium", "high"] + """ + High level guidance for the amount of context window space to use for the + search. One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[WebSearchOptionsUserLocation] + """Approximate location parameters for the search.""" + + +class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase, total=False): + stream: Optional[Literal[False]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events) + for more information, along with the + [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. + """ + + +class CompletionCreateParamsStreaming(CompletionCreateParamsBase): + stream: Required[Literal[True]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events) + for more information, along with the + [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. + """ + + +CompletionCreateParams = Union[CompletionCreateParamsNonStreaming, CompletionCreateParamsStreaming] diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/completion_list_params.py b/venv/lib/python3.12/site-packages/openai/types/chat/completion_list_params.py new file mode 100644 index 0000000..09dd278 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/completion_list_params.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +from ..shared_params.metadata import Metadata + +__all__ = ["CompletionListParams"] + + +class CompletionListParams(TypedDict, total=False): + after: str + """Identifier for the last chat completion from the previous pagination request.""" + + limit: int + """Number of Chat Completions to retrieve.""" + + metadata: Optional[Metadata] + """A list of metadata keys to filter the Chat Completions by. Example: + + `metadata[key1]=value1&metadata[key2]=value2` + """ + + model: str + """The model used to generate the Chat Completions.""" + + order: Literal["asc", "desc"] + """Sort order for Chat Completions by timestamp. + + Use `asc` for ascending order or `desc` for descending order. Defaults to `asc`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/completion_update_params.py b/venv/lib/python3.12/site-packages/openai/types/chat/completion_update_params.py new file mode 100644 index 0000000..5b60c4c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/completion_update_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ..shared_params.metadata import Metadata + +__all__ = ["CompletionUpdateParams"] + + +class CompletionUpdateParams(TypedDict, total=False): + metadata: Required[Optional[Metadata]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/completions/__init__.py b/venv/lib/python3.12/site-packages/openai/types/chat/completions/__init__.py new file mode 100644 index 0000000..a086360 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/completions/__init__.py @@ -0,0 +1,5 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .message_list_params import MessageListParams as MessageListParams diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/completions/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/completions/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c546429 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/completions/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/completions/__pycache__/message_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/chat/completions/__pycache__/message_list_params.cpython-312.pyc new file mode 100644 index 0000000..7edbc23 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/chat/completions/__pycache__/message_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/completions/message_list_params.py b/venv/lib/python3.12/site-packages/openai/types/chat/completions/message_list_params.py new file mode 100644 index 0000000..b3c3ebf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/completions/message_list_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["MessageListParams"] + + +class MessageListParams(TypedDict, total=False): + after: str + """Identifier for the last message from the previous pagination request.""" + + limit: int + """Number of messages to retrieve.""" + + order: Literal["asc", "desc"] + """Sort order for messages by timestamp. + + Use `asc` for ascending order or `desc` for descending order. Defaults to `asc`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/parsed_chat_completion.py b/venv/lib/python3.12/site-packages/openai/types/chat/parsed_chat_completion.py new file mode 100644 index 0000000..ba7dc78 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/parsed_chat_completion.py @@ -0,0 +1,38 @@ +from typing import List, Generic, TypeVar, Optional + +from ..._models import GenericModel +from .chat_completion import Choice, ChatCompletion +from .chat_completion_message import ChatCompletionMessage +from .parsed_function_tool_call import ParsedFunctionToolCall + +__all__ = ["ParsedChatCompletion", "ParsedChoice"] + + +ContentType = TypeVar("ContentType") + + +# we need to disable this check because we're overriding properties +# with subclasses of their types which is technically unsound as +# properties can be mutated. +# pyright: reportIncompatibleVariableOverride=false + + +class ParsedChatCompletionMessage(ChatCompletionMessage, GenericModel, Generic[ContentType]): + parsed: Optional[ContentType] = None + """The auto-parsed message contents""" + + tool_calls: Optional[List[ParsedFunctionToolCall]] = None # type: ignore[assignment] + """The tool calls generated by the model, such as function calls.""" + + +class ParsedChoice(Choice, GenericModel, Generic[ContentType]): + message: ParsedChatCompletionMessage[ContentType] + """A chat completion message generated by the model.""" + + +class ParsedChatCompletion(ChatCompletion, GenericModel, Generic[ContentType]): + choices: List[ParsedChoice[ContentType]] # type: ignore[assignment] + """A list of chat completion choices. + + Can be more than one if `n` is greater than 1. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/chat/parsed_function_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/chat/parsed_function_tool_call.py new file mode 100644 index 0000000..9c31b0f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat/parsed_function_tool_call.py @@ -0,0 +1,27 @@ +from typing import Optional + +from .chat_completion_message_function_tool_call import Function, ChatCompletionMessageFunctionToolCall + +__all__ = ["ParsedFunctionToolCall", "ParsedFunction"] + +# we need to disable this check because we're overriding properties +# with subclasses of their types which is technically unsound as +# properties can be mutated. +# pyright: reportIncompatibleVariableOverride=false + + +class ParsedFunction(Function): + parsed_arguments: Optional[object] = None + """ + The arguments to call the function with. + + If you used `openai.pydantic_function_tool()` then this will be an + instance of the given `BaseModel`. + + Otherwise, this will be the parsed JSON arguments. + """ + + +class ParsedFunctionToolCall(ChatCompletionMessageFunctionToolCall): + function: ParsedFunction + """The function that the model called.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/chat_model.py b/venv/lib/python3.12/site-packages/openai/types/chat_model.py new file mode 100644 index 0000000..eedbb9b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/chat_model.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from .shared import chat_model + +__all__ = ["ChatModel"] + +ChatModel = chat_model.ChatModel diff --git a/venv/lib/python3.12/site-packages/openai/types/completion.py b/venv/lib/python3.12/site-packages/openai/types/completion.py new file mode 100644 index 0000000..bdb2a17 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/completion.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .._models import BaseModel +from .completion_usage import CompletionUsage +from .completion_choice import CompletionChoice + +__all__ = ["Completion"] + + +class Completion(BaseModel): + """Represents a completion response from the API. + + Note: both the streamed and non-streamed response objects share the same shape (unlike the chat endpoint). + """ + + id: str + """A unique identifier for the completion.""" + + choices: List[CompletionChoice] + """The list of completion choices the model generated for the input prompt.""" + + created: int + """The Unix timestamp (in seconds) of when the completion was created.""" + + model: str + """The model used for completion.""" + + object: Literal["text_completion"] + """The object type, which is always "text_completion" """ + + system_fingerprint: Optional[str] = None + """This fingerprint represents the backend configuration that the model runs with. + + Can be used in conjunction with the `seed` request parameter to understand when + backend changes have been made that might impact determinism. + """ + + usage: Optional[CompletionUsage] = None + """Usage statistics for the completion request.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/completion_choice.py b/venv/lib/python3.12/site-packages/openai/types/completion_choice.py new file mode 100644 index 0000000..bbe873b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/completion_choice.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["CompletionChoice", "Logprobs"] + + +class Logprobs(BaseModel): + text_offset: Optional[List[int]] = None + + token_logprobs: Optional[List[float]] = None + + tokens: Optional[List[str]] = None + + top_logprobs: Optional[List[Dict[str, float]]] = None + + +class CompletionChoice(BaseModel): + finish_reason: Literal["stop", "length", "content_filter"] + """The reason the model stopped generating tokens. + + This will be `stop` if the model hit a natural stop point or a provided stop + sequence, `length` if the maximum number of tokens specified in the request was + reached, or `content_filter` if content was omitted due to a flag from our + content filters. + """ + + index: int + + logprobs: Optional[Logprobs] = None + + text: str diff --git a/venv/lib/python3.12/site-packages/openai/types/completion_create_params.py b/venv/lib/python3.12/site-packages/openai/types/completion_create_params.py new file mode 100644 index 0000000..72ba426 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/completion_create_params.py @@ -0,0 +1,190 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from .._types import SequenceNotStr +from .chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam + +__all__ = ["CompletionCreateParamsBase", "CompletionCreateParamsNonStreaming", "CompletionCreateParamsStreaming"] + + +class CompletionCreateParamsBase(TypedDict, total=False): + model: Required[Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]]] + """ID of the model to use. + + You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + """ + + prompt: Required[Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None]] + """ + The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + """ + + best_of: Optional[int] + """ + Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + """ + + echo: Optional[bool] + """Echo back the prompt in addition to the completion""" + + frequency_penalty: Optional[float] + """Number between -2.0 and 2.0. + + Positive values penalize new tokens based on their existing frequency in the + text so far, decreasing the model's likelihood to repeat the same line verbatim. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + """ + + logit_bias: Optional[Dict[str, int]] + """Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text + to token IDs. Mathematically, the bias is added to the logits generated by the + model prior to sampling. The exact effect will vary per model, but values + between -1 and 1 should decrease or increase likelihood of selection; values + like -100 or 100 should result in a ban or exclusive selection of the relevant + token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + """ + + logprobs: Optional[int] + """ + Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + """ + + max_tokens: Optional[int] + """ + The maximum number of [tokens](https://platform.openai.com/tokenizer) that can + be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + """ + + n: Optional[int] + """How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + """ + + presence_penalty: Optional[float] + """Number between -2.0 and 2.0. + + Positive values penalize new tokens based on whether they appear in the text so + far, increasing the model's likelihood to talk about new topics. + + [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text) + """ + + seed: Optional[int] + """ + If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + """ + + stop: Union[Optional[str], SequenceNotStr[str], None] + """Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + """ + + stream_options: Optional[ChatCompletionStreamOptionsParam] + """Options for streaming response. Only set this when you set `stream: true`.""" + + suffix: Optional[str] + """The suffix that comes after a completion of inserted text. + + This parameter is only supported for `gpt-3.5-turbo-instruct`. + """ + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + user: str + """ + A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + +class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase, total=False): + stream: Optional[Literal[False]] + """Whether to stream back partial progress. + + If set, tokens will be sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + """ + + +class CompletionCreateParamsStreaming(CompletionCreateParamsBase): + stream: Required[Literal[True]] + """Whether to stream back partial progress. + + If set, tokens will be sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + """ + + +CompletionCreateParams = Union[CompletionCreateParamsNonStreaming, CompletionCreateParamsStreaming] diff --git a/venv/lib/python3.12/site-packages/openai/types/completion_usage.py b/venv/lib/python3.12/site-packages/openai/types/completion_usage.py new file mode 100644 index 0000000..73bd2bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/completion_usage.py @@ -0,0 +1,72 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["CompletionUsage", "CompletionTokensDetails", "PromptTokensDetails"] + + +class CompletionTokensDetails(BaseModel): + """Breakdown of tokens used in a completion.""" + + accepted_prediction_tokens: Optional[int] = None + """ + When using Predicted Outputs, the number of tokens in the prediction that + appeared in the completion. + """ + + audio_tokens: Optional[int] = None + """Audio input tokens generated by the model.""" + + reasoning_tokens: Optional[int] = None + """Tokens generated by the model for reasoning.""" + + rejected_prediction_tokens: Optional[int] = None + """ + When using Predicted Outputs, the number of tokens in the prediction that did + not appear in the completion. However, like reasoning tokens, these tokens are + still counted in the total completion tokens for purposes of billing, output, + and context window limits. + """ + + text_tokens: Optional[int] = None + """Text output tokens generated by the model.""" + + +class PromptTokensDetails(BaseModel): + """Breakdown of tokens used in the prompt.""" + + audio_tokens: Optional[int] = None + """Audio input tokens present in the prompt.""" + + cache_write_tokens: Optional[int] = None + """The unadjusted number of prompt tokens written to cache.""" + + cached_tokens: Optional[int] = None + """Cached tokens present in the prompt.""" + + image_tokens: Optional[int] = None + """Image input tokens present in the prompt.""" + + text_tokens: Optional[int] = None + """Text input tokens present in the prompt.""" + + +class CompletionUsage(BaseModel): + """Usage statistics for the completion request.""" + + completion_tokens: int + """Number of tokens in the generated completion.""" + + prompt_tokens: int + """Number of tokens in the prompt.""" + + total_tokens: int + """Total number of tokens used in the request (prompt + completion).""" + + completion_tokens_details: Optional[CompletionTokensDetails] = None + """Breakdown of tokens used in a completion.""" + + prompt_tokens_details: Optional[PromptTokensDetails] = None + """Breakdown of tokens used in the prompt.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/container_create_params.py b/venv/lib/python3.12/site-packages/openai/types/container_create_params.py new file mode 100644 index 0000000..2bfa522 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/container_create_params.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .._types import SequenceNotStr +from .responses.inline_skill_param import InlineSkillParam +from .responses.skill_reference_param import SkillReferenceParam +from .responses.container_network_policy_disabled_param import ContainerNetworkPolicyDisabledParam +from .responses.container_network_policy_allowlist_param import ContainerNetworkPolicyAllowlistParam + +__all__ = ["ContainerCreateParams", "ExpiresAfter", "NetworkPolicy", "Skill"] + + +class ContainerCreateParams(TypedDict, total=False): + name: Required[str] + """Name of the container to create.""" + + expires_after: ExpiresAfter + """Container expiration time in seconds relative to the 'anchor' time.""" + + file_ids: SequenceNotStr[str] + """IDs of files to copy to the container.""" + + memory_limit: Literal["1g", "4g", "16g", "64g"] + """Optional memory limit for the container. Defaults to "1g".""" + + network_policy: NetworkPolicy + """Network access policy for the container.""" + + skills: Iterable[Skill] + """An optional list of skills referenced by id or inline data.""" + + +class ExpiresAfter(TypedDict, total=False): + """Container expiration time in seconds relative to the 'anchor' time.""" + + anchor: Required[Literal["last_active_at"]] + """Time anchor for the expiration time. + + Currently only 'last_active_at' is supported. + """ + + minutes: Required[int] + + +NetworkPolicy: TypeAlias = Union[ContainerNetworkPolicyDisabledParam, ContainerNetworkPolicyAllowlistParam] + +Skill: TypeAlias = Union[SkillReferenceParam, InlineSkillParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/container_create_response.py b/venv/lib/python3.12/site-packages/openai/types/container_create_response.py new file mode 100644 index 0000000..98e98fa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/container_create_response.py @@ -0,0 +1,65 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ContainerCreateResponse", "ExpiresAfter", "NetworkPolicy"] + + +class ExpiresAfter(BaseModel): + """ + The container will expire after this time period. + The anchor is the reference point for the expiration. + The minutes is the number of minutes after the anchor before the container expires. + """ + + anchor: Optional[Literal["last_active_at"]] = None + """The reference point for the expiration.""" + + minutes: Optional[int] = None + """The number of minutes after the anchor before the container expires.""" + + +class NetworkPolicy(BaseModel): + """Network access policy for the container.""" + + type: Literal["allowlist", "disabled"] + """The network policy mode.""" + + allowed_domains: Optional[List[str]] = None + """Allowed outbound domains when `type` is `allowlist`.""" + + +class ContainerCreateResponse(BaseModel): + id: str + """Unique identifier for the container.""" + + created_at: int + """Unix timestamp (in seconds) when the container was created.""" + + name: str + """Name of the container.""" + + object: str + """The type of this object.""" + + status: str + """Status of the container (e.g., active, deleted).""" + + expires_after: Optional[ExpiresAfter] = None + """ + The container will expire after this time period. The anchor is the reference + point for the expiration. The minutes is the number of minutes after the anchor + before the container expires. + """ + + last_active_at: Optional[int] = None + """Unix timestamp (in seconds) when the container was last active.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit configured for the container.""" + + network_policy: Optional[NetworkPolicy] = None + """Network access policy for the container.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/container_list_params.py b/venv/lib/python3.12/site-packages/openai/types/container_list_params.py new file mode 100644 index 0000000..7e642f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/container_list_params.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ContainerListParams"] + + +class ContainerListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + name: str + """Filter results by container name.""" + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/container_list_response.py b/venv/lib/python3.12/site-packages/openai/types/container_list_response.py new file mode 100644 index 0000000..b0a1693 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/container_list_response.py @@ -0,0 +1,65 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ContainerListResponse", "ExpiresAfter", "NetworkPolicy"] + + +class ExpiresAfter(BaseModel): + """ + The container will expire after this time period. + The anchor is the reference point for the expiration. + The minutes is the number of minutes after the anchor before the container expires. + """ + + anchor: Optional[Literal["last_active_at"]] = None + """The reference point for the expiration.""" + + minutes: Optional[int] = None + """The number of minutes after the anchor before the container expires.""" + + +class NetworkPolicy(BaseModel): + """Network access policy for the container.""" + + type: Literal["allowlist", "disabled"] + """The network policy mode.""" + + allowed_domains: Optional[List[str]] = None + """Allowed outbound domains when `type` is `allowlist`.""" + + +class ContainerListResponse(BaseModel): + id: str + """Unique identifier for the container.""" + + created_at: int + """Unix timestamp (in seconds) when the container was created.""" + + name: str + """Name of the container.""" + + object: str + """The type of this object.""" + + status: str + """Status of the container (e.g., active, deleted).""" + + expires_after: Optional[ExpiresAfter] = None + """ + The container will expire after this time period. The anchor is the reference + point for the expiration. The minutes is the number of minutes after the anchor + before the container expires. + """ + + last_active_at: Optional[int] = None + """Unix timestamp (in seconds) when the container was last active.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit configured for the container.""" + + network_policy: Optional[NetworkPolicy] = None + """Network access policy for the container.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/container_retrieve_response.py b/venv/lib/python3.12/site-packages/openai/types/container_retrieve_response.py new file mode 100644 index 0000000..508aa7c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/container_retrieve_response.py @@ -0,0 +1,65 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ContainerRetrieveResponse", "ExpiresAfter", "NetworkPolicy"] + + +class ExpiresAfter(BaseModel): + """ + The container will expire after this time period. + The anchor is the reference point for the expiration. + The minutes is the number of minutes after the anchor before the container expires. + """ + + anchor: Optional[Literal["last_active_at"]] = None + """The reference point for the expiration.""" + + minutes: Optional[int] = None + """The number of minutes after the anchor before the container expires.""" + + +class NetworkPolicy(BaseModel): + """Network access policy for the container.""" + + type: Literal["allowlist", "disabled"] + """The network policy mode.""" + + allowed_domains: Optional[List[str]] = None + """Allowed outbound domains when `type` is `allowlist`.""" + + +class ContainerRetrieveResponse(BaseModel): + id: str + """Unique identifier for the container.""" + + created_at: int + """Unix timestamp (in seconds) when the container was created.""" + + name: str + """Name of the container.""" + + object: str + """The type of this object.""" + + status: str + """Status of the container (e.g., active, deleted).""" + + expires_after: Optional[ExpiresAfter] = None + """ + The container will expire after this time period. The anchor is the reference + point for the expiration. The minutes is the number of minutes after the anchor + before the container expires. + """ + + last_active_at: Optional[int] = None + """Unix timestamp (in seconds) when the container was last active.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit configured for the container.""" + + network_policy: Optional[NetworkPolicy] = None + """Network access policy for the container.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/__init__.py b/venv/lib/python3.12/site-packages/openai/types/containers/__init__.py new file mode 100644 index 0000000..7375f1b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/containers/__init__.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .file_list_params import FileListParams as FileListParams +from .file_create_params import FileCreateParams as FileCreateParams +from .file_list_response import FileListResponse as FileListResponse +from .file_create_response import FileCreateResponse as FileCreateResponse +from .file_retrieve_response import FileRetrieveResponse as FileRetrieveResponse diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ed77aa9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_create_params.cpython-312.pyc new file mode 100644 index 0000000..51a1396 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_create_response.cpython-312.pyc new file mode 100644 index 0000000..4636521 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_list_params.cpython-312.pyc new file mode 100644 index 0000000..a09ced3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_list_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_list_response.cpython-312.pyc new file mode 100644 index 0000000..13a222a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_list_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_retrieve_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_retrieve_response.cpython-312.pyc new file mode 100644 index 0000000..9177f90 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/containers/__pycache__/file_retrieve_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/file_create_params.py b/venv/lib/python3.12/site-packages/openai/types/containers/file_create_params.py new file mode 100644 index 0000000..36f66db --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/containers/file_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from ..._types import FileTypes + +__all__ = ["FileCreateParams"] + + +class FileCreateParams(TypedDict, total=False): + file: FileTypes + """The File object (not file name) to be uploaded.""" + + file_id: str + """Name of the file to create.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/file_create_response.py b/venv/lib/python3.12/site-packages/openai/types/containers/file_create_response.py new file mode 100644 index 0000000..025cd69 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/containers/file_create_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FileCreateResponse"] + + +class FileCreateResponse(BaseModel): + id: str + """Unique identifier for the file.""" + + bytes: int + """Size of the file in bytes.""" + + container_id: str + """The container this file belongs to.""" + + created_at: int + """Unix timestamp (in seconds) when the file was created.""" + + object: Literal["container.file"] + """The type of this object (`container.file`).""" + + path: str + """Path of the file in the container.""" + + source: str + """Source of the file (e.g., `user`, `assistant`).""" diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/file_list_params.py b/venv/lib/python3.12/site-packages/openai/types/containers/file_list_params.py new file mode 100644 index 0000000..2d1f20f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/containers/file_list_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["FileListParams"] + + +class FileListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/file_list_response.py b/venv/lib/python3.12/site-packages/openai/types/containers/file_list_response.py new file mode 100644 index 0000000..3b00802 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/containers/file_list_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FileListResponse"] + + +class FileListResponse(BaseModel): + id: str + """Unique identifier for the file.""" + + bytes: int + """Size of the file in bytes.""" + + container_id: str + """The container this file belongs to.""" + + created_at: int + """Unix timestamp (in seconds) when the file was created.""" + + object: Literal["container.file"] + """The type of this object (`container.file`).""" + + path: str + """Path of the file in the container.""" + + source: str + """Source of the file (e.g., `user`, `assistant`).""" diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/file_retrieve_response.py b/venv/lib/python3.12/site-packages/openai/types/containers/file_retrieve_response.py new file mode 100644 index 0000000..51e08b3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/containers/file_retrieve_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FileRetrieveResponse"] + + +class FileRetrieveResponse(BaseModel): + id: str + """Unique identifier for the file.""" + + bytes: int + """Size of the file in bytes.""" + + container_id: str + """The container this file belongs to.""" + + created_at: int + """Unix timestamp (in seconds) when the file was created.""" + + object: Literal["container.file"] + """The type of this object (`container.file`).""" + + path: str + """Path of the file in the container.""" + + source: str + """Source of the file (e.g., `user`, `assistant`).""" diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/files/__init__.py b/venv/lib/python3.12/site-packages/openai/types/containers/files/__init__.py new file mode 100644 index 0000000..273c8b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/containers/files/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/venv/lib/python3.12/site-packages/openai/types/containers/files/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/containers/files/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..21c123a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/containers/files/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/content_provenance_check.py b/venv/lib/python3.12/site-packages/openai/types/content_provenance_check.py new file mode 100644 index 0000000..65777f8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/content_provenance_check.py @@ -0,0 +1,82 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from .._utils import PropertyInfo +from .._models import BaseModel + +__all__ = ["ContentProvenanceCheck", "Result", "ResultC2PA", "ResultSynthID"] + + +class ResultC2PA(BaseModel): + generated_at: Optional[str] = None + """ + The UTC RFC 3339 timestamp recorded by the provenance signal for when the asset + was generated, when available. + """ + + issuer: Optional[str] = None + """The C2PA manifest issuer, when available.""" + + model: Optional[str] = None + """The OpenAI model recorded by the provenance signal, when available.""" + + outcome: Literal["detected", "not_detected"] + """ + Whether a supported OpenAI C2PA provenance signal was detected. If + `not_detected`, it means the tool did not find supported signals in the uploaded + file. The content could still have been generated by OpenAI if the metadata was + stripped or has evidence of tampering, the watermark was degraded, it comes from + a legacy generation model, or it was created before provenance signals were + available. Content could also still be AI-generated by another company's model, + which the tool currently does not detect. + """ + + type: Literal["c2pa"] + """The provenance signal type. Always `c2pa`.""" + + validation_state: Literal["trusted", "valid", "invalid", "not_present"] + """The validation status of the C2PA manifest in the uploaded image.""" + + +class ResultSynthID(BaseModel): + generated_at: Optional[str] = None + """ + The UTC RFC 3339 timestamp recorded by the provenance signal for when the asset + was generated, when available. + """ + + model: Optional[str] = None + """The OpenAI model recorded by the provenance signal, when available.""" + + outcome: Literal["detected", "not_detected"] + """ + Whether a supported OpenAI SynthID watermark was detected. If `not_detected`, it + means the tool did not find supported signals in the uploaded file. The content + could still have been generated by OpenAI if the metadata was stripped or has + evidence of tampering, the watermark was degraded, it comes from a legacy + generation model, or it was created before provenance signals were available. + Content could also still be AI-generated by another company's model, which the + tool currently does not detect. + """ + + type: Literal["synthid"] + """The provenance signal type. Always `synthid`.""" + + +Result: TypeAlias = Annotated[Union[ResultC2PA, ResultSynthID], PropertyInfo(discriminator="type")] + + +class ContentProvenanceCheck(BaseModel): + created_at: int + """The Unix timestamp, in seconds, when the provenance check was created.""" + + object: Literal["content_provenance_check"] + """The object type. Always `content_provenance_check` for this endpoint.""" + + results: List[Result] + """The provenance results that apply to the uploaded file. + + Image results include C2PA and SynthID; audio results include SynthID. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/content_provenance_check_create_params.py b/venv/lib/python3.12/site-packages/openai/types/content_provenance_check_create_params.py new file mode 100644 index 0000000..a012ce0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/content_provenance_check_create_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import FileTypes + +__all__ = ["ContentProvenanceCheckCreateParams"] + + +class ContentProvenanceCheckCreateParams(TypedDict, total=False): + file: Required[FileTypes] + """The image or audio file to check for supported OpenAI provenance signals.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__init__.py b/venv/lib/python3.12/site-packages/openai/types/conversations/__init__.py new file mode 100644 index 0000000..55f924d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/__init__.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .message import Message as Message +from .conversation import Conversation as Conversation +from .text_content import TextContent as TextContent +from .refusal_content import RefusalContent as RefusalContent +from .item_list_params import ItemListParams as ItemListParams +from .conversation_item import ConversationItem as ConversationItem +from .input_file_content import InputFileContent as InputFileContent +from .input_text_content import InputTextContent as InputTextContent +from .item_create_params import ItemCreateParams as ItemCreateParams +from .input_image_content import InputImageContent as InputImageContent +from .output_text_content import OutputTextContent as OutputTextContent +from .item_retrieve_params import ItemRetrieveParams as ItemRetrieveParams +from .summary_text_content import SummaryTextContent as SummaryTextContent +from .refusal_content_param import RefusalContentParam as RefusalContentParam +from .conversation_item_list import ConversationItemList as ConversationItemList +from .input_file_content_param import InputFileContentParam as InputFileContentParam +from .input_text_content_param import InputTextContentParam as InputTextContentParam +from .input_image_content_param import InputImageContentParam as InputImageContentParam +from .output_text_content_param import OutputTextContentParam as OutputTextContentParam +from .conversation_create_params import ConversationCreateParams as ConversationCreateParams +from .conversation_update_params import ConversationUpdateParams as ConversationUpdateParams +from .computer_screenshot_content import ComputerScreenshotContent as ComputerScreenshotContent +from .conversation_deleted_resource import ConversationDeletedResource as ConversationDeletedResource diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7408144 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/computer_screenshot_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/computer_screenshot_content.cpython-312.pyc new file mode 100644 index 0000000..139d9b7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/computer_screenshot_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation.cpython-312.pyc new file mode 100644 index 0000000..9f94fd4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_create_params.cpython-312.pyc new file mode 100644 index 0000000..b7da727 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_deleted_resource.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_deleted_resource.cpython-312.pyc new file mode 100644 index 0000000..d5be8aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_deleted_resource.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_item.cpython-312.pyc new file mode 100644 index 0000000..3fd763d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_item_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_item_list.cpython-312.pyc new file mode 100644 index 0000000..0612790 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_item_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_update_params.cpython-312.pyc new file mode 100644 index 0000000..5c2e8ea Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/conversation_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_file_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_file_content.cpython-312.pyc new file mode 100644 index 0000000..f0d4355 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_file_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_file_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_file_content_param.cpython-312.pyc new file mode 100644 index 0000000..11c1c10 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_file_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_image_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_image_content.cpython-312.pyc new file mode 100644 index 0000000..729c04d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_image_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_image_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_image_content_param.cpython-312.pyc new file mode 100644 index 0000000..01ff0bc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_image_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_text_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_text_content.cpython-312.pyc new file mode 100644 index 0000000..f29e6ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_text_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_text_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_text_content_param.cpython-312.pyc new file mode 100644 index 0000000..c7814a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/input_text_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/item_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/item_create_params.cpython-312.pyc new file mode 100644 index 0000000..522133a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/item_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/item_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/item_list_params.cpython-312.pyc new file mode 100644 index 0000000..4f39cc2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/item_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/item_retrieve_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/item_retrieve_params.cpython-312.pyc new file mode 100644 index 0000000..30611c8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/item_retrieve_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/message.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/message.cpython-312.pyc new file mode 100644 index 0000000..f58935d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/output_text_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/output_text_content.cpython-312.pyc new file mode 100644 index 0000000..980396b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/output_text_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/output_text_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/output_text_content_param.cpython-312.pyc new file mode 100644 index 0000000..1bd3b05 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/output_text_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/refusal_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/refusal_content.cpython-312.pyc new file mode 100644 index 0000000..19cc2c8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/refusal_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/refusal_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/refusal_content_param.cpython-312.pyc new file mode 100644 index 0000000..2309e57 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/refusal_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/summary_text_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/summary_text_content.cpython-312.pyc new file mode 100644 index 0000000..f15a58f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/summary_text_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/text_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/text_content.cpython-312.pyc new file mode 100644 index 0000000..3e35ae2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/conversations/__pycache__/text_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/computer_screenshot_content.py b/venv/lib/python3.12/site-packages/openai/types/conversations/computer_screenshot_content.py new file mode 100644 index 0000000..b2fc076 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/computer_screenshot_content.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from ..responses.image_detail import ImageDetail + +__all__ = ["ComputerScreenshotContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class ComputerScreenshotContent(BaseModel): + """A screenshot of a computer.""" + + detail: ImageDetail + """The detail level of the screenshot image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + file_id: Optional[str] = None + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: Optional[str] = None + """The URL of the screenshot image.""" + + type: Literal["computer_screenshot"] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/conversation.py b/venv/lib/python3.12/site-packages/openai/types/conversations/conversation.py new file mode 100644 index 0000000..aa03503 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/conversation.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["Conversation"] + + +class Conversation(BaseModel): + id: str + """The unique ID of the conversation.""" + + created_at: int + """ + The time at which the conversation was created, measured in seconds since the + Unix epoch. + """ + + metadata: object + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters. + """ + + object: Literal["conversation"] + """The object type, which is always `conversation`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_create_params.py b/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_create_params.py new file mode 100644 index 0000000..df48059 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_create_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import TypedDict + +from ..shared_params.metadata import Metadata +from ..responses.response_input_item_param import ResponseInputItemParam + +__all__ = ["ConversationCreateParams"] + + +class ConversationCreateParams(TypedDict, total=False): + items: Optional[Iterable[ResponseInputItemParam]] + """Initial items to include in the conversation context. + + You may add up to 20 items at a time. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_deleted_resource.py b/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_deleted_resource.py new file mode 100644 index 0000000..5de4e33 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_deleted_resource.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationDeletedResource"] + + +class ConversationDeletedResource(BaseModel): + id: str + + deleted: bool + + object: Literal["conversation.deleted"] diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_item.py b/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_item.py new file mode 100644 index 0000000..bb10ae1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_item.py @@ -0,0 +1,333 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from .message import Message +from ..._utils import PropertyInfo +from ..._models import BaseModel +from ..responses.tool import Tool +from ..responses.mcp_tool_call_error import McpToolCallError +from ..responses.response_reasoning_item import ResponseReasoningItem +from ..responses.response_compaction_item import ResponseCompactionItem +from ..responses.response_custom_tool_call import ResponseCustomToolCall +from ..responses.response_tool_search_call import ResponseToolSearchCall +from ..responses.response_computer_tool_call import ResponseComputerToolCall +from ..responses.response_function_web_search import ResponseFunctionWebSearch +from ..responses.response_apply_patch_tool_call import ResponseApplyPatchToolCall +from ..responses.response_file_search_tool_call import ResponseFileSearchToolCall +from ..responses.response_custom_tool_call_output import ResponseCustomToolCallOutput +from ..responses.response_function_tool_call_item import ResponseFunctionToolCallItem +from ..responses.response_tool_search_output_item import ResponseToolSearchOutputItem +from ..responses.response_function_shell_tool_call import ResponseFunctionShellToolCall +from ..responses.response_configuration_update_item import ResponseConfigurationUpdateItem +from ..responses.response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall +from ..responses.response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput +from ..responses.response_computer_tool_call_output_item import ResponseComputerToolCallOutputItem +from ..responses.response_function_tool_call_output_item import ResponseFunctionToolCallOutputItem +from ..responses.response_function_shell_tool_call_output import ResponseFunctionShellToolCallOutput + +__all__ = [ + "ConversationItem", + "ImageGenerationCall", + "AdditionalTools", + "Program", + "ProgramOutput", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallOutput", + "McpListTools", + "McpListToolsTool", + "McpApprovalRequest", + "McpApprovalResponse", + "McpCall", +] + + +class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + + id: str + """The unique ID of the image generation call.""" + + result: Optional[str] = None + """The generated image encoded in base64.""" + + status: Literal["in_progress", "completed", "generating", "failed"] + """The status of the image generation call.""" + + type: Literal["image_generation_call"] + """The type of the image generation call. Always `image_generation_call`.""" + + action: Optional[Literal["generate", "edit", "auto"]] = None + """The action used for image generation.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] = None + """The background setting used for generation.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] = None + """The output format used for generation.""" + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None + """The quality of the image generated by the image generation tool call. + + One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`. + """ + + revised_prompt: Optional[str] = None + """The prompt that was used after any model prompt rewriting.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + +class AdditionalTools(BaseModel): + id: str + """The unique ID of the additional tools item.""" + + role: Literal["unknown", "user", "assistant", "system", "critic", "discriminator", "developer", "tool"] + """The role that provided the additional tools.""" + + tools: List[Tool] + """The additional tool definitions made available at this item.""" + + type: Literal["additional_tools"] + """The type of the item. Always `additional_tools`.""" + + +class Program(BaseModel): + id: str + """The unique ID of the program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The type of the item. Always `program`.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of the program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output item.""" + + type: Literal["program_output"] + """The type of the item. Always `program_output`.""" + + +class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + + command: List[str] + """The command to run.""" + + env: Dict[str, str] + """Environment variables to set for the command.""" + + type: Literal["exec"] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] = None + """Optional user to run the command as.""" + + working_directory: Optional[str] = None + """Optional working directory to run the command in.""" + + +class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + + id: str + """The unique ID of the local shell call.""" + + action: LocalShellCallAction + """Execute a shell command on the server.""" + + call_id: str + """The unique ID of the local shell tool call generated by the model.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the local shell call.""" + + type: Literal["local_shell_call"] + """The type of the local shell call. Always `local_shell_call`.""" + + +class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + + id: str + """The unique ID of the local shell tool call generated by the model.""" + + output: str + """A JSON string of the output of the local shell tool call.""" + + type: Literal["local_shell_call_output"] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + + input_schema: object + """The JSON schema describing the tool's input.""" + + name: str + """The name of the tool.""" + + annotations: Optional[object] = None + """Additional annotations about the tool.""" + + description: Optional[str] = None + """The description of the tool.""" + + +class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + + id: str + """The unique ID of the list.""" + + server_label: str + """The label of the MCP server.""" + + tools: List[McpListToolsTool] + """The tools available on the server.""" + + type: Literal["mcp_list_tools"] + """The type of the item. Always `mcp_list_tools`.""" + + error: Optional[str] = None + """Error message if the server could not list tools.""" + + +class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + + id: str + """The unique ID of the approval request.""" + + arguments: str + """A JSON string of arguments for the tool.""" + + name: str + """The name of the tool to run.""" + + server_label: str + """The label of the MCP server making the request.""" + + type: Literal["mcp_approval_request"] + """The type of the item. Always `mcp_approval_request`.""" + + +class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + + id: str + """The unique ID of the approval response""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + reason: Optional[str] = None + """Optional reason for the decision.""" + + +class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + + id: str + """The unique ID of the tool call.""" + + arguments: str + """A JSON string of the arguments passed to the tool.""" + + name: str + """The name of the tool that was run.""" + + server_label: str + """The label of the MCP server running the tool.""" + + type: Literal["mcp_call"] + """The type of the item. Always `mcp_call`.""" + + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[McpToolCallError] = None + """The error from the tool call, if any.""" + + output: Optional[str] = None + """The output from the tool call.""" + + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +ConversationItem: TypeAlias = Annotated[ + Union[ + Message, + ResponseFunctionToolCallItem, + ResponseFunctionToolCallOutputItem, + ResponseFileSearchToolCall, + ResponseFunctionWebSearch, + ImageGenerationCall, + ResponseComputerToolCall, + ResponseComputerToolCallOutputItem, + ResponseToolSearchCall, + ResponseToolSearchOutputItem, + AdditionalTools, + ResponseConfigurationUpdateItem, + ResponseReasoningItem, + Program, + ProgramOutput, + ResponseCompactionItem, + ResponseCodeInterpreterToolCall, + LocalShellCall, + LocalShellCallOutput, + ResponseFunctionShellToolCall, + ResponseFunctionShellToolCallOutput, + ResponseApplyPatchToolCall, + ResponseApplyPatchToolCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + ResponseCustomToolCall, + ResponseCustomToolCallOutput, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_item_list.py b/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_item_list.py new file mode 100644 index 0000000..488fb11 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_item_list.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemList"] + + +class ConversationItemList(BaseModel): + """A list of Conversation items.""" + + data: List[ConversationItem] + """A list of conversation items.""" + + first_id: str + """The ID of the first item in the list.""" + + has_more: bool + """Whether there are more items available.""" + + last_id: str + """The ID of the last item in the list.""" + + object: Literal["list"] + """The type of object returned, must be `list`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_update_params.py b/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_update_params.py new file mode 100644 index 0000000..ae9415f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/conversation_update_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ..shared_params.metadata import Metadata + +__all__ = ["ConversationUpdateParams"] + + +class ConversationUpdateParams(TypedDict, total=False): + metadata: Required[Optional[Metadata]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/input_file_content.py b/venv/lib/python3.12/site-packages/openai/types/conversations/input_file_content.py new file mode 100644 index 0000000..2b2c589 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/input_file_content.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..responses.response_input_file import ResponseInputFile + +__all__ = ["InputFileContent"] + +InputFileContent = ResponseInputFile diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/input_file_content_param.py b/venv/lib/python3.12/site-packages/openai/types/conversations/input_file_content_param.py new file mode 100644 index 0000000..edf4ceb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/input_file_content_param.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..responses.response_input_file_param import ResponseInputFileParam + +InputFileContentParam = ResponseInputFileParam diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/input_image_content.py b/venv/lib/python3.12/site-packages/openai/types/conversations/input_image_content.py new file mode 100644 index 0000000..cd1b91b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/input_image_content.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..responses.response_input_image import ResponseInputImage + +__all__ = ["InputImageContent"] + +InputImageContent = ResponseInputImage diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/input_image_content_param.py b/venv/lib/python3.12/site-packages/openai/types/conversations/input_image_content_param.py new file mode 100644 index 0000000..10d0525 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/input_image_content_param.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..responses.response_input_image_param import ResponseInputImageParam + +InputImageContentParam = ResponseInputImageParam diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/input_text_content.py b/venv/lib/python3.12/site-packages/openai/types/conversations/input_text_content.py new file mode 100644 index 0000000..4f0489e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/input_text_content.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..responses.response_input_text import ResponseInputText + +__all__ = ["InputTextContent"] + +InputTextContent = ResponseInputText diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/input_text_content_param.py b/venv/lib/python3.12/site-packages/openai/types/conversations/input_text_content_param.py new file mode 100644 index 0000000..b773c1e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/input_text_content_param.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..responses.response_input_text_param import ResponseInputTextParam + +InputTextContentParam = ResponseInputTextParam diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/item_create_params.py b/venv/lib/python3.12/site-packages/openai/types/conversations/item_create_params.py new file mode 100644 index 0000000..d61f464 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/item_create_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Iterable +from typing_extensions import Required, TypedDict + +from ..responses.response_includable import ResponseIncludable +from ..responses.response_input_item_param import ResponseInputItemParam + +__all__ = ["ItemCreateParams"] + + +class ItemCreateParams(TypedDict, total=False): + items: Required[Iterable[ResponseInputItemParam]] + """The items to add to the conversation. You may add up to 20 items at a time.""" + + include: List[ResponseIncludable] + """Additional fields to include in the response. + + See the `include` parameter for + [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29) + for more information. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/item_list_params.py b/venv/lib/python3.12/site-packages/openai/types/conversations/item_list_params.py new file mode 100644 index 0000000..1bd1e6d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/item_list_params.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +from ..responses.response_includable import ResponseIncludable + +__all__ = ["ItemListParams"] + + +class ItemListParams(TypedDict, total=False): + after: str + """An item ID to list items after, used in pagination.""" + + include: List[ResponseIncludable] + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/item_retrieve_params.py b/venv/lib/python3.12/site-packages/openai/types/conversations/item_retrieve_params.py new file mode 100644 index 0000000..21a3be9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/item_retrieve_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Required, TypedDict + +from ..responses.response_includable import ResponseIncludable + +__all__ = ["ItemRetrieveParams"] + + +class ItemRetrieveParams(TypedDict, total=False): + conversation_id: Required[str] + + include: List[ResponseIncludable] + """Additional fields to include in the response. + + See the `include` parameter for + [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29) + for more information. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/message.py b/venv/lib/python3.12/site-packages/openai/types/conversations/message.py new file mode 100644 index 0000000..1b63ad4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/message.py @@ -0,0 +1,78 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .text_content import TextContent +from .summary_text_content import SummaryTextContent +from .computer_screenshot_content import ComputerScreenshotContent +from ..responses.response_input_file import ResponseInputFile +from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_image import ResponseInputImage +from ..responses.response_output_text import ResponseOutputText +from ..responses.response_output_refusal import ResponseOutputRefusal + +__all__ = ["Message", "Content", "ContentReasoningText"] + + +class ContentReasoningText(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +Content: TypeAlias = Annotated[ + Union[ + ResponseInputText, + ResponseOutputText, + TextContent, + SummaryTextContent, + ContentReasoningText, + ResponseOutputRefusal, + ResponseInputImage, + ComputerScreenshotContent, + ResponseInputFile, + ], + PropertyInfo(discriminator="type"), +] + + +class Message(BaseModel): + """A message to or from the model.""" + + id: str + """The unique ID of the message.""" + + content: List[Content] + """The content of the message""" + + role: Literal["unknown", "user", "assistant", "system", "critic", "discriminator", "developer", "tool"] + """The role of the message. + + One of `unknown`, `user`, `assistant`, `system`, `critic`, `discriminator`, + `developer`, or `tool`. + """ + + status: Literal["in_progress", "completed", "incomplete"] + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["message"] + """The type of the message. Always set to `message`.""" + + phase: Optional[Literal["commentary", "final_answer"]] = None + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/output_text_content.py b/venv/lib/python3.12/site-packages/openai/types/conversations/output_text_content.py new file mode 100644 index 0000000..81c9259 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/output_text_content.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..responses.response_output_text import ResponseOutputText + +__all__ = ["OutputTextContent"] + +OutputTextContent = ResponseOutputText diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/output_text_content_param.py b/venv/lib/python3.12/site-packages/openai/types/conversations/output_text_content_param.py new file mode 100644 index 0000000..da1de7d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/output_text_content_param.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..responses.response_output_text_param import ResponseOutputTextParam + +OutputTextContentParam = ResponseOutputTextParam diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/refusal_content.py b/venv/lib/python3.12/site-packages/openai/types/conversations/refusal_content.py new file mode 100644 index 0000000..b9bbe1d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/refusal_content.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..responses.response_output_refusal import ResponseOutputRefusal + +__all__ = ["RefusalContent"] + +RefusalContent = ResponseOutputRefusal diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/refusal_content_param.py b/venv/lib/python3.12/site-packages/openai/types/conversations/refusal_content_param.py new file mode 100644 index 0000000..2fdc845 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/refusal_content_param.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..responses.response_output_refusal_param import ResponseOutputRefusalParam + +RefusalContentParam = ResponseOutputRefusalParam diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/summary_text_content.py b/venv/lib/python3.12/site-packages/openai/types/conversations/summary_text_content.py new file mode 100644 index 0000000..3a6e3c7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/summary_text_content.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SummaryTextContent"] + + +class SummaryTextContent(BaseModel): + """A summary text from the model.""" + + text: str + """A summary of the reasoning output from the model so far.""" + + type: Literal["summary_text"] + """The type of the object. Always `summary_text`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/conversations/text_content.py b/venv/lib/python3.12/site-packages/openai/types/conversations/text_content.py new file mode 100644 index 0000000..fbb2ef0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/conversations/text_content.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["TextContent"] + + +class TextContent(BaseModel): + """A text content.""" + + text: str + + type: Literal["text"] diff --git a/venv/lib/python3.12/site-packages/openai/types/create_embedding_response.py b/venv/lib/python3.12/site-packages/openai/types/create_embedding_response.py new file mode 100644 index 0000000..29b3f13 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/create_embedding_response.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from .._models import BaseModel +from .embedding import Embedding + +__all__ = ["CreateEmbeddingResponse", "Usage"] + + +class Usage(BaseModel): + """The usage information for the request.""" + + prompt_tokens: int + """The number of tokens used by the prompt.""" + + total_tokens: int + """The total number of tokens used by the request.""" + + +class CreateEmbeddingResponse(BaseModel): + data: List[Embedding] + """The list of embeddings generated by the model.""" + + model: str + """The name of the model used to generate the embedding.""" + + object: Literal["list"] + """The object type, which is always "list".""" + + usage: Usage + """The usage information for the request.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/deleted_skill.py b/venv/lib/python3.12/site-packages/openai/types/deleted_skill.py new file mode 100644 index 0000000..e6b6d14 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/deleted_skill.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["DeletedSkill"] + + +class DeletedSkill(BaseModel): + id: str + + deleted: bool + + object: Literal["skill.deleted"] diff --git a/venv/lib/python3.12/site-packages/openai/types/embedding.py b/venv/lib/python3.12/site-packages/openai/types/embedding.py new file mode 100644 index 0000000..f9aaa85 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/embedding.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["Embedding"] + + +class Embedding(BaseModel): + """Represents an embedding vector returned by embedding endpoint.""" + + embedding: List[float] + """The embedding vector, which is a list of floats. + + The length of vector depends on the model as listed in the + [embedding guide](https://developers.openai.com/api/docs/guides/embeddings). + """ + + index: int + """The index of the embedding in the list of embeddings.""" + + object: Literal["embedding"] + """The object type, which is always "embedding".""" diff --git a/venv/lib/python3.12/site-packages/openai/types/embedding_create_params.py b/venv/lib/python3.12/site-packages/openai/types/embedding_create_params.py new file mode 100644 index 0000000..9444ab5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/embedding_create_params.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypedDict + +from .._types import SequenceNotStr +from .embedding_model import EmbeddingModel + +__all__ = ["EmbeddingCreateParams"] + + +class EmbeddingCreateParams(TypedDict, total=False): + input: Required[Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]]]] + """Input text to embed, encoded as a string or array of tokens. + + To embed multiple inputs in a single request, pass an array of strings or array + of token arrays. The input must not exceed the max input tokens for the model + (8192 tokens for all embedding models), cannot be an empty string, and any array + must be 2048 dimensions or less. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. In addition to the per-input token limit, all embedding + models enforce a maximum of 300,000 tokens summed across all inputs in a single + request. + """ + + model: Required[Union[str, EmbeddingModel]] + """ID of the model to use. + + You can use the + [List models](https://developers.openai.com/api/reference/resources/models/methods/list) + API to see all of your available models, or see our + [Model overview](https://developers.openai.com/api/docs/models) for descriptions + of them. + """ + + dimensions: int + """The number of dimensions the resulting output embeddings should have. + + Only supported in `text-embedding-3` and later models. + """ + + encoding_format: Literal["float", "base64"] + """The format to return the embeddings in. + + Can be either `float` or [`base64`](https://pypi.org/project/pybase64/). + """ + + user: str + """ + A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/embedding_model.py b/venv/lib/python3.12/site-packages/openai/types/embedding_model.py new file mode 100644 index 0000000..f6e4e71 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/embedding_model.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["EmbeddingModel"] + +EmbeddingModel: TypeAlias = Literal["text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large"] diff --git a/venv/lib/python3.12/site-packages/openai/types/eval_create_params.py b/venv/lib/python3.12/site-packages/openai/types/eval_create_params.py new file mode 100644 index 0000000..419a04a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/eval_create_params.py @@ -0,0 +1,244 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .._types import SequenceNotStr +from .shared_params.metadata import Metadata +from .graders.grader_inputs_param import GraderInputsParam +from .graders.python_grader_param import PythonGraderParam +from .graders.score_model_grader_param import ScoreModelGraderParam +from .graders.string_check_grader_param import StringCheckGraderParam +from .responses.response_input_text_param import ResponseInputTextParam +from .graders.text_similarity_grader_param import TextSimilarityGraderParam +from .responses.response_input_audio_param import ResponseInputAudioParam + +__all__ = [ + "EvalCreateParams", + "DataSourceConfig", + "DataSourceConfigCustom", + "DataSourceConfigLogs", + "DataSourceConfigStoredCompletions", + "TestingCriterion", + "TestingCriterionLabelModel", + "TestingCriterionLabelModelInput", + "TestingCriterionLabelModelInputSimpleInputMessage", + "TestingCriterionLabelModelInputEvalItem", + "TestingCriterionLabelModelInputEvalItemContent", + "TestingCriterionLabelModelInputEvalItemContentOutputText", + "TestingCriterionLabelModelInputEvalItemContentInputImage", + "TestingCriterionTextSimilarity", + "TestingCriterionPython", + "TestingCriterionScoreModel", +] + + +class EvalCreateParams(TypedDict, total=False): + data_source_config: Required[DataSourceConfig] + """The configuration for the data source used for the evaluation runs. + + Dictates the schema of the data used in the evaluation. + """ + + testing_criteria: Required[Iterable[TestingCriterion]] + """A list of graders for all eval runs in this group. + + Graders can reference variables in the data source using double curly braces + notation, like `{{item.variable_name}}`. To reference the model's output, use + the `sample` namespace (ie, `{{sample.output_text}}`). + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the evaluation.""" + + +class DataSourceConfigCustom(TypedDict, total=False): + """ + A CustomDataSourceConfig object that defines the schema for the data source used for the evaluation runs. + This schema is used to define the shape of the data that will be: + - Used to define your testing criteria and + - What data is required when creating a run + """ + + item_schema: Required[Dict[str, object]] + """The json schema for each row in the data source.""" + + type: Required[Literal["custom"]] + """The type of data source. Always `custom`.""" + + include_sample_schema: bool + """ + Whether the eval should expect you to populate the sample namespace (ie, by + generating responses off of your data source) + """ + + +class DataSourceConfigLogs(TypedDict, total=False): + """ + A data source config which specifies the metadata property of your logs query. + This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + """ + + type: Required[Literal["logs"]] + """The type of data source. Always `logs`.""" + + metadata: Dict[str, object] + """Metadata filters for the logs data source.""" + + +class DataSourceConfigStoredCompletions(TypedDict, total=False): + """Deprecated in favor of LogsDataSourceConfig.""" + + type: Required[Literal["stored_completions"]] + """The type of data source. Always `stored_completions`.""" + + metadata: Dict[str, object] + """Metadata filters for the stored completions data source.""" + + +DataSourceConfig: TypeAlias = Union[DataSourceConfigCustom, DataSourceConfigLogs, DataSourceConfigStoredCompletions] + + +class TestingCriterionLabelModelInputSimpleInputMessage(TypedDict, total=False): + content: Required[str] + """The content of the message.""" + + role: Required[str] + """The role of the message (e.g. "system", "assistant", "user").""" + + +class TestingCriterionLabelModelInputEvalItemContentOutputText(TypedDict, total=False): + """A text output from the model.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +class TestingCriterionLabelModelInputEvalItemContentInputImage(TypedDict, total=False): + """An image input block used within EvalItem content arrays.""" + + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +TestingCriterionLabelModelInputEvalItemContent: TypeAlias = Union[ + str, + ResponseInputTextParam, + TestingCriterionLabelModelInputEvalItemContentOutputText, + TestingCriterionLabelModelInputEvalItemContentInputImage, + ResponseInputAudioParam, + GraderInputsParam, +] + + +class TestingCriterionLabelModelInputEvalItem(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: Required[TestingCriterionLabelModelInputEvalItemContent] + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" + + +TestingCriterionLabelModelInput: TypeAlias = Union[ + TestingCriterionLabelModelInputSimpleInputMessage, TestingCriterionLabelModelInputEvalItem +] + + +class TestingCriterionLabelModel(TypedDict, total=False): + """ + A LabelModelGrader object which uses a model to assign labels to each item + in the evaluation. + """ + + input: Required[Iterable[TestingCriterionLabelModelInput]] + """A list of chat messages forming the prompt or context. + + May include variable references to the `item` namespace, ie {{item.name}}. + """ + + labels: Required[SequenceNotStr[str]] + """The labels to classify to each item in the evaluation.""" + + model: Required[str] + """The model to use for the evaluation. Must support structured outputs.""" + + name: Required[str] + """The name of the grader.""" + + passing_labels: Required[SequenceNotStr[str]] + """The labels that indicate a passing result. Must be a subset of labels.""" + + type: Required[Literal["label_model"]] + """The object type, which is always `label_model`.""" + + +class TestingCriterionTextSimilarity(TextSimilarityGraderParam, total=False): + """A TextSimilarityGrader object which grades text based on similarity metrics.""" + + pass_threshold: Required[float] + """The threshold for the score.""" + + +class TestingCriterionPython(PythonGraderParam, total=False): + """A PythonGrader object that runs a python script on the input.""" + + pass_threshold: float + """The threshold for the score.""" + + +class TestingCriterionScoreModel(ScoreModelGraderParam, total=False): + """A ScoreModelGrader object that uses a model to assign a score to the input.""" + + pass_threshold: float + """The threshold for the score.""" + + +TestingCriterion: TypeAlias = Union[ + TestingCriterionLabelModel, + StringCheckGraderParam, + TestingCriterionTextSimilarity, + TestingCriterionPython, + TestingCriterionScoreModel, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/eval_create_response.py b/venv/lib/python3.12/site-packages/openai/types/eval_create_response.py new file mode 100644 index 0000000..fcd3e71 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/eval_create_response.py @@ -0,0 +1,130 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from .._utils import PropertyInfo +from .._models import BaseModel +from .shared.metadata import Metadata +from .graders.python_grader import PythonGrader +from .graders.label_model_grader import LabelModelGrader +from .graders.score_model_grader import ScoreModelGrader +from .graders.string_check_grader import StringCheckGrader +from .eval_custom_data_source_config import EvalCustomDataSourceConfig +from .graders.text_similarity_grader import TextSimilarityGrader +from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig + +__all__ = [ + "EvalCreateResponse", + "DataSourceConfig", + "DataSourceConfigLogs", + "TestingCriterion", + "TestingCriterionEvalGraderTextSimilarity", + "TestingCriterionEvalGraderPython", + "TestingCriterionEvalGraderScoreModel", +] + + +class DataSourceConfigLogs(BaseModel): + """ + A LogsDataSourceConfig which specifies the metadata property of your logs query. + This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + The schema returned by this data source config is used to defined what variables are available in your evals. + `item` and `sample` are both defined when using this data source config. + """ + + schema_: Dict[str, object] = FieldInfo(alias="schema") + """ + The json schema for the run data source items. Learn how to build JSON schemas + [here](https://json-schema.org/). + """ + + type: Literal["logs"] + """The type of data source. Always `logs`.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + +DataSourceConfig: TypeAlias = Annotated[ + Union[EvalCustomDataSourceConfig, DataSourceConfigLogs, EvalStoredCompletionsDataSourceConfig], + PropertyInfo(discriminator="type"), +] + + +class TestingCriterionEvalGraderTextSimilarity(TextSimilarityGrader): + __test__ = False + """A TextSimilarityGrader object which grades text based on similarity metrics.""" + pass_threshold: float + """The threshold for the score.""" + + +class TestingCriterionEvalGraderPython(PythonGrader): + __test__ = False + """A PythonGrader object that runs a python script on the input.""" + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + +class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): + __test__ = False + """A ScoreModelGrader object that uses a model to assign a score to the input.""" + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + +TestingCriterion: TypeAlias = Union[ + LabelModelGrader, + StringCheckGrader, + TestingCriterionEvalGraderTextSimilarity, + TestingCriterionEvalGraderPython, + TestingCriterionEvalGraderScoreModel, +] + + +class EvalCreateResponse(BaseModel): + """ + An Eval object with a data source config and testing criteria. + An Eval represents a task to be done for your LLM integration. + Like: + - Improve the quality of my chatbot + - See how well my chatbot handles customer support + - Check if o4-mini is better at my usecase than gpt-6-astra + """ + + id: str + """Unique identifier for the evaluation.""" + + created_at: int + """The Unix timestamp (in seconds) for when the eval was created.""" + + data_source_config: DataSourceConfig + """Configuration of data sources used in runs of the evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the evaluation.""" + + object: Literal["eval"] + """The object type.""" + + testing_criteria: List[TestingCriterion] + """A list of testing criteria.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/eval_custom_data_source_config.py b/venv/lib/python3.12/site-packages/openai/types/eval_custom_data_source_config.py new file mode 100644 index 0000000..19c3bec --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/eval_custom_data_source_config.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["EvalCustomDataSourceConfig"] + + +class EvalCustomDataSourceConfig(BaseModel): + """ + A CustomDataSourceConfig which specifies the schema of your `item` and optionally `sample` namespaces. + The response schema defines the shape of the data that will be: + - Used to define your testing criteria and + - What data is required when creating a run + """ + + schema_: Dict[str, object] = FieldInfo(alias="schema") + """ + The json schema for the run data source items. Learn how to build JSON schemas + [here](https://json-schema.org/). + """ + + type: Literal["custom"] + """The type of data source. Always `custom`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/eval_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/eval_delete_response.py new file mode 100644 index 0000000..7fd526d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/eval_delete_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["EvalDeleteResponse"] + + +class EvalDeleteResponse(BaseModel): + deleted: bool + + eval_id: str + + object: str diff --git a/venv/lib/python3.12/site-packages/openai/types/eval_list_params.py b/venv/lib/python3.12/site-packages/openai/types/eval_list_params.py new file mode 100644 index 0000000..fc882e5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/eval_list_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["EvalListParams"] + + +class EvalListParams(TypedDict, total=False): + after: str + """Identifier for the last eval from the previous pagination request.""" + + limit: int + """Number of evals to retrieve.""" + + order: Literal["asc", "desc"] + """Sort order for evals by timestamp. + + Use `asc` for ascending order or `desc` for descending order. + """ + + order_by: Literal["created_at", "updated_at"] + """Evals can be ordered by creation time or last updated time. + + Use `created_at` for creation time or `updated_at` for last updated time. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/eval_list_response.py b/venv/lib/python3.12/site-packages/openai/types/eval_list_response.py new file mode 100644 index 0000000..8e9a4da --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/eval_list_response.py @@ -0,0 +1,130 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from .._utils import PropertyInfo +from .._models import BaseModel +from .shared.metadata import Metadata +from .graders.python_grader import PythonGrader +from .graders.label_model_grader import LabelModelGrader +from .graders.score_model_grader import ScoreModelGrader +from .graders.string_check_grader import StringCheckGrader +from .eval_custom_data_source_config import EvalCustomDataSourceConfig +from .graders.text_similarity_grader import TextSimilarityGrader +from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig + +__all__ = [ + "EvalListResponse", + "DataSourceConfig", + "DataSourceConfigLogs", + "TestingCriterion", + "TestingCriterionEvalGraderTextSimilarity", + "TestingCriterionEvalGraderPython", + "TestingCriterionEvalGraderScoreModel", +] + + +class DataSourceConfigLogs(BaseModel): + """ + A LogsDataSourceConfig which specifies the metadata property of your logs query. + This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + The schema returned by this data source config is used to defined what variables are available in your evals. + `item` and `sample` are both defined when using this data source config. + """ + + schema_: Dict[str, object] = FieldInfo(alias="schema") + """ + The json schema for the run data source items. Learn how to build JSON schemas + [here](https://json-schema.org/). + """ + + type: Literal["logs"] + """The type of data source. Always `logs`.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + +DataSourceConfig: TypeAlias = Annotated[ + Union[EvalCustomDataSourceConfig, DataSourceConfigLogs, EvalStoredCompletionsDataSourceConfig], + PropertyInfo(discriminator="type"), +] + + +class TestingCriterionEvalGraderTextSimilarity(TextSimilarityGrader): + __test__ = False + """A TextSimilarityGrader object which grades text based on similarity metrics.""" + pass_threshold: float + """The threshold for the score.""" + + +class TestingCriterionEvalGraderPython(PythonGrader): + __test__ = False + """A PythonGrader object that runs a python script on the input.""" + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + +class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): + __test__ = False + """A ScoreModelGrader object that uses a model to assign a score to the input.""" + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + +TestingCriterion: TypeAlias = Union[ + LabelModelGrader, + StringCheckGrader, + TestingCriterionEvalGraderTextSimilarity, + TestingCriterionEvalGraderPython, + TestingCriterionEvalGraderScoreModel, +] + + +class EvalListResponse(BaseModel): + """ + An Eval object with a data source config and testing criteria. + An Eval represents a task to be done for your LLM integration. + Like: + - Improve the quality of my chatbot + - See how well my chatbot handles customer support + - Check if o4-mini is better at my usecase than gpt-6-astra + """ + + id: str + """Unique identifier for the evaluation.""" + + created_at: int + """The Unix timestamp (in seconds) for when the eval was created.""" + + data_source_config: DataSourceConfig + """Configuration of data sources used in runs of the evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the evaluation.""" + + object: Literal["eval"] + """The object type.""" + + testing_criteria: List[TestingCriterion] + """A list of testing criteria.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/eval_retrieve_response.py b/venv/lib/python3.12/site-packages/openai/types/eval_retrieve_response.py new file mode 100644 index 0000000..afbb934 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/eval_retrieve_response.py @@ -0,0 +1,130 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from .._utils import PropertyInfo +from .._models import BaseModel +from .shared.metadata import Metadata +from .graders.python_grader import PythonGrader +from .graders.label_model_grader import LabelModelGrader +from .graders.score_model_grader import ScoreModelGrader +from .graders.string_check_grader import StringCheckGrader +from .eval_custom_data_source_config import EvalCustomDataSourceConfig +from .graders.text_similarity_grader import TextSimilarityGrader +from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig + +__all__ = [ + "EvalRetrieveResponse", + "DataSourceConfig", + "DataSourceConfigLogs", + "TestingCriterion", + "TestingCriterionEvalGraderTextSimilarity", + "TestingCriterionEvalGraderPython", + "TestingCriterionEvalGraderScoreModel", +] + + +class DataSourceConfigLogs(BaseModel): + """ + A LogsDataSourceConfig which specifies the metadata property of your logs query. + This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + The schema returned by this data source config is used to defined what variables are available in your evals. + `item` and `sample` are both defined when using this data source config. + """ + + schema_: Dict[str, object] = FieldInfo(alias="schema") + """ + The json schema for the run data source items. Learn how to build JSON schemas + [here](https://json-schema.org/). + """ + + type: Literal["logs"] + """The type of data source. Always `logs`.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + +DataSourceConfig: TypeAlias = Annotated[ + Union[EvalCustomDataSourceConfig, DataSourceConfigLogs, EvalStoredCompletionsDataSourceConfig], + PropertyInfo(discriminator="type"), +] + + +class TestingCriterionEvalGraderTextSimilarity(TextSimilarityGrader): + __test__ = False + """A TextSimilarityGrader object which grades text based on similarity metrics.""" + pass_threshold: float + """The threshold for the score.""" + + +class TestingCriterionEvalGraderPython(PythonGrader): + __test__ = False + """A PythonGrader object that runs a python script on the input.""" + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + +class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): + __test__ = False + """A ScoreModelGrader object that uses a model to assign a score to the input.""" + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + +TestingCriterion: TypeAlias = Union[ + LabelModelGrader, + StringCheckGrader, + TestingCriterionEvalGraderTextSimilarity, + TestingCriterionEvalGraderPython, + TestingCriterionEvalGraderScoreModel, +] + + +class EvalRetrieveResponse(BaseModel): + """ + An Eval object with a data source config and testing criteria. + An Eval represents a task to be done for your LLM integration. + Like: + - Improve the quality of my chatbot + - See how well my chatbot handles customer support + - Check if o4-mini is better at my usecase than gpt-6-astra + """ + + id: str + """Unique identifier for the evaluation.""" + + created_at: int + """The Unix timestamp (in seconds) for when the eval was created.""" + + data_source_config: DataSourceConfig + """Configuration of data sources used in runs of the evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the evaluation.""" + + object: Literal["eval"] + """The object type.""" + + testing_criteria: List[TestingCriterion] + """A list of testing criteria.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/eval_stored_completions_data_source_config.py b/venv/lib/python3.12/site-packages/openai/types/eval_stored_completions_data_source_config.py new file mode 100644 index 0000000..c68e553 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/eval_stored_completions_data_source_config.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel +from .shared.metadata import Metadata + +__all__ = ["EvalStoredCompletionsDataSourceConfig"] + + +class EvalStoredCompletionsDataSourceConfig(BaseModel): + """Deprecated in favor of LogsDataSourceConfig.""" + + schema_: Dict[str, object] = FieldInfo(alias="schema") + """ + The json schema for the run data source items. Learn how to build JSON schemas + [here](https://json-schema.org/). + """ + + type: Literal["stored_completions"] + """The type of data source. Always `stored_completions`.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/eval_update_params.py b/venv/lib/python3.12/site-packages/openai/types/eval_update_params.py new file mode 100644 index 0000000..194b4ac --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/eval_update_params.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +from .shared_params.metadata import Metadata + +__all__ = ["EvalUpdateParams"] + + +class EvalUpdateParams(TypedDict, total=False): + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """Rename the evaluation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/eval_update_response.py b/venv/lib/python3.12/site-packages/openai/types/eval_update_response.py new file mode 100644 index 0000000..1e52219 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/eval_update_response.py @@ -0,0 +1,130 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from .._utils import PropertyInfo +from .._models import BaseModel +from .shared.metadata import Metadata +from .graders.python_grader import PythonGrader +from .graders.label_model_grader import LabelModelGrader +from .graders.score_model_grader import ScoreModelGrader +from .graders.string_check_grader import StringCheckGrader +from .eval_custom_data_source_config import EvalCustomDataSourceConfig +from .graders.text_similarity_grader import TextSimilarityGrader +from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig + +__all__ = [ + "EvalUpdateResponse", + "DataSourceConfig", + "DataSourceConfigLogs", + "TestingCriterion", + "TestingCriterionEvalGraderTextSimilarity", + "TestingCriterionEvalGraderPython", + "TestingCriterionEvalGraderScoreModel", +] + + +class DataSourceConfigLogs(BaseModel): + """ + A LogsDataSourceConfig which specifies the metadata property of your logs query. + This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + The schema returned by this data source config is used to defined what variables are available in your evals. + `item` and `sample` are both defined when using this data source config. + """ + + schema_: Dict[str, object] = FieldInfo(alias="schema") + """ + The json schema for the run data source items. Learn how to build JSON schemas + [here](https://json-schema.org/). + """ + + type: Literal["logs"] + """The type of data source. Always `logs`.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + +DataSourceConfig: TypeAlias = Annotated[ + Union[EvalCustomDataSourceConfig, DataSourceConfigLogs, EvalStoredCompletionsDataSourceConfig], + PropertyInfo(discriminator="type"), +] + + +class TestingCriterionEvalGraderTextSimilarity(TextSimilarityGrader): + __test__ = False + """A TextSimilarityGrader object which grades text based on similarity metrics.""" + pass_threshold: float + """The threshold for the score.""" + + +class TestingCriterionEvalGraderPython(PythonGrader): + __test__ = False + """A PythonGrader object that runs a python script on the input.""" + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + +class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): + __test__ = False + """A ScoreModelGrader object that uses a model to assign a score to the input.""" + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + +TestingCriterion: TypeAlias = Union[ + LabelModelGrader, + StringCheckGrader, + TestingCriterionEvalGraderTextSimilarity, + TestingCriterionEvalGraderPython, + TestingCriterionEvalGraderScoreModel, +] + + +class EvalUpdateResponse(BaseModel): + """ + An Eval object with a data source config and testing criteria. + An Eval represents a task to be done for your LLM integration. + Like: + - Improve the quality of my chatbot + - See how well my chatbot handles customer support + - Check if o4-mini is better at my usecase than gpt-6-astra + """ + + id: str + """Unique identifier for the evaluation.""" + + created_at: int + """The Unix timestamp (in seconds) for when the eval was created.""" + + data_source_config: DataSourceConfig + """Configuration of data sources used in runs of the evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the evaluation.""" + + object: Literal["eval"] + """The object type.""" + + testing_criteria: List[TestingCriterion] + """A list of testing criteria.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__init__.py b/venv/lib/python3.12/site-packages/openai/types/evals/__init__.py new file mode 100644 index 0000000..1c58a78 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/__init__.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .eval_api_error import EvalAPIError as EvalAPIError +from .run_list_params import RunListParams as RunListParams +from .run_create_params import RunCreateParams as RunCreateParams +from .run_list_response import RunListResponse as RunListResponse +from .run_cancel_response import RunCancelResponse as RunCancelResponse +from .run_create_response import RunCreateResponse as RunCreateResponse +from .run_delete_response import RunDeleteResponse as RunDeleteResponse +from .run_retrieve_response import RunRetrieveResponse as RunRetrieveResponse +from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource as CreateEvalJSONLRunDataSource +from .create_eval_completions_run_data_source import ( + CreateEvalCompletionsRunDataSource as CreateEvalCompletionsRunDataSource, +) +from .create_eval_jsonl_run_data_source_param import ( + CreateEvalJSONLRunDataSourceParam as CreateEvalJSONLRunDataSourceParam, +) +from .create_eval_completions_run_data_source_param import ( + CreateEvalCompletionsRunDataSourceParam as CreateEvalCompletionsRunDataSourceParam, +) diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b6d92c2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/create_eval_completions_run_data_source.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/create_eval_completions_run_data_source.cpython-312.pyc new file mode 100644 index 0000000..e88b294 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/create_eval_completions_run_data_source.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/create_eval_completions_run_data_source_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/create_eval_completions_run_data_source_param.cpython-312.pyc new file mode 100644 index 0000000..bb4f514 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/create_eval_completions_run_data_source_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/create_eval_jsonl_run_data_source.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/create_eval_jsonl_run_data_source.cpython-312.pyc new file mode 100644 index 0000000..980bf65 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/create_eval_jsonl_run_data_source.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/create_eval_jsonl_run_data_source_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/create_eval_jsonl_run_data_source_param.cpython-312.pyc new file mode 100644 index 0000000..82db041 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/create_eval_jsonl_run_data_source_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/eval_api_error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/eval_api_error.cpython-312.pyc new file mode 100644 index 0000000..8a2ab04 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/eval_api_error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_cancel_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_cancel_response.cpython-312.pyc new file mode 100644 index 0000000..b9f00f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_cancel_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_create_params.cpython-312.pyc new file mode 100644 index 0000000..2540b4c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_create_response.cpython-312.pyc new file mode 100644 index 0000000..edc23f5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_delete_response.cpython-312.pyc new file mode 100644 index 0000000..5cc5edd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_list_params.cpython-312.pyc new file mode 100644 index 0000000..51d77ce Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_list_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_list_response.cpython-312.pyc new file mode 100644 index 0000000..1dd7ded Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_list_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_retrieve_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_retrieve_response.cpython-312.pyc new file mode 100644 index 0000000..dcb73dc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/__pycache__/run_retrieve_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/create_eval_completions_run_data_source.py b/venv/lib/python3.12/site-packages/openai/types/evals/create_eval_completions_run_data_source.py new file mode 100644 index 0000000..3d23e4c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/create_eval_completions_run_data_source.py @@ -0,0 +1,252 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from ..shared.metadata import Metadata +from ..graders.grader_inputs import GraderInputs +from ..shared.reasoning_effort import ReasoningEffort +from ..shared.response_format_text import ResponseFormatText +from ..responses.easy_input_message import EasyInputMessage +from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio +from ..chat.chat_completion_function_tool import ChatCompletionFunctionTool +from ..shared.response_format_json_object import ResponseFormatJSONObject +from ..shared.response_format_json_schema import ResponseFormatJSONSchema + +__all__ = [ + "CreateEvalCompletionsRunDataSource", + "Source", + "SourceFileContent", + "SourceFileContentContent", + "SourceFileID", + "SourceStoredCompletions", + "InputMessages", + "InputMessagesTemplate", + "InputMessagesTemplateTemplate", + "InputMessagesTemplateTemplateEvalItem", + "InputMessagesTemplateTemplateEvalItemContent", + "InputMessagesTemplateTemplateEvalItemContentOutputText", + "InputMessagesTemplateTemplateEvalItemContentInputImage", + "InputMessagesItemReference", + "SamplingParams", + "SamplingParamsResponseFormat", +] + + +class SourceFileContentContent(BaseModel): + item: Dict[str, object] + + sample: Optional[Dict[str, object]] = None + + +class SourceFileContent(BaseModel): + content: List[SourceFileContentContent] + """The content of the jsonl file.""" + + type: Literal["file_content"] + """The type of jsonl source. Always `file_content`.""" + + +class SourceFileID(BaseModel): + id: str + """The identifier of the file.""" + + type: Literal["file_id"] + """The type of jsonl source. Always `file_id`.""" + + +class SourceStoredCompletions(BaseModel): + """A StoredCompletionsRunDataSource configuration describing a set of filters""" + + type: Literal["stored_completions"] + """The type of source. Always `stored_completions`.""" + + created_after: Optional[int] = None + """An optional Unix timestamp to filter items created after this time.""" + + created_before: Optional[int] = None + """An optional Unix timestamp to filter items created before this time.""" + + limit: Optional[int] = None + """An optional maximum number of items to return.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Optional[str] = None + """An optional model to filter by (e.g., 'gpt-6-astra').""" + + +Source: TypeAlias = Annotated[ + Union[SourceFileContent, SourceFileID, SourceStoredCompletions], PropertyInfo(discriminator="type") +] + + +class InputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class InputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ + str, + ResponseInputText, + InputMessagesTemplateTemplateEvalItemContentOutputText, + InputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudio, + GraderInputs, +] + + +class InputMessagesTemplateTemplateEvalItem(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: InputMessagesTemplateTemplateEvalItemContent + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +InputMessagesTemplateTemplate: TypeAlias = Union[EasyInputMessage, InputMessagesTemplateTemplateEvalItem] + + +class InputMessagesTemplate(BaseModel): + template: List[InputMessagesTemplateTemplate] + """A list of chat messages forming the prompt or context. + + May include variable references to the `item` namespace, ie {{item.name}}. + """ + + type: Literal["template"] + """The type of input messages. Always `template`.""" + + +class InputMessagesItemReference(BaseModel): + item_reference: str + """A reference to a variable in the `item` namespace. Ie, "item.input_trajectory" """ + + type: Literal["item_reference"] + """The type of input messages. Always `item_reference`.""" + + +InputMessages: TypeAlias = Annotated[ + Union[InputMessagesTemplate, InputMessagesItemReference], PropertyInfo(discriminator="type") +] + +SamplingParamsResponseFormat: TypeAlias = Union[ResponseFormatText, ResponseFormatJSONSchema, ResponseFormatJSONObject] + + +class SamplingParams(BaseModel): + max_completion_tokens: Optional[int] = None + """The maximum number of tokens in the generated output.""" + + reasoning_effort: Optional[ReasoningEffort] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + response_format: Optional[SamplingParamsResponseFormat] = None + """An object specifying the format that the model must output. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + seed: Optional[int] = None + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] = None + """A higher temperature increases randomness in the outputs.""" + + tools: Optional[List[ChatCompletionFunctionTool]] = None + """A list of tools the model may call. + + Currently, only functions are supported as a tool. Use this to provide a list of + functions the model may generate JSON inputs for. A max of 128 functions are + supported. + """ + + top_p: Optional[float] = None + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class CreateEvalCompletionsRunDataSource(BaseModel): + """A CompletionsRunDataSource object describing a model sampling configuration.""" + + source: Source + """Determines what populates the `item` namespace in this run's data source.""" + + type: Literal["completions"] + """The type of run data source. Always `completions`.""" + + input_messages: Optional[InputMessages] = None + """Used when sampling from a model. + + Dictates the structure of the messages passed into the model. Can either be a + reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template + with variable references to the `item` namespace. + """ + + model: Optional[str] = None + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + sampling_params: Optional[SamplingParams] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/create_eval_completions_run_data_source_param.py b/venv/lib/python3.12/site-packages/openai/types/evals/create_eval_completions_run_data_source_param.py new file mode 100644 index 0000000..bf636ef --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -0,0 +1,248 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..shared_params.metadata import Metadata +from ..shared.reasoning_effort import ReasoningEffort +from ..graders.grader_inputs_param import GraderInputsParam +from ..responses.easy_input_message_param import EasyInputMessageParam +from ..shared_params.response_format_text import ResponseFormatText +from ..responses.response_input_text_param import ResponseInputTextParam +from ..responses.response_input_audio_param import ResponseInputAudioParam +from ..chat.chat_completion_function_tool_param import ChatCompletionFunctionToolParam +from ..shared_params.response_format_json_object import ResponseFormatJSONObject +from ..shared_params.response_format_json_schema import ResponseFormatJSONSchema + +__all__ = [ + "CreateEvalCompletionsRunDataSourceParam", + "Source", + "SourceFileContent", + "SourceFileContentContent", + "SourceFileID", + "SourceStoredCompletions", + "InputMessages", + "InputMessagesTemplate", + "InputMessagesTemplateTemplate", + "InputMessagesTemplateTemplateEvalItem", + "InputMessagesTemplateTemplateEvalItemContent", + "InputMessagesTemplateTemplateEvalItemContentOutputText", + "InputMessagesTemplateTemplateEvalItemContentInputImage", + "InputMessagesItemReference", + "SamplingParams", + "SamplingParamsResponseFormat", +] + + +class SourceFileContentContent(TypedDict, total=False): + item: Required[Dict[str, object]] + + sample: Dict[str, object] + + +class SourceFileContent(TypedDict, total=False): + content: Required[Iterable[SourceFileContentContent]] + """The content of the jsonl file.""" + + type: Required[Literal["file_content"]] + """The type of jsonl source. Always `file_content`.""" + + +class SourceFileID(TypedDict, total=False): + id: Required[str] + """The identifier of the file.""" + + type: Required[Literal["file_id"]] + """The type of jsonl source. Always `file_id`.""" + + +class SourceStoredCompletions(TypedDict, total=False): + """A StoredCompletionsRunDataSource configuration describing a set of filters""" + + type: Required[Literal["stored_completions"]] + """The type of source. Always `stored_completions`.""" + + created_after: Optional[int] + """An optional Unix timestamp to filter items created after this time.""" + + created_before: Optional[int] + """An optional Unix timestamp to filter items created before this time.""" + + limit: Optional[int] + """An optional maximum number of items to return.""" + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Optional[str] + """An optional model to filter by (e.g., 'gpt-6-astra').""" + + +Source: TypeAlias = Union[SourceFileContent, SourceFileID, SourceStoredCompletions] + + +class InputMessagesTemplateTemplateEvalItemContentOutputText(TypedDict, total=False): + """A text output from the model.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +class InputMessagesTemplateTemplateEvalItemContentInputImage(TypedDict, total=False): + """An image input block used within EvalItem content arrays.""" + + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ + str, + ResponseInputTextParam, + InputMessagesTemplateTemplateEvalItemContentOutputText, + InputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudioParam, + GraderInputsParam, +] + + +class InputMessagesTemplateTemplateEvalItem(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: Required[InputMessagesTemplateTemplateEvalItemContent] + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" + + +InputMessagesTemplateTemplate: TypeAlias = Union[EasyInputMessageParam, InputMessagesTemplateTemplateEvalItem] + + +class InputMessagesTemplate(TypedDict, total=False): + template: Required[Iterable[InputMessagesTemplateTemplate]] + """A list of chat messages forming the prompt or context. + + May include variable references to the `item` namespace, ie {{item.name}}. + """ + + type: Required[Literal["template"]] + """The type of input messages. Always `template`.""" + + +class InputMessagesItemReference(TypedDict, total=False): + item_reference: Required[str] + """A reference to a variable in the `item` namespace. Ie, "item.input_trajectory" """ + + type: Required[Literal["item_reference"]] + """The type of input messages. Always `item_reference`.""" + + +InputMessages: TypeAlias = Union[InputMessagesTemplate, InputMessagesItemReference] + +SamplingParamsResponseFormat: TypeAlias = Union[ResponseFormatText, ResponseFormatJSONSchema, ResponseFormatJSONObject] + + +class SamplingParams(TypedDict, total=False): + max_completion_tokens: int + """The maximum number of tokens in the generated output.""" + + reasoning_effort: Optional[ReasoningEffort] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + response_format: SamplingParamsResponseFormat + """An object specifying the format that the model must output. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + seed: int + """A seed value to initialize the randomness, during sampling.""" + + temperature: float + """A higher temperature increases randomness in the outputs.""" + + tools: Iterable[ChatCompletionFunctionToolParam] + """A list of tools the model may call. + + Currently, only functions are supported as a tool. Use this to provide a list of + functions the model may generate JSON inputs for. A max of 128 functions are + supported. + """ + + top_p: float + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class CreateEvalCompletionsRunDataSourceParam(TypedDict, total=False): + """A CompletionsRunDataSource object describing a model sampling configuration.""" + + source: Required[Source] + """Determines what populates the `item` namespace in this run's data source.""" + + type: Required[Literal["completions"]] + """The type of run data source. Always `completions`.""" + + input_messages: InputMessages + """Used when sampling from a model. + + Dictates the structure of the messages passed into the model. Can either be a + reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template + with variable references to the `item` namespace. + """ + + model: str + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + sampling_params: SamplingParams diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/create_eval_jsonl_run_data_source.py b/venv/lib/python3.12/site-packages/openai/types/evals/create_eval_jsonl_run_data_source.py new file mode 100644 index 0000000..7f9e7f8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/create_eval_jsonl_run_data_source.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["CreateEvalJSONLRunDataSource", "Source", "SourceFileContent", "SourceFileContentContent", "SourceFileID"] + + +class SourceFileContentContent(BaseModel): + item: Dict[str, object] + + sample: Optional[Dict[str, object]] = None + + +class SourceFileContent(BaseModel): + content: List[SourceFileContentContent] + """The content of the jsonl file.""" + + type: Literal["file_content"] + """The type of jsonl source. Always `file_content`.""" + + +class SourceFileID(BaseModel): + id: str + """The identifier of the file.""" + + type: Literal["file_id"] + """The type of jsonl source. Always `file_id`.""" + + +Source: TypeAlias = Annotated[Union[SourceFileContent, SourceFileID], PropertyInfo(discriminator="type")] + + +class CreateEvalJSONLRunDataSource(BaseModel): + """ + A JsonlRunDataSource object with that specifies a JSONL file that matches the eval + """ + + source: Source + """Determines what populates the `item` namespace in the data source.""" + + type: Literal["jsonl"] + """The type of data source. Always `jsonl`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/create_eval_jsonl_run_data_source_param.py b/venv/lib/python3.12/site-packages/openai/types/evals/create_eval_jsonl_run_data_source_param.py new file mode 100644 index 0000000..f4e7c1d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/create_eval_jsonl_run_data_source_param.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "CreateEvalJSONLRunDataSourceParam", + "Source", + "SourceFileContent", + "SourceFileContentContent", + "SourceFileID", +] + + +class SourceFileContentContent(TypedDict, total=False): + item: Required[Dict[str, object]] + + sample: Dict[str, object] + + +class SourceFileContent(TypedDict, total=False): + content: Required[Iterable[SourceFileContentContent]] + """The content of the jsonl file.""" + + type: Required[Literal["file_content"]] + """The type of jsonl source. Always `file_content`.""" + + +class SourceFileID(TypedDict, total=False): + id: Required[str] + """The identifier of the file.""" + + type: Required[Literal["file_id"]] + """The type of jsonl source. Always `file_id`.""" + + +Source: TypeAlias = Union[SourceFileContent, SourceFileID] + + +class CreateEvalJSONLRunDataSourceParam(TypedDict, total=False): + """ + A JsonlRunDataSource object with that specifies a JSONL file that matches the eval + """ + + source: Required[Source] + """Determines what populates the `item` namespace in the data source.""" + + type: Required[Literal["jsonl"]] + """The type of data source. Always `jsonl`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/eval_api_error.py b/venv/lib/python3.12/site-packages/openai/types/evals/eval_api_error.py new file mode 100644 index 0000000..f566803 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/eval_api_error.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["EvalAPIError"] + + +class EvalAPIError(BaseModel): + """An object representing an error response from the Eval API.""" + + code: str + """The error code.""" + + message: str + """The error message.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/run_cancel_response.py b/venv/lib/python3.12/site-packages/openai/types/evals/run_cancel_response.py new file mode 100644 index 0000000..cd6de0f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/run_cancel_response.py @@ -0,0 +1,441 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .eval_api_error import EvalAPIError +from ..responses.tool import Tool +from ..shared.metadata import Metadata +from ..graders.grader_inputs import GraderInputs +from ..shared.reasoning_effort import ReasoningEffort +from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio +from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource +from ..responses.response_format_text_config import ResponseFormatTextConfig +from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource + +__all__ = [ + "RunCancelResponse", + "DataSource", + "DataSourceResponses", + "DataSourceResponsesSource", + "DataSourceResponsesSourceFileContent", + "DataSourceResponsesSourceFileContentContent", + "DataSourceResponsesSourceFileID", + "DataSourceResponsesSourceResponses", + "DataSourceResponsesInputMessages", + "DataSourceResponsesInputMessagesTemplate", + "DataSourceResponsesInputMessagesTemplateTemplate", + "DataSourceResponsesInputMessagesTemplateTemplateChatMessage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", + "DataSourceResponsesInputMessagesItemReference", + "DataSourceResponsesSamplingParams", + "DataSourceResponsesSamplingParamsText", + "PerModelUsage", + "PerTestingCriteriaResult", + "ResultCounts", +] + + +class DataSourceResponsesSourceFileContentContent(BaseModel): + item: Dict[str, object] + + sample: Optional[Dict[str, object]] = None + + +class DataSourceResponsesSourceFileContent(BaseModel): + content: List[DataSourceResponsesSourceFileContentContent] + """The content of the jsonl file.""" + + type: Literal["file_content"] + """The type of jsonl source. Always `file_content`.""" + + +class DataSourceResponsesSourceFileID(BaseModel): + id: str + """The identifier of the file.""" + + type: Literal["file_id"] + """The type of jsonl source. Always `file_id`.""" + + +class DataSourceResponsesSourceResponses(BaseModel): + """A EvalResponsesSource object describing a run data source configuration.""" + + type: Literal["responses"] + """The type of run data source. Always `responses`.""" + + created_after: Optional[int] = None + """Only include items created after this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + created_before: Optional[int] = None + """Only include items created before this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + instructions_search: Optional[str] = None + """Optional string to search the 'instructions' field. + + This is a query parameter used to select responses. + """ + + metadata: Optional[object] = None + """Metadata filter for the responses. + + This is a query parameter used to select responses. + """ + + model: Optional[str] = None + """The name of the model to find responses for. + + This is a query parameter used to select responses. + """ + + reasoning_effort: Optional[ReasoningEffort] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + temperature: Optional[float] = None + """Sampling temperature. This is a query parameter used to select responses.""" + + tools: Optional[List[str]] = None + """List of tool names. This is a query parameter used to select responses.""" + + top_p: Optional[float] = None + """Nucleus sampling parameter. This is a query parameter used to select responses.""" + + users: Optional[List[str]] = None + """List of user identifiers. This is a query parameter used to select responses.""" + + +DataSourceResponsesSource: TypeAlias = Annotated[ + Union[DataSourceResponsesSourceFileContent, DataSourceResponsesSourceFileID, DataSourceResponsesSourceResponses], + PropertyInfo(discriminator="type"), +] + + +class DataSourceResponsesInputMessagesTemplateTemplateChatMessage(BaseModel): + content: str + """The content of the message.""" + + role: str + """The role of the message (e.g. "system", "assistant", "user").""" + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ + str, + ResponseInputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudio, + GraderInputs, +] + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +DataSourceResponsesInputMessagesTemplateTemplate: TypeAlias = Union[ + DataSourceResponsesInputMessagesTemplateTemplateChatMessage, + DataSourceResponsesInputMessagesTemplateTemplateEvalItem, +] + + +class DataSourceResponsesInputMessagesTemplate(BaseModel): + template: List[DataSourceResponsesInputMessagesTemplateTemplate] + """A list of chat messages forming the prompt or context. + + May include variable references to the `item` namespace, ie {{item.name}}. + """ + + type: Literal["template"] + """The type of input messages. Always `template`.""" + + +class DataSourceResponsesInputMessagesItemReference(BaseModel): + item_reference: str + """A reference to a variable in the `item` namespace. Ie, "item.name" """ + + type: Literal["item_reference"] + """The type of input messages. Always `item_reference`.""" + + +DataSourceResponsesInputMessages: TypeAlias = Annotated[ + Union[DataSourceResponsesInputMessagesTemplate, DataSourceResponsesInputMessagesItemReference], + PropertyInfo(discriminator="type"), +] + + +class DataSourceResponsesSamplingParamsText(BaseModel): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + format: Optional[ResponseFormatTextConfig] = None + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + +class DataSourceResponsesSamplingParams(BaseModel): + max_completion_tokens: Optional[int] = None + """The maximum number of tokens in the generated output.""" + + reasoning_effort: Optional[ReasoningEffort] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + seed: Optional[int] = None + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] = None + """A higher temperature increases randomness in the outputs.""" + + text: Optional[DataSourceResponsesSamplingParamsText] = None + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + tools: Optional[List[Tool]] = None + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + """ + + top_p: Optional[float] = None + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class DataSourceResponses(BaseModel): + """A ResponsesRunDataSource object describing a model sampling configuration.""" + + source: DataSourceResponsesSource + """Determines what populates the `item` namespace in this run's data source.""" + + type: Literal["responses"] + """The type of run data source. Always `responses`.""" + + input_messages: Optional[DataSourceResponsesInputMessages] = None + """Used when sampling from a model. + + Dictates the structure of the messages passed into the model. Can either be a + reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template + with variable references to the `item` namespace. + """ + + model: Optional[str] = None + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + sampling_params: Optional[DataSourceResponsesSamplingParams] = None + + +DataSource: TypeAlias = Annotated[ + Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource, DataSourceResponses], + PropertyInfo(discriminator="type"), +] + + +class PerModelUsage(BaseModel): + cached_tokens: int + """The number of tokens retrieved from cache.""" + + completion_tokens: int + """The number of completion tokens generated.""" + + invocation_count: int + """The number of invocations.""" + + run_model_name: str = FieldInfo(alias="model_name") + """The name of the model.""" + + prompt_tokens: int + """The number of prompt tokens used.""" + + total_tokens: int + """The total number of tokens used.""" + + +class PerTestingCriteriaResult(BaseModel): + failed: int + """Number of tests failed for this criteria.""" + + passed: int + """Number of tests passed for this criteria.""" + + testing_criteria: str + """A description of the testing criteria.""" + + +class ResultCounts(BaseModel): + """Counters summarizing the outcomes of the evaluation run.""" + + errored: int + """Number of output items that resulted in an error.""" + + failed: int + """Number of output items that failed to pass the evaluation.""" + + passed: int + """Number of output items that passed the evaluation.""" + + total: int + """Total number of executed output items.""" + + +class RunCancelResponse(BaseModel): + """A schema representing an evaluation run.""" + + id: str + """Unique identifier for the evaluation run.""" + + created_at: int + """Unix timestamp (in seconds) when the evaluation run was created.""" + + data_source: DataSource + """Information about the run's data source.""" + + error: EvalAPIError + """An object representing an error response from the Eval API.""" + + eval_id: str + """The identifier of the associated evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: str + """The model that is evaluated, if applicable.""" + + name: str + """The name of the evaluation run.""" + + object: Literal["eval.run"] + """The type of the object. Always "eval.run".""" + + per_model_usage: List[PerModelUsage] + """Usage statistics for each model during the evaluation run.""" + + per_testing_criteria_results: List[PerTestingCriteriaResult] + """Results per testing criteria applied during the evaluation run.""" + + report_url: str + """The URL to the rendered evaluation run report on the UI dashboard.""" + + result_counts: ResultCounts + """Counters summarizing the outcomes of the evaluation run.""" + + status: str + """The status of the evaluation run.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/run_create_params.py b/venv/lib/python3.12/site-packages/openai/types/evals/run_create_params.py new file mode 100644 index 0000000..9e701d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/run_create_params.py @@ -0,0 +1,360 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from ..responses.tool_param import ToolParam +from ..shared_params.metadata import Metadata +from ..shared.reasoning_effort import ReasoningEffort +from ..graders.grader_inputs_param import GraderInputsParam +from ..responses.response_input_text_param import ResponseInputTextParam +from ..responses.response_input_audio_param import ResponseInputAudioParam +from .create_eval_jsonl_run_data_source_param import CreateEvalJSONLRunDataSourceParam +from ..responses.response_format_text_config_param import ResponseFormatTextConfigParam +from .create_eval_completions_run_data_source_param import CreateEvalCompletionsRunDataSourceParam + +__all__ = [ + "RunCreateParams", + "DataSource", + "DataSourceCreateEvalResponsesRunDataSource", + "DataSourceCreateEvalResponsesRunDataSourceSource", + "DataSourceCreateEvalResponsesRunDataSourceSourceFileContent", + "DataSourceCreateEvalResponsesRunDataSourceSourceFileContentContent", + "DataSourceCreateEvalResponsesRunDataSourceSourceFileID", + "DataSourceCreateEvalResponsesRunDataSourceSourceResponses", + "DataSourceCreateEvalResponsesRunDataSourceInputMessages", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplate", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplate", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateChatMessage", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItem", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesItemReference", + "DataSourceCreateEvalResponsesRunDataSourceSamplingParams", + "DataSourceCreateEvalResponsesRunDataSourceSamplingParamsText", +] + + +class RunCreateParams(TypedDict, total=False): + data_source: Required[DataSource] + """Details about the run's data source.""" + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the run.""" + + +class DataSourceCreateEvalResponsesRunDataSourceSourceFileContentContent(TypedDict, total=False): + item: Required[Dict[str, object]] + + sample: Dict[str, object] + + +class DataSourceCreateEvalResponsesRunDataSourceSourceFileContent(TypedDict, total=False): + content: Required[Iterable[DataSourceCreateEvalResponsesRunDataSourceSourceFileContentContent]] + """The content of the jsonl file.""" + + type: Required[Literal["file_content"]] + """The type of jsonl source. Always `file_content`.""" + + +class DataSourceCreateEvalResponsesRunDataSourceSourceFileID(TypedDict, total=False): + id: Required[str] + """The identifier of the file.""" + + type: Required[Literal["file_id"]] + """The type of jsonl source. Always `file_id`.""" + + +class DataSourceCreateEvalResponsesRunDataSourceSourceResponses(TypedDict, total=False): + """A EvalResponsesSource object describing a run data source configuration.""" + + type: Required[Literal["responses"]] + """The type of run data source. Always `responses`.""" + + created_after: Optional[int] + """Only include items created after this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + created_before: Optional[int] + """Only include items created before this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + instructions_search: Optional[str] + """Optional string to search the 'instructions' field. + + This is a query parameter used to select responses. + """ + + metadata: Optional[object] + """Metadata filter for the responses. + + This is a query parameter used to select responses. + """ + + model: Optional[str] + """The name of the model to find responses for. + + This is a query parameter used to select responses. + """ + + reasoning_effort: Optional[ReasoningEffort] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + temperature: Optional[float] + """Sampling temperature. This is a query parameter used to select responses.""" + + tools: Optional[SequenceNotStr[str]] + """List of tool names. This is a query parameter used to select responses.""" + + top_p: Optional[float] + """Nucleus sampling parameter. This is a query parameter used to select responses.""" + + users: Optional[SequenceNotStr[str]] + """List of user identifiers. This is a query parameter used to select responses.""" + + +DataSourceCreateEvalResponsesRunDataSourceSource: TypeAlias = Union[ + DataSourceCreateEvalResponsesRunDataSourceSourceFileContent, + DataSourceCreateEvalResponsesRunDataSourceSourceFileID, + DataSourceCreateEvalResponsesRunDataSourceSourceResponses, +] + + +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateChatMessage(TypedDict, total=False): + content: Required[str] + """The content of the message.""" + + role: Required[str] + """The role of the message (e.g. "system", "assistant", "user").""" + + +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText( + TypedDict, total=False +): + """A text output from the model.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage( + TypedDict, total=False +): + """An image input block used within EvalItem content arrays.""" + + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ + str, + ResponseInputTextParam, + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText, + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudioParam, + GraderInputsParam, +] + + +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItem(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: Required[DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent] + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" + + +DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplate: TypeAlias = Union[ + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateChatMessage, + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItem, +] + + +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplate(TypedDict, total=False): + template: Required[Iterable[DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplate]] + """A list of chat messages forming the prompt or context. + + May include variable references to the `item` namespace, ie {{item.name}}. + """ + + type: Required[Literal["template"]] + """The type of input messages. Always `template`.""" + + +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesItemReference(TypedDict, total=False): + item_reference: Required[str] + """A reference to a variable in the `item` namespace. Ie, "item.name" """ + + type: Required[Literal["item_reference"]] + """The type of input messages. Always `item_reference`.""" + + +DataSourceCreateEvalResponsesRunDataSourceInputMessages: TypeAlias = Union[ + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplate, + DataSourceCreateEvalResponsesRunDataSourceInputMessagesItemReference, +] + + +class DataSourceCreateEvalResponsesRunDataSourceSamplingParamsText(TypedDict, total=False): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + format: ResponseFormatTextConfigParam + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + +class DataSourceCreateEvalResponsesRunDataSourceSamplingParams(TypedDict, total=False): + max_completion_tokens: int + """The maximum number of tokens in the generated output.""" + + reasoning_effort: Optional[ReasoningEffort] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + seed: int + """A seed value to initialize the randomness, during sampling.""" + + temperature: float + """A higher temperature increases randomness in the outputs.""" + + text: DataSourceCreateEvalResponsesRunDataSourceSamplingParamsText + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + tools: Iterable[ToolParam] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + """ + + top_p: float + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class DataSourceCreateEvalResponsesRunDataSource(TypedDict, total=False): + """A ResponsesRunDataSource object describing a model sampling configuration.""" + + source: Required[DataSourceCreateEvalResponsesRunDataSourceSource] + """Determines what populates the `item` namespace in this run's data source.""" + + type: Required[Literal["responses"]] + """The type of run data source. Always `responses`.""" + + input_messages: DataSourceCreateEvalResponsesRunDataSourceInputMessages + """Used when sampling from a model. + + Dictates the structure of the messages passed into the model. Can either be a + reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template + with variable references to the `item` namespace. + """ + + model: str + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + sampling_params: DataSourceCreateEvalResponsesRunDataSourceSamplingParams + + +DataSource: TypeAlias = Union[ + CreateEvalJSONLRunDataSourceParam, + CreateEvalCompletionsRunDataSourceParam, + DataSourceCreateEvalResponsesRunDataSource, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/run_create_response.py b/venv/lib/python3.12/site-packages/openai/types/evals/run_create_response.py new file mode 100644 index 0000000..1eb6348 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/run_create_response.py @@ -0,0 +1,441 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .eval_api_error import EvalAPIError +from ..responses.tool import Tool +from ..shared.metadata import Metadata +from ..graders.grader_inputs import GraderInputs +from ..shared.reasoning_effort import ReasoningEffort +from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio +from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource +from ..responses.response_format_text_config import ResponseFormatTextConfig +from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource + +__all__ = [ + "RunCreateResponse", + "DataSource", + "DataSourceResponses", + "DataSourceResponsesSource", + "DataSourceResponsesSourceFileContent", + "DataSourceResponsesSourceFileContentContent", + "DataSourceResponsesSourceFileID", + "DataSourceResponsesSourceResponses", + "DataSourceResponsesInputMessages", + "DataSourceResponsesInputMessagesTemplate", + "DataSourceResponsesInputMessagesTemplateTemplate", + "DataSourceResponsesInputMessagesTemplateTemplateChatMessage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", + "DataSourceResponsesInputMessagesItemReference", + "DataSourceResponsesSamplingParams", + "DataSourceResponsesSamplingParamsText", + "PerModelUsage", + "PerTestingCriteriaResult", + "ResultCounts", +] + + +class DataSourceResponsesSourceFileContentContent(BaseModel): + item: Dict[str, object] + + sample: Optional[Dict[str, object]] = None + + +class DataSourceResponsesSourceFileContent(BaseModel): + content: List[DataSourceResponsesSourceFileContentContent] + """The content of the jsonl file.""" + + type: Literal["file_content"] + """The type of jsonl source. Always `file_content`.""" + + +class DataSourceResponsesSourceFileID(BaseModel): + id: str + """The identifier of the file.""" + + type: Literal["file_id"] + """The type of jsonl source. Always `file_id`.""" + + +class DataSourceResponsesSourceResponses(BaseModel): + """A EvalResponsesSource object describing a run data source configuration.""" + + type: Literal["responses"] + """The type of run data source. Always `responses`.""" + + created_after: Optional[int] = None + """Only include items created after this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + created_before: Optional[int] = None + """Only include items created before this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + instructions_search: Optional[str] = None + """Optional string to search the 'instructions' field. + + This is a query parameter used to select responses. + """ + + metadata: Optional[object] = None + """Metadata filter for the responses. + + This is a query parameter used to select responses. + """ + + model: Optional[str] = None + """The name of the model to find responses for. + + This is a query parameter used to select responses. + """ + + reasoning_effort: Optional[ReasoningEffort] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + temperature: Optional[float] = None + """Sampling temperature. This is a query parameter used to select responses.""" + + tools: Optional[List[str]] = None + """List of tool names. This is a query parameter used to select responses.""" + + top_p: Optional[float] = None + """Nucleus sampling parameter. This is a query parameter used to select responses.""" + + users: Optional[List[str]] = None + """List of user identifiers. This is a query parameter used to select responses.""" + + +DataSourceResponsesSource: TypeAlias = Annotated[ + Union[DataSourceResponsesSourceFileContent, DataSourceResponsesSourceFileID, DataSourceResponsesSourceResponses], + PropertyInfo(discriminator="type"), +] + + +class DataSourceResponsesInputMessagesTemplateTemplateChatMessage(BaseModel): + content: str + """The content of the message.""" + + role: str + """The role of the message (e.g. "system", "assistant", "user").""" + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ + str, + ResponseInputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudio, + GraderInputs, +] + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +DataSourceResponsesInputMessagesTemplateTemplate: TypeAlias = Union[ + DataSourceResponsesInputMessagesTemplateTemplateChatMessage, + DataSourceResponsesInputMessagesTemplateTemplateEvalItem, +] + + +class DataSourceResponsesInputMessagesTemplate(BaseModel): + template: List[DataSourceResponsesInputMessagesTemplateTemplate] + """A list of chat messages forming the prompt or context. + + May include variable references to the `item` namespace, ie {{item.name}}. + """ + + type: Literal["template"] + """The type of input messages. Always `template`.""" + + +class DataSourceResponsesInputMessagesItemReference(BaseModel): + item_reference: str + """A reference to a variable in the `item` namespace. Ie, "item.name" """ + + type: Literal["item_reference"] + """The type of input messages. Always `item_reference`.""" + + +DataSourceResponsesInputMessages: TypeAlias = Annotated[ + Union[DataSourceResponsesInputMessagesTemplate, DataSourceResponsesInputMessagesItemReference], + PropertyInfo(discriminator="type"), +] + + +class DataSourceResponsesSamplingParamsText(BaseModel): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + format: Optional[ResponseFormatTextConfig] = None + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + +class DataSourceResponsesSamplingParams(BaseModel): + max_completion_tokens: Optional[int] = None + """The maximum number of tokens in the generated output.""" + + reasoning_effort: Optional[ReasoningEffort] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + seed: Optional[int] = None + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] = None + """A higher temperature increases randomness in the outputs.""" + + text: Optional[DataSourceResponsesSamplingParamsText] = None + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + tools: Optional[List[Tool]] = None + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + """ + + top_p: Optional[float] = None + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class DataSourceResponses(BaseModel): + """A ResponsesRunDataSource object describing a model sampling configuration.""" + + source: DataSourceResponsesSource + """Determines what populates the `item` namespace in this run's data source.""" + + type: Literal["responses"] + """The type of run data source. Always `responses`.""" + + input_messages: Optional[DataSourceResponsesInputMessages] = None + """Used when sampling from a model. + + Dictates the structure of the messages passed into the model. Can either be a + reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template + with variable references to the `item` namespace. + """ + + model: Optional[str] = None + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + sampling_params: Optional[DataSourceResponsesSamplingParams] = None + + +DataSource: TypeAlias = Annotated[ + Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource, DataSourceResponses], + PropertyInfo(discriminator="type"), +] + + +class PerModelUsage(BaseModel): + cached_tokens: int + """The number of tokens retrieved from cache.""" + + completion_tokens: int + """The number of completion tokens generated.""" + + invocation_count: int + """The number of invocations.""" + + run_model_name: str = FieldInfo(alias="model_name") + """The name of the model.""" + + prompt_tokens: int + """The number of prompt tokens used.""" + + total_tokens: int + """The total number of tokens used.""" + + +class PerTestingCriteriaResult(BaseModel): + failed: int + """Number of tests failed for this criteria.""" + + passed: int + """Number of tests passed for this criteria.""" + + testing_criteria: str + """A description of the testing criteria.""" + + +class ResultCounts(BaseModel): + """Counters summarizing the outcomes of the evaluation run.""" + + errored: int + """Number of output items that resulted in an error.""" + + failed: int + """Number of output items that failed to pass the evaluation.""" + + passed: int + """Number of output items that passed the evaluation.""" + + total: int + """Total number of executed output items.""" + + +class RunCreateResponse(BaseModel): + """A schema representing an evaluation run.""" + + id: str + """Unique identifier for the evaluation run.""" + + created_at: int + """Unix timestamp (in seconds) when the evaluation run was created.""" + + data_source: DataSource + """Information about the run's data source.""" + + error: EvalAPIError + """An object representing an error response from the Eval API.""" + + eval_id: str + """The identifier of the associated evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: str + """The model that is evaluated, if applicable.""" + + name: str + """The name of the evaluation run.""" + + object: Literal["eval.run"] + """The type of the object. Always "eval.run".""" + + per_model_usage: List[PerModelUsage] + """Usage statistics for each model during the evaluation run.""" + + per_testing_criteria_results: List[PerTestingCriteriaResult] + """Results per testing criteria applied during the evaluation run.""" + + report_url: str + """The URL to the rendered evaluation run report on the UI dashboard.""" + + result_counts: ResultCounts + """Counters summarizing the outcomes of the evaluation run.""" + + status: str + """The status of the evaluation run.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/run_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/evals/run_delete_response.py new file mode 100644 index 0000000..cd65712 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/run_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["RunDeleteResponse"] + + +class RunDeleteResponse(BaseModel): + deleted: Optional[bool] = None + + object: Optional[str] = None + + run_id: Optional[str] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/run_list_params.py b/venv/lib/python3.12/site-packages/openai/types/evals/run_list_params.py new file mode 100644 index 0000000..040b8dd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/run_list_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["RunListParams"] + + +class RunListParams(TypedDict, total=False): + after: str + """Identifier for the last run from the previous pagination request.""" + + limit: int + """Number of runs to retrieve.""" + + order: Literal["asc", "desc"] + """Sort order for runs by timestamp. + + Use `asc` for ascending order or `desc` for descending order. Defaults to `asc`. + """ + + status: Literal["queued", "in_progress", "completed", "canceled", "failed"] + """Filter runs by status. + + One of `queued` | `in_progress` | `failed` | `completed` | `canceled`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/run_list_response.py b/venv/lib/python3.12/site-packages/openai/types/evals/run_list_response.py new file mode 100644 index 0000000..1185ab4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/run_list_response.py @@ -0,0 +1,441 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .eval_api_error import EvalAPIError +from ..responses.tool import Tool +from ..shared.metadata import Metadata +from ..graders.grader_inputs import GraderInputs +from ..shared.reasoning_effort import ReasoningEffort +from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio +from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource +from ..responses.response_format_text_config import ResponseFormatTextConfig +from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource + +__all__ = [ + "RunListResponse", + "DataSource", + "DataSourceResponses", + "DataSourceResponsesSource", + "DataSourceResponsesSourceFileContent", + "DataSourceResponsesSourceFileContentContent", + "DataSourceResponsesSourceFileID", + "DataSourceResponsesSourceResponses", + "DataSourceResponsesInputMessages", + "DataSourceResponsesInputMessagesTemplate", + "DataSourceResponsesInputMessagesTemplateTemplate", + "DataSourceResponsesInputMessagesTemplateTemplateChatMessage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", + "DataSourceResponsesInputMessagesItemReference", + "DataSourceResponsesSamplingParams", + "DataSourceResponsesSamplingParamsText", + "PerModelUsage", + "PerTestingCriteriaResult", + "ResultCounts", +] + + +class DataSourceResponsesSourceFileContentContent(BaseModel): + item: Dict[str, object] + + sample: Optional[Dict[str, object]] = None + + +class DataSourceResponsesSourceFileContent(BaseModel): + content: List[DataSourceResponsesSourceFileContentContent] + """The content of the jsonl file.""" + + type: Literal["file_content"] + """The type of jsonl source. Always `file_content`.""" + + +class DataSourceResponsesSourceFileID(BaseModel): + id: str + """The identifier of the file.""" + + type: Literal["file_id"] + """The type of jsonl source. Always `file_id`.""" + + +class DataSourceResponsesSourceResponses(BaseModel): + """A EvalResponsesSource object describing a run data source configuration.""" + + type: Literal["responses"] + """The type of run data source. Always `responses`.""" + + created_after: Optional[int] = None + """Only include items created after this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + created_before: Optional[int] = None + """Only include items created before this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + instructions_search: Optional[str] = None + """Optional string to search the 'instructions' field. + + This is a query parameter used to select responses. + """ + + metadata: Optional[object] = None + """Metadata filter for the responses. + + This is a query parameter used to select responses. + """ + + model: Optional[str] = None + """The name of the model to find responses for. + + This is a query parameter used to select responses. + """ + + reasoning_effort: Optional[ReasoningEffort] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + temperature: Optional[float] = None + """Sampling temperature. This is a query parameter used to select responses.""" + + tools: Optional[List[str]] = None + """List of tool names. This is a query parameter used to select responses.""" + + top_p: Optional[float] = None + """Nucleus sampling parameter. This is a query parameter used to select responses.""" + + users: Optional[List[str]] = None + """List of user identifiers. This is a query parameter used to select responses.""" + + +DataSourceResponsesSource: TypeAlias = Annotated[ + Union[DataSourceResponsesSourceFileContent, DataSourceResponsesSourceFileID, DataSourceResponsesSourceResponses], + PropertyInfo(discriminator="type"), +] + + +class DataSourceResponsesInputMessagesTemplateTemplateChatMessage(BaseModel): + content: str + """The content of the message.""" + + role: str + """The role of the message (e.g. "system", "assistant", "user").""" + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ + str, + ResponseInputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudio, + GraderInputs, +] + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +DataSourceResponsesInputMessagesTemplateTemplate: TypeAlias = Union[ + DataSourceResponsesInputMessagesTemplateTemplateChatMessage, + DataSourceResponsesInputMessagesTemplateTemplateEvalItem, +] + + +class DataSourceResponsesInputMessagesTemplate(BaseModel): + template: List[DataSourceResponsesInputMessagesTemplateTemplate] + """A list of chat messages forming the prompt or context. + + May include variable references to the `item` namespace, ie {{item.name}}. + """ + + type: Literal["template"] + """The type of input messages. Always `template`.""" + + +class DataSourceResponsesInputMessagesItemReference(BaseModel): + item_reference: str + """A reference to a variable in the `item` namespace. Ie, "item.name" """ + + type: Literal["item_reference"] + """The type of input messages. Always `item_reference`.""" + + +DataSourceResponsesInputMessages: TypeAlias = Annotated[ + Union[DataSourceResponsesInputMessagesTemplate, DataSourceResponsesInputMessagesItemReference], + PropertyInfo(discriminator="type"), +] + + +class DataSourceResponsesSamplingParamsText(BaseModel): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + format: Optional[ResponseFormatTextConfig] = None + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + +class DataSourceResponsesSamplingParams(BaseModel): + max_completion_tokens: Optional[int] = None + """The maximum number of tokens in the generated output.""" + + reasoning_effort: Optional[ReasoningEffort] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + seed: Optional[int] = None + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] = None + """A higher temperature increases randomness in the outputs.""" + + text: Optional[DataSourceResponsesSamplingParamsText] = None + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + tools: Optional[List[Tool]] = None + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + """ + + top_p: Optional[float] = None + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class DataSourceResponses(BaseModel): + """A ResponsesRunDataSource object describing a model sampling configuration.""" + + source: DataSourceResponsesSource + """Determines what populates the `item` namespace in this run's data source.""" + + type: Literal["responses"] + """The type of run data source. Always `responses`.""" + + input_messages: Optional[DataSourceResponsesInputMessages] = None + """Used when sampling from a model. + + Dictates the structure of the messages passed into the model. Can either be a + reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template + with variable references to the `item` namespace. + """ + + model: Optional[str] = None + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + sampling_params: Optional[DataSourceResponsesSamplingParams] = None + + +DataSource: TypeAlias = Annotated[ + Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource, DataSourceResponses], + PropertyInfo(discriminator="type"), +] + + +class PerModelUsage(BaseModel): + cached_tokens: int + """The number of tokens retrieved from cache.""" + + completion_tokens: int + """The number of completion tokens generated.""" + + invocation_count: int + """The number of invocations.""" + + run_model_name: str = FieldInfo(alias="model_name") + """The name of the model.""" + + prompt_tokens: int + """The number of prompt tokens used.""" + + total_tokens: int + """The total number of tokens used.""" + + +class PerTestingCriteriaResult(BaseModel): + failed: int + """Number of tests failed for this criteria.""" + + passed: int + """Number of tests passed for this criteria.""" + + testing_criteria: str + """A description of the testing criteria.""" + + +class ResultCounts(BaseModel): + """Counters summarizing the outcomes of the evaluation run.""" + + errored: int + """Number of output items that resulted in an error.""" + + failed: int + """Number of output items that failed to pass the evaluation.""" + + passed: int + """Number of output items that passed the evaluation.""" + + total: int + """Total number of executed output items.""" + + +class RunListResponse(BaseModel): + """A schema representing an evaluation run.""" + + id: str + """Unique identifier for the evaluation run.""" + + created_at: int + """Unix timestamp (in seconds) when the evaluation run was created.""" + + data_source: DataSource + """Information about the run's data source.""" + + error: EvalAPIError + """An object representing an error response from the Eval API.""" + + eval_id: str + """The identifier of the associated evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: str + """The model that is evaluated, if applicable.""" + + name: str + """The name of the evaluation run.""" + + object: Literal["eval.run"] + """The type of the object. Always "eval.run".""" + + per_model_usage: List[PerModelUsage] + """Usage statistics for each model during the evaluation run.""" + + per_testing_criteria_results: List[PerTestingCriteriaResult] + """Results per testing criteria applied during the evaluation run.""" + + report_url: str + """The URL to the rendered evaluation run report on the UI dashboard.""" + + result_counts: ResultCounts + """Counters summarizing the outcomes of the evaluation run.""" + + status: str + """The status of the evaluation run.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/run_retrieve_response.py b/venv/lib/python3.12/site-packages/openai/types/evals/run_retrieve_response.py new file mode 100644 index 0000000..c35695f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/run_retrieve_response.py @@ -0,0 +1,441 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .eval_api_error import EvalAPIError +from ..responses.tool import Tool +from ..shared.metadata import Metadata +from ..graders.grader_inputs import GraderInputs +from ..shared.reasoning_effort import ReasoningEffort +from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio +from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource +from ..responses.response_format_text_config import ResponseFormatTextConfig +from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource + +__all__ = [ + "RunRetrieveResponse", + "DataSource", + "DataSourceResponses", + "DataSourceResponsesSource", + "DataSourceResponsesSourceFileContent", + "DataSourceResponsesSourceFileContentContent", + "DataSourceResponsesSourceFileID", + "DataSourceResponsesSourceResponses", + "DataSourceResponsesInputMessages", + "DataSourceResponsesInputMessagesTemplate", + "DataSourceResponsesInputMessagesTemplateTemplate", + "DataSourceResponsesInputMessagesTemplateTemplateChatMessage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", + "DataSourceResponsesInputMessagesItemReference", + "DataSourceResponsesSamplingParams", + "DataSourceResponsesSamplingParamsText", + "PerModelUsage", + "PerTestingCriteriaResult", + "ResultCounts", +] + + +class DataSourceResponsesSourceFileContentContent(BaseModel): + item: Dict[str, object] + + sample: Optional[Dict[str, object]] = None + + +class DataSourceResponsesSourceFileContent(BaseModel): + content: List[DataSourceResponsesSourceFileContentContent] + """The content of the jsonl file.""" + + type: Literal["file_content"] + """The type of jsonl source. Always `file_content`.""" + + +class DataSourceResponsesSourceFileID(BaseModel): + id: str + """The identifier of the file.""" + + type: Literal["file_id"] + """The type of jsonl source. Always `file_id`.""" + + +class DataSourceResponsesSourceResponses(BaseModel): + """A EvalResponsesSource object describing a run data source configuration.""" + + type: Literal["responses"] + """The type of run data source. Always `responses`.""" + + created_after: Optional[int] = None + """Only include items created after this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + created_before: Optional[int] = None + """Only include items created before this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + instructions_search: Optional[str] = None + """Optional string to search the 'instructions' field. + + This is a query parameter used to select responses. + """ + + metadata: Optional[object] = None + """Metadata filter for the responses. + + This is a query parameter used to select responses. + """ + + model: Optional[str] = None + """The name of the model to find responses for. + + This is a query parameter used to select responses. + """ + + reasoning_effort: Optional[ReasoningEffort] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + temperature: Optional[float] = None + """Sampling temperature. This is a query parameter used to select responses.""" + + tools: Optional[List[str]] = None + """List of tool names. This is a query parameter used to select responses.""" + + top_p: Optional[float] = None + """Nucleus sampling parameter. This is a query parameter used to select responses.""" + + users: Optional[List[str]] = None + """List of user identifiers. This is a query parameter used to select responses.""" + + +DataSourceResponsesSource: TypeAlias = Annotated[ + Union[DataSourceResponsesSourceFileContent, DataSourceResponsesSourceFileID, DataSourceResponsesSourceResponses], + PropertyInfo(discriminator="type"), +] + + +class DataSourceResponsesInputMessagesTemplateTemplateChatMessage(BaseModel): + content: str + """The content of the message.""" + + role: str + """The role of the message (e.g. "system", "assistant", "user").""" + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ + str, + ResponseInputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudio, + GraderInputs, +] + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +DataSourceResponsesInputMessagesTemplateTemplate: TypeAlias = Union[ + DataSourceResponsesInputMessagesTemplateTemplateChatMessage, + DataSourceResponsesInputMessagesTemplateTemplateEvalItem, +] + + +class DataSourceResponsesInputMessagesTemplate(BaseModel): + template: List[DataSourceResponsesInputMessagesTemplateTemplate] + """A list of chat messages forming the prompt or context. + + May include variable references to the `item` namespace, ie {{item.name}}. + """ + + type: Literal["template"] + """The type of input messages. Always `template`.""" + + +class DataSourceResponsesInputMessagesItemReference(BaseModel): + item_reference: str + """A reference to a variable in the `item` namespace. Ie, "item.name" """ + + type: Literal["item_reference"] + """The type of input messages. Always `item_reference`.""" + + +DataSourceResponsesInputMessages: TypeAlias = Annotated[ + Union[DataSourceResponsesInputMessagesTemplate, DataSourceResponsesInputMessagesItemReference], + PropertyInfo(discriminator="type"), +] + + +class DataSourceResponsesSamplingParamsText(BaseModel): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + format: Optional[ResponseFormatTextConfig] = None + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + +class DataSourceResponsesSamplingParams(BaseModel): + max_completion_tokens: Optional[int] = None + """The maximum number of tokens in the generated output.""" + + reasoning_effort: Optional[ReasoningEffort] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + seed: Optional[int] = None + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] = None + """A higher temperature increases randomness in the outputs.""" + + text: Optional[DataSourceResponsesSamplingParamsText] = None + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + tools: Optional[List[Tool]] = None + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + """ + + top_p: Optional[float] = None + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class DataSourceResponses(BaseModel): + """A ResponsesRunDataSource object describing a model sampling configuration.""" + + source: DataSourceResponsesSource + """Determines what populates the `item` namespace in this run's data source.""" + + type: Literal["responses"] + """The type of run data source. Always `responses`.""" + + input_messages: Optional[DataSourceResponsesInputMessages] = None + """Used when sampling from a model. + + Dictates the structure of the messages passed into the model. Can either be a + reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template + with variable references to the `item` namespace. + """ + + model: Optional[str] = None + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + sampling_params: Optional[DataSourceResponsesSamplingParams] = None + + +DataSource: TypeAlias = Annotated[ + Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource, DataSourceResponses], + PropertyInfo(discriminator="type"), +] + + +class PerModelUsage(BaseModel): + cached_tokens: int + """The number of tokens retrieved from cache.""" + + completion_tokens: int + """The number of completion tokens generated.""" + + invocation_count: int + """The number of invocations.""" + + run_model_name: str = FieldInfo(alias="model_name") + """The name of the model.""" + + prompt_tokens: int + """The number of prompt tokens used.""" + + total_tokens: int + """The total number of tokens used.""" + + +class PerTestingCriteriaResult(BaseModel): + failed: int + """Number of tests failed for this criteria.""" + + passed: int + """Number of tests passed for this criteria.""" + + testing_criteria: str + """A description of the testing criteria.""" + + +class ResultCounts(BaseModel): + """Counters summarizing the outcomes of the evaluation run.""" + + errored: int + """Number of output items that resulted in an error.""" + + failed: int + """Number of output items that failed to pass the evaluation.""" + + passed: int + """Number of output items that passed the evaluation.""" + + total: int + """Total number of executed output items.""" + + +class RunRetrieveResponse(BaseModel): + """A schema representing an evaluation run.""" + + id: str + """Unique identifier for the evaluation run.""" + + created_at: int + """Unix timestamp (in seconds) when the evaluation run was created.""" + + data_source: DataSource + """Information about the run's data source.""" + + error: EvalAPIError + """An object representing an error response from the Eval API.""" + + eval_id: str + """The identifier of the associated evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: str + """The model that is evaluated, if applicable.""" + + name: str + """The name of the evaluation run.""" + + object: Literal["eval.run"] + """The type of the object. Always "eval.run".""" + + per_model_usage: List[PerModelUsage] + """Usage statistics for each model during the evaluation run.""" + + per_testing_criteria_results: List[PerTestingCriteriaResult] + """Results per testing criteria applied during the evaluation run.""" + + report_url: str + """The URL to the rendered evaluation run report on the UI dashboard.""" + + result_counts: ResultCounts + """Counters summarizing the outcomes of the evaluation run.""" + + status: str + """The status of the evaluation run.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/runs/__init__.py b/venv/lib/python3.12/site-packages/openai/types/evals/runs/__init__.py new file mode 100644 index 0000000..ca2b64e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/runs/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .output_item_list_params import OutputItemListParams as OutputItemListParams +from .output_item_list_response import OutputItemListResponse as OutputItemListResponse +from .output_item_retrieve_response import OutputItemRetrieveResponse as OutputItemRetrieveResponse diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/runs/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/runs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7ee4459 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/runs/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/runs/__pycache__/output_item_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/runs/__pycache__/output_item_list_params.cpython-312.pyc new file mode 100644 index 0000000..bce7fc0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/runs/__pycache__/output_item_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/runs/__pycache__/output_item_list_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/runs/__pycache__/output_item_list_response.cpython-312.pyc new file mode 100644 index 0000000..e3908af Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/runs/__pycache__/output_item_list_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/runs/__pycache__/output_item_retrieve_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/evals/runs/__pycache__/output_item_retrieve_response.cpython-312.pyc new file mode 100644 index 0000000..ffb8a8e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/evals/runs/__pycache__/output_item_retrieve_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/runs/output_item_list_params.py b/venv/lib/python3.12/site-packages/openai/types/evals/runs/output_item_list_params.py new file mode 100644 index 0000000..15c4a29 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/runs/output_item_list_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["OutputItemListParams"] + + +class OutputItemListParams(TypedDict, total=False): + eval_id: Required[str] + + after: str + """Identifier for the last output item from the previous pagination request.""" + + limit: int + """Number of output items to retrieve.""" + + order: Literal["asc", "desc"] + """Sort order for output items by timestamp. + + Use `asc` for ascending order or `desc` for descending order. Defaults to `asc`. + """ + + status: Literal["fail", "pass"] + """Filter output items by status. + + Use `failed` to filter by failed output items or `pass` to filter by passed + output items. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/runs/output_item_list_response.py b/venv/lib/python3.12/site-packages/openai/types/evals/runs/output_item_list_response.py new file mode 100644 index 0000000..5db0f34 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/runs/output_item_list_response.py @@ -0,0 +1,144 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import TYPE_CHECKING, Dict, List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel +from ..eval_api_error import EvalAPIError + +__all__ = ["OutputItemListResponse", "Result", "Sample", "SampleInput", "SampleOutput", "SampleUsage"] + + +class Result(BaseModel): + """A single grader result for an evaluation run output item.""" + + name: str + """The name of the grader.""" + + passed: bool + """Whether the grader considered the output a pass.""" + + score: float + """The numeric score produced by the grader.""" + + sample: Optional[Dict[str, object]] = None + """Optional sample or intermediate data produced by the grader.""" + + type: Optional[str] = None + """The grader type (for example, "string-check-grader").""" + + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] + + +class SampleInput(BaseModel): + """An input message.""" + + content: str + """The content of the message.""" + + role: str + """The role of the message sender (e.g., system, user, developer).""" + + +class SampleOutput(BaseModel): + content: Optional[str] = None + """The content of the message.""" + + role: Optional[str] = None + """The role of the message (e.g. "system", "assistant", "user").""" + + +class SampleUsage(BaseModel): + """Token usage details for the sample.""" + + cached_tokens: int + """The number of tokens retrieved from cache.""" + + completion_tokens: int + """The number of completion tokens generated.""" + + prompt_tokens: int + """The number of prompt tokens used.""" + + total_tokens: int + """The total number of tokens used.""" + + +class Sample(BaseModel): + """A sample containing the input and output of the evaluation run.""" + + error: EvalAPIError + """An object representing an error response from the Eval API.""" + + finish_reason: str + """The reason why the sample generation was finished.""" + + input: List[SampleInput] + """An array of input messages.""" + + max_completion_tokens: int + """The maximum number of tokens allowed for completion.""" + + model: str + """The model used for generating the sample.""" + + output: List[SampleOutput] + """An array of output messages.""" + + seed: int + """The seed used for generating the sample.""" + + temperature: float + """The sampling temperature used.""" + + top_p: float + """The top_p value used for sampling.""" + + usage: SampleUsage + """Token usage details for the sample.""" + + +class OutputItemListResponse(BaseModel): + """A schema representing an evaluation run output item.""" + + id: str + """Unique identifier for the evaluation run output item.""" + + created_at: int + """Unix timestamp (in seconds) when the evaluation run was created.""" + + datasource_item: Dict[str, object] + """Details of the input data source item.""" + + datasource_item_id: int + """The identifier for the data source item.""" + + eval_id: str + """The identifier of the evaluation group.""" + + object: Literal["eval.run.output_item"] + """The type of the object. Always "eval.run.output_item".""" + + results: List[Result] + """A list of grader results for this output item.""" + + run_id: str + """The identifier of the evaluation run associated with this output item.""" + + sample: Sample + """A sample containing the input and output of the evaluation run.""" + + status: str + """The status of the evaluation run.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/evals/runs/output_item_retrieve_response.py b/venv/lib/python3.12/site-packages/openai/types/evals/runs/output_item_retrieve_response.py new file mode 100644 index 0000000..096ff4d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/evals/runs/output_item_retrieve_response.py @@ -0,0 +1,144 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import TYPE_CHECKING, Dict, List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel +from ..eval_api_error import EvalAPIError + +__all__ = ["OutputItemRetrieveResponse", "Result", "Sample", "SampleInput", "SampleOutput", "SampleUsage"] + + +class Result(BaseModel): + """A single grader result for an evaluation run output item.""" + + name: str + """The name of the grader.""" + + passed: bool + """Whether the grader considered the output a pass.""" + + score: float + """The numeric score produced by the grader.""" + + sample: Optional[Dict[str, object]] = None + """Optional sample or intermediate data produced by the grader.""" + + type: Optional[str] = None + """The grader type (for example, "string-check-grader").""" + + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] + + +class SampleInput(BaseModel): + """An input message.""" + + content: str + """The content of the message.""" + + role: str + """The role of the message sender (e.g., system, user, developer).""" + + +class SampleOutput(BaseModel): + content: Optional[str] = None + """The content of the message.""" + + role: Optional[str] = None + """The role of the message (e.g. "system", "assistant", "user").""" + + +class SampleUsage(BaseModel): + """Token usage details for the sample.""" + + cached_tokens: int + """The number of tokens retrieved from cache.""" + + completion_tokens: int + """The number of completion tokens generated.""" + + prompt_tokens: int + """The number of prompt tokens used.""" + + total_tokens: int + """The total number of tokens used.""" + + +class Sample(BaseModel): + """A sample containing the input and output of the evaluation run.""" + + error: EvalAPIError + """An object representing an error response from the Eval API.""" + + finish_reason: str + """The reason why the sample generation was finished.""" + + input: List[SampleInput] + """An array of input messages.""" + + max_completion_tokens: int + """The maximum number of tokens allowed for completion.""" + + model: str + """The model used for generating the sample.""" + + output: List[SampleOutput] + """An array of output messages.""" + + seed: int + """The seed used for generating the sample.""" + + temperature: float + """The sampling temperature used.""" + + top_p: float + """The top_p value used for sampling.""" + + usage: SampleUsage + """Token usage details for the sample.""" + + +class OutputItemRetrieveResponse(BaseModel): + """A schema representing an evaluation run output item.""" + + id: str + """Unique identifier for the evaluation run output item.""" + + created_at: int + """Unix timestamp (in seconds) when the evaluation run was created.""" + + datasource_item: Dict[str, object] + """Details of the input data source item.""" + + datasource_item_id: int + """The identifier for the data source item.""" + + eval_id: str + """The identifier of the evaluation group.""" + + object: Literal["eval.run.output_item"] + """The type of the object. Always "eval.run.output_item".""" + + results: List[Result] + """A list of grader results for this output item.""" + + run_id: str + """The identifier of the evaluation run associated with this output item.""" + + sample: Sample + """A sample containing the input and output of the evaluation run.""" + + status: str + """The status of the evaluation run.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/file_chunking_strategy.py b/venv/lib/python3.12/site-packages/openai/types/file_chunking_strategy.py new file mode 100644 index 0000000..087cd46 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/file_chunking_strategy.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from .._utils import PropertyInfo +from .other_file_chunking_strategy_object import OtherFileChunkingStrategyObject +from .static_file_chunking_strategy_object import StaticFileChunkingStrategyObject + +__all__ = ["FileChunkingStrategy"] + +FileChunkingStrategy: TypeAlias = Annotated[ + Union[StaticFileChunkingStrategyObject, OtherFileChunkingStrategyObject], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/file_chunking_strategy_param.py b/venv/lib/python3.12/site-packages/openai/types/file_chunking_strategy_param.py new file mode 100644 index 0000000..e5f2afb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/file_chunking_strategy_param.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .auto_file_chunking_strategy_param import AutoFileChunkingStrategyParam +from .static_file_chunking_strategy_object_param import StaticFileChunkingStrategyObjectParam + +__all__ = ["FileChunkingStrategyParam"] + +FileChunkingStrategyParam: TypeAlias = Union[AutoFileChunkingStrategyParam, StaticFileChunkingStrategyObjectParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/file_content.py b/venv/lib/python3.12/site-packages/openai/types/file_content.py new file mode 100644 index 0000000..a96d56b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/file_content.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["FileContent"] + +FileContent: TypeAlias = str diff --git a/venv/lib/python3.12/site-packages/openai/types/file_create_params.py b/venv/lib/python3.12/site-packages/openai/types/file_create_params.py new file mode 100644 index 0000000..6480e36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/file_create_params.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes +from .file_purpose import FilePurpose + +__all__ = ["FileCreateParams", "ExpiresAfter"] + + +class FileCreateParams(TypedDict, total=False): + file: Required[FileTypes] + """The File object (not file name) to be uploaded.""" + + purpose: Required[FilePurpose] + """The intended purpose of the uploaded file. One of: + + - `assistants`: Used in the Assistants API + - `batch`: Used in the Batch API + - `fine-tune`: Used for fine-tuning + - `vision`: Images used for vision fine-tuning + - `user_data`: Flexible file type for any purpose + - `evals`: Used for eval data sets + """ + + expires_after: ExpiresAfter + """The expiration policy for a file. + + By default, files with `purpose=batch` expire after 30 days and all other files + are persisted until they are manually deleted. + """ + + +class ExpiresAfter(TypedDict, total=False): + """The expiration policy for a file. + + By default, files with `purpose=batch` expire after 30 days and all other files are persisted until they are manually deleted. + """ + + anchor: Required[Literal["created_at"]] + """Anchor timestamp after which the expiration policy applies. + + Supported anchors: `created_at`. + """ + + seconds: Required[int] + """The number of seconds after the anchor time that the file will expire. + + Must be between 3600 (1 hour) and 2592000 (30 days). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/file_deleted.py b/venv/lib/python3.12/site-packages/openai/types/file_deleted.py new file mode 100644 index 0000000..3234feb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/file_deleted.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["FileDeleted"] + + +class FileDeleted(BaseModel): + id: str + + deleted: bool + + object: Literal["file"] diff --git a/venv/lib/python3.12/site-packages/openai/types/file_list_params.py b/venv/lib/python3.12/site-packages/openai/types/file_list_params.py new file mode 100644 index 0000000..a2b0779 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/file_list_params.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["FileListParams"] + + +class FileListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 10,000, and the default is 10,000. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ + + purpose: str + """Only return files with the given purpose.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/file_object.py b/venv/lib/python3.12/site-packages/openai/types/file_object.py new file mode 100644 index 0000000..b846ade --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/file_object.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["FileObject"] + + +class FileObject(BaseModel): + """The `File` object represents a document that has been uploaded to OpenAI.""" + + id: str + """The file identifier, which can be referenced in the API endpoints.""" + + bytes: int + """The size of the file, in bytes.""" + + created_at: int + """The Unix timestamp (in seconds) for when the file was created.""" + + filename: str + """The name of the file.""" + + object: Literal["file"] + """The object type, which is always `file`.""" + + purpose: Literal[ + "assistants", + "assistants_output", + "batch", + "batch_output", + "fine-tune", + "fine-tune-results", + "vision", + "user_data", + ] + """The intended purpose of the file. + + Supported values are `assistants`, `assistants_output`, `batch`, `batch_output`, + `fine-tune`, `fine-tune-results`, `vision`, and `user_data`. + """ + + status: Literal["uploaded", "processed", "error"] + """Deprecated. + + The current status of the file, which can be either `uploaded`, `processed`, or + `error`. + """ + + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the file will expire.""" + + status_details: Optional[str] = None + """Deprecated. + + For details on why a fine-tuning training file failed validation, see the + `error` field on `fine_tuning.job`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/file_purpose.py b/venv/lib/python3.12/site-packages/openai/types/file_purpose.py new file mode 100644 index 0000000..425f8e5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/file_purpose.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["FilePurpose"] + +FilePurpose: TypeAlias = Literal["assistants", "batch", "fine-tune", "vision", "user_data", "evals"] diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__init__.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__init__.py new file mode 100644 index 0000000..479c772 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__init__.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .dpo_method import DpoMethod as DpoMethod +from .fine_tuning_job import FineTuningJob as FineTuningJob +from .job_list_params import JobListParams as JobListParams +from .dpo_method_param import DpoMethodParam as DpoMethodParam +from .job_create_params import JobCreateParams as JobCreateParams +from .supervised_method import SupervisedMethod as SupervisedMethod +from .dpo_hyperparameters import DpoHyperparameters as DpoHyperparameters +from .reinforcement_method import ReinforcementMethod as ReinforcementMethod +from .fine_tuning_job_event import FineTuningJobEvent as FineTuningJobEvent +from .job_list_events_params import JobListEventsParams as JobListEventsParams +from .supervised_method_param import SupervisedMethodParam as SupervisedMethodParam +from .dpo_hyperparameters_param import DpoHyperparametersParam as DpoHyperparametersParam +from .reinforcement_method_param import ReinforcementMethodParam as ReinforcementMethodParam +from .supervised_hyperparameters import SupervisedHyperparameters as SupervisedHyperparameters +from .fine_tuning_job_integration import FineTuningJobIntegration as FineTuningJobIntegration +from .reinforcement_hyperparameters import ReinforcementHyperparameters as ReinforcementHyperparameters +from .supervised_hyperparameters_param import SupervisedHyperparametersParam as SupervisedHyperparametersParam +from .fine_tuning_job_wandb_integration import FineTuningJobWandbIntegration as FineTuningJobWandbIntegration +from .reinforcement_hyperparameters_param import ReinforcementHyperparametersParam as ReinforcementHyperparametersParam +from .fine_tuning_job_wandb_integration_object import ( + FineTuningJobWandbIntegrationObject as FineTuningJobWandbIntegrationObject, +) diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..eab20ce Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/dpo_hyperparameters.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/dpo_hyperparameters.cpython-312.pyc new file mode 100644 index 0000000..8f4ad42 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/dpo_hyperparameters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/dpo_hyperparameters_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/dpo_hyperparameters_param.cpython-312.pyc new file mode 100644 index 0000000..dcafbe7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/dpo_hyperparameters_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/dpo_method.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/dpo_method.cpython-312.pyc new file mode 100644 index 0000000..d526183 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/dpo_method.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/dpo_method_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/dpo_method_param.cpython-312.pyc new file mode 100644 index 0000000..e7aa27c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/dpo_method_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job.cpython-312.pyc new file mode 100644 index 0000000..05b6e18 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_event.cpython-312.pyc new file mode 100644 index 0000000..a5871fb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_integration.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_integration.cpython-312.pyc new file mode 100644 index 0000000..d186596 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_integration.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration.cpython-312.pyc new file mode 100644 index 0000000..1818a7c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration_object.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration_object.cpython-312.pyc new file mode 100644 index 0000000..aaa1f2e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration_object.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/job_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/job_create_params.cpython-312.pyc new file mode 100644 index 0000000..dc1c262 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/job_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/job_list_events_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/job_list_events_params.cpython-312.pyc new file mode 100644 index 0000000..5d1eaae Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/job_list_events_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/job_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/job_list_params.cpython-312.pyc new file mode 100644 index 0000000..d01188f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/job_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/reinforcement_hyperparameters.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/reinforcement_hyperparameters.cpython-312.pyc new file mode 100644 index 0000000..5ac3a7a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/reinforcement_hyperparameters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/reinforcement_hyperparameters_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/reinforcement_hyperparameters_param.cpython-312.pyc new file mode 100644 index 0000000..71486b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/reinforcement_hyperparameters_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/reinforcement_method.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/reinforcement_method.cpython-312.pyc new file mode 100644 index 0000000..ffd275a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/reinforcement_method.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/reinforcement_method_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/reinforcement_method_param.cpython-312.pyc new file mode 100644 index 0000000..f1696d4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/reinforcement_method_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/supervised_hyperparameters.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/supervised_hyperparameters.cpython-312.pyc new file mode 100644 index 0000000..015b795 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/supervised_hyperparameters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/supervised_hyperparameters_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/supervised_hyperparameters_param.cpython-312.pyc new file mode 100644 index 0000000..7516c9d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/supervised_hyperparameters_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/supervised_method.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/supervised_method.cpython-312.pyc new file mode 100644 index 0000000..2624223 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/supervised_method.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/supervised_method_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/supervised_method_param.cpython-312.pyc new file mode 100644 index 0000000..cc5f4d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/__pycache__/supervised_method_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__init__.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__init__.py new file mode 100644 index 0000000..d9754eb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .grader_run_params import GraderRunParams as GraderRunParams +from .grader_run_response import GraderRunResponse as GraderRunResponse +from .grader_validate_params import GraderValidateParams as GraderValidateParams +from .grader_validate_response import GraderValidateResponse as GraderValidateResponse diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..03c228b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/grader_run_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/grader_run_params.cpython-312.pyc new file mode 100644 index 0000000..62d72df Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/grader_run_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/grader_run_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/grader_run_response.cpython-312.pyc new file mode 100644 index 0000000..be0b827 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/grader_run_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/grader_validate_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/grader_validate_params.cpython-312.pyc new file mode 100644 index 0000000..571c600 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/grader_validate_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/grader_validate_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/grader_validate_response.cpython-312.pyc new file mode 100644 index 0000000..a86953d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/__pycache__/grader_validate_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/grader_run_params.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/grader_run_params.py new file mode 100644 index 0000000..1919962 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/grader_run_params.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict + +from ...graders.multi_grader_param import MultiGraderParam +from ...graders.python_grader_param import PythonGraderParam +from ...graders.score_model_grader_param import ScoreModelGraderParam +from ...graders.string_check_grader_param import StringCheckGraderParam +from ...graders.text_similarity_grader_param import TextSimilarityGraderParam + +__all__ = ["GraderRunParams", "Grader"] + + +class GraderRunParams(TypedDict, total=False): + grader: Required[Grader] + """The grader used for the fine-tuning job.""" + + model_sample: Required[str] + """The model sample to be evaluated. + + This value will be used to populate the `sample` namespace. See + [the guide](https://developers.openai.com/api/docs/guides/graders) for more + details. The `output_json` variable will be populated if the model sample is a + valid JSON string. + """ + + item: object + """The dataset item provided to the grader. + + This will be used to populate the `item` namespace. See + [the guide](https://developers.openai.com/api/docs/guides/graders) for more + details. + """ + + +Grader: TypeAlias = Union[ + StringCheckGraderParam, TextSimilarityGraderParam, PythonGraderParam, ScoreModelGraderParam, MultiGraderParam +] diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/grader_run_response.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/grader_run_response.py new file mode 100644 index 0000000..9023af5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/grader_run_response.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Optional + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel + +__all__ = ["GraderRunResponse", "Metadata", "MetadataErrors"] + + +class MetadataErrors(BaseModel): + formula_parse_error: bool + + invalid_variable_error: bool + + api_model_grader_parse_error: bool = FieldInfo(alias="model_grader_parse_error") + + api_model_grader_refusal_error: bool = FieldInfo(alias="model_grader_refusal_error") + + api_model_grader_server_error: bool = FieldInfo(alias="model_grader_server_error") + + api_model_grader_server_error_details: Optional[str] = FieldInfo( + alias="model_grader_server_error_details", default=None + ) + + other_error: bool + + python_grader_runtime_error: bool + + python_grader_runtime_error_details: Optional[str] = None + + python_grader_server_error: bool + + python_grader_server_error_type: Optional[str] = None + + sample_parse_error: bool + + truncated_observation_error: bool + + unresponsive_reward_error: bool + + +class Metadata(BaseModel): + errors: MetadataErrors + + execution_time: float + + name: str + + sampled_model_name: Optional[str] = None + + scores: Dict[str, object] + + token_usage: Optional[int] = None + + type: str + + +class GraderRunResponse(BaseModel): + metadata: Metadata + + api_model_grader_token_usage_per_model: Dict[str, object] = FieldInfo(alias="model_grader_token_usage_per_model") + + reward: float + + sub_rewards: Dict[str, object] diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/grader_validate_params.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/grader_validate_params.py new file mode 100644 index 0000000..b4498d1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/grader_validate_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict + +from ...graders.multi_grader_param import MultiGraderParam +from ...graders.python_grader_param import PythonGraderParam +from ...graders.score_model_grader_param import ScoreModelGraderParam +from ...graders.string_check_grader_param import StringCheckGraderParam +from ...graders.text_similarity_grader_param import TextSimilarityGraderParam + +__all__ = ["GraderValidateParams", "Grader"] + + +class GraderValidateParams(TypedDict, total=False): + grader: Required[Grader] + """The grader used for the fine-tuning job.""" + + +Grader: TypeAlias = Union[ + StringCheckGraderParam, TextSimilarityGraderParam, PythonGraderParam, ScoreModelGraderParam, MultiGraderParam +] diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/grader_validate_response.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/grader_validate_response.py new file mode 100644 index 0000000..ae52470 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/alpha/grader_validate_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import TypeAlias + +from ...._models import BaseModel +from ...graders.multi_grader import MultiGrader +from ...graders.python_grader import PythonGrader +from ...graders.score_model_grader import ScoreModelGrader +from ...graders.string_check_grader import StringCheckGrader +from ...graders.text_similarity_grader import TextSimilarityGrader + +__all__ = ["GraderValidateResponse", "Grader"] + +Grader: TypeAlias = Union[StringCheckGrader, TextSimilarityGrader, PythonGrader, ScoreModelGrader, MultiGrader] + + +class GraderValidateResponse(BaseModel): + grader: Optional[Grader] = None + """The grader used for the fine-tuning job.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__init__.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__init__.py new file mode 100644 index 0000000..115574c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__init__.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .permission_list_params import PermissionListParams as PermissionListParams +from .permission_create_params import PermissionCreateParams as PermissionCreateParams +from .permission_list_response import PermissionListResponse as PermissionListResponse +from .permission_create_response import PermissionCreateResponse as PermissionCreateResponse +from .permission_delete_response import PermissionDeleteResponse as PermissionDeleteResponse +from .permission_retrieve_params import PermissionRetrieveParams as PermissionRetrieveParams +from .permission_retrieve_response import PermissionRetrieveResponse as PermissionRetrieveResponse diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a0d1ea8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_create_params.cpython-312.pyc new file mode 100644 index 0000000..a0a82bf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_create_response.cpython-312.pyc new file mode 100644 index 0000000..1e8dddb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_delete_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_delete_response.cpython-312.pyc new file mode 100644 index 0000000..e9db345 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_delete_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_list_params.cpython-312.pyc new file mode 100644 index 0000000..7328f51 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_list_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_list_response.cpython-312.pyc new file mode 100644 index 0000000..2bcd6ce Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_list_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_retrieve_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_retrieve_params.cpython-312.pyc new file mode 100644 index 0000000..838c358 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_retrieve_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_retrieve_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_retrieve_response.cpython-312.pyc new file mode 100644 index 0000000..27a0167 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/__pycache__/permission_retrieve_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_create_params.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_create_params.py new file mode 100644 index 0000000..a9a8d3a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_create_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["PermissionCreateParams"] + + +class PermissionCreateParams(TypedDict, total=False): + project_ids: Required[SequenceNotStr[str]] + """The project identifiers to grant access to.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_create_response.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_create_response.py new file mode 100644 index 0000000..9a4a28d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_create_response.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["PermissionCreateResponse"] + + +class PermissionCreateResponse(BaseModel): + """ + The `checkpoint.permission` object represents a permission for a fine-tuned model checkpoint. + """ + + id: str + """The permission identifier, which can be referenced in the API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the permission was created.""" + + object: Literal["checkpoint.permission"] + """The object type, which is always "checkpoint.permission".""" + + project_id: str + """The project identifier that the permission is for.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_delete_response.py new file mode 100644 index 0000000..dc961a9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_delete_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["PermissionDeleteResponse"] + + +class PermissionDeleteResponse(BaseModel): + id: str + """The ID of the fine-tuned model checkpoint permission that was deleted.""" + + deleted: bool + """Whether the fine-tuned model checkpoint permission was successfully deleted.""" + + object: Literal["checkpoint.permission"] + """The object type, which is always "checkpoint.permission".""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_list_params.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_list_params.py new file mode 100644 index 0000000..5b99121 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_list_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["PermissionListParams"] + + +class PermissionListParams(TypedDict, total=False): + after: str + """Identifier for the last permission ID from the previous pagination request.""" + + limit: int + """Number of permissions to retrieve.""" + + order: Literal["ascending", "descending"] + """The order in which to retrieve permissions.""" + + project_id: str + """The ID of the project to get permissions for.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_list_response.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_list_response.py new file mode 100644 index 0000000..4a8a240 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_list_response.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["PermissionListResponse"] + + +class PermissionListResponse(BaseModel): + """ + The `checkpoint.permission` object represents a permission for a fine-tuned model checkpoint. + """ + + id: str + """The permission identifier, which can be referenced in the API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the permission was created.""" + + object: Literal["checkpoint.permission"] + """The object type, which is always "checkpoint.permission".""" + + project_id: str + """The project identifier that the permission is for.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_retrieve_params.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_retrieve_params.py new file mode 100644 index 0000000..d852770 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_retrieve_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["PermissionRetrieveParams"] + + +class PermissionRetrieveParams(TypedDict, total=False): + after: str + """Identifier for the last permission ID from the previous pagination request.""" + + limit: int + """Number of permissions to retrieve.""" + + order: Literal["ascending", "descending"] + """The order in which to retrieve permissions.""" + + project_id: str + """The ID of the project to get permissions for.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py new file mode 100644 index 0000000..a36aa14 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["PermissionRetrieveResponse", "Data"] + + +class Data(BaseModel): + """ + The `checkpoint.permission` object represents a permission for a fine-tuned model checkpoint. + """ + + id: str + """The permission identifier, which can be referenced in the API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the permission was created.""" + + object: Literal["checkpoint.permission"] + """The object type, which is always "checkpoint.permission".""" + + project_id: str + """The project identifier that the permission is for.""" + + +class PermissionRetrieveResponse(BaseModel): + data: List[Data] + + has_more: bool + + object: Literal["list"] + + first_id: Optional[str] = None + + last_id: Optional[str] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/dpo_hyperparameters.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/dpo_hyperparameters.py new file mode 100644 index 0000000..08ef624 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/dpo_hyperparameters.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["DpoHyperparameters"] + + +class DpoHyperparameters(BaseModel): + """The hyperparameters used for the DPO fine-tuning job.""" + + batch_size: Union[Literal["auto"], int, None] = None + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + beta: Union[Literal["auto"], float, None] = None + """The beta value for the DPO method. + + A higher beta value will increase the weight of the penalty between the policy + and reference model. + """ + + learning_rate_multiplier: Union[Literal["auto"], float, None] = None + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int, None] = None + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/dpo_hyperparameters_param.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/dpo_hyperparameters_param.py new file mode 100644 index 0000000..7e1530b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/dpo_hyperparameters_param.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypedDict + +__all__ = ["DpoHyperparametersParam"] + + +class DpoHyperparametersParam(TypedDict, total=False): + """The hyperparameters used for the DPO fine-tuning job.""" + + batch_size: Union[Literal["auto"], int] + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + beta: Union[Literal["auto"], float] + """The beta value for the DPO method. + + A higher beta value will increase the weight of the penalty between the policy + and reference model. + """ + + learning_rate_multiplier: Union[Literal["auto"], float] + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int] + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/dpo_method.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/dpo_method.py new file mode 100644 index 0000000..f2b2c01 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/dpo_method.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .dpo_hyperparameters import DpoHyperparameters + +__all__ = ["DpoMethod"] + + +class DpoMethod(BaseModel): + """Configuration for the DPO fine-tuning method.""" + + hyperparameters: Optional[DpoHyperparameters] = None + """The hyperparameters used for the DPO fine-tuning job.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/dpo_method_param.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/dpo_method_param.py new file mode 100644 index 0000000..039b894 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/dpo_method_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .dpo_hyperparameters_param import DpoHyperparametersParam + +__all__ = ["DpoMethodParam"] + + +class DpoMethodParam(TypedDict, total=False): + """Configuration for the DPO fine-tuning method.""" + + hyperparameters: DpoHyperparametersParam + """The hyperparameters used for the DPO fine-tuning job.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job.py new file mode 100644 index 0000000..daf5890 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job.py @@ -0,0 +1,176 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .dpo_method import DpoMethod +from ..shared.metadata import Metadata +from .supervised_method import SupervisedMethod +from .reinforcement_method import ReinforcementMethod +from .fine_tuning_job_wandb_integration_object import FineTuningJobWandbIntegrationObject + +__all__ = ["FineTuningJob", "Error", "Hyperparameters", "Method"] + + +class Error(BaseModel): + """ + For fine-tuning jobs that have `failed`, this will contain more information on the cause of the failure. + """ + + code: str + """A machine-readable error code.""" + + message: str + """A human-readable error message.""" + + param: Optional[str] = None + """The parameter that was invalid, usually `training_file` or `validation_file`. + + This field will be null if the failure was not parameter-specific. + """ + + +class Hyperparameters(BaseModel): + """The hyperparameters used for the fine-tuning job. + + This value will only be returned when running `supervised` jobs. + """ + + batch_size: Union[Literal["auto"], int, None] = None + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + learning_rate_multiplier: Union[Literal["auto"], float, None] = None + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int, None] = None + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ + + +class Method(BaseModel): + """The method used for fine-tuning.""" + + type: Literal["supervised", "dpo", "reinforcement"] + """The type of method. Is either `supervised`, `dpo`, or `reinforcement`.""" + + dpo: Optional[DpoMethod] = None + """Configuration for the DPO fine-tuning method.""" + + reinforcement: Optional[ReinforcementMethod] = None + """Configuration for the reinforcement fine-tuning method.""" + + supervised: Optional[SupervisedMethod] = None + """Configuration for the supervised fine-tuning method.""" + + +class FineTuningJob(BaseModel): + """ + The `fine_tuning.job` object represents a fine-tuning job that has been created through the API. + """ + + id: str + """The object identifier, which can be referenced in the API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the fine-tuning job was created.""" + + error: Optional[Error] = None + """ + For fine-tuning jobs that have `failed`, this will contain more information on + the cause of the failure. + """ + + fine_tuned_model: Optional[str] = None + """The name of the fine-tuned model that is being created. + + The value will be null if the fine-tuning job is still running. + """ + + finished_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the fine-tuning job was finished. + + The value will be null if the fine-tuning job is still running. + """ + + hyperparameters: Hyperparameters + """The hyperparameters used for the fine-tuning job. + + This value will only be returned when running `supervised` jobs. + """ + + model: str + """The base model that is being fine-tuned.""" + + object: Literal["fine_tuning.job"] + """The object type, which is always "fine_tuning.job".""" + + organization_id: str + """The organization that owns the fine-tuning job.""" + + result_files: List[str] + """The compiled results file ID(s) for the fine-tuning job. + + You can retrieve the results with the + [Files API](https://developers.openai.com/api/reference/resources/files/methods/content). + """ + + seed: int + """The seed used for the fine-tuning job.""" + + status: Literal["validating_files", "queued", "running", "succeeded", "failed", "cancelled"] + """ + The current status of the fine-tuning job, which can be either + `validating_files`, `queued`, `running`, `succeeded`, `failed`, or `cancelled`. + """ + + trained_tokens: Optional[int] = None + """The total number of billable tokens processed by this fine-tuning job. + + The value will be null if the fine-tuning job is still running. + """ + + training_file: str + """The file ID used for training. + + You can retrieve the training data with the + [Files API](https://developers.openai.com/api/reference/resources/files/methods/content). + """ + + validation_file: Optional[str] = None + """The file ID used for validation. + + You can retrieve the validation results with the + [Files API](https://developers.openai.com/api/reference/resources/files/methods/content). + """ + + estimated_finish: Optional[int] = None + """ + The Unix timestamp (in seconds) for when the fine-tuning job is estimated to + finish. The value will be null if the fine-tuning job is not running. + """ + + integrations: Optional[List[FineTuningJobWandbIntegrationObject]] = None + """A list of integrations to enable for this fine-tuning job.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + method: Optional[Method] = None + """The method used for fine-tuning.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job_event.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job_event.py new file mode 100644 index 0000000..aa64262 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +import builtins +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FineTuningJobEvent"] + + +class FineTuningJobEvent(BaseModel): + """Fine-tuning job event object""" + + id: str + """The object identifier.""" + + created_at: int + """The Unix timestamp (in seconds) for when the fine-tuning job was created.""" + + level: Literal["info", "warn", "error"] + """The log level of the event.""" + + message: str + """The message of the event.""" + + object: Literal["fine_tuning.job.event"] + """The object type, which is always "fine_tuning.job.event".""" + + data: Optional[builtins.object] = None + """The data associated with the event.""" + + type: Optional[Literal["message", "metrics"]] = None + """The type of event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job_integration.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job_integration.py new file mode 100644 index 0000000..8fefe8a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job_integration.py @@ -0,0 +1,5 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from .fine_tuning_job_wandb_integration_object import FineTuningJobWandbIntegrationObject + +FineTuningJobIntegration = FineTuningJobWandbIntegrationObject diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py new file mode 100644 index 0000000..512249e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel + +__all__ = ["FineTuningJobWandbIntegration"] + + +class FineTuningJobWandbIntegration(BaseModel): + """The settings for your integration with Weights and Biases. + + This payload specifies the project that + metrics will be sent to. Optionally, you can set an explicit display name for your run, add tags + to your run, and set a default entity (team, username, etc) to be associated with your run. + """ + + project: str + """The name of the project that the new run will be created under.""" + + entity: Optional[str] = None + """The entity to use for the run. + + This allows you to set the team or username of the WandB user that you would + like associated with the run. If not set, the default entity for the registered + WandB API key is used. + """ + + name: Optional[str] = None + """A display name to set for the run. + + If not set, we will use the Job ID as the name. + """ + + tags: Optional[List[str]] = None + """A list of tags to be attached to the newly created run. + + These tags are passed through directly to WandB. Some default tags are generated + by OpenAI: "openai/finetune", "openai/{base-model}", "openai/{ftjob-abcdef}". + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job_wandb_integration_object.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job_wandb_integration_object.py new file mode 100644 index 0000000..db49d35 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/fine_tuning_job_wandb_integration_object.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .fine_tuning_job_wandb_integration import FineTuningJobWandbIntegration + +__all__ = ["FineTuningJobWandbIntegrationObject"] + + +class FineTuningJobWandbIntegrationObject(BaseModel): + type: Literal["wandb"] + """The type of the integration being enabled for the fine-tuning job""" + + wandb: FineTuningJobWandbIntegration + """The settings for your integration with Weights and Biases. + + This payload specifies the project that metrics will be sent to. Optionally, you + can set an explicit display name for your run, add tags to your run, and set a + default entity (team, username, etc) to be associated with your run. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/job_create_params.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/job_create_params.py new file mode 100644 index 0000000..d8e0856 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/job_create_params.py @@ -0,0 +1,191 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr +from .dpo_method_param import DpoMethodParam +from ..shared_params.metadata import Metadata +from .supervised_method_param import SupervisedMethodParam +from .reinforcement_method_param import ReinforcementMethodParam + +__all__ = ["JobCreateParams", "Hyperparameters", "Integration", "IntegrationWandb", "Method"] + + +class JobCreateParams(TypedDict, total=False): + model: Required[Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo", "gpt-4o-mini"]]] + """The name of the model to fine-tune. + + You can select one of the + [supported models](https://developers.openai.com/api/docs/guides/model-optimization#fine-tuning-methods). + """ + + training_file: Required[str] + """The ID of an uploaded file that contains training data. + + See + [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) + for how to upload a file. + + Your dataset must be formatted as a JSONL file. Additionally, you must upload + your file with the purpose `fine-tune`. + + The contents of the file should differ depending on if the model uses the + [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data), + [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) + format, or if the fine-tuning method uses the + [preference](https://developers.openai.com/api/docs/guides/direct-preference-optimization) + format. + + See the + [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) + for more details. + """ + + hyperparameters: Hyperparameters + """ + The hyperparameters used for the fine-tuning job. This value is now deprecated + in favor of `method`, and should be passed in under the `method` parameter. + """ + + integrations: Optional[Iterable[Integration]] + """A list of integrations to enable for your fine-tuning job.""" + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + method: Method + """The method used for fine-tuning.""" + + seed: Optional[int] + """The seed controls the reproducibility of the job. + + Passing in the same seed and job parameters should produce the same results, but + may differ in rare cases. If a seed is not specified, one will be generated for + you. + """ + + suffix: Optional[str] + """ + A string of up to 64 characters that will be added to your fine-tuned model + name. + + For example, a `suffix` of "custom-model-name" would produce a model name like + `ft:gpt-4o-mini:openai:custom-model-name:7p4lURel`. + """ + + validation_file: Optional[str] + """The ID of an uploaded file that contains validation data. + + If you provide this file, the data is used to generate validation metrics + periodically during fine-tuning. These metrics can be viewed in the fine-tuning + results file. The same data should not be present in both train and validation + files. + + Your dataset must be formatted as a JSONL file. You must upload your file with + the purpose `fine-tune`. + + See the + [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) + for more details. + """ + + +class Hyperparameters(TypedDict, total=False): + """ + The hyperparameters used for the fine-tuning job. + This value is now deprecated in favor of `method`, and should be passed in under the `method` parameter. + """ + + batch_size: Union[Literal["auto"], int] + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + learning_rate_multiplier: Union[Literal["auto"], float] + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int] + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ + + +class IntegrationWandb(TypedDict, total=False): + """The settings for your integration with Weights and Biases. + + This payload specifies the project that + metrics will be sent to. Optionally, you can set an explicit display name for your run, add tags + to your run, and set a default entity (team, username, etc) to be associated with your run. + """ + + project: Required[str] + """The name of the project that the new run will be created under.""" + + entity: Optional[str] + """The entity to use for the run. + + This allows you to set the team or username of the WandB user that you would + like associated with the run. If not set, the default entity for the registered + WandB API key is used. + """ + + name: Optional[str] + """A display name to set for the run. + + If not set, we will use the Job ID as the name. + """ + + tags: SequenceNotStr[str] + """A list of tags to be attached to the newly created run. + + These tags are passed through directly to WandB. Some default tags are generated + by OpenAI: "openai/finetune", "openai/{base-model}", "openai/{ftjob-abcdef}". + """ + + +class Integration(TypedDict, total=False): + type: Required[Literal["wandb"]] + """The type of integration to enable. + + Currently, only "wandb" (Weights and Biases) is supported. + """ + + wandb: Required[IntegrationWandb] + """The settings for your integration with Weights and Biases. + + This payload specifies the project that metrics will be sent to. Optionally, you + can set an explicit display name for your run, add tags to your run, and set a + default entity (team, username, etc) to be associated with your run. + """ + + +class Method(TypedDict, total=False): + """The method used for fine-tuning.""" + + type: Required[Literal["supervised", "dpo", "reinforcement"]] + """The type of method. Is either `supervised`, `dpo`, or `reinforcement`.""" + + dpo: DpoMethodParam + """Configuration for the DPO fine-tuning method.""" + + reinforcement: ReinforcementMethodParam + """Configuration for the reinforcement fine-tuning method.""" + + supervised: SupervisedMethodParam + """Configuration for the supervised fine-tuning method.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/job_list_events_params.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/job_list_events_params.py new file mode 100644 index 0000000..bd862c8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/job_list_events_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["JobListEventsParams"] + + +class JobListEventsParams(TypedDict, total=False): + after: str + """Identifier for the last event from the previous pagination request.""" + + limit: int + """Number of events to retrieve.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/job_list_params.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/job_list_params.py new file mode 100644 index 0000000..c31ec92 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/job_list_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import TypedDict + +__all__ = ["JobListParams"] + + +class JobListParams(TypedDict, total=False): + after: str + """Identifier for the last job from the previous pagination request.""" + + limit: int + """Number of fine-tuning jobs to retrieve.""" + + metadata: Optional[Dict[str, str]] + """Optional metadata filter. + + To filter, use the syntax `metadata[k]=v`. Alternatively, set `metadata=null` to + indicate no metadata. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/__init__.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/__init__.py new file mode 100644 index 0000000..91b5f99 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .checkpoint_list_params import CheckpointListParams as CheckpointListParams +from .fine_tuning_job_checkpoint import FineTuningJobCheckpoint as FineTuningJobCheckpoint diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c6a5fc8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/__pycache__/checkpoint_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/__pycache__/checkpoint_list_params.cpython-312.pyc new file mode 100644 index 0000000..e96738b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/__pycache__/checkpoint_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/__pycache__/fine_tuning_job_checkpoint.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/__pycache__/fine_tuning_job_checkpoint.cpython-312.pyc new file mode 100644 index 0000000..bbb3275 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/__pycache__/fine_tuning_job_checkpoint.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/checkpoint_list_params.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/checkpoint_list_params.py new file mode 100644 index 0000000..11c320b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/checkpoint_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["CheckpointListParams"] + + +class CheckpointListParams(TypedDict, total=False): + after: str + """Identifier for the last checkpoint ID from the previous pagination request.""" + + limit: int + """Number of checkpoints to retrieve.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py new file mode 100644 index 0000000..ea8960b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["FineTuningJobCheckpoint", "Metrics"] + + +class Metrics(BaseModel): + """Metrics at the step number during the fine-tuning job.""" + + full_valid_loss: Optional[float] = None + + full_valid_mean_token_accuracy: Optional[float] = None + + step: Optional[float] = None + + train_loss: Optional[float] = None + + train_mean_token_accuracy: Optional[float] = None + + valid_loss: Optional[float] = None + + valid_mean_token_accuracy: Optional[float] = None + + +class FineTuningJobCheckpoint(BaseModel): + """ + The `fine_tuning.job.checkpoint` object represents a model checkpoint for a fine-tuning job that is ready to use. + """ + + id: str + """The checkpoint identifier, which can be referenced in the API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the checkpoint was created.""" + + fine_tuned_model_checkpoint: str + """The name of the fine-tuned checkpoint model that is created.""" + + fine_tuning_job_id: str + """The name of the fine-tuning job that this checkpoint was created from.""" + + metrics: Metrics + """Metrics at the step number during the fine-tuning job.""" + + object: Literal["fine_tuning.job.checkpoint"] + """The object type, which is always "fine_tuning.job.checkpoint".""" + + step_number: int + """The step number that the checkpoint was created at.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/reinforcement_hyperparameters.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/reinforcement_hyperparameters.py new file mode 100644 index 0000000..def73ac --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/reinforcement_hyperparameters.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ReinforcementHyperparameters"] + + +class ReinforcementHyperparameters(BaseModel): + """The hyperparameters used for the reinforcement fine-tuning job.""" + + batch_size: Union[Literal["auto"], int, None] = None + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + compute_multiplier: Union[Literal["auto"], float, None] = None + """ + Multiplier on amount of compute used for exploring search space during training. + """ + + eval_interval: Union[Literal["auto"], int, None] = None + """The number of training steps between evaluation runs.""" + + eval_samples: Union[Literal["auto"], int, None] = None + """Number of evaluation samples to generate per training step.""" + + learning_rate_multiplier: Union[Literal["auto"], float, None] = None + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int, None] = None + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ + + reasoning_effort: Optional[Literal["default", "low", "medium", "high"]] = None + """Level of reasoning effort.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/reinforcement_hyperparameters_param.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/reinforcement_hyperparameters_param.py new file mode 100644 index 0000000..84de6bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/reinforcement_hyperparameters_param.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypedDict + +__all__ = ["ReinforcementHyperparametersParam"] + + +class ReinforcementHyperparametersParam(TypedDict, total=False): + """The hyperparameters used for the reinforcement fine-tuning job.""" + + batch_size: Union[Literal["auto"], int] + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + compute_multiplier: Union[Literal["auto"], float] + """ + Multiplier on amount of compute used for exploring search space during training. + """ + + eval_interval: Union[Literal["auto"], int] + """The number of training steps between evaluation runs.""" + + eval_samples: Union[Literal["auto"], int] + """Number of evaluation samples to generate per training step.""" + + learning_rate_multiplier: Union[Literal["auto"], float] + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int] + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ + + reasoning_effort: Literal["default", "low", "medium", "high"] + """Level of reasoning effort.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/reinforcement_method.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/reinforcement_method.py new file mode 100644 index 0000000..9fbde81 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/reinforcement_method.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import TypeAlias + +from ..._models import BaseModel +from ..graders.multi_grader import MultiGrader +from ..graders.python_grader import PythonGrader +from ..graders.score_model_grader import ScoreModelGrader +from ..graders.string_check_grader import StringCheckGrader +from .reinforcement_hyperparameters import ReinforcementHyperparameters +from ..graders.text_similarity_grader import TextSimilarityGrader + +__all__ = ["ReinforcementMethod", "Grader"] + +Grader: TypeAlias = Union[StringCheckGrader, TextSimilarityGrader, PythonGrader, ScoreModelGrader, MultiGrader] + + +class ReinforcementMethod(BaseModel): + """Configuration for the reinforcement fine-tuning method.""" + + grader: Grader + """The grader used for the fine-tuning job.""" + + hyperparameters: Optional[ReinforcementHyperparameters] = None + """The hyperparameters used for the reinforcement fine-tuning job.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/reinforcement_method_param.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/reinforcement_method_param.py new file mode 100644 index 0000000..424210c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/reinforcement_method_param.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict + +from ..graders.multi_grader_param import MultiGraderParam +from ..graders.python_grader_param import PythonGraderParam +from ..graders.score_model_grader_param import ScoreModelGraderParam +from ..graders.string_check_grader_param import StringCheckGraderParam +from .reinforcement_hyperparameters_param import ReinforcementHyperparametersParam +from ..graders.text_similarity_grader_param import TextSimilarityGraderParam + +__all__ = ["ReinforcementMethodParam", "Grader"] + +Grader: TypeAlias = Union[ + StringCheckGraderParam, TextSimilarityGraderParam, PythonGraderParam, ScoreModelGraderParam, MultiGraderParam +] + + +class ReinforcementMethodParam(TypedDict, total=False): + """Configuration for the reinforcement fine-tuning method.""" + + grader: Required[Grader] + """The grader used for the fine-tuning job.""" + + hyperparameters: ReinforcementHyperparametersParam + """The hyperparameters used for the reinforcement fine-tuning job.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/supervised_hyperparameters.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/supervised_hyperparameters.py new file mode 100644 index 0000000..fe0716c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/supervised_hyperparameters.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SupervisedHyperparameters"] + + +class SupervisedHyperparameters(BaseModel): + """The hyperparameters used for the fine-tuning job.""" + + batch_size: Union[Literal["auto"], int, None] = None + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + learning_rate_multiplier: Union[Literal["auto"], float, None] = None + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int, None] = None + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/supervised_hyperparameters_param.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/supervised_hyperparameters_param.py new file mode 100644 index 0000000..2a1afa6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/supervised_hyperparameters_param.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypedDict + +__all__ = ["SupervisedHyperparametersParam"] + + +class SupervisedHyperparametersParam(TypedDict, total=False): + """The hyperparameters used for the fine-tuning job.""" + + batch_size: Union[Literal["auto"], int] + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + learning_rate_multiplier: Union[Literal["auto"], float] + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int] + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/supervised_method.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/supervised_method.py new file mode 100644 index 0000000..c7468f8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/supervised_method.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .supervised_hyperparameters import SupervisedHyperparameters + +__all__ = ["SupervisedMethod"] + + +class SupervisedMethod(BaseModel): + """Configuration for the supervised fine-tuning method.""" + + hyperparameters: Optional[SupervisedHyperparameters] = None + """The hyperparameters used for the fine-tuning job.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/fine_tuning/supervised_method_param.py b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/supervised_method_param.py new file mode 100644 index 0000000..4ccfb7a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/fine_tuning/supervised_method_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .supervised_hyperparameters_param import SupervisedHyperparametersParam + +__all__ = ["SupervisedMethodParam"] + + +class SupervisedMethodParam(TypedDict, total=False): + """Configuration for the supervised fine-tuning method.""" + + hyperparameters: SupervisedHyperparametersParam + """The hyperparameters used for the fine-tuning job.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__init__.py b/venv/lib/python3.12/site-packages/openai/types/graders/__init__.py new file mode 100644 index 0000000..f292148 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/__init__.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .multi_grader import MultiGrader as MultiGrader +from .grader_inputs import GraderInputs as GraderInputs +from .python_grader import PythonGrader as PythonGrader +from .label_model_grader import LabelModelGrader as LabelModelGrader +from .multi_grader_param import MultiGraderParam as MultiGraderParam +from .score_model_grader import ScoreModelGrader as ScoreModelGrader +from .grader_inputs_param import GraderInputsParam as GraderInputsParam +from .python_grader_param import PythonGraderParam as PythonGraderParam +from .string_check_grader import StringCheckGrader as StringCheckGrader +from .text_similarity_grader import TextSimilarityGrader as TextSimilarityGrader +from .label_model_grader_param import LabelModelGraderParam as LabelModelGraderParam +from .score_model_grader_param import ScoreModelGraderParam as ScoreModelGraderParam +from .string_check_grader_param import StringCheckGraderParam as StringCheckGraderParam +from .text_similarity_grader_param import TextSimilarityGraderParam as TextSimilarityGraderParam diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d245bff Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/grader_inputs.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/grader_inputs.cpython-312.pyc new file mode 100644 index 0000000..94dc804 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/grader_inputs.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/grader_inputs_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/grader_inputs_param.cpython-312.pyc new file mode 100644 index 0000000..6c987a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/grader_inputs_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/label_model_grader.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/label_model_grader.cpython-312.pyc new file mode 100644 index 0000000..253f562 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/label_model_grader.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/label_model_grader_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/label_model_grader_param.cpython-312.pyc new file mode 100644 index 0000000..9291790 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/label_model_grader_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/multi_grader.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/multi_grader.cpython-312.pyc new file mode 100644 index 0000000..eb35fed Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/multi_grader.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/multi_grader_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/multi_grader_param.cpython-312.pyc new file mode 100644 index 0000000..224d069 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/multi_grader_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/python_grader.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/python_grader.cpython-312.pyc new file mode 100644 index 0000000..17275fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/python_grader.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/python_grader_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/python_grader_param.cpython-312.pyc new file mode 100644 index 0000000..1d7b40c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/python_grader_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/score_model_grader.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/score_model_grader.cpython-312.pyc new file mode 100644 index 0000000..f051e05 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/score_model_grader.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/score_model_grader_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/score_model_grader_param.cpython-312.pyc new file mode 100644 index 0000000..77a304b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/score_model_grader_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/string_check_grader.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/string_check_grader.cpython-312.pyc new file mode 100644 index 0000000..b83f510 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/string_check_grader.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/string_check_grader_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/string_check_grader_param.cpython-312.pyc new file mode 100644 index 0000000..e1983bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/string_check_grader_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/text_similarity_grader.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/text_similarity_grader.cpython-312.pyc new file mode 100644 index 0000000..978d128 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/text_similarity_grader.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/text_similarity_grader_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/text_similarity_grader_param.cpython-312.pyc new file mode 100644 index 0000000..ccc329d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/graders/__pycache__/text_similarity_grader_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/grader_inputs.py b/venv/lib/python3.12/site-packages/openai/types/graders/grader_inputs.py new file mode 100644 index 0000000..75537e7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/grader_inputs.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio + +__all__ = ["GraderInputs", "GraderInputItem", "GraderInputItemOutputText", "GraderInputItemInputImage"] + + +class GraderInputItemOutputText(BaseModel): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class GraderInputItemInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +GraderInputItem: TypeAlias = Union[ + str, ResponseInputText, GraderInputItemOutputText, GraderInputItemInputImage, ResponseInputAudio +] + +GraderInputs: TypeAlias = List[GraderInputItem] diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/grader_inputs_param.py b/venv/lib/python3.12/site-packages/openai/types/graders/grader_inputs_param.py new file mode 100644 index 0000000..9cbb246 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/grader_inputs_param.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..responses.response_input_text_param import ResponseInputTextParam +from ..responses.response_input_audio_param import ResponseInputAudioParam + +__all__ = [ + "GraderInputsParam", + "GraderInputsParamItem", + "GraderInputsParamItemOutputText", + "GraderInputsParamItemInputImage", +] + + +class GraderInputsParamItemOutputText(TypedDict, total=False): + """A text output from the model.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +class GraderInputsParamItemInputImage(TypedDict, total=False): + """An image input block used within EvalItem content arrays.""" + + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +GraderInputsParamItem: TypeAlias = Union[ + str, + ResponseInputTextParam, + GraderInputsParamItemOutputText, + GraderInputsParamItemInputImage, + ResponseInputAudioParam, +] + +GraderInputsParam: TypeAlias = List[GraderInputsParamItem] diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/label_model_grader.py b/venv/lib/python3.12/site-packages/openai/types/graders/label_model_grader.py new file mode 100644 index 0000000..a323720 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/label_model_grader.py @@ -0,0 +1,92 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .grader_inputs import GraderInputs +from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio + +__all__ = ["LabelModelGrader", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] + + +class InputContentOutputText(BaseModel): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class InputContentInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputContent: TypeAlias = Union[ + str, ResponseInputText, InputContentOutputText, InputContentInputImage, ResponseInputAudio, GraderInputs +] + + +class Input(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: InputContent + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +class LabelModelGrader(BaseModel): + """ + A LabelModelGrader object which uses a model to assign labels to each item + in the evaluation. + """ + + input: List[Input] + + labels: List[str] + """The labels to assign to each item in the evaluation.""" + + model: str + """The model to use for the evaluation. Must support structured outputs.""" + + name: str + """The name of the grader.""" + + passing_labels: List[str] + """The labels that indicate a passing result. Must be a subset of labels.""" + + type: Literal["label_model"] + """The object type, which is always `label_model`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/label_model_grader_param.py b/venv/lib/python3.12/site-packages/openai/types/graders/label_model_grader_param.py new file mode 100644 index 0000000..72d1f62 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/label_model_grader_param.py @@ -0,0 +1,99 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .grader_inputs_param import GraderInputsParam +from ..responses.response_input_text_param import ResponseInputTextParam +from ..responses.response_input_audio_param import ResponseInputAudioParam + +__all__ = ["LabelModelGraderParam", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] + + +class InputContentOutputText(TypedDict, total=False): + """A text output from the model.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +class InputContentInputImage(TypedDict, total=False): + """An image input block used within EvalItem content arrays.""" + + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputContent: TypeAlias = Union[ + str, + ResponseInputTextParam, + InputContentOutputText, + InputContentInputImage, + ResponseInputAudioParam, + GraderInputsParam, +] + + +class Input(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: Required[InputContent] + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" + + +class LabelModelGraderParam(TypedDict, total=False): + """ + A LabelModelGrader object which uses a model to assign labels to each item + in the evaluation. + """ + + input: Required[Iterable[Input]] + + labels: Required[SequenceNotStr[str]] + """The labels to assign to each item in the evaluation.""" + + model: Required[str] + """The model to use for the evaluation. Must support structured outputs.""" + + name: Required[str] + """The name of the grader.""" + + passing_labels: Required[SequenceNotStr[str]] + """The labels that indicate a passing result. Must be a subset of labels.""" + + type: Required[Literal["label_model"]] + """The object type, which is always `label_model`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/multi_grader.py b/venv/lib/python3.12/site-packages/openai/types/graders/multi_grader.py new file mode 100644 index 0000000..f843859 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/multi_grader.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .python_grader import PythonGrader +from .label_model_grader import LabelModelGrader +from .score_model_grader import ScoreModelGrader +from .string_check_grader import StringCheckGrader +from .text_similarity_grader import TextSimilarityGrader + +__all__ = ["MultiGrader", "Graders"] + +Graders: TypeAlias = Union[StringCheckGrader, TextSimilarityGrader, PythonGrader, ScoreModelGrader, LabelModelGrader] + + +class MultiGrader(BaseModel): + """ + A MultiGrader object combines the output of multiple graders to produce a single score. + """ + + calculate_output: str + """A formula to calculate the output based on grader results.""" + + graders: Graders + """ + A StringCheckGrader object that performs a string comparison between input and + reference using a specified operation. + """ + + name: str + """The name of the grader.""" + + type: Literal["multi"] + """The object type, which is always `multi`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/multi_grader_param.py b/venv/lib/python3.12/site-packages/openai/types/graders/multi_grader_param.py new file mode 100644 index 0000000..ef406f4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/multi_grader_param.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .python_grader_param import PythonGraderParam +from .label_model_grader_param import LabelModelGraderParam +from .score_model_grader_param import ScoreModelGraderParam +from .string_check_grader_param import StringCheckGraderParam +from .text_similarity_grader_param import TextSimilarityGraderParam + +__all__ = ["MultiGraderParam", "Graders"] + +Graders: TypeAlias = Union[ + StringCheckGraderParam, TextSimilarityGraderParam, PythonGraderParam, ScoreModelGraderParam, LabelModelGraderParam +] + + +class MultiGraderParam(TypedDict, total=False): + """ + A MultiGrader object combines the output of multiple graders to produce a single score. + """ + + calculate_output: Required[str] + """A formula to calculate the output based on grader results.""" + + graders: Required[Graders] + """ + A StringCheckGrader object that performs a string comparison between input and + reference using a specified operation. + """ + + name: Required[str] + """The name of the grader.""" + + type: Required[Literal["multi"]] + """The object type, which is always `multi`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/python_grader.py b/venv/lib/python3.12/site-packages/openai/types/graders/python_grader.py new file mode 100644 index 0000000..96ce207 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/python_grader.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["PythonGrader"] + + +class PythonGrader(BaseModel): + """A PythonGrader object that runs a python script on the input.""" + + name: str + """The name of the grader.""" + + source: str + """The source code of the python script.""" + + type: Literal["python"] + """The object type, which is always `python`.""" + + image_tag: Optional[str] = None + """The image tag to use for the python script.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/python_grader_param.py b/venv/lib/python3.12/site-packages/openai/types/graders/python_grader_param.py new file mode 100644 index 0000000..a4ba6e8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/python_grader_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["PythonGraderParam"] + + +class PythonGraderParam(TypedDict, total=False): + """A PythonGrader object that runs a python script on the input.""" + + name: Required[str] + """The name of the grader.""" + + source: Required[str] + """The source code of the python script.""" + + type: Required[Literal["python"]] + """The object type, which is always `python`.""" + + image_tag: str + """The image tag to use for the python script.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/score_model_grader.py b/venv/lib/python3.12/site-packages/openai/types/graders/score_model_grader.py new file mode 100644 index 0000000..419427d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/score_model_grader.py @@ -0,0 +1,129 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .grader_inputs import GraderInputs +from ..shared.reasoning_effort import ReasoningEffort +from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio + +__all__ = [ + "ScoreModelGrader", + "Input", + "InputContent", + "InputContentOutputText", + "InputContentInputImage", + "SamplingParams", +] + + +class InputContentOutputText(BaseModel): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class InputContentInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputContent: TypeAlias = Union[ + str, ResponseInputText, InputContentOutputText, InputContentInputImage, ResponseInputAudio, GraderInputs +] + + +class Input(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: InputContent + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +class SamplingParams(BaseModel): + """The sampling parameters for the model.""" + + max_completions_tokens: Optional[int] = None + """The maximum number of tokens the grader model may generate in its response.""" + + reasoning_effort: Optional[ReasoningEffort] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + seed: Optional[int] = None + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] = None + """A higher temperature increases randomness in the outputs.""" + + top_p: Optional[float] = None + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class ScoreModelGrader(BaseModel): + """A ScoreModelGrader object that uses a model to assign a score to the input.""" + + input: List[Input] + """The input messages evaluated by the grader. + + Supports text, output text, input image, and input audio content blocks, and may + include template strings. + """ + + model: str + """The model to use for the evaluation.""" + + name: str + """The name of the grader.""" + + type: Literal["score_model"] + """The object type, which is always `score_model`.""" + + range: Optional[List[float]] = None + """The range of the score. Defaults to `[0, 1]`.""" + + sampling_params: Optional[SamplingParams] = None + """The sampling parameters for the model.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/score_model_grader_param.py b/venv/lib/python3.12/site-packages/openai/types/graders/score_model_grader_param.py new file mode 100644 index 0000000..78b797f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/score_model_grader_param.py @@ -0,0 +1,135 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .grader_inputs_param import GraderInputsParam +from ..shared.reasoning_effort import ReasoningEffort +from ..responses.response_input_text_param import ResponseInputTextParam +from ..responses.response_input_audio_param import ResponseInputAudioParam + +__all__ = [ + "ScoreModelGraderParam", + "Input", + "InputContent", + "InputContentOutputText", + "InputContentInputImage", + "SamplingParams", +] + + +class InputContentOutputText(TypedDict, total=False): + """A text output from the model.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +class InputContentInputImage(TypedDict, total=False): + """An image input block used within EvalItem content arrays.""" + + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputContent: TypeAlias = Union[ + str, + ResponseInputTextParam, + InputContentOutputText, + InputContentInputImage, + ResponseInputAudioParam, + GraderInputsParam, +] + + +class Input(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: Required[InputContent] + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" + + +class SamplingParams(TypedDict, total=False): + """The sampling parameters for the model.""" + + max_completions_tokens: Optional[int] + """The maximum number of tokens the grader model may generate in its response.""" + + reasoning_effort: Optional[ReasoningEffort] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + seed: Optional[int] + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] + """A higher temperature increases randomness in the outputs.""" + + top_p: Optional[float] + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class ScoreModelGraderParam(TypedDict, total=False): + """A ScoreModelGrader object that uses a model to assign a score to the input.""" + + input: Required[Iterable[Input]] + """The input messages evaluated by the grader. + + Supports text, output text, input image, and input audio content blocks, and may + include template strings. + """ + + model: Required[str] + """The model to use for the evaluation.""" + + name: Required[str] + """The name of the grader.""" + + type: Required[Literal["score_model"]] + """The object type, which is always `score_model`.""" + + range: Iterable[float] + """The range of the score. Defaults to `[0, 1]`.""" + + sampling_params: SamplingParams + """The sampling parameters for the model.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/string_check_grader.py b/venv/lib/python3.12/site-packages/openai/types/graders/string_check_grader.py new file mode 100644 index 0000000..8df02a7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/string_check_grader.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["StringCheckGrader"] + + +class StringCheckGrader(BaseModel): + """ + A StringCheckGrader object that performs a string comparison between input and reference using a specified operation. + """ + + input: str + """The input text. This may include template strings.""" + + name: str + """The name of the grader.""" + + operation: Literal["eq", "ne", "like", "ilike"] + """The string check operation to perform. One of `eq`, `ne`, `like`, or `ilike`.""" + + reference: str + """The reference text. This may include template strings.""" + + type: Literal["string_check"] + """The object type, which is always `string_check`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/string_check_grader_param.py b/venv/lib/python3.12/site-packages/openai/types/graders/string_check_grader_param.py new file mode 100644 index 0000000..fe15ee8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/string_check_grader_param.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["StringCheckGraderParam"] + + +class StringCheckGraderParam(TypedDict, total=False): + """ + A StringCheckGrader object that performs a string comparison between input and reference using a specified operation. + """ + + input: Required[str] + """The input text. This may include template strings.""" + + name: Required[str] + """The name of the grader.""" + + operation: Required[Literal["eq", "ne", "like", "ilike"]] + """The string check operation to perform. One of `eq`, `ne`, `like`, or `ilike`.""" + + reference: Required[str] + """The reference text. This may include template strings.""" + + type: Required[Literal["string_check"]] + """The object type, which is always `string_check`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/text_similarity_grader.py b/venv/lib/python3.12/site-packages/openai/types/graders/text_similarity_grader.py new file mode 100644 index 0000000..4330f1b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/text_similarity_grader.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["TextSimilarityGrader"] + + +class TextSimilarityGrader(BaseModel): + """A TextSimilarityGrader object which grades text based on similarity metrics.""" + + evaluation_metric: Literal[ + "cosine", + "fuzzy_match", + "bleu", + "gleu", + "meteor", + "rouge_1", + "rouge_2", + "rouge_3", + "rouge_4", + "rouge_5", + "rouge_l", + ] + """The evaluation metric to use. + + One of `cosine`, `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, + `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. + """ + + input: str + """The text being graded.""" + + name: str + """The name of the grader.""" + + reference: str + """The text being graded against.""" + + type: Literal["text_similarity"] + """The type of grader.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/graders/text_similarity_grader_param.py b/venv/lib/python3.12/site-packages/openai/types/graders/text_similarity_grader_param.py new file mode 100644 index 0000000..f601131 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/graders/text_similarity_grader_param.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["TextSimilarityGraderParam"] + + +class TextSimilarityGraderParam(TypedDict, total=False): + """A TextSimilarityGrader object which grades text based on similarity metrics.""" + + evaluation_metric: Required[ + Literal[ + "cosine", + "fuzzy_match", + "bleu", + "gleu", + "meteor", + "rouge_1", + "rouge_2", + "rouge_3", + "rouge_4", + "rouge_5", + "rouge_l", + ] + ] + """The evaluation metric to use. + + One of `cosine`, `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, + `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. + """ + + input: Required[str] + """The text being graded.""" + + name: Required[str] + """The name of the grader.""" + + reference: Required[str] + """The text being graded against.""" + + type: Required[Literal["text_similarity"]] + """The type of grader.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/image.py b/venv/lib/python3.12/site-packages/openai/types/image.py new file mode 100644 index 0000000..882300d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/image.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["Image"] + + +class Image(BaseModel): + """Represents the content or the URL of an image generated by the OpenAI API.""" + + b64_json: Optional[str] = None + """The base64-encoded JSON of the generated image. + + Returned by default for the GPT image models, and only present if + `response_format` is set to `b64_json` for `dall-e-2` and `dall-e-3`. + """ + + revised_prompt: Optional[str] = None + """For `dall-e-3` only, the revised prompt that was used to generate the image.""" + + url: Optional[str] = None + """ + When using `dall-e-2` or `dall-e-3`, the URL of the generated image if + `response_format` is set to `url` (default value). Unsupported for the GPT image + models. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/image_create_variation_params.py b/venv/lib/python3.12/site-packages/openai/types/image_create_variation_params.py new file mode 100644 index 0000000..a33840d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/image_create_variation_params.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes +from .image_model import ImageModel + +__all__ = ["ImageCreateVariationParams"] + + +class ImageCreateVariationParams(TypedDict, total=False): + image: Required[FileTypes] + """The image to use as the basis for the variation(s). + + Must be a valid PNG file, less than 4MB, and square. + """ + + model: Union[str, ImageModel, None] + """The model to use for image generation. + + Only `dall-e-2` is supported at this time. + """ + + n: Optional[int] + """The number of images to generate. Must be between 1 and 10.""" + + response_format: Optional[Literal["url", "b64_json"]] + """The format in which the generated images are returned. + + Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the + image has been generated. + """ + + size: Optional[Literal["256x256", "512x512", "1024x1024"]] + """The size of the generated images. + + Must be one of `256x256`, `512x512`, or `1024x1024`. + """ + + user: str + """ + A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/image_edit_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/image_edit_completed_event.py new file mode 100644 index 0000000..ceee34c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/image_edit_completed_event.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ImageEditCompletedEvent", "Usage", "UsageInputTokensDetails"] + + +class UsageInputTokensDetails(BaseModel): + """The input tokens detailed information for the image generation.""" + + image_tokens: int + """The number of image tokens in the input prompt.""" + + text_tokens: int + """The number of text tokens in the input prompt.""" + + +class Usage(BaseModel): + """ + For the GPT image models only, the token usage information for the image generation. + """ + + input_tokens: int + """The number of tokens (images and text) in the input prompt.""" + + input_tokens_details: UsageInputTokensDetails + """The input tokens detailed information for the image generation.""" + + output_tokens: int + """The number of image tokens in the output image.""" + + total_tokens: int + """The total number of tokens (images and text) used for the image generation.""" + + +class ImageEditCompletedEvent(BaseModel): + """Emitted when image editing has completed and the final image is available.""" + + b64_json: str + """Base64-encoded final edited image data, suitable for rendering as an image.""" + + background: Literal["transparent", "opaque", "auto"] + """The background setting for the edited image.""" + + created_at: int + """The Unix timestamp when the event was created.""" + + output_format: Literal["png", "webp", "jpeg"] + """The output format for the edited image.""" + + quality: Literal["low", "medium", "high", "xhigh", "max", "auto"] + """The quality setting for the edited image.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]] + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + type: Literal["image_edit.completed"] + """The type of the event. Always `image_edit.completed`.""" + + usage: Usage + """ + For the GPT image models only, the token usage information for the image + generation. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/image_edit_params.py b/venv/lib/python3.12/site-packages/openai/types/image_edit_params.py new file mode 100644 index 0000000..0cf8368 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/image_edit_params.py @@ -0,0 +1,165 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes, SequenceNotStr +from .image_model import ImageModel + +__all__ = ["ImageEditParamsBase", "ImageEditParamsNonStreaming", "ImageEditParamsStreaming"] + + +class ImageEditParamsBase(TypedDict, total=False): + image: Required[Union[FileTypes, SequenceNotStr[FileTypes]]] + """The image(s) to edit. Must be a supported image file or an array of images. + + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should + be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 + images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. + """ + + prompt: Required[str] + """A text description of the desired image(s). + + The maximum length is 1000 characters for `dall-e-2`, and 32000 characters for + the GPT image models. + """ + + background: Optional[Literal["transparent", "opaque", "auto"]] + """Allows to set transparency for the background of the generated image(s). + + Must be one of `transparent`, `opaque`, or `auto` (default value). When `auto` + is used, the model will automatically determine the best background for the + image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + """ + + input_fidelity: Optional[Literal["high", "low"]] + """Controls fidelity to the original input image(s). + + This parameter is supported for GPT image models that support input fidelity. + `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter. + """ + + mask: FileTypes + """An additional image whose fully transparent areas (e.g. + + where alpha is zero) indicate where `image` should be edited. If there are + multiple images provided, the mask will be applied on the first image. Must be a + valid PNG file, less than 4MB, and have the same dimensions as `image`. + """ + + model: Union[str, ImageModel, None] + """The model to use for image generation. + + One of `dall-e-2` or a GPT image model (`gpt-image-1`, `gpt-image-1-mini`, + `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, `gpt-image-2.5-flare-2026-09-08`, or + `chatgpt-image-latest`). Defaults to `gpt-image-1.5`. + """ + + n: Optional[int] + """The number of images to generate. Must be between 1 and 10.""" + + output_compression: Optional[int] + """The compression level (0-100%) for the generated images. + + This parameter is only supported for the GPT image models with the `webp` or + `jpeg` output formats, and defaults to 100. + """ + + output_format: Optional[Literal["png", "jpeg", "webp"]] + """The format in which the generated images are returned. + + This parameter is only supported for the GPT image models. Must be one of `png`, + `jpeg`, or `webp`. The default value is `png`. + """ + + partial_images: Optional[int] + """The number of partial images to generate. + + This parameter is used for streaming responses that return partial images. Value + must be between 0 and 3. When set to 0, the response will be a single image sent + in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + """ + + quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] + """The quality of the image that will be generated for GPT image models. + + The GPT image models support `low`, `medium`, and `high`. + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, also support `xhigh` and `max`. Defaults to `auto`. + """ + + response_format: Optional[Literal["url", "b64_json"]] + """The format in which the generated images are returned. + + Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the + image has been generated. This parameter is only supported for `dall-e-2` + (default is `url` for `dall-e-2`), as GPT image models always return + base64-encoded images. + """ + + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + """The size of the generated images. + + For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and + `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as + `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be + divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. + Resolutions above `2560x1440` are experimental, and the maximum supported + resolution is `3840x2160`. The requested size must also satisfy the model's + current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and + `1024x1536` are supported by the GPT image models; `auto` is supported for + models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, + `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, + or `1024x1792`. + """ + + user: str + """ + A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + +class ImageEditParamsNonStreaming(ImageEditParamsBase, total=False): + stream: Optional[Literal[False]] + """Edit the image in streaming mode. + + Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. + """ + + +class ImageEditParamsStreaming(ImageEditParamsBase): + stream: Required[Literal[True]] + """Edit the image in streaming mode. + + Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. + """ + + +ImageEditParams = Union[ImageEditParamsNonStreaming, ImageEditParamsStreaming] diff --git a/venv/lib/python3.12/site-packages/openai/types/image_edit_partial_image_event.py b/venv/lib/python3.12/site-packages/openai/types/image_edit_partial_image_event.py new file mode 100644 index 0000000..acdd5c8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/image_edit_partial_image_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ImageEditPartialImageEvent"] + + +class ImageEditPartialImageEvent(BaseModel): + """Emitted when a partial image is available during image editing streaming.""" + + b64_json: str + """Base64-encoded partial image data, suitable for rendering as an image.""" + + background: Literal["transparent", "opaque", "auto"] + """The background setting for the requested edited image.""" + + created_at: int + """The Unix timestamp when the event was created.""" + + output_format: Literal["png", "webp", "jpeg"] + """The output format for the requested edited image.""" + + partial_image_index: int + """0-based index for the partial image (streaming).""" + + quality: Literal["low", "medium", "high", "xhigh", "max", "auto"] + """The quality setting for the requested edited image.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]] + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + type: Literal["image_edit.partial_image"] + """The type of the event. Always `image_edit.partial_image`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/image_edit_stream_event.py b/venv/lib/python3.12/site-packages/openai/types/image_edit_stream_event.py new file mode 100644 index 0000000..85f6c51 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/image_edit_stream_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from .._utils import PropertyInfo +from .image_edit_completed_event import ImageEditCompletedEvent +from .image_edit_partial_image_event import ImageEditPartialImageEvent + +__all__ = ["ImageEditStreamEvent"] + +ImageEditStreamEvent: TypeAlias = Annotated[ + Union[ImageEditPartialImageEvent, ImageEditCompletedEvent], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/image_gen_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/image_gen_completed_event.py new file mode 100644 index 0000000..23a10ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/image_gen_completed_event.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ImageGenCompletedEvent", "Usage", "UsageInputTokensDetails"] + + +class UsageInputTokensDetails(BaseModel): + """The input tokens detailed information for the image generation.""" + + image_tokens: int + """The number of image tokens in the input prompt.""" + + text_tokens: int + """The number of text tokens in the input prompt.""" + + +class Usage(BaseModel): + """ + For the GPT image models only, the token usage information for the image generation. + """ + + input_tokens: int + """The number of tokens (images and text) in the input prompt.""" + + input_tokens_details: UsageInputTokensDetails + """The input tokens detailed information for the image generation.""" + + output_tokens: int + """The number of image tokens in the output image.""" + + total_tokens: int + """The total number of tokens (images and text) used for the image generation.""" + + +class ImageGenCompletedEvent(BaseModel): + """Emitted when image generation has completed and the final image is available.""" + + b64_json: str + """Base64-encoded image data, suitable for rendering as an image.""" + + background: Literal["transparent", "opaque", "auto"] + """The background setting for the generated image.""" + + created_at: int + """The Unix timestamp when the event was created.""" + + output_format: Literal["png", "webp", "jpeg"] + """The output format for the generated image.""" + + quality: Literal["low", "medium", "high", "xhigh", "max", "auto"] + """The quality setting for the generated image.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]] + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + type: Literal["image_generation.completed"] + """The type of the event. Always `image_generation.completed`.""" + + usage: Usage + """ + For the GPT image models only, the token usage information for the image + generation. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/image_gen_partial_image_event.py b/venv/lib/python3.12/site-packages/openai/types/image_gen_partial_image_event.py new file mode 100644 index 0000000..4ddeb4a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/image_gen_partial_image_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ImageGenPartialImageEvent"] + + +class ImageGenPartialImageEvent(BaseModel): + """Emitted when a partial image is available during image generation streaming.""" + + b64_json: str + """Base64-encoded partial image data, suitable for rendering as an image.""" + + background: Literal["transparent", "opaque", "auto"] + """The background setting for the requested image.""" + + created_at: int + """The Unix timestamp when the event was created.""" + + output_format: Literal["png", "webp", "jpeg"] + """The output format for the requested image.""" + + partial_image_index: int + """0-based index for the partial image (streaming).""" + + quality: Literal["low", "medium", "high", "xhigh", "max", "auto"] + """The quality setting for the requested image.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]] + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + type: Literal["image_generation.partial_image"] + """The type of the event. Always `image_generation.partial_image`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/image_gen_stream_event.py b/venv/lib/python3.12/site-packages/openai/types/image_gen_stream_event.py new file mode 100644 index 0000000..cd98997 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/image_gen_stream_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from .._utils import PropertyInfo +from .image_gen_completed_event import ImageGenCompletedEvent +from .image_gen_partial_image_event import ImageGenPartialImageEvent + +__all__ = ["ImageGenStreamEvent"] + +ImageGenStreamEvent: TypeAlias = Annotated[ + Union[ImageGenPartialImageEvent, ImageGenCompletedEvent], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/image_generate_params.py b/venv/lib/python3.12/site-packages/openai/types/image_generate_params.py new file mode 100644 index 0000000..339276d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/image_generate_params.py @@ -0,0 +1,162 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypedDict + +from .image_model import ImageModel + +__all__ = ["ImageGenerateParamsBase", "ImageGenerateParamsNonStreaming", "ImageGenerateParamsStreaming"] + + +class ImageGenerateParamsBase(TypedDict, total=False): + prompt: Required[str] + """A text description of the desired image(s). + + The maximum length is 32000 characters for the GPT image models, 1000 characters + for `dall-e-2` and 4000 characters for `dall-e-3`. + """ + + background: Optional[Literal["transparent", "opaque", "auto"]] + """Allows to set transparency for the background of the generated image(s). + + Must be one of `transparent`, `opaque`, or `auto` (default value). When `auto` + is used, the model will automatically determine the best background for the + image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + """ + + model: Union[str, ImageModel, None] + """The model to use for image generation. + + One of `dall-e-2`, `dall-e-3`, or a GPT image model (`gpt-image-1`, + `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, + `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, + `gpt-image-2.5-flare`, `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` + unless a parameter specific to the GPT image models is used. + """ + + moderation: Optional[Literal["low", "auto"]] + """ + Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). + """ + + n: Optional[int] + """The number of images to generate. + + Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported. + """ + + output_compression: Optional[int] + """The compression level (0-100%) for the generated images. + + This parameter is only supported for the GPT image models with the `webp` or + `jpeg` output formats, and defaults to 100. + """ + + output_format: Optional[Literal["png", "jpeg", "webp"]] + """The format in which the generated images are returned. + + This parameter is only supported for the GPT image models. Must be one of `png`, + `jpeg`, or `webp`. + """ + + partial_images: Optional[int] + """The number of partial images to generate. + + This parameter is used for streaming responses that return partial images. Value + must be between 0 and 3. When set to 0, the response will be a single image sent + in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + """ + + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] + """The quality of the image that will be generated. + + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for the GPT image models. + - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their + `2026-09-08` snapshots, also support `xhigh` and `max`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. + """ + + response_format: Optional[Literal["url", "b64_json"]] + """The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. + + Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the + image has been generated. This parameter isn't supported for the GPT image + models, which always return base64-encoded images. + """ + + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + """The size of the generated images. + + For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and + `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as + `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be + divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. + Resolutions above `2560x1440` are experimental, and the maximum supported + resolution is `3840x2160`. The requested size must also satisfy the model's + current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and + `1024x1536` are supported by the GPT image models; `auto` is supported for + models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, + `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, + or `1024x1792`. + """ + + style: Optional[Literal["vivid", "natural"]] + """The style of the generated images. + + This parameter is only supported for `dall-e-3`. Must be one of `vivid` or + `natural`. Vivid causes the model to lean towards generating hyper-real and + dramatic images. Natural causes the model to produce more natural, less + hyper-real looking images. + """ + + user: str + """ + A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + +class ImageGenerateParamsNonStreaming(ImageGenerateParamsBase, total=False): + stream: Optional[Literal[False]] + """Generate the image in streaming mode. + + Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. This parameter is only supported for the GPT image models. + """ + + +class ImageGenerateParamsStreaming(ImageGenerateParamsBase): + stream: Required[Literal[True]] + """Generate the image in streaming mode. + + Defaults to `false`. See the + [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) + for more information. This parameter is only supported for the GPT image models. + """ + + +ImageGenerateParams = Union[ImageGenerateParamsNonStreaming, ImageGenerateParamsStreaming] diff --git a/venv/lib/python3.12/site-packages/openai/types/image_input_reference_param.py b/venv/lib/python3.12/site-packages/openai/types/image_input_reference_param.py new file mode 100644 index 0000000..b5e19a2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/image_input_reference_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ImageInputReferenceParam"] + + +class ImageInputReferenceParam(TypedDict, total=False): + file_id: str + + image_url: str + """A fully qualified URL or base64-encoded data URL.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/image_model.py b/venv/lib/python3.12/site-packages/openai/types/image_model.py new file mode 100644 index 0000000..21a2f2b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/image_model.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ImageModel"] + +ImageModel: TypeAlias = Literal[ + "gpt-image-1", + "gpt-image-1-mini", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-2.5-sunburst", + "gpt-image-2.5-sunburst-2026-09-08", + "gpt-image-2.5-flare", + "gpt-image-2.5-flare-2026-09-08", + "gpt-image-1.5", + "chatgpt-image-latest", + "dall-e-2", + "dall-e-3", +] diff --git a/venv/lib/python3.12/site-packages/openai/types/images_response.py b/venv/lib/python3.12/site-packages/openai/types/images_response.py new file mode 100644 index 0000000..ea8d4be --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/images_response.py @@ -0,0 +1,79 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from .image import Image +from .._models import BaseModel + +__all__ = ["ImagesResponse", "Usage", "UsageInputTokensDetails", "UsageOutputTokensDetails"] + + +class UsageInputTokensDetails(BaseModel): + """The input tokens detailed information for the image generation.""" + + image_tokens: int + """The number of image tokens in the input prompt.""" + + text_tokens: int + """The number of text tokens in the input prompt.""" + + +class UsageOutputTokensDetails(BaseModel): + """The output token details for the image generation.""" + + image_tokens: int + """The number of image output tokens generated by the model.""" + + text_tokens: int + """The number of text output tokens generated by the model.""" + + +class Usage(BaseModel): + """For `gpt-image-1` only, the token usage information for the image generation.""" + + input_tokens: int + """The number of tokens (images and text) in the input prompt.""" + + input_tokens_details: UsageInputTokensDetails + """The input tokens detailed information for the image generation.""" + + output_tokens: int + """The number of output tokens generated by the model.""" + + total_tokens: int + """The total number of tokens (images and text) used for the image generation.""" + + output_tokens_details: Optional[UsageOutputTokensDetails] = None + """The output token details for the image generation.""" + + +class ImagesResponse(BaseModel): + """The response from the image generation endpoint.""" + + created: int + """The Unix timestamp (in seconds) of when the image was created.""" + + background: Optional[Literal["transparent", "opaque"]] = None + """The background parameter used for the image generation. + + Either `transparent` or `opaque`. + """ + + data: Optional[List[Image]] = None + """The list of generated images.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] = None + """The output format of the image generation. Either `png`, `webp`, or `jpeg`.""" + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max"]] = None + """The quality of the image generated. + + One of `low`, `medium`, `high`, `xhigh`, or `max`. + """ + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + usage: Optional[Usage] = None + """For `gpt-image-1` only, the token usage information for the image generation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__init__.py b/venv/lib/python3.12/site-packages/openai/types/live/__init__.py new file mode 100644 index 0000000..6d19983 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/__init__.py @@ -0,0 +1,95 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .error import Error as Error +from .info_event import InfoEvent as InfoEvent +from .error_event import ErrorEvent as ErrorEvent +from .audio_format import AudioFormat as AudioFormat +from .client_event import ClientEvent as ClientEvent +from .custom_voice import CustomVoice as CustomVoice +from .initial_item import InitialItem as InitialItem +from .server_event import ServerEvent as ServerEvent +from .client_config import ClientConfig as ClientConfig +from .function_tool import FunctionTool as FunctionTool +from .session_usage import SessionUsage as SessionUsage +from .built_in_voice import BuiltInVoice as BuiltInVoice +from .response_event import ResponseEvent as ResponseEvent +from .session_config import SessionConfig as SessionConfig +from .session_resource import SessionResource as SessionResource +from .client_delegation import ClientDelegation as ClientDelegation +from .fork_client_event import ForkClientEvent as ForkClientEvent +from .fork_server_event import ForkServerEvent as ForkServerEvent +from .audio_format_param import AudioFormatParam as AudioFormatParam +from .client_event_param import ClientEventParam as ClientEventParam +from .custom_voice_param import CustomVoiceParam as CustomVoiceParam +from .initial_item_param import InitialItemParam as InitialItemParam +from .live_create_params import LiveCreateParams as LiveCreateParams +from .client_config_param import ClientConfigParam as ClientConfigParam +from .data_channel_config import DataChannelConfig as DataChannelConfig +from .fork_session_config import ForkSessionConfig as ForkSessionConfig +from .function_tool_param import FunctionToolParam as FunctionToolParam +from .session_close_event import SessionCloseEvent as SessionCloseEvent +from .session_fork_params import SessionForkParams as SessionForkParams +from .session_start_event import SessionStartEvent as SessionStartEvent +from .connect_client_event import ConnectClientEvent as ConnectClientEvent +from .connect_server_event import ConnectServerEvent as ConnectServerEvent +from .live_create_response import LiveCreateResponse as LiveCreateResponse +from .session_closed_event import SessionClosedEvent as SessionClosedEvent +from .session_config_param import SessionConfigParam as SessionConfigParam +from .session_refer_params import SessionReferParams as SessionReferParams +from .session_update_event import SessionUpdateEvent as SessionUpdateEvent +from .response_create_event import ResponseCreateEvent as ResponseCreateEvent +from .server_event_selector import ServerEventSelector as ServerEventSelector +from .session_accept_params import SessionAcceptParams as SessionAcceptParams +from .session_fork_response import SessionForkResponse as SessionForkResponse +from .session_reject_params import SessionRejectParams as SessionRejectParams +from .session_started_event import SessionStartedEvent as SessionStartedEvent +from .session_update_config import SessionUpdateConfig as SessionUpdateConfig +from .session_updated_event import SessionUpdatedEvent as SessionUpdatedEvent +from .thinking_append_event import ThinkingAppendEvent as ThinkingAppendEvent +from .input_audio_mute_event import InputAudioMuteEvent as InputAudioMuteEvent +from .client_delegation_param import ClientDelegationParam as ClientDelegationParam +from .commentary_append_event import CommentaryAppendEvent as CommentaryAppendEvent +from .fork_client_event_param import ForkClientEventParam as ForkClientEventParam +from .input_audio_muted_event import InputAudioMutedEvent as InputAudioMutedEvent +from .sideband_connect_params import SidebandConnectParams as SidebandConnectParams +from .thinking_appended_event import ThinkingAppendedEvent as ThinkingAppendedEvent +from .delegation_created_event import DelegationCreatedEvent as DelegationCreatedEvent +from .fork_session_start_event import ForkSessionStartEvent as ForkSessionStartEvent +from .input_audio_append_event import InputAudioAppendEvent as InputAudioAppendEvent +from .input_audio_unmute_event import InputAudioUnmuteEvent as InputAudioUnmuteEvent +from .output_audio_delta_event import OutputAudioDeltaEvent as OutputAudioDeltaEvent +from .commentary_appended_event import CommentaryAppendedEvent as CommentaryAppendedEvent +from .data_channel_config_param import DataChannelConfigParam as DataChannelConfigParam +from .fork_session_config_param import ForkSessionConfigParam as ForkSessionConfigParam +from .input_audio_unmuted_event import InputAudioUnmutedEvent as InputAudioUnmutedEvent +from .instructions_append_event import InstructionsAppendEvent as InstructionsAppendEvent +from .session_close_event_param import SessionCloseEventParam as SessionCloseEventParam +from .session_start_event_param import SessionStartEventParam as SessionStartEventParam +from .connect_client_event_param import ConnectClientEventParam as ConnectClientEventParam +from .media_session_config_param import MediaSessionConfigParam as MediaSessionConfigParam +from .response_item_create_event import ResponseItemCreateEvent as ResponseItemCreateEvent +from .session_update_event_param import SessionUpdateEventParam as SessionUpdateEventParam +from .instructions_appended_event import InstructionsAppendedEvent as InstructionsAppendedEvent +from .response_create_event_param import ResponseCreateEventParam as ResponseCreateEventParam +from .responses_delegation_config import ResponsesDelegationConfig as ResponsesDelegationConfig +from .server_event_selector_param import ServerEventSelectorParam as ServerEventSelectorParam +from .session_update_config_param import SessionUpdateConfigParam as SessionUpdateConfigParam +from .session_usage_updated_event import SessionUsageUpdatedEvent as SessionUsageUpdatedEvent +from .thinking_append_event_param import ThinkingAppendEventParam as ThinkingAppendEventParam +from .input_audio_mute_event_param import InputAudioMuteEventParam as InputAudioMuteEventParam +from .input_transcript_delta_event import InputTranscriptDeltaEvent as InputTranscriptDeltaEvent +from .commentary_append_event_param import CommentaryAppendEventParam as CommentaryAppendEventParam +from .output_transcript_delta_event import OutputTranscriptDeltaEvent as OutputTranscriptDeltaEvent +from .fork_session_start_event_param import ForkSessionStartEventParam as ForkSessionStartEventParam +from .input_audio_append_event_param import InputAudioAppendEventParam as InputAudioAppendEventParam +from .input_audio_unmute_event_param import InputAudioUnmuteEventParam as InputAudioUnmuteEventParam +from .instructions_append_event_param import InstructionsAppendEventParam as InstructionsAppendEventParam +from .media_session_fork_config_param import MediaSessionForkConfigParam as MediaSessionForkConfigParam +from .response_item_create_event_param import ResponseItemCreateEventParam as ResponseItemCreateEventParam +from .responses_delegation_config_param import ResponsesDelegationConfigParam as ResponsesDelegationConfigParam +from .responses_delegation_update_config import ResponsesDelegationUpdateConfig as ResponsesDelegationUpdateConfig +from .responses_delegation_update_config_param import ( + ResponsesDelegationUpdateConfigParam as ResponsesDelegationUpdateConfigParam, +) diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ce68f54 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/audio_format.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/audio_format.cpython-312.pyc new file mode 100644 index 0000000..6f537e5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/audio_format.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/audio_format_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/audio_format_param.cpython-312.pyc new file mode 100644 index 0000000..d4259c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/audio_format_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/built_in_voice.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/built_in_voice.cpython-312.pyc new file mode 100644 index 0000000..1f4de5b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/built_in_voice.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_config.cpython-312.pyc new file mode 100644 index 0000000..48ef0a8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_config_param.cpython-312.pyc new file mode 100644 index 0000000..d2b4350 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_delegation.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_delegation.cpython-312.pyc new file mode 100644 index 0000000..fa03a5a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_delegation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_delegation_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_delegation_param.cpython-312.pyc new file mode 100644 index 0000000..6687e3d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_delegation_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_event.cpython-312.pyc new file mode 100644 index 0000000..e296953 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_event_param.cpython-312.pyc new file mode 100644 index 0000000..fd65d9a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/client_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/commentary_append_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/commentary_append_event.cpython-312.pyc new file mode 100644 index 0000000..3c09dde Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/commentary_append_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/commentary_append_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/commentary_append_event_param.cpython-312.pyc new file mode 100644 index 0000000..45e74d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/commentary_append_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/commentary_appended_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/commentary_appended_event.cpython-312.pyc new file mode 100644 index 0000000..c9e3d10 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/commentary_appended_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/connect_client_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/connect_client_event.cpython-312.pyc new file mode 100644 index 0000000..4e5ef28 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/connect_client_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/connect_client_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/connect_client_event_param.cpython-312.pyc new file mode 100644 index 0000000..0a0b05f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/connect_client_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/connect_server_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/connect_server_event.cpython-312.pyc new file mode 100644 index 0000000..c47b06d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/connect_server_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/custom_voice.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/custom_voice.cpython-312.pyc new file mode 100644 index 0000000..6dee0f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/custom_voice.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/custom_voice_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/custom_voice_param.cpython-312.pyc new file mode 100644 index 0000000..df5ebe3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/custom_voice_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/data_channel_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/data_channel_config.cpython-312.pyc new file mode 100644 index 0000000..30b98ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/data_channel_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/data_channel_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/data_channel_config_param.cpython-312.pyc new file mode 100644 index 0000000..b26095a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/data_channel_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/delegation_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/delegation_created_event.cpython-312.pyc new file mode 100644 index 0000000..9e76e0c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/delegation_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/error.cpython-312.pyc new file mode 100644 index 0000000..53e899a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/error_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/error_event.cpython-312.pyc new file mode 100644 index 0000000..99cc9a9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/error_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_client_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_client_event.cpython-312.pyc new file mode 100644 index 0000000..eb7bda0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_client_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_client_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_client_event_param.cpython-312.pyc new file mode 100644 index 0000000..01716fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_client_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_server_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_server_event.cpython-312.pyc new file mode 100644 index 0000000..5f4b3a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_server_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_session_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_session_config.cpython-312.pyc new file mode 100644 index 0000000..5f7bcd0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_session_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_session_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_session_config_param.cpython-312.pyc new file mode 100644 index 0000000..514a59a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_session_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_session_start_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_session_start_event.cpython-312.pyc new file mode 100644 index 0000000..4ab5324 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_session_start_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_session_start_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_session_start_event_param.cpython-312.pyc new file mode 100644 index 0000000..3eab6e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/fork_session_start_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/function_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/function_tool.cpython-312.pyc new file mode 100644 index 0000000..b2bfd5c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/function_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/function_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/function_tool_param.cpython-312.pyc new file mode 100644 index 0000000..65f7504 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/function_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/info_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/info_event.cpython-312.pyc new file mode 100644 index 0000000..a6ed3ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/info_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/initial_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/initial_item.cpython-312.pyc new file mode 100644 index 0000000..d76d819 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/initial_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/initial_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/initial_item_param.cpython-312.pyc new file mode 100644 index 0000000..0f78fd2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/initial_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_append_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_append_event.cpython-312.pyc new file mode 100644 index 0000000..8de5a3a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_append_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_append_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_append_event_param.cpython-312.pyc new file mode 100644 index 0000000..5d52a88 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_append_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_mute_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_mute_event.cpython-312.pyc new file mode 100644 index 0000000..237c753 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_mute_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_mute_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_mute_event_param.cpython-312.pyc new file mode 100644 index 0000000..9ddc3f7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_mute_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_muted_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_muted_event.cpython-312.pyc new file mode 100644 index 0000000..f822cfe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_muted_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_unmute_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_unmute_event.cpython-312.pyc new file mode 100644 index 0000000..a4e3269 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_unmute_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_unmute_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_unmute_event_param.cpython-312.pyc new file mode 100644 index 0000000..381302a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_unmute_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_unmuted_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_unmuted_event.cpython-312.pyc new file mode 100644 index 0000000..c6e8771 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_audio_unmuted_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_transcript_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_transcript_delta_event.cpython-312.pyc new file mode 100644 index 0000000..e571648 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/input_transcript_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/instructions_append_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/instructions_append_event.cpython-312.pyc new file mode 100644 index 0000000..16bf0ce Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/instructions_append_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/instructions_append_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/instructions_append_event_param.cpython-312.pyc new file mode 100644 index 0000000..d848450 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/instructions_append_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/instructions_appended_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/instructions_appended_event.cpython-312.pyc new file mode 100644 index 0000000..8b72e2e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/instructions_appended_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/live_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/live_create_params.cpython-312.pyc new file mode 100644 index 0000000..5ab19f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/live_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/live_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/live_create_response.cpython-312.pyc new file mode 100644 index 0000000..d32c35b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/live_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/media_session_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/media_session_config_param.cpython-312.pyc new file mode 100644 index 0000000..5196a43 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/media_session_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/media_session_fork_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/media_session_fork_config_param.cpython-312.pyc new file mode 100644 index 0000000..4ba687a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/media_session_fork_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/output_audio_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/output_audio_delta_event.cpython-312.pyc new file mode 100644 index 0000000..9be0805 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/output_audio_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/output_transcript_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/output_transcript_delta_event.cpython-312.pyc new file mode 100644 index 0000000..d41db44 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/output_transcript_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_create_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_create_event.cpython-312.pyc new file mode 100644 index 0000000..b7bcd73 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_create_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_create_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_create_event_param.cpython-312.pyc new file mode 100644 index 0000000..25b97b7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_create_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_event.cpython-312.pyc new file mode 100644 index 0000000..195b992 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_item_create_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_item_create_event.cpython-312.pyc new file mode 100644 index 0000000..82ed880 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_item_create_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_item_create_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_item_create_event_param.cpython-312.pyc new file mode 100644 index 0000000..4887e43 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/response_item_create_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/responses_delegation_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/responses_delegation_config.cpython-312.pyc new file mode 100644 index 0000000..caa5473 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/responses_delegation_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/responses_delegation_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/responses_delegation_config_param.cpython-312.pyc new file mode 100644 index 0000000..2b9fcdf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/responses_delegation_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/responses_delegation_update_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/responses_delegation_update_config.cpython-312.pyc new file mode 100644 index 0000000..9ea391a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/responses_delegation_update_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/responses_delegation_update_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/responses_delegation_update_config_param.cpython-312.pyc new file mode 100644 index 0000000..e9ba0df Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/responses_delegation_update_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/server_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/server_event.cpython-312.pyc new file mode 100644 index 0000000..5dadeb0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/server_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/server_event_selector.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/server_event_selector.cpython-312.pyc new file mode 100644 index 0000000..6223f70 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/server_event_selector.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/server_event_selector_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/server_event_selector_param.cpython-312.pyc new file mode 100644 index 0000000..1a6c4ba Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/server_event_selector_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_accept_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_accept_params.cpython-312.pyc new file mode 100644 index 0000000..c7ae6af Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_accept_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_close_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_close_event.cpython-312.pyc new file mode 100644 index 0000000..0eb8802 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_close_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_close_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_close_event_param.cpython-312.pyc new file mode 100644 index 0000000..98847dc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_close_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_closed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_closed_event.cpython-312.pyc new file mode 100644 index 0000000..abf4eb3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_closed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_config.cpython-312.pyc new file mode 100644 index 0000000..efb2a1d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_config_param.cpython-312.pyc new file mode 100644 index 0000000..cf7ad1a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_fork_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_fork_params.cpython-312.pyc new file mode 100644 index 0000000..d191dc3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_fork_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_fork_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_fork_response.cpython-312.pyc new file mode 100644 index 0000000..2e2636b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_fork_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_refer_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_refer_params.cpython-312.pyc new file mode 100644 index 0000000..c1211a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_refer_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_reject_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_reject_params.cpython-312.pyc new file mode 100644 index 0000000..331b6aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_reject_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_resource.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_resource.cpython-312.pyc new file mode 100644 index 0000000..5606cc9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_resource.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_start_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_start_event.cpython-312.pyc new file mode 100644 index 0000000..4cd3fcf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_start_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_start_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_start_event_param.cpython-312.pyc new file mode 100644 index 0000000..ae6275a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_start_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_started_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_started_event.cpython-312.pyc new file mode 100644 index 0000000..688951b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_started_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_update_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_update_config.cpython-312.pyc new file mode 100644 index 0000000..323644e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_update_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_update_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_update_config_param.cpython-312.pyc new file mode 100644 index 0000000..44770fb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_update_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_update_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_update_event.cpython-312.pyc new file mode 100644 index 0000000..9e1ab9f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_update_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_update_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_update_event_param.cpython-312.pyc new file mode 100644 index 0000000..ede60a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_update_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_updated_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_updated_event.cpython-312.pyc new file mode 100644 index 0000000..57b36ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_updated_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_usage.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_usage.cpython-312.pyc new file mode 100644 index 0000000..d0ff79c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_usage.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_usage_updated_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_usage_updated_event.cpython-312.pyc new file mode 100644 index 0000000..9d06943 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/session_usage_updated_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/sideband_connect_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/sideband_connect_params.cpython-312.pyc new file mode 100644 index 0000000..5b6a734 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/sideband_connect_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/thinking_append_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/thinking_append_event.cpython-312.pyc new file mode 100644 index 0000000..ad88aa8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/thinking_append_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/thinking_append_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/thinking_append_event_param.cpython-312.pyc new file mode 100644 index 0000000..a3710c3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/thinking_append_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/thinking_appended_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/thinking_appended_event.cpython-312.pyc new file mode 100644 index 0000000..9efd5fa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/live/__pycache__/thinking_appended_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/audio_format.py b/venv/lib/python3.12/site-packages/openai/types/live/audio_format.py new file mode 100644 index 0000000..d932e89 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/audio_format.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["AudioFormat", "AudioPCM", "AudioPCMU", "AudioPCMA"] + + +class AudioPCM(BaseModel): + """Raw, mono 16-bit little-endian PCM audio for a Live WebSocket connection.""" + + rate: Literal[16000, 24000] + """Audio sample rate in hertz. + + Live WebSocket PCM audio supports 16000 or 24000 Hz. + """ + + type: Literal["audio/pcm"] + """The audio encoding. Always `audio/pcm`.""" + + +class AudioPCMU(BaseModel): + """Raw, mono G.711 μ-law audio for a Live WebSocket connection.""" + + rate: int + """Audio sample rate in hertz. G.711 audio uses 8000 Hz.""" + + type: Literal["audio/pcmu"] + """The audio encoding. Always `audio/pcmu`.""" + + +class AudioPCMA(BaseModel): + """Raw, mono G.711 A-law audio for a Live WebSocket connection.""" + + rate: int + """Audio sample rate in hertz. G.711 audio uses 8000 Hz.""" + + type: Literal["audio/pcma"] + """The audio encoding. Always `audio/pcma`.""" + + +AudioFormat: TypeAlias = Annotated[Union[AudioPCM, AudioPCMU, AudioPCMA], PropertyInfo(discriminator="type")] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/audio_format_param.py b/venv/lib/python3.12/site-packages/openai/types/live/audio_format_param.py new file mode 100644 index 0000000..6e3dec8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/audio_format_param.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["AudioFormatParam", "AudioPCM", "AudioPCMU", "AudioPCMA"] + + +class AudioPCM(TypedDict, total=False): + """Raw, mono 16-bit little-endian PCM audio for a Live WebSocket connection.""" + + rate: Required[Literal[16000, 24000]] + """Audio sample rate in hertz. + + Live WebSocket PCM audio supports 16000 or 24000 Hz. + """ + + type: Required[Literal["audio/pcm"]] + """The audio encoding. Always `audio/pcm`.""" + + +class AudioPCMU(TypedDict, total=False): + """Raw, mono G.711 μ-law audio for a Live WebSocket connection.""" + + rate: Required[int] + """Audio sample rate in hertz. G.711 audio uses 8000 Hz.""" + + type: Required[Literal["audio/pcmu"]] + """The audio encoding. Always `audio/pcmu`.""" + + +class AudioPCMA(TypedDict, total=False): + """Raw, mono G.711 A-law audio for a Live WebSocket connection.""" + + rate: Required[int] + """Audio sample rate in hertz. G.711 audio uses 8000 Hz.""" + + type: Required[Literal["audio/pcma"]] + """The audio encoding. Always `audio/pcma`.""" + + +AudioFormatParam: TypeAlias = Union[AudioPCM, AudioPCMU, AudioPCMA] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/built_in_voice.py b/venv/lib/python3.12/site-packages/openai/types/live/built_in_voice.py new file mode 100644 index 0000000..cb6b628 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/built_in_voice.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["BuiltInVoice"] + +BuiltInVoice: TypeAlias = Literal[ + "alloy", + "ash", + "ballad", + "beacon", + "bossa", + "cedar", + "cinder", + "coral", + "delta", + "echo", + "gleam", + "marin", + "meridian", + "quartz", + "ripple", + "sage", + "shimmer", + "stone", + "tempo", + "verse", + "vesper", + "willow", +] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/client_config.py b/venv/lib/python3.12/site-packages/openai/types/live/client_config.py new file mode 100644 index 0000000..7e24a54 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/client_config.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel +from .data_channel_config import DataChannelConfig + +__all__ = ["ClientConfig"] + + +class ClientConfig(BaseModel): + """ + Startup-only capabilities for an untrusted frontend attached to a unified WebRTC session. Trusted sideband connections are unaffected. + """ + + data_channel: DataChannelConfig + """Client and server event permissions for the WebRTC frontend data channel.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/live/client_config_param.py b/venv/lib/python3.12/site-packages/openai/types/live/client_config_param.py new file mode 100644 index 0000000..604be72 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/client_config_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .data_channel_config_param import DataChannelConfigParam + +__all__ = ["ClientConfigParam"] + + +class ClientConfigParam(TypedDict, total=False): + """ + Startup-only capabilities for an untrusted frontend attached to a unified WebRTC session. Trusted sideband connections are unaffected. + """ + + data_channel: Required[DataChannelConfigParam] + """Client and server event permissions for the WebRTC frontend data channel.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/live/client_delegation.py b/venv/lib/python3.12/site-packages/openai/types/live/client_delegation.py new file mode 100644 index 0000000..6826f18 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/client_delegation.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ClientDelegation"] + + +class ClientDelegation(BaseModel): + """Delegate tasks to your application. + + The Live session emits delegation events that your backend handles. + """ + + type: Literal["client"] + """The delegation owner. Always `client` for tasks handled by your application.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/live/client_delegation_param.py b/venv/lib/python3.12/site-packages/openai/types/live/client_delegation_param.py new file mode 100644 index 0000000..018c8ad --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/client_delegation_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ClientDelegationParam"] + + +class ClientDelegationParam(TypedDict, total=False): + """Delegate tasks to your application. + + The Live session emits delegation events that your backend handles. + """ + + type: Required[Literal["client"]] + """The delegation owner. Always `client` for tasks handled by your application.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/live/client_event.py b/venv/lib/python3.12/site-packages/openai/types/live/client_event.py new file mode 100644 index 0000000..b804b89 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/client_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .session_close_event import SessionCloseEvent +from .session_start_event import SessionStartEvent +from .session_update_event import SessionUpdateEvent +from .response_create_event import ResponseCreateEvent +from .thinking_append_event import ThinkingAppendEvent +from .input_audio_mute_event import InputAudioMuteEvent +from .commentary_append_event import CommentaryAppendEvent +from .input_audio_append_event import InputAudioAppendEvent +from .input_audio_unmute_event import InputAudioUnmuteEvent +from .instructions_append_event import InstructionsAppendEvent +from .response_item_create_event import ResponseItemCreateEvent + +__all__ = ["ClientEvent"] + +ClientEvent: TypeAlias = Annotated[ + Union[ + SessionStartEvent, + SessionUpdateEvent, + InputAudioAppendEvent, + InputAudioMuteEvent, + InputAudioUnmuteEvent, + InstructionsAppendEvent, + ThinkingAppendEvent, + CommentaryAppendEvent, + ResponseItemCreateEvent, + ResponseCreateEvent, + SessionCloseEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/client_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/client_event_param.py new file mode 100644 index 0000000..be0ec90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/client_event_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .session_close_event_param import SessionCloseEventParam +from .session_start_event_param import SessionStartEventParam +from .session_update_event_param import SessionUpdateEventParam +from .response_create_event_param import ResponseCreateEventParam +from .thinking_append_event_param import ThinkingAppendEventParam +from .input_audio_mute_event_param import InputAudioMuteEventParam +from .commentary_append_event_param import CommentaryAppendEventParam +from .input_audio_append_event_param import InputAudioAppendEventParam +from .input_audio_unmute_event_param import InputAudioUnmuteEventParam +from .instructions_append_event_param import InstructionsAppendEventParam +from .response_item_create_event_param import ResponseItemCreateEventParam + +__all__ = ["ClientEventParam"] + +ClientEventParam: TypeAlias = Union[ + SessionStartEventParam, + SessionUpdateEventParam, + InputAudioAppendEventParam, + InputAudioMuteEventParam, + InputAudioUnmuteEventParam, + InstructionsAppendEventParam, + ThinkingAppendEventParam, + CommentaryAppendEventParam, + ResponseItemCreateEventParam, + ResponseCreateEventParam, + SessionCloseEventParam, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/commentary_append_event.py b/venv/lib/python3.12/site-packages/openai/types/live/commentary_append_event.py new file mode 100644 index 0000000..6f6e6b0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/commentary_append_event.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["CommentaryAppendEvent"] + + +class CommentaryAppendEvent(BaseModel): + """ + Provide context the Live model can communicate to the user, optionally for an existing client delegation. + """ + + content: str + """Speakable context for the Live model, limited to 500 tokens. + + Use this for a result the model should communicate; use session.thinking.append + for silent context. + """ + + delegation_id: Optional[str] = None + """Required, nullable. + + Set null for general session context, or use the ID from + session.delegation.created for an existing client delegation. Non-null IDs are + not accepted with Responses delegation. + """ + + type: Literal["session.commentary.append"] + """The Live client event type. Always `session.commentary.append`.""" + + event_id: Optional[str] = None + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/commentary_append_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/commentary_append_event_param.py new file mode 100644 index 0000000..f390075 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/commentary_append_event_param.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["CommentaryAppendEventParam"] + + +class CommentaryAppendEventParam(TypedDict, total=False): + """ + Provide context the Live model can communicate to the user, optionally for an existing client delegation. + """ + + content: Required[str] + """Speakable context for the Live model, limited to 500 tokens. + + Use this for a result the model should communicate; use session.thinking.append + for silent context. + """ + + delegation_id: Required[Optional[str]] + """Required, nullable. + + Set null for general session context, or use the ID from + session.delegation.created for an existing client delegation. Non-null IDs are + not accepted with Responses delegation. + """ + + type: Required[Literal["session.commentary.append"]] + """The Live client event type. Always `session.commentary.append`.""" + + event_id: Optional[str] + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/commentary_appended_event.py b/venv/lib/python3.12/site-packages/openai/types/live/commentary_appended_event.py new file mode 100644 index 0000000..df5abf2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/commentary_appended_event.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["CommentaryAppendedEvent"] + + +class CommentaryAppendedEvent(BaseModel): + """ + Returned when a session.commentary.append command is accepted into the Live session timeline. Acknowledges the added commentary without guaranteeing exact wording or completed audio playback. + """ + + end_ms: int + """ + The end of this event on the Live session timeline, in milliseconds from the + beginning of the session. For appended context, this can equal start_ms. + """ + + event_id: str + """The unique ID of the Live server event.""" + + start_ms: int + """ + The start of this event on the Live session timeline, in milliseconds from the + beginning of the session. + """ + + type: Literal["session.commentary.appended"] + """The event type, always `session.commentary.appended`.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/connect_client_event.py b/venv/lib/python3.12/site-packages/openai/types/live/connect_client_event.py new file mode 100644 index 0000000..30433d3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/connect_client_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .session_close_event import SessionCloseEvent +from .session_update_event import SessionUpdateEvent +from .response_create_event import ResponseCreateEvent +from .thinking_append_event import ThinkingAppendEvent +from .input_audio_mute_event import InputAudioMuteEvent +from .commentary_append_event import CommentaryAppendEvent +from .input_audio_unmute_event import InputAudioUnmuteEvent +from .instructions_append_event import InstructionsAppendEvent +from .response_item_create_event import ResponseItemCreateEvent + +__all__ = ["ConnectClientEvent"] + +ConnectClientEvent: TypeAlias = Annotated[ + Union[ + SessionUpdateEvent, + InputAudioMuteEvent, + InputAudioUnmuteEvent, + InstructionsAppendEvent, + ThinkingAppendEvent, + CommentaryAppendEvent, + ResponseItemCreateEvent, + ResponseCreateEvent, + SessionCloseEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/connect_client_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/connect_client_event_param.py new file mode 100644 index 0000000..9fadbcb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/connect_client_event_param.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .session_close_event_param import SessionCloseEventParam +from .session_update_event_param import SessionUpdateEventParam +from .response_create_event_param import ResponseCreateEventParam +from .thinking_append_event_param import ThinkingAppendEventParam +from .input_audio_mute_event_param import InputAudioMuteEventParam +from .commentary_append_event_param import CommentaryAppendEventParam +from .input_audio_unmute_event_param import InputAudioUnmuteEventParam +from .instructions_append_event_param import InstructionsAppendEventParam +from .response_item_create_event_param import ResponseItemCreateEventParam + +__all__ = ["ConnectClientEventParam"] + +ConnectClientEventParam: TypeAlias = Union[ + SessionUpdateEventParam, + InputAudioMuteEventParam, + InputAudioUnmuteEventParam, + InstructionsAppendEventParam, + ThinkingAppendEventParam, + CommentaryAppendEventParam, + ResponseItemCreateEventParam, + ResponseCreateEventParam, + SessionCloseEventParam, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/connect_server_event.py b/venv/lib/python3.12/site-packages/openai/types/live/connect_server_event.py new file mode 100644 index 0000000..6b5b57a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/connect_server_event.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .info_event import InfoEvent +from .error_event import ErrorEvent +from .response_event import ResponseEvent +from .session_closed_event import SessionClosedEvent +from .session_started_event import SessionStartedEvent +from .session_updated_event import SessionUpdatedEvent +from .input_audio_muted_event import InputAudioMutedEvent +from .thinking_appended_event import ThinkingAppendedEvent +from .delegation_created_event import DelegationCreatedEvent +from .commentary_appended_event import CommentaryAppendedEvent +from .input_audio_unmuted_event import InputAudioUnmutedEvent +from .instructions_appended_event import InstructionsAppendedEvent +from .session_usage_updated_event import SessionUsageUpdatedEvent +from .input_transcript_delta_event import InputTranscriptDeltaEvent +from .output_transcript_delta_event import OutputTranscriptDeltaEvent + +__all__ = ["ConnectServerEvent"] + +ConnectServerEvent: TypeAlias = Annotated[ + Union[ + SessionStartedEvent, + SessionUpdatedEvent, + InputAudioMutedEvent, + InputAudioUnmutedEvent, + InstructionsAppendedEvent, + ThinkingAppendedEvent, + CommentaryAppendedEvent, + InputTranscriptDeltaEvent, + OutputTranscriptDeltaEvent, + DelegationCreatedEvent, + ResponseEvent, + SessionUsageUpdatedEvent, + SessionClosedEvent, + ErrorEvent, + InfoEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/custom_voice.py b/venv/lib/python3.12/site-packages/openai/types/live/custom_voice.py new file mode 100644 index 0000000..da1b4eb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/custom_voice.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["CustomVoice"] + + +class CustomVoice(BaseModel): + id: str diff --git a/venv/lib/python3.12/site-packages/openai/types/live/custom_voice_param.py b/venv/lib/python3.12/site-packages/openai/types/live/custom_voice_param.py new file mode 100644 index 0000000..be2c6e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/custom_voice_param.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["CustomVoiceParam"] + + +class CustomVoiceParam(TypedDict, total=False): + id: Required[str] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/data_channel_config.py b/venv/lib/python3.12/site-packages/openai/types/live/data_channel_config.py new file mode 100644 index 0000000..f01cd28 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/data_channel_config.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal + +from ..._models import BaseModel +from .server_event_selector import ServerEventSelector + +__all__ = ["DataChannelConfig"] + + +class DataChannelConfig(BaseModel): + """ + Control which Live events an untrusted WebRTC frontend can send and receive over its data channel. These restrictions do not apply to trusted sideband connections. + """ + + allowed_client_events: Union[Literal["all"], List[str], None] = None + """Client event types that the frontend data channel may send. + + Use 'all' to allow every client event; an empty array allows none. Omission + preserves the existing allow-all behavior. + """ + + allowed_server_events: Union[Literal["all"], List[ServerEventSelector], None] = None + """Server events that may be sent to the frontend data channel. + + Use 'all' to allow every server event; an empty array allows none. Omission + preserves the existing allow-all behavior. Responses events use an object with + type 'response.event' and a response_event selector. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/data_channel_config_param.py b/venv/lib/python3.12/site-packages/openai/types/live/data_channel_config_param.py new file mode 100644 index 0000000..88beb16 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/data_channel_config_param.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, TypedDict + +from ..._types import SequenceNotStr +from .server_event_selector_param import ServerEventSelectorParam + +__all__ = ["DataChannelConfigParam"] + + +class DataChannelConfigParam(TypedDict, total=False): + """ + Control which Live events an untrusted WebRTC frontend can send and receive over its data channel. These restrictions do not apply to trusted sideband connections. + """ + + allowed_client_events: Union[Literal["all"], SequenceNotStr[str]] + """Client event types that the frontend data channel may send. + + Use 'all' to allow every client event; an empty array allows none. Omission + preserves the existing allow-all behavior. + """ + + allowed_server_events: Union[Literal["all"], Iterable[ServerEventSelectorParam]] + """Server events that may be sent to the frontend data channel. + + Use 'all' to allow every server event; an empty array allows none. Omission + preserves the existing allow-all behavior. Responses events use an object with + type 'response.event' and a response_event selector. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/delegation_created_event.py b/venv/lib/python3.12/site-packages/openai/types/live/delegation_created_event.py new file mode 100644 index 0000000..2fe8284 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/delegation_created_event.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["DelegationCreatedEvent", "Delegation"] + + +class Delegation(BaseModel): + """The delegated work identifier and destination. + + This object contains metadata, not the task text. + """ + + id: str + """The unique ID of the delegation. + + Use this as delegation_id when replying to client-owned work or correlating + Responses events. + """ + + target: Literal["client", "responses"] + """ + Where the Live model delegated the work: `client` for your application, or + `responses` for the configured Responses backend. + """ + + type: Literal["delegation"] + """The object type, always `delegation`.""" + + response_id: Optional[str] = None + """The ID of the Responses API response associated with a Responses delegation. + + Omitted for client delegations. + """ + + +class DelegationCreatedEvent(BaseModel): + """ + Returned when the Live model delegates work to your application or a Responses backend. Contains delegation metadata and the position on the session timeline where the work was delegated. + """ + + delegation: Delegation + """The delegated work identifier and destination. + + This object contains metadata, not the task text. + """ + + event_id: str + """The unique ID of the Live server event.""" + + offset_ms: int + """ + The position on the Live session timeline where the delegation was created, in + milliseconds from the beginning of the session. + """ + + type: Literal["session.delegation.created"] + """The event type, always `session.delegation.created`.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/error.py b/venv/lib/python3.12/site-packages/openai/types/live/error.py new file mode 100644 index 0000000..e66d5ca --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/error.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["Error"] + + +class Error(BaseModel): + """ + Details of an error encountered by the Live session, including the affected parameter or client command when available. + """ + + code: str + """ + A machine-readable code identifying the Live error, such as `unknown_parameter`. + """ + + message: str + """A human-readable explanation of the Live error.""" + + type: str + """ + The category of error, such as `invalid_request_error` for an invalid Live + client command. + """ + + client_event_id: Optional[str] = None + """The event_id of the client command that caused the error, when supplied.""" + + param: Optional[str] = None + """The parameter that caused the error, when applicable, such as `session.voice`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/live/error_event.py b/venv/lib/python3.12/site-packages/openai/types/live/error_event.py new file mode 100644 index 0000000..773e981 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/error_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .error import Error +from ..._models import BaseModel + +__all__ = ["ErrorEvent"] + + +class ErrorEvent(BaseModel): + """Reports an error in the Live session, such as an invalid client command. + + Use error.client_event_id, when present, to identify the command that caused the error. + """ + + error: Error + """Details of the Live error and the client command that caused it, when known.""" + + event_id: str + """The unique ID of the Live server event.""" + + type: Literal["error"] + """The event type, always `error`.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/fork_client_event.py b/venv/lib/python3.12/site-packages/openai/types/live/fork_client_event.py new file mode 100644 index 0000000..e642683 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/fork_client_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .session_close_event import SessionCloseEvent +from .session_update_event import SessionUpdateEvent +from .response_create_event import ResponseCreateEvent +from .thinking_append_event import ThinkingAppendEvent +from .input_audio_mute_event import InputAudioMuteEvent +from .commentary_append_event import CommentaryAppendEvent +from .fork_session_start_event import ForkSessionStartEvent +from .input_audio_append_event import InputAudioAppendEvent +from .input_audio_unmute_event import InputAudioUnmuteEvent +from .instructions_append_event import InstructionsAppendEvent +from .response_item_create_event import ResponseItemCreateEvent + +__all__ = ["ForkClientEvent"] + +ForkClientEvent: TypeAlias = Annotated[ + Union[ + ForkSessionStartEvent, + SessionUpdateEvent, + InputAudioAppendEvent, + InputAudioMuteEvent, + InputAudioUnmuteEvent, + InstructionsAppendEvent, + ThinkingAppendEvent, + CommentaryAppendEvent, + ResponseItemCreateEvent, + ResponseCreateEvent, + SessionCloseEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/fork_client_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/fork_client_event_param.py new file mode 100644 index 0000000..dd69151 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/fork_client_event_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .session_close_event_param import SessionCloseEventParam +from .session_update_event_param import SessionUpdateEventParam +from .response_create_event_param import ResponseCreateEventParam +from .thinking_append_event_param import ThinkingAppendEventParam +from .input_audio_mute_event_param import InputAudioMuteEventParam +from .commentary_append_event_param import CommentaryAppendEventParam +from .fork_session_start_event_param import ForkSessionStartEventParam +from .input_audio_append_event_param import InputAudioAppendEventParam +from .input_audio_unmute_event_param import InputAudioUnmuteEventParam +from .instructions_append_event_param import InstructionsAppendEventParam +from .response_item_create_event_param import ResponseItemCreateEventParam + +__all__ = ["ForkClientEventParam"] + +ForkClientEventParam: TypeAlias = Union[ + ForkSessionStartEventParam, + SessionUpdateEventParam, + InputAudioAppendEventParam, + InputAudioMuteEventParam, + InputAudioUnmuteEventParam, + InstructionsAppendEventParam, + ThinkingAppendEventParam, + CommentaryAppendEventParam, + ResponseItemCreateEventParam, + ResponseCreateEventParam, + SessionCloseEventParam, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/fork_server_event.py b/venv/lib/python3.12/site-packages/openai/types/live/fork_server_event.py new file mode 100644 index 0000000..133103b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/fork_server_event.py @@ -0,0 +1,161 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .info_event import InfoEvent +from .error_event import ErrorEvent +from .response_event import ResponseEvent +from .session_closed_event import SessionClosedEvent +from .session_started_event import SessionStartedEvent +from .session_updated_event import SessionUpdatedEvent +from .input_audio_muted_event import InputAudioMutedEvent +from .thinking_appended_event import ThinkingAppendedEvent +from .delegation_created_event import DelegationCreatedEvent +from .output_audio_delta_event import OutputAudioDeltaEvent +from .commentary_appended_event import CommentaryAppendedEvent +from .input_audio_unmuted_event import InputAudioUnmutedEvent +from .instructions_appended_event import InstructionsAppendedEvent +from .session_usage_updated_event import SessionUsageUpdatedEvent +from .input_transcript_delta_event import InputTranscriptDeltaEvent +from .output_transcript_delta_event import OutputTranscriptDeltaEvent + +__all__ = [ + "ForkServerEvent", + "SessionInputAudioAppend", + "TransportDtmfReceived", + "TransportDtmfSend", + "TransportRinging", + "TransportAnswered", + "TransportFailed", + "TransportFailedError", +] + + +class SessionInputAudioAppend(BaseModel): + """ + Input audio received from the primary transport and reflected to a Live sideband connection before model-input muting. + """ + + audio: str + """ + Base64-encoded raw mono PCM16LE at 24 kHz received from the primary transport, + reflected to the sideband before model-input muting. This server event uses the + same audio key as the client command, but is not an acknowledgment of it. + """ + + type: Literal["session.input_audio.append"] + """The event type, always `session.input_audio.append`.""" + + +class TransportDtmfReceived(BaseModel): + """A SIP DTMF keypress received from the caller. + + Delivered only to sideband observers. + """ + + event: str + + event_id: str + + type: Literal["transport.dtmf.received"] + + +class TransportDtmfSend(BaseModel): + """A SIP DTMF keypress successfully sent by the hosted tool. + + Delivered only to sideband observers; this is not a client command. + """ + + event: str + + event_id: str + + type: Literal["transport.dtmf.send"] + + +class TransportRinging(BaseModel): + """The outbound SIP provider leg is ringing or providing early media. + + Delivered only to sideband observers. + """ + + event_id: str + + session_id: str + """The canonical Live session ID.""" + + type: Literal["transport.ringing"] + + +class TransportAnswered(BaseModel): + """The outbound SIP provider leg answered and media is established. + + Delivered only to sideband observers. + """ + + event_id: str + + session_id: str + """The canonical Live session ID.""" + + type: Literal["transport.answered"] + + +class TransportFailedError(BaseModel): + code: str + """The call setup failure code.""" + + message: str + + type: Literal["call_error"] + + param: Optional[str] = None + """The parameter related to the error, if any. Empty when no parameter applies.""" + + +class TransportFailed(BaseModel): + """An asynchronous outbound SIP setup failure. + + Delivered only to sideband observers. + """ + + error: TransportFailedError + + event_id: str + + session_id: str + """The canonical Live session ID.""" + + type: Literal["transport.failed"] + + +ForkServerEvent: TypeAlias = Annotated[ + Union[ + SessionStartedEvent, + SessionUpdatedEvent, + InputAudioMutedEvent, + InputAudioUnmutedEvent, + InstructionsAppendedEvent, + ThinkingAppendedEvent, + CommentaryAppendedEvent, + SessionInputAudioAppend, + OutputAudioDeltaEvent, + InputTranscriptDeltaEvent, + OutputTranscriptDeltaEvent, + DelegationCreatedEvent, + ResponseEvent, + SessionUsageUpdatedEvent, + SessionClosedEvent, + ErrorEvent, + InfoEvent, + TransportDtmfReceived, + TransportDtmfSend, + TransportRinging, + TransportAnswered, + TransportFailed, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/fork_session_config.py b/venv/lib/python3.12/site-packages/openai/types/live/fork_session_config.py new file mode 100644 index 0000000..4529bdf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/fork_session_config.py @@ -0,0 +1,76 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .audio_format import AudioFormat +from .client_config import ClientConfig +from .responses_delegation_update_config import ResponsesDelegationUpdateConfig + +__all__ = ["ForkSessionConfig", "Audio", "Delegation"] + + +class Audio(BaseModel): + """Audio format for a WebSocket fork. + + WebRTC forks negotiate their audio format and must omit this field. + """ + + format: Optional[AudioFormat] = None + """ + Audio encoding and sample rate for audio sent and received over a Live WebSocket + connection. WebRTC and SIP negotiate their media format separately. + """ + + +class Delegation(BaseModel): + """Overrides for the stored session’s Responses backend. + + Only supported when the stored session already uses Responses delegation; the delegation type cannot change. + """ + + type: Literal["responses"] + """The delegation owner. + + Always `responses` for tasks handled by the Responses API. + """ + + responses: Optional[ResponsesDelegationUpdateConfig] = None + """Responses backend settings to update. + + Omitted settings keep their existing values. + """ + + +class ForkSessionConfig(BaseModel): + """Overrides for a stored session after connecting to the fork WebSocket. + + An empty object inherits the stored configuration; do not supply a new model. audio.format applies only to the new WebSocket connection. client overrides are only supported for WebRTC forks. + """ + + audio: Optional[Audio] = None + """Audio format for a WebSocket fork. + + WebRTC forks negotiate their audio format and must omit this field. + """ + + client: Optional[ClientConfig] = None + """Frontend data-channel permissions for a WebRTC fork. + + Omitted permissions inherit the stored values. Not supported for WebSocket + forks. + """ + + delegation: Optional[Delegation] = None + """Overrides for the stored session’s Responses backend. + + Only supported when the stored session already uses Responses delegation; the + delegation type cannot change. + """ + + store: Optional[bool] = None + """Whether to store the forked session. + + Omission inherits the stored session's setting. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/fork_session_config_param.py b/venv/lib/python3.12/site-packages/openai/types/live/fork_session_config_param.py new file mode 100644 index 0000000..0897486 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/fork_session_config_param.py @@ -0,0 +1,76 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .audio_format_param import AudioFormatParam +from .client_config_param import ClientConfigParam +from .responses_delegation_update_config_param import ResponsesDelegationUpdateConfigParam + +__all__ = ["ForkSessionConfigParam", "Audio", "Delegation"] + + +class Audio(TypedDict, total=False): + """Audio format for a WebSocket fork. + + WebRTC forks negotiate their audio format and must omit this field. + """ + + format: AudioFormatParam + """ + Audio encoding and sample rate for audio sent and received over a Live WebSocket + connection. WebRTC and SIP negotiate their media format separately. + """ + + +class Delegation(TypedDict, total=False): + """Overrides for the stored session’s Responses backend. + + Only supported when the stored session already uses Responses delegation; the delegation type cannot change. + """ + + type: Required[Literal["responses"]] + """The delegation owner. + + Always `responses` for tasks handled by the Responses API. + """ + + responses: ResponsesDelegationUpdateConfigParam + """Responses backend settings to update. + + Omitted settings keep their existing values. + """ + + +class ForkSessionConfigParam(TypedDict, total=False): + """Overrides for a stored session after connecting to the fork WebSocket. + + An empty object inherits the stored configuration; do not supply a new model. audio.format applies only to the new WebSocket connection. client overrides are only supported for WebRTC forks. + """ + + audio: Audio + """Audio format for a WebSocket fork. + + WebRTC forks negotiate their audio format and must omit this field. + """ + + client: ClientConfigParam + """Frontend data-channel permissions for a WebRTC fork. + + Omitted permissions inherit the stored values. Not supported for WebSocket + forks. + """ + + delegation: Delegation + """Overrides for the stored session’s Responses backend. + + Only supported when the stored session already uses Responses delegation; the + delegation type cannot change. + """ + + store: bool + """Whether to store the forked session. + + Omission inherits the stored session's setting. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/fork_session_start_event.py b/venv/lib/python3.12/site-packages/openai/types/live/fork_session_start_event.py new file mode 100644 index 0000000..cedb84f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/fork_session_start_event.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .fork_session_config import ForkSessionConfig + +__all__ = ["ForkSessionStartEvent"] + + +class ForkSessionStartEvent(BaseModel): + """Start a Live session after connecting to a stored session’s fork WebSocket. + + Send an empty `session` object to use the stored configuration. + """ + + session: ForkSessionConfig + """Overrides for a stored session after connecting to the fork WebSocket. + + An empty object inherits the stored configuration; do not supply a new model. + audio.format applies only to the new WebSocket connection. client overrides are + only supported for WebRTC forks. + """ + + type: Literal["session.start"] + """The Live client event type. Always `session.start`.""" + + event_id: Optional[str] = None + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/fork_session_start_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/fork_session_start_event_param.py new file mode 100644 index 0000000..4a258f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/fork_session_start_event_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from .fork_session_config_param import ForkSessionConfigParam + +__all__ = ["ForkSessionStartEventParam"] + + +class ForkSessionStartEventParam(TypedDict, total=False): + """Start a Live session after connecting to a stored session’s fork WebSocket. + + Send an empty `session` object to use the stored configuration. + """ + + session: Required[ForkSessionConfigParam] + """Overrides for a stored session after connecting to the fork WebSocket. + + An empty object inherits the stored configuration; do not supply a new model. + audio.format applies only to the new WebSocket connection. client overrides are + only supported for WebRTC forks. + """ + + type: Required[Literal["session.start"]] + """The Live client event type. Always `session.start`.""" + + event_id: Optional[str] + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/function_tool.py b/venv/lib/python3.12/site-packages/openai/types/live/function_tool.py new file mode 100644 index 0000000..0bbed58 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/function_tool.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FunctionTool"] + + +class FunctionTool(BaseModel): + """ + A function tool available to the Responses backend when the Live model delegates a task. + """ + + name: str + """The name the delegated Responses model uses when calling this function.""" + + type: Literal["function"] + """The tool type. Always `function`.""" + + description: Optional[str] = None + """What the function does and when the delegated Responses model should call it.""" + + parameters: Optional[Dict[str, object]] = None + """A JSON Schema object describing the arguments accepted by the function.""" + + strict: Optional[bool] = None + """ + Whether the delegated Responses model must follow the function’s parameter + schema exactly. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/function_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/live/function_tool_param.py new file mode 100644 index 0000000..20a396a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/function_tool_param.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["FunctionToolParam"] + + +class FunctionToolParam(TypedDict, total=False): + """ + A function tool available to the Responses backend when the Live model delegates a task. + """ + + name: Required[str] + """The name the delegated Responses model uses when calling this function.""" + + type: Required[Literal["function"]] + """The tool type. Always `function`.""" + + description: Optional[str] + """What the function does and when the delegated Responses model should call it.""" + + parameters: Optional[Dict[str, object]] + """A JSON Schema object describing the arguments accepted by the function.""" + + strict: Optional[bool] + """ + Whether the delegated Responses model must follow the function’s parameter + schema exactly. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/info_event.py b/venv/lib/python3.12/site-packages/openai/types/live/info_event.py new file mode 100644 index 0000000..03c11f1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/info_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InfoEvent"] + + +class InfoEvent(BaseModel): + """ + An informational notice about the Live session, such as the event permissions applied to a frontend data channel. + """ + + code: str + """A machine-readable code for the notice, such as `data_channel_permissions`.""" + + event_id: str + """The unique ID of the Live server event.""" + + message: str + """A human-readable explanation of the Live session notice.""" + + type: Literal["info"] + """The event type, always `info`.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/initial_item.py b/venv/lib/python3.12/site-packages/openai/types/live/initial_item.py new file mode 100644 index 0000000..4f99f8f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/initial_item.py @@ -0,0 +1,153 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "InitialItem", + "Developer", + "DeveloperContent", + "User", + "UserContent", + "Assistant", + "AssistantContent", + "AssistantContentText", + "AssistantContentOutputText", +] + + +class DeveloperContent(BaseModel): + """Text supplied in a developer or user message when starting a Live session.""" + + text: str + """The message text to include in the Live session’s initial conversation history.""" + + type: Optional[Literal["input_text"]] = None + """The text content type. Always `input_text`.""" + + +class Developer(BaseModel): + """A developer message included in the initial text history of a Live session.""" + + content: List[DeveloperContent] + """The message content. + + Supply exactly one text part for the initial Live conversation history. + """ + + role: Literal["developer"] + """The author of this history message. Always `developer`.""" + + id: Optional[str] = None + """An optional identifier for the supplied history message. + + Live uses the message’s role and text to initialize the conversation. + """ + + status: Optional[Literal["incomplete", "completed"]] = None + """The supplied message’s status. + + Live uses its text as history and does not resume an incomplete message. + """ + + type: Optional[Literal["message"]] = None + """The history item type. Always `message`.""" + + +class UserContent(BaseModel): + """Text supplied in a developer or user message when starting a Live session.""" + + text: str + """The message text to include in the Live session’s initial conversation history.""" + + type: Optional[Literal["input_text"]] = None + """The text content type. Always `input_text`.""" + + +class User(BaseModel): + """A user message included in the initial text history of a Live session.""" + + content: List[UserContent] + """The message content. + + Supply exactly one text part for the initial Live conversation history. + """ + + role: Literal["user"] + """The author of this history message. Always `user`.""" + + id: Optional[str] = None + """An optional identifier for the supplied history message. + + Live uses the message’s role and text to initialize the conversation. + """ + + status: Optional[Literal["incomplete", "completed"]] = None + """The supplied message’s status. + + Live uses its text as history and does not resume an incomplete message. + """ + + type: Optional[Literal["message"]] = None + """The history item type. Always `message`.""" + + +class AssistantContentText(BaseModel): + """Assistant text supplied as conversation history when starting a Live session.""" + + text: str + """The message text to include in the Live session’s initial conversation history.""" + + type: Optional[Literal["text"]] = None + """The text content type. Always `text`.""" + + +class AssistantContentOutputText(BaseModel): + """ + Assistant output text supplied as conversation history when starting a Live session. + """ + + text: str + """The message text to include in the Live session’s initial conversation history.""" + + type: Literal["output_text"] + """The text content type. Always `output_text`.""" + + +AssistantContent: TypeAlias = Annotated[ + Union[AssistantContentText, AssistantContentOutputText], PropertyInfo(discriminator="type") +] + + +class Assistant(BaseModel): + """An assistant message included in the initial text history of a Live session.""" + + content: List[AssistantContent] + """The message content. + + Supply exactly one text part for the initial Live conversation history. + """ + + role: Literal["assistant"] + """The author of this history message. Always `assistant`.""" + + id: Optional[str] = None + """An optional identifier for the supplied history message. + + Live uses the message’s role and text to initialize the conversation. + """ + + status: Optional[Literal["incomplete", "completed"]] = None + """The supplied message’s status. + + Live uses its text as history and does not resume an incomplete message. + """ + + type: Optional[Literal["message"]] = None + """The history item type. Always `message`.""" + + +InitialItem: TypeAlias = Annotated[Union[Developer, User, Assistant], PropertyInfo(discriminator="role")] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/initial_item_param.py b/venv/lib/python3.12/site-packages/openai/types/live/initial_item_param.py new file mode 100644 index 0000000..b8771d6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/initial_item_param.py @@ -0,0 +1,150 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "InitialItemParam", + "Developer", + "DeveloperContent", + "User", + "UserContent", + "Assistant", + "AssistantContent", + "AssistantContentText", + "AssistantContentOutputText", +] + + +class DeveloperContent(TypedDict, total=False): + """Text supplied in a developer or user message when starting a Live session.""" + + text: Required[str] + """The message text to include in the Live session’s initial conversation history.""" + + type: Literal["input_text"] + """The text content type. Always `input_text`.""" + + +class Developer(TypedDict, total=False): + """A developer message included in the initial text history of a Live session.""" + + content: Required[Iterable[DeveloperContent]] + """The message content. + + Supply exactly one text part for the initial Live conversation history. + """ + + role: Required[Literal["developer"]] + """The author of this history message. Always `developer`.""" + + id: Optional[str] + """An optional identifier for the supplied history message. + + Live uses the message’s role and text to initialize the conversation. + """ + + status: Optional[Literal["incomplete", "completed"]] + """The supplied message’s status. + + Live uses its text as history and does not resume an incomplete message. + """ + + type: Literal["message"] + """The history item type. Always `message`.""" + + +class UserContent(TypedDict, total=False): + """Text supplied in a developer or user message when starting a Live session.""" + + text: Required[str] + """The message text to include in the Live session’s initial conversation history.""" + + type: Literal["input_text"] + """The text content type. Always `input_text`.""" + + +class User(TypedDict, total=False): + """A user message included in the initial text history of a Live session.""" + + content: Required[Iterable[UserContent]] + """The message content. + + Supply exactly one text part for the initial Live conversation history. + """ + + role: Required[Literal["user"]] + """The author of this history message. Always `user`.""" + + id: Optional[str] + """An optional identifier for the supplied history message. + + Live uses the message’s role and text to initialize the conversation. + """ + + status: Optional[Literal["incomplete", "completed"]] + """The supplied message’s status. + + Live uses its text as history and does not resume an incomplete message. + """ + + type: Literal["message"] + """The history item type. Always `message`.""" + + +class AssistantContentText(TypedDict, total=False): + """Assistant text supplied as conversation history when starting a Live session.""" + + text: Required[str] + """The message text to include in the Live session’s initial conversation history.""" + + type: Literal["text"] + """The text content type. Always `text`.""" + + +class AssistantContentOutputText(TypedDict, total=False): + """ + Assistant output text supplied as conversation history when starting a Live session. + """ + + text: Required[str] + """The message text to include in the Live session’s initial conversation history.""" + + type: Required[Literal["output_text"]] + """The text content type. Always `output_text`.""" + + +AssistantContent: TypeAlias = Union[AssistantContentText, AssistantContentOutputText] + + +class Assistant(TypedDict, total=False): + """An assistant message included in the initial text history of a Live session.""" + + content: Required[Iterable[AssistantContent]] + """The message content. + + Supply exactly one text part for the initial Live conversation history. + """ + + role: Required[Literal["assistant"]] + """The author of this history message. Always `assistant`.""" + + id: Optional[str] + """An optional identifier for the supplied history message. + + Live uses the message’s role and text to initialize the conversation. + """ + + status: Optional[Literal["incomplete", "completed"]] + """The supplied message’s status. + + Live uses its text as history and does not resume an incomplete message. + """ + + type: Literal["message"] + """The history item type. Always `message`.""" + + +InitialItemParam: TypeAlias = Union[Developer, User, Assistant] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/input_audio_append_event.py b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_append_event.py new file mode 100644 index 0000000..49db050 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_append_event.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioAppendEvent"] + + +class InputAudioAppendEvent(BaseModel): + """Send audio to a Live session over its primary WebSocket. + + WebRTC and SIP sessions send audio over their media transport. + """ + + audio: str + """ + Base64-encoded raw audio in the startup-selected format, without a WAV or other + container header. Primary WebSocket only; media transports use their audio + track. Audio appends have no acknowledgment. Reflected sideband server events + reuse this event type and audio key, with no timestamps or event_id; their audio + is always mono PCM16LE at 24 kHz. + """ + + type: Literal["session.input_audio.append"] + """The Live client event type. Always `session.input_audio.append`.""" + + event_id: Optional[str] = None + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/input_audio_append_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_append_event_param.py new file mode 100644 index 0000000..ece0c43 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_append_event_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioAppendEventParam"] + + +class InputAudioAppendEventParam(TypedDict, total=False): + """Send audio to a Live session over its primary WebSocket. + + WebRTC and SIP sessions send audio over their media transport. + """ + + audio: Required[str] + """ + Base64-encoded raw audio in the startup-selected format, without a WAV or other + container header. Primary WebSocket only; media transports use their audio + track. Audio appends have no acknowledgment. Reflected sideband server events + reuse this event type and audio key, with no timestamps or event_id; their audio + is always mono PCM16LE at 24 kHz. + """ + + type: Required[Literal["session.input_audio.append"]] + """The Live client event type. Always `session.input_audio.append`.""" + + event_id: Optional[str] + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/input_audio_mute_event.py b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_mute_event.py new file mode 100644 index 0000000..f870fa4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_mute_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioMuteEvent"] + + +class InputAudioMuteEvent(BaseModel): + """Mute audio input to the Live model without closing the session. + + The server acknowledges with `session.input_audio.muted`. + """ + + type: Literal["session.input_audio.mute"] + """The Live client event type. Always `session.input_audio.mute`.""" + + event_id: Optional[str] = None + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/input_audio_mute_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_mute_event_param.py new file mode 100644 index 0000000..0e504c4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_mute_event_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioMuteEventParam"] + + +class InputAudioMuteEventParam(TypedDict, total=False): + """Mute audio input to the Live model without closing the session. + + The server acknowledges with `session.input_audio.muted`. + """ + + type: Required[Literal["session.input_audio.mute"]] + """The Live client event type. Always `session.input_audio.mute`.""" + + event_id: Optional[str] + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/input_audio_muted_event.py b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_muted_event.py new file mode 100644 index 0000000..682f32e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_muted_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioMutedEvent"] + + +class InputAudioMutedEvent(BaseModel): + """Returned when a session.input_audio.mute command is accepted. + + Input audio is no longer sent to the model; sideband audio reflection continues. + """ + + event_id: str + """The unique ID of the Live server event.""" + + type: Literal["session.input_audio.muted"] + """The event type, always `session.input_audio.muted`.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/input_audio_unmute_event.py b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_unmute_event.py new file mode 100644 index 0000000..fedc9bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_unmute_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioUnmuteEvent"] + + +class InputAudioUnmuteEvent(BaseModel): + """Resume audio input to a Live model after muting it. + + The server acknowledges with `session.input_audio.unmuted`. + """ + + type: Literal["session.input_audio.unmute"] + """The Live client event type. Always `session.input_audio.unmute`.""" + + event_id: Optional[str] = None + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/input_audio_unmute_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_unmute_event_param.py new file mode 100644 index 0000000..a75ccf2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_unmute_event_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioUnmuteEventParam"] + + +class InputAudioUnmuteEventParam(TypedDict, total=False): + """Resume audio input to a Live model after muting it. + + The server acknowledges with `session.input_audio.unmuted`. + """ + + type: Required[Literal["session.input_audio.unmute"]] + """The Live client event type. Always `session.input_audio.unmute`.""" + + event_id: Optional[str] + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/input_audio_unmuted_event.py b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_unmuted_event.py new file mode 100644 index 0000000..cf559f8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/input_audio_unmuted_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioUnmutedEvent"] + + +class InputAudioUnmutedEvent(BaseModel): + """Returned when a session.input_audio.unmute command is accepted. + + Input audio is sent to the model again. + """ + + event_id: str + """The unique ID of the Live server event.""" + + type: Literal["session.input_audio.unmuted"] + """The event type, always `session.input_audio.unmuted`.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/input_transcript_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/live/input_transcript_delta_event.py new file mode 100644 index 0000000..e7ec8f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/input_transcript_delta_event.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputTranscriptDeltaEvent"] + + +class InputTranscriptDeltaEvent(BaseModel): + """A transcript fragment for user input audio in the Live session. + + Accumulate fragments in delivery order; these events do not define complete turns or include a transcript-done event. + """ + + delta: str + """The transcript text fragment for the audio in this time range. + + Append fragments in delivery order to build the transcript. + """ + + end_ms: int + """ + The end of this event on the Live session timeline, in milliseconds from the + beginning of the session. For appended context, this can equal start_ms. + """ + + event_id: str + """The unique ID of the Live server event.""" + + start_ms: int + """ + The start of this event on the Live session timeline, in milliseconds from the + beginning of the session. + """ + + type: Literal["session.input_transcript.delta"] + """The event type, always `session.input_transcript.delta`.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/instructions_append_event.py b/venv/lib/python3.12/site-packages/openai/types/live/instructions_append_event.py new file mode 100644 index 0000000..a682ace --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/instructions_append_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InstructionsAppendEvent"] + + +class InstructionsAppendEvent(BaseModel): + """ + Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation. + """ + + content: str + """Instruction text to append, limited to 500 tokens. + + This is a plain string, not an array of content parts. + """ + + delegation_id: Optional[str] = None + """Required, nullable. + + Set null for general session context, or use the ID from + session.delegation.created for an existing client delegation. Non-null IDs are + not accepted with Responses delegation. + """ + + type: Literal["session.instructions.append"] + """The Live client event type. Always `session.instructions.append`.""" + + event_id: Optional[str] = None + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/instructions_append_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/instructions_append_event_param.py new file mode 100644 index 0000000..34d874b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/instructions_append_event_param.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InstructionsAppendEventParam"] + + +class InstructionsAppendEventParam(TypedDict, total=False): + """ + Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation. + """ + + content: Required[str] + """Instruction text to append, limited to 500 tokens. + + This is a plain string, not an array of content parts. + """ + + delegation_id: Required[Optional[str]] + """Required, nullable. + + Set null for general session context, or use the ID from + session.delegation.created for an existing client delegation. Non-null IDs are + not accepted with Responses delegation. + """ + + type: Required[Literal["session.instructions.append"]] + """The Live client event type. Always `session.instructions.append`.""" + + event_id: Optional[str] + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/instructions_appended_event.py b/venv/lib/python3.12/site-packages/openai/types/live/instructions_appended_event.py new file mode 100644 index 0000000..3bc7532 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/instructions_appended_event.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InstructionsAppendedEvent"] + + +class InstructionsAppendedEvent(BaseModel): + """ + Returned when a session.instructions.append command is accepted into the Live session timeline. Acknowledges the appended instructions without guaranteeing that the model has acted on them. + """ + + end_ms: int + """ + The end of this event on the Live session timeline, in milliseconds from the + beginning of the session. For appended context, this can equal start_ms. + """ + + event_id: str + """The unique ID of the Live server event.""" + + start_ms: int + """ + The start of this event on the Live session timeline, in milliseconds from the + beginning of the session. + """ + + type: Literal["session.instructions.appended"] + """The event type, always `session.instructions.appended`.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/live_create_params.py b/venv/lib/python3.12/site-packages/openai/types/live/live_create_params.py new file mode 100644 index 0000000..db23ff1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/live_create_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .media_session_config_param import MediaSessionConfigParam + +__all__ = ["LiveCreateParams", "Transport"] + + +class LiveCreateParams(TypedDict, total=False): + session: Required[MediaSessionConfigParam] + """Startup configuration for the Live session.""" + + transport: Required[Transport] + """WebRTC transport with the browser's SDP offer.""" + + +class Transport(TypedDict, total=False): + """WebRTC transport with the browser's SDP offer.""" + + sdp: Required[str] + """Session Description Protocol message for the WebRTC connection.""" + + type: Required[Literal["webrtc"]] + """The transport used for the Live session. Always `webrtc`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/live/live_create_response.py b/venv/lib/python3.12/site-packages/openai/types/live/live_create_response.py new file mode 100644 index 0000000..ce5dc80 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/live_create_response.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["LiveCreateResponse", "Session", "Transport"] + + +class Session(BaseModel): + """The newly created Live session. + + Use its ID for session controls and sideband connections. + """ + + id: str + """Opaque session identifier. + + Preserve the returned value unchanged, including its prefix. + """ + + +class Transport(BaseModel): + """WebRTC transport with the SDP answer.""" + + sdp: str + """Session Description Protocol message for the WebRTC connection.""" + + type: Literal["webrtc"] + """The transport used for the Live session. Always `webrtc`.""" + + +class LiveCreateResponse(BaseModel): + """The created Live session identifier and WebRTC answer. + + Apply transport.sdp as the peer's remote answer and wait for session.started on the data channel before sending commands. + """ + + session: Session + """The newly created Live session. + + Use its ID for session controls and sideband connections. + """ + + transport: Transport + """WebRTC transport with the SDP answer.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/live/media_session_config_param.py b/venv/lib/python3.12/site-packages/openai/types/live/media_session_config_param.py new file mode 100644 index 0000000..f0c259c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/media_session_config_param.py @@ -0,0 +1,119 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .built_in_voice import BuiltInVoice +from .custom_voice_param import CustomVoiceParam +from .initial_item_param import InitialItemParam +from .client_config_param import ClientConfigParam +from .client_delegation_param import ClientDelegationParam +from .responses_delegation_config_param import ResponsesDelegationConfigParam + +__all__ = ["MediaSessionConfigParam", "Audio", "AudioOutput", "AudioOutputVoice", "Delegation", "DelegationResponses"] + +AudioOutputVoice: TypeAlias = Union[str, BuiltInVoice, CustomVoiceParam] + + +class AudioOutput(TypedDict, total=False): + """Settings for speech generated by the Live model. + + Choose the voice before starting the session. + """ + + voice: AudioOutputVoice + """ + The voice used for Live speech, as a built-in voice name or a custom voice + object containing its ID. Defaults to `marin` and cannot change after startup. + """ + + +class Audio(TypedDict, total=False): + """Startup audio configuration. + + WebRTC and SIP negotiate their audio format on the media transport. + """ + + output: AudioOutput + """Settings for speech generated by the Live model. + + Choose the voice before starting the session. + """ + + +class DelegationResponses(TypedDict, total=False): + """Delegate tasks to a Responses model managed by the Live session.""" + + responses: Required[ResponsesDelegationConfigParam] + """ + Backend model, prompt, and tools used when the Live session delegates a task to + Responses. + """ + + type: Required[Literal["responses"]] + """The delegation owner. + + Always `responses` for tasks handled by the Responses API. + """ + + +Delegation: TypeAlias = Union[ClientDelegationParam, DelegationResponses] + + +class MediaSessionConfigParam(TypedDict, total=False): + """Startup configuration for a Live media session. + + Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) when writing frontend instructions and the backend prompt under delegation.responses.instructions. + """ + + model: Required[Union[str, Literal["gpt-live-1"]]] + """The Live model. + + Required in the session configuration for every transport; do not pass it as a + URL query parameter. + """ + + audio: Audio + """Startup audio configuration. + + WebRTC and SIP negotiate their audio format on the media transport. + """ + + client: ClientConfigParam + """ + Startup-only capabilities for an untrusted frontend attached to a unified WebRTC + session. Trusted sideband connections are unaffected. + """ + + delegation: Optional[Delegation] + """Who handles tasks delegated by the Live model. + + Omitted or null selects your application; use `responses` to let the API manage + a Responses backend. + """ + + input: Iterable[InitialItemParam] + """Ordered text-only history supplied before startup. + + Supports developer, user, and assistant messages with one text part each; at + most 128 messages and 8,192 rendered tokens in total. + """ + + instructions: Optional[str] + """ + Frontend instructions for voice, conversation, interruptions, and when to + delegate. Start with the + [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting); + put business rules and tool workflows in a separate + [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). + Limited to 16,384 client-supplied tokens. Omitted or blank instructions use + server defaults. Immutable after startup. + """ + + store: bool + """Whether to store the session for later forking and recording download. + + Defaults to false for new sessions. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/media_session_fork_config_param.py b/venv/lib/python3.12/site-packages/openai/types/live/media_session_fork_config_param.py new file mode 100644 index 0000000..3bc9529 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/media_session_fork_config_param.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .client_config_param import ClientConfigParam +from .responses_delegation_update_config_param import ResponsesDelegationUpdateConfigParam + +__all__ = ["MediaSessionForkConfigParam", "Delegation"] + + +class Delegation(TypedDict, total=False): + """ + Update the Responses backend for an existing Live session without changing delegation ownership. + """ + + type: Required[Literal["responses"]] + """The delegation owner. + + Always `responses` for tasks handled by the Responses API. + """ + + responses: ResponsesDelegationUpdateConfigParam + """Responses backend settings to update. + + Omitted settings keep their existing values. + """ + + +class MediaSessionForkConfigParam(TypedDict, total=False): + """Optional overrides for a stored Live session. + + Omitted settings are inherited. The model, voice, frontend instructions, and prior conversation come from the stored session. WebRTC negotiates its audio format; audio.format is only supported on WebSocket forks. + """ + + client: ClientConfigParam + """ + Startup-only capabilities for an untrusted frontend attached to a unified WebRTC + session. Trusted sideband connections are unaffected. + """ + + delegation: Delegation + """ + Update the Responses backend for an existing Live session without changing + delegation ownership. + """ + + store: bool + """Whether to store the forked session. + + Omission inherits the stored session's setting. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/output_audio_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/live/output_audio_delta_event.py new file mode 100644 index 0000000..9ec7e27 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/output_audio_delta_event.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["OutputAudioDeltaEvent"] + + +class OutputAudioDeltaEvent(BaseModel): + """An audio chunk generated by the Live model. + + Decode and play primary WebSocket chunks in delivery order using the configured session audio format. Sideband connections receive reflected output audio with timestamps. + """ + + delta: str + """Base64-encoded raw audio. + + Primary WebSocket events use the session's configured format; reflected sideband + events use mono PCM16LE at 24 kHz. + """ + + type: Literal["session.output_audio.delta"] + """The event type, always `session.output_audio.delta`.""" + + end_ms: Optional[int] = None + """Exclusive session-relative end in milliseconds. + + Required on reflected sideband events; omitted on the primary WebSocket. Dropped + output frames leave gaps between reflected ranges. + """ + + start_ms: Optional[int] = None + """Inclusive session-relative start in milliseconds. + + Required on reflected sideband events; omitted on the primary WebSocket. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/output_transcript_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/live/output_transcript_delta_event.py new file mode 100644 index 0000000..6463888 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/output_transcript_delta_event.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["OutputTranscriptDeltaEvent"] + + +class OutputTranscriptDeltaEvent(BaseModel): + """A transcript fragment for assistant output audio in the Live session. + + Accumulate fragments in delivery order; these events do not define complete turns or include a transcript-done event. + """ + + delta: str + """The transcript text fragment for the audio in this time range. + + Append fragments in delivery order to build the transcript. + """ + + end_ms: int + """ + The end of this event on the Live session timeline, in milliseconds from the + beginning of the session. For appended context, this can equal start_ms. + """ + + event_id: str + """The unique ID of the Live server event.""" + + start_ms: int + """ + The start of this event on the Live session timeline, in milliseconds from the + beginning of the session. + """ + + type: Literal["session.output_transcript.delta"] + """The event type, always `session.output_transcript.delta`.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/response_create_event.py b/venv/lib/python3.12/site-packages/openai/types/live/response_create_event.py new file mode 100644 index 0000000..dbcbc4f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/response_create_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCreateEvent"] + + +class ResponseCreateEvent(BaseModel): + """ + Request a response from the Live session’s Responses backend, or continue a delegated response waiting for tool results. Requires Responses delegation. + """ + + type: Literal["response.create"] + """The Live client event type. Always `response.create`.""" + + event_id: Optional[str] = None + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/response_create_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/response_create_event_param.py new file mode 100644 index 0000000..3c19088 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/response_create_event_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseCreateEventParam"] + + +class ResponseCreateEventParam(TypedDict, total=False): + """ + Request a response from the Live session’s Responses backend, or continue a delegated response waiting for tool results. Requires Responses delegation. + """ + + type: Required[Literal["response.create"]] + """The Live client event type. Always `response.create`.""" + + event_id: Optional[str] + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/response_event.py b/venv/lib/python3.12/site-packages/openai/types/live/response_event.py new file mode 100644 index 0000000..99adf3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/response_event.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseEvent"] + + +class ResponseEvent(BaseModel): + """A streaming Responses API event from a backend delegated to by the Live session. + + Use the outer delegation_id to associate the nested stream with its Live delegation. + """ + + event: Dict[str, object] + """The nested Responses streaming event. + + Dispatch on its type field. Response lifecycle snapshots omit input and clear + instructions, tools, and output to keep messages small; consume granular output + events for the generated content. + """ + + event_id: str + """The unique ID of the Live server event.""" + + type: Literal["response.event"] + """The event type, always `response.event`.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ + + delegation_id: Optional[str] = None + """The Live delegation associated with the nested Responses event. + + May be null or omitted when the event cannot be correlated with a delegation. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/response_item_create_event.py b/venv/lib/python3.12/site-packages/openai/types/live/response_item_create_event.py new file mode 100644 index 0000000..aee309d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/response_item_create_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from ..responses.response_input_item import ResponseInputItem + +__all__ = ["ResponseItemCreateEvent"] + + +class ResponseItemCreateEvent(BaseModel): + """Add an input item to the Live session’s Responses backend. + + Requires Responses delegation; use `response.create` to request a response. + """ + + item: ResponseInputItem + """ + An input item to append to the Responses backend conversation, such as a user + message or a function tool result. + """ + + type: Literal["response.item.create"] + """The Live client event type. Always `response.item.create`.""" + + event_id: Optional[str] = None + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/response_item_create_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/response_item_create_event_param.py new file mode 100644 index 0000000..e409aa9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/response_item_create_event_param.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ..responses.response_input_item_param import ResponseInputItemParam + +__all__ = ["ResponseItemCreateEventParam"] + + +class ResponseItemCreateEventParam(TypedDict, total=False): + """Add an input item to the Live session’s Responses backend. + + Requires Responses delegation; use `response.create` to request a response. + """ + + item: Required[ResponseInputItemParam] + """ + An input item to append to the Responses backend conversation, such as a user + message or a function tool result. + """ + + type: Required[Literal["response.item.create"]] + """The Live client event type. Always `response.item.create`.""" + + event_id: Optional[str] + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/responses_delegation_config.py b/venv/lib/python3.12/site-packages/openai/types/live/responses_delegation_config.py new file mode 100644 index 0000000..1d8c548 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/responses_delegation_config.py @@ -0,0 +1,120 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .function_tool import FunctionTool + +__all__ = [ + "ResponsesDelegationConfig", + "Reasoning", + "Text", + "ToolChoice", + "ToolChoiceLiveFunctionToolChoiceParam", + "ToolChoiceLiveMCPToolChoiceParam", + "Tool", + "ToolWebSearch", +] + + +class Reasoning(BaseModel): + """Reasoning settings passed to each delegated Responses request.""" + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] = None + """How much reasoning effort the delegated Responses model should use. + + Supported values depend on the backend model. + """ + + summary: Optional[Literal["concise", "detailed", "auto"]] = None + """ + The reasoning summary to request from the delegated Responses model, when + supported. + """ + + +class Text(BaseModel): + """Text generation settings passed to each delegated Responses request.""" + + verbosity: Optional[Literal["low", "medium", "high"]] = None + """The amount of detail in text generated by the Responses backend. + + This does not configure the Live model’s spoken delivery. + """ + + +class ToolChoiceLiveFunctionToolChoiceParam(BaseModel): + name: str + + type: Literal["function"] + + +class ToolChoiceLiveMCPToolChoiceParam(BaseModel): + name: str + + server_label: str + + type: Literal["mcp"] + + +ToolChoice: TypeAlias = Union[ + Literal["auto", "none", "required"], ToolChoiceLiveFunctionToolChoiceParam, ToolChoiceLiveMCPToolChoiceParam +] + + +class ToolWebSearch(BaseModel): + """A web search tool available to the Live session’s Responses backend.""" + + type: Literal["web_search"] + """The tool type. Always `web_search`.""" + + +Tool: TypeAlias = Annotated[Union[FunctionTool, ToolWebSearch], PropertyInfo(discriminator="type")] + + +class ResponsesDelegationConfig(BaseModel): + """ + Model, prompt, and tool settings for tasks delegated by the Live session to a Responses backend. + """ + + model: str + """The model used for server-owned Responses delegations.""" + + instructions: Optional[str] = None + """Instructions for the delegated Responses model, separate from Live instructions. + + See + [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). + """ + + max_output_tokens: Optional[int] = None + """Maximum number of output tokens for each delegated response.""" + + parallel_tool_calls: Optional[bool] = None + """ + Whether the delegated Responses model may request multiple tool calls in a + single response. + """ + + reasoning: Optional[Reasoning] = None + """Reasoning settings passed to each delegated Responses request.""" + + service_tier: Optional[Literal["auto", "default", "fast_tier_temp_pilot", "flex", "priority", "ultrafast"]] = None + """Service tier for delegated Responses requests.""" + + text: Optional[Text] = None + """Text generation settings passed to each delegated Responses request.""" + + tool_choice: Optional[ToolChoice] = None + """ + Controls which tool the Responses backend uses when handling a task delegated by + the Live model. + """ + + tools: Optional[List[Tool]] = None + """ + Tools available to the Responses backend while it handles tasks delegated by the + Live model. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/responses_delegation_config_param.py b/venv/lib/python3.12/site-packages/openai/types/live/responses_delegation_config_param.py new file mode 100644 index 0000000..1e7e58b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/responses_delegation_config_param.py @@ -0,0 +1,120 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .function_tool_param import FunctionToolParam + +__all__ = [ + "ResponsesDelegationConfigParam", + "Reasoning", + "Text", + "ToolChoice", + "ToolChoiceLiveFunctionToolChoiceParam", + "ToolChoiceLiveMCPToolChoiceParam", + "Tool", + "ToolWebSearch", +] + + +class Reasoning(TypedDict, total=False): + """Reasoning settings passed to each delegated Responses request.""" + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] + """How much reasoning effort the delegated Responses model should use. + + Supported values depend on the backend model. + """ + + summary: Optional[Literal["concise", "detailed", "auto"]] + """ + The reasoning summary to request from the delegated Responses model, when + supported. + """ + + +class Text(TypedDict, total=False): + """Text generation settings passed to each delegated Responses request.""" + + verbosity: Optional[Literal["low", "medium", "high"]] + """The amount of detail in text generated by the Responses backend. + + This does not configure the Live model’s spoken delivery. + """ + + +class ToolChoiceLiveFunctionToolChoiceParam(TypedDict, total=False): + name: Required[str] + + type: Required[Literal["function"]] + + +class ToolChoiceLiveMCPToolChoiceParam(TypedDict, total=False): + name: Required[str] + + server_label: Required[str] + + type: Required[Literal["mcp"]] + + +ToolChoice: TypeAlias = Union[ + Literal["auto", "none", "required"], ToolChoiceLiveFunctionToolChoiceParam, ToolChoiceLiveMCPToolChoiceParam +] + + +class ToolWebSearch(TypedDict, total=False): + """A web search tool available to the Live session’s Responses backend.""" + + type: Required[Literal["web_search"]] + """The tool type. Always `web_search`.""" + + +Tool: TypeAlias = Union[FunctionToolParam, ToolWebSearch] + + +class ResponsesDelegationConfigParam(TypedDict, total=False): + """ + Model, prompt, and tool settings for tasks delegated by the Live session to a Responses backend. + """ + + model: Required[str] + """The model used for server-owned Responses delegations.""" + + instructions: Optional[str] + """Instructions for the delegated Responses model, separate from Live instructions. + + See + [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). + """ + + max_output_tokens: Optional[int] + """Maximum number of output tokens for each delegated response.""" + + parallel_tool_calls: Optional[bool] + """ + Whether the delegated Responses model may request multiple tool calls in a + single response. + """ + + reasoning: Optional[Reasoning] + """Reasoning settings passed to each delegated Responses request.""" + + service_tier: Optional[Literal["auto", "default", "fast_tier_temp_pilot", "flex", "priority", "ultrafast"]] + """Service tier for delegated Responses requests.""" + + text: Optional[Text] + """Text generation settings passed to each delegated Responses request.""" + + tool_choice: ToolChoice + """ + Controls which tool the Responses backend uses when handling a task delegated by + the Live model. + """ + + tools: Iterable[Tool] + """ + Tools available to the Responses backend while it handles tasks delegated by the + Live model. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/responses_delegation_update_config.py b/venv/lib/python3.12/site-packages/openai/types/live/responses_delegation_update_config.py new file mode 100644 index 0000000..03a01da --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/responses_delegation_update_config.py @@ -0,0 +1,124 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .function_tool import FunctionTool + +__all__ = [ + "ResponsesDelegationUpdateConfig", + "Reasoning", + "Text", + "ToolChoice", + "ToolChoiceLiveFunctionToolChoiceParam", + "ToolChoiceLiveMCPToolChoiceParam", + "Tool", + "ToolWebSearch", +] + + +class Reasoning(BaseModel): + """Reasoning settings passed to each delegated Responses request.""" + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] = None + """How much reasoning effort the delegated Responses model should use. + + Supported values depend on the backend model. + """ + + summary: Optional[Literal["concise", "detailed", "auto"]] = None + """ + The reasoning summary to request from the delegated Responses model, when + supported. + """ + + +class Text(BaseModel): + """Text generation settings passed to each delegated Responses request.""" + + verbosity: Optional[Literal["low", "medium", "high"]] = None + """The amount of detail in text generated by the Responses backend. + + This does not configure the Live model’s spoken delivery. + """ + + +class ToolChoiceLiveFunctionToolChoiceParam(BaseModel): + name: str + + type: Literal["function"] + + +class ToolChoiceLiveMCPToolChoiceParam(BaseModel): + name: str + + server_label: str + + type: Literal["mcp"] + + +ToolChoice: TypeAlias = Union[ + Literal["auto", "none", "required"], ToolChoiceLiveFunctionToolChoiceParam, ToolChoiceLiveMCPToolChoiceParam +] + + +class ToolWebSearch(BaseModel): + """A web search tool available to the Live session’s Responses backend.""" + + type: Literal["web_search"] + """The tool type. Always `web_search`.""" + + +Tool: TypeAlias = Annotated[Union[FunctionTool, ToolWebSearch], PropertyInfo(discriminator="type")] + + +class ResponsesDelegationUpdateConfig(BaseModel): + """Updates to the Responses backend of an existing Live session. + + Omitted settings retain their current values. + """ + + instructions: Optional[str] = None + """Instructions for the delegated Responses model, separate from Live instructions. + + See + [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). + """ + + max_output_tokens: Optional[int] = None + """Maximum number of output tokens for each delegated response.""" + + model: Optional[str] = None + """The Responses backend model to use for subsequent delegated requests. + + Omit to keep the current backend model. + """ + + parallel_tool_calls: Optional[bool] = None + """ + Whether the delegated Responses model may request multiple tool calls in a + single response. + """ + + reasoning: Optional[Reasoning] = None + """Reasoning settings passed to each delegated Responses request.""" + + service_tier: Optional[Literal["auto", "default", "fast_tier_temp_pilot", "flex", "priority", "ultrafast"]] = None + """Service tier for delegated Responses requests.""" + + text: Optional[Text] = None + """Text generation settings passed to each delegated Responses request.""" + + tool_choice: Optional[ToolChoice] = None + """ + Controls which tool the Responses backend uses when handling a task delegated by + the Live model. + """ + + tools: Optional[List[Tool]] = None + """ + Tools available to the Responses backend while it handles tasks delegated by the + Live model. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/responses_delegation_update_config_param.py b/venv/lib/python3.12/site-packages/openai/types/live/responses_delegation_update_config_param.py new file mode 100644 index 0000000..740691d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/responses_delegation_update_config_param.py @@ -0,0 +1,124 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .function_tool_param import FunctionToolParam + +__all__ = [ + "ResponsesDelegationUpdateConfigParam", + "Reasoning", + "Text", + "ToolChoice", + "ToolChoiceLiveFunctionToolChoiceParam", + "ToolChoiceLiveMCPToolChoiceParam", + "Tool", + "ToolWebSearch", +] + + +class Reasoning(TypedDict, total=False): + """Reasoning settings passed to each delegated Responses request.""" + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] + """How much reasoning effort the delegated Responses model should use. + + Supported values depend on the backend model. + """ + + summary: Optional[Literal["concise", "detailed", "auto"]] + """ + The reasoning summary to request from the delegated Responses model, when + supported. + """ + + +class Text(TypedDict, total=False): + """Text generation settings passed to each delegated Responses request.""" + + verbosity: Optional[Literal["low", "medium", "high"]] + """The amount of detail in text generated by the Responses backend. + + This does not configure the Live model’s spoken delivery. + """ + + +class ToolChoiceLiveFunctionToolChoiceParam(TypedDict, total=False): + name: Required[str] + + type: Required[Literal["function"]] + + +class ToolChoiceLiveMCPToolChoiceParam(TypedDict, total=False): + name: Required[str] + + server_label: Required[str] + + type: Required[Literal["mcp"]] + + +ToolChoice: TypeAlias = Union[ + Literal["auto", "none", "required"], ToolChoiceLiveFunctionToolChoiceParam, ToolChoiceLiveMCPToolChoiceParam +] + + +class ToolWebSearch(TypedDict, total=False): + """A web search tool available to the Live session’s Responses backend.""" + + type: Required[Literal["web_search"]] + """The tool type. Always `web_search`.""" + + +Tool: TypeAlias = Union[FunctionToolParam, ToolWebSearch] + + +class ResponsesDelegationUpdateConfigParam(TypedDict, total=False): + """Updates to the Responses backend of an existing Live session. + + Omitted settings retain their current values. + """ + + instructions: Optional[str] + """Instructions for the delegated Responses model, separate from Live instructions. + + See + [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). + """ + + max_output_tokens: Optional[int] + """Maximum number of output tokens for each delegated response.""" + + model: str + """The Responses backend model to use for subsequent delegated requests. + + Omit to keep the current backend model. + """ + + parallel_tool_calls: Optional[bool] + """ + Whether the delegated Responses model may request multiple tool calls in a + single response. + """ + + reasoning: Optional[Reasoning] + """Reasoning settings passed to each delegated Responses request.""" + + service_tier: Optional[Literal["auto", "default", "fast_tier_temp_pilot", "flex", "priority", "ultrafast"]] + """Service tier for delegated Responses requests.""" + + text: Optional[Text] + """Text generation settings passed to each delegated Responses request.""" + + tool_choice: ToolChoice + """ + Controls which tool the Responses backend uses when handling a task delegated by + the Live model. + """ + + tools: Iterable[Tool] + """ + Tools available to the Responses backend while it handles tasks delegated by the + Live model. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/server_event.py b/venv/lib/python3.12/site-packages/openai/types/live/server_event.py new file mode 100644 index 0000000..416dd51 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/server_event.py @@ -0,0 +1,161 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .info_event import InfoEvent +from .error_event import ErrorEvent +from .response_event import ResponseEvent +from .session_closed_event import SessionClosedEvent +from .session_started_event import SessionStartedEvent +from .session_updated_event import SessionUpdatedEvent +from .input_audio_muted_event import InputAudioMutedEvent +from .thinking_appended_event import ThinkingAppendedEvent +from .delegation_created_event import DelegationCreatedEvent +from .output_audio_delta_event import OutputAudioDeltaEvent +from .commentary_appended_event import CommentaryAppendedEvent +from .input_audio_unmuted_event import InputAudioUnmutedEvent +from .instructions_appended_event import InstructionsAppendedEvent +from .session_usage_updated_event import SessionUsageUpdatedEvent +from .input_transcript_delta_event import InputTranscriptDeltaEvent +from .output_transcript_delta_event import OutputTranscriptDeltaEvent + +__all__ = [ + "ServerEvent", + "SessionInputAudioAppend", + "TransportDtmfReceived", + "TransportDtmfSend", + "TransportRinging", + "TransportAnswered", + "TransportFailed", + "TransportFailedError", +] + + +class SessionInputAudioAppend(BaseModel): + """ + Input audio received from the primary transport and reflected to a Live sideband connection before model-input muting. + """ + + audio: str + """ + Base64-encoded raw mono PCM16LE at 24 kHz received from the primary transport, + reflected to the sideband before model-input muting. This server event uses the + same audio key as the client command, but is not an acknowledgment of it. + """ + + type: Literal["session.input_audio.append"] + """The event type, always `session.input_audio.append`.""" + + +class TransportDtmfReceived(BaseModel): + """A SIP DTMF keypress received from the caller. + + Delivered only to sideband observers. + """ + + event: str + + event_id: str + + type: Literal["transport.dtmf.received"] + + +class TransportDtmfSend(BaseModel): + """A SIP DTMF keypress successfully sent by the hosted tool. + + Delivered only to sideband observers; this is not a client command. + """ + + event: str + + event_id: str + + type: Literal["transport.dtmf.send"] + + +class TransportRinging(BaseModel): + """The outbound SIP provider leg is ringing or providing early media. + + Delivered only to sideband observers. + """ + + event_id: str + + session_id: str + """The canonical Live session ID.""" + + type: Literal["transport.ringing"] + + +class TransportAnswered(BaseModel): + """The outbound SIP provider leg answered and media is established. + + Delivered only to sideband observers. + """ + + event_id: str + + session_id: str + """The canonical Live session ID.""" + + type: Literal["transport.answered"] + + +class TransportFailedError(BaseModel): + code: str + """The call setup failure code.""" + + message: str + + type: Literal["call_error"] + + param: Optional[str] = None + """The parameter related to the error, if any. Empty when no parameter applies.""" + + +class TransportFailed(BaseModel): + """An asynchronous outbound SIP setup failure. + + Delivered only to sideband observers. + """ + + error: TransportFailedError + + event_id: str + + session_id: str + """The canonical Live session ID.""" + + type: Literal["transport.failed"] + + +ServerEvent: TypeAlias = Annotated[ + Union[ + SessionStartedEvent, + SessionUpdatedEvent, + InputAudioMutedEvent, + InputAudioUnmutedEvent, + InstructionsAppendedEvent, + ThinkingAppendedEvent, + CommentaryAppendedEvent, + SessionInputAudioAppend, + OutputAudioDeltaEvent, + InputTranscriptDeltaEvent, + OutputTranscriptDeltaEvent, + DelegationCreatedEvent, + ResponseEvent, + SessionUsageUpdatedEvent, + SessionClosedEvent, + ErrorEvent, + InfoEvent, + TransportDtmfReceived, + TransportDtmfSend, + TransportRinging, + TransportAnswered, + TransportFailed, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/live/server_event_selector.py b/venv/lib/python3.12/site-packages/openai/types/live/server_event_selector.py new file mode 100644 index 0000000..528f647 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/server_event_selector.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["ServerEventSelector"] + + +class ServerEventSelector(BaseModel): + """A Live server event selector for the WebRTC frontend data channel.""" + + type: str + """The outer Live server event type. Use 'response.event' for Responses events.""" + + response_event: Optional[str] = None + """The nested Responses event type. + + Required when type is 'response.event'; forbidden for other event types. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/server_event_selector_param.py b/venv/lib/python3.12/site-packages/openai/types/live/server_event_selector_param.py new file mode 100644 index 0000000..20c6b3f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/server_event_selector_param.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ServerEventSelectorParam"] + + +class ServerEventSelectorParam(TypedDict, total=False): + """A Live server event selector for the WebRTC frontend data channel.""" + + type: Required[str] + """The outer Live server event type. Use 'response.event' for Responses events.""" + + response_event: str + """The nested Responses event type. + + Required when type is 'response.event'; forbidden for other event types. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_accept_params.py b/venv/lib/python3.12/site-packages/openai/types/live/session_accept_params.py new file mode 100644 index 0000000..96946c4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_accept_params.py @@ -0,0 +1,128 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .built_in_voice import BuiltInVoice +from .custom_voice_param import CustomVoiceParam +from .initial_item_param import InitialItemParam +from .client_delegation_param import ClientDelegationParam +from .responses_delegation_config_param import ResponsesDelegationConfigParam + +__all__ = [ + "SessionAcceptParams", + "Session", + "SessionAudio", + "SessionAudioOutput", + "SessionAudioOutputVoice", + "SessionDelegation", + "SessionDelegationResponses", +] + + +class SessionAcceptParams(TypedDict, total=False): + session: Required[Session] + """ + Model and startup configuration for the Live session that answers the incoming + SIP call. + """ + + +SessionAudioOutputVoice: TypeAlias = Union[str, BuiltInVoice, CustomVoiceParam] + + +class SessionAudioOutput(TypedDict, total=False): + """Settings for speech generated by the Live model. + + Choose the voice before starting the session. + """ + + voice: SessionAudioOutputVoice + """ + The voice used for Live speech, as a built-in voice name or a custom voice + object containing its ID. Defaults to `marin` and cannot change after startup. + """ + + +class SessionAudio(TypedDict, total=False): + """Startup audio output configuration. + + SIP negotiates the media format; audio.format is only accepted for primary WebSockets. Voice cannot change after startup. + """ + + output: SessionAudioOutput + """Settings for speech generated by the Live model. + + Choose the voice before starting the session. + """ + + +class SessionDelegationResponses(TypedDict, total=False): + """Delegate tasks to a Responses model managed by the Live session.""" + + responses: Required[ResponsesDelegationConfigParam] + """ + Backend model, prompt, and tools used when the Live session delegates a task to + Responses. + """ + + type: Required[Literal["responses"]] + """The delegation owner. + + Always `responses` for tasks handled by the Responses API. + """ + + +SessionDelegation: TypeAlias = Union[ClientDelegationParam, SessionDelegationResponses] + + +class Session(TypedDict, total=False): + """ + Model and startup configuration for the Live session that answers the incoming SIP call. + """ + + model: Required[Union[str, Literal["gpt-live-1"]]] + """The Live model to use for the accepted call.""" + + type: Required[Literal["live"]] + """The session type. Always `live`.""" + + audio: SessionAudio + """Startup audio output configuration. + + SIP negotiates the media format; audio.format is only accepted for primary + WebSockets. Voice cannot change after startup. + """ + + delegation: Optional[SessionDelegation] + """Who handles tasks delegated by the Live model. + + Omitted or null selects your application; use `responses` to let the API manage + a Responses backend. + """ + + input: Iterable[InitialItemParam] + """Ordered text-only history supplied before startup. + + Supports developer, user, and assistant messages with one text part each; at + most 128 messages and 8,192 rendered tokens in total. + """ + + instructions: Optional[str] + """ + Frontend instructions for voice, conversation, interruptions, and when to + delegate. Start with the + [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting); + put business rules and tool workflows in a separate + [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). + Limited to 16,384 client-supplied tokens. Omitted or blank instructions use + server defaults. Immutable after startup. + """ + + store: bool + """Whether to store the session for later forking and recording download. + + Defaults to false for new sessions. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_close_event.py b/venv/lib/python3.12/site-packages/openai/types/live/session_close_event.py new file mode 100644 index 0000000..e301e90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_close_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SessionCloseEvent"] + + +class SessionCloseEvent(BaseModel): + """Request that the Live session close. + + The terminal `session.closed` event contains the close reason and final usage. + """ + + type: Literal["session.close"] + """The Live client event type. Always `session.close`.""" + + event_id: Optional[str] = None + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_close_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/session_close_event_param.py new file mode 100644 index 0000000..bbefae6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_close_event_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["SessionCloseEventParam"] + + +class SessionCloseEventParam(TypedDict, total=False): + """Request that the Live session close. + + The terminal `session.closed` event contains the close reason and final usage. + """ + + type: Required[Literal["session.close"]] + """The Live client event type. Always `session.close`.""" + + event_id: Optional[str] + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_closed_event.py b/venv/lib/python3.12/site-packages/openai/types/live/session_closed_event.py new file mode 100644 index 0000000..958eb45 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_closed_event.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .session_usage import SessionUsage +from .session_resource import SessionResource + +__all__ = ["SessionClosedEvent"] + + +class SessionClosedEvent(BaseModel): + """ + Returned after the Live session finishes finalizing, with the close reason, final session snapshot, and cumulative audio usage. A connection closing without this event does not confirm successful finalization. + """ + + event_id: str + """The unique ID of the Live server event.""" + + reason: Literal["close_requested", "expired", "content", "remote_hangup", "connection_lost"] + """ + Why the Live session ended: `close_requested` for an application close or hangup + request, `expired` for the session duration limit, `content` for a safety + filter, `remote_hangup` for a graceful remote disconnect, or `connection_lost` + for an unexpected primary or upstream disconnection. + """ + + session: SessionResource + """The resolved Live session configuration and server-assigned session metadata.""" + + type: Literal["session.closed"] + """The event type, always `session.closed`.""" + + usage: SessionUsage + """The final cumulative Live audio usage after session finalization.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_config.py b/venv/lib/python3.12/site-packages/openai/types/live/session_config.py new file mode 100644 index 0000000..8d4c8f3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_config.py @@ -0,0 +1,122 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .audio_format import AudioFormat +from .custom_voice import CustomVoice +from .initial_item import InitialItem +from .client_config import ClientConfig +from .built_in_voice import BuiltInVoice +from .client_delegation import ClientDelegation +from .responses_delegation_config import ResponsesDelegationConfig + +__all__ = ["SessionConfig", "Audio", "AudioOutput", "AudioOutputVoice", "Delegation", "DelegationResponses"] + +AudioOutputVoice: TypeAlias = Union[str, BuiltInVoice, CustomVoice] + + +class AudioOutput(BaseModel): + """The voice used for speech generated by the Live model.""" + + voice: Optional[AudioOutputVoice] = None + """ + The voice used for Live speech, as a built-in voice name or a custom voice + object containing its ID. Defaults to `marin` and cannot change after startup. + """ + + +class Audio(BaseModel): + """Startup audio configuration. + + Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their media format. Voice and format are immutable after startup. + """ + + format: Optional[AudioFormat] = None + """ + Audio encoding and sample rate for audio sent and received over a Live WebSocket + connection. WebRTC and SIP negotiate their media format separately. + """ + + output: Optional[AudioOutput] = None + """The voice used for speech generated by the Live model.""" + + +class DelegationResponses(BaseModel): + """Delegate tasks to a Responses model managed by the Live session.""" + + responses: ResponsesDelegationConfig + """ + Backend model, prompt, and tools used when the Live session delegates a task to + Responses. + """ + + type: Literal["responses"] + """The delegation owner. + + Always `responses` for tasks handled by the Responses API. + """ + + +Delegation: TypeAlias = Annotated[ + Union[ClientDelegation, DelegationResponses, None], PropertyInfo(discriminator="type") +] + + +class SessionConfig(BaseModel): + """ + Initial configuration for a Live session, including its model, conversation instructions, audio, and delegated task handling. + """ + + model: Union[str, Literal["gpt-live-1"]] + """The Live model. + + Required in the session configuration for every transport; do not pass it as a + URL query parameter. + """ + + audio: Optional[Audio] = None + """Startup audio configuration. + + Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their + media format. Voice and format are immutable after startup. + """ + + client: Optional[ClientConfig] = None + """ + Startup-only capabilities for an untrusted frontend attached to a unified WebRTC + session. Trusted sideband connections are unaffected. + """ + + delegation: Optional[Delegation] = None + """Who handles tasks delegated by the Live model. + + Omitted or null selects your application; use `responses` to let the API manage + a Responses backend. + """ + + input: Optional[List[InitialItem]] = None + """Ordered text-only history supplied before startup. + + Supports developer, user, and assistant messages with one text part each; at + most 128 messages and 8,192 rendered tokens in total. + """ + + instructions: Optional[str] = None + """ + Frontend instructions for voice, conversation, interruptions, and when to + delegate. Start with the + [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting); + put business rules and tool workflows in a separate + [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). + Limited to 16,384 client-supplied tokens. Omitted or blank instructions use + server defaults. Immutable after startup. + """ + + store: Optional[bool] = None + """Whether to store the session for later forking and recording download. + + Defaults to false for new sessions. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_config_param.py b/venv/lib/python3.12/site-packages/openai/types/live/session_config_param.py new file mode 100644 index 0000000..2d68b77 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_config_param.py @@ -0,0 +1,120 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .built_in_voice import BuiltInVoice +from .audio_format_param import AudioFormatParam +from .custom_voice_param import CustomVoiceParam +from .initial_item_param import InitialItemParam +from .client_config_param import ClientConfigParam +from .client_delegation_param import ClientDelegationParam +from .responses_delegation_config_param import ResponsesDelegationConfigParam + +__all__ = ["SessionConfigParam", "Audio", "AudioOutput", "AudioOutputVoice", "Delegation", "DelegationResponses"] + +AudioOutputVoice: TypeAlias = Union[str, BuiltInVoice, CustomVoiceParam] + + +class AudioOutput(TypedDict, total=False): + """The voice used for speech generated by the Live model.""" + + voice: AudioOutputVoice + """ + The voice used for Live speech, as a built-in voice name or a custom voice + object containing its ID. Defaults to `marin` and cannot change after startup. + """ + + +class Audio(TypedDict, total=False): + """Startup audio configuration. + + Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their media format. Voice and format are immutable after startup. + """ + + format: AudioFormatParam + """ + Audio encoding and sample rate for audio sent and received over a Live WebSocket + connection. WebRTC and SIP negotiate their media format separately. + """ + + output: AudioOutput + """The voice used for speech generated by the Live model.""" + + +class DelegationResponses(TypedDict, total=False): + """Delegate tasks to a Responses model managed by the Live session.""" + + responses: Required[ResponsesDelegationConfigParam] + """ + Backend model, prompt, and tools used when the Live session delegates a task to + Responses. + """ + + type: Required[Literal["responses"]] + """The delegation owner. + + Always `responses` for tasks handled by the Responses API. + """ + + +Delegation: TypeAlias = Union[ClientDelegationParam, DelegationResponses] + + +class SessionConfigParam(TypedDict, total=False): + """ + Initial configuration for a Live session, including its model, conversation instructions, audio, and delegated task handling. + """ + + model: Required[Union[str, Literal["gpt-live-1"]]] + """The Live model. + + Required in the session configuration for every transport; do not pass it as a + URL query parameter. + """ + + audio: Audio + """Startup audio configuration. + + Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their + media format. Voice and format are immutable after startup. + """ + + client: ClientConfigParam + """ + Startup-only capabilities for an untrusted frontend attached to a unified WebRTC + session. Trusted sideband connections are unaffected. + """ + + delegation: Optional[Delegation] + """Who handles tasks delegated by the Live model. + + Omitted or null selects your application; use `responses` to let the API manage + a Responses backend. + """ + + input: Iterable[InitialItemParam] + """Ordered text-only history supplied before startup. + + Supports developer, user, and assistant messages with one text part each; at + most 128 messages and 8,192 rendered tokens in total. + """ + + instructions: Optional[str] + """ + Frontend instructions for voice, conversation, interruptions, and when to + delegate. Start with the + [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting); + put business rules and tool workflows in a separate + [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). + Limited to 16,384 client-supplied tokens. Omitted or blank instructions use + server defaults. Immutable after startup. + """ + + store: bool + """Whether to store the session for later forking and recording download. + + Defaults to false for new sessions. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_fork_params.py b/venv/lib/python3.12/site-packages/openai/types/live/session_fork_params.py new file mode 100644 index 0000000..dd823a1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_fork_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .media_session_fork_config_param import MediaSessionForkConfigParam + +__all__ = ["SessionForkParams", "Transport"] + + +class SessionForkParams(TypedDict, total=False): + transport: Required[Transport] + """ + WebRTC transport with an SDP offer for the new connection to the forked session. + """ + + session: MediaSessionForkConfigParam + """Optional configuration overrides for the new Live session. + + Omit this object or send an empty object to inherit the stored session's + settings. + """ + + +class Transport(TypedDict, total=False): + """ + WebRTC transport with an SDP offer for the new connection to the forked session. + """ + + sdp: Required[str] + """Session Description Protocol message for the WebRTC connection.""" + + type: Required[Literal["webrtc"]] + """The transport used for the Live session. Always `webrtc`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_fork_response.py b/venv/lib/python3.12/site-packages/openai/types/live/session_fork_response.py new file mode 100644 index 0000000..3b30547 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_fork_response.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SessionForkResponse", "Session", "Transport"] + + +class Session(BaseModel): + """The newly created Live session. + + Use its ID for session controls and sideband connections. + """ + + id: str + """Opaque session identifier. + + Preserve the returned value unchanged, including its prefix. + """ + + +class Transport(BaseModel): + """WebRTC transport with the SDP answer.""" + + sdp: str + """Session Description Protocol message for the WebRTC connection.""" + + type: Literal["webrtc"] + """The transport used for the Live session. Always `webrtc`.""" + + +class SessionForkResponse(BaseModel): + """The created Live session identifier and WebRTC answer. + + Apply transport.sdp as the peer's remote answer and wait for session.started on the data channel before sending commands. + """ + + session: Session + """The newly created Live session. + + Use its ID for session controls and sideband connections. + """ + + transport: Transport + """WebRTC transport with the SDP answer.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_refer_params.py b/venv/lib/python3.12/site-packages/openai/types/live/session_refer_params.py new file mode 100644 index 0000000..b7025ea --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_refer_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["SessionReferParams"] + + +class SessionReferParams(TypedDict, total=False): + target_uri: Required[str] + """ + Nonblank URI for the SIP Refer-To header, such as tel:+14155550123 or + sip:agent@example.com. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_reject_params.py b/venv/lib/python3.12/site-packages/openai/types/live/session_reject_params.py new file mode 100644 index 0000000..90ab4e8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_reject_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["SessionRejectParams"] + + +class SessionRejectParams(TypedDict, total=False): + status_code: Required[int] + """SIP rejection status sent to the caller. This field is required.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_resource.py b/venv/lib/python3.12/site-packages/openai/types/live/session_resource.py new file mode 100644 index 0000000..e2937f0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_resource.py @@ -0,0 +1,136 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .audio_format import AudioFormat +from .custom_voice import CustomVoice +from .initial_item import InitialItem +from .client_config import ClientConfig +from .built_in_voice import BuiltInVoice +from .client_delegation import ClientDelegation +from .responses_delegation_config import ResponsesDelegationConfig + +__all__ = ["SessionResource", "Audio", "AudioOutput", "AudioOutputVoice", "Delegation", "DelegationResponses"] + +AudioOutputVoice: TypeAlias = Union[str, BuiltInVoice, CustomVoice] + + +class AudioOutput(BaseModel): + """The voice used for speech generated by the Live model.""" + + voice: Optional[AudioOutputVoice] = None + """ + The voice used for Live speech, as a built-in voice name or a custom voice + object containing its ID. Defaults to `marin` and cannot change after startup. + """ + + +class Audio(BaseModel): + """Startup audio configuration. + + Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their media format. Voice and format are immutable after startup. + """ + + format: Optional[AudioFormat] = None + """ + Audio encoding and sample rate for audio sent and received over a Live WebSocket + connection. WebRTC and SIP negotiate their media format separately. + """ + + output: Optional[AudioOutput] = None + """The voice used for speech generated by the Live model.""" + + +class DelegationResponses(BaseModel): + """Delegate tasks to a Responses model managed by the Live session.""" + + responses: ResponsesDelegationConfig + """ + Backend model, prompt, and tools used when the Live session delegates a task to + Responses. + """ + + type: Literal["responses"] + """The delegation owner. + + Always `responses` for tasks handled by the Responses API. + """ + + +Delegation: TypeAlias = Annotated[ + Union[ClientDelegation, DelegationResponses, None], PropertyInfo(discriminator="type") +] + + +class SessionResource(BaseModel): + """The resolved Live session configuration and server-assigned session metadata.""" + + id: str + """The unique ID of the Live session. + + Use this ID for sideband connections, forking, and recording download. + """ + + expires_at: int + """The Unix timestamp, in seconds, at which the Live session expires.""" + + model: Union[str, Literal["gpt-live-1"]] + """The Live model. + + Required in the session configuration for every transport; do not pass it as a + URL query parameter. + """ + + status: Literal["active"] + """The status of the session snapshot. + + Always `active`, including the final snapshot in session.closed; use the event + type to determine that the session has closed. + """ + + audio: Optional[Audio] = None + """Startup audio configuration. + + Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their + media format. Voice and format are immutable after startup. + """ + + client: Optional[ClientConfig] = None + """ + Startup-only capabilities for an untrusted frontend attached to a unified WebRTC + session. Trusted sideband connections are unaffected. + """ + + delegation: Optional[Delegation] = None + """Who handles tasks delegated by the Live model. + + Omitted or null selects your application; use `responses` to let the API manage + a Responses backend. + """ + + input: Optional[List[InitialItem]] = None + """Ordered text-only history supplied before startup. + + Supports developer, user, and assistant messages with one text part each; at + most 128 messages and 8,192 rendered tokens in total. + """ + + instructions: Optional[str] = None + """ + Frontend instructions for voice, conversation, interruptions, and when to + delegate. Start with the + [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting); + put business rules and tool workflows in a separate + [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). + Limited to 16,384 client-supplied tokens. Omitted or blank instructions use + server defaults. Immutable after startup. + """ + + store: Optional[bool] = None + """Whether to store the session for later forking and recording download. + + Defaults to false for new sessions. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_start_event.py b/venv/lib/python3.12/site-packages/openai/types/live/session_start_event.py new file mode 100644 index 0000000..1911d44 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_start_event.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .session_config import SessionConfig + +__all__ = ["SessionStartEvent"] + + +class SessionStartEvent(BaseModel): + """Start a Live session on a primary WebSocket. + + Send this event before other commands and wait for `session.started`. + """ + + session: SessionConfig + """Initial configuration for a primary WebSocket. + + Send session.start first and wait for session.started before application + commands. WebRTC creation already starts the session; do not send this event + again on its data channel. + """ + + type: Literal["session.start"] + """The Live client event type. Always `session.start`.""" + + event_id: Optional[str] = None + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_start_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/session_start_event_param.py new file mode 100644 index 0000000..0cae186 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_start_event_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from .session_config_param import SessionConfigParam + +__all__ = ["SessionStartEventParam"] + + +class SessionStartEventParam(TypedDict, total=False): + """Start a Live session on a primary WebSocket. + + Send this event before other commands and wait for `session.started`. + """ + + session: Required[SessionConfigParam] + """Initial configuration for a primary WebSocket. + + Send session.start first and wait for session.started before application + commands. WebRTC creation already starts the session; do not send this event + again on its data channel. + """ + + type: Required[Literal["session.start"]] + """The Live client event type. Always `session.start`.""" + + event_id: Optional[str] + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_started_event.py b/venv/lib/python3.12/site-packages/openai/types/live/session_started_event.py new file mode 100644 index 0000000..5f090ca --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_started_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .session_resource import SessionResource + +__all__ = ["SessionStartedEvent"] + + +class SessionStartedEvent(BaseModel): + """Returned when a Live session has started. + + Contains the resolved session configuration, including server defaults. + """ + + event_id: str + """The unique ID of the Live server event.""" + + session: SessionResource + """The resolved Live session configuration and server-assigned session metadata.""" + + type: Literal["session.started"] + """The event type, always `session.started`.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_update_config.py b/venv/lib/python3.12/site-packages/openai/types/live/session_update_config.py new file mode 100644 index 0000000..788bcc4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_update_config.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .client_delegation import ClientDelegation +from .responses_delegation_update_config import ResponsesDelegationUpdateConfig + +__all__ = ["SessionUpdateConfig", "Delegation", "DelegationResponses"] + + +class DelegationResponses(BaseModel): + """ + Update the Responses backend for an existing Live session without changing delegation ownership. + """ + + type: Literal["responses"] + """The delegation owner. + + Always `responses` for tasks handled by the Responses API. + """ + + responses: Optional[ResponsesDelegationUpdateConfig] = None + """Responses backend settings to update. + + Omitted settings keep their existing values. + """ + + +Delegation: TypeAlias = Annotated[ + Union[ClientDelegation, DelegationResponses, None], PropertyInfo(discriminator="type") +] + + +class SessionUpdateConfig(BaseModel): + """Changes to an active Live session. + + Only delegation backend settings can be updated after startup. + """ + + delegation: Optional[Delegation] = None + """Delegation settings to update. + + The delegation type must match the current session; omitted settings retain + their values. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_update_config_param.py b/venv/lib/python3.12/site-packages/openai/types/live/session_update_config_param.py new file mode 100644 index 0000000..575cc60 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_update_config_param.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .client_delegation_param import ClientDelegationParam +from .responses_delegation_update_config_param import ResponsesDelegationUpdateConfigParam + +__all__ = ["SessionUpdateConfigParam", "Delegation", "DelegationResponses"] + + +class DelegationResponses(TypedDict, total=False): + """ + Update the Responses backend for an existing Live session without changing delegation ownership. + """ + + type: Required[Literal["responses"]] + """The delegation owner. + + Always `responses` for tasks handled by the Responses API. + """ + + responses: ResponsesDelegationUpdateConfigParam + """Responses backend settings to update. + + Omitted settings keep their existing values. + """ + + +Delegation: TypeAlias = Union[ClientDelegationParam, DelegationResponses] + + +class SessionUpdateConfigParam(TypedDict, total=False): + """Changes to an active Live session. + + Only delegation backend settings can be updated after startup. + """ + + delegation: Optional[Delegation] + """Delegation settings to update. + + The delegation type must match the current session; omitted settings retain + their values. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_update_event.py b/venv/lib/python3.12/site-packages/openai/types/live/session_update_event.py new file mode 100644 index 0000000..9fee82a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_update_event.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .session_update_config import SessionUpdateConfig + +__all__ = ["SessionUpdateEvent"] + + +class SessionUpdateEvent(BaseModel): + """Update the delegation settings of an active Live session. + + The server acknowledges accepted changes with `session.updated`. + """ + + session: SessionUpdateConfig + """Sparse delegation updates. + + Omitted settings retain their values. The delegation type cannot change, + including resetting Responses delegation to null or client. Model, frontend + instructions, audio, and startup input are immutable. + """ + + type: Literal["session.update"] + """The Live client event type. Always `session.update`.""" + + event_id: Optional[str] = None + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_update_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/session_update_event_param.py new file mode 100644 index 0000000..d72c970 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_update_event_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from .session_update_config_param import SessionUpdateConfigParam + +__all__ = ["SessionUpdateEventParam"] + + +class SessionUpdateEventParam(TypedDict, total=False): + """Update the delegation settings of an active Live session. + + The server acknowledges accepted changes with `session.updated`. + """ + + session: Required[SessionUpdateConfigParam] + """Sparse delegation updates. + + Omitted settings retain their values. The delegation type cannot change, + including resetting Responses delegation to null or client. Model, frontend + instructions, audio, and startup input are immutable. + """ + + type: Required[Literal["session.update"]] + """The Live client event type. Always `session.update`.""" + + event_id: Optional[str] + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_updated_event.py b/venv/lib/python3.12/site-packages/openai/types/live/session_updated_event.py new file mode 100644 index 0000000..b408d29 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_updated_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .session_resource import SessionResource + +__all__ = ["SessionUpdatedEvent"] + + +class SessionUpdatedEvent(BaseModel): + """Returned when a Live session update is accepted. + + Contains the resolved session configuration after the update. + """ + + event_id: str + """The unique ID of the Live server event.""" + + session: SessionResource + """The resolved Live session configuration and server-assigned session metadata.""" + + type: Literal["session.updated"] + """The event type, always `session.updated`.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_usage.py b/venv/lib/python3.12/site-packages/openai/types/live/session_usage.py new file mode 100644 index 0000000..bd6e1d3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_usage.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["SessionUsage"] + + +class SessionUsage(BaseModel): + """Cumulative audio duration for a Live session. + + Values are totals for the session, not increments to sum across usage events. + """ + + seconds: float + """The cumulative Live audio duration in seconds. + + Do not sum this value across usage events. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/session_usage_updated_event.py b/venv/lib/python3.12/site-packages/openai/types/live/session_usage_updated_event.py new file mode 100644 index 0000000..abae9ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/session_usage_updated_event.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .session_usage import SessionUsage + +__all__ = ["SessionUsageUpdatedEvent", "ContextWindow"] + + +class ContextWindow(BaseModel): + """The latest measured Live context-window usage. + + Omitted when the context limit is unknown. + """ + + usage_ratio: float + """The latest active context token count divided by the Live model context limit. + + Can decrease after compaction and may lag between measured audio frames. + """ + + +class SessionUsageUpdatedEvent(BaseModel): + """ + Reports cumulative Live audio usage and, when available, the most recent context-window usage. Delegated Responses token usage is reported separately in response.event events. + """ + + event_id: str + """The unique ID of the Live server event.""" + + type: Literal["session.usage.updated"] + """The event type, always `session.usage.updated`.""" + + usage: SessionUsage + """The cumulative Live audio usage so far.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ + + context_window: Optional[ContextWindow] = None + """The latest measured Live context-window usage. + + Omitted when the context limit is unknown. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/sideband_connect_params.py b/venv/lib/python3.12/site-packages/openai/types/live/sideband_connect_params.py new file mode 100644 index 0000000..9f5a314 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/sideband_connect_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SidebandConnectParams"] + + +class SidebandConnectParams(TypedDict, total=False): + graceful_close: bool + """Opt in to the graceful WebSocket closing handshake when the session ends. + + The server may also enable this behavior by default. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/thinking_append_event.py b/venv/lib/python3.12/site-packages/openai/types/live/thinking_append_event.py new file mode 100644 index 0000000..3caf259 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/thinking_append_event.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ThinkingAppendEvent"] + + +class ThinkingAppendEvent(BaseModel): + """ + Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation. + """ + + content: str + """Silent reasoning or progress context, limited to 500 tokens. + + It does not directly request speech, but can influence later speech and is not a + secrecy boundary. + """ + + delegation_id: Optional[str] = None + """Required, nullable. + + Set null for general session context, or use the ID from + session.delegation.created for an existing client delegation. Non-null IDs are + not accepted with Responses delegation. + """ + + type: Literal["session.thinking.append"] + """The Live client event type. Always `session.thinking.append`.""" + + event_id: Optional[str] = None + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/thinking_append_event_param.py b/venv/lib/python3.12/site-packages/openai/types/live/thinking_append_event_param.py new file mode 100644 index 0000000..ca94550 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/thinking_append_event_param.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ThinkingAppendEventParam"] + + +class ThinkingAppendEventParam(TypedDict, total=False): + """ + Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation. + """ + + content: Required[str] + """Silent reasoning or progress context, limited to 500 tokens. + + It does not directly request speech, but can influence later speech and is not a + secrecy boundary. + """ + + delegation_id: Required[Optional[str]] + """Required, nullable. + + Set null for general session context, or use the ID from + session.delegation.created for an existing client delegation. Non-null IDs are + not accepted with Responses delegation. + """ + + type: Required[Literal["session.thinking.append"]] + """The Live client event type. Always `session.thinking.append`.""" + + event_id: Optional[str] + """ + Optional client identifier for correlating this command with a server event's + client_event_id or error.client_event_id. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/live/thinking_appended_event.py b/venv/lib/python3.12/site-packages/openai/types/live/thinking_appended_event.py new file mode 100644 index 0000000..14f19e5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/live/thinking_appended_event.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ThinkingAppendedEvent"] + + +class ThinkingAppendedEvent(BaseModel): + """ + Returned when a session.thinking.append command is accepted into the Live session timeline. Acknowledges the added reasoning context without guaranteeing any spoken output. + """ + + end_ms: int + """ + The end of this event on the Live session timeline, in milliseconds from the + beginning of the session. For appended context, this can equal start_ms. + """ + + event_id: str + """The unique ID of the Live server event.""" + + start_ms: int + """ + The start of this event on the Live session timeline, in milliseconds from the + beginning of the session. + """ + + type: Literal["session.thinking.appended"] + """The event type, always `session.thinking.appended`.""" + + client_event_id: Optional[str] = None + """ + The event_id of the client command associated with this server event, when + supplied. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/model.py b/venv/lib/python3.12/site-packages/openai/types/model.py new file mode 100644 index 0000000..a78bdd4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/model.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["Model"] + + +class Model(BaseModel): + """Describes an OpenAI model offering that can be used with the API.""" + + id: str + """The model identifier, which can be referenced in the API endpoints.""" + + created: int + """The Unix timestamp (in seconds) when the model was created.""" + + object: Literal["model"] + """The object type, which is always "model".""" + + owned_by: str + """The organization that owns the model.""" + + shutdown_date: Optional[str] = None + """The date when the model will shut down, or null if not announced.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/model_deleted.py b/venv/lib/python3.12/site-packages/openai/types/model_deleted.py new file mode 100644 index 0000000..50e7c74 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/model_deleted.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["ModelDeleted"] + + +class ModelDeleted(BaseModel): + id: str + + deleted: bool + + object: str diff --git a/venv/lib/python3.12/site-packages/openai/types/moderation.py b/venv/lib/python3.12/site-packages/openai/types/moderation.py new file mode 100644 index 0000000..e3d9d00 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/moderation.py @@ -0,0 +1,194 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["Moderation", "Categories", "CategoryAppliedInputTypes", "CategoryScores"] + + +class Categories(BaseModel): + """A list of the categories, and whether they are flagged or not.""" + + harassment: bool + """ + Content that expresses, incites, or promotes harassing language towards any + target. + """ + + harassment_threatening: bool = FieldInfo(alias="harassment/threatening") + """ + Harassment content that also includes violence or serious harm towards any + target. + """ + + hate: bool + """ + Content that expresses, incites, or promotes hate based on race, gender, + ethnicity, religion, nationality, sexual orientation, disability status, or + caste. Hateful content aimed at non-protected groups (e.g., chess players) is + harassment. + """ + + hate_threatening: bool = FieldInfo(alias="hate/threatening") + """ + Hateful content that also includes violence or serious harm towards the targeted + group based on race, gender, ethnicity, religion, nationality, sexual + orientation, disability status, or caste. + """ + + illicit: Optional[bool] = None + """ + Content that includes instructions or advice that facilitate the planning or + execution of wrongdoing, or that gives advice or instruction on how to commit + illicit acts. For example, "how to shoplift" would fit this category. + """ + + illicit_violent: Optional[bool] = FieldInfo(alias="illicit/violent", default=None) + """ + Content that includes instructions or advice that facilitate the planning or + execution of wrongdoing that also includes violence, or that gives advice or + instruction on the procurement of any weapon. + """ + + self_harm: bool = FieldInfo(alias="self-harm") + """ + Content that promotes, encourages, or depicts acts of self-harm, such as + suicide, cutting, and eating disorders. + """ + + self_harm_instructions: bool = FieldInfo(alias="self-harm/instructions") + """ + Content that encourages performing acts of self-harm, such as suicide, cutting, + and eating disorders, or that gives instructions or advice on how to commit such + acts. + """ + + self_harm_intent: bool = FieldInfo(alias="self-harm/intent") + """ + Content where the speaker expresses that they are engaging or intend to engage + in acts of self-harm, such as suicide, cutting, and eating disorders. + """ + + sexual: bool + """ + Content meant to arouse sexual excitement, such as the description of sexual + activity, or that promotes sexual services (excluding sex education and + wellness). + """ + + sexual_minors: bool = FieldInfo(alias="sexual/minors") + """Sexual content that includes an individual who is under 18 years old.""" + + violence: bool + """Content that depicts death, violence, or physical injury.""" + + violence_graphic: bool = FieldInfo(alias="violence/graphic") + """Content that depicts death, violence, or physical injury in graphic detail.""" + + +class CategoryAppliedInputTypes(BaseModel): + """ + A list of the categories along with the input type(s) that the score applies to. + """ + + harassment: List[Literal["text"]] + """The applied input type(s) for the category 'harassment'.""" + + harassment_threatening: List[Literal["text"]] = FieldInfo(alias="harassment/threatening") + """The applied input type(s) for the category 'harassment/threatening'.""" + + hate: List[Literal["text"]] + """The applied input type(s) for the category 'hate'.""" + + hate_threatening: List[Literal["text"]] = FieldInfo(alias="hate/threatening") + """The applied input type(s) for the category 'hate/threatening'.""" + + illicit: List[Literal["text"]] + """The applied input type(s) for the category 'illicit'.""" + + illicit_violent: List[Literal["text"]] = FieldInfo(alias="illicit/violent") + """The applied input type(s) for the category 'illicit/violent'.""" + + self_harm: List[Literal["text", "image"]] = FieldInfo(alias="self-harm") + """The applied input type(s) for the category 'self-harm'.""" + + self_harm_instructions: List[Literal["text", "image"]] = FieldInfo(alias="self-harm/instructions") + """The applied input type(s) for the category 'self-harm/instructions'.""" + + self_harm_intent: List[Literal["text", "image"]] = FieldInfo(alias="self-harm/intent") + """The applied input type(s) for the category 'self-harm/intent'.""" + + sexual: List[Literal["text", "image"]] + """The applied input type(s) for the category 'sexual'.""" + + sexual_minors: List[Literal["text"]] = FieldInfo(alias="sexual/minors") + """The applied input type(s) for the category 'sexual/minors'.""" + + violence: List[Literal["text", "image"]] + """The applied input type(s) for the category 'violence'.""" + + violence_graphic: List[Literal["text", "image"]] = FieldInfo(alias="violence/graphic") + """The applied input type(s) for the category 'violence/graphic'.""" + + +class CategoryScores(BaseModel): + """A list of the categories along with their scores as predicted by model.""" + + harassment: float + """The score for the category 'harassment'.""" + + harassment_threatening: float = FieldInfo(alias="harassment/threatening") + """The score for the category 'harassment/threatening'.""" + + hate: float + """The score for the category 'hate'.""" + + hate_threatening: float = FieldInfo(alias="hate/threatening") + """The score for the category 'hate/threatening'.""" + + illicit: float + """The score for the category 'illicit'.""" + + illicit_violent: float = FieldInfo(alias="illicit/violent") + """The score for the category 'illicit/violent'.""" + + self_harm: float = FieldInfo(alias="self-harm") + """The score for the category 'self-harm'.""" + + self_harm_instructions: float = FieldInfo(alias="self-harm/instructions") + """The score for the category 'self-harm/instructions'.""" + + self_harm_intent: float = FieldInfo(alias="self-harm/intent") + """The score for the category 'self-harm/intent'.""" + + sexual: float + """The score for the category 'sexual'.""" + + sexual_minors: float = FieldInfo(alias="sexual/minors") + """The score for the category 'sexual/minors'.""" + + violence: float + """The score for the category 'violence'.""" + + violence_graphic: float = FieldInfo(alias="violence/graphic") + """The score for the category 'violence/graphic'.""" + + +class Moderation(BaseModel): + categories: Categories + """A list of the categories, and whether they are flagged or not.""" + + category_applied_input_types: CategoryAppliedInputTypes + """ + A list of the categories along with the input type(s) that the score applies to. + """ + + category_scores: CategoryScores + """A list of the categories along with their scores as predicted by model.""" + + flagged: bool + """Whether any of the below categories are flagged.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/moderation_create_params.py b/venv/lib/python3.12/site-packages/openai/types/moderation_create_params.py new file mode 100644 index 0000000..724148f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/moderation_create_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr +from .moderation_model import ModerationModel +from .moderation_multi_modal_input_param import ModerationMultiModalInputParam + +__all__ = ["ModerationCreateParams"] + + +class ModerationCreateParams(TypedDict, total=False): + input: Required[Union[str, SequenceNotStr[str], Iterable[ModerationMultiModalInputParam]]] + """Input (or inputs) to classify. + + Can be a single string, an array of strings, or an array of multi-modal input + objects similar to other models. + """ + + model: Union[str, ModerationModel] + """The content moderation model you would like to use. + + Learn more in + [the moderation guide](https://developers.openai.com/api/docs/guides/moderation), + and learn about available models + [here](https://developers.openai.com/api/docs/guides/moderation). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/moderation_create_response.py b/venv/lib/python3.12/site-packages/openai/types/moderation_create_response.py new file mode 100644 index 0000000..18a4217 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/moderation_create_response.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List + +from .._models import BaseModel +from .moderation import Moderation + +__all__ = ["ModerationCreateResponse"] + + +class ModerationCreateResponse(BaseModel): + """Represents if a given text input is potentially harmful.""" + + id: str + """The unique identifier for the moderation request.""" + + model: str + """The model used to generate the moderation results.""" + + results: List[Moderation] + """A list of moderation objects.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/moderation_image_url_input_param.py b/venv/lib/python3.12/site-packages/openai/types/moderation_image_url_input_param.py new file mode 100644 index 0000000..13ba1c4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/moderation_image_url_input_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ModerationImageURLInputParam", "ImageURL"] + + +class ImageURL(TypedDict, total=False): + """Contains either an image URL or a data URL for a base64 encoded image.""" + + url: Required[str] + """Either a URL of the image or the base64 encoded image data.""" + + +class ModerationImageURLInputParam(TypedDict, total=False): + """An object describing an image to classify.""" + + image_url: Required[ImageURL] + """Contains either an image URL or a data URL for a base64 encoded image.""" + + type: Required[Literal["image_url"]] + """Always `image_url`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/moderation_model.py b/venv/lib/python3.12/site-packages/openai/types/moderation_model.py new file mode 100644 index 0000000..7c0ecf2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/moderation_model.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ModerationModel"] + +ModerationModel: TypeAlias = Literal[ + "omni-moderation-latest", "omni-moderation-2024-09-26", "text-moderation-latest", "text-moderation-stable" +] diff --git a/venv/lib/python3.12/site-packages/openai/types/moderation_multi_modal_input_param.py b/venv/lib/python3.12/site-packages/openai/types/moderation_multi_modal_input_param.py new file mode 100644 index 0000000..de8261e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/moderation_multi_modal_input_param.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .moderation_text_input_param import ModerationTextInputParam +from .moderation_image_url_input_param import ModerationImageURLInputParam + +__all__ = ["ModerationMultiModalInputParam"] + +ModerationMultiModalInputParam: TypeAlias = Union[ModerationImageURLInputParam, ModerationTextInputParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/moderation_text_input_param.py b/venv/lib/python3.12/site-packages/openai/types/moderation_text_input_param.py new file mode 100644 index 0000000..5688caa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/moderation_text_input_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ModerationTextInputParam"] + + +class ModerationTextInputParam(TypedDict, total=False): + """An object describing text to classify.""" + + text: Required[str] + """A string of text to classify.""" + + type: Required[Literal["text"]] + """Always `text`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/other_file_chunking_strategy_object.py b/venv/lib/python3.12/site-packages/openai/types/other_file_chunking_strategy_object.py new file mode 100644 index 0000000..b62fb63 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/other_file_chunking_strategy_object.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["OtherFileChunkingStrategyObject"] + + +class OtherFileChunkingStrategyObject(BaseModel): + """This is returned when the chunking strategy is unknown. + + Typically, this is because the file was indexed before the `chunking_strategy` concept was introduced in the API. + """ + + type: Literal["other"] + """Always `other`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__init__.py b/venv/lib/python3.12/site-packages/openai/types/realtime/__init__.py new file mode 100644 index 0000000..e4a94a9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/__init__.py @@ -0,0 +1,242 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .realtime_error import RealtimeError as RealtimeError +from .call_refer_params import CallReferParams as CallReferParams +from .conversation_item import ConversationItem as ConversationItem +from .realtime_response import RealtimeResponse as RealtimeResponse +from .call_accept_params import CallAcceptParams as CallAcceptParams +from .call_create_params import CallCreateParams as CallCreateParams +from .call_reject_params import CallRejectParams as CallRejectParams +from .realtime_reasoning import RealtimeReasoning as RealtimeReasoning +from .audio_transcription import AudioTranscription as AudioTranscription +from .log_prob_properties import LogProbProperties as LogProbProperties +from .realtime_truncation import RealtimeTruncation as RealtimeTruncation +from .response_done_event import ResponseDoneEvent as ResponseDoneEvent +from .noise_reduction_type import NoiseReductionType as NoiseReductionType +from .realtime_error_event import RealtimeErrorEvent as RealtimeErrorEvent +from .session_update_event import SessionUpdateEvent as SessionUpdateEvent +from .mcp_list_tools_failed import McpListToolsFailed as McpListToolsFailed +from .realtime_audio_config import RealtimeAudioConfig as RealtimeAudioConfig +from .realtime_client_event import RealtimeClientEvent as RealtimeClientEvent +from .realtime_server_event import RealtimeServerEvent as RealtimeServerEvent +from .realtime_tools_config import RealtimeToolsConfig as RealtimeToolsConfig +from .response_cancel_event import ResponseCancelEvent as ResponseCancelEvent +from .response_create_event import ResponseCreateEvent as ResponseCreateEvent +from .session_created_event import SessionCreatedEvent as SessionCreatedEvent +from .session_updated_event import SessionUpdatedEvent as SessionUpdatedEvent +from .conversation_item_done import ConversationItemDone as ConversationItemDone +from .realtime_audio_formats import RealtimeAudioFormats as RealtimeAudioFormats +from .realtime_function_tool import RealtimeFunctionTool as RealtimeFunctionTool +from .realtime_mcp_tool_call import RealtimeMcpToolCall as RealtimeMcpToolCall +from .realtime_mcphttp_error import RealtimeMcphttpError as RealtimeMcphttpError +from .response_created_event import ResponseCreatedEvent as ResponseCreatedEvent +from .conversation_item_added import ConversationItemAdded as ConversationItemAdded +from .conversation_item_param import ConversationItemParam as ConversationItemParam +from .realtime_connect_params import RealtimeConnectParams as RealtimeConnectParams +from .realtime_mcp_list_tools import RealtimeMcpListTools as RealtimeMcpListTools +from .realtime_response_usage import RealtimeResponseUsage as RealtimeResponseUsage +from .realtime_tracing_config import RealtimeTracingConfig as RealtimeTracingConfig +from .mcp_list_tools_completed import McpListToolsCompleted as McpListToolsCompleted +from .realtime_reasoning_param import RealtimeReasoningParam as RealtimeReasoningParam +from .realtime_response_status import RealtimeResponseStatus as RealtimeResponseStatus +from .response_mcp_call_failed import ResponseMcpCallFailed as ResponseMcpCallFailed +from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent +from .audio_transcription_param import AudioTranscriptionParam as AudioTranscriptionParam +from .rate_limits_updated_event import RateLimitsUpdatedEvent as RateLimitsUpdatedEvent +from .realtime_reasoning_effort import RealtimeReasoningEffort as RealtimeReasoningEffort +from .realtime_truncation_param import RealtimeTruncationParam as RealtimeTruncationParam +from .response_audio_done_event import ResponseAudioDoneEvent as ResponseAudioDoneEvent +from .response_text_delta_event import ResponseTextDeltaEvent as ResponseTextDeltaEvent +from .conversation_created_event import ConversationCreatedEvent as ConversationCreatedEvent +from .mcp_list_tools_in_progress import McpListToolsInProgress as McpListToolsInProgress +from .response_audio_delta_event import ResponseAudioDeltaEvent as ResponseAudioDeltaEvent +from .session_update_event_param import SessionUpdateEventParam as SessionUpdateEventParam +from .client_secret_create_params import ClientSecretCreateParams as ClientSecretCreateParams +from .realtime_audio_config_input import RealtimeAudioConfigInput as RealtimeAudioConfigInput +from .realtime_audio_config_param import RealtimeAudioConfigParam as RealtimeAudioConfigParam +from .realtime_client_event_param import RealtimeClientEventParam as RealtimeClientEventParam +from .realtime_mcp_protocol_error import RealtimeMcpProtocolError as RealtimeMcpProtocolError +from .realtime_tool_choice_config import RealtimeToolChoiceConfig as RealtimeToolChoiceConfig +from .realtime_tools_config_param import RealtimeToolsConfigParam as RealtimeToolsConfigParam +from .realtime_tools_config_union import RealtimeToolsConfigUnion as RealtimeToolsConfigUnion +from .response_cancel_event_param import ResponseCancelEventParam as ResponseCancelEventParam +from .response_create_event_param import ResponseCreateEventParam as ResponseCreateEventParam +from .response_mcp_call_completed import ResponseMcpCallCompleted as ResponseMcpCallCompleted +from .realtime_audio_config_output import RealtimeAudioConfigOutput as RealtimeAudioConfigOutput +from .realtime_audio_formats_param import RealtimeAudioFormatsParam as RealtimeAudioFormatsParam +from .realtime_function_tool_param import RealtimeFunctionToolParam as RealtimeFunctionToolParam +from .realtime_mcp_tool_call_param import RealtimeMcpToolCallParam as RealtimeMcpToolCallParam +from .realtime_mcphttp_error_param import RealtimeMcphttpErrorParam as RealtimeMcphttpErrorParam +from .client_secret_create_response import ClientSecretCreateResponse as ClientSecretCreateResponse +from .realtime_mcp_approval_request import RealtimeMcpApprovalRequest as RealtimeMcpApprovalRequest +from .realtime_mcp_list_tools_param import RealtimeMcpListToolsParam as RealtimeMcpListToolsParam +from .realtime_tracing_config_param import RealtimeTracingConfigParam as RealtimeTracingConfigParam +from .response_mcp_call_in_progress import ResponseMcpCallInProgress as ResponseMcpCallInProgress +from .conversation_item_create_event import ConversationItemCreateEvent as ConversationItemCreateEvent +from .conversation_item_delete_event import ConversationItemDeleteEvent as ConversationItemDeleteEvent +from .input_audio_buffer_clear_event import InputAudioBufferClearEvent as InputAudioBufferClearEvent +from .realtime_mcp_approval_response import RealtimeMcpApprovalResponse as RealtimeMcpApprovalResponse +from .conversation_item_created_event import ConversationItemCreatedEvent as ConversationItemCreatedEvent +from .conversation_item_deleted_event import ConversationItemDeletedEvent as ConversationItemDeletedEvent +from .input_audio_buffer_append_event import InputAudioBufferAppendEvent as InputAudioBufferAppendEvent +from .input_audio_buffer_commit_event import InputAudioBufferCommitEvent as InputAudioBufferCommitEvent +from .output_audio_buffer_clear_event import OutputAudioBufferClearEvent as OutputAudioBufferClearEvent +from .realtime_response_create_params import RealtimeResponseCreateParams as RealtimeResponseCreateParams +from .realtime_session_create_request import RealtimeSessionCreateRequest as RealtimeSessionCreateRequest +from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent +from .conversation_item_retrieve_event import ConversationItemRetrieveEvent as ConversationItemRetrieveEvent +from .conversation_item_truncate_event import ConversationItemTruncateEvent as ConversationItemTruncateEvent +from .input_audio_buffer_cleared_event import InputAudioBufferClearedEvent as InputAudioBufferClearedEvent +from .realtime_session_create_response import RealtimeSessionCreateResponse as RealtimeSessionCreateResponse +from .response_content_part_done_event import ResponseContentPartDoneEvent as ResponseContentPartDoneEvent +from .response_mcp_call_arguments_done import ResponseMcpCallArgumentsDone as ResponseMcpCallArgumentsDone +from .response_output_item_added_event import ResponseOutputItemAddedEvent as ResponseOutputItemAddedEvent +from .conversation_item_truncated_event import ConversationItemTruncatedEvent as ConversationItemTruncatedEvent +from .realtime_audio_config_input_param import RealtimeAudioConfigInputParam as RealtimeAudioConfigInputParam +from .realtime_mcp_protocol_error_param import RealtimeMcpProtocolErrorParam as RealtimeMcpProtocolErrorParam +from .realtime_mcp_tool_execution_error import RealtimeMcpToolExecutionError as RealtimeMcpToolExecutionError +from .realtime_response_create_mcp_tool import RealtimeResponseCreateMcpTool as RealtimeResponseCreateMcpTool +from .realtime_tool_choice_config_param import RealtimeToolChoiceConfigParam as RealtimeToolChoiceConfigParam +from .realtime_tools_config_union_param import RealtimeToolsConfigUnionParam as RealtimeToolsConfigUnionParam +from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent +from .response_mcp_call_arguments_delta import ResponseMcpCallArgumentsDelta as ResponseMcpCallArgumentsDelta +from .input_audio_buffer_committed_event import InputAudioBufferCommittedEvent as InputAudioBufferCommittedEvent +from .realtime_audio_config_output_param import RealtimeAudioConfigOutputParam as RealtimeAudioConfigOutputParam +from .realtime_audio_input_turn_detection import RealtimeAudioInputTurnDetection as RealtimeAudioInputTurnDetection +from .realtime_mcp_approval_request_param import RealtimeMcpApprovalRequestParam as RealtimeMcpApprovalRequestParam +from .realtime_truncation_retention_ratio import RealtimeTruncationRetentionRatio as RealtimeTruncationRetentionRatio +from .conversation_item_create_event_param import ConversationItemCreateEventParam as ConversationItemCreateEventParam +from .conversation_item_delete_event_param import ConversationItemDeleteEventParam as ConversationItemDeleteEventParam +from .input_audio_buffer_clear_event_param import InputAudioBufferClearEventParam as InputAudioBufferClearEventParam +from .input_audio_buffer_timeout_triggered import InputAudioBufferTimeoutTriggered as InputAudioBufferTimeoutTriggered +from .realtime_mcp_approval_response_param import RealtimeMcpApprovalResponseParam as RealtimeMcpApprovalResponseParam +from .realtime_transcription_session_audio import RealtimeTranscriptionSessionAudio as RealtimeTranscriptionSessionAudio +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent as ResponseAudioTranscriptDoneEvent +from .input_audio_buffer_append_event_param import InputAudioBufferAppendEventParam as InputAudioBufferAppendEventParam +from .input_audio_buffer_commit_event_param import InputAudioBufferCommitEventParam as InputAudioBufferCommitEventParam +from .output_audio_buffer_clear_event_param import OutputAudioBufferClearEventParam as OutputAudioBufferClearEventParam +from .realtime_response_create_audio_output import ( + RealtimeResponseCreateAudioOutput as RealtimeResponseCreateAudioOutput, +) +from .realtime_response_create_params_param import ( + RealtimeResponseCreateParamsParam as RealtimeResponseCreateParamsParam, +) +from .realtime_session_create_request_param import ( + RealtimeSessionCreateRequestParam as RealtimeSessionCreateRequestParam, +) +from .response_audio_transcript_delta_event import ( + ResponseAudioTranscriptDeltaEvent as ResponseAudioTranscriptDeltaEvent, +) +from .conversation_item_retrieve_event_param import ( + ConversationItemRetrieveEventParam as ConversationItemRetrieveEventParam, +) +from .conversation_item_truncate_event_param import ( + ConversationItemTruncateEventParam as ConversationItemTruncateEventParam, +) +from .input_audio_buffer_speech_started_event import ( + InputAudioBufferSpeechStartedEvent as InputAudioBufferSpeechStartedEvent, +) +from .input_audio_buffer_speech_stopped_event import ( + InputAudioBufferSpeechStoppedEvent as InputAudioBufferSpeechStoppedEvent, +) +from .realtime_conversation_item_user_message import ( + RealtimeConversationItemUserMessage as RealtimeConversationItemUserMessage, +) +from .realtime_mcp_tool_execution_error_param import ( + RealtimeMcpToolExecutionErrorParam as RealtimeMcpToolExecutionErrorParam, +) +from .realtime_response_create_mcp_tool_param import ( + RealtimeResponseCreateMcpToolParam as RealtimeResponseCreateMcpToolParam, +) +from .realtime_conversation_item_function_call import ( + RealtimeConversationItemFunctionCall as RealtimeConversationItemFunctionCall, +) +from .realtime_audio_input_turn_detection_param import ( + RealtimeAudioInputTurnDetectionParam as RealtimeAudioInputTurnDetectionParam, +) +from .realtime_conversation_item_system_message import ( + RealtimeConversationItemSystemMessage as RealtimeConversationItemSystemMessage, +) +from .realtime_truncation_retention_ratio_param import ( + RealtimeTruncationRetentionRatioParam as RealtimeTruncationRetentionRatioParam, +) +from .realtime_transcription_session_audio_input import ( + RealtimeTranscriptionSessionAudioInput as RealtimeTranscriptionSessionAudioInput, +) +from .realtime_transcription_session_audio_param import ( + RealtimeTranscriptionSessionAudioParam as RealtimeTranscriptionSessionAudioParam, +) +from .realtime_response_create_audio_output_param import ( + RealtimeResponseCreateAudioOutputParam as RealtimeResponseCreateAudioOutputParam, +) +from .realtime_response_usage_input_token_details import ( + RealtimeResponseUsageInputTokenDetails as RealtimeResponseUsageInputTokenDetails, +) +from .response_function_call_arguments_done_event import ( + ResponseFunctionCallArgumentsDoneEvent as ResponseFunctionCallArgumentsDoneEvent, +) +from .input_audio_buffer_dtmf_event_received_event import ( + InputAudioBufferDtmfEventReceivedEvent as InputAudioBufferDtmfEventReceivedEvent, +) +from .realtime_conversation_item_assistant_message import ( + RealtimeConversationItemAssistantMessage as RealtimeConversationItemAssistantMessage, +) +from .realtime_response_usage_output_token_details import ( + RealtimeResponseUsageOutputTokenDetails as RealtimeResponseUsageOutputTokenDetails, +) +from .response_function_call_arguments_delta_event import ( + ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, +) +from .realtime_conversation_item_user_message_param import ( + RealtimeConversationItemUserMessageParam as RealtimeConversationItemUserMessageParam, +) +from .realtime_transcription_session_create_request import ( + RealtimeTranscriptionSessionCreateRequest as RealtimeTranscriptionSessionCreateRequest, +) +from .realtime_transcription_session_turn_detection import ( + RealtimeTranscriptionSessionTurnDetection as RealtimeTranscriptionSessionTurnDetection, +) +from .realtime_conversation_item_function_call_param import ( + RealtimeConversationItemFunctionCallParam as RealtimeConversationItemFunctionCallParam, +) +from .realtime_transcription_session_create_response import ( + RealtimeTranscriptionSessionCreateResponse as RealtimeTranscriptionSessionCreateResponse, +) +from .realtime_conversation_item_function_call_output import ( + RealtimeConversationItemFunctionCallOutput as RealtimeConversationItemFunctionCallOutput, +) +from .realtime_conversation_item_system_message_param import ( + RealtimeConversationItemSystemMessageParam as RealtimeConversationItemSystemMessageParam, +) +from .realtime_transcription_session_audio_input_param import ( + RealtimeTranscriptionSessionAudioInputParam as RealtimeTranscriptionSessionAudioInputParam, +) +from .realtime_conversation_item_assistant_message_param import ( + RealtimeConversationItemAssistantMessageParam as RealtimeConversationItemAssistantMessageParam, +) +from .conversation_item_input_audio_transcription_segment import ( + ConversationItemInputAudioTranscriptionSegment as ConversationItemInputAudioTranscriptionSegment, +) +from .realtime_transcription_session_create_request_param import ( + RealtimeTranscriptionSessionCreateRequestParam as RealtimeTranscriptionSessionCreateRequestParam, +) +from .realtime_conversation_item_function_call_output_param import ( + RealtimeConversationItemFunctionCallOutputParam as RealtimeConversationItemFunctionCallOutputParam, +) +from .conversation_item_input_audio_transcription_delta_event import ( + ConversationItemInputAudioTranscriptionDeltaEvent as ConversationItemInputAudioTranscriptionDeltaEvent, +) +from .conversation_item_input_audio_transcription_failed_event import ( + ConversationItemInputAudioTranscriptionFailedEvent as ConversationItemInputAudioTranscriptionFailedEvent, +) +from .realtime_transcription_session_audio_input_turn_detection import ( + RealtimeTranscriptionSessionAudioInputTurnDetection as RealtimeTranscriptionSessionAudioInputTurnDetection, +) +from .conversation_item_input_audio_transcription_completed_event import ( + ConversationItemInputAudioTranscriptionCompletedEvent as ConversationItemInputAudioTranscriptionCompletedEvent, +) +from .realtime_transcription_session_audio_input_turn_detection_param import ( + RealtimeTranscriptionSessionAudioInputTurnDetectionParam as RealtimeTranscriptionSessionAudioInputTurnDetectionParam, +) diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2229fcc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/audio_transcription.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/audio_transcription.cpython-312.pyc new file mode 100644 index 0000000..1bcc16d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/audio_transcription.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/audio_transcription_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/audio_transcription_param.cpython-312.pyc new file mode 100644 index 0000000..c9ae876 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/audio_transcription_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/call_accept_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/call_accept_params.cpython-312.pyc new file mode 100644 index 0000000..3543313 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/call_accept_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/call_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/call_create_params.cpython-312.pyc new file mode 100644 index 0000000..4af27cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/call_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/call_refer_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/call_refer_params.cpython-312.pyc new file mode 100644 index 0000000..5580aa9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/call_refer_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/call_reject_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/call_reject_params.cpython-312.pyc new file mode 100644 index 0000000..55dce75 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/call_reject_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/client_secret_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/client_secret_create_params.cpython-312.pyc new file mode 100644 index 0000000..0c7c516 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/client_secret_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/client_secret_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/client_secret_create_response.cpython-312.pyc new file mode 100644 index 0000000..631ef5c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/client_secret_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_created_event.cpython-312.pyc new file mode 100644 index 0000000..4c090c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item.cpython-312.pyc new file mode 100644 index 0000000..6d4d510 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_added.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_added.cpython-312.pyc new file mode 100644 index 0000000..713936a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_added.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_create_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_create_event.cpython-312.pyc new file mode 100644 index 0000000..2e7f553 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_create_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_create_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_create_event_param.cpython-312.pyc new file mode 100644 index 0000000..66050fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_create_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_created_event.cpython-312.pyc new file mode 100644 index 0000000..48b6f1f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_delete_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_delete_event.cpython-312.pyc new file mode 100644 index 0000000..dc6fd8c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_delete_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_delete_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_delete_event_param.cpython-312.pyc new file mode 100644 index 0000000..1911f40 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_delete_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_deleted_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_deleted_event.cpython-312.pyc new file mode 100644 index 0000000..d8eb46d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_deleted_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_done.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_done.cpython-312.pyc new file mode 100644 index 0000000..70b7f9c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_done.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_completed_event.cpython-312.pyc new file mode 100644 index 0000000..e81c349 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_delta_event.cpython-312.pyc new file mode 100644 index 0000000..483d942 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_failed_event.cpython-312.pyc new file mode 100644 index 0000000..3da8086 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_segment.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_segment.cpython-312.pyc new file mode 100644 index 0000000..58d2093 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_input_audio_transcription_segment.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_param.cpython-312.pyc new file mode 100644 index 0000000..f65af93 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_retrieve_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_retrieve_event.cpython-312.pyc new file mode 100644 index 0000000..3b2aff2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_retrieve_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_retrieve_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_retrieve_event_param.cpython-312.pyc new file mode 100644 index 0000000..51dad68 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_retrieve_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_truncate_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_truncate_event.cpython-312.pyc new file mode 100644 index 0000000..08c8b58 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_truncate_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_truncate_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_truncate_event_param.cpython-312.pyc new file mode 100644 index 0000000..8b97fa8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_truncate_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_truncated_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_truncated_event.cpython-312.pyc new file mode 100644 index 0000000..fc02cfc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/conversation_item_truncated_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_append_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_append_event.cpython-312.pyc new file mode 100644 index 0000000..4297da1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_append_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_append_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_append_event_param.cpython-312.pyc new file mode 100644 index 0000000..1a6f90b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_append_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_clear_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_clear_event.cpython-312.pyc new file mode 100644 index 0000000..5bf9469 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_clear_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_clear_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_clear_event_param.cpython-312.pyc new file mode 100644 index 0000000..33fd410 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_clear_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_cleared_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_cleared_event.cpython-312.pyc new file mode 100644 index 0000000..f47da51 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_cleared_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_commit_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_commit_event.cpython-312.pyc new file mode 100644 index 0000000..2426d2b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_commit_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_commit_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_commit_event_param.cpython-312.pyc new file mode 100644 index 0000000..173ca22 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_commit_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_committed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_committed_event.cpython-312.pyc new file mode 100644 index 0000000..971ef23 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_committed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_dtmf_event_received_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_dtmf_event_received_event.cpython-312.pyc new file mode 100644 index 0000000..ac90564 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_dtmf_event_received_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_speech_started_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_speech_started_event.cpython-312.pyc new file mode 100644 index 0000000..d97cc49 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_speech_started_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_speech_stopped_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_speech_stopped_event.cpython-312.pyc new file mode 100644 index 0000000..8e2f024 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_speech_stopped_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_timeout_triggered.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_timeout_triggered.cpython-312.pyc new file mode 100644 index 0000000..1c41f16 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/input_audio_buffer_timeout_triggered.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/log_prob_properties.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/log_prob_properties.cpython-312.pyc new file mode 100644 index 0000000..6ba99b8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/log_prob_properties.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/mcp_list_tools_completed.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/mcp_list_tools_completed.cpython-312.pyc new file mode 100644 index 0000000..b4fead0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/mcp_list_tools_completed.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/mcp_list_tools_failed.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/mcp_list_tools_failed.cpython-312.pyc new file mode 100644 index 0000000..3da31e2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/mcp_list_tools_failed.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/mcp_list_tools_in_progress.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/mcp_list_tools_in_progress.cpython-312.pyc new file mode 100644 index 0000000..18962a8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/mcp_list_tools_in_progress.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/noise_reduction_type.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/noise_reduction_type.cpython-312.pyc new file mode 100644 index 0000000..232a61f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/noise_reduction_type.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/output_audio_buffer_clear_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/output_audio_buffer_clear_event.cpython-312.pyc new file mode 100644 index 0000000..3510aca Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/output_audio_buffer_clear_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/output_audio_buffer_clear_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/output_audio_buffer_clear_event_param.cpython-312.pyc new file mode 100644 index 0000000..e05cfc1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/output_audio_buffer_clear_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/rate_limits_updated_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/rate_limits_updated_event.cpython-312.pyc new file mode 100644 index 0000000..b78cc2c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/rate_limits_updated_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config.cpython-312.pyc new file mode 100644 index 0000000..af06f58 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_input.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_input.cpython-312.pyc new file mode 100644 index 0000000..5efca6e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_input.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_input_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_input_param.cpython-312.pyc new file mode 100644 index 0000000..c79e570 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_input_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_output.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_output.cpython-312.pyc new file mode 100644 index 0000000..8125e4f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_output.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_output_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_output_param.cpython-312.pyc new file mode 100644 index 0000000..dd24c90 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_output_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_param.cpython-312.pyc new file mode 100644 index 0000000..dc896eb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_formats.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_formats.cpython-312.pyc new file mode 100644 index 0000000..8c2a675 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_formats.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_formats_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_formats_param.cpython-312.pyc new file mode 100644 index 0000000..c946d19 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_formats_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_input_turn_detection.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_input_turn_detection.cpython-312.pyc new file mode 100644 index 0000000..d047754 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_input_turn_detection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_input_turn_detection_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_input_turn_detection_param.cpython-312.pyc new file mode 100644 index 0000000..e7ff5ea Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_audio_input_turn_detection_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_client_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_client_event.cpython-312.pyc new file mode 100644 index 0000000..4aa2ba8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_client_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_client_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_client_event_param.cpython-312.pyc new file mode 100644 index 0000000..0b3704b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_client_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_connect_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_connect_params.cpython-312.pyc new file mode 100644 index 0000000..d02c554 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_connect_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_assistant_message.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_assistant_message.cpython-312.pyc new file mode 100644 index 0000000..167a5d3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_assistant_message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_assistant_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_assistant_message_param.cpython-312.pyc new file mode 100644 index 0000000..df8698c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_assistant_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_function_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_function_call.cpython-312.pyc new file mode 100644 index 0000000..4a4e503 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_function_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_function_call_output.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_function_call_output.cpython-312.pyc new file mode 100644 index 0000000..8ff1813 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_function_call_output.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_function_call_output_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_function_call_output_param.cpython-312.pyc new file mode 100644 index 0000000..ca01685 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_function_call_output_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_function_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_function_call_param.cpython-312.pyc new file mode 100644 index 0000000..1694fdf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_function_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_system_message.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_system_message.cpython-312.pyc new file mode 100644 index 0000000..9481ee7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_system_message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_system_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_system_message_param.cpython-312.pyc new file mode 100644 index 0000000..54c5e5b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_system_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_user_message.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_user_message.cpython-312.pyc new file mode 100644 index 0000000..ce5feac Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_user_message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_user_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_user_message_param.cpython-312.pyc new file mode 100644 index 0000000..ba246d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_conversation_item_user_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_error.cpython-312.pyc new file mode 100644 index 0000000..7fc4c8a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_error_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_error_event.cpython-312.pyc new file mode 100644 index 0000000..87b526b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_error_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_function_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_function_tool.cpython-312.pyc new file mode 100644 index 0000000..42586f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_function_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_function_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_function_tool_param.cpython-312.pyc new file mode 100644 index 0000000..ec69576 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_function_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_approval_request.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_approval_request.cpython-312.pyc new file mode 100644 index 0000000..4f07e50 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_approval_request.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_approval_request_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_approval_request_param.cpython-312.pyc new file mode 100644 index 0000000..93f753f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_approval_request_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_approval_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_approval_response.cpython-312.pyc new file mode 100644 index 0000000..a303a98 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_approval_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_approval_response_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_approval_response_param.cpython-312.pyc new file mode 100644 index 0000000..482284f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_approval_response_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_list_tools.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_list_tools.cpython-312.pyc new file mode 100644 index 0000000..a6f4d82 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_list_tools.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_list_tools_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_list_tools_param.cpython-312.pyc new file mode 100644 index 0000000..6f7d199 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_list_tools_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_protocol_error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_protocol_error.cpython-312.pyc new file mode 100644 index 0000000..0f10d00 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_protocol_error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_protocol_error_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_protocol_error_param.cpython-312.pyc new file mode 100644 index 0000000..7b1e48a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_protocol_error_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_tool_call.cpython-312.pyc new file mode 100644 index 0000000..46e5c53 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..c21b7d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_tool_execution_error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_tool_execution_error.cpython-312.pyc new file mode 100644 index 0000000..fae2611 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_tool_execution_error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_tool_execution_error_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_tool_execution_error_param.cpython-312.pyc new file mode 100644 index 0000000..64eeedb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcp_tool_execution_error_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcphttp_error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcphttp_error.cpython-312.pyc new file mode 100644 index 0000000..c82e331 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcphttp_error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcphttp_error_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcphttp_error_param.cpython-312.pyc new file mode 100644 index 0000000..fdd7824 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_mcphttp_error_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_reasoning.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_reasoning.cpython-312.pyc new file mode 100644 index 0000000..c8bb290 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_reasoning.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_reasoning_effort.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_reasoning_effort.cpython-312.pyc new file mode 100644 index 0000000..6a04f49 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_reasoning_effort.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_reasoning_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_reasoning_param.cpython-312.pyc new file mode 100644 index 0000000..6246cf5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_reasoning_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response.cpython-312.pyc new file mode 100644 index 0000000..f642050 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_audio_output.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_audio_output.cpython-312.pyc new file mode 100644 index 0000000..eb0e385 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_audio_output.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_audio_output_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_audio_output_param.cpython-312.pyc new file mode 100644 index 0000000..b986ca0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_audio_output_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_mcp_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_mcp_tool.cpython-312.pyc new file mode 100644 index 0000000..794440b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_mcp_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_mcp_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_mcp_tool_param.cpython-312.pyc new file mode 100644 index 0000000..d67a81e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_mcp_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_params.cpython-312.pyc new file mode 100644 index 0000000..44ae2e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_params_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_params_param.cpython-312.pyc new file mode 100644 index 0000000..310b8dd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_create_params_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_status.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_status.cpython-312.pyc new file mode 100644 index 0000000..9901a0d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_status.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_usage.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_usage.cpython-312.pyc new file mode 100644 index 0000000..49d9953 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_usage.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_usage_input_token_details.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_usage_input_token_details.cpython-312.pyc new file mode 100644 index 0000000..2970280 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_usage_input_token_details.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_usage_output_token_details.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_usage_output_token_details.cpython-312.pyc new file mode 100644 index 0000000..8f5c645 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_response_usage_output_token_details.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_server_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_server_event.cpython-312.pyc new file mode 100644 index 0000000..e3d8db9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_server_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_session_create_request.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_session_create_request.cpython-312.pyc new file mode 100644 index 0000000..5fad09f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_session_create_request.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_session_create_request_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_session_create_request_param.cpython-312.pyc new file mode 100644 index 0000000..dfd6de2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_session_create_request_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_session_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_session_create_response.cpython-312.pyc new file mode 100644 index 0000000..618e289 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_session_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tool_choice_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tool_choice_config.cpython-312.pyc new file mode 100644 index 0000000..800ee73 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tool_choice_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tool_choice_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tool_choice_config_param.cpython-312.pyc new file mode 100644 index 0000000..d6e30c2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tool_choice_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tools_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tools_config.cpython-312.pyc new file mode 100644 index 0000000..338fb30 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tools_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tools_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tools_config_param.cpython-312.pyc new file mode 100644 index 0000000..0970521 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tools_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tools_config_union.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tools_config_union.cpython-312.pyc new file mode 100644 index 0000000..8df4527 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tools_config_union.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tools_config_union_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tools_config_union_param.cpython-312.pyc new file mode 100644 index 0000000..a7eec39 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tools_config_union_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tracing_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tracing_config.cpython-312.pyc new file mode 100644 index 0000000..6e271a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tracing_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tracing_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tracing_config_param.cpython-312.pyc new file mode 100644 index 0000000..7f277ab Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_tracing_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio.cpython-312.pyc new file mode 100644 index 0000000..dd2344f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_input.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_input.cpython-312.pyc new file mode 100644 index 0000000..322237d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_input.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_input_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_input_param.cpython-312.pyc new file mode 100644 index 0000000..99fed15 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_input_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_input_turn_detection.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_input_turn_detection.cpython-312.pyc new file mode 100644 index 0000000..4715cd6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_input_turn_detection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_input_turn_detection_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_input_turn_detection_param.cpython-312.pyc new file mode 100644 index 0000000..3bae007 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_input_turn_detection_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_param.cpython-312.pyc new file mode 100644 index 0000000..b194098 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_audio_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_create_request.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_create_request.cpython-312.pyc new file mode 100644 index 0000000..26cd4df Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_create_request.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_create_request_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_create_request_param.cpython-312.pyc new file mode 100644 index 0000000..03775a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_create_request_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_create_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_create_response.cpython-312.pyc new file mode 100644 index 0000000..0c5e5f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_create_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_turn_detection.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_turn_detection.cpython-312.pyc new file mode 100644 index 0000000..ee0a4cf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_transcription_session_turn_detection.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_truncation.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_truncation.cpython-312.pyc new file mode 100644 index 0000000..edfb2cb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_truncation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_truncation_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_truncation_param.cpython-312.pyc new file mode 100644 index 0000000..124acdd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_truncation_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_truncation_retention_ratio.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_truncation_retention_ratio.cpython-312.pyc new file mode 100644 index 0000000..c52854a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_truncation_retention_ratio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_truncation_retention_ratio_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_truncation_retention_ratio_param.cpython-312.pyc new file mode 100644 index 0000000..6b76251 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/realtime_truncation_retention_ratio_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_audio_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_audio_delta_event.cpython-312.pyc new file mode 100644 index 0000000..a658042 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_audio_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_audio_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_audio_done_event.cpython-312.pyc new file mode 100644 index 0000000..ff89be6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_audio_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_audio_transcript_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_audio_transcript_delta_event.cpython-312.pyc new file mode 100644 index 0000000..1c25f94 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_audio_transcript_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_audio_transcript_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_audio_transcript_done_event.cpython-312.pyc new file mode 100644 index 0000000..0a95a83 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_audio_transcript_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_cancel_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_cancel_event.cpython-312.pyc new file mode 100644 index 0000000..36264f8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_cancel_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_cancel_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_cancel_event_param.cpython-312.pyc new file mode 100644 index 0000000..51a71d6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_cancel_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_content_part_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_content_part_added_event.cpython-312.pyc new file mode 100644 index 0000000..3c33443 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_content_part_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_content_part_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_content_part_done_event.cpython-312.pyc new file mode 100644 index 0000000..52463df Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_content_part_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_create_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_create_event.cpython-312.pyc new file mode 100644 index 0000000..d6d4097 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_create_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_create_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_create_event_param.cpython-312.pyc new file mode 100644 index 0000000..22b5a4a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_create_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_created_event.cpython-312.pyc new file mode 100644 index 0000000..20ef998 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_done_event.cpython-312.pyc new file mode 100644 index 0000000..7c5a97b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_function_call_arguments_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_function_call_arguments_delta_event.cpython-312.pyc new file mode 100644 index 0000000..88c4d58 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_function_call_arguments_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_function_call_arguments_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_function_call_arguments_done_event.cpython-312.pyc new file mode 100644 index 0000000..7bcca62 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_function_call_arguments_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_arguments_delta.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_arguments_delta.cpython-312.pyc new file mode 100644 index 0000000..7db34c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_arguments_delta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_arguments_done.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_arguments_done.cpython-312.pyc new file mode 100644 index 0000000..11262f9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_arguments_done.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_completed.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_completed.cpython-312.pyc new file mode 100644 index 0000000..0d4529a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_completed.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_failed.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_failed.cpython-312.pyc new file mode 100644 index 0000000..497a9a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_failed.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_in_progress.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_in_progress.cpython-312.pyc new file mode 100644 index 0000000..fd0e4ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_mcp_call_in_progress.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_output_item_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_output_item_added_event.cpython-312.pyc new file mode 100644 index 0000000..c827bc5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_output_item_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_output_item_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_output_item_done_event.cpython-312.pyc new file mode 100644 index 0000000..2f2b5a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_output_item_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_text_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_text_delta_event.cpython-312.pyc new file mode 100644 index 0000000..647a09b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_text_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_text_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_text_done_event.cpython-312.pyc new file mode 100644 index 0000000..c4851a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/response_text_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/session_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/session_created_event.cpython-312.pyc new file mode 100644 index 0000000..f6385f8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/session_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/session_update_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/session_update_event.cpython-312.pyc new file mode 100644 index 0000000..87f7b93 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/session_update_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/session_update_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/session_update_event_param.cpython-312.pyc new file mode 100644 index 0000000..5610f1e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/session_update_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/session_updated_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/session_updated_event.cpython-312.pyc new file mode 100644 index 0000000..c8d9b22 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/realtime/__pycache__/session_updated_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/audio_transcription.py b/venv/lib/python3.12/site-packages/openai/types/realtime/audio_transcription.py new file mode 100644 index 0000000..65c789d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/audio_transcription.py @@ -0,0 +1,70 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AudioTranscription"] + + +class AudioTranscription(BaseModel): + delay: Optional[Literal["minimal", "low", "medium", "high", "xhigh"]] = None + """ + Controls how long the model waits before emitting transcription text. Higher + values can improve transcription accuracy at the cost of latency. Only supported + with `gpt-realtime-whisper` in GA Realtime sessions. + """ + + keywords: Optional[List[str]] = None + """Words or phrases to guide transcription of the input audio. + + Supported by `gpt-transcribe` and `gpt-live-transcribe`. + """ + + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + languages: Optional[List[str]] = None + """ + Possible languages of the input audio, in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format. + Supported by `gpt-transcribe` and `gpt-live-transcribe`. + """ + + model: Union[ + str, + Literal[ + "whisper-1", + "gpt-transcribe", + "gpt-live-transcribe", + "gpt-4o-mini-transcribe", + "gpt-4o-mini-transcribe-2025-12-15", + "gpt-4o-transcribe", + "gpt-4o-transcribe-diarize", + "gpt-realtime-whisper", + ], + None, + ] = None + """The model to use for transcription. + + Current options are `whisper-1`, `gpt-transcribe`, `gpt-live-transcribe`, + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, + `gpt-4o-transcribe`, `gpt-4o-transcribe-diarize`, and `gpt-realtime-whisper`. + Use `gpt-4o-transcribe-diarize` when you need diarization with speaker labels. + """ + + prompt: Optional[str] = None + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://developers.openai.com/api/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models (excluding `gpt-4o-transcribe-diarize`), the + prompt is a free text string, for example "expect words related to technology". + Prompt is not supported with `gpt-realtime-whisper` in GA Realtime sessions. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/audio_transcription_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/audio_transcription_param.py new file mode 100644 index 0000000..d8b282f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/audio_transcription_param.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["AudioTranscriptionParam"] + + +class AudioTranscriptionParam(TypedDict, total=False): + delay: Literal["minimal", "low", "medium", "high", "xhigh"] + """ + Controls how long the model waits before emitting transcription text. Higher + values can improve transcription accuracy at the cost of latency. Only supported + with `gpt-realtime-whisper` in GA Realtime sessions. + """ + + keywords: SequenceNotStr[str] + """Words or phrases to guide transcription of the input audio. + + Supported by `gpt-transcribe` and `gpt-live-transcribe`. + """ + + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + languages: SequenceNotStr[str] + """ + Possible languages of the input audio, in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format. + Supported by `gpt-transcribe` and `gpt-live-transcribe`. + """ + + model: Union[ + str, + Literal[ + "whisper-1", + "gpt-transcribe", + "gpt-live-transcribe", + "gpt-4o-mini-transcribe", + "gpt-4o-mini-transcribe-2025-12-15", + "gpt-4o-transcribe", + "gpt-4o-transcribe-diarize", + "gpt-realtime-whisper", + ], + ] + """The model to use for transcription. + + Current options are `whisper-1`, `gpt-transcribe`, `gpt-live-transcribe`, + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, + `gpt-4o-transcribe`, `gpt-4o-transcribe-diarize`, and `gpt-realtime-whisper`. + Use `gpt-4o-transcribe-diarize` when you need diarization with speaker labels. + """ + + prompt: str + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://developers.openai.com/api/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models (excluding `gpt-4o-transcribe-diarize`), the + prompt is a free text string, for example "expect words related to technology". + Prompt is not supported with `gpt-realtime-whisper` in GA Realtime sessions. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/call_accept_params.py b/venv/lib/python3.12/site-packages/openai/types/realtime/call_accept_params.py new file mode 100644 index 0000000..3e681ac --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/call_accept_params.py @@ -0,0 +1,145 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Required, TypedDict + +from .realtime_reasoning_param import RealtimeReasoningParam +from .realtime_truncation_param import RealtimeTruncationParam +from .realtime_audio_config_param import RealtimeAudioConfigParam +from .realtime_tools_config_param import RealtimeToolsConfigParam +from .realtime_tracing_config_param import RealtimeTracingConfigParam +from ..responses.response_prompt_param import ResponsePromptParam +from .realtime_tool_choice_config_param import RealtimeToolChoiceConfigParam + +__all__ = ["CallAcceptParams"] + + +class CallAcceptParams(TypedDict, total=False): + type: Required[Literal["realtime"]] + """The type of session to create. Always `realtime` for the Realtime API.""" + + audio: RealtimeAudioConfigParam + """Configuration for input and output audio.""" + + include: List[Literal["item.input_audio_transcription.logprobs"]] + """Additional fields to include in server outputs. + + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ + + instructions: str + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_output_tokens: Union[int, Literal["inf"]] + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + model: Union[ + str, + Literal[ + "gpt-realtime", + "gpt-realtime-1.5", + "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15", + ], + ] + """The Realtime model used for this session.""" + + output_modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + It defaults to `["audio"]`, indicating that the model will respond with audio + plus a transcript. `["text"]` can be used to make the model respond with text + only. It is not possible to request both `text` and `audio` at the same time. + """ + + parallel_tool_calls: bool + """Whether the model may call multiple tools in parallel. + + Only supported by reasoning Realtime models such as `gpt-realtime-2`. + """ + + prompt: Optional[ResponsePromptParam] + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + reasoning: RealtimeReasoningParam + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + + tool_choice: RealtimeToolChoiceConfigParam + """How the model chooses tools. + + Provide one of the string modes or force a specific function/MCP tool. + """ + + tools: RealtimeToolsConfigParam + """Tools available to the model.""" + + tracing: Optional[RealtimeTracingConfigParam] + """ + Realtime API can write session traces to the + [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to + disable tracing. Once tracing is enabled for a session, the configuration cannot + be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + truncation: RealtimeTruncationParam + """ + When the number of tokens in a conversation exceeds the model's input token + limit, the conversation be truncated, meaning messages (starting from the + oldest) will not be included in the model's context. A 32k context model with + 4,096 max output tokens can only include 28,224 tokens in the context before + truncation occurs. + + Clients can configure truncation behavior to truncate with a lower max token + limit, which is an effective way to control token usage and cost. + + Truncation will reduce the number of cached tokens on the next turn (busting the + cache), since messages are dropped from the beginning of the context. However, + clients can also configure truncation to retain messages up to a fraction of the + maximum context size, which will reduce the need for future truncations and thus + improve the cache rate. + + Truncation can be disabled entirely, which means the server will never truncate + but would instead return an error if the conversation exceeds the model's input + token limit. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/call_create_params.py b/venv/lib/python3.12/site-packages/openai/types/realtime/call_create_params.py new file mode 100644 index 0000000..0d2c8bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/call_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .realtime_session_create_request_param import RealtimeSessionCreateRequestParam + +__all__ = ["CallCreateParams"] + + +class CallCreateParams(TypedDict, total=False): + sdp: Required[str] + """WebRTC Session Description Protocol (SDP) offer generated by the caller.""" + + session: RealtimeSessionCreateRequestParam + """Realtime session object configuration.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/call_refer_params.py b/venv/lib/python3.12/site-packages/openai/types/realtime/call_refer_params.py new file mode 100644 index 0000000..09977a7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/call_refer_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["CallReferParams"] + + +class CallReferParams(TypedDict, total=False): + target_uri: Required[str] + """URI that should appear in the SIP Refer-To header. + + Supports values like `tel:+14155550123` or `sip:agent@example.com`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/call_reject_params.py b/venv/lib/python3.12/site-packages/openai/types/realtime/call_reject_params.py new file mode 100644 index 0000000..31cd6f1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/call_reject_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["CallRejectParams"] + + +class CallRejectParams(TypedDict, total=False): + status_code: int + """SIP response code to send back to the caller. + + Defaults to `603` (Decline) when omitted. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/client_secret_create_params.py b/venv/lib/python3.12/site-packages/openai/types/realtime/client_secret_create_params.py new file mode 100644 index 0000000..1cf7ed7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/client_secret_create_params.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias, TypedDict + +from .realtime_session_create_request_param import RealtimeSessionCreateRequestParam +from .realtime_transcription_session_create_request_param import RealtimeTranscriptionSessionCreateRequestParam + +__all__ = ["ClientSecretCreateParams", "ExpiresAfter", "Session"] + + +class ClientSecretCreateParams(TypedDict, total=False): + expires_after: ExpiresAfter + """Configuration for the client secret expiration. + + Expiration refers to the time after which a client secret will no longer be + valid for creating sessions. The session itself may continue after that time + once started. A secret can be used to create multiple sessions until it expires. + """ + + session: Session + """Session configuration to use for the client secret. + + Choose either a realtime session or a transcription session. + """ + + +class ExpiresAfter(TypedDict, total=False): + """Configuration for the client secret expiration. + + Expiration refers to the time after which + a client secret will no longer be valid for creating sessions. The session itself may + continue after that time once started. A secret can be used to create multiple sessions + until it expires. + """ + + anchor: Literal["created_at"] + """ + The anchor point for the client secret expiration, meaning that `seconds` will + be added to the `created_at` time of the client secret to produce an expiration + timestamp. Only `created_at` is currently supported. + """ + + seconds: int + """The number of seconds from the anchor point to the expiration. + + Select a value between `10` and `7200` (2 hours). This default to 600 seconds + (10 minutes) if not specified. + """ + + +Session: TypeAlias = Union[RealtimeSessionCreateRequestParam, RealtimeTranscriptionSessionCreateRequestParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/client_secret_create_response.py b/venv/lib/python3.12/site-packages/openai/types/realtime/client_secret_create_response.py new file mode 100644 index 0000000..f6f6dca --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/client_secret_create_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .realtime_session_create_response import RealtimeSessionCreateResponse +from .realtime_transcription_session_create_response import RealtimeTranscriptionSessionCreateResponse + +__all__ = ["ClientSecretCreateResponse", "Session"] + +Session: TypeAlias = Annotated[ + Union[RealtimeSessionCreateResponse, RealtimeTranscriptionSessionCreateResponse], PropertyInfo(discriminator="type") +] + + +class ClientSecretCreateResponse(BaseModel): + """Response from creating a session and client secret for the Realtime API.""" + + expires_at: int + """Expiration timestamp for the client secret, in seconds since epoch.""" + + session: Session + """The session configuration for either a realtime or transcription session.""" + + value: str + """The generated client secret value.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_created_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_created_event.py new file mode 100644 index 0000000..58ed14f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_created_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationCreatedEvent", "Conversation"] + + +class Conversation(BaseModel): + """The conversation resource.""" + + id: Optional[str] = None + """The unique ID of the conversation.""" + + object: Optional[Literal["realtime.conversation"]] = None + """The object type, must be `realtime.conversation`.""" + + +class ConversationCreatedEvent(BaseModel): + """Returned when a conversation is created. Emitted right after session creation.""" + + conversation: Conversation + """The conversation resource.""" + + event_id: str + """The unique ID of the server event.""" + + type: Literal["conversation.created"] + """The event type, must be `conversation.created`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item.py new file mode 100644 index 0000000..8563798 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .realtime_mcp_tool_call import RealtimeMcpToolCall +from .realtime_mcp_list_tools import RealtimeMcpListTools +from .realtime_mcp_approval_request import RealtimeMcpApprovalRequest +from .realtime_mcp_approval_response import RealtimeMcpApprovalResponse +from .realtime_conversation_item_user_message import RealtimeConversationItemUserMessage +from .realtime_conversation_item_function_call import RealtimeConversationItemFunctionCall +from .realtime_conversation_item_system_message import RealtimeConversationItemSystemMessage +from .realtime_conversation_item_assistant_message import RealtimeConversationItemAssistantMessage +from .realtime_conversation_item_function_call_output import RealtimeConversationItemFunctionCallOutput + +__all__ = ["ConversationItem"] + +ConversationItem: TypeAlias = Annotated[ + Union[ + RealtimeConversationItemSystemMessage, + RealtimeConversationItemUserMessage, + RealtimeConversationItemAssistantMessage, + RealtimeConversationItemFunctionCall, + RealtimeConversationItemFunctionCallOutput, + RealtimeMcpApprovalResponse, + RealtimeMcpListTools, + RealtimeMcpToolCall, + RealtimeMcpApprovalRequest, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_added.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_added.py new file mode 100644 index 0000000..68e0c8f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_added.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemAdded"] + + +class ConversationItemAdded(BaseModel): + """Sent by the server when an Item is added to the default Conversation. + + This can happen in several cases: + - When the client sends a `conversation.item.create` event. + - When the input audio buffer is committed. In this case the item will be a user message containing the audio from the buffer. + - When the model is generating a Response. In this case the `conversation.item.added` event will be sent when the model starts generating a specific Item, and thus it will not yet have any content (and `status` will be `in_progress`). + + The event will include the full content of the Item (except when model is generating a Response) except for audio data, which can be retrieved separately with a `conversation.item.retrieve` event if necessary. + """ + + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """A single item within a Realtime conversation.""" + + type: Literal["conversation.item.added"] + """The event type, must be `conversation.item.added`.""" + + previous_item_id: Optional[str] = None + """The ID of the item that precedes this one, if any. + + This is used to maintain ordering when items are inserted. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_create_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_create_event.py new file mode 100644 index 0000000..6d9b629 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_create_event.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemCreateEvent"] + + +class ConversationItemCreateEvent(BaseModel): + """ + Add a new Item to the Conversation's context, including messages, function + calls, and function call responses. This event can be used both to populate a + "history" of the conversation and to add new items mid-stream, but has the + current limitation that it cannot populate assistant audio messages. + + If successful, the server will emit a `conversation.item.added` event and, + when the item is finalized, a `conversation.item.done` event. Otherwise, an + `error` event will be sent. + """ + + item: ConversationItem + """A single item within a Realtime conversation.""" + + type: Literal["conversation.item.create"] + """The event type, must be `conversation.item.create`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" + + previous_item_id: Optional[str] = None + """The ID of the preceding item after which the new item will be inserted. + + If not set, the new item will be appended to the end of the conversation. + + If set to `root`, the new item will be added to the beginning of the + conversation. + + If set to an existing ID, it allows an item to be inserted mid-conversation. If + the ID cannot be found, an error will be returned and the item will not be + added. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_create_event_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_create_event_param.py new file mode 100644 index 0000000..955893b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_create_event_param.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .conversation_item_param import ConversationItemParam + +__all__ = ["ConversationItemCreateEventParam"] + + +class ConversationItemCreateEventParam(TypedDict, total=False): + """ + Add a new Item to the Conversation's context, including messages, function + calls, and function call responses. This event can be used both to populate a + "history" of the conversation and to add new items mid-stream, but has the + current limitation that it cannot populate assistant audio messages. + + If successful, the server will emit a `conversation.item.added` event and, + when the item is finalized, a `conversation.item.done` event. Otherwise, an + `error` event will be sent. + """ + + item: Required[ConversationItemParam] + """A single item within a Realtime conversation.""" + + type: Required[Literal["conversation.item.create"]] + """The event type, must be `conversation.item.create`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" + + previous_item_id: str + """The ID of the preceding item after which the new item will be inserted. + + If not set, the new item will be appended to the end of the conversation. + + If set to `root`, the new item will be added to the beginning of the + conversation. + + If set to an existing ID, it allows an item to be inserted mid-conversation. If + the ID cannot be found, an error will be returned and the item will not be + added. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_created_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_created_event.py new file mode 100644 index 0000000..97b2eb7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_created_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemCreatedEvent"] + + +class ConversationItemCreatedEvent(BaseModel): + """Returned when a conversation item is created. + + There are several scenarios that produce this event: + - The server is generating a Response, which if successful will produce + either one or two Items, which will be of type `message` + (role `assistant`) or type `function_call`. + - The input audio buffer has been committed, either by the client or the + server (in `server_vad` mode). The server will take the content of the + input audio buffer and add it to a new user message Item. + - The client has sent a `conversation.item.create` event to add a new Item + to the Conversation. + """ + + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """A single item within a Realtime conversation.""" + + type: Literal["conversation.item.created"] + """The event type, must be `conversation.item.created`.""" + + previous_item_id: Optional[str] = None + """ + The ID of the preceding item in the Conversation context, allows the client to + understand the order of the conversation. Can be `null` if the item has no + predecessor. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_delete_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_delete_event.py new file mode 100644 index 0000000..f683e43 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_delete_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemDeleteEvent"] + + +class ConversationItemDeleteEvent(BaseModel): + """Send this event when you want to remove any item from the conversation + history. + + The server will respond with a `conversation.item.deleted` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + + item_id: str + """The ID of the item to delete.""" + + type: Literal["conversation.item.delete"] + """The event type, must be `conversation.item.delete`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_delete_event_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_delete_event_param.py new file mode 100644 index 0000000..0c06b2b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_delete_event_param.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConversationItemDeleteEventParam"] + + +class ConversationItemDeleteEventParam(TypedDict, total=False): + """Send this event when you want to remove any item from the conversation + history. + + The server will respond with a `conversation.item.deleted` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + + item_id: Required[str] + """The ID of the item to delete.""" + + type: Required[Literal["conversation.item.delete"]] + """The event type, must be `conversation.item.delete`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_deleted_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_deleted_event.py new file mode 100644 index 0000000..21bcd93 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_deleted_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemDeletedEvent"] + + +class ConversationItemDeletedEvent(BaseModel): + """ + Returned when an item in the conversation is deleted by the client with a + `conversation.item.delete` event. This event is used to synchronize the + server's understanding of the conversation history with the client's view. + """ + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item that was deleted.""" + + type: Literal["conversation.item.deleted"] + """The event type, must be `conversation.item.deleted`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_done.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_done.py new file mode 100644 index 0000000..4622f64 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_done.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemDone"] + + +class ConversationItemDone(BaseModel): + """Returned when a conversation item is finalized. + + The event will include the full content of the Item except for audio data, which can be retrieved separately with a `conversation.item.retrieve` event if needed. + """ + + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """A single item within a Realtime conversation.""" + + type: Literal["conversation.item.done"] + """The event type, must be `conversation.item.done`.""" + + previous_item_id: Optional[str] = None + """The ID of the item that precedes this one, if any. + + This is used to maintain ordering when items are inserted. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py new file mode 100644 index 0000000..a2b98e1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py @@ -0,0 +1,106 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .log_prob_properties import LogProbProperties +from ..audio.transcription_language import TranscriptionLanguage + +__all__ = [ + "ConversationItemInputAudioTranscriptionCompletedEvent", + "Usage", + "UsageTranscriptTextUsageTokens", + "UsageTranscriptTextUsageTokensInputTokenDetails", + "UsageTranscriptTextUsageDuration", +] + + +class UsageTranscriptTextUsageTokensInputTokenDetails(BaseModel): + """Details about the input tokens billed for this request.""" + + audio_tokens: Optional[int] = None + """Number of audio tokens billed for this request.""" + + text_tokens: Optional[int] = None + """Number of text tokens billed for this request.""" + + +class UsageTranscriptTextUsageTokens(BaseModel): + """Usage statistics for models billed by token usage.""" + + input_tokens: int + """Number of input tokens billed for this request.""" + + output_tokens: int + """Number of output tokens generated.""" + + total_tokens: int + """Total number of tokens used (input + output).""" + + type: Literal["tokens"] + """The type of the usage object. Always `tokens` for this variant.""" + + input_token_details: Optional[UsageTranscriptTextUsageTokensInputTokenDetails] = None + """Details about the input tokens billed for this request.""" + + +class UsageTranscriptTextUsageDuration(BaseModel): + """Usage statistics for models billed by audio input duration.""" + + seconds: float + """Duration of the input audio in seconds.""" + + type: Literal["duration"] + """The type of the usage object. Always `duration` for this variant.""" + + +Usage: TypeAlias = Union[UsageTranscriptTextUsageTokens, UsageTranscriptTextUsageDuration] + + +class ConversationItemInputAudioTranscriptionCompletedEvent(BaseModel): + """ + This event is the output of audio transcription for user audio written to the + user audio buffer. Transcription begins when the input audio buffer is + committed by the client or server (when VAD is enabled). Transcription runs + asynchronously with Response creation, so this event may come before or after + the Response events. + + Realtime API models accept audio natively, and thus input transcription is a + separate process run on a separate ASR (Automatic Speech Recognition) model. + The transcript may diverge somewhat from the model's interpretation, and + should be treated as a rough guide. + """ + + content_index: int + """The index of the content part containing the audio.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item containing the audio that is being transcribed.""" + + transcript: str + """The transcribed text.""" + + type: Literal["conversation.item.input_audio_transcription.completed"] + """ + The event type, must be `conversation.item.input_audio_transcription.completed`. + """ + + usage: Usage + """ + Usage statistics for the transcription, this is billed according to the ASR + model's pricing rather than the realtime model's pricing. + """ + + languages: Optional[List[TranscriptionLanguage]] = None + """The languages detected in the audio. + + Returned by `gpt-transcribe`. An empty array indicates that no language could be + reliably detected. + """ + + logprobs: Optional[List[LogProbProperties]] = None + """The log probabilities of the transcription.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py new file mode 100644 index 0000000..6e528de --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .log_prob_properties import LogProbProperties + +__all__ = ["ConversationItemInputAudioTranscriptionDeltaEvent"] + + +class ConversationItemInputAudioTranscriptionDeltaEvent(BaseModel): + """ + Returned when the text value of an input audio transcription content part is updated with incremental transcription results. + """ + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item containing the audio that is being transcribed.""" + + type: Literal["conversation.item.input_audio_transcription.delta"] + """The event type, must be `conversation.item.input_audio_transcription.delta`.""" + + content_index: Optional[int] = None + """The index of the content part in the item's content array.""" + + delta: Optional[str] = None + """The text delta.""" + + logprobs: Optional[List[LogProbProperties]] = None + """The log probabilities of the transcription. + + These can be enabled by configuring the session with + `"include": ["item.input_audio_transcription.logprobs"]`. Each entry in the + array corresponds a log probability of which token would be selected for this + chunk of transcription. This can help to identify if it was possible there were + multiple valid options for a given chunk of transcription. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py new file mode 100644 index 0000000..d927148 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemInputAudioTranscriptionFailedEvent", "Error"] + + +class Error(BaseModel): + """Details of the transcription error.""" + + code: Optional[str] = None + """Error code, if any.""" + + message: Optional[str] = None + """A human-readable error message.""" + + param: Optional[str] = None + """Parameter related to the error, if any.""" + + type: Optional[str] = None + """The type of error.""" + + +class ConversationItemInputAudioTranscriptionFailedEvent(BaseModel): + """ + Returned when input audio transcription is configured, and a transcription + request for a user message failed. These events are separate from other + `error` events so that the client can identify the related Item. + """ + + content_index: int + """The index of the content part containing the audio.""" + + error: Error + """Details of the transcription error.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item.""" + + type: Literal["conversation.item.input_audio_transcription.failed"] + """The event type, must be `conversation.item.input_audio_transcription.failed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_input_audio_transcription_segment.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_input_audio_transcription_segment.py new file mode 100644 index 0000000..611bd0d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_input_audio_transcription_segment.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemInputAudioTranscriptionSegment"] + + +class ConversationItemInputAudioTranscriptionSegment(BaseModel): + """Returned when an input audio transcription segment is identified for an item.""" + + id: str + """The segment identifier.""" + + content_index: int + """The index of the input audio content part within the item.""" + + end: float + """End time of the segment in seconds.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item containing the input audio content.""" + + speaker: str + """The detected speaker label for this segment.""" + + start: float + """Start time of the segment in seconds.""" + + text: str + """The text for this segment.""" + + type: Literal["conversation.item.input_audio_transcription.segment"] + """The event type, must be `conversation.item.input_audio_transcription.segment`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_param.py new file mode 100644 index 0000000..2a5429d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_param.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .realtime_mcp_tool_call_param import RealtimeMcpToolCallParam +from .realtime_mcp_list_tools_param import RealtimeMcpListToolsParam +from .realtime_mcp_approval_request_param import RealtimeMcpApprovalRequestParam +from .realtime_mcp_approval_response_param import RealtimeMcpApprovalResponseParam +from .realtime_conversation_item_user_message_param import RealtimeConversationItemUserMessageParam +from .realtime_conversation_item_function_call_param import RealtimeConversationItemFunctionCallParam +from .realtime_conversation_item_system_message_param import RealtimeConversationItemSystemMessageParam +from .realtime_conversation_item_assistant_message_param import RealtimeConversationItemAssistantMessageParam +from .realtime_conversation_item_function_call_output_param import RealtimeConversationItemFunctionCallOutputParam + +__all__ = ["ConversationItemParam"] + +ConversationItemParam: TypeAlias = Union[ + RealtimeConversationItemSystemMessageParam, + RealtimeConversationItemUserMessageParam, + RealtimeConversationItemAssistantMessageParam, + RealtimeConversationItemFunctionCallParam, + RealtimeConversationItemFunctionCallOutputParam, + RealtimeMcpApprovalResponseParam, + RealtimeMcpListToolsParam, + RealtimeMcpToolCallParam, + RealtimeMcpApprovalRequestParam, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_retrieve_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_retrieve_event.py new file mode 100644 index 0000000..ec6fda9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_retrieve_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemRetrieveEvent"] + + +class ConversationItemRetrieveEvent(BaseModel): + """ + Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. + The server will respond with a `conversation.item.retrieved` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + + item_id: str + """The ID of the item to retrieve.""" + + type: Literal["conversation.item.retrieve"] + """The event type, must be `conversation.item.retrieve`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_retrieve_event_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_retrieve_event_param.py new file mode 100644 index 0000000..9dcd398 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_retrieve_event_param.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConversationItemRetrieveEventParam"] + + +class ConversationItemRetrieveEventParam(TypedDict, total=False): + """ + Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. + The server will respond with a `conversation.item.retrieved` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + + item_id: Required[str] + """The ID of the item to retrieve.""" + + type: Required[Literal["conversation.item.retrieve"]] + """The event type, must be `conversation.item.retrieve`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_truncate_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_truncate_event.py new file mode 100644 index 0000000..55a52a2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_truncate_event.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemTruncateEvent"] + + +class ConversationItemTruncateEvent(BaseModel): + """Send this event to truncate a previous assistant message’s audio. + + The server + will produce audio faster than realtime, so this event is useful when the user + interrupts to truncate audio that has already been sent to the client but not + yet played. This will synchronize the server's understanding of the audio with + the client's playback. + + Truncating audio will delete the server-side text transcript to ensure there + is not text in the context that hasn't been heard by the user. + + If successful, the server will respond with a `conversation.item.truncated` + event. + """ + + audio_end_ms: int + """Inclusive duration up to which audio is truncated, in milliseconds. + + If the audio_end_ms is greater than the actual audio duration, the server will + respond with an error. + """ + + content_index: int + """The index of the content part to truncate. Set this to `0`.""" + + item_id: str + """The ID of the assistant message item to truncate. + + Only assistant message items can be truncated. + """ + + type: Literal["conversation.item.truncate"] + """The event type, must be `conversation.item.truncate`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_truncate_event_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_truncate_event_param.py new file mode 100644 index 0000000..2184da2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_truncate_event_param.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConversationItemTruncateEventParam"] + + +class ConversationItemTruncateEventParam(TypedDict, total=False): + """Send this event to truncate a previous assistant message’s audio. + + The server + will produce audio faster than realtime, so this event is useful when the user + interrupts to truncate audio that has already been sent to the client but not + yet played. This will synchronize the server's understanding of the audio with + the client's playback. + + Truncating audio will delete the server-side text transcript to ensure there + is not text in the context that hasn't been heard by the user. + + If successful, the server will respond with a `conversation.item.truncated` + event. + """ + + audio_end_ms: Required[int] + """Inclusive duration up to which audio is truncated, in milliseconds. + + If the audio_end_ms is greater than the actual audio duration, the server will + respond with an error. + """ + + content_index: Required[int] + """The index of the content part to truncate. Set this to `0`.""" + + item_id: Required[str] + """The ID of the assistant message item to truncate. + + Only assistant message items can be truncated. + """ + + type: Required[Literal["conversation.item.truncate"]] + """The event type, must be `conversation.item.truncate`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_truncated_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_truncated_event.py new file mode 100644 index 0000000..7aa1a18 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/conversation_item_truncated_event.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemTruncatedEvent"] + + +class ConversationItemTruncatedEvent(BaseModel): + """ + Returned when an earlier assistant audio message item is truncated by the + client with a `conversation.item.truncate` event. This event is used to + synchronize the server's understanding of the audio with the client's playback. + + This action will truncate the audio and remove the server-side text transcript + to ensure there is no text in the context that hasn't been heard by the user. + """ + + audio_end_ms: int + """The duration up to which the audio was truncated, in milliseconds.""" + + content_index: int + """The index of the content part that was truncated.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the assistant message item that was truncated.""" + + type: Literal["conversation.item.truncated"] + """The event type, must be `conversation.item.truncated`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_append_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_append_event.py new file mode 100644 index 0000000..f978b92 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_append_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferAppendEvent"] + + +class InputAudioBufferAppendEvent(BaseModel): + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. A "commit" will create a new + user message item in the conversation history from the buffer content and clear the buffer. + Input audio transcription (if enabled) will be generated when the buffer is committed. + + If VAD is enabled the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. Input audio noise reduction operates on writes to the audio buffer. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike most other client events, the server will + not send a confirmation response to this event. + """ + + audio: str + """Base64-encoded audio bytes. + + This must be in the format specified by the `input_audio_format` field in the + session configuration. + """ + + type: Literal["input_audio_buffer.append"] + """The event type, must be `input_audio_buffer.append`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_append_event_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_append_event_param.py new file mode 100644 index 0000000..cd765c2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_append_event_param.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioBufferAppendEventParam"] + + +class InputAudioBufferAppendEventParam(TypedDict, total=False): + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. A "commit" will create a new + user message item in the conversation history from the buffer content and clear the buffer. + Input audio transcription (if enabled) will be generated when the buffer is committed. + + If VAD is enabled the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. Input audio noise reduction operates on writes to the audio buffer. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike most other client events, the server will + not send a confirmation response to this event. + """ + + audio: Required[str] + """Base64-encoded audio bytes. + + This must be in the format specified by the `input_audio_format` field in the + session configuration. + """ + + type: Required[Literal["input_audio_buffer.append"]] + """The event type, must be `input_audio_buffer.append`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_clear_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_clear_event.py new file mode 100644 index 0000000..0814637 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_clear_event.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferClearEvent"] + + +class InputAudioBufferClearEvent(BaseModel): + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + + type: Literal["input_audio_buffer.clear"] + """The event type, must be `input_audio_buffer.clear`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_clear_event_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_clear_event_param.py new file mode 100644 index 0000000..0cbce71 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_clear_event_param.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioBufferClearEventParam"] + + +class InputAudioBufferClearEventParam(TypedDict, total=False): + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + + type: Required[Literal["input_audio_buffer.clear"]] + """The event type, must be `input_audio_buffer.clear`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_cleared_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_cleared_event.py new file mode 100644 index 0000000..3c6d5e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_cleared_event.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferClearedEvent"] + + +class InputAudioBufferClearedEvent(BaseModel): + """ + Returned when the input audio buffer is cleared by the client with a + `input_audio_buffer.clear` event. + """ + + event_id: str + """The unique ID of the server event.""" + + type: Literal["input_audio_buffer.cleared"] + """The event type, must be `input_audio_buffer.cleared`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_commit_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_commit_event.py new file mode 100644 index 0000000..b68301b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_commit_event.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferCommitEvent"] + + +class InputAudioBufferCommitEvent(BaseModel): + """ + Send this event to commit the user input audio buffer, which will create a new user message item in the conversation. This event will produce an error if the input audio buffer is empty. When in Server VAD mode, the client does not need to send this event, the server will commit the audio buffer automatically. + + Committing the input audio buffer will trigger input audio transcription (if enabled in session configuration), but it will not create a response from the model. The server will respond with an `input_audio_buffer.committed` event. + """ + + type: Literal["input_audio_buffer.commit"] + """The event type, must be `input_audio_buffer.commit`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_commit_event_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_commit_event_param.py new file mode 100644 index 0000000..8777a56 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_commit_event_param.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioBufferCommitEventParam"] + + +class InputAudioBufferCommitEventParam(TypedDict, total=False): + """ + Send this event to commit the user input audio buffer, which will create a new user message item in the conversation. This event will produce an error if the input audio buffer is empty. When in Server VAD mode, the client does not need to send this event, the server will commit the audio buffer automatically. + + Committing the input audio buffer will trigger input audio transcription (if enabled in session configuration), but it will not create a response from the model. The server will respond with an `input_audio_buffer.committed` event. + """ + + type: Required[Literal["input_audio_buffer.commit"]] + """The event type, must be `input_audio_buffer.commit`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_committed_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_committed_event.py new file mode 100644 index 0000000..3eb3975 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_committed_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferCommittedEvent"] + + +class InputAudioBufferCommittedEvent(BaseModel): + """ + Returned when an input audio buffer is committed, either by the client or + automatically in server VAD mode. The `item_id` property is the ID of the user + message item that will be created, thus a `conversation.item.created` event + will also be sent to the client. + """ + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item that will be created.""" + + type: Literal["input_audio_buffer.committed"] + """The event type, must be `input_audio_buffer.committed`.""" + + previous_item_id: Optional[str] = None + """ + The ID of the preceding item after which the new item will be inserted. Can be + `null` if the item has no predecessor. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py new file mode 100644 index 0000000..ec81282 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferDtmfEventReceivedEvent"] + + +class InputAudioBufferDtmfEventReceivedEvent(BaseModel): + """**SIP Only:** Returned when an DTMF event is received. + + A DTMF event is a message that + represents a telephone keypad press (0–9, *, #, A–D). The `event` property + is the keypad that the user press. The `received_at` is the UTC Unix Timestamp + that the server received the event. + """ + + event: str + """The telephone keypad that was pressed by the user.""" + + received_at: int + """UTC Unix Timestamp when DTMF Event was received by server.""" + + type: Literal["input_audio_buffer.dtmf_event_received"] + """The event type, must be `input_audio_buffer.dtmf_event_received`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_speech_started_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_speech_started_event.py new file mode 100644 index 0000000..ea9ff3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_speech_started_event.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferSpeechStartedEvent"] + + +class InputAudioBufferSpeechStartedEvent(BaseModel): + """ + Sent by the server when in `server_vad` mode to indicate that speech has been + detected in the audio buffer. This can happen any time audio is added to the + buffer (unless speech is already detected). The client may want to use this + event to interrupt audio playback or provide visual feedback to the user. + + The client should expect to receive a `input_audio_buffer.speech_stopped` event + when speech stops. The `item_id` property is the ID of the user message item + that will be created when speech stops and will also be included in the + `input_audio_buffer.speech_stopped` event (unless the client manually commits + the audio buffer during VAD activation). + """ + + audio_start_ms: int + """ + Milliseconds from the start of all audio written to the buffer during the + session when speech was first detected. This will correspond to the beginning of + audio sent to the model, and thus includes the `prefix_padding_ms` configured in + the Session. + """ + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item that will be created when speech stops.""" + + type: Literal["input_audio_buffer.speech_started"] + """The event type, must be `input_audio_buffer.speech_started`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_speech_stopped_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_speech_stopped_event.py new file mode 100644 index 0000000..9e33d70 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_speech_stopped_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferSpeechStoppedEvent"] + + +class InputAudioBufferSpeechStoppedEvent(BaseModel): + """ + Returned in `server_vad` mode when the server detects the end of speech in + the audio buffer. The server will also send an `conversation.item.created` + event with the user message item that is created from the audio buffer. + """ + + audio_end_ms: int + """Milliseconds since the session started when speech stopped. + + This will correspond to the end of audio sent to the model, and thus includes + the `min_silence_duration_ms` configured in the Session. + """ + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item that will be created.""" + + type: Literal["input_audio_buffer.speech_stopped"] + """The event type, must be `input_audio_buffer.speech_stopped`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_timeout_triggered.py b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_timeout_triggered.py new file mode 100644 index 0000000..ef54c5a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/input_audio_buffer_timeout_triggered.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferTimeoutTriggered"] + + +class InputAudioBufferTimeoutTriggered(BaseModel): + """Returned when the Server VAD timeout is triggered for the input audio buffer. + + This is configured + with `idle_timeout_ms` in the `turn_detection` settings of the session, and it indicates that + there hasn't been any speech detected for the configured duration. + + The `audio_start_ms` and `audio_end_ms` fields indicate the segment of audio after the last + model response up to the triggering time, as an offset from the beginning of audio written + to the input audio buffer. This means it demarcates the segment of audio that was silent and + the difference between the start and end values will roughly match the configured timeout. + + The empty audio will be committed to the conversation as an `input_audio` item (there will be a + `input_audio_buffer.committed` event) and a model response will be generated. There may be speech + that didn't trigger VAD but is still detected by the model, so the model may respond with + something relevant to the conversation or a prompt to continue speaking. + """ + + audio_end_ms: int + """ + Millisecond offset of audio written to the input audio buffer at the time the + timeout was triggered. + """ + + audio_start_ms: int + """ + Millisecond offset of audio written to the input audio buffer that was after the + playback time of the last model response. + """ + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item associated with this segment.""" + + type: Literal["input_audio_buffer.timeout_triggered"] + """The event type, must be `input_audio_buffer.timeout_triggered`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/log_prob_properties.py b/venv/lib/python3.12/site-packages/openai/types/realtime/log_prob_properties.py new file mode 100644 index 0000000..b9302ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/log_prob_properties.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel + +__all__ = ["LogProbProperties"] + + +class LogProbProperties(BaseModel): + """A log probability object.""" + + token: str + """The token that was used to generate the log probability.""" + + bytes: List[int] + """The bytes that were used to generate the log probability.""" + + logprob: float + """The log probability of the token.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/mcp_list_tools_completed.py b/venv/lib/python3.12/site-packages/openai/types/realtime/mcp_list_tools_completed.py new file mode 100644 index 0000000..d0e1fc5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/mcp_list_tools_completed.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["McpListToolsCompleted"] + + +class McpListToolsCompleted(BaseModel): + """Returned when listing MCP tools has completed for an item.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP list tools item.""" + + type: Literal["mcp_list_tools.completed"] + """The event type, must be `mcp_list_tools.completed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/mcp_list_tools_failed.py b/venv/lib/python3.12/site-packages/openai/types/realtime/mcp_list_tools_failed.py new file mode 100644 index 0000000..a15375f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/mcp_list_tools_failed.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["McpListToolsFailed"] + + +class McpListToolsFailed(BaseModel): + """Returned when listing MCP tools has failed for an item.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP list tools item.""" + + type: Literal["mcp_list_tools.failed"] + """The event type, must be `mcp_list_tools.failed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/mcp_list_tools_in_progress.py b/venv/lib/python3.12/site-packages/openai/types/realtime/mcp_list_tools_in_progress.py new file mode 100644 index 0000000..188ba46 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/mcp_list_tools_in_progress.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["McpListToolsInProgress"] + + +class McpListToolsInProgress(BaseModel): + """Returned when listing MCP tools is in progress for an item.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP list tools item.""" + + type: Literal["mcp_list_tools.in_progress"] + """The event type, must be `mcp_list_tools.in_progress`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/noise_reduction_type.py b/venv/lib/python3.12/site-packages/openai/types/realtime/noise_reduction_type.py new file mode 100644 index 0000000..6a6bc2a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/noise_reduction_type.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["NoiseReductionType"] + +NoiseReductionType: TypeAlias = Literal["near_field", "far_field"] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/output_audio_buffer_clear_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/output_audio_buffer_clear_event.py new file mode 100644 index 0000000..5190d1a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/output_audio_buffer_clear_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["OutputAudioBufferClearEvent"] + + +class OutputAudioBufferClearEvent(BaseModel): + """**WebRTC/SIP Only:** Emit to cut off the current audio response. + + This will trigger the server to + stop generating audio and emit a `output_audio_buffer.cleared` event. This + event should be preceded by a `response.cancel` client event to stop the + generation of the current response. + [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + + type: Literal["output_audio_buffer.clear"] + """The event type, must be `output_audio_buffer.clear`.""" + + event_id: Optional[str] = None + """The unique ID of the client event used for error handling.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/output_audio_buffer_clear_event_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/output_audio_buffer_clear_event_param.py new file mode 100644 index 0000000..5bad1df --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/output_audio_buffer_clear_event_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["OutputAudioBufferClearEventParam"] + + +class OutputAudioBufferClearEventParam(TypedDict, total=False): + """**WebRTC/SIP Only:** Emit to cut off the current audio response. + + This will trigger the server to + stop generating audio and emit a `output_audio_buffer.cleared` event. This + event should be preceded by a `response.cancel` client event to stop the + generation of the current response. + [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + + type: Required[Literal["output_audio_buffer.clear"]] + """The event type, must be `output_audio_buffer.clear`.""" + + event_id: str + """The unique ID of the client event used for error handling.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/rate_limits_updated_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/rate_limits_updated_event.py new file mode 100644 index 0000000..a6b335f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/rate_limits_updated_event.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RateLimitsUpdatedEvent", "RateLimit"] + + +class RateLimit(BaseModel): + limit: Optional[int] = None + """The maximum allowed value for the rate limit.""" + + name: Optional[Literal["requests", "tokens"]] = None + """The name of the rate limit (`requests`, `tokens`).""" + + remaining: Optional[int] = None + """The remaining value before the limit is reached.""" + + reset_seconds: Optional[float] = None + """Seconds until the rate limit resets.""" + + +class RateLimitsUpdatedEvent(BaseModel): + """Emitted at the beginning of a Response to indicate the updated rate limits. + + + When a Response is created some tokens will be "reserved" for the output + tokens, the rate limits shown here reflect that reservation, which is then + adjusted accordingly once the Response is completed. + """ + + event_id: str + """The unique ID of the server event.""" + + rate_limits: List[RateLimit] + """List of rate limit information.""" + + type: Literal["rate_limits.updated"] + """The event type, must be `rate_limits.updated`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config.py new file mode 100644 index 0000000..0f52094 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .realtime_audio_config_input import RealtimeAudioConfigInput +from .realtime_audio_config_output import RealtimeAudioConfigOutput + +__all__ = ["RealtimeAudioConfig"] + + +class RealtimeAudioConfig(BaseModel): + """Configuration for input and output audio.""" + + input: Optional[RealtimeAudioConfigInput] = None + + output: Optional[RealtimeAudioConfigOutput] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_input.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_input.py new file mode 100644 index 0000000..1c11fef --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_input.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .audio_transcription import AudioTranscription +from .noise_reduction_type import NoiseReductionType +from .realtime_audio_formats import RealtimeAudioFormats +from .realtime_audio_input_turn_detection import RealtimeAudioInputTurnDetection + +__all__ = ["RealtimeAudioConfigInput", "NoiseReduction"] + + +class NoiseReduction(BaseModel): + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. + Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model. + Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio. + """ + + type: Optional[NoiseReductionType] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class RealtimeAudioConfigInput(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The format of the input audio.""" + + noise_reduction: Optional[NoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + transcription: Optional[AudioTranscription] = None + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + turn_detection: Optional[RealtimeAudioInputTurnDetection] = None + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. + + Server VAD means that the model will detect the start and end of speech based on + audio volume and respond at the end of user speech. + + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + + For `gpt-realtime-whisper` transcription sessions, turn detection must be set to + `null`; VAD is not supported. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_input_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_input_param.py new file mode 100644 index 0000000..36b911c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_input_param.py @@ -0,0 +1,75 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +from .noise_reduction_type import NoiseReductionType +from .audio_transcription_param import AudioTranscriptionParam +from .realtime_audio_formats_param import RealtimeAudioFormatsParam +from .realtime_audio_input_turn_detection_param import RealtimeAudioInputTurnDetectionParam + +__all__ = ["RealtimeAudioConfigInputParam", "NoiseReduction"] + + +class NoiseReduction(TypedDict, total=False): + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. + Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model. + Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio. + """ + + type: NoiseReductionType + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class RealtimeAudioConfigInputParam(TypedDict, total=False): + format: RealtimeAudioFormatsParam + """The format of the input audio.""" + + noise_reduction: NoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + transcription: AudioTranscriptionParam + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + turn_detection: Optional[RealtimeAudioInputTurnDetectionParam] + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. + + Server VAD means that the model will detect the start and end of speech based on + audio volume and respond at the end of user speech. + + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + + For `gpt-realtime-whisper` transcription sessions, turn detection must be set to + `null`; VAD is not supported. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_output.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_output.py new file mode 100644 index 0000000..d9efcdd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_output.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .realtime_audio_formats import RealtimeAudioFormats + +__all__ = ["RealtimeAudioConfigOutput", "Voice", "VoiceID"] + + +class VoiceID(BaseModel): + """Custom voice reference.""" + + id: str + """The custom voice ID, e.g. `voice_1234`.""" + + +Voice: TypeAlias = Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], VoiceID +] + + +class RealtimeAudioConfigOutput(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The format of the output audio.""" + + speed: Optional[float] = None + """ + The speed of the model's spoken response as a multiple of the original speed. + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + + This parameter is a post-processing adjustment to the audio after it is + generated, it's also possible to prompt the model to speak faster or slower. + """ + + voice: Optional[Voice] = None + """The voice the model uses to respond. + + Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, + `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice + object with an `id`, for example `{ "id": "voice_1234" }`. Voice cannot be + changed during the session once the model has responded with audio at least + once. We recommend `marin` and `cedar` for best quality. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_output_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_output_param.py new file mode 100644 index 0000000..e6680e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_output_param.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .realtime_audio_formats_param import RealtimeAudioFormatsParam + +__all__ = ["RealtimeAudioConfigOutputParam", "Voice", "VoiceID"] + + +class VoiceID(TypedDict, total=False): + """Custom voice reference.""" + + id: Required[str] + """The custom voice ID, e.g. `voice_1234`.""" + + +Voice: TypeAlias = Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], VoiceID +] + + +class RealtimeAudioConfigOutputParam(TypedDict, total=False): + format: RealtimeAudioFormatsParam + """The format of the output audio.""" + + speed: float + """ + The speed of the model's spoken response as a multiple of the original speed. + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + + This parameter is a post-processing adjustment to the audio after it is + generated, it's also possible to prompt the model to speak faster or slower. + """ + + voice: Voice + """The voice the model uses to respond. + + Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, + `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice + object with an `id`, for example `{ "id": "voice_1234" }`. Voice cannot be + changed during the session once the model has responded with audio at least + once. We recommend `marin` and `cedar` for best quality. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_param.py new file mode 100644 index 0000000..477570a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_config_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .realtime_audio_config_input_param import RealtimeAudioConfigInputParam +from .realtime_audio_config_output_param import RealtimeAudioConfigOutputParam + +__all__ = ["RealtimeAudioConfigParam"] + + +class RealtimeAudioConfigParam(TypedDict, total=False): + """Configuration for input and output audio.""" + + input: RealtimeAudioConfigInputParam + + output: RealtimeAudioConfigOutputParam diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_formats.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_formats.py new file mode 100644 index 0000000..c4591b0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_formats.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["RealtimeAudioFormats", "AudioPCM", "AudioPCMU", "AudioPCMA"] + + +class AudioPCM(BaseModel): + """The PCM audio format. Only a 24kHz sample rate is supported.""" + + rate: Optional[Literal[24000]] = None + """The sample rate of the audio. Always `24000`.""" + + type: Optional[Literal["audio/pcm"]] = None + """The audio format. Always `audio/pcm`.""" + + +class AudioPCMU(BaseModel): + """The G.711 μ-law format.""" + + type: Optional[Literal["audio/pcmu"]] = None + """The audio format. Always `audio/pcmu`.""" + + +class AudioPCMA(BaseModel): + """The G.711 A-law format.""" + + type: Optional[Literal["audio/pcma"]] = None + """The audio format. Always `audio/pcma`.""" + + +RealtimeAudioFormats: TypeAlias = Annotated[Union[AudioPCM, AudioPCMU, AudioPCMA], PropertyInfo(discriminator="type")] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_formats_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_formats_param.py new file mode 100644 index 0000000..37ffdb6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_formats_param.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias, TypedDict + +__all__ = ["RealtimeAudioFormatsParam", "AudioPCM", "AudioPCMU", "AudioPCMA"] + + +class AudioPCM(TypedDict, total=False): + """The PCM audio format. Only a 24kHz sample rate is supported.""" + + rate: Literal[24000] + """The sample rate of the audio. Always `24000`.""" + + type: Literal["audio/pcm"] + """The audio format. Always `audio/pcm`.""" + + +class AudioPCMU(TypedDict, total=False): + """The G.711 μ-law format.""" + + type: Literal["audio/pcmu"] + """The audio format. Always `audio/pcmu`.""" + + +class AudioPCMA(TypedDict, total=False): + """The G.711 A-law format.""" + + type: Literal["audio/pcma"] + """The audio format. Always `audio/pcma`.""" + + +RealtimeAudioFormatsParam: TypeAlias = Union[AudioPCM, AudioPCMU, AudioPCMA] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_input_turn_detection.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_input_turn_detection.py new file mode 100644 index 0000000..e864a22 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_input_turn_detection.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["RealtimeAudioInputTurnDetection", "ServerVad", "SemanticVad"] + + +class ServerVad(BaseModel): + """ + Server-side voice activity detection (VAD) which flips on when user speech is detected and off after a period of silence. + """ + + type: Literal["server_vad"] + """Type of turn detection, `server_vad` to turn on simple Server VAD.""" + + create_response: Optional[bool] = None + """Whether or not to automatically generate a response when a VAD stop event + occurs. + + If `interrupt_response` is set to `false` this may fail to create a response if + the model is already responding. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. + """ + + idle_timeout_ms: Optional[int] = None + """Optional timeout after which a model response will be triggered automatically. + + This is useful for situations in which a long pause from the user is unexpected, + such as a phone call. The model will effectively prompt the user to continue the + conversation based on the current context. + + The timeout value will be applied after the last model response's audio has + finished playing, i.e. it's set to the `response.done` time plus audio playback + duration. + + An `input_audio_buffer.timeout_triggered` event (plus events associated with the + Response) will be emitted when the timeout is reached. Idle timeout is currently + only supported for `server_vad` mode. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt (cancel) any ongoing response with + output to the default conversation (i.e. `conversation` of `auto`) when a VAD + start event occurs. If `true` then the response will be cancelled, otherwise it + will continue until complete. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. + """ + + prefix_padding_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: Optional[float] = None + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + +class SemanticVad(BaseModel): + """ + Server-side semantic turn detection which uses a model to determine when the user has finished speaking. + """ + + type: Literal["semantic_vad"] + """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" + + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, + 4s, and 2s respectively. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + +RealtimeAudioInputTurnDetection: TypeAlias = Annotated[ + Union[ServerVad, SemanticVad, None], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_input_turn_detection_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_input_turn_detection_param.py new file mode 100644 index 0000000..3429c9e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_audio_input_turn_detection_param.py @@ -0,0 +1,112 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["RealtimeAudioInputTurnDetectionParam", "ServerVad", "SemanticVad"] + + +class ServerVad(TypedDict, total=False): + """ + Server-side voice activity detection (VAD) which flips on when user speech is detected and off after a period of silence. + """ + + type: Required[Literal["server_vad"]] + """Type of turn detection, `server_vad` to turn on simple Server VAD.""" + + create_response: bool + """Whether or not to automatically generate a response when a VAD stop event + occurs. + + If `interrupt_response` is set to `false` this may fail to create a response if + the model is already responding. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. + """ + + idle_timeout_ms: Optional[int] + """Optional timeout after which a model response will be triggered automatically. + + This is useful for situations in which a long pause from the user is unexpected, + such as a phone call. The model will effectively prompt the user to continue the + conversation based on the current context. + + The timeout value will be applied after the last model response's audio has + finished playing, i.e. it's set to the `response.done` time plus audio playback + duration. + + An `input_audio_buffer.timeout_triggered` event (plus events associated with the + Response) will be emitted when the timeout is reached. Idle timeout is currently + only supported for `server_vad` mode. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt (cancel) any ongoing response with + output to the default conversation (i.e. `conversation` of `auto`) when a VAD + start event occurs. If `true` then the response will be cancelled, otherwise it + will continue until complete. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. + """ + + prefix_padding_ms: int + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: int + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: float + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + +class SemanticVad(TypedDict, total=False): + """ + Server-side semantic turn detection which uses a model to determine when the user has finished speaking. + """ + + type: Required[Literal["semantic_vad"]] + """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" + + create_response: bool + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, + 4s, and 2s respectively. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + +RealtimeAudioInputTurnDetectionParam: TypeAlias = Union[ServerVad, SemanticVad] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_client_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_client_event.py new file mode 100644 index 0000000..d351f02 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_client_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .session_update_event import SessionUpdateEvent +from .response_cancel_event import ResponseCancelEvent +from .response_create_event import ResponseCreateEvent +from .conversation_item_create_event import ConversationItemCreateEvent +from .conversation_item_delete_event import ConversationItemDeleteEvent +from .input_audio_buffer_clear_event import InputAudioBufferClearEvent +from .input_audio_buffer_append_event import InputAudioBufferAppendEvent +from .input_audio_buffer_commit_event import InputAudioBufferCommitEvent +from .output_audio_buffer_clear_event import OutputAudioBufferClearEvent +from .conversation_item_retrieve_event import ConversationItemRetrieveEvent +from .conversation_item_truncate_event import ConversationItemTruncateEvent + +__all__ = ["RealtimeClientEvent"] + +RealtimeClientEvent: TypeAlias = Annotated[ + Union[ + ConversationItemCreateEvent, + ConversationItemDeleteEvent, + ConversationItemRetrieveEvent, + ConversationItemTruncateEvent, + InputAudioBufferAppendEvent, + InputAudioBufferClearEvent, + OutputAudioBufferClearEvent, + InputAudioBufferCommitEvent, + ResponseCancelEvent, + ResponseCreateEvent, + SessionUpdateEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_client_event_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_client_event_param.py new file mode 100644 index 0000000..30fa229 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_client_event_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .session_update_event_param import SessionUpdateEventParam +from .response_cancel_event_param import ResponseCancelEventParam +from .response_create_event_param import ResponseCreateEventParam +from .conversation_item_create_event_param import ConversationItemCreateEventParam +from .conversation_item_delete_event_param import ConversationItemDeleteEventParam +from .input_audio_buffer_clear_event_param import InputAudioBufferClearEventParam +from .input_audio_buffer_append_event_param import InputAudioBufferAppendEventParam +from .input_audio_buffer_commit_event_param import InputAudioBufferCommitEventParam +from .output_audio_buffer_clear_event_param import OutputAudioBufferClearEventParam +from .conversation_item_retrieve_event_param import ConversationItemRetrieveEventParam +from .conversation_item_truncate_event_param import ConversationItemTruncateEventParam + +__all__ = ["RealtimeClientEventParam"] + +RealtimeClientEventParam: TypeAlias = Union[ + ConversationItemCreateEventParam, + ConversationItemDeleteEventParam, + ConversationItemRetrieveEventParam, + ConversationItemTruncateEventParam, + InputAudioBufferAppendEventParam, + InputAudioBufferClearEventParam, + OutputAudioBufferClearEventParam, + InputAudioBufferCommitEventParam, + ResponseCancelEventParam, + ResponseCreateEventParam, + SessionUpdateEventParam, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_connect_params.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_connect_params.py new file mode 100644 index 0000000..346a17f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_connect_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["RealtimeConnectParams"] + + +class RealtimeConnectParams(TypedDict, total=False): + call_id: str + + model: str diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_assistant_message.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_assistant_message.py new file mode 100644 index 0000000..d430fc9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_assistant_message.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeConversationItemAssistantMessage", "Content"] + + +class Content(BaseModel): + audio: Optional[str] = None + """ + Base64-encoded audio bytes, these will be parsed as the format specified in the + session output audio type configuration. This defaults to PCM 16-bit 24kHz mono + if not specified. + """ + + text: Optional[str] = None + """The text content.""" + + transcript: Optional[str] = None + """ + The transcript of the audio content, this will always be present if the output + type is `audio`. + """ + + type: Optional[Literal["output_text", "output_audio"]] = None + """ + The content type, `output_text` or `output_audio` depending on the session + `output_modalities` configuration. + """ + + +class RealtimeConversationItemAssistantMessage(BaseModel): + """An assistant message item in a Realtime conversation.""" + + content: List[Content] + """The content of the message.""" + + role: Literal["assistant"] + """The role of the message sender. Always `assistant`.""" + + type: Literal["message"] + """The type of the item. Always `message`.""" + + id: Optional[str] = None + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ + + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item. Has no effect on the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_assistant_message_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_assistant_message_param.py new file mode 100644 index 0000000..d75278b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_assistant_message_param.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeConversationItemAssistantMessageParam", "Content"] + + +class Content(TypedDict, total=False): + audio: str + """ + Base64-encoded audio bytes, these will be parsed as the format specified in the + session output audio type configuration. This defaults to PCM 16-bit 24kHz mono + if not specified. + """ + + text: str + """The text content.""" + + transcript: str + """ + The transcript of the audio content, this will always be present if the output + type is `audio`. + """ + + type: Literal["output_text", "output_audio"] + """ + The content type, `output_text` or `output_audio` depending on the session + `output_modalities` configuration. + """ + + +class RealtimeConversationItemAssistantMessageParam(TypedDict, total=False): + """An assistant message item in a Realtime conversation.""" + + content: Required[Iterable[Content]] + """The content of the message.""" + + role: Required[Literal["assistant"]] + """The role of the message sender. Always `assistant`.""" + + type: Required[Literal["message"]] + """The type of the item. Always `message`.""" + + id: str + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ + + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item. Has no effect on the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_function_call.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_function_call.py new file mode 100644 index 0000000..cfe299f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_function_call.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeConversationItemFunctionCall"] + + +class RealtimeConversationItemFunctionCall(BaseModel): + """A function call item in a Realtime conversation.""" + + arguments: str + """The arguments of the function call. + + This is a JSON-encoded string representing the arguments passed to the function, + for example `{"arg1": "value1", "arg2": 42}`. + """ + + name: str + """The name of the function being called.""" + + type: Literal["function_call"] + """The type of the item. Always `function_call`.""" + + id: Optional[str] = None + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ + + call_id: Optional[str] = None + """The ID of the function call.""" + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ + + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item. Has no effect on the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_function_call_output.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_function_call_output.py new file mode 100644 index 0000000..2a9442c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_function_call_output.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeConversationItemFunctionCallOutput"] + + +class RealtimeConversationItemFunctionCallOutput(BaseModel): + """A function call output item in a Realtime conversation.""" + + call_id: str + """The ID of the function call this output is for.""" + + output: str + """ + The output of the function call, this is free text and can contain any + information or simply be empty. + """ + + type: Literal["function_call_output"] + """The type of the item. Always `function_call_output`.""" + + id: Optional[str] = None + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ + + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item. Has no effect on the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_function_call_output_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_function_call_output_param.py new file mode 100644 index 0000000..37d3f06 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_function_call_output_param.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeConversationItemFunctionCallOutputParam"] + + +class RealtimeConversationItemFunctionCallOutputParam(TypedDict, total=False): + """A function call output item in a Realtime conversation.""" + + call_id: Required[str] + """The ID of the function call this output is for.""" + + output: Required[str] + """ + The output of the function call, this is free text and can contain any + information or simply be empty. + """ + + type: Required[Literal["function_call_output"]] + """The type of the item. Always `function_call_output`.""" + + id: str + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ + + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item. Has no effect on the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_function_call_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_function_call_param.py new file mode 100644 index 0000000..d88adbf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_function_call_param.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeConversationItemFunctionCallParam"] + + +class RealtimeConversationItemFunctionCallParam(TypedDict, total=False): + """A function call item in a Realtime conversation.""" + + arguments: Required[str] + """The arguments of the function call. + + This is a JSON-encoded string representing the arguments passed to the function, + for example `{"arg1": "value1", "arg2": 42}`. + """ + + name: Required[str] + """The name of the function being called.""" + + type: Required[Literal["function_call"]] + """The type of the item. Always `function_call`.""" + + id: str + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ + + call_id: str + """The ID of the function call.""" + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ + + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item. Has no effect on the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_system_message.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_system_message.py new file mode 100644 index 0000000..e988b15 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_system_message.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeConversationItemSystemMessage", "Content"] + + +class Content(BaseModel): + text: Optional[str] = None + """The text content.""" + + type: Optional[Literal["input_text"]] = None + """The content type. Always `input_text` for system messages.""" + + +class RealtimeConversationItemSystemMessage(BaseModel): + """ + A system message in a Realtime conversation can be used to provide additional context or instructions to the model. This is similar but distinct from the instruction prompt provided at the start of a conversation, as system messages can be added at any point in the conversation. For major changes to the conversation's behavior, use instructions, but for smaller updates (e.g. "the user is now asking about a different topic"), use system messages. + """ + + content: List[Content] + """The content of the message.""" + + role: Literal["system"] + """The role of the message sender. Always `system`.""" + + type: Literal["message"] + """The type of the item. Always `message`.""" + + id: Optional[str] = None + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ + + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item. Has no effect on the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_system_message_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_system_message_param.py new file mode 100644 index 0000000..10c94bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_system_message_param.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeConversationItemSystemMessageParam", "Content"] + + +class Content(TypedDict, total=False): + text: str + """The text content.""" + + type: Literal["input_text"] + """The content type. Always `input_text` for system messages.""" + + +class RealtimeConversationItemSystemMessageParam(TypedDict, total=False): + """ + A system message in a Realtime conversation can be used to provide additional context or instructions to the model. This is similar but distinct from the instruction prompt provided at the start of a conversation, as system messages can be added at any point in the conversation. For major changes to the conversation's behavior, use instructions, but for smaller updates (e.g. "the user is now asking about a different topic"), use system messages. + """ + + content: Required[Iterable[Content]] + """The content of the message.""" + + role: Required[Literal["system"]] + """The role of the message sender. Always `system`.""" + + type: Required[Literal["message"]] + """The type of the item. Always `message`.""" + + id: str + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ + + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item. Has no effect on the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_user_message.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_user_message.py new file mode 100644 index 0000000..a66dc6a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_user_message.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeConversationItemUserMessage", "Content"] + + +class Content(BaseModel): + audio: Optional[str] = None + """ + Base64-encoded audio bytes (for `input_audio`), these will be parsed as the + format specified in the session input audio type configuration. This defaults to + PCM 16-bit 24kHz mono if not specified. + """ + + detail: Optional[Literal["auto", "low", "high"]] = None + """The detail level of the image (for `input_image`). + + `auto` will default to `high`. + """ + + image_url: Optional[str] = None + """Base64-encoded image bytes (for `input_image`) as a data URI. + + For example `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...`. Supported + formats are PNG and JPEG. + """ + + text: Optional[str] = None + """The text content (for `input_text`).""" + + transcript: Optional[str] = None + """Transcript of the audio (for `input_audio`). + + This is not sent to the model, but will be attached to the message item for + reference. + """ + + type: Optional[Literal["input_text", "input_audio", "input_image"]] = None + """The content type (`input_text`, `input_audio`, or `input_image`).""" + + +class RealtimeConversationItemUserMessage(BaseModel): + """A user message item in a Realtime conversation.""" + + content: List[Content] + """The content of the message.""" + + role: Literal["user"] + """The role of the message sender. Always `user`.""" + + type: Literal["message"] + """The type of the item. Always `message`.""" + + id: Optional[str] = None + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ + + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item. Has no effect on the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_user_message_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_user_message_param.py new file mode 100644 index 0000000..f12c255 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_conversation_item_user_message_param.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeConversationItemUserMessageParam", "Content"] + + +class Content(TypedDict, total=False): + audio: str + """ + Base64-encoded audio bytes (for `input_audio`), these will be parsed as the + format specified in the session input audio type configuration. This defaults to + PCM 16-bit 24kHz mono if not specified. + """ + + detail: Literal["auto", "low", "high"] + """The detail level of the image (for `input_image`). + + `auto` will default to `high`. + """ + + image_url: str + """Base64-encoded image bytes (for `input_image`) as a data URI. + + For example `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...`. Supported + formats are PNG and JPEG. + """ + + text: str + """The text content (for `input_text`).""" + + transcript: str + """Transcript of the audio (for `input_audio`). + + This is not sent to the model, but will be attached to the message item for + reference. + """ + + type: Literal["input_text", "input_audio", "input_image"] + """The content type (`input_text`, `input_audio`, or `input_image`).""" + + +class RealtimeConversationItemUserMessageParam(TypedDict, total=False): + """A user message item in a Realtime conversation.""" + + content: Required[Iterable[Content]] + """The content of the message.""" + + role: Required[Literal["user"]] + """The role of the message sender. Always `user`.""" + + type: Required[Literal["message"]] + """The type of the item. Always `message`.""" + + id: str + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ + + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item. Has no effect on the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_error.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_error.py new file mode 100644 index 0000000..254cf23 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_error.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["RealtimeError"] + + +class RealtimeError(BaseModel): + """Details of the error.""" + + message: str + """A human-readable error message.""" + + type: str + """The type of error (e.g., "invalid_request_error", "server_error").""" + + code: Optional[str] = None + """Error code, if any.""" + + event_id: Optional[str] = None + """The event_id of the client event that caused the error, if applicable.""" + + param: Optional[str] = None + """Parameter related to the error, if any.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_error_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_error_event.py new file mode 100644 index 0000000..1cd8636 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_error_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_error import RealtimeError + +__all__ = ["RealtimeErrorEvent"] + + +class RealtimeErrorEvent(BaseModel): + """ + Returned when an error occurs, which could be a client problem or a server + problem. Most errors are recoverable and the session will stay open, we + recommend to implementers to monitor and log error messages by default. + """ + + error: RealtimeError + """Details of the error.""" + + event_id: str + """The unique ID of the server event.""" + + type: Literal["error"] + """The event type, must be `error`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_function_tool.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_function_tool.py new file mode 100644 index 0000000..8ef6047 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_function_tool.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeFunctionTool"] + + +class RealtimeFunctionTool(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_function_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_function_tool_param.py new file mode 100644 index 0000000..d769d78 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_function_tool_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["RealtimeFunctionToolParam"] + + +class RealtimeFunctionToolParam(TypedDict, total=False): + description: str + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: str + """The name of the function.""" + + parameters: object + """Parameters of the function in JSON Schema.""" + + type: Literal["function"] + """The type of the tool, i.e. `function`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_approval_request.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_approval_request.py new file mode 100644 index 0000000..1e8d25a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_approval_request.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeMcpApprovalRequest"] + + +class RealtimeMcpApprovalRequest(BaseModel): + """A Realtime item requesting human approval of a tool invocation.""" + + id: str + """The unique ID of the approval request.""" + + arguments: str + """A JSON string of arguments for the tool.""" + + name: str + """The name of the tool to run.""" + + server_label: str + """The label of the MCP server making the request.""" + + type: Literal["mcp_approval_request"] + """The type of the item. Always `mcp_approval_request`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_approval_request_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_approval_request_param.py new file mode 100644 index 0000000..c255bb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_approval_request_param.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeMcpApprovalRequestParam"] + + +class RealtimeMcpApprovalRequestParam(TypedDict, total=False): + """A Realtime item requesting human approval of a tool invocation.""" + + id: Required[str] + """The unique ID of the approval request.""" + + arguments: Required[str] + """A JSON string of arguments for the tool.""" + + name: Required[str] + """The name of the tool to run.""" + + server_label: Required[str] + """The label of the MCP server making the request.""" + + type: Required[Literal["mcp_approval_request"]] + """The type of the item. Always `mcp_approval_request`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_approval_response.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_approval_response.py new file mode 100644 index 0000000..9828cdc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_approval_response.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeMcpApprovalResponse"] + + +class RealtimeMcpApprovalResponse(BaseModel): + """A Realtime item responding to an MCP approval request.""" + + id: str + """The unique ID of the approval response.""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + reason: Optional[str] = None + """Optional reason for the decision.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_approval_response_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_approval_response_param.py new file mode 100644 index 0000000..ce9a671 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_approval_response_param.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeMcpApprovalResponseParam"] + + +class RealtimeMcpApprovalResponseParam(TypedDict, total=False): + """A Realtime item responding to an MCP approval request.""" + + id: Required[str] + """The unique ID of the approval response.""" + + approval_request_id: Required[str] + """The ID of the approval request being answered.""" + + approve: Required[bool] + """Whether the request was approved.""" + + type: Required[Literal["mcp_approval_response"]] + """The type of the item. Always `mcp_approval_response`.""" + + reason: Optional[str] + """Optional reason for the decision.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_list_tools.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_list_tools.py new file mode 100644 index 0000000..96df14e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_list_tools.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeMcpListTools", "Tool"] + + +class Tool(BaseModel): + """A tool available on an MCP server.""" + + input_schema: object + """The JSON schema describing the tool's input.""" + + name: str + """The name of the tool.""" + + annotations: Optional[object] = None + """Additional annotations about the tool.""" + + description: Optional[str] = None + """The description of the tool.""" + + +class RealtimeMcpListTools(BaseModel): + """A Realtime item listing tools available on an MCP server.""" + + server_label: str + """The label of the MCP server.""" + + tools: List[Tool] + """The tools available on the server.""" + + type: Literal["mcp_list_tools"] + """The type of the item. Always `mcp_list_tools`.""" + + id: Optional[str] = None + """The unique ID of the list.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_list_tools_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_list_tools_param.py new file mode 100644 index 0000000..44f1bbe --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_list_tools_param.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeMcpListToolsParam", "Tool"] + + +class Tool(TypedDict, total=False): + """A tool available on an MCP server.""" + + input_schema: Required[object] + """The JSON schema describing the tool's input.""" + + name: Required[str] + """The name of the tool.""" + + annotations: Optional[object] + """Additional annotations about the tool.""" + + description: Optional[str] + """The description of the tool.""" + + +class RealtimeMcpListToolsParam(TypedDict, total=False): + """A Realtime item listing tools available on an MCP server.""" + + server_label: Required[str] + """The label of the MCP server.""" + + tools: Required[Iterable[Tool]] + """The tools available on the server.""" + + type: Required[Literal["mcp_list_tools"]] + """The type of the item. Always `mcp_list_tools`.""" + + id: str + """The unique ID of the list.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_protocol_error.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_protocol_error.py new file mode 100644 index 0000000..c97ba69 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_protocol_error.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeMcpProtocolError"] + + +class RealtimeMcpProtocolError(BaseModel): + code: int + + message: str + + type: Literal["protocol_error"] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_protocol_error_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_protocol_error_param.py new file mode 100644 index 0000000..04b2bd8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_protocol_error_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeMcpProtocolErrorParam"] + + +class RealtimeMcpProtocolErrorParam(TypedDict, total=False): + code: Required[int] + + message: Required[str] + + type: Required[Literal["protocol_error"]] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_tool_call.py new file mode 100644 index 0000000..5b9b1d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_tool_call.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .realtime_mcphttp_error import RealtimeMcphttpError +from .realtime_mcp_protocol_error import RealtimeMcpProtocolError +from .realtime_mcp_tool_execution_error import RealtimeMcpToolExecutionError + +__all__ = ["RealtimeMcpToolCall", "Error"] + +Error: TypeAlias = Annotated[ + Union[RealtimeMcpProtocolError, RealtimeMcpToolExecutionError, RealtimeMcphttpError, None], + PropertyInfo(discriminator="type"), +] + + +class RealtimeMcpToolCall(BaseModel): + """A Realtime item representing an invocation of a tool on an MCP server.""" + + id: str + """The unique ID of the tool call.""" + + arguments: str + """A JSON string of the arguments passed to the tool.""" + + name: str + """The name of the tool that was run.""" + + server_label: str + """The label of the MCP server running the tool.""" + + type: Literal["mcp_call"] + """The type of the item. Always `mcp_call`.""" + + approval_request_id: Optional[str] = None + """The ID of an associated approval request, if any.""" + + error: Optional[Error] = None + """The error from the tool call, if any.""" + + output: Optional[str] = None + """The output from the tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_tool_call_param.py new file mode 100644 index 0000000..772cbf5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_tool_call_param.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .realtime_mcphttp_error_param import RealtimeMcphttpErrorParam +from .realtime_mcp_protocol_error_param import RealtimeMcpProtocolErrorParam +from .realtime_mcp_tool_execution_error_param import RealtimeMcpToolExecutionErrorParam + +__all__ = ["RealtimeMcpToolCallParam", "Error"] + +Error: TypeAlias = Union[RealtimeMcpProtocolErrorParam, RealtimeMcpToolExecutionErrorParam, RealtimeMcphttpErrorParam] + + +class RealtimeMcpToolCallParam(TypedDict, total=False): + """A Realtime item representing an invocation of a tool on an MCP server.""" + + id: Required[str] + """The unique ID of the tool call.""" + + arguments: Required[str] + """A JSON string of the arguments passed to the tool.""" + + name: Required[str] + """The name of the tool that was run.""" + + server_label: Required[str] + """The label of the MCP server running the tool.""" + + type: Required[Literal["mcp_call"]] + """The type of the item. Always `mcp_call`.""" + + approval_request_id: Optional[str] + """The ID of an associated approval request, if any.""" + + error: Optional[Error] + """The error from the tool call, if any.""" + + output: Optional[str] + """The output from the tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_tool_execution_error.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_tool_execution_error.py new file mode 100644 index 0000000..4443c2e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_tool_execution_error.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeMcpToolExecutionError"] + + +class RealtimeMcpToolExecutionError(BaseModel): + message: str + + type: Literal["tool_execution_error"] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_tool_execution_error_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_tool_execution_error_param.py new file mode 100644 index 0000000..660b8e0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcp_tool_execution_error_param.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeMcpToolExecutionErrorParam"] + + +class RealtimeMcpToolExecutionErrorParam(TypedDict, total=False): + message: Required[str] + + type: Required[Literal["tool_execution_error"]] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcphttp_error.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcphttp_error.py new file mode 100644 index 0000000..d17a860 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcphttp_error.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeMcphttpError"] + + +class RealtimeMcphttpError(BaseModel): + code: int + + message: str + + type: Literal["http_error"] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcphttp_error_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcphttp_error_param.py new file mode 100644 index 0000000..603ae10 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_mcphttp_error_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeMcphttpErrorParam"] + + +class RealtimeMcphttpErrorParam(TypedDict, total=False): + code: Required[int] + + message: Required[str] + + type: Required[Literal["http_error"]] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_reasoning.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_reasoning.py new file mode 100644 index 0000000..3ae14c2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_reasoning.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .realtime_reasoning_effort import RealtimeReasoningEffort + +__all__ = ["RealtimeReasoning"] + + +class RealtimeReasoning(BaseModel): + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + + effort: Optional[RealtimeReasoningEffort] = None + """ + Constrains effort on reasoning for reasoning-capable Realtime models such as + `gpt-realtime-2`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_reasoning_effort.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_reasoning_effort.py new file mode 100644 index 0000000..21502d9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_reasoning_effort.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["RealtimeReasoningEffort"] + +RealtimeReasoningEffort: TypeAlias = Literal["minimal", "low", "medium", "high", "xhigh"] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_reasoning_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_reasoning_param.py new file mode 100644 index 0000000..f0cc21e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_reasoning_param.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .realtime_reasoning_effort import RealtimeReasoningEffort + +__all__ = ["RealtimeReasoningParam"] + + +class RealtimeReasoningParam(TypedDict, total=False): + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + + effort: RealtimeReasoningEffort + """ + Constrains effort on reasoning for reasoning-capable Realtime models such as + `gpt-realtime-2`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response.py new file mode 100644 index 0000000..c285b0e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from ..shared.metadata import Metadata +from .conversation_item import ConversationItem +from .realtime_audio_formats import RealtimeAudioFormats +from .realtime_response_usage import RealtimeResponseUsage +from .realtime_response_status import RealtimeResponseStatus + +__all__ = ["RealtimeResponse", "Audio", "AudioOutput"] + + +class AudioOutput(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The format of the output audio.""" + + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None + ] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. We recommend + `marin` and `cedar` for best quality. + """ + + +class Audio(BaseModel): + """Configuration for audio output.""" + + output: Optional[AudioOutput] = None + + +class RealtimeResponse(BaseModel): + """The response resource.""" + + id: Optional[str] = None + """The unique ID of the response, will look like `resp_1234`.""" + + audio: Optional[Audio] = None + """Configuration for audio output.""" + + conversation_id: Optional[str] = None + """ + Which conversation the response is added to, determined by the `conversation` + field in the `response.create` event. If `auto`, the response will be added to + the default conversation and the value of `conversation_id` will be an id like + `conv_1234`. If `none`, the response will not be added to any conversation and + the value of `conversation_id` will be `null`. If responses are being triggered + automatically by VAD the response will be added to the default conversation + """ + + max_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls, that was used in this response. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + object: Optional[Literal["realtime.response"]] = None + """The object type, must be `realtime.response`.""" + + output: Optional[List[ConversationItem]] = None + """The list of output items generated by the response.""" + + output_modalities: Optional[List[Literal["text", "audio"]]] = None + """ + The set of modalities the model used to respond, currently the only possible + values are `[\"audio\"]`, `[\"text\"]`. Audio output always include a text + transcript. Setting the output to mode `text` will disable audio output from the + model. + """ + + status: Optional[Literal["completed", "cancelled", "failed", "incomplete", "in_progress"]] = None + """ + The final status of the response (`completed`, `cancelled`, `failed`, or + `incomplete`, `in_progress`). + """ + + status_details: Optional[RealtimeResponseStatus] = None + """Additional details about the status.""" + + usage: Optional[RealtimeResponseUsage] = None + """Usage statistics for the Response, this will correspond to billing. + + A Realtime API session will maintain a conversation context and append new Items + to the Conversation, thus output from previous turns (text and audio tokens) + will become the input for later turns. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_audio_output.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_audio_output.py new file mode 100644 index 0000000..9ea6c89 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_audio_output.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .realtime_audio_formats import RealtimeAudioFormats + +__all__ = ["RealtimeResponseCreateAudioOutput", "Output", "OutputVoice", "OutputVoiceID"] + + +class OutputVoiceID(BaseModel): + """Custom voice reference.""" + + id: str + """The custom voice ID, e.g. `voice_1234`.""" + + +OutputVoice: TypeAlias = Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], OutputVoiceID +] + + +class Output(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The format of the output audio.""" + + voice: Optional[OutputVoice] = None + """The voice the model uses to respond. + + Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, + `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice + object with an `id`, for example `{ "id": "voice_1234" }`. Voice cannot be + changed during the session once the model has responded with audio at least + once. We recommend `marin` and `cedar` for best quality. + """ + + +class RealtimeResponseCreateAudioOutput(BaseModel): + """Configuration for audio input and output.""" + + output: Optional[Output] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_audio_output_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_audio_output_param.py new file mode 100644 index 0000000..d89a193 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_audio_output_param.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .realtime_audio_formats_param import RealtimeAudioFormatsParam + +__all__ = ["RealtimeResponseCreateAudioOutputParam", "Output", "OutputVoice", "OutputVoiceID"] + + +class OutputVoiceID(TypedDict, total=False): + """Custom voice reference.""" + + id: Required[str] + """The custom voice ID, e.g. `voice_1234`.""" + + +OutputVoice: TypeAlias = Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], OutputVoiceID +] + + +class Output(TypedDict, total=False): + format: RealtimeAudioFormatsParam + """The format of the output audio.""" + + voice: OutputVoice + """The voice the model uses to respond. + + Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, + `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice + object with an `id`, for example `{ "id": "voice_1234" }`. Voice cannot be + changed during the session once the model has responded with audio at least + once. We recommend `marin` and `cedar` for best quality. + """ + + +class RealtimeResponseCreateAudioOutputParam(TypedDict, total=False): + """Configuration for audio input and output.""" + + output: Output diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_mcp_tool.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_mcp_tool.py new file mode 100644 index 0000000..76f6c22 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_mcp_tool.py @@ -0,0 +1,169 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel + +__all__ = [ + "RealtimeResponseCreateMcpTool", + "AllowedTools", + "AllowedToolsMcpToolFilter", + "RequireApproval", + "RequireApprovalMcpToolApprovalFilter", + "RequireApprovalMcpToolApprovalFilterAlways", + "RequireApprovalMcpToolApprovalFilterNever", +] + + +class AllowedToolsMcpToolFilter(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +AllowedTools: TypeAlias = Union[List[str], AllowedToolsMcpToolFilter, None] + + +class RequireApprovalMcpToolApprovalFilterAlways(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class RequireApprovalMcpToolApprovalFilterNever(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class RequireApprovalMcpToolApprovalFilter(BaseModel): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + + always: Optional[RequireApprovalMcpToolApprovalFilterAlways] = None + """A filter object to specify which tools are allowed.""" + + never: Optional[RequireApprovalMcpToolApprovalFilterNever] = None + """A filter object to specify which tools are allowed.""" + + +RequireApproval: TypeAlias = Union[RequireApprovalMcpToolApprovalFilter, Literal["always", "never"], None] + + +class RealtimeResponseCreateMcpTool(BaseModel): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + """ + + server_label: str + """A label for this MCP server, used to identify it in tool calls.""" + + type: Literal["mcp"] + """The type of the MCP tool. Always `mcp`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + allowed_tools: Optional[AllowedTools] = None + """List of allowed tool names or a filter object.""" + + authorization: Optional[str] = None + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Optional[ + Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + ] = None + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors + [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors). + + This field is deprecated for models released after September 1, 2026. Use + `server_url` to connect to a remote MCP server, or `tunnel_id` to connect + through a Secure MCP Tunnel. + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + defer_loading: Optional[bool] = None + """Whether this MCP tool is deferred and discovered via tool search.""" + + headers: Optional[Dict[str, str]] = None + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[RequireApproval] = None + """Specify which of the MCP server's tools require approval.""" + + server_description: Optional[str] = None + """Optional description of the MCP server, used to provide more context.""" + + server_url: Optional[str] = None + """The URL for the MCP server. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: Optional[str] = None + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_mcp_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_mcp_tool_param.py new file mode 100644 index 0000000..41a02bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_mcp_tool_param.py @@ -0,0 +1,169 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "RealtimeResponseCreateMcpToolParam", + "AllowedTools", + "AllowedToolsMcpToolFilter", + "RequireApproval", + "RequireApprovalMcpToolApprovalFilter", + "RequireApprovalMcpToolApprovalFilterAlways", + "RequireApprovalMcpToolApprovalFilterNever", +] + + +class AllowedToolsMcpToolFilter(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +AllowedTools: TypeAlias = Union[SequenceNotStr[str], AllowedToolsMcpToolFilter] + + +class RequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class RequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class RequireApprovalMcpToolApprovalFilter(TypedDict, total=False): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + + always: RequireApprovalMcpToolApprovalFilterAlways + """A filter object to specify which tools are allowed.""" + + never: RequireApprovalMcpToolApprovalFilterNever + """A filter object to specify which tools are allowed.""" + + +RequireApproval: TypeAlias = Union[RequireApprovalMcpToolApprovalFilter, Literal["always", "never"]] + + +class RealtimeResponseCreateMcpToolParam(TypedDict, total=False): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + """ + + server_label: Required[str] + """A label for this MCP server, used to identify it in tool calls.""" + + type: Required[Literal["mcp"]] + """The type of the MCP tool. Always `mcp`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + allowed_tools: Optional[AllowedTools] + """List of allowed tool names or a filter object.""" + + authorization: str + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors + [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors). + + This field is deprecated for models released after September 1, 2026. Use + `server_url` to connect to a remote MCP server, or `tunnel_id` to connect + through a Secure MCP Tunnel. + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + defer_loading: bool + """Whether this MCP tool is deferred and discovered via tool search.""" + + headers: Optional[Dict[str, str]] + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[RequireApproval] + """Specify which of the MCP server's tools require approval.""" + + server_description: str + """Optional description of the MCP server, used to provide more context.""" + + server_url: str + """The URL for the MCP server. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: str + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_params.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_params.py new file mode 100644 index 0000000..8991279 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_params.py @@ -0,0 +1,110 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from ..shared.metadata import Metadata +from .conversation_item import ConversationItem +from .realtime_reasoning import RealtimeReasoning +from .realtime_function_tool import RealtimeFunctionTool +from ..responses.response_prompt import ResponsePrompt +from ..responses.tool_choice_mcp import ToolChoiceMcp +from ..responses.tool_choice_options import ToolChoiceOptions +from ..responses.tool_choice_function import ToolChoiceFunction +from .realtime_response_create_mcp_tool import RealtimeResponseCreateMcpTool +from .realtime_response_create_audio_output import RealtimeResponseCreateAudioOutput + +__all__ = ["RealtimeResponseCreateParams", "ToolChoice", "Tool"] + +ToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunction, ToolChoiceMcp] + +Tool: TypeAlias = Union[RealtimeFunctionTool, RealtimeResponseCreateMcpTool] + + +class RealtimeResponseCreateParams(BaseModel): + """Create a new Realtime response with these parameters""" + + audio: Optional[RealtimeResponseCreateAudioOutput] = None + """Configuration for audio input and output.""" + + conversation: Union[str, Literal["auto", "none"], None] = None + """Controls which conversation the response is added to. + + Currently supports `auto` and `none`, with `auto` as the default value. The + `auto` value means that the contents of the response will be added to the + default conversation. Set this to `none` to create an out-of-band response which + will not add items to default conversation. + """ + + input: Optional[List[ConversationItem]] = None + """Input items to include in the prompt for the model. + + Using this field creates a new context for this Response instead of using the + default conversation. An empty array `[]` will clear the context for this + Response. Note that this can include references to items that previously + appeared in the session using their id. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. Note that the server sets default instructions which will be used if + this field is not set and are visible in the `session.created` event at the + start of the session. + """ + + max_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + output_modalities: Optional[List[Literal["text", "audio"]]] = None + """ + The set of modalities the model used to respond, currently the only possible + values are `[\"audio\"]`, `[\"text\"]`. Audio output always include a text + transcript. Setting the output to mode `text` will disable audio output from the + model. + """ + + parallel_tool_calls: Optional[bool] = None + """Whether the model may call multiple tools in parallel. + + Only supported by reasoning Realtime models such as `gpt-realtime-2`. + """ + + prompt: Optional[ResponsePrompt] = None + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + reasoning: Optional[RealtimeReasoning] = None + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + + tool_choice: Optional[ToolChoice] = None + """How the model chooses tools. + + Provide one of the string modes or force a specific function/MCP tool. + """ + + tools: Optional[List[Tool]] = None + """Tools available to the model.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_params_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_params_param.py new file mode 100644 index 0000000..1801019 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_create_params_param.py @@ -0,0 +1,111 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, TypeAlias, TypedDict + +from ..shared_params.metadata import Metadata +from .conversation_item_param import ConversationItemParam +from .realtime_reasoning_param import RealtimeReasoningParam +from .realtime_function_tool_param import RealtimeFunctionToolParam +from ..responses.tool_choice_options import ToolChoiceOptions +from ..responses.response_prompt_param import ResponsePromptParam +from ..responses.tool_choice_mcp_param import ToolChoiceMcpParam +from ..responses.tool_choice_function_param import ToolChoiceFunctionParam +from .realtime_response_create_mcp_tool_param import RealtimeResponseCreateMcpToolParam +from .realtime_response_create_audio_output_param import RealtimeResponseCreateAudioOutputParam + +__all__ = ["RealtimeResponseCreateParamsParam", "ToolChoice", "Tool"] + +ToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunctionParam, ToolChoiceMcpParam] + +Tool: TypeAlias = Union[RealtimeFunctionToolParam, RealtimeResponseCreateMcpToolParam] + + +class RealtimeResponseCreateParamsParam(TypedDict, total=False): + """Create a new Realtime response with these parameters""" + + audio: RealtimeResponseCreateAudioOutputParam + """Configuration for audio input and output.""" + + conversation: Union[str, Literal["auto", "none"]] + """Controls which conversation the response is added to. + + Currently supports `auto` and `none`, with `auto` as the default value. The + `auto` value means that the contents of the response will be added to the + default conversation. Set this to `none` to create an out-of-band response which + will not add items to default conversation. + """ + + input: Iterable[ConversationItemParam] + """Input items to include in the prompt for the model. + + Using this field creates a new context for this Response instead of using the + default conversation. An empty array `[]` will clear the context for this + Response. Note that this can include references to items that previously + appeared in the session using their id. + """ + + instructions: str + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. Note that the server sets default instructions which will be used if + this field is not set and are visible in the `session.created` event at the + start of the session. + """ + + max_output_tokens: Union[int, Literal["inf"]] + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + output_modalities: List[Literal["text", "audio"]] + """ + The set of modalities the model used to respond, currently the only possible + values are `[\"audio\"]`, `[\"text\"]`. Audio output always include a text + transcript. Setting the output to mode `text` will disable audio output from the + model. + """ + + parallel_tool_calls: bool + """Whether the model may call multiple tools in parallel. + + Only supported by reasoning Realtime models such as `gpt-realtime-2`. + """ + + prompt: Optional[ResponsePromptParam] + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + reasoning: RealtimeReasoningParam + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + + tool_choice: ToolChoice + """How the model chooses tools. + + Provide one of the string modes or force a specific function/MCP tool. + """ + + tools: Iterable[Tool] + """Tools available to the model.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_status.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_status.py new file mode 100644 index 0000000..8b01089 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_status.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeResponseStatus", "Error"] + + +class Error(BaseModel): + """ + A description of the error that caused the response to fail, + populated when the `status` is `failed`. + """ + + code: Optional[str] = None + """Error code, if any.""" + + type: Optional[str] = None + """The type of error.""" + + +class RealtimeResponseStatus(BaseModel): + """Additional details about the status.""" + + error: Optional[Error] = None + """ + A description of the error that caused the response to fail, populated when the + `status` is `failed`. + """ + + reason: Optional[Literal["turn_detected", "client_cancelled", "max_output_tokens", "content_filter"]] = None + """The reason the Response did not complete. + + For a `cancelled` Response, one of `turn_detected` (the server VAD detected a + new start of speech) or `client_cancelled` (the client sent a cancel event). For + an `incomplete` Response, one of `max_output_tokens` or `content_filter` (the + server-side safety filter activated and cut off the response). + """ + + type: Optional[Literal["completed", "cancelled", "incomplete", "failed"]] = None + """ + The type of error that caused the response to fail, corresponding with the + `status` field (`completed`, `cancelled`, `incomplete`, `failed`). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_usage.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_usage.py new file mode 100644 index 0000000..f756434 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_usage.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .realtime_response_usage_input_token_details import RealtimeResponseUsageInputTokenDetails +from .realtime_response_usage_output_token_details import RealtimeResponseUsageOutputTokenDetails + +__all__ = ["RealtimeResponseUsage"] + + +class RealtimeResponseUsage(BaseModel): + """Usage statistics for the Response, this will correspond to billing. + + A + Realtime API session will maintain a conversation context and append new + Items to the Conversation, thus output from previous turns (text and + audio tokens) will become the input for later turns. + """ + + input_token_details: Optional[RealtimeResponseUsageInputTokenDetails] = None + """Details about the input tokens used in the Response. + + Cached tokens are tokens from previous turns in the conversation that are + included as context for the current response. Cached tokens here are counted as + a subset of input tokens, meaning input tokens will include cached and uncached + tokens. + """ + + input_tokens: Optional[int] = None + """ + The number of input tokens used in the Response, including text and audio + tokens. + """ + + output_token_details: Optional[RealtimeResponseUsageOutputTokenDetails] = None + """Details about the output tokens used in the Response.""" + + output_tokens: Optional[int] = None + """ + The number of output tokens sent in the Response, including text and audio + tokens. + """ + + total_tokens: Optional[int] = None + """ + The total number of tokens in the Response including input and output text and + audio tokens. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_usage_input_token_details.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_usage_input_token_details.py new file mode 100644 index 0000000..3e289ac --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_usage_input_token_details.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["RealtimeResponseUsageInputTokenDetails", "CachedTokensDetails"] + + +class CachedTokensDetails(BaseModel): + """Details about the cached tokens used as input for the Response.""" + + audio_tokens: Optional[int] = None + """The number of cached audio tokens used as input for the Response.""" + + image_tokens: Optional[int] = None + """The number of cached image tokens used as input for the Response.""" + + text_tokens: Optional[int] = None + """The number of cached text tokens used as input for the Response.""" + + +class RealtimeResponseUsageInputTokenDetails(BaseModel): + """Details about the input tokens used in the Response. + + Cached tokens are tokens from previous turns in the conversation that are included as context for the current response. Cached tokens here are counted as a subset of input tokens, meaning input tokens will include cached and uncached tokens. + """ + + audio_tokens: Optional[int] = None + """The number of audio tokens used as input for the Response.""" + + cached_tokens: Optional[int] = None + """The number of cached tokens used as input for the Response.""" + + cached_tokens_details: Optional[CachedTokensDetails] = None + """Details about the cached tokens used as input for the Response.""" + + image_tokens: Optional[int] = None + """The number of image tokens used as input for the Response.""" + + text_tokens: Optional[int] = None + """The number of text tokens used as input for the Response.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_usage_output_token_details.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_usage_output_token_details.py new file mode 100644 index 0000000..eca5ca1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_response_usage_output_token_details.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["RealtimeResponseUsageOutputTokenDetails"] + + +class RealtimeResponseUsageOutputTokenDetails(BaseModel): + """Details about the output tokens used in the Response.""" + + audio_tokens: Optional[int] = None + """The number of audio tokens used in the Response.""" + + text_tokens: Optional[int] = None + """The number of text tokens used in the Response.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_server_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_server_event.py new file mode 100644 index 0000000..24416e6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_server_event.py @@ -0,0 +1,185 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .conversation_item import ConversationItem +from .response_done_event import ResponseDoneEvent +from .realtime_error_event import RealtimeErrorEvent +from .mcp_list_tools_failed import McpListToolsFailed +from .session_created_event import SessionCreatedEvent +from .session_updated_event import SessionUpdatedEvent +from .conversation_item_done import ConversationItemDone +from .response_created_event import ResponseCreatedEvent +from .conversation_item_added import ConversationItemAdded +from .mcp_list_tools_completed import McpListToolsCompleted +from .response_mcp_call_failed import ResponseMcpCallFailed +from .response_text_done_event import ResponseTextDoneEvent +from .rate_limits_updated_event import RateLimitsUpdatedEvent +from .response_audio_done_event import ResponseAudioDoneEvent +from .response_text_delta_event import ResponseTextDeltaEvent +from .conversation_created_event import ConversationCreatedEvent +from .mcp_list_tools_in_progress import McpListToolsInProgress +from .response_audio_delta_event import ResponseAudioDeltaEvent +from .response_mcp_call_completed import ResponseMcpCallCompleted +from .response_mcp_call_in_progress import ResponseMcpCallInProgress +from .conversation_item_created_event import ConversationItemCreatedEvent +from .conversation_item_deleted_event import ConversationItemDeletedEvent +from .response_output_item_done_event import ResponseOutputItemDoneEvent +from .input_audio_buffer_cleared_event import InputAudioBufferClearedEvent +from .response_content_part_done_event import ResponseContentPartDoneEvent +from .response_mcp_call_arguments_done import ResponseMcpCallArgumentsDone +from .response_output_item_added_event import ResponseOutputItemAddedEvent +from .conversation_item_truncated_event import ConversationItemTruncatedEvent +from .response_content_part_added_event import ResponseContentPartAddedEvent +from .response_mcp_call_arguments_delta import ResponseMcpCallArgumentsDelta +from .input_audio_buffer_committed_event import InputAudioBufferCommittedEvent +from .input_audio_buffer_timeout_triggered import InputAudioBufferTimeoutTriggered +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent +from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent +from .input_audio_buffer_speech_started_event import InputAudioBufferSpeechStartedEvent +from .input_audio_buffer_speech_stopped_event import InputAudioBufferSpeechStoppedEvent +from .response_function_call_arguments_done_event import ResponseFunctionCallArgumentsDoneEvent +from .input_audio_buffer_dtmf_event_received_event import InputAudioBufferDtmfEventReceivedEvent +from .response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent +from .conversation_item_input_audio_transcription_segment import ConversationItemInputAudioTranscriptionSegment +from .conversation_item_input_audio_transcription_delta_event import ConversationItemInputAudioTranscriptionDeltaEvent +from .conversation_item_input_audio_transcription_failed_event import ConversationItemInputAudioTranscriptionFailedEvent +from .conversation_item_input_audio_transcription_completed_event import ( + ConversationItemInputAudioTranscriptionCompletedEvent, +) + +__all__ = [ + "RealtimeServerEvent", + "ConversationItemRetrieved", + "OutputAudioBufferStarted", + "OutputAudioBufferStopped", + "OutputAudioBufferCleared", +] + + +class ConversationItemRetrieved(BaseModel): + """Returned when a conversation item is retrieved with `conversation.item.retrieve`. + + This is provided as a way to fetch the server's representation of an item, for example to get access to the post-processed audio data after noise cancellation and VAD. It includes the full content of the Item, including audio data. + """ + + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """A single item within a Realtime conversation.""" + + type: Literal["conversation.item.retrieved"] + """The event type, must be `conversation.item.retrieved`.""" + + +class OutputAudioBufferStarted(BaseModel): + """ + **WebRTC/SIP Only:** Emitted when the server begins streaming audio to the client. This event is + emitted after an audio content part has been added (`response.content_part.added`) + to the response. + [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + + event_id: str + """The unique ID of the server event.""" + + response_id: str + """The unique ID of the response that produced the audio.""" + + type: Literal["output_audio_buffer.started"] + """The event type, must be `output_audio_buffer.started`.""" + + +class OutputAudioBufferStopped(BaseModel): + """ + **WebRTC/SIP Only:** Emitted when the output audio buffer has been completely drained on the server, + and no more audio is forthcoming. This event is emitted after the full response + data has been sent to the client (`response.done`). + [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + + event_id: str + """The unique ID of the server event.""" + + response_id: str + """The unique ID of the response that produced the audio.""" + + type: Literal["output_audio_buffer.stopped"] + """The event type, must be `output_audio_buffer.stopped`.""" + + +class OutputAudioBufferCleared(BaseModel): + """**WebRTC/SIP Only:** Emitted when the output audio buffer is cleared. + + This happens either in VAD + mode when the user has interrupted (`input_audio_buffer.speech_started`), + or when the client has emitted the `output_audio_buffer.clear` event to manually + cut off the current audio response. + [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + + event_id: str + """The unique ID of the server event.""" + + response_id: str + """The unique ID of the response that produced the audio.""" + + type: Literal["output_audio_buffer.cleared"] + """The event type, must be `output_audio_buffer.cleared`.""" + + +RealtimeServerEvent: TypeAlias = Annotated[ + Union[ + ConversationCreatedEvent, + ConversationItemCreatedEvent, + ConversationItemDeletedEvent, + ConversationItemInputAudioTranscriptionCompletedEvent, + ConversationItemInputAudioTranscriptionDeltaEvent, + ConversationItemInputAudioTranscriptionFailedEvent, + ConversationItemRetrieved, + ConversationItemTruncatedEvent, + RealtimeErrorEvent, + InputAudioBufferClearedEvent, + InputAudioBufferCommittedEvent, + InputAudioBufferDtmfEventReceivedEvent, + InputAudioBufferSpeechStartedEvent, + InputAudioBufferSpeechStoppedEvent, + RateLimitsUpdatedEvent, + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreatedEvent, + ResponseDoneEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + SessionCreatedEvent, + SessionUpdatedEvent, + OutputAudioBufferStarted, + OutputAudioBufferStopped, + OutputAudioBufferCleared, + ConversationItemAdded, + ConversationItemDone, + InputAudioBufferTimeoutTriggered, + ConversationItemInputAudioTranscriptionSegment, + McpListToolsInProgress, + McpListToolsCompleted, + McpListToolsFailed, + ResponseMcpCallArgumentsDelta, + ResponseMcpCallArgumentsDone, + ResponseMcpCallInProgress, + ResponseMcpCallCompleted, + ResponseMcpCallFailed, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_session_create_request.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_session_create_request.py new file mode 100644 index 0000000..3b15f99 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_session_create_request.py @@ -0,0 +1,147 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_reasoning import RealtimeReasoning +from .realtime_truncation import RealtimeTruncation +from .realtime_audio_config import RealtimeAudioConfig +from .realtime_tools_config import RealtimeToolsConfig +from .realtime_tracing_config import RealtimeTracingConfig +from ..responses.response_prompt import ResponsePrompt +from .realtime_tool_choice_config import RealtimeToolChoiceConfig + +__all__ = ["RealtimeSessionCreateRequest"] + + +class RealtimeSessionCreateRequest(BaseModel): + """Realtime session object configuration.""" + + type: Literal["realtime"] + """The type of session to create. Always `realtime` for the Realtime API.""" + + audio: Optional[RealtimeAudioConfig] = None + """Configuration for input and output audio.""" + + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """Additional fields to include in server outputs. + + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + model: Union[ + str, + Literal[ + "gpt-realtime", + "gpt-realtime-1.5", + "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15", + ], + None, + ] = None + """The Realtime model used for this session.""" + + output_modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + It defaults to `["audio"]`, indicating that the model will respond with audio + plus a transcript. `["text"]` can be used to make the model respond with text + only. It is not possible to request both `text` and `audio` at the same time. + """ + + parallel_tool_calls: Optional[bool] = None + """Whether the model may call multiple tools in parallel. + + Only supported by reasoning Realtime models such as `gpt-realtime-2`. + """ + + prompt: Optional[ResponsePrompt] = None + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + reasoning: Optional[RealtimeReasoning] = None + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + + tool_choice: Optional[RealtimeToolChoiceConfig] = None + """How the model chooses tools. + + Provide one of the string modes or force a specific function/MCP tool. + """ + + tools: Optional[RealtimeToolsConfig] = None + """Tools available to the model.""" + + tracing: Optional[RealtimeTracingConfig] = None + """ + Realtime API can write session traces to the + [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to + disable tracing. Once tracing is enabled for a session, the configuration cannot + be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + truncation: Optional[RealtimeTruncation] = None + """ + When the number of tokens in a conversation exceeds the model's input token + limit, the conversation be truncated, meaning messages (starting from the + oldest) will not be included in the model's context. A 32k context model with + 4,096 max output tokens can only include 28,224 tokens in the context before + truncation occurs. + + Clients can configure truncation behavior to truncate with a lower max token + limit, which is an effective way to control token usage and cost. + + Truncation will reduce the number of cached tokens on the next turn (busting the + cache), since messages are dropped from the beginning of the context. However, + clients can also configure truncation to retain messages up to a fraction of the + maximum context size, which will reduce the need for future truncations and thus + improve the cache rate. + + Truncation can be disabled entirely, which means the server will never truncate + but would instead return an error if the conversation exceeds the model's input + token limit. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_session_create_request_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_session_create_request_param.py new file mode 100644 index 0000000..d1ce2f1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_session_create_request_param.py @@ -0,0 +1,147 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Required, TypedDict + +from .realtime_reasoning_param import RealtimeReasoningParam +from .realtime_truncation_param import RealtimeTruncationParam +from .realtime_audio_config_param import RealtimeAudioConfigParam +from .realtime_tools_config_param import RealtimeToolsConfigParam +from .realtime_tracing_config_param import RealtimeTracingConfigParam +from ..responses.response_prompt_param import ResponsePromptParam +from .realtime_tool_choice_config_param import RealtimeToolChoiceConfigParam + +__all__ = ["RealtimeSessionCreateRequestParam"] + + +class RealtimeSessionCreateRequestParam(TypedDict, total=False): + """Realtime session object configuration.""" + + type: Required[Literal["realtime"]] + """The type of session to create. Always `realtime` for the Realtime API.""" + + audio: RealtimeAudioConfigParam + """Configuration for input and output audio.""" + + include: List[Literal["item.input_audio_transcription.logprobs"]] + """Additional fields to include in server outputs. + + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ + + instructions: str + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_output_tokens: Union[int, Literal["inf"]] + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + model: Union[ + str, + Literal[ + "gpt-realtime", + "gpt-realtime-1.5", + "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15", + ], + ] + """The Realtime model used for this session.""" + + output_modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + It defaults to `["audio"]`, indicating that the model will respond with audio + plus a transcript. `["text"]` can be used to make the model respond with text + only. It is not possible to request both `text` and `audio` at the same time. + """ + + parallel_tool_calls: bool + """Whether the model may call multiple tools in parallel. + + Only supported by reasoning Realtime models such as `gpt-realtime-2`. + """ + + prompt: Optional[ResponsePromptParam] + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + reasoning: RealtimeReasoningParam + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + + tool_choice: RealtimeToolChoiceConfigParam + """How the model chooses tools. + + Provide one of the string modes or force a specific function/MCP tool. + """ + + tools: RealtimeToolsConfigParam + """Tools available to the model.""" + + tracing: Optional[RealtimeTracingConfigParam] + """ + Realtime API can write session traces to the + [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to + disable tracing. Once tracing is enabled for a session, the configuration cannot + be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + truncation: RealtimeTruncationParam + """ + When the number of tokens in a conversation exceeds the model's input token + limit, the conversation be truncated, meaning messages (starting from the + oldest) will not be included in the model's context. A 32k context model with + 4,096 max output tokens can only include 28,224 tokens in the context before + truncation occurs. + + Clients can configure truncation behavior to truncate with a lower max token + limit, which is an effective way to control token usage and cost. + + Truncation will reduce the number of cached tokens on the next turn (busting the + cache), since messages are dropped from the beginning of the context. However, + clients can also configure truncation to retain messages up to a fraction of the + maximum context size, which will reduce the need for future truncations and thus + improve the cache rate. + + Truncation can be disabled entirely, which means the server will never truncate + but would instead return an error if the conversation exceeds the model's input + token limit. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_session_create_response.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_session_create_response.py new file mode 100644 index 0000000..fee2b2e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_session_create_response.py @@ -0,0 +1,554 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .realtime_reasoning import RealtimeReasoning +from .audio_transcription import AudioTranscription +from .realtime_truncation import RealtimeTruncation +from .noise_reduction_type import NoiseReductionType +from .realtime_audio_formats import RealtimeAudioFormats +from .realtime_function_tool import RealtimeFunctionTool +from ..responses.response_prompt import ResponsePrompt +from ..responses.tool_choice_mcp import ToolChoiceMcp +from ..responses.tool_choice_options import ToolChoiceOptions +from ..responses.tool_choice_function import ToolChoiceFunction + +__all__ = [ + "RealtimeSessionCreateResponse", + "Audio", + "AudioInput", + "AudioInputNoiseReduction", + "AudioInputTurnDetection", + "AudioInputTurnDetectionServerVad", + "AudioInputTurnDetectionSemanticVad", + "AudioOutput", + "ToolChoice", + "Tool", + "ToolMcpTool", + "ToolMcpToolAllowedTools", + "ToolMcpToolAllowedToolsMcpToolFilter", + "ToolMcpToolRequireApproval", + "ToolMcpToolRequireApprovalMcpToolApprovalFilter", + "ToolMcpToolRequireApprovalMcpToolApprovalFilterAlways", + "ToolMcpToolRequireApprovalMcpToolApprovalFilterNever", + "Tracing", + "TracingTracingConfiguration", +] + + +class AudioInputNoiseReduction(BaseModel): + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. + Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model. + Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio. + """ + + type: Optional[NoiseReductionType] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class AudioInputTurnDetectionServerVad(BaseModel): + """ + Server-side voice activity detection (VAD) which flips on when user speech is detected and off after a period of silence. + """ + + type: Literal["server_vad"] + """Type of turn detection, `server_vad` to turn on simple Server VAD.""" + + create_response: Optional[bool] = None + """Whether or not to automatically generate a response when a VAD stop event + occurs. + + If `interrupt_response` is set to `false` this may fail to create a response if + the model is already responding. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. + """ + + idle_timeout_ms: Optional[int] = None + """Optional timeout after which a model response will be triggered automatically. + + This is useful for situations in which a long pause from the user is unexpected, + such as a phone call. The model will effectively prompt the user to continue the + conversation based on the current context. + + The timeout value will be applied after the last model response's audio has + finished playing, i.e. it's set to the `response.done` time plus audio playback + duration. + + An `input_audio_buffer.timeout_triggered` event (plus events associated with the + Response) will be emitted when the timeout is reached. Idle timeout is currently + only supported for `server_vad` mode. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt (cancel) any ongoing response with + output to the default conversation (i.e. `conversation` of `auto`) when a VAD + start event occurs. If `true` then the response will be cancelled, otherwise it + will continue until complete. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. + """ + + prefix_padding_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: Optional[float] = None + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + +class AudioInputTurnDetectionSemanticVad(BaseModel): + """ + Server-side semantic turn detection which uses a model to determine when the user has finished speaking. + """ + + type: Literal["semantic_vad"] + """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" + + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, + 4s, and 2s respectively. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + +AudioInputTurnDetection: TypeAlias = Annotated[ + Union[AudioInputTurnDetectionServerVad, AudioInputTurnDetectionSemanticVad, None], + PropertyInfo(discriminator="type"), +] + + +class AudioInput(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The format of the input audio.""" + + noise_reduction: Optional[AudioInputNoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + transcription: Optional[AudioTranscription] = None + + turn_detection: Optional[AudioInputTurnDetection] = None + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. + + Server VAD means that the model will detect the start and end of speech based on + audio volume and respond at the end of user speech. + + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + + For `gpt-realtime-whisper` transcription sessions, turn detection must be set to + `null`; VAD is not supported. + """ + + +class AudioOutput(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The format of the output audio.""" + + speed: Optional[float] = None + """ + The speed of the model's spoken response as a multiple of the original speed. + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + + This parameter is a post-processing adjustment to the audio after it is + generated, it's also possible to prompt the model to speak faster or slower. + """ + + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None + ] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. We recommend + `marin` and `cedar` for best quality. + """ + + +class Audio(BaseModel): + """Configuration for input and output audio.""" + + input: Optional[AudioInput] = None + + output: Optional[AudioOutput] = None + + +ToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunction, ToolChoiceMcp] + + +class ToolMcpToolAllowedToolsMcpToolFilter(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +ToolMcpToolAllowedTools: TypeAlias = Union[List[str], ToolMcpToolAllowedToolsMcpToolFilter, None] + + +class ToolMcpToolRequireApprovalMcpToolApprovalFilterAlways(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class ToolMcpToolRequireApprovalMcpToolApprovalFilterNever(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class ToolMcpToolRequireApprovalMcpToolApprovalFilter(BaseModel): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + + always: Optional[ToolMcpToolRequireApprovalMcpToolApprovalFilterAlways] = None + """A filter object to specify which tools are allowed.""" + + never: Optional[ToolMcpToolRequireApprovalMcpToolApprovalFilterNever] = None + """A filter object to specify which tools are allowed.""" + + +ToolMcpToolRequireApproval: TypeAlias = Union[ + ToolMcpToolRequireApprovalMcpToolApprovalFilter, Literal["always", "never"], None +] + + +class ToolMcpTool(BaseModel): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + """ + + server_label: str + """A label for this MCP server, used to identify it in tool calls.""" + + type: Literal["mcp"] + """The type of the MCP tool. Always `mcp`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + allowed_tools: Optional[ToolMcpToolAllowedTools] = None + """List of allowed tool names or a filter object.""" + + authorization: Optional[str] = None + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Optional[ + Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + ] = None + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors + [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors). + + This field is deprecated for models released after September 1, 2026. Use + `server_url` to connect to a remote MCP server, or `tunnel_id` to connect + through a Secure MCP Tunnel. + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + defer_loading: Optional[bool] = None + """Whether this MCP tool is deferred and discovered via tool search.""" + + headers: Optional[Dict[str, str]] = None + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[ToolMcpToolRequireApproval] = None + """Specify which of the MCP server's tools require approval.""" + + server_description: Optional[str] = None + """Optional description of the MCP server, used to provide more context.""" + + server_url: Optional[str] = None + """The URL for the MCP server. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: Optional[str] = None + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + +Tool: TypeAlias = Union[RealtimeFunctionTool, ToolMcpTool] + + +class TracingTracingConfiguration(BaseModel): + """Granular configuration for tracing.""" + + group_id: Optional[str] = None + """ + The group id to attach to this trace to enable filtering and grouping in the + Traces Dashboard. + """ + + metadata: Optional[object] = None + """ + The arbitrary metadata to attach to this trace to enable filtering in the Traces + Dashboard. + """ + + workflow_name: Optional[str] = None + """The name of the workflow to attach to this trace. + + This is used to name the trace in the Traces Dashboard. + """ + + +Tracing: TypeAlias = Union[Literal["auto"], TracingTracingConfiguration, None] + + +class RealtimeSessionCreateResponse(BaseModel): + """A Realtime session configuration object.""" + + id: str + """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" + + object: Literal["realtime.session"] + """The object type. Always `realtime.session`.""" + + type: Literal["realtime"] + """The type of session to create. Always `realtime` for the Realtime API.""" + + audio: Optional[Audio] = None + """Configuration for input and output audio.""" + + expires_at: Optional[int] = None + """Expiration timestamp for the session, in seconds since epoch.""" + + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """Additional fields to include in server outputs. + + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + model: Union[ + str, + Literal[ + "gpt-realtime", + "gpt-realtime-1.5", + "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15", + ], + None, + ] = None + """The Realtime model used for this session.""" + + output_modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + It defaults to `["audio"]`, indicating that the model will respond with audio + plus a transcript. `["text"]` can be used to make the model respond with text + only. It is not possible to request both `text` and `audio` at the same time. + """ + + prompt: Optional[ResponsePrompt] = None + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + reasoning: Optional[RealtimeReasoning] = None + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + + tool_choice: Optional[ToolChoice] = None + """How the model chooses tools. + + Provide one of the string modes or force a specific function/MCP tool. + """ + + tools: Optional[List[Tool]] = None + """Tools available to the model.""" + + tracing: Optional[Tracing] = None + """ + Realtime API can write session traces to the + [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to + disable tracing. Once tracing is enabled for a session, the configuration cannot + be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + truncation: Optional[RealtimeTruncation] = None + """ + When the number of tokens in a conversation exceeds the model's input token + limit, the conversation be truncated, meaning messages (starting from the + oldest) will not be included in the model's context. A 32k context model with + 4,096 max output tokens can only include 28,224 tokens in the context before + truncation occurs. + + Clients can configure truncation behavior to truncate with a lower max token + limit, which is an effective way to control token usage and cost. + + Truncation will reduce the number of cached tokens on the next turn (busting the + cache), since messages are dropped from the beginning of the context. However, + clients can also configure truncation to retain messages up to a fraction of the + maximum context size, which will reduce the need for future truncations and thus + improve the cache rate. + + Truncation can be disabled entirely, which means the server will never truncate + but would instead return an error if the conversation exceeds the model's input + token limit. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tool_choice_config.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tool_choice_config.py new file mode 100644 index 0000000..31fb018 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tool_choice_config.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import TypeAlias + +from ..responses.tool_choice_mcp import ToolChoiceMcp +from ..responses.tool_choice_options import ToolChoiceOptions +from ..responses.tool_choice_function import ToolChoiceFunction + +__all__ = ["RealtimeToolChoiceConfig"] + +RealtimeToolChoiceConfig: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunction, ToolChoiceMcp] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tool_choice_config_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tool_choice_config_param.py new file mode 100644 index 0000000..4f3a41b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tool_choice_config_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from ..responses.tool_choice_options import ToolChoiceOptions +from ..responses.tool_choice_mcp_param import ToolChoiceMcpParam +from ..responses.tool_choice_function_param import ToolChoiceFunctionParam + +__all__ = ["RealtimeToolChoiceConfigParam"] + +RealtimeToolChoiceConfigParam: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunctionParam, ToolChoiceMcpParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tools_config.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tools_config.py new file mode 100644 index 0000000..e8a58b8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tools_config.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .realtime_tools_config_union import RealtimeToolsConfigUnion + +__all__ = ["RealtimeToolsConfig"] + +RealtimeToolsConfig: TypeAlias = List[RealtimeToolsConfigUnion] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tools_config_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tools_config_param.py new file mode 100644 index 0000000..4e7fc1d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tools_config_param.py @@ -0,0 +1,177 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .realtime_function_tool_param import RealtimeFunctionToolParam + +__all__ = [ + "RealtimeToolsConfigParam", + "RealtimeToolsConfigUnionParam", + "Mcp", + "McpAllowedTools", + "McpAllowedToolsMcpToolFilter", + "McpRequireApproval", + "McpRequireApprovalMcpToolApprovalFilter", + "McpRequireApprovalMcpToolApprovalFilterAlways", + "McpRequireApprovalMcpToolApprovalFilterNever", +] + + +class McpAllowedToolsMcpToolFilter(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +McpAllowedTools: TypeAlias = Union[SequenceNotStr[str], McpAllowedToolsMcpToolFilter] + + +class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + + always: McpRequireApprovalMcpToolApprovalFilterAlways + """A filter object to specify which tools are allowed.""" + + never: McpRequireApprovalMcpToolApprovalFilterNever + """A filter object to specify which tools are allowed.""" + + +McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"]] + + +class Mcp(TypedDict, total=False): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + """ + + server_label: Required[str] + """A label for this MCP server, used to identify it in tool calls.""" + + type: Required[Literal["mcp"]] + """The type of the MCP tool. Always `mcp`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + allowed_tools: Optional[McpAllowedTools] + """List of allowed tool names or a filter object.""" + + authorization: str + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors + [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors). + + This field is deprecated for models released after September 1, 2026. Use + `server_url` to connect to a remote MCP server, or `tunnel_id` to connect + through a Secure MCP Tunnel. + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + defer_loading: bool + """Whether this MCP tool is deferred and discovered via tool search.""" + + headers: Optional[Dict[str, str]] + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[McpRequireApproval] + """Specify which of the MCP server's tools require approval.""" + + server_description: str + """Optional description of the MCP server, used to provide more context.""" + + server_url: str + """The URL for the MCP server. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: str + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + +RealtimeToolsConfigUnionParam: TypeAlias = Union[RealtimeFunctionToolParam, Mcp] + +RealtimeToolsConfigParam: TypeAlias = List[RealtimeToolsConfigUnionParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tools_config_union.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tools_config_union.py new file mode 100644 index 0000000..fece641 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tools_config_union.py @@ -0,0 +1,175 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .realtime_function_tool import RealtimeFunctionTool + +__all__ = [ + "RealtimeToolsConfigUnion", + "Mcp", + "McpAllowedTools", + "McpAllowedToolsMcpToolFilter", + "McpRequireApproval", + "McpRequireApprovalMcpToolApprovalFilter", + "McpRequireApprovalMcpToolApprovalFilterAlways", + "McpRequireApprovalMcpToolApprovalFilterNever", +] + + +class McpAllowedToolsMcpToolFilter(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpToolFilter, None] + + +class McpRequireApprovalMcpToolApprovalFilterAlways(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilterNever(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilter(BaseModel): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + + always: Optional[McpRequireApprovalMcpToolApprovalFilterAlways] = None + """A filter object to specify which tools are allowed.""" + + never: Optional[McpRequireApprovalMcpToolApprovalFilterNever] = None + """A filter object to specify which tools are allowed.""" + + +McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"], None] + + +class Mcp(BaseModel): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + """ + + server_label: str + """A label for this MCP server, used to identify it in tool calls.""" + + type: Literal["mcp"] + """The type of the MCP tool. Always `mcp`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + allowed_tools: Optional[McpAllowedTools] = None + """List of allowed tool names or a filter object.""" + + authorization: Optional[str] = None + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Optional[ + Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + ] = None + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors + [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors). + + This field is deprecated for models released after September 1, 2026. Use + `server_url` to connect to a remote MCP server, or `tunnel_id` to connect + through a Secure MCP Tunnel. + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + defer_loading: Optional[bool] = None + """Whether this MCP tool is deferred and discovered via tool search.""" + + headers: Optional[Dict[str, str]] = None + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[McpRequireApproval] = None + """Specify which of the MCP server's tools require approval.""" + + server_description: Optional[str] = None + """Optional description of the MCP server, used to provide more context.""" + + server_url: Optional[str] = None + """The URL for the MCP server. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: Optional[str] = None + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + +RealtimeToolsConfigUnion: TypeAlias = Annotated[Union[RealtimeFunctionTool, Mcp], PropertyInfo(discriminator="type")] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tools_config_union_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tools_config_union_param.py new file mode 100644 index 0000000..5a08352 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tools_config_union_param.py @@ -0,0 +1,174 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .realtime_function_tool_param import RealtimeFunctionToolParam + +__all__ = [ + "RealtimeToolsConfigUnionParam", + "Mcp", + "McpAllowedTools", + "McpAllowedToolsMcpToolFilter", + "McpRequireApproval", + "McpRequireApprovalMcpToolApprovalFilter", + "McpRequireApprovalMcpToolApprovalFilterAlways", + "McpRequireApprovalMcpToolApprovalFilterNever", +] + + +class McpAllowedToolsMcpToolFilter(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +McpAllowedTools: TypeAlias = Union[SequenceNotStr[str], McpAllowedToolsMcpToolFilter] + + +class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + + always: McpRequireApprovalMcpToolApprovalFilterAlways + """A filter object to specify which tools are allowed.""" + + never: McpRequireApprovalMcpToolApprovalFilterNever + """A filter object to specify which tools are allowed.""" + + +McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"]] + + +class Mcp(TypedDict, total=False): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + """ + + server_label: Required[str] + """A label for this MCP server, used to identify it in tool calls.""" + + type: Required[Literal["mcp"]] + """The type of the MCP tool. Always `mcp`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + allowed_tools: Optional[McpAllowedTools] + """List of allowed tool names or a filter object.""" + + authorization: str + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors + [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors). + + This field is deprecated for models released after September 1, 2026. Use + `server_url` to connect to a remote MCP server, or `tunnel_id` to connect + through a Secure MCP Tunnel. + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + defer_loading: bool + """Whether this MCP tool is deferred and discovered via tool search.""" + + headers: Optional[Dict[str, str]] + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[McpRequireApproval] + """Specify which of the MCP server's tools require approval.""" + + server_description: str + """Optional description of the MCP server, used to provide more context.""" + + server_url: str + """The URL for the MCP server. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: str + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + +RealtimeToolsConfigUnionParam: TypeAlias = Union[RealtimeFunctionToolParam, Mcp] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tracing_config.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tracing_config.py new file mode 100644 index 0000000..a7d767a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tracing_config.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel + +__all__ = ["RealtimeTracingConfig", "TracingConfiguration"] + + +class TracingConfiguration(BaseModel): + """Granular configuration for tracing.""" + + group_id: Optional[str] = None + """ + The group id to attach to this trace to enable filtering and grouping in the + Traces Dashboard. + """ + + metadata: Optional[object] = None + """ + The arbitrary metadata to attach to this trace to enable filtering in the Traces + Dashboard. + """ + + workflow_name: Optional[str] = None + """The name of the workflow to attach to this trace. + + This is used to name the trace in the Traces Dashboard. + """ + + +RealtimeTracingConfig: TypeAlias = Union[Literal["auto"], TracingConfiguration, None] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tracing_config_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tracing_config_param.py new file mode 100644 index 0000000..87826dd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_tracing_config_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias, TypedDict + +__all__ = ["RealtimeTracingConfigParam", "TracingConfiguration"] + + +class TracingConfiguration(TypedDict, total=False): + """Granular configuration for tracing.""" + + group_id: str + """ + The group id to attach to this trace to enable filtering and grouping in the + Traces Dashboard. + """ + + metadata: object + """ + The arbitrary metadata to attach to this trace to enable filtering in the Traces + Dashboard. + """ + + workflow_name: str + """The name of the workflow to attach to this trace. + + This is used to name the trace in the Traces Dashboard. + """ + + +RealtimeTracingConfigParam: TypeAlias = Union[Literal["auto"], TracingConfiguration] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio.py new file mode 100644 index 0000000..652e381 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .realtime_transcription_session_audio_input import RealtimeTranscriptionSessionAudioInput + +__all__ = ["RealtimeTranscriptionSessionAudio"] + + +class RealtimeTranscriptionSessionAudio(BaseModel): + """Configuration for input and output audio.""" + + input: Optional[RealtimeTranscriptionSessionAudioInput] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_input.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_input.py new file mode 100644 index 0000000..a80aa80 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_input.py @@ -0,0 +1,75 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .audio_transcription import AudioTranscription +from .noise_reduction_type import NoiseReductionType +from .realtime_audio_formats import RealtimeAudioFormats +from .realtime_transcription_session_audio_input_turn_detection import ( + RealtimeTranscriptionSessionAudioInputTurnDetection, +) + +__all__ = ["RealtimeTranscriptionSessionAudioInput", "NoiseReduction"] + + +class NoiseReduction(BaseModel): + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. + Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model. + Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio. + """ + + type: Optional[NoiseReductionType] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class RealtimeTranscriptionSessionAudioInput(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The PCM audio format. Only a 24kHz sample rate is supported.""" + + noise_reduction: Optional[NoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + transcription: Optional[AudioTranscription] = None + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + turn_detection: Optional[RealtimeTranscriptionSessionAudioInputTurnDetection] = None + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. + + Server VAD means that the model will detect the start and end of speech based on + audio volume and respond at the end of user speech. + + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + + For `gpt-realtime-whisper` transcription sessions, turn detection must be set to + `null`; VAD is not supported. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_input_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_input_param.py new file mode 100644 index 0000000..f58885c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_input_param.py @@ -0,0 +1,77 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +from .noise_reduction_type import NoiseReductionType +from .audio_transcription_param import AudioTranscriptionParam +from .realtime_audio_formats_param import RealtimeAudioFormatsParam +from .realtime_transcription_session_audio_input_turn_detection_param import ( + RealtimeTranscriptionSessionAudioInputTurnDetectionParam, +) + +__all__ = ["RealtimeTranscriptionSessionAudioInputParam", "NoiseReduction"] + + +class NoiseReduction(TypedDict, total=False): + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. + Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model. + Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio. + """ + + type: NoiseReductionType + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class RealtimeTranscriptionSessionAudioInputParam(TypedDict, total=False): + format: RealtimeAudioFormatsParam + """The PCM audio format. Only a 24kHz sample rate is supported.""" + + noise_reduction: NoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + transcription: AudioTranscriptionParam + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + turn_detection: Optional[RealtimeTranscriptionSessionAudioInputTurnDetectionParam] + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. + + Server VAD means that the model will detect the start and end of speech based on + audio volume and respond at the end of user speech. + + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + + For `gpt-realtime-whisper` transcription sessions, turn detection must be set to + `null`; VAD is not supported. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py new file mode 100644 index 0000000..74f34e8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["RealtimeTranscriptionSessionAudioInputTurnDetection", "ServerVad", "SemanticVad"] + + +class ServerVad(BaseModel): + """ + Server-side voice activity detection (VAD) which flips on when user speech is detected and off after a period of silence. + """ + + type: Literal["server_vad"] + """Type of turn detection, `server_vad` to turn on simple Server VAD.""" + + create_response: Optional[bool] = None + """Whether or not to automatically generate a response when a VAD stop event + occurs. + + If `interrupt_response` is set to `false` this may fail to create a response if + the model is already responding. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. + """ + + idle_timeout_ms: Optional[int] = None + """Optional timeout after which a model response will be triggered automatically. + + This is useful for situations in which a long pause from the user is unexpected, + such as a phone call. The model will effectively prompt the user to continue the + conversation based on the current context. + + The timeout value will be applied after the last model response's audio has + finished playing, i.e. it's set to the `response.done` time plus audio playback + duration. + + An `input_audio_buffer.timeout_triggered` event (plus events associated with the + Response) will be emitted when the timeout is reached. Idle timeout is currently + only supported for `server_vad` mode. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt (cancel) any ongoing response with + output to the default conversation (i.e. `conversation` of `auto`) when a VAD + start event occurs. If `true` then the response will be cancelled, otherwise it + will continue until complete. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. + """ + + prefix_padding_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: Optional[float] = None + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + +class SemanticVad(BaseModel): + """ + Server-side semantic turn detection which uses a model to determine when the user has finished speaking. + """ + + type: Literal["semantic_vad"] + """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" + + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, + 4s, and 2s respectively. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + +RealtimeTranscriptionSessionAudioInputTurnDetection: TypeAlias = Annotated[ + Union[ServerVad, SemanticVad, None], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py new file mode 100644 index 0000000..82b378f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py @@ -0,0 +1,112 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["RealtimeTranscriptionSessionAudioInputTurnDetectionParam", "ServerVad", "SemanticVad"] + + +class ServerVad(TypedDict, total=False): + """ + Server-side voice activity detection (VAD) which flips on when user speech is detected and off after a period of silence. + """ + + type: Required[Literal["server_vad"]] + """Type of turn detection, `server_vad` to turn on simple Server VAD.""" + + create_response: bool + """Whether or not to automatically generate a response when a VAD stop event + occurs. + + If `interrupt_response` is set to `false` this may fail to create a response if + the model is already responding. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. + """ + + idle_timeout_ms: Optional[int] + """Optional timeout after which a model response will be triggered automatically. + + This is useful for situations in which a long pause from the user is unexpected, + such as a phone call. The model will effectively prompt the user to continue the + conversation based on the current context. + + The timeout value will be applied after the last model response's audio has + finished playing, i.e. it's set to the `response.done` time plus audio playback + duration. + + An `input_audio_buffer.timeout_triggered` event (plus events associated with the + Response) will be emitted when the timeout is reached. Idle timeout is currently + only supported for `server_vad` mode. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt (cancel) any ongoing response with + output to the default conversation (i.e. `conversation` of `auto`) when a VAD + start event occurs. If `true` then the response will be cancelled, otherwise it + will continue until complete. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. + """ + + prefix_padding_ms: int + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: int + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: float + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + +class SemanticVad(TypedDict, total=False): + """ + Server-side semantic turn detection which uses a model to determine when the user has finished speaking. + """ + + type: Required[Literal["semantic_vad"]] + """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" + + create_response: bool + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, + 4s, and 2s respectively. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + +RealtimeTranscriptionSessionAudioInputTurnDetectionParam: TypeAlias = Union[ServerVad, SemanticVad] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_param.py new file mode 100644 index 0000000..cc496de --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_audio_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .realtime_transcription_session_audio_input_param import RealtimeTranscriptionSessionAudioInputParam + +__all__ = ["RealtimeTranscriptionSessionAudioParam"] + + +class RealtimeTranscriptionSessionAudioParam(TypedDict, total=False): + """Configuration for input and output audio.""" + + input: RealtimeTranscriptionSessionAudioInputParam diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_create_request.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_create_request.py new file mode 100644 index 0000000..ce1f25d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_create_request.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_transcription_session_audio import RealtimeTranscriptionSessionAudio + +__all__ = ["RealtimeTranscriptionSessionCreateRequest"] + + +class RealtimeTranscriptionSessionCreateRequest(BaseModel): + """Realtime transcription session object configuration.""" + + type: Literal["transcription"] + """The type of session to create. + + Always `transcription` for transcription sessions. + """ + + audio: Optional[RealtimeTranscriptionSessionAudio] = None + """Configuration for input and output audio.""" + + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """Additional fields to include in server outputs. + + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_create_request_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_create_request_param.py new file mode 100644 index 0000000..8dfa532 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_create_request_param.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from .realtime_transcription_session_audio_param import RealtimeTranscriptionSessionAudioParam + +__all__ = ["RealtimeTranscriptionSessionCreateRequestParam"] + + +class RealtimeTranscriptionSessionCreateRequestParam(TypedDict, total=False): + """Realtime transcription session object configuration.""" + + type: Required[Literal["transcription"]] + """The type of session to create. + + Always `transcription` for transcription sessions. + """ + + audio: RealtimeTranscriptionSessionAudioParam + """Configuration for input and output audio.""" + + include: List[Literal["item.input_audio_transcription.logprobs"]] + """Additional fields to include in server outputs. + + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_create_response.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_create_response.py new file mode 100644 index 0000000..c821c37 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_create_response.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .audio_transcription import AudioTranscription +from .noise_reduction_type import NoiseReductionType +from .realtime_audio_formats import RealtimeAudioFormats +from .realtime_transcription_session_turn_detection import RealtimeTranscriptionSessionTurnDetection + +__all__ = ["RealtimeTranscriptionSessionCreateResponse", "Audio", "AudioInput", "AudioInputNoiseReduction"] + + +class AudioInputNoiseReduction(BaseModel): + """Configuration for input audio noise reduction.""" + + type: Optional[NoiseReductionType] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class AudioInput(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The PCM audio format. Only a 24kHz sample rate is supported.""" + + noise_reduction: Optional[AudioInputNoiseReduction] = None + """Configuration for input audio noise reduction.""" + + transcription: Optional[AudioTranscription] = None + + turn_detection: Optional[RealtimeTranscriptionSessionTurnDetection] = None + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. For `gpt-realtime-whisper`, this must be `null`; VAD is not supported. + """ + + +class Audio(BaseModel): + """Configuration for input audio for the session.""" + + input: Optional[AudioInput] = None + + +class RealtimeTranscriptionSessionCreateResponse(BaseModel): + """A Realtime transcription session configuration object.""" + + id: str + """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" + + object: str + """The object type. Always `realtime.transcription_session`.""" + + type: Literal["transcription"] + """The type of session. Always `transcription` for transcription sessions.""" + + audio: Optional[Audio] = None + """Configuration for input audio for the session.""" + + expires_at: Optional[int] = None + """Expiration timestamp for the session, in seconds since epoch.""" + + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """Additional fields to include in server outputs. + + - `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_turn_detection.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_turn_detection.py new file mode 100644 index 0000000..4e85255 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_transcription_session_turn_detection.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["RealtimeTranscriptionSessionTurnDetection"] + + +class RealtimeTranscriptionSessionTurnDetection(BaseModel): + """Configuration for turn detection. + + Can be set to `null` to turn off. Server + VAD means that the model will detect the start and end of speech based on + audio volume and respond at the end of user speech. For `gpt-realtime-whisper`, this must be `null`; VAD is not supported. + """ + + prefix_padding_ms: Optional[int] = None + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: Optional[float] = None + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: Optional[str] = None + """Type of turn detection, only `server_vad` is currently supported.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_truncation.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_truncation.py new file mode 100644 index 0000000..82be27c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_truncation.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from .realtime_truncation_retention_ratio import RealtimeTruncationRetentionRatio + +__all__ = ["RealtimeTruncation"] + +RealtimeTruncation: TypeAlias = Union[Literal["auto", "disabled"], RealtimeTruncationRetentionRatio] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_truncation_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_truncation_param.py new file mode 100644 index 0000000..0bbe278 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_truncation_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from .realtime_truncation_retention_ratio_param import RealtimeTruncationRetentionRatioParam + +__all__ = ["RealtimeTruncationParam"] + +RealtimeTruncationParam: TypeAlias = Union[Literal["auto", "disabled"], RealtimeTruncationRetentionRatioParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_truncation_retention_ratio.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_truncation_retention_ratio.py new file mode 100644 index 0000000..142fbb8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_truncation_retention_ratio.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeTruncationRetentionRatio", "TokenLimits"] + + +class TokenLimits(BaseModel): + """Optional custom token limits for this truncation strategy. + + If not provided, the model's default token limits will be used. + """ + + post_instructions: Optional[int] = None + """ + Maximum tokens allowed in the conversation after instructions (which including + tool definitions). For example, setting this to 5,000 would mean that truncation + would occur when the conversation exceeds 5,000 tokens after instructions. This + cannot be higher than the model's context window size minus the maximum output + tokens. + """ + + +class RealtimeTruncationRetentionRatio(BaseModel): + """ + Retain a fraction of the conversation tokens when the conversation exceeds the input token limit. This allows you to amortize truncations across multiple turns, which can help improve cached token usage. + """ + + retention_ratio: float + """ + Fraction of post-instruction conversation tokens to retain (`0.0` - `1.0`) when + the conversation exceeds the input token limit. Setting this to `0.8` means that + messages will be dropped until 80% of the maximum allowed tokens are used. This + helps reduce the frequency of truncations and improve cache rates. + """ + + type: Literal["retention_ratio"] + """Use retention ratio truncation.""" + + token_limits: Optional[TokenLimits] = None + """Optional custom token limits for this truncation strategy. + + If not provided, the model's default token limits will be used. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_truncation_retention_ratio_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_truncation_retention_ratio_param.py new file mode 100644 index 0000000..2369c8c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/realtime_truncation_retention_ratio_param.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeTruncationRetentionRatioParam", "TokenLimits"] + + +class TokenLimits(TypedDict, total=False): + """Optional custom token limits for this truncation strategy. + + If not provided, the model's default token limits will be used. + """ + + post_instructions: int + """ + Maximum tokens allowed in the conversation after instructions (which including + tool definitions). For example, setting this to 5,000 would mean that truncation + would occur when the conversation exceeds 5,000 tokens after instructions. This + cannot be higher than the model's context window size minus the maximum output + tokens. + """ + + +class RealtimeTruncationRetentionRatioParam(TypedDict, total=False): + """ + Retain a fraction of the conversation tokens when the conversation exceeds the input token limit. This allows you to amortize truncations across multiple turns, which can help improve cached token usage. + """ + + retention_ratio: Required[float] + """ + Fraction of post-instruction conversation tokens to retain (`0.0` - `1.0`) when + the conversation exceeds the input token limit. Setting this to `0.8` means that + messages will be dropped until 80% of the maximum allowed tokens are used. This + helps reduce the frequency of truncations and improve cache rates. + """ + + type: Required[Literal["retention_ratio"]] + """Use retention ratio truncation.""" + + token_limits: TokenLimits + """Optional custom token limits for this truncation strategy. + + If not provided, the model's default token limits will be used. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_audio_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_audio_delta_event.py new file mode 100644 index 0000000..9efa9bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_audio_delta_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioDeltaEvent"] + + +class ResponseAudioDeltaEvent(BaseModel): + """Returned when the model-generated audio is updated.""" + + content_index: int + """The index of the content part in the item's content array.""" + + delta: str + """Base64-encoded audio data delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.output_audio.delta"] + """The event type, must be `response.output_audio.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_audio_done_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_audio_done_event.py new file mode 100644 index 0000000..ec900cd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_audio_done_event.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioDoneEvent"] + + +class ResponseAudioDoneEvent(BaseModel): + """Returned when the model-generated audio is done. + + Also emitted when a Response + is interrupted, incomplete, or cancelled. + """ + + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.output_audio.done"] + """The event type, must be `response.output_audio.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_audio_transcript_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_audio_transcript_delta_event.py new file mode 100644 index 0000000..ea6a81b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_audio_transcript_delta_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioTranscriptDeltaEvent"] + + +class ResponseAudioTranscriptDeltaEvent(BaseModel): + """Returned when the model-generated transcription of audio output is updated.""" + + content_index: int + """The index of the content part in the item's content array.""" + + delta: str + """The transcript delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.output_audio_transcript.delta"] + """The event type, must be `response.output_audio_transcript.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_audio_transcript_done_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_audio_transcript_done_event.py new file mode 100644 index 0000000..cd42750 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_audio_transcript_done_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioTranscriptDoneEvent"] + + +class ResponseAudioTranscriptDoneEvent(BaseModel): + """ + Returned when the model-generated transcription of audio output is done + streaming. Also emitted when a Response is interrupted, incomplete, or + cancelled. + """ + + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + transcript: str + """The final transcript of the audio.""" + + type: Literal["response.output_audio_transcript.done"] + """The event type, must be `response.output_audio_transcript.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_cancel_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_cancel_event.py new file mode 100644 index 0000000..3d16c25 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_cancel_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCancelEvent"] + + +class ResponseCancelEvent(BaseModel): + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.done` event with a status of `response.status=cancelled`. If + there is no response to cancel, the server will respond with an error. It's safe + to call `response.cancel` even if no response is in progress, an error will be + returned the session will remain unaffected. + """ + + type: Literal["response.cancel"] + """The event type, must be `response.cancel`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" + + response_id: Optional[str] = None + """ + A specific response ID to cancel - if not provided, will cancel an in-progress + response in the default conversation. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_cancel_event_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_cancel_event_param.py new file mode 100644 index 0000000..b6f77aa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_cancel_event_param.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseCancelEventParam"] + + +class ResponseCancelEventParam(TypedDict, total=False): + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.done` event with a status of `response.status=cancelled`. If + there is no response to cancel, the server will respond with an error. It's safe + to call `response.cancel` even if no response is in progress, an error will be + returned the session will remain unaffected. + """ + + type: Required[Literal["response.cancel"]] + """The event type, must be `response.cancel`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" + + response_id: str + """ + A specific response ID to cancel - if not provided, will cancel an in-progress + response in the default conversation. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_content_part_added_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_content_part_added_event.py new file mode 100644 index 0000000..4a052c2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_content_part_added_event.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseContentPartAddedEvent", "Part"] + + +class Part(BaseModel): + """The content part that was added.""" + + audio: Optional[str] = None + """Base64-encoded audio data (if type is "audio").""" + + text: Optional[str] = None + """The text content (if type is "text").""" + + transcript: Optional[str] = None + """The transcript of the audio (if type is "audio").""" + + type: Optional[Literal["text", "audio"]] = None + """The content type ("text", "audio").""" + + +class ResponseContentPartAddedEvent(BaseModel): + """ + Returned when a new content part is added to an assistant message item during + response generation. + """ + + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item to which the content part was added.""" + + output_index: int + """The index of the output item in the response.""" + + part: Part + """The content part that was added.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.content_part.added"] + """The event type, must be `response.content_part.added`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_content_part_done_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_content_part_done_event.py new file mode 100644 index 0000000..1e3c411 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_content_part_done_event.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseContentPartDoneEvent", "Part"] + + +class Part(BaseModel): + """The content part that is done.""" + + audio: Optional[str] = None + """Base64-encoded audio data (if type is "audio").""" + + text: Optional[str] = None + """The text content (if type is "text").""" + + transcript: Optional[str] = None + """The transcript of the audio (if type is "audio").""" + + type: Optional[Literal["text", "audio"]] = None + """The content type ("text", "audio").""" + + +class ResponseContentPartDoneEvent(BaseModel): + """ + Returned when a content part is done streaming in an assistant message item. + Also emitted when a Response is interrupted, incomplete, or cancelled. + """ + + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + part: Part + """The content part that is done.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.content_part.done"] + """The event type, must be `response.content_part.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_create_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_create_event.py new file mode 100644 index 0000000..ad9f949 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_create_event.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_response_create_params import RealtimeResponseCreateParams + +__all__ = ["ResponseCreateEvent"] + + +class ResponseCreateEvent(BaseModel): + """ + This event instructs the server to create a Response, which means triggering + model inference. When in Server VAD mode, the server will create Responses + automatically. + + A Response will include at least one Item, and may have two, in which case + the second will be a function call. These Items will be appended to the + conversation history by default. + + The server will respond with a `response.created` event, events for Items + and content created, and finally a `response.done` event to indicate the + Response is complete. + + The `response.create` event includes inference configuration like + `instructions` and `tools`. If these are set, they will override the Session's + configuration for this Response only. + + Responses can be created out-of-band of the default Conversation, meaning that they can + have arbitrary input, and it's possible to disable writing the output to the Conversation. + Only one Response can write to the default Conversation at a time, but otherwise multiple + Responses can be created in parallel. The `metadata` field is a good way to disambiguate + multiple simultaneous Responses. + + Clients can set `conversation` to `none` to create a Response that does not write to the default + Conversation. Arbitrary input can be provided with the `input` field, which is an array accepting + raw Items and references to existing Items. + """ + + type: Literal["response.create"] + """The event type, must be `response.create`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" + + response: Optional[RealtimeResponseCreateParams] = None + """Create a new Realtime response with these parameters""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_create_event_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_create_event_param.py new file mode 100644 index 0000000..35fc980 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_create_event_param.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .realtime_response_create_params_param import RealtimeResponseCreateParamsParam + +__all__ = ["ResponseCreateEventParam"] + + +class ResponseCreateEventParam(TypedDict, total=False): + """ + This event instructs the server to create a Response, which means triggering + model inference. When in Server VAD mode, the server will create Responses + automatically. + + A Response will include at least one Item, and may have two, in which case + the second will be a function call. These Items will be appended to the + conversation history by default. + + The server will respond with a `response.created` event, events for Items + and content created, and finally a `response.done` event to indicate the + Response is complete. + + The `response.create` event includes inference configuration like + `instructions` and `tools`. If these are set, they will override the Session's + configuration for this Response only. + + Responses can be created out-of-band of the default Conversation, meaning that they can + have arbitrary input, and it's possible to disable writing the output to the Conversation. + Only one Response can write to the default Conversation at a time, but otherwise multiple + Responses can be created in parallel. The `metadata` field is a good way to disambiguate + multiple simultaneous Responses. + + Clients can set `conversation` to `none` to create a Response that does not write to the default + Conversation. Arbitrary input can be provided with the `input` field, which is an array accepting + raw Items and references to existing Items. + """ + + type: Required[Literal["response.create"]] + """The event type, must be `response.create`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" + + response: RealtimeResponseCreateParamsParam + """Create a new Realtime response with these parameters""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_created_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_created_event.py new file mode 100644 index 0000000..8048aea --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_created_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_response import RealtimeResponse + +__all__ = ["ResponseCreatedEvent"] + + +class ResponseCreatedEvent(BaseModel): + """Returned when a new Response is created. + + The first event of response creation, + where the response is in an initial state of `in_progress`. + """ + + event_id: str + """The unique ID of the server event.""" + + response: RealtimeResponse + """The response resource.""" + + type: Literal["response.created"] + """The event type, must be `response.created`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_done_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_done_event.py new file mode 100644 index 0000000..5ed4303 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_done_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_response import RealtimeResponse + +__all__ = ["ResponseDoneEvent"] + + +class ResponseDoneEvent(BaseModel): + """Returned when a Response is done streaming. + + Always emitted, no matter the + final state. The Response object included in the `response.done` event will + include all output Items in the Response but will omit the raw audio data. + + Clients should check the `status` field of the Response to determine if it was successful + (`completed`) or if there was another outcome: `cancelled`, `failed`, or `incomplete`. + + A response will contain all output items that were generated during the response, excluding + any audio content. + """ + + event_id: str + """The unique ID of the server event.""" + + response: RealtimeResponse + """The response resource.""" + + type: Literal["response.done"] + """The event type, must be `response.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_function_call_arguments_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_function_call_arguments_delta_event.py new file mode 100644 index 0000000..76c2144 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_function_call_arguments_delta_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFunctionCallArgumentsDeltaEvent"] + + +class ResponseFunctionCallArgumentsDeltaEvent(BaseModel): + """Returned when the model-generated function call arguments are updated.""" + + call_id: str + """The ID of the function call.""" + + delta: str + """The arguments delta as a JSON string.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the function call item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.function_call_arguments.delta"] + """The event type, must be `response.function_call_arguments.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_function_call_arguments_done_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_function_call_arguments_done_event.py new file mode 100644 index 0000000..7cd8d53 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_function_call_arguments_done_event.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFunctionCallArgumentsDoneEvent"] + + +class ResponseFunctionCallArgumentsDoneEvent(BaseModel): + """ + Returned when the model-generated function call arguments are done streaming. + Also emitted when a Response is interrupted, incomplete, or cancelled. + """ + + arguments: str + """The final arguments as a JSON string.""" + + call_id: str + """The ID of the function call.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the function call item.""" + + name: str + """The name of the function that was called.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.function_call_arguments.done"] + """The event type, must be `response.function_call_arguments.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_arguments_delta.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_arguments_delta.py new file mode 100644 index 0000000..30638dc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_arguments_delta.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallArgumentsDelta"] + + +class ResponseMcpCallArgumentsDelta(BaseModel): + """Returned when MCP tool call arguments are updated during response generation.""" + + delta: str + """The JSON-encoded arguments delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP tool call item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.mcp_call_arguments.delta"] + """The event type, must be `response.mcp_call_arguments.delta`.""" + + obfuscation: Optional[str] = None + """If present, indicates the delta text was obfuscated.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_arguments_done.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_arguments_done.py new file mode 100644 index 0000000..327b7db --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_arguments_done.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallArgumentsDone"] + + +class ResponseMcpCallArgumentsDone(BaseModel): + """Returned when MCP tool call arguments are finalized during response generation.""" + + arguments: str + """The final JSON-encoded arguments string.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP tool call item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.mcp_call_arguments.done"] + """The event type, must be `response.mcp_call_arguments.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_completed.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_completed.py new file mode 100644 index 0000000..39f4612 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_completed.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallCompleted"] + + +class ResponseMcpCallCompleted(BaseModel): + """Returned when an MCP tool call has completed successfully.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP tool call item.""" + + output_index: int + """The index of the output item in the response.""" + + type: Literal["response.mcp_call.completed"] + """The event type, must be `response.mcp_call.completed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_failed.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_failed.py new file mode 100644 index 0000000..66e917b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_failed.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallFailed"] + + +class ResponseMcpCallFailed(BaseModel): + """Returned when an MCP tool call has failed.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP tool call item.""" + + output_index: int + """The index of the output item in the response.""" + + type: Literal["response.mcp_call.failed"] + """The event type, must be `response.mcp_call.failed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_in_progress.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_in_progress.py new file mode 100644 index 0000000..3a9cd5c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_mcp_call_in_progress.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallInProgress"] + + +class ResponseMcpCallInProgress(BaseModel): + """Returned when an MCP tool call has started and is in progress.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP tool call item.""" + + output_index: int + """The index of the output item in the response.""" + + type: Literal["response.mcp_call.in_progress"] + """The event type, must be `response.mcp_call.in_progress`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_output_item_added_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_output_item_added_event.py new file mode 100644 index 0000000..c43eff6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_output_item_added_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ResponseOutputItemAddedEvent"] + + +class ResponseOutputItemAddedEvent(BaseModel): + """Returned when a new Item is created during Response generation.""" + + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """A single item within a Realtime conversation.""" + + output_index: int + """The index of the output item in the Response.""" + + response_id: str + """The ID of the Response to which the item belongs.""" + + type: Literal["response.output_item.added"] + """The event type, must be `response.output_item.added`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_output_item_done_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_output_item_done_event.py new file mode 100644 index 0000000..447ea17 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_output_item_done_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ResponseOutputItemDoneEvent"] + + +class ResponseOutputItemDoneEvent(BaseModel): + """Returned when an Item is done streaming. + + Also emitted when a Response is + interrupted, incomplete, or cancelled. + """ + + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """A single item within a Realtime conversation.""" + + output_index: int + """The index of the output item in the Response.""" + + response_id: str + """The ID of the Response to which the item belongs.""" + + type: Literal["response.output_item.done"] + """The event type, must be `response.output_item.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_text_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_text_delta_event.py new file mode 100644 index 0000000..0528036 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_text_delta_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseTextDeltaEvent"] + + +class ResponseTextDeltaEvent(BaseModel): + """Returned when the text value of an "output_text" content part is updated.""" + + content_index: int + """The index of the content part in the item's content array.""" + + delta: str + """The text delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.output_text.delta"] + """The event type, must be `response.output_text.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/response_text_done_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/response_text_done_event.py new file mode 100644 index 0000000..ae411c6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/response_text_done_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseTextDoneEvent"] + + +class ResponseTextDoneEvent(BaseModel): + """Returned when the text value of an "output_text" content part is done streaming. + + Also + emitted when a Response is interrupted, incomplete, or cancelled. + """ + + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + text: str + """The final text content.""" + + type: Literal["response.output_text.done"] + """The event type, must be `response.output_text.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/session_created_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/session_created_event.py new file mode 100644 index 0000000..9e2291b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/session_created_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .realtime_session_create_request import RealtimeSessionCreateRequest +from .realtime_transcription_session_create_request import RealtimeTranscriptionSessionCreateRequest + +__all__ = ["SessionCreatedEvent", "Session"] + +Session: TypeAlias = Union[RealtimeSessionCreateRequest, RealtimeTranscriptionSessionCreateRequest] + + +class SessionCreatedEvent(BaseModel): + """Returned when a Session is created. + + Emitted automatically when a new + connection is established as the first server event. This event will contain + the default Session configuration. + """ + + event_id: str + """The unique ID of the server event.""" + + session: Session + """The session configuration.""" + + type: Literal["session.created"] + """The event type, must be `session.created`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/session_update_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/session_update_event.py new file mode 100644 index 0000000..16a16ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/session_update_event.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .realtime_session_create_request import RealtimeSessionCreateRequest +from .realtime_transcription_session_create_request import RealtimeTranscriptionSessionCreateRequest + +__all__ = ["SessionUpdateEvent", "Session"] + +Session: TypeAlias = Union[RealtimeSessionCreateRequest, RealtimeTranscriptionSessionCreateRequest] + + +class SessionUpdateEvent(BaseModel): + """ + Send this event to update the session’s configuration. + The client may send this event at any time to update any field + except for `voice` and `model`. `voice` can be updated only if there have been no other audio outputs yet. + + When the server receives a `session.update`, it will respond + with a `session.updated` event showing the full, effective configuration. + Only the fields that are present in the `session.update` are updated. To clear a field like + `instructions`, pass an empty string. To clear a field like `tools`, pass an empty array. + To clear a field like `turn_detection`, pass `null`. + """ + + session: Session + """Update the Realtime session. + + Choose either a realtime session or a transcription session. + """ + + type: Literal["session.update"] + """The event type, must be `session.update`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event. + + This is an arbitrary string that a client may assign. It will be passed back if + there is an error with the event, but the corresponding `session.updated` event + will not include it. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/session_update_event_param.py b/venv/lib/python3.12/site-packages/openai/types/realtime/session_update_event_param.py new file mode 100644 index 0000000..8f75bf1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/session_update_event_param.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .realtime_session_create_request_param import RealtimeSessionCreateRequestParam +from .realtime_transcription_session_create_request_param import RealtimeTranscriptionSessionCreateRequestParam + +__all__ = ["SessionUpdateEventParam", "Session"] + +Session: TypeAlias = Union[RealtimeSessionCreateRequestParam, RealtimeTranscriptionSessionCreateRequestParam] + + +class SessionUpdateEventParam(TypedDict, total=False): + """ + Send this event to update the session’s configuration. + The client may send this event at any time to update any field + except for `voice` and `model`. `voice` can be updated only if there have been no other audio outputs yet. + + When the server receives a `session.update`, it will respond + with a `session.updated` event showing the full, effective configuration. + Only the fields that are present in the `session.update` are updated. To clear a field like + `instructions`, pass an empty string. To clear a field like `tools`, pass an empty array. + To clear a field like `turn_detection`, pass `null`. + """ + + session: Required[Session] + """Update the Realtime session. + + Choose either a realtime session or a transcription session. + """ + + type: Required[Literal["session.update"]] + """The event type, must be `session.update`.""" + + event_id: str + """Optional client-generated ID used to identify this event. + + This is an arbitrary string that a client may assign. It will be passed back if + there is an error with the event, but the corresponding `session.updated` event + will not include it. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/realtime/session_updated_event.py b/venv/lib/python3.12/site-packages/openai/types/realtime/session_updated_event.py new file mode 100644 index 0000000..b2fb607 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/realtime/session_updated_event.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .realtime_session_create_request import RealtimeSessionCreateRequest +from .realtime_transcription_session_create_request import RealtimeTranscriptionSessionCreateRequest + +__all__ = ["SessionUpdatedEvent", "Session"] + +Session: TypeAlias = Union[RealtimeSessionCreateRequest, RealtimeTranscriptionSessionCreateRequest] + + +class SessionUpdatedEvent(BaseModel): + """ + Returned when a session is updated with a `session.update` event, unless + there is an error. + """ + + event_id: str + """The unique ID of the server event.""" + + session: Session + """The session configuration.""" + + type: Literal["session.updated"] + """The event type, must be `session.updated`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__init__.py b/venv/lib/python3.12/site-packages/openai/types/responses/__init__.py new file mode 100644 index 0000000..d54f73a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/__init__.py @@ -0,0 +1,369 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .tool import Tool as Tool +from .response import Response as Response +from .tool_param import ToolParam as ToolParam +from .custom_tool import CustomTool as CustomTool +from .local_skill import LocalSkill as LocalSkill +from .image_detail import ImageDetail as ImageDetail +from .inline_skill import InlineSkill as InlineSkill +from .service_tier import ServiceTier as ServiceTier +from .computer_tool import ComputerTool as ComputerTool +from .function_tool import FunctionTool as FunctionTool +from .response_item import ResponseItem as ResponseItem +from .container_auto import ContainerAuto as ContainerAuto +from .namespace_tool import NamespaceTool as NamespaceTool +from .response_error import ResponseError as ResponseError +from .response_input import ResponseInput as ResponseInput +from .response_usage import ResponseUsage as ResponseUsage +from .computer_action import ComputerAction as ComputerAction +from .parsed_response import ( + ParsedContent as ParsedContent, + ParsedResponse as ParsedResponse, + ParsedResponseOutputItem as ParsedResponseOutputItem, + ParsedResponseOutputText as ParsedResponseOutputText, + ParsedResponseOutputMessage as ParsedResponseOutputMessage, + ParsedResponseFunctionToolCall as ParsedResponseFunctionToolCall, +) +from .response_prompt import ResponsePrompt as ResponsePrompt +from .response_status import ResponseStatus as ResponseStatus +from .skill_reference import SkillReference as SkillReference +from .tool_choice_mcp import ToolChoiceMcp as ToolChoiceMcp +from .web_search_tool import WebSearchTool as WebSearchTool +from .apply_patch_tool import ApplyPatchTool as ApplyPatchTool +from .file_search_tool import FileSearchTool as FileSearchTool +from .tool_search_tool import ToolSearchTool as ToolSearchTool +from .custom_tool_param import CustomToolParam as CustomToolParam +from .local_environment import LocalEnvironment as LocalEnvironment +from .local_skill_param import LocalSkillParam as LocalSkillParam +from .tool_choice_shell import ToolChoiceShell as ToolChoiceShell +from .tool_choice_types import ToolChoiceTypes as ToolChoiceTypes +from .compacted_response import CompactedResponse as CompactedResponse +from .easy_input_message import EasyInputMessage as EasyInputMessage +from .inline_skill_param import InlineSkillParam as InlineSkillParam +from .response_item_list import ResponseItemList as ResponseItemList +from .tool_choice_custom import ToolChoiceCustom as ToolChoiceCustom +from .computer_tool_param import ComputerToolParam as ComputerToolParam +from .container_reference import ContainerReference as ContainerReference +from .function_shell_tool import FunctionShellTool as FunctionShellTool +from .function_tool_param import FunctionToolParam as FunctionToolParam +from .inline_skill_source import InlineSkillSource as InlineSkillSource +from .mcp_tool_call_error import McpToolCallError as McpToolCallError +from .response_includable import ResponseIncludable as ResponseIncludable +from .response_input_file import ResponseInputFile as ResponseInputFile +from .response_input_item import ResponseInputItem as ResponseInputItem +from .response_input_text import ResponseInputText as ResponseInputText +from .tool_choice_allowed import ToolChoiceAllowed as ToolChoiceAllowed +from .tool_choice_options import ToolChoiceOptions as ToolChoiceOptions +from .computer_action_list import ComputerActionList as ComputerActionList +from .container_auto_param import ContainerAutoParam as ContainerAutoParam +from .namespace_tool_param import NamespaceToolParam as NamespaceToolParam +from .response_error_event import ResponseErrorEvent as ResponseErrorEvent +from .response_input_audio import ResponseInputAudio as ResponseInputAudio +from .response_input_image import ResponseInputImage as ResponseInputImage +from .response_input_param import ResponseInputParam as ResponseInputParam +from .response_output_item import ResponseOutputItem as ResponseOutputItem +from .response_output_text import ResponseOutputText as ResponseOutputText +from .response_steer_event import ResponseSteerEvent as ResponseSteerEvent +from .response_steer_input import ResponseSteerInput as ResponseSteerInput +from .response_text_config import ResponseTextConfig as ResponseTextConfig +from .tool_choice_function import ToolChoiceFunction as ToolChoiceFunction +from .computer_action_param import ComputerActionParam as ComputerActionParam +from .response_failed_event import ResponseFailedEvent as ResponseFailedEvent +from .response_prompt_param import ResponsePromptParam as ResponsePromptParam +from .response_queued_event import ResponseQueuedEvent as ResponseQueuedEvent +from .response_stream_event import ResponseStreamEvent as ResponseStreamEvent +from .skill_reference_param import SkillReferenceParam as SkillReferenceParam +from .tool_choice_mcp_param import ToolChoiceMcpParam as ToolChoiceMcpParam +from .web_search_tool_param import WebSearchToolParam as WebSearchToolParam +from .apply_patch_tool_param import ApplyPatchToolParam as ApplyPatchToolParam +from .file_search_tool_param import FileSearchToolParam as FileSearchToolParam +from .input_item_list_params import InputItemListParams as InputItemListParams +from .response_create_params import ResponseCreateParams as ResponseCreateParams +from .response_created_event import ResponseCreatedEvent as ResponseCreatedEvent +from .response_input_content import ResponseInputContent as ResponseInputContent +from .responses_client_event import ResponsesClientEvent as ResponsesClientEvent +from .responses_server_event import ResponsesServerEvent as ResponsesServerEvent +from .tool_search_tool_param import ToolSearchToolParam as ToolSearchToolParam +from .local_environment_param import LocalEnvironmentParam as LocalEnvironmentParam +from .response_compact_params import ResponseCompactParams as ResponseCompactParams +from .response_output_message import ResponseOutputMessage as ResponseOutputMessage +from .response_output_refusal import ResponseOutputRefusal as ResponseOutputRefusal +from .response_reasoning_item import ResponseReasoningItem as ResponseReasoningItem +from .tool_choice_apply_patch import ToolChoiceApplyPatch as ToolChoiceApplyPatch +from .tool_choice_shell_param import ToolChoiceShellParam as ToolChoiceShellParam +from .tool_choice_types_param import ToolChoiceTypesParam as ToolChoiceTypesParam +from .web_search_preview_tool import WebSearchPreviewTool as WebSearchPreviewTool +from .easy_input_message_param import EasyInputMessageParam as EasyInputMessageParam +from .input_token_count_params import InputTokenCountParams as InputTokenCountParams +from .response_compaction_item import ResponseCompactionItem as ResponseCompactionItem +from .response_completed_event import ResponseCompletedEvent as ResponseCompletedEvent +from .response_retrieve_params import ResponseRetrieveParams as ResponseRetrieveParams +from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent +from .tool_choice_custom_param import ToolChoiceCustomParam as ToolChoiceCustomParam +from .computer_use_preview_tool import ComputerUsePreviewTool as ComputerUsePreviewTool +from .container_reference_param import ContainerReferenceParam as ContainerReferenceParam +from .function_shell_tool_param import FunctionShellToolParam as FunctionShellToolParam +from .inline_skill_source_param import InlineSkillSourceParam as InlineSkillSourceParam +from .mcp_tool_call_error_param import McpToolCallErrorParam as McpToolCallErrorParam +from .response_audio_done_event import ResponseAudioDoneEvent as ResponseAudioDoneEvent +from .response_custom_tool_call import ResponseCustomToolCall as ResponseCustomToolCall +from .response_incomplete_event import ResponseIncompleteEvent as ResponseIncompleteEvent +from .response_input_file_param import ResponseInputFileParam as ResponseInputFileParam +from .response_input_item_param import ResponseInputItemParam as ResponseInputItemParam +from .response_input_text_param import ResponseInputTextParam as ResponseInputTextParam +from .response_steer_error_code import ResponseSteerErrorCode as ResponseSteerErrorCode +from .response_text_delta_event import ResponseTextDeltaEvent as ResponseTextDeltaEvent +from .response_tool_search_call import ResponseToolSearchCall as ResponseToolSearchCall +from .tool_choice_allowed_param import ToolChoiceAllowedParam as ToolChoiceAllowedParam +from .computer_action_list_param import ComputerActionListParam as ComputerActionListParam +from .input_token_count_response import InputTokenCountResponse as InputTokenCountResponse +from .response_audio_delta_event import ResponseAudioDeltaEvent as ResponseAudioDeltaEvent +from .response_in_progress_event import ResponseInProgressEvent as ResponseInProgressEvent +from .response_input_audio_param import ResponseInputAudioParam as ResponseInputAudioParam +from .response_input_image_param import ResponseInputImageParam as ResponseInputImageParam +from .response_local_environment import ResponseLocalEnvironment as ResponseLocalEnvironment +from .response_output_text_param import ResponseOutputTextParam as ResponseOutputTextParam +from .response_steer_event_param import ResponseSteerEventParam as ResponseSteerEventParam +from .response_steer_input_param import ResponseSteerInputParam as ResponseSteerInputParam +from .response_text_config_param import ResponseTextConfigParam as ResponseTextConfigParam +from .tool_choice_function_param import ToolChoiceFunctionParam as ToolChoiceFunctionParam +from .response_computer_tool_call import ResponseComputerToolCall as ResponseComputerToolCall +from .response_conversation_param import ResponseConversationParam as ResponseConversationParam +from .response_format_text_config import ResponseFormatTextConfig as ResponseFormatTextConfig +from .response_function_tool_call import ResponseFunctionToolCall as ResponseFunctionToolCall +from .response_input_file_content import ResponseInputFileContent as ResponseInputFileContent +from .response_input_message_item import ResponseInputMessageItem as ResponseInputMessageItem +from .response_input_text_content import ResponseInputTextContent as ResponseInputTextContent +from .response_refusal_done_event import ResponseRefusalDoneEvent as ResponseRefusalDoneEvent +from .response_steer_failed_event import ResponseSteerFailedEvent as ResponseSteerFailedEvent +from .response_container_reference import ResponseContainerReference as ResponseContainerReference +from .response_function_web_search import ResponseFunctionWebSearch as ResponseFunctionWebSearch +from .response_input_content_param import ResponseInputContentParam as ResponseInputContentParam +from .response_input_image_content import ResponseInputImageContent as ResponseInputImageContent +from .response_refusal_delta_event import ResponseRefusalDeltaEvent as ResponseRefusalDeltaEvent +from .response_steer_input_content import ResponseSteerInputContent as ResponseSteerInputContent +from .response_steer_pending_event import ResponseSteerPendingEvent as ResponseSteerPendingEvent +from .responses_client_event_param import ResponsesClientEventParam as ResponsesClientEventParam +from .response_output_message_param import ResponseOutputMessageParam as ResponseOutputMessageParam +from .response_output_refusal_param import ResponseOutputRefusalParam as ResponseOutputRefusalParam +from .response_reasoning_item_param import ResponseReasoningItemParam as ResponseReasoningItemParam +from .response_steer_accepted_event import ResponseSteerAcceptedEvent as ResponseSteerAcceptedEvent +from .response_steer_pending_reason import ResponseSteerPendingReason as ResponseSteerPendingReason +from .response_steer_required_input import ResponseSteerRequiredInput as ResponseSteerRequiredInput +from .tool_choice_apply_patch_param import ToolChoiceApplyPatchParam as ToolChoiceApplyPatchParam +from .web_search_preview_tool_param import WebSearchPreviewToolParam as WebSearchPreviewToolParam +from .response_apply_patch_tool_call import ResponseApplyPatchToolCall as ResponseApplyPatchToolCall +from .response_compaction_item_param import ResponseCompactionItemParam as ResponseCompactionItemParam +from .response_custom_tool_call_item import ResponseCustomToolCallItem as ResponseCustomToolCallItem +from .response_file_search_tool_call import ResponseFileSearchToolCall as ResponseFileSearchToolCall +from .response_mcp_call_failed_event import ResponseMcpCallFailedEvent as ResponseMcpCallFailedEvent +from .computer_use_preview_tool_param import ComputerUsePreviewToolParam as ComputerUsePreviewToolParam +from .response_custom_tool_call_param import ResponseCustomToolCallParam as ResponseCustomToolCallParam +from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent +from .response_content_part_done_event import ResponseContentPartDoneEvent as ResponseContentPartDoneEvent +from .response_custom_tool_call_output import ResponseCustomToolCallOutput as ResponseCustomToolCallOutput +from .response_function_tool_call_item import ResponseFunctionToolCallItem as ResponseFunctionToolCallItem +from .response_output_item_added_event import ResponseOutputItemAddedEvent as ResponseOutputItemAddedEvent +from .response_tool_search_output_item import ResponseToolSearchOutputItem as ResponseToolSearchOutputItem +from .container_network_policy_disabled import ContainerNetworkPolicyDisabled as ContainerNetworkPolicyDisabled +from .response_computer_tool_call_param import ResponseComputerToolCallParam as ResponseComputerToolCallParam +from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent +from .response_conversation_param_param import ResponseConversationParamParam as ResponseConversationParamParam +from .response_format_text_config_param import ResponseFormatTextConfigParam as ResponseFormatTextConfigParam +from .response_function_shell_tool_call import ResponseFunctionShellToolCall as ResponseFunctionShellToolCall +from .response_function_tool_call_param import ResponseFunctionToolCallParam as ResponseFunctionToolCallParam +from .response_input_file_content_param import ResponseInputFileContentParam as ResponseInputFileContentParam +from .response_input_text_content_param import ResponseInputTextContentParam as ResponseInputTextContentParam +from .response_mcp_call_completed_event import ResponseMcpCallCompletedEvent as ResponseMcpCallCompletedEvent +from .container_network_policy_allowlist import ContainerNetworkPolicyAllowlist as ContainerNetworkPolicyAllowlist +from .response_configuration_update_item import ResponseConfigurationUpdateItem as ResponseConfigurationUpdateItem +from .response_function_call_output_item import ResponseFunctionCallOutputItem as ResponseFunctionCallOutputItem +from .response_function_web_search_param import ResponseFunctionWebSearchParam as ResponseFunctionWebSearchParam +from .response_input_image_content_param import ResponseInputImageContentParam as ResponseInputImageContentParam +from .response_reasoning_text_done_event import ResponseReasoningTextDoneEvent as ResponseReasoningTextDoneEvent +from .response_steer_input_content_param import ResponseSteerInputContentParam as ResponseSteerInputContentParam +from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall as ResponseCodeInterpreterToolCall +from .response_input_message_content_list import ResponseInputMessageContentList as ResponseInputMessageContentList +from .response_mcp_call_in_progress_event import ResponseMcpCallInProgressEvent as ResponseMcpCallInProgressEvent +from .response_reasoning_text_delta_event import ResponseReasoningTextDeltaEvent as ResponseReasoningTextDeltaEvent +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent as ResponseAudioTranscriptDoneEvent +from .response_compaction_compacting_event import ResponseCompactionCompactingEvent as ResponseCompactionCompactingEvent +from .response_compaction_item_param_param import ResponseCompactionItemParamParam as ResponseCompactionItemParamParam +from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam as ResponseFileSearchToolCallParam +from .response_mcp_list_tools_failed_event import ResponseMcpListToolsFailedEvent as ResponseMcpListToolsFailedEvent +from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput as ResponseApplyPatchToolCallOutput +from .response_audio_transcript_delta_event import ( + ResponseAudioTranscriptDeltaEvent as ResponseAudioTranscriptDeltaEvent, +) +from .response_custom_tool_call_output_item import ResponseCustomToolCallOutputItem as ResponseCustomToolCallOutputItem +from .container_network_policy_domain_secret import ( + ContainerNetworkPolicyDomainSecret as ContainerNetworkPolicyDomainSecret, +) +from .response_custom_tool_call_output_param import ( + ResponseCustomToolCallOutputParam as ResponseCustomToolCallOutputParam, +) +from .response_mcp_call_arguments_done_event import ( + ResponseMcpCallArgumentsDoneEvent as ResponseMcpCallArgumentsDoneEvent, +) +from .response_shell_call_command_done_event import ( + ResponseShellCallCommandDoneEvent as ResponseShellCallCommandDoneEvent, +) +from .response_tool_search_output_item_param import ( + ResponseToolSearchOutputItemParam as ResponseToolSearchOutputItemParam, +) +from .container_network_policy_disabled_param import ( + ContainerNetworkPolicyDisabledParam as ContainerNetworkPolicyDisabledParam, +) +from .response_computer_tool_call_output_item import ( + ResponseComputerToolCallOutputItem as ResponseComputerToolCallOutputItem, +) +from .response_format_text_json_schema_config import ( + ResponseFormatTextJSONSchemaConfig as ResponseFormatTextJSONSchemaConfig, +) +from .response_function_call_output_item_list import ( + ResponseFunctionCallOutputItemList as ResponseFunctionCallOutputItemList, +) +from .response_function_tool_call_output_item import ( + ResponseFunctionToolCallOutputItem as ResponseFunctionToolCallOutputItem, +) +from .response_image_gen_call_completed_event import ( + ResponseImageGenCallCompletedEvent as ResponseImageGenCallCompletedEvent, +) +from .response_mcp_call_arguments_delta_event import ( + ResponseMcpCallArgumentsDeltaEvent as ResponseMcpCallArgumentsDeltaEvent, +) +from .response_mcp_list_tools_completed_event import ( + ResponseMcpListToolsCompletedEvent as ResponseMcpListToolsCompletedEvent, +) +from .response_shell_call_command_added_event import ( + ResponseShellCallCommandAddedEvent as ResponseShellCallCommandAddedEvent, +) +from .response_shell_call_command_delta_event import ( + ResponseShellCallCommandDeltaEvent as ResponseShellCallCommandDeltaEvent, +) +from .container_network_policy_allowlist_param import ( + ContainerNetworkPolicyAllowlistParam as ContainerNetworkPolicyAllowlistParam, +) +from .response_configuration_update_item_param import ( + ResponseConfigurationUpdateItemParam as ResponseConfigurationUpdateItemParam, +) +from .response_function_call_output_item_param import ( + ResponseFunctionCallOutputItemParam as ResponseFunctionCallOutputItemParam, +) +from .response_function_shell_tool_call_output import ( + ResponseFunctionShellToolCallOutput as ResponseFunctionShellToolCallOutput, +) +from .response_image_gen_call_generating_event import ( + ResponseImageGenCallGeneratingEvent as ResponseImageGenCallGeneratingEvent, +) +from .response_web_search_call_completed_event import ( + ResponseWebSearchCallCompletedEvent as ResponseWebSearchCallCompletedEvent, +) +from .response_web_search_call_searching_event import ( + ResponseWebSearchCallSearchingEvent as ResponseWebSearchCallSearchingEvent, +) +from .response_code_interpreter_tool_call_param import ( + ResponseCodeInterpreterToolCallParam as ResponseCodeInterpreterToolCallParam, +) +from .response_file_search_call_completed_event import ( + ResponseFileSearchCallCompletedEvent as ResponseFileSearchCallCompletedEvent, +) +from .response_file_search_call_searching_event import ( + ResponseFileSearchCallSearchingEvent as ResponseFileSearchCallSearchingEvent, +) +from .response_image_gen_call_in_progress_event import ( + ResponseImageGenCallInProgressEvent as ResponseImageGenCallInProgressEvent, +) +from .response_input_message_content_list_param import ( + ResponseInputMessageContentListParam as ResponseInputMessageContentListParam, +) +from .response_mcp_list_tools_in_progress_event import ( + ResponseMcpListToolsInProgressEvent as ResponseMcpListToolsInProgressEvent, +) +from .response_custom_tool_call_input_done_event import ( + ResponseCustomToolCallInputDoneEvent as ResponseCustomToolCallInputDoneEvent, +) +from .response_reasoning_summary_part_done_event import ( + ResponseReasoningSummaryPartDoneEvent as ResponseReasoningSummaryPartDoneEvent, +) +from .response_reasoning_summary_text_done_event import ( + ResponseReasoningSummaryTextDoneEvent as ResponseReasoningSummaryTextDoneEvent, +) +from .response_web_search_call_in_progress_event import ( + ResponseWebSearchCallInProgressEvent as ResponseWebSearchCallInProgressEvent, +) +from .response_custom_tool_call_input_delta_event import ( + ResponseCustomToolCallInputDeltaEvent as ResponseCustomToolCallInputDeltaEvent, +) +from .response_file_search_call_in_progress_event import ( + ResponseFileSearchCallInProgressEvent as ResponseFileSearchCallInProgressEvent, +) +from .response_function_call_arguments_done_event import ( + ResponseFunctionCallArgumentsDoneEvent as ResponseFunctionCallArgumentsDoneEvent, +) +from .response_function_shell_call_output_content import ( + ResponseFunctionShellCallOutputContent as ResponseFunctionShellCallOutputContent, +) +from .response_image_gen_call_partial_image_event import ( + ResponseImageGenCallPartialImageEvent as ResponseImageGenCallPartialImageEvent, +) +from .response_output_text_annotation_added_event import ( + ResponseOutputTextAnnotationAddedEvent as ResponseOutputTextAnnotationAddedEvent, +) +from .response_reasoning_summary_part_added_event import ( + ResponseReasoningSummaryPartAddedEvent as ResponseReasoningSummaryPartAddedEvent, +) +from .response_reasoning_summary_text_delta_event import ( + ResponseReasoningSummaryTextDeltaEvent as ResponseReasoningSummaryTextDeltaEvent, +) +from .container_network_policy_domain_secret_param import ( + ContainerNetworkPolicyDomainSecretParam as ContainerNetworkPolicyDomainSecretParam, +) +from .response_function_call_arguments_delta_event import ( + ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, +) +from .response_tool_search_output_item_param_param import ( + ResponseToolSearchOutputItemParamParam as ResponseToolSearchOutputItemParamParam, +) +from .response_computer_tool_call_output_screenshot import ( + ResponseComputerToolCallOutputScreenshot as ResponseComputerToolCallOutputScreenshot, +) +from .response_format_text_json_schema_config_param import ( + ResponseFormatTextJSONSchemaConfigParam as ResponseFormatTextJSONSchemaConfigParam, +) +from .response_function_call_output_item_list_param import ( + ResponseFunctionCallOutputItemListParam as ResponseFunctionCallOutputItemListParam, +) +from .response_shell_call_output_content_done_event import ( + ResponseShellCallOutputContentDoneEvent as ResponseShellCallOutputContentDoneEvent, +) +from .response_code_interpreter_call_code_done_event import ( + ResponseCodeInterpreterCallCodeDoneEvent as ResponseCodeInterpreterCallCodeDoneEvent, +) +from .response_code_interpreter_call_completed_event import ( + ResponseCodeInterpreterCallCompletedEvent as ResponseCodeInterpreterCallCompletedEvent, +) +from .response_configuration_update_item_param_param import ( + ResponseConfigurationUpdateItemParamParam as ResponseConfigurationUpdateItemParamParam, +) +from .response_shell_call_output_content_delta_event import ( + ResponseShellCallOutputContentDeltaEvent as ResponseShellCallOutputContentDeltaEvent, +) +from .response_code_interpreter_call_code_delta_event import ( + ResponseCodeInterpreterCallCodeDeltaEvent as ResponseCodeInterpreterCallCodeDeltaEvent, +) +from .response_code_interpreter_call_in_progress_event import ( + ResponseCodeInterpreterCallInProgressEvent as ResponseCodeInterpreterCallInProgressEvent, +) +from .response_code_interpreter_call_interpreting_event import ( + ResponseCodeInterpreterCallInterpretingEvent as ResponseCodeInterpreterCallInterpretingEvent, +) +from .response_function_shell_call_output_content_param import ( + ResponseFunctionShellCallOutputContentParam as ResponseFunctionShellCallOutputContentParam, +) +from .response_computer_tool_call_output_screenshot_param import ( + ResponseComputerToolCallOutputScreenshotParam as ResponseComputerToolCallOutputScreenshotParam, +) diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b74e78c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/apply_patch_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/apply_patch_tool.cpython-312.pyc new file mode 100644 index 0000000..cb1bfb6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/apply_patch_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/apply_patch_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/apply_patch_tool_param.cpython-312.pyc new file mode 100644 index 0000000..dcbe8d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/apply_patch_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/compacted_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/compacted_response.cpython-312.pyc new file mode 100644 index 0000000..19bdffb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/compacted_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_action.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_action.cpython-312.pyc new file mode 100644 index 0000000..2ac42f0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_action.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_action_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_action_list.cpython-312.pyc new file mode 100644 index 0000000..db3a14d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_action_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_action_list_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_action_list_param.cpython-312.pyc new file mode 100644 index 0000000..a3dcd77 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_action_list_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_action_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_action_param.cpython-312.pyc new file mode 100644 index 0000000..fa1b432 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_action_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_tool.cpython-312.pyc new file mode 100644 index 0000000..96bdbc8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_tool_param.cpython-312.pyc new file mode 100644 index 0000000..a94deb1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_use_preview_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_use_preview_tool.cpython-312.pyc new file mode 100644 index 0000000..f0af16a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_use_preview_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_use_preview_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_use_preview_tool_param.cpython-312.pyc new file mode 100644 index 0000000..f75ec13 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/computer_use_preview_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_auto.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_auto.cpython-312.pyc new file mode 100644 index 0000000..737680c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_auto.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_auto_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_auto_param.cpython-312.pyc new file mode 100644 index 0000000..3ddee3e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_auto_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_allowlist.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_allowlist.cpython-312.pyc new file mode 100644 index 0000000..f18ed1a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_allowlist.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_allowlist_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_allowlist_param.cpython-312.pyc new file mode 100644 index 0000000..3b674b3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_allowlist_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_disabled.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_disabled.cpython-312.pyc new file mode 100644 index 0000000..f7469e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_disabled.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_disabled_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_disabled_param.cpython-312.pyc new file mode 100644 index 0000000..8ba7f0b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_disabled_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_domain_secret.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_domain_secret.cpython-312.pyc new file mode 100644 index 0000000..d9cb635 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_domain_secret.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_domain_secret_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_domain_secret_param.cpython-312.pyc new file mode 100644 index 0000000..182b270 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_network_policy_domain_secret_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_reference.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_reference.cpython-312.pyc new file mode 100644 index 0000000..1b90f79 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_reference.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_reference_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_reference_param.cpython-312.pyc new file mode 100644 index 0000000..dd16ce0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/container_reference_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/custom_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/custom_tool.cpython-312.pyc new file mode 100644 index 0000000..619f68f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/custom_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/custom_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/custom_tool_param.cpython-312.pyc new file mode 100644 index 0000000..9194a41 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/custom_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/easy_input_message.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/easy_input_message.cpython-312.pyc new file mode 100644 index 0000000..f1b3059 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/easy_input_message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/easy_input_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/easy_input_message_param.cpython-312.pyc new file mode 100644 index 0000000..eb89ac3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/easy_input_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/file_search_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/file_search_tool.cpython-312.pyc new file mode 100644 index 0000000..0213f33 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/file_search_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/file_search_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/file_search_tool_param.cpython-312.pyc new file mode 100644 index 0000000..9e79d40 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/file_search_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/function_shell_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/function_shell_tool.cpython-312.pyc new file mode 100644 index 0000000..b858490 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/function_shell_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/function_shell_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/function_shell_tool_param.cpython-312.pyc new file mode 100644 index 0000000..04d6dca Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/function_shell_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/function_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/function_tool.cpython-312.pyc new file mode 100644 index 0000000..e8ba5b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/function_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/function_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/function_tool_param.cpython-312.pyc new file mode 100644 index 0000000..8db59c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/function_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/image_detail.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/image_detail.cpython-312.pyc new file mode 100644 index 0000000..ac69f42 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/image_detail.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/inline_skill.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/inline_skill.cpython-312.pyc new file mode 100644 index 0000000..96503a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/inline_skill.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/inline_skill_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/inline_skill_param.cpython-312.pyc new file mode 100644 index 0000000..e2b0511 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/inline_skill_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/inline_skill_source.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/inline_skill_source.cpython-312.pyc new file mode 100644 index 0000000..c6071dd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/inline_skill_source.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/inline_skill_source_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/inline_skill_source_param.cpython-312.pyc new file mode 100644 index 0000000..a1d5798 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/inline_skill_source_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/input_item_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/input_item_list_params.cpython-312.pyc new file mode 100644 index 0000000..bd677dc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/input_item_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/input_token_count_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/input_token_count_params.cpython-312.pyc new file mode 100644 index 0000000..4487fa1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/input_token_count_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/input_token_count_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/input_token_count_response.cpython-312.pyc new file mode 100644 index 0000000..a78c6af Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/input_token_count_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/local_environment.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/local_environment.cpython-312.pyc new file mode 100644 index 0000000..0c39b5e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/local_environment.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/local_environment_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/local_environment_param.cpython-312.pyc new file mode 100644 index 0000000..38bd76f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/local_environment_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/local_skill.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/local_skill.cpython-312.pyc new file mode 100644 index 0000000..1147e8b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/local_skill.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/local_skill_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/local_skill_param.cpython-312.pyc new file mode 100644 index 0000000..2ec377a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/local_skill_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/mcp_tool_call_error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/mcp_tool_call_error.cpython-312.pyc new file mode 100644 index 0000000..4f64850 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/mcp_tool_call_error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/mcp_tool_call_error_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/mcp_tool_call_error_param.cpython-312.pyc new file mode 100644 index 0000000..f309ba0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/mcp_tool_call_error_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/namespace_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/namespace_tool.cpython-312.pyc new file mode 100644 index 0000000..4f185b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/namespace_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/namespace_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/namespace_tool_param.cpython-312.pyc new file mode 100644 index 0000000..b4e31ac Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/namespace_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/parsed_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/parsed_response.cpython-312.pyc new file mode 100644 index 0000000..65a400f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/parsed_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response.cpython-312.pyc new file mode 100644 index 0000000..b51a680 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_apply_patch_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_apply_patch_tool_call.cpython-312.pyc new file mode 100644 index 0000000..945ab59 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_apply_patch_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_apply_patch_tool_call_output.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_apply_patch_tool_call_output.cpython-312.pyc new file mode 100644 index 0000000..8fa5b0f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_apply_patch_tool_call_output.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_audio_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_audio_delta_event.cpython-312.pyc new file mode 100644 index 0000000..0472d29 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_audio_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_audio_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_audio_done_event.cpython-312.pyc new file mode 100644 index 0000000..56ef253 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_audio_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_audio_transcript_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_audio_transcript_delta_event.cpython-312.pyc new file mode 100644 index 0000000..ee6f22b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_audio_transcript_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_audio_transcript_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_audio_transcript_done_event.cpython-312.pyc new file mode 100644 index 0000000..b988c83 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_audio_transcript_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_code_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_code_delta_event.cpython-312.pyc new file mode 100644 index 0000000..898cacb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_code_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_code_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_code_done_event.cpython-312.pyc new file mode 100644 index 0000000..ed215a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_code_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_completed_event.cpython-312.pyc new file mode 100644 index 0000000..76e8a73 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..79ddfde Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_interpreting_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_interpreting_event.cpython-312.pyc new file mode 100644 index 0000000..d3d8561 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_call_interpreting_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_tool_call.cpython-312.pyc new file mode 100644 index 0000000..5f8364b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..1a622ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_code_interpreter_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compact_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compact_params.cpython-312.pyc new file mode 100644 index 0000000..2027418 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compact_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compaction_compacting_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compaction_compacting_event.cpython-312.pyc new file mode 100644 index 0000000..dbbe20b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compaction_compacting_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compaction_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compaction_item.cpython-312.pyc new file mode 100644 index 0000000..85dd4db Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compaction_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compaction_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compaction_item_param.cpython-312.pyc new file mode 100644 index 0000000..ff9d46d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compaction_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compaction_item_param_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compaction_item_param_param.cpython-312.pyc new file mode 100644 index 0000000..930b266 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_compaction_item_param_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_completed_event.cpython-312.pyc new file mode 100644 index 0000000..44a23b8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call.cpython-312.pyc new file mode 100644 index 0000000..ec951d3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call_output_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call_output_item.cpython-312.pyc new file mode 100644 index 0000000..8f4edc8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call_output_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call_output_screenshot.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call_output_screenshot.cpython-312.pyc new file mode 100644 index 0000000..8ac0b52 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call_output_screenshot.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call_output_screenshot_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call_output_screenshot_param.cpython-312.pyc new file mode 100644 index 0000000..5921b9f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call_output_screenshot_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..701d6e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_computer_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_configuration_update_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_configuration_update_item.cpython-312.pyc new file mode 100644 index 0000000..49275b9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_configuration_update_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_configuration_update_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_configuration_update_item_param.cpython-312.pyc new file mode 100644 index 0000000..2f9f1a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_configuration_update_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_configuration_update_item_param_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_configuration_update_item_param_param.cpython-312.pyc new file mode 100644 index 0000000..0c3ce59 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_configuration_update_item_param_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_container_reference.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_container_reference.cpython-312.pyc new file mode 100644 index 0000000..f3df6df Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_container_reference.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_content_part_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_content_part_added_event.cpython-312.pyc new file mode 100644 index 0000000..cff0f21 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_content_part_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_content_part_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_content_part_done_event.cpython-312.pyc new file mode 100644 index 0000000..9943657 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_content_part_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_conversation_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_conversation_param.cpython-312.pyc new file mode 100644 index 0000000..fda5ecc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_conversation_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_conversation_param_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_conversation_param_param.cpython-312.pyc new file mode 100644 index 0000000..c9873fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_conversation_param_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_create_params.cpython-312.pyc new file mode 100644 index 0000000..4d05fda Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_created_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_created_event.cpython-312.pyc new file mode 100644 index 0000000..03c3796 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_created_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call.cpython-312.pyc new file mode 100644 index 0000000..5767e54 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_input_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_input_delta_event.cpython-312.pyc new file mode 100644 index 0000000..82e0f64 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_input_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_input_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_input_done_event.cpython-312.pyc new file mode 100644 index 0000000..b1e49d5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_input_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_item.cpython-312.pyc new file mode 100644 index 0000000..34f5dbe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_output.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_output.cpython-312.pyc new file mode 100644 index 0000000..d158e1c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_output.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_output_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_output_item.cpython-312.pyc new file mode 100644 index 0000000..7246aed Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_output_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_output_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_output_param.cpython-312.pyc new file mode 100644 index 0000000..537666b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_output_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..de628af Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_custom_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_error.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_error.cpython-312.pyc new file mode 100644 index 0000000..4d33ddc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_error.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_error_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_error_event.cpython-312.pyc new file mode 100644 index 0000000..1dfd3c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_error_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_failed_event.cpython-312.pyc new file mode 100644 index 0000000..16d67b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_call_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_call_completed_event.cpython-312.pyc new file mode 100644 index 0000000..7cf4226 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_call_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_call_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_call_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..7b44b90 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_call_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_call_searching_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_call_searching_event.cpython-312.pyc new file mode 100644 index 0000000..4402ec5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_call_searching_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_tool_call.cpython-312.pyc new file mode 100644 index 0000000..c7f8769 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..ec64d74 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_file_search_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_format_text_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_format_text_config.cpython-312.pyc new file mode 100644 index 0000000..b59f64a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_format_text_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_format_text_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_format_text_config_param.cpython-312.pyc new file mode 100644 index 0000000..a0db3cc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_format_text_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_format_text_json_schema_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_format_text_json_schema_config.cpython-312.pyc new file mode 100644 index 0000000..f3b5061 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_format_text_json_schema_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_format_text_json_schema_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_format_text_json_schema_config_param.cpython-312.pyc new file mode 100644 index 0000000..dbe6ae7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_format_text_json_schema_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_arguments_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_arguments_delta_event.cpython-312.pyc new file mode 100644 index 0000000..7b0cf70 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_arguments_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_arguments_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_arguments_done_event.cpython-312.pyc new file mode 100644 index 0000000..50f9431 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_arguments_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_output_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_output_item.cpython-312.pyc new file mode 100644 index 0000000..7dbe668 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_output_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_output_item_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_output_item_list.cpython-312.pyc new file mode 100644 index 0000000..a58a7eb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_output_item_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_output_item_list_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_output_item_list_param.cpython-312.pyc new file mode 100644 index 0000000..e6a033d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_output_item_list_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_output_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_output_item_param.cpython-312.pyc new file mode 100644 index 0000000..3fa0e11 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_call_output_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_shell_call_output_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_shell_call_output_content.cpython-312.pyc new file mode 100644 index 0000000..f46ecb6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_shell_call_output_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_shell_call_output_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_shell_call_output_content_param.cpython-312.pyc new file mode 100644 index 0000000..933733a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_shell_call_output_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_shell_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_shell_tool_call.cpython-312.pyc new file mode 100644 index 0000000..a5a3b17 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_shell_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_shell_tool_call_output.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_shell_tool_call_output.cpython-312.pyc new file mode 100644 index 0000000..1007627 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_shell_tool_call_output.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_tool_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_tool_call.cpython-312.pyc new file mode 100644 index 0000000..3796660 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_tool_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_tool_call_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_tool_call_item.cpython-312.pyc new file mode 100644 index 0000000..79b888a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_tool_call_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_tool_call_output_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_tool_call_output_item.cpython-312.pyc new file mode 100644 index 0000000..cee527d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_tool_call_output_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_tool_call_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_tool_call_param.cpython-312.pyc new file mode 100644 index 0000000..0a2f04a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_tool_call_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_web_search.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_web_search.cpython-312.pyc new file mode 100644 index 0000000..640ee49 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_web_search.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_web_search_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_web_search_param.cpython-312.pyc new file mode 100644 index 0000000..3185172 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_function_web_search_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_image_gen_call_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_image_gen_call_completed_event.cpython-312.pyc new file mode 100644 index 0000000..9a219fd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_image_gen_call_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_image_gen_call_generating_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_image_gen_call_generating_event.cpython-312.pyc new file mode 100644 index 0000000..e41fe65 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_image_gen_call_generating_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_image_gen_call_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_image_gen_call_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..a69533d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_image_gen_call_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_image_gen_call_partial_image_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_image_gen_call_partial_image_event.cpython-312.pyc new file mode 100644 index 0000000..3900189 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_image_gen_call_partial_image_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..20329f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_includable.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_includable.cpython-312.pyc new file mode 100644 index 0000000..a6c71cb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_includable.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_incomplete_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_incomplete_event.cpython-312.pyc new file mode 100644 index 0000000..7bb8a35 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_incomplete_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input.cpython-312.pyc new file mode 100644 index 0000000..5617b2e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_audio.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_audio.cpython-312.pyc new file mode 100644 index 0000000..ba91737 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_audio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_audio_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_audio_param.cpython-312.pyc new file mode 100644 index 0000000..64df4ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_audio_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_content.cpython-312.pyc new file mode 100644 index 0000000..8a70b87 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_content_param.cpython-312.pyc new file mode 100644 index 0000000..5118634 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_file.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_file.cpython-312.pyc new file mode 100644 index 0000000..3022415 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_file.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_file_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_file_content.cpython-312.pyc new file mode 100644 index 0000000..e49ff9d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_file_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_file_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_file_content_param.cpython-312.pyc new file mode 100644 index 0000000..a4cfbfa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_file_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_file_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_file_param.cpython-312.pyc new file mode 100644 index 0000000..c382487 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_file_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_image.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_image.cpython-312.pyc new file mode 100644 index 0000000..16347d3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_image.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_image_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_image_content.cpython-312.pyc new file mode 100644 index 0000000..03447e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_image_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_image_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_image_content_param.cpython-312.pyc new file mode 100644 index 0000000..a5bad28 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_image_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_image_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_image_param.cpython-312.pyc new file mode 100644 index 0000000..f3e072e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_image_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_item.cpython-312.pyc new file mode 100644 index 0000000..c68b16a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_item_param.cpython-312.pyc new file mode 100644 index 0000000..bdb03ab Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_message_content_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_message_content_list.cpython-312.pyc new file mode 100644 index 0000000..959702e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_message_content_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_message_content_list_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_message_content_list_param.cpython-312.pyc new file mode 100644 index 0000000..bb86660 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_message_content_list_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_message_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_message_item.cpython-312.pyc new file mode 100644 index 0000000..1035f72 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_message_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_param.cpython-312.pyc new file mode 100644 index 0000000..04fa2aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_text.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_text.cpython-312.pyc new file mode 100644 index 0000000..abeee9e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_text_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_text_content.cpython-312.pyc new file mode 100644 index 0000000..f51b7ba Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_text_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_text_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_text_content_param.cpython-312.pyc new file mode 100644 index 0000000..ed7ad13 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_text_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_text_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_text_param.cpython-312.pyc new file mode 100644 index 0000000..e3c9294 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_input_text_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_item.cpython-312.pyc new file mode 100644 index 0000000..e2a6dbc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_item_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_item_list.cpython-312.pyc new file mode 100644 index 0000000..baaaeb6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_item_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_local_environment.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_local_environment.cpython-312.pyc new file mode 100644 index 0000000..80164fd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_local_environment.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_arguments_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_arguments_delta_event.cpython-312.pyc new file mode 100644 index 0000000..275ece6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_arguments_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_arguments_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_arguments_done_event.cpython-312.pyc new file mode 100644 index 0000000..9e30730 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_arguments_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_completed_event.cpython-312.pyc new file mode 100644 index 0000000..28ccd05 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_failed_event.cpython-312.pyc new file mode 100644 index 0000000..f3103db Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..24e230a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_call_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_list_tools_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_list_tools_completed_event.cpython-312.pyc new file mode 100644 index 0000000..de984e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_list_tools_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_list_tools_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_list_tools_failed_event.cpython-312.pyc new file mode 100644 index 0000000..399dbbe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_list_tools_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_list_tools_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_list_tools_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..0a42639 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_mcp_list_tools_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_item.cpython-312.pyc new file mode 100644 index 0000000..d21ba0c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_item_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_item_added_event.cpython-312.pyc new file mode 100644 index 0000000..966a2a9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_item_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_item_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_item_done_event.cpython-312.pyc new file mode 100644 index 0000000..5141a89 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_item_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_message.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_message.cpython-312.pyc new file mode 100644 index 0000000..10400a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_message.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_message_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_message_param.cpython-312.pyc new file mode 100644 index 0000000..3fa145a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_message_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_refusal.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_refusal.cpython-312.pyc new file mode 100644 index 0000000..8d7a519 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_refusal.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_refusal_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_refusal_param.cpython-312.pyc new file mode 100644 index 0000000..9331001 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_refusal_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_text.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_text.cpython-312.pyc new file mode 100644 index 0000000..157ab18 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_text_annotation_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_text_annotation_added_event.cpython-312.pyc new file mode 100644 index 0000000..426d244 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_text_annotation_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_text_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_text_param.cpython-312.pyc new file mode 100644 index 0000000..84848bf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_output_text_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_prompt.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_prompt.cpython-312.pyc new file mode 100644 index 0000000..5795a00 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_prompt.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_prompt_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_prompt_param.cpython-312.pyc new file mode 100644 index 0000000..53b1f72 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_prompt_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_queued_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_queued_event.cpython-312.pyc new file mode 100644 index 0000000..db83d4d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_queued_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_item.cpython-312.pyc new file mode 100644 index 0000000..8c8cdfe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_item_param.cpython-312.pyc new file mode 100644 index 0000000..4203dd0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_summary_part_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_summary_part_added_event.cpython-312.pyc new file mode 100644 index 0000000..63ce76a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_summary_part_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_summary_part_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_summary_part_done_event.cpython-312.pyc new file mode 100644 index 0000000..f18b74d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_summary_part_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_summary_text_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_summary_text_delta_event.cpython-312.pyc new file mode 100644 index 0000000..ee43461 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_summary_text_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_summary_text_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_summary_text_done_event.cpython-312.pyc new file mode 100644 index 0000000..a9c7379 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_summary_text_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_text_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_text_delta_event.cpython-312.pyc new file mode 100644 index 0000000..33ece0e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_text_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_text_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_text_done_event.cpython-312.pyc new file mode 100644 index 0000000..d322779 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_reasoning_text_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_refusal_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_refusal_delta_event.cpython-312.pyc new file mode 100644 index 0000000..9ba99f0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_refusal_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_refusal_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_refusal_done_event.cpython-312.pyc new file mode 100644 index 0000000..1ead919 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_refusal_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_retrieve_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_retrieve_params.cpython-312.pyc new file mode 100644 index 0000000..667cc80 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_retrieve_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_command_added_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_command_added_event.cpython-312.pyc new file mode 100644 index 0000000..bd8dc38 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_command_added_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_command_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_command_delta_event.cpython-312.pyc new file mode 100644 index 0000000..ac32256 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_command_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_command_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_command_done_event.cpython-312.pyc new file mode 100644 index 0000000..f9f7915 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_command_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_output_content_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_output_content_delta_event.cpython-312.pyc new file mode 100644 index 0000000..dc03364 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_output_content_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_output_content_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_output_content_done_event.cpython-312.pyc new file mode 100644 index 0000000..5379282 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_shell_call_output_content_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_status.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_status.cpython-312.pyc new file mode 100644 index 0000000..c3c3674 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_status.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_accepted_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_accepted_event.cpython-312.pyc new file mode 100644 index 0000000..23a28e8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_accepted_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_error_code.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_error_code.cpython-312.pyc new file mode 100644 index 0000000..447b8a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_error_code.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_event.cpython-312.pyc new file mode 100644 index 0000000..c3a3876 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_event_param.cpython-312.pyc new file mode 100644 index 0000000..a97a61c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_failed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_failed_event.cpython-312.pyc new file mode 100644 index 0000000..4ad7d78 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_failed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_input.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_input.cpython-312.pyc new file mode 100644 index 0000000..1bca23c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_input.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_input_content.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_input_content.cpython-312.pyc new file mode 100644 index 0000000..15ddd47 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_input_content.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_input_content_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_input_content_param.cpython-312.pyc new file mode 100644 index 0000000..707fcda Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_input_content_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_input_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_input_param.cpython-312.pyc new file mode 100644 index 0000000..f1e130c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_input_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_pending_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_pending_event.cpython-312.pyc new file mode 100644 index 0000000..80b6c02 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_pending_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_pending_reason.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_pending_reason.cpython-312.pyc new file mode 100644 index 0000000..3e8de35 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_pending_reason.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_required_input.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_required_input.cpython-312.pyc new file mode 100644 index 0000000..21aee59 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_steer_required_input.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_stream_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_stream_event.cpython-312.pyc new file mode 100644 index 0000000..5d379f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_stream_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_text_config.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_text_config.cpython-312.pyc new file mode 100644 index 0000000..3b60c6c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_text_config.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_text_config_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_text_config_param.cpython-312.pyc new file mode 100644 index 0000000..33ac0de Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_text_config_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_text_delta_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_text_delta_event.cpython-312.pyc new file mode 100644 index 0000000..353bfc4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_text_delta_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_text_done_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_text_done_event.cpython-312.pyc new file mode 100644 index 0000000..c888e28 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_text_done_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_tool_search_call.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_tool_search_call.cpython-312.pyc new file mode 100644 index 0000000..583bd42 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_tool_search_call.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_tool_search_output_item.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_tool_search_output_item.cpython-312.pyc new file mode 100644 index 0000000..274af15 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_tool_search_output_item.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_tool_search_output_item_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_tool_search_output_item_param.cpython-312.pyc new file mode 100644 index 0000000..f6cbe17 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_tool_search_output_item_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_tool_search_output_item_param_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_tool_search_output_item_param_param.cpython-312.pyc new file mode 100644 index 0000000..a1b42c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_tool_search_output_item_param_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_usage.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_usage.cpython-312.pyc new file mode 100644 index 0000000..40f5afd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_usage.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_web_search_call_completed_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_web_search_call_completed_event.cpython-312.pyc new file mode 100644 index 0000000..5fd6e24 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_web_search_call_completed_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_web_search_call_in_progress_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_web_search_call_in_progress_event.cpython-312.pyc new file mode 100644 index 0000000..3baf34a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_web_search_call_in_progress_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_web_search_call_searching_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_web_search_call_searching_event.cpython-312.pyc new file mode 100644 index 0000000..c2db290 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/response_web_search_call_searching_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/responses_client_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/responses_client_event.cpython-312.pyc new file mode 100644 index 0000000..07a1c42 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/responses_client_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/responses_client_event_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/responses_client_event_param.cpython-312.pyc new file mode 100644 index 0000000..8a15aaf Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/responses_client_event_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/responses_server_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/responses_server_event.cpython-312.pyc new file mode 100644 index 0000000..d2ca28a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/responses_server_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/service_tier.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/service_tier.cpython-312.pyc new file mode 100644 index 0000000..8930dbc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/service_tier.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/skill_reference.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/skill_reference.cpython-312.pyc new file mode 100644 index 0000000..76bdbd8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/skill_reference.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/skill_reference_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/skill_reference_param.cpython-312.pyc new file mode 100644 index 0000000..34d7ec6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/skill_reference_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool.cpython-312.pyc new file mode 100644 index 0000000..0542fe4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_allowed.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_allowed.cpython-312.pyc new file mode 100644 index 0000000..a571f0d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_allowed.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_allowed_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_allowed_param.cpython-312.pyc new file mode 100644 index 0000000..406d35c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_allowed_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_apply_patch.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_apply_patch.cpython-312.pyc new file mode 100644 index 0000000..8e31a05 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_apply_patch.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_apply_patch_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_apply_patch_param.cpython-312.pyc new file mode 100644 index 0000000..aa89c45 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_apply_patch_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_custom.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_custom.cpython-312.pyc new file mode 100644 index 0000000..138552c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_custom.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_custom_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_custom_param.cpython-312.pyc new file mode 100644 index 0000000..a2d3cfe Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_custom_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_function.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_function.cpython-312.pyc new file mode 100644 index 0000000..56d1b7b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_function.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_function_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_function_param.cpython-312.pyc new file mode 100644 index 0000000..fb2677b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_function_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_mcp.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_mcp.cpython-312.pyc new file mode 100644 index 0000000..bb70dfd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_mcp.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_mcp_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_mcp_param.cpython-312.pyc new file mode 100644 index 0000000..f11e13f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_mcp_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_options.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_options.cpython-312.pyc new file mode 100644 index 0000000..784c8e7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_options.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_shell.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_shell.cpython-312.pyc new file mode 100644 index 0000000..ec129d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_shell.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_shell_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_shell_param.cpython-312.pyc new file mode 100644 index 0000000..5e37995 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_shell_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_types.cpython-312.pyc new file mode 100644 index 0000000..a87745e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_types_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_types_param.cpython-312.pyc new file mode 100644 index 0000000..5faf0db Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_choice_types_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_param.cpython-312.pyc new file mode 100644 index 0000000..f41f86c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_search_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_search_tool.cpython-312.pyc new file mode 100644 index 0000000..285fbe4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_search_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_search_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_search_tool_param.cpython-312.pyc new file mode 100644 index 0000000..05d2d5d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/tool_search_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/web_search_preview_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/web_search_preview_tool.cpython-312.pyc new file mode 100644 index 0000000..2c82d98 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/web_search_preview_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/web_search_preview_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/web_search_preview_tool_param.cpython-312.pyc new file mode 100644 index 0000000..4f96e0d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/web_search_preview_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/web_search_tool.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/web_search_tool.cpython-312.pyc new file mode 100644 index 0000000..210552b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/web_search_tool.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/web_search_tool_param.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/web_search_tool_param.cpython-312.pyc new file mode 100644 index 0000000..c846299 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/responses/__pycache__/web_search_tool_param.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/apply_patch_tool.py b/venv/lib/python3.12/site-packages/openai/types/responses/apply_patch_tool.py new file mode 100644 index 0000000..d85e3a5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/apply_patch_tool.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ApplyPatchTool"] + + +class ApplyPatchTool(BaseModel): + """Allows the assistant to create, delete, or update files using unified diffs.""" + + type: Literal["apply_patch"] + """The type of the tool. Always `apply_patch`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/apply_patch_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/apply_patch_tool_param.py new file mode 100644 index 0000000..f2058e5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/apply_patch_tool_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ApplyPatchToolParam"] + + +class ApplyPatchToolParam(TypedDict, total=False): + """Allows the assistant to create, delete, or update files using unified diffs.""" + + type: Required[Literal["apply_patch"]] + """The type of the tool. Always `apply_patch`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/compacted_response.py b/venv/lib/python3.12/site-packages/openai/types/responses/compacted_response.py new file mode 100644 index 0000000..1da3d2e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/compacted_response.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_usage import ResponseUsage +from .response_output_item import ResponseOutputItem + +__all__ = ["CompactedResponse"] + + +class CompactedResponse(BaseModel): + id: str + """The unique identifier for the compacted response.""" + + created_at: int + """Unix timestamp (in seconds) when the compacted conversation was created.""" + + object: Literal["response.compaction"] + """The object type. Always `response.compaction`.""" + + output: List[ResponseOutputItem] + """The compacted list of output items. + + This is a list of all user messages, followed by a single compaction item. + """ + + usage: ResponseUsage + """ + Token accounting for the compaction pass, including cached, reasoning, and total + tokens. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/computer_action.py b/venv/lib/python3.12/site-packages/openai/types/responses/computer_action.py new file mode 100644 index 0000000..3aa9a82 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/computer_action.py @@ -0,0 +1,196 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "ComputerAction", + "Click", + "DoubleClick", + "Drag", + "DragPath", + "Keypress", + "Move", + "Screenshot", + "Scroll", + "Type", + "Wait", +] + + +class Click(BaseModel): + """A click action.""" + + button: Literal["left", "right", "wheel", "back", "forward"] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Literal["click"] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: int + """The x-coordinate where the click occurred.""" + + y: int + """The y-coordinate where the click occurred.""" + + keys: Optional[List[str]] = None + """The keys being held while clicking.""" + + +class DoubleClick(BaseModel): + """A double click action.""" + + keys: Optional[List[str]] = None + """The keys being held while double-clicking.""" + + type: Literal["double_click"] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: int + """The x-coordinate where the double click occurred.""" + + y: int + """The y-coordinate where the double click occurred.""" + + +class DragPath(BaseModel): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: int + """The x-coordinate.""" + + y: int + """The y-coordinate.""" + + +class Drag(BaseModel): + """A drag action.""" + + path: List[DragPath] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Literal["drag"] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + keys: Optional[List[str]] = None + """The keys being held while dragging the mouse.""" + + +class Keypress(BaseModel): + """A collection of keypresses the model would like to perform.""" + + keys: List[str] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Literal["keypress"] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class Move(BaseModel): + """A mouse move action.""" + + type: Literal["move"] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: int + """The x-coordinate to move to.""" + + y: int + """The y-coordinate to move to.""" + + keys: Optional[List[str]] = None + """The keys being held while moving the mouse.""" + + +class Screenshot(BaseModel): + """A screenshot action.""" + + type: Literal["screenshot"] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class Scroll(BaseModel): + """A scroll action.""" + + scroll_x: int + """The horizontal scroll distance.""" + + scroll_y: int + """The vertical scroll distance.""" + + type: Literal["scroll"] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: int + """The x-coordinate where the scroll occurred.""" + + y: int + """The y-coordinate where the scroll occurred.""" + + keys: Optional[List[str]] = None + """The keys being held while scrolling.""" + + +class Type(BaseModel): + """An action to type in text.""" + + text: str + """The text to type.""" + + type: Literal["type"] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class Wait(BaseModel): + """A wait action.""" + + type: Literal["wait"] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +ComputerAction: TypeAlias = Annotated[ + Union[Click, DoubleClick, Drag, Keypress, Move, Screenshot, Scroll, Type, Wait], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/computer_action_list.py b/venv/lib/python3.12/site-packages/openai/types/responses/computer_action_list.py new file mode 100644 index 0000000..0bef415 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/computer_action_list.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .computer_action import ComputerAction + +__all__ = ["ComputerActionList"] + +ComputerActionList: TypeAlias = List[ComputerAction] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/computer_action_list_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/computer_action_list_param.py new file mode 100644 index 0000000..6906690 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/computer_action_list_param.py @@ -0,0 +1,198 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "ComputerActionListParam", + "ComputerActionParam", + "Click", + "DoubleClick", + "Drag", + "DragPath", + "Keypress", + "Move", + "Screenshot", + "Scroll", + "Type", + "Wait", +] + + +class Click(TypedDict, total=False): + """A click action.""" + + button: Required[Literal["left", "right", "wheel", "back", "forward"]] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Required[Literal["click"]] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: Required[int] + """The x-coordinate where the click occurred.""" + + y: Required[int] + """The y-coordinate where the click occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while clicking.""" + + +class DoubleClick(TypedDict, total=False): + """A double click action.""" + + keys: Required[Optional[SequenceNotStr[str]]] + """The keys being held while double-clicking.""" + + type: Required[Literal["double_click"]] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: Required[int] + """The x-coordinate where the double click occurred.""" + + y: Required[int] + """The y-coordinate where the double click occurred.""" + + +class DragPath(TypedDict, total=False): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: Required[int] + """The x-coordinate.""" + + y: Required[int] + """The y-coordinate.""" + + +class Drag(TypedDict, total=False): + """A drag action.""" + + path: Required[Iterable[DragPath]] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Required[Literal["drag"]] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + keys: Optional[SequenceNotStr[str]] + """The keys being held while dragging the mouse.""" + + +class Keypress(TypedDict, total=False): + """A collection of keypresses the model would like to perform.""" + + keys: Required[SequenceNotStr[str]] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Required[Literal["keypress"]] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class Move(TypedDict, total=False): + """A mouse move action.""" + + type: Required[Literal["move"]] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: Required[int] + """The x-coordinate to move to.""" + + y: Required[int] + """The y-coordinate to move to.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while moving the mouse.""" + + +class Screenshot(TypedDict, total=False): + """A screenshot action.""" + + type: Required[Literal["screenshot"]] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class Scroll(TypedDict, total=False): + """A scroll action.""" + + scroll_x: Required[int] + """The horizontal scroll distance.""" + + scroll_y: Required[int] + """The vertical scroll distance.""" + + type: Required[Literal["scroll"]] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: Required[int] + """The x-coordinate where the scroll occurred.""" + + y: Required[int] + """The y-coordinate where the scroll occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while scrolling.""" + + +class Type(TypedDict, total=False): + """An action to type in text.""" + + text: Required[str] + """The text to type.""" + + type: Required[Literal["type"]] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class Wait(TypedDict, total=False): + """A wait action.""" + + type: Required[Literal["wait"]] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +ComputerActionParam: TypeAlias = Union[Click, DoubleClick, Drag, Keypress, Move, Screenshot, Scroll, Type, Wait] + +ComputerActionListParam: TypeAlias = List[ComputerActionParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/computer_action_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/computer_action_param.py new file mode 100644 index 0000000..a01452d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/computer_action_param.py @@ -0,0 +1,195 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "ComputerActionParam", + "Click", + "DoubleClick", + "Drag", + "DragPath", + "Keypress", + "Move", + "Screenshot", + "Scroll", + "Type", + "Wait", +] + + +class Click(TypedDict, total=False): + """A click action.""" + + button: Required[Literal["left", "right", "wheel", "back", "forward"]] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Required[Literal["click"]] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: Required[int] + """The x-coordinate where the click occurred.""" + + y: Required[int] + """The y-coordinate where the click occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while clicking.""" + + +class DoubleClick(TypedDict, total=False): + """A double click action.""" + + keys: Required[Optional[SequenceNotStr[str]]] + """The keys being held while double-clicking.""" + + type: Required[Literal["double_click"]] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: Required[int] + """The x-coordinate where the double click occurred.""" + + y: Required[int] + """The y-coordinate where the double click occurred.""" + + +class DragPath(TypedDict, total=False): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: Required[int] + """The x-coordinate.""" + + y: Required[int] + """The y-coordinate.""" + + +class Drag(TypedDict, total=False): + """A drag action.""" + + path: Required[Iterable[DragPath]] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Required[Literal["drag"]] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + keys: Optional[SequenceNotStr[str]] + """The keys being held while dragging the mouse.""" + + +class Keypress(TypedDict, total=False): + """A collection of keypresses the model would like to perform.""" + + keys: Required[SequenceNotStr[str]] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Required[Literal["keypress"]] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class Move(TypedDict, total=False): + """A mouse move action.""" + + type: Required[Literal["move"]] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: Required[int] + """The x-coordinate to move to.""" + + y: Required[int] + """The y-coordinate to move to.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while moving the mouse.""" + + +class Screenshot(TypedDict, total=False): + """A screenshot action.""" + + type: Required[Literal["screenshot"]] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class Scroll(TypedDict, total=False): + """A scroll action.""" + + scroll_x: Required[int] + """The horizontal scroll distance.""" + + scroll_y: Required[int] + """The vertical scroll distance.""" + + type: Required[Literal["scroll"]] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: Required[int] + """The x-coordinate where the scroll occurred.""" + + y: Required[int] + """The y-coordinate where the scroll occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while scrolling.""" + + +class Type(TypedDict, total=False): + """An action to type in text.""" + + text: Required[str] + """The text to type.""" + + type: Required[Literal["type"]] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class Wait(TypedDict, total=False): + """A wait action.""" + + type: Required[Literal["wait"]] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +ComputerActionParam: TypeAlias = Union[Click, DoubleClick, Drag, Keypress, Move, Screenshot, Scroll, Type, Wait] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/computer_tool.py b/venv/lib/python3.12/site-packages/openai/types/responses/computer_tool.py new file mode 100644 index 0000000..151d71f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/computer_tool.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ComputerTool"] + + +class ComputerTool(BaseModel): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use). + """ + + type: Literal["computer"] + """The type of the computer tool. Always `computer`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/computer_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/computer_tool_param.py new file mode 100644 index 0000000..5d8eed8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/computer_tool_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ComputerToolParam"] + + +class ComputerToolParam(TypedDict, total=False): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use). + """ + + type: Required[Literal["computer"]] + """The type of the computer tool. Always `computer`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/computer_use_preview_tool.py b/venv/lib/python3.12/site-packages/openai/types/responses/computer_use_preview_tool.py new file mode 100644 index 0000000..548de39 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/computer_use_preview_tool.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ComputerUsePreviewTool"] + + +class ComputerUsePreviewTool(BaseModel): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use). + """ + + display_height: int + """The height of the computer display.""" + + display_width: int + """The width of the computer display.""" + + environment: Literal["windows", "mac", "linux", "ubuntu", "browser"] + """The type of computer environment to control.""" + + type: Literal["computer_use_preview"] + """The type of the computer use tool. Always `computer_use_preview`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/computer_use_preview_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/computer_use_preview_tool_param.py new file mode 100644 index 0000000..39109a1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/computer_use_preview_tool_param.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ComputerUsePreviewToolParam"] + + +class ComputerUsePreviewToolParam(TypedDict, total=False): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use). + """ + + display_height: Required[int] + """The height of the computer display.""" + + display_width: Required[int] + """The width of the computer display.""" + + environment: Required[Literal["windows", "mac", "linux", "ubuntu", "browser"]] + """The type of computer environment to control.""" + + type: Required[Literal["computer_use_preview"]] + """The type of the computer use tool. Always `computer_use_preview`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/container_auto.py b/venv/lib/python3.12/site-packages/openai/types/responses/container_auto.py new file mode 100644 index 0000000..8f71f9d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/container_auto.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .inline_skill import InlineSkill +from .skill_reference import SkillReference +from .container_network_policy_disabled import ContainerNetworkPolicyDisabled +from .container_network_policy_allowlist import ContainerNetworkPolicyAllowlist + +__all__ = ["ContainerAuto", "NetworkPolicy", "Skill"] + +NetworkPolicy: TypeAlias = Annotated[ + Union[ContainerNetworkPolicyDisabled, ContainerNetworkPolicyAllowlist], PropertyInfo(discriminator="type") +] + +Skill: TypeAlias = Annotated[Union[SkillReference, InlineSkill], PropertyInfo(discriminator="type")] + + +class ContainerAuto(BaseModel): + type: Literal["container_auto"] + """Automatically creates a container for this request""" + + file_ids: Optional[List[str]] = None + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit for the container.""" + + network_policy: Optional[NetworkPolicy] = None + """Network access policy for the container.""" + + skills: Optional[List[Skill]] = None + """An optional list of skills referenced by id or inline data.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/container_auto_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/container_auto_param.py new file mode 100644 index 0000000..dc01ab9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/container_auto_param.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .inline_skill_param import InlineSkillParam +from .skill_reference_param import SkillReferenceParam +from .container_network_policy_disabled_param import ContainerNetworkPolicyDisabledParam +from .container_network_policy_allowlist_param import ContainerNetworkPolicyAllowlistParam + +__all__ = ["ContainerAutoParam", "NetworkPolicy", "Skill"] + +NetworkPolicy: TypeAlias = Union[ContainerNetworkPolicyDisabledParam, ContainerNetworkPolicyAllowlistParam] + +Skill: TypeAlias = Union[SkillReferenceParam, InlineSkillParam] + + +class ContainerAutoParam(TypedDict, total=False): + type: Required[Literal["container_auto"]] + """Automatically creates a container for this request""" + + file_ids: SequenceNotStr[str] + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] + """The memory limit for the container.""" + + network_policy: NetworkPolicy + """Network access policy for the container.""" + + skills: Iterable[Skill] + """An optional list of skills referenced by id or inline data.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_allowlist.py b/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_allowlist.py new file mode 100644 index 0000000..c8b4d5d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_allowlist.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .container_network_policy_domain_secret import ContainerNetworkPolicyDomainSecret + +__all__ = ["ContainerNetworkPolicyAllowlist"] + + +class ContainerNetworkPolicyAllowlist(BaseModel): + allowed_domains: List[str] + """A list of allowed domains when type is `allowlist`.""" + + type: Literal["allowlist"] + """Allow outbound network access only to specified domains. Always `allowlist`.""" + + domain_secrets: Optional[List[ContainerNetworkPolicyDomainSecret]] = None + """Optional domain-scoped secrets for allowlisted domains.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_allowlist_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_allowlist_param.py new file mode 100644 index 0000000..c63a7b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_allowlist_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr +from .container_network_policy_domain_secret_param import ContainerNetworkPolicyDomainSecretParam + +__all__ = ["ContainerNetworkPolicyAllowlistParam"] + + +class ContainerNetworkPolicyAllowlistParam(TypedDict, total=False): + allowed_domains: Required[SequenceNotStr[str]] + """A list of allowed domains when type is `allowlist`.""" + + type: Required[Literal["allowlist"]] + """Allow outbound network access only to specified domains. Always `allowlist`.""" + + domain_secrets: Iterable[ContainerNetworkPolicyDomainSecretParam] + """Optional domain-scoped secrets for allowlisted domains.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_disabled.py b/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_disabled.py new file mode 100644 index 0000000..eef8ad5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_disabled.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ContainerNetworkPolicyDisabled"] + + +class ContainerNetworkPolicyDisabled(BaseModel): + type: Literal["disabled"] + """Disable outbound network access. Always `disabled`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_disabled_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_disabled_param.py new file mode 100644 index 0000000..2c2ffdb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_disabled_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ContainerNetworkPolicyDisabledParam"] + + +class ContainerNetworkPolicyDisabledParam(TypedDict, total=False): + type: Required[Literal["disabled"]] + """Disable outbound network access. Always `disabled`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_domain_secret.py b/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_domain_secret.py new file mode 100644 index 0000000..25b88f1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_domain_secret.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["ContainerNetworkPolicyDomainSecret"] + + +class ContainerNetworkPolicyDomainSecret(BaseModel): + domain: str + """The domain associated with the secret.""" + + name: str + """The name of the secret to inject for the domain.""" + + value: str + """The secret value to inject for the domain.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_domain_secret_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_domain_secret_param.py new file mode 100644 index 0000000..28274fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/container_network_policy_domain_secret_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ContainerNetworkPolicyDomainSecretParam"] + + +class ContainerNetworkPolicyDomainSecretParam(TypedDict, total=False): + domain: Required[str] + """The domain associated with the secret.""" + + name: Required[str] + """The name of the secret to inject for the domain.""" + + value: Required[str] + """The secret value to inject for the domain.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/container_reference.py b/venv/lib/python3.12/site-packages/openai/types/responses/container_reference.py new file mode 100644 index 0000000..855ad04 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/container_reference.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ContainerReference"] + + +class ContainerReference(BaseModel): + container_id: str + """The ID of the referenced container.""" + + type: Literal["container_reference"] + """References a container created with the /v1/containers endpoint""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/container_reference_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/container_reference_param.py new file mode 100644 index 0000000..8389253 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/container_reference_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ContainerReferenceParam"] + + +class ContainerReferenceParam(TypedDict, total=False): + container_id: Required[str] + """The ID of the referenced container.""" + + type: Required[Literal["container_reference"]] + """References a container created with the /v1/containers endpoint""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/custom_tool.py b/venv/lib/python3.12/site-packages/openai/types/responses/custom_tool.py new file mode 100644 index 0000000..4a723b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/custom_tool.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel +from ..shared.custom_tool_input_format import CustomToolInputFormat + +__all__ = ["CustomTool"] + + +class CustomTool(BaseModel): + """A custom tool that processes input using a specified format. + + Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) + """ + + name: str + """The name of the custom tool, used to identify it in tool calls.""" + + type: Literal["custom"] + """The type of the custom tool. Always `custom`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + async_: Optional[bool] = FieldInfo(alias="async", default=None) + """ + Whether the tool response can be returned asynchronously versus immediately + returned on next response creation. + """ + + defer_loading: Optional[bool] = None + """Whether this tool should be deferred and discovered via tool search.""" + + description: Optional[str] = None + """Optional description of the custom tool, used to provide more context.""" + + format: Optional[CustomToolInputFormat] = None + """The input format for the custom tool. Default is unconstrained text.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/custom_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/custom_tool_param.py new file mode 100644 index 0000000..8a6e7aa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/custom_tool_param.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Required, TypedDict + +from ..shared_params.custom_tool_input_format import CustomToolInputFormat + +__all__ = ["CustomToolParam"] + +_CustomToolParamReservedKeywords = TypedDict( + "_CustomToolParamReservedKeywords", + { + "async": bool, + }, + total=False, +) + + +class CustomToolParam(_CustomToolParamReservedKeywords, total=False): + """A custom tool that processes input using a specified format. + + Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) + """ + + name: Required[str] + """The name of the custom tool, used to identify it in tool calls.""" + + type: Required[Literal["custom"]] + """The type of the custom tool. Always `custom`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + defer_loading: bool + """Whether this tool should be deferred and discovered via tool search.""" + + description: str + """Optional description of the custom tool, used to provide more context.""" + + format: CustomToolInputFormat + """The input format for the custom tool. Default is unconstrained text.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/easy_input_message.py b/venv/lib/python3.12/site-packages/openai/types/responses/easy_input_message.py new file mode 100644 index 0000000..b497811 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/easy_input_message.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_input_message_content_list import ResponseInputMessageContentList + +__all__ = ["EasyInputMessage"] + + +class EasyInputMessage(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: Union[str, ResponseInputMessageContentList] + """ + Text, image, or audio input to the model, used to generate a response. Can also + contain previous assistant responses. + """ + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + phase: Optional[Literal["commentary", "final_answer"]] = None + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/easy_input_message_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/easy_input_message_param.py new file mode 100644 index 0000000..6473153 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/easy_input_message_param.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypedDict + +from .response_input_message_content_list_param import ResponseInputMessageContentListParam + +__all__ = ["EasyInputMessageParam"] + + +class EasyInputMessageParam(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: Required[Union[str, ResponseInputMessageContentListParam]] + """ + Text, image, or audio input to the model, used to generate a response. Can also + contain previous assistant responses. + """ + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + phase: Optional[Literal["commentary", "final_answer"]] + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/file_search_tool.py b/venv/lib/python3.12/site-packages/openai/types/responses/file_search_tool.py new file mode 100644 index 0000000..d0c30e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/file_search_tool.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from ..shared.compound_filter import CompoundFilter +from ..shared.comparison_filter import ComparisonFilter + +__all__ = ["FileSearchTool", "Filters", "RankingOptions", "RankingOptionsHybridSearch"] + +Filters: TypeAlias = Union[ComparisonFilter, CompoundFilter, None] + + +class RankingOptionsHybridSearch(BaseModel): + """ + Weights that control how reciprocal rank fusion balances semantic embedding matches versus sparse keyword matches when hybrid search is enabled. + """ + + embedding_weight: float + """The weight of the embedding in the reciprocal ranking fusion.""" + + text_weight: float + """The weight of the text in the reciprocal ranking fusion.""" + + +class RankingOptions(BaseModel): + """Ranking options for search.""" + + hybrid_search: Optional[RankingOptionsHybridSearch] = None + """ + Weights that control how reciprocal rank fusion balances semantic embedding + matches versus sparse keyword matches when hybrid search is enabled. + """ + + ranker: Optional[Literal["auto", "default-2024-11-15"]] = None + """The ranker to use for the file search.""" + + score_threshold: Optional[float] = None + """The score threshold for the file search, a number between 0 and 1. + + Numbers closer to 1 will attempt to return only the most relevant results, but + may return fewer results. + """ + + +class FileSearchTool(BaseModel): + """A tool that searches for relevant content from uploaded files. + + Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search). + """ + + type: Literal["file_search"] + """The type of the file search tool. Always `file_search`.""" + + vector_store_ids: List[str] + """The IDs of the vector stores to search.""" + + filters: Optional[Filters] = None + """A filter to apply.""" + + max_num_results: Optional[int] = None + """The maximum number of results to return. + + This number should be between 1 and 50 inclusive. + """ + + ranking_options: Optional[RankingOptions] = None + """Ranking options for search.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/file_search_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/file_search_tool_param.py new file mode 100644 index 0000000..802c75b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/file_search_tool_param.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from ..shared_params.compound_filter import CompoundFilter +from ..shared_params.comparison_filter import ComparisonFilter + +__all__ = ["FileSearchToolParam", "Filters", "RankingOptions", "RankingOptionsHybridSearch"] + +Filters: TypeAlias = Union[ComparisonFilter, CompoundFilter] + + +class RankingOptionsHybridSearch(TypedDict, total=False): + """ + Weights that control how reciprocal rank fusion balances semantic embedding matches versus sparse keyword matches when hybrid search is enabled. + """ + + embedding_weight: Required[float] + """The weight of the embedding in the reciprocal ranking fusion.""" + + text_weight: Required[float] + """The weight of the text in the reciprocal ranking fusion.""" + + +class RankingOptions(TypedDict, total=False): + """Ranking options for search.""" + + hybrid_search: RankingOptionsHybridSearch + """ + Weights that control how reciprocal rank fusion balances semantic embedding + matches versus sparse keyword matches when hybrid search is enabled. + """ + + ranker: Literal["auto", "default-2024-11-15"] + """The ranker to use for the file search.""" + + score_threshold: float + """The score threshold for the file search, a number between 0 and 1. + + Numbers closer to 1 will attempt to return only the most relevant results, but + may return fewer results. + """ + + +class FileSearchToolParam(TypedDict, total=False): + """A tool that searches for relevant content from uploaded files. + + Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search). + """ + + type: Required[Literal["file_search"]] + """The type of the file search tool. Always `file_search`.""" + + vector_store_ids: Required[SequenceNotStr[str]] + """The IDs of the vector stores to search.""" + + filters: Optional[Filters] + """A filter to apply.""" + + max_num_results: int + """The maximum number of results to return. + + This number should be between 1 and 50 inclusive. + """ + + ranking_options: RankingOptions + """Ranking options for search.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/function_shell_tool.py b/venv/lib/python3.12/site-packages/openai/types/responses/function_shell_tool.py new file mode 100644 index 0000000..2d8542f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/function_shell_tool.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .container_auto import ContainerAuto +from .local_environment import LocalEnvironment +from .container_reference import ContainerReference + +__all__ = ["FunctionShellTool", "Environment"] + +Environment: TypeAlias = Annotated[ + Union[ContainerAuto, LocalEnvironment, ContainerReference, None], PropertyInfo(discriminator="type") +] + + +class FunctionShellTool(BaseModel): + """A tool that allows the model to execute shell commands.""" + + type: Literal["shell"] + """The type of the shell tool. Always `shell`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + environment: Optional[Environment] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/function_shell_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/function_shell_tool_param.py new file mode 100644 index 0000000..a8867cb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/function_shell_tool_param.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .container_auto_param import ContainerAutoParam +from .local_environment_param import LocalEnvironmentParam +from .container_reference_param import ContainerReferenceParam + +__all__ = ["FunctionShellToolParam", "Environment"] + +Environment: TypeAlias = Union[ContainerAutoParam, LocalEnvironmentParam, ContainerReferenceParam] + + +class FunctionShellToolParam(TypedDict, total=False): + """A tool that allows the model to execute shell commands.""" + + type: Required[Literal["shell"]] + """The type of the shell tool. Always `shell`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + environment: Optional[Environment] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/function_tool.py b/venv/lib/python3.12/site-packages/openai/types/responses/function_tool.py new file mode 100644 index 0000000..38d1005 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/function_tool.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["FunctionTool"] + + +class FunctionTool(BaseModel): + """Defines a function in your own code the model can choose to call. + + Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling). + """ + + name: str + """The name of the function to call.""" + + parameters: Optional[Dict[str, object]] = None + """A JSON schema object describing the parameters of the function.""" + + strict: Optional[bool] = None + """Whether strict parameter validation is enforced for this function tool.""" + + type: Literal["function"] + """The type of the function tool. Always `function`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + async_: Optional[bool] = FieldInfo(alias="async", default=None) + + defer_loading: Optional[bool] = None + """Whether this function is deferred and loaded via tool search.""" + + description: Optional[str] = None + """A description of the function. + + Used by the model to determine whether or not to call the function. + """ + + output_schema: Optional[Dict[str, object]] = None + """ + A JSON schema object describing the JSON value encoded in string outputs for + this function. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/function_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/function_tool_param.py new file mode 100644 index 0000000..5e4055e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/function_tool_param.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["FunctionToolParam"] + +_FunctionToolParamReservedKeywords = TypedDict( + "_FunctionToolParamReservedKeywords", + { + "async": bool, + }, + total=False, +) + + +class FunctionToolParam(_FunctionToolParamReservedKeywords, total=False): + """Defines a function in your own code the model can choose to call. + + Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling). + """ + + name: Required[str] + """The name of the function to call.""" + + parameters: Required[Optional[Dict[str, object]]] + """A JSON schema object describing the parameters of the function.""" + + strict: Required[Optional[bool]] + """Whether strict parameter validation is enforced for this function tool.""" + + type: Required[Literal["function"]] + """The type of the function tool. Always `function`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + defer_loading: bool + """Whether this function is deferred and loaded via tool search.""" + + description: Optional[str] + """A description of the function. + + Used by the model to determine whether or not to call the function. + """ + + output_schema: Optional[Dict[str, object]] + """ + A JSON schema object describing the JSON value encoded in string outputs for + this function. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/image_detail.py b/venv/lib/python3.12/site-packages/openai/types/responses/image_detail.py new file mode 100644 index 0000000..873ade4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/image_detail.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ImageDetail"] + +ImageDetail: TypeAlias = Literal["low", "high", "auto", "original"] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/inline_skill.py b/venv/lib/python3.12/site-packages/openai/types/responses/inline_skill.py new file mode 100644 index 0000000..178ea92 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/inline_skill.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .inline_skill_source import InlineSkillSource + +__all__ = ["InlineSkill"] + + +class InlineSkill(BaseModel): + description: str + """The description of the skill.""" + + name: str + """The name of the skill.""" + + source: InlineSkillSource + """Inline skill payload""" + + type: Literal["inline"] + """Defines an inline skill for this request.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/inline_skill_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/inline_skill_param.py new file mode 100644 index 0000000..20dc3c3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/inline_skill_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .inline_skill_source_param import InlineSkillSourceParam + +__all__ = ["InlineSkillParam"] + + +class InlineSkillParam(TypedDict, total=False): + description: Required[str] + """The description of the skill.""" + + name: Required[str] + """The name of the skill.""" + + source: Required[InlineSkillSourceParam] + """Inline skill payload""" + + type: Required[Literal["inline"]] + """Defines an inline skill for this request.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/inline_skill_source.py b/venv/lib/python3.12/site-packages/openai/types/responses/inline_skill_source.py new file mode 100644 index 0000000..4c3fd88 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/inline_skill_source.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InlineSkillSource"] + + +class InlineSkillSource(BaseModel): + """Inline skill payload""" + + data: str + """Base64-encoded skill zip bundle.""" + + media_type: Literal["application/zip"] + """The media type of the inline skill payload. Must be `application/zip`.""" + + type: Literal["base64"] + """The type of the inline skill source. Must be `base64`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/inline_skill_source_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/inline_skill_source_param.py new file mode 100644 index 0000000..e485ec2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/inline_skill_source_param.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InlineSkillSourceParam"] + + +class InlineSkillSourceParam(TypedDict, total=False): + """Inline skill payload""" + + data: Required[str] + """Base64-encoded skill zip bundle.""" + + media_type: Required[Literal["application/zip"]] + """The media type of the inline skill payload. Must be `application/zip`.""" + + type: Required[Literal["base64"]] + """The type of the inline skill source. Must be `base64`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/input_item_list_params.py b/venv/lib/python3.12/site-packages/openai/types/responses/input_item_list_params.py new file mode 100644 index 0000000..da576f6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/input_item_list_params.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +from .response_includable import ResponseIncludable + +__all__ = ["InputItemListParams"] + + +class InputItemListParams(TypedDict, total=False): + after: str + """An item ID to list items after, used in pagination.""" + + include: List[ResponseIncludable] + """Additional fields to include in the response. + + See the `include` parameter for Response creation above for more information. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/input_token_count_params.py b/venv/lib/python3.12/site-packages/openai/types/responses/input_token_count_params.py new file mode 100644 index 0000000..a0f0963 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/input_token_count_params.py @@ -0,0 +1,165 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .tool_param import ToolParam +from .tool_choice_options import ToolChoiceOptions +from .tool_choice_mcp_param import ToolChoiceMcpParam +from .tool_choice_shell_param import ToolChoiceShellParam +from .tool_choice_types_param import ToolChoiceTypesParam +from ..shared_params.reasoning import Reasoning +from .tool_choice_custom_param import ToolChoiceCustomParam +from .response_input_item_param import ResponseInputItemParam +from .tool_choice_allowed_param import ToolChoiceAllowedParam +from .tool_choice_function_param import ToolChoiceFunctionParam +from .tool_choice_apply_patch_param import ToolChoiceApplyPatchParam +from .response_conversation_param_param import ResponseConversationParamParam +from .response_format_text_config_param import ResponseFormatTextConfigParam + +__all__ = [ + "InputTokenCountParams", + "Conversation", + "Text", + "ToolChoice", + "ToolChoiceSpecificProgrammaticToolCallingParam", +] + + +class InputTokenCountParams(TypedDict, total=False): + conversation: Optional[Conversation] + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + input: Union[str, Iterable[ResponseInputItemParam], None] + """Text, image, or file inputs to the model, used to generate a response""" + + instructions: Optional[str] + """ + A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + """ + + model: Optional[str] + """Model ID used to generate the response, like `gpt-4o` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + parallel_tool_calls: Optional[bool] + """Whether to allow the model to run tool calls in parallel.""" + + personality: Union[str, Literal["friendly", "pragmatic"]] + """A model-owned style preset to apply to this request. + + Omit this parameter to use the model's default style. Supported values may + expand over time. Values must be at most 64 characters. + """ + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + reasoning: Optional[Reasoning] + """ + **gpt-5 and o-series models only** Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + text: Optional[Text] + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + tool_choice: Optional[ToolChoice] + """Controls which tool the model should use, if any.""" + + tools: Optional[Iterable[ToolParam]] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + """ + + truncation: Literal["auto", "disabled"] + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. - `disabled` (default): If the + input size will exceed the context window size for a model, the request will + fail with a 400 error. + """ + + +Conversation: TypeAlias = Union[str, ResponseConversationParamParam] + + +class Text(TypedDict, total=False): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + format: ResponseFormatTextConfigParam + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. The default is `medium`. + """ + + +class ToolChoiceSpecificProgrammaticToolCallingParam(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ToolChoice: TypeAlias = Union[ + ToolChoiceOptions, + ToolChoiceAllowedParam, + ToolChoiceTypesParam, + ToolChoiceFunctionParam, + ToolChoiceMcpParam, + ToolChoiceCustomParam, + ToolChoiceSpecificProgrammaticToolCallingParam, + ToolChoiceApplyPatchParam, + ToolChoiceShellParam, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/input_token_count_response.py b/venv/lib/python3.12/site-packages/openai/types/responses/input_token_count_response.py new file mode 100644 index 0000000..ac46a67 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/input_token_count_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputTokenCountResponse"] + + +class InputTokenCountResponse(BaseModel): + input_tokens: int + + object: Literal["response.input_tokens"] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/local_environment.py b/venv/lib/python3.12/site-packages/openai/types/responses/local_environment.py new file mode 100644 index 0000000..8008e19 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/local_environment.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .local_skill import LocalSkill + +__all__ = ["LocalEnvironment"] + + +class LocalEnvironment(BaseModel): + type: Literal["local"] + """Use a local computer environment.""" + + skills: Optional[List[LocalSkill]] = None + """An optional list of skills.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/local_environment_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/local_environment_param.py new file mode 100644 index 0000000..c7ea15f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/local_environment_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +from .local_skill_param import LocalSkillParam + +__all__ = ["LocalEnvironmentParam"] + + +class LocalEnvironmentParam(TypedDict, total=False): + type: Required[Literal["local"]] + """Use a local computer environment.""" + + skills: Iterable[LocalSkillParam] + """An optional list of skills.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/local_skill.py b/venv/lib/python3.12/site-packages/openai/types/responses/local_skill.py new file mode 100644 index 0000000..50d2fc6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/local_skill.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["LocalSkill"] + + +class LocalSkill(BaseModel): + description: str + """The description of the skill.""" + + name: str + """The name of the skill.""" + + path: str + """The path to the directory containing the skill.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/local_skill_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/local_skill_param.py new file mode 100644 index 0000000..7068698 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/local_skill_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["LocalSkillParam"] + + +class LocalSkillParam(TypedDict, total=False): + description: Required[str] + """The description of the skill.""" + + name: Required[str] + """The name of the skill.""" + + path: Required[str] + """The path to the directory containing the skill.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/mcp_tool_call_error.py b/venv/lib/python3.12/site-packages/openai/types/responses/mcp_tool_call_error.py new file mode 100644 index 0000000..6a4eed5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/mcp_tool_call_error.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["McpToolCallError", "McpProtocolError", "McpToolExecutionError", "HTTPError"] + + +class McpProtocolError(BaseModel): + code: int + + message: str + + type: Literal["mcp_protocol_error"] + + +class McpToolExecutionError(BaseModel): + content: object + + type: Literal["mcp_tool_execution_error"] + + +class HTTPError(BaseModel): + code: int + + message: str + + type: Literal["http_error"] + + +McpToolCallError: TypeAlias = Annotated[ + Union[McpProtocolError, McpToolExecutionError, HTTPError], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/mcp_tool_call_error_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/mcp_tool_call_error_param.py new file mode 100644 index 0000000..2e0b26e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/mcp_tool_call_error_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["McpToolCallErrorParam", "McpProtocolError", "McpToolExecutionError", "HTTPError"] + + +class McpProtocolError(TypedDict, total=False): + code: Required[int] + + message: Required[str] + + type: Required[Literal["mcp_protocol_error"]] + + +class McpToolExecutionError(TypedDict, total=False): + content: Required[object] + + type: Required[Literal["mcp_tool_execution_error"]] + + +class HTTPError(TypedDict, total=False): + code: Required[int] + + message: Required[str] + + type: Required[Literal["http_error"]] + + +McpToolCallErrorParam: TypeAlias = Union[McpProtocolError, McpToolExecutionError, HTTPError] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/namespace_tool.py b/venv/lib/python3.12/site-packages/openai/types/responses/namespace_tool.py new file mode 100644 index 0000000..eaa95f6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/namespace_tool.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .custom_tool import CustomTool + +__all__ = ["NamespaceTool", "Tool", "ToolFunction"] + + +class ToolFunction(BaseModel): + name: str + + type: Literal["function"] + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + async_: Optional[bool] = FieldInfo(alias="async", default=None) + """ + Whether the tool response can be returned asynchronously versus immediately + returned on next response creation. + """ + + defer_loading: Optional[bool] = None + """Whether this function should be deferred and discovered via tool search.""" + + description: Optional[str] = None + + output_schema: Optional[Dict[str, object]] = None + """ + A JSON Schema describing the JSON value encoded in string outputs for this + function tool. This does not describe content-array outputs. + """ + + parameters: Optional[object] = None + + strict: Optional[bool] = None + """Whether to enforce strict parameter validation. + + If omitted, Responses attempts to use strict validation when the schema is + compatible, and falls back to non-strict validation otherwise. + """ + + +Tool: TypeAlias = Annotated[Union[ToolFunction, CustomTool], PropertyInfo(discriminator="type")] + + +class NamespaceTool(BaseModel): + """Groups function/custom tools under a shared namespace.""" + + description: str + """A description of the namespace shown to the model.""" + + name: str + """The namespace name used in tool calls (for example, `crm`).""" + + tools: List[Tool] + """The function/custom tools available inside this namespace.""" + + type: Literal["namespace"] + """The type of the tool. Always `namespace`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/namespace_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/namespace_tool_param.py new file mode 100644 index 0000000..55baaac --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/namespace_tool_param.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .custom_tool_param import CustomToolParam + +__all__ = ["NamespaceToolParam", "Tool", "ToolFunction"] + +_ToolFunctionReservedKeywords = TypedDict( + "_ToolFunctionReservedKeywords", + { + "async": bool, + }, + total=False, +) + + +class ToolFunction(_ToolFunctionReservedKeywords, total=False): + name: Required[str] + + type: Required[Literal["function"]] + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + defer_loading: bool + """Whether this function should be deferred and discovered via tool search.""" + + description: Optional[str] + + output_schema: Optional[Dict[str, object]] + """ + A JSON Schema describing the JSON value encoded in string outputs for this + function tool. This does not describe content-array outputs. + """ + + parameters: Optional[object] + + strict: Optional[bool] + """Whether to enforce strict parameter validation. + + If omitted, Responses attempts to use strict validation when the schema is + compatible, and falls back to non-strict validation otherwise. + """ + + +Tool: TypeAlias = Union[ToolFunction, CustomToolParam] + + +class NamespaceToolParam(TypedDict, total=False): + """Groups function/custom tools under a shared namespace.""" + + description: Required[str] + """A description of the namespace shown to the model.""" + + name: Required[str] + """The namespace name used in tool calls (for example, `crm`).""" + + tools: Required[Iterable[Tool]] + """The function/custom tools available inside this namespace.""" + + type: Required[Literal["namespace"]] + """The type of the tool. Always `namespace`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/parsed_response.py b/venv/lib/python3.12/site-packages/openai/types/responses/parsed_response.py new file mode 100644 index 0000000..d0977fa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/parsed_response.py @@ -0,0 +1,124 @@ +from typing import TYPE_CHECKING, List, Union, Generic, TypeVar, Optional +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .response import Response +from ..._models import GenericModel +from .response_output_item import ( + McpCall, + Program, + McpListTools, + ProgramOutput, + LocalShellCall, + AdditionalTools, + McpApprovalRequest, + ImageGenerationCall, + McpApprovalResponse, + LocalShellCallAction, + LocalShellCallOutput, +) +from .response_output_text import ResponseOutputText +from .response_output_message import ResponseOutputMessage +from .response_output_refusal import ResponseOutputRefusal +from .response_reasoning_item import ResponseReasoningItem +from .response_compaction_item import ResponseCompactionItem +from .response_custom_tool_call import ResponseCustomToolCall +from .response_tool_search_call import ResponseToolSearchCall +from .response_computer_tool_call import ResponseComputerToolCall +from .response_function_tool_call import ResponseFunctionToolCall +from .response_function_web_search import ResponseFunctionWebSearch +from .response_apply_patch_tool_call import ResponseApplyPatchToolCall +from .response_file_search_tool_call import ResponseFileSearchToolCall +from .response_tool_search_output_item import ResponseToolSearchOutputItem +from .response_function_shell_tool_call import ResponseFunctionShellToolCall +from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall +from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput +from .response_custom_tool_call_output_item import ResponseCustomToolCallOutputItem +from .response_computer_tool_call_output_item import ResponseComputerToolCallOutputItem +from .response_function_tool_call_output_item import ResponseFunctionToolCallOutputItem +from .response_function_shell_tool_call_output import ResponseFunctionShellToolCallOutput + +__all__ = ["ParsedResponse", "ParsedResponseOutputMessage", "ParsedResponseOutputText"] + +ContentType = TypeVar("ContentType") + +# we need to disable this check because we're overriding properties +# with subclasses of their types which is technically unsound as +# properties can be mutated. +# pyright: reportIncompatibleVariableOverride=false + + +class ParsedResponseOutputText(ResponseOutputText, GenericModel, Generic[ContentType]): + parsed: Optional[ContentType] = None + + +ParsedContent: TypeAlias = Annotated[ + Union[ParsedResponseOutputText[ContentType], ResponseOutputRefusal], + PropertyInfo(discriminator="type"), +] + + +class ParsedResponseOutputMessage(ResponseOutputMessage, GenericModel, Generic[ContentType]): + if TYPE_CHECKING: + content: List[ParsedContent[ContentType]] # type: ignore[assignment] + else: + content: List[ParsedContent] + + +class ParsedResponseFunctionToolCall(ResponseFunctionToolCall): + parsed_arguments: object = None + + __api_exclude__ = {"parsed_arguments"} + + +ParsedResponseOutputItem: TypeAlias = Annotated[ + Union[ + ParsedResponseOutputMessage[ContentType], + ParsedResponseFunctionToolCall, + ResponseFileSearchToolCall, + ResponseFunctionWebSearch, + ResponseComputerToolCall, + ResponseComputerToolCallOutputItem, + ResponseToolSearchCall, + ResponseToolSearchOutputItem, + AdditionalTools, + ResponseReasoningItem, + Program, + ProgramOutput, + McpCall, + McpApprovalRequest, + McpApprovalResponse, + ImageGenerationCall, + LocalShellCall, + LocalShellCallOutput, + LocalShellCallAction, + McpListTools, + ResponseCodeInterpreterToolCall, + ResponseCompactionItem, + ResponseFunctionShellToolCall, + ResponseFunctionShellToolCallOutput, + ResponseApplyPatchToolCall, + ResponseApplyPatchToolCallOutput, + ResponseFunctionToolCallOutputItem, + ResponseCustomToolCall, + ResponseCustomToolCallOutputItem, + ], + PropertyInfo(discriminator="type"), +] + + +class ParsedResponse(Response, GenericModel, Generic[ContentType]): + if TYPE_CHECKING: + output: List[ParsedResponseOutputItem[ContentType]] # type: ignore[assignment] + else: + output: List[ParsedResponseOutputItem] + + @property + def output_parsed(self) -> Optional[ContentType]: + for output in self.output: + if output.type == "message": + for content in output.content: + if content.type == "output_text" and content.parsed: + return content.parsed + + return None diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response.py b/venv/lib/python3.12/site-packages/openai/types/responses/response.py new file mode 100644 index 0000000..e1f3052 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response.py @@ -0,0 +1,569 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from .tool import Tool +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .service_tier import ServiceTier +from .response_error import ResponseError +from .response_usage import ResponseUsage +from .response_prompt import ResponsePrompt +from .response_status import ResponseStatus +from .tool_choice_mcp import ToolChoiceMcp +from ..shared.metadata import Metadata +from ..shared.reasoning import Reasoning +from .tool_choice_shell import ToolChoiceShell +from .tool_choice_types import ToolChoiceTypes +from .tool_choice_custom import ToolChoiceCustom +from .response_input_item import ResponseInputItem +from .tool_choice_allowed import ToolChoiceAllowed +from .tool_choice_options import ToolChoiceOptions +from .response_output_item import ResponseOutputItem +from .response_text_config import ResponseTextConfig +from .tool_choice_function import ToolChoiceFunction +from ..shared.responses_model import ResponsesModel +from .tool_choice_apply_patch import ToolChoiceApplyPatch + +__all__ = [ + "Response", + "AccessPrograms", + "IncompleteDetails", + "ToolChoice", + "ToolChoiceSpecificProgrammaticToolCallingParam", + "Conversation", + "Moderation", + "ModerationInput", + "ModerationInputModerationResult", + "ModerationInputError", + "ModerationOutput", + "ModerationOutputModerationResult", + "ModerationOutputError", + "PromptCacheDiagnostics", + "PromptCacheDiagnosticsCacheMiss", + "PromptCacheDiagnosticsCacheHit", + "PromptCacheDiagnosticsComparisonResponseNotFound", + "PromptCacheDiagnosticsUnavailable", + "PromptCacheOptions", +] + + +class AccessPrograms(BaseModel): + cyber: Literal["standard", "daybreak_blue", "daybreak_red"] + """The effective Cyber access program used for this response.""" + + +class IncompleteDetails(BaseModel): + """Details about why the response is incomplete.""" + + reason: Optional[Literal["max_output_tokens", "max_messages", "content_filter", "steered"]] = None + """The reason why the response is incomplete. + + `steered` means the response stopped at a safe output boundary after a WebSocket + `response.steer` event. The server can then create a successor response + automatically with the queued input. + """ + + +class ToolChoiceSpecificProgrammaticToolCallingParam(BaseModel): + type: Literal["programmatic_tool_calling"] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ToolChoice: TypeAlias = Union[ + ToolChoiceOptions, + ToolChoiceAllowed, + ToolChoiceTypes, + ToolChoiceFunction, + ToolChoiceMcp, + ToolChoiceCustom, + ToolChoiceSpecificProgrammaticToolCallingParam, + ToolChoiceApplyPatch, + ToolChoiceShell, +] + + +class Conversation(BaseModel): + """The conversation that this response belonged to. + + Input items and output items from this response were automatically added to this conversation. + """ + + id: str + """The unique ID of the conversation that this response was associated with.""" + + +class ModerationInputModerationResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationInputError(BaseModel): + """An error produced while attempting moderation for the response input or output.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which was always `error` for moderation failures.""" + + +ModerationInput: TypeAlias = Annotated[ + Union[ModerationInputModerationResult, ModerationInputError], PropertyInfo(discriminator="type") +] + + +class ModerationOutputModerationResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationOutputError(BaseModel): + """An error produced while attempting moderation for the response input or output.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which was always `error` for moderation failures.""" + + +ModerationOutput: TypeAlias = Annotated[ + Union[ModerationOutputModerationResult, ModerationOutputError], PropertyInfo(discriminator="type") +] + + +class Moderation(BaseModel): + """ + Moderation results for the response input and output, if moderated completions were requested. + """ + + input: ModerationInput + """Moderation for the response input.""" + + output: ModerationOutput + """Moderation for the response output.""" + + +class PromptCacheDiagnosticsCacheMiss(BaseModel): + cache_missed_tokens: int + """ + The estimated number of input tokens affected after the first detected + divergence. + """ + + reason: Literal[ + "model_changed", + "prompt_cache_key_changed", + "tools_changed", + "text_format_changed", + "reasoning_effort_changed", + "verbosity_changed", + "context_compacted", + "input_changed", + "service_tier_changed", + ] + """The reason prompt cache reuse did not occur.""" + + type: Literal["cache_miss"] + + comparison_reusable_tokens: Optional[int] = None + """The raw token count of the reusable prefix in the compared response.""" + + +class PromptCacheDiagnosticsCacheHit(BaseModel): + type: Literal["cache_hit"] + + +class PromptCacheDiagnosticsComparisonResponseNotFound(BaseModel): + type: Literal["comparison_response_not_found"] + + +class PromptCacheDiagnosticsUnavailable(BaseModel): + type: Literal["unavailable"] + + +PromptCacheDiagnostics: TypeAlias = Annotated[ + Union[ + PromptCacheDiagnosticsCacheMiss, + PromptCacheDiagnosticsCacheHit, + PromptCacheDiagnosticsComparisonResponseNotFound, + PromptCacheDiagnosticsUnavailable, + ], + PropertyInfo(discriminator="type"), +] + + +class PromptCacheOptions(BaseModel): + """The prompt-caching options that were applied to the response. + + Supported for `gpt-5.6` and later models. + """ + + mode: Literal["implicit", "explicit"] + """Whether implicit prompt-cache breakpoints were enabled.""" + + ttl: Literal["30m"] + """The minimum lifetime applied to each cache breakpoint.""" + + comparison_response_id: Optional[str] = None + """The response ID supplied as the prompt cache diagnostics comparison.""" + + +class Response(BaseModel): + id: str + """Unique identifier for this Response.""" + + access_programs: Optional[AccessPrograms] = None + + created_at: float + """Unix timestamp (in seconds) of when this Response was created.""" + + error: Optional[ResponseError] = None + """An error object returned when the model fails to generate a Response.""" + + incomplete_details: Optional[IncompleteDetails] = None + """Details about why the response is incomplete.""" + + instructions: Union[str, List[ResponseInputItem], None] = None + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: ResponsesModel + """Model ID used to generate the response, like `gpt-6-astra`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + object: Literal["response"] + """The object type of this resource - always set to `response`.""" + + output: List[ResponseOutputItem] + """An array of content items generated by the model. + + - The length and order of items in the `output` array is dependent on the + model's response. + - Rather than accessing the first item in the `output` array and assuming it's + an `assistant` message with the content generated by the model, you might + consider using the `output_text` property where supported in SDKs. + """ + + parallel_tool_calls: bool + """Whether to allow the model to run tool calls in parallel.""" + + temperature: Optional[float] = None + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + tool_choice: ToolChoice + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: List[Tool] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_p: Optional[float] = None + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + background: Optional[bool] = None + """ + Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + """ + + completed_at: Optional[float] = None + """ + Unix timestamp (in seconds) of when this Response was completed. Only present + when the status is `completed`. + """ + + conversation: Optional[Conversation] = None + """The conversation that this response belonged to. + + Input items and output items from this response were automatically added to this + conversation. + """ + + max_output_tokens: Optional[int] = None + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] = None + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + moderation: Optional[Moderation] = None + """ + Moderation results for the response input and output, if moderated completions + were requested. + """ + + previous_response_id: Optional[str] = None + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[ResponsePrompt] = None + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + prompt_cache_diagnostics: Optional[PromptCacheDiagnostics] = None + """Prompt cache diagnostics requested for this response.""" + + prompt_cache_key: Optional[str] = None + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + """ + + prompt_cache_options: Optional[PromptCacheOptions] = None + """The prompt-caching options that were applied to the response. + + Supported for `gpt-5.6` and later models. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] = None + """Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + """ + + reasoning: Optional[Reasoning] = None + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + safety_identifier: Optional[str] = None + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + service_tier: Optional[ServiceTier] = None + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + status: Optional[ResponseStatus] = None + """The status of the response generation. + + One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or + `incomplete`. + """ + + text: Optional[ResponseTextConfig] = None + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + top_logprobs: Optional[int] = None + """ + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + """ + + truncation: Optional[Literal["auto", "disabled"]] = None + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + usage: Optional[ResponseUsage] = None + """ + Represents token usage details including input tokens, output tokens, a + breakdown of output tokens, and the total tokens used. + """ + + user: Optional[str] = None + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + @property + def output_text(self) -> str: + """Convenience property that aggregates all `output_text` items from the `output` list. + + If no `output_text` content blocks exist, then an empty string is returned. + """ + texts: List[str] = [] + for output in self.output: + if output.type == "message": + for content in output.content: + # Tolerate null text from compatible providers without changing the public string type. + if content.type == "output_text" and content.text is not None: # pyright: ignore[reportUnnecessaryComparison] + texts.append(content.text) + + return "".join(texts) diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_apply_patch_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_apply_patch_tool_call.py new file mode 100644 index 0000000..ae9d88f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_apply_patch_tool_call.py @@ -0,0 +1,104 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "ResponseApplyPatchToolCall", + "Operation", + "OperationCreateFile", + "OperationDeleteFile", + "OperationUpdateFile", + "Caller", + "CallerDirect", + "CallerProgram", +] + + +class OperationCreateFile(BaseModel): + """Instruction describing how to create a file via the apply_patch tool.""" + + diff: str + """Diff to apply.""" + + path: str + """Path of the file to create.""" + + type: Literal["create_file"] + """Create a new file with the provided diff.""" + + +class OperationDeleteFile(BaseModel): + """Instruction describing how to delete a file via the apply_patch tool.""" + + path: str + """Path of the file to delete.""" + + type: Literal["delete_file"] + """Delete the specified file.""" + + +class OperationUpdateFile(BaseModel): + """Instruction describing how to update a file via the apply_patch tool.""" + + diff: str + """Diff to apply.""" + + path: str + """Path of the file to update.""" + + type: Literal["update_file"] + """Update an existing file with the provided diff.""" + + +Operation: TypeAlias = Annotated[ + Union[OperationCreateFile, OperationDeleteFile, OperationUpdateFile], PropertyInfo(discriminator="type") +] + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class ResponseApplyPatchToolCall(BaseModel): + """A tool call that applies file diffs by creating, deleting, or updating files.""" + + id: str + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + operation: Operation + """ + One of the create_file, delete_file, or update_file operations applied via + apply_patch. + """ + + status: Literal["in_progress", "completed"] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Literal["apply_patch_call"] + """The type of the item. Always `apply_patch_call`.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The ID of the entity that created this tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_apply_patch_tool_call_output.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_apply_patch_tool_call_output.py new file mode 100644 index 0000000..79954ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_apply_patch_tool_call_output.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["ResponseApplyPatchToolCallOutput", "Caller", "CallerDirect", "CallerProgram"] + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class ResponseApplyPatchToolCallOutput(BaseModel): + """The output emitted by an apply patch tool call.""" + + id: str + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + status: Literal["completed", "failed"] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Literal["apply_patch_call_output"] + """The type of the item. Always `apply_patch_call_output`.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The ID of the entity that created this tool call output.""" + + output: Optional[str] = None + """Optional textual output returned by the apply patch tool.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_audio_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_audio_delta_event.py new file mode 100644 index 0000000..515c327 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_audio_delta_event.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioDeltaEvent"] + + +class ResponseAudioDeltaEvent(BaseModel): + """Emitted when there is a partial audio response.""" + + delta: str + """A chunk of Base64 encoded response audio bytes.""" + + sequence_number: int + """A sequence number for this chunk of the stream response.""" + + type: Literal["response.audio.delta"] + """The type of the event. Always `response.audio.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_audio_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_audio_done_event.py new file mode 100644 index 0000000..5b63b40 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_audio_done_event.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioDoneEvent"] + + +class ResponseAudioDoneEvent(BaseModel): + """Emitted when the audio response is complete.""" + + sequence_number: int + """The sequence number of the delta.""" + + type: Literal["response.audio.done"] + """The type of the event. Always `response.audio.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_audio_transcript_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_audio_transcript_delta_event.py new file mode 100644 index 0000000..ddef2c6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_audio_transcript_delta_event.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioTranscriptDeltaEvent"] + + +class ResponseAudioTranscriptDeltaEvent(BaseModel): + """Emitted when there is a partial transcript of audio.""" + + delta: str + """The partial transcript of the audio response.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.audio.transcript.delta"] + """The type of the event. Always `response.audio.transcript.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_audio_transcript_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_audio_transcript_done_event.py new file mode 100644 index 0000000..72e326d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_audio_transcript_done_event.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioTranscriptDoneEvent"] + + +class ResponseAudioTranscriptDoneEvent(BaseModel): + """Emitted when the full audio transcript is completed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.audio.transcript.done"] + """The type of the event. Always `response.audio.transcript.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_code_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_code_delta_event.py new file mode 100644 index 0000000..36ee9fa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_code_delta_event.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCodeInterpreterCallCodeDeltaEvent"] + + +class ResponseCodeInterpreterCallCodeDeltaEvent(BaseModel): + """Emitted when a partial code snippet is streamed by the code interpreter.""" + + delta: str + """The partial code snippet being streamed by the code interpreter.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """ + The index of the output item in the response for which the code is being + streamed. + """ + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call_code.delta"] + """The type of the event. Always `response.code_interpreter_call_code.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_code_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_code_done_event.py new file mode 100644 index 0000000..3fbbf2f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_code_done_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCodeInterpreterCallCodeDoneEvent"] + + +class ResponseCodeInterpreterCallCodeDoneEvent(BaseModel): + """Emitted when the code snippet is finalized by the code interpreter.""" + + code: str + """The final code snippet output by the code interpreter.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """The index of the output item in the response for which the code is finalized.""" + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call_code.done"] + """The type of the event. Always `response.code_interpreter_call_code.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_completed_event.py new file mode 100644 index 0000000..cf800e8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_completed_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCodeInterpreterCallCompletedEvent"] + + +class ResponseCodeInterpreterCallCompletedEvent(BaseModel): + """Emitted when the code interpreter call is completed.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """ + The index of the output item in the response for which the code interpreter call + is completed. + """ + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call.completed"] + """The type of the event. Always `response.code_interpreter_call.completed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_in_progress_event.py new file mode 100644 index 0000000..b59bf97 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_in_progress_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCodeInterpreterCallInProgressEvent"] + + +class ResponseCodeInterpreterCallInProgressEvent(BaseModel): + """Emitted when a code interpreter call is in progress.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """ + The index of the output item in the response for which the code interpreter call + is in progress. + """ + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call.in_progress"] + """The type of the event. Always `response.code_interpreter_call.in_progress`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_interpreting_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_interpreting_event.py new file mode 100644 index 0000000..634e78b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_call_interpreting_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCodeInterpreterCallInterpretingEvent"] + + +class ResponseCodeInterpreterCallInterpretingEvent(BaseModel): + """Emitted when the code interpreter is actively interpreting the code snippet.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """ + The index of the output item in the response for which the code interpreter is + interpreting code. + """ + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call.interpreting"] + """The type of the event. Always `response.code_interpreter_call.interpreting`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_tool_call.py new file mode 100644 index 0000000..d122508 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_tool_call.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["ResponseCodeInterpreterToolCall", "Output", "OutputLogs", "OutputImage"] + + +class OutputLogs(BaseModel): + """The logs output from the code interpreter.""" + + logs: str + """The logs output from the code interpreter.""" + + type: Literal["logs"] + """The type of the output. Always `logs`.""" + + +class OutputImage(BaseModel): + """The image output from the code interpreter.""" + + type: Literal["image"] + """The type of the output. Always `image`.""" + + url: str + """The URL of the image output from the code interpreter.""" + + +Output: TypeAlias = Annotated[Union[OutputLogs, OutputImage], PropertyInfo(discriminator="type")] + + +class ResponseCodeInterpreterToolCall(BaseModel): + """A tool call to run code.""" + + id: str + """The unique ID of the code interpreter tool call.""" + + code: Optional[str] = None + """The code to run, or null if not available.""" + + container_id: str + """The ID of the container used to run the code.""" + + outputs: Optional[List[Output]] = None + """ + The outputs generated by the code interpreter, such as logs or images. Can be + null if no outputs are available. + """ + + status: Literal["in_progress", "completed", "incomplete", "interpreting", "failed"] + """The status of the code interpreter tool call. + + Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and + `failed`. + """ + + type: Literal["code_interpreter_call"] + """The type of the code interpreter tool call. Always `code_interpreter_call`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_tool_call_param.py new file mode 100644 index 0000000..e7d550e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_code_interpreter_tool_call_param.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["ResponseCodeInterpreterToolCallParam", "Output", "OutputLogs", "OutputImage"] + + +class OutputLogs(TypedDict, total=False): + """The logs output from the code interpreter.""" + + logs: Required[str] + """The logs output from the code interpreter.""" + + type: Required[Literal["logs"]] + """The type of the output. Always `logs`.""" + + +class OutputImage(TypedDict, total=False): + """The image output from the code interpreter.""" + + type: Required[Literal["image"]] + """The type of the output. Always `image`.""" + + url: Required[str] + """The URL of the image output from the code interpreter.""" + + +Output: TypeAlias = Union[OutputLogs, OutputImage] + + +class ResponseCodeInterpreterToolCallParam(TypedDict, total=False): + """A tool call to run code.""" + + id: Required[str] + """The unique ID of the code interpreter tool call.""" + + code: Required[Optional[str]] + """The code to run, or null if not available.""" + + container_id: Required[str] + """The ID of the container used to run the code.""" + + outputs: Required[Optional[Iterable[Output]]] + """ + The outputs generated by the code interpreter, such as logs or images. Can be + null if no outputs are available. + """ + + status: Required[Literal["in_progress", "completed", "incomplete", "interpreting", "failed"]] + """The status of the code interpreter tool call. + + Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and + `failed`. + """ + + type: Required[Literal["code_interpreter_call"]] + """The type of the code interpreter tool call. Always `code_interpreter_call`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_compact_params.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_compact_params.py new file mode 100644 index 0000000..8fccf63 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_compact_params.py @@ -0,0 +1,220 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from .response_input_item_param import ResponseInputItemParam + +__all__ = ["ResponseCompactParams", "PromptCacheOptions"] + + +class ResponseCompactParams(TypedDict, total=False): + model: Required[ + Union[ + Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], + str, + None, + ] + ] + """Model ID used to generate the response, like `gpt-6-astra`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + input: Union[str, Iterable[ResponseInputItemParam], None] + """Text, image, or file inputs to the model, used to generate a response""" + + instructions: Optional[str] + """ + A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + """ + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt_cache_key: Optional[str] + """A key to use when reading from or writing to the prompt cache.""" + + prompt_cache_options: Optional[PromptCacheOptions] + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] + """How long to retain a prompt cache entry created by this request.""" + + service_tier: Optional[Literal["auto", "default", "fast", "flex", "priority"]] + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. - If set to 'default', then the request will be processed + with the standard pricing and performance for the selected model. - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. - To + opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) + at the request level, include the `service_tier=fast` or + `service_tier=priority` parameter for Responses or Chat Completions. For + models with a dedicated Fast tier, either value resolves to + `service_tier=fast`; for other models, either value resolves to + `service_tier=priority`. - When not set, the default behavior is 'auto'. When + the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + +class PromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_compaction_compacting_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_compaction_compacting_event.py new file mode 100644 index 0000000..ac530a7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_compaction_compacting_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCompactionCompactingEvent"] + + +class ResponseCompactionCompactingEvent(BaseModel): + """Emitted when new summary content is sampled for a compaction trigger. + + Contains no summary content. + """ + + item_id: str + """The ID of the compaction output item.""" + + output_index: int + """The index of the compaction output item.""" + + sequence_number: int + """The sequence number of the event that was emitted.""" + + type: Literal["response.compaction.compacting"] + """The type of the event, always `response.compaction.compacting`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_compaction_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_compaction_item.py new file mode 100644 index 0000000..c69f01b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_compaction_item.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCompactionItem"] + + +class ResponseCompactionItem(BaseModel): + """ + A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact). + """ + + id: str + """The unique ID of the compaction item.""" + + encrypted_content: str + """The encrypted content that was produced by compaction.""" + + type: Literal["compaction"] + """The type of the item. Always `compaction`.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_compaction_item_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_compaction_item_param.py new file mode 100644 index 0000000..9336d58 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_compaction_item_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCompactionItemParam"] + + +class ResponseCompactionItemParam(BaseModel): + """ + A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact). + """ + + encrypted_content: str + """The encrypted content of the compaction summary.""" + + type: Literal["compaction"] + """The type of the item. Always `compaction`.""" + + id: Optional[str] = None + """The ID of the compaction item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_compaction_item_param_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_compaction_item_param_param.py new file mode 100644 index 0000000..87cd0f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_compaction_item_param_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseCompactionItemParamParam"] + + +class ResponseCompactionItemParamParam(TypedDict, total=False): + """ + A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact). + """ + + encrypted_content: Required[str] + """The encrypted content of the compaction summary.""" + + type: Required[Literal["compaction"]] + """The type of the item. Always `compaction`.""" + + id: Optional[str] + """The ID of the compaction item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_completed_event.py new file mode 100644 index 0000000..2fe3878 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_completed_event.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .response import Response +from ..._models import BaseModel + +__all__ = ["ResponseCompletedEvent"] + + +class ResponseCompletedEvent(BaseModel): + """Emitted when the model response is complete.""" + + response: Response + """Properties of the completed response.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.completed"] + """The type of the event. Always `response.completed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call.py new file mode 100644 index 0000000..d2e3d62 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call.py @@ -0,0 +1,259 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .computer_action_list import ComputerActionList + +__all__ = [ + "ResponseComputerToolCall", + "PendingSafetyCheck", + "Action", + "ActionClick", + "ActionDoubleClick", + "ActionDrag", + "ActionDragPath", + "ActionKeypress", + "ActionMove", + "ActionScreenshot", + "ActionScroll", + "ActionType", + "ActionWait", +] + + +class PendingSafetyCheck(BaseModel): + """A pending safety check for the computer call.""" + + id: str + """The ID of the pending safety check.""" + + code: Optional[str] = None + """The type of the pending safety check.""" + + message: Optional[str] = None + """Details about the pending safety check.""" + + +class ActionClick(BaseModel): + """A click action.""" + + button: Literal["left", "right", "wheel", "back", "forward"] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Literal["click"] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: int + """The x-coordinate where the click occurred.""" + + y: int + """The y-coordinate where the click occurred.""" + + keys: Optional[List[str]] = None + """The keys being held while clicking.""" + + +class ActionDoubleClick(BaseModel): + """A double click action.""" + + keys: Optional[List[str]] = None + """The keys being held while double-clicking.""" + + type: Literal["double_click"] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: int + """The x-coordinate where the double click occurred.""" + + y: int + """The y-coordinate where the double click occurred.""" + + +class ActionDragPath(BaseModel): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: int + """The x-coordinate.""" + + y: int + """The y-coordinate.""" + + +class ActionDrag(BaseModel): + """A drag action.""" + + path: List[ActionDragPath] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Literal["drag"] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + keys: Optional[List[str]] = None + """The keys being held while dragging the mouse.""" + + +class ActionKeypress(BaseModel): + """A collection of keypresses the model would like to perform.""" + + keys: List[str] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Literal["keypress"] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class ActionMove(BaseModel): + """A mouse move action.""" + + type: Literal["move"] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: int + """The x-coordinate to move to.""" + + y: int + """The y-coordinate to move to.""" + + keys: Optional[List[str]] = None + """The keys being held while moving the mouse.""" + + +class ActionScreenshot(BaseModel): + """A screenshot action.""" + + type: Literal["screenshot"] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class ActionScroll(BaseModel): + """A scroll action.""" + + scroll_x: int + """The horizontal scroll distance.""" + + scroll_y: int + """The vertical scroll distance.""" + + type: Literal["scroll"] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: int + """The x-coordinate where the scroll occurred.""" + + y: int + """The y-coordinate where the scroll occurred.""" + + keys: Optional[List[str]] = None + """The keys being held while scrolling.""" + + +class ActionType(BaseModel): + """An action to type in text.""" + + text: str + """The text to type.""" + + type: Literal["type"] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class ActionWait(BaseModel): + """A wait action.""" + + type: Literal["wait"] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +Action: TypeAlias = Annotated[ + Union[ + ActionClick, + ActionDoubleClick, + ActionDrag, + ActionKeypress, + ActionMove, + ActionScreenshot, + ActionScroll, + ActionType, + ActionWait, + ], + PropertyInfo(discriminator="type"), +] + + +class ResponseComputerToolCall(BaseModel): + """A tool call to a computer use tool. + + See the + [computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information. + """ + + id: str + """The unique ID of the computer call.""" + + call_id: str + """An identifier used when responding to the tool call with output.""" + + pending_safety_checks: List[PendingSafetyCheck] + """The pending safety checks for the computer call.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["computer_call"] + """The type of the computer call. Always `computer_call`.""" + + action: Optional[Action] = None + """A click action.""" + + actions: Optional[ComputerActionList] = None + """Flattened batched actions for `computer_use`. + + Each action includes an `type` discriminator and action-specific fields. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call_output_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call_output_item.py new file mode 100644 index 0000000..784221b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call_output_item.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_computer_tool_call_output_screenshot import ResponseComputerToolCallOutputScreenshot + +__all__ = ["ResponseComputerToolCallOutputItem", "AcknowledgedSafetyCheck"] + + +class AcknowledgedSafetyCheck(BaseModel): + """A pending safety check for the computer call.""" + + id: str + """The ID of the pending safety check.""" + + code: Optional[str] = None + """The type of the pending safety check.""" + + message: Optional[str] = None + """Details about the pending safety check.""" + + +class ResponseComputerToolCallOutputItem(BaseModel): + id: str + """The unique ID of the computer call tool output.""" + + call_id: str + """The ID of the computer tool call that produced the output.""" + + output: ResponseComputerToolCallOutputScreenshot + """A computer screenshot image used with the computer use tool.""" + + status: Literal["completed", "incomplete", "failed", "in_progress"] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + type: Literal["computer_call_output"] + """The type of the computer tool call output. Always `computer_call_output`.""" + + acknowledged_safety_checks: Optional[List[AcknowledgedSafetyCheck]] = None + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call_output_screenshot.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call_output_screenshot.py new file mode 100644 index 0000000..f6c4eec --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call_output_screenshot.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseComputerToolCallOutputScreenshot"] + + +class ResponseComputerToolCallOutputScreenshot(BaseModel): + """A computer screenshot image used with the computer use tool.""" + + type: Literal["computer_screenshot"] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + file_id: Optional[str] = None + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: Optional[str] = None + """The URL of the screenshot image.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call_output_screenshot_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call_output_screenshot_param.py new file mode 100644 index 0000000..e49f7de --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call_output_screenshot_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseComputerToolCallOutputScreenshotParam"] + + +class ResponseComputerToolCallOutputScreenshotParam(TypedDict, total=False): + """A computer screenshot image used with the computer use tool.""" + + type: Required[Literal["computer_screenshot"]] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + file_id: str + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: str + """The URL of the screenshot image.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call_param.py new file mode 100644 index 0000000..e07da69 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_computer_tool_call_param.py @@ -0,0 +1,257 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .computer_action_list_param import ComputerActionListParam + +__all__ = [ + "ResponseComputerToolCallParam", + "PendingSafetyCheck", + "Action", + "ActionClick", + "ActionDoubleClick", + "ActionDrag", + "ActionDragPath", + "ActionKeypress", + "ActionMove", + "ActionScreenshot", + "ActionScroll", + "ActionType", + "ActionWait", +] + + +class PendingSafetyCheck(TypedDict, total=False): + """A pending safety check for the computer call.""" + + id: Required[str] + """The ID of the pending safety check.""" + + code: Optional[str] + """The type of the pending safety check.""" + + message: Optional[str] + """Details about the pending safety check.""" + + +class ActionClick(TypedDict, total=False): + """A click action.""" + + button: Required[Literal["left", "right", "wheel", "back", "forward"]] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Required[Literal["click"]] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: Required[int] + """The x-coordinate where the click occurred.""" + + y: Required[int] + """The y-coordinate where the click occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while clicking.""" + + +class ActionDoubleClick(TypedDict, total=False): + """A double click action.""" + + keys: Required[Optional[SequenceNotStr[str]]] + """The keys being held while double-clicking.""" + + type: Required[Literal["double_click"]] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: Required[int] + """The x-coordinate where the double click occurred.""" + + y: Required[int] + """The y-coordinate where the double click occurred.""" + + +class ActionDragPath(TypedDict, total=False): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: Required[int] + """The x-coordinate.""" + + y: Required[int] + """The y-coordinate.""" + + +class ActionDrag(TypedDict, total=False): + """A drag action.""" + + path: Required[Iterable[ActionDragPath]] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Required[Literal["drag"]] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + keys: Optional[SequenceNotStr[str]] + """The keys being held while dragging the mouse.""" + + +class ActionKeypress(TypedDict, total=False): + """A collection of keypresses the model would like to perform.""" + + keys: Required[SequenceNotStr[str]] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Required[Literal["keypress"]] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class ActionMove(TypedDict, total=False): + """A mouse move action.""" + + type: Required[Literal["move"]] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: Required[int] + """The x-coordinate to move to.""" + + y: Required[int] + """The y-coordinate to move to.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while moving the mouse.""" + + +class ActionScreenshot(TypedDict, total=False): + """A screenshot action.""" + + type: Required[Literal["screenshot"]] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class ActionScroll(TypedDict, total=False): + """A scroll action.""" + + scroll_x: Required[int] + """The horizontal scroll distance.""" + + scroll_y: Required[int] + """The vertical scroll distance.""" + + type: Required[Literal["scroll"]] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: Required[int] + """The x-coordinate where the scroll occurred.""" + + y: Required[int] + """The y-coordinate where the scroll occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while scrolling.""" + + +class ActionType(TypedDict, total=False): + """An action to type in text.""" + + text: Required[str] + """The text to type.""" + + type: Required[Literal["type"]] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class ActionWait(TypedDict, total=False): + """A wait action.""" + + type: Required[Literal["wait"]] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +Action: TypeAlias = Union[ + ActionClick, + ActionDoubleClick, + ActionDrag, + ActionKeypress, + ActionMove, + ActionScreenshot, + ActionScroll, + ActionType, + ActionWait, +] + + +class ResponseComputerToolCallParam(TypedDict, total=False): + """A tool call to a computer use tool. + + See the + [computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information. + """ + + id: Required[str] + """The unique ID of the computer call.""" + + call_id: Required[str] + """An identifier used when responding to the tool call with output.""" + + pending_safety_checks: Required[Iterable[PendingSafetyCheck]] + """The pending safety checks for the computer call.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Required[Literal["computer_call"]] + """The type of the computer call. Always `computer_call`.""" + + action: Action + """A click action.""" + + actions: ComputerActionListParam + """Flattened batched actions for `computer_use`. + + Each action includes an `type` discriminator and action-specific fields. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_configuration_update_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_configuration_update_item.py new file mode 100644 index 0000000..5d14e60 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_configuration_update_item.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from ..shared.reasoning_effort import ReasoningEffort + +__all__ = ["ResponseConfigurationUpdateItem", "Reasoning"] + + +class Reasoning(BaseModel): + """The reasoning configuration applied by this update.""" + + effort: Optional[ReasoningEffort] = None + """ + The reasoning effort used for subsequent responses until another configuration + update replaces it. + """ + + +class ResponseConfigurationUpdateItem(BaseModel): + """ + A configuration update that applies to subsequent responses until it is + replaced by another configuration update. + """ + + id: str + """The unique ID of the configuration update item.""" + + type: Literal["configuration_update"] + """The item type. Always `configuration_update`.""" + + reasoning: Optional[Reasoning] = None + """The reasoning configuration applied by this update.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_configuration_update_item_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_configuration_update_item_param.py new file mode 100644 index 0000000..7a18c2a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_configuration_update_item_param.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from ..shared.reasoning_effort import ReasoningEffort + +__all__ = ["ResponseConfigurationUpdateItemParam", "Reasoning"] + + +class Reasoning(BaseModel): + """Updates to reasoning configuration. Only effort is supported.""" + + effort: Optional[ReasoningEffort] = None + """ + The reasoning effort to use for subsequent responses until another configuration + update replaces it. + """ + + +class ResponseConfigurationUpdateItemParam(BaseModel): + """An update to the conversation's response configuration. + + The configuration + remains in effect for subsequent responses until it is replaced by another + configuration update. + """ + + type: Literal["configuration_update"] + """The item type. Always `configuration_update`.""" + + id: Optional[str] = None + """The unique ID of the configuration update item.""" + + reasoning: Optional[Reasoning] = None + """Updates to reasoning configuration. Only effort is supported.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_configuration_update_item_param_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_configuration_update_item_param_param.py new file mode 100644 index 0000000..ed7e594 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_configuration_update_item_param_param.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ..shared.reasoning_effort import ReasoningEffort + +__all__ = ["ResponseConfigurationUpdateItemParamParam", "Reasoning"] + + +class Reasoning(TypedDict, total=False): + """Updates to reasoning configuration. Only effort is supported.""" + + effort: Optional[ReasoningEffort] + """ + The reasoning effort to use for subsequent responses until another configuration + update replaces it. + """ + + +class ResponseConfigurationUpdateItemParamParam(TypedDict, total=False): + """An update to the conversation's response configuration. + + The configuration + remains in effect for subsequent responses until it is replaced by another + configuration update. + """ + + type: Required[Literal["configuration_update"]] + """The item type. Always `configuration_update`.""" + + id: Optional[str] + """The unique ID of the configuration update item.""" + + reasoning: Reasoning + """Updates to reasoning configuration. Only effort is supported.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_container_reference.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_container_reference.py new file mode 100644 index 0000000..0f850b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_container_reference.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseContainerReference"] + + +class ResponseContainerReference(BaseModel): + """Represents a container created with /v1/containers.""" + + container_id: str + + type: Literal["container_reference"] + """The environment type. Always `container_reference`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_content_part_added_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_content_part_added_event.py new file mode 100644 index 0000000..1fc89b6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_content_part_added_event.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .response_output_text import ResponseOutputText +from .response_output_refusal import ResponseOutputRefusal + +__all__ = ["ResponseContentPartAddedEvent", "Part", "PartReasoningText"] + + +class PartReasoningText(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +Part: TypeAlias = Annotated[ + Union[ResponseOutputText, ResponseOutputRefusal, PartReasoningText], PropertyInfo(discriminator="type") +] + + +class ResponseContentPartAddedEvent(BaseModel): + """Emitted when a new content part is added.""" + + content_index: int + """The index of the content part that was added.""" + + item_id: str + """The ID of the output item that the content part was added to.""" + + output_index: int + """The index of the output item that the content part was added to.""" + + part: Part + """The content part that was added.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.content_part.added"] + """The type of the event. Always `response.content_part.added`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_content_part_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_content_part_done_event.py new file mode 100644 index 0000000..d817253 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_content_part_done_event.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .response_output_text import ResponseOutputText +from .response_output_refusal import ResponseOutputRefusal + +__all__ = ["ResponseContentPartDoneEvent", "Part", "PartReasoningText"] + + +class PartReasoningText(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +Part: TypeAlias = Annotated[ + Union[ResponseOutputText, ResponseOutputRefusal, PartReasoningText], PropertyInfo(discriminator="type") +] + + +class ResponseContentPartDoneEvent(BaseModel): + """Emitted when a content part is done.""" + + content_index: int + """The index of the content part that is done.""" + + item_id: str + """The ID of the output item that the content part was added to.""" + + output_index: int + """The index of the output item that the content part was added to.""" + + part: Part + """The content part that is done.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.content_part.done"] + """The type of the event. Always `response.content_part.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_conversation_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_conversation_param.py new file mode 100644 index 0000000..d66b0b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_conversation_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["ResponseConversationParam"] + + +class ResponseConversationParam(BaseModel): + """The conversation that this response belongs to.""" + + id: str + """The unique ID of the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_conversation_param_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_conversation_param_param.py new file mode 100644 index 0000000..ba43d8d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_conversation_param_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ResponseConversationParamParam"] + + +class ResponseConversationParamParam(TypedDict, total=False): + """The conversation that this response belongs to.""" + + id: Required[str] + """The unique ID of the conversation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_create_params.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_create_params.py new file mode 100644 index 0000000..2203730 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_create_params.py @@ -0,0 +1,502 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .tool_param import ToolParam +from .service_tier import ServiceTier +from .response_includable import ResponseIncludable +from .tool_choice_options import ToolChoiceOptions +from .response_input_param import ResponseInputParam +from .response_prompt_param import ResponsePromptParam +from .tool_choice_mcp_param import ToolChoiceMcpParam +from ..shared_params.metadata import Metadata +from .tool_choice_shell_param import ToolChoiceShellParam +from .tool_choice_types_param import ToolChoiceTypesParam +from ..shared_params.reasoning import Reasoning +from .tool_choice_custom_param import ToolChoiceCustomParam +from .tool_choice_allowed_param import ToolChoiceAllowedParam +from .response_text_config_param import ResponseTextConfigParam +from .tool_choice_function_param import ToolChoiceFunctionParam +from .tool_choice_apply_patch_param import ToolChoiceApplyPatchParam +from ..shared_params.responses_model import ResponsesModel +from .response_conversation_param_param import ResponseConversationParamParam + +__all__ = [ + "ResponseCreateParamsBase", + "AccessPrograms", + "ContextManagement", + "Conversation", + "Moderation", + "ModerationPolicy", + "ModerationPolicyInput", + "ModerationPolicyOutput", + "PromptCacheOptions", + "StreamOptions", + "ToolChoice", + "ToolChoiceSpecificProgrammaticToolCallingParam", + "ResponseCreateParamsNonStreaming", + "ResponseCreateParamsStreaming", +] + + +class ResponseCreateParamsBase(TypedDict, total=False): + access_programs: AccessPrograms + """Domain-specific access programs to use for this request.""" + + background: Optional[bool] + """ + Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + """ + + context_management: Optional[Iterable[ContextManagement]] + """Context management configuration for this request.""" + + conversation: Optional[Conversation] + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + include: Optional[List[ResponseIncludable]] + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + """ + + input: Union[str, ResponseInputParam] + """Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + """ + + instructions: Optional[str] + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + max_output_tokens: Optional[int] + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: ResponsesModel + """Model ID used to generate the response, like `gpt-6-astra`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + moderation: Optional[Moderation] + """Configuration for running moderation on the input and output of this response.""" + + parallel_tool_calls: Optional[bool] + """Whether to allow the model to run tool calls in parallel.""" + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[ResponsePromptParam] + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + prompt_cache_key: Optional[str] + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + """ + + prompt_cache_options: PromptCacheOptions + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] + """Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + """ + + reasoning: Optional[Reasoning] + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + safety_identifier: Optional[str] + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + service_tier: Optional[ServiceTier] + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + store: Optional[bool] + """Whether to store the generated model response for later retrieval via API. + + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + """ + + stream_options: Optional[StreamOptions] + """Options for streaming responses. Only set this when you set `stream: true`.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + text: ResponseTextConfigParam + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + tool_choice: ToolChoice + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: Iterable[ToolParam] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_logprobs: Optional[int] + """ + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + truncation: Optional[Literal["auto", "disabled"]] + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + user: str + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + +class AccessPrograms(TypedDict, total=False): + """Domain-specific access programs to use for this request.""" + + cyber: Literal["standard", "daybreak_blue", "daybreak_red"] + """The Cyber access program to use for this request. + + Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If + omitted, the API resolves the program from the model's Cyber tier and your + organization and project access, subject to model-specific eligibility + restrictions. By default, models without a Cyber tier use Standard. Blue-tier + models use Daybreak Blue when authorized; otherwise they fall back to Standard + unless the model requires Daybreak access. Red-tier models use Daybreak Red and + require authorization. Requests that require unavailable Daybreak access + return 403. An implicit Standard fallback is represented by null in the + response's access_programs field, rather than an explicit Standard selection. + """ + + +class ContextManagement(TypedDict, total=False): + type: Required[str] + """The context management entry type. Currently only 'compaction' is supported.""" + + compact_threshold: Optional[int] + """Token threshold at which compaction should be triggered for this entry.""" + + +Conversation: TypeAlias = Union[str, ResponseConversationParamParam] + + +class ModerationPolicyInput(TypedDict, total=False): + """The moderation policy for the response input.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicyOutput(TypedDict, total=False): + """The moderation policy for the response output.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicy(TypedDict, total=False): + """The policy to apply to moderated response input and output.""" + + input: Optional[ModerationPolicyInput] + """The moderation policy for the response input.""" + + output: Optional[ModerationPolicyOutput] + """The moderation policy for the response output.""" + + +class Moderation(TypedDict, total=False): + """Configuration for running moderation on the input and output of this response.""" + + model: Required[str] + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + policy: Optional[ModerationPolicy] + """The policy to apply to moderated response input and output.""" + + +class PromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details. + """ + + comparison_response_id: Optional[str] + """The ID of a response to compare when diagnosing prompt cache reuse. + + Supplying this field requests prompt cache diagnostics when the feature is + enabled. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + prewarm: bool + """Prepares the prompt cache without generating output. + + Defaults to `false`. When set to `true`, overrides the `generate` field to + `false`. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + + +class StreamOptions(TypedDict, total=False): + """Options for streaming responses. Only set this when you set `stream: true`.""" + + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + +class ToolChoiceSpecificProgrammaticToolCallingParam(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ToolChoice: TypeAlias = Union[ + ToolChoiceOptions, + ToolChoiceAllowedParam, + ToolChoiceTypesParam, + ToolChoiceFunctionParam, + ToolChoiceMcpParam, + ToolChoiceCustomParam, + ToolChoiceSpecificProgrammaticToolCallingParam, + ToolChoiceApplyPatchParam, + ToolChoiceShellParam, +] + + +class ResponseCreateParamsNonStreaming(ResponseCreateParamsBase, total=False): + stream: Optional[Literal[False]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + """ + + +class ResponseCreateParamsStreaming(ResponseCreateParamsBase): + stream: Required[Literal[True]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + """ + + +ResponseCreateParams = Union[ResponseCreateParamsNonStreaming, ResponseCreateParamsStreaming] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_created_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_created_event.py new file mode 100644 index 0000000..00c314a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_created_event.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .response import Response +from ..._models import BaseModel + +__all__ = ["ResponseCreatedEvent"] + + +class ResponseCreatedEvent(BaseModel): + """An event that is emitted when a response is created.""" + + response: Response + """The response that was created.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.created"] + """The type of the event. Always `response.created`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call.py new file mode 100644 index 0000000..9b6eb9e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["ResponseCustomToolCall", "Caller", "CallerDirect", "CallerProgram"] + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class ResponseCustomToolCall(BaseModel): + """A call to a custom tool created by the model.""" + + call_id: str + """An identifier used to map this custom tool call to a tool call output.""" + + input: str + """The input for the custom tool call generated by the model.""" + + name: str + """The name of the custom tool being called.""" + + type: Literal["custom_tool_call"] + """The type of the custom tool call. Always `custom_tool_call`.""" + + id: Optional[str] = None + """The unique ID of the custom tool call in the OpenAI platform.""" + + async_: Optional[bool] = FieldInfo(alias="async", default=None) + """Whether the custom tool call runs asynchronously.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + namespace: Optional[str] = None + """The namespace of the custom tool being called.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_input_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_input_delta_event.py new file mode 100644 index 0000000..7b03cf3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_input_delta_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCustomToolCallInputDeltaEvent"] + + +class ResponseCustomToolCallInputDeltaEvent(BaseModel): + """Event representing a delta (partial update) to the input of a custom tool call.""" + + delta: str + """The incremental input data (delta) for the custom tool call.""" + + item_id: str + """Unique identifier for the API item associated with this event.""" + + output_index: int + """The index of the output this delta applies to.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.custom_tool_call_input.delta"] + """The event type identifier.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_input_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_input_done_event.py new file mode 100644 index 0000000..ab4443b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_input_done_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCustomToolCallInputDoneEvent"] + + +class ResponseCustomToolCallInputDoneEvent(BaseModel): + """Event indicating that input for a custom tool call is complete.""" + + input: str + """The complete input data for the custom tool call.""" + + item_id: str + """Unique identifier for the API item associated with this event.""" + + output_index: int + """The index of the output this event applies to.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.custom_tool_call_input.done"] + """The event type identifier.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_item.py new file mode 100644 index 0000000..f933761 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_item.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .response_custom_tool_call import ResponseCustomToolCall + +__all__ = ["ResponseCustomToolCallItem"] + + +class ResponseCustomToolCallItem(ResponseCustomToolCall): + """A call to a custom tool created by the model.""" + + id: str # type: ignore + """The unique ID of the custom tool call item.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_output.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_output.py new file mode 100644 index 0000000..8023f47 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_output.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .response_input_file import ResponseInputFile +from .response_input_text import ResponseInputText +from .response_input_image import ResponseInputImage + +__all__ = ["ResponseCustomToolCallOutput", "OutputOutputContentList", "Caller", "CallerDirect", "CallerProgram"] + +OutputOutputContentList: TypeAlias = Annotated[ + Union[ResponseInputText, ResponseInputImage, ResponseInputFile], PropertyInfo(discriminator="type") +] + + +class CallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class ResponseCustomToolCallOutput(BaseModel): + """The output of a custom tool call from your code, being sent back to the model.""" + + call_id: str + """The call ID, used to map this custom tool call output to a custom tool call.""" + + output: Union[str, List[OutputOutputContentList]] + """ + The output from the custom tool call generated by your code. Can be a string or + an list of output content. + """ + + type: Literal["custom_tool_call_output"] + """The type of the custom tool call output. Always `custom_tool_call_output`.""" + + id: Optional[str] = None + """The unique ID of the custom tool call output in the OpenAI platform.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_output_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_output_item.py new file mode 100644 index 0000000..d54cada --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_output_item.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .response_custom_tool_call_output import ResponseCustomToolCallOutput + +__all__ = ["ResponseCustomToolCallOutputItem"] + + +class ResponseCustomToolCallOutputItem(ResponseCustomToolCallOutput): + """The output of a custom tool call from your code, being sent back to the model.""" + + id: str # type: ignore + """The unique ID of the custom tool call output item.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_output_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_output_param.py new file mode 100644 index 0000000..db5c895 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_output_param.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .response_input_file_param import ResponseInputFileParam +from .response_input_text_param import ResponseInputTextParam +from .response_input_image_param import ResponseInputImageParam + +__all__ = ["ResponseCustomToolCallOutputParam", "OutputOutputContentList", "Caller", "CallerDirect", "CallerProgram"] + +OutputOutputContentList: TypeAlias = Union[ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam] + + +class CallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class CallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +Caller: TypeAlias = Union[CallerDirect, CallerProgram] + + +class ResponseCustomToolCallOutputParam(TypedDict, total=False): + """The output of a custom tool call from your code, being sent back to the model.""" + + call_id: Required[str] + """The call ID, used to map this custom tool call output to a custom tool call.""" + + output: Required[Union[str, Iterable[OutputOutputContentList]]] + """ + The output from the custom tool call generated by your code. Can be a string or + an list of output content. + """ + + type: Required[Literal["custom_tool_call_output"]] + """The type of the custom tool call output. Always `custom_tool_call_output`.""" + + id: str + """The unique ID of the custom tool call output in the OpenAI platform.""" + + caller: Optional[Caller] + """The execution context that produced this tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_param.py new file mode 100644 index 0000000..359c756 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_custom_tool_call_param.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["ResponseCustomToolCallParam", "Caller", "CallerDirect", "CallerProgram"] + + +class CallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + + +class CallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + + +Caller: TypeAlias = Union[CallerDirect, CallerProgram] + + +_ResponseCustomToolCallParamReservedKeywords = TypedDict( + "_ResponseCustomToolCallParamReservedKeywords", + { + "async": bool, + }, + total=False, +) + + +class ResponseCustomToolCallParam(_ResponseCustomToolCallParamReservedKeywords, total=False): + """A call to a custom tool created by the model.""" + + call_id: Required[str] + """An identifier used to map this custom tool call to a tool call output.""" + + input: Required[str] + """The input for the custom tool call generated by the model.""" + + name: Required[str] + """The name of the custom tool being called.""" + + type: Required[Literal["custom_tool_call"]] + """The type of the custom tool call. Always `custom_tool_call`.""" + + id: str + """The unique ID of the custom tool call in the OpenAI platform.""" + + caller: Optional[Caller] + """The execution context that produced this tool call.""" + + namespace: str + """The namespace of the custom tool being called.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_error.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_error.py new file mode 100644 index 0000000..7979cb2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_error.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseError", "Misalignment", "MisalignmentSteer"] + + +class MisalignmentSteer(BaseModel): + """An optional public continuation instruction.""" + + message: str + """The public continuation instruction.""" + + +class Misalignment(BaseModel): + detailed_explanation: Optional[str] = None + """The public explanation for this block.""" + + error_type: Union[ + str, + Literal[ + "potentially_unintended_data_transfer", + "potentially_unintended_data_access", + "potentially_unintended_destructive_activity", + "other", + ], + None, + ] = None + """An optional classification; clients must accept additional values.""" + + steer: Optional[MisalignmentSteer] = None + """An optional public continuation instruction.""" + + +class ResponseError(BaseModel): + """An error object returned when the model fails to generate a Response.""" + + code: Literal[ + "server_error", + "rate_limit_exceeded", + "invalid_prompt", + "data_residency_mismatch", + "bio_policy", + "misalignment_policy_violation", + "vector_store_timeout", + "invalid_image", + "invalid_image_format", + "invalid_base64_image", + "invalid_image_url", + "image_too_large", + "image_too_small", + "image_parse_error", + "image_content_policy_violation", + "invalid_image_mode", + "image_file_too_large", + "unsupported_image_media_type", + "empty_image_file", + "failed_to_download_image", + "image_file_not_found", + ] + """The error code for the response.""" + + message: str + """A human-readable description of the error.""" + + misalignment: Optional[Misalignment] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_error_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_error_event.py new file mode 100644 index 0000000..23793b3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_error_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseErrorEvent"] + + +class ResponseErrorEvent(BaseModel): + """Emitted when an error occurs.""" + + code: Optional[str] = None + """The error code.""" + + message: str + """The error message.""" + + param: Optional[str] = None + """The error parameter.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["error"] + """The type of the event. Always `error`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_failed_event.py new file mode 100644 index 0000000..acc100d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_failed_event.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .response import Response +from ..._models import BaseModel + +__all__ = ["ResponseFailedEvent"] + + +class ResponseFailedEvent(BaseModel): + """An event that is emitted when a response fails.""" + + response: Response + """The response that failed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.failed"] + """The type of the event. Always `response.failed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_call_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_call_completed_event.py new file mode 100644 index 0000000..b1dc3a0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_call_completed_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFileSearchCallCompletedEvent"] + + +class ResponseFileSearchCallCompletedEvent(BaseModel): + """Emitted when a file search call is completed (results found).""" + + item_id: str + """The ID of the output item that the file search call is initiated.""" + + output_index: int + """The index of the output item that the file search call is initiated.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.file_search_call.completed"] + """The type of the event. Always `response.file_search_call.completed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_call_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_call_in_progress_event.py new file mode 100644 index 0000000..c6c16ca --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_call_in_progress_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFileSearchCallInProgressEvent"] + + +class ResponseFileSearchCallInProgressEvent(BaseModel): + """Emitted when a file search call is initiated.""" + + item_id: str + """The ID of the output item that the file search call is initiated.""" + + output_index: int + """The index of the output item that the file search call is initiated.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.file_search_call.in_progress"] + """The type of the event. Always `response.file_search_call.in_progress`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_call_searching_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_call_searching_event.py new file mode 100644 index 0000000..c47f37f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_call_searching_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFileSearchCallSearchingEvent"] + + +class ResponseFileSearchCallSearchingEvent(BaseModel): + """Emitted when a file search is currently searching.""" + + item_id: str + """The ID of the output item that the file search call is initiated.""" + + output_index: int + """The index of the output item that the file search call is searching.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.file_search_call.searching"] + """The type of the event. Always `response.file_search_call.searching`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_tool_call.py new file mode 100644 index 0000000..423d0eb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_tool_call.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFileSearchToolCall", "Result"] + + +class Result(BaseModel): + attributes: Optional[Dict[str, Union[str, float, bool]]] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + file_id: Optional[str] = None + """The unique ID of the file.""" + + filename: Optional[str] = None + """The name of the file.""" + + score: Optional[float] = None + """The relevance score of the file - a value between 0 and 1.""" + + text: Optional[str] = None + """The text that was retrieved from the file.""" + + +class ResponseFileSearchToolCall(BaseModel): + """The results of a file search tool call. + + See the + [file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information. + """ + + id: str + """The unique ID of the file search tool call.""" + + queries: List[str] + """The queries used to search for files.""" + + status: Literal["in_progress", "searching", "completed", "incomplete", "failed"] + """The status of the file search tool call. + + One of `in_progress`, `searching`, `incomplete` or `failed`, + """ + + type: Literal["file_search_call"] + """The type of the file search tool call. Always `file_search_call`.""" + + results: Optional[List[Result]] = None + """The results of the file search tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_tool_call_param.py new file mode 100644 index 0000000..0b6a1db --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_file_search_tool_call_param.py @@ -0,0 +1,59 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["ResponseFileSearchToolCallParam", "Result"] + + +class Result(TypedDict, total=False): + attributes: Optional[Dict[str, Union[str, float, bool]]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + file_id: str + """The unique ID of the file.""" + + filename: str + """The name of the file.""" + + score: float + """The relevance score of the file - a value between 0 and 1.""" + + text: str + """The text that was retrieved from the file.""" + + +class ResponseFileSearchToolCallParam(TypedDict, total=False): + """The results of a file search tool call. + + See the + [file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information. + """ + + id: Required[str] + """The unique ID of the file search tool call.""" + + queries: Required[SequenceNotStr[str]] + """The queries used to search for files.""" + + status: Required[Literal["in_progress", "searching", "completed", "incomplete", "failed"]] + """The status of the file search tool call. + + One of `in_progress`, `searching`, `incomplete` or `failed`, + """ + + type: Required[Literal["file_search_call"]] + """The type of the file search tool call. Always `file_search_call`.""" + + results: Optional[Iterable[Result]] + """The results of the file search tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_format_text_config.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_format_text_config.py new file mode 100644 index 0000000..25eb4b6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_format_text_config.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..shared.response_format_text import ResponseFormatText +from ..shared.response_format_json_object import ResponseFormatJSONObject +from .response_format_text_json_schema_config import ResponseFormatTextJSONSchemaConfig + +__all__ = ["ResponseFormatTextConfig"] + +ResponseFormatTextConfig: TypeAlias = Annotated[ + Union[ResponseFormatText, ResponseFormatTextJSONSchemaConfig, ResponseFormatJSONObject], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_format_text_config_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_format_text_config_param.py new file mode 100644 index 0000000..b6bbac0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_format_text_config_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from ..shared_params.response_format_text import ResponseFormatText +from ..shared_params.response_format_json_object import ResponseFormatJSONObject +from .response_format_text_json_schema_config_param import ResponseFormatTextJSONSchemaConfigParam + +__all__ = ["ResponseFormatTextConfigParam"] + +ResponseFormatTextConfigParam: TypeAlias = Union[ + ResponseFormatText, ResponseFormatTextJSONSchemaConfigParam, ResponseFormatJSONObject +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_format_text_json_schema_config.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_format_text_json_schema_config.py new file mode 100644 index 0000000..2bceec6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_format_text_json_schema_config.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["ResponseFormatTextJSONSchemaConfig"] + + +class ResponseFormatTextJSONSchemaConfig(BaseModel): + """JSON Schema response format. + + Used to generate structured JSON responses. + Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs). + """ + + name: str + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + schema_: Dict[str, object] = FieldInfo(alias="schema") + """ + The schema for the response format, described as a JSON Schema object. Learn how + to build JSON schemas [here](https://json-schema.org/). + """ + + type: Literal["json_schema"] + """The type of response format being defined. Always `json_schema`.""" + + description: Optional[str] = None + """ + A description of what the response format is for, used by the model to determine + how to respond in the format. + """ + + strict: Optional[bool] = None + """ + Whether to enable strict schema adherence when generating the output. If set to + true, the model will always follow the exact schema defined in the `schema` + field. Only a subset of JSON Schema is supported when `strict` is `true`. To + learn more, read the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_format_text_json_schema_config_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_format_text_json_schema_config_param.py new file mode 100644 index 0000000..6bfec36 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_format_text_json_schema_config_param.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseFormatTextJSONSchemaConfigParam"] + + +class ResponseFormatTextJSONSchemaConfigParam(TypedDict, total=False): + """JSON Schema response format. + + Used to generate structured JSON responses. + Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs). + """ + + name: Required[str] + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + schema: Required[Dict[str, object]] + """ + The schema for the response format, described as a JSON Schema object. Learn how + to build JSON schemas [here](https://json-schema.org/). + """ + + type: Required[Literal["json_schema"]] + """The type of response format being defined. Always `json_schema`.""" + + description: str + """ + A description of what the response format is for, used by the model to determine + how to respond in the format. + """ + + strict: Optional[bool] + """ + Whether to enable strict schema adherence when generating the output. If set to + true, the model will always follow the exact schema defined in the `schema` + field. Only a subset of JSON Schema is supported when `strict` is `true`. To + learn more, read the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_arguments_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_arguments_delta_event.py new file mode 100644 index 0000000..e088a14 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_arguments_delta_event.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFunctionCallArgumentsDeltaEvent"] + + +class ResponseFunctionCallArgumentsDeltaEvent(BaseModel): + """Emitted when there is a partial function-call arguments delta.""" + + delta: str + """The function-call arguments delta that is added.""" + + item_id: str + """The ID of the output item that the function-call arguments delta is added to.""" + + output_index: int + """ + The index of the output item that the function-call arguments delta is added to. + """ + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.function_call_arguments.delta"] + """The type of the event. Always `response.function_call_arguments.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_arguments_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_arguments_done_event.py new file mode 100644 index 0000000..793edac --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_arguments_done_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFunctionCallArgumentsDoneEvent"] + + +class ResponseFunctionCallArgumentsDoneEvent(BaseModel): + """Emitted when function-call arguments are finalized.""" + + arguments: str + """The function-call arguments.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.function_call_arguments.done"] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_output_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_output_item.py new file mode 100644 index 0000000..63f954c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_output_item.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .response_input_file_content import ResponseInputFileContent +from .response_input_text_content import ResponseInputTextContent +from .response_input_image_content import ResponseInputImageContent + +__all__ = ["ResponseFunctionCallOutputItem"] + +ResponseFunctionCallOutputItem: TypeAlias = Annotated[ + Union[ResponseInputTextContent, ResponseInputImageContent, ResponseInputFileContent], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_output_item_list.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_output_item_list.py new file mode 100644 index 0000000..a29cdb6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_output_item_list.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .response_function_call_output_item import ResponseFunctionCallOutputItem + +__all__ = ["ResponseFunctionCallOutputItemList"] + +ResponseFunctionCallOutputItemList: TypeAlias = List[ResponseFunctionCallOutputItem] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_output_item_list_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_output_item_list_param.py new file mode 100644 index 0000000..162f064 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_output_item_list_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import TypeAlias + +from .response_input_file_content_param import ResponseInputFileContentParam +from .response_input_text_content_param import ResponseInputTextContentParam +from .response_input_image_content_param import ResponseInputImageContentParam + +__all__ = ["ResponseFunctionCallOutputItemListParam", "ResponseFunctionCallOutputItemParam"] + +ResponseFunctionCallOutputItemParam: TypeAlias = Union[ + ResponseInputTextContentParam, ResponseInputImageContentParam, ResponseInputFileContentParam +] + +ResponseFunctionCallOutputItemListParam: TypeAlias = List[ResponseFunctionCallOutputItemParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_output_item_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_output_item_param.py new file mode 100644 index 0000000..63414fe --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_call_output_item_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .response_input_file_content_param import ResponseInputFileContentParam +from .response_input_text_content_param import ResponseInputTextContentParam +from .response_input_image_content_param import ResponseInputImageContentParam + +__all__ = ["ResponseFunctionCallOutputItemParam"] + +ResponseFunctionCallOutputItemParam: TypeAlias = Union[ + ResponseInputTextContentParam, ResponseInputImageContentParam, ResponseInputFileContentParam +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_shell_call_output_content.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_shell_call_output_content.py new file mode 100644 index 0000000..34dfc4b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_shell_call_output_content.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["ResponseFunctionShellCallOutputContent", "Outcome", "OutcomeTimeout", "OutcomeExit"] + + +class OutcomeTimeout(BaseModel): + """Indicates that the shell call exceeded its configured time limit.""" + + type: Literal["timeout"] + """The outcome type. Always `timeout`.""" + + +class OutcomeExit(BaseModel): + """Indicates that the shell commands finished and returned an exit code.""" + + exit_code: int + """The exit code returned by the shell process.""" + + type: Literal["exit"] + """The outcome type. Always `exit`.""" + + +Outcome: TypeAlias = Annotated[Union[OutcomeTimeout, OutcomeExit], PropertyInfo(discriminator="type")] + + +class ResponseFunctionShellCallOutputContent(BaseModel): + """Captured stdout and stderr for a portion of a shell tool call output.""" + + outcome: Outcome + """The exit or timeout outcome associated with this shell call.""" + + stderr: str + """Captured stderr output for the shell call.""" + + stdout: str + """Captured stdout output for the shell call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_shell_call_output_content_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_shell_call_output_content_param.py new file mode 100644 index 0000000..6029b59 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_shell_call_output_content_param.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["ResponseFunctionShellCallOutputContentParam", "Outcome", "OutcomeTimeout", "OutcomeExit"] + + +class OutcomeTimeout(TypedDict, total=False): + """Indicates that the shell call exceeded its configured time limit.""" + + type: Required[Literal["timeout"]] + """The outcome type. Always `timeout`.""" + + +class OutcomeExit(TypedDict, total=False): + """Indicates that the shell commands finished and returned an exit code.""" + + exit_code: Required[int] + """The exit code returned by the shell process.""" + + type: Required[Literal["exit"]] + """The outcome type. Always `exit`.""" + + +Outcome: TypeAlias = Union[OutcomeTimeout, OutcomeExit] + + +class ResponseFunctionShellCallOutputContentParam(TypedDict, total=False): + """Captured stdout and stderr for a portion of a shell tool call output.""" + + outcome: Required[Outcome] + """The exit or timeout outcome associated with this shell call.""" + + stderr: Required[str] + """Captured stderr output for the shell call.""" + + stdout: Required[str] + """Captured stdout output for the shell call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_shell_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_shell_tool_call.py new file mode 100644 index 0000000..eb8d9fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_shell_tool_call.py @@ -0,0 +1,76 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .response_local_environment import ResponseLocalEnvironment +from .response_container_reference import ResponseContainerReference + +__all__ = ["ResponseFunctionShellToolCall", "Action", "Environment", "Caller", "CallerDirect", "CallerProgram"] + + +class Action(BaseModel): + """The shell commands and limits that describe how to run the tool call.""" + + commands: List[str] + + max_output_length: Optional[int] = None + """Optional maximum number of characters to return from each command.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the commands.""" + + +Environment: TypeAlias = Annotated[ + Union[ResponseLocalEnvironment, ResponseContainerReference, None], PropertyInfo(discriminator="type") +] + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class ResponseFunctionShellToolCall(BaseModel): + """A tool call that executes one or more shell commands in a managed environment.""" + + id: str + """The unique ID of the shell tool call. + + Populated when this item is returned via API. + """ + + action: Action + """The shell commands and limits that describe how to run the tool call.""" + + call_id: str + """The unique ID of the shell tool call generated by the model.""" + + environment: Optional[Environment] = None + """Represents the use of a local environment to perform shell actions.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + type: Literal["shell_call"] + """The type of the item. Always `shell_call`.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The ID of the entity that created this tool call.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_shell_tool_call_output.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_shell_tool_call_output.py new file mode 100644 index 0000000..ddab97c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_shell_tool_call_output.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "ResponseFunctionShellToolCallOutput", + "Output", + "OutputOutcome", + "OutputOutcomeTimeout", + "OutputOutcomeExit", + "Caller", + "CallerDirect", + "CallerProgram", +] + + +class OutputOutcomeTimeout(BaseModel): + """Indicates that the shell call exceeded its configured time limit.""" + + type: Literal["timeout"] + """The outcome type. Always `timeout`.""" + + +class OutputOutcomeExit(BaseModel): + """Indicates that the shell commands finished and returned an exit code.""" + + exit_code: int + """Exit code from the shell process.""" + + type: Literal["exit"] + """The outcome type. Always `exit`.""" + + +OutputOutcome: TypeAlias = Annotated[Union[OutputOutcomeTimeout, OutputOutcomeExit], PropertyInfo(discriminator="type")] + + +class Output(BaseModel): + """The content of a shell tool call output that was emitted.""" + + outcome: OutputOutcome + """ + Represents either an exit outcome (with an exit code) or a timeout outcome for a + shell call output chunk. + """ + + stderr: str + """The standard error output that was captured.""" + + stdout: str + """The standard output that was captured.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class ResponseFunctionShellToolCallOutput(BaseModel): + """The output of a shell tool call that was emitted.""" + + id: str + """The unique ID of the shell call output. + + Populated when this item is returned via API. + """ + + call_id: str + """The unique ID of the shell tool call generated by the model.""" + + max_output_length: Optional[int] = None + """The maximum length of the shell command output. + + This is generated by the model and should be passed back with the raw output. + """ + + output: List[Output] + """An array of shell call output contents""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the shell call output. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + type: Literal["shell_call_output"] + """The type of the shell call output. Always `shell_call_output`.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_tool_call.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_tool_call.py new file mode 100644 index 0000000..c51f680 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_tool_call.py @@ -0,0 +1,64 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["ResponseFunctionToolCall", "Caller", "CallerDirect", "CallerProgram"] + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class ResponseFunctionToolCall(BaseModel): + """A tool call to run a function. + + See the + [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information. + """ + + arguments: str + """A JSON string of the arguments to pass to the function.""" + + call_id: str + """The unique ID of the function tool call generated by the model.""" + + name: str + """The name of the function to run.""" + + type: Literal["function_call"] + """The type of the function tool call. Always `function_call`.""" + + id: Optional[str] = None + """The unique ID of the function tool call.""" + + async_: Optional[bool] = FieldInfo(alias="async", default=None) + """Whether the function tool call runs asynchronously.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + namespace: Optional[str] = None + """The namespace of the function to run.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_tool_call_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_tool_call_item.py new file mode 100644 index 0000000..fa01376 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_tool_call_item.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .response_function_tool_call import ResponseFunctionToolCall + +__all__ = ["ResponseFunctionToolCallItem"] + + +class ResponseFunctionToolCallItem(ResponseFunctionToolCall): + """A tool call to run a function. + + See the + [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information. + """ + + id: str # type: ignore + """The unique ID of the function tool call.""" + + status: Literal["in_progress", "completed", "incomplete"] # type: ignore + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_tool_call_output_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_tool_call_output_item.py new file mode 100644 index 0000000..0c1c785 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_tool_call_output_item.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .response_input_file import ResponseInputFile +from .response_input_text import ResponseInputText +from .response_input_image import ResponseInputImage + +__all__ = ["ResponseFunctionToolCallOutputItem", "OutputOutputContentList", "Caller", "CallerDirect", "CallerProgram"] + +OutputOutputContentList: TypeAlias = Annotated[ + Union[ResponseInputText, ResponseInputImage, ResponseInputFile], PropertyInfo(discriminator="type") +] + + +class CallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class ResponseFunctionToolCallOutputItem(BaseModel): + id: str + """The unique ID of the function call tool output.""" + + output: Union[str, List[OutputOutputContentList]] + """ + The output from the function call generated by your code. Can be a string or an + list of output content. + """ + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["function_call_output"] + """The type of the function tool call output. Always `function_call_output`.""" + + call_id: Optional[str] = None + """The unique ID of the function tool call generated by the model.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" + + name: Optional[str] = None + """The name of the tool that produced the output.""" + + namespace: Optional[str] = None + """The namespace of the tool that produced the output.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_tool_call_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_tool_call_param.py new file mode 100644 index 0000000..096a639 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_tool_call_param.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["ResponseFunctionToolCallParam", "Caller", "CallerDirect", "CallerProgram"] + + +class CallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + + +class CallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + + +Caller: TypeAlias = Union[CallerDirect, CallerProgram] + + +_ResponseFunctionToolCallParamReservedKeywords = TypedDict( + "_ResponseFunctionToolCallParamReservedKeywords", + { + "async": bool, + }, + total=False, +) + + +class ResponseFunctionToolCallParam(_ResponseFunctionToolCallParamReservedKeywords, total=False): + """A tool call to run a function. + + See the + [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information. + """ + + arguments: Required[str] + """A JSON string of the arguments to pass to the function.""" + + call_id: Required[str] + """The unique ID of the function tool call generated by the model.""" + + name: Required[str] + """The name of the function to run.""" + + type: Required[Literal["function_call"]] + """The type of the function tool call. Always `function_call`.""" + + id: str + """The unique ID of the function tool call.""" + + caller: Optional[Caller] + """The execution context that produced this tool call.""" + + namespace: str + """The namespace of the function to run.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_web_search.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_web_search.py new file mode 100644 index 0000000..a758172 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_web_search.py @@ -0,0 +1,91 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "ResponseFunctionWebSearch", + "Action", + "ActionSearch", + "ActionSearchSource", + "ActionOpenPage", + "ActionFind", +] + + +class ActionSearchSource(BaseModel): + """A source used in the search.""" + + type: Literal["url"] + """The type of source. Always `url`.""" + + url: str + """The URL of the source.""" + + +class ActionSearch(BaseModel): + """Action type "search" - Performs a web search query.""" + + type: Literal["search"] + """The action type.""" + + queries: Optional[List[str]] = None + """The search queries.""" + + query: Optional[str] = None + """The search query.""" + + sources: Optional[List[ActionSearchSource]] = None + """The sources used in the search.""" + + +class ActionOpenPage(BaseModel): + """Action type "open_page" - Opens a specific URL from search results.""" + + type: Literal["open_page"] + """The action type.""" + + url: Optional[str] = None + """The URL opened by the model.""" + + +class ActionFind(BaseModel): + """Action type "find_in_page": Searches for a pattern within a loaded page.""" + + pattern: str + """The pattern or text to search for within the page.""" + + type: Literal["find_in_page"] + """The action type.""" + + url: str + """The URL of the page searched for the pattern.""" + + +Action: TypeAlias = Annotated[Union[ActionSearch, ActionOpenPage, ActionFind], PropertyInfo(discriminator="type")] + + +class ResponseFunctionWebSearch(BaseModel): + """The results of a web search tool call. + + See the + [web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information. + """ + + id: str + """The unique ID of the web search tool call.""" + + action: Action + """ + An object describing the specific action taken in this web search call. Includes + details on how the model used the web (search, open_page, find_in_page). + """ + + status: Literal["in_progress", "searching", "completed", "failed", "incomplete"] + """The status of the web search tool call.""" + + type: Literal["web_search_call"] + """The type of the web search tool call. Always `web_search_call`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_function_web_search_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_web_search_param.py new file mode 100644 index 0000000..419b87c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_function_web_search_param.py @@ -0,0 +1,92 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "ResponseFunctionWebSearchParam", + "Action", + "ActionSearch", + "ActionSearchSource", + "ActionOpenPage", + "ActionFind", +] + + +class ActionSearchSource(TypedDict, total=False): + """A source used in the search.""" + + type: Required[Literal["url"]] + """The type of source. Always `url`.""" + + url: Required[str] + """The URL of the source.""" + + +class ActionSearch(TypedDict, total=False): + """Action type "search" - Performs a web search query.""" + + type: Required[Literal["search"]] + """The action type.""" + + queries: SequenceNotStr[str] + """The search queries.""" + + query: str + """The search query.""" + + sources: Iterable[ActionSearchSource] + """The sources used in the search.""" + + +class ActionOpenPage(TypedDict, total=False): + """Action type "open_page" - Opens a specific URL from search results.""" + + type: Required[Literal["open_page"]] + """The action type.""" + + url: Optional[str] + """The URL opened by the model.""" + + +class ActionFind(TypedDict, total=False): + """Action type "find_in_page": Searches for a pattern within a loaded page.""" + + pattern: Required[str] + """The pattern or text to search for within the page.""" + + type: Required[Literal["find_in_page"]] + """The action type.""" + + url: Required[str] + """The URL of the page searched for the pattern.""" + + +Action: TypeAlias = Union[ActionSearch, ActionOpenPage, ActionFind] + + +class ResponseFunctionWebSearchParam(TypedDict, total=False): + """The results of a web search tool call. + + See the + [web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information. + """ + + id: Required[str] + """The unique ID of the web search tool call.""" + + action: Required[Action] + """ + An object describing the specific action taken in this web search call. Includes + details on how the model used the web (search, open_page, find_in_page). + """ + + status: Required[Literal["in_progress", "searching", "completed", "failed", "incomplete"]] + """The status of the web search tool call.""" + + type: Required[Literal["web_search_call"]] + """The type of the web search tool call. Always `web_search_call`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_image_gen_call_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_image_gen_call_completed_event.py new file mode 100644 index 0000000..0bceb7d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_image_gen_call_completed_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseImageGenCallCompletedEvent"] + + +class ResponseImageGenCallCompletedEvent(BaseModel): + """ + Emitted when an image generation tool call has completed and the final image is available. + """ + + item_id: str + """The unique identifier of the image generation item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.image_generation_call.completed"] + """The type of the event. Always 'response.image_generation_call.completed'.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_image_gen_call_generating_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_image_gen_call_generating_event.py new file mode 100644 index 0000000..134a3b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_image_gen_call_generating_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseImageGenCallGeneratingEvent"] + + +class ResponseImageGenCallGeneratingEvent(BaseModel): + """ + Emitted when an image generation tool call is actively generating an image (intermediate state). + """ + + item_id: str + """The unique identifier of the image generation item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of the image generation item being processed.""" + + type: Literal["response.image_generation_call.generating"] + """The type of the event. Always 'response.image_generation_call.generating'.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_image_gen_call_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_image_gen_call_in_progress_event.py new file mode 100644 index 0000000..7aadf16 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_image_gen_call_in_progress_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseImageGenCallInProgressEvent"] + + +class ResponseImageGenCallInProgressEvent(BaseModel): + """Emitted when an image generation tool call is in progress.""" + + item_id: str + """The unique identifier of the image generation item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of the image generation item being processed.""" + + type: Literal["response.image_generation_call.in_progress"] + """The type of the event. Always 'response.image_generation_call.in_progress'.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_image_gen_call_partial_image_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_image_gen_call_partial_image_event.py new file mode 100644 index 0000000..fbcc923 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_image_gen_call_partial_image_event.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseImageGenCallPartialImageEvent"] + + +class ResponseImageGenCallPartialImageEvent(BaseModel): + """Emitted when a partial image is available during image generation streaming.""" + + item_id: str + """The unique identifier of the image generation item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + partial_image_b64: str + """Base64-encoded partial image data, suitable for rendering as an image.""" + + partial_image_index: int + """ + 0-based index for the partial image (backend is 1-based, but this is 0-based for + the user). + """ + + sequence_number: int + """The sequence number of the image generation item being processed.""" + + type: Literal["response.image_generation_call.partial_image"] + """The type of the event. Always 'response.image_generation_call.partial_image'.""" + + background: Optional[str] = None + """The background setting that was used.""" + + output_format: Optional[str] = None + """The output format that was used.""" + + quality: Optional[str] = None + """The image quality that was used.""" + + size: Optional[str] = None + """The image size that was used.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_in_progress_event.py new file mode 100644 index 0000000..b4fc8d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_in_progress_event.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .response import Response +from ..._models import BaseModel + +__all__ = ["ResponseInProgressEvent"] + + +class ResponseInProgressEvent(BaseModel): + """Emitted when the response is in progress.""" + + response: Response + """The response that is in progress.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.in_progress"] + """The type of the event. Always `response.in_progress`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_includable.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_includable.py new file mode 100644 index 0000000..6d9bb68 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_includable.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ResponseIncludable"] + +ResponseIncludable: TypeAlias = Literal[ + "file_search_call.results", + "web_search_call.results", + "web_search_call.action.sources", + "message.input_image.image_url", + "computer_call_output.output.image_url", + "code_interpreter_call.outputs", + "reasoning.encrypted_content", + "message.output_text.logprobs", +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_incomplete_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_incomplete_event.py new file mode 100644 index 0000000..1556d82 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_incomplete_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .response import Response +from ..._models import BaseModel + +__all__ = ["ResponseIncompleteEvent"] + + +class ResponseIncompleteEvent(BaseModel): + """An event that is emitted when a response finishes as incomplete. + + Over WebSocket, steering can finish a response with + `response.incomplete_details.reason` set to `steered`, followed automatically + by a successor `response.created` that commits the queued steering input. + """ + + response: Response + """The response that was incomplete.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.incomplete"] + """The type of the event. Always `response.incomplete`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input.py new file mode 100644 index 0000000..5f15457 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .response_input_item import ResponseInputItem + +__all__ = ["ResponseInput"] + +ResponseInput: TypeAlias = List[ResponseInputItem] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_audio.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_audio.py new file mode 100644 index 0000000..07fc822 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_audio.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseInputAudio", "InputAudio"] + + +class InputAudio(BaseModel): + data: str + """Base64-encoded audio data.""" + + format: Literal["mp3", "wav"] + """The format of the audio data. Currently supported formats are `mp3` and `wav`.""" + + +class ResponseInputAudio(BaseModel): + """An audio input to the model.""" + + input_audio: InputAudio + + type: Literal["input_audio"] + """The type of the input item. Always `input_audio`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_audio_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_audio_param.py new file mode 100644 index 0000000..088772b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_audio_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseInputAudioParam", "InputAudio"] + + +class InputAudio(TypedDict, total=False): + data: Required[str] + """Base64-encoded audio data.""" + + format: Required[Literal["mp3", "wav"]] + """The format of the audio data. Currently supported formats are `mp3` and `wav`.""" + + +class ResponseInputAudioParam(TypedDict, total=False): + """An audio input to the model.""" + + input_audio: Required[InputAudio] + + type: Required[Literal["input_audio"]] + """The type of the input item. Always `input_audio`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_content.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_content.py new file mode 100644 index 0000000..d2d6498 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_content.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .response_input_file import ResponseInputFile +from .response_input_text import ResponseInputText +from .response_input_image import ResponseInputImage + +__all__ = ["ResponseInputContent"] + +ResponseInputContent: TypeAlias = Annotated[ + Union[ResponseInputText, ResponseInputImage, ResponseInputFile], PropertyInfo(discriminator="type") +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_content_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_content_param.py new file mode 100644 index 0000000..b565f50 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_content_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .response_input_file_param import ResponseInputFileParam +from .response_input_text_param import ResponseInputTextParam +from .response_input_image_param import ResponseInputImageParam + +__all__ = ["ResponseInputContentParam"] + +ResponseInputContentParam: TypeAlias = Union[ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_file.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_file.py new file mode 100644 index 0000000..6337f4e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_file.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseInputFile", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class ResponseInputFile(BaseModel): + """A file input to the model.""" + + type: Literal["input_file"] + """The type of the input item. Always `input_file`.""" + + detail: Optional[Literal["auto", "low", "high"]] = None + """The detail level of the file to be sent to the model. + + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. + """ + + file_data: Optional[str] = None + """The content of the file to be sent to the model.""" + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + file_url: Optional[str] = None + """The URL of the file to be sent to the model.""" + + filename: Optional[str] = None + """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_file_content.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_file_content.py new file mode 100644 index 0000000..685d751 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_file_content.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseInputFileContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class ResponseInputFileContent(BaseModel): + """A file input to the model.""" + + type: Literal["input_file"] + """The type of the input item. Always `input_file`.""" + + detail: Optional[Literal["auto", "low", "high"]] = None + """The detail level of the file to be sent to the model. + + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. + """ + + file_data: Optional[str] = None + """The base64-encoded data of the file to be sent to the model.""" + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + file_url: Optional[str] = None + """The URL of the file to be sent to the model.""" + + filename: Optional[str] = None + """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_file_content_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_file_content_param.py new file mode 100644 index 0000000..07c7f9f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_file_content_param.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseInputFileContentParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class ResponseInputFileContentParam(TypedDict, total=False): + """A file input to the model.""" + + type: Required[Literal["input_file"]] + """The type of the input item. Always `input_file`.""" + + detail: Literal["auto", "low", "high"] + """The detail level of the file to be sent to the model. + + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. + """ + + file_data: Optional[str] + """The base64-encoded data of the file to be sent to the model.""" + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + file_url: Optional[str] + """The URL of the file to be sent to the model.""" + + filename: Optional[str] + """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_file_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_file_param.py new file mode 100644 index 0000000..a78fe28 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_file_param.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseInputFileParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class ResponseInputFileParam(TypedDict, total=False): + """A file input to the model.""" + + type: Required[Literal["input_file"]] + """The type of the input item. Always `input_file`.""" + + detail: Literal["auto", "low", "high"] + """The detail level of the file to be sent to the model. + + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. + """ + + file_data: str + """The content of the file to be sent to the model.""" + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + file_url: str + """The URL of the file to be sent to the model.""" + + filename: str + """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_image.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_image.py new file mode 100644 index 0000000..8efb56f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_image.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .image_detail import ImageDetail + +__all__ = ["ResponseInputImage", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class ResponseInputImage(BaseModel): + """An image input to the model. + + Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision). + """ + + detail: ImageDetail + """The detail level of the image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + type: Literal["input_image"] + """The type of the input item. Always `input_image`.""" + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] = None + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_image_content.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_image_content.py new file mode 100644 index 0000000..999a9df --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_image_content.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .image_detail import ImageDetail + +__all__ = ["ResponseInputImageContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class ResponseInputImageContent(BaseModel): + """An image input to the model. + + Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision) + """ + + type: Literal["input_image"] + """The type of the input item. Always `input_image`.""" + + detail: Optional[ImageDetail] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] = None + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_image_content_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_image_content_param.py new file mode 100644 index 0000000..a045fc5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_image_content_param.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from .image_detail import ImageDetail + +__all__ = ["ResponseInputImageContentParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class ResponseInputImageContentParam(TypedDict, total=False): + """An image input to the model. + + Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision) + """ + + type: Required[Literal["input_image"]] + """The type of the input item. Always `input_image`.""" + + detail: Optional[ImageDetail] + """The detail level of the image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_image_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_image_param.py new file mode 100644 index 0000000..dc99692 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_image_param.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from .image_detail import ImageDetail + +__all__ = ["ResponseInputImageParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class ResponseInputImageParam(TypedDict, total=False): + """An image input to the model. + + Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision). + """ + + detail: Required[ImageDetail] + """The detail level of the image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + type: Required[Literal["input_image"]] + """The type of the input item. Always `input_image`.""" + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_item.py new file mode 100644 index 0000000..e346822 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_item.py @@ -0,0 +1,785 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from .tool import Tool +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .local_environment import LocalEnvironment +from .easy_input_message import EasyInputMessage +from .container_reference import ContainerReference +from .mcp_tool_call_error import McpToolCallError +from .response_output_message import ResponseOutputMessage +from .response_reasoning_item import ResponseReasoningItem +from .response_custom_tool_call import ResponseCustomToolCall +from .response_computer_tool_call import ResponseComputerToolCall +from .response_function_tool_call import ResponseFunctionToolCall +from .response_function_web_search import ResponseFunctionWebSearch +from .response_compaction_item_param import ResponseCompactionItemParam +from .response_file_search_tool_call import ResponseFileSearchToolCall +from .response_custom_tool_call_output import ResponseCustomToolCallOutput +from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall +from .response_input_message_content_list import ResponseInputMessageContentList +from .response_tool_search_output_item_param import ResponseToolSearchOutputItemParam +from .response_function_call_output_item_list import ResponseFunctionCallOutputItemList +from .response_configuration_update_item_param import ResponseConfigurationUpdateItemParam +from .response_function_shell_call_output_content import ResponseFunctionShellCallOutputContent +from .response_computer_tool_call_output_screenshot import ResponseComputerToolCallOutputScreenshot + +__all__ = [ + "ResponseInputItem", + "Message", + "ComputerCallOutput", + "ComputerCallOutputAcknowledgedSafetyCheck", + "FunctionCallOutput", + "FunctionCallOutputCaller", + "FunctionCallOutputCallerDirect", + "FunctionCallOutputCallerProgram", + "ToolSearchCall", + "AdditionalTools", + "ImageGenerationCall", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallOutput", + "ShellCall", + "ShellCallAction", + "ShellCallCaller", + "ShellCallCallerDirect", + "ShellCallCallerProgram", + "ShellCallEnvironment", + "ShellCallOutput", + "ShellCallOutputCaller", + "ShellCallOutputCallerDirect", + "ShellCallOutputCallerProgram", + "ApplyPatchCall", + "ApplyPatchCallOperation", + "ApplyPatchCallOperationCreateFile", + "ApplyPatchCallOperationDeleteFile", + "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallCaller", + "ApplyPatchCallCallerDirect", + "ApplyPatchCallCallerProgram", + "ApplyPatchCallOutput", + "ApplyPatchCallOutputCaller", + "ApplyPatchCallOutputCallerDirect", + "ApplyPatchCallOutputCallerProgram", + "McpListTools", + "McpListToolsTool", + "McpApprovalRequest", + "McpApprovalResponse", + "McpCall", + "CompactionTrigger", + "ItemReference", + "Program", + "ProgramOutput", +] + + +class Message(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. + """ + + content: ResponseInputMessageContentList + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Literal["user", "system", "developer"] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always set to `message`.""" + + +class ComputerCallOutputAcknowledgedSafetyCheck(BaseModel): + """A pending safety check for the computer call.""" + + id: str + """The ID of the pending safety check.""" + + code: Optional[str] = None + """The type of the pending safety check.""" + + message: Optional[str] = None + """Details about the pending safety check.""" + + +class ComputerCallOutput(BaseModel): + """The output of a computer tool call.""" + + call_id: str + """The ID of the computer tool call that produced the output.""" + + output: ResponseComputerToolCallOutputScreenshot + """A computer screenshot image used with the computer use tool.""" + + type: Literal["computer_call_output"] + """The type of the computer tool call output. Always `computer_call_output`.""" + + id: Optional[str] = None + """The ID of the computer tool call output.""" + + acknowledged_safety_checks: Optional[List[ComputerCallOutputAcknowledgedSafetyCheck]] = None + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + +class FunctionCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class FunctionCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +FunctionCallOutputCaller: TypeAlias = Annotated[ + Union[FunctionCallOutputCallerDirect, FunctionCallOutputCallerProgram, None], PropertyInfo(discriminator="type") +] + + +class FunctionCallOutput(BaseModel): + """The output of a function tool call.""" + + output: Union[str, ResponseFunctionCallOutputItemList] + """Text, image, or file output of the function tool call.""" + + type: Literal["function_call_output"] + """The type of the function tool call output. Always `function_call_output`.""" + + id: Optional[str] = None + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + call_id: Optional[str] = None + """The unique ID of the function tool call generated by the model.""" + + caller: Optional[FunctionCallOutputCaller] = None + """The execution context that produced this tool call.""" + + name: Optional[str] = None + """The name of the tool that produced the output.""" + + namespace: Optional[str] = None + """The namespace of the tool that produced the output.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +class ToolSearchCall(BaseModel): + arguments: object + """The arguments supplied to the tool search call.""" + + type: Literal["tool_search_call"] + """The item type. Always `tool_search_call`.""" + + id: Optional[str] = None + """The unique ID of this tool search call.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Optional[Literal["server", "client"]] = None + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the tool search call.""" + + +class AdditionalTools(BaseModel): + role: Literal["developer"] + """The role that provided the additional tools. Only `developer` is supported.""" + + tools: List[Tool] + """A list of additional tools made available at this item.""" + + type: Literal["additional_tools"] + """The item type. Always `additional_tools`.""" + + id: Optional[str] = None + """The unique ID of this additional tools item.""" + + +class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + + id: str + """The unique ID of the image generation call.""" + + result: Optional[str] = None + """The generated image encoded in base64.""" + + status: Literal["in_progress", "completed", "generating", "failed"] + """The status of the image generation call.""" + + type: Literal["image_generation_call"] + """The type of the image generation call. Always `image_generation_call`.""" + + action: Optional[Literal["generate", "edit", "auto"]] = None + """The action used for image generation.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] = None + """The background setting used for generation.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] = None + """The output format used for generation.""" + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None + """The quality of the image generated by the image generation tool call. + + One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`. + """ + + revised_prompt: Optional[str] = None + """The prompt that was used after any model prompt rewriting.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + +class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + + command: List[str] + """The command to run.""" + + env: Dict[str, str] + """Environment variables to set for the command.""" + + type: Literal["exec"] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] = None + """Optional user to run the command as.""" + + working_directory: Optional[str] = None + """Optional working directory to run the command in.""" + + +class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + + id: str + """The unique ID of the local shell call.""" + + action: LocalShellCallAction + """Execute a shell command on the server.""" + + call_id: str + """The unique ID of the local shell tool call generated by the model.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the local shell call.""" + + type: Literal["local_shell_call"] + """The type of the local shell call. Always `local_shell_call`.""" + + +class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + + id: str + """The unique ID of the local shell tool call generated by the model.""" + + output: str + """A JSON string of the output of the local shell tool call.""" + + type: Literal["local_shell_call_output"] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class ShellCallAction(BaseModel): + """The shell commands and limits that describe how to run the tool call.""" + + commands: List[str] + """Ordered shell commands for the execution environment to run.""" + + max_output_length: Optional[int] = None + """ + Maximum number of UTF-8 characters to capture from combined stdout and stderr + output. + """ + + timeout_ms: Optional[int] = None + """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" + + +class ShellCallCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ShellCallCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ShellCallCaller: TypeAlias = Annotated[ + Union[ShellCallCallerDirect, ShellCallCallerProgram, None], PropertyInfo(discriminator="type") +] + +ShellCallEnvironment: TypeAlias = Annotated[ + Union[LocalEnvironment, ContainerReference, None], PropertyInfo(discriminator="type") +] + + +class ShellCall(BaseModel): + """A tool representing a request to execute one or more shell commands.""" + + action: ShellCallAction + """The shell commands and limits that describe how to run the tool call.""" + + call_id: str + """The unique ID of the shell tool call generated by the model.""" + + type: Literal["shell_call"] + """The type of the item. Always `shell_call`.""" + + id: Optional[str] = None + """The unique ID of the shell tool call. + + Populated when this item is returned via API. + """ + + caller: Optional[ShellCallCaller] = None + """The execution context that produced this tool call.""" + + environment: Optional[ShellCallEnvironment] = None + """The environment to execute the shell commands in.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + +class ShellCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ShellCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ShellCallOutputCaller: TypeAlias = Annotated[ + Union[ShellCallOutputCallerDirect, ShellCallOutputCallerProgram, None], PropertyInfo(discriminator="type") +] + + +class ShellCallOutput(BaseModel): + """The streamed output items emitted by a shell tool call.""" + + call_id: str + """The unique ID of the shell tool call generated by the model.""" + + output: List[ResponseFunctionShellCallOutputContent] + """ + Captured chunks of stdout and stderr output, along with their associated + outcomes. + """ + + type: Literal["shell_call_output"] + """The type of the item. Always `shell_call_output`.""" + + id: Optional[str] = None + """The unique ID of the shell tool call output. + + Populated when this item is returned via API. + """ + + caller: Optional[ShellCallOutputCaller] = None + """The execution context that produced this tool call.""" + + max_output_length: Optional[int] = None + """ + The maximum number of UTF-8 characters captured for this shell call's combined + output. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the shell call output.""" + + +class ApplyPatchCallOperationCreateFile(BaseModel): + """Instruction for creating a new file via the apply_patch tool.""" + + diff: str + """Unified diff content to apply when creating the file.""" + + path: str + """Path of the file to create relative to the workspace root.""" + + type: Literal["create_file"] + """The operation type. Always `create_file`.""" + + +class ApplyPatchCallOperationDeleteFile(BaseModel): + """Instruction for deleting an existing file via the apply_patch tool.""" + + path: str + """Path of the file to delete relative to the workspace root.""" + + type: Literal["delete_file"] + """The operation type. Always `delete_file`.""" + + +class ApplyPatchCallOperationUpdateFile(BaseModel): + """Instruction for updating an existing file via the apply_patch tool.""" + + diff: str + """Unified diff content to apply to the existing file.""" + + path: str + """Path of the file to update relative to the workspace root.""" + + type: Literal["update_file"] + """The operation type. Always `update_file`.""" + + +ApplyPatchCallOperation: TypeAlias = Annotated[ + Union[ApplyPatchCallOperationCreateFile, ApplyPatchCallOperationDeleteFile, ApplyPatchCallOperationUpdateFile], + PropertyInfo(discriminator="type"), +] + + +class ApplyPatchCallCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ApplyPatchCallCaller: TypeAlias = Annotated[ + Union[ApplyPatchCallCallerDirect, ApplyPatchCallCallerProgram, None], PropertyInfo(discriminator="type") +] + + +class ApplyPatchCall(BaseModel): + """ + A tool call representing a request to create, delete, or update files using diff patches. + """ + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + operation: ApplyPatchCallOperation + """ + The specific create, delete, or update instruction for the apply_patch tool + call. + """ + + status: Literal["in_progress", "completed"] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Literal["apply_patch_call"] + """The type of the item. Always `apply_patch_call`.""" + + id: Optional[str] = None + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + caller: Optional[ApplyPatchCallCaller] = None + """The execution context that produced this tool call.""" + + +class ApplyPatchCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ApplyPatchCallOutputCaller: TypeAlias = Annotated[ + Union[ApplyPatchCallOutputCallerDirect, ApplyPatchCallOutputCallerProgram, None], PropertyInfo(discriminator="type") +] + + +class ApplyPatchCallOutput(BaseModel): + """The streamed output emitted by an apply patch tool call.""" + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + status: Literal["completed", "failed"] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Literal["apply_patch_call_output"] + """The type of the item. Always `apply_patch_call_output`.""" + + id: Optional[str] = None + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + caller: Optional[ApplyPatchCallOutputCaller] = None + """The execution context that produced this tool call.""" + + output: Optional[str] = None + """ + Optional human-readable log text from the apply patch tool (e.g., patch results + or errors). + """ + + +class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + + input_schema: object + """The JSON schema describing the tool's input.""" + + name: str + """The name of the tool.""" + + annotations: Optional[object] = None + """Additional annotations about the tool.""" + + description: Optional[str] = None + """The description of the tool.""" + + +class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + + id: str + """The unique ID of the list.""" + + server_label: str + """The label of the MCP server.""" + + tools: List[McpListToolsTool] + """The tools available on the server.""" + + type: Literal["mcp_list_tools"] + """The type of the item. Always `mcp_list_tools`.""" + + error: Optional[str] = None + """Error message if the server could not list tools.""" + + +class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + + id: str + """The unique ID of the approval request.""" + + arguments: str + """A JSON string of arguments for the tool.""" + + name: str + """The name of the tool to run.""" + + server_label: str + """The label of the MCP server making the request.""" + + type: Literal["mcp_approval_request"] + """The type of the item. Always `mcp_approval_request`.""" + + +class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + id: Optional[str] = None + """The unique ID of the approval response""" + + reason: Optional[str] = None + """Optional reason for the decision.""" + + +class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + + id: str + """The unique ID of the tool call.""" + + arguments: str + """A JSON string of the arguments passed to the tool.""" + + name: str + """The name of the tool that was run.""" + + server_label: str + """The label of the MCP server running the tool.""" + + type: Literal["mcp_call"] + """The type of the item. Always `mcp_call`.""" + + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[McpToolCallError] = None + """The error from the tool call, if any.""" + + output: Optional[str] = None + """The output from the tool call.""" + + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +class CompactionTrigger(BaseModel): + """Compacts the current context. Must be the final input item.""" + + type: Literal["compaction_trigger"] + """The type of the item. Always `compaction_trigger`.""" + + +class ItemReference(BaseModel): + """An internal identifier for an item to reference.""" + + id: str + """The ID of the item to reference.""" + + type: Optional[Literal["item_reference"]] = None + """The type of item to reference. Always `item_reference`.""" + + +class Program(BaseModel): + id: str + """The unique ID of this program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The item type. Always `program`.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of this program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output.""" + + type: Literal["program_output"] + """The item type. Always `program_output`.""" + + +ResponseInputItem: TypeAlias = Annotated[ + Union[ + EasyInputMessage, + Message, + ResponseOutputMessage, + ResponseFileSearchToolCall, + ResponseComputerToolCall, + ComputerCallOutput, + ResponseFunctionWebSearch, + ResponseFunctionToolCall, + FunctionCallOutput, + ToolSearchCall, + ResponseToolSearchOutputItemParam, + AdditionalTools, + ResponseConfigurationUpdateItemParam, + ResponseReasoningItem, + ResponseCompactionItemParam, + ImageGenerationCall, + ResponseCodeInterpreterToolCall, + LocalShellCall, + LocalShellCallOutput, + ShellCall, + ShellCallOutput, + ApplyPatchCall, + ApplyPatchCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + ResponseCustomToolCallOutput, + ResponseCustomToolCall, + CompactionTrigger, + ItemReference, + Program, + ProgramOutput, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_item_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_item_param.py new file mode 100644 index 0000000..f490ad3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_item_param.py @@ -0,0 +1,770 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .tool_param import ToolParam +from .local_environment_param import LocalEnvironmentParam +from .easy_input_message_param import EasyInputMessageParam +from .container_reference_param import ContainerReferenceParam +from .mcp_tool_call_error_param import McpToolCallErrorParam +from .response_output_message_param import ResponseOutputMessageParam +from .response_reasoning_item_param import ResponseReasoningItemParam +from .response_custom_tool_call_param import ResponseCustomToolCallParam +from .response_computer_tool_call_param import ResponseComputerToolCallParam +from .response_function_tool_call_param import ResponseFunctionToolCallParam +from .response_function_web_search_param import ResponseFunctionWebSearchParam +from .response_compaction_item_param_param import ResponseCompactionItemParamParam +from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam +from .response_custom_tool_call_output_param import ResponseCustomToolCallOutputParam +from .response_code_interpreter_tool_call_param import ResponseCodeInterpreterToolCallParam +from .response_input_message_content_list_param import ResponseInputMessageContentListParam +from .response_tool_search_output_item_param_param import ResponseToolSearchOutputItemParamParam +from .response_function_call_output_item_list_param import ResponseFunctionCallOutputItemListParam +from .response_configuration_update_item_param_param import ResponseConfigurationUpdateItemParamParam +from .response_function_shell_call_output_content_param import ResponseFunctionShellCallOutputContentParam +from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam + +__all__ = [ + "ResponseInputItemParam", + "Message", + "ComputerCallOutput", + "ComputerCallOutputAcknowledgedSafetyCheck", + "FunctionCallOutput", + "FunctionCallOutputCaller", + "FunctionCallOutputCallerDirect", + "FunctionCallOutputCallerProgram", + "ToolSearchCall", + "AdditionalTools", + "ImageGenerationCall", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallOutput", + "ShellCall", + "ShellCallAction", + "ShellCallCaller", + "ShellCallCallerDirect", + "ShellCallCallerProgram", + "ShellCallEnvironment", + "ShellCallOutput", + "ShellCallOutputCaller", + "ShellCallOutputCallerDirect", + "ShellCallOutputCallerProgram", + "ApplyPatchCall", + "ApplyPatchCallOperation", + "ApplyPatchCallOperationCreateFile", + "ApplyPatchCallOperationDeleteFile", + "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallCaller", + "ApplyPatchCallCallerDirect", + "ApplyPatchCallCallerProgram", + "ApplyPatchCallOutput", + "ApplyPatchCallOutputCaller", + "ApplyPatchCallOutputCallerDirect", + "ApplyPatchCallOutputCallerProgram", + "McpListTools", + "McpListToolsTool", + "McpApprovalRequest", + "McpApprovalResponse", + "McpCall", + "CompactionTrigger", + "ItemReference", + "Program", + "ProgramOutput", +] + + +class Message(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. + """ + + content: Required[ResponseInputMessageContentListParam] + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Required[Literal["user", "system", "developer"]] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["message"] + """The type of the message input. Always set to `message`.""" + + +class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): + """A pending safety check for the computer call.""" + + id: Required[str] + """The ID of the pending safety check.""" + + code: Optional[str] + """The type of the pending safety check.""" + + message: Optional[str] + """Details about the pending safety check.""" + + +class ComputerCallOutput(TypedDict, total=False): + """The output of a computer tool call.""" + + call_id: Required[str] + """The ID of the computer tool call that produced the output.""" + + output: Required[ResponseComputerToolCallOutputScreenshotParam] + """A computer screenshot image used with the computer use tool.""" + + type: Required[Literal["computer_call_output"]] + """The type of the computer tool call output. Always `computer_call_output`.""" + + id: Optional[str] + """The ID of the computer tool call output.""" + + acknowledged_safety_checks: Optional[Iterable[ComputerCallOutputAcknowledgedSafetyCheck]] + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + +class FunctionCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class FunctionCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +FunctionCallOutputCaller: TypeAlias = Union[FunctionCallOutputCallerDirect, FunctionCallOutputCallerProgram] + + +class FunctionCallOutput(TypedDict, total=False): + """The output of a function tool call.""" + + output: Required[Union[str, ResponseFunctionCallOutputItemListParam]] + """Text, image, or file output of the function tool call.""" + + type: Required[Literal["function_call_output"]] + """The type of the function tool call output. Always `function_call_output`.""" + + id: Optional[str] + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + call_id: Optional[str] + """The unique ID of the function tool call generated by the model.""" + + caller: Optional[FunctionCallOutputCaller] + """The execution context that produced this tool call.""" + + name: Optional[str] + """The name of the tool that produced the output.""" + + namespace: Optional[str] + """The namespace of the tool that produced the output.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +class ToolSearchCall(TypedDict, total=False): + arguments: Required[object] + """The arguments supplied to the tool search call.""" + + type: Required[Literal["tool_search_call"]] + """The item type. Always `tool_search_call`.""" + + id: Optional[str] + """The unique ID of this tool search call.""" + + call_id: Optional[str] + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the tool search call.""" + + +class AdditionalTools(TypedDict, total=False): + role: Required[Literal["developer"]] + """The role that provided the additional tools. Only `developer` is supported.""" + + tools: Required[Iterable[ToolParam]] + """A list of additional tools made available at this item.""" + + type: Required[Literal["additional_tools"]] + """The item type. Always `additional_tools`.""" + + id: Optional[str] + """The unique ID of this additional tools item.""" + + +class ImageGenerationCall(TypedDict, total=False): + """An image generation request made by the model.""" + + id: Required[str] + """The unique ID of the image generation call.""" + + result: Required[Optional[str]] + """The generated image encoded in base64.""" + + status: Required[Literal["in_progress", "completed", "generating", "failed"]] + """The status of the image generation call.""" + + type: Required[Literal["image_generation_call"]] + """The type of the image generation call. Always `image_generation_call`.""" + + action: Optional[Literal["generate", "edit", "auto"]] + """The action used for image generation.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] + """The background setting used for generation.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] + """The output format used for generation.""" + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] + """The quality of the image generated by the image generation tool call. + + One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`. + """ + + revised_prompt: Optional[str] + """The prompt that was used after any model prompt rewriting.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + +class LocalShellCallAction(TypedDict, total=False): + """Execute a shell command on the server.""" + + command: Required[SequenceNotStr[str]] + """The command to run.""" + + env: Required[Dict[str, str]] + """Environment variables to set for the command.""" + + type: Required[Literal["exec"]] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] + """Optional user to run the command as.""" + + working_directory: Optional[str] + """Optional working directory to run the command in.""" + + +class LocalShellCall(TypedDict, total=False): + """A tool call to run a command on the local shell.""" + + id: Required[str] + """The unique ID of the local shell call.""" + + action: Required[LocalShellCallAction] + """Execute a shell command on the server.""" + + call_id: Required[str] + """The unique ID of the local shell tool call generated by the model.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the local shell call.""" + + type: Required[Literal["local_shell_call"]] + """The type of the local shell call. Always `local_shell_call`.""" + + +class LocalShellCallOutput(TypedDict, total=False): + """The output of a local shell tool call.""" + + id: Required[str] + """The unique ID of the local shell tool call generated by the model.""" + + output: Required[str] + """A JSON string of the output of the local shell tool call.""" + + type: Required[Literal["local_shell_call_output"]] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class ShellCallAction(TypedDict, total=False): + """The shell commands and limits that describe how to run the tool call.""" + + commands: Required[SequenceNotStr[str]] + """Ordered shell commands for the execution environment to run.""" + + max_output_length: Optional[int] + """ + Maximum number of UTF-8 characters to capture from combined stdout and stderr + output. + """ + + timeout_ms: Optional[int] + """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" + + +class ShellCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallCaller: TypeAlias = Union[ShellCallCallerDirect, ShellCallCallerProgram] + +ShellCallEnvironment: TypeAlias = Union[LocalEnvironmentParam, ContainerReferenceParam] + + +class ShellCall(TypedDict, total=False): + """A tool representing a request to execute one or more shell commands.""" + + action: Required[ShellCallAction] + """The shell commands and limits that describe how to run the tool call.""" + + call_id: Required[str] + """The unique ID of the shell tool call generated by the model.""" + + type: Required[Literal["shell_call"]] + """The type of the item. Always `shell_call`.""" + + id: Optional[str] + """The unique ID of the shell tool call. + + Populated when this item is returned via API. + """ + + caller: Optional[ShellCallCaller] + """The execution context that produced this tool call.""" + + environment: Optional[ShellCallEnvironment] + """The environment to execute the shell commands in.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + +class ShellCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallOutputCaller: TypeAlias = Union[ShellCallOutputCallerDirect, ShellCallOutputCallerProgram] + + +class ShellCallOutput(TypedDict, total=False): + """The streamed output items emitted by a shell tool call.""" + + call_id: Required[str] + """The unique ID of the shell tool call generated by the model.""" + + output: Required[Iterable[ResponseFunctionShellCallOutputContentParam]] + """ + Captured chunks of stdout and stderr output, along with their associated + outcomes. + """ + + type: Required[Literal["shell_call_output"]] + """The type of the item. Always `shell_call_output`.""" + + id: Optional[str] + """The unique ID of the shell tool call output. + + Populated when this item is returned via API. + """ + + caller: Optional[ShellCallOutputCaller] + """The execution context that produced this tool call.""" + + max_output_length: Optional[int] + """ + The maximum number of UTF-8 characters captured for this shell call's combined + output. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call output.""" + + +class ApplyPatchCallOperationCreateFile(TypedDict, total=False): + """Instruction for creating a new file via the apply_patch tool.""" + + diff: Required[str] + """Unified diff content to apply when creating the file.""" + + path: Required[str] + """Path of the file to create relative to the workspace root.""" + + type: Required[Literal["create_file"]] + """The operation type. Always `create_file`.""" + + +class ApplyPatchCallOperationDeleteFile(TypedDict, total=False): + """Instruction for deleting an existing file via the apply_patch tool.""" + + path: Required[str] + """Path of the file to delete relative to the workspace root.""" + + type: Required[Literal["delete_file"]] + """The operation type. Always `delete_file`.""" + + +class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): + """Instruction for updating an existing file via the apply_patch tool.""" + + diff: Required[str] + """Unified diff content to apply to the existing file.""" + + path: Required[str] + """Path of the file to update relative to the workspace root.""" + + type: Required[Literal["update_file"]] + """The operation type. Always `update_file`.""" + + +ApplyPatchCallOperation: TypeAlias = Union[ + ApplyPatchCallOperationCreateFile, ApplyPatchCallOperationDeleteFile, ApplyPatchCallOperationUpdateFile +] + + +class ApplyPatchCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallCaller: TypeAlias = Union[ApplyPatchCallCallerDirect, ApplyPatchCallCallerProgram] + + +class ApplyPatchCall(TypedDict, total=False): + """ + A tool call representing a request to create, delete, or update files using diff patches. + """ + + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + operation: Required[ApplyPatchCallOperation] + """ + The specific create, delete, or update instruction for the apply_patch tool + call. + """ + + status: Required[Literal["in_progress", "completed"]] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Required[Literal["apply_patch_call"]] + """The type of the item. Always `apply_patch_call`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + caller: Optional[ApplyPatchCallCaller] + """The execution context that produced this tool call.""" + + +class ApplyPatchCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallOutputCaller: TypeAlias = Union[ApplyPatchCallOutputCallerDirect, ApplyPatchCallOutputCallerProgram] + + +class ApplyPatchCallOutput(TypedDict, total=False): + """The streamed output emitted by an apply patch tool call.""" + + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + status: Required[Literal["completed", "failed"]] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Required[Literal["apply_patch_call_output"]] + """The type of the item. Always `apply_patch_call_output`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + caller: Optional[ApplyPatchCallOutputCaller] + """The execution context that produced this tool call.""" + + output: Optional[str] + """ + Optional human-readable log text from the apply patch tool (e.g., patch results + or errors). + """ + + +class McpListToolsTool(TypedDict, total=False): + """A tool available on an MCP server.""" + + input_schema: Required[object] + """The JSON schema describing the tool's input.""" + + name: Required[str] + """The name of the tool.""" + + annotations: Optional[object] + """Additional annotations about the tool.""" + + description: Optional[str] + """The description of the tool.""" + + +class McpListTools(TypedDict, total=False): + """A list of tools available on an MCP server.""" + + id: Required[str] + """The unique ID of the list.""" + + server_label: Required[str] + """The label of the MCP server.""" + + tools: Required[Iterable[McpListToolsTool]] + """The tools available on the server.""" + + type: Required[Literal["mcp_list_tools"]] + """The type of the item. Always `mcp_list_tools`.""" + + error: Optional[str] + """Error message if the server could not list tools.""" + + +class McpApprovalRequest(TypedDict, total=False): + """A request for human approval of a tool invocation.""" + + id: Required[str] + """The unique ID of the approval request.""" + + arguments: Required[str] + """A JSON string of arguments for the tool.""" + + name: Required[str] + """The name of the tool to run.""" + + server_label: Required[str] + """The label of the MCP server making the request.""" + + type: Required[Literal["mcp_approval_request"]] + """The type of the item. Always `mcp_approval_request`.""" + + +class McpApprovalResponse(TypedDict, total=False): + """A response to an MCP approval request.""" + + approval_request_id: Required[str] + """The ID of the approval request being answered.""" + + approve: Required[bool] + """Whether the request was approved.""" + + type: Required[Literal["mcp_approval_response"]] + """The type of the item. Always `mcp_approval_response`.""" + + id: Optional[str] + """The unique ID of the approval response""" + + reason: Optional[str] + """Optional reason for the decision.""" + + +class McpCall(TypedDict, total=False): + """An invocation of a tool on an MCP server.""" + + id: Required[str] + """The unique ID of the tool call.""" + + arguments: Required[str] + """A JSON string of the arguments passed to the tool.""" + + name: Required[str] + """The name of the tool that was run.""" + + server_label: Required[str] + """The label of the MCP server running the tool.""" + + type: Required[Literal["mcp_call"]] + """The type of the item. Always `mcp_call`.""" + + approval_request_id: Optional[str] + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[McpToolCallErrorParam] + """The error from the tool call, if any.""" + + output: Optional[str] + """The output from the tool call.""" + + status: Literal["in_progress", "completed", "incomplete", "calling", "failed"] + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +class CompactionTrigger(TypedDict, total=False): + """Compacts the current context. Must be the final input item.""" + + type: Required[Literal["compaction_trigger"]] + """The type of the item. Always `compaction_trigger`.""" + + +class ItemReference(TypedDict, total=False): + """An internal identifier for an item to reference.""" + + id: Required[str] + """The ID of the item to reference.""" + + type: Optional[Literal["item_reference"]] + """The type of item to reference. Always `item_reference`.""" + + +class Program(TypedDict, total=False): + id: Required[str] + """The unique ID of this program item.""" + + call_id: Required[str] + """The stable call ID of the program item.""" + + code: Required[str] + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: Required[str] + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Required[Literal["program"]] + """The item type. Always `program`.""" + + +class ProgramOutput(TypedDict, total=False): + id: Required[str] + """The unique ID of this program output item.""" + + call_id: Required[str] + """The call ID of the program item.""" + + result: Required[str] + """The result produced by the program item.""" + + status: Required[Literal["completed", "incomplete"]] + """The terminal status of the program output.""" + + type: Required[Literal["program_output"]] + """The item type. Always `program_output`.""" + + +ResponseInputItemParam: TypeAlias = Union[ + EasyInputMessageParam, + Message, + ResponseOutputMessageParam, + ResponseFileSearchToolCallParam, + ResponseComputerToolCallParam, + ComputerCallOutput, + ResponseFunctionWebSearchParam, + ResponseFunctionToolCallParam, + FunctionCallOutput, + ToolSearchCall, + ResponseToolSearchOutputItemParamParam, + AdditionalTools, + ResponseConfigurationUpdateItemParamParam, + ResponseReasoningItemParam, + ResponseCompactionItemParamParam, + ImageGenerationCall, + ResponseCodeInterpreterToolCallParam, + LocalShellCall, + LocalShellCallOutput, + ShellCall, + ShellCallOutput, + ApplyPatchCall, + ApplyPatchCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + ResponseCustomToolCallOutputParam, + ResponseCustomToolCallParam, + CompactionTrigger, + ItemReference, + Program, + ProgramOutput, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_message_content_list.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_message_content_list.py new file mode 100644 index 0000000..976accd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_message_content_list.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .response_input_content import ResponseInputContent + +__all__ = ["ResponseInputMessageContentList"] + +ResponseInputMessageContentList: TypeAlias = List[ResponseInputContent] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_message_content_list_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_message_content_list_param.py new file mode 100644 index 0000000..d83b917 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_message_content_list_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import TypeAlias + +from .response_input_file_param import ResponseInputFileParam +from .response_input_text_param import ResponseInputTextParam +from .response_input_image_param import ResponseInputImageParam + +__all__ = ["ResponseInputMessageContentListParam", "ResponseInputContentParam"] + +ResponseInputContentParam: TypeAlias = Union[ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam] + +ResponseInputMessageContentListParam: TypeAlias = List[ResponseInputContentParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_message_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_message_item.py new file mode 100644 index 0000000..e818259 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_message_item.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_input_message_content_list import ResponseInputMessageContentList + +__all__ = ["ResponseInputMessageItem"] + + +class ResponseInputMessageItem(BaseModel): + id: str + """The unique ID of the message input.""" + + content: ResponseInputMessageContentList + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Literal["user", "system", "developer"] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + type: Literal["message"] + """The type of the message input. Always set to `message`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_param.py new file mode 100644 index 0000000..776f1df --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_param.py @@ -0,0 +1,773 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .tool_param import ToolParam +from .local_environment_param import LocalEnvironmentParam +from .easy_input_message_param import EasyInputMessageParam +from .container_reference_param import ContainerReferenceParam +from .mcp_tool_call_error_param import McpToolCallErrorParam +from .response_output_message_param import ResponseOutputMessageParam +from .response_reasoning_item_param import ResponseReasoningItemParam +from .response_custom_tool_call_param import ResponseCustomToolCallParam +from .response_computer_tool_call_param import ResponseComputerToolCallParam +from .response_function_tool_call_param import ResponseFunctionToolCallParam +from .response_function_web_search_param import ResponseFunctionWebSearchParam +from .response_compaction_item_param_param import ResponseCompactionItemParamParam +from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam +from .response_custom_tool_call_output_param import ResponseCustomToolCallOutputParam +from .response_code_interpreter_tool_call_param import ResponseCodeInterpreterToolCallParam +from .response_input_message_content_list_param import ResponseInputMessageContentListParam +from .response_tool_search_output_item_param_param import ResponseToolSearchOutputItemParamParam +from .response_function_call_output_item_list_param import ResponseFunctionCallOutputItemListParam +from .response_configuration_update_item_param_param import ResponseConfigurationUpdateItemParamParam +from .response_function_shell_call_output_content_param import ResponseFunctionShellCallOutputContentParam +from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam + +__all__ = [ + "ResponseInputParam", + "ResponseInputItemParam", + "Message", + "ComputerCallOutput", + "ComputerCallOutputAcknowledgedSafetyCheck", + "FunctionCallOutput", + "FunctionCallOutputCaller", + "FunctionCallOutputCallerDirect", + "FunctionCallOutputCallerProgram", + "ToolSearchCall", + "AdditionalTools", + "ImageGenerationCall", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallOutput", + "ShellCall", + "ShellCallAction", + "ShellCallCaller", + "ShellCallCallerDirect", + "ShellCallCallerProgram", + "ShellCallEnvironment", + "ShellCallOutput", + "ShellCallOutputCaller", + "ShellCallOutputCallerDirect", + "ShellCallOutputCallerProgram", + "ApplyPatchCall", + "ApplyPatchCallOperation", + "ApplyPatchCallOperationCreateFile", + "ApplyPatchCallOperationDeleteFile", + "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallCaller", + "ApplyPatchCallCallerDirect", + "ApplyPatchCallCallerProgram", + "ApplyPatchCallOutput", + "ApplyPatchCallOutputCaller", + "ApplyPatchCallOutputCallerDirect", + "ApplyPatchCallOutputCallerProgram", + "McpListTools", + "McpListToolsTool", + "McpApprovalRequest", + "McpApprovalResponse", + "McpCall", + "CompactionTrigger", + "ItemReference", + "Program", + "ProgramOutput", +] + + +class Message(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. + """ + + content: Required[ResponseInputMessageContentListParam] + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Required[Literal["user", "system", "developer"]] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["message"] + """The type of the message input. Always set to `message`.""" + + +class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): + """A pending safety check for the computer call.""" + + id: Required[str] + """The ID of the pending safety check.""" + + code: Optional[str] + """The type of the pending safety check.""" + + message: Optional[str] + """Details about the pending safety check.""" + + +class ComputerCallOutput(TypedDict, total=False): + """The output of a computer tool call.""" + + call_id: Required[str] + """The ID of the computer tool call that produced the output.""" + + output: Required[ResponseComputerToolCallOutputScreenshotParam] + """A computer screenshot image used with the computer use tool.""" + + type: Required[Literal["computer_call_output"]] + """The type of the computer tool call output. Always `computer_call_output`.""" + + id: Optional[str] + """The ID of the computer tool call output.""" + + acknowledged_safety_checks: Optional[Iterable[ComputerCallOutputAcknowledgedSafetyCheck]] + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + +class FunctionCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class FunctionCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +FunctionCallOutputCaller: TypeAlias = Union[FunctionCallOutputCallerDirect, FunctionCallOutputCallerProgram] + + +class FunctionCallOutput(TypedDict, total=False): + """The output of a function tool call.""" + + output: Required[Union[str, ResponseFunctionCallOutputItemListParam]] + """Text, image, or file output of the function tool call.""" + + type: Required[Literal["function_call_output"]] + """The type of the function tool call output. Always `function_call_output`.""" + + id: Optional[str] + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + call_id: Optional[str] + """The unique ID of the function tool call generated by the model.""" + + caller: Optional[FunctionCallOutputCaller] + """The execution context that produced this tool call.""" + + name: Optional[str] + """The name of the tool that produced the output.""" + + namespace: Optional[str] + """The namespace of the tool that produced the output.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +class ToolSearchCall(TypedDict, total=False): + arguments: Required[object] + """The arguments supplied to the tool search call.""" + + type: Required[Literal["tool_search_call"]] + """The item type. Always `tool_search_call`.""" + + id: Optional[str] + """The unique ID of this tool search call.""" + + call_id: Optional[str] + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the tool search call.""" + + +class AdditionalTools(TypedDict, total=False): + role: Required[Literal["developer"]] + """The role that provided the additional tools. Only `developer` is supported.""" + + tools: Required[Iterable[ToolParam]] + """A list of additional tools made available at this item.""" + + type: Required[Literal["additional_tools"]] + """The item type. Always `additional_tools`.""" + + id: Optional[str] + """The unique ID of this additional tools item.""" + + +class ImageGenerationCall(TypedDict, total=False): + """An image generation request made by the model.""" + + id: Required[str] + """The unique ID of the image generation call.""" + + result: Required[Optional[str]] + """The generated image encoded in base64.""" + + status: Required[Literal["in_progress", "completed", "generating", "failed"]] + """The status of the image generation call.""" + + type: Required[Literal["image_generation_call"]] + """The type of the image generation call. Always `image_generation_call`.""" + + action: Optional[Literal["generate", "edit", "auto"]] + """The action used for image generation.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] + """The background setting used for generation.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] + """The output format used for generation.""" + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] + """The quality of the image generated by the image generation tool call. + + One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`. + """ + + revised_prompt: Optional[str] + """The prompt that was used after any model prompt rewriting.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + +class LocalShellCallAction(TypedDict, total=False): + """Execute a shell command on the server.""" + + command: Required[SequenceNotStr[str]] + """The command to run.""" + + env: Required[Dict[str, str]] + """Environment variables to set for the command.""" + + type: Required[Literal["exec"]] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] + """Optional user to run the command as.""" + + working_directory: Optional[str] + """Optional working directory to run the command in.""" + + +class LocalShellCall(TypedDict, total=False): + """A tool call to run a command on the local shell.""" + + id: Required[str] + """The unique ID of the local shell call.""" + + action: Required[LocalShellCallAction] + """Execute a shell command on the server.""" + + call_id: Required[str] + """The unique ID of the local shell tool call generated by the model.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the local shell call.""" + + type: Required[Literal["local_shell_call"]] + """The type of the local shell call. Always `local_shell_call`.""" + + +class LocalShellCallOutput(TypedDict, total=False): + """The output of a local shell tool call.""" + + id: Required[str] + """The unique ID of the local shell tool call generated by the model.""" + + output: Required[str] + """A JSON string of the output of the local shell tool call.""" + + type: Required[Literal["local_shell_call_output"]] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class ShellCallAction(TypedDict, total=False): + """The shell commands and limits that describe how to run the tool call.""" + + commands: Required[SequenceNotStr[str]] + """Ordered shell commands for the execution environment to run.""" + + max_output_length: Optional[int] + """ + Maximum number of UTF-8 characters to capture from combined stdout and stderr + output. + """ + + timeout_ms: Optional[int] + """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" + + +class ShellCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallCaller: TypeAlias = Union[ShellCallCallerDirect, ShellCallCallerProgram] + +ShellCallEnvironment: TypeAlias = Union[LocalEnvironmentParam, ContainerReferenceParam] + + +class ShellCall(TypedDict, total=False): + """A tool representing a request to execute one or more shell commands.""" + + action: Required[ShellCallAction] + """The shell commands and limits that describe how to run the tool call.""" + + call_id: Required[str] + """The unique ID of the shell tool call generated by the model.""" + + type: Required[Literal["shell_call"]] + """The type of the item. Always `shell_call`.""" + + id: Optional[str] + """The unique ID of the shell tool call. + + Populated when this item is returned via API. + """ + + caller: Optional[ShellCallCaller] + """The execution context that produced this tool call.""" + + environment: Optional[ShellCallEnvironment] + """The environment to execute the shell commands in.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + +class ShellCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallOutputCaller: TypeAlias = Union[ShellCallOutputCallerDirect, ShellCallOutputCallerProgram] + + +class ShellCallOutput(TypedDict, total=False): + """The streamed output items emitted by a shell tool call.""" + + call_id: Required[str] + """The unique ID of the shell tool call generated by the model.""" + + output: Required[Iterable[ResponseFunctionShellCallOutputContentParam]] + """ + Captured chunks of stdout and stderr output, along with their associated + outcomes. + """ + + type: Required[Literal["shell_call_output"]] + """The type of the item. Always `shell_call_output`.""" + + id: Optional[str] + """The unique ID of the shell tool call output. + + Populated when this item is returned via API. + """ + + caller: Optional[ShellCallOutputCaller] + """The execution context that produced this tool call.""" + + max_output_length: Optional[int] + """ + The maximum number of UTF-8 characters captured for this shell call's combined + output. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call output.""" + + +class ApplyPatchCallOperationCreateFile(TypedDict, total=False): + """Instruction for creating a new file via the apply_patch tool.""" + + diff: Required[str] + """Unified diff content to apply when creating the file.""" + + path: Required[str] + """Path of the file to create relative to the workspace root.""" + + type: Required[Literal["create_file"]] + """The operation type. Always `create_file`.""" + + +class ApplyPatchCallOperationDeleteFile(TypedDict, total=False): + """Instruction for deleting an existing file via the apply_patch tool.""" + + path: Required[str] + """Path of the file to delete relative to the workspace root.""" + + type: Required[Literal["delete_file"]] + """The operation type. Always `delete_file`.""" + + +class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): + """Instruction for updating an existing file via the apply_patch tool.""" + + diff: Required[str] + """Unified diff content to apply to the existing file.""" + + path: Required[str] + """Path of the file to update relative to the workspace root.""" + + type: Required[Literal["update_file"]] + """The operation type. Always `update_file`.""" + + +ApplyPatchCallOperation: TypeAlias = Union[ + ApplyPatchCallOperationCreateFile, ApplyPatchCallOperationDeleteFile, ApplyPatchCallOperationUpdateFile +] + + +class ApplyPatchCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallCaller: TypeAlias = Union[ApplyPatchCallCallerDirect, ApplyPatchCallCallerProgram] + + +class ApplyPatchCall(TypedDict, total=False): + """ + A tool call representing a request to create, delete, or update files using diff patches. + """ + + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + operation: Required[ApplyPatchCallOperation] + """ + The specific create, delete, or update instruction for the apply_patch tool + call. + """ + + status: Required[Literal["in_progress", "completed"]] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Required[Literal["apply_patch_call"]] + """The type of the item. Always `apply_patch_call`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + caller: Optional[ApplyPatchCallCaller] + """The execution context that produced this tool call.""" + + +class ApplyPatchCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallOutputCaller: TypeAlias = Union[ApplyPatchCallOutputCallerDirect, ApplyPatchCallOutputCallerProgram] + + +class ApplyPatchCallOutput(TypedDict, total=False): + """The streamed output emitted by an apply patch tool call.""" + + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + status: Required[Literal["completed", "failed"]] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Required[Literal["apply_patch_call_output"]] + """The type of the item. Always `apply_patch_call_output`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + caller: Optional[ApplyPatchCallOutputCaller] + """The execution context that produced this tool call.""" + + output: Optional[str] + """ + Optional human-readable log text from the apply patch tool (e.g., patch results + or errors). + """ + + +class McpListToolsTool(TypedDict, total=False): + """A tool available on an MCP server.""" + + input_schema: Required[object] + """The JSON schema describing the tool's input.""" + + name: Required[str] + """The name of the tool.""" + + annotations: Optional[object] + """Additional annotations about the tool.""" + + description: Optional[str] + """The description of the tool.""" + + +class McpListTools(TypedDict, total=False): + """A list of tools available on an MCP server.""" + + id: Required[str] + """The unique ID of the list.""" + + server_label: Required[str] + """The label of the MCP server.""" + + tools: Required[Iterable[McpListToolsTool]] + """The tools available on the server.""" + + type: Required[Literal["mcp_list_tools"]] + """The type of the item. Always `mcp_list_tools`.""" + + error: Optional[str] + """Error message if the server could not list tools.""" + + +class McpApprovalRequest(TypedDict, total=False): + """A request for human approval of a tool invocation.""" + + id: Required[str] + """The unique ID of the approval request.""" + + arguments: Required[str] + """A JSON string of arguments for the tool.""" + + name: Required[str] + """The name of the tool to run.""" + + server_label: Required[str] + """The label of the MCP server making the request.""" + + type: Required[Literal["mcp_approval_request"]] + """The type of the item. Always `mcp_approval_request`.""" + + +class McpApprovalResponse(TypedDict, total=False): + """A response to an MCP approval request.""" + + approval_request_id: Required[str] + """The ID of the approval request being answered.""" + + approve: Required[bool] + """Whether the request was approved.""" + + type: Required[Literal["mcp_approval_response"]] + """The type of the item. Always `mcp_approval_response`.""" + + id: Optional[str] + """The unique ID of the approval response""" + + reason: Optional[str] + """Optional reason for the decision.""" + + +class McpCall(TypedDict, total=False): + """An invocation of a tool on an MCP server.""" + + id: Required[str] + """The unique ID of the tool call.""" + + arguments: Required[str] + """A JSON string of the arguments passed to the tool.""" + + name: Required[str] + """The name of the tool that was run.""" + + server_label: Required[str] + """The label of the MCP server running the tool.""" + + type: Required[Literal["mcp_call"]] + """The type of the item. Always `mcp_call`.""" + + approval_request_id: Optional[str] + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[McpToolCallErrorParam] + """The error from the tool call, if any.""" + + output: Optional[str] + """The output from the tool call.""" + + status: Literal["in_progress", "completed", "incomplete", "calling", "failed"] + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +class CompactionTrigger(TypedDict, total=False): + """Compacts the current context. Must be the final input item.""" + + type: Required[Literal["compaction_trigger"]] + """The type of the item. Always `compaction_trigger`.""" + + +class ItemReference(TypedDict, total=False): + """An internal identifier for an item to reference.""" + + id: Required[str] + """The ID of the item to reference.""" + + type: Optional[Literal["item_reference"]] + """The type of item to reference. Always `item_reference`.""" + + +class Program(TypedDict, total=False): + id: Required[str] + """The unique ID of this program item.""" + + call_id: Required[str] + """The stable call ID of the program item.""" + + code: Required[str] + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: Required[str] + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Required[Literal["program"]] + """The item type. Always `program`.""" + + +class ProgramOutput(TypedDict, total=False): + id: Required[str] + """The unique ID of this program output item.""" + + call_id: Required[str] + """The call ID of the program item.""" + + result: Required[str] + """The result produced by the program item.""" + + status: Required[Literal["completed", "incomplete"]] + """The terminal status of the program output.""" + + type: Required[Literal["program_output"]] + """The item type. Always `program_output`.""" + + +ResponseInputItemParam: TypeAlias = Union[ + EasyInputMessageParam, + Message, + ResponseOutputMessageParam, + ResponseFileSearchToolCallParam, + ResponseComputerToolCallParam, + ComputerCallOutput, + ResponseFunctionWebSearchParam, + ResponseFunctionToolCallParam, + FunctionCallOutput, + ToolSearchCall, + ResponseToolSearchOutputItemParamParam, + AdditionalTools, + ResponseConfigurationUpdateItemParamParam, + ResponseReasoningItemParam, + ResponseCompactionItemParamParam, + ImageGenerationCall, + ResponseCodeInterpreterToolCallParam, + LocalShellCall, + LocalShellCallOutput, + ShellCall, + ShellCallOutput, + ApplyPatchCall, + ApplyPatchCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + ResponseCustomToolCallOutputParam, + ResponseCustomToolCallParam, + CompactionTrigger, + ItemReference, + Program, + ProgramOutput, +] + +ResponseInputParam: TypeAlias = List[ResponseInputItemParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_text.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_text.py new file mode 100644 index 0000000..028f506 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_text.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseInputText", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class ResponseInputText(BaseModel): + """A text input to the model.""" + + text: str + """The text input to the model.""" + + type: Literal["input_text"] + """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_text_content.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_text_content.py new file mode 100644 index 0000000..354b92d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_text_content.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseInputTextContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class ResponseInputTextContent(BaseModel): + """A text input to the model.""" + + text: str + """The text input to the model.""" + + type: Literal["input_text"] + """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_text_content_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_text_content_param.py new file mode 100644 index 0000000..3044469 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_text_content_param.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseInputTextContentParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class ResponseInputTextContentParam(TypedDict, total=False): + """A text input to the model.""" + + text: Required[str] + """The text input to the model.""" + + type: Required[Literal["input_text"]] + """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_input_text_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_text_param.py new file mode 100644 index 0000000..494be06 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_input_text_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseInputTextParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class ResponseInputTextParam(TypedDict, total=False): + """A text input to the model.""" + + text: Required[str] + """The text input to the model.""" + + type: Required[Literal["input_text"]] + """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_item.py new file mode 100644 index 0000000..4b14d4d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_item.py @@ -0,0 +1,335 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from .tool import Tool +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .mcp_tool_call_error import McpToolCallError +from .response_output_message import ResponseOutputMessage +from .response_reasoning_item import ResponseReasoningItem +from .response_compaction_item import ResponseCompactionItem +from .response_tool_search_call import ResponseToolSearchCall +from .response_computer_tool_call import ResponseComputerToolCall +from .response_input_message_item import ResponseInputMessageItem +from .response_function_web_search import ResponseFunctionWebSearch +from .response_apply_patch_tool_call import ResponseApplyPatchToolCall +from .response_custom_tool_call_item import ResponseCustomToolCallItem +from .response_file_search_tool_call import ResponseFileSearchToolCall +from .response_function_tool_call_item import ResponseFunctionToolCallItem +from .response_tool_search_output_item import ResponseToolSearchOutputItem +from .response_function_shell_tool_call import ResponseFunctionShellToolCall +from .response_configuration_update_item import ResponseConfigurationUpdateItem +from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall +from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput +from .response_custom_tool_call_output_item import ResponseCustomToolCallOutputItem +from .response_computer_tool_call_output_item import ResponseComputerToolCallOutputItem +from .response_function_tool_call_output_item import ResponseFunctionToolCallOutputItem +from .response_function_shell_tool_call_output import ResponseFunctionShellToolCallOutput + +__all__ = [ + "ResponseItem", + "AdditionalTools", + "Program", + "ProgramOutput", + "ImageGenerationCall", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallOutput", + "McpListTools", + "McpListToolsTool", + "McpApprovalRequest", + "McpApprovalResponse", + "McpCall", +] + + +class AdditionalTools(BaseModel): + id: str + """The unique ID of the additional tools item.""" + + role: Literal["unknown", "user", "assistant", "system", "critic", "discriminator", "developer", "tool"] + """The role that provided the additional tools.""" + + tools: List[Tool] + """The additional tool definitions made available at this item.""" + + type: Literal["additional_tools"] + """The type of the item. Always `additional_tools`.""" + + +class Program(BaseModel): + id: str + """The unique ID of the program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The type of the item. Always `program`.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of the program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output item.""" + + type: Literal["program_output"] + """The type of the item. Always `program_output`.""" + + +class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + + id: str + """The unique ID of the image generation call.""" + + result: Optional[str] = None + """The generated image encoded in base64.""" + + status: Literal["in_progress", "completed", "generating", "failed"] + """The status of the image generation call.""" + + type: Literal["image_generation_call"] + """The type of the image generation call. Always `image_generation_call`.""" + + action: Optional[Literal["generate", "edit", "auto"]] = None + """The action used for image generation.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] = None + """The background setting used for generation.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] = None + """The output format used for generation.""" + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None + """The quality of the image generated by the image generation tool call. + + One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`. + """ + + revised_prompt: Optional[str] = None + """The prompt that was used after any model prompt rewriting.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + +class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + + command: List[str] + """The command to run.""" + + env: Dict[str, str] + """Environment variables to set for the command.""" + + type: Literal["exec"] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] = None + """Optional user to run the command as.""" + + working_directory: Optional[str] = None + """Optional working directory to run the command in.""" + + +class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + + id: str + """The unique ID of the local shell call.""" + + action: LocalShellCallAction + """Execute a shell command on the server.""" + + call_id: str + """The unique ID of the local shell tool call generated by the model.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the local shell call.""" + + type: Literal["local_shell_call"] + """The type of the local shell call. Always `local_shell_call`.""" + + +class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + + id: str + """The unique ID of the local shell tool call generated by the model.""" + + output: str + """A JSON string of the output of the local shell tool call.""" + + type: Literal["local_shell_call_output"] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + + input_schema: object + """The JSON schema describing the tool's input.""" + + name: str + """The name of the tool.""" + + annotations: Optional[object] = None + """Additional annotations about the tool.""" + + description: Optional[str] = None + """The description of the tool.""" + + +class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + + id: str + """The unique ID of the list.""" + + server_label: str + """The label of the MCP server.""" + + tools: List[McpListToolsTool] + """The tools available on the server.""" + + type: Literal["mcp_list_tools"] + """The type of the item. Always `mcp_list_tools`.""" + + error: Optional[str] = None + """Error message if the server could not list tools.""" + + +class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + + id: str + """The unique ID of the approval request.""" + + arguments: str + """A JSON string of arguments for the tool.""" + + name: str + """The name of the tool to run.""" + + server_label: str + """The label of the MCP server making the request.""" + + type: Literal["mcp_approval_request"] + """The type of the item. Always `mcp_approval_request`.""" + + +class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + + id: str + """The unique ID of the approval response""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + reason: Optional[str] = None + """Optional reason for the decision.""" + + +class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + + id: str + """The unique ID of the tool call.""" + + arguments: str + """A JSON string of the arguments passed to the tool.""" + + name: str + """The name of the tool that was run.""" + + server_label: str + """The label of the MCP server running the tool.""" + + type: Literal["mcp_call"] + """The type of the item. Always `mcp_call`.""" + + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[McpToolCallError] = None + """The error from the tool call, if any.""" + + output: Optional[str] = None + """The output from the tool call.""" + + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +ResponseItem: TypeAlias = Annotated[ + Union[ + ResponseInputMessageItem, + ResponseOutputMessage, + ResponseFileSearchToolCall, + ResponseComputerToolCall, + ResponseComputerToolCallOutputItem, + ResponseFunctionWebSearch, + ResponseFunctionToolCallItem, + ResponseFunctionToolCallOutputItem, + ResponseToolSearchCall, + ResponseToolSearchOutputItem, + AdditionalTools, + ResponseConfigurationUpdateItem, + ResponseReasoningItem, + Program, + ProgramOutput, + ResponseCompactionItem, + ImageGenerationCall, + ResponseCodeInterpreterToolCall, + LocalShellCall, + LocalShellCallOutput, + ResponseFunctionShellToolCall, + ResponseFunctionShellToolCallOutput, + ResponseApplyPatchToolCall, + ResponseApplyPatchToolCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + ResponseCustomToolCallItem, + ResponseCustomToolCallOutputItem, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_item_list.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_item_list.py new file mode 100644 index 0000000..2f8db0e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_item_list.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_item import ResponseItem + +__all__ = ["ResponseItemList"] + + +class ResponseItemList(BaseModel): + """A list of Response items.""" + + data: List[ResponseItem] + """A list of items used to generate this response.""" + + first_id: str + """The ID of the first item in the list.""" + + has_more: bool + """Whether there are more items available.""" + + last_id: str + """The ID of the last item in the list.""" + + object: Literal["list"] + """The type of object returned, must be `list`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_local_environment.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_local_environment.py new file mode 100644 index 0000000..a8f8eca --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_local_environment.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseLocalEnvironment"] + + +class ResponseLocalEnvironment(BaseModel): + """Represents the use of a local environment to perform shell actions.""" + + type: Literal["local"] + """The environment type. Always `local`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_arguments_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_arguments_delta_event.py new file mode 100644 index 0000000..5438616 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_arguments_delta_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallArgumentsDeltaEvent"] + + +class ResponseMcpCallArgumentsDeltaEvent(BaseModel): + """ + Emitted when there is a delta (partial update) to the arguments of an MCP tool call. + """ + + delta: str + """ + A JSON string containing the partial update to the arguments for the MCP tool + call. + """ + + item_id: str + """The unique identifier of the MCP tool call item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call_arguments.delta"] + """The type of the event. Always 'response.mcp_call_arguments.delta'.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_arguments_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_arguments_done_event.py new file mode 100644 index 0000000..a70d0b8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_arguments_done_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallArgumentsDoneEvent"] + + +class ResponseMcpCallArgumentsDoneEvent(BaseModel): + """Emitted when the arguments for an MCP tool call are finalized.""" + + arguments: str + """A JSON string containing the finalized arguments for the MCP tool call.""" + + item_id: str + """The unique identifier of the MCP tool call item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call_arguments.done"] + """The type of the event. Always 'response.mcp_call_arguments.done'.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_completed_event.py new file mode 100644 index 0000000..29719b8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_completed_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallCompletedEvent"] + + +class ResponseMcpCallCompletedEvent(BaseModel): + """Emitted when an MCP tool call has completed successfully.""" + + item_id: str + """The ID of the MCP tool call item that completed.""" + + output_index: int + """The index of the output item that completed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call.completed"] + """The type of the event. Always 'response.mcp_call.completed'.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_failed_event.py new file mode 100644 index 0000000..7241d6f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_failed_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallFailedEvent"] + + +class ResponseMcpCallFailedEvent(BaseModel): + """Emitted when an MCP tool call has failed.""" + + item_id: str + """The ID of the MCP tool call item that failed.""" + + output_index: int + """The index of the output item that failed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call.failed"] + """The type of the event. Always 'response.mcp_call.failed'.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_in_progress_event.py new file mode 100644 index 0000000..73a3b45 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_call_in_progress_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallInProgressEvent"] + + +class ResponseMcpCallInProgressEvent(BaseModel): + """Emitted when an MCP tool call is in progress.""" + + item_id: str + """The unique identifier of the MCP tool call item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call.in_progress"] + """The type of the event. Always 'response.mcp_call.in_progress'.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_list_tools_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_list_tools_completed_event.py new file mode 100644 index 0000000..a775494 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_list_tools_completed_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpListToolsCompletedEvent"] + + +class ResponseMcpListToolsCompletedEvent(BaseModel): + """Emitted when the list of available MCP tools has been successfully retrieved.""" + + item_id: str + """The ID of the MCP tool call item that produced this output.""" + + output_index: int + """The index of the output item that was processed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_list_tools.completed"] + """The type of the event. Always 'response.mcp_list_tools.completed'.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_list_tools_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_list_tools_failed_event.py new file mode 100644 index 0000000..86928cd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_list_tools_failed_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpListToolsFailedEvent"] + + +class ResponseMcpListToolsFailedEvent(BaseModel): + """Emitted when the attempt to list available MCP tools has failed.""" + + item_id: str + """The ID of the MCP tool call item that failed.""" + + output_index: int + """The index of the output item that failed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_list_tools.failed"] + """The type of the event. Always 'response.mcp_list_tools.failed'.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_list_tools_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_list_tools_in_progress_event.py new file mode 100644 index 0000000..4ef204e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_mcp_list_tools_in_progress_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpListToolsInProgressEvent"] + + +class ResponseMcpListToolsInProgressEvent(BaseModel): + """ + Emitted when the system is in the process of retrieving the list of available MCP tools. + """ + + item_id: str + """The ID of the MCP tool call item that is being processed.""" + + output_index: int + """The index of the output item that is being processed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_list_tools.in_progress"] + """The type of the event. Always 'response.mcp_list_tools.in_progress'.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_output_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_item.py new file mode 100644 index 0000000..9492c2a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_item.py @@ -0,0 +1,331 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from .tool import Tool +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .mcp_tool_call_error import McpToolCallError +from .response_output_message import ResponseOutputMessage +from .response_reasoning_item import ResponseReasoningItem +from .response_compaction_item import ResponseCompactionItem +from .response_custom_tool_call import ResponseCustomToolCall +from .response_tool_search_call import ResponseToolSearchCall +from .response_computer_tool_call import ResponseComputerToolCall +from .response_function_tool_call import ResponseFunctionToolCall +from .response_function_web_search import ResponseFunctionWebSearch +from .response_apply_patch_tool_call import ResponseApplyPatchToolCall +from .response_file_search_tool_call import ResponseFileSearchToolCall +from .response_tool_search_output_item import ResponseToolSearchOutputItem +from .response_function_shell_tool_call import ResponseFunctionShellToolCall +from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall +from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput +from .response_custom_tool_call_output_item import ResponseCustomToolCallOutputItem +from .response_computer_tool_call_output_item import ResponseComputerToolCallOutputItem +from .response_function_tool_call_output_item import ResponseFunctionToolCallOutputItem +from .response_function_shell_tool_call_output import ResponseFunctionShellToolCallOutput + +__all__ = [ + "ResponseOutputItem", + "Program", + "ProgramOutput", + "AdditionalTools", + "ImageGenerationCall", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallOutput", + "McpCall", + "McpListTools", + "McpListToolsTool", + "McpApprovalRequest", + "McpApprovalResponse", +] + + +class Program(BaseModel): + id: str + """The unique ID of the program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The type of the item. Always `program`.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of the program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output item.""" + + type: Literal["program_output"] + """The type of the item. Always `program_output`.""" + + +class AdditionalTools(BaseModel): + id: str + """The unique ID of the additional tools item.""" + + role: Literal["unknown", "user", "assistant", "system", "critic", "discriminator", "developer", "tool"] + """The role that provided the additional tools.""" + + tools: List[Tool] + """The additional tool definitions made available at this item.""" + + type: Literal["additional_tools"] + """The type of the item. Always `additional_tools`.""" + + +class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + + id: str + """The unique ID of the image generation call.""" + + result: Optional[str] = None + """The generated image encoded in base64.""" + + status: Literal["in_progress", "completed", "generating", "failed"] + """The status of the image generation call.""" + + type: Literal["image_generation_call"] + """The type of the image generation call. Always `image_generation_call`.""" + + action: Optional[Literal["generate", "edit", "auto"]] = None + """The action used for image generation.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] = None + """The background setting used for generation.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] = None + """The output format used for generation.""" + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None + """The quality of the image generated by the image generation tool call. + + One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`. + """ + + revised_prompt: Optional[str] = None + """The prompt that was used after any model prompt rewriting.""" + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None + """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.""" + + +class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + + command: List[str] + """The command to run.""" + + env: Dict[str, str] + """Environment variables to set for the command.""" + + type: Literal["exec"] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] = None + """Optional user to run the command as.""" + + working_directory: Optional[str] = None + """Optional working directory to run the command in.""" + + +class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + + id: str + """The unique ID of the local shell call.""" + + action: LocalShellCallAction + """Execute a shell command on the server.""" + + call_id: str + """The unique ID of the local shell tool call generated by the model.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the local shell call.""" + + type: Literal["local_shell_call"] + """The type of the local shell call. Always `local_shell_call`.""" + + +class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + + id: str + """The unique ID of the local shell tool call generated by the model.""" + + output: str + """A JSON string of the output of the local shell tool call.""" + + type: Literal["local_shell_call_output"] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + + id: str + """The unique ID of the tool call.""" + + arguments: str + """A JSON string of the arguments passed to the tool.""" + + name: str + """The name of the tool that was run.""" + + server_label: str + """The label of the MCP server running the tool.""" + + type: Literal["mcp_call"] + """The type of the item. Always `mcp_call`.""" + + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[McpToolCallError] = None + """The error from the tool call, if any.""" + + output: Optional[str] = None + """The output from the tool call.""" + + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + + input_schema: object + """The JSON schema describing the tool's input.""" + + name: str + """The name of the tool.""" + + annotations: Optional[object] = None + """Additional annotations about the tool.""" + + description: Optional[str] = None + """The description of the tool.""" + + +class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + + id: str + """The unique ID of the list.""" + + server_label: str + """The label of the MCP server.""" + + tools: List[McpListToolsTool] + """The tools available on the server.""" + + type: Literal["mcp_list_tools"] + """The type of the item. Always `mcp_list_tools`.""" + + error: Optional[str] = None + """Error message if the server could not list tools.""" + + +class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + + id: str + """The unique ID of the approval request.""" + + arguments: str + """A JSON string of arguments for the tool.""" + + name: str + """The name of the tool to run.""" + + server_label: str + """The label of the MCP server making the request.""" + + type: Literal["mcp_approval_request"] + """The type of the item. Always `mcp_approval_request`.""" + + +class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + + id: str + """The unique ID of the approval response""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + reason: Optional[str] = None + """Optional reason for the decision.""" + + +ResponseOutputItem: TypeAlias = Annotated[ + Union[ + ResponseOutputMessage, + ResponseFileSearchToolCall, + ResponseFunctionToolCall, + ResponseFunctionToolCallOutputItem, + ResponseFunctionWebSearch, + ResponseComputerToolCall, + ResponseComputerToolCallOutputItem, + ResponseReasoningItem, + Program, + ProgramOutput, + ResponseToolSearchCall, + ResponseToolSearchOutputItem, + AdditionalTools, + ResponseCompactionItem, + ImageGenerationCall, + ResponseCodeInterpreterToolCall, + LocalShellCall, + LocalShellCallOutput, + ResponseFunctionShellToolCall, + ResponseFunctionShellToolCallOutput, + ResponseApplyPatchToolCall, + ResponseApplyPatchToolCallOutput, + McpCall, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + ResponseCustomToolCall, + ResponseCustomToolCallOutputItem, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_output_item_added_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_item_added_event.py new file mode 100644 index 0000000..36ac0e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_item_added_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_output_item import ResponseOutputItem + +__all__ = ["ResponseOutputItemAddedEvent"] + + +class ResponseOutputItemAddedEvent(BaseModel): + """Emitted when a new output item is added.""" + + item: ResponseOutputItem + """The output item that was added. + + For reasoning items, `encrypted_content` may be incomplete while the item is in + progress. Use the reasoning item from the corresponding + `response.output_item.done` event when passing it as input to a subsequent + request. + """ + + output_index: int + """The index of the output item that was added.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.output_item.added"] + """The type of the event. Always `response.output_item.added`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_output_item_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_item_done_event.py new file mode 100644 index 0000000..7609337 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_item_done_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_output_item import ResponseOutputItem + +__all__ = ["ResponseOutputItemDoneEvent"] + + +class ResponseOutputItemDoneEvent(BaseModel): + """Emitted when an output item is marked done.""" + + item: ResponseOutputItem + """The output item that was marked done.""" + + output_index: int + """The index of the output item that was marked done.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.output_item.done"] + """The type of the event. Always `response.output_item.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_output_message.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_message.py new file mode 100644 index 0000000..d041df6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_message.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .response_output_text import ResponseOutputText +from .response_output_refusal import ResponseOutputRefusal + +__all__ = ["ResponseOutputMessage", "Content"] + +Content: TypeAlias = Annotated[Union[ResponseOutputText, ResponseOutputRefusal], PropertyInfo(discriminator="type")] + + +class ResponseOutputMessage(BaseModel): + """An output message from the model.""" + + id: str + """The unique ID of the output message.""" + + content: List[Content] + """The content of the output message.""" + + role: Literal["assistant"] + """The role of the output message. Always `assistant`.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + type: Literal["message"] + """The type of the output message. Always `message`.""" + + phase: Optional[Literal["commentary", "final_answer"]] = None + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_output_message_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_message_param.py new file mode 100644 index 0000000..4552d9b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_message_param.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .response_output_text_param import ResponseOutputTextParam +from .response_output_refusal_param import ResponseOutputRefusalParam + +__all__ = ["ResponseOutputMessageParam", "Content"] + +Content: TypeAlias = Union[ResponseOutputTextParam, ResponseOutputRefusalParam] + + +class ResponseOutputMessageParam(TypedDict, total=False): + """An output message from the model.""" + + id: Required[str] + """The unique ID of the output message.""" + + content: Required[Iterable[Content]] + """The content of the output message.""" + + role: Required[Literal["assistant"]] + """The role of the output message. Always `assistant`.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + type: Required[Literal["message"]] + """The type of the output message. Always `message`.""" + + phase: Optional[Literal["commentary", "final_answer"]] + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_output_refusal.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_refusal.py new file mode 100644 index 0000000..54e7271 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_refusal.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseOutputRefusal"] + + +class ResponseOutputRefusal(BaseModel): + """A refusal from the model.""" + + refusal: str + """The refusal explanation from the model.""" + + type: Literal["refusal"] + """The type of the refusal. Always `refusal`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_output_refusal_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_refusal_param.py new file mode 100644 index 0000000..b5687f1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_refusal_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseOutputRefusalParam"] + + +class ResponseOutputRefusalParam(TypedDict, total=False): + """A refusal from the model.""" + + refusal: Required[str] + """The refusal explanation from the model.""" + + type: Required[Literal["refusal"]] + """The type of the refusal. Always `refusal`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_output_text.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_text.py new file mode 100644 index 0000000..7bcfe59 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_text.py @@ -0,0 +1,131 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "ResponseOutputText", + "Annotation", + "AnnotationFileCitation", + "AnnotationURLCitation", + "AnnotationContainerFileCitation", + "AnnotationFilePath", + "Logprob", + "LogprobTopLogprob", +] + + +class AnnotationFileCitation(BaseModel): + """A citation to a file.""" + + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the file cited.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_citation"] + """The type of the file citation. Always `file_citation`.""" + + +class AnnotationURLCitation(BaseModel): + """A citation for a web resource used to generate a model response.""" + + end_index: int + """The index of the last character of the URL citation in the message.""" + + start_index: int + """The index of the first character of the URL citation in the message.""" + + title: str + """The title of the web resource.""" + + type: Literal["url_citation"] + """The type of the URL citation. Always `url_citation`.""" + + url: str + """The URL of the web resource.""" + + +class AnnotationContainerFileCitation(BaseModel): + """A citation for a container file used to generate a model response.""" + + container_id: str + """The ID of the container file.""" + + end_index: int + """The index of the last character of the container file citation in the message.""" + + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the container file cited.""" + + start_index: int + """The index of the first character of the container file citation in the message.""" + + type: Literal["container_file_citation"] + """The type of the container file citation. Always `container_file_citation`.""" + + +class AnnotationFilePath(BaseModel): + """A path to a file.""" + + file_id: str + """The ID of the file.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_path"] + """The type of the file path. Always `file_path`.""" + + +Annotation: TypeAlias = Annotated[ + Union[AnnotationFileCitation, AnnotationURLCitation, AnnotationContainerFileCitation, AnnotationFilePath], + PropertyInfo(discriminator="type"), +] + + +class LogprobTopLogprob(BaseModel): + """The top log probability of a token.""" + + token: str + + bytes: List[int] + + logprob: float + + +class Logprob(BaseModel): + """The log probability of a token.""" + + token: str + + bytes: List[int] + + logprob: float + + top_logprobs: List[LogprobTopLogprob] + + +class ResponseOutputText(BaseModel): + """A text output from the model.""" + + annotations: List[Annotation] + """The annotations of the text output.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + logprobs: Optional[List[Logprob]] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_output_text_annotation_added_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_text_annotation_added_event.py new file mode 100644 index 0000000..5bc6608 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_text_annotation_added_event.py @@ -0,0 +1,117 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "ResponseOutputTextAnnotationAddedEvent", + "Annotation", + "AnnotationFileCitation", + "AnnotationURLCitation", + "AnnotationContainerFileCitation", + "AnnotationFilePath", +] + + +class AnnotationFileCitation(BaseModel): + """A citation to a file.""" + + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the file cited.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_citation"] + """The type of the file citation. Always `file_citation`.""" + + +class AnnotationURLCitation(BaseModel): + """A citation for a web resource used to generate a model response.""" + + end_index: int + """The index of the last character of the URL citation in the message.""" + + start_index: int + """The index of the first character of the URL citation in the message.""" + + title: str + """The title of the web resource.""" + + type: Literal["url_citation"] + """The type of the URL citation. Always `url_citation`.""" + + url: str + """The URL of the web resource.""" + + +class AnnotationContainerFileCitation(BaseModel): + """A citation for a container file used to generate a model response.""" + + container_id: str + """The ID of the container file.""" + + end_index: int + """The index of the last character of the container file citation in the message.""" + + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the container file cited.""" + + start_index: int + """The index of the first character of the container file citation in the message.""" + + type: Literal["container_file_citation"] + """The type of the container file citation. Always `container_file_citation`.""" + + +class AnnotationFilePath(BaseModel): + """A path to a file.""" + + file_id: str + """The ID of the file.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_path"] + """The type of the file path. Always `file_path`.""" + + +Annotation: TypeAlias = Annotated[ + Union[AnnotationFileCitation, AnnotationURLCitation, AnnotationContainerFileCitation, AnnotationFilePath, None], + PropertyInfo(discriminator="type"), +] + + +class ResponseOutputTextAnnotationAddedEvent(BaseModel): + """Emitted when an annotation is added to output text content.""" + + annotation: Optional[Annotation] = None + """The annotation object being added. (See annotation schema for details.)""" + + annotation_index: int + """The index of the annotation within the content part.""" + + content_index: int + """The index of the content part within the output item.""" + + item_id: str + """The unique identifier of the item to which the annotation is being added.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.output_text.annotation.added"] + """The type of the event. Always 'response.output_text.annotation.added'.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_output_text_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_text_param.py new file mode 100644 index 0000000..abcd16e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_output_text_param.py @@ -0,0 +1,129 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "ResponseOutputTextParam", + "Annotation", + "AnnotationFileCitation", + "AnnotationURLCitation", + "AnnotationContainerFileCitation", + "AnnotationFilePath", + "Logprob", + "LogprobTopLogprob", +] + + +class AnnotationFileCitation(TypedDict, total=False): + """A citation to a file.""" + + file_id: Required[str] + """The ID of the file.""" + + filename: Required[str] + """The filename of the file cited.""" + + index: Required[int] + """The index of the file in the list of files.""" + + type: Required[Literal["file_citation"]] + """The type of the file citation. Always `file_citation`.""" + + +class AnnotationURLCitation(TypedDict, total=False): + """A citation for a web resource used to generate a model response.""" + + end_index: Required[int] + """The index of the last character of the URL citation in the message.""" + + start_index: Required[int] + """The index of the first character of the URL citation in the message.""" + + title: Required[str] + """The title of the web resource.""" + + type: Required[Literal["url_citation"]] + """The type of the URL citation. Always `url_citation`.""" + + url: Required[str] + """The URL of the web resource.""" + + +class AnnotationContainerFileCitation(TypedDict, total=False): + """A citation for a container file used to generate a model response.""" + + container_id: Required[str] + """The ID of the container file.""" + + end_index: Required[int] + """The index of the last character of the container file citation in the message.""" + + file_id: Required[str] + """The ID of the file.""" + + filename: Required[str] + """The filename of the container file cited.""" + + start_index: Required[int] + """The index of the first character of the container file citation in the message.""" + + type: Required[Literal["container_file_citation"]] + """The type of the container file citation. Always `container_file_citation`.""" + + +class AnnotationFilePath(TypedDict, total=False): + """A path to a file.""" + + file_id: Required[str] + """The ID of the file.""" + + index: Required[int] + """The index of the file in the list of files.""" + + type: Required[Literal["file_path"]] + """The type of the file path. Always `file_path`.""" + + +Annotation: TypeAlias = Union[ + AnnotationFileCitation, AnnotationURLCitation, AnnotationContainerFileCitation, AnnotationFilePath +] + + +class LogprobTopLogprob(TypedDict, total=False): + """The top log probability of a token.""" + + token: Required[str] + + bytes: Required[Iterable[int]] + + logprob: Required[float] + + +class Logprob(TypedDict, total=False): + """The log probability of a token.""" + + token: Required[str] + + bytes: Required[Iterable[int]] + + logprob: Required[float] + + top_logprobs: Required[Iterable[LogprobTopLogprob]] + + +class ResponseOutputTextParam(TypedDict, total=False): + """A text output from the model.""" + + annotations: Required[Iterable[Annotation]] + """The annotations of the text output.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + logprobs: Iterable[Logprob] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_prompt.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_prompt.py new file mode 100644 index 0000000..e5080af --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_prompt.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Union, Optional +from typing_extensions import TypeAlias + +from ..._models import BaseModel +from .response_input_file import ResponseInputFile +from .response_input_text import ResponseInputText +from .response_input_image import ResponseInputImage + +__all__ = ["ResponsePrompt", "Variables"] + +Variables: TypeAlias = Union[str, ResponseInputText, ResponseInputImage, ResponseInputFile] + + +class ResponsePrompt(BaseModel): + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + id: str + """The unique identifier of the prompt template to use.""" + + variables: Optional[Dict[str, Variables]] = None + """Optional map of values to substitute in for variables in your prompt. + + The substitution values can either be strings, or other Response input types + like images or files. + """ + + version: Optional[str] = None + """Optional version of the prompt template.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_prompt_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_prompt_param.py new file mode 100644 index 0000000..2db1bd1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_prompt_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Required, TypeAlias, TypedDict + +from .response_input_file_param import ResponseInputFileParam +from .response_input_text_param import ResponseInputTextParam +from .response_input_image_param import ResponseInputImageParam + +__all__ = ["ResponsePromptParam", "Variables"] + +Variables: TypeAlias = Union[str, ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam] + + +class ResponsePromptParam(TypedDict, total=False): + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + id: Required[str] + """The unique identifier of the prompt template to use.""" + + variables: Optional[Dict[str, Variables]] + """Optional map of values to substitute in for variables in your prompt. + + The substitution values can either be strings, or other Response input types + like images or files. + """ + + version: Optional[str] + """Optional version of the prompt template.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_queued_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_queued_event.py new file mode 100644 index 0000000..729c164 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_queued_event.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .response import Response +from ..._models import BaseModel + +__all__ = ["ResponseQueuedEvent"] + + +class ResponseQueuedEvent(BaseModel): + """Emitted when a response is queued and waiting to be processed.""" + + response: Response + """The full response object that is queued.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.queued"] + """The type of the event. Always 'response.queued'.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_item.py new file mode 100644 index 0000000..8744204 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_item.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningItem", "Summary", "Content"] + + +class Summary(BaseModel): + """A summary text from the model.""" + + text: str + """A summary of the reasoning output from the model so far.""" + + type: Literal["summary_text"] + """The type of the object. Always `summary_text`.""" + + +class Content(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +class ResponseReasoningItem(BaseModel): + """ + A description of the chain of thought used by a reasoning model while generating + a response. Be sure to include these items in your `input` to the Responses API + for subsequent turns of a conversation if you are manually + [managing context](https://developers.openai.com/api/docs/guides/conversation-state). + """ + + id: str + """The unique identifier of the reasoning content.""" + + summary: List[Summary] + """Reasoning summary content.""" + + type: Literal["reasoning"] + """The type of the object. Always `reasoning`.""" + + content: Optional[List[Content]] = None + """Reasoning text content.""" + + encrypted_content: Optional[str] = None + """The encrypted content of the reasoning item. + + This is populated by default for reasoning items returned by + `POST /v1/responses` and WebSocket `response.create` requests. + + When streaming, use the completed reasoning item and its `encrypted_content` + from the `response.output_item.done` event in subsequent requests. The + `encrypted_content` in `response.output_item.added` may be incomplete. This is + especially important when `store` is `false` or when using Zero Data Retention. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_item_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_item_param.py new file mode 100644 index 0000000..fb1196d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_item_param.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseReasoningItemParam", "Summary", "Content"] + + +class Summary(TypedDict, total=False): + """A summary text from the model.""" + + text: Required[str] + """A summary of the reasoning output from the model so far.""" + + type: Required[Literal["summary_text"]] + """The type of the object. Always `summary_text`.""" + + +class Content(TypedDict, total=False): + """Reasoning text from the model.""" + + text: Required[str] + """The reasoning text from the model.""" + + type: Required[Literal["reasoning_text"]] + """The type of the reasoning text. Always `reasoning_text`.""" + + +class ResponseReasoningItemParam(TypedDict, total=False): + """ + A description of the chain of thought used by a reasoning model while generating + a response. Be sure to include these items in your `input` to the Responses API + for subsequent turns of a conversation if you are manually + [managing context](https://developers.openai.com/api/docs/guides/conversation-state). + """ + + id: Required[str] + """The unique identifier of the reasoning content.""" + + summary: Required[Iterable[Summary]] + """Reasoning summary content.""" + + type: Required[Literal["reasoning"]] + """The type of the object. Always `reasoning`.""" + + content: Iterable[Content] + """Reasoning text content.""" + + encrypted_content: Optional[str] + """The encrypted content of the reasoning item. + + This is populated by default for reasoning items returned by + `POST /v1/responses` and WebSocket `response.create` requests. + + When streaming, use the completed reasoning item and its `encrypted_content` + from the `response.output_item.done` event in subsequent requests. The + `encrypted_content` in `response.output_item.added` may be incomplete. This is + especially important when `store` is `false` or when using Zero Data Retention. + """ + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_summary_part_added_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_summary_part_added_event.py new file mode 100644 index 0000000..1132070 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_summary_part_added_event.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningSummaryPartAddedEvent", "Part"] + + +class Part(BaseModel): + """The summary part that was added.""" + + text: str + """The text of the summary part.""" + + type: Literal["summary_text"] + """The type of the summary part. Always `summary_text`.""" + + +class ResponseReasoningSummaryPartAddedEvent(BaseModel): + """Emitted when a new reasoning summary part is added.""" + + item_id: str + """The ID of the item this summary part is associated with.""" + + output_index: int + """The index of the output item this summary part is associated with.""" + + part: Part + """The summary part that was added.""" + + sequence_number: int + """The sequence number of this event.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + type: Literal["response.reasoning_summary_part.added"] + """The type of the event. Always `response.reasoning_summary_part.added`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_summary_part_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_summary_part_done_event.py new file mode 100644 index 0000000..9ba0f56 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_summary_part_done_event.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningSummaryPartDoneEvent", "Part"] + + +class Part(BaseModel): + """The completed summary part.""" + + text: str + """The text of the summary part.""" + + type: Literal["summary_text"] + """The type of the summary part. Always `summary_text`.""" + + +class ResponseReasoningSummaryPartDoneEvent(BaseModel): + """Emitted when a reasoning summary part is completed.""" + + item_id: str + """The ID of the item this summary part is associated with.""" + + output_index: int + """The index of the output item this summary part is associated with.""" + + part: Part + """The completed summary part.""" + + sequence_number: int + """The sequence number of this event.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + type: Literal["response.reasoning_summary_part.done"] + """The type of the event. Always `response.reasoning_summary_part.done`.""" + + status: Optional[Literal["incomplete"]] = None + """The completion status of the summary part. + + Omitted when the part completed normally and set to `incomplete` when generation + was interrupted. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_summary_text_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_summary_text_delta_event.py new file mode 100644 index 0000000..56f93db --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_summary_text_delta_event.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningSummaryTextDeltaEvent"] + + +class ResponseReasoningSummaryTextDeltaEvent(BaseModel): + """Emitted when a delta is added to a reasoning summary text.""" + + delta: str + """The text delta that was added to the summary.""" + + item_id: str + """The ID of the item this summary text delta is associated with.""" + + output_index: int + """The index of the output item this summary text delta is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + type: Literal["response.reasoning_summary_text.delta"] + """The type of the event. Always `response.reasoning_summary_text.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_summary_text_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_summary_text_done_event.py new file mode 100644 index 0000000..c7c2ac5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_summary_text_done_event.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningSummaryTextDoneEvent"] + + +class ResponseReasoningSummaryTextDoneEvent(BaseModel): + """Emitted when a reasoning summary text is completed.""" + + item_id: str + """The ID of the item this summary text is associated with.""" + + output_index: int + """The index of the output item this summary text is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + text: str + """The full text of the completed reasoning summary.""" + + type: Literal["response.reasoning_summary_text.done"] + """The type of the event. Always `response.reasoning_summary_text.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_text_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_text_delta_event.py new file mode 100644 index 0000000..c75caac --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_text_delta_event.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningTextDeltaEvent"] + + +class ResponseReasoningTextDeltaEvent(BaseModel): + """Emitted when a delta is added to a reasoning text.""" + + content_index: int + """The index of the reasoning content part this delta is associated with.""" + + delta: str + """The text delta that was added to the reasoning content.""" + + item_id: str + """The ID of the item this reasoning text delta is associated with.""" + + output_index: int + """The index of the output item this reasoning text delta is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.reasoning_text.delta"] + """The type of the event. Always `response.reasoning_text.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_text_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_text_done_event.py new file mode 100644 index 0000000..4dc39bb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_reasoning_text_done_event.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningTextDoneEvent"] + + +class ResponseReasoningTextDoneEvent(BaseModel): + """Emitted when a reasoning text is completed.""" + + content_index: int + """The index of the reasoning content part.""" + + item_id: str + """The ID of the item this reasoning text is associated with.""" + + output_index: int + """The index of the output item this reasoning text is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + text: str + """The full text of the completed reasoning content.""" + + type: Literal["response.reasoning_text.done"] + """The type of the event. Always `response.reasoning_text.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_refusal_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_refusal_delta_event.py new file mode 100644 index 0000000..b00f074 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_refusal_delta_event.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseRefusalDeltaEvent"] + + +class ResponseRefusalDeltaEvent(BaseModel): + """Emitted when there is a partial refusal text.""" + + content_index: int + """The index of the content part that the refusal text is added to.""" + + delta: str + """The refusal text that is added.""" + + item_id: str + """The ID of the output item that the refusal text is added to.""" + + output_index: int + """The index of the output item that the refusal text is added to.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.refusal.delta"] + """The type of the event. Always `response.refusal.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_refusal_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_refusal_done_event.py new file mode 100644 index 0000000..abe5b91 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_refusal_done_event.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseRefusalDoneEvent"] + + +class ResponseRefusalDoneEvent(BaseModel): + """Emitted when refusal text is finalized.""" + + content_index: int + """The index of the content part that the refusal text is finalized.""" + + item_id: str + """The ID of the output item that the refusal text is finalized.""" + + output_index: int + """The index of the output item that the refusal text is finalized.""" + + refusal: str + """The refusal text that is finalized.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.refusal.done"] + """The type of the event. Always `response.refusal.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_retrieve_params.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_retrieve_params.py new file mode 100644 index 0000000..b6dae0f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_retrieve_params.py @@ -0,0 +1,59 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import Literal, Required, TypedDict + +from .response_includable import ResponseIncludable + +__all__ = ["ResponseRetrieveParamsBase", "ResponseRetrieveParamsNonStreaming", "ResponseRetrieveParamsStreaming"] + + +class ResponseRetrieveParamsBase(TypedDict, total=False): + include: List[ResponseIncludable] + """Additional fields to include in the response. + + See the `include` parameter for Response creation above for more information. + """ + + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + starting_after: int + """The sequence number of the event after which to start streaming.""" + + +class ResponseRetrieveParamsNonStreaming(ResponseRetrieveParamsBase, total=False): + stream: Literal[False] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + """ + + +class ResponseRetrieveParamsStreaming(ResponseRetrieveParamsBase): + stream: Required[Literal[True]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + """ + + +ResponseRetrieveParams = Union[ResponseRetrieveParamsNonStreaming, ResponseRetrieveParamsStreaming] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_command_added_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_command_added_event.py new file mode 100644 index 0000000..6318d2f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_command_added_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseShellCallCommandAddedEvent"] + + +class ResponseShellCallCommandAddedEvent(BaseModel): + """A streaming event that indicated a shell command was added to a tool call.""" + + command: str + """The shell command that was added.""" + + command_index: int + """The index of the shell command that was added.""" + + output_index: int + """The index of the output item that was updated.""" + + sequence_number: int + """The sequence number of the event that was emitted.""" + + type: Literal["response.shell_call_command.added"] + """The type of the event, always `response.shell_call_command.added`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_command_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_command_delta_event.py new file mode 100644 index 0000000..541bcf0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_command_delta_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseShellCallCommandDeltaEvent"] + + +class ResponseShellCallCommandDeltaEvent(BaseModel): + """A streaming event that indicated a shell command was incrementally updated.""" + + command_index: int + """The index of the shell command that was updated.""" + + delta: str + """The shell command delta that was appended.""" + + output_index: int + """The index of the output item that was updated.""" + + sequence_number: int + """The sequence number of the event that was emitted.""" + + type: Literal["response.shell_call_command.delta"] + """The type of the event, always `response.shell_call_command.delta`.""" + + obfuscation: Optional[str] = None + """An obfuscation string that was added to pad the event payload.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_command_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_command_done_event.py new file mode 100644 index 0000000..8cd667b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_command_done_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseShellCallCommandDoneEvent"] + + +class ResponseShellCallCommandDoneEvent(BaseModel): + """A streaming event that indicated a shell command was completed.""" + + command: str + """The final shell command that was emitted.""" + + command_index: int + """The index of the shell command that was completed.""" + + output_index: int + """The index of the output item that was updated.""" + + sequence_number: int + """The sequence number of the event that was emitted.""" + + type: Literal["response.shell_call_command.done"] + """The type of the event, always `response.shell_call_command.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_output_content_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_output_content_delta_event.py new file mode 100644 index 0000000..1bcf7fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_output_content_delta_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseShellCallOutputContentDeltaEvent", "Delta"] + + +class Delta(BaseModel): + """The stdout/stderr delta that was emitted.""" + + stderr: Optional[str] = None + """The stderr delta that was emitted.""" + + stdout: Optional[str] = None + """The stdout delta that was emitted.""" + + +class ResponseShellCallOutputContentDeltaEvent(BaseModel): + """A streaming event that indicated shell call output was incrementally added.""" + + command_index: int + """The index of the shell command that produced output.""" + + delta: Delta + """The stdout/stderr delta that was emitted.""" + + item_id: str + """The ID of the output item that was updated.""" + + output_index: int + """The index of the output item that was updated.""" + + sequence_number: int + """The sequence number of the event that was emitted.""" + + type: Literal["response.shell_call_output_content.delta"] + """The type of the event, always `response.shell_call_output_content.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_output_content_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_output_content_done_event.py new file mode 100644 index 0000000..d67f61d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_shell_call_output_content_done_event.py @@ -0,0 +1,76 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "ResponseShellCallOutputContentDoneEvent", + "Output", + "OutputOutcome", + "OutputOutcomeTimeout", + "OutputOutcomeExit", +] + + +class OutputOutcomeTimeout(BaseModel): + """Indicates that the shell call exceeded its configured time limit.""" + + type: Literal["timeout"] + """The outcome type. Always `timeout`.""" + + +class OutputOutcomeExit(BaseModel): + """Indicates that the shell commands finished and returned an exit code.""" + + exit_code: int + """Exit code from the shell process.""" + + type: Literal["exit"] + """The outcome type. Always `exit`.""" + + +OutputOutcome: TypeAlias = Annotated[Union[OutputOutcomeTimeout, OutputOutcomeExit], PropertyInfo(discriminator="type")] + + +class Output(BaseModel): + """The content of a shell tool call output that was emitted.""" + + outcome: OutputOutcome + """ + Represents either an exit outcome (with an exit code) or a timeout outcome for a + shell call output chunk. + """ + + stderr: str + """The standard error output that was captured.""" + + stdout: str + """The standard output that was captured.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" + + +class ResponseShellCallOutputContentDoneEvent(BaseModel): + """A streaming event that indicated shell call output was completed.""" + + command_index: int + """The index of the shell command that produced output.""" + + item_id: str + """The ID of the output item that was updated.""" + + output: List[Output] + """The output contents emitted for the shell command.""" + + output_index: int + """The index of the output item that was updated.""" + + sequence_number: int + """The sequence number of the event that was emitted.""" + + type: Literal["response.shell_call_output_content.done"] + """The type of the event, always `response.shell_call_output_content.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_status.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_status.py new file mode 100644 index 0000000..4878564 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_status.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ResponseStatus"] + +ResponseStatus: TypeAlias = Literal["completed", "failed", "in_progress", "cancelled", "queued", "incomplete"] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_accepted_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_accepted_event.py new file mode 100644 index 0000000..0315cb2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_accepted_event.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseSteerAcceptedEvent", "Steer"] + + +class Steer(BaseModel): + """The accepted steering submission.""" + + id: str + """The ID assigned to the steering submission.""" + + previous_response_id: str + """The ID of the response being steered.""" + + +class ResponseSteerAcceptedEvent(BaseModel): + """Emitted when steering input has been validated and queued. + + Acceptance means + the server owns the input, not that it has been applied. The successor's + `response.created` event is the commit point. If accepted input cannot be + committed, `response.steer.failed` returns it with the same steering ID. + + When the response stops for client-owned tool output or approval, the input + remains queued and `response.steer.pending` is emitted after + `response.completed`. Fill the pending event's `required_input` stubs with + saved results and send one matching explicit `response.create` per parent. + Do not resend accepted input while it is still queued. + """ + + sequence_number: int + """The sequence number for this event.""" + + steer: Steer + """The accepted steering submission.""" + + type: Literal["response.steer.accepted"] + """The event discriminator. Always `response.steer.accepted`.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the target response's `response.create` event + supplied a `stream_id`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_error_code.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_error_code.py new file mode 100644 index 0000000..ee83af9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_error_code.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +__all__ = ["ResponseSteerErrorCode"] + +ResponseSteerErrorCode: TypeAlias = Union[ + Literal[ + "response_not_found", + "invalid_input", + "steering_not_supported", + "too_many_pending_steers", + "response_already_completed", + "response_not_active", + "successor_creation_failed", + ], + str, +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_event.py new file mode 100644 index 0000000..9367f9d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_event.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_steer_input import ResponseSteerInput + +__all__ = ["ResponseSteerEvent"] + + +class ResponseSteerEvent(BaseModel): + """Queues user input to steer a response on this WebSocket connection. + + Input + can contain text, images, and files. Steering is supported only for + single-agent responses on models and execution modes that support steering. + Responses bound to a conversation or using automatic compaction do not + support steering. + + A `response.steer.accepted` event acknowledges that the server owns the + queued input, not that it has been applied. The successor's `response.created` + event is the commit point. Input that cannot be committed is returned in + `response.steer.failed`. + + Steering may cause the active response to finish at a safe output boundary + with `response.incomplete` and `incomplete_details.reason` set to `steered`, + followed automatically by a successor `response.created`. Normal completion + can also be followed by an automatic successor. Automatic successors inherit + the previous response's settings and continue from it with the queued input. + + If the response stops for client-owned tool output or approval, accepted + steering input remains queued and `response.steer.pending` is emitted after + `response.completed`. Fill the `required_input` stubs from that event with + saved tool results or approval decisions, and send one explicit + `response.create` per parent with the same `previous_response_id` and + WebSocket lane. Do not rerun tools or resend accepted steering input. The + queued input is prepended in submission order to that request's input, and + the explicit request retains its own settings. + + This event accepts only `type`, `previous_response_id`, and `input`. Do not + send `stream_id`; the target response determines the WebSocket lane. + """ + + input: ResponseSteerInput + """Input to queue for a continuation of the response. + + Uses the same string or input-item shape as `response.create.input`, with a + non-empty array when supplying input items. + + Steering accepts only messages with the `user` role. Each message may contain + only `type`, `role`, and `content`, with `content` as a string or an array of + `input_text`, `input_image`, and `input_file` parts. The optional `type` must be + `message`. Other roles, tool outputs, and item types are not supported for + steering. + """ + + previous_response_id: str + """The ID of the response to steer on this WebSocket connection.""" + + type: Literal["response.steer"] + """The event discriminator. Always `response.steer`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_event_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_event_param.py new file mode 100644 index 0000000..5a6ae1a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_event_param.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .response_steer_input_param import ResponseSteerInputParam + +__all__ = ["ResponseSteerEventParam"] + + +class ResponseSteerEventParam(TypedDict, total=False): + """Queues user input to steer a response on this WebSocket connection. + + Input + can contain text, images, and files. Steering is supported only for + single-agent responses on models and execution modes that support steering. + Responses bound to a conversation or using automatic compaction do not + support steering. + + A `response.steer.accepted` event acknowledges that the server owns the + queued input, not that it has been applied. The successor's `response.created` + event is the commit point. Input that cannot be committed is returned in + `response.steer.failed`. + + Steering may cause the active response to finish at a safe output boundary + with `response.incomplete` and `incomplete_details.reason` set to `steered`, + followed automatically by a successor `response.created`. Normal completion + can also be followed by an automatic successor. Automatic successors inherit + the previous response's settings and continue from it with the queued input. + + If the response stops for client-owned tool output or approval, accepted + steering input remains queued and `response.steer.pending` is emitted after + `response.completed`. Fill the `required_input` stubs from that event with + saved tool results or approval decisions, and send one explicit + `response.create` per parent with the same `previous_response_id` and + WebSocket lane. Do not rerun tools or resend accepted steering input. The + queued input is prepended in submission order to that request's input, and + the explicit request retains its own settings. + + This event accepts only `type`, `previous_response_id`, and `input`. Do not + send `stream_id`; the target response determines the WebSocket lane. + """ + + input: Required[ResponseSteerInputParam] + """Input to queue for a continuation of the response. + + Uses the same string or input-item shape as `response.create.input`, with a + non-empty array when supplying input items. + + Steering accepts only messages with the `user` role. Each message may contain + only `type`, `role`, and `content`, with `content` as a string or an array of + `input_text`, `input_image`, and `input_file` parts. The optional `type` must be + `message`. Other roles, tool outputs, and item types are not supported for + steering. + """ + + previous_response_id: Required[str] + """The ID of the response to steer on this WebSocket connection.""" + + type: Required[Literal["response.steer"]] + """The event discriminator. Always `response.steer`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_failed_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_failed_event.py new file mode 100644 index 0000000..2eb4c78 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_failed_event.py @@ -0,0 +1,92 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_steer_input import ResponseSteerInput +from .response_steer_error_code import ResponseSteerErrorCode + +__all__ = ["ResponseSteerFailedEvent", "Error", "Steer"] + + +class Error(BaseModel): + """Information about why the input could not be committed.""" + + code: ResponseSteerErrorCode + """A machine-readable steering error code. + + Clients should handle unknown values because additional codes may be introduced. + Known values include: + + - `response_not_found`: The target response is not available on this connection. + - `invalid_input`: The event or input failed validation. + - `steering_not_supported`: The model or response execution mode does not + support steering. + - `too_many_pending_steers`: Too much steering input is pending for the + response. + - `response_already_completed`: The response completed and is no longer + accepting steering input. + - `response_not_active`: The response is no longer accepting steering input. + - `successor_creation_failed`: The successor response could not be created. + """ + + message: str + """A human-readable description of the error.""" + + type: Literal["invalid_request_error"] + """The error type. Always `invalid_request_error`.""" + + +class Steer(BaseModel): + """The steering submission that could not be committed.""" + + input: ResponseSteerInput + """Input to queue for a continuation of the response. + + Uses the same string or input-item shape as `response.create.input`, with a + non-empty array when supplying input items. + + Steering accepts only messages with the `user` role. Each message may contain + only `type`, `role`, and `content`, with `content` as a string or an array of + `input_text`, `input_image`, and `input_file` parts. The optional `type` must be + `message`. Other roles, tool outputs, and item types are not supported for + steering. + """ + + previous_response_id: str + """The ID of the response that was targeted for steering.""" + + id: Optional[str] = None + """The ID assigned to the steering submission, if one was allocated.""" + + +class ResponseSteerFailedEvent(BaseModel): + """ + Emitted when steering input is rejected or cannot be committed to a + successor response. Returns the original, uncommitted input so the client + can carry it into `response.create` when appropriate. Invalid input must + be corrected before retrying. + + Failures after acceptance include the same steering ID. Failures before an + ID is allocated omit `steer.id`. A lost connection or missing acknowledgement + leaves the outcome unknown; it is not proof that the input was rejected. + """ + + error: Error + """Information about why the input could not be committed.""" + + sequence_number: int + """The sequence number for this event.""" + + steer: Steer + """The steering submission that could not be committed.""" + + type: Literal["response.steer.failed"] + """The event discriminator. Always `response.steer.failed`.""" + + stream_id: Optional[str] = None + """ + The WebSocket lane that emitted this event, when the target response is + available and its `response.create` event supplied a `stream_id`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_input.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_input.py new file mode 100644 index 0000000..78426ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_input.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .response_steer_input_content import ResponseSteerInputContent +from .response_function_call_output_item_list import ResponseFunctionCallOutputItemList + +__all__ = [ + "ResponseSteerInput", + "ResponseSteerInputItemList", + "ResponseSteerInputItemListMessage", + "ResponseSteerInputItemListFunctionCallOutput", + "ResponseSteerInputItemListFunctionCallOutputCaller", + "ResponseSteerInputItemListFunctionCallOutputCallerDirect", + "ResponseSteerInputItemListFunctionCallOutputCallerProgram", +] + + +class ResponseSteerInputItemListMessage(BaseModel): + content: Union[List[ResponseSteerInputContent], str] + """The message content, as an array of content parts.""" + + role: Literal["user"] + """The message role. Always `user`.""" + + type: Literal["message"] + """The item type. Always `message`.""" + + id: Optional[str] = None + """The unique ID of this message item.""" + + status: Optional[str] = None + """The status of the message item.""" + + +class ResponseSteerInputItemListFunctionCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ResponseSteerInputItemListFunctionCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ResponseSteerInputItemListFunctionCallOutputCaller: TypeAlias = Annotated[ + Union[ + ResponseSteerInputItemListFunctionCallOutputCallerDirect, + ResponseSteerInputItemListFunctionCallOutputCallerProgram, + None, + ], + PropertyInfo(discriminator="type"), +] + + +class ResponseSteerInputItemListFunctionCallOutput(BaseModel): + """The output of a function tool call.""" + + output: Union[str, ResponseFunctionCallOutputItemList] + """Text, image, or file output of the function tool call.""" + + type: Literal["function_call_output"] + """The type of the function tool call output. Always `function_call_output`.""" + + id: Optional[str] = None + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + call_id: Optional[str] = None + """The unique ID of the function tool call generated by the model.""" + + caller: Optional[ResponseSteerInputItemListFunctionCallOutputCaller] = None + """The execution context that produced this tool call.""" + + name: Optional[str] = None + """The name of the tool that produced the output.""" + + namespace: Optional[str] = None + """The namespace of the tool that produced the output.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +ResponseSteerInputItemList: TypeAlias = Annotated[ + Union[ResponseSteerInputItemListMessage, ResponseSteerInputItemListFunctionCallOutput], + PropertyInfo(discriminator="type"), +] + +ResponseSteerInput: TypeAlias = Union[str, List[ResponseSteerInputItemList]] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_input_content.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_input_content.py new file mode 100644 index 0000000..3cdbdd4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_input_content.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .response_input_file_content import ResponseInputFileContent +from .response_input_text_content import ResponseInputTextContent +from .response_input_image_content import ResponseInputImageContent + +__all__ = ["ResponseSteerInputContent"] + +ResponseSteerInputContent: TypeAlias = Annotated[ + Union[ResponseInputTextContent, ResponseInputImageContent, ResponseInputFileContent], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_input_content_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_input_content_param.py new file mode 100644 index 0000000..9dd806b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_input_content_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .response_input_file_content_param import ResponseInputFileContentParam +from .response_input_text_content_param import ResponseInputTextContentParam +from .response_input_image_content_param import ResponseInputImageContentParam + +__all__ = ["ResponseSteerInputContentParam"] + +ResponseSteerInputContentParam: TypeAlias = Union[ + ResponseInputTextContentParam, ResponseInputImageContentParam, ResponseInputFileContentParam +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_input_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_input_param.py new file mode 100644 index 0000000..dcaa31d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_input_param.py @@ -0,0 +1,96 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .response_steer_input_content_param import ResponseSteerInputContentParam +from .response_function_call_output_item_list_param import ResponseFunctionCallOutputItemListParam + +__all__ = [ + "ResponseSteerInputParam", + "ResponseSteerInputItemList", + "ResponseSteerInputItemListMessage", + "ResponseSteerInputItemListFunctionCallOutput", + "ResponseSteerInputItemListFunctionCallOutputCaller", + "ResponseSteerInputItemListFunctionCallOutputCallerDirect", + "ResponseSteerInputItemListFunctionCallOutputCallerProgram", +] + + +class ResponseSteerInputItemListMessage(TypedDict, total=False): + content: Required[Union[Iterable[ResponseSteerInputContentParam], str]] + """The message content, as an array of content parts.""" + + role: Required[Literal["user"]] + """The message role. Always `user`.""" + + type: Required[Literal["message"]] + """The item type. Always `message`.""" + + id: Optional[str] + """The unique ID of this message item.""" + + status: Optional[str] + """The status of the message item.""" + + +class ResponseSteerInputItemListFunctionCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ResponseSteerInputItemListFunctionCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ResponseSteerInputItemListFunctionCallOutputCaller: TypeAlias = Union[ + ResponseSteerInputItemListFunctionCallOutputCallerDirect, ResponseSteerInputItemListFunctionCallOutputCallerProgram +] + + +class ResponseSteerInputItemListFunctionCallOutput(TypedDict, total=False): + """The output of a function tool call.""" + + output: Required[Union[str, ResponseFunctionCallOutputItemListParam]] + """Text, image, or file output of the function tool call.""" + + type: Required[Literal["function_call_output"]] + """The type of the function tool call output. Always `function_call_output`.""" + + id: Optional[str] + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + call_id: Optional[str] + """The unique ID of the function tool call generated by the model.""" + + caller: Optional[ResponseSteerInputItemListFunctionCallOutputCaller] + """The execution context that produced this tool call.""" + + name: Optional[str] + """The name of the tool that produced the output.""" + + namespace: Optional[str] + """The namespace of the tool that produced the output.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +ResponseSteerInputItemList: TypeAlias = Union[ + ResponseSteerInputItemListMessage, ResponseSteerInputItemListFunctionCallOutput +] + +ResponseSteerInputParam: TypeAlias = Union[str, Iterable[ResponseSteerInputItemList]] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_pending_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_pending_event.py new file mode 100644 index 0000000..3d35f3e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_pending_event.py @@ -0,0 +1,74 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_steer_pending_reason import ResponseSteerPendingReason +from .response_steer_required_input import ResponseSteerRequiredInput + +__all__ = ["ResponseSteerPendingEvent", "Steer"] + + +class Steer(BaseModel): + """The steering submission that remains queued.""" + + id: str + """The ID assigned to the steering submission.""" + + previous_response_id: str + """The ID of the response being steered.""" + + +class ResponseSteerPendingEvent(BaseModel): + """ + Emitted when accepted steering input remains queued after the target + response completes. The server still owns the input. Do not resend it. + The successor's `response.created` event is the commit point. + + When `reason` is `waiting_for_required_input`, this event follows + `response.completed` while the response waits for the tool results or + approval decisions identified by `required_input`. Copy those stubs, fill + their result fields using the ordinary `response.create` input schemas, + and submit one continuation per parent with the same `previous_response_id` + and WebSocket lane. Use saved results without rerunning tools. The queued + steering input is prepended in submission order to the continuation's + input. That explicit request retains its own settings. + + This notification is emitted at most once per steering submission. Multiple + submissions for the same parent can report the same required inputs; they + do not each require a separate continuation. + """ + + reason: ResponseSteerPendingReason + """ + An extensible enum describing why accepted steering input is still queued. + Clients should handle unknown values because additional reasons may be + introduced. Known values include: + + - `waiting_for_required_input`: The response is waiting for the tool results or + approval decisions identified by `required_input`. + """ + + required_input: List[ResponseSteerRequiredInput] + """ + Input stubs identifying outstanding client-owned tool results or approval + decisions. Each stub contains identifying fields only; the client supplies the + result before including it in `response.create`. + """ + + sequence_number: int + """The sequence number for this event.""" + + steer: Steer + """The steering submission that remains queued.""" + + type: Literal["response.steer.pending"] + """The event discriminator. Always `response.steer.pending`.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the target response's `response.create` event + supplied a `stream_id`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_pending_reason.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_pending_reason.py new file mode 100644 index 0000000..cafd06c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_pending_reason.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +__all__ = ["ResponseSteerPendingReason"] + +ResponseSteerPendingReason: TypeAlias = Union[Literal["waiting_for_required_input"], str] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_required_input.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_required_input.py new file mode 100644 index 0000000..9c1c19b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_steer_required_input.py @@ -0,0 +1,114 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "ResponseSteerRequiredInput", + "FunctionCallOutput", + "CustomToolCallOutput", + "ComputerCallOutput", + "ShellCallOutput", + "ApplyPatchCallOutput", + "ToolSearchOutput", + "McpApprovalResponse", +] + + +class FunctionCallOutput(BaseModel): + """Supply `output` using the function tool call output input schema.""" + + call_id: str + + name: str + + type: Literal["function_call_output"] + + +class CustomToolCallOutput(BaseModel): + """Supply `output` using the custom tool call output input schema. + + The + original custom tool call supplies the tool's name. + """ + + call_id: str + + type: Literal["custom_tool_call_output"] + + +class ComputerCallOutput(BaseModel): + """ + Supply `output` using the computer tool call output input schema, + including any required `acknowledged_safety_checks`. + """ + + call_id: str + + type: Literal["computer_call_output"] + + +class ShellCallOutput(BaseModel): + """Supply `output` using the shell tool call output input schema. + + Each + output entry includes `stdout`, `stderr`, and `outcome`. + """ + + call_id: str + + type: Literal["shell_call_output"] + + +class ApplyPatchCallOutput(BaseModel): + """ + Supply `status` and optional `output` using the apply patch tool call + output input schema. + """ + + call_id: str + + type: Literal["apply_patch_call_output"] + + +class ToolSearchOutput(BaseModel): + """ + Supply `tools` using the tool search output input schema, retaining + `execution: "client"`. + """ + + call_id: str + + execution: Literal["client"] + + type: Literal["tool_search_output"] + + +class McpApprovalResponse(BaseModel): + """Supply `approve` using the MCP approval response input schema. + + An + optional `reason` can be supplied when denying the request. The original + approval request identifies the tool and server. + """ + + approval_request_id: str + + type: Literal["mcp_approval_response"] + + +ResponseSteerRequiredInput: TypeAlias = Annotated[ + Union[ + FunctionCallOutput, + CustomToolCallOutput, + ComputerCallOutput, + ShellCallOutput, + ApplyPatchCallOutput, + ToolSearchOutput, + McpApprovalResponse, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_stream_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_stream_event.py new file mode 100644 index 0000000..a396c9c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_stream_event.py @@ -0,0 +1,132 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .response_error_event import ResponseErrorEvent +from .response_failed_event import ResponseFailedEvent +from .response_queued_event import ResponseQueuedEvent +from .response_created_event import ResponseCreatedEvent +from .response_completed_event import ResponseCompletedEvent +from .response_text_done_event import ResponseTextDoneEvent +from .response_audio_done_event import ResponseAudioDoneEvent +from .response_incomplete_event import ResponseIncompleteEvent +from .response_text_delta_event import ResponseTextDeltaEvent +from .response_audio_delta_event import ResponseAudioDeltaEvent +from .response_in_progress_event import ResponseInProgressEvent +from .response_refusal_done_event import ResponseRefusalDoneEvent +from .response_refusal_delta_event import ResponseRefusalDeltaEvent +from .response_mcp_call_failed_event import ResponseMcpCallFailedEvent +from .response_output_item_done_event import ResponseOutputItemDoneEvent +from .response_content_part_done_event import ResponseContentPartDoneEvent +from .response_output_item_added_event import ResponseOutputItemAddedEvent +from .response_content_part_added_event import ResponseContentPartAddedEvent +from .response_mcp_call_completed_event import ResponseMcpCallCompletedEvent +from .response_reasoning_text_done_event import ResponseReasoningTextDoneEvent +from .response_mcp_call_in_progress_event import ResponseMcpCallInProgressEvent +from .response_reasoning_text_delta_event import ResponseReasoningTextDeltaEvent +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent +from .response_compaction_compacting_event import ResponseCompactionCompactingEvent +from .response_mcp_list_tools_failed_event import ResponseMcpListToolsFailedEvent +from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent +from .response_mcp_call_arguments_done_event import ResponseMcpCallArgumentsDoneEvent +from .response_shell_call_command_done_event import ResponseShellCallCommandDoneEvent +from .response_image_gen_call_completed_event import ResponseImageGenCallCompletedEvent +from .response_mcp_call_arguments_delta_event import ResponseMcpCallArgumentsDeltaEvent +from .response_mcp_list_tools_completed_event import ResponseMcpListToolsCompletedEvent +from .response_shell_call_command_added_event import ResponseShellCallCommandAddedEvent +from .response_shell_call_command_delta_event import ResponseShellCallCommandDeltaEvent +from .response_image_gen_call_generating_event import ResponseImageGenCallGeneratingEvent +from .response_web_search_call_completed_event import ResponseWebSearchCallCompletedEvent +from .response_web_search_call_searching_event import ResponseWebSearchCallSearchingEvent +from .response_file_search_call_completed_event import ResponseFileSearchCallCompletedEvent +from .response_file_search_call_searching_event import ResponseFileSearchCallSearchingEvent +from .response_image_gen_call_in_progress_event import ResponseImageGenCallInProgressEvent +from .response_mcp_list_tools_in_progress_event import ResponseMcpListToolsInProgressEvent +from .response_custom_tool_call_input_done_event import ResponseCustomToolCallInputDoneEvent +from .response_reasoning_summary_part_done_event import ResponseReasoningSummaryPartDoneEvent +from .response_reasoning_summary_text_done_event import ResponseReasoningSummaryTextDoneEvent +from .response_web_search_call_in_progress_event import ResponseWebSearchCallInProgressEvent +from .response_custom_tool_call_input_delta_event import ResponseCustomToolCallInputDeltaEvent +from .response_file_search_call_in_progress_event import ResponseFileSearchCallInProgressEvent +from .response_function_call_arguments_done_event import ResponseFunctionCallArgumentsDoneEvent +from .response_image_gen_call_partial_image_event import ResponseImageGenCallPartialImageEvent +from .response_output_text_annotation_added_event import ResponseOutputTextAnnotationAddedEvent +from .response_reasoning_summary_part_added_event import ResponseReasoningSummaryPartAddedEvent +from .response_reasoning_summary_text_delta_event import ResponseReasoningSummaryTextDeltaEvent +from .response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent +from .response_shell_call_output_content_done_event import ResponseShellCallOutputContentDoneEvent +from .response_code_interpreter_call_code_done_event import ResponseCodeInterpreterCallCodeDoneEvent +from .response_code_interpreter_call_completed_event import ResponseCodeInterpreterCallCompletedEvent +from .response_shell_call_output_content_delta_event import ResponseShellCallOutputContentDeltaEvent +from .response_code_interpreter_call_code_delta_event import ResponseCodeInterpreterCallCodeDeltaEvent +from .response_code_interpreter_call_in_progress_event import ResponseCodeInterpreterCallInProgressEvent +from .response_code_interpreter_call_interpreting_event import ResponseCodeInterpreterCallInterpretingEvent + +__all__ = ["ResponseStreamEvent"] + +ResponseStreamEvent: TypeAlias = Annotated[ + Union[ + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseCodeInterpreterCallCodeDeltaEvent, + ResponseCodeInterpreterCallCodeDoneEvent, + ResponseCodeInterpreterCallCompletedEvent, + ResponseCodeInterpreterCallInProgressEvent, + ResponseCodeInterpreterCallInterpretingEvent, + ResponseCompactionCompactingEvent, + ResponseCompletedEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreatedEvent, + ResponseErrorEvent, + ResponseFileSearchCallCompletedEvent, + ResponseFileSearchCallInProgressEvent, + ResponseFileSearchCallSearchingEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseShellCallCommandAddedEvent, + ResponseShellCallCommandDeltaEvent, + ResponseShellCallCommandDoneEvent, + ResponseShellCallOutputContentDeltaEvent, + ResponseShellCallOutputContentDoneEvent, + ResponseInProgressEvent, + ResponseFailedEvent, + ResponseIncompleteEvent, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseReasoningSummaryPartAddedEvent, + ResponseReasoningSummaryPartDoneEvent, + ResponseReasoningSummaryTextDeltaEvent, + ResponseReasoningSummaryTextDoneEvent, + ResponseReasoningTextDeltaEvent, + ResponseReasoningTextDoneEvent, + ResponseRefusalDeltaEvent, + ResponseRefusalDoneEvent, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + ResponseWebSearchCallCompletedEvent, + ResponseWebSearchCallInProgressEvent, + ResponseWebSearchCallSearchingEvent, + ResponseImageGenCallCompletedEvent, + ResponseImageGenCallGeneratingEvent, + ResponseImageGenCallInProgressEvent, + ResponseImageGenCallPartialImageEvent, + ResponseMcpCallArgumentsDeltaEvent, + ResponseMcpCallArgumentsDoneEvent, + ResponseMcpCallCompletedEvent, + ResponseMcpCallFailedEvent, + ResponseMcpCallInProgressEvent, + ResponseMcpListToolsCompletedEvent, + ResponseMcpListToolsFailedEvent, + ResponseMcpListToolsInProgressEvent, + ResponseOutputTextAnnotationAddedEvent, + ResponseQueuedEvent, + ResponseCustomToolCallInputDeltaEvent, + ResponseCustomToolCallInputDoneEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_text_config.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_text_config.py new file mode 100644 index 0000000..9e2eca2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_text_config.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_format_text_config import ResponseFormatTextConfig + +__all__ = ["ResponseTextConfig"] + + +class ResponseTextConfig(BaseModel): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + format: Optional[ResponseFormatTextConfig] = None + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] = None + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. The default is `medium`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_text_config_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_text_config_param.py new file mode 100644 index 0000000..6969497 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_text_config_param.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +from .response_format_text_config_param import ResponseFormatTextConfigParam + +__all__ = ["ResponseTextConfigParam"] + + +class ResponseTextConfigParam(TypedDict, total=False): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + format: ResponseFormatTextConfigParam + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. The default is `medium`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_text_delta_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_text_delta_event.py new file mode 100644 index 0000000..680e749 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_text_delta_event.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseTextDeltaEvent", "Logprob", "LogprobTopLogprob"] + + +class LogprobTopLogprob(BaseModel): + token: Optional[str] = None + """A possible text token.""" + + logprob: Optional[float] = None + """The log probability of this token.""" + + +class Logprob(BaseModel): + """ + A logprob is the logarithmic probability that the model assigns to producing + a particular token at a given position in the sequence. Less-negative (higher) + logprob values indicate greater model confidence in that token choice. + """ + + token: str + """A possible text token.""" + + logprob: float + """The log probability of this token.""" + + top_logprobs: Optional[List[LogprobTopLogprob]] = None + """The log probabilities of up to 20 of the most likely tokens.""" + + +class ResponseTextDeltaEvent(BaseModel): + """Emitted when there is an additional text delta.""" + + content_index: int + """The index of the content part that the text delta was added to.""" + + delta: str + """The text delta that was added.""" + + item_id: str + """The ID of the output item that the text delta was added to.""" + + logprobs: List[Logprob] + """The log probabilities of the tokens in the delta.""" + + output_index: int + """The index of the output item that the text delta was added to.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.output_text.delta"] + """The type of the event. Always `response.output_text.delta`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_text_done_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_text_done_event.py new file mode 100644 index 0000000..2295627 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_text_done_event.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseTextDoneEvent", "Logprob", "LogprobTopLogprob"] + + +class LogprobTopLogprob(BaseModel): + token: Optional[str] = None + """A possible text token.""" + + logprob: Optional[float] = None + """The log probability of this token.""" + + +class Logprob(BaseModel): + """ + A logprob is the logarithmic probability that the model assigns to producing + a particular token at a given position in the sequence. Less-negative (higher) + logprob values indicate greater model confidence in that token choice. + """ + + token: str + """A possible text token.""" + + logprob: float + """The log probability of this token.""" + + top_logprobs: Optional[List[LogprobTopLogprob]] = None + """The log probabilities of up to 20 of the most likely tokens.""" + + +class ResponseTextDoneEvent(BaseModel): + """Emitted when text content is finalized.""" + + content_index: int + """The index of the content part that the text content is finalized.""" + + item_id: str + """The ID of the output item that the text content is finalized.""" + + logprobs: List[Logprob] + """The log probabilities of the tokens in the delta.""" + + output_index: int + """The index of the output item that the text content is finalized.""" + + sequence_number: int + """The sequence number for this event.""" + + text: str + """The text content that is finalized.""" + + type: Literal["response.output_text.done"] + """The type of the event. Always `response.output_text.done`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_tool_search_call.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_tool_search_call.py new file mode 100644 index 0000000..1f9e408 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_tool_search_call.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseToolSearchCall"] + + +class ResponseToolSearchCall(BaseModel): + id: str + """The unique ID of the tool search call item.""" + + arguments: object + """Arguments used for the tool search call.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the tool search call item that was recorded.""" + + type: Literal["tool_search_call"] + """The type of the item. Always `tool_search_call`.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_tool_search_output_item.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_tool_search_output_item.py new file mode 100644 index 0000000..2bbad0e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_tool_search_output_item.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .tool import Tool +from ..._models import BaseModel + +__all__ = ["ResponseToolSearchOutputItem"] + + +class ResponseToolSearchOutputItem(BaseModel): + id: str + """The unique ID of the tool search output item.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the tool search output item that was recorded.""" + + tools: List[Tool] + """The loaded tool definitions returned by tool search.""" + + type: Literal["tool_search_output"] + """The type of the item. Always `tool_search_output`.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_tool_search_output_item_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_tool_search_output_item_param.py new file mode 100644 index 0000000..77b9b5c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_tool_search_output_item_param.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .tool import Tool +from ..._models import BaseModel + +__all__ = ["ResponseToolSearchOutputItemParam"] + + +class ResponseToolSearchOutputItemParam(BaseModel): + tools: List[Tool] + """The loaded tool definitions returned by the tool search output.""" + + type: Literal["tool_search_output"] + """The item type. Always `tool_search_output`.""" + + id: Optional[str] = None + """The unique ID of this tool search output.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Optional[Literal["server", "client"]] = None + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the tool search output.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_tool_search_output_item_param_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_tool_search_output_item_param_param.py new file mode 100644 index 0000000..1c110ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_tool_search_output_item_param_param.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from .tool_param import ToolParam + +__all__ = ["ResponseToolSearchOutputItemParamParam"] + + +class ResponseToolSearchOutputItemParamParam(TypedDict, total=False): + tools: Required[Iterable[ToolParam]] + """The loaded tool definitions returned by the tool search output.""" + + type: Required[Literal["tool_search_output"]] + """The item type. Always `tool_search_output`.""" + + id: Optional[str] + """The unique ID of this tool search output.""" + + call_id: Optional[str] + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the tool search output.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_usage.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_usage.py new file mode 100644 index 0000000..33e8ac2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_usage.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["ResponseUsage", "InputTokensDetails", "OutputTokensDetails"] + + +class InputTokensDetails(BaseModel): + """A detailed breakdown of the input tokens.""" + + cache_write_tokens: int + """The number of input tokens that were written to the cache.""" + + cached_tokens: int + """ + The number of tokens that were retrieved from the cache. + [More on prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching). + """ + + +class OutputTokensDetails(BaseModel): + """A detailed breakdown of the output tokens.""" + + reasoning_tokens: int + """The number of reasoning tokens.""" + + +class ResponseUsage(BaseModel): + """ + Represents token usage details including input tokens, output tokens, + a breakdown of output tokens, and the total tokens used. + """ + + input_tokens: int + """The number of input tokens.""" + + input_tokens_details: InputTokensDetails + """A detailed breakdown of the input tokens.""" + + output_tokens: int + """The number of output tokens.""" + + output_tokens_details: OutputTokensDetails + """A detailed breakdown of the output tokens.""" + + total_tokens: int + """The total number of tokens used.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_web_search_call_completed_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_web_search_call_completed_event.py new file mode 100644 index 0000000..826bbcf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_web_search_call_completed_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseWebSearchCallCompletedEvent"] + + +class ResponseWebSearchCallCompletedEvent(BaseModel): + """Emitted when a web search call is completed.""" + + item_id: str + """Unique ID for the output item associated with the web search call.""" + + output_index: int + """The index of the output item that the web search call is associated with.""" + + sequence_number: int + """The sequence number of the web search call being processed.""" + + type: Literal["response.web_search_call.completed"] + """The type of the event. Always `response.web_search_call.completed`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_web_search_call_in_progress_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_web_search_call_in_progress_event.py new file mode 100644 index 0000000..49e2511 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_web_search_call_in_progress_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseWebSearchCallInProgressEvent"] + + +class ResponseWebSearchCallInProgressEvent(BaseModel): + """Emitted when a web search call is initiated.""" + + item_id: str + """Unique ID for the output item associated with the web search call.""" + + output_index: int + """The index of the output item that the web search call is associated with.""" + + sequence_number: int + """The sequence number of the web search call being processed.""" + + type: Literal["response.web_search_call.in_progress"] + """The type of the event. Always `response.web_search_call.in_progress`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/response_web_search_call_searching_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/response_web_search_call_searching_event.py new file mode 100644 index 0000000..e57d621 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/response_web_search_call_searching_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseWebSearchCallSearchingEvent"] + + +class ResponseWebSearchCallSearchingEvent(BaseModel): + """Emitted when a web search call is executing.""" + + item_id: str + """Unique ID for the output item associated with the web search call.""" + + output_index: int + """The index of the output item that the web search call is associated with.""" + + sequence_number: int + """The sequence number of the web search call being processed.""" + + type: Literal["response.web_search_call.searching"] + """The type of the event. Always `response.web_search_call.searching`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/responses_client_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/responses_client_event.py new file mode 100644 index 0000000..70707e5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/responses_client_event.py @@ -0,0 +1,544 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from .tool import Tool +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .service_tier import ServiceTier +from .response_input import ResponseInput +from .response_prompt import ResponsePrompt +from .tool_choice_mcp import ToolChoiceMcp +from ..shared.metadata import Metadata +from ..shared.reasoning import Reasoning +from .tool_choice_shell import ToolChoiceShell +from .tool_choice_types import ToolChoiceTypes +from .tool_choice_custom import ToolChoiceCustom +from .response_includable import ResponseIncludable +from .tool_choice_allowed import ToolChoiceAllowed +from .tool_choice_options import ToolChoiceOptions +from .response_steer_event import ResponseSteerEvent +from .response_text_config import ResponseTextConfig +from .tool_choice_function import ToolChoiceFunction +from ..shared.responses_model import ResponsesModel +from .tool_choice_apply_patch import ToolChoiceApplyPatch +from .response_conversation_param import ResponseConversationParam + +__all__ = [ + "ResponsesClientEvent", + "ResponseCreate", + "ResponseCreateAccessPrograms", + "ResponseCreateContextManagement", + "ResponseCreateConversation", + "ResponseCreateModeration", + "ResponseCreateModerationPolicy", + "ResponseCreateModerationPolicyInput", + "ResponseCreateModerationPolicyOutput", + "ResponseCreatePromptCacheOptions", + "ResponseCreateStreamOptions", + "ResponseCreateToolChoice", + "ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam", +] + +# custom code for back compat exports +__all__ += [ + "ContextManagement", + "Conversation", + "Moderation", + "ModerationPolicy", + "ModerationPolicyInput", + "ModerationPolicyOutput", + "PromptCacheOptions", + "StreamOptions", + "ToolChoice", + "ToolChoiceSpecificProgrammaticToolCallingParam", +] +# end custom code for back compat exports + + +class ResponseCreateAccessPrograms(BaseModel): + """Domain-specific access programs to use for this request.""" + + cyber: Optional[Literal["standard", "daybreak_blue", "daybreak_red"]] = None + """The Cyber access program to use for this request. + + Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If + omitted, the API resolves the program from the model's Cyber tier and your + organization and project access, subject to model-specific eligibility + restrictions. By default, models without a Cyber tier use Standard. Blue-tier + models use Daybreak Blue when authorized; otherwise they fall back to Standard + unless the model requires Daybreak access. Red-tier models use Daybreak Red and + require authorization. Requests that require unavailable Daybreak access + return 403. An implicit Standard fallback is represented by null in the + response's access_programs field, rather than an explicit Standard selection. + """ + + +class ResponseCreateContextManagement(BaseModel): + type: str + """The context management entry type. Currently only 'compaction' is supported.""" + + compact_threshold: Optional[int] = None + """Token threshold at which compaction should be triggered for this entry.""" + + +ResponseCreateConversation: TypeAlias = Union[str, ResponseConversationParam, None] + + +class ResponseCreateModerationPolicyInput(BaseModel): + """The moderation policy for the response input.""" + + mode: Literal["score", "block"] + + +class ResponseCreateModerationPolicyOutput(BaseModel): + """The moderation policy for the response output.""" + + mode: Literal["score", "block"] + + +class ResponseCreateModerationPolicy(BaseModel): + """The policy to apply to moderated response input and output.""" + + input: Optional[ResponseCreateModerationPolicyInput] = None + """The moderation policy for the response input.""" + + output: Optional[ResponseCreateModerationPolicyOutput] = None + """The moderation policy for the response output.""" + + +class ResponseCreateModeration(BaseModel): + """Configuration for running moderation on the input and output of this response.""" + + model: str + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + policy: Optional[ResponseCreateModerationPolicy] = None + """The policy to apply to moderated response input and output.""" + + +class ResponseCreatePromptCacheOptions(BaseModel): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details. + """ + + comparison_response_id: Optional[str] = None + """The ID of a response to compare when diagnosing prompt cache reuse. + + Supplying this field requests prompt cache diagnostics when the feature is + enabled. + """ + + mode: Optional[Literal["implicit", "explicit"]] = None + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + prewarm: Optional[bool] = None + """Prepares the prompt cache without generating output. + + Defaults to `false`. When set to `true`, overrides the `generate` field to + `false`. + """ + + ttl: Optional[Literal["30m"]] = None + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + + +class ResponseCreateStreamOptions(BaseModel): + """Options for streaming responses. Only set this when you set `stream: true`.""" + + include_obfuscation: Optional[bool] = None + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + +class ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam(BaseModel): + type: Literal["programmatic_tool_calling"] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ResponseCreateToolChoice: TypeAlias = Union[ + ToolChoiceOptions, + ToolChoiceAllowed, + ToolChoiceTypes, + ToolChoiceFunction, + ToolChoiceMcp, + ToolChoiceCustom, + ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam, + ToolChoiceApplyPatch, + ToolChoiceShell, +] + + +class ResponseCreate(BaseModel): + """ + Client event for creating a response over a persistent WebSocket connection. + This payload uses the same top-level fields as `POST /v1/responses`, plus + WebSocket-only envelope metadata. + + Notes: + - `stream` is implicit over WebSocket and should not be sent. + - `background` is not supported over WebSocket. + - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`. + """ + + type: Literal["response.create"] + """The type of the client event. Always `response.create`.""" + + access_programs: Optional[ResponseCreateAccessPrograms] = None + """Domain-specific access programs to use for this request.""" + + background: Optional[bool] = None + """ + Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + """ + + context_management: Optional[List[ResponseCreateContextManagement]] = None + """Context management configuration for this request.""" + + conversation: Optional[ResponseCreateConversation] = None + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + include: Optional[List[ResponseIncludable]] = None + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + """ + + input: Union[str, ResponseInput, None] = None + """Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + """ + + instructions: Optional[str] = None + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + max_output_tokens: Optional[int] = None + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] = None + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Optional[ResponsesModel] = None + """Model ID used to generate the response, like `gpt-6-astra`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + moderation: Optional[ResponseCreateModeration] = None + """Configuration for running moderation on the input and output of this response.""" + + parallel_tool_calls: Optional[bool] = None + """Whether to allow the model to run tool calls in parallel.""" + + previous_response_id: Optional[str] = None + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[ResponsePrompt] = None + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + prompt_cache_key: Optional[str] = None + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + """ + + prompt_cache_options: Optional[ResponseCreatePromptCacheOptions] = None + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] = None + """Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + """ + + reasoning: Optional[Reasoning] = None + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + safety_identifier: Optional[str] = None + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + service_tier: Optional[ServiceTier] = None + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + store: Optional[bool] = None + """Whether to store the generated model response for later retrieval via API. + + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + """ + + stream: Optional[bool] = None + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + """ + + stream_id: Optional[str] = None + """The WebSocket lane for this response. + + Requests with the same `stream_id` are processed FIFO, and events for the + response echo the same `stream_id`. + + `stream_id` controls routing; `previous_response_id` controls conversation + lineage, so a new lane can fork from a response created on another lane. + """ + + stream_options: Optional[ResponseCreateStreamOptions] = None + """Options for streaming responses. Only set this when you set `stream: true`.""" + + temperature: Optional[float] = None + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + text: Optional[ResponseTextConfig] = None + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + tool_choice: Optional[ResponseCreateToolChoice] = None + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: Optional[List[Tool]] = None + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_logprobs: Optional[int] = None + """ + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + """ + + top_p: Optional[float] = None + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + truncation: Optional[Literal["auto", "disabled"]] = None + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + user: Optional[str] = None + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + +ResponsesClientEvent: TypeAlias = Annotated[ + Union[ResponseCreate, ResponseSteerEvent], PropertyInfo(discriminator="type") +] + + +# custom code for back compat aliases +# Preserve the names exposed before ResponsesClientEvent became a union. +ContextManagement: TypeAlias = ResponseCreateContextManagement +Conversation: TypeAlias = ResponseCreateConversation +Moderation: TypeAlias = ResponseCreateModeration +ModerationPolicy: TypeAlias = ResponseCreateModerationPolicy +ModerationPolicyInput: TypeAlias = ResponseCreateModerationPolicyInput +ModerationPolicyOutput: TypeAlias = ResponseCreateModerationPolicyOutput +PromptCacheOptions: TypeAlias = ResponseCreatePromptCacheOptions +StreamOptions: TypeAlias = ResponseCreateStreamOptions +ToolChoice: TypeAlias = ResponseCreateToolChoice +ToolChoiceSpecificProgrammaticToolCallingParam: TypeAlias = ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam +# end custom code for back compat aliases diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/responses_client_event_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/responses_client_event_param.py new file mode 100644 index 0000000..a970363 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/responses_client_event_param.py @@ -0,0 +1,542 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .tool_param import ToolParam +from .service_tier import ServiceTier +from .response_includable import ResponseIncludable +from .tool_choice_options import ToolChoiceOptions +from .response_input_param import ResponseInputParam +from .response_prompt_param import ResponsePromptParam +from .tool_choice_mcp_param import ToolChoiceMcpParam +from ..shared_params.metadata import Metadata +from .tool_choice_shell_param import ToolChoiceShellParam +from .tool_choice_types_param import ToolChoiceTypesParam +from ..shared_params.reasoning import Reasoning +from .tool_choice_custom_param import ToolChoiceCustomParam +from .tool_choice_allowed_param import ToolChoiceAllowedParam +from .response_steer_event_param import ResponseSteerEventParam +from .response_text_config_param import ResponseTextConfigParam +from .tool_choice_function_param import ToolChoiceFunctionParam +from .tool_choice_apply_patch_param import ToolChoiceApplyPatchParam +from ..shared_params.responses_model import ResponsesModel +from .response_conversation_param_param import ResponseConversationParamParam + +__all__ = [ + "ResponsesClientEventParam", + "ResponseCreate", + "ResponseCreateAccessPrograms", + "ResponseCreateContextManagement", + "ResponseCreateConversation", + "ResponseCreateModeration", + "ResponseCreateModerationPolicy", + "ResponseCreateModerationPolicyInput", + "ResponseCreateModerationPolicyOutput", + "ResponseCreatePromptCacheOptions", + "ResponseCreateStreamOptions", + "ResponseCreateToolChoice", + "ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam", +] + +# custom code for back compat exports +__all__ += [ + "ContextManagement", + "Conversation", + "Moderation", + "ModerationPolicy", + "ModerationPolicyInput", + "ModerationPolicyOutput", + "PromptCacheOptions", + "StreamOptions", + "ToolChoice", + "ToolChoiceSpecificProgrammaticToolCallingParam", +] +# end custom code for back compat exports + + +class ResponseCreateAccessPrograms(TypedDict, total=False): + """Domain-specific access programs to use for this request.""" + + cyber: Literal["standard", "daybreak_blue", "daybreak_red"] + """The Cyber access program to use for this request. + + Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If + omitted, the API resolves the program from the model's Cyber tier and your + organization and project access, subject to model-specific eligibility + restrictions. By default, models without a Cyber tier use Standard. Blue-tier + models use Daybreak Blue when authorized; otherwise they fall back to Standard + unless the model requires Daybreak access. Red-tier models use Daybreak Red and + require authorization. Requests that require unavailable Daybreak access + return 403. An implicit Standard fallback is represented by null in the + response's access_programs field, rather than an explicit Standard selection. + """ + + +class ResponseCreateContextManagement(TypedDict, total=False): + type: Required[str] + """The context management entry type. Currently only 'compaction' is supported.""" + + compact_threshold: Optional[int] + """Token threshold at which compaction should be triggered for this entry.""" + + +ResponseCreateConversation: TypeAlias = Union[str, ResponseConversationParamParam] + + +class ResponseCreateModerationPolicyInput(TypedDict, total=False): + """The moderation policy for the response input.""" + + mode: Required[Literal["score", "block"]] + + +class ResponseCreateModerationPolicyOutput(TypedDict, total=False): + """The moderation policy for the response output.""" + + mode: Required[Literal["score", "block"]] + + +class ResponseCreateModerationPolicy(TypedDict, total=False): + """The policy to apply to moderated response input and output.""" + + input: Optional[ResponseCreateModerationPolicyInput] + """The moderation policy for the response input.""" + + output: Optional[ResponseCreateModerationPolicyOutput] + """The moderation policy for the response output.""" + + +class ResponseCreateModeration(TypedDict, total=False): + """Configuration for running moderation on the input and output of this response.""" + + model: Required[str] + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + policy: Optional[ResponseCreateModerationPolicy] + """The policy to apply to moderated response input and output.""" + + +class ResponseCreatePromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details. + """ + + comparison_response_id: Optional[str] + """The ID of a response to compare when diagnosing prompt cache reuse. + + Supplying this field requests prompt cache diagnostics when the feature is + enabled. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + prewarm: bool + """Prepares the prompt cache without generating output. + + Defaults to `false`. When set to `true`, overrides the `generate` field to + `false`. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + + +class ResponseCreateStreamOptions(TypedDict, total=False): + """Options for streaming responses. Only set this when you set `stream: true`.""" + + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + +class ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ResponseCreateToolChoice: TypeAlias = Union[ + ToolChoiceOptions, + ToolChoiceAllowedParam, + ToolChoiceTypesParam, + ToolChoiceFunctionParam, + ToolChoiceMcpParam, + ToolChoiceCustomParam, + ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam, + ToolChoiceApplyPatchParam, + ToolChoiceShellParam, +] + + +class ResponseCreate(TypedDict, total=False): + """ + Client event for creating a response over a persistent WebSocket connection. + This payload uses the same top-level fields as `POST /v1/responses`, plus + WebSocket-only envelope metadata. + + Notes: + - `stream` is implicit over WebSocket and should not be sent. + - `background` is not supported over WebSocket. + - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`. + """ + + type: Required[Literal["response.create"]] + """The type of the client event. Always `response.create`.""" + + access_programs: ResponseCreateAccessPrograms + """Domain-specific access programs to use for this request.""" + + background: Optional[bool] + """ + Whether to run the model response in the background. + [Learn more](https://developers.openai.com/api/docs/guides/background). + """ + + context_management: Optional[Iterable[ResponseCreateContextManagement]] + """Context management configuration for this request.""" + + conversation: Optional[ResponseCreateConversation] + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + include: Optional[List[ResponseIncludable]] + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + """ + + input: Union[str, ResponseInputParam] + """Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision) + - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs) + - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) + - [Function calling](https://developers.openai.com/api/docs/guides/function-calling) + """ + + instructions: Optional[str] + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + max_output_tokens: Optional[int] + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: ResponsesModel + """Model ID used to generate the response, like `gpt-6-astra`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://developers.openai.com/api/docs/models) to browse and + compare available models. + """ + + moderation: Optional[ResponseCreateModeration] + """Configuration for running moderation on the input and output of this response.""" + + parallel_tool_calls: Optional[bool] + """Whether to allow the model to run tool calls in parallel.""" + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[ResponsePromptParam] + """ + Reference to a prompt template and its variables. + [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code). + """ + + prompt_cache_key: Optional[str] + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching). + """ + + prompt_cache_options: ResponseCreatePromptCacheOptions + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) + for current details. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] + """Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + """ + + reasoning: Optional[Reasoning] + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + safety_identifier: Optional[str] + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + service_tier: Optional[ServiceTier] + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to + '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then + the request will be processed with the Flex Processing service tier. + - To opt-in to + [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the + request level, include the `service_tier=fast` or `service_tier=priority` + parameter for Responses or Chat Completions. The response will show + `service_tier=priority` regardless of if you specify `service_tier=fast` or + `priority` in your request. + - If set to 'ultrafast', then the request will be processed with the + access-controlled Ultrafast Processing service tier. This tier is currently + available for `gpt-5.6-sol`; a response served through it will show + `service_tier=ultrafast`. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + store: Optional[bool] + """Whether to store the generated model response for later retrieval via API. + + Defaults to true when omitted. If set to true, response data will be stored for + at least 30 days, subject to the + [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses). + """ + + stream: Optional[bool] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events) + for more information. + """ + + stream_id: str + """The WebSocket lane for this response. + + Requests with the same `stream_id` are processed FIFO, and events for the + response echo the same `stream_id`. + + `stream_id` controls routing; `previous_response_id` controls conversation + lineage, so a new lane can fork from a response created on another lane. + """ + + stream_options: Optional[ResponseCreateStreamOptions] + """Options for streaming responses. Only set this when you set `stream: true`.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + text: ResponseTextConfigParam + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text) + - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs) + """ + + tool_choice: ResponseCreateToolChoice + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: Iterable[ToolParam] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + or + [file search](https://developers.openai.com/api/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://developers.openai.com/api/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_logprobs: Optional[int] + """ + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + truncation: Optional[Literal["auto", "disabled"]] + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + user: str + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers). + """ + + +ResponsesClientEventParam: TypeAlias = Union[ResponseCreate, ResponseSteerEventParam] + + +# custom code for back compat aliases +# Preserve the names exposed before ResponsesClientEvent became a union. +ContextManagement: TypeAlias = ResponseCreateContextManagement +Conversation: TypeAlias = ResponseCreateConversation +Moderation: TypeAlias = ResponseCreateModeration +ModerationPolicy: TypeAlias = ResponseCreateModerationPolicy +ModerationPolicyInput: TypeAlias = ResponseCreateModerationPolicyInput +ModerationPolicyOutput: TypeAlias = ResponseCreateModerationPolicyOutput +PromptCacheOptions: TypeAlias = ResponseCreatePromptCacheOptions +StreamOptions: TypeAlias = ResponseCreateStreamOptions +ToolChoice: TypeAlias = ResponseCreateToolChoice +ToolChoiceSpecificProgrammaticToolCallingParam: TypeAlias = ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam +# end custom code for back compat aliases diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/responses_server_event.py b/venv/lib/python3.12/site-packages/openai/types/responses/responses_server_event.py new file mode 100644 index 0000000..e3faa05 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/responses_server_event.py @@ -0,0 +1,928 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .response_failed_event import ResponseFailedEvent +from .response_queued_event import ResponseQueuedEvent +from .response_created_event import ResponseCreatedEvent +from .response_completed_event import ResponseCompletedEvent +from .response_text_done_event import ResponseTextDoneEvent +from .response_audio_done_event import ResponseAudioDoneEvent +from .response_incomplete_event import ResponseIncompleteEvent +from .response_text_delta_event import ResponseTextDeltaEvent +from .response_audio_delta_event import ResponseAudioDeltaEvent +from .response_in_progress_event import ResponseInProgressEvent +from .response_refusal_done_event import ResponseRefusalDoneEvent +from .response_steer_failed_event import ResponseSteerFailedEvent +from .response_refusal_delta_event import ResponseRefusalDeltaEvent +from .response_steer_pending_event import ResponseSteerPendingEvent +from .response_steer_accepted_event import ResponseSteerAcceptedEvent +from .response_mcp_call_failed_event import ResponseMcpCallFailedEvent +from .response_output_item_done_event import ResponseOutputItemDoneEvent +from .response_content_part_done_event import ResponseContentPartDoneEvent +from .response_output_item_added_event import ResponseOutputItemAddedEvent +from .response_content_part_added_event import ResponseContentPartAddedEvent +from .response_mcp_call_completed_event import ResponseMcpCallCompletedEvent +from .response_reasoning_text_done_event import ResponseReasoningTextDoneEvent +from .response_mcp_call_in_progress_event import ResponseMcpCallInProgressEvent +from .response_reasoning_text_delta_event import ResponseReasoningTextDeltaEvent +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent +from .response_compaction_compacting_event import ResponseCompactionCompactingEvent +from .response_mcp_list_tools_failed_event import ResponseMcpListToolsFailedEvent +from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent +from .response_mcp_call_arguments_done_event import ResponseMcpCallArgumentsDoneEvent +from .response_shell_call_command_done_event import ResponseShellCallCommandDoneEvent +from .response_image_gen_call_completed_event import ResponseImageGenCallCompletedEvent +from .response_mcp_call_arguments_delta_event import ResponseMcpCallArgumentsDeltaEvent +from .response_mcp_list_tools_completed_event import ResponseMcpListToolsCompletedEvent +from .response_shell_call_command_added_event import ResponseShellCallCommandAddedEvent +from .response_shell_call_command_delta_event import ResponseShellCallCommandDeltaEvent +from .response_image_gen_call_generating_event import ResponseImageGenCallGeneratingEvent +from .response_web_search_call_completed_event import ResponseWebSearchCallCompletedEvent +from .response_web_search_call_searching_event import ResponseWebSearchCallSearchingEvent +from .response_file_search_call_completed_event import ResponseFileSearchCallCompletedEvent +from .response_file_search_call_searching_event import ResponseFileSearchCallSearchingEvent +from .response_image_gen_call_in_progress_event import ResponseImageGenCallInProgressEvent +from .response_mcp_list_tools_in_progress_event import ResponseMcpListToolsInProgressEvent +from .response_custom_tool_call_input_done_event import ResponseCustomToolCallInputDoneEvent +from .response_reasoning_summary_part_done_event import ResponseReasoningSummaryPartDoneEvent +from .response_reasoning_summary_text_done_event import ResponseReasoningSummaryTextDoneEvent +from .response_web_search_call_in_progress_event import ResponseWebSearchCallInProgressEvent +from .response_custom_tool_call_input_delta_event import ResponseCustomToolCallInputDeltaEvent +from .response_file_search_call_in_progress_event import ResponseFileSearchCallInProgressEvent +from .response_function_call_arguments_done_event import ResponseFunctionCallArgumentsDoneEvent +from .response_image_gen_call_partial_image_event import ResponseImageGenCallPartialImageEvent +from .response_output_text_annotation_added_event import ResponseOutputTextAnnotationAddedEvent +from .response_reasoning_summary_part_added_event import ResponseReasoningSummaryPartAddedEvent +from .response_reasoning_summary_text_delta_event import ResponseReasoningSummaryTextDeltaEvent +from .response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent +from .response_shell_call_output_content_done_event import ResponseShellCallOutputContentDoneEvent +from .response_code_interpreter_call_code_done_event import ResponseCodeInterpreterCallCodeDoneEvent +from .response_code_interpreter_call_completed_event import ResponseCodeInterpreterCallCompletedEvent +from .response_shell_call_output_content_delta_event import ResponseShellCallOutputContentDeltaEvent +from .response_code_interpreter_call_code_delta_event import ResponseCodeInterpreterCallCodeDeltaEvent +from .response_code_interpreter_call_in_progress_event import ResponseCodeInterpreterCallInProgressEvent +from .response_code_interpreter_call_interpreting_event import ResponseCodeInterpreterCallInterpretingEvent + +__all__ = [ + "ResponsesServerEvent", + "ResponseAudioWsDelta", + "ResponseAudioWsDone", + "ResponseAudioTranscriptWsDelta", + "ResponseAudioTranscriptWsDone", + "ResponseCodeInterpreterCallCodeWsDelta", + "ResponseCodeInterpreterCallCodeWsDone", + "ResponseCodeInterpreterCallWsCompleted", + "ResponseCodeInterpreterCallInWsProgress", + "ResponseCodeInterpreterCallWsInterpreting", + "ResponseCompactionWsCompacting", + "ResponseWsCompleted", + "ResponseContentPartWsAdded", + "ResponseContentPartWsDone", + "ResponseWsCreated", + "ResponseFileSearchCallWsCompleted", + "ResponseFileSearchCallInWsProgress", + "ResponseFileSearchCallWsSearching", + "ResponseFunctionCallArgumentsWsDelta", + "ResponseFunctionCallArgumentsWsDone", + "ResponseShellCallCommandWsAdded", + "ResponseShellCallCommandWsDelta", + "ResponseShellCallCommandWsDone", + "ResponseShellCallOutputContentWsDelta", + "ResponseShellCallOutputContentWsDone", + "ResponseInWsProgress", + "ResponseWsFailed", + "ResponseWsIncomplete", + "ResponseOutputItemWsAdded", + "ResponseOutputItemWsDone", + "ResponseReasoningSummaryPartWsAdded", + "ResponseReasoningSummaryPartWsDone", + "ResponseReasoningSummaryTextWsDelta", + "ResponseReasoningSummaryTextWsDone", + "ResponseReasoningTextWsDelta", + "ResponseReasoningTextWsDone", + "ResponseRefusalWsDelta", + "ResponseRefusalWsDone", + "ResponseTextWsDelta", + "ResponseTextWsDone", + "ResponseWebSearchCallWsCompleted", + "ResponseWebSearchCallInWsProgress", + "ResponseWebSearchCallWsSearching", + "ResponseImageGenCallWsCompleted", + "ResponseImageGenCallWsGenerating", + "ResponseImageGenCallInWsProgress", + "ResponseImageGenCallPartialWsImage", + "ResponseMcpCallArgumentsWsDelta", + "ResponseMcpCallArgumentsWsDone", + "ResponseMcpCallWsCompleted", + "ResponseMcpCallWsFailed", + "ResponseMcpCallInWsProgress", + "ResponseMcpListToolsWsCompleted", + "ResponseMcpListToolsWsFailed", + "ResponseMcpListToolsInWsProgress", + "ResponseOutputTextAnnotationWsAdded", + "ResponseWsQueued", + "ResponseCustomToolCallInputWsDelta", + "ResponseCustomToolCallInputWsDone", + "ResponseWsError", + "ResponseWsErrorError", + "ResponseWsErrorErrorMisalignment", + "ResponseWsErrorErrorMisalignmentSteer", +] + + +class ResponseAudioWsDelta(ResponseAudioDeltaEvent): + """Emitted when there is a partial audio response.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseAudioWsDone(ResponseAudioDoneEvent): + """Emitted when the audio response is complete.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseAudioTranscriptWsDelta(ResponseAudioTranscriptDeltaEvent): + """Emitted when there is a partial transcript of audio.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseAudioTranscriptWsDone(ResponseAudioTranscriptDoneEvent): + """Emitted when the full audio transcript is completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseCodeInterpreterCallCodeWsDelta(ResponseCodeInterpreterCallCodeDeltaEvent): + """Emitted when a partial code snippet is streamed by the code interpreter.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseCodeInterpreterCallCodeWsDone(ResponseCodeInterpreterCallCodeDoneEvent): + """Emitted when the code snippet is finalized by the code interpreter.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseCodeInterpreterCallWsCompleted(ResponseCodeInterpreterCallCompletedEvent): + """Emitted when the code interpreter call is completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseCodeInterpreterCallInWsProgress(ResponseCodeInterpreterCallInProgressEvent): + """Emitted when a code interpreter call is in progress.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseCodeInterpreterCallWsInterpreting(ResponseCodeInterpreterCallInterpretingEvent): + """Emitted when the code interpreter is actively interpreting the code snippet.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseCompactionWsCompacting(ResponseCompactionCompactingEvent): + """Emitted when new summary content is sampled for a compaction trigger. + + Contains no summary content. + """ + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseWsCompleted(ResponseCompletedEvent): + """Emitted when the model response is complete.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseContentPartWsAdded(ResponseContentPartAddedEvent): + """Emitted when a new content part is added.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseContentPartWsDone(ResponseContentPartDoneEvent): + """Emitted when a content part is done.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseWsCreated(ResponseCreatedEvent): + """An event that is emitted when a response is created.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseFileSearchCallWsCompleted(ResponseFileSearchCallCompletedEvent): + """Emitted when a file search call is completed (results found).""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseFileSearchCallInWsProgress(ResponseFileSearchCallInProgressEvent): + """Emitted when a file search call is initiated.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseFileSearchCallWsSearching(ResponseFileSearchCallSearchingEvent): + """Emitted when a file search is currently searching.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseFunctionCallArgumentsWsDelta(ResponseFunctionCallArgumentsDeltaEvent): + """Emitted when there is a partial function-call arguments delta.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseFunctionCallArgumentsWsDone(ResponseFunctionCallArgumentsDoneEvent): + """Emitted when function-call arguments are finalized.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseShellCallCommandWsAdded(ResponseShellCallCommandAddedEvent): + """A streaming event that indicated a shell command was added to a tool call.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseShellCallCommandWsDelta(ResponseShellCallCommandDeltaEvent): + """A streaming event that indicated a shell command was incrementally updated.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseShellCallCommandWsDone(ResponseShellCallCommandDoneEvent): + """A streaming event that indicated a shell command was completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseShellCallOutputContentWsDelta(ResponseShellCallOutputContentDeltaEvent): + """A streaming event that indicated shell call output was incrementally added.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseShellCallOutputContentWsDone(ResponseShellCallOutputContentDoneEvent): + """A streaming event that indicated shell call output was completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseInWsProgress(ResponseInProgressEvent): + """Emitted when the response is in progress.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseWsFailed(ResponseFailedEvent): + """An event that is emitted when a response fails.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseWsIncomplete(ResponseIncompleteEvent): + """An event that is emitted when a response finishes as incomplete. + + Over WebSocket, steering can finish a response with + `response.incomplete_details.reason` set to `steered`, followed automatically + by a successor `response.created` that commits the queued steering input. + """ + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseOutputItemWsAdded(ResponseOutputItemAddedEvent): + """Emitted when a new output item is added.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseOutputItemWsDone(ResponseOutputItemDoneEvent): + """Emitted when an output item is marked done.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseReasoningSummaryPartWsAdded(ResponseReasoningSummaryPartAddedEvent): + """Emitted when a new reasoning summary part is added.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseReasoningSummaryPartWsDone(ResponseReasoningSummaryPartDoneEvent): + """Emitted when a reasoning summary part is completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseReasoningSummaryTextWsDelta(ResponseReasoningSummaryTextDeltaEvent): + """Emitted when a delta is added to a reasoning summary text.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseReasoningSummaryTextWsDone(ResponseReasoningSummaryTextDoneEvent): + """Emitted when a reasoning summary text is completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseReasoningTextWsDelta(ResponseReasoningTextDeltaEvent): + """Emitted when a delta is added to a reasoning text.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseReasoningTextWsDone(ResponseReasoningTextDoneEvent): + """Emitted when a reasoning text is completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseRefusalWsDelta(ResponseRefusalDeltaEvent): + """Emitted when there is a partial refusal text.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseRefusalWsDone(ResponseRefusalDoneEvent): + """Emitted when refusal text is finalized.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseTextWsDelta(ResponseTextDeltaEvent): + """Emitted when there is an additional text delta.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseTextWsDone(ResponseTextDoneEvent): + """Emitted when text content is finalized.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseWebSearchCallWsCompleted(ResponseWebSearchCallCompletedEvent): + """Emitted when a web search call is completed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseWebSearchCallInWsProgress(ResponseWebSearchCallInProgressEvent): + """Emitted when a web search call is initiated.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseWebSearchCallWsSearching(ResponseWebSearchCallSearchingEvent): + """Emitted when a web search call is executing.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseImageGenCallWsCompleted(ResponseImageGenCallCompletedEvent): + """ + Emitted when an image generation tool call has completed and the final image is available. + """ + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseImageGenCallWsGenerating(ResponseImageGenCallGeneratingEvent): + """ + Emitted when an image generation tool call is actively generating an image (intermediate state). + """ + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseImageGenCallInWsProgress(ResponseImageGenCallInProgressEvent): + """Emitted when an image generation tool call is in progress.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseImageGenCallPartialWsImage(ResponseImageGenCallPartialImageEvent): + """Emitted when a partial image is available during image generation streaming.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseMcpCallArgumentsWsDelta(ResponseMcpCallArgumentsDeltaEvent): + """ + Emitted when there is a delta (partial update) to the arguments of an MCP tool call. + """ + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseMcpCallArgumentsWsDone(ResponseMcpCallArgumentsDoneEvent): + """Emitted when the arguments for an MCP tool call are finalized.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseMcpCallWsCompleted(ResponseMcpCallCompletedEvent): + """Emitted when an MCP tool call has completed successfully.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseMcpCallWsFailed(ResponseMcpCallFailedEvent): + """Emitted when an MCP tool call has failed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseMcpCallInWsProgress(ResponseMcpCallInProgressEvent): + """Emitted when an MCP tool call is in progress.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseMcpListToolsWsCompleted(ResponseMcpListToolsCompletedEvent): + """Emitted when the list of available MCP tools has been successfully retrieved.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseMcpListToolsWsFailed(ResponseMcpListToolsFailedEvent): + """Emitted when the attempt to list available MCP tools has failed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseMcpListToolsInWsProgress(ResponseMcpListToolsInProgressEvent): + """ + Emitted when the system is in the process of retrieving the list of available MCP tools. + """ + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseOutputTextAnnotationWsAdded(ResponseOutputTextAnnotationAddedEvent): + """Emitted when an annotation is added to output text content.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseWsQueued(ResponseQueuedEvent): + """Emitted when a response is queued and waiting to be processed.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseCustomToolCallInputWsDelta(ResponseCustomToolCallInputDeltaEvent): + """Event representing a delta (partial update) to the input of a custom tool call.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseCustomToolCallInputWsDone(ResponseCustomToolCallInputDoneEvent): + """Event indicating that input for a custom tool call is complete.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +class ResponseWsErrorErrorMisalignmentSteer(BaseModel): + """An optional public continuation instruction.""" + + message: str + """The public continuation instruction.""" + + +class ResponseWsErrorErrorMisalignment(BaseModel): + detailed_explanation: Optional[str] = None + """The public explanation for this block.""" + + error_type: Union[ + str, + Literal[ + "potentially_unintended_data_transfer", + "potentially_unintended_data_access", + "potentially_unintended_destructive_activity", + "other", + ], + None, + ] = None + """An optional classification; clients must accept additional values.""" + + steer: Optional[ResponseWsErrorErrorMisalignmentSteer] = None + """An optional public continuation instruction.""" + + +class ResponseWsErrorError(BaseModel): + """Details about the error.""" + + code: Optional[str] = None + """The error code that was emitted, if any.""" + + message: str + """The human-readable error message that was emitted.""" + + param: Optional[str] = None + """The parameter name that was associated with the error, if any.""" + + type: str + """The error type that was emitted.""" + + headers: Optional[Dict[str, str]] = None + """The response headers that were emitted with the error, if any.""" + + misalignment: Optional[ResponseWsErrorErrorMisalignment] = None + + +class ResponseWsError(BaseModel): + """Emitted when an error occurs while processing a Responses WebSocket request.""" + + error: ResponseWsErrorError + """Details about the error.""" + + type: Literal["error"] + """The type of the event. Always `error`.""" + + sequence_number: Optional[int] = None + """The sequence number of an error emitted by the response stream.""" + + status: Optional[int] = None + """The HTTP status code associated with a WebSocket protocol error.""" + + stream_id: Optional[str] = None + """The WebSocket lane that emitted this event. + + This field is present when the originating `response.create` event supplied a + `stream_id`. + """ + + +ResponsesServerEvent: TypeAlias = Annotated[ + Union[ + ResponseAudioWsDelta, + ResponseAudioWsDone, + ResponseAudioTranscriptWsDelta, + ResponseAudioTranscriptWsDone, + ResponseCodeInterpreterCallCodeWsDelta, + ResponseCodeInterpreterCallCodeWsDone, + ResponseCodeInterpreterCallWsCompleted, + ResponseCodeInterpreterCallInWsProgress, + ResponseCodeInterpreterCallWsInterpreting, + ResponseCompactionWsCompacting, + ResponseWsCompleted, + ResponseContentPartWsAdded, + ResponseContentPartWsDone, + ResponseWsCreated, + ResponseFileSearchCallWsCompleted, + ResponseFileSearchCallInWsProgress, + ResponseFileSearchCallWsSearching, + ResponseFunctionCallArgumentsWsDelta, + ResponseFunctionCallArgumentsWsDone, + ResponseShellCallCommandWsAdded, + ResponseShellCallCommandWsDelta, + ResponseShellCallCommandWsDone, + ResponseShellCallOutputContentWsDelta, + ResponseShellCallOutputContentWsDone, + ResponseInWsProgress, + ResponseWsFailed, + ResponseWsIncomplete, + ResponseOutputItemWsAdded, + ResponseOutputItemWsDone, + ResponseReasoningSummaryPartWsAdded, + ResponseReasoningSummaryPartWsDone, + ResponseReasoningSummaryTextWsDelta, + ResponseReasoningSummaryTextWsDone, + ResponseReasoningTextWsDelta, + ResponseReasoningTextWsDone, + ResponseRefusalWsDelta, + ResponseRefusalWsDone, + ResponseTextWsDelta, + ResponseTextWsDone, + ResponseWebSearchCallWsCompleted, + ResponseWebSearchCallInWsProgress, + ResponseWebSearchCallWsSearching, + ResponseImageGenCallWsCompleted, + ResponseImageGenCallWsGenerating, + ResponseImageGenCallInWsProgress, + ResponseImageGenCallPartialWsImage, + ResponseMcpCallArgumentsWsDelta, + ResponseMcpCallArgumentsWsDone, + ResponseMcpCallWsCompleted, + ResponseMcpCallWsFailed, + ResponseMcpCallInWsProgress, + ResponseMcpListToolsWsCompleted, + ResponseMcpListToolsWsFailed, + ResponseMcpListToolsInWsProgress, + ResponseOutputTextAnnotationWsAdded, + ResponseWsQueued, + ResponseCustomToolCallInputWsDelta, + ResponseCustomToolCallInputWsDone, + ResponseWsError, + ResponseSteerAcceptedEvent, + ResponseSteerPendingEvent, + ResponseSteerFailedEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/service_tier.py b/venv/lib/python3.12/site-packages/openai/types/responses/service_tier.py new file mode 100644 index 0000000..5fffe96 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/service_tier.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal, TypeAlias + +__all__ = ["ServiceTier"] + +ServiceTier: TypeAlias = Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/skill_reference.py b/venv/lib/python3.12/site-packages/openai/types/responses/skill_reference.py new file mode 100644 index 0000000..cf25048 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/skill_reference.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SkillReference"] + + +class SkillReference(BaseModel): + skill_id: str + """The ID of the referenced skill.""" + + type: Literal["skill_reference"] + """References a skill created with the /v1/skills endpoint.""" + + version: Optional[str] = None + """Optional skill version. Use a positive integer or 'latest'. Omit for default.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/skill_reference_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/skill_reference_param.py new file mode 100644 index 0000000..22b71e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/skill_reference_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["SkillReferenceParam"] + + +class SkillReferenceParam(TypedDict, total=False): + skill_id: Required[str] + """The ID of the referenced skill.""" + + type: Required[Literal["skill_reference"]] + """References a skill created with the /v1/skills endpoint.""" + + version: str + """Optional skill version. Use a positive integer or 'latest'. Omit for default.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool.py new file mode 100644 index 0000000..214a761 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool.py @@ -0,0 +1,395 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from . import web_search_tool +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .custom_tool import CustomTool +from .computer_tool import ComputerTool +from .function_tool import FunctionTool +from .namespace_tool import NamespaceTool +from .web_search_tool import WebSearchTool +from .apply_patch_tool import ApplyPatchTool +from .file_search_tool import FileSearchTool +from .tool_search_tool import ToolSearchTool +from .function_shell_tool import FunctionShellTool +from .web_search_preview_tool import WebSearchPreviewTool +from .computer_use_preview_tool import ComputerUsePreviewTool +from .container_network_policy_disabled import ContainerNetworkPolicyDisabled +from .container_network_policy_allowlist import ContainerNetworkPolicyAllowlist + +__all__ = [ + "Tool", + "WebSearchTool", + "Mcp", + "McpAllowedTools", + "McpAllowedToolsMcpToolFilter", + "McpRequireApproval", + "McpRequireApprovalMcpToolApprovalFilter", + "McpRequireApprovalMcpToolApprovalFilterAlways", + "McpRequireApprovalMcpToolApprovalFilterNever", + "CodeInterpreter", + "CodeInterpreterContainer", + "CodeInterpreterContainerCodeInterpreterToolAuto", + "CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy", + "ProgrammaticToolCalling", + "ImageGeneration", + "ImageGenerationInputImageMask", + "LocalShell", +] + +WebSearchToolFilters = web_search_tool.Filters +WebSearchToolUserLocation = web_search_tool.UserLocation + + +class McpAllowedToolsMcpToolFilter(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpToolFilter, None] + + +class McpRequireApprovalMcpToolApprovalFilterAlways(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilterNever(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilter(BaseModel): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + + always: Optional[McpRequireApprovalMcpToolApprovalFilterAlways] = None + """A filter object to specify which tools are allowed.""" + + never: Optional[McpRequireApprovalMcpToolApprovalFilterNever] = None + """A filter object to specify which tools are allowed.""" + + +McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"], None] + + +class Mcp(BaseModel): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + """ + + server_label: str + """A label for this MCP server, used to identify it in tool calls.""" + + type: Literal["mcp"] + """The type of the MCP tool. Always `mcp`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + allowed_tools: Optional[McpAllowedTools] = None + """List of allowed tool names or a filter object.""" + + authorization: Optional[str] = None + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Optional[ + Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + ] = None + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors + [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors). + + This field is deprecated for models released after September 1, 2026. Use + `server_url` to connect to a remote MCP server, or `tunnel_id` to connect + through a Secure MCP Tunnel. + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + defer_loading: Optional[bool] = None + """Whether this MCP tool is deferred and discovered via tool search.""" + + headers: Optional[Dict[str, str]] = None + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[McpRequireApproval] = None + """Specify which of the MCP server's tools require approval.""" + + server_description: Optional[str] = None + """Optional description of the MCP server, used to provide more context.""" + + server_url: Optional[str] = None + """The URL for the MCP server. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: Optional[str] = None + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + +CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy: TypeAlias = Annotated[ + Union[ContainerNetworkPolicyDisabled, ContainerNetworkPolicyAllowlist], PropertyInfo(discriminator="type") +] + + +class CodeInterpreterContainerCodeInterpreterToolAuto(BaseModel): + """Configuration for a code interpreter container. + + Optionally specify the IDs of the files to run the code on. + """ + + type: Literal["auto"] + """Always `auto`.""" + + file_ids: Optional[List[str]] = None + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit for the code interpreter container.""" + + network_policy: Optional[CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy] = None + """Network access policy for the container.""" + + +CodeInterpreterContainer: TypeAlias = Union[str, CodeInterpreterContainerCodeInterpreterToolAuto] + + +class CodeInterpreter(BaseModel): + """A tool that runs Python code to help generate a response to a prompt.""" + + container: CodeInterpreterContainer + """The code interpreter container. + + Can be a container ID or an object that specifies uploaded file IDs to make + available to your code, along with an optional `memory_limit` setting. + """ + + type: Literal["code_interpreter"] + """The type of the code interpreter tool. Always `code_interpreter`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + +class ProgrammaticToolCalling(BaseModel): + type: Literal["programmatic_tool_calling"] + """The type of the tool. Always `programmatic_tool_calling`.""" + + +class ImageGenerationInputImageMask(BaseModel): + """Optional mask for inpainting. + + Contains `image_url` + (string, optional) and `file_id` (string, optional). + """ + + file_id: Optional[str] = None + """File ID for the mask image.""" + + image_url: Optional[str] = None + """Base64-encoded mask image.""" + + +class ImageGeneration(BaseModel): + """A tool that generates images using the GPT image models.""" + + type: Literal["image_generation"] + """The type of the image generation tool. Always `image_generation`.""" + + action: Optional[Literal["generate", "edit", "auto"]] = None + """Whether to generate a new image or edit an existing image. Default: `auto`.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] = None + """Allows to set transparency for the background of the generated image(s). + + Must be one of `transparent`, `opaque`, or `auto` (default value). When `auto` + is used, the model will automatically determine the best background for the + image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + """ + + input_fidelity: Optional[Literal["high", "low"]] = None + """Controls fidelity to the original input image(s). + + This parameter is supported for GPT image models that support input fidelity. + `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter. + """ + + input_image_mask: Optional[ImageGenerationInputImageMask] = None + """Optional mask for inpainting. + + Contains `image_url` (string, optional) and `file_id` (string, optional). + """ + + model: Union[ + str, + Literal[ + "gpt-image-1", + "gpt-image-1-mini", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-2.5-sunburst", + "gpt-image-2.5-sunburst-2026-09-08", + "gpt-image-2.5-flare", + "gpt-image-2.5-flare-2026-09-08", + "gpt-image-1.5", + "chatgpt-image-latest", + ], + None, + ] = None + """The image generation model to use. + + One of `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default: + `gpt-image-1`. + """ + + moderation: Optional[Literal["auto", "low"]] = None + """Moderation level for the generated image. Default: `auto`.""" + + output_compression: Optional[int] = None + """Compression level for the output image. Default: 100.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] = None + """The output format of the generated image. + + One of `png`, `webp`, or `jpeg`. Default: `png`. + """ + + partial_images: Optional[int] = None + """ + Number of partial images to generate in streaming mode, from 0 (default value) + to 3. + """ + + quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None + """The quality of the generated image. + + The GPT image models support `low`, `medium`, and `high`. + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, also support `xhigh` and `max`. Default: `auto`. + """ + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"], None] = None + """The size of the generated images. + + For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and + `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as + `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be + divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. + Resolutions above `2560x1440` are experimental, and the maximum supported + resolution is `3840x2160`. The requested size must also satisfy the model's + current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and + `1024x1536` are supported by the GPT image models; `auto` is supported for + models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, + `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, + or `1024x1792`. + """ + + +class LocalShell(BaseModel): + """A tool that allows the model to execute shell commands in a local environment.""" + + type: Literal["local_shell"] + """The type of the local shell tool. Always `local_shell`.""" + + +Tool: TypeAlias = Annotated[ + Union[ + FunctionTool, + FileSearchTool, + ComputerTool, + ComputerUsePreviewTool, + WebSearchTool, + Mcp, + CodeInterpreter, + ProgrammaticToolCalling, + ImageGeneration, + LocalShell, + FunctionShellTool, + CustomTool, + NamespaceTool, + ToolSearchTool, + WebSearchPreviewTool, + ApplyPatchTool, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_allowed.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_allowed.py new file mode 100644 index 0000000..cf89508 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_allowed.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolChoiceAllowed"] + + +class ToolChoiceAllowed(BaseModel): + """Constrains the tools available to the model to a pre-defined set.""" + + mode: Literal["auto", "required"] + """Constrains the tools available to the model to a pre-defined set. + + `auto` allows the model to pick from among the allowed tools and generate a + message. + + `required` requires the model to call one or more of the allowed tools. + """ + + tools: List[Dict[str, object]] + """A list of tool definitions that the model should be allowed to call. + + For the Responses API, the list of tool definitions might look like: + + ```json + [ + { "type": "function", "name": "get_weather" }, + { "type": "mcp", "server_label": "deepwiki" }, + { "type": "image_generation" } + ] + ``` + """ + + type: Literal["allowed_tools"] + """Allowed tool configuration type. Always `allowed_tools`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_allowed_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_allowed_param.py new file mode 100644 index 0000000..8c6b555 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_allowed_param.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolChoiceAllowedParam"] + + +class ToolChoiceAllowedParam(TypedDict, total=False): + """Constrains the tools available to the model to a pre-defined set.""" + + mode: Required[Literal["auto", "required"]] + """Constrains the tools available to the model to a pre-defined set. + + `auto` allows the model to pick from among the allowed tools and generate a + message. + + `required` requires the model to call one or more of the allowed tools. + """ + + tools: Required[Iterable[Dict[str, object]]] + """A list of tool definitions that the model should be allowed to call. + + For the Responses API, the list of tool definitions might look like: + + ```json + [ + { "type": "function", "name": "get_weather" }, + { "type": "mcp", "server_label": "deepwiki" }, + { "type": "image_generation" } + ] + ``` + """ + + type: Required[Literal["allowed_tools"]] + """Allowed tool configuration type. Always `allowed_tools`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_apply_patch.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_apply_patch.py new file mode 100644 index 0000000..0602696 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_apply_patch.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolChoiceApplyPatch"] + + +class ToolChoiceApplyPatch(BaseModel): + """Forces the model to call the apply_patch tool when executing a tool call.""" + + type: Literal["apply_patch"] + """The tool to call. Always `apply_patch`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_apply_patch_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_apply_patch_param.py new file mode 100644 index 0000000..2edf6f5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_apply_patch_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolChoiceApplyPatchParam"] + + +class ToolChoiceApplyPatchParam(TypedDict, total=False): + """Forces the model to call the apply_patch tool when executing a tool call.""" + + type: Required[Literal["apply_patch"]] + """The tool to call. Always `apply_patch`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_custom.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_custom.py new file mode 100644 index 0000000..90d382d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_custom.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolChoiceCustom"] + + +class ToolChoiceCustom(BaseModel): + """Use this option to force the model to call a specific custom tool.""" + + name: str + """The name of the custom tool to call.""" + + type: Literal["custom"] + """For custom tool calling, the type is always `custom`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_custom_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_custom_param.py new file mode 100644 index 0000000..1eb8524 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_custom_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolChoiceCustomParam"] + + +class ToolChoiceCustomParam(TypedDict, total=False): + """Use this option to force the model to call a specific custom tool.""" + + name: Required[str] + """The name of the custom tool to call.""" + + type: Required[Literal["custom"]] + """For custom tool calling, the type is always `custom`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_function.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_function.py new file mode 100644 index 0000000..698ed18 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_function.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolChoiceFunction"] + + +class ToolChoiceFunction(BaseModel): + """Use this option to force the model to call a specific function.""" + + name: str + """The name of the function to call.""" + + type: Literal["function"] + """For function calling, the type is always `function`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_function_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_function_param.py new file mode 100644 index 0000000..8e5d389 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_function_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolChoiceFunctionParam"] + + +class ToolChoiceFunctionParam(TypedDict, total=False): + """Use this option to force the model to call a specific function.""" + + name: Required[str] + """The name of the function to call.""" + + type: Required[Literal["function"]] + """For function calling, the type is always `function`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_mcp.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_mcp.py new file mode 100644 index 0000000..3f73882 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_mcp.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolChoiceMcp"] + + +class ToolChoiceMcp(BaseModel): + """ + Use this option to force the model to call a specific tool on a remote MCP server. + """ + + server_label: str + """The label of the MCP server to use.""" + + type: Literal["mcp"] + """For MCP tools, the type is always `mcp`.""" + + name: Optional[str] = None + """The name of the tool to call on the server.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_mcp_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_mcp_param.py new file mode 100644 index 0000000..8a85c94 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_mcp_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolChoiceMcpParam"] + + +class ToolChoiceMcpParam(TypedDict, total=False): + """ + Use this option to force the model to call a specific tool on a remote MCP server. + """ + + server_label: Required[str] + """The label of the MCP server to use.""" + + type: Required[Literal["mcp"]] + """For MCP tools, the type is always `mcp`.""" + + name: Optional[str] + """The name of the tool to call on the server.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_options.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_options.py new file mode 100644 index 0000000..f1354da --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_options.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ToolChoiceOptions"] + +ToolChoiceOptions: TypeAlias = Literal["none", "auto", "required"] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_shell.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_shell.py new file mode 100644 index 0000000..0cda346 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_shell.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolChoiceShell"] + + +class ToolChoiceShell(BaseModel): + """Forces the model to call the shell tool when a tool call is required.""" + + type: Literal["shell"] + """The tool to call. Always `shell`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_shell_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_shell_param.py new file mode 100644 index 0000000..3e1848f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_shell_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolChoiceShellParam"] + + +class ToolChoiceShellParam(TypedDict, total=False): + """Forces the model to call the shell tool when a tool call is required.""" + + type: Required[Literal["shell"]] + """The tool to call. Always `shell`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_types.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_types.py new file mode 100644 index 0000000..1b545a4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_types.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolChoiceTypes"] + + +class ToolChoiceTypes(BaseModel): + """ + Indicates that the model should use a built-in tool to generate a response. + [Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools). + """ + + type: Literal[ + "file_search", + "web_search_preview", + "computer", + "computer_use_preview", + "computer_use", + "web_search_preview_2025_03_11", + "image_generation", + "code_interpreter", + ] + """The type of hosted tool the model should to use. + + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + + Allowed values are: + + - `file_search` + - `web_search_preview` + - `computer` + - `computer_use_preview` + - `computer_use` + - `code_interpreter` + - `image_generation` + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_types_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_types_param.py new file mode 100644 index 0000000..4b56501 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_choice_types_param.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolChoiceTypesParam"] + + +class ToolChoiceTypesParam(TypedDict, total=False): + """ + Indicates that the model should use a built-in tool to generate a response. + [Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools). + """ + + type: Required[ + Literal[ + "file_search", + "web_search_preview", + "computer", + "computer_use_preview", + "computer_use", + "web_search_preview_2025_03_11", + "image_generation", + "code_interpreter", + ] + ] + """The type of hosted tool the model should to use. + + Learn more about + [built-in tools](https://developers.openai.com/api/docs/guides/tools). + + Allowed values are: + + - `file_search` + - `web_search_preview` + - `computer` + - `computer_use_preview` + - `computer_use` + - `code_interpreter` + - `image_generation` + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_param.py new file mode 100644 index 0000000..8bef6cb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_param.py @@ -0,0 +1,394 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from . import web_search_tool_param +from ..chat import ChatCompletionFunctionToolParam +from ..._types import SequenceNotStr +from .custom_tool_param import CustomToolParam +from .computer_tool_param import ComputerToolParam +from .function_tool_param import FunctionToolParam +from .namespace_tool_param import NamespaceToolParam +from .web_search_tool_param import WebSearchToolParam +from .apply_patch_tool_param import ApplyPatchToolParam +from .file_search_tool_param import FileSearchToolParam +from .tool_search_tool_param import ToolSearchToolParam +from .function_shell_tool_param import FunctionShellToolParam +from .web_search_preview_tool_param import WebSearchPreviewToolParam +from .computer_use_preview_tool_param import ComputerUsePreviewToolParam +from .container_network_policy_disabled_param import ContainerNetworkPolicyDisabledParam +from .container_network_policy_allowlist_param import ContainerNetworkPolicyAllowlistParam + +__all__ = [ + "ToolParam", + "Mcp", + "McpAllowedTools", + "McpAllowedToolsMcpToolFilter", + "McpRequireApproval", + "McpRequireApprovalMcpToolApprovalFilter", + "McpRequireApprovalMcpToolApprovalFilterAlways", + "McpRequireApprovalMcpToolApprovalFilterNever", + "CodeInterpreter", + "CodeInterpreterContainer", + "CodeInterpreterContainerCodeInterpreterToolAuto", + "CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy", + "ProgrammaticToolCalling", + "ImageGeneration", + "ImageGenerationInputImageMask", + "LocalShell", +] + +WebSearchTool = web_search_tool_param.WebSearchToolParam +WebSearchToolFilters = web_search_tool_param.Filters +WebSearchToolUserLocation = web_search_tool_param.UserLocation + + +class McpAllowedToolsMcpToolFilter(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +McpAllowedTools: TypeAlias = Union[SequenceNotStr[str], McpAllowedToolsMcpToolFilter] + + +class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + + always: McpRequireApprovalMcpToolApprovalFilterAlways + """A filter object to specify which tools are allowed.""" + + never: McpRequireApprovalMcpToolApprovalFilterNever + """A filter object to specify which tools are allowed.""" + + +McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"]] + + +class Mcp(TypedDict, total=False): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp). + """ + + server_label: Required[str] + """A label for this MCP server, used to identify it in tool calls.""" + + type: Required[Literal["mcp"]] + """The type of the MCP tool. Always `mcp`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + allowed_tools: Optional[McpAllowedTools] + """List of allowed tool names or a filter object.""" + + authorization: str + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors + [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors). + + This field is deprecated for models released after September 1, 2026. Use + `server_url` to connect to a remote MCP server, or `tunnel_id` to connect + through a Secure MCP Tunnel. + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + defer_loading: bool + """Whether this MCP tool is deferred and discovered via tool search.""" + + headers: Optional[Dict[str, str]] + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[McpRequireApproval] + """Specify which of the MCP server's tools require approval.""" + + server_description: str + """Optional description of the MCP server, used to provide more context.""" + + server_url: str + """The URL for the MCP server. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: str + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + +CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy: TypeAlias = Union[ + ContainerNetworkPolicyDisabledParam, ContainerNetworkPolicyAllowlistParam +] + + +class CodeInterpreterContainerCodeInterpreterToolAuto(TypedDict, total=False): + """Configuration for a code interpreter container. + + Optionally specify the IDs of the files to run the code on. + """ + + type: Required[Literal["auto"]] + """Always `auto`.""" + + file_ids: SequenceNotStr[str] + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] + """The memory limit for the code interpreter container.""" + + network_policy: CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy + """Network access policy for the container.""" + + +CodeInterpreterContainer: TypeAlias = Union[str, CodeInterpreterContainerCodeInterpreterToolAuto] + + +class CodeInterpreter(TypedDict, total=False): + """A tool that runs Python code to help generate a response to a prompt.""" + + container: Required[CodeInterpreterContainer] + """The code interpreter container. + + Can be a container ID or an object that specifies uploaded file IDs to make + available to your code, along with an optional `memory_limit` setting. + """ + + type: Required[Literal["code_interpreter"]] + """The type of the code interpreter tool. Always `code_interpreter`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + +class ProgrammaticToolCalling(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The type of the tool. Always `programmatic_tool_calling`.""" + + +class ImageGenerationInputImageMask(TypedDict, total=False): + """Optional mask for inpainting. + + Contains `image_url` + (string, optional) and `file_id` (string, optional). + """ + + file_id: str + """File ID for the mask image.""" + + image_url: str + """Base64-encoded mask image.""" + + +class ImageGeneration(TypedDict, total=False): + """A tool that generates images using the GPT image models.""" + + type: Required[Literal["image_generation"]] + """The type of the image generation tool. Always `image_generation`.""" + + action: Literal["generate", "edit", "auto"] + """Whether to generate a new image or edit an existing image. Default: `auto`.""" + + background: Literal["transparent", "opaque", "auto"] + """Allows to set transparency for the background of the generated image(s). + + Must be one of `transparent`, `opaque`, or `auto` (default value). When `auto` + is used, the model will automatically determine the best background for the + image. + + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, support `opaque` and `transparent` backgrounds. Transparent + backgrounds are available for supported GPT Image models. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, + set the output format to `png` or `webp`. + """ + + input_fidelity: Optional[Literal["high", "low"]] + """Controls fidelity to the original input image(s). + + This parameter is supported for GPT image models that support input fidelity. + `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter. + """ + + input_image_mask: ImageGenerationInputImageMask + """Optional mask for inpainting. + + Contains `image_url` (string, optional) and `file_id` (string, optional). + """ + + model: Union[ + str, + Literal[ + "gpt-image-1", + "gpt-image-1-mini", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-2.5-sunburst", + "gpt-image-2.5-sunburst-2026-09-08", + "gpt-image-2.5-flare", + "gpt-image-2.5-flare-2026-09-08", + "gpt-image-1.5", + "chatgpt-image-latest", + ], + ] + """The image generation model to use. + + One of `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, + `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default: + `gpt-image-1`. + """ + + moderation: Literal["auto", "low"] + """Moderation level for the generated image. Default: `auto`.""" + + output_compression: int + """Compression level for the output image. Default: 100.""" + + output_format: Literal["png", "webp", "jpeg"] + """The output format of the generated image. + + One of `png`, `webp`, or `jpeg`. Default: `png`. + """ + + partial_images: int + """ + Number of partial images to generate in streaming mode, from 0 (default value) + to 3. + """ + + quality: Literal["low", "medium", "high", "xhigh", "max", "auto"] + """The quality of the generated image. + + The GPT image models support `low`, `medium`, and `high`. + `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` + snapshots, also support `xhigh` and `max`. Default: `auto`. + """ + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]] + """The size of the generated images. + + For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, + `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and + `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as + `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be + divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. + Resolutions above `2560x1440` are experimental, and the maximum supported + resolution is `3840x2160`. The requested size must also satisfy the model's + current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and + `1024x1536` are supported by the GPT image models; `auto` is supported for + models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, + `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, + or `1024x1792`. + """ + + +class LocalShell(TypedDict, total=False): + """A tool that allows the model to execute shell commands in a local environment.""" + + type: Required[Literal["local_shell"]] + """The type of the local shell tool. Always `local_shell`.""" + + +ToolParam: TypeAlias = Union[ + FunctionToolParam, + FileSearchToolParam, + ComputerToolParam, + ComputerUsePreviewToolParam, + WebSearchToolParam, + Mcp, + CodeInterpreter, + ProgrammaticToolCalling, + ImageGeneration, + LocalShell, + FunctionShellToolParam, + CustomToolParam, + NamespaceToolParam, + ToolSearchToolParam, + WebSearchPreviewToolParam, + ApplyPatchToolParam, +] + + +ParseableToolParam: TypeAlias = Union[ToolParam, ChatCompletionFunctionToolParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_search_tool.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_search_tool.py new file mode 100644 index 0000000..11b90b6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_search_tool.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolSearchTool"] + + +class ToolSearchTool(BaseModel): + """Hosted or BYOT tool search configuration for deferred tools.""" + + type: Literal["tool_search"] + """The type of the tool. Always `tool_search`.""" + + description: Optional[str] = None + """Description shown to the model for a client-executed tool search tool.""" + + execution: Optional[Literal["server", "client"]] = None + """Whether tool search is executed by the server or by the client.""" + + parameters: Optional[object] = None + """Parameter schema for a client-executed tool search tool.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/tool_search_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/tool_search_tool_param.py new file mode 100644 index 0000000..83b6196 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/tool_search_tool_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolSearchToolParam"] + + +class ToolSearchToolParam(TypedDict, total=False): + """Hosted or BYOT tool search configuration for deferred tools.""" + + type: Required[Literal["tool_search"]] + """The type of the tool. Always `tool_search`.""" + + description: Optional[str] + """Description shown to the model for a client-executed tool search tool.""" + + execution: Literal["server", "client"] + """Whether tool search is executed by the server or by the client.""" + + parameters: Optional[object] + """Parameter schema for a client-executed tool search tool.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/web_search_preview_tool.py b/venv/lib/python3.12/site-packages/openai/types/responses/web_search_preview_tool.py new file mode 100644 index 0000000..5bd2005 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/web_search_preview_tool.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["WebSearchPreviewTool", "UserLocation"] + + +class UserLocation(BaseModel): + """The approximate location of the user. + + If omitted or null, defaults to the United States. To avoid this fallback, pass `{"type": "approximate"}` without location fields. To localize results, provide the relevant location fields. + """ + + type: Literal["approximate"] + """The type of location approximation. Always `approximate`.""" + + city: Optional[str] = None + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] = None + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] = None + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] = None + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + +class WebSearchPreviewTool(BaseModel): + """This tool searches the web for relevant results to use in a response. + + Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + """ + + type: Literal["web_search_preview", "web_search_preview_2025_03_11"] + """The type of the web search tool. + + One of `web_search_preview` or `web_search_preview_2025_03_11`. + """ + + search_content_types: Optional[List[Literal["text", "image"]]] = None + + search_context_size: Optional[Literal["low", "medium", "high"]] = None + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] = None + """The approximate location of the user. + + If omitted or null, defaults to the United States. To avoid this fallback, pass + `{"type": "approximate"}` without location fields. To localize results, provide + the relevant location fields. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/web_search_preview_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/web_search_preview_tool_param.py new file mode 100644 index 0000000..fc4f07c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/web_search_preview_tool_param.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["WebSearchPreviewToolParam", "UserLocation"] + + +class UserLocation(TypedDict, total=False): + """The approximate location of the user. + + If omitted or null, defaults to the United States. To avoid this fallback, pass `{"type": "approximate"}` without location fields. To localize results, provide the relevant location fields. + """ + + type: Required[Literal["approximate"]] + """The type of location approximation. Always `approximate`.""" + + city: Optional[str] + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + +class WebSearchPreviewToolParam(TypedDict, total=False): + """This tool searches the web for relevant results to use in a response. + + Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + """ + + type: Required[Literal["web_search_preview", "web_search_preview_2025_03_11"]] + """The type of the web search tool. + + One of `web_search_preview` or `web_search_preview_2025_03_11`. + """ + + search_content_types: List[Literal["text", "image"]] + + search_context_size: Literal["low", "medium", "high"] + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] + """The approximate location of the user. + + If omitted or null, defaults to the United States. To avoid this fallback, pass + `{"type": "approximate"}` without location fields. To localize results, provide + the relevant location fields. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/web_search_tool.py b/venv/lib/python3.12/site-packages/openai/types/responses/web_search_tool.py new file mode 100644 index 0000000..bbdc014 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/web_search_tool.py @@ -0,0 +1,90 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["WebSearchTool", "Filters", "UserLocation"] + + +class Filters(BaseModel): + """Filters for the search.""" + + allowed_domains: Optional[List[str]] = None + """Allowed domains for the search. + + If not provided, all domains are allowed. Subdomains of the provided domains are + allowed as well. + + Example: `["pubmed.ncbi.nlm.nih.gov"]` + """ + + +class UserLocation(BaseModel): + """The approximate location of the user. + + If omitted or null, defaults to the + United States. To avoid this fallback, pass `{"type": "approximate"}` without + location fields. To localize results, provide the relevant location fields. + """ + + city: Optional[str] = None + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] = None + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] = None + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] = None + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + type: Optional[Literal["approximate"]] = None + """The type of location approximation. Always `approximate`.""" + + +class WebSearchTool(BaseModel): + """Search the Internet for sources related to the prompt. + + Learn more about the + [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + """ + + type: Literal["web_search", "web_search_2025_08_26"] + """The type of the web search tool. + + One of `web_search` or `web_search_2025_08_26`. + """ + + external_web_access: Optional[bool] = None + """Allow live internet access for web search. + + Defaults to true when omitted. When false, the web search tool runs in + offline/cache-only mode and will not fetch new external content. + """ + + filters: Optional[Filters] = None + """Filters for the search.""" + + search_context_size: Optional[Literal["low", "medium", "high"]] = None + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] = None + """The approximate location of the user. + + If omitted or null, defaults to the United States. To avoid this fallback, pass + `{"type": "approximate"}` without location fields. To localize results, provide + the relevant location fields. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/responses/web_search_tool_param.py b/venv/lib/python3.12/site-packages/openai/types/responses/web_search_tool_param.py new file mode 100644 index 0000000..1e6568a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/responses/web_search_tool_param.py @@ -0,0 +1,92 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["WebSearchToolParam", "Filters", "UserLocation"] + + +class Filters(TypedDict, total=False): + """Filters for the search.""" + + allowed_domains: Optional[SequenceNotStr[str]] + """Allowed domains for the search. + + If not provided, all domains are allowed. Subdomains of the provided domains are + allowed as well. + + Example: `["pubmed.ncbi.nlm.nih.gov"]` + """ + + +class UserLocation(TypedDict, total=False): + """The approximate location of the user. + + If omitted or null, defaults to the + United States. To avoid this fallback, pass `{"type": "approximate"}` without + location fields. To localize results, provide the relevant location fields. + """ + + city: Optional[str] + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + type: Literal["approximate"] + """The type of location approximation. Always `approximate`.""" + + +class WebSearchToolParam(TypedDict, total=False): + """Search the Internet for sources related to the prompt. + + Learn more about the + [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search). + """ + + type: Required[Literal["web_search", "web_search_2025_08_26"]] + """The type of the web search tool. + + One of `web_search` or `web_search_2025_08_26`. + """ + + external_web_access: bool + """Allow live internet access for web search. + + Defaults to true when omitted. When false, the web search tool runs in + offline/cache-only mode and will not fetch new external content. + """ + + filters: Optional[Filters] + """Filters for the search.""" + + search_context_size: Literal["low", "medium", "high"] + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] + """The approximate location of the user. + + If omitted or null, defaults to the United States. To avoid this fallback, pass + `{"type": "approximate"}` without location fields. To localize results, provide + the relevant location fields. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/safety/__init__.py b/venv/lib/python3.12/site-packages/openai/types/safety/__init__.py new file mode 100644 index 0000000..406a8fa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/safety/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .safety_case import SafetyCase as SafetyCase +from .safety_alert import SafetyAlert as SafetyAlert diff --git a/venv/lib/python3.12/site-packages/openai/types/safety/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/safety/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5177e4a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/safety/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/safety/__pycache__/safety_alert.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/safety/__pycache__/safety_alert.cpython-312.pyc new file mode 100644 index 0000000..16d8bb5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/safety/__pycache__/safety_alert.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/safety/__pycache__/safety_case.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/safety/__pycache__/safety_case.cpython-312.pyc new file mode 100644 index 0000000..100bad5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/safety/__pycache__/safety_case.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/safety/safety_alert.py b/venv/lib/python3.12/site-packages/openai/types/safety/safety_alert.py new file mode 100644 index 0000000..a86f4ae --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/safety/safety_alert.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SafetyAlert"] + + +class SafetyAlert(BaseModel): + id: str + + created_at: int + + error_type: Literal[ + "potentially_unintended_data_transfer", + "potentially_unintended_data_access", + "potentially_unintended_destructive_activity", + "other", + ] + + model: str + + object: Literal["safety.alert"] + + reason: Optional[str] = None + """ + A customer-safe description derived from error_type, or null for zero data + retention requests. + """ + + request_id: str + + request_paused: bool + """Whether block registration succeeded for this request. + + This does not confirm that response execution stopped. + """ + + response_id: str diff --git a/venv/lib/python3.12/site-packages/openai/types/safety/safety_case.py b/venv/lib/python3.12/site-packages/openai/types/safety/safety_case.py new file mode 100644 index 0000000..7a5d59a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/safety/safety_case.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SafetyCase", "Notice"] + + +class Notice(BaseModel): + type: Literal["warning", "deactivation"] + + +class SafetyCase(BaseModel): + id: str + + created_at: int + + entity_identifier: str + + notice: Notice + + object: Literal["safety.case"] + + reason: Optional[str] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__init__.py b/venv/lib/python3.12/site-packages/openai/types/shared/__init__.py new file mode 100644 index 0000000..741adb2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/__init__.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from .metadata import Metadata as Metadata +from .reasoning import Reasoning as Reasoning +from .all_models import AllModels as AllModels +from .chat_model import ChatModel as ChatModel +from .error_object import ErrorObject as ErrorObject +from .compound_filter import CompoundFilter as CompoundFilter +from .responses_model import ResponsesModel as ResponsesModel +from .oauth_error_code import OAuthErrorCode as OAuthErrorCode +from .reasoning_effort import ReasoningEffort as ReasoningEffort +from .comparison_filter import ComparisonFilter as ComparisonFilter +from .function_definition import FunctionDefinition as FunctionDefinition +from .function_parameters import FunctionParameters as FunctionParameters +from .response_format_text import ResponseFormatText as ResponseFormatText +from .custom_tool_input_format import CustomToolInputFormat as CustomToolInputFormat +from .response_format_json_object import ResponseFormatJSONObject as ResponseFormatJSONObject +from .response_format_json_schema import ResponseFormatJSONSchema as ResponseFormatJSONSchema +from .response_format_text_python import ResponseFormatTextPython as ResponseFormatTextPython +from .response_format_text_grammar import ResponseFormatTextGrammar as ResponseFormatTextGrammar diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c146b14 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/all_models.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/all_models.cpython-312.pyc new file mode 100644 index 0000000..e6fee7b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/all_models.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/chat_model.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/chat_model.cpython-312.pyc new file mode 100644 index 0000000..9f517c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/chat_model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/comparison_filter.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/comparison_filter.cpython-312.pyc new file mode 100644 index 0000000..d34bfb6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/comparison_filter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/compound_filter.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/compound_filter.cpython-312.pyc new file mode 100644 index 0000000..67e1f30 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/compound_filter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/custom_tool_input_format.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/custom_tool_input_format.cpython-312.pyc new file mode 100644 index 0000000..acadd7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/custom_tool_input_format.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/error_object.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/error_object.cpython-312.pyc new file mode 100644 index 0000000..9cd81e2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/error_object.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/function_definition.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/function_definition.cpython-312.pyc new file mode 100644 index 0000000..e9f263b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/function_definition.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/function_parameters.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/function_parameters.cpython-312.pyc new file mode 100644 index 0000000..fc8ee19 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/function_parameters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/metadata.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/metadata.cpython-312.pyc new file mode 100644 index 0000000..7afdc04 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/metadata.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/oauth_error_code.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/oauth_error_code.cpython-312.pyc new file mode 100644 index 0000000..a6a4d31 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/oauth_error_code.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/reasoning.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/reasoning.cpython-312.pyc new file mode 100644 index 0000000..ef166f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/reasoning.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/reasoning_effort.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/reasoning_effort.cpython-312.pyc new file mode 100644 index 0000000..a708ac2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/reasoning_effort.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_json_object.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_json_object.cpython-312.pyc new file mode 100644 index 0000000..6e5884a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_json_object.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_json_schema.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_json_schema.cpython-312.pyc new file mode 100644 index 0000000..f5e6cdd Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_json_schema.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_text.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_text.cpython-312.pyc new file mode 100644 index 0000000..b24ca3a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_text_grammar.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_text_grammar.cpython-312.pyc new file mode 100644 index 0000000..c3e11b9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_text_grammar.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_text_python.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_text_python.cpython-312.pyc new file mode 100644 index 0000000..e2db1a4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/response_format_text_python.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/responses_model.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/responses_model.cpython-312.pyc new file mode 100644 index 0000000..461f36a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared/__pycache__/responses_model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/all_models.py b/venv/lib/python3.12/site-packages/openai/types/shared/all_models.py new file mode 100644 index 0000000..f702f8a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/all_models.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from .chat_model import ChatModel + +__all__ = ["AllModels"] + +AllModels: TypeAlias = Union[ + str, + ChatModel, + Literal[ + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], +] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/chat_model.py b/venv/lib/python3.12/site-packages/openai/types/shared/chat_model.py new file mode 100644 index 0000000..dac8f9e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/chat_model.py @@ -0,0 +1,96 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ChatModel"] + +ChatModel: TypeAlias = Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", +] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/comparison_filter.py b/venv/lib/python3.12/site-packages/openai/types/shared/comparison_filter.py new file mode 100644 index 0000000..7bcc829 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/comparison_filter.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ComparisonFilter"] + + +class ComparisonFilter(BaseModel): + """ + A filter used to compare a specified attribute key to a given value using a defined comparison operation. + """ + + key: str + """The key to compare against the value.""" + + type: Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"] + """ + Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, + `nin`. + + - `eq`: equals + - `ne`: not equal + - `gt`: greater than + - `gte`: greater than or equal + - `lt`: less than + - `lte`: less than or equal + - `in`: in + - `nin`: not in + """ + + value: Union[str, float, bool, List[Union[str, float]]] + """ + The value to compare against the attribute key; supports string, number, or + boolean types. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/compound_filter.py b/venv/lib/python3.12/site-packages/openai/types/shared/compound_filter.py new file mode 100644 index 0000000..02d09c2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/compound_filter.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .comparison_filter import ComparisonFilter + +__all__ = ["CompoundFilter", "Filter"] + +Filter: TypeAlias = Union[ComparisonFilter, object] + + +class CompoundFilter(BaseModel): + """Combine multiple filters using `and` or `or`.""" + + filters: List[Filter] + """Array of filters to combine. + + Items can be `ComparisonFilter` or `CompoundFilter`. + """ + + type: Literal["and", "or"] + """Type of operation: `and` or `or`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/custom_tool_input_format.py b/venv/lib/python3.12/site-packages/openai/types/shared/custom_tool_input_format.py new file mode 100644 index 0000000..62ae8b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/custom_tool_input_format.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["CustomToolInputFormat", "Text", "Grammar"] + + +class Text(BaseModel): + """Unconstrained free-form text.""" + + type: Literal["text"] + """Unconstrained text format. Always `text`.""" + + +class Grammar(BaseModel): + """A grammar defined by the user.""" + + definition: str + """The grammar definition.""" + + syntax: Literal["lark", "regex"] + """The syntax of the grammar definition. One of `lark` or `regex`.""" + + type: Literal["grammar"] + """Grammar format. Always `grammar`.""" + + +CustomToolInputFormat: TypeAlias = Annotated[Union[Text, Grammar], PropertyInfo(discriminator="type")] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/error_object.py b/venv/lib/python3.12/site-packages/openai/types/shared/error_object.py new file mode 100644 index 0000000..7aa1920 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/error_object.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ErrorObject", "Misalignment", "MisalignmentSteer"] + + +class MisalignmentSteer(BaseModel): + """An optional public continuation instruction.""" + + message: str + """The public continuation instruction.""" + + +class Misalignment(BaseModel): + detailed_explanation: Optional[str] = None + """The public explanation for this block.""" + + error_type: Union[ + str, + Literal[ + "potentially_unintended_data_transfer", + "potentially_unintended_data_access", + "potentially_unintended_destructive_activity", + "other", + ], + None, + ] = None + """An optional classification; clients must accept additional values.""" + + steer: Optional[MisalignmentSteer] = None + """An optional public continuation instruction.""" + + +class ErrorObject(BaseModel): + code: Optional[str] = None + + message: str + + param: Optional[str] = None + + type: str + + misalignment: Optional[Misalignment] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/function_definition.py b/venv/lib/python3.12/site-packages/openai/types/shared/function_definition.py new file mode 100644 index 0000000..b7bbc98 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/function_definition.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .function_parameters import FunctionParameters + +__all__ = ["FunctionDefinition"] + + +class FunctionDefinition(BaseModel): + name: str + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + description: Optional[str] = None + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + parameters: Optional[FunctionParameters] = None + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](https://developers.openai.com/api/docs/guides/function-calling) + for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + Omitting `parameters` defines a function with an empty parameter list. + """ + + strict: Optional[bool] = None + """Whether to enable strict schema adherence when generating the function call. + + If set to true, the model will follow the exact schema defined in the + `parameters` field. Only a subset of JSON Schema is supported when `strict` is + `true`. Learn more about Structured Outputs in the + [function calling guide](https://developers.openai.com/api/docs/guides/function-calling). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/function_parameters.py b/venv/lib/python3.12/site-packages/openai/types/shared/function_parameters.py new file mode 100644 index 0000000..6a7ed56 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/function_parameters.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict +from typing_extensions import TypeAlias + +__all__ = ["FunctionParameters"] + +FunctionParameters: TypeAlias = Dict[str, object] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/metadata.py b/venv/lib/python3.12/site-packages/openai/types/shared/metadata.py new file mode 100644 index 0000000..854eb38 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/metadata.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict +from typing_extensions import TypeAlias + +__all__ = ["Metadata"] + +Metadata: TypeAlias = Dict[str, str] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/oauth_error_code.py b/venv/lib/python3.12/site-packages/openai/types/shared/oauth_error_code.py new file mode 100644 index 0000000..14b95a8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/oauth_error_code.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +__all__ = ["OAuthErrorCode"] + +OAuthErrorCode: TypeAlias = Union[Literal["invalid_grant", "invalid_subject_token"], str] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/reasoning.py b/venv/lib/python3.12/site-packages/openai/types/shared/reasoning.py new file mode 100644 index 0000000..d3a642f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/reasoning.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .reasoning_effort import ReasoningEffort + +__all__ = ["Reasoning"] + + +class Reasoning(BaseModel): + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + context: Optional[Literal["auto", "current_turn", "all_turns"]] = None + """ + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + + effort: Optional[ReasoningEffort] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + generate_summary: Optional[Literal["auto", "concise", "detailed"]] = None + """**Deprecated:** use `summary` instead. + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + mode: Union[str, Literal["standard", "pro"], None] = None + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] = None + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. + + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/reasoning_effort.py b/venv/lib/python3.12/site-packages/openai/types/shared/reasoning_effort.py new file mode 100644 index 0000000..b244c12 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/reasoning_effort.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal, TypeAlias + +__all__ = ["ReasoningEffort"] + +ReasoningEffort: TypeAlias = Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/response_format_json_object.py b/venv/lib/python3.12/site-packages/openai/types/shared/response_format_json_object.py new file mode 100644 index 0000000..c9aa41f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/response_format_json_object.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFormatJSONObject"] + + +class ResponseFormatJSONObject(BaseModel): + """JSON object response format. + + An older method of generating JSON responses. + Using `json_schema` is recommended for models that support it. Note that the + model will not generate JSON without a system or user message instructing it + to do so. + """ + + type: Literal["json_object"] + """The type of response format being defined. Always `json_object`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/response_format_json_schema.py b/venv/lib/python3.12/site-packages/openai/types/shared/response_format_json_schema.py new file mode 100644 index 0000000..2e9a924 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/response_format_json_schema.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["ResponseFormatJSONSchema", "JSONSchema"] + + +class JSONSchema(BaseModel): + """Structured Outputs configuration options, including a JSON Schema.""" + + name: str + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + description: Optional[str] = None + """ + A description of what the response format is for, used by the model to determine + how to respond in the format. + """ + + schema_: Optional[Dict[str, object]] = FieldInfo(alias="schema", default=None) + """ + The schema for the response format, described as a JSON Schema object. Learn how + to build JSON schemas [here](https://json-schema.org/). + """ + + strict: Optional[bool] = None + """ + Whether to enable strict schema adherence when generating the output. If set to + true, the model will always follow the exact schema defined in the `schema` + field. Only a subset of JSON Schema is supported when `strict` is `true`. To + learn more, read the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + """ + + +class ResponseFormatJSONSchema(BaseModel): + """JSON Schema response format. + + Used to generate structured JSON responses. + Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs). + """ + + json_schema: JSONSchema + """Structured Outputs configuration options, including a JSON Schema.""" + + type: Literal["json_schema"] + """The type of response format being defined. Always `json_schema`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/response_format_text.py b/venv/lib/python3.12/site-packages/openai/types/shared/response_format_text.py new file mode 100644 index 0000000..2ac1ce8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/response_format_text.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFormatText"] + + +class ResponseFormatText(BaseModel): + """Default response format. Used to generate text responses.""" + + type: Literal["text"] + """The type of response format being defined. Always `text`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/response_format_text_grammar.py b/venv/lib/python3.12/site-packages/openai/types/shared/response_format_text_grammar.py new file mode 100644 index 0000000..cfabbb5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/response_format_text_grammar.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFormatTextGrammar"] + + +class ResponseFormatTextGrammar(BaseModel): + """ + A custom grammar for the model to follow when generating text. + Learn more in the [custom grammars guide](https://developers.openai.com/api/docs/guides/function-calling#context-free-grammars). + """ + + grammar: str + """The custom grammar for the model to follow.""" + + type: Literal["grammar"] + """The type of response format being defined. Always `grammar`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/response_format_text_python.py b/venv/lib/python3.12/site-packages/openai/types/shared/response_format_text_python.py new file mode 100644 index 0000000..45b0288 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/response_format_text_python.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFormatTextPython"] + + +class ResponseFormatTextPython(BaseModel): + """Configure the model to generate valid Python code. + + See the + [custom grammars guide](https://developers.openai.com/api/docs/guides/function-calling#context-free-grammars) for more details. + """ + + type: Literal["python"] + """The type of response format being defined. Always `python`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/shared/responses_model.py b/venv/lib/python3.12/site-packages/openai/types/shared/responses_model.py new file mode 100644 index 0000000..efbbb06 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared/responses_model.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from .chat_model import ChatModel + +__all__ = ["ResponsesModel"] + +ResponsesModel: TypeAlias = Union[ + str, + ChatModel, + Literal[ + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], +] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__init__.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/__init__.py new file mode 100644 index 0000000..749b645 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/__init__.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from .metadata import Metadata as Metadata +from .reasoning import Reasoning as Reasoning +from .chat_model import ChatModel as ChatModel +from .compound_filter import CompoundFilter as CompoundFilter +from .responses_model import ResponsesModel as ResponsesModel +from .oauth_error_code import OAuthErrorCode as OAuthErrorCode +from .reasoning_effort import ReasoningEffort as ReasoningEffort +from .comparison_filter import ComparisonFilter as ComparisonFilter +from .function_definition import FunctionDefinition as FunctionDefinition +from .function_parameters import FunctionParameters as FunctionParameters +from .response_format_text import ResponseFormatText as ResponseFormatText +from .custom_tool_input_format import CustomToolInputFormat as CustomToolInputFormat +from .response_format_json_object import ResponseFormatJSONObject as ResponseFormatJSONObject +from .response_format_json_schema import ResponseFormatJSONSchema as ResponseFormatJSONSchema diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..8577a7a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/chat_model.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/chat_model.cpython-312.pyc new file mode 100644 index 0000000..7557526 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/chat_model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/comparison_filter.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/comparison_filter.cpython-312.pyc new file mode 100644 index 0000000..1a6f5b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/comparison_filter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/compound_filter.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/compound_filter.cpython-312.pyc new file mode 100644 index 0000000..5efdacc Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/compound_filter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/custom_tool_input_format.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/custom_tool_input_format.cpython-312.pyc new file mode 100644 index 0000000..51d43d3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/custom_tool_input_format.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/function_definition.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/function_definition.cpython-312.pyc new file mode 100644 index 0000000..b77bf53 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/function_definition.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/function_parameters.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/function_parameters.cpython-312.pyc new file mode 100644 index 0000000..e7b5276 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/function_parameters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/metadata.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/metadata.cpython-312.pyc new file mode 100644 index 0000000..4073951 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/metadata.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/oauth_error_code.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/oauth_error_code.cpython-312.pyc new file mode 100644 index 0000000..7c821a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/oauth_error_code.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/reasoning.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/reasoning.cpython-312.pyc new file mode 100644 index 0000000..ebf9c29 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/reasoning.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/reasoning_effort.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/reasoning_effort.cpython-312.pyc new file mode 100644 index 0000000..33e86f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/reasoning_effort.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/response_format_json_object.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/response_format_json_object.cpython-312.pyc new file mode 100644 index 0000000..059b914 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/response_format_json_object.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/response_format_json_schema.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/response_format_json_schema.cpython-312.pyc new file mode 100644 index 0000000..c4c2e46 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/response_format_json_schema.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/response_format_text.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/response_format_text.cpython-312.pyc new file mode 100644 index 0000000..c06785e Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/response_format_text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/responses_model.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/responses_model.cpython-312.pyc new file mode 100644 index 0000000..4920506 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/shared_params/__pycache__/responses_model.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/chat_model.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/chat_model.py new file mode 100644 index 0000000..5f97eeb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/chat_model.py @@ -0,0 +1,98 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ChatModel"] + +ChatModel: TypeAlias = Literal[ + "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", +] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/comparison_filter.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/comparison_filter.py new file mode 100644 index 0000000..1745884 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/comparison_filter.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["ComparisonFilter"] + + +class ComparisonFilter(TypedDict, total=False): + """ + A filter used to compare a specified attribute key to a given value using a defined comparison operation. + """ + + key: Required[str] + """The key to compare against the value.""" + + type: Required[Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"]] + """ + Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, + `nin`. + + - `eq`: equals + - `ne`: not equal + - `gt`: greater than + - `gte`: greater than or equal + - `lt`: less than + - `lte`: less than or equal + - `in`: in + - `nin`: not in + """ + + value: Required[Union[str, float, bool, SequenceNotStr[Union[str, float]]]] + """ + The value to compare against the attribute key; supports string, number, or + boolean types. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/compound_filter.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/compound_filter.py new file mode 100644 index 0000000..b5c6aaf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/compound_filter.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .comparison_filter import ComparisonFilter + +__all__ = ["CompoundFilter", "Filter"] + +Filter: TypeAlias = Union[ComparisonFilter, object] + + +class CompoundFilter(TypedDict, total=False): + """Combine multiple filters using `and` or `or`.""" + + filters: Required[Iterable[Filter]] + """Array of filters to combine. + + Items can be `ComparisonFilter` or `CompoundFilter`. + """ + + type: Required[Literal["and", "or"]] + """Type of operation: `and` or `or`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/custom_tool_input_format.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/custom_tool_input_format.py new file mode 100644 index 0000000..f53730e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/custom_tool_input_format.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["CustomToolInputFormat", "Text", "Grammar"] + + +class Text(TypedDict, total=False): + """Unconstrained free-form text.""" + + type: Required[Literal["text"]] + """Unconstrained text format. Always `text`.""" + + +class Grammar(TypedDict, total=False): + """A grammar defined by the user.""" + + definition: Required[str] + """The grammar definition.""" + + syntax: Required[Literal["lark", "regex"]] + """The syntax of the grammar definition. One of `lark` or `regex`.""" + + type: Required[Literal["grammar"]] + """Grammar format. Always `grammar`.""" + + +CustomToolInputFormat: TypeAlias = Union[Text, Grammar] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/function_definition.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/function_definition.py new file mode 100644 index 0000000..8837687 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/function_definition.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from .function_parameters import FunctionParameters + +__all__ = ["FunctionDefinition"] + + +class FunctionDefinition(TypedDict, total=False): + name: Required[str] + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + description: str + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + parameters: FunctionParameters + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](https://developers.openai.com/api/docs/guides/function-calling) + for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + Omitting `parameters` defines a function with an empty parameter list. + """ + + strict: Optional[bool] + """Whether to enable strict schema adherence when generating the function call. + + If set to true, the model will follow the exact schema defined in the + `parameters` field. Only a subset of JSON Schema is supported when `strict` is + `true`. Learn more about Structured Outputs in the + [function calling guide](https://developers.openai.com/api/docs/guides/function-calling). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/function_parameters.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/function_parameters.py new file mode 100644 index 0000000..bdf53da --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/function_parameters.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import TypeAlias + +__all__ = ["FunctionParameters"] + +FunctionParameters: TypeAlias = Dict[str, object] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/metadata.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/metadata.py new file mode 100644 index 0000000..d2c53f0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/metadata.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import TypeAlias + +__all__ = ["Metadata"] + +Metadata: TypeAlias = Dict[str, str] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/oauth_error_code.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/oauth_error_code.py new file mode 100644 index 0000000..c7d8b20 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/oauth_error_code.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias + +__all__ = ["OAuthErrorCode"] + +OAuthErrorCode: TypeAlias = Union[Literal["invalid_grant", "invalid_subject_token"], str] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/reasoning.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/reasoning.py new file mode 100644 index 0000000..e3bbf8d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/reasoning.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, TypedDict + +from ..shared.reasoning_effort import ReasoningEffort + +__all__ = ["Reasoning"] + + +class Reasoning(TypedDict, total=False): + """ + Configuration options for + [reasoning models](https://developers.openai.com/api/docs/guides/reasoning). + """ + + context: Optional[Literal["auto", "current_turn", "all_turns"]] + """ + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + + effort: Optional[ReasoningEffort] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for + model-specific support. + """ + + generate_summary: Optional[Literal["auto", "concise", "detailed"]] + """**Deprecated:** use `summary` instead. + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + mode: Union[str, Literal["standard", "pro"]] + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. + + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/reasoning_effort.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/reasoning_effort.py new file mode 100644 index 0000000..28a327c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/reasoning_effort.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypeAlias + +__all__ = ["ReasoningEffort"] + +ReasoningEffort: TypeAlias = Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/response_format_json_object.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/response_format_json_object.py new file mode 100644 index 0000000..2098fd8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/response_format_json_object.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseFormatJSONObject"] + + +class ResponseFormatJSONObject(TypedDict, total=False): + """JSON object response format. + + An older method of generating JSON responses. + Using `json_schema` is recommended for models that support it. Note that the + model will not generate JSON without a system or user message instructing it + to do so. + """ + + type: Required[Literal["json_object"]] + """The type of response format being defined. Always `json_object`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/response_format_json_schema.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/response_format_json_schema.py new file mode 100644 index 0000000..2b6bbdc --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/response_format_json_schema.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseFormatJSONSchema", "JSONSchema"] + + +class JSONSchema(TypedDict, total=False): + """Structured Outputs configuration options, including a JSON Schema.""" + + name: Required[str] + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + description: str + """ + A description of what the response format is for, used by the model to determine + how to respond in the format. + """ + + schema: Dict[str, object] + """ + The schema for the response format, described as a JSON Schema object. Learn how + to build JSON schemas [here](https://json-schema.org/). + """ + + strict: Optional[bool] + """ + Whether to enable strict schema adherence when generating the output. If set to + true, the model will always follow the exact schema defined in the `schema` + field. Only a subset of JSON Schema is supported when `strict` is `true`. To + learn more, read the + [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs). + """ + + +class ResponseFormatJSONSchema(TypedDict, total=False): + """JSON Schema response format. + + Used to generate structured JSON responses. + Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs). + """ + + json_schema: Required[JSONSchema] + """Structured Outputs configuration options, including a JSON Schema.""" + + type: Required[Literal["json_schema"]] + """The type of response format being defined. Always `json_schema`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/response_format_text.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/response_format_text.py new file mode 100644 index 0000000..36895e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/response_format_text.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseFormatText"] + + +class ResponseFormatText(TypedDict, total=False): + """Default response format. Used to generate text responses.""" + + type: Required[Literal["text"]] + """The type of response format being defined. Always `text`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/shared_params/responses_model.py b/venv/lib/python3.12/site-packages/openai/types/shared_params/responses_model.py new file mode 100644 index 0000000..3c9f91c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/shared_params/responses_model.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from ..shared.chat_model import ChatModel + +__all__ = ["ResponsesModel"] + +ResponsesModel: TypeAlias = Union[ + str, + ChatModel, + Literal[ + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research", + ], +] diff --git a/venv/lib/python3.12/site-packages/openai/types/skill.py b/venv/lib/python3.12/site-packages/openai/types/skill.py new file mode 100644 index 0000000..9368db8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/skill.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["Skill"] + + +class Skill(BaseModel): + id: str + """Unique identifier for the skill.""" + + created_at: int + """Unix timestamp (seconds) for when the skill was created.""" + + default_version: str + """Default version for the skill.""" + + description: str + """Description of the skill.""" + + latest_version: str + """Latest version for the skill.""" + + name: str + """Name of the skill.""" + + object: Literal["skill"] + """The object type, which is `skill`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/skill_create_params.py b/venv/lib/python3.12/site-packages/openai/types/skill_create_params.py new file mode 100644 index 0000000..0eb4a8c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/skill_create_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypedDict + +from .._types import FileTypes, SequenceNotStr + +__all__ = ["SkillCreateParams"] + + +class SkillCreateParams(TypedDict, total=False): + files: Union[SequenceNotStr[FileTypes], FileTypes] + """Skill files to upload (directory upload) or a single zip file.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/skill_list.py b/venv/lib/python3.12/site-packages/openai/types/skill_list.py new file mode 100644 index 0000000..a3f00f5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/skill_list.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .skill import Skill +from .._models import BaseModel + +__all__ = ["SkillList"] + + +class SkillList(BaseModel): + data: List[Skill] + """A list of items""" + + first_id: Optional[str] = None + """The ID of the first item in the list.""" + + has_more: bool + """Whether there are more items available.""" + + last_id: Optional[str] = None + """The ID of the last item in the list.""" + + object: Literal["list"] + """The type of object returned, must be `list`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/skill_list_params.py b/venv/lib/python3.12/site-packages/openai/types/skill_list_params.py new file mode 100644 index 0000000..1bf2d27 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/skill_list_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["SkillListParams"] + + +class SkillListParams(TypedDict, total=False): + after: str + """Identifier for the last item from the previous pagination request""" + + limit: int + """Number of items to retrieve""" + + order: Literal["asc", "desc"] + """Sort order of results by timestamp. + + Use `asc` for ascending order or `desc` for descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/skill_update_params.py b/venv/lib/python3.12/site-packages/openai/types/skill_update_params.py new file mode 100644 index 0000000..3af31ac --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/skill_update_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["SkillUpdateParams"] + + +class SkillUpdateParams(TypedDict, total=False): + default_version: Required[str] + """The skill version number to set as default.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/__init__.py b/venv/lib/python3.12/site-packages/openai/types/skills/__init__.py new file mode 100644 index 0000000..7400107 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/skills/__init__.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .skill_version import SkillVersion as SkillVersion +from .skill_version_list import SkillVersionList as SkillVersionList +from .version_list_params import VersionListParams as VersionListParams +from .deleted_skill_version import DeletedSkillVersion as DeletedSkillVersion +from .version_create_params import VersionCreateParams as VersionCreateParams diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d6709ac Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/deleted_skill_version.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/deleted_skill_version.cpython-312.pyc new file mode 100644 index 0000000..e62e317 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/deleted_skill_version.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/skill_version.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/skill_version.cpython-312.pyc new file mode 100644 index 0000000..4f2e7e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/skill_version.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/skill_version_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/skill_version_list.cpython-312.pyc new file mode 100644 index 0000000..14982e5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/skill_version_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/version_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/version_create_params.cpython-312.pyc new file mode 100644 index 0000000..d1f68e2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/version_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/version_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/version_list_params.cpython-312.pyc new file mode 100644 index 0000000..ebd8f0f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/skills/__pycache__/version_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/deleted_skill_version.py b/venv/lib/python3.12/site-packages/openai/types/skills/deleted_skill_version.py new file mode 100644 index 0000000..a1272f3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/skills/deleted_skill_version.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["DeletedSkillVersion"] + + +class DeletedSkillVersion(BaseModel): + id: str + + deleted: bool + + object: Literal["skill.version.deleted"] + + version: str + """The deleted skill version.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/skill_version.py b/venv/lib/python3.12/site-packages/openai/types/skills/skill_version.py new file mode 100644 index 0000000..749d74e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/skills/skill_version.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SkillVersion"] + + +class SkillVersion(BaseModel): + id: str + """Unique identifier for the skill version.""" + + created_at: int + """Unix timestamp (seconds) for when the version was created.""" + + description: str + """Description of the skill version.""" + + name: str + """Name of the skill version.""" + + object: Literal["skill.version"] + """The object type, which is `skill.version`.""" + + skill_id: str + """Identifier of the skill for this version.""" + + version: str + """Version number for this skill.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/skill_version_list.py b/venv/lib/python3.12/site-packages/openai/types/skills/skill_version_list.py new file mode 100644 index 0000000..56acc45 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/skills/skill_version_list.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .skill_version import SkillVersion + +__all__ = ["SkillVersionList"] + + +class SkillVersionList(BaseModel): + data: List[SkillVersion] + """A list of items""" + + first_id: Optional[str] = None + """The ID of the first item in the list.""" + + has_more: bool + """Whether there are more items available.""" + + last_id: Optional[str] = None + """The ID of the last item in the list.""" + + object: Literal["list"] + """The type of object returned, must be `list`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/version_create_params.py b/venv/lib/python3.12/site-packages/openai/types/skills/version_create_params.py new file mode 100644 index 0000000..c912b70 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/skills/version_create_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypedDict + +from ..._types import FileTypes, SequenceNotStr + +__all__ = ["VersionCreateParams"] + + +class VersionCreateParams(TypedDict, total=False): + default: bool + """Whether to set this version as the default.""" + + files: Union[SequenceNotStr[FileTypes], FileTypes] + """Skill files to upload (directory upload) or a single zip file.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/version_list_params.py b/venv/lib/python3.12/site-packages/openai/types/skills/version_list_params.py new file mode 100644 index 0000000..a0c5e28 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/skills/version_list_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["VersionListParams"] + + +class VersionListParams(TypedDict, total=False): + after: str + """The skill version ID to start after.""" + + limit: int + """Number of versions to retrieve.""" + + order: Literal["asc", "desc"] + """Sort order of results by version number.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/versions/__init__.py b/venv/lib/python3.12/site-packages/openai/types/skills/versions/__init__.py new file mode 100644 index 0000000..273c8b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/skills/versions/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/venv/lib/python3.12/site-packages/openai/types/skills/versions/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/skills/versions/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b8fe29d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/skills/versions/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/static_file_chunking_strategy.py b/venv/lib/python3.12/site-packages/openai/types/static_file_chunking_strategy.py new file mode 100644 index 0000000..e5e0d18 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/static_file_chunking_strategy.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["StaticFileChunkingStrategy"] + + +class StaticFileChunkingStrategy(BaseModel): + chunk_overlap_tokens: int + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: int + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/static_file_chunking_strategy_object.py b/venv/lib/python3.12/site-packages/openai/types/static_file_chunking_strategy_object.py new file mode 100644 index 0000000..c8d901d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/static_file_chunking_strategy_object.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel +from .static_file_chunking_strategy import StaticFileChunkingStrategy + +__all__ = ["StaticFileChunkingStrategyObject"] + + +class StaticFileChunkingStrategyObject(BaseModel): + static: StaticFileChunkingStrategy + + type: Literal["static"] + """Always `static`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/static_file_chunking_strategy_object_param.py b/venv/lib/python3.12/site-packages/openai/types/static_file_chunking_strategy_object_param.py new file mode 100644 index 0000000..1978677 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/static_file_chunking_strategy_object_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .static_file_chunking_strategy_param import StaticFileChunkingStrategyParam + +__all__ = ["StaticFileChunkingStrategyObjectParam"] + + +class StaticFileChunkingStrategyObjectParam(TypedDict, total=False): + """Customize your own chunking strategy by setting chunk size and chunk overlap.""" + + static: Required[StaticFileChunkingStrategyParam] + + type: Required[Literal["static"]] + """Always `static`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/static_file_chunking_strategy_param.py b/venv/lib/python3.12/site-packages/openai/types/static_file_chunking_strategy_param.py new file mode 100644 index 0000000..4b6847f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/static_file_chunking_strategy_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["StaticFileChunkingStrategyParam"] + + +class StaticFileChunkingStrategyParam(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/upload.py b/venv/lib/python3.12/site-packages/openai/types/upload.py new file mode 100644 index 0000000..8b57923 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/upload.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel +from .file_object import FileObject + +__all__ = ["Upload"] + + +class Upload(BaseModel): + """The Upload object can accept byte chunks in the form of Parts.""" + + id: str + """The Upload unique identifier, which can be referenced in API endpoints.""" + + bytes: int + """The intended number of bytes to be uploaded.""" + + created_at: int + """The Unix timestamp (in seconds) for when the Upload was created.""" + + expires_at: int + """The Unix timestamp (in seconds) for when the Upload will expire.""" + + filename: str + """The name of the file to be uploaded.""" + + object: Literal["upload"] + """The object type, which is always "upload".""" + + purpose: str + """The intended purpose of the file. + + [Please refer here](https://developers.openai.com/api/reference/resources/files#%28resource%29%20files%20%3E%20%28model%29%20file_object%20%3E%20%28schema%29%20%3E%20%28property%29%20purpose) + for acceptable values. + """ + + status: Literal["pending", "completed", "cancelled", "expired"] + """The status of the Upload.""" + + file: Optional[FileObject] = None + """The `File` object represents a document that has been uploaded to OpenAI.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/upload_complete_params.py b/venv/lib/python3.12/site-packages/openai/types/upload_complete_params.py new file mode 100644 index 0000000..2d5ab1c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/upload_complete_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["UploadCompleteParams"] + + +class UploadCompleteParams(TypedDict, total=False): + part_ids: Required[SequenceNotStr[str]] + """The ordered list of Part IDs.""" + + md5: str + """ + The optional md5 checksum for the file contents to verify if the bytes uploaded + matches what you expect. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/upload_create_params.py b/venv/lib/python3.12/site-packages/openai/types/upload_create_params.py new file mode 100644 index 0000000..0a43068 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/upload_create_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .file_purpose import FilePurpose + +__all__ = ["UploadCreateParams", "ExpiresAfter"] + + +class UploadCreateParams(TypedDict, total=False): + bytes: Required[int] + """The number of bytes in the file you are uploading.""" + + filename: Required[str] + """The name of the file to upload.""" + + mime_type: Required[str] + """The MIME type of the file. + + This must fall within the supported MIME types for your file purpose. See the + supported MIME types for assistants and vision. + """ + + purpose: Required[FilePurpose] + """The intended purpose of the uploaded file. + + See the + [documentation on File purposes](https://developers.openai.com/api/reference/resources/files/methods/create#%28resource%29%20files%20%3E%20%28method%29%20create%20%3E%20%28params%29%200%20%3E%20%28param%29%20purpose%20%3E%20%28schema%29). + """ + + expires_after: ExpiresAfter + """The expiration policy for a file. + + By default, files with `purpose=batch` expire after 30 days and all other files + are persisted until they are manually deleted. + """ + + +class ExpiresAfter(TypedDict, total=False): + """The expiration policy for a file. + + By default, files with `purpose=batch` expire after 30 days and all other files are persisted until they are manually deleted. + """ + + anchor: Required[Literal["created_at"]] + """Anchor timestamp after which the expiration policy applies. + + Supported anchors: `created_at`. + """ + + seconds: Required[int] + """The number of seconds after the anchor time that the file will expire. + + Must be between 3600 (1 hour) and 2592000 (30 days). + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/uploads/__init__.py b/venv/lib/python3.12/site-packages/openai/types/uploads/__init__.py new file mode 100644 index 0000000..532940b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/uploads/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .upload_part import UploadPart as UploadPart +from .part_create_params import PartCreateParams as PartCreateParams diff --git a/venv/lib/python3.12/site-packages/openai/types/uploads/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/uploads/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..80d99da Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/uploads/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/uploads/__pycache__/part_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/uploads/__pycache__/part_create_params.cpython-312.pyc new file mode 100644 index 0000000..4e3c7b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/uploads/__pycache__/part_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/uploads/__pycache__/upload_part.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/uploads/__pycache__/upload_part.cpython-312.pyc new file mode 100644 index 0000000..35c4835 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/uploads/__pycache__/upload_part.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/uploads/part_create_params.py b/venv/lib/python3.12/site-packages/openai/types/uploads/part_create_params.py new file mode 100644 index 0000000..38da27c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/uploads/part_create_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ..._types import FileTypes + +__all__ = ["PartCreateParams"] + + +class PartCreateParams(TypedDict, total=False): + data: Required[FileTypes] + """The chunk of bytes for this Part.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/uploads/upload_part.py b/venv/lib/python3.12/site-packages/openai/types/uploads/upload_part.py new file mode 100644 index 0000000..49fdb6d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/uploads/upload_part.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["UploadPart"] + + +class UploadPart(BaseModel): + """The upload Part represents a chunk of bytes we can add to an Upload object.""" + + id: str + """The upload Part unique identifier, which can be referenced in API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the Part was created.""" + + object: Literal["upload.part"] + """The object type, which is always `upload.part`.""" + + upload_id: str + """The ID of the Upload object that this Part was added to.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_store.py b/venv/lib/python3.12/site-packages/openai/types/vector_store.py new file mode 100644 index 0000000..40e9326 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_store.py @@ -0,0 +1,88 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel +from .shared.metadata import Metadata + +__all__ = ["VectorStore", "FileCounts", "ExpiresAfter"] + + +class FileCounts(BaseModel): + cancelled: int + """The number of files that were cancelled.""" + + completed: int + """The number of files that have been successfully processed.""" + + failed: int + """The number of files that have failed to process.""" + + in_progress: int + """The number of files that are currently being processed.""" + + total: int + """The total number of files.""" + + +class ExpiresAfter(BaseModel): + """The expiration policy for a vector store.""" + + anchor: Literal["last_active_at"] + """Anchor timestamp after which the expiration policy applies. + + Supported anchors: `last_active_at`. + """ + + days: int + """The number of days after the anchor time that the vector store will expire.""" + + +class VectorStore(BaseModel): + """ + A vector store is a collection of processed files can be used by the `file_search` tool. + """ + + id: str + """The identifier, which can be referenced in API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the vector store was created.""" + + file_counts: FileCounts + + last_active_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the vector store was last active.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the vector store.""" + + object: Literal["vector_store"] + """The object type, which is always `vector_store`.""" + + status: Literal["expired", "in_progress", "completed"] + """ + The status of the vector store, which can be either `expired`, `in_progress`, or + `completed`. A status of `completed` indicates that the vector store is ready + for use. + """ + + usage_bytes: int + """The total number of bytes used by the files in the vector store.""" + + expires_after: Optional[ExpiresAfter] = None + """The expiration policy for a vector store.""" + + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the vector store will expire.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_store_create_params.py b/venv/lib/python3.12/site-packages/openai/types/vector_store_create_params.py new file mode 100644 index 0000000..0f6e258 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_store_create_params.py @@ -0,0 +1,63 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from .._types import SequenceNotStr +from .shared_params.metadata import Metadata +from .file_chunking_strategy_param import FileChunkingStrategyParam + +__all__ = ["VectorStoreCreateParams", "ExpiresAfter"] + + +class VectorStoreCreateParams(TypedDict, total=False): + chunking_strategy: FileChunkingStrategyParam + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. Only applicable if `file_ids` is + non-empty. + """ + + description: str + """A description for the vector store. + + Can be used to describe the vector store's purpose. + """ + + expires_after: ExpiresAfter + """The expiration policy for a vector store.""" + + file_ids: SequenceNotStr[str] + """ + A list of [File](https://developers.openai.com/api/reference/resources/files) + IDs that the vector store should use. Useful for tools like `file_search` that + can access files. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the vector store.""" + + +class ExpiresAfter(TypedDict, total=False): + """The expiration policy for a vector store.""" + + anchor: Required[Literal["last_active_at"]] + """Anchor timestamp after which the expiration policy applies. + + Supported anchors: `last_active_at`. + """ + + days: Required[int] + """The number of days after the anchor time that the vector store will expire.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_store_deleted.py b/venv/lib/python3.12/site-packages/openai/types/vector_store_deleted.py new file mode 100644 index 0000000..e4a7605 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_store_deleted.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["VectorStoreDeleted"] + + +class VectorStoreDeleted(BaseModel): + id: str + + deleted: bool + + object: Literal["vector_store.deleted"] diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_store_list_params.py b/venv/lib/python3.12/site-packages/openai/types/vector_store_list_params.py new file mode 100644 index 0000000..9493285 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_store_list_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["VectorStoreListParams"] + + +class VectorStoreListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, starting with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_store_search_params.py b/venv/lib/python3.12/site-packages/openai/types/vector_store_search_params.py new file mode 100644 index 0000000..25abc2d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_store_search_params.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .._types import SequenceNotStr +from .shared_params.compound_filter import CompoundFilter +from .shared_params.comparison_filter import ComparisonFilter + +__all__ = ["VectorStoreSearchParams", "Filters", "RankingOptions"] + + +class VectorStoreSearchParams(TypedDict, total=False): + query: Required[Union[str, SequenceNotStr[str]]] + """A query string for a search""" + + filters: Filters + """A filter to apply based on file attributes.""" + + max_num_results: int + """The maximum number of results to return. + + This number should be between 1 and 50 inclusive. + """ + + ranking_options: RankingOptions + """Ranking options for search.""" + + rewrite_query: bool + """Whether to rewrite the natural language query for vector search.""" + + +Filters: TypeAlias = Union[ComparisonFilter, CompoundFilter] + + +class RankingOptions(TypedDict, total=False): + """Ranking options for search.""" + + ranker: Literal["none", "auto", "default-2024-11-15"] + """Enable re-ranking; set to `none` to disable, which can help reduce latency.""" + + score_threshold: float diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_store_search_response.py b/venv/lib/python3.12/site-packages/openai/types/vector_store_search_response.py new file mode 100644 index 0000000..4139fa0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_store_search_response.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["VectorStoreSearchResponse", "Content"] + + +class Content(BaseModel): + text: str + """The text content returned from search.""" + + type: Literal["text"] + """The type of content.""" + + +class VectorStoreSearchResponse(BaseModel): + attributes: Optional[Dict[str, Union[str, float, bool]]] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + content: List[Content] + """Content chunks from the file.""" + + file_id: str + """The ID of the vector store file.""" + + filename: str + """The name of the vector store file.""" + + score: float + """The similarity score for the result.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_store_update_params.py b/venv/lib/python3.12/site-packages/openai/types/vector_store_update_params.py new file mode 100644 index 0000000..2807c4e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_store_update_params.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from .shared_params.metadata import Metadata + +__all__ = ["VectorStoreUpdateParams", "ExpiresAfter"] + + +class VectorStoreUpdateParams(TypedDict, total=False): + expires_after: Optional[ExpiresAfter] + """The expiration policy for a vector store.""" + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: Optional[str] + """The name of the vector store.""" + + +class ExpiresAfter(TypedDict, total=False): + """The expiration policy for a vector store.""" + + anchor: Required[Literal["last_active_at"]] + """Anchor timestamp after which the expiration policy applies. + + Supported anchors: `last_active_at`. + """ + + days: Required[int] + """The number of days after the anchor time that the vector store will expire.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/__init__.py b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__init__.py new file mode 100644 index 0000000..5967d90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__init__.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .file_list_params import FileListParams as FileListParams +from .vector_store_file import VectorStoreFile as VectorStoreFile +from .file_create_params import FileCreateParams as FileCreateParams +from .file_update_params import FileUpdateParams as FileUpdateParams +from .file_content_response import FileContentResponse as FileContentResponse +from .vector_store_file_batch import VectorStoreFileBatch as VectorStoreFileBatch +from .file_batch_create_params import FileBatchCreateParams as FileBatchCreateParams +from .vector_store_file_deleted import VectorStoreFileDeleted as VectorStoreFileDeleted +from .file_batch_list_files_params import FileBatchListFilesParams as FileBatchListFilesParams diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6e9b52f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_batch_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_batch_create_params.cpython-312.pyc new file mode 100644 index 0000000..358158d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_batch_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_batch_list_files_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_batch_list_files_params.cpython-312.pyc new file mode 100644 index 0000000..6e86609 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_batch_list_files_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_content_response.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_content_response.cpython-312.pyc new file mode 100644 index 0000000..111870b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_content_response.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_create_params.cpython-312.pyc new file mode 100644 index 0000000..d5ada0d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_list_params.cpython-312.pyc new file mode 100644 index 0000000..84b9de1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_update_params.cpython-312.pyc new file mode 100644 index 0000000..52ed5ce Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/file_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/vector_store_file.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/vector_store_file.cpython-312.pyc new file mode 100644 index 0000000..43e407b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/vector_store_file.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/vector_store_file_batch.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/vector_store_file_batch.cpython-312.pyc new file mode 100644 index 0000000..7ba2a5c Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/vector_store_file_batch.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/vector_store_file_deleted.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/vector_store_file_deleted.cpython-312.pyc new file mode 100644 index 0000000..e301030 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/vector_stores/__pycache__/vector_store_file_deleted.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_batch_create_params.py b/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_batch_create_params.py new file mode 100644 index 0000000..0654588 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_batch_create_params.py @@ -0,0 +1,76 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Required, TypedDict + +from ..._types import SequenceNotStr +from ..file_chunking_strategy_param import FileChunkingStrategyParam + +__all__ = ["FileBatchCreateParams", "File"] + + +class FileBatchCreateParams(TypedDict, total=False): + attributes: Optional[Dict[str, Union[str, float, bool]]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + chunking_strategy: FileChunkingStrategyParam + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. Only applicable if `file_ids` is + non-empty. + """ + + file_ids: SequenceNotStr[str] + """ + A list of [File](https://developers.openai.com/api/reference/resources/files) + IDs that the vector store should use. Useful for tools like `file_search` that + can access files. If `attributes` or `chunking_strategy` are provided, they will + be applied to all files in the batch. The maximum batch size is 2000 files. This + endpoint is recommended for multi-file ingestion and helps reduce + per-vector-store write request pressure. Mutually exclusive with `files`. + """ + + files: Iterable[File] + """ + A list of objects that each include a `file_id` plus optional `attributes` or + `chunking_strategy`. Use this when you need to override metadata for specific + files. The global `attributes` or `chunking_strategy` will be ignored and must + be specified for each file. The maximum batch size is 2000 files. This endpoint + is recommended for multi-file ingestion and helps reduce per-vector-store write + request pressure. Mutually exclusive with `file_ids`. + """ + + +class File(TypedDict, total=False): + file_id: Required[str] + """ + A [File](https://developers.openai.com/api/reference/resources/files) ID that + the vector store should use. Useful for tools like `file_search` that can access + files. For multi-file ingestion, we recommend + [`file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create) + to minimize per-vector-store write requests. + """ + + attributes: Optional[Dict[str, Union[str, float, bool]]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + chunking_strategy: FileChunkingStrategyParam + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. Only applicable if `file_ids` is + non-empty. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_batch_list_files_params.py b/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_batch_list_files_params.py new file mode 100644 index 0000000..1542374 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_batch_list_files_params.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["FileBatchListFilesParams"] + + +class FileBatchListFilesParams(TypedDict, total=False): + vector_store_id: Required[str] + + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, starting with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + filter: Literal["in_progress", "completed", "failed", "cancelled"] + """Filter by file status. + + One of `in_progress`, `completed`, `failed`, `cancelled`. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_content_response.py b/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_content_response.py new file mode 100644 index 0000000..4e91600 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_content_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["FileContentResponse"] + + +class FileContentResponse(BaseModel): + text: Optional[str] = None + """The text content""" + + type: Optional[str] = None + """The content type (currently only `"text"`)""" diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_create_params.py b/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_create_params.py new file mode 100644 index 0000000..5836e3a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_create_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Required, TypedDict + +from ..file_chunking_strategy_param import FileChunkingStrategyParam + +__all__ = ["FileCreateParams"] + + +class FileCreateParams(TypedDict, total=False): + file_id: Required[str] + """ + A [File](https://developers.openai.com/api/reference/resources/files) ID that + the vector store should use. Useful for tools like `file_search` that can access + files. For multi-file ingestion, we recommend + [`file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create) + to minimize per-vector-store write requests. + """ + + attributes: Optional[Dict[str, Union[str, float, bool]]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + chunking_strategy: FileChunkingStrategyParam + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. Only applicable if `file_ids` is + non-empty. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_list_params.py b/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_list_params.py new file mode 100644 index 0000000..893c697 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_list_params.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["FileListParams"] + + +class FileListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, starting with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + filter: Literal["in_progress", "completed", "failed", "cancelled"] + """Filter by file status. + + One of `in_progress`, `completed`, `failed`, `cancelled`. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_update_params.py b/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_update_params.py new file mode 100644 index 0000000..6d357a8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_stores/file_update_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Required, TypedDict + +__all__ = ["FileUpdateParams"] + + +class FileUpdateParams(TypedDict, total=False): + vector_store_id: Required[str] + + attributes: Required[Optional[Dict[str, Union[str, float, bool]]]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/vector_store_file.py b/venv/lib/python3.12/site-packages/openai/types/vector_stores/vector_store_file.py new file mode 100644 index 0000000..d3a8ed1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_stores/vector_store_file.py @@ -0,0 +1,74 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from ..file_chunking_strategy import FileChunkingStrategy + +__all__ = ["VectorStoreFile", "LastError"] + + +class LastError(BaseModel): + """The last error associated with this vector store file. + + Will be `null` if there are no errors. + """ + + code: Literal["server_error", "unsupported_file", "invalid_file"] + """One of `server_error`, `unsupported_file`, or `invalid_file`.""" + + message: str + """A human-readable description of the error.""" + + +class VectorStoreFile(BaseModel): + """A list of files attached to a vector store.""" + + id: str + """The identifier, which can be referenced in API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the vector store file was created.""" + + last_error: Optional[LastError] = None + """The last error associated with this vector store file. + + Will be `null` if there are no errors. + """ + + object: Literal["vector_store.file"] + """The object type, which is always `vector_store.file`.""" + + status: Literal["in_progress", "completed", "cancelled", "failed"] + """ + The status of the vector store file, which can be either `in_progress`, + `completed`, `cancelled`, or `failed`. The status `completed` indicates that the + vector store file is ready for use. + """ + + usage_bytes: int + """The total vector store usage in bytes. + + Note that this may be different from the original file size. + """ + + vector_store_id: str + """ + The ID of the + [vector store](https://developers.openai.com/api/reference/resources/vector_stores) + that the [File](https://developers.openai.com/api/reference/resources/files) is + attached to. + """ + + attributes: Optional[Dict[str, Union[str, float, bool]]] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + chunking_strategy: Optional[FileChunkingStrategy] = None + """The strategy used to chunk the file.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/vector_store_file_batch.py b/venv/lib/python3.12/site-packages/openai/types/vector_stores/vector_store_file_batch.py new file mode 100644 index 0000000..1cdc92b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_stores/vector_store_file_batch.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["VectorStoreFileBatch", "FileCounts"] + + +class FileCounts(BaseModel): + cancelled: int + """The number of files that where cancelled.""" + + completed: int + """The number of files that have been processed.""" + + failed: int + """The number of files that have failed to process.""" + + in_progress: int + """The number of files that are currently being processed.""" + + total: int + """The total number of files.""" + + +class VectorStoreFileBatch(BaseModel): + """A batch of files attached to a vector store.""" + + id: str + """The identifier, which can be referenced in API endpoints.""" + + created_at: int + """ + The Unix timestamp (in seconds) for when the vector store files batch was + created. + """ + + file_counts: FileCounts + + object: Literal["vector_store.files_batch"] + """The object type, which is always `vector_store.file_batch`.""" + + status: Literal["in_progress", "completed", "cancelled", "failed"] + """ + The status of the vector store files batch, which can be either `in_progress`, + `completed`, `cancelled` or `failed`. + """ + + vector_store_id: str + """ + The ID of the + [vector store](https://developers.openai.com/api/reference/resources/vector_stores) + that the [File](https://developers.openai.com/api/reference/resources/files) is + attached to. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/vector_stores/vector_store_file_deleted.py b/venv/lib/python3.12/site-packages/openai/types/vector_stores/vector_store_file_deleted.py new file mode 100644 index 0000000..e345a81 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/vector_stores/vector_store_file_deleted.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["VectorStoreFileDeleted"] + + +class VectorStoreFileDeleted(BaseModel): + id: str + + deleted: bool + + object: Literal["vector_store.file.deleted"] diff --git a/venv/lib/python3.12/site-packages/openai/types/video.py b/venv/lib/python3.12/site-packages/openai/types/video.py new file mode 100644 index 0000000..da5445d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from .._models import BaseModel +from .video_size import VideoSize +from .video_model import VideoModel +from .video_seconds import VideoSeconds +from .video_create_error import VideoCreateError + +__all__ = ["Video"] + + +class Video(BaseModel): + """Structured information describing a generated video job.""" + + id: str + """Unique identifier for the video job.""" + + completed_at: Optional[int] = None + """Unix timestamp (seconds) for when the job completed, if finished.""" + + created_at: int + """Unix timestamp (seconds) for when the job was created.""" + + error: Optional[VideoCreateError] = None + """Error payload that explains why generation failed, if applicable.""" + + expires_at: Optional[int] = None + """Unix timestamp (seconds) for when the downloadable assets expire, if set.""" + + model: VideoModel + """The video generation model that produced the job.""" + + object: Literal["video"] + """The object type, which is always `video`.""" + + progress: int + """Approximate completion percentage for the generation task.""" + + prompt: Optional[str] = None + """The prompt that was used to generate the video.""" + + remixed_from_video_id: Optional[str] = None + """Identifier of the source video if this video is a remix.""" + + seconds: Union[str, VideoSeconds] + """Duration of the generated clip in seconds. + + For extensions, this is the stitched total duration. + """ + + size: VideoSize + """The resolution of the generated video.""" + + status: Literal["queued", "in_progress", "completed", "failed"] + """Current lifecycle status of the video job.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/video_create_character_params.py b/venv/lib/python3.12/site-packages/openai/types/video_create_character_params.py new file mode 100644 index 0000000..ba51b13 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_create_character_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import FileTypes + +__all__ = ["VideoCreateCharacterParams"] + + +class VideoCreateCharacterParams(TypedDict, total=False): + name: Required[str] + """Display name for this API character.""" + + video: Required[FileTypes] + """Video file used to create a character.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/video_create_character_response.py b/venv/lib/python3.12/site-packages/openai/types/video_create_character_response.py new file mode 100644 index 0000000..464f42c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_create_character_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["VideoCreateCharacterResponse"] + + +class VideoCreateCharacterResponse(BaseModel): + id: Optional[str] = None + """Identifier for the character creation cameo.""" + + created_at: int + """Unix timestamp (in seconds) when the character was created.""" + + name: Optional[str] = None + """Display name for the character.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/video_create_error.py b/venv/lib/python3.12/site-packages/openai/types/video_create_error.py new file mode 100644 index 0000000..556e954 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_create_error.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Dict, Union, Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["VideoCreateError", "Misalignment", "MisalignmentSteer"] + + +class MisalignmentSteer(BaseModel): + """An optional public continuation instruction.""" + + message: str + """The public continuation instruction.""" + + +class Misalignment(BaseModel): + detailed_explanation: Optional[str] = None + """The public explanation for this block.""" + + error_type: Union[ + str, + Literal[ + "potentially_unintended_data_transfer", + "potentially_unintended_data_access", + "potentially_unintended_destructive_activity", + "other", + ], + None, + ] = None + """An optional classification; clients must accept additional values.""" + + steer: Optional[MisalignmentSteer] = None + """An optional public continuation instruction.""" + + +class VideoCreateError(BaseModel): + """An error that occurred while generating the response.""" + + code: str + """A machine-readable error code that was returned.""" + + message: str + """A human-readable description of the error that was returned.""" + + headers: Optional[Dict[str, str]] = None + """ + The Retry-After and Retry-After-Ms headers returned with the original error, if + any. + """ + + misalignment: Optional[Misalignment] = None diff --git a/venv/lib/python3.12/site-packages/openai/types/video_create_params.py b/venv/lib/python3.12/site-packages/openai/types/video_create_params.py new file mode 100644 index 0000000..2802ee3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_create_params.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict + +from .._types import FileTypes +from .video_size import VideoSize +from .video_seconds import VideoSeconds +from .video_model_param import VideoModelParam +from .image_input_reference_param import ImageInputReferenceParam + +__all__ = ["VideoCreateParams", "InputReference"] + + +class VideoCreateParams(TypedDict, total=False): + prompt: Required[str] + """Text prompt that describes the video to generate.""" + + input_reference: InputReference + """Optional reference asset upload or reference object that guides generation.""" + + model: VideoModelParam + """The video generation model to use (allowed values: sora-2, sora-2-pro). + + Defaults to `sora-2`. + """ + + seconds: VideoSeconds + """Clip duration in seconds (allowed values: 4, 8, 12). Defaults to 4 seconds.""" + + size: VideoSize + """ + Output resolution formatted as width x height (allowed values: 720x1280, + 1280x720, 1024x1792, 1792x1024). Defaults to 720x1280. + """ + + +InputReference: TypeAlias = Union[FileTypes, ImageInputReferenceParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/video_delete_response.py b/venv/lib/python3.12/site-packages/openai/types/video_delete_response.py new file mode 100644 index 0000000..045a8da --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_delete_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["VideoDeleteResponse"] + + +class VideoDeleteResponse(BaseModel): + """Confirmation payload returned after deleting a video.""" + + id: str + """Identifier of the deleted video.""" + + deleted: bool + """Indicates that the video resource was deleted.""" + + object: Literal["video.deleted"] + """The object type that signals the deletion response.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/video_download_content_params.py b/venv/lib/python3.12/site-packages/openai/types/video_download_content_params.py new file mode 100644 index 0000000..1184684 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_download_content_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["VideoDownloadContentParams"] + + +class VideoDownloadContentParams(TypedDict, total=False): + variant: Literal["video", "thumbnail", "spritesheet"] + """Which downloadable asset to return. Defaults to the MP4 video.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/video_edit_params.py b/venv/lib/python3.12/site-packages/openai/types/video_edit_params.py new file mode 100644 index 0000000..aa4d4f5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_edit_params.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict + +from .._types import FileTypes + +__all__ = ["VideoEditParams", "Video", "VideoVideoReferenceInputParam"] + + +class VideoEditParams(TypedDict, total=False): + prompt: Required[str] + """Text prompt that describes how to edit the source video.""" + + video: Required[Video] + """Reference to the completed video to edit.""" + + +class VideoVideoReferenceInputParam(TypedDict, total=False): + """Reference to the completed video to edit.""" + + id: Required[str] + """The identifier of the completed video.""" + + +Video: TypeAlias = Union[FileTypes, VideoVideoReferenceInputParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/video_extend_params.py b/venv/lib/python3.12/site-packages/openai/types/video_extend_params.py new file mode 100644 index 0000000..09f3ca0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_extend_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict + +from .._types import FileTypes +from .video_seconds import VideoSeconds + +__all__ = ["VideoExtendParams", "Video", "VideoVideoReferenceInputParam"] + + +class VideoExtendParams(TypedDict, total=False): + prompt: Required[str] + """Updated text prompt that directs the extension generation.""" + + seconds: Required[VideoSeconds] + """ + Length of the newly generated extension segment in seconds (allowed values: 4, + 8, 12, 16, 20). + """ + + video: Required[Video] + """Reference to the completed video to extend.""" + + +class VideoVideoReferenceInputParam(TypedDict, total=False): + """Reference to the completed video.""" + + id: Required[str] + """The identifier of the completed video.""" + + +Video: TypeAlias = Union[FileTypes, VideoVideoReferenceInputParam] diff --git a/venv/lib/python3.12/site-packages/openai/types/video_get_character_response.py b/venv/lib/python3.12/site-packages/openai/types/video_get_character_response.py new file mode 100644 index 0000000..f9468ef --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_get_character_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["VideoGetCharacterResponse"] + + +class VideoGetCharacterResponse(BaseModel): + id: Optional[str] = None + """Identifier for the character creation cameo.""" + + created_at: int + """Unix timestamp (in seconds) when the character was created.""" + + name: Optional[str] = None + """Display name for the character.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/video_list_params.py b/venv/lib/python3.12/site-packages/openai/types/video_list_params.py new file mode 100644 index 0000000..102d0ad --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_list_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["VideoListParams"] + + +class VideoListParams(TypedDict, total=False): + after: str + """Identifier for the last item from the previous pagination request""" + + limit: int + """Number of items to retrieve""" + + order: Literal["asc", "desc"] + """Sort order of results by timestamp. + + Use `asc` for ascending order or `desc` for descending order. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/video_model.py b/venv/lib/python3.12/site-packages/openai/types/video_model.py new file mode 100644 index 0000000..6a17c30 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_model.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +__all__ = ["VideoModel"] + +VideoModel: TypeAlias = Union[ + str, Literal["sora-2", "sora-2-pro", "sora-2-2025-10-06", "sora-2-pro-2025-10-06", "sora-2-2025-12-08"] +] diff --git a/venv/lib/python3.12/site-packages/openai/types/video_model_param.py b/venv/lib/python3.12/site-packages/openai/types/video_model_param.py new file mode 100644 index 0000000..7c3a1fb --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_model_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias + +__all__ = ["VideoModelParam"] + +VideoModelParam: TypeAlias = Union[ + str, Literal["sora-2", "sora-2-pro", "sora-2-2025-10-06", "sora-2-pro-2025-10-06", "sora-2-2025-12-08"] +] diff --git a/venv/lib/python3.12/site-packages/openai/types/video_remix_params.py b/venv/lib/python3.12/site-packages/openai/types/video_remix_params.py new file mode 100644 index 0000000..6184b2f --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_remix_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["VideoRemixParams"] + + +class VideoRemixParams(TypedDict, total=False): + prompt: Required[str] + """Updated text prompt that directs the remix generation.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/video_seconds.py b/venv/lib/python3.12/site-packages/openai/types/video_seconds.py new file mode 100644 index 0000000..b5ec38b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_seconds.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["VideoSeconds"] + +VideoSeconds: TypeAlias = Literal["4", "8", "12"] diff --git a/venv/lib/python3.12/site-packages/openai/types/video_size.py b/venv/lib/python3.12/site-packages/openai/types/video_size.py new file mode 100644 index 0000000..2f87245 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/video_size.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["VideoSize"] + +VideoSize: TypeAlias = Literal["720x1280", "1280x720", "1024x1792", "1792x1024"] diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__init__.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/__init__.py new file mode 100644 index 0000000..dad0bfa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/__init__.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .webhook_endpoint import WebhookEndpoint as WebhookEndpoint +from .webhook_list_params import WebhookListParams as WebhookListParams +from .webhook_test_params import WebhookTestParams as WebhookTestParams +from .unwrap_webhook_event import UnwrapWebhookEvent as UnwrapWebhookEvent +from .webhook_create_params import WebhookCreateParams as WebhookCreateParams +from .webhook_endpoint_list import WebhookEndpointList as WebhookEndpointList +from .webhook_update_params import WebhookUpdateParams as WebhookUpdateParams +from .webhook_event_type_list import WebhookEventTypeList as WebhookEventTypeList +from .deleted_webhook_endpoint import DeletedWebhookEndpoint as DeletedWebhookEndpoint +from .batch_failed_webhook_event import BatchFailedWebhookEvent as BatchFailedWebhookEvent +from .batch_expired_webhook_event import BatchExpiredWebhookEvent as BatchExpiredWebhookEvent +from .webhook_endpoint_test_result import WebhookEndpointTestResult as WebhookEndpointTestResult +from .webhook_endpoint_with_secret import WebhookEndpointWithSecret as WebhookEndpointWithSecret +from .webhook_rotate_secret_params import WebhookRotateSecretParams as WebhookRotateSecretParams +from .batch_cancelled_webhook_event import BatchCancelledWebhookEvent as BatchCancelledWebhookEvent +from .batch_completed_webhook_event import BatchCompletedWebhookEvent as BatchCompletedWebhookEvent +from .eval_run_failed_webhook_event import EvalRunFailedWebhookEvent as EvalRunFailedWebhookEvent +from .response_failed_webhook_event import ResponseFailedWebhookEvent as ResponseFailedWebhookEvent +from .eval_run_canceled_webhook_event import EvalRunCanceledWebhookEvent as EvalRunCanceledWebhookEvent +from .eval_run_succeeded_webhook_event import EvalRunSucceededWebhookEvent as EvalRunSucceededWebhookEvent +from .live_call_incoming_webhook_event import ( + LiveCallIncomingWebhookEvent as LiveCallIncomingWebhookEvent, # pyright: ignore[reportDeprecated] +) +from .response_cancelled_webhook_event import ResponseCancelledWebhookEvent as ResponseCancelledWebhookEvent +from .response_completed_webhook_event import ResponseCompletedWebhookEvent as ResponseCompletedWebhookEvent +from .response_incomplete_webhook_event import ResponseIncompleteWebhookEvent as ResponseIncompleteWebhookEvent +from .safety_alert_created_webhook_event import SafetyAlertCreatedWebhookEvent as SafetyAlertCreatedWebhookEvent +from .safety_warning_issued_webhook_event import SafetyWarningIssuedWebhookEvent as SafetyWarningIssuedWebhookEvent +from .fine_tuning_job_failed_webhook_event import FineTuningJobFailedWebhookEvent as FineTuningJobFailedWebhookEvent +from .realtime_call_incoming_webhook_event import RealtimeCallIncomingWebhookEvent as RealtimeCallIncomingWebhookEvent +from .live_transport_incoming_webhook_event import ( + LiveTransportIncomingWebhookEvent as LiveTransportIncomingWebhookEvent, +) +from .safety_org_alert_created_webhook_event import ( + SafetyOrgAlertCreatedWebhookEvent as SafetyOrgAlertCreatedWebhookEvent, +) +from .fine_tuning_job_cancelled_webhook_event import ( + FineTuningJobCancelledWebhookEvent as FineTuningJobCancelledWebhookEvent, +) +from .fine_tuning_job_succeeded_webhook_event import ( + FineTuningJobSucceededWebhookEvent as FineTuningJobSucceededWebhookEvent, +) +from .safety_identifier_blocked_webhook_event import ( + SafetyIdentifierBlockedWebhookEvent as SafetyIdentifierBlockedWebhookEvent, +) +from .safety_deactivation_issued_webhook_event import ( + SafetyDeactivationIssuedWebhookEvent as SafetyDeactivationIssuedWebhookEvent, +) diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..19cc13a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/batch_cancelled_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/batch_cancelled_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..3ece51b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/batch_cancelled_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/batch_completed_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/batch_completed_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..426b29a Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/batch_completed_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/batch_expired_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/batch_expired_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..94c21d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/batch_expired_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/batch_failed_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/batch_failed_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..1b9b59f Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/batch_failed_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/deleted_webhook_endpoint.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/deleted_webhook_endpoint.cpython-312.pyc new file mode 100644 index 0000000..62e5ecb Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/deleted_webhook_endpoint.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/eval_run_canceled_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/eval_run_canceled_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..0d61ade Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/eval_run_canceled_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/eval_run_failed_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/eval_run_failed_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..5568d86 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/eval_run_failed_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/eval_run_succeeded_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/eval_run_succeeded_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..d7d5126 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/eval_run_succeeded_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/fine_tuning_job_cancelled_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/fine_tuning_job_cancelled_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..59842a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/fine_tuning_job_cancelled_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/fine_tuning_job_failed_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/fine_tuning_job_failed_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..fa14e58 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/fine_tuning_job_failed_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/fine_tuning_job_succeeded_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/fine_tuning_job_succeeded_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..02a9379 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/fine_tuning_job_succeeded_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/live_call_incoming_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/live_call_incoming_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..3e10b39 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/live_call_incoming_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/live_transport_incoming_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/live_transport_incoming_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..c0cd4d4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/live_transport_incoming_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/realtime_call_incoming_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/realtime_call_incoming_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..b8bb421 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/realtime_call_incoming_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/response_cancelled_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/response_cancelled_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..d3a0274 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/response_cancelled_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/response_completed_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/response_completed_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..c4111c6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/response_completed_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/response_failed_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/response_failed_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..8fbfd26 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/response_failed_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/response_incomplete_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/response_incomplete_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..f8528ba Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/response_incomplete_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_alert_created_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_alert_created_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..80da1e8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_alert_created_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_deactivation_issued_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_deactivation_issued_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..c530b65 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_deactivation_issued_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_identifier_blocked_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_identifier_blocked_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..b0deee8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_identifier_blocked_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_org_alert_created_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_org_alert_created_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..79c8bed Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_org_alert_created_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_warning_issued_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_warning_issued_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..6e5783d Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/safety_warning_issued_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/unwrap_webhook_event.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/unwrap_webhook_event.cpython-312.pyc new file mode 100644 index 0000000..6abcea4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/unwrap_webhook_event.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_create_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_create_params.cpython-312.pyc new file mode 100644 index 0000000..0120f30 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_create_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_endpoint.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_endpoint.cpython-312.pyc new file mode 100644 index 0000000..6ee7ca4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_endpoint.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_endpoint_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_endpoint_list.cpython-312.pyc new file mode 100644 index 0000000..09fad2b Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_endpoint_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_endpoint_test_result.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_endpoint_test_result.cpython-312.pyc new file mode 100644 index 0000000..b1903e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_endpoint_test_result.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_endpoint_with_secret.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_endpoint_with_secret.cpython-312.pyc new file mode 100644 index 0000000..fcc2964 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_endpoint_with_secret.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_event_type_list.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_event_type_list.cpython-312.pyc new file mode 100644 index 0000000..b9f78d3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_event_type_list.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_list_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_list_params.cpython-312.pyc new file mode 100644 index 0000000..84247f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_list_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_rotate_secret_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_rotate_secret_params.cpython-312.pyc new file mode 100644 index 0000000..f13a7ce Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_rotate_secret_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_test_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_test_params.cpython-312.pyc new file mode 100644 index 0000000..4955ac1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_test_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_update_params.cpython-312.pyc b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_update_params.cpython-312.pyc new file mode 100644 index 0000000..34f5059 Binary files /dev/null and b/venv/lib/python3.12/site-packages/openai/types/webhooks/__pycache__/webhook_update_params.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/batch_cancelled_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/batch_cancelled_webhook_event.py new file mode 100644 index 0000000..59d6a02 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/batch_cancelled_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BatchCancelledWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the batch API request.""" + + +class BatchCancelledWebhookEvent(BaseModel): + """Sent when a batch API request has been cancelled.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the batch API request was cancelled.""" + + data: Data + """Event data payload.""" + + type: Literal["batch.cancelled"] + """The type of the event. Always `batch.cancelled`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/batch_completed_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/batch_completed_webhook_event.py new file mode 100644 index 0000000..57ef20e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/batch_completed_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BatchCompletedWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the batch API request.""" + + +class BatchCompletedWebhookEvent(BaseModel): + """Sent when a batch API request has been completed.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the batch API request was completed.""" + + data: Data + """Event data payload.""" + + type: Literal["batch.completed"] + """The type of the event. Always `batch.completed`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/batch_expired_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/batch_expired_webhook_event.py new file mode 100644 index 0000000..df58aa5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/batch_expired_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BatchExpiredWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the batch API request.""" + + +class BatchExpiredWebhookEvent(BaseModel): + """Sent when a batch API request has expired.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the batch API request expired.""" + + data: Data + """Event data payload.""" + + type: Literal["batch.expired"] + """The type of the event. Always `batch.expired`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/batch_failed_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/batch_failed_webhook_event.py new file mode 100644 index 0000000..df443fe --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/batch_failed_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BatchFailedWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the batch API request.""" + + +class BatchFailedWebhookEvent(BaseModel): + """Sent when a batch API request has failed.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the batch API request failed.""" + + data: Data + """Event data payload.""" + + type: Literal["batch.failed"] + """The type of the event. Always `batch.failed`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/deleted_webhook_endpoint.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/deleted_webhook_endpoint.py new file mode 100644 index 0000000..9ee6182 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/deleted_webhook_endpoint.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["DeletedWebhookEndpoint"] + + +class DeletedWebhookEndpoint(BaseModel): + id: str + """The ID of the deleted webhook endpoint.""" + + deleted: bool + """Whether the endpoint was deleted.""" + + object: Literal["webhook_endpoint.deleted"] + """The object type, which is always webhook_endpoint.deleted.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/eval_run_canceled_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/eval_run_canceled_webhook_event.py new file mode 100644 index 0000000..09d3900 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/eval_run_canceled_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["EvalRunCanceledWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the eval run.""" + + +class EvalRunCanceledWebhookEvent(BaseModel): + """Sent when an eval run has been canceled.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the eval run was canceled.""" + + data: Data + """Event data payload.""" + + type: Literal["eval.run.canceled"] + """The type of the event. Always `eval.run.canceled`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/eval_run_failed_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/eval_run_failed_webhook_event.py new file mode 100644 index 0000000..e8e8e24 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/eval_run_failed_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["EvalRunFailedWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the eval run.""" + + +class EvalRunFailedWebhookEvent(BaseModel): + """Sent when an eval run has failed.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the eval run failed.""" + + data: Data + """Event data payload.""" + + type: Literal["eval.run.failed"] + """The type of the event. Always `eval.run.failed`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/eval_run_succeeded_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/eval_run_succeeded_webhook_event.py new file mode 100644 index 0000000..8107c4e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/eval_run_succeeded_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["EvalRunSucceededWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the eval run.""" + + +class EvalRunSucceededWebhookEvent(BaseModel): + """Sent when an eval run has succeeded.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the eval run succeeded.""" + + data: Data + """Event data payload.""" + + type: Literal["eval.run.succeeded"] + """The type of the event. Always `eval.run.succeeded`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/fine_tuning_job_cancelled_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/fine_tuning_job_cancelled_webhook_event.py new file mode 100644 index 0000000..195e62c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/fine_tuning_job_cancelled_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FineTuningJobCancelledWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the fine-tuning job.""" + + +class FineTuningJobCancelledWebhookEvent(BaseModel): + """Sent when a fine-tuning job has been cancelled.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the fine-tuning job was cancelled.""" + + data: Data + """Event data payload.""" + + type: Literal["fine_tuning.job.cancelled"] + """The type of the event. Always `fine_tuning.job.cancelled`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/fine_tuning_job_failed_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/fine_tuning_job_failed_webhook_event.py new file mode 100644 index 0000000..15bc33e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/fine_tuning_job_failed_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FineTuningJobFailedWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the fine-tuning job.""" + + +class FineTuningJobFailedWebhookEvent(BaseModel): + """Sent when a fine-tuning job has failed.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the fine-tuning job failed.""" + + data: Data + """Event data payload.""" + + type: Literal["fine_tuning.job.failed"] + """The type of the event. Always `fine_tuning.job.failed`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/fine_tuning_job_succeeded_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/fine_tuning_job_succeeded_webhook_event.py new file mode 100644 index 0000000..f2c4f95 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/fine_tuning_job_succeeded_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FineTuningJobSucceededWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the fine-tuning job.""" + + +class FineTuningJobSucceededWebhookEvent(BaseModel): + """Sent when a fine-tuning job has succeeded.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the fine-tuning job succeeded.""" + + data: Data + """Event data payload.""" + + type: Literal["fine_tuning.job.succeeded"] + """The type of the event. Always `fine_tuning.job.succeeded`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/live_call_incoming_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/live_call_incoming_webhook_event.py new file mode 100644 index 0000000..2caba71 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/live_call_incoming_webhook_event.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, deprecated + +from ..._models import BaseModel + +__all__ = ["LiveCallIncomingWebhookEvent", "Data", "DataSipHeader"] + + +class DataSipHeader(BaseModel): + """A header from the SIP Invite.""" + + name: str + """Name of the SIP Header.""" + + value: str + """Value of the SIP Header.""" + + +class Data(BaseModel): + """Event data payload.""" + + session_id: str + """The `live_...` ID of the pending SIP session. + + Pass this value unchanged to Live call controls and sideband connections. The + corresponding `realtime.call.incoming` event uses a separate `rtc_...` call ID. + """ + + sip_headers: List[DataSipHeader] + """ + Headers from the SIP INVITE, excluding SIP authorization headers. Retained + names, values, repeated entries, and order are preserved. Treat these values as + untrusted call metadata. + """ + + sip_media_security: Union[Literal["rtp", "srtp"], str, None] = None + """Media protection selected on the SIP leg during SDP negotiation. + + `srtp` indicates SRTP; `rtp` indicates unencrypted RTP. Omitted when unknown. + This does not describe SIP signaling security or confirm that media has flowed. + Clients should handle unrecognized values as unknown. + """ + + +@deprecated("Use LiveTransportIncomingWebhookEvent (live.transport.incoming) instead.", category=None) +class LiveCallIncomingWebhookEvent(BaseModel): + """Deprecated: use `live.transport.incoming`. + + Retained for existing subscriptions + during migration; new subscriptions to this event are not allowed. + Sent when an incoming API SIP session is available for Live acceptance. The + same pending session can also emit `realtime.call.incoming`; the first + successful Realtime or Live accept endpoint selects the runtime surface. + """ + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the event was created.""" + + data: Data + """Event data payload.""" + + type: Literal["live.call.incoming"] + """The type of the event. Always `live.call.incoming`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/live_transport_incoming_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/live_transport_incoming_webhook_event.py new file mode 100644 index 0000000..97c9307 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/live_transport_incoming_webhook_event.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["LiveTransportIncomingWebhookEvent", "Data", "DataSipHeader"] + + +class DataSipHeader(BaseModel): + """A header from the SIP Invite.""" + + name: str + """Name of the SIP Header.""" + + value: str + """Value of the SIP Header.""" + + +class Data(BaseModel): + """Event data payload.""" + + session_id: str + """The `live_...` ID of the pending SIP session. + + Forward this value unchanged when accepting or rejecting the call through the + Live API. + """ + + sip_headers: List[DataSipHeader] + """ + Headers from the SIP INVITE, excluding SIP authorization headers. Retained + names, values, repeated entries, and order are preserved. Treat these values as + untrusted call metadata. + """ + + type: Literal["sip"] + """The incoming transport type. Always `sip`.""" + + sip_media_security: Union[Literal["rtp", "srtp"], str, None] = None + """Media protection selected on the SIP leg during SDP negotiation. + + `srtp` indicates SRTP; `rtp` indicates unencrypted RTP. Omitted when unknown. + This does not describe SIP signaling security or confirm that media has flowed. + Clients should handle unrecognized values as unknown. + """ + + +class LiveTransportIncomingWebhookEvent(BaseModel): + """Sent when an incoming API SIP session is available for Live acceptance. + + The + same pending session can also emit `realtime.call.incoming`; the first + successful Realtime or Live accept endpoint selects the runtime surface. + """ + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the event was created.""" + + data: Data + """Event data payload.""" + + type: Literal["live.transport.incoming"] + """The type of the event. Always `live.transport.incoming`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/realtime_call_incoming_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/realtime_call_incoming_webhook_event.py new file mode 100644 index 0000000..7a2095d --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/realtime_call_incoming_webhook_event.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeCallIncomingWebhookEvent", "Data", "DataSipHeader"] + + +class DataSipHeader(BaseModel): + """A header from the SIP Invite.""" + + name: str + """Name of the SIP Header.""" + + value: str + """Value of the SIP Header.""" + + +class Data(BaseModel): + """Event data payload.""" + + call_id: str + """The ID of the pending SIP call. + + Pass this value unchanged when accepting or rejecting the call through the + Realtime API. For the Live API, use the `session_id` from + `live.transport.incoming` instead. + """ + + sip_headers: List[DataSipHeader] + """ + Headers from the SIP INVITE, excluding SIP authorization headers. Retained + names, values, repeated entries, and order are preserved. Treat these values as + untrusted call metadata. + """ + + sip_media_security: Union[Literal["rtp", "srtp"], str, None] = None + """Media protection selected on the SIP leg during SDP negotiation. + + `srtp` indicates SRTP; `rtp` indicates unencrypted RTP. Omitted when unknown. + This does not describe SIP signaling security or confirm that media has flowed. + Clients should handle unrecognized values as unknown. + """ + + +class RealtimeCallIncomingWebhookEvent(BaseModel): + """ + Sent when an incoming API SIP session is available for Realtime acceptance. + The same pending session can also emit `live.transport.incoming`; the first + successful Realtime or Live accept endpoint selects the runtime surface. + """ + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the model response was completed.""" + + data: Data + """Event data payload.""" + + type: Literal["realtime.call.incoming"] + """The type of the event. Always `realtime.call.incoming`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/response_cancelled_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/response_cancelled_webhook_event.py new file mode 100644 index 0000000..b98cff1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/response_cancelled_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCancelledWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the model response.""" + + +class ResponseCancelledWebhookEvent(BaseModel): + """Sent when a background response has been cancelled.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the model response was cancelled.""" + + data: Data + """Event data payload.""" + + type: Literal["response.cancelled"] + """The type of the event. Always `response.cancelled`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/response_completed_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/response_completed_webhook_event.py new file mode 100644 index 0000000..781e3ce --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/response_completed_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCompletedWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the model response.""" + + +class ResponseCompletedWebhookEvent(BaseModel): + """Sent when a background response has been completed.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the model response was completed.""" + + data: Data + """Event data payload.""" + + type: Literal["response.completed"] + """The type of the event. Always `response.completed`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/response_failed_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/response_failed_webhook_event.py new file mode 100644 index 0000000..ee0dc2c --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/response_failed_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFailedWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the model response.""" + + +class ResponseFailedWebhookEvent(BaseModel): + """Sent when a background response has failed.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the model response failed.""" + + data: Data + """Event data payload.""" + + type: Literal["response.failed"] + """The type of the event. Always `response.failed`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/response_incomplete_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/response_incomplete_webhook_event.py new file mode 100644 index 0000000..73659d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/response_incomplete_webhook_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseIncompleteWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + id: str + """The unique ID of the model response.""" + + +class ResponseIncompleteWebhookEvent(BaseModel): + """Sent when a background response has been interrupted.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the model response was interrupted.""" + + data: Data + """Event data payload.""" + + type: Literal["response.incomplete"] + """The type of the event. Always `response.incomplete`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_alert_created_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_alert_created_webhook_event.py new file mode 100644 index 0000000..0898b93 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_alert_created_webhook_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SafetyAlertCreatedWebhookEvent", "Data"] + + +class Data(BaseModel): + id: str + """The safety alert ID to pass to `GET /v1/safety/alerts/{id}`.""" + + +class SafetyAlertCreatedWebhookEvent(BaseModel): + """Sent when an approved safety alert is available for an API project.""" + + id: str + """The unique ID of the webhook event.""" + + created_at: int + """The Unix timestamp in seconds when the event was created.""" + + data: Data + + object: Literal["event"] + """Always `event`.""" + + type: Literal["safety.alert.created"] + """Always `safety.alert.created`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_deactivation_issued_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_deactivation_issued_webhook_event.py new file mode 100644 index 0000000..0abdc2e --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_deactivation_issued_webhook_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SafetyDeactivationIssuedWebhookEvent", "Data"] + + +class Data(BaseModel): + id: str + """The safety case ID to pass to `GET /v1/safety/cases/{id}`.""" + + +class SafetyDeactivationIssuedWebhookEvent(BaseModel): + """ + Sent when a deactivation is issued for a safety identifier in your organization. + """ + + id: str + """The unique ID of the webhook event.""" + + created_at: int + """The Unix timestamp in seconds when the event was created.""" + + data: Data + + object: Literal["event"] + """Always `event`.""" + + type: Literal["safety.deactivation_issued"] + """Always `safety.deactivation_issued`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_identifier_blocked_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_identifier_blocked_webhook_event.py new file mode 100644 index 0000000..b908ad4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_identifier_blocked_webhook_event.py @@ -0,0 +1,44 @@ +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SafetyIdentifierBlockedWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + safety_category: str + """The safety category that triggered the block, such as `bio` or `cyber`.""" + + safety_identifier: str + """The stable safety identifier associated with the blocked request.""" + + model: Optional[str] = None + """The model used for the blocked request, if available.""" + + project_id: Optional[str] = None + """The project associated with the blocked request, if available.""" + + request_id: Optional[str] = None + """The OpenAI request ID for the blocked request, if available.""" + + +class SafetyIdentifierBlockedWebhookEvent(BaseModel): + """Sent when a request associated with a safety identifier has been blocked.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the request was blocked.""" + + data: Data + """Event data payload.""" + + type: Literal["safety_identifier.blocked"] + """The type of the event. Always `safety_identifier.blocked`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_org_alert_created_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_org_alert_created_webhook_event.py new file mode 100644 index 0000000..43ce274 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_org_alert_created_webhook_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SafetyOrgAlertCreatedWebhookEvent", "Data"] + + +class Data(BaseModel): + id: str + """The safety alert ID to pass to `GET /v1/safety/alerts/{id}`.""" + + +class SafetyOrgAlertCreatedWebhookEvent(BaseModel): + """Sent when an approved safety alert is available for an enterprise workspace.""" + + id: str + """The unique ID of the webhook event.""" + + created_at: int + """The Unix timestamp in seconds when the event was created.""" + + data: Data + + object: Literal["event"] + """Always `event`.""" + + type: Literal["safety.org_alert.created"] + """Always `safety.org_alert.created`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_warning_issued_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_warning_issued_webhook_event.py new file mode 100644 index 0000000..9f37faa --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/safety_warning_issued_webhook_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SafetyWarningIssuedWebhookEvent", "Data"] + + +class Data(BaseModel): + id: str + """The safety case ID to pass to `GET /v1/safety/cases/{id}`.""" + + +class SafetyWarningIssuedWebhookEvent(BaseModel): + """Sent when a warning is issued for a safety identifier in your organization.""" + + id: str + """The unique ID of the webhook event.""" + + created_at: int + """The Unix timestamp in seconds when the event was created.""" + + data: Data + + object: Literal["event"] + """Always `event`.""" + + type: Literal["safety.warning_issued"] + """Always `safety.warning_issued`.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/unwrap_webhook_event.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/unwrap_webhook_event.py new file mode 100644 index 0000000..84fc0d6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/unwrap_webhook_event.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .batch_failed_webhook_event import BatchFailedWebhookEvent +from .batch_expired_webhook_event import BatchExpiredWebhookEvent +from .batch_cancelled_webhook_event import BatchCancelledWebhookEvent +from .batch_completed_webhook_event import BatchCompletedWebhookEvent +from .eval_run_failed_webhook_event import EvalRunFailedWebhookEvent +from .response_failed_webhook_event import ResponseFailedWebhookEvent +from .eval_run_canceled_webhook_event import EvalRunCanceledWebhookEvent +from .eval_run_succeeded_webhook_event import EvalRunSucceededWebhookEvent +from .live_call_incoming_webhook_event import LiveCallIncomingWebhookEvent # pyright: ignore[reportDeprecated] +from .response_cancelled_webhook_event import ResponseCancelledWebhookEvent +from .response_completed_webhook_event import ResponseCompletedWebhookEvent +from .response_incomplete_webhook_event import ResponseIncompleteWebhookEvent +from .safety_alert_created_webhook_event import SafetyAlertCreatedWebhookEvent +from .safety_warning_issued_webhook_event import SafetyWarningIssuedWebhookEvent +from .fine_tuning_job_failed_webhook_event import FineTuningJobFailedWebhookEvent +from .realtime_call_incoming_webhook_event import RealtimeCallIncomingWebhookEvent +from .live_transport_incoming_webhook_event import LiveTransportIncomingWebhookEvent +from .safety_org_alert_created_webhook_event import SafetyOrgAlertCreatedWebhookEvent +from .fine_tuning_job_cancelled_webhook_event import FineTuningJobCancelledWebhookEvent +from .fine_tuning_job_succeeded_webhook_event import FineTuningJobSucceededWebhookEvent +from .safety_deactivation_issued_webhook_event import SafetyDeactivationIssuedWebhookEvent + +__all__ = ["UnwrapWebhookEvent"] + +UnwrapWebhookEvent: TypeAlias = Annotated[ + Union[ + BatchCancelledWebhookEvent, + BatchCompletedWebhookEvent, + BatchExpiredWebhookEvent, + BatchFailedWebhookEvent, + EvalRunCanceledWebhookEvent, + EvalRunFailedWebhookEvent, + EvalRunSucceededWebhookEvent, + FineTuningJobCancelledWebhookEvent, + FineTuningJobFailedWebhookEvent, + FineTuningJobSucceededWebhookEvent, + LiveCallIncomingWebhookEvent, # pyright: ignore[reportDeprecated] # Preserve legacy payload parsing. + LiveTransportIncomingWebhookEvent, + RealtimeCallIncomingWebhookEvent, + ResponseCancelledWebhookEvent, + ResponseCompletedWebhookEvent, + ResponseFailedWebhookEvent, + ResponseIncompleteWebhookEvent, + SafetyAlertCreatedWebhookEvent, + SafetyDeactivationIssuedWebhookEvent, + SafetyOrgAlertCreatedWebhookEvent, + SafetyWarningIssuedWebhookEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_create_params.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_create_params.py new file mode 100644 index 0000000..39faf04 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_create_params.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["WebhookCreateParams"] + + +class WebhookCreateParams(TypedDict, total=False): + event_types: Required[ + List[ + Literal[ + "batch.completed", + "batch.failed", + "batch.expired", + "batch.cancelled", + "response.completed", + "response.failed", + "response.cancelled", + "response.incomplete", + "eval.run.succeeded", + "eval.run.failed", + "eval.run.canceled", + "fine_tuning.job.succeeded", + "fine_tuning.job.failed", + "fine_tuning.job.cancelled", + "realtime.call.incoming", + "video.completed", + "video.failed", + "safety.alert.created", + ] + ] + ] + """The event types that trigger deliveries to this endpoint.""" + + name: Required[str] + """A human-readable name for the webhook endpoint.""" + + url: Required[str] + """The HTTPS URL that receives webhook deliveries.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_endpoint.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_endpoint.py new file mode 100644 index 0000000..5e3d648 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_endpoint.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["WebhookEndpoint"] + + +class WebhookEndpoint(BaseModel): + id: str + """The unique ID of the webhook endpoint.""" + + created_at: int + """The Unix timestamp when the endpoint was created.""" + + event_types: List[str] + """The event types that trigger deliveries to this endpoint.""" + + name: str + """The human-readable name of the endpoint.""" + + object: Literal["webhook_endpoint"] + """The object type, which is always webhook_endpoint.""" + + signing_secret_hint: Optional[str] = None + """A masked hint for the endpoint's signing secret.""" + + url: str + """The HTTPS URL that receives webhook deliveries.""" + + updated_at: Optional[int] = None + """The Unix timestamp of the last endpoint configuration or signing-secret change. + + Initialized at creation; tests and unchanged updates do not advance it. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_endpoint_list.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_endpoint_list.py new file mode 100644 index 0000000..3c9ce04 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_endpoint_list.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .webhook_endpoint import WebhookEndpoint + +__all__ = ["WebhookEndpointList"] + + +class WebhookEndpointList(BaseModel): + data: List[WebhookEndpoint] + """The webhook endpoints in this page.""" + + first_id: Optional[str] = None + """The ID of the first endpoint in this page.""" + + has_more: bool + """Whether more webhook endpoints are available.""" + + last_id: Optional[str] = None + """The ID of the last endpoint in this page.""" + + object: Literal["list"] + """The object type, which is always list.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_endpoint_test_result.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_endpoint_test_result.py new file mode 100644 index 0000000..3d7aaa4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_endpoint_test_result.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["WebhookEndpointTestResult"] + + +class WebhookEndpointTestResult(BaseModel): + event_type: str + """The event type sent in the test.""" + + object: Literal["webhook_endpoint.test"] + """The object type, which is always webhook_endpoint.test.""" + + status_code: int + """The HTTP status code returned by the endpoint.""" + + success: Literal[True] + """Whether the test request completed. + + Always true for returned results; use status_code to determine the endpoint + response. + """ + + webhook_endpoint_id: str + """The ID of the webhook endpoint that received the test.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_endpoint_with_secret.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_endpoint_with_secret.py new file mode 100644 index 0000000..9cfe5b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_endpoint_with_secret.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field + +from ..._models import BaseModel + +__all__ = ["WebhookEndpointWithSecret"] + + +class WebhookEndpointWithSecret(BaseModel): + id: str + """The unique ID of the webhook endpoint.""" + + created_at: int + """The Unix timestamp when the endpoint was created.""" + + event_types: List[str] + """The event types that trigger deliveries to this endpoint.""" + + name: str + """The human-readable name of the endpoint.""" + + object: Literal["webhook_endpoint"] + """The object type, which is always webhook_endpoint.""" + + signing_secret: str = Field(repr=False) + """The endpoint's signing secret. + + This is returned only when the endpoint is created or the secret is rotated. + """ + + signing_secret_hint: Optional[str] = None + """A masked hint for the endpoint's signing secret.""" + + url: str + """The HTTPS URL that receives webhook deliveries.""" + + updated_at: Optional[int] = None + """The Unix timestamp of the last endpoint configuration or signing-secret change. + + Initialized at creation; tests and unchanged updates do not advance it. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_event_type_list.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_event_type_list.py new file mode 100644 index 0000000..a9065de --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_event_type_list.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["WebhookEventTypeList"] + + +class WebhookEventTypeList(BaseModel): + data: List[str] + """The webhook event types available to the authenticated project.""" + + object: Literal["list"] + """The object type, which is always list.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_list_params.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_list_params.py new file mode 100644 index 0000000..441b29a --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_list_params.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["WebhookListParams"] + + +class WebhookListParams(TypedDict, total=False): + after: Optional[str] + """ID of the last webhook endpoint from the previous page.""" + + limit: int + """Maximum number of webhook endpoints to return. Defaults to 20.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_rotate_secret_params.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_rotate_secret_params.py new file mode 100644 index 0000000..01cd5b6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_rotate_secret_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["WebhookRotateSecretParams"] + + +class WebhookRotateSecretParams(TypedDict, total=False): + keep_old_secret_active_for_24_hours: bool + """Whether to keep the previous signing secret valid for 24 hours after rotation. + + Defaults to false, which invalidates the previous secret immediately. + """ diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_test_params.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_test_params.py new file mode 100644 index 0000000..2b41c12 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_test_params.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["WebhookTestParams"] + + +class WebhookTestParams(TypedDict, total=False): + event_type: Required[ + Literal[ + "batch.completed", + "batch.failed", + "batch.expired", + "batch.cancelled", + "response.completed", + "response.failed", + "response.cancelled", + "response.incomplete", + "eval.run.succeeded", + "eval.run.failed", + "eval.run.canceled", + "fine_tuning.job.succeeded", + "fine_tuning.job.failed", + "fine_tuning.job.cancelled", + "realtime.call.incoming", + "video.completed", + "video.failed", + "safety.alert.created", + ] + ] + """The event type to send as a sample delivery.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_update_params.py b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_update_params.py new file mode 100644 index 0000000..ceafbaf --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/webhooks/webhook_update_params.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +__all__ = ["WebhookUpdateParams"] + + +class WebhookUpdateParams(TypedDict, total=False): + event_types: List[ + Literal[ + "batch.completed", + "batch.failed", + "batch.expired", + "batch.cancelled", + "response.completed", + "response.failed", + "response.cancelled", + "response.incomplete", + "eval.run.succeeded", + "eval.run.failed", + "eval.run.canceled", + "fine_tuning.job.succeeded", + "fine_tuning.job.failed", + "fine_tuning.job.cancelled", + "realtime.call.incoming", + "video.completed", + "video.failed", + "safety.alert.created", + ] + ] + """The complete set of event types that should trigger deliveries.""" + + name: str + """A new human-readable name for the webhook endpoint.""" + + url: str + """A new HTTPS URL that receives webhook deliveries.""" diff --git a/venv/lib/python3.12/site-packages/openai/types/websocket_connection_options.py b/venv/lib/python3.12/site-packages/openai/types/websocket_connection_options.py new file mode 100644 index 0000000..46d8f9b --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/websocket_connection_options.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import TYPE_CHECKING +from typing_extensions import Sequence, TypeAlias, TypedDict + +__all__ = ["WebSocketConnectionOptions", "WebsocketConnectionOptions"] + +if TYPE_CHECKING: + from websockets import Subprotocol + from websockets.extensions import ClientExtensionFactory + + +class WebSocketConnectionOptions(TypedDict, total=False): + """WebSocket connection options copied from `websockets`. + + For example: https://websockets.readthedocs.io/en/stable/reference/asyncio/client.html#websockets.asyncio.client.connect + """ + + extensions: Sequence[ClientExtensionFactory] | None + """List of supported extensions, in order in which they should be negotiated and run.""" + + subprotocols: Sequence[Subprotocol] | None + """List of supported subprotocols, in order of decreasing preference.""" + + compression: str | None + """The “permessage-deflate” extension is enabled by default. Set compression to None to disable it. See the [compression guide](https://websockets.readthedocs.io/en/stable/topics/compression.html) for details.""" + + # limits + max_size: int | None + """Maximum size of incoming messages in bytes. None disables the limit.""" + + max_queue: int | None | tuple[int | None, int | None] + """High-water mark of the buffer where frames are received. It defaults to 16 frames. The low-water mark defaults to max_queue // 4. You may pass a (high, low) tuple to set the high-water and low-water marks. If you want to disable flow control entirely, you may set it to None, although that’s a bad idea.""" + + write_limit: int | tuple[int, int | None] + """High-water mark of write buffer in bytes. It is passed to set_write_buffer_limits(). It defaults to 32 KiB. You may pass a (high, low) tuple to set the high-water and low-water marks.""" + + +# Backward compatibility for pre-rename imports. +WebsocketConnectionOptions: TypeAlias = WebSocketConnectionOptions diff --git a/venv/lib/python3.12/site-packages/openai/types/websocket_reconnection.py b/venv/lib/python3.12/site-packages/openai/types/websocket_reconnection.py new file mode 100644 index 0000000..68b7154 --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/types/websocket_reconnection.py @@ -0,0 +1,64 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import dataclasses +from typing_extensions import TypedDict + +from .._types import Query, Headers + + +@dataclasses.dataclass(frozen=True) +class ReconnectingEvent: + """Information about a reconnection attempt, passed to the ``on_reconnecting`` handler.""" + + attempt: int + """Which retry attempt this is (1-based).""" + + max_attempts: int + """Total attempts that will be made.""" + + delay: float + """Delay in seconds before this attempt connects.""" + + close_code: int + """The WebSocket close code that triggered reconnection.""" + + extra_query: Query + """The current query parameters.""" + + extra_headers: Headers + """The current headers.""" + + +class ReconnectingOverrides(TypedDict, total=False): + """Optional overrides returned from the ``on_reconnecting`` handler + to customize the next reconnection attempt.""" + + extra_query: Query + """If provided, assigns the query parameters for the next connection.""" + + extra_headers: Headers + """If provided, assigns the headers for the next connection.""" + + abort: bool + """If set to ``True``, will stop attempting to reconnect.""" + + +# RFC 6455 §7.4.1 +_RECOVERABLE_CLOSE_CODES: frozenset[int] = frozenset( + { + 1001, # Going away (server shutting down) + 1005, # No status code (abnormal) + 1006, # Abnormal closure (network drop) + 1011, # Internal server error + 1012, # Service restart + 1013, # Try again later + 1015, # TLS handshake failure + } +) + + +def is_recoverable_close(code: int) -> bool: + """Return ``True`` if the WebSocket close *code* is worth retrying.""" + return code in _RECOVERABLE_CLOSE_CODES diff --git a/venv/lib/python3.12/site-packages/openai/version.py b/venv/lib/python3.12/site-packages/openai/version.py new file mode 100644 index 0000000..01a08ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/openai/version.py @@ -0,0 +1,3 @@ +from ._version import __version__ + +VERSION: str = __version__ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/_logs/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/_logs/__init__.py new file mode 100644 index 0000000..cff2b8c --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/_logs/__init__.py @@ -0,0 +1,47 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +""" +The OpenTelemetry logging API describes the classes used to generate logs and events. + +The :class:`.LoggerProvider` provides users access to the :class:`.Logger`. + +This module provides abstract (i.e. unimplemented) classes required for +logging, and a concrete no-op implementation :class:`.NoOpLogger` that allows applications +to use the API package alone without a supporting implementation. + +To get a logger, you need to provide the package name from which you are +calling the logging APIs to OpenTelemetry by calling `LoggerProvider.get_logger` +with the calling module name and the version of your package. + +The following code shows how to obtain a logger using the global :class:`.LoggerProvider`:: + + from opentelemetry._logs import get_logger + + logger = get_logger("example-logger") + +.. versionadded:: 1.15.0 +""" + +from opentelemetry._logs._internal import ( + Logger, + LoggerProvider, + LogRecord, + NoOpLogger, + NoOpLoggerProvider, + get_logger, + get_logger_provider, + set_logger_provider, +) +from opentelemetry._logs.severity import SeverityNumber + +__all__ = [ + "LogRecord", + "Logger", + "LoggerProvider", + "NoOpLogger", + "NoOpLoggerProvider", + "SeverityNumber", + "get_logger", + "get_logger_provider", + "set_logger_provider", +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/_logs/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/_logs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..50c6e93 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/_logs/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/_logs/_internal/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/_logs/_internal/__init__.py new file mode 100644 index 0000000..4c049f9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/_logs/_internal/__init__.py @@ -0,0 +1,484 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +""" +The OpenTelemetry logging API describes the classes used to generate logs and events. + +The :class:`.LoggerProvider` provides users access to the :class:`.Logger`. + +This module provides abstract (i.e. unimplemented) classes required for +logging, and a concrete no-op implementation :class:`.NoOpLogger` that allows applications +to use the API package alone without a supporting implementation. + +To get a logger, you need to provide the package name from which you are +calling the logging APIs to OpenTelemetry by calling `LoggerProvider.get_logger` +with the calling module name and the version of your package. + +The following code shows how to obtain a logger using the global :class:`.LoggerProvider`:: + + from opentelemetry._logs import get_logger + + logger = get_logger("example-logger") + +.. versionadded:: 1.15.0 +""" + +from __future__ import annotations + +from abc import ABC, abstractmethod +from logging import getLogger +from os import environ +from time import time_ns +from typing import cast, overload + +from typing_extensions import deprecated + +from opentelemetry._logs.severity import SeverityNumber +from opentelemetry.context import get_current +from opentelemetry.context.context import Context +from opentelemetry.environment_variables import _OTEL_PYTHON_LOGGER_PROVIDER +from opentelemetry.trace import get_current_span +from opentelemetry.trace.span import TraceFlags +from opentelemetry.util._once import Once +from opentelemetry.util._providers import _load_provider +from opentelemetry.util.types import AnyValue, Attributes + +_logger = getLogger(__name__) + + +class LogRecord(ABC): + """A LogRecord instance represents an event being logged. + + LogRecord instances are created and emitted via `Logger` + every time something is logged. They contain all the information + pertinent to the event being logged. + """ + + @overload + def __init__( + self, + *, + timestamp: int | None = None, + observed_timestamp: int | None = None, + context: Context | None = None, + severity_text: str | None = None, + severity_number: SeverityNumber | None = None, + body: AnyValue = None, + attributes: Attributes = None, + event_name: str | None = None, + exception: BaseException | None = None, + ) -> None: ... + + @overload + @deprecated( + "LogRecord init with `trace_id`, `span_id`, and/or `trace_flags` is deprecated since 1.35.0. Use `context` instead." + ) + def __init__( + self, + *, + timestamp: int | None = None, + observed_timestamp: int | None = None, + trace_id: int | None = None, + span_id: int | None = None, + trace_flags: TraceFlags | None = None, + severity_text: str | None = None, + severity_number: SeverityNumber | None = None, + body: AnyValue = None, + attributes: Attributes = None, + ) -> None: ... + + def __init__( + self, + *, + timestamp: int | None = None, + observed_timestamp: int | None = None, + context: Context | None = None, + trace_id: int | None = None, + span_id: int | None = None, + trace_flags: TraceFlags | None = None, + severity_text: str | None = None, + severity_number: SeverityNumber | None = None, + body: AnyValue = None, + attributes: Attributes = None, + event_name: str | None = None, + exception: BaseException | None = None, + ) -> None: + if not context: + context = get_current() + span_context = get_current_span(context).get_span_context() + self.timestamp = timestamp + if observed_timestamp is None: + observed_timestamp = time_ns() + self.observed_timestamp = observed_timestamp + self.context = context + self.trace_id = trace_id or span_context.trace_id + self.span_id = span_id or span_context.span_id + self.trace_flags = trace_flags or span_context.trace_flags + self.severity_text = severity_text + self.severity_number = severity_number + self.body = body + self.attributes = attributes + self.event_name = event_name + self.exception = exception + + +class Logger(ABC): + """Handles emitting events and logs via `LogRecord`.""" + + def __init__( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + attributes: Attributes = None, + ) -> None: + super().__init__() + self._name = name + self._version = version + self._schema_url = schema_url + self._attributes = attributes + + @overload + def emit( + self, + *, + timestamp: int | None = None, + observed_timestamp: int | None = None, + context: Context | None = None, + severity_number: SeverityNumber | None = None, + severity_text: str | None = None, + body: AnyValue | None = None, + attributes: Attributes = None, + event_name: str | None = None, + exception: BaseException | None = None, + ) -> None: ... + + @overload + def emit( + self, + record: LogRecord, + ) -> None: ... + + @abstractmethod + def emit( + self, + record: LogRecord | None = None, + *, + timestamp: int | None = None, + observed_timestamp: int | None = None, + context: Context | None = None, + severity_number: SeverityNumber | None = None, + severity_text: str | None = None, + body: AnyValue | None = None, + attributes: Attributes = None, + event_name: str | None = None, + exception: BaseException | None = None, + ) -> None: + """Emits a :class:`LogRecord` representing a log to the processing pipeline.""" + + @abstractmethod + def enabled( + self, + *, + context: Context | None = None, + severity_number: SeverityNumber | None = None, + event_name: str | None = None, + ) -> bool: + """Returns whether the logger is enabled for the given arguments. + + Instrumentation should call this before performing expensive work to + construct a log record, and skip that work if ``False`` is returned. + + The returned value may change over time and should be checked each time + before emitting a log record. + """ + + +class NoOpLogger(Logger): + """The default Logger used when no Logger implementation is available. + + All operations are no-op. + """ + + @overload + def emit( + self, + *, + timestamp: int | None = None, + observed_timestamp: int | None = None, + context: Context | None = None, + severity_number: SeverityNumber | None = None, + severity_text: str | None = None, + body: AnyValue | None = None, + attributes: Attributes = None, + event_name: str | None = None, + exception: BaseException | None = None, + ) -> None: ... + + @overload + def emit( # pylint:disable=arguments-differ + self, + record: LogRecord, + ) -> None: ... + + def emit( + self, + record: LogRecord | None = None, + *, + timestamp: int | None = None, + observed_timestamp: int | None = None, + context: Context | None = None, + severity_number: SeverityNumber | None = None, + severity_text: str | None = None, + body: AnyValue | None = None, + attributes: Attributes = None, + event_name: str | None = None, + exception: BaseException | None = None, + ) -> None: + pass + + def enabled( + self, + *, + context: Context | None = None, + severity_number: SeverityNumber | None = None, + event_name: str | None = None, + ) -> bool: + return False + + +class ProxyLogger(Logger): + def __init__( # pylint: disable=super-init-not-called + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + attributes: Attributes = None, + ): + self._name = name + self._version = version + self._schema_url = schema_url + self._attributes = attributes + self._real_logger: Logger | None = None + self._noop_logger = NoOpLogger(name) + + @property + def _logger(self) -> Logger: + if self._real_logger: + return self._real_logger + + if _LOGGER_PROVIDER: + self._real_logger = _LOGGER_PROVIDER.get_logger( + self._name, + self._version, + self._schema_url, + self._attributes, + ) + return self._real_logger + return self._noop_logger + + @overload + def emit( + self, + *, + timestamp: int | None = None, + observed_timestamp: int | None = None, + context: Context | None = None, + severity_number: SeverityNumber | None = None, + severity_text: str | None = None, + body: AnyValue | None = None, + attributes: Attributes = None, + event_name: str | None = None, + exception: BaseException | None = None, + ) -> None: ... + + @overload + def emit( # pylint:disable=arguments-differ + self, + record: LogRecord, + ) -> None: ... + + def emit( + self, + record: LogRecord | None = None, + *, + timestamp: int | None = None, + observed_timestamp: int | None = None, + context: Context | None = None, + severity_number: SeverityNumber | None = None, + severity_text: str | None = None, + body: AnyValue | None = None, + attributes: Attributes = None, + event_name: str | None = None, + exception: BaseException | None = None, + ) -> None: + if record: + self._logger.emit(record) + else: + self._logger.emit( + timestamp=timestamp, + observed_timestamp=observed_timestamp, + context=context, + severity_number=severity_number, + severity_text=severity_text, + body=body, + attributes=attributes, + event_name=event_name, + exception=exception, + ) + + def enabled( + self, + *, + context: Context | None = None, + severity_number: SeverityNumber | None = None, + event_name: str | None = None, + ) -> bool: + return self._logger.enabled( + context=context, + severity_number=severity_number, + event_name=event_name, + ) + + +class LoggerProvider(ABC): + """ + LoggerProvider is the entry point of the API. It provides access to Logger instances. + """ + + @abstractmethod + def get_logger( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + attributes: Attributes = None, + ) -> Logger: + """Returns a `Logger` for use by the given instrumentation library. + + For any two calls with identical parameters, it is undefined whether the same + or different `Logger` instances are returned. + + This function may return different `Logger` types (e.g. a no-op logger + vs. a functional logger). + + Args: + name: The name of the instrumenting module, package or class. + This should *not* be the name of the module, package or class that is + instrumented but the name of the code doing the instrumentation. + E.g., instead of ``"requests"``, use + ``"opentelemetry.instrumentation.requests"``. + + For log sources which define a logger name (e.g. logging.Logger.name) + the Logger Name should be recorded as the instrumentation scope name. + + version: Optional. The version string of the + instrumenting library. Usually this should be the same as + ``importlib.metadata.version(instrumenting_library_name)``. + + schema_url: Optional. Specifies the Schema URL of the emitted telemetry. + + attributes: Optional. Specifies the instrumentation scope attributes to + associate with emitted telemetry. + """ + + +class NoOpLoggerProvider(LoggerProvider): + """The default LoggerProvider used when no LoggerProvider implementation is available.""" + + def get_logger( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + attributes: Attributes = None, + ) -> Logger: + """Returns a NoOpLogger.""" + return NoOpLogger(name, version=version, schema_url=schema_url, attributes=attributes) + + +class ProxyLoggerProvider(LoggerProvider): + def get_logger( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + attributes: Attributes = None, + ) -> Logger: + if _LOGGER_PROVIDER: + return _LOGGER_PROVIDER.get_logger( + name, + version=version, + schema_url=schema_url, + attributes=attributes, + ) + return ProxyLogger( + name, + version=version, + schema_url=schema_url, + attributes=attributes, + ) + + +_LOGGER_PROVIDER_SET_ONCE = Once() +_LOGGER_PROVIDER: LoggerProvider | None = None +_PROXY_LOGGER_PROVIDER = ProxyLoggerProvider() + + +def get_logger_provider() -> LoggerProvider: + """Gets the current global :class:`~.LoggerProvider` object.""" + global _LOGGER_PROVIDER # pylint: disable=global-variable-not-assigned + if _LOGGER_PROVIDER is None: + if _OTEL_PYTHON_LOGGER_PROVIDER not in environ: + return _PROXY_LOGGER_PROVIDER + + logger_provider: LoggerProvider = _load_provider( # type: ignore + _OTEL_PYTHON_LOGGER_PROVIDER, "logger_provider" + ) + _set_logger_provider(logger_provider, log=False) + + # _LOGGER_PROVIDER will have been set by one thread + return cast("LoggerProvider", _LOGGER_PROVIDER) + + +def _set_logger_provider(logger_provider: LoggerProvider, log: bool) -> None: + def set_lp() -> None: + global _LOGGER_PROVIDER # pylint: disable=global-statement + _LOGGER_PROVIDER = logger_provider + + did_set = _LOGGER_PROVIDER_SET_ONCE.do_once(set_lp) + + if log and not did_set: + _logger.warning("Overriding of current LoggerProvider is not allowed") + + +def set_logger_provider(logger_provider: LoggerProvider) -> None: + """Sets the current global :class:`~.LoggerProvider` object. + + This can only be done once, a warning will be logged if any further attempt + is made. + """ + _set_logger_provider(logger_provider, log=True) + + +def get_logger( + instrumenting_module_name: str, + instrumenting_library_version: str = "", + logger_provider: LoggerProvider | None = None, + schema_url: str | None = None, + attributes: Attributes = None, +) -> Logger: + """Returns a `Logger` for use within a python process. + + This function is a convenience wrapper for + opentelemetry.sdk._logs.LoggerProvider.get_logger. + + If logger_provider param is omitted the current configured one is used. + """ + if logger_provider is None: + logger_provider = get_logger_provider() + return logger_provider.get_logger( + instrumenting_module_name, + instrumenting_library_version, + schema_url, + attributes, + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/_logs/_internal/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/_logs/_internal/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..11db734 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/_logs/_internal/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/_logs/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/_logs/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/_logs/severity/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/_logs/severity/__init__.py new file mode 100644 index 0000000..96ffcc6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/_logs/severity/__init__.py @@ -0,0 +1,44 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import enum + + +class SeverityNumber(enum.Enum): + """Numerical value of severity. + + Smaller numerical values correspond to less severe events + (such as debug events), larger numerical values correspond + to more severe events (such as errors and critical events). + + See the `Log Data Model`_ spec for more info and how to map the + severity from source format to OTLP Model. + + .. _Log Data Model: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#field-severitynumber + """ + + UNSPECIFIED = 0 + TRACE = 1 + TRACE2 = 2 + TRACE3 = 3 + TRACE4 = 4 + DEBUG = 5 + DEBUG2 = 6 + DEBUG3 = 7 + DEBUG4 = 8 + INFO = 9 + INFO2 = 10 + INFO3 = 11 + INFO4 = 12 + WARN = 13 + WARN2 = 14 + WARN3 = 15 + WARN4 = 16 + ERROR = 17 + ERROR2 = 18 + ERROR3 = 19 + ERROR4 = 20 + FATAL = 21 + FATAL2 = 22 + FATAL3 = 23 + FATAL4 = 24 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/_logs/severity/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/_logs/severity/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..778740d Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/_logs/severity/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/attributes/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/attributes/__init__.py new file mode 100644 index 0000000..6bc3590 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/attributes/__init__.py @@ -0,0 +1,243 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# pyright: reportUnnecessaryIsInstance=false + +import copy +import logging +import threading +from collections.abc import Mapping, MutableMapping, Sequence +from types import NoneType +from typing import TYPE_CHECKING, Any, overload + +from typing_extensions import assert_never, deprecated + +from opentelemetry.util import types + +_logger = logging.getLogger(__name__) + + +# Calling str(x) will use an object's `__str__` method if it exists, otherwise it will use it's `__repr__` method. +# If neither is defined it uses the base class's `object.__repr__` method, which returns a string that is hard to understand. +# So in that case we drop the key/value pair. +def _is_non_custom_str(key: Any) -> bool: + return type(key).__str__ is not object.__str__ or type(key).__repr__ is not object.__repr__ + + +@overload +def _clean_attribute_value( + value: Mapping[str, types.AnyValue], + max_string_value_length: int | None, +) -> Mapping[str, types.AnyValue]: ... + + +@overload +def _clean_attribute_value( + value: types.AnyValue, + max_string_value_length: int | None, +) -> types.AnyValue: ... + + +def _clean_attribute_value( + value: types.AnyValue, + max_string_value_length: int | None, +) -> types.AnyValue: + """Recursively checks if an attribute value is valid and cleans it if required. + + String values are truncated to max_string_value_length if provided. + Anything that isn't of `types.AnyValue`, we attempt to cast to `str`. + If this fails, the value is replaced with None. Sequence's are converted to tuples and mappings + are copied into new dicts. + + Returns: + The recursively cleaned AnyValue. + """ + if isinstance(value, (NoneType, bool, int, float, bytes)): + return value + if isinstance(value, str): + if max_string_value_length is not None and len(value) > max_string_value_length: + _logger.warning( + "String attribute value exceeds max length of %d, truncating.", + max_string_value_length, + ) + value = value[:max_string_value_length] + return value + if isinstance(value, Sequence): + return tuple(_clean_attribute_value(v, max_string_value_length) for v in value) + if isinstance(value, Mapping): + cleaned_mapping: dict[str, types.AnyValue] = {} + for key, val in value.items(): + if not key: + _logger.warning( + "invalid attribute key `%s`. must be non-empty string. Dropping key from attributes.", + key, + ) + continue + # Spec says to convert unknown types to strings if possible (here and below too). + if not isinstance(key, str): + _logger.warning( + "Invalid type `%s` for attribute key `%s`, must be a str. Key's `__str__/__repr__` method will be called if it exists, otherwise the key/value pair will be dropped.", + type(key), + key, + ) + if _is_non_custom_str(key): + key = str(key) + else: + continue + cleaned_mapping[key] = _clean_attribute_value(val, max_string_value_length) + return cleaned_mapping + if TYPE_CHECKING: + assert_never(value) + _logger.warning( + "Invalid type `%s` for attribute value. Expected one of bool, str, None, bytes, int, float or a " + "Mapping or Sequence of those types. Value's __str__ method will be called if it exists, otherwise the value will be replaced with None.", + type(value), + ) + if _is_non_custom_str(value): + return str(value) + return None + + +class BoundedAttributes(MutableMapping[str, types.AnyValue]): + """A dict with a fixed max capacity which cleans and potentially drops values to ensure they are valid attribute values. + + Args: + maxlen: The maximum number of attributes to store, use None for no limit. + attributes: The initial attributes to store. + immutable: Defaults to true. Whether to allow adding/removing of attributes after the initialisation of the instance. + max_value_len: The maximum length of string values, use None for no limit. + extended_attributes: Deprecated. Kept for backwards compatibility. Extended attributes are now always used for attributes everywhere. + + When the dict is full and a new element is added, the oldest element is dropped. Attributes are made to be immutable when set in this container. + So passing a mutable list as an attribute value, and then mutating it after will not change it's value in this container. + """ + + @overload + def __init__( + self, + maxlen: int | None = None, + attributes: types.Attributes = None, + immutable: bool = True, + max_value_len: int | None = None, + ) -> None: ... + + @overload + @deprecated( + "Creating BoundedAttributes with `extended_attributes` set is deprecated. " + "The `extended_attributes` param is no longer used and will be removed " + "in a future release. Extended attributes are now always used for attributes everywhere." + ) + def __init__( + self, + maxlen: int | None = None, + attributes: types.Attributes = None, + immutable: bool = True, + max_value_len: int | None = None, + extended_attributes: bool = False, + ) -> None: ... + + def __init__( + self, + maxlen: int | None = None, + attributes: types.Attributes = None, + immutable: bool = True, + max_value_len: int | None = None, + extended_attributes: bool = False, + ) -> None: + if maxlen is not None and maxlen < 0: + raise ValueError("maxlen must be valid int greater or equal to 0") + if max_value_len is not None and max_value_len < 0: + raise ValueError("max_value_len must be valid int greater or equal to 0") + self._dict: dict[str, types.AnyValue] = {} + self.maxlen = maxlen + self.dropped = 0 + self.max_value_len = max_value_len + self._lock = threading.Lock() + # setting False before adding items + self._immutable = False + if attributes: + self._set_items(attributes) + self._immutable = immutable + + def __repr__(self) -> str: + return f"{dict(self._dict)}" + + def __getitem__(self, key: str) -> types.AnyValue: + return self._dict[key] + + def _raise_if_immutable(self) -> None: + if self._immutable: + raise TypeError("Cannot mutate immutable BoundedAttributes") + + def __setitem__(self, key: str, value: types.AnyValue) -> None: + self._raise_if_immutable() + if self.maxlen is not None and self.maxlen == 0: + with self._lock: + self.dropped += 1 + return + if not key or not isinstance(key, str): + _logger.warning( + "invalid key `%s`. must be non-empty string. Dropping key from attributes.", + key, + ) + with self._lock: + self.dropped += 1 + return + cleaned = _clean_attribute_value(value, self.max_value_len) + with self._lock: + self._setitem_locked(key, cleaned) + + def _set_items(self, attributes: Mapping[str, types.AnyValue]) -> None: + self._raise_if_immutable() + if self.maxlen is not None and self.maxlen == 0: + with self._lock: + self.dropped += len(attributes) + return + cleaned_attributes: Mapping[str, types.AnyValue] = _clean_attribute_value(attributes, self.max_value_len) + with self._lock: + self.dropped += len(attributes) - len(cleaned_attributes) + for key, value in cleaned_attributes.items(): + self._setitem_locked(key, value) + + def _setitem_locked(self, key: str, value: types.AnyValue) -> None: + if key in self._dict: + del self._dict[key] + if self.maxlen is not None and len(self._dict) >= self.maxlen: + _logger.warning( + "Attributes dict is full. Dropping the oldest key-value pair from attributes to make space for the new key-value pair.", + ) + # Dictionaries are insertion ordered in Python, this is the recommended way to get the oldest value. + del self._dict[next(iter(self._dict.keys()))] + self.dropped += 1 + + self._dict[key] = value + + def __delitem__(self, key: str) -> None: + self._raise_if_immutable() + del self._dict[key] + + def __iter__(self): + if self._immutable: + return iter(self._dict) + with self._lock: + return iter(list(self._dict)) + + def __len__(self) -> int: + return len(self._dict) + + def __deepcopy__(self, memo: dict[int, Any]) -> "BoundedAttributes": + copy_ = BoundedAttributes( + maxlen=self.maxlen, + immutable=self._immutable, + max_value_len=self.max_value_len, + ) + memo[id(self)] = copy_ + with self._lock: + # Assign _dict directly to avoid re-cleaning already clean values + # and to bypass the immutability guard in __setitem__ + copy_._dict = copy.deepcopy(self._dict, memo) + copy_.dropped = self.dropped + return copy_ + + def copy(self) -> MutableMapping[str, types.AnyValue]: + return self._dict.copy() diff --git a/venv/lib/python3.12/site-packages/opentelemetry/attributes/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/attributes/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6a253da Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/attributes/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/attributes/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/attributes/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/baggage/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/baggage/__init__.py new file mode 100644 index 0000000..b852a4f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/baggage/__init__.py @@ -0,0 +1,122 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from collections.abc import Mapping +from logging import getLogger +from re import compile +from types import MappingProxyType + +from opentelemetry.context import create_key, get_value, set_value +from opentelemetry.context.context import Context +from opentelemetry.util.re import ( + _BAGGAGE_PROPERTY_FORMAT, + _KEY_FORMAT, + _VALUE_FORMAT, +) + +_BAGGAGE_KEY = create_key("baggage") +_logger = getLogger(__name__) + +_KEY_PATTERN = compile(_KEY_FORMAT) +_VALUE_PATTERN = compile(_VALUE_FORMAT) +_PROPERT_PATTERN = compile(_BAGGAGE_PROPERTY_FORMAT) + + +def get_all( + context: Context | None = None, +) -> Mapping[str, object]: + """Returns the name/value pairs in the Baggage + + Args: + context: The Context to use. If not set, uses current Context + + Returns: + The name/value pairs in the Baggage + """ + return MappingProxyType(_get_baggage_value(context=context)) + + +def get_baggage(name: str, context: Context | None = None) -> object | None: + """Provides access to the value for a name/value pair in the + Baggage + + Args: + name: The name of the value to retrieve + context: The Context to use. If not set, uses current Context + + Returns: + The value associated with the given name, or null if the given name is + not present. + """ + return _get_baggage_value(context=context).get(name) + + +def set_baggage(name: str, value: object, context: Context | None = None) -> Context: + """Sets a value in the Baggage + + Args: + name: The name of the value to set + value: The value to set + context: The Context to use. If not set, uses current Context + + Returns: + A Context with the value updated + """ + baggage = _get_baggage_value(context=context).copy() + baggage[name] = value + return set_value(_BAGGAGE_KEY, baggage, context=context) + + +def remove_baggage(name: str, context: Context | None = None) -> Context: + """Removes a value from the Baggage + + Args: + name: The name of the value to remove + context: The Context to use. If not set, uses current Context + + Returns: + A Context with the name/value removed + """ + baggage = _get_baggage_value(context=context).copy() + baggage.pop(name, None) + + return set_value(_BAGGAGE_KEY, baggage, context=context) + + +def clear(context: Context | None = None) -> Context: + """Removes all values from the Baggage + + Args: + context: The Context to use. If not set, uses current Context + + Returns: + A Context with all baggage entries removed + """ + return set_value(_BAGGAGE_KEY, {}, context=context) + + +def _get_baggage_value(context: Context | None = None) -> dict[str, object]: + baggage = get_value(_BAGGAGE_KEY, context=context) + if isinstance(baggage, dict): + return baggage + return {} + + +def _is_valid_key(name: str) -> bool: + return _KEY_PATTERN.fullmatch(str(name)) is not None + + +def _is_valid_value(value: object) -> bool: + parts = str(value).split(";") + # W3C Baggage allows optional whitespace between the value and ";" + is_valid_value = _VALUE_PATTERN.fullmatch(parts[0].rstrip(" \t")) is not None + if len(parts) > 1: # one or more properties metadata + for property in parts[1:]: + if _PROPERT_PATTERN.fullmatch(property) is None: + is_valid_value = False + break + return is_valid_value + + +def _is_valid_pair(key: str, value: str) -> bool: + return _is_valid_key(key) and _is_valid_value(value) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/baggage/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/baggage/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f596010 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/baggage/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/baggage/propagation/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/baggage/propagation/__init__.py new file mode 100644 index 0000000..b06ca31 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/baggage/propagation/__init__.py @@ -0,0 +1,179 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +# +from collections.abc import Iterable, Iterator, Mapping +from logging import getLogger +from re import split +from urllib.parse import quote_plus, unquote_plus + +from opentelemetry.baggage import _is_valid_pair, get_all, set_baggage +from opentelemetry.context import get_current +from opentelemetry.context.context import Context +from opentelemetry.propagators import textmap +from opentelemetry.util.re import _DELIMITER_PATTERN + +_logger = getLogger(__name__) + + +def _filter_valid_entries( + entries: Iterable[str], + max_pair_length: int, +) -> Iterator[str]: + for entry in entries: + if not entry: + continue + if not entry.isascii(): + _logger.warning( + "Baggage entry with key `%s` contains non-ASCII characters", + entry.split("=", 1)[0], + ) + continue + if len(entry) > max_pair_length: + _logger.warning( + "Baggage entry with key `%s` exceeded the maximum number of bytes per list-member with length %d", + entry.split("=", 1)[0], + len(entry), + ) + continue + yield entry + + +def _apply_baggage_limits( + entries: Iterable[str], + max_pairs: int, + max_pair_length: int, + max_header_length: int, +) -> Iterator[str]: + """Apply W3C Baggage size limits to a sequence of baggage entries. + + Yields entries that fit within the W3C specification limits. + Logs warnings when entries are dropped. + """ + length = 0 + for index, entry in enumerate(_filter_valid_entries(entries, max_pair_length)): + if index >= max_pairs: + _logger.warning("Baggage exceeded the maximum number of list-members") + return + + length += (1 if index > 0 else 0) + len(entry) + if length > max_header_length: + _logger.warning("Baggage exceeded the maximum number of bytes per baggage-string") + return + yield entry + + +class W3CBaggagePropagator(textmap.TextMapPropagator): + """Extracts and injects Baggage which is used to annotate telemetry.""" + + _MAX_HEADER_LENGTH = 8192 + _MAX_PAIR_LENGTH = 4096 + _MAX_PAIRS = 180 + _BAGGAGE_HEADER_NAME = "baggage" + + def extract( + self, + carrier: textmap.CarrierT, + context: Context | None = None, + getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter, + ) -> Context: + """Extract Baggage from the carrier. + + See + `opentelemetry.propagators.textmap.TextMapPropagator.extract` + """ + + if context is None: + context = get_current() + + header = _extract_first_element(getter.get(carrier, self._BAGGAGE_HEADER_NAME)) + + if not header: + return context + + if len(header.encode()) > self._MAX_HEADER_LENGTH: + _logger.warning( + "Baggage header `%s` exceeded the maximum number of bytes per baggage-string", + header, + ) + return context + + baggage_entries = split(_DELIMITER_PATTERN, header) + + for entry in _apply_baggage_limits( + baggage_entries, + max_pairs=self._MAX_PAIRS, + max_pair_length=self._MAX_PAIR_LENGTH, + max_header_length=self._MAX_HEADER_LENGTH, + ): + try: + name, value = entry.split("=", 1) + except Exception: # pylint: disable=broad-exception-caught + _logger.warning("Baggage list-member `%s` doesn't match the format", entry) + continue + + # W3C Baggage allows optional whitespace around "=" + name = name.strip(" \t") + value = value.strip(" \t") + + if not _is_valid_pair(name, value): + _logger.warning("Invalid baggage entry: `%s`", entry) + continue + + name = unquote_plus(name).strip() + value = unquote_plus(value).strip() + + context = set_baggage( + name, + value, + context=context, + ) + + return context + + def inject( + self, + carrier: textmap.CarrierT, + context: Context | None = None, + setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter, + ) -> None: + """Injects Baggage into the carrier. + + See + `opentelemetry.propagators.textmap.TextMapPropagator.inject` + """ + baggage_entries = get_all(context=context) + if not baggage_entries: + return + + baggage_string = ",".join( + _apply_baggage_limits( + _encode_baggage_pairs(baggage_entries), + max_pairs=self._MAX_PAIRS, + max_pair_length=self._MAX_PAIR_LENGTH, + max_header_length=self._MAX_HEADER_LENGTH, + ) + ) + + if baggage_string: + setter.set(carrier, self._BAGGAGE_HEADER_NAME, baggage_string) + + @property + def fields(self) -> set[str]: + """Returns a set with the fields set in `inject`.""" + return {self._BAGGAGE_HEADER_NAME} + + +def _encode_baggage_pairs( + baggage_entries: Mapping[str, object], +) -> Iterator[str]: + """Yield URL-encoded 'key=value' pairs from baggage entries.""" + for key, value in baggage_entries.items(): + yield quote_plus(str(key)) + "=" + quote_plus(str(value)) + + +def _extract_first_element( + items: Iterable[textmap.CarrierT] | None, +) -> textmap.CarrierT | None: + if items is None: + return None + return next(iter(items), None) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/baggage/propagation/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/baggage/propagation/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d26f91e Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/baggage/propagation/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/baggage/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/baggage/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/context/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/context/__init__.py new file mode 100644 index 0000000..9bd2084 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/context/__init__.py @@ -0,0 +1,147 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import logging +import os +from contextvars import Token +from uuid import uuid4 + +# pylint: disable=wrong-import-position +from opentelemetry.context.context import Context, _RuntimeContext +from opentelemetry.context.contextvars_context import ContextVarsRuntimeContext +from opentelemetry.environment_variables import OTEL_PYTHON_CONTEXT + +logger = logging.getLogger(__name__) + + +def _load_runtime_context() -> _RuntimeContext: + """Initialize the RuntimeContext + + Returns: + An instance of RuntimeContext. + """ + configured_context = os.environ.get(OTEL_PYTHON_CONTEXT) + if not configured_context: + return ContextVarsRuntimeContext() + + # pylint: disable=import-outside-toplevel,no-name-in-module + from opentelemetry.util._importlib_metadata import ( # noqa: PLC0415 + entry_points, + ) + + try: + return next(iter(entry_points(group="opentelemetry_context", name=configured_context))).load()() + except Exception: # pylint: disable=broad-exception-caught + logger.exception( + "Failed to load context: %s, falling back to contextvars_context", + configured_context, + ) + return ContextVarsRuntimeContext() + + +_RUNTIME_CONTEXT = _load_runtime_context() + + +def create_key(keyname: str) -> str: + """To allow cross-cutting concern to control access to their local state, + the RuntimeContext API provides a function which takes a keyname as input, + and returns a unique key. + Args: + keyname: The key name is for debugging purposes and is not required to be unique. + Returns: + A unique string representing the newly created key. + """ + return keyname + "-" + str(uuid4()) + + +def get_value(key: str, context: Context | None = None) -> object: + """To access the local state of a concern, the RuntimeContext API + provides a function which takes a context and a key as input, + and returns a value. + + Args: + key: The key of the value to retrieve. + context: The context from which to retrieve the value, if None, the current context is used. + + Returns: + The value associated with the key. + """ + return context.get(key) if context is not None else get_current().get(key) + + +def set_value(key: str, value: object, context: Context | None = None) -> Context: + """To record the local state of a cross-cutting concern, the + RuntimeContext API provides a function which takes a context, a + key, and a value as input, and returns an updated context + which contains the new value. + + Args: + key: The key of the entry to set. + value: The value of the entry to set. + context: The context to copy, if None, the current context is used. + + Returns: + A new `Context` containing the value set. + """ + if context is None: + context = get_current() + new_values = context.copy() + new_values[key] = value + return Context(new_values) + + +def get_current() -> Context: + """To access the context associated with program execution, + the Context API provides a function which takes no arguments + and returns a Context. + + Returns: + The current `Context` object. + """ + return _RUNTIME_CONTEXT.get_current() + + +def attach(context: Context) -> Token[Context]: + """Associates a Context with the caller's current execution unit. Returns + a token that can be used to restore the previous Context. + + Args: + context: The Context to set as current. + + Returns: + A token that can be used with `detach` to reset the context. + """ + return _RUNTIME_CONTEXT.attach(context) + + +def detach(token: Token[Context]) -> None: + """Resets the Context associated with the caller's current execution unit + to the value it had before attaching a specified Context. + + Args: + token: The Token that was returned by a previous call to attach a Context. + """ + try: + _RUNTIME_CONTEXT.detach(token) + except Exception: # pylint: disable=broad-exception-caught + logger.exception("Failed to detach context") + + +# FIXME This is a temporary location for the suppress instrumentation key. +# Once the decision around how to suppress instrumentation is made in the +# spec, this key should be moved accordingly. +_ON_EMIT_RECURSION_COUNT_KEY = create_key("on_emit_recursion_count") +_SUPPRESS_INSTRUMENTATION_KEY = create_key("suppress_instrumentation") +_SUPPRESS_HTTP_INSTRUMENTATION_KEY = create_key("suppress_http_instrumentation") + +__all__ = [ + "Context", + "attach", + "create_key", + "detach", + "get_current", + "get_value", + "set_value", +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/context/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/context/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d14785e Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/context/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/context/__pycache__/context.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/context/__pycache__/context.cpython-312.pyc new file mode 100644 index 0000000..840a695 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/context/__pycache__/context.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/context/__pycache__/contextvars_context.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/context/__pycache__/contextvars_context.cpython-312.pyc new file mode 100644 index 0000000..2c88494 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/context/__pycache__/contextvars_context.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/context/context.py b/venv/lib/python3.12/site-packages/opentelemetry/context/context.py new file mode 100644 index 0000000..984bf42 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/context/context.py @@ -0,0 +1,65 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from abc import ABC, abstractmethod +from contextvars import Token + + +class Context(dict[str, object]): + def __setitem__(self, key: str, value: object) -> None: + raise ValueError + + def __delitem__(self, key: str) -> None: + raise ValueError + + def setdefault(self, key: str, default: object = None) -> object: + raise ValueError + + def pop(self, key: str, *args: object) -> object: + raise ValueError + + def popitem(self) -> tuple[str, object]: + raise ValueError + + def clear(self) -> None: + raise ValueError + + def update(self, *args: object, **kwargs: object) -> None: + raise ValueError + + def __ior__(self, other: object) -> Context: + raise ValueError + + +class _RuntimeContext(ABC): + """The RuntimeContext interface provides a wrapper for the different + mechanisms that are used to propagate context in Python. + Implementations can be made available via entry_points and + selected through environment variables. + """ + + @abstractmethod + def attach(self, context: Context) -> Token[Context]: + """Sets the current `Context` object. Returns a + token that can be used to reset to the previous `Context`. + + Args: + context: The Context to set. + """ + + @abstractmethod + def get_current(self) -> Context: + """Returns the current `Context` object.""" + + @abstractmethod + def detach(self, token: Token[Context]) -> None: + """Resets Context to a previous value + + Args: + token: A reference to a previous Context. + """ + + +__all__ = ["Context"] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/context/contextvars_context.py b/venv/lib/python3.12/site-packages/opentelemetry/context/contextvars_context.py new file mode 100644 index 0000000..3cbcdd9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/context/contextvars_context.py @@ -0,0 +1,43 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from contextvars import ContextVar, Token + +from opentelemetry.context.context import Context, _RuntimeContext + + +class ContextVarsRuntimeContext(_RuntimeContext): + """An implementation of the RuntimeContext interface which wraps ContextVar under + the hood. This is the preferred implementation for usage with Python 3.5+ + """ + + _CONTEXT_KEY = "current_context" + + def __init__(self) -> None: + self._current_context = ContextVar(self._CONTEXT_KEY, default=Context()) + + def attach(self, context: Context) -> Token[Context]: + """Sets the current `Context` object. Returns a + token that can be used to reset to the previous `Context`. + + Args: + context: The Context to set. + """ + return self._current_context.set(context) + + def get_current(self) -> Context: + """Returns the current `Context` object.""" + return self._current_context.get() + + def detach(self, token: Token[Context]) -> None: + """Resets Context to a previous value + + Args: + token: A reference to a previous Context. + """ + self._current_context.reset(token) + + +__all__ = ["ContextVarsRuntimeContext"] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/context/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/context/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/environment_variables/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/environment_variables/__init__.py new file mode 100644 index 0000000..b335c82 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/environment_variables/__init__.py @@ -0,0 +1,72 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +OTEL_LOGS_EXPORTER = "OTEL_LOGS_EXPORTER" +""" +.. envvar:: OTEL_LOGS_EXPORTER + +""" + +OTEL_METRICS_EXPORTER = "OTEL_METRICS_EXPORTER" +""" +.. envvar:: OTEL_METRICS_EXPORTER + +Specifies which exporter is used for metrics. See `General SDK Configuration +`_. + +**Default value:** ``"otlp"`` + +**Example:** + +``export OTEL_METRICS_EXPORTER="prometheus"`` + +Accepted values for ``OTEL_METRICS_EXPORTER`` are: + +- ``"otlp"`` +- ``"prometheus"`` +- ``"none"``: No automatically configured exporter for metrics. + +.. note:: + + Exporter packages may add entry points for group ``opentelemetry_metrics_exporter`` which + can then be used with this environment variable by name. The entry point should point to + either a `opentelemetry.sdk.metrics.export.MetricExporter` (push exporter) or + `opentelemetry.sdk.metrics.export.MetricReader` (pull exporter) subclass; it must be + constructable without any required arguments. This mechanism is considered experimental and + may change in subsequent releases. +""" + +OTEL_PROPAGATORS = "OTEL_PROPAGATORS" +""" +.. envvar:: OTEL_PROPAGATORS +""" + +OTEL_PYTHON_CONTEXT = "OTEL_PYTHON_CONTEXT" +""" +.. envvar:: OTEL_PYTHON_CONTEXT +""" + +OTEL_PYTHON_ID_GENERATOR = "OTEL_PYTHON_ID_GENERATOR" +""" +.. envvar:: OTEL_PYTHON_ID_GENERATOR +""" + +OTEL_TRACES_EXPORTER = "OTEL_TRACES_EXPORTER" +""" +.. envvar:: OTEL_TRACES_EXPORTER +""" + +OTEL_PYTHON_TRACER_PROVIDER = "OTEL_PYTHON_TRACER_PROVIDER" +""" +.. envvar:: OTEL_PYTHON_TRACER_PROVIDER +""" + +OTEL_PYTHON_METER_PROVIDER = "OTEL_PYTHON_METER_PROVIDER" +""" +.. envvar:: OTEL_PYTHON_METER_PROVIDER +""" + +_OTEL_PYTHON_LOGGER_PROVIDER = "OTEL_PYTHON_LOGGER_PROVIDER" +""" +.. envvar:: OTEL_PYTHON_LOGGER_PROVIDER +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/environment_variables/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/environment_variables/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e4a9065 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/environment_variables/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/environment_variables/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/environment_variables/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__init__.py new file mode 100644 index 0000000..e701872 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__init__.py @@ -0,0 +1,73 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from typing import TYPE_CHECKING, cast + +# pylint: disable-next=import-error +from opentelemetry.exporter.http.transport._requests import ( + RequestsHTTPTransport as _RequestsHTTPTransport, +) + +# pylint: disable-next=import-error +from opentelemetry.exporter.http.transport._urllib3 import ( + Urllib3HTTPTransport as _Urllib3HTTPTransport, +) + +if TYPE_CHECKING: + from typing import Any, Protocol + + from opentelemetry.exporter.http.transport._base import BaseHTTPTransport + + class BaseHTTPTransportFactory(Protocol): + def __call__( + self, + *, + verify: bool | str, + cert: str | tuple[str, str] | None, + **kwargs: Any, + ) -> BaseHTTPTransport: ... + + +_KNOWN_TRANSPORTS: dict[str, BaseHTTPTransportFactory] = { + "requests": _RequestsHTTPTransport, + "urllib3": _Urllib3HTTPTransport, +} + + +def _load_http_transport_factory(name: str) -> BaseHTTPTransportFactory: + """Return the transport factory registered under *name*. + + Checks the built-in transport registry first to avoid the overhead of an + entry-point scan for built-in transports. Falls back to standard entry-point + discovery for user supplied transports registered under the + ``opentelemetry_http_transport`` group. + + :param name: Entry point name, e.g. ``"requests"`` or ``"urllib3"``. + :returns: A callable with signature + ``(*, verify, cert, **kwargs) -> BaseHTTPTransport``. + :raises ValueError: If no transport is registered under *name*. + :raises TypeError: If the loaded entry point is not callable. + """ + if name in _KNOWN_TRANSPORTS: + return _KNOWN_TRANSPORTS[name] + # pylint: disable-next=import-outside-toplevel,import-error + from opentelemetry.util._importlib_metadata import ( # noqa: PLC0415 + entry_points, + ) + + ep = next( + iter(entry_points(group="opentelemetry_http_transport", name=name)), + None, + ) + if not ep: + raise ValueError( + f"No HTTP transport registered under name {name!r}. " + "Install the corresponding extra or register an entry point " + "under the 'opentelemetry_http_transport' group." + ) + factory = ep.load() + if not callable(factory): + raise TypeError(f"Transport {name!r} loaded from entry point is not callable (got {factory!r}).") + return cast("BaseHTTPTransportFactory", factory) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..9626e52 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__pycache__/_base.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__pycache__/_base.cpython-312.pyc new file mode 100644 index 0000000..9538f3a Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__pycache__/_base.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__pycache__/_requests.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__pycache__/_requests.cpython-312.pyc new file mode 100644 index 0000000..b9765aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__pycache__/_requests.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__pycache__/_urllib3.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__pycache__/_urllib3.cpython-312.pyc new file mode 100644 index 0000000..ff9f43c Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/__pycache__/_urllib3.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/_base.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/_base.py new file mode 100644 index 0000000..74b7976 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/_base.py @@ -0,0 +1,91 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import json +from abc import ABC, abstractmethod +from dataclasses import dataclass +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Mapping + from typing import Any + + +@dataclass(frozen=True, slots=True) +class BaseHTTPResult(ABC): + """Outcome of a single HTTP request made by a :class:`BaseHTTPTransport`. + + Either ``status_code`` and ``reason`` are populated (server responded), + or ``error`` is set (request failed before a response was received). + """ + + status_code: int | None = None + reason: str | None = None + error: Exception | None = None + + @abstractmethod + def content(self) -> bytes: + """Return the raw response body. + + Implementations may raise an exception if the returned content is malformed. + """ + + @abstractmethod + def headers(self) -> Mapping[str, str]: + """Return the response headers. + + The returned mapping MUST be case-insensitive with respect to header + keys. Headers with multiple values are represented as a single string + of comma separated values. + + Implementations may raise an exception if the returned headers are malformed. + """ + + def text(self) -> str: + """Return the response body decoded as UTF-8. + + Implementations may raise an exception if the returned string is malformed. + """ + return self.content().decode("utf-8") + + def json(self) -> Any: + """Return the response body parsed as JSON. + + Implementations may raise an exception if no response content + is available or the returned JSON is malformed. + """ + return json.loads(self.text()) + + +class BaseHTTPTransport(ABC): + """Abstract HTTP transport interface used by HTTP exporters.""" + + @abstractmethod + def request( + self, + method: str, + url: str, + *, + headers: dict[str, str] | None = None, + timeout: float | None = None, + data: bytes | None = None, + ) -> BaseHTTPResult: + """Send an HTTP request and return the result. + + :param method: HTTP method (e.g. ``"POST"``). + :param url: Target URL. + :param headers: Optional HTTP headers to include in the request. + :param timeout: Optional request timeout in seconds. + :param data: Optional request body. + :returns: A :class:`BaseHTTPResult` describing the outcome. + """ + + @abstractmethod + def close(self) -> None: + """Release any resources held by the transport.""" + + @abstractmethod + def is_connection_error(self, exception: Exception | None) -> bool: + """Return ``True`` if the exception is a transport-level connection error.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/_requests.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/_requests.py new file mode 100644 index 0000000..9ad205e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/_requests.py @@ -0,0 +1,116 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import functools +from dataclasses import dataclass, field +from typing import TYPE_CHECKING + +# pylint: disable-next=import-error +from opentelemetry.exporter.http.transport._base import ( + BaseHTTPResult, + BaseHTTPTransport, +) + +if TYPE_CHECKING: + from collections.abc import Mapping + from typing import Any + + import requests + from requests import Response + + +@functools.cache +def _get_connection_error_types() -> tuple[type[Exception], ...]: + # pylint: disable-next=import-outside-toplevel + import requests.exceptions # noqa: PLC0415 + + return ( + requests.exceptions.ConnectionError, + requests.exceptions.ConnectTimeout, + requests.exceptions.ReadTimeout, + requests.exceptions.Timeout, + requests.exceptions.SSLError, + requests.exceptions.ProxyError, + ) + + +@dataclass(frozen=True, slots=True) +class RequestsHTTPResult(BaseHTTPResult): + response: Response | None = field(default=None, hash=False, compare=False) + + def content(self) -> bytes: + if self.response is None: + return b"" + return self.response.content or b"" + + def text(self) -> str: + if self.response is None: + return "" + return self.response.text or "" + + def json(self) -> Any: + if self.response is None: + raise ValueError("No response available.") + return self.response.json() + + def headers(self) -> Mapping[str, str]: + if self.response is None: + return {} + return self.response.headers + + +class RequestsHTTPTransport(BaseHTTPTransport): + def __init__( + self, + *, + verify: bool | str = True, + cert: str | tuple[str, str] | None = None, + session: requests.Session | None = None, + **kwargs: Any, + ) -> None: + # pylint: disable-next=import-outside-toplevel + import requests # noqa: PLC0415 + + self._session = session if session is not None else requests.Session() + self._session.verify = verify + if cert is not None: + self._session.cert = cert + + def request( + self, + method: str, + url: str, + *, + headers: dict[str, str] | None = None, + timeout: float | None = None, + data: bytes | None = None, + ) -> BaseHTTPResult: + try: + response = self._session.request( + method=method, + url=url, + headers=headers, + data=data, + timeout=timeout, + allow_redirects=False, + ) + # pylint: disable-next=broad-exception-caught + except Exception as error: + # pylint: disable-next=unexpected-keyword-arg + return RequestsHTTPResult(error=error) + + # pylint: disable-next=unexpected-keyword-arg + return RequestsHTTPResult( + status_code=response.status_code, + reason=response.reason, + response=response, + ) + + # pylint: disable-next=no-self-use + def is_connection_error(self, exception: Exception | None) -> bool: + return isinstance(exception, _get_connection_error_types()) + + def close(self) -> None: + self._session.close() diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/_urllib3.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/_urllib3.py new file mode 100644 index 0000000..37ebed5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/_urllib3.py @@ -0,0 +1,131 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import functools +import json +from dataclasses import dataclass, field +from typing import TYPE_CHECKING + +# pylint: disable-next=import-error +from opentelemetry.exporter.http.transport._base import ( + BaseHTTPResult, + BaseHTTPTransport, +) + +if TYPE_CHECKING: + from collections.abc import Mapping + from typing import Any + + from urllib3 import BaseHTTPResponse + + +@functools.cache +def _get_connection_error_types() -> tuple[type[Exception], ...]: + # pylint: disable-next=import-outside-toplevel + import urllib3.exceptions # noqa: PLC0415 + + types: list[type[Exception]] = [ + urllib3.exceptions.ConnectionError, + urllib3.exceptions.NewConnectionError, + urllib3.exceptions.ConnectTimeoutError, + urllib3.exceptions.MaxRetryError, + urllib3.exceptions.ProtocolError, + ] + + # NameResolutionError was added in urllib3 2.0 + name_resolution_error = getattr(urllib3.exceptions, "NameResolutionError", None) + if name_resolution_error is not None: + types.append(name_resolution_error) + + return tuple(types) + + +@dataclass(frozen=True, slots=True) +class Urllib3HTTPResult(BaseHTTPResult): + response: BaseHTTPResponse | None = field(default=None, hash=False, compare=False) + + def content(self) -> bytes: + if self.response is None: + return b"" + return self.response.data or b"" + + def headers(self) -> Mapping[str, str]: + if self.response is None: + return {} + return self.response.headers + + def json(self) -> Any: + if self.response is None: + raise ValueError("No response available.") + return json.loads(self.content()) + + +class Urllib3HTTPTransport(BaseHTTPTransport): + def __init__( + self, + *, + verify: bool | str = True, + cert: str | tuple[str, str] | None = None, + **kwargs: Any, + ) -> None: + # pylint: disable-next=import-outside-toplevel + import urllib3 # noqa: PLC0415 + + pool_kwargs: dict[str, object] = { + "retries": urllib3.Retry(0, redirect=False), + } + if verify is False: + pool_kwargs["cert_reqs"] = "CERT_NONE" + else: + pool_kwargs["cert_reqs"] = "CERT_REQUIRED" + if isinstance(verify, str): + pool_kwargs["ca_certs"] = verify + if isinstance(cert, tuple): + pool_kwargs["cert_file"] = cert[0] + pool_kwargs["key_file"] = cert[1] + elif isinstance(cert, str): + pool_kwargs["cert_file"] = cert + + self._pool = urllib3.PoolManager(**pool_kwargs) # type: ignore + + def request( + self, + method: str, + url: str, + *, + headers: dict[str, str] | None = None, + timeout: float | None = None, + data: bytes | None = None, + ) -> BaseHTTPResult: + # pylint: disable-next=import-outside-toplevel + import urllib3 # noqa: PLC0415 + + try: + response = self._pool.request( + method=method, + url=url, + headers=headers, + body=data, + timeout=urllib3.Timeout(total=timeout) if timeout is not None else None, + preload_content=True, + ) + # pylint: disable-next=broad-exception-caught + except Exception as error: + # pylint: disable-next=unexpected-keyword-arg + return Urllib3HTTPResult(error=error) + + # pylint: disable-next=unexpected-keyword-arg + return Urllib3HTTPResult( + status_code=response.status, + reason=response.reason, + response=response, + ) + + # pylint: disable-next=no-self-use + def is_connection_error(self, exception: Exception | None) -> bool: + return isinstance(exception, _get_connection_error_types()) + + def close(self) -> None: + self._pool.clear() diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/version/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/version/__init__.py new file mode 100644 index 0000000..d3741ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/version/__init__.py @@ -0,0 +1,4 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +__version__ = "0.66b0" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/version/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/version/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e730b51 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/http/transport/version/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__init__.py new file mode 100644 index 0000000..e57cf4a --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__init__.py @@ -0,0 +1,2 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..cbbdfe0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__pycache__/_aggregation.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__pycache__/_aggregation.cpython-312.pyc new file mode 100644 index 0000000..7cd858a Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__pycache__/_aggregation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__pycache__/_exporter_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__pycache__/_exporter_metrics.cpython-312.pyc new file mode 100644 index 0000000..a6f56be Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__pycache__/_exporter_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__pycache__/http.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__pycache__/http.cpython-312.pyc new file mode 100644 index 0000000..b9bf788 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/__pycache__/http.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/_aggregation.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/_aggregation.py new file mode 100644 index 0000000..a7caef0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/_aggregation.py @@ -0,0 +1,112 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import logging +import os + +from opentelemetry.sdk.environment_variables import ( + OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION, + OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE, +) +from opentelemetry.sdk.metrics import ( + Counter, + Histogram, + ObservableCounter, + ObservableGauge, + ObservableUpDownCounter, + UpDownCounter, +) +from opentelemetry.sdk.metrics.export import AggregationTemporality +from opentelemetry.sdk.metrics.view import ( + Aggregation, + ExplicitBucketHistogramAggregation, + ExponentialBucketHistogramAggregation, +) + +_logger = logging.getLogger(__name__) + + +def _get_temporality( + preferred_temporality: dict[type, AggregationTemporality] | None, +) -> dict[type, AggregationTemporality]: + temporality_preference = ( + os.environ.get( + OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE, + "CUMULATIVE", + ) + .upper() + .strip() + ) + + instrument_class_temporality: dict[type, AggregationTemporality] + match temporality_preference: + case "DELTA": + instrument_class_temporality = { + Counter: AggregationTemporality.DELTA, + UpDownCounter: AggregationTemporality.CUMULATIVE, + Histogram: AggregationTemporality.DELTA, + ObservableCounter: AggregationTemporality.DELTA, + ObservableUpDownCounter: AggregationTemporality.CUMULATIVE, + ObservableGauge: AggregationTemporality.CUMULATIVE, + } + case "LOWMEMORY": + instrument_class_temporality = { + Counter: AggregationTemporality.DELTA, + UpDownCounter: AggregationTemporality.CUMULATIVE, + Histogram: AggregationTemporality.DELTA, + ObservableCounter: AggregationTemporality.CUMULATIVE, + ObservableUpDownCounter: AggregationTemporality.CUMULATIVE, + ObservableGauge: AggregationTemporality.CUMULATIVE, + } + case _: + if temporality_preference != "CUMULATIVE": + _logger.warning( + ("Invalid value for %s: %s, using cumulative temporality aggregation"), + OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE, + temporality_preference, + ) + instrument_class_temporality = { + Counter: AggregationTemporality.CUMULATIVE, + UpDownCounter: AggregationTemporality.CUMULATIVE, + Histogram: AggregationTemporality.CUMULATIVE, + ObservableCounter: AggregationTemporality.CUMULATIVE, + ObservableUpDownCounter: AggregationTemporality.CUMULATIVE, + ObservableGauge: AggregationTemporality.CUMULATIVE, + } + + return instrument_class_temporality | (preferred_temporality or {}) + + +def _get_aggregation( + preferred_aggregation: dict[type, Aggregation] | None, +) -> dict[type, Aggregation]: + default_histogram_aggregation = ( + os.environ.get( + OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION, + "explicit_bucket_histogram", + ) + .upper() + .strip() + ) + + instrument_class_aggregation: dict[type, Aggregation] + match default_histogram_aggregation: + case "BASE2_EXPONENTIAL_BUCKET_HISTOGRAM": + instrument_class_aggregation = { + Histogram: ExponentialBucketHistogramAggregation(), + } + case _: + if default_histogram_aggregation != "EXPLICIT_BUCKET_HISTOGRAM": + _logger.warning( + ("Invalid value for %s: %s, using explicit bucket histogram aggregation"), + OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION, + default_histogram_aggregation, + ) + + instrument_class_aggregation = { + Histogram: ExplicitBucketHistogramAggregation(), + } + + return instrument_class_aggregation | (preferred_aggregation or {}) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/_exporter_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/_exporter_metrics.py new file mode 100644 index 0000000..8d51674 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/_exporter_metrics.py @@ -0,0 +1,152 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections import Counter +from collections.abc import Iterator +from contextlib import AbstractContextManager, contextmanager +from dataclasses import dataclass +from time import perf_counter +from typing import TYPE_CHECKING, Protocol +from urllib.parse import urlparse + +from opentelemetry.metrics import MeterProvider, get_meter_provider +from opentelemetry.semconv._incubating.attributes.otel_attributes import ( + OTEL_COMPONENT_NAME, + OTEL_COMPONENT_TYPE, + OtelComponentTypeValues, +) +from opentelemetry.semconv._incubating.metrics.otel_metrics import ( + create_otel_sdk_exporter_log_exported, + create_otel_sdk_exporter_log_inflight, + create_otel_sdk_exporter_metric_data_point_exported, + create_otel_sdk_exporter_metric_data_point_inflight, + create_otel_sdk_exporter_operation_duration, + create_otel_sdk_exporter_span_exported, + create_otel_sdk_exporter_span_inflight, +) +from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE +from opentelemetry.semconv.attributes.server_attributes import ( + SERVER_ADDRESS, + SERVER_PORT, +) + +if TYPE_CHECKING: + from typing import Literal + + from opentelemetry.util.types import AnyValue, Attributes + +_component_counter = Counter() + +_GRPC_COMPONENT_TYPES = frozenset( + { + OtelComponentTypeValues.OTLP_GRPC_SPAN_EXPORTER, + OtelComponentTypeValues.OTLP_GRPC_LOG_EXPORTER, + OtelComponentTypeValues.OTLP_GRPC_METRIC_EXPORTER, + } +) + + +@dataclass +class ExportResult: + error: Exception | None = None + error_attrs: Attributes = None + + +class ExporterMetricsT(Protocol): + def export_operation(self, num_items: int) -> AbstractContextManager[ExportResult]: ... + + +class NoOpExporterMetrics: + @contextmanager + # pylint: disable-next=no-self-use + def export_operation(self, num_items: int) -> Iterator[ExportResult]: + yield ExportResult() + + +class ExporterMetrics: + def __init__( + self, + component_type: OtelComponentTypeValues | None, + signal: Literal["traces", "metrics", "logs"], + endpoint: str, + meter_provider: MeterProvider | None, + ) -> None: + if signal == "traces": + create_exported = create_otel_sdk_exporter_span_exported + create_inflight = create_otel_sdk_exporter_span_inflight + elif signal == "logs": + create_exported = create_otel_sdk_exporter_log_exported + create_inflight = create_otel_sdk_exporter_log_inflight + else: + create_exported = create_otel_sdk_exporter_metric_data_point_exported + create_inflight = create_otel_sdk_exporter_metric_data_point_inflight + + if not endpoint.startswith("//") and "://" not in endpoint: + endpoint = f"//{endpoint}" + parsed_endpoint = urlparse(endpoint) + + port = parsed_endpoint.port + if port is None: + if component_type in _GRPC_COMPONENT_TYPES or parsed_endpoint.scheme == "https": + port = 443 + elif parsed_endpoint.scheme == "http": + port = 80 + + component_type_value = component_type.value if component_type else "unknown_otlp_exporter" + count = _component_counter[component_type_value] + _component_counter[component_type_value] = count + 1 + self._standard_attrs: dict[str, AnyValue] = { + OTEL_COMPONENT_TYPE: component_type_value, + OTEL_COMPONENT_NAME: f"{component_type_value}/{count}", + } + if parsed_endpoint.hostname: + self._standard_attrs[SERVER_ADDRESS] = parsed_endpoint.hostname + if port is not None: + self._standard_attrs[SERVER_PORT] = port + + meter_provider = meter_provider or get_meter_provider() + meter = meter_provider.get_meter("opentelemetry-sdk") + self._inflight = create_inflight(meter) + self._exported = create_exported(meter) + self._duration = create_otel_sdk_exporter_operation_duration(meter) + + @contextmanager + def export_operation(self, num_items: int) -> Iterator[ExportResult]: + start_time = perf_counter() + self._inflight.add(num_items, self._standard_attrs) + + result = ExportResult() + try: + yield result + finally: + error = result.error + error_attrs = result.error_attrs + + end_time = perf_counter() + self._inflight.add(-num_items, self._standard_attrs) + exported_attrs = ( + {**self._standard_attrs, ERROR_TYPE: type(error).__qualname__} if error else self._standard_attrs + ) + self._exported.add(num_items, exported_attrs) + duration_attrs = {**exported_attrs, **error_attrs} if error_attrs else exported_attrs + self._duration.record(end_time - start_time, duration_attrs) + + +def create_exporter_metrics( + component_type: OtelComponentTypeValues | None, + signal: Literal["traces", "metrics", "logs"], + endpoint: str, + meter_provider: MeterProvider | None, + enabled: bool, +) -> ExporterMetricsT: + if not enabled: + return NoOpExporterMetrics() + + return ExporterMetrics( + component_type, + signal, + endpoint, + meter_provider, + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/http.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/http.py new file mode 100644 index 0000000..aadfb24 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/http.py @@ -0,0 +1,248 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import enum +import gzip +import logging +import math +import random +import threading +import time +import zlib +from collections.abc import Mapping +from dataclasses import dataclass +from datetime import timezone +from email.utils import parsedate_to_datetime +from http import HTTPStatus +from io import BytesIO +from typing import TYPE_CHECKING, Final, Literal + +if TYPE_CHECKING: + from opentelemetry.exporter.http.transport._base import ( + BaseHTTPResult, + BaseHTTPTransport, + ) + +_logger = logging.getLogger(__name__) + +_MAX_RETRIES: Final[int] = 6 +_DEFAULT_TIMEOUT: Final[float] = 10.0 +_DEFAULT_JITTER: Final[float] = 0.2 + + +_RETRYABLE_STATUS_CODES: Final[frozenset[int]] = frozenset( + { + HTTPStatus.TOO_MANY_REQUESTS.value, + HTTPStatus.BAD_GATEWAY.value, + HTTPStatus.SERVICE_UNAVAILABLE.value, + HTTPStatus.GATEWAY_TIMEOUT.value, + } +) + + +def _is_retryable(status_code: int | None) -> bool: + return status_code in _RETRYABLE_STATUS_CODES + + +def _extract_retry_after(result: BaseHTTPResult) -> float | None: + """Parse the ``Retry-After`` header (RFC 7231).""" + try: + value = result.headers().get("retry-after") + # pylint: disable-next=broad-exception-caught + except Exception: + return None + if value is None: + return None + value = value.strip() + + # delay-seconds: a non-negative decimal integer. + try: + seconds = float(value) + except ValueError: + pass + else: + return max(seconds, 0.0) if math.isfinite(seconds) else None + + # HTTP-date: wait until the indicated absolute time. + try: + retry_at = parsedate_to_datetime(value) + except (TypeError, ValueError): + return None + if retry_at.tzinfo is None: + retry_at = retry_at.replace(tzinfo=timezone.utc) + return max(retry_at.timestamp() - time.time(), 0.0) + + +class Compression(enum.Enum): + NONE = "none" + DEFLATE = "deflate" + GZIP = "gzip" + + @staticmethod + def from_str(value: str) -> Compression: + match value.strip().lower(): + case "none": + return Compression.NONE + case "deflate": + return Compression.DEFLATE + case "gzip": + return Compression.GZIP + case _: + raise ValueError(f"Invalid compression type: {value!r}. Expected one of: 'none', 'deflate', 'gzip'.") + + +@dataclass(slots=True, frozen=True) +class _ExportResult: + """Outcome of an OTLP export attempt, including retry exhaustion.""" + + success: bool + status_code: int | None + reason: str | None + error: Exception | None + + +class _OTLPHTTPClient: + """Sends serialized OTLP payloads over HTTP with retry logic. + + Compression, backoff, and connection-error recovery are handled internally. + Callers interact through the :meth:`export` and :meth:`close` methods. + """ + + def __init__( + self, + transport: BaseHTTPTransport, + endpoint: str, + kind: Literal["spans", "logs", "metrics"], + timeout: float = _DEFAULT_TIMEOUT, + compression: Compression = Compression.NONE, + headers: Mapping[str, str] | None = None, + jitter: float = _DEFAULT_JITTER, + logger: logging.Logger | None = None, + ) -> None: + self._transport = transport + self._endpoint = endpoint + self._timeout = timeout + self._compression = compression + self._headers = dict(headers) if headers is not None else {} + if self._compression is not Compression.NONE and not any( + key.lower() == "content-encoding" for key in self._headers + ): + self._headers["Content-Encoding"] = self._compression.value + self._kind = kind + self._jitter = min(max(jitter, 0.0), 1.0) + self._logger = logger if logger is not None else _logger + self._shutdown = False + self._shutdown_event = threading.Event() + + def _compute_backoff(self, retry: int) -> float: + return 2**retry * random.uniform(1 - self._jitter, 1 + self._jitter) + + def _compress(self, serialized_data: bytes) -> bytes: + if self._compression is Compression.GZIP: + buf = BytesIO() + with gzip.GzipFile(fileobj=buf, mode="w") as gz: + gz.write(serialized_data) + return buf.getvalue() + if self._compression is Compression.DEFLATE: + return zlib.compress(serialized_data) + return serialized_data + + def _submit(self, data: bytes, timeout: float) -> BaseHTTPResult: + deadline = time.time() + timeout + result = self._transport.request( + "POST", + self._endpoint, + headers=self._headers, + data=data, + timeout=timeout, + ) + if ( + result.error is not None + and self._transport.is_connection_error(result.error) + and (remaining := deadline - time.time()) > 0 + ): + # Immediately retry connection errors once without backoff. These + # usually indicate a stale pooled connection that the transport will + # reestablish on the next attempt. + result = self._transport.request( + "POST", + self._endpoint, + headers=self._headers, + data=data, + timeout=remaining, + ) + return result + + def export(self, data: bytes) -> _ExportResult: + """Export a serialized payload, retrying on transient failures. + + :param data: Serialized bytes to send. + :returns: An :class:`ExportResult` indicating success or the reason for failure. + """ + data = self._compress(data) + deadline = time.time() + self._timeout + + for retry in range(_MAX_RETRIES): + backoff = self._compute_backoff(retry) + status_code: int | None = None + reason: str | None = None + export_error: Exception | None + retryable: bool + + try: + result = self._submit(data, max(deadline - time.time(), 0.0)) + # pylint: disable-next=broad-exception-caught + except Exception as error: + export_error = error + retryable = False + else: + status_code = result.status_code + reason = result.reason + if status_code is not None and 200 <= status_code < 400: + return _ExportResult(True, status_code, reason, None) + export_error = result.error + retryable = ( + _is_retryable(status_code) if status_code else self._transport.is_connection_error(result.error) + ) + if retryable and status_code is not None and (retry_after := _extract_retry_after(result)) is not None: + backoff = retry_after + + if not retryable: + self._logger.error( + "Failed to export %s batch code: %s, reason: %s", + self._kind, + status_code, + reason or export_error or "unknown", + ) + return _ExportResult(False, status_code, reason, export_error) + + if retry + 1 == _MAX_RETRIES or backoff > (deadline - time.time()) or self._shutdown_event.is_set(): + self._logger.error( + "Failed to export %s batch due to timeout, max retries or shutdown.", + self._kind, + ) + return _ExportResult(False, status_code, reason, export_error) + + self._logger.warning( + "Transient error %s encountered while exporting %s batch, retrying in %.2fs.", + reason or export_error, + self._kind, + backoff, + ) + shutdown = self._shutdown_event.wait(backoff) + if shutdown: + self._logger.warning("Shutdown in progress, aborting retry.") + break + + return _ExportResult(False, None, None, None) + + def shutdown(self) -> None: + """Shutdown the client.""" + if self._shutdown: + self._logger.warning("OTLP client already shutdown, ignoring call") + return + self._shutdown = True + self._shutdown_event.set() + self._transport.close() diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/version/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/version/__init__.py new file mode 100644 index 0000000..d3741ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/version/__init__.py @@ -0,0 +1,4 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +__version__ = "0.66b0" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/version/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/version/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1749a02 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/common/version/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__init__.py new file mode 100644 index 0000000..7ad07b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__init__.py @@ -0,0 +1,7 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from opentelemetry.exporter.otlp.proto.common.version import __version__ + +__all__ = ["__version__"] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..babdfb5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__pycache__/_log_encoder.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__pycache__/_log_encoder.cpython-312.pyc new file mode 100644 index 0000000..de9628b Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__pycache__/_log_encoder.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__pycache__/metrics_encoder.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__pycache__/metrics_encoder.cpython-312.pyc new file mode 100644 index 0000000..06794f3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__pycache__/metrics_encoder.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__pycache__/trace_encoder.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__pycache__/trace_encoder.cpython-312.pyc new file mode 100644 index 0000000..3896126 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/__pycache__/trace_encoder.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py new file mode 100644 index 0000000..55895fc --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py @@ -0,0 +1,121 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from __future__ import annotations + +import logging +from collections.abc import Callable, Mapping, Sequence +from typing import ( + Any, + TypeVar, +) + +from opentelemetry.proto.common.v1.common_pb2 import AnyValue as PB2AnyValue +from opentelemetry.proto.common.v1.common_pb2 import ( + ArrayValue as PB2ArrayValue, +) +from opentelemetry.proto.common.v1.common_pb2 import ( + InstrumentationScope as PB2InstrumentationScope, +) +from opentelemetry.proto.common.v1.common_pb2 import KeyValue as PB2KeyValue +from opentelemetry.proto.common.v1.common_pb2 import ( + KeyValueList as PB2KeyValueList, +) +from opentelemetry.proto.resource.v1.resource_pb2 import ( + Resource as PB2Resource, +) +from opentelemetry.sdk.trace import Resource +from opentelemetry.sdk.util.instrumentation import InstrumentationScope +from opentelemetry.util.types import Attributes + +_logger = logging.getLogger(__name__) + +_TypingResourceT = TypeVar("_TypingResourceT") +_ResourceDataT = TypeVar("_ResourceDataT") + + +def _encode_instrumentation_scope( + instrumentation_scope: InstrumentationScope, +) -> PB2InstrumentationScope: + if instrumentation_scope is None: + return PB2InstrumentationScope() + return PB2InstrumentationScope( + name=instrumentation_scope.name, + version=instrumentation_scope.version, + attributes=_encode_attributes(instrumentation_scope.attributes), + ) + + +def _encode_resource(resource: Resource) -> PB2Resource: + return PB2Resource(attributes=_encode_attributes(resource.attributes)) + + +def _encode_value(value: Any) -> PB2AnyValue: + if value is None: + return PB2AnyValue() + if isinstance(value, bool): + return PB2AnyValue(bool_value=value) + if isinstance(value, str): + return PB2AnyValue(string_value=value) + if isinstance(value, int): + return PB2AnyValue(int_value=value) + if isinstance(value, float): + return PB2AnyValue(double_value=value) + if isinstance(value, bytes): + return PB2AnyValue(bytes_value=value) + if isinstance(value, Sequence): + return PB2AnyValue(array_value=PB2ArrayValue(values=[_encode_value(v) for v in value])) + if isinstance(value, Mapping): + return PB2AnyValue( + kvlist_value=PB2KeyValueList(values=[_encode_key_value(str(k), v) for k, v in value.items()]) + ) + raise Exception(f"Invalid type {type(value)} of value {value}") + + +def _encode_key_value(key: str, value: Any) -> PB2KeyValue: + return PB2KeyValue(key=key, value=_encode_value(value)) + + +def _encode_span_id(span_id: int) -> bytes: + return span_id.to_bytes(length=8, byteorder="big", signed=False) + + +def _encode_trace_id(trace_id: int) -> bytes: + return trace_id.to_bytes(length=16, byteorder="big", signed=False) + + +def _encode_attributes(attributes: Attributes) -> list[PB2KeyValue]: + if not attributes: + return [] + pb2_attributes = [] + for key, value in attributes.items(): + # pylint: disable=broad-exception-caught + try: + pb2_attributes.append(_encode_key_value(key, value)) + except Exception as error: + _logger.exception("Failed to encode key %s: %s", key, error) + return pb2_attributes + + +def _get_resource_data( + sdk_resource_scope_data: dict[Resource, _ResourceDataT], + resource_class: Callable[..., _TypingResourceT], + name: str, +) -> list[_TypingResourceT]: + resource_data = [] + + for ( + sdk_resource, + scope_data, + ) in sdk_resource_scope_data.items(): + collector_resource = PB2Resource(attributes=_encode_attributes(sdk_resource.attributes)) + resource_data.append( + resource_class( + **{ + "resource": collector_resource, + f"scope_{name}": scope_data.values(), + } + ) + ) + return resource_data diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..33ab557 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py new file mode 100644 index 0000000..bbb907a --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py @@ -0,0 +1,87 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +from collections import defaultdict +from collections.abc import Sequence + +from opentelemetry.exporter.otlp.proto.common._internal import ( + _encode_attributes, + _encode_instrumentation_scope, + _encode_resource, + _encode_span_id, + _encode_trace_id, + _encode_value, +) +from opentelemetry.proto.collector.logs.v1.logs_service_pb2 import ( + ExportLogsServiceRequest, +) +from opentelemetry.proto.logs.v1.logs_pb2 import LogRecord as PB2LogRecord +from opentelemetry.proto.logs.v1.logs_pb2 import ( + ResourceLogs, + ScopeLogs, +) +from opentelemetry.sdk._logs import ReadableLogRecord + + +def encode_logs( + batch: Sequence[ReadableLogRecord], +) -> ExportLogsServiceRequest: + return ExportLogsServiceRequest(resource_logs=_encode_resource_logs(batch)) + + +def _encode_log(readable_log_record: ReadableLogRecord) -> PB2LogRecord: + span_id = ( + None if readable_log_record.log_record.span_id == 0 else _encode_span_id(readable_log_record.log_record.span_id) + ) + trace_id = ( + None + if readable_log_record.log_record.trace_id == 0 + else _encode_trace_id(readable_log_record.log_record.trace_id) + ) + return PB2LogRecord( + time_unix_nano=readable_log_record.log_record.timestamp, + observed_time_unix_nano=readable_log_record.log_record.observed_timestamp, + span_id=span_id, + trace_id=trace_id, + flags=int(readable_log_record.log_record.trace_flags), + body=_encode_value(readable_log_record.log_record.body), + severity_text=readable_log_record.log_record.severity_text, + attributes=_encode_attributes(readable_log_record.log_record.attributes), + dropped_attributes_count=readable_log_record.dropped_attributes, + severity_number=getattr(readable_log_record.log_record.severity_number, "value", None), + event_name=readable_log_record.log_record.event_name, + ) + + +def _encode_resource_logs( + batch: Sequence[ReadableLogRecord], +) -> list[ResourceLogs]: + sdk_resource_logs = defaultdict(lambda: defaultdict(list)) + + for readable_log in batch: + sdk_resource = readable_log.resource + sdk_instrumentation = readable_log.instrumentation_scope or None + pb2_log = _encode_log(readable_log) + + sdk_resource_logs[sdk_resource][sdk_instrumentation].append(pb2_log) + + pb2_resource_logs = [] + + for sdk_resource, sdk_instrumentations in sdk_resource_logs.items(): + scope_logs = [] + for sdk_instrumentation, pb2_logs in sdk_instrumentations.items(): + scope_logs.append( + ScopeLogs( + scope=(_encode_instrumentation_scope(sdk_instrumentation)), + log_records=pb2_logs, + schema_url=sdk_instrumentation.schema_url if sdk_instrumentation else None, + ) + ) + pb2_resource_logs.append( + ResourceLogs( + resource=_encode_resource(sdk_resource), + scope_logs=scope_logs, + schema_url=sdk_resource.schema_url, + ) + ) + + return pb2_resource_logs diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c9424a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py new file mode 100644 index 0000000..37ae556 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py @@ -0,0 +1,348 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import logging +from os import environ + +from opentelemetry.exporter.otlp.proto.common._internal import ( + _encode_attributes, + _encode_instrumentation_scope, + _encode_span_id, + _encode_trace_id, +) +from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import ( + ExportMetricsServiceRequest, +) +from opentelemetry.proto.metrics.v1 import metrics_pb2 as pb2 +from opentelemetry.proto.resource.v1.resource_pb2 import ( + Resource as PB2Resource, +) +from opentelemetry.sdk.environment_variables import ( + OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION, + OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE, +) +from opentelemetry.sdk.metrics import ( + Counter, + Exemplar, + Histogram, + ObservableCounter, + ObservableGauge, + ObservableUpDownCounter, + UpDownCounter, +) +from opentelemetry.sdk.metrics.export import ( + AggregationTemporality, + Gauge, + MetricExporter, + MetricsData, + Sum, +) +from opentelemetry.sdk.metrics.export import ( + ExponentialHistogram as ExponentialHistogramType, +) +from opentelemetry.sdk.metrics.export import ( + Histogram as HistogramType, +) +from opentelemetry.sdk.metrics.view import ( + Aggregation, + ExplicitBucketHistogramAggregation, + ExponentialBucketHistogramAggregation, +) + +_logger = logging.getLogger(__name__) + + +class OTLPMetricExporterMixin: + def _common_configuration( + self, + preferred_temporality: dict[type, AggregationTemporality] | None = None, + preferred_aggregation: dict[type, Aggregation] | None = None, + ) -> None: + MetricExporter.__init__( + self, + preferred_temporality=self._get_temporality(preferred_temporality), + preferred_aggregation=self._get_aggregation(preferred_aggregation), + ) + + def _get_temporality( + self, preferred_temporality: dict[type, AggregationTemporality] + ) -> dict[type, AggregationTemporality]: + otel_exporter_otlp_metrics_temporality_preference = ( + environ.get( + OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE, + "CUMULATIVE", + ) + .upper() + .strip() + ) + + if otel_exporter_otlp_metrics_temporality_preference == "DELTA": + instrument_class_temporality = { + Counter: AggregationTemporality.DELTA, + UpDownCounter: AggregationTemporality.CUMULATIVE, + Histogram: AggregationTemporality.DELTA, + ObservableCounter: AggregationTemporality.DELTA, + ObservableUpDownCounter: AggregationTemporality.CUMULATIVE, + ObservableGauge: AggregationTemporality.CUMULATIVE, + } + + elif otel_exporter_otlp_metrics_temporality_preference == "LOWMEMORY": + instrument_class_temporality = { + Counter: AggregationTemporality.DELTA, + UpDownCounter: AggregationTemporality.CUMULATIVE, + Histogram: AggregationTemporality.DELTA, + ObservableCounter: AggregationTemporality.CUMULATIVE, + ObservableUpDownCounter: AggregationTemporality.CUMULATIVE, + ObservableGauge: AggregationTemporality.CUMULATIVE, + } + + else: + if otel_exporter_otlp_metrics_temporality_preference != ("CUMULATIVE"): + _logger.warning( + "Unrecognized OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE value found: %s, using CUMULATIVE", + otel_exporter_otlp_metrics_temporality_preference, + ) + instrument_class_temporality = { + Counter: AggregationTemporality.CUMULATIVE, + UpDownCounter: AggregationTemporality.CUMULATIVE, + Histogram: AggregationTemporality.CUMULATIVE, + ObservableCounter: AggregationTemporality.CUMULATIVE, + ObservableUpDownCounter: AggregationTemporality.CUMULATIVE, + ObservableGauge: AggregationTemporality.CUMULATIVE, + } + + instrument_class_temporality.update(preferred_temporality or {}) + + return instrument_class_temporality + + def _get_aggregation( + self, + preferred_aggregation: dict[type, Aggregation], + ) -> dict[type, Aggregation]: + otel_exporter_otlp_metrics_default_histogram_aggregation = environ.get( + OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION, + "explicit_bucket_histogram", + ) + + if otel_exporter_otlp_metrics_default_histogram_aggregation == ("base2_exponential_bucket_histogram"): + instrument_class_aggregation = { + Histogram: ExponentialBucketHistogramAggregation(), + } + + else: + if otel_exporter_otlp_metrics_default_histogram_aggregation != ("explicit_bucket_histogram"): + _logger.warning( + ("Invalid value for %s: %s, using explicit bucket histogram aggregation"), + OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION, + otel_exporter_otlp_metrics_default_histogram_aggregation, + ) + + instrument_class_aggregation = { + Histogram: ExplicitBucketHistogramAggregation(), + } + + instrument_class_aggregation.update(preferred_aggregation or {}) + + return instrument_class_aggregation + + +class EncodingException(Exception): + """ + Raised by encode_metrics() when an exception is caught during encoding. Contains the problematic metric so + the misbehaving metric name and details can be logged during exception handling. + """ + + def __init__(self, original_exception, metric): + super().__init__() + self.original_exception = original_exception + self.metric = metric + + def __str__(self): + return f"{self.metric}\n{self.original_exception}" + + +def encode_metrics(data: MetricsData) -> ExportMetricsServiceRequest: + resource_metrics_dict = {} + + for resource_metrics in data.resource_metrics: + _encode_resource_metrics(resource_metrics, resource_metrics_dict) + + resource_data = [] + for ( + sdk_resource, + scope_data, + ) in resource_metrics_dict.items(): + resource_data.append( + pb2.ResourceMetrics( + resource=PB2Resource(attributes=_encode_attributes(sdk_resource.attributes)), + scope_metrics=scope_data.values(), + schema_url=sdk_resource.schema_url, + ) + ) + return ExportMetricsServiceRequest(resource_metrics=resource_data) + + +def _encode_resource_metrics(resource_metrics, resource_metrics_dict): + resource = resource_metrics.resource + # It is safe to assume that each entry in data.resource_metrics is + # associated with an unique resource. + scope_metrics_dict = {} + resource_metrics_dict[resource] = scope_metrics_dict + for scope_metrics in resource_metrics.scope_metrics: + instrumentation_scope = scope_metrics.scope + + # The SDK groups metrics in instrumentation scopes already so + # there is no need to check for existing instrumentation scopes + # here. + pb2_scope_metrics = pb2.ScopeMetrics( + scope=_encode_instrumentation_scope(instrumentation_scope), + schema_url=instrumentation_scope.schema_url, + ) + + scope_metrics_dict[instrumentation_scope] = pb2_scope_metrics + + for metric in scope_metrics.metrics: + pb2_metric = pb2.Metric( + name=metric.name, + description=metric.description, + unit=metric.unit, + ) + + try: + _encode_metric(metric, pb2_metric) + except Exception as ex: + # `from None` so we don't get "During handling of the above exception, another exception occurred:" + raise EncodingException(ex, metric) from None + + pb2_scope_metrics.metrics.append(pb2_metric) + + +def _encode_metric(metric, pb2_metric): + if isinstance(metric.data, Gauge): + for data_point in metric.data.data_points: + pt = pb2.NumberDataPoint( + attributes=_encode_attributes(data_point.attributes), + time_unix_nano=data_point.time_unix_nano, + exemplars=_encode_exemplars(data_point.exemplars), + ) + if isinstance(data_point.value, int): + pt.as_int = data_point.value + else: + pt.as_double = data_point.value + pb2_metric.gauge.data_points.append(pt) + + elif isinstance(metric.data, HistogramType): + for data_point in metric.data.data_points: + pt = pb2.HistogramDataPoint( + attributes=_encode_attributes(data_point.attributes), + time_unix_nano=data_point.time_unix_nano, + start_time_unix_nano=data_point.start_time_unix_nano, + exemplars=_encode_exemplars(data_point.exemplars), + count=data_point.count, + sum=data_point.sum, + bucket_counts=data_point.bucket_counts, + explicit_bounds=data_point.explicit_bounds, + max=data_point.max, + min=data_point.min, + ) + pb2_metric.histogram.aggregation_temporality = metric.data.aggregation_temporality + pb2_metric.histogram.data_points.append(pt) + + elif isinstance(metric.data, Sum): + for data_point in metric.data.data_points: + pt = pb2.NumberDataPoint( + attributes=_encode_attributes(data_point.attributes), + start_time_unix_nano=data_point.start_time_unix_nano, + time_unix_nano=data_point.time_unix_nano, + exemplars=_encode_exemplars(data_point.exemplars), + ) + if isinstance(data_point.value, int): + pt.as_int = data_point.value + else: + pt.as_double = data_point.value + # note that because sum is a message type, the + # fields must be set individually rather than + # instantiating a pb2.Sum and setting it once + pb2_metric.sum.aggregation_temporality = metric.data.aggregation_temporality + pb2_metric.sum.is_monotonic = metric.data.is_monotonic + pb2_metric.sum.data_points.append(pt) + + elif isinstance(metric.data, ExponentialHistogramType): + for data_point in metric.data.data_points: + if data_point.positive.bucket_counts: + positive = pb2.ExponentialHistogramDataPoint.Buckets( + offset=data_point.positive.offset, + bucket_counts=data_point.positive.bucket_counts, + ) + else: + positive = None + + if data_point.negative.bucket_counts: + negative = pb2.ExponentialHistogramDataPoint.Buckets( + offset=data_point.negative.offset, + bucket_counts=data_point.negative.bucket_counts, + ) + else: + negative = None + + pt = pb2.ExponentialHistogramDataPoint( + attributes=_encode_attributes(data_point.attributes), + time_unix_nano=data_point.time_unix_nano, + start_time_unix_nano=data_point.start_time_unix_nano, + exemplars=_encode_exemplars(data_point.exemplars), + count=data_point.count, + sum=data_point.sum, + scale=data_point.scale, + zero_count=data_point.zero_count, + positive=positive, + negative=negative, + flags=data_point.flags, + max=data_point.max, + min=data_point.min, + ) + pb2_metric.exponential_histogram.aggregation_temporality = metric.data.aggregation_temporality + pb2_metric.exponential_histogram.data_points.append(pt) + + else: + _logger.warning( + "unsupported data type %s", + metric.data.__class__.__name__, + ) + + +def _encode_exemplars(sdk_exemplars: list[Exemplar]) -> list[pb2.Exemplar]: + """ + Converts a list of SDK Exemplars into a list of protobuf Exemplars. + + Args: + sdk_exemplars (list): The list of exemplars from the OpenTelemetry SDK. + + Returns: + list: A list of protobuf exemplars. + """ + pb_exemplars = [] + for sdk_exemplar in sdk_exemplars: + if sdk_exemplar.span_id is not None and sdk_exemplar.trace_id is not None: + pb_exemplar = pb2.Exemplar( + time_unix_nano=sdk_exemplar.time_unix_nano, + span_id=_encode_span_id(sdk_exemplar.span_id), + trace_id=_encode_trace_id(sdk_exemplar.trace_id), + filtered_attributes=_encode_attributes(sdk_exemplar.filtered_attributes), + ) + else: + pb_exemplar = pb2.Exemplar( + time_unix_nano=sdk_exemplar.time_unix_nano, + filtered_attributes=_encode_attributes(sdk_exemplar.filtered_attributes), + ) + + # Assign the value based on its type in the SDK exemplar + if isinstance(sdk_exemplar.value, float): + pb_exemplar.as_double = sdk_exemplar.value + elif isinstance(sdk_exemplar.value, int): + pb_exemplar.as_int = sdk_exemplar.value + else: + raise ValueError("Exemplar value must be an int or float") + pb_exemplars.append(pb_exemplar) + + return pb_exemplars diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c73ddbc Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py new file mode 100644 index 0000000..af4ad6c --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py @@ -0,0 +1,175 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import logging +from collections import defaultdict +from collections.abc import Sequence + +from opentelemetry.exporter.otlp.proto.common._internal import ( + _encode_attributes, + _encode_instrumentation_scope, + _encode_resource, + _encode_span_id, + _encode_trace_id, +) +from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ( + ExportTraceServiceRequest as PB2ExportTraceServiceRequest, +) +from opentelemetry.proto.trace.v1.trace_pb2 import ( + ResourceSpans as PB2ResourceSpans, +) +from opentelemetry.proto.trace.v1.trace_pb2 import ScopeSpans as PB2ScopeSpans +from opentelemetry.proto.trace.v1.trace_pb2 import Span as PB2SPan +from opentelemetry.proto.trace.v1.trace_pb2 import SpanFlags as PB2SpanFlags +from opentelemetry.proto.trace.v1.trace_pb2 import Status as PB2Status +from opentelemetry.sdk.trace import Event, ReadableSpan +from opentelemetry.trace import Link, SpanKind +from opentelemetry.trace.span import SpanContext, Status, TraceState + +# pylint: disable=E1101 +_SPAN_KIND_MAP = { + SpanKind.INTERNAL: PB2SPan.SpanKind.SPAN_KIND_INTERNAL, + SpanKind.SERVER: PB2SPan.SpanKind.SPAN_KIND_SERVER, + SpanKind.CLIENT: PB2SPan.SpanKind.SPAN_KIND_CLIENT, + SpanKind.PRODUCER: PB2SPan.SpanKind.SPAN_KIND_PRODUCER, + SpanKind.CONSUMER: PB2SPan.SpanKind.SPAN_KIND_CONSUMER, +} + +_logger = logging.getLogger(__name__) + + +def encode_spans( + sdk_spans: Sequence[ReadableSpan], +) -> PB2ExportTraceServiceRequest: + return PB2ExportTraceServiceRequest(resource_spans=_encode_resource_spans(sdk_spans)) + + +def _encode_resource_spans( + sdk_spans: Sequence[ReadableSpan], +) -> list[PB2ResourceSpans]: + # We need to inspect the spans and group + structure them as: + # + # Resource + # Instrumentation Library + # Spans + # + # First loop organizes the SDK spans in this structure. Protobuf messages + # are not hashable so we stick with SDK data in this phase. + # + # Second loop encodes the data into Protobuf format. + # + sdk_resource_spans = defaultdict(lambda: defaultdict(list)) + + for sdk_span in sdk_spans: + sdk_resource = sdk_span.resource + sdk_instrumentation = sdk_span.instrumentation_scope or None + pb2_span = _encode_span(sdk_span) + + sdk_resource_spans[sdk_resource][sdk_instrumentation].append(pb2_span) + + pb2_resource_spans = [] + + for sdk_resource, sdk_instrumentations in sdk_resource_spans.items(): + scope_spans = [] + for sdk_instrumentation, pb2_spans in sdk_instrumentations.items(): + scope_spans.append( + PB2ScopeSpans( + scope=(_encode_instrumentation_scope(sdk_instrumentation)), + spans=pb2_spans, + schema_url=sdk_instrumentation.schema_url if sdk_instrumentation else None, + ) + ) + pb2_resource_spans.append( + PB2ResourceSpans( + resource=_encode_resource(sdk_resource), + scope_spans=scope_spans, + schema_url=sdk_resource.schema_url, + ) + ) + + return pb2_resource_spans + + +def _span_flags(parent_span_context: SpanContext | None) -> int: + flags = PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK + if parent_span_context and parent_span_context.is_remote: + flags |= PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK + return flags + + +def _encode_span(sdk_span: ReadableSpan) -> PB2SPan: + span_context = sdk_span.get_span_context() + return PB2SPan( + trace_id=_encode_trace_id(span_context.trace_id), + span_id=_encode_span_id(span_context.span_id), + trace_state=_encode_trace_state(span_context.trace_state), + parent_span_id=_encode_parent_id(sdk_span.parent), + name=sdk_span.name, + kind=_SPAN_KIND_MAP[sdk_span.kind], + start_time_unix_nano=sdk_span.start_time, + end_time_unix_nano=sdk_span.end_time, + attributes=_encode_attributes(sdk_span.attributes), + events=_encode_events(sdk_span.events), + links=_encode_links(sdk_span.links), + status=_encode_status(sdk_span.status), + dropped_attributes_count=sdk_span.dropped_attributes, + dropped_events_count=sdk_span.dropped_events, + dropped_links_count=sdk_span.dropped_links, + flags=_span_flags(sdk_span.parent), + ) + + +def _encode_events( + events: Sequence[Event], +) -> list[PB2SPan.Event] | None: + pb2_events = None + if events: + pb2_events = [] + for event in events: + encoded_event = PB2SPan.Event( + name=event.name, + time_unix_nano=event.timestamp, + attributes=_encode_attributes(event.attributes), + dropped_attributes_count=event.dropped_attributes, + ) + pb2_events.append(encoded_event) + return pb2_events + + +def _encode_links(links: Sequence[Link]) -> Sequence[PB2SPan.Link]: + pb2_links = None + if links: + pb2_links = [] + for link in links: + encoded_link = PB2SPan.Link( + trace_id=_encode_trace_id(link.context.trace_id), + span_id=_encode_span_id(link.context.span_id), + attributes=_encode_attributes(link.attributes), + dropped_attributes_count=link.dropped_attributes, + flags=_span_flags(link.context), + ) + pb2_links.append(encoded_link) + return pb2_links + + +def _encode_status(status: Status) -> PB2Status | None: + pb2_status = None + if status is not None: + pb2_status = PB2Status( + code=status.status_code.value, + message=status.description, + ) + return pb2_status + + +def _encode_trace_state(trace_state: TraceState) -> str | None: + pb2_trace_state = None + if trace_state is not None: + pb2_trace_state = ",".join([f"{key}={value}" for key, value in (trace_state.items())]) + return pb2_trace_state + + +def _encode_parent_id(context: SpanContext | None) -> bytes | None: + if context: + return _encode_span_id(context.span_id) + return None diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f793272 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_log_encoder.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_log_encoder.py new file mode 100644 index 0000000..e55fa05 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/_log_encoder.py @@ -0,0 +1,9 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from opentelemetry.exporter.otlp.proto.common._internal._log_encoder import ( + encode_logs, +) + +__all__ = ["encode_logs"] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/metrics_encoder.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/metrics_encoder.py new file mode 100644 index 0000000..be1c83a --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/metrics_encoder.py @@ -0,0 +1,9 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from opentelemetry.exporter.otlp.proto.common._internal.metrics_encoder import ( + encode_metrics, +) + +__all__ = ["encode_metrics"] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/trace_encoder.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/trace_encoder.py new file mode 100644 index 0000000..590ea7d --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/trace_encoder.py @@ -0,0 +1,9 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from opentelemetry.exporter.otlp.proto.common._internal.trace_encoder import ( + encode_spans, +) + +__all__ = ["encode_spans"] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/version/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/version/__init__.py new file mode 100644 index 0000000..178452f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/version/__init__.py @@ -0,0 +1,4 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +__version__ = "1.45.0" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/version/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/version/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b6edecb Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/common/version/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/__init__.py new file mode 100644 index 0000000..69f51ec --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/__init__.py @@ -0,0 +1,75 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +""" +This library allows to export tracing data to an OTLP collector. + +Usage +----- + +The **OTLP Span Exporter** allows to export `OpenTelemetry`_ traces to the +`OTLP`_ collector. + +You can configure the exporter with the following environment variables: + +- :envvar:`OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` +- :envvar:`OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` +- :envvar:`OTEL_EXPORTER_OTLP_TRACES_HEADERS` +- :envvar:`OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` +- :envvar:`OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` +- :envvar:`OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE` +- :envvar:`OTEL_EXPORTER_OTLP_TIMEOUT` +- :envvar:`OTEL_EXPORTER_OTLP_PROTOCOL` +- :envvar:`OTEL_EXPORTER_OTLP_HEADERS` +- :envvar:`OTEL_EXPORTER_OTLP_ENDPOINT` +- :envvar:`OTEL_EXPORTER_OTLP_COMPRESSION` +- :envvar:`OTEL_EXPORTER_OTLP_CERTIFICATE` + +.. _OTLP: https://github.com/open-telemetry/opentelemetry-collector/ +.. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/ + +.. code:: python + + from opentelemetry import trace + from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter + from opentelemetry.sdk.resources import Resource + from opentelemetry.sdk.trace import TracerProvider + from opentelemetry.sdk.trace.export import BatchSpanProcessor + + # Resource can be required for some backends, e.g. Jaeger + # If resource wouldn't be set - traces wouldn't appears in Jaeger + resource = Resource.create({ + "service.name": "service" + }) + + trace.set_tracer_provider(TracerProvider(resource=resource)) + tracer = trace.get_tracer(__name__) + + otlp_exporter = OTLPSpanExporter() + + span_processor = BatchSpanProcessor(otlp_exporter) + + trace.get_tracer_provider().add_span_processor(span_processor) + + with tracer.start_as_current_span("foo"): + print("Hello world!") + +API +--- +""" + +import enum + +from .version import __version__ + +_OTLP_HTTP_HEADERS = { + "Content-Type": "application/x-protobuf", + "User-Agent": "OTel-OTLP-Exporter-Python/" + __version__, +} + + +class Compression(enum.Enum): + NoCompression = "none" + Deflate = "deflate" + Gzip = "gzip" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b0ca337 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/_common/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/_common/__init__.py new file mode 100644 index 0000000..31bc650 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/_common/__init__.py @@ -0,0 +1,219 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import logging +import os +from collections.abc import Mapping +from os import environ +from typing import TYPE_CHECKING, Literal + +from opentelemetry.exporter.http.transport._requests import ( + RequestsHTTPTransport, +) +from opentelemetry.exporter.http.transport._urllib3 import ( + Urllib3HTTPTransport, +) +from opentelemetry.exporter.otlp.common import http as _http +from opentelemetry.exporter.otlp.proto.http import ( + _OTLP_HTTP_HEADERS, + Compression, +) +from opentelemetry.sdk.environment_variables import ( + _OTEL_PYTHON_EXPORTER_OTLP_HTTP_CREDENTIAL_PROVIDER, + OTEL_EXPORTER_OTLP_CERTIFICATE, + OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, + OTEL_EXPORTER_OTLP_CLIENT_KEY, + OTEL_EXPORTER_OTLP_COMPRESSION, + OTEL_EXPORTER_OTLP_ENDPOINT, + OTEL_EXPORTER_OTLP_HEADERS, + OTEL_EXPORTER_OTLP_TIMEOUT, +) +from opentelemetry.util._importlib_metadata import entry_points +from opentelemetry.util.re import parse_env_headers + +if TYPE_CHECKING: + import requests + + from opentelemetry.exporter.http.transport._base import BaseHTTPTransport + + _CredentialEnvVar = Literal[ + "OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER", + "OTEL_PYTHON_EXPORTER_OTLP_HTTP_TRACES_CREDENTIAL_PROVIDER", + "OTEL_PYTHON_EXPORTER_OTLP_HTTP_METRICS_CREDENTIAL_PROVIDER", + ] + +_logger = logging.getLogger(__name__) + +_DEFAULT_ENDPOINT = "http://localhost:4318" +_DEFAULT_TIMEOUT = 10 + +# 64 MiB, in bytes. +_DEFAULT_MAX_REQUEST_SIZE = 64 * 1024 * 1024 + + +class RequestPayloadTooLargeError(Exception): + """A serialized OTLP request exceeded the configured ``max_request_size``. + + The class name is emitted as the ``error.type`` attribute on the exporter's + failed-export metric, so renaming it changes observable telemetry. + """ + + +def _is_request_too_large(serialized_data: bytes, max_request_size: int) -> bool: + """Return True if the serialized request exceeds a positive size limit. + + The size is measured on the uncompressed serialized request, matching the + OTLP specification's "before compression" request-size limit. A + ``max_request_size`` of ``0`` (or any non-positive value) disables the + check. + """ + return 0 < max_request_size < len(serialized_data) + + +def _load_session_from_envvar( + cred_envvar: _CredentialEnvVar, +) -> requests.Session | None: + _credential_env = environ.get(_OTEL_PYTHON_EXPORTER_OTLP_HTTP_CREDENTIAL_PROVIDER) or environ.get(cred_envvar) + if _credential_env: + try: + # pylint: disable-next=import-outside-toplevel + import requests # noqa: PLC0415 + except ImportError as exc: + raise ImportError( + "The 'requests' package is required to load a credential " + "provider session but is not installed. Install it with " + "`pip install opentelemetry-exporter-otlp-proto-http[requests]` " + "or `pip install requests`." + ) from exc + + try: + maybe_session = next( + iter( + entry_points( + group="opentelemetry_otlp_credential_provider", + name=_credential_env, + ) + ) + ).load()() + except StopIteration: + raise RuntimeError( + f"Requested component '{_credential_env}' not found in " + f"entry point 'opentelemetry_otlp_credential_provider'" + ) + if isinstance(maybe_session, requests.Session): + return maybe_session + else: + raise RuntimeError( + f"Requested component '{_credential_env}' is of type {type(maybe_session)}" + f" must be of type `requests.Session`." + ) + return None + + +def _normalize_compression( + compression: Compression | _http.Compression | None, +) -> _http.Compression | None: + match compression: + case Compression.NoCompression: + return _http.Compression.NONE + case Compression.Deflate: + return _http.Compression.DEFLATE + case Compression.Gzip: + return _http.Compression.GZIP + case _: + return compression + + +def _resolve_endpoint( + endpoint_env_var: str, + default_path: Literal["v1/traces", "v1/metrics", "v1/logs"], +) -> str: + if endpoint := os.environ.get(endpoint_env_var): + return endpoint + + base_endpoint = os.environ.get(OTEL_EXPORTER_OTLP_ENDPOINT) or _DEFAULT_ENDPOINT + + return f"{base_endpoint.removesuffix('/')}/{default_path}" + + +def _resolve_headers( + headers: Mapping[str, str] | None, + headers_env_var: str, +) -> dict[str, str]: + headers_ = {k.lower(): v for k, v in _OTLP_HTTP_HEADERS.items()} + env_headers = parse_env_headers( + os.environ.get(headers_env_var) or os.environ.get(OTEL_EXPORTER_OTLP_HEADERS, ""), + liberal=True, + ) + headers_.update(env_headers) + if headers: + headers_.update({key.lower(): value for key, value in headers.items()}) + return headers_ + + +def _resolve_timeout( + timeout_env_var: str, +) -> float: + raw = os.environ.get(timeout_env_var) or os.environ.get(OTEL_EXPORTER_OTLP_TIMEOUT) or _DEFAULT_TIMEOUT + + try: + return float(raw) + except ValueError: + _logger.warning( + "Invalid timeout value %r, using default of %s seconds", + raw, + _DEFAULT_TIMEOUT, + ) + return float(_DEFAULT_TIMEOUT) + + +def _resolve_compression(compression_env_var: str) -> _http.Compression: + value = ( + (os.environ.get(compression_env_var) or os.environ.get(OTEL_EXPORTER_OTLP_COMPRESSION) or "none") + .lower() + .strip() + ) + + try: + return _http.Compression.from_str(value) + except ValueError: + _logger.warning("Unsupported compression type: %s", value) + return _http.Compression.NONE + + +def _build_transport( + certificate_file: str | None, + client_key_file: str | None, + client_certificate_file: str | None, + certificate_env_var: str, + client_key_env_var: str, + client_certificate_env_var: str, + session: requests.Session | None, +) -> BaseHTTPTransport: + verify: bool | str = ( + certificate_file + or os.environ.get(certificate_env_var) + or os.environ.get(OTEL_EXPORTER_OTLP_CERTIFICATE) + or True + ) + client_key_file = ( + client_key_file or os.environ.get(client_key_env_var) or os.environ.get(OTEL_EXPORTER_OTLP_CLIENT_KEY) + ) + client_certificate_file = ( + client_certificate_file + or os.environ.get(client_certificate_env_var) + or os.environ.get(OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE) + ) + cert = ( + (client_certificate_file, client_key_file) + if client_certificate_file and client_key_file + else client_certificate_file + ) + + return ( + RequestsHTTPTransport(verify=verify, cert=cert, session=session) + if session + else Urllib3HTTPTransport(verify=verify, cert=cert) + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/_common/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/_common/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4d7b670 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/_common/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py new file mode 100644 index 0000000..69e7d43 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py @@ -0,0 +1,223 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import logging +import os +from collections.abc import Mapping, Sequence +from typing import TYPE_CHECKING, overload + +from opentelemetry.exporter.otlp.common import http as _http +from opentelemetry.exporter.otlp.common._exporter_metrics import ( + create_exporter_metrics, +) +from opentelemetry.exporter.otlp.proto.common._log_encoder import encode_logs +from opentelemetry.exporter.otlp.proto.http import Compression +from opentelemetry.exporter.otlp.proto.http._common import ( + _DEFAULT_MAX_REQUEST_SIZE, + RequestPayloadTooLargeError, + _build_transport, + _is_request_too_large, + _load_session_from_envvar, + _normalize_compression, + _resolve_compression, + _resolve_endpoint, + _resolve_headers, + _resolve_timeout, +) +from opentelemetry.metrics import MeterProvider +from opentelemetry.sdk._logs import ReadableLogRecord +from opentelemetry.sdk._logs.export import ( + LogRecordExporter, + LogRecordExportResult, +) +from opentelemetry.sdk._shared_internal import DuplicateFilter +from opentelemetry.sdk.environment_variables import ( + _OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER, + OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE, + OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE, + OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY, + OTEL_EXPORTER_OTLP_LOGS_COMPRESSION, + OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, + OTEL_EXPORTER_OTLP_LOGS_HEADERS, + OTEL_EXPORTER_OTLP_LOGS_TIMEOUT, + OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, +) +from opentelemetry.semconv._incubating.attributes.otel_attributes import ( + OtelComponentTypeValues, +) +from opentelemetry.semconv.attributes.http_attributes import ( + HTTP_RESPONSE_STATUS_CODE, +) + +if TYPE_CHECKING: + import requests + + from opentelemetry.exporter.http.transport._base import BaseHTTPTransport + +_logger = logging.getLogger(__name__) +# This prevents logs generated when a log fails to be written to generate another log which fails to be written etc. etc. +_logger.addFilter(DuplicateFilter()) + + +DEFAULT_COMPRESSION = Compression.NoCompression +DEFAULT_ENDPOINT = "http://localhost:4318/" +DEFAULT_LOGS_EXPORT_PATH = "v1/logs" +DEFAULT_TIMEOUT = 10 # in seconds + + +class OTLPLogExporter(LogRecordExporter): + @overload + def __init__( + self, + endpoint: str | None = None, + certificate_file: str | None = None, + client_key_file: str | None = None, + client_certificate_file: str | None = None, + headers: Mapping[str, str] | None = None, + timeout: float | None = None, + compression: Compression | _http.Compression | None = None, + session: requests.Session | None = None, + *, + max_request_size: int | None = None, + meter_provider: MeterProvider | None = None, + ) -> None: ... + + @overload + def __init__( + self, + endpoint: str | None = None, + certificate_file: None = None, + client_key_file: None = None, + client_certificate_file: None = None, + headers: Mapping[str, str] | None = None, + timeout: float | None = None, + compression: Compression | _http.Compression | None = None, + session: requests.Session | None = None, + *, + max_request_size: int | None = None, + meter_provider: MeterProvider | None = None, + _transport: BaseHTTPTransport, + ) -> None: ... + + def __init__( + self, + endpoint: str | None = None, + certificate_file: str | None = None, + client_key_file: str | None = None, + client_certificate_file: str | None = None, + headers: Mapping[str, str] | None = None, + timeout: float | None = None, + compression: Compression | _http.Compression | None = None, + session: requests.Session | None = None, + *, + max_request_size: int | None = None, + meter_provider: MeterProvider | None = None, + _transport: BaseHTTPTransport | None = None, + ) -> None: + """OTLP HTTP log exporter. + + Args: + endpoint: Full URL of the OTLP/HTTP signal endpoint, including the signal + path. Example: ``http://collector:4318/v1/logs``. For a base URL without a + signal path, set the ``OTEL_EXPORTER_OTLP_ENDPOINT`` environment variable. + certificate_file: Path to the CA certificate file for TLS. + client_key_file: Path to the client key file for mTLS. + client_certificate_file: Path to the client certificate file for mTLS. + headers: Headers to send with each export request. + timeout: Timeout in seconds for each export request. + compression: Compression to use; one of none, gzip, deflate. + session: Requests session to use at export. + max_request_size: Maximum size in bytes of a serialized request, + measured before compression. A request exceeding this size is + dropped before being sent. Defaults to 64 MiB; a value of 0 (or + any non-positive value) disables the limit. Batch processors + group log records by count rather than serialized size, so a + batch whose serialized request exceeds this limit is dropped as + a whole and recorded as a failed export; reduce the processor's + ``max_export_batch_size`` (or raise/disable this limit) if + batches may approach it. + meter_provider: MeterProvider used for the exporter's own metrics. + """ + self._endpoint = endpoint or _resolve_endpoint(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, DEFAULT_LOGS_EXPORT_PATH) + self._compression = _normalize_compression(compression) or _resolve_compression( + OTEL_EXPORTER_OTLP_LOGS_COMPRESSION + ) + transport = _transport or _build_transport( + certificate_file, + client_key_file, + client_certificate_file, + OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE, + OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY, + OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE, + session=session or _load_session_from_envvar(_OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER), + ) + self._max_request_size = _DEFAULT_MAX_REQUEST_SIZE if max_request_size is None else max_request_size + self._client = _http._OTLPHTTPClient( + transport=transport, + endpoint=self._endpoint, + kind="logs", + timeout=timeout if timeout is not None else _resolve_timeout(OTEL_EXPORTER_OTLP_LOGS_TIMEOUT), + compression=self._compression, + headers=_resolve_headers(headers, OTEL_EXPORTER_OTLP_LOGS_HEADERS), + logger=_logger, + ) + self._shutdown = False + + self._metrics = create_exporter_metrics( + OtelComponentTypeValues.OTLP_HTTP_LOG_EXPORTER, + "logs", + self._endpoint, + meter_provider, + os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "").strip().lower() == "true", + ) + + def export(self, batch: Sequence[ReadableLogRecord]) -> LogRecordExportResult: + if self._shutdown: + _logger.warning("Exporter already shutdown, ignoring batch") + return LogRecordExportResult.FAILURE + + with self._metrics.export_operation(len(batch)) as result: + try: + serialized_data = encode_logs(batch).SerializeToString() + # pylint: disable-next=broad-exception-caught + except Exception as error: + _logger.error("Failed to encode logs batch: %s", error) + result.error = error + return LogRecordExportResult.FAILURE + + if _is_request_too_large(serialized_data, self._max_request_size): + _logger.warning( + "Dropping logs batch: serialized size %d bytes exceeds max_request_size %d bytes.", + len(serialized_data), + self._max_request_size, + ) + result.error = RequestPayloadTooLargeError( + f"Serialized logs request size {len(serialized_data)} " + f"bytes exceeds max_request_size " + f"{self._max_request_size} bytes." + ) + return LogRecordExportResult.FAILURE + + export_result = self._client.export(serialized_data) + if not export_result.success: + result.error = export_result.error + result.error_attrs = ( + {HTTP_RESPONSE_STATUS_CODE: export_result.status_code} + if export_result.status_code is not None + else None + ) + return LogRecordExportResult.FAILURE + return LogRecordExportResult.SUCCESS + + def force_flush(self, timeout_millis: int = 10_000) -> bool: + """Nothing is buffered in this exporter, so this method does nothing.""" + return True + + def shutdown(self): + if self._shutdown: + _logger.warning("Exporter already shutdown, ignoring call") + return + self._shutdown = True + self._client.shutdown() diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/_log_exporter/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/_log_exporter/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b4a9db8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/_log_exporter/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/metric_exporter/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/metric_exporter/__init__.py new file mode 100644 index 0000000..0509696 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/metric_exporter/__init__.py @@ -0,0 +1,611 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import logging +import os +from collections.abc import Callable, Iterable, Mapping +from typing import ( # noqa: F401 + TYPE_CHECKING, + Any, + Optional, + overload, +) + +from typing_extensions import deprecated + +from opentelemetry.exporter.otlp.common import http as _http +from opentelemetry.exporter.otlp.common._aggregation import ( + _get_aggregation, + _get_temporality, +) +from opentelemetry.exporter.otlp.common._exporter_metrics import ( + create_exporter_metrics, +) +from opentelemetry.exporter.otlp.proto.common._internal import ( + _get_resource_data, +) +from opentelemetry.exporter.otlp.proto.common.metrics_encoder import ( + encode_metrics, +) +from opentelemetry.exporter.otlp.proto.http import Compression +from opentelemetry.exporter.otlp.proto.http._common import ( + _DEFAULT_MAX_REQUEST_SIZE, + RequestPayloadTooLargeError, + _build_transport, + _is_request_too_large, + _load_session_from_envvar, + _normalize_compression, + _resolve_compression, + _resolve_endpoint, + _resolve_headers, + _resolve_timeout, +) +from opentelemetry.metrics import MeterProvider +from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import ( + ExportMetricsServiceRequest, +) +from opentelemetry.proto.common.v1.common_pb2 import ( # noqa: F401 + AnyValue, + ArrayValue, + InstrumentationScope, + KeyValue, + KeyValueList, +) +from opentelemetry.proto.metrics.v1 import metrics_pb2 as pb2 +from opentelemetry.proto.resource.v1.resource_pb2 import Resource # noqa: F401 +from opentelemetry.proto.resource.v1.resource_pb2 import ( + Resource as PB2Resource, +) +from opentelemetry.sdk.environment_variables import ( + _OTEL_PYTHON_EXPORTER_OTLP_HTTP_METRICS_CREDENTIAL_PROVIDER, + OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE, + OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE, + OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY, + OTEL_EXPORTER_OTLP_METRICS_COMPRESSION, + OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, + OTEL_EXPORTER_OTLP_METRICS_HEADERS, + OTEL_EXPORTER_OTLP_METRICS_TIMEOUT, + OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, +) +from opentelemetry.sdk.metrics._internal.aggregation import Aggregation +from opentelemetry.sdk.metrics.export import ( # noqa: F401 + AggregationTemporality, + Gauge, + MetricExporter, + MetricExportResult, + MetricsData, + Sum, +) +from opentelemetry.sdk.metrics.export import ( # noqa: F401 + Histogram as HistogramType, +) +from opentelemetry.sdk.resources import Resource as SDKResource +from opentelemetry.semconv._incubating.attributes.otel_attributes import ( + OtelComponentTypeValues, +) +from opentelemetry.semconv.attributes.http_attributes import ( + HTTP_RESPONSE_STATUS_CODE, +) + +if TYPE_CHECKING: + import requests + + from opentelemetry.exporter.http.transport._base import BaseHTTPTransport + +_logger = logging.getLogger(__name__) + + +DEFAULT_COMPRESSION = Compression.NoCompression +DEFAULT_ENDPOINT = "http://localhost:4318/" +DEFAULT_METRICS_EXPORT_PATH = "v1/metrics" +DEFAULT_TIMEOUT = 10 # in seconds + + +class OTLPMetricExporter(MetricExporter): + @overload + def __init__( + self, + endpoint: str | None = None, + certificate_file: str | None = None, + client_key_file: str | None = None, + client_certificate_file: str | None = None, + headers: Mapping[str, str] | None = None, + timeout: float | None = None, + compression: Compression | _http.Compression | None = None, + session: requests.Session | None = None, + preferred_temporality: dict[type, AggregationTemporality] | None = None, + preferred_aggregation: dict[type, Aggregation] | None = None, + max_export_batch_size: int | None = None, + *, + max_request_size: int | None = None, + meter_provider: MeterProvider | None = None, + ) -> None: ... + + @overload + def __init__( + self, + endpoint: str | None = None, + certificate_file: None = None, + client_key_file: None = None, + client_certificate_file: None = None, + headers: Mapping[str, str] | None = None, + timeout: float | None = None, + compression: Compression | _http.Compression | None = None, + session: requests.Session | None = None, + preferred_temporality: dict[type, AggregationTemporality] | None = None, + preferred_aggregation: dict[type, Aggregation] | None = None, + max_export_batch_size: int | None = None, + *, + max_request_size: int | None = None, + meter_provider: MeterProvider | None = None, + _transport: BaseHTTPTransport, + ) -> None: ... + + def __init__( + self, + endpoint: str | None = None, + certificate_file: str | None = None, + client_key_file: str | None = None, + client_certificate_file: str | None = None, + headers: Mapping[str, str] | None = None, + timeout: float | None = None, + compression: Compression | _http.Compression | None = None, + session: requests.Session | None = None, + preferred_temporality: dict[type, AggregationTemporality] | None = None, + preferred_aggregation: dict[type, Aggregation] | None = None, + max_export_batch_size: int | None = None, + *, + max_request_size: int | None = None, + meter_provider: MeterProvider | None = None, + _transport: BaseHTTPTransport | None = None, + ) -> None: + """OTLP HTTP metrics exporter + + Args: + endpoint: Full URL of the OTLP/HTTP signal endpoint, including the signal + path. Example: ``http://collector:4318/v1/metrics``. For a base URL without a + signal path, set the ``OTEL_EXPORTER_OTLP_ENDPOINT`` environment variable. + certificate_file: Path to the certificate file to use for any TLS + client_key_file: Path to the client key file to use for any TLS + client_certificate_file: Path to the client certificate file to use for any TLS + headers: Headers to be sent with HTTP requests at export + timeout: Timeout in seconds for export + compression: Compression to use; one of none, gzip, deflate + session: Requests session to use at export + preferred_temporality: Map of preferred temporality for each metric type. + See `opentelemetry.sdk.metrics.export.MetricReader` for more details on what + preferred temporality is. + preferred_aggregation: Map of preferred aggregation for each metric type. + See `opentelemetry.sdk.metrics.export.MetricReader` for more details on what + preferred aggregation is. + max_export_batch_size: Maximum number of data points to export in a single request. + If not set there is no limit to the number of data points in a request. + If it is set and the number of data points exceeds the max, the request will be split. + max_request_size: Maximum size in bytes of a serialized request, measured before + compression. A request exceeding this size is dropped before being sent. Defaults + to 64 MiB; a value of 0 (or any non-positive value) disables the limit. Requests + are sized after any ``max_export_batch_size`` splitting; a batch whose serialized + request still exceeds this limit is dropped as a whole and recorded as a failed + export. Reduce ``max_export_batch_size`` (or raise/disable this limit) if batches + may approach it. + meter_provider: MeterProvider used for the exporter's own metrics. + """ + MetricExporter.__init__( + self, + preferred_temporality=_get_temporality(preferred_temporality), + preferred_aggregation=_get_aggregation(preferred_aggregation), + ) + self._endpoint = endpoint or _resolve_endpoint(OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, DEFAULT_METRICS_EXPORT_PATH) + self._compression = _normalize_compression(compression) or _resolve_compression( + OTEL_EXPORTER_OTLP_METRICS_COMPRESSION + ) + transport = _transport or _build_transport( + certificate_file, + client_key_file, + client_certificate_file, + OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE, + OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY, + OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE, + session=session or _load_session_from_envvar(_OTEL_PYTHON_EXPORTER_OTLP_HTTP_METRICS_CREDENTIAL_PROVIDER), + ) + self._client = _http._OTLPHTTPClient( + transport=transport, + endpoint=self._endpoint, + kind="metrics", + timeout=timeout if timeout is not None else _resolve_timeout(OTEL_EXPORTER_OTLP_METRICS_TIMEOUT), + compression=self._compression, + headers=_resolve_headers(headers, OTEL_EXPORTER_OTLP_METRICS_HEADERS), + logger=_logger, + ) + self._max_export_batch_size: int | None = max_export_batch_size + self._max_request_size = _DEFAULT_MAX_REQUEST_SIZE if max_request_size is None else max_request_size + self._shutdown = False + + self._metrics = create_exporter_metrics( + OtelComponentTypeValues.OTLP_HTTP_METRIC_EXPORTER, + "metrics", + self._endpoint, + meter_provider, + os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "").strip().lower() == "true", + ) + + def _export_batch( + self, + export_request: ExportMetricsServiceRequest, + ) -> MetricExportResult: + with self._metrics.export_operation(_count_data_points(export_request)) as result: + serialized_data = export_request.SerializeToString() + if _is_request_too_large(serialized_data, self._max_request_size): + _logger.warning( + "Dropping metrics batch: serialized size %d bytes exceeds max_request_size %d bytes.", + len(serialized_data), + self._max_request_size, + ) + result.error = RequestPayloadTooLargeError( + f"Serialized metrics request size {len(serialized_data)} " + f"bytes exceeds max_request_size " + f"{self._max_request_size} bytes." + ) + return MetricExportResult.FAILURE + export_result = self._client.export(serialized_data) + if not export_result.success: + result.error = export_result.error + result.error_attrs = ( + {HTTP_RESPONSE_STATUS_CODE: export_result.status_code} + if export_result.status_code is not None + else None + ) + return MetricExportResult.FAILURE + return MetricExportResult.SUCCESS + + def export( + self, + metrics_data: MetricsData, + timeout_millis: float | None = 10000, + **kwargs, + ) -> MetricExportResult: + if self._shutdown: + _logger.warning("Exporter already shutdown, ignoring batch") + return MetricExportResult.FAILURE + + try: + export_request = encode_metrics(metrics_data) + # pylint: disable-next=broad-exception-caught + except Exception as error: + _logger.error("Failed to encode metrics batch: %s", error) + return MetricExportResult.FAILURE + + # If no batch size configured, export as single batch with retries as configured + if self._max_export_batch_size is None: + return self._export_batch(export_request) + + for batch in _split_metrics_data(export_request, self._max_export_batch_size): + export_result = self._export_batch(batch) + if export_result != MetricExportResult.SUCCESS: + return MetricExportResult.FAILURE + + # Only returns SUCCESS if all batches succeeded + return MetricExportResult.SUCCESS + + def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None: + if self._shutdown: + _logger.warning("Exporter already shutdown, ignoring call") + return + self._shutdown = True + self._client.shutdown() + + def force_flush(self, timeout_millis: float = 10_000) -> bool: + """Nothing is buffered in this exporter, so this method does nothing.""" + return True + + def set_meter_provider(self, meter_provider: MeterProvider) -> None: + self._metrics = create_exporter_metrics( + OtelComponentTypeValues.OTLP_HTTP_METRIC_EXPORTER, + "metrics", + self._endpoint, + meter_provider, + os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "").strip().lower() == "true", + ) + + +def _count_data_points(export_request: ExportMetricsServiceRequest) -> int: + """Count the number of data points in an encoded metrics export request.""" + count = 0 + for resource_metrics in export_request.resource_metrics: + for scope_metrics in resource_metrics.scope_metrics: + for metric in scope_metrics.metrics: + field_name = metric.WhichOneof("data") + if field_name: + count += len(getattr(metric, field_name).data_points) + return count + + +def _split_metrics_data( + metrics_data: ExportMetricsServiceRequest, + max_export_batch_size: int | None = None, +) -> Iterable[ExportMetricsServiceRequest]: + """Splits metrics data into several ExportMetricsServiceRequest (copies protobuf originals), + based on configured data point max export batch size. + + Args: + metrics_data: metrics object based on HTTP protocol buffer definition + + Returns: + Iterable[ExportMetricsServiceRequest]: An iterable of ExportMetricsServiceRequest objects containing + ExportMetricsServiceRequest.ResourceMetrics, ExportMetricsServiceRequest.ScopeMetrics, ExportMetricsServiceRequest.Metrics, and data points + """ + if not max_export_batch_size: + return metrics_data + + batch_size: int = 0 + # Stores split metrics data as editable references + # used to write batched pb2 objects for export when finalized + split_resource_metrics = [] + + for resource_metrics in metrics_data.resource_metrics: + split_scope_metrics = [] + split_resource_metrics.append( + { + "resource": resource_metrics.resource, + "schema_url": resource_metrics.schema_url, + "scope_metrics": split_scope_metrics, + } + ) + + for scope_metrics in resource_metrics.scope_metrics: + split_metrics = [] + split_scope_metrics.append( + { + "scope": scope_metrics.scope, + "schema_url": scope_metrics.schema_url, + "metrics": split_metrics, + } + ) + + for metric in scope_metrics.metrics: + split_data_points = [] + field_name = metric.WhichOneof("data") + if not field_name: + _logger.warning("Tried to split and export an unsupported metric type. Skipping.") + continue + + # Get data container using field name + # and build metric dictionary dynamically for conciseness + data_container = getattr(metric, field_name) + metric_dict = { + "name": metric.name, + "description": metric.description, + "unit": metric.unit, + field_name: { + "data_points": split_data_points, + }, + } + if hasattr(data_container, "aggregation_temporality"): + metric_dict[field_name]["aggregation_temporality"] = data_container.aggregation_temporality + if hasattr(data_container, "is_monotonic"): + metric_dict[field_name]["is_monotonic"] = data_container.is_monotonic + split_metrics.append(metric_dict) + + current_data_points = data_container.data_points + for data_point in current_data_points: + split_data_points.append(data_point) + batch_size += 1 + + if batch_size >= max_export_batch_size: + yield ExportMetricsServiceRequest( + resource_metrics=_get_split_resource_metrics_pb2(split_resource_metrics) + ) + + # Reset all the reference variables with current metrics_data position + # minus yielded data_points. Need to clear data_points and keep metric + # to avoid duplicate data_point export + batch_size = 0 + split_data_points = [] + + # Rebuild metric dict generically using same approach as initial creation + field_name = metric.WhichOneof("data") + if field_name is None: + _logger.warning("Tried to split and export an unsupported metric type. Skipping.") + continue + data_container = getattr(metric, field_name) + metric_dict = { + "name": metric.name, + "description": metric.description, + "unit": metric.unit, + field_name: { + "data_points": split_data_points, + }, + } + if hasattr(data_container, "aggregation_temporality"): + metric_dict[field_name]["aggregation_temporality"] = data_container.aggregation_temporality + if hasattr(data_container, "is_monotonic"): + metric_dict[field_name]["is_monotonic"] = data_container.is_monotonic + + split_metrics = [metric_dict] + split_scope_metrics = [ + { + "scope": scope_metrics.scope, + "schema_url": scope_metrics.schema_url, + "metrics": split_metrics, + } + ] + split_resource_metrics = [ + { + "resource": resource_metrics.resource, + "schema_url": resource_metrics.schema_url, + "scope_metrics": split_scope_metrics, + } + ] + + if not split_data_points: + # If data_points is empty remove the whole metric + split_metrics.pop() + + if not split_metrics: + # If metrics is empty remove the whole scope_metrics + split_scope_metrics.pop() + + if not split_scope_metrics: + # If scope_metrics is empty remove the whole resource_metrics + split_resource_metrics.pop() + + if batch_size > 0: + yield ExportMetricsServiceRequest(resource_metrics=_get_split_resource_metrics_pb2(split_resource_metrics)) + + +def _get_split_resource_metrics_pb2( + split_resource_metrics: list[dict], +) -> list[pb2.ResourceMetrics]: + """Helper that returns a list of pb2.ResourceMetrics objects based on split_resource_metrics. + Example input: + + ```python + [ + { + "resource": , + "schema_url": "http://foo-bar", + "scope_metrics": [ + "scope": , + "schema_url": "http://foo-baz", + "metrics": [ + { + "name": "apples", + "description": "number of apples purchased", + "sum": { + "aggregation_temporality": 1, + "is_monotonic": "false", + "data_points": [ + { + start_time_unix_nano: 1000 + time_unix_nano: 1001 + exemplars { + time_unix_nano: 1002 + span_id: "foo-span" + trace_id: "foo-trace" + as_int: 5 + } + as_int: 5 + } + ] + } + }, + ], + ], + }, + ] + ``` + + Args: + split_resource_metrics: A list of dict representations of ResourceMetrics, + ScopeMetrics, Metrics, and data points. + + Returns: + List[pb2.ResourceMetrics]: A list of pb2.ResourceMetrics objects containing + pb2.ScopeMetrics, pb2.Metrics, and data points + """ + split_resource_metrics_pb = [] + for resource_metrics in split_resource_metrics: + new_resource_metrics = pb2.ResourceMetrics( + resource=resource_metrics.get("resource"), + scope_metrics=[], + schema_url=resource_metrics.get("schema_url") or "", + ) + for scope_metrics in resource_metrics.get("scope_metrics", []): + new_scope_metrics = pb2.ScopeMetrics( + scope=scope_metrics.get("scope"), + metrics=[], + schema_url=scope_metrics.get("schema_url") or "", + ) + + for metric in scope_metrics.get("metrics", []): + new_metric = None + data_points = [] + + if "sum" in metric: + new_metric = pb2.Metric( + name=metric.get("name"), + description=metric.get("description"), + unit=metric.get("unit"), + sum=pb2.Sum( + data_points=[], + aggregation_temporality=metric.get("sum").get("aggregation_temporality"), + is_monotonic=metric.get("sum").get("is_monotonic"), + ), + ) + data_points = metric.get("sum").get("data_points") + elif "histogram" in metric: + new_metric = pb2.Metric( + name=metric.get("name"), + description=metric.get("description"), + unit=metric.get("unit"), + histogram=pb2.Histogram( + data_points=[], + aggregation_temporality=metric.get("histogram").get("aggregation_temporality"), + ), + ) + data_points = metric.get("histogram").get("data_points") + elif "exponential_histogram" in metric: + new_metric = pb2.Metric( + name=metric.get("name"), + description=metric.get("description"), + unit=metric.get("unit"), + exponential_histogram=pb2.ExponentialHistogram( + data_points=[], + aggregation_temporality=metric.get("exponential_histogram").get("aggregation_temporality"), + ), + ) + data_points = metric.get("exponential_histogram").get("data_points") + elif "gauge" in metric: + new_metric = pb2.Metric( + name=metric.get("name"), + description=metric.get("description"), + unit=metric.get("unit"), + gauge=pb2.Gauge( + data_points=[], + ), + ) + data_points = metric.get("gauge").get("data_points") + elif "summary" in metric: + new_metric = pb2.Metric( + name=metric.get("name"), + description=metric.get("description"), + unit=metric.get("unit"), + summary=pb2.Summary( + data_points=[], + ), + ) + data_points = metric.get("summary").get("data_points") + else: + _logger.warning("Tried to split and export an unsupported metric type. Skipping.") + continue + + # Append data points generically using the field name from the metric dict + for field_name in [ + "sum", + "histogram", + "exponential_histogram", + "gauge", + "summary", + ]: + if field_name in metric: + metric_data_container = getattr(new_metric, field_name) + for data_point in data_points: + metric_data_container.data_points.append(data_point) + break + + new_scope_metrics.metrics.append(new_metric) + new_resource_metrics.scope_metrics.append(new_scope_metrics) + split_resource_metrics_pb.append(new_resource_metrics) + return split_resource_metrics_pb + + +@deprecated( + "Use one of the encoders from opentelemetry-exporter-otlp-proto-common instead. Deprecated since version 1.18.0.", +) +def get_resource_data( + sdk_resource_scope_data: dict[SDKResource, Any], # ResourceDataT? + resource_class: Callable[..., PB2Resource], + name: str, +) -> list[PB2Resource]: + return _get_resource_data(sdk_resource_scope_data, resource_class, name) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/metric_exporter/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/metric_exporter/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a9e180c Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/metric_exporter/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py new file mode 100644 index 0000000..adb6787 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py @@ -0,0 +1,219 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import logging +import os +from collections.abc import Mapping, Sequence +from typing import TYPE_CHECKING, overload + +from opentelemetry.exporter.otlp.common import http as _http +from opentelemetry.exporter.otlp.common._exporter_metrics import ( + create_exporter_metrics, +) +from opentelemetry.exporter.otlp.proto.common.trace_encoder import ( + encode_spans, +) +from opentelemetry.exporter.otlp.proto.http import Compression +from opentelemetry.exporter.otlp.proto.http._common import ( + _DEFAULT_MAX_REQUEST_SIZE, + RequestPayloadTooLargeError, + _build_transport, + _is_request_too_large, + _load_session_from_envvar, + _normalize_compression, + _resolve_compression, + _resolve_endpoint, + _resolve_headers, + _resolve_timeout, +) +from opentelemetry.metrics import MeterProvider +from opentelemetry.sdk.environment_variables import ( + _OTEL_PYTHON_EXPORTER_OTLP_HTTP_TRACES_CREDENTIAL_PROVIDER, + OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE, + OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE, + OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY, + OTEL_EXPORTER_OTLP_TRACES_COMPRESSION, + OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, + OTEL_EXPORTER_OTLP_TRACES_HEADERS, + OTEL_EXPORTER_OTLP_TRACES_TIMEOUT, + OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, +) +from opentelemetry.sdk.trace import ReadableSpan +from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult +from opentelemetry.semconv._incubating.attributes.otel_attributes import ( + OtelComponentTypeValues, +) +from opentelemetry.semconv.attributes.http_attributes import ( + HTTP_RESPONSE_STATUS_CODE, +) + +if TYPE_CHECKING: + import requests + + from opentelemetry.exporter.http.transport._base import BaseHTTPTransport + +_logger = logging.getLogger(__name__) + + +DEFAULT_COMPRESSION = Compression.NoCompression +DEFAULT_ENDPOINT = "http://localhost:4318/" +DEFAULT_TRACES_EXPORT_PATH = "v1/traces" +DEFAULT_TIMEOUT = 10 # in seconds + + +class OTLPSpanExporter(SpanExporter): + @overload + def __init__( + self, + endpoint: str | None = None, + certificate_file: str | None = None, + client_key_file: str | None = None, + client_certificate_file: str | None = None, + headers: Mapping[str, str] | None = None, + timeout: float | None = None, + compression: Compression | _http.Compression | None = None, + session: requests.Session | None = None, + *, + max_request_size: int | None = None, + meter_provider: MeterProvider | None = None, + ) -> None: ... + + @overload + def __init__( + self, + endpoint: str | None = None, + certificate_file: None = None, + client_key_file: None = None, + client_certificate_file: None = None, + headers: Mapping[str, str] | None = None, + timeout: float | None = None, + compression: Compression | _http.Compression | None = None, + session: requests.Session | None = None, + *, + max_request_size: int | None = None, + meter_provider: MeterProvider | None = None, + _transport: BaseHTTPTransport, + ) -> None: ... + + def __init__( + self, + endpoint: str | None = None, + certificate_file: str | None = None, + client_key_file: str | None = None, + client_certificate_file: str | None = None, + headers: Mapping[str, str] | None = None, + timeout: float | None = None, + compression: Compression | _http.Compression | None = None, + session: requests.Session | None = None, + *, + max_request_size: int | None = None, + meter_provider: MeterProvider | None = None, + _transport: BaseHTTPTransport | None = None, + ) -> None: + """OTLP HTTP span exporter. + + Args: + endpoint: Full URL of the OTLP/HTTP signal endpoint, including the signal + path. Example: ``http://collector:4318/v1/traces``. For a base URL without a + signal path, set the ``OTEL_EXPORTER_OTLP_ENDPOINT`` environment variable. + certificate_file: Path to the CA certificate file for TLS. + client_key_file: Path to the client key file for mTLS. + client_certificate_file: Path to the client certificate file for mTLS. + headers: Headers to send with each export request. + timeout: Timeout in seconds for each export request. + compression: Compression to use; one of none, gzip, deflate. + session: Requests session to use at export. + max_request_size: Maximum size in bytes of a serialized request, + measured before compression. A request exceeding this size is + dropped before being sent. Defaults to 64 MiB; a value of 0 (or + any non-positive value) disables the limit. Batch processors + group spans by count rather than serialized size, so a batch + whose serialized request exceeds this limit is dropped as a + whole and recorded as a failed export; reduce the processor's + ``max_export_batch_size`` (or raise/disable this limit) if + batches may approach it. + meter_provider: MeterProvider used for the exporter's own metrics. + """ + self._endpoint = endpoint or _resolve_endpoint(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, DEFAULT_TRACES_EXPORT_PATH) + self._compression = _normalize_compression(compression) or _resolve_compression( + OTEL_EXPORTER_OTLP_TRACES_COMPRESSION + ) + transport = _transport or _build_transport( + certificate_file, + client_key_file, + client_certificate_file, + OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE, + OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY, + OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE, + session=session or _load_session_from_envvar(_OTEL_PYTHON_EXPORTER_OTLP_HTTP_TRACES_CREDENTIAL_PROVIDER), + ) + self._max_request_size = _DEFAULT_MAX_REQUEST_SIZE if max_request_size is None else max_request_size + self._client = _http._OTLPHTTPClient( + transport=transport, + endpoint=self._endpoint, + kind="spans", + timeout=timeout if timeout is not None else _resolve_timeout(OTEL_EXPORTER_OTLP_TRACES_TIMEOUT), + compression=self._compression, + headers=_resolve_headers(headers, OTEL_EXPORTER_OTLP_TRACES_HEADERS), + logger=_logger, + ) + self._shutdown = False + + self._metrics = create_exporter_metrics( + OtelComponentTypeValues.OTLP_HTTP_SPAN_EXPORTER, + "traces", + self._endpoint, + meter_provider, + os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "").strip().lower() == "true", + ) + + def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: + if self._shutdown: + _logger.warning("Exporter already shutdown, ignoring batch") + return SpanExportResult.FAILURE + + with self._metrics.export_operation(len(spans)) as result: + try: + serialized_data = encode_spans(spans).SerializePartialToString() + # pylint: disable-next=broad-exception-caught + except Exception as error: + _logger.error("Failed to encode span batch: %s", error) + result.error = error + return SpanExportResult.FAILURE + + if _is_request_too_large(serialized_data, self._max_request_size): + _logger.warning( + "Dropping span batch: serialized size %d bytes exceeds max_request_size %d bytes.", + len(serialized_data), + self._max_request_size, + ) + result.error = RequestPayloadTooLargeError( + f"Serialized span request size {len(serialized_data)} " + f"bytes exceeds max_request_size " + f"{self._max_request_size} bytes." + ) + return SpanExportResult.FAILURE + + export_result = self._client.export(serialized_data) + if not export_result.success: + result.error = export_result.error + result.error_attrs = ( + {HTTP_RESPONSE_STATUS_CODE: export_result.status_code} + if export_result.status_code is not None + else None + ) + return SpanExportResult.FAILURE + return SpanExportResult.SUCCESS + + def shutdown(self): + if self._shutdown: + _logger.warning("Exporter already shutdown, ignoring call") + return + self._shutdown = True + self._client.shutdown() + + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Nothing is buffered in this exporter, so this method does nothing.""" + return True diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/trace_exporter/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/trace_exporter/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..bd199a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/trace_exporter/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py new file mode 100644 index 0000000..252dab8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py @@ -0,0 +1,55 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import logging # noqa: F401 +from collections import abc # noqa: F401 +from collections.abc import Sequence # noqa: F401 + +from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ( # noqa: F401 + ExportTraceServiceRequest as PB2ExportTraceServiceRequest, +) +from opentelemetry.proto.common.v1.common_pb2 import ( # noqa: F401 + AnyValue as PB2AnyValue, +) +from opentelemetry.proto.common.v1.common_pb2 import ( # noqa: F401 + ArrayValue as PB2ArrayValue, +) +from opentelemetry.proto.common.v1.common_pb2 import ( # noqa: F401 + InstrumentationScope as PB2InstrumentationScope, +) +from opentelemetry.proto.common.v1.common_pb2 import ( # noqa: F401 + KeyValue as PB2KeyValue, +) +from opentelemetry.proto.resource.v1.resource_pb2 import ( # noqa: F401 + Resource as PB2Resource, +) +from opentelemetry.proto.trace.v1.trace_pb2 import ( # noqa: F401 + ResourceSpans as PB2ResourceSpans, +) +from opentelemetry.proto.trace.v1.trace_pb2 import ( # noqa: F401 + ScopeSpans as PB2ScopeSpans, +) +from opentelemetry.proto.trace.v1.trace_pb2 import ( # noqa: F401 + Span as PB2SPan, +) +from opentelemetry.proto.trace.v1.trace_pb2 import ( # noqa: F401 + Status as PB2Status, +) +from opentelemetry.sdk.trace import ( + Event, # noqa: F401 + Resource, # noqa: F401 +) +from opentelemetry.sdk.trace import Span as SDKSpan # noqa: F401 +from opentelemetry.sdk.util.instrumentation import ( # noqa: F401 + InstrumentationScope, +) +from opentelemetry.trace import ( + Link, # noqa: F401 + SpanKind, # noqa: F401 +) +from opentelemetry.trace.span import ( # noqa: F401 + SpanContext, + Status, + TraceState, +) +from opentelemetry.util.types import Attributes # noqa: F401 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..41a71e5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/version/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/version/__init__.py new file mode 100644 index 0000000..178452f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/version/__init__.py @@ -0,0 +1,4 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +__version__ = "1.45.0" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/version/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/version/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..52a525b Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/exporter/otlp/proto/http/version/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/_semconv.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/_semconv.cpython-312.pyc new file mode 100644 index 0000000..90163fb Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/_semconv.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/bootstrap.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/bootstrap.cpython-312.pyc new file mode 100644 index 0000000..2c614e7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/bootstrap.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/bootstrap_gen.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/bootstrap_gen.cpython-312.pyc new file mode 100644 index 0000000..59cd685 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/bootstrap_gen.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/cidict.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/cidict.cpython-312.pyc new file mode 100644 index 0000000..d7a9e7b Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/cidict.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/dependencies.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/dependencies.cpython-312.pyc new file mode 100644 index 0000000..1dc018d Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/dependencies.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/distro.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/distro.cpython-312.pyc new file mode 100644 index 0000000..5053203 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/distro.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/environment_variables.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/environment_variables.cpython-312.pyc new file mode 100644 index 0000000..e286dea Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/environment_variables.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/instrumentor.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/instrumentor.cpython-312.pyc new file mode 100644 index 0000000..c99dd16 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/instrumentor.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/log_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/log_utils.cpython-312.pyc new file mode 100644 index 0000000..e72377d Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/log_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/propagators.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/propagators.cpython-312.pyc new file mode 100644 index 0000000..9bf1e1f Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/propagators.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/sqlcommenter_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/sqlcommenter_utils.cpython-312.pyc new file mode 100644 index 0000000..8ea4978 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/sqlcommenter_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..7961f3a Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/version.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/version.cpython-312.pyc new file mode 100644 index 0000000..6d99d5d Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/__pycache__/version.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_labeler/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_labeler/__init__.py new file mode 100644 index 0000000..a27f341 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_labeler/__init__.py @@ -0,0 +1,88 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +""" +OpenTelemetry Labeler +===================== + +The labeler utility provides a way to add custom attributes to metrics. + +This was inspired by OpenTelemetry Go's net/http instrumentation Labeler +https://github.com/open-telemetry/opentelemetry-go-contrib/pull/306 + +Usage +----- + +The labeler is typically used within the context of an instrumented request +or operation. Use ``get_labeler`` to obtain a labeler instance for the +current context, then add attributes using the ``add`` or +``add_attributes`` methods. + +Example +------- + +Here's a framework-agnostic example showing manual use of the labeler: + +.. code-block:: python + + from opentelemetry.instrumentation._labeler import ( + enrich_metric_attributes, + get_labeler, + ) + from opentelemetry.metrics import get_meter + + meter = get_meter("example.manual") + duration_histogram = meter.create_histogram( + name="http.server.request.duration", + unit="s", + description="Duration of HTTP server requests.", + ) + + + def record_request(user_id: str, duration_s: float) -> None: + labeler = get_labeler() + labeler.add("user_id", user_id) + labeler.add_attributes( + { + "has_premium": user_id in ["123", "456"], + "experiment_group": "control", + "feature_enabled": True, + "user_segment": "active", + } + ) + + base_attributes = { + "http.request.method": "GET", + "http.response.status_code": 200, + } + duration_histogram.record( + max(duration_s, 0), + enrich_metric_attributes(base_attributes), + ) + +This package introduces the shared Labeler API and helper utilities. +Framework-specific integration points that call +``enrich_metric_attributes`` (for example before ``Histogram.record``) +can be added by individual instrumentors. + +When instrumentors use ``enrich_metric_attributes``, it does not +overwrite base attributes that exist at the same keys. +""" + +from opentelemetry.instrumentation._labeler._internal import ( + Labeler, + clear_labeler, + enrich_metric_attributes, + get_labeler, + get_labeler_attributes, + set_labeler, +) + +__all__ = [ + "Labeler", + "clear_labeler", + "enrich_metric_attributes", + "get_labeler", + "get_labeler_attributes", + "set_labeler", +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_labeler/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_labeler/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e64d16f Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_labeler/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_labeler/_internal/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_labeler/_internal/__init__.py new file mode 100644 index 0000000..c7e474e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_labeler/_internal/__init__.py @@ -0,0 +1,250 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import logging +import threading +from collections.abc import Mapping +from types import MappingProxyType +from typing import Any + +from opentelemetry.context import attach, create_key, get_value, set_value +from opentelemetry.util.types import AttributeValue + +LABELER_CONTEXT_KEY = create_key("otel_labeler") + +_logger = logging.getLogger(__name__) + + +class Labeler: + """ + Stores custom attributes for the current OTel context. + + This feature is experimental and unstable. + """ + + def __init__(self, max_custom_attrs: int = 20, max_attr_value_length: int = 100): + """ + Initialize a new Labeler instance. + + Args: + max_custom_attrs: Maximum number of custom attributes to store. + When this limit is reached, new attributes will be ignored; + existing attributes can still be updated. + max_attr_value_length: Maximum length for string attribute values. + String values exceeding this length will be truncated. + """ + self._lock = threading.Lock() + self._attributes: dict[str, str | int | float | bool] = {} + self._max_custom_attrs = max_custom_attrs + self._max_attr_value_length = max_attr_value_length + + def add(self, key: str, value: Any) -> None: + """ + Add a single attribute to the labeler, subject to the labeler's limits: + - If max_custom_attrs limit is reached and this is a new key, the attribute is ignored + - String values exceeding max_attr_value_length are truncated + + Args: + key: attribute key + value: attribute value, must be a primitive type: str, int, float, or bool + """ + if not isinstance(value, (str, int, float, bool)): + _logger.warning( + "Skipping attribute '%s': value must be str, int, float, or bool, got %s", + key, + type(value).__name__, + ) + return + + with self._lock: + if len(self._attributes) >= self._max_custom_attrs and key not in self._attributes: + return + + if isinstance(value, str) and len(value) > self._max_attr_value_length: + value = value[: self._max_attr_value_length] + + self._attributes[key] = value + + def add_attributes(self, attributes: dict[str, Any]) -> None: + """ + Add multiple attributes to the labeler, subject to the labeler's limits: + - If max_custom_attrs limit is reached and this is a new key, the attribute is ignored + - Existing attributes can still be updated + - String values exceeding max_attr_value_length are truncated + + Args: + attributes: Dictionary of attributes to add. Values must be primitive types + (str, int, float, or bool) + """ + with self._lock: + for key, value in attributes.items(): + if not isinstance(value, (str, int, float, bool)): + _logger.warning( + "Skipping attribute '%s': value must be str, int, float, or bool, got %s", + key, + type(value).__name__, + ) + continue + + if len(self._attributes) >= self._max_custom_attrs and key not in self._attributes: + continue + + if isinstance(value, str) and len(value) > self._max_attr_value_length: + value = value[: self._max_attr_value_length] + + self._attributes[key] = value + + def get_attributes(self) -> Mapping[str, str | int | float | bool]: + """ + Return a read-only mapping view of attributes in this labeler. + """ + with self._lock: + return MappingProxyType(self._attributes) + + def clear(self) -> None: + with self._lock: + self._attributes.clear() + + def __len__(self) -> int: + with self._lock: + return len(self._attributes) + + +def _attach_context_value(value: Labeler | None) -> None: + """ + Attach a new OpenTelemetry context containing the given labeler value. + + This helper is fail-safe: context attach errors are suppressed and + logged at debug level. + + Args: + value: Labeler instance to store in context, or ``None`` to clear it. + """ + try: + updated_context = set_value(LABELER_CONTEXT_KEY, value) + attach(updated_context) + except Exception: # pylint: disable=broad-exception-caught + _logger.debug("Failed to attach labeler context", exc_info=True) + + +def get_labeler() -> Labeler: + """ + Get the Labeler instance for the current OTel context. + + If no Labeler exists in the current context, a new one is created + and stored in the context. + + Returns: + Labeler instance for the current OTel context, or a new empty Labeler + if no Labeler is currently stored in context. + """ + try: + current_value = get_value(LABELER_CONTEXT_KEY) + except Exception: # pylint: disable=broad-exception-caught + _logger.debug("Failed to read labeler from context", exc_info=True) + current_value = None + + if isinstance(current_value, Labeler): + return current_value + + labeler = Labeler() + _attach_context_value(labeler) + return labeler + + +def set_labeler(labeler: Any) -> None: + """ + Set the Labeler instance for the current OTel context. + + Args: + labeler: The Labeler instance to set + """ + if not isinstance(labeler, Labeler): + _logger.warning( + "Skipping set_labeler: value must be Labeler, got %s", + type(labeler).__name__, + ) + return + _attach_context_value(labeler) + + +def clear_labeler() -> None: + """ + Clear the Labeler instance from the current OTel context. + + This is primarily intended for test isolation or manual context-lifecycle + management. In typical framework-instrumented request handling, + applications generally should not need to call this directly. + """ + _attach_context_value(None) + + +def get_labeler_attributes() -> Mapping[str, str | int | float | bool]: + """ + Get attributes from the current labeler, if any. + + Returns: + Read-only mapping of custom attributes, or an empty read-only mapping + if no labeler exists. + """ + empty_attributes: dict[str, str | int | float | bool] = {} + try: + current_value = get_value(LABELER_CONTEXT_KEY) + except Exception: # pylint: disable=broad-exception-caught + _logger.debug("Failed to read labeler attributes from context", exc_info=True) + return MappingProxyType(empty_attributes) + + if not isinstance(current_value, Labeler): + return MappingProxyType(empty_attributes) + return current_value.get_attributes() + + +def enrich_metric_attributes( + base_attributes: dict[str, Any], + enrich_enabled: bool = True, +) -> dict[str, AttributeValue]: + """ + Combines base_attributes with custom attributes from the current labeler, + returning a new dictionary of attributes according to the labeler configuration: + - Attributes that would override base_attributes are skipped + - If max_custom_attrs limit is reached and this is a new key, the attribute is ignored + - String values exceeding max_attr_value_length are truncated + + Args: + base_attributes: The base attributes for the metric + enrich_enabled: Whether to include custom labeler attributes + + Returns: + Dictionary combining base and custom attributes. If no custom attributes, + returns a copy of the original base attributes. + """ + if not enrich_enabled: + return base_attributes.copy() + + labeler_attributes = get_labeler_attributes() + if not labeler_attributes: + return base_attributes.copy() + + try: + labeler = get_value(LABELER_CONTEXT_KEY) + except Exception: # pylint: disable=broad-exception-caught + labeler = None + + if not isinstance(labeler, Labeler): + return base_attributes.copy() + + enriched_attributes = base_attributes.copy() + added_count = 0 + for key, value in labeler_attributes.items(): + if added_count >= labeler._max_custom_attrs: + break + if key in base_attributes: + continue + + if isinstance(value, str) and len(value) > labeler._max_attr_value_length: + value = value[: labeler._max_attr_value_length] + + enriched_attributes[key] = value + added_count += 1 + + return enriched_attributes diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_labeler/_internal/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_labeler/_internal/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5131b33 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_labeler/_internal/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_semconv.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_semconv.py new file mode 100644 index 0000000..e1af0c0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/_semconv.py @@ -0,0 +1,725 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import os +import threading +from collections.abc import Container, Mapping, MutableMapping +from enum import Enum +from urllib.parse import urlparse + +from packaging import version as package_version + +from opentelemetry.instrumentation.utils import http_status_to_status_code +from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_NAME, + DB_OPERATION, + DB_REDIS_DATABASE_INDEX, + DB_STATEMENT, + DB_SYSTEM, + DB_USER, +) +from opentelemetry.semconv._incubating.attributes.http_attributes import ( + HTTP_FLAVOR, + HTTP_HOST, + HTTP_METHOD, + HTTP_SCHEME, + HTTP_SERVER_NAME, + HTTP_STATUS_CODE, + HTTP_TARGET, + HTTP_URL, + HTTP_USER_AGENT, +) +from opentelemetry.semconv._incubating.attributes.net_attributes import ( + NET_HOST_NAME, + NET_HOST_PORT, + NET_PEER_IP, + NET_PEER_NAME, + NET_PEER_PORT, + NET_TRANSPORT, +) +from opentelemetry.semconv.attributes.client_attributes import ( + CLIENT_ADDRESS, + CLIENT_PORT, +) +from opentelemetry.semconv.attributes.db_attributes import ( + DB_NAMESPACE, + DB_OPERATION_NAME, + DB_QUERY_TEXT, + DB_SYSTEM_NAME, +) +from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE +from opentelemetry.semconv.attributes.http_attributes import ( + HTTP_REQUEST_METHOD, + HTTP_REQUEST_METHOD_ORIGINAL, + HTTP_RESPONSE_STATUS_CODE, + HTTP_ROUTE, +) +from opentelemetry.semconv.attributes.network_attributes import ( + NETWORK_PROTOCOL_VERSION, + NETWORK_TRANSPORT, +) +from opentelemetry.semconv.attributes.server_attributes import ( + SERVER_ADDRESS, + SERVER_PORT, +) +from opentelemetry.semconv.attributes.url_attributes import ( + URL_FULL, + URL_PATH, + URL_QUERY, + URL_SCHEME, +) +from opentelemetry.semconv.attributes.user_agent_attributes import ( + USER_AGENT_ORIGINAL, +) +from opentelemetry.semconv.schemas import Schemas +from opentelemetry.trace import Span +from opentelemetry.trace.status import Status, StatusCode +from opentelemetry.util.types import AttributeValue + +# Values defined in milliseconds +HTTP_DURATION_HISTOGRAM_BUCKETS_OLD = ( + 0.0, + 5.0, + 10.0, + 25.0, + 50.0, + 75.0, + 100.0, + 250.0, + 500.0, + 750.0, + 1000.0, + 2500.0, + 5000.0, + 7500.0, + 10000.0, +) + +# Values defined in seconds +HTTP_DURATION_HISTOGRAM_BUCKETS_NEW = ( + 0.005, + 0.01, + 0.025, + 0.05, + 0.075, + 0.1, + 0.25, + 0.5, + 0.75, + 1, + 2.5, + 5, + 7.5, + 10, +) + +# These lists represent attributes for metrics that are currently supported + +_client_duration_attrs_old = [ + HTTP_STATUS_CODE, + HTTP_HOST, + HTTP_METHOD, + HTTP_FLAVOR, + HTTP_SCHEME, + NET_PEER_PORT, + NET_PEER_NAME, +] + +_client_duration_attrs_new = [ + ERROR_TYPE, + HTTP_REQUEST_METHOD, + HTTP_RESPONSE_STATUS_CODE, + NETWORK_PROTOCOL_VERSION, + SERVER_ADDRESS, + SERVER_PORT, + # TODO: Support opt-in for scheme in new semconv + # URL_SCHEME, +] + +_server_duration_attrs_old = [ + HTTP_METHOD, + HTTP_HOST, + HTTP_SCHEME, + HTTP_STATUS_CODE, + HTTP_FLAVOR, + HTTP_SERVER_NAME, + NET_HOST_NAME, + NET_HOST_PORT, +] + +_server_duration_attrs_new = [ + ERROR_TYPE, + HTTP_REQUEST_METHOD, + HTTP_RESPONSE_STATUS_CODE, + HTTP_ROUTE, + NETWORK_PROTOCOL_VERSION, + URL_SCHEME, +] + +_server_active_requests_count_attrs_old = [ + HTTP_METHOD, + HTTP_HOST, + HTTP_SCHEME, + HTTP_FLAVOR, + HTTP_SERVER_NAME, +] + +_server_active_requests_count_attrs_new = [ + HTTP_REQUEST_METHOD, + URL_SCHEME, + # TODO: Support SERVER_ADDRESS AND SERVER_PORT +] + +OTEL_SEMCONV_STABILITY_OPT_IN = "OTEL_SEMCONV_STABILITY_OPT_IN" + +# Legacy/default schema version when schema_url was first introduced +_LEGACY_SCHEMA_VERSION = "1.11.0" + + +class _OpenTelemetryStabilitySignalType(Enum): + HTTP = "http" + DATABASE = "database" + GEN_AI = "gen_ai" + + +class _StabilityMode(Enum): + DEFAULT = "default" + HTTP = "http" + HTTP_DUP = "http/dup" + DATABASE = "database" + DATABASE_DUP = "database/dup" + GEN_AI_LATEST_EXPERIMENTAL = "gen_ai_latest_experimental" + + +def _report_new(mode: _StabilityMode): + return mode != _StabilityMode.DEFAULT + + +def _report_old(mode: _StabilityMode): + return mode not in (_StabilityMode.HTTP, _StabilityMode.DATABASE) + + +class _OpenTelemetrySemanticConventionStability: + _initialized = False + _lock = threading.Lock() + _OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING = {} + + @classmethod + def _initialize(cls): + with cls._lock: + if cls._initialized: + return + + # Users can pass in comma delimited string for opt-in options + # Only values for http, gen ai, and database stability are supported for now + opt_in = os.environ.get(OTEL_SEMCONV_STABILITY_OPT_IN) + + if not opt_in: + # early return in case of default + cls._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING = { + _OpenTelemetryStabilitySignalType.HTTP: _StabilityMode.DEFAULT, + _OpenTelemetryStabilitySignalType.DATABASE: _StabilityMode.DEFAULT, + _OpenTelemetryStabilitySignalType.GEN_AI: _StabilityMode.DEFAULT, + } + cls._initialized = True + return + + opt_in_list = [s.strip() for s in opt_in.split(",")] + + cls._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[_OpenTelemetryStabilitySignalType.HTTP] = cls._filter_mode( + opt_in_list, _StabilityMode.HTTP, _StabilityMode.HTTP_DUP + ) + + cls._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[_OpenTelemetryStabilitySignalType.GEN_AI] = cls._filter_mode( + opt_in_list, + _StabilityMode.DEFAULT, + _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL, + ) + + cls._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[_OpenTelemetryStabilitySignalType.DATABASE] = cls._filter_mode( + opt_in_list, + _StabilityMode.DATABASE, + _StabilityMode.DATABASE_DUP, + ) + cls._initialized = True + + @staticmethod + def _filter_mode(opt_in_list, stable_mode, dup_mode): + # Process semconv stability opt-in + # http/dup,database/dup has higher precedence over http,database + if dup_mode.value in opt_in_list: + return dup_mode + + return stable_mode if stable_mode.value in opt_in_list else _StabilityMode.DEFAULT + + @classmethod + def _get_opentelemetry_stability_opt_in_mode(cls, signal_type: _OpenTelemetryStabilitySignalType) -> _StabilityMode: + # Get OpenTelemetry opt-in mode based off of signal type (http, messaging, etc.) + return cls._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING.get(signal_type, _StabilityMode.DEFAULT) + + +def _get_semconv_opt_in_modes( + signal_types: tuple[_OpenTelemetryStabilitySignalType, ...], +) -> dict[_OpenTelemetryStabilitySignalType, _StabilityMode]: + """Returns a mapping of signal type to mode for the provided + signal_types (one/more of DATABASE, HTTP, GEN_AI). + """ + _OpenTelemetrySemanticConventionStability._initialize() + return { + signal_type: _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(signal_type) + for signal_type in signal_types + } + + +def _filter_semconv_duration_attrs( + attrs: Mapping[str, AttributeValue], + old_attrs: Container[AttributeValue], + new_attrs: Container[AttributeValue], + sem_conv_opt_in_mode: _StabilityMode = _StabilityMode.DEFAULT, +) -> dict[str, AttributeValue]: + filtered_attrs = {} + # duration is two different metrics depending on sem_conv_opt_in_mode, so no DUP attributes + allowed_attributes = new_attrs if sem_conv_opt_in_mode == _StabilityMode.HTTP else old_attrs + for key, val in attrs.items(): + if key in allowed_attributes: + filtered_attrs[key] = val + return filtered_attrs + + +def _filter_semconv_active_request_count_attr( + attrs: Mapping[str, AttributeValue], + old_attrs: Container[AttributeValue], + new_attrs: Container[AttributeValue], + sem_conv_opt_in_mode: _StabilityMode = _StabilityMode.DEFAULT, +) -> dict[str, AttributeValue]: + filtered_attrs = {} + if _report_old(sem_conv_opt_in_mode): + for key, val in attrs.items(): + if key in old_attrs: + filtered_attrs[key] = val + if _report_new(sem_conv_opt_in_mode): + for key, val in attrs.items(): + if key in new_attrs: + filtered_attrs[key] = val + return filtered_attrs + + +def set_string_attribute( + result: MutableMapping[str, AttributeValue], + key: str, + value: AttributeValue, +) -> None: + if value: + result[key] = value + + +def set_int_attribute( + result: MutableMapping[str, AttributeValue], + key: str, + value: AttributeValue, +) -> None: + if value: + try: + result[key] = int(value) + except (ValueError, TypeError): + pass + + +def _set_http_method( + result: MutableMapping[str, AttributeValue], + original: str, + normalized: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + original = original.strip() + normalized = normalized.strip() + # See https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-spans.md#common-attributes + # Method is case sensitive. "http.request.method_original" should not be sanitized or automatically capitalized. + if original != normalized and _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, HTTP_REQUEST_METHOD_ORIGINAL, original) + + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, HTTP_METHOD, normalized) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, HTTP_REQUEST_METHOD, normalized) + + +def _set_http_status_code( + result: MutableMapping[str, AttributeValue], + code: str | int, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_int_attribute(result, HTTP_STATUS_CODE, code) + if _report_new(sem_conv_opt_in_mode): + set_int_attribute(result, HTTP_RESPONSE_STATUS_CODE, code) + + +def _set_http_url( + result: MutableMapping[str, AttributeValue], + url: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, HTTP_URL, url) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, URL_FULL, url) + + +def _set_http_scheme( + result: MutableMapping[str, AttributeValue], + scheme: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, HTTP_SCHEME, scheme) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, URL_SCHEME, scheme) + + +def _set_http_flavor_version( + result: MutableMapping[str, AttributeValue], + version: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, HTTP_FLAVOR, version) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, NETWORK_PROTOCOL_VERSION, version) + + +def _set_http_user_agent( + result: MutableMapping[str, AttributeValue], + user_agent: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, HTTP_USER_AGENT, user_agent) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, USER_AGENT_ORIGINAL, user_agent) + + +# Client + + +def _set_http_host_client( + result: MutableMapping[str, AttributeValue], + host: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, HTTP_HOST, host) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SERVER_ADDRESS, host) + + +def _set_http_net_peer_name_client( + result: MutableMapping[str, AttributeValue], + peer_name: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, NET_PEER_NAME, peer_name) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SERVER_ADDRESS, peer_name) + + +def _set_http_peer_port_client( + result: MutableMapping[str, AttributeValue], + port: str | int, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_int_attribute(result, NET_PEER_PORT, port) + if _report_new(sem_conv_opt_in_mode): + set_int_attribute(result, SERVER_PORT, port) + + +def _set_http_network_protocol_version( + result: MutableMapping[str, AttributeValue], + version: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, HTTP_FLAVOR, version) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, NETWORK_PROTOCOL_VERSION, version) + + +# Server + + +def _set_http_net_host( + result: MutableMapping[str, AttributeValue], + host: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, NET_HOST_NAME, host) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SERVER_ADDRESS, host) + + +def _set_http_net_host_port( + result: MutableMapping[str, AttributeValue], + port: str | int, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_int_attribute(result, NET_HOST_PORT, port) + if _report_new(sem_conv_opt_in_mode): + set_int_attribute(result, SERVER_PORT, port) + + +def _set_http_target( + result: MutableMapping[str, AttributeValue], + target: str, + path: str | None, + query: str | None, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, HTTP_TARGET, target) + if _report_new(sem_conv_opt_in_mode): + if path: + set_string_attribute(result, URL_PATH, path) + if query: + set_string_attribute(result, URL_QUERY, query) + + +def _set_http_host_server( + result: MutableMapping[str, AttributeValue], + host: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, HTTP_HOST, host) + if _report_new(sem_conv_opt_in_mode): + if not result.get(SERVER_ADDRESS): + set_string_attribute(result, SERVER_ADDRESS, host) + + +# net.peer.ip -> net.sock.peer.addr +# https://github.com/open-telemetry/semantic-conventions/blob/40db676ca0e735aa84f242b5a0fb14e49438b69b/schemas/1.15.0#L18 +# net.sock.peer.addr -> client.socket.address for server spans (TODO) AND client.address if missing +# https://github.com/open-telemetry/semantic-conventions/blob/v1.21.0/CHANGELOG.md#v1210-2023-07-13 +# https://github.com/open-telemetry/semantic-conventions/blob/main/docs/non-normative/http-migration.md#common-attributes-across-http-client-and-server-spans +def _set_http_peer_ip_server( + result: MutableMapping[str, AttributeValue], + ip: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, NET_PEER_IP, ip) + if _report_new(sem_conv_opt_in_mode): + # Only populate if not already populated + if not result.get(CLIENT_ADDRESS): + set_string_attribute(result, CLIENT_ADDRESS, ip) + + +def _set_http_peer_port_server( + result: MutableMapping[str, AttributeValue], + port: str | int, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_int_attribute(result, NET_PEER_PORT, port) + if _report_new(sem_conv_opt_in_mode): + set_int_attribute(result, CLIENT_PORT, port) + + +def _set_http_net_peer_name_server( + result: MutableMapping[str, AttributeValue], + name: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, NET_PEER_NAME, name) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, CLIENT_ADDRESS, name) + + +# Database + + +def _set_db_system( + result: MutableMapping[str, AttributeValue], + system: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, DB_SYSTEM, system) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, DB_SYSTEM_NAME, system) + + +def _set_db_name( + result: MutableMapping[str, AttributeValue], + name: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, DB_NAME, name) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, DB_NAMESPACE, name) + + +def _set_db_statement( + result: MutableMapping[str, AttributeValue], + statement: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + # skip the statement if it's None but set it if it's an empty string + if statement is None: + return + + if _report_old(sem_conv_opt_in_mode): + result[DB_STATEMENT] = statement + if _report_new(sem_conv_opt_in_mode): + result[DB_QUERY_TEXT] = statement + + +def _set_db_user( + result: MutableMapping[str, AttributeValue], + user: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, DB_USER, user) + # No new attribute - db.user was removed with no replacement + + +def _set_db_operation( + result: MutableMapping[str, AttributeValue], + operation: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, DB_OPERATION, operation) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, DB_OPERATION_NAME, operation) + + +def _set_db_redis_database_index( + result: MutableMapping[str, AttributeValue], + database_index: int, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + if database_index is not None: + result[DB_REDIS_DATABASE_INDEX] = int(database_index) + # No new attribute - db.redis.database_index was removed with no replacement in semconv 1.38.0 + + +def _set_net_transport( + result: MutableMapping[str, AttributeValue], + old_transport: AttributeValue, + new_transport: AttributeValue, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, NET_TRANSPORT, old_transport) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, NETWORK_TRANSPORT, new_transport) + + +# General + + +def _set_status( + span: Span, + metrics_attributes: MutableMapping[str, AttributeValue], + status_code: int, + status_code_str: str, + server_span: bool = True, + sem_conv_opt_in_mode: _StabilityMode = _StabilityMode.DEFAULT, +) -> None: + if status_code < 0: + if _report_new(sem_conv_opt_in_mode): + metrics_attributes[ERROR_TYPE] = status_code_str + if span.is_recording(): + if _report_new(sem_conv_opt_in_mode): + span.set_attribute(ERROR_TYPE, status_code_str) + span.set_status( + Status( + StatusCode.ERROR, + "Non-integer HTTP status: " + status_code_str, + ) + ) + else: + status = http_status_to_status_code(status_code, server_span=server_span) + + if _report_old(sem_conv_opt_in_mode): + if span.is_recording(): + span.set_attribute(HTTP_STATUS_CODE, status_code) + metrics_attributes[HTTP_STATUS_CODE] = status_code + if _report_new(sem_conv_opt_in_mode): + if span.is_recording(): + span.set_attribute(HTTP_RESPONSE_STATUS_CODE, status_code) + metrics_attributes[HTTP_RESPONSE_STATUS_CODE] = status_code + if status == StatusCode.ERROR: + if span.is_recording(): + span.set_attribute(ERROR_TYPE, status_code_str) + metrics_attributes[ERROR_TYPE] = status_code_str + if span.is_recording(): + span.set_status(Status(status)) + + +def _get_schema_url(mode: _StabilityMode) -> str: + """Get schema version URL for a single signal type's opt-in mode (backwards compatible). + + For new instrumentations using multiple signal types, use + _get_schema_url_for_signal_types() + """ + if mode is _StabilityMode.DEFAULT: + return f"https://opentelemetry.io/schemas/{_LEGACY_SCHEMA_VERSION}" + return Schemas.V1_21_0.value + + +def _get_schema_version_for_opt_in_mode( + signal_type: _OpenTelemetryStabilitySignalType, + mode: _StabilityMode, +) -> str: + """Get the schema version for a specific signal type and opt-in mode.""" + if mode == _StabilityMode.DEFAULT: + return _LEGACY_SCHEMA_VERSION + + signal_versions = { + _OpenTelemetryStabilitySignalType.HTTP: Schemas.V1_21_0.value, + _OpenTelemetryStabilitySignalType.DATABASE: Schemas.V1_25_0.value, + _OpenTelemetryStabilitySignalType.GEN_AI: Schemas.V1_26_0.value, + } + schema_url = signal_versions.get(signal_type) + if not schema_url: + return _LEGACY_SCHEMA_VERSION + + path = urlparse(schema_url).path + schema_version = path.rstrip("/").split("/")[-1] + return schema_version or _LEGACY_SCHEMA_VERSION + + +def _get_schema_url_for_signal_types( + signal_types: list[_OpenTelemetryStabilitySignalType], +) -> str: + """Get the highest applicable schema URL for multiple signal types. + + Note: + Instrumentors should call _OpenTelemetrySemanticConventionStability._initialize() + before using this function to ensure proper initialization of stability modes. + + Args: + signal_types: List of signal types used by the instrumentation + + Returns: + Schema URL string representing the highest applicable semconv version + """ + highest_schema_version = _LEGACY_SCHEMA_VERSION + for signal_type in signal_types: + mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(signal_type) + schema_version = _get_schema_version_for_opt_in_mode(signal_type, mode) + # Keep the highest for all signals + if package_version.Version(schema_version) > package_version.Version(highest_schema_version): + highest_schema_version = schema_version + return f"https://opentelemetry.io/schemas/{highest_schema_version}" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/__init__.py new file mode 100644 index 0000000..b0bdf4a --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/__init__.py @@ -0,0 +1,168 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from argparse import REMAINDER, ArgumentParser +from logging import getLogger +from os import environ, execl, getcwd +from os.path import abspath, dirname, pathsep +from re import sub +from shutil import which + +from opentelemetry.instrumentation.auto_instrumentation._load import ( + _load_configurators, + _load_distro, + _load_instrumentors, +) +from opentelemetry.instrumentation.environment_variables import ( + OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH, +) +from opentelemetry.instrumentation.utils import _python_path_without_directory +from opentelemetry.instrumentation.version import __version__ +from opentelemetry.util._importlib_metadata import entry_points + +_logger = getLogger(__name__) + + +def run() -> None: + parser = ArgumentParser( + description=""" + opentelemetry-instrument automatically instruments a Python + program and its dependencies and then runs the program. + """, + epilog=""" + Optional arguments (except for --help and --version) for opentelemetry-instrument + directly correspond with OpenTelemetry environment variables. The + corresponding optional argument is formed by removing the OTEL_ or + OTEL_PYTHON_ prefix from the environment variable and lower casing the + rest. For example, the optional argument --attribute_value_length_limit + corresponds with the environment variable + OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT. + + These optional arguments will override the current value of the + corresponding environment variable during the execution of the command. + """, + ) + + argument_otel_environment_variable = {} + + for entry_point in entry_points(group="opentelemetry_environment_variables"): + environment_variable_module = entry_point.load() + + for attribute in dir(environment_variable_module): + if attribute.startswith("OTEL_"): + argument = sub(r"OTEL_(PYTHON_)?", "", attribute).lower() + + parser.add_argument( + f"--{argument}", + required=False, + ) + argument_otel_environment_variable[argument] = attribute + + parser.add_argument( + "--version", + help="print version information", + action="version", + version="%(prog)s " + __version__, + ) + parser.add_argument("command", help="Your Python application.") + parser.add_argument( + "command_args", + help="Arguments for your application.", + nargs=REMAINDER, + ) + + args = parser.parse_args() + + for argument, otel_environment_variable in (argument_otel_environment_variable).items(): + value = getattr(args, argument) + if value is not None: + environ[otel_environment_variable] = value + + python_path = environ.get("PYTHONPATH") + + if not python_path: + python_path = [] + + else: + python_path = python_path.split(pathsep) + + cwd_path = getcwd() + + # This is being added to support applications that are being run from their + # own executable, like Django. + # FIXME investigate if there is another way to achieve this + if cwd_path not in python_path: + python_path.insert(0, cwd_path) + + filedir_path = dirname(abspath(__file__)) + + python_path = [path for path in python_path if path != filedir_path] + + python_path.insert(0, filedir_path) + + environ["PYTHONPATH"] = pathsep.join(python_path) + + executable = which(args.command) + execl(executable, executable, *args.command_args) + + +def _initialize(*, swallow_exceptions: bool = True) -> None: + # handle optional gevent monkey patching. This is done via environment variables so it may be used from the + # opentelemetry operator + gevent_patch: str | None = environ.get(OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH) + if gevent_patch is not None: + if gevent_patch != "patch_all": + _logger.error( + "%s value must be `patch_all`", + OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH, + ) + else: + try: + # pylint: disable=import-outside-toplevel + from gevent import monkey # noqa: PLC0415 + + getattr(monkey, gevent_patch)() + except ImportError: + _logger.exception("Failed to monkey patch with gevent because gevent is not available") + if not swallow_exceptions: + raise + + try: + distro = _load_distro() + distro.configure() + _load_configurators() + _load_instrumentors(distro) + except Exception as exc: # pylint: disable=broad-except + _logger.exception("Failed to auto initialize OpenTelemetry") + if not swallow_exceptions: + raise exc + + +def initialize(*, swallow_exceptions: bool = True) -> None: + """ + Setup auto-instrumentation, called by the sitecustomize module + + :param swallow_exceptions: Whether or not to propagate instrumentation exceptions to the caller. Exceptions are logged and swallowed by default. + """ + filedir = dirname(abspath(__file__)) + + python_path = environ.get("PYTHONPATH") + auto_instrumentation_path_was_present = python_path is not None and filedir in python_path.split(pathsep) + + # Remove the auto-instrumentation path during initialization to prevent + # auto-instrumentation from executing in subprocesses spawned during this phase. + # This suppression is performed to avoid creating a recursive loop scenario + # where subprocesses spawned in the initialization phase execute the + # initialization phase again, spawning more subprocesses. + if python_path is not None: + environ["PYTHONPATH"] = _python_path_without_directory(python_path, filedir, pathsep) + + try: + _initialize(swallow_exceptions=swallow_exceptions) + finally: + if auto_instrumentation_path_was_present: + current = environ.get("PYTHONPATH", "") + if filedir not in current.split(pathsep): + environ["PYTHONPATH"] = filedir + pathsep + current if current else filedir diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2773caf Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/__pycache__/_load.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/__pycache__/_load.cpython-312.pyc new file mode 100644 index 0000000..0dbfe74 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/__pycache__/_load.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/__pycache__/sitecustomize.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/__pycache__/sitecustomize.cpython-312.pyc new file mode 100644 index 0000000..1a1b62c Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/__pycache__/sitecustomize.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/_load.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/_load.py new file mode 100644 index 0000000..647168b --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/_load.py @@ -0,0 +1,153 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from functools import cached_property +from logging import getLogger +from os import environ + +from opentelemetry.instrumentation.dependencies import ( + DependencyConflictError, + get_dist_dependency_conflicts, +) +from opentelemetry.instrumentation.distro import BaseDistro, DefaultDistro +from opentelemetry.instrumentation.environment_variables import ( + OTEL_PYTHON_CONFIGURATOR, + OTEL_PYTHON_DISABLED_INSTRUMENTATIONS, + OTEL_PYTHON_DISTRO, +) +from opentelemetry.instrumentation.version import __version__ +from opentelemetry.util._importlib_metadata import ( + EntryPoint, + distributions, + entry_points, +) + +_logger = getLogger(__name__) + +SKIPPED_INSTRUMENTATIONS_WILDCARD = "*" + + +class _EntryPointDistFinder: + @cached_property + def _mapping(self): + return {self._key_for(ep): dist for dist in distributions() for ep in dist.entry_points} + + def dist_for(self, entry_point: EntryPoint): + dist = getattr(entry_point, "dist", None) + if dist: + return dist + + return self._mapping.get(self._key_for(entry_point)) + + @staticmethod + def _key_for(entry_point: EntryPoint): + return f"{entry_point.group}:{entry_point.name}:{entry_point.value}" + + +def _load_distro() -> BaseDistro: + distro_name = environ.get(OTEL_PYTHON_DISTRO, None) + for entry_point in entry_points(group="opentelemetry_distro"): + try: + # If no distro is specified, use first to come up. + if distro_name is None or distro_name == entry_point.name: + distro = entry_point.load()() + if not isinstance(distro, BaseDistro): + _logger.debug( + "%s is not an OpenTelemetry Distro. Skipping", + entry_point.name, + ) + continue + _logger.debug("Distribution %s will be configured", entry_point.name) + return distro + except Exception as exc: # pylint: disable=broad-except + _logger.exception("Distribution %s configuration failed", entry_point.name) + raise exc + return DefaultDistro() + + +def _load_instrumentors(distro): + package_to_exclude = environ.get(OTEL_PYTHON_DISABLED_INSTRUMENTATIONS, []) + entry_point_finder = _EntryPointDistFinder() + if isinstance(package_to_exclude, str): + package_to_exclude = package_to_exclude.split(",") + # to handle users entering "requests , flask" or "requests, flask" with spaces + package_to_exclude = [x.strip() for x in package_to_exclude] + + for entry_point in entry_points(group="opentelemetry_pre_instrument"): + entry_point.load()() + + for entry_point in entry_points(group="opentelemetry_instrumentor"): + if SKIPPED_INSTRUMENTATIONS_WILDCARD in package_to_exclude: + break + + if entry_point.name in package_to_exclude: + _logger.debug("Instrumentation skipped for library %s", entry_point.name) + continue + + try: + entry_point_dist = entry_point_finder.dist_for(entry_point) + conflict = get_dist_dependency_conflicts(entry_point_dist) + if conflict: + conflict._log(_logger, entry_point.name) + continue + + # tell instrumentation to not run dep checks again as we already did it above + distro.load_instrumentor(entry_point, skip_dep_check=True) + _logger.debug("Instrumented %s", entry_point.name) + except DependencyConflictError as exc: + # Dependency conflicts are generally caught from get_dist_dependency_conflicts + # returning a DependencyConflict. Keeping this error handling in case custom + # distro and instrumentor behavior raises a DependencyConflictError later. + # See https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610 + exc.conflict._log(_logger, entry_point.name) + continue + except ModuleNotFoundError as exc: + # ModuleNotFoundError is raised when the library is not installed + # and the instrumentation is not required to be loaded. + # See https://github.com/open-telemetry/opentelemetry-python-contrib/issues/3421 + _logger.debug("Skipping instrumentation %s: %s", entry_point.name, exc.msg) + continue + except ImportError: + # in scenarios using the kubernetes operator to do autoinstrumentation some + # instrumentors (usually requiring binary extensions) may fail to load + # because the injected autoinstrumentation code does not match the application + # environment regarding python version, libc, etc... In this case it's better + # to skip the single instrumentation rather than failing to load everything + # so treat differently ImportError than the rest of exceptions + _logger.exception("Importing of %s failed, skipping it", entry_point.name) + continue + except Exception as exc: # pylint: disable=broad-except + _logger.exception("Instrumenting of %s failed", entry_point.name) + raise exc + + for entry_point in entry_points(group="opentelemetry_post_instrument"): + entry_point.load()() + + +def _load_configurators(): + configurator_name = environ.get(OTEL_PYTHON_CONFIGURATOR, None) + configured = None + for entry_point in entry_points(group="opentelemetry_configurator"): + if configured is not None: + _logger.warning( + "Configuration of %s not loaded, %s already loaded", + entry_point.name, + configured, + ) + continue + try: + if configurator_name is None or configurator_name == entry_point.name: + entry_point.load()().configure(auto_instrumentation_version=__version__) # type: ignore + configured = entry_point.name + else: + _logger.warning( + "Configuration of %s not loaded because %s is set by %s", + entry_point.name, + configurator_name, + OTEL_PYTHON_CONFIGURATOR, + ) + except Exception as exc: # pylint: disable=broad-except + _logger.exception("Configuration of %s failed", entry_point.name) + raise exc diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/sitecustomize.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/sitecustomize.py new file mode 100644 index 0000000..e430c60 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/auto_instrumentation/sitecustomize.py @@ -0,0 +1,6 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from opentelemetry.instrumentation.auto_instrumentation import initialize + +initialize() diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/bootstrap.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/bootstrap.py new file mode 100644 index 0000000..056c9b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/bootstrap.py @@ -0,0 +1,166 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import argparse +import logging +import sys +from subprocess import ( + PIPE, + CalledProcessError, + Popen, + SubprocessError, + check_call, +) + +from packaging.requirements import Requirement + +from opentelemetry.instrumentation.bootstrap_gen import ( + default_instrumentations as gen_default_instrumentations, +) +from opentelemetry.instrumentation.bootstrap_gen import ( + libraries as gen_libraries, +) +from opentelemetry.instrumentation.version import __version__ +from opentelemetry.util._importlib_metadata import ( + PackageNotFoundError, + version, +) + +logger = logging.getLogger(__name__) + + +def _syscall(func): + def wrapper(package=None): + try: + if package: + return func(package) + return func() + except SubprocessError as exp: + cmd = getattr(exp, "cmd", None) + if cmd: + msg = f'Error calling system command "{" ".join(cmd)}"' + if package: + msg = f'{msg} for package "{package}"' + raise RuntimeError(msg) + + return wrapper + + +@_syscall +def _sys_pip_install(package): + # explicit upgrade strategy to override potential pip config + try: + check_call( + [ + sys.executable, + "-m", + "pip", + "install", + "-U", + "--upgrade-strategy", + "only-if-needed", + package, + ] + ) + except CalledProcessError as error: + print(error) + + +def _pip_check(libraries): + """Ensures none of the instrumentations have dependency conflicts. + Clean check reported as: + 'No broken requirements found.' + Dependency conflicts are reported as: + 'opentelemetry-instrumentation-flask 1.0.1 has requirement opentelemetry-sdk<2.0,>=1.0, but you have opentelemetry-sdk 0.5.' + To not be too restrictive, we'll only check for relevant packages. + """ + with Popen([sys.executable, "-m", "pip", "check"], stdout=PIPE) as check_pipe: + pip_check = check_pipe.communicate()[0].decode() + pip_check_lower = pip_check.lower() + for package_tup in libraries: + for package in package_tup: + if package.lower() in pip_check_lower: + raise RuntimeError(f"Dependency conflict found: {pip_check}") + + +def _is_installed(req): + req = Requirement(req) + + try: + dist_version = version(req.name) + except PackageNotFoundError: + return False + + if not req.specifier.filter(dist_version): + logger.warning( + "instrumentation for package %s is available but version %s is installed. Skipping.", + req, + dist_version, + ) + return False + return True + + +def _find_installed_libraries(default_instrumentations, libraries): + yield from default_instrumentations + + for lib in libraries: + if _is_installed(lib["library"]): + yield lib["instrumentation"] + + +def _run_requirements(default_instrumentations, libraries): + logger.setLevel(logging.ERROR) + print("\n".join(_find_installed_libraries(default_instrumentations, libraries))) + + +def _run_install(default_instrumentations, libraries): + for lib in _find_installed_libraries(default_instrumentations, libraries): + _sys_pip_install(lib) + _pip_check(libraries) + + +def run( + default_instrumentations: list | None = None, + libraries: list | None = None, +) -> None: + action_install = "install" + action_requirements = "requirements" + + parser = argparse.ArgumentParser( + description=""" + opentelemetry-bootstrap detects installed libraries and automatically + installs the relevant instrumentation packages for them. + """ + ) + parser.add_argument( + "--version", + help="print version information", + action="version", + version="%(prog)s " + __version__, + ) + parser.add_argument( + "-a", + "--action", + choices=[action_install, action_requirements], + default=action_requirements, + help=""" + install - uses pip to install the new requirements using to the + currently active site-package. + requirements - prints out the new requirements to stdout. Action can + be piped and appended to a requirements.txt file. + """, + ) + args = parser.parse_args() + + if libraries is None: + libraries = gen_libraries + + if default_instrumentations is None: + default_instrumentations = gen_default_instrumentations + + cmd = { + action_install: _run_install, + action_requirements: _run_requirements, + }[args.action] + cmd(default_instrumentations, libraries) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/bootstrap_gen.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/bootstrap_gen.py new file mode 100644 index 0000000..9229e18 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/bootstrap_gen.py @@ -0,0 +1,70 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# DO NOT EDIT. THIS FILE WAS AUTOGENERATED FROM INSTRUMENTATION PACKAGES. +# RUN `python scripts/generate_instrumentation_bootstrap.py` TO REGENERATE. + +libraries = [ + {"library": "openai >= 1.26.0", "instrumentation": "opentelemetry-instrumentation-openai-v2"}, + {"library": "google-cloud-aiplatform >= 1.64", "instrumentation": "opentelemetry-instrumentation-vertexai>=2.0b0"}, + {"library": "aio_pika >= 7.2.0, < 11.0.0", "instrumentation": "opentelemetry-instrumentation-aio-pika==0.66b0"}, + {"library": "aiohttp ~= 3.0", "instrumentation": "opentelemetry-instrumentation-aiohttp-client==0.66b0"}, + {"library": "aiohttp ~= 3.0", "instrumentation": "opentelemetry-instrumentation-aiohttp-server==0.66b0"}, + {"library": "aiokafka >= 0.8, < 1.0", "instrumentation": "opentelemetry-instrumentation-aiokafka==0.66b0"}, + {"library": "aiopg >= 0.13.0, < 2.0.0", "instrumentation": "opentelemetry-instrumentation-aiopg==0.66b0"}, + {"library": "asgiref ~= 3.0", "instrumentation": "opentelemetry-instrumentation-asgi==0.66b0"}, + {"library": "asyncclick ~= 8.0", "instrumentation": "opentelemetry-instrumentation-asyncclick==0.66b0"}, + {"library": "asyncpg >= 0.12.0", "instrumentation": "opentelemetry-instrumentation-asyncpg==0.66b0"}, + {"library": "boto3 ~= 1.0", "instrumentation": "opentelemetry-instrumentation-boto3sqs==0.66b0"}, + {"library": "botocore ~= 1.0", "instrumentation": "opentelemetry-instrumentation-botocore==0.66b0"}, + {"library": "aiobotocore >= 2.0, < 4.0", "instrumentation": "opentelemetry-instrumentation-botocore==0.66b0"}, + {"library": "cassandra-driver ~= 3.25", "instrumentation": "opentelemetry-instrumentation-cassandra==0.66b0"}, + {"library": "scylla-driver ~= 3.25", "instrumentation": "opentelemetry-instrumentation-cassandra==0.66b0"}, + {"library": "celery >= 4.0, < 6.0", "instrumentation": "opentelemetry-instrumentation-celery==0.66b0"}, + {"library": "click >= 8.1.3, < 9.0.0", "instrumentation": "opentelemetry-instrumentation-click==0.66b0"}, + { + "library": "confluent-kafka >= 1.8.2, < 3.0.0", + "instrumentation": "opentelemetry-instrumentation-confluent-kafka==0.66b0", + }, + {"library": "django >= 2.0", "instrumentation": "opentelemetry-instrumentation-django==0.66b0"}, + {"library": "falcon >= 1.4.1, < 5.0.0", "instrumentation": "opentelemetry-instrumentation-falcon==0.66b0"}, + {"library": "fastapi ~= 0.92", "instrumentation": "opentelemetry-instrumentation-fastapi==0.66b0"}, + {"library": "flask >= 1.0", "instrumentation": "opentelemetry-instrumentation-flask==0.66b0"}, + {"library": "grpcio >= 1.42.0", "instrumentation": "opentelemetry-instrumentation-grpc==0.66b0"}, + {"library": "httpx >= 0.18.0", "instrumentation": "opentelemetry-instrumentation-httpx==0.66b0"}, + {"library": "httpx2 >= 2.0.0", "instrumentation": "opentelemetry-instrumentation-httpx==0.66b0"}, + {"library": "jinja2 >= 2.7, < 4.0", "instrumentation": "opentelemetry-instrumentation-jinja2==0.66b0"}, + {"library": "kafka-python >= 2.0, < 4.0", "instrumentation": "opentelemetry-instrumentation-kafka-python==0.66b0"}, + {"library": "mysql-connector-python >= 8.0", "instrumentation": "opentelemetry-instrumentation-mysql==0.66b0"}, + {"library": "mysqlclient < 3", "instrumentation": "opentelemetry-instrumentation-mysqlclient==0.66b0"}, + {"library": "pika >= 0.12.0", "instrumentation": "opentelemetry-instrumentation-pika==0.66b0"}, + {"library": "psycopg >= 3.1.0", "instrumentation": "opentelemetry-instrumentation-psycopg==0.66b0"}, + {"library": "psycopg2 >= 2.7.3.1", "instrumentation": "opentelemetry-instrumentation-psycopg2==0.66b0"}, + {"library": "psycopg2-binary >= 2.7.3.1", "instrumentation": "opentelemetry-instrumentation-psycopg2==0.66b0"}, + {"library": "pymemcache >= 1.3.5, < 5", "instrumentation": "opentelemetry-instrumentation-pymemcache==0.66b0"}, + {"library": "pymongo >= 3.1, < 5.0", "instrumentation": "opentelemetry-instrumentation-pymongo==0.66b0"}, + {"library": "pymssql >= 2.1.5, < 3", "instrumentation": "opentelemetry-instrumentation-pymssql==0.66b0"}, + {"library": "PyMySQL < 2", "instrumentation": "opentelemetry-instrumentation-pymysql==0.66b0"}, + {"library": "pyramid >= 1.7", "instrumentation": "opentelemetry-instrumentation-pyramid==0.66b0"}, + {"library": "redis >= 2.6", "instrumentation": "opentelemetry-instrumentation-redis==0.66b0"}, + {"library": "remoulade >= 0.50", "instrumentation": "opentelemetry-instrumentation-remoulade==0.66b0"}, + {"library": "requests ~= 2.0", "instrumentation": "opentelemetry-instrumentation-requests==0.66b0"}, + {"library": "sqlalchemy >= 1.0.0, < 2.1.0", "instrumentation": "opentelemetry-instrumentation-sqlalchemy==0.66b0"}, + {"library": "starlette >= 0.13", "instrumentation": "opentelemetry-instrumentation-starlette==0.66b0"}, + {"library": "structlog >= 21.1", "instrumentation": "opentelemetry-instrumentation-structlog==0.66b0"}, + {"library": "psutil >= 5", "instrumentation": "opentelemetry-instrumentation-system-metrics==0.66b0"}, + {"library": "tornado >= 5.1.1", "instrumentation": "opentelemetry-instrumentation-tornado==0.66b0"}, + {"library": "tortoise-orm >= 0.17.0", "instrumentation": "opentelemetry-instrumentation-tortoiseorm==0.66b0"}, + {"library": "pydantic >= 1.10.2", "instrumentation": "opentelemetry-instrumentation-tortoiseorm==0.66b0"}, + {"library": "urllib3 >= 1.0.0, < 3.0.0", "instrumentation": "opentelemetry-instrumentation-urllib3==0.66b0"}, +] +default_instrumentations = [ + "opentelemetry-instrumentation-asyncio==0.66b0", + "opentelemetry-instrumentation-dbapi==0.66b0", + "opentelemetry-instrumentation-exceptions==0.66b0", + "opentelemetry-instrumentation-logging==0.66b0", + "opentelemetry-instrumentation-sqlite3==0.66b0", + "opentelemetry-instrumentation-threading==0.66b0", + "opentelemetry-instrumentation-urllib==0.66b0", + "opentelemetry-instrumentation-wsgi==0.66b0", +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/cidict.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/cidict.py new file mode 100644 index 0000000..8681484 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/cidict.py @@ -0,0 +1,71 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections.abc import Iterable, Iterator, Mapping, MutableMapping +from typing import ( + Any, + TypeVar, +) + +KT = TypeVar("KT") +VT = TypeVar("VT") + + +class CIDict(MutableMapping[KT, VT]): + def __init__( + self, + data: Mapping[KT, VT] | Iterable[tuple[KT, VT]] | None = None, + ) -> None: + self._data: dict[KT, tuple[KT, VT]] = {} + if data is None: + data = {} + self.update(data) + + @staticmethod + def _normalize_key(key: KT) -> KT: + if isinstance(key, str): + return key.lower() # type: ignore + return key + + def _get_entry(self, key: KT) -> tuple[KT, VT]: + normalized_key = self._normalize_key(key) + if normalized_key in self._data: + return self._data[normalized_key] + raise KeyError(repr(key)) + + def original_key(self, key: KT) -> KT: + return self._get_entry(key)[0] + + def normalized_items(self) -> Iterable[tuple[KT, VT]]: + return ((key, value[1]) for key, value in self._data.items()) + + def __setitem__(self, key: KT, value: VT, /) -> None: + self._data[self._normalize_key(key)] = (key, value) + + def __delitem__(self, key: KT, /) -> None: + try: + del self._data[self._normalize_key(key)] + except KeyError: + raise KeyError(repr(key)) from None + + def __getitem__(self, key: KT, /) -> VT: + return self._get_entry(key)[1] + + def __len__(self) -> int: + return len(self._data) + + def __iter__(self) -> Iterator[KT]: + return (key for key, _ in self._data.values()) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({dict(self.items())!r})" + + def __eq__(self, other: object) -> bool: + if isinstance(other, CIDict): + return dict(self.normalized_items()) == dict(other.normalized_items()) + if not isinstance(other, Mapping): + return False + ciother: CIDict[Any, Any] = CIDict(other) + return dict(self.normalized_items()) == dict(ciother.normalized_items()) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/dependencies.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/dependencies.py new file mode 100644 index 0000000..4dd6b6e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/dependencies.py @@ -0,0 +1,209 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections.abc import Collection +from logging import Logger, getLogger + +from packaging.requirements import InvalidRequirement, Requirement + +from opentelemetry.util._importlib_metadata import ( + Distribution, + PackageNotFoundError, + version, +) + +_logger = getLogger(__name__) + + +def _format_deps(deps: Collection[str]) -> str: + return ", ".join(str(dep) for dep in deps) + + +class DependencyConflict: + """Represents a dependency conflict in OpenTelemetry instrumentation. + + This class is used to track conflicts between required dependencies and the + actual installed packages. It supports two scenarios: + + 1. Standard conflicts where all dependencies are required + 2. Either/or conflicts where only one of a set of dependencies is required + + Attributes: + required: The required dependency specification that conflicts with what's installed. + found: The actual dependency that was found installed (if any). + required_any: Collection of dependency specifications where any one would satisfy + the requirement (for either/or scenarios). + found_any: Collection of actual dependencies found for either/or scenarios. + """ + + required: str | None = None + found: str | None = None + # The following fields are used when an instrumentation requires any of a set of dependencies rather than all. + required_any: Collection[str] = None + found_any: Collection[str] = None + + def __init__( + self, + required: str | None = None, + found: str | None = None, + required_any: Collection[str] = None, + found_any: Collection[str] = None, + ): + self.required = required + self.found = found + # The following fields are used when an instrumentation requires any of a set of dependencies rather than all. + self.required_any = required_any + self.found_any = found_any + + @property + def _is_version_conflict(self) -> bool: + """False when the dependency is not installed at all, rather than installed at an incompatible version.""" + return bool(self.found or self.found_any) + + def _log(self, logger: Logger, instrumentor_name: str) -> None: + """Log as an error for a version conflict, at debug level when the dependency is not installed.""" + log = logger.error if self._is_version_conflict else logger.debug + log(self._format_message(instrumentor_name)) + + def _format_message(self, instrumentor_name: str | None = None) -> str: + subject = instrumentor_name or "This instrumentation" + if self.required: + if self.found: + return ( + f'{subject} only instruments "{self.required}", ' + f'but currently installed version ("{self.found}") falls outside of that range, ' + f"so nothing can be instrumented." + ) + return ( + f'{subject} only instruments "{self.required}", ' + f"but no installed version was found, so nothing can be instrumented." + ) + if self.required_any: + if self.found_any: + return ( + f'{subject} instruments any of "{_format_deps(self.required_any)}", ' + f'but currently installed version(s) ("{_format_deps(self.found_any)}") fall outside of that range, ' + f"so nothing can be instrumented." + ) + return ( + f'{subject} requires any of "{_format_deps(self.required_any)}", ' + f"but none are installed, so nothing can be instrumented." + ) + return f"{subject} has an unknown dependency conflict, so nothing can be instrumented." + + def __str__(self): + return self._format_message() + + +class DependencyConflictError(Exception): + conflict: DependencyConflict + + def __init__(self, conflict: DependencyConflict): + self.conflict = conflict + + def __str__(self): + return str(self.conflict) + + +def get_dist_dependency_conflicts( + dist: Distribution, +) -> DependencyConflict | None: + instrumentation_deps = [] + instrumentation_any_deps = [] + extra = "extra" + instruments = "instruments" + instruments_marker = {extra: instruments} + instruments_any = "instruments-any" + instruments_any_marker = {extra: instruments_any} + if dist.requires: + for dep in dist.requires: + if extra not in dep: + continue + if instruments not in dep and instruments_any not in dep: + continue + + req = Requirement(dep) + if req.marker.evaluate(instruments_marker): # type: ignore + instrumentation_deps.append(req) # type: ignore + if req.marker.evaluate(instruments_any_marker): # type: ignore + instrumentation_any_deps.append(req) # type: ignore + return get_dependency_conflicts(instrumentation_deps, instrumentation_any_deps) # type: ignore + + +def get_dependency_conflicts( + deps: Collection[str | Requirement], # Dependencies all of which are required + deps_any: Collection[str | Requirement] | None = None, # Dependencies any of which are required +) -> DependencyConflict | None: + for dep in deps: + if isinstance(dep, Requirement): + req = dep + else: + try: + req = Requirement(dep) + except InvalidRequirement as exc: + _logger.warning( + 'error parsing dependency, reporting as a conflict: "%s" - %s', + dep, + exc, + ) + return DependencyConflict(dep) + + try: + dist_version = version(req.name) + except PackageNotFoundError: + return DependencyConflict(dep) + + if not req.specifier.contains(dist_version): + return DependencyConflict(dep, f"{req.name} {dist_version}") + + # If all the dependencies in "instruments" are present, check "instruments-any" for conflicts. + if deps_any: + return _get_dependency_conflicts_any(deps_any) + return None + + +# This is a helper functions designed to ease reading and meet linting requirements. +def _get_dependency_conflicts_any( + deps_any: Collection[str | Requirement], +) -> DependencyConflict | None: + if not deps_any: + return None + is_dependency_conflict = True + required_any: Collection[str] = [] + found_any: Collection[str] = [] + for dep in deps_any: + if isinstance(dep, Requirement): + req = dep + else: + try: + req = Requirement(dep) + except InvalidRequirement as exc: + _logger.warning( + 'error parsing dependency, reporting as a conflict: "%s" - %s', + dep, + exc, + ) + return DependencyConflict(dep) + + try: + dist_version = version(req.name) + except PackageNotFoundError: + required_any.append(str(dep)) + continue + + if req.specifier.contains(dist_version): + # Since only one of the instrumentation_any dependencies is required, there is no dependency conflict. + is_dependency_conflict = False + break + # If the version does not match, add it to the list of unfulfilled requirement options. + required_any.append(str(dep)) + found_any.append(f"{req.name} {dist_version}") + + if is_dependency_conflict: + return DependencyConflict( + required_any=required_any, + found_any=found_any, + ) + return None diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/distro.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/distro.py new file mode 100644 index 0000000..760eb46 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/distro.py @@ -0,0 +1,59 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +# type: ignore + +""" +OpenTelemetry Base Distribution (Distro) +""" + +from abc import ABC, abstractmethod +from logging import getLogger + +from opentelemetry.instrumentation.instrumentor import BaseInstrumentor +from opentelemetry.util._importlib_metadata import EntryPoint + +_LOG = getLogger(__name__) + + +class BaseDistro(ABC): + """An ABC for distro""" + + _instance = None + + def __new__(cls, *args, **kwargs): + if cls._instance is None: + cls._instance = object.__new__(cls, *args, **kwargs) + + return cls._instance + + @abstractmethod + def _configure(self, **kwargs): + """Configure the distribution""" + + def configure(self, **kwargs): + """Configure the distribution""" + self._configure(**kwargs) + + def load_instrumentor( # pylint: disable=no-self-use + self, entry_point: EntryPoint, **kwargs + ): + """Takes an instrumentation entry point and activates it by instantiating + and calling instrument() on it. + This is called for each opentelemetry_instrumentor entry point by auto + instrumentation. + + Distros can override this method to customize the behavior by + inspecting each entry point and configuring them in special ways, + passing additional arguments, load a replacement/fork instead, + skip loading entirely, etc. + """ + instrumentor: BaseInstrumentor = entry_point.load() + instrumentor().instrument(**kwargs) + + +class DefaultDistro(BaseDistro): + def _configure(self, **kwargs): + pass + + +__all__ = ["BaseDistro", "DefaultDistro"] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/environment_variables.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/environment_variables.py new file mode 100644 index 0000000..27773aa --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/environment_variables.py @@ -0,0 +1,32 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +OTEL_PYTHON_DISABLED_INSTRUMENTATIONS = "OTEL_PYTHON_DISABLED_INSTRUMENTATIONS" +""" +.. envvar:: OTEL_PYTHON_DISABLED_INSTRUMENTATIONS +""" + +OTEL_PYTHON_DISTRO = "OTEL_PYTHON_DISTRO" +""" +.. envvar:: OTEL_PYTHON_DISTRO +""" + +OTEL_PYTHON_CONFIGURATOR = "OTEL_PYTHON_CONFIGURATOR" +""" +.. envvar:: OTEL_PYTHON_CONFIGURATOR +""" + +OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH = ( + "OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH" +) +""" +.. envvar:: OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH +""" + +OTEL_SEMCONV_STABILITY_OPT_IN = "OTEL_SEMCONV_STABILITY_OPT_IN" +""" +.. envvar:: OTEL_SEMCONV_STABILITY_OPT_IN + +Opt-in to stable semantic convention signals. +Comma-separated list of signals, e.g. ``http``, ``http/dup``. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/instrumentor.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/instrumentor.py new file mode 100644 index 0000000..4f70ae8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/instrumentor.py @@ -0,0 +1,136 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +# type: ignore + +""" +OpenTelemetry Base Instrumentor +""" + +from __future__ import annotations + +from abc import ABC, abstractmethod +from collections.abc import Collection +from logging import getLogger +from typing import Any + +from opentelemetry.instrumentation._semconv import ( + _OpenTelemetrySemanticConventionStability, +) +from opentelemetry.instrumentation.dependencies import ( + DependencyConflict, + DependencyConflictError, + get_dependency_conflicts, +) + +_LOG = getLogger(__name__) + + +class BaseInstrumentor(ABC): + """An ABC for instrumentors. + + Child classes of this ABC should instrument specific third + party libraries or frameworks either by using the + ``opentelemetry-instrument`` command or by calling their methods + directly. + + Since every third party library or framework is different and has different + instrumentation needs, more methods can be added to the child classes as + needed to provide practical instrumentation to the end user. + """ + + _instance = None + _is_instrumented_by_opentelemetry = False + + def __new__(cls, *args, **kwargs): + if cls._instance is None: + cls._instance = object.__new__(cls) + + return cls._instance + + @property + def is_instrumented_by_opentelemetry(self): + return self._is_instrumented_by_opentelemetry + + @abstractmethod + def instrumentation_dependencies(self) -> Collection[str]: + """Return a list of python packages with versions that the will be instrumented. + + The format should be the same as used in requirements.txt or pyproject.toml. + + For example, if an instrumentation instruments requests 1.x, this method should look + like: + + def instrumentation_dependencies(self) -> Collection[str]: + return ['requests ~= 1.0'] + + This will ensure that the instrumentation will only be used when the specified library + is present in the environment. + """ + + def _instrument(self, **kwargs: Any): + """Instrument the library""" + + @abstractmethod + def _uninstrument(self, **kwargs: Any): + """Uninstrument the library""" + + def _check_dependency_conflicts(self) -> DependencyConflict | None: + dependencies = self.instrumentation_dependencies() + return get_dependency_conflicts(dependencies) + + def instrument(self, **kwargs: Any): + """Instrument the library + + This method will be called without any optional arguments by the + ``opentelemetry-instrument`` command. + + This means that calling this method directly without passing any + optional values should do the very same thing that the + ``opentelemetry-instrument`` command does. + """ + + if self._is_instrumented_by_opentelemetry: + _LOG.warning("Attempting to instrument while already instrumented") + return None + + # check if instrumentor has any missing or conflicting dependencies + skip_dep_check = kwargs.pop("skip_dep_check", False) + raise_exception_on_conflict = kwargs.pop("raise_exception_on_conflict", False) + if not skip_dep_check: + conflict = self._check_dependency_conflicts() + if conflict: + # auto-instrumentation path: don't log conflict as error, instead + # let _load_instrumentors handle the exception + if raise_exception_on_conflict: + raise DependencyConflictError(conflict) + # manual instrumentation path: log the conflict as error + _LOG.error(conflict._format_message(self.__class__.__name__)) + return None + + # initialize semantic conventions opt-in if needed + _OpenTelemetrySemanticConventionStability._initialize() + + result = self._instrument( # pylint: disable=assignment-from-no-return + **kwargs + ) + self._is_instrumented_by_opentelemetry = True + return result + + def uninstrument(self, **kwargs: Any): + """Uninstrument the library + + See ``BaseInstrumentor.instrument`` for more information regarding the + usage of ``kwargs``. + """ + + if self._is_instrumented_by_opentelemetry: + result = self._uninstrument(**kwargs) + self._is_instrumented_by_opentelemetry = False + return result + + _LOG.warning("Attempting to uninstrument while already uninstrumented") + + return None + + +__all__ = ["BaseInstrumentor"] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/log_utils.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/log_utils.py new file mode 100644 index 0000000..a962f77 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/log_utils.py @@ -0,0 +1,65 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from opentelemetry._logs import SeverityNumber + +_STD_TO_OTEL = { + 10: SeverityNumber.DEBUG, + 11: SeverityNumber.DEBUG2, + 12: SeverityNumber.DEBUG3, + 13: SeverityNumber.DEBUG4, + 14: SeverityNumber.DEBUG4, + 15: SeverityNumber.DEBUG4, + 16: SeverityNumber.DEBUG4, + 17: SeverityNumber.DEBUG4, + 18: SeverityNumber.DEBUG4, + 19: SeverityNumber.DEBUG4, + 20: SeverityNumber.INFO, + 21: SeverityNumber.INFO2, + 22: SeverityNumber.INFO3, + 23: SeverityNumber.INFO4, + 24: SeverityNumber.INFO4, + 25: SeverityNumber.INFO4, + 26: SeverityNumber.INFO4, + 27: SeverityNumber.INFO4, + 28: SeverityNumber.INFO4, + 29: SeverityNumber.INFO4, + 30: SeverityNumber.WARN, + 31: SeverityNumber.WARN2, + 32: SeverityNumber.WARN3, + 33: SeverityNumber.WARN4, + 34: SeverityNumber.WARN4, + 35: SeverityNumber.WARN4, + 36: SeverityNumber.WARN4, + 37: SeverityNumber.WARN4, + 38: SeverityNumber.WARN4, + 39: SeverityNumber.WARN4, + 40: SeverityNumber.ERROR, + 41: SeverityNumber.ERROR2, + 42: SeverityNumber.ERROR3, + 43: SeverityNumber.ERROR4, + 44: SeverityNumber.ERROR4, + 45: SeverityNumber.ERROR4, + 46: SeverityNumber.ERROR4, + 47: SeverityNumber.ERROR4, + 48: SeverityNumber.ERROR4, + 49: SeverityNumber.ERROR4, + 50: SeverityNumber.FATAL, + 51: SeverityNumber.FATAL2, + 52: SeverityNumber.FATAL3, + 53: SeverityNumber.FATAL4, +} + + +def std_to_otel(levelno: int) -> SeverityNumber: + """ + Map a Python log level number as defined in + https://docs.python.org/3/library/logging.html#logging-levels + to an OTel log severity number as defined in + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#field-severitynumber + """ + if levelno < 10: + return SeverityNumber.UNSPECIFIED + if levelno > 53: + return SeverityNumber.FATAL4 + return _STD_TO_OTEL[levelno] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/propagators.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/propagators.py new file mode 100644 index 0000000..acfd602 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/propagators.py @@ -0,0 +1,113 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +""" +This module implements experimental propagators to inject trace context +into response carriers. This is useful for server side frameworks that start traces +when server requests and want to share the trace context with the client so the +client can add its spans to the same trace. + +This is part of an upcoming W3C spec and will eventually make it to the Otel spec. + +https://w3c.github.io/trace-context/#trace-context-http-response-headers-format +""" + +from abc import ABC, abstractmethod + +from opentelemetry import trace +from opentelemetry.context.context import Context +from opentelemetry.propagators import textmap +from opentelemetry.trace import format_span_id, format_trace_id + +_HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers" +_RESPONSE_PROPAGATOR = None + + +def get_global_response_propagator(): + return _RESPONSE_PROPAGATOR + + +def set_global_response_propagator(propagator): + global _RESPONSE_PROPAGATOR # pylint:disable=global-statement + _RESPONSE_PROPAGATOR = propagator + + +class Setter(ABC): + @abstractmethod + def set(self, carrier, key, value): + """Inject the provided key value pair in carrier.""" + + +class DictHeaderSetter(Setter): + def set(self, carrier, key, value): # pylint: disable=no-self-use + old_value = carrier.get(key, "") + if old_value: + value = f"{old_value}, {value}" + carrier[key] = value + + +class FuncSetter(Setter): + """FuncSetter converts a function into a valid Setter. Any function that + can set values in a carrier can be converted into a Setter by using + FuncSetter. This is useful when injecting trace context into non-dict + objects such HTTP Response objects for different framework. + + For example, it can be used to create a setter for Falcon response object + as: + + setter = FuncSetter(falcon.api.Response.append_header) + + and then used with the propagator as: + + propagator.inject(falcon_response, setter=setter) + + This would essentially make the propagator call `falcon_response.append_header(key, value)` + """ + + def __init__(self, func): + self._func = func + + def set(self, carrier, key, value): + self._func(carrier, key, value) + + +default_setter = DictHeaderSetter() + + +class ResponsePropagator(ABC): + @abstractmethod + def inject( + self, + carrier: textmap.CarrierT, + context: Context | None = None, + setter: textmap.Setter = default_setter, + ) -> None: + """Injects SpanContext into the HTTP response carrier.""" + + +class TraceResponsePropagator(ResponsePropagator): + """Experimental propagator that injects tracecontext into HTTP responses.""" + + def inject( + self, + carrier: textmap.CarrierT, + context: Context | None = None, + setter: textmap.Setter = default_setter, + ) -> None: + """Injects SpanContext into the HTTP response carrier.""" + span = trace.get_current_span(context) + span_context = span.get_span_context() + if span_context == trace.INVALID_SPAN_CONTEXT: + return + + header_name = "traceresponse" + setter.set( + carrier, + header_name, + f"00-{format_trace_id(span_context.trace_id)}-{format_span_id(span_context.span_id)}-{span_context.trace_flags:02x}", + ) + setter.set( + carrier, + _HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, + header_name, + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/sqlcommenter_utils.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/sqlcommenter_utils.py new file mode 100644 index 0000000..8747bb0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/sqlcommenter_utils.py @@ -0,0 +1,92 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import itertools +import sys +from typing import TYPE_CHECKING, Any + +from opentelemetry import context +from opentelemetry.instrumentation.utils import _url_quote + +if sys.version_info >= (3, 14): + from string.templatelib import Template as _Template +else: + _Template = () + +if TYPE_CHECKING: + if sys.version_info >= (3, 14): + from string.templatelib import Template + else: + from typing import Never + + Template = Never + from typing import overload + + @overload + def _add_sql_comment(sql: str, **meta: Any) -> str: ... + + @overload + def _add_sql_comment(sql: Template, **meta: Any) -> Template: ... + + +def _add_sql_comment(sql: str | Template, **meta: Any) -> str | Template: + """ + Appends comments to the sql statement and returns it + """ + meta.update(**_add_framework_tags()) + comment = _generate_sql_comment(**meta) + + if isinstance(sql, _Template): + last = sql.strings[-1].rstrip() + if last.endswith(";"): + last = last[:-1] + comment + ";" + else: + last += comment + args = [ + *itertools.chain.from_iterable(zip(sql.strings[:-1], sql.interpolations)), + last, + ] + return _Template(*args) + + sql = sql.rstrip() + if sql.endswith(";"): + sql = sql[:-1] + comment + ";" + else: + sql = sql + comment + return sql + + +def _generate_sql_comment(**meta) -> str: + """ + Return a SQL comment with comma delimited key=value pairs created from + **meta kwargs. + """ + key_value_delimiter = "," + + if not meta: # No entries added. + return "" + + # Sort the keywords to ensure that caching works and that testing is + # deterministic. It eases visual inspection as well. + return ( + " /*" + + key_value_delimiter.join( + f"{_url_quote(key)}={_url_quote(value)!r}" for key, value in sorted(meta.items()) if value is not None + ) + + "*/" + ) + + +def _add_framework_tags() -> dict: + """ + Returns orm related tags if any set by the context + """ + + sqlcommenter_framework_values = ( + context.get_value("SQLCOMMENTER_ORM_TAGS_AND_VALUES") + if context.get_value("SQLCOMMENTER_ORM_TAGS_AND_VALUES") + else {} + ) + return sqlcommenter_framework_values diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/__init__.py new file mode 100644 index 0000000..5056cde --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/__init__.py @@ -0,0 +1,169 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +""" +Instrument threading to propagate OpenTelemetry context. + +Usage +----- + +.. code-block:: python + + from opentelemetry.instrumentation.threading import ThreadingInstrumentor + + ThreadingInstrumentor().instrument() + +This library provides instrumentation for the `threading` module to ensure that +the OpenTelemetry context is propagated across threads. It is important to note +that this instrumentation does not produce any telemetry data on its own. It +merely ensures that the context is correctly propagated when threads are used. + + +When instrumented, new threads created using threading.Thread, threading.Timer, +or within futures.ThreadPoolExecutor will have the current OpenTelemetry +context attached, and this context will be re-activated in the thread's +run method or the executor's worker thread." +""" + +from __future__ import annotations + +import threading +from collections.abc import Callable, Collection +from concurrent import futures +from typing import TYPE_CHECKING, Any + +from wrapt import ( + wrap_function_wrapper, # type: ignore[reportUnknownVariableType] +) + +from opentelemetry import context +from opentelemetry.instrumentation.instrumentor import BaseInstrumentor +from opentelemetry.instrumentation.threading.package import _instruments +from opentelemetry.instrumentation.utils import unwrap + +if TYPE_CHECKING: + from typing import Protocol, TypeVar + + R = TypeVar("R") + + class HasOtelContext(Protocol): + _otel_context: context.Context + + +class ThreadingInstrumentor(BaseInstrumentor): + __WRAPPER_START_METHOD = "start" + __WRAPPER_RUN_METHOD = "run" + __WRAPPER_SUBMIT_METHOD = "submit" + + def instrumentation_dependencies(self) -> Collection[str]: + return _instruments + + def _instrument(self, **kwargs: Any): + self._instrument_thread() + self._instrument_timer() + self._instrument_thread_pool() + + def _uninstrument(self, **kwargs: Any): + self._uninstrument_thread() + self._uninstrument_timer() + self._uninstrument_thread_pool() + + @staticmethod + def _instrument_thread(): + wrap_function_wrapper( + threading.Thread, + ThreadingInstrumentor.__WRAPPER_START_METHOD, + ThreadingInstrumentor.__wrap_threading_start, + ) + wrap_function_wrapper( + threading.Thread, + ThreadingInstrumentor.__WRAPPER_RUN_METHOD, + ThreadingInstrumentor.__wrap_threading_run, + ) + + @staticmethod + def _instrument_timer(): + wrap_function_wrapper( + threading.Timer, + ThreadingInstrumentor.__WRAPPER_START_METHOD, + ThreadingInstrumentor.__wrap_threading_start, + ) + wrap_function_wrapper( + threading.Timer, + ThreadingInstrumentor.__WRAPPER_RUN_METHOD, + ThreadingInstrumentor.__wrap_threading_run, + ) + + @staticmethod + def _instrument_thread_pool(): + wrap_function_wrapper( + futures.ThreadPoolExecutor, + ThreadingInstrumentor.__WRAPPER_SUBMIT_METHOD, + ThreadingInstrumentor.__wrap_thread_pool_submit, + ) + + @staticmethod + def _uninstrument_thread(): + unwrap(threading.Thread, ThreadingInstrumentor.__WRAPPER_START_METHOD) + unwrap(threading.Thread, ThreadingInstrumentor.__WRAPPER_RUN_METHOD) + + @staticmethod + def _uninstrument_timer(): + unwrap(threading.Timer, ThreadingInstrumentor.__WRAPPER_START_METHOD) + unwrap(threading.Timer, ThreadingInstrumentor.__WRAPPER_RUN_METHOD) + + @staticmethod + def _uninstrument_thread_pool(): + unwrap( + futures.ThreadPoolExecutor, + ThreadingInstrumentor.__WRAPPER_SUBMIT_METHOD, + ) + + @staticmethod + def __wrap_threading_start( + call_wrapped: Callable[[], None], + instance: HasOtelContext, + args: tuple[()], + kwargs: dict[str, Any], + ) -> None: + instance._otel_context = context.get_current() + return call_wrapped(*args, **kwargs) + + @staticmethod + def __wrap_threading_run( + call_wrapped: Callable[..., R], + instance: HasOtelContext, + args: tuple[Any, ...], + kwargs: dict[str, Any], + ) -> R: + token = None + try: + if hasattr(instance, "_otel_context"): + token = context.attach(instance._otel_context) + return call_wrapped(*args, **kwargs) + finally: + if token is not None: + context.detach(token) + + @staticmethod + def __wrap_thread_pool_submit( + call_wrapped: Callable[..., R], + instance: futures.ThreadPoolExecutor, + args: tuple[Callable[..., Any], ...], + kwargs: dict[str, Any], + ) -> R: + # obtain the original function and wrapped kwargs + original_func = args[0] + otel_context = context.get_current() + + def wrapped_func(*func_args: Any, **func_kwargs: Any) -> R: + token = None + try: + token = context.attach(otel_context) + return original_func(*func_args, **func_kwargs) + finally: + if token is not None: + context.detach(token) + + # replace the original function with the wrapped function + new_args: tuple[Callable[..., Any], ...] = (wrapped_func,) + args[1:] + return call_wrapped(*new_args, **kwargs) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..efed984 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/__pycache__/package.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/__pycache__/package.cpython-312.pyc new file mode 100644 index 0000000..c19c340 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/__pycache__/package.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/__pycache__/version.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/__pycache__/version.cpython-312.pyc new file mode 100644 index 0000000..7be79b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/__pycache__/version.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/package.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/package.py new file mode 100644 index 0000000..68cc3b4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/package.py @@ -0,0 +1,6 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +_instruments = () + +_supports_metrics = False diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/version.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/version.py new file mode 100644 index 0000000..d3741ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/threading/version.py @@ -0,0 +1,4 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +__version__ = "0.66b0" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/utils.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/utils.py new file mode 100644 index 0000000..5af1c52 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/utils.py @@ -0,0 +1,214 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import sys +import urllib.parse +from collections.abc import Generator, Sequence +from contextlib import contextmanager +from importlib import import_module +from re import escape, sub +from typing import Any + +try: + # wrapt 2.0.0+ + from wrapt import BaseObjectProxy # pylint: disable=no-name-in-module +except ImportError: + from wrapt import ObjectProxy as BaseObjectProxy + +from opentelemetry import context, trace + +# pylint: disable=E0611 +# FIXME: fix the importing of these private attributes when the location of the _SUPPRESS_HTTP_INSTRUMENTATION_KEY is defined.= +from opentelemetry.context import ( + _SUPPRESS_HTTP_INSTRUMENTATION_KEY, + _SUPPRESS_INSTRUMENTATION_KEY, +) + +# pylint: disable=E0611 +from opentelemetry.propagate import extract +from opentelemetry.trace import StatusCode +from opentelemetry.trace.propagation.tracecontext import ( + TraceContextTextMapPropagator, +) + +propagator = TraceContextTextMapPropagator() + +_SUPPRESS_INSTRUMENTATION_KEY_PLAIN = "suppress_instrumentation" # Set for backward compatibility + + +def extract_attributes_from_object( + obj: Any, attributes: Sequence[str], existing: dict[str, str] | None = None +) -> dict[str, str]: + extracted: dict[str, str] = {} + if existing: + extracted.update(existing) + for attr in attributes: + value = getattr(obj, attr, None) + if value is not None: + extracted[attr] = str(value) + return extracted + + +def http_status_to_status_code( + status: int, + allow_redirect: bool = True, + server_span: bool = False, +) -> StatusCode: + """Converts an HTTP status code to an OpenTelemetry canonical status code + + Args: + status (int): HTTP status code + """ + # See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#status + if not isinstance(status, int): + return StatusCode.UNSET + + if status < 100: + return StatusCode.ERROR + if status <= 299: + return StatusCode.UNSET + if status <= 399 and allow_redirect: + return StatusCode.UNSET + if status <= 499 and server_span: + return StatusCode.UNSET + return StatusCode.ERROR + + +def unwrap(obj: object, attr: str): + """Given a function that was wrapped by wrapt.wrap_function_wrapper, unwrap it + + The object containing the function to unwrap may be passed as dotted module path string. + + Args: + obj: Object that holds a reference to the wrapped function or dotted import path as string + attr (str): Name of the wrapped function + """ + if isinstance(obj, str): + try: + module_path, class_name = obj.rsplit(".", 1) + except ValueError as exc: + raise ImportError(f"Cannot parse '{obj}' as dotted import path") from exc + if module_path not in sys.modules: + # Was never imported, meaning it could never have been wrapped + return + module = import_module(module_path) + try: + obj = getattr(module, class_name) + except AttributeError as exc: + raise ImportError(f"Cannot import '{class_name}' from '{module}'") from exc + + func = getattr(obj, attr, None) + if func and isinstance(func, BaseObjectProxy) and hasattr(func, "__wrapped__"): + setattr(obj, attr, func.__wrapped__) + + +def _start_internal_or_server_span( + tracer, + span_name, + start_time, + context_carrier, + context_getter, + attributes=None, +): + """Returns internal or server span along with the token which can be used by caller to reset context + + + Args: + tracer : tracer in use by given instrumentation library + span_name (string): name of the span + start_time : start time of the span + context_carrier : object which contains values that are + used to construct a Context. This object + must be paired with an appropriate getter + which understands how to extract a value from it. + context_getter : an object which contains a get function that can retrieve zero + or more values from the carrier and a keys function that can get all the keys + from carrier. + """ + + token = ctx = span_kind = None + if trace.get_current_span() is trace.INVALID_SPAN: + ctx = extract(context_carrier, getter=context_getter) + token = context.attach(ctx) + span_kind = trace.SpanKind.SERVER + else: + ctx = context.get_current() + span_kind = trace.SpanKind.INTERNAL + span = tracer.start_span( + name=span_name, + context=ctx, + kind=span_kind, + start_time=start_time, + attributes=attributes, + ) + return span, token + + +def _url_quote(s: Any) -> str: # pylint: disable=invalid-name + if not isinstance(s, (str, bytes)): + return s + quoted = urllib.parse.quote(s) + # Since SQL uses '%' as a keyword, '%' is a by-product of url quoting + # e.g. foo,bar --> foo%2Cbar + # thus in our quoting, we need to escape it too to finally give + # foo,bar --> foo%%2Cbar + return quoted.replace("%", "%%") + + +def _get_opentelemetry_values() -> dict[str, Any]: + """ + Return the OpenTelemetry Trace and Span IDs if Span ID is set in the + OpenTelemetry execution context. + """ + # Insert the W3C TraceContext generated + _headers: dict[str, Any] = {} + propagator.inject(_headers) + return _headers + + +def _python_path_without_directory(python_path, directory, path_separator): + return sub( + rf"{escape(directory)}{path_separator}(?!$)", + "", + python_path, + ) + + +def is_instrumentation_enabled() -> bool: + return not ( + context.get_value(_SUPPRESS_INSTRUMENTATION_KEY) or context.get_value(_SUPPRESS_INSTRUMENTATION_KEY_PLAIN) + ) + + +def is_http_instrumentation_enabled() -> bool: + return is_instrumentation_enabled() and not context.get_value(_SUPPRESS_HTTP_INSTRUMENTATION_KEY) + + +@contextmanager +def _suppress_instrumentation(*keys: str) -> Generator[None]: + """Suppress instrumentation within the context.""" + ctx = context.get_current() + for key in keys: + ctx = context.set_value(key, True, ctx) + token = context.attach(ctx) + try: + yield + finally: + if token: + context.detach(token) + + +@contextmanager +def suppress_instrumentation() -> Generator[None]: + """Suppress instrumentation within the context.""" + with _suppress_instrumentation(_SUPPRESS_INSTRUMENTATION_KEY, _SUPPRESS_INSTRUMENTATION_KEY_PLAIN): + yield + + +@contextmanager +def suppress_http_instrumentation() -> Generator[None]: + """Suppress instrumentation within the context.""" + with _suppress_instrumentation(_SUPPRESS_HTTP_INSTRUMENTATION_KEY): + yield diff --git a/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/version.py b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/version.py new file mode 100644 index 0000000..d3741ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/instrumentation/version.py @@ -0,0 +1,4 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +__version__ = "0.66b0" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/metrics/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/metrics/__init__.py new file mode 100644 index 0000000..b25a60e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/metrics/__init__.py @@ -0,0 +1,121 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +""" +The OpenTelemetry metrics API describes the classes used to generate +metrics. + +The :class:`.MeterProvider` provides users access to the :class:`.Meter` which in +turn is used to create :class:`.Instrument` objects. The :class:`.Instrument` objects are +used to record measurements. + +This module provides abstract (i.e. unimplemented) classes required for +metrics, and a concrete no-op implementation :class:`.NoOpMeter` that allows applications +to use the API package alone without a supporting implementation. + +To get a meter, you need to provide the package name from which you are +calling the meter APIs to OpenTelemetry by calling `MeterProvider.get_meter` +with the calling instrumentation name and the version of your package. + +The following code shows how to obtain a meter using the global :class:`.MeterProvider`:: + + from opentelemetry.metrics import get_meter + + meter = get_meter("example-meter") + counter = meter.create_counter("example-counter") + +.. versionadded:: 1.10.0 +.. versionchanged:: 1.12.0rc +""" + +from opentelemetry.metrics._internal import ( + Meter, + MeterProvider, + NoOpMeter, + NoOpMeterProvider, + get_meter, + get_meter_provider, + set_meter_provider, +) +from opentelemetry.metrics._internal.instrument import ( + Asynchronous, + CallbackOptions, + CallbackT, + Counter, + Histogram, + Instrument, + NoOpCounter, + NoOpHistogram, + NoOpObservableCounter, + NoOpObservableGauge, + NoOpObservableUpDownCounter, + NoOpUpDownCounter, + ObservableCounter, + ObservableGauge, + ObservableUpDownCounter, + Synchronous, + UpDownCounter, +) +from opentelemetry.metrics._internal.instrument import Gauge as _Gauge +from opentelemetry.metrics._internal.instrument import NoOpGauge as _NoOpGauge +from opentelemetry.metrics._internal.observation import Observation + +for obj in [ + Counter, + Synchronous, + Asynchronous, + CallbackOptions, + _Gauge, + _NoOpGauge, + get_meter_provider, + get_meter, + Histogram, + Meter, + MeterProvider, + Instrument, + NoOpCounter, + NoOpHistogram, + NoOpMeter, + NoOpMeterProvider, + NoOpObservableCounter, + NoOpObservableGauge, + NoOpObservableUpDownCounter, + NoOpUpDownCounter, + ObservableCounter, + ObservableGauge, + ObservableUpDownCounter, + Observation, + set_meter_provider, + UpDownCounter, +]: + obj.__module__ = __name__ + +__all__ = [ + "Asynchronous", + "CallbackOptions", + "CallbackT", + "Counter", + "Histogram", + "Instrument", + "Meter", + "MeterProvider", + "NoOpCounter", + "NoOpHistogram", + "NoOpMeter", + "NoOpMeterProvider", + "NoOpObservableCounter", + "NoOpObservableGauge", + "NoOpObservableUpDownCounter", + "NoOpUpDownCounter", + "ObservableCounter", + "ObservableGauge", + "ObservableUpDownCounter", + "Observation", + "Synchronous", + "UpDownCounter", + "_Gauge", + "_NoOpGauge", + "get_meter", + "get_meter_provider", + "set_meter_provider", +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/metrics/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/metrics/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5e42226 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/metrics/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/__init__.py new file mode 100644 index 0000000..183966b --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/__init__.py @@ -0,0 +1,892 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# pylint: disable=too-many-ancestors + +""" +The OpenTelemetry metrics API describes the classes used to generate +metrics. + +The :class:`.MeterProvider` provides users access to the :class:`.Meter` which in +turn is used to create :class:`.Instrument` objects. The :class:`.Instrument` objects are +used to record measurements. + +This module provides abstract (i.e. unimplemented) classes required for +metrics, and a concrete no-op implementation :class:`.NoOpMeter` that allows applications +to use the API package alone without a supporting implementation. + +To get a meter, you need to provide the package name from which you are +calling the meter APIs to OpenTelemetry by calling `MeterProvider.get_meter` +with the calling instrumentation name and the version of your package. + +The following code shows how to obtain a meter using the global :class:`.MeterProvider`:: + + from opentelemetry.metrics import get_meter + + meter = get_meter("example-meter") + counter = meter.create_counter("example-counter") + +.. versionadded:: 1.10.0 +""" + +import warnings +from abc import ABC, abstractmethod +from collections.abc import Sequence +from dataclasses import dataclass +from logging import getLogger +from os import environ +from threading import Lock +from typing import cast + +from opentelemetry.environment_variables import OTEL_PYTHON_METER_PROVIDER +from opentelemetry.metrics._internal.instrument import ( + CallbackT, + Counter, + Gauge, + Histogram, + NoOpCounter, + NoOpGauge, + NoOpHistogram, + NoOpObservableCounter, + NoOpObservableGauge, + NoOpObservableUpDownCounter, + NoOpUpDownCounter, + ObservableCounter, + ObservableGauge, + ObservableUpDownCounter, + UpDownCounter, + _MetricsHistogramAdvisory, + _ProxyCounter, + _ProxyGauge, + _ProxyHistogram, + _ProxyObservableCounter, + _ProxyObservableGauge, + _ProxyObservableUpDownCounter, + _ProxyUpDownCounter, +) +from opentelemetry.util._once import Once +from opentelemetry.util._providers import _load_provider +from opentelemetry.util.types import ( + Attributes, +) + +_logger = getLogger(__name__) + + +# pylint: disable=invalid-name +_ProxyInstrumentT = ( + _ProxyCounter + | _ProxyHistogram + | _ProxyGauge + | _ProxyObservableCounter + | _ProxyObservableGauge + | _ProxyObservableUpDownCounter + | _ProxyUpDownCounter +) + + +class MeterProvider(ABC): + """ + MeterProvider is the entry point of the API. It provides access to `Meter` instances. + """ + + @abstractmethod + def get_meter( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + attributes: Attributes = None, + ) -> "Meter": + """Returns a `Meter` for use by the given instrumentation library. + + For any two calls it is undefined whether the same or different + `Meter` instances are returned, even for different library names. + + This function may return different `Meter` types (e.g. a no-op meter + vs. a functional meter). + + Args: + name: The name of the instrumenting module. + ``__name__`` should be avoided as this can result in + different meter names if the meters are in different files. + It is better to use a fixed string that can be imported where + needed and used consistently as the name of the meter. + + This should *not* be the name of the module that is + instrumented but the name of the module doing the instrumentation. + E.g., instead of ``"requests"``, use + ``"opentelemetry.instrumentation.requests"``. + + version: Optional. The version string of the + instrumenting library. Usually this should be the same as + ``importlib.metadata.version(instrumenting_library_name)``. + + schema_url: Optional. Specifies the Schema URL of the emitted telemetry. + attributes: Optional. Attributes that are associated with the emitted telemetry. + """ + + +class NoOpMeterProvider(MeterProvider): + """The default MeterProvider used when no MeterProvider implementation is available.""" + + def get_meter( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + attributes: Attributes = None, + ) -> "Meter": + """Returns a NoOpMeter.""" + return NoOpMeter(name, version=version, schema_url=schema_url) + + +class _ProxyMeterProvider(MeterProvider): + def __init__(self) -> None: + self._lock = Lock() + self._meters: list[_ProxyMeter] = [] + self._real_meter_provider: MeterProvider | None = None + + def get_meter( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + attributes: Attributes = None, + ) -> "Meter": + with self._lock: + if self._real_meter_provider is not None: + return self._real_meter_provider.get_meter(name, version, schema_url) + + meter = _ProxyMeter(name, version=version, schema_url=schema_url) + self._meters.append(meter) + return meter + + def on_set_meter_provider(self, meter_provider: MeterProvider) -> None: + with self._lock: + self._real_meter_provider = meter_provider + for meter in self._meters: + meter.on_set_meter_provider(meter_provider) + + +@dataclass +class _InstrumentRegistrationStatus: + instrument_id: str + already_registered: bool + conflict: bool + current_advisory: _MetricsHistogramAdvisory | None + + +class Meter(ABC): + """Handles instrument creation. + + This class provides methods for creating instruments which are then + used to produce measurements. + """ + + def __init__( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + ) -> None: + super().__init__() + self._name = name + self._version = version + self._schema_url = schema_url + self._instrument_ids: dict[str, _MetricsHistogramAdvisory | None] = {} + self._instrument_ids_lock = Lock() + + @property + def name(self) -> str: + """ + The name of the instrumenting module. + """ + return self._name + + @property + def version(self) -> str | None: + """ + The version string of the instrumenting library. + """ + return self._version + + @property + def schema_url(self) -> str | None: + """ + Specifies the Schema URL of the emitted telemetry + """ + return self._schema_url + + def _register_instrument( + self, + name: str, + type_: type, + unit: str, + description: str, + advisory: _MetricsHistogramAdvisory | None = None, + ) -> _InstrumentRegistrationStatus: + """ + Register an instrument with the name, type, unit and description as + identifying keys and the advisory as value. + + Returns a tuple. The first value is the instrument id. + The second value is an `_InstrumentRegistrationStatus` where + `already_registered` is `True` if the instrument has been registered + already. + If `conflict` is set to True the `current_advisory` attribute contains + the registered instrument advisory. + """ + + instrument_id = ",".join([name.strip().lower(), type_.__name__, unit, description]) + + already_registered = False + conflict = False + current_advisory = None + + with self._instrument_ids_lock: + # we are not using get because None is a valid value + already_registered = instrument_id in self._instrument_ids + if already_registered: + current_advisory = self._instrument_ids[instrument_id] + conflict = current_advisory != advisory + else: + self._instrument_ids[instrument_id] = advisory + + return _InstrumentRegistrationStatus( + instrument_id=instrument_id, + already_registered=already_registered, + conflict=conflict, + current_advisory=current_advisory, + ) + + @staticmethod + def _log_instrument_registration_conflict( + name: str, + instrumentation_type: str, + unit: str, + description: str, + status: _InstrumentRegistrationStatus, + ) -> None: + _logger.warning( + "An instrument with name %s, type %s, unit %s and " + "description %s has been created already with a " + "different advisory value %s and will be used instead.", + name, + instrumentation_type, + unit, + description, + status.current_advisory, + ) + + @abstractmethod + def create_counter( + self, + name: str, + unit: str = "", + description: str = "", + ) -> Counter: + """Creates a `Counter` instrument + + Args: + name: The name of the instrument to be created + unit: The unit for observations this instrument reports. For + example, ``By`` for bytes. UCUM units are recommended. + description: A description for this instrument and what it measures. + """ + + @abstractmethod + def create_up_down_counter( + self, + name: str, + unit: str = "", + description: str = "", + ) -> UpDownCounter: + """Creates an `UpDownCounter` instrument + + Args: + name: The name of the instrument to be created + unit: The unit for observations this instrument reports. For + example, ``By`` for bytes. UCUM units are recommended. + description: A description for this instrument and what it measures. + """ + + @abstractmethod + def create_observable_counter( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> ObservableCounter: + """Creates an `ObservableCounter` instrument + + An observable counter observes a monotonically increasing count by calling provided + callbacks which accept a :class:`~opentelemetry.metrics.CallbackOptions` and return + multiple :class:`~opentelemetry.metrics.Observation`. + + For example, an observable counter could be used to report system CPU + time periodically. Here is a basic implementation:: + + def cpu_time_callback( + options: CallbackOptions, + ) -> Iterable[Observation]: + observations = [] + with open("/proc/stat") as procstat: + procstat.readline() # skip the first line + for line in procstat: + if not line.startswith("cpu"): + break + cpu, *states = line.split() + observations.append( + Observation( + int(states[0]) // 100, + {"cpu": cpu, "state": "user"}, + ) + ) + observations.append( + Observation( + int(states[1]) // 100, + {"cpu": cpu, "state": "nice"}, + ) + ) + observations.append( + Observation( + int(states[2]) // 100, + {"cpu": cpu, "state": "system"}, + ) + ) + # ... other states + return observations + + + meter.create_observable_counter( + "system.cpu.time", + callbacks=[cpu_time_callback], + unit="s", + description="CPU time", + ) + + To reduce memory usage, you can use generator callbacks instead of + building the full list:: + + def cpu_time_callback( + options: CallbackOptions, + ) -> Iterable[Observation]: + with open("/proc/stat") as procstat: + procstat.readline() # skip the first line + for line in procstat: + if not line.startswith("cpu"): + break + cpu, *states = line.split() + yield Observation( + int(states[0]) // 100, + {"cpu": cpu, "state": "user"}, + ) + yield Observation( + int(states[1]) // 100, + {"cpu": cpu, "state": "nice"}, + ) + # ... other states + + Alternatively, you can pass a sequence of generators directly instead of a sequence of + callbacks, which each should return iterables of :class:`~opentelemetry.metrics.Observation`:: + + def cpu_time_callback( + states_to_include: set[str], + ) -> Iterable[Iterable[Observation]]: + # accept options sent in from OpenTelemetry + options = yield + while True: + observations = [] + with open("/proc/stat") as procstat: + procstat.readline() # skip the first line + for line in procstat: + if not line.startswith("cpu"): + break + cpu, *states = line.split() + if "user" in states_to_include: + observations.append( + Observation( + int(states[0]) // 100, + {"cpu": cpu, "state": "user"}, + ) + ) + if "nice" in states_to_include: + observations.append( + Observation( + int(states[1]) // 100, + {"cpu": cpu, "state": "nice"}, + ) + ) + # ... other states + # yield the observations and receive the options for next iteration + options = yield observations + + + meter.create_observable_counter( + "system.cpu.time", + callbacks=[cpu_time_callback({"user", "system"})], + unit="s", + description="CPU time", + ) + + The :class:`~opentelemetry.metrics.CallbackOptions` contain a timeout which the + callback should respect. For example if the callback does asynchronous work, like + making HTTP requests, it should respect the timeout:: + + def scrape_http_callback( + options: CallbackOptions, + ) -> Iterable[Observation]: + r = requests.get( + "http://scrapethis.com", + timeout=options.timeout_millis / 10**3, + ) + for value in r.json(): + yield Observation(value) + + Args: + name: The name of the instrument to be created + callbacks: A sequence of callbacks that return an iterable of + :class:`~opentelemetry.metrics.Observation`. Alternatively, can be a sequence of generators that each + yields iterables of :class:`~opentelemetry.metrics.Observation`. + unit: The unit for observations this instrument reports. For + example, ``By`` for bytes. UCUM units are recommended. + description: A description for this instrument and what it measures. + """ + + @abstractmethod + def create_histogram( + self, + name: str, + unit: str = "", + description: str = "", + *, + explicit_bucket_boundaries_advisory: Sequence[float] | None = None, + ) -> Histogram: + """Creates a :class:`~opentelemetry.metrics.Histogram` instrument + + Args: + name: The name of the instrument to be created + unit: The unit for observations this instrument reports. For + example, ``By`` for bytes. UCUM units are recommended. + description: A description for this instrument and what it measures. + """ + + def create_gauge( # type: ignore # pylint: disable=no-self-use + self, + name: str, + unit: str = "", + description: str = "", + ) -> Gauge: # pyright: ignore[reportReturnType] + """Creates a ``Gauge`` instrument + + Args: + name: The name of the instrument to be created + unit: The unit for observations this instrument reports. For + example, ``By`` for bytes. UCUM units are recommended. + description: A description for this instrument and what it measures. + """ + warnings.warn("create_gauge() is not implemented and will be a no-op") + + @abstractmethod + def create_observable_gauge( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> ObservableGauge: + """Creates an `ObservableGauge` instrument + + Args: + name: The name of the instrument to be created + callbacks: A sequence of callbacks that return an iterable of + :class:`~opentelemetry.metrics.Observation`. Alternatively, can be a generator that yields iterables + of :class:`~opentelemetry.metrics.Observation`. + unit: The unit for observations this instrument reports. For + example, ``By`` for bytes. UCUM units are recommended. + description: A description for this instrument and what it measures. + """ + + @abstractmethod + def create_observable_up_down_counter( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> ObservableUpDownCounter: + """Creates an `ObservableUpDownCounter` instrument + + Args: + name: The name of the instrument to be created + callbacks: A sequence of callbacks that return an iterable of + :class:`~opentelemetry.metrics.Observation`. Alternatively, can be a generator that yields iterables + of :class:`~opentelemetry.metrics.Observation`. + unit: The unit for observations this instrument reports. For + example, ``By`` for bytes. UCUM units are recommended. + description: A description for this instrument and what it measures. + """ + + +class _ProxyMeter(Meter): + def __init__( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + ) -> None: + super().__init__(name, version=version, schema_url=schema_url) + self._lock = Lock() + self._instruments: list[_ProxyInstrumentT] = [] + self._real_meter: Meter | None = None + + def on_set_meter_provider(self, meter_provider: MeterProvider) -> None: + """Called when a real meter provider is set on the creating _ProxyMeterProvider + + Creates a real backing meter for this instance and notifies all created + instruments so they can create real backing instruments. + """ + real_meter = meter_provider.get_meter(self._name, self._version, self._schema_url) + + with self._lock: + self._real_meter = real_meter + # notify all proxy instruments of the new meter so they can create + # real instruments to back themselves + for instrument in self._instruments: + instrument.on_meter_set(real_meter) + + def create_counter( + self, + name: str, + unit: str = "", + description: str = "", + ) -> Counter: + with self._lock: + if self._real_meter: + return self._real_meter.create_counter(name, unit, description) + proxy = _ProxyCounter(name, unit, description) + self._instruments.append(proxy) + return proxy + + def create_up_down_counter( + self, + name: str, + unit: str = "", + description: str = "", + ) -> UpDownCounter: + with self._lock: + if self._real_meter: + return self._real_meter.create_up_down_counter(name, unit, description) + proxy = _ProxyUpDownCounter(name, unit, description) + self._instruments.append(proxy) + return proxy + + def create_observable_counter( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> ObservableCounter: + with self._lock: + if self._real_meter: + return self._real_meter.create_observable_counter(name, callbacks, unit, description) + proxy = _ProxyObservableCounter(name, callbacks, unit=unit, description=description) + self._instruments.append(proxy) + return proxy + + def create_histogram( + self, + name: str, + unit: str = "", + description: str = "", + *, + explicit_bucket_boundaries_advisory: Sequence[float] | None = None, + ) -> Histogram: + with self._lock: + if self._real_meter: + return self._real_meter.create_histogram( + name, + unit, + description, + explicit_bucket_boundaries_advisory=explicit_bucket_boundaries_advisory, + ) + proxy = _ProxyHistogram(name, unit, description, explicit_bucket_boundaries_advisory) + self._instruments.append(proxy) + return proxy + + def create_gauge( + self, + name: str, + unit: str = "", + description: str = "", + ) -> Gauge: + with self._lock: + if self._real_meter: + return self._real_meter.create_gauge(name, unit, description) + proxy = _ProxyGauge(name, unit, description) + self._instruments.append(proxy) + return proxy + + def create_observable_gauge( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> ObservableGauge: + with self._lock: + if self._real_meter: + return self._real_meter.create_observable_gauge(name, callbacks, unit, description) + proxy = _ProxyObservableGauge(name, callbacks, unit=unit, description=description) + self._instruments.append(proxy) + return proxy + + def create_observable_up_down_counter( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> ObservableUpDownCounter: + with self._lock: + if self._real_meter: + return self._real_meter.create_observable_up_down_counter( + name, + callbacks, + unit, + description, + ) + proxy = _ProxyObservableUpDownCounter(name, callbacks, unit=unit, description=description) + self._instruments.append(proxy) + return proxy + + +class NoOpMeter(Meter): + """The default Meter used when no Meter implementation is available. + + All operations are no-op. + """ + + def create_counter( + self, + name: str, + unit: str = "", + description: str = "", + ) -> Counter: + """Returns a no-op Counter.""" + status = self._register_instrument(name, NoOpCounter, unit, description) + if status.conflict: + self._log_instrument_registration_conflict( + name, + Counter.__name__, + unit, + description, + status, + ) + + return NoOpCounter(name, unit=unit, description=description) + + def create_gauge( + self, + name: str, + unit: str = "", + description: str = "", + ) -> Gauge: + """Returns a no-op Gauge.""" + status = self._register_instrument(name, NoOpGauge, unit, description) + if status.conflict: + self._log_instrument_registration_conflict( + name, + Gauge.__name__, + unit, + description, + status, + ) + return NoOpGauge(name, unit=unit, description=description) + + def create_up_down_counter( + self, + name: str, + unit: str = "", + description: str = "", + ) -> UpDownCounter: + """Returns a no-op UpDownCounter.""" + status = self._register_instrument(name, NoOpUpDownCounter, unit, description) + if status.conflict: + self._log_instrument_registration_conflict( + name, + UpDownCounter.__name__, + unit, + description, + status, + ) + return NoOpUpDownCounter(name, unit=unit, description=description) + + def create_observable_counter( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> ObservableCounter: + """Returns a no-op ObservableCounter.""" + status = self._register_instrument(name, NoOpObservableCounter, unit, description) + if status.conflict: + self._log_instrument_registration_conflict( + name, + ObservableCounter.__name__, + unit, + description, + status, + ) + return NoOpObservableCounter( + name, + callbacks, + unit=unit, + description=description, + ) + + def create_histogram( + self, + name: str, + unit: str = "", + description: str = "", + *, + explicit_bucket_boundaries_advisory: Sequence[float] | None = None, + ) -> Histogram: + """Returns a no-op Histogram.""" + status = self._register_instrument( + name, + NoOpHistogram, + unit, + description, + _MetricsHistogramAdvisory(explicit_bucket_boundaries=explicit_bucket_boundaries_advisory), + ) + if status.conflict: + self._log_instrument_registration_conflict( + name, + Histogram.__name__, + unit, + description, + status, + ) + return NoOpHistogram( + name, + unit=unit, + description=description, + explicit_bucket_boundaries_advisory=explicit_bucket_boundaries_advisory, + ) + + def create_observable_gauge( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> ObservableGauge: + """Returns a no-op ObservableGauge.""" + status = self._register_instrument(name, NoOpObservableGauge, unit, description) + if status.conflict: + self._log_instrument_registration_conflict( + name, + ObservableGauge.__name__, + unit, + description, + status, + ) + return NoOpObservableGauge( + name, + callbacks, + unit=unit, + description=description, + ) + + def create_observable_up_down_counter( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> ObservableUpDownCounter: + """Returns a no-op ObservableUpDownCounter.""" + status = self._register_instrument(name, NoOpObservableUpDownCounter, unit, description) + if status.conflict: + self._log_instrument_registration_conflict( + name, + ObservableUpDownCounter.__name__, + unit, + description, + status, + ) + return NoOpObservableUpDownCounter( + name, + callbacks, + unit=unit, + description=description, + ) + + +_METER_PROVIDER_SET_ONCE = Once() +_METER_PROVIDER: MeterProvider | None = None +_PROXY_METER_PROVIDER = _ProxyMeterProvider() + + +def get_meter( + name: str, + version: str = "", + meter_provider: MeterProvider | None = None, + schema_url: str | None = None, + attributes: Attributes = None, +) -> "Meter": + """Returns a `Meter` for use by the given instrumentation library. + + This function is a convenience wrapper for + `opentelemetry.metrics.MeterProvider.get_meter`. + + If meter_provider is omitted the current configured one is used. + """ + if meter_provider is None: + meter_provider = get_meter_provider() + return meter_provider.get_meter(name, version, schema_url, attributes) + + +def _set_meter_provider(meter_provider: MeterProvider, log: bool) -> None: + def set_mp() -> None: + global _METER_PROVIDER # pylint: disable=global-statement + _METER_PROVIDER = meter_provider + + # gives all proxies real instruments off the newly set meter provider + _PROXY_METER_PROVIDER.on_set_meter_provider(meter_provider) + + did_set = _METER_PROVIDER_SET_ONCE.do_once(set_mp) + + if log and not did_set: + _logger.warning("Overriding of current MeterProvider is not allowed") + + +def set_meter_provider(meter_provider: MeterProvider) -> None: + """Sets the current global :class:`~.MeterProvider` object. + + This can only be done once, a warning will be logged if any further attempt + is made. + """ + _set_meter_provider(meter_provider, log=True) + + +def get_meter_provider() -> MeterProvider: + """Gets the current global :class:`~.MeterProvider` object.""" + + if _METER_PROVIDER is None: + if OTEL_PYTHON_METER_PROVIDER not in environ: + return _PROXY_METER_PROVIDER + + meter_provider: MeterProvider = _load_provider( # type: ignore + OTEL_PYTHON_METER_PROVIDER, "meter_provider" + ) + _set_meter_provider(meter_provider, log=False) + + # _METER_PROVIDER will have been set by one thread + return cast("MeterProvider", _METER_PROVIDER) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..56cfda8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/__pycache__/instrument.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/__pycache__/instrument.cpython-312.pyc new file mode 100644 index 0000000..d08a967 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/__pycache__/instrument.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/__pycache__/observation.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/__pycache__/observation.cpython-312.pyc new file mode 100644 index 0000000..5b22f28 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/__pycache__/observation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/instrument.py b/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/instrument.py new file mode 100644 index 0000000..5ebe05e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/instrument.py @@ -0,0 +1,540 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# pylint: disable=too-many-ancestors + + +from abc import ABC, abstractmethod +from collections.abc import Callable, Generator, Iterable, Sequence +from dataclasses import dataclass +from logging import getLogger +from re import compile as re_compile +from typing import ( + Generic, + TypeVar, +) + +# pylint: disable=unused-import; needed for typing and sphinx +from opentelemetry import metrics +from opentelemetry.context import Context +from opentelemetry.metrics._internal.observation import Observation +from opentelemetry.util.types import ( + Attributes, +) + +_logger = getLogger(__name__) + +_name_regex = re_compile(r"[a-zA-Z][-_./a-zA-Z0-9]{0,254}") +_unit_regex = re_compile(r"[\x00-\x7F]{0,63}") + + +@dataclass(frozen=True) +class _MetricsHistogramAdvisory: + explicit_bucket_boundaries: Sequence[float] | None = None + + +@dataclass(frozen=True) +class CallbackOptions: + """Options for the callback + + Args: + timeout_millis: Timeout for the callback's execution. If the callback does asynchronous + work (e.g. HTTP requests), it should respect this timeout. + """ + + timeout_millis: float = 10_000 + + +InstrumentT = TypeVar("InstrumentT", bound="Instrument") +# pylint: disable=invalid-name +CallbackT = Callable[[CallbackOptions], Iterable[Observation]] | Generator[Iterable[Observation], CallbackOptions, None] + + +class Instrument(ABC): + """Abstract class that serves as base for all instruments.""" + + @abstractmethod + def __init__( + self, + name: str, + unit: str = "", + description: str = "", + ) -> None: + pass + + @staticmethod + def _check_name_unit_description(name: str, unit: str, description: str) -> dict[str, str | None]: + """ + Checks the following instrument name, unit and description for + compliance with the spec. + + Returns a dict with keys "name", "unit" and "description", the + corresponding values will be the checked strings or `None` if the value + is invalid. If valid, the checked strings should be used instead of the + original values. + """ + + result: dict[str, str | None] = {} + + if _name_regex.fullmatch(name) is not None: + result["name"] = name + else: + result["name"] = None + + if unit is None: + unit = "" + if _unit_regex.fullmatch(unit) is not None: + result["unit"] = unit + else: + result["unit"] = None + + if description is None: + result["description"] = "" + else: + result["description"] = description + + return result + + +class _ProxyInstrument(ABC, Generic[InstrumentT]): + def __init__( + self, + name: str, + unit: str = "", + description: str = "", + ) -> None: + self._name = name + self._unit = unit + self._description = description + self._real_instrument: InstrumentT | None = None + + def on_meter_set(self, meter: "metrics.Meter") -> None: + """Called when a real meter is set on the creating _ProxyMeter""" + + # We don't need any locking on proxy instruments because it's OK if some + # measurements get dropped while a real backing instrument is being + # created. + self._real_instrument = self._create_real_instrument(meter) + + @abstractmethod + def _create_real_instrument(self, meter: "metrics.Meter") -> InstrumentT: + """Create an instance of the real instrument. Implement this.""" + + +class _ProxyAsynchronousInstrument(_ProxyInstrument[InstrumentT]): + def __init__( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> None: + super().__init__(name, unit, description) + self._callbacks = callbacks + + +class Synchronous(Instrument): + """Base class for all synchronous instruments""" + + +class Asynchronous(Instrument): + """Base class for all asynchronous instruments""" + + @abstractmethod + def __init__( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> None: + super().__init__(name, unit=unit, description=description) + + +class Counter(Synchronous): + """A Counter is a synchronous `Instrument` which supports non-negative increments.""" + + @abstractmethod + def add( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ) -> None: + """Records an increment to the counter. + + Args: + amount: The amount to increment the counter by. Must be non-negative. + attributes: Optional set of attributes to associate with the measurement. + context: Optional context to associate with the measurement. If not + provided, the current context is used. + """ + + +class NoOpCounter(Counter): + """No-op implementation of `Counter`.""" + + def __init__( + self, + name: str, + unit: str = "", + description: str = "", + ) -> None: + super().__init__(name, unit=unit, description=description) + + def add( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ) -> None: + return super().add(amount, attributes=attributes, context=context) + + +class _ProxyCounter(_ProxyInstrument[Counter], Counter): + def add( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ) -> None: + if self._real_instrument: + self._real_instrument.add(amount, attributes, context) + + def _create_real_instrument(self, meter: "metrics.Meter") -> Counter: + return meter.create_counter( + self._name, + self._unit, + self._description, + ) + + +class UpDownCounter(Synchronous): + """An UpDownCounter is a synchronous `Instrument` which supports increments and decrements.""" + + @abstractmethod + def add( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ) -> None: + """Records an increment or decrement to the counter. + + Unlike `Counter`, the ``amount`` may be negative, allowing the + instrument to track values that go up and down (e.g. number of + active requests, queue depth). + + Args: + amount: The amount to add to the counter. May be positive or negative. + attributes: Optional set of attributes to associate with the measurement. + context: Optional context to associate with the measurement. If not + provided, the current context is used. + """ + + +class NoOpUpDownCounter(UpDownCounter): + """No-op implementation of `UpDownCounter`.""" + + def __init__( + self, + name: str, + unit: str = "", + description: str = "", + ) -> None: + super().__init__(name, unit=unit, description=description) + + def add( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ) -> None: + return super().add(amount, attributes=attributes, context=context) + + +class _ProxyUpDownCounter(_ProxyInstrument[UpDownCounter], UpDownCounter): + def add( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ) -> None: + if self._real_instrument: + self._real_instrument.add(amount, attributes, context) + + def _create_real_instrument(self, meter: "metrics.Meter") -> UpDownCounter: + return meter.create_up_down_counter( + self._name, + self._unit, + self._description, + ) + + +class ObservableCounter(Asynchronous): + """An ObservableCounter is an asynchronous `Instrument` which reports monotonically + increasing value(s) when the instrument is being observed. + """ + + +class NoOpObservableCounter(ObservableCounter): + """No-op implementation of `ObservableCounter`.""" + + def __init__( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> None: + super().__init__( + name, + callbacks, + unit=unit, + description=description, + ) + + +class _ProxyObservableCounter(_ProxyAsynchronousInstrument[ObservableCounter], ObservableCounter): + def _create_real_instrument(self, meter: "metrics.Meter") -> ObservableCounter: + return meter.create_observable_counter( + self._name, + self._callbacks, + self._unit, + self._description, + ) + + +class ObservableUpDownCounter(Asynchronous): + """An ObservableUpDownCounter is an asynchronous `Instrument` which reports additive value(s) (e.g. + the process heap size - it makes sense to report the heap size from multiple processes and sum them + up, so we get the total heap usage) when the instrument is being observed. + """ + + +class NoOpObservableUpDownCounter(ObservableUpDownCounter): + """No-op implementation of `ObservableUpDownCounter`.""" + + def __init__( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> None: + super().__init__( + name, + callbacks, + unit=unit, + description=description, + ) + + +class _ProxyObservableUpDownCounter( + _ProxyAsynchronousInstrument[ObservableUpDownCounter], + ObservableUpDownCounter, +): + def _create_real_instrument(self, meter: "metrics.Meter") -> ObservableUpDownCounter: + return meter.create_observable_up_down_counter( + self._name, + self._callbacks, + self._unit, + self._description, + ) + + +class Histogram(Synchronous): + """Histogram is a synchronous `Instrument` which can be used to report arbitrary values + that are likely to be statistically meaningful. It is intended for statistics such as + histograms, summaries, and percentile. + """ + + @abstractmethod + def __init__( + self, + name: str, + unit: str = "", + description: str = "", + explicit_bucket_boundaries_advisory: Sequence[float] | None = None, + ) -> None: + pass + + @abstractmethod + def record( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ) -> None: + """Records a measurement. + + Used to report measurements that are likely to be statistically + meaningful, such as request durations, payload sizes, or any value + for which a distribution (e.g. percentiles) is useful. + + Args: + amount: The measurement to record. Should be non-negative in most + cases; negative values are only meaningful when the histogram + is used to track signed deltas. + attributes: Optional set of attributes to associate with the measurement. + context: Optional context to associate with the measurement. If not + provided, the current context is used. + """ + + +class NoOpHistogram(Histogram): + """No-op implementation of `Histogram`.""" + + def __init__( + self, + name: str, + unit: str = "", + description: str = "", + explicit_bucket_boundaries_advisory: Sequence[float] | None = None, + ) -> None: + super().__init__( + name, + unit=unit, + description=description, + explicit_bucket_boundaries_advisory=explicit_bucket_boundaries_advisory, + ) + + def record( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ) -> None: + return super().record(amount, attributes=attributes, context=context) + + +class _ProxyHistogram(_ProxyInstrument[Histogram], Histogram): + def __init__( + self, + name: str, + unit: str = "", + description: str = "", + explicit_bucket_boundaries_advisory: Sequence[float] | None = None, + ) -> None: + super().__init__(name, unit=unit, description=description) + self._explicit_bucket_boundaries_advisory = explicit_bucket_boundaries_advisory + + def record( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ) -> None: + if self._real_instrument: + self._real_instrument.record(amount, attributes, context) + + def _create_real_instrument(self, meter: "metrics.Meter") -> Histogram: + return meter.create_histogram( + self._name, + self._unit, + self._description, + explicit_bucket_boundaries_advisory=self._explicit_bucket_boundaries_advisory, + ) + + +class ObservableGauge(Asynchronous): + """Asynchronous Gauge is an asynchronous `Instrument` which reports non-additive value(s) (e.g. + the room temperature - it makes no sense to report the temperature value from multiple rooms + and sum them up) when the instrument is being observed. + """ + + +class NoOpObservableGauge(ObservableGauge): + """No-op implementation of `ObservableGauge`.""" + + def __init__( + self, + name: str, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + ) -> None: + super().__init__( + name, + callbacks, + unit=unit, + description=description, + ) + + +class _ProxyObservableGauge( + _ProxyAsynchronousInstrument[ObservableGauge], + ObservableGauge, +): + def _create_real_instrument(self, meter: "metrics.Meter") -> ObservableGauge: + return meter.create_observable_gauge( + self._name, + self._callbacks, + self._unit, + self._description, + ) + + +class Gauge(Synchronous): + """A Gauge is a synchronous `Instrument` which can be used to record non-additive values as they occur.""" + + @abstractmethod + def set( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ) -> None: + """Records the current value of the gauge. + + The gauge reports the last recorded value when observed. It is + intended for non-additive measurements where only the current + value matters (e.g. CPU utilisation percentage, room temperature). + + Args: + amount: The current value to record. + attributes: Optional set of attributes to associate with the measurement. + context: Optional context to associate with the measurement. If not + provided, the current context is used. + """ + + +class NoOpGauge(Gauge): + """No-op implementation of ``Gauge``.""" + + def __init__( + self, + name: str, + unit: str = "", + description: str = "", + ) -> None: + super().__init__(name, unit=unit, description=description) + + def set( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ) -> None: + return super().set(amount, attributes=attributes, context=context) + + +class _ProxyGauge( + _ProxyInstrument[Gauge], + Gauge, +): + def set( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ) -> None: + if self._real_instrument: + self._real_instrument.set(amount, attributes, context) + + def _create_real_instrument(self, meter: "metrics.Meter") -> Gauge: + return meter.create_gauge( + self._name, + self._unit, + self._description, + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/observation.py b/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/observation.py new file mode 100644 index 0000000..0ee7a31 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/metrics/_internal/observation.py @@ -0,0 +1,51 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from opentelemetry.context import Context +from opentelemetry.util.types import Attributes + + +class Observation: + """A measurement observed in an asynchronous instrument + + Return/yield instances of this class from asynchronous instrument callbacks. + + Args: + value: The float or int measured value + attributes: The measurement's attributes + context: The measurement's context + """ + + def __init__( + self, + value: float, + attributes: Attributes = None, + context: Context | None = None, + ) -> None: + self._value = value + self._attributes = attributes + self._context = context + + @property + def value(self) -> float | int: + return self._value + + @property + def attributes(self) -> Attributes: + return self._attributes + + @property + def context(self) -> Context | None: + return self._context + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, Observation) + and self.value == other.value + and self.attributes == other.attributes + and self.context == other.context + ) + + def __repr__(self) -> str: + return f"Observation(value={self.value}, attributes={self.attributes}, context={self.context})" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/metrics/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/metrics/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/propagate/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/propagate/__init__.py new file mode 100644 index 0000000..15f876f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/propagate/__init__.py @@ -0,0 +1,171 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +""" +API for propagation of context. + +The propagators for the +``opentelemetry.propagators.composite.CompositePropagator`` can be defined +via configuration in the ``OTEL_PROPAGATORS`` environment variable. This +variable should be set to a comma-separated string of names of values for the +``opentelemetry_propagator`` entry point. For example, setting +``OTEL_PROPAGATORS`` to ``tracecontext,baggage`` (which is the default value) +would instantiate +``opentelemetry.propagators.composite.CompositePropagator`` with 2 +propagators, one of type +``opentelemetry.trace.propagation.tracecontext.TraceContextTextMapPropagator`` +and other of type ``opentelemetry.baggage.propagation.W3CBaggagePropagator``. +Notice that these propagator classes are defined as +``opentelemetry_propagator`` entry points in the ``pyproject.toml`` file of +``opentelemetry``. + +Example:: + + import flask + import requests + from opentelemetry import propagate + + + PROPAGATOR = propagate.get_global_textmap() + + + def get_header_from_flask_request(request, key): + return request.headers.get_all(key) + + + def set_header_into_requests_request(request: requests.Request, key: str, value: str): + request.headers[key] = value + + + def example_route(): + context = PROPAGATOR.extract(get_header_from_flask_request, flask.request) + request_to_downstream = requests.Request("GET", "http://httpbin.org/get") + PROPAGATOR.inject( + set_header_into_requests_request, + request_to_downstream, + context=context, + ) + session = requests.Session() + session.send(request_to_downstream.prepare()) + + +.. _Propagation API Specification: + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/api-propagators.md +""" + +from logging import getLogger +from os import environ + +from opentelemetry.context.context import Context +from opentelemetry.environment_variables import OTEL_PROPAGATORS +from opentelemetry.propagators import composite, textmap + +logger = getLogger(__name__) + + +def extract( + carrier: textmap.CarrierT, + context: Context | None = None, + getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter, +) -> Context: + """Uses the configured propagator to extract a Context from the carrier. + + Args: + getter: an object which contains a get function that can retrieve zero + or more values from the carrier and a keys function that can get all the keys + from carrier. + carrier: and object which contains values that are + used to construct a Context. This object + must be paired with an appropriate getter + which understands how to extract a value from it. + context: an optional Context to use. Defaults to root + context if not set. + """ + return get_global_textmap().extract(carrier, context, getter=getter) + + +def inject( + carrier: textmap.CarrierT, + context: Context | None = None, + setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter, +) -> None: + """Uses the configured propagator to inject a Context into the carrier. + + Args: + carrier: the medium used by Propagators to read + values from and write values to. + Should be paired with setter, which + should know how to set header values on the carrier. + context: An optional Context to use. Defaults to current + context if not set. + setter: An optional `Setter` object that can set values + on the carrier. + """ + get_global_textmap().inject(carrier, context=context, setter=setter) + + +def _load_propagators() -> textmap.TextMapPropagator: + configured = environ.get(OTEL_PROPAGATORS) + if not configured: + # pylint: disable=import-outside-toplevel,no-name-in-module + from opentelemetry.baggage.propagation import ( # noqa: PLC0415 + W3CBaggagePropagator, + ) + + # pylint: disable=import-outside-toplevel,no-name-in-module + from opentelemetry.trace.propagation.tracecontext import ( # noqa: PLC0415 + TraceContextTextMapPropagator, + ) + + return composite.CompositePropagator([TraceContextTextMapPropagator(), W3CBaggagePropagator()]) + + # pylint: disable=import-outside-toplevel,no-name-in-module + from opentelemetry.util._importlib_metadata import ( # noqa: PLC0415 + entry_points, + ) + + _propagators: list[textmap.TextMapPropagator] = [] + for _propagator in configured.split(","): + _propagator = _propagator.strip() + if _propagator.lower() == "none": + logger.debug("OTEL_PROPAGATORS environment variable contains none, removing all propagators") + return composite.CompositePropagator([]) + try: + _propagators.append( + next( # type: ignore + iter( # type: ignore + entry_points( # type: ignore[misc] + group="opentelemetry_propagator", + name=_propagator, + ) + ) + ).load()() + ) + except StopIteration: + raise ValueError(f"Propagator {_propagator} not found. It is either misspelled or not installed.") + except Exception: # pylint: disable=broad-exception-caught + logger.exception("Failed to load propagator: %s", _propagator) + raise + return composite.CompositePropagator(_propagators) + + +_HTTP_TEXT_FORMAT: textmap.TextMapPropagator = _load_propagators() + +# Deprecated: propagators, environ_propagators and propagator names were never intendended to be part of the public API. +propagators: list[textmap.TextMapPropagator] = [] + +environ_propagators = environ.get(OTEL_PROPAGATORS, "tracecontext,baggage") + +for propagator in environ_propagators.split(","): # type: ignore[assignment] + propagator = propagator.strip() + + +def get_global_textmap() -> textmap.TextMapPropagator: + return _HTTP_TEXT_FORMAT + + +def set_global_textmap( + http_text_format: textmap.TextMapPropagator, +) -> None: + global _HTTP_TEXT_FORMAT # pylint:disable=global-statement + _HTTP_TEXT_FORMAT = http_text_format diff --git a/venv/lib/python3.12/site-packages/opentelemetry/propagate/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/propagate/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4197135 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/propagate/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/propagate/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/propagate/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/propagators/__pycache__/_envcarrier.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/propagators/__pycache__/_envcarrier.cpython-312.pyc new file mode 100644 index 0000000..fca0d79 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/propagators/__pycache__/_envcarrier.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/propagators/__pycache__/composite.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/propagators/__pycache__/composite.cpython-312.pyc new file mode 100644 index 0000000..b30de2f Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/propagators/__pycache__/composite.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/propagators/__pycache__/textmap.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/propagators/__pycache__/textmap.cpython-312.pyc new file mode 100644 index 0000000..83d92c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/propagators/__pycache__/textmap.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/propagators/_envcarrier.py b/venv/lib/python3.12/site-packages/opentelemetry/propagators/_envcarrier.py new file mode 100644 index 0000000..c474745 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/propagators/_envcarrier.py @@ -0,0 +1,94 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +"""Environment variable carriers for text map propagators. + +Use :class:`EnvironmentGetter` with the environment mapping to extract from, +usually ``os.environ`` during application or child-process initialization. +Use :class:`EnvironmentSetter` with a mutable environment copy when preparing +the environment for a child process. +""" + +import re +from collections.abc import Mapping, MutableMapping + +from opentelemetry.propagators.textmap import Getter, Setter + + +def _normalize_key(key: str) -> str: + if not key: + return "_" + result = re.sub(r"[^A-Za-z0-9_]", "_", key).upper() + if result and result[0].isdigit(): + result = "_" + result + return result + + +def _is_normalized_key(key: str) -> bool: + if not key: + return False + if "0" <= key[0] <= "9": + return False + return all("A" <= char <= "Z" or "0" <= char <= "9" or char == "_" for char in key) + + +class EnvironmentGetter(Getter[Mapping[str, str]]): + """Getter implementation for extracting context and baggage from environment variables. + + EnvironmentGetter reads from the mapping provided as the carrier, normalizes + requested keys, and provides simple data access without validation. + + Example usage: + getter = EnvironmentGetter() + traceparent = getter.get(os.environ, "traceparent") + """ + + def get(self, carrier: Mapping[str, str], key: str) -> list[str] | None: + """Get a value from the environment carrier for the given key. + + Args: + carrier: Mapping to read environment variables from + key: The key to look up (will be normalized) + + Returns: + A list with a single string value if the key exists, None otherwise. + """ + val = carrier.get(_normalize_key(key)) + if val is None: + return None + return [val] + + def keys(self, carrier: Mapping[str, str]) -> list[str]: + """Get all keys from the environment carrier. + + Args: + carrier: Mapping to read environment variable keys from + + Returns: + List of all already-normalized environment variable keys. + """ + return [key for key in carrier.keys() if _is_normalized_key(key)] + + +class EnvironmentSetter(Setter[MutableMapping[str, str]]): + """Setter implementation for building environment variable dictionaries. + + EnvironmentSetter builds a dictionary of environment variables that + can be passed to utilities like subprocess.run() + + Example usage: + setter = EnvironmentSetter() + env_vars = {} + setter.set(env_vars, "traceparent", "00-trace-id-span-id-01") + subprocess.run(myCommand, env=env_vars) + """ + + def set(self, carrier: MutableMapping[str, str], key: str, value: str) -> None: + """Set a value in the carrier dictionary for the given key. + + Args: + carrier: Dictionary to store environment variables + key: The key to set (normalized) + value: The value to set + """ + carrier[_normalize_key(key)] = value diff --git a/venv/lib/python3.12/site-packages/opentelemetry/propagators/composite.py b/venv/lib/python3.12/site-packages/opentelemetry/propagators/composite.py new file mode 100644 index 0000000..769e9f9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/propagators/composite.py @@ -0,0 +1,78 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +import collections.abc +import logging + +from typing_extensions import deprecated + +from opentelemetry.context.context import Context +from opentelemetry.propagators import textmap + +logger = logging.getLogger(__name__) + + +class CompositePropagator(textmap.TextMapPropagator): + """CompositePropagator provides a mechanism for combining multiple + propagators into a single one. + + Args: + propagators: the list of propagators to use + """ + + def __init__(self, propagators: collections.abc.Sequence[textmap.TextMapPropagator]) -> None: + self._propagators = propagators + + def extract( + self, + carrier: textmap.CarrierT, + context: Context | None = None, + getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter, + ) -> Context: + """Run each of the configured propagators with the given context and carrier. + Propagators are run in the order they are configured, if multiple + propagators write the same context key, the propagator later in the list + will override previous propagators. + + See `opentelemetry.propagators.textmap.TextMapPropagator.extract` + """ + for propagator in self._propagators: + context = propagator.extract(carrier, context, getter=getter) + return context # type: ignore + + def inject( + self, + carrier: textmap.CarrierT, + context: Context | None = None, + setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter, + ) -> None: + """Run each of the configured propagators with the given context and carrier. + Propagators are run in the order they are configured, if multiple + propagators write the same carrier key, the propagator later in the list + will override previous propagators. + + See `opentelemetry.propagators.textmap.TextMapPropagator.inject` + """ + for propagator in self._propagators: + propagator.inject(carrier, context, setter=setter) + + @property + def fields(self) -> set[str]: + """Returns a set with the fields set in `inject`. + + See + `opentelemetry.propagators.textmap.TextMapPropagator.fields` + """ + composite_fields = set() + + for propagator in self._propagators: + for field in propagator.fields: + composite_fields.add(field) + + return composite_fields + + +@deprecated("You should use CompositePropagator. Deprecated since version 1.2.0.") +class CompositeHTTPPropagator(CompositePropagator): + """CompositeHTTPPropagator provides a mechanism for combining multiple + propagators into a single one. + """ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/propagators/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/propagators/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/propagators/textmap.py b/venv/lib/python3.12/site-packages/opentelemetry/propagators/textmap.py new file mode 100644 index 0000000..7dc132e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/propagators/textmap.py @@ -0,0 +1,181 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import abc +import typing +from collections.abc import Iterable, Mapping, MutableMapping + +from opentelemetry.context.context import Context + +CarrierT = typing.TypeVar("CarrierT") +# pylint: disable=invalid-name +CarrierValT = list[str] | str + + +class Getter(abc.ABC, typing.Generic[CarrierT]): + """This class implements a Getter that enables extracting propagated + fields from a carrier. + """ + + @abc.abstractmethod + def get(self, carrier: CarrierT, key: str) -> list[str] | None: + """Function that can retrieve zero + or more values from the carrier. In the case that + the value does not exist, returns None. + + Args: + carrier: An object which contains values that are used to + construct a Context. + key: key of a field in carrier. + Returns: first value of the propagation key or None if the key doesn't + exist. + """ + + @abc.abstractmethod + def keys(self, carrier: CarrierT) -> list[str]: + """Function that can retrieve all the keys in a carrier object. + + Args: + carrier: An object which contains values that are + used to construct a Context. + Returns: + list of keys from the carrier. + """ + + +class Setter(abc.ABC, typing.Generic[CarrierT]): + """This class implements a Setter that enables injecting propagated + fields into a carrier. + """ + + @abc.abstractmethod + def set(self, carrier: CarrierT, key: str, value: str) -> None: + """Function that can set a value into a carrier"" + + Args: + carrier: An object which contains values that are used to + construct a Context. + key: key of a field in carrier. + value: value for a field in carrier. + """ + + +class DefaultGetter(Getter[Mapping[str, CarrierValT]]): + def get(self, carrier: Mapping[str, CarrierValT], key: str) -> list[str] | None: + """Getter implementation to retrieve a value from a dictionary. + + Args: + carrier: dictionary in which to get value + key: the key used to get the value + Returns: + A list with a single string with the value if it exists, else None. + """ + val = carrier.get(key, None) + if val is None: + return None + if isinstance(val, Iterable) and not isinstance(val, str): + return list(val) + return [val] + + def keys(self, carrier: Mapping[str, CarrierValT]) -> list[str]: + """Keys implementation that returns all keys from a dictionary.""" + return list(carrier.keys()) + + +default_getter: Getter[CarrierT] = DefaultGetter() # type: ignore + + +class DefaultSetter(Setter[MutableMapping[str, CarrierValT]]): + def set( + self, + carrier: MutableMapping[str, CarrierValT], + key: str, + value: CarrierValT, + ) -> None: + """Setter implementation to set a value into a dictionary. + + Args: + carrier: dictionary in which to set value + key: the key used to set the value + value: the value to set + """ + carrier[key] = value + + +default_setter: Setter[CarrierT] = DefaultSetter() # type: ignore + + +class TextMapPropagator(abc.ABC): + """This class provides an interface that enables extracting and injecting + context into headers of HTTP requests. HTTP frameworks and clients + can integrate with TextMapPropagator by providing the object containing the + headers, and a getter and setter function for the extraction and + injection of values, respectively. + + """ + + @abc.abstractmethod + def extract( + self, + carrier: CarrierT, + context: Context | None = None, + getter: Getter[CarrierT] = default_getter, + ) -> Context: + """Create a Context from values in the carrier. + + The extract function should retrieve values from the carrier + object using getter, and use values to populate a + Context value and return it. + + Args: + getter: a function that can retrieve zero + or more values from the carrier. In the case that + the value does not exist, return an empty list. + carrier: and object which contains values that are + used to construct a Context. This object + must be paired with an appropriate getter + which understands how to extract a value from it. + context: an optional Context to use. Defaults to root + context if not set. + Returns: + A Context with configuration found in the carrier. + + """ + + @abc.abstractmethod + def inject( + self, + carrier: CarrierT, + context: Context | None = None, + setter: Setter[CarrierT] = default_setter, + ) -> None: + """Inject values from a Context into a carrier. + + inject enables the propagation of values into HTTP clients or + other objects which perform an HTTP request. Implementations + should use the `Setter` 's set method to set values on the + carrier. + + Args: + carrier: An object that a place to define HTTP headers. + Should be paired with setter, which should + know how to set header values on the carrier. + context: an optional Context to use. Defaults to current + context if not set. + setter: An optional `Setter` object that can set values + on the carrier. + + """ + + @property + @abc.abstractmethod + def fields(self) -> set[str]: + """ + Gets the fields set in the carrier by the `inject` method. + + If the carrier is reused, its fields that correspond with the ones + present in this attribute should be deleted before calling `inject`. + + Returns: + A set with the fields set in `inject`. + """ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d53fcbf Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..cff63b3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/__pycache__/logs_service_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/__pycache__/logs_service_pb2.cpython-312.pyc new file mode 100644 index 0000000..8f1beea Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/__pycache__/logs_service_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/__pycache__/logs_service_pb2_grpc.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/__pycache__/logs_service_pb2_grpc.cpython-312.pyc new file mode 100644 index 0000000..f5aaafa Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/__pycache__/logs_service_pb2_grpc.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/logs_service_pb2.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/logs_service_pb2.py new file mode 100644 index 0000000..81f124f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/logs_service_pb2.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: opentelemetry/proto/collector/logs/v1/logs_service.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from opentelemetry.proto.logs.v1 import logs_pb2 as opentelemetry_dot_proto_dot_logs_dot_v1_dot_logs__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n8opentelemetry/proto/collector/logs/v1/logs_service.proto\x12%opentelemetry.proto.collector.logs.v1\x1a&opentelemetry/proto/logs/v1/logs.proto\"\\\n\x18\x45xportLogsServiceRequest\x12@\n\rresource_logs\x18\x01 \x03(\x0b\x32).opentelemetry.proto.logs.v1.ResourceLogs\"u\n\x19\x45xportLogsServiceResponse\x12X\n\x0fpartial_success\x18\x01 \x01(\x0b\x32?.opentelemetry.proto.collector.logs.v1.ExportLogsPartialSuccess\"O\n\x18\x45xportLogsPartialSuccess\x12\x1c\n\x14rejected_log_records\x18\x01 \x01(\x03\x12\x15\n\rerror_message\x18\x02 \x01(\t2\x9d\x01\n\x0bLogsService\x12\x8d\x01\n\x06\x45xport\x12?.opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest\x1a@.opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse\"\x00\x42\x98\x01\n(io.opentelemetry.proto.collector.logs.v1B\x10LogsServiceProtoP\x01Z0go.opentelemetry.io/proto/otlp/collector/logs/v1\xaa\x02%OpenTelemetry.Proto.Collector.Logs.V1b\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'opentelemetry.proto.collector.logs.v1.logs_service_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n(io.opentelemetry.proto.collector.logs.v1B\020LogsServiceProtoP\001Z0go.opentelemetry.io/proto/otlp/collector/logs/v1\252\002%OpenTelemetry.Proto.Collector.Logs.V1' + _globals['_EXPORTLOGSSERVICEREQUEST']._serialized_start=139 + _globals['_EXPORTLOGSSERVICEREQUEST']._serialized_end=231 + _globals['_EXPORTLOGSSERVICERESPONSE']._serialized_start=233 + _globals['_EXPORTLOGSSERVICERESPONSE']._serialized_end=350 + _globals['_EXPORTLOGSPARTIALSUCCESS']._serialized_start=352 + _globals['_EXPORTLOGSPARTIALSUCCESS']._serialized_end=431 + _globals['_LOGSSERVICE']._serialized_start=434 + _globals['_LOGSSERVICE']._serialized_end=591 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/logs_service_pb2.pyi b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/logs_service_pb2.pyi new file mode 100644 index 0000000..99e2a0a --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/logs_service_pb2.pyi @@ -0,0 +1,117 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2020, OpenTelemetry Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import opentelemetry.proto.logs.v1.logs_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class ExportLogsServiceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESOURCE_LOGS_FIELD_NUMBER: builtins.int + @property + def resource_logs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.logs.v1.logs_pb2.ResourceLogs]: + """An array of ResourceLogs. + For data coming from a single resource this array will typically contain one + element. Intermediary nodes (such as OpenTelemetry Collector) that receive + data from multiple origins typically batch the data before forwarding further and + in that case this array will contain multiple elements. + """ + def __init__( + self, + *, + resource_logs: collections.abc.Iterable[opentelemetry.proto.logs.v1.logs_pb2.ResourceLogs] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["resource_logs", b"resource_logs"]) -> None: ... + +global___ExportLogsServiceRequest = ExportLogsServiceRequest + +@typing_extensions.final +class ExportLogsServiceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTIAL_SUCCESS_FIELD_NUMBER: builtins.int + @property + def partial_success(self) -> global___ExportLogsPartialSuccess: + """The details of a partially successful export request. + + If the request is only partially accepted + (i.e. when the server accepts only parts of the data and rejects the rest) + the server MUST initialize the `partial_success` field and MUST + set the `rejected_` with the number of items it rejected. + + Servers MAY also make use of the `partial_success` field to convey + warnings/suggestions to senders even when the request was fully accepted. + In such cases, the `rejected_` MUST have a value of `0` and + the `error_message` MUST be non-empty. + + A `partial_success` message with an empty value (rejected_ = 0 and + `error_message` = "") is equivalent to it not being set/present. Senders + SHOULD interpret it the same way as in the full success case. + """ + def __init__( + self, + *, + partial_success: global___ExportLogsPartialSuccess | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["partial_success", b"partial_success"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["partial_success", b"partial_success"]) -> None: ... + +global___ExportLogsServiceResponse = ExportLogsServiceResponse + +@typing_extensions.final +class ExportLogsPartialSuccess(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + REJECTED_LOG_RECORDS_FIELD_NUMBER: builtins.int + ERROR_MESSAGE_FIELD_NUMBER: builtins.int + rejected_log_records: builtins.int + """The number of rejected log records. + + A `rejected_` field holding a `0` value indicates that the + request was fully accepted. + """ + error_message: builtins.str + """A developer-facing human-readable message in English. It should be used + either to explain why the server rejected parts of the data during a partial + success or to convey warnings/suggestions during a full success. The message + should offer guidance on how users can address such issues. + + error_message is an optional field. An error_message with an empty value + is equivalent to it not being set. + """ + def __init__( + self, + *, + rejected_log_records: builtins.int = ..., + error_message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["error_message", b"error_message", "rejected_log_records", b"rejected_log_records"]) -> None: ... + +global___ExportLogsPartialSuccess = ExportLogsPartialSuccess diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/logs_service_pb2_grpc.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/logs_service_pb2_grpc.py new file mode 100644 index 0000000..bb64c98 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/logs/v1/logs_service_pb2_grpc.py @@ -0,0 +1,110 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +from opentelemetry.proto.collector.logs.v1 import logs_service_pb2 as opentelemetry_dot_proto_dot_collector_dot_logs_dot_v1_dot_logs__service__pb2 + +GRPC_GENERATED_VERSION = '1.63.2' +GRPC_VERSION = grpc.__version__ +EXPECTED_ERROR_RELEASE = '1.65.0' +SCHEDULED_RELEASE_DATE = 'June 25, 2024' +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + warnings.warn( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in opentelemetry/proto/collector/logs/v1/logs_service_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + + f' This warning will become an error in {EXPECTED_ERROR_RELEASE},' + + f' scheduled for release on {SCHEDULED_RELEASE_DATE}.', + RuntimeWarning + ) + + +class LogsServiceStub(object): + """Service that can be used to push logs between one Application instrumented with + OpenTelemetry and an collector, or between an collector and a central collector (in this + case logs are sent/received to/from multiple Applications). + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Export = channel.unary_unary( + '/opentelemetry.proto.collector.logs.v1.LogsService/Export', + request_serializer=opentelemetry_dot_proto_dot_collector_dot_logs_dot_v1_dot_logs__service__pb2.ExportLogsServiceRequest.SerializeToString, + response_deserializer=opentelemetry_dot_proto_dot_collector_dot_logs_dot_v1_dot_logs__service__pb2.ExportLogsServiceResponse.FromString, + _registered_method=True) + + +class LogsServiceServicer(object): + """Service that can be used to push logs between one Application instrumented with + OpenTelemetry and an collector, or between an collector and a central collector (in this + case logs are sent/received to/from multiple Applications). + """ + + def Export(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_LogsServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Export': grpc.unary_unary_rpc_method_handler( + servicer.Export, + request_deserializer=opentelemetry_dot_proto_dot_collector_dot_logs_dot_v1_dot_logs__service__pb2.ExportLogsServiceRequest.FromString, + response_serializer=opentelemetry_dot_proto_dot_collector_dot_logs_dot_v1_dot_logs__service__pb2.ExportLogsServiceResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'opentelemetry.proto.collector.logs.v1.LogsService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class LogsService(object): + """Service that can be used to push logs between one Application instrumented with + OpenTelemetry and an collector, or between an collector and a central collector (in this + case logs are sent/received to/from multiple Applications). + """ + + @staticmethod + def Export(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/opentelemetry.proto.collector.logs.v1.LogsService/Export', + opentelemetry_dot_proto_dot_collector_dot_logs_dot_v1_dot_logs__service__pb2.ExportLogsServiceRequest.SerializeToString, + opentelemetry_dot_proto_dot_collector_dot_logs_dot_v1_dot_logs__service__pb2.ExportLogsServiceResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..9f6e98b Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5e518a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/__pycache__/metrics_service_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/__pycache__/metrics_service_pb2.cpython-312.pyc new file mode 100644 index 0000000..8636f48 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/__pycache__/metrics_service_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/__pycache__/metrics_service_pb2_grpc.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/__pycache__/metrics_service_pb2_grpc.cpython-312.pyc new file mode 100644 index 0000000..8ef8301 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/__pycache__/metrics_service_pb2_grpc.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/metrics_service_pb2.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/metrics_service_pb2.py new file mode 100644 index 0000000..6083655 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/metrics_service_pb2.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: opentelemetry/proto/collector/metrics/v1/metrics_service.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from opentelemetry.proto.metrics.v1 import metrics_pb2 as opentelemetry_dot_proto_dot_metrics_dot_v1_dot_metrics__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n>opentelemetry/proto/collector/metrics/v1/metrics_service.proto\x12(opentelemetry.proto.collector.metrics.v1\x1a,opentelemetry/proto/metrics/v1/metrics.proto\"h\n\x1b\x45xportMetricsServiceRequest\x12I\n\x10resource_metrics\x18\x01 \x03(\x0b\x32/.opentelemetry.proto.metrics.v1.ResourceMetrics\"~\n\x1c\x45xportMetricsServiceResponse\x12^\n\x0fpartial_success\x18\x01 \x01(\x0b\x32\x45.opentelemetry.proto.collector.metrics.v1.ExportMetricsPartialSuccess\"R\n\x1b\x45xportMetricsPartialSuccess\x12\x1c\n\x14rejected_data_points\x18\x01 \x01(\x03\x12\x15\n\rerror_message\x18\x02 \x01(\t2\xac\x01\n\x0eMetricsService\x12\x99\x01\n\x06\x45xport\x12\x45.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest\x1a\x46.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse\"\x00\x42\xa4\x01\n+io.opentelemetry.proto.collector.metrics.v1B\x13MetricsServiceProtoP\x01Z3go.opentelemetry.io/proto/otlp/collector/metrics/v1\xaa\x02(OpenTelemetry.Proto.Collector.Metrics.V1b\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'opentelemetry.proto.collector.metrics.v1.metrics_service_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n+io.opentelemetry.proto.collector.metrics.v1B\023MetricsServiceProtoP\001Z3go.opentelemetry.io/proto/otlp/collector/metrics/v1\252\002(OpenTelemetry.Proto.Collector.Metrics.V1' + _globals['_EXPORTMETRICSSERVICEREQUEST']._serialized_start=154 + _globals['_EXPORTMETRICSSERVICEREQUEST']._serialized_end=258 + _globals['_EXPORTMETRICSSERVICERESPONSE']._serialized_start=260 + _globals['_EXPORTMETRICSSERVICERESPONSE']._serialized_end=386 + _globals['_EXPORTMETRICSPARTIALSUCCESS']._serialized_start=388 + _globals['_EXPORTMETRICSPARTIALSUCCESS']._serialized_end=470 + _globals['_METRICSSERVICE']._serialized_start=473 + _globals['_METRICSSERVICE']._serialized_end=645 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/metrics_service_pb2.pyi b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/metrics_service_pb2.pyi new file mode 100644 index 0000000..fe3c44f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/metrics_service_pb2.pyi @@ -0,0 +1,117 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2019, OpenTelemetry Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import opentelemetry.proto.metrics.v1.metrics_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class ExportMetricsServiceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESOURCE_METRICS_FIELD_NUMBER: builtins.int + @property + def resource_metrics(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.metrics.v1.metrics_pb2.ResourceMetrics]: + """An array of ResourceMetrics. + For data coming from a single resource this array will typically contain one + element. Intermediary nodes (such as OpenTelemetry Collector) that receive + data from multiple origins typically batch the data before forwarding further and + in that case this array will contain multiple elements. + """ + def __init__( + self, + *, + resource_metrics: collections.abc.Iterable[opentelemetry.proto.metrics.v1.metrics_pb2.ResourceMetrics] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["resource_metrics", b"resource_metrics"]) -> None: ... + +global___ExportMetricsServiceRequest = ExportMetricsServiceRequest + +@typing_extensions.final +class ExportMetricsServiceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTIAL_SUCCESS_FIELD_NUMBER: builtins.int + @property + def partial_success(self) -> global___ExportMetricsPartialSuccess: + """The details of a partially successful export request. + + If the request is only partially accepted + (i.e. when the server accepts only parts of the data and rejects the rest) + the server MUST initialize the `partial_success` field and MUST + set the `rejected_` with the number of items it rejected. + + Servers MAY also make use of the `partial_success` field to convey + warnings/suggestions to senders even when the request was fully accepted. + In such cases, the `rejected_` MUST have a value of `0` and + the `error_message` MUST be non-empty. + + A `partial_success` message with an empty value (rejected_ = 0 and + `error_message` = "") is equivalent to it not being set/present. Senders + SHOULD interpret it the same way as in the full success case. + """ + def __init__( + self, + *, + partial_success: global___ExportMetricsPartialSuccess | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["partial_success", b"partial_success"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["partial_success", b"partial_success"]) -> None: ... + +global___ExportMetricsServiceResponse = ExportMetricsServiceResponse + +@typing_extensions.final +class ExportMetricsPartialSuccess(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + REJECTED_DATA_POINTS_FIELD_NUMBER: builtins.int + ERROR_MESSAGE_FIELD_NUMBER: builtins.int + rejected_data_points: builtins.int + """The number of rejected data points. + + A `rejected_` field holding a `0` value indicates that the + request was fully accepted. + """ + error_message: builtins.str + """A developer-facing human-readable message in English. It should be used + either to explain why the server rejected parts of the data during a partial + success or to convey warnings/suggestions during a full success. The message + should offer guidance on how users can address such issues. + + error_message is an optional field. An error_message with an empty value + is equivalent to it not being set. + """ + def __init__( + self, + *, + rejected_data_points: builtins.int = ..., + error_message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["error_message", b"error_message", "rejected_data_points", b"rejected_data_points"]) -> None: ... + +global___ExportMetricsPartialSuccess = ExportMetricsPartialSuccess diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/metrics_service_pb2_grpc.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/metrics_service_pb2_grpc.py new file mode 100644 index 0000000..f124bfe --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/metrics/v1/metrics_service_pb2_grpc.py @@ -0,0 +1,110 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +from opentelemetry.proto.collector.metrics.v1 import metrics_service_pb2 as opentelemetry_dot_proto_dot_collector_dot_metrics_dot_v1_dot_metrics__service__pb2 + +GRPC_GENERATED_VERSION = '1.63.2' +GRPC_VERSION = grpc.__version__ +EXPECTED_ERROR_RELEASE = '1.65.0' +SCHEDULED_RELEASE_DATE = 'June 25, 2024' +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + warnings.warn( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in opentelemetry/proto/collector/metrics/v1/metrics_service_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + + f' This warning will become an error in {EXPECTED_ERROR_RELEASE},' + + f' scheduled for release on {SCHEDULED_RELEASE_DATE}.', + RuntimeWarning + ) + + +class MetricsServiceStub(object): + """Service that can be used to push metrics between one Application + instrumented with OpenTelemetry and a collector, or between a collector and a + central collector. + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Export = channel.unary_unary( + '/opentelemetry.proto.collector.metrics.v1.MetricsService/Export', + request_serializer=opentelemetry_dot_proto_dot_collector_dot_metrics_dot_v1_dot_metrics__service__pb2.ExportMetricsServiceRequest.SerializeToString, + response_deserializer=opentelemetry_dot_proto_dot_collector_dot_metrics_dot_v1_dot_metrics__service__pb2.ExportMetricsServiceResponse.FromString, + _registered_method=True) + + +class MetricsServiceServicer(object): + """Service that can be used to push metrics between one Application + instrumented with OpenTelemetry and a collector, or between a collector and a + central collector. + """ + + def Export(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_MetricsServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Export': grpc.unary_unary_rpc_method_handler( + servicer.Export, + request_deserializer=opentelemetry_dot_proto_dot_collector_dot_metrics_dot_v1_dot_metrics__service__pb2.ExportMetricsServiceRequest.FromString, + response_serializer=opentelemetry_dot_proto_dot_collector_dot_metrics_dot_v1_dot_metrics__service__pb2.ExportMetricsServiceResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'opentelemetry.proto.collector.metrics.v1.MetricsService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class MetricsService(object): + """Service that can be used to push metrics between one Application + instrumented with OpenTelemetry and a collector, or between a collector and a + central collector. + """ + + @staticmethod + def Export(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/opentelemetry.proto.collector.metrics.v1.MetricsService/Export', + opentelemetry_dot_proto_dot_collector_dot_metrics_dot_v1_dot_metrics__service__pb2.ExportMetricsServiceRequest.SerializeToString, + opentelemetry_dot_proto_dot_collector_dot_metrics_dot_v1_dot_metrics__service__pb2.ExportMetricsServiceResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/__pycache__/profiles_service_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/__pycache__/profiles_service_pb2.cpython-312.pyc new file mode 100644 index 0000000..1ea5abf Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/__pycache__/profiles_service_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/__pycache__/profiles_service_pb2_grpc.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/__pycache__/profiles_service_pb2_grpc.cpython-312.pyc new file mode 100644 index 0000000..e47426a Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/__pycache__/profiles_service_pb2_grpc.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb2.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb2.py new file mode 100644 index 0000000..9e2f619 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb2.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: opentelemetry/proto/collector/profiles/v1development/profiles_service.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from opentelemetry.proto.profiles.v1development import profiles_pb2 as opentelemetry_dot_proto_dot_profiles_dot_v1development_dot_profiles__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\nKopentelemetry/proto/collector/profiles/v1development/profiles_service.proto\x12\x34opentelemetry.proto.collector.profiles.v1development\x1a\x39opentelemetry/proto/profiles/v1development/profiles.proto\"\xcb\x01\n\x1c\x45xportProfilesServiceRequest\x12W\n\x11resource_profiles\x18\x01 \x03(\x0b\x32<.opentelemetry.proto.profiles.v1development.ResourceProfiles\x12R\n\ndictionary\x18\x02 \x01(\x0b\x32>.opentelemetry.proto.profiles.v1development.ProfilesDictionary\"\x8c\x01\n\x1d\x45xportProfilesServiceResponse\x12k\n\x0fpartial_success\x18\x01 \x01(\x0b\x32R.opentelemetry.proto.collector.profiles.v1development.ExportProfilesPartialSuccess\"P\n\x1c\x45xportProfilesPartialSuccess\x12\x19\n\x11rejected_profiles\x18\x01 \x01(\x03\x12\x15\n\rerror_message\x18\x02 \x01(\t2\xc7\x01\n\x0fProfilesService\x12\xb3\x01\n\x06\x45xport\x12R.opentelemetry.proto.collector.profiles.v1development.ExportProfilesServiceRequest\x1aS.opentelemetry.proto.collector.profiles.v1development.ExportProfilesServiceResponse\"\x00\x42\xc9\x01\n7io.opentelemetry.proto.collector.profiles.v1developmentB\x14ProfilesServiceProtoP\x01Z?go.opentelemetry.io/proto/otlp/collector/profiles/v1development\xaa\x02\x34OpenTelemetry.Proto.Collector.Profiles.V1Developmentb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'opentelemetry.proto.collector.profiles.v1development.profiles_service_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n7io.opentelemetry.proto.collector.profiles.v1developmentB\024ProfilesServiceProtoP\001Z?go.opentelemetry.io/proto/otlp/collector/profiles/v1development\252\0024OpenTelemetry.Proto.Collector.Profiles.V1Development' + _globals['_EXPORTPROFILESSERVICEREQUEST']._serialized_start=193 + _globals['_EXPORTPROFILESSERVICEREQUEST']._serialized_end=396 + _globals['_EXPORTPROFILESSERVICERESPONSE']._serialized_start=399 + _globals['_EXPORTPROFILESSERVICERESPONSE']._serialized_end=539 + _globals['_EXPORTPROFILESPARTIALSUCCESS']._serialized_start=541 + _globals['_EXPORTPROFILESPARTIALSUCCESS']._serialized_end=621 + _globals['_PROFILESSERVICE']._serialized_start=624 + _globals['_PROFILESSERVICE']._serialized_end=823 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb2.pyi b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb2.pyi new file mode 100644 index 0000000..e8b7a82 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb2.pyi @@ -0,0 +1,123 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2023, OpenTelemetry Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import opentelemetry.proto.profiles.v1development.profiles_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class ExportProfilesServiceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESOURCE_PROFILES_FIELD_NUMBER: builtins.int + DICTIONARY_FIELD_NUMBER: builtins.int + @property + def resource_profiles(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.profiles.v1development.profiles_pb2.ResourceProfiles]: + """An array of ResourceProfiles. + For data coming from a single resource this array will typically contain one + element. Intermediary nodes (such as OpenTelemetry Collector) that receive + data from multiple origins typically batch the data before forwarding further and + in that case this array will contain multiple elements. + """ + @property + def dictionary(self) -> opentelemetry.proto.profiles.v1development.profiles_pb2.ProfilesDictionary: + """The reference table containing all data shared by profiles across the message being sent.""" + def __init__( + self, + *, + resource_profiles: collections.abc.Iterable[opentelemetry.proto.profiles.v1development.profiles_pb2.ResourceProfiles] | None = ..., + dictionary: opentelemetry.proto.profiles.v1development.profiles_pb2.ProfilesDictionary | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["dictionary", b"dictionary"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["dictionary", b"dictionary", "resource_profiles", b"resource_profiles"]) -> None: ... + +global___ExportProfilesServiceRequest = ExportProfilesServiceRequest + +@typing_extensions.final +class ExportProfilesServiceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTIAL_SUCCESS_FIELD_NUMBER: builtins.int + @property + def partial_success(self) -> global___ExportProfilesPartialSuccess: + """The details of a partially successful export request. + + If the request is only partially accepted + (i.e. when the server accepts only parts of the data and rejects the rest) + the server MUST initialize the `partial_success` field and MUST + set the `rejected_` with the number of items it rejected. + + Servers MAY also make use of the `partial_success` field to convey + warnings/suggestions to senders even when the request was fully accepted. + In such cases, the `rejected_` MUST have a value of `0` and + the `error_message` MUST be non-empty. + + A `partial_success` message with an empty value (rejected_ = 0 and + `error_message` = "") is equivalent to it not being set/present. Senders + SHOULD interpret it the same way as in the full success case. + """ + def __init__( + self, + *, + partial_success: global___ExportProfilesPartialSuccess | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["partial_success", b"partial_success"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["partial_success", b"partial_success"]) -> None: ... + +global___ExportProfilesServiceResponse = ExportProfilesServiceResponse + +@typing_extensions.final +class ExportProfilesPartialSuccess(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + REJECTED_PROFILES_FIELD_NUMBER: builtins.int + ERROR_MESSAGE_FIELD_NUMBER: builtins.int + rejected_profiles: builtins.int + """The number of rejected profiles. + + A `rejected_` field holding a `0` value indicates that the + request was fully accepted. + """ + error_message: builtins.str + """A developer-facing human-readable message in English. It should be used + either to explain why the server rejected parts of the data during a partial + success or to convey warnings/suggestions during a full success. The message + should offer guidance on how users can address such issues. + + error_message is an optional field. An error_message with an empty value + is equivalent to it not being set. + """ + def __init__( + self, + *, + rejected_profiles: builtins.int = ..., + error_message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["error_message", b"error_message", "rejected_profiles", b"rejected_profiles"]) -> None: ... + +global___ExportProfilesPartialSuccess = ExportProfilesPartialSuccess diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb2_grpc.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb2_grpc.py new file mode 100644 index 0000000..3742ae5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb2_grpc.py @@ -0,0 +1,107 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +from opentelemetry.proto.collector.profiles.v1development import profiles_service_pb2 as opentelemetry_dot_proto_dot_collector_dot_profiles_dot_v1development_dot_profiles__service__pb2 + +GRPC_GENERATED_VERSION = '1.63.2' +GRPC_VERSION = grpc.__version__ +EXPECTED_ERROR_RELEASE = '1.65.0' +SCHEDULED_RELEASE_DATE = 'June 25, 2024' +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + warnings.warn( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in opentelemetry/proto/collector/profiles/v1development/profiles_service_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + + f' This warning will become an error in {EXPECTED_ERROR_RELEASE},' + + f' scheduled for release on {SCHEDULED_RELEASE_DATE}.', + RuntimeWarning + ) + + +class ProfilesServiceStub(object): + """Service that can be used to push profiles between one Application instrumented with + OpenTelemetry and a collector, or between a collector and a central collector. + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Export = channel.unary_unary( + '/opentelemetry.proto.collector.profiles.v1development.ProfilesService/Export', + request_serializer=opentelemetry_dot_proto_dot_collector_dot_profiles_dot_v1development_dot_profiles__service__pb2.ExportProfilesServiceRequest.SerializeToString, + response_deserializer=opentelemetry_dot_proto_dot_collector_dot_profiles_dot_v1development_dot_profiles__service__pb2.ExportProfilesServiceResponse.FromString, + _registered_method=True) + + +class ProfilesServiceServicer(object): + """Service that can be used to push profiles between one Application instrumented with + OpenTelemetry and a collector, or between a collector and a central collector. + """ + + def Export(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_ProfilesServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Export': grpc.unary_unary_rpc_method_handler( + servicer.Export, + request_deserializer=opentelemetry_dot_proto_dot_collector_dot_profiles_dot_v1development_dot_profiles__service__pb2.ExportProfilesServiceRequest.FromString, + response_serializer=opentelemetry_dot_proto_dot_collector_dot_profiles_dot_v1development_dot_profiles__service__pb2.ExportProfilesServiceResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'opentelemetry.proto.collector.profiles.v1development.ProfilesService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class ProfilesService(object): + """Service that can be used to push profiles between one Application instrumented with + OpenTelemetry and a collector, or between a collector and a central collector. + """ + + @staticmethod + def Export(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/opentelemetry.proto.collector.profiles.v1development.ProfilesService/Export', + opentelemetry_dot_proto_dot_collector_dot_profiles_dot_v1development_dot_profiles__service__pb2.ExportProfilesServiceRequest.SerializeToString, + opentelemetry_dot_proto_dot_collector_dot_profiles_dot_v1development_dot_profiles__service__pb2.ExportProfilesServiceResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f532713 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f98b0be Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/__pycache__/trace_service_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/__pycache__/trace_service_pb2.cpython-312.pyc new file mode 100644 index 0000000..dc0d233 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/__pycache__/trace_service_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/__pycache__/trace_service_pb2_grpc.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/__pycache__/trace_service_pb2_grpc.cpython-312.pyc new file mode 100644 index 0000000..7fc4512 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/__pycache__/trace_service_pb2_grpc.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/trace_service_pb2.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/trace_service_pb2.py new file mode 100644 index 0000000..c0ad62b --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/trace_service_pb2.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: opentelemetry/proto/collector/trace/v1/trace_service.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from opentelemetry.proto.trace.v1 import trace_pb2 as opentelemetry_dot_proto_dot_trace_dot_v1_dot_trace__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n:opentelemetry/proto/collector/trace/v1/trace_service.proto\x12&opentelemetry.proto.collector.trace.v1\x1a(opentelemetry/proto/trace/v1/trace.proto\"`\n\x19\x45xportTraceServiceRequest\x12\x43\n\x0eresource_spans\x18\x01 \x03(\x0b\x32+.opentelemetry.proto.trace.v1.ResourceSpans\"x\n\x1a\x45xportTraceServiceResponse\x12Z\n\x0fpartial_success\x18\x01 \x01(\x0b\x32\x41.opentelemetry.proto.collector.trace.v1.ExportTracePartialSuccess\"J\n\x19\x45xportTracePartialSuccess\x12\x16\n\x0erejected_spans\x18\x01 \x01(\x03\x12\x15\n\rerror_message\x18\x02 \x01(\t2\xa2\x01\n\x0cTraceService\x12\x91\x01\n\x06\x45xport\x12\x41.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest\x1a\x42.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse\"\x00\x42\x9c\x01\n)io.opentelemetry.proto.collector.trace.v1B\x11TraceServiceProtoP\x01Z1go.opentelemetry.io/proto/otlp/collector/trace/v1\xaa\x02&OpenTelemetry.Proto.Collector.Trace.V1b\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'opentelemetry.proto.collector.trace.v1.trace_service_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n)io.opentelemetry.proto.collector.trace.v1B\021TraceServiceProtoP\001Z1go.opentelemetry.io/proto/otlp/collector/trace/v1\252\002&OpenTelemetry.Proto.Collector.Trace.V1' + _globals['_EXPORTTRACESERVICEREQUEST']._serialized_start=144 + _globals['_EXPORTTRACESERVICEREQUEST']._serialized_end=240 + _globals['_EXPORTTRACESERVICERESPONSE']._serialized_start=242 + _globals['_EXPORTTRACESERVICERESPONSE']._serialized_end=362 + _globals['_EXPORTTRACEPARTIALSUCCESS']._serialized_start=364 + _globals['_EXPORTTRACEPARTIALSUCCESS']._serialized_end=438 + _globals['_TRACESERVICE']._serialized_start=441 + _globals['_TRACESERVICE']._serialized_end=603 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/trace_service_pb2.pyi b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/trace_service_pb2.pyi new file mode 100644 index 0000000..ceb4db5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/trace_service_pb2.pyi @@ -0,0 +1,117 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2019, OpenTelemetry Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import opentelemetry.proto.trace.v1.trace_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class ExportTraceServiceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESOURCE_SPANS_FIELD_NUMBER: builtins.int + @property + def resource_spans(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.trace.v1.trace_pb2.ResourceSpans]: + """An array of ResourceSpans. + For data coming from a single resource this array will typically contain one + element. Intermediary nodes (such as OpenTelemetry Collector) that receive + data from multiple origins typically batch the data before forwarding further and + in that case this array will contain multiple elements. + """ + def __init__( + self, + *, + resource_spans: collections.abc.Iterable[opentelemetry.proto.trace.v1.trace_pb2.ResourceSpans] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["resource_spans", b"resource_spans"]) -> None: ... + +global___ExportTraceServiceRequest = ExportTraceServiceRequest + +@typing_extensions.final +class ExportTraceServiceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTIAL_SUCCESS_FIELD_NUMBER: builtins.int + @property + def partial_success(self) -> global___ExportTracePartialSuccess: + """The details of a partially successful export request. + + If the request is only partially accepted + (i.e. when the server accepts only parts of the data and rejects the rest) + the server MUST initialize the `partial_success` field and MUST + set the `rejected_` with the number of items it rejected. + + Servers MAY also make use of the `partial_success` field to convey + warnings/suggestions to senders even when the request was fully accepted. + In such cases, the `rejected_` MUST have a value of `0` and + the `error_message` MUST be non-empty. + + A `partial_success` message with an empty value (rejected_ = 0 and + `error_message` = "") is equivalent to it not being set/present. Senders + SHOULD interpret it the same way as in the full success case. + """ + def __init__( + self, + *, + partial_success: global___ExportTracePartialSuccess | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["partial_success", b"partial_success"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["partial_success", b"partial_success"]) -> None: ... + +global___ExportTraceServiceResponse = ExportTraceServiceResponse + +@typing_extensions.final +class ExportTracePartialSuccess(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + REJECTED_SPANS_FIELD_NUMBER: builtins.int + ERROR_MESSAGE_FIELD_NUMBER: builtins.int + rejected_spans: builtins.int + """The number of rejected spans. + + A `rejected_` field holding a `0` value indicates that the + request was fully accepted. + """ + error_message: builtins.str + """A developer-facing human-readable message in English. It should be used + either to explain why the server rejected parts of the data during a partial + success or to convey warnings/suggestions during a full success. The message + should offer guidance on how users can address such issues. + + error_message is an optional field. An error_message with an empty value + is equivalent to it not being set. + """ + def __init__( + self, + *, + rejected_spans: builtins.int = ..., + error_message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["error_message", b"error_message", "rejected_spans", b"rejected_spans"]) -> None: ... + +global___ExportTracePartialSuccess = ExportTracePartialSuccess diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/trace_service_pb2_grpc.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/trace_service_pb2_grpc.py new file mode 100644 index 0000000..f1cdf03 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/collector/trace/v1/trace_service_pb2_grpc.py @@ -0,0 +1,110 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +from opentelemetry.proto.collector.trace.v1 import trace_service_pb2 as opentelemetry_dot_proto_dot_collector_dot_trace_dot_v1_dot_trace__service__pb2 + +GRPC_GENERATED_VERSION = '1.63.2' +GRPC_VERSION = grpc.__version__ +EXPECTED_ERROR_RELEASE = '1.65.0' +SCHEDULED_RELEASE_DATE = 'June 25, 2024' +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + warnings.warn( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in opentelemetry/proto/collector/trace/v1/trace_service_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + + f' This warning will become an error in {EXPECTED_ERROR_RELEASE},' + + f' scheduled for release on {SCHEDULED_RELEASE_DATE}.', + RuntimeWarning + ) + + +class TraceServiceStub(object): + """Service that can be used to push spans between one Application instrumented with + OpenTelemetry and a collector, or between a collector and a central collector (in this + case spans are sent/received to/from multiple Applications). + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Export = channel.unary_unary( + '/opentelemetry.proto.collector.trace.v1.TraceService/Export', + request_serializer=opentelemetry_dot_proto_dot_collector_dot_trace_dot_v1_dot_trace__service__pb2.ExportTraceServiceRequest.SerializeToString, + response_deserializer=opentelemetry_dot_proto_dot_collector_dot_trace_dot_v1_dot_trace__service__pb2.ExportTraceServiceResponse.FromString, + _registered_method=True) + + +class TraceServiceServicer(object): + """Service that can be used to push spans between one Application instrumented with + OpenTelemetry and a collector, or between a collector and a central collector (in this + case spans are sent/received to/from multiple Applications). + """ + + def Export(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_TraceServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Export': grpc.unary_unary_rpc_method_handler( + servicer.Export, + request_deserializer=opentelemetry_dot_proto_dot_collector_dot_trace_dot_v1_dot_trace__service__pb2.ExportTraceServiceRequest.FromString, + response_serializer=opentelemetry_dot_proto_dot_collector_dot_trace_dot_v1_dot_trace__service__pb2.ExportTraceServiceResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'opentelemetry.proto.collector.trace.v1.TraceService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class TraceService(object): + """Service that can be used to push spans between one Application instrumented with + OpenTelemetry and a collector, or between a collector and a central collector (in this + case spans are sent/received to/from multiple Applications). + """ + + @staticmethod + def Export(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/opentelemetry.proto.collector.trace.v1.TraceService/Export', + opentelemetry_dot_proto_dot_collector_dot_trace_dot_v1_dot_trace__service__pb2.ExportTraceServiceRequest.SerializeToString, + opentelemetry_dot_proto_dot_collector_dot_trace_dot_v1_dot_trace__service__pb2.ExportTraceServiceResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/common/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/common/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/common/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..3d758c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/common/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..8e65dbc Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/__pycache__/common_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/__pycache__/common_pb2.cpython-312.pyc new file mode 100644 index 0000000..e86500f Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/__pycache__/common_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/common_pb2.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/common_pb2.py new file mode 100644 index 0000000..1e816f2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/common_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: opentelemetry/proto/common/v1/common.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n*opentelemetry/proto/common/v1/common.proto\x12\x1dopentelemetry.proto.common.v1\"\xad\x02\n\x08\x41nyValue\x12\x16\n\x0cstring_value\x18\x01 \x01(\tH\x00\x12\x14\n\nbool_value\x18\x02 \x01(\x08H\x00\x12\x13\n\tint_value\x18\x03 \x01(\x03H\x00\x12\x16\n\x0c\x64ouble_value\x18\x04 \x01(\x01H\x00\x12@\n\x0b\x61rray_value\x18\x05 \x01(\x0b\x32).opentelemetry.proto.common.v1.ArrayValueH\x00\x12\x43\n\x0ckvlist_value\x18\x06 \x01(\x0b\x32+.opentelemetry.proto.common.v1.KeyValueListH\x00\x12\x15\n\x0b\x62ytes_value\x18\x07 \x01(\x0cH\x00\x12\x1f\n\x15string_value_strindex\x18\x08 \x01(\x05H\x00\x42\x07\n\x05value\"E\n\nArrayValue\x12\x37\n\x06values\x18\x01 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.AnyValue\"G\n\x0cKeyValueList\x12\x37\n\x06values\x18\x01 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\"e\n\x08KeyValue\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x36\n\x05value\x18\x02 \x01(\x0b\x32\'.opentelemetry.proto.common.v1.AnyValue\x12\x14\n\x0ckey_strindex\x18\x03 \x01(\x05\"\x94\x01\n\x14InstrumentationScope\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12;\n\nattributes\x18\x03 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\x12 \n\x18\x64ropped_attributes_count\x18\x04 \x01(\r\"X\n\tEntityRef\x12\x12\n\nschema_url\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x0f\n\x07id_keys\x18\x03 \x03(\t\x12\x18\n\x10\x64\x65scription_keys\x18\x04 \x03(\tB{\n io.opentelemetry.proto.common.v1B\x0b\x43ommonProtoP\x01Z(go.opentelemetry.io/proto/otlp/common/v1\xaa\x02\x1dOpenTelemetry.Proto.Common.V1b\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'opentelemetry.proto.common.v1.common_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n io.opentelemetry.proto.common.v1B\013CommonProtoP\001Z(go.opentelemetry.io/proto/otlp/common/v1\252\002\035OpenTelemetry.Proto.Common.V1' + _globals['_ANYVALUE']._serialized_start=78 + _globals['_ANYVALUE']._serialized_end=379 + _globals['_ARRAYVALUE']._serialized_start=381 + _globals['_ARRAYVALUE']._serialized_end=450 + _globals['_KEYVALUELIST']._serialized_start=452 + _globals['_KEYVALUELIST']._serialized_end=523 + _globals['_KEYVALUE']._serialized_start=525 + _globals['_KEYVALUE']._serialized_end=626 + _globals['_INSTRUMENTATIONSCOPE']._serialized_start=629 + _globals['_INSTRUMENTATIONSCOPE']._serialized_end=777 + _globals['_ENTITYREF']._serialized_start=779 + _globals['_ENTITYREF']._serialized_end=867 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/common_pb2.pyi b/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/common_pb2.pyi new file mode 100644 index 0000000..205bba7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/common/v1/common_pb2.pyi @@ -0,0 +1,280 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2019, OpenTelemetry Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class AnyValue(google.protobuf.message.Message): + """Represents any type of attribute value. AnyValue may contain a + primitive value such as a string or integer or it may contain an arbitrary nested + object containing arrays, key-value lists and primitives. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + STRING_VALUE_FIELD_NUMBER: builtins.int + BOOL_VALUE_FIELD_NUMBER: builtins.int + INT_VALUE_FIELD_NUMBER: builtins.int + DOUBLE_VALUE_FIELD_NUMBER: builtins.int + ARRAY_VALUE_FIELD_NUMBER: builtins.int + KVLIST_VALUE_FIELD_NUMBER: builtins.int + BYTES_VALUE_FIELD_NUMBER: builtins.int + STRING_VALUE_STRINDEX_FIELD_NUMBER: builtins.int + string_value: builtins.str + bool_value: builtins.bool + int_value: builtins.int + double_value: builtins.float + @property + def array_value(self) -> global___ArrayValue: ... + @property + def kvlist_value(self) -> global___KeyValueList: ... + bytes_value: builtins.bytes + string_value_strindex: builtins.int + """Reference to the string value in ProfilesDictionary.string_table. + + Note: This is currently used exclusively in the Profiling signal. + Implementers of OTLP receivers for signals other than Profiling should + treat the presence of this value as a non-fatal issue. + Log an error or warning indicating an unexpected field intended for the + Profiling signal and process the data as if this value were absent or + empty, ignoring its semantic content for the non-Profiling signal. + + Status: [Development] + """ + def __init__( + self, + *, + string_value: builtins.str = ..., + bool_value: builtins.bool = ..., + int_value: builtins.int = ..., + double_value: builtins.float = ..., + array_value: global___ArrayValue | None = ..., + kvlist_value: global___KeyValueList | None = ..., + bytes_value: builtins.bytes = ..., + string_value_strindex: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["array_value", b"array_value", "bool_value", b"bool_value", "bytes_value", b"bytes_value", "double_value", b"double_value", "int_value", b"int_value", "kvlist_value", b"kvlist_value", "string_value", b"string_value", "string_value_strindex", b"string_value_strindex", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["array_value", b"array_value", "bool_value", b"bool_value", "bytes_value", b"bytes_value", "double_value", b"double_value", "int_value", b"int_value", "kvlist_value", b"kvlist_value", "string_value", b"string_value", "string_value_strindex", b"string_value_strindex", "value", b"value"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["value", b"value"]) -> typing_extensions.Literal["string_value", "bool_value", "int_value", "double_value", "array_value", "kvlist_value", "bytes_value", "string_value_strindex"] | None: ... + +global___AnyValue = AnyValue + +@typing_extensions.final +class ArrayValue(google.protobuf.message.Message): + """ArrayValue is a list of AnyValue messages. We need ArrayValue as a message + since oneof in AnyValue does not allow repeated fields. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VALUES_FIELD_NUMBER: builtins.int + @property + def values(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AnyValue]: + """Array of values. The array may be empty (contain 0 elements).""" + def __init__( + self, + *, + values: collections.abc.Iterable[global___AnyValue] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["values", b"values"]) -> None: ... + +global___ArrayValue = ArrayValue + +@typing_extensions.final +class KeyValueList(google.protobuf.message.Message): + """KeyValueList is a list of KeyValue messages. We need KeyValueList as a message + since `oneof` in AnyValue does not allow repeated fields. Everywhere else where we need + a list of KeyValue messages (e.g. in Span) we use `repeated KeyValue` directly to + avoid unnecessary extra wrapping (which slows down the protocol). The 2 approaches + are semantically equivalent. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VALUES_FIELD_NUMBER: builtins.int + @property + def values(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___KeyValue]: + """A collection of key/value pairs of key-value pairs. The list may be empty (may + contain 0 elements). + + The keys MUST be unique (it is not allowed to have more than one + value with the same key). + The behavior of software that receives duplicated keys can be unpredictable. + """ + def __init__( + self, + *, + values: collections.abc.Iterable[global___KeyValue] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["values", b"values"]) -> None: ... + +global___KeyValueList = KeyValueList + +@typing_extensions.final +class KeyValue(google.protobuf.message.Message): + """Represents a key-value pair that is used to store Span attributes, Link + attributes, etc. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + KEY_STRINDEX_FIELD_NUMBER: builtins.int + key: builtins.str + """The key name of the pair. + key_ref MUST NOT be set if key is used. + """ + @property + def value(self) -> global___AnyValue: + """The value of the pair.""" + key_strindex: builtins.int + """Reference to the string key in ProfilesDictionary.string_table. + key MUST NOT be set if key_strindex is used. + + Note: This is currently used exclusively in the Profiling signal. + Implementers of OTLP receivers for signals other than Profiling should + treat the presence of this key as a non-fatal issue. + Log an error or warning indicating an unexpected field intended for the + Profiling signal and process the data as if this value were absent or + empty, ignoring its semantic content for the non-Profiling signal. + + Status: [Development] + """ + def __init__( + self, + *, + key: builtins.str = ..., + value: global___AnyValue | None = ..., + key_strindex: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "key_strindex", b"key_strindex", "value", b"value"]) -> None: ... + +global___KeyValue = KeyValue + +@typing_extensions.final +class InstrumentationScope(google.protobuf.message.Message): + """InstrumentationScope is a message representing the instrumentation scope information + such as the fully qualified name and version. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + DROPPED_ATTRIBUTES_COUNT_FIELD_NUMBER: builtins.int + name: builtins.str + """A name denoting the Instrumentation scope. + An empty instrumentation scope name means the name is unknown. + """ + version: builtins.str + """Defines the version of the instrumentation scope. + An empty instrumentation scope version means the version is unknown. + """ + @property + def attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___KeyValue]: + """Additional attributes that describe the scope. [Optional]. + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. + """ + dropped_attributes_count: builtins.int + """The number of attributes that were discarded. Attributes + can be discarded because their keys are too long or because there are too many + attributes. If this value is 0, then no attributes were dropped. + """ + def __init__( + self, + *, + name: builtins.str = ..., + version: builtins.str = ..., + attributes: collections.abc.Iterable[global___KeyValue] | None = ..., + dropped_attributes_count: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["attributes", b"attributes", "dropped_attributes_count", b"dropped_attributes_count", "name", b"name", "version", b"version"]) -> None: ... + +global___InstrumentationScope = InstrumentationScope + +@typing_extensions.final +class EntityRef(google.protobuf.message.Message): + """A reference to an Entity. + Entity represents an object of interest associated with produced telemetry: e.g spans, metrics, profiles, or logs. + + Status: [Development] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SCHEMA_URL_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + ID_KEYS_FIELD_NUMBER: builtins.int + DESCRIPTION_KEYS_FIELD_NUMBER: builtins.int + schema_url: builtins.str + """The Schema URL, if known. This is the identifier of the Schema that the entity data + is recorded in. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + + This schema_url applies to the data in this message and to the Resource attributes + referenced by id_keys and description_keys. + TODO: discuss if we are happy with this somewhat complicated definition of what + the schema_url applies to. + + This field obsoletes the schema_url field in ResourceMetrics/ResourceSpans/ResourceLogs. + """ + type: builtins.str + """Defines the type of the entity. MUST not change during the lifetime of the entity. + For example: "service" or "host". This field is required and MUST not be empty + for valid entities. + """ + @property + def id_keys(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """Attribute Keys that identify the entity. + MUST not change during the lifetime of the entity. The Id must contain at least one attribute. + These keys MUST exist in the containing {message}.attributes. + """ + @property + def description_keys(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """Descriptive (non-identifying) attribute keys of the entity. + MAY change over the lifetime of the entity. MAY be empty. + These attribute keys are not part of entity's identity. + These keys MUST exist in the containing {message}.attributes. + """ + def __init__( + self, + *, + schema_url: builtins.str = ..., + type: builtins.str = ..., + id_keys: collections.abc.Iterable[builtins.str] | None = ..., + description_keys: collections.abc.Iterable[builtins.str] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["description_keys", b"description_keys", "id_keys", b"id_keys", "schema_url", b"schema_url", "type", b"type"]) -> None: ... + +global___EntityRef = EntityRef diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/logs/v1/__pycache__/logs_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/logs/v1/__pycache__/logs_pb2.cpython-312.pyc new file mode 100644 index 0000000..9496f89 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/logs/v1/__pycache__/logs_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/logs/v1/logs_pb2.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/logs/v1/logs_pb2.py new file mode 100644 index 0000000..3fe64e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/logs/v1/logs_pb2.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: opentelemetry/proto/logs/v1/logs.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from opentelemetry.proto.common.v1 import common_pb2 as opentelemetry_dot_proto_dot_common_dot_v1_dot_common__pb2 +from opentelemetry.proto.resource.v1 import resource_pb2 as opentelemetry_dot_proto_dot_resource_dot_v1_dot_resource__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&opentelemetry/proto/logs/v1/logs.proto\x12\x1bopentelemetry.proto.logs.v1\x1a*opentelemetry/proto/common/v1/common.proto\x1a.opentelemetry/proto/resource/v1/resource.proto\"L\n\x08LogsData\x12@\n\rresource_logs\x18\x01 \x03(\x0b\x32).opentelemetry.proto.logs.v1.ResourceLogs\"\xa3\x01\n\x0cResourceLogs\x12;\n\x08resource\x18\x01 \x01(\x0b\x32).opentelemetry.proto.resource.v1.Resource\x12:\n\nscope_logs\x18\x02 \x03(\x0b\x32&.opentelemetry.proto.logs.v1.ScopeLogs\x12\x12\n\nschema_url\x18\x03 \x01(\tJ\x06\x08\xe8\x07\x10\xe9\x07\"\xa0\x01\n\tScopeLogs\x12\x42\n\x05scope\x18\x01 \x01(\x0b\x32\x33.opentelemetry.proto.common.v1.InstrumentationScope\x12;\n\x0blog_records\x18\x02 \x03(\x0b\x32&.opentelemetry.proto.logs.v1.LogRecord\x12\x12\n\nschema_url\x18\x03 \x01(\t\"\x83\x03\n\tLogRecord\x12\x16\n\x0etime_unix_nano\x18\x01 \x01(\x06\x12\x1f\n\x17observed_time_unix_nano\x18\x0b \x01(\x06\x12\x44\n\x0fseverity_number\x18\x02 \x01(\x0e\x32+.opentelemetry.proto.logs.v1.SeverityNumber\x12\x15\n\rseverity_text\x18\x03 \x01(\t\x12\x35\n\x04\x62ody\x18\x05 \x01(\x0b\x32\'.opentelemetry.proto.common.v1.AnyValue\x12;\n\nattributes\x18\x06 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\x12 \n\x18\x64ropped_attributes_count\x18\x07 \x01(\r\x12\r\n\x05\x66lags\x18\x08 \x01(\x07\x12\x10\n\x08trace_id\x18\t \x01(\x0c\x12\x0f\n\x07span_id\x18\n \x01(\x0c\x12\x12\n\nevent_name\x18\x0c \x01(\tJ\x04\x08\x04\x10\x05*\xc3\x05\n\x0eSeverityNumber\x12\x1f\n\x1bSEVERITY_NUMBER_UNSPECIFIED\x10\x00\x12\x19\n\x15SEVERITY_NUMBER_TRACE\x10\x01\x12\x1a\n\x16SEVERITY_NUMBER_TRACE2\x10\x02\x12\x1a\n\x16SEVERITY_NUMBER_TRACE3\x10\x03\x12\x1a\n\x16SEVERITY_NUMBER_TRACE4\x10\x04\x12\x19\n\x15SEVERITY_NUMBER_DEBUG\x10\x05\x12\x1a\n\x16SEVERITY_NUMBER_DEBUG2\x10\x06\x12\x1a\n\x16SEVERITY_NUMBER_DEBUG3\x10\x07\x12\x1a\n\x16SEVERITY_NUMBER_DEBUG4\x10\x08\x12\x18\n\x14SEVERITY_NUMBER_INFO\x10\t\x12\x19\n\x15SEVERITY_NUMBER_INFO2\x10\n\x12\x19\n\x15SEVERITY_NUMBER_INFO3\x10\x0b\x12\x19\n\x15SEVERITY_NUMBER_INFO4\x10\x0c\x12\x18\n\x14SEVERITY_NUMBER_WARN\x10\r\x12\x19\n\x15SEVERITY_NUMBER_WARN2\x10\x0e\x12\x19\n\x15SEVERITY_NUMBER_WARN3\x10\x0f\x12\x19\n\x15SEVERITY_NUMBER_WARN4\x10\x10\x12\x19\n\x15SEVERITY_NUMBER_ERROR\x10\x11\x12\x1a\n\x16SEVERITY_NUMBER_ERROR2\x10\x12\x12\x1a\n\x16SEVERITY_NUMBER_ERROR3\x10\x13\x12\x1a\n\x16SEVERITY_NUMBER_ERROR4\x10\x14\x12\x19\n\x15SEVERITY_NUMBER_FATAL\x10\x15\x12\x1a\n\x16SEVERITY_NUMBER_FATAL2\x10\x16\x12\x1a\n\x16SEVERITY_NUMBER_FATAL3\x10\x17\x12\x1a\n\x16SEVERITY_NUMBER_FATAL4\x10\x18*Y\n\x0eLogRecordFlags\x12\x1f\n\x1bLOG_RECORD_FLAGS_DO_NOT_USE\x10\x00\x12&\n!LOG_RECORD_FLAGS_TRACE_FLAGS_MASK\x10\xff\x01\x42s\n\x1eio.opentelemetry.proto.logs.v1B\tLogsProtoP\x01Z&go.opentelemetry.io/proto/otlp/logs/v1\xaa\x02\x1bOpenTelemetry.Proto.Logs.V1b\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'opentelemetry.proto.logs.v1.logs_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\036io.opentelemetry.proto.logs.v1B\tLogsProtoP\001Z&go.opentelemetry.io/proto/otlp/logs/v1\252\002\033OpenTelemetry.Proto.Logs.V1' + _globals['_SEVERITYNUMBER']._serialized_start=961 + _globals['_SEVERITYNUMBER']._serialized_end=1668 + _globals['_LOGRECORDFLAGS']._serialized_start=1670 + _globals['_LOGRECORDFLAGS']._serialized_end=1759 + _globals['_LOGSDATA']._serialized_start=163 + _globals['_LOGSDATA']._serialized_end=239 + _globals['_RESOURCELOGS']._serialized_start=242 + _globals['_RESOURCELOGS']._serialized_end=405 + _globals['_SCOPELOGS']._serialized_start=408 + _globals['_SCOPELOGS']._serialized_end=568 + _globals['_LOGRECORD']._serialized_start=571 + _globals['_LOGRECORD']._serialized_end=958 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/logs/v1/logs_pb2.pyi b/venv/lib/python3.12/site-packages/opentelemetry/proto/logs/v1/logs_pb2.pyi new file mode 100644 index 0000000..03edd53 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/logs/v1/logs_pb2.pyi @@ -0,0 +1,365 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2020, OpenTelemetry Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import opentelemetry.proto.common.v1.common_pb2 +import opentelemetry.proto.resource.v1.resource_pb2 +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _SeverityNumber: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _SeverityNumberEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_SeverityNumber.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SEVERITY_NUMBER_UNSPECIFIED: _SeverityNumber.ValueType # 0 + SEVERITY_NUMBER_TRACE: _SeverityNumber.ValueType # 1 + SEVERITY_NUMBER_TRACE2: _SeverityNumber.ValueType # 2 + SEVERITY_NUMBER_TRACE3: _SeverityNumber.ValueType # 3 + SEVERITY_NUMBER_TRACE4: _SeverityNumber.ValueType # 4 + SEVERITY_NUMBER_DEBUG: _SeverityNumber.ValueType # 5 + SEVERITY_NUMBER_DEBUG2: _SeverityNumber.ValueType # 6 + SEVERITY_NUMBER_DEBUG3: _SeverityNumber.ValueType # 7 + SEVERITY_NUMBER_DEBUG4: _SeverityNumber.ValueType # 8 + SEVERITY_NUMBER_INFO: _SeverityNumber.ValueType # 9 + SEVERITY_NUMBER_INFO2: _SeverityNumber.ValueType # 10 + SEVERITY_NUMBER_INFO3: _SeverityNumber.ValueType # 11 + SEVERITY_NUMBER_INFO4: _SeverityNumber.ValueType # 12 + SEVERITY_NUMBER_WARN: _SeverityNumber.ValueType # 13 + SEVERITY_NUMBER_WARN2: _SeverityNumber.ValueType # 14 + SEVERITY_NUMBER_WARN3: _SeverityNumber.ValueType # 15 + SEVERITY_NUMBER_WARN4: _SeverityNumber.ValueType # 16 + SEVERITY_NUMBER_ERROR: _SeverityNumber.ValueType # 17 + SEVERITY_NUMBER_ERROR2: _SeverityNumber.ValueType # 18 + SEVERITY_NUMBER_ERROR3: _SeverityNumber.ValueType # 19 + SEVERITY_NUMBER_ERROR4: _SeverityNumber.ValueType # 20 + SEVERITY_NUMBER_FATAL: _SeverityNumber.ValueType # 21 + SEVERITY_NUMBER_FATAL2: _SeverityNumber.ValueType # 22 + SEVERITY_NUMBER_FATAL3: _SeverityNumber.ValueType # 23 + SEVERITY_NUMBER_FATAL4: _SeverityNumber.ValueType # 24 + +class SeverityNumber(_SeverityNumber, metaclass=_SeverityNumberEnumTypeWrapper): + """Possible values for LogRecord.SeverityNumber.""" + +SEVERITY_NUMBER_UNSPECIFIED: SeverityNumber.ValueType # 0 +SEVERITY_NUMBER_TRACE: SeverityNumber.ValueType # 1 +SEVERITY_NUMBER_TRACE2: SeverityNumber.ValueType # 2 +SEVERITY_NUMBER_TRACE3: SeverityNumber.ValueType # 3 +SEVERITY_NUMBER_TRACE4: SeverityNumber.ValueType # 4 +SEVERITY_NUMBER_DEBUG: SeverityNumber.ValueType # 5 +SEVERITY_NUMBER_DEBUG2: SeverityNumber.ValueType # 6 +SEVERITY_NUMBER_DEBUG3: SeverityNumber.ValueType # 7 +SEVERITY_NUMBER_DEBUG4: SeverityNumber.ValueType # 8 +SEVERITY_NUMBER_INFO: SeverityNumber.ValueType # 9 +SEVERITY_NUMBER_INFO2: SeverityNumber.ValueType # 10 +SEVERITY_NUMBER_INFO3: SeverityNumber.ValueType # 11 +SEVERITY_NUMBER_INFO4: SeverityNumber.ValueType # 12 +SEVERITY_NUMBER_WARN: SeverityNumber.ValueType # 13 +SEVERITY_NUMBER_WARN2: SeverityNumber.ValueType # 14 +SEVERITY_NUMBER_WARN3: SeverityNumber.ValueType # 15 +SEVERITY_NUMBER_WARN4: SeverityNumber.ValueType # 16 +SEVERITY_NUMBER_ERROR: SeverityNumber.ValueType # 17 +SEVERITY_NUMBER_ERROR2: SeverityNumber.ValueType # 18 +SEVERITY_NUMBER_ERROR3: SeverityNumber.ValueType # 19 +SEVERITY_NUMBER_ERROR4: SeverityNumber.ValueType # 20 +SEVERITY_NUMBER_FATAL: SeverityNumber.ValueType # 21 +SEVERITY_NUMBER_FATAL2: SeverityNumber.ValueType # 22 +SEVERITY_NUMBER_FATAL3: SeverityNumber.ValueType # 23 +SEVERITY_NUMBER_FATAL4: SeverityNumber.ValueType # 24 +global___SeverityNumber = SeverityNumber + +class _LogRecordFlags: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _LogRecordFlagsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_LogRecordFlags.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + LOG_RECORD_FLAGS_DO_NOT_USE: _LogRecordFlags.ValueType # 0 + """The zero value for the enum. Should not be used for comparisons. + Instead use bitwise "and" with the appropriate mask as shown above. + """ + LOG_RECORD_FLAGS_TRACE_FLAGS_MASK: _LogRecordFlags.ValueType # 255 + """Bits 0-7 are used for trace flags.""" + +class LogRecordFlags(_LogRecordFlags, metaclass=_LogRecordFlagsEnumTypeWrapper): + """LogRecordFlags represents constants used to interpret the + LogRecord.flags field, which is protobuf 'fixed32' type and is to + be used as bit-fields. Each non-zero value defined in this enum is + a bit-mask. To extract the bit-field, for example, use an + expression like: + + (logRecord.flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK) + """ + +LOG_RECORD_FLAGS_DO_NOT_USE: LogRecordFlags.ValueType # 0 +"""The zero value for the enum. Should not be used for comparisons. +Instead use bitwise "and" with the appropriate mask as shown above. +""" +LOG_RECORD_FLAGS_TRACE_FLAGS_MASK: LogRecordFlags.ValueType # 255 +"""Bits 0-7 are used for trace flags.""" +global___LogRecordFlags = LogRecordFlags + +@typing_extensions.final +class LogsData(google.protobuf.message.Message): + """LogsData represents the logs data that can be stored in a persistent storage, + OR can be embedded by other protocols that transfer OTLP logs data but do not + implement the OTLP protocol. + + The main difference between this message and collector protocol is that + in this message there will not be any "control" or "metadata" specific to + OTLP protocol. + + When new fields are added into this message, the OTLP request MUST be updated + as well. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESOURCE_LOGS_FIELD_NUMBER: builtins.int + @property + def resource_logs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ResourceLogs]: + """An array of ResourceLogs. + For data coming from a single resource this array will typically contain + one element. Intermediary nodes that receive data from multiple origins + typically batch the data before forwarding further and in that case this + array will contain multiple elements. + """ + def __init__( + self, + *, + resource_logs: collections.abc.Iterable[global___ResourceLogs] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["resource_logs", b"resource_logs"]) -> None: ... + +global___LogsData = LogsData + +@typing_extensions.final +class ResourceLogs(google.protobuf.message.Message): + """A collection of ScopeLogs from a Resource.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESOURCE_FIELD_NUMBER: builtins.int + SCOPE_LOGS_FIELD_NUMBER: builtins.int + SCHEMA_URL_FIELD_NUMBER: builtins.int + @property + def resource(self) -> opentelemetry.proto.resource.v1.resource_pb2.Resource: + """The resource for the logs in this message. + If this field is not set then resource info is unknown. + """ + @property + def scope_logs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ScopeLogs]: + """A list of ScopeLogs that originate from a resource.""" + schema_url: builtins.str + """The Schema URL, if known. This is the identifier of the Schema that the resource data + is recorded in. Notably, the last part of the URL path is the version number of the + schema: http[s]://server[:port]/path/. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + This schema_url applies to the data in the "resource" field. It does not apply + to the data in the "scope_logs" field which have their own schema_url field. + """ + def __init__( + self, + *, + resource: opentelemetry.proto.resource.v1.resource_pb2.Resource | None = ..., + scope_logs: collections.abc.Iterable[global___ScopeLogs] | None = ..., + schema_url: builtins.str = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["resource", b"resource"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["resource", b"resource", "schema_url", b"schema_url", "scope_logs", b"scope_logs"]) -> None: ... + +global___ResourceLogs = ResourceLogs + +@typing_extensions.final +class ScopeLogs(google.protobuf.message.Message): + """A collection of Logs produced by a Scope.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SCOPE_FIELD_NUMBER: builtins.int + LOG_RECORDS_FIELD_NUMBER: builtins.int + SCHEMA_URL_FIELD_NUMBER: builtins.int + @property + def scope(self) -> opentelemetry.proto.common.v1.common_pb2.InstrumentationScope: + """The instrumentation scope information for the logs in this message. + Semantically when InstrumentationScope isn't set, it is equivalent with + an empty instrumentation scope name (unknown). + """ + @property + def log_records(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___LogRecord]: + """A list of log records.""" + schema_url: builtins.str + """The Schema URL, if known. This is the identifier of the Schema that the log data + is recorded in. Notably, the last part of the URL path is the version number of the + schema: http[s]://server[:port]/path/. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + This schema_url applies to the data in the "scope" field and all logs in the + "log_records" field. + """ + def __init__( + self, + *, + scope: opentelemetry.proto.common.v1.common_pb2.InstrumentationScope | None = ..., + log_records: collections.abc.Iterable[global___LogRecord] | None = ..., + schema_url: builtins.str = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["scope", b"scope"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["log_records", b"log_records", "schema_url", b"schema_url", "scope", b"scope"]) -> None: ... + +global___ScopeLogs = ScopeLogs + +@typing_extensions.final +class LogRecord(google.protobuf.message.Message): + """A log record according to OpenTelemetry Log Data Model: + https://github.com/open-telemetry/oteps/blob/main/text/logs/0097-log-data-model.md + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + OBSERVED_TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + SEVERITY_NUMBER_FIELD_NUMBER: builtins.int + SEVERITY_TEXT_FIELD_NUMBER: builtins.int + BODY_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + DROPPED_ATTRIBUTES_COUNT_FIELD_NUMBER: builtins.int + FLAGS_FIELD_NUMBER: builtins.int + TRACE_ID_FIELD_NUMBER: builtins.int + SPAN_ID_FIELD_NUMBER: builtins.int + EVENT_NAME_FIELD_NUMBER: builtins.int + time_unix_nano: builtins.int + """time_unix_nano is the time when the event occurred. + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + Value of 0 indicates unknown or missing timestamp. + """ + observed_time_unix_nano: builtins.int + """Time when the event was observed by the collection system. + For events that originate in OpenTelemetry (e.g. using OpenTelemetry Logging SDK) + this timestamp is typically set at the generation time and is equal to Timestamp. + For events originating externally and collected by OpenTelemetry (e.g. using + Collector) this is the time when OpenTelemetry's code observed the event measured + by the clock of the OpenTelemetry code. This field MUST be set once the event is + observed by OpenTelemetry. + + For converting OpenTelemetry log data to formats that support only one timestamp or + when receiving OpenTelemetry log data by recipients that support only one timestamp + internally the following logic is recommended: + - Use time_unix_nano if it is present, otherwise use observed_time_unix_nano. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + Value of 0 indicates unknown or missing timestamp. + """ + severity_number: global___SeverityNumber.ValueType + """Numerical value of the severity, normalized to values described in Log Data Model. + [Optional]. + """ + severity_text: builtins.str + """The severity text (also known as log level). The original string representation as + it is known at the source. [Optional]. + """ + @property + def body(self) -> opentelemetry.proto.common.v1.common_pb2.AnyValue: + """A value containing the body of the log record. Can be for example a human-readable + string message (including multi-line) describing the event in a free form or it can + be a structured data composed of arrays and maps of other values. [Optional]. + """ + @property + def attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.common.v1.common_pb2.KeyValue]: + """Additional attributes that describe the specific event occurrence. [Optional]. + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. + """ + dropped_attributes_count: builtins.int + flags: builtins.int + """Flags, a bit field. 8 least significant bits are the trace flags as + defined in W3C Trace Context specification. 24 most significant bits are reserved + and must be set to 0. Readers must not assume that 24 most significant bits + will be zero and must correctly mask the bits when reading 8-bit trace flag (use + flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK). [Optional]. + """ + trace_id: builtins.bytes + """A unique identifier for a trace. All logs from the same trace share + the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR + of length other than 16 bytes is considered invalid (empty string in OTLP/JSON + is zero-length and thus is also invalid). + + This field is optional. + + The receivers SHOULD assume that the log record is not associated with a + trace if any of the following is true: + - the field is not present, + - the field contains an invalid value. + """ + span_id: builtins.bytes + """A unique identifier for a span within a trace, assigned when the span + is created. The ID is an 8-byte array. An ID with all zeroes OR of length + other than 8 bytes is considered invalid (empty string in OTLP/JSON + is zero-length and thus is also invalid). + + This field is optional. If the sender specifies a valid span_id then it SHOULD also + specify a valid trace_id. + + The receivers SHOULD assume that the log record is not associated with a + span if any of the following is true: + - the field is not present, + - the field contains an invalid value. + """ + event_name: builtins.str + """A unique identifier of event category/type. + All events with the same event_name are expected to conform to the same + schema for both their attributes and their body. + + Recommended to be fully qualified and short (no longer than 256 characters). + + Presence of event_name on the log record identifies this record + as an event. + + [Optional]. + """ + def __init__( + self, + *, + time_unix_nano: builtins.int = ..., + observed_time_unix_nano: builtins.int = ..., + severity_number: global___SeverityNumber.ValueType = ..., + severity_text: builtins.str = ..., + body: opentelemetry.proto.common.v1.common_pb2.AnyValue | None = ..., + attributes: collections.abc.Iterable[opentelemetry.proto.common.v1.common_pb2.KeyValue] | None = ..., + dropped_attributes_count: builtins.int = ..., + flags: builtins.int = ..., + trace_id: builtins.bytes = ..., + span_id: builtins.bytes = ..., + event_name: builtins.str = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["body", b"body"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["attributes", b"attributes", "body", b"body", "dropped_attributes_count", b"dropped_attributes_count", "event_name", b"event_name", "flags", b"flags", "observed_time_unix_nano", b"observed_time_unix_nano", "severity_number", b"severity_number", "severity_text", b"severity_text", "span_id", b"span_id", "time_unix_nano", b"time_unix_nano", "trace_id", b"trace_id"]) -> None: ... + +global___LogRecord = LogRecord diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..00a9e8e Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b1cf7ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/__pycache__/metrics_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/__pycache__/metrics_pb2.cpython-312.pyc new file mode 100644 index 0000000..27c69cb Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/__pycache__/metrics_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/metrics_pb2.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/metrics_pb2.py new file mode 100644 index 0000000..a337a58 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/metrics_pb2.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: opentelemetry/proto/metrics/v1/metrics.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from opentelemetry.proto.common.v1 import common_pb2 as opentelemetry_dot_proto_dot_common_dot_v1_dot_common__pb2 +from opentelemetry.proto.resource.v1 import resource_pb2 as opentelemetry_dot_proto_dot_resource_dot_v1_dot_resource__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,opentelemetry/proto/metrics/v1/metrics.proto\x12\x1eopentelemetry.proto.metrics.v1\x1a*opentelemetry/proto/common/v1/common.proto\x1a.opentelemetry/proto/resource/v1/resource.proto\"X\n\x0bMetricsData\x12I\n\x10resource_metrics\x18\x01 \x03(\x0b\x32/.opentelemetry.proto.metrics.v1.ResourceMetrics\"\xaf\x01\n\x0fResourceMetrics\x12;\n\x08resource\x18\x01 \x01(\x0b\x32).opentelemetry.proto.resource.v1.Resource\x12\x43\n\rscope_metrics\x18\x02 \x03(\x0b\x32,.opentelemetry.proto.metrics.v1.ScopeMetrics\x12\x12\n\nschema_url\x18\x03 \x01(\tJ\x06\x08\xe8\x07\x10\xe9\x07\"\x9f\x01\n\x0cScopeMetrics\x12\x42\n\x05scope\x18\x01 \x01(\x0b\x32\x33.opentelemetry.proto.common.v1.InstrumentationScope\x12\x37\n\x07metrics\x18\x02 \x03(\x0b\x32&.opentelemetry.proto.metrics.v1.Metric\x12\x12\n\nschema_url\x18\x03 \x01(\t\"\xcd\x03\n\x06Metric\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x0c\n\x04unit\x18\x03 \x01(\t\x12\x36\n\x05gauge\x18\x05 \x01(\x0b\x32%.opentelemetry.proto.metrics.v1.GaugeH\x00\x12\x32\n\x03sum\x18\x07 \x01(\x0b\x32#.opentelemetry.proto.metrics.v1.SumH\x00\x12>\n\thistogram\x18\t \x01(\x0b\x32).opentelemetry.proto.metrics.v1.HistogramH\x00\x12U\n\x15\x65xponential_histogram\x18\n \x01(\x0b\x32\x34.opentelemetry.proto.metrics.v1.ExponentialHistogramH\x00\x12:\n\x07summary\x18\x0b \x01(\x0b\x32\'.opentelemetry.proto.metrics.v1.SummaryH\x00\x12\x39\n\x08metadata\x18\x0c \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValueB\x06\n\x04\x64\x61taJ\x04\x08\x04\x10\x05J\x04\x08\x06\x10\x07J\x04\x08\x08\x10\t\"M\n\x05Gauge\x12\x44\n\x0b\x64\x61ta_points\x18\x01 \x03(\x0b\x32/.opentelemetry.proto.metrics.v1.NumberDataPoint\"\xba\x01\n\x03Sum\x12\x44\n\x0b\x64\x61ta_points\x18\x01 \x03(\x0b\x32/.opentelemetry.proto.metrics.v1.NumberDataPoint\x12W\n\x17\x61ggregation_temporality\x18\x02 \x01(\x0e\x32\x36.opentelemetry.proto.metrics.v1.AggregationTemporality\x12\x14\n\x0cis_monotonic\x18\x03 \x01(\x08\"\xad\x01\n\tHistogram\x12G\n\x0b\x64\x61ta_points\x18\x01 \x03(\x0b\x32\x32.opentelemetry.proto.metrics.v1.HistogramDataPoint\x12W\n\x17\x61ggregation_temporality\x18\x02 \x01(\x0e\x32\x36.opentelemetry.proto.metrics.v1.AggregationTemporality\"\xc3\x01\n\x14\x45xponentialHistogram\x12R\n\x0b\x64\x61ta_points\x18\x01 \x03(\x0b\x32=.opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint\x12W\n\x17\x61ggregation_temporality\x18\x02 \x01(\x0e\x32\x36.opentelemetry.proto.metrics.v1.AggregationTemporality\"P\n\x07Summary\x12\x45\n\x0b\x64\x61ta_points\x18\x01 \x03(\x0b\x32\x30.opentelemetry.proto.metrics.v1.SummaryDataPoint\"\x86\x02\n\x0fNumberDataPoint\x12;\n\nattributes\x18\x07 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\x12\x1c\n\x14start_time_unix_nano\x18\x02 \x01(\x06\x12\x16\n\x0etime_unix_nano\x18\x03 \x01(\x06\x12\x13\n\tas_double\x18\x04 \x01(\x01H\x00\x12\x10\n\x06\x61s_int\x18\x06 \x01(\x10H\x00\x12;\n\texemplars\x18\x05 \x03(\x0b\x32(.opentelemetry.proto.metrics.v1.Exemplar\x12\r\n\x05\x66lags\x18\x08 \x01(\rB\x07\n\x05valueJ\x04\x08\x01\x10\x02\"\xe6\x02\n\x12HistogramDataPoint\x12;\n\nattributes\x18\t \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\x12\x1c\n\x14start_time_unix_nano\x18\x02 \x01(\x06\x12\x16\n\x0etime_unix_nano\x18\x03 \x01(\x06\x12\r\n\x05\x63ount\x18\x04 \x01(\x06\x12\x10\n\x03sum\x18\x05 \x01(\x01H\x00\x88\x01\x01\x12\x15\n\rbucket_counts\x18\x06 \x03(\x06\x12\x17\n\x0f\x65xplicit_bounds\x18\x07 \x03(\x01\x12;\n\texemplars\x18\x08 \x03(\x0b\x32(.opentelemetry.proto.metrics.v1.Exemplar\x12\r\n\x05\x66lags\x18\n \x01(\r\x12\x10\n\x03min\x18\x0b \x01(\x01H\x01\x88\x01\x01\x12\x10\n\x03max\x18\x0c \x01(\x01H\x02\x88\x01\x01\x42\x06\n\x04_sumB\x06\n\x04_minB\x06\n\x04_maxJ\x04\x08\x01\x10\x02\"\xda\x04\n\x1d\x45xponentialHistogramDataPoint\x12;\n\nattributes\x18\x01 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\x12\x1c\n\x14start_time_unix_nano\x18\x02 \x01(\x06\x12\x16\n\x0etime_unix_nano\x18\x03 \x01(\x06\x12\r\n\x05\x63ount\x18\x04 \x01(\x06\x12\x10\n\x03sum\x18\x05 \x01(\x01H\x00\x88\x01\x01\x12\r\n\x05scale\x18\x06 \x01(\x11\x12\x12\n\nzero_count\x18\x07 \x01(\x06\x12W\n\x08positive\x18\x08 \x01(\x0b\x32\x45.opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint.Buckets\x12W\n\x08negative\x18\t \x01(\x0b\x32\x45.opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint.Buckets\x12\r\n\x05\x66lags\x18\n \x01(\r\x12;\n\texemplars\x18\x0b \x03(\x0b\x32(.opentelemetry.proto.metrics.v1.Exemplar\x12\x10\n\x03min\x18\x0c \x01(\x01H\x01\x88\x01\x01\x12\x10\n\x03max\x18\r \x01(\x01H\x02\x88\x01\x01\x12\x16\n\x0ezero_threshold\x18\x0e \x01(\x01\x1a\x30\n\x07\x42uckets\x12\x0e\n\x06offset\x18\x01 \x01(\x11\x12\x15\n\rbucket_counts\x18\x02 \x03(\x04\x42\x06\n\x04_sumB\x06\n\x04_minB\x06\n\x04_max\"\xc5\x02\n\x10SummaryDataPoint\x12;\n\nattributes\x18\x07 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\x12\x1c\n\x14start_time_unix_nano\x18\x02 \x01(\x06\x12\x16\n\x0etime_unix_nano\x18\x03 \x01(\x06\x12\r\n\x05\x63ount\x18\x04 \x01(\x06\x12\x0b\n\x03sum\x18\x05 \x01(\x01\x12Y\n\x0fquantile_values\x18\x06 \x03(\x0b\x32@.opentelemetry.proto.metrics.v1.SummaryDataPoint.ValueAtQuantile\x12\r\n\x05\x66lags\x18\x08 \x01(\r\x1a\x32\n\x0fValueAtQuantile\x12\x10\n\x08quantile\x18\x01 \x01(\x01\x12\r\n\x05value\x18\x02 \x01(\x01J\x04\x08\x01\x10\x02\"\xc1\x01\n\x08\x45xemplar\x12\x44\n\x13\x66iltered_attributes\x18\x07 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\x12\x16\n\x0etime_unix_nano\x18\x02 \x01(\x06\x12\x13\n\tas_double\x18\x03 \x01(\x01H\x00\x12\x10\n\x06\x61s_int\x18\x06 \x01(\x10H\x00\x12\x0f\n\x07span_id\x18\x04 \x01(\x0c\x12\x10\n\x08trace_id\x18\x05 \x01(\x0c\x42\x07\n\x05valueJ\x04\x08\x01\x10\x02*\x8c\x01\n\x16\x41ggregationTemporality\x12\'\n#AGGREGATION_TEMPORALITY_UNSPECIFIED\x10\x00\x12!\n\x1d\x41GGREGATION_TEMPORALITY_DELTA\x10\x01\x12&\n\"AGGREGATION_TEMPORALITY_CUMULATIVE\x10\x02*^\n\x0e\x44\x61taPointFlags\x12\x1f\n\x1b\x44\x41TA_POINT_FLAGS_DO_NOT_USE\x10\x00\x12+\n\'DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK\x10\x01\x42\x7f\n!io.opentelemetry.proto.metrics.v1B\x0cMetricsProtoP\x01Z)go.opentelemetry.io/proto/otlp/metrics/v1\xaa\x02\x1eOpenTelemetry.Proto.Metrics.V1b\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'opentelemetry.proto.metrics.v1.metrics_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n!io.opentelemetry.proto.metrics.v1B\014MetricsProtoP\001Z)go.opentelemetry.io/proto/otlp/metrics/v1\252\002\036OpenTelemetry.Proto.Metrics.V1' + _globals['_AGGREGATIONTEMPORALITY']._serialized_start=3546 + _globals['_AGGREGATIONTEMPORALITY']._serialized_end=3686 + _globals['_DATAPOINTFLAGS']._serialized_start=3688 + _globals['_DATAPOINTFLAGS']._serialized_end=3782 + _globals['_METRICSDATA']._serialized_start=172 + _globals['_METRICSDATA']._serialized_end=260 + _globals['_RESOURCEMETRICS']._serialized_start=263 + _globals['_RESOURCEMETRICS']._serialized_end=438 + _globals['_SCOPEMETRICS']._serialized_start=441 + _globals['_SCOPEMETRICS']._serialized_end=600 + _globals['_METRIC']._serialized_start=603 + _globals['_METRIC']._serialized_end=1064 + _globals['_GAUGE']._serialized_start=1066 + _globals['_GAUGE']._serialized_end=1143 + _globals['_SUM']._serialized_start=1146 + _globals['_SUM']._serialized_end=1332 + _globals['_HISTOGRAM']._serialized_start=1335 + _globals['_HISTOGRAM']._serialized_end=1508 + _globals['_EXPONENTIALHISTOGRAM']._serialized_start=1511 + _globals['_EXPONENTIALHISTOGRAM']._serialized_end=1706 + _globals['_SUMMARY']._serialized_start=1708 + _globals['_SUMMARY']._serialized_end=1788 + _globals['_NUMBERDATAPOINT']._serialized_start=1791 + _globals['_NUMBERDATAPOINT']._serialized_end=2053 + _globals['_HISTOGRAMDATAPOINT']._serialized_start=2056 + _globals['_HISTOGRAMDATAPOINT']._serialized_end=2414 + _globals['_EXPONENTIALHISTOGRAMDATAPOINT']._serialized_start=2417 + _globals['_EXPONENTIALHISTOGRAMDATAPOINT']._serialized_end=3019 + _globals['_EXPONENTIALHISTOGRAMDATAPOINT_BUCKETS']._serialized_start=2947 + _globals['_EXPONENTIALHISTOGRAMDATAPOINT_BUCKETS']._serialized_end=2995 + _globals['_SUMMARYDATAPOINT']._serialized_start=3022 + _globals['_SUMMARYDATAPOINT']._serialized_end=3347 + _globals['_SUMMARYDATAPOINT_VALUEATQUANTILE']._serialized_start=3291 + _globals['_SUMMARYDATAPOINT_VALUEATQUANTILE']._serialized_end=3341 + _globals['_EXEMPLAR']._serialized_start=3350 + _globals['_EXEMPLAR']._serialized_end=3543 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/metrics_pb2.pyi b/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/metrics_pb2.pyi new file mode 100644 index 0000000..0f374be --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/metrics/v1/metrics_pb2.pyi @@ -0,0 +1,1177 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2019, OpenTelemetry Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import opentelemetry.proto.common.v1.common_pb2 +import opentelemetry.proto.resource.v1.resource_pb2 +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _AggregationTemporality: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _AggregationTemporalityEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_AggregationTemporality.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + AGGREGATION_TEMPORALITY_UNSPECIFIED: _AggregationTemporality.ValueType # 0 + """UNSPECIFIED is the default AggregationTemporality, it MUST not be used.""" + AGGREGATION_TEMPORALITY_DELTA: _AggregationTemporality.ValueType # 1 + """DELTA is an AggregationTemporality for a metric aggregator which reports + changes since last report time. Successive metrics contain aggregation of + values from continuous and non-overlapping intervals. + + The values for a DELTA metric are based only on the time interval + associated with one measurement cycle. There is no dependency on + previous measurements like is the case for CUMULATIVE metrics. + + For example, consider a system measuring the number of requests that + it receives and reports the sum of these requests every second as a + DELTA metric: + + 1. The system starts receiving at time=t_0. + 2. A request is received, the system measures 1 request. + 3. A request is received, the system measures 1 request. + 4. A request is received, the system measures 1 request. + 5. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0 to + t_0+1 with a value of 3. + 6. A request is received, the system measures 1 request. + 7. A request is received, the system measures 1 request. + 8. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0+1 to + t_0+2 with a value of 2. + """ + AGGREGATION_TEMPORALITY_CUMULATIVE: _AggregationTemporality.ValueType # 2 + """CUMULATIVE is an AggregationTemporality for a metric aggregator which + reports changes since a fixed start time. This means that current values + of a CUMULATIVE metric depend on all previous measurements since the + start time. Because of this, the sender is required to retain this state + in some form. If this state is lost or invalidated, the CUMULATIVE metric + values MUST be reset and a new fixed start time following the last + reported measurement time sent MUST be used. + + For example, consider a system measuring the number of requests that + it receives and reports the sum of these requests every second as a + CUMULATIVE metric: + + 1. The system starts receiving at time=t_0. + 2. A request is received, the system measures 1 request. + 3. A request is received, the system measures 1 request. + 4. A request is received, the system measures 1 request. + 5. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0 to + t_0+1 with a value of 3. + 6. A request is received, the system measures 1 request. + 7. A request is received, the system measures 1 request. + 8. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0 to + t_0+2 with a value of 5. + 9. The system experiences a fault and loses state. + 10. The system recovers and resumes receiving at time=t_1. + 11. A request is received, the system measures 1 request. + 12. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_1 to + t_0+1 with a value of 1. + + Note: Even though, when reporting changes since last report time, using + CUMULATIVE is valid, it is not recommended. This may cause problems for + systems that do not use start_time to determine when the aggregation + value was reset (e.g. Prometheus). + """ + +class AggregationTemporality(_AggregationTemporality, metaclass=_AggregationTemporalityEnumTypeWrapper): + """AggregationTemporality defines how a metric aggregator reports aggregated + values. It describes how those values relate to the time interval over + which they are aggregated. + """ + +AGGREGATION_TEMPORALITY_UNSPECIFIED: AggregationTemporality.ValueType # 0 +"""UNSPECIFIED is the default AggregationTemporality, it MUST not be used.""" +AGGREGATION_TEMPORALITY_DELTA: AggregationTemporality.ValueType # 1 +"""DELTA is an AggregationTemporality for a metric aggregator which reports +changes since last report time. Successive metrics contain aggregation of +values from continuous and non-overlapping intervals. + +The values for a DELTA metric are based only on the time interval +associated with one measurement cycle. There is no dependency on +previous measurements like is the case for CUMULATIVE metrics. + +For example, consider a system measuring the number of requests that +it receives and reports the sum of these requests every second as a +DELTA metric: + + 1. The system starts receiving at time=t_0. + 2. A request is received, the system measures 1 request. + 3. A request is received, the system measures 1 request. + 4. A request is received, the system measures 1 request. + 5. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0 to + t_0+1 with a value of 3. + 6. A request is received, the system measures 1 request. + 7. A request is received, the system measures 1 request. + 8. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0+1 to + t_0+2 with a value of 2. +""" +AGGREGATION_TEMPORALITY_CUMULATIVE: AggregationTemporality.ValueType # 2 +"""CUMULATIVE is an AggregationTemporality for a metric aggregator which +reports changes since a fixed start time. This means that current values +of a CUMULATIVE metric depend on all previous measurements since the +start time. Because of this, the sender is required to retain this state +in some form. If this state is lost or invalidated, the CUMULATIVE metric +values MUST be reset and a new fixed start time following the last +reported measurement time sent MUST be used. + +For example, consider a system measuring the number of requests that +it receives and reports the sum of these requests every second as a +CUMULATIVE metric: + + 1. The system starts receiving at time=t_0. + 2. A request is received, the system measures 1 request. + 3. A request is received, the system measures 1 request. + 4. A request is received, the system measures 1 request. + 5. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0 to + t_0+1 with a value of 3. + 6. A request is received, the system measures 1 request. + 7. A request is received, the system measures 1 request. + 8. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0 to + t_0+2 with a value of 5. + 9. The system experiences a fault and loses state. + 10. The system recovers and resumes receiving at time=t_1. + 11. A request is received, the system measures 1 request. + 12. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_1 to + t_0+1 with a value of 1. + +Note: Even though, when reporting changes since last report time, using +CUMULATIVE is valid, it is not recommended. This may cause problems for +systems that do not use start_time to determine when the aggregation +value was reset (e.g. Prometheus). +""" +global___AggregationTemporality = AggregationTemporality + +class _DataPointFlags: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _DataPointFlagsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DataPointFlags.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DATA_POINT_FLAGS_DO_NOT_USE: _DataPointFlags.ValueType # 0 + """The zero value for the enum. Should not be used for comparisons. + Instead use bitwise "and" with the appropriate mask as shown above. + """ + DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK: _DataPointFlags.ValueType # 1 + """This DataPoint is valid but has no recorded value. This value + SHOULD be used to reflect explicitly missing data in a series, as + for an equivalent to the Prometheus "staleness marker". + """ + +class DataPointFlags(_DataPointFlags, metaclass=_DataPointFlagsEnumTypeWrapper): + """DataPointFlags is defined as a protobuf 'uint32' type and is to be used as a + bit-field representing 32 distinct boolean flags. Each flag defined in this + enum is a bit-mask. To test the presence of a single flag in the flags of + a data point, for example, use an expression like: + + (point.flags & DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK) == DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK + """ + +DATA_POINT_FLAGS_DO_NOT_USE: DataPointFlags.ValueType # 0 +"""The zero value for the enum. Should not be used for comparisons. +Instead use bitwise "and" with the appropriate mask as shown above. +""" +DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK: DataPointFlags.ValueType # 1 +"""This DataPoint is valid but has no recorded value. This value +SHOULD be used to reflect explicitly missing data in a series, as +for an equivalent to the Prometheus "staleness marker". +""" +global___DataPointFlags = DataPointFlags + +@typing_extensions.final +class MetricsData(google.protobuf.message.Message): + """MetricsData represents the metrics data that can be stored in a persistent + storage, OR can be embedded by other protocols that transfer OTLP metrics + data but do not implement the OTLP protocol. + + MetricsData + └─── ResourceMetrics + ├── Resource + ├── SchemaURL + └── ScopeMetrics + ├── Scope + ├── SchemaURL + └── Metric + ├── Name + ├── Description + ├── Unit + └── data + ├── Gauge + ├── Sum + ├── Histogram + ├── ExponentialHistogram + └── Summary + + The main difference between this message and collector protocol is that + in this message there will not be any "control" or "metadata" specific to + OTLP protocol. + + When new fields are added into this message, the OTLP request MUST be updated + as well. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESOURCE_METRICS_FIELD_NUMBER: builtins.int + @property + def resource_metrics(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ResourceMetrics]: + """An array of ResourceMetrics. + For data coming from a single resource this array will typically contain + one element. Intermediary nodes that receive data from multiple origins + typically batch the data before forwarding further and in that case this + array will contain multiple elements. + """ + def __init__( + self, + *, + resource_metrics: collections.abc.Iterable[global___ResourceMetrics] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["resource_metrics", b"resource_metrics"]) -> None: ... + +global___MetricsData = MetricsData + +@typing_extensions.final +class ResourceMetrics(google.protobuf.message.Message): + """A collection of ScopeMetrics from a Resource.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESOURCE_FIELD_NUMBER: builtins.int + SCOPE_METRICS_FIELD_NUMBER: builtins.int + SCHEMA_URL_FIELD_NUMBER: builtins.int + @property + def resource(self) -> opentelemetry.proto.resource.v1.resource_pb2.Resource: + """The resource for the metrics in this message. + If this field is not set then no resource info is known. + """ + @property + def scope_metrics(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ScopeMetrics]: + """A list of metrics that originate from a resource.""" + schema_url: builtins.str + """The Schema URL, if known. This is the identifier of the Schema that the resource data + is recorded in. Notably, the last part of the URL path is the version number of the + schema: http[s]://server[:port]/path/. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + This schema_url applies to the data in the "resource" field. It does not apply + to the data in the "scope_metrics" field which have their own schema_url field. + """ + def __init__( + self, + *, + resource: opentelemetry.proto.resource.v1.resource_pb2.Resource | None = ..., + scope_metrics: collections.abc.Iterable[global___ScopeMetrics] | None = ..., + schema_url: builtins.str = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["resource", b"resource"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["resource", b"resource", "schema_url", b"schema_url", "scope_metrics", b"scope_metrics"]) -> None: ... + +global___ResourceMetrics = ResourceMetrics + +@typing_extensions.final +class ScopeMetrics(google.protobuf.message.Message): + """A collection of Metrics produced by an Scope.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SCOPE_FIELD_NUMBER: builtins.int + METRICS_FIELD_NUMBER: builtins.int + SCHEMA_URL_FIELD_NUMBER: builtins.int + @property + def scope(self) -> opentelemetry.proto.common.v1.common_pb2.InstrumentationScope: + """The instrumentation scope information for the metrics in this message. + Semantically when InstrumentationScope isn't set, it is equivalent with + an empty instrumentation scope name (unknown). + """ + @property + def metrics(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Metric]: + """A list of metrics that originate from an instrumentation library.""" + schema_url: builtins.str + """The Schema URL, if known. This is the identifier of the Schema that the metric data + is recorded in. Notably, the last part of the URL path is the version number of the + schema: http[s]://server[:port]/path/. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + This schema_url applies to the data in the "scope" field and all metrics in the + "metrics" field. + """ + def __init__( + self, + *, + scope: opentelemetry.proto.common.v1.common_pb2.InstrumentationScope | None = ..., + metrics: collections.abc.Iterable[global___Metric] | None = ..., + schema_url: builtins.str = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["scope", b"scope"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["metrics", b"metrics", "schema_url", b"schema_url", "scope", b"scope"]) -> None: ... + +global___ScopeMetrics = ScopeMetrics + +@typing_extensions.final +class Metric(google.protobuf.message.Message): + """Defines a Metric which has one or more timeseries. The following is a + brief summary of the Metric data model. For more details, see: + + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md + + The data model and relation between entities is shown in the + diagram below. Here, "DataPoint" is the term used to refer to any + one of the specific data point value types, and "points" is the term used + to refer to any one of the lists of points contained in the Metric. + + - Metric is composed of a metadata and data. + - Metadata part contains a name, description, unit. + - Data is one of the possible types (Sum, Gauge, Histogram, Summary). + - DataPoint contains timestamps, attributes, and one of the possible value type + fields. + + Metric + +------------+ + |name | + |description | + |unit | +------------------------------------+ + |data |---> |Gauge, Sum, Histogram, Summary, ... | + +------------+ +------------------------------------+ + + Data [One of Gauge, Sum, Histogram, Summary, ...] + +-----------+ + |... | // Metadata about the Data. + |points |--+ + +-----------+ | + | +---------------------------+ + | |DataPoint 1 | + v |+------+------+ +------+ | + +-----+ ||label |label |...|label | | + | 1 |-->||value1|value2|...|valueN| | + +-----+ |+------+------+ +------+ | + | . | |+-----+ | + | . | ||value| | + | . | |+-----+ | + | . | +---------------------------+ + | . | . + | . | . + | . | . + | . | +---------------------------+ + | . | |DataPoint M | + +-----+ |+------+------+ +------+ | + | M |-->||label |label |...|label | | + +-----+ ||value1|value2|...|valueN| | + |+------+------+ +------+ | + |+-----+ | + ||value| | + |+-----+ | + +---------------------------+ + + Each distinct type of DataPoint represents the output of a specific + aggregation function, the result of applying the DataPoint's + associated function of to one or more measurements. + + All DataPoint types have three common fields: + - Attributes includes key-value pairs associated with the data point + - TimeUnixNano is required, set to the end time of the aggregation + - StartTimeUnixNano is optional, but strongly encouraged for DataPoints + having an AggregationTemporality field, as discussed below. + + Both TimeUnixNano and StartTimeUnixNano values are expressed as + UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + + # TimeUnixNano + + This field is required, having consistent interpretation across + DataPoint types. TimeUnixNano is the moment corresponding to when + the data point's aggregate value was captured. + + Data points with the 0 value for TimeUnixNano SHOULD be rejected + by consumers. + + # StartTimeUnixNano + + StartTimeUnixNano in general allows detecting when a sequence of + observations is unbroken. This field indicates to consumers the + start time for points with cumulative and delta + AggregationTemporality, and it should be included whenever possible + to support correct rate calculation. Although it may be omitted + when the start time is truly unknown, setting StartTimeUnixNano is + strongly encouraged. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + UNIT_FIELD_NUMBER: builtins.int + GAUGE_FIELD_NUMBER: builtins.int + SUM_FIELD_NUMBER: builtins.int + HISTOGRAM_FIELD_NUMBER: builtins.int + EXPONENTIAL_HISTOGRAM_FIELD_NUMBER: builtins.int + SUMMARY_FIELD_NUMBER: builtins.int + METADATA_FIELD_NUMBER: builtins.int + name: builtins.str + """The name of the metric.""" + description: builtins.str + """A description of the metric, which can be used in documentation.""" + unit: builtins.str + """The unit in which the metric value is reported. Follows the format + described by https://unitsofmeasure.org/ucum.html. + """ + @property + def gauge(self) -> global___Gauge: ... + @property + def sum(self) -> global___Sum: ... + @property + def histogram(self) -> global___Histogram: ... + @property + def exponential_histogram(self) -> global___ExponentialHistogram: ... + @property + def summary(self) -> global___Summary: ... + @property + def metadata(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.common.v1.common_pb2.KeyValue]: + """Additional metadata attributes that describe the metric. [Optional]. + Attributes are non-identifying. + Consumers SHOULD NOT need to be aware of these attributes. + These attributes MAY be used to encode information allowing + for lossless roundtrip translation to / from another data model. + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. + """ + def __init__( + self, + *, + name: builtins.str = ..., + description: builtins.str = ..., + unit: builtins.str = ..., + gauge: global___Gauge | None = ..., + sum: global___Sum | None = ..., + histogram: global___Histogram | None = ..., + exponential_histogram: global___ExponentialHistogram | None = ..., + summary: global___Summary | None = ..., + metadata: collections.abc.Iterable[opentelemetry.proto.common.v1.common_pb2.KeyValue] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["data", b"data", "exponential_histogram", b"exponential_histogram", "gauge", b"gauge", "histogram", b"histogram", "sum", b"sum", "summary", b"summary"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["data", b"data", "description", b"description", "exponential_histogram", b"exponential_histogram", "gauge", b"gauge", "histogram", b"histogram", "metadata", b"metadata", "name", b"name", "sum", b"sum", "summary", b"summary", "unit", b"unit"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["data", b"data"]) -> typing_extensions.Literal["gauge", "sum", "histogram", "exponential_histogram", "summary"] | None: ... + +global___Metric = Metric + +@typing_extensions.final +class Gauge(google.protobuf.message.Message): + """Gauge represents the type of a scalar metric that always exports the + "current value" for every data point. It should be used for an "unknown" + aggregation. + + A Gauge does not support different aggregation temporalities. Given the + aggregation is unknown, points cannot be combined using the same + aggregation, regardless of aggregation temporalities. Therefore, + AggregationTemporality is not included. Consequently, this also means + "StartTimeUnixNano" is ignored for all data points. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DATA_POINTS_FIELD_NUMBER: builtins.int + @property + def data_points(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___NumberDataPoint]: + """The time series data points. + Note: Multiple time series may be included (same timestamp, different attributes). + """ + def __init__( + self, + *, + data_points: collections.abc.Iterable[global___NumberDataPoint] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["data_points", b"data_points"]) -> None: ... + +global___Gauge = Gauge + +@typing_extensions.final +class Sum(google.protobuf.message.Message): + """Sum represents the type of a scalar metric that is calculated as a sum of all + reported measurements over a time interval. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DATA_POINTS_FIELD_NUMBER: builtins.int + AGGREGATION_TEMPORALITY_FIELD_NUMBER: builtins.int + IS_MONOTONIC_FIELD_NUMBER: builtins.int + @property + def data_points(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___NumberDataPoint]: + """The time series data points. + Note: Multiple time series may be included (same timestamp, different attributes). + """ + aggregation_temporality: global___AggregationTemporality.ValueType + """aggregation_temporality describes if the aggregator reports delta changes + since last report time, or cumulative changes since a fixed start time. + """ + is_monotonic: builtins.bool + """Represents whether the sum is monotonic.""" + def __init__( + self, + *, + data_points: collections.abc.Iterable[global___NumberDataPoint] | None = ..., + aggregation_temporality: global___AggregationTemporality.ValueType = ..., + is_monotonic: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["aggregation_temporality", b"aggregation_temporality", "data_points", b"data_points", "is_monotonic", b"is_monotonic"]) -> None: ... + +global___Sum = Sum + +@typing_extensions.final +class Histogram(google.protobuf.message.Message): + """Histogram represents the type of a metric that is calculated by aggregating + as a Histogram of all reported measurements over a time interval. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DATA_POINTS_FIELD_NUMBER: builtins.int + AGGREGATION_TEMPORALITY_FIELD_NUMBER: builtins.int + @property + def data_points(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___HistogramDataPoint]: + """The time series data points. + Note: Multiple time series may be included (same timestamp, different attributes). + """ + aggregation_temporality: global___AggregationTemporality.ValueType + """aggregation_temporality describes if the aggregator reports delta changes + since last report time, or cumulative changes since a fixed start time. + """ + def __init__( + self, + *, + data_points: collections.abc.Iterable[global___HistogramDataPoint] | None = ..., + aggregation_temporality: global___AggregationTemporality.ValueType = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["aggregation_temporality", b"aggregation_temporality", "data_points", b"data_points"]) -> None: ... + +global___Histogram = Histogram + +@typing_extensions.final +class ExponentialHistogram(google.protobuf.message.Message): + """ExponentialHistogram represents the type of a metric that is calculated by aggregating + as a ExponentialHistogram of all reported double measurements over a time interval. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DATA_POINTS_FIELD_NUMBER: builtins.int + AGGREGATION_TEMPORALITY_FIELD_NUMBER: builtins.int + @property + def data_points(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ExponentialHistogramDataPoint]: + """The time series data points. + Note: Multiple time series may be included (same timestamp, different attributes). + """ + aggregation_temporality: global___AggregationTemporality.ValueType + """aggregation_temporality describes if the aggregator reports delta changes + since last report time, or cumulative changes since a fixed start time. + """ + def __init__( + self, + *, + data_points: collections.abc.Iterable[global___ExponentialHistogramDataPoint] | None = ..., + aggregation_temporality: global___AggregationTemporality.ValueType = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["aggregation_temporality", b"aggregation_temporality", "data_points", b"data_points"]) -> None: ... + +global___ExponentialHistogram = ExponentialHistogram + +@typing_extensions.final +class Summary(google.protobuf.message.Message): + """Summary metric data are used to convey quantile summaries, + a Prometheus (see: https://prometheus.io/docs/concepts/metric_types/#summary) + and OpenMetrics (see: https://github.com/prometheus/OpenMetrics/blob/4dbf6075567ab43296eed941037c12951faafb92/protos/prometheus.proto#L45) + data type. These data points cannot always be merged in a meaningful way. + While they can be useful in some applications, histogram data points are + recommended for new applications. + Summary metrics do not have an aggregation temporality field. This is + because the count and sum fields of a SummaryDataPoint are assumed to be + cumulative values. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DATA_POINTS_FIELD_NUMBER: builtins.int + @property + def data_points(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___SummaryDataPoint]: + """The time series data points. + Note: Multiple time series may be included (same timestamp, different attributes). + """ + def __init__( + self, + *, + data_points: collections.abc.Iterable[global___SummaryDataPoint] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["data_points", b"data_points"]) -> None: ... + +global___Summary = Summary + +@typing_extensions.final +class NumberDataPoint(google.protobuf.message.Message): + """NumberDataPoint is a single data point in a timeseries that describes the + time-varying scalar value of a metric. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ATTRIBUTES_FIELD_NUMBER: builtins.int + START_TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + AS_DOUBLE_FIELD_NUMBER: builtins.int + AS_INT_FIELD_NUMBER: builtins.int + EXEMPLARS_FIELD_NUMBER: builtins.int + FLAGS_FIELD_NUMBER: builtins.int + @property + def attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.common.v1.common_pb2.KeyValue]: + """The set of key/value pairs that uniquely identify the timeseries from + where this point belongs. The list may be empty (may contain 0 elements). + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. + """ + start_time_unix_nano: builtins.int + """StartTimeUnixNano is optional but strongly encouraged, see the + the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. + """ + time_unix_nano: builtins.int + """TimeUnixNano is required, see the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. + """ + as_double: builtins.float + as_int: builtins.int + @property + def exemplars(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Exemplar]: + """(Optional) List of exemplars collected from + measurements that were used to form the data point + """ + flags: builtins.int + """Flags that apply to this specific data point. See DataPointFlags + for the available flags and their meaning. + """ + def __init__( + self, + *, + attributes: collections.abc.Iterable[opentelemetry.proto.common.v1.common_pb2.KeyValue] | None = ..., + start_time_unix_nano: builtins.int = ..., + time_unix_nano: builtins.int = ..., + as_double: builtins.float = ..., + as_int: builtins.int = ..., + exemplars: collections.abc.Iterable[global___Exemplar] | None = ..., + flags: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["as_double", b"as_double", "as_int", b"as_int", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["as_double", b"as_double", "as_int", b"as_int", "attributes", b"attributes", "exemplars", b"exemplars", "flags", b"flags", "start_time_unix_nano", b"start_time_unix_nano", "time_unix_nano", b"time_unix_nano", "value", b"value"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["value", b"value"]) -> typing_extensions.Literal["as_double", "as_int"] | None: ... + +global___NumberDataPoint = NumberDataPoint + +@typing_extensions.final +class HistogramDataPoint(google.protobuf.message.Message): + """HistogramDataPoint is a single data point in a timeseries that describes the + time-varying values of a Histogram. A Histogram contains summary statistics + for a population of values, it may optionally contain the distribution of + those values across a set of buckets. + + If the histogram contains the distribution of values, then both + "explicit_bounds" and "bucket counts" fields must be defined. + If the histogram does not contain the distribution of values, then both + "explicit_bounds" and "bucket_counts" must be omitted and only "count" and + "sum" are known. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ATTRIBUTES_FIELD_NUMBER: builtins.int + START_TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + COUNT_FIELD_NUMBER: builtins.int + SUM_FIELD_NUMBER: builtins.int + BUCKET_COUNTS_FIELD_NUMBER: builtins.int + EXPLICIT_BOUNDS_FIELD_NUMBER: builtins.int + EXEMPLARS_FIELD_NUMBER: builtins.int + FLAGS_FIELD_NUMBER: builtins.int + MIN_FIELD_NUMBER: builtins.int + MAX_FIELD_NUMBER: builtins.int + @property + def attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.common.v1.common_pb2.KeyValue]: + """The set of key/value pairs that uniquely identify the timeseries from + where this point belongs. The list may be empty (may contain 0 elements). + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. + """ + start_time_unix_nano: builtins.int + """StartTimeUnixNano is optional but strongly encouraged, see the + the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. + """ + time_unix_nano: builtins.int + """TimeUnixNano is required, see the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. + """ + count: builtins.int + """count is the number of values in the population. Must be non-negative. This + value must be equal to the sum of the "count" fields in buckets if a + histogram is provided. + """ + sum: builtins.float + """sum of the values in the population. If count is zero then this field + must be zero. + + Note: Sum should only be filled out when measuring non-negative discrete + events, and is assumed to be monotonic over the values of these events. + Negative events *can* be recorded, but sum should not be filled out when + doing so. This is specifically to enforce compatibility w/ OpenMetrics, + see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram + """ + @property + def bucket_counts(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """bucket_counts is an optional field contains the count values of histogram + for each bucket. + + The sum of the bucket_counts must equal the value in the count field. + + The number of elements in bucket_counts array must be by one greater than + the number of elements in explicit_bounds array. The exception to this rule + is when the length of bucket_counts is 0, then the length of explicit_bounds + must also be 0. + """ + @property + def explicit_bounds(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.float]: + """explicit_bounds specifies buckets with explicitly defined bounds for values. + + The boundaries for bucket at index i are: + + (-infinity, explicit_bounds[i]] for i == 0 + (explicit_bounds[i-1], explicit_bounds[i]] for 0 < i < size(explicit_bounds) + (explicit_bounds[i-1], +infinity) for i == size(explicit_bounds) + + The values in the explicit_bounds array must be strictly increasing. + + Histogram buckets are inclusive of their upper boundary, except the last + bucket where the boundary is at infinity. This format is intentionally + compatible with the OpenMetrics histogram definition. + + If bucket_counts length is 0 then explicit_bounds length must also be 0, + otherwise the data point is invalid. + """ + @property + def exemplars(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Exemplar]: + """(Optional) List of exemplars collected from + measurements that were used to form the data point + """ + flags: builtins.int + """Flags that apply to this specific data point. See DataPointFlags + for the available flags and their meaning. + """ + min: builtins.float + """min is the minimum value over (start_time, end_time].""" + max: builtins.float + """max is the maximum value over (start_time, end_time].""" + def __init__( + self, + *, + attributes: collections.abc.Iterable[opentelemetry.proto.common.v1.common_pb2.KeyValue] | None = ..., + start_time_unix_nano: builtins.int = ..., + time_unix_nano: builtins.int = ..., + count: builtins.int = ..., + sum: builtins.float | None = ..., + bucket_counts: collections.abc.Iterable[builtins.int] | None = ..., + explicit_bounds: collections.abc.Iterable[builtins.float] | None = ..., + exemplars: collections.abc.Iterable[global___Exemplar] | None = ..., + flags: builtins.int = ..., + min: builtins.float | None = ..., + max: builtins.float | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["_max", b"_max", "_min", b"_min", "_sum", b"_sum", "max", b"max", "min", b"min", "sum", b"sum"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["_max", b"_max", "_min", b"_min", "_sum", b"_sum", "attributes", b"attributes", "bucket_counts", b"bucket_counts", "count", b"count", "exemplars", b"exemplars", "explicit_bounds", b"explicit_bounds", "flags", b"flags", "max", b"max", "min", b"min", "start_time_unix_nano", b"start_time_unix_nano", "sum", b"sum", "time_unix_nano", b"time_unix_nano"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing_extensions.Literal["_max", b"_max"]) -> typing_extensions.Literal["max"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing_extensions.Literal["_min", b"_min"]) -> typing_extensions.Literal["min"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing_extensions.Literal["_sum", b"_sum"]) -> typing_extensions.Literal["sum"] | None: ... + +global___HistogramDataPoint = HistogramDataPoint + +@typing_extensions.final +class ExponentialHistogramDataPoint(google.protobuf.message.Message): + """ExponentialHistogramDataPoint is a single data point in a timeseries that describes the + time-varying values of a ExponentialHistogram of double values. A ExponentialHistogram contains + summary statistics for a population of values, it may optionally contain the + distribution of those values across a set of buckets. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing_extensions.final + class Buckets(google.protobuf.message.Message): + """Buckets are a set of bucket counts, encoded in a contiguous array + of counts. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + OFFSET_FIELD_NUMBER: builtins.int + BUCKET_COUNTS_FIELD_NUMBER: builtins.int + offset: builtins.int + """The bucket index of the first entry in the bucket_counts array. + + Note: This uses a varint encoding as a simple form of compression. + """ + @property + def bucket_counts(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """An array of count values, where bucket_counts[i] carries + the count of the bucket at index (offset+i). bucket_counts[i] is the count + of values greater than base^(offset+i) and less than or equal to + base^(offset+i+1). + + Note: By contrast, the explicit HistogramDataPoint uses + fixed64. This field is expected to have many buckets, + especially zeros, so uint64 has been selected to ensure + varint encoding. + """ + def __init__( + self, + *, + offset: builtins.int = ..., + bucket_counts: collections.abc.Iterable[builtins.int] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["bucket_counts", b"bucket_counts", "offset", b"offset"]) -> None: ... + + ATTRIBUTES_FIELD_NUMBER: builtins.int + START_TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + COUNT_FIELD_NUMBER: builtins.int + SUM_FIELD_NUMBER: builtins.int + SCALE_FIELD_NUMBER: builtins.int + ZERO_COUNT_FIELD_NUMBER: builtins.int + POSITIVE_FIELD_NUMBER: builtins.int + NEGATIVE_FIELD_NUMBER: builtins.int + FLAGS_FIELD_NUMBER: builtins.int + EXEMPLARS_FIELD_NUMBER: builtins.int + MIN_FIELD_NUMBER: builtins.int + MAX_FIELD_NUMBER: builtins.int + ZERO_THRESHOLD_FIELD_NUMBER: builtins.int + @property + def attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.common.v1.common_pb2.KeyValue]: + """The set of key/value pairs that uniquely identify the timeseries from + where this point belongs. The list may be empty (may contain 0 elements). + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. + """ + start_time_unix_nano: builtins.int + """StartTimeUnixNano is optional but strongly encouraged, see the + the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. + """ + time_unix_nano: builtins.int + """TimeUnixNano is required, see the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. + """ + count: builtins.int + """The number of values in the population. Must be + non-negative. This value must be equal to the sum of the "bucket_counts" + values in the positive and negative Buckets plus the "zero_count" field. + """ + sum: builtins.float + """The sum of the values in the population. If count is zero then this field + must be zero. + + Note: Sum should only be filled out when measuring non-negative discrete + events, and is assumed to be monotonic over the values of these events. + Negative events *can* be recorded, but sum should not be filled out when + doing so. This is specifically to enforce compatibility w/ OpenMetrics, + see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram + """ + scale: builtins.int + """scale describes the resolution of the histogram. Boundaries are + located at powers of the base, where: + + base = (2^(2^-scale)) + + The histogram bucket identified by `index`, a signed integer, + contains values that are greater than (base^index) and + less than or equal to (base^(index+1)). + + The positive and negative ranges of the histogram are expressed + separately. Negative values are mapped by their absolute value + into the negative range using the same scale as the positive range. + + scale is not restricted by the protocol, as the permissible + values depend on the range of the data. + """ + zero_count: builtins.int + """The count of values that are either exactly zero or + within the region considered zero by the instrumentation at the + tolerated degree of precision. This bucket stores values that + cannot be expressed using the standard exponential formula as + well as values that have been rounded to zero. + + Implementations MAY consider the zero bucket to have probability + mass equal to (zero_count / count). + """ + @property + def positive(self) -> global___ExponentialHistogramDataPoint.Buckets: + """positive carries the positive range of exponential bucket counts.""" + @property + def negative(self) -> global___ExponentialHistogramDataPoint.Buckets: + """negative carries the negative range of exponential bucket counts.""" + flags: builtins.int + """Flags that apply to this specific data point. See DataPointFlags + for the available flags and their meaning. + """ + @property + def exemplars(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Exemplar]: + """(Optional) List of exemplars collected from + measurements that were used to form the data point + """ + min: builtins.float + """The minimum value over (start_time, end_time].""" + max: builtins.float + """The maximum value over (start_time, end_time].""" + zero_threshold: builtins.float + """ZeroThreshold may be optionally set to convey the width of the zero + region. Where the zero region is defined as the closed interval + [-ZeroThreshold, ZeroThreshold]. + When ZeroThreshold is 0, zero count bucket stores values that cannot be + expressed using the standard exponential formula as well as values that + have been rounded to zero. + """ + def __init__( + self, + *, + attributes: collections.abc.Iterable[opentelemetry.proto.common.v1.common_pb2.KeyValue] | None = ..., + start_time_unix_nano: builtins.int = ..., + time_unix_nano: builtins.int = ..., + count: builtins.int = ..., + sum: builtins.float | None = ..., + scale: builtins.int = ..., + zero_count: builtins.int = ..., + positive: global___ExponentialHistogramDataPoint.Buckets | None = ..., + negative: global___ExponentialHistogramDataPoint.Buckets | None = ..., + flags: builtins.int = ..., + exemplars: collections.abc.Iterable[global___Exemplar] | None = ..., + min: builtins.float | None = ..., + max: builtins.float | None = ..., + zero_threshold: builtins.float = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["_max", b"_max", "_min", b"_min", "_sum", b"_sum", "max", b"max", "min", b"min", "negative", b"negative", "positive", b"positive", "sum", b"sum"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["_max", b"_max", "_min", b"_min", "_sum", b"_sum", "attributes", b"attributes", "count", b"count", "exemplars", b"exemplars", "flags", b"flags", "max", b"max", "min", b"min", "negative", b"negative", "positive", b"positive", "scale", b"scale", "start_time_unix_nano", b"start_time_unix_nano", "sum", b"sum", "time_unix_nano", b"time_unix_nano", "zero_count", b"zero_count", "zero_threshold", b"zero_threshold"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing_extensions.Literal["_max", b"_max"]) -> typing_extensions.Literal["max"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing_extensions.Literal["_min", b"_min"]) -> typing_extensions.Literal["min"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing_extensions.Literal["_sum", b"_sum"]) -> typing_extensions.Literal["sum"] | None: ... + +global___ExponentialHistogramDataPoint = ExponentialHistogramDataPoint + +@typing_extensions.final +class SummaryDataPoint(google.protobuf.message.Message): + """SummaryDataPoint is a single data point in a timeseries that describes the + time-varying values of a Summary metric. The count and sum fields represent + cumulative values. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing_extensions.final + class ValueAtQuantile(google.protobuf.message.Message): + """Represents the value at a given quantile of a distribution. + + To record Min and Max values following conventions are used: + - The 1.0 quantile is equivalent to the maximum value observed. + - The 0.0 quantile is equivalent to the minimum value observed. + + See the following issue for more context: + https://github.com/open-telemetry/opentelemetry-proto/issues/125 + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + QUANTILE_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + quantile: builtins.float + """The quantile of a distribution. Must be in the interval + [0.0, 1.0]. + """ + value: builtins.float + """The value at the given quantile of a distribution. + + Quantile values must NOT be negative. + """ + def __init__( + self, + *, + quantile: builtins.float = ..., + value: builtins.float = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["quantile", b"quantile", "value", b"value"]) -> None: ... + + ATTRIBUTES_FIELD_NUMBER: builtins.int + START_TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + COUNT_FIELD_NUMBER: builtins.int + SUM_FIELD_NUMBER: builtins.int + QUANTILE_VALUES_FIELD_NUMBER: builtins.int + FLAGS_FIELD_NUMBER: builtins.int + @property + def attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.common.v1.common_pb2.KeyValue]: + """The set of key/value pairs that uniquely identify the timeseries from + where this point belongs. The list may be empty (may contain 0 elements). + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. + """ + start_time_unix_nano: builtins.int + """StartTimeUnixNano is optional but strongly encouraged, see the + the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. + """ + time_unix_nano: builtins.int + """TimeUnixNano is required, see the detailed comments above Metric. + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. + """ + count: builtins.int + """count is the number of values in the population. Must be non-negative.""" + sum: builtins.float + """sum of the values in the population. If count is zero then this field + must be zero. + + Note: Sum should only be filled out when measuring non-negative discrete + events, and is assumed to be monotonic over the values of these events. + Negative events *can* be recorded, but sum should not be filled out when + doing so. This is specifically to enforce compatibility w/ OpenMetrics, + see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#summary + """ + @property + def quantile_values(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___SummaryDataPoint.ValueAtQuantile]: + """(Optional) list of values at different quantiles of the distribution calculated + from the current snapshot. The quantiles must be strictly increasing. + """ + flags: builtins.int + """Flags that apply to this specific data point. See DataPointFlags + for the available flags and their meaning. + """ + def __init__( + self, + *, + attributes: collections.abc.Iterable[opentelemetry.proto.common.v1.common_pb2.KeyValue] | None = ..., + start_time_unix_nano: builtins.int = ..., + time_unix_nano: builtins.int = ..., + count: builtins.int = ..., + sum: builtins.float = ..., + quantile_values: collections.abc.Iterable[global___SummaryDataPoint.ValueAtQuantile] | None = ..., + flags: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["attributes", b"attributes", "count", b"count", "flags", b"flags", "quantile_values", b"quantile_values", "start_time_unix_nano", b"start_time_unix_nano", "sum", b"sum", "time_unix_nano", b"time_unix_nano"]) -> None: ... + +global___SummaryDataPoint = SummaryDataPoint + +@typing_extensions.final +class Exemplar(google.protobuf.message.Message): + """A representation of an exemplar, which is a sample input measurement. + Exemplars also hold information about the environment when the measurement + was recorded, for example the span and trace ID of the active span when the + exemplar was recorded. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + FILTERED_ATTRIBUTES_FIELD_NUMBER: builtins.int + TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + AS_DOUBLE_FIELD_NUMBER: builtins.int + AS_INT_FIELD_NUMBER: builtins.int + SPAN_ID_FIELD_NUMBER: builtins.int + TRACE_ID_FIELD_NUMBER: builtins.int + @property + def filtered_attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.common.v1.common_pb2.KeyValue]: + """The set of key/value pairs that were filtered out by the aggregator, but + recorded alongside the original measurement. Only key/value pairs that were + filtered out by the aggregator should be included + """ + time_unix_nano: builtins.int + """time_unix_nano is the exact time when this exemplar was recorded + + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + 1970. + """ + as_double: builtins.float + as_int: builtins.int + span_id: builtins.bytes + """(Optional) Span ID of the exemplar trace. + span_id may be missing if the measurement is not recorded inside a trace + or if the trace is not sampled. + """ + trace_id: builtins.bytes + """(Optional) Trace ID of the exemplar trace. + trace_id may be missing if the measurement is not recorded inside a trace + or if the trace is not sampled. + """ + def __init__( + self, + *, + filtered_attributes: collections.abc.Iterable[opentelemetry.proto.common.v1.common_pb2.KeyValue] | None = ..., + time_unix_nano: builtins.int = ..., + as_double: builtins.float = ..., + as_int: builtins.int = ..., + span_id: builtins.bytes = ..., + trace_id: builtins.bytes = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["as_double", b"as_double", "as_int", b"as_int", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["as_double", b"as_double", "as_int", b"as_int", "filtered_attributes", b"filtered_attributes", "span_id", b"span_id", "time_unix_nano", b"time_unix_nano", "trace_id", b"trace_id", "value", b"value"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["value", b"value"]) -> typing_extensions.Literal["as_double", "as_int"] | None: ... + +global___Exemplar = Exemplar diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/profiles/v1development/__pycache__/profiles_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/profiles/v1development/__pycache__/profiles_pb2.cpython-312.pyc new file mode 100644 index 0000000..db7305c Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/profiles/v1development/__pycache__/profiles_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/profiles/v1development/profiles_pb2.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/profiles/v1development/profiles_pb2.py new file mode 100644 index 0000000..f78c6ab --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/profiles/v1development/profiles_pb2.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: opentelemetry/proto/profiles/v1development/profiles.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from opentelemetry.proto.common.v1 import common_pb2 as opentelemetry_dot_proto_dot_common_dot_v1_dot_common__pb2 +from opentelemetry.proto.resource.v1 import resource_pb2 as opentelemetry_dot_proto_dot_resource_dot_v1_dot_resource__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n9opentelemetry/proto/profiles/v1development/profiles.proto\x12*opentelemetry.proto.profiles.v1development\x1a*opentelemetry/proto/common/v1/common.proto\x1a.opentelemetry/proto/resource/v1/resource.proto\"\xf6\x03\n\x12ProfilesDictionary\x12J\n\rmapping_table\x18\x01 \x03(\x0b\x32\x33.opentelemetry.proto.profiles.v1development.Mapping\x12L\n\x0elocation_table\x18\x02 \x03(\x0b\x32\x34.opentelemetry.proto.profiles.v1development.Location\x12L\n\x0e\x66unction_table\x18\x03 \x03(\x0b\x32\x34.opentelemetry.proto.profiles.v1development.Function\x12\x44\n\nlink_table\x18\x04 \x03(\x0b\x32\x30.opentelemetry.proto.profiles.v1development.Link\x12\x14\n\x0cstring_table\x18\x05 \x03(\t\x12T\n\x0f\x61ttribute_table\x18\x06 \x03(\x0b\x32;.opentelemetry.proto.profiles.v1development.KeyValueAndUnit\x12\x46\n\x0bstack_table\x18\x07 \x03(\x0b\x32\x31.opentelemetry.proto.profiles.v1development.Stack\"\xbb\x01\n\x0cProfilesData\x12W\n\x11resource_profiles\x18\x01 \x03(\x0b\x32<.opentelemetry.proto.profiles.v1development.ResourceProfiles\x12R\n\ndictionary\x18\x02 \x01(\x0b\x32>.opentelemetry.proto.profiles.v1development.ProfilesDictionary\"\xbe\x01\n\x10ResourceProfiles\x12;\n\x08resource\x18\x01 \x01(\x0b\x32).opentelemetry.proto.resource.v1.Resource\x12Q\n\x0escope_profiles\x18\x02 \x03(\x0b\x32\x39.opentelemetry.proto.profiles.v1development.ScopeProfiles\x12\x12\n\nschema_url\x18\x03 \x01(\tJ\x06\x08\xe8\x07\x10\xe9\x07\"\xae\x01\n\rScopeProfiles\x12\x42\n\x05scope\x18\x01 \x01(\x0b\x32\x33.opentelemetry.proto.common.v1.InstrumentationScope\x12\x45\n\x08profiles\x18\x02 \x03(\x0b\x32\x33.opentelemetry.proto.profiles.v1development.Profile\x12\x12\n\nschema_url\x18\x03 \x01(\t\"\xb1\x03\n\x07Profile\x12J\n\x0bsample_type\x18\x01 \x01(\x0b\x32\x35.opentelemetry.proto.profiles.v1development.ValueType\x12\x43\n\x07samples\x18\x02 \x03(\x0b\x32\x32.opentelemetry.proto.profiles.v1development.Sample\x12\x16\n\x0etime_unix_nano\x18\x03 \x01(\x06\x12\x15\n\rduration_nano\x18\x04 \x01(\x04\x12J\n\x0bperiod_type\x18\x05 \x01(\x0b\x32\x35.opentelemetry.proto.profiles.v1development.ValueType\x12\x0e\n\x06period\x18\x06 \x01(\x03\x12\x12\n\nprofile_id\x18\x07 \x01(\x0c\x12 \n\x18\x64ropped_attributes_count\x18\x08 \x01(\r\x12\x1f\n\x17original_payload_format\x18\t \x01(\t\x12\x18\n\x10original_payload\x18\n \x01(\x0c\x12\x19\n\x11\x61ttribute_indices\x18\x0b \x03(\x05\")\n\x04Link\x12\x10\n\x08trace_id\x18\x01 \x01(\x0c\x12\x0f\n\x07span_id\x18\x02 \x01(\x0c\"9\n\tValueType\x12\x15\n\rtype_strindex\x18\x01 \x01(\x05\x12\x15\n\runit_strindex\x18\x02 \x01(\x05\"z\n\x06Sample\x12\x13\n\x0bstack_index\x18\x01 \x01(\x05\x12\x19\n\x11\x61ttribute_indices\x18\x02 \x03(\x05\x12\x12\n\nlink_index\x18\x03 \x01(\x05\x12\x0e\n\x06values\x18\x04 \x03(\x03\x12\x1c\n\x14timestamps_unix_nano\x18\x05 \x03(\x06\"\x80\x01\n\x07Mapping\x12\x14\n\x0cmemory_start\x18\x01 \x01(\x04\x12\x14\n\x0cmemory_limit\x18\x02 \x01(\x04\x12\x13\n\x0b\x66ile_offset\x18\x03 \x01(\x04\x12\x19\n\x11\x66ilename_strindex\x18\x04 \x01(\x05\x12\x19\n\x11\x61ttribute_indices\x18\x05 \x03(\x05\"!\n\x05Stack\x12\x18\n\x10location_indices\x18\x01 \x03(\x05\"\x8e\x01\n\x08Location\x12\x15\n\rmapping_index\x18\x01 \x01(\x05\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\x04\x12?\n\x05lines\x18\x03 \x03(\x0b\x32\x30.opentelemetry.proto.profiles.v1development.Line\x12\x19\n\x11\x61ttribute_indices\x18\x04 \x03(\x05\"<\n\x04Line\x12\x16\n\x0e\x66unction_index\x18\x01 \x01(\x05\x12\x0c\n\x04line\x18\x02 \x01(\x03\x12\x0e\n\x06\x63olumn\x18\x03 \x01(\x03\"n\n\x08\x46unction\x12\x15\n\rname_strindex\x18\x01 \x01(\x05\x12\x1c\n\x14system_name_strindex\x18\x02 \x01(\x05\x12\x19\n\x11\x66ilename_strindex\x18\x03 \x01(\x05\x12\x12\n\nstart_line\x18\x04 \x01(\x03\"v\n\x0fKeyValueAndUnit\x12\x14\n\x0ckey_strindex\x18\x01 \x01(\x05\x12\x36\n\x05value\x18\x02 \x01(\x0b\x32\'.opentelemetry.proto.common.v1.AnyValue\x12\x15\n\runit_strindex\x18\x03 \x01(\x05\x42\xa4\x01\n-io.opentelemetry.proto.profiles.v1developmentB\rProfilesProtoP\x01Z5go.opentelemetry.io/proto/otlp/profiles/v1development\xaa\x02*OpenTelemetry.Proto.Profiles.V1Developmentb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'opentelemetry.proto.profiles.v1development.profiles_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n-io.opentelemetry.proto.profiles.v1developmentB\rProfilesProtoP\001Z5go.opentelemetry.io/proto/otlp/profiles/v1development\252\002*OpenTelemetry.Proto.Profiles.V1Development' + _globals['_PROFILESDICTIONARY']._serialized_start=198 + _globals['_PROFILESDICTIONARY']._serialized_end=700 + _globals['_PROFILESDATA']._serialized_start=703 + _globals['_PROFILESDATA']._serialized_end=890 + _globals['_RESOURCEPROFILES']._serialized_start=893 + _globals['_RESOURCEPROFILES']._serialized_end=1083 + _globals['_SCOPEPROFILES']._serialized_start=1086 + _globals['_SCOPEPROFILES']._serialized_end=1260 + _globals['_PROFILE']._serialized_start=1263 + _globals['_PROFILE']._serialized_end=1696 + _globals['_LINK']._serialized_start=1698 + _globals['_LINK']._serialized_end=1739 + _globals['_VALUETYPE']._serialized_start=1741 + _globals['_VALUETYPE']._serialized_end=1798 + _globals['_SAMPLE']._serialized_start=1800 + _globals['_SAMPLE']._serialized_end=1922 + _globals['_MAPPING']._serialized_start=1925 + _globals['_MAPPING']._serialized_end=2053 + _globals['_STACK']._serialized_start=2055 + _globals['_STACK']._serialized_end=2088 + _globals['_LOCATION']._serialized_start=2091 + _globals['_LOCATION']._serialized_end=2233 + _globals['_LINE']._serialized_start=2235 + _globals['_LINE']._serialized_end=2295 + _globals['_FUNCTION']._serialized_start=2297 + _globals['_FUNCTION']._serialized_end=2407 + _globals['_KEYVALUEANDUNIT']._serialized_start=2409 + _globals['_KEYVALUEANDUNIT']._serialized_end=2527 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/profiles/v1development/profiles_pb2.pyi b/venv/lib/python3.12/site-packages/opentelemetry/proto/profiles/v1development/profiles_pb2.pyi new file mode 100644 index 0000000..01938ed --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/profiles/v1development/profiles_pb2.pyi @@ -0,0 +1,800 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2023, OpenTelemetry Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +This file includes work covered by the following copyright and permission notices: + +Copyright 2016 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import opentelemetry.proto.common.v1.common_pb2 +import opentelemetry.proto.resource.v1.resource_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class ProfilesDictionary(google.protobuf.message.Message): + """ Relationships Diagram + + ┌──────────────────┐ LEGEND + │ ProfilesData │ ─────┐ + └──────────────────┘ │ ─────▶ embedded + │ │ + │ 1-n │ ─────▷ referenced by index + ▼ ▼ + ┌──────────────────┐ ┌────────────────────┐ + │ ResourceProfiles │ │ ProfilesDictionary │ + └──────────────────┘ └────────────────────┘ + │ + │ 1-n + ▼ + ┌──────────────────┐ + │ ScopeProfiles │ + └──────────────────┘ + │ + │ 1-n + ▼ + ┌──────────────────┐ + │ Profile │ + └──────────────────┘ + │ n-1 + │ 1-n ┌───────────────────────────────────────┐ + ▼ │ ▽ + ┌──────────────────┐ 1-n ┌─────────────────┐ ┌──────────┐ + │ Sample │ ──────▷ │ KeyValueAndUnit │ │ Link │ + └──────────────────┘ └─────────────────┘ └──────────┘ + │ △ △ + │ n-1 │ │ 1-n + ▽ │ │ + ┌──────────────────┐ │ │ + │ Stack │ │ │ + └──────────────────┘ │ │ + │ 1-n │ │ + │ 1-n ┌────────────────┘ │ + ▽ │ │ + ┌──────────────────┐ n-1 ┌─────────────┐ + │ Location │ ──────▷ │ Mapping │ + └──────────────────┘ └─────────────┘ + │ + │ 1-n + ▼ + ┌──────────────────┐ + │ Line │ + └──────────────────┘ + │ + │ 1-1 + ▽ + ┌──────────────────┐ + │ Function │ + └──────────────────┘ + + ProfilesDictionary represents the profiles data shared across the + entire message being sent. The following applies to all fields in this + message: + + - A dictionary is an array of dictionary items. Users of the dictionary + compactly reference the items using the index within the array. + + - A dictionary MUST have a zero value encoded as the first element. This + allows for _index fields pointing into the dictionary to use a 0 pointer + value to indicate 'null' / 'not set'. Unless otherwise defined, a 'zero + value' message value is one with all default field values, so as to + minimize wire encoded size. + + - There SHOULD NOT be dupes in a dictionary. The identity of dictionary + items is based on their value, recursively as needed. If a particular + implementation does emit duplicated items, it MUST NOT attempt to give them + meaning based on the index or order. A profile processor may remove + duplicate items and this MUST NOT have any observable effects for + consumers. + + - There SHOULD NOT be orphaned (unreferenced) items in a dictionary. A + profile processor may remove ("garbage-collect") orphaned items and this + MUST NOT have any observable effects for consumers. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MAPPING_TABLE_FIELD_NUMBER: builtins.int + LOCATION_TABLE_FIELD_NUMBER: builtins.int + FUNCTION_TABLE_FIELD_NUMBER: builtins.int + LINK_TABLE_FIELD_NUMBER: builtins.int + STRING_TABLE_FIELD_NUMBER: builtins.int + ATTRIBUTE_TABLE_FIELD_NUMBER: builtins.int + STACK_TABLE_FIELD_NUMBER: builtins.int + @property + def mapping_table(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Mapping]: + """Mappings from address ranges to the image/binary/library mapped + into that address range referenced by locations via Location.mapping_index. + + mapping_table[0] must always be zero value (Mapping{}) and present. + """ + @property + def location_table(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Location]: + """Locations referenced by samples via Stack.location_indices. + + location_table[0] must always be zero value (Location{}) and present. + """ + @property + def function_table(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Function]: + """Functions referenced by locations via Line.function_index. + + function_table[0] must always be zero value (Function{}) and present. + """ + @property + def link_table(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Link]: + """Links referenced by samples via Sample.link_index. + + link_table[0] must always be zero value (Link{}) and present. + """ + @property + def string_table(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """A common table for strings referenced by various messages. + + string_table[0] must always be "" and present. + """ + @property + def attribute_table(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___KeyValueAndUnit]: + """A common table for attributes referenced by the Profile, Sample, Mapping + and Location messages below through attribute_indices field. Each entry is + a key/value pair with an optional unit. Since this is a dictionary table, + multiple entries with the same key may be present, unlike direct attribute + tables like Resource.attributes. The referencing attribute_indices fields, + though, do maintain the key uniqueness requirement. + + It's recommended to use attributes for variables with bounded cardinality, + such as categorical variables + (https://en.wikipedia.org/wiki/Categorical_variable). Using an attribute of + a floating point type (e.g., CPU time) in a sample can quickly make every + attribute value unique, defeating the purpose of the dictionary and + impractically increasing the profile size. + + Examples of attributes: + "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" + "abc.com/myattribute": true + "allocation_size": 128 bytes + + attribute_table[0] must always be zero value (KeyValueAndUnit{}) and present. + """ + @property + def stack_table(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Stack]: + """Stacks referenced by samples via Sample.stack_index. + + stack_table[0] must always be zero value (Stack{}) and present. + """ + def __init__( + self, + *, + mapping_table: collections.abc.Iterable[global___Mapping] | None = ..., + location_table: collections.abc.Iterable[global___Location] | None = ..., + function_table: collections.abc.Iterable[global___Function] | None = ..., + link_table: collections.abc.Iterable[global___Link] | None = ..., + string_table: collections.abc.Iterable[builtins.str] | None = ..., + attribute_table: collections.abc.Iterable[global___KeyValueAndUnit] | None = ..., + stack_table: collections.abc.Iterable[global___Stack] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["attribute_table", b"attribute_table", "function_table", b"function_table", "link_table", b"link_table", "location_table", b"location_table", "mapping_table", b"mapping_table", "stack_table", b"stack_table", "string_table", b"string_table"]) -> None: ... + +global___ProfilesDictionary = ProfilesDictionary + +@typing_extensions.final +class ProfilesData(google.protobuf.message.Message): + """ProfilesData represents the profiles data that can be stored in persistent storage, + OR can be embedded by other protocols that transfer OTLP profiles data but do not + implement the OTLP protocol. + + The main difference between this message and collector protocol is that + in this message there will not be any "control" or "metadata" specific to + OTLP protocol. + + When new fields are added into this message, the OTLP request MUST be updated + as well. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESOURCE_PROFILES_FIELD_NUMBER: builtins.int + DICTIONARY_FIELD_NUMBER: builtins.int + @property + def resource_profiles(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ResourceProfiles]: + """An array of ResourceProfiles. + For data coming from an SDK profiler, this array will typically contain one + element. Host-level profilers will usually create one ResourceProfile per + container, as well as one additional ResourceProfile grouping all samples + from non-containerized processes. + Other resource groupings are possible as well and clarified via + Resource.attributes and semantic conventions. + Tools that visualize profiles should prefer displaying + resources_profiles[0].scope_profiles[0].profiles[0] by default. + """ + @property + def dictionary(self) -> global___ProfilesDictionary: + """One instance of ProfilesDictionary""" + def __init__( + self, + *, + resource_profiles: collections.abc.Iterable[global___ResourceProfiles] | None = ..., + dictionary: global___ProfilesDictionary | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["dictionary", b"dictionary"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["dictionary", b"dictionary", "resource_profiles", b"resource_profiles"]) -> None: ... + +global___ProfilesData = ProfilesData + +@typing_extensions.final +class ResourceProfiles(google.protobuf.message.Message): + """A collection of ScopeProfiles from a Resource.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESOURCE_FIELD_NUMBER: builtins.int + SCOPE_PROFILES_FIELD_NUMBER: builtins.int + SCHEMA_URL_FIELD_NUMBER: builtins.int + @property + def resource(self) -> opentelemetry.proto.resource.v1.resource_pb2.Resource: + """The resource for the profiles in this message. + If this field is not set then no resource info is known. + """ + @property + def scope_profiles(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ScopeProfiles]: + """A list of ScopeProfiles that originate from a resource.""" + schema_url: builtins.str + """The Schema URL, if known. This is the identifier of the Schema that the resource data + is recorded in. Notably, the last part of the URL path is the version number of the + schema: http[s]://server[:port]/path/. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + This schema_url applies to the data in the "resource" field. It does not apply + to the data in the "scope_profiles" field which have their own schema_url field. + """ + def __init__( + self, + *, + resource: opentelemetry.proto.resource.v1.resource_pb2.Resource | None = ..., + scope_profiles: collections.abc.Iterable[global___ScopeProfiles] | None = ..., + schema_url: builtins.str = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["resource", b"resource"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["resource", b"resource", "schema_url", b"schema_url", "scope_profiles", b"scope_profiles"]) -> None: ... + +global___ResourceProfiles = ResourceProfiles + +@typing_extensions.final +class ScopeProfiles(google.protobuf.message.Message): + """A collection of Profiles produced by an InstrumentationScope.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SCOPE_FIELD_NUMBER: builtins.int + PROFILES_FIELD_NUMBER: builtins.int + SCHEMA_URL_FIELD_NUMBER: builtins.int + @property + def scope(self) -> opentelemetry.proto.common.v1.common_pb2.InstrumentationScope: + """The instrumentation scope information for the profiles in this message. + Semantically when InstrumentationScope isn't set, it is equivalent with + an empty instrumentation scope name (unknown). + """ + @property + def profiles(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Profile]: + """A list of Profiles that originate from an instrumentation scope.""" + schema_url: builtins.str + """The Schema URL, if known. This is the identifier of the Schema that the profile data + is recorded in. Notably, the last part of the URL path is the version number of the + schema: http[s]://server[:port]/path/. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + This schema_url applies to the data in the "scope" field and all profiles in the + "profiles" field. + """ + def __init__( + self, + *, + scope: opentelemetry.proto.common.v1.common_pb2.InstrumentationScope | None = ..., + profiles: collections.abc.Iterable[global___Profile] | None = ..., + schema_url: builtins.str = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["scope", b"scope"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["profiles", b"profiles", "schema_url", b"schema_url", "scope", b"scope"]) -> None: ... + +global___ScopeProfiles = ScopeProfiles + +@typing_extensions.final +class Profile(google.protobuf.message.Message): + """Profile is a common stacktrace profile format. + + Measurements represented with this format should follow the + following conventions: + + - Consumers should treat unset optional fields as if they had been + set with their default value. + + - When possible, measurements should be stored in "unsampled" form + that is most useful to humans. There should be enough + information present to determine the original sampled values. + + - The profile is represented as a set of samples, where each sample + references a stack trace which is a list of locations, each belonging + to a mapping. + - There is a N->1 relationship from Stack.location_indices entries to + locations. For every Stack.location_indices entry there must be a + unique Location with that index. + - There is an optional N->1 relationship from locations to + mappings. For every nonzero Location.mapping_id there must be a + unique Mapping with that index. + + Represents a complete profile, including sample types, samples, mappings to + binaries, stacks, locations, functions, string table, and additional + metadata. It modifies and annotates pprof Profile with OpenTelemetry + specific fields. + + Note that whilst fields in this message retain the name and field id from pprof in most cases + for ease of understanding data migration, it is not intended that pprof:Profile and + OpenTelemetry:Profile encoding be wire compatible. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SAMPLE_TYPE_FIELD_NUMBER: builtins.int + SAMPLES_FIELD_NUMBER: builtins.int + TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + DURATION_NANO_FIELD_NUMBER: builtins.int + PERIOD_TYPE_FIELD_NUMBER: builtins.int + PERIOD_FIELD_NUMBER: builtins.int + PROFILE_ID_FIELD_NUMBER: builtins.int + DROPPED_ATTRIBUTES_COUNT_FIELD_NUMBER: builtins.int + ORIGINAL_PAYLOAD_FORMAT_FIELD_NUMBER: builtins.int + ORIGINAL_PAYLOAD_FIELD_NUMBER: builtins.int + ATTRIBUTE_INDICES_FIELD_NUMBER: builtins.int + @property + def sample_type(self) -> global___ValueType: + """The type and unit of all Sample.values in this profile. + For a cpu or off-cpu profile this might be: + ["cpu","nanoseconds"] or ["off_cpu","nanoseconds"] + For a heap profile, this might be: + ["allocated_objects","count"] or ["allocated_space","bytes"], + """ + @property + def samples(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Sample]: + """The set of samples recorded in this profile.""" + time_unix_nano: builtins.int + """The following fields 3-12 are informational, do not affect + interpretation of results. + + Time of collection. Value is UNIX Epoch time in nanoseconds since 00:00:00 + UTC on 1 January 1970. + """ + duration_nano: builtins.int + """Duration of the profile. For instant profiles like live heap snapshot, the + duration can be zero but it may be preferable to set time_unix_nano to the + process start time and duration_nano to the relative time when the profile + was gathered. This ensures Sample.timestamps_unix_nano values such as + allocation timestamp fall into the profile time range. + """ + @property + def period_type(self) -> global___ValueType: + """The kind of events between sampled occurrences. + e.g [ "cpu","cycles" ] or [ "heap","bytes" ] + """ + period: builtins.int + """The number of events between sampled occurrences.""" + profile_id: builtins.bytes + """A globally unique identifier for a profile. The ID is a 16-byte array. An ID with + all zeroes is considered invalid. It may be used for deduplication and signal + correlation purposes. It is acceptable to treat two profiles with different values + in this field as not equal, even if they represented the same object at an earlier + time. + This field is optional; an ID may be assigned to an ID-less profile in a later step. + """ + dropped_attributes_count: builtins.int + """The number of attributes that were discarded. Attributes + can be discarded because their keys are too long or because there are too many + attributes. If this value is 0, then no attributes were dropped. + """ + original_payload_format: builtins.str + """The original payload format. See also original_payload. Optional, but the + format and the bytes must be set or unset together. + + The allowed values for the format string are defined by the OpenTelemetry + specification. Some examples are "jfr", "pprof", "linux_perf". + + The original payload may be optionally provided when the conversion to the + OLTP format was done from a different format with some loss of the fidelity + and the receiver may want to store the original payload to allow future + lossless export or reinterpretation. Some examples of the original format + are JFR (Java Flight Recorder), pprof, Linux perf. + + Even when the original payload is in a format that is semantically close to + OTLP, such as pprof, a conversion may still be lossy in some cases (e.g. if + the pprof file contains custom extensions or conventions). + + The original payload can be large in size, so including the original + payload should be configurable by the profiler or collector options. The + default behavior should be to not include the original payload. + """ + original_payload: builtins.bytes + """The original payload bytes. See also original_payload_format. Optional, but + format and the bytes must be set or unset together. + """ + @property + def attribute_indices(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """References to attributes in attribute_table. [optional]""" + def __init__( + self, + *, + sample_type: global___ValueType | None = ..., + samples: collections.abc.Iterable[global___Sample] | None = ..., + time_unix_nano: builtins.int = ..., + duration_nano: builtins.int = ..., + period_type: global___ValueType | None = ..., + period: builtins.int = ..., + profile_id: builtins.bytes = ..., + dropped_attributes_count: builtins.int = ..., + original_payload_format: builtins.str = ..., + original_payload: builtins.bytes = ..., + attribute_indices: collections.abc.Iterable[builtins.int] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["period_type", b"period_type", "sample_type", b"sample_type"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["attribute_indices", b"attribute_indices", "dropped_attributes_count", b"dropped_attributes_count", "duration_nano", b"duration_nano", "original_payload", b"original_payload", "original_payload_format", b"original_payload_format", "period", b"period", "period_type", b"period_type", "profile_id", b"profile_id", "sample_type", b"sample_type", "samples", b"samples", "time_unix_nano", b"time_unix_nano"]) -> None: ... + +global___Profile = Profile + +@typing_extensions.final +class Link(google.protobuf.message.Message): + """A pointer from a profile Sample to a trace Span. + Connects a profile sample to a trace span, identified by unique trace and span IDs. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACE_ID_FIELD_NUMBER: builtins.int + SPAN_ID_FIELD_NUMBER: builtins.int + trace_id: builtins.bytes + """A unique identifier of a trace that this linked span is part of. The ID is a + 16-byte array. + """ + span_id: builtins.bytes + """A unique identifier for the linked span. The ID is an 8-byte array.""" + def __init__( + self, + *, + trace_id: builtins.bytes = ..., + span_id: builtins.bytes = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["span_id", b"span_id", "trace_id", b"trace_id"]) -> None: ... + +global___Link = Link + +@typing_extensions.final +class ValueType(google.protobuf.message.Message): + """ValueType describes the type and units of a value.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TYPE_STRINDEX_FIELD_NUMBER: builtins.int + UNIT_STRINDEX_FIELD_NUMBER: builtins.int + type_strindex: builtins.int + """Index into ProfilesDictionary.string_table.""" + unit_strindex: builtins.int + """Index into ProfilesDictionary.string_table.""" + def __init__( + self, + *, + type_strindex: builtins.int = ..., + unit_strindex: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["type_strindex", b"type_strindex", "unit_strindex", b"unit_strindex"]) -> None: ... + +global___ValueType = ValueType + +@typing_extensions.final +class Sample(google.protobuf.message.Message): + """Each Sample records values encountered in some program context. The program + context is typically a stack trace, perhaps augmented with auxiliary + information like the thread-id, some indicator of a higher level request + being handled etc. + + A Sample MUST have have at least one values or timestamps_unix_nano entry. If + both fields are populated, they MUST contain the same number of elements, and + the elements at the same index MUST refer to the same event. + + For the purposes of efficiently representing aggregated data observations, a Sample is regarded + as having a shared identity and an associated collection of per-observation data points. + Samples having the same identity SHOULD be combined by inserting timestamps and values to the data arrays. + + Examples of different ways ('shapes') of representing a sample with the total value of 10: + + Report of a stacktrace at 10 timestamps (consumers must assume the value is 1 for each point): + values: [] + timestamps_unix_nano: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + + Report of a stacktrace with an aggregated value without timestamps: + values: [10] + timestamps_unix_nano: [] + + Report of a stacktrace at 4 timestamps where each point records a specific value: + values: [2, 2, 3, 3] + timestamps_unix_nano: [1, 2, 3, 4] + + All Samples for a Profile SHOULD have the same shape, i.e. all data observation series should consistently + adopt the same data recording style. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + STACK_INDEX_FIELD_NUMBER: builtins.int + ATTRIBUTE_INDICES_FIELD_NUMBER: builtins.int + LINK_INDEX_FIELD_NUMBER: builtins.int + VALUES_FIELD_NUMBER: builtins.int + TIMESTAMPS_UNIX_NANO_FIELD_NUMBER: builtins.int + stack_index: builtins.int + """A Sample's identity (i.e. 'primary key') is the tuple of {stack_index, set_of(attribute_indices), link_index} + + Reference to stack in ProfilesDictionary.stack_table. + """ + @property + def attribute_indices(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """References to attributes in ProfilesDictionary.attribute_table. [optional]""" + link_index: builtins.int + """Reference to link in ProfilesDictionary.link_table. [optional] + It can be unset / set to 0 if no link exists, as link_table[0] is always a 'null' default value. + """ + @property + def values(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """The following fields may contain per-observation data and do not form part of the Sample's identity. + + The type and unit of each value is defined by Profile.sample_type. + """ + @property + def timestamps_unix_nano(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """Timestamps associated with Sample. Value is UNIX Epoch time in nanoseconds + since 00:00:00 UTC on 1 January 1970. The timestamps should fall within the + [Profile.time_unix_nano, Profile.time_unix_nano + Profile.duration_nano) + time range. + """ + def __init__( + self, + *, + stack_index: builtins.int = ..., + attribute_indices: collections.abc.Iterable[builtins.int] | None = ..., + link_index: builtins.int = ..., + values: collections.abc.Iterable[builtins.int] | None = ..., + timestamps_unix_nano: collections.abc.Iterable[builtins.int] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["attribute_indices", b"attribute_indices", "link_index", b"link_index", "stack_index", b"stack_index", "timestamps_unix_nano", b"timestamps_unix_nano", "values", b"values"]) -> None: ... + +global___Sample = Sample + +@typing_extensions.final +class Mapping(google.protobuf.message.Message): + """Describes the mapping of a binary in memory, including its address range, + file offset, and metadata like build ID + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MEMORY_START_FIELD_NUMBER: builtins.int + MEMORY_LIMIT_FIELD_NUMBER: builtins.int + FILE_OFFSET_FIELD_NUMBER: builtins.int + FILENAME_STRINDEX_FIELD_NUMBER: builtins.int + ATTRIBUTE_INDICES_FIELD_NUMBER: builtins.int + memory_start: builtins.int + """Address at which the binary (or DLL) is loaded into memory.""" + memory_limit: builtins.int + """The limit of the address range occupied by this mapping.""" + file_offset: builtins.int + """Offset in the binary that corresponds to the first mapped address.""" + filename_strindex: builtins.int + """The object this entry is loaded from. This can be a filename on + disk for the main binary and shared libraries, or virtual + abstractions like "[vdso]". + Index into ProfilesDictionary.string_table. + """ + @property + def attribute_indices(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """References to attributes in ProfilesDictionary.attribute_table. [optional]""" + def __init__( + self, + *, + memory_start: builtins.int = ..., + memory_limit: builtins.int = ..., + file_offset: builtins.int = ..., + filename_strindex: builtins.int = ..., + attribute_indices: collections.abc.Iterable[builtins.int] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["attribute_indices", b"attribute_indices", "file_offset", b"file_offset", "filename_strindex", b"filename_strindex", "memory_limit", b"memory_limit", "memory_start", b"memory_start"]) -> None: ... + +global___Mapping = Mapping + +@typing_extensions.final +class Stack(google.protobuf.message.Message): + """A Stack represents a stack trace as a list of locations.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCATION_INDICES_FIELD_NUMBER: builtins.int + @property + def location_indices(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """References to locations in ProfilesDictionary.location_table. + The first location is the leaf frame. + """ + def __init__( + self, + *, + location_indices: collections.abc.Iterable[builtins.int] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["location_indices", b"location_indices"]) -> None: ... + +global___Stack = Stack + +@typing_extensions.final +class Location(google.protobuf.message.Message): + """Describes function and line table debug information.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MAPPING_INDEX_FIELD_NUMBER: builtins.int + ADDRESS_FIELD_NUMBER: builtins.int + LINES_FIELD_NUMBER: builtins.int + ATTRIBUTE_INDICES_FIELD_NUMBER: builtins.int + mapping_index: builtins.int + """Reference to mapping in ProfilesDictionary.mapping_table. + It can be unset / set to 0 if the mapping is unknown or not applicable for + this profile type, as mapping_table[0] is always a 'null' default mapping. + """ + address: builtins.int + """The instruction address for this location, if available. It + should be within [Mapping.memory_start...Mapping.memory_limit] + for the corresponding mapping. A non-leaf address may be in the + middle of a call instruction. It is up to display tools to find + the beginning of the instruction if necessary. + """ + @property + def lines(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Line]: + """Multiple line indicates this location has inlined functions, + where the last entry represents the caller into which the + preceding entries were inlined. + + E.g., if memcpy() is inlined into printf: + lines[0].function_name == "memcpy" + lines[1].function_name == "printf" + """ + @property + def attribute_indices(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """References to attributes in ProfilesDictionary.attribute_table. [optional]""" + def __init__( + self, + *, + mapping_index: builtins.int = ..., + address: builtins.int = ..., + lines: collections.abc.Iterable[global___Line] | None = ..., + attribute_indices: collections.abc.Iterable[builtins.int] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["address", b"address", "attribute_indices", b"attribute_indices", "lines", b"lines", "mapping_index", b"mapping_index"]) -> None: ... + +global___Location = Location + +@typing_extensions.final +class Line(google.protobuf.message.Message): + """Details a specific line in a source code, linked to a function.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + FUNCTION_INDEX_FIELD_NUMBER: builtins.int + LINE_FIELD_NUMBER: builtins.int + COLUMN_FIELD_NUMBER: builtins.int + function_index: builtins.int + """Reference to function in ProfilesDictionary.function_table.""" + line: builtins.int + """Line number in source code. 0 means unset.""" + column: builtins.int + """Column number in source code. 0 means unset.""" + def __init__( + self, + *, + function_index: builtins.int = ..., + line: builtins.int = ..., + column: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["column", b"column", "function_index", b"function_index", "line", b"line"]) -> None: ... + +global___Line = Line + +@typing_extensions.final +class Function(google.protobuf.message.Message): + """Describes a function, including its human-readable name, system name, + source file, and starting line number in the source. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_STRINDEX_FIELD_NUMBER: builtins.int + SYSTEM_NAME_STRINDEX_FIELD_NUMBER: builtins.int + FILENAME_STRINDEX_FIELD_NUMBER: builtins.int + START_LINE_FIELD_NUMBER: builtins.int + name_strindex: builtins.int + """The function name. Empty string if not available.""" + system_name_strindex: builtins.int + """Function name, as identified by the system. For instance, + it can be a C++ mangled name. Empty string if not available. + """ + filename_strindex: builtins.int + """Source file containing the function. Empty string if not available.""" + start_line: builtins.int + """Line number in source file. 0 means unset.""" + def __init__( + self, + *, + name_strindex: builtins.int = ..., + system_name_strindex: builtins.int = ..., + filename_strindex: builtins.int = ..., + start_line: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["filename_strindex", b"filename_strindex", "name_strindex", b"name_strindex", "start_line", b"start_line", "system_name_strindex", b"system_name_strindex"]) -> None: ... + +global___Function = Function + +@typing_extensions.final +class KeyValueAndUnit(google.protobuf.message.Message): + """A custom 'dictionary native' style of encoding attributes which is more convenient + for profiles than opentelemetry.proto.common.v1.KeyValue + Specifically, uses the string table for keys and allows optional unit information. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_STRINDEX_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + UNIT_STRINDEX_FIELD_NUMBER: builtins.int + key_strindex: builtins.int + """The index into the string table for the attribute's key.""" + @property + def value(self) -> opentelemetry.proto.common.v1.common_pb2.AnyValue: + """The value of the attribute.""" + unit_strindex: builtins.int + """The index into the string table for the attribute's unit. + zero indicates implicit (by semconv) or non-defined unit. + """ + def __init__( + self, + *, + key_strindex: builtins.int = ..., + value: opentelemetry.proto.common.v1.common_pb2.AnyValue | None = ..., + unit_strindex: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["key_strindex", b"key_strindex", "unit_strindex", b"unit_strindex", "value", b"value"]) -> None: ... + +global___KeyValueAndUnit = KeyValueAndUnit diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/proto/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d3f8520 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..26f3440 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/__pycache__/resource_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/__pycache__/resource_pb2.cpython-312.pyc new file mode 100644 index 0000000..dadbb31 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/__pycache__/resource_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/resource_pb2.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/resource_pb2.py new file mode 100644 index 0000000..f7066fc --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/resource_pb2.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: opentelemetry/proto/resource/v1/resource.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from opentelemetry.proto.common.v1 import common_pb2 as opentelemetry_dot_proto_dot_common_dot_v1_dot_common__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n.opentelemetry/proto/resource/v1/resource.proto\x12\x1fopentelemetry.proto.resource.v1\x1a*opentelemetry/proto/common/v1/common.proto\"\xa8\x01\n\x08Resource\x12;\n\nattributes\x18\x01 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\x12 \n\x18\x64ropped_attributes_count\x18\x02 \x01(\r\x12=\n\x0b\x65ntity_refs\x18\x03 \x03(\x0b\x32(.opentelemetry.proto.common.v1.EntityRefB\x83\x01\n\"io.opentelemetry.proto.resource.v1B\rResourceProtoP\x01Z*go.opentelemetry.io/proto/otlp/resource/v1\xaa\x02\x1fOpenTelemetry.Proto.Resource.V1b\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'opentelemetry.proto.resource.v1.resource_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\"io.opentelemetry.proto.resource.v1B\rResourceProtoP\001Z*go.opentelemetry.io/proto/otlp/resource/v1\252\002\037OpenTelemetry.Proto.Resource.V1' + _globals['_RESOURCE']._serialized_start=128 + _globals['_RESOURCE']._serialized_end=296 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/resource_pb2.pyi b/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/resource_pb2.pyi new file mode 100644 index 0000000..61472c5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/resource/v1/resource_pb2.pyi @@ -0,0 +1,70 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2019, OpenTelemetry Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import opentelemetry.proto.common.v1.common_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class Resource(google.protobuf.message.Message): + """Resource information.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ATTRIBUTES_FIELD_NUMBER: builtins.int + DROPPED_ATTRIBUTES_COUNT_FIELD_NUMBER: builtins.int + ENTITY_REFS_FIELD_NUMBER: builtins.int + @property + def attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.common.v1.common_pb2.KeyValue]: + """Set of attributes that describe the resource. + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. + """ + dropped_attributes_count: builtins.int + """The number of dropped attributes. If the value is 0, then + no attributes were dropped. + """ + @property + def entity_refs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.common.v1.common_pb2.EntityRef]: + """Set of entities that participate in this Resource. + + Note: keys in the references MUST exist in attributes of this message. + + Status: [Development] + """ + def __init__( + self, + *, + attributes: collections.abc.Iterable[opentelemetry.proto.common.v1.common_pb2.KeyValue] | None = ..., + dropped_attributes_count: builtins.int = ..., + entity_refs: collections.abc.Iterable[opentelemetry.proto.common.v1.common_pb2.EntityRef] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["attributes", b"attributes", "dropped_attributes_count", b"dropped_attributes_count", "entity_refs", b"entity_refs"]) -> None: ... + +global___Resource = Resource diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f397192 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f247887 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/__pycache__/trace_pb2.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/__pycache__/trace_pb2.cpython-312.pyc new file mode 100644 index 0000000..73671e8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/__pycache__/trace_pb2.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/trace_pb2.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/trace_pb2.py new file mode 100644 index 0000000..61a2d0f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/trace_pb2.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: opentelemetry/proto/trace/v1/trace.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from opentelemetry.proto.common.v1 import common_pb2 as opentelemetry_dot_proto_dot_common_dot_v1_dot_common__pb2 +from opentelemetry.proto.resource.v1 import resource_pb2 as opentelemetry_dot_proto_dot_resource_dot_v1_dot_resource__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(opentelemetry/proto/trace/v1/trace.proto\x12\x1copentelemetry.proto.trace.v1\x1a*opentelemetry/proto/common/v1/common.proto\x1a.opentelemetry/proto/resource/v1/resource.proto\"Q\n\nTracesData\x12\x43\n\x0eresource_spans\x18\x01 \x03(\x0b\x32+.opentelemetry.proto.trace.v1.ResourceSpans\"\xa7\x01\n\rResourceSpans\x12;\n\x08resource\x18\x01 \x01(\x0b\x32).opentelemetry.proto.resource.v1.Resource\x12=\n\x0bscope_spans\x18\x02 \x03(\x0b\x32(.opentelemetry.proto.trace.v1.ScopeSpans\x12\x12\n\nschema_url\x18\x03 \x01(\tJ\x06\x08\xe8\x07\x10\xe9\x07\"\x97\x01\n\nScopeSpans\x12\x42\n\x05scope\x18\x01 \x01(\x0b\x32\x33.opentelemetry.proto.common.v1.InstrumentationScope\x12\x31\n\x05spans\x18\x02 \x03(\x0b\x32\".opentelemetry.proto.trace.v1.Span\x12\x12\n\nschema_url\x18\x03 \x01(\t\"\x84\x08\n\x04Span\x12\x10\n\x08trace_id\x18\x01 \x01(\x0c\x12\x0f\n\x07span_id\x18\x02 \x01(\x0c\x12\x13\n\x0btrace_state\x18\x03 \x01(\t\x12\x16\n\x0eparent_span_id\x18\x04 \x01(\x0c\x12\r\n\x05\x66lags\x18\x10 \x01(\x07\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x39\n\x04kind\x18\x06 \x01(\x0e\x32+.opentelemetry.proto.trace.v1.Span.SpanKind\x12\x1c\n\x14start_time_unix_nano\x18\x07 \x01(\x06\x12\x1a\n\x12\x65nd_time_unix_nano\x18\x08 \x01(\x06\x12;\n\nattributes\x18\t \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\x12 \n\x18\x64ropped_attributes_count\x18\n \x01(\r\x12\x38\n\x06\x65vents\x18\x0b \x03(\x0b\x32(.opentelemetry.proto.trace.v1.Span.Event\x12\x1c\n\x14\x64ropped_events_count\x18\x0c \x01(\r\x12\x36\n\x05links\x18\r \x03(\x0b\x32\'.opentelemetry.proto.trace.v1.Span.Link\x12\x1b\n\x13\x64ropped_links_count\x18\x0e \x01(\r\x12\x34\n\x06status\x18\x0f \x01(\x0b\x32$.opentelemetry.proto.trace.v1.Status\x1a\x8c\x01\n\x05\x45vent\x12\x16\n\x0etime_unix_nano\x18\x01 \x01(\x06\x12\x0c\n\x04name\x18\x02 \x01(\t\x12;\n\nattributes\x18\x03 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\x12 \n\x18\x64ropped_attributes_count\x18\x04 \x01(\r\x1a\xac\x01\n\x04Link\x12\x10\n\x08trace_id\x18\x01 \x01(\x0c\x12\x0f\n\x07span_id\x18\x02 \x01(\x0c\x12\x13\n\x0btrace_state\x18\x03 \x01(\t\x12;\n\nattributes\x18\x04 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\x12 \n\x18\x64ropped_attributes_count\x18\x05 \x01(\r\x12\r\n\x05\x66lags\x18\x06 \x01(\x07\"\x99\x01\n\x08SpanKind\x12\x19\n\x15SPAN_KIND_UNSPECIFIED\x10\x00\x12\x16\n\x12SPAN_KIND_INTERNAL\x10\x01\x12\x14\n\x10SPAN_KIND_SERVER\x10\x02\x12\x14\n\x10SPAN_KIND_CLIENT\x10\x03\x12\x16\n\x12SPAN_KIND_PRODUCER\x10\x04\x12\x16\n\x12SPAN_KIND_CONSUMER\x10\x05\"\xae\x01\n\x06Status\x12\x0f\n\x07message\x18\x02 \x01(\t\x12=\n\x04\x63ode\x18\x03 \x01(\x0e\x32/.opentelemetry.proto.trace.v1.Status.StatusCode\"N\n\nStatusCode\x12\x15\n\x11STATUS_CODE_UNSET\x10\x00\x12\x12\n\x0eSTATUS_CODE_OK\x10\x01\x12\x15\n\x11STATUS_CODE_ERROR\x10\x02J\x04\x08\x01\x10\x02*\x9c\x01\n\tSpanFlags\x12\x19\n\x15SPAN_FLAGS_DO_NOT_USE\x10\x00\x12 \n\x1bSPAN_FLAGS_TRACE_FLAGS_MASK\x10\xff\x01\x12*\n%SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK\x10\x80\x02\x12&\n!SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK\x10\x80\x04\x42w\n\x1fio.opentelemetry.proto.trace.v1B\nTraceProtoP\x01Z\'go.opentelemetry.io/proto/otlp/trace/v1\xaa\x02\x1cOpenTelemetry.Proto.Trace.V1b\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'opentelemetry.proto.trace.v1.trace_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\037io.opentelemetry.proto.trace.v1B\nTraceProtoP\001Z\'go.opentelemetry.io/proto/otlp/trace/v1\252\002\034OpenTelemetry.Proto.Trace.V1' + _globals['_SPANFLAGS']._serialized_start=1782 + _globals['_SPANFLAGS']._serialized_end=1938 + _globals['_TRACESDATA']._serialized_start=166 + _globals['_TRACESDATA']._serialized_end=247 + _globals['_RESOURCESPANS']._serialized_start=250 + _globals['_RESOURCESPANS']._serialized_end=417 + _globals['_SCOPESPANS']._serialized_start=420 + _globals['_SCOPESPANS']._serialized_end=571 + _globals['_SPAN']._serialized_start=574 + _globals['_SPAN']._serialized_end=1602 + _globals['_SPAN_EVENT']._serialized_start=1131 + _globals['_SPAN_EVENT']._serialized_end=1271 + _globals['_SPAN_LINK']._serialized_start=1274 + _globals['_SPAN_LINK']._serialized_end=1446 + _globals['_SPAN_SPANKIND']._serialized_start=1449 + _globals['_SPAN_SPANKIND']._serialized_end=1602 + _globals['_STATUS']._serialized_start=1605 + _globals['_STATUS']._serialized_end=1779 + _globals['_STATUS_STATUSCODE']._serialized_start=1695 + _globals['_STATUS_STATUSCODE']._serialized_end=1773 +# @@protoc_insertion_point(module_scope) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/trace_pb2.pyi b/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/trace_pb2.pyi new file mode 100644 index 0000000..e21336f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/trace/v1/trace_pb2.pyi @@ -0,0 +1,586 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2019, OpenTelemetry Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import opentelemetry.proto.common.v1.common_pb2 +import opentelemetry.proto.resource.v1.resource_pb2 +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _SpanFlags: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _SpanFlagsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_SpanFlags.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SPAN_FLAGS_DO_NOT_USE: _SpanFlags.ValueType # 0 + """The zero value for the enum. Should not be used for comparisons. + Instead use bitwise "and" with the appropriate mask as shown above. + """ + SPAN_FLAGS_TRACE_FLAGS_MASK: _SpanFlags.ValueType # 255 + """Bits 0-7 are used for trace flags.""" + SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK: _SpanFlags.ValueType # 256 + """Bits 8 and 9 are used to indicate that the parent span or link span is remote. + Bit 8 (`HAS_IS_REMOTE`) indicates whether the value is known. + Bit 9 (`IS_REMOTE`) indicates whether the span or link is remote. + """ + SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK: _SpanFlags.ValueType # 512 + +class SpanFlags(_SpanFlags, metaclass=_SpanFlagsEnumTypeWrapper): + """SpanFlags represents constants used to interpret the + Span.flags field, which is protobuf 'fixed32' type and is to + be used as bit-fields. Each non-zero value defined in this enum is + a bit-mask. To extract the bit-field, for example, use an + expression like: + + (span.flags & SPAN_FLAGS_TRACE_FLAGS_MASK) + + See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. + + Note that Span flags were introduced in version 1.1 of the + OpenTelemetry protocol. Older Span producers do not set this + field, consequently consumers should not rely on the absence of a + particular flag bit to indicate the presence of a particular feature. + """ + +SPAN_FLAGS_DO_NOT_USE: SpanFlags.ValueType # 0 +"""The zero value for the enum. Should not be used for comparisons. +Instead use bitwise "and" with the appropriate mask as shown above. +""" +SPAN_FLAGS_TRACE_FLAGS_MASK: SpanFlags.ValueType # 255 +"""Bits 0-7 are used for trace flags.""" +SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK: SpanFlags.ValueType # 256 +"""Bits 8 and 9 are used to indicate that the parent span or link span is remote. +Bit 8 (`HAS_IS_REMOTE`) indicates whether the value is known. +Bit 9 (`IS_REMOTE`) indicates whether the span or link is remote. +""" +SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK: SpanFlags.ValueType # 512 +global___SpanFlags = SpanFlags + +@typing_extensions.final +class TracesData(google.protobuf.message.Message): + """TracesData represents the traces data that can be stored in a persistent storage, + OR can be embedded by other protocols that transfer OTLP traces data but do + not implement the OTLP protocol. + + The main difference between this message and collector protocol is that + in this message there will not be any "control" or "metadata" specific to + OTLP protocol. + + When new fields are added into this message, the OTLP request MUST be updated + as well. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESOURCE_SPANS_FIELD_NUMBER: builtins.int + @property + def resource_spans(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ResourceSpans]: + """An array of ResourceSpans. + For data coming from a single resource this array will typically contain + one element. Intermediary nodes that receive data from multiple origins + typically batch the data before forwarding further and in that case this + array will contain multiple elements. + """ + def __init__( + self, + *, + resource_spans: collections.abc.Iterable[global___ResourceSpans] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["resource_spans", b"resource_spans"]) -> None: ... + +global___TracesData = TracesData + +@typing_extensions.final +class ResourceSpans(google.protobuf.message.Message): + """A collection of ScopeSpans from a Resource.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESOURCE_FIELD_NUMBER: builtins.int + SCOPE_SPANS_FIELD_NUMBER: builtins.int + SCHEMA_URL_FIELD_NUMBER: builtins.int + @property + def resource(self) -> opentelemetry.proto.resource.v1.resource_pb2.Resource: + """The resource for the spans in this message. + If this field is not set then no resource info is known. + """ + @property + def scope_spans(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ScopeSpans]: + """A list of ScopeSpans that originate from a resource.""" + schema_url: builtins.str + """The Schema URL, if known. This is the identifier of the Schema that the resource data + is recorded in. Notably, the last part of the URL path is the version number of the + schema: http[s]://server[:port]/path/. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + This schema_url applies to the data in the "resource" field. It does not apply + to the data in the "scope_spans" field which have their own schema_url field. + """ + def __init__( + self, + *, + resource: opentelemetry.proto.resource.v1.resource_pb2.Resource | None = ..., + scope_spans: collections.abc.Iterable[global___ScopeSpans] | None = ..., + schema_url: builtins.str = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["resource", b"resource"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["resource", b"resource", "schema_url", b"schema_url", "scope_spans", b"scope_spans"]) -> None: ... + +global___ResourceSpans = ResourceSpans + +@typing_extensions.final +class ScopeSpans(google.protobuf.message.Message): + """A collection of Spans produced by an InstrumentationScope.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SCOPE_FIELD_NUMBER: builtins.int + SPANS_FIELD_NUMBER: builtins.int + SCHEMA_URL_FIELD_NUMBER: builtins.int + @property + def scope(self) -> opentelemetry.proto.common.v1.common_pb2.InstrumentationScope: + """The instrumentation scope information for the spans in this message. + Semantically when InstrumentationScope isn't set, it is equivalent with + an empty instrumentation scope name (unknown). + """ + @property + def spans(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Span]: + """A list of Spans that originate from an instrumentation scope.""" + schema_url: builtins.str + """The Schema URL, if known. This is the identifier of the Schema that the span data + is recorded in. Notably, the last part of the URL path is the version number of the + schema: http[s]://server[:port]/path/. To learn more about Schema URL see + https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + This schema_url applies to the data in the "scope" field and all spans and span + events in the "spans" field. + """ + def __init__( + self, + *, + scope: opentelemetry.proto.common.v1.common_pb2.InstrumentationScope | None = ..., + spans: collections.abc.Iterable[global___Span] | None = ..., + schema_url: builtins.str = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["scope", b"scope"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["schema_url", b"schema_url", "scope", b"scope", "spans", b"spans"]) -> None: ... + +global___ScopeSpans = ScopeSpans + +@typing_extensions.final +class Span(google.protobuf.message.Message): + """A Span represents a single operation performed by a single component of the system. + + The next available field id is 17. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _SpanKind: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _SpanKindEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Span._SpanKind.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SPAN_KIND_UNSPECIFIED: Span._SpanKind.ValueType # 0 + """Unspecified. Do NOT use as default. + Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED. + """ + SPAN_KIND_INTERNAL: Span._SpanKind.ValueType # 1 + """Indicates that the span represents an internal operation within an application, + as opposed to an operation happening at the boundaries. Default value. + """ + SPAN_KIND_SERVER: Span._SpanKind.ValueType # 2 + """Indicates that the span covers server-side handling of an RPC or other + remote network request. + """ + SPAN_KIND_CLIENT: Span._SpanKind.ValueType # 3 + """Indicates that the span describes a request to some remote service.""" + SPAN_KIND_PRODUCER: Span._SpanKind.ValueType # 4 + """Indicates that the span describes a producer sending a message to a broker. + Unlike CLIENT and SERVER, there is often no direct critical path latency relationship + between producer and consumer spans. A PRODUCER span ends when the message was accepted + by the broker while the logical processing of the message might span a much longer time. + """ + SPAN_KIND_CONSUMER: Span._SpanKind.ValueType # 5 + """Indicates that the span describes consumer receiving a message from a broker. + Like the PRODUCER kind, there is often no direct critical path latency relationship + between producer and consumer spans. + """ + + class SpanKind(_SpanKind, metaclass=_SpanKindEnumTypeWrapper): + """SpanKind is the type of span. Can be used to specify additional relationships between spans + in addition to a parent/child relationship. + """ + + SPAN_KIND_UNSPECIFIED: Span.SpanKind.ValueType # 0 + """Unspecified. Do NOT use as default. + Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED. + """ + SPAN_KIND_INTERNAL: Span.SpanKind.ValueType # 1 + """Indicates that the span represents an internal operation within an application, + as opposed to an operation happening at the boundaries. Default value. + """ + SPAN_KIND_SERVER: Span.SpanKind.ValueType # 2 + """Indicates that the span covers server-side handling of an RPC or other + remote network request. + """ + SPAN_KIND_CLIENT: Span.SpanKind.ValueType # 3 + """Indicates that the span describes a request to some remote service.""" + SPAN_KIND_PRODUCER: Span.SpanKind.ValueType # 4 + """Indicates that the span describes a producer sending a message to a broker. + Unlike CLIENT and SERVER, there is often no direct critical path latency relationship + between producer and consumer spans. A PRODUCER span ends when the message was accepted + by the broker while the logical processing of the message might span a much longer time. + """ + SPAN_KIND_CONSUMER: Span.SpanKind.ValueType # 5 + """Indicates that the span describes consumer receiving a message from a broker. + Like the PRODUCER kind, there is often no direct critical path latency relationship + between producer and consumer spans. + """ + + @typing_extensions.final + class Event(google.protobuf.message.Message): + """Event is a time-stamped annotation of the span, consisting of user-supplied + text description and key-value pairs. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + DROPPED_ATTRIBUTES_COUNT_FIELD_NUMBER: builtins.int + time_unix_nano: builtins.int + """The time the event occurred.""" + name: builtins.str + """The name of the event. + This field is semantically required to be set to non-empty string. + """ + @property + def attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.common.v1.common_pb2.KeyValue]: + """A collection of attribute key/value pairs on the event. + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. + """ + dropped_attributes_count: builtins.int + """The number of dropped attributes. If the value is 0, + then no attributes were dropped. + """ + def __init__( + self, + *, + time_unix_nano: builtins.int = ..., + name: builtins.str = ..., + attributes: collections.abc.Iterable[opentelemetry.proto.common.v1.common_pb2.KeyValue] | None = ..., + dropped_attributes_count: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["attributes", b"attributes", "dropped_attributes_count", b"dropped_attributes_count", "name", b"name", "time_unix_nano", b"time_unix_nano"]) -> None: ... + + @typing_extensions.final + class Link(google.protobuf.message.Message): + """A pointer from the current span to another span in the same trace or in a + different trace. For example, this can be used in batching operations, + where a single batch handler processes multiple requests from different + traces or when the handler receives a request from a different project. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACE_ID_FIELD_NUMBER: builtins.int + SPAN_ID_FIELD_NUMBER: builtins.int + TRACE_STATE_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + DROPPED_ATTRIBUTES_COUNT_FIELD_NUMBER: builtins.int + FLAGS_FIELD_NUMBER: builtins.int + trace_id: builtins.bytes + """A unique identifier of a trace that this linked span is part of. The ID is a + 16-byte array. + """ + span_id: builtins.bytes + """A unique identifier for the linked span. The ID is an 8-byte array.""" + trace_state: builtins.str + """The trace_state associated with the link.""" + @property + def attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.common.v1.common_pb2.KeyValue]: + """A collection of attribute key/value pairs on the link. + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. + """ + dropped_attributes_count: builtins.int + """The number of dropped attributes. If the value is 0, + then no attributes were dropped. + """ + flags: builtins.int + """Flags, a bit field. + + Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace + Context specification. To read the 8-bit W3C trace flag, use + `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. + + See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. + + Bits 8 and 9 represent the 3 states of whether the link is remote. + The states are (unknown, is not remote, is remote). + To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. + To read whether the link is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. + + Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. + When creating new spans, bits 10-31 (most-significant 22-bits) MUST be zero. + + [Optional]. + """ + def __init__( + self, + *, + trace_id: builtins.bytes = ..., + span_id: builtins.bytes = ..., + trace_state: builtins.str = ..., + attributes: collections.abc.Iterable[opentelemetry.proto.common.v1.common_pb2.KeyValue] | None = ..., + dropped_attributes_count: builtins.int = ..., + flags: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["attributes", b"attributes", "dropped_attributes_count", b"dropped_attributes_count", "flags", b"flags", "span_id", b"span_id", "trace_id", b"trace_id", "trace_state", b"trace_state"]) -> None: ... + + TRACE_ID_FIELD_NUMBER: builtins.int + SPAN_ID_FIELD_NUMBER: builtins.int + TRACE_STATE_FIELD_NUMBER: builtins.int + PARENT_SPAN_ID_FIELD_NUMBER: builtins.int + FLAGS_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + KIND_FIELD_NUMBER: builtins.int + START_TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + END_TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + DROPPED_ATTRIBUTES_COUNT_FIELD_NUMBER: builtins.int + EVENTS_FIELD_NUMBER: builtins.int + DROPPED_EVENTS_COUNT_FIELD_NUMBER: builtins.int + LINKS_FIELD_NUMBER: builtins.int + DROPPED_LINKS_COUNT_FIELD_NUMBER: builtins.int + STATUS_FIELD_NUMBER: builtins.int + trace_id: builtins.bytes + """A unique identifier for a trace. All spans from the same trace share + the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR + of length other than 16 bytes is considered invalid (empty string in OTLP/JSON + is zero-length and thus is also invalid). + + This field is required. + """ + span_id: builtins.bytes + """A unique identifier for a span within a trace, assigned when the span + is created. The ID is an 8-byte array. An ID with all zeroes OR of length + other than 8 bytes is considered invalid (empty string in OTLP/JSON + is zero-length and thus is also invalid). + + This field is required. + """ + trace_state: builtins.str + """trace_state conveys information about request position in multiple distributed tracing graphs. + It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header + See also https://github.com/w3c/distributed-tracing for more details about this field. + """ + parent_span_id: builtins.bytes + """The `span_id` of this span's parent span. If this is a root span, then this + field must be empty. The ID is an 8-byte array. + """ + flags: builtins.int + """Flags, a bit field. + + Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace + Context specification. To read the 8-bit W3C trace flag, use + `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. + + See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. + + Bits 8 and 9 represent the 3 states of whether a span's parent + is remote. The states are (unknown, is not remote, is remote). + To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. + To read whether the span is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. + + When creating span messages, if the message is logically forwarded from another source + with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD + be copied as-is. If creating from a source that does not have an equivalent flags field + (such as a runtime representation of an OpenTelemetry span), the high 22 bits MUST + be set to zero. + Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. + + [Optional]. + """ + name: builtins.str + """A description of the span's operation. + + For example, the name can be a qualified method name or a file name + and a line number where the operation is called. A best practice is to use + the same display name at the same call point in an application. + This makes it easier to correlate spans in different traces. + + This field is semantically required to be set to non-empty string. + Empty value is equivalent to an unknown span name. + + This field is required. + """ + kind: global___Span.SpanKind.ValueType + """Distinguishes between spans generated in a particular context. For example, + two spans with the same name may be distinguished using `CLIENT` (caller) + and `SERVER` (callee) to identify queueing latency associated with the span. + """ + start_time_unix_nano: builtins.int + """The start time of the span. On the client side, this is the time + kept by the local machine where the span execution starts. On the server side, this + is the time when the server's application handler starts running. + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + + This field is semantically required and it is expected that end_time >= start_time. + """ + end_time_unix_nano: builtins.int + """The end time of the span. On the client side, this is the time + kept by the local machine where the span execution ends. On the server side, this + is the time when the server application handler stops running. + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + + This field is semantically required and it is expected that end_time >= start_time. + """ + @property + def attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[opentelemetry.proto.common.v1.common_pb2.KeyValue]: + """A collection of key/value pairs. Note, global attributes + like server name can be set using the resource API. Examples of attributes: + + "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" + "/http/server_latency": 300 + "example.com/myattribute": true + "example.com/score": 10.239 + + Attribute keys MUST be unique (it is not allowed to have more than one + attribute with the same key). + The behavior of software that receives duplicated keys can be unpredictable. + """ + dropped_attributes_count: builtins.int + """The number of attributes that were discarded. Attributes + can be discarded because their keys are too long or because there are too many + attributes. If this value is 0, then no attributes were dropped. + """ + @property + def events(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Span.Event]: + """A collection of Event items.""" + dropped_events_count: builtins.int + """The number of dropped events. If the value is 0, then no + events were dropped. + """ + @property + def links(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Span.Link]: + """A collection of Links, which are references from this span to a span + in the same or different trace. + """ + dropped_links_count: builtins.int + """The number of dropped links after the maximum size was + enforced. If this value is 0, then no links were dropped. + """ + @property + def status(self) -> global___Status: + """An optional final status for this span. Semantically when Status isn't set, it means + span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0). + """ + def __init__( + self, + *, + trace_id: builtins.bytes = ..., + span_id: builtins.bytes = ..., + trace_state: builtins.str = ..., + parent_span_id: builtins.bytes = ..., + flags: builtins.int = ..., + name: builtins.str = ..., + kind: global___Span.SpanKind.ValueType = ..., + start_time_unix_nano: builtins.int = ..., + end_time_unix_nano: builtins.int = ..., + attributes: collections.abc.Iterable[opentelemetry.proto.common.v1.common_pb2.KeyValue] | None = ..., + dropped_attributes_count: builtins.int = ..., + events: collections.abc.Iterable[global___Span.Event] | None = ..., + dropped_events_count: builtins.int = ..., + links: collections.abc.Iterable[global___Span.Link] | None = ..., + dropped_links_count: builtins.int = ..., + status: global___Status | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["status", b"status"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["attributes", b"attributes", "dropped_attributes_count", b"dropped_attributes_count", "dropped_events_count", b"dropped_events_count", "dropped_links_count", b"dropped_links_count", "end_time_unix_nano", b"end_time_unix_nano", "events", b"events", "flags", b"flags", "kind", b"kind", "links", b"links", "name", b"name", "parent_span_id", b"parent_span_id", "span_id", b"span_id", "start_time_unix_nano", b"start_time_unix_nano", "status", b"status", "trace_id", b"trace_id", "trace_state", b"trace_state"]) -> None: ... + +global___Span = Span + +@typing_extensions.final +class Status(google.protobuf.message.Message): + """The Status type defines a logical error model that is suitable for different + programming environments, including REST APIs and RPC APIs. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _StatusCode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _StatusCodeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Status._StatusCode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + STATUS_CODE_UNSET: Status._StatusCode.ValueType # 0 + """The default status.""" + STATUS_CODE_OK: Status._StatusCode.ValueType # 1 + """The Span has been validated by an Application developer or Operator to + have completed successfully. + """ + STATUS_CODE_ERROR: Status._StatusCode.ValueType # 2 + """The Span contains an error.""" + + class StatusCode(_StatusCode, metaclass=_StatusCodeEnumTypeWrapper): + """For the semantics of status codes see + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status + """ + + STATUS_CODE_UNSET: Status.StatusCode.ValueType # 0 + """The default status.""" + STATUS_CODE_OK: Status.StatusCode.ValueType # 1 + """The Span has been validated by an Application developer or Operator to + have completed successfully. + """ + STATUS_CODE_ERROR: Status.StatusCode.ValueType # 2 + """The Span contains an error.""" + + MESSAGE_FIELD_NUMBER: builtins.int + CODE_FIELD_NUMBER: builtins.int + message: builtins.str + """A developer-facing human readable error message.""" + code: global___Status.StatusCode.ValueType + """The status code.""" + def __init__( + self, + *, + message: builtins.str = ..., + code: global___Status.StatusCode.ValueType = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["code", b"code", "message", b"message"]) -> None: ... + +global___Status = Status diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/version/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/proto/version/__init__.py new file mode 100644 index 0000000..178452f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/proto/version/__init__.py @@ -0,0 +1,4 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +__version__ = "1.45.0" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/proto/version/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/proto/version/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4c70345 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/proto/version/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/__init__.pyi b/venv/lib/python3.12/site-packages/opentelemetry/sdk/__init__.pyi new file mode 100644 index 0000000..e57edc0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/__init__.pyi @@ -0,0 +1,18 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +The OpenTelemetry SDK package is an implementation of the OpenTelemetry +API +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_configuration/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_configuration/__init__.py new file mode 100644 index 0000000..2516e14 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_configuration/__init__.py @@ -0,0 +1,676 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +# + +""" +OpenTelemetry SDK Configurator for Easy Instrumentation with Distros +""" + +from __future__ import annotations + +import logging +import logging.config +import os +import warnings +from abc import ABC, abstractmethod +from collections.abc import Callable, Mapping, Sequence +from os import environ +from typing import Any, Literal, Protocol + +from opentelemetry._logs import set_logger_provider +from opentelemetry.environment_variables import ( + OTEL_LOGS_EXPORTER, + OTEL_METRICS_EXPORTER, + OTEL_PYTHON_ID_GENERATOR, + OTEL_TRACES_EXPORTER, +) +from opentelemetry.metrics import set_meter_provider +from opentelemetry.sdk._logs import ( + LoggerProvider, + LoggingHandler, + LogRecordProcessor, +) +from opentelemetry.sdk._logs._internal import _LoggerConfiguratorT +from opentelemetry.sdk._logs.export import ( + BatchLogRecordProcessor, + LogRecordExporter, +) +from opentelemetry.sdk.environment_variables import ( + _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, + OTEL_CONFIG_FILE, + OTEL_EXPORTER_OTLP_LOGS_PROTOCOL, + OTEL_EXPORTER_OTLP_METRICS_PROTOCOL, + OTEL_EXPORTER_OTLP_PROTOCOL, + OTEL_EXPORTER_OTLP_TRACES_PROTOCOL, + OTEL_PYTHON_LOGGER_CONFIGURATOR, + OTEL_PYTHON_METER_CONFIGURATOR, + OTEL_PYTHON_TRACER_CONFIGURATOR, + OTEL_TRACES_SAMPLER, + OTEL_TRACES_SAMPLER_ARG, +) +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics._internal import _MeterConfiguratorT +from opentelemetry.sdk.metrics.export import ( + MetricExporter, + MetricReader, + PeriodicExportingMetricReader, +) +from opentelemetry.sdk.resources import Attributes, Resource +from opentelemetry.sdk.trace import ( + SpanProcessor, + TracerProvider, + _TracerConfiguratorT, +) +from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter +from opentelemetry.sdk.trace.id_generator import IdGenerator +from opentelemetry.sdk.trace.sampling import Sampler +from opentelemetry.semconv.resource import ResourceAttributes +from opentelemetry.trace import set_tracer_provider +from opentelemetry.util._importlib_metadata import entry_points + +_EXPORTER_OTLP = "otlp" +_EXPORTER_OTLP_PROTO_GRPC = "otlp_proto_grpc" +_EXPORTER_OTLP_PROTO_HTTP = "otlp_proto_http" + +_EXPORTER_BY_OTLP_PROTOCOL = { + "grpc": _EXPORTER_OTLP_PROTO_GRPC, + "http/protobuf": _EXPORTER_OTLP_PROTO_HTTP, +} + +_EXPORTER_ENV_BY_SIGNAL_TYPE = { + "traces": OTEL_TRACES_EXPORTER, + "metrics": OTEL_METRICS_EXPORTER, + "logs": OTEL_LOGS_EXPORTER, +} + +_PROTOCOL_ENV_BY_SIGNAL_TYPE = { + "traces": OTEL_EXPORTER_OTLP_TRACES_PROTOCOL, + "metrics": OTEL_EXPORTER_OTLP_METRICS_PROTOCOL, + "logs": OTEL_EXPORTER_OTLP_LOGS_PROTOCOL, +} + +_RANDOM_ID_GENERATOR = "random" +_DEFAULT_ID_GENERATOR = _RANDOM_ID_GENERATOR + +_OTEL_SAMPLER_ENTRY_POINT_GROUP = "opentelemetry_traces_sampler" + +_logger = logging.getLogger(__name__) + +ExporterArgsMap = Mapping[ + type[SpanExporter] | type[MetricExporter] | type[MetricReader] | type[LogRecordExporter], + Mapping[str, Any], +] + + +class _ConfigurationExporterSpanProcessorT(Protocol): + def __call__(self, span_exporter: SpanExporter, *args, **kwargs) -> SpanProcessor: ... + + +class _ConfigurationExporterLogRecordProcessorT(Protocol): + def __call__(self, exporter: LogRecordExporter, *args, **kwargs) -> LogRecordProcessor: ... + + +def _import_config_components(selected_components: Sequence[str], entry_point_name: str) -> list[tuple[str, type]]: + component_implementations = [] + + for selected_component in selected_components: + try: + component_implementations.append( + ( + selected_component, + next(iter(entry_points(group=entry_point_name, name=selected_component))).load(), + ) + ) + except KeyError: + raise RuntimeError(f"Requested entry point '{entry_point_name}' not found") + + except StopIteration: + raise RuntimeError( + f"Requested component '{selected_component}' not found in entry point '{entry_point_name}'" + ) + + return component_implementations + + +def _get_sampler() -> str | None: + return environ.get(OTEL_TRACES_SAMPLER, None) + + +def _get_id_generator() -> str: + return environ.get(OTEL_PYTHON_ID_GENERATOR, _DEFAULT_ID_GENERATOR) + + +def _get_tracer_configurator() -> str | None: + return environ.get(OTEL_PYTHON_TRACER_CONFIGURATOR, None) + + +def _get_meter_configurator() -> str | None: + return environ.get(OTEL_PYTHON_METER_CONFIGURATOR, None) + + +def _get_logger_configurator() -> str | None: + return environ.get(OTEL_PYTHON_LOGGER_CONFIGURATOR, None) + + +def _get_exporter_entry_point(exporter_name: str, signal_type: Literal["traces", "metrics", "logs"]): + if exporter_name not in ( + _EXPORTER_OTLP, + _EXPORTER_OTLP_PROTO_GRPC, + _EXPORTER_OTLP_PROTO_HTTP, + ): + return exporter_name + + # Checking env vars for OTLP protocol (grpc/http). + otlp_protocol = environ.get(_PROTOCOL_ENV_BY_SIGNAL_TYPE[signal_type]) or environ.get(OTEL_EXPORTER_OTLP_PROTOCOL) + + if not otlp_protocol: + if exporter_name == _EXPORTER_OTLP: + return _EXPORTER_OTLP_PROTO_GRPC + return exporter_name + + otlp_protocol = otlp_protocol.strip() + + if exporter_name == _EXPORTER_OTLP: + if otlp_protocol not in _EXPORTER_BY_OTLP_PROTOCOL: + # Invalid value was set by the env var + raise RuntimeError(f"Unsupported OTLP protocol '{otlp_protocol}' is configured") + + return _EXPORTER_BY_OTLP_PROTOCOL[otlp_protocol] + + # grpc/http already specified by exporter_name, only add a warning in case + # of a conflict. + exporter_name_by_env = _EXPORTER_BY_OTLP_PROTOCOL.get(otlp_protocol) + if exporter_name_by_env and exporter_name != exporter_name_by_env: + _logger.warning( + "Conflicting values for %s OTLP exporter protocol, using '%s'", + signal_type, + exporter_name, + ) + + return exporter_name + + +def _get_exporter_names( + signal_type: Literal["traces", "metrics", "logs"], +) -> list[str]: + names = environ.get(_EXPORTER_ENV_BY_SIGNAL_TYPE.get(signal_type, "")) + + if not names or names.lower().strip() == "none": + return [] + + return [_get_exporter_entry_point(_exporter.strip(), signal_type) for _exporter in names.split(",")] + + +def _init_tracing( + exporters: dict[str, type[SpanExporter]], + id_generator: IdGenerator | None = None, + sampler: Sampler | None = None, + resource: Resource | None = None, + exporter_args_map: ExporterArgsMap | None = None, + span_processors: Sequence[SpanProcessor] | None = None, + export_span_processor: _ConfigurationExporterSpanProcessorT | None = None, + tracer_configurator: _TracerConfiguratorT | None = None, +): + provider = TracerProvider( + id_generator=id_generator, + sampler=sampler, + resource=resource, + _tracer_configurator=tracer_configurator, + ) + set_tracer_provider(provider) + + exporter_args_map = exporter_args_map or {} + export_processor = export_span_processor or BatchSpanProcessor + + span_processors = span_processors or [] + for span_processor in span_processors: + provider.add_span_processor(span_processor) + + for _, exporter_class in exporters.items(): + exporter_args = exporter_args_map.get(exporter_class, {}) + provider.add_span_processor(export_processor(exporter_class(**exporter_args))) + + +def _init_metrics( + exporters_or_readers: dict[str, type[MetricExporter | MetricReader]], + resource: Resource | None = None, + exporter_args_map: ExporterArgsMap | None = None, + meter_configurator: _MeterConfiguratorT | None = None, +): + metric_readers = [] + + exporter_args_map = exporter_args_map or {} + for _, exporter_or_reader_class in exporters_or_readers.items(): + exporter_args = exporter_args_map.get(exporter_or_reader_class, {}) + if issubclass(exporter_or_reader_class, MetricReader): + metric_readers.append(exporter_or_reader_class(**exporter_args)) + else: + metric_readers.append(PeriodicExportingMetricReader(exporter_or_reader_class(**exporter_args))) + + provider = MeterProvider( + resource=resource, + metric_readers=metric_readers, + _meter_configurator=meter_configurator, + ) + set_meter_provider(provider) + + +# pylint: disable-next=too-many-locals +def _init_logging( + exporters: dict[str, type[LogRecordExporter]], + resource: Resource | None = None, + setup_logging_handler: bool = True, + exporter_args_map: ExporterArgsMap | None = None, + log_record_processors: Sequence[LogRecordProcessor] | None = None, + export_log_record_processor: _ConfigurationExporterLogRecordProcessorT | None = None, + logger_configurator: _LoggerConfiguratorT | None = None, +): + provider = LoggerProvider(resource=resource, _logger_configurator=logger_configurator) + set_logger_provider(provider) + + exporter_args_map = exporter_args_map or {} + export_processor = export_log_record_processor or BatchLogRecordProcessor + + log_record_processors = log_record_processors or [] + for log_record_processor in log_record_processors: + provider.add_log_record_processor(log_record_processor) + + for _, exporter_class in exporters.items(): + exporter_args = exporter_args_map.get(exporter_class, {}) + provider.add_log_record_processor(export_processor(exporter_class(**exporter_args))) + + if setup_logging_handler: + warnings.warn( + "The `OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED` environment variable " + "and the `LoggingHandler` in `opentelemetry-sdk` that it controls are deprecated." + "Install `opentelemetry-instrumentation-logging` package instead.", + DeprecationWarning, + ) + + # Add OTel handler + handler = LoggingHandler(level=logging.NOTSET, logger_provider=provider) + logging.getLogger().addHandler(handler) + _overwrite_logging_config_fns(handler) + + +def _overwrite_logging_config_fns(handler: LoggingHandler) -> None: + root = logging.getLogger() + + def wrapper(config_fn: Callable) -> Callable: + def overwritten_config_fn(*args, **kwargs): + removed_handler = False + # We don't want the OTLP handler to be modified or deleted by the logging config functions. + # So we remove it and then add it back after the function call. + if handler in root.handlers: + removed_handler = True + root.handlers.remove(handler) + try: + config_fn(*args, **kwargs) + finally: + # Ensure handler is added back if logging function throws exception. + if removed_handler: + root.addHandler(handler) + + return overwritten_config_fn + + logging.config.fileConfig = wrapper(logging.config.fileConfig) + logging.config.dictConfig = wrapper(logging.config.dictConfig) + logging.basicConfig = wrapper(logging.basicConfig) + + +def _import_logger_configurator( + logger_configurator_name: str | None, +) -> _LoggerConfiguratorT | None: + if not logger_configurator_name: + return None + + try: + _, logger_configurator_impl = _import_config_components( + [logger_configurator_name.strip()], + "_opentelemetry_logger_configurator", + )[0] + except Exception as exc: # pylint: disable=broad-exception-caught + _logger.warning( + "Using default logger configurator. Failed to load logger configurator, %s: %s", + logger_configurator_name, + exc, + ) + return None + return logger_configurator_impl + + +def _import_tracer_configurator( + tracer_configurator_name: str | None, +) -> _TracerConfiguratorT | None: + if not tracer_configurator_name: + return None + + try: + _, tracer_configurator_impl = _import_config_components( + [tracer_configurator_name.strip()], + "_opentelemetry_tracer_configurator", + )[0] + except Exception as exc: # pylint: disable=broad-exception-caught + _logger.warning( + "Using default tracer configurator. Failed to load tracer configurator, %s: %s", + tracer_configurator_name, + exc, + ) + return None + return tracer_configurator_impl + + +def _import_meter_configurator( + meter_configurator_name: str | None, +) -> _MeterConfiguratorT | None: + if not meter_configurator_name: + return None + + try: + _, meter_configurator_impl = _import_config_components( + [meter_configurator_name.strip()], + "_opentelemetry_meter_configurator", + )[0] + except Exception as exc: # pylint: disable=broad-exception-caught + _logger.warning( + "Using default meter configurator. Failed to load meter configurator, %s: %s", + meter_configurator_name, + exc, + ) + return None + return meter_configurator_impl + + +def _import_exporters( + trace_exporter_names: Sequence[str], + metric_exporter_names: Sequence[str], + log_exporter_names: Sequence[str], +) -> tuple[ + dict[str, type[SpanExporter]], + dict[str, type[MetricExporter | MetricReader]], + dict[str, type[LogRecordExporter]], +]: + trace_exporters = {} + metric_exporters = {} + log_exporters = {} + + for ( + exporter_name, + exporter_impl, + ) in _import_config_components(trace_exporter_names, "opentelemetry_traces_exporter"): + if issubclass(exporter_impl, SpanExporter): + trace_exporters[exporter_name] = exporter_impl + else: + raise RuntimeError(f"{exporter_name} is not a trace exporter") + + for ( + exporter_name, + exporter_impl, + ) in _import_config_components(metric_exporter_names, "opentelemetry_metrics_exporter"): + # The metric exporter components may be push MetricExporter or pull exporters which + # subclass MetricReader directly + if issubclass(exporter_impl, (MetricExporter, MetricReader)): + metric_exporters[exporter_name] = exporter_impl + else: + raise RuntimeError(f"{exporter_name} is not a metric exporter") + + for ( + exporter_name, + exporter_impl, + ) in _import_config_components(log_exporter_names, "opentelemetry_logs_exporter"): + if issubclass(exporter_impl, LogRecordExporter): + log_exporters[exporter_name] = exporter_impl + else: + raise RuntimeError(f"{exporter_name} is not a log exporter") + + return trace_exporters, metric_exporters, log_exporters + + +def _import_sampler_factory( + sampler_name: str, +) -> Callable[[float | str | None], Sampler]: + _, sampler_impl = _import_config_components([sampler_name.strip()], _OTEL_SAMPLER_ENTRY_POINT_GROUP)[0] + return sampler_impl + + +def _import_sampler(sampler_name: str | None) -> Sampler | None: + if not sampler_name: + return None + try: + sampler_factory = _import_sampler_factory(sampler_name) + arg = None + if sampler_name in ("traceidratio", "parentbased_traceidratio"): + try: + rate = float(os.getenv(OTEL_TRACES_SAMPLER_ARG, "")) + except (ValueError, TypeError): + _logger.warning("Could not convert TRACES_SAMPLER_ARG to float. Using default value 1.0.") + rate = 1.0 + arg = rate + else: + arg = os.getenv(OTEL_TRACES_SAMPLER_ARG) + + sampler = sampler_factory(arg) + if not isinstance(sampler, Sampler): + message = f"Sampler factory, {sampler_factory}, produced output, {sampler}, which is not a Sampler." + _logger.warning(message) + raise ValueError(message) + return sampler + except Exception as exc: # pylint: disable=broad-exception-caught + _logger.warning( + "Using default sampler. Failed to initialize sampler, %s: %s", + sampler_name, + exc, + ) + return None + + +def _import_id_generator(id_generator_name: str) -> IdGenerator: + id_generator_name, id_generator_impl = _import_config_components( + [id_generator_name.strip()], "opentelemetry_id_generator" + )[0] + + if issubclass(id_generator_impl, IdGenerator): + return id_generator_impl() + + raise RuntimeError(f"{id_generator_name} is not an IdGenerator") + + +def _import_opamp( + name: Literal["pre_sdk_init_function", "post_sdk_init_function"], +) -> Callable[[Resource], None] | None: + """Helper for OpAMP entry points loading + + This in development, at the moment we are looking for a callable that takes + the resource and instantiate an OpAMP agent. + Since configuration is not specified every implementer may have its own. + Refer to the opentelemetry-opamp-client package on how to setup the OpAMP agent. + """ + entry_point = None + try: + entry_point = next(iter(entry_points(group="_opentelemetry_opamp", name=name))) + return entry_point.load() + except StopIteration: + _logger.debug("No OpAMP init function found") + except AttributeError as exc: + _logger.warning( + "Failed to load OpAMP init function from entry point, %s: %s", + entry_point, + exc, + ) + + return None + + +def _initialize_components( + auto_instrumentation_version: str | None = None, + trace_exporter_names: list[str] | None = None, + metric_exporter_names: list[str] | None = None, + log_exporter_names: list[str] | None = None, + sampler: Sampler | None = None, + resource_attributes: Attributes | None = None, + id_generator: IdGenerator | None = None, + setup_logging_handler: bool | None = None, + exporter_args_map: ExporterArgsMap | None = None, + span_processors: Sequence[SpanProcessor] | None = None, + export_span_processor: _ConfigurationExporterSpanProcessorT | None = None, + log_record_processors: Sequence[LogRecordProcessor] | None = None, + export_log_record_processor: _ConfigurationExporterLogRecordProcessorT | None = None, + tracer_configurator: _TracerConfiguratorT | None = None, + meter_configurator: _MeterConfiguratorT | None = None, + logger_configurator: _LoggerConfiguratorT | None = None, +): + # pylint: disable=too-many-locals,too-many-branches + if resource_attributes is None: + resource_attributes = {} + # populate version if using auto-instrumentation + if auto_instrumentation_version: + resource_attributes[ResourceAttributes.TELEMETRY_AUTO_VERSION] = ( # type: ignore[reportIndexIssue] + auto_instrumentation_version + ) + # if env var OTEL_RESOURCE_ATTRIBUTES is given, it will read the service_name + # from the env variable else defaults to "unknown_service" + resource = Resource.create(resource_attributes) + + # OpAMP is a system created to configure OpenTelemetry SDKs with a remote config. + # This is different than other init helpers because setting up OpAMP requires distro + # provided code as it's not strictly specified. We have two entry points for OpAMP: + # one called early for people that want it blocking to get an updated config before + # setting up the rest of the SDK and the other after for people that want the + # SDK already setup. + _init_opamp = _import_opamp("pre_sdk_init_function") + if _init_opamp is not None: + _init_opamp(resource) + + if trace_exporter_names is None: + trace_exporter_names = [] + if metric_exporter_names is None: + metric_exporter_names = [] + if log_exporter_names is None: + log_exporter_names = [] + span_exporters, metric_exporters, log_exporters = _import_exporters( + trace_exporter_names + _get_exporter_names("traces"), + metric_exporter_names + _get_exporter_names("metrics"), + log_exporter_names + _get_exporter_names("logs"), + ) + if sampler is None: + sampler_name = _get_sampler() + sampler = _import_sampler(sampler_name) + if id_generator is None: + id_generator_name = _get_id_generator() + id_generator = _import_id_generator(id_generator_name) + + if tracer_configurator is None: + tracer_configurator_name = _get_tracer_configurator() + tracer_configurator = _import_tracer_configurator(tracer_configurator_name) + if meter_configurator is None: + meter_configurator_name = _get_meter_configurator() + meter_configurator = _import_meter_configurator(meter_configurator_name) + if logger_configurator is None: + logger_configurator_name = _get_logger_configurator() + logger_configurator = _import_logger_configurator(logger_configurator_name) + + _init_tracing( + exporters=span_exporters, + id_generator=id_generator, + sampler=sampler, + resource=resource, + exporter_args_map=exporter_args_map, + span_processors=span_processors, + export_span_processor=export_span_processor, + tracer_configurator=tracer_configurator, + ) + _init_metrics( + exporters_or_readers=metric_exporters, + resource=resource, + exporter_args_map=exporter_args_map, + meter_configurator=meter_configurator, + ) + if setup_logging_handler is None: + setup_logging_handler = ( + os.getenv(_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, "false").strip().lower() == "true" + ) + _init_logging( + log_exporters, + resource, + setup_logging_handler, + exporter_args_map=exporter_args_map, + log_record_processors=log_record_processors, + export_log_record_processor=export_log_record_processor, + logger_configurator=logger_configurator, + ) + + _init_opamp = _import_opamp("post_sdk_init_function") + if _init_opamp is not None: + _init_opamp(resource) + + +class _BaseConfigurator(ABC): + """An ABC for configurators + + Configurators are used to configure + SDKs (i.e. TracerProvider, MeterProvider, Processors...) + to reduce the amount of manual configuration required. + """ + + _instance = None + _is_instrumented = False + + def __new__(cls, *args, **kwargs): + if cls._instance is None: + cls._instance = super().__new__(cls) + + return cls._instance + + @abstractmethod + def _configure(self, **kwargs): + """Configure the SDK""" + + def configure(self, **kwargs): + """Configure the SDK""" + self._configure(**kwargs) + + +class _OTelSDKConfigurator(_BaseConfigurator): + """A basic Configurator by OTel Python for initializing OTel SDK components + + Initializes several crucial OTel SDK components (i.e. TracerProvider, + MeterProvider, Processors...) according to a default implementation. Other + Configurators can subclass and slightly alter this initialization. + + NOTE: This class should not be instantiated nor should it become an entry + point on the `opentelemetry-sdk` package. Instead, distros should subclass + this Configurator and enhance it as needed. + """ + + def _configure(self, **kwargs): + if config_file := environ.get(OTEL_CONFIG_FILE): + # Declarative configuration lives in the separate + # ``opentelemetry-configuration`` package. Import lazily so the + # SDK has no runtime dependency on it; users who don't set + # ``OTEL_CONFIG_FILE`` never pay the import cost. + try: + # opentelemetry-configuration is an optional runtime dep + # and is not installed in this package's lint env, so + # silence the static-analysis no-name-in-module on the + # conditional import. + # pylint: disable=import-outside-toplevel,no-name-in-module + from opentelemetry.configuration import ( # noqa: PLC0415 + configure_sdk, + load_config_file, + ) + except ImportError as exc: + raise RuntimeError( + f"{OTEL_CONFIG_FILE} is set but " + "opentelemetry-configuration is not installed. " + "Install it with: pip install opentelemetry-configuration" + ) from exc + + if kwargs: + _logger.warning( + "%s is set; ignoring configurator kwargs: %s", + OTEL_CONFIG_FILE, + sorted(kwargs), + ) + configure_sdk(load_config_file(config_file)) + return + _initialize_components(**kwargs) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_configuration/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_configuration/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a406f92 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_configuration/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/__init__.py new file mode 100644 index 0000000..36f218e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/__init__.py @@ -0,0 +1,32 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from opentelemetry.sdk._logs._internal import ( + ConcurrentMultiLogRecordProcessor, + LogDroppedAttributesWarning, + Logger, + LoggerProvider, + LoggingHandler, + LogLimits, + LogRecordDroppedAttributesWarning, + LogRecordLimits, + LogRecordProcessor, + ReadableLogRecord, + ReadWriteLogRecord, + SynchronousMultiLogRecordProcessor, +) + +__all__ = [ + "ConcurrentMultiLogRecordProcessor", + "LogDroppedAttributesWarning", + "LogLimits", + "LogRecordDroppedAttributesWarning", + "LogRecordLimits", + "LogRecordProcessor", + "Logger", + "LoggerProvider", + "LoggingHandler", + "ReadWriteLogRecord", + "ReadableLogRecord", + "SynchronousMultiLogRecordProcessor", +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0e231a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/__init__.py new file mode 100644 index 0000000..bc09fd1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/__init__.py @@ -0,0 +1,1092 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import abc +import atexit +import base64 +import concurrent.futures +import json +import logging +import os +import threading +import traceback +import warnings +from collections.abc import Callable, Mapping, Sequence +from dataclasses import dataclass, field +from os import environ +from threading import Lock +from time import time_ns +from types import NoneType +from typing import ( # noqa + Any, + Tuple, + Union, + cast, + overload, +) +from weakref import WeakMethod, WeakSet + +from typing_extensions import deprecated + +from opentelemetry._logs import Logger as APILogger +from opentelemetry._logs import LoggerProvider as APILoggerProvider +from opentelemetry._logs import ( + LogRecord, + NoOpLogger, + SeverityNumber, + get_logger, + get_logger_provider, +) +from opentelemetry.attributes import BoundedAttributes +from opentelemetry.context import get_current +from opentelemetry.context.context import Context +from opentelemetry.metrics import MeterProvider, get_meter_provider +from opentelemetry.sdk._logs._internal._exceptions import ( + _copy_log_record_with_exception, + _create_log_record_with_exception, + _set_log_record_exception_attributes, +) +from opentelemetry.sdk._logs._internal._logger_metrics import ( + LoggerMetricsT, + create_logger_metrics, +) +from opentelemetry.sdk.environment_variables import ( + OTEL_ATTRIBUTE_COUNT_LIMIT, + OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, + OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT, + OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT, + OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, + OTEL_SDK_DISABLED, +) +from opentelemetry.sdk.environment_variables._internal import ( + parse_boolean_environment_variable, +) +from opentelemetry.sdk.resources import ( + Resource, + _get_process_dependent_resource, +) +from opentelemetry.sdk.util import ns_to_iso_str +from opentelemetry.sdk.util._configurator import RuleBasedConfigurator +from opentelemetry.sdk.util.instrumentation import ( + InstrumentationScope, +) +from opentelemetry.semconv.attributes import ( + code_attributes, + exception_attributes, +) +from opentelemetry.trace import ( + format_span_id, + format_trace_id, +) +from opentelemetry.util.types import AnyValue, Attributes + +# pylint: disable=too-many-lines + +_DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT = 128 +_ENV_VALUE_UNSET = "" + +_logger = logging.getLogger(__name__) + + +class BytesEncoder(json.JSONEncoder): + def default(self, o): + if isinstance(o, bytes): + return base64.b64encode(o).decode() + return super().default(o) + + +class LogRecordDroppedAttributesWarning(UserWarning): + """Custom warning to indicate dropped log attributes due to limits. + + This class is used to filter and handle these specific warnings separately + from other warnings, ensuring that they are only shown once without + interfering with default user warnings. + """ + + +warnings.simplefilter("once", LogRecordDroppedAttributesWarning) + + +@deprecated( + "Use LogRecordDroppedAttributesWarning. Since logs are not stable yet this WILL be removed in future releases." +) +class LogDroppedAttributesWarning(LogRecordDroppedAttributesWarning): + pass + + +class LogRecordLimits: + """This class is based on a SpanLimits class in the Tracing module. + + This class represents the limits that should be enforced on recorded data such as events, links, attributes etc. + + This class does not enforce any limits itself. It only provides a way to read limits from env, + default values and from user provided arguments. + + All limit arguments must be either a non-negative integer, ``None`` or ``LogRecordLimits.UNSET``. + + - All limit arguments are optional. + - If a limit argument is not set, the class will try to read its value from the corresponding + environment variable. + - If the environment variable is not set, the default value, if any, will be used. + + Limit precedence: + + - If a model specific limit is set, it will be used. + - Else if the corresponding global limit is set, it will be used. + - Else if the model specific limit has a default value, the default value will be used. + - Else if the global limit has a default value, the default value will be used. + + Args: + max_attributes: Maximum number of attributes that can be added to a log record (global fallback). + Environment variable: ``OTEL_ATTRIBUTE_COUNT_LIMIT`` + Default: {_DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT} + max_attribute_length: Maximum length an attribute value can have (global fallback). Values + longer than the specified length will be truncated. + max_log_record_attributes: Maximum number of attributes that can be added to a log record. + Environment variable: ``OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT`` + Falls back to ``max_attributes`` when unset. + max_log_record_attribute_length: Maximum length a log record attribute value can have. + Environment variable: ``OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT`` + Falls back to ``max_attribute_length`` when unset. + """ + + UNSET = -1 + + def __init__( + self, + max_attributes: int | None = None, + max_attribute_length: int | None = None, + max_log_record_attributes: int | None = None, + max_log_record_attribute_length: int | None = None, + ): + # global attribute count + global_max_attributes = self._from_env_if_absent(max_attributes, OTEL_ATTRIBUTE_COUNT_LIMIT) + self.max_attributes = ( + global_max_attributes if global_max_attributes is not None else _DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT + ) + + # global attribute length + self.max_attribute_length = self._from_env_if_absent( + max_attribute_length, + OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, + ) + + self.max_log_record_attributes = self._from_env_if_absent( + max_log_record_attributes, + OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT, + default=self.max_attributes, + ) + + self.max_log_record_attribute_length = self._from_env_if_absent( + max_log_record_attribute_length, + OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT, + default=self.max_attribute_length, + ) + + def __repr__(self): + return ( + f"{type(self).__name__}(" + f"max_attributes={self.max_attributes}, " + f"max_attribute_length={self.max_attribute_length}, " + f"max_log_record_attributes={self.max_log_record_attributes}, " + f"max_log_record_attribute_length={self.max_log_record_attribute_length})" + ) + + @classmethod + def _from_env_if_absent(cls, value: int | None, env_var: str, default: int | None = None) -> int | None: + if value == cls.UNSET: + return None + + err_msg = "{} must be a non-negative integer but got {}" + + # if no value is provided for the limit, try to load it from env + if value is None: + # return default value if env var is not set + if env_var not in environ: + return default + + str_value = environ.get(env_var, "").strip().lower() + if str_value == _ENV_VALUE_UNSET: + return None + + try: + value = int(str_value) + except ValueError: + raise ValueError(err_msg.format(env_var, str_value)) + + if value < 0: + raise ValueError(err_msg.format(env_var, value)) + return value + + +@deprecated("Use LogRecordLimits. Since logs are not stable yet this WILL be removed in future releases.") +class LogLimits(LogRecordLimits): + pass + + +@dataclass(frozen=True) +class ReadableLogRecord: + """Readable LogRecord should be kept exactly in-sync with ReadWriteLogRecord, only difference is the frozen=True param.""" + + log_record: LogRecord + resource: Resource + instrumentation_scope: InstrumentationScope | None = None + limits: LogRecordLimits | None = None + + @property + def dropped_attributes(self) -> int: + if isinstance(self.log_record.attributes, BoundedAttributes): + return self.log_record.attributes.dropped + return 0 + + def to_json(self, indent: int | None = 4) -> str: + return json.dumps( + { + "body": self.log_record.body, + "severity_number": self.log_record.severity_number.value + if self.log_record.severity_number is not None + else None, + "severity_text": self.log_record.severity_text, + "attributes": (dict(self.log_record.attributes) if bool(self.log_record.attributes) else None), + "dropped_attributes": self.dropped_attributes, + "timestamp": ns_to_iso_str(self.log_record.timestamp) + if self.log_record.timestamp is not None + else None, + "observed_timestamp": ns_to_iso_str(self.log_record.observed_timestamp), + "trace_id": ( + f"0x{format_trace_id(self.log_record.trace_id)}" if self.log_record.trace_id is not None else "" + ), + "span_id": ( + f"0x{format_span_id(self.log_record.span_id)}" if self.log_record.span_id is not None else "" + ), + "trace_flags": self.log_record.trace_flags, + "resource": json.loads(self.resource.to_json()), + "event_name": self.log_record.event_name if self.log_record.event_name else "", + }, + indent=indent, + cls=BytesEncoder, + ) + + +@dataclass +class ReadWriteLogRecord: + """A ReadWriteLogRecord instance represents an event being logged. + ReadWriteLogRecord instances are created and emitted via `Logger` + every time something is logged. They contain all the information + pertinent to the event being logged. + """ + + log_record: LogRecord + resource: Resource | None = Resource.create({}) + instrumentation_scope: InstrumentationScope | None = None + limits: LogRecordLimits = field(default_factory=LogRecordLimits) + + def __post_init__(self): + self.log_record.attributes = BoundedAttributes( + maxlen=self.limits.max_log_record_attributes, + attributes=self.log_record.attributes if self.log_record.attributes else None, + immutable=False, + max_value_len=self.limits.max_log_record_attribute_length, + extended_attributes=True, + ) + if self.dropped_attributes > 0: + warnings.warn( + "Log record attributes were dropped due to limits", + LogRecordDroppedAttributesWarning, + stacklevel=2, + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ReadWriteLogRecord): + return NotImplemented + return self.__dict__ == other.__dict__ + + @property + def dropped_attributes(self) -> int: + if isinstance(self.log_record.attributes, BoundedAttributes): + return self.log_record.attributes.dropped + return 0 + + @classmethod + def _from_api_log_record( + cls, + *, + record: LogRecord, + resource: Resource, + instrumentation_scope: InstrumentationScope | None = None, + limits: LogRecordLimits | None = None, + ) -> ReadWriteLogRecord: + return cls( + log_record=record, + resource=resource, + instrumentation_scope=instrumentation_scope, + **({} if limits is None else {"limits": limits}), + ) + + +class LogRecordProcessor(abc.ABC): + """Interface to hook the log record emitting action. + + Log processors can be registered directly using + :func:`LoggerProvider.add_log_record_processor` and they are invoked + in the same order as they were registered. + + Implementers of custom log processors should be aware of the following: + + Error Handling + -------------- + According to the OpenTelemetry error handling principles, the SDK should + not throw unhandled exceptions at runtime. When implementing a custom + ``LogRecordProcessor``, it is the **processor's responsibility** to handle + any exceptions that may be raised by the exporter's ``export()`` method. + + The ``LogRecordExporter.export()`` method may raise exceptions (e.g., + network errors, timeouts). If these exceptions are not caught, they will + propagate up and potentially crash the application. + + Custom processor implementations should wrap exporter calls in a + try/except block. See ``SimpleLogRecordProcessor`` for a reference + implementation:: + + def on_emit(self, log_record: ReadWriteLogRecord): + try: + self._exporter.export((log_record,)) + except Exception: # pylint: disable=broad-exception-caught + logger.exception("Exception while exporting logs.") + + The ``BatchLogRecordProcessor`` handles this implicitly since export + operations occur in a background thread where exceptions cannot bubble + up to the caller. + """ + + @abc.abstractmethod + def on_emit(self, log_record: ReadWriteLogRecord) -> None: + """Emits the ``ReadWriteLogRecord``. + + Implementers should handle any exceptions raised during log processing + to prevent application crashes. See the class docstring for details + on error handling expectations. + """ + + def enabled( # pylint: disable=no-self-use,unused-argument + self, + *, + context: Context | None = None, + instrumentation_scope: InstrumentationScope | None = None, + severity_number: SeverityNumber | None = None, + event_name: str | None = None, + ) -> bool: + """Returns whether this processor would emit a record for the given arguments. + + Processors that support filtering MAY override this method. The default + implementation returns ``True``. + """ + return True + + @abc.abstractmethod + def shutdown(self) -> None: + """Called when a :class:`opentelemetry.sdk._logs.Logger` is shutdown""" + + @abc.abstractmethod + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Export all the received logs to the configured Exporter that have not yet + been exported. + + Args: + timeout_millis: The maximum amount of time to wait for logs to be + exported. + + Returns: + False if the timeout is exceeded, True otherwise. + """ + + +# Temporary fix until https://github.com/PyCQA/pylint/issues/4098 is resolved +# pylint:disable=no-member +class SynchronousMultiLogRecordProcessor(LogRecordProcessor): + """Implementation of class:`LogRecordProcessor` that forwards all received + events to a list of log processors sequentially. + + The underlying log processors are called in sequential order as they were + added. + """ + + def __init__(self): + # use a tuple to avoid race conditions when adding a new log and + # iterating through it on "emit". + self._log_record_processors = () # type: tuple[LogRecordProcessor, ...] + self._lock = threading.Lock() + + def add_log_record_processor(self, log_record_processor: LogRecordProcessor) -> None: + """Adds a Logprocessor to the list of log processors handled by this instance""" + with self._lock: + self._log_record_processors += (log_record_processor,) + + def on_emit(self, log_record: ReadWriteLogRecord) -> None: + for lp in self._log_record_processors: + lp.on_emit(log_record) + + def enabled( + self, + *, + context: Context | None = None, + instrumentation_scope: InstrumentationScope | None = None, + severity_number: SeverityNumber | None = None, + event_name: str | None = None, + ) -> bool: + processors = self._log_record_processors + return any( + lp.enabled( + context=context, + instrumentation_scope=instrumentation_scope, + severity_number=severity_number, + event_name=event_name, + ) + for lp in processors + ) + + def shutdown(self) -> None: + """Shutdown the log processors one by one""" + for lp in self._log_record_processors: + lp.shutdown() + + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Force flush the log processors one by one + + Args: + timeout_millis: The maximum amount of time to wait for logs to be + exported. If the first n log processors exceeded the timeout + then remaining log processors will not be flushed. + + Returns: + True if all the log processors flushes the logs within timeout, + False otherwise. + """ + deadline_ns = time_ns() + timeout_millis * 1000000 + all_flushed = True + for lp in self._log_record_processors: + current_ts = time_ns() + if current_ts >= deadline_ns: + return False + + if lp.force_flush((deadline_ns - current_ts) // 1000000) is False: + all_flushed = False + + return all_flushed + + +class ConcurrentMultiLogRecordProcessor(LogRecordProcessor): + """Implementation of :class:`LogRecordProcessor` that forwards all received + events to a list of log processors in parallel. + + Calls to the underlying log processors are forwarded in parallel by + submitting them to a thread pool executor and waiting until each log + processor finished its work. + + Args: + max_workers: The number of threads managed by the thread pool executor + and thus defining how many log processors can work in parallel. + """ + + def __init__(self, max_workers: int = 2): + # use a tuple to avoid race conditions when adding a new log and + # iterating through it on "emit". + self._log_record_processors = () # type: tuple[LogRecordProcessor, ...] + self._lock = threading.Lock() + self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) + + def add_log_record_processor(self, log_record_processor: LogRecordProcessor): + with self._lock: + self._log_record_processors += (log_record_processor,) + + def _submit_and_wait( + self, + func: Callable[[LogRecordProcessor], Callable[..., None]], + *args: Any, + **kwargs: Any, + ): + futures = [] + for lp in self._log_record_processors: + future = self._executor.submit(func(lp), *args, **kwargs) + futures.append(future) + for future in futures: + future.result() + + def on_emit(self, log_record: ReadWriteLogRecord) -> None: + self._submit_and_wait(lambda lp: lp.on_emit, log_record) + + def enabled( + self, + *, + context: Context | None = None, + instrumentation_scope: InstrumentationScope | None = None, + severity_number: SeverityNumber | None = None, + event_name: str | None = None, + ) -> bool: + processors = self._log_record_processors + return any( + lp.enabled( + context=context, + instrumentation_scope=instrumentation_scope, + severity_number=severity_number, + event_name=event_name, + ) + for lp in processors + ) + + def shutdown(self) -> None: + self._submit_and_wait(lambda lp: lp.shutdown) + + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Force flush the log processors in parallel. + + Args: + timeout_millis: The maximum amount of time to wait for logs to be + exported. + + Returns: + True if all the log processors flushes the logs within timeout, + False otherwise. + """ + futures = [] + for lp in self._log_record_processors: + future = self._executor.submit(lp.force_flush, timeout_millis) + futures.append(future) + + done_futures, not_done_futures = concurrent.futures.wait(futures, timeout_millis / 1e3) + + if not_done_futures: + return False + + for future in done_futures: + if future.result() is False: + return False + + return True + + +# skip natural LogRecord attributes +# http://docs.python.org/library/logging.html#logrecord-attributes +_RESERVED_ATTRS = frozenset( + ( + "asctime", + "args", + "created", + "exc_info", + "exc_text", + "filename", + "funcName", + "getMessage", + "message", + "levelname", + "levelno", + "lineno", + "module", + "msecs", + "msg", + "name", + "pathname", + "process", + "processName", + "relativeCreated", + "stack_info", + "thread", + "threadName", + "taskName", + ) +) + + +class LoggingHandler(logging.Handler): + """A handler class which writes logging records, in OTLP format, to + a network destination or file. Supports signals from the `logging` module. + https://docs.python.org/3/library/logging.html + """ + + def __init__( + self, + level: int = logging.NOTSET, + logger_provider: APILoggerProvider | None = None, + ) -> None: + super().__init__(level=level) + self._logger_provider = logger_provider or get_logger_provider() + + warnings.warn( + "`LoggingHandler` in `opentelemetry-sdk` is deprecated. Use the " + "handler from `opentelemetry-instrumentation-logging` instead.", + DeprecationWarning, + ) + + @staticmethod + def _get_attributes(record: logging.LogRecord) -> Attributes: + attributes = {k: v for k, v in vars(record).items() if k not in _RESERVED_ATTRS} + + # Add standard code attributes for logs. + attributes[code_attributes.CODE_FILE_PATH] = record.pathname + attributes[code_attributes.CODE_FUNCTION_NAME] = record.funcName + attributes[code_attributes.CODE_LINE_NUMBER] = record.lineno + + if record.exc_info: + exctype, value, tb = record.exc_info + if exctype is not None: + attributes[exception_attributes.EXCEPTION_TYPE] = exctype.__name__ + if value is not None and value.args: + attributes[exception_attributes.EXCEPTION_MESSAGE] = str(value.args[0]) + if tb is not None: + # https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-spans/#stacktrace-representation + attributes[exception_attributes.EXCEPTION_STACKTRACE] = "".join( + traceback.format_exception(*record.exc_info) + ) + return attributes + + def _translate(self, record: logging.LogRecord) -> LogRecord: + timestamp = int(record.created * 1e9) + observered_timestamp = time_ns() + attributes = self._get_attributes(record) + severity_number = std_to_otel(record.levelno) + if self.formatter: + body = self.format(record) + else: + # `record.getMessage()` uses `record.msg` as a template to format + # `record.args` into. There is a special case in `record.getMessage()` + # where it will only attempt formatting if args are provided, + # otherwise, it just stringifies `record.msg`. + # + # Since the OTLP body field has a type of 'any' and the logging module + # is sometimes used in such a way that objects incorrectly end up + # set as record.msg, in those cases we would like to bypass + # `record.getMessage()` completely and set the body to the object + # itself instead of its string representation. + # For more background, see: https://github.com/open-telemetry/opentelemetry-python/pull/4216 + if not record.args and not isinstance(record.msg, str): + # if record.msg is not a value we can export, cast it to string + # TODO: https://github.com/open-telemetry/opentelemetry-python/issues/5304 - do something better + # than just casting to a string. + if not isinstance( + record.msg, + ( + NoneType, + bool, + bytes, + int, + float, + str, + Sequence, + Mapping, + ), + ): + body = str(record.msg) + else: + body = record.msg + else: + body = record.getMessage() + + # Map Python log level names to OTel severity text as defined in + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#displaying-severity + # Python "WARNING" -> OTel "WARN" (see #3548) + # Python "CRITICAL" -> OTel "FATAL" (see #4984) + _python_to_otel_severity_text = { + "WARNING": "WARN", + "CRITICAL": "FATAL", + } + level_name = _python_to_otel_severity_text.get(record.levelname, record.levelname) + + return LogRecord( + timestamp=timestamp, + observed_timestamp=observered_timestamp, + context=get_current() or None, + severity_text=level_name, + severity_number=severity_number, + body=body, + attributes=attributes, + ) + + def emit(self, record: logging.LogRecord) -> None: + """ + Emit a record. Skip emitting if logger is NoOp. + + The record is translated to OTel format, and then sent across the pipeline. + """ + logger = get_logger(record.name, logger_provider=self._logger_provider) + if not isinstance(logger, NoOpLogger): + logger.emit(self._translate(record)) + + def flush(self) -> None: + """ + Flushes the logging output. Skip flushing if logging_provider has no force_flush method. + """ + if hasattr(self._logger_provider, "force_flush") and callable( + self._logger_provider.force_flush # type: ignore[reportAttributeAccessIssue] + ): + # This is done in a separate thread to avoid a potential deadlock, for + # details see https://github.com/open-telemetry/opentelemetry-python/pull/4636. + thread = threading.Thread(target=self._logger_provider.force_flush) # type: ignore[reportAttributeAccessIssue] + thread.start() + + +@dataclass +class _LoggerConfig: + is_enabled: bool = True + + @classmethod + def default(cls) -> _LoggerConfig: + return _LoggerConfig() + + +class Logger(APILogger): + def __init__( + self, + resource: Resource, + multi_log_record_processor: SynchronousMultiLogRecordProcessor | ConcurrentMultiLogRecordProcessor, + instrumentation_scope: InstrumentationScope, + *, + logger_metrics: LoggerMetricsT, + log_record_limits: LogRecordLimits | None = None, + _logger_config: _LoggerConfig, + ): + super().__init__( + instrumentation_scope.name, + instrumentation_scope.version, + instrumentation_scope.schema_url, + instrumentation_scope.attributes, + ) + self._resource = resource + self._multi_log_record_processor = multi_log_record_processor + self._instrumentation_scope = instrumentation_scope + self._logger_metrics = logger_metrics + self._logger_config = _logger_config + self._log_record_limits = log_record_limits or LogRecordLimits() + + def _is_enabled(self) -> bool: + return self._logger_config.is_enabled + + def enabled( + self, + *, + context: Context | None = None, + severity_number: SeverityNumber | None = None, + event_name: str | None = None, + ) -> bool: + if not self._is_enabled(): + return False + return self._multi_log_record_processor.enabled( + context=context, + instrumentation_scope=self._instrumentation_scope, + severity_number=severity_number, + event_name=event_name, + ) + + def _set_logger_config(self, logger_config: _LoggerConfig) -> None: + self._logger_config = logger_config + + def _set_resource(self, resource: Resource) -> None: + self._resource = resource + + @property + def instrumentation_scope(self): + return self._instrumentation_scope + + @property + def resource(self): + return self._resource + + # pylint: disable=arguments-differ + def emit( + self, + record: LogRecord | None = None, + *, + timestamp: int | None = None, + observed_timestamp: int | None = None, + context: Context | None = None, + severity_number: SeverityNumber | None = None, + severity_text: str | None = None, + body: AnyValue | None = None, + attributes: Attributes = None, + event_name: str | None = None, + exception: BaseException | None = None, + ) -> None: + """Emits the :class:`ReadWriteLogRecord` by setting instrumentation scope + and forwarding to the processor. + """ + if not self._is_enabled(): + return + # If a record is provided, use it directly + if record is not None: + if not isinstance(record, ReadWriteLogRecord): + if record.exception is not None: + record = _copy_log_record_with_exception(record) + # pylint:disable=protected-access + writable_record = ReadWriteLogRecord._from_api_log_record( + record=record, + resource=self._resource, + instrumentation_scope=self._instrumentation_scope, + limits=self._log_record_limits, + ) + else: + _set_log_record_exception_attributes(record.log_record) + writable_record = record + else: + # Create a record from individual parameters + log_record = _create_log_record_with_exception( + timestamp=timestamp, + observed_timestamp=observed_timestamp, + context=context, + severity_number=severity_number, + severity_text=severity_text, + body=body, + attributes=attributes, + event_name=event_name, + exception=exception, + ) + # pylint:disable=protected-access + writable_record = ReadWriteLogRecord._from_api_log_record( + record=log_record, + resource=self._resource, + instrumentation_scope=self._instrumentation_scope, + limits=self._log_record_limits, + ) + + self._logger_metrics.emit_log() + self._multi_log_record_processor.on_emit(writable_record) + + +_LoggerConfiguratorT = Callable[[InstrumentationScope], _LoggerConfig] +_RuleBasedLoggerConfigurator = RuleBasedConfigurator[_LoggerConfig] + + +def _default_logger_configurator( + _logger_scope: InstrumentationScope, +) -> _LoggerConfig: + return _LoggerConfig.default() + + +def _disable_logger_configurator( + _logger_scope: InstrumentationScope, +) -> _LoggerConfig: + return _LoggerConfig(is_enabled=False) + + +class LoggerProvider(APILoggerProvider): + def __init__( + self, + resource: Resource | None = None, + shutdown_on_exit: bool = True, + multi_log_record_processor: SynchronousMultiLogRecordProcessor + | ConcurrentMultiLogRecordProcessor + | None = None, + *, + meter_provider: MeterProvider | None = None, + log_record_limits: LogRecordLimits | None = None, + _logger_configurator: _LoggerConfiguratorT | None = None, + ): + if resource is None: + self._resource = Resource.create({}) + else: + self._resource = resource + self._multi_log_record_processor = multi_log_record_processor or SynchronousMultiLogRecordProcessor() + self._logger_metrics = create_logger_metrics( + meter_provider or get_meter_provider(), + parse_boolean_environment_variable(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED), + ) + disabled = environ.get(OTEL_SDK_DISABLED, "") + self._disabled = disabled.lower().strip() == "true" + self._logger_configurator = _logger_configurator or _default_logger_configurator + self._log_record_limits = log_record_limits or LogRecordLimits() + self._at_exit_handler = None + if shutdown_on_exit: + self._at_exit_handler = atexit.register(self.shutdown) + self._logger_cache = {} + self._logger_cache_lock = Lock() + self._active_loggers: WeakSet[Logger] = WeakSet() + self._active_loggers_lock = Lock() + if hasattr(os, "register_at_fork"): + weak_at_fork = WeakMethod(self._handle_fork) + + def _after_in_child() -> None: + if at_fork := weak_at_fork(): + at_fork() + + os.register_at_fork(after_in_child=_after_in_child) + + def _handle_fork(self) -> None: + self._logger_cache_lock = Lock() + self._active_loggers_lock = Lock() + self._update_resource(_get_process_dependent_resource()) + + @property + def resource(self): + return self._resource + + def _update_resource(self, resource: Resource) -> None: + with self._active_loggers_lock: + self._resource = self._resource.merge(resource) + for logger in list(self._active_loggers): + # pylint: disable-next=protected-access + logger._set_resource(self._resource) + + def _get_logger_no_cache( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + attributes: Attributes = None, + ) -> Logger: + scope = InstrumentationScope(name, version, schema_url, attributes) + + return Logger( + self._resource, + self._multi_log_record_processor, + scope, + logger_metrics=self._logger_metrics, + _logger_config=self._apply_logger_configurator(scope), + log_record_limits=self._log_record_limits, + ) + + def _get_logger_cached( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + ) -> Logger: + with self._logger_cache_lock: + key = (name, version, schema_url) + if key in self._logger_cache: + return self._logger_cache[key] + + self._logger_cache[key] = self._get_logger_no_cache(name, version, schema_url) + return self._logger_cache[key] + + def get_logger( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + attributes: Attributes = None, + ) -> APILogger: + if self._disabled: + return NoOpLogger( + name, + version=version, + schema_url=schema_url, + attributes=attributes, + ) + logger = ( + self._get_logger_cached(name, version, schema_url) + if attributes is None + else self._get_logger_no_cache(name, version, schema_url, attributes) + ) + with self._active_loggers_lock: + self._active_loggers.add(logger) + return logger + + def add_log_record_processor(self, log_record_processor: LogRecordProcessor): + """Registers a new :class:`LogRecordProcessor` for this `LoggerProvider` instance. + + The log processors are invoked in the same order they are registered. + """ + self._multi_log_record_processor.add_log_record_processor(log_record_processor) + + def _set_logger_configurator(self, *, logger_configurator: _LoggerConfiguratorT): + """Set a new LoggerConfigurator for this LoggerProvider. + + Setting a new LoggerConfigurator will result in the configurator being called + for each outstanding Logger and for any newly created loggers thereafter. + Therefore, it is important that the provided function returns quickly. + """ + self._logger_configurator = logger_configurator + with self._active_loggers_lock: + for logger in list(self._active_loggers): + # pylint: disable-next=protected-access + logger._set_logger_config(self._apply_logger_configurator(logger.instrumentation_scope)) + + def _apply_logger_configurator(self, instrumentation_scope: InstrumentationScope) -> _LoggerConfig: + try: + return self._logger_configurator(instrumentation_scope) + # pylint: disable-next=broad-exception-caught + except Exception: + _logger.exception( + "logger configurator failed for scope '%s', using default config", + instrumentation_scope.name, + ) + return _LoggerConfig.default() + + def shutdown(self) -> None: + """Shuts down the log processors.""" + self._multi_log_record_processor.shutdown() + if self._at_exit_handler is not None: + atexit.unregister(self._at_exit_handler) + self._at_exit_handler = None + + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Force flush the log processors. + + Args: + timeout_millis: The maximum amount of time to wait for logs to be + exported. + + Returns: + True if all the log processors flushes the logs within timeout, + False otherwise. + """ + return self._multi_log_record_processor.force_flush(timeout_millis) + + +_STD_TO_OTEL = { + 10: SeverityNumber.DEBUG, + 11: SeverityNumber.DEBUG2, + 12: SeverityNumber.DEBUG3, + 13: SeverityNumber.DEBUG4, + 14: SeverityNumber.DEBUG4, + 15: SeverityNumber.DEBUG4, + 16: SeverityNumber.DEBUG4, + 17: SeverityNumber.DEBUG4, + 18: SeverityNumber.DEBUG4, + 19: SeverityNumber.DEBUG4, + 20: SeverityNumber.INFO, + 21: SeverityNumber.INFO2, + 22: SeverityNumber.INFO3, + 23: SeverityNumber.INFO4, + 24: SeverityNumber.INFO4, + 25: SeverityNumber.INFO4, + 26: SeverityNumber.INFO4, + 27: SeverityNumber.INFO4, + 28: SeverityNumber.INFO4, + 29: SeverityNumber.INFO4, + 30: SeverityNumber.WARN, + 31: SeverityNumber.WARN2, + 32: SeverityNumber.WARN3, + 33: SeverityNumber.WARN4, + 34: SeverityNumber.WARN4, + 35: SeverityNumber.WARN4, + 36: SeverityNumber.WARN4, + 37: SeverityNumber.WARN4, + 38: SeverityNumber.WARN4, + 39: SeverityNumber.WARN4, + 40: SeverityNumber.ERROR, + 41: SeverityNumber.ERROR2, + 42: SeverityNumber.ERROR3, + 43: SeverityNumber.ERROR4, + 44: SeverityNumber.ERROR4, + 45: SeverityNumber.ERROR4, + 46: SeverityNumber.ERROR4, + 47: SeverityNumber.ERROR4, + 48: SeverityNumber.ERROR4, + 49: SeverityNumber.ERROR4, + 50: SeverityNumber.FATAL, + 51: SeverityNumber.FATAL2, + 52: SeverityNumber.FATAL3, + 53: SeverityNumber.FATAL4, +} + + +def std_to_otel(levelno: int) -> SeverityNumber: + """ + Map python log levelno as defined in https://docs.python.org/3/library/logging.html#logging-levels + to OTel log severity number as defined here: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#field-severitynumber + """ + if levelno < 10: + return SeverityNumber.UNSPECIFIED + if levelno > 53: + return SeverityNumber.FATAL4 + return _STD_TO_OTEL[levelno] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..9d2aef1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/__pycache__/_exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/__pycache__/_exceptions.cpython-312.pyc new file mode 100644 index 0000000..a61e5c8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/__pycache__/_exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/__pycache__/_logger_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/__pycache__/_logger_metrics.cpython-312.pyc new file mode 100644 index 0000000..b3e8a58 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/__pycache__/_logger_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/_exceptions.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/_exceptions.py new file mode 100644 index 0000000..bd4a953 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/_exceptions.py @@ -0,0 +1,114 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import traceback + +from opentelemetry._logs import LogRecord +from opentelemetry.attributes import BoundedAttributes +from opentelemetry.semconv.attributes import exception_attributes +from opentelemetry.util.types import AnyValue, Attributes + + +def _get_exception_attributes( + exception: BaseException, +) -> dict[str, AnyValue]: + stacktrace = "".join(traceback.format_exception(type(exception), value=exception, tb=exception.__traceback__)) + module = type(exception).__module__ + qualname = type(exception).__qualname__ + exception_type = f"{module}.{qualname}" if module and module != "builtins" else qualname + return { + exception_attributes.EXCEPTION_TYPE: exception_type, + exception_attributes.EXCEPTION_MESSAGE: str(exception), + exception_attributes.EXCEPTION_STACKTRACE: stacktrace, + } + + +def _get_attributes_with_exception( + attributes: Attributes, + exception: BaseException | None, +) -> Attributes: + if exception is None: + return attributes + + exception_attributes_map = _get_exception_attributes(exception) + if attributes is None: + attributes_map: Attributes = {} + else: + attributes_map = attributes + + if isinstance(attributes_map, BoundedAttributes): + bounded_attributes = attributes_map + merged = BoundedAttributes( + maxlen=bounded_attributes.maxlen, + attributes=bounded_attributes, + immutable=False, + max_value_len=bounded_attributes.max_value_len, + ) + merged.dropped = bounded_attributes.dropped + for key, value in exception_attributes_map.items(): + if key not in merged: + merged[key] = value + return merged + + return exception_attributes_map | dict(attributes_map.items()) + + +def _copy_log_record( + record: LogRecord, + attributes: Attributes, +) -> LogRecord: + copied_record = LogRecord( + timestamp=record.timestamp, + observed_timestamp=record.observed_timestamp, + context=record.context, + severity_text=record.severity_text, + severity_number=record.severity_number, + body=record.body, + attributes=attributes, + event_name=record.event_name, + exception=getattr(record, "exception", None), + ) + copied_record.trace_id = record.trace_id + copied_record.span_id = record.span_id + copied_record.trace_flags = record.trace_flags + return copied_record + + +def _copy_log_record_with_exception(record: LogRecord) -> LogRecord: + return _copy_log_record( + record, + _get_attributes_with_exception(record.attributes, record.exception), + ) + + +def _set_log_record_exception_attributes(record: LogRecord) -> None: + record.attributes = _get_attributes_with_exception( + record.attributes, + record.exception, + ) + + +def _create_log_record_with_exception( + *, + timestamp: int | None = None, + observed_timestamp: int | None = None, + context=None, + severity_number=None, + severity_text: str | None = None, + body: AnyValue | None = None, + attributes: Attributes = None, + event_name: str | None = None, + exception: BaseException | None = None, +) -> LogRecord: + return LogRecord( + timestamp=timestamp, + observed_timestamp=observed_timestamp, + context=context, + severity_number=severity_number, + severity_text=severity_text, + body=body, + attributes=_get_attributes_with_exception(attributes, exception), + event_name=event_name, + exception=exception, + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/_logger_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/_logger_metrics.py new file mode 100644 index 0000000..1b068c5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/_logger_metrics.py @@ -0,0 +1,37 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Protocol + +from opentelemetry import metrics as metrics_api +from opentelemetry.semconv._incubating.metrics.otel_metrics import ( + create_otel_sdk_log_created, +) + + +class LoggerMetricsT(Protocol): + def emit_log(self) -> None: ... + + +class NoOpLoggerMetrics: + def emit_log(self) -> None: + pass + + +class LoggerMetrics: + def __init__(self, meter_provider: metrics_api.MeterProvider) -> None: + meter = meter_provider.get_meter("opentelemetry-sdk") + self._created_logs = create_otel_sdk_log_created(meter) + + def emit_log(self) -> None: + self._created_logs.add(1) + + +def create_logger_metrics( + meter_provider: metrics_api.MeterProvider, + enabled: bool, +) -> LoggerMetricsT: + if not enabled: + return NoOpLoggerMetrics() + + return LoggerMetrics(meter_provider) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/export/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/export/__init__.py new file mode 100644 index 0000000..d6ff771 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/export/__init__.py @@ -0,0 +1,392 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import abc +import copy +import enum +import logging +import sys +from collections.abc import Callable, Sequence +from os import environ, linesep +from typing import IO + +from typing_extensions import deprecated + +from opentelemetry.context import ( + _ON_EMIT_RECURSION_COUNT_KEY, + _SUPPRESS_INSTRUMENTATION_KEY, + Context, + attach, + detach, + get_value, + set_value, +) +from opentelemetry.metrics import MeterProvider, get_meter_provider +from opentelemetry.sdk._logs import ( + LogRecordProcessor, + ReadableLogRecord, + ReadWriteLogRecord, +) +from opentelemetry.sdk._shared_internal import ( + BatchProcessor, + DuplicateFilter, +) +from opentelemetry.sdk._shared_internal._processor_metrics import ( + create_processor_metrics, +) +from opentelemetry.sdk.environment_variables import ( + OTEL_BLRP_EXPORT_TIMEOUT, + OTEL_BLRP_MAX_EXPORT_BATCH_SIZE, + OTEL_BLRP_MAX_QUEUE_SIZE, + OTEL_BLRP_SCHEDULE_DELAY, + OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, +) +from opentelemetry.sdk.environment_variables._internal import ( + parse_boolean_environment_variable, +) +from opentelemetry.sdk.resources import Resource +from opentelemetry.semconv._incubating.attributes.otel_attributes import ( + OtelComponentTypeValues, +) + +_DEFAULT_SCHEDULE_DELAY_MILLIS = 1000 +_DEFAULT_MAX_EXPORT_BATCH_SIZE = 512 +_DEFAULT_EXPORT_TIMEOUT_MILLIS = 30000 +_DEFAULT_MAX_QUEUE_SIZE = 2048 +_ENV_VAR_INT_VALUE_ERROR_MESSAGE = "Unable to parse value for %s as integer. Defaulting to %s." +_logger = logging.getLogger(__name__) +_logger.addFilter(DuplicateFilter()) + +_propagate_false_logger = logging.getLogger(__name__ + ".propagate.false") +_propagate_false_logger.propagate = False + + +class LogRecordExportResult(enum.Enum): + SUCCESS = 0 + FAILURE = 1 + + +@deprecated("Use LogRecordExportResult. Since logs are not stable yet this WILL be removed in future releases.") +class LogExportResult(enum.Enum): + SUCCESS = 0 + FAILURE = 1 + + +class LogRecordExporter(abc.ABC): + """Interface for exporting logs. + + Interface to be implemented by services that want to export logs received + in their own format. + + To export data this MUST be registered to the :class:`opentelemetry.sdk._logs.Logger` + using a log processor. + + Important + --------- + The ``export()`` method may raise exceptions (e.g., network errors, + timeouts, serialization errors). It is the responsibility of the + ``LogRecordProcessor`` calling this exporter to handle these exceptions + appropriately to prevent application crashes. See ``LogRecordProcessor`` + for guidance on implementing proper error handling. + """ + + @abc.abstractmethod + def export(self, batch: Sequence[ReadableLogRecord]) -> LogRecordExportResult: + """Exports a batch of logs. + + Args: + batch: The list of ``ReadableLogRecord`` objects to be exported. + + Returns: + The result of the export. + + Raises: + Exception: This method may raise exceptions on network errors, + timeouts, or other failures. Callers (i.e., log processors) + should handle these exceptions to comply with OpenTelemetry + error handling principles. + """ + + @abc.abstractmethod + def shutdown(self): + """Shuts down the exporter. + + Called when the SDK is shut down. + """ + + @abc.abstractmethod + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Hint to ensure that the export of any ``ReadableLogRecord`` objects + the exporter has received prior to the call to ``force_flush`` SHOULD be + completed as soon as possible, preferably before returning from this method. + + Args: + timeout_millis: The maximum amount of time to wait for the flush to + complete, in milliseconds. + + Returns: + ``True`` if the flush completed successfully within the timeout, + ``False`` otherwise. + """ + + +@deprecated("Use LogRecordExporter. Since logs are not stable yet this WILL be removed in future releases.") +class LogExporter(LogRecordExporter): + pass + + +class ConsoleLogRecordExporter(LogRecordExporter): + """Implementation of :class:`LogRecordExporter` that prints log records to the + console. + + This class can be used for diagnostic purposes. It prints the exported + log records to the console STDOUT. + """ + + def __init__( + self, + out: IO = sys.stdout, + formatter: Callable[[ReadableLogRecord], str] = lambda record: record.to_json() + linesep, + ): + self.out = out + self.formatter = formatter + + def export(self, batch: Sequence[ReadableLogRecord]): + for log_record in batch: + self.out.write(self.formatter(log_record)) + self.out.flush() + return LogRecordExportResult.SUCCESS + + def shutdown(self): + pass + + def force_flush(self, timeout_millis: int = 30000) -> bool: + return True + + +@deprecated("Use ConsoleLogRecordExporter. Since logs are not stable yet this WILL be removed in future releases.") +class ConsoleLogExporter(ConsoleLogRecordExporter): + pass + + +class SimpleLogRecordProcessor(LogRecordProcessor): + """Implementation of LogRecordProcessor that exports logs synchronously. + + This processor passes received logs directly to the configured + ``LogRecordExporter`` as soon as they are emitted. + + This class serves as a reference implementation for custom log processors, + demonstrating proper error handling. Note how the ``on_emit`` method wraps + the exporter call in a try/except block to prevent exceptions from + propagating to the application. + """ + + def __init__( + self, + exporter: LogRecordExporter, + *, + meter_provider: MeterProvider | None = None, + ): + self._exporter = exporter + self._shutdown = False + self._metrics = create_processor_metrics( + "logs", + OtelComponentTypeValues.SIMPLE_LOG_PROCESSOR, + meter_provider or get_meter_provider(), + enabled=parse_boolean_environment_variable(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED), + ) + + def on_emit(self, log_record: ReadWriteLogRecord): + # Prevent entering a recursive loop. + cnt = get_value(_ON_EMIT_RECURSION_COUNT_KEY) or 0 + # Recursive depth of 3 is sort of arbitrary. It's possible that an Exporter.export call + # emits a log which returns us to this function, but when we call Exporter.export again the log + # is no longer emitted and we exit this recursive loop naturally, a depth of >3 allows 3 + # recursive log calls but exits after because it's likely endless. + if cnt > 3: # pyright: ignore[reportOperatorIssue] + _propagate_false_logger.warning( + "SimpleLogRecordProcessor.on_emit has entered a recursive loop. Dropping log and exiting the loop." + ) + return + token = attach( + set_value( + _SUPPRESS_INSTRUMENTATION_KEY, + True, + set_value(_ON_EMIT_RECURSION_COUNT_KEY, cnt + 1), # pyright: ignore[reportOperatorIssue] + ) + ) + try: + if self._shutdown: + _logger.warning("Processor is already shutdown, ignoring call") + self._metrics.drop_items(1, "already_shutdown") + return + # Convert ReadWriteLogRecord to ReadableLogRecord before exporting + # Note: resource should not be None at this point as it's set during Logger.emit() + resource = log_record.resource if log_record.resource is not None else Resource.create({}) + readable_log_record = ReadableLogRecord( + log_record=log_record.log_record, + resource=resource, + instrumentation_scope=log_record.instrumentation_scope, + limits=log_record.limits, + ) + # Record on submission to the exporter. + self._metrics.finish_items(1) + self._exporter.export((readable_log_record,)) + except Exception: # pylint: disable=broad-exception-caught + _logger.exception("Exception while exporting logs.") + finally: + detach(token) + + def shutdown(self): + self._shutdown = True + self._exporter.shutdown() + + def force_flush(self, timeout_millis: int = 30000) -> bool: # pylint: disable=no-self-use + return True + + +class BatchLogRecordProcessor(LogRecordProcessor): + """This is an implementation of LogRecordProcessor which creates batches of + received logs and sends them to the configured LogRecordExporter. + + `BatchLogRecordProcessor` is configurable with the following environment + variables which correspond to constructor parameters: + + - :envvar:`OTEL_BLRP_SCHEDULE_DELAY` + - :envvar:`OTEL_BLRP_MAX_QUEUE_SIZE` + - :envvar:`OTEL_BLRP_MAX_EXPORT_BATCH_SIZE` + - :envvar:`OTEL_BLRP_EXPORT_TIMEOUT` + + All the logic for emitting logs, shutting down etc. resides in the BatchProcessor class. + """ + + def __init__( + self, + exporter: LogRecordExporter, + schedule_delay_millis: float | None = None, + max_export_batch_size: int | None = None, + export_timeout_millis: float | None = None, + max_queue_size: int | None = None, + *, + meter_provider: MeterProvider | None = None, + ): + if max_queue_size is None: + max_queue_size = BatchLogRecordProcessor._default_max_queue_size() + + if schedule_delay_millis is None: + schedule_delay_millis = BatchLogRecordProcessor._default_schedule_delay_millis() + + if max_export_batch_size is None: + max_export_batch_size = BatchLogRecordProcessor._default_max_export_batch_size() + # Not used. No way currently to pass timeout to export. + if export_timeout_millis is None: + export_timeout_millis = BatchLogRecordProcessor._default_export_timeout_millis() + + BatchLogRecordProcessor._validate_arguments(max_queue_size, schedule_delay_millis, max_export_batch_size) + # Initializes BatchProcessor + self._batch_processor = BatchProcessor( + exporter, + schedule_delay_millis, + max_export_batch_size, + export_timeout_millis, + max_queue_size, + "Log", + create_processor_metrics( + "logs", + OtelComponentTypeValues.BATCHING_LOG_PROCESSOR, + meter_provider or get_meter_provider(), + capacity=max_queue_size, + enabled=parse_boolean_environment_variable(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED), + ), + ) + + def on_emit(self, log_record: ReadWriteLogRecord) -> None: + # Convert ReadWriteLogRecord to ReadableLogRecord before passing to BatchProcessor + # Note: resource should not be None at this point as it's set during Logger.emit() + resource = log_record.resource if log_record.resource is not None else Resource.create({}) + # Shallow copy the API log record to break the reference to the potentially large context + # while keeping the original context intact for other processors. + api_log_record = copy.copy(log_record.log_record) + api_log_record.context = Context() + + readable_log_record = ReadableLogRecord( + log_record=api_log_record, + resource=resource, + instrumentation_scope=log_record.instrumentation_scope, + limits=log_record.limits, + ) + return self._batch_processor.emit(readable_log_record) + + def shutdown(self): + return self._batch_processor.shutdown() + + def force_flush(self, timeout_millis: int = 30000) -> bool: + return self._batch_processor.force_flush(timeout_millis) + + @staticmethod + def _default_max_queue_size(): + try: + return int(environ.get(OTEL_BLRP_MAX_QUEUE_SIZE, _DEFAULT_MAX_QUEUE_SIZE)) + except ValueError: + _logger.exception( + _ENV_VAR_INT_VALUE_ERROR_MESSAGE, + OTEL_BLRP_MAX_QUEUE_SIZE, + _DEFAULT_MAX_QUEUE_SIZE, + ) + return _DEFAULT_MAX_QUEUE_SIZE + + @staticmethod + def _default_schedule_delay_millis(): + try: + return int(environ.get(OTEL_BLRP_SCHEDULE_DELAY, _DEFAULT_SCHEDULE_DELAY_MILLIS)) + except ValueError: + _logger.exception( + _ENV_VAR_INT_VALUE_ERROR_MESSAGE, + OTEL_BLRP_SCHEDULE_DELAY, + _DEFAULT_SCHEDULE_DELAY_MILLIS, + ) + return _DEFAULT_SCHEDULE_DELAY_MILLIS + + @staticmethod + def _default_max_export_batch_size(): + try: + return int( + environ.get( + OTEL_BLRP_MAX_EXPORT_BATCH_SIZE, + _DEFAULT_MAX_EXPORT_BATCH_SIZE, + ) + ) + except ValueError: + _logger.exception( + _ENV_VAR_INT_VALUE_ERROR_MESSAGE, + OTEL_BLRP_MAX_EXPORT_BATCH_SIZE, + _DEFAULT_MAX_EXPORT_BATCH_SIZE, + ) + return _DEFAULT_MAX_EXPORT_BATCH_SIZE + + @staticmethod + def _default_export_timeout_millis(): + try: + return int(environ.get(OTEL_BLRP_EXPORT_TIMEOUT, _DEFAULT_EXPORT_TIMEOUT_MILLIS)) + except ValueError: + _logger.exception( + _ENV_VAR_INT_VALUE_ERROR_MESSAGE, + OTEL_BLRP_EXPORT_TIMEOUT, + _DEFAULT_EXPORT_TIMEOUT_MILLIS, + ) + return _DEFAULT_EXPORT_TIMEOUT_MILLIS + + @staticmethod + def _validate_arguments(max_queue_size, schedule_delay_millis, max_export_batch_size): + if max_queue_size <= 0: + raise ValueError("max_queue_size must be a positive integer.") + + if schedule_delay_millis <= 0: + raise ValueError("schedule_delay_millis must be positive.") + + if max_export_batch_size <= 0: + raise ValueError("max_export_batch_size must be a positive integer.") + + if max_export_batch_size > max_queue_size: + raise ValueError("max_export_batch_size must be less than or equal to max_queue_size.") diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/export/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/export/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c18c89a Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/export/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/export/__pycache__/in_memory_log_exporter.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/export/__pycache__/in_memory_log_exporter.cpython-312.pyc new file mode 100644 index 0000000..a3b41d5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/export/__pycache__/in_memory_log_exporter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py new file mode 100644 index 0000000..d11a174 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py @@ -0,0 +1,53 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import collections.abc +import threading + +from typing_extensions import deprecated + +from opentelemetry.sdk._logs import ReadableLogRecord +from opentelemetry.sdk._logs.export import ( + LogRecordExporter, + LogRecordExportResult, +) + + +class InMemoryLogRecordExporter(LogRecordExporter): + """Implementation of :class:`.LogRecordExporter` that stores logs in memory. + + This class can be used for testing purposes. It stores the exported logs + in a list in memory that can be retrieved using the + :meth:`.get_finished_logs` method. + """ + + def __init__(self): + self._logs = [] + self._lock = threading.Lock() + self._stopped = False + + def clear(self) -> None: + with self._lock: + self._logs.clear() + + def get_finished_logs(self) -> tuple[ReadableLogRecord, ...]: + with self._lock: + return tuple(self._logs) + + def export(self, batch: collections.abc.Sequence[ReadableLogRecord]) -> LogRecordExportResult: + if self._stopped: + return LogRecordExportResult.FAILURE + with self._lock: + self._logs.extend(batch) + return LogRecordExportResult.SUCCESS + + def shutdown(self) -> None: + self._stopped = True + + def force_flush(self, timeout_millis: int = 30000) -> bool: + return True + + +@deprecated("Use InMemoryLogRecordExporter. Since logs are not stable yet this WILL be removed in future releases.") +class InMemoryLogExporter(InMemoryLogRecordExporter): + pass diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/export/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/export/__init__.py new file mode 100644 index 0000000..310a194 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/export/__init__.py @@ -0,0 +1,32 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from opentelemetry.sdk._logs._internal.export import ( + BatchLogRecordProcessor, + ConsoleLogExporter, + ConsoleLogRecordExporter, + LogExporter, + LogExportResult, + LogRecordExporter, + LogRecordExportResult, + SimpleLogRecordProcessor, +) + +# The point module is not in the export directory to avoid a circular import. +from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( + InMemoryLogExporter, + InMemoryLogRecordExporter, +) + +__all__ = [ + "BatchLogRecordProcessor", + "ConsoleLogExporter", + "ConsoleLogRecordExporter", + "InMemoryLogExporter", + "InMemoryLogRecordExporter", + "LogExportResult", + "LogExporter", + "LogRecordExportResult", + "LogRecordExporter", + "SimpleLogRecordProcessor", +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/export/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/export/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..721bd62 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_logs/export/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_shared_internal/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_shared_internal/__init__.py new file mode 100644 index 0000000..cf46ea6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_shared_internal/__init__.py @@ -0,0 +1,237 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import collections +import enum +import inspect +import logging +import os +import threading +import time +import weakref +from abc import abstractmethod +from typing import ( + Generic, + Protocol, + TypeVar, +) + +from opentelemetry.context import ( + _SUPPRESS_INSTRUMENTATION_KEY, + attach, + detach, + set_value, +) +from opentelemetry.sdk._shared_internal._processor_metrics import ( + ProcessorMetricsT, +) +from opentelemetry.util._once import Once + + +class DuplicateFilter(logging.Filter): + """Filter that can be applied to internal `logger`'s. + + Currently applied to `logger`s on the export logs path to prevent endlessly logging the same log + in cases where logging itself is failing.""" + + def filter(self, record): + current_log = ( + record.module, + record.levelno, + record.msg, + # We need to pick a time longer than the OTLP LogExporter timeout + # which defaults to 10 seconds, but not pick something so long that + # it filters out useful logs. + time.time() // 20, + ) + if current_log != getattr(self, "last_log", None): + self.last_log = current_log # pylint: disable=attribute-defined-outside-init + return True + # False means python's `logging` module will no longer process this log. + return False + + +class BatchExportStrategy(enum.Enum): + EXPORT_ALL = 0 + EXPORT_WHILE_BATCH_EXCEEDS_THRESHOLD = 1 + EXPORT_AT_LEAST_ONE_BATCH = 2 + + +Telemetry = TypeVar("Telemetry") + + +class Exporter(Protocol[Telemetry]): + @abstractmethod + def export(self, batch: list[Telemetry], /): + raise NotImplementedError + + @abstractmethod + def shutdown(self): + raise NotImplementedError + + +_logger = logging.getLogger(__name__) +_logger.addFilter(DuplicateFilter()) + + +class BatchProcessor(Generic[Telemetry]): + """This class can be used with exporter's that implement the above + Exporter interface to buffer and send telemetry in batch through + the exporter.""" + + def __init__( + self, + exporter: Exporter[Telemetry], + schedule_delay_millis: float, + max_export_batch_size: int, + export_timeout_millis: float, + max_queue_size: int, + exporting: str, + metrics: ProcessorMetricsT, + ): + self._bsp_reset_once = Once() + self._exporter = exporter + self._max_queue_size = max_queue_size + self._schedule_delay_millis = schedule_delay_millis + self._schedule_delay = schedule_delay_millis / 1e3 + self._max_export_batch_size = max_export_batch_size + # Not used. No way currently to pass timeout to export. + # TODO(https://github.com/open-telemetry/opentelemetry-python/issues/4555): figure out what this should do. + self._export_timeout_millis = export_timeout_millis + # Deque is thread safe. + self._queue = collections.deque([], max_queue_size) + self._worker_thread = threading.Thread( + name=f"OtelBatch{exporting}RecordProcessor", + target=self.worker, + daemon=True, + ) + self._exporting = exporting + + self._shutdown = False + self._shutdown_timeout_exceeded = False + self._export_lock = threading.Lock() + self._worker_awaken = threading.Event() + self._worker_thread.start() + if hasattr(os, "register_at_fork"): + weak_reinit = weakref.WeakMethod(self._at_fork_reinit) + + def _after_in_child() -> None: + if reinit := weak_reinit(): + reinit() + + os.register_at_fork(after_in_child=_after_in_child) + self._pid = os.getpid() + + metrics.register_queue_size(lambda: len(self._queue)) + self._metrics = metrics + + def _should_export_batch(self, batch_strategy: BatchExportStrategy, num_iterations: int) -> bool: + if not self._queue or self._shutdown_timeout_exceeded: + return False + # Always continue to export while queue length exceeds max batch size. + if len(self._queue) >= self._max_export_batch_size: + return True + if batch_strategy is BatchExportStrategy.EXPORT_ALL: + return True + if batch_strategy is BatchExportStrategy.EXPORT_AT_LEAST_ONE_BATCH: + return num_iterations == 0 + return False + + def _at_fork_reinit(self): + self._export_lock = threading.Lock() + self._worker_awaken = threading.Event() + self._queue.clear() + self._worker_thread = threading.Thread( + name=f"OtelBatch{self._exporting}RecordProcessor", + target=self.worker, + daemon=True, + ) + self._worker_thread.start() + self._pid = os.getpid() + + def worker(self): + while not self._shutdown: + # Lots of strategies in the spec for setting next timeout. + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#batching-processor. + # Shutdown will interrupt this sleep. Emit will interrupt this sleep only if the queue is bigger then threshold. + sleep_interrupted = self._worker_awaken.wait(self._schedule_delay) + if self._shutdown: + break + self._export( + BatchExportStrategy.EXPORT_WHILE_BATCH_EXCEEDS_THRESHOLD + if sleep_interrupted + else BatchExportStrategy.EXPORT_AT_LEAST_ONE_BATCH + ) + self._worker_awaken.clear() + self._export(BatchExportStrategy.EXPORT_ALL) + + def _export(self, batch_strategy: BatchExportStrategy) -> None: + with self._export_lock: + iteration = 0 + # We could see concurrent export calls from worker and force_flush. We call _should_export_batch + # once the lock is obtained to see if we still need to make the requested export. + while self._should_export_batch(batch_strategy, iteration): + iteration += 1 + token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True)) + count = min( + self._max_export_batch_size, + len(self._queue), + ) + # Oldest records are at the back, so pop from there. + batch = [self._queue.pop() for _ in range(count)] + # Record on submission to the exporter. + self._metrics.finish_items(count) + try: + self._exporter.export(batch) + except Exception: # pylint: disable=broad-exception-caught + _logger.exception("Exception while exporting %s.", self._exporting) + detach(token) + + def emit(self, data: Telemetry) -> None: + if self._shutdown: + _logger.info("Shutdown called, ignoring %s.", self._exporting) + self._metrics.drop_items(1, "already_shutdown") + return + if self._pid != os.getpid(): + self._bsp_reset_once.do_once(self._at_fork_reinit) + if len(self._queue) == self._max_queue_size: + _logger.warning("Queue full, dropping %s.", self._exporting) + self._metrics.drop_items(1) + # This will drop a log from the right side if the queue is at _max_queue_size. + self._queue.appendleft(data) + if len(self._queue) >= self._max_export_batch_size and not self._worker_awaken.is_set(): + self._worker_awaken.set() + + def shutdown(self, timeout_millis: int = 30000): + if self._shutdown: + return + shutdown_should_end = time.time() + (timeout_millis / 1000) + # Causes emit to reject telemetry and makes force_flush a no-op. + self._shutdown = True + # Interrupts sleep in the worker if it's sleeping. + self._worker_awaken.set() + self._worker_thread.join(timeout_millis / 1000) + # Stops worker thread from calling export again if queue is still not empty. + self._shutdown_timeout_exceeded = True + # We want to shutdown immediately only if we already waited `timeout_secs`. + # Otherwise we pass the remaining timeout to the exporter. + # Some exporter's shutdown support a timeout param. + if "timeout_millis" in inspect.getfullargspec(self._exporter.shutdown).args: + remaining_millis = (shutdown_should_end - time.time()) * 1000 + self._exporter.shutdown(timeout_millis=max(0, remaining_millis)) # type: ignore + else: + self._exporter.shutdown() + # Worker thread **should** be finished at this point, because we called shutdown on the exporter, + # and set shutdown_is_occuring to prevent further export calls. It's possible that a single export + # call is ongoing and the thread isn't finished. In this case we will return instead of waiting on + # the thread to finish. + + # TODO: Fix force flush so the timeout is used https://github.com/open-telemetry/opentelemetry-python/issues/4568. + def force_flush(self, timeout_millis: int | None = None) -> bool: + if self._shutdown: + return False + # Blocking call to export. + self._export(BatchExportStrategy.EXPORT_ALL) + return True diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_shared_internal/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_shared_internal/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b950871 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_shared_internal/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_shared_internal/__pycache__/_processor_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_shared_internal/__pycache__/_processor_metrics.cpython-312.pyc new file mode 100644 index 0000000..bb9bac9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_shared_internal/__pycache__/_processor_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/_shared_internal/_processor_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_shared_internal/_processor_metrics.py new file mode 100644 index 0000000..699f7e1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/_shared_internal/_processor_metrics.py @@ -0,0 +1,140 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections import Counter +from collections.abc import Callable +from typing import Literal, Protocol + +from opentelemetry.metrics import CallbackOptions, MeterProvider, Observation +from opentelemetry.semconv._incubating.attributes.otel_attributes import ( + OTEL_COMPONENT_NAME, + OTEL_COMPONENT_TYPE, + OtelComponentTypeValues, +) +from opentelemetry.semconv._incubating.metrics.otel_metrics import ( + OTEL_SDK_PROCESSOR_LOG_QUEUE_SIZE, + OTEL_SDK_PROCESSOR_SPAN_QUEUE_SIZE, + create_otel_sdk_processor_log_processed, + create_otel_sdk_processor_log_queue_capacity, + create_otel_sdk_processor_span_processed, + create_otel_sdk_processor_span_queue_capacity, +) +from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE + +_component_counter = Counter() + + +class ProcessorMetricsT(Protocol): + def register_queue_size(self, get_queue_size: Callable[[], int]) -> None: ... + + def drop_items(self, count: int, error_type: str = "queue_full") -> None: ... + + def finish_items(self, count: int) -> None: ... + + +class NoOpProcessorMetrics: + def register_queue_size(self, get_queue_size: Callable[[], int]) -> None: + pass + + def drop_items(self, count: int, error_type: str = "queue_full") -> None: + pass + + def finish_items(self, count: int) -> None: + pass + + +class ProcessorMetrics: + def __init__( + self, + signal: Literal["traces", "logs"], + component_type: OtelComponentTypeValues, + meter_provider: MeterProvider, + *, + capacity: int | None = None, + ) -> None: + self._signal = signal + meter = meter_provider.get_meter("opentelemetry-sdk") + self._meter = meter + + count = _component_counter[component_type.value] + _component_counter[component_type.value] = count + 1 + + self._standard_attrs = { + OTEL_COMPONENT_TYPE: component_type.value, + OTEL_COMPONENT_NAME: f"{component_type.value}/{count}", + } + + self._dropped_attrs = { + **self._standard_attrs, + ERROR_TYPE: "queue_full", + } + + self._already_shutdown_attrs = { + **self._standard_attrs, + ERROR_TYPE: "already_shutdown", + } + + if signal == "traces": + create_processed = create_otel_sdk_processor_span_processed + create_queue_capacity = create_otel_sdk_processor_span_queue_capacity + else: + create_processed = create_otel_sdk_processor_log_processed + create_queue_capacity = create_otel_sdk_processor_log_queue_capacity + + self._processed = create_processed(meter) + + if capacity is not None: + self._queue_capacity = create_queue_capacity(meter) + self._queue_capacity.add(capacity, self._standard_attrs) + + def register_queue_size(self, get_queue_size: Callable[[], int]) -> None: + def record_queue_size( + _options: CallbackOptions, + ) -> tuple[Observation]: + return (Observation(get_queue_size(), self._standard_attrs),) + + if self._signal == "traces": + queue_size_name = OTEL_SDK_PROCESSOR_SPAN_QUEUE_SIZE + queue_size_description = "The number of spans in the queue of a given instance of an SDK span processor." + queue_size_unit = "{span}" + else: + queue_size_name = OTEL_SDK_PROCESSOR_LOG_QUEUE_SIZE + queue_size_description = "The number of logs in the queue of a given instance of an SDK log processor." + queue_size_unit = "{log}" + + self._meter.create_observable_up_down_counter( + queue_size_name, + callbacks=(record_queue_size,), + description=queue_size_description, + unit=queue_size_unit, + ) + + def drop_items(self, count: int, error_type: str = "queue_full") -> None: + if error_type == "already_shutdown": + self._processed.add(count, self._already_shutdown_attrs) + else: + self._processed.add(count, self._dropped_attrs) + + def finish_items(self, count: int) -> None: + self._processed.add(count, self._standard_attrs) + + +def create_processor_metrics( + signal: Literal["traces", "logs"], + component_type: OtelComponentTypeValues, + meter_provider: MeterProvider, + *, + capacity: int | None = None, + enabled: bool, +) -> ProcessorMetricsT: + if not enabled: + return NoOpProcessorMetrics() + + return ProcessorMetrics( + signal, + component_type, + meter_provider, + capacity=capacity, + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/environment_variables/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/environment_variables/__init__.py new file mode 100644 index 0000000..303f629 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/environment_variables/__init__.py @@ -0,0 +1,863 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +OTEL_SDK_DISABLED = "OTEL_SDK_DISABLED" +""" +.. envvar:: OTEL_SDK_DISABLED + +The :envvar:`OTEL_SDK_DISABLED` environment variable disables the SDK for all signals +Default: "false" +""" + +OTEL_CONFIG_FILE = "OTEL_CONFIG_FILE" +""" +.. envvar:: OTEL_CONFIG_FILE + +The :envvar:`OTEL_CONFIG_FILE` environment variable points the SDK at a +declarative configuration file (YAML or JSON). When set, the file is the +sole source for SDK construction. Spec-defined ``OTEL_*`` variables with +schema equivalents are ignored. Env vars may still be read indirectly by +components the file enables (e.g. resource detectors) and via +``${env:VAR}`` substitution inside the file. + +Python-implementation extensions outside the spec (``OTEL_PYTHON_*`` +variables such as ``OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED`` or +:envvar:`OTEL_PYTHON_TRACER_CONFIGURATOR`) are also bypassed when +:envvar:`OTEL_CONFIG_FILE` is set, because the env-var initialisation path +is skipped entirely in favour of the declarative file. Honouring these +alongside a config file is tracked separately. + +See the OpenTelemetry declarative configuration specification for details. +""" + +OTEL_RESOURCE_ATTRIBUTES = "OTEL_RESOURCE_ATTRIBUTES" +""" +.. envvar:: OTEL_RESOURCE_ATTRIBUTES + +The :envvar:`OTEL_RESOURCE_ATTRIBUTES` environment variable allows resource +attributes to be passed to the SDK at process invocation. The attributes from +:envvar:`OTEL_RESOURCE_ATTRIBUTES` are merged with those passed to +`Resource.create`, meaning :envvar:`OTEL_RESOURCE_ATTRIBUTES` takes *lower* +priority. Attributes should be in the format ``key1=value1,key2=value2``. +Additional details are available `in the specification +`__. + +.. code-block:: console + + $ OTEL_RESOURCE_ATTRIBUTES="service.name=shoppingcard,will_be_overridden=foo" python - <`__. +""" + +OTEL_EXPORTER_OTLP_TIMEOUT = "OTEL_EXPORTER_OTLP_TIMEOUT" +""" +.. envvar:: OTEL_EXPORTER_OTLP_TIMEOUT + +The :envvar:`OTEL_EXPORTER_OTLP_TIMEOUT` is the maximum time (in seconds) the OTLP exporter will wait for each batch export. +Default: 10 +""" + +OTEL_EXPORTER_OTLP_ENDPOINT = "OTEL_EXPORTER_OTLP_ENDPOINT" +""" +.. envvar:: OTEL_EXPORTER_OTLP_ENDPOINT + +The :envvar:`OTEL_EXPORTER_OTLP_ENDPOINT` target to which the exporter is going to send spans or metrics. +The endpoint MUST be a valid URL host, and MAY contain a scheme (http or https), port and path. +A scheme of https indicates a secure connection and takes precedence over the insecure configuration setting. +Default: "http://localhost:4317" +""" + +OTEL_EXPORTER_OTLP_INSECURE = "OTEL_EXPORTER_OTLP_INSECURE" +""" +.. envvar:: OTEL_EXPORTER_OTLP_INSECURE + +The :envvar:`OTEL_EXPORTER_OTLP_INSECURE` represents whether to enable client transport security for gRPC requests. +A scheme of https takes precedence over this configuration setting. +Default: False +""" + +OTEL_EXPORTER_OTLP_TRACES_INSECURE = "OTEL_EXPORTER_OTLP_TRACES_INSECURE" +""" +.. envvar:: OTEL_EXPORTER_OTLP_TRACES_INSECURE + +The :envvar:`OTEL_EXPORTER_OTLP_TRACES_INSECURE` represents whether to enable client transport security +for gRPC requests for spans. A scheme of https takes precedence over the this configuration setting. +Default: False +""" + + +OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT" +""" +.. envvar:: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT + +The :envvar:`OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` target to which the span exporter is going to send spans. +The endpoint MUST be a valid URL host, and MAY contain a scheme (http or https), port and path. +A scheme of https indicates a secure connection and takes precedence over this configuration setting. +""" + +OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT" +""" +.. envvar:: OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + +The :envvar:`OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` target to which the metrics exporter is going to send metrics. +The endpoint MUST be a valid URL host, and MAY contain a scheme (http or https), port and path. +A scheme of https indicates a secure connection and takes precedence over this configuration setting. +""" + +OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT" +""" +.. envvar:: OTEL_EXPORTER_OTLP_LOGS_ENDPOINT + +The :envvar:`OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` target to which the log exporter is going to send logs. +The endpoint MUST be a valid URL host, and MAY contain a scheme (http or https), port and path. +A scheme of https indicates a secure connection and takes precedence over this configuration setting. +""" + +_OTEL_PYTHON_EXPORTER_OTLP_GRPC_LOGS_CREDENTIAL_PROVIDER = "OTEL_PYTHON_EXPORTER_OTLP_GRPC_LOGS_CREDENTIAL_PROVIDER" +""" +.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_GRPC_LOGS_CREDENTIAL_PROVIDER + +The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_GRPC_LOGS_CREDENTIAL_PROVIDER` provides `grpc.ChannelCredentials` to the grpc OTLP Log exporter, +Entry point providers should implement the following: + +.. code-block:: python + + import grpc + + # Add a reference to this function under the `opentelemetry_otlp_credential_provider` entry point. + def channel_credential_provider() -> grpc.ChannelCredentials: + +Note: This environment variable is experimental and subject to change. +""" + +_OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER = "OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER" +""" +.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER + +The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER` provides `requests.Session` for the HTTP OTLP Log exporter. +Entry point providers should implement the following: + +.. code-block:: python + + import requests + + # Add a reference to this function under the `opentelemetry_otlp_credential_provider` entry point. + def request_session_provder() -> requests.Session: + +Note: This environment variable is experimental and subject to change. +""" +_OTEL_PYTHON_EXPORTER_OTLP_HTTP_CREDENTIAL_PROVIDER = "OTEL_PYTHON_EXPORTER_OTLP_HTTP_CREDENTIAL_PROVIDER" +""" +.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_HTTP_CREDENTIAL_PROVIDER + +The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_HTTP_CREDENTIAL_PROVIDER` provides `requests.Session` for all HTTP OTLP exporters. +Entry point providers should implement the following: + +.. code-block:: python + + import requests + + # Add a reference to this function under the `opentelemetry_otlp_credential_provider` entry point. + def request_session_provder() -> requests.Session: + +Note: This environment variable is experimental and subject to change. +""" +_OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER = "OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER" +""" +.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER + +The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER` provides `grpc.ChannelCredentials` for all GRPC OTLP exporters. +Entry point providers should implement the following: + +.. code-block:: python + + import grpc + + # Add a reference to this function under the `opentelemetry_otlp_credential_provider` entry point. + def channel_credential_provider() -> grpc.ChannelCredentials: + +Note: This environment variable is experimental and subject to change. +""" +_OTEL_PYTHON_EXPORTER_OTLP_HTTP_TRACES_CREDENTIAL_PROVIDER = "OTEL_PYTHON_EXPORTER_OTLP_HTTP_TRACES_CREDENTIAL_PROVIDER" +""" +.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_HTTP_TRACES_CREDENTIAL_PROVIDER + +The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_HTTP_TRACES_CREDENTIAL_PROVIDER` provides `requests.Session` to the HTTP OTLP Span exporter. +Entry point providers should implement the following: + +.. code-block:: python + + import requests + + # Add a reference to this function under the `opentelemetry_otlp_credential_provider` entry point. + def request_session_provder() -> requests.Session: + +Note: This environment variable is experimental and subject to change. +""" +_OTEL_PYTHON_EXPORTER_OTLP_GRPC_TRACES_CREDENTIAL_PROVIDER = "OTEL_PYTHON_EXPORTER_OTLP_GRPC_TRACES_CREDENTIAL_PROVIDER" +""" +.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_GRPC_TRACES_CREDENTIAL_PROVIDER + +The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_GRPC_TRACES_CREDENTIAL_PROVIDER` provides `grpc.ChannelCredentials` to the GRPC OTLP Span exporter. +Entry point providers should implement the following: + +.. code-block:: python + + import grpc + + # Add a reference to this function under the `opentelemetry_otlp_credential_provider` entry point. + def channel_credential_provider() -> grpc.ChannelCredentials: + +Note: This environment variable is experimental and subject to change. +""" +_OTEL_PYTHON_EXPORTER_OTLP_HTTP_METRICS_CREDENTIAL_PROVIDER = ( + "OTEL_PYTHON_EXPORTER_OTLP_HTTP_METRICS_CREDENTIAL_PROVIDER" +) +""" +.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_HTTP_METRICS_CREDENTIAL_PROVIDER + +The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_HTTP_METRICS_CREDENTIAL_PROVIDER` provides `requests.Session` to the HTTP OTLP Metric exporter. +Entry point providers should implement the following: + +.. code-block:: python + + import requests + + # Add a reference to this function under the `opentelemetry_otlp_credential_provider` entry point. + def request_session_provder() -> requests.Session: + +Note: This environment variable is experimental and subject to change. +""" +_OTEL_PYTHON_EXPORTER_OTLP_GRPC_METRICS_CREDENTIAL_PROVIDER = ( + "OTEL_PYTHON_EXPORTER_OTLP_GRPC_METRICS_CREDENTIAL_PROVIDER" +) +""" +.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_GRPC_METRICS_CREDENTIAL_PROVIDER + +The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_GRPC_METRICS_CREDENTIAL_PROVIDER` provides `grpc.ChannelCredentials` to the GRPC OTLP Metric exporter. +Entry point providers should implement the following: + +.. code-block:: python + + import grpc + + # Add a reference to this function under the `opentelemetry_otlp_credential_provider` entry point. + def channel_credential_provider() -> grpc.ChannelCredentials: + +Note: This environment variable is experimental and subject to change. +""" + +_OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES = "OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES" +""" +.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES + +The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES` stores a comma-separated list of human-readable +gRPC error codes that are considered retryable for the OTLP gRPC exporters (e.g. `UNAVAILABLE, DEADLINE_EXCEEDED`). +Supported error codes are defined in `grpc.StatusCode` and are parsed in a case-insensitive manner. + +Note: This environment variable is experimental and subject to change. +""" + +OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE = "OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE" +""" +.. envvar:: OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE + +The :envvar:`OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE` stores the path to the certificate file for +TLS credentials of gRPC client for traces. Should only be used for a secure connection for tracing. +""" + +OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE = "OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE" +""" +.. envvar:: OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE + +The :envvar:`OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE` stores the path to the certificate file for +TLS credentials of gRPC client for metrics. Should only be used for a secure connection for exporting metrics. +""" + +OTEL_EXPORTER_OTLP_CLIENT_KEY = "OTEL_EXPORTER_OTLP_CLIENT_KEY" +""" +.. envvar:: OTEL_EXPORTER_OTLP_CLIENT_KEY + +The :envvar:`OTEL_EXPORTER_OTLP_CLIENT_KEY` stores the path to the client private key to use +in mTLS communication in PEM format. +""" + +OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY = "OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY" +""" +.. envvar:: OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY + +The :envvar:`OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY` stores the path to the client private key to use +in mTLS communication in PEM format for traces. +""" + +OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY = "OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY" +""" +.. envvar:: OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY + +The :envvar:`OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY` stores the path to the client private key to use +in mTLS communication in PEM format for metrics. +""" + +OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY = "OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY" +""" +.. envvar:: OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY + +The :envvar:`OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY` stores the path to the client private key to use +in mTLS communication in PEM format for logs. +""" + +OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE = "OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE" +""" +.. envvar:: OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE + +The :envvar:`OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE` stores the path to the client certificate/chain trust for +clients private key to use in mTLS communication in PEM format. +""" + +OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE = "OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE" +""" +.. envvar:: OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE + +The :envvar:`OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE` stores the path to the client certificate/chain trust for +clients private key to use in mTLS communication in PEM format for traces. +""" + +OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE = "OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE" +""" +.. envvar:: OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE + +The :envvar:`OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE` stores the path to the client certificate/chain trust for +clients private key to use in mTLS communication in PEM format for metrics. +""" + +OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE = "OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE" +""" +.. envvar:: OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE + +The :envvar:`OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE` stores the path to the client certificate/chain trust for +clients private key to use in mTLS communication in PEM format for logs. +""" + +OTEL_EXPORTER_OTLP_TRACES_HEADERS = "OTEL_EXPORTER_OTLP_TRACES_HEADERS" +""" +.. envvar:: OTEL_EXPORTER_OTLP_TRACES_HEADERS + +The :envvar:`OTEL_EXPORTER_OTLP_TRACES_HEADERS` contains the key-value pairs to be used as headers for spans +associated with gRPC or HTTP requests. +""" + +OTEL_EXPORTER_OTLP_METRICS_HEADERS = "OTEL_EXPORTER_OTLP_METRICS_HEADERS" +""" +.. envvar:: OTEL_EXPORTER_OTLP_METRICS_HEADERS + +The :envvar:`OTEL_EXPORTER_OTLP_METRICS_HEADERS` contains the key-value pairs to be used as headers for metrics +associated with gRPC or HTTP requests. +""" + +OTEL_EXPORTER_OTLP_LOGS_HEADERS = "OTEL_EXPORTER_OTLP_LOGS_HEADERS" +""" +.. envvar:: OTEL_EXPORTER_OTLP_LOGS_HEADERS + +The :envvar:`OTEL_EXPORTER_OTLP_LOGS_HEADERS` contains the key-value pairs to be used as headers for logs +associated with gRPC or HTTP requests. +""" + +OTEL_EXPORTER_OTLP_TRACES_COMPRESSION = "OTEL_EXPORTER_OTLP_TRACES_COMPRESSION" +""" +.. envvar:: OTEL_EXPORTER_OTLP_TRACES_COMPRESSION + +Same as :envvar:`OTEL_EXPORTER_OTLP_COMPRESSION` but only for the span +exporter. If both are present, this takes higher precedence. +""" + +OTEL_EXPORTER_OTLP_METRICS_COMPRESSION = "OTEL_EXPORTER_OTLP_METRICS_COMPRESSION" +""" +.. envvar:: OTEL_EXPORTER_OTLP_METRICS_COMPRESSION + +Same as :envvar:`OTEL_EXPORTER_OTLP_COMPRESSION` but only for the metric +exporter. If both are present, this takes higher precedence. +""" + +OTEL_EXPORTER_OTLP_LOGS_COMPRESSION = "OTEL_EXPORTER_OTLP_LOGS_COMPRESSION" +""" +.. envvar:: OTEL_EXPORTER_OTLP_LOGS_COMPRESSION + +Same as :envvar:`OTEL_EXPORTER_OTLP_COMPRESSION` but only for the log +exporter. If both are present, this takes higher precedence. +""" + +OTEL_EXPORTER_OTLP_TRACES_TIMEOUT = "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT" +""" +.. envvar:: OTEL_EXPORTER_OTLP_TRACES_TIMEOUT + +The :envvar:`OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` is the maximum time (in seconds) the OTLP exporter will +wait for each batch export for spans. +Default: 10 +""" + +OTEL_EXPORTER_OTLP_METRICS_TIMEOUT = "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT" +""" +.. envvar:: OTEL_EXPORTER_OTLP_METRICS_TIMEOUT + +The :envvar:`OTEL_EXPORTER_OTLP_METRICS_TIMEOUT` is the maximum time (in seconds) the OTLP exporter will +wait for each batch export for metrics. +Default: 10 +""" + +OTEL_EXPORTER_OTLP_METRICS_INSECURE = "OTEL_EXPORTER_OTLP_METRICS_INSECURE" +""" +.. envvar:: OTEL_EXPORTER_OTLP_METRICS_INSECURE + +The :envvar:`OTEL_EXPORTER_OTLP_METRICS_INSECURE` represents whether to enable client transport security +for gRPC requests for metrics. A scheme of https takes precedence over the this configuration setting. +Default: False +""" + +OTEL_EXPORTER_OTLP_LOGS_INSECURE = "OTEL_EXPORTER_OTLP_LOGS_INSECURE" +""" +.. envvar:: OTEL_EXPORTER_OTLP_LOGS_INSECURE + +The :envvar:`OTEL_EXPORTER_OTLP_LOGS_INSECURE` represents whether to enable client transport security +for gRPC requests for logs. A scheme of https takes precedence over the this configuration setting. +Default: False +""" + +OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE = "OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE" +""" +.. envvar:: OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE + +The :envvar:`OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE` stores the path to the certificate file for +TLS credentials of gRPC client for logs. Should only be used for a secure connection for logs. +""" + +OTEL_EXPORTER_OTLP_LOGS_TIMEOUT = "OTEL_EXPORTER_OTLP_LOGS_TIMEOUT" +""" +.. envvar:: OTEL_EXPORTER_OTLP_LOGS_TIMEOUT + +The :envvar:`OTEL_EXPORTER_OTLP_LOGS_TIMEOUT` is the maximum time (in seconds) the OTLP exporter will +wait for each batch export for logs. +Default: 10 +""" + +OTEL_SERVICE_NAME = "OTEL_SERVICE_NAME" +""" +.. envvar:: OTEL_SERVICE_NAME + +Convenience environment variable for setting the service name resource attribute. +The following two environment variables have the same effect + +.. code-block:: console + + OTEL_SERVICE_NAME=my-python-service + + OTEL_RESOURCE_ATTRIBUTES=service.name=my-python-service + + +If both are set, :envvar:`OTEL_SERVICE_NAME` takes precedence. +""" + + +_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED = "OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED" +""" +.. envvar:: OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED + +The :envvar:`OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED` environment variable allows users to +enable/disable the auto instrumentation for the python logging module. +Default: False + +Note: Logs SDK and its related settings are experimental. + +.. warning:: + + This option is deprecated, instead you should install `opentelemetry-instrumentation-logging`. +""" + + +OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE = "OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE" +""" +.. envvar:: OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE + +The :envvar:`OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` environment +variable allows users to set the default aggregation temporality policy to use +on the basis of instrument kind. The valid (case-insensitive) values are: + +``CUMULATIVE``: Use ``CUMULATIVE`` aggregation temporality for all instrument kinds. +``DELTA``: Use ``DELTA`` aggregation temporality for ``Counter``, ``Asynchronous Counter`` and ``Histogram``. +Use ``CUMULATIVE`` aggregation temporality for ``UpDownCounter`` and ``Asynchronous UpDownCounter``. +``LOWMEMORY``: Use ``DELTA`` aggregation temporality for ``Counter`` and ``Histogram``. +Use ``CUMULATIVE`` aggregation temporality for ``UpDownCounter``, ``AsynchronousCounter`` and ``Asynchronous UpDownCounter``. +""" + +OTEL_METRIC_EXPORT_INTERVAL = "OTEL_METRIC_EXPORT_INTERVAL" +""" +.. envvar:: OTEL_METRIC_EXPORT_INTERVAL + +The :envvar:`OTEL_METRIC_EXPORT_INTERVAL` is the time interval (in milliseconds) between the start of two export attempts. +""" + +OTEL_METRIC_EXPORT_TIMEOUT = "OTEL_METRIC_EXPORT_TIMEOUT" +""" +.. envvar:: OTEL_METRIC_EXPORT_TIMEOUT + +The :envvar:`OTEL_METRIC_EXPORT_TIMEOUT` is the maximum allowed time (in milliseconds) to export data. +""" + +OTEL_METRICS_EXEMPLAR_FILTER = "OTEL_METRICS_EXEMPLAR_FILTER" +""" +.. envvar:: OTEL_METRICS_EXEMPLAR_FILTER + +The :envvar:`OTEL_METRICS_EXEMPLAR_FILTER` is the filter for which measurements can become Exemplars. +""" + +OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION = "OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION" +""" +.. envvar:: OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION + +The :envvar:`OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION` is the default aggregation to use for histogram instruments. +""" + +OTEL_EXPERIMENTAL_RESOURCE_DETECTORS = "OTEL_EXPERIMENTAL_RESOURCE_DETECTORS" +""" +.. envvar:: OTEL_EXPERIMENTAL_RESOURCE_DETECTORS + +The :envvar:`OTEL_EXPERIMENTAL_RESOURCE_DETECTORS` is a comma-separated string +of names of resource detectors. These names must be the same as the names of +entry points for the ```opentelemetry_resource_detector``` entry point. This is an +experimental feature and the name of this variable and its behavior can change +in a non-backwards compatible way. + +Detectors are run in the order they are listed and their attributes are merged +in that order, with later detectors taking precedence over earlier ones on +conflicting keys. The ``otel`` detector (which reads +:envvar:`OTEL_RESOURCE_ATTRIBUTES` and :envvar:`OTEL_SERVICE_NAME`) is always +appended last unless explicitly placed elsewhere in the list, ensuring +environment variable attributes take highest priority among detectors. +""" + +OTEL_EXPORTER_PROMETHEUS_HOST = "OTEL_EXPORTER_PROMETHEUS_HOST" +""" +.. envvar:: OTEL_EXPORTER_PROMETHEUS_HOST + +The :envvar:`OTEL_EXPORTER_PROMETHEUS_HOST` environment variable configures the host used by +the Prometheus exporter. +Default: "localhost" + +This is an experimental environment variable and the name of this variable and its behavior can +change in a non-backwards compatible way. +""" + +OTEL_EXPORTER_PROMETHEUS_PORT = "OTEL_EXPORTER_PROMETHEUS_PORT" +""" +.. envvar:: OTEL_EXPORTER_PROMETHEUS_PORT + +The :envvar:`OTEL_EXPORTER_PROMETHEUS_PORT` environment variable configures the port used by +the Prometheus exporter. +Default: 9464 + +This is an experimental environment variable and the name of this variable and its behavior can +change in a non-backwards compatible way. +""" + +OTEL_PYTHON_TRACER_CONFIGURATOR = "OTEL_PYTHON_TRACER_CONFIGURATOR" +""" +.. envvar:: OTEL_PYTHON_TRACER_CONFIGURATOR + +The :envvar:`OTEL_PYTHON_TRACER_CONFIGURATOR` environment variable allows users to set a +custom Tracer Configurator function. +Default: opentelemetry.sdk.trace._default_tracer_configurator + +This is an experimental environment variable and the name of this variable and its behavior can +change in a non-backwards compatible way. +""" + +OTEL_PYTHON_METER_CONFIGURATOR = "OTEL_PYTHON_METER_CONFIGURATOR" +""" +.. envvar:: OTEL_PYTHON_METER_CONFIGURATOR + +The :envvar:`OTEL_PYTHON_METER_CONFIGURATOR` environment variable allows users to set a +custom Meter Configurator function. +Default: opentelemetry.sdk.metrics._internal._default_meter_configurator + +This is an experimental environment variable and the name of this variable and its behavior can +change in a non-backwards compatible way. +""" + +OTEL_PYTHON_LOGGER_CONFIGURATOR = "OTEL_PYTHON_LOGGER_CONFIGURATOR" +""" +.. envvar:: OTEL_PYTHON_LOGGER_CONFIGURATOR + +The :envvar:`OTEL_PYTHON_LOGGER_CONFIGURATOR` environment variable allows users to set a +custom Logger Configurator function. +Default: opentelemetry.sdk._logs._internal._default_logger_configurator + +This is an experimental environment variable and the name of this variable and its behavior can +change in a non-backwards compatible way. +""" + +OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED = "OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED" +""" +.. envvar:: OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED + +The :envvar:`OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED` environment variable enables +metrics emitted by the SDK about its own internal state. +Default: "false" +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/environment_variables/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/environment_variables/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d6276f8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/environment_variables/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/environment_variables/__pycache__/_internal.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/environment_variables/__pycache__/_internal.cpython-312.pyc new file mode 100644 index 0000000..f7da069 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/environment_variables/__pycache__/_internal.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/environment_variables/_internal.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/environment_variables/_internal.py new file mode 100644 index 0000000..0f0fa93 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/environment_variables/_internal.py @@ -0,0 +1,26 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from logging import getLogger +from os import environ + +_logger = getLogger(__name__) + + +def parse_boolean_environment_variable(environment_variable: str, default: bool = False) -> bool: + value = environ.get(environment_variable) + if value is None: + return default + + match value.strip().lower(): + case "true": + return True + case "false": + return False + case _: + _logger.warning( + "Invalid value for %s: %r. Expected 'true' or 'false'.", + environment_variable, + value, + ) + return default diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/error_handler/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/error_handler/__init__.py new file mode 100644 index 0000000..c98952c --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/error_handler/__init__.py @@ -0,0 +1,128 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +""" +Global Error Handler + +This module provides a global error handler and an interface that allows +error handlers to be registered with the global error handler via entry points. +A default error handler is also provided. + +To use this feature, users can create an error handler that is registered +using the ``opentelemetry_error_handler`` entry point. A class is to be +registered in this entry point, this class must inherit from the +``opentelemetry.sdk.error_handler.ErrorHandler`` class and implement the +corresponding ``handle`` method. This method will receive the exception object +that is to be handled. The error handler class should also inherit from the +exception classes it wants to handle. For example, this would be an error +handler that handles ``ZeroDivisionError``: + +.. code:: python + + from opentelemetry.sdk.error_handler import ErrorHandler + from logging import getLogger + + logger = getLogger(__name__) + + + class ErrorHandler0(ErrorHandler, ZeroDivisionError): + + def _handle(self, error: Exception, *args, **kwargs): + + logger.exception("ErrorHandler0 handling a ZeroDivisionError") + +To use the global error handler, just instantiate it as a context manager where +you want exceptions to be handled: + + +.. code:: python + + from opentelemetry.sdk.error_handler import GlobalErrorHandler + + with GlobalErrorHandler(): + 1 / 0 + +If the class of the exception raised in the scope of the ``GlobalErrorHandler`` +object is not parent of any registered error handler, then the default error +handler will handle the exception. This default error handler will only log the +exception to standard logging, the exception won't be raised any further. +""" + +from abc import ABC, abstractmethod +from logging import getLogger + +from opentelemetry.util._importlib_metadata import entry_points + +logger = getLogger(__name__) + + +class ErrorHandler(ABC): + @abstractmethod + def _handle(self, error: Exception, *args, **kwargs): + """ + Handle an exception + """ + + +class _DefaultErrorHandler(ErrorHandler): + """ + Default error handler + + This error handler just logs the exception using standard logging. + """ + + # pylint: disable=useless-return + def _handle(self, error: Exception, *args, **kwargs): + logger.exception("Error handled by default error handler: ") + + +class GlobalErrorHandler: + """ + Global error handler + + This is a singleton class that can be instantiated anywhere to get the + global error handler. This object provides a handle method that receives + an exception object that will be handled by the registered error handlers. + """ + + _instance = None + + def __new__(cls) -> "GlobalErrorHandler": + if cls._instance is None: + cls._instance = super().__new__(cls) + + return cls._instance + + def __enter__(self): + pass + + # pylint: disable=no-self-use + def __exit__(self, exc_type, exc_value, traceback): + if exc_value is None: + return None + + plugin_handled = False + + error_handler_entry_points = entry_points(group="opentelemetry_error_handler") + + for error_handler_entry_point in error_handler_entry_points: + error_handler_class = error_handler_entry_point.load() + + if issubclass(error_handler_class, exc_value.__class__): + try: + error_handler_class()._handle(exc_value) + plugin_handled = True + + # pylint: disable=broad-exception-caught + except Exception as error_handling_error: + logger.exception( + "%s error while handling error %s by error handler %s", + error_handling_error.__class__.__name__, + exc_value.__class__.__name__, + error_handler_class.__name__, + ) + + if not plugin_handled: + _DefaultErrorHandler()._handle(exc_value) + + return True diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/error_handler/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/error_handler/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..394e074 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/error_handler/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/__init__.py new file mode 100644 index 0000000..27a577f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/__init__.py @@ -0,0 +1,49 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from opentelemetry.sdk.metrics import export, view +from opentelemetry.sdk.metrics._internal import Meter, MeterProvider +from opentelemetry.sdk.metrics._internal.exceptions import MetricsTimeoutError +from opentelemetry.sdk.metrics._internal.exemplar import ( + AlignedHistogramBucketExemplarReservoir, + AlwaysOffExemplarFilter, + AlwaysOnExemplarFilter, + Exemplar, + ExemplarFilter, + ExemplarReservoir, + SimpleFixedSizeExemplarReservoir, + TraceBasedExemplarFilter, +) +from opentelemetry.sdk.metrics._internal.instrument import ( + Counter, + Histogram, + ObservableCounter, + ObservableGauge, + ObservableUpDownCounter, + UpDownCounter, +) +from opentelemetry.sdk.metrics._internal.instrument import Gauge as _Gauge + +__all__ = [ + "AlignedHistogramBucketExemplarReservoir", + "AlwaysOffExemplarFilter", + "AlwaysOnExemplarFilter", + "Counter", + "Exemplar", + "ExemplarFilter", + "ExemplarReservoir", + "Histogram", + "Meter", + "MeterProvider", + "MetricsTimeoutError", + "ObservableCounter", + "ObservableGauge", + "ObservableUpDownCounter", + "SimpleFixedSizeExemplarReservoir", + "TraceBasedExemplarFilter", + "UpDownCounter", + "_Gauge", + "export", + "view", +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..9ed9ee3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__init__.py new file mode 100644 index 0000000..2a43dc2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__init__.py @@ -0,0 +1,659 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import os +import weakref +from atexit import register, unregister +from collections.abc import Callable, Sequence +from dataclasses import dataclass +from logging import getLogger +from os import environ +from threading import Lock +from time import time_ns + +# This kind of import is needed to avoid Sphinx errors. +import opentelemetry.sdk.metrics +from opentelemetry.metrics import Counter as APICounter +from opentelemetry.metrics import Histogram as APIHistogram +from opentelemetry.metrics import Meter as APIMeter +from opentelemetry.metrics import MeterProvider as APIMeterProvider +from opentelemetry.metrics import NoOpMeter +from opentelemetry.metrics import ObservableCounter as APIObservableCounter +from opentelemetry.metrics import ObservableGauge as APIObservableGauge +from opentelemetry.metrics import ( + ObservableUpDownCounter as APIObservableUpDownCounter, +) +from opentelemetry.metrics import UpDownCounter as APIUpDownCounter +from opentelemetry.metrics import _Gauge as APIGauge +from opentelemetry.sdk.environment_variables import ( + OTEL_METRICS_EXEMPLAR_FILTER, + OTEL_SDK_DISABLED, +) +from opentelemetry.sdk.metrics._internal.exceptions import MetricsTimeoutError +from opentelemetry.sdk.metrics._internal.exemplar import ( + AlwaysOffExemplarFilter, + AlwaysOnExemplarFilter, + ExemplarFilter, + TraceBasedExemplarFilter, +) +from opentelemetry.sdk.metrics._internal.instrument import ( + _Counter, + _Gauge, + _Histogram, + _ObservableCounter, + _ObservableGauge, + _ObservableUpDownCounter, + _UpDownCounter, +) +from opentelemetry.sdk.metrics._internal.measurement_consumer import ( + MeasurementConsumer, + SynchronousMeasurementConsumer, +) +from opentelemetry.sdk.metrics._internal.sdk_configuration import ( + SdkConfiguration, +) +from opentelemetry.sdk.resources import ( + Resource, + _get_process_dependent_resource, +) +from opentelemetry.sdk.util._configurator import RuleBasedConfigurator +from opentelemetry.sdk.util.instrumentation import ( + InstrumentationScope, +) +from opentelemetry.util._once import Once +from opentelemetry.util.types import ( + Attributes, +) + +_logger = getLogger(__name__) + + +@dataclass +class _MeterConfig: + is_enabled: bool = True + + @classmethod + def default(cls) -> "_MeterConfig": + return _MeterConfig() + + +class _ProxyMeterConfig: + def __init__(self, config: _MeterConfig): + self._config = config + + @property + def is_enabled(self) -> bool: + return self._config.is_enabled + + def update(self, config: _MeterConfig) -> None: + self._config = config + + +class Meter(APIMeter): + """See `opentelemetry.metrics.Meter`.""" + + def __init__( + self, + instrumentation_scope: InstrumentationScope, + measurement_consumer: MeasurementConsumer, + *, + _meter_config: _MeterConfig | None = None, + ): + super().__init__( + name=instrumentation_scope.name, + version=instrumentation_scope.version, + schema_url=instrumentation_scope.schema_url, + ) + self._instrumentation_scope = instrumentation_scope + self._measurement_consumer = measurement_consumer + self._instrument_id_instrument = {} + self._instrument_registration_lock = Lock() + self._meter_config = _ProxyMeterConfig(_meter_config or _MeterConfig.default()) + + def _is_enabled(self) -> bool: + return self._meter_config.is_enabled + + def _set_meter_config(self, meter_config: _MeterConfig) -> None: + self._meter_config.update(meter_config) + + def create_counter(self, name, unit="", description="") -> APICounter: + with self._instrument_registration_lock: + status = self._register_instrument(name, _Counter, unit, description) + if not status.already_registered: + self._instrument_id_instrument[status.instrument_id] = _Counter( + name, + self._instrumentation_scope, + self._measurement_consumer, + unit, + description, + _meter_config=self._meter_config, + ) + instrument = self._instrument_id_instrument[status.instrument_id] + + if status.conflict: + # FIXME #2558 go through all views here and check if this + # instrument registration conflict can be fixed. If it can be, do + # not log the following warning. + self._log_instrument_registration_conflict( + name, + APICounter.__name__, + unit, + description, + status, + ) + return instrument + + def create_up_down_counter(self, name, unit="", description="") -> APIUpDownCounter: + with self._instrument_registration_lock: + status = self._register_instrument(name, _UpDownCounter, unit, description) + if not status.already_registered: + self._instrument_id_instrument[status.instrument_id] = _UpDownCounter( + name, + self._instrumentation_scope, + self._measurement_consumer, + unit, + description, + _meter_config=self._meter_config, + ) + instrument = self._instrument_id_instrument[status.instrument_id] + + if status.conflict: + # FIXME #2558 go through all views here and check if this + # instrument registration conflict can be fixed. If it can be, do + # not log the following warning. + self._log_instrument_registration_conflict( + name, + APIUpDownCounter.__name__, + unit, + description, + status, + ) + return instrument + + def create_observable_counter( + self, + name, + callbacks=None, + unit="", + description="", + ) -> APIObservableCounter: + with self._instrument_registration_lock: + status = self._register_instrument(name, _ObservableCounter, unit, description) + if not status.already_registered: + self._instrument_id_instrument[status.instrument_id] = _ObservableCounter( + name, + self._instrumentation_scope, + self._measurement_consumer, + callbacks, + unit, + description, + _meter_config=self._meter_config, + ) + instrument = self._instrument_id_instrument[status.instrument_id] + + if not status.already_registered: + self._measurement_consumer.register_asynchronous_instrument(instrument) + + if status.conflict: + # FIXME #2558 go through all views here and check if this + # instrument registration conflict can be fixed. If it can be, do + # not log the following warning. + self._log_instrument_registration_conflict( + name, + APIObservableCounter.__name__, + unit, + description, + status, + ) + return instrument + + def create_histogram( + self, + name: str, + unit: str = "", + description: str = "", + *, + explicit_bucket_boundaries_advisory: Sequence[float] | None = None, + ) -> APIHistogram: + if explicit_bucket_boundaries_advisory is not None: + invalid_advisory = False + if isinstance(explicit_bucket_boundaries_advisory, Sequence): + try: + invalid_advisory = not ( + all(isinstance(e, (float, int)) for e in explicit_bucket_boundaries_advisory) + ) + except (KeyError, TypeError): + invalid_advisory = True + else: + invalid_advisory = True + + if invalid_advisory: + explicit_bucket_boundaries_advisory = None + _logger.warning("explicit_bucket_boundaries_advisory must be a sequence of numbers") + + with self._instrument_registration_lock: + status = self._register_instrument( + name, + _Histogram, + unit, + description, + explicit_bucket_boundaries_advisory, + ) + if not status.already_registered: + self._instrument_id_instrument[status.instrument_id] = _Histogram( + name, + self._instrumentation_scope, + self._measurement_consumer, + unit, + description, + explicit_bucket_boundaries_advisory, + _meter_config=self._meter_config, + ) + instrument = self._instrument_id_instrument[status.instrument_id] + + if status.conflict: + # FIXME #2558 go through all views here and check if this + # instrument registration conflict can be fixed. If it can be, do + # not log the following warning. + self._log_instrument_registration_conflict( + name, + APIHistogram.__name__, + unit, + description, + status, + ) + return instrument + + def create_gauge(self, name, unit="", description="") -> APIGauge: + with self._instrument_registration_lock: + status = self._register_instrument(name, _Gauge, unit, description) + if not status.already_registered: + self._instrument_id_instrument[status.instrument_id] = _Gauge( + name, + self._instrumentation_scope, + self._measurement_consumer, + unit, + description, + _meter_config=self._meter_config, + ) + instrument = self._instrument_id_instrument[status.instrument_id] + + if status.conflict: + # FIXME #2558 go through all views here and check if this + # instrument registration conflict can be fixed. If it can be, do + # not log the following warning. + self._log_instrument_registration_conflict( + name, + APIGauge.__name__, + unit, + description, + status, + ) + return instrument + + def create_observable_gauge(self, name, callbacks=None, unit="", description="") -> APIObservableGauge: + with self._instrument_registration_lock: + status = self._register_instrument(name, _ObservableGauge, unit, description) + if not status.already_registered: + self._instrument_id_instrument[status.instrument_id] = _ObservableGauge( + name, + self._instrumentation_scope, + self._measurement_consumer, + callbacks, + unit, + description, + _meter_config=self._meter_config, + ) + instrument = self._instrument_id_instrument[status.instrument_id] + + if not status.already_registered: + self._measurement_consumer.register_asynchronous_instrument(instrument) + + if status.conflict: + # FIXME #2558 go through all views here and check if this + # instrument registration conflict can be fixed. If it can be, do + # not log the following warning. + self._log_instrument_registration_conflict( + name, + APIObservableGauge.__name__, + unit, + description, + status, + ) + return instrument + + def create_observable_up_down_counter( + self, name, callbacks=None, unit="", description="" + ) -> APIObservableUpDownCounter: + with self._instrument_registration_lock: + status = self._register_instrument(name, _ObservableUpDownCounter, unit, description) + if not status.already_registered: + self._instrument_id_instrument[status.instrument_id] = _ObservableUpDownCounter( + name, + self._instrumentation_scope, + self._measurement_consumer, + callbacks, + unit, + description, + _meter_config=self._meter_config, + ) + instrument = self._instrument_id_instrument[status.instrument_id] + + if not status.already_registered: + self._measurement_consumer.register_asynchronous_instrument(instrument) + + if status.conflict: + # FIXME #2558 go through all views here and check if this + # instrument registration conflict can be fixed. If it can be, do + # not log the following warning. + self._log_instrument_registration_conflict( + name, + APIObservableUpDownCounter.__name__, + unit, + description, + status, + ) + return instrument + + +def _get_exemplar_filter(exemplar_filter: str) -> ExemplarFilter: + if exemplar_filter == "trace_based": + return TraceBasedExemplarFilter() + if exemplar_filter == "always_on": + return AlwaysOnExemplarFilter() + if exemplar_filter == "always_off": + return AlwaysOffExemplarFilter() + msg = f"Unknown exemplar filter '{exemplar_filter}'." + raise ValueError(msg) + + +_MeterConfiguratorT = Callable[[InstrumentationScope], _MeterConfig] +_RuleBasedMeterConfigurator = RuleBasedConfigurator[_MeterConfig] + + +def _default_meter_configurator( + _meter_scope: InstrumentationScope, +) -> _MeterConfig: + return _MeterConfig.default() + + +def _disable_meter_configurator( + _meter_scope: InstrumentationScope, +) -> _MeterConfig: + return _MeterConfig(is_enabled=False) + + +class MeterProvider(APIMeterProvider): + r"""See `opentelemetry.metrics.MeterProvider`. + + Args: + metric_readers: Register metric readers to collect metrics from the SDK + on demand. Each :class:`opentelemetry.sdk.metrics.export.MetricReader` is + completely independent and will collect separate streams of + metrics. For push-based export, use + :class:`opentelemetry.sdk.metrics.export.PeriodicExportingMetricReader`. + resource: The resource representing what the metrics emitted from the SDK pertain to. + shutdown_on_exit: If true, registers an `atexit` handler to call + `MeterProvider.shutdown` + views: The views to configure the metric output the SDK + + .. code-block:: python + :caption: Push-based export with PeriodicExportingMetricReader + + from opentelemetry.sdk.metrics import MeterProvider + from opentelemetry.sdk.metrics.export import ( + ConsoleMetricExporter, + PeriodicExportingMetricReader, + ) + + reader = PeriodicExportingMetricReader(ConsoleMetricExporter()) + provider = MeterProvider(metric_readers=[reader]) + + By default, instruments which do not match any :class:`opentelemetry.sdk.metrics.view.View` (or if no :class:`opentelemetry.sdk.metrics.view.View`\ s + are provided) will report metrics with the default aggregation for the + instrument's kind. To disable instruments by default, configure a match-all + :class:`opentelemetry.sdk.metrics.view.View` with `DropAggregation` and then create :class:`opentelemetry.sdk.metrics.view.View`\ s to re-enable + individual instruments: + + .. code-block:: python + :caption: Disable default views + + MeterProvider( + views=[ + View(instrument_name="*", aggregation=DropAggregation()), + View(instrument_name="mycounter"), + ], + # ... + ) + """ + + _all_metric_readers_lock = Lock() + _all_metric_readers = weakref.WeakSet() + + def __init__( + self, + metric_readers: Sequence["opentelemetry.sdk.metrics.export.MetricReader"] = (), + resource: Resource | None = None, + exemplar_filter: ExemplarFilter | None = None, + shutdown_on_exit: bool = True, + views: Sequence["opentelemetry.sdk.metrics.view.View"] = (), + *, + _meter_configurator: _MeterConfiguratorT | None = None, + ): + self._lock = Lock() + self._meter_lock = Lock() + self._atexit_handler = None + if resource is None: + resource = Resource.create({}) + self._sdk_config = SdkConfiguration( + exemplar_filter=( + exemplar_filter or _get_exemplar_filter(environ.get(OTEL_METRICS_EXEMPLAR_FILTER, "trace_based")) + ), + resource=resource, + views=views, + ) + self._metric_readers = metric_readers + self._measurement_consumer = SynchronousMeasurementConsumer( + sdk_config=self._sdk_config, + metric_readers=metric_readers, + ) + disabled = environ.get(OTEL_SDK_DISABLED, "") + self._disabled = disabled.lower().strip() == "true" + + if shutdown_on_exit: + self._atexit_handler = register(self.shutdown) + + self._meters: dict[InstrumentationScope, Meter] = {} + self._shutdown_once = Once() + self._shutdown = False + self._meter_configurator = _meter_configurator or _default_meter_configurator + + for metric_reader in self._metric_readers: + with self._all_metric_readers_lock: + if metric_reader in self._all_metric_readers: + # pylint: disable=broad-exception-raised + raise Exception( + f"MetricReader {metric_reader} has been registered already in other MeterProvider instance" + ) + + self._all_metric_readers.add(metric_reader) + + metric_reader._set_collect_callback(self._measurement_consumer.collect) + metric_reader._set_meter_provider(self) + + if hasattr(os, "register_at_fork"): + weak_at_fork = weakref.WeakMethod(self._handle_fork) + + def _after_in_child() -> None: + if at_fork := weak_at_fork(): + at_fork() + + os.register_at_fork(after_in_child=_after_in_child) + + def _handle_fork(self) -> None: + self._lock = Lock() + self._meter_lock = Lock() + type(self)._all_metric_readers_lock = Lock() + self._update_resource(_get_process_dependent_resource()) + + def _set_meter_configurator(self, *, meter_configurator: _MeterConfiguratorT): + """Set a new MeterConfigurator for this MeterProvider. + + Setting a new MeterConfigurator will result in the configurator being called + for each outstanding Meter and for any newly created meters thereafter. + Therefore, it is important that the provided function returns quickly. + """ + with self._meter_lock: + self._meter_configurator = meter_configurator + for instrumentation_scope, meter in self._meters.items(): + # pylint: disable-next=protected-access + meter._set_meter_config(self._apply_meter_configurator(instrumentation_scope)) + + def _update_resource(self, resource: Resource) -> None: + with self._meter_lock: + self._sdk_config.resource = self._sdk_config.resource.merge(resource) + + def _apply_meter_configurator(self, instrumentation_scope: InstrumentationScope) -> _MeterConfig: + try: + return self._meter_configurator(instrumentation_scope) + # pylint: disable-next=broad-exception-caught + except Exception: + _logger.exception( + "meter configurator failed for scope '%s', using default config", + instrumentation_scope.name, + ) + return _MeterConfig.default() + + def force_flush(self, timeout_millis: float = 10_000) -> bool: + deadline_ns = time_ns() + timeout_millis * 10**6 + + metric_reader_error = {} + + for metric_reader in self._metric_readers: + current_ts = time_ns() + try: + if current_ts >= deadline_ns: + raise MetricsTimeoutError("Timed out while flushing metric readers") + metric_reader.force_flush(timeout_millis=(deadline_ns - current_ts) / 10**6) + + # pylint: disable=broad-exception-caught + except Exception as error: + metric_reader_error[metric_reader] = error + + if metric_reader_error: + metric_reader_error_string = "\n".join( + [ + f"{metric_reader.__class__.__name__}: {error!r}" + for metric_reader, error in metric_reader_error.items() + ] + ) + + # pylint: disable=broad-exception-raised + raise Exception( + "MeterProvider.force_flush failed because the following " + "metric readers failed during collect:\n" + f"{metric_reader_error_string}" + ) + return True + + def shutdown(self, timeout_millis: float = 30_000): + deadline_ns = time_ns() + timeout_millis * 10**6 + + def _shutdown(): + self._shutdown = True + + did_shutdown = self._shutdown_once.do_once(_shutdown) + + if not did_shutdown: + _logger.warning("shutdown can only be called once") + return + + metric_reader_error = {} + + for metric_reader in self._metric_readers: + current_ts = time_ns() + try: + if current_ts >= deadline_ns: + # pylint: disable=broad-exception-raised + raise Exception("Didn't get to execute, deadline already exceeded") + metric_reader.shutdown(timeout_millis=(deadline_ns - current_ts) / 10**6) + + # pylint: disable=broad-exception-caught + except Exception as error: + metric_reader_error[metric_reader] = error + + if self._atexit_handler is not None: + unregister(self._atexit_handler) + self._atexit_handler = None + + if metric_reader_error: + metric_reader_error_string = "\n".join( + [ + f"{metric_reader.__class__.__name__}: {error!r}" + for metric_reader, error in metric_reader_error.items() + ] + ) + + # pylint: disable=broad-exception-raised + raise Exception( + "MeterProvider.shutdown failed because the following " + "metric readers failed during shutdown:\n" + f"{metric_reader_error_string}" + ) + + def get_meter( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + attributes: Attributes = None, + ) -> APIMeter: + if self._disabled: + return NoOpMeter(name, version=version, schema_url=schema_url) + + if self._shutdown: + _logger.warning("A shutdown `MeterProvider` can not provide a `Meter`") + return NoOpMeter(name, version=version, schema_url=schema_url) + + if not name: + _logger.warning("Meter name cannot be None or empty.") + return NoOpMeter(name, version=version, schema_url=schema_url) + + instrumentation_scope = InstrumentationScope(name, version, schema_url, attributes) + with self._meter_lock: + if not self._meters.get(instrumentation_scope): + # FIXME #2558 pass SDKConfig object to meter so that the meter + # has access to views. + self._meters[instrumentation_scope] = Meter( + instrumentation_scope, + self._measurement_consumer, + _meter_config=self._apply_meter_configurator(instrumentation_scope), + ) + return self._meters[instrumentation_scope] + + def add_metric_reader(self, metric_reader: "opentelemetry.sdk.metrics.export.MetricReader") -> None: + with self._all_metric_readers_lock: + if metric_reader in self._all_metric_readers: + _logger.warning( + "MetricReader '%s' has been registered already!", + metric_reader, + ) + return + self._measurement_consumer.add_metric_reader(metric_reader) + # pylint: disable-next=protected-access + metric_reader._set_collect_callback(self._measurement_consumer.collect) + self._all_metric_readers.add(metric_reader) + + def remove_metric_reader( + self, + metric_reader: "opentelemetry.sdk.metrics.export.MetricReader", + ) -> None: + with self._all_metric_readers_lock: + if metric_reader not in self._all_metric_readers: + _logger.warning("MetricReader '%s' has not been registered!", metric_reader) + return + self._measurement_consumer.remove_metric_reader(metric_reader) + # pylint: disable-next=protected-access + metric_reader._set_collect_callback(None) + metric_reader.shutdown() + self._all_metric_readers.remove(metric_reader) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0ac57c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/_view_instrument_match.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/_view_instrument_match.cpython-312.pyc new file mode 100644 index 0000000..c58f806 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/_view_instrument_match.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/aggregation.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/aggregation.cpython-312.pyc new file mode 100644 index 0000000..9123bb0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/aggregation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..af28fea Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/instrument.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/instrument.cpython-312.pyc new file mode 100644 index 0000000..81334a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/instrument.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/measurement.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/measurement.cpython-312.pyc new file mode 100644 index 0000000..06228fb Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/measurement.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/measurement_consumer.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/measurement_consumer.cpython-312.pyc new file mode 100644 index 0000000..6d52fc4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/measurement_consumer.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/metric_reader_storage.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/metric_reader_storage.cpython-312.pyc new file mode 100644 index 0000000..bf8ef06 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/metric_reader_storage.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/point.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/point.cpython-312.pyc new file mode 100644 index 0000000..adfd39d Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/point.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/sdk_configuration.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/sdk_configuration.cpython-312.pyc new file mode 100644 index 0000000..42156b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/sdk_configuration.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/view.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/view.cpython-312.pyc new file mode 100644 index 0000000..09a130d Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/__pycache__/view.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py new file mode 100644 index 0000000..06a45f8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py @@ -0,0 +1,175 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from collections.abc import Mapping, Sequence +from logging import getLogger +from threading import Lock +from time import time_ns +from types import NoneType +from typing import TYPE_CHECKING, cast + +from typing_extensions import assert_never + +from opentelemetry.sdk.metrics._internal.aggregation import ( + Aggregation, + AggregationTemporality, + DefaultAggregation, + _Aggregation, + _SumAggregation, +) +from opentelemetry.sdk.metrics._internal.instrument import _Instrument +from opentelemetry.sdk.metrics._internal.measurement import Measurement +from opentelemetry.sdk.metrics._internal.point import DataPointT +from opentelemetry.sdk.metrics._internal.view import View +from opentelemetry.util.types import AnyValue, Attributes + +_logger = getLogger(__name__) + +# Every branch is tagged so that values Python considers equal but the OTel +# data model does not -- True/1/1.0, or a sequence of pairs and the mapping +# it resembles -- produce different aggregation keys. +_HashedAttributes = tuple[ + str, + str + | bool + | int + | float + | bytes + | None + | tuple["_HashedAttributes", ...] + | tuple[tuple[str, "_HashedAttributes"], ...], +] + + +# pylint: disable=inconsistent-return-statements +def _hash_attributes(value: Attributes | AnyValue) -> _HashedAttributes: + # Attributes have been cleaned and validated when Measurement was instantiated, + # so value is guaranteed to match one of the branches below at runtime. + if isinstance(value, (NoneType, str, int, float, bool, bytes)): + # bool is a subclass of int and 1 == 1.0, so the value alone is not + # enough to tell these apart. + return (type(value).__name__, value) + if isinstance(value, Sequence): + return ("sequence", tuple(_hash_attributes(v) for v in value)) + if isinstance(value, Mapping): + return ( + "mapping", + tuple( + (k, _hash_attributes(value[k])) + for k in sorted( + value, + key=lambda item: item if isinstance(item, str) else str(item), + ) + ), + ) + if TYPE_CHECKING: + assert_never(value) + + +class _ViewInstrumentMatch: + def __init__( + self, + view: View, + instrument: _Instrument, + instrument_class_aggregation: dict[type, Aggregation], + ): + self._view = view + self._instrument = instrument + self._attributes_aggregation: dict[_HashedAttributes, _Aggregation] = {} + self._lock = Lock() + self._instrument_class_aggregation = instrument_class_aggregation + self._name = self._view._name or self._instrument.name + self._description = self._view._description or self._instrument.description + if not isinstance(self._view._aggregation, DefaultAggregation): + self._aggregation = self._view._aggregation._create_aggregation( + self._instrument, + None, + self._view._exemplar_reservoir_factory, + 0, + ) + else: + self._aggregation = self._instrument_class_aggregation[self._instrument.__class__]._create_aggregation( + self._instrument, + None, + self._view._exemplar_reservoir_factory, + 0, + ) + + def conflicts(self, other: "_ViewInstrumentMatch") -> bool: + # pylint: disable=protected-access + + result = ( + self._name == other._name + and self._instrument.unit == other._instrument.unit + # The aggregation class is being used here instead of data point + # type since they are functionally equivalent. + and self._aggregation.__class__ == other._aggregation.__class__ + ) + if not result: + return result + + if isinstance(self._aggregation, _SumAggregation): + # if result is True the two aggregation are of the same type + self._aggregation = cast(_SumAggregation, self._aggregation) + other._aggregation = cast(_SumAggregation, other._aggregation) + + result = ( + self._aggregation._instrument_is_monotonic == other._aggregation._instrument_is_monotonic + and self._aggregation._instrument_aggregation_temporality + == other._aggregation._instrument_aggregation_temporality + ) + + return result + + # pylint: disable=protected-access + def consume_measurement(self, measurement: Measurement, should_sample_exemplar: bool = True) -> None: + attributes = {} + if measurement.attributes: + # Make a shallow copy since the user can mutate the dict after the fact. + # The user can still modify mutable attribute values (lists/dicts) + # leading to unexpected behavior, but deep copying is expensive. + attributes = dict(measurement.attributes) + if self._view._attribute_keys is not None: + attributes = {k: v for k, v in attributes.items() if k in self._view._attribute_keys} + + aggr_key = _hash_attributes(attributes) + + if aggr_key not in self._attributes_aggregation: + with self._lock: + if aggr_key not in self._attributes_aggregation: + if not isinstance(self._view._aggregation, DefaultAggregation): + aggregation = self._view._aggregation._create_aggregation( + self._instrument, + attributes, + self._view._exemplar_reservoir_factory, + time_ns(), + ) + else: + aggregation = self._instrument_class_aggregation[ + self._instrument.__class__ + ]._create_aggregation( + self._instrument, + attributes, + self._view._exemplar_reservoir_factory, + time_ns(), + ) + self._attributes_aggregation[aggr_key] = aggregation + + self._attributes_aggregation[aggr_key].aggregate(measurement, should_sample_exemplar) + + def collect( + self, + collection_aggregation_temporality: AggregationTemporality, + collection_start_nanos: int, + ) -> Sequence[DataPointT] | None: + data_points: list[DataPointT] = [] + with self._lock: + for aggregation in self._attributes_aggregation.values(): + data_point = aggregation.collect(collection_aggregation_temporality, collection_start_nanos) + if data_point is not None: + data_points.append(data_point) + + # Returning here None instead of an empty list because the caller + # does not consume a sequence and to be consistent with the rest of + # collect methods that also return None. + return data_points or None diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/aggregation.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/aggregation.py new file mode 100644 index 0000000..ac09219 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/aggregation.py @@ -0,0 +1,1354 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# pylint: disable=too-many-lines + +import math +from abc import ABC, abstractmethod +from bisect import bisect_left +from collections.abc import Callable, Sequence +from enum import IntEnum +from functools import partial +from logging import getLogger +from threading import Lock +from typing import ( + Generic, + TypeVar, + cast, +) + +from opentelemetry.metrics import ( + Asynchronous, + Counter, + Histogram, + ObservableCounter, + ObservableGauge, + ObservableUpDownCounter, + Synchronous, + UpDownCounter, + _Gauge, +) +from opentelemetry.sdk.metrics._internal.exemplar import ( + Exemplar, + ExemplarReservoirBuilder, +) +from opentelemetry.sdk.metrics._internal.exponential_histogram.buckets import ( + Buckets, +) +from opentelemetry.sdk.metrics._internal.exponential_histogram.mapping import ( + Mapping, +) +from opentelemetry.sdk.metrics._internal.exponential_histogram.mapping.exponent_mapping import ( + ExponentMapping, +) +from opentelemetry.sdk.metrics._internal.exponential_histogram.mapping.logarithm_mapping import ( + LogarithmMapping, +) +from opentelemetry.sdk.metrics._internal.instrument import _Instrument +from opentelemetry.sdk.metrics._internal.measurement import Measurement +from opentelemetry.sdk.metrics._internal.point import Buckets as BucketsPoint +from opentelemetry.sdk.metrics._internal.point import ( + ExponentialHistogramDataPoint, + HistogramDataPoint, + NumberDataPoint, + Sum, +) +from opentelemetry.sdk.metrics._internal.point import Gauge as GaugePoint +from opentelemetry.sdk.metrics._internal.point import ( + Histogram as HistogramPoint, +) +from opentelemetry.util.types import Attributes + +_DataPointVarT = TypeVar("_DataPointVarT", NumberDataPoint, HistogramDataPoint) + +_logger = getLogger(__name__) + + +class AggregationTemporality(IntEnum): + """ + The temporality to use when aggregating data. + + Can be one of the following values: + """ + + UNSPECIFIED = 0 + DELTA = 1 + CUMULATIVE = 2 + + +class _Aggregation(ABC, Generic[_DataPointVarT]): + def __init__( + self, + attributes: Attributes, + reservoir_builder: ExemplarReservoirBuilder, + ): + self._lock = Lock() + self._attributes = attributes + self._reservoir = reservoir_builder() + self._previous_point = None + + @abstractmethod + def aggregate(self, measurement: Measurement, should_sample_exemplar: bool = True) -> None: + """Aggregate a measurement. + + Args: + measurement: Measurement to aggregate + should_sample_exemplar: Whether the measurement should be sampled by the exemplars reservoir or not. + """ + + @abstractmethod + def collect( + self, + collection_aggregation_temporality: AggregationTemporality, + collection_start_nano: int, + ) -> _DataPointVarT | None: + pass + + def _collect_exemplars(self) -> Sequence[Exemplar]: + """Returns the collected exemplars. + + Returns: + The exemplars collected by the reservoir + """ + return self._reservoir.collect(self._attributes) + + def _sample_exemplar(self, measurement: Measurement, should_sample_exemplar: bool) -> None: + """Offer the measurement to the exemplar reservoir for sampling. + + It should be called within the each :ref:`aggregate` call. + + Args: + measurement: The new measurement + should_sample_exemplar: Whether the measurement should be sampled by the exemplars reservoir or not. + """ + if should_sample_exemplar: + self._reservoir.offer( + measurement.value, + measurement.time_unix_nano, + measurement.attributes, + measurement.context, + ) + + +class _DropAggregation(_Aggregation): + def aggregate(self, measurement: Measurement, should_sample_exemplar: bool = True) -> None: + pass + + def collect( + self, + collection_aggregation_temporality: AggregationTemporality, + collection_start_nano: int, + ) -> _DataPointVarT | None: + pass + + +class _SumAggregation(_Aggregation[Sum]): + def __init__( + self, + attributes: Attributes, + instrument_is_monotonic: bool, + instrument_aggregation_temporality: AggregationTemporality, + start_time_unix_nano: int, + reservoir_builder: ExemplarReservoirBuilder, + ): + super().__init__(attributes, reservoir_builder) + + self._start_time_unix_nano = start_time_unix_nano + self._instrument_aggregation_temporality = instrument_aggregation_temporality + self._instrument_is_monotonic = instrument_is_monotonic + + self._value = None + + self._previous_collection_start_nano = self._start_time_unix_nano + self._previous_value = 0 + + def aggregate(self, measurement: Measurement, should_sample_exemplar: bool = True) -> None: + with self._lock: + if self._value is None: + self._value = 0 + + self._value = self._value + measurement.value + + self._sample_exemplar(measurement, should_sample_exemplar) + + def collect( + self, + collection_aggregation_temporality: AggregationTemporality, + collection_start_nano: int, + ) -> NumberDataPoint | None: + """ + Atomically return a point for the current value of the metric and + reset the aggregation value. + + Synchronous instruments have a method which is called directly with + increments for a given quantity: + + For example, an instrument that counts the amount of passengers in + every vehicle that crosses a certain point in a highway: + + synchronous_instrument.add(2) + collect(...) # 2 passengers are counted + synchronous_instrument.add(3) + collect(...) # 3 passengers are counted + synchronous_instrument.add(1) + collect(...) # 1 passenger is counted + + In this case the instrument aggregation temporality is DELTA because + every value represents an increment to the count, + + Asynchronous instruments have a callback which returns the total value + of a given quantity: + + For example, an instrument that measures the amount of bytes written to + a certain hard drive: + + callback() -> 1352 + collect(...) # 1352 bytes have been written so far + callback() -> 2324 + collect(...) # 2324 bytes have been written so far + callback() -> 4542 + collect(...) # 4542 bytes have been written so far + + In this case the instrument aggregation temporality is CUMULATIVE + because every value represents the total of the measurement. + + There is also the collection aggregation temporality, which is passed + to this method. The collection aggregation temporality defines the + nature of the returned value by this aggregation. + + When the collection aggregation temporality matches the + instrument aggregation temporality, then this method returns the + current value directly: + + synchronous_instrument.add(2) + collect(DELTA) -> 2 + synchronous_instrument.add(3) + collect(DELTA) -> 3 + synchronous_instrument.add(1) + collect(DELTA) -> 1 + + callback() -> 1352 + collect(CUMULATIVE) -> 1352 + callback() -> 2324 + collect(CUMULATIVE) -> 2324 + callback() -> 4542 + collect(CUMULATIVE) -> 4542 + + When the collection aggregation temporality does not match the + instrument aggregation temporality, then a conversion is made. For this + purpose, this aggregation keeps a private attribute, + self._previous_value. + + When the instrument is synchronous: + + self._previous_value is the sum of every previously + collected (delta) value. In this case, the returned (cumulative) value + will be: + + self._previous_value + value + + synchronous_instrument.add(2) + collect(CUMULATIVE) -> 2 + synchronous_instrument.add(3) + collect(CUMULATIVE) -> 5 + synchronous_instrument.add(1) + collect(CUMULATIVE) -> 6 + + Also, as a diagram: + + time -> + + self._previous_value + |-------------| + + value (delta) + |----| + + returned value (cumulative) + |------------------| + + When the instrument is asynchronous: + + self._previous_value is the value of the previously + collected (cumulative) value. In this case, the returned (delta) value + will be: + + value - self._previous_value + + callback() -> 1352 + collect(DELTA) -> 1352 + callback() -> 2324 + collect(DELTA) -> 972 + callback() -> 4542 + collect(DELTA) -> 2218 + + Also, as a diagram: + + time -> + + self._previous_value + |-------------| + + value (cumulative) + |------------------| + + returned value (delta) + |----| + """ + + with self._lock: + value = self._value + self._value = None + + if self._instrument_aggregation_temporality is AggregationTemporality.DELTA: + # This happens when the corresponding instrument for this + # aggregation is synchronous. + if collection_aggregation_temporality is AggregationTemporality.DELTA: + previous_collection_start_nano = self._previous_collection_start_nano + self._previous_collection_start_nano = collection_start_nano + + if value is None: + return None + + return NumberDataPoint( + attributes=self._attributes, + exemplars=self._collect_exemplars(), + start_time_unix_nano=previous_collection_start_nano, + time_unix_nano=collection_start_nano, + value=value, + ) + + if value is None: + value = 0 + + self._previous_value = value + self._previous_value + + return NumberDataPoint( + attributes=self._attributes, + exemplars=self._collect_exemplars(), + start_time_unix_nano=self._start_time_unix_nano, + time_unix_nano=collection_start_nano, + value=self._previous_value, + ) + + # This happens when the corresponding instrument for this + # aggregation is asynchronous. + + if value is None: + # This happens when the corresponding instrument callback + # does not produce measurements. + return None + + if collection_aggregation_temporality is AggregationTemporality.DELTA: + result_value = value - self._previous_value + + self._previous_value = value + + previous_collection_start_nano = self._previous_collection_start_nano + self._previous_collection_start_nano = collection_start_nano + + return NumberDataPoint( + attributes=self._attributes, + exemplars=self._collect_exemplars(), + start_time_unix_nano=previous_collection_start_nano, + time_unix_nano=collection_start_nano, + value=result_value, + ) + + return NumberDataPoint( + attributes=self._attributes, + exemplars=self._collect_exemplars(), + start_time_unix_nano=self._start_time_unix_nano, + time_unix_nano=collection_start_nano, + value=value, + ) + + +class _LastValueAggregation(_Aggregation[GaugePoint]): + def __init__( + self, + attributes: Attributes, + reservoir_builder: ExemplarReservoirBuilder, + instrument_is_synchronous: bool, + ): + super().__init__(attributes, reservoir_builder) + self._instrument_is_synchronous = instrument_is_synchronous + self._value = None + + def aggregate(self, measurement: Measurement, should_sample_exemplar: bool = True): + with self._lock: + self._value = measurement.value + + self._sample_exemplar(measurement, should_sample_exemplar) + + def collect( + self, + collection_aggregation_temporality: AggregationTemporality, + collection_start_nano: int, + ) -> _DataPointVarT | None: + """ + Atomically return a point for the current value of the metric. + """ + with self._lock: + if self._value is None: + return None + value = self._value + if not ( + self._instrument_is_synchronous + and collection_aggregation_temporality is AggregationTemporality.CUMULATIVE + ): + self._value = None + + exemplars = self._collect_exemplars() + + return NumberDataPoint( + attributes=self._attributes, + exemplars=exemplars, + start_time_unix_nano=None, + time_unix_nano=collection_start_nano, + value=value, + ) + + +_DEFAULT_EXPLICIT_BUCKET_HISTOGRAM_AGGREGATION_BOUNDARIES: Sequence[float] = ( + 0.0, + 5.0, + 10.0, + 25.0, + 50.0, + 75.0, + 100.0, + 250.0, + 500.0, + 750.0, + 1000.0, + 2500.0, + 5000.0, + 7500.0, + 10000.0, +) + + +class _ExplicitBucketHistogramAggregation(_Aggregation[HistogramPoint]): + def __init__( + self, + attributes: Attributes, + instrument_aggregation_temporality: AggregationTemporality, + start_time_unix_nano: int, + reservoir_builder: ExemplarReservoirBuilder, + boundaries: Sequence[float] | None = None, + record_min_max: bool = True, + ): + if boundaries is None: + boundaries = _DEFAULT_EXPLICIT_BUCKET_HISTOGRAM_AGGREGATION_BOUNDARIES + if boundaries: + for idx, bound in enumerate(boundaries): + if not math.isfinite(bound): + raise ValueError(f"invalid boundary: {bound!r}") + if idx and boundaries[idx - 1] >= bound: + raise ValueError("boundaries must be strictly increasing") + super().__init__( + attributes, + reservoir_builder=partial(reservoir_builder, boundaries=boundaries), + ) + + self._instrument_aggregation_temporality = instrument_aggregation_temporality + self._start_time_unix_nano = start_time_unix_nano + self._boundaries = tuple(boundaries) + self._record_min_max = record_min_max + + self._value = None + self._min = math.inf + self._max = -math.inf + self._sum = 0 + + self._previous_value = None + self._previous_min = math.inf + self._previous_max = -math.inf + self._previous_sum = 0 + + self._previous_collection_start_nano = self._start_time_unix_nano + + def _get_empty_bucket_counts(self) -> list[int]: + return [0] * (len(self._boundaries) + 1) + + def aggregate(self, measurement: Measurement, should_sample_exemplar: bool = True) -> None: + with self._lock: + if self._value is None: + self._value = self._get_empty_bucket_counts() + + measurement_value = measurement.value + + self._sum += measurement_value + + if self._record_min_max: + self._min = min(self._min, measurement_value) + self._max = max(self._max, measurement_value) + + self._value[bisect_left(self._boundaries, measurement_value)] += 1 + + self._sample_exemplar(measurement, should_sample_exemplar) + + def collect( + self, + collection_aggregation_temporality: AggregationTemporality, + collection_start_nano: int, + ) -> _DataPointVarT | None: + """ + Atomically return a point for the current value of the metric. + """ + + with self._lock: + value = self._value + sum_ = self._sum + min_ = self._min + max_ = self._max + + self._value = None + self._sum = 0 + self._min = math.inf + self._max = -math.inf + + if self._instrument_aggregation_temporality is AggregationTemporality.DELTA: + # This happens when the corresponding instrument for this + # aggregation is synchronous. + if collection_aggregation_temporality is AggregationTemporality.DELTA: + previous_collection_start_nano = self._previous_collection_start_nano + self._previous_collection_start_nano = collection_start_nano + + if value is None: + return None + + return HistogramDataPoint( + attributes=self._attributes, + exemplars=self._collect_exemplars(), + start_time_unix_nano=previous_collection_start_nano, + time_unix_nano=collection_start_nano, + count=sum(value), + sum=sum_, + bucket_counts=tuple(value), + explicit_bounds=self._boundaries, + min=min_, + max=max_, + ) + + if value is None: + value = self._get_empty_bucket_counts() + + if self._previous_value is None: + self._previous_value = self._get_empty_bucket_counts() + + self._previous_value = [ + value_element + previous_value_element + for ( + value_element, + previous_value_element, + ) in zip(value, self._previous_value) + ] + self._previous_min = min(min_, self._previous_min) + self._previous_max = max(max_, self._previous_max) + self._previous_sum = sum_ + self._previous_sum + + return HistogramDataPoint( + attributes=self._attributes, + exemplars=self._collect_exemplars(), + start_time_unix_nano=self._start_time_unix_nano, + time_unix_nano=collection_start_nano, + count=sum(self._previous_value), + sum=self._previous_sum, + bucket_counts=tuple(self._previous_value), + explicit_bounds=self._boundaries, + min=self._previous_min, + max=self._previous_max, + ) + + return None + + +# pylint: disable=protected-access +class _ExponentialBucketHistogramAggregation(_Aggregation[HistogramPoint]): + # _min_max_size and _max_max_size are the smallest and largest values + # the max_size parameter may have, respectively. + + # _min_max_size is is the smallest reasonable value which is small enough + # to contain the entire normal floating point range at the minimum scale. + _min_max_size = 2 + + # _max_max_size is an arbitrary limit meant to limit accidental creation of + # giant exponential bucket histograms. + _max_max_size = 16384 + + def __init__( + self, + attributes: Attributes, + reservoir_builder: ExemplarReservoirBuilder, + instrument_aggregation_temporality: AggregationTemporality, + start_time_unix_nano: int, + # This is the default maximum number of buckets per positive or + # negative number range. The value 160 is specified by OpenTelemetry. + # See the derivation here: + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#exponential-bucket-histogram-aggregation) + max_size: int = 160, + max_scale: int = 20, + record_min_max: bool = True, + ): + # max_size is the maximum capacity of the positive and negative + # buckets. + # _sum is the sum of all the values aggregated by this aggregator. + # _count is the count of all calls to aggregate. + # _zero_count is the count of all the calls to aggregate when the value + # to be aggregated is exactly 0. + # _min is the smallest value aggregated by this aggregator. + # _max is the smallest value aggregated by this aggregator. + # _positive holds the positive values. + # _negative holds the negative values by their absolute value. + if max_size < self._min_max_size: + raise ValueError(f"Buckets max size {max_size} is smaller than minimum max size {self._min_max_size}") + + if max_size > self._max_max_size: + raise ValueError(f"Buckets max size {max_size} is larger than maximum max size {self._max_max_size}") + if max_scale > 20: + _logger.warning( + "max_scale is set to %s which is larger than the recommended value of 20", + max_scale, + ) + + # This aggregation is analogous to _ExplicitBucketHistogramAggregation, + # the only difference is that with every call to aggregate, the size + # and amount of buckets can change (in + # _ExplicitBucketHistogramAggregation both size and amount of buckets + # remain constant once it is instantiated). + + super().__init__( + attributes, + reservoir_builder=partial(reservoir_builder, size=min(20, max_size)), + ) + + self._instrument_aggregation_temporality = instrument_aggregation_temporality + self._start_time_unix_nano = start_time_unix_nano + self._max_size = max_size + self._max_scale = max_scale + self._record_min_max = record_min_max + + self._value_positive = None + self._value_negative = None + self._min = math.inf + self._max = -math.inf + self._sum = 0 + self._count = 0 + self._zero_count = 0 + self._scale = None + + self._previous_value_positive = None + self._previous_value_negative = None + self._previous_min = math.inf + self._previous_max = -math.inf + self._previous_sum = 0 + self._previous_count = 0 + self._previous_zero_count = 0 + self._previous_scale = None + + self._previous_collection_start_nano = self._start_time_unix_nano + + self._mapping = self._new_mapping(self._max_scale) + + def aggregate(self, measurement: Measurement, should_sample_exemplar: bool = True) -> None: + # pylint: disable=too-many-branches,too-many-statements, too-many-locals + + with self._lock: + if self._value_positive is None: + self._value_positive = Buckets() + if self._value_negative is None: + self._value_negative = Buckets() + + measurement_value = measurement.value + + self._sum += measurement_value + + if self._record_min_max: + self._min = min(self._min, measurement_value) + self._max = max(self._max, measurement_value) + + self._count += 1 + + if measurement_value == 0: + self._zero_count += 1 + + if self._count == self._zero_count: + self._scale = 0 + + return + + if measurement_value > 0: + value = self._value_positive + + else: + measurement_value = -measurement_value + value = self._value_negative + + # The following code finds out if it is necessary to change the + # buckets to hold the incoming measurement_value, changes them if + # necessary. This process does not exist in + # _ExplicitBucketHistogram aggregation because the buckets there + # are constant in size and amount. + index = self._mapping.map_to_index(measurement_value) + + is_rescaling_needed = False + low, high = 0, 0 + + if len(value) == 0: + value.index_start = index + value.index_end = index + value.index_base = index + + elif index < value.index_start and (value.index_end - index) >= self._max_size: + is_rescaling_needed = True + low = index + high = value.index_end + + elif index > value.index_end and (index - value.index_start) >= self._max_size: + is_rescaling_needed = True + low = value.index_start + high = index + + if is_rescaling_needed: + scale_change = self._get_scale_change(low, high) + self._downscale( + scale_change, + self._value_positive, + self._value_negative, + ) + self._mapping = self._new_mapping(self._mapping.scale - scale_change) + + index = self._mapping.map_to_index(measurement_value) + + self._scale = self._mapping.scale + + if index < value.index_start: + span = value.index_end - index + + if span >= len(value.counts): + value.grow(span + 1, self._max_size) + + value.index_start = index + + elif index > value.index_end: + span = index - value.index_start + + if span >= len(value.counts): + value.grow(span + 1, self._max_size) + + value.index_end = index + + bucket_index = index - value.index_base + + if bucket_index < 0: + bucket_index += len(value.counts) + + # Now the buckets have been changed if needed and bucket_index will + # be used to increment the counter of the bucket that needs to be + # incremented. + + # This is analogous to + # self._value[bisect_left(self._boundaries, measurement_value)] += 1 + # in _ExplicitBucketHistogramAggregation.aggregate + value.increment_bucket(bucket_index) + + self._sample_exemplar(measurement, should_sample_exemplar) + + def collect( + self, + collection_aggregation_temporality: AggregationTemporality, + collection_start_nano: int, + ) -> _DataPointVarT | None: + """ + Atomically return a point for the current value of the metric. + """ + + # pylint: disable=too-many-statements, too-many-locals + with self._lock: + value_positive = self._value_positive + value_negative = self._value_negative + sum_ = self._sum + min_ = self._min + max_ = self._max + count = self._count + zero_count = self._zero_count + scale = self._scale + + self._value_positive = None + self._value_negative = None + self._sum = 0 + self._min = math.inf + self._max = -math.inf + self._count = 0 + self._zero_count = 0 + self._scale = None + + if self._instrument_aggregation_temporality is AggregationTemporality.DELTA: + # This happens when the corresponding instrument for this + # aggregation is synchronous. + if collection_aggregation_temporality is AggregationTemporality.DELTA: + previous_collection_start_nano = self._previous_collection_start_nano + self._previous_collection_start_nano = collection_start_nano + + if value_positive is None and value_negative is None: + return None + + return ExponentialHistogramDataPoint( + attributes=self._attributes, + exemplars=self._collect_exemplars(), + start_time_unix_nano=previous_collection_start_nano, + time_unix_nano=collection_start_nano, + count=count, + sum=sum_, + scale=scale, + zero_count=zero_count, + positive=BucketsPoint( + offset=value_positive.offset, + bucket_counts=(value_positive.get_offset_counts()), + ), + negative=BucketsPoint( + offset=value_negative.offset, + bucket_counts=(value_negative.get_offset_counts()), + ), + # FIXME: Find the right value for flags + flags=0, + min=min_, + max=max_, + ) + + # Here collection_temporality is CUMULATIVE. + # instrument_temporality is always DELTA for the time being. + # Here we need to handle the case where: + # collect is called after at least one other call to collect + # (there is data in previous buckets, a call to merge is needed + # to handle possible differences in bucket sizes). + # collect is called without another call previous call to + # collect was made (there is no previous buckets, previous, + # empty buckets that are the same scale of the current buckets + # need to be made so that they can be cumulatively aggregated + # to the current buckets). + + if value_positive is None and self._previous_value_positive is None: + # This happens if collect is called for the first time + # and aggregate has not yet been called. + value_positive = Buckets() + self._previous_value_positive = value_positive.copy_empty() + if value_negative is None and self._previous_value_negative is None: + value_negative = Buckets() + self._previous_value_negative = value_negative.copy_empty() + if scale is None and self._previous_scale is None: + scale = self._mapping.scale + self._previous_scale = scale + + if value_positive is not None and self._previous_value_positive is None: + # This happens when collect is called the very first time + # and aggregate has been called before. + + # We need previous buckets to add them to the current ones. + # When collect is called for the first time, there are no + # previous buckets, so we need to create empty buckets to + # add them to the current ones. The addition of empty + # buckets to the current ones will result in the current + # ones unchanged. + + # The way the previous buckets are generated here is + # different from the explicit bucket histogram where + # the size and amount of the buckets does not change once + # they are instantiated. Here, the size and amount of the + # buckets can change with every call to aggregate. In order + # to get empty buckets that can be added to the current + # ones resulting in the current ones unchanged we need to + # generate empty buckets that have the same size and amount + # as the current ones, this is what copy_empty does. + self._previous_value_positive = value_positive.copy_empty() + if value_negative is not None and self._previous_value_negative is None: + self._previous_value_negative = value_negative.copy_empty() + if scale is not None and self._previous_scale is None: + self._previous_scale = scale + + if value_positive is None and self._previous_value_positive is not None: + value_positive = self._previous_value_positive.copy_empty() + if value_negative is None and self._previous_value_negative is not None: + value_negative = self._previous_value_negative.copy_empty() + if scale is None and self._previous_scale is not None: + scale = self._previous_scale + + # here self._previous_value_negative and self._previous_value_positive are not Optional anymore + self._previous_value_negative = cast(Buckets, self._previous_value_negative) + self._previous_value_positive = cast(Buckets, self._previous_value_positive) + + min_scale = min(self._previous_scale, scale) + + low_positive, high_positive = self._get_low_high_previous_current( + self._previous_value_positive, + value_positive, + scale, + min_scale, + ) + low_negative, high_negative = self._get_low_high_previous_current( + self._previous_value_negative, + value_negative, + scale, + min_scale, + ) + + min_scale = min( + min_scale - self._get_scale_change(low_positive, high_positive), + min_scale - self._get_scale_change(low_negative, high_negative), + ) + + self._downscale( + self._previous_scale - min_scale, + self._previous_value_positive, + self._previous_value_negative, + ) + + # self._merge adds the values from value to + # self._previous_value, this is analogous to + # self._previous_value = [ + # value_element + previous_value_element + # for ( + # value_element, + # previous_value_element, + # ) in zip(value, self._previous_value) + # ] + # in _ExplicitBucketHistogramAggregation.collect. + self._merge( + self._previous_value_positive, + value_positive, + scale, + min_scale, + collection_aggregation_temporality, + ) + self._merge( + self._previous_value_negative, + value_negative, + scale, + min_scale, + collection_aggregation_temporality, + ) + + self._previous_min = min(min_, self._previous_min) + self._previous_max = max(max_, self._previous_max) + self._previous_sum = sum_ + self._previous_sum + self._previous_count = count + self._previous_count + self._previous_zero_count = zero_count + self._previous_zero_count + self._previous_scale = min_scale + + return ExponentialHistogramDataPoint( + attributes=self._attributes, + exemplars=self._collect_exemplars(), + start_time_unix_nano=self._start_time_unix_nano, + time_unix_nano=collection_start_nano, + count=self._previous_count, + sum=self._previous_sum, + scale=self._previous_scale, + zero_count=self._previous_zero_count, + positive=BucketsPoint( + offset=self._previous_value_positive.offset, + bucket_counts=(self._previous_value_positive.get_offset_counts()), + ), + negative=BucketsPoint( + offset=self._previous_value_negative.offset, + bucket_counts=(self._previous_value_negative.get_offset_counts()), + ), + # FIXME: Find the right value for flags + flags=0, + min=self._previous_min, + max=self._previous_max, + ) + + return None + + def _get_low_high_previous_current( + self, + previous_point_buckets, + current_point_buckets, + current_scale, + min_scale, + ): + (previous_point_low, previous_point_high) = self._get_low_high( + previous_point_buckets, self._previous_scale, min_scale + ) + (current_point_low, current_point_high) = self._get_low_high(current_point_buckets, current_scale, min_scale) + + if current_point_low > current_point_high: + low = previous_point_low + high = previous_point_high + + elif previous_point_low > previous_point_high: + low = current_point_low + high = current_point_high + + else: + low = min(previous_point_low, current_point_low) + high = max(previous_point_high, current_point_high) + + return low, high + + @staticmethod + def _get_low_high(buckets, scale, min_scale): + if buckets.counts == [0]: + return 0, -1 + + shift = scale - min_scale + + return buckets.index_start >> shift, buckets.index_end >> shift + + @staticmethod + def _new_mapping(scale: int) -> Mapping: + if scale <= 0: + return ExponentMapping(scale) + return LogarithmMapping(scale) + + def _get_scale_change(self, low, high): + change = 0 + + while high - low >= self._max_size: + high = high >> 1 + low = low >> 1 + + change += 1 + + return change + + @staticmethod + def _downscale(change: int, positive, negative): + if change == 0: + return + + if change < 0: + # pylint: disable=broad-exception-raised + raise Exception("Invalid change of scale") + + positive.downscale(change) + negative.downscale(change) + + def _merge( + self, + previous_buckets: Buckets, + current_buckets: Buckets, + current_scale, + min_scale, + aggregation_temporality, + ): + current_change = current_scale - min_scale + + for current_bucket_index, current_bucket in enumerate(current_buckets.counts): + if current_bucket == 0: + continue + + # Not considering the case where len(previous_buckets) == 0. This + # would not happen because self._previous_point is only assigned to + # an ExponentialHistogramDataPoint object if self._count != 0. + + current_index = current_buckets.index_base + current_bucket_index + if current_index > current_buckets.index_end: + current_index -= len(current_buckets.counts) + + index = current_index >> current_change + + if index < previous_buckets.index_start: + span = previous_buckets.index_end - index + + if span >= self._max_size: + # pylint: disable=broad-exception-raised + raise Exception("Incorrect merge scale") + + if span >= len(previous_buckets.counts): + previous_buckets.grow(span + 1, self._max_size) + + previous_buckets.index_start = index + + if index > previous_buckets.index_end: + span = index - previous_buckets.index_start + + if span >= self._max_size: + # pylint: disable=broad-exception-raised + raise Exception("Incorrect merge scale") + + if span >= len(previous_buckets.counts): + previous_buckets.grow(span + 1, self._max_size) + + previous_buckets.index_end = index + + bucket_index = index - previous_buckets.index_base + + if bucket_index < 0: + bucket_index += len(previous_buckets.counts) + + if aggregation_temporality is AggregationTemporality.DELTA: + current_bucket = -current_bucket + + previous_buckets.increment_bucket(bucket_index, increment=current_bucket) + + +class Aggregation(ABC): + """ + Base class for all aggregation types. + """ + + @abstractmethod + def _create_aggregation( + self, + instrument: _Instrument, + attributes: Attributes, + reservoir_factory: Callable[[type[_Aggregation]], ExemplarReservoirBuilder], + start_time_unix_nano: int, + ) -> _Aggregation: + """Creates an aggregation""" + + +class DefaultAggregation(Aggregation): + """ + The default aggregation to be used in a `View`. + + This aggregation will create an actual aggregation depending on the + instrument type, as specified next: + + ==================================================== ==================================== + Instrument Aggregation + ==================================================== ==================================== + `opentelemetry.sdk.metrics.Counter` `SumAggregation` + `opentelemetry.sdk.metrics.UpDownCounter` `SumAggregation` + `opentelemetry.sdk.metrics.ObservableCounter` `SumAggregation` + `opentelemetry.sdk.metrics.ObservableUpDownCounter` `SumAggregation` + `opentelemetry.sdk.metrics.Histogram` `ExplicitBucketHistogramAggregation` + `opentelemetry.sdk.metrics.ObservableGauge` `LastValueAggregation` + ==================================================== ==================================== + """ + + def _create_aggregation( + self, + instrument: _Instrument, + attributes: Attributes, + reservoir_factory: Callable[[type[_Aggregation]], ExemplarReservoirBuilder], + start_time_unix_nano: int, + ) -> _Aggregation: + # pylint: disable=too-many-return-statements + if isinstance(instrument, Counter): + return _SumAggregation( + attributes, + reservoir_builder=reservoir_factory(_SumAggregation), + instrument_is_monotonic=True, + instrument_aggregation_temporality=(AggregationTemporality.DELTA), + start_time_unix_nano=start_time_unix_nano, + ) + if isinstance(instrument, UpDownCounter): + return _SumAggregation( + attributes, + reservoir_builder=reservoir_factory(_SumAggregation), + instrument_is_monotonic=False, + instrument_aggregation_temporality=(AggregationTemporality.DELTA), + start_time_unix_nano=start_time_unix_nano, + ) + + if isinstance(instrument, ObservableCounter): + return _SumAggregation( + attributes, + reservoir_builder=reservoir_factory(_SumAggregation), + instrument_is_monotonic=True, + instrument_aggregation_temporality=(AggregationTemporality.CUMULATIVE), + start_time_unix_nano=start_time_unix_nano, + ) + + if isinstance(instrument, ObservableUpDownCounter): + return _SumAggregation( + attributes, + reservoir_builder=reservoir_factory(_SumAggregation), + instrument_is_monotonic=False, + instrument_aggregation_temporality=(AggregationTemporality.CUMULATIVE), + start_time_unix_nano=start_time_unix_nano, + ) + + if isinstance(instrument, Histogram): + boundaries = instrument._advisory.explicit_bucket_boundaries + return _ExplicitBucketHistogramAggregation( + attributes, + reservoir_builder=reservoir_factory(_ExplicitBucketHistogramAggregation), + instrument_aggregation_temporality=(AggregationTemporality.DELTA), + boundaries=boundaries, + start_time_unix_nano=start_time_unix_nano, + ) + + if isinstance(instrument, ObservableGauge): + return _LastValueAggregation( + attributes, + reservoir_builder=reservoir_factory(_LastValueAggregation), + instrument_is_synchronous=False, + ) + + if isinstance(instrument, _Gauge): + return _LastValueAggregation( + attributes, + reservoir_builder=reservoir_factory(_LastValueAggregation), + instrument_is_synchronous=True, + ) + + # pylint: disable=broad-exception-raised + raise Exception(f"Invalid instrument type {type(instrument)} found") + + +class ExponentialBucketHistogramAggregation(Aggregation): + """This aggregation informs the SDK to collect: + + - Count of Measurement values falling using a base-2 exponential formula. + - Arithmetic sum of Measurement values in population. This SHOULD NOT be collected when used with instruments that record negative measurements, e.g. UpDownCounter or ObservableGauge. + - Min (optional) Measurement value in population. + - Max (optional) Measurement value in population. + + Args: + max_size: Maximum number of buckets in each of the positive and negative ranges, not counting the special zero bucket. + max_scale: Maximum scale factor. + record_min_max: Whether to record min and max. + """ + + def __init__( + self, + max_size: int = 160, + max_scale: int = 20, + record_min_max: bool = True, + ): + self._max_size = max_size + self._max_scale = max_scale + self._record_min_max = record_min_max + + def _create_aggregation( + self, + instrument: _Instrument, + attributes: Attributes, + reservoir_factory: Callable[[type[_Aggregation]], ExemplarReservoirBuilder], + start_time_unix_nano: int, + ) -> _Aggregation: + instrument_aggregation_temporality = AggregationTemporality.UNSPECIFIED + if isinstance(instrument, Synchronous): + instrument_aggregation_temporality = AggregationTemporality.DELTA + elif isinstance(instrument, Asynchronous): + instrument_aggregation_temporality = AggregationTemporality.CUMULATIVE + + return _ExponentialBucketHistogramAggregation( + attributes, + reservoir_factory(_ExponentialBucketHistogramAggregation), + instrument_aggregation_temporality, + start_time_unix_nano, + max_size=self._max_size, + max_scale=self._max_scale, + record_min_max=self._record_min_max, + ) + + +class ExplicitBucketHistogramAggregation(Aggregation): + """This aggregation informs the SDK to collect: + + - Count of Measurement values falling within explicit bucket boundaries. + - Arithmetic sum of Measurement values in population. This SHOULD NOT be collected when used with instruments that record negative measurements, e.g. UpDownCounter or ObservableGauge. + - Min (optional) Measurement value in population. + - Max (optional) Measurement value in population. + + + Args: + boundaries: Array of increasing values representing explicit bucket boundary values. + record_min_max: Whether to record min and max. + """ + + def __init__( + self, + boundaries: Sequence[float] | None = None, + record_min_max: bool = True, + ) -> None: + self._boundaries = boundaries + self._record_min_max = record_min_max + + def _create_aggregation( + self, + instrument: _Instrument, + attributes: Attributes, + reservoir_factory: Callable[[type[_Aggregation]], ExemplarReservoirBuilder], + start_time_unix_nano: int, + ) -> _Aggregation: + instrument_aggregation_temporality = AggregationTemporality.UNSPECIFIED + if isinstance(instrument, Synchronous): + instrument_aggregation_temporality = AggregationTemporality.DELTA + elif isinstance(instrument, Asynchronous): + instrument_aggregation_temporality = AggregationTemporality.CUMULATIVE + + if self._boundaries is not None: + boundaries = self._boundaries + else: + # guard for usage with instruments without advisory + advisory = getattr(instrument, "_advisory", None) + boundaries = advisory.explicit_bucket_boundaries if advisory is not None else None + + return _ExplicitBucketHistogramAggregation( + attributes, + instrument_aggregation_temporality, + start_time_unix_nano, + reservoir_factory(_ExplicitBucketHistogramAggregation), + boundaries, + self._record_min_max, + ) + + +class SumAggregation(Aggregation): + """This aggregation informs the SDK to collect: + + - The arithmetic sum of Measurement values. + """ + + def _create_aggregation( + self, + instrument: _Instrument, + attributes: Attributes, + reservoir_factory: Callable[[type[_Aggregation]], ExemplarReservoirBuilder], + start_time_unix_nano: int, + ) -> _Aggregation: + instrument_aggregation_temporality = AggregationTemporality.UNSPECIFIED + if isinstance(instrument, Synchronous): + instrument_aggregation_temporality = AggregationTemporality.DELTA + elif isinstance(instrument, Asynchronous): + instrument_aggregation_temporality = AggregationTemporality.CUMULATIVE + + return _SumAggregation( + attributes, + isinstance(instrument, (Counter, ObservableCounter)), + instrument_aggregation_temporality, + start_time_unix_nano, + reservoir_factory(_SumAggregation), + ) + + +class LastValueAggregation(Aggregation): + """ + This aggregation informs the SDK to collect: + + - The last Measurement. + - The timestamp of the last Measurement. + """ + + def _create_aggregation( + self, + instrument: _Instrument, + attributes: Attributes, + reservoir_factory: Callable[[type[_Aggregation]], ExemplarReservoirBuilder], + start_time_unix_nano: int, + ) -> _Aggregation: + return _LastValueAggregation( + attributes, + reservoir_builder=reservoir_factory(_LastValueAggregation), + instrument_is_synchronous=isinstance(instrument, Synchronous), + ) + + +class DropAggregation(Aggregation): + """Using this aggregation will make all measurements be ignored.""" + + def _create_aggregation( + self, + instrument: _Instrument, + attributes: Attributes, + reservoir_factory: Callable[[type[_Aggregation]], ExemplarReservoirBuilder], + start_time_unix_nano: int, + ) -> _Aggregation: + return _DropAggregation(attributes, reservoir_factory(_DropAggregation)) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exceptions.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exceptions.py new file mode 100644 index 0000000..5ae8519 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exceptions.py @@ -0,0 +1,6 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +class MetricsTimeoutError(Exception): + """Raised when a metrics function times out""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__init__.py new file mode 100644 index 0000000..8068406 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__init__.py @@ -0,0 +1,28 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from .exemplar import Exemplar +from .exemplar_filter import ( + AlwaysOffExemplarFilter, + AlwaysOnExemplarFilter, + ExemplarFilter, + TraceBasedExemplarFilter, +) +from .exemplar_reservoir import ( + AlignedHistogramBucketExemplarReservoir, + ExemplarReservoir, + ExemplarReservoirBuilder, + SimpleFixedSizeExemplarReservoir, +) + +__all__ = [ + "AlignedHistogramBucketExemplarReservoir", + "AlwaysOffExemplarFilter", + "AlwaysOnExemplarFilter", + "Exemplar", + "ExemplarFilter", + "ExemplarReservoir", + "ExemplarReservoirBuilder", + "SimpleFixedSizeExemplarReservoir", + "TraceBasedExemplarFilter", +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1831aed Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/exemplar.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/exemplar.cpython-312.pyc new file mode 100644 index 0000000..e9dfc75 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/exemplar.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/exemplar_filter.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/exemplar_filter.cpython-312.pyc new file mode 100644 index 0000000..a7dbd2b Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/exemplar_filter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/exemplar_reservoir.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/exemplar_reservoir.cpython-312.pyc new file mode 100644 index 0000000..cfddbdb Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/exemplar_reservoir.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/exemplar.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/exemplar.py new file mode 100644 index 0000000..e864529 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/exemplar.py @@ -0,0 +1,33 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import dataclasses + +from opentelemetry.util.types import Attributes + + +@dataclasses.dataclass(frozen=True) +class Exemplar: + """A representation of an exemplar, which is a sample input measurement. + + Exemplars also hold information about the environment when the measurement + was recorded, for example the span and trace ID of the active span when the + exemplar was recorded. + + Attributes: + trace_id: (optional) The trace associated with a recording + span_id: (optional) The span associated with a recording + time_unix_nano: The time of the observation + value: The recorded value + filtered_attributes: A set of filtered attributes which provide additional insight into the Context when the observation was made. + + References: + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md#exemplars + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#exemplar + """ + + filtered_attributes: Attributes + value: int | float + time_unix_nano: int + span_id: int | None = None + trace_id: int | None = None diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_filter.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_filter.py new file mode 100644 index 0000000..2117382 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_filter.py @@ -0,0 +1,120 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from abc import ABC, abstractmethod + +from opentelemetry import trace +from opentelemetry.context import Context +from opentelemetry.trace.span import INVALID_SPAN +from opentelemetry.util.types import Attributes + + +class ExemplarFilter(ABC): + """``ExemplarFilter`` determines which measurements are eligible for becoming an + ``Exemplar``. + + Exemplar filters are used to filter measurements before attempting to store them + in a reservoir. + + Reference: + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#exemplarfilter + """ + + @abstractmethod + def should_sample( + self, + value: float, + time_unix_nano: int, + attributes: Attributes, + context: Context, + ) -> bool: + """Returns whether or not a reservoir should attempt to filter a measurement. + + Args: + value: The value of the measurement + timestamp: A timestamp that best represents when the measurement was taken + attributes: The complete set of measurement attributes + context: The Context of the measurement + """ + raise NotImplementedError("ExemplarFilter.should_sample is not implemented") + + +class AlwaysOnExemplarFilter(ExemplarFilter): + """An ExemplarFilter which makes all measurements eligible for being an Exemplar. + + Reference: + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#alwayson + """ + + def should_sample( + self, + value: float, + time_unix_nano: int, + attributes: Attributes, + context: Context, + ) -> bool: + """Returns whether or not a reservoir should attempt to filter a measurement. + + Args: + value: The value of the measurement + timestamp: A timestamp that best represents when the measurement was taken + attributes: The complete set of measurement attributes + context: The Context of the measurement + """ + return True + + +class AlwaysOffExemplarFilter(ExemplarFilter): + """An ExemplarFilter which makes no measurements eligible for being an Exemplar. + + Using this ExemplarFilter is as good as disabling Exemplar feature. + + Reference: + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#alwaysoff + """ + + def should_sample( + self, + value: float, + time_unix_nano: int, + attributes: Attributes, + context: Context, + ) -> bool: + """Returns whether or not a reservoir should attempt to filter a measurement. + + Args: + value: The value of the measurement + timestamp: A timestamp that best represents when the measurement was taken + attributes: The complete set of measurement attributes + context: The Context of the measurement + """ + return False + + +class TraceBasedExemplarFilter(ExemplarFilter): + """An ExemplarFilter which makes those measurements eligible for being an Exemplar, + which are recorded in the context of a sampled parent span. + + Reference: + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#tracebased + """ + + def should_sample( + self, + value: float, + time_unix_nano: int, + attributes: Attributes, + context: Context, + ) -> bool: + """Returns whether or not a reservoir should attempt to filter a measurement. + + Args: + value: The value of the measurement + timestamp: A timestamp that best represents when the measurement was taken + attributes: The complete set of measurement attributes + context: The Context of the measurement + """ + span = trace.get_current_span(context) + if span == INVALID_SPAN: + return False + return span.get_span_context().trace_flags.sampled diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_reservoir.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_reservoir.py new file mode 100644 index 0000000..8bc9289 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_reservoir.py @@ -0,0 +1,291 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from abc import ABC, abstractmethod +from collections import defaultdict +from collections.abc import Callable, Mapping, Sequence +from random import randrange +from threading import Lock +from typing import ( + Any, +) + +from opentelemetry import trace +from opentelemetry.context import Context +from opentelemetry.trace.span import INVALID_SPAN +from opentelemetry.util.types import Attributes + +from .exemplar import Exemplar + + +class ExemplarReservoir(ABC): + """ExemplarReservoir provide a method to offer measurements to the reservoir + and another to collect accumulated Exemplars. + + Note: + The constructor MUST accept ``**kwargs`` that may be set from aggregation + parameters. + + Note: + All methods MUST be safe to call concurrently. Measurements are offered + from application threads while a metric reader collects from its own + thread. + + Reference: + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#exemplarreservoir + """ + + @abstractmethod + def offer( + self, + value: float, + time_unix_nano: int, + attributes: Attributes, + context: Context, + ) -> None: + """Offers a measurement to be sampled. + + Args: + value: Measured value + time_unix_nano: Measurement instant + attributes: Measurement attributes + context: Measurement context + """ + raise NotImplementedError("ExemplarReservoir.offer is not implemented") + + @abstractmethod + def collect(self, point_attributes: Attributes) -> list[Exemplar]: + """Returns accumulated Exemplars and also resets the reservoir for the next + sampling period + + Args: + point_attributes: The attributes associated with metric point. + + Returns: + a list of ``opentelemetry.sdk.metrics._internal.exemplar.exemplar.Exemplar`` s. Returned + exemplars contain the attributes that were filtered out by the aggregator, + but recorded alongside the original measurement. + """ + raise NotImplementedError("ExemplarReservoir.collect is not implemented") + + +class ExemplarBucket: + def __init__(self) -> None: + self.__value: int | float = 0 + self.__attributes: Attributes = None + self.__time_unix_nano: int = 0 + self.__span_id: int | None = None + self.__trace_id: int | None = None + self.__offered: bool = False + + def offer( + self, + value: float, + time_unix_nano: int, + attributes: Attributes, + context: Context, + ) -> None: + """Offers a measurement to be sampled. + + Args: + value: Measured value + time_unix_nano: Measurement instant + attributes: Measurement attributes + context: Measurement context + """ + self.__value = value + self.__time_unix_nano = time_unix_nano + self.__attributes = attributes + span = trace.get_current_span(context) + if span != INVALID_SPAN: + span_context = span.get_span_context() + self.__span_id = span_context.span_id + self.__trace_id = span_context.trace_id + + self.__offered = True + + def collect(self, point_attributes: Attributes) -> Exemplar | None: + """May return an Exemplar and resets the bucket for the next sampling period.""" + if not self.__offered: + return None + + # filters out attributes from the measurement that are already included in the metric data point + # See the specification for more details: + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#exemplar + filtered_attributes = ( + {k: v for k, v in self.__attributes.items() if k not in point_attributes} if self.__attributes else None + ) + + exemplar = Exemplar( + filtered_attributes, + self.__value, + self.__time_unix_nano, + self.__span_id, + self.__trace_id, + ) + self.__reset() + return exemplar + + def __reset(self) -> None: + """Reset the bucket state after a collection cycle.""" + self.__value = 0 + self.__attributes = {} + self.__time_unix_nano = 0 + self.__span_id = None + self.__trace_id = None + self.__offered = False + + +class BucketIndexError(ValueError): + """An exception raised when the bucket index cannot be found.""" + + +class FixedSizeExemplarReservoirABC(ExemplarReservoir): + """Abstract class for a reservoir with fixed size.""" + + def __init__(self, size: int, **kwargs) -> None: + super().__init__(**kwargs) + self._size: int = size + self._reservoir_storage: Mapping[int, ExemplarBucket] = defaultdict(ExemplarBucket) + self._lock = Lock() + + def collect(self, point_attributes: Attributes) -> list[Exemplar]: + """Returns accumulated Exemplars and also resets the reservoir for the next + sampling period + + Args: + point_attributes: The attributes associated with metric point. + + Returns: + a list of ``opentelemetry.sdk.metrics._internal.exemplar.exemplar.Exemplar`` s. Returned + exemplars contain the attributes that were filtered out by the aggregator, + but recorded alongside the original measurement. + """ + with self._lock: + exemplars = [ + e + for e in (bucket.collect(point_attributes) for _, bucket in sorted(self._reservoir_storage.items())) + if e is not None + ] + self._reset() + return exemplars + + def offer( + self, + value: float, + time_unix_nano: int, + attributes: Attributes, + context: Context, + ) -> None: + """Offers a measurement to be sampled. + + Args: + value: Measured value + time_unix_nano: Measurement instant + attributes: Measurement attributes + context: Measurement context + """ + with self._lock: + try: + index = self._find_bucket_index(value, time_unix_nano, attributes, context) + + self._reservoir_storage[index].offer(value, time_unix_nano, attributes, context) + except BucketIndexError: + # Ignore invalid bucket index + pass + + @abstractmethod + def _find_bucket_index( + self, + value: float, + time_unix_nano: int, + attributes: Attributes, + context: Context, + ) -> int: + """Determines the bucket index for the given measurement. + + It should be implemented by subclasses based on specific strategies. + + Args: + value: Measured value + time_unix_nano: Measurement instant + attributes: Measurement attributes + context: Measurement context + + Returns: + The bucket index + + Raises: + BucketIndexError: If no bucket index can be found. + """ + + def _reset(self) -> None: + """Reset the reservoir by resetting any stateful logic after a collection cycle.""" + + +class SimpleFixedSizeExemplarReservoir(FixedSizeExemplarReservoirABC): + """This reservoir uses an uniformly-weighted sampling algorithm based on the number + of samples the reservoir has seen so far to determine if the offered measurements + should be sampled. + + Reference: + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#simplefixedsizeexemplarreservoir + """ + + def __init__(self, size: int = 1, **kwargs) -> None: + super().__init__(size, **kwargs) + self._measurements_seen: int = 0 + + def _reset(self) -> None: + super()._reset() + self._measurements_seen = 0 + + def _find_bucket_index( + self, + value: float, + time_unix_nano: int, + attributes: Attributes, + context: Context, + ) -> int: + if self._measurements_seen < self._size: + self._measurements_seen += 1 + return self._measurements_seen - 1 + + self._measurements_seen += 1 + index = randrange(0, self._measurements_seen) + if index < self._size: + return index + + raise BucketIndexError("Unable to find the bucket index.") + + +class AlignedHistogramBucketExemplarReservoir(FixedSizeExemplarReservoirABC): + """This Exemplar reservoir takes a configuration parameter that is the + configuration of a Histogram. This implementation keeps the last seen measurement + that falls within a histogram bucket. + + Reference: + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#alignedhistogrambucketexemplarreservoir + """ + + def __init__(self, boundaries: Sequence[float], **kwargs) -> None: + super().__init__(len(boundaries) + 1, **kwargs) + self._boundaries: Sequence[float] = boundaries + + def _find_bucket_index( + self, + value: float, + time_unix_nano: int, + attributes: Attributes, + context: Context, + ) -> int: + for index, boundary in enumerate(self._boundaries): + if value <= boundary: + return index + return len(self._boundaries) + + +# ExemplarReservoirBuilder: A callable that takes Aggregation parameters +# (e.g. boundaries for _ExplicitBucketHistogramAggregation) and returns +# an ExemplarReservoir instance. +ExemplarReservoirBuilder = Callable[[dict[str, Any]], ExemplarReservoir] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..3b25183 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/__pycache__/buckets.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/__pycache__/buckets.cpython-312.pyc new file mode 100644 index 0000000..9535898 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/__pycache__/buckets.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/buckets.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/buckets.py new file mode 100644 index 0000000..a874970 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/buckets.py @@ -0,0 +1,177 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from math import ceil, log2 + + +class Buckets: + # No method of this class is protected by locks because instances of this + # class are only used in methods that are protected by locks themselves. + + def __init__(self): + self._counts = [0] + + # The term index refers to the number of the exponential histogram bucket + # used to determine its boundaries. The lower boundary of a bucket is + # determined by base ** index and the upper boundary of a bucket is + # determined by base ** (index + 1). index values are signedto account + # for values less than or equal to 1. + + # self._index_* will all have values equal to a certain index that is + # determined by the corresponding mapping _map_to_index function and + # the value of the index depends on the value passed to _map_to_index. + + # Index of the 0th position in self._counts: self._counts[0] is the + # count in the bucket with index self.__index_base. + self.__index_base = 0 + + # self.__index_start is the smallest index value represented in + # self._counts. + self.__index_start = 0 + + # self.__index_start is the largest index value represented in + # self._counts. + self.__index_end = 0 + + @property + def index_start(self) -> int: + return self.__index_start + + @index_start.setter + def index_start(self, value: int) -> None: + self.__index_start = value + + @property + def index_end(self) -> int: + return self.__index_end + + @index_end.setter + def index_end(self, value: int) -> None: + self.__index_end = value + + @property + def index_base(self) -> int: + return self.__index_base + + @index_base.setter + def index_base(self, value: int) -> None: + self.__index_base = value + + @property + def counts(self): + return self._counts + + def get_offset_counts(self): + bias = self.__index_base - self.__index_start + return self._counts[-bias:] + self._counts[:-bias] + + def grow(self, needed: int, max_size: int) -> None: + size = len(self._counts) + bias = self.__index_base - self.__index_start + old_positive_limit = size - bias + + # 2 ** ceil(log2(needed)) finds the smallest power of two that is larger + # or equal than needed: + # 2 ** ceil(log2(1)) == 1 + # 2 ** ceil(log2(2)) == 2 + # 2 ** ceil(log2(3)) == 4 + # 2 ** ceil(log2(4)) == 4 + # 2 ** ceil(log2(5)) == 8 + # 2 ** ceil(log2(6)) == 8 + # 2 ** ceil(log2(7)) == 8 + # 2 ** ceil(log2(8)) == 8 + new_size = min(2 ** ceil(log2(needed)), max_size) + + new_positive_limit = new_size - bias + + tmp = [0] * new_size + tmp[new_positive_limit:] = self._counts[old_positive_limit:] + tmp[0:old_positive_limit] = self._counts[0:old_positive_limit] + self._counts = tmp + + @property + def offset(self) -> int: + return self.__index_start + + def __len__(self) -> int: + if len(self._counts) == 0: + return 0 + + if self.__index_end == self.__index_start and self[0] == 0: + return 0 + + return self.__index_end - self.__index_start + 1 + + def __getitem__(self, key: int) -> int: + bias = self.__index_base - self.__index_start + + if key < bias: + key += len(self._counts) + + key -= bias + + return self._counts[key] + + def downscale(self, amount: int) -> None: + """ + Rotates, then collapses 2 ** amount to 1 buckets. + """ + + bias = self.__index_base - self.__index_start + + if bias != 0: + self.__index_base = self.__index_start + + # [0, 1, 2, 3, 4] Original backing array + + self._counts = self._counts[::-1] + # [4, 3, 2, 1, 0] + + self._counts = self._counts[:bias][::-1] + self._counts[bias:][::-1] + # [3, 4, 0, 1, 2] This is a rotation of the backing array. + + size = 1 + self.__index_end - self.__index_start + each = 1 << amount + inpos = 0 + outpos = 0 + + pos = self.__index_start + + while pos <= self.__index_end: + mod = pos % each + if mod < 0: + mod += each + + index = mod + + while index < each and inpos < size: + if outpos != inpos: + self._counts[outpos] += self._counts[inpos] + self._counts[inpos] = 0 + + inpos += 1 + pos += 1 + index += 1 + + outpos += 1 + + self.__index_start >>= amount + self.__index_end >>= amount + self.__index_base = self.__index_start + + def increment_bucket(self, bucket_index: int, increment: int = 1) -> None: + self._counts[bucket_index] += increment + + def copy_empty(self) -> "Buckets": + copy = Buckets() + + # pylint: disable=no-member + # pylint: disable=protected-access + # pylint: disable=attribute-defined-outside-init + # pylint: disable=invalid-name + copy._Buckets__index_base = self._Buckets__index_base # type: ignore[reportArgumentType] + copy._Buckets__index_start = self._Buckets__index_start # type: ignore[reportArgumentType] + copy._Buckets__index_end = self._Buckets__index_end # type: ignore[reportArgumentType] + copy._counts = [0 for _ in self._counts] + + return copy diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__init__.py new file mode 100644 index 0000000..a62a2e6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__init__.py @@ -0,0 +1,94 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from abc import ABC, abstractmethod +from threading import Lock + + +class Mapping(ABC): + """ + Parent class for `LogarithmMapping` and `ExponentialMapping`. + """ + + _mappings: dict[int, "Mapping"] + _mappings_lock: Lock + + _min_scale: int + _max_scale: int + + # pylint: disable=no-member + def __new__(cls, scale: int): + with cls._mappings_lock: + # cls._mappings and cls._mappings_lock are implemented in each of + # the child classes as a dictionary and a lock, respectively. They + # are not instantiated here because that would lead to both child + # classes having the same instance of cls._mappings and + # cls._mappings_lock. + if scale not in cls._mappings: + cls._mappings[scale] = super().__new__(cls) + cls._mappings[scale]._init(scale) + + return cls._mappings[scale] + + @abstractmethod + def _init(self, scale: int) -> None: + # pylint: disable=attribute-defined-outside-init + + if scale > self._get_max_scale(): + # pylint: disable=broad-exception-raised + raise Exception(f"scale is larger than {self._max_scale}") + + if scale < self._get_min_scale(): + # pylint: disable=broad-exception-raised + raise Exception(f"scale is smaller than {self._min_scale}") + + # The size of the exponential histogram buckets is determined by a + # parameter known as scale, larger values of scale will produce smaller + # buckets. Bucket boundaries of the exponential histogram are located + # at integer powers of the base, where: + # + # base = 2 ** (2 ** (-scale)) + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md#all-scales-use-the-logarithm-function + self._scale = scale + + @abstractmethod + def _get_min_scale(self) -> int: + """ + Return the smallest possible value for the mapping scale + """ + + @abstractmethod + def _get_max_scale(self) -> int: + """ + Return the largest possible value for the mapping scale + """ + + @abstractmethod + def map_to_index(self, value: float) -> int: + """ + Maps positive floating point values to indexes corresponding to + `Mapping.scale`. Implementations are not expected to handle zeros, + +inf, NaN, or negative values. + """ + + @abstractmethod + def get_lower_boundary(self, index: int) -> float: + """ + Returns the lower boundary of a given bucket index. The index is + expected to map onto a range that is at least partially inside the + range of normal floating point values. If the corresponding + bucket's upper boundary is less than or equal to 2 ** -1022, + :class:`~opentelemetry.sdk.metrics.MappingUnderflowError` + will be raised. If the corresponding bucket's lower boundary is greater + than ``sys.float_info.max``, + :class:`~opentelemetry.sdk.metrics.MappingOverflowError` + will be raised. + """ + + @property + def scale(self) -> int: + """ + Returns the parameter that controls the resolution of this mapping. + See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/datamodel.md#exponential-scale + """ + return self._scale diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..bee4ba8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/errors.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/errors.cpython-312.pyc new file mode 100644 index 0000000..7b9b22b Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/errors.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/exponent_mapping.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/exponent_mapping.cpython-312.pyc new file mode 100644 index 0000000..cc7193e Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/exponent_mapping.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/ieee_754.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/ieee_754.cpython-312.pyc new file mode 100644 index 0000000..33b4ac4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/ieee_754.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/logarithm_mapping.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/logarithm_mapping.cpython-312.pyc new file mode 100644 index 0000000..e4d9131 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/logarithm_mapping.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/errors.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/errors.py new file mode 100644 index 0000000..8bf64b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/errors.py @@ -0,0 +1,15 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +class MappingUnderflowError(Exception): + """ + Raised when computing the lower boundary of an index that maps into a + denormal floating point value. + """ + + +class MappingOverflowError(Exception): + """ + Raised when computing the lower boundary of an index that maps into +inf. + """ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/exponent_mapping.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/exponent_mapping.py new file mode 100644 index 0000000..d800003 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/exponent_mapping.py @@ -0,0 +1,145 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from math import ldexp +from threading import Lock + +from opentelemetry.sdk.metrics._internal.exponential_histogram.mapping import ( + Mapping, +) +from opentelemetry.sdk.metrics._internal.exponential_histogram.mapping.errors import ( + MappingOverflowError, + MappingUnderflowError, +) +from opentelemetry.sdk.metrics._internal.exponential_histogram.mapping.ieee_754 import ( + MANTISSA_WIDTH, + MAX_NORMAL_EXPONENT, + MIN_NORMAL_EXPONENT, + MIN_NORMAL_VALUE, + get_ieee_754_exponent, + get_ieee_754_mantissa, +) + + +class ExponentMapping(Mapping): + # Reference implementation here: + # https://github.com/open-telemetry/opentelemetry-go/blob/0e6f9c29c10d6078e8131418e1d1d166c7195d61/sdk/metric/aggregator/exponential/mapping/exponent/exponent.go + + _mappings = {} + _mappings_lock = Lock() + + _min_scale = -10 + _max_scale = 0 + + def _get_min_scale(self): + # _min_scale defines the point at which the exponential mapping + # function becomes useless for 64-bit floats. With scale -10, ignoring + # subnormal values, bucket indices range from -1 to 1. + return -10 + + def _get_max_scale(self): + # _max_scale is the largest scale supported by exponential mapping. Use + # a logarithm mapping for larger scales. + return 0 + + def _init(self, scale: int): + # pylint: disable=attribute-defined-outside-init + + super()._init(scale) + + # self._min_normal_lower_boundary_index is the largest index such that + # base ** index < MIN_NORMAL_VALUE and + # base ** (index + 1) >= MIN_NORMAL_VALUE. An exponential histogram + # bucket with this index covers the range + # (base ** index, base (index + 1)], including MIN_NORMAL_VALUE. This + # is the smallest valid index that contains at least one normal value. + index = MIN_NORMAL_EXPONENT >> -self._scale + + if -self._scale < 2: + # For scales -1 and 0, the maximum value 2 ** -1022 is a + # power-of-two multiple, meaning base ** index == MIN_NORMAL_VALUE. + # Subtracting 1 so that base ** (index + 1) == MIN_NORMAL_VALUE. + index -= 1 + + self._min_normal_lower_boundary_index = index + + # self._max_normal_lower_boundary_index is the index such that + # base**index equals the greatest representable lower boundary. An + # exponential histogram bucket with this index covers the range + # ((2 ** 1024) / base, 2 ** 1024], which includes opentelemetry.sdk. + # metrics._internal.exponential_histogram.ieee_754.MAX_NORMAL_VALUE. + # This bucket is incomplete, since the upper boundary cannot be + # represented. One greater than this index corresponds with the bucket + # containing values > 2 ** 1024. + self._max_normal_lower_boundary_index = MAX_NORMAL_EXPONENT >> -self._scale + + def map_to_index(self, value: float) -> int: + if value < MIN_NORMAL_VALUE: + return self._min_normal_lower_boundary_index + + exponent = get_ieee_754_exponent(value) + + # Positive integers are represented in binary as having an infinite + # amount of leading zeroes, for example 2 is represented as ...00010. + + # A negative integer -x is represented in binary as the complement of + # (x - 1). For example, -4 is represented as the complement of 4 - 1 + # == 3. 3 is represented as ...00011. Its compliment is ...11100, the + # binary representation of -4. + + # get_ieee_754_mantissa(value) gets the positive integer made up + # from the rightmost MANTISSA_WIDTH bits (the mantissa) of the IEEE + # 754 representation of value. If value is an exact power of 2, all + # these MANTISSA_WIDTH bits would be all zeroes, and when 1 is + # subtracted the resulting value is -1. The binary representation of + # -1 is ...111, so when these bits are right shifted MANTISSA_WIDTH + # places, the resulting value for correction is -1. If value is not an + # exact power of 2, at least one of the rightmost MANTISSA_WIDTH + # bits would be 1 (even for values whose decimal part is 0, like 5.0 + # since the IEEE 754 of such number is too the product of a power of 2 + # (defined in the exponent part of the IEEE 754 representation) and the + # value defined in the mantissa). Having at least one of the rightmost + # MANTISSA_WIDTH bit being 1 means that get_ieee_754(value) will + # always be greater or equal to 1, and when 1 is subtracted, the + # result will be greater or equal to 0, whose representation in binary + # will be of at most MANTISSA_WIDTH ones that have an infinite + # amount of leading zeroes. When those MANTISSA_WIDTH bits are + # shifted to the right MANTISSA_WIDTH places, the resulting value + # will be 0. + + # In summary, correction will be -1 if value is a power of 2, 0 if not. + + # map_to_index requires value to be a finite, positive real number. + # 0, inf, and NaN violate this precondition. + + # Zero is represented in IEEE 754 with all exponent bits set to 0, + # giving get_ieee_754_exponent a result of -1023. Since -1023 is less + # than MIN_NORMAL_EXPONENT (-1022), zero is caught by the guard above + # and returned early. + + # Inf is represented in IEEE 754 with all 11 exponent bits set to 1 + # and a mantissa of 0, giving get_ieee_754_exponent a result of 1024 + # and correction a value of -1. + + # NaN is represented in IEEE 754 with all 11 exponent bits set to 1 + # and a non-zero mantissa of unspecified bit pattern, producing an + # unspecified correction value. + + # Inf and NaN are not caught by the guard above. Callers must ensure + # that only finite, positive values are passed to map_to_index. + correction = (get_ieee_754_mantissa(value) - 1) >> MANTISSA_WIDTH + + return (exponent + correction) >> -self._scale + + def get_lower_boundary(self, index: int) -> float: + if index < self._min_normal_lower_boundary_index: + raise MappingUnderflowError() + + if index > self._max_normal_lower_boundary_index: + raise MappingOverflowError() + + return ldexp(1, index << -self._scale) + + @property + def scale(self) -> int: + return self._scale diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/ieee_754.md b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/ieee_754.md new file mode 100644 index 0000000..0cf5c8c --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/ieee_754.md @@ -0,0 +1,175 @@ +# IEEE 754 Explained + +IEEE 754 is a standard that defines a way to represent certain mathematical +objects using binary numbers. + +## Binary Number Fields + +The binary numbers used in IEEE 754 can have different lengths, the length that +is interesting for the purposes of this project is 64 bits. These binary +numbers are made up of 3 contiguous fields of bits, from left to right: + +1. 1 sign bit +2. 11 exponent bits +3. 52 mantissa bits + +Depending on the values these fields have, the represented mathematical object +can be one of: + +* Floating point number +* Zero +* NaN +* Infinite + +## Floating Point Numbers + +IEEE 754 represents a floating point number $f$ using an exponential +notation with 4 components: $sign$, $mantissa$, $base$ and $exponent$: + +$$f = sign \times mantissa \times base ^ {exponent}$$ + +There are two possible representations of floating point numbers: +_normal_ and _denormal_, which have different valid values for +their $mantissa$ and $exponent$ fields. + +### Binary Representation + +$sign$, $mantissa$, and $exponent$ are represented in binary, the +representation of each component has certain details explained next. + +$base$ is always $2$ and it is not represented in binary. + +#### Sign + +$sign$ can have 2 values: + +1. $1$ if the `sign` bit is `0` +2. $-1$ if the `sign` bit is `1`. + +#### Mantissa + +##### Normal Floating Point Numbers + +$mantissa$ is a positive fractional number whose integer part is $1$, for example +$1.2345 \dots$. The `mantissa` bits represent only the fractional part and the +$mantissa$ value can be calculated as: + +$$mantissa = 1 + \sum_{i=1}^{52} b_{i} \times 2^{-i} = 1 + \frac{b_{1}}{2^{1}} + \frac{b_{2}}{2^{2}} + \dots + \frac{b_{51}}{2^{51}} + \frac{b_{52}}{2^{52}}$$ + +Where $b_{i}$ is: + +1. $0$ if the bit at the position `i - 1` is `0`. +2. $1$ if the bit at the position `i - 1` is `1`. + +##### Denormal Floating Point Numbers + +$mantissa$ is a positive fractional number whose integer part is $0$, for example +$0.12345 \dots$. The `mantissa` bits represent only the fractional part and the +$mantissa$ value can be calculated as: + +$$mantissa = \sum_{i=1}^{52} b_{i} \times 2^{-i} = \frac{b_{1}}{2^{1}} + \frac{b_{2}}{2^{2}} + \dots + \frac{b_{51}}{2^{51}} + \frac{b_{52}}{2^{52}}$$ + +Where $b_{i}$ is: + +1. $0$ if the bit at the position `i - 1` is `0`. +2. $1$ if the bit at the position `i - 1` is `1`. + +#### Exponent + +##### Normal Floating Point Numbers + +Only the following bit sequences are allowed: `00000000001` to `11111111110`. +That is, there must be at least one `0` and one `1` in the exponent bits. + +The actual value of the $exponent$ can be calculated as: + +$$exponent = v - bias$$ + +where $v$ is the value of the binary number in the exponent bits and $bias$ is $1023$. +Considering the restrictions above, the respective minimum and maximum values for the +exponent are: + +1. `00000000001` = $1$, $1 - 1023 = -1022$ +2. `11111111110` = $2046$, $2046 - 1023 = 1023$ + +So, $exponent$ is an integer in the range $\left[-1022, 1023\right]$. + + +##### Denormal Floating Point Numbers + +$exponent$ is always $-1022$. Nevertheless, it is always represented as `00000000000`. + +### Normal and Denormal Floating Point Numbers + +The smallest absolute value a normal floating point number can have is calculated +like this: + +$$1 \times 1.0\dots0 \times 2^{-1022} = 2.2250738585072014 \times 10^{-308}$$ + +Since normal floating point numbers always have a $1$ as the integer part of the +$mantissa$, then smaller values can be achieved by using the smallest possible exponent +( $-1022$ ) and a $0$ in the integer part of the $mantissa$, but significant digits are lost. + +The smallest absolute value a denormal floating point number can have is calculated +like this: + +$$1 \times 2^{-52} \times 2^{-1022} = 5 \times 10^{-324}$$ + +## Zero + +Zero is represented like this: + +* Sign bit: `X` +* Exponent bits: `00000000000` +* Mantissa bits: `0000000000000000000000000000000000000000000000000000` + +where `X` means `0` or `1`. + +## NaN + +There are 2 kinds of NaNs that are represented: + +1. QNaNs (Quiet NaNs): represent the result of indeterminate operations. +2. SNaNs (Signalling NaNs): represent the result of invalid operations. + +### QNaNs + +QNaNs are represented like this: + +* Sign bit: `X` +* Exponent bits: `11111111111` +* Mantissa bits: `1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` + +where `X` means `0` or `1`. + +### SNaNs + +SNaNs are represented like this: + +* Sign bit: `X` +* Exponent bits: `11111111111` +* Mantissa bits: `0XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1` + +where `X` means `0` or `1`. + +## Infinite + +### Positive Infinite + +Positive infinite is represented like this: + +* Sign bit: `0` +* Exponent bits: `11111111111` +* Mantissa bits: `0000000000000000000000000000000000000000000000000000` + +where `X` means `0` or `1`. + +### Negative Infinite + +Negative infinite is represented like this: + +* Sign bit: `1` +* Exponent bits: `11111111111` +* Mantissa bits: `0000000000000000000000000000000000000000000000000000` + +where `X` means `0` or `1`. diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/ieee_754.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/ieee_754.py new file mode 100644 index 0000000..b1815c4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/ieee_754.py @@ -0,0 +1,106 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from ctypes import c_double, c_uint64 +from sys import float_info + +# IEEE 754 64-bit floating point numbers use 11 bits for the exponent and 52 +# bits for the mantissa. +MANTISSA_WIDTH = 52 +EXPONENT_WIDTH = 11 + +# This mask is equivalent to 52 "1" bits (there are 13 hexadecimal 4-bit "f"s +# in the mantissa mask, 13 * 4 == 52) or 0xfffffffffffff in hexadecimal. +MANTISSA_MASK = (1 << MANTISSA_WIDTH) - 1 + +# There are 11 bits for the exponent, but the exponent values 0 (11 "0" +# bits) and 2047 (11 "1" bits) have special meanings so the exponent range is +# from 1 to 2046. To calculate the exponent value, 1023 (the bias) is +# subtracted from the exponent, so the exponent value range is from -1022 to +# +1023. +EXPONENT_BIAS = (2 ** (EXPONENT_WIDTH - 1)) - 1 + +# All the exponent mask bits are set to 1 for the 11 exponent bits. +EXPONENT_MASK = ((1 << EXPONENT_WIDTH) - 1) << MANTISSA_WIDTH + +# The sign mask has the first bit set to 1 and the rest to 0. +SIGN_MASK = 1 << (EXPONENT_WIDTH + MANTISSA_WIDTH) + +# For normal floating point numbers, the exponent can have a value in the +# range [-1022, 1023]. +MIN_NORMAL_EXPONENT = -EXPONENT_BIAS + 1 +MAX_NORMAL_EXPONENT = EXPONENT_BIAS + +# The smallest possible normal value is 2.2250738585072014e-308. +# This value is the result of using the smallest possible number in the +# mantissa, 1.0000000000000000000000000000000000000000000000000000 (52 "0"s in +# the fractional part) and a single "1" in the exponent. +# Finally 1 * (2 ** -1022) = 2.2250738585072014e-308. +MIN_NORMAL_VALUE = float_info.min + +# Greatest possible normal value (1.7976931348623157e+308) +# The binary representation of a float in scientific notation uses (for the +# mantissa) one bit for the integer part (which is implicit) and 52 bits for +# the fractional part. Consider a float binary 1.111. It is equal to 1 + 1/2 + +# 1/4 + 1/8. The greatest possible value in the 52-bit binary mantissa would be +# then 1.1111111111111111111111111111111111111111111111111111 (52 "1"s in the +# fractional part) whose decimal value is 1.9999999999999998. Finally, +# 1.9999999999999998 * (2 ** 1023) = 1.7976931348623157e+308. +MAX_NORMAL_VALUE = float_info.max + + +def get_ieee_754_exponent(value: float) -> int: + """ + Gets the exponent of the IEEE 754 representation of a float. + """ + + return ( + ( + # This step gives the integer that corresponds to the IEEE 754 + # representation of a float. For example, consider + # -MAX_NORMAL_VALUE for an example. We choose this value because + # of its binary representation which makes easy to understand the + # subsequent operations. + # + # c_uint64.from_buffer(c_double(-MAX_NORMAL_VALUE)).value == 18442240474082181119 + # bin(18442240474082181119) == '0b1111111111101111111111111111111111111111111111111111111111111111' + # + # The first bit of the previous binary number is the sign bit: 1 (1 means negative, 0 means positive) + # The next 11 bits are the exponent bits: 11111111110 + # The next 52 bits are the mantissa bits: 1111111111111111111111111111111111111111111111111111 + # + # This step isolates the exponent bits, turning every bit outside + # of the exponent field (sign and mantissa bits) to 0. + c_uint64.from_buffer(c_double(value)).value & EXPONENT_MASK + # For the example this means: + # 18442240474082181119 & EXPONENT_MASK == 9214364837600034816 + # bin(9214364837600034816) == '0b111111111100000000000000000000000000000000000000000000000000000' + # Notice that the previous binary representation does not include + # leading zeroes, so the sign bit is not included since it is a + # zero. + ) + # This step moves the exponent bits to the right, removing the + # mantissa bits that were set to 0 by the previous step. This + # leaves the IEEE 754 exponent value, ready for the next step. + >> MANTISSA_WIDTH + # For the example this means: + # 9214364837600034816 >> MANTISSA_WIDTH == 2046 + # bin(2046) == '0b11111111110' + # As shown above, these are the original 11 bits that correspond to the + # exponent. + # This step subtracts the exponent bias from the IEEE 754 value, + # leaving the actual exponent value. + ) - EXPONENT_BIAS + # For the example this means: + # 2046 - EXPONENT_BIAS == 1023 + # As mentioned in a comment above, the largest value for the exponent is + + +def get_ieee_754_mantissa(value: float) -> int: + return ( + c_uint64.from_buffer(c_double(value)).value + # This step isolates the mantissa bits. There is no need to do any + # bit shifting as the mantissa bits are already the rightmost field + # in an IEEE 754 representation. + & MANTISSA_MASK + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/logarithm_mapping.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/logarithm_mapping.py new file mode 100644 index 0000000..a0641d7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/logarithm_mapping.py @@ -0,0 +1,123 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from math import exp, floor, ldexp, log +from threading import Lock + +from opentelemetry.sdk.metrics._internal.exponential_histogram.mapping import ( + Mapping, +) +from opentelemetry.sdk.metrics._internal.exponential_histogram.mapping.errors import ( + MappingOverflowError, + MappingUnderflowError, +) +from opentelemetry.sdk.metrics._internal.exponential_histogram.mapping.ieee_754 import ( + MAX_NORMAL_EXPONENT, + MIN_NORMAL_EXPONENT, + MIN_NORMAL_VALUE, + get_ieee_754_exponent, + get_ieee_754_mantissa, +) + + +class LogarithmMapping(Mapping): + # Reference implementation here: + # https://github.com/open-telemetry/opentelemetry-go/blob/0e6f9c29c10d6078e8131418e1d1d166c7195d61/sdk/metric/aggregator/exponential/mapping/logarithm/logarithm.go + + _mappings = {} + _mappings_lock = Lock() + + _min_scale = 1 + _max_scale = 20 + + def _get_min_scale(self): + # _min_scale ensures that ExponentMapping is used for zero and negative + # scale values. + return self._min_scale + + def _get_max_scale(self): + # _max_scale is 20. The OpenTelemetry specification requires that + # bucket indices fit within a signed 32-bit integer. At scale 20, + # the maximum bucket index is ((MAX_NORMAL_EXPONENT + 1) << 20) - 1, + # which fits within this range. At scale 21, the maximum bucket + # index reaches the upper limit of a signed 32-bit integer, making + # it difficult to test correctness. See: + # https://github.com/lightstep/otel-launcher-go/blob/c9ca8483be067a39ab306b09060446e7fda65f35/lightstep/sdk/metric/aggregator/histogram/structure/README.md#mapping-function + # https://github.com/open-telemetry/opentelemetry-go/blob/0e6f9c29c10d6078e8131418e1d1d166c7195d61/sdk/metric/aggregator/exponential/mapping/logarithm/logarithm.go#L32-L45 + return self._max_scale + + def _init(self, scale: int): + # pylint: disable=attribute-defined-outside-init + + super()._init(scale) + + # self._scale_factor is defined as a multiplier because multiplication + # is faster than division. self._scale_factor is defined as: + # index = log(value) * self._scale_factor + # Where: + # index = log(value) / log(base) + # index = log(value) / log(2 ** (2 ** -scale)) + # index = log(value) / ((2 ** -scale) * log(2)) + # index = log(value) * ((1 / log(2)) * (2 ** scale)) + # self._scale_factor = ((1 / log(2)) * (2 ** scale)) + # self._scale_factor = (1 /log(2)) * (2 ** scale) + # self._scale_factor = ldexp(1 / log(2), scale) + # This implementation was copied from a Java prototype. See: + # https://github.com/newrelic-experimental/newrelic-sketch-java/blob/1ce245713603d61ba3a4510f6df930a5479cd3f6/src/main/java/com/newrelic/nrsketch/indexer/LogIndexer.java + # for the equations used here. + self._scale_factor = ldexp(1 / log(2), scale) + + # self._min_normal_lower_boundary_index is the index such that + # base ** index == MIN_NORMAL_VALUE. An exponential histogram bucket + # with this index covers the range + # (MIN_NORMAL_VALUE, MIN_NORMAL_VALUE * base]. One less than this index + # corresponds with the bucket containing values <= MIN_NORMAL_VALUE. + self._min_normal_lower_boundary_index = MIN_NORMAL_EXPONENT << self._scale + + # self._max_normal_lower_boundary_index is the index such that + # base ** index equals the greatest representable lower boundary. An + # exponential histogram bucket with this index covers the range + # ((2 ** 1024) / base, 2 ** 1024], which includes opentelemetry.sdk. + # metrics._internal.exponential_histogram.ieee_754.MAX_NORMAL_VALUE. + # This bucket is incomplete, since the upper boundary cannot be + # represented. One greater than this index corresponds with the bucket + # containing values > 2 ** 1024. + self._max_normal_lower_boundary_index = ((MAX_NORMAL_EXPONENT + 1) << self._scale) - 1 + + def map_to_index(self, value: float) -> int: + """ + Maps positive floating point values to indexes corresponding to scale. + """ + + # value is subnormal + if value <= MIN_NORMAL_VALUE: + return self._min_normal_lower_boundary_index - 1 + + # value is an exact power of two. + if get_ieee_754_mantissa(value) == 0: + exponent = get_ieee_754_exponent(value) + return (exponent << self._scale) - 1 + + return min( + floor(log(value) * self._scale_factor), + self._max_normal_lower_boundary_index, + ) + + def get_lower_boundary(self, index: int) -> float: + if index >= self._max_normal_lower_boundary_index: + if index == self._max_normal_lower_boundary_index: + return 2 * exp((index - (1 << self._scale)) / self._scale_factor) + raise MappingOverflowError() + + if index <= self._min_normal_lower_boundary_index: + if index == self._min_normal_lower_boundary_index: + return MIN_NORMAL_VALUE + if index == self._min_normal_lower_boundary_index - 1: + return exp((index + (1 << self._scale)) / self._scale_factor) / 2 + raise MappingUnderflowError() + + return exp(index / self._scale_factor) + + @property + def scale(self) -> int: + return self._scale diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/export/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/export/__init__.py new file mode 100644 index 0000000..cb10405 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/export/__init__.py @@ -0,0 +1,546 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import math +import os +import weakref +from abc import ABC, abstractmethod +from collections.abc import Callable, Iterable +from enum import Enum +from logging import getLogger +from os import environ, linesep +from sys import stdout +from threading import Event, Lock, RLock, Thread +from time import perf_counter, time_ns +from typing import IO + +from typing_extensions import final + +# This kind of import is needed to avoid Sphinx errors. +import opentelemetry.sdk.metrics._internal +from opentelemetry.context import ( + _SUPPRESS_INSTRUMENTATION_KEY, + attach, + detach, + set_value, +) +from opentelemetry.metrics import MeterProvider, NoOpMeterProvider +from opentelemetry.sdk.environment_variables import ( + OTEL_METRIC_EXPORT_INTERVAL, + OTEL_METRIC_EXPORT_TIMEOUT, + OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, +) +from opentelemetry.sdk.environment_variables._internal import ( + parse_boolean_environment_variable, +) +from opentelemetry.sdk.metrics._internal.aggregation import ( + AggregationTemporality, + DefaultAggregation, +) +from opentelemetry.sdk.metrics._internal.exceptions import MetricsTimeoutError +from opentelemetry.sdk.metrics._internal.instrument import ( + Counter, + Gauge, + Histogram, + ObservableCounter, + ObservableGauge, + ObservableUpDownCounter, + UpDownCounter, + _Counter, + _Gauge, + _Histogram, + _ObservableCounter, + _ObservableGauge, + _ObservableUpDownCounter, + _UpDownCounter, +) +from opentelemetry.sdk.metrics._internal.point import MetricsData +from opentelemetry.semconv._incubating.attributes.otel_attributes import ( + OtelComponentTypeValues, +) +from opentelemetry.util._once import Once + +from ._metric_reader_metrics import create_metric_reader_metrics + +_logger = getLogger(__name__) + + +class MetricExportResult(Enum): + """Result of exporting a metric + + Can be any of the following values:""" + + SUCCESS = 0 + FAILURE = 1 + + +class MetricExporter(ABC): + """Interface for exporting metrics. + + Interface to be implemented by services that want to export metrics received + in their own format. + + Args: + preferred_temporality: Used by `opentelemetry.sdk.metrics.export.PeriodicExportingMetricReader` to + configure exporter level preferred temporality. See `opentelemetry.sdk.metrics.export.MetricReader` for + more details on what preferred temporality is. + preferred_aggregation: Used by `opentelemetry.sdk.metrics.export.PeriodicExportingMetricReader` to + configure exporter level preferred aggregation. See `opentelemetry.sdk.metrics.export.MetricReader` for + more details on what preferred aggregation is. + """ + + def __init__( + self, + preferred_temporality: dict[type, AggregationTemporality] | None = None, + preferred_aggregation: dict[type, opentelemetry.sdk.metrics.view.Aggregation] | None = None, + ) -> None: + self._preferred_temporality = preferred_temporality + self._preferred_aggregation = preferred_aggregation + + @abstractmethod + def export( + self, + metrics_data: MetricsData, + timeout_millis: float = 10_000, + **kwargs, + ) -> MetricExportResult: + """Exports a batch of telemetry data. + + Args: + metrics: The list of `opentelemetry.sdk.metrics.export.Metric` objects to be exported + + Returns: + The result of the export + """ + + @abstractmethod + def force_flush(self, timeout_millis: float = 10_000) -> bool: + """ + Ensure that export of any metrics currently received by the exporter + are completed as soon as possible. + """ + + @abstractmethod + def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None: + """Shuts down the exporter. + + Called when the SDK is shut down. + """ + + +class ConsoleMetricExporter(MetricExporter): + """Implementation of :class:`MetricExporter` that prints metrics to the + console. + + This class can be used for diagnostic purposes. It prints the exported + metrics to the console STDOUT. + """ + + def __init__( + self, + out: IO = stdout, + formatter: Callable[[MetricsData], str] = lambda metrics_data: metrics_data.to_json() + linesep, + preferred_temporality: dict[type, AggregationTemporality] | None = None, + preferred_aggregation: dict[type, opentelemetry.sdk.metrics.view.Aggregation] | None = None, + ): + super().__init__( + preferred_temporality=preferred_temporality, + preferred_aggregation=preferred_aggregation, + ) + self.out = out + self.formatter = formatter + + def export( + self, + metrics_data: MetricsData, + timeout_millis: float = 10_000, + **kwargs, + ) -> MetricExportResult: + self.out.write(self.formatter(metrics_data)) + self.out.flush() + return MetricExportResult.SUCCESS + + def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None: + pass + + def force_flush(self, timeout_millis: float = 10_000) -> bool: + return True + + +class MetricReader(ABC): + # pylint: disable=too-many-branches,broad-exception-raised + """ + Base class for all metric readers + + Args: + preferred_temporality: A mapping between instrument classes and + aggregation temporality. By default uses CUMULATIVE for all instrument + classes. This mapping will be used to define the default aggregation + temporality of every instrument class. If the user wants to make a + change in the default aggregation temporality of an instrument class, + it is enough to pass here a dictionary whose keys are the instrument + classes and the values are the corresponding desired aggregation + temporalities of the classes that the user wants to change, not all of + them. The classes not included in the passed dictionary will retain + their association to their default aggregation temporalities. + preferred_aggregation: A mapping between instrument classes and + aggregation instances. By default maps all instrument classes to an + instance of `DefaultAggregation`. This mapping will be used to + define the default aggregation of every instrument class. If the + user wants to make a change in the default aggregation of an + instrument class, it is enough to pass here a dictionary whose keys + are the instrument classes and the values are the corresponding + desired aggregation for the instrument classes that the user wants + to change, not necessarily all of them. The classes not included in + the passed dictionary will retain their association to their + default aggregations. The aggregation defined here will be + overridden by an aggregation defined by a view that is not + `DefaultAggregation`. + + .. document protected _receive_metrics which is a intended to be overridden by subclass + .. automethod:: _receive_metrics + """ + + def __init__( + self, + preferred_temporality: dict[type, AggregationTemporality] | None = None, + preferred_aggregation: dict[type, opentelemetry.sdk.metrics.view.Aggregation] | None = None, + *, + otel_component_type: OtelComponentTypeValues | None = None, + ) -> None: + self._collect: ( + Callable[ + [ + opentelemetry.sdk.metrics.export.MetricReader, + AggregationTemporality, + ], + Iterable[opentelemetry.sdk.metrics.export.Metric], + ] + | None + ) = None + + self._instrument_class_temporality = { + _Counter: AggregationTemporality.CUMULATIVE, + _UpDownCounter: AggregationTemporality.CUMULATIVE, + _Histogram: AggregationTemporality.CUMULATIVE, + _Gauge: AggregationTemporality.CUMULATIVE, + _ObservableCounter: AggregationTemporality.CUMULATIVE, + _ObservableUpDownCounter: AggregationTemporality.CUMULATIVE, + _ObservableGauge: AggregationTemporality.CUMULATIVE, + } + + if preferred_temporality is not None: + for temporality in preferred_temporality.values(): + if temporality not in ( + AggregationTemporality.CUMULATIVE, + AggregationTemporality.DELTA, + ): + raise Exception(f"Invalid temporality value found {temporality}") + + if preferred_temporality is not None: + for typ, temporality in preferred_temporality.items(): + if typ is Counter: + self._instrument_class_temporality[_Counter] = temporality + elif typ is UpDownCounter: + self._instrument_class_temporality[_UpDownCounter] = temporality + elif typ is Histogram: + self._instrument_class_temporality[_Histogram] = temporality + elif typ is Gauge: + self._instrument_class_temporality[_Gauge] = temporality + elif typ is ObservableCounter: + self._instrument_class_temporality[_ObservableCounter] = temporality + elif typ is ObservableUpDownCounter: + self._instrument_class_temporality[_ObservableUpDownCounter] = temporality + elif typ is ObservableGauge: + self._instrument_class_temporality[_ObservableGauge] = temporality + else: + raise Exception(f"Invalid instrument class found {typ}") + + self._preferred_temporality = preferred_temporality + self._instrument_class_aggregation = { + _Counter: DefaultAggregation(), + _UpDownCounter: DefaultAggregation(), + _Histogram: DefaultAggregation(), + _Gauge: DefaultAggregation(), + _ObservableCounter: DefaultAggregation(), + _ObservableUpDownCounter: DefaultAggregation(), + _ObservableGauge: DefaultAggregation(), + } + + if preferred_aggregation is not None: + for typ, aggregation in preferred_aggregation.items(): + if typ is Counter: + self._instrument_class_aggregation[_Counter] = aggregation + elif typ is UpDownCounter: + self._instrument_class_aggregation[_UpDownCounter] = aggregation + elif typ is Histogram: + self._instrument_class_aggregation[_Histogram] = aggregation + elif typ is Gauge: + self._instrument_class_aggregation[_Gauge] = aggregation + elif typ is ObservableCounter: + self._instrument_class_aggregation[_ObservableCounter] = aggregation + elif typ is ObservableUpDownCounter: + self._instrument_class_aggregation[_ObservableUpDownCounter] = aggregation + elif typ is ObservableGauge: + self._instrument_class_aggregation[_ObservableGauge] = aggregation + else: + raise Exception(f"Invalid instrument class found {typ}") + + self._otel_component_type = otel_component_type.value if otel_component_type else type(self).__qualname__ + self._metrics = create_metric_reader_metrics( + self._otel_component_type, + NoOpMeterProvider(), + parse_boolean_environment_variable(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED), + ) + + @final + def collect(self, timeout_millis: float = 10_000) -> None: + """Collects the metrics from the internal SDK state and + invokes the `_receive_metrics` with the collection. + + Args: + timeout_millis: Amount of time in milliseconds before this function + raises a timeout error. + + If any of the underlying ``collect`` methods called by this method + fails by any reason (including timeout) an exception will be raised + detailing the individual errors that caused this function to fail. + """ + if self._collect is None: + _logger.warning("Cannot call collect on a MetricReader until it is registered on a MeterProvider") + return + + start_time = perf_counter() + try: + metrics = self._collect(self, timeout_millis=timeout_millis) + finally: + self._metrics.record_collection(perf_counter() - start_time) + + if metrics is not None: + self._receive_metrics( + metrics, + timeout_millis=timeout_millis, + ) + + @final + def _set_collect_callback( + self, + func: Callable[ + [ + opentelemetry.sdk.metrics.export.MetricReader, + AggregationTemporality, + ], + MetricsData, + ] + | None, + ) -> None: + """This function is internal to the SDK. It should not be called or overridden by users""" + self._collect = func + + @abstractmethod + def _receive_metrics( + self, + metrics_data: MetricsData, + timeout_millis: float = 10_000, + **kwargs, + ) -> None: + """Called by `MetricReader.collect` when it receives a batch of metrics""" + + def _set_meter_provider(self, meter_provider: MeterProvider) -> None: + self._metrics = create_metric_reader_metrics( + self._otel_component_type, + meter_provider, + parse_boolean_environment_variable(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED), + ) + + def force_flush(self, timeout_millis: float = 10_000) -> bool: + self.collect(timeout_millis=timeout_millis) + return True + + @abstractmethod + def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None: + """Shuts down the MetricReader. This method provides a way + for the MetricReader to do any cleanup required. A metric reader can + only be shutdown once, any subsequent calls are ignored and return + failure status. + + When a `MetricReader` is registered on a + :class:`~opentelemetry.sdk.metrics.MeterProvider`, + :meth:`~opentelemetry.sdk.metrics.MeterProvider.shutdown` will invoke this + automatically. + """ + + +class InMemoryMetricReader(MetricReader): + """Implementation of `MetricReader` that returns its metrics from :func:`get_metrics_data`. + + This is useful for e.g. unit tests. + """ + + def __init__( + self, + preferred_temporality: dict[type, AggregationTemporality] | None = None, + preferred_aggregation: dict[type, opentelemetry.sdk.metrics.view.Aggregation] | None = None, + ) -> None: + super().__init__( + preferred_temporality=preferred_temporality, + preferred_aggregation=preferred_aggregation, + ) + self._lock = RLock() + self._metrics_data: MetricsData | None = None + + def get_metrics_data( + self, + ) -> MetricsData | None: + """Reads and returns current metrics from the SDK""" + with self._lock: + self.collect() + metrics_data = self._metrics_data + self._metrics_data = None + return metrics_data + + def _receive_metrics( + self, + metrics_data: MetricsData, + timeout_millis: float = 10_000, + **kwargs, + ) -> None: + with self._lock: + self._metrics_data = metrics_data + + def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None: + pass + + +class PeriodicExportingMetricReader(MetricReader): + """`PeriodicExportingMetricReader` is an implementation of `MetricReader` + that collects metrics based on a user-configurable time interval, and passes the + metrics to the configured exporter. If the time interval is set to `math.inf`, the + reader will not invoke periodic collection. + + The configured exporter's :py:meth:`~MetricExporter.export` method will not be called + concurrently. + """ + + def __init__( + self, + exporter: MetricExporter, + export_interval_millis: float | None = None, + export_timeout_millis: float | None = None, + ) -> None: + # PeriodicExportingMetricReader defers to exporter for configuration + super().__init__( + preferred_temporality=exporter._preferred_temporality, + preferred_aggregation=exporter._preferred_aggregation, + otel_component_type=OtelComponentTypeValues.PERIODIC_METRIC_READER, + ) + + # This lock is held whenever calling self._exporter.export() to prevent concurrent + # execution of MetricExporter.export() + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#exportbatch + self._export_lock = Lock() + + self._exporter = exporter + if export_interval_millis is None: + try: + export_interval_millis = float(environ.get(OTEL_METRIC_EXPORT_INTERVAL, 60000)) + except ValueError: + _logger.warning("Found invalid value for export interval, using default") + export_interval_millis = 60000 + if export_timeout_millis is None: + try: + export_timeout_millis = float(environ.get(OTEL_METRIC_EXPORT_TIMEOUT, 30000)) + except ValueError: + _logger.warning("Found invalid value for export timeout, using default") + export_timeout_millis = 30000 + self._export_interval_millis = export_interval_millis + self._export_timeout_millis = export_timeout_millis + self._shutdown = False + self._shutdown_event = Event() + self._shutdown_once = Once() + self._daemon_thread = None + if self._export_interval_millis > 0 and self._export_interval_millis < math.inf: + self._daemon_thread = Thread( + name="OtelPeriodicExportingMetricReader", + target=self._ticker, + daemon=True, + ) + self._daemon_thread.start() + if hasattr(os, "register_at_fork"): + weak_at_fork = weakref.WeakMethod(self._at_fork_reinit) + + def _after_in_child() -> None: + if at_fork := weak_at_fork(): + at_fork() + + os.register_at_fork(after_in_child=_after_in_child) + elif self._export_interval_millis <= 0: + raise ValueError( + f"interval value {self._export_interval_millis} is invalid \ + and needs to be larger than zero." + ) + + def _at_fork_reinit(self): + self._daemon_thread = Thread( + name="OtelPeriodicExportingMetricReader", + target=self._ticker, + daemon=True, + ) + self._daemon_thread.start() + + def _ticker(self) -> None: + interval_secs = self._export_interval_millis / 1e3 + while not self._shutdown_event.wait(interval_secs): + try: + self.collect(timeout_millis=self._export_timeout_millis) + except MetricsTimeoutError: + _logger.warning( + "Metric collection timed out. Will try again after %s seconds", + interval_secs, + exc_info=True, + ) + # one last collection below before shutting down completely + try: + self.collect(timeout_millis=self._export_interval_millis) + except MetricsTimeoutError: + _logger.warning( + "Metric collection timed out.", + exc_info=True, + ) + + def _receive_metrics( + self, + metrics_data: MetricsData, + timeout_millis: float = 10_000, + **kwargs, + ) -> None: + token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True)) + # pylint: disable=broad-exception-caught,invalid-name + try: + with self._export_lock: + self._exporter.export(metrics_data, timeout_millis=timeout_millis) + except Exception: + _logger.exception("Exception while exporting metrics") + detach(token) + + def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None: + deadline_ns = time_ns() + timeout_millis * 10**6 + + def _shutdown(): + self._shutdown = True + + did_set = self._shutdown_once.do_once(_shutdown) + if not did_set: + _logger.warning("Can't shutdown multiple times") + return + + self._shutdown_event.set() + if self._daemon_thread: + self._daemon_thread.join(timeout=(deadline_ns - time_ns()) / 10**9) + self._exporter.shutdown(timeout=(deadline_ns - time_ns()) / 10**6) + + def force_flush(self, timeout_millis: float = 10_000) -> bool: + super().force_flush(timeout_millis=timeout_millis) + self._exporter.force_flush(timeout_millis=timeout_millis) + return True diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/export/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/export/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..07ab096 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/export/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/export/__pycache__/_metric_reader_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/export/__pycache__/_metric_reader_metrics.cpython-312.pyc new file mode 100644 index 0000000..f05e191 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/export/__pycache__/_metric_reader_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/export/_metric_reader_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/export/_metric_reader_metrics.py new file mode 100644 index 0000000..019f1cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/export/_metric_reader_metrics.py @@ -0,0 +1,54 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from collections import Counter +from typing import Protocol + +from opentelemetry.metrics import MeterProvider +from opentelemetry.semconv._incubating.attributes.otel_attributes import ( + OTEL_COMPONENT_NAME, + OTEL_COMPONENT_TYPE, +) +from opentelemetry.semconv._incubating.metrics.otel_metrics import ( + create_otel_sdk_metric_reader_collection_duration, +) + +_component_counter = Counter() + + +class MetricReaderMetricsT(Protocol): + def record_collection(self, duration: float) -> None: ... + + +class NoOpMetricReaderMetrics: + def record_collection(self, duration: float) -> None: + pass + + +class MetricReaderMetrics: + def __init__(self, component_type: str, meter_provider: MeterProvider) -> None: + meter = meter_provider.get_meter("opentelemetry-sdk") + + count = _component_counter[component_type] + _component_counter[component_type] = count + 1 + + self._standard_attrs = { + OTEL_COMPONENT_TYPE: component_type, + OTEL_COMPONENT_NAME: f"{component_type}/{count}", + } + + self._collection_duration = create_otel_sdk_metric_reader_collection_duration(meter) + + def record_collection(self, duration: float) -> None: + self._collection_duration.record(duration, self._standard_attrs) + + +def create_metric_reader_metrics( + component_type: str, + meter_provider: MeterProvider, + enabled: bool, +) -> MetricReaderMetricsT: + if not enabled: + return NoOpMetricReaderMetrics() + + return MetricReaderMetrics(component_type, meter_provider) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/instrument.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/instrument.py new file mode 100644 index 0000000..872715b --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/instrument.py @@ -0,0 +1,400 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# pylint: disable=too-many-ancestors, unused-import +from __future__ import annotations + +import math +from collections.abc import Generator, Iterable, Sequence +from logging import getLogger +from time import time_ns +from typing import ( + TYPE_CHECKING, + Protocol, + cast, + runtime_checkable, +) + +# This kind of import is needed to avoid Sphinx errors. +from opentelemetry.context import Context, get_current +from opentelemetry.metrics import Asynchronous, CallbackT, Synchronous +from opentelemetry.metrics import Counter as APICounter +from opentelemetry.metrics import Histogram as APIHistogram +from opentelemetry.metrics import ObservableCounter as APIObservableCounter +from opentelemetry.metrics import ObservableGauge as APIObservableGauge +from opentelemetry.metrics import ( + ObservableUpDownCounter as APIObservableUpDownCounter, +) +from opentelemetry.metrics import UpDownCounter as APIUpDownCounter +from opentelemetry.metrics import _Gauge as APIGauge +from opentelemetry.metrics._internal.instrument import ( + CallbackOptions, + _MetricsHistogramAdvisory, +) +from opentelemetry.sdk.metrics._internal.measurement import Measurement +from opentelemetry.util.types import Attributes + +if TYPE_CHECKING: + from opentelemetry.sdk.metrics._internal import ( + _ProxyMeterConfig, + ) + from opentelemetry.sdk.metrics._internal.measurement_consumer import ( + MeasurementConsumer, + ) + from opentelemetry.sdk.util.instrumentation import InstrumentationScope + + +_logger = getLogger(__name__) + + +_NAME_ERROR_MESSAGE = "Expected ASCII string of maximum length 255 characters but got {}" +_UNIT_ERROR_MESSAGE = "Expected ASCII string of maximum length 63 characters but got {}" + + +@runtime_checkable +class _Instrument(Protocol): + name: str + unit: str + description: str + instrumentation_scope: InstrumentationScope + + +class _Synchronous(_Instrument, Synchronous): + def __init__( + self, + name: str, + instrumentation_scope: InstrumentationScope, + measurement_consumer: MeasurementConsumer, + unit: str = "", + description: str = "", + *, + _meter_config: _ProxyMeterConfig | None = None, + ): + # pylint: disable=no-member + result = self._check_name_unit_description(name, unit, description) + + if result["name"] is None: + # pylint: disable=broad-exception-raised + raise Exception(_NAME_ERROR_MESSAGE.format(name)) + + if result["unit"] is None: + # pylint: disable=broad-exception-raised + raise Exception(_UNIT_ERROR_MESSAGE.format(unit)) + + name = result["name"] + unit = result["unit"] + description = cast(str, result["description"]) + + self.name = name.lower() + self.unit = unit + self.description = description + self.instrumentation_scope = instrumentation_scope + self._measurement_consumer = measurement_consumer + self._meter_config = _meter_config + super().__init__(name, unit=unit, description=description) + + def _is_enabled(self) -> bool: + return self._meter_config is None or self._meter_config.is_enabled + + +class _Asynchronous(_Instrument, Asynchronous): + def __init__( + self, + name: str, + instrumentation_scope: InstrumentationScope, + measurement_consumer: MeasurementConsumer, + callbacks: Sequence[CallbackT] | None = None, + unit: str = "", + description: str = "", + *, + _meter_config: _ProxyMeterConfig | None = None, + ): + # pylint: disable=no-member + result = self._check_name_unit_description(name, unit, description) + + if result["name"] is None: + # pylint: disable=broad-exception-raised + raise Exception(_NAME_ERROR_MESSAGE.format(name)) + + if result["unit"] is None: + # pylint: disable=broad-exception-raised + raise Exception(_UNIT_ERROR_MESSAGE.format(unit)) + + name = result["name"] + unit = result["unit"] + description = cast(str, result["description"]) + + self.name = name.lower() + self.unit = unit + self.description = description + self.instrumentation_scope = instrumentation_scope + self._measurement_consumer = measurement_consumer + self._meter_config = _meter_config + super().__init__(name, callbacks, unit=unit, description=description) + + self._callbacks: list[CallbackT] = [] + + if callbacks is not None: + for callback in callbacks: + if isinstance(callback, Generator): + # advance generator to it's first yield + next(callback) + + def inner( + options: CallbackOptions, + callback=callback, + ) -> Iterable[Measurement]: + try: + return callback.send(options) + except StopIteration: + return [] + + self._callbacks.append(inner) + else: + self._callbacks.append(callback) + + def _is_enabled(self) -> bool: + return self._meter_config is None or self._meter_config.is_enabled + + def callback(self, callback_options: CallbackOptions) -> Iterable[Measurement]: + if not self._is_enabled(): + return + for callback in self._callbacks: + try: + for api_measurement in callback(callback_options): + if not math.isfinite(api_measurement.value): + _logger.warning( + "Callback returned a non-finite value %s for instrument %s, ignoring measurement.", + api_measurement.value, + self.name, + ) + continue + yield Measurement( + api_measurement.value, + time_unix_nano=time_ns(), + instrument=self, + context=api_measurement.context or get_current(), + attributes=api_measurement.attributes, + ) + except Exception: # pylint: disable=broad-exception-caught + _logger.exception("Callback failed for instrument %s.", self.name) + + +class Counter(_Synchronous, APICounter): + def __new__(cls, *args, **kwargs): + if cls is Counter: + raise TypeError("Counter must be instantiated via a meter.") + return super().__new__(cls) + + def add( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ): + if not self._is_enabled(): + super().add(amount, attributes=attributes, context=context) + return + + if not math.isfinite(amount): + _logger.warning( + "Add amount %s is not finite on Counter %s, ignoring measurement.", + amount, + self.name, + ) + return + if amount < 0: + _logger.warning("Add amount must be non-negative on Counter %s.", self.name) + return + time_unix_nano = time_ns() + self._measurement_consumer.consume_measurement( + Measurement( + amount, + time_unix_nano, + self, + context or get_current(), + attributes, + ) + ) + + +class UpDownCounter(_Synchronous, APIUpDownCounter): + def __new__(cls, *args, **kwargs): + if cls is UpDownCounter: + raise TypeError("UpDownCounter must be instantiated via a meter.") + return super().__new__(cls) + + def add( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ): + if not self._is_enabled(): + super().add(amount, attributes=attributes, context=context) + return + + if not math.isfinite(amount): + _logger.warning( + "Add amount %s is not finite on UpDownCounter %s, ignoring measurement.", + amount, + self.name, + ) + return + time_unix_nano = time_ns() + self._measurement_consumer.consume_measurement( + Measurement( + amount, + time_unix_nano, + self, + context or get_current(), + attributes, + ) + ) + + +class ObservableCounter(_Asynchronous, APIObservableCounter): + def __new__(cls, *args, **kwargs): + if cls is ObservableCounter: + raise TypeError("ObservableCounter must be instantiated via a meter.") + return super().__new__(cls) + + +class ObservableUpDownCounter(_Asynchronous, APIObservableUpDownCounter): + def __new__(cls, *args, **kwargs): + if cls is ObservableUpDownCounter: + raise TypeError("ObservableUpDownCounter must be instantiated via a meter.") + return super().__new__(cls) + + +class Histogram(_Synchronous, APIHistogram): + def __init__( + self, + name: str, + instrumentation_scope: InstrumentationScope, + measurement_consumer: MeasurementConsumer, + unit: str = "", + description: str = "", + explicit_bucket_boundaries_advisory: Sequence[float] | None = None, + *, + _meter_config: _ProxyMeterConfig | None = None, + ): + super().__init__( + name, + unit=unit, + description=description, + instrumentation_scope=instrumentation_scope, + measurement_consumer=measurement_consumer, + _meter_config=_meter_config, + ) + self._advisory = _MetricsHistogramAdvisory(explicit_bucket_boundaries=explicit_bucket_boundaries_advisory) + + def __new__(cls, *args, **kwargs): + if cls is Histogram: + raise TypeError("Histogram must be instantiated via a meter.") + return super().__new__(cls) + + def record( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ): + if not self._is_enabled(): + super().record(amount, attributes=attributes, context=context) + return + + if not math.isfinite(amount): + _logger.warning( + "Record amount %s is not finite on Histogram %s, ignoring measurement.", + amount, + self.name, + ) + return + if amount < 0: + _logger.warning( + "Record amount must be non-negative on Histogram %s.", + self.name, + ) + return + time_unix_nano = time_ns() + self._measurement_consumer.consume_measurement( + Measurement( + amount, + time_unix_nano, + self, + context or get_current(), + attributes, + ) + ) + + +class Gauge(_Synchronous, APIGauge): + def __new__(cls, *args, **kwargs): + if cls is Gauge: + raise TypeError("Gauge must be instantiated via a meter.") + return super().__new__(cls) + + def set( + self, + amount: float, + attributes: Attributes = None, + context: Context | None = None, + ): + if not self._is_enabled(): + super().set(amount, attributes=attributes, context=context) + return + + if not math.isfinite(amount): + _logger.warning( + "Set amount %s is not finite on Gauge %s, ignoring measurement.", + amount, + self.name, + ) + return + time_unix_nano = time_ns() + self._measurement_consumer.consume_measurement( + Measurement( + amount, + time_unix_nano, + self, + context or get_current(), + attributes, + ) + ) + + +class ObservableGauge(_Asynchronous, APIObservableGauge): + def __new__(cls, *args, **kwargs): + if cls is ObservableGauge: + raise TypeError("ObservableGauge must be instantiated via a meter.") + return super().__new__(cls) + + +# Below classes exist to prevent the direct instantiation +class _Counter(Counter): + pass + + +class _UpDownCounter(UpDownCounter): + pass + + +class _ObservableCounter(ObservableCounter): + pass + + +class _ObservableUpDownCounter(ObservableUpDownCounter): + pass + + +class _Histogram(Histogram): + pass + + +class _Gauge(Gauge): + pass + + +class _ObservableGauge(ObservableGauge): + pass diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/measurement.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/measurement.py new file mode 100644 index 0000000..4e9d5ee --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/measurement.py @@ -0,0 +1,54 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections.abc import Mapping +from dataclasses import dataclass +from logging import getLogger +from typing import TYPE_CHECKING + +from opentelemetry.attributes import _clean_attribute_value +from opentelemetry.context import Context +from opentelemetry.util.types import Attributes + +_logger = getLogger(__name__) + +if TYPE_CHECKING: + from opentelemetry.sdk.metrics._internal.instrument import _Instrument + + +@dataclass(frozen=True) +class Measurement: + """ + Represents a data point reported via the metrics API to the SDK. + + Attributes: + value: Measured value + time_unix_nano: The time the API call was made to record the Measurement + instrument: The instrument that produced this `Measurement`. + context: The active Context of the Measurement at API call time. + attributes: Measurement attributes. Mutating mutable attribute values (lists or dicts) + outside of the Measurement object can lead to undefined and bad behavior. + """ + + value: int | float + time_unix_nano: int + instrument: _Instrument + context: Context + attributes: Attributes = None + + def __post_init__(self) -> None: + if self.attributes is not None: + if isinstance(self.attributes, Mapping): + object.__setattr__( + self, + "attributes", + _clean_attribute_value(self.attributes, None), + ) + else: + _logger.warning( + "Invalid type '%s' for attributes. Expected a Mapping or None.", + type(self.attributes), + ) + object.__setattr__(self, "attributes", None) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/measurement_consumer.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/measurement_consumer.py new file mode 100644 index 0000000..a332418 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/measurement_consumer.py @@ -0,0 +1,140 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# pylint: disable=unused-import + +from abc import ABC, abstractmethod +from collections.abc import Iterable, Mapping +from threading import Lock +from time import time_ns + +# This kind of import is needed to avoid Sphinx errors. +import opentelemetry.sdk.metrics +import opentelemetry.sdk.metrics._internal.instrument +from opentelemetry.metrics._internal.instrument import CallbackOptions +from opentelemetry.sdk.metrics._internal.exceptions import MetricsTimeoutError +from opentelemetry.sdk.metrics._internal.measurement import Measurement +from opentelemetry.sdk.metrics._internal.metric_reader_storage import ( + MetricReaderStorage, +) +from opentelemetry.sdk.metrics._internal.point import MetricsData + + +class MeasurementConsumer(ABC): + @abstractmethod + def consume_measurement(self, measurement: Measurement) -> None: + pass + + @abstractmethod + def register_asynchronous_instrument( + self, + instrument: ("opentelemetry.sdk.metrics._internal.instrument._Asynchronous"), + ): + pass + + @abstractmethod + def collect( + self, + metric_reader: "opentelemetry.sdk.metrics.export.MetricReader", + timeout_millis: float = 10_000, + ) -> MetricsData | None: + pass + + +class SynchronousMeasurementConsumer(MeasurementConsumer): + def __init__( + self, + sdk_config: "opentelemetry.sdk.metrics._internal.sdk_configuration.SdkConfiguration", + metric_readers: Iterable["opentelemetry.sdk.metrics.MetricReader"], + ) -> None: + self._lock = Lock() + self._sdk_config = sdk_config + self._reader_storages: Mapping[opentelemetry.sdk.metrics.export.MetricReader, MetricReaderStorage] = { + reader: MetricReaderStorage( + sdk_config, + reader._instrument_class_temporality, + reader._instrument_class_aggregation, + ) + for reader in metric_readers + } + self._async_instruments: list[opentelemetry.sdk.metrics._internal.instrument._Asynchronous] = [] + + def consume_measurement(self, measurement: Measurement) -> None: + should_sample_exemplar = self._sdk_config.exemplar_filter.should_sample( + measurement.value, + measurement.time_unix_nano, + measurement.attributes, + measurement.context, + ) + # `_reader_storages` is replaced (never mutated in place) by + # `add_metric_reader` and `remove_metric_reader`, so it is safe + # to iterate over without a lock. + for reader_storage in self._reader_storages.values(): + reader_storage.consume_measurement(measurement, should_sample_exemplar) + + def register_asynchronous_instrument( + self, + instrument: ("opentelemetry.sdk.metrics._internal.instrument._Asynchronous"), + ) -> None: + with self._lock: + self._async_instruments.append(instrument) + + def collect( + self, + metric_reader: "opentelemetry.sdk.metrics.export.MetricReader", + timeout_millis: float = 10_000, + ) -> MetricsData | None: + with self._lock: + metric_reader_storage = self._reader_storages[metric_reader] + # for now, just use the defaults + callback_options = CallbackOptions() + deadline_ns = time_ns() + (timeout_millis * 1e6) + + default_timeout_ns = 10000 * 1e6 + + for async_instrument in self._async_instruments: + remaining_time = deadline_ns - time_ns() + + if remaining_time < default_timeout_ns: + callback_options = CallbackOptions(timeout_millis=remaining_time / 1e6) + + measurements = async_instrument.callback(callback_options) + if time_ns() >= deadline_ns: + raise MetricsTimeoutError("Timed out while executing callback") + + for measurement in measurements: + should_sample_exemplar = self._sdk_config.exemplar_filter.should_sample( + measurement.value, + measurement.time_unix_nano, + measurement.attributes, + measurement.context, + ) + metric_reader_storage.consume_measurement(measurement, should_sample_exemplar) + + result = self._reader_storages[metric_reader].collect() + + return result + + def add_metric_reader(self, metric_reader: "opentelemetry.sdk.metrics.MetricReader") -> None: + """Registers a new metric reader.""" + # Build a new mapping and swap it in atomically so that + # a concurrent consume_measurement never iterates a mapping + # that is being mutated in place. + with self._lock: + new_reader_storages = dict(self._reader_storages) + new_reader_storages[metric_reader] = MetricReaderStorage( + self._sdk_config, + # pylint: disable-next=protected-access + metric_reader._instrument_class_temporality, + # pylint: disable-next=protected-access + metric_reader._instrument_class_aggregation, + ) + self._reader_storages = new_reader_storages + + def remove_metric_reader(self, metric_reader: "opentelemetry.sdk.metrics.MetricReader") -> None: + """Unregisters the given metric reader.""" + # Mutate using copy-on-write: see add_metric_reader. + with self._lock: + new_reader_storages = dict(self._reader_storages) + new_reader_storages.pop(metric_reader) + self._reader_storages = new_reader_storages diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/metric_reader_storage.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/metric_reader_storage.py new file mode 100644 index 0000000..d0ef125 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/metric_reader_storage.py @@ -0,0 +1,265 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from logging import getLogger +from threading import RLock +from time import time_ns + +from opentelemetry.metrics import ( + Asynchronous, + Counter, + ObservableCounter, +) +from opentelemetry.sdk.metrics._internal._view_instrument_match import ( + _ViewInstrumentMatch, +) +from opentelemetry.sdk.metrics._internal.aggregation import ( + Aggregation, + AggregationTemporality, + ExplicitBucketHistogramAggregation, + ExponentialBucketHistogramAggregation, + _DropAggregation, + _ExplicitBucketHistogramAggregation, + _ExponentialBucketHistogramAggregation, + _LastValueAggregation, + _SumAggregation, +) +from opentelemetry.sdk.metrics._internal.instrument import _Instrument +from opentelemetry.sdk.metrics._internal.measurement import Measurement +from opentelemetry.sdk.metrics._internal.point import ( + ExponentialHistogram, + Gauge, + Histogram, + Metric, + MetricsData, + ResourceMetrics, + ScopeMetrics, + Sum, +) +from opentelemetry.sdk.metrics._internal.sdk_configuration import ( + SdkConfiguration, +) +from opentelemetry.sdk.metrics._internal.view import View +from opentelemetry.sdk.util.instrumentation import InstrumentationScope + +_logger = getLogger(__name__) + +_DEFAULT_VIEW = View(instrument_name="") + + +class MetricReaderStorage: + """The SDK's storage for a given reader""" + + def __init__( + self, + sdk_config: SdkConfiguration, + instrument_class_temporality: dict[type, AggregationTemporality], + instrument_class_aggregation: dict[type, Aggregation], + ) -> None: + self._lock = RLock() + self._sdk_config = sdk_config + self._instrument_view_instrument_matches: dict[_Instrument, list[_ViewInstrumentMatch]] = {} + self._instrument_class_temporality = instrument_class_temporality + self._instrument_class_aggregation = instrument_class_aggregation + + def _get_or_init_view_instrument_match(self, instrument: _Instrument) -> list[_ViewInstrumentMatch]: + # Optimistically get the relevant views for the given instrument. Once set for a given + # instrument, the mapping will never change + + if instrument in self._instrument_view_instrument_matches: + return self._instrument_view_instrument_matches[instrument] + + with self._lock: + # double check if it was set before we held the lock + if instrument in self._instrument_view_instrument_matches: + return self._instrument_view_instrument_matches[instrument] + + # not present, hold the lock and add a new mapping + view_instrument_matches = [] + + self._handle_view_instrument_match(instrument, view_instrument_matches) + + # if no view targeted the instrument, use the default + if not view_instrument_matches: + view_instrument_matches.append( + _ViewInstrumentMatch( + view=_DEFAULT_VIEW, + instrument=instrument, + instrument_class_aggregation=(self._instrument_class_aggregation), + ) + ) + self._instrument_view_instrument_matches[instrument] = view_instrument_matches + + return view_instrument_matches + + def consume_measurement(self, measurement: Measurement, should_sample_exemplar: bool = True) -> None: + for view_instrument_match in self._get_or_init_view_instrument_match(measurement.instrument): + view_instrument_match.consume_measurement(measurement, should_sample_exemplar) + + def collect(self) -> MetricsData | None: + # Use a list instead of yielding to prevent a slow reader from holding + # SDK locks + + # While holding the lock, new _ViewInstrumentMatch can't be added from + # another thread (so we are sure we collect all existing view). + # However, instruments can still send measurements that will make it + # into the individual aggregations; collection will acquire those locks + # iteratively to keep locking as fine-grained as possible. One side + # effect is that end times can be slightly skewed among the metric + # streams produced by the SDK, but we still align the output timestamps + # for a single instrument. + + collection_start_nanos = time_ns() + + with self._lock: + instrumentation_scope_scope_metrics: dict[InstrumentationScope, ScopeMetrics] = {} + + instrument_matches_snapshot = list(self._instrument_view_instrument_matches.items()) + + for ( + instrument, + view_instrument_matches, + ) in instrument_matches_snapshot: + aggregation_temporality = self._instrument_class_temporality[instrument.__class__] + + metrics: list[Metric] = [] + + for view_instrument_match in view_instrument_matches: + data_points = view_instrument_match.collect(aggregation_temporality, collection_start_nanos) + + if data_points is None: + continue + + if isinstance( + # pylint: disable=protected-access + view_instrument_match._aggregation, + _SumAggregation, + ): + data = Sum( + aggregation_temporality=aggregation_temporality, + data_points=data_points, + is_monotonic=isinstance(instrument, (Counter, ObservableCounter)), + ) + elif isinstance( + # pylint: disable=protected-access + view_instrument_match._aggregation, + _LastValueAggregation, + ): + data = Gauge(data_points=data_points) + elif isinstance( + # pylint: disable=protected-access + view_instrument_match._aggregation, + _ExplicitBucketHistogramAggregation, + ): + data = Histogram( + data_points=data_points, + aggregation_temporality=aggregation_temporality, + ) + elif isinstance( + # pylint: disable=protected-access + view_instrument_match._aggregation, + _DropAggregation, + ): + continue + + elif isinstance( + # pylint: disable=protected-access + view_instrument_match._aggregation, + _ExponentialBucketHistogramAggregation, + ): + data = ExponentialHistogram( + data_points=data_points, + aggregation_temporality=aggregation_temporality, + ) + + metrics.append( + Metric( + # pylint: disable=protected-access + # pylint: disable=possibly-used-before-assignment + name=view_instrument_match._name, + description=view_instrument_match._description, + unit=view_instrument_match._instrument.unit, + data=data, + ) + ) + + if metrics: + if instrument.instrumentation_scope not in (instrumentation_scope_scope_metrics): + instrumentation_scope_scope_metrics[instrument.instrumentation_scope] = ScopeMetrics( + scope=instrument.instrumentation_scope, + metrics=metrics, + schema_url=instrument.instrumentation_scope.schema_url, + ) + else: + instrumentation_scope_scope_metrics[instrument.instrumentation_scope].metrics.extend(metrics) + + if instrumentation_scope_scope_metrics: + return MetricsData( + resource_metrics=[ + ResourceMetrics( + resource=self._sdk_config.resource, + scope_metrics=list(instrumentation_scope_scope_metrics.values()), + schema_url=self._sdk_config.resource.schema_url, + ) + ] + ) + + return None + + def _handle_view_instrument_match( + self, + instrument: _Instrument, + view_instrument_matches: list["_ViewInstrumentMatch"], + ) -> None: + for view in self._sdk_config.views: + # pylint: disable=protected-access + if not view._match(instrument): + continue + + if not self._check_view_instrument_compatibility(view, instrument): + continue + + new_view_instrument_match = _ViewInstrumentMatch( + view=view, + instrument=instrument, + instrument_class_aggregation=(self._instrument_class_aggregation), + ) + + for existing_view_instrument_matches in self._instrument_view_instrument_matches.values(): + for existing_view_instrument_match in existing_view_instrument_matches: + if existing_view_instrument_match.conflicts(new_view_instrument_match): + _logger.warning( + "Views %s and %s will cause conflicting metrics identities", + existing_view_instrument_match._view, + new_view_instrument_match._view, + ) + + view_instrument_matches.append(new_view_instrument_match) + + @staticmethod + def _check_view_instrument_compatibility(view: View, instrument: _Instrument) -> bool: + """ + Checks if a view and an instrument are compatible. + + Returns `true` if they are compatible and a `_ViewInstrumentMatch` + object should be created, `false` otherwise. + """ + + result = True + + # pylint: disable=protected-access + if isinstance(instrument, Asynchronous) and isinstance( + view._aggregation, + ( + ExplicitBucketHistogramAggregation, + ExponentialBucketHistogramAggregation, + ), + ): + _logger.warning( + "View %s and instrument %s will produce semantic errors when matched, the view has not been applied.", + view, + instrument, + ) + result = False + + return result diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/point.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/point.py new file mode 100644 index 0000000..bb375fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/point.py @@ -0,0 +1,239 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# pylint: disable=unused-import + +from collections.abc import Sequence +from dataclasses import asdict, dataclass, field +from json import dumps, loads + +# This kind of import is needed to avoid Sphinx errors. +import opentelemetry.sdk.metrics._internal +from opentelemetry.sdk.metrics._internal.exemplar import Exemplar +from opentelemetry.sdk.resources import Resource +from opentelemetry.sdk.util.instrumentation import InstrumentationScope +from opentelemetry.util.types import Attributes + + +@dataclass(frozen=True) +class NumberDataPoint: + """Single data point in a timeseries that describes the time-varying scalar + value of a metric. + """ + + attributes: Attributes + start_time_unix_nano: int + time_unix_nano: int + value: int | float + exemplars: Sequence[Exemplar] = field(default_factory=list) + + def to_json(self, indent: int | None = 4) -> str: + return dumps(asdict(self), indent=indent) + + +@dataclass(frozen=True) +class HistogramDataPoint: + """Single data point in a timeseries that describes the time-varying scalar + value of a metric. + """ + + attributes: Attributes + start_time_unix_nano: int + time_unix_nano: int + count: int + sum: int | float + bucket_counts: Sequence[int] + explicit_bounds: Sequence[float] + min: float + max: float + exemplars: Sequence[Exemplar] = field(default_factory=list) + + def to_json(self, indent: int | None = 4) -> str: + return dumps(asdict(self), indent=indent) + + +@dataclass(frozen=True) +class Buckets: + offset: int + bucket_counts: Sequence[int] + + +@dataclass(frozen=True) +class ExponentialHistogramDataPoint: + """Single data point in a timeseries whose boundaries are defined by an + exponential function. This timeseries describes the time-varying scalar + value of a metric. + """ + + attributes: Attributes + start_time_unix_nano: int + time_unix_nano: int + count: int + sum: int | float + scale: int + zero_count: int + positive: Buckets + negative: Buckets + flags: int + min: float + max: float + exemplars: Sequence[Exemplar] = field(default_factory=list) + + def to_json(self, indent: int | None = 4) -> str: + return dumps(asdict(self), indent=indent) + + +@dataclass(frozen=True) +class ExponentialHistogram: + """Represents the type of a metric that is calculated by aggregating as an + ExponentialHistogram of all reported measurements over a time interval. + """ + + data_points: Sequence[ExponentialHistogramDataPoint] + aggregation_temporality: "opentelemetry.sdk.metrics.export.AggregationTemporality" + + def to_json(self, indent: int | None = 4) -> str: + return dumps( + { + "data_points": [loads(data_point.to_json(indent=indent)) for data_point in self.data_points], + "aggregation_temporality": self.aggregation_temporality, + }, + indent=indent, + ) + + +@dataclass(frozen=True) +class Sum: + """Represents the type of a scalar metric that is calculated as a sum of + all reported measurements over a time interval.""" + + data_points: Sequence[NumberDataPoint] + aggregation_temporality: "opentelemetry.sdk.metrics.export.AggregationTemporality" + is_monotonic: bool + + def to_json(self, indent: int | None = 4) -> str: + return dumps( + { + "data_points": [loads(data_point.to_json(indent=indent)) for data_point in self.data_points], + "aggregation_temporality": self.aggregation_temporality, + "is_monotonic": self.is_monotonic, + }, + indent=indent, + ) + + +@dataclass(frozen=True) +class Gauge: + """Represents the type of a scalar metric that always exports the current + value for every data point. It should be used for an unknown + aggregation.""" + + data_points: Sequence[NumberDataPoint] + + def to_json(self, indent: int | None = 4) -> str: + return dumps( + { + "data_points": [loads(data_point.to_json(indent=indent)) for data_point in self.data_points], + }, + indent=indent, + ) + + +@dataclass(frozen=True) +class Histogram: + """Represents the type of a metric that is calculated by aggregating as a + histogram of all reported measurements over a time interval.""" + + data_points: Sequence[HistogramDataPoint] + aggregation_temporality: "opentelemetry.sdk.metrics.export.AggregationTemporality" + + def to_json(self, indent: int | None = 4) -> str: + return dumps( + { + "data_points": [loads(data_point.to_json(indent=indent)) for data_point in self.data_points], + "aggregation_temporality": self.aggregation_temporality, + }, + indent=indent, + ) + + +# pylint: disable=invalid-name +DataT = Sum | Gauge | Histogram | ExponentialHistogram +DataPointT = NumberDataPoint | HistogramDataPoint | ExponentialHistogramDataPoint + + +@dataclass(frozen=True) +class Metric: + """Represents a metric point in the OpenTelemetry data model to be + exported.""" + + name: str + description: str | None + unit: str | None + data: DataT + + def to_json(self, indent: int | None = 4) -> str: + return dumps( + { + "name": self.name, + "description": self.description or "", + "unit": self.unit or "", + "data": loads(self.data.to_json(indent=indent)), + }, + indent=indent, + ) + + +@dataclass(frozen=True) +class ScopeMetrics: + """A collection of Metrics produced by a scope""" + + scope: InstrumentationScope + metrics: Sequence[Metric] + schema_url: str + + def to_json(self, indent: int | None = 4) -> str: + return dumps( + { + "scope": loads(self.scope.to_json(indent=indent)), + "metrics": [loads(metric.to_json(indent=indent)) for metric in self.metrics], + "schema_url": self.schema_url, + }, + indent=indent, + ) + + +@dataclass(frozen=True) +class ResourceMetrics: + """A collection of ScopeMetrics from a Resource""" + + resource: Resource + scope_metrics: Sequence[ScopeMetrics] + schema_url: str + + def to_json(self, indent: int | None = 4) -> str: + return dumps( + { + "resource": loads(self.resource.to_json(indent=indent)), + "scope_metrics": [loads(scope_metrics.to_json(indent=indent)) for scope_metrics in self.scope_metrics], + "schema_url": self.schema_url, + }, + indent=indent, + ) + + +@dataclass(frozen=True) +class MetricsData: + """An array of ResourceMetrics""" + + resource_metrics: Sequence[ResourceMetrics] + + def to_json(self, indent: int | None = 4) -> str: + return dumps( + { + "resource_metrics": [ + loads(resource_metrics.to_json(indent=indent)) for resource_metrics in self.resource_metrics + ] + }, + indent=indent, + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/sdk_configuration.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/sdk_configuration.py new file mode 100644 index 0000000..a143026 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/sdk_configuration.py @@ -0,0 +1,18 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# pylint: disable=unused-import + +from collections.abc import Sequence +from dataclasses import dataclass + +# This kind of import is needed to avoid Sphinx errors. +import opentelemetry.sdk.metrics +import opentelemetry.sdk.resources + + +@dataclass +class SdkConfiguration: + exemplar_filter: "opentelemetry.sdk.metrics.ExemplarFilter" + resource: "opentelemetry.sdk.resources.Resource" + views: Sequence["opentelemetry.sdk.metrics.view.View"] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/view.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/view.py new file mode 100644 index 0000000..31ac34c --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/_internal/view.py @@ -0,0 +1,174 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from collections.abc import Callable +from fnmatch import fnmatchcase +from logging import getLogger + +from opentelemetry.metrics import Instrument +from opentelemetry.sdk.metrics._internal.aggregation import ( + Aggregation, + DefaultAggregation, + _Aggregation, + _ExplicitBucketHistogramAggregation, + _ExponentialBucketHistogramAggregation, +) +from opentelemetry.sdk.metrics._internal.exemplar import ( + AlignedHistogramBucketExemplarReservoir, + ExemplarReservoirBuilder, + SimpleFixedSizeExemplarReservoir, +) +from opentelemetry.sdk.metrics._internal.instrument import _Instrument + +_logger = getLogger(__name__) + + +def _default_reservoir_factory( + aggregation_type: type[_Aggregation], +) -> ExemplarReservoirBuilder: + """Default reservoir factory per aggregation.""" + if issubclass(aggregation_type, _ExplicitBucketHistogramAggregation): + return AlignedHistogramBucketExemplarReservoir + if issubclass(aggregation_type, _ExponentialBucketHistogramAggregation): + return SimpleFixedSizeExemplarReservoir + return SimpleFixedSizeExemplarReservoir + + +class View: + """ + A `View` configuration parameters can be used for the following + purposes: + + 1. Match instruments: When an instrument matches a view, measurements + received by that instrument will be processed. + 2. Customize metric streams: A metric stream is identified by a match + between a view and an instrument and a set of attributes. The metric + stream can be customized by certain attributes of the corresponding view. + + The attributes documented next serve one of the previous two purposes. + + Args: + instrument_type: This is an instrument matching attribute: the class the + instrument must be to match the view. + + instrument_name: This is an instrument matching attribute: the name the + instrument must have to match the view. Wild card characters are supported. Wild + card characters should not be used with this attribute if the view has also a + ``name`` defined. + + meter_name: This is an instrument matching attribute: the name the + instrument meter must have to match the view. + + meter_version: This is an instrument matching attribute: the version + the instrument meter must have to match the view. + + meter_schema_url: This is an instrument matching attribute: the schema + URL the instrument meter must have to match the view. + + name: This is a metric stream customizing attribute: the name of the + metric stream. If `None`, the name of the instrument will be used. + + description: This is a metric stream customizing attribute: the + description of the metric stream. If `None`, the description of the instrument will + be used. + + attribute_keys: This is a metric stream customizing attribute: this is + a set of attribute keys. If not `None` then only the measurement attributes that + are in ``attribute_keys`` will be used to identify the metric stream. + + aggregation: This is a metric stream customizing attribute: the + aggregation instance to use when data is aggregated for the + corresponding metrics stream. If `None` an instance of + `DefaultAggregation` will be used. + + exemplar_reservoir_factory: This is a metric stream customizing attribute: + the exemplar reservoir factory + + instrument_unit: This is an instrument matching attribute: the unit the + instrument must have to match the view. + + This class is not intended to be subclassed by the user. + """ + + _default_aggregation = DefaultAggregation() + + def __init__( + self, + instrument_type: type[Instrument] | None = None, + instrument_name: str | None = None, + meter_name: str | None = None, + meter_version: str | None = None, + meter_schema_url: str | None = None, + name: str | None = None, + description: str | None = None, + attribute_keys: set[str] | None = None, + aggregation: Aggregation | None = None, + exemplar_reservoir_factory: Callable[[type[_Aggregation]], ExemplarReservoirBuilder] | None = None, + instrument_unit: str | None = None, + ): + if ( + instrument_type + is instrument_name + is instrument_unit + is meter_name + is meter_version + is meter_schema_url + is None + ): + # pylint: disable=broad-exception-raised + raise Exception(f"Some instrument selection criteria must be provided for View {name}") + + if name is not None and instrument_name is not None and ("*" in instrument_name or "?" in instrument_name): + # pylint: disable=broad-exception-raised + raise Exception(f"View {name} declared with wildcard characters in instrument_name") + + # _name, _description, _aggregation, _exemplar_reservoir_factory and + # _attribute_keys will be accessed when instantiating a _ViewInstrumentMatch. + self._name = name + self._instrument_type = instrument_type + self._instrument_name = instrument_name + self._instrument_unit = instrument_unit + self._meter_name = meter_name + self._meter_version = meter_version + self._meter_schema_url = meter_schema_url + + self._description = description + self._attribute_keys = attribute_keys + self._aggregation = aggregation or self._default_aggregation + self._exemplar_reservoir_factory = exemplar_reservoir_factory or _default_reservoir_factory + + # pylint: disable=too-many-return-statements + # pylint: disable=too-many-branches + def _match(self, instrument: _Instrument) -> bool: + if self._instrument_type is not None: + if not isinstance(instrument, self._instrument_type): + return False + + if self._instrument_name is not None: + # Instrument names are treated case-insensitively and are stored + # lower-cased by the SDK, so the pattern is lower-cased to match. + # fnmatchcase is used instead of fnmatch so it does not rely + # on the host platform's filename case sensitivity (normcase). + if not fnmatchcase(instrument.name, self._instrument_name.lower()): + return False + + if self._instrument_unit is not None: + # Units are case-sensitive; fnmatchcase keeps matching consistent + # across platforms. + if not fnmatchcase(instrument.unit, self._instrument_unit): + return False + + if self._meter_name is not None: + if instrument.instrumentation_scope.name != self._meter_name: + return False + + if self._meter_version is not None: + if instrument.instrumentation_scope.version != self._meter_version: + return False + + if self._meter_schema_url is not None: + if instrument.instrumentation_scope.schema_url != self._meter_schema_url: + return False + + return True diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/export/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/export/__init__.py new file mode 100644 index 0000000..3685e53 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/export/__init__.py @@ -0,0 +1,57 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from opentelemetry.sdk.metrics._internal.aggregation import ( + AggregationTemporality, +) +from opentelemetry.sdk.metrics._internal.export import ( + ConsoleMetricExporter, + InMemoryMetricReader, + MetricExporter, + MetricExportResult, + MetricReader, + PeriodicExportingMetricReader, +) + +# The point module is not in the export directory to avoid a circular import. +from opentelemetry.sdk.metrics._internal.point import ( + Buckets, + DataPointT, + DataT, + ExponentialHistogram, + ExponentialHistogramDataPoint, + Gauge, + Histogram, + HistogramDataPoint, + Metric, + MetricsData, + NumberDataPoint, + ResourceMetrics, + ScopeMetrics, + Sum, +) + +__all__ = [ + "AggregationTemporality", + "Buckets", + "ConsoleMetricExporter", + "DataPointT", + "DataT", + "ExponentialHistogram", + "ExponentialHistogramDataPoint", + "Gauge", + "Histogram", + "HistogramDataPoint", + "InMemoryMetricReader", + "Metric", + "MetricExportResult", + "MetricExporter", + "MetricReader", + "MetricsData", + "NumberDataPoint", + "PeriodicExportingMetricReader", + "ResourceMetrics", + "ScopeMetrics", + "Sum", +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/export/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/export/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2f2d652 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/export/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/view/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/view/__init__.py new file mode 100644 index 0000000..3aeb664 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/view/__init__.py @@ -0,0 +1,24 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from opentelemetry.sdk.metrics._internal.aggregation import ( + Aggregation, + DefaultAggregation, + DropAggregation, + ExplicitBucketHistogramAggregation, + ExponentialBucketHistogramAggregation, + LastValueAggregation, + SumAggregation, +) +from opentelemetry.sdk.metrics._internal.view import View + +__all__ = [ + "Aggregation", + "DefaultAggregation", + "DropAggregation", + "ExplicitBucketHistogramAggregation", + "ExponentialBucketHistogramAggregation", + "LastValueAggregation", + "SumAggregation", + "View", +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/view/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/view/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..cb294f8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/metrics/view/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/sdk/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/resources/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/resources/__init__.py new file mode 100644 index 0000000..7c88f61 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/resources/__init__.py @@ -0,0 +1,778 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +""" +This package implements `OpenTelemetry Resources +`_: + + *A Resource is an immutable representation of the entity producing + telemetry. For example, a process producing telemetry that is running in + a container on Kubernetes has a Pod name, it is in a namespace and + possibly is part of a Deployment which also has a name. All three of + these attributes can be included in the Resource.* + +Resource objects are created with `Resource.create`, which accepts attributes +(key-values). Resources should NOT be created via constructor except by `ResourceDetector` +instances which can't use `Resource.create` to avoid infinite loops. Working with +`Resource` objects should only be done via the Resource API methods. Resource +attributes can also be passed at process invocation in the +:envvar:`OTEL_RESOURCE_ATTRIBUTES` environment variable. You should register +your resource with the `opentelemetry.sdk.trace.TracerProvider` by passing +them into their constructors. The `Resource` passed to a provider is available +to the exporter, which can send on this information as it sees fit. + +.. code-block:: python + + trace.set_tracer_provider( + TracerProvider( + resource=Resource.create( + { + "service.name": "shoppingcart", + "service.instance.id": "instance-12", + } + ), + ), + ) + print(trace.get_tracer_provider().resource.attributes) + + { + "telemetry.sdk.language": "python", + "telemetry.sdk.name": "opentelemetry", + "telemetry.sdk.version": "0.13.dev0", + "service.name": "shoppingcart", + "service.instance.id": "instance-12", + } + +Note that the OpenTelemetry project documents certain `"standard attributes" +`_ +that have prescribed semantic meanings, for example ``service.name`` in the +above example. +""" + +# ResourceAttributes is deprecated +# pyright: reportDeprecated=false + +import abc +import concurrent.futures +import logging +import os +import platform +import socket +import subprocess +import sys +import threading +import uuid +from collections.abc import Mapping, Sequence +from json import dumps +from os import environ +from types import ModuleType +from urllib import parse + +from opentelemetry.attributes import BoundedAttributes +from opentelemetry.sdk.environment_variables import ( + OTEL_EXPERIMENTAL_RESOURCE_DETECTORS, + OTEL_RESOURCE_ATTRIBUTES, + OTEL_SERVICE_NAME, +) +from opentelemetry.sdk.version import ( + __version__ as _OPENTELEMETRY_SDK_VERSION, +) +from opentelemetry.semconv.resource import ResourceAttributes +from opentelemetry.util.types import AnyValue + +psutil: ModuleType | None = None + +try: + import psutil as psutil_module + + psutil = psutil_module +except ImportError: + pass + +# Only available on Windows, where it is used to read the MachineGuid for host.id. +winreg: ModuleType | None = None + +try: + import winreg as winreg_module + + winreg = winreg_module +except ImportError: + pass + +LabelValue = AnyValue +Attributes = Mapping[str, LabelValue] +logger = logging.getLogger(__name__) +CLOUD_PROVIDER = ResourceAttributes.CLOUD_PROVIDER +CLOUD_ACCOUNT_ID = ResourceAttributes.CLOUD_ACCOUNT_ID +CLOUD_REGION = ResourceAttributes.CLOUD_REGION +CLOUD_AVAILABILITY_ZONE = ResourceAttributes.CLOUD_AVAILABILITY_ZONE +CONTAINER_NAME = ResourceAttributes.CONTAINER_NAME +CONTAINER_ID = ResourceAttributes.CONTAINER_ID +CONTAINER_IMAGE_NAME = ResourceAttributes.CONTAINER_IMAGE_NAME +CONTAINER_IMAGE_TAG = ResourceAttributes.CONTAINER_IMAGE_TAG +DEPLOYMENT_ENVIRONMENT = ResourceAttributes.DEPLOYMENT_ENVIRONMENT +FAAS_NAME = ResourceAttributes.FAAS_NAME +FAAS_ID = ResourceAttributes.FAAS_ID +FAAS_VERSION = ResourceAttributes.FAAS_VERSION +FAAS_INSTANCE = ResourceAttributes.FAAS_INSTANCE +HOST_NAME = ResourceAttributes.HOST_NAME +HOST_ARCH = ResourceAttributes.HOST_ARCH +HOST_ID = ResourceAttributes.HOST_ID +HOST_TYPE = ResourceAttributes.HOST_TYPE +HOST_IMAGE_NAME = ResourceAttributes.HOST_IMAGE_NAME +HOST_IMAGE_ID = ResourceAttributes.HOST_IMAGE_ID +HOST_IMAGE_VERSION = ResourceAttributes.HOST_IMAGE_VERSION +KUBERNETES_CLUSTER_NAME = ResourceAttributes.K8S_CLUSTER_NAME +KUBERNETES_NAMESPACE_NAME = ResourceAttributes.K8S_NAMESPACE_NAME +KUBERNETES_POD_UID = ResourceAttributes.K8S_POD_UID +KUBERNETES_POD_NAME = ResourceAttributes.K8S_POD_NAME +KUBERNETES_CONTAINER_NAME = ResourceAttributes.K8S_CONTAINER_NAME +KUBERNETES_REPLICA_SET_UID = ResourceAttributes.K8S_REPLICASET_UID +KUBERNETES_REPLICA_SET_NAME = ResourceAttributes.K8S_REPLICASET_NAME +KUBERNETES_DEPLOYMENT_UID = ResourceAttributes.K8S_DEPLOYMENT_UID +KUBERNETES_DEPLOYMENT_NAME = ResourceAttributes.K8S_DEPLOYMENT_NAME +KUBERNETES_STATEFUL_SET_UID = ResourceAttributes.K8S_STATEFULSET_UID +KUBERNETES_STATEFUL_SET_NAME = ResourceAttributes.K8S_STATEFULSET_NAME +KUBERNETES_DAEMON_SET_UID = ResourceAttributes.K8S_DAEMONSET_UID +KUBERNETES_DAEMON_SET_NAME = ResourceAttributes.K8S_DAEMONSET_NAME +KUBERNETES_JOB_UID = ResourceAttributes.K8S_JOB_UID +KUBERNETES_JOB_NAME = ResourceAttributes.K8S_JOB_NAME +KUBERNETES_CRON_JOB_UID = ResourceAttributes.K8S_CRONJOB_UID +KUBERNETES_CRON_JOB_NAME = ResourceAttributes.K8S_CRONJOB_NAME +OS_DESCRIPTION = ResourceAttributes.OS_DESCRIPTION +OS_TYPE = ResourceAttributes.OS_TYPE +OS_VERSION = ResourceAttributes.OS_VERSION +PROCESS_PID = ResourceAttributes.PROCESS_PID +PROCESS_PARENT_PID = ResourceAttributes.PROCESS_PARENT_PID +PROCESS_EXECUTABLE_NAME = ResourceAttributes.PROCESS_EXECUTABLE_NAME +PROCESS_EXECUTABLE_PATH = ResourceAttributes.PROCESS_EXECUTABLE_PATH +PROCESS_COMMAND = ResourceAttributes.PROCESS_COMMAND +PROCESS_COMMAND_LINE = ResourceAttributes.PROCESS_COMMAND_LINE +PROCESS_COMMAND_ARGS = ResourceAttributes.PROCESS_COMMAND_ARGS +PROCESS_OWNER = ResourceAttributes.PROCESS_OWNER +PROCESS_RUNTIME_NAME = ResourceAttributes.PROCESS_RUNTIME_NAME +PROCESS_RUNTIME_VERSION = ResourceAttributes.PROCESS_RUNTIME_VERSION +PROCESS_RUNTIME_DESCRIPTION = ResourceAttributes.PROCESS_RUNTIME_DESCRIPTION +SERVICE_NAME = ResourceAttributes.SERVICE_NAME +SERVICE_NAMESPACE = ResourceAttributes.SERVICE_NAMESPACE +SERVICE_INSTANCE_ID = ResourceAttributes.SERVICE_INSTANCE_ID +SERVICE_VERSION = ResourceAttributes.SERVICE_VERSION +TELEMETRY_SDK_NAME = ResourceAttributes.TELEMETRY_SDK_NAME +TELEMETRY_SDK_VERSION = ResourceAttributes.TELEMETRY_SDK_VERSION +TELEMETRY_AUTO_VERSION = ResourceAttributes.TELEMETRY_AUTO_VERSION +TELEMETRY_SDK_LANGUAGE = ResourceAttributes.TELEMETRY_SDK_LANGUAGE + + +class Resource: + """A Resource is an immutable representation of the entity producing telemetry as Attributes.""" + + _attributes: BoundedAttributes + _schema_url: str + + def __init__(self, attributes: Attributes, schema_url: str | None = None): + # Immutable set to true so attributes cannot be added or removed after creation. + self._attributes = BoundedAttributes(attributes=attributes, immutable=True) + if schema_url is None: + schema_url = "" + self._schema_url = schema_url + + @staticmethod + def create( + attributes: Attributes | None = None, + schema_url: str | None = None, + ) -> "Resource": + """Creates a new `Resource` from attributes. + + `ResourceDetector` instances should not call this method. + + Args: + attributes: Optional zero or more key-value pairs. + schema_url: Optional URL pointing to the schema + + Returns: + The newly-created Resource. + """ + + if not attributes: + attributes = {} + + resource = get_aggregated_resources(_build_resource_detectors(), _DEFAULT_RESOURCE).merge( + Resource(attributes, schema_url) + ) + + if not resource.attributes.get(SERVICE_NAME, None): + default_service_name = "unknown_service" + if sys.executable: + default_service_name += f":{os.path.basename(sys.executable)}" + resource = resource.merge(Resource({SERVICE_NAME: default_service_name}, schema_url)) + return resource + + @staticmethod + def get_empty() -> "Resource": + return _EMPTY_RESOURCE + + @property + def attributes(self) -> Attributes: + return self._attributes + + @property + def schema_url(self) -> str: + return self._schema_url + + def merge(self, other: "Resource") -> "Resource": + """Merges this resource and an updating resource into a new `Resource`. + + If a key exists on both the old and updating resource, the value of the + updating resource will override the old resource value. + + The updating resource's `schema_url` will be used only if the old + `schema_url` is empty. Attempting to merge two resources with + different, non-empty values for `schema_url` will result in an error + and return the old resource. + + Args: + other: The other resource to be merged. + + Returns: + The newly-created Resource. + """ + merged_attributes = dict(self.attributes) + merged_attributes.update(other.attributes) + + if self.schema_url == "": + schema_url = other.schema_url + elif other.schema_url == "": + schema_url = self.schema_url + elif self.schema_url == other.schema_url: + schema_url = other.schema_url + else: + logger.error( + "Failed to merge resources: The two schemas %s and %s are incompatible", + self.schema_url, + other.schema_url, + ) + return self + return Resource(merged_attributes, schema_url) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Resource): + return False + return self._attributes == other._attributes and self._schema_url == other._schema_url + + def __hash__(self) -> int: + return hash(f"{dumps(self._attributes.copy(), sort_keys=True, default=_json_default)}|{self._schema_url}") + + def to_json(self, indent: int | None = 4) -> str: + return dumps( + { + "attributes": dict(self.attributes), + "schema_url": self._schema_url, + }, + indent=indent, + default=_json_default, + ) + + +def _json_default(value: object) -> str: + """Represent attribute values that `json` cannot encode natively. + + `types.AnyValue` admits `bytes`, which `json.dumps` rejects. Rendering it + as hex keeps `to_json` readable and keeps `__hash__` working; `__eq__` + still distinguishes `b"\x01"` from the string `"01"`, so the resulting + hash collision is harmless. + """ + if isinstance(value, (bytes, bytearray)): + return value.hex() + return str(value) + + +_EMPTY_RESOURCE = Resource({}) + +_DEFAULT_RESOURCE = Resource( + { + TELEMETRY_SDK_LANGUAGE: "python", + TELEMETRY_SDK_NAME: "opentelemetry", + TELEMETRY_SDK_VERSION: _OPENTELEMETRY_SDK_VERSION, + } +) + + +_service_instance_id: str | None = None +_service_instance_id_pid: int | None = None +_service_instance_id_lock = threading.Lock() + + +class ResourceDetector(abc.ABC): + def __init__(self, raise_on_error: bool = False) -> None: + self.raise_on_error = raise_on_error + + @abc.abstractmethod + def detect(self) -> "Resource": + """Don't call `Resource.create` here to avoid an infinite loop, instead instantiate `Resource` directly""" + raise NotImplementedError() + + # pylint: disable-next=no-self-use + def is_process_dependent(self) -> bool: + """Return whether this detector depends on the current process identity. + + Process dependent detectors may return resource attributes that become + stale after a process identity change, such as :func:`os.fork`. + Detectors returning ``True`` should be re-run after such changes so the + resulting :class:`Resource` describes the current process. + + Returns: + ``True`` if this detector should be re-run after process identity + changes otherwise ``False``. + """ + return False + + +class OTELResourceDetector(ResourceDetector): + # pylint: disable=no-self-use + def detect(self) -> "Resource": + env_resources_items = environ.get(OTEL_RESOURCE_ATTRIBUTES) + env_resource_map: dict[str, AnyValue] = {} + + if env_resources_items: + for item in env_resources_items.split(","): + try: + key, value = item.split("=", maxsplit=1) + except ValueError as exc: + logger.warning( + "Invalid key value resource attribute pair %s: %s", + item, + exc, + ) + continue + value_url_decoded = parse.unquote(value.strip()) + env_resource_map[key.strip()] = value_url_decoded + + service_name = environ.get(OTEL_SERVICE_NAME) + if service_name: + env_resource_map[SERVICE_NAME] = service_name + return Resource(env_resource_map) + + +class ProcessResourceDetector(ResourceDetector): + """Detect process resource attributes. + + Args: + raise_on_error: Raise errors from detection instead of logging them. + include_command_args: Include ``process.command_args`` and + ``process.command_line``. These attributes can contain sensitive + command-line values, so they are excluded by default. + """ + + def __init__( + self, + raise_on_error: bool = False, + *, + include_command_args: bool = False, + ) -> None: + super().__init__(raise_on_error=raise_on_error) + self._include_command_args = include_command_args + + # pylint: disable=no-self-use + def is_process_dependent(self) -> bool: + return True + + def detect(self) -> "Resource": + _runtime_version = ".".join( + map( + str, + ( + sys.version_info[:3] + if sys.version_info.releaselevel == "final" and not sys.version_info.serial + else sys.version_info + ), + ) + ) + _process_pid = os.getpid() + # Use sys.orig_argv, which preserves the original arguments received + # by the interpreter. This correctly captures ``python -m `` + # invocations where sys.argv is rewritten to the resolved module path + # and the ``-m `` information is lost. Only read argv[0] by + # default because full command arguments are opt-in. + _process_command = sys.orig_argv[0] if sys.orig_argv else "" + executable = sys.executable or "" + resource_info: dict[str, AnyValue] = { + PROCESS_RUNTIME_DESCRIPTION: sys.version, + PROCESS_RUNTIME_NAME: sys.implementation.name, + PROCESS_RUNTIME_VERSION: _runtime_version, + PROCESS_PID: _process_pid, + PROCESS_EXECUTABLE_NAME: os.path.basename(executable), + PROCESS_EXECUTABLE_PATH: executable, + PROCESS_COMMAND: _process_command, + } + if self._include_command_args: + _process_argv = list(sys.orig_argv) + resource_info[PROCESS_COMMAND_LINE] = " ".join(_process_argv) + resource_info[PROCESS_COMMAND_ARGS] = _process_argv + + if hasattr(os, "getppid"): + # pypy3 does not have getppid() + resource_info[PROCESS_PARENT_PID] = os.getppid() + + if psutil is not None: + process = psutil.Process() + username = process.username() + resource_info[PROCESS_OWNER] = username + + return Resource(resource_info) # type: ignore + + +class OsResourceDetector(ResourceDetector): + """Detect os resources based on `Operating System conventions `_.""" + + def detect(self) -> "Resource": + """Returns a resource with with ``os.type`` and ``os.version``. + + Python's platform library + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + To grab this information, Python's ``platform`` does not return what a + user might expect it to. Below is a breakdown of its return values in + different operating systems. + + .. code-block:: python + :caption: Linux + + >>> platform.system() + 'Linux' + >>> platform.release() + '6.5.0-35-generic' + >>> platform.version() + '#35~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue May 7 09:00:52 UTC 2' + + .. code-block:: python + :caption: MacOS + + >>> platform.system() + 'Darwin' + >>> platform.release() + '23.0.0' + >>> platform.version() + 'Darwin Kernel Version 23.0.0: Fri Sep 15 14:42:57 PDT 2023; root:xnu-10002.1.13~1/RELEASE_ARM64_T8112' + + .. code-block:: python + :caption: Windows + + >>> platform.system() + 'Windows' + >>> platform.release() + '2022Server' + >>> platform.version() + '10.0.20348' + + .. code-block:: python + :caption: FreeBSD + + >>> platform.system() + 'FreeBSD' + >>> platform.release() + '14.1-RELEASE' + >>> platform.version() + 'FreeBSD 14.1-RELEASE releng/14.1-n267679-10e31f0946d8 GENERIC' + + .. code-block:: python + :caption: Solaris + + >>> platform.system() + 'SunOS' + >>> platform.release() + '5.11' + >>> platform.version() + '11.4.0.15.0' + + """ + + os_type = platform.system().lower() + os_version = platform.release() + + # See docstring + if os_type == "windows": + os_version = platform.version() + # Align SunOS with conventions + elif os_type == "sunos": + os_type = "solaris" + os_version = platform.version() + + return Resource( + { + OS_TYPE: os_type, + OS_VERSION: os_version, + } + ) + + +# Non-privileged machine id sources per the semantic conventions: +# https://opentelemetry.io/docs/specs/semconv/resource/host/#non-privileged-machine-id-lookup +_LINUX_MACHINE_ID_PATHS = ("/etc/machine-id", "/var/lib/dbus/machine-id") +_BSD_HOSTID_PATH = "/etc/hostid" +_BSD_KENV_COMMAND = ("/bin/kenv", "-q", "smbios.system.uuid") +_MACOS_IOREG_COMMAND = ("/usr/sbin/ioreg", "-rd1", "-c", "IOPlatformExpertDevice") +_WINDOWS_CRYPTOGRAPHY_KEY = r"SOFTWARE\Microsoft\Cryptography" +_WINDOWS_MACHINE_GUID_VALUE = "MachineGuid" +# Deliberately below get_aggregated_resources' per detector timeout so that a +# hung command still leaves time for host.name and host.arch to be returned. +_COMMAND_TIMEOUT_SECONDS = 2 + + +def _read_machine_id_file(path: str) -> str | None: + try: + with open(path, encoding="utf8") as machine_id_file: + return machine_id_file.read().strip() or None + except OSError as exception: + logger.debug("Failed to read %s: %s", path, exception) + return None + + +def _run_command(command: tuple[str, ...]) -> str: + """Returns the command's stdout, or "" when the source is unavailable here. + + A non-zero exit or a missing binary means this host has no machine id to + offer. + """ + try: + completed = subprocess.run( + command, + capture_output=True, + text=True, + timeout=_COMMAND_TIMEOUT_SECONDS, + check=True, + ) + except (subprocess.CalledProcessError, FileNotFoundError) as exception: + logger.debug("Failed to run %s: %s", command[0], exception) + return "" + return completed.stdout + + +def _get_linux_machine_id() -> str | None: + for path in _LINUX_MACHINE_ID_PATHS: + machine_id = _read_machine_id_file(path) + if machine_id: + return machine_id + return None + + +def _get_bsd_machine_id() -> str | None: + return _read_machine_id_file(_BSD_HOSTID_PATH) or _run_command(_BSD_KENV_COMMAND).strip() or None + + +def _get_macos_machine_id() -> str | None: + for line in _run_command(_MACOS_IOREG_COMMAND).splitlines(): + # The line looks like: ` "IOPlatformUUID" = "AAAAAAAA-BBBB-..."` + key, separator, value = line.partition("=") + if not separator or key.strip().strip('"') != "IOPlatformUUID": + continue + + machine_id = value.strip().strip('"') + if machine_id: + return machine_id + return None + + +def _get_windows_machine_id() -> str | None: + if winreg is None: + logger.debug("winreg is unavailable, cannot detect %s", HOST_ID) + return None + with winreg.OpenKey( + winreg.HKEY_LOCAL_MACHINE, + _WINDOWS_CRYPTOGRAPHY_KEY, + access=winreg.KEY_READ | winreg.KEY_WOW64_64KEY, + ) as key: + machine_guid, _ = winreg.QueryValueEx(key, _WINDOWS_MACHINE_GUID_VALUE) + return str(machine_guid) if machine_guid else None + + +def _get_host_id() -> str | None: + system = platform.system() + if system == "Linux": + return _get_linux_machine_id() + if system == "Darwin": + return _get_macos_machine_id() + if system == "Windows": + return _get_windows_machine_id() + if system == "DragonFly" or system.endswith("BSD"): + return _get_bsd_machine_id() + logger.debug("Unsupported OS type for %s detection: %s", HOST_ID, system) + return None + + +class _HostResourceDetector(ResourceDetector): # type: ignore[reportUnusedClass] + """ + The HostResourceDetector detects the hostname, architecture and host id + attributes. + + ``host.id`` is the non-privileged machine id described by the `Host resource + conventions `_, + and is omitted when it cannot be determined. A failed lookup does not + prevent ``host.name`` and ``host.arch`` from being detected unless + ``raise_on_error=True``. + """ + + def detect(self) -> "Resource": + resource_info: dict[str, AnyValue] = { + HOST_NAME: socket.gethostname(), + HOST_ARCH: platform.machine(), + } + + # A failed host id lookup must not cost the caller the attributes above, + # so it is guarded here rather than relying on the handling in + # get_aggregated_resources: detect() is also called directly. + try: + if host_id := _get_host_id(): + resource_info[HOST_ID] = host_id + # pylint: disable=broad-exception-caught + except Exception as exception: + logger.warning("Failed to detect %s: %s", HOST_ID, exception) + if self.raise_on_error: + raise + + return Resource(resource_info) + + +class ServiceInstanceIdResourceDetector(ResourceDetector): + """Detects service.instance.id as a random UUID v4. + + Per the OpenTelemetry specification, SDKs SHOULD generate a random v1/v4 + UUID for service.instance.id to uniquely identify each service instance. + The ID is shared across all detector instances within the same process and + regenerated automatically when the process PID changes (e.g. after a fork). + + Note: because this detector is process dependent, providers refresh it + automatically after a fork and merge the newly detected value on top of + the existing resource. This means that if a user explicitly sets + `service.instance.id` (e.g. via OTEL_RESOURCE_ATTRIBUTES or + Resource.create(attributes=...)), that value will be overwritten with a + newly generated UUID the next time the process forks. This differs from + Resource.create(), which does preserve user supplied overrides. + """ + + def is_process_dependent(self) -> bool: + return True + + def detect(self) -> "Resource": + # pylint: disable-next=global-statement + global _service_instance_id, _service_instance_id_pid + with _service_instance_id_lock: + current_pid = os.getpid() + if _service_instance_id is None or _service_instance_id_pid != current_pid: + _service_instance_id = str(uuid.uuid4()) + _service_instance_id_pid = current_pid + instance_id = _service_instance_id + return Resource({SERVICE_INSTANCE_ID: instance_id}) + + +def _build_resource_detectors() -> list["ResourceDetector"]: + """Returns the ordered list of resource detectors to use for Resource.create. + + Fast path: if no extra detectors are configured, returns only the two + built-in detectors without scanning entry_points. + + "service_instance" (ServiceInstanceIdResourceDetector) is prepended unless + it is explicitly configured. "otel" (OTELResourceDetector) is last so + that OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME take highest merge + priority, but an explicit position in + OTEL_EXPERIMENTAL_RESOURCE_DETECTORS is respected for either name. + """ + configured_detector_names = [ + name.strip() for name in environ.get(OTEL_EXPERIMENTAL_RESOURCE_DETECTORS, "").split(",") if name.strip() + ] + default_detector_names = [] if "service_instance" in configured_detector_names else ["service_instance"] + detector_names: list[str] = list(dict.fromkeys(default_detector_names + configured_detector_names + ["otel"])) + + # Fast path: only the two built-in detectors — no entry_points scan needed. + if detector_names == ["service_instance", "otel"]: + return [ServiceInstanceIdResourceDetector(), OTELResourceDetector()] + + # pylint: disable=import-outside-toplevel + from opentelemetry.util._importlib_metadata import ( # noqa: PLC0415 + entry_points, # type: ignore[reportUnknownVariableType] + ) + + if "*" in detector_names: + registered = set( + name + for name in entry_points(group="opentelemetry_resource_detector").names # type: ignore[reportUnknownArgumentType] + if name != "otel" + ) + expansion = sorted(registered - set(detector_names)) + idx = detector_names.index("*") + detector_names = detector_names[:idx] + expansion + detector_names[idx + 1 :] + + detectors: list[ResourceDetector] = [] + for name in detector_names: + try: + detectors.append( + next( + iter( + entry_points( + group="opentelemetry_resource_detector", + name=name, + ) # type: ignore[reportUnknownArgumentType] + ) + ).load()() + ) + except Exception: # pylint: disable=broad-exception-caught + logger.exception( + "Failed to load resource detector '%s', skipping", + name, + ) + return detectors + + +def _get_process_dependent_resource() -> Resource: # pyright: ignore[reportUnusedFunction] + return get_aggregated_resources( + [detector for detector in _build_resource_detectors() if detector.is_process_dependent()], + Resource.get_empty(), + ) + + +def get_aggregated_resources( + detectors: Sequence["ResourceDetector"], + initial_resource: Resource | None = None, + timeout: int = 5, +) -> "Resource": + """Retrieves resources from detectors in the order that they were passed + + :param detectors: List of resources in order of priority + :param initial_resource: Static resource. This has highest priority + :param timeout: Number of seconds to wait for each detector to return + :return: + """ + detectors_merged_resource = initial_resource or Resource.create() + + # The executor is not used as a context manager: exiting it would call + # `shutdown(wait=True)` and join still-running workers, so a detector that + # blocks longer than `timeout` would hold up the whole call regardless of + # `future.result(timeout=...)`. Shut down without waiting instead so the + # timeout actually bounds this call. A detector that outlives the timeout + # continues in its worker thread until detect() returns. + executor = concurrent.futures.ThreadPoolExecutor(max_workers=4) + try: + futures = [executor.submit(detector.detect) for detector in detectors] + for detector_ind, future in enumerate(futures): + detector = detectors[detector_ind] + detected_resource: Resource = _EMPTY_RESOURCE + try: + detected_resource = future.result(timeout=timeout) + except concurrent.futures.TimeoutError as ex: + if detector.raise_on_error: + raise ex + logger.warning( + "Detector %s took longer than %s seconds, skipping", + detector, + timeout, + ) + # pylint: disable=broad-exception-caught + except Exception as ex: + if detector.raise_on_error: + raise ex + logger.warning("Exception %s in detector %s, ignoring", ex, detector) + finally: + detectors_merged_resource = detectors_merged_resource.merge(detected_resource) + finally: + executor.shutdown(wait=False, cancel_futures=True) + + return detectors_merged_resource diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/resources/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/resources/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a80d431 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/resources/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__init__.py new file mode 100644 index 0000000..4555a28 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__init__.py @@ -0,0 +1,1425 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# pylint: disable=too-many-lines +import abc +import atexit +import concurrent.futures +import json +import logging +import os +import threading +import traceback +import typing +import weakref +from collections.abc import ( + Callable, + Iterator, + Mapping, + MutableMapping, + Sequence, +) +from dataclasses import dataclass +from os import environ +from time import time_ns +from types import MappingProxyType, TracebackType +from typing import ( + Any, +) +from warnings import filterwarnings + +from typing_extensions import deprecated + +from opentelemetry import context as context_api +from opentelemetry import metrics as metrics_api +from opentelemetry import trace as trace_api +from opentelemetry.attributes import BoundedAttributes +from opentelemetry.sdk import util +from opentelemetry.sdk.environment_variables import ( + OTEL_ATTRIBUTE_COUNT_LIMIT, + OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, + OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT, + OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, + OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, + OTEL_SDK_DISABLED, + OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, + OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, + OTEL_SPAN_EVENT_COUNT_LIMIT, + OTEL_SPAN_LINK_COUNT_LIMIT, +) +from opentelemetry.sdk.environment_variables._internal import ( + parse_boolean_environment_variable, +) +from opentelemetry.sdk.resources import ( + Resource, + _get_process_dependent_resource, +) +from opentelemetry.sdk.trace import sampling +from opentelemetry.sdk.trace._tracer_metrics import create_tracer_metrics +from opentelemetry.sdk.trace.id_generator import IdGenerator, RandomIdGenerator +from opentelemetry.sdk.util import BoundedList +from opentelemetry.sdk.util._configurator import RuleBasedConfigurator +from opentelemetry.sdk.util.instrumentation import ( + InstrumentationInfo, + InstrumentationScope, +) +from opentelemetry.semconv.attributes.exception_attributes import ( + EXCEPTION_ESCAPED, + EXCEPTION_MESSAGE, + EXCEPTION_STACKTRACE, + EXCEPTION_TYPE, +) +from opentelemetry.trace import NoOpTracer, SpanContext +from opentelemetry.trace.status import Status, StatusCode +from opentelemetry.util import types +from opentelemetry.util._decorator import _agnosticcontextmanager + +logger = logging.getLogger(__name__) + +_DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT = 128 +_DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT = 128 +_DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT = 128 +_DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT = 128 +_DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT = 128 +_DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT = 128 + + +_ENV_VALUE_UNSET = "" + + +class SpanProcessor: + """Interface which allows hooks for SDK's `Span` start and end method + invocations. + + Span processors can be registered directly using + :func:`TracerProvider.add_span_processor` and they are invoked + in the same order as they were registered. + """ + + def on_start( + self, + span: "Span", + parent_context: context_api.Context | None = None, + ) -> None: + """Called when a :class:`opentelemetry.trace.Span` is started. + + This method is called synchronously on the thread that starts the + span, therefore it should not block or throw an exception. + + Args: + span: The :class:`opentelemetry.trace.Span` that just started. + parent_context: The parent context of the span that just started. + """ + + def _on_ending(self, span: "Span") -> None: + """Called when a :class:`opentelemetry.trace.Span` is ending. + + This method is called synchronously on the thread that ends the + span, therefore it should not block or throw an exception. + + Args: + span: The :class:`opentelemetry.trace.Span` that is ending. + """ + + def on_end(self, span: "ReadableSpan") -> None: + """Called when a :class:`opentelemetry.trace.Span` is ended. + + This method is called synchronously on the thread that ends the + span, therefore it should not block or throw an exception. + + Args: + span: The :class:`opentelemetry.trace.Span` that just ended. + """ + + def shutdown(self) -> None: + """Called when a :class:`opentelemetry.sdk.trace.TracerProvider` is shutdown.""" + + def force_flush(self, timeout_millis: int = 30000) -> bool: # pylint: disable=no-self-use + """Export all ended spans to the configured Exporter that have not yet + been exported. + + Args: + timeout_millis: The maximum amount of time to wait for spans to be + exported. + + Returns: + False if the timeout is exceeded, True otherwise. + """ + return True + + +# Temporary fix until https://github.com/PyCQA/pylint/issues/4098 is resolved +# pylint:disable=no-member +class SynchronousMultiSpanProcessor(SpanProcessor): + """Implementation of class:`SpanProcessor` that forwards all received + events to a list of span processors sequentially. + + The underlying span processors are called in sequential order as they were + added. + """ + + _span_processors: tuple[SpanProcessor, ...] + + def __init__(self): + # use a tuple to avoid race conditions when adding a new span and + # iterating through it on "on_start" and "on_end". + self._span_processors = () + self._lock = threading.Lock() + + def add_span_processor(self, span_processor: SpanProcessor) -> None: + """Adds a SpanProcessor to the list handled by this instance.""" + with self._lock: + self._span_processors += (span_processor,) + + def on_start( + self, + span: "Span", + parent_context: context_api.Context | None = None, + ) -> None: + for sp in self._span_processors: + sp.on_start(span, parent_context=parent_context) + + def _on_ending(self, span: "Span") -> None: + for sp in self._span_processors: + # pylint: disable=protected-access + sp._on_ending(span) + + def on_end(self, span: "ReadableSpan") -> None: + for sp in self._span_processors: + sp.on_end(span) + + def shutdown(self) -> None: + """Sequentially shuts down all underlying span processors.""" + for sp in self._span_processors: + sp.shutdown() + + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Sequentially calls force_flush on all underlying + :class:`SpanProcessor` + + Args: + timeout_millis: The maximum amount of time over all span processors + to wait for spans to be exported. In case the first n span + processors exceeded the timeout followup span processors will be + skipped. + + Returns: + True if all span processors flushed their spans within the + given timeout, False otherwise. + """ + deadline_ns = time_ns() + timeout_millis * 1000000 + all_flushed = True + for sp in self._span_processors: + current_time_ns = time_ns() + if current_time_ns >= deadline_ns: + return False + + if sp.force_flush((deadline_ns - current_time_ns) // 1000000) is False: + all_flushed = False + + return all_flushed + + +class ConcurrentMultiSpanProcessor(SpanProcessor): + """Implementation of :class:`SpanProcessor` that forwards all received + events to a list of span processors in parallel. + + Calls to the underlying span processors are forwarded in parallel by + submitting them to a thread pool executor and waiting until each span + processor finished its work. + + Args: + num_threads: The number of threads managed by the thread pool executor + and thus defining how many span processors can work in parallel. + """ + + _span_processors: tuple[SpanProcessor, ...] + + def __init__(self, num_threads: int = 2): + # use a tuple to avoid race conditions when adding a new span and + # iterating through it on "on_start" and "on_end". + self._span_processors = () + self._lock = threading.Lock() + self._init_executor(num_threads) + if hasattr(os, "register_at_fork"): + # Only the main thread is kept in forked processed, the executor + # needs to be re-instantiated to get a fresh pool of threads: + weak_reinit = weakref.WeakMethod(self._init_executor) + + def _after_in_child() -> None: + reinit = weak_reinit() + if reinit is not None: + reinit(num_threads) + + os.register_at_fork(after_in_child=_after_in_child) + + def _init_executor(self, num_threads: int) -> None: + self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) + + def add_span_processor(self, span_processor: SpanProcessor) -> None: + """Adds a SpanProcessor to the list handled by this instance.""" + with self._lock: + self._span_processors += (span_processor,) + + def _submit_and_await( + self, + func: Callable[[SpanProcessor], Callable[..., None]], + *args: Any, + **kwargs: Any, + ): + futures = [] + for sp in self._span_processors: + future = self._executor.submit(func(sp), *args, **kwargs) + futures.append(future) + for future in futures: + future.result() + + def on_start( + self, + span: "Span", + parent_context: context_api.Context | None = None, + ) -> None: + self._submit_and_await(lambda sp: sp.on_start, span, parent_context=parent_context) + + def _on_ending(self, span: "Span") -> None: + # pylint: disable=protected-access + self._submit_and_await(lambda sp: sp._on_ending, span) + + def on_end(self, span: "ReadableSpan") -> None: + self._submit_and_await(lambda sp: sp.on_end, span) + + def shutdown(self) -> None: + """Shuts down all underlying span processors in parallel.""" + self._submit_and_await(lambda sp: sp.shutdown) + + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Calls force_flush on all underlying span processors in parallel. + + Args: + timeout_millis: The maximum amount of time to wait for spans to be + exported. + + Returns: + True if all span processors flushed their spans within the given + timeout, False otherwise. + """ + futures = [] + for sp in self._span_processors: + future = self._executor.submit(sp.force_flush, timeout_millis) + futures.append(future) + + timeout_sec = timeout_millis / 1e3 + done_futures, not_done_futures = concurrent.futures.wait(futures, timeout_sec) + if not_done_futures: + return False + + for future in done_futures: + if future.result() is False: + return False + + return True + + +class EventBase(abc.ABC): + def __init__(self, name: str, timestamp: int | None = None) -> None: + self._name = name + if timestamp is None: + self._timestamp = time_ns() + else: + self._timestamp = timestamp + + @property + def name(self) -> str: + return self._name + + @property + def timestamp(self) -> int: + return self._timestamp + + @property + @abc.abstractmethod + def attributes(self) -> types.Attributes: + pass + + +class Event(EventBase): + """A text annotation with a set of attributes. The attributes of an event + are immutable. + + Args: + name: Name of the event. + attributes: Attributes of the event. + timestamp: Timestamp of the event. If `None` it will filled + automatically. + """ + + def __init__( + self, + name: str, + attributes: types.Attributes = None, + timestamp: int | None = None, + limit: int | None = _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, + ) -> None: + super().__init__(name, timestamp) + self._attributes = attributes + + @property + def attributes(self) -> types.Attributes: + return self._attributes + + @property + def dropped_attributes(self) -> int: + if isinstance(self._attributes, BoundedAttributes): + return self._attributes.dropped + return 0 + + +def _check_span_ended(func): + def wrapper(self, *args, **kwargs): + already_ended = False + with self._lock: # pylint: disable=protected-access + if self._end_time is None: # pylint: disable=protected-access + func(self, *args, **kwargs) + else: + already_ended = True + + if already_ended: + logger.warning("Tried calling %s on an ended span.", func.__name__) + + return wrapper + + +def _is_valid_link(context: SpanContext, attributes: types.Attributes) -> bool: + return bool(context and (context.is_valid or (attributes or context.trace_state))) + + +class ReadableSpan: + """Provides read-only access to span attributes. + + Users should NOT be creating these objects directly. `ReadableSpan`s are created as + a direct result from using the tracing pipeline via the `Tracer`. + + """ + + def __init__( + self, + name: str, + context: trace_api.SpanContext | None = None, + parent: trace_api.SpanContext | None = None, + resource: Resource | None = None, + attributes: types.Attributes = None, + events: Sequence[Event] = (), + links: Sequence[trace_api.Link] = (), + kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, + instrumentation_info: InstrumentationInfo | None = None, + status: Status = Status(StatusCode.UNSET), + start_time: int | None = None, + end_time: int | None = None, + instrumentation_scope: InstrumentationScope | None = None, + ) -> None: + self._name = name + self._context = context + self._kind = kind + self._instrumentation_info = instrumentation_info + self._instrumentation_scope = instrumentation_scope + self._parent = parent + self._start_time = start_time + self._end_time = end_time + self._attributes = attributes + self._events = events + self._links = links + if resource is None: + self._resource = Resource.create({}) + else: + self._resource = resource + self._status = status + + @property + def dropped_attributes(self) -> int: + if isinstance(self._attributes, BoundedAttributes): + return self._attributes.dropped + return 0 + + @property + def dropped_events(self) -> int: + if isinstance(self._events, BoundedList): + return self._events.dropped + return 0 + + @property + def dropped_links(self) -> int: + if isinstance(self._links, BoundedList): + return self._links.dropped + return 0 + + @property + def name(self) -> str: + return self._name + + def get_span_context(self) -> trace_api.SpanContext | None: + return self._context + + @property + def context(self): + return self._context + + @property + def kind(self) -> trace_api.SpanKind: + return self._kind + + @property + def parent(self) -> trace_api.SpanContext | None: + return self._parent + + @property + def start_time(self) -> int | None: + return self._start_time + + @property + def end_time(self) -> int | None: + return self._end_time + + @property + def status(self) -> trace_api.Status: + return self._status + + @property + def attributes(self) -> types.Attributes: + return MappingProxyType(self._attributes or {}) + + @property + def events(self) -> Sequence[Event]: + return tuple(self._events) + + @property + def links(self) -> Sequence[trace_api.Link]: + return tuple(self._links) + + @property + def resource(self) -> Resource: + return self._resource + + @property + @deprecated("You should use instrumentation_scope. Deprecated since version 1.11.1.") + def instrumentation_info(self) -> InstrumentationInfo | None: + return self._instrumentation_info + + @property + def instrumentation_scope(self) -> InstrumentationScope | None: + return self._instrumentation_scope + + def to_json(self, indent: int | None = 4): + parent_id = None + if self.parent is not None: + parent_id = f"0x{trace_api.format_span_id(self.parent.span_id)}" + + start_time = None + if self._start_time: + start_time = util.ns_to_iso_str(self._start_time) + + end_time = None + if self._end_time: + end_time = util.ns_to_iso_str(self._end_time) + + status = { + "status_code": str(self._status.status_code.name), + } + if self._status.description: + status["description"] = self._status.description + + f_span = { + "name": self._name, + "context": (self._format_context(self._context) if self._context else None), + "kind": str(self.kind), + "parent_id": parent_id, + "start_time": start_time, + "end_time": end_time, + "status": status, + "attributes": self._format_attributes(self._attributes), + "events": self._format_events(self._events), + "links": self._format_links(self._links), + "resource": json.loads(self.resource.to_json()), + } + + return json.dumps(f_span, indent=indent) + + @staticmethod + def _format_context(context: SpanContext) -> dict[str, str]: + return { + "trace_id": f"0x{trace_api.format_trace_id(context.trace_id)}", + "span_id": f"0x{trace_api.format_span_id(context.span_id)}", + "trace_state": repr(context.trace_state), + } + + @staticmethod + def _format_attributes( + attributes: types.Attributes, + ) -> dict[str, Any] | None: + if attributes is not None and not isinstance(attributes, dict): + return dict(attributes) + return attributes + + @staticmethod + def _format_events(events: Sequence[Event]) -> list[dict[str, Any]]: + return [ + { + "name": event.name, + "timestamp": util.ns_to_iso_str(event.timestamp), + "attributes": Span._format_attributes( # pylint: disable=protected-access + event.attributes + ), + } + for event in events + ] + + @staticmethod + def _format_links(links: Sequence[trace_api.Link]) -> list[dict[str, Any]]: + return [ + { + "context": Span._format_context( # pylint: disable=protected-access + link.context + ), + "attributes": Span._format_attributes( # pylint: disable=protected-access + link.attributes + ), + } + for link in links + ] + + +class SpanLimits: + """The limits that should be enforced on recorded data such as events, links, attributes etc. + + This class does not enforce any limits itself. It only provides a way to read limits from env, + default values and from user provided arguments. + + All limit arguments must be either a non-negative integer, ``None`` or ``SpanLimits.UNSET``. + + - All limit arguments are optional. + - If a limit argument is not set, the class will try to read its value from the corresponding + environment variable. + - If the environment variable is not set, the default value, if any, will be used. + + Limit precedence: + + - If a model specific limit is set, it will be used. + - Else if the corresponding global limit is set, it will be used. + - Else if the model specific limit has a default value, the default value will be used. + - Else if the global limit has a default value, the default value will be used. + + Args: + max_attributes: Maximum number of attributes that can be added to a span, event, and link. + Environment variable: OTEL_ATTRIBUTE_COUNT_LIMIT + Default: {_DEFAULT_ATTRIBUTE_COUNT_LIMIT} + max_events: Maximum number of events that can be added to a Span. + Environment variable: OTEL_SPAN_EVENT_COUNT_LIMIT + Default: {_DEFAULT_SPAN_EVENT_COUNT_LIMIT} + max_links: Maximum number of links that can be added to a Span. + Environment variable: OTEL_SPAN_LINK_COUNT_LIMIT + Default: {_DEFAULT_SPAN_LINK_COUNT_LIMIT} + max_span_attributes: Maximum number of attributes that can be added to a Span. + Environment variable: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT + Default: {_DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT} + max_event_attributes: Maximum number of attributes that can be added to an Event. + Default: {_DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT} + max_link_attributes: Maximum number of attributes that can be added to a Link. + Default: {_DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT} + max_attribute_length: Maximum length an attribute value can have. Values longer than + the specified length will be truncated. + max_span_attribute_length: Maximum length a span attribute value can have. Values longer than + the specified length will be truncated. + """ + + UNSET = -1 + + def __init__( + self, + max_attributes: int | None = None, + max_events: int | None = None, + max_links: int | None = None, + max_span_attributes: int | None = None, + max_event_attributes: int | None = None, + max_link_attributes: int | None = None, + max_attribute_length: int | None = None, + max_span_attribute_length: int | None = None, + ): + # span events and links count + self.max_events = self._from_env_if_absent( + max_events, + OTEL_SPAN_EVENT_COUNT_LIMIT, + _DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT, + ) + self.max_links = self._from_env_if_absent( + max_links, + OTEL_SPAN_LINK_COUNT_LIMIT, + _DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT, + ) + + # attribute count + global_max_attributes = self._from_env_if_absent(max_attributes, OTEL_ATTRIBUTE_COUNT_LIMIT) + self.max_attributes = ( + global_max_attributes if global_max_attributes is not None else _DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT + ) + + self.max_span_attributes = self._from_env_if_absent( + max_span_attributes, + OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, + (global_max_attributes if global_max_attributes is not None else _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT), + ) + self.max_event_attributes = self._from_env_if_absent( + max_event_attributes, + OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT, + (global_max_attributes if global_max_attributes is not None else _DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT), + ) + self.max_link_attributes = self._from_env_if_absent( + max_link_attributes, + OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, + (global_max_attributes if global_max_attributes is not None else _DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT), + ) + + # attribute length + self.max_attribute_length = self._from_env_if_absent( + max_attribute_length, + OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, + ) + self.max_span_attribute_length = self._from_env_if_absent( + max_span_attribute_length, + OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, + # use global attribute length limit as default + self.max_attribute_length, + ) + + def __repr__(self): + return f"{type(self).__name__}(max_span_attributes={self.max_span_attributes}, max_events_attributes={self.max_event_attributes}, max_link_attributes={self.max_link_attributes}, max_attributes={self.max_attributes}, max_events={self.max_events}, max_links={self.max_links}, max_attribute_length={self.max_attribute_length})" + + @classmethod + def _from_env_if_absent(cls, value: int | None, env_var: str, default: int | None = None) -> int | None: + if value == cls.UNSET: + return None + + err_msg = "{} must be a non-negative integer but got {}" + + # if no value is provided for the limit, try to load it from env + if value is None: + # return default value if env var is not set + if env_var not in environ: + return default + + str_value = environ.get(env_var, "").strip().lower() + if str_value == _ENV_VALUE_UNSET: + return None + + try: + value = int(str_value) + except ValueError: + raise ValueError(err_msg.format(env_var, str_value)) + + if value < 0: + raise ValueError(err_msg.format(env_var, value)) + return value + + +_UnsetLimits = SpanLimits( + max_attributes=SpanLimits.UNSET, + max_events=SpanLimits.UNSET, + max_links=SpanLimits.UNSET, + max_span_attributes=SpanLimits.UNSET, + max_event_attributes=SpanLimits.UNSET, + max_link_attributes=SpanLimits.UNSET, + max_attribute_length=SpanLimits.UNSET, + max_span_attribute_length=SpanLimits.UNSET, +) + +# not removed for backward compat. please use SpanLimits instead. +SPAN_ATTRIBUTE_COUNT_LIMIT = SpanLimits._from_env_if_absent( # pylint: disable=protected-access + None, + OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, + _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, +) + + +class Span(trace_api.Span, ReadableSpan): + """See `opentelemetry.trace.Span`. + + Users should create `Span` objects via the `Tracer` instead of this + constructor. + + Args: + name: The name of the operation this span represents + context: The immutable span context + parent: This span's parent's `opentelemetry.trace.SpanContext`, or + None if this is a root span + sampler: The sampler used to create this span + trace_config: Unused. Originally intended for trace-level configuration + from the OpenTelemetry protocol, but the upstream ``TraceConfig`` + proto was removed. Retained for backwards compatibility. + resource: Entity producing telemetry + attributes: The span's attributes to be exported + events: Timestamped events to be exported + links: Links to other spans to be exported + span_processor: `SpanProcessor` to invoke when starting and ending + this `Span`. + limits: `SpanLimits` instance that was passed to the `TracerProvider` + """ + + def __new__(cls, *args, **kwargs): + if cls is Span: + raise TypeError("Span must be instantiated via a tracer.") + return super().__new__(cls) + + # pylint: disable=too-many-locals + def __init__( + self, + name: str, + context: trace_api.SpanContext, + parent: trace_api.SpanContext | None = None, + sampler: sampling.Sampler | None = None, + trace_config: None = None, + resource: Resource | None = None, + attributes: types.Attributes = None, + events: Sequence[Event] | None = None, + links: Sequence[trace_api.Link] = (), + kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, + span_processor: SpanProcessor = SpanProcessor(), + instrumentation_info: InstrumentationInfo | None = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + limits=_UnsetLimits, + instrumentation_scope: InstrumentationScope | None = None, + *, + record_end_metrics: Callable[[], None] | None = None, + ) -> None: + if resource is None: + resource = Resource.create({}) + super().__init__( + name=name, + context=context, + parent=parent, + kind=kind, + resource=resource, + instrumentation_info=instrumentation_info, + instrumentation_scope=instrumentation_scope, + ) + self._sampler = sampler + self._trace_config = trace_config + self._record_exception = record_exception + self._set_status_on_exception = set_status_on_exception + self._span_processor = span_processor + self._limits = limits + self._lock = threading.Lock() + self._attributes = BoundedAttributes( + self._limits.max_span_attributes, + attributes, + immutable=False, + max_value_len=self._limits.max_span_attribute_length, + ) + self._events = self._new_events() + if events: + for event in events: + # Immutable set to true so attributes cannot be added or removed after creation. + event._attributes = BoundedAttributes( + self._limits.max_event_attributes, + event.attributes, + max_value_len=self._limits.max_attribute_length, + immutable=True, + ) + self._events.append(event) + + self._links = self._new_links(links) + + self._record_end_metrics = record_end_metrics + + def __repr__(self): + return f'{type(self).__name__}(name="{self._name}", context={self._context})' + + def _new_events(self): + return BoundedList(self._limits.max_events) + + def _new_links(self, links: Sequence[trace_api.Link]): + if not links: + return BoundedList(self._limits.max_links) + + valid_links = [] + for link in links: + if link and _is_valid_link(link.context, link.attributes): + # Immutable set to true so attributes cannot be added or removed after creation. + # pylint: disable=protected-access + link._attributes = BoundedAttributes( + self._limits.max_link_attributes, + link.attributes, + max_value_len=self._limits.max_attribute_length, + immutable=True, + ) + valid_links.append(link) + + return BoundedList.from_seq(self._limits.max_links, valid_links) + + def get_span_context(self) -> trace_api.SpanContext: + return typing.cast(trace_api.SpanContext, self._context) + + def set_attributes(self, attributes: Mapping[str, types.AnyValue]) -> None: + with self._lock: + if self._end_time is not None: + logger.warning("Setting attribute on ended span.") + return + + self._attributes._set_items(attributes) # pylint: disable=protected-access + + def set_attribute(self, key: str, value: types.AnyValue) -> None: + with self._lock: + if self._end_time is not None: + logger.warning("Setting attribute on ended span.") + return + + self._attributes[key] = value + + @_check_span_ended + def _add_event(self, event: EventBase) -> None: + self._events.append(event) + + def add_event( + self, + name: str, + attributes: types.Attributes = None, + timestamp: int | None = None, + ) -> None: + # Immutable set to true so attributes cannot be added or removed after creation. + attributes = BoundedAttributes( + self._limits.max_event_attributes, + attributes, + max_value_len=self._limits.max_attribute_length, + immutable=True, + ) + self._add_event( + Event( + name=name, + attributes=attributes, + timestamp=timestamp, + ) + ) + + @_check_span_ended + def _add_link(self, link: trace_api.Link) -> None: + self._links.append(link) + + def add_link( + self, + context: SpanContext, + attributes: types.Attributes = None, + ) -> None: + if not _is_valid_link(context, attributes): + return + # Immutable set to true so attributes cannot be added or removed after creation. + attributes = BoundedAttributes( + self._limits.max_link_attributes, + attributes, + max_value_len=self._limits.max_attribute_length, + immutable=True, + ) + self._add_link( + trace_api.Link( + context=context, + attributes=attributes, + ) + ) + + def _readable_span(self) -> ReadableSpan: + return ReadableSpan( + name=self._name, + context=self._context, + parent=self._parent, + resource=self._resource, + attributes=self._attributes, + events=self._events, + links=self._links, + kind=self.kind, + status=self._status, + start_time=self._start_time, + end_time=self._end_time, + instrumentation_info=self._instrumentation_info, + instrumentation_scope=self._instrumentation_scope, + ) + + def start( + self, + start_time: int | None = None, + parent_context: context_api.Context | None = None, + ) -> None: + with self._lock: + if self._start_time is not None: + logger.warning("Calling start() on a started span.") + return + self._start_time = start_time if start_time is not None else time_ns() + + self._span_processor.on_start(self, parent_context=parent_context) + + def end(self, end_time: int | None = None) -> None: + with self._lock: + if self._start_time is None: + raise RuntimeError("Calling end() on a not started span.") + if self._end_time is not None: + logger.warning("Calling end() on an ended span.") + return + + self._end_time = end_time if end_time is not None else time_ns() + self._attributes._immutable = True # pylint: disable=protected-access + + if self._record_end_metrics: + self._record_end_metrics() + # pylint: disable=protected-access + self._span_processor._on_ending(self) + self._span_processor.on_end(self._readable_span()) + + @_check_span_ended + def update_name(self, name: str) -> None: + self._name = name + + def is_recording(self) -> bool: + return self._end_time is None + + @_check_span_ended + def set_status( + self, + status: Status | StatusCode, + description: str | None = None, + ) -> None: + # Ignore future calls if status is already set to OK + # Ignore calls to set to StatusCode.UNSET + if isinstance(status, Status): + if self._status and self._status.status_code is StatusCode.OK or status.status_code is StatusCode.UNSET: + return + if description is not None: + logger.warning( + "Description %s ignored. Use either `Status` or `(StatusCode, Description)`", + description, + ) + self._status = status + elif isinstance(status, StatusCode): + if self._status and self._status.status_code is StatusCode.OK or status is StatusCode.UNSET: + return + self._status = Status(status, description) + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + """Ends context manager and calls `end` on the `Span`.""" + if exc_val is not None and self.is_recording(): + # Record the exception as an event + # pylint:disable=protected-access + if self._record_exception: + self.record_exception(exception=exc_val, escaped=True) + # Records status if span is used as context manager + # i.e. with tracer.start_span() as span: + if self._set_status_on_exception: + self.set_status( + Status( + status_code=StatusCode.ERROR, + description=(f"{type(exc_val).__name__}: {exc_val}"), + ) + ) + + super().__exit__(exc_type, exc_val, exc_tb) + + def record_exception( + self, + exception: BaseException, + attributes: types.Attributes = None, + timestamp: int | None = None, + escaped: bool = False, + ) -> None: + """Records an exception as a span event.""" + stacktrace = "".join(traceback.format_exception(exception)) + module = type(exception).__module__ + qualname = type(exception).__qualname__ + exception_type = f"{module}.{qualname}" if module and module != "builtins" else qualname + _attributes: MutableMapping[str, types.AnyValue] = { + EXCEPTION_TYPE: exception_type, + EXCEPTION_MESSAGE: str(exception), + EXCEPTION_STACKTRACE: stacktrace, + EXCEPTION_ESCAPED: str(escaped), + } + if attributes: + _attributes.update(attributes) + self.add_event(name="exception", attributes=_attributes, timestamp=timestamp) + + +class _Span(Span): + """Protected implementation of `opentelemetry.trace.Span`. + + This constructor exists to prevent the instantiation of the `Span` class + by other mechanisms than through the `Tracer`. + """ + + +@dataclass +class _TracerConfig: + is_enabled: bool + + @classmethod + def default(cls): + return cls(is_enabled=True) + + +class Tracer(trace_api.Tracer): + """See `opentelemetry.trace.Tracer`.""" + + def __init__( + self, + sampler: sampling.Sampler, + resource: Resource, + span_processor: SynchronousMultiSpanProcessor | ConcurrentMultiSpanProcessor, + id_generator: IdGenerator, + instrumentation_info: InstrumentationInfo, + span_limits: SpanLimits, + instrumentation_scope: InstrumentationScope, + *, + meter_provider: metrics_api.MeterProvider | None = None, + _tracer_config: _TracerConfig | None = None, + ) -> None: + self.sampler = sampler + self.resource = resource + self.span_processor = span_processor + self.id_generator = id_generator + self.instrumentation_info = instrumentation_info + self._span_limits = span_limits + self._instrumentation_scope = instrumentation_scope + self._tracer_config = _tracer_config or _TracerConfig.default() + + meter_provider = meter_provider or metrics_api.get_meter_provider() + self._tracer_metrics = create_tracer_metrics( + meter_provider, + parse_boolean_environment_variable(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED), + ) + + def _set_tracer_config(self, tracer_config: _TracerConfig): + self._tracer_config = tracer_config + + def _set_resource(self, resource: Resource) -> None: + self.resource = resource + + def _is_enabled(self) -> bool: + """If the tracer is not enabled, start_span will create a NonRecordingSpan""" + return self._tracer_config.is_enabled + + @_agnosticcontextmanager # pylint: disable=protected-access + def start_as_current_span( + self, + name: str, + context: context_api.Context | None = None, + kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: Sequence[trace_api.Link] | None = (), + start_time: int | None = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + end_on_exit: bool = True, + ) -> Iterator[trace_api.Span]: + span = self.start_span( + name=name, + context=context, + kind=kind, + attributes=attributes, + links=links, + start_time=start_time, + record_exception=record_exception, + set_status_on_exception=set_status_on_exception, + ) + with trace_api.use_span( + span, + end_on_exit=end_on_exit, + record_exception=record_exception, + set_status_on_exception=set_status_on_exception, + ) as span: + yield span + + def start_span( # pylint: disable=too-many-locals + self, + name: str, + context: context_api.Context | None = None, + kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: Sequence[trace_api.Link] | None = (), + start_time: int | None = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + ) -> trace_api.Span: + links = links or () + parent_span_context = trace_api.get_current_span(context).get_span_context() + + if parent_span_context is not None and not isinstance(parent_span_context, trace_api.SpanContext): + raise TypeError("parent_span_context must be a SpanContext or None.") + + if not self._is_enabled(): + return trace_api.NonRecordingSpan(context=parent_span_context) + + # is_valid determines root span + if parent_span_context is None or not parent_span_context.is_valid: + parent_span_context = None + trace_id = self.id_generator.generate_trace_id() + else: + trace_id = parent_span_context.trace_id + + # The sampler decides whether to create a real or no-op span at the + # time of span creation. No-op spans do not record events, and are not + # exported. + # The sampler may also add attributes to the newly-created span, e.g. + # to include information about the sampling result. + # The sampler may also modify the parent span context's tracestate + sampling_result = self.sampler.should_sample(context, trace_id, name, kind, attributes, links) + + trace_flags = ( + trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED) + if sampling_result.decision.is_sampled() + else trace_api.TraceFlags(trace_api.TraceFlags.DEFAULT) + ) + + if parent_span_context is None: + random_trace_id = self.id_generator.is_trace_id_random() + else: + random_trace_id = parent_span_context.trace_flags.random_trace_id + + if random_trace_id: + trace_flags = trace_api.TraceFlags(trace_flags | trace_api.TraceFlags.RANDOM_TRACE_ID) + + span_context = trace_api.SpanContext( + trace_id, + self.id_generator.generate_span_id(), + is_remote=False, + trace_flags=trace_flags, + trace_state=sampling_result.trace_state, + ) + + record_end_metrics = self._tracer_metrics.start_span(parent_span_context, sampling_result.decision) + + # Only record if is_recording() is true + if sampling_result.decision.is_recording(): + # pylint:disable=protected-access + span = _Span( + name=name, + context=span_context, + parent=parent_span_context, + sampler=self.sampler, + resource=self.resource, + attributes=sampling_result.attributes, + span_processor=self.span_processor, + kind=kind, + links=links, + instrumentation_info=self.instrumentation_info, + record_exception=record_exception, + set_status_on_exception=set_status_on_exception, + limits=self._span_limits, + instrumentation_scope=self._instrumentation_scope, + record_end_metrics=record_end_metrics, + ) + span.start(start_time=start_time, parent_context=context) + else: + span = trace_api.NonRecordingSpan(context=span_context) + return span + + +_TracerConfiguratorT = Callable[[InstrumentationScope], _TracerConfig] +_RuleBasedTracerConfigurator = RuleBasedConfigurator[_TracerConfig] + + +def _default_tracer_configurator( + tracer_scope: InstrumentationScope, +) -> _TracerConfig: + """Default Tracer Configurator implementation + + In order to update Tracers configs you need to call + TracerProvider._set_tracer_configurator with a function + implementing this interface returning a Tracer Config.""" + return _RuleBasedTracerConfigurator( + rules=[], + default_config=_TracerConfig.default(), + )(tracer_scope) + + +def _disable_tracer_configurator( + tracer_scope: InstrumentationScope, +) -> _TracerConfig: + return _RuleBasedTracerConfigurator( + rules=[], + default_config=_TracerConfig(is_enabled=False), + )(tracer_scope) + + +class TracerProvider(trace_api.TracerProvider): + """See `opentelemetry.trace.TracerProvider`.""" + + def __init__( + self, + sampler: sampling.Sampler | None = None, + resource: Resource | None = None, + shutdown_on_exit: bool = True, + active_span_processor: SynchronousMultiSpanProcessor | ConcurrentMultiSpanProcessor | None = None, + id_generator: IdGenerator | None = None, + span_limits: SpanLimits | None = None, + *, + meter_provider: metrics_api.MeterProvider | None = None, + _tracer_configurator: _TracerConfiguratorT | None = None, + ) -> None: + self._active_span_processor = active_span_processor or SynchronousMultiSpanProcessor() + if id_generator is None: + self.id_generator = RandomIdGenerator() + else: + self.id_generator = id_generator + if resource is None: + self._resource = Resource.create({}) + else: + self._resource = resource + if not sampler: + sampler = sampling._get_from_env_or_default() + self.sampler = sampler + self._span_limits = span_limits or SpanLimits() + disabled = environ.get(OTEL_SDK_DISABLED, "") + self._disabled = disabled.lower().strip() == "true" + self._atexit_handler = None + self._meter_provider = meter_provider + + if shutdown_on_exit: + self._atexit_handler = atexit.register(self.shutdown) + + self._tracer_configurator = _tracer_configurator or _default_tracer_configurator + self._tracers_lock = threading.Lock() + self._tracers: dict[InstrumentationScope, Tracer] = {} + if hasattr(os, "register_at_fork"): + weak_at_fork = weakref.WeakMethod(self._handle_fork) + + def _after_in_child() -> None: + if at_fork := weak_at_fork(): + at_fork() + + os.register_at_fork(after_in_child=_after_in_child) + + def _handle_fork(self) -> None: + self._tracers_lock = threading.Lock() + self._update_resource(_get_process_dependent_resource()) + + def _set_tracer_configurator(self, *, tracer_configurator: _TracerConfiguratorT): + """This is the function used to update the TracerProvider TracerConfigurator + + Setting a new TracerConfigurator for a TracerProvider will update the + TracerConfig of all Tracers create by this TracerProvider. + """ + self._tracer_configurator = tracer_configurator + with self._tracers_lock: + for instrumentation_scope, tracer in self._tracers.items(): + tracer_config = self._apply_tracer_configurator(instrumentation_scope) + # pylint: disable-next=protected-access + tracer._set_tracer_config(tracer_config) + + @property + def resource(self) -> Resource: + return self._resource + + def _update_resource(self, resource: Resource) -> None: + with self._tracers_lock: + self._resource = self._resource.merge(resource) + for tracer in self._tracers.values(): + tracer._set_resource(self._resource) # pylint: disable=protected-access + + def _apply_tracer_configurator(self, instrumentation_scope: InstrumentationScope): + try: + return self._tracer_configurator(instrumentation_scope) + except Exception: # pylint: disable=broad-exception-caught + logger.exception( + "Failed to create a Tracer Config for %s, using default Tracer config", + instrumentation_scope, + ) + return _TracerConfig.default() + + def get_tracer( + self, + instrumenting_module_name: str, + instrumenting_library_version: str | None = None, + schema_url: str | None = None, + attributes: types.Attributes = None, + ) -> "trace_api.Tracer": + if self._disabled: + return NoOpTracer() + if not instrumenting_module_name: # Reject empty strings too. + instrumenting_module_name = "" + logger.error("get_tracer called with missing module name.") + if instrumenting_library_version is None: + instrumenting_library_version = "" + + filterwarnings( + "ignore", + message=(r"You should use InstrumentationScope. Deprecated since version 1.11.1."), + category=DeprecationWarning, + module="opentelemetry.sdk.trace", + ) + + instrumentation_info = InstrumentationInfo( + instrumenting_module_name, + instrumenting_library_version, + schema_url, + ) + + instrumentation_scope = InstrumentationScope( + instrumenting_module_name, + instrumenting_library_version, + schema_url, + attributes, + ) + + with self._tracers_lock: + if instrumentation_scope in self._tracers: + return self._tracers[instrumentation_scope] + + tracer_config = self._apply_tracer_configurator(instrumentation_scope) + tracer = Tracer( + self.sampler, + self.resource, + self._active_span_processor, + self.id_generator, + instrumentation_info, + self._span_limits, + instrumentation_scope, + meter_provider=self._meter_provider, + _tracer_config=tracer_config, + ) + self._tracers[instrumentation_scope] = tracer + + return tracer + + def add_span_processor(self, span_processor: SpanProcessor) -> None: + """Registers a new :class:`SpanProcessor` for this `TracerProvider`. + + The span processors are invoked in the same order they are registered. + """ + + # no lock here because add_span_processor is thread safe for both + # SynchronousMultiSpanProcessor and ConcurrentMultiSpanProcessor. + self._active_span_processor.add_span_processor(span_processor) + + def shutdown(self) -> None: + """Shut down the span processors added to the tracer provider.""" + self._active_span_processor.shutdown() + if self._atexit_handler is not None: + atexit.unregister(self._atexit_handler) + self._atexit_handler = None + + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Requests the active span processor to process all spans that have not + yet been processed. + + By default force flush is called sequentially on all added span + processors. This means that span processors further back in the list + have less time to flush their spans. + To have span processors flush their spans in parallel it is possible to + initialize the tracer provider with an instance of + `ConcurrentMultiSpanProcessor` at the cost of using multiple threads. + + Args: + timeout_millis: The maximum amount of time to wait for spans to be + processed. + + Returns: + False if the timeout is exceeded, True otherwise. + """ + return self._active_span_processor.force_flush(timeout_millis) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c910a70 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__pycache__/_tracer_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__pycache__/_tracer_metrics.cpython-312.pyc new file mode 100644 index 0000000..d2834cf Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__pycache__/_tracer_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__pycache__/id_generator.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__pycache__/id_generator.cpython-312.pyc new file mode 100644 index 0000000..8d04ca9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__pycache__/id_generator.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__pycache__/sampling.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__pycache__/sampling.cpython-312.pyc new file mode 100644 index 0000000..40c2f80 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/__pycache__/sampling.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__init__.py new file mode 100644 index 0000000..e838448 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__init__.py @@ -0,0 +1,22 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +__all__ = [ + "ComposableSampler", + "SamplingIntent", + "composable_always_off", + "composable_always_on", + "composable_parent_threshold", + "composable_rule_based", + "composable_traceid_ratio_based", + "composite_sampler", +] + + +from ._always_off import composable_always_off +from ._always_on import composable_always_on +from ._composable import ComposableSampler, SamplingIntent +from ._parent_threshold import composable_parent_threshold +from ._rule_based import composable_rule_based +from ._sampler import composite_sampler +from ._traceid_ratio import composable_traceid_ratio_based diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..48f794c Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_always_off.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_always_off.cpython-312.pyc new file mode 100644 index 0000000..d8aa03b Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_always_off.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_always_on.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_always_on.cpython-312.pyc new file mode 100644 index 0000000..4622a39 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_always_on.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_composable.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_composable.cpython-312.pyc new file mode 100644 index 0000000..64c73c5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_composable.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_parent_threshold.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_parent_threshold.cpython-312.pyc new file mode 100644 index 0000000..8793d54 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_parent_threshold.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_rule_based.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_rule_based.cpython-312.pyc new file mode 100644 index 0000000..bea4cd9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_rule_based.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_sampler.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_sampler.cpython-312.pyc new file mode 100644 index 0000000..8dfa76b Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_sampler.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_trace_state.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_trace_state.cpython-312.pyc new file mode 100644 index 0000000..641a8fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_trace_state.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_traceid_ratio.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_traceid_ratio.cpython-312.pyc new file mode 100644 index 0000000..18dac53 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_traceid_ratio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_util.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_util.cpython-312.pyc new file mode 100644 index 0000000..f7c5412 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_util.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_always_off.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_always_off.py new file mode 100644 index 0000000..7c5c4e1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_always_off.py @@ -0,0 +1,44 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections.abc import Sequence + +from opentelemetry.context import Context +from opentelemetry.trace import Link, SpanKind, TraceState +from opentelemetry.util.types import Attributes + +from ._composable import ComposableSampler, SamplingIntent +from ._util import INVALID_THRESHOLD + +_intent = SamplingIntent(threshold=INVALID_THRESHOLD, threshold_reliable=False) + + +class _ComposableAlwaysOffSampler(ComposableSampler): + def sampling_intent( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None = None, + ) -> SamplingIntent: + return _intent + + def get_description(self) -> str: + return "ComposableAlwaysOff" + + +_always_off = _ComposableAlwaysOffSampler() + + +def composable_always_off() -> ComposableSampler: + """Returns a composable sampler that does not sample any span. + + - Always returns a SamplingIntent with no threshold, indicating all spans should be dropped + - Sets threshold_reliable to false + - Does not add any attributes + """ + return _always_off diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_always_on.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_always_on.py new file mode 100644 index 0000000..fd2dbdb --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_always_on.py @@ -0,0 +1,44 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections.abc import Sequence + +from opentelemetry.context import Context +from opentelemetry.trace import Link, SpanKind, TraceState +from opentelemetry.util.types import Attributes + +from ._composable import ComposableSampler, SamplingIntent +from ._util import MIN_THRESHOLD + +_intent = SamplingIntent(threshold=MIN_THRESHOLD) + + +class _ComposableAlwaysOnSampler(ComposableSampler): + def sampling_intent( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None = None, + ) -> SamplingIntent: + return _intent + + def get_description(self) -> str: + return "ComposableAlwaysOn" + + +_always_on = _ComposableAlwaysOnSampler() + + +def composable_always_on() -> ComposableSampler: + """Returns a composable sampler that samples all spans. + + - Always returns a SamplingIntent with threshold set to sample all spans (threshold = 0) + - Sets threshold_reliable to true + - Does not add any attributes + """ + return _always_on diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_composable.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_composable.py new file mode 100644 index 0000000..6bedc8d --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_composable.py @@ -0,0 +1,49 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections.abc import Callable, Sequence +from dataclasses import dataclass, field +from typing import Protocol + +from opentelemetry.context import Context +from opentelemetry.trace import Link, SpanKind, TraceState +from opentelemetry.util.types import Attributes + + +@dataclass(frozen=True) +class SamplingIntent: + """Information to make a consistent sampling decision.""" + + threshold: int + """The sampling threshold value. A lower threshold increases the likelihood of sampling.""" + + threshold_reliable: bool = field(default=True) + """Indicates whether the threshold is reliable for Span-to-Metrics estimation.""" + + attributes: Attributes = field(default=None) + """Any attributes to be added to a sampled span.""" + + update_trace_state: Callable[[TraceState], TraceState] = field(default=lambda ts: ts) + """Any updates to be made to trace state.""" + + +class ComposableSampler(Protocol): + """A sampler that can be composed to make a final sampling decision.""" + + def sampling_intent( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None, + ) -> SamplingIntent: + """Returns information to make a sampling decision.""" + ... # pylint: disable=unnecessary-ellipsis + + def get_description(self) -> str: + """Returns a description of the sampler.""" + ... # pylint: disable=unnecessary-ellipsis diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_parent_threshold.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_parent_threshold.py new file mode 100644 index 0000000..88af9a5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_parent_threshold.py @@ -0,0 +1,72 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections.abc import Sequence + +from opentelemetry.context import Context +from opentelemetry.trace import Link, SpanKind, TraceState, get_current_span +from opentelemetry.util.types import Attributes + +from ._composable import ComposableSampler, SamplingIntent +from ._trace_state import OtelTraceState +from ._util import ( + INVALID_THRESHOLD, + MIN_THRESHOLD, + is_valid_threshold, +) + + +class _ComposableParentThreshold(ComposableSampler): + def __init__(self, root_sampler: ComposableSampler): + self._root_sampler = root_sampler + self._description = f"ComposableParentThreshold{{root={root_sampler.get_description()}}}" + + def sampling_intent( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None = None, + ) -> SamplingIntent: + parent_span = get_current_span(parent_ctx) + parent_span_ctx = parent_span.get_span_context() + is_root = not parent_span_ctx.is_valid + if is_root: + return self._root_sampler.sampling_intent(parent_ctx, name, span_kind, attributes, links, trace_state) + + ot_trace_state = OtelTraceState.parse(trace_state) + + if is_valid_threshold(ot_trace_state.threshold): + return SamplingIntent( + threshold=ot_trace_state.threshold, + threshold_reliable=True, + ) + + threshold = MIN_THRESHOLD if parent_span_ctx.trace_flags.sampled else INVALID_THRESHOLD + return SamplingIntent(threshold=threshold, threshold_reliable=False) + + def get_description(self) -> str: + return self._description + + +def composable_parent_threshold( + root_sampler: ComposableSampler, +) -> ComposableSampler: + """Returns a consistent sampler that respects the sampling decision of + the parent span or falls-back to the given sampler if it is a root span. + + - For spans without a parent context, delegate to the root sampler + - For spans with a parent context, returns a SamplingIntent that propagates the parent's sampling decision + - Returns the parent's threshold if available; otherwise, if the parent's sampled flag is set, + returns threshold=0; otherwise, if the parent's sampled flag is not set, no threshold is returned. + - Sets threshold_reliable to match the parent’s reliability, which is true if the parent had a threshold. + - Does not add any attributes + + Args: + root_sampler: The root sampler to use for spans without a parent context. + """ + return _ComposableParentThreshold(root_sampler) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_rule_based.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_rule_based.py new file mode 100644 index 0000000..8a0e814 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_rule_based.py @@ -0,0 +1,271 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import logging +from collections.abc import Sequence +from fnmatch import fnmatchcase +from typing import Protocol + +from opentelemetry.context import Context +from opentelemetry.trace import ( + Link, + SpanKind, + TraceState, + get_current_span, +) +from opentelemetry.util.types import AnyValue, Attributes + +from ._composable import ComposableSampler, SamplingIntent +from ._util import INVALID_THRESHOLD + + +class PredicateT(Protocol): + def __call__( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None, + ) -> bool: ... + + def __str__(self) -> str: ... + + +class AttributePredicate: + """An exact match of an attribute value""" + + def __init__(self, key: str, value: AnyValue): + logging.warning("This is deprecated, use AttributeValuesPredicate instead") + self.key = key + self.value = value + + def __call__( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None, + ) -> bool: + if not attributes: + return False + return attributes.get(self.key) == self.value + + def __str__(self): + return f"{self.key}={self.value}" + + +class AlwaysMatchPredicate: + def __call__( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None, + ) -> bool: + return True + + def __str__(self) -> str: + return "AlwaysMatch" + + +class AllPredicate: + def __init__(self, predicates: Sequence[PredicateT]): + self._predicates = tuple(predicates) + + def __call__( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None, + ) -> bool: + return all( + predicate( + parent_ctx, + name, + span_kind, + attributes, + links, + trace_state, + ) + for predicate in self._predicates + ) + + def __str__(self) -> str: + return " && ".join(str(predicate) for predicate in self._predicates) + + +class AttributeValuesPredicate: + def __init__(self, key: str, values: Sequence[str]): + self._key = key + self._values = frozenset(values) + + def __call__( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None, + ) -> bool: + if not attributes or self._key not in attributes: + return False + return any(str(value) in self._values for value in _attribute_values(attributes[self._key])) + + def __str__(self) -> str: + values = ",".join(sorted(self._values)) + return f"{self._key} in [{values}]" + + +class AttributePatternsPredicate: + def __init__( + self, + key: str, + included: Sequence[str] | None = None, + excluded: Sequence[str] | None = None, + ): + self._key = key + self._included = tuple(included or ()) + self._excluded = tuple(excluded or ()) + + def __call__( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None, + ) -> bool: + if not attributes or self._key not in attributes: + return False + return any(self._matches_value(str(value)) for value in _attribute_values(attributes[self._key])) + + def _matches_value(self, value: str) -> bool: + included = not self._included or any(fnmatchcase(value, pattern) for pattern in self._included) + excluded = any(fnmatchcase(value, pattern) for pattern in self._excluded) + return included and not excluded + + def __str__(self) -> str: + return f"{self._key} matches" + + +class SpanKindPredicate: + def __init__(self, span_kinds: Sequence[SpanKind]): + self._span_kinds = frozenset(span_kinds) + + def __call__( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None, + ) -> bool: + return span_kind in self._span_kinds + + def __str__(self) -> str: + kinds = ",".join(kind.name.lower() for kind in self._span_kinds) + return f"span_kind in [{kinds}]" + + +class ParentPredicate: + def __init__(self, parents: Sequence[str]): + self._parents = frozenset(parents) + + def __call__( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None, + ) -> bool: + parent_span_context = get_current_span(parent_ctx).get_span_context() + if not parent_span_context.is_valid: + parent = "none" + elif parent_span_context.is_remote: + parent = "remote" + else: + parent = "local" + return parent in self._parents + + def __str__(self) -> str: + parents = ",".join(self._parents) + return f"parent in [{parents}]" + + +def _attribute_values(value): + if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)): + return value + return (value,) + + +RulesT = Sequence[tuple[PredicateT, ComposableSampler]] + +_non_sampling_intent = SamplingIntent(threshold=INVALID_THRESHOLD, threshold_reliable=False) + + +class _ComposableRuleBased(ComposableSampler): + def __init__(self, rules: RulesT): + # work on an internal copy of the rules + self._rules = list(rules) + + def sampling_intent( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None = None, + ) -> SamplingIntent: + for predicate, sampler in self._rules: + if predicate( + parent_ctx=parent_ctx, + name=name, + span_kind=span_kind, + attributes=attributes, + links=links, + trace_state=trace_state, + ): + return sampler.sampling_intent( + parent_ctx=parent_ctx, + name=name, + span_kind=span_kind, + attributes=attributes, + links=links, + trace_state=trace_state, + ) + return _non_sampling_intent + + def get_description(self) -> str: + rules_str = ",".join(f"({predicate}:{sampler.get_description()})" for predicate, sampler in self._rules) + return f"ComposableRuleBased{{[{rules_str}]}}" + + +def composable_rule_based( + rules: RulesT, +) -> ComposableSampler: + """Returns a consistent sampler that: + + - Evaluates a series of rules based on predicates and returns the SamplingIntent from the first matching sampler + - If no rules match, returns a non-sampling intent + + Args: + rules: A list of (Predicate, ComposableSampler) pairs, where Predicate is a function that evaluates whether a rule applies + """ + return _ComposableRuleBased(rules) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_sampler.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_sampler.py new file mode 100644 index 0000000..7d74a3f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_sampler.py @@ -0,0 +1,88 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections.abc import Sequence + +from opentelemetry.context import Context +from opentelemetry.sdk.trace.sampling import Decision, Sampler, SamplingResult +from opentelemetry.trace import Link, SpanKind, TraceState +from opentelemetry.util.types import Attributes + +from ._composable import ComposableSampler, SamplingIntent +from ._trace_state import OTEL_TRACE_STATE_KEY, OtelTraceState +from ._util import INVALID_THRESHOLD, is_valid_random_value, is_valid_threshold + + +class _CompositeSampler(Sampler): + def __init__(self, delegate: ComposableSampler): + self._delegate = delegate + + def should_sample( + self, + parent_context: Context | None, + trace_id: int, + name: str, + kind: SpanKind | None = None, + attributes: Attributes = None, + links: Sequence[Link] | None = None, + trace_state: TraceState | None = None, + ) -> SamplingResult: + ot_trace_state = OtelTraceState.parse(trace_state) + + intent = self._delegate.sampling_intent(parent_context, name, kind, attributes, links, trace_state) + threshold = intent.threshold + + if is_valid_threshold(threshold): + adjusted_count_correct = intent.threshold_reliable + if is_valid_random_value(ot_trace_state.random_value): + randomness = ot_trace_state.random_value + else: + # Use last 56 bits of trace_id as randomness + randomness = trace_id & 0x00FFFFFFFFFFFFFF + sampled = threshold <= randomness + else: + sampled = False + adjusted_count_correct = False + + decision = Decision.RECORD_AND_SAMPLE if sampled else Decision.DROP + if sampled and adjusted_count_correct: + ot_trace_state.threshold = threshold + else: + ot_trace_state.threshold = INVALID_THRESHOLD + + return SamplingResult( + decision, + intent.attributes, + _update_trace_state(trace_state, ot_trace_state, intent), + ) + + def get_description(self) -> str: + return self._delegate.get_description() + + +def _update_trace_state( + trace_state: TraceState | None, + ot_trace_state: OtelTraceState, + intent: SamplingIntent, +) -> TraceState | None: + otts = ot_trace_state.serialize() + if not trace_state: + if otts: + return TraceState(((OTEL_TRACE_STATE_KEY, otts),)) + return None + new_trace_state = intent.update_trace_state(trace_state) + if otts: + return new_trace_state.update(OTEL_TRACE_STATE_KEY, otts) + return new_trace_state + + +def composite_sampler(delegate: ComposableSampler) -> Sampler: + """A sampler that uses a a composable sampler to make its decision while + handling tracestate. + + Args: + delegate: The composable sampler to use for making sampling decisions. + """ + return _CompositeSampler(delegate) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_trace_state.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_trace_state.py new file mode 100644 index 0000000..8133454 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_trace_state.py @@ -0,0 +1,121 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections.abc import Sequence +from dataclasses import dataclass + +from opentelemetry.trace import TraceState + +from ._util import ( + INVALID_RANDOM_VALUE, + INVALID_THRESHOLD, + MAX_THRESHOLD, + is_valid_random_value, + is_valid_threshold, +) + +OTEL_TRACE_STATE_KEY = "ot" + +_TRACE_STATE_SIZE_LIMIT = 256 +_MAX_VALUE_LENGTH = 14 # 56 bits, 4 bits per hex digit + + +@dataclass +class OtelTraceState: + """Marshals OpenTelemetry tracestate for sampling parameters. + + https://opentelemetry.io/docs/specs/otel/trace/tracestate-probability-sampling/ + """ + + random_value: int + threshold: int + rest: Sequence[str] + + @staticmethod + def invalid() -> OtelTraceState: + return OtelTraceState(INVALID_RANDOM_VALUE, INVALID_THRESHOLD, ()) + + @staticmethod + def parse(trace_state: TraceState | None) -> OtelTraceState: + if not trace_state: + return OtelTraceState.invalid() + + ot = trace_state.get(OTEL_TRACE_STATE_KEY, "") + + if not ot or len(ot) > _TRACE_STATE_SIZE_LIMIT: + return OtelTraceState.invalid() + + threshold = INVALID_THRESHOLD + random_value = INVALID_RANDOM_VALUE + + members = ot.split(";") + rest: list[str] | None = None + for member in members: + if member.startswith("th:"): + threshold = _parse_th(member[len("th:") :], INVALID_THRESHOLD) + continue + if member.startswith("rv:"): + random_value = _parse_rv(member[len("rv:") :], INVALID_RANDOM_VALUE) + continue + if rest is None: + rest = [member] + else: + rest.append(member) + + return OtelTraceState(random_value=random_value, threshold=threshold, rest=rest or ()) + + def serialize(self) -> str: + if not is_valid_threshold(self.threshold) and not is_valid_random_value(self.random_value) and not self.rest: + return "" + + parts: list[str] = [] + if is_valid_threshold(self.threshold) and self.threshold != MAX_THRESHOLD: + parts.append(f"th:{serialize_th(self.threshold)}") + if is_valid_random_value(self.random_value): + parts.append(f"rv:{_serialize_rv(self.random_value)}") + if self.rest: + parts.extend(self.rest) + res = ";".join(parts) + while len(res) > _TRACE_STATE_SIZE_LIMIT: + delim_idx = res.rfind(";") + if delim_idx == -1: + break + res = res[:delim_idx] + return res + + +def _parse_th(value: str, default: int) -> int: + if not value or len(value) > _MAX_VALUE_LENGTH: + return default + + try: + parsed = int(value, 16) + except ValueError: + return default + + # th value is compressed by removing all trailing zeros, + # so we restore them to get the real value. + trailing_zeros = _MAX_VALUE_LENGTH - len(value) + return parsed << (trailing_zeros * 4) + + +def _parse_rv(value: str, default: int) -> int: + if not value or len(value) != _MAX_VALUE_LENGTH: + return default + + try: + return int(value, 16) + except ValueError: + return default + + +def serialize_th(threshold: int) -> str: + if not threshold: + return "0" + return f"{threshold:014x}".rstrip("0") + + +def _serialize_rv(random_value: int) -> str: + return f"{random_value:014x}" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_traceid_ratio.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_traceid_ratio.py new file mode 100644 index 0000000..2a6488f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_traceid_ratio.py @@ -0,0 +1,67 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections.abc import Sequence + +from opentelemetry.context import Context +from opentelemetry.trace import Link, SpanKind, TraceState +from opentelemetry.util.types import Attributes + +from ._composable import ComposableSampler, SamplingIntent +from ._trace_state import serialize_th +from ._util import INVALID_THRESHOLD, MAX_THRESHOLD, calculate_threshold + + +class ComposableTraceIDRatioBased(ComposableSampler): + _threshold: int + _description: str + + def __init__(self, ratio: float): + threshold = calculate_threshold(ratio) + if threshold == MAX_THRESHOLD: + threshold_str = "max" + else: + threshold_str = serialize_th(threshold) + if threshold != MAX_THRESHOLD: + intent = SamplingIntent(threshold=threshold) + else: + intent = SamplingIntent(threshold=INVALID_THRESHOLD, threshold_reliable=False) + self._intent = intent + self._description = f"ComposableTraceIDRatioBased{{threshold={threshold_str}, ratio={ratio}}}" + + def sampling_intent( + self, + parent_ctx: Context | None, + name: str, + span_kind: SpanKind | None, + attributes: Attributes, + links: Sequence[Link] | None, + trace_state: TraceState | None, + ) -> SamplingIntent: + return self._intent + + def get_description(self) -> str: + return self._description + + +def composable_traceid_ratio_based( + ratio: float, +) -> ComposableSampler: + """Returns a composable sampler that samples each span with a fixed ratio. + + - Returns a SamplingIntent with threshold determined by the configured sampling ratio + - Sets threshold_reliable to true + - Does not add any attributes + + Note: + If the ratio is 0, it will behave as an ComposableAlwaysOff sampler instead. + + Args: + ratio: The sampling ratio to use (between 0.0 and 1.0). + """ + if not 0.0 <= ratio <= 1.0: + raise ValueError("Sampling ratio must be between 0.0 and 1.0") + + return ComposableTraceIDRatioBased(ratio) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_util.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_util.py new file mode 100644 index 0000000..dc8c10a --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_sampling_experimental/_util.py @@ -0,0 +1,23 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +RANDOM_VALUE_BITS = 56 +MAX_THRESHOLD = 1 << RANDOM_VALUE_BITS # 0% sampling +MIN_THRESHOLD = 0 # 100% sampling +MAX_RANDOM_VALUE = MAX_THRESHOLD - 1 +INVALID_THRESHOLD = -1 +INVALID_RANDOM_VALUE = -1 + +_probability_threshold_scale = float.fromhex("0x1p56") + + +def calculate_threshold(sampling_probability: float) -> int: + return MAX_THRESHOLD - round(sampling_probability * _probability_threshold_scale) + + +def is_valid_threshold(threshold: int) -> bool: + return MIN_THRESHOLD <= threshold <= MAX_THRESHOLD + + +def is_valid_random_value(random_value: int) -> bool: + return 0 <= random_value <= MAX_RANDOM_VALUE diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_tracer_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_tracer_metrics.py new file mode 100644 index 0000000..480d7b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/_tracer_metrics.py @@ -0,0 +1,103 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections.abc import Callable +from typing import Protocol + +from opentelemetry import metrics as metrics_api +from opentelemetry.sdk.trace.sampling import Decision +from opentelemetry.semconv._incubating.attributes.otel_attributes import ( + OTEL_SPAN_PARENT_ORIGIN, + OTEL_SPAN_SAMPLING_RESULT, + OtelSpanSamplingResultValues, +) +from opentelemetry.semconv._incubating.metrics.otel_metrics import ( + create_otel_sdk_span_live, + create_otel_sdk_span_started, +) +from opentelemetry.trace.span import SpanContext + + +class TracerMetricsT(Protocol): + def start_span( + self, + parent_span_context: SpanContext | None, + sampling_decision: Decision, + ) -> Callable[[], None]: ... + + +# pylint: disable=no-self-use +class NoOpTracerMetrics: + def start_span( + self, + parent_span_context: SpanContext | None, + sampling_decision: Decision, + ) -> Callable[[], None]: + return noop + + +class TracerMetrics: + def __init__(self, meter_provider: metrics_api.MeterProvider) -> None: + meter = meter_provider.get_meter("opentelemetry-sdk") + + self._started_spans = create_otel_sdk_span_started(meter) + self._live_spans = create_otel_sdk_span_live(meter) + + def start_span( + self, + parent_span_context: SpanContext | None, + sampling_decision: Decision, + ) -> Callable[[], None]: + sampling_result_value = sampling_result(sampling_decision) + self._started_spans.add( + 1, + { + OTEL_SPAN_PARENT_ORIGIN: parent_origin(parent_span_context), + OTEL_SPAN_SAMPLING_RESULT: sampling_result_value, + }, + ) + + if not sampling_decision.is_recording(): + return noop + + live_span_attrs = { + OTEL_SPAN_SAMPLING_RESULT: sampling_result_value, + } + self._live_spans.add(1, live_span_attrs) + + def end_span() -> None: + self._live_spans.add(-1, live_span_attrs) + + return end_span + + +def create_tracer_metrics( + meter_provider: metrics_api.MeterProvider, + enabled: bool, +) -> TracerMetricsT: + if not enabled: + return NoOpTracerMetrics() + + return TracerMetrics(meter_provider) + + +def noop() -> None: + pass + + +def parent_origin(span_ctx: SpanContext | None) -> str: + if span_ctx is None: + return "none" + if span_ctx.is_remote: + return "remote" + return "local" + + +def sampling_result(decision: Decision) -> str: + if decision == Decision.RECORD_AND_SAMPLE: + return OtelSpanSamplingResultValues.RECORD_AND_SAMPLE.value + if decision == Decision.RECORD_ONLY: + return OtelSpanSamplingResultValues.RECORD_ONLY.value + return OtelSpanSamplingResultValues.DROP.value diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/export/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/export/__init__.py new file mode 100644 index 0000000..5253341 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/export/__init__.py @@ -0,0 +1,316 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import collections.abc +import logging +import sys +import typing +from enum import Enum +from os import environ, linesep + +from opentelemetry.context import ( + _SUPPRESS_INSTRUMENTATION_KEY, + Context, + attach, + detach, + set_value, +) +from opentelemetry.metrics import MeterProvider, get_meter_provider +from opentelemetry.sdk._shared_internal import ( + BatchProcessor, +) +from opentelemetry.sdk._shared_internal._processor_metrics import ( + create_processor_metrics, +) +from opentelemetry.sdk.environment_variables import ( + OTEL_BSP_EXPORT_TIMEOUT, + OTEL_BSP_MAX_EXPORT_BATCH_SIZE, + OTEL_BSP_MAX_QUEUE_SIZE, + OTEL_BSP_SCHEDULE_DELAY, + OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, +) +from opentelemetry.sdk.environment_variables._internal import ( + parse_boolean_environment_variable, +) +from opentelemetry.sdk.trace import ReadableSpan, Span, SpanProcessor +from opentelemetry.semconv._incubating.attributes.otel_attributes import ( + OtelComponentTypeValues, +) + +_DEFAULT_SCHEDULE_DELAY_MILLIS = 5000 +_DEFAULT_MAX_EXPORT_BATCH_SIZE = 512 +_DEFAULT_EXPORT_TIMEOUT_MILLIS = 30000 +_DEFAULT_MAX_QUEUE_SIZE = 2048 +_ENV_VAR_INT_VALUE_ERROR_MESSAGE = "Unable to parse value for %s as integer. Defaulting to %s." + +logger = logging.getLogger(__name__) + + +class SpanExportResult(Enum): + SUCCESS = 0 + FAILURE = 1 + + +class SpanExporter: + """Interface for exporting spans. + + Interface to be implemented by services that want to export spans recorded + in their own format. + + To export data this MUST be registered to the :class`opentelemetry.sdk.trace.Tracer` using a + `SimpleSpanProcessor` or a `BatchSpanProcessor`. + """ + + def export(self, spans: collections.abc.Sequence[ReadableSpan]) -> SpanExportResult: # pyright: ignore[reportReturnType] + """Exports a batch of telemetry data. + + Args: + spans: The list of `opentelemetry.trace.Span` objects to be exported + + Returns: + The result of the export + """ + + def shutdown(self) -> None: + """Shuts down the exporter. + + Called when the SDK is shut down. + """ + + def force_flush(self, timeout_millis: int = 30000) -> bool: # pyright: ignore[reportReturnType] + """Hint to ensure that the export of any spans the exporter has received + prior to the call to ForceFlush SHOULD be completed as soon as possible, preferably + before returning from this method. + """ + + +class SimpleSpanProcessor(SpanProcessor): + """Simple SpanProcessor implementation. + + SimpleSpanProcessor is an implementation of `SpanProcessor` that + passes ended spans directly to the configured `SpanExporter`. + """ + + def __init__( + self, + span_exporter: SpanExporter, + *, + meter_provider: MeterProvider | None = None, + ): + self.span_exporter = span_exporter + self._shutdown = False + self._metrics = create_processor_metrics( + "traces", + OtelComponentTypeValues.SIMPLE_SPAN_PROCESSOR, + meter_provider or get_meter_provider(), + enabled=parse_boolean_environment_variable(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED), + ) + + def on_start(self, span: Span, parent_context: Context | None = None) -> None: + pass + + def _on_ending(self, span: Span) -> None: + pass + + def on_end(self, span: ReadableSpan) -> None: + if not (span.context and span.context.trace_flags.sampled): + return + if self._shutdown: + logger.warning("Processor is already shutdown, ignoring call") + self._metrics.drop_items(1, "already_shutdown") + return + token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True)) + # Record on submission to the exporter. + self._metrics.finish_items(1) + try: + self.span_exporter.export((span,)) + # pylint: disable=broad-exception-caught + except Exception: + logger.exception("Exception while exporting Span.") + finally: + detach(token) + + def shutdown(self) -> None: + self._shutdown = True + self.span_exporter.shutdown() + + def force_flush(self, timeout_millis: int = 30000) -> bool: + # pylint: disable=unused-argument + return True + + +class BatchSpanProcessor(SpanProcessor): + """Batch span processor implementation. + + `BatchSpanProcessor` is an implementation of `SpanProcessor` that + batches ended spans and pushes them to the configured `SpanExporter`. + + `BatchSpanProcessor` is configurable with the following environment + variables which correspond to constructor parameters: + + - :envvar:`OTEL_BSP_SCHEDULE_DELAY` + - :envvar:`OTEL_BSP_MAX_QUEUE_SIZE` + - :envvar:`OTEL_BSP_MAX_EXPORT_BATCH_SIZE` + - :envvar:`OTEL_BSP_EXPORT_TIMEOUT` + + All the logic for emitting spans, shutting down etc. resides in the `BatchProcessor` class. + """ + + def __init__( + self, + span_exporter: SpanExporter, + max_queue_size: int | None = None, + schedule_delay_millis: float | None = None, + max_export_batch_size: int | None = None, + export_timeout_millis: float | None = None, + *, + meter_provider: MeterProvider | None = None, + ): + if max_queue_size is None: + max_queue_size = BatchSpanProcessor._default_max_queue_size() + + if schedule_delay_millis is None: + schedule_delay_millis = BatchSpanProcessor._default_schedule_delay_millis() + + if max_export_batch_size is None: + max_export_batch_size = BatchSpanProcessor._default_max_export_batch_size() + + # Not used. No way currently to pass timeout to export. + if export_timeout_millis is None: + export_timeout_millis = BatchSpanProcessor._default_export_timeout_millis() + + BatchSpanProcessor._validate_arguments(max_queue_size, schedule_delay_millis, max_export_batch_size) + + self._batch_processor = BatchProcessor( + span_exporter, + schedule_delay_millis, + max_export_batch_size, + export_timeout_millis, + max_queue_size, + "Span", + create_processor_metrics( + "traces", + OtelComponentTypeValues.BATCHING_SPAN_PROCESSOR, + meter_provider or get_meter_provider(), + capacity=max_queue_size, + enabled=parse_boolean_environment_variable(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED), + ), + ) + + # Added for backward compatibility. Not recommended to directly access/use underlying exporter. + @property + def span_exporter(self): + return self._batch_processor._exporter # pylint: disable=protected-access + + def on_start(self, span: Span, parent_context: Context | None = None) -> None: + pass + + def _on_ending(self, span: Span) -> None: + pass + + def on_end(self, span: ReadableSpan) -> None: + if not (span.context and span.context.trace_flags.sampled): + return + self._batch_processor.emit(span) + + def shutdown(self): + return self._batch_processor.shutdown() + + def force_flush(self, timeout_millis: int | None = None) -> bool: + return self._batch_processor.force_flush(timeout_millis) + + @staticmethod + def _default_max_queue_size(): + try: + return int(environ.get(OTEL_BSP_MAX_QUEUE_SIZE, _DEFAULT_MAX_QUEUE_SIZE)) + except ValueError: + logger.exception( + _ENV_VAR_INT_VALUE_ERROR_MESSAGE, + OTEL_BSP_MAX_QUEUE_SIZE, + _DEFAULT_MAX_QUEUE_SIZE, + ) + return _DEFAULT_MAX_QUEUE_SIZE + + @staticmethod + def _default_schedule_delay_millis(): + try: + return int(environ.get(OTEL_BSP_SCHEDULE_DELAY, _DEFAULT_SCHEDULE_DELAY_MILLIS)) + except ValueError: + logger.exception( + _ENV_VAR_INT_VALUE_ERROR_MESSAGE, + OTEL_BSP_SCHEDULE_DELAY, + _DEFAULT_SCHEDULE_DELAY_MILLIS, + ) + return _DEFAULT_SCHEDULE_DELAY_MILLIS + + @staticmethod + def _default_max_export_batch_size(): + try: + return int( + environ.get( + OTEL_BSP_MAX_EXPORT_BATCH_SIZE, + _DEFAULT_MAX_EXPORT_BATCH_SIZE, + ) + ) + except ValueError: + logger.exception( + _ENV_VAR_INT_VALUE_ERROR_MESSAGE, + OTEL_BSP_MAX_EXPORT_BATCH_SIZE, + _DEFAULT_MAX_EXPORT_BATCH_SIZE, + ) + return _DEFAULT_MAX_EXPORT_BATCH_SIZE + + @staticmethod + def _default_export_timeout_millis(): + try: + return int(environ.get(OTEL_BSP_EXPORT_TIMEOUT, _DEFAULT_EXPORT_TIMEOUT_MILLIS)) + except ValueError: + logger.exception( + _ENV_VAR_INT_VALUE_ERROR_MESSAGE, + OTEL_BSP_EXPORT_TIMEOUT, + _DEFAULT_EXPORT_TIMEOUT_MILLIS, + ) + return _DEFAULT_EXPORT_TIMEOUT_MILLIS + + @staticmethod + def _validate_arguments(max_queue_size, schedule_delay_millis, max_export_batch_size): + if max_queue_size <= 0: + raise ValueError("max_queue_size must be a positive integer.") + + if schedule_delay_millis <= 0: + raise ValueError("schedule_delay_millis must be positive.") + + if max_export_batch_size <= 0: + raise ValueError("max_export_batch_size must be a positive integer.") + + if max_export_batch_size > max_queue_size: + raise ValueError("max_export_batch_size must be less than or equal to max_queue_size.") + + +class ConsoleSpanExporter(SpanExporter): + """Implementation of :class:`SpanExporter` that prints spans to the + console. + + This class can be used for diagnostic purposes. It prints the exported + spans to the console STDOUT. + """ + + def __init__( + self, + service_name: str | None = None, + out: typing.IO = sys.stdout, + formatter: collections.abc.Callable[[ReadableSpan], str] = lambda span: span.to_json() + linesep, + ): + self.out = out + self.formatter = formatter + self.service_name = service_name + + def export(self, spans: collections.abc.Sequence[ReadableSpan]) -> SpanExportResult: + for span in spans: + self.out.write(self.formatter(span)) + self.out.flush() + return SpanExportResult.SUCCESS + + def force_flush(self, timeout_millis: int = 30000) -> bool: + return True diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/export/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/export/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..9af4c68 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/export/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/export/__pycache__/in_memory_span_exporter.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/export/__pycache__/in_memory_span_exporter.cpython-312.pyc new file mode 100644 index 0000000..126be30 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/export/__pycache__/in_memory_span_exporter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/export/in_memory_span_exporter.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/export/in_memory_span_exporter.py new file mode 100644 index 0000000..dbe0da1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/export/in_memory_span_exporter.py @@ -0,0 +1,56 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import collections.abc +import threading +from collections import deque + +from opentelemetry.sdk.trace import ReadableSpan +from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult + + +class InMemorySpanExporter(SpanExporter): + """Implementation of :class:`.SpanExporter` that stores spans in memory. + + This class can be used for testing purposes. It stores the exported spans + in a deque in memory that can be retrieved using the + :func:`.get_finished_spans` method. + + Args: + max_spans: Maximum number of spans to store in memory. When the limit + is reached, the oldest spans are dropped automatically. If ``None`` + (default), there is no limit. + """ + + def __init__(self, max_spans: int | None = None) -> None: + self._finished_spans: deque[ReadableSpan] = deque(maxlen=max_spans) + self._stopped = False + self._lock = threading.Lock() + + def clear(self) -> None: + """Clear list of collected spans.""" + with self._lock: + self._finished_spans.clear() + + def get_finished_spans(self) -> tuple[ReadableSpan, ...]: + """Get list of collected spans.""" + with self._lock: + return tuple(self._finished_spans) + + def export(self, spans: collections.abc.Sequence[ReadableSpan]) -> SpanExportResult: + """Stores a list of spans in memory.""" + if self._stopped: + return SpanExportResult.FAILURE + with self._lock: + self._finished_spans.extend(spans) + return SpanExportResult.SUCCESS + + def shutdown(self) -> None: + """Shut downs the exporter. + + Calls to export after the exporter has been shut down will fail. + """ + self._stopped = True + + def force_flush(self, timeout_millis: int = 30000) -> bool: + return True diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/id_generator.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/id_generator.py new file mode 100644 index 0000000..f23e333 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/id_generator.py @@ -0,0 +1,72 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import abc +import random + +from opentelemetry import trace + + +class IdGenerator(abc.ABC): + @abc.abstractmethod + def generate_span_id(self) -> int: + """Get a new span ID. + + Returns: + A 64-bit int for use as a span ID + """ + + @abc.abstractmethod + def generate_trace_id(self) -> int: + """Get a new trace ID. + + Implementations should at least make the 56 least significant bits + uniformly random. Samplers like the `TraceIdRatioBased` sampler rely on + this randomness to make sampling decisions. + + If the implementation does randomly generate the 56 least significant bits, + it should also implement `is_trace_id_random` to return True. + + See `the specification on TraceIdRatioBased `_. + + Returns: + A 128-bit int for use as a trace ID + """ + + # pylint: disable=no-self-use + def is_trace_id_random(self) -> bool: + """Indicates whether generated trace IDs are random. + + When True, the `trace-id` field will have the `random-trace-id` flag set + in the W3C traceparent header. Per the W3C Trace Context specification, + this indicates that at least the 7 rightmost bytes (56 bits) of the + trace ID were generated randomly with uniform distribution. + + See `the W3C Trace Context specification `_. + + Returns: + True if this generator produces random IDs, False otherwise. + """ + # By default, return False for backwards compatibility. + return False + + +class RandomIdGenerator(IdGenerator): + """The default ID generator for TracerProvider which randomly generates all + bits when generating IDs. + """ + + def generate_span_id(self) -> int: + span_id = random.getrandbits(64) + while span_id == trace.INVALID_SPAN_ID: + span_id = random.getrandbits(64) + return span_id + + def generate_trace_id(self) -> int: + trace_id = random.getrandbits(128) + while trace_id == trace.INVALID_TRACE_ID: + trace_id = random.getrandbits(128) + return trace_id + + def is_trace_id_random(self) -> bool: + return True diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/sampling.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/sampling.py new file mode 100644 index 0000000..156b659 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/trace/sampling.py @@ -0,0 +1,488 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +""" +For general information about sampling, see `the specification `_. + +OpenTelemetry provides two types of samplers: + +- `StaticSampler` +- `TraceIdRatioBased` + +A `StaticSampler` always returns the same sampling result regardless of the conditions. Both possible StaticSamplers are already created: + +- Always sample spans: ALWAYS_ON +- Never sample spans: ALWAYS_OFF + +A `TraceIdRatioBased` sampler makes a random sampling result based on the sampling probability given. + +If the span being sampled has a parent, `ParentBased` will respect the parent delegate sampler. Otherwise, it returns the sampling result from the given root sampler. + +Currently, sampling results are always made during the creation of the span. However, this might not always be the case in the future (see `OTEP #115 `_). + +Custom samplers can be created by subclassing `Sampler` and implementing `Sampler.should_sample` as well as `Sampler.get_description`. + +Samplers are able to modify the `opentelemetry.trace.span.TraceState` of the parent of the span being created. For custom samplers, it is suggested to implement `Sampler.should_sample` to utilize the +parent span context's `opentelemetry.trace.span.TraceState` and pass into the `SamplingResult` instead of the explicit trace_state field passed into the parameter of `Sampler.should_sample`. + +To use a sampler, pass it into the tracer provider constructor. For example: + +.. code:: python + + from opentelemetry import trace + from opentelemetry.sdk.trace import TracerProvider + from opentelemetry.sdk.trace.export import ( + ConsoleSpanExporter, + SimpleSpanProcessor, + ) + from opentelemetry.sdk.trace.sampling import TraceIdRatioBased + + # sample 1 in every 1000 traces + sampler = TraceIdRatioBased(1/1000) + + # set the sampler onto the global tracer provider + trace.set_tracer_provider(TracerProvider(sampler=sampler)) + + # set up an exporter for sampled spans + trace.get_tracer_provider().add_span_processor( + SimpleSpanProcessor(ConsoleSpanExporter()) + ) + + # created spans will now be sampled by the TraceIdRatioBased sampler + with trace.get_tracer(__name__).start_as_current_span("Test Span"): + ... + +The tracer sampler can also be configured via environment variables ``OTEL_TRACES_SAMPLER`` and ``OTEL_TRACES_SAMPLER_ARG`` (only if applicable). +The list of built-in values for ``OTEL_TRACES_SAMPLER`` are: + + * always_on - Sampler that always samples spans, regardless of the parent span's sampling decision. + * always_off - Sampler that never samples spans, regardless of the parent span's sampling decision. + * traceidratio - Sampler that samples probabilistically based on rate. + * parentbased_always_on - (default) Sampler that respects its parent span's sampling decision, but otherwise always samples. + * parentbased_always_off - Sampler that respects its parent span's sampling decision, but otherwise never samples. + * parentbased_traceidratio - Sampler that respects its parent span's sampling decision, but otherwise samples probabilistically based on rate. + +Sampling probability can be set with ``OTEL_TRACES_SAMPLER_ARG`` if the sampler is traceidratio or parentbased_traceidratio. Rate must be in the range [0.0,1.0]. When not provided rate will be set to +1.0 (maximum rate possible). + +Prev example but with environment variables. Please make sure to set the env ``OTEL_TRACES_SAMPLER=traceidratio`` and ``OTEL_TRACES_SAMPLER_ARG=0.001``. + +.. code:: python + + from opentelemetry import trace + from opentelemetry.sdk.trace import TracerProvider + from opentelemetry.sdk.trace.export import ( + ConsoleSpanExporter, + SimpleSpanProcessor, + ) + + trace.set_tracer_provider(TracerProvider()) + + # set up an exporter for sampled spans + trace.get_tracer_provider().add_span_processor( + SimpleSpanProcessor(ConsoleSpanExporter()) + ) + + # created spans will now be sampled by the TraceIdRatioBased sampler with rate 1/1000. + with trace.get_tracer(__name__).start_as_current_span("Test Span"): + ... + +When utilizing a configurator, you can configure a custom sampler. In order to create a configurable custom sampler, create an entry point for the custom sampler +factory method or function under the entry point group, ``opentelemetry_traces_sampler``. The custom sampler factory method must be of type ``Callable[[str], Sampler]``, taking a single string argument and +returning a Sampler object. The single input will come from the string value of the ``OTEL_TRACES_SAMPLER_ARG`` environment variable. If ``OTEL_TRACES_SAMPLER_ARG`` is not configured, the input will +be an empty string. For example: + +.. code:: python + + setup( + ... + entry_points={ + ... + "opentelemetry_traces_sampler": [ + "custom_sampler_name = path.to.sampler.factory.method:CustomSamplerFactory.get_sampler" + ] + } + ) + # ... + class CustomRatioSampler(Sampler): + def __init__(rate): + # ... + # ... + class CustomSamplerFactory: + @staticmethod + def get_sampler(sampler_argument): + try: + rate = float(sampler_argument) + return CustomSampler(rate) + except ValueError: # In case argument is empty string. + return CustomSampler(0.5) + +In order to configure you application with a custom sampler's entry point, set the ``OTEL_TRACES_SAMPLER`` environment variable to the key name of the entry point. For example, to configured the +above sampler, set ``OTEL_TRACES_SAMPLER=custom_sampler_name`` and ``OTEL_TRACES_SAMPLER_ARG=0.5``. +""" + +from __future__ import annotations + +import abc +import enum +import os +from collections.abc import Sequence +from logging import getLogger +from types import MappingProxyType + +# pylint: disable=unused-import +from opentelemetry.context import Context +from opentelemetry.sdk.environment_variables import ( + OTEL_TRACES_SAMPLER, + OTEL_TRACES_SAMPLER_ARG, +) +from opentelemetry.trace import Link, SpanKind, get_current_span +from opentelemetry.trace.span import TraceState +from opentelemetry.util.types import Attributes + +_logger = getLogger(__name__) + + +class Decision(enum.Enum): + # IsRecording() == false, span will not be recorded and all events and attributes will be dropped. + DROP = 0 + # IsRecording() == true, but Sampled flag MUST NOT be set. + RECORD_ONLY = 1 + # IsRecording() == true AND Sampled flag` MUST be set. + RECORD_AND_SAMPLE = 2 + + def is_recording(self): + return self in (Decision.RECORD_ONLY, Decision.RECORD_AND_SAMPLE) + + def is_sampled(self): + return self is Decision.RECORD_AND_SAMPLE + + +class SamplingResult: + """A sampling result as applied to a newly-created Span. + + Args: + decision: A sampling decision based off of whether the span is recorded + and the sampled flag in trace flags in the span context. + attributes: Attributes to add to the `opentelemetry.trace.Span`. + trace_state: The tracestate used for the `opentelemetry.trace.Span`. + Could possibly have been modified by the sampler. + """ + + def __repr__(self) -> str: + return f"{type(self).__name__}({self.decision!s}, attributes={self.attributes!s})" + + def __init__( + self, + decision: Decision, + attributes: Attributes = None, + trace_state: TraceState | None = None, + ) -> None: + self.decision = decision + if attributes is None: + self.attributes = MappingProxyType({}) + else: + self.attributes = MappingProxyType(attributes) + self.trace_state = trace_state + + +class Sampler(abc.ABC): + @abc.abstractmethod + def should_sample( + self, + parent_context: Context | None, + trace_id: int, + name: str, + kind: SpanKind | None = None, + attributes: Attributes = None, + links: Sequence[Link] | None = None, + trace_state: TraceState | None = None, + ) -> SamplingResult: + pass + + @abc.abstractmethod + def get_description(self) -> str: + pass + + +class StaticSampler(Sampler): + """Sampler that always returns the same decision.""" + + def __init__(self, decision: Decision) -> None: + self._decision = decision + + def should_sample( + self, + parent_context: Context | None, + trace_id: int, + name: str, + kind: SpanKind | None = None, + attributes: Attributes = None, + links: Sequence[Link] | None = None, + trace_state: TraceState | None = None, + ) -> SamplingResult: + if self._decision is Decision.DROP: + attributes = None + return SamplingResult( + self._decision, + attributes, + _get_parent_trace_state(parent_context), + ) + + def get_description(self) -> str: + if self._decision is Decision.DROP: + return "AlwaysOffSampler" + return "AlwaysOnSampler" + + +ALWAYS_OFF = StaticSampler(Decision.DROP) +"""Sampler that never samples spans, regardless of the parent span's sampling decision.""" + +ALWAYS_ON = StaticSampler(Decision.RECORD_AND_SAMPLE) +"""Sampler that always samples spans, regardless of the parent span's sampling decision.""" + + +class TraceIdRatioBased(Sampler): + """ + Sampler that makes sampling decisions probabilistically based on `rate`. + + Args: + rate: Probability (between 0 and 1) that a span will be sampled + """ + + def __init__(self, rate: float): + if rate < 0.0 or rate > 1.0: + raise ValueError("Probability must be in range [0.0, 1.0].") + self._rate = rate + self._bound = self.get_bound_for_rate(self._rate) + + # For compatibility with 64 bit trace IDs, the sampler checks the 64 + # low-order bits of the trace ID to decide whether to sample a given trace. + TRACE_ID_LIMIT = (1 << 64) - 1 + + @classmethod + def get_bound_for_rate(cls, rate: float) -> int: + return round(rate * (cls.TRACE_ID_LIMIT + 1)) + + @property + def rate(self) -> float: + return self._rate + + @property + def bound(self) -> int: + return self._bound + + def should_sample( + self, + parent_context: Context | None, + trace_id: int, + name: str, + kind: SpanKind | None = None, + attributes: Attributes = None, + links: Sequence[Link] | None = None, + trace_state: TraceState | None = None, + ) -> SamplingResult: + decision = Decision.DROP + if trace_id & self.TRACE_ID_LIMIT < self.bound: + decision = Decision.RECORD_AND_SAMPLE + if decision is Decision.DROP: + attributes = None + return SamplingResult( + decision, + attributes, + _get_parent_trace_state(parent_context), + ) + + def get_description(self) -> str: + return f"TraceIdRatioBased{{{self._rate}}}" + + +class ParentBased(Sampler): + """ + If a parent is set, applies the respective delegate sampler. + Otherwise, uses the root provided at initialization to make a + decision. + + Args: + root: Sampler called for spans with no parent (root spans). + remote_parent_sampled: Sampler called for a remote sampled parent. + remote_parent_not_sampled: Sampler called for a remote parent that is + not sampled. + local_parent_sampled: Sampler called for a local sampled parent. + local_parent_not_sampled: Sampler called for a local parent that is + not sampled. + """ + + def __init__( + self, + root: Sampler, + remote_parent_sampled: Sampler = ALWAYS_ON, + remote_parent_not_sampled: Sampler = ALWAYS_OFF, + local_parent_sampled: Sampler = ALWAYS_ON, + local_parent_not_sampled: Sampler = ALWAYS_OFF, + ): + self._root = root + self._remote_parent_sampled = remote_parent_sampled + self._remote_parent_not_sampled = remote_parent_not_sampled + self._local_parent_sampled = local_parent_sampled + self._local_parent_not_sampled = local_parent_not_sampled + + def should_sample( + self, + parent_context: Context | None, + trace_id: int, + name: str, + kind: SpanKind | None = None, + attributes: Attributes = None, + links: Sequence[Link] | None = None, + trace_state: TraceState | None = None, + ) -> SamplingResult: + parent_span_context = get_current_span(parent_context).get_span_context() + # default to the root sampler + sampler = self._root + # respect the sampling and remote flag of the parent if present + if parent_span_context is not None and parent_span_context.is_valid: + if parent_span_context.is_remote: + if parent_span_context.trace_flags.sampled: + sampler = self._remote_parent_sampled + else: + sampler = self._remote_parent_not_sampled + else: + if parent_span_context.trace_flags.sampled: + sampler = self._local_parent_sampled + else: + sampler = self._local_parent_not_sampled + + return sampler.should_sample( + parent_context=parent_context, + trace_id=trace_id, + name=name, + kind=kind, + attributes=attributes, + links=links, + ) + + def get_description(self): + return f"ParentBased{{root:{self._root.get_description()},remoteParentSampled:{self._remote_parent_sampled.get_description()},remoteParentNotSampled:{self._remote_parent_not_sampled.get_description()},localParentSampled:{self._local_parent_sampled.get_description()},localParentNotSampled:{self._local_parent_not_sampled.get_description()}}}" + + +class AlwaysRecordSampler(Sampler): + """ + This sampler will return the sampling result of the provided `root`, unless the + sampling result contains the sampling decision `Decision.DROP`, in which case, a + new sampling result will be returned that is functionally equivalent to the original, except that + it contains the sampling decision `Decision.RECORD_ONLY`. This ensures that all + spans are recorded, with no change to sampling. + + The intended use case of this sampler is to provide a means of sending all spans to a + processor without having an impact on the sampling rate. This may be desirable if a user wishes + to count or otherwise measure all spans produced in a service, without incurring the cost of 100% + sampling. + """ + + def __init__(self, root: Sampler): + if root is None: + raise ValueError("root must not be None") + self._root = root + + def should_sample( + self, + parent_context: Context | None, + trace_id: int, + name: str, + kind: SpanKind | None = None, + attributes: Attributes = None, + links: Sequence[Link] | None = None, + trace_state: TraceState | None = None, + ) -> SamplingResult: + result: SamplingResult = self._root.should_sample( + parent_context, + trace_id, + name, + kind, + attributes, + links, + trace_state, + ) + if result.decision is Decision.DROP: + result = SamplingResult(Decision.RECORD_ONLY, result.attributes, result.trace_state) + + return result + + def get_description(self): + return f"AlwaysRecordSampler{{{self._root.get_description()}}}" + + +DEFAULT_OFF = ParentBased(ALWAYS_OFF) +"""Sampler that respects its parent span's sampling decision, but otherwise never samples.""" + +DEFAULT_ON = ParentBased(ALWAYS_ON) +"""Sampler that respects its parent span's sampling decision, but otherwise always samples.""" + + +class ParentBasedTraceIdRatio(ParentBased): + """ + Sampler that respects its parent span's sampling decision, but otherwise + samples probabilistically based on `rate`. + """ + + def __init__(self, rate: float): + root = TraceIdRatioBased(rate=rate) + super().__init__(root=root) + + +class _AlwaysOff(StaticSampler): + def __init__(self, _): + super().__init__(Decision.DROP) + + +class _AlwaysOn(StaticSampler): + def __init__(self, _): + super().__init__(Decision.RECORD_AND_SAMPLE) + + +class _ParentBasedAlwaysOff(ParentBased): + def __init__(self, _): + super().__init__(ALWAYS_OFF) + + +class _ParentBasedAlwaysOn(ParentBased): + def __init__(self, _): + super().__init__(ALWAYS_ON) + + +_KNOWN_SAMPLERS = { + "always_on": ALWAYS_ON, + "always_off": ALWAYS_OFF, + "parentbased_always_on": DEFAULT_ON, + "parentbased_always_off": DEFAULT_OFF, + "traceidratio": TraceIdRatioBased, + "parentbased_traceidratio": ParentBasedTraceIdRatio, +} + + +def _get_from_env_or_default() -> Sampler: + trace_sampler = os.getenv(OTEL_TRACES_SAMPLER, "parentbased_always_on").lower() + if trace_sampler not in _KNOWN_SAMPLERS: + _logger.warning("Couldn't recognize sampler %s.", trace_sampler) + trace_sampler = "parentbased_always_on" + + if trace_sampler in ("traceidratio", "parentbased_traceidratio"): + try: + rate = float(os.getenv(OTEL_TRACES_SAMPLER_ARG, "")) + return _KNOWN_SAMPLERS[trace_sampler](rate) + except (ValueError, TypeError): + _logger.warning("Could not convert TRACES_SAMPLER_ARG to float. Using default rate 1.0.") + return _KNOWN_SAMPLERS[trace_sampler](1.0) + + return _KNOWN_SAMPLERS[trace_sampler] + + +def _get_parent_trace_state( + parent_context: Context | None, +) -> TraceState | None: + parent_span_context = get_current_span(parent_context).get_span_context() + if parent_span_context is None or not parent_span_context.is_valid: + return None + return parent_span_context.trace_state diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__init__.py new file mode 100644 index 0000000..d689f4d --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__init__.py @@ -0,0 +1,140 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import copy +import datetime +import threading +from collections import deque +from collections.abc import MutableMapping, Sequence + +from typing_extensions import deprecated + + +def ns_to_iso_str(nanoseconds): + """Get an ISO 8601 string from time_ns value.""" + ts = datetime.datetime.fromtimestamp(nanoseconds / 1e9, tz=datetime.timezone.utc) + return ts.strftime("%Y-%m-%dT%H:%M:%S.%fZ") + + +def get_dict_as_key(labels): + """Converts a dict to be used as a unique key""" + return tuple( + sorted( + map( + lambda kv: (kv[0], tuple(kv[1])) if isinstance(kv[1], list) else kv, + labels.items(), + ) + ) + ) + + +class BoundedList(Sequence): + """An append only list with a fixed max size. + + Calls to `append` and `extend` will drop the oldest elements if there is + not enough room. + """ + + def __init__(self, maxlen: int | None): + self.dropped = 0 + self._dq = deque(maxlen=maxlen) # type: deque + self._lock = threading.Lock() + + def __deepcopy__(self, memo): + copy_ = BoundedList(0) + memo[id(self)] = copy_ + with self._lock: + copy_.dropped = self.dropped + copy_._dq = copy.deepcopy(self._dq, memo) + return copy_ + + def __repr__(self): + return f"{type(self).__name__}({list(self._dq)}, maxlen={self._dq.maxlen})" + + def __getitem__(self, index): + return self._dq[index] + + def __len__(self): + return len(self._dq) + + def __iter__(self): + with self._lock: + return iter(list(self._dq)) + + def append(self, item): + with self._lock: + if self._dq.maxlen is not None and len(self._dq) == self._dq.maxlen: + self.dropped += 1 + self._dq.append(item) + + def extend(self, seq): + with self._lock: + if self._dq.maxlen is not None: + to_drop = len(seq) + len(self._dq) - self._dq.maxlen + if to_drop > 0: + self.dropped += to_drop + self._dq.extend(seq) + + @classmethod + def from_seq(cls, maxlen, seq): + seq = tuple(seq) + bounded_list = cls(maxlen) + bounded_list.extend(seq) + return bounded_list + + +@deprecated("Deprecated since version 1.4.0.") +class BoundedDict(MutableMapping): + """An ordered dict with a fixed max capacity. + + Oldest elements are dropped when the dict is full and a new element is + added. + """ + + def __init__(self, maxlen: int | None): + if maxlen is not None: + if not isinstance(maxlen, int): + raise ValueError + if maxlen < 0: + raise ValueError + self.maxlen = maxlen + self.dropped = 0 + self._dict = {} # type: dict + self._lock = threading.Lock() # type: threading.Lock + + def __repr__(self): + return f"{type(self).__name__}({dict(self._dict)}, maxlen={self.maxlen})" + + def __getitem__(self, key): + return self._dict[key] + + def __setitem__(self, key, value): + with self._lock: + if self.maxlen is not None and self.maxlen == 0: + self.dropped += 1 + return + + if key in self._dict: + del self._dict[key] + elif self.maxlen is not None and len(self._dict) == self.maxlen: + del self._dict[next(iter(self._dict.keys()))] + self.dropped += 1 + self._dict[key] = value + + def __delitem__(self, key): + del self._dict[key] + + def __iter__(self): + with self._lock: + return iter(self._dict.copy()) + + def __len__(self): + return len(self._dict) + + @classmethod + def from_map(cls, maxlen, mapping): + mapping = dict(mapping) + bounded_dict = cls(maxlen) + for key, value in mapping.items(): + bounded_dict[key] = value + return bounded_dict diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__init__.pyi b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__init__.pyi new file mode 100644 index 0000000..1b76965 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__init__.pyi @@ -0,0 +1,76 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import ( + Iterable, + Iterator, + Mapping, + MutableMapping, + Sequence, +) +from typing import ( + Any, + TypeVar, + overload, +) + +from opentelemetry.util.types import AnyValue, AttributesAsKey + +_T = TypeVar("_T") +_KT = TypeVar("_KT") +_VT = TypeVar("_VT") + +def ns_to_iso_str(nanoseconds: int) -> str: ... +def get_dict_as_key( + labels: Mapping[str, AnyValue], +) -> AttributesAsKey: ... + +# pylint: disable=no-self-use +class BoundedList(Sequence[_T]): + """An append only list with a fixed max size. + + Calls to `append` and `extend` will drop the oldest elements if there is + not enough room. + """ + + dropped: int + def __init__(self, maxlen: int | None): ... + def __deepcopy__(self, memo: dict[int, Any]) -> BoundedList[_T]: ... + def insert(self, index: int, value: _T) -> None: ... + @overload + def __getitem__(self, i: int) -> _T: ... + @overload + def __getitem__(self, s: slice) -> Sequence[_T]: ... + def __len__(self) -> int: ... + def append(self, item: _T) -> None: ... + def extend(self, seq: Sequence[_T]) -> None: ... + @classmethod + def from_seq(cls, maxlen: int | None, seq: Iterable[_T]) -> BoundedList[_T]: ... # pylint: disable=undefined-variable + +class BoundedDict(MutableMapping[_KT, _VT]): + """An ordered dict with a fixed max capacity. + + Oldest elements are dropped when the dict is full and a new element is + added. + """ + + dropped: int + def __init__(self, maxlen: int): ... + def __getitem__(self, k: _KT) -> _VT: ... + def __setitem__(self, k: _KT, v: _VT) -> None: ... + def __delitem__(self, v: _KT) -> None: ... + def __iter__(self) -> Iterator[_KT]: ... + def __len__(self) -> int: ... + @classmethod + def from_map(cls, maxlen: int, mapping: Mapping[_KT, _VT]) -> BoundedDict[_KT, _VT]: ... # pylint: disable=undefined-variable diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f07bb25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__pycache__/_configurator.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__pycache__/_configurator.cpython-312.pyc new file mode 100644 index 0000000..507a184 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__pycache__/_configurator.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__pycache__/instrumentation.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__pycache__/instrumentation.cpython-312.pyc new file mode 100644 index 0000000..03acc95 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/__pycache__/instrumentation.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/_configurator.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/_configurator.py new file mode 100644 index 0000000..d431495 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/_configurator.py @@ -0,0 +1,28 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +from collections.abc import Sequence +from typing import Generic, TypeVar + +from opentelemetry.sdk.util.instrumentation import ( + InstrumentationScope, + _InstrumentationScopePredicateT, +) + +ConfigT = TypeVar("ConfigT") +ConfiguratorRulesT = Sequence[tuple[_InstrumentationScopePredicateT, ConfigT]] + + +class RuleBasedConfigurator(Generic[ConfigT]): + def __init__(self, *, rules: ConfiguratorRulesT, default_config: ConfigT): + self._rules = rules + self._default_config = default_config + + def __call__(self, scope: InstrumentationScope) -> ConfigT: + for predicate, config in self._rules: + if predicate(scope): + return config + # by default return default config + return self._default_config + + def update_rules(self, rules: ConfiguratorRulesT) -> None: + self._rules = rules diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/instrumentation.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/instrumentation.py new file mode 100644 index 0000000..e41ca76 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/util/instrumentation.py @@ -0,0 +1,179 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +from collections.abc import Callable +from fnmatch import fnmatchcase +from json import dumps + +from typing_extensions import deprecated + +from opentelemetry.attributes import BoundedAttributes +from opentelemetry.util.types import ( # TODO: see if we can remove F401 when using new sphinx version # noqa: F401 # pylint: disable=unused-import + AnyValue, + Attributes, +) + + +class InstrumentationInfo: + """Immutable information about an instrumentation library module. + + See `opentelemetry.trace.TracerProvider.get_tracer` for the meaning of these + properties. + """ + + __slots__ = ("_name", "_version", "_schema_url") + + @deprecated("You should use InstrumentationScope. Deprecated since version 1.11.1.") + def __init__( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + ): + self._name = name + self._version = version + if schema_url is None: + schema_url = "" + self._schema_url = schema_url + + def __repr__(self): + return f"{type(self).__name__}({self._name}, {self._version}, {self._schema_url})" + + def __hash__(self): + return hash((self._name, self._version, self._schema_url)) + + def __eq__(self, value): + return type(value) is type(self) and ( + self._name, + self._version, + self._schema_url, + ) == (value._name, value._version, value._schema_url) + + def __lt__(self, value): + if type(value) is not type(self): + return NotImplemented + return (self._name, self._version, self._schema_url) < ( + value._name, + value._version, + value._schema_url, + ) + + @property + def schema_url(self) -> str | None: + return self._schema_url + + @property + def version(self) -> str | None: + return self._version + + @property + def name(self) -> str: + return self._name + + +class InstrumentationScope: + """A logical unit of the application code with which the emitted telemetry can be + associated. + + See `opentelemetry.trace.TracerProvider.get_tracer` for the meaning of these + properties. + """ + + __slots__ = ( + "_name", + "_version", + "_schema_url", + "_attributes", + ) + + def __init__( + self, + name: str, + version: str | None = None, + schema_url: str | None = None, + attributes: Attributes = None, + ) -> None: + self._name = name + self._version = version + if schema_url is None: + schema_url = "" + self._schema_url = schema_url + # Attributes cannot be added/removed after creation. + self._attributes = BoundedAttributes(attributes=attributes, immutable=True) + + def __repr__(self) -> str: + return f"{type(self).__name__}({self._name}, {self._version}, {self._schema_url}, {self._attributes})" + + def __hash__(self) -> int: + return hash((self._name, self._version, self._schema_url)) + + def __eq__(self, value: object) -> bool: + if not isinstance(value, InstrumentationScope): + return NotImplemented + return ( + self._name, + self._version, + self._schema_url, + self._attributes, + ) == ( + value._name, + value._version, + value._schema_url, + value._attributes, + ) + + def __lt__(self, value: object) -> bool: + if not isinstance(value, InstrumentationScope): + return NotImplemented + return ( + self._name, + self._version, + self._schema_url, + self._attributes, + ) < ( + value._name, + value._version, + value._schema_url, + value._attributes, + ) + + @property + def schema_url(self) -> str | None: + return self._schema_url + + @property + def version(self) -> str | None: + return self._version + + @property + def name(self) -> str: + return self._name + + @property + def attributes(self) -> Attributes: + return self._attributes + + def to_json(self, indent: int | None = 4) -> str: + return dumps( + { + "name": self._name, + "version": self._version, + "schema_url": self._schema_url, + "attributes": (dict(self._attributes) if bool(self._attributes) else None), + }, + indent=indent, + ) + + +_InstrumentationScopePredicateT = Callable[[InstrumentationScope], bool] + + +def _scope_name_matches_glob( + glob_pattern: str, +) -> _InstrumentationScopePredicateT: + def inner(scope: InstrumentationScope) -> bool: + # Scope name matching is case-sensitive. fnmatchcase is used instead of + # fnmatch so it does not rely on the host platform's filename case + # sensitivity (normcase), which lower-cases both operands on Windows. + return fnmatchcase(scope.name, glob_pattern) + + return inner diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/version/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/sdk/version/__init__.py new file mode 100644 index 0000000..178452f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/sdk/version/__init__.py @@ -0,0 +1,4 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +__version__ = "1.45.0" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/sdk/version/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/sdk/version/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..68b114c Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/sdk/version/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..692a63e Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/__pycache__/schemas.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/__pycache__/schemas.cpython-312.pyc new file mode 100644 index 0000000..ed179e5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/__pycache__/schemas.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/app_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/app_attributes.cpython-312.pyc new file mode 100644 index 0000000..33627ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/app_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/artifact_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/artifact_attributes.cpython-312.pyc new file mode 100644 index 0000000..b139ac8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/artifact_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/aws_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/aws_attributes.cpython-312.pyc new file mode 100644 index 0000000..ad4a670 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/aws_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/az_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/az_attributes.cpython-312.pyc new file mode 100644 index 0000000..9642303 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/az_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/azure_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/azure_attributes.cpython-312.pyc new file mode 100644 index 0000000..c78d69f Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/azure_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/browser_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/browser_attributes.cpython-312.pyc new file mode 100644 index 0000000..2f70bc3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/browser_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cassandra_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cassandra_attributes.cpython-312.pyc new file mode 100644 index 0000000..c8928f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cassandra_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cicd_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cicd_attributes.cpython-312.pyc new file mode 100644 index 0000000..e1cee77 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cicd_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/client_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/client_attributes.cpython-312.pyc new file mode 100644 index 0000000..0176906 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/client_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cloud_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cloud_attributes.cpython-312.pyc new file mode 100644 index 0000000..3f48824 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cloud_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cloudevents_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cloudevents_attributes.cpython-312.pyc new file mode 100644 index 0000000..d385bec Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cloudevents_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cloudfoundry_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cloudfoundry_attributes.cpython-312.pyc new file mode 100644 index 0000000..2b6e70b Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cloudfoundry_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/code_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/code_attributes.cpython-312.pyc new file mode 100644 index 0000000..78f3b64 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/code_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/container_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/container_attributes.cpython-312.pyc new file mode 100644 index 0000000..178ed36 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/container_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cpu_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cpu_attributes.cpython-312.pyc new file mode 100644 index 0000000..0e53a57 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cpu_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cpython_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cpython_attributes.cpython-312.pyc new file mode 100644 index 0000000..780f5a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/cpython_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/db_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/db_attributes.cpython-312.pyc new file mode 100644 index 0000000..f595e28 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/db_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/deployment_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/deployment_attributes.cpython-312.pyc new file mode 100644 index 0000000..2d3442f Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/deployment_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/destination_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/destination_attributes.cpython-312.pyc new file mode 100644 index 0000000..8398fb8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/destination_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/device_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/device_attributes.cpython-312.pyc new file mode 100644 index 0000000..a73af6d Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/device_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/disk_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/disk_attributes.cpython-312.pyc new file mode 100644 index 0000000..6cc265c Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/disk_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/dns_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/dns_attributes.cpython-312.pyc new file mode 100644 index 0000000..5bf1cdb Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/dns_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/elasticsearch_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/elasticsearch_attributes.cpython-312.pyc new file mode 100644 index 0000000..5a64900 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/elasticsearch_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/enduser_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/enduser_attributes.cpython-312.pyc new file mode 100644 index 0000000..4aeb0e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/enduser_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/error_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/error_attributes.cpython-312.pyc new file mode 100644 index 0000000..766030b Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/error_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/event_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/event_attributes.cpython-312.pyc new file mode 100644 index 0000000..2e73531 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/event_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/exception_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/exception_attributes.cpython-312.pyc new file mode 100644 index 0000000..61d43c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/exception_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/faas_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/faas_attributes.cpython-312.pyc new file mode 100644 index 0000000..de0ccff Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/faas_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/feature_flag_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/feature_flag_attributes.cpython-312.pyc new file mode 100644 index 0000000..a39af1e Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/feature_flag_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/file_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/file_attributes.cpython-312.pyc new file mode 100644 index 0000000..a275f2f Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/file_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/gcp_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/gcp_attributes.cpython-312.pyc new file mode 100644 index 0000000..14cb92f Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/gcp_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/gen_ai_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/gen_ai_attributes.cpython-312.pyc new file mode 100644 index 0000000..2e4f60b Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/gen_ai_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/geo_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/geo_attributes.cpython-312.pyc new file mode 100644 index 0000000..ebab834 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/geo_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/graphql_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/graphql_attributes.cpython-312.pyc new file mode 100644 index 0000000..4f2ef3a Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/graphql_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/heroku_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/heroku_attributes.cpython-312.pyc new file mode 100644 index 0000000..d52fbb5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/heroku_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/host_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/host_attributes.cpython-312.pyc new file mode 100644 index 0000000..7115468 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/host_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/http_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/http_attributes.cpython-312.pyc new file mode 100644 index 0000000..e5b8953 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/http_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/hw_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/hw_attributes.cpython-312.pyc new file mode 100644 index 0000000..a491492 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/hw_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/jsonrpc_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/jsonrpc_attributes.cpython-312.pyc new file mode 100644 index 0000000..b2598c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/jsonrpc_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/k8s_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/k8s_attributes.cpython-312.pyc new file mode 100644 index 0000000..a206ff0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/k8s_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/linux_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/linux_attributes.cpython-312.pyc new file mode 100644 index 0000000..86de2fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/linux_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/log_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/log_attributes.cpython-312.pyc new file mode 100644 index 0000000..923904a Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/log_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/mainframe_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/mainframe_attributes.cpython-312.pyc new file mode 100644 index 0000000..1c96898 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/mainframe_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/mcp_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/mcp_attributes.cpython-312.pyc new file mode 100644 index 0000000..fe51a2b Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/mcp_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/message_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/message_attributes.cpython-312.pyc new file mode 100644 index 0000000..a2b27c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/message_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/messaging_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/messaging_attributes.cpython-312.pyc new file mode 100644 index 0000000..bdfda54 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/messaging_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/net_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/net_attributes.cpython-312.pyc new file mode 100644 index 0000000..89543d6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/net_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/network_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/network_attributes.cpython-312.pyc new file mode 100644 index 0000000..321ece0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/network_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/nfs_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/nfs_attributes.cpython-312.pyc new file mode 100644 index 0000000..8132a54 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/nfs_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/oci_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/oci_attributes.cpython-312.pyc new file mode 100644 index 0000000..b3fd615 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/oci_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/onc_rpc_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/onc_rpc_attributes.cpython-312.pyc new file mode 100644 index 0000000..36d55b3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/onc_rpc_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/openai_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/openai_attributes.cpython-312.pyc new file mode 100644 index 0000000..b1e331e Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/openai_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/openshift_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/openshift_attributes.cpython-312.pyc new file mode 100644 index 0000000..e2f625d Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/openshift_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/opentracing_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/opentracing_attributes.cpython-312.pyc new file mode 100644 index 0000000..1367538 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/opentracing_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/oracle_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/oracle_attributes.cpython-312.pyc new file mode 100644 index 0000000..b293dc6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/oracle_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/oracle_cloud_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/oracle_cloud_attributes.cpython-312.pyc new file mode 100644 index 0000000..a5b0075 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/oracle_cloud_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/os_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/os_attributes.cpython-312.pyc new file mode 100644 index 0000000..3e76d94 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/os_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/otel_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/otel_attributes.cpython-312.pyc new file mode 100644 index 0000000..5aaf890 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/otel_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/other_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/other_attributes.cpython-312.pyc new file mode 100644 index 0000000..8fc4403 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/other_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/peer_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/peer_attributes.cpython-312.pyc new file mode 100644 index 0000000..c1e6829 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/peer_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/pool_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/pool_attributes.cpython-312.pyc new file mode 100644 index 0000000..9993580 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/pool_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/pprof_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/pprof_attributes.cpython-312.pyc new file mode 100644 index 0000000..fe4fcb0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/pprof_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/process_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/process_attributes.cpython-312.pyc new file mode 100644 index 0000000..6716a9d Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/process_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/profile_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/profile_attributes.cpython-312.pyc new file mode 100644 index 0000000..c2b7a01 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/profile_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/rpc_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/rpc_attributes.cpython-312.pyc new file mode 100644 index 0000000..1aecd57 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/rpc_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/security_rule_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/security_rule_attributes.cpython-312.pyc new file mode 100644 index 0000000..ed5125a Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/security_rule_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/server_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/server_attributes.cpython-312.pyc new file mode 100644 index 0000000..213f71a Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/server_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/service_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/service_attributes.cpython-312.pyc new file mode 100644 index 0000000..93aae2f Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/service_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/session_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/session_attributes.cpython-312.pyc new file mode 100644 index 0000000..80d3f0e Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/session_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/source_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/source_attributes.cpython-312.pyc new file mode 100644 index 0000000..8e6d864 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/source_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/system_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/system_attributes.cpython-312.pyc new file mode 100644 index 0000000..b7fd574 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/system_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/telemetry_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/telemetry_attributes.cpython-312.pyc new file mode 100644 index 0000000..d32f889 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/telemetry_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/test_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/test_attributes.cpython-312.pyc new file mode 100644 index 0000000..65a28f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/test_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/thread_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/thread_attributes.cpython-312.pyc new file mode 100644 index 0000000..fce4237 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/thread_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/tls_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/tls_attributes.cpython-312.pyc new file mode 100644 index 0000000..4152310 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/tls_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/url_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/url_attributes.cpython-312.pyc new file mode 100644 index 0000000..847b2f9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/url_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/user_agent_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/user_agent_attributes.cpython-312.pyc new file mode 100644 index 0000000..20defbe Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/user_agent_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/user_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/user_attributes.cpython-312.pyc new file mode 100644 index 0000000..629465e Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/user_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/vcs_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/vcs_attributes.cpython-312.pyc new file mode 100644 index 0000000..59df9dd Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/vcs_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/webengine_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/webengine_attributes.cpython-312.pyc new file mode 100644 index 0000000..f2a7f89 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/webengine_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/zos_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/zos_attributes.cpython-312.pyc new file mode 100644 index 0000000..abd0749 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/__pycache__/zos_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/app_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/app_attributes.py new file mode 100644 index 0000000..77289c2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/app_attributes.py @@ -0,0 +1,90 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +APP_BUILD_ID: Final = "app.build_id" +""" +Unique identifier for a particular build or compilation of the application. +""" + +APP_CRASH_ID: Final = "app.crash.id" +""" +A unique identifier representing an instance of an end user facing app crash. +Note: Its value MAY be meaningful and be used as a reference for telemetry and metadata recorded by +the same instrumentation (e.g. it is an ID generated by an external source that captured the crash). +It MAY come from a source external to the instrumentation such that it can be used to look up additional +data from other sources as well as facilitate deduplication. +""" + +APP_INSTALLATION_ID: Final = "app.installation.id" +""" +A unique identifier representing the installation of an application on a specific device. +Note: Its value SHOULD persist across launches of the same application installation, including through application upgrades. +It SHOULD change if the application is uninstalled or if all applications of the vendor are uninstalled. +Additionally, users might be able to reset this value (e.g. by clearing application data). +If an app is installed multiple times on the same device (e.g. in different accounts on Android), each `app.installation.id` SHOULD have a different value. +If multiple OpenTelemetry SDKs are used within the same application, they SHOULD use the same value for `app.installation.id`. +Hardware IDs (e.g. serial number, IMEI, MAC address) MUST NOT be used as the `app.installation.id`. + +For iOS, this value SHOULD be equal to the [vendor identifier](https://developer.apple.com/documentation/uikit/uidevice/identifierforvendor). + +For Android, examples of `app.installation.id` implementations include: + +- [Firebase Installation ID](https://firebase.google.com/docs/projects/manage-installations). +- A globally unique UUID which is persisted across sessions in your application. +- [App set ID](https://developer.android.com/identity/app-set-id). +- [`Settings.getString(Settings.Secure.ANDROID_ID)`](https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID). + +More information about Android identifier best practices can be found in the [Android user data IDs guide](https://developer.android.com/training/articles/user-data-ids). +""" + +APP_JANK_FRAME_COUNT: Final = "app.jank.frame_count" +""" +A number of frame renders that experienced jank. +Note: Depending on platform limitations, the value provided MAY be approximation. +""" + +APP_JANK_PERIOD: Final = "app.jank.period" +""" +The time period, in seconds, for which this jank is being reported. +""" + +APP_JANK_THRESHOLD: Final = "app.jank.threshold" +""" +The minimum rendering threshold for this jank, in seconds. +""" + +APP_SCREEN_COORDINATE_X: Final = "app.screen.coordinate.x" +""" +The x (horizontal) coordinate of a screen coordinate, in screen pixels. +""" + +APP_SCREEN_COORDINATE_Y: Final = "app.screen.coordinate.y" +""" +The y (vertical) component of a screen coordinate, in screen pixels. +""" + +APP_SCREEN_ID: Final = "app.screen.id" +""" +An identifier that uniquely differentiates this screen from other screens in the same application. +Note: A screen represents only the part of the device display drawn by the app. It typically contains multiple widgets or UI components and is larger in scope than individual widgets. Multiple screens can coexist on the same display simultaneously (e.g., split view on tablets). +""" + +APP_SCREEN_NAME: Final = "app.screen.name" +""" +The name of an application screen. +Note: A screen represents only the part of the device display drawn by the app. It typically contains multiple widgets or UI components and is larger in scope than individual widgets. Multiple screens can coexist on the same display simultaneously (e.g., split view on tablets). +""" + +APP_WIDGET_ID: Final = "app.widget.id" +""" +An identifier that uniquely differentiates this widget from other widgets in the same application. +Note: A widget is an application component, typically an on-screen visual GUI element. +""" + +APP_WIDGET_NAME: Final = "app.widget.name" +""" +The name of an application widget. +Note: A widget is an application component, typically an on-screen visual GUI element. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/artifact_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/artifact_attributes.py new file mode 100644 index 0000000..ab3dcef --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/artifact_attributes.py @@ -0,0 +1,51 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +ARTIFACT_ATTESTATION_FILENAME: Final = "artifact.attestation.filename" +""" +The provenance filename of the built attestation which directly relates to the build artifact filename. This filename SHOULD accompany the artifact at publish time. See the [SLSA Relationship](https://slsa.dev/spec/v1.0/distributing-provenance#relationship-between-artifacts-and-attestations) specification for more information. +""" + +ARTIFACT_ATTESTATION_HASH: Final = "artifact.attestation.hash" +""" +The full [hash value (see glossary)](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf), of the built attestation. Some envelopes in the [software attestation space](https://github.com/in-toto/attestation/tree/main/spec) also refer to this as the **digest**. +""" + +ARTIFACT_ATTESTATION_ID: Final = "artifact.attestation.id" +""" +The ID of the build [software attestation](https://slsa.dev/attestation-model). +""" + +ARTIFACT_FILENAME: Final = "artifact.filename" +""" +The human readable filename of the artifact, typically generated during build and release processes. Often includes the package name and version in the filename. +Note: This filename can also act as the [Package Name](https://slsa.dev/spec/v1.0/terminology#package-model) +in cases where the package ecosystem maps accordingly. +Additionally, the artifact [can be published](https://slsa.dev/spec/v1.0/terminology#software-supply-chain) +for others, but that is not a guarantee. +""" + +ARTIFACT_HASH: Final = "artifact.hash" +""" +The full [hash value (see glossary)](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf), often found in checksum.txt on a release of the artifact and used to verify package integrity. +Note: The specific algorithm used to create the cryptographic hash value is +not defined. In situations where an artifact has multiple +cryptographic hashes, it is up to the implementer to choose which +hash value to set here; this should be the most secure hash algorithm +that is suitable for the situation and consistent with the +corresponding attestation. The implementer can then provide the other +hash values through an additional set of attribute extensions as they +deem necessary. +""" + +ARTIFACT_PURL: Final = "artifact.purl" +""" +The [Package URL](https://github.com/package-url/purl-spec) of the [package artifact](https://slsa.dev/spec/v1.0/terminology#package-model) provides a standard way to identify and locate the packaged artifact. +""" + +ARTIFACT_VERSION: Final = "artifact.version" +""" +The version of the artifact. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/aws_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/aws_attributes.py new file mode 100644 index 0000000..e9113aa --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/aws_attributes.py @@ -0,0 +1,316 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +AWS_BEDROCK_GUARDRAIL_ID: Final = "aws.bedrock.guardrail.id" +""" +The unique identifier of the AWS Bedrock Guardrail. A [guardrail](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html) helps safeguard and prevent unwanted behavior from model responses or user messages. +""" + +AWS_BEDROCK_KNOWLEDGE_BASE_ID: Final = "aws.bedrock.knowledge_base.id" +""" +The unique identifier of the AWS Bedrock Knowledge base. A [knowledge base](https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base.html) is a bank of information that can be queried by models to generate more relevant responses and augment prompts. +""" + +AWS_DYNAMODB_ATTRIBUTE_DEFINITIONS: Final = "aws.dynamodb.attribute_definitions" +""" +The JSON-serialized value of each item in the `AttributeDefinitions` request field. +""" + +AWS_DYNAMODB_ATTRIBUTES_TO_GET: Final = "aws.dynamodb.attributes_to_get" +""" +The value of the `AttributesToGet` request parameter. +""" + +AWS_DYNAMODB_CONSISTENT_READ: Final = "aws.dynamodb.consistent_read" +""" +The value of the `ConsistentRead` request parameter. +""" + +AWS_DYNAMODB_CONSUMED_CAPACITY: Final = "aws.dynamodb.consumed_capacity" +""" +The JSON-serialized value of each item in the `ConsumedCapacity` response field. +""" + +AWS_DYNAMODB_COUNT: Final = "aws.dynamodb.count" +""" +The value of the `Count` response parameter. +""" + +AWS_DYNAMODB_EXCLUSIVE_START_TABLE: Final = "aws.dynamodb.exclusive_start_table" +""" +The value of the `ExclusiveStartTableName` request parameter. +""" + +AWS_DYNAMODB_GLOBAL_SECONDARY_INDEX_UPDATES: Final = "aws.dynamodb.global_secondary_index_updates" +""" +The JSON-serialized value of each item in the `GlobalSecondaryIndexUpdates` request field. +""" + +AWS_DYNAMODB_GLOBAL_SECONDARY_INDEXES: Final = "aws.dynamodb.global_secondary_indexes" +""" +The JSON-serialized value of each item of the `GlobalSecondaryIndexes` request field. +""" + +AWS_DYNAMODB_INDEX_NAME: Final = "aws.dynamodb.index_name" +""" +The value of the `IndexName` request parameter. +""" + +AWS_DYNAMODB_ITEM_COLLECTION_METRICS: Final = "aws.dynamodb.item_collection_metrics" +""" +The JSON-serialized value of the `ItemCollectionMetrics` response field. +""" + +AWS_DYNAMODB_LIMIT: Final = "aws.dynamodb.limit" +""" +The value of the `Limit` request parameter. +""" + +AWS_DYNAMODB_LOCAL_SECONDARY_INDEXES: Final = "aws.dynamodb.local_secondary_indexes" +""" +The JSON-serialized value of each item of the `LocalSecondaryIndexes` request field. +""" + +AWS_DYNAMODB_PROJECTION: Final = "aws.dynamodb.projection" +""" +The value of the `ProjectionExpression` request parameter. +""" + +AWS_DYNAMODB_PROVISIONED_READ_CAPACITY: Final = "aws.dynamodb.provisioned_read_capacity" +""" +The value of the `ProvisionedThroughput.ReadCapacityUnits` request parameter. +""" + +AWS_DYNAMODB_PROVISIONED_WRITE_CAPACITY: Final = "aws.dynamodb.provisioned_write_capacity" +""" +The value of the `ProvisionedThroughput.WriteCapacityUnits` request parameter. +""" + +AWS_DYNAMODB_SCAN_FORWARD: Final = "aws.dynamodb.scan_forward" +""" +The value of the `ScanIndexForward` request parameter. +""" + +AWS_DYNAMODB_SCANNED_COUNT: Final = "aws.dynamodb.scanned_count" +""" +The value of the `ScannedCount` response parameter. +""" + +AWS_DYNAMODB_SEGMENT: Final = "aws.dynamodb.segment" +""" +The value of the `Segment` request parameter. +""" + +AWS_DYNAMODB_SELECT: Final = "aws.dynamodb.select" +""" +The value of the `Select` request parameter. +""" + +AWS_DYNAMODB_TABLE_COUNT: Final = "aws.dynamodb.table_count" +""" +The number of items in the `TableNames` response parameter. +""" + +AWS_DYNAMODB_TABLE_NAMES: Final = "aws.dynamodb.table_names" +""" +The keys in the `RequestItems` object field. +""" + +AWS_DYNAMODB_TOTAL_SEGMENTS: Final = "aws.dynamodb.total_segments" +""" +The value of the `TotalSegments` request parameter. +""" + +AWS_ECS_CLUSTER_ARN: Final = "aws.ecs.cluster.arn" +""" +The ARN of an [ECS cluster](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html). +""" + +AWS_ECS_CONTAINER_ARN: Final = "aws.ecs.container.arn" +""" +The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). +""" + +AWS_ECS_LAUNCHTYPE: Final = "aws.ecs.launchtype" +""" +The [launch type](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/launch_types.html) for an ECS task. +""" + +AWS_ECS_TASK_ARN: Final = "aws.ecs.task.arn" +""" +The ARN of a running [ECS task](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-account-settings.html#ecs-resource-ids). +""" + +AWS_ECS_TASK_FAMILY: Final = "aws.ecs.task.family" +""" +The family name of the [ECS task definition](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html) used to create the ECS task. +""" + +AWS_ECS_TASK_ID: Final = "aws.ecs.task.id" +""" +The ID of a running ECS task. The ID MUST be extracted from `task.arn`. +""" + +AWS_ECS_TASK_REVISION: Final = "aws.ecs.task.revision" +""" +The revision for the task definition used to create the ECS task. +""" + +AWS_EKS_CLUSTER_ARN: Final = "aws.eks.cluster.arn" +""" +The ARN of an EKS cluster. +""" + +AWS_EXTENDED_REQUEST_ID: Final = "aws.extended_request_id" +""" +The AWS extended request ID as returned in the response header `x-amz-id-2`. +""" + +AWS_KINESIS_STREAM_NAME: Final = "aws.kinesis.stream_name" +""" +The name of the AWS Kinesis [stream](https://docs.aws.amazon.com/streams/latest/dev/introduction.html) the request refers to. Corresponds to the `--stream-name` parameter of the Kinesis [describe-stream](https://docs.aws.amazon.com/cli/latest/reference/kinesis/describe-stream.html) operation. +""" + +AWS_LAMBDA_INVOKED_ARN: Final = "aws.lambda.invoked_arn" +""" +The full invoked ARN as provided on the `Context` passed to the function (`Lambda-Runtime-Invoked-Function-Arn` header on the `/runtime/invocation/next` applicable). +Note: This may be different from `cloud.resource_id` if an alias is involved. +""" + +AWS_LAMBDA_RESOURCE_MAPPING_ID: Final = "aws.lambda.resource_mapping.id" +""" +The UUID of the [AWS Lambda EvenSource Mapping](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html). An event source is mapped to a lambda function. It's contents are read by Lambda and used to trigger a function. This isn't available in the lambda execution context or the lambda runtime environment. This is going to be populated by the AWS SDK for each language when that UUID is present. Some of these operations are Create/Delete/Get/List/Update EventSourceMapping. +""" + +AWS_LOG_GROUP_ARNS: Final = "aws.log.group.arns" +""" +The Amazon Resource Name(s) (ARN) of the AWS log group(s). +Note: See the [log group ARN format documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). +""" + +AWS_LOG_GROUP_NAMES: Final = "aws.log.group.names" +""" +The name(s) of the AWS log group(s) an application is writing to. +Note: Multiple log groups must be supported for cases like multi-container applications, where a single application has sidecar containers, and each write to their own log group. +""" + +AWS_LOG_STREAM_ARNS: Final = "aws.log.stream.arns" +""" +The ARN(s) of the AWS log stream(s). +Note: See the [log stream ARN format documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). One log group can contain several log streams, so these ARNs necessarily identify both a log group and a log stream. +""" + +AWS_LOG_STREAM_NAMES: Final = "aws.log.stream.names" +""" +The name(s) of the AWS log stream(s) an application is writing to. +""" + +AWS_REQUEST_ID: Final = "aws.request_id" +""" +The AWS request ID as returned in the response headers `x-amzn-requestid`, `x-amzn-request-id` or `x-amz-request-id`. +""" + +AWS_S3_BUCKET: Final = "aws.s3.bucket" +""" +The S3 bucket name the request refers to. Corresponds to the `--bucket` parameter of the [S3 API](https://docs.aws.amazon.com/cli/latest/reference/s3api/index.html) operations. +Note: The `bucket` attribute is applicable to all S3 operations that reference a bucket, i.e. that require the bucket name as a mandatory parameter. +This applies to almost all S3 operations except `list-buckets`. +""" + +AWS_S3_COPY_SOURCE: Final = "aws.s3.copy_source" +""" +The source object (in the form `bucket`/`key`) for the copy operation. +Note: The `copy_source` attribute applies to S3 copy operations and corresponds to the `--copy-source` parameter +of the [copy-object operation within the S3 API](https://docs.aws.amazon.com/cli/latest/reference/s3api/copy-object.html). +This applies in particular to the following operations: + +- [copy-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/copy-object.html) +- [upload-part-copy](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part-copy.html). +""" + +AWS_S3_DELETE: Final = "aws.s3.delete" +""" +The delete request container that specifies the objects to be deleted. +Note: The `delete` attribute is only applicable to the [delete-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/delete-object.html) operation. +The `delete` attribute corresponds to the `--delete` parameter of the +[delete-objects operation within the S3 API](https://docs.aws.amazon.com/cli/latest/reference/s3api/delete-objects.html). +""" + +AWS_S3_KEY: Final = "aws.s3.key" +""" +The S3 object key the request refers to. Corresponds to the `--key` parameter of the [S3 API](https://docs.aws.amazon.com/cli/latest/reference/s3api/index.html) operations. +Note: The `key` attribute is applicable to all object-related S3 operations, i.e. that require the object key as a mandatory parameter. +This applies in particular to the following operations: + +- [copy-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/copy-object.html) +- [delete-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/delete-object.html) +- [get-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/get-object.html) +- [head-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/head-object.html) +- [put-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/put-object.html) +- [restore-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/restore-object.html) +- [select-object-content](https://docs.aws.amazon.com/cli/latest/reference/s3api/select-object-content.html) +- [abort-multipart-upload](https://docs.aws.amazon.com/cli/latest/reference/s3api/abort-multipart-upload.html) +- [complete-multipart-upload](https://docs.aws.amazon.com/cli/latest/reference/s3api/complete-multipart-upload.html) +- [create-multipart-upload](https://docs.aws.amazon.com/cli/latest/reference/s3api/create-multipart-upload.html) +- [list-parts](https://docs.aws.amazon.com/cli/latest/reference/s3api/list-parts.html) +- [upload-part](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part.html) +- [upload-part-copy](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part-copy.html). +""" + +AWS_S3_PART_NUMBER: Final = "aws.s3.part_number" +""" +The part number of the part being uploaded in a multipart-upload operation. This is a positive integer between 1 and 10,000. +Note: The `part_number` attribute is only applicable to the [upload-part](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part.html) +and [upload-part-copy](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part-copy.html) operations. +The `part_number` attribute corresponds to the `--part-number` parameter of the +[upload-part operation within the S3 API](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part.html). +""" + +AWS_S3_UPLOAD_ID: Final = "aws.s3.upload_id" +""" +Upload ID that identifies the multipart upload. +Note: The `upload_id` attribute applies to S3 multipart-upload operations and corresponds to the `--upload-id` parameter +of the [S3 API](https://docs.aws.amazon.com/cli/latest/reference/s3api/index.html) multipart operations. +This applies in particular to the following operations: + +- [abort-multipart-upload](https://docs.aws.amazon.com/cli/latest/reference/s3api/abort-multipart-upload.html) +- [complete-multipart-upload](https://docs.aws.amazon.com/cli/latest/reference/s3api/complete-multipart-upload.html) +- [list-parts](https://docs.aws.amazon.com/cli/latest/reference/s3api/list-parts.html) +- [upload-part](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part.html) +- [upload-part-copy](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part-copy.html). +""" + +AWS_SECRETSMANAGER_SECRET_ARN: Final = "aws.secretsmanager.secret.arn" +""" +The ARN of the Secret stored in the Secrets Manager. +""" + +AWS_SNS_TOPIC_ARN: Final = "aws.sns.topic.arn" +""" +The ARN of the AWS SNS Topic. An Amazon SNS [topic](https://docs.aws.amazon.com/sns/latest/dg/sns-create-topic.html) is a logical access point that acts as a communication channel. +""" + +AWS_SQS_QUEUE_URL: Final = "aws.sqs.queue.url" +""" +The URL of the AWS SQS Queue. It's a unique identifier for a queue in Amazon Simple Queue Service (SQS) and is used to access the queue and perform actions on it. +""" + +AWS_STEP_FUNCTIONS_ACTIVITY_ARN: Final = "aws.step_functions.activity.arn" +""" +The ARN of the AWS Step Functions Activity. +""" + +AWS_STEP_FUNCTIONS_STATE_MACHINE_ARN: Final = "aws.step_functions.state_machine.arn" +""" +The ARN of the AWS Step Functions State Machine. +""" + + +class AwsEcsLaunchtypeValues(Enum): + EC2 = "ec2" + """Amazon EC2.""" + FARGATE = "fargate" + """Amazon Fargate.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/az_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/az_attributes.py new file mode 100644 index 0000000..7595d9a --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/az_attributes.py @@ -0,0 +1,14 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +AZ_NAMESPACE: Final = "az.namespace" +""" +Deprecated: Replaced by `azure.resource_provider.namespace`. +""" + +AZ_SERVICE_REQUEST_ID: Final = "az.service_request_id" +""" +Deprecated: Replaced by `azure.service.request.id`. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/azure_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/azure_attributes.py new file mode 100644 index 0000000..fb1a7f1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/azure_attributes.py @@ -0,0 +1,76 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +AZURE_CLIENT_ID: Final = "azure.client.id" +""" +The unique identifier of the client instance. +""" + +AZURE_COSMOSDB_CONNECTION_MODE: Final = "azure.cosmosdb.connection.mode" +""" +Cosmos client connection mode. +""" + +AZURE_COSMOSDB_CONSISTENCY_LEVEL: Final = "azure.cosmosdb.consistency.level" +""" +Account or request [consistency level](https://learn.microsoft.com/azure/cosmos-db/consistency-levels). +""" + +AZURE_COSMOSDB_OPERATION_CONTACTED_REGIONS: Final = "azure.cosmosdb.operation.contacted_regions" +""" +List of regions contacted during operation in the order that they were contacted. If there is more than one region listed, it indicates that the operation was performed on multiple regions i.e. cross-regional call. +Note: Region name matches the format of `displayName` in [Azure Location API](https://learn.microsoft.com/rest/api/resources/subscriptions/list-locations). +""" + +AZURE_COSMOSDB_OPERATION_REQUEST_CHARGE: Final = "azure.cosmosdb.operation.request_charge" +""" +The number of request units consumed by the operation. +""" + +AZURE_COSMOSDB_REQUEST_BODY_SIZE: Final = "azure.cosmosdb.request.body.size" +""" +Request payload size in bytes. +""" + +AZURE_COSMOSDB_RESPONSE_SUB_STATUS_CODE: Final = "azure.cosmosdb.response.sub_status_code" +""" +Cosmos DB sub status code. +""" + +AZURE_RESOURCE_GROUP_NAME: Final = "azure.resource_group.name" +""" +The name of the Azure [resource group](https://learn.microsoft.com/azure/azure-resource-manager/management/manage-resource-groups-portal) the resource belongs to. +""" + +AZURE_RESOURCE_PROVIDER_NAMESPACE: Final = "azure.resource_provider.namespace" +""" +[Azure Resource Provider Namespace](https://learn.microsoft.com/azure/azure-resource-manager/management/azure-services-resource-providers) as recognized by the client. +""" + +AZURE_SERVICE_REQUEST_ID: Final = "azure.service.request.id" +""" +The unique identifier of the service request. It's generated by the Azure service and returned with the response. +""" + + +class AzureCosmosdbConnectionModeValues(Enum): + GATEWAY = "gateway" + """Gateway (HTTP) connection.""" + DIRECT = "direct" + """Direct connection.""" + + +class AzureCosmosdbConsistencyLevelValues(Enum): + STRONG = "Strong" + """Strong.""" + BOUNDED_STALENESS = "BoundedStaleness" + """Bounded Staleness.""" + SESSION = "Session" + """Session.""" + EVENTUAL = "Eventual" + """Eventual.""" + CONSISTENT_PREFIX = "ConsistentPrefix" + """Consistent Prefix.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/browser_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/browser_attributes.py new file mode 100644 index 0000000..ff96d0c --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/browser_attributes.py @@ -0,0 +1,104 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +BROWSER_BRANDS: Final = "browser.brands" +""" +Array of brand name and version separated by a space. +Note: This value is intended to be taken from the [UA client hints API](https://wicg.github.io/ua-client-hints/#interface) (`navigator.userAgentData.brands`). +""" + +BROWSER_DOCUMENT_URL_FULL: Final = "browser.document.url.full" +""" +Absolute URL of the current browser document according to [RFC3986](https://www.rfc-editor.org/rfc/rfc3986). +""" + +BROWSER_LANGUAGE: Final = "browser.language" +""" +Preferred language of the user using the browser. +Note: This value is intended to be taken from the Navigator API `navigator.language`. +""" + +BROWSER_MOBILE: Final = "browser.mobile" +""" +A boolean that is true if the browser is running on a mobile device. +Note: This value is intended to be taken from the [UA client hints API](https://wicg.github.io/ua-client-hints/#interface) (`navigator.userAgentData.mobile`). If unavailable, this attribute SHOULD be left unset. +""" + +BROWSER_PLATFORM: Final = "browser.platform" +""" +The platform on which the browser is running. +Note: This value is intended to be taken from the [UA client hints API](https://wicg.github.io/ua-client-hints/#interface) (`navigator.userAgentData.platform`). If unavailable, the legacy `navigator.platform` API SHOULD NOT be used instead and this attribute SHOULD be left unset in order for the values to be consistent. +The list of possible values is defined in the [W3C User-Agent Client Hints specification](https://wicg.github.io/ua-client-hints/#sec-ch-ua-platform). Note that some (but not all) of these values can overlap with values in the [`os.type` and `os.name` attributes](./os.md). However, for consistency, the values in the `browser.platform` attribute should capture the exact value that the user agent provides. +""" + +BROWSER_WEB_VITAL_DELTA: Final = "browser.web_vital.delta" +""" +The delta between the current value and the last-reported value. See [delta](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#report-only-the-delta-of-changes). +""" + +BROWSER_WEB_VITAL_ID: Final = "browser.web_vital.id" +""" +A unique ID representing this particular metric instance. +""" + +BROWSER_WEB_VITAL_NAME: Final = "browser.web_vital.name" +""" +Name of the web vital. +""" + +BROWSER_WEB_VITAL_NAVIGATION_TYPE: Final = "browser.web_vital.navigation_type" +""" +The type of navigation, as reported by the [Navigation Timing API](https://developer.mozilla.org/docs/Web/API/PerformanceNavigationTiming/type), with additional values reported by the web-vitals library. +""" + +BROWSER_WEB_VITAL_RATING: Final = "browser.web_vital.rating" +""" +The rating of the web vital value against the "good", "needs improvement", and "poor" thresholds defined for the metric. +""" + +BROWSER_WEB_VITAL_VALUE: Final = "browser.web_vital.value" +""" +Value of the web vital. +""" + + +class BrowserWebVitalNameValues(Enum): + CLS = "cls" + """Cumulative Layout Shift. See [cls](https://web.dev/articles/cls).""" + LCP = "lcp" + """Largest Contentful Paint. See [lcp](https://web.dev/articles/lcp).""" + FCP = "fcp" + """First Contentful Paint. See [fcp](https://web.dev/articles/fcp).""" + INP = "inp" + """Interaction to Next Paint. See [inp](https://web.dev/articles/inp).""" + TTFB = "ttfb" + """Time to First Byte. See [ttfb](https://web.dev/articles/ttfb).""" + FID = "fid" + """Deprecated: Replaced by Interaction to Next Paint (`inp`), which became a Core Web Vital in March 2024. See [inp](https://web.dev/articles/inp).""" + + +class BrowserWebVitalNavigationTypeValues(Enum): + NAVIGATE = "navigate" + """Navigation started by clicking a link, entering a URL, form submission, or a script operation.""" + RELOAD = "reload" + """Navigation through a reload operation or a `Location.reload()` call.""" + BACK_FORWARD = "back-forward" + """Navigation through the browser's history traversal (e.g. back/forward buttons).""" + BACK_FORWARD_CACHE = "back-forward-cache" + """Navigation restoring a page from the back/forward cache (bfcache).""" + PRERENDER = "prerender" + """Navigation to a page that was prerendered.""" + RESTORE = "restore" + """Navigation restoring a page that was previously discarded by the browser.""" + + +class BrowserWebVitalRatingValues(Enum): + GOOD = "good" + """The metric value is within the "good" threshold.""" + NEEDS_IMPROVEMENT = "needs-improvement" + """The metric value is within the "needs improvement" threshold.""" + POOR = "poor" + """The metric value is within the "poor" threshold.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cassandra_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cassandra_attributes.py new file mode 100644 index 0000000..976a5f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cassandra_attributes.py @@ -0,0 +1,60 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +CASSANDRA_CONSISTENCY_LEVEL: Final = "cassandra.consistency.level" +""" +The consistency level of the query. Based on consistency values from [CQL](https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/dml/dmlConfigConsistency.html). +""" + +CASSANDRA_COORDINATOR_DC: Final = "cassandra.coordinator.dc" +""" +The data center of the coordinating node for a query. +""" + +CASSANDRA_COORDINATOR_ID: Final = "cassandra.coordinator.id" +""" +The ID of the coordinating node for a query. +""" + +CASSANDRA_PAGE_SIZE: Final = "cassandra.page.size" +""" +The fetch size used for paging, i.e. how many rows will be returned at once. +""" + +CASSANDRA_QUERY_IDEMPOTENT: Final = "cassandra.query.idempotent" +""" +Whether or not the query is idempotent. +""" + +CASSANDRA_SPECULATIVE_EXECUTION_COUNT: Final = "cassandra.speculative_execution.count" +""" +The number of times a query was speculatively executed. Not set or `0` if the query was not executed speculatively. +""" + + +class CassandraConsistencyLevelValues(Enum): + ALL = "all" + """All.""" + EACH_QUORUM = "each_quorum" + """Each Quorum.""" + QUORUM = "quorum" + """Quorum.""" + LOCAL_QUORUM = "local_quorum" + """Local Quorum.""" + ONE = "one" + """One.""" + TWO = "two" + """Two.""" + THREE = "three" + """Three.""" + LOCAL_ONE = "local_one" + """Local One.""" + ANY = "any" + """Any.""" + SERIAL = "serial" + """Serial.""" + LOCAL_SERIAL = "local_serial" + """Local Serial.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cicd_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cicd_attributes.py new file mode 100644 index 0000000..a686908 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cicd_attributes.py @@ -0,0 +1,152 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +CICD_PIPELINE_ACTION_NAME: Final = "cicd.pipeline.action.name" +""" +The kind of action a pipeline run is performing. +""" + +CICD_PIPELINE_NAME: Final = "cicd.pipeline.name" +""" +The human readable name of the pipeline within a CI/CD system. +""" + +CICD_PIPELINE_RESULT: Final = "cicd.pipeline.result" +""" +The result of a pipeline run. +""" + +CICD_PIPELINE_RUN_ID: Final = "cicd.pipeline.run.id" +""" +The unique identifier of a pipeline run within a CI/CD system. +""" + +CICD_PIPELINE_RUN_STATE: Final = "cicd.pipeline.run.state" +""" +The pipeline run goes through these states during its lifecycle. +""" + +CICD_PIPELINE_RUN_URL_FULL: Final = "cicd.pipeline.run.url.full" +""" +The [URL](https://wikipedia.org/wiki/URL) of the pipeline run, providing the complete address in order to locate and identify the pipeline run. +""" + +CICD_PIPELINE_TASK_NAME: Final = "cicd.pipeline.task.name" +""" +The human readable name of a task within a pipeline. Task here most closely aligns with a [computing process](https://wikipedia.org/wiki/Pipeline_(computing)) in a pipeline. Other terms for tasks include commands, steps, and procedures. +""" + +CICD_PIPELINE_TASK_RUN_ID: Final = "cicd.pipeline.task.run.id" +""" +The unique identifier of a task run within a pipeline. +Note: For a given pipeline run and task, the `cicd.pipeline.task.run.id` MUST be unique within that run. For the same task across different runs of the same pipeline, the `cicd.pipeline.task.run.id` MAY remain the same, enabling correlation of `cicd.pipeline.task.run.result` values across multiple pipeline runs. +""" + +CICD_PIPELINE_TASK_RUN_RESULT: Final = "cicd.pipeline.task.run.result" +""" +The result of a task run. +""" + +CICD_PIPELINE_TASK_RUN_URL_FULL: Final = "cicd.pipeline.task.run.url.full" +""" +The [URL](https://wikipedia.org/wiki/URL) of the pipeline task run, providing the complete address in order to locate and identify the pipeline task run. +""" + +CICD_PIPELINE_TASK_TYPE: Final = "cicd.pipeline.task.type" +""" +The type of the task within a pipeline. +""" + +CICD_SYSTEM_COMPONENT: Final = "cicd.system.component" +""" +The name of a component of the CI/CD system. +""" + +CICD_WORKER_ID: Final = "cicd.worker.id" +""" +The unique identifier of a worker within a CI/CD system. +""" + +CICD_WORKER_NAME: Final = "cicd.worker.name" +""" +The name of a worker within a CI/CD system. +""" + +CICD_WORKER_STATE: Final = "cicd.worker.state" +""" +The state of a CI/CD worker / agent. +""" + +CICD_WORKER_URL_FULL: Final = "cicd.worker.url.full" +""" +The [URL](https://wikipedia.org/wiki/URL) of the worker, providing the complete address in order to locate and identify the worker. +""" + + +class CicdPipelineActionNameValues(Enum): + BUILD = "BUILD" + """The pipeline run is executing a build.""" + RUN = "RUN" + """The pipeline run is executing.""" + SYNC = "SYNC" + """The pipeline run is executing a sync.""" + + +class CicdPipelineResultValues(Enum): + SUCCESS = "success" + """The pipeline run finished successfully.""" + FAILURE = "failure" + """The pipeline run did not finish successfully, eg. due to a compile error or a failing test. Such failures are usually detected by non-zero exit codes of the tools executed in the pipeline run.""" + ERROR = "error" + """The pipeline run failed due to an error in the CI/CD system, eg. due to the worker being killed.""" + TIMEOUT = "timeout" + """A timeout caused the pipeline run to be interrupted.""" + CANCELLATION = "cancellation" + """The pipeline run was cancelled, eg. by a user manually cancelling the pipeline run.""" + SKIP = "skip" + """The pipeline run was skipped, eg. due to a precondition not being met.""" + + +class CicdPipelineRunStateValues(Enum): + PENDING = "pending" + """The run pending state spans from the event triggering the pipeline run until the execution of the run starts (eg. time spent in a queue, provisioning agents, creating run resources).""" + EXECUTING = "executing" + """The executing state spans the execution of any run tasks (eg. build, test).""" + FINALIZING = "finalizing" + """The finalizing state spans from when the run has finished executing (eg. cleanup of run resources).""" + + +class CicdPipelineTaskRunResultValues(Enum): + SUCCESS = "success" + """The task run finished successfully.""" + FAILURE = "failure" + """The task run did not finish successfully, eg. due to a compile error or a failing test. Such failures are usually detected by non-zero exit codes of the tools executed in the task run.""" + ERROR = "error" + """The task run failed due to an error in the CI/CD system, eg. due to the worker being killed.""" + TIMEOUT = "timeout" + """A timeout caused the task run to be interrupted.""" + CANCELLATION = "cancellation" + """The task run was cancelled, eg. by a user manually cancelling the task run.""" + SKIP = "skip" + """The task run was skipped, eg. due to a precondition not being met.""" + + +class CicdPipelineTaskTypeValues(Enum): + BUILD = "build" + """build.""" + TEST = "test" + """test.""" + DEPLOY = "deploy" + """deploy.""" + + +class CicdWorkerStateValues(Enum): + AVAILABLE = "available" + """The worker is not performing work for the CI/CD system. It is available to the CI/CD system to perform work on (online / idle).""" + BUSY = "busy" + """The worker is performing work for the CI/CD system.""" + OFFLINE = "offline" + """The worker is not available to the CI/CD system (disconnected / down).""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/client_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/client_attributes.py new file mode 100644 index 0000000..b68dea7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/client_attributes.py @@ -0,0 +1,14 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +CLIENT_ADDRESS: Final = "client.address" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.client_attributes.CLIENT_ADDRESS`. +""" + +CLIENT_PORT: Final = "client.port" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.client_attributes.CLIENT_PORT`. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cloud_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cloud_attributes.py new file mode 100644 index 0000000..87570ea --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cloud_attributes.py @@ -0,0 +1,156 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +CLOUD_ACCOUNT_ID: Final = "cloud.account.id" +""" +The cloud account ID the resource is assigned to. +Note: For Azure, this is the subscription ID. +""" + +CLOUD_AVAILABILITY_ZONE: Final = "cloud.availability_zone" +""" +Cloud regions often have multiple, isolated locations known as zones to increase availability. Availability zone represents the zone where the resource is running. +Note: Availability zones are called "zones" on Alibaba Cloud and Google Cloud. +""" + +CLOUD_PLATFORM: Final = "cloud.platform" +""" +The cloud platform in use. +Note: The prefix of the service SHOULD match the one specified in `cloud.provider`. +""" + +CLOUD_PROVIDER: Final = "cloud.provider" +""" +Name of the cloud provider. +""" + +CLOUD_REGION: Final = "cloud.region" +""" +The geographical region within a cloud provider. When associated with a resource, this attribute specifies the region where the resource operates. When calling services or APIs deployed on a cloud, this attribute identifies the region where the called destination is deployed. +Note: Refer to your provider's docs to see the available regions, for example [Alibaba Cloud regions](https://www.alibabacloud.com/help/doc-detail/40654.htm), [AWS regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/), [Azure regions](https://azure.microsoft.com/global-infrastructure/geographies/), [Google Cloud regions](https://cloud.google.com/about/locations), or [Tencent Cloud regions](https://www.tencentcloud.com/document/product/213/6091). +""" + +CLOUD_RESOURCE_ID: Final = "cloud.resource_id" +""" +Cloud provider-specific native identifier of the monitored cloud resource (e.g. an [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) on AWS, a [fully qualified resource ID](https://learn.microsoft.com/rest/api/resources/resources/get-by-id) on Azure, a [full resource name](https://google.aip.dev/122#full-resource-names) on GCP). +Note: On some cloud providers, it may not be possible to determine the full ID at startup, +so it may be necessary to set `cloud.resource_id` as a span attribute instead. + +The exact value to use for `cloud.resource_id` depends on the cloud provider. +The following well-known definitions MUST be used if you set this attribute and they apply: + +- **AWS Lambda:** The function [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html). + Take care not to use the "invoked ARN" directly but replace any + [alias suffix](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) + with the resolved function version, as the same runtime instance may be invocable with + multiple different aliases. +- **GCP:** The [URI of the resource](https://cloud.google.com/iam/docs/full-resource-names) +- **Azure:** The [Fully Qualified Resource ID](https://learn.microsoft.com/rest/api/resources/resources/get-by-id) of the invoked function, + *not* the function app, having the form + `/subscriptions//resourceGroups//providers/Microsoft.Web/sites//functions/`. + This means that a span attribute MUST be used, as an Azure function app can host multiple functions that would usually share + a TracerProvider. +""" + + +class CloudPlatformValues(Enum): + AKAMAI_CLOUD_COMPUTE = "akamai_cloud.compute" + """Akamai Cloud Compute.""" + ALIBABA_CLOUD_ECS = "alibaba_cloud_ecs" + """Alibaba Cloud Elastic Compute Service.""" + ALIBABA_CLOUD_FC = "alibaba_cloud_fc" + """Alibaba Cloud Function Compute.""" + ALIBABA_CLOUD_OPENSHIFT = "alibaba_cloud_openshift" + """Red Hat OpenShift on Alibaba Cloud.""" + AWS_EC2 = "aws_ec2" + """AWS Elastic Compute Cloud.""" + AWS_ECS = "aws_ecs" + """AWS Elastic Container Service.""" + AWS_EKS = "aws_eks" + """AWS Elastic Kubernetes Service.""" + AWS_LAMBDA = "aws_lambda" + """AWS Lambda.""" + AWS_ELASTIC_BEANSTALK = "aws_elastic_beanstalk" + """AWS Elastic Beanstalk.""" + AWS_APP_RUNNER = "aws_app_runner" + """AWS App Runner.""" + AWS_OPENSHIFT = "aws_openshift" + """Red Hat OpenShift on AWS (ROSA).""" + AZURE_VM = "azure.vm" + """Azure Virtual Machines.""" + AZURE_CONTAINER_APPS = "azure.container_apps" + """Azure Container Apps.""" + AZURE_CONTAINER_INSTANCES = "azure.container_instances" + """Azure Container Instances.""" + AZURE_AKS = "azure.aks" + """Azure Kubernetes Service.""" + AZURE_FUNCTIONS = "azure.functions" + """Azure Functions.""" + AZURE_APP_SERVICE = "azure.app_service" + """Azure App Service.""" + AZURE_OPENSHIFT = "azure.openshift" + """Azure Red Hat OpenShift.""" + GCP_AGENT_ENGINE = "gcp.agent_engine" + """Google Vertex AI Agent Engine.""" + GCP_BARE_METAL_SOLUTION = "gcp_bare_metal_solution" + """Google Bare Metal Solution (BMS).""" + GCP_COMPUTE_ENGINE = "gcp_compute_engine" + """Google Cloud Compute Engine (GCE).""" + GCP_CLOUD_RUN = "gcp_cloud_run" + """Google Cloud Run.""" + GCP_KUBERNETES_ENGINE = "gcp_kubernetes_engine" + """Google Cloud Kubernetes Engine (GKE).""" + GCP_CLOUD_FUNCTIONS = "gcp_cloud_functions" + """Google Cloud Functions (GCF).""" + GCP_APP_ENGINE = "gcp_app_engine" + """Google Cloud App Engine (GAE).""" + GCP_OPENSHIFT = "gcp_openshift" + """Red Hat OpenShift on Google Cloud.""" + HETZNER_CLOUD_SERVER = "hetzner.cloud_server" + """Server on Hetzner Cloud.""" + IBM_CLOUD_OPENSHIFT = "ibm_cloud_openshift" + """Red Hat OpenShift on IBM Cloud.""" + ORACLE_CLOUD_COMPUTE = "oracle_cloud_compute" + """Compute on Oracle Cloud Infrastructure (OCI).""" + ORACLE_CLOUD_OKE = "oracle_cloud_oke" + """Kubernetes Engine (OKE) on Oracle Cloud Infrastructure (OCI).""" + SCALEWAY_CLOUD_COMPUTE = "scaleway_cloud_compute" + """Compute on Scaleway Cloud.""" + TENCENT_CLOUD_CVM = "tencent_cloud_cvm" + """Tencent Cloud Cloud Virtual Machine (CVM).""" + TENCENT_CLOUD_EKS = "tencent_cloud_eks" + """Tencent Cloud Elastic Kubernetes Service (EKS).""" + TENCENT_CLOUD_SCF = "tencent_cloud_scf" + """Tencent Cloud Serverless Cloud Function (SCF).""" + VULTR_CLOUD_COMPUTE = "vultr.cloud_compute" + """Vultr Cloud Compute.""" + + +class CloudProviderValues(Enum): + AKAMAI_CLOUD = "akamai_cloud" + """Akamai Cloud.""" + ALIBABA_CLOUD = "alibaba_cloud" + """Alibaba Cloud.""" + AWS = "aws" + """Amazon Web Services.""" + AZURE = "azure" + """Microsoft Azure.""" + GCP = "gcp" + """Google Cloud Platform.""" + HEROKU = "heroku" + """Heroku Platform as a Service.""" + HETZNER = "hetzner" + """Hetzner.""" + IBM_CLOUD = "ibm_cloud" + """IBM Cloud.""" + ORACLE_CLOUD = "oracle_cloud" + """Oracle Cloud Infrastructure (OCI).""" + SCALEWAY_CLOUD = "scaleway_cloud" + """Scaleway Cloud.""" + TENCENT_CLOUD = "tencent_cloud" + """Tencent Cloud.""" + VULTR = "vultr" + """Vultr.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cloudevents_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cloudevents_attributes.py new file mode 100644 index 0000000..fe3226a --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cloudevents_attributes.py @@ -0,0 +1,29 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +CLOUDEVENTS_EVENT_ID: Final = "cloudevents.event_id" +""" +The [event_id](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#id) uniquely identifies the event. +""" + +CLOUDEVENTS_EVENT_SOURCE: Final = "cloudevents.event_source" +""" +The [source](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#source-1) identifies the context in which an event happened. +""" + +CLOUDEVENTS_EVENT_SPEC_VERSION: Final = "cloudevents.event_spec_version" +""" +The [version of the CloudEvents specification](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#specversion) which the event uses. +""" + +CLOUDEVENTS_EVENT_SUBJECT: Final = "cloudevents.event_subject" +""" +The [subject](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#subject) of the event in the context of the event producer (identified by source). +""" + +CLOUDEVENTS_EVENT_TYPE: Final = "cloudevents.event_type" +""" +The [event_type](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#type) contains a value describing the type of event related to the originating occurrence. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cloudfoundry_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cloudfoundry_attributes.py new file mode 100644 index 0000000..9ed23c0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cloudfoundry_attributes.py @@ -0,0 +1,107 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +CLOUDFOUNDRY_APP_ID: Final = "cloudfoundry.app.id" +""" +The guid of the application. +Note: Application instrumentation should use the value from environment +variable `VCAP_APPLICATION.application_id`. This is the same value as +reported by `cf app --guid`. +""" + +CLOUDFOUNDRY_APP_INSTANCE_ID: Final = "cloudfoundry.app.instance.id" +""" +The index of the application instance. 0 when just one instance is active. +Note: CloudFoundry defines the `instance_id` in the [Loggregator v2 envelope](https://github.com/cloudfoundry/loggregator-api#v2-envelope). +It is used for logs and metrics emitted by CloudFoundry. It is +supposed to contain the application instance index for applications +deployed on the runtime. + +Application instrumentation should use the value from environment +variable `CF_INSTANCE_INDEX`. +""" + +CLOUDFOUNDRY_APP_NAME: Final = "cloudfoundry.app.name" +""" +The name of the application. +Note: Application instrumentation should use the value from environment +variable `VCAP_APPLICATION.application_name`. This is the same value +as reported by `cf apps`. +""" + +CLOUDFOUNDRY_ORG_ID: Final = "cloudfoundry.org.id" +""" +The guid of the CloudFoundry org the application is running in. +Note: Application instrumentation should use the value from environment +variable `VCAP_APPLICATION.org_id`. This is the same value as +reported by `cf org --guid`. +""" + +CLOUDFOUNDRY_ORG_NAME: Final = "cloudfoundry.org.name" +""" +The name of the CloudFoundry organization the app is running in. +Note: Application instrumentation should use the value from environment +variable `VCAP_APPLICATION.org_name`. This is the same value as +reported by `cf orgs`. +""" + +CLOUDFOUNDRY_PROCESS_ID: Final = "cloudfoundry.process.id" +""" +The UID identifying the process. +Note: Application instrumentation should use the value from environment +variable `VCAP_APPLICATION.process_id`. It is supposed to be equal to +`VCAP_APPLICATION.app_id` for applications deployed to the runtime. +For system components, this could be the actual PID. +""" + +CLOUDFOUNDRY_PROCESS_TYPE: Final = "cloudfoundry.process.type" +""" +The type of process. +Note: CloudFoundry applications can consist of multiple jobs. Usually the +main process will be of type `web`. There can be additional background +tasks or side-cars with different process types. +""" + +CLOUDFOUNDRY_SPACE_ID: Final = "cloudfoundry.space.id" +""" +The guid of the CloudFoundry space the application is running in. +Note: Application instrumentation should use the value from environment +variable `VCAP_APPLICATION.space_id`. This is the same value as +reported by `cf space --guid`. +""" + +CLOUDFOUNDRY_SPACE_NAME: Final = "cloudfoundry.space.name" +""" +The name of the CloudFoundry space the application is running in. +Note: Application instrumentation should use the value from environment +variable `VCAP_APPLICATION.space_name`. This is the same value as +reported by `cf spaces`. +""" + +CLOUDFOUNDRY_SYSTEM_ID: Final = "cloudfoundry.system.id" +""" +A guid or another name describing the event source. +Note: CloudFoundry defines the `source_id` in the [Loggregator v2 envelope](https://github.com/cloudfoundry/loggregator-api#v2-envelope). +It is used for logs and metrics emitted by CloudFoundry. It is +supposed to contain the component name, e.g. "gorouter", for +CloudFoundry components. + +When system components are instrumented, values from the +[Bosh spec](https://bosh.io/docs/jobs/#properties-spec) +should be used. The `system.id` should be set to +`spec.deployment/spec.name`. +""" + +CLOUDFOUNDRY_SYSTEM_INSTANCE_ID: Final = "cloudfoundry.system.instance.id" +""" +A guid describing the concrete instance of the event source. +Note: CloudFoundry defines the `instance_id` in the [Loggregator v2 envelope](https://github.com/cloudfoundry/loggregator-api#v2-envelope). +It is used for logs and metrics emitted by CloudFoundry. It is +supposed to contain the vm ID for CloudFoundry components. + +When system components are instrumented, values from the +[Bosh spec](https://bosh.io/docs/jobs/#properties-spec) +should be used. The `system.instance.id` should be set to `spec.id`. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/code_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/code_attributes.py new file mode 100644 index 0000000..130aebc --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/code_attributes.py @@ -0,0 +1,54 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +CODE_COLUMN: Final = "code.column" +""" +Deprecated: Replaced by `code.column.number`. +""" + +CODE_COLUMN_NUMBER: Final = "code.column.number" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.code_attributes.CODE_COLUMN_NUMBER`. +""" + +CODE_FILE_PATH: Final = "code.file.path" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.code_attributes.CODE_FILE_PATH`. +""" + +CODE_FILEPATH: Final = "code.filepath" +""" +Deprecated: Replaced by `code.file.path`. +""" + +CODE_FUNCTION: Final = "code.function" +""" +Deprecated: Value should be included in `code.function.name` which is expected to be a fully-qualified name. +""" + +CODE_FUNCTION_NAME: Final = "code.function.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.code_attributes.CODE_FUNCTION_NAME`. +""" + +CODE_LINE_NUMBER: Final = "code.line.number" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.code_attributes.CODE_LINE_NUMBER`. +""" + +CODE_LINENO: Final = "code.lineno" +""" +Deprecated: Replaced by `code.line.number`. +""" + +CODE_NAMESPACE: Final = "code.namespace" +""" +Deprecated: Value should be included in `code.function.name` which is expected to be a fully-qualified name. +""" + +CODE_STACKTRACE: Final = "code.stacktrace" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.code_attributes.CODE_STACKTRACE`. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/container_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/container_attributes.py new file mode 100644 index 0000000..fc1f49a --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/container_attributes.py @@ -0,0 +1,114 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +CONTAINER_COMMAND: Final = "container.command" +""" +The command used to run the container (i.e. the command name). +Note: If using embedded credentials or sensitive data, it is recommended to remove them to prevent potential leakage. +""" + +CONTAINER_COMMAND_ARGS: Final = "container.command_args" +""" +All the command arguments (including the command/executable itself) run by the container. +""" + +CONTAINER_COMMAND_LINE: Final = "container.command_line" +""" +The full command run by the container as a single string representing the full command. +""" + +CONTAINER_CPU_STATE: Final = "container.cpu.state" +""" +Deprecated: Replaced by `cpu.mode`. +""" + +CONTAINER_CSI_PLUGIN_NAME: Final = "container.csi.plugin.name" +""" +The name of the CSI ([Container Storage Interface](https://github.com/container-storage-interface/spec)) plugin used by the volume. +Note: This can sometimes be referred to as a "driver" in CSI implementations. This should represent the `name` field of the GetPluginInfo RPC. +""" + +CONTAINER_CSI_VOLUME_ID: Final = "container.csi.volume.id" +""" +The unique volume ID returned by the CSI ([Container Storage Interface](https://github.com/container-storage-interface/spec)) plugin. +Note: This can sometimes be referred to as a "volume handle" in CSI implementations. This should represent the `Volume.volume_id` field in CSI spec. +""" + +CONTAINER_ID: Final = "container.id" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.container_attributes.CONTAINER_ID`. +""" + +CONTAINER_IMAGE_ID: Final = "container.image.id" +""" +Runtime specific image identifier. Usually a hash algorithm followed by a UUID. +Note: Docker defines a sha256 of the image ID; `container.image.id` corresponds to the `Image` field from the Docker container inspect [API](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Container/operation/ContainerInspect) endpoint. +K8s defines a link to the container registry repository with digest `"imageID": "registry.azurecr.io /namespace/service/dockerfile@sha256:bdeabd40c3a8a492eaf9e8e44d0ebbb84bac7ee25ac0cf8a7159d25f62555625"`. +The ID is assigned by the container runtime and can vary in different environments. Consider using `oci.manifest.digest` if it is important to identify the same image in different environments/runtimes. +""" + +CONTAINER_IMAGE_NAME: Final = "container.image.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.container_attributes.CONTAINER_IMAGE_NAME`. +""" + +CONTAINER_IMAGE_REPO_DIGESTS: Final = "container.image.repo_digests" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.container_attributes.CONTAINER_IMAGE_REPO_DIGESTS`. +""" + +CONTAINER_IMAGE_TAGS: Final = "container.image.tags" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.container_attributes.CONTAINER_IMAGE_TAGS`. +""" + +CONTAINER_LABEL_TEMPLATE: Final = "container.label" +""" +Container labels, `` being the label name, the value being the label value. +Note: For example, a Docker container label `app` with value `nginx` SHOULD be recorded as the `container.label.app` attribute with value `"nginx"`. +""" + +CONTAINER_LABELS_TEMPLATE: Final = "container.labels" +""" +Deprecated: Replaced by `container.label`. +""" + +CONTAINER_NAME: Final = "container.name" +""" +Container name used by container runtime. +""" + +CONTAINER_RUNTIME: Final = "container.runtime" +""" +Deprecated: Replaced by `container.runtime.name`. +""" + +CONTAINER_RUNTIME_DESCRIPTION: Final = "container.runtime.description" +""" +A description about the runtime which could include, for example details about the CRI/API version being used or other customizations. +""" + +CONTAINER_RUNTIME_NAME: Final = "container.runtime.name" +""" +The container runtime managing this container. +""" + +CONTAINER_RUNTIME_VERSION: Final = "container.runtime.version" +""" +The version of the runtime of this process, as returned by the runtime without modification. +""" + + +@deprecated("The attribute container.cpu.state is deprecated - Replaced by `cpu.mode`") +class ContainerCpuStateValues(Enum): + USER = "user" + """When tasks of the cgroup are in user mode (Linux). When all container processes are in user mode (Windows).""" + SYSTEM = "system" + """When CPU is used by the system (host OS).""" + KERNEL = "kernel" + """When tasks of the cgroup are in kernel mode (Linux). When all container processes are in kernel mode (Windows).""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cpu_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cpu_attributes.py new file mode 100644 index 0000000..a2982d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cpu_attributes.py @@ -0,0 +1,34 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +CPU_LOGICAL_NUMBER: Final = "cpu.logical_number" +""" +The logical CPU number [0..n-1]. +""" + +CPU_MODE: Final = "cpu.mode" +""" +The mode of the CPU. +""" + + +class CpuModeValues(Enum): + USER = "user" + """User.""" + SYSTEM = "system" + """System.""" + NICE = "nice" + """Nice.""" + IDLE = "idle" + """Idle.""" + IOWAIT = "iowait" + """IO Wait.""" + INTERRUPT = "interrupt" + """Interrupt.""" + STEAL = "steal" + """Steal.""" + KERNEL = "kernel" + """Deprecated: Replaced by `system`.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cpython_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cpython_attributes.py new file mode 100644 index 0000000..90ef205 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/cpython_attributes.py @@ -0,0 +1,19 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +CPYTHON_GC_GENERATION: Final = "cpython.gc.generation" +""" +Value of the garbage collector collection generation. +""" + + +class CPythonGCGenerationValues(Enum): + GENERATION_0 = 0 + """Generation 0.""" + GENERATION_1 = 1 + """Generation 1.""" + GENERATION_2 = 2 + """Generation 2.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/db_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/db_attributes.py new file mode 100644 index 0000000..e71fcd4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/db_attributes.py @@ -0,0 +1,574 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +DB_CASSANDRA_CONSISTENCY_LEVEL: Final = "db.cassandra.consistency_level" +""" +Deprecated: Replaced by `cassandra.consistency.level`. +""" + +DB_CASSANDRA_COORDINATOR_DC: Final = "db.cassandra.coordinator.dc" +""" +Deprecated: Replaced by `cassandra.coordinator.dc`. +""" + +DB_CASSANDRA_COORDINATOR_ID: Final = "db.cassandra.coordinator.id" +""" +Deprecated: Replaced by `cassandra.coordinator.id`. +""" + +DB_CASSANDRA_IDEMPOTENCE: Final = "db.cassandra.idempotence" +""" +Deprecated: Replaced by `cassandra.query.idempotent`. +""" + +DB_CASSANDRA_PAGE_SIZE: Final = "db.cassandra.page_size" +""" +Deprecated: Replaced by `cassandra.page.size`. +""" + +DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT: Final = "db.cassandra.speculative_execution_count" +""" +Deprecated: Replaced by `cassandra.speculative_execution.count`. +""" + +DB_CASSANDRA_TABLE: Final = "db.cassandra.table" +""" +Deprecated: Replaced by `db.collection.name`. +""" + +DB_CLIENT_CONNECTION_POOL_NAME: Final = "db.client.connection.pool.name" +""" +The name of the connection pool; unique within the instrumented application. In case the connection pool implementation doesn't provide a name, instrumentation SHOULD use a combination of parameters that would make the name unique, for example, combining attributes `server.address`, `server.port`, and `db.namespace`, formatted as `server.address:server.port/db.namespace`. Instrumentations that generate connection pool name following different patterns SHOULD document it. +""" + +DB_CLIENT_CONNECTION_STATE: Final = "db.client.connection.state" +""" +The state of a connection in the pool. +""" + +DB_CLIENT_CONNECTIONS_POOL_NAME: Final = "db.client.connections.pool.name" +""" +Deprecated: Replaced by `db.client.connection.pool.name`. +""" + +DB_CLIENT_CONNECTIONS_STATE: Final = "db.client.connections.state" +""" +Deprecated: Replaced by `db.client.connection.state`. +""" + +DB_COLLECTION_NAME: Final = "db.collection.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DB_COLLECTION_NAME`. +""" + +DB_CONNECTION_STRING: Final = "db.connection_string" +""" +Deprecated: Replaced by `server.address` and `server.port`. +""" + +DB_COSMOSDB_CLIENT_ID: Final = "db.cosmosdb.client_id" +""" +Deprecated: Replaced by `azure.client.id`. +""" + +DB_COSMOSDB_CONNECTION_MODE: Final = "db.cosmosdb.connection_mode" +""" +Deprecated: Replaced by `azure.cosmosdb.connection.mode`. +""" + +DB_COSMOSDB_CONSISTENCY_LEVEL: Final = "db.cosmosdb.consistency_level" +""" +Deprecated: Replaced by `azure.cosmosdb.consistency.level`. +""" + +DB_COSMOSDB_CONTAINER: Final = "db.cosmosdb.container" +""" +Deprecated: Replaced by `db.collection.name`. +""" + +DB_COSMOSDB_OPERATION_TYPE: Final = "db.cosmosdb.operation_type" +""" +Deprecated: Removed, no replacement at this time. +""" + +DB_COSMOSDB_REGIONS_CONTACTED: Final = "db.cosmosdb.regions_contacted" +""" +Deprecated: Replaced by `azure.cosmosdb.operation.contacted_regions`. +""" + +DB_COSMOSDB_REQUEST_CHARGE: Final = "db.cosmosdb.request_charge" +""" +Deprecated: Replaced by `azure.cosmosdb.operation.request_charge`. +""" + +DB_COSMOSDB_REQUEST_CONTENT_LENGTH: Final = "db.cosmosdb.request_content_length" +""" +Deprecated: Replaced by `azure.cosmosdb.request.body.size`. +""" + +DB_COSMOSDB_STATUS_CODE: Final = "db.cosmosdb.status_code" +""" +Deprecated: Use `db.response.status_code` instead. +""" + +DB_COSMOSDB_SUB_STATUS_CODE: Final = "db.cosmosdb.sub_status_code" +""" +Deprecated: Replaced by `azure.cosmosdb.response.sub_status_code`. +""" + +DB_ELASTICSEARCH_CLUSTER_NAME: Final = "db.elasticsearch.cluster.name" +""" +Deprecated: Replaced by `db.namespace`. +""" + +DB_ELASTICSEARCH_NODE_NAME: Final = "db.elasticsearch.node.name" +""" +Deprecated: Replaced by `elasticsearch.node.name`. +""" + +DB_ELASTICSEARCH_PATH_PARTS_TEMPLATE: Final = "db.elasticsearch.path_parts" +""" +Deprecated: Replaced by `db.operation.parameter`. +""" + +DB_INSTANCE_ID: Final = "db.instance.id" +""" +Deprecated: Removed, no general replacement at this time. For Elasticsearch, use `db.elasticsearch.node.name` instead. +""" + +DB_JDBC_DRIVER_CLASSNAME: Final = "db.jdbc.driver_classname" +""" +Deprecated: Removed, no replacement at this time. +""" + +DB_MONGODB_COLLECTION: Final = "db.mongodb.collection" +""" +Deprecated: Replaced by `db.collection.name`. +""" + +DB_MSSQL_INSTANCE_NAME: Final = "db.mssql.instance_name" +""" +Deprecated: Removed, no replacement at this time. +""" + +DB_NAME: Final = "db.name" +""" +Deprecated: Replaced by `db.namespace`. +""" + +DB_NAMESPACE: Final = "db.namespace" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DB_NAMESPACE`. +""" + +DB_OPERATION: Final = "db.operation" +""" +Deprecated: Replaced by `db.operation.name`. +""" + +DB_OPERATION_BATCH_SIZE: Final = "db.operation.batch.size" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DB_OPERATION_BATCH_SIZE`. +""" + +DB_OPERATION_NAME: Final = "db.operation.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DB_OPERATION_NAME`. +""" + +DB_OPERATION_PARAMETER_TEMPLATE: Final = "db.operation.parameter" +""" +A database operation parameter, with `` being the parameter name, and the attribute value being a string representation of the parameter value. +Note: For example, a client-side maximum number of rows to read from the database +MAY be recorded as the `db.operation.parameter.max_rows` attribute. + +`db.query.text` parameters SHOULD be captured using `db.query.parameter.` +instead of `db.operation.parameter.`. +""" + +DB_QUERY_PARAMETER_TEMPLATE: Final = "db.query.parameter" +""" +A database query parameter, with `` being the parameter name, and the attribute value being a string representation of the parameter value. +Note: If a query parameter has no name and instead is referenced only by index, +then `` SHOULD be the 0-based index. + +`db.query.parameter.` SHOULD match +up with the parameterized placeholders present in `db.query.text`. + +It is RECOMMENDED to capture the value as provided by the application +without attempting to do any case normalization or sanitization. + +Instrumentations SHOULD NOT capture `db.query.parameter.` by default +since values may contain PII or sensitive details. +Application operators are expected to enable specific keys depending +on their privacy and security considerations. + +`db.query.parameter.` SHOULD NOT be captured on batch operations. + +Examples: + +- For a query `SELECT * FROM users where username = %s` with the parameter `"jdoe"`, + the attribute `db.query.parameter.0` SHOULD be set to `"jdoe"`. + +- For a query `"SELECT * FROM users WHERE username = %(userName)s;` with parameter + `userName = "jdoe"`, the attribute `db.query.parameter.userName` SHOULD be set to `"jdoe"`. +""" + +DB_QUERY_SUMMARY: Final = "db.query.summary" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DB_QUERY_SUMMARY`. +""" + +DB_QUERY_TEXT: Final = "db.query.text" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DB_QUERY_TEXT`. +""" + +DB_REDIS_DATABASE_INDEX: Final = "db.redis.database_index" +""" +Deprecated: Replaced by `db.namespace` (string). +""" + +DB_RESPONSE_RETURNED_ROWS: Final = "db.response.returned_rows" +""" +Number of rows returned by the operation. +""" + +DB_RESPONSE_STATUS_CODE: Final = "db.response.status_code" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DB_RESPONSE_STATUS_CODE`. +""" + +DB_SQL_TABLE: Final = "db.sql.table" +""" +Deprecated: Replaced by `db.collection.name`, but only if not extracting the value from `db.query.text`. +""" + +DB_STATEMENT: Final = "db.statement" +""" +Deprecated: Replaced by `db.query.text`. +""" + +DB_STORED_PROCEDURE_NAME: Final = "db.stored_procedure.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DB_STORED_PROCEDURE_NAME`. +""" + +DB_SYSTEM: Final = "db.system" +""" +Deprecated: Replaced by `db.system.name`. +""" + +DB_SYSTEM_NAME: Final = "db.system.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DB_SYSTEM_NAME`. +""" + +DB_USER: Final = "db.user" +""" +Deprecated: Removed, no replacement at this time. +""" + + +@deprecated("The attribute db.cassandra.consistency_level is deprecated - Replaced by `cassandra.consistency.level`") +class DbCassandraConsistencyLevelValues(Enum): + ALL = "all" + """all.""" + EACH_QUORUM = "each_quorum" + """each_quorum.""" + QUORUM = "quorum" + """quorum.""" + LOCAL_QUORUM = "local_quorum" + """local_quorum.""" + ONE = "one" + """one.""" + TWO = "two" + """two.""" + THREE = "three" + """three.""" + LOCAL_ONE = "local_one" + """local_one.""" + ANY = "any" + """any.""" + SERIAL = "serial" + """serial.""" + LOCAL_SERIAL = "local_serial" + """local_serial.""" + + +class DbClientConnectionStateValues(Enum): + IDLE = "idle" + """idle.""" + USED = "used" + """used.""" + + +@deprecated("The attribute db.client.connections.state is deprecated - Replaced by `db.client.connection.state`") +class DbClientConnectionsStateValues(Enum): + IDLE = "idle" + """idle.""" + USED = "used" + """used.""" + + +@deprecated("The attribute db.cosmosdb.connection_mode is deprecated - Replaced by `azure.cosmosdb.connection.mode`") +class DbCosmosdbConnectionModeValues(Enum): + GATEWAY = "gateway" + """Gateway (HTTP) connection.""" + DIRECT = "direct" + """Direct connection.""" + + +@deprecated( + "The attribute db.cosmosdb.consistency_level is deprecated - Replaced by `azure.cosmosdb.consistency.level`" +) +class DbCosmosdbConsistencyLevelValues(Enum): + STRONG = "Strong" + """strong.""" + BOUNDED_STALENESS = "BoundedStaleness" + """bounded_staleness.""" + SESSION = "Session" + """session.""" + EVENTUAL = "Eventual" + """eventual.""" + CONSISTENT_PREFIX = "ConsistentPrefix" + """consistent_prefix.""" + + +@deprecated("The attribute db.cosmosdb.operation_type is deprecated - Removed, no replacement at this time") +class DbCosmosdbOperationTypeValues(Enum): + BATCH = "batch" + """batch.""" + CREATE = "create" + """create.""" + DELETE = "delete" + """delete.""" + EXECUTE = "execute" + """execute.""" + EXECUTE_JAVASCRIPT = "execute_javascript" + """execute_javascript.""" + INVALID = "invalid" + """invalid.""" + HEAD = "head" + """head.""" + HEAD_FEED = "head_feed" + """head_feed.""" + PATCH = "patch" + """patch.""" + QUERY = "query" + """query.""" + QUERY_PLAN = "query_plan" + """query_plan.""" + READ = "read" + """read.""" + READ_FEED = "read_feed" + """read_feed.""" + REPLACE = "replace" + """replace.""" + UPSERT = "upsert" + """upsert.""" + + +@deprecated("The attribute db.system is deprecated - Replaced by `db.system.name`") +class DbSystemValues(Enum): + OTHER_SQL = "other_sql" + """Some other SQL database. Fallback only. See notes.""" + ADABAS = "adabas" + """Adabas (Adaptable Database System).""" + CACHE = "cache" + """Deprecated: Replaced by `intersystems_cache`.""" + INTERSYSTEMS_CACHE = "intersystems_cache" + """InterSystems Caché.""" + CASSANDRA = "cassandra" + """Apache Cassandra.""" + CLICKHOUSE = "clickhouse" + """ClickHouse.""" + CLOUDSCAPE = "cloudscape" + """Deprecated: Replaced by `other_sql`.""" + COCKROACHDB = "cockroachdb" + """CockroachDB.""" + COLDFUSION = "coldfusion" + """Deprecated: Obsoleted.""" + COSMOSDB = "cosmosdb" + """Microsoft Azure Cosmos DB.""" + COUCHBASE = "couchbase" + """Couchbase.""" + COUCHDB = "couchdb" + """CouchDB.""" + DB2 = "db2" + """IBM Db2.""" + DERBY = "derby" + """Apache Derby.""" + DYNAMODB = "dynamodb" + """Amazon DynamoDB.""" + EDB = "edb" + """EnterpriseDB.""" + ELASTICSEARCH = "elasticsearch" + """Elasticsearch.""" + FILEMAKER = "filemaker" + """FileMaker.""" + FIREBIRD = "firebird" + """Firebird.""" + FIRSTSQL = "firstsql" + """Deprecated: Replaced by `other_sql`.""" + GEODE = "geode" + """Apache Geode.""" + H2 = "h2" + """H2.""" + HANADB = "hanadb" + """SAP HANA.""" + HBASE = "hbase" + """Apache HBase.""" + HIVE = "hive" + """Apache Hive.""" + HSQLDB = "hsqldb" + """HyperSQL DataBase.""" + INFLUXDB = "influxdb" + """InfluxDB.""" + INFORMIX = "informix" + """Informix.""" + INGRES = "ingres" + """Ingres.""" + INSTANTDB = "instantdb" + """InstantDB.""" + INTERBASE = "interbase" + """InterBase.""" + MARIADB = "mariadb" + """MariaDB.""" + MAXDB = "maxdb" + """SAP MaxDB.""" + MEMCACHED = "memcached" + """Memcached.""" + MONGODB = "mongodb" + """MongoDB.""" + MSSQL = "mssql" + """Microsoft SQL Server.""" + MSSQLCOMPACT = "mssqlcompact" + """Deprecated: Replaced by `other_sql`.""" + MYSQL = "mysql" + """MySQL.""" + NEO4J = "neo4j" + """Neo4j.""" + NETEZZA = "netezza" + """Netezza.""" + OPENSEARCH = "opensearch" + """OpenSearch.""" + ORACLE = "oracle" + """Oracle Database.""" + PERVASIVE = "pervasive" + """Pervasive PSQL.""" + POINTBASE = "pointbase" + """PointBase.""" + POSTGRESQL = "postgresql" + """PostgreSQL.""" + PROGRESS = "progress" + """Progress Database.""" + REDIS = "redis" + """Redis.""" + REDSHIFT = "redshift" + """Amazon Redshift.""" + SPANNER = "spanner" + """Cloud Spanner.""" + SQLITE = "sqlite" + """SQLite.""" + SYBASE = "sybase" + """Sybase.""" + TERADATA = "teradata" + """Teradata.""" + TRINO = "trino" + """Trino.""" + VERTICA = "vertica" + """Vertica.""" + + +@deprecated( + "Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DbSystemNameValues`." +) +class DbSystemNameValues(Enum): + OTHER_SQL = "other_sql" + """Some other SQL database. Fallback only.""" + SOFTWAREAG_ADABAS = "softwareag.adabas" + """[Adabas (Adaptable Database System)](https://documentation.softwareag.com/?pf=adabas).""" + ACTIAN_INGRES = "actian.ingres" + """[Actian Ingres](https://www.actian.com/databases/ingres/).""" + AWS_DYNAMODB = "aws.dynamodb" + """[Amazon DynamoDB](https://aws.amazon.com/pm/dynamodb/).""" + AWS_REDSHIFT = "aws.redshift" + """[Amazon Redshift](https://aws.amazon.com/redshift/).""" + AZURE_COSMOSDB = "azure.cosmosdb" + """[Azure Cosmos DB](https://learn.microsoft.com/azure/cosmos-db).""" + INTERSYSTEMS_CACHE = "intersystems.cache" + """[InterSystems Caché](https://www.intersystems.com/products/cache/).""" + CASSANDRA = "cassandra" + """[Apache Cassandra](https://cassandra.apache.org/).""" + CLICKHOUSE = "clickhouse" + """[ClickHouse](https://clickhouse.com/).""" + COCKROACHDB = "cockroachdb" + """[CockroachDB](https://www.cockroachlabs.com/).""" + COUCHBASE = "couchbase" + """[Couchbase](https://www.couchbase.com/).""" + COUCHDB = "couchdb" + """[Apache CouchDB](https://couchdb.apache.org/).""" + DERBY = "derby" + """[Apache Derby](https://db.apache.org/derby/).""" + ELASTICSEARCH = "elasticsearch" + """[Elasticsearch](https://www.elastic.co/elasticsearch).""" + FIREBIRDSQL = "firebirdsql" + """[Firebird](https://www.firebirdsql.org/).""" + GCP_SPANNER = "gcp.spanner" + """[Google Cloud Spanner](https://cloud.google.com/spanner).""" + GEODE = "geode" + """[Apache Geode](https://geode.apache.org/).""" + H2DATABASE = "h2database" + """[H2 Database](https://h2database.com/).""" + HBASE = "hbase" + """[Apache HBase](https://hbase.apache.org/).""" + HIVE = "hive" + """[Apache Hive](https://hive.apache.org/).""" + HSQLDB = "hsqldb" + """[HyperSQL Database](https://hsqldb.org/).""" + IBM_DB2 = "ibm.db2" + """[IBM Db2](https://www.ibm.com/db2).""" + IBM_INFORMIX = "ibm.informix" + """[IBM Informix](https://www.ibm.com/products/informix).""" + IBM_NETEZZA = "ibm.netezza" + """[IBM Netezza](https://www.ibm.com/products/netezza).""" + INFLUXDB = "influxdb" + """[InfluxDB](https://www.influxdata.com/).""" + INSTANTDB = "instantdb" + """[Instant](https://www.instantdb.com/).""" + MARIADB = "mariadb" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DbSystemNameValues.MARIADB`.""" + MEMCACHED = "memcached" + """[Memcached](https://memcached.org/).""" + MONGODB = "mongodb" + """[MongoDB](https://www.mongodb.com/).""" + MICROSOFT_SQL_SERVER = "microsoft.sql_server" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DbSystemNameValues.MICROSOFT_SQL_SERVER`.""" + MYSQL = "mysql" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DbSystemNameValues.MYSQL`.""" + NEO4J = "neo4j" + """[Neo4j](https://neo4j.com/).""" + OPENSEARCH = "opensearch" + """[OpenSearch](https://opensearch.org/).""" + ORACLE_DB = "oracle.db" + """[Oracle Database](https://www.oracle.com/database/).""" + POSTGRESQL = "postgresql" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.db_attributes.DbSystemNameValues.POSTGRESQL`.""" + REDIS = "redis" + """[Redis](https://redis.io/).""" + SAP_HANA = "sap.hana" + """[SAP HANA](https://www.sap.com/products/technology-platform/hana/what-is-sap-hana.html).""" + SAP_MAXDB = "sap.maxdb" + """[SAP MaxDB](https://maxdb.sap.com/).""" + SQLITE = "sqlite" + """[SQLite](https://www.sqlite.org/).""" + TERADATA = "teradata" + """[Teradata](https://www.teradata.com/).""" + TRINO = "trino" + """[Trino](https://trino.io/).""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/deployment_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/deployment_attributes.py new file mode 100644 index 0000000..77fa069 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/deployment_attributes.py @@ -0,0 +1,53 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +DEPLOYMENT_ENVIRONMENT: Final = "deployment.environment" +""" +Deprecated: Replaced by `deployment.environment.name`. +""" + +DEPLOYMENT_ENVIRONMENT_NAME: Final = "deployment.environment.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.deployment_attributes.DEPLOYMENT_ENVIRONMENT_NAME`. +""" + +DEPLOYMENT_ID: Final = "deployment.id" +""" +The ID of the deployment. +""" + +DEPLOYMENT_NAME: Final = "deployment.name" +""" +The name of the deployment. +""" + +DEPLOYMENT_STATUS: Final = "deployment.status" +""" +The status of the deployment. +""" + + +@deprecated( + "Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.deployment_attributes.DeploymentEnvironmentNameValues`." +) +class DeploymentEnvironmentNameValues(Enum): + PRODUCTION = "production" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.deployment_attributes.DeploymentEnvironmentNameValues.PRODUCTION`.""" + STAGING = "staging" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.deployment_attributes.DeploymentEnvironmentNameValues.STAGING`.""" + TEST = "test" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.deployment_attributes.DeploymentEnvironmentNameValues.TEST`.""" + DEVELOPMENT = "development" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.deployment_attributes.DeploymentEnvironmentNameValues.DEVELOPMENT`.""" + + +class DeploymentStatusValues(Enum): + FAILED = "failed" + """failed.""" + SUCCEEDED = "succeeded" + """succeeded.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/destination_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/destination_attributes.py new file mode 100644 index 0000000..40f7de9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/destination_attributes.py @@ -0,0 +1,15 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +DESTINATION_ADDRESS: Final = "destination.address" +""" +Destination address - domain name if available without reverse DNS lookup; otherwise, IP address or UNIX domain socket name. +Note: When observed from the source side, and when communicating through an intermediary, `destination.address` SHOULD represent the destination address behind any intermediaries, for example proxies, if it's available. +""" + +DESTINATION_PORT: Final = "destination.port" +""" +Destination port number. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/device_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/device_attributes.py new file mode 100644 index 0000000..87dcaaa --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/device_attributes.py @@ -0,0 +1,43 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +DEVICE_ID: Final = "device.id" +""" +A unique identifier representing the device. +Note: Its value SHOULD be identical for all apps on a device and it SHOULD NOT change if an app is uninstalled and re-installed. +However, it might be resettable by the user for all apps on a device. +Hardware IDs (e.g. vendor-specific serial number, IMEI or MAC address) MAY be used as values. + +More information about Android identifier best practices can be found in the [Android user data IDs guide](https://developer.android.com/training/articles/user-data-ids). + +> [!WARNING] +> +> This attribute may contain sensitive (PII) information. Caution should be taken when storing personal data or anything which can identify a user. GDPR and data protection laws may apply, +> ensure you do your own due diligence. +> +> Due to these reasons, this identifier is not recommended for consumer applications and will likely result in rejection from both Google Play and App Store. +> However, it may be appropriate for specific enterprise scenarios, such as kiosk devices or enterprise-managed devices, with appropriate compliance clearance. +> Any instrumentation providing this identifier MUST implement it as an opt-in feature. +> +> See [`app.installation.id`](/docs/registry/attributes/app.md#app-installation-id) for a more privacy-preserving alternative. +""" + +DEVICE_MANUFACTURER: Final = "device.manufacturer" +""" +The name of the device manufacturer. +Note: The Android OS provides this field via [Build](https://developer.android.com/reference/android/os/Build#MANUFACTURER). iOS apps SHOULD hardcode the value `Apple`. +""" + +DEVICE_MODEL_IDENTIFIER: Final = "device.model.identifier" +""" +The model identifier for the device. +Note: It's recommended this value represents a machine-readable version of the model identifier rather than the market or consumer-friendly name of the device. +""" + +DEVICE_MODEL_NAME: Final = "device.model.name" +""" +The marketing name for the device model. +Note: It's recommended this value represents a human-readable version of the device model rather than a machine-readable alternative. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/disk_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/disk_attributes.py new file mode 100644 index 0000000..1e08f41 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/disk_attributes.py @@ -0,0 +1,17 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +DISK_IO_DIRECTION: Final = "disk.io.direction" +""" +The disk IO operation direction. +""" + + +class DiskIoDirectionValues(Enum): + READ = "read" + """read.""" + WRITE = "write" + """write.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/dns_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/dns_attributes.py new file mode 100644 index 0000000..83def12 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/dns_attributes.py @@ -0,0 +1,15 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +DNS_ANSWERS: Final = "dns.answers" +""" +The list of IPv4 or IPv6 addresses resolved during DNS lookup. +""" + +DNS_QUESTION_NAME: Final = "dns.question.name" +""" +The name being queried. +Note: The name represents the queried domain name as it appears in the DNS query without any additional normalization. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/elasticsearch_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/elasticsearch_attributes.py new file mode 100644 index 0000000..8674875 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/elasticsearch_attributes.py @@ -0,0 +1,9 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +ELASTICSEARCH_NODE_NAME: Final = "elasticsearch.node.name" +""" +Represents the human-readable identifier of the node/instance to which a request was routed. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/enduser_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/enduser_attributes.py new file mode 100644 index 0000000..121a2c0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/enduser_attributes.py @@ -0,0 +1,32 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +ENDUSER_ID: Final = "enduser.id" +""" +Unique identifier of an end user in the system. It maybe a username, email address, or other identifier. +Note: Unique identifier of an end user in the system. + +> [!Warning] +> This field contains sensitive (PII) information. +""" + +ENDUSER_PSEUDO_ID: Final = "enduser.pseudo.id" +""" +Pseudonymous identifier of an end user. This identifier should be a random value that is not directly linked or associated with the end user's actual identity. +Note: Pseudonymous identifier of an end user. + +> [!Warning] +> This field contains sensitive (linkable PII) information. +""" + +ENDUSER_ROLE: Final = "enduser.role" +""" +Deprecated: Use `user.roles` instead. +""" + +ENDUSER_SCOPE: Final = "enduser.scope" +""" +Deprecated: Removed, no replacement at this time. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/error_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/error_attributes.py new file mode 100644 index 0000000..53e2212 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/error_attributes.py @@ -0,0 +1,25 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +ERROR_MESSAGE: Final = "error.message" +""" +Deprecated: Use domain-specific error message attribute. For example, use `feature_flag.error.message` for feature flag errors. +""" + +ERROR_TYPE: Final = "error.type" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.error_attributes.ERROR_TYPE`. +""" + + +@deprecated( + "Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.error_attributes.ErrorTypeValues`." +) +class ErrorTypeValues(Enum): + OTHER = "_OTHER" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.error_attributes.ErrorTypeValues.OTHER`.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/event_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/event_attributes.py new file mode 100644 index 0000000..05f481b --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/event_attributes.py @@ -0,0 +1,9 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +EVENT_NAME: Final = "event.name" +""" +Deprecated: The value of this attribute MUST now be set as the value of the EventName field on the LogRecord to indicate that the LogRecord represents an Event. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/exception_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/exception_attributes.py new file mode 100644 index 0000000..efec936 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/exception_attributes.py @@ -0,0 +1,24 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +EXCEPTION_ESCAPED: Final = "exception.escaped" +""" +Deprecated: It's no longer recommended to record exceptions that are handled and do not escape the scope of a span. +""" + +EXCEPTION_MESSAGE: Final = "exception.message" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.exception_attributes.EXCEPTION_MESSAGE`. +""" + +EXCEPTION_STACKTRACE: Final = "exception.stacktrace" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.exception_attributes.EXCEPTION_STACKTRACE`. +""" + +EXCEPTION_TYPE: Final = "exception.type" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.exception_attributes.EXCEPTION_TYPE`. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/faas_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/faas_attributes.py new file mode 100644 index 0000000..21a0b6f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/faas_attributes.py @@ -0,0 +1,150 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +FAAS_COLDSTART: Final = "faas.coldstart" +""" +A boolean that is true if the serverless function is executed for the first time (aka cold-start). +""" + +FAAS_CRON: Final = "faas.cron" +""" +A string containing the schedule period as [Cron Expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). +""" + +FAAS_DOCUMENT_COLLECTION: Final = "faas.document.collection" +""" +The name of the source on which the triggering operation was performed. For example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos DB to the database name. +""" + +FAAS_DOCUMENT_NAME: Final = "faas.document.name" +""" +The document name/table subjected to the operation. For example, in Cloud Storage or S3 is the name of the file, and in Cosmos DB the table name. +""" + +FAAS_DOCUMENT_OPERATION: Final = "faas.document.operation" +""" +Describes the type of the operation that was performed on the data. +""" + +FAAS_DOCUMENT_TIME: Final = "faas.document.time" +""" +A string containing the time when the data was accessed in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). +""" + +FAAS_INSTANCE: Final = "faas.instance" +""" +The execution environment ID as a string, that will be potentially reused for other invocations to the same function/function version. +Note: - **AWS Lambda:** Use the (full) log stream name. +""" + +FAAS_INVOCATION_ID: Final = "faas.invocation_id" +""" +The invocation ID of the current function invocation. +""" + +FAAS_INVOKED_NAME: Final = "faas.invoked_name" +""" +The name of the invoked function. +Note: SHOULD be equal to the `faas.name` resource attribute of the invoked function. +""" + +FAAS_INVOKED_PROVIDER: Final = "faas.invoked_provider" +""" +The cloud provider of the invoked function. +Note: SHOULD be equal to the `cloud.provider` resource attribute of the invoked function. +""" + +FAAS_INVOKED_REGION: Final = "faas.invoked_region" +""" +The cloud region of the invoked function. +Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked function. +""" + +FAAS_MAX_MEMORY: Final = "faas.max_memory" +""" +The amount of memory available to the serverless function converted to Bytes. +Note: It's recommended to set this attribute since e.g. too little memory can easily stop a Java AWS Lambda function from working correctly. On AWS Lambda, the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this information (which must be multiplied by 1,048,576). +""" + +FAAS_NAME: Final = "faas.name" +""" +The name of the single function that this runtime instance executes. +Note: This is the name of the function as configured/deployed on the FaaS +platform and is usually different from the name of the callback +function (which may be stored in the +[`code.namespace`/`code.function.name`](/docs/general/attributes.md#source-code-attributes) +span attributes). + +For some cloud providers, the above definition is ambiguous. The following +definition of function name MUST be used for this attribute +(and consequently the span name) for the listed cloud providers/products: + +- **Azure:** The full name `/`, i.e., function app name + followed by a forward slash followed by the function name (this form + can also be seen in the resource JSON for the function). + This means that a span attribute MUST be used, as an Azure function + app can host multiple functions that would usually share + a TracerProvider (see also the `cloud.resource_id` attribute). +""" + +FAAS_TIME: Final = "faas.time" +""" +A string containing the function invocation time in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). +""" + +FAAS_TRIGGER: Final = "faas.trigger" +""" +Type of the trigger which caused this function invocation. +""" + +FAAS_VERSION: Final = "faas.version" +""" +The immutable version of the function being executed. +Note: Depending on the cloud provider and platform, use: + +- **AWS Lambda:** The [function version](https://docs.aws.amazon.com/lambda/latest/dg/configuration-versions.html) + (an integer represented as a decimal string). +- **Google Cloud Run (Services):** The [revision](https://cloud.google.com/run/docs/managing/revisions) + (i.e., the function name plus the revision suffix). +- **Google Cloud Functions:** The value of the + [`K_REVISION` environment variable](https://cloud.google.com/run/docs/container-contract#services-env-vars). +- **Azure Functions:** Not applicable. Do not set this attribute. +""" + + +class FaasDocumentOperationValues(Enum): + INSERT = "insert" + """When a new object is created.""" + EDIT = "edit" + """When an object is modified.""" + DELETE = "delete" + """When an object is deleted.""" + + +class FaasInvokedProviderValues(Enum): + ALIBABA_CLOUD = "alibaba_cloud" + """Alibaba Cloud.""" + AWS = "aws" + """Amazon Web Services.""" + AZURE = "azure" + """Microsoft Azure.""" + GCP = "gcp" + """Google Cloud Platform.""" + TENCENT_CLOUD = "tencent_cloud" + """Tencent Cloud.""" + + +class FaasTriggerValues(Enum): + DATASOURCE = "datasource" + """A response to some data source operation such as a database or filesystem read/write.""" + HTTP = "http" + """To provide an answer to an inbound HTTP request.""" + PUBSUB = "pubsub" + """A function is set to be executed when messages are sent to a messaging system.""" + TIMER = "timer" + """A function is scheduled to be executed regularly.""" + OTHER = "other" + """If none of the others apply.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/feature_flag_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/feature_flag_attributes.py new file mode 100644 index 0000000..414e6d2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/feature_flag_attributes.py @@ -0,0 +1,119 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +FEATURE_FLAG_CONTEXT_ID: Final = "feature_flag.context.id" +""" +The unique identifier for the flag evaluation context. For example, the targeting key. +""" + +FEATURE_FLAG_ERROR_MESSAGE: Final = "feature_flag.error.message" +""" +A message providing more detail about an error that occurred during feature flag evaluation in human-readable form. +""" + +FEATURE_FLAG_EVALUATION_ERROR_MESSAGE: Final = "feature_flag.evaluation.error.message" +""" +Deprecated: Replaced by `feature_flag.error.message`. +""" + +FEATURE_FLAG_EVALUATION_REASON: Final = "feature_flag.evaluation.reason" +""" +Deprecated: Replaced by `feature_flag.result.reason`. +""" + +FEATURE_FLAG_KEY: Final = "feature_flag.key" +""" +The lookup key of the feature flag. +""" + +FEATURE_FLAG_PROVIDER_NAME: Final = "feature_flag.provider.name" +""" +Identifies the feature flag provider. +""" + +FEATURE_FLAG_RESULT_REASON: Final = "feature_flag.result.reason" +""" +The reason code which shows how a feature flag value was determined. +""" + +FEATURE_FLAG_RESULT_VALUE: Final = "feature_flag.result.value" +""" +The evaluated value of the feature flag. +Note: With some feature flag providers, feature flag results can be quite large or contain private or sensitive details. +Because of this, `feature_flag.result.variant` is often the preferred attribute if it is available. + +It may be desirable to redact or otherwise limit the size and scope of `feature_flag.result.value` if possible. +Because the evaluated flag value is unstructured and may be any type, it is left to the instrumentation author to determine how best to achieve this. +""" + +FEATURE_FLAG_RESULT_VARIANT: Final = "feature_flag.result.variant" +""" +A semantic identifier for an evaluated flag value. +Note: A semantic identifier, commonly referred to as a variant, provides a means +for referring to a value without including the value itself. This can +provide additional context for understanding the meaning behind a value. +For example, the variant `red` maybe be used for the value `#c05543`. +""" + +FEATURE_FLAG_SET_ID: Final = "feature_flag.set.id" +""" +The identifier of the [flag set](https://openfeature.dev/specification/glossary/#flag-set) to which the feature flag belongs. +""" + +FEATURE_FLAG_VARIANT: Final = "feature_flag.variant" +""" +Deprecated: Replaced by `feature_flag.result.variant`. +""" + +FEATURE_FLAG_VERSION: Final = "feature_flag.version" +""" +The version of the ruleset used during the evaluation. This may be any stable value which uniquely identifies the ruleset. +""" + + +@deprecated("The attribute feature_flag.evaluation.reason is deprecated - Replaced by `feature_flag.result.reason`") +class FeatureFlagEvaluationReasonValues(Enum): + STATIC = "static" + """The resolved value is static (no dynamic evaluation).""" + DEFAULT = "default" + """The resolved value fell back to a pre-configured value (no dynamic evaluation occurred or dynamic evaluation yielded no result).""" + TARGETING_MATCH = "targeting_match" + """The resolved value was the result of a dynamic evaluation, such as a rule or specific user-targeting.""" + SPLIT = "split" + """The resolved value was the result of pseudorandom assignment.""" + CACHED = "cached" + """The resolved value was retrieved from cache.""" + DISABLED = "disabled" + """The resolved value was the result of the flag being disabled in the management system.""" + UNKNOWN = "unknown" + """The reason for the resolved value could not be determined.""" + STALE = "stale" + """The resolved value is non-authoritative or possibly out of date.""" + ERROR = "error" + """The resolved value was the result of an error.""" + + +class FeatureFlagResultReasonValues(Enum): + STATIC = "static" + """The resolved value is static (no dynamic evaluation).""" + DEFAULT = "default" + """The resolved value fell back to a pre-configured value (no dynamic evaluation occurred or dynamic evaluation yielded no result).""" + TARGETING_MATCH = "targeting_match" + """The resolved value was the result of a dynamic evaluation, such as a rule or specific user-targeting.""" + SPLIT = "split" + """The resolved value was the result of pseudorandom assignment.""" + CACHED = "cached" + """The resolved value was retrieved from cache.""" + DISABLED = "disabled" + """The resolved value was the result of the flag being disabled in the management system.""" + UNKNOWN = "unknown" + """The reason for the resolved value could not be determined.""" + STALE = "stale" + """The resolved value is non-authoritative or possibly out of date.""" + ERROR = "error" + """The resolved value was the result of an error.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/file_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/file_attributes.py new file mode 100644 index 0000000..f84f0ea --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/file_attributes.py @@ -0,0 +1,125 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +FILE_ACCESSED: Final = "file.accessed" +""" +Time when the file was last accessed, in ISO 8601 format. +Note: This attribute might not be supported by some file systems — NFS, FAT32, in embedded OS, etc. +""" + +FILE_ATTRIBUTES: Final = "file.attributes" +""" +Array of file attributes. +Note: Attributes names depend on the OS or file system. Here’s a non-exhaustive list of values expected for this attribute: `archive`, `compressed`, `directory`, `encrypted`, `execute`, `hidden`, `immutable`, `journaled`, `read`, `readonly`, `symbolic link`, `system`, `temporary`, `write`. +""" + +FILE_CHANGED: Final = "file.changed" +""" +Time when the file attributes or metadata was last changed, in ISO 8601 format. +Note: `file.changed` captures the time when any of the file's properties or attributes (including the content) are changed, while `file.modified` captures the timestamp when the file content is modified. +""" + +FILE_CREATED: Final = "file.created" +""" +Time when the file was created, in ISO 8601 format. +Note: This attribute might not be supported by some file systems — NFS, FAT32, in embedded OS, etc. +""" + +FILE_DIRECTORY: Final = "file.directory" +""" +Directory where the file is located. It should include the drive letter, when appropriate. +""" + +FILE_EXTENSION: Final = "file.extension" +""" +File extension, excluding the leading dot. +Note: When the filename has multiple extensions (example.tar.gz), only the last one should be captured ("gz", not "tar.gz"). +""" + +FILE_FORK_NAME: Final = "file.fork_name" +""" +Name of the fork. A fork is additional data associated with a filesystem object. +Note: On Linux, a resource fork is used to store additional data with a filesystem object. A file always has at least one fork for the data portion, and additional forks may exist. +On NTFS, this is analogous to an Alternate Data Stream (ADS), and the default data stream for a file is just called $DATA. Zone.Identifier is commonly used by Windows to track contents downloaded from the internet. An ADS is typically of the form: C:\\path\\to\\filename.extension:some_fork_name, and some_fork_name is the value that should populate `fork_name`. `filename.extension` should populate `file.name`, and `extension` should populate `file.extension`. The full path, `file.path`, will include the fork name. +""" + +FILE_GROUP_ID: Final = "file.group.id" +""" +Primary Group ID (GID) of the file. +""" + +FILE_GROUP_NAME: Final = "file.group.name" +""" +Primary group name of the file. +""" + +FILE_INODE: Final = "file.inode" +""" +Inode representing the file in the filesystem. +""" + +FILE_LOCK_MECHANISM: Final = "file.lock.mechanism" +""" +The lock mechanism such as noted by [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html). +""" + +FILE_LOCK_MODE: Final = "file.lock.mode" +""" +Mode of lock or operation such as documented by [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html). +""" + +FILE_LOCK_TYPE: Final = "file.lock.type" +""" +The lock type as represented by i.e. [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html)'s l_type. +""" + +FILE_MODE: Final = "file.mode" +""" +Mode of the file in octal representation. +""" + +FILE_MODIFIED: Final = "file.modified" +""" +Time when the file content was last modified, in ISO 8601 format. +""" + +FILE_NAME: Final = "file.name" +""" +Name of the file including the extension, without the directory. +""" + +FILE_OWNER_ID: Final = "file.owner.id" +""" +The user ID (UID) or security identifier (SID) of the file owner. +""" + +FILE_OWNER_NAME: Final = "file.owner.name" +""" +Username of the file owner. +""" + +FILE_PATH: Final = "file.path" +""" +Full path to the file, including the filename. It should include the drive letter, when appropriate. +""" + +FILE_SIZE: Final = "file.size" +""" +File size in bytes. +""" + +FILE_SYMBOLIC_LINK_TARGET_PATH: Final = "file.symbolic_link.target_path" +""" +Path to the target of a symbolic link. +Note: This attribute is only applicable to symbolic links. +""" + + +class FileLockTypeValues(Enum): + READ = "read" + """read.""" + WRITE = "write" + """write.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/gcp_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/gcp_attributes.py new file mode 100644 index 0000000..b472e4c --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/gcp_attributes.py @@ -0,0 +1,234 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +GCP_APPHUB_APPLICATION_CONTAINER: Final = "gcp.apphub.application.container" +""" +The container within GCP where the AppHub application is defined. +""" + +GCP_APPHUB_APPLICATION_ID: Final = "gcp.apphub.application.id" +""" +The name of the application as configured in AppHub. +""" + +GCP_APPHUB_APPLICATION_LOCATION: Final = "gcp.apphub.application.location" +""" +The GCP zone or region where the application is defined. +""" + +GCP_APPHUB_SERVICE_CRITICALITY_TYPE: Final = "gcp.apphub.service.criticality_type" +""" +Criticality of a service indicates its importance to the business. +Note: [See AppHub type enum](https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type). +""" + +GCP_APPHUB_SERVICE_ENVIRONMENT_TYPE: Final = "gcp.apphub.service.environment_type" +""" +Environment of a service is the stage of a software lifecycle. +Note: [See AppHub environment type](https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type_1). +""" + +GCP_APPHUB_SERVICE_ID: Final = "gcp.apphub.service.id" +""" +The name of the service as configured in AppHub. +""" + +GCP_APPHUB_WORKLOAD_CRITICALITY_TYPE: Final = "gcp.apphub.workload.criticality_type" +""" +Criticality of a workload indicates its importance to the business. +Note: [See AppHub type enum](https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type). +""" + +GCP_APPHUB_WORKLOAD_ENVIRONMENT_TYPE: Final = "gcp.apphub.workload.environment_type" +""" +Environment of a workload is the stage of a software lifecycle. +Note: [See AppHub environment type](https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type_1). +""" + +GCP_APPHUB_WORKLOAD_ID: Final = "gcp.apphub.workload.id" +""" +The name of the workload as configured in AppHub. +""" + +GCP_APPHUB_DESTINATION_APPLICATION_CONTAINER: Final = "gcp.apphub_destination.application.container" +""" +The container within GCP where the AppHub destination application is defined. +""" + +GCP_APPHUB_DESTINATION_APPLICATION_ID: Final = "gcp.apphub_destination.application.id" +""" +The name of the destination application as configured in AppHub. +""" + +GCP_APPHUB_DESTINATION_APPLICATION_LOCATION: Final = "gcp.apphub_destination.application.location" +""" +The GCP zone or region where the destination application is defined. +""" + +GCP_APPHUB_DESTINATION_SERVICE_CRITICALITY_TYPE: Final = "gcp.apphub_destination.service.criticality_type" +""" +Criticality of a destination workload indicates its importance to the business as specified in [AppHub type enum](https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type). +""" + +GCP_APPHUB_DESTINATION_SERVICE_ENVIRONMENT_TYPE: Final = "gcp.apphub_destination.service.environment_type" +""" +Software lifecycle stage of a destination service as defined [AppHub environment type](https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type_1). +""" + +GCP_APPHUB_DESTINATION_SERVICE_ID: Final = "gcp.apphub_destination.service.id" +""" +The name of the destination service as configured in AppHub. +""" + +GCP_APPHUB_DESTINATION_WORKLOAD_CRITICALITY_TYPE: Final = "gcp.apphub_destination.workload.criticality_type" +""" +Criticality of a destination workload indicates its importance to the business as specified in [AppHub type enum](https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type). +""" + +GCP_APPHUB_DESTINATION_WORKLOAD_ENVIRONMENT_TYPE: Final = "gcp.apphub_destination.workload.environment_type" +""" +Environment of a destination workload is the stage of a software lifecycle as provided in the [AppHub environment type](https://cloud.google.com/app-hub/docs/reference/rest/v1/Attributes#type_1). +""" + +GCP_APPHUB_DESTINATION_WORKLOAD_ID: Final = "gcp.apphub_destination.workload.id" +""" +The name of the destination workload as configured in AppHub. +""" + +GCP_CLIENT_SERVICE: Final = "gcp.client.service" +""" +Identifies the Google Cloud service for which the official client library is intended. +Note: Intended to be a stable identifier for Google Cloud client libraries that is uniform across implementation languages. The value should be derived from the canonical service domain for the service; for example, 'foo.googleapis.com' should result in a value of 'foo'. +""" + +GCP_CLOUD_RUN_JOB_EXECUTION: Final = "gcp.cloud_run.job.execution" +""" +The name of the Cloud Run [execution](https://cloud.google.com/run/docs/managing/job-executions) being run for the Job, as set by the [`CLOUD_RUN_EXECUTION`](https://cloud.google.com/run/docs/container-contract#jobs-env-vars) environment variable. +""" + +GCP_CLOUD_RUN_JOB_TASK_INDEX: Final = "gcp.cloud_run.job.task_index" +""" +The index for a task within an execution as provided by the [`CLOUD_RUN_TASK_INDEX`](https://cloud.google.com/run/docs/container-contract#jobs-env-vars) environment variable. +""" + +GCP_GCE_INSTANCE_HOSTNAME: Final = "gcp.gce.instance.hostname" +""" +The hostname of a GCE instance. This is the full value of the default or [custom hostname](https://cloud.google.com/compute/docs/instances/custom-hostname-vm). +""" + +GCP_GCE_INSTANCE_LABELS_TEMPLATE: Final = "gcp.gce.instance.labels" +""" +GCE instance labels, `` being the label name and the value being the label value. +Note: For example, a GCE instance label `team` with value `observability` SHOULD be recorded as the `gcp.gce.instance.labels.team` attribute with value `"observability"`. The `` MUST be the exact GCE instance label key. +""" + +GCP_GCE_INSTANCE_NAME: Final = "gcp.gce.instance.name" +""" +The instance name of a GCE instance. This is the value provided by `host.name`, the visible name of the instance in the Cloud Console UI, and the prefix for the default hostname of the instance as defined by the [default internal DNS name](https://cloud.google.com/compute/docs/internal-dns#instance-fully-qualified-domain-names). +""" + +GCP_GCE_INSTANCE_GROUP_MANAGER_NAME: Final = "gcp.gce.instance_group_manager.name" +""" +The name of the Instance Group Manager (IGM) that manages this VM, if any. +""" + +GCP_GCE_INSTANCE_GROUP_MANAGER_REGION: Final = "gcp.gce.instance_group_manager.region" +""" +The region of a **regional** Instance Group Manager (e.g., `us-central1`). Set this **only** when the IGM is regional. +""" + +GCP_GCE_INSTANCE_GROUP_MANAGER_ZONE: Final = "gcp.gce.instance_group_manager.zone" +""" +The zone of a **zonal** Instance Group Manager (e.g., `us-central1-a`). Set this **only** when the IGM is zonal. +""" + + +class GcpApphubServiceCriticalityTypeValues(Enum): + MISSION_CRITICAL = "MISSION_CRITICAL" + """Mission critical service.""" + HIGH = "HIGH" + """High impact.""" + MEDIUM = "MEDIUM" + """Medium impact.""" + LOW = "LOW" + """Low impact.""" + + +class GcpApphubServiceEnvironmentTypeValues(Enum): + PRODUCTION = "PRODUCTION" + """Production environment.""" + STAGING = "STAGING" + """Staging environment.""" + TEST = "TEST" + """Test environment.""" + DEVELOPMENT = "DEVELOPMENT" + """Development environment.""" + + +class GcpApphubWorkloadCriticalityTypeValues(Enum): + MISSION_CRITICAL = "MISSION_CRITICAL" + """Mission critical service.""" + HIGH = "HIGH" + """High impact.""" + MEDIUM = "MEDIUM" + """Medium impact.""" + LOW = "LOW" + """Low impact.""" + + +class GcpApphubWorkloadEnvironmentTypeValues(Enum): + PRODUCTION = "PRODUCTION" + """Production environment.""" + STAGING = "STAGING" + """Staging environment.""" + TEST = "TEST" + """Test environment.""" + DEVELOPMENT = "DEVELOPMENT" + """Development environment.""" + + +class GcpApphubDestinationServiceCriticalityTypeValues(Enum): + MISSION_CRITICAL = "MISSION_CRITICAL" + """Mission critical service.""" + HIGH = "HIGH" + """High impact.""" + MEDIUM = "MEDIUM" + """Medium impact.""" + LOW = "LOW" + """Low impact.""" + + +class GcpApphubDestinationServiceEnvironmentTypeValues(Enum): + PRODUCTION = "PRODUCTION" + """Production environment.""" + STAGING = "STAGING" + """Staging environment.""" + TEST = "TEST" + """Test environment.""" + DEVELOPMENT = "DEVELOPMENT" + """Development environment.""" + + +class GcpApphubDestinationWorkloadCriticalityTypeValues(Enum): + MISSION_CRITICAL = "MISSION_CRITICAL" + """Mission critical service.""" + HIGH = "HIGH" + """High impact.""" + MEDIUM = "MEDIUM" + """Medium impact.""" + LOW = "LOW" + """Low impact.""" + + +class GcpApphubDestinationWorkloadEnvironmentTypeValues(Enum): + PRODUCTION = "PRODUCTION" + """Production environment.""" + STAGING = "STAGING" + """Staging environment.""" + TEST = "TEST" + """Test environment.""" + DEVELOPMENT = "DEVELOPMENT" + """Development environment.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/gen_ai_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/gen_ai_attributes.py new file mode 100644 index 0000000..b4635b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/gen_ai_attributes.py @@ -0,0 +1,459 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +GEN_AI_AGENT_DESCRIPTION: Final = "gen_ai.agent.description" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_AGENT_ID: Final = "gen_ai.agent.id" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_AGENT_NAME: Final = "gen_ai.agent.name" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_AGENT_VERSION: Final = "gen_ai.agent.version" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_COMPLETION: Final = "gen_ai.completion" +""" +Deprecated: Removed, no replacement at this time. +""" + +GEN_AI_CONVERSATION_ID: Final = "gen_ai.conversation.id" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_DATA_SOURCE_ID: Final = "gen_ai.data_source.id" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_EMBEDDINGS_DIMENSION_COUNT: Final = "gen_ai.embeddings.dimension.count" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_EVALUATION_EXPLANATION: Final = "gen_ai.evaluation.explanation" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_EVALUATION_NAME: Final = "gen_ai.evaluation.name" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_EVALUATION_SCORE_LABEL: Final = "gen_ai.evaluation.score.label" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_EVALUATION_SCORE_VALUE: Final = "gen_ai.evaluation.score.value" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_INPUT_MESSAGES: Final = "gen_ai.input.messages" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_OPENAI_REQUEST_RESPONSE_FORMAT: Final = "gen_ai.openai.request.response_format" +""" +Deprecated: Replaced by `gen_ai.output.type`, which has moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_OPENAI_REQUEST_SEED: Final = "gen_ai.openai.request.seed" +""" +Deprecated: Replaced by `gen_ai.request.seed`, which has moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_OPENAI_REQUEST_SERVICE_TIER: Final = "gen_ai.openai.request.service_tier" +""" +Deprecated: Replaced by `openai.request.service_tier`, which has moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_OPENAI_RESPONSE_SERVICE_TIER: Final = "gen_ai.openai.response.service_tier" +""" +Deprecated: Replaced by `openai.response.service_tier`, which has moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_OPENAI_RESPONSE_SYSTEM_FINGERPRINT: Final = "gen_ai.openai.response.system_fingerprint" +""" +Deprecated: Replaced by `openai.response.system_fingerprint`, which has moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_OPERATION_NAME: Final = "gen_ai.operation.name" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_OUTPUT_MESSAGES: Final = "gen_ai.output.messages" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_OUTPUT_TYPE: Final = "gen_ai.output.type" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_PROMPT: Final = "gen_ai.prompt" +""" +Deprecated: Removed, no replacement at this time. +""" + +GEN_AI_PROMPT_NAME: Final = "gen_ai.prompt.name" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_PROVIDER_NAME: Final = "gen_ai.provider.name" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_REQUEST_CHOICE_COUNT: Final = "gen_ai.request.choice.count" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_REQUEST_ENCODING_FORMATS: Final = "gen_ai.request.encoding_formats" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_REQUEST_FREQUENCY_PENALTY: Final = "gen_ai.request.frequency_penalty" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_REQUEST_MAX_TOKENS: Final = "gen_ai.request.max_tokens" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_REQUEST_MODEL: Final = "gen_ai.request.model" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_REQUEST_PRESENCE_PENALTY: Final = "gen_ai.request.presence_penalty" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_REQUEST_SEED: Final = "gen_ai.request.seed" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_REQUEST_STOP_SEQUENCES: Final = "gen_ai.request.stop_sequences" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_REQUEST_STREAM: Final = "gen_ai.request.stream" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_REQUEST_TEMPERATURE: Final = "gen_ai.request.temperature" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_REQUEST_TOP_K: Final = "gen_ai.request.top_k" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_REQUEST_TOP_P: Final = "gen_ai.request.top_p" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_RESPONSE_FINISH_REASONS: Final = "gen_ai.response.finish_reasons" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_RESPONSE_ID: Final = "gen_ai.response.id" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_RESPONSE_MODEL: Final = "gen_ai.response.model" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK: Final = "gen_ai.response.time_to_first_chunk" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_RETRIEVAL_DOCUMENTS: Final = "gen_ai.retrieval.documents" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_RETRIEVAL_QUERY_TEXT: Final = "gen_ai.retrieval.query.text" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_SYSTEM: Final = "gen_ai.system" +""" +Deprecated: Replaced by `gen_ai.provider.name`, which has moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_SYSTEM_INSTRUCTIONS: Final = "gen_ai.system_instructions" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_TOKEN_TYPE: Final = "gen_ai.token.type" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_TOOL_CALL_ARGUMENTS: Final = "gen_ai.tool.call.arguments" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_TOOL_CALL_ID: Final = "gen_ai.tool.call.id" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_TOOL_CALL_RESULT: Final = "gen_ai.tool.call.result" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_TOOL_DEFINITIONS: Final = "gen_ai.tool.definitions" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_TOOL_DESCRIPTION: Final = "gen_ai.tool.description" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_TOOL_NAME: Final = "gen_ai.tool.name" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_TOOL_TYPE: Final = "gen_ai.tool.type" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS: Final = "gen_ai.usage.cache_creation.input_tokens" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS: Final = "gen_ai.usage.cache_read.input_tokens" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_USAGE_COMPLETION_TOKENS: Final = "gen_ai.usage.completion_tokens" +""" +Deprecated: Replaced by `gen_ai.usage.output_tokens`, which has moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_USAGE_INPUT_TOKENS: Final = "gen_ai.usage.input_tokens" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_USAGE_OUTPUT_TOKENS: Final = "gen_ai.usage.output_tokens" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_USAGE_PROMPT_TOKENS: Final = "gen_ai.usage.prompt_tokens" +""" +Deprecated: Replaced by `gen_ai.usage.input_tokens`, which has moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_USAGE_REASONING_OUTPUT_TOKENS: Final = "gen_ai.usage.reasoning.output_tokens" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +GEN_AI_WORKFLOW_NAME: Final = "gen_ai.workflow.name" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +@deprecated( + "The attribute gen_ai.openai.request.response_format is deprecated - Replaced by `gen_ai.output.type`, which has moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai)" +) +class GenAiOpenaiRequestResponseFormatValues(Enum): + TEXT = "text" + """Text response format.""" + JSON_OBJECT = "json_object" + """JSON object response format.""" + JSON_SCHEMA = "json_schema" + """JSON schema response format.""" + + +@deprecated( + "The attribute gen_ai.openai.request.service_tier is deprecated - Replaced by `openai.request.service_tier`, which has moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai)" +) +class GenAiOpenaiRequestServiceTierValues(Enum): + AUTO = "auto" + """The system will utilize scale tier credits until they are exhausted.""" + DEFAULT = "default" + """The system will utilize the default scale tier.""" + + +@deprecated( + "The attribute gen_ai.operation.name is deprecated - Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai)" +) +class GenAiOperationNameValues(Enum): + CHAT = "chat" + """Chat completion operation such as [OpenAI Chat API](https://platform.openai.com/docs/api-reference/chat).""" + GENERATE_CONTENT = "generate_content" + """Multimodal content generation operation such as [Gemini Generate Content](https://ai.google.dev/api/generate-content).""" + TEXT_COMPLETION = "text_completion" + """Text completions operation such as [OpenAI Completions API (Legacy)](https://platform.openai.com/docs/api-reference/completions).""" + EMBEDDINGS = "embeddings" + """Embeddings operation such as [OpenAI Create embeddings API](https://platform.openai.com/docs/api-reference/embeddings/create).""" + RETRIEVAL = "retrieval" + """Retrieval operation such as [OpenAI Search Vector Store API](https://platform.openai.com/docs/api-reference/vector-stores/search).""" + CREATE_AGENT = "create_agent" + """Create GenAI agent.""" + INVOKE_AGENT = "invoke_agent" + """Invoke GenAI agent.""" + EXECUTE_TOOL = "execute_tool" + """Execute a tool.""" + INVOKE_WORKFLOW = "invoke_workflow" + """Invoke GenAI workflow.""" + + +@deprecated( + "The attribute gen_ai.output.type is deprecated - Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai)" +) +class GenAiOutputTypeValues(Enum): + TEXT = "text" + """Plain text.""" + JSON = "json" + """JSON object with known or unknown schema.""" + IMAGE = "image" + """Image.""" + SPEECH = "speech" + """Speech.""" + + +@deprecated( + "The attribute gen_ai.provider.name is deprecated - Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai)" +) +class GenAiProviderNameValues(Enum): + OPENAI = "openai" + """[OpenAI](https://openai.com/).""" + GCP_GEN_AI = "gcp.gen_ai" + """Any Google generative AI endpoint.""" + GCP_VERTEX_AI = "gcp.vertex_ai" + """[Vertex AI](https://cloud.google.com/vertex-ai).""" + GCP_GEMINI = "gcp.gemini" + """[Gemini](https://cloud.google.com/products/gemini).""" + ANTHROPIC = "anthropic" + """[Anthropic](https://www.anthropic.com/).""" + COHERE = "cohere" + """[Cohere](https://cohere.com/).""" + AZURE_AI_INFERENCE = "azure.ai.inference" + """Azure AI Inference.""" + AZURE_AI_OPENAI = "azure.ai.openai" + """[Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/overview).""" + IBM_WATSONX_AI = "ibm.watsonx.ai" + """[IBM Watsonx AI](https://www.ibm.com/products/watsonx-ai).""" + AWS_BEDROCK = "aws.bedrock" + """[AWS Bedrock](https://aws.amazon.com/bedrock).""" + PERPLEXITY = "perplexity" + """[Perplexity](https://www.perplexity.ai/).""" + X_AI = "x_ai" + """[xAI](https://x.ai/).""" + DEEPSEEK = "deepseek" + """[DeepSeek](https://www.deepseek.com/).""" + GROQ = "groq" + """[Groq](https://groq.com/).""" + MISTRAL_AI = "mistral_ai" + """[Mistral AI](https://mistral.ai/).""" + + +@deprecated( + "The attribute gen_ai.system is deprecated - Replaced by `gen_ai.provider.name`, which has moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai)" +) +class GenAiSystemValues(Enum): + OPENAI = "openai" + """OpenAI.""" + GCP_GEN_AI = "gcp.gen_ai" + """Any Google generative AI endpoint.""" + GCP_VERTEX_AI = "gcp.vertex_ai" + """Vertex AI.""" + GCP_GEMINI = "gcp.gemini" + """Gemini.""" + VERTEX_AI = "vertex_ai" + """Deprecated: Replaced by `gcp.vertex_ai`.""" + GEMINI = "gemini" + """Deprecated: Replaced by `gcp.gemini`.""" + ANTHROPIC = "anthropic" + """Anthropic.""" + COHERE = "cohere" + """Cohere.""" + AZ_AI_INFERENCE = "az.ai.inference" + """Deprecated: Replaced by `azure.ai.inference`.""" + AZ_AI_OPENAI = "az.ai.openai" + """Deprecated: Replaced by `azure.ai.openai`.""" + AZURE_AI_INFERENCE = "azure.ai.inference" + """Azure AI Inference.""" + AZURE_AI_OPENAI = "azure.ai.openai" + """Azure OpenAI.""" + IBM_WATSONX_AI = "ibm.watsonx.ai" + """IBM Watsonx AI.""" + AWS_BEDROCK = "aws.bedrock" + """AWS Bedrock.""" + PERPLEXITY = "perplexity" + """Perplexity.""" + XAI = "xai" + """xAI.""" + DEEPSEEK = "deepseek" + """DeepSeek.""" + GROQ = "groq" + """Groq.""" + MISTRAL_AI = "mistral_ai" + """Mistral AI.""" + + +@deprecated( + "The attribute gen_ai.token.type is deprecated - Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai)" +) +class GenAiTokenTypeValues(Enum): + INPUT = "input" + """Input tokens (prompt, input, etc.).""" + COMPLETION = "output" + """Deprecated: Replaced by `output`.""" + OUTPUT = "output" + """Output tokens (completion, response, etc.).""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/geo_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/geo_attributes.py new file mode 100644 index 0000000..a32cfdd --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/geo_attributes.py @@ -0,0 +1,57 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +GEO_CONTINENT_CODE: Final = "geo.continent.code" +""" +Two-letter code representing continent’s name. +""" + +GEO_COUNTRY_ISO_CODE: Final = "geo.country.iso_code" +""" +Two-letter ISO Country Code ([ISO 3166-1 alpha2](https://wikipedia.org/wiki/ISO_3166-1#Codes)). +""" + +GEO_LOCALITY_NAME: Final = "geo.locality.name" +""" +Locality name. Represents the name of a city, town, village, or similar populated place. +""" + +GEO_LOCATION_LAT: Final = "geo.location.lat" +""" +Latitude of the geo location in [WGS84](https://wikipedia.org/wiki/World_Geodetic_System#WGS84). +""" + +GEO_LOCATION_LON: Final = "geo.location.lon" +""" +Longitude of the geo location in [WGS84](https://wikipedia.org/wiki/World_Geodetic_System#WGS84). +""" + +GEO_POSTAL_CODE: Final = "geo.postal_code" +""" +Postal code associated with the location. Values appropriate for this field may also be known as a postcode or ZIP code and will vary widely from country to country. +""" + +GEO_REGION_ISO_CODE: Final = "geo.region.iso_code" +""" +Region ISO code ([ISO 3166-2](https://wikipedia.org/wiki/ISO_3166-2)). +""" + + +class GeoContinentCodeValues(Enum): + AF = "AF" + """Africa.""" + AN = "AN" + """Antarctica.""" + AS = "AS" + """Asia.""" + EU = "EU" + """Europe.""" + NA = "NA" + """North America.""" + OC = "OC" + """Oceania.""" + SA = "SA" + """South America.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/graphql_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/graphql_attributes.py new file mode 100644 index 0000000..83c0d67 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/graphql_attributes.py @@ -0,0 +1,30 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +GRAPHQL_DOCUMENT: Final = "graphql.document" +""" +The GraphQL document being executed. +Note: If instrumentation can reliably identify and redact sensitive information it SHOULD do it. +""" + +GRAPHQL_OPERATION_NAME: Final = "graphql.operation.name" +""" +The name of the operation being executed. +""" + +GRAPHQL_OPERATION_TYPE: Final = "graphql.operation.type" +""" +The type of the operation being executed. +""" + + +class GraphqlOperationTypeValues(Enum): + QUERY = "query" + """GraphQL query.""" + MUTATION = "mutation" + """GraphQL mutation.""" + SUBSCRIPTION = "subscription" + """GraphQL subscription.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/heroku_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/heroku_attributes.py new file mode 100644 index 0000000..2de0654 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/heroku_attributes.py @@ -0,0 +1,19 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +HEROKU_APP_ID: Final = "heroku.app.id" +""" +Unique identifier for the application. +""" + +HEROKU_RELEASE_COMMIT: Final = "heroku.release.commit" +""" +Commit hash for the current release. +""" + +HEROKU_RELEASE_CREATION_TIMESTAMP: Final = "heroku.release.creation_timestamp" +""" +Time and date the release was created. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/host_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/host_attributes.py new file mode 100644 index 0000000..0bf5391 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/host_attributes.py @@ -0,0 +1,102 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +HOST_ARCH: Final = "host.arch" +""" +The CPU architecture the host system is running on. +""" + +HOST_CPU_CACHE_L2_SIZE: Final = "host.cpu.cache.l2.size" +""" +The amount of level 2 memory cache available to the processor (in Bytes). +""" + +HOST_CPU_FAMILY: Final = "host.cpu.family" +""" +Family or generation of the CPU. +""" + +HOST_CPU_MODEL_ID: Final = "host.cpu.model.id" +""" +Model identifier. It provides more granular information about the CPU, distinguishing it from other CPUs within the same family. +""" + +HOST_CPU_MODEL_NAME: Final = "host.cpu.model.name" +""" +Model designation of the processor. +""" + +HOST_CPU_STEPPING: Final = "host.cpu.stepping" +""" +Stepping or core revisions. +""" + +HOST_CPU_VENDOR_ID: Final = "host.cpu.vendor.id" +""" +Processor manufacturer identifier. A maximum 12-character string. +Note: [CPUID](https://wiki.osdev.org/CPUID) command returns the vendor ID string in EBX, EDX and ECX registers. Writing these to memory in this order results in a 12-character string. +""" + +HOST_ID: Final = "host.id" +""" +Unique host ID. For Cloud, this must be the instance_id assigned by the cloud provider. For non-containerized systems, this should be the `machine-id`. See the table below for the sources to use to determine the `machine-id` based on operating system. +""" + +HOST_IMAGE_ID: Final = "host.image.id" +""" +VM image ID or host OS image ID. For Cloud, this value is from the provider. +""" + +HOST_IMAGE_NAME: Final = "host.image.name" +""" +Name of the VM image or OS install the host was instantiated from. +""" + +HOST_IMAGE_VERSION: Final = "host.image.version" +""" +The version string of the VM image or host OS as defined in [Version Attributes](/docs/resource/README.md#version-attributes). +""" + +HOST_IP: Final = "host.ip" +""" +Available IP addresses of the host, excluding loopback interfaces. +Note: IPv4 Addresses MUST be specified in dotted-quad notation. IPv6 addresses MUST be specified in the [RFC 5952](https://www.rfc-editor.org/rfc/rfc5952.html) format. +""" + +HOST_MAC: Final = "host.mac" +""" +Available MAC addresses of the host, excluding loopback interfaces. +Note: MAC Addresses MUST be represented in [IEEE RA hexadecimal form](https://standards.ieee.org/wp-content/uploads/import/documents/tutorials/eui.pdf): as hyphen-separated octets in uppercase hexadecimal form from most to least significant. +""" + +HOST_NAME: Final = "host.name" +""" +Name of the host. On UNIX systems, it may contain what the hostname command returns, or the fully qualified hostname, or another name specified by the user. +""" + +HOST_TYPE: Final = "host.type" +""" +Type of host. For Cloud, this must be the machine type. +""" + + +class HostArchValues(Enum): + AMD64 = "amd64" + """AMD64.""" + ARM32 = "arm32" + """ARM32.""" + ARM64 = "arm64" + """ARM64.""" + IA64 = "ia64" + """Itanium.""" + PPC32 = "ppc32" + """32-bit PowerPC.""" + PPC64 = "ppc64" + """64-bit PowerPC.""" + S390X = "s390x" + """IBM z/Architecture.""" + X86 = "x86" + """32-bit x86.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/http_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/http_attributes.py new file mode 100644 index 0000000..f9f77d1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/http_attributes.py @@ -0,0 +1,190 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +HTTP_CLIENT_IP: Final = "http.client_ip" +""" +Deprecated: Replaced by `client.address`. +""" + +HTTP_CONNECTION_STATE: Final = "http.connection.state" +""" +State of the HTTP connection in the HTTP connection pool. +""" + +HTTP_FLAVOR: Final = "http.flavor" +""" +Deprecated: Split into `network.protocol.name` and `network.protocol.version`. +""" + +HTTP_HOST: Final = "http.host" +""" +Deprecated: Replaced by one of `server.address`, `client.address` or `http.request.header.host`, depending on the usage. +""" + +HTTP_METHOD: Final = "http.method" +""" +Deprecated: Replaced by `http.request.method`. +""" + +HTTP_REQUEST_BODY_SIZE: Final = "http.request.body.size" +""" +The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. +""" + +HTTP_REQUEST_HEADER_TEMPLATE: Final = "http.request.header" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HTTP_REQUEST_HEADER_TEMPLATE`. +""" + +HTTP_REQUEST_METHOD: Final = "http.request.method" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HTTP_REQUEST_METHOD`. +""" + +HTTP_REQUEST_METHOD_ORIGINAL: Final = "http.request.method_original" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HTTP_REQUEST_METHOD_ORIGINAL`. +""" + +HTTP_REQUEST_RESEND_COUNT: Final = "http.request.resend_count" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HTTP_REQUEST_RESEND_COUNT`. +""" + +HTTP_REQUEST_SIZE: Final = "http.request.size" +""" +The total size of the request in bytes. This should be the total number of bytes sent over the wire, including the request line (HTTP/1.1), framing (HTTP/2 and HTTP/3), headers, and request body if any. +""" + +HTTP_REQUEST_CONTENT_LENGTH: Final = "http.request_content_length" +""" +Deprecated: Replaced by `http.request.header.content-length`. +""" + +HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED: Final = "http.request_content_length_uncompressed" +""" +Deprecated: Replaced by `http.request.body.size`. +""" + +HTTP_RESPONSE_BODY_SIZE: Final = "http.response.body.size" +""" +The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. +""" + +HTTP_RESPONSE_HEADER_TEMPLATE: Final = "http.response.header" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HTTP_RESPONSE_HEADER_TEMPLATE`. +""" + +HTTP_RESPONSE_SIZE: Final = "http.response.size" +""" +The total size of the response in bytes. This should be the total number of bytes sent over the wire, including the status line (HTTP/1.1), framing (HTTP/2 and HTTP/3), headers, and response body and trailers if any. +""" + +HTTP_RESPONSE_STATUS_CODE: Final = "http.response.status_code" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HTTP_RESPONSE_STATUS_CODE`. +""" + +HTTP_RESPONSE_CONTENT_LENGTH: Final = "http.response_content_length" +""" +Deprecated: Replaced by `http.response.header.content-length`. +""" + +HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED: Final = "http.response_content_length_uncompressed" +""" +Deprecated: Replaced by `http.response.body.size`. +""" + +HTTP_ROUTE: Final = "http.route" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HTTP_ROUTE`. +""" + +HTTP_SCHEME: Final = "http.scheme" +""" +Deprecated: Replaced by `url.scheme`. +""" + +HTTP_SERVER_NAME: Final = "http.server_name" +""" +Deprecated: Replaced by `server.address`. +""" + +HTTP_STATUS_CODE: Final = "http.status_code" +""" +Deprecated: Replaced by `http.response.status_code`. +""" + +HTTP_TARGET: Final = "http.target" +""" +Deprecated: Split to `url.path` and `url.query`. +""" + +HTTP_URL: Final = "http.url" +""" +Deprecated: Replaced by `url.full`. +""" + +HTTP_USER_AGENT: Final = "http.user_agent" +""" +Deprecated: Replaced by `user_agent.original`. +""" + + +class HttpConnectionStateValues(Enum): + ACTIVE = "active" + """active state.""" + IDLE = "idle" + """idle state.""" + + +@deprecated( + "The attribute http.flavor is deprecated - Split into `network.protocol.name` and `network.protocol.version`" +) +class HttpFlavorValues(Enum): + HTTP_1_0 = "1.0" + """HTTP/1.0.""" + HTTP_1_1 = "1.1" + """HTTP/1.1.""" + HTTP_2_0 = "2.0" + """HTTP/2.""" + HTTP_3_0 = "3.0" + """HTTP/3.""" + SPDY = "SPDY" + """SPDY protocol.""" + QUIC = "QUIC" + """QUIC protocol.""" + + +@deprecated( + "Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HttpRequestMethodValues`." +) +class HttpRequestMethodValues(Enum): + CONNECT = "CONNECT" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HttpRequestMethodValues.CONNECT`.""" + DELETE = "DELETE" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HttpRequestMethodValues.DELETE`.""" + GET = "GET" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HttpRequestMethodValues.GET`.""" + HEAD = "HEAD" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HttpRequestMethodValues.HEAD`.""" + OPTIONS = "OPTIONS" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HttpRequestMethodValues.OPTIONS`.""" + PATCH = "PATCH" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HttpRequestMethodValues.PATCH`.""" + POST = "POST" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HttpRequestMethodValues.POST`.""" + PUT = "PUT" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HttpRequestMethodValues.PUT`.""" + TRACE = "TRACE" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HttpRequestMethodValues.TRACE`.""" + QUERY = "QUERY" + """QUERY method.""" + OTHER = "_OTHER" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.http_attributes.HttpRequestMethodValues.OTHER`.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/hw_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/hw_attributes.py new file mode 100644 index 0000000..548e6fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/hw_attributes.py @@ -0,0 +1,243 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +HW_BATTERY_CAPACITY: Final = "hw.battery.capacity" +""" +Design capacity in Watts-hours or Ampere-hours. +""" + +HW_BATTERY_CHEMISTRY: Final = "hw.battery.chemistry" +""" +Battery [chemistry](https://schemas.dmtf.org/wbem/cim-html/2.31.0/CIM_Battery.html), e.g. Lithium-Ion, Nickel-Cadmium, etc. +""" + +HW_BATTERY_STATE: Final = "hw.battery.state" +""" +The current state of the battery. +""" + +HW_BIOS_VERSION: Final = "hw.bios_version" +""" +BIOS version of the hardware component. +""" + +HW_DRIVER_VERSION: Final = "hw.driver_version" +""" +Driver version for the hardware component. +""" + +HW_ENCLOSURE_TYPE: Final = "hw.enclosure.type" +""" +Type of the enclosure (useful for modular systems). +""" + +HW_FIRMWARE_VERSION: Final = "hw.firmware_version" +""" +Firmware version of the hardware component. +""" + +HW_GPU_TASK: Final = "hw.gpu.task" +""" +Type of task the GPU is performing. +""" + +HW_ID: Final = "hw.id" +""" +An identifier for the hardware component, unique within the monitored host. +""" + +HW_LIMIT_TYPE: Final = "hw.limit_type" +""" +Type of limit for hardware components. +""" + +HW_LOGICAL_DISK_RAID_LEVEL: Final = "hw.logical_disk.raid_level" +""" +RAID Level of the logical disk. +""" + +HW_LOGICAL_DISK_STATE: Final = "hw.logical_disk.state" +""" +State of the logical disk space usage. +""" + +HW_MEMORY_TYPE: Final = "hw.memory.type" +""" +Type of the memory module. +""" + +HW_MODEL: Final = "hw.model" +""" +Descriptive model name of the hardware component. +""" + +HW_NAME: Final = "hw.name" +""" +An easily-recognizable name for the hardware component. +""" + +HW_NETWORK_LOGICAL_ADDRESSES: Final = "hw.network.logical_addresses" +""" +Logical addresses of the adapter (e.g. IP address, or WWPN). +""" + +HW_NETWORK_PHYSICAL_ADDRESS: Final = "hw.network.physical_address" +""" +Physical address of the adapter (e.g. MAC address, or WWNN). +""" + +HW_PARENT: Final = "hw.parent" +""" +Unique identifier of the parent component (typically the `hw.id` attribute of the enclosure, or disk controller). +""" + +HW_PHYSICAL_DISK_SMART_ATTRIBUTE: Final = "hw.physical_disk.smart_attribute" +""" +[S.M.A.R.T.](https://wikipedia.org/wiki/S.M.A.R.T.) (Self-Monitoring, Analysis, and Reporting Technology) attribute of the physical disk. +""" + +HW_PHYSICAL_DISK_STATE: Final = "hw.physical_disk.state" +""" +State of the physical disk endurance utilization. +""" + +HW_PHYSICAL_DISK_TYPE: Final = "hw.physical_disk.type" +""" +Type of the physical disk. +""" + +HW_SENSOR_LOCATION: Final = "hw.sensor_location" +""" +Location of the sensor. +""" + +HW_SERIAL_NUMBER: Final = "hw.serial_number" +""" +Serial number of the hardware component. +""" + +HW_STATE: Final = "hw.state" +""" +The current state of the component. +""" + +HW_TAPE_DRIVE_OPERATION_TYPE: Final = "hw.tape_drive.operation_type" +""" +Type of tape drive operation. +""" + +HW_TYPE: Final = "hw.type" +""" +Type of the component. +Note: Describes the category of the hardware component for which `hw.state` is being reported. For example, `hw.type=temperature` along with `hw.state=degraded` would indicate that the temperature of the hardware component has been reported as `degraded`. +""" + +HW_VENDOR: Final = "hw.vendor" +""" +Vendor name of the hardware component. +""" + + +class HwBatteryStateValues(Enum): + CHARGING = "charging" + """Charging.""" + DISCHARGING = "discharging" + """Discharging.""" + + +class HwGpuTaskValues(Enum): + DECODER = "decoder" + """Decoder.""" + ENCODER = "encoder" + """Encoder.""" + GENERAL = "general" + """General.""" + + +class HwLimitTypeValues(Enum): + CRITICAL = "critical" + """Critical.""" + DEGRADED = "degraded" + """Degraded.""" + HIGH_CRITICAL = "high.critical" + """High Critical.""" + HIGH_DEGRADED = "high.degraded" + """High Degraded.""" + LOW_CRITICAL = "low.critical" + """Low Critical.""" + LOW_DEGRADED = "low.degraded" + """Low Degraded.""" + MAX = "max" + """Maximum.""" + THROTTLED = "throttled" + """Throttled.""" + TURBO = "turbo" + """Turbo.""" + + +class HwLogicalDiskStateValues(Enum): + USED = "used" + """Used.""" + FREE = "free" + """Free.""" + + +class HwPhysicalDiskStateValues(Enum): + REMAINING = "remaining" + """Remaining.""" + + +class HwStateValues(Enum): + DEGRADED = "degraded" + """Degraded.""" + FAILED = "failed" + """Failed.""" + NEEDS_CLEANING = "needs_cleaning" + """Needs Cleaning.""" + OK = "ok" + """OK.""" + PREDICTED_FAILURE = "predicted_failure" + """Predicted Failure.""" + + +class HwTapeDriveOperationTypeValues(Enum): + MOUNT = "mount" + """Mount.""" + UNMOUNT = "unmount" + """Unmount.""" + CLEAN = "clean" + """Clean.""" + + +class HwTypeValues(Enum): + BATTERY = "battery" + """Battery.""" + CPU = "cpu" + """CPU.""" + DISK_CONTROLLER = "disk_controller" + """Disk controller.""" + ENCLOSURE = "enclosure" + """Enclosure.""" + FAN = "fan" + """Fan.""" + GPU = "gpu" + """GPU.""" + LOGICAL_DISK = "logical_disk" + """Logical disk.""" + MEMORY = "memory" + """Memory.""" + NETWORK = "network" + """Network.""" + PHYSICAL_DISK = "physical_disk" + """Physical disk.""" + POWER_SUPPLY = "power_supply" + """Power supply.""" + TAPE_DRIVE = "tape_drive" + """Tape drive.""" + TEMPERATURE = "temperature" + """Temperature.""" + VOLTAGE = "voltage" + """Voltage.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/jsonrpc_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/jsonrpc_attributes.py new file mode 100644 index 0000000..fcea823 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/jsonrpc_attributes.py @@ -0,0 +1,16 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +JSONRPC_PROTOCOL_VERSION: Final = "jsonrpc.protocol.version" +""" +Protocol version, as specified in the `jsonrpc` property of the request and its corresponding response. +""" + +JSONRPC_REQUEST_ID: Final = "jsonrpc.request.id" +""" +A string representation of the `id` property of the request and its corresponding response. +Note: Under the [JSON-RPC specification](https://www.jsonrpc.org/specification), the `id` property may be a string, number, null, or omitted entirely. When omitted, the request is treated as a notification. Using `null` is not equivalent to omitting the `id`, but it is discouraged. +Instrumentations SHOULD NOT capture this attribute when the `id` is `null` or omitted. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/k8s_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/k8s_attributes.py new file mode 100644 index 0000000..0b2eed3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/k8s_attributes.py @@ -0,0 +1,721 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +K8S_CLUSTER_NAME: Final = "k8s.cluster.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_CLUSTER_NAME`. +""" + +K8S_CLUSTER_UID: Final = "k8s.cluster.uid" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_CLUSTER_UID`. +""" + +K8S_CONTAINER_EPHEMERAL_STORAGE_FS_TYPE: Final = "k8s.container.ephemeral_storage.fs_type" +""" +The type of file system component for ephemeral storage. +Note: Eviction decisions based on ephemeral-storage resource limits are made based on the total container usage. +""" + +K8S_CONTAINER_NAME: Final = "k8s.container.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_CONTAINER_NAME`. +""" + +K8S_CONTAINER_RESTART_COUNT: Final = "k8s.container.restart_count" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_CONTAINER_RESTART_COUNT`. +""" + +K8S_CONTAINER_STATUS_LAST_TERMINATED_REASON: Final = "k8s.container.status.last_terminated_reason" +""" +Last terminated reason of the Container. +""" + +K8S_CONTAINER_STATUS_REASON: Final = "k8s.container.status.reason" +""" +The reason for the container state. Corresponds to the `reason` field of the: [K8s ContainerStateWaiting](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatewaiting-v1-core) or [K8s ContainerStateTerminated](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstateterminated-v1-core). +""" + +K8S_CONTAINER_STATUS_STATE: Final = "k8s.container.status.state" +""" +The state of the container. [K8s ContainerState](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstate-v1-core). +""" + +K8S_CRONJOB_ANNOTATION_TEMPLATE: Final = "k8s.cronjob.annotation" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_CRONJOB_ANNOTATION_TEMPLATE`. +""" + +K8S_CRONJOB_LABEL_TEMPLATE: Final = "k8s.cronjob.label" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_CRONJOB_LABEL_TEMPLATE`. +""" + +K8S_CRONJOB_NAME: Final = "k8s.cronjob.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_CRONJOB_NAME`. +""" + +K8S_CRONJOB_UID: Final = "k8s.cronjob.uid" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_CRONJOB_UID`. +""" + +K8S_DAEMONSET_ANNOTATION_TEMPLATE: Final = "k8s.daemonset.annotation" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_DAEMONSET_ANNOTATION_TEMPLATE`. +""" + +K8S_DAEMONSET_LABEL_TEMPLATE: Final = "k8s.daemonset.label" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_DAEMONSET_LABEL_TEMPLATE`. +""" + +K8S_DAEMONSET_NAME: Final = "k8s.daemonset.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_DAEMONSET_NAME`. +""" + +K8S_DAEMONSET_UID: Final = "k8s.daemonset.uid" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_DAEMONSET_UID`. +""" + +K8S_DEPLOYMENT_ANNOTATION_TEMPLATE: Final = "k8s.deployment.annotation" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_DEPLOYMENT_ANNOTATION_TEMPLATE`. +""" + +K8S_DEPLOYMENT_LABEL_TEMPLATE: Final = "k8s.deployment.label" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_DEPLOYMENT_LABEL_TEMPLATE`. +""" + +K8S_DEPLOYMENT_NAME: Final = "k8s.deployment.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_DEPLOYMENT_NAME`. +""" + +K8S_DEPLOYMENT_UID: Final = "k8s.deployment.uid" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_DEPLOYMENT_UID`. +""" + +K8S_HPA_METRIC_TYPE: Final = "k8s.hpa.metric.type" +""" +The type of metric source for the horizontal pod autoscaler. +Note: This attribute reflects the `type` field of spec.metrics[] in the HPA. +""" + +K8S_HPA_NAME: Final = "k8s.hpa.name" +""" +The name of the horizontal pod autoscaler. +""" + +K8S_HPA_SCALETARGETREF_API_VERSION: Final = "k8s.hpa.scaletargetref.api_version" +""" +The API version of the target resource to scale for the HorizontalPodAutoscaler. +Note: This maps to the `apiVersion` field in the `scaleTargetRef` of the HPA spec. +""" + +K8S_HPA_SCALETARGETREF_KIND: Final = "k8s.hpa.scaletargetref.kind" +""" +The kind of the target resource to scale for the HorizontalPodAutoscaler. +Note: This maps to the `kind` field in the `scaleTargetRef` of the HPA spec. +""" + +K8S_HPA_SCALETARGETREF_NAME: Final = "k8s.hpa.scaletargetref.name" +""" +The name of the target resource to scale for the HorizontalPodAutoscaler. +Note: This maps to the `name` field in the `scaleTargetRef` of the HPA spec. +""" + +K8S_HPA_UID: Final = "k8s.hpa.uid" +""" +The UID of the horizontal pod autoscaler. +""" + +K8S_HUGEPAGE_SIZE: Final = "k8s.hugepage.size" +""" +The size (identifier) of the K8s huge page. +""" + +K8S_JOB_ANNOTATION_TEMPLATE: Final = "k8s.job.annotation" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_JOB_ANNOTATION_TEMPLATE`. +""" + +K8S_JOB_LABEL_TEMPLATE: Final = "k8s.job.label" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_JOB_LABEL_TEMPLATE`. +""" + +K8S_JOB_NAME: Final = "k8s.job.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_JOB_NAME`. +""" + +K8S_JOB_UID: Final = "k8s.job.uid" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_JOB_UID`. +""" + +K8S_NAMESPACE_ANNOTATION_TEMPLATE: Final = "k8s.namespace.annotation" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_NAMESPACE_ANNOTATION_TEMPLATE`. +""" + +K8S_NAMESPACE_LABEL_TEMPLATE: Final = "k8s.namespace.label" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_NAMESPACE_LABEL_TEMPLATE`. +""" + +K8S_NAMESPACE_NAME: Final = "k8s.namespace.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_NAMESPACE_NAME`. +""" + +K8S_NAMESPACE_PHASE: Final = "k8s.namespace.phase" +""" +The phase of the K8s namespace. +Note: This attribute aligns with the `phase` field of the +[K8s NamespaceStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#namespacestatus-v1-core). +""" + +K8S_NODE_ANNOTATION_TEMPLATE: Final = "k8s.node.annotation" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_NODE_ANNOTATION_TEMPLATE`. +""" + +K8S_NODE_CONDITION_STATUS: Final = "k8s.node.condition.status" +""" +The status of the condition, one of True, False, Unknown. +Note: This attribute aligns with the `status` field of the +[NodeCondition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#nodecondition-v1-core). +""" + +K8S_NODE_CONDITION_TYPE: Final = "k8s.node.condition.type" +""" +The condition type of a K8s Node. +Note: K8s Node conditions as described +by [K8s documentation](https://kubernetes.io/docs/reference/node/node-status/#condition). + +This attribute aligns with the `type` field of the +[NodeCondition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#nodecondition-v1-core) + +The set of possible values is not limited to those listed here. Managed Kubernetes environments, +or custom controllers MAY introduce additional node condition types. +When this occurs, the exact value as reported by the Kubernetes API SHOULD be used. +""" + +K8S_NODE_LABEL_TEMPLATE: Final = "k8s.node.label" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_NODE_LABEL_TEMPLATE`. +""" + +K8S_NODE_NAME: Final = "k8s.node.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_NODE_NAME`. +""" + +K8S_NODE_SYSTEM_CONTAINER_NAME: Final = "k8s.node.system_container.name" +""" +The name of the system container running on the K8s Node. +""" + +K8S_NODE_UID: Final = "k8s.node.uid" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_NODE_UID`. +""" + +K8S_PERSISTENTVOLUME_ANNOTATION_TEMPLATE: Final = "k8s.persistentvolume.annotation" +""" +The annotation placed on the PersistentVolume, the `` being the annotation name, the value being the annotation value, even if the value is empty. +Note: Examples: + +- An annotation `pv.kubernetes.io/provisioned-by` with value `kubernetes.io/aws-ebs` SHOULD be recorded as + the `k8s.persistentvolume.annotation.pv.kubernetes.io/provisioned-by` attribute with value `"kubernetes.io/aws-ebs"`. +- An annotation `data` with empty string value SHOULD be recorded as + the `k8s.persistentvolume.annotation.data` attribute with value `""`. +""" + +K8S_PERSISTENTVOLUME_LABEL_TEMPLATE: Final = "k8s.persistentvolume.label" +""" +The label placed on the PersistentVolume, the `` being the label name, the value being the label value, even if the value is empty. +Note: Examples: + +- A label `type` with value `ssd` SHOULD be recorded as + the `k8s.persistentvolume.label.type` attribute with value `"ssd"`. +- A label `data` with empty string value SHOULD be recorded as + the `k8s.persistentvolume.label.data` attribute with value `""`. +""" + +K8S_PERSISTENTVOLUME_NAME: Final = "k8s.persistentvolume.name" +""" +The name of the PersistentVolume. +""" + +K8S_PERSISTENTVOLUME_RECLAIM_POLICY: Final = "k8s.persistentvolume.reclaim_policy" +""" +The reclaim policy of the PersistentVolume. +Note: This attribute aligns with the `persistentVolumeReclaimPolicy` field of the +[K8s PersistentVolumeSpec](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-v1/#PersistentVolumeSpec). +""" + +K8S_PERSISTENTVOLUME_STATUS_PHASE: Final = "k8s.persistentvolume.status.phase" +""" +The phase of the PersistentVolume. +Note: This attribute aligns with the `phase` field of the +[K8s PersistentVolumeStatus](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-v1/#PersistentVolumeStatus). +""" + +K8S_PERSISTENTVOLUME_UID: Final = "k8s.persistentvolume.uid" +""" +The UID of the PersistentVolume. +""" + +K8S_PERSISTENTVOLUMECLAIM_ANNOTATION_TEMPLATE: Final = "k8s.persistentvolumeclaim.annotation" +""" +The annotation placed on the PersistentVolumeClaim, the `` being the annotation name, the value being the annotation value, even if the value is empty. +Note: Examples: + +- An annotation `volume.beta.kubernetes.io/storage-provisioner` with value `kubernetes.io/aws-ebs` SHOULD be recorded as + the `k8s.persistentvolumeclaim.annotation.volume.beta.kubernetes.io/storage-provisioner` attribute with value `"kubernetes.io/aws-ebs"`. +- An annotation `data` with empty string value SHOULD be recorded as + the `k8s.persistentvolumeclaim.annotation.data` attribute with value `""`. +""" + +K8S_PERSISTENTVOLUMECLAIM_LABEL_TEMPLATE: Final = "k8s.persistentvolumeclaim.label" +""" +The label placed on the PersistentVolumeClaim, the `` being the label name, the value being the label value, even if the value is empty. +Note: Examples: + +- A label `app` with value `my-app` SHOULD be recorded as + the `k8s.persistentvolumeclaim.label.app` attribute with value `"my-app"`. +- A label `data` with empty string value SHOULD be recorded as + the `k8s.persistentvolumeclaim.label.data` attribute with value `""`. +""" + +K8S_PERSISTENTVOLUMECLAIM_NAME: Final = "k8s.persistentvolumeclaim.name" +""" +The name of the PersistentVolumeClaim. +""" + +K8S_PERSISTENTVOLUMECLAIM_STATUS_PHASE: Final = "k8s.persistentvolumeclaim.status.phase" +""" +The phase of the PersistentVolumeClaim. +Note: This attribute aligns with the `phase` field of the +[K8s PersistentVolumeClaimStatus](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-claim-v1/#PersistentVolumeClaimStatus). +""" + +K8S_PERSISTENTVOLUMECLAIM_UID: Final = "k8s.persistentvolumeclaim.uid" +""" +The UID of the PersistentVolumeClaim. +""" + +K8S_POD_ANNOTATION_TEMPLATE: Final = "k8s.pod.annotation" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_POD_ANNOTATION_TEMPLATE`. +""" + +K8S_POD_HOSTNAME: Final = "k8s.pod.hostname" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_POD_HOSTNAME`. +""" + +K8S_POD_IP: Final = "k8s.pod.ip" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_POD_IP`. +""" + +K8S_POD_LABEL_TEMPLATE: Final = "k8s.pod.label" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_POD_LABEL_TEMPLATE`. +""" + +K8S_POD_LABELS_TEMPLATE: Final = "k8s.pod.labels" +""" +Deprecated: Replaced by `k8s.pod.label`. +""" + +K8S_POD_NAME: Final = "k8s.pod.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_POD_NAME`. +""" + +K8S_POD_START_TIME: Final = "k8s.pod.start_time" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_POD_START_TIME`. +""" + +K8S_POD_STATUS_PHASE: Final = "k8s.pod.status.phase" +""" +The phase for the pod. Corresponds to the `phase` field of the: [K8s PodStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.33/#podstatus-v1-core). +""" + +K8S_POD_STATUS_REASON: Final = "k8s.pod.status.reason" +""" +The reason for the pod state. Corresponds to the `reason` field of the: [K8s PodStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.33/#podstatus-v1-core). +""" + +K8S_POD_UID: Final = "k8s.pod.uid" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_POD_UID`. +""" + +K8S_REPLICASET_ANNOTATION_TEMPLATE: Final = "k8s.replicaset.annotation" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_REPLICASET_ANNOTATION_TEMPLATE`. +""" + +K8S_REPLICASET_LABEL_TEMPLATE: Final = "k8s.replicaset.label" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_REPLICASET_LABEL_TEMPLATE`. +""" + +K8S_REPLICASET_NAME: Final = "k8s.replicaset.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_REPLICASET_NAME`. +""" + +K8S_REPLICASET_UID: Final = "k8s.replicaset.uid" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_REPLICASET_UID`. +""" + +K8S_REPLICATIONCONTROLLER_NAME: Final = "k8s.replicationcontroller.name" +""" +The name of the replication controller. +""" + +K8S_REPLICATIONCONTROLLER_UID: Final = "k8s.replicationcontroller.uid" +""" +The UID of the replication controller. +""" + +K8S_RESOURCEQUOTA_NAME: Final = "k8s.resourcequota.name" +""" +The name of the resource quota. +""" + +K8S_RESOURCEQUOTA_RESOURCE_NAME: Final = "k8s.resourcequota.resource_name" +""" +The name of the K8s resource a resource quota defines. +Note: The value for this attribute can be either the full `count/[.]` string (e.g., count/deployments.apps, count/pods), or, for certain core Kubernetes resources, just the resource name (e.g., pods, services, configmaps). Both forms are supported by Kubernetes for object count quotas. See [Kubernetes Resource Quotas documentation](https://kubernetes.io/docs/concepts/policy/resource-quotas/#quota-on-object-count) for more details. +""" + +K8S_RESOURCEQUOTA_UID: Final = "k8s.resourcequota.uid" +""" +The UID of the resource quota. +""" + +K8S_SERVICE_ANNOTATION_TEMPLATE: Final = "k8s.service.annotation" +""" +The annotation placed on the Service, the `` being the annotation name, the value being the annotation value, even if the value is empty. +Note: Examples: + +- An annotation `prometheus.io/scrape` with value `true` SHOULD be recorded as + the `k8s.service.annotation.prometheus.io/scrape` attribute with value `"true"`. +- An annotation `data` with empty string value SHOULD be recorded as + the `k8s.service.annotation.data` attribute with value `""`. +""" + +K8S_SERVICE_ENDPOINT_ADDRESS_TYPE: Final = "k8s.service.endpoint.address_type" +""" +The address type of the service endpoint. +Note: The network address family or type of the endpoint. +This attribute aligns with the `addressType` field of the +[K8s EndpointSlice](https://kubernetes.io/docs/reference/kubernetes-api/service-resources/endpoint-slice-v1/). +It is used to differentiate metrics when a Service is backed by multiple address types +(e.g., in dual-stack clusters). +""" + +K8S_SERVICE_ENDPOINT_CONDITION: Final = "k8s.service.endpoint.condition" +""" +The condition of the service endpoint. +Note: The current operational condition of the service endpoint. +An endpoint can have multiple conditions set at once (e.g., both `serving` and `terminating` during rollout). +This attribute aligns with the condition fields in the [K8s EndpointSlice](https://kubernetes.io/docs/reference/kubernetes-api/service-resources/endpoint-slice-v1/). +""" + +K8S_SERVICE_ENDPOINT_ZONE: Final = "k8s.service.endpoint.zone" +""" +The zone of the service endpoint. +Note: The zone where the endpoint is located, typically corresponding to a failure domain. +This attribute aligns with the `zone` field of endpoints in the +[K8s EndpointSlice](https://kubernetes.io/docs/reference/kubernetes-api/service-resources/endpoint-slice-v1/). +It enables zone-aware monitoring of service endpoint distribution and supports +features like [Topology Aware Routing](https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/). + +If the zone is not populated (e.g., nodes without the `topology.kubernetes.io/zone` label), +the attribute value will be an empty string. +""" + +K8S_SERVICE_LABEL_TEMPLATE: Final = "k8s.service.label" +""" +The label placed on the Service, the `` being the label name, the value being the label value, even if the value is empty. +Note: Examples: + +- A label `app` with value `my-service` SHOULD be recorded as + the `k8s.service.label.app` attribute with value `"my-service"`. +- A label `data` with empty string value SHOULD be recorded as + the `k8s.service.label.data` attribute with value `""`. +""" + +K8S_SERVICE_NAME: Final = "k8s.service.name" +""" +The name of the Service. +""" + +K8S_SERVICE_PUBLISH_NOT_READY_ADDRESSES: Final = "k8s.service.publish_not_ready_addresses" +""" +Whether the Service publishes not-ready endpoints. +Note: Whether the Service is configured to publish endpoints before the pods are ready. +This attribute is typically used to indicate that a Service (such as a headless +Service for a StatefulSet) allows peer discovery before pods pass their readiness probes. +It aligns with the `publishNotReadyAddresses` field of the +[K8s ServiceSpec](https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/#ServiceSpec). +""" + +K8S_SERVICE_SELECTOR_TEMPLATE: Final = "k8s.service.selector" +""" +The selector key-value pair placed on the Service, the `` being the selector key, the value being the selector value. +Note: These selectors are used to correlate with pod labels. Each selector key-value pair becomes a separate attribute. + +Examples: + +- A selector `app=my-app` SHOULD be recorded as + the `k8s.service.selector.app` attribute with value `"my-app"`. +- A selector `version=v1` SHOULD be recorded as + the `k8s.service.selector.version` attribute with value `"v1"`. +""" + +K8S_SERVICE_TRAFFIC_DISTRIBUTION: Final = "k8s.service.traffic_distribution" +""" +The traffic distribution policy for the Service. +Note: Specifies how traffic is distributed to endpoints for this Service. +This attribute aligns with the `trafficDistribution` field of the +[K8s ServiceSpec](https://kubernetes.io/docs/reference/networking/virtual-ips/#traffic-distribution). +Known values include `PreferSameZone` (prefer endpoints in the same zone as the client) and +`PreferSameNode` (prefer endpoints on the same node, fallback to same zone, then cluster-wide). +If this field is not set on the Service, the attribute SHOULD NOT be emitted. +When not set, Kubernetes distributes traffic evenly across all endpoints cluster-wide. +""" + +K8S_SERVICE_TYPE: Final = "k8s.service.type" +""" +The type of the Kubernetes Service. +Note: This attribute aligns with the `type` field of the +[K8s ServiceSpec](https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/#ServiceSpec). +""" + +K8S_SERVICE_UID: Final = "k8s.service.uid" +""" +The UID of the Service. +""" + +K8S_STATEFULSET_ANNOTATION_TEMPLATE: Final = "k8s.statefulset.annotation" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_STATEFULSET_ANNOTATION_TEMPLATE`. +""" + +K8S_STATEFULSET_LABEL_TEMPLATE: Final = "k8s.statefulset.label" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_STATEFULSET_LABEL_TEMPLATE`. +""" + +K8S_STATEFULSET_NAME: Final = "k8s.statefulset.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_STATEFULSET_NAME`. +""" + +K8S_STATEFULSET_UID: Final = "k8s.statefulset.uid" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.k8s_attributes.K8S_STATEFULSET_UID`. +""" + +K8S_STORAGECLASS_NAME: Final = "k8s.storageclass.name" +""" +The name of K8s [StorageClass](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#storageclass-v1-storage-k8s-io) object. +""" + +K8S_VOLUME_NAME: Final = "k8s.volume.name" +""" +The name of the K8s volume. +""" + +K8S_VOLUME_TYPE: Final = "k8s.volume.type" +""" +The type of the K8s volume. +""" + + +class K8sContainerEphemeralStorageFsTypeValues(Enum): + ROOTFS = "rootfs" + """For the container's writable layer usage.""" + LOGS = "logs" + """For the container's log files usage (stdout/stderr).""" + + +class K8sContainerStatusReasonValues(Enum): + CONTAINER_CREATING = "ContainerCreating" + """The container is being created.""" + CRASH_LOOP_BACK_OFF = "CrashLoopBackOff" + """The container is in a crash loop back off state.""" + CREATE_CONTAINER_CONFIG_ERROR = "CreateContainerConfigError" + """There was an error creating the container configuration.""" + ERR_IMAGE_PULL = "ErrImagePull" + """There was an error pulling the container image.""" + IMAGE_PULL_BACK_OFF = "ImagePullBackOff" + """The container image pull is in back off state.""" + OOM_KILLED = "OOMKilled" + """The container was killed due to out of memory.""" + COMPLETED = "Completed" + """The container has completed execution.""" + ERROR = "Error" + """There was an error with the container.""" + CONTAINER_CANNOT_RUN = "ContainerCannotRun" + """The container cannot run.""" + + +class K8sContainerStatusStateValues(Enum): + TERMINATED = "terminated" + """The container has terminated.""" + RUNNING = "running" + """The container is running.""" + WAITING = "waiting" + """The container is waiting.""" + + +class K8sNamespacePhaseValues(Enum): + ACTIVE = "active" + """Active namespace phase as described by [K8s API](https://pkg.go.dev/k8s.io/api@v0.31.3/core/v1#NamespacePhase).""" + TERMINATING = "terminating" + """Terminating namespace phase as described by [K8s API](https://pkg.go.dev/k8s.io/api@v0.31.3/core/v1#NamespacePhase).""" + + +class K8sNodeConditionStatusValues(Enum): + CONDITION_TRUE = "true" + """condition_true.""" + CONDITION_FALSE = "false" + """condition_false.""" + CONDITION_UNKNOWN = "unknown" + """condition_unknown.""" + + +class K8sNodeConditionTypeValues(Enum): + READY = "Ready" + """The node is healthy and ready to accept pods.""" + DISK_PRESSURE = "DiskPressure" + """Pressure exists on the disk size—that is, if the disk capacity is low.""" + MEMORY_PRESSURE = "MemoryPressure" + """Pressure exists on the node memory—that is, if the node memory is low.""" + PID_PRESSURE = "PIDPressure" + """Pressure exists on the processes—that is, if there are too many processes on the node.""" + NETWORK_UNAVAILABLE = "NetworkUnavailable" + """The network for the node is not correctly configured.""" + + +class K8sPersistentvolumeReclaimPolicyValues(Enum): + DELETE = "Delete" + """The volume will be deleted when released from its claim.""" + RECYCLE = "Recycle" + """The volume will be recycled (basic scrub) when released from its claim.""" + RETAIN = "Retain" + """The volume will be retained when released from its claim.""" + + +class K8sPersistentvolumeStatusPhaseValues(Enum): + AVAILABLE = "Available" + """The volume is available and not yet bound to a claim.""" + BOUND = "Bound" + """The volume is bound to a claim.""" + FAILED = "Failed" + """The volume has failed its automatic reclamation.""" + PENDING = "Pending" + """The volume is being provisioned.""" + RELEASED = "Released" + """The claim has been deleted but the volume is not yet available.""" + + +class K8sPersistentvolumeclaimStatusPhaseValues(Enum): + BOUND = "Bound" + """The claim is bound to a volume.""" + LOST = "Lost" + """The claim has lost its underlying volume (the volume does not exist anymore).""" + PENDING = "Pending" + """The claim has not yet been bound to a volume.""" + + +class K8sPodStatusPhaseValues(Enum): + PENDING = "Pending" + """The pod has been accepted by the system, but one or more of the containers has not been started. This includes time before being bound to a node, as well as time spent pulling images onto the host.""" + RUNNING = "Running" + """The pod has been bound to a node and all of the containers have been started. At least one container is still running or is in the process of being restarted.""" + SUCCEEDED = "Succeeded" + """All containers in the pod have voluntarily terminated with a container exit code of 0, and the system is not going to restart any of these containers.""" + FAILED = "Failed" + """All containers in the pod have terminated, and at least one container has terminated in a failure (exited with a non-zero exit code or was stopped by the system).""" + UNKNOWN = "Unknown" + """For some reason the state of the pod could not be obtained, typically due to an error in communicating with the host of the pod.""" + + +class K8sPodStatusReasonValues(Enum): + EVICTED = "Evicted" + """The pod is evicted.""" + NODE_AFFINITY = "NodeAffinity" + """The pod is in a status because of its node affinity.""" + NODE_LOST = "NodeLost" + """The reason on a pod when its state cannot be confirmed as kubelet is unresponsive on the node it is (was) running.""" + SHUTDOWN = "Shutdown" + """The node is shutdown.""" + UNEXPECTED_ADMISSION_ERROR = "UnexpectedAdmissionError" + """The pod was rejected admission to the node because of an error during admission that could not be categorized.""" + + +class K8sServiceEndpointAddressTypeValues(Enum): + IPV4 = "IPv4" + """IPv4 address type.""" + IPV6 = "IPv6" + """IPv6 address type.""" + FQDN = "FQDN" + """FQDN address type.""" + + +class K8sServiceEndpointConditionValues(Enum): + READY = "ready" + """The endpoint is ready to receive new connections.""" + SERVING = "serving" + """The endpoint is currently handling traffic.""" + TERMINATING = "terminating" + """The endpoint is in the process of shutting down.""" + + +class K8sServiceTypeValues(Enum): + CLUSTER_IP = "ClusterIP" + """ClusterIP service type.""" + NODE_PORT = "NodePort" + """NodePort service type.""" + LOAD_BALANCER = "LoadBalancer" + """LoadBalancer service type.""" + EXTERNAL_NAME = "ExternalName" + """ExternalName service type.""" + + +class K8sVolumeTypeValues(Enum): + PERSISTENT_VOLUME_CLAIM = "persistentVolumeClaim" + """A [persistentVolumeClaim](https://kubernetes.io/docs/concepts/storage/volumes/#persistentvolumeclaim) volume.""" + CONFIG_MAP = "configMap" + """A [configMap](https://kubernetes.io/docs/concepts/storage/volumes/#configmap) volume.""" + DOWNWARD_API = "downwardAPI" + """A [downwardAPI](https://kubernetes.io/docs/concepts/storage/volumes/#downwardapi) volume.""" + EMPTY_DIR = "emptyDir" + """An [emptyDir](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir) volume.""" + SECRET = "secret" + """A [secret](https://kubernetes.io/docs/concepts/storage/volumes/#secret) volume.""" + LOCAL = "local" + """A [local](https://kubernetes.io/docs/concepts/storage/volumes/#local) volume.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/linux_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/linux_attributes.py new file mode 100644 index 0000000..6a8ffb4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/linux_attributes.py @@ -0,0 +1,20 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +LINUX_MEMORY_SLAB_STATE: Final = "linux.memory.slab.state" +""" +Deprecated: Replaced by `system.memory.linux.slab.state`. +""" + + +@deprecated("The attribute linux.memory.slab.state is deprecated - Replaced by `system.memory.linux.slab.state`") +class LinuxMemorySlabStateValues(Enum): + RECLAIMABLE = "reclaimable" + """reclaimable.""" + UNRECLAIMABLE = "unreclaimable" + """unreclaimable.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/log_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/log_attributes.py new file mode 100644 index 0000000..3026407 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/log_attributes.py @@ -0,0 +1,50 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +LOG_FILE_NAME: Final = "log.file.name" +""" +The basename of the file. +""" + +LOG_FILE_NAME_RESOLVED: Final = "log.file.name_resolved" +""" +The basename of the file, with symlinks resolved. +""" + +LOG_FILE_PATH: Final = "log.file.path" +""" +The full path to the file. +""" + +LOG_FILE_PATH_RESOLVED: Final = "log.file.path_resolved" +""" +The full path to the file, with symlinks resolved. +""" + +LOG_IOSTREAM: Final = "log.iostream" +""" +The stream associated with the log. See below for a list of well-known values. +""" + +LOG_RECORD_ORIGINAL: Final = "log.record.original" +""" +The complete original Log Record. +Note: This value MAY be added when processing a Log Record which was originally transmitted as a string or equivalent data type AND the Body field of the Log Record does not contain the same value. (e.g. a syslog or a log record read from a file.). +""" + +LOG_RECORD_UID: Final = "log.record.uid" +""" +A unique identifier for the Log Record. +Note: If an ID is provided, other log records with the same ID will be considered duplicates and can be removed safely. This means, that two distinguishable log records MUST have different values. +The ID MAY be an [Universally Unique Lexicographically Sortable Identifier (ULID)](https://github.com/ulid/spec), but other identifiers (e.g. UUID) may be used as needed. +""" + + +class LogIostreamValues(Enum): + STDOUT = "stdout" + """Logs from stdout stream.""" + STDERR = "stderr" + """Events from stderr stream.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/mainframe_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/mainframe_attributes.py new file mode 100644 index 0000000..bfa8d19 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/mainframe_attributes.py @@ -0,0 +1,9 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +MAINFRAME_LPAR_NAME: Final = "mainframe.lpar.name" +""" +Name of the logical partition that hosts a systems with a mainframe operating system. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/mcp_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/mcp_attributes.py new file mode 100644 index 0000000..4f48aff --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/mcp_attributes.py @@ -0,0 +1,83 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +MCP_METHOD_NAME: Final = "mcp.method.name" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +MCP_PROTOCOL_VERSION: Final = "mcp.protocol.version" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +MCP_RESOURCE_URI: Final = "mcp.resource.uri" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +MCP_SESSION_ID: Final = "mcp.session.id" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +@deprecated( + "The attribute mcp.method.name is deprecated - Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai)" +) +class McpMethodNameValues(Enum): + NOTIFICATIONS_CANCELLED = "notifications/cancelled" + """Notification cancelling a previously-issued request.""" + INITIALIZE = "initialize" + """Request to initialize the MCP client.""" + NOTIFICATIONS_INITIALIZED = "notifications/initialized" + """Notification indicating that the MCP client has been initialized.""" + NOTIFICATIONS_PROGRESS = "notifications/progress" + """Notification indicating the progress for a long-running operation.""" + PING = "ping" + """Request to check that the other party is still alive.""" + RESOURCES_LIST = "resources/list" + """Request to list resources available on server.""" + RESOURCES_TEMPLATES_LIST = "resources/templates/list" + """Request to list resource templates available on server.""" + RESOURCES_READ = "resources/read" + """Request to read a resource.""" + NOTIFICATIONS_RESOURCES_LIST_CHANGED = "notifications/resources/list_changed" + """Notification indicating that the list of resources has changed.""" + RESOURCES_SUBSCRIBE = "resources/subscribe" + """Request to subscribe to a resource.""" + RESOURCES_UNSUBSCRIBE = "resources/unsubscribe" + """Request to unsubscribe from resource updates.""" + NOTIFICATIONS_RESOURCES_UPDATED = "notifications/resources/updated" + """Notification indicating that a resource has been updated.""" + PROMPTS_LIST = "prompts/list" + """Request to list prompts available on server.""" + PROMPTS_GET = "prompts/get" + """Request to get a prompt.""" + NOTIFICATIONS_PROMPTS_LIST_CHANGED = "notifications/prompts/list_changed" + """Notification indicating that the list of prompts has changed.""" + TOOLS_LIST = "tools/list" + """Request to list tools available on server.""" + TOOLS_CALL = "tools/call" + """Request to call a tool.""" + NOTIFICATIONS_TOOLS_LIST_CHANGED = "notifications/tools/list_changed" + """Notification indicating that the list of tools has changed.""" + LOGGING_SET_LEVEL = "logging/setLevel" + """Request to set the logging level.""" + NOTIFICATIONS_MESSAGE = "notifications/message" + """Notification indicating that a message has been received.""" + SAMPLING_CREATE_MESSAGE = "sampling/createMessage" + """Request to create a sampling message.""" + COMPLETION_COMPLETE = "completion/complete" + """Request to complete a prompt.""" + ROOTS_LIST = "roots/list" + """Request to list roots available on server.""" + NOTIFICATIONS_ROOTS_LIST_CHANGED = "notifications/roots/list_changed" + """Notification indicating that the list of roots has changed.""" + ELICITATION_CREATE = "elicitation/create" + """Request from the server to elicit additional information from the user via the client.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/message_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/message_attributes.py new file mode 100644 index 0000000..07f9f3e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/message_attributes.py @@ -0,0 +1,35 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +MESSAGE_COMPRESSED_SIZE: Final = "message.compressed_size" +""" +Deprecated: Deprecated, no replacement at this time. +""" + +MESSAGE_ID: Final = "message.id" +""" +Deprecated: Deprecated, no replacement at this time. +""" + +MESSAGE_TYPE: Final = "message.type" +""" +Deprecated: Deprecated, no replacement at this time. +""" + +MESSAGE_UNCOMPRESSED_SIZE: Final = "message.uncompressed_size" +""" +Deprecated: Deprecated, no replacement at this time. +""" + + +@deprecated("The attribute message.type is deprecated - Deprecated, no replacement at this time") +class MessageTypeValues(Enum): + SENT = "SENT" + """sent.""" + RECEIVED = "RECEIVED" + """received.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/messaging_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/messaging_attributes.py new file mode 100644 index 0000000..9f462f3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/messaging_attributes.py @@ -0,0 +1,327 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +MESSAGING_BATCH_MESSAGE_COUNT: Final = "messaging.batch.message_count" +""" +The number of messages sent, received, or processed in the scope of the batching operation. +Note: Instrumentations SHOULD NOT set `messaging.batch.message_count` on spans that operate with a single message. When a messaging client library supports both batch and single-message API for the same operation, instrumentations SHOULD use `messaging.batch.message_count` for batching APIs and SHOULD NOT use it for single-message APIs. +""" + +MESSAGING_CLIENT_ID: Final = "messaging.client.id" +""" +A unique identifier for the client that consumes or produces a message. +""" + +MESSAGING_CONSUMER_GROUP_NAME: Final = "messaging.consumer.group.name" +""" +The name of the consumer group with which a consumer is associated. +Note: Semantic conventions for individual messaging systems SHOULD document whether `messaging.consumer.group.name` is applicable and what it means in the context of that system. +""" + +MESSAGING_DESTINATION_ANONYMOUS: Final = "messaging.destination.anonymous" +""" +A boolean that is true if the message destination is anonymous (could be unnamed or have auto-generated name). +""" + +MESSAGING_DESTINATION_NAME: Final = "messaging.destination.name" +""" +The message destination name. +Note: SHOULD uniquely identify a specific queue, topic or other entity within the broker. If +the broker doesn't have such notion, it SHOULD uniquely identify the broker. +""" + +MESSAGING_DESTINATION_PARTITION_ID: Final = "messaging.destination.partition.id" +""" +The identifier of the partition messages are sent to or received from, unique within the `messaging.destination.name`. +""" + +MESSAGING_DESTINATION_SUBSCRIPTION_NAME: Final = "messaging.destination.subscription.name" +""" +The name of the destination subscription from which a message is consumed. +Note: Semantic conventions for individual messaging systems SHOULD document whether `messaging.destination.subscription.name` is applicable and what it means in the context of that system. +""" + +MESSAGING_DESTINATION_TEMPLATE: Final = "messaging.destination.template" +""" +Low cardinality representation of the messaging destination name. +Note: Destination names could be constructed from templates. An example would be a destination name involving a username or product ID. Although the destination name in this case is of high cardinality, the underlying template is of low cardinality and can be effectively used for grouping and aggregation. +""" + +MESSAGING_DESTINATION_TEMPORARY: Final = "messaging.destination.temporary" +""" +A boolean that is true if the message destination is temporary and might not exist anymore after messages are processed. +""" + +MESSAGING_DESTINATION_PUBLISH_ANONYMOUS: Final = "messaging.destination_publish.anonymous" +""" +Deprecated: Removed. No replacement at this time. +""" + +MESSAGING_DESTINATION_PUBLISH_NAME: Final = "messaging.destination_publish.name" +""" +Deprecated: Removed. No replacement at this time. +""" + +MESSAGING_EVENTHUBS_CONSUMER_GROUP: Final = "messaging.eventhubs.consumer.group" +""" +Deprecated: Replaced by `messaging.consumer.group.name`. +""" + +MESSAGING_EVENTHUBS_MESSAGE_ENQUEUED_TIME: Final = "messaging.eventhubs.message.enqueued_time" +""" +The UTC epoch seconds at which the message has been accepted and stored in the entity. +""" + +MESSAGING_GCP_PUBSUB_MESSAGE_ACK_DEADLINE: Final = "messaging.gcp_pubsub.message.ack_deadline" +""" +The ack deadline in seconds set for the modify ack deadline request. +""" + +MESSAGING_GCP_PUBSUB_MESSAGE_ACK_ID: Final = "messaging.gcp_pubsub.message.ack_id" +""" +The ack ID for a given message. +""" + +MESSAGING_GCP_PUBSUB_MESSAGE_DELIVERY_ATTEMPT: Final = "messaging.gcp_pubsub.message.delivery_attempt" +""" +The delivery attempt for a given message. +""" + +MESSAGING_GCP_PUBSUB_MESSAGE_ORDERING_KEY: Final = "messaging.gcp_pubsub.message.ordering_key" +""" +The ordering key for a given message. If the attribute is not present, the message does not have an ordering key. +""" + +MESSAGING_KAFKA_CLUSTER_ID: Final = "messaging.kafka.cluster.id" +""" +The Kafka cluster ID, obtained from the broker metadata exposed through the Kafka client (or AdminClient) API. +Note: The cluster ID is a unique identifier reported by the Kafka broker. It identifies the cluster independently of the individual brokers the client is configured to connect to, and remains stable even if broker hostnames, IP addresses, or ports change. +""" + +MESSAGING_KAFKA_CONSUMER_GROUP: Final = "messaging.kafka.consumer.group" +""" +Deprecated: Replaced by `messaging.consumer.group.name`. +""" + +MESSAGING_KAFKA_DESTINATION_PARTITION: Final = "messaging.kafka.destination.partition" +""" +Deprecated: Record string representation of the partition ID in `messaging.destination.partition.id` attribute. +""" + +MESSAGING_KAFKA_MESSAGE_KEY: Final = "messaging.kafka.message.key" +""" +Message keys in Kafka are used for grouping alike messages to ensure they're processed on the same partition. They differ from `messaging.message.id` in that they're not unique. If the key is `null`, the attribute MUST NOT be set. +Note: If the key type is not string, it's string representation has to be supplied for the attribute. If the key has no unambiguous, canonical string form, don't include its value. +""" + +MESSAGING_KAFKA_MESSAGE_OFFSET: Final = "messaging.kafka.message.offset" +""" +Deprecated: Replaced by `messaging.kafka.offset`. +""" + +MESSAGING_KAFKA_MESSAGE_TOMBSTONE: Final = "messaging.kafka.message.tombstone" +""" +A boolean that is true if the message is a tombstone. +""" + +MESSAGING_KAFKA_OFFSET: Final = "messaging.kafka.offset" +""" +The offset of a record in the corresponding Kafka partition. +""" + +MESSAGING_MESSAGE_BODY_SIZE: Final = "messaging.message.body.size" +""" +The size of the message body in bytes. +Note: This can refer to both the compressed or uncompressed body size. If both sizes are known, the uncompressed +body size should be used. +""" + +MESSAGING_MESSAGE_CONVERSATION_ID: Final = "messaging.message.conversation_id" +""" +The conversation ID identifying the conversation to which the message belongs, represented as a string. Sometimes called "Correlation ID". +""" + +MESSAGING_MESSAGE_ENVELOPE_SIZE: Final = "messaging.message.envelope.size" +""" +The size of the message body and metadata in bytes. +Note: This can refer to both the compressed or uncompressed size. If both sizes are known, the uncompressed +size should be used. +""" + +MESSAGING_MESSAGE_ID: Final = "messaging.message.id" +""" +A value used by the messaging system as an identifier for the message, represented as a string. +""" + +MESSAGING_OPERATION: Final = "messaging.operation" +""" +Deprecated: Replaced by `messaging.operation.type`. +""" + +MESSAGING_OPERATION_NAME: Final = "messaging.operation.name" +""" +The system-specific name of the messaging operation. +""" + +MESSAGING_OPERATION_TYPE: Final = "messaging.operation.type" +""" +A string identifying the type of the messaging operation. +Note: If a custom value is used, it MUST be of low cardinality. +""" + +MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY: Final = "messaging.rabbitmq.destination.routing_key" +""" +RabbitMQ message routing key. +""" + +MESSAGING_RABBITMQ_MESSAGE_DELIVERY_TAG: Final = "messaging.rabbitmq.message.delivery_tag" +""" +RabbitMQ message delivery tag. +""" + +MESSAGING_ROCKETMQ_CLIENT_GROUP: Final = "messaging.rocketmq.client_group" +""" +Deprecated: Replaced by `messaging.consumer.group.name` on the consumer spans. No replacement for producer spans. +""" + +MESSAGING_ROCKETMQ_CONSUMPTION_MODEL: Final = "messaging.rocketmq.consumption_model" +""" +Model of message consumption. This only applies to consumer spans. +""" + +MESSAGING_ROCKETMQ_MESSAGE_DELAY_TIME_LEVEL: Final = "messaging.rocketmq.message.delay_time_level" +""" +The delay time level for delay message, which determines the message delay time. +""" + +MESSAGING_ROCKETMQ_MESSAGE_DELIVERY_TIMESTAMP: Final = "messaging.rocketmq.message.delivery_timestamp" +""" +The timestamp in milliseconds that the delay message is expected to be delivered to consumer. +""" + +MESSAGING_ROCKETMQ_MESSAGE_GROUP: Final = "messaging.rocketmq.message.group" +""" +It is essential for FIFO message. Messages that belong to the same message group are always processed one by one within the same consumer group. +""" + +MESSAGING_ROCKETMQ_MESSAGE_KEYS: Final = "messaging.rocketmq.message.keys" +""" +Key(s) of message, another way to mark message besides message ID. +""" + +MESSAGING_ROCKETMQ_MESSAGE_TAG: Final = "messaging.rocketmq.message.tag" +""" +The secondary classifier of message besides topic. +""" + +MESSAGING_ROCKETMQ_MESSAGE_TYPE: Final = "messaging.rocketmq.message.type" +""" +Type of message. +""" + +MESSAGING_ROCKETMQ_NAMESPACE: Final = "messaging.rocketmq.namespace" +""" +Namespace of RocketMQ resources, resources in different namespaces are individual. +""" + +MESSAGING_SERVICEBUS_DESTINATION_SUBSCRIPTION_NAME: Final = "messaging.servicebus.destination.subscription_name" +""" +Deprecated: Replaced by `messaging.destination.subscription.name`. +""" + +MESSAGING_SERVICEBUS_DISPOSITION_STATUS: Final = "messaging.servicebus.disposition_status" +""" +Describes the [settlement type](https://learn.microsoft.com/azure/service-bus-messaging/message-transfers-locks-settlement#peeklock). +""" + +MESSAGING_SERVICEBUS_MESSAGE_DELIVERY_COUNT: Final = "messaging.servicebus.message.delivery_count" +""" +Number of deliveries that have been attempted for this message. +""" + +MESSAGING_SERVICEBUS_MESSAGE_ENQUEUED_TIME: Final = "messaging.servicebus.message.enqueued_time" +""" +The UTC epoch seconds at which the message has been accepted and stored in the entity. +""" + +MESSAGING_SYSTEM: Final = "messaging.system" +""" +The messaging system as identified by the client instrumentation. +Note: The actual messaging system may differ from the one known by the client. For example, when using Kafka client libraries to communicate with Azure Event Hubs, the `messaging.system` is set to `kafka` based on the instrumentation's best knowledge. +""" + + +class MessagingOperationTypeValues(Enum): + CREATE = "create" + """A message is created. "Create" spans always refer to a single message and are used to provide a unique creation context for messages in batch sending scenarios.""" + SEND = "send" + """One or more messages are provided for sending to an intermediary. If a single message is sent, the context of the "Send" span can be used as the creation context and no "Create" span needs to be created.""" + RECEIVE = "receive" + """One or more messages are requested by a consumer. This operation refers to pull-based scenarios, where consumers explicitly call methods of messaging SDKs to receive messages.""" + PROCESS = "process" + """One or more messages are processed by a consumer.""" + SETTLE = "settle" + """One or more messages are settled.""" + DELIVER = "deliver" + """Deprecated: Replaced by `process`.""" + PUBLISH = "publish" + """Deprecated: Replaced by `send`.""" + + +class MessagingRocketmqConsumptionModelValues(Enum): + CLUSTERING = "clustering" + """Clustering consumption model.""" + BROADCASTING = "broadcasting" + """Broadcasting consumption model.""" + + +class MessagingRocketmqMessageTypeValues(Enum): + NORMAL = "normal" + """Normal message.""" + FIFO = "fifo" + """FIFO message.""" + DELAY = "delay" + """Delay message.""" + TRANSACTION = "transaction" + """Transaction message.""" + + +class MessagingServicebusDispositionStatusValues(Enum): + COMPLETE = "complete" + """Message is completed.""" + ABANDON = "abandon" + """Message is abandoned.""" + DEAD_LETTER = "dead_letter" + """Message is sent to dead letter queue.""" + DEFER = "defer" + """Message is deferred.""" + + +class MessagingSystemValues(Enum): + ACTIVEMQ = "activemq" + """Apache ActiveMQ.""" + AWS_SNS = "aws.sns" + """Amazon Simple Notification Service (SNS).""" + AWS_SQS = "aws_sqs" + """Amazon Simple Queue Service (SQS).""" + EVENTGRID = "eventgrid" + """Azure Event Grid.""" + EVENTHUBS = "eventhubs" + """Azure Event Hubs.""" + SERVICEBUS = "servicebus" + """Azure Service Bus.""" + GCP_PUBSUB = "gcp_pubsub" + """Google Cloud Pub/Sub.""" + JMS = "jms" + """Java Message Service.""" + KAFKA = "kafka" + """Apache Kafka.""" + RABBITMQ = "rabbitmq" + """RabbitMQ.""" + ROCKETMQ = "rocketmq" + """Apache RocketMQ.""" + PULSAR = "pulsar" + """Apache Pulsar.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/net_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/net_attributes.py new file mode 100644 index 0000000..f5fa3be --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/net_attributes.py @@ -0,0 +1,106 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +NET_HOST_IP: Final = "net.host.ip" +""" +Deprecated: Replaced by `network.local.address`. +""" + +NET_HOST_NAME: Final = "net.host.name" +""" +Deprecated: Replaced by `server.address`. +""" + +NET_HOST_PORT: Final = "net.host.port" +""" +Deprecated: Replaced by `server.port`. +""" + +NET_PEER_IP: Final = "net.peer.ip" +""" +Deprecated: Replaced by `network.peer.address`. +""" + +NET_PEER_NAME: Final = "net.peer.name" +""" +Deprecated: Replaced by `server.address` on client spans and `client.address` on server spans. +""" + +NET_PEER_PORT: Final = "net.peer.port" +""" +Deprecated: Replaced by `server.port` on client spans and `client.port` on server spans. +""" + +NET_PROTOCOL_NAME: Final = "net.protocol.name" +""" +Deprecated: Replaced by `network.protocol.name`. +""" + +NET_PROTOCOL_VERSION: Final = "net.protocol.version" +""" +Deprecated: Replaced by `network.protocol.version`. +""" + +NET_SOCK_FAMILY: Final = "net.sock.family" +""" +Deprecated: Split to `network.transport` and `network.type`. +""" + +NET_SOCK_HOST_ADDR: Final = "net.sock.host.addr" +""" +Deprecated: Replaced by `network.local.address`. +""" + +NET_SOCK_HOST_PORT: Final = "net.sock.host.port" +""" +Deprecated: Replaced by `network.local.port`. +""" + +NET_SOCK_PEER_ADDR: Final = "net.sock.peer.addr" +""" +Deprecated: Replaced by `network.peer.address`. +""" + +NET_SOCK_PEER_NAME: Final = "net.sock.peer.name" +""" +Deprecated: Removed. No replacement at this time. +""" + +NET_SOCK_PEER_PORT: Final = "net.sock.peer.port" +""" +Deprecated: Replaced by `network.peer.port`. +""" + +NET_TRANSPORT: Final = "net.transport" +""" +Deprecated: Replaced by `network.transport`. +""" + + +@deprecated("The attribute net.sock.family is deprecated - Split to `network.transport` and `network.type`") +class NetSockFamilyValues(Enum): + INET = "inet" + """IPv4 address.""" + INET6 = "inet6" + """IPv6 address.""" + UNIX = "unix" + """UNIX domain socket path.""" + + +@deprecated("The attribute net.transport is deprecated - Replaced by `network.transport`") +class NetTransportValues(Enum): + IP_TCP = "ip_tcp" + """ip_tcp.""" + IP_UDP = "ip_udp" + """ip_udp.""" + PIPE = "pipe" + """Named or anonymous pipe.""" + INPROC = "inproc" + """In-process communication.""" + OTHER = "other" + """Something else (non IP-based).""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/network_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/network_attributes.py new file mode 100644 index 0000000..e9c09d1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/network_attributes.py @@ -0,0 +1,209 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +NETWORK_CARRIER_ICC: Final = "network.carrier.icc" +""" +The ISO 3166-1 alpha-2 2-character country code associated with the mobile carrier network. +""" + +NETWORK_CARRIER_MCC: Final = "network.carrier.mcc" +""" +The mobile carrier country code. +""" + +NETWORK_CARRIER_MNC: Final = "network.carrier.mnc" +""" +The mobile carrier network code. +""" + +NETWORK_CARRIER_NAME: Final = "network.carrier.name" +""" +The name of the mobile carrier. +""" + +NETWORK_CONNECTION_STATE: Final = "network.connection.state" +""" +The state of network connection. +Note: Connection states are defined as part of the [rfc9293](https://datatracker.ietf.org/doc/html/rfc9293#section-3.3.2). +""" + +NETWORK_CONNECTION_SUBTYPE: Final = "network.connection.subtype" +""" +This describes more details regarding the connection.type. It may be the type of cell technology connection, but it could be used for describing details about a Wi-Fi connection. +""" + +NETWORK_CONNECTION_TYPE: Final = "network.connection.type" +""" +The internet connection type. +""" + +NETWORK_INTERFACE_NAME: Final = "network.interface.name" +""" +The network interface name. +""" + +NETWORK_IO_DIRECTION: Final = "network.io.direction" +""" +The direction of traffic from the perspective of the observing host's physical or virtual network interface. It should not be used to represent the logical direction of a stateful connection or network flow. +""" + +NETWORK_LOCAL_ADDRESS: Final = "network.local.address" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NETWORK_LOCAL_ADDRESS`. +""" + +NETWORK_LOCAL_PORT: Final = "network.local.port" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NETWORK_LOCAL_PORT`. +""" + +NETWORK_PEER_ADDRESS: Final = "network.peer.address" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NETWORK_PEER_ADDRESS`. +""" + +NETWORK_PEER_PORT: Final = "network.peer.port" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NETWORK_PEER_PORT`. +""" + +NETWORK_PROTOCOL_NAME: Final = "network.protocol.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NETWORK_PROTOCOL_NAME`. +""" + +NETWORK_PROTOCOL_VERSION: Final = "network.protocol.version" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NETWORK_PROTOCOL_VERSION`. +""" + +NETWORK_TRANSPORT: Final = "network.transport" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NETWORK_TRANSPORT`. +""" + +NETWORK_TYPE: Final = "network.type" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NETWORK_TYPE`. +""" + + +class NetworkConnectionStateValues(Enum): + CLOSED = "closed" + """closed.""" + CLOSE_WAIT = "close_wait" + """close_wait.""" + CLOSING = "closing" + """closing.""" + ESTABLISHED = "established" + """established.""" + FIN_WAIT_1 = "fin_wait_1" + """fin_wait_1.""" + FIN_WAIT_2 = "fin_wait_2" + """fin_wait_2.""" + LAST_ACK = "last_ack" + """last_ack.""" + LISTEN = "listen" + """listen.""" + SYN_RECEIVED = "syn_received" + """syn_received.""" + SYN_SENT = "syn_sent" + """syn_sent.""" + TIME_WAIT = "time_wait" + """time_wait.""" + + +class NetworkConnectionSubtypeValues(Enum): + GPRS = "gprs" + """GPRS.""" + EDGE = "edge" + """EDGE.""" + UMTS = "umts" + """UMTS.""" + CDMA = "cdma" + """CDMA.""" + EVDO_0 = "evdo_0" + """EVDO Rel. 0.""" + EVDO_A = "evdo_a" + """EVDO Rev. A.""" + CDMA2000_1XRTT = "cdma2000_1xrtt" + """CDMA2000 1XRTT.""" + HSDPA = "hsdpa" + """HSDPA.""" + HSUPA = "hsupa" + """HSUPA.""" + HSPA = "hspa" + """HSPA.""" + IDEN = "iden" + """IDEN.""" + EVDO_B = "evdo_b" + """EVDO Rev. B.""" + LTE = "lte" + """LTE.""" + EHRPD = "ehrpd" + """EHRPD.""" + HSPAP = "hspap" + """HSPAP.""" + GSM = "gsm" + """GSM.""" + TD_SCDMA = "td_scdma" + """TD-SCDMA.""" + IWLAN = "iwlan" + """IWLAN.""" + NR = "nr" + """5G NR (New Radio).""" + NRNSA = "nrnsa" + """5G NRNSA (New Radio Non-Standalone).""" + LTE_CA = "lte_ca" + """LTE CA.""" + + +class NetworkConnectionTypeValues(Enum): + WIFI = "wifi" + """Wi-Fi.""" + WIRED = "wired" + """wired.""" + CELL = "cell" + """cell.""" + UNAVAILABLE = "unavailable" + """unavailable.""" + UNKNOWN = "unknown" + """unknown.""" + + +class NetworkIoDirectionValues(Enum): + TRANSMIT = "transmit" + """transmit.""" + RECEIVE = "receive" + """receive.""" + + +@deprecated( + "Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NetworkTransportValues`." +) +class NetworkTransportValues(Enum): + TCP = "tcp" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NetworkTransportValues.TCP`.""" + UDP = "udp" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NetworkTransportValues.UDP`.""" + PIPE = "pipe" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NetworkTransportValues.PIPE`.""" + UNIX = "unix" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NetworkTransportValues.UNIX`.""" + QUIC = "quic" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NetworkTransportValues.QUIC`.""" + + +@deprecated( + "Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NetworkTypeValues`." +) +class NetworkTypeValues(Enum): + IPV4 = "ipv4" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NetworkTypeValues.IPV4`.""" + IPV6 = "ipv6" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.network_attributes.NetworkTypeValues.IPV6`.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/nfs_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/nfs_attributes.py new file mode 100644 index 0000000..3ef1dd5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/nfs_attributes.py @@ -0,0 +1,14 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +NFS_OPERATION_NAME: Final = "nfs.operation.name" +""" +NFSv4+ operation name. +""" + +NFS_SERVER_REPCACHE_STATUS: Final = "nfs.server.repcache.status" +""" +Linux: one of "hit" (NFSD_STATS_RC_HITS), "miss" (NFSD_STATS_RC_MISSES), or "nocache" (NFSD_STATS_RC_NOCACHE -- uncacheable). +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/oci_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/oci_attributes.py new file mode 100644 index 0000000..c3342c0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/oci_attributes.py @@ -0,0 +1,11 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +OCI_MANIFEST_DIGEST: Final = "oci.manifest.digest" +""" +The digest of the OCI image manifest. For container images specifically is the digest by which the container image is known. +Note: Follows [OCI Image Manifest Specification](https://github.com/opencontainers/image-spec/blob/main/manifest.md), and specifically the [Digest property](https://github.com/opencontainers/image-spec/blob/main/descriptor.md#digests). +An example can be found in [Example Image Manifest](https://github.com/opencontainers/image-spec/blob/main/manifest.md#example-image-manifest). +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/onc_rpc_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/onc_rpc_attributes.py new file mode 100644 index 0000000..eecf84c --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/onc_rpc_attributes.py @@ -0,0 +1,24 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +ONC_RPC_PROCEDURE_NAME: Final = "onc_rpc.procedure.name" +""" +ONC/Sun RPC procedure name. +""" + +ONC_RPC_PROCEDURE_NUMBER: Final = "onc_rpc.procedure.number" +""" +ONC/Sun RPC procedure number. +""" + +ONC_RPC_PROGRAM_NAME: Final = "onc_rpc.program.name" +""" +ONC/Sun RPC program name. +""" + +ONC_RPC_VERSION: Final = "onc_rpc.version" +""" +ONC/Sun RPC program version. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/openai_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/openai_attributes.py new file mode 100644 index 0000000..5055820 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/openai_attributes.py @@ -0,0 +1,47 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +OPENAI_API_TYPE: Final = "openai.api.type" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +OPENAI_REQUEST_SERVICE_TIER: Final = "openai.request.service_tier" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +OPENAI_RESPONSE_SERVICE_TIER: Final = "openai.response.service_tier" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + +OPENAI_RESPONSE_SYSTEM_FINGERPRINT: Final = "openai.response.system_fingerprint" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +@deprecated( + "The attribute openai.api.type is deprecated - Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai)" +) +class OpenaiApiTypeValues(Enum): + CHAT_COMPLETIONS = "chat_completions" + """The OpenAI [Chat Completions API](https://developers.openai.com/api/reference/chat-completions/overview).""" + RESPONSES = "responses" + """The OpenAI [Responses API](https://developers.openai.com/api/reference/responses/overview).""" + + +@deprecated( + "The attribute openai.request.service_tier is deprecated - Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai)" +) +class OpenaiRequestServiceTierValues(Enum): + AUTO = "auto" + """The system will utilize scale tier credits until they are exhausted.""" + DEFAULT = "default" + """The system will utilize the default scale tier.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/openshift_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/openshift_attributes.py new file mode 100644 index 0000000..db1abf0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/openshift_attributes.py @@ -0,0 +1,14 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +OPENSHIFT_CLUSTERQUOTA_NAME: Final = "openshift.clusterquota.name" +""" +The name of the cluster quota. +""" + +OPENSHIFT_CLUSTERQUOTA_UID: Final = "openshift.clusterquota.uid" +""" +The UID of the cluster quota. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/opentracing_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/opentracing_attributes.py new file mode 100644 index 0000000..5237de9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/opentracing_attributes.py @@ -0,0 +1,18 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +OPENTRACING_REF_TYPE: Final = "opentracing.ref_type" +""" +Parent-child Reference type. +Note: The causal relationship between a child Span and a parent Span. +""" + + +class OpentracingRefTypeValues(Enum): + CHILD_OF = "child_of" + """The parent Span depends on the child Span in some capacity.""" + FOLLOWS_FROM = "follows_from" + """The parent Span doesn't depend in any way on the result of the child Span.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/oracle_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/oracle_attributes.py new file mode 100644 index 0000000..191be10 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/oracle_attributes.py @@ -0,0 +1,47 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +ORACLE_DB_DOMAIN: Final = "oracle.db.domain" +""" +The database domain associated with the connection. +Note: This attribute SHOULD be set to the value of the `DB_DOMAIN` initialization parameter, +as exposed in `v$parameter`. `DB_DOMAIN` defines the domain portion of the global +database name and SHOULD be configured when a database is, or may become, part of a +distributed environment. Its value consists of one or more valid identifiers +(alphanumeric ASCII characters) separated by periods. +""" + +ORACLE_DB_INSTANCE_NAME: Final = "oracle.db.instance.name" +""" +The instance name associated with the connection in an Oracle Real Application Clusters environment. +Note: There can be multiple instances associated with a single database service. It indicates the +unique instance name to which the connection is currently bound. For non-RAC databases, this value +defaults to the `oracle.db.name`. +""" + +ORACLE_DB_NAME: Final = "oracle.db.name" +""" +The database name associated with the connection. +Note: This attribute SHOULD be set to the value of the parameter `DB_NAME` exposed in `v$parameter`. +""" + +ORACLE_DB_PDB: Final = "oracle.db.pdb" +""" +The pluggable database (PDB) name associated with the connection. +Note: This attribute SHOULD reflect the PDB that the session is currently connected to. +If instrumentation cannot reliably obtain the active PDB name for each operation +without issuing an additional query (such as `SELECT SYS_CONTEXT`), it is +RECOMMENDED to fall back to the PDB name specified at connection establishment. +""" + +ORACLE_DB_SERVICE: Final = "oracle.db.service" +""" +The service name currently associated with the database connection. +Note: The effective service name for a connection can change during its lifetime, +for example after executing SQL, `ALTER SESSION`. If an instrumentation cannot reliably +obtain the current service name for each operation without issuing an additional +query (such as `SELECT SYS_CONTEXT`), it is RECOMMENDED to fall back to the +service name originally provided at connection establishment. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/oracle_cloud_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/oracle_cloud_attributes.py new file mode 100644 index 0000000..a829590 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/oracle_cloud_attributes.py @@ -0,0 +1,10 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +ORACLE_CLOUD_REALM: Final = "oracle_cloud.realm" +""" +The OCI realm identifier that indicates the isolated partition in which the tenancy and its resources reside. +Note: See [OCI documentation on realms](https://docs.oracle.com/iaas/Content/General/Concepts/regions.htm). +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/os_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/os_attributes.py new file mode 100644 index 0000000..b10d4bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/os_attributes.py @@ -0,0 +1,57 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +OS_BUILD_ID: Final = "os.build_id" +""" +Unique identifier for a particular build or compilation of the operating system. +""" + +OS_DESCRIPTION: Final = "os.description" +""" +Human readable (not intended to be parsed) OS version information, like e.g. reported by `ver` or `lsb_release -a` commands. +""" + +OS_NAME: Final = "os.name" +""" +Human readable operating system name. +""" + +OS_TYPE: Final = "os.type" +""" +The operating system type. +""" + +OS_VERSION: Final = "os.version" +""" +The version string of the operating system as defined in [Version Attributes](/docs/resource/README.md#version-attributes). +""" + + +class OsTypeValues(Enum): + WINDOWS = "windows" + """Microsoft Windows.""" + LINUX = "linux" + """Linux.""" + DARWIN = "darwin" + """Apple Darwin.""" + FREEBSD = "freebsd" + """FreeBSD.""" + NETBSD = "netbsd" + """NetBSD.""" + OPENBSD = "openbsd" + """OpenBSD.""" + DRAGONFLYBSD = "dragonflybsd" + """DragonFly BSD.""" + HPUX = "hpux" + """HP-UX (Hewlett Packard UNIX).""" + AIX = "aix" + """AIX (Advanced Interactive eXecutive).""" + SOLARIS = "solaris" + """SunOS, Oracle Solaris.""" + Z_OS = "z_os" + """Deprecated: Replaced by `zos`.""" + ZOS = "zos" + """IBM z/OS.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/otel_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/otel_attributes.py new file mode 100644 index 0000000..a0c31d2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/otel_attributes.py @@ -0,0 +1,145 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +OTEL_COMPONENT_NAME: Final = "otel.component.name" +""" +A name uniquely identifying the instance of the OpenTelemetry component within its containing SDK instance. +Note: Implementations SHOULD ensure a low cardinality for this attribute, even across application or SDK restarts. +E.g. implementations MUST NOT use UUIDs as values for this attribute. + +Implementations MAY achieve these goals by following a `/` pattern, e.g. `batching_span_processor/0`. +Hereby `otel.component.type` refers to the corresponding attribute value of the component. + +The value of `instance-counter` MAY be automatically assigned by the component and uniqueness within the enclosing SDK instance MUST be guaranteed. +For example, `` MAY be implemented by using a monotonically increasing counter (starting with `0`), which is incremented every time an +instance of the given component type is started. + +With this implementation, for example the first Batching Span Processor would have `batching_span_processor/0` +as `otel.component.name`, the second one `batching_span_processor/1` and so on. +These values will therefore be reused in the case of an application restart. +""" + +OTEL_COMPONENT_TYPE: Final = "otel.component.type" +""" +A name identifying the type of the OpenTelemetry component. +Note: If none of the standardized values apply, implementations SHOULD use the language-defined name of the type. +E.g. for Java the fully qualified classname SHOULD be used in this case. +""" + +OTEL_EVENT_NAME: Final = "otel.event.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.otel_attributes.OTEL_EVENT_NAME`. +""" + +OTEL_LIBRARY_NAME: Final = "otel.library.name" +""" +Deprecated: Replaced by `otel.scope.name`. +""" + +OTEL_LIBRARY_VERSION: Final = "otel.library.version" +""" +Deprecated: Replaced by `otel.scope.version`. +""" + +OTEL_SCOPE_NAME: Final = "otel.scope.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.otel_attributes.OTEL_SCOPE_NAME`. +""" + +OTEL_SCOPE_SCHEMA_URL: Final = "otel.scope.schema_url" +""" +The schema URL of the instrumentation scope. +""" + +OTEL_SCOPE_VERSION: Final = "otel.scope.version" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.otel_attributes.OTEL_SCOPE_VERSION`. +""" + +OTEL_SPAN_PARENT_ORIGIN: Final = "otel.span.parent.origin" +""" +Determines whether the span has a parent span, and if so, [whether it is a remote parent](https://opentelemetry.io/docs/specs/otel/trace/api/#isremote). +""" + +OTEL_SPAN_SAMPLING_RESULT: Final = "otel.span.sampling_result" +""" +The result value of the sampler for this span. +""" + +OTEL_STATUS_CODE: Final = "otel.status_code" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.otel_attributes.OTEL_STATUS_CODE`. +""" + +OTEL_STATUS_DESCRIPTION: Final = "otel.status_description" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.otel_attributes.OTEL_STATUS_DESCRIPTION`. +""" + + +class OtelComponentTypeValues(Enum): + BATCHING_SPAN_PROCESSOR = "batching_span_processor" + """The built-in SDK batching span processor.""" + SIMPLE_SPAN_PROCESSOR = "simple_span_processor" + """The built-in SDK simple span processor.""" + BATCHING_LOG_PROCESSOR = "batching_log_processor" + """The built-in SDK batching log record processor.""" + SIMPLE_LOG_PROCESSOR = "simple_log_processor" + """The built-in SDK simple log record processor.""" + OTLP_GRPC_SPAN_EXPORTER = "otlp_grpc_span_exporter" + """OTLP span exporter over gRPC with protobuf serialization.""" + OTLP_HTTP_SPAN_EXPORTER = "otlp_http_span_exporter" + """OTLP span exporter over HTTP with protobuf serialization.""" + OTLP_HTTP_JSON_SPAN_EXPORTER = "otlp_http_json_span_exporter" + """OTLP span exporter over HTTP with JSON serialization.""" + ZIPKIN_HTTP_SPAN_EXPORTER = "zipkin_http_span_exporter" + """Zipkin span exporter over HTTP.""" + OTLP_GRPC_LOG_EXPORTER = "otlp_grpc_log_exporter" + """OTLP log record exporter over gRPC with protobuf serialization.""" + OTLP_HTTP_LOG_EXPORTER = "otlp_http_log_exporter" + """OTLP log record exporter over HTTP with protobuf serialization.""" + OTLP_HTTP_JSON_LOG_EXPORTER = "otlp_http_json_log_exporter" + """OTLP log record exporter over HTTP with JSON serialization.""" + PERIODIC_METRIC_READER = "periodic_metric_reader" + """The built-in SDK periodically exporting metric reader.""" + OTLP_GRPC_METRIC_EXPORTER = "otlp_grpc_metric_exporter" + """OTLP metric exporter over gRPC with protobuf serialization.""" + OTLP_HTTP_METRIC_EXPORTER = "otlp_http_metric_exporter" + """OTLP metric exporter over HTTP with protobuf serialization.""" + OTLP_HTTP_JSON_METRIC_EXPORTER = "otlp_http_json_metric_exporter" + """OTLP metric exporter over HTTP with JSON serialization.""" + PROMETHEUS_HTTP_TEXT_METRIC_EXPORTER = "prometheus_http_text_metric_exporter" + """Prometheus metric exporter over HTTP with the default text-based format.""" + + +class OtelSpanParentOriginValues(Enum): + NONE = "none" + """The span does not have a parent, it is a root span.""" + LOCAL = "local" + """The span has a parent and the parent's span context [isRemote()](https://opentelemetry.io/docs/specs/otel/trace/api/#isremote) is false.""" + REMOTE = "remote" + """The span has a parent and the parent's span context [isRemote()](https://opentelemetry.io/docs/specs/otel/trace/api/#isremote) is true.""" + + +class OtelSpanSamplingResultValues(Enum): + DROP = "DROP" + """The span is not sampled and not recording.""" + RECORD_ONLY = "RECORD_ONLY" + """The span is not sampled, but recording.""" + RECORD_AND_SAMPLE = "RECORD_AND_SAMPLE" + """The span is sampled and recording.""" + + +@deprecated( + "Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.otel_attributes.OtelStatusCodeValues`." +) +class OtelStatusCodeValues(Enum): + OK = "OK" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.otel_attributes.OtelStatusCodeValues.OK`.""" + ERROR = "ERROR" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.otel_attributes.OtelStatusCodeValues.ERROR`.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/other_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/other_attributes.py new file mode 100644 index 0000000..ef9a5a9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/other_attributes.py @@ -0,0 +1,20 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +STATE: Final = "state" +""" +Deprecated: Replaced by `db.client.connection.state`. +""" + + +@deprecated("The attribute state is deprecated - Replaced by `db.client.connection.state`") +class StateValues(Enum): + IDLE = "idle" + """idle.""" + USED = "used" + """used.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/peer_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/peer_attributes.py new file mode 100644 index 0000000..27ff455 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/peer_attributes.py @@ -0,0 +1,9 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +PEER_SERVICE: Final = "peer.service" +""" +Deprecated: Replaced by `service.peer.name`. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/pool_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/pool_attributes.py new file mode 100644 index 0000000..cd3fa90 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/pool_attributes.py @@ -0,0 +1,9 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +POOL_NAME: Final = "pool.name" +""" +Deprecated: Replaced by `db.client.connection.pool.name`. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/pprof_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/pprof_attributes.py new file mode 100644 index 0000000..f99c8a3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/pprof_attributes.py @@ -0,0 +1,62 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +PPROF_LOCATION_IS_FOLDED: Final = "pprof.location.is_folded" +""" +Provides an indication that multiple symbols map to this location's address, for example due to identical code folding by the linker. In that case the line information represents one of the multiple symbols. This field must be recomputed when the symbolization state of the profile changes. +""" + +PPROF_MAPPING_HAS_FILENAMES: Final = "pprof.mapping.has_filenames" +""" +Indicates that there are filenames related to this mapping. +""" + +PPROF_MAPPING_HAS_FUNCTIONS: Final = "pprof.mapping.has_functions" +""" +Indicates that there are functions related to this mapping. +""" + +PPROF_MAPPING_HAS_INLINE_FRAMES: Final = "pprof.mapping.has_inline_frames" +""" +Indicates that there are inline frames related to this mapping. +""" + +PPROF_MAPPING_HAS_LINE_NUMBERS: Final = "pprof.mapping.has_line_numbers" +""" +Indicates that there are line numbers related to this mapping. +""" + +PPROF_PROFILE_COMMENT: Final = "pprof.profile.comment" +""" +Free-form text associated with the profile. This field should not be used to store any machine-readable information, it is only for human-friendly content. +""" + +PPROF_PROFILE_DOC_URL: Final = "pprof.profile.doc_url" +""" +Documentation link for this profile type. +Note: The URL must be absolute and may be missing if the profile was generated by code that did not supply a link. +""" + +PPROF_PROFILE_DROP_FRAMES: Final = "pprof.profile.drop_frames" +""" +Frames with Function.function_name fully matching the regular expression will be dropped from the samples, along with their successors. +""" + +PPROF_PROFILE_KEEP_FRAMES: Final = "pprof.profile.keep_frames" +""" +Frames with Function.function_name fully matching the regular expression will be kept, even if it matches drop_frames. +""" + +PPROF_SCOPE_DEFAULT_SAMPLE_TYPE: Final = "pprof.scope.default_sample_type" +""" +Records the pprof's default_sample_type in the original profile. Not set if the default sample type was missing. +Note: This attribute, if present, MUST be set at the scope level (resource_profiles[].scope_profiles[].scope.attributes[]). +""" + +PPROF_SCOPE_SAMPLE_TYPE_ORDER: Final = "pprof.scope.sample_type_order" +""" +Records the indexes of the sample types in the original profile. +Note: This attribute, if present, MUST be set at the scope level (resource_profiles[].scope_profiles[].scope.attributes[]). +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/process_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/process_attributes.py new file mode 100644 index 0000000..84d151c --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/process_attributes.py @@ -0,0 +1,253 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +PROCESS_ARGS_COUNT: Final = "process.args_count" +""" +Length of the process.command_args array. +Note: This field can be useful for querying or performing bucket analysis on how many arguments were provided to start a process. More arguments may be an indication of suspicious activity. +""" + +PROCESS_COMMAND: Final = "process.command" +""" +The command used to launch the process (i.e. the command name). On Linux based systems, can be set to the zeroth string in `proc/[pid]/cmdline`. On Windows, can be set to the first parameter extracted from `GetCommandLineW`. +""" + +PROCESS_COMMAND_ARGS: Final = "process.command_args" +""" +All the command arguments (including the command/executable itself) as received by the process. On Linux-based systems (and some other Unixoid systems supporting procfs), can be set according to the list of null-delimited strings extracted from `proc/[pid]/cmdline`. For libc-based executables, this would be the full argv vector passed to `main`. SHOULD NOT be collected by default unless there is sanitization that excludes sensitive data. +""" + +PROCESS_COMMAND_LINE: Final = "process.command_line" +""" +The full command used to launch the process as a single string representing the full command. On Windows, can be set to the result of `GetCommandLineW`. Do not set this if you have to assemble it just for monitoring; use `process.command_args` instead. SHOULD NOT be collected by default unless there is sanitization that excludes sensitive data. +""" + +PROCESS_CONTEXT_SWITCH_TYPE: Final = "process.context_switch.type" +""" +Specifies whether the context switches for this data point were voluntary or involuntary. +""" + +PROCESS_CPU_STATE: Final = "process.cpu.state" +""" +Deprecated: Replaced by `cpu.mode`. +""" + +PROCESS_CREATION_TIME: Final = "process.creation.time" +""" +The date and time the process was created, in ISO 8601 format. +""" + +PROCESS_ENVIRONMENT_VARIABLE_TEMPLATE: Final = "process.environment_variable" +""" +Process environment variables, `` being the environment variable name, the value being the environment variable value. +Note: Examples: + +- an environment variable `USER` with value `"ubuntu"` SHOULD be recorded +as the `process.environment_variable.USER` attribute with value `"ubuntu"`. + +- an environment variable `PATH` with value `"/usr/local/bin:/usr/bin"` +SHOULD be recorded as the `process.environment_variable.PATH` attribute +with value `"/usr/local/bin:/usr/bin"`. +""" + +PROCESS_EXECUTABLE_BUILD_ID_GNU: Final = "process.executable.build_id.gnu" +""" +The GNU build ID as found in the `.note.gnu.build-id` ELF section (hex string). +""" + +PROCESS_EXECUTABLE_BUILD_ID_GO: Final = "process.executable.build_id.go" +""" +The Go build ID as retrieved by `go tool buildid `. +""" + +PROCESS_EXECUTABLE_BUILD_ID_HTLHASH: Final = "process.executable.build_id.htlhash" +""" +Deterministic build ID for executables. +Note: GNU and Go build IDs may be stripped or unavailable in some environments +(e.g., Alpine Linux, Docker images). This attribute provides a deterministic +build ID computed by hashing the first and last 4096 bytes of the file +along with its length: + +``` +Input ← Concat(File[:4096], File[-4096:], BigEndianUInt64(Len(File))) +Digest ← SHA256(Input) +BuildID ← Digest[:16] +``` + +The result is the first 16 bytes (128 bits) of the SHA256 digest, +represented as a hex string. +""" + +PROCESS_EXECUTABLE_BUILD_ID_PROFILING: Final = "process.executable.build_id.profiling" +""" +Deprecated: Replaced by `process.executable.build_id.htlhash`. +""" + +PROCESS_EXECUTABLE_NAME: Final = "process.executable.name" +""" +The name of the process executable. On Linux based systems, this SHOULD be set to the base name of the target of `/proc/[pid]/exe`. On Windows, this SHOULD be set to the base name of `GetProcessImageFileNameW`. +""" + +PROCESS_EXECUTABLE_PATH: Final = "process.executable.path" +""" +The full path to the process executable. On Linux based systems, can be set to the target of `proc/[pid]/exe`. On Windows, can be set to the result of `GetProcessImageFileNameW`. +""" + +PROCESS_EXIT_CODE: Final = "process.exit.code" +""" +The exit code of the process. +""" + +PROCESS_EXIT_TIME: Final = "process.exit.time" +""" +The date and time the process exited, in ISO 8601 format. +""" + +PROCESS_GROUP_LEADER_PID: Final = "process.group_leader.pid" +""" +The PID of the process's group leader. This is also the process group ID (PGID) of the process. +""" + +PROCESS_INTERACTIVE: Final = "process.interactive" +""" +Whether the process is connected to an interactive shell. +""" + +PROCESS_LINUX_CGROUP: Final = "process.linux.cgroup" +""" +The control group associated with the process. +Note: Control groups (cgroups) are a kernel feature used to organize and manage process resources. This attribute provides the path(s) to the cgroup(s) associated with the process, which should match the contents of the [/proc/\\[PID\\]/cgroup](https://man7.org/linux/man-pages/man7/cgroups.7.html) file. +""" + +PROCESS_OWNER: Final = "process.owner" +""" +The username of the user that owns the process. +""" + +PROCESS_PAGING_FAULT_TYPE: Final = "process.paging.fault_type" +""" +Deprecated: Replaced by `system.paging.fault.type`. +""" + +PROCESS_PARENT_PID: Final = "process.parent_pid" +""" +Parent Process identifier (PPID). +""" + +PROCESS_PID: Final = "process.pid" +""" +Process identifier (PID). +""" + +PROCESS_REAL_USER_ID: Final = "process.real_user.id" +""" +The real user ID (RUID) of the process. +""" + +PROCESS_REAL_USER_NAME: Final = "process.real_user.name" +""" +The username of the real user of the process. +""" + +PROCESS_RUNTIME_DESCRIPTION: Final = "process.runtime.description" +""" +An additional description about the runtime of the process, for example a specific vendor customization of the runtime environment. +""" + +PROCESS_RUNTIME_NAME: Final = "process.runtime.name" +""" +The name of the runtime of this process. +""" + +PROCESS_RUNTIME_VERSION: Final = "process.runtime.version" +""" +The version of the runtime of this process, as returned by the runtime without modification. +""" + +PROCESS_SAVED_USER_ID: Final = "process.saved_user.id" +""" +The saved user ID (SUID) of the process. +""" + +PROCESS_SAVED_USER_NAME: Final = "process.saved_user.name" +""" +The username of the saved user. +""" + +PROCESS_SESSION_LEADER_PID: Final = "process.session_leader.pid" +""" +The PID of the process's session leader. This is also the session ID (SID) of the process. +""" + +PROCESS_STATE: Final = "process.state" +""" +The process state, e.g., [Linux Process State Codes](https://man7.org/linux/man-pages/man1/ps.1.html#PROCESS_STATE_CODES). +""" + +PROCESS_TITLE: Final = "process.title" +""" +Process title (proctitle). +Note: In many Unix-like systems, process title (proctitle), is the string that represents the name or command line of a running process, displayed by system monitoring tools like ps, top, and htop. +""" + +PROCESS_USER_ID: Final = "process.user.id" +""" +The effective user ID (EUID) of the process. +""" + +PROCESS_USER_NAME: Final = "process.user.name" +""" +The username of the effective user of the process. +""" + +PROCESS_VPID: Final = "process.vpid" +""" +Virtual process identifier. +Note: The process ID within a PID namespace. This is not necessarily unique across all processes on the host but it is unique within the process namespace that the process exists within. +""" + +PROCESS_WORKING_DIRECTORY: Final = "process.working_directory" +""" +The working directory of the process. +""" + + +class ProcessContextSwitchTypeValues(Enum): + VOLUNTARY = "voluntary" + """voluntary.""" + INVOLUNTARY = "involuntary" + """involuntary.""" + + +@deprecated("The attribute process.cpu.state is deprecated - Replaced by `cpu.mode`") +class ProcessCpuStateValues(Enum): + SYSTEM = "system" + """system.""" + USER = "user" + """user.""" + WAIT = "wait" + """wait.""" + + +@deprecated("The attribute process.paging.fault_type is deprecated - Replaced by `system.paging.fault.type`") +class ProcessPagingFaultTypeValues(Enum): + MAJOR = "major" + """major.""" + MINOR = "minor" + """minor.""" + + +class ProcessStateValues(Enum): + RUNNING = "running" + """running.""" + SLEEPING = "sleeping" + """sleeping.""" + STOPPED = "stopped" + """stopped.""" + DEFUNCT = "defunct" + """defunct.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/profile_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/profile_attributes.py new file mode 100644 index 0000000..7fd6de2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/profile_attributes.py @@ -0,0 +1,39 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +PROFILE_FRAME_TYPE: Final = "profile.frame.type" +""" +Describes the interpreter or compiler of a single frame. +""" + + +class ProfileFrameTypeValues(Enum): + DOTNET = "dotnet" + """[.NET](https://wikipedia.org/wiki/.NET).""" + JVM = "jvm" + """[JVM](https://wikipedia.org/wiki/Java_virtual_machine).""" + KERNEL = "kernel" + """[Kernel](https://wikipedia.org/wiki/Kernel_(operating_system)).""" + NATIVE = "native" + """Can be one of but not limited to [C](https://wikipedia.org/wiki/C_(programming_language)), [C++](https://wikipedia.org/wiki/C%2B%2B), [Go](https://wikipedia.org/wiki/Go_(programming_language)) or [Rust](https://wikipedia.org/wiki/Rust_(programming_language)). If possible, a more precise value MUST be used.""" + PERL = "perl" + """[Perl](https://wikipedia.org/wiki/Perl).""" + PHP = "php" + """[PHP](https://wikipedia.org/wiki/PHP).""" + CPYTHON = "cpython" + """[Python](https://wikipedia.org/wiki/Python_(programming_language)).""" + RUBY = "ruby" + """[Ruby](https://wikipedia.org/wiki/Ruby_(programming_language)).""" + V8JS = "v8js" + """[V8JS](https://wikipedia.org/wiki/V8_(JavaScript_engine)).""" + BEAM = "beam" + """[Erlang](https://en.wikipedia.org/wiki/BEAM_(Erlang_virtual_machine)).""" + GO = "go" + """[Go](https://wikipedia.org/wiki/Go_(programming_language)),.""" + RUST = "rust" + """[Rust](https://wikipedia.org/wiki/Rust_(programming_language)).""" + LUAJIT = "luajit" + """[LuaJIT](https://en.wikipedia.org/wiki/LuaJIT).""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/rpc_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/rpc_attributes.py new file mode 100644 index 0000000..f263182 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/rpc_attributes.py @@ -0,0 +1,264 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +RPC_CONNECT_RPC_ERROR_CODE: Final = "rpc.connect_rpc.error_code" +""" +Deprecated: Replaced by `rpc.response.status_code`. +""" + +RPC_CONNECT_RPC_REQUEST_METADATA_TEMPLATE: Final = "rpc.connect_rpc.request.metadata" +""" +Deprecated: Replaced by `rpc.request.metadata`. +""" + +RPC_CONNECT_RPC_RESPONSE_METADATA_TEMPLATE: Final = "rpc.connect_rpc.response.metadata" +""" +Deprecated: Replaced by `rpc.response.metadata`. +""" + +RPC_GRPC_REQUEST_METADATA_TEMPLATE: Final = "rpc.grpc.request.metadata" +""" +Deprecated: Replaced by `rpc.request.metadata`. +""" + +RPC_GRPC_RESPONSE_METADATA_TEMPLATE: Final = "rpc.grpc.response.metadata" +""" +Deprecated: Replaced by `rpc.response.metadata`. +""" + +RPC_GRPC_STATUS_CODE: Final = "rpc.grpc.status_code" +""" +Deprecated: Use string representation of the gRPC status code on the `rpc.response.status_code` attribute. +""" + +RPC_JSONRPC_ERROR_CODE: Final = "rpc.jsonrpc.error_code" +""" +Deprecated: Use string representation of the error code on the `rpc.response.status_code` attribute. +""" + +RPC_JSONRPC_ERROR_MESSAGE: Final = "rpc.jsonrpc.error_message" +""" +Deprecated: Use the span status description when reporting JSON-RPC spans. +""" + +RPC_JSONRPC_REQUEST_ID: Final = "rpc.jsonrpc.request_id" +""" +Deprecated: Replaced by `jsonrpc.request.id`. +""" + +RPC_JSONRPC_VERSION: Final = "rpc.jsonrpc.version" +""" +Deprecated: Replaced by `jsonrpc.protocol.version`. +""" + +RPC_MESSAGE_COMPRESSED_SIZE: Final = "rpc.message.compressed_size" +""" +Deprecated: Deprecated, no replacement at this time. +""" + +RPC_MESSAGE_ID: Final = "rpc.message.id" +""" +Deprecated: Deprecated, no replacement at this time. +""" + +RPC_MESSAGE_TYPE: Final = "rpc.message.type" +""" +Deprecated: Deprecated, no replacement at this time. +""" + +RPC_MESSAGE_UNCOMPRESSED_SIZE: Final = "rpc.message.uncompressed_size" +""" +Deprecated: Deprecated, no replacement at this time. +""" + +RPC_METHOD: Final = "rpc.method" +""" +The fully-qualified logical name of the method from the RPC interface perspective. +Note: The method name MAY have unbounded cardinality in edge or error cases. + +Some RPC frameworks or libraries provide a fixed set of recognized methods +for client stubs and server implementations. Instrumentations for such +frameworks MUST set this attribute to the original method name only +when the method is recognized by the framework or library. + +When the method is not recognized, for example, when the server receives +a request for a method that is not predefined on the server, or when +instrumentation is not able to reliably detect if the method is predefined, +the attribute MUST be set to `_OTHER`. + +If the RPC instrumentation could end up converting valid RPC methods to +`_OTHER`, then it SHOULD provide a way to configure the list of recognized +RPC methods. + +The `rpc.method` can be different from the name of any implementing +method/function. +The `code.function.name` attribute may be used to record the fully-qualified +method actually executing the call on the server side, or the +RPC client stub method on the client side. +""" + +RPC_METHOD_ORIGINAL: Final = "rpc.method_original" +""" +The original name of the method used by the client. +""" + +RPC_REQUEST_METADATA_TEMPLATE: Final = "rpc.request.metadata" +""" +RPC request metadata, `` being the normalized RPC metadata key (lowercase), the value being the metadata values. +Note: Instrumentations SHOULD require an explicit configuration of which metadata values are to be captured. +Including all request metadata values can be a security risk - explicit configuration helps avoid leaking sensitive information. + +For example, a property `my-custom-key` with value `["1.2.3.4", "1.2.3.5"]` SHOULD be recorded as +`rpc.request.metadata.my-custom-key` attribute with value `["1.2.3.4", "1.2.3.5"]`. +""" + +RPC_RESPONSE_METADATA_TEMPLATE: Final = "rpc.response.metadata" +""" +RPC response metadata, `` being the normalized RPC metadata key (lowercase), the value being the metadata values. +Note: Instrumentations SHOULD require an explicit configuration of which metadata values are to be captured. +Including all response metadata values can be a security risk - explicit configuration helps avoid leaking sensitive information. + +For example, a property `my-custom-key` with value `["attribute_value"]` SHOULD be recorded as +the `rpc.response.metadata.my-custom-key` attribute with value `["attribute_value"]`. +""" + +RPC_RESPONSE_STATUS_CODE: Final = "rpc.response.status_code" +""" +Status code of the RPC returned by the RPC server or generated by the client. +Note: Usually it represents an error code, but may also represent partial success, warning, or differentiate between various types of successful outcomes. +Semantic conventions for individual RPC frameworks SHOULD document what `rpc.response.status_code` means in the context of that system and which values are considered to represent errors. +""" + +RPC_SERVICE: Final = "rpc.service" +""" +Deprecated: Value should be included in `rpc.method` which is expected to be a fully-qualified name. +""" + +RPC_SYSTEM: Final = "rpc.system" +""" +Deprecated: Replaced by `rpc.system.name`. +""" + +RPC_SYSTEM_NAME: Final = "rpc.system.name" +""" +The Remote Procedure Call (RPC) system. +Note: The client and server RPC systems may differ for the same RPC interaction. For example, a client may use Apache Dubbo or Connect RPC to communicate with a server that uses gRPC since both protocols provide compatibility with gRPC. +""" + + +@deprecated("The attribute rpc.connect_rpc.error_code is deprecated - Replaced by `rpc.response.status_code`") +class RpcConnectRpcErrorCodeValues(Enum): + CANCELLED = "cancelled" + """cancelled.""" + UNKNOWN = "unknown" + """unknown.""" + INVALID_ARGUMENT = "invalid_argument" + """invalid_argument.""" + DEADLINE_EXCEEDED = "deadline_exceeded" + """deadline_exceeded.""" + NOT_FOUND = "not_found" + """not_found.""" + ALREADY_EXISTS = "already_exists" + """already_exists.""" + PERMISSION_DENIED = "permission_denied" + """permission_denied.""" + RESOURCE_EXHAUSTED = "resource_exhausted" + """resource_exhausted.""" + FAILED_PRECONDITION = "failed_precondition" + """failed_precondition.""" + ABORTED = "aborted" + """aborted.""" + OUT_OF_RANGE = "out_of_range" + """out_of_range.""" + UNIMPLEMENTED = "unimplemented" + """unimplemented.""" + INTERNAL = "internal" + """internal.""" + UNAVAILABLE = "unavailable" + """unavailable.""" + DATA_LOSS = "data_loss" + """data_loss.""" + UNAUTHENTICATED = "unauthenticated" + """unauthenticated.""" + + +@deprecated( + "The attribute rpc.grpc.status_code is deprecated - Use string representation of the gRPC status code on the `rpc.response.status_code` attribute" +) +class RpcGrpcStatusCodeValues(Enum): + OK = 0 + """OK.""" + CANCELLED = 1 + """CANCELLED.""" + UNKNOWN = 2 + """UNKNOWN.""" + INVALID_ARGUMENT = 3 + """INVALID_ARGUMENT.""" + DEADLINE_EXCEEDED = 4 + """DEADLINE_EXCEEDED.""" + NOT_FOUND = 5 + """NOT_FOUND.""" + ALREADY_EXISTS = 6 + """ALREADY_EXISTS.""" + PERMISSION_DENIED = 7 + """PERMISSION_DENIED.""" + RESOURCE_EXHAUSTED = 8 + """RESOURCE_EXHAUSTED.""" + FAILED_PRECONDITION = 9 + """FAILED_PRECONDITION.""" + ABORTED = 10 + """ABORTED.""" + OUT_OF_RANGE = 11 + """OUT_OF_RANGE.""" + UNIMPLEMENTED = 12 + """UNIMPLEMENTED.""" + INTERNAL = 13 + """INTERNAL.""" + UNAVAILABLE = 14 + """UNAVAILABLE.""" + DATA_LOSS = 15 + """DATA_LOSS.""" + UNAUTHENTICATED = 16 + """UNAUTHENTICATED.""" + + +@deprecated("The attribute rpc.message.type is deprecated - Deprecated, no replacement at this time") +class RpcMessageTypeValues(Enum): + SENT = "SENT" + """sent.""" + RECEIVED = "RECEIVED" + """received.""" + + +@deprecated("The attribute rpc.system is deprecated - Replaced by `rpc.system.name`") +class RpcSystemValues(Enum): + GRPC = "grpc" + """gRPC.""" + JAVA_RMI = "java_rmi" + """Java RMI.""" + DOTNET_WCF = "dotnet_wcf" + """.NET WCF.""" + APACHE_DUBBO = "apache_dubbo" + """Apache Dubbo.""" + CONNECT_RPC = "connect_rpc" + """Connect RPC.""" + ONC_RPC = "onc_rpc" + """[ONC RPC (Sun RPC)](https://datatracker.ietf.org/doc/html/rfc5531).""" + JSONRPC = "jsonrpc" + """JSON-RPC.""" + + +class RpcSystemNameValues(Enum): + GRPC = "grpc" + """[gRPC](https://grpc.io/).""" + DUBBO = "dubbo" + """[Apache Dubbo](https://dubbo.apache.org/).""" + CONNECTRPC = "connectrpc" + """[Connect RPC](https://connectrpc.com/).""" + JSONRPC = "jsonrpc" + """[JSON-RPC](https://www.jsonrpc.org/).""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/security_rule_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/security_rule_attributes.py new file mode 100644 index 0000000..fa4ec42 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/security_rule_attributes.py @@ -0,0 +1,45 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +SECURITY_RULE_CATEGORY: Final = "security_rule.category" +""" +A categorization value keyword used by the entity using the rule for detection of this event. +""" + +SECURITY_RULE_DESCRIPTION: Final = "security_rule.description" +""" +The description of the rule generating the event. +""" + +SECURITY_RULE_LICENSE: Final = "security_rule.license" +""" +Name of the license under which the rule used to generate this event is made available. +""" + +SECURITY_RULE_NAME: Final = "security_rule.name" +""" +The name of the rule or signature generating the event. +""" + +SECURITY_RULE_REFERENCE: Final = "security_rule.reference" +""" +Reference URL to additional information about the rule used to generate this event. +Note: The URL can point to the vendor’s documentation about the rule. If that’s not available, it can also be a link to a more general page describing this type of alert. +""" + +SECURITY_RULE_RULESET_NAME: Final = "security_rule.ruleset.name" +""" +Name of the ruleset, policy, group, or parent category in which the rule used to generate this event is a member. +""" + +SECURITY_RULE_UUID: Final = "security_rule.uuid" +""" +A rule ID that is unique within the scope of a set or group of agents, observers, or other entities using the rule for detection of this event. +""" + +SECURITY_RULE_VERSION: Final = "security_rule.version" +""" +The version / revision of the rule being used for analysis. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/server_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/server_attributes.py new file mode 100644 index 0000000..865dcde --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/server_attributes.py @@ -0,0 +1,14 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +SERVER_ADDRESS: Final = "server.address" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.server_attributes.SERVER_ADDRESS`. +""" + +SERVER_PORT: Final = "server.port" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.server_attributes.SERVER_PORT`. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/service_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/service_attributes.py new file mode 100644 index 0000000..eb2dcd6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/service_attributes.py @@ -0,0 +1,52 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +SERVICE_CRITICALITY: Final = "service.criticality" +""" +The operational criticality of the service. +Note: Application developers are encouraged to set `service.criticality` to express the operational importance of their services. Telemetry consumers MAY use this attribute to optimize telemetry collection or improve user experience. +""" + +SERVICE_INSTANCE_ID: Final = "service.instance.id" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.service_attributes.SERVICE_INSTANCE_ID`. +""" + +SERVICE_NAME: Final = "service.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.service_attributes.SERVICE_NAME`. +""" + +SERVICE_NAMESPACE: Final = "service.namespace" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.service_attributes.SERVICE_NAMESPACE`. +""" + +SERVICE_PEER_NAME: Final = "service.peer.name" +""" +Logical name of the service on the other side of the connection. SHOULD be equal to the actual [`service.name`](/docs/resource/README.md#service) resource attribute of the remote service if any. +""" + +SERVICE_PEER_NAMESPACE: Final = "service.peer.namespace" +""" +Logical namespace of the service on the other side of the connection. SHOULD be equal to the actual [`service.namespace`](/docs/resource/README.md#service) resource attribute of the remote service if any. +""" + +SERVICE_VERSION: Final = "service.version" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.service_attributes.SERVICE_VERSION`. +""" + + +class ServiceCriticalityValues(Enum): + CRITICAL = "critical" + """Service is business-critical; downtime directly impacts revenue, user experience, or core functionality.""" + HIGH = "high" + """Service is important but has degradation tolerance or fallback mechanisms.""" + MEDIUM = "medium" + """Service provides supplementary functionality; degradation has limited user impact.""" + LOW = "low" + """Service is non-essential to core operations; used for background tasks or internal tools.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/session_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/session_attributes.py new file mode 100644 index 0000000..db9a5ed --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/session_attributes.py @@ -0,0 +1,14 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +SESSION_ID: Final = "session.id" +""" +A unique ID to identify a session. +""" + +SESSION_PREVIOUS_ID: Final = "session.previous_id" +""" +The previous `session.id` for this user, when known. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/source_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/source_attributes.py new file mode 100644 index 0000000..1960b24 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/source_attributes.py @@ -0,0 +1,15 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +SOURCE_ADDRESS: Final = "source.address" +""" +Source address - domain name if available without reverse DNS lookup; otherwise, IP address or UNIX domain socket name. +Note: When observed from the destination side, and when communicating through an intermediary, `source.address` SHOULD represent the source address behind any intermediaries, for example proxies, if it's available. +""" + +SOURCE_PORT: Final = "source.port" +""" +Source port number. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/system_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/system_attributes.py new file mode 100644 index 0000000..4873f7a --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/system_attributes.py @@ -0,0 +1,242 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +SYSTEM_CPU_LOGICAL_NUMBER: Final = "system.cpu.logical_number" +""" +Deprecated: Replaced by `cpu.logical_number`. +""" + +SYSTEM_CPU_STATE: Final = "system.cpu.state" +""" +Deprecated: Replaced by `cpu.mode`. +""" + +SYSTEM_DEVICE: Final = "system.device" +""" +The device identifier. +""" + +SYSTEM_FILESYSTEM_MODE: Final = "system.filesystem.mode" +""" +The filesystem mode. +""" + +SYSTEM_FILESYSTEM_MOUNTPOINT: Final = "system.filesystem.mountpoint" +""" +The filesystem mount path. +""" + +SYSTEM_FILESYSTEM_STATE: Final = "system.filesystem.state" +""" +The filesystem state. +""" + +SYSTEM_FILESYSTEM_TYPE: Final = "system.filesystem.type" +""" +The filesystem type. +""" + +SYSTEM_MEMORY_LINUX_HUGEPAGES_STATE: Final = "system.memory.linux.hugepages.state" +""" +The Linux HugePages memory state. +""" + +SYSTEM_MEMORY_LINUX_SLAB_STATE: Final = "system.memory.linux.slab.state" +""" +The Linux Slab memory state. +""" + +SYSTEM_MEMORY_STATE: Final = "system.memory.state" +""" +The memory state. +""" + +SYSTEM_NETWORK_STATE: Final = "system.network.state" +""" +Deprecated: Replaced by `network.connection.state`. +""" + +SYSTEM_PAGING_DIRECTION: Final = "system.paging.direction" +""" +The paging access direction. +""" + +SYSTEM_PAGING_FAULT_TYPE: Final = "system.paging.fault.type" +""" +The paging fault type. +""" + +SYSTEM_PAGING_STATE: Final = "system.paging.state" +""" +The memory paging state. +""" + +SYSTEM_PAGING_TYPE: Final = "system.paging.type" +""" +Deprecated: Replaced by `system.paging.fault.type`. +""" + +SYSTEM_PROCESS_STATUS: Final = "system.process.status" +""" +Deprecated: Replaced by `process.state`. +""" + +SYSTEM_PROCESSES_STATUS: Final = "system.processes.status" +""" +Deprecated: Replaced by `process.state`. +""" + + +@deprecated("The attribute system.cpu.state is deprecated - Replaced by `cpu.mode`") +class SystemCpuStateValues(Enum): + USER = "user" + """user.""" + SYSTEM = "system" + """system.""" + NICE = "nice" + """nice.""" + IDLE = "idle" + """idle.""" + IOWAIT = "iowait" + """iowait.""" + INTERRUPT = "interrupt" + """interrupt.""" + STEAL = "steal" + """steal.""" + + +class SystemFilesystemStateValues(Enum): + USED = "used" + """used.""" + FREE = "free" + """free.""" + RESERVED = "reserved" + """reserved.""" + + +class SystemFilesystemTypeValues(Enum): + FAT32 = "fat32" + """fat32.""" + EXFAT = "exfat" + """exfat.""" + NTFS = "ntfs" + """ntfs.""" + REFS = "refs" + """refs.""" + HFSPLUS = "hfsplus" + """hfsplus.""" + EXT4 = "ext4" + """ext4.""" + + +class SystemMemoryLinuxHugepagesStateValues(Enum): + FREE = "free" + """free.""" + USED = "used" + """used.""" + + +class SystemMemoryLinuxSlabStateValues(Enum): + RECLAIMABLE = "reclaimable" + """reclaimable.""" + UNRECLAIMABLE = "unreclaimable" + """unreclaimable.""" + + +class SystemMemoryStateValues(Enum): + USED = "used" + """Actual used virtual memory in bytes.""" + FREE = "free" + """free.""" + SHARED = "shared" + """Deprecated: Removed, report shared memory usage with `metric.system.memory.linux.shared` metric.""" + BUFFERS = "buffers" + """buffers.""" + CACHED = "cached" + """cached.""" + + +@deprecated("The attribute system.network.state is deprecated - Replaced by `network.connection.state`") +class SystemNetworkStateValues(Enum): + CLOSE = "close" + """close.""" + CLOSE_WAIT = "close_wait" + """close_wait.""" + CLOSING = "closing" + """closing.""" + DELETE = "delete" + """delete.""" + ESTABLISHED = "established" + """established.""" + FIN_WAIT_1 = "fin_wait_1" + """fin_wait_1.""" + FIN_WAIT_2 = "fin_wait_2" + """fin_wait_2.""" + LAST_ACK = "last_ack" + """last_ack.""" + LISTEN = "listen" + """listen.""" + SYN_RECV = "syn_recv" + """syn_recv.""" + SYN_SENT = "syn_sent" + """syn_sent.""" + TIME_WAIT = "time_wait" + """time_wait.""" + + +class SystemPagingDirectionValues(Enum): + IN = "in" + """in.""" + OUT = "out" + """out.""" + + +class SystemPagingFaultTypeValues(Enum): + MAJOR = "major" + """major.""" + MINOR = "minor" + """minor.""" + + +class SystemPagingStateValues(Enum): + USED = "used" + """used.""" + FREE = "free" + """free.""" + + +@deprecated("The attribute system.paging.type is deprecated - Replaced by `system.paging.fault.type`") +class SystemPagingTypeValues(Enum): + MAJOR = "major" + """major.""" + MINOR = "minor" + """minor.""" + + +@deprecated("The attribute system.process.status is deprecated - Replaced by `process.state`") +class SystemProcessStatusValues(Enum): + RUNNING = "running" + """running.""" + SLEEPING = "sleeping" + """sleeping.""" + STOPPED = "stopped" + """stopped.""" + DEFUNCT = "defunct" + """defunct.""" + + +@deprecated("The attribute system.processes.status is deprecated - Replaced by `process.state`") +class SystemProcessesStatusValues(Enum): + RUNNING = "running" + """running.""" + SLEEPING = "sleeping" + """sleeping.""" + STOPPED = "stopped" + """stopped.""" + DEFUNCT = "defunct" + """defunct.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/telemetry_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/telemetry_attributes.py new file mode 100644 index 0000000..cefc925 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/telemetry_attributes.py @@ -0,0 +1,64 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +TELEMETRY_DISTRO_NAME: Final = "telemetry.distro.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TELEMETRY_DISTRO_NAME`. +""" + +TELEMETRY_DISTRO_VERSION: Final = "telemetry.distro.version" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TELEMETRY_DISTRO_VERSION`. +""" + +TELEMETRY_SDK_LANGUAGE: Final = "telemetry.sdk.language" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TELEMETRY_SDK_LANGUAGE`. +""" + +TELEMETRY_SDK_NAME: Final = "telemetry.sdk.name" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TELEMETRY_SDK_NAME`. +""" + +TELEMETRY_SDK_VERSION: Final = "telemetry.sdk.version" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TELEMETRY_SDK_VERSION`. +""" + + +@deprecated( + "Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues`." +) +class TelemetrySdkLanguageValues(Enum): + CPP = "cpp" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues.CPP`.""" + DOTNET = "dotnet" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues.DOTNET`.""" + ERLANG = "erlang" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues.ERLANG`.""" + GO = "go" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues.GO`.""" + JAVA = "java" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues.JAVA`.""" + KOTLIN = "kotlin" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues.KOTLIN`.""" + NODEJS = "nodejs" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues.NODEJS`.""" + PHP = "php" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues.PHP`.""" + PYTHON = "python" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues.PYTHON`.""" + RUBY = "ruby" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues.RUBY`.""" + RUST = "rust" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues.RUST`.""" + SWIFT = "swift" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues.SWIFT`.""" + WEBJS = "webjs" + """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TelemetrySdkLanguageValues.WEBJS`.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/test_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/test_attributes.py new file mode 100644 index 0000000..4a3ef39 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/test_attributes.py @@ -0,0 +1,47 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +TEST_CASE_NAME: Final = "test.case.name" +""" +The fully qualified human readable name of the [test case](https://wikipedia.org/wiki/Test_case). +""" + +TEST_CASE_RESULT_STATUS: Final = "test.case.result.status" +""" +The status of the actual test case result from test execution. +""" + +TEST_SUITE_NAME: Final = "test.suite.name" +""" +The human readable name of a [test suite](https://wikipedia.org/wiki/Test_suite). +""" + +TEST_SUITE_RUN_STATUS: Final = "test.suite.run.status" +""" +The status of the test suite run. +""" + + +class TestCaseResultStatusValues(Enum): + PASS = "pass" + """pass.""" + FAIL = "fail" + """fail.""" + + +class TestSuiteRunStatusValues(Enum): + SUCCESS = "success" + """success.""" + FAILURE = "failure" + """failure.""" + SKIPPED = "skipped" + """skipped.""" + ABORTED = "aborted" + """aborted.""" + TIMED_OUT = "timed_out" + """timed_out.""" + IN_PROGRESS = "in_progress" + """in_progress.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/thread_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/thread_attributes.py new file mode 100644 index 0000000..29c4143 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/thread_attributes.py @@ -0,0 +1,33 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +THREAD_ID: Final = "thread.id" +""" +Current "managed" thread ID (as opposed to OS thread ID). +Note: Examples of where the value can be extracted from: + +| Language or platform | Source | +| --- | --- | +| JVM | `Thread.currentThread().threadId()` | +| .NET | `Thread.CurrentThread.ManagedThreadId` | +| Python | `threading.current_thread().ident` | +| Ruby | `Thread.current.object_id` | +| C++ | `std::this_thread::get_id()` | +| Erlang | `erlang:self()` |. +""" + +THREAD_NAME: Final = "thread.name" +""" +Current thread name. +Note: Examples of where the value can be extracted from: + +| Language or platform | Source | +| --- | --- | +| JVM | `Thread.currentThread().getName()` | +| .NET | `Thread.CurrentThread.Name` | +| Python | `threading.current_thread().name` | +| Ruby | `Thread.current.name` | +| Erlang | `erlang:process_info(self(), registered_name)` |. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/tls_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/tls_attributes.py new file mode 100644 index 0000000..60e722c --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/tls_attributes.py @@ -0,0 +1,158 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +TLS_CIPHER: Final = "tls.cipher" +""" +String indicating the [cipher](https://datatracker.ietf.org/doc/html/rfc5246#appendix-A.5) used during the current connection. +Note: The values allowed for `tls.cipher` MUST be one of the `Descriptions` of the [registered TLS Cipher Suits](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#table-tls-parameters-4). +""" + +TLS_CLIENT_CERTIFICATE: Final = "tls.client.certificate" +""" +PEM-encoded stand-alone certificate offered by the client. This is usually mutually-exclusive of `client.certificate_chain` since this value also exists in that list. +""" + +TLS_CLIENT_CERTIFICATE_CHAIN: Final = "tls.client.certificate_chain" +""" +Array of PEM-encoded certificates that make up the certificate chain offered by the client. This is usually mutually-exclusive of `client.certificate` since that value should be the first certificate in the chain. +""" + +TLS_CLIENT_HASH_MD5: Final = "tls.client.hash.md5" +""" +Certificate fingerprint using the MD5 digest of DER-encoded version of certificate offered by the client. For consistency with other hash values, this value should be formatted as an uppercase hash. +""" + +TLS_CLIENT_HASH_SHA1: Final = "tls.client.hash.sha1" +""" +Certificate fingerprint using the SHA1 digest of DER-encoded version of certificate offered by the client. For consistency with other hash values, this value should be formatted as an uppercase hash. +""" + +TLS_CLIENT_HASH_SHA256: Final = "tls.client.hash.sha256" +""" +Certificate fingerprint using the SHA256 digest of DER-encoded version of certificate offered by the client. For consistency with other hash values, this value should be formatted as an uppercase hash. +""" + +TLS_CLIENT_ISSUER: Final = "tls.client.issuer" +""" +Distinguished name of [subject](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6) of the issuer of the x.509 certificate presented by the client. +""" + +TLS_CLIENT_JA3: Final = "tls.client.ja3" +""" +A hash that identifies clients based on how they perform an SSL/TLS handshake. +""" + +TLS_CLIENT_NOT_AFTER: Final = "tls.client.not_after" +""" +Date/Time indicating when client certificate is no longer considered valid. +""" + +TLS_CLIENT_NOT_BEFORE: Final = "tls.client.not_before" +""" +Date/Time indicating when client certificate is first considered valid. +""" + +TLS_CLIENT_SERVER_NAME: Final = "tls.client.server_name" +""" +Deprecated: Replaced by `server.address`. +""" + +TLS_CLIENT_SUBJECT: Final = "tls.client.subject" +""" +Distinguished name of subject of the x.509 certificate presented by the client. +""" + +TLS_CLIENT_SUPPORTED_CIPHERS: Final = "tls.client.supported_ciphers" +""" +Array of ciphers offered by the client during the client hello. +""" + +TLS_CURVE: Final = "tls.curve" +""" +String indicating the curve used for the given cipher, when applicable. +""" + +TLS_ESTABLISHED: Final = "tls.established" +""" +Boolean flag indicating if the TLS negotiation was successful and transitioned to an encrypted tunnel. +""" + +TLS_NEXT_PROTOCOL: Final = "tls.next_protocol" +""" +String indicating the protocol being tunneled. Per the values in the [IANA registry](https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids), this string should be lower case. +""" + +TLS_PROTOCOL_NAME: Final = "tls.protocol.name" +""" +Normalized lowercase protocol name parsed from original string of the negotiated [SSL/TLS protocol version](https://docs.openssl.org/1.1.1/man3/SSL_get_version/#return-values). +""" + +TLS_PROTOCOL_VERSION: Final = "tls.protocol.version" +""" +Numeric part of the version parsed from the original string of the negotiated [SSL/TLS protocol version](https://docs.openssl.org/1.1.1/man3/SSL_get_version/#return-values). +""" + +TLS_RESUMED: Final = "tls.resumed" +""" +Boolean flag indicating if this TLS connection was resumed from an existing TLS negotiation. +""" + +TLS_SERVER_CERTIFICATE: Final = "tls.server.certificate" +""" +PEM-encoded stand-alone certificate offered by the server. This is usually mutually-exclusive of `server.certificate_chain` since this value also exists in that list. +""" + +TLS_SERVER_CERTIFICATE_CHAIN: Final = "tls.server.certificate_chain" +""" +Array of PEM-encoded certificates that make up the certificate chain offered by the server. This is usually mutually-exclusive of `server.certificate` since that value should be the first certificate in the chain. +""" + +TLS_SERVER_HASH_MD5: Final = "tls.server.hash.md5" +""" +Certificate fingerprint using the MD5 digest of DER-encoded version of certificate offered by the server. For consistency with other hash values, this value should be formatted as an uppercase hash. +""" + +TLS_SERVER_HASH_SHA1: Final = "tls.server.hash.sha1" +""" +Certificate fingerprint using the SHA1 digest of DER-encoded version of certificate offered by the server. For consistency with other hash values, this value should be formatted as an uppercase hash. +""" + +TLS_SERVER_HASH_SHA256: Final = "tls.server.hash.sha256" +""" +Certificate fingerprint using the SHA256 digest of DER-encoded version of certificate offered by the server. For consistency with other hash values, this value should be formatted as an uppercase hash. +""" + +TLS_SERVER_ISSUER: Final = "tls.server.issuer" +""" +Distinguished name of [subject](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6) of the issuer of the x.509 certificate presented by the client. +""" + +TLS_SERVER_JA3S: Final = "tls.server.ja3s" +""" +A hash that identifies servers based on how they perform an SSL/TLS handshake. +""" + +TLS_SERVER_NOT_AFTER: Final = "tls.server.not_after" +""" +Date/Time indicating when server certificate is no longer considered valid. +""" + +TLS_SERVER_NOT_BEFORE: Final = "tls.server.not_before" +""" +Date/Time indicating when server certificate is first considered valid. +""" + +TLS_SERVER_SUBJECT: Final = "tls.server.subject" +""" +Distinguished name of subject of the x.509 certificate presented by the server. +""" + + +class TlsProtocolNameValues(Enum): + SSL = "ssl" + """SSL.""" + TLS = "tls" + """TLS.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/url_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/url_attributes.py new file mode 100644 index 0000000..345349a --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/url_attributes.py @@ -0,0 +1,76 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +URL_DOMAIN: Final = "url.domain" +""" +Domain extracted from the `url.full`, such as "opentelemetry.io". +Note: In some cases a URL may refer to an IP and/or port directly, without a domain name. In this case, the IP address would go to the domain field. If the URL contains a [literal IPv6 address](https://www.rfc-editor.org/rfc/rfc2732#section-2) enclosed by `[` and `]`, the `[` and `]` characters should also be captured in the domain field. +""" + +URL_EXTENSION: Final = "url.extension" +""" +The file extension extracted from the `url.full`, excluding the leading dot. +Note: The file extension is only set if it exists, as not every URL has a file extension. When the filename has multiple extensions `example.tar.gz`, only the last one should be captured `gz`, not `tar.gz`. +""" + +URL_FRAGMENT: Final = "url.fragment" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.url_attributes.URL_FRAGMENT`. +""" + +URL_FULL: Final = "url.full" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.url_attributes.URL_FULL`. +""" + +URL_ORIGINAL: Final = "url.original" +""" +Unmodified original URL as seen in the event source. +Note: In network monitoring, the observed URL may be a full URL, whereas in access logs, the URL is often just represented as a path. This field is meant to represent the URL as it was observed, complete or not. +`url.original` might contain credentials passed via URL in form of `https://username:password@www.example.com/`. In such case password and username SHOULD NOT be redacted and attribute's value SHOULD remain the same. +""" + +URL_PATH: Final = "url.path" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.url_attributes.URL_PATH`. +""" + +URL_PORT: Final = "url.port" +""" +Port extracted from the `url.full`. +""" + +URL_QUERY: Final = "url.query" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.url_attributes.URL_QUERY`. +""" + +URL_REGISTERED_DOMAIN: Final = "url.registered_domain" +""" +The highest registered URL domain, stripped of the subdomain. +Note: This value can be determined precisely with the [public suffix list](https://publicsuffix.org/). For example, the registered domain for `foo.example.com` is `example.com`. Trying to approximate this by simply taking the last two labels will not work well for TLDs such as `co.uk`. +""" + +URL_SCHEME: Final = "url.scheme" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.url_attributes.URL_SCHEME`. +""" + +URL_SUBDOMAIN: Final = "url.subdomain" +""" +The subdomain portion of a fully qualified domain name includes all of the names except the hostname under the registered_domain. In a partially qualified domain, or if the qualification level of the full name cannot be determined, subdomain contains all of the names below the registered domain. +Note: The subdomain portion of `www.east.mydomain.co.uk` is `east`. If the domain has multiple levels of subdomain, such as `sub2.sub1.example.com`, the subdomain field should contain `sub2.sub1`, with no trailing period. +""" + +URL_TEMPLATE: Final = "url.template" +""" +The low-cardinality template of an [absolute path reference](https://www.rfc-editor.org/rfc/rfc3986#section-4.2). +""" + +URL_TOP_LEVEL_DOMAIN: Final = "url.top_level_domain" +""" +The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for example.com is `com`. +Note: This value can be determined precisely with the [public suffix list](https://publicsuffix.org/). +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/user_agent_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/user_agent_attributes.py new file mode 100644 index 0000000..bccceba --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/user_agent_attributes.py @@ -0,0 +1,47 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +USER_AGENT_NAME: Final = "user_agent.name" +""" +Name of the user-agent extracted from original. Usually refers to the browser's name. +Note: [Example](https://uaparser.dev/#demo) of extracting browser's name from original string. In the case of using a user-agent for non-browser products, such as microservices with multiple names/versions inside the `user_agent.original`, the most significant name SHOULD be selected. In such a scenario it should align with `user_agent.version`. +""" + +USER_AGENT_ORIGINAL: Final = "user_agent.original" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.user_agent_attributes.USER_AGENT_ORIGINAL`. +""" + +USER_AGENT_OS_NAME: Final = "user_agent.os.name" +""" +Human readable operating system name. +Note: For mapping user agent strings to OS names, libraries such as [ua-parser](https://github.com/ua-parser) can be utilized. +""" + +USER_AGENT_OS_VERSION: Final = "user_agent.os.version" +""" +The version string of the operating system as defined in [Version Attributes](/docs/resource/README.md#version-attributes). +Note: For mapping user agent strings to OS versions, libraries such as [ua-parser](https://github.com/ua-parser) can be utilized. +""" + +USER_AGENT_SYNTHETIC_TYPE: Final = "user_agent.synthetic.type" +""" +Specifies the category of synthetic traffic, such as tests or bots. +Note: This attribute MAY be derived from the contents of the `user_agent.original` attribute. Components that populate the attribute are responsible for determining what they consider to be synthetic bot or test traffic. This attribute can either be set for self-identification purposes, or on telemetry detected to be generated as a result of a synthetic request. This attribute is useful for distinguishing between genuine client traffic and synthetic traffic generated by bots or tests. +""" + +USER_AGENT_VERSION: Final = "user_agent.version" +""" +Version of the user-agent extracted from original. Usually refers to the browser's version. +Note: [Example](https://uaparser.dev/#demo) of extracting browser's version from original string. In the case of using a user-agent for non-browser products, such as microservices with multiple names/versions inside the `user_agent.original`, the most significant version SHOULD be selected. In such a scenario it should align with `user_agent.name`. +""" + + +class UserAgentSyntheticTypeValues(Enum): + BOT = "bot" + """Bot source.""" + TEST = "test" + """Synthetic test source.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/user_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/user_attributes.py new file mode 100644 index 0000000..91d0dbc --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/user_attributes.py @@ -0,0 +1,35 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +USER_EMAIL: Final = "user.email" +""" +User email address. +""" + +USER_FULL_NAME: Final = "user.full_name" +""" +User's full name. +""" + +USER_HASH: Final = "user.hash" +""" +Unique user hash to correlate information for a user in anonymized form. +Note: Useful if `user.id` or `user.name` contain confidential information and cannot be used. +""" + +USER_ID: Final = "user.id" +""" +Unique identifier of the user. +""" + +USER_NAME: Final = "user.name" +""" +Short name or login/username of the user. +""" + +USER_ROLES: Final = "user.roles" +""" +Array of user roles at the time of the event. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/vcs_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/vcs_attributes.py new file mode 100644 index 0000000..b56f26c --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/vcs_attributes.py @@ -0,0 +1,218 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +from typing_extensions import deprecated + +VCS_CHANGE_ID: Final = "vcs.change.id" +""" +The ID of the change (pull request/merge request/changelist) if applicable. This is usually a unique (within repository) identifier generated by the VCS system. +""" + +VCS_CHANGE_STATE: Final = "vcs.change.state" +""" +The state of the change (pull request/merge request/changelist). +""" + +VCS_CHANGE_TITLE: Final = "vcs.change.title" +""" +The human readable title of the change (pull request/merge request/changelist). This title is often a brief summary of the change and may get merged in to a ref as the commit summary. +""" + +VCS_LINE_CHANGE_TYPE: Final = "vcs.line_change.type" +""" +The type of line change being measured on a branch or change. +""" + +VCS_OWNER_NAME: Final = "vcs.owner.name" +""" +The group owner within the version control system. +""" + +VCS_PROVIDER_NAME: Final = "vcs.provider.name" +""" +The name of the version control system provider. +""" + +VCS_REF_BASE_NAME: Final = "vcs.ref.base.name" +""" +The name of the [reference](https://git-scm.com/docs/gitglossary#def_ref) such as **branch** or **tag** in the repository. +Note: `base` refers to the starting point of a change. For example, `main` +would be the base reference of type branch if you've created a new +reference of type branch from it and created new commits. +""" + +VCS_REF_BASE_REVISION: Final = "vcs.ref.base.revision" +""" +The revision, literally [revised version](https://www.merriam-webster.com/dictionary/revision), The revision most often refers to a commit object in Git, or a revision number in SVN. +Note: `base` refers to the starting point of a change. For example, `main` +would be the base reference of type branch if you've created a new +reference of type branch from it and created new commits. The +revision can be a full [hash value (see +glossary)](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf), +of the recorded change to a ref within a repository pointing to a +commit [commit](https://git-scm.com/docs/git-commit) object. It does +not necessarily have to be a hash; it can simply define a [revision +number](https://svnbook.red-bean.com/en/1.7/svn.tour.revs.specifiers.html) +which is an integer that is monotonically increasing. In cases where +it is identical to the `ref.base.name`, it SHOULD still be included. +It is up to the implementer to decide which value to set as the +revision based on the VCS system and situational context. +""" + +VCS_REF_BASE_TYPE: Final = "vcs.ref.base.type" +""" +The type of the [reference](https://git-scm.com/docs/gitglossary#def_ref) in the repository. +Note: `base` refers to the starting point of a change. For example, `main` +would be the base reference of type branch if you've created a new +reference of type branch from it and created new commits. +""" + +VCS_REF_HEAD_NAME: Final = "vcs.ref.head.name" +""" +The name of the [reference](https://git-scm.com/docs/gitglossary#def_ref) such as **branch** or **tag** in the repository. +Note: `head` refers to where you are right now; the current reference at a +given time. +""" + +VCS_REF_HEAD_REVISION: Final = "vcs.ref.head.revision" +""" +The revision, literally [revised version](https://www.merriam-webster.com/dictionary/revision), The revision most often refers to a commit object in Git, or a revision number in SVN. +Note: `head` refers to where you are right now; the current reference at a +given time.The revision can be a full [hash value (see +glossary)](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf), +of the recorded change to a ref within a repository pointing to a +commit [commit](https://git-scm.com/docs/git-commit) object. It does +not necessarily have to be a hash; it can simply define a [revision +number](https://svnbook.red-bean.com/en/1.7/svn.tour.revs.specifiers.html) +which is an integer that is monotonically increasing. In cases where +it is identical to the `ref.head.name`, it SHOULD still be included. +It is up to the implementer to decide which value to set as the +revision based on the VCS system and situational context. +""" + +VCS_REF_HEAD_TYPE: Final = "vcs.ref.head.type" +""" +The type of the [reference](https://git-scm.com/docs/gitglossary#def_ref) in the repository. +Note: `head` refers to where you are right now; the current reference at a +given time. +""" + +VCS_REF_TYPE: Final = "vcs.ref.type" +""" +The type of the [reference](https://git-scm.com/docs/gitglossary#def_ref) in the repository. +""" + +VCS_REPOSITORY_CHANGE_ID: Final = "vcs.repository.change.id" +""" +Deprecated: Replaced by `vcs.change.id`. +""" + +VCS_REPOSITORY_CHANGE_TITLE: Final = "vcs.repository.change.title" +""" +Deprecated: Replaced by `vcs.change.title`. +""" + +VCS_REPOSITORY_NAME: Final = "vcs.repository.name" +""" +The human readable name of the repository. It SHOULD NOT include any additional identifier like Group/SubGroup in GitLab or organization in GitHub. +Note: Due to it only being the name, it can clash with forks of the same +repository if collecting telemetry across multiple orgs or groups in +the same backends. +""" + +VCS_REPOSITORY_REF_NAME: Final = "vcs.repository.ref.name" +""" +Deprecated: Replaced by `vcs.ref.head.name`. +""" + +VCS_REPOSITORY_REF_REVISION: Final = "vcs.repository.ref.revision" +""" +Deprecated: Replaced by `vcs.ref.head.revision`. +""" + +VCS_REPOSITORY_REF_TYPE: Final = "vcs.repository.ref.type" +""" +Deprecated: Replaced by `vcs.ref.head.type`. +""" + +VCS_REPOSITORY_URL_FULL: Final = "vcs.repository.url.full" +""" +The [canonical URL](https://support.google.com/webmasters/answer/10347851) of the repository providing the complete HTTP(S) address in order to locate and identify the repository through a browser. +Note: In Git Version Control Systems, the canonical URL SHOULD NOT include +the `.git` extension. +""" + +VCS_REVISION_DELTA_DIRECTION: Final = "vcs.revision_delta.direction" +""" +The type of revision comparison. +""" + + +class VcsChangeStateValues(Enum): + OPEN = "open" + """Open means the change is currently active and under review. It hasn't been merged into the target branch yet, and it's still possible to make changes or add comments.""" + WIP = "wip" + """WIP (work-in-progress, draft) means the change is still in progress and not yet ready for a full review. It might still undergo significant changes.""" + CLOSED = "closed" + """Closed means the merge request has been closed without merging. This can happen for various reasons, such as the changes being deemed unnecessary, the issue being resolved in another way, or the author deciding to withdraw the request.""" + MERGED = "merged" + """Merged indicates that the change has been successfully integrated into the target codebase.""" + + +class VcsLineChangeTypeValues(Enum): + ADDED = "added" + """How many lines were added.""" + REMOVED = "removed" + """How many lines were removed.""" + + +class VcsProviderNameValues(Enum): + GITHUB = "github" + """[GitHub](https://github.com).""" + GITLAB = "gitlab" + """[GitLab](https://gitlab.com).""" + GITTEA = "gittea" + """Deprecated: Replaced by `gitea`.""" + GITEA = "gitea" + """[Gitea](https://gitea.io).""" + BITBUCKET = "bitbucket" + """[Bitbucket](https://bitbucket.org).""" + + +class VcsRefBaseTypeValues(Enum): + BRANCH = "branch" + """[branch](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefbranchabranch).""" + TAG = "tag" + """[tag](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddeftagatag).""" + + +class VcsRefHeadTypeValues(Enum): + BRANCH = "branch" + """[branch](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefbranchabranch).""" + TAG = "tag" + """[tag](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddeftagatag).""" + + +class VcsRefTypeValues(Enum): + BRANCH = "branch" + """[branch](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefbranchabranch).""" + TAG = "tag" + """[tag](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddeftagatag).""" + + +@deprecated("The attribute vcs.repository.ref.type is deprecated - Replaced by `vcs.ref.head.type`") +class VcsRepositoryRefTypeValues(Enum): + BRANCH = "branch" + """[branch](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefbranchabranch).""" + TAG = "tag" + """[tag](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddeftagatag).""" + + +class VcsRevisionDeltaDirectionValues(Enum): + BEHIND = "behind" + """How many revisions the change is behind the target ref.""" + AHEAD = "ahead" + """How many revisions the change is ahead of the target ref.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/webengine_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/webengine_attributes.py new file mode 100644 index 0000000..0d0bf87 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/webengine_attributes.py @@ -0,0 +1,19 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +WEBENGINE_DESCRIPTION: Final = "webengine.description" +""" +Additional description of the web engine (e.g. detailed version and edition information). +""" + +WEBENGINE_NAME: Final = "webengine.name" +""" +The name of the web engine. +""" + +WEBENGINE_VERSION: Final = "webengine.version" +""" +The version of the web engine. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/zos_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/zos_attributes.py new file mode 100644 index 0000000..3c65710 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/attributes/zos_attributes.py @@ -0,0 +1,14 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +ZOS_SMF_ID: Final = "zos.smf.id" +""" +The System Management Facility (SMF) Identifier uniquely identified a z/OS system within a SYSPLEX or mainframe environment and is used for system and performance analysis. +""" + +ZOS_SYSPLEX_NAME: Final = "zos.sysplex.name" +""" +The name of the SYSPLEX to which the z/OS system belongs too. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/azure_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/azure_metrics.cpython-312.pyc new file mode 100644 index 0000000..8572f7e Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/azure_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/cicd_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/cicd_metrics.cpython-312.pyc new file mode 100644 index 0000000..f27324a Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/cicd_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/container_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/container_metrics.cpython-312.pyc new file mode 100644 index 0000000..6130a19 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/container_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/cpu_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/cpu_metrics.cpython-312.pyc new file mode 100644 index 0000000..42e22df Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/cpu_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/cpython_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/cpython_metrics.cpython-312.pyc new file mode 100644 index 0000000..c818a56 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/cpython_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/db_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/db_metrics.cpython-312.pyc new file mode 100644 index 0000000..824dc3e Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/db_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/dns_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/dns_metrics.cpython-312.pyc new file mode 100644 index 0000000..ea1c061 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/dns_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/faas_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/faas_metrics.cpython-312.pyc new file mode 100644 index 0000000..e347f5a Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/faas_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/gen_ai_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/gen_ai_metrics.cpython-312.pyc new file mode 100644 index 0000000..3523d32 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/gen_ai_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/http_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/http_metrics.cpython-312.pyc new file mode 100644 index 0000000..93f4a43 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/http_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/hw_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/hw_metrics.cpython-312.pyc new file mode 100644 index 0000000..92f835a Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/hw_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/k8s_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/k8s_metrics.cpython-312.pyc new file mode 100644 index 0000000..f1a3e1d Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/k8s_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/mcp_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/mcp_metrics.cpython-312.pyc new file mode 100644 index 0000000..4933a6f Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/mcp_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/messaging_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/messaging_metrics.cpython-312.pyc new file mode 100644 index 0000000..b537344 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/messaging_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/nfs_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/nfs_metrics.cpython-312.pyc new file mode 100644 index 0000000..cbdcaf3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/nfs_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/openshift_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/openshift_metrics.cpython-312.pyc new file mode 100644 index 0000000..adf3926 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/openshift_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/otel_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/otel_metrics.cpython-312.pyc new file mode 100644 index 0000000..663ec0d Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/otel_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/process_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/process_metrics.cpython-312.pyc new file mode 100644 index 0000000..571d3a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/process_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/rpc_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/rpc_metrics.cpython-312.pyc new file mode 100644 index 0000000..8ddae0f Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/rpc_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/system_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/system_metrics.cpython-312.pyc new file mode 100644 index 0000000..55dd952 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/system_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/vcs_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/vcs_metrics.cpython-312.pyc new file mode 100644 index 0000000..c8fa580 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/__pycache__/vcs_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/azure_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/azure_metrics.py new file mode 100644 index 0000000..5e22235 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/azure_metrics.py @@ -0,0 +1,40 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Histogram, Meter, UpDownCounter + +AZURE_COSMOSDB_CLIENT_ACTIVE_INSTANCE_COUNT: Final = "azure.cosmosdb.client.active_instance.count" +""" +Number of active client instances +Instrument: updowncounter +Unit: {instance} +""" + + +def create_azure_cosmosdb_client_active_instance_count(meter: Meter) -> UpDownCounter: + """Number of active client instances""" + return meter.create_up_down_counter( + name=AZURE_COSMOSDB_CLIENT_ACTIVE_INSTANCE_COUNT, + description="Number of active client instances.", + unit="{instance}", + ) + + +AZURE_COSMOSDB_CLIENT_OPERATION_REQUEST_CHARGE: Final = "azure.cosmosdb.client.operation.request_charge" +""" +[Request units](https://learn.microsoft.com/azure/cosmos-db/request-units) consumed by the operation +Instrument: histogram +Unit: {request_unit} +""" + + +def create_azure_cosmosdb_client_operation_request_charge(meter: Meter) -> Histogram: + """[Request units](https://learn.microsoft.com/azure/cosmos-db/request-units) consumed by the operation""" + return meter.create_histogram( + name=AZURE_COSMOSDB_CLIENT_OPERATION_REQUEST_CHARGE, + description="[Request units](https://learn.microsoft.com/azure/cosmos-db/request-units) consumed by the operation.", + unit="{request_unit}", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/cicd_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/cicd_metrics.py new file mode 100644 index 0000000..748cf30 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/cicd_metrics.py @@ -0,0 +1,94 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Counter, Histogram, Meter, UpDownCounter + +CICD_PIPELINE_RUN_ACTIVE: Final = "cicd.pipeline.run.active" +""" +The number of pipeline runs currently active in the system by state +Instrument: updowncounter +Unit: {run} +""" + + +def create_cicd_pipeline_run_active(meter: Meter) -> UpDownCounter: + """The number of pipeline runs currently active in the system by state""" + return meter.create_up_down_counter( + name=CICD_PIPELINE_RUN_ACTIVE, + description="The number of pipeline runs currently active in the system by state.", + unit="{run}", + ) + + +CICD_PIPELINE_RUN_DURATION: Final = "cicd.pipeline.run.duration" +""" +Duration of a pipeline run grouped by pipeline, state and result +Instrument: histogram +Unit: s +""" + + +def create_cicd_pipeline_run_duration(meter: Meter) -> Histogram: + """Duration of a pipeline run grouped by pipeline, state and result""" + return meter.create_histogram( + name=CICD_PIPELINE_RUN_DURATION, + description="Duration of a pipeline run grouped by pipeline, state and result.", + unit="s", + ) + + +CICD_PIPELINE_RUN_ERRORS: Final = "cicd.pipeline.run.errors" +""" +The number of errors encountered in pipeline runs (eg. compile, test failures) +Instrument: counter +Unit: {error} +Note: There might be errors in a pipeline run that are non fatal (eg. they are suppressed) or in a parallel stage multiple stages could have a fatal error. +This means that this error count might not be the same as the count of metric `cicd.pipeline.run.duration` with run result `failure`. +""" + + +def create_cicd_pipeline_run_errors(meter: Meter) -> Counter: + """The number of errors encountered in pipeline runs (eg. compile, test failures)""" + return meter.create_counter( + name=CICD_PIPELINE_RUN_ERRORS, + description="The number of errors encountered in pipeline runs (eg. compile, test failures).", + unit="{error}", + ) + + +CICD_SYSTEM_ERRORS: Final = "cicd.system.errors" +""" +The number of errors in a component of the CI/CD system (eg. controller, scheduler, agent) +Instrument: counter +Unit: {error} +Note: Errors in pipeline run execution are explicitly excluded. Ie a test failure is not counted in this metric. +""" + + +def create_cicd_system_errors(meter: Meter) -> Counter: + """The number of errors in a component of the CI/CD system (eg. controller, scheduler, agent)""" + return meter.create_counter( + name=CICD_SYSTEM_ERRORS, + description="The number of errors in a component of the CI/CD system (eg. controller, scheduler, agent).", + unit="{error}", + ) + + +CICD_WORKER_COUNT: Final = "cicd.worker.count" +""" +The number of workers on the CI/CD system by state +Instrument: updowncounter +Unit: {worker} +""" + + +def create_cicd_worker_count(meter: Meter) -> UpDownCounter: + """The number of workers on the CI/CD system by state""" + return meter.create_up_down_counter( + name=CICD_WORKER_COUNT, + description="The number of workers on the CI/CD system by state.", + unit="{worker}", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/container_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/container_metrics.py new file mode 100644 index 0000000..13c945f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/container_metrics.py @@ -0,0 +1,281 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from collections.abc import Callable, Generator, Iterable, Sequence +from typing import Final + +from opentelemetry.metrics import CallbackOptions, Counter, Meter, ObservableGauge, Observation, UpDownCounter + +# pylint: disable=invalid-name +CallbackT = Callable[[CallbackOptions], Iterable[Observation]] | Generator[Iterable[Observation], CallbackOptions, None] + + +CONTAINER_CPU_TIME: Final = "container.cpu.time" +""" +Total CPU time consumed +Instrument: counter +Unit: s +Note: CPU time consumed by the specific container on all available CPU cores. +""" + + +def create_container_cpu_time(meter: Meter) -> Counter: + """Total CPU time consumed""" + return meter.create_counter( + name=CONTAINER_CPU_TIME, + description="Total CPU time consumed.", + unit="s", + ) + + +CONTAINER_CPU_USAGE: Final = "container.cpu.usage" +""" +Container's CPU usage, measured in CPUs. Range from 0 to the number of allocatable CPUs +Instrument: gauge +Unit: {cpu} +Note: CPU usage of the specific container on all available CPU cores. It is calculated as the change in cumulative CPU time (container.cpu.time) over a measurement interval, divided by the elapsed time: usageCores = (cpuTimeEnd - cpuTimeStart) / elapsedSeconds. +""" + + +def create_container_cpu_usage(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Container's CPU usage, measured in CPUs. Range from 0 to the number of allocatable CPUs""" + return meter.create_observable_gauge( + name=CONTAINER_CPU_USAGE, + callbacks=callbacks, + description="Container's CPU usage, measured in CPUs. Range from 0 to the number of allocatable CPUs.", + unit="{cpu}", + ) + + +CONTAINER_DISK_IO: Final = "container.disk.io" +""" +Disk bytes for the container +Instrument: counter +Unit: By +Note: The total number of bytes read/written successfully (aggregated from all disks). +""" + + +def create_container_disk_io(meter: Meter) -> Counter: + """Disk bytes for the container""" + return meter.create_counter( + name=CONTAINER_DISK_IO, + description="Disk bytes for the container.", + unit="By", + ) + + +CONTAINER_FILESYSTEM_AVAILABLE: Final = "container.filesystem.available" +""" +Container filesystem available bytes +Instrument: updowncounter +Unit: By +Note: In K8s, this metric is derived from the +[FsStats.AvailableBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field +of the [ContainerStats.Rootfs](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#ContainerStats) +of the Kubelet's stats API. +""" + + +def create_container_filesystem_available(meter: Meter) -> UpDownCounter: + """Container filesystem available bytes""" + return meter.create_up_down_counter( + name=CONTAINER_FILESYSTEM_AVAILABLE, + description="Container filesystem available bytes.", + unit="By", + ) + + +CONTAINER_FILESYSTEM_CAPACITY: Final = "container.filesystem.capacity" +""" +Container filesystem capacity +Instrument: updowncounter +Unit: By +Note: In K8s, this metric is derived from the +[FsStats.CapacityBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field +of the [ContainerStats.Rootfs](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#ContainerStats) +of the Kubelet's stats API. +""" + + +def create_container_filesystem_capacity(meter: Meter) -> UpDownCounter: + """Container filesystem capacity""" + return meter.create_up_down_counter( + name=CONTAINER_FILESYSTEM_CAPACITY, + description="Container filesystem capacity.", + unit="By", + ) + + +CONTAINER_FILESYSTEM_USAGE: Final = "container.filesystem.usage" +""" +Container filesystem usage +Instrument: updowncounter +Unit: By +Note: This may not equal capacity - available. + +In K8s, this metric is derived from the +[FsStats.UsedBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field +of the [ContainerStats.Rootfs](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#ContainerStats) +of the Kubelet's stats API. +""" + + +def create_container_filesystem_usage(meter: Meter) -> UpDownCounter: + """Container filesystem usage""" + return meter.create_up_down_counter( + name=CONTAINER_FILESYSTEM_USAGE, + description="Container filesystem usage.", + unit="By", + ) + + +CONTAINER_MEMORY_AVAILABLE: Final = "container.memory.available" +""" +Container memory available +Instrument: updowncounter +Unit: By +Note: Available memory for use. This is defined as the memory limit - workingSetBytes. If memory limit is undefined, the available bytes is omitted. +In general, this metric can be derived from [cadvisor](https://github.com/google/cadvisor/blob/v0.53.0/docs/storage/prometheus.md#prometheus-container-metrics) and by subtracting the `container_memory_working_set_bytes` metric from the `container_spec_memory_limit_bytes` metric. +In K8s, this metric is derived from the [MemoryStats.AvailableBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [ContainerStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#ContainerStats) of the Kubelet's stats API. +""" + + +def create_container_memory_available(meter: Meter) -> UpDownCounter: + """Container memory available""" + return meter.create_up_down_counter( + name=CONTAINER_MEMORY_AVAILABLE, + description="Container memory available.", + unit="By", + ) + + +CONTAINER_MEMORY_PAGING_FAULTS: Final = "container.memory.paging.faults" +""" +Deprecated: Replaced by `container.paging.faults`. +""" + + +def create_container_memory_paging_faults(meter: Meter) -> Counter: + """Deprecated, use `container.paging.faults` instead""" + return meter.create_counter( + name=CONTAINER_MEMORY_PAGING_FAULTS, + description="Deprecated, use `container.paging.faults` instead.", + unit="{fault}", + ) + + +CONTAINER_MEMORY_RSS: Final = "container.memory.rss" +""" +Container memory RSS +Instrument: updowncounter +Unit: By +Note: In general, this metric can be derived from [cadvisor](https://github.com/google/cadvisor/blob/v0.53.0/docs/storage/prometheus.md#prometheus-container-metrics) and specifically the `container_memory_rss` metric. +In K8s, this metric is derived from the [MemoryStats.RSSBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [ContainerStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#ContainerStats) of the Kubelet's stats API. +""" + + +def create_container_memory_rss(meter: Meter) -> UpDownCounter: + """Container memory RSS""" + return meter.create_up_down_counter( + name=CONTAINER_MEMORY_RSS, + description="Container memory RSS.", + unit="By", + ) + + +CONTAINER_MEMORY_USAGE: Final = "container.memory.usage" +""" +Memory usage of the container +Instrument: updowncounter +Unit: By +Note: Current memory usage, including all memory regardless of when it was accessed. +In general, this metric can be derived from [cadvisor](https://github.com/google/cadvisor/blob/v0.53.0/docs/storage/prometheus.md#prometheus-container-metrics) and specifically the `container_memory_usage_bytes` metric. +In K8s, this metric is derived from the [MemoryStats.UsageBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [ContainerStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#ContainerStats) of the Kubelet's stats API. +""" + + +def create_container_memory_usage(meter: Meter) -> UpDownCounter: + """Memory usage of the container""" + return meter.create_up_down_counter( + name=CONTAINER_MEMORY_USAGE, + description="Memory usage of the container.", + unit="By", + ) + + +CONTAINER_MEMORY_WORKING_SET: Final = "container.memory.working_set" +""" +Container memory working set +Instrument: updowncounter +Unit: By +Note: In general, this metric can be derived from [cadvisor](https://github.com/google/cadvisor/blob/v0.53.0/docs/storage/prometheus.md#prometheus-container-metrics) and specifically the `container_memory_working_set_bytes` metric. +In K8s, this metric is derived from the [MemoryStats.WorkingSetBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [ContainerStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#ContainerStats) of the Kubelet's stats API. +""" + + +def create_container_memory_working_set(meter: Meter) -> UpDownCounter: + """Container memory working set""" + return meter.create_up_down_counter( + name=CONTAINER_MEMORY_WORKING_SET, + description="Container memory working set.", + unit="By", + ) + + +CONTAINER_NETWORK_IO: Final = "container.network.io" +""" +Network bytes for the container +Instrument: counter +Unit: By +Note: The number of bytes sent/received on all network interfaces by the container. +""" + + +def create_container_network_io(meter: Meter) -> Counter: + """Network bytes for the container""" + return meter.create_counter( + name=CONTAINER_NETWORK_IO, + description="Network bytes for the container.", + unit="By", + ) + + +CONTAINER_PAGING_FAULTS: Final = "container.paging.faults" +""" +Container memory paging faults +Instrument: counter +Unit: {fault} +Note: In general, this metric can be derived from [cadvisor](https://github.com/google/cadvisor/blob/v0.53.0/docs/storage/prometheus.md#prometheus-container-metrics) and specifically the `container_memory_failures_total{failure_type=pgfault, scope=container}` and `container_memory_failures_total{failure_type=pgmajfault, scope=container}`metric. +In K8s, this metric is derived from the [MemoryStats.PageFaults](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) and [MemoryStats.MajorPageFaults](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [ContainerStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#ContainerStats) of the Kubelet's stats API. +""" + + +def create_container_paging_faults(meter: Meter) -> Counter: + """Container memory paging faults""" + return meter.create_counter( + name=CONTAINER_PAGING_FAULTS, + description="Container memory paging faults.", + unit="{fault}", + ) + + +CONTAINER_UPTIME: Final = "container.uptime" +""" +The time the container has been running +Instrument: gauge +Unit: s +Note: Instrumentations SHOULD use a gauge with type `double` and measure uptime in seconds as a floating point number with the highest precision available. +The actual accuracy would depend on the instrumentation and operating system. +""" + + +def create_container_uptime(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """The time the container has been running""" + return meter.create_observable_gauge( + name=CONTAINER_UPTIME, + callbacks=callbacks, + description="The time the container has been running.", + unit="s", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/cpu_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/cpu_metrics.py new file mode 100644 index 0000000..c532c26 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/cpu_metrics.py @@ -0,0 +1,58 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from collections.abc import Callable, Generator, Iterable, Sequence +from typing import Final + +from opentelemetry.metrics import CallbackOptions, Counter, Meter, ObservableGauge, Observation + +# pylint: disable=invalid-name +CallbackT = Callable[[CallbackOptions], Iterable[Observation]] | Generator[Iterable[Observation], CallbackOptions, None] + + +CPU_FREQUENCY: Final = "cpu.frequency" +""" +Deprecated: Replaced by `system.cpu.frequency`. +""" + + +def create_cpu_frequency(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Deprecated. Use `system.cpu.frequency` instead""" + return meter.create_observable_gauge( + name=CPU_FREQUENCY, + callbacks=callbacks, + description="Deprecated. Use `system.cpu.frequency` instead.", + unit="{Hz}", + ) + + +CPU_TIME: Final = "cpu.time" +""" +Deprecated: Replaced by `system.cpu.time`. +""" + + +def create_cpu_time(meter: Meter) -> Counter: + """Deprecated. Use `system.cpu.time` instead""" + return meter.create_counter( + name=CPU_TIME, + description="Deprecated. Use `system.cpu.time` instead.", + unit="s", + ) + + +CPU_UTILIZATION: Final = "cpu.utilization" +""" +Deprecated: Replaced by `system.cpu.utilization`. +""" + + +def create_cpu_utilization(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Deprecated. Use `system.cpu.utilization` instead""" + return meter.create_observable_gauge( + name=CPU_UTILIZATION, + callbacks=callbacks, + description="Deprecated. Use `system.cpu.utilization` instead.", + unit="1", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/cpython_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/cpython_metrics.py new file mode 100644 index 0000000..64670af --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/cpython_metrics.py @@ -0,0 +1,60 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Counter, Meter + +CPYTHON_GC_COLLECTED_OBJECTS: Final = "cpython.gc.collected_objects" +""" +The total number of objects collected inside a generation since interpreter start +Instrument: counter +Unit: {object} +Note: This metric reports data from [`gc.stats()`](https://docs.python.org/3/library/gc.html#gc.get_stats). +""" + + +def create_cpython_gc_collected_objects(meter: Meter) -> Counter: + """The total number of objects collected inside a generation since interpreter start""" + return meter.create_counter( + name=CPYTHON_GC_COLLECTED_OBJECTS, + description="The total number of objects collected inside a generation since interpreter start.", + unit="{object}", + ) + + +CPYTHON_GC_COLLECTIONS: Final = "cpython.gc.collections" +""" +The number of times a generation was collected since interpreter start +Instrument: counter +Unit: {collection} +Note: This metric reports data from [`gc.stats()`](https://docs.python.org/3/library/gc.html#gc.get_stats). +""" + + +def create_cpython_gc_collections(meter: Meter) -> Counter: + """The number of times a generation was collected since interpreter start""" + return meter.create_counter( + name=CPYTHON_GC_COLLECTIONS, + description="The number of times a generation was collected since interpreter start.", + unit="{collection}", + ) + + +CPYTHON_GC_UNCOLLECTABLE_OBJECTS: Final = "cpython.gc.uncollectable_objects" +""" +The total number of objects which were found to be uncollectable inside a generation since interpreter start +Instrument: counter +Unit: {object} +Note: This metric reports data from [`gc.stats()`](https://docs.python.org/3/library/gc.html#gc.get_stats). +""" + + +def create_cpython_gc_uncollectable_objects(meter: Meter) -> Counter: + """The total number of objects which were found to be uncollectable inside a generation since interpreter start""" + return meter.create_counter( + name=CPYTHON_GC_UNCOLLECTABLE_OBJECTS, + description="The total number of objects which were found to be uncollectable inside a generation since interpreter start.", + unit="{object}", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/db_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/db_metrics.py new file mode 100644 index 0000000..e7ae0f0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/db_metrics.py @@ -0,0 +1,356 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Counter, Histogram, Meter, UpDownCounter + +DB_CLIENT_CONNECTION_COUNT: Final = "db.client.connection.count" +""" +The number of connections that are currently in state described by the `state` attribute +Instrument: updowncounter +Unit: {connection} +""" + + +def create_db_client_connection_count(meter: Meter) -> UpDownCounter: + """The number of connections that are currently in state described by the `state` attribute""" + return meter.create_up_down_counter( + name=DB_CLIENT_CONNECTION_COUNT, + description="The number of connections that are currently in state described by the `state` attribute.", + unit="{connection}", + ) + + +DB_CLIENT_CONNECTION_CREATE_TIME: Final = "db.client.connection.create_time" +""" +The time it took to create a new connection +Instrument: histogram +Unit: s +""" + + +def create_db_client_connection_create_time(meter: Meter) -> Histogram: + """The time it took to create a new connection""" + return meter.create_histogram( + name=DB_CLIENT_CONNECTION_CREATE_TIME, + description="The time it took to create a new connection.", + unit="s", + ) + + +DB_CLIENT_CONNECTION_IDLE_MAX: Final = "db.client.connection.idle.max" +""" +The maximum number of idle open connections allowed +Instrument: updowncounter +Unit: {connection} +""" + + +def create_db_client_connection_idle_max(meter: Meter) -> UpDownCounter: + """The maximum number of idle open connections allowed""" + return meter.create_up_down_counter( + name=DB_CLIENT_CONNECTION_IDLE_MAX, + description="The maximum number of idle open connections allowed.", + unit="{connection}", + ) + + +DB_CLIENT_CONNECTION_IDLE_MIN: Final = "db.client.connection.idle.min" +""" +The minimum number of idle open connections allowed +Instrument: updowncounter +Unit: {connection} +""" + + +def create_db_client_connection_idle_min(meter: Meter) -> UpDownCounter: + """The minimum number of idle open connections allowed""" + return meter.create_up_down_counter( + name=DB_CLIENT_CONNECTION_IDLE_MIN, + description="The minimum number of idle open connections allowed.", + unit="{connection}", + ) + + +DB_CLIENT_CONNECTION_MAX: Final = "db.client.connection.max" +""" +The maximum number of open connections allowed +Instrument: updowncounter +Unit: {connection} +""" + + +def create_db_client_connection_max(meter: Meter) -> UpDownCounter: + """The maximum number of open connections allowed""" + return meter.create_up_down_counter( + name=DB_CLIENT_CONNECTION_MAX, + description="The maximum number of open connections allowed.", + unit="{connection}", + ) + + +DB_CLIENT_CONNECTION_PENDING_REQUESTS: Final = "db.client.connection.pending_requests" +""" +The number of current pending requests for an open connection +Instrument: updowncounter +Unit: {request} +""" + + +def create_db_client_connection_pending_requests(meter: Meter) -> UpDownCounter: + """The number of current pending requests for an open connection""" + return meter.create_up_down_counter( + name=DB_CLIENT_CONNECTION_PENDING_REQUESTS, + description="The number of current pending requests for an open connection.", + unit="{request}", + ) + + +DB_CLIENT_CONNECTION_TIMEOUTS: Final = "db.client.connection.timeouts" +""" +The number of connection timeouts that have occurred trying to obtain a connection from the pool +Instrument: counter +Unit: {timeout} +""" + + +def create_db_client_connection_timeouts(meter: Meter) -> Counter: + """The number of connection timeouts that have occurred trying to obtain a connection from the pool""" + return meter.create_counter( + name=DB_CLIENT_CONNECTION_TIMEOUTS, + description="The number of connection timeouts that have occurred trying to obtain a connection from the pool.", + unit="{timeout}", + ) + + +DB_CLIENT_CONNECTION_USE_TIME: Final = "db.client.connection.use_time" +""" +The time between borrowing a connection and returning it to the pool +Instrument: histogram +Unit: s +""" + + +def create_db_client_connection_use_time(meter: Meter) -> Histogram: + """The time between borrowing a connection and returning it to the pool""" + return meter.create_histogram( + name=DB_CLIENT_CONNECTION_USE_TIME, + description="The time between borrowing a connection and returning it to the pool.", + unit="s", + ) + + +DB_CLIENT_CONNECTION_WAIT_TIME: Final = "db.client.connection.wait_time" +""" +The time it took to obtain an open connection from the pool +Instrument: histogram +Unit: s +""" + + +def create_db_client_connection_wait_time(meter: Meter) -> Histogram: + """The time it took to obtain an open connection from the pool""" + return meter.create_histogram( + name=DB_CLIENT_CONNECTION_WAIT_TIME, + description="The time it took to obtain an open connection from the pool.", + unit="s", + ) + + +DB_CLIENT_CONNECTIONS_CREATE_TIME: Final = "db.client.connections.create_time" +""" +Deprecated: Replaced by `db.client.connection.create_time` with unit `s`. +""" + + +def create_db_client_connections_create_time(meter: Meter) -> Histogram: + """Deprecated, use `db.client.connection.create_time` instead. Note: the unit also changed from `ms` to `s`""" + return meter.create_histogram( + name=DB_CLIENT_CONNECTIONS_CREATE_TIME, + description="Deprecated, use `db.client.connection.create_time` instead. Note: the unit also changed from `ms` to `s`.", + unit="ms", + ) + + +DB_CLIENT_CONNECTIONS_IDLE_MAX: Final = "db.client.connections.idle.max" +""" +Deprecated: Replaced by `db.client.connection.idle.max`. +""" + + +def create_db_client_connections_idle_max(meter: Meter) -> UpDownCounter: + """Deprecated, use `db.client.connection.idle.max` instead""" + return meter.create_up_down_counter( + name=DB_CLIENT_CONNECTIONS_IDLE_MAX, + description="Deprecated, use `db.client.connection.idle.max` instead.", + unit="{connection}", + ) + + +DB_CLIENT_CONNECTIONS_IDLE_MIN: Final = "db.client.connections.idle.min" +""" +Deprecated: Replaced by `db.client.connection.idle.min`. +""" + + +def create_db_client_connections_idle_min(meter: Meter) -> UpDownCounter: + """Deprecated, use `db.client.connection.idle.min` instead""" + return meter.create_up_down_counter( + name=DB_CLIENT_CONNECTIONS_IDLE_MIN, + description="Deprecated, use `db.client.connection.idle.min` instead.", + unit="{connection}", + ) + + +DB_CLIENT_CONNECTIONS_MAX: Final = "db.client.connections.max" +""" +Deprecated: Replaced by `db.client.connection.max`. +""" + + +def create_db_client_connections_max(meter: Meter) -> UpDownCounter: + """Deprecated, use `db.client.connection.max` instead""" + return meter.create_up_down_counter( + name=DB_CLIENT_CONNECTIONS_MAX, + description="Deprecated, use `db.client.connection.max` instead.", + unit="{connection}", + ) + + +DB_CLIENT_CONNECTIONS_PENDING_REQUESTS: Final = "db.client.connections.pending_requests" +""" +Deprecated: Replaced by `db.client.connection.pending_requests`. +""" + + +def create_db_client_connections_pending_requests(meter: Meter) -> UpDownCounter: + """Deprecated, use `db.client.connection.pending_requests` instead""" + return meter.create_up_down_counter( + name=DB_CLIENT_CONNECTIONS_PENDING_REQUESTS, + description="Deprecated, use `db.client.connection.pending_requests` instead.", + unit="{request}", + ) + + +DB_CLIENT_CONNECTIONS_TIMEOUTS: Final = "db.client.connections.timeouts" +""" +Deprecated: Replaced by `db.client.connection.timeouts`. +""" + + +def create_db_client_connections_timeouts(meter: Meter) -> Counter: + """Deprecated, use `db.client.connection.timeouts` instead""" + return meter.create_counter( + name=DB_CLIENT_CONNECTIONS_TIMEOUTS, + description="Deprecated, use `db.client.connection.timeouts` instead.", + unit="{timeout}", + ) + + +DB_CLIENT_CONNECTIONS_USAGE: Final = "db.client.connections.usage" +""" +Deprecated: Replaced by `db.client.connection.count`. +""" + + +def create_db_client_connections_usage(meter: Meter) -> UpDownCounter: + """Deprecated, use `db.client.connection.count` instead""" + return meter.create_up_down_counter( + name=DB_CLIENT_CONNECTIONS_USAGE, + description="Deprecated, use `db.client.connection.count` instead.", + unit="{connection}", + ) + + +DB_CLIENT_CONNECTIONS_USE_TIME: Final = "db.client.connections.use_time" +""" +Deprecated: Replaced by `db.client.connection.use_time` with unit `s`. +""" + + +def create_db_client_connections_use_time(meter: Meter) -> Histogram: + """Deprecated, use `db.client.connection.use_time` instead. Note: the unit also changed from `ms` to `s`""" + return meter.create_histogram( + name=DB_CLIENT_CONNECTIONS_USE_TIME, + description="Deprecated, use `db.client.connection.use_time` instead. Note: the unit also changed from `ms` to `s`.", + unit="ms", + ) + + +DB_CLIENT_CONNECTIONS_WAIT_TIME: Final = "db.client.connections.wait_time" +""" +Deprecated: Replaced by `db.client.connection.wait_time` with unit `s`. +""" + + +def create_db_client_connections_wait_time(meter: Meter) -> Histogram: + """Deprecated, use `db.client.connection.wait_time` instead. Note: the unit also changed from `ms` to `s`""" + return meter.create_histogram( + name=DB_CLIENT_CONNECTIONS_WAIT_TIME, + description="Deprecated, use `db.client.connection.wait_time` instead. Note: the unit also changed from `ms` to `s`.", + unit="ms", + ) + + +DB_CLIENT_COSMOSDB_ACTIVE_INSTANCE_COUNT: Final = "db.client.cosmosdb.active_instance.count" +""" +Deprecated: Replaced by `azure.cosmosdb.client.active_instance.count`. +""" + + +def create_db_client_cosmosdb_active_instance_count(meter: Meter) -> UpDownCounter: + """Deprecated, use `azure.cosmosdb.client.active_instance.count` instead""" + return meter.create_up_down_counter( + name=DB_CLIENT_COSMOSDB_ACTIVE_INSTANCE_COUNT, + description="Deprecated, use `azure.cosmosdb.client.active_instance.count` instead.", + unit="{instance}", + ) + + +DB_CLIENT_COSMOSDB_OPERATION_REQUEST_CHARGE: Final = "db.client.cosmosdb.operation.request_charge" +""" +Deprecated: Replaced by `azure.cosmosdb.client.operation.request_charge`. +""" + + +def create_db_client_cosmosdb_operation_request_charge(meter: Meter) -> Histogram: + """Deprecated, use `azure.cosmosdb.client.operation.request_charge` instead""" + return meter.create_histogram( + name=DB_CLIENT_COSMOSDB_OPERATION_REQUEST_CHARGE, + description="Deprecated, use `azure.cosmosdb.client.operation.request_charge` instead.", + unit="{request_unit}", + ) + + +DB_CLIENT_OPERATION_DURATION: Final = "db.client.operation.duration" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.metrics.db_metrics.DB_CLIENT_OPERATION_DURATION`. +""" + + +def create_db_client_operation_duration(meter: Meter) -> Histogram: + """Duration of database client operations""" + return meter.create_histogram( + name=DB_CLIENT_OPERATION_DURATION, + description="Duration of database client operations.", + unit="s", + ) + + +DB_CLIENT_RESPONSE_RETURNED_ROWS: Final = "db.client.response.returned_rows" +""" +The actual number of records returned by the database operation +Instrument: histogram +Unit: {row} +""" + + +def create_db_client_response_returned_rows(meter: Meter) -> Histogram: + """The actual number of records returned by the database operation""" + return meter.create_histogram( + name=DB_CLIENT_RESPONSE_RETURNED_ROWS, + description="The actual number of records returned by the database operation.", + unit="{row}", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/dns_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/dns_metrics.py new file mode 100644 index 0000000..1ff3bdf --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/dns_metrics.py @@ -0,0 +1,23 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Histogram, Meter + +DNS_LOOKUP_DURATION: Final = "dns.lookup.duration" +""" +Measures the time taken to perform a DNS lookup +Instrument: histogram +Unit: s +""" + + +def create_dns_lookup_duration(meter: Meter) -> Histogram: + """Measures the time taken to perform a DNS lookup""" + return meter.create_histogram( + name=DNS_LOOKUP_DURATION, + description="Measures the time taken to perform a DNS lookup.", + unit="s", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/faas_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/faas_metrics.py new file mode 100644 index 0000000..894e778 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/faas_metrics.py @@ -0,0 +1,159 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Counter, Histogram, Meter + +FAAS_COLDSTARTS: Final = "faas.coldstarts" +""" +Number of invocation cold starts +Instrument: counter +Unit: {coldstart} +""" + + +def create_faas_coldstarts(meter: Meter) -> Counter: + """Number of invocation cold starts""" + return meter.create_counter( + name=FAAS_COLDSTARTS, + description="Number of invocation cold starts.", + unit="{coldstart}", + ) + + +FAAS_CPU_USAGE: Final = "faas.cpu_usage" +""" +Distribution of CPU usage per invocation +Instrument: histogram +Unit: s +""" + + +def create_faas_cpu_usage(meter: Meter) -> Histogram: + """Distribution of CPU usage per invocation""" + return meter.create_histogram( + name=FAAS_CPU_USAGE, + description="Distribution of CPU usage per invocation.", + unit="s", + ) + + +FAAS_ERRORS: Final = "faas.errors" +""" +Number of invocation errors +Instrument: counter +Unit: {error} +""" + + +def create_faas_errors(meter: Meter) -> Counter: + """Number of invocation errors""" + return meter.create_counter( + name=FAAS_ERRORS, + description="Number of invocation errors.", + unit="{error}", + ) + + +FAAS_INIT_DURATION: Final = "faas.init_duration" +""" +Measures the duration of the function's initialization, such as a cold start +Instrument: histogram +Unit: s +""" + + +def create_faas_init_duration(meter: Meter) -> Histogram: + """Measures the duration of the function's initialization, such as a cold start""" + return meter.create_histogram( + name=FAAS_INIT_DURATION, + description="Measures the duration of the function's initialization, such as a cold start.", + unit="s", + ) + + +FAAS_INVOCATIONS: Final = "faas.invocations" +""" +Number of successful invocations +Instrument: counter +Unit: {invocation} +""" + + +def create_faas_invocations(meter: Meter) -> Counter: + """Number of successful invocations""" + return meter.create_counter( + name=FAAS_INVOCATIONS, + description="Number of successful invocations.", + unit="{invocation}", + ) + + +FAAS_INVOKE_DURATION: Final = "faas.invoke_duration" +""" +Measures the duration of the function's logic execution +Instrument: histogram +Unit: s +""" + + +def create_faas_invoke_duration(meter: Meter) -> Histogram: + """Measures the duration of the function's logic execution""" + return meter.create_histogram( + name=FAAS_INVOKE_DURATION, + description="Measures the duration of the function's logic execution.", + unit="s", + ) + + +FAAS_MEM_USAGE: Final = "faas.mem_usage" +""" +Distribution of max memory usage per invocation +Instrument: histogram +Unit: By +""" + + +def create_faas_mem_usage(meter: Meter) -> Histogram: + """Distribution of max memory usage per invocation""" + return meter.create_histogram( + name=FAAS_MEM_USAGE, + description="Distribution of max memory usage per invocation.", + unit="By", + ) + + +FAAS_NET_IO: Final = "faas.net_io" +""" +Distribution of net I/O usage per invocation +Instrument: histogram +Unit: By +""" + + +def create_faas_net_io(meter: Meter) -> Histogram: + """Distribution of net I/O usage per invocation""" + return meter.create_histogram( + name=FAAS_NET_IO, + description="Distribution of net I/O usage per invocation.", + unit="By", + ) + + +FAAS_TIMEOUTS: Final = "faas.timeouts" +""" +Number of invocation timeouts +Instrument: counter +Unit: {timeout} +""" + + +def create_faas_timeouts(meter: Meter) -> Counter: + """Number of invocation timeouts""" + return meter.create_counter( + name=FAAS_TIMEOUTS, + description="Number of invocation timeouts.", + unit="{timeout}", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/gen_ai_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/gen_ai_metrics.py new file mode 100644 index 0000000..e91a2d3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/gen_ai_metrics.py @@ -0,0 +1,111 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Histogram, Meter + +GEN_AI_CLIENT_OPERATION_DURATION: Final = "gen_ai.client.operation.duration" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +def create_gen_ai_client_operation_duration(meter: Meter) -> Histogram: + """GenAI operation duration""" + return meter.create_histogram( + name=GEN_AI_CLIENT_OPERATION_DURATION, + description="GenAI operation duration.", + unit="s", + ) + + +GEN_AI_CLIENT_OPERATION_TIME_PER_OUTPUT_CHUNK: Final = "gen_ai.client.operation.time_per_output_chunk" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +def create_gen_ai_client_operation_time_per_output_chunk(meter: Meter) -> Histogram: + """Time per output chunk, recorded for each chunk received after the first one, measured as the time elapsed from the end of the previous chunk to the end of the current chunk""" + return meter.create_histogram( + name=GEN_AI_CLIENT_OPERATION_TIME_PER_OUTPUT_CHUNK, + description="Time per output chunk, recorded for each chunk received after the first one, measured as the time elapsed from the end of the previous chunk to the end of the current chunk.", + unit="s", + ) + + +GEN_AI_CLIENT_OPERATION_TIME_TO_FIRST_CHUNK: Final = "gen_ai.client.operation.time_to_first_chunk" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +def create_gen_ai_client_operation_time_to_first_chunk(meter: Meter) -> Histogram: + """Time to receive the first chunk, measured from when the client issues the generation request to when the first chunk is received in the response stream""" + return meter.create_histogram( + name=GEN_AI_CLIENT_OPERATION_TIME_TO_FIRST_CHUNK, + description="Time to receive the first chunk, measured from when the client issues the generation request to when the first chunk is received in the response stream.", + unit="s", + ) + + +GEN_AI_CLIENT_TOKEN_USAGE: Final = "gen_ai.client.token.usage" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +def create_gen_ai_client_token_usage(meter: Meter) -> Histogram: + """Number of input and output tokens used""" + return meter.create_histogram( + name=GEN_AI_CLIENT_TOKEN_USAGE, + description="Number of input and output tokens used.", + unit="{token}", + ) + + +GEN_AI_SERVER_REQUEST_DURATION: Final = "gen_ai.server.request.duration" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +def create_gen_ai_server_request_duration(meter: Meter) -> Histogram: + """Generative AI server request duration such as time-to-last byte or last output token""" + return meter.create_histogram( + name=GEN_AI_SERVER_REQUEST_DURATION, + description="Generative AI server request duration such as time-to-last byte or last output token.", + unit="s", + ) + + +GEN_AI_SERVER_TIME_PER_OUTPUT_TOKEN: Final = "gen_ai.server.time_per_output_token" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +def create_gen_ai_server_time_per_output_token(meter: Meter) -> Histogram: + """Time per output token generated after the first token for successful responses""" + return meter.create_histogram( + name=GEN_AI_SERVER_TIME_PER_OUTPUT_TOKEN, + description="Time per output token generated after the first token for successful responses.", + unit="s", + ) + + +GEN_AI_SERVER_TIME_TO_FIRST_TOKEN: Final = "gen_ai.server.time_to_first_token" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +def create_gen_ai_server_time_to_first_token(meter: Meter) -> Histogram: + """Time to generate first token for successful responses""" + return meter.create_histogram( + name=GEN_AI_SERVER_TIME_TO_FIRST_TOKEN, + description="Time to generate first token for successful responses.", + unit="s", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/http_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/http_metrics.py new file mode 100644 index 0000000..4ea7198 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/http_metrics.py @@ -0,0 +1,176 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Histogram, Meter, UpDownCounter + +HTTP_CLIENT_ACTIVE_REQUESTS: Final = "http.client.active_requests" +""" +Number of active HTTP requests +Instrument: updowncounter +Unit: {request} +""" + + +def create_http_client_active_requests(meter: Meter) -> UpDownCounter: + """Number of active HTTP requests""" + return meter.create_up_down_counter( + name=HTTP_CLIENT_ACTIVE_REQUESTS, + description="Number of active HTTP requests.", + unit="{request}", + ) + + +HTTP_CLIENT_CONNECTION_DURATION: Final = "http.client.connection.duration" +""" +The duration of the successfully established outbound HTTP connections +Instrument: histogram +Unit: s +""" + + +def create_http_client_connection_duration(meter: Meter) -> Histogram: + """The duration of the successfully established outbound HTTP connections""" + return meter.create_histogram( + name=HTTP_CLIENT_CONNECTION_DURATION, + description="The duration of the successfully established outbound HTTP connections.", + unit="s", + ) + + +HTTP_CLIENT_OPEN_CONNECTIONS: Final = "http.client.open_connections" +""" +Number of outbound HTTP connections that are currently active or idle on the client +Instrument: updowncounter +Unit: {connection} +""" + + +def create_http_client_open_connections(meter: Meter) -> UpDownCounter: + """Number of outbound HTTP connections that are currently active or idle on the client""" + return meter.create_up_down_counter( + name=HTTP_CLIENT_OPEN_CONNECTIONS, + description="Number of outbound HTTP connections that are currently active or idle on the client.", + unit="{connection}", + ) + + +HTTP_CLIENT_REQUEST_BODY_SIZE: Final = "http.client.request.body.size" +""" +Size of HTTP client request bodies +Instrument: histogram +Unit: By +Note: The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. +""" + + +def create_http_client_request_body_size(meter: Meter) -> Histogram: + """Size of HTTP client request bodies""" + return meter.create_histogram( + name=HTTP_CLIENT_REQUEST_BODY_SIZE, + description="Size of HTTP client request bodies.", + unit="By", + ) + + +HTTP_CLIENT_REQUEST_DURATION: Final = "http.client.request.duration" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.metrics.http_metrics.HTTP_CLIENT_REQUEST_DURATION`. +""" + + +def create_http_client_request_duration(meter: Meter) -> Histogram: + """Duration of HTTP client requests""" + return meter.create_histogram( + name=HTTP_CLIENT_REQUEST_DURATION, + description="Duration of HTTP client requests.", + unit="s", + ) + + +HTTP_CLIENT_RESPONSE_BODY_SIZE: Final = "http.client.response.body.size" +""" +Size of HTTP client response bodies +Instrument: histogram +Unit: By +Note: The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. +""" + + +def create_http_client_response_body_size(meter: Meter) -> Histogram: + """Size of HTTP client response bodies""" + return meter.create_histogram( + name=HTTP_CLIENT_RESPONSE_BODY_SIZE, + description="Size of HTTP client response bodies.", + unit="By", + ) + + +HTTP_SERVER_ACTIVE_REQUESTS: Final = "http.server.active_requests" +""" +Number of active HTTP server requests +Instrument: updowncounter +Unit: {request} +""" + + +def create_http_server_active_requests(meter: Meter) -> UpDownCounter: + """Number of active HTTP server requests""" + return meter.create_up_down_counter( + name=HTTP_SERVER_ACTIVE_REQUESTS, + description="Number of active HTTP server requests.", + unit="{request}", + ) + + +HTTP_SERVER_REQUEST_BODY_SIZE: Final = "http.server.request.body.size" +""" +Size of HTTP server request bodies +Instrument: histogram +Unit: By +Note: The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. +""" + + +def create_http_server_request_body_size(meter: Meter) -> Histogram: + """Size of HTTP server request bodies""" + return meter.create_histogram( + name=HTTP_SERVER_REQUEST_BODY_SIZE, + description="Size of HTTP server request bodies.", + unit="By", + ) + + +HTTP_SERVER_REQUEST_DURATION: Final = "http.server.request.duration" +""" +Deprecated in favor of stable :py:const:`opentelemetry.semconv.metrics.http_metrics.HTTP_SERVER_REQUEST_DURATION`. +""" + + +def create_http_server_request_duration(meter: Meter) -> Histogram: + """Duration of HTTP server requests""" + return meter.create_histogram( + name=HTTP_SERVER_REQUEST_DURATION, + description="Duration of HTTP server requests.", + unit="s", + ) + + +HTTP_SERVER_RESPONSE_BODY_SIZE: Final = "http.server.response.body.size" +""" +Size of HTTP server response bodies +Instrument: histogram +Unit: By +Note: The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. +""" + + +def create_http_server_response_body_size(meter: Meter) -> Histogram: + """Size of HTTP server response bodies""" + return meter.create_histogram( + name=HTTP_SERVER_RESPONSE_BODY_SIZE, + description="Size of HTTP server response bodies.", + unit="By", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/hw_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/hw_metrics.py new file mode 100644 index 0000000..b88bfdf --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/hw_metrics.py @@ -0,0 +1,755 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from collections.abc import Callable, Generator, Iterable, Sequence +from typing import Final + +from opentelemetry.metrics import CallbackOptions, Counter, Meter, ObservableGauge, Observation, UpDownCounter + +# pylint: disable=invalid-name +CallbackT = Callable[[CallbackOptions], Iterable[Observation]] | Generator[Iterable[Observation], CallbackOptions, None] + + +HW_BATTERY_CHARGE: Final = "hw.battery.charge" +""" +Remaining fraction of battery charge +Instrument: gauge +Unit: 1 +""" + + +def create_hw_battery_charge(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Remaining fraction of battery charge""" + return meter.create_observable_gauge( + name=HW_BATTERY_CHARGE, + callbacks=callbacks, + description="Remaining fraction of battery charge.", + unit="1", + ) + + +HW_BATTERY_CHARGE_LIMIT: Final = "hw.battery.charge.limit" +""" +Lower limit of battery charge fraction to ensure proper operation +Instrument: gauge +Unit: 1 +""" + + +def create_hw_battery_charge_limit(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Lower limit of battery charge fraction to ensure proper operation""" + return meter.create_observable_gauge( + name=HW_BATTERY_CHARGE_LIMIT, + callbacks=callbacks, + description="Lower limit of battery charge fraction to ensure proper operation.", + unit="1", + ) + + +HW_BATTERY_TIME_LEFT: Final = "hw.battery.time_left" +""" +Time left before battery is completely charged or discharged +Instrument: gauge +Unit: s +""" + + +def create_hw_battery_time_left(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Time left before battery is completely charged or discharged""" + return meter.create_observable_gauge( + name=HW_BATTERY_TIME_LEFT, + callbacks=callbacks, + description="Time left before battery is completely charged or discharged.", + unit="s", + ) + + +HW_CPU_SPEED: Final = "hw.cpu.speed" +""" +CPU current frequency +Instrument: gauge +Unit: Hz +""" + + +def create_hw_cpu_speed(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """CPU current frequency""" + return meter.create_observable_gauge( + name=HW_CPU_SPEED, + callbacks=callbacks, + description="CPU current frequency.", + unit="Hz", + ) + + +HW_CPU_SPEED_LIMIT: Final = "hw.cpu.speed.limit" +""" +CPU maximum frequency +Instrument: gauge +Unit: Hz +""" + + +def create_hw_cpu_speed_limit(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """CPU maximum frequency""" + return meter.create_observable_gauge( + name=HW_CPU_SPEED_LIMIT, + callbacks=callbacks, + description="CPU maximum frequency.", + unit="Hz", + ) + + +HW_ENERGY: Final = "hw.energy" +""" +Energy consumed by the component +Instrument: counter +Unit: J +""" + + +def create_hw_energy(meter: Meter) -> Counter: + """Energy consumed by the component""" + return meter.create_counter( + name=HW_ENERGY, + description="Energy consumed by the component.", + unit="J", + ) + + +HW_ERRORS: Final = "hw.errors" +""" +Number of errors encountered by the component +Instrument: counter +Unit: {error} +""" + + +def create_hw_errors(meter: Meter) -> Counter: + """Number of errors encountered by the component""" + return meter.create_counter( + name=HW_ERRORS, + description="Number of errors encountered by the component.", + unit="{error}", + ) + + +HW_FAN_SPEED: Final = "hw.fan.speed" +""" +Fan speed in revolutions per minute +Instrument: gauge +Unit: rpm +""" + + +def create_hw_fan_speed(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Fan speed in revolutions per minute""" + return meter.create_observable_gauge( + name=HW_FAN_SPEED, + callbacks=callbacks, + description="Fan speed in revolutions per minute.", + unit="rpm", + ) + + +HW_FAN_SPEED_LIMIT: Final = "hw.fan.speed.limit" +""" +Speed limit in rpm +Instrument: gauge +Unit: rpm +""" + + +def create_hw_fan_speed_limit(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Speed limit in rpm""" + return meter.create_observable_gauge( + name=HW_FAN_SPEED_LIMIT, + callbacks=callbacks, + description="Speed limit in rpm.", + unit="rpm", + ) + + +HW_FAN_SPEED_RATIO: Final = "hw.fan.speed_ratio" +""" +Fan speed expressed as a fraction of its maximum speed +Instrument: gauge +Unit: 1 +""" + + +def create_hw_fan_speed_ratio(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Fan speed expressed as a fraction of its maximum speed""" + return meter.create_observable_gauge( + name=HW_FAN_SPEED_RATIO, + callbacks=callbacks, + description="Fan speed expressed as a fraction of its maximum speed.", + unit="1", + ) + + +HW_GPU_IO: Final = "hw.gpu.io" +""" +Received and transmitted bytes by the GPU +Instrument: counter +Unit: By +""" + + +def create_hw_gpu_io(meter: Meter) -> Counter: + """Received and transmitted bytes by the GPU""" + return meter.create_counter( + name=HW_GPU_IO, + description="Received and transmitted bytes by the GPU.", + unit="By", + ) + + +HW_GPU_MEMORY_LIMIT: Final = "hw.gpu.memory.limit" +""" +Size of the GPU memory +Instrument: updowncounter +Unit: By +""" + + +def create_hw_gpu_memory_limit(meter: Meter) -> UpDownCounter: + """Size of the GPU memory""" + return meter.create_up_down_counter( + name=HW_GPU_MEMORY_LIMIT, + description="Size of the GPU memory.", + unit="By", + ) + + +HW_GPU_MEMORY_USAGE: Final = "hw.gpu.memory.usage" +""" +GPU memory used +Instrument: updowncounter +Unit: By +""" + + +def create_hw_gpu_memory_usage(meter: Meter) -> UpDownCounter: + """GPU memory used""" + return meter.create_up_down_counter( + name=HW_GPU_MEMORY_USAGE, + description="GPU memory used.", + unit="By", + ) + + +HW_GPU_MEMORY_UTILIZATION: Final = "hw.gpu.memory.utilization" +""" +Fraction of GPU memory used +Instrument: gauge +Unit: 1 +""" + + +def create_hw_gpu_memory_utilization(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Fraction of GPU memory used""" + return meter.create_observable_gauge( + name=HW_GPU_MEMORY_UTILIZATION, + callbacks=callbacks, + description="Fraction of GPU memory used.", + unit="1", + ) + + +HW_GPU_UTILIZATION: Final = "hw.gpu.utilization" +""" +Fraction of time spent in a specific task +Instrument: gauge +Unit: 1 +""" + + +def create_hw_gpu_utilization(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Fraction of time spent in a specific task""" + return meter.create_observable_gauge( + name=HW_GPU_UTILIZATION, + callbacks=callbacks, + description="Fraction of time spent in a specific task.", + unit="1", + ) + + +HW_HOST_AMBIENT_TEMPERATURE: Final = "hw.host.ambient_temperature" +""" +Ambient (external) temperature of the physical host +Instrument: gauge +Unit: Cel +""" + + +def create_hw_host_ambient_temperature(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Ambient (external) temperature of the physical host""" + return meter.create_observable_gauge( + name=HW_HOST_AMBIENT_TEMPERATURE, + callbacks=callbacks, + description="Ambient (external) temperature of the physical host.", + unit="Cel", + ) + + +HW_HOST_ENERGY: Final = "hw.host.energy" +""" +Total energy consumed by the entire physical host, in joules +Instrument: counter +Unit: J +Note: The overall energy usage of a host MUST be reported using the specific `hw.host.energy` and `hw.host.power` metrics **only**, instead of the generic `hw.energy` and `hw.power` described in the previous section, to prevent summing up overlapping values. +""" + + +def create_hw_host_energy(meter: Meter) -> Counter: + """Total energy consumed by the entire physical host, in joules""" + return meter.create_counter( + name=HW_HOST_ENERGY, + description="Total energy consumed by the entire physical host, in joules.", + unit="J", + ) + + +HW_HOST_HEATING_MARGIN: Final = "hw.host.heating_margin" +""" +By how many degrees Celsius the temperature of the physical host can be increased, before reaching a warning threshold on one of the internal sensors +Instrument: gauge +Unit: Cel +""" + + +def create_hw_host_heating_margin(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """By how many degrees Celsius the temperature of the physical host can be increased, before reaching a warning threshold on one of the internal sensors""" + return meter.create_observable_gauge( + name=HW_HOST_HEATING_MARGIN, + callbacks=callbacks, + description="By how many degrees Celsius the temperature of the physical host can be increased, before reaching a warning threshold on one of the internal sensors.", + unit="Cel", + ) + + +HW_HOST_POWER: Final = "hw.host.power" +""" +Instantaneous power consumed by the entire physical host in Watts (`hw.host.energy` is preferred) +Instrument: gauge +Unit: W +Note: The overall energy usage of a host MUST be reported using the specific `hw.host.energy` and `hw.host.power` metrics **only**, instead of the generic `hw.energy` and `hw.power` described in the previous section, to prevent summing up overlapping values. +""" + + +def create_hw_host_power(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Instantaneous power consumed by the entire physical host in Watts (`hw.host.energy` is preferred)""" + return meter.create_observable_gauge( + name=HW_HOST_POWER, + callbacks=callbacks, + description="Instantaneous power consumed by the entire physical host in Watts (`hw.host.energy` is preferred).", + unit="W", + ) + + +HW_LOGICAL_DISK_LIMIT: Final = "hw.logical_disk.limit" +""" +Size of the logical disk +Instrument: updowncounter +Unit: By +""" + + +def create_hw_logical_disk_limit(meter: Meter) -> UpDownCounter: + """Size of the logical disk""" + return meter.create_up_down_counter( + name=HW_LOGICAL_DISK_LIMIT, + description="Size of the logical disk.", + unit="By", + ) + + +HW_LOGICAL_DISK_USAGE: Final = "hw.logical_disk.usage" +""" +Logical disk space usage +Instrument: updowncounter +Unit: By +""" + + +def create_hw_logical_disk_usage(meter: Meter) -> UpDownCounter: + """Logical disk space usage""" + return meter.create_up_down_counter( + name=HW_LOGICAL_DISK_USAGE, + description="Logical disk space usage.", + unit="By", + ) + + +HW_LOGICAL_DISK_UTILIZATION: Final = "hw.logical_disk.utilization" +""" +Logical disk space utilization as a fraction +Instrument: gauge +Unit: 1 +""" + + +def create_hw_logical_disk_utilization(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Logical disk space utilization as a fraction""" + return meter.create_observable_gauge( + name=HW_LOGICAL_DISK_UTILIZATION, + callbacks=callbacks, + description="Logical disk space utilization as a fraction.", + unit="1", + ) + + +HW_MEMORY_SIZE: Final = "hw.memory.size" +""" +Size of the memory module +Instrument: updowncounter +Unit: By +""" + + +def create_hw_memory_size(meter: Meter) -> UpDownCounter: + """Size of the memory module""" + return meter.create_up_down_counter( + name=HW_MEMORY_SIZE, + description="Size of the memory module.", + unit="By", + ) + + +HW_NETWORK_BANDWIDTH_LIMIT: Final = "hw.network.bandwidth.limit" +""" +Link speed +Instrument: updowncounter +Unit: By/s +""" + + +def create_hw_network_bandwidth_limit(meter: Meter) -> UpDownCounter: + """Link speed""" + return meter.create_up_down_counter( + name=HW_NETWORK_BANDWIDTH_LIMIT, + description="Link speed.", + unit="By/s", + ) + + +HW_NETWORK_BANDWIDTH_UTILIZATION: Final = "hw.network.bandwidth.utilization" +""" +Utilization of the network bandwidth as a fraction +Instrument: gauge +Unit: 1 +""" + + +def create_hw_network_bandwidth_utilization(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Utilization of the network bandwidth as a fraction""" + return meter.create_observable_gauge( + name=HW_NETWORK_BANDWIDTH_UTILIZATION, + callbacks=callbacks, + description="Utilization of the network bandwidth as a fraction.", + unit="1", + ) + + +HW_NETWORK_IO: Final = "hw.network.io" +""" +Received and transmitted network traffic in bytes +Instrument: counter +Unit: By +""" + + +def create_hw_network_io(meter: Meter) -> Counter: + """Received and transmitted network traffic in bytes""" + return meter.create_counter( + name=HW_NETWORK_IO, + description="Received and transmitted network traffic in bytes.", + unit="By", + ) + + +HW_NETWORK_PACKETS: Final = "hw.network.packets" +""" +Received and transmitted network traffic in packets (or frames) +Instrument: counter +Unit: {packet} +""" + + +def create_hw_network_packets(meter: Meter) -> Counter: + """Received and transmitted network traffic in packets (or frames)""" + return meter.create_counter( + name=HW_NETWORK_PACKETS, + description="Received and transmitted network traffic in packets (or frames).", + unit="{packet}", + ) + + +HW_NETWORK_UP: Final = "hw.network.up" +""" +Link status: `1` (up) or `0` (down) +Instrument: updowncounter +Unit: 1 +""" + + +def create_hw_network_up(meter: Meter) -> UpDownCounter: + """Link status: `1` (up) or `0` (down)""" + return meter.create_up_down_counter( + name=HW_NETWORK_UP, + description="Link status: `1` (up) or `0` (down).", + unit="1", + ) + + +HW_PHYSICAL_DISK_ENDURANCE_UTILIZATION: Final = "hw.physical_disk.endurance_utilization" +""" +Endurance remaining for this SSD disk +Instrument: gauge +Unit: 1 +""" + + +def create_hw_physical_disk_endurance_utilization( + meter: Meter, callbacks: Sequence[CallbackT] | None +) -> ObservableGauge: + """Endurance remaining for this SSD disk""" + return meter.create_observable_gauge( + name=HW_PHYSICAL_DISK_ENDURANCE_UTILIZATION, + callbacks=callbacks, + description="Endurance remaining for this SSD disk.", + unit="1", + ) + + +HW_PHYSICAL_DISK_SIZE: Final = "hw.physical_disk.size" +""" +Size of the disk +Instrument: updowncounter +Unit: By +""" + + +def create_hw_physical_disk_size(meter: Meter) -> UpDownCounter: + """Size of the disk""" + return meter.create_up_down_counter( + name=HW_PHYSICAL_DISK_SIZE, + description="Size of the disk.", + unit="By", + ) + + +HW_PHYSICAL_DISK_SMART: Final = "hw.physical_disk.smart" +""" +Value of the corresponding [S.M.A.R.T.](https://wikipedia.org/wiki/S.M.A.R.T.) (Self-Monitoring, Analysis, and Reporting Technology) attribute +Instrument: gauge +Unit: 1 +""" + + +def create_hw_physical_disk_smart(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Value of the corresponding [S.M.A.R.T.](https://wikipedia.org/wiki/S.M.A.R.T.) (Self-Monitoring, Analysis, and Reporting Technology) attribute""" + return meter.create_observable_gauge( + name=HW_PHYSICAL_DISK_SMART, + callbacks=callbacks, + description="Value of the corresponding [S.M.A.R.T.](https://wikipedia.org/wiki/S.M.A.R.T.) (Self-Monitoring, Analysis, and Reporting Technology) attribute.", + unit="1", + ) + + +HW_POWER: Final = "hw.power" +""" +Instantaneous power consumed by the component +Instrument: gauge +Unit: W +Note: It is recommended to report `hw.energy` instead of `hw.power` when possible. +""" + + +def create_hw_power(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Instantaneous power consumed by the component""" + return meter.create_observable_gauge( + name=HW_POWER, + callbacks=callbacks, + description="Instantaneous power consumed by the component.", + unit="W", + ) + + +HW_POWER_SUPPLY_LIMIT: Final = "hw.power_supply.limit" +""" +Maximum power output of the power supply +Instrument: updowncounter +Unit: W +""" + + +def create_hw_power_supply_limit(meter: Meter) -> UpDownCounter: + """Maximum power output of the power supply""" + return meter.create_up_down_counter( + name=HW_POWER_SUPPLY_LIMIT, + description="Maximum power output of the power supply.", + unit="W", + ) + + +HW_POWER_SUPPLY_USAGE: Final = "hw.power_supply.usage" +""" +Current power output of the power supply +Instrument: updowncounter +Unit: W +""" + + +def create_hw_power_supply_usage(meter: Meter) -> UpDownCounter: + """Current power output of the power supply""" + return meter.create_up_down_counter( + name=HW_POWER_SUPPLY_USAGE, + description="Current power output of the power supply.", + unit="W", + ) + + +HW_POWER_SUPPLY_UTILIZATION: Final = "hw.power_supply.utilization" +""" +Utilization of the power supply as a fraction of its maximum output +Instrument: gauge +Unit: 1 +""" + + +def create_hw_power_supply_utilization(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Utilization of the power supply as a fraction of its maximum output""" + return meter.create_observable_gauge( + name=HW_POWER_SUPPLY_UTILIZATION, + callbacks=callbacks, + description="Utilization of the power supply as a fraction of its maximum output.", + unit="1", + ) + + +HW_STATUS: Final = "hw.status" +""" +Operational status: `1` (true) or `0` (false) for each of the possible states +Instrument: updowncounter +Unit: 1 +Note: `hw.status` is currently specified as an *UpDownCounter* but would ideally be represented using a [*StateSet* as defined in OpenMetrics](https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#stateset). This semantic convention will be updated once *StateSet* is specified in OpenTelemetry. This planned change is not expected to have any consequence on the way users query their timeseries backend to retrieve the values of `hw.status` over time. +""" + + +def create_hw_status(meter: Meter) -> UpDownCounter: + """Operational status: `1` (true) or `0` (false) for each of the possible states""" + return meter.create_up_down_counter( + name=HW_STATUS, + description="Operational status: `1` (true) or `0` (false) for each of the possible states.", + unit="1", + ) + + +HW_TAPE_DRIVE_OPERATIONS: Final = "hw.tape_drive.operations" +""" +Operations performed by the tape drive +Instrument: counter +Unit: {operation} +""" + + +def create_hw_tape_drive_operations(meter: Meter) -> Counter: + """Operations performed by the tape drive""" + return meter.create_counter( + name=HW_TAPE_DRIVE_OPERATIONS, + description="Operations performed by the tape drive.", + unit="{operation}", + ) + + +HW_TEMPERATURE: Final = "hw.temperature" +""" +Temperature in degrees Celsius +Instrument: gauge +Unit: Cel +""" + + +def create_hw_temperature(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Temperature in degrees Celsius""" + return meter.create_observable_gauge( + name=HW_TEMPERATURE, + callbacks=callbacks, + description="Temperature in degrees Celsius.", + unit="Cel", + ) + + +HW_TEMPERATURE_LIMIT: Final = "hw.temperature.limit" +""" +Temperature limit in degrees Celsius +Instrument: gauge +Unit: Cel +""" + + +def create_hw_temperature_limit(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Temperature limit in degrees Celsius""" + return meter.create_observable_gauge( + name=HW_TEMPERATURE_LIMIT, + callbacks=callbacks, + description="Temperature limit in degrees Celsius.", + unit="Cel", + ) + + +HW_VOLTAGE: Final = "hw.voltage" +""" +Voltage measured by the sensor +Instrument: gauge +Unit: V +""" + + +def create_hw_voltage(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Voltage measured by the sensor""" + return meter.create_observable_gauge( + name=HW_VOLTAGE, + callbacks=callbacks, + description="Voltage measured by the sensor.", + unit="V", + ) + + +HW_VOLTAGE_LIMIT: Final = "hw.voltage.limit" +""" +Voltage limit in Volts +Instrument: gauge +Unit: V +""" + + +def create_hw_voltage_limit(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Voltage limit in Volts""" + return meter.create_observable_gauge( + name=HW_VOLTAGE_LIMIT, + callbacks=callbacks, + description="Voltage limit in Volts.", + unit="V", + ) + + +HW_VOLTAGE_NOMINAL: Final = "hw.voltage.nominal" +""" +Nominal (expected) voltage +Instrument: gauge +Unit: V +""" + + +def create_hw_voltage_nominal(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Nominal (expected) voltage""" + return meter.create_observable_gauge( + name=HW_VOLTAGE_NOMINAL, + callbacks=callbacks, + description="Nominal (expected) voltage.", + unit="V", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/k8s_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/k8s_metrics.py new file mode 100644 index 0000000..9587c96 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/k8s_metrics.py @@ -0,0 +1,2952 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from collections.abc import Callable, Generator, Iterable, Sequence +from typing import Final + +from opentelemetry.metrics import CallbackOptions, Counter, Meter, ObservableGauge, Observation, UpDownCounter + +# pylint: disable=invalid-name +CallbackT = Callable[[CallbackOptions], Iterable[Observation]] | Generator[Iterable[Observation], CallbackOptions, None] + + +K8S_CONTAINER_CPU_LIMIT: Final = "k8s.container.cpu.limit" +""" +Deprecated: Replaced by `k8s.container.cpu.limit.desired`. +""" + + +def create_k8s_container_cpu_limit(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.container.cpu.limit.desired` and `k8s.container.cpu.limit.current` instead""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_CPU_LIMIT, + description="Deprecated, use `k8s.container.cpu.limit.desired` and `k8s.container.cpu.limit.current` instead.", + unit="{cpu}", + ) + + +K8S_CONTAINER_CPU_LIMIT_CURRENT: Final = "k8s.container.cpu.limit.current" +""" +Maximum CPU resource limit currently configured for a running container +Instrument: updowncounter +Unit: {cpu} +Note: This metric aligns with the limit in the +[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of +[K8s ContainerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatus-v1-core) +(status.containerStatuses[*].resources). Also see `Actual Resources` in + for more details. +""" + + +def create_k8s_container_cpu_limit_current(meter: Meter) -> UpDownCounter: + """Maximum CPU resource limit currently configured for a running container""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_CPU_LIMIT_CURRENT, + description="Maximum CPU resource limit currently configured for a running container.", + unit="{cpu}", + ) + + +K8S_CONTAINER_CPU_LIMIT_DESIRED: Final = "k8s.container.cpu.limit.desired" +""" +Maximum CPU resource limit as defined by the container spec +Instrument: updowncounter +Unit: {cpu} +Note: This metric aligns with the limit in the +[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of +[K8s Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#container-v1-core) +(spec.containers[*].resources). Also see `Desired Resources` in + for more details. +""" + + +def create_k8s_container_cpu_limit_desired(meter: Meter) -> UpDownCounter: + """Maximum CPU resource limit as defined by the container spec""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_CPU_LIMIT_DESIRED, + description="Maximum CPU resource limit as defined by the container spec.", + unit="{cpu}", + ) + + +K8S_CONTAINER_CPU_LIMIT_UTILIZATION: Final = "k8s.container.cpu.limit.utilization" +""" +The ratio of container CPU usage to its current CPU limit +Instrument: gauge +Unit: 1 +Note: The current CPU limit reflects the actual resources applied to the container, as reported by +[ContainerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatus-v1-core). +The value range is [0.0,1.0]. A value of 1.0 means the container is using 100% of its actual CPU limit. +If the CPU limit is not set, this metric SHOULD NOT be emitted for that container. +""" + + +def create_k8s_container_cpu_limit_utilization(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """The ratio of container CPU usage to its current CPU limit""" + return meter.create_observable_gauge( + name=K8S_CONTAINER_CPU_LIMIT_UTILIZATION, + callbacks=callbacks, + description="The ratio of container CPU usage to its current CPU limit.", + unit="1", + ) + + +K8S_CONTAINER_CPU_REQUEST: Final = "k8s.container.cpu.request" +""" +Deprecated: Replaced by `k8s.container.cpu.request.desired`. +""" + + +def create_k8s_container_cpu_request(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.container.cpu.request.desired` and `k8s.container.cpu.request.current` instead""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_CPU_REQUEST, + description="Deprecated, use `k8s.container.cpu.request.desired` and `k8s.container.cpu.request.current` instead.", + unit="{cpu}", + ) + + +K8S_CONTAINER_CPU_REQUEST_CURRENT: Final = "k8s.container.cpu.request.current" +""" +CPU resource requested currently configured for a running container +Instrument: updowncounter +Unit: {cpu} +Note: This metric aligns with the request in the +[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of +[K8s ContainerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatus-v1-core) +(status.containerStatuses[*].resources). Also see `Actual Resources` in + for more details. +""" + + +def create_k8s_container_cpu_request_current(meter: Meter) -> UpDownCounter: + """CPU resource requested currently configured for a running container""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_CPU_REQUEST_CURRENT, + description="CPU resource requested currently configured for a running container.", + unit="{cpu}", + ) + + +K8S_CONTAINER_CPU_REQUEST_DESIRED: Final = "k8s.container.cpu.request.desired" +""" +CPU resource requested as defined by the container spec +Instrument: updowncounter +Unit: {cpu} +Note: This metric aligns with the request in the +[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of +[K8s Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#container-v1-core) +(spec.containers[*].resources). Also see `Desired Resources` in + for more details. +""" + + +def create_k8s_container_cpu_request_desired(meter: Meter) -> UpDownCounter: + """CPU resource requested as defined by the container spec""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_CPU_REQUEST_DESIRED, + description="CPU resource requested as defined by the container spec.", + unit="{cpu}", + ) + + +K8S_CONTAINER_CPU_REQUEST_UTILIZATION: Final = "k8s.container.cpu.request.utilization" +""" +The ratio of container CPU usage to its current CPU request +Instrument: gauge +Unit: 1 +Note: The current CPU request reflects the request applied to the running container, as reported by +[ContainerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatus-v1-core). +The value range is [0.0,1.0]. A value of 1.0 means the container is using 100% of its actual CPU request. +If the CPU request is not set, this metric SHOULD NOT be emitted for that container. +""" + + +def create_k8s_container_cpu_request_utilization( + meter: Meter, callbacks: Sequence[CallbackT] | None +) -> ObservableGauge: + """The ratio of container CPU usage to its current CPU request""" + return meter.create_observable_gauge( + name=K8S_CONTAINER_CPU_REQUEST_UTILIZATION, + callbacks=callbacks, + description="The ratio of container CPU usage to its current CPU request.", + unit="1", + ) + + +K8S_CONTAINER_EPHEMERAL_STORAGE_LIMIT: Final = "k8s.container.ephemeral_storage.limit" +""" +Maximum ephemeral storage resource limit set for the container +Instrument: updowncounter +Unit: By +Note: See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core for details. +""" + + +def create_k8s_container_ephemeral_storage_limit(meter: Meter) -> UpDownCounter: + """Maximum ephemeral storage resource limit set for the container""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_EPHEMERAL_STORAGE_LIMIT, + description="Maximum ephemeral storage resource limit set for the container.", + unit="By", + ) + + +K8S_CONTAINER_EPHEMERAL_STORAGE_REQUEST: Final = "k8s.container.ephemeral_storage.request" +""" +Ephemeral storage resource requested for the container +Instrument: updowncounter +Unit: By +Note: See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core for details. +""" + + +def create_k8s_container_ephemeral_storage_request(meter: Meter) -> UpDownCounter: + """Ephemeral storage resource requested for the container""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_EPHEMERAL_STORAGE_REQUEST, + description="Ephemeral storage resource requested for the container.", + unit="By", + ) + + +K8S_CONTAINER_EPHEMERAL_STORAGE_USAGE: Final = "k8s.container.ephemeral_storage.usage" +""" +The ephemeral storage used by a container +Instrument: updowncounter +Unit: By +Note: The value for this metric can be compared against `metric.k8s.container.ephemeral_storage.request` and `metric.k8s.container.ephemeral_storage.limit`. +""" + + +def create_k8s_container_ephemeral_storage_usage(meter: Meter) -> UpDownCounter: + """The ephemeral storage used by a container""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_EPHEMERAL_STORAGE_USAGE, + description="The ephemeral storage used by a container.", + unit="By", + ) + + +K8S_CONTAINER_MEMORY_LIMIT: Final = "k8s.container.memory.limit" +""" +Deprecated: Replaced by `k8s.container.memory.limit.desired`. +""" + + +def create_k8s_container_memory_limit(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.container.memory.limit.desired` and `k8s.container.memory.limit.current` instead""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_MEMORY_LIMIT, + description="Deprecated, use `k8s.container.memory.limit.desired` and `k8s.container.memory.limit.current` instead.", + unit="By", + ) + + +K8S_CONTAINER_MEMORY_LIMIT_CURRENT: Final = "k8s.container.memory.limit.current" +""" +Maximum memory resource limit currently configured for a running container +Instrument: updowncounter +Unit: By +Note: This metric aligns with the limit in the +[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of +[K8s ContainerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatus-v1-core) +(status.containerStatuses[*].resources). Also see `Actual Resources` in + for more details. +""" + + +def create_k8s_container_memory_limit_current(meter: Meter) -> UpDownCounter: + """Maximum memory resource limit currently configured for a running container""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_MEMORY_LIMIT_CURRENT, + description="Maximum memory resource limit currently configured for a running container.", + unit="By", + ) + + +K8S_CONTAINER_MEMORY_LIMIT_DESIRED: Final = "k8s.container.memory.limit.desired" +""" +Maximum memory resource limit as defined by the container spec +Instrument: updowncounter +Unit: By +Note: This metric aligns with the limit in the +[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of +[K8s Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#container-v1-core) +(spec.containers[*].resources). Also see `Desired Resources` in + for more details. +""" + + +def create_k8s_container_memory_limit_desired(meter: Meter) -> UpDownCounter: + """Maximum memory resource limit as defined by the container spec""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_MEMORY_LIMIT_DESIRED, + description="Maximum memory resource limit as defined by the container spec.", + unit="By", + ) + + +K8S_CONTAINER_MEMORY_REQUEST: Final = "k8s.container.memory.request" +""" +Deprecated: Replaced by `k8s.container.memory.request.desired`. +""" + + +def create_k8s_container_memory_request(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.container.memory.request.desired` and `k8s.container.memory.request.current` instead""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_MEMORY_REQUEST, + description="Deprecated, use `k8s.container.memory.request.desired` and `k8s.container.memory.request.current` instead.", + unit="By", + ) + + +K8S_CONTAINER_MEMORY_REQUEST_CURRENT: Final = "k8s.container.memory.request.current" +""" +Memory resource request currently configured for a running container +Instrument: updowncounter +Unit: By +Note: This metric aligns with the request in the +[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of +[K8s ContainerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatus-v1-core) +(status.containerStatuses[*].resources). Also see `Actual Resources` in + for more details. +""" + + +def create_k8s_container_memory_request_current(meter: Meter) -> UpDownCounter: + """Memory resource request currently configured for a running container""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_MEMORY_REQUEST_CURRENT, + description="Memory resource request currently configured for a running container.", + unit="By", + ) + + +K8S_CONTAINER_MEMORY_REQUEST_DESIRED: Final = "k8s.container.memory.request.desired" +""" +Memory resource requested as defined by the container spec +Instrument: updowncounter +Unit: By +Note: This metric aligns with the request in the +[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of +[K8s Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#container-v1-core) +(spec.containers[*].resources). Also see `Desired Resources` in + for more details. +""" + + +def create_k8s_container_memory_request_desired(meter: Meter) -> UpDownCounter: + """Memory resource requested as defined by the container spec""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_MEMORY_REQUEST_DESIRED, + description="Memory resource requested as defined by the container spec.", + unit="By", + ) + + +K8S_CONTAINER_READY: Final = "k8s.container.ready" +""" +Indicates whether the container is currently marked as ready to accept traffic, based on its readiness probe (1 = ready, 0 = not ready) +Instrument: updowncounter +Unit: {container} +Note: This metric SHOULD reflect the value of the `ready` field in the +[K8s ContainerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatus-v1-core). +""" + + +def create_k8s_container_ready(meter: Meter) -> UpDownCounter: + """Indicates whether the container is currently marked as ready to accept traffic, based on its readiness probe (1 = ready, 0 = not ready)""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_READY, + description="Indicates whether the container is currently marked as ready to accept traffic, based on its readiness probe (1 = ready, 0 = not ready).", + unit="{container}", + ) + + +K8S_CONTAINER_RESTART_COUNT: Final = "k8s.container.restart.count" +""" +Describes how many times the container has restarted (since the last counter reset) +Instrument: updowncounter +Unit: {restart} +Note: This value is pulled directly from the K8s API and the value can go indefinitely high and be reset to 0 +at any time depending on how your kubelet is configured to prune dead containers. +It is best to not depend too much on the exact value but rather look at it as +either == 0, in which case you can conclude there were no restarts in the recent past, or > 0, in which case +you can conclude there were restarts in the recent past, and not try and analyze the value beyond that. +""" + + +def create_k8s_container_restart_count(meter: Meter) -> UpDownCounter: + """Describes how many times the container has restarted (since the last counter reset)""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_RESTART_COUNT, + description="Describes how many times the container has restarted (since the last counter reset).", + unit="{restart}", + ) + + +K8S_CONTAINER_STATUS_REASON: Final = "k8s.container.status.reason" +""" +Describes the number of K8s containers that are currently in a state for a given reason +Instrument: updowncounter +Unit: {container} +Note: All possible container state reasons will be reported at each time interval to avoid missing metrics. +Only the value corresponding to the current state reason will be non-zero. +""" + + +def create_k8s_container_status_reason(meter: Meter) -> UpDownCounter: + """Describes the number of K8s containers that are currently in a state for a given reason""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_STATUS_REASON, + description="Describes the number of K8s containers that are currently in a state for a given reason.", + unit="{container}", + ) + + +K8S_CONTAINER_STATUS_STATE: Final = "k8s.container.status.state" +""" +Describes the number of K8s containers that are currently in a given state +Instrument: updowncounter +Unit: {container} +Note: All possible container states will be reported at each time interval to avoid missing metrics. +Only the value corresponding to the current state will be non-zero. +""" + + +def create_k8s_container_status_state(meter: Meter) -> UpDownCounter: + """Describes the number of K8s containers that are currently in a given state""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_STATUS_STATE, + description="Describes the number of K8s containers that are currently in a given state.", + unit="{container}", + ) + + +K8S_CONTAINER_STORAGE_LIMIT: Final = "k8s.container.storage.limit" +""" +Maximum storage resource limit set for the container +Instrument: updowncounter +Unit: By +Note: See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core for details. +""" + + +def create_k8s_container_storage_limit(meter: Meter) -> UpDownCounter: + """Maximum storage resource limit set for the container""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_STORAGE_LIMIT, + description="Maximum storage resource limit set for the container.", + unit="By", + ) + + +K8S_CONTAINER_STORAGE_REQUEST: Final = "k8s.container.storage.request" +""" +Storage resource requested for the container +Instrument: updowncounter +Unit: By +Note: See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core for details. +""" + + +def create_k8s_container_storage_request(meter: Meter) -> UpDownCounter: + """Storage resource requested for the container""" + return meter.create_up_down_counter( + name=K8S_CONTAINER_STORAGE_REQUEST, + description="Storage resource requested for the container.", + unit="By", + ) + + +K8S_CRONJOB_ACTIVE_JOBS: Final = "k8s.cronjob.active_jobs" +""" +Deprecated: Replaced by `k8s.cronjob.job.active`. +""" + + +def create_k8s_cronjob_active_jobs(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.cronjob.job.active` instead""" + return meter.create_up_down_counter( + name=K8S_CRONJOB_ACTIVE_JOBS, + description="Deprecated, use `k8s.cronjob.job.active` instead.", + unit="{job}", + ) + + +K8S_CRONJOB_JOB_ACTIVE: Final = "k8s.cronjob.job.active" +""" +The number of actively running jobs for a cronjob +Instrument: updowncounter +Unit: {job} +Note: This metric aligns with the `active` field of the +[K8s CronJobStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#cronjobstatus-v1-batch). +""" + + +def create_k8s_cronjob_job_active(meter: Meter) -> UpDownCounter: + """The number of actively running jobs for a cronjob""" + return meter.create_up_down_counter( + name=K8S_CRONJOB_JOB_ACTIVE, + description="The number of actively running jobs for a cronjob.", + unit="{job}", + ) + + +K8S_DAEMONSET_CURRENT_SCHEDULED_NODES: Final = "k8s.daemonset.current_scheduled_nodes" +""" +Deprecated: Replaced by `k8s.daemonset.node.current_scheduled`. +""" + + +def create_k8s_daemonset_current_scheduled_nodes(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.daemonset.node.current_scheduled` instead""" + return meter.create_up_down_counter( + name=K8S_DAEMONSET_CURRENT_SCHEDULED_NODES, + description="Deprecated, use `k8s.daemonset.node.current_scheduled` instead.", + unit="{node}", + ) + + +K8S_DAEMONSET_DESIRED_SCHEDULED_NODES: Final = "k8s.daemonset.desired_scheduled_nodes" +""" +Deprecated: Replaced by `k8s.daemonset.node.desired_scheduled`. +""" + + +def create_k8s_daemonset_desired_scheduled_nodes(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.daemonset.node.desired_scheduled` instead""" + return meter.create_up_down_counter( + name=K8S_DAEMONSET_DESIRED_SCHEDULED_NODES, + description="Deprecated, use `k8s.daemonset.node.desired_scheduled` instead.", + unit="{node}", + ) + + +K8S_DAEMONSET_MISSCHEDULED_NODES: Final = "k8s.daemonset.misscheduled_nodes" +""" +Deprecated: Replaced by `k8s.daemonset.node.misscheduled`. +""" + + +def create_k8s_daemonset_misscheduled_nodes(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.daemonset.node.misscheduled` instead""" + return meter.create_up_down_counter( + name=K8S_DAEMONSET_MISSCHEDULED_NODES, + description="Deprecated, use `k8s.daemonset.node.misscheduled` instead.", + unit="{node}", + ) + + +K8S_DAEMONSET_NODE_CURRENT_SCHEDULED: Final = "k8s.daemonset.node.current_scheduled" +""" +Number of nodes that are running at least 1 daemon pod and are supposed to run the daemon pod +Instrument: updowncounter +Unit: {node} +Note: This metric aligns with the `currentNumberScheduled` field of the +[K8s DaemonSetStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#daemonsetstatus-v1-apps). +""" + + +def create_k8s_daemonset_node_current_scheduled(meter: Meter) -> UpDownCounter: + """Number of nodes that are running at least 1 daemon pod and are supposed to run the daemon pod""" + return meter.create_up_down_counter( + name=K8S_DAEMONSET_NODE_CURRENT_SCHEDULED, + description="Number of nodes that are running at least 1 daemon pod and are supposed to run the daemon pod.", + unit="{node}", + ) + + +K8S_DAEMONSET_NODE_DESIRED_SCHEDULED: Final = "k8s.daemonset.node.desired_scheduled" +""" +Number of nodes that should be running the daemon pod (including nodes currently running the daemon pod) +Instrument: updowncounter +Unit: {node} +Note: This metric aligns with the `desiredNumberScheduled` field of the +[K8s DaemonSetStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#daemonsetstatus-v1-apps). +""" + + +def create_k8s_daemonset_node_desired_scheduled(meter: Meter) -> UpDownCounter: + """Number of nodes that should be running the daemon pod (including nodes currently running the daemon pod)""" + return meter.create_up_down_counter( + name=K8S_DAEMONSET_NODE_DESIRED_SCHEDULED, + description="Number of nodes that should be running the daemon pod (including nodes currently running the daemon pod).", + unit="{node}", + ) + + +K8S_DAEMONSET_NODE_MISSCHEDULED: Final = "k8s.daemonset.node.misscheduled" +""" +Number of nodes that are running the daemon pod, but are not supposed to run the daemon pod +Instrument: updowncounter +Unit: {node} +Note: This metric aligns with the `numberMisscheduled` field of the +[K8s DaemonSetStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#daemonsetstatus-v1-apps). +""" + + +def create_k8s_daemonset_node_misscheduled(meter: Meter) -> UpDownCounter: + """Number of nodes that are running the daemon pod, but are not supposed to run the daemon pod""" + return meter.create_up_down_counter( + name=K8S_DAEMONSET_NODE_MISSCHEDULED, + description="Number of nodes that are running the daemon pod, but are not supposed to run the daemon pod.", + unit="{node}", + ) + + +K8S_DAEMONSET_NODE_READY: Final = "k8s.daemonset.node.ready" +""" +Number of nodes that should be running the daemon pod and have one or more of the daemon pod running and ready +Instrument: updowncounter +Unit: {node} +Note: This metric aligns with the `numberReady` field of the +[K8s DaemonSetStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#daemonsetstatus-v1-apps). +""" + + +def create_k8s_daemonset_node_ready(meter: Meter) -> UpDownCounter: + """Number of nodes that should be running the daemon pod and have one or more of the daemon pod running and ready""" + return meter.create_up_down_counter( + name=K8S_DAEMONSET_NODE_READY, + description="Number of nodes that should be running the daemon pod and have one or more of the daemon pod running and ready.", + unit="{node}", + ) + + +K8S_DAEMONSET_READY_NODES: Final = "k8s.daemonset.ready_nodes" +""" +Deprecated: Replaced by `k8s.daemonset.node.ready`. +""" + + +def create_k8s_daemonset_ready_nodes(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.daemonset.node.ready` instead""" + return meter.create_up_down_counter( + name=K8S_DAEMONSET_READY_NODES, + description="Deprecated, use `k8s.daemonset.node.ready` instead.", + unit="{node}", + ) + + +K8S_DEPLOYMENT_AVAILABLE_PODS: Final = "k8s.deployment.available_pods" +""" +Deprecated: Replaced by `k8s.deployment.pod.available`. +""" + + +def create_k8s_deployment_available_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.deployment.pod.available` instead""" + return meter.create_up_down_counter( + name=K8S_DEPLOYMENT_AVAILABLE_PODS, + description="Deprecated, use `k8s.deployment.pod.available` instead.", + unit="{pod}", + ) + + +K8S_DEPLOYMENT_DESIRED_PODS: Final = "k8s.deployment.desired_pods" +""" +Deprecated: Replaced by `k8s.deployment.pod.desired`. +""" + + +def create_k8s_deployment_desired_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.deployment.pod.desired` instead""" + return meter.create_up_down_counter( + name=K8S_DEPLOYMENT_DESIRED_PODS, + description="Deprecated, use `k8s.deployment.pod.desired` instead.", + unit="{pod}", + ) + + +K8S_DEPLOYMENT_POD_AVAILABLE: Final = "k8s.deployment.pod.available" +""" +Total number of available replica pods (ready for at least minReadySeconds) targeted by this deployment +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `availableReplicas` field of the +[K8s DeploymentStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#deploymentstatus-v1-apps). +""" + + +def create_k8s_deployment_pod_available(meter: Meter) -> UpDownCounter: + """Total number of available replica pods (ready for at least minReadySeconds) targeted by this deployment""" + return meter.create_up_down_counter( + name=K8S_DEPLOYMENT_POD_AVAILABLE, + description="Total number of available replica pods (ready for at least minReadySeconds) targeted by this deployment.", + unit="{pod}", + ) + + +K8S_DEPLOYMENT_POD_DESIRED: Final = "k8s.deployment.pod.desired" +""" +Number of desired replica pods in this deployment +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `replicas` field of the +[K8s DeploymentSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#deploymentspec-v1-apps). +""" + + +def create_k8s_deployment_pod_desired(meter: Meter) -> UpDownCounter: + """Number of desired replica pods in this deployment""" + return meter.create_up_down_counter( + name=K8S_DEPLOYMENT_POD_DESIRED, + description="Number of desired replica pods in this deployment.", + unit="{pod}", + ) + + +K8S_HPA_CURRENT_PODS: Final = "k8s.hpa.current_pods" +""" +Deprecated: Replaced by `k8s.hpa.pod.current`. +""" + + +def create_k8s_hpa_current_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.hpa.pod.current` instead""" + return meter.create_up_down_counter( + name=K8S_HPA_CURRENT_PODS, + description="Deprecated, use `k8s.hpa.pod.current` instead.", + unit="{pod}", + ) + + +K8S_HPA_DESIRED_PODS: Final = "k8s.hpa.desired_pods" +""" +Deprecated: Replaced by `k8s.hpa.pod.desired`. +""" + + +def create_k8s_hpa_desired_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.hpa.pod.desired` instead""" + return meter.create_up_down_counter( + name=K8S_HPA_DESIRED_PODS, + description="Deprecated, use `k8s.hpa.pod.desired` instead.", + unit="{pod}", + ) + + +K8S_HPA_MAX_PODS: Final = "k8s.hpa.max_pods" +""" +Deprecated: Replaced by `k8s.hpa.pod.max`. +""" + + +def create_k8s_hpa_max_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.hpa.pod.max` instead""" + return meter.create_up_down_counter( + name=K8S_HPA_MAX_PODS, + description="Deprecated, use `k8s.hpa.pod.max` instead.", + unit="{pod}", + ) + + +K8S_HPA_METRIC_TARGET_CPU_AVERAGE_UTILIZATION: Final = "k8s.hpa.metric.target.cpu.average_utilization" +""" +Target average utilization, in percentage, for CPU resource in HPA config +Instrument: gauge +Unit: 1 +Note: This metric aligns with the `averageUtilization` field of the +[K8s HPA MetricTarget](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#metrictarget-v2-autoscaling). +If the type of the metric is [`ContainerResource`](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-metrics-apis), +the `k8s.container.name` attribute MUST be set to identify the specific container within the pod to which the metric applies. +""" + + +def create_k8s_hpa_metric_target_cpu_average_utilization( + meter: Meter, callbacks: Sequence[CallbackT] | None +) -> ObservableGauge: + """Target average utilization, in percentage, for CPU resource in HPA config""" + return meter.create_observable_gauge( + name=K8S_HPA_METRIC_TARGET_CPU_AVERAGE_UTILIZATION, + callbacks=callbacks, + description="Target average utilization, in percentage, for CPU resource in HPA config.", + unit="1", + ) + + +K8S_HPA_METRIC_TARGET_CPU_AVERAGE_VALUE: Final = "k8s.hpa.metric.target.cpu.average_value" +""" +Target average value for CPU resource in HPA config +Instrument: gauge +Unit: {cpu} +Note: This metric aligns with the `averageValue` field of the +[K8s HPA MetricTarget](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#metrictarget-v2-autoscaling). +If the type of the metric is [`ContainerResource`](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-metrics-apis), +the `k8s.container.name` attribute MUST be set to identify the specific container within the pod to which the metric applies. +""" + + +def create_k8s_hpa_metric_target_cpu_average_value( + meter: Meter, callbacks: Sequence[CallbackT] | None +) -> ObservableGauge: + """Target average value for CPU resource in HPA config""" + return meter.create_observable_gauge( + name=K8S_HPA_METRIC_TARGET_CPU_AVERAGE_VALUE, + callbacks=callbacks, + description="Target average value for CPU resource in HPA config.", + unit="{cpu}", + ) + + +K8S_HPA_METRIC_TARGET_CPU_VALUE: Final = "k8s.hpa.metric.target.cpu.value" +""" +Target value for CPU resource in HPA config +Instrument: gauge +Unit: {cpu} +Note: This metric aligns with the `value` field of the +[K8s HPA MetricTarget](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#metrictarget-v2-autoscaling). +If the type of the metric is [`ContainerResource`](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-metrics-apis), +the `k8s.container.name` attribute MUST be set to identify the specific container within the pod to which the metric applies. +""" + + +def create_k8s_hpa_metric_target_cpu_value(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Target value for CPU resource in HPA config""" + return meter.create_observable_gauge( + name=K8S_HPA_METRIC_TARGET_CPU_VALUE, + callbacks=callbacks, + description="Target value for CPU resource in HPA config.", + unit="{cpu}", + ) + + +K8S_HPA_MIN_PODS: Final = "k8s.hpa.min_pods" +""" +Deprecated: Replaced by `k8s.hpa.pod.min`. +""" + + +def create_k8s_hpa_min_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.hpa.pod.min` instead""" + return meter.create_up_down_counter( + name=K8S_HPA_MIN_PODS, + description="Deprecated, use `k8s.hpa.pod.min` instead.", + unit="{pod}", + ) + + +K8S_HPA_POD_CURRENT: Final = "k8s.hpa.pod.current" +""" +Current number of replica pods managed by this horizontal pod autoscaler, as last seen by the autoscaler +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `currentReplicas` field of the +[K8s HorizontalPodAutoscalerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#horizontalpodautoscalerstatus-v2-autoscaling). +""" + + +def create_k8s_hpa_pod_current(meter: Meter) -> UpDownCounter: + """Current number of replica pods managed by this horizontal pod autoscaler, as last seen by the autoscaler""" + return meter.create_up_down_counter( + name=K8S_HPA_POD_CURRENT, + description="Current number of replica pods managed by this horizontal pod autoscaler, as last seen by the autoscaler.", + unit="{pod}", + ) + + +K8S_HPA_POD_DESIRED: Final = "k8s.hpa.pod.desired" +""" +Desired number of replica pods managed by this horizontal pod autoscaler, as last calculated by the autoscaler +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `desiredReplicas` field of the +[K8s HorizontalPodAutoscalerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#horizontalpodautoscalerstatus-v2-autoscaling). +""" + + +def create_k8s_hpa_pod_desired(meter: Meter) -> UpDownCounter: + """Desired number of replica pods managed by this horizontal pod autoscaler, as last calculated by the autoscaler""" + return meter.create_up_down_counter( + name=K8S_HPA_POD_DESIRED, + description="Desired number of replica pods managed by this horizontal pod autoscaler, as last calculated by the autoscaler.", + unit="{pod}", + ) + + +K8S_HPA_POD_MAX: Final = "k8s.hpa.pod.max" +""" +The upper limit for the number of replica pods to which the autoscaler can scale up +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `maxReplicas` field of the +[K8s HorizontalPodAutoscalerSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#horizontalpodautoscalerspec-v2-autoscaling). +""" + + +def create_k8s_hpa_pod_max(meter: Meter) -> UpDownCounter: + """The upper limit for the number of replica pods to which the autoscaler can scale up""" + return meter.create_up_down_counter( + name=K8S_HPA_POD_MAX, + description="The upper limit for the number of replica pods to which the autoscaler can scale up.", + unit="{pod}", + ) + + +K8S_HPA_POD_MIN: Final = "k8s.hpa.pod.min" +""" +The lower limit for the number of replica pods to which the autoscaler can scale down +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `minReplicas` field of the +[K8s HorizontalPodAutoscalerSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#horizontalpodautoscalerspec-v2-autoscaling). +""" + + +def create_k8s_hpa_pod_min(meter: Meter) -> UpDownCounter: + """The lower limit for the number of replica pods to which the autoscaler can scale down""" + return meter.create_up_down_counter( + name=K8S_HPA_POD_MIN, + description="The lower limit for the number of replica pods to which the autoscaler can scale down.", + unit="{pod}", + ) + + +K8S_JOB_ACTIVE_PODS: Final = "k8s.job.active_pods" +""" +Deprecated: Replaced by `k8s.job.pod.active`. +""" + + +def create_k8s_job_active_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.job.pod.active` instead""" + return meter.create_up_down_counter( + name=K8S_JOB_ACTIVE_PODS, + description="Deprecated, use `k8s.job.pod.active` instead.", + unit="{pod}", + ) + + +K8S_JOB_DESIRED_SUCCESSFUL_PODS: Final = "k8s.job.desired_successful_pods" +""" +Deprecated: Replaced by `k8s.job.pod.desired_successful`. +""" + + +def create_k8s_job_desired_successful_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.job.pod.desired_successful` instead""" + return meter.create_up_down_counter( + name=K8S_JOB_DESIRED_SUCCESSFUL_PODS, + description="Deprecated, use `k8s.job.pod.desired_successful` instead.", + unit="{pod}", + ) + + +K8S_JOB_FAILED_PODS: Final = "k8s.job.failed_pods" +""" +Deprecated: Replaced by `k8s.job.pod.failed`. +""" + + +def create_k8s_job_failed_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.job.pod.failed` instead""" + return meter.create_up_down_counter( + name=K8S_JOB_FAILED_PODS, + description="Deprecated, use `k8s.job.pod.failed` instead.", + unit="{pod}", + ) + + +K8S_JOB_MAX_PARALLEL_PODS: Final = "k8s.job.max_parallel_pods" +""" +Deprecated: Replaced by `k8s.job.pod.max_parallel`. +""" + + +def create_k8s_job_max_parallel_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.job.pod.max_parallel` instead""" + return meter.create_up_down_counter( + name=K8S_JOB_MAX_PARALLEL_PODS, + description="Deprecated, use `k8s.job.pod.max_parallel` instead.", + unit="{pod}", + ) + + +K8S_JOB_POD_ACTIVE: Final = "k8s.job.pod.active" +""" +The number of pending and actively running pods for a job +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `active` field of the +[K8s JobStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#jobstatus-v1-batch). +""" + + +def create_k8s_job_pod_active(meter: Meter) -> UpDownCounter: + """The number of pending and actively running pods for a job""" + return meter.create_up_down_counter( + name=K8S_JOB_POD_ACTIVE, + description="The number of pending and actively running pods for a job.", + unit="{pod}", + ) + + +K8S_JOB_POD_DESIRED_SUCCESSFUL: Final = "k8s.job.pod.desired_successful" +""" +The desired number of successfully finished pods the job should be run with +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `completions` field of the +[K8s JobSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#jobspec-v1-batch). +""" + + +def create_k8s_job_pod_desired_successful(meter: Meter) -> UpDownCounter: + """The desired number of successfully finished pods the job should be run with""" + return meter.create_up_down_counter( + name=K8S_JOB_POD_DESIRED_SUCCESSFUL, + description="The desired number of successfully finished pods the job should be run with.", + unit="{pod}", + ) + + +K8S_JOB_POD_FAILED: Final = "k8s.job.pod.failed" +""" +The number of pods which reached phase Failed for a job +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `failed` field of the +[K8s JobStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#jobstatus-v1-batch). +""" + + +def create_k8s_job_pod_failed(meter: Meter) -> UpDownCounter: + """The number of pods which reached phase Failed for a job""" + return meter.create_up_down_counter( + name=K8S_JOB_POD_FAILED, + description="The number of pods which reached phase Failed for a job.", + unit="{pod}", + ) + + +K8S_JOB_POD_MAX_PARALLEL: Final = "k8s.job.pod.max_parallel" +""" +The max desired number of pods the job should run at any given time +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `parallelism` field of the +[K8s JobSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#jobspec-v1-batch). +""" + + +def create_k8s_job_pod_max_parallel(meter: Meter) -> UpDownCounter: + """The max desired number of pods the job should run at any given time""" + return meter.create_up_down_counter( + name=K8S_JOB_POD_MAX_PARALLEL, + description="The max desired number of pods the job should run at any given time.", + unit="{pod}", + ) + + +K8S_JOB_POD_SUCCESSFUL: Final = "k8s.job.pod.successful" +""" +The number of pods which reached phase Succeeded for a job +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `succeeded` field of the +[K8s JobStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#jobstatus-v1-batch). +""" + + +def create_k8s_job_pod_successful(meter: Meter) -> UpDownCounter: + """The number of pods which reached phase Succeeded for a job""" + return meter.create_up_down_counter( + name=K8S_JOB_POD_SUCCESSFUL, + description="The number of pods which reached phase Succeeded for a job.", + unit="{pod}", + ) + + +K8S_JOB_SUCCESSFUL_PODS: Final = "k8s.job.successful_pods" +""" +Deprecated: Replaced by `k8s.job.pod.successful`. +""" + + +def create_k8s_job_successful_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.job.pod.successful` instead""" + return meter.create_up_down_counter( + name=K8S_JOB_SUCCESSFUL_PODS, + description="Deprecated, use `k8s.job.pod.successful` instead.", + unit="{pod}", + ) + + +K8S_NAMESPACE_PHASE: Final = "k8s.namespace.phase" +""" +Describes number of K8s namespaces that are currently in a given phase +Instrument: updowncounter +Unit: {namespace} +""" + + +def create_k8s_namespace_phase(meter: Meter) -> UpDownCounter: + """Describes number of K8s namespaces that are currently in a given phase""" + return meter.create_up_down_counter( + name=K8S_NAMESPACE_PHASE, + description="Describes number of K8s namespaces that are currently in a given phase.", + unit="{namespace}", + ) + + +K8S_NODE_ALLOCATABLE_CPU: Final = "k8s.node.allocatable.cpu" +""" +Deprecated: Replaced by `k8s.node.cpu.allocatable`. +""" + + +def create_k8s_node_allocatable_cpu(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.node.cpu.allocatable` instead""" + return meter.create_up_down_counter( + name=K8S_NODE_ALLOCATABLE_CPU, + description="Deprecated, use `k8s.node.cpu.allocatable` instead.", + unit="{cpu}", + ) + + +K8S_NODE_ALLOCATABLE_EPHEMERAL_STORAGE: Final = "k8s.node.allocatable.ephemeral_storage" +""" +Deprecated: Replaced by `k8s.node.ephemeral_storage.allocatable`. +""" + + +def create_k8s_node_allocatable_ephemeral_storage(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.node.ephemeral_storage.allocatable` instead""" + return meter.create_up_down_counter( + name=K8S_NODE_ALLOCATABLE_EPHEMERAL_STORAGE, + description="Deprecated, use `k8s.node.ephemeral_storage.allocatable` instead.", + unit="By", + ) + + +K8S_NODE_ALLOCATABLE_MEMORY: Final = "k8s.node.allocatable.memory" +""" +Deprecated: Replaced by `k8s.node.memory.allocatable`. +""" + + +def create_k8s_node_allocatable_memory(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.node.memory.allocatable` instead""" + return meter.create_up_down_counter( + name=K8S_NODE_ALLOCATABLE_MEMORY, + description="Deprecated, use `k8s.node.memory.allocatable` instead.", + unit="By", + ) + + +K8S_NODE_ALLOCATABLE_PODS: Final = "k8s.node.allocatable.pods" +""" +Deprecated: Replaced by `k8s.node.pod.allocatable`. +""" + + +def create_k8s_node_allocatable_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.node.pod.allocatable` instead""" + return meter.create_up_down_counter( + name=K8S_NODE_ALLOCATABLE_PODS, + description="Deprecated, use `k8s.node.pod.allocatable` instead.", + unit="{pod}", + ) + + +K8S_NODE_CONDITION_STATUS: Final = "k8s.node.condition.status" +""" +Describes the condition of a particular Node +Instrument: updowncounter +Unit: {node} +Note: All possible node condition pairs (type and status) will be reported at each time interval to avoid missing metrics. Condition pairs corresponding to the current conditions' statuses will be non-zero. +""" + + +def create_k8s_node_condition_status(meter: Meter) -> UpDownCounter: + """Describes the condition of a particular Node""" + return meter.create_up_down_counter( + name=K8S_NODE_CONDITION_STATUS, + description="Describes the condition of a particular Node.", + unit="{node}", + ) + + +K8S_NODE_CPU_ALLOCATABLE: Final = "k8s.node.cpu.allocatable" +""" +Amount of CPU allocatable on the node +Instrument: updowncounter +Unit: {cpu} +""" + + +def create_k8s_node_cpu_allocatable(meter: Meter) -> UpDownCounter: + """Amount of CPU allocatable on the node""" + return meter.create_up_down_counter( + name=K8S_NODE_CPU_ALLOCATABLE, + description="Amount of CPU allocatable on the node.", + unit="{cpu}", + ) + + +K8S_NODE_CPU_TIME: Final = "k8s.node.cpu.time" +""" +Total CPU time consumed +Instrument: counter +Unit: s +Note: Total CPU time consumed by the specific Node on all available CPU cores. +""" + + +def create_k8s_node_cpu_time(meter: Meter) -> Counter: + """Total CPU time consumed""" + return meter.create_counter( + name=K8S_NODE_CPU_TIME, + description="Total CPU time consumed.", + unit="s", + ) + + +K8S_NODE_CPU_USAGE: Final = "k8s.node.cpu.usage" +""" +Node's CPU usage, measured in CPUs. Range from 0 to the number of allocatable CPUs +Instrument: gauge +Unit: {cpu} +Note: CPU usage of the specific Node on all available CPU cores. It is calculated as the change in cumulative CPU time (k8s.node.cpu.time) over a measurement interval, divided by the elapsed time: usageCores = (cpuTimeEnd - cpuTimeStart) / elapsedSeconds. +""" + + +def create_k8s_node_cpu_usage(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Node's CPU usage, measured in CPUs. Range from 0 to the number of allocatable CPUs""" + return meter.create_observable_gauge( + name=K8S_NODE_CPU_USAGE, + callbacks=callbacks, + description="Node's CPU usage, measured in CPUs. Range from 0 to the number of allocatable CPUs.", + unit="{cpu}", + ) + + +K8S_NODE_EPHEMERAL_STORAGE_ALLOCATABLE: Final = "k8s.node.ephemeral_storage.allocatable" +""" +Amount of ephemeral-storage allocatable on the node +Instrument: updowncounter +Unit: By +""" + + +def create_k8s_node_ephemeral_storage_allocatable(meter: Meter) -> UpDownCounter: + """Amount of ephemeral-storage allocatable on the node""" + return meter.create_up_down_counter( + name=K8S_NODE_EPHEMERAL_STORAGE_ALLOCATABLE, + description="Amount of ephemeral-storage allocatable on the node.", + unit="By", + ) + + +K8S_NODE_FILESYSTEM_AVAILABLE: Final = "k8s.node.filesystem.available" +""" +Node filesystem available bytes +Instrument: updowncounter +Unit: By +Note: This metric is derived from the +[FsStats.AvailableBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field +of the [NodeStats.Fs](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#NodeStats) +of the Kubelet's stats API. +""" + + +def create_k8s_node_filesystem_available(meter: Meter) -> UpDownCounter: + """Node filesystem available bytes""" + return meter.create_up_down_counter( + name=K8S_NODE_FILESYSTEM_AVAILABLE, + description="Node filesystem available bytes.", + unit="By", + ) + + +K8S_NODE_FILESYSTEM_CAPACITY: Final = "k8s.node.filesystem.capacity" +""" +Node filesystem capacity +Instrument: updowncounter +Unit: By +Note: This metric is derived from the +[FsStats.CapacityBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field +of the [NodeStats.Fs](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#NodeStats) +of the Kubelet's stats API. +""" + + +def create_k8s_node_filesystem_capacity(meter: Meter) -> UpDownCounter: + """Node filesystem capacity""" + return meter.create_up_down_counter( + name=K8S_NODE_FILESYSTEM_CAPACITY, + description="Node filesystem capacity.", + unit="By", + ) + + +K8S_NODE_FILESYSTEM_INODE_COUNT: Final = "k8s.node.filesystem.inode.count" +""" +The total inodes in the node's root filesystem +Instrument: updowncounter +Unit: {inode} +Note: This metric is derived from the +[FsStats.Inodes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field +of the [NodeStats.Fs](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#NodeStats) +of the Kubelet's stats API. +""" + + +def create_k8s_node_filesystem_inode_count(meter: Meter) -> UpDownCounter: + """The total inodes in the node's root filesystem""" + return meter.create_up_down_counter( + name=K8S_NODE_FILESYSTEM_INODE_COUNT, + description="The total inodes in the node's root filesystem.", + unit="{inode}", + ) + + +K8S_NODE_FILESYSTEM_INODE_FREE: Final = "k8s.node.filesystem.inode.free" +""" +The free inodes in the node's root filesystem +Instrument: updowncounter +Unit: {inode} +Note: This metric is derived from the +[FsStats.InodesFree](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field +of the [NodeStats.Fs](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#NodeStats) +of the Kubelet's stats API. +""" + + +def create_k8s_node_filesystem_inode_free(meter: Meter) -> UpDownCounter: + """The free inodes in the node's root filesystem""" + return meter.create_up_down_counter( + name=K8S_NODE_FILESYSTEM_INODE_FREE, + description="The free inodes in the node's root filesystem.", + unit="{inode}", + ) + + +K8S_NODE_FILESYSTEM_USAGE: Final = "k8s.node.filesystem.usage" +""" +Node filesystem usage +Instrument: updowncounter +Unit: By +Note: This may not equal capacity - available. + +This metric is derived from the +[FsStats.UsedBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field +of the [NodeStats.Fs](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#NodeStats) +of the Kubelet's stats API. +""" + + +def create_k8s_node_filesystem_usage(meter: Meter) -> UpDownCounter: + """Node filesystem usage""" + return meter.create_up_down_counter( + name=K8S_NODE_FILESYSTEM_USAGE, + description="Node filesystem usage.", + unit="By", + ) + + +K8S_NODE_MEMORY_ALLOCATABLE: Final = "k8s.node.memory.allocatable" +""" +Amount of memory allocatable on the node +Instrument: updowncounter +Unit: By +""" + + +def create_k8s_node_memory_allocatable(meter: Meter) -> UpDownCounter: + """Amount of memory allocatable on the node""" + return meter.create_up_down_counter( + name=K8S_NODE_MEMORY_ALLOCATABLE, + description="Amount of memory allocatable on the node.", + unit="By", + ) + + +K8S_NODE_MEMORY_AVAILABLE: Final = "k8s.node.memory.available" +""" +Node memory available +Instrument: updowncounter +Unit: By +Note: Available memory for use. This is defined as the memory limit - workingSetBytes. If memory limit is undefined, the available bytes is omitted. +This metric is derived from the [MemoryStats.AvailableBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [NodeStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#NodeStats) of the Kubelet's stats API. +""" + + +def create_k8s_node_memory_available(meter: Meter) -> UpDownCounter: + """Node memory available""" + return meter.create_up_down_counter( + name=K8S_NODE_MEMORY_AVAILABLE, + description="Node memory available.", + unit="By", + ) + + +K8S_NODE_MEMORY_PAGING_FAULTS: Final = "k8s.node.memory.paging.faults" +""" +Deprecated: Replaced by `k8s.node.paging.faults`. +""" + + +def create_k8s_node_memory_paging_faults(meter: Meter) -> Counter: + """Deprecated, use `k8s.node.paging.faults` instead""" + return meter.create_counter( + name=K8S_NODE_MEMORY_PAGING_FAULTS, + description="Deprecated, use `k8s.node.paging.faults` instead.", + unit="{fault}", + ) + + +K8S_NODE_MEMORY_RSS: Final = "k8s.node.memory.rss" +""" +Node memory RSS +Instrument: updowncounter +Unit: By +Note: The amount of anonymous and swap cache memory (includes transparent hugepages). +This metric is derived from the [MemoryStats.RSSBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [NodeStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#NodeStats) of the Kubelet's stats API. +""" + + +def create_k8s_node_memory_rss(meter: Meter) -> UpDownCounter: + """Node memory RSS""" + return meter.create_up_down_counter( + name=K8S_NODE_MEMORY_RSS, + description="Node memory RSS.", + unit="By", + ) + + +K8S_NODE_MEMORY_USAGE: Final = "k8s.node.memory.usage" +""" +Memory usage of the Node +Instrument: updowncounter +Unit: By +Note: Total memory in use. This includes all memory regardless of when it was accessed. +This metric is derived from the [MemoryStats.UsageBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [NodeStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#NodeStats) of the Kubelet's stats API. +""" + + +def create_k8s_node_memory_usage(meter: Meter) -> UpDownCounter: + """Memory usage of the Node""" + return meter.create_up_down_counter( + name=K8S_NODE_MEMORY_USAGE, + description="Memory usage of the Node.", + unit="By", + ) + + +K8S_NODE_MEMORY_WORKING_SET: Final = "k8s.node.memory.working_set" +""" +Node memory working set +Instrument: updowncounter +Unit: By +Note: The amount of working set memory. This includes recently accessed memory, dirty memory, and kernel memory. WorkingSetBytes is <= UsageBytes. +This metric is derived from the [MemoryStats.WorkingSetBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [NodeStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#NodeStats) of the Kubelet's stats API. +""" + + +def create_k8s_node_memory_working_set(meter: Meter) -> UpDownCounter: + """Node memory working set""" + return meter.create_up_down_counter( + name=K8S_NODE_MEMORY_WORKING_SET, + description="Node memory working set.", + unit="By", + ) + + +K8S_NODE_NETWORK_ERRORS: Final = "k8s.node.network.errors" +""" +Node network errors +Instrument: counter +Unit: {error} +""" + + +def create_k8s_node_network_errors(meter: Meter) -> Counter: + """Node network errors""" + return meter.create_counter( + name=K8S_NODE_NETWORK_ERRORS, + description="Node network errors.", + unit="{error}", + ) + + +K8S_NODE_NETWORK_IO: Final = "k8s.node.network.io" +""" +Network bytes for the Node +Instrument: counter +Unit: By +""" + + +def create_k8s_node_network_io(meter: Meter) -> Counter: + """Network bytes for the Node""" + return meter.create_counter( + name=K8S_NODE_NETWORK_IO, + description="Network bytes for the Node.", + unit="By", + ) + + +K8S_NODE_PAGING_FAULTS: Final = "k8s.node.paging.faults" +""" +Node memory paging faults +Instrument: counter +Unit: {fault} +Note: Cumulative number of major/minor page faults. +This metric is derived from the [MemoryStats.PageFaults](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) and [MemoryStats.MajorPageFaults](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) fields of the [NodeStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#NodeStats) of the Kubelet's stats API. +""" + + +def create_k8s_node_paging_faults(meter: Meter) -> Counter: + """Node memory paging faults""" + return meter.create_counter( + name=K8S_NODE_PAGING_FAULTS, + description="Node memory paging faults.", + unit="{fault}", + ) + + +K8S_NODE_POD_ALLOCATABLE: Final = "k8s.node.pod.allocatable" +""" +Amount of pods allocatable on the node +Instrument: updowncounter +Unit: {pod} +""" + + +def create_k8s_node_pod_allocatable(meter: Meter) -> UpDownCounter: + """Amount of pods allocatable on the node""" + return meter.create_up_down_counter( + name=K8S_NODE_POD_ALLOCATABLE, + description="Amount of pods allocatable on the node.", + unit="{pod}", + ) + + +K8S_NODE_SYSTEM_CONTAINER_CPU_TIME: Final = "k8s.node.system_container.cpu.time" +""" +Node's system container CPU time +Instrument: counter +Unit: s +Note: This metric is derived from the [CPUStats.UsageCoreNanoSeconds](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L236) field of the [ContainerStats](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L157C6-L157C20) of [Node.SystemContainers](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L40) of the Kubelet's stats API. +""" + + +def create_k8s_node_system_container_cpu_time(meter: Meter) -> Counter: + """Node's system container CPU time""" + return meter.create_counter( + name=K8S_NODE_SYSTEM_CONTAINER_CPU_TIME, + description="Node's system container CPU time.", + unit="s", + ) + + +K8S_NODE_SYSTEM_CONTAINER_CPU_USAGE: Final = "k8s.node.system_container.cpu.usage" +""" +Node's system container CPU usage, measured in CPUs +Instrument: gauge +Unit: {cpu} +Note: CPU usage of the specific System Container on all available CPU cores. It is calculated as the change in cumulative CPU time (k8s.node.system_container.cpu.time) over a measurement interval, divided by the elapsed time: usageCores = (cpuTimeEnd - cpuTimeStart) / elapsedSeconds. +""" + + +def create_k8s_node_system_container_cpu_usage(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Node's system container CPU usage, measured in CPUs""" + return meter.create_observable_gauge( + name=K8S_NODE_SYSTEM_CONTAINER_CPU_USAGE, + callbacks=callbacks, + description="Node's system container CPU usage, measured in CPUs.", + unit="{cpu}", + ) + + +K8S_NODE_SYSTEM_CONTAINER_MEMORY_USAGE: Final = "k8s.node.system_container.memory.usage" +""" +Node's system container memory usage +Instrument: updowncounter +Unit: By +Note: This metric is derived from the [MemoryStats.UsageBytes](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L252) field of the [ContainerStats](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L157C6-L157C20) of [Node.SystemContainers](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L40) of the Kubelet's stats API. +""" + + +def create_k8s_node_system_container_memory_usage(meter: Meter) -> UpDownCounter: + """Node's system container memory usage""" + return meter.create_up_down_counter( + name=K8S_NODE_SYSTEM_CONTAINER_MEMORY_USAGE, + description="Node's system container memory usage.", + unit="By", + ) + + +K8S_NODE_SYSTEM_CONTAINER_MEMORY_WORKING_SET: Final = "k8s.node.system_container.memory.working_set" +""" +The amount of working set memory +Instrument: updowncounter +Unit: By +Note: This metric is derived from the [MemoryStats.WorkingSetBytes](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L256) field of the [ContainerStats](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L157C6-L157C20) of [Node.SystemContainers](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L40) of the Kubelet's stats API. +""" + + +def create_k8s_node_system_container_memory_working_set(meter: Meter) -> UpDownCounter: + """The amount of working set memory""" + return meter.create_up_down_counter( + name=K8S_NODE_SYSTEM_CONTAINER_MEMORY_WORKING_SET, + description="The amount of working set memory.", + unit="By", + ) + + +K8S_NODE_UPTIME: Final = "k8s.node.uptime" +""" +The time the Node has been running +Instrument: gauge +Unit: s +Note: Instrumentations SHOULD use a gauge with type `double` and measure uptime in seconds as a floating point number with the highest precision available. +The actual accuracy would depend on the instrumentation and operating system. +""" + + +def create_k8s_node_uptime(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """The time the Node has been running""" + return meter.create_observable_gauge( + name=K8S_NODE_UPTIME, + callbacks=callbacks, + description="The time the Node has been running.", + unit="s", + ) + + +K8S_PERSISTENTVOLUME_STATUS_PHASE: Final = "k8s.persistentvolume.status.phase" +""" +Number of PersistentVolumes in a given phase +Instrument: updowncounter +Unit: {persistentvolume} +Note: All possible phases should be reported at each interval to avoid gaps in the time series. +This metric is derived from the `.status.phase` field of the +[K8s PersistentVolumeStatus](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-v1/#PersistentVolumeStatus). +""" + + +def create_k8s_persistentvolume_status_phase(meter: Meter) -> UpDownCounter: + """Number of PersistentVolumes in a given phase""" + return meter.create_up_down_counter( + name=K8S_PERSISTENTVOLUME_STATUS_PHASE, + description="Number of PersistentVolumes in a given phase.", + unit="{persistentvolume}", + ) + + +K8S_PERSISTENTVOLUME_STORAGE_CAPACITY: Final = "k8s.persistentvolume.storage.capacity" +""" +The storage capacity of the PersistentVolume +Instrument: updowncounter +Unit: By +Note: This metric is derived from the `.spec.capacity.storage` field of the [K8s PersistentVolumeSpec](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-v1/#PersistentVolumeSpec). +""" + + +def create_k8s_persistentvolume_storage_capacity(meter: Meter) -> UpDownCounter: + """The storage capacity of the PersistentVolume""" + return meter.create_up_down_counter( + name=K8S_PERSISTENTVOLUME_STORAGE_CAPACITY, + description="The storage capacity of the PersistentVolume.", + unit="By", + ) + + +K8S_PERSISTENTVOLUMECLAIM_STATUS_PHASE: Final = "k8s.persistentvolumeclaim.status.phase" +""" +Number of PersistentVolumeClaims in a given phase +Instrument: updowncounter +Unit: {persistentvolumeclaim} +Note: All possible phases should be reported at each interval to avoid gaps in the time series. +This metric is derived from the `.status.phase` field of the +[K8s PersistentVolumeClaimStatus](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-claim-v1/#PersistentVolumeClaimStatus). +""" + + +def create_k8s_persistentvolumeclaim_status_phase(meter: Meter) -> UpDownCounter: + """Number of PersistentVolumeClaims in a given phase""" + return meter.create_up_down_counter( + name=K8S_PERSISTENTVOLUMECLAIM_STATUS_PHASE, + description="Number of PersistentVolumeClaims in a given phase.", + unit="{persistentvolumeclaim}", + ) + + +K8S_PERSISTENTVOLUMECLAIM_STORAGE_CAPACITY: Final = "k8s.persistentvolumeclaim.storage.capacity" +""" +The actual storage capacity provisioned for the PersistentVolumeClaim +Instrument: updowncounter +Unit: By +Note: Only available when the PVC is bound. May differ from the requested capacity due to provisioner rounding. +This metric is derived from the `.status.capacity.storage` field of the +[K8s PersistentVolumeClaimStatus](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-claim-v1/#PersistentVolumeClaimStatus). +""" + + +def create_k8s_persistentvolumeclaim_storage_capacity(meter: Meter) -> UpDownCounter: + """The actual storage capacity provisioned for the PersistentVolumeClaim""" + return meter.create_up_down_counter( + name=K8S_PERSISTENTVOLUMECLAIM_STORAGE_CAPACITY, + description="The actual storage capacity provisioned for the PersistentVolumeClaim.", + unit="By", + ) + + +K8S_PERSISTENTVOLUMECLAIM_STORAGE_REQUEST: Final = "k8s.persistentvolumeclaim.storage.request" +""" +The storage requested by the PersistentVolumeClaim +Instrument: updowncounter +Unit: By +Note: This metric is derived from the `.spec.resources.requests.storage` field of the [K8s PersistentVolumeClaimSpec](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-claim-v1/#PersistentVolumeClaimSpec). +""" + + +def create_k8s_persistentvolumeclaim_storage_request(meter: Meter) -> UpDownCounter: + """The storage requested by the PersistentVolumeClaim""" + return meter.create_up_down_counter( + name=K8S_PERSISTENTVOLUMECLAIM_STORAGE_REQUEST, + description="The storage requested by the PersistentVolumeClaim.", + unit="By", + ) + + +K8S_POD_CPU_TIME: Final = "k8s.pod.cpu.time" +""" +Total CPU time consumed +Instrument: counter +Unit: s +Note: Total CPU time consumed by the specific Pod on all available CPU cores. +""" + + +def create_k8s_pod_cpu_time(meter: Meter) -> Counter: + """Total CPU time consumed""" + return meter.create_counter( + name=K8S_POD_CPU_TIME, + description="Total CPU time consumed.", + unit="s", + ) + + +K8S_POD_CPU_USAGE: Final = "k8s.pod.cpu.usage" +""" +Pod's CPU usage, measured in CPUs. Range from 0 to the number of allocatable CPUs +Instrument: gauge +Unit: {cpu} +Note: CPU usage of the specific Pod on all available CPU cores. It is calculated as the change in cumulative CPU time (k8s.pod.cpu.time) over a measurement interval, divided by the elapsed time: usageCores = (cpuTimeEnd - cpuTimeStart) / elapsedSeconds. +""" + + +def create_k8s_pod_cpu_usage(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Pod's CPU usage, measured in CPUs. Range from 0 to the number of allocatable CPUs""" + return meter.create_observable_gauge( + name=K8S_POD_CPU_USAGE, + callbacks=callbacks, + description="Pod's CPU usage, measured in CPUs. Range from 0 to the number of allocatable CPUs.", + unit="{cpu}", + ) + + +K8S_POD_FILESYSTEM_AVAILABLE: Final = "k8s.pod.filesystem.available" +""" +Pod filesystem available bytes +Instrument: updowncounter +Unit: By +Note: This metric is derived from the +[FsStats.AvailableBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field +of the [PodStats.EphemeralStorage](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#PodStats) +of the Kubelet's stats API. +""" + + +def create_k8s_pod_filesystem_available(meter: Meter) -> UpDownCounter: + """Pod filesystem available bytes""" + return meter.create_up_down_counter( + name=K8S_POD_FILESYSTEM_AVAILABLE, + description="Pod filesystem available bytes.", + unit="By", + ) + + +K8S_POD_FILESYSTEM_CAPACITY: Final = "k8s.pod.filesystem.capacity" +""" +Pod filesystem capacity +Instrument: updowncounter +Unit: By +Note: This metric is derived from the +[FsStats.CapacityBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field +of the [PodStats.EphemeralStorage](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#PodStats) +of the Kubelet's stats API. +""" + + +def create_k8s_pod_filesystem_capacity(meter: Meter) -> UpDownCounter: + """Pod filesystem capacity""" + return meter.create_up_down_counter( + name=K8S_POD_FILESYSTEM_CAPACITY, + description="Pod filesystem capacity.", + unit="By", + ) + + +K8S_POD_FILESYSTEM_USAGE: Final = "k8s.pod.filesystem.usage" +""" +Pod filesystem usage +Instrument: updowncounter +Unit: By +Note: This may not equal capacity - available. + +This metric is derived from the +[FsStats.UsedBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field +of the [PodStats.EphemeralStorage](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#PodStats) +of the Kubelet's stats API. +""" + + +def create_k8s_pod_filesystem_usage(meter: Meter) -> UpDownCounter: + """Pod filesystem usage""" + return meter.create_up_down_counter( + name=K8S_POD_FILESYSTEM_USAGE, + description="Pod filesystem usage.", + unit="By", + ) + + +K8S_POD_MEMORY_AVAILABLE: Final = "k8s.pod.memory.available" +""" +Pod memory available +Instrument: updowncounter +Unit: By +Note: Available memory for use. This is defined as the memory limit - workingSetBytes. If memory limit is undefined, the available bytes is omitted. +This metric is derived from the [MemoryStats.AvailableBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [PodStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#PodStats) of the Kubelet's stats API. +""" + + +def create_k8s_pod_memory_available(meter: Meter) -> UpDownCounter: + """Pod memory available""" + return meter.create_up_down_counter( + name=K8S_POD_MEMORY_AVAILABLE, + description="Pod memory available.", + unit="By", + ) + + +K8S_POD_MEMORY_PAGING_FAULTS: Final = "k8s.pod.memory.paging.faults" +""" +Deprecated: Replaced by `k8s.pod.paging.faults`. +""" + + +def create_k8s_pod_memory_paging_faults(meter: Meter) -> Counter: + """Deprecated, use `k8s.pod.paging.faults` instead""" + return meter.create_counter( + name=K8S_POD_MEMORY_PAGING_FAULTS, + description="Deprecated, use `k8s.pod.paging.faults` instead.", + unit="{fault}", + ) + + +K8S_POD_MEMORY_RSS: Final = "k8s.pod.memory.rss" +""" +Pod memory RSS +Instrument: updowncounter +Unit: By +Note: The amount of anonymous and swap cache memory (includes transparent hugepages). +This metric is derived from the [MemoryStats.RSSBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [PodStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#PodStats) of the Kubelet's stats API. +""" + + +def create_k8s_pod_memory_rss(meter: Meter) -> UpDownCounter: + """Pod memory RSS""" + return meter.create_up_down_counter( + name=K8S_POD_MEMORY_RSS, + description="Pod memory RSS.", + unit="By", + ) + + +K8S_POD_MEMORY_USAGE: Final = "k8s.pod.memory.usage" +""" +Memory usage of the Pod +Instrument: updowncounter +Unit: By +Note: Total memory in use. This includes all memory regardless of when it was accessed. +This metric is derived from the [MemoryStats.UsageBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [PodStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#PodStats) of the Kubelet's stats API. +""" + + +def create_k8s_pod_memory_usage(meter: Meter) -> UpDownCounter: + """Memory usage of the Pod""" + return meter.create_up_down_counter( + name=K8S_POD_MEMORY_USAGE, + description="Memory usage of the Pod.", + unit="By", + ) + + +K8S_POD_MEMORY_WORKING_SET: Final = "k8s.pod.memory.working_set" +""" +Pod memory working set +Instrument: updowncounter +Unit: By +Note: The amount of working set memory. This includes recently accessed memory, dirty memory, and kernel memory. WorkingSetBytes is <= UsageBytes. +This metric is derived from the [MemoryStats.WorkingSetBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [PodStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#PodStats) of the Kubelet's stats API. +""" + + +def create_k8s_pod_memory_working_set(meter: Meter) -> UpDownCounter: + """Pod memory working set""" + return meter.create_up_down_counter( + name=K8S_POD_MEMORY_WORKING_SET, + description="Pod memory working set.", + unit="By", + ) + + +K8S_POD_NETWORK_ERRORS: Final = "k8s.pod.network.errors" +""" +Pod network errors +Instrument: counter +Unit: {error} +""" + + +def create_k8s_pod_network_errors(meter: Meter) -> Counter: + """Pod network errors""" + return meter.create_counter( + name=K8S_POD_NETWORK_ERRORS, + description="Pod network errors.", + unit="{error}", + ) + + +K8S_POD_NETWORK_IO: Final = "k8s.pod.network.io" +""" +Network bytes for the Pod +Instrument: counter +Unit: By +""" + + +def create_k8s_pod_network_io(meter: Meter) -> Counter: + """Network bytes for the Pod""" + return meter.create_counter( + name=K8S_POD_NETWORK_IO, + description="Network bytes for the Pod.", + unit="By", + ) + + +K8S_POD_PAGING_FAULTS: Final = "k8s.pod.paging.faults" +""" +Pod memory paging faults +Instrument: counter +Unit: {fault} +Note: Cumulative number of major/minor page faults. +This metric is derived from the [MemoryStats.PageFaults](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) and [MemoryStats.MajorPageFaults](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [PodStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#PodStats) of the Kubelet's stats API. +""" + + +def create_k8s_pod_paging_faults(meter: Meter) -> Counter: + """Pod memory paging faults""" + return meter.create_counter( + name=K8S_POD_PAGING_FAULTS, + description="Pod memory paging faults.", + unit="{fault}", + ) + + +K8S_POD_STATUS_PHASE: Final = "k8s.pod.status.phase" +""" +Describes number of K8s Pods that are currently in a given phase +Instrument: updowncounter +Unit: {pod} +Note: All possible pod phases will be reported at each time interval to avoid missing metrics. +Only the value corresponding to the current phase will be non-zero. +""" + + +def create_k8s_pod_status_phase(meter: Meter) -> UpDownCounter: + """Describes number of K8s Pods that are currently in a given phase""" + return meter.create_up_down_counter( + name=K8S_POD_STATUS_PHASE, + description="Describes number of K8s Pods that are currently in a given phase.", + unit="{pod}", + ) + + +K8S_POD_STATUS_REASON: Final = "k8s.pod.status.reason" +""" +Describes the number of K8s Pods that are currently in a state for a given reason +Instrument: updowncounter +Unit: {pod} +Note: All possible pod status reasons will be reported at each time interval to avoid missing metrics. +Only the value corresponding to the current reason will be non-zero. +""" + + +def create_k8s_pod_status_reason(meter: Meter) -> UpDownCounter: + """Describes the number of K8s Pods that are currently in a state for a given reason""" + return meter.create_up_down_counter( + name=K8S_POD_STATUS_REASON, + description="Describes the number of K8s Pods that are currently in a state for a given reason.", + unit="{pod}", + ) + + +K8S_POD_UPTIME: Final = "k8s.pod.uptime" +""" +The time the Pod has been running +Instrument: gauge +Unit: s +Note: Instrumentations SHOULD use a gauge with type `double` and measure uptime in seconds as a floating point number with the highest precision available. +The actual accuracy would depend on the instrumentation and operating system. +""" + + +def create_k8s_pod_uptime(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """The time the Pod has been running""" + return meter.create_observable_gauge( + name=K8S_POD_UPTIME, + callbacks=callbacks, + description="The time the Pod has been running.", + unit="s", + ) + + +K8S_POD_VOLUME_AVAILABLE: Final = "k8s.pod.volume.available" +""" +Pod volume storage space available +Instrument: updowncounter +Unit: By +Note: This metric is derived from the +[VolumeStats.AvailableBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#VolumeStats) field +of the [PodStats](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#PodStats) of the +Kubelet's stats API. +""" + + +def create_k8s_pod_volume_available(meter: Meter) -> UpDownCounter: + """Pod volume storage space available""" + return meter.create_up_down_counter( + name=K8S_POD_VOLUME_AVAILABLE, + description="Pod volume storage space available.", + unit="By", + ) + + +K8S_POD_VOLUME_CAPACITY: Final = "k8s.pod.volume.capacity" +""" +Pod volume total capacity +Instrument: updowncounter +Unit: By +Note: This metric is derived from the +[VolumeStats.CapacityBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#VolumeStats) field +of the [PodStats](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#PodStats) of the +Kubelet's stats API. +""" + + +def create_k8s_pod_volume_capacity(meter: Meter) -> UpDownCounter: + """Pod volume total capacity""" + return meter.create_up_down_counter( + name=K8S_POD_VOLUME_CAPACITY, + description="Pod volume total capacity.", + unit="By", + ) + + +K8S_POD_VOLUME_INODE_COUNT: Final = "k8s.pod.volume.inode.count" +""" +The total inodes in the filesystem of the Pod's volume +Instrument: updowncounter +Unit: {inode} +Note: This metric is derived from the +[VolumeStats.Inodes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#VolumeStats) field +of the [PodStats](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#PodStats) of the +Kubelet's stats API. +""" + + +def create_k8s_pod_volume_inode_count(meter: Meter) -> UpDownCounter: + """The total inodes in the filesystem of the Pod's volume""" + return meter.create_up_down_counter( + name=K8S_POD_VOLUME_INODE_COUNT, + description="The total inodes in the filesystem of the Pod's volume.", + unit="{inode}", + ) + + +K8S_POD_VOLUME_INODE_FREE: Final = "k8s.pod.volume.inode.free" +""" +The free inodes in the filesystem of the Pod's volume +Instrument: updowncounter +Unit: {inode} +Note: This metric is derived from the +[VolumeStats.InodesFree](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#VolumeStats) field +of the [PodStats](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#PodStats) of the +Kubelet's stats API. +""" + + +def create_k8s_pod_volume_inode_free(meter: Meter) -> UpDownCounter: + """The free inodes in the filesystem of the Pod's volume""" + return meter.create_up_down_counter( + name=K8S_POD_VOLUME_INODE_FREE, + description="The free inodes in the filesystem of the Pod's volume.", + unit="{inode}", + ) + + +K8S_POD_VOLUME_INODE_USED: Final = "k8s.pod.volume.inode.used" +""" +The inodes used by the filesystem of the Pod's volume +Instrument: updowncounter +Unit: {inode} +Note: This metric is derived from the +[VolumeStats.InodesUsed](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#VolumeStats) field +of the [PodStats](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#PodStats) of the +Kubelet's stats API. + +This may not be equal to `inodes - free` because filesystem may share inodes with other filesystems. +""" + + +def create_k8s_pod_volume_inode_used(meter: Meter) -> UpDownCounter: + """The inodes used by the filesystem of the Pod's volume""" + return meter.create_up_down_counter( + name=K8S_POD_VOLUME_INODE_USED, + description="The inodes used by the filesystem of the Pod's volume.", + unit="{inode}", + ) + + +K8S_POD_VOLUME_USAGE: Final = "k8s.pod.volume.usage" +""" +Pod volume usage +Instrument: updowncounter +Unit: By +Note: This may not equal capacity - available. + +This metric is derived from the +[VolumeStats.UsedBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#VolumeStats) field +of the [PodStats](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#PodStats) of the +Kubelet's stats API. +""" + + +def create_k8s_pod_volume_usage(meter: Meter) -> UpDownCounter: + """Pod volume usage""" + return meter.create_up_down_counter( + name=K8S_POD_VOLUME_USAGE, + description="Pod volume usage.", + unit="By", + ) + + +K8S_REPLICASET_AVAILABLE_PODS: Final = "k8s.replicaset.available_pods" +""" +Deprecated: Replaced by `k8s.replicaset.pod.available`. +""" + + +def create_k8s_replicaset_available_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.replicaset.pod.available` instead""" + return meter.create_up_down_counter( + name=K8S_REPLICASET_AVAILABLE_PODS, + description="Deprecated, use `k8s.replicaset.pod.available` instead.", + unit="{pod}", + ) + + +K8S_REPLICASET_DESIRED_PODS: Final = "k8s.replicaset.desired_pods" +""" +Deprecated: Replaced by `k8s.replicaset.pod.desired`. +""" + + +def create_k8s_replicaset_desired_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.replicaset.pod.desired` instead""" + return meter.create_up_down_counter( + name=K8S_REPLICASET_DESIRED_PODS, + description="Deprecated, use `k8s.replicaset.pod.desired` instead.", + unit="{pod}", + ) + + +K8S_REPLICASET_POD_AVAILABLE: Final = "k8s.replicaset.pod.available" +""" +Total number of available replica pods (ready for at least minReadySeconds) targeted by this replicaset +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `availableReplicas` field of the +[K8s ReplicaSetStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#replicasetstatus-v1-apps). +""" + + +def create_k8s_replicaset_pod_available(meter: Meter) -> UpDownCounter: + """Total number of available replica pods (ready for at least minReadySeconds) targeted by this replicaset""" + return meter.create_up_down_counter( + name=K8S_REPLICASET_POD_AVAILABLE, + description="Total number of available replica pods (ready for at least minReadySeconds) targeted by this replicaset.", + unit="{pod}", + ) + + +K8S_REPLICASET_POD_DESIRED: Final = "k8s.replicaset.pod.desired" +""" +Number of desired replica pods in this replicaset +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `replicas` field of the +[K8s ReplicaSetSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#replicasetspec-v1-apps). +""" + + +def create_k8s_replicaset_pod_desired(meter: Meter) -> UpDownCounter: + """Number of desired replica pods in this replicaset""" + return meter.create_up_down_counter( + name=K8S_REPLICASET_POD_DESIRED, + description="Number of desired replica pods in this replicaset.", + unit="{pod}", + ) + + +K8S_REPLICATION_CONTROLLER_AVAILABLE_PODS: Final = "k8s.replication_controller.available_pods" +""" +Deprecated: Replaced by `k8s.replicationcontroller.pod.available`. +""" + + +def create_k8s_replication_controller_available_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.replicationcontroller.pod.available` instead""" + return meter.create_up_down_counter( + name=K8S_REPLICATION_CONTROLLER_AVAILABLE_PODS, + description="Deprecated, use `k8s.replicationcontroller.pod.available` instead.", + unit="{pod}", + ) + + +K8S_REPLICATION_CONTROLLER_DESIRED_PODS: Final = "k8s.replication_controller.desired_pods" +""" +Deprecated: Replaced by `k8s.replicationcontroller.pod.desired`. +""" + + +def create_k8s_replication_controller_desired_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.replicationcontroller.pod.desired` instead""" + return meter.create_up_down_counter( + name=K8S_REPLICATION_CONTROLLER_DESIRED_PODS, + description="Deprecated, use `k8s.replicationcontroller.pod.desired` instead.", + unit="{pod}", + ) + + +K8S_REPLICATIONCONTROLLER_AVAILABLE_PODS: Final = "k8s.replicationcontroller.available_pods" +""" +Deprecated: Replaced by `k8s.replicationcontroller.pod.available`. +""" + + +def create_k8s_replicationcontroller_available_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.replicationcontroller.pod.available` instead""" + return meter.create_up_down_counter( + name=K8S_REPLICATIONCONTROLLER_AVAILABLE_PODS, + description="Deprecated, use `k8s.replicationcontroller.pod.available` instead.", + unit="{pod}", + ) + + +K8S_REPLICATIONCONTROLLER_DESIRED_PODS: Final = "k8s.replicationcontroller.desired_pods" +""" +Deprecated: Replaced by `k8s.replicationcontroller.pod.desired`. +""" + + +def create_k8s_replicationcontroller_desired_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.replicationcontroller.pod.desired` instead""" + return meter.create_up_down_counter( + name=K8S_REPLICATIONCONTROLLER_DESIRED_PODS, + description="Deprecated, use `k8s.replicationcontroller.pod.desired` instead.", + unit="{pod}", + ) + + +K8S_REPLICATIONCONTROLLER_POD_AVAILABLE: Final = "k8s.replicationcontroller.pod.available" +""" +Total number of available replica pods (ready for at least minReadySeconds) targeted by this replication controller +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `availableReplicas` field of the +[K8s ReplicationControllerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#replicationcontrollerstatus-v1-core). +""" + + +def create_k8s_replicationcontroller_pod_available(meter: Meter) -> UpDownCounter: + """Total number of available replica pods (ready for at least minReadySeconds) targeted by this replication controller""" + return meter.create_up_down_counter( + name=K8S_REPLICATIONCONTROLLER_POD_AVAILABLE, + description="Total number of available replica pods (ready for at least minReadySeconds) targeted by this replication controller.", + unit="{pod}", + ) + + +K8S_REPLICATIONCONTROLLER_POD_DESIRED: Final = "k8s.replicationcontroller.pod.desired" +""" +Number of desired replica pods in this replication controller +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `replicas` field of the +[K8s ReplicationControllerSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#replicationcontrollerspec-v1-core). +""" + + +def create_k8s_replicationcontroller_pod_desired(meter: Meter) -> UpDownCounter: + """Number of desired replica pods in this replication controller""" + return meter.create_up_down_counter( + name=K8S_REPLICATIONCONTROLLER_POD_DESIRED, + description="Number of desired replica pods in this replication controller.", + unit="{pod}", + ) + + +K8S_RESOURCEQUOTA_CPU_LIMIT_HARD: Final = "k8s.resourcequota.cpu.limit.hard" +""" +The CPU limits in a specific namespace. +The value represents the configured quota limit of the resource in the namespace +Instrument: updowncounter +Unit: {cpu} +Note: This metric is retrieved from the `hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_cpu_limit_hard(meter: Meter) -> UpDownCounter: + """The CPU limits in a specific namespace. + The value represents the configured quota limit of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_CPU_LIMIT_HARD, + description="The CPU limits in a specific namespace. The value represents the configured quota limit of the resource in the namespace.", + unit="{cpu}", + ) + + +K8S_RESOURCEQUOTA_CPU_LIMIT_USED: Final = "k8s.resourcequota.cpu.limit.used" +""" +The CPU limits in a specific namespace. +The value represents the current observed total usage of the resource in the namespace +Instrument: updowncounter +Unit: {cpu} +Note: This metric is retrieved from the `used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_cpu_limit_used(meter: Meter) -> UpDownCounter: + """The CPU limits in a specific namespace. + The value represents the current observed total usage of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_CPU_LIMIT_USED, + description="The CPU limits in a specific namespace. The value represents the current observed total usage of the resource in the namespace.", + unit="{cpu}", + ) + + +K8S_RESOURCEQUOTA_CPU_REQUEST_HARD: Final = "k8s.resourcequota.cpu.request.hard" +""" +The CPU requests in a specific namespace. +The value represents the configured quota limit of the resource in the namespace +Instrument: updowncounter +Unit: {cpu} +Note: This metric is retrieved from the `hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_cpu_request_hard(meter: Meter) -> UpDownCounter: + """The CPU requests in a specific namespace. + The value represents the configured quota limit of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_CPU_REQUEST_HARD, + description="The CPU requests in a specific namespace. The value represents the configured quota limit of the resource in the namespace.", + unit="{cpu}", + ) + + +K8S_RESOURCEQUOTA_CPU_REQUEST_USED: Final = "k8s.resourcequota.cpu.request.used" +""" +The CPU requests in a specific namespace. +The value represents the current observed total usage of the resource in the namespace +Instrument: updowncounter +Unit: {cpu} +Note: This metric is retrieved from the `used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_cpu_request_used(meter: Meter) -> UpDownCounter: + """The CPU requests in a specific namespace. + The value represents the current observed total usage of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_CPU_REQUEST_USED, + description="The CPU requests in a specific namespace. The value represents the current observed total usage of the resource in the namespace.", + unit="{cpu}", + ) + + +K8S_RESOURCEQUOTA_EPHEMERAL_STORAGE_LIMIT_HARD: Final = "k8s.resourcequota.ephemeral_storage.limit.hard" +""" +The sum of local ephemeral storage limits in the namespace. +The value represents the configured quota limit of the resource in the namespace +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_ephemeral_storage_limit_hard(meter: Meter) -> UpDownCounter: + """The sum of local ephemeral storage limits in the namespace. + The value represents the configured quota limit of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_EPHEMERAL_STORAGE_LIMIT_HARD, + description="The sum of local ephemeral storage limits in the namespace. The value represents the configured quota limit of the resource in the namespace.", + unit="By", + ) + + +K8S_RESOURCEQUOTA_EPHEMERAL_STORAGE_LIMIT_USED: Final = "k8s.resourcequota.ephemeral_storage.limit.used" +""" +The sum of local ephemeral storage limits in the namespace. +The value represents the current observed total usage of the resource in the namespace +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_ephemeral_storage_limit_used(meter: Meter) -> UpDownCounter: + """The sum of local ephemeral storage limits in the namespace. + The value represents the current observed total usage of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_EPHEMERAL_STORAGE_LIMIT_USED, + description="The sum of local ephemeral storage limits in the namespace. The value represents the current observed total usage of the resource in the namespace.", + unit="By", + ) + + +K8S_RESOURCEQUOTA_EPHEMERAL_STORAGE_REQUEST_HARD: Final = "k8s.resourcequota.ephemeral_storage.request.hard" +""" +The sum of local ephemeral storage requests in the namespace. +The value represents the configured quota limit of the resource in the namespace +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_ephemeral_storage_request_hard(meter: Meter) -> UpDownCounter: + """The sum of local ephemeral storage requests in the namespace. + The value represents the configured quota limit of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_EPHEMERAL_STORAGE_REQUEST_HARD, + description="The sum of local ephemeral storage requests in the namespace. The value represents the configured quota limit of the resource in the namespace.", + unit="By", + ) + + +K8S_RESOURCEQUOTA_EPHEMERAL_STORAGE_REQUEST_USED: Final = "k8s.resourcequota.ephemeral_storage.request.used" +""" +The sum of local ephemeral storage requests in the namespace. +The value represents the current observed total usage of the resource in the namespace +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_ephemeral_storage_request_used(meter: Meter) -> UpDownCounter: + """The sum of local ephemeral storage requests in the namespace. + The value represents the current observed total usage of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_EPHEMERAL_STORAGE_REQUEST_USED, + description="The sum of local ephemeral storage requests in the namespace. The value represents the current observed total usage of the resource in the namespace.", + unit="By", + ) + + +K8S_RESOURCEQUOTA_HUGEPAGE_COUNT_REQUEST_HARD: Final = "k8s.resourcequota.hugepage_count.request.hard" +""" +The huge page requests in a specific namespace. +The value represents the configured quota limit of the resource in the namespace +Instrument: updowncounter +Unit: {hugepage} +Note: This metric is retrieved from the `hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_hugepage_count_request_hard(meter: Meter) -> UpDownCounter: + """The huge page requests in a specific namespace. + The value represents the configured quota limit of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_HUGEPAGE_COUNT_REQUEST_HARD, + description="The huge page requests in a specific namespace. The value represents the configured quota limit of the resource in the namespace.", + unit="{hugepage}", + ) + + +K8S_RESOURCEQUOTA_HUGEPAGE_COUNT_REQUEST_USED: Final = "k8s.resourcequota.hugepage_count.request.used" +""" +The huge page requests in a specific namespace. +The value represents the current observed total usage of the resource in the namespace +Instrument: updowncounter +Unit: {hugepage} +Note: This metric is retrieved from the `used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_hugepage_count_request_used(meter: Meter) -> UpDownCounter: + """The huge page requests in a specific namespace. + The value represents the current observed total usage of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_HUGEPAGE_COUNT_REQUEST_USED, + description="The huge page requests in a specific namespace. The value represents the current observed total usage of the resource in the namespace.", + unit="{hugepage}", + ) + + +K8S_RESOURCEQUOTA_MEMORY_LIMIT_HARD: Final = "k8s.resourcequota.memory.limit.hard" +""" +The memory limits in a specific namespace. +The value represents the configured quota limit of the resource in the namespace +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_memory_limit_hard(meter: Meter) -> UpDownCounter: + """The memory limits in a specific namespace. + The value represents the configured quota limit of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_MEMORY_LIMIT_HARD, + description="The memory limits in a specific namespace. The value represents the configured quota limit of the resource in the namespace.", + unit="By", + ) + + +K8S_RESOURCEQUOTA_MEMORY_LIMIT_USED: Final = "k8s.resourcequota.memory.limit.used" +""" +The memory limits in a specific namespace. +The value represents the current observed total usage of the resource in the namespace +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_memory_limit_used(meter: Meter) -> UpDownCounter: + """The memory limits in a specific namespace. + The value represents the current observed total usage of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_MEMORY_LIMIT_USED, + description="The memory limits in a specific namespace. The value represents the current observed total usage of the resource in the namespace.", + unit="By", + ) + + +K8S_RESOURCEQUOTA_MEMORY_REQUEST_HARD: Final = "k8s.resourcequota.memory.request.hard" +""" +The memory requests in a specific namespace. +The value represents the configured quota limit of the resource in the namespace +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_memory_request_hard(meter: Meter) -> UpDownCounter: + """The memory requests in a specific namespace. + The value represents the configured quota limit of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_MEMORY_REQUEST_HARD, + description="The memory requests in a specific namespace. The value represents the configured quota limit of the resource in the namespace.", + unit="By", + ) + + +K8S_RESOURCEQUOTA_MEMORY_REQUEST_USED: Final = "k8s.resourcequota.memory.request.used" +""" +The memory requests in a specific namespace. +The value represents the current observed total usage of the resource in the namespace +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_memory_request_used(meter: Meter) -> UpDownCounter: + """The memory requests in a specific namespace. + The value represents the current observed total usage of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_MEMORY_REQUEST_USED, + description="The memory requests in a specific namespace. The value represents the current observed total usage of the resource in the namespace.", + unit="By", + ) + + +K8S_RESOURCEQUOTA_OBJECT_COUNT_HARD: Final = "k8s.resourcequota.object_count.hard" +""" +The object count limits in a specific namespace. +The value represents the configured quota limit of the resource in the namespace +Instrument: updowncounter +Unit: {object} +Note: This metric is retrieved from the `hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_object_count_hard(meter: Meter) -> UpDownCounter: + """The object count limits in a specific namespace. + The value represents the configured quota limit of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_OBJECT_COUNT_HARD, + description="The object count limits in a specific namespace. The value represents the configured quota limit of the resource in the namespace.", + unit="{object}", + ) + + +K8S_RESOURCEQUOTA_OBJECT_COUNT_USED: Final = "k8s.resourcequota.object_count.used" +""" +The object count limits in a specific namespace. +The value represents the current observed total usage of the resource in the namespace +Instrument: updowncounter +Unit: {object} +Note: This metric is retrieved from the `used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). +""" + + +def create_k8s_resourcequota_object_count_used(meter: Meter) -> UpDownCounter: + """The object count limits in a specific namespace. + The value represents the current observed total usage of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_OBJECT_COUNT_USED, + description="The object count limits in a specific namespace. The value represents the current observed total usage of the resource in the namespace.", + unit="{object}", + ) + + +K8S_RESOURCEQUOTA_PERSISTENTVOLUMECLAIM_COUNT_HARD: Final = "k8s.resourcequota.persistentvolumeclaim_count.hard" +""" +The total number of PersistentVolumeClaims that can exist in the namespace. +The value represents the configured quota limit of the resource in the namespace +Instrument: updowncounter +Unit: {persistentvolumeclaim} +Note: This metric is retrieved from the `hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). + +The `k8s.storageclass.name` should be required when a resource quota is defined for a specific +storage class. +""" + + +def create_k8s_resourcequota_persistentvolumeclaim_count_hard(meter: Meter) -> UpDownCounter: + """The total number of PersistentVolumeClaims that can exist in the namespace. + The value represents the configured quota limit of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_PERSISTENTVOLUMECLAIM_COUNT_HARD, + description="The total number of PersistentVolumeClaims that can exist in the namespace. The value represents the configured quota limit of the resource in the namespace.", + unit="{persistentvolumeclaim}", + ) + + +K8S_RESOURCEQUOTA_PERSISTENTVOLUMECLAIM_COUNT_USED: Final = "k8s.resourcequota.persistentvolumeclaim_count.used" +""" +The total number of PersistentVolumeClaims that can exist in the namespace. +The value represents the current observed total usage of the resource in the namespace +Instrument: updowncounter +Unit: {persistentvolumeclaim} +Note: This metric is retrieved from the `used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). + +The `k8s.storageclass.name` should be required when a resource quota is defined for a specific +storage class. +""" + + +def create_k8s_resourcequota_persistentvolumeclaim_count_used(meter: Meter) -> UpDownCounter: + """The total number of PersistentVolumeClaims that can exist in the namespace. + The value represents the current observed total usage of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_PERSISTENTVOLUMECLAIM_COUNT_USED, + description="The total number of PersistentVolumeClaims that can exist in the namespace. The value represents the current observed total usage of the resource in the namespace.", + unit="{persistentvolumeclaim}", + ) + + +K8S_RESOURCEQUOTA_STORAGE_REQUEST_HARD: Final = "k8s.resourcequota.storage.request.hard" +""" +The storage requests in a specific namespace. +The value represents the configured quota limit of the resource in the namespace +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). + +The `k8s.storageclass.name` should be required when a resource quota is defined for a specific +storage class. +""" + + +def create_k8s_resourcequota_storage_request_hard(meter: Meter) -> UpDownCounter: + """The storage requests in a specific namespace. + The value represents the configured quota limit of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_STORAGE_REQUEST_HARD, + description="The storage requests in a specific namespace. The value represents the configured quota limit of the resource in the namespace.", + unit="By", + ) + + +K8S_RESOURCEQUOTA_STORAGE_REQUEST_USED: Final = "k8s.resourcequota.storage.request.used" +""" +The storage requests in a specific namespace. +The value represents the current observed total usage of the resource in the namespace +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core). + +The `k8s.storageclass.name` should be required when a resource quota is defined for a specific +storage class. +""" + + +def create_k8s_resourcequota_storage_request_used(meter: Meter) -> UpDownCounter: + """The storage requests in a specific namespace. + The value represents the current observed total usage of the resource in the namespace""" + return meter.create_up_down_counter( + name=K8S_RESOURCEQUOTA_STORAGE_REQUEST_USED, + description="The storage requests in a specific namespace. The value represents the current observed total usage of the resource in the namespace.", + unit="By", + ) + + +K8S_SERVICE_ENDPOINT_COUNT: Final = "k8s.service.endpoint.count" +""" +Number of endpoints for a service by condition and address type +Instrument: gauge +Unit: {endpoint} +Note: This metric is derived from the Kubernetes [EndpointSlice API](https://kubernetes.io/docs/reference/kubernetes-api/service-resources/endpoint-slice-v1/). +It reports the number of network endpoints backing a Service, broken down by their condition and address type. + +In dual-stack or multi-protocol clusters, separate counts are reported for each address family (`IPv4`, `IPv6`, `FQDN`). + +When the optional `zone` attribute is enabled, counts are further broken down by availability zone for zone-aware monitoring. + +An endpoint may be reported under multiple conditions simultaneously (e.g., both `serving` and `terminating` during a graceful shutdown). +See [K8s EndpointConditions](https://kubernetes.io/docs/reference/kubernetes-api/service-resources/endpoint-slice-v1/) for more details. + +The conditions represent: +- `ready`: Endpoints capable of receiving new connections. +- `serving`: Endpoints currently handling traffic. +- `terminating`: Endpoints that are being phased out but may still be handling existing connections. + +For Services with `publishNotReadyAddresses` enabled (common for headless StatefulSets), +this metric will include endpoints that are published despite not being ready. +The `k8s.service.publish_not_ready_addresses` resource attribute indicates this setting. +""" + + +def create_k8s_service_endpoint_count(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Number of endpoints for a service by condition and address type""" + return meter.create_observable_gauge( + name=K8S_SERVICE_ENDPOINT_COUNT, + callbacks=callbacks, + description="Number of endpoints for a service by condition and address type.", + unit="{endpoint}", + ) + + +K8S_SERVICE_LOAD_BALANCER_INGRESS_COUNT: Final = "k8s.service.load_balancer.ingress.count" +""" +Number of load balancer ingress points (external IPs/hostnames) assigned to the service +Instrument: gauge +Unit: {ingress} +Note: This metric reports the number of external ingress points (IP addresses or hostnames) +assigned to a LoadBalancer Service. + +It is only emitted for Services of type `LoadBalancer` and reflects the assignments +made by the underlying infrastructure's load balancer controller in the +[.status.loadBalancer.ingress](https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/#ServiceStatus) field. + +A value of `0` indicates that no ingress points have been assigned yet (e.g., during provisioning). +A value greater than `1` may occur when multiple IPs or hostnames are assigned (e.g., dual-stack configurations). + +This metric signals that external endpoints have been assigned by the load balancer controller, but it does not +guarantee that the load balancer is healthy. +""" + + +def create_k8s_service_load_balancer_ingress_count( + meter: Meter, callbacks: Sequence[CallbackT] | None +) -> ObservableGauge: + """Number of load balancer ingress points (external IPs/hostnames) assigned to the service""" + return meter.create_observable_gauge( + name=K8S_SERVICE_LOAD_BALANCER_INGRESS_COUNT, + callbacks=callbacks, + description="Number of load balancer ingress points (external IPs/hostnames) assigned to the service.", + unit="{ingress}", + ) + + +K8S_STATEFULSET_CURRENT_PODS: Final = "k8s.statefulset.current_pods" +""" +Deprecated: Replaced by `k8s.statefulset.pod.current`. +""" + + +def create_k8s_statefulset_current_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.statefulset.pod.current` instead""" + return meter.create_up_down_counter( + name=K8S_STATEFULSET_CURRENT_PODS, + description="Deprecated, use `k8s.statefulset.pod.current` instead.", + unit="{pod}", + ) + + +K8S_STATEFULSET_DESIRED_PODS: Final = "k8s.statefulset.desired_pods" +""" +Deprecated: Replaced by `k8s.statefulset.pod.desired`. +""" + + +def create_k8s_statefulset_desired_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.statefulset.pod.desired` instead""" + return meter.create_up_down_counter( + name=K8S_STATEFULSET_DESIRED_PODS, + description="Deprecated, use `k8s.statefulset.pod.desired` instead.", + unit="{pod}", + ) + + +K8S_STATEFULSET_POD_CURRENT: Final = "k8s.statefulset.pod.current" +""" +The number of replica pods created by the statefulset controller from the statefulset version indicated by currentRevision +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `currentReplicas` field of the +[K8s StatefulSetStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#statefulsetstatus-v1-apps). +""" + + +def create_k8s_statefulset_pod_current(meter: Meter) -> UpDownCounter: + """The number of replica pods created by the statefulset controller from the statefulset version indicated by currentRevision""" + return meter.create_up_down_counter( + name=K8S_STATEFULSET_POD_CURRENT, + description="The number of replica pods created by the statefulset controller from the statefulset version indicated by currentRevision.", + unit="{pod}", + ) + + +K8S_STATEFULSET_POD_DESIRED: Final = "k8s.statefulset.pod.desired" +""" +Number of desired replica pods in this statefulset +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `replicas` field of the +[K8s StatefulSetSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#statefulsetspec-v1-apps). +""" + + +def create_k8s_statefulset_pod_desired(meter: Meter) -> UpDownCounter: + """Number of desired replica pods in this statefulset""" + return meter.create_up_down_counter( + name=K8S_STATEFULSET_POD_DESIRED, + description="Number of desired replica pods in this statefulset.", + unit="{pod}", + ) + + +K8S_STATEFULSET_POD_READY: Final = "k8s.statefulset.pod.ready" +""" +The number of replica pods created for this statefulset with a Ready Condition +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `readyReplicas` field of the +[K8s StatefulSetStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#statefulsetstatus-v1-apps). +""" + + +def create_k8s_statefulset_pod_ready(meter: Meter) -> UpDownCounter: + """The number of replica pods created for this statefulset with a Ready Condition""" + return meter.create_up_down_counter( + name=K8S_STATEFULSET_POD_READY, + description="The number of replica pods created for this statefulset with a Ready Condition.", + unit="{pod}", + ) + + +K8S_STATEFULSET_POD_UPDATED: Final = "k8s.statefulset.pod.updated" +""" +Number of replica pods created by the statefulset controller from the statefulset version indicated by updateRevision +Instrument: updowncounter +Unit: {pod} +Note: This metric aligns with the `updatedReplicas` field of the +[K8s StatefulSetStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#statefulsetstatus-v1-apps). +""" + + +def create_k8s_statefulset_pod_updated(meter: Meter) -> UpDownCounter: + """Number of replica pods created by the statefulset controller from the statefulset version indicated by updateRevision""" + return meter.create_up_down_counter( + name=K8S_STATEFULSET_POD_UPDATED, + description="Number of replica pods created by the statefulset controller from the statefulset version indicated by updateRevision.", + unit="{pod}", + ) + + +K8S_STATEFULSET_READY_PODS: Final = "k8s.statefulset.ready_pods" +""" +Deprecated: Replaced by `k8s.statefulset.pod.ready`. +""" + + +def create_k8s_statefulset_ready_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.statefulset.pod.ready` instead""" + return meter.create_up_down_counter( + name=K8S_STATEFULSET_READY_PODS, + description="Deprecated, use `k8s.statefulset.pod.ready` instead.", + unit="{pod}", + ) + + +K8S_STATEFULSET_UPDATED_PODS: Final = "k8s.statefulset.updated_pods" +""" +Deprecated: Replaced by `k8s.statefulset.pod.updated`. +""" + + +def create_k8s_statefulset_updated_pods(meter: Meter) -> UpDownCounter: + """Deprecated, use `k8s.statefulset.pod.updated` instead""" + return meter.create_up_down_counter( + name=K8S_STATEFULSET_UPDATED_PODS, + description="Deprecated, use `k8s.statefulset.pod.updated` instead.", + unit="{pod}", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/mcp_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/mcp_metrics.py new file mode 100644 index 0000000..f843a70 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/mcp_metrics.py @@ -0,0 +1,66 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Histogram, Meter + +MCP_CLIENT_OPERATION_DURATION: Final = "mcp.client.operation.duration" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +def create_mcp_client_operation_duration(meter: Meter) -> Histogram: + """The duration of the MCP request or notification as observed on the sender from the time it was sent until the response or ack is received""" + return meter.create_histogram( + name=MCP_CLIENT_OPERATION_DURATION, + description="The duration of the MCP request or notification as observed on the sender from the time it was sent until the response or ack is received.", + unit="s", + ) + + +MCP_CLIENT_SESSION_DURATION: Final = "mcp.client.session.duration" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +def create_mcp_client_session_duration(meter: Meter) -> Histogram: + """The duration of the MCP session as observed on the MCP client""" + return meter.create_histogram( + name=MCP_CLIENT_SESSION_DURATION, + description="The duration of the MCP session as observed on the MCP client.", + unit="s", + ) + + +MCP_SERVER_OPERATION_DURATION: Final = "mcp.server.operation.duration" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +def create_mcp_server_operation_duration(meter: Meter) -> Histogram: + """MCP request or notification duration as observed on the receiver from the time it was received until the result or ack is sent""" + return meter.create_histogram( + name=MCP_SERVER_OPERATION_DURATION, + description="MCP request or notification duration as observed on the receiver from the time it was received until the result or ack is sent.", + unit="s", + ) + + +MCP_SERVER_SESSION_DURATION: Final = "mcp.server.session.duration" +""" +Deprecated: Moved to the [OpenTelemetry GenAI semantic conventions repository](https://github.com/open-telemetry/semantic-conventions-genai). +""" + + +def create_mcp_server_session_duration(meter: Meter) -> Histogram: + """The duration of the MCP session as observed on the MCP server""" + return meter.create_histogram( + name=MCP_SERVER_SESSION_DURATION, + description="The duration of the MCP session as observed on the MCP server.", + unit="s", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/messaging_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/messaging_metrics.py new file mode 100644 index 0000000..e16a394 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/messaging_metrics.py @@ -0,0 +1,169 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Counter, Histogram, Meter + +MESSAGING_CLIENT_CONSUMED_MESSAGES: Final = "messaging.client.consumed.messages" +""" +Number of messages that were delivered to the application +Instrument: counter +Unit: {message} +Note: Records the number of messages pulled from the broker or number of messages dispatched to the application in push-based scenarios. +The metric SHOULD be reported once per message delivery. For example, if receiving and processing operations are both instrumented for a single message delivery, this counter is incremented when the message is received and not reported when it is processed. +""" + + +def create_messaging_client_consumed_messages(meter: Meter) -> Counter: + """Number of messages that were delivered to the application""" + return meter.create_counter( + name=MESSAGING_CLIENT_CONSUMED_MESSAGES, + description="Number of messages that were delivered to the application.", + unit="{message}", + ) + + +MESSAGING_CLIENT_OPERATION_DURATION: Final = "messaging.client.operation.duration" +""" +Duration of messaging operation initiated by a producer or consumer client +Instrument: histogram +Unit: s +Note: This metric SHOULD NOT be used to report processing duration - processing duration is reported in `messaging.process.duration` metric. +""" + + +def create_messaging_client_operation_duration(meter: Meter) -> Histogram: + """Duration of messaging operation initiated by a producer or consumer client""" + return meter.create_histogram( + name=MESSAGING_CLIENT_OPERATION_DURATION, + description="Duration of messaging operation initiated by a producer or consumer client.", + unit="s", + ) + + +MESSAGING_CLIENT_PUBLISHED_MESSAGES: Final = "messaging.client.published.messages" +""" +Deprecated: Replaced by `messaging.client.sent.messages`. +""" + + +def create_messaging_client_published_messages(meter: Meter) -> Counter: + """Deprecated. Use `messaging.client.sent.messages` instead""" + return meter.create_counter( + name=MESSAGING_CLIENT_PUBLISHED_MESSAGES, + description="Deprecated. Use `messaging.client.sent.messages` instead.", + unit="{message}", + ) + + +MESSAGING_CLIENT_SENT_MESSAGES: Final = "messaging.client.sent.messages" +""" +Number of messages producer attempted to send to the broker +Instrument: counter +Unit: {message} +Note: This metric MUST NOT count messages that were created but haven't yet been sent. +""" + + +def create_messaging_client_sent_messages(meter: Meter) -> Counter: + """Number of messages producer attempted to send to the broker""" + return meter.create_counter( + name=MESSAGING_CLIENT_SENT_MESSAGES, + description="Number of messages producer attempted to send to the broker.", + unit="{message}", + ) + + +MESSAGING_PROCESS_DURATION: Final = "messaging.process.duration" +""" +Duration of processing operation +Instrument: histogram +Unit: s +Note: This metric MUST be reported for operations with `messaging.operation.type` that matches `process`. +""" + + +def create_messaging_process_duration(meter: Meter) -> Histogram: + """Duration of processing operation""" + return meter.create_histogram( + name=MESSAGING_PROCESS_DURATION, + description="Duration of processing operation.", + unit="s", + ) + + +MESSAGING_PROCESS_MESSAGES: Final = "messaging.process.messages" +""" +Deprecated: Replaced by `messaging.client.consumed.messages`. +""" + + +def create_messaging_process_messages(meter: Meter) -> Counter: + """Deprecated. Use `messaging.client.consumed.messages` instead""" + return meter.create_counter( + name=MESSAGING_PROCESS_MESSAGES, + description="Deprecated. Use `messaging.client.consumed.messages` instead.", + unit="{message}", + ) + + +MESSAGING_PUBLISH_DURATION: Final = "messaging.publish.duration" +""" +Deprecated: Replaced by `messaging.client.operation.duration`. +""" + + +def create_messaging_publish_duration(meter: Meter) -> Histogram: + """Deprecated. Use `messaging.client.operation.duration` instead""" + return meter.create_histogram( + name=MESSAGING_PUBLISH_DURATION, + description="Deprecated. Use `messaging.client.operation.duration` instead.", + unit="s", + ) + + +MESSAGING_PUBLISH_MESSAGES: Final = "messaging.publish.messages" +""" +Deprecated: Replaced by `messaging.client.sent.messages`. +""" + + +def create_messaging_publish_messages(meter: Meter) -> Counter: + """Deprecated. Use `messaging.client.sent.messages` instead""" + return meter.create_counter( + name=MESSAGING_PUBLISH_MESSAGES, + description="Deprecated. Use `messaging.client.sent.messages` instead.", + unit="{message}", + ) + + +MESSAGING_RECEIVE_DURATION: Final = "messaging.receive.duration" +""" +Deprecated: Replaced by `messaging.client.operation.duration`. +""" + + +def create_messaging_receive_duration(meter: Meter) -> Histogram: + """Deprecated. Use `messaging.client.operation.duration` instead""" + return meter.create_histogram( + name=MESSAGING_RECEIVE_DURATION, + description="Deprecated. Use `messaging.client.operation.duration` instead.", + unit="s", + ) + + +MESSAGING_RECEIVE_MESSAGES: Final = "messaging.receive.messages" +""" +Deprecated: Replaced by `messaging.client.consumed.messages`. +""" + + +def create_messaging_receive_messages(meter: Meter) -> Counter: + """Deprecated. Use `messaging.client.consumed.messages` instead""" + return meter.create_counter( + name=MESSAGING_RECEIVE_MESSAGES, + description="Deprecated. Use `messaging.client.consumed.messages` instead.", + unit="{message}", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/nfs_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/nfs_metrics.py new file mode 100644 index 0000000..99f626f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/nfs_metrics.py @@ -0,0 +1,290 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Counter, Meter, UpDownCounter + +NFS_CLIENT_NET_COUNT: Final = "nfs.client.net.count" +""" +Reports the count of kernel NFS client TCP segments and UDP datagrams handled +Instrument: counter +Unit: {record} +Note: Linux: this metric is taken from the Linux kernel's svc_stat.netudpcnt and svc_stat.nettcpcnt. +""" + + +def create_nfs_client_net_count(meter: Meter) -> Counter: + """Reports the count of kernel NFS client TCP segments and UDP datagrams handled""" + return meter.create_counter( + name=NFS_CLIENT_NET_COUNT, + description="Reports the count of kernel NFS client TCP segments and UDP datagrams handled.", + unit="{record}", + ) + + +NFS_CLIENT_NET_TCP_CONNECTION_ACCEPTED: Final = "nfs.client.net.tcp.connection.accepted" +""" +Reports the count of kernel NFS client TCP connections accepted +Instrument: counter +Unit: {connection} +Note: Linux: this metric is taken from the Linux kernel's svc_stat.nettcpconn. +""" + + +def create_nfs_client_net_tcp_connection_accepted(meter: Meter) -> Counter: + """Reports the count of kernel NFS client TCP connections accepted""" + return meter.create_counter( + name=NFS_CLIENT_NET_TCP_CONNECTION_ACCEPTED, + description="Reports the count of kernel NFS client TCP connections accepted.", + unit="{connection}", + ) + + +NFS_CLIENT_OPERATION_COUNT: Final = "nfs.client.operation.count" +""" +Reports the count of kernel NFSv4+ client operations +Instrument: counter +Unit: {operation} +""" + + +def create_nfs_client_operation_count(meter: Meter) -> Counter: + """Reports the count of kernel NFSv4+ client operations""" + return meter.create_counter( + name=NFS_CLIENT_OPERATION_COUNT, + description="Reports the count of kernel NFSv4+ client operations.", + unit="{operation}", + ) + + +NFS_CLIENT_PROCEDURE_COUNT: Final = "nfs.client.procedure.count" +""" +Reports the count of kernel NFS client procedures +Instrument: counter +Unit: {procedure} +""" + + +def create_nfs_client_procedure_count(meter: Meter) -> Counter: + """Reports the count of kernel NFS client procedures""" + return meter.create_counter( + name=NFS_CLIENT_PROCEDURE_COUNT, + description="Reports the count of kernel NFS client procedures.", + unit="{procedure}", + ) + + +NFS_CLIENT_RPC_AUTHREFRESH_COUNT: Final = "nfs.client.rpc.authrefresh.count" +""" +Reports the count of kernel NFS client RPC authentication refreshes +Instrument: counter +Unit: {authrefresh} +Note: Linux: this metric is taken from the Linux kernel's svc_stat.rpcauthrefresh. +""" + + +def create_nfs_client_rpc_authrefresh_count(meter: Meter) -> Counter: + """Reports the count of kernel NFS client RPC authentication refreshes""" + return meter.create_counter( + name=NFS_CLIENT_RPC_AUTHREFRESH_COUNT, + description="Reports the count of kernel NFS client RPC authentication refreshes.", + unit="{authrefresh}", + ) + + +NFS_CLIENT_RPC_COUNT: Final = "nfs.client.rpc.count" +""" +Reports the count of kernel NFS client RPCs sent, regardless of whether they're accepted/rejected by the server +Instrument: counter +Unit: {request} +Note: Linux: this metric is taken from the Linux kernel's svc_stat.rpccnt. +""" + + +def create_nfs_client_rpc_count(meter: Meter) -> Counter: + """Reports the count of kernel NFS client RPCs sent, regardless of whether they're accepted/rejected by the server""" + return meter.create_counter( + name=NFS_CLIENT_RPC_COUNT, + description="Reports the count of kernel NFS client RPCs sent, regardless of whether they're accepted/rejected by the server.", + unit="{request}", + ) + + +NFS_CLIENT_RPC_RETRANSMIT_COUNT: Final = "nfs.client.rpc.retransmit.count" +""" +Reports the count of kernel NFS client RPC retransmits +Instrument: counter +Unit: {retransmit} +Note: Linux: this metric is taken from the Linux kernel's svc_stat.rpcretrans. +""" + + +def create_nfs_client_rpc_retransmit_count(meter: Meter) -> Counter: + """Reports the count of kernel NFS client RPC retransmits""" + return meter.create_counter( + name=NFS_CLIENT_RPC_RETRANSMIT_COUNT, + description="Reports the count of kernel NFS client RPC retransmits.", + unit="{retransmit}", + ) + + +NFS_SERVER_FH_STALE_COUNT: Final = "nfs.server.fh.stale.count" +""" +Reports the count of kernel NFS server stale file handles +Instrument: counter +Unit: {fh} +Note: Linux: this metric is taken from the Linux kernel NFSD_STATS_FH_STALE counter in the nfsd_net struct. +""" + + +def create_nfs_server_fh_stale_count(meter: Meter) -> Counter: + """Reports the count of kernel NFS server stale file handles""" + return meter.create_counter( + name=NFS_SERVER_FH_STALE_COUNT, + description="Reports the count of kernel NFS server stale file handles.", + unit="{fh}", + ) + + +NFS_SERVER_IO: Final = "nfs.server.io" +""" +Reports the count of kernel NFS server bytes returned to receive and transmit (read and write) requests +Instrument: counter +Unit: By +Note: Linux: this metric is taken from the Linux kernel NFSD_STATS_IO_READ and NFSD_STATS_IO_WRITE counters in the nfsd_net struct. +""" + + +def create_nfs_server_io(meter: Meter) -> Counter: + """Reports the count of kernel NFS server bytes returned to receive and transmit (read and write) requests""" + return meter.create_counter( + name=NFS_SERVER_IO, + description="Reports the count of kernel NFS server bytes returned to receive and transmit (read and write) requests.", + unit="By", + ) + + +NFS_SERVER_NET_COUNT: Final = "nfs.server.net.count" +""" +Reports the count of kernel NFS server TCP segments and UDP datagrams handled +Instrument: counter +Unit: {record} +Note: Linux: this metric is taken from the Linux kernel's svc_stat.nettcpcnt and svc_stat.netudpcnt. +""" + + +def create_nfs_server_net_count(meter: Meter) -> Counter: + """Reports the count of kernel NFS server TCP segments and UDP datagrams handled""" + return meter.create_counter( + name=NFS_SERVER_NET_COUNT, + description="Reports the count of kernel NFS server TCP segments and UDP datagrams handled.", + unit="{record}", + ) + + +NFS_SERVER_NET_TCP_CONNECTION_ACCEPTED: Final = "nfs.server.net.tcp.connection.accepted" +""" +Reports the count of kernel NFS server TCP connections accepted +Instrument: counter +Unit: {connection} +Note: Linux: this metric is taken from the Linux kernel's svc_stat.nettcpconn. +""" + + +def create_nfs_server_net_tcp_connection_accepted(meter: Meter) -> Counter: + """Reports the count of kernel NFS server TCP connections accepted""" + return meter.create_counter( + name=NFS_SERVER_NET_TCP_CONNECTION_ACCEPTED, + description="Reports the count of kernel NFS server TCP connections accepted.", + unit="{connection}", + ) + + +NFS_SERVER_OPERATION_COUNT: Final = "nfs.server.operation.count" +""" +Reports the count of kernel NFSv4+ server operations +Instrument: counter +Unit: {operation} +""" + + +def create_nfs_server_operation_count(meter: Meter) -> Counter: + """Reports the count of kernel NFSv4+ server operations""" + return meter.create_counter( + name=NFS_SERVER_OPERATION_COUNT, + description="Reports the count of kernel NFSv4+ server operations.", + unit="{operation}", + ) + + +NFS_SERVER_PROCEDURE_COUNT: Final = "nfs.server.procedure.count" +""" +Reports the count of kernel NFS server procedures +Instrument: counter +Unit: {procedure} +""" + + +def create_nfs_server_procedure_count(meter: Meter) -> Counter: + """Reports the count of kernel NFS server procedures""" + return meter.create_counter( + name=NFS_SERVER_PROCEDURE_COUNT, + description="Reports the count of kernel NFS server procedures.", + unit="{procedure}", + ) + + +NFS_SERVER_REPCACHE_REQUESTS: Final = "nfs.server.repcache.requests" +""" +Reports the kernel NFS server reply cache request count by cache hit status +Instrument: counter +Unit: {request} +""" + + +def create_nfs_server_repcache_requests(meter: Meter) -> Counter: + """Reports the kernel NFS server reply cache request count by cache hit status""" + return meter.create_counter( + name=NFS_SERVER_REPCACHE_REQUESTS, + description="Reports the kernel NFS server reply cache request count by cache hit status.", + unit="{request}", + ) + + +NFS_SERVER_RPC_COUNT: Final = "nfs.server.rpc.count" +""" +Reports the count of kernel NFS server RPCs handled +Instrument: counter +Unit: {request} +Note: Linux: this metric is taken from the Linux kernel's svc_stat.rpccnt, the count of good RPCs. This metric can have +an error.type of "format", "auth", or "client" for svc_stat.badfmt, svc_stat.badauth, and svc_stat.badclnt. +""" + + +def create_nfs_server_rpc_count(meter: Meter) -> Counter: + """Reports the count of kernel NFS server RPCs handled""" + return meter.create_counter( + name=NFS_SERVER_RPC_COUNT, + description="Reports the count of kernel NFS server RPCs handled.", + unit="{request}", + ) + + +NFS_SERVER_THREAD_COUNT: Final = "nfs.server.thread.count" +""" +Reports the count of kernel NFS server available threads +Instrument: updowncounter +Unit: {thread} +Note: Linux: this metric is taken from the Linux kernel nfsd_th_cnt variable. +""" + + +def create_nfs_server_thread_count(meter: Meter) -> UpDownCounter: + """Reports the count of kernel NFS server available threads""" + return meter.create_up_down_counter( + name=NFS_SERVER_THREAD_COUNT, + description="Reports the count of kernel NFS server available threads.", + unit="{thread}", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/openshift_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/openshift_metrics.py new file mode 100644 index 0000000..0da385a --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/openshift_metrics.py @@ -0,0 +1,442 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Meter, UpDownCounter + +OPENSHIFT_CLUSTERQUOTA_CPU_LIMIT_HARD: Final = "openshift.clusterquota.cpu.limit.hard" +""" +The enforced hard limit of the resource across all projects +Instrument: updowncounter +Unit: {cpu} +Note: This metric is retrieved from the `Status.Total.Hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_cpu_limit_hard(meter: Meter) -> UpDownCounter: + """The enforced hard limit of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_CPU_LIMIT_HARD, + description="The enforced hard limit of the resource across all projects.", + unit="{cpu}", + ) + + +OPENSHIFT_CLUSTERQUOTA_CPU_LIMIT_USED: Final = "openshift.clusterquota.cpu.limit.used" +""" +The current observed total usage of the resource across all projects +Instrument: updowncounter +Unit: {cpu} +Note: This metric is retrieved from the `Status.Total.Used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_cpu_limit_used(meter: Meter) -> UpDownCounter: + """The current observed total usage of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_CPU_LIMIT_USED, + description="The current observed total usage of the resource across all projects.", + unit="{cpu}", + ) + + +OPENSHIFT_CLUSTERQUOTA_CPU_REQUEST_HARD: Final = "openshift.clusterquota.cpu.request.hard" +""" +The enforced hard limit of the resource across all projects +Instrument: updowncounter +Unit: {cpu} +Note: This metric is retrieved from the `Status.Total.Hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_cpu_request_hard(meter: Meter) -> UpDownCounter: + """The enforced hard limit of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_CPU_REQUEST_HARD, + description="The enforced hard limit of the resource across all projects.", + unit="{cpu}", + ) + + +OPENSHIFT_CLUSTERQUOTA_CPU_REQUEST_USED: Final = "openshift.clusterquota.cpu.request.used" +""" +The current observed total usage of the resource across all projects +Instrument: updowncounter +Unit: {cpu} +Note: This metric is retrieved from the `Status.Total.Used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_cpu_request_used(meter: Meter) -> UpDownCounter: + """The current observed total usage of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_CPU_REQUEST_USED, + description="The current observed total usage of the resource across all projects.", + unit="{cpu}", + ) + + +OPENSHIFT_CLUSTERQUOTA_EPHEMERAL_STORAGE_LIMIT_HARD: Final = "openshift.clusterquota.ephemeral_storage.limit.hard" +""" +The enforced hard limit of the resource across all projects +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `Status.Total.Hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_ephemeral_storage_limit_hard(meter: Meter) -> UpDownCounter: + """The enforced hard limit of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_EPHEMERAL_STORAGE_LIMIT_HARD, + description="The enforced hard limit of the resource across all projects.", + unit="By", + ) + + +OPENSHIFT_CLUSTERQUOTA_EPHEMERAL_STORAGE_LIMIT_USED: Final = "openshift.clusterquota.ephemeral_storage.limit.used" +""" +The current observed total usage of the resource across all projects +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `Status.Total.Used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_ephemeral_storage_limit_used(meter: Meter) -> UpDownCounter: + """The current observed total usage of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_EPHEMERAL_STORAGE_LIMIT_USED, + description="The current observed total usage of the resource across all projects.", + unit="By", + ) + + +OPENSHIFT_CLUSTERQUOTA_EPHEMERAL_STORAGE_REQUEST_HARD: Final = "openshift.clusterquota.ephemeral_storage.request.hard" +""" +The enforced hard limit of the resource across all projects +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `Status.Total.Hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_ephemeral_storage_request_hard(meter: Meter) -> UpDownCounter: + """The enforced hard limit of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_EPHEMERAL_STORAGE_REQUEST_HARD, + description="The enforced hard limit of the resource across all projects.", + unit="By", + ) + + +OPENSHIFT_CLUSTERQUOTA_EPHEMERAL_STORAGE_REQUEST_USED: Final = "openshift.clusterquota.ephemeral_storage.request.used" +""" +The current observed total usage of the resource across all projects +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `Status.Total.Used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_ephemeral_storage_request_used(meter: Meter) -> UpDownCounter: + """The current observed total usage of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_EPHEMERAL_STORAGE_REQUEST_USED, + description="The current observed total usage of the resource across all projects.", + unit="By", + ) + + +OPENSHIFT_CLUSTERQUOTA_HUGEPAGE_COUNT_REQUEST_HARD: Final = "openshift.clusterquota.hugepage_count.request.hard" +""" +The enforced hard limit of the resource across all projects +Instrument: updowncounter +Unit: {hugepage} +Note: This metric is retrieved from the `Status.Total.Hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_hugepage_count_request_hard(meter: Meter) -> UpDownCounter: + """The enforced hard limit of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_HUGEPAGE_COUNT_REQUEST_HARD, + description="The enforced hard limit of the resource across all projects.", + unit="{hugepage}", + ) + + +OPENSHIFT_CLUSTERQUOTA_HUGEPAGE_COUNT_REQUEST_USED: Final = "openshift.clusterquota.hugepage_count.request.used" +""" +The current observed total usage of the resource across all projects +Instrument: updowncounter +Unit: {hugepage} +Note: This metric is retrieved from the `Status.Total.Used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_hugepage_count_request_used(meter: Meter) -> UpDownCounter: + """The current observed total usage of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_HUGEPAGE_COUNT_REQUEST_USED, + description="The current observed total usage of the resource across all projects.", + unit="{hugepage}", + ) + + +OPENSHIFT_CLUSTERQUOTA_MEMORY_LIMIT_HARD: Final = "openshift.clusterquota.memory.limit.hard" +""" +The enforced hard limit of the resource across all projects +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `Status.Total.Hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_memory_limit_hard(meter: Meter) -> UpDownCounter: + """The enforced hard limit of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_MEMORY_LIMIT_HARD, + description="The enforced hard limit of the resource across all projects.", + unit="By", + ) + + +OPENSHIFT_CLUSTERQUOTA_MEMORY_LIMIT_USED: Final = "openshift.clusterquota.memory.limit.used" +""" +The current observed total usage of the resource across all projects +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `Status.Total.Used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_memory_limit_used(meter: Meter) -> UpDownCounter: + """The current observed total usage of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_MEMORY_LIMIT_USED, + description="The current observed total usage of the resource across all projects.", + unit="By", + ) + + +OPENSHIFT_CLUSTERQUOTA_MEMORY_REQUEST_HARD: Final = "openshift.clusterquota.memory.request.hard" +""" +The enforced hard limit of the resource across all projects +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `Status.Total.Hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_memory_request_hard(meter: Meter) -> UpDownCounter: + """The enforced hard limit of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_MEMORY_REQUEST_HARD, + description="The enforced hard limit of the resource across all projects.", + unit="By", + ) + + +OPENSHIFT_CLUSTERQUOTA_MEMORY_REQUEST_USED: Final = "openshift.clusterquota.memory.request.used" +""" +The current observed total usage of the resource across all projects +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `Status.Total.Used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_memory_request_used(meter: Meter) -> UpDownCounter: + """The current observed total usage of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_MEMORY_REQUEST_USED, + description="The current observed total usage of the resource across all projects.", + unit="By", + ) + + +OPENSHIFT_CLUSTERQUOTA_OBJECT_COUNT_HARD: Final = "openshift.clusterquota.object_count.hard" +""" +The enforced hard limit of the resource across all projects +Instrument: updowncounter +Unit: {object} +Note: This metric is retrieved from the `Status.Total.Hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_object_count_hard(meter: Meter) -> UpDownCounter: + """The enforced hard limit of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_OBJECT_COUNT_HARD, + description="The enforced hard limit of the resource across all projects.", + unit="{object}", + ) + + +OPENSHIFT_CLUSTERQUOTA_OBJECT_COUNT_USED: Final = "openshift.clusterquota.object_count.used" +""" +The current observed total usage of the resource across all projects +Instrument: updowncounter +Unit: {object} +Note: This metric is retrieved from the `Status.Total.Used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). +""" + + +def create_openshift_clusterquota_object_count_used(meter: Meter) -> UpDownCounter: + """The current observed total usage of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_OBJECT_COUNT_USED, + description="The current observed total usage of the resource across all projects.", + unit="{object}", + ) + + +OPENSHIFT_CLUSTERQUOTA_PERSISTENTVOLUMECLAIM_COUNT_HARD: Final = ( + "openshift.clusterquota.persistentvolumeclaim_count.hard" +) +""" +The enforced hard limit of the resource across all projects +Instrument: updowncounter +Unit: {persistentvolumeclaim} +Note: This metric is retrieved from the `Status.Total.Hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). + +The `k8s.storageclass.name` should be required when a resource quota is defined for a specific +storage class. +""" + + +def create_openshift_clusterquota_persistentvolumeclaim_count_hard(meter: Meter) -> UpDownCounter: + """The enforced hard limit of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_PERSISTENTVOLUMECLAIM_COUNT_HARD, + description="The enforced hard limit of the resource across all projects.", + unit="{persistentvolumeclaim}", + ) + + +OPENSHIFT_CLUSTERQUOTA_PERSISTENTVOLUMECLAIM_COUNT_USED: Final = ( + "openshift.clusterquota.persistentvolumeclaim_count.used" +) +""" +The current observed total usage of the resource across all projects +Instrument: updowncounter +Unit: {persistentvolumeclaim} +Note: This metric is retrieved from the `Status.Total.Used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). + +The `k8s.storageclass.name` should be required when a resource quota is defined for a specific +storage class. +""" + + +def create_openshift_clusterquota_persistentvolumeclaim_count_used(meter: Meter) -> UpDownCounter: + """The current observed total usage of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_PERSISTENTVOLUMECLAIM_COUNT_USED, + description="The current observed total usage of the resource across all projects.", + unit="{persistentvolumeclaim}", + ) + + +OPENSHIFT_CLUSTERQUOTA_STORAGE_REQUEST_HARD: Final = "openshift.clusterquota.storage.request.hard" +""" +The enforced hard limit of the resource across all projects +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `Status.Total.Hard` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). + +The `k8s.storageclass.name` should be required when a resource quota is defined for a specific +storage class. +""" + + +def create_openshift_clusterquota_storage_request_hard(meter: Meter) -> UpDownCounter: + """The enforced hard limit of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_STORAGE_REQUEST_HARD, + description="The enforced hard limit of the resource across all projects.", + unit="By", + ) + + +OPENSHIFT_CLUSTERQUOTA_STORAGE_REQUEST_USED: Final = "openshift.clusterquota.storage.request.used" +""" +The current observed total usage of the resource across all projects +Instrument: updowncounter +Unit: By +Note: This metric is retrieved from the `Status.Total.Used` field of the +[K8s ResourceQuotaStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcequotastatus-v1-core) +of the +[ClusterResourceQuota](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/schedule_and_quota_apis/clusterresourcequota-quota-openshift-io-v1#status-total). + +The `k8s.storageclass.name` should be required when a resource quota is defined for a specific +storage class. +""" + + +def create_openshift_clusterquota_storage_request_used(meter: Meter) -> UpDownCounter: + """The current observed total usage of the resource across all projects""" + return meter.create_up_down_counter( + name=OPENSHIFT_CLUSTERQUOTA_STORAGE_REQUEST_USED, + description="The current observed total usage of the resource across all projects.", + unit="By", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/otel_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/otel_metrics.py new file mode 100644 index 0000000..48e3e34 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/otel_metrics.py @@ -0,0 +1,443 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Counter, Histogram, Meter, UpDownCounter + +OTEL_SDK_EXPORTER_LOG_EXPORTED: Final = "otel.sdk.exporter.log.exported" +""" +The number of log records for which the export has finished, either successful or failed +Instrument: counter +Unit: {log_record} +Note: For successful exports, `error.type` MUST NOT be set. For failed exports, `error.type` MUST contain the failure cause. +For exporters with partial success semantics (e.g. OTLP with `rejected_log_records`), rejected log records MUST count as failed and only non-rejected log records count as success. +If no rejection reason is available, `rejected` SHOULD be used as value for `error.type`. +If the exporter retries failed export attempts, the export operation is considered finished only after the final attempt has concluded. +Each log record MUST be counted exactly once per export operation: intermediate failed attempts that are followed by a retry MUST NOT increment the counter, +and `error.type` reflects the cause of the final attempt. +""" + + +def create_otel_sdk_exporter_log_exported(meter: Meter) -> Counter: + """The number of log records for which the export has finished, either successful or failed""" + return meter.create_counter( + name=OTEL_SDK_EXPORTER_LOG_EXPORTED, + description="The number of log records for which the export has finished, either successful or failed.", + unit="{log_record}", + ) + + +OTEL_SDK_EXPORTER_LOG_INFLIGHT: Final = "otel.sdk.exporter.log.inflight" +""" +The number of log records which were passed to the exporter, but that have not been exported yet (neither successful, nor failed) +Instrument: updowncounter +Unit: {log_record} +Note: Log records are counted as inflight from when they are passed to the exporter until the export operation has concluded. +If the exporter retries failed export attempts, log records remain inflight across all retry attempts and any backoff between them. +""" + + +def create_otel_sdk_exporter_log_inflight(meter: Meter) -> UpDownCounter: + """The number of log records which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)""" + return meter.create_up_down_counter( + name=OTEL_SDK_EXPORTER_LOG_INFLIGHT, + description="The number of log records which were passed to the exporter, but that have not been exported yet (neither successful, nor failed).", + unit="{log_record}", + ) + + +OTEL_SDK_EXPORTER_METRIC_DATA_POINT_EXPORTED: Final = "otel.sdk.exporter.metric_data_point.exported" +""" +The number of metric data points for which the export has finished, either successful or failed +Instrument: counter +Unit: {data_point} +Note: For successful exports, `error.type` MUST NOT be set. For failed exports, `error.type` MUST contain the failure cause. +For exporters with partial success semantics (e.g. OTLP with `rejected_data_points`), rejected data points MUST count as failed and only non-rejected data points count as success. +If no rejection reason is available, `rejected` SHOULD be used as value for `error.type`. +If the exporter retries failed export attempts, the export operation is considered finished only after the final attempt has concluded. +Each metric data point MUST be counted exactly once per export operation: intermediate failed attempts that are followed by a retry MUST NOT increment the counter, +and `error.type` reflects the cause of the final attempt. +""" + + +def create_otel_sdk_exporter_metric_data_point_exported(meter: Meter) -> Counter: + """The number of metric data points for which the export has finished, either successful or failed""" + return meter.create_counter( + name=OTEL_SDK_EXPORTER_METRIC_DATA_POINT_EXPORTED, + description="The number of metric data points for which the export has finished, either successful or failed.", + unit="{data_point}", + ) + + +OTEL_SDK_EXPORTER_METRIC_DATA_POINT_INFLIGHT: Final = "otel.sdk.exporter.metric_data_point.inflight" +""" +The number of metric data points which were passed to the exporter, but that have not been exported yet (neither successful, nor failed) +Instrument: updowncounter +Unit: {data_point} +Note: Metric data points are counted as inflight from when they are passed to the exporter until the export operation has concluded. +If the exporter retries failed export attempts, metric data points remain inflight across all retry attempts and any backoff between them. +""" + + +def create_otel_sdk_exporter_metric_data_point_inflight(meter: Meter) -> UpDownCounter: + """The number of metric data points which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)""" + return meter.create_up_down_counter( + name=OTEL_SDK_EXPORTER_METRIC_DATA_POINT_INFLIGHT, + description="The number of metric data points which were passed to the exporter, but that have not been exported yet (neither successful, nor failed).", + unit="{data_point}", + ) + + +OTEL_SDK_EXPORTER_OPERATION_DURATION: Final = "otel.sdk.exporter.operation.duration" +""" +The duration of exporting a batch of telemetry records +Instrument: histogram +Unit: s +Note: This metric defines successful operations using the full success definitions for [HTTP](https://github.com/open-telemetry/opentelemetry-proto/blob/v1.5.0/docs/specification.md#full-success-1) +and [gRPC](https://github.com/open-telemetry/opentelemetry-proto/blob/v1.5.0/docs/specification.md#full-success). Anything else is defined as an unsuccessful operation. For successful +operations, `error.type` MUST NOT be set. For unsuccessful export operations, `error.type` MUST contain a relevant failure cause. +If the exporter retries failed export attempts, exactly one observation MUST be recorded per export operation, +covering the wall-clock duration from the start of the first attempt through the conclusion of the final attempt (including any backoff between attempts). +`error.type` reflects the cause of the final attempt. +""" + + +def create_otel_sdk_exporter_operation_duration(meter: Meter) -> Histogram: + """The duration of exporting a batch of telemetry records""" + return meter.create_histogram( + name=OTEL_SDK_EXPORTER_OPERATION_DURATION, + description="The duration of exporting a batch of telemetry records.", + unit="s", + ) + + +OTEL_SDK_EXPORTER_SPAN_EXPORTED: Final = "otel.sdk.exporter.span.exported" +""" +The number of spans for which the export has finished, either successful or failed +Instrument: counter +Unit: {span} +Note: For successful exports, `error.type` MUST NOT be set. For failed exports, `error.type` MUST contain the failure cause. +For exporters with partial success semantics (e.g. OTLP with `rejected_spans`), rejected spans MUST count as failed and only non-rejected spans count as success. +If no rejection reason is available, `rejected` SHOULD be used as value for `error.type`. +If the exporter retries failed export attempts, the export operation is considered finished only after the final attempt has concluded. +Each span MUST be counted exactly once per export operation: intermediate failed attempts that are followed by a retry MUST NOT increment the counter, +and `error.type` reflects the cause of the final attempt. +""" + + +def create_otel_sdk_exporter_span_exported(meter: Meter) -> Counter: + """The number of spans for which the export has finished, either successful or failed""" + return meter.create_counter( + name=OTEL_SDK_EXPORTER_SPAN_EXPORTED, + description="The number of spans for which the export has finished, either successful or failed.", + unit="{span}", + ) + + +OTEL_SDK_EXPORTER_SPAN_EXPORTED_COUNT: Final = "otel.sdk.exporter.span.exported.count" +""" +Deprecated: Replaced by `otel.sdk.exporter.span.exported`. +""" + + +def create_otel_sdk_exporter_span_exported_count(meter: Meter) -> UpDownCounter: + """Deprecated, use `otel.sdk.exporter.span.exported` instead""" + return meter.create_up_down_counter( + name=OTEL_SDK_EXPORTER_SPAN_EXPORTED_COUNT, + description="Deprecated, use `otel.sdk.exporter.span.exported` instead.", + unit="{span}", + ) + + +OTEL_SDK_EXPORTER_SPAN_INFLIGHT: Final = "otel.sdk.exporter.span.inflight" +""" +The number of spans which were passed to the exporter, but that have not been exported yet (neither successful, nor failed) +Instrument: updowncounter +Unit: {span} +Note: Spans are counted as inflight from when they are passed to the exporter until the export operation has concluded. +If the exporter retries failed export attempts, spans remain inflight across all retry attempts and any backoff between them. +""" + + +def create_otel_sdk_exporter_span_inflight(meter: Meter) -> UpDownCounter: + """The number of spans which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)""" + return meter.create_up_down_counter( + name=OTEL_SDK_EXPORTER_SPAN_INFLIGHT, + description="The number of spans which were passed to the exporter, but that have not been exported yet (neither successful, nor failed).", + unit="{span}", + ) + + +OTEL_SDK_EXPORTER_SPAN_INFLIGHT_COUNT: Final = "otel.sdk.exporter.span.inflight.count" +""" +Deprecated: Replaced by `otel.sdk.exporter.span.inflight`. +""" + + +def create_otel_sdk_exporter_span_inflight_count(meter: Meter) -> UpDownCounter: + """Deprecated, use `otel.sdk.exporter.span.inflight` instead""" + return meter.create_up_down_counter( + name=OTEL_SDK_EXPORTER_SPAN_INFLIGHT_COUNT, + description="Deprecated, use `otel.sdk.exporter.span.inflight` instead.", + unit="{span}", + ) + + +OTEL_SDK_LOG_CREATED: Final = "otel.sdk.log.created" +""" +The number of logs submitted to enabled SDK Loggers +Instrument: counter +Unit: {log_record} +""" + + +def create_otel_sdk_log_created(meter: Meter) -> Counter: + """The number of logs submitted to enabled SDK Loggers""" + return meter.create_counter( + name=OTEL_SDK_LOG_CREATED, + description="The number of logs submitted to enabled SDK Loggers.", + unit="{log_record}", + ) + + +OTEL_SDK_METRIC_READER_COLLECTION_DURATION: Final = "otel.sdk.metric_reader.collection.duration" +""" +The duration of the collect operation of the metric reader +Instrument: histogram +Unit: s +Note: For successful collections, `error.type` MUST NOT be set. For failed collections, `error.type` SHOULD contain the failure cause. +It can happen that metrics collection is successful for some MetricProducers, while others fail. In that case `error.type` SHOULD be set to any of the failure causes. +""" + + +def create_otel_sdk_metric_reader_collection_duration(meter: Meter) -> Histogram: + """The duration of the collect operation of the metric reader""" + return meter.create_histogram( + name=OTEL_SDK_METRIC_READER_COLLECTION_DURATION, + description="The duration of the collect operation of the metric reader.", + unit="s", + ) + + +OTEL_SDK_PROCESSOR_LOG_PROCESSED: Final = "otel.sdk.processor.log.processed" +""" +The number of log records for which the processing has finished, either successful or failed +Instrument: counter +Unit: {log_record} +Note: For successful processing, `error.type` MUST NOT be set. For failed processing, `error.type` MUST contain the failure cause. +SDK Batching Log Record Processors MUST use `queue_full` as the value of `error.type` for log records dropped due to a full queue. +If a processor reports a log record dropped because it has already been shut down, `error.type` MUST be `already_shutdown`. +Whether and when a processor drops such log records is governed by the SDK specification, not by this metric. +For the SDK Simple and Batching Log Record Processors, a log record MUST be counted as successfully processed at the point the +processor invokes the export operation. For batching processors, all log records in the batch passed to the exporter are counted +at that point; log records accepted into the processor's queue but not yet passed to the exporter have not been processed. +Implementations MUST NOT delay this count until the export operation concludes, and the outcome of the export operation, +including an immediate failure of the invocation itself, MUST NOT affect this metric. +Export outcomes are reported by `otel.sdk.exporter.log.exported`. +""" + + +def create_otel_sdk_processor_log_processed(meter: Meter) -> Counter: + """The number of log records for which the processing has finished, either successful or failed""" + return meter.create_counter( + name=OTEL_SDK_PROCESSOR_LOG_PROCESSED, + description="The number of log records for which the processing has finished, either successful or failed.", + unit="{log_record}", + ) + + +OTEL_SDK_PROCESSOR_LOG_QUEUE_CAPACITY: Final = "otel.sdk.processor.log.queue.capacity" +""" +The maximum number of log records the queue of a given instance of an SDK Log Record processor can hold +Instrument: updowncounter +Unit: {log_record} +Note: Only applies to Log Record processors which use a queue, e.g. the SDK Batching Log Record Processor. +""" + + +def create_otel_sdk_processor_log_queue_capacity(meter: Meter) -> UpDownCounter: + """The maximum number of log records the queue of a given instance of an SDK Log Record processor can hold""" + return meter.create_up_down_counter( + name=OTEL_SDK_PROCESSOR_LOG_QUEUE_CAPACITY, + description="The maximum number of log records the queue of a given instance of an SDK Log Record processor can hold.", + unit="{log_record}", + ) + + +OTEL_SDK_PROCESSOR_LOG_QUEUE_SIZE: Final = "otel.sdk.processor.log.queue.size" +""" +The number of log records in the queue of a given instance of an SDK log processor +Instrument: updowncounter +Unit: {log_record} +Note: Only applies to log record processors which use a queue, e.g. the SDK Batching Log Record Processor. +""" + + +def create_otel_sdk_processor_log_queue_size(meter: Meter) -> UpDownCounter: + """The number of log records in the queue of a given instance of an SDK log processor""" + return meter.create_up_down_counter( + name=OTEL_SDK_PROCESSOR_LOG_QUEUE_SIZE, + description="The number of log records in the queue of a given instance of an SDK log processor.", + unit="{log_record}", + ) + + +OTEL_SDK_PROCESSOR_SPAN_PROCESSED: Final = "otel.sdk.processor.span.processed" +""" +The number of spans for which the processing has finished, either successful or failed +Instrument: counter +Unit: {span} +Note: For successful processing, `error.type` MUST NOT be set. For failed processing, `error.type` MUST contain the failure cause. +SDK Batching Span Processors MUST use `queue_full` as the value of `error.type` for spans dropped due to a full queue. +If a processor reports a span dropped because it has already been shut down, `error.type` MUST be `already_shutdown`. +Whether and when a processor drops such spans is governed by the SDK specification, not by this metric. +For the SDK Simple and Batching Span Processors, a span MUST be counted as successfully processed at the point the processor +invokes the export operation. For batching processors, all spans in the batch passed to the exporter are counted at that point; +spans accepted into the processor's queue but not yet passed to the exporter have not been processed. +Implementations MUST NOT delay this count until the export operation concludes, and the outcome of the export operation, +including an immediate failure of the invocation itself, MUST NOT affect this metric. +Export outcomes are reported by `otel.sdk.exporter.span.exported`. +""" + + +def create_otel_sdk_processor_span_processed(meter: Meter) -> Counter: + """The number of spans for which the processing has finished, either successful or failed""" + return meter.create_counter( + name=OTEL_SDK_PROCESSOR_SPAN_PROCESSED, + description="The number of spans for which the processing has finished, either successful or failed.", + unit="{span}", + ) + + +OTEL_SDK_PROCESSOR_SPAN_PROCESSED_COUNT: Final = "otel.sdk.processor.span.processed.count" +""" +Deprecated: Replaced by `otel.sdk.processor.span.processed`. +""" + + +def create_otel_sdk_processor_span_processed_count(meter: Meter) -> UpDownCounter: + """Deprecated, use `otel.sdk.processor.span.processed` instead""" + return meter.create_up_down_counter( + name=OTEL_SDK_PROCESSOR_SPAN_PROCESSED_COUNT, + description="Deprecated, use `otel.sdk.processor.span.processed` instead.", + unit="{span}", + ) + + +OTEL_SDK_PROCESSOR_SPAN_QUEUE_CAPACITY: Final = "otel.sdk.processor.span.queue.capacity" +""" +The maximum number of spans the queue of a given instance of an SDK span processor can hold +Instrument: updowncounter +Unit: {span} +Note: Only applies to span processors which use a queue, e.g. the SDK Batching Span Processor. +""" + + +def create_otel_sdk_processor_span_queue_capacity(meter: Meter) -> UpDownCounter: + """The maximum number of spans the queue of a given instance of an SDK span processor can hold""" + return meter.create_up_down_counter( + name=OTEL_SDK_PROCESSOR_SPAN_QUEUE_CAPACITY, + description="The maximum number of spans the queue of a given instance of an SDK span processor can hold.", + unit="{span}", + ) + + +OTEL_SDK_PROCESSOR_SPAN_QUEUE_SIZE: Final = "otel.sdk.processor.span.queue.size" +""" +The number of spans in the queue of a given instance of an SDK span processor +Instrument: updowncounter +Unit: {span} +Note: Only applies to span processors which use a queue, e.g. the SDK Batching Span Processor. +""" + + +def create_otel_sdk_processor_span_queue_size(meter: Meter) -> UpDownCounter: + """The number of spans in the queue of a given instance of an SDK span processor""" + return meter.create_up_down_counter( + name=OTEL_SDK_PROCESSOR_SPAN_QUEUE_SIZE, + description="The number of spans in the queue of a given instance of an SDK span processor.", + unit="{span}", + ) + + +OTEL_SDK_SPAN_ENDED: Final = "otel.sdk.span.ended" +""" +Deprecated: Obsoleted. +""" + + +def create_otel_sdk_span_ended(meter: Meter) -> Counter: + """Use `otel.sdk.span.started` minus `otel.sdk.span.live` to derive this value""" + return meter.create_counter( + name=OTEL_SDK_SPAN_ENDED, + description="Use `otel.sdk.span.started` minus `otel.sdk.span.live` to derive this value.", + unit="{span}", + ) + + +OTEL_SDK_SPAN_ENDED_COUNT: Final = "otel.sdk.span.ended.count" +""" +Deprecated: Obsoleted. +""" + + +def create_otel_sdk_span_ended_count(meter: Meter) -> Counter: + """Use `otel.sdk.span.started` minus `otel.sdk.span.live` to derive this value""" + return meter.create_counter( + name=OTEL_SDK_SPAN_ENDED_COUNT, + description="Use `otel.sdk.span.started` minus `otel.sdk.span.live` to derive this value.", + unit="{span}", + ) + + +OTEL_SDK_SPAN_LIVE: Final = "otel.sdk.span.live" +""" +The number of created spans with `recording=true` for which the end operation has not been called yet +Instrument: updowncounter +Unit: {span} +Note: Non-recording spans are not counted, hence `otel.span.sampling_result` can only take values `RECORD_ONLY` and `RECORD_AND_SAMPLE`, not `DROP`. +""" + + +def create_otel_sdk_span_live(meter: Meter) -> UpDownCounter: + """The number of created spans with `recording=true` for which the end operation has not been called yet""" + return meter.create_up_down_counter( + name=OTEL_SDK_SPAN_LIVE, + description="The number of created spans with `recording=true` for which the end operation has not been called yet.", + unit="{span}", + ) + + +OTEL_SDK_SPAN_LIVE_COUNT: Final = "otel.sdk.span.live.count" +""" +Deprecated: Replaced by `otel.sdk.span.live`. +""" + + +def create_otel_sdk_span_live_count(meter: Meter) -> UpDownCounter: + """Deprecated, use `otel.sdk.span.live` instead""" + return meter.create_up_down_counter( + name=OTEL_SDK_SPAN_LIVE_COUNT, + description="Deprecated, use `otel.sdk.span.live` instead.", + unit="{span}", + ) + + +OTEL_SDK_SPAN_STARTED: Final = "otel.sdk.span.started" +""" +The number of created spans +Instrument: counter +Unit: {span} +Note: Implementations MUST record this metric for all spans, even for non-recording ones. +""" + + +def create_otel_sdk_span_started(meter: Meter) -> Counter: + """The number of created spans""" + return meter.create_counter( + name=OTEL_SDK_SPAN_STARTED, + description="The number of created spans.", + unit="{span}", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/process_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/process_metrics.py new file mode 100644 index 0000000..50cfc5e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/process_metrics.py @@ -0,0 +1,286 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from collections.abc import Callable, Generator, Iterable, Sequence +from typing import Final + +from opentelemetry.metrics import CallbackOptions, Counter, Meter, ObservableGauge, Observation, UpDownCounter + +# pylint: disable=invalid-name +CallbackT = Callable[[CallbackOptions], Iterable[Observation]] | Generator[Iterable[Observation], CallbackOptions, None] + + +PROCESS_CONTEXT_SWITCHES: Final = "process.context_switches" +""" +Number of times the process has been context switched +Instrument: counter +Unit: {context_switch} +""" + + +def create_process_context_switches(meter: Meter) -> Counter: + """Number of times the process has been context switched""" + return meter.create_counter( + name=PROCESS_CONTEXT_SWITCHES, + description="Number of times the process has been context switched.", + unit="{context_switch}", + ) + + +PROCESS_CPU_TIME: Final = "process.cpu.time" +""" +Total CPU seconds broken down by different CPU modes +Instrument: counter +Unit: s +""" + + +def create_process_cpu_time(meter: Meter) -> Counter: + """Total CPU seconds broken down by different CPU modes""" + return meter.create_counter( + name=PROCESS_CPU_TIME, + description="Total CPU seconds broken down by different CPU modes.", + unit="s", + ) + + +PROCESS_CPU_UTILIZATION: Final = "process.cpu.utilization" +""" +Difference in process.cpu.time since the last measurement, divided by the elapsed time and number of CPUs available to the process +Instrument: gauge +Unit: 1 +""" + + +def create_process_cpu_utilization(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Difference in process.cpu.time since the last measurement, divided by the elapsed time and number of CPUs available to the process""" + return meter.create_observable_gauge( + name=PROCESS_CPU_UTILIZATION, + callbacks=callbacks, + description="Difference in process.cpu.time since the last measurement, divided by the elapsed time and number of CPUs available to the process.", + unit="1", + ) + + +PROCESS_DISK_IO: Final = "process.disk.io" +""" +Disk bytes transferred +Instrument: counter +Unit: By +""" + + +def create_process_disk_io(meter: Meter) -> Counter: + """Disk bytes transferred""" + return meter.create_counter( + name=PROCESS_DISK_IO, + description="Disk bytes transferred.", + unit="By", + ) + + +PROCESS_DISK_OPERATIONS: Final = "process.disk.operations" +""" +Number of disk operations performed by the process +Instrument: counter +Unit: {operation} +""" + + +def create_process_disk_operations(meter: Meter) -> Counter: + """Number of disk operations performed by the process""" + return meter.create_counter( + name=PROCESS_DISK_OPERATIONS, + description="Number of disk operations performed by the process.", + unit="{operation}", + ) + + +PROCESS_MEMORY_USAGE: Final = "process.memory.usage" +""" +The amount of physical memory in use +Instrument: updowncounter +Unit: By +""" + + +def create_process_memory_usage(meter: Meter) -> UpDownCounter: + """The amount of physical memory in use""" + return meter.create_up_down_counter( + name=PROCESS_MEMORY_USAGE, + description="The amount of physical memory in use.", + unit="By", + ) + + +PROCESS_MEMORY_UTILIZATION: Final = "process.memory.utilization" +""" +Percentage of total physical memory that is used by the process +Instrument: gauge +Unit: 1 +""" + + +def create_process_memory_utilization(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Percentage of total physical memory that is used by the process""" + return meter.create_observable_gauge( + name=PROCESS_MEMORY_UTILIZATION, + callbacks=callbacks, + description="Percentage of total physical memory that is used by the process.", + unit="1", + ) + + +PROCESS_MEMORY_VIRTUAL: Final = "process.memory.virtual" +""" +The amount of committed virtual memory +Instrument: updowncounter +Unit: By +""" + + +def create_process_memory_virtual(meter: Meter) -> UpDownCounter: + """The amount of committed virtual memory""" + return meter.create_up_down_counter( + name=PROCESS_MEMORY_VIRTUAL, + description="The amount of committed virtual memory.", + unit="By", + ) + + +PROCESS_NETWORK_IO: Final = "process.network.io" +""" +Network bytes transferred +Instrument: counter +Unit: By +""" + + +def create_process_network_io(meter: Meter) -> Counter: + """Network bytes transferred""" + return meter.create_counter( + name=PROCESS_NETWORK_IO, + description="Network bytes transferred.", + unit="By", + ) + + +PROCESS_OPEN_FILE_DESCRIPTOR_COUNT: Final = "process.open_file_descriptor.count" +""" +Deprecated: Replaced by `process.unix.file_descriptor.count`. +""" + + +def create_process_open_file_descriptor_count(meter: Meter) -> UpDownCounter: + """Deprecated, use `process.unix.file_descriptor.count` instead""" + return meter.create_up_down_counter( + name=PROCESS_OPEN_FILE_DESCRIPTOR_COUNT, + description="Deprecated, use `process.unix.file_descriptor.count` instead.", + unit="{file_descriptor}", + ) + + +PROCESS_PAGING_FAULTS: Final = "process.paging.faults" +""" +Number of page faults the process has made +Instrument: counter +Unit: {fault} +""" + + +def create_process_paging_faults(meter: Meter) -> Counter: + """Number of page faults the process has made""" + return meter.create_counter( + name=PROCESS_PAGING_FAULTS, + description="Number of page faults the process has made.", + unit="{fault}", + ) + + +PROCESS_SIGNALS_PENDING: Final = "process.signals_pending" +""" +Number of pending signals for the process +Instrument: updowncounter +Unit: {signal} +""" + + +def create_process_signals_pending(meter: Meter) -> UpDownCounter: + """Number of pending signals for the process""" + return meter.create_up_down_counter( + name=PROCESS_SIGNALS_PENDING, + description="Number of pending signals for the process.", + unit="{signal}", + ) + + +PROCESS_THREAD_COUNT: Final = "process.thread.count" +""" +Process threads count +Instrument: updowncounter +Unit: {thread} +""" + + +def create_process_thread_count(meter: Meter) -> UpDownCounter: + """Process threads count""" + return meter.create_up_down_counter( + name=PROCESS_THREAD_COUNT, + description="Process threads count.", + unit="{thread}", + ) + + +PROCESS_UNIX_FILE_DESCRIPTOR_COUNT: Final = "process.unix.file_descriptor.count" +""" +Number of UNIX file descriptors in use by the process +Instrument: updowncounter +Unit: {file_descriptor} +""" + + +def create_process_unix_file_descriptor_count(meter: Meter) -> UpDownCounter: + """Number of UNIX file descriptors in use by the process""" + return meter.create_up_down_counter( + name=PROCESS_UNIX_FILE_DESCRIPTOR_COUNT, + description="Number of UNIX file descriptors in use by the process.", + unit="{file_descriptor}", + ) + + +PROCESS_UPTIME: Final = "process.uptime" +""" +The time the process has been running +Instrument: gauge +Unit: s +Note: Instrumentations SHOULD use a gauge with type `double` and measure uptime in seconds as a floating point number with the highest precision available. +The actual accuracy would depend on the instrumentation and operating system. +""" + + +def create_process_uptime(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """The time the process has been running""" + return meter.create_observable_gauge( + name=PROCESS_UPTIME, + callbacks=callbacks, + description="The time the process has been running.", + unit="s", + ) + + +PROCESS_WINDOWS_HANDLE_COUNT: Final = "process.windows.handle.count" +""" +Number of handles held by the process +Instrument: updowncounter +Unit: {handle} +""" + + +def create_process_windows_handle_count(meter: Meter) -> UpDownCounter: + """Number of handles held by the process""" + return meter.create_up_down_counter( + name=PROCESS_WINDOWS_HANDLE_COUNT, + description="Number of handles held by the process.", + unit="{handle}", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/rpc_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/rpc_metrics.py new file mode 100644 index 0000000..47d1f5d --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/rpc_metrics.py @@ -0,0 +1,194 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +from opentelemetry.metrics import Histogram, Meter + +RPC_CLIENT_CALL_DURATION: Final = "rpc.client.call.duration" +""" +Measures the duration of an outgoing Remote Procedure Call (RPC) +Instrument: histogram +Unit: s +Note: When this metric is reported alongside an RPC client span, the metric value +SHOULD be the same as the RPC client span duration. +""" + + +def create_rpc_client_call_duration(meter: Meter) -> Histogram: + """Measures the duration of an outgoing Remote Procedure Call (RPC)""" + return meter.create_histogram( + name=RPC_CLIENT_CALL_DURATION, + description="Measures the duration of an outgoing Remote Procedure Call (RPC).", + unit="s", + ) + + +RPC_CLIENT_DURATION: Final = "rpc.client.duration" +""" +Deprecated: Replaced by `rpc.client.call.duration` with unit `s`. +""" + + +def create_rpc_client_duration(meter: Meter) -> Histogram: + """Deprecated, use `rpc.client.call.duration` instead. Note: the unit also changed from `ms` to `s`""" + return meter.create_histogram( + name=RPC_CLIENT_DURATION, + description="Deprecated, use `rpc.client.call.duration` instead. Note: the unit also changed from `ms` to `s`.", + unit="ms", + ) + + +RPC_CLIENT_REQUEST_SIZE: Final = "rpc.client.request.size" +""" +Deprecated: Removed, no replacement at this time. +""" + + +def create_rpc_client_request_size(meter: Meter) -> Histogram: + """Measures the size of RPC request messages (uncompressed)""" + return meter.create_histogram( + name=RPC_CLIENT_REQUEST_SIZE, + description="Measures the size of RPC request messages (uncompressed).", + unit="By", + ) + + +RPC_CLIENT_REQUESTS_PER_RPC: Final = "rpc.client.requests_per_rpc" +""" +Deprecated: Removed, no replacement at this time. +""" + + +def create_rpc_client_requests_per_rpc(meter: Meter) -> Histogram: + """Measures the number of messages received per RPC""" + return meter.create_histogram( + name=RPC_CLIENT_REQUESTS_PER_RPC, + description="Measures the number of messages received per RPC.", + unit="{count}", + ) + + +RPC_CLIENT_RESPONSE_SIZE: Final = "rpc.client.response.size" +""" +Deprecated: Removed, no replacement at this time. +""" + + +def create_rpc_client_response_size(meter: Meter) -> Histogram: + """Measures the size of RPC response messages (uncompressed)""" + return meter.create_histogram( + name=RPC_CLIENT_RESPONSE_SIZE, + description="Measures the size of RPC response messages (uncompressed).", + unit="By", + ) + + +RPC_CLIENT_RESPONSES_PER_RPC: Final = "rpc.client.responses_per_rpc" +""" +Deprecated: Removed, no replacement at this time. +""" + + +def create_rpc_client_responses_per_rpc(meter: Meter) -> Histogram: + """Measures the number of messages sent per RPC""" + return meter.create_histogram( + name=RPC_CLIENT_RESPONSES_PER_RPC, + description="Measures the number of messages sent per RPC.", + unit="{count}", + ) + + +RPC_SERVER_CALL_DURATION: Final = "rpc.server.call.duration" +""" +Measures the duration of an incoming Remote Procedure Call (RPC) +Instrument: histogram +Unit: s +Note: When this metric is reported alongside an RPC server span, the metric value +SHOULD be the same as the RPC server span duration. +""" + + +def create_rpc_server_call_duration(meter: Meter) -> Histogram: + """Measures the duration of an incoming Remote Procedure Call (RPC)""" + return meter.create_histogram( + name=RPC_SERVER_CALL_DURATION, + description="Measures the duration of an incoming Remote Procedure Call (RPC).", + unit="s", + ) + + +RPC_SERVER_DURATION: Final = "rpc.server.duration" +""" +Deprecated: Replaced by `rpc.server.call.duration` with unit `s`. +""" + + +def create_rpc_server_duration(meter: Meter) -> Histogram: + """Deprecated, use `rpc.server.call.duration` instead. Note: the unit also changed from `ms` to `s`""" + return meter.create_histogram( + name=RPC_SERVER_DURATION, + description="Deprecated, use `rpc.server.call.duration` instead. Note: the unit also changed from `ms` to `s`.", + unit="ms", + ) + + +RPC_SERVER_REQUEST_SIZE: Final = "rpc.server.request.size" +""" +Deprecated: Removed, no replacement at this time. +""" + + +def create_rpc_server_request_size(meter: Meter) -> Histogram: + """Measures the size of RPC request messages (uncompressed)""" + return meter.create_histogram( + name=RPC_SERVER_REQUEST_SIZE, + description="Measures the size of RPC request messages (uncompressed).", + unit="By", + ) + + +RPC_SERVER_REQUESTS_PER_RPC: Final = "rpc.server.requests_per_rpc" +""" +Deprecated: Removed, no replacement at this time. +""" + + +def create_rpc_server_requests_per_rpc(meter: Meter) -> Histogram: + """Measures the number of messages received per RPC""" + return meter.create_histogram( + name=RPC_SERVER_REQUESTS_PER_RPC, + description="Measures the number of messages received per RPC.", + unit="{count}", + ) + + +RPC_SERVER_RESPONSE_SIZE: Final = "rpc.server.response.size" +""" +Deprecated: Removed, no replacement at this time. +""" + + +def create_rpc_server_response_size(meter: Meter) -> Histogram: + """Measures the size of RPC response messages (uncompressed)""" + return meter.create_histogram( + name=RPC_SERVER_RESPONSE_SIZE, + description="Measures the size of RPC response messages (uncompressed).", + unit="By", + ) + + +RPC_SERVER_RESPONSES_PER_RPC: Final = "rpc.server.responses_per_rpc" +""" +Deprecated: Removed, no replacement at this time. +""" + + +def create_rpc_server_responses_per_rpc(meter: Meter) -> Histogram: + """Measures the number of messages sent per RPC""" + return meter.create_histogram( + name=RPC_SERVER_RESPONSES_PER_RPC, + description="Measures the number of messages sent per RPC.", + unit="{count}", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/system_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/system_metrics.py new file mode 100644 index 0000000..02f5632 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/system_metrics.py @@ -0,0 +1,835 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from collections.abc import Callable, Generator, Iterable, Sequence +from typing import Final + +from opentelemetry.metrics import CallbackOptions, Counter, Meter, ObservableGauge, Observation, UpDownCounter + +# pylint: disable=invalid-name +CallbackT = Callable[[CallbackOptions], Iterable[Observation]] | Generator[Iterable[Observation], CallbackOptions, None] + + +SYSTEM_CPU_FREQUENCY: Final = "system.cpu.frequency" +""" +Operating frequency of the logical CPU in Hertz +Instrument: gauge +Unit: Hz +""" + + +def create_system_cpu_frequency(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Operating frequency of the logical CPU in Hertz""" + return meter.create_observable_gauge( + name=SYSTEM_CPU_FREQUENCY, + callbacks=callbacks, + description="Operating frequency of the logical CPU in Hertz.", + unit="Hz", + ) + + +SYSTEM_CPU_LOGICAL_COUNT: Final = "system.cpu.logical.count" +""" +Reports the number of logical (virtual) processor cores created by the operating system to manage multitasking +Instrument: updowncounter +Unit: {cpu} +Note: Calculated by multiplying the number of sockets by the number of cores per socket, and then by the number of threads per core. +""" + + +def create_system_cpu_logical_count(meter: Meter) -> UpDownCounter: + """Reports the number of logical (virtual) processor cores created by the operating system to manage multitasking""" + return meter.create_up_down_counter( + name=SYSTEM_CPU_LOGICAL_COUNT, + description="Reports the number of logical (virtual) processor cores created by the operating system to manage multitasking.", + unit="{cpu}", + ) + + +SYSTEM_CPU_PHYSICAL_COUNT: Final = "system.cpu.physical.count" +""" +Reports the number of actual physical processor cores on the hardware +Instrument: updowncounter +Unit: {cpu} +Note: Calculated by multiplying the number of sockets by the number of cores per socket. +""" + + +def create_system_cpu_physical_count(meter: Meter) -> UpDownCounter: + """Reports the number of actual physical processor cores on the hardware""" + return meter.create_up_down_counter( + name=SYSTEM_CPU_PHYSICAL_COUNT, + description="Reports the number of actual physical processor cores on the hardware.", + unit="{cpu}", + ) + + +SYSTEM_CPU_TIME: Final = "system.cpu.time" +""" +Seconds each logical CPU spent on each mode +Instrument: counter +Unit: s +""" + + +def create_system_cpu_time(meter: Meter) -> Counter: + """Seconds each logical CPU spent on each mode""" + return meter.create_counter( + name=SYSTEM_CPU_TIME, + description="Seconds each logical CPU spent on each mode.", + unit="s", + ) + + +SYSTEM_CPU_UTILIZATION: Final = "system.cpu.utilization" +""" +For each logical CPU, the utilization is calculated as the change in cumulative CPU time (cpu.time) over a measurement interval, divided by the elapsed time +Instrument: gauge +Unit: 1 +""" + + +def create_system_cpu_utilization(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """For each logical CPU, the utilization is calculated as the change in cumulative CPU time (cpu.time) over a measurement interval, divided by the elapsed time""" + return meter.create_observable_gauge( + name=SYSTEM_CPU_UTILIZATION, + callbacks=callbacks, + description="For each logical CPU, the utilization is calculated as the change in cumulative CPU time (cpu.time) over a measurement interval, divided by the elapsed time.", + unit="1", + ) + + +SYSTEM_DISK_IO: Final = "system.disk.io" +""" +Disk bytes transferred +Instrument: counter +Unit: By +""" + + +def create_system_disk_io(meter: Meter) -> Counter: + """Disk bytes transferred""" + return meter.create_counter( + name=SYSTEM_DISK_IO, + description="Disk bytes transferred.", + unit="By", + ) + + +SYSTEM_DISK_IO_TIME: Final = "system.disk.io_time" +""" +Time disk spent activated +Instrument: counter +Unit: s +Note: The real elapsed time ("wall clock") used in the I/O path (time from operations running in parallel are not counted). Measured as: + +- Linux: Field 13 from [procfs-diskstats](https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats) +- Windows: The complement of + ["Disk\\% Idle Time"](https://learn.microsoft.com/archive/blogs/askcore/windows-performance-monitor-disk-counters-explained#windows-performance-monitor-disk-counters-explained) + performance counter: `uptime * (100 - "Disk\\% Idle Time") / 100`. +""" + + +def create_system_disk_io_time(meter: Meter) -> Counter: + """Time disk spent activated""" + return meter.create_counter( + name=SYSTEM_DISK_IO_TIME, + description="Time disk spent activated.", + unit="s", + ) + + +SYSTEM_DISK_LIMIT: Final = "system.disk.limit" +""" +The total storage capacity of the disk +Instrument: updowncounter +Unit: By +""" + + +def create_system_disk_limit(meter: Meter) -> UpDownCounter: + """The total storage capacity of the disk""" + return meter.create_up_down_counter( + name=SYSTEM_DISK_LIMIT, + description="The total storage capacity of the disk.", + unit="By", + ) + + +SYSTEM_DISK_MERGED: Final = "system.disk.merged" +""" +The number of disk reads/writes merged into single physical disk access operations +Instrument: counter +Unit: {operation} +""" + + +def create_system_disk_merged(meter: Meter) -> Counter: + """The number of disk reads/writes merged into single physical disk access operations""" + return meter.create_counter( + name=SYSTEM_DISK_MERGED, + description="The number of disk reads/writes merged into single physical disk access operations.", + unit="{operation}", + ) + + +SYSTEM_DISK_OPERATION_TIME: Final = "system.disk.operation_time" +""" +Sum of the time each operation took to complete +Instrument: counter +Unit: s +Note: Because it is the sum of time each request took, parallel-issued requests each contribute to make the count grow. Measured as: + +- Linux: Fields 7 & 11 from [procfs-diskstats](https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats) +- Windows: "Avg. Disk sec/Read" perf counter multiplied by "Disk Reads/sec" perf counter (similar for Writes). +""" + + +def create_system_disk_operation_time(meter: Meter) -> Counter: + """Sum of the time each operation took to complete""" + return meter.create_counter( + name=SYSTEM_DISK_OPERATION_TIME, + description="Sum of the time each operation took to complete.", + unit="s", + ) + + +SYSTEM_DISK_OPERATIONS: Final = "system.disk.operations" +""" +Disk operations count +Instrument: counter +Unit: {operation} +""" + + +def create_system_disk_operations(meter: Meter) -> Counter: + """Disk operations count""" + return meter.create_counter( + name=SYSTEM_DISK_OPERATIONS, + description="Disk operations count.", + unit="{operation}", + ) + + +SYSTEM_FILESYSTEM_LIMIT: Final = "system.filesystem.limit" +""" +The total storage capacity of the filesystem +Instrument: updowncounter +Unit: By +""" + + +def create_system_filesystem_limit(meter: Meter) -> UpDownCounter: + """The total storage capacity of the filesystem""" + return meter.create_up_down_counter( + name=SYSTEM_FILESYSTEM_LIMIT, + description="The total storage capacity of the filesystem.", + unit="By", + ) + + +SYSTEM_FILESYSTEM_LOCK_COUNT: Final = "system.filesystem.lock.count" +""" +Filesystem lock counts +Instrument: updowncounter +Unit: {lock} +""" + + +def create_system_filesystem_lock_count(meter: Meter) -> UpDownCounter: + """Filesystem lock counts""" + return meter.create_up_down_counter( + name=SYSTEM_FILESYSTEM_LOCK_COUNT, + description="Filesystem lock counts.", + unit="{lock}", + ) + + +SYSTEM_FILESYSTEM_USAGE: Final = "system.filesystem.usage" +""" +Reports a filesystem's space usage across different states +Instrument: updowncounter +Unit: By +Note: The sum of all `system.filesystem.usage` values over the different `system.filesystem.state` attributes +SHOULD equal the total storage capacity of the filesystem, that is `system.filesystem.limit`. +""" + + +def create_system_filesystem_usage(meter: Meter) -> UpDownCounter: + """Reports a filesystem's space usage across different states""" + return meter.create_up_down_counter( + name=SYSTEM_FILESYSTEM_USAGE, + description="Reports a filesystem's space usage across different states.", + unit="By", + ) + + +SYSTEM_FILESYSTEM_UTILIZATION: Final = "system.filesystem.utilization" +""" +Fraction of filesystem bytes used +Instrument: gauge +Unit: 1 +""" + + +def create_system_filesystem_utilization(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Fraction of filesystem bytes used""" + return meter.create_observable_gauge( + name=SYSTEM_FILESYSTEM_UTILIZATION, + callbacks=callbacks, + description="Fraction of filesystem bytes used.", + unit="1", + ) + + +SYSTEM_LINUX_MEMORY_AVAILABLE: Final = "system.linux.memory.available" +""" +Deprecated: Replaced by `system.memory.linux.available`. +""" + + +def create_system_linux_memory_available(meter: Meter) -> Counter: + """The number of packets transferred""" + return meter.create_counter( + name=SYSTEM_LINUX_MEMORY_AVAILABLE, + description="The number of packets transferred.", + unit="{packet}", + ) + + +SYSTEM_LINUX_MEMORY_SLAB_USAGE: Final = "system.linux.memory.slab.usage" +""" +Deprecated: Replaced by `system.memory.linux.slab.usage`. +""" + + +def create_system_linux_memory_slab_usage(meter: Meter) -> Counter: + """The number of packets transferred""" + return meter.create_counter( + name=SYSTEM_LINUX_MEMORY_SLAB_USAGE, + description="The number of packets transferred.", + unit="{packet}", + ) + + +SYSTEM_MEMORY_LIMIT: Final = "system.memory.limit" +""" +Total virtual memory available in the system +Instrument: updowncounter +Unit: By +""" + + +def create_system_memory_limit(meter: Meter) -> UpDownCounter: + """Total virtual memory available in the system""" + return meter.create_up_down_counter( + name=SYSTEM_MEMORY_LIMIT, + description="Total virtual memory available in the system.", + unit="By", + ) + + +SYSTEM_MEMORY_LINUX_AVAILABLE: Final = "system.memory.linux.available" +""" +An estimate of how much memory is available for starting new applications, without causing swapping +Instrument: updowncounter +Unit: By +Note: This is an alternative to `system.memory.usage` metric with `state=free`. +Linux starting from 3.14 exports "available" memory. It takes "free" memory as a baseline, and then factors in kernel-specific values. +This is supposed to be more accurate than just "free" memory. +For reference, see the calculations [here](https://superuser.com/a/980821). +See also `MemAvailable` in [/proc/meminfo](https://man7.org/linux/man-pages/man5/proc.5.html). +""" + + +def create_system_memory_linux_available(meter: Meter) -> UpDownCounter: + """An estimate of how much memory is available for starting new applications, without causing swapping""" + return meter.create_up_down_counter( + name=SYSTEM_MEMORY_LINUX_AVAILABLE, + description="An estimate of how much memory is available for starting new applications, without causing swapping.", + unit="By", + ) + + +SYSTEM_MEMORY_LINUX_HUGEPAGES_LIMIT: Final = "system.memory.linux.hugepages.limit" +""" +Total number of hugepages available +Instrument: updowncounter +Unit: {page} +""" + + +def create_system_memory_linux_hugepages_limit(meter: Meter) -> UpDownCounter: + """Total number of hugepages available""" + return meter.create_up_down_counter( + name=SYSTEM_MEMORY_LINUX_HUGEPAGES_LIMIT, + description="Total number of hugepages available.", + unit="{page}", + ) + + +SYSTEM_MEMORY_LINUX_HUGEPAGES_PAGE_SIZE: Final = "system.memory.linux.hugepages.page_size" +""" +System hugepage size in bytes +Instrument: updowncounter +Unit: By +""" + + +def create_system_memory_linux_hugepages_page_size(meter: Meter) -> UpDownCounter: + """System hugepage size in bytes""" + return meter.create_up_down_counter( + name=SYSTEM_MEMORY_LINUX_HUGEPAGES_PAGE_SIZE, + description="System hugepage size in bytes.", + unit="By", + ) + + +SYSTEM_MEMORY_LINUX_HUGEPAGES_RESERVED: Final = "system.memory.linux.hugepages.reserved" +""" +Number of reserved hugepages +Instrument: updowncounter +Unit: {page} +Note: Hugepages for which a commitment to allocate has been made, but no allocation has yet been made. +This is reported as a separate metric rather than a `usage` state because reserved pages are already counted in `free` pages. +They represent a subset of free pages that cannot be used for non-reserved allocations. +""" + + +def create_system_memory_linux_hugepages_reserved(meter: Meter) -> UpDownCounter: + """Number of reserved hugepages""" + return meter.create_up_down_counter( + name=SYSTEM_MEMORY_LINUX_HUGEPAGES_RESERVED, + description="Number of reserved hugepages.", + unit="{page}", + ) + + +SYSTEM_MEMORY_LINUX_HUGEPAGES_SURPLUS: Final = "system.memory.linux.hugepages.surplus" +""" +Number of surplus hugepages +Instrument: updowncounter +Unit: {page} +Note: Overcommitted hugepages beyond the persistent pool. +This is reported as a separate metric rather than a `usage` state because surplus pages can be in either `used` or `free` state. +Including them in `usage` would break the convention that `usage` states sum to the `limit`. +""" + + +def create_system_memory_linux_hugepages_surplus(meter: Meter) -> UpDownCounter: + """Number of surplus hugepages""" + return meter.create_up_down_counter( + name=SYSTEM_MEMORY_LINUX_HUGEPAGES_SURPLUS, + description="Number of surplus hugepages.", + unit="{page}", + ) + + +SYSTEM_MEMORY_LINUX_HUGEPAGES_USAGE: Final = "system.memory.linux.hugepages.usage" +""" +Number of hugepages in use by state +Instrument: updowncounter +Unit: {page} +""" + + +def create_system_memory_linux_hugepages_usage(meter: Meter) -> UpDownCounter: + """Number of hugepages in use by state""" + return meter.create_up_down_counter( + name=SYSTEM_MEMORY_LINUX_HUGEPAGES_USAGE, + description="Number of hugepages in use by state.", + unit="{page}", + ) + + +SYSTEM_MEMORY_LINUX_HUGEPAGES_UTILIZATION: Final = "system.memory.linux.hugepages.utilization" +""" +Percentage of hugepages in use by state +Instrument: gauge +Unit: 1 +""" + + +def create_system_memory_linux_hugepages_utilization( + meter: Meter, callbacks: Sequence[CallbackT] | None +) -> ObservableGauge: + """Percentage of hugepages in use by state""" + return meter.create_observable_gauge( + name=SYSTEM_MEMORY_LINUX_HUGEPAGES_UTILIZATION, + callbacks=callbacks, + description="Percentage of hugepages in use by state.", + unit="1", + ) + + +SYSTEM_MEMORY_LINUX_SHARED: Final = "system.memory.linux.shared" +""" +Shared memory used (mostly by tmpfs) +Instrument: updowncounter +Unit: By +Note: Equivalent of `shared` from [`free` command](https://man7.org/linux/man-pages/man1/free.1.html) or +`Shmem` from [`/proc/meminfo`](https://man7.org/linux/man-pages/man5/proc.5.html)". +""" + + +def create_system_memory_linux_shared(meter: Meter) -> UpDownCounter: + """Shared memory used (mostly by tmpfs)""" + return meter.create_up_down_counter( + name=SYSTEM_MEMORY_LINUX_SHARED, + description="Shared memory used (mostly by tmpfs).", + unit="By", + ) + + +SYSTEM_MEMORY_LINUX_SLAB_USAGE: Final = "system.memory.linux.slab.usage" +""" +Reports the memory used by the Linux kernel for managing caches of frequently used objects +Instrument: updowncounter +Unit: By +Note: The sum over the `reclaimable` and `unreclaimable` state values in `memory.linux.slab.usage` SHOULD be equal to the total slab memory available on the system. +Note that the total slab memory is not constant and may vary over time. +See also the [Slab allocator](https://blogs.oracle.com/linux/post/understanding-linux-kernel-memory-statistics) and `Slab` in [/proc/meminfo](https://man7.org/linux/man-pages/man5/proc.5.html). +""" + + +def create_system_memory_linux_slab_usage(meter: Meter) -> UpDownCounter: + """Reports the memory used by the Linux kernel for managing caches of frequently used objects""" + return meter.create_up_down_counter( + name=SYSTEM_MEMORY_LINUX_SLAB_USAGE, + description="Reports the memory used by the Linux kernel for managing caches of frequently used objects.", + unit="By", + ) + + +SYSTEM_MEMORY_SHARED: Final = "system.memory.shared" +""" +Deprecated: Replaced by `system.memory.linux.shared`. +""" + + +def create_system_memory_shared(meter: Meter) -> UpDownCounter: + """Deprecated, use `system.memory.linux.shared` instead""" + return meter.create_up_down_counter( + name=SYSTEM_MEMORY_SHARED, + description="Deprecated, use `system.memory.linux.shared` instead.", + unit="By", + ) + + +SYSTEM_MEMORY_USAGE: Final = "system.memory.usage" +""" +Reports memory in use by state +Instrument: updowncounter +Unit: By +""" + + +def create_system_memory_usage(meter: Meter) -> UpDownCounter: + """Reports memory in use by state""" + return meter.create_up_down_counter( + name=SYSTEM_MEMORY_USAGE, + description="Reports memory in use by state.", + unit="By", + ) + + +SYSTEM_MEMORY_UTILIZATION: Final = "system.memory.utilization" +""" +Percentage of memory bytes in use +Instrument: gauge +Unit: 1 +""" + + +def create_system_memory_utilization(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Percentage of memory bytes in use""" + return meter.create_observable_gauge( + name=SYSTEM_MEMORY_UTILIZATION, + callbacks=callbacks, + description="Percentage of memory bytes in use.", + unit="1", + ) + + +SYSTEM_NETWORK_CONNECTION_COUNT: Final = "system.network.connection.count" +""" +The number of connections +Instrument: updowncounter +Unit: {connection} +""" + + +def create_system_network_connection_count(meter: Meter) -> UpDownCounter: + """The number of connections""" + return meter.create_up_down_counter( + name=SYSTEM_NETWORK_CONNECTION_COUNT, + description="The number of connections.", + unit="{connection}", + ) + + +SYSTEM_NETWORK_CONNECTIONS: Final = "system.network.connections" +""" +Deprecated: Replaced by `system.network.connection.count`. +""" + + +def create_system_network_connections(meter: Meter) -> UpDownCounter: + """Deprecated, use `system.network.connection.count` instead""" + return meter.create_up_down_counter( + name=SYSTEM_NETWORK_CONNECTIONS, + description="Deprecated, use `system.network.connection.count` instead.", + unit="{connection}", + ) + + +SYSTEM_NETWORK_DROPPED: Final = "system.network.dropped" +""" +Deprecated: Replaced by `system.network.packet.dropped`. +""" + + +def create_system_network_dropped(meter: Meter) -> Counter: + """Count of packets that are dropped or discarded even though there was no error""" + return meter.create_counter( + name=SYSTEM_NETWORK_DROPPED, + description="Count of packets that are dropped or discarded even though there was no error.", + unit="{packet}", + ) + + +SYSTEM_NETWORK_ERRORS: Final = "system.network.errors" +""" +Count of network errors detected +Instrument: counter +Unit: {error} +Note: Measured as: + +- Linux: the `errs` column in `/proc/net/dev` ([source](https://web.archive.org/web/20180321091318/http://www.onlamp.com/pub/a/linux/2000/11/16/LinuxAdmin.html)). +- Windows: [`InErrors`/`OutErrors`](https://docs.microsoft.com/windows/win32/api/netioapi/ns-netioapi-mib_if_row2) + from [`GetIfEntry2`](https://docs.microsoft.com/windows/win32/api/netioapi/nf-netioapi-getifentry2). +""" + + +def create_system_network_errors(meter: Meter) -> Counter: + """Count of network errors detected""" + return meter.create_counter( + name=SYSTEM_NETWORK_ERRORS, + description="Count of network errors detected.", + unit="{error}", + ) + + +SYSTEM_NETWORK_IO: Final = "system.network.io" +""" +The number of bytes transmitted and received +Instrument: counter +Unit: By +""" + + +def create_system_network_io(meter: Meter) -> Counter: + """The number of bytes transmitted and received""" + return meter.create_counter( + name=SYSTEM_NETWORK_IO, + description="The number of bytes transmitted and received.", + unit="By", + ) + + +SYSTEM_NETWORK_PACKET_COUNT: Final = "system.network.packet.count" +""" +The number of packets transferred +Instrument: counter +Unit: {packet} +""" + + +def create_system_network_packet_count(meter: Meter) -> Counter: + """The number of packets transferred""" + return meter.create_counter( + name=SYSTEM_NETWORK_PACKET_COUNT, + description="The number of packets transferred.", + unit="{packet}", + ) + + +SYSTEM_NETWORK_PACKET_DROPPED: Final = "system.network.packet.dropped" +""" +Count of packets that are dropped or discarded even though there was no error +Instrument: counter +Unit: {packet} +Note: Measured as: + +- Linux: the `drop` column in `/proc/net/dev` ([source](https://web.archive.org/web/20180321091318/http://www.onlamp.com/pub/a/linux/2000/11/16/LinuxAdmin.html)) +- Windows: [`InDiscards`/`OutDiscards`](https://docs.microsoft.com/windows/win32/api/netioapi/ns-netioapi-mib_if_row2) + from [`GetIfEntry2`](https://docs.microsoft.com/windows/win32/api/netioapi/nf-netioapi-getifentry2). +""" + + +def create_system_network_packet_dropped(meter: Meter) -> Counter: + """Count of packets that are dropped or discarded even though there was no error""" + return meter.create_counter( + name=SYSTEM_NETWORK_PACKET_DROPPED, + description="Count of packets that are dropped or discarded even though there was no error.", + unit="{packet}", + ) + + +SYSTEM_NETWORK_PACKETS: Final = "system.network.packets" +""" +Deprecated: Replaced by `system.network.packet.count`. +""" + + +def create_system_network_packets(meter: Meter) -> Counter: + """The number of packets transferred""" + return meter.create_counter( + name=SYSTEM_NETWORK_PACKETS, + description="The number of packets transferred.", + unit="{packet}", + ) + + +SYSTEM_PAGING_FAULTS: Final = "system.paging.faults" +""" +The number of page faults +Instrument: counter +Unit: {fault} +""" + + +def create_system_paging_faults(meter: Meter) -> Counter: + """The number of page faults""" + return meter.create_counter( + name=SYSTEM_PAGING_FAULTS, + description="The number of page faults.", + unit="{fault}", + ) + + +SYSTEM_PAGING_OPERATIONS: Final = "system.paging.operations" +""" +The number of paging operations +Instrument: counter +Unit: {operation} +""" + + +def create_system_paging_operations(meter: Meter) -> Counter: + """The number of paging operations""" + return meter.create_counter( + name=SYSTEM_PAGING_OPERATIONS, + description="The number of paging operations.", + unit="{operation}", + ) + + +SYSTEM_PAGING_USAGE: Final = "system.paging.usage" +""" +UNIX swap or windows pagefile usage +Instrument: updowncounter +Unit: By +""" + + +def create_system_paging_usage(meter: Meter) -> UpDownCounter: + """UNIX swap or windows pagefile usage""" + return meter.create_up_down_counter( + name=SYSTEM_PAGING_USAGE, + description="UNIX swap or windows pagefile usage.", + unit="By", + ) + + +SYSTEM_PAGING_UTILIZATION: Final = "system.paging.utilization" +""" +Swap (UNIX) or pagefile (windows) utilization +Instrument: gauge +Unit: 1 +""" + + +def create_system_paging_utilization(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Swap (UNIX) or pagefile (windows) utilization""" + return meter.create_observable_gauge( + name=SYSTEM_PAGING_UTILIZATION, + callbacks=callbacks, + description="Swap (UNIX) or pagefile (windows) utilization.", + unit="1", + ) + + +SYSTEM_PROCESS_COUNT: Final = "system.process.count" +""" +Total number of processes in each state +Instrument: updowncounter +Unit: {process} +""" + + +def create_system_process_count(meter: Meter) -> UpDownCounter: + """Total number of processes in each state""" + return meter.create_up_down_counter( + name=SYSTEM_PROCESS_COUNT, + description="Total number of processes in each state.", + unit="{process}", + ) + + +SYSTEM_PROCESS_CREATED: Final = "system.process.created" +""" +Total number of processes created over uptime of the host +Instrument: counter +Unit: {process} +""" + + +def create_system_process_created(meter: Meter) -> Counter: + """Total number of processes created over uptime of the host""" + return meter.create_counter( + name=SYSTEM_PROCESS_CREATED, + description="Total number of processes created over uptime of the host.", + unit="{process}", + ) + + +SYSTEM_PROCESS_LIMIT: Final = "system.process.limit" +""" +The maximum number of concurrent processes/tasks allowed by the operating system +Instrument: updowncounter +Unit: {thread} +Note: On Linux, this corresponds to `/proc/sys/kernel/pid_max` or `/proc/sys/kernel/threads-max`. +A per-user process limit may also be retrieved via `getrlimit(RLIMIT_NPROC)`. +On BSD-like systems, this corresponds to `sysctl kern.maxproc`. This metric is unsupported on Windows systems. +""" + + +def create_system_process_limit(meter: Meter) -> UpDownCounter: + """The maximum number of concurrent processes/tasks allowed by the operating system""" + return meter.create_up_down_counter( + name=SYSTEM_PROCESS_LIMIT, + description="The maximum number of concurrent processes/tasks allowed by the operating system.", + unit="{thread}", + ) + + +SYSTEM_UPTIME: Final = "system.uptime" +""" +The time the system has been running +Instrument: gauge +Unit: s +Note: Instrumentations SHOULD use a gauge with type `double` and measure uptime in seconds as a floating point number with the highest precision available. +The actual accuracy would depend on the instrumentation and operating system. +""" + + +def create_system_uptime(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """The time the system has been running""" + return meter.create_observable_gauge( + name=SYSTEM_UPTIME, + callbacks=callbacks, + description="The time the system has been running.", + unit="s", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/vcs_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/vcs_metrics.py new file mode 100644 index 0000000..8398cfe --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/_incubating/metrics/vcs_metrics.py @@ -0,0 +1,193 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from collections.abc import Callable, Generator, Iterable, Sequence +from typing import Final + +from opentelemetry.metrics import CallbackOptions, Meter, ObservableGauge, Observation, UpDownCounter + +# pylint: disable=invalid-name +CallbackT = Callable[[CallbackOptions], Iterable[Observation]] | Generator[Iterable[Observation], CallbackOptions, None] + + +VCS_CHANGE_COUNT: Final = "vcs.change.count" +""" +The number of changes (pull requests/merge requests/changelists) in a repository, categorized by their state (e.g. open or merged) +Instrument: updowncounter +Unit: {change} +""" + + +def create_vcs_change_count(meter: Meter) -> UpDownCounter: + """The number of changes (pull requests/merge requests/changelists) in a repository, categorized by their state (e.g. open or merged)""" + return meter.create_up_down_counter( + name=VCS_CHANGE_COUNT, + description="The number of changes (pull requests/merge requests/changelists) in a repository, categorized by their state (e.g. open or merged).", + unit="{change}", + ) + + +VCS_CHANGE_DURATION: Final = "vcs.change.duration" +""" +The time duration a change (pull request/merge request/changelist) has been in a given state +Instrument: gauge +Unit: s +""" + + +def create_vcs_change_duration(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """The time duration a change (pull request/merge request/changelist) has been in a given state""" + return meter.create_observable_gauge( + name=VCS_CHANGE_DURATION, + callbacks=callbacks, + description="The time duration a change (pull request/merge request/changelist) has been in a given state.", + unit="s", + ) + + +VCS_CHANGE_TIME_TO_APPROVAL: Final = "vcs.change.time_to_approval" +""" +The amount of time since its creation it took a change (pull request/merge request/changelist) to get the first approval +Instrument: gauge +Unit: s +""" + + +def create_vcs_change_time_to_approval(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """The amount of time since its creation it took a change (pull request/merge request/changelist) to get the first approval""" + return meter.create_observable_gauge( + name=VCS_CHANGE_TIME_TO_APPROVAL, + callbacks=callbacks, + description="The amount of time since its creation it took a change (pull request/merge request/changelist) to get the first approval.", + unit="s", + ) + + +VCS_CHANGE_TIME_TO_MERGE: Final = "vcs.change.time_to_merge" +""" +The amount of time since its creation it took a change (pull request/merge request/changelist) to get merged into the target(base) ref +Instrument: gauge +Unit: s +""" + + +def create_vcs_change_time_to_merge(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """The amount of time since its creation it took a change (pull request/merge request/changelist) to get merged into the target(base) ref""" + return meter.create_observable_gauge( + name=VCS_CHANGE_TIME_TO_MERGE, + callbacks=callbacks, + description="The amount of time since its creation it took a change (pull request/merge request/changelist) to get merged into the target(base) ref.", + unit="s", + ) + + +VCS_CONTRIBUTOR_COUNT: Final = "vcs.contributor.count" +""" +The number of unique contributors to a repository +Instrument: gauge +Unit: {contributor} +""" + + +def create_vcs_contributor_count(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """The number of unique contributors to a repository""" + return meter.create_observable_gauge( + name=VCS_CONTRIBUTOR_COUNT, + callbacks=callbacks, + description="The number of unique contributors to a repository.", + unit="{contributor}", + ) + + +VCS_REF_COUNT: Final = "vcs.ref.count" +""" +The number of refs of type branch or tag in a repository +Instrument: updowncounter +Unit: {ref} +""" + + +def create_vcs_ref_count(meter: Meter) -> UpDownCounter: + """The number of refs of type branch or tag in a repository""" + return meter.create_up_down_counter( + name=VCS_REF_COUNT, + description="The number of refs of type branch or tag in a repository.", + unit="{ref}", + ) + + +VCS_REF_LINES_DELTA: Final = "vcs.ref.lines_delta" +""" +The number of lines added/removed in a ref (branch) relative to the ref from the `vcs.ref.base.name` attribute +Instrument: gauge +Unit: {line} +Note: This metric should be reported for each `vcs.line_change.type` value. For example if a ref added 3 lines and removed 2 lines, +instrumentation SHOULD report two measurements: 3 and 2 (both positive numbers). +If number of lines added/removed should be calculated from the start of time, then `vcs.ref.base.name` SHOULD be set to an empty string. +""" + + +def create_vcs_ref_lines_delta(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """The number of lines added/removed in a ref (branch) relative to the ref from the `vcs.ref.base.name` attribute""" + return meter.create_observable_gauge( + name=VCS_REF_LINES_DELTA, + callbacks=callbacks, + description="The number of lines added/removed in a ref (branch) relative to the ref from the `vcs.ref.base.name` attribute.", + unit="{line}", + ) + + +VCS_REF_REVISIONS_DELTA: Final = "vcs.ref.revisions_delta" +""" +The number of revisions (commits) a ref (branch) is ahead/behind the branch from the `vcs.ref.base.name` attribute +Instrument: gauge +Unit: {revision} +Note: This metric should be reported for each `vcs.revision_delta.direction` value. For example if branch `a` is 3 commits behind and 2 commits ahead of `trunk`, +instrumentation SHOULD report two measurements: 3 and 2 (both positive numbers) and `vcs.ref.base.name` is set to `trunk`. +""" + + +def create_vcs_ref_revisions_delta(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """The number of revisions (commits) a ref (branch) is ahead/behind the branch from the `vcs.ref.base.name` attribute""" + return meter.create_observable_gauge( + name=VCS_REF_REVISIONS_DELTA, + callbacks=callbacks, + description="The number of revisions (commits) a ref (branch) is ahead/behind the branch from the `vcs.ref.base.name` attribute.", + unit="{revision}", + ) + + +VCS_REF_TIME: Final = "vcs.ref.time" +""" +Time a ref (branch) created from the default branch (trunk) has existed. The `ref.type` attribute will always be `branch` +Instrument: gauge +Unit: s +""" + + +def create_vcs_ref_time(meter: Meter, callbacks: Sequence[CallbackT] | None) -> ObservableGauge: + """Time a ref (branch) created from the default branch (trunk) has existed. The `ref.type` attribute will always be `branch`""" + return meter.create_observable_gauge( + name=VCS_REF_TIME, + callbacks=callbacks, + description="Time a ref (branch) created from the default branch (trunk) has existed. The `ref.type` attribute will always be `branch`.", + unit="s", + ) + + +VCS_REPOSITORY_COUNT: Final = "vcs.repository.count" +""" +The number of repositories in an organization +Instrument: updowncounter +Unit: {repository} +""" + + +def create_vcs_repository_count(meter: Meter) -> UpDownCounter: + """The number of repositories in an organization""" + return meter.create_up_down_counter( + name=VCS_REPOSITORY_COUNT, + description="The number of repositories in an organization.", + unit="{repository}", + ) diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7d15fe0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/client_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/client_attributes.cpython-312.pyc new file mode 100644 index 0000000..03f4b57 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/client_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/code_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/code_attributes.cpython-312.pyc new file mode 100644 index 0000000..5548ac6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/code_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/container_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/container_attributes.cpython-312.pyc new file mode 100644 index 0000000..9003510 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/container_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/db_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/db_attributes.cpython-312.pyc new file mode 100644 index 0000000..5ecd8d6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/db_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/deployment_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/deployment_attributes.cpython-312.pyc new file mode 100644 index 0000000..bd2efa8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/deployment_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/error_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/error_attributes.cpython-312.pyc new file mode 100644 index 0000000..46a85e2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/error_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/exception_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/exception_attributes.cpython-312.pyc new file mode 100644 index 0000000..e24f4f3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/exception_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/http_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/http_attributes.cpython-312.pyc new file mode 100644 index 0000000..8241e04 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/http_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/k8s_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/k8s_attributes.cpython-312.pyc new file mode 100644 index 0000000..a576eed Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/k8s_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/network_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/network_attributes.cpython-312.pyc new file mode 100644 index 0000000..18b4fa0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/network_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/otel_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/otel_attributes.cpython-312.pyc new file mode 100644 index 0000000..4b32abb Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/otel_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/server_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/server_attributes.cpython-312.pyc new file mode 100644 index 0000000..75a55be Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/server_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/service_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/service_attributes.cpython-312.pyc new file mode 100644 index 0000000..6224e68 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/service_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/telemetry_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/telemetry_attributes.cpython-312.pyc new file mode 100644 index 0000000..8720d86 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/telemetry_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/url_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/url_attributes.cpython-312.pyc new file mode 100644 index 0000000..31f2b3b Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/url_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/user_agent_attributes.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/user_agent_attributes.cpython-312.pyc new file mode 100644 index 0000000..0012e00 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/__pycache__/user_agent_attributes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/client_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/client_attributes.py new file mode 100644 index 0000000..cf7bb33 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/client_attributes.py @@ -0,0 +1,16 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +CLIENT_ADDRESS: Final = "client.address" +""" +Client address - domain name if available without reverse DNS lookup; otherwise, IP address or UNIX domain socket name. +Note: When observed from the server side, and when communicating through an intermediary, `client.address` SHOULD represent the client address behind any intermediaries, for example proxies, if it's available. +""" + +CLIENT_PORT: Final = "client.port" +""" +Client port number. +Note: When observed from the server side, and when communicating through an intermediary, `client.port` SHOULD represent the client port behind any intermediaries, for example proxies, if it's available. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/code_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/code_attributes.py new file mode 100644 index 0000000..142fb95 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/code_attributes.py @@ -0,0 +1,44 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +CODE_COLUMN_NUMBER: Final = "code.column.number" +""" +The column number in `code.file.path` best representing the operation. It SHOULD point within the code unit named in `code.function.name`. This attribute MUST NOT be used on the Profile signal since the data is already captured in 'message Line'. This constraint is imposed to prevent redundancy and maintain data integrity. +""" + +CODE_FILE_PATH: Final = "code.file.path" +""" +The source code filename that identifies the code unit as uniquely as possible (preferably an absolute file path). This attribute MUST NOT be used on the Profile signal since the data is already captured in 'message Function'. This constraint is imposed to prevent redundancy and maintain data integrity. +""" + +CODE_FUNCTION_NAME: Final = "code.function.name" +""" +The method or function fully-qualified name without arguments. The value should fit the natural representation of the language runtime, which is also likely the same used within `code.stacktrace` attribute value. This attribute MUST NOT be used on the Profile signal since the data is already captured in 'message Function'. This constraint is imposed to prevent redundancy and maintain data integrity. +Note: Values and format depends on each language runtime, thus it is impossible to provide an exhaustive list of examples. +The values are usually the same (or prefixes of) the ones found in native stack trace representation stored in +`code.stacktrace` without information on arguments. + +Examples: + +* Java method: `com.example.MyHttpService.serveRequest` +* Java anonymous class method: `com.mycompany.Main$1.myMethod` +* Java lambda method: `com.mycompany.Main$$Lambda/0x0000748ae4149c00.myMethod` +* PHP function: `GuzzleHttp\\Client::transfer` +* Go function: `github.com/my/repo/pkg.foo.func5` +* Elixir: `OpenTelemetry.Ctx.new` +* Erlang: `opentelemetry_ctx:new` +* Rust: `playground::my_module::my_cool_func` +* C function: `fopen`. +""" + +CODE_LINE_NUMBER: Final = "code.line.number" +""" +The line number in `code.file.path` best representing the operation. It SHOULD point within the code unit named in `code.function.name`. This attribute MUST NOT be used on the Profile signal since the data is already captured in 'message Line'. This constraint is imposed to prevent redundancy and maintain data integrity. +""" + +CODE_STACKTRACE: Final = "code.stacktrace" +""" +A stacktrace as a string in the natural representation for the language runtime. The representation is identical to [`exception.stacktrace`](/docs/exceptions/exceptions-spans.md#stacktrace-representation). This attribute MUST NOT be used on the Profile signal since the data is already captured in 'message Location'. This constraint is imposed to prevent redundancy and maintain data integrity. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/container_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/container_attributes.py new file mode 100644 index 0000000..cc37397 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/container_attributes.py @@ -0,0 +1,25 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +CONTAINER_ID: Final = "container.id" +""" +Container ID. Usually a UUID, as for example used to [identify Docker containers](https://docs.docker.com/engine/containers/run/#container-identification). The UUID might be abbreviated. +""" + +CONTAINER_IMAGE_NAME: Final = "container.image.name" +""" +Name of the image the container was built on. +""" + +CONTAINER_IMAGE_REPO_DIGESTS: Final = "container.image.repo_digests" +""" +Repository digests of the container image as provided by the container runtime. +Note: [Docker](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Image/operation/ImageInspect) and [CRI](https://github.com/kubernetes/cri-api/blob/c75ef5b473bbe2d0a4fc92f82235efd665ea8e9f/pkg/apis/runtime/v1/api.proto#L1237-L1238) report those under the `RepoDigests` field. +""" + +CONTAINER_IMAGE_TAGS: Final = "container.image.tags" +""" +Container image tags. An example can be found in [Docker Image Inspect](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Image/operation/ImageInspect). Should be only the `` section of the full name for example from `registry.example.com/my-org/my-image:`. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/db_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/db_attributes.py new file mode 100644 index 0000000..26e7b48 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/db_attributes.py @@ -0,0 +1,134 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +DB_COLLECTION_NAME: Final = "db.collection.name" +""" +The name of a collection (table, container) within the database. +Note: It is RECOMMENDED to capture the value as provided by the application +without attempting to do any case normalization. + +The collection name SHOULD NOT be extracted from `db.query.text`, +when the database system supports query text with multiple collections +in non-batch operations. + +For batch operations, if the individual operations are known to have the same +collection name then that collection name SHOULD be used. +""" + +DB_NAMESPACE: Final = "db.namespace" +""" +The name of the database, fully qualified within the server address and port. +Note: If a database system has multiple namespace components, they SHOULD be concatenated from the most general to the most specific namespace component, using `|` as a separator between the components. Any missing components (and their associated separators) SHOULD be omitted. +Semantic conventions for individual database systems SHOULD document what `db.namespace` means in the context of that system. +It is RECOMMENDED to capture the value as provided by the application without attempting to do any case normalization. +""" + +DB_OPERATION_BATCH_SIZE: Final = "db.operation.batch.size" +""" +The number of database operations included in a batch operation. +Note: Except for empty batch requests described below, a batch operation contains two +or more database operations explicitly submitted as separate operations in a single +client call, protocol message, or database command. + +Requests to batch APIs that contain only one operation SHOULD be modeled as single +operations, not as batch operations. + +A database call is not a batch operation solely because one operation accepts +multiple operands, such as keys, rows, documents, points, or other data elements, +including Redis [`MGET`](https://redis.io/docs/latest/commands/mget/) with +multiple keys. + +In batch APIs that execute the same parameterized operation with parameter sets, +each parameter set represents one database operation for determining whether the +request is a batch operation. Requests with only one parameter set SHOULD be modeled +as single operations, not as batch operations. + +`db.operation.batch.size` SHOULD be set to the number of operations in the batch. +It SHOULD NOT be set for non-batch operations. + +A request to execute a batch operation with no operations SHOULD also be treated +as a batch operation, and `db.operation.batch.size` SHOULD be set to `0`. +""" + +DB_OPERATION_NAME: Final = "db.operation.name" +""" +The name of the operation or command being executed. +Note: It is RECOMMENDED to capture the value as provided by the application +without attempting to do any case normalization. + +The operation name SHOULD NOT be extracted from `db.query.text`, +when the database system supports query text with multiple operations +in non-batch operations. + +If spaces can occur in the operation name, multiple consecutive spaces +SHOULD be normalized to a single space. + +For batch operations, if the individual operations are known to have the same operation name +then that operation name SHOULD be used prepended by `BATCH `, +otherwise `db.operation.name` SHOULD be `BATCH` or some other database +system specific term if more applicable. +""" + +DB_QUERY_SUMMARY: Final = "db.query.summary" +""" +Low cardinality summary of a database query. +Note: The query summary describes a class of database queries and is useful +as a grouping key, especially when analyzing telemetry for database +calls involving complex queries. + +Summary may be available to the instrumentation through +instrumentation hooks or other means. If it is not available, instrumentations +that support query parsing SHOULD generate a summary following +[Generating query summary](/docs/db/database-spans.md#generating-a-summary-of-the-query) +section. + +For batch operations, if the individual operations are known to have the same query summary +then that query summary SHOULD be used prepended by `BATCH `, +otherwise `db.query.summary` SHOULD be `BATCH` or some other database +system specific term if more applicable. +""" + +DB_QUERY_TEXT: Final = "db.query.text" +""" +The database query being executed. +Note: For sanitization see [Sanitization of `db.query.text`](/docs/db/database-spans.md#sanitization-of-dbquerytext). +For batch operations, if the individual operations are known to have the same query text then that query text SHOULD be used, otherwise all of the individual query texts SHOULD be concatenated with separator `; ` or some other database system specific separator if more applicable. +Parameterized query text SHOULD NOT be sanitized. Even though parameterized query text can potentially have sensitive data, by using a parameterized query the user is giving a strong signal that any sensitive data will be passed as parameter values, and the benefit to observability of capturing the static part of the query text by default outweighs the risk. +""" + +DB_RESPONSE_STATUS_CODE: Final = "db.response.status_code" +""" +Database response status code. +Note: The status code returned by the database. Usually it represents an error code, but may also represent partial success, warning, or differentiate between various types of successful outcomes. +Semantic conventions for individual database systems SHOULD document what `db.response.status_code` means in the context of that system. +""" + +DB_STORED_PROCEDURE_NAME: Final = "db.stored_procedure.name" +""" +The name of a stored procedure within the database. +Note: It is RECOMMENDED to capture the value as provided by the application +without attempting to do any case normalization. + +For batch operations, if the individual operations are known to have the same +stored procedure name then that stored procedure name SHOULD be used. +""" + +DB_SYSTEM_NAME: Final = "db.system.name" +""" +The database management system (DBMS) product as identified by the client instrumentation. +Note: The actual DBMS may differ from the one identified by the client. For example, when using PostgreSQL client libraries to connect to a CockroachDB, the `db.system.name` is set to `postgresql` based on the instrumentation's best knowledge. +""" + + +class DbSystemNameValues(Enum): + MARIADB = "mariadb" + """[MariaDB](https://mariadb.org/).""" + MICROSOFT_SQL_SERVER = "microsoft.sql_server" + """[Microsoft SQL Server](https://www.microsoft.com/sql-server).""" + MYSQL = "mysql" + """[MySQL](https://www.mysql.com/).""" + POSTGRESQL = "postgresql" + """[PostgreSQL](https://www.postgresql.org/).""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/deployment_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/deployment_attributes.py new file mode 100644 index 0000000..338f935 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/deployment_attributes.py @@ -0,0 +1,28 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +DEPLOYMENT_ENVIRONMENT_NAME: Final = "deployment.environment.name" +""" +Name of the [deployment environment](https://wikipedia.org/wiki/Deployment_environment) (aka deployment tier). +Note: `deployment.environment.name` does not affect the uniqueness constraints defined through +the `service.namespace`, `service.name` and `service.instance.id` resource attributes. +This implies that resources carrying the following attribute combinations MUST be +considered to be identifying the same service: + +- `service.name=frontend`, `deployment.environment.name=production` +- `service.name=frontend`, `deployment.environment.name=staging`. +""" + + +class DeploymentEnvironmentNameValues(Enum): + PRODUCTION = "production" + """Production environment.""" + STAGING = "staging" + """Staging environment.""" + TEST = "test" + """Testing environment.""" + DEVELOPMENT = "development" + """Development environment.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/error_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/error_attributes.py new file mode 100644 index 0000000..9c4efd7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/error_attributes.py @@ -0,0 +1,40 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +ERROR_TYPE: Final = "error.type" +""" +Describes a class of error the operation ended with. +Note: The `error.type` SHOULD be predictable, and SHOULD have low cardinality. + +When `error.type` is set to a type (e.g., an exception type), its +canonical class name identifying the type within the artifact SHOULD be used. + +If the recorded error type is a wrapper that is not meaningful for +failure classification, instrumentation MAY use the type of the inner +error instead. For example, in Go, errors created with `fmt.Errorf` +using `%w` MAY be unwrapped when the wrapper type does not help +classify the failure. + +Instrumentations SHOULD document the list of errors they report. + +The cardinality of `error.type` within one instrumentation library SHOULD be low. +Telemetry consumers that aggregate data from multiple instrumentation libraries and applications +should be prepared for `error.type` to have high cardinality at query time when no +additional filters are applied. + +If the operation has completed successfully, instrumentations SHOULD NOT set `error.type`. + +If a specific domain defines its own set of error identifiers (such as HTTP or RPC status codes), +it's RECOMMENDED to: + +- Use a domain-specific attribute +- Set `error.type` to capture all errors, regardless of whether they are defined within the domain-specific set or not. +""" + + +class ErrorTypeValues(Enum): + OTHER = "_OTHER" + """A fallback error value to be used when the instrumentation doesn't define a custom value.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/exception_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/exception_attributes.py new file mode 100644 index 0000000..2d7e748 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/exception_attributes.py @@ -0,0 +1,32 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +EXCEPTION_ESCAPED: Final = "exception.escaped" +""" +Deprecated: It's no longer recommended to record exceptions that are handled and do not escape the scope of a span. +""" + +EXCEPTION_MESSAGE: Final = "exception.message" +""" +The exception message. +Note: > [!WARNING] +> +> This attribute may contain sensitive information. +""" + +EXCEPTION_STACKTRACE: Final = "exception.stacktrace" +""" +A stacktrace as a string in the natural representation for the language runtime. The representation is to be determined and documented by each language SIG. +""" + +EXCEPTION_TYPE: Final = "exception.type" +""" +The type of the exception (its fully-qualified class name, if applicable). The dynamic type of the exception should be preferred over the static type in languages that support it. +Note: If the recorded exception type is a wrapper that is not meaningful for +failure classification, instrumentation MAY use the type of the inner +exception instead. For example, in Go, errors created with `fmt.Errorf` +using `%w` MAY be unwrapped when the wrapper type does not help +classify the failure. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/http_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/http_attributes.py new file mode 100644 index 0000000..5dbad61 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/http_attributes.py @@ -0,0 +1,127 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +HTTP_REQUEST_HEADER_TEMPLATE: Final = "http.request.header" +""" +HTTP request headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. +Note: Instrumentations SHOULD require an explicit configuration of which headers are to be captured. +Including all request headers can be a security risk - explicit configuration helps avoid leaking sensitive information. + +The `User-Agent` header is already captured in the `user_agent.original` attribute. +Users MAY explicitly configure instrumentations to capture them even though it is not recommended. + +The attribute value MUST consist of either multiple header values as an array of strings +or a single-item array containing a possibly comma-concatenated string, depending on the way +the HTTP library provides access to headers. + +Examples: + +- A header `Content-Type: application/json` SHOULD be recorded as the `http.request.header.content-type` + attribute with value `["application/json"]`. +- A header `X-Forwarded-For: 1.2.3.4, 1.2.3.5` SHOULD be recorded as the `http.request.header.x-forwarded-for` + attribute with value `["1.2.3.4", "1.2.3.5"]` or `["1.2.3.4, 1.2.3.5"]` depending on the HTTP library. +""" + +HTTP_REQUEST_METHOD: Final = "http.request.method" +""" +HTTP request method. +Note: HTTP request method value SHOULD be "known" to the instrumentation. +By default, this convention defines "known" methods as the ones listed in [RFC9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-methods), +the PATCH method defined in [RFC5789](https://www.rfc-editor.org/rfc/rfc5789.html) +and the QUERY method defined in [httpbis-safe-method-w-body](https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/?include_text=1). + +If the HTTP request method is not known to instrumentation, it MUST set the `http.request.method` attribute to `_OTHER`. + +If the HTTP instrumentation could end up converting valid HTTP request methods to `_OTHER`, then it MUST provide a way to override +the list of known HTTP methods. If this override is done via environment variable, then the environment variable MUST be named +OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated list of case-sensitive known HTTP methods. + +![Development](https://img.shields.io/badge/-development-blue) +If this override is done via declarative configuration, then the list MUST be configurable via the `known_methods` property +(an array of case-sensitive strings with minimum items 0) under `.instrumentation/development.general.http.client` and/or +`.instrumentation/development.general.http.server`. + +In either case, this list MUST be a full override of the default known methods, +it is not a list of known methods in addition to the defaults. + +HTTP method names are case-sensitive and `http.request.method` attribute value MUST match a known HTTP method name exactly. +Instrumentations for specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical equivalent. +Tracing instrumentations that do so, MUST also set `http.request.method_original` to the original value. +""" + +HTTP_REQUEST_METHOD_ORIGINAL: Final = "http.request.method_original" +""" +Original HTTP method sent by the client in the request line. +""" + +HTTP_REQUEST_RESEND_COUNT: Final = "http.request.resend_count" +""" +The ordinal number of request resending attempt (for any reason, including redirects). +Note: The resend count SHOULD be updated each time an HTTP request gets resent by the client, regardless of what was the cause of the resending (e.g. redirection, authorization failure, 503 Server Unavailable, network issues, or any other). +""" + +HTTP_RESPONSE_HEADER_TEMPLATE: Final = "http.response.header" +""" +HTTP response headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. +Note: Instrumentations SHOULD require an explicit configuration of which headers are to be captured. +Including all response headers can be a security risk - explicit configuration helps avoid leaking sensitive information. + +Users MAY explicitly configure instrumentations to capture them even though it is not recommended. + +The attribute value MUST consist of either multiple header values as an array of strings +or a single-item array containing a possibly comma-concatenated string, depending on the way +the HTTP library provides access to headers. + +Examples: + +- A header `Content-Type: application/json` header SHOULD be recorded as the `http.request.response.content-type` + attribute with value `["application/json"]`. +- A header `My-custom-header: abc, def` header SHOULD be recorded as the `http.response.header.my-custom-header` + attribute with value `["abc", "def"]` or `["abc, def"]` depending on the HTTP library. +""" + +HTTP_RESPONSE_STATUS_CODE: Final = "http.response.status_code" +""" +[HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). +""" + +HTTP_ROUTE: Final = "http.route" +""" +The matched route template for the request. This MUST be low-cardinality and include all static path segments, with dynamic path segments represented with placeholders. +Note: MUST NOT be populated when this is not supported by the HTTP server framework as the route attribute should have low-cardinality and the URI path can NOT substitute it. +SHOULD include the [application root](/docs/http/http-spans.md#http-server-definitions) if there is one. + +A static path segment is a part of the route template with a fixed, low-cardinality value. This includes literal strings like `/users/` and placeholders that +are constrained to a finite, predefined set of values, e.g. `{controller}` or `{action}`. + +A dynamic path segment is a placeholder for a value that can have high cardinality and is not constrained to a predefined list like static path segments. + +Instrumentations SHOULD use routing information provided by the corresponding web framework. They SHOULD pick the most precise source of routing information and MAY +support custom route formatting. Instrumentations SHOULD document the format and the API used to obtain the route string. +""" + + +class HttpRequestMethodValues(Enum): + CONNECT = "CONNECT" + """CONNECT method.""" + DELETE = "DELETE" + """DELETE method.""" + GET = "GET" + """GET method.""" + HEAD = "HEAD" + """HEAD method.""" + OPTIONS = "OPTIONS" + """OPTIONS method.""" + PATCH = "PATCH" + """PATCH method.""" + POST = "POST" + """POST method.""" + PUT = "PUT" + """PUT method.""" + TRACE = "TRACE" + """TRACE method.""" + OTHER = "_OTHER" + """Any HTTP method that the instrumentation has no prior knowledge of.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/k8s_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/k8s_attributes.py new file mode 100644 index 0000000..ca07fad --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/k8s_attributes.py @@ -0,0 +1,362 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +K8S_CLUSTER_NAME: Final = "k8s.cluster.name" +""" +The name of the cluster. +""" + +K8S_CLUSTER_UID: Final = "k8s.cluster.uid" +""" +A pseudo-ID for the cluster, set to the UID of the `kube-system` namespace. +Note: K8s doesn't have support for obtaining a cluster ID. If this is ever +added, we will recommend collecting the `k8s.cluster.uid` through the +official APIs. In the meantime, we are able to use the `uid` of the +`kube-system` namespace as a proxy for cluster ID. Read on for the +rationale. + +Every object created in a K8s cluster is assigned a distinct UID. The +`kube-system` namespace is used by Kubernetes itself and will exist +for the lifetime of the cluster. Using the `uid` of the `kube-system` +namespace is a reasonable proxy for the K8s ClusterID as it will only +change if the cluster is rebuilt. Furthermore, Kubernetes UIDs are +UUIDs as standardized by +[ISO/IEC 9834-8 and ITU-T X.667](https://www.itu.int/ITU-T/studygroups/com17/oid.html). +Which states: + +> If generated according to one of the mechanisms defined in Rec. +> ITU-T X.667 | ISO/IEC 9834-8, a UUID is either guaranteed to be +> different from all other UUIDs generated before 3603 A.D., or is +> extremely likely to be different (depending on the mechanism chosen). + +Therefore, UIDs between clusters should be extremely unlikely to +conflict. +""" + +K8S_CONTAINER_NAME: Final = "k8s.container.name" +""" +The name of the Container from Pod specification, must be unique within a Pod. Container runtime usually uses different globally unique name (`container.name`). +""" + +K8S_CONTAINER_RESTART_COUNT: Final = "k8s.container.restart_count" +""" +Number of times the container was restarted. This attribute can be used to identify a particular container (running or stopped) within a container spec. +""" + +K8S_CRONJOB_ANNOTATION_TEMPLATE: Final = "k8s.cronjob.annotation" +""" +The cronjob annotation placed on the CronJob, the `` being the annotation name, the value being the annotation value. +Note: Examples: + +- An annotation `retries` with value `4` SHOULD be recorded as the + `k8s.cronjob.annotation.retries` attribute with value `"4"`. +- An annotation `data` with empty string value SHOULD be recorded as + the `k8s.cronjob.annotation.data` attribute with value `""`. +""" + +K8S_CRONJOB_LABEL_TEMPLATE: Final = "k8s.cronjob.label" +""" +The label placed on the CronJob, the `` being the label name, the value being the label value. +Note: Examples: + +- A label `type` with value `weekly` SHOULD be recorded as the + `k8s.cronjob.label.type` attribute with value `"weekly"`. +- A label `automated` with empty string value SHOULD be recorded as + the `k8s.cronjob.label.automated` attribute with value `""`. +""" + +K8S_CRONJOB_NAME: Final = "k8s.cronjob.name" +""" +The name of the CronJob. +""" + +K8S_CRONJOB_UID: Final = "k8s.cronjob.uid" +""" +The UID of the CronJob. +""" + +K8S_DAEMONSET_ANNOTATION_TEMPLATE: Final = "k8s.daemonset.annotation" +""" +The annotation placed on the DaemonSet, the `` being the annotation name, the value being the annotation value, even if the value is empty. +Note: Examples: + +- An annotation `replicas` with value `1` SHOULD be recorded + as the `k8s.daemonset.annotation.replicas` attribute with value `"1"`. +- An annotation `data` with empty string value SHOULD be recorded as + the `k8s.daemonset.annotation.data` attribute with value `""`. +""" + +K8S_DAEMONSET_LABEL_TEMPLATE: Final = "k8s.daemonset.label" +""" +The label placed on the DaemonSet, the `` being the label name, the value being the label value, even if the value is empty. +Note: Examples: + +- A label `app` with value `guestbook` SHOULD be recorded + as the `k8s.daemonset.label.app` attribute with value `"guestbook"`. +- A label `injected` with empty string value SHOULD be recorded as + the `k8s.daemonset.label.injected` attribute with value `""`. +""" + +K8S_DAEMONSET_NAME: Final = "k8s.daemonset.name" +""" +The name of the DaemonSet. +""" + +K8S_DAEMONSET_UID: Final = "k8s.daemonset.uid" +""" +The UID of the DaemonSet. +""" + +K8S_DEPLOYMENT_ANNOTATION_TEMPLATE: Final = "k8s.deployment.annotation" +""" +The annotation placed on the Deployment, the `` being the annotation name, the value being the annotation value, even if the value is empty. +Note: Examples: + +- An annotation `replicas` with value `1` SHOULD be recorded + as the `k8s.deployment.annotation.replicas` attribute with value `"1"`. +- An annotation `data` with empty string value SHOULD be recorded as + the `k8s.deployment.annotation.data` attribute with value `""`. +""" + +K8S_DEPLOYMENT_LABEL_TEMPLATE: Final = "k8s.deployment.label" +""" +The label placed on the Deployment, the `` being the label name, the value being the label value, even if the value is empty. +Note: Examples: + +- A label `app` with value `guestbook` SHOULD be recorded + as the `k8s.deployment.label.app` attribute with value `"guestbook"`. +- A label `injected` with empty string value SHOULD be recorded as + the `k8s.deployment.label.injected` attribute with value `""`. +""" + +K8S_DEPLOYMENT_NAME: Final = "k8s.deployment.name" +""" +The name of the Deployment. +""" + +K8S_DEPLOYMENT_UID: Final = "k8s.deployment.uid" +""" +The UID of the Deployment. +""" + +K8S_JOB_ANNOTATION_TEMPLATE: Final = "k8s.job.annotation" +""" +The annotation placed on the Job, the `` being the annotation name, the value being the annotation value, even if the value is empty. +Note: Examples: + +- An annotation `number` with value `1` SHOULD be recorded + as the `k8s.job.annotation.number` attribute with value `"1"`. +- An annotation `data` with empty string value SHOULD be recorded as + the `k8s.job.annotation.data` attribute with value `""`. +""" + +K8S_JOB_LABEL_TEMPLATE: Final = "k8s.job.label" +""" +The label placed on the Job, the `` being the label name, the value being the label value, even if the value is empty. +Note: Examples: + +- A label `jobtype` with value `ci` SHOULD be recorded + as the `k8s.job.label.jobtype` attribute with value `"ci"`. +- A label `automated` with empty string value SHOULD be recorded as + the `k8s.job.label.automated` attribute with value `""`. +""" + +K8S_JOB_NAME: Final = "k8s.job.name" +""" +The name of the Job. +""" + +K8S_JOB_UID: Final = "k8s.job.uid" +""" +The UID of the Job. +""" + +K8S_NAMESPACE_ANNOTATION_TEMPLATE: Final = "k8s.namespace.annotation" +""" +The annotation placed on the Namespace, the `` being the annotation name, the value being the annotation value, even if the value is empty. +Note: Examples: + +- An annotation `ttl` with value `0` SHOULD be recorded + as the `k8s.namespace.annotation.ttl` attribute with value `"0"`. +- An annotation `data` with empty string value SHOULD be recorded as + the `k8s.namespace.annotation.data` attribute with value `""`. +""" + +K8S_NAMESPACE_LABEL_TEMPLATE: Final = "k8s.namespace.label" +""" +The label placed on the Namespace, the `` being the label name, the value being the label value, even if the value is empty. +Note: Examples: + +- A label `kubernetes.io/metadata.name` with value `default` SHOULD be recorded + as the `k8s.namespace.label.kubernetes.io/metadata.name` attribute with value `"default"`. +- A label `data` with empty string value SHOULD be recorded as + the `k8s.namespace.label.data` attribute with value `""`. +""" + +K8S_NAMESPACE_NAME: Final = "k8s.namespace.name" +""" +The name of the namespace that the pod is running in. +""" + +K8S_NODE_ANNOTATION_TEMPLATE: Final = "k8s.node.annotation" +""" +The annotation placed on the Node, the `` being the annotation name, the value being the annotation value, even if the value is empty. +Note: Examples: + +- An annotation `node.alpha.kubernetes.io/ttl` with value `0` SHOULD be recorded as + the `k8s.node.annotation.node.alpha.kubernetes.io/ttl` attribute with value `"0"`. +- An annotation `data` with empty string value SHOULD be recorded as + the `k8s.node.annotation.data` attribute with value `""`. +""" + +K8S_NODE_LABEL_TEMPLATE: Final = "k8s.node.label" +""" +The label placed on the Node, the `` being the label name, the value being the label value, even if the value is empty. +Note: Examples: + +- A label `kubernetes.io/arch` with value `arm64` SHOULD be recorded + as the `k8s.node.label.kubernetes.io/arch` attribute with value `"arm64"`. +- A label `data` with empty string value SHOULD be recorded as + the `k8s.node.label.data` attribute with value `""`. +""" + +K8S_NODE_NAME: Final = "k8s.node.name" +""" +The name of the Node. +""" + +K8S_NODE_UID: Final = "k8s.node.uid" +""" +The UID of the Node. +""" + +K8S_POD_ANNOTATION_TEMPLATE: Final = "k8s.pod.annotation" +""" +The annotation placed on the Pod, the `` being the annotation name, the value being the annotation value. +Note: Examples: + +- An annotation `kubernetes.io/enforce-mountable-secrets` with value `true` SHOULD be recorded as + the `k8s.pod.annotation.kubernetes.io/enforce-mountable-secrets` attribute with value `"true"`. +- An annotation `mycompany.io/arch` with value `x64` SHOULD be recorded as + the `k8s.pod.annotation.mycompany.io/arch` attribute with value `"x64"`. +- An annotation `data` with empty string value SHOULD be recorded as + the `k8s.pod.annotation.data` attribute with value `""`. +""" + +K8S_POD_HOSTNAME: Final = "k8s.pod.hostname" +""" +Specifies the hostname of the Pod. +Note: The K8s Pod spec has an optional hostname field, which can be used to specify a hostname. +Refer to [K8s docs](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-hostname-and-subdomain-field) +for more information about this field. + +This attribute aligns with the `hostname` field of the +[K8s PodSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#podspec-v1-core). +""" + +K8S_POD_IP: Final = "k8s.pod.ip" +""" +IP address allocated to the Pod. +Note: This attribute aligns with the `podIP` field of the +[K8s PodStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#podstatus-v1-core). +""" + +K8S_POD_LABEL_TEMPLATE: Final = "k8s.pod.label" +""" +The label placed on the Pod, the `` being the label name, the value being the label value. +Note: Examples: + +- A label `app` with value `my-app` SHOULD be recorded as + the `k8s.pod.label.app` attribute with value `"my-app"`. +- A label `mycompany.io/arch` with value `x64` SHOULD be recorded as + the `k8s.pod.label.mycompany.io/arch` attribute with value `"x64"`. +- A label `data` with empty string value SHOULD be recorded as + the `k8s.pod.label.data` attribute with value `""`. +""" + +K8S_POD_NAME: Final = "k8s.pod.name" +""" +The name of the Pod. +""" + +K8S_POD_START_TIME: Final = "k8s.pod.start_time" +""" +The start timestamp of the Pod. +Note: Date and time at which the object was acknowledged by the Kubelet. +This is before the Kubelet pulled the container image(s) for the pod. + +This attribute aligns with the `startTime` field of the +[K8s PodStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#podstatus-v1-core), +in ISO 8601 (RFC 3339 compatible) format. +""" + +K8S_POD_UID: Final = "k8s.pod.uid" +""" +The UID of the Pod. +""" + +K8S_REPLICASET_ANNOTATION_TEMPLATE: Final = "k8s.replicaset.annotation" +""" +The annotation placed on the ReplicaSet, the `` being the annotation name, the value being the annotation value, even if the value is empty. +Note: Examples: + +- An annotation `replicas` with value `0` SHOULD be recorded + as the `k8s.replicaset.annotation.replicas` attribute with value `"0"`. +- An annotation `data` with empty string value SHOULD be recorded as + the `k8s.replicaset.annotation.data` attribute with value `""`. +""" + +K8S_REPLICASET_LABEL_TEMPLATE: Final = "k8s.replicaset.label" +""" +The label placed on the ReplicaSet, the `` being the label name, the value being the label value, even if the value is empty. +Note: Examples: + +- A label `app` with value `guestbook` SHOULD be recorded + as the `k8s.replicaset.label.app` attribute with value `"guestbook"`. +- A label `injected` with empty string value SHOULD be recorded as + the `k8s.replicaset.label.injected` attribute with value `""`. +""" + +K8S_REPLICASET_NAME: Final = "k8s.replicaset.name" +""" +The name of the ReplicaSet. +""" + +K8S_REPLICASET_UID: Final = "k8s.replicaset.uid" +""" +The UID of the ReplicaSet. +""" + +K8S_STATEFULSET_ANNOTATION_TEMPLATE: Final = "k8s.statefulset.annotation" +""" +The annotation placed on the StatefulSet, the `` being the annotation name, the value being the annotation value, even if the value is empty. +Note: Examples: + +- An annotation `replicas` with value `1` SHOULD be recorded + as the `k8s.statefulset.annotation.replicas` attribute with value `"1"`. +- An annotation `data` with empty string value SHOULD be recorded as + the `k8s.statefulset.annotation.data` attribute with value `""`. +""" + +K8S_STATEFULSET_LABEL_TEMPLATE: Final = "k8s.statefulset.label" +""" +The label placed on the StatefulSet, the `` being the label name, the value being the label value, even if the value is empty. +Note: Examples: + +- A label `app` with value `guestbook` SHOULD be recorded + as the `k8s.statefulset.label.app` attribute with value `"guestbook"`. +- A label `injected` with empty string value SHOULD be recorded as + the `k8s.statefulset.label.injected` attribute with value `""`. +""" + +K8S_STATEFULSET_NAME: Final = "k8s.statefulset.name" +""" +The name of the StatefulSet. +""" + +K8S_STATEFULSET_UID: Final = "k8s.statefulset.uid" +""" +The UID of the StatefulSet. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/network_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/network_attributes.py new file mode 100644 index 0000000..7f4cd4a --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/network_attributes.py @@ -0,0 +1,73 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +NETWORK_LOCAL_ADDRESS: Final = "network.local.address" +""" +Local address of the network connection - IP address or UNIX domain socket name. +""" + +NETWORK_LOCAL_PORT: Final = "network.local.port" +""" +Local port number of the network connection. +""" + +NETWORK_PEER_ADDRESS: Final = "network.peer.address" +""" +Peer address of the network connection - IP address or UNIX domain socket name. +""" + +NETWORK_PEER_PORT: Final = "network.peer.port" +""" +Peer port number of the network connection. +""" + +NETWORK_PROTOCOL_NAME: Final = "network.protocol.name" +""" +[OSI application layer](https://wikipedia.org/wiki/Application_layer) or non-OSI equivalent. +Note: The value SHOULD be normalized to lowercase. +""" + +NETWORK_PROTOCOL_VERSION: Final = "network.protocol.version" +""" +The actual version of the protocol used for network communication. +Note: If protocol version is subject to negotiation (for example using [ALPN](https://www.rfc-editor.org/rfc/rfc7301.html)), this attribute SHOULD be set to the negotiated version. If the actual protocol version is not known, this attribute SHOULD NOT be set. +""" + +NETWORK_TRANSPORT: Final = "network.transport" +""" +[OSI transport layer](https://wikipedia.org/wiki/Transport_layer) or [inter-process communication method](https://wikipedia.org/wiki/Inter-process_communication). +Note: The value SHOULD be normalized to lowercase. + +Consider always setting the transport when setting a port number, since +a port number is ambiguous without knowing the transport. For example +different processes could be listening on TCP port 12345 and UDP port 12345. +""" + +NETWORK_TYPE: Final = "network.type" +""" +[OSI network layer](https://wikipedia.org/wiki/Network_layer) or non-OSI equivalent. +Note: The value SHOULD be normalized to lowercase. +""" + + +class NetworkTransportValues(Enum): + TCP = "tcp" + """TCP.""" + UDP = "udp" + """UDP.""" + PIPE = "pipe" + """Named or anonymous pipe.""" + UNIX = "unix" + """UNIX domain socket.""" + QUIC = "quic" + """QUIC.""" + + +class NetworkTypeValues(Enum): + IPV4 = "ipv4" + """IPv4.""" + IPV6 = "ipv6" + """IPv6.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/otel_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/otel_attributes.py new file mode 100644 index 0000000..3bb5747 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/otel_attributes.py @@ -0,0 +1,38 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +OTEL_EVENT_NAME: Final = "otel.event.name" +""" +Identifies the class / type of event. +Note: This attribute SHOULD be used by non-OTLP exporters when destination does not support `EventName` or equivalent field. This attribute MAY be used by applications using existing logging libraries so that it can be used to set the `EventName` field by Collector or SDK components. +""" + +OTEL_SCOPE_NAME: Final = "otel.scope.name" +""" +The name of the instrumentation scope - (`InstrumentationScope.Name` in OTLP). +""" + +OTEL_SCOPE_VERSION: Final = "otel.scope.version" +""" +The version of the instrumentation scope - (`InstrumentationScope.Version` in OTLP). +""" + +OTEL_STATUS_CODE: Final = "otel.status_code" +""" +Name of the code, either "OK" or "ERROR". MUST NOT be set if the status code is UNSET. +""" + +OTEL_STATUS_DESCRIPTION: Final = "otel.status_description" +""" +Description of the Status if it has a value, otherwise not set. +""" + + +class OtelStatusCodeValues(Enum): + OK = "OK" + """The operation has been validated by an Application developer or Operator to have completed successfully.""" + ERROR = "ERROR" + """The operation contains an error.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/server_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/server_attributes.py new file mode 100644 index 0000000..1da2920 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/server_attributes.py @@ -0,0 +1,16 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +SERVER_ADDRESS: Final = "server.address" +""" +Server domain name if available without reverse DNS lookup; otherwise, IP address or UNIX domain socket name. +Note: When observed from the client side, and when communicating through an intermediary, `server.address` SHOULD represent the server address behind any intermediaries, for example proxies, if it's available. +""" + +SERVER_PORT: Final = "server.port" +""" +Server port number. +Note: When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD represent the server port behind any intermediaries, for example proxies, if it's available. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/service_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/service_attributes.py new file mode 100644 index 0000000..1ee79b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/service_attributes.py @@ -0,0 +1,53 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +SERVICE_INSTANCE_ID: Final = "service.instance.id" +""" +The string ID of the service instance. +Note: MUST be unique for each instance of the same `service.namespace,service.name` pair (in other words +`service.namespace,service.name,service.instance.id` triplet MUST be globally unique). The ID helps to +distinguish instances of the same service that exist at the same time (e.g. instances of a horizontally scaled +service). + +Implementations, such as SDKs, are recommended to generate a random Version 1 or Version 4 [RFC +4122](https://www.ietf.org/rfc/rfc4122.txt) UUID, but are free to use an inherent unique ID as the source of +this value if stability is desirable. In that case, the ID SHOULD be used as source of a UUID Version 5 and +SHOULD use the following UUID as the namespace: `4d63009a-8d0f-11ee-aad7-4c796ed8e320`. + +UUIDs are typically recommended, as only an opaque value for the purposes of identifying a service instance is +needed. Similar to what can be seen in the man page for the +[`/etc/machine-id`](https://www.freedesktop.org/software/systemd/man/latest/machine-id.html) file, the underlying +data, such as pod name and namespace should be treated as confidential, being the user's choice to expose it +or not via another resource attribute. + +For applications running behind an application server (like unicorn), we do not recommend using one identifier +for all processes participating in the application. Instead, it's recommended each division (e.g. a worker +thread in unicorn) to have its own instance.id. + +It's not recommended for a Collector to set `service.instance.id` if it can't unambiguously determine the +service instance that is generating that telemetry. For instance, creating an UUID based on `pod.name` will +likely be wrong, as the Collector might not know from which container within that pod the telemetry originated. +However, Collectors can set the `service.instance.id` if they can unambiguously determine the service instance +for that telemetry. This is typically the case for scraping receivers, as they know the target address and +port. +""" + +SERVICE_NAME: Final = "service.name" +""" +Logical name of the service. +Note: MUST be the same for all instances of horizontally scaled services. If the value was not specified, SDKs MUST fallback to `unknown_service:` concatenated with the process executable name, e.g. `unknown_service:bash`. If the process executable name is not available, the value MUST be set to `unknown_service`. +The process executable name is the name of the process executable, the same value as described by the [`process.executable.name`](process.md) resource attribute. +""" + +SERVICE_NAMESPACE: Final = "service.namespace" +""" +A namespace for `service.name`. +Note: A string value having a meaning that helps to distinguish a group of services, for example the team name that owns a group of services. `service.name` is expected to be unique within the same namespace. If `service.namespace` is not specified in the Resource then `service.name` is expected to be unique for all services that have no explicit namespace defined (so the empty/unspecified namespace is simply one more valid namespace). Zero-length namespace string is assumed equal to unspecified namespace. +""" + +SERVICE_VERSION: Final = "service.version" +""" +The version string of the service component. The format is not defined by these conventions. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/telemetry_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/telemetry_attributes.py new file mode 100644 index 0000000..4d390c4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/telemetry_attributes.py @@ -0,0 +1,67 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Final + +TELEMETRY_DISTRO_NAME: Final = "telemetry.distro.name" +""" +The name of the auto instrumentation agent or distribution, if used. +Note: Official auto instrumentation agents and distributions SHOULD set the `telemetry.distro.name` attribute to +a string starting with `opentelemetry-`, e.g. `opentelemetry-java-instrumentation`. +""" + +TELEMETRY_DISTRO_VERSION: Final = "telemetry.distro.version" +""" +The version string of the auto instrumentation agent or distribution, if used. +""" + +TELEMETRY_SDK_LANGUAGE: Final = "telemetry.sdk.language" +""" +The language of the telemetry SDK. +""" + +TELEMETRY_SDK_NAME: Final = "telemetry.sdk.name" +""" +The name of the telemetry SDK as defined above. +Note: The OpenTelemetry SDK MUST set the `telemetry.sdk.name` attribute to `opentelemetry`. +If another SDK, like a fork or a vendor-provided implementation, is used, this SDK MUST set the +`telemetry.sdk.name` attribute to the fully-qualified class or module name of this SDK's main entry point +or another suitable identifier depending on the language. +The identifier `opentelemetry` is reserved and MUST NOT be used in this case. +All custom identifiers SHOULD be stable across different versions of an implementation. +""" + +TELEMETRY_SDK_VERSION: Final = "telemetry.sdk.version" +""" +The version string of the telemetry SDK. +""" + + +class TelemetrySdkLanguageValues(Enum): + CPP = "cpp" + """[C++](https://opentelemetry.io/docs/languages/cpp/).""" + DOTNET = "dotnet" + """[.NET](https://opentelemetry.io/docs/languages/dotnet/).""" + ERLANG = "erlang" + """[Erlang/Elixir](https://opentelemetry.io/docs/languages/erlang/).""" + GO = "go" + """[Go](https://opentelemetry.io/docs/languages/go/).""" + JAVA = "java" + """[Java](https://opentelemetry.io/docs/languages/java/).""" + KOTLIN = "kotlin" + """[Kotlin](https://opentelemetry.io/docs/languages/kotlin/).""" + NODEJS = "nodejs" + """[Node.js](https://opentelemetry.io/docs/languages/js/).""" + PHP = "php" + """[PHP](https://opentelemetry.io/docs/languages/php/).""" + PYTHON = "python" + """[Python](https://opentelemetry.io/docs/languages/python/).""" + RUBY = "ruby" + """[Ruby](https://opentelemetry.io/docs/languages/ruby/).""" + RUST = "rust" + """[Rust](https://opentelemetry.io/docs/languages/rust/).""" + SWIFT = "swift" + """[Swift](https://opentelemetry.io/docs/languages/swift/).""" + WEBJS = "webjs" + """[Browser](https://opentelemetry.io/docs/languages/js/).""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/url_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/url_attributes.py new file mode 100644 index 0000000..920de47 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/url_attributes.py @@ -0,0 +1,88 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +URL_FRAGMENT: Final = "url.fragment" +""" +The [URI fragment](https://www.rfc-editor.org/rfc/rfc3986#section-3.5) component. +""" + +URL_FULL: Final = "url.full" +""" +Absolute URL describing a network resource according to [RFC3986](https://www.rfc-editor.org/rfc/rfc3986). +Note: For network calls, URL usually has `scheme://host[:port][path][?query][#fragment]` format, where the fragment +is not transmitted over HTTP, but if it is known, it SHOULD be included nevertheless. + +`url.full` MUST NOT contain credentials passed via URL in form of `https://username:password@www.example.com/`. +In such case username and password SHOULD be redacted and attribute's value SHOULD be `https://REDACTED:REDACTED@www.example.com/`. + +`url.full` SHOULD capture the absolute URL when it is available (or can be reconstructed). + +Sensitive content provided in `url.full` SHOULD be scrubbed when instrumentations can identify it. + +![Development](https://img.shields.io/badge/-development-blue) +Query string values for the following keys SHOULD be redacted by default and replaced by the +value `REDACTED`: + +* [`X-Amz-Signature`](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-authentication-methods.html) +* [`X-Amz-Credential`](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-authentication-methods.html) +* [`X-Amz-Security-Token`](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-authentication-methods.html) +* [`sig`](https://learn.microsoft.com/azure/storage/common/storage-sas-overview#sas-token) +* [`X-Goog-Signature`](https://cloud.google.com/storage/docs/access-control/signed-urls) + +This list is subject to change over time. + +Matching of query parameter keys against the sensitive list SHOULD be case-sensitive. + +![Development](https://img.shields.io/badge/-development-blue) +Instrumentation MAY provide a way to override this list via declarative configuration. +If so, it SHOULD use the `sensitive_query_parameters` property +(an array of case-sensitive strings with minimum items 0) under +`.instrumentation/development.general.sanitization.url`. +This list is a full override of the default sensitive query parameter keys, +it is not a list of keys in addition to the defaults. + +When a query string value is redacted, the query string key SHOULD still be preserved, e.g. +`https://www.example.com/path?color=blue&sig=REDACTED`. +""" + +URL_PATH: Final = "url.path" +""" +The [URI path](https://www.rfc-editor.org/rfc/rfc3986#section-3.3) component. +Note: Sensitive content provided in `url.path` SHOULD be scrubbed when instrumentations can identify it. +""" + +URL_QUERY: Final = "url.query" +""" +The [URI query](https://www.rfc-editor.org/rfc/rfc3986#section-3.4) component. +Note: Sensitive content provided in `url.query` SHOULD be scrubbed when instrumentations can identify it. + +![Development](https://img.shields.io/badge/-development-blue) +Query string values for the following keys SHOULD be redacted by default and replaced by the value `REDACTED`: + +* [`X-Amz-Signature`](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-authentication-methods.html) +* [`X-Amz-Credential`](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-authentication-methods.html) +* [`X-Amz-Security-Token`](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-authentication-methods.html) +* [`sig`](https://learn.microsoft.com/azure/storage/common/storage-sas-overview#sas-token) +* [`X-Goog-Signature`](https://cloud.google.com/storage/docs/access-control/signed-urls) + +This list is subject to change over time. + +Matching of query parameter keys against the sensitive list SHOULD be case-sensitive. + +Instrumentation MAY provide a way to override this list via declarative configuration. +If so, it SHOULD use the `sensitive_query_parameters` property +(an array of case-sensitive strings with minimum items 0) under +`.instrumentation/development.general.sanitization.url`. +This list is a full override of the default sensitive query parameter keys, +it is not a list of keys in addition to the defaults. + +When a query string value is redacted, the query string key SHOULD still be preserved, e.g. +`q=OpenTelemetry&sig=REDACTED`. +""" + +URL_SCHEME: Final = "url.scheme" +""" +The [URI scheme](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) component identifying the used protocol. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/user_agent_attributes.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/user_agent_attributes.py new file mode 100644 index 0000000..6d7e44f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/attributes/user_agent_attributes.py @@ -0,0 +1,9 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing import Final + +USER_AGENT_ORIGINAL: Final = "user_agent.original" +""" +Value of the [HTTP User-Agent](https://www.rfc-editor.org/rfc/rfc9110.html#field.user-agent) header sent by the client. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/__init__.py new file mode 100644 index 0000000..fce5f37 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/__init__.py @@ -0,0 +1,191 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from typing_extensions import deprecated + + +@deprecated( + "Use metrics defined in the :py:const:`opentelemetry.semconv.metrics` and :py:const:`opentelemetry.semconv._incubating.metrics` modules instead. Deprecated since version 1.25.0.", +) +class MetricInstruments: + SCHEMA_URL = "https://opentelemetry.io/schemas/1.21.0" + """ + The URL of the OpenTelemetry schema for these keys and values. + """ + + HTTP_SERVER_DURATION = "http.server.duration" + """ + Measures the duration of inbound HTTP requests + Instrument: histogram + Unit: s + """ + + HTTP_SERVER_ACTIVE_REQUESTS = "http.server.active_requests" + """ + Measures the number of concurrent HTTP requests that are currently in-flight + Instrument: updowncounter + Unit: {request} + """ + + HTTP_SERVER_REQUEST_SIZE = "http.server.request.size" + """ + Measures the size of HTTP request messages (compressed) + Instrument: histogram + Unit: By + """ + + HTTP_SERVER_RESPONSE_SIZE = "http.server.response.size" + """ + Measures the size of HTTP response messages (compressed) + Instrument: histogram + Unit: By + """ + + HTTP_CLIENT_DURATION = "http.client.duration" + """ + Measures the duration of outbound HTTP requests + Instrument: histogram + Unit: s + """ + + HTTP_CLIENT_REQUEST_SIZE = "http.client.request.size" + """ + Measures the size of HTTP request messages (compressed) + Instrument: histogram + Unit: By + """ + + HTTP_CLIENT_RESPONSE_SIZE = "http.client.response.size" + """ + Measures the size of HTTP response messages (compressed) + Instrument: histogram + Unit: By + """ + + PROCESS_RUNTIME_JVM_MEMORY_INIT = "process.runtime.jvm.memory.init" + """ + Measure of initial memory requested + Instrument: updowncounter + Unit: By + """ + + PROCESS_RUNTIME_JVM_SYSTEM_CPU_UTILIZATION = "process.runtime.jvm.system.cpu.utilization" + """ + Recent CPU utilization for the whole system as reported by the JVM + Instrument: gauge + Unit: 1 + """ + + PROCESS_RUNTIME_JVM_SYSTEM_CPU_LOAD_1M = "process.runtime.jvm.system.cpu.load_1m" + """ + Average CPU load of the whole system for the last minute as reported by the JVM + Instrument: gauge + Unit: 1 + """ + + PROCESS_RUNTIME_JVM_BUFFER_USAGE = "process.runtime.jvm.buffer.usage" + """ + Measure of memory used by buffers + Instrument: updowncounter + Unit: By + """ + + PROCESS_RUNTIME_JVM_BUFFER_LIMIT = "process.runtime.jvm.buffer.limit" + """ + Measure of total memory capacity of buffers + Instrument: updowncounter + Unit: By + """ + + PROCESS_RUNTIME_JVM_BUFFER_COUNT = "process.runtime.jvm.buffer.count" + """ + Number of buffers in the pool + Instrument: updowncounter + Unit: {buffer} + """ + + PROCESS_RUNTIME_JVM_MEMORY_USAGE = "process.runtime.jvm.memory.usage" + """ + Measure of memory used + Instrument: updowncounter + Unit: By + """ + + PROCESS_RUNTIME_JVM_MEMORY_COMMITTED = "process.runtime.jvm.memory.committed" + """ + Measure of memory committed + Instrument: updowncounter + Unit: By + """ + + PROCESS_RUNTIME_JVM_MEMORY_LIMIT = "process.runtime.jvm.memory.limit" + """ + Measure of max obtainable memory + Instrument: updowncounter + Unit: By + """ + + PROCESS_RUNTIME_JVM_MEMORY_USAGE_AFTER_LAST_GC = "process.runtime.jvm.memory.usage_after_last_gc" + """ + Measure of memory used, as measured after the most recent garbage collection event on this pool + Instrument: updowncounter + Unit: By + """ + + PROCESS_RUNTIME_JVM_GC_DURATION = "process.runtime.jvm.gc.duration" + """ + Duration of JVM garbage collection actions + Instrument: histogram + Unit: s + """ + + PROCESS_RUNTIME_JVM_THREADS_COUNT = "process.runtime.jvm.threads.count" + """ + Number of executing platform threads + Instrument: updowncounter + Unit: {thread} + """ + + PROCESS_RUNTIME_JVM_CLASSES_LOADED = "process.runtime.jvm.classes.loaded" + """ + Number of classes loaded since JVM start + Instrument: counter + Unit: {class} + """ + + PROCESS_RUNTIME_JVM_CLASSES_UNLOADED = "process.runtime.jvm.classes.unloaded" + """ + Number of classes unloaded since JVM start + Instrument: counter + Unit: {class} + """ + + PROCESS_RUNTIME_JVM_CLASSES_CURRENT_LOADED = "process.runtime.jvm.classes.current_loaded" + """ + Number of classes currently loaded + Instrument: updowncounter + Unit: {class} + """ + + PROCESS_RUNTIME_JVM_CPU_TIME = "process.runtime.jvm.cpu.time" + """ + CPU time used by the process as reported by the JVM + Instrument: counter + Unit: s + """ + + PROCESS_RUNTIME_JVM_CPU_RECENT_UTILIZATION = "process.runtime.jvm.cpu.recent_utilization" + """ + Recent CPU utilization for the process as reported by the JVM + Instrument: gauge + Unit: 1 + """ + + # Manually defined metrics + + DB_CLIENT_CONNECTIONS_USAGE = "db.client.connections.usage" + """ + The number of connections that are currently in state described by the `state` attribute + Instrument: UpDownCounter + Unit: {connection} + """ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2eadf5e Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/__pycache__/db_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/__pycache__/db_metrics.cpython-312.pyc new file mode 100644 index 0000000..82b540f Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/__pycache__/db_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/__pycache__/http_metrics.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/__pycache__/http_metrics.cpython-312.pyc new file mode 100644 index 0000000..8a71989 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/__pycache__/http_metrics.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/db_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/db_metrics.py new file mode 100644 index 0000000..6031728 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/db_metrics.py @@ -0,0 +1,13 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +DB_CLIENT_OPERATION_DURATION: Final = "db.client.operation.duration" +""" +Duration of database client operations +Instrument: histogram +Unit: s +Note: Batch operations SHOULD be recorded as a single operation. +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/http_metrics.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/http_metrics.py new file mode 100644 index 0000000..924d41d --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/metrics/http_metrics.py @@ -0,0 +1,20 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +from typing import Final + +HTTP_CLIENT_REQUEST_DURATION: Final = "http.client.request.duration" +""" +Duration of HTTP client requests +Instrument: histogram +Unit: s +""" + + +HTTP_SERVER_REQUEST_DURATION: Final = "http.server.request.duration" +""" +Duration of HTTP server requests +Instrument: histogram +Unit: s +""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/semconv/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/resource/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/resource/__init__.py new file mode 100644 index 0000000..59c2fd5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/resource/__init__.py @@ -0,0 +1,875 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# pylint: disable=too-many-lines + +from enum import Enum + +from typing_extensions import deprecated + + +@deprecated( + "Use attributes defined in the :py:const:`opentelemetry.semconv.attributes` and :py:const:`opentelemetry.semconv._incubating.attributes` modules instead. Deprecated since version 1.25.0.", +) +class ResourceAttributes: + SCHEMA_URL = "https://opentelemetry.io/schemas/1.21.0" + """ + The URL of the OpenTelemetry schema for these keys and values. + """ + BROWSER_BRANDS = "browser.brands" + """ + Array of brand name and version separated by a space. + Note: This value is intended to be taken from the [UA client hints API](https://wicg.github.io/ua-client-hints/#interface) (`navigator.userAgentData.brands`). + """ + + BROWSER_PLATFORM = "browser.platform" + """ + The platform on which the browser is running. + Note: This value is intended to be taken from the [UA client hints API](https://wicg.github.io/ua-client-hints/#interface) (`navigator.userAgentData.platform`). If unavailable, the legacy `navigator.platform` API SHOULD NOT be used instead and this attribute SHOULD be left unset in order for the values to be consistent. + The list of possible values is defined in the [W3C User-Agent Client Hints specification](https://wicg.github.io/ua-client-hints/#sec-ch-ua-platform). Note that some (but not all) of these values can overlap with values in the [`os.type` and `os.name` attributes](./os.md). However, for consistency, the values in the `browser.platform` attribute should capture the exact value that the user agent provides. + """ + + BROWSER_MOBILE = "browser.mobile" + """ + A boolean that is true if the browser is running on a mobile device. + Note: This value is intended to be taken from the [UA client hints API](https://wicg.github.io/ua-client-hints/#interface) (`navigator.userAgentData.mobile`). If unavailable, this attribute SHOULD be left unset. + """ + + BROWSER_LANGUAGE = "browser.language" + """ + Preferred language of the user using the browser. + Note: This value is intended to be taken from the Navigator API `navigator.language`. + """ + + USER_AGENT_ORIGINAL = "user_agent.original" + """ + Full user-agent string provided by the browser. + Note: The user-agent value SHOULD be provided only from browsers that do not have a mechanism to retrieve brands and platform individually from the User-Agent Client Hints API. To retrieve the value, the legacy `navigator.userAgent` API can be used. + """ + + CLOUD_PROVIDER = "cloud.provider" + """ + Name of the cloud provider. + """ + + CLOUD_ACCOUNT_ID = "cloud.account.id" + """ + The cloud account ID the resource is assigned to. + """ + + CLOUD_REGION = "cloud.region" + """ + The geographical region the resource is running. + Note: Refer to your provider's docs to see the available regions, for example [Alibaba Cloud regions](https://www.alibabacloud.com/help/doc-detail/40654.htm), [AWS regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/), [Azure regions](https://azure.microsoft.com/en-us/global-infrastructure/geographies/), [Google Cloud regions](https://cloud.google.com/about/locations), or [Tencent Cloud regions](https://www.tencentcloud.com/document/product/213/6091). + """ + + CLOUD_RESOURCE_ID = "cloud.resource_id" + """ + Cloud provider-specific native identifier of the monitored cloud resource (e.g. an [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) on AWS, a [fully qualified resource ID](https://learn.microsoft.com/en-us/rest/api/resources/resources/get-by-id) on Azure, a [full resource name](https://cloud.google.com/apis/design/resource_names#full_resource_name) on GCP). + Note: On some cloud providers, it may not be possible to determine the full ID at startup, + so it may be necessary to set `cloud.resource_id` as a span attribute instead. + + The exact value to use for `cloud.resource_id` depends on the cloud provider. + The following well-known definitions MUST be used if you set this attribute and they apply: + + * **AWS Lambda:** The function [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html). + Take care not to use the "invoked ARN" directly but replace any + [alias suffix](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) + with the resolved function version, as the same runtime instance may be invokable with + multiple different aliases. + * **GCP:** The [URI of the resource](https://cloud.google.com/iam/docs/full-resource-names) + * **Azure:** The [Fully Qualified Resource ID](https://docs.microsoft.com/en-us/rest/api/resources/resources/get-by-id) of the invoked function, + *not* the function app, having the form + `/subscriptions//resourceGroups//providers/Microsoft.Web/sites//functions/`. + This means that a span attribute MUST be used, as an Azure function app can host multiple functions that would usually share + a TracerProvider. + """ + + CLOUD_AVAILABILITY_ZONE = "cloud.availability_zone" + """ + Cloud regions often have multiple, isolated locations known as zones to increase availability. Availability zone represents the zone where the resource is running. + Note: Availability zones are called "zones" on Alibaba Cloud and Google Cloud. + """ + + CLOUD_PLATFORM = "cloud.platform" + """ + The cloud platform in use. + Note: The prefix of the service SHOULD match the one specified in `cloud.provider`. + """ + + AWS_ECS_CONTAINER_ARN = "aws.ecs.container.arn" + """ + The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). + """ + + AWS_ECS_CLUSTER_ARN = "aws.ecs.cluster.arn" + """ + The ARN of an [ECS cluster](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html). + """ + + AWS_ECS_LAUNCHTYPE = "aws.ecs.launchtype" + """ + The [launch type](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/launch_types.html) for an ECS task. + """ + + AWS_ECS_TASK_ARN = "aws.ecs.task.arn" + """ + The ARN of an [ECS task definition](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html). + """ + + AWS_ECS_TASK_FAMILY = "aws.ecs.task.family" + """ + The task definition family this task definition is a member of. + """ + + AWS_ECS_TASK_REVISION = "aws.ecs.task.revision" + """ + The revision for this task definition. + """ + + AWS_EKS_CLUSTER_ARN = "aws.eks.cluster.arn" + """ + The ARN of an EKS cluster. + """ + + AWS_LOG_GROUP_NAMES = "aws.log.group.names" + """ + The name(s) of the AWS log group(s) an application is writing to. + Note: Multiple log groups must be supported for cases like multi-container applications, where a single application has sidecar containers, and each write to their own log group. + """ + + AWS_LOG_GROUP_ARNS = "aws.log.group.arns" + """ + The Amazon Resource Name(s) (ARN) of the AWS log group(s). + Note: See the [log group ARN format documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). + """ + + AWS_LOG_STREAM_NAMES = "aws.log.stream.names" + """ + The name(s) of the AWS log stream(s) an application is writing to. + """ + + AWS_LOG_STREAM_ARNS = "aws.log.stream.arns" + """ + The ARN(s) of the AWS log stream(s). + Note: See the [log stream ARN format documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). One log group can contain several log streams, so these ARNs necessarily identify both a log group and a log stream. + """ + + GCP_CLOUD_RUN_JOB_EXECUTION = "gcp.cloud_run.job.execution" + """ + The name of the Cloud Run [execution](https://cloud.google.com/run/docs/managing/job-executions) being run for the Job, as set by the [`CLOUD_RUN_EXECUTION`](https://cloud.google.com/run/docs/container-contract#jobs-env-vars) environment variable. + """ + + GCP_CLOUD_RUN_JOB_TASK_INDEX = "gcp.cloud_run.job.task_index" + """ + The index for a task within an execution as provided by the [`CLOUD_RUN_TASK_INDEX`](https://cloud.google.com/run/docs/container-contract#jobs-env-vars) environment variable. + """ + + GCP_GCE_INSTANCE_NAME = "gcp.gce.instance.name" + """ + The instance name of a GCE instance. This is the value provided by `host.name`, the visible name of the instance in the Cloud Console UI, and the prefix for the default hostname of the instance as defined by the [default internal DNS name](https://cloud.google.com/compute/docs/internal-dns#instance-fully-qualified-domain-names). + """ + + GCP_GCE_INSTANCE_HOSTNAME = "gcp.gce.instance.hostname" + """ + The hostname of a GCE instance. This is the full value of the default or [custom hostname](https://cloud.google.com/compute/docs/instances/custom-hostname-vm). + """ + + HEROKU_RELEASE_CREATION_TIMESTAMP = "heroku.release.creation_timestamp" + """ + Time and date the release was created. + """ + + HEROKU_RELEASE_COMMIT = "heroku.release.commit" + """ + Commit hash for the current release. + """ + + HEROKU_APP_ID = "heroku.app.id" + """ + Unique identifier for the application. + """ + + CONTAINER_NAME = "container.name" + """ + Container name used by container runtime. + """ + + CONTAINER_ID = "container.id" + """ + Container ID. Usually a UUID, as for example used to [identify Docker containers](https://docs.docker.com/engine/reference/run/#container-identification). The UUID might be abbreviated. + """ + + CONTAINER_RUNTIME = "container.runtime" + """ + The container runtime managing this container. + """ + + CONTAINER_IMAGE_NAME = "container.image.name" + """ + Name of the image the container was built on. + """ + + CONTAINER_IMAGE_TAG = "container.image.tag" + """ + Container image tag. + """ + + CONTAINER_IMAGE_ID = "container.image.id" + """ + Runtime specific image identifier. Usually a hash algorithm followed by a UUID. + Note: Docker defines a sha256 of the image id; `container.image.id` corresponds to the `Image` field from the Docker container inspect [API](https://docs.docker.com/engine/api/v1.43/#tag/Container/operation/ContainerInspect) endpoint. + K8s defines a link to the container registry repository with digest `"imageID": "registry.azurecr.io /namespace/service/dockerfile@sha256:bdeabd40c3a8a492eaf9e8e44d0ebbb84bac7ee25ac0cf8a7159d25f62555625"`. + OCI defines a digest of manifest. + """ + + CONTAINER_COMMAND = "container.command" + """ + The command used to run the container (i.e. the command name). + Note: If using embedded credentials or sensitive data, it is recommended to remove them to prevent potential leakage. + """ + + CONTAINER_COMMAND_LINE = "container.command_line" + """ + The full command run by the container as a single string representing the full command. [2]. + """ + + CONTAINER_COMMAND_ARGS = "container.command_args" + """ + All the command arguments (including the command/executable itself) run by the container. [2]. + """ + + DEPLOYMENT_ENVIRONMENT = "deployment.environment" + """ + Name of the [deployment environment](https://en.wikipedia.org/wiki/Deployment_environment) (aka deployment tier). + """ + + DEVICE_ID = "device.id" + """ + A unique identifier representing the device. + Note: The device identifier MUST only be defined using the values outlined below. This value is not an advertising identifier and MUST NOT be used as such. On iOS (Swift or Objective-C), this value MUST be equal to the [vendor identifier](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor). On Android (Java or Kotlin), this value MUST be equal to the Firebase Installation ID or a globally unique UUID which is persisted across sessions in your application. More information can be found [here](https://developer.android.com/training/articles/user-data-ids) on best practices and exact implementation details. Caution should be taken when storing personal data or anything which can identify a user. GDPR and data protection laws may apply, ensure you do your own due diligence. + """ + + DEVICE_MODEL_IDENTIFIER = "device.model.identifier" + """ + The model identifier for the device. + Note: It's recommended this value represents a machine readable version of the model identifier rather than the market or consumer-friendly name of the device. + """ + + DEVICE_MODEL_NAME = "device.model.name" + """ + The marketing name for the device model. + Note: It's recommended this value represents a human readable version of the device model rather than a machine readable alternative. + """ + + DEVICE_MANUFACTURER = "device.manufacturer" + """ + The name of the device manufacturer. + Note: The Android OS provides this field via [Build](https://developer.android.com/reference/android/os/Build#MANUFACTURER). iOS apps SHOULD hardcode the value `Apple`. + """ + + FAAS_NAME = "faas.name" + """ + The name of the single function that this runtime instance executes. + Note: This is the name of the function as configured/deployed on the FaaS + platform and is usually different from the name of the callback + function (which may be stored in the + [`code.namespace`/`code.function`](/docs/general/general-attributes.md#source-code-attributes) + span attributes). + + For some cloud providers, the above definition is ambiguous. The following + definition of function name MUST be used for this attribute + (and consequently the span name) for the listed cloud providers/products: + + * **Azure:** The full name `/`, i.e., function app name + followed by a forward slash followed by the function name (this form + can also be seen in the resource JSON for the function). + This means that a span attribute MUST be used, as an Azure function + app can host multiple functions that would usually share + a TracerProvider (see also the `cloud.resource_id` attribute). + """ + + FAAS_VERSION = "faas.version" + """ + The immutable version of the function being executed. + Note: Depending on the cloud provider and platform, use: + + * **AWS Lambda:** The [function version](https://docs.aws.amazon.com/lambda/latest/dg/configuration-versions.html) + (an integer represented as a decimal string). + * **Google Cloud Run (Services):** The [revision](https://cloud.google.com/run/docs/managing/revisions) + (i.e., the function name plus the revision suffix). + * **Google Cloud Functions:** The value of the + [`K_REVISION` environment variable](https://cloud.google.com/functions/docs/env-var#runtime_environment_variables_set_automatically). + * **Azure Functions:** Not applicable. Do not set this attribute. + """ + + FAAS_INSTANCE = "faas.instance" + """ + The execution environment ID as a string, that will be potentially reused for other invocations to the same function/function version. + Note: * **AWS Lambda:** Use the (full) log stream name. + """ + + FAAS_MAX_MEMORY = "faas.max_memory" + """ + The amount of memory available to the serverless function converted to Bytes. + Note: It's recommended to set this attribute since e.g. too little memory can easily stop a Java AWS Lambda function from working correctly. On AWS Lambda, the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this information (which must be multiplied by 1,048,576). + """ + + HOST_ID = "host.id" + """ + Unique host ID. For Cloud, this must be the instance_id assigned by the cloud provider. For non-containerized systems, this should be the `machine-id`. See the table below for the sources to use to determine the `machine-id` based on operating system. + """ + + HOST_NAME = "host.name" + """ + Name of the host. On Unix systems, it may contain what the hostname command returns, or the fully qualified hostname, or another name specified by the user. + """ + + HOST_TYPE = "host.type" + """ + Type of host. For Cloud, this must be the machine type. + """ + + HOST_ARCH = "host.arch" + """ + The CPU architecture the host system is running on. + """ + + HOST_IMAGE_NAME = "host.image.name" + """ + Name of the VM image or OS install the host was instantiated from. + """ + + HOST_IMAGE_ID = "host.image.id" + """ + VM image ID or host OS image ID. For Cloud, this value is from the provider. + """ + + HOST_IMAGE_VERSION = "host.image.version" + """ + The version string of the VM image or host OS as defined in [Version Attributes](README.md#version-attributes). + """ + + K8S_CLUSTER_NAME = "k8s.cluster.name" + """ + The name of the cluster. + """ + + K8S_CLUSTER_UID = "k8s.cluster.uid" + """ + A pseudo-ID for the cluster, set to the UID of the `kube-system` namespace. + Note: K8s does not have support for obtaining a cluster ID. If this is ever + added, we will recommend collecting the `k8s.cluster.uid` through the + official APIs. In the meantime, we are able to use the `uid` of the + `kube-system` namespace as a proxy for cluster ID. Read on for the + rationale. + + Every object created in a K8s cluster is assigned a distinct UID. The + `kube-system` namespace is used by Kubernetes itself and will exist + for the lifetime of the cluster. Using the `uid` of the `kube-system` + namespace is a reasonable proxy for the K8s ClusterID as it will only + change if the cluster is rebuilt. Furthermore, Kubernetes UIDs are + UUIDs as standardized by + [ISO/IEC 9834-8 and ITU-T X.667](https://www.itu.int/ITU-T/studygroups/com17/oid.html). + Which states: + + > If generated according to one of the mechanisms defined in Rec. + ITU-T X.667 | ISO/IEC 9834-8, a UUID is either guaranteed to be + different from all other UUIDs generated before 3603 A.D., or is + extremely likely to be different (depending on the mechanism chosen). + + Therefore, UIDs between clusters should be extremely unlikely to + conflict. + """ + + K8S_NODE_NAME = "k8s.node.name" + """ + The name of the Node. + """ + + K8S_NODE_UID = "k8s.node.uid" + """ + The UID of the Node. + """ + + K8S_NAMESPACE_NAME = "k8s.namespace.name" + """ + The name of the namespace that the pod is running in. + """ + + K8S_POD_UID = "k8s.pod.uid" + """ + The UID of the Pod. + """ + + K8S_POD_NAME = "k8s.pod.name" + """ + The name of the Pod. + """ + + K8S_CONTAINER_NAME = "k8s.container.name" + """ + The name of the Container from Pod specification, must be unique within a Pod. Container runtime usually uses different globally unique name (`container.name`). + """ + + K8S_CONTAINER_RESTART_COUNT = "k8s.container.restart_count" + """ + Number of times the container was restarted. This attribute can be used to identify a particular container (running or stopped) within a container spec. + """ + + K8S_REPLICASET_UID = "k8s.replicaset.uid" + """ + The UID of the ReplicaSet. + """ + + K8S_REPLICASET_NAME = "k8s.replicaset.name" + """ + The name of the ReplicaSet. + """ + + K8S_DEPLOYMENT_UID = "k8s.deployment.uid" + """ + The UID of the Deployment. + """ + + K8S_DEPLOYMENT_NAME = "k8s.deployment.name" + """ + The name of the Deployment. + """ + + K8S_STATEFULSET_UID = "k8s.statefulset.uid" + """ + The UID of the StatefulSet. + """ + + K8S_STATEFULSET_NAME = "k8s.statefulset.name" + """ + The name of the StatefulSet. + """ + + K8S_DAEMONSET_UID = "k8s.daemonset.uid" + """ + The UID of the DaemonSet. + """ + + K8S_DAEMONSET_NAME = "k8s.daemonset.name" + """ + The name of the DaemonSet. + """ + + K8S_JOB_UID = "k8s.job.uid" + """ + The UID of the Job. + """ + + K8S_JOB_NAME = "k8s.job.name" + """ + The name of the Job. + """ + + K8S_CRONJOB_UID = "k8s.cronjob.uid" + """ + The UID of the CronJob. + """ + + K8S_CRONJOB_NAME = "k8s.cronjob.name" + """ + The name of the CronJob. + """ + + OS_TYPE = "os.type" + """ + The operating system type. + """ + + OS_DESCRIPTION = "os.description" + """ + Human readable (not intended to be parsed) OS version information, like e.g. reported by `ver` or `lsb_release -a` commands. + """ + + OS_NAME = "os.name" + """ + Human readable operating system name. + """ + + OS_VERSION = "os.version" + """ + The version string of the operating system as defined in [Version Attributes](/docs/resource/README.md#version-attributes). + """ + + PROCESS_PID = "process.pid" + """ + Process identifier (PID). + """ + + PROCESS_PARENT_PID = "process.parent_pid" + """ + Parent Process identifier (PID). + """ + + PROCESS_EXECUTABLE_NAME = "process.executable.name" + """ + The name of the process executable. On Linux based systems, can be set to the `Name` in `proc/[pid]/status`. On Windows, can be set to the base name of `GetProcessImageFileNameW`. + """ + + PROCESS_EXECUTABLE_PATH = "process.executable.path" + """ + The full path to the process executable. On Linux based systems, can be set to the target of `proc/[pid]/exe`. On Windows, can be set to the result of `GetProcessImageFileNameW`. + """ + + PROCESS_COMMAND = "process.command" + """ + The command used to launch the process (i.e. the command name). On Linux based systems, can be set to the zeroth string in `proc/[pid]/cmdline`. On Windows, can be set to the first parameter extracted from `GetCommandLineW`. + """ + + PROCESS_COMMAND_LINE = "process.command_line" + """ + The full command used to launch the process as a single string representing the full command. On Windows, can be set to the result of `GetCommandLineW`. Do not set this if you have to assemble it just for monitoring; use `process.command_args` instead. + """ + + PROCESS_COMMAND_ARGS = "process.command_args" + """ + All the command arguments (including the command/executable itself) as received by the process. On Linux-based systems (and some other Unixoid systems supporting procfs), can be set according to the list of null-delimited strings extracted from `proc/[pid]/cmdline`. For libc-based executables, this would be the full argv vector passed to `main`. + """ + + PROCESS_OWNER = "process.owner" + """ + The username of the user that owns the process. + """ + + PROCESS_RUNTIME_NAME = "process.runtime.name" + """ + The name of the runtime of this process. For compiled native binaries, this SHOULD be the name of the compiler. + """ + + PROCESS_RUNTIME_VERSION = "process.runtime.version" + """ + The version of the runtime of this process, as returned by the runtime without modification. + """ + + PROCESS_RUNTIME_DESCRIPTION = "process.runtime.description" + """ + An additional description about the runtime of the process, for example a specific vendor customization of the runtime environment. + """ + + SERVICE_NAME = "service.name" + """ + Logical name of the service. + Note: MUST be the same for all instances of horizontally scaled services. If the value was not specified, SDKs MUST fallback to `unknown_service:` concatenated with [`process.executable.name`](process.md#process), e.g. `unknown_service:bash`. If `process.executable.name` is not available, the value MUST be set to `unknown_service`. + """ + + SERVICE_VERSION = "service.version" + """ + The version string of the service API or implementation. The format is not defined by these conventions. + """ + + SERVICE_NAMESPACE = "service.namespace" + """ + A namespace for `service.name`. + Note: A string value having a meaning that helps to distinguish a group of services, for example the team name that owns a group of services. `service.name` is expected to be unique within the same namespace. If `service.namespace` is not specified in the Resource then `service.name` is expected to be unique for all services that have no explicit namespace defined (so the empty/unspecified namespace is simply one more valid namespace). Zero-length namespace string is assumed equal to unspecified namespace. + """ + + SERVICE_INSTANCE_ID = "service.instance.id" + """ + The string ID of the service instance. + Note: MUST be unique for each instance of the same `service.namespace,service.name` pair (in other words `service.namespace,service.name,service.instance.id` triplet MUST be globally unique). The ID helps to distinguish instances of the same service that exist at the same time (e.g. instances of a horizontally scaled service). It is preferable for the ID to be persistent and stay the same for the lifetime of the service instance, however it is acceptable that the ID is ephemeral and changes during important lifetime events for the service (e.g. service restarts). If the service has no inherent unique ID that can be used as the value of this attribute it is recommended to generate a random Version 1 or Version 4 RFC 4122 UUID (services aiming for reproducible UUIDs may also use Version 5, see RFC 4122 for more recommendations). + """ + + TELEMETRY_SDK_NAME = "telemetry.sdk.name" + """ + The name of the telemetry SDK as defined above. + Note: The OpenTelemetry SDK MUST set the `telemetry.sdk.name` attribute to `opentelemetry`. + If another SDK, like a fork or a vendor-provided implementation, is used, this SDK MUST set the + `telemetry.sdk.name` attribute to the fully-qualified class or module name of this SDK's main entry point + or another suitable identifier depending on the language. + The identifier `opentelemetry` is reserved and MUST NOT be used in this case. + All custom identifiers SHOULD be stable across different versions of an implementation. + """ + + TELEMETRY_SDK_LANGUAGE = "telemetry.sdk.language" + """ + The language of the telemetry SDK. + """ + + TELEMETRY_SDK_VERSION = "telemetry.sdk.version" + """ + The version string of the telemetry SDK. + """ + + TELEMETRY_AUTO_VERSION = "telemetry.auto.version" + """ + The version string of the auto instrumentation agent, if used. + """ + + WEBENGINE_NAME = "webengine.name" + """ + The name of the web engine. + """ + + WEBENGINE_VERSION = "webengine.version" + """ + The version of the web engine. + """ + + WEBENGINE_DESCRIPTION = "webengine.description" + """ + Additional description of the web engine (e.g. detailed version and edition information). + """ + + OTEL_SCOPE_NAME = "otel.scope.name" + """ + The name of the instrumentation scope - (`InstrumentationScope.Name` in OTLP). + """ + + OTEL_SCOPE_VERSION = "otel.scope.version" + """ + The version of the instrumentation scope - (`InstrumentationScope.Version` in OTLP). + """ + + OTEL_LIBRARY_NAME = "otel.library.name" + """ + Deprecated, use the `otel.scope.name` attribute. + """ + + OTEL_LIBRARY_VERSION = "otel.library.version" + """ + Deprecated, use the `otel.scope.version` attribute. + """ + + # Manually defined deprecated attributes + + FAAS_ID = "faas.id" + """ + Deprecated, use the `cloud.resource.id` attribute. + """ + + +@deprecated( + "Use :py:const:`opentelemetry.semconv._incubating.attributes.CloudProviderValues` instead. Deprecated since version 1.25.0.", +) +class CloudProviderValues(Enum): + ALIBABA_CLOUD = "alibaba_cloud" + """Alibaba Cloud.""" + + AWS = "aws" + """Amazon Web Services.""" + + AZURE = "azure" + """Microsoft Azure.""" + + GCP = "gcp" + """Google Cloud Platform.""" + + HEROKU = "heroku" + """Heroku Platform as a Service.""" + + IBM_CLOUD = "ibm_cloud" + """IBM Cloud.""" + + TENCENT_CLOUD = "tencent_cloud" + """Tencent Cloud.""" + + +@deprecated( + "Use :py:const:`opentelemetry.semconv._incubating.attributes.CloudPlatformValues` instead. Deprecated since version 1.25.0.", +) +class CloudPlatformValues(Enum): + ALIBABA_CLOUD_ECS = "alibaba_cloud_ecs" + """Alibaba Cloud Elastic Compute Service.""" + + ALIBABA_CLOUD_FC = "alibaba_cloud_fc" + """Alibaba Cloud Function Compute.""" + + ALIBABA_CLOUD_OPENSHIFT = "alibaba_cloud_openshift" + """Red Hat OpenShift on Alibaba Cloud.""" + + AWS_EC2 = "aws_ec2" + """AWS Elastic Compute Cloud.""" + + AWS_ECS = "aws_ecs" + """AWS Elastic Container Service.""" + + AWS_EKS = "aws_eks" + """AWS Elastic Kubernetes Service.""" + + AWS_LAMBDA = "aws_lambda" + """AWS Lambda.""" + + AWS_ELASTIC_BEANSTALK = "aws_elastic_beanstalk" + """AWS Elastic Beanstalk.""" + + AWS_APP_RUNNER = "aws_app_runner" + """AWS App Runner.""" + + AWS_OPENSHIFT = "aws_openshift" + """Red Hat OpenShift on AWS (ROSA).""" + + AZURE_VM = "azure_vm" + """Azure Virtual Machines.""" + + AZURE_CONTAINER_INSTANCES = "azure_container_instances" + """Azure Container Instances.""" + + AZURE_AKS = "azure_aks" + """Azure Kubernetes Service.""" + + AZURE_FUNCTIONS = "azure_functions" + """Azure Functions.""" + + AZURE_APP_SERVICE = "azure_app_service" + """Azure App Service.""" + + AZURE_OPENSHIFT = "azure_openshift" + """Azure Red Hat OpenShift.""" + + GCP_BARE_METAL_SOLUTION = "gcp_bare_metal_solution" + """Google Bare Metal Solution (BMS).""" + + GCP_COMPUTE_ENGINE = "gcp_compute_engine" + """Google Cloud Compute Engine (GCE).""" + + GCP_CLOUD_RUN = "gcp_cloud_run" + """Google Cloud Run.""" + + GCP_KUBERNETES_ENGINE = "gcp_kubernetes_engine" + """Google Cloud Kubernetes Engine (GKE).""" + + GCP_CLOUD_FUNCTIONS = "gcp_cloud_functions" + """Google Cloud Functions (GCF).""" + + GCP_APP_ENGINE = "gcp_app_engine" + """Google Cloud App Engine (GAE).""" + + GCP_OPENSHIFT = "gcp_openshift" + """Red Hat OpenShift on Google Cloud.""" + + IBM_CLOUD_OPENSHIFT = "ibm_cloud_openshift" + """Red Hat OpenShift on IBM Cloud.""" + + TENCENT_CLOUD_CVM = "tencent_cloud_cvm" + """Tencent Cloud Cloud Virtual Machine (CVM).""" + + TENCENT_CLOUD_EKS = "tencent_cloud_eks" + """Tencent Cloud Elastic Kubernetes Service (EKS).""" + + TENCENT_CLOUD_SCF = "tencent_cloud_scf" + """Tencent Cloud Serverless Cloud Function (SCF).""" + + +@deprecated( + "Use :py:const:`opentelemetry.semconv._incubating.attributes.AwsEcsLaunchtypeValues` instead. Deprecated since version 1.25.0.", +) +class AwsEcsLaunchtypeValues(Enum): + EC2 = "ec2" + """ec2.""" + + FARGATE = "fargate" + """fargate.""" + + +@deprecated( + "Use :py:const:`opentelemetry.semconv._incubating.attributes.HostArchValues` instead. Deprecated since version 1.25.0.", +) +class HostArchValues(Enum): + AMD64 = "amd64" + """AMD64.""" + + ARM32 = "arm32" + """ARM32.""" + + ARM64 = "arm64" + """ARM64.""" + + IA64 = "ia64" + """Itanium.""" + + PPC32 = "ppc32" + """32-bit PowerPC.""" + + PPC64 = "ppc64" + """64-bit PowerPC.""" + + S390X = "s390x" + """IBM z/Architecture.""" + + X86 = "x86" + """32-bit x86.""" + + +@deprecated( + "Use :py:const:`opentelemetry.semconv._incubating.attributes.OsTypeValues` instead. Deprecated since version 1.25.0.", +) +class OsTypeValues(Enum): + WINDOWS = "windows" + """Microsoft Windows.""" + + LINUX = "linux" + """Linux.""" + + DARWIN = "darwin" + """Apple Darwin.""" + + FREEBSD = "freebsd" + """FreeBSD.""" + + NETBSD = "netbsd" + """NetBSD.""" + + OPENBSD = "openbsd" + """OpenBSD.""" + + DRAGONFLYBSD = "dragonflybsd" + """DragonFly BSD.""" + + HPUX = "hpux" + """HP-UX (Hewlett Packard Unix).""" + + AIX = "aix" + """AIX (Advanced Interactive eXecutive).""" + + SOLARIS = "solaris" + """SunOS, Oracle Solaris.""" + + Z_OS = "z_os" + """IBM z/OS.""" + + +@deprecated( + "Use :py:const:`opentelemetry.semconv.attributes.TelemetrySdkLanguageValues` instead. Deprecated since version 1.25.0.", +) +class TelemetrySdkLanguageValues(Enum): + CPP = "cpp" + """cpp.""" + + DOTNET = "dotnet" + """dotnet.""" + + ERLANG = "erlang" + """erlang.""" + + GO = "go" + """go.""" + + JAVA = "java" + """java.""" + + NODEJS = "nodejs" + """nodejs.""" + + PHP = "php" + """php.""" + + PYTHON = "python" + """python.""" + + RUBY = "ruby" + """ruby.""" + + RUST = "rust" + """rust.""" + + SWIFT = "swift" + """swift.""" + + WEBJS = "webjs" + """webjs.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/resource/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/resource/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c38b498 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/resource/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/schemas.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/schemas.py new file mode 100644 index 0000000..01c01f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/schemas.py @@ -0,0 +1,118 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum + + +class Schemas(Enum): + V1_21_0 = "https://opentelemetry.io/schemas/1.21.0" + """ + The URL of the OpenTelemetry schema version 1.21.0. + """ + + V1_23_1 = "https://opentelemetry.io/schemas/1.23.1" + """ + The URL of the OpenTelemetry schema version 1.23.1. + """ + + V1_25_0 = "https://opentelemetry.io/schemas/1.25.0" + """ + The URL of the OpenTelemetry schema version 1.25.0. + """ + + V1_26_0 = "https://opentelemetry.io/schemas/1.26.0" + """ + The URL of the OpenTelemetry schema version 1.26.0. + """ + + V1_27_0 = "https://opentelemetry.io/schemas/1.27.0" + """ + The URL of the OpenTelemetry schema version 1.27.0. + """ + + V1_28_0 = "https://opentelemetry.io/schemas/1.28.0" + """ + The URL of the OpenTelemetry schema version 1.28.0. + """ + + V1_29_0 = "https://opentelemetry.io/schemas/1.29.0" + """ + The URL of the OpenTelemetry schema version 1.29.0. + """ + + V1_30_0 = "https://opentelemetry.io/schemas/1.30.0" + """ + The URL of the OpenTelemetry schema version 1.30.0. + """ + + V1_31_0 = "https://opentelemetry.io/schemas/1.31.0" + """ + The URL of the OpenTelemetry schema version 1.31.0. + """ + + V1_32_0 = "https://opentelemetry.io/schemas/1.32.0" + """ + The URL of the OpenTelemetry schema version 1.32.0. + """ + + V1_33_0 = "https://opentelemetry.io/schemas/1.33.0" + """ + The URL of the OpenTelemetry schema version 1.33.0. + """ + + V1_34_0 = "https://opentelemetry.io/schemas/1.34.0" + """ + The URL of the OpenTelemetry schema version 1.34.0. + """ + V1_36_0 = "https://opentelemetry.io/schemas/1.36.0" + """ + The URL of the OpenTelemetry schema version 1.36.0. + """ + + V1_37_0 = "https://opentelemetry.io/schemas/1.37.0" + """ + The URL of the OpenTelemetry schema version 1.37.0. + """ + + V1_38_0 = "https://opentelemetry.io/schemas/1.38.0" + """ + The URL of the OpenTelemetry schema version 1.38.0. + """ + + V1_39_0 = "https://opentelemetry.io/schemas/1.39.0" + """ + The URL of the OpenTelemetry schema version 1.39.0. + """ + + V1_40_0 = "https://opentelemetry.io/schemas/1.40.0" + """ + The URL of the OpenTelemetry schema version 1.40.0. + """ + + V1_41_0 = "https://opentelemetry.io/schemas/1.41.0" + """ + The URL of the OpenTelemetry schema version 1.41.0. + """ + + V1_41_1 = "https://opentelemetry.io/schemas/1.41.1" + """ + The URL of the OpenTelemetry schema version 1.41.1. + """ + + V1_42_0 = "https://opentelemetry.io/schemas/1.42.0" + """ + The URL of the OpenTelemetry schema version 1.42.0. + """ + + V1_43_0 = "https://opentelemetry.io/schemas/1.43.0" + """ + The URL of the OpenTelemetry schema version 1.43.0. + """ + + V1_44_0 = "https://opentelemetry.io/schemas/1.44.0" + """ + The URL of the OpenTelemetry schema version 1.44.0. + """ + + # when generating new semantic conventions, + # make sure to add new versions version here. diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/trace/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/trace/__init__.py new file mode 100644 index 0000000..2ce2c40 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/trace/__init__.py @@ -0,0 +1,2164 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# pylint: disable=too-many-lines + +from enum import Enum + +from typing_extensions import deprecated + + +@deprecated( + "Use attributes defined in the :py:const:`opentelemetry.semconv.attributes` and :py:const:`opentelemetry.semconv._incubating.attributes` modules instead. Deprecated since version 1.25.0.", +) +class SpanAttributes: + SCHEMA_URL = "https://opentelemetry.io/schemas/1.21.0" + """ + The URL of the OpenTelemetry schema for these keys and values. + """ + CLIENT_ADDRESS = "client.address" + """ + Client address - unix domain socket name, IPv4 or IPv6 address. + Note: When observed from the server side, and when communicating through an intermediary, `client.address` SHOULD represent client address behind any intermediaries (e.g. proxies) if it's available. + """ + + CLIENT_PORT = "client.port" + """ + Client port number. + Note: When observed from the server side, and when communicating through an intermediary, `client.port` SHOULD represent client port behind any intermediaries (e.g. proxies) if it's available. + """ + + CLIENT_SOCKET_ADDRESS = "client.socket.address" + """ + Immediate client peer address - unix domain socket name, IPv4 or IPv6 address. + """ + + CLIENT_SOCKET_PORT = "client.socket.port" + """ + Immediate client peer port number. + """ + + HTTP_METHOD = "http.method" + """ + Deprecated, use `http.request.method` instead. + """ + + HTTP_STATUS_CODE = "http.status_code" + """ + Deprecated, use `http.response.status_code` instead. + """ + + HTTP_SCHEME = "http.scheme" + """ + Deprecated, use `url.scheme` instead. + """ + + HTTP_URL = "http.url" + """ + Deprecated, use `url.full` instead. + """ + + HTTP_TARGET = "http.target" + """ + Deprecated, use `url.path` and `url.query` instead. + """ + + HTTP_REQUEST_CONTENT_LENGTH = "http.request_content_length" + """ + Deprecated, use `http.request.body.size` instead. + """ + + HTTP_RESPONSE_CONTENT_LENGTH = "http.response_content_length" + """ + Deprecated, use `http.response.body.size` instead. + """ + + NET_SOCK_PEER_NAME = "net.sock.peer.name" + """ + Deprecated, use `server.socket.domain` on client spans. + """ + + NET_SOCK_PEER_ADDR = "net.sock.peer.addr" + """ + Deprecated, use `server.socket.address` on client spans and `client.socket.address` on server spans. + """ + + NET_SOCK_PEER_PORT = "net.sock.peer.port" + """ + Deprecated, use `server.socket.port` on client spans and `client.socket.port` on server spans. + """ + + NET_PEER_NAME = "net.peer.name" + """ + Deprecated, use `server.address` on client spans and `client.address` on server spans. + """ + + NET_PEER_PORT = "net.peer.port" + """ + Deprecated, use `server.port` on client spans and `client.port` on server spans. + """ + + NET_HOST_NAME = "net.host.name" + """ + Deprecated, use `server.address`. + """ + + NET_HOST_PORT = "net.host.port" + """ + Deprecated, use `server.port`. + """ + + NET_SOCK_HOST_ADDR = "net.sock.host.addr" + """ + Deprecated, use `server.socket.address`. + """ + + NET_SOCK_HOST_PORT = "net.sock.host.port" + """ + Deprecated, use `server.socket.port`. + """ + + NET_TRANSPORT = "net.transport" + """ + Deprecated, use `network.transport`. + """ + + NET_PROTOCOL_NAME = "net.protocol.name" + """ + Deprecated, use `network.protocol.name`. + """ + + NET_PROTOCOL_VERSION = "net.protocol.version" + """ + Deprecated, use `network.protocol.version`. + """ + + NET_SOCK_FAMILY = "net.sock.family" + """ + Deprecated, use `network.transport` and `network.type`. + """ + + DESTINATION_DOMAIN = "destination.domain" + """ + The domain name of the destination system. + Note: This value may be a host name, a fully qualified domain name, or another host naming format. + """ + + DESTINATION_ADDRESS = "destination.address" + """ + Peer address, for example IP address or UNIX socket name. + """ + + DESTINATION_PORT = "destination.port" + """ + Peer port number. + """ + + EXCEPTION_TYPE = "exception.type" + """ + The type of the exception (its fully-qualified class name, if applicable). The dynamic type of the exception should be preferred over the static type in languages that support it. + """ + + EXCEPTION_MESSAGE = "exception.message" + """ + The exception message. + """ + + EXCEPTION_STACKTRACE = "exception.stacktrace" + """ + A stacktrace as a string in the natural representation for the language runtime. The representation is to be determined and documented by each language SIG. + """ + + HTTP_REQUEST_METHOD = "http.request.method" + """ + HTTP request method. + Note: HTTP request method value SHOULD be "known" to the instrumentation. + By default, this convention defines "known" methods as the ones listed in [RFC9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-methods) + and the PATCH method defined in [RFC5789](https://www.rfc-editor.org/rfc/rfc5789.html). + + If the HTTP request method is not known to instrumentation, it MUST set the `http.request.method` attribute to `_OTHER` and, except if reporting a metric, MUST + set the exact method received in the request line as value of the `http.request.method_original` attribute. + + If the HTTP instrumentation could end up converting valid HTTP request methods to `_OTHER`, then it MUST provide a way to override + the list of known HTTP methods. If this override is done via environment variable, then the environment variable MUST be named + OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated list of case-sensitive known HTTP methods + (this list MUST be a full override of the default known method, it is not a list of known methods in addition to the defaults). + + HTTP method names are case-sensitive and `http.request.method` attribute value MUST match a known HTTP method name exactly. + Instrumentations for specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical equivalent. + Tracing instrumentations that do so, MUST also set `http.request.method_original` to the original value. + """ + + HTTP_RESPONSE_STATUS_CODE = "http.response.status_code" + """ + [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). + """ + + NETWORK_PROTOCOL_NAME = "network.protocol.name" + """ + [OSI Application Layer](https://osi-model.com/application-layer/) or non-OSI equivalent. The value SHOULD be normalized to lowercase. + """ + + NETWORK_PROTOCOL_VERSION = "network.protocol.version" + """ + Version of the application layer protocol used. See note below. + Note: `network.protocol.version` refers to the version of the protocol used and might be different from the protocol client's version. If the HTTP client used has a version of `0.27.2`, but sends HTTP version `1.1`, this attribute should be set to `1.1`. + """ + + SERVER_ADDRESS = "server.address" + """ + Host identifier of the ["URI origin"](https://www.rfc-editor.org/rfc/rfc9110.html#name-uri-origin) HTTP request is sent to. + Note: Determined by using the first of the following that applies + + - Host identifier of the [request target](https://www.rfc-editor.org/rfc/rfc9110.html#target.resource) + if it's sent in absolute-form + - Host identifier of the `Host` header + + SHOULD NOT be set if capturing it would require an extra DNS lookup. + """ + + SERVER_PORT = "server.port" + """ + Port identifier of the ["URI origin"](https://www.rfc-editor.org/rfc/rfc9110.html#name-uri-origin) HTTP request is sent to. + Note: When [request target](https://www.rfc-editor.org/rfc/rfc9110.html#target.resource) is absolute URI, `server.port` MUST match URI port identifier, otherwise it MUST match `Host` header port identifier. + """ + + HTTP_ROUTE = "http.route" + """ + The matched route (path template in the format used by the respective server framework). See note below. + Note: MUST NOT be populated when this is not supported by the HTTP server framework as the route attribute should have low-cardinality and the URI path can NOT substitute it. + SHOULD include the [application root](/docs/http/http-spans.md#http-server-definitions) if there is one. + """ + + URL_SCHEME = "url.scheme" + """ + The [URI scheme](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) component identifying the used protocol. + """ + + EVENT_NAME = "event.name" + """ + The name identifies the event. + """ + + EVENT_DOMAIN = "event.domain" + """ + The domain identifies the business context for the events. + Note: Events across different domains may have same `event.name`, yet be + unrelated events. + """ + + LOG_RECORD_UID = "log.record.uid" + """ + A unique identifier for the Log Record. + Note: If an id is provided, other log records with the same id will be considered duplicates and can be removed safely. This means, that two distinguishable log records MUST have different values. + The id MAY be an [Universally Unique Lexicographically Sortable Identifier (ULID)](https://github.com/ulid/spec), but other identifiers (e.g. UUID) may be used as needed. + """ + + FEATURE_FLAG_KEY = "feature_flag.key" + """ + The unique identifier of the feature flag. + """ + + FEATURE_FLAG_PROVIDER_NAME = "feature_flag.provider_name" + """ + The name of the service provider that performs the flag evaluation. + """ + + FEATURE_FLAG_VARIANT = "feature_flag.variant" + """ + SHOULD be a semantic identifier for a value. If one is unavailable, a stringified version of the value can be used. + Note: A semantic identifier, commonly referred to as a variant, provides a means + for referring to a value without including the value itself. This can + provide additional context for understanding the meaning behind a value. + For example, the variant `red` maybe be used for the value `#c05543`. + + A stringified version of the value can be used in situations where a + semantic identifier is unavailable. String representation of the value + should be determined by the implementer. + """ + + LOG_IOSTREAM = "log.iostream" + """ + The stream associated with the log. See below for a list of well-known values. + """ + + LOG_FILE_NAME = "log.file.name" + """ + The basename of the file. + """ + + LOG_FILE_PATH = "log.file.path" + """ + The full path to the file. + """ + + LOG_FILE_NAME_RESOLVED = "log.file.name_resolved" + """ + The basename of the file, with symlinks resolved. + """ + + LOG_FILE_PATH_RESOLVED = "log.file.path_resolved" + """ + The full path to the file, with symlinks resolved. + """ + + SERVER_SOCKET_ADDRESS = "server.socket.address" + """ + Physical server IP address or Unix socket address. If set from the client, should simply use the socket's peer address, and not attempt to find any actual server IP (i.e., if set from client, this may represent some proxy server instead of the logical server). + """ + + POOL = "pool" + """ + Name of the buffer pool. + Note: Pool names are generally obtained via [BufferPoolMXBean#getName()](https://docs.oracle.com/en/java/javase/11/docs/api/java.management/java/lang/management/BufferPoolMXBean.html#getName()). + """ + + TYPE = "type" + """ + The type of memory. + """ + + SERVER_SOCKET_DOMAIN = "server.socket.domain" + """ + The domain name of an immediate peer. + Note: Typically observed from the client side, and represents a proxy or other intermediary domain name. + """ + + SERVER_SOCKET_PORT = "server.socket.port" + """ + Physical server port. + """ + + SOURCE_DOMAIN = "source.domain" + """ + The domain name of the source system. + Note: This value may be a host name, a fully qualified domain name, or another host naming format. + """ + + SOURCE_ADDRESS = "source.address" + """ + Source address, for example IP address or Unix socket name. + """ + + SOURCE_PORT = "source.port" + """ + Source port number. + """ + + AWS_LAMBDA_INVOKED_ARN = "aws.lambda.invoked_arn" + """ + The full invoked ARN as provided on the `Context` passed to the function (`Lambda-Runtime-Invoked-Function-Arn` header on the `/runtime/invocation/next` applicable). + Note: This may be different from `cloud.resource_id` if an alias is involved. + """ + + CLOUDEVENTS_EVENT_ID = "cloudevents.event_id" + """ + The [event_id](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#id) uniquely identifies the event. + """ + + CLOUDEVENTS_EVENT_SOURCE = "cloudevents.event_source" + """ + The [source](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#source-1) identifies the context in which an event happened. + """ + + CLOUDEVENTS_EVENT_SPEC_VERSION = "cloudevents.event_spec_version" + """ + The [version of the CloudEvents specification](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#specversion) which the event uses. + """ + + CLOUDEVENTS_EVENT_TYPE = "cloudevents.event_type" + """ + The [event_type](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#type) contains a value describing the type of event related to the originating occurrence. + """ + + CLOUDEVENTS_EVENT_SUBJECT = "cloudevents.event_subject" + """ + The [subject](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#subject) of the event in the context of the event producer (identified by source). + """ + + OPENTRACING_REF_TYPE = "opentracing.ref_type" + """ + Parent-child Reference type. + Note: The causal relationship between a child Span and a parent Span. + """ + + DB_SYSTEM = "db.system" + """ + An identifier for the database management system (DBMS) product being used. See below for a list of well-known identifiers. + """ + + DB_CONNECTION_STRING = "db.connection_string" + """ + The connection string used to connect to the database. It is recommended to remove embedded credentials. + """ + + DB_USER = "db.user" + """ + Username for accessing the database. + """ + + DB_JDBC_DRIVER_CLASSNAME = "db.jdbc.driver_classname" + """ + The fully-qualified class name of the [Java Database Connectivity (JDBC)](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) driver used to connect. + """ + + DB_NAME = "db.name" + """ + This attribute is used to report the name of the database being accessed. For commands that switch the database, this should be set to the target database (even if the command fails). + Note: In some SQL databases, the database name to be used is called "schema name". In case there are multiple layers that could be considered for database name (e.g. Oracle instance name and schema name), the database name to be used is the more specific layer (e.g. Oracle schema name). + """ + + DB_STATEMENT = "db.statement" + """ + The database statement being executed. + """ + + DB_OPERATION = "db.operation" + """ + The name of the operation being executed, e.g. the [MongoDB command name](https://docs.mongodb.com/manual/reference/command/#database-operations) such as `findAndModify`, or the SQL keyword. + Note: When setting this to an SQL keyword, it is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if the operation name is provided by the library being instrumented. If the SQL statement has an ambiguous operation, or performs more than one operation, this value may be omitted. + """ + + NETWORK_TRANSPORT = "network.transport" + """ + [OSI Transport Layer](https://osi-model.com/transport-layer/) or [Inter-process Communication method](https://en.wikipedia.org/wiki/Inter-process_communication). The value SHOULD be normalized to lowercase. + """ + + NETWORK_TYPE = "network.type" + """ + [OSI Network Layer](https://osi-model.com/network-layer/) or non-OSI equivalent. The value SHOULD be normalized to lowercase. + """ + + DB_MSSQL_INSTANCE_NAME = "db.mssql.instance_name" + """ + The Microsoft SQL Server [instance name](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) connecting to. This name is used to determine the port of a named instance. + Note: If setting a `db.mssql.instance_name`, `server.port` is no longer required (but still recommended if non-standard). + """ + + DB_CASSANDRA_PAGE_SIZE = "db.cassandra.page_size" + """ + The fetch size used for paging, i.e. how many rows will be returned at once. + """ + + DB_CASSANDRA_CONSISTENCY_LEVEL = "db.cassandra.consistency_level" + """ + The consistency level of the query. Based on consistency values from [CQL](https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/dml/dmlConfigConsistency.html). + """ + + DB_CASSANDRA_TABLE = "db.cassandra.table" + """ + The name of the primary table that the operation is acting upon, including the keyspace name (if applicable). + Note: This mirrors the db.sql.table attribute but references cassandra rather than sql. It is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if it is provided by the library being instrumented. If the operation is acting upon an anonymous table, or more than one table, this value MUST NOT be set. + """ + + DB_CASSANDRA_IDEMPOTENCE = "db.cassandra.idempotence" + """ + Whether or not the query is idempotent. + """ + + DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT = "db.cassandra.speculative_execution_count" + """ + The number of times a query was speculatively executed. Not set or `0` if the query was not executed speculatively. + """ + + DB_CASSANDRA_COORDINATOR_ID = "db.cassandra.coordinator.id" + """ + The ID of the coordinating node for a query. + """ + + DB_CASSANDRA_COORDINATOR_DC = "db.cassandra.coordinator.dc" + """ + The data center of the coordinating node for a query. + """ + + DB_REDIS_DATABASE_INDEX = "db.redis.database_index" + """ + The index of the database being accessed as used in the [`SELECT` command](https://redis.io/commands/select), provided as an integer. To be used instead of the generic `db.name` attribute. + """ + + DB_MONGODB_COLLECTION = "db.mongodb.collection" + """ + The collection being accessed within the database stated in `db.name`. + """ + + URL_FULL = "url.full" + """ + Absolute URL describing a network resource according to [RFC3986](https://www.rfc-editor.org/rfc/rfc3986). + Note: For network calls, URL usually has `scheme://host[:port][path][?query][#fragment]` format, where the fragment is not transmitted over HTTP, but if it is known, it should be included nevertheless. + `url.full` MUST NOT contain credentials passed via URL in form of `https://username:password@www.example.com/`. In such case username and password should be redacted and attribute's value should be `https://REDACTED:REDACTED@www.example.com/`. + `url.full` SHOULD capture the absolute URL when it is available (or can be reconstructed) and SHOULD NOT be validated or modified except for sanitizing purposes. + """ + + DB_SQL_TABLE = "db.sql.table" + """ + The name of the primary table that the operation is acting upon, including the database name (if applicable). + Note: It is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if it is provided by the library being instrumented. If the operation is acting upon an anonymous table, or more than one table, this value MUST NOT be set. + """ + + DB_COSMOSDB_CLIENT_ID = "db.cosmosdb.client_id" + """ + Unique Cosmos client instance id. + """ + + DB_COSMOSDB_OPERATION_TYPE = "db.cosmosdb.operation_type" + """ + CosmosDB Operation Type. + """ + + USER_AGENT_ORIGINAL = "user_agent.original" + """ + Full user-agent string is generated by Cosmos DB SDK. + Note: The user-agent value is generated by SDK which is a combination of
`sdk_version` : Current version of SDK. e.g. 'cosmos-netstandard-sdk/3.23.0'
`direct_pkg_version` : Direct package version used by Cosmos DB SDK. e.g. '3.23.1'
`number_of_client_instances` : Number of cosmos client instances created by the application. e.g. '1'
`type_of_machine_architecture` : Machine architecture. e.g. 'X64'
`operating_system` : Operating System. e.g. 'Linux 5.4.0-1098-azure 104 18'
`runtime_framework` : Runtime Framework. e.g. '.NET Core 3.1.32'
`failover_information` : Generated key to determine if region failover enabled. + Format Reg-{D (Disabled discovery)}-S(application region)|L(List of preferred regions)|N(None, user did not configure it). + Default value is "NS". + """ + + DB_COSMOSDB_CONNECTION_MODE = "db.cosmosdb.connection_mode" + """ + Cosmos client connection mode. + """ + + DB_COSMOSDB_CONTAINER = "db.cosmosdb.container" + """ + Cosmos DB container name. + """ + + DB_COSMOSDB_REQUEST_CONTENT_LENGTH = "db.cosmosdb.request_content_length" + """ + Request payload size in bytes. + """ + + DB_COSMOSDB_STATUS_CODE = "db.cosmosdb.status_code" + """ + Cosmos DB status code. + """ + + DB_COSMOSDB_SUB_STATUS_CODE = "db.cosmosdb.sub_status_code" + """ + Cosmos DB sub status code. + """ + + DB_COSMOSDB_REQUEST_CHARGE = "db.cosmosdb.request_charge" + """ + RU consumed for that operation. + """ + + OTEL_STATUS_CODE = "otel.status_code" + """ + Name of the code, either "OK" or "ERROR". MUST NOT be set if the status code is UNSET. + """ + + OTEL_STATUS_DESCRIPTION = "otel.status_description" + """ + Description of the Status if it has a value, otherwise not set. + """ + + FAAS_TRIGGER = "faas.trigger" + """ + Type of the trigger which caused this function invocation. + Note: For the server/consumer span on the incoming side, + `faas.trigger` MUST be set. + + Clients invoking FaaS instances usually cannot set `faas.trigger`, + since they would typically need to look in the payload to determine + the event type. If clients set it, it should be the same as the + trigger that corresponding incoming would have (i.e., this has + nothing to do with the underlying transport used to make the API + call to invoke the lambda, which is often HTTP). + """ + + FAAS_INVOCATION_ID = "faas.invocation_id" + """ + The invocation ID of the current function invocation. + """ + + CLOUD_RESOURCE_ID = "cloud.resource_id" + """ + Cloud provider-specific native identifier of the monitored cloud resource (e.g. an [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) on AWS, a [fully qualified resource ID](https://learn.microsoft.com/en-us/rest/api/resources/resources/get-by-id) on Azure, a [full resource name](https://cloud.google.com/apis/design/resource_names#full_resource_name) on GCP). + Note: On some cloud providers, it may not be possible to determine the full ID at startup, + so it may be necessary to set `cloud.resource_id` as a span attribute instead. + + The exact value to use for `cloud.resource_id` depends on the cloud provider. + The following well-known definitions MUST be used if you set this attribute and they apply: + + * **AWS Lambda:** The function [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html). + Take care not to use the "invoked ARN" directly but replace any + [alias suffix](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) + with the resolved function version, as the same runtime instance may be invokable with + multiple different aliases. + * **GCP:** The [URI of the resource](https://cloud.google.com/iam/docs/full-resource-names) + * **Azure:** The [Fully Qualified Resource ID](https://docs.microsoft.com/en-us/rest/api/resources/resources/get-by-id) of the invoked function, + *not* the function app, having the form + `/subscriptions//resourceGroups//providers/Microsoft.Web/sites//functions/`. + This means that a span attribute MUST be used, as an Azure function app can host multiple functions that would usually share + a TracerProvider. + """ + + FAAS_DOCUMENT_COLLECTION = "faas.document.collection" + """ + The name of the source on which the triggering operation was performed. For example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos DB to the database name. + """ + + FAAS_DOCUMENT_OPERATION = "faas.document.operation" + """ + Describes the type of the operation that was performed on the data. + """ + + FAAS_DOCUMENT_TIME = "faas.document.time" + """ + A string containing the time when the data was accessed in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). + """ + + FAAS_DOCUMENT_NAME = "faas.document.name" + """ + The document name/table subjected to the operation. For example, in Cloud Storage or S3 is the name of the file, and in Cosmos DB the table name. + """ + + URL_PATH = "url.path" + """ + The [URI path](https://www.rfc-editor.org/rfc/rfc3986#section-3.3) component. + Note: When missing, the value is assumed to be `/`. + """ + + URL_QUERY = "url.query" + """ + The [URI query](https://www.rfc-editor.org/rfc/rfc3986#section-3.4) component. + Note: Sensitive content provided in query string SHOULD be scrubbed when instrumentations can identify it. + """ + + MESSAGING_SYSTEM = "messaging.system" + """ + A string identifying the messaging system. + """ + + MESSAGING_OPERATION = "messaging.operation" + """ + A string identifying the kind of messaging operation as defined in the [Operation names](#operation-names) section above. + Note: If a custom value is used, it MUST be of low cardinality. + """ + + MESSAGING_BATCH_MESSAGE_COUNT = "messaging.batch.message_count" + """ + The number of messages sent, received, or processed in the scope of the batching operation. + Note: Instrumentations SHOULD NOT set `messaging.batch.message_count` on spans that operate with a single message. When a messaging client library supports both batch and single-message API for the same operation, instrumentations SHOULD use `messaging.batch.message_count` for batching APIs and SHOULD NOT use it for single-message APIs. + """ + + MESSAGING_CLIENT_ID = "messaging.client_id" + """ + A unique identifier for the client that consumes or produces a message. + """ + + MESSAGING_DESTINATION_NAME = "messaging.destination.name" + """ + The message destination name. + Note: Destination name SHOULD uniquely identify a specific queue, topic or other entity within the broker. If + the broker does not have such notion, the destination name SHOULD uniquely identify the broker. + """ + + MESSAGING_DESTINATION_TEMPLATE = "messaging.destination.template" + """ + Low cardinality representation of the messaging destination name. + Note: Destination names could be constructed from templates. An example would be a destination name involving a user name or product id. Although the destination name in this case is of high cardinality, the underlying template is of low cardinality and can be effectively used for grouping and aggregation. + """ + + MESSAGING_DESTINATION_TEMPORARY = "messaging.destination.temporary" + """ + A boolean that is true if the message destination is temporary and might not exist anymore after messages are processed. + """ + + MESSAGING_DESTINATION_ANONYMOUS = "messaging.destination.anonymous" + """ + A boolean that is true if the message destination is anonymous (could be unnamed or have auto-generated name). + """ + + MESSAGING_MESSAGE_ID = "messaging.message.id" + """ + A value used by the messaging system as an identifier for the message, represented as a string. + """ + + MESSAGING_MESSAGE_CONVERSATION_ID = "messaging.message.conversation_id" + """ + The [conversation ID](#conversations) identifying the conversation to which the message belongs, represented as a string. Sometimes called "Correlation ID". + """ + + MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES = "messaging.message.payload_size_bytes" + """ + The (uncompressed) size of the message payload in bytes. Also use this attribute if it is unknown whether the compressed or uncompressed payload size is reported. + """ + + MESSAGING_MESSAGE_PAYLOAD_COMPRESSED_SIZE_BYTES = "messaging.message.payload_compressed_size_bytes" + """ + The compressed size of the message payload in bytes. + """ + + FAAS_TIME = "faas.time" + """ + A string containing the function invocation time in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). + """ + + FAAS_CRON = "faas.cron" + """ + A string containing the schedule period as [Cron Expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). + """ + + FAAS_COLDSTART = "faas.coldstart" + """ + A boolean that is true if the serverless function is executed for the first time (aka cold-start). + """ + + FAAS_INVOKED_NAME = "faas.invoked_name" + """ + The name of the invoked function. + Note: SHOULD be equal to the `faas.name` resource attribute of the invoked function. + """ + + FAAS_INVOKED_PROVIDER = "faas.invoked_provider" + """ + The cloud provider of the invoked function. + Note: SHOULD be equal to the `cloud.provider` resource attribute of the invoked function. + """ + + FAAS_INVOKED_REGION = "faas.invoked_region" + """ + The cloud region of the invoked function. + Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked function. + """ + + NETWORK_CONNECTION_TYPE = "network.connection.type" + """ + The internet connection type. + """ + + NETWORK_CONNECTION_SUBTYPE = "network.connection.subtype" + """ + This describes more details regarding the connection.type. It may be the type of cell technology connection, but it could be used for describing details about a wifi connection. + """ + + NETWORK_CARRIER_NAME = "network.carrier.name" + """ + The name of the mobile carrier. + """ + + NETWORK_CARRIER_MCC = "network.carrier.mcc" + """ + The mobile carrier country code. + """ + + NETWORK_CARRIER_MNC = "network.carrier.mnc" + """ + The mobile carrier network code. + """ + + NETWORK_CARRIER_ICC = "network.carrier.icc" + """ + The ISO 3166-1 alpha-2 2-character country code associated with the mobile carrier network. + """ + + PEER_SERVICE = "peer.service" + """ + The [`service.name`](/docs/resource/README.md#service) of the remote service. SHOULD be equal to the actual `service.name` resource attribute of the remote service if any. + """ + + ENDUSER_ID = "enduser.id" + """ + Username or client_id extracted from the access token or [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in the inbound request from outside the system. + """ + + ENDUSER_ROLE = "enduser.role" + """ + Actual/assumed role the client is making the request under extracted from token or application security context. + """ + + ENDUSER_SCOPE = "enduser.scope" + """ + Scopes or granted authorities the client currently possesses extracted from token or application security context. The value would come from the scope associated with an [OAuth 2.0 Access Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute value in a [SAML 2.0 Assertion](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). + """ + + THREAD_ID = "thread.id" + """ + Current "managed" thread ID (as opposed to OS thread ID). + """ + + THREAD_NAME = "thread.name" + """ + Current thread name. + """ + + CODE_FUNCTION = "code.function" + """ + The method or function name, or equivalent (usually rightmost part of the code unit's name). + """ + + CODE_NAMESPACE = "code.namespace" + """ + The "namespace" within which `code.function` is defined. Usually the qualified class or module name, such that `code.namespace` + some separator + `code.function` form a unique identifier for the code unit. + """ + + CODE_FILEPATH = "code.filepath" + """ + The source code file name that identifies the code unit as uniquely as possible (preferably an absolute file path). + """ + + CODE_LINENO = "code.lineno" + """ + The line number in `code.filepath` best representing the operation. It SHOULD point within the code unit named in `code.function`. + """ + + CODE_COLUMN = "code.column" + """ + The column number in `code.filepath` best representing the operation. It SHOULD point within the code unit named in `code.function`. + """ + + HTTP_REQUEST_METHOD_ORIGINAL = "http.request.method_original" + """ + Original HTTP method sent by the client in the request line. + """ + + HTTP_REQUEST_BODY_SIZE = "http.request.body.size" + """ + The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. + """ + + HTTP_RESPONSE_BODY_SIZE = "http.response.body.size" + """ + The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. + """ + + HTTP_RESEND_COUNT = "http.resend_count" + """ + The ordinal number of request resending attempt (for any reason, including redirects). + Note: The resend count SHOULD be updated each time an HTTP request gets resent by the client, regardless of what was the cause of the resending (e.g. redirection, authorization failure, 503 Server Unavailable, network issues, or any other). + """ + + RPC_SYSTEM = "rpc.system" + """ + The value `aws-api`. + """ + + RPC_SERVICE = "rpc.service" + """ + The name of the service to which a request is made, as returned by the AWS SDK. + Note: This is the logical name of the service from the RPC interface perspective, which can be different from the name of any implementing class. The `code.namespace` attribute may be used to store the latter (despite the attribute name, it may include a class name; e.g., class with method actually executing the call on the server side, RPC client stub class on the client side). + """ + + RPC_METHOD = "rpc.method" + """ + The name of the operation corresponding to the request, as returned by the AWS SDK. + Note: This is the logical name of the method from the RPC interface perspective, which can be different from the name of any implementing method/function. The `code.function` attribute may be used to store the latter (e.g., method actually executing the call on the server side, RPC client stub method on the client side). + """ + + AWS_REQUEST_ID = "aws.request_id" + """ + The AWS request ID as returned in the response headers `x-amz-request-id` or `x-amz-requestid`. + """ + + AWS_DYNAMODB_TABLE_NAMES = "aws.dynamodb.table_names" + """ + The keys in the `RequestItems` object field. + """ + + AWS_DYNAMODB_CONSUMED_CAPACITY = "aws.dynamodb.consumed_capacity" + """ + The JSON-serialized value of each item in the `ConsumedCapacity` response field. + """ + + AWS_DYNAMODB_ITEM_COLLECTION_METRICS = "aws.dynamodb.item_collection_metrics" + """ + The JSON-serialized value of the `ItemCollectionMetrics` response field. + """ + + AWS_DYNAMODB_PROVISIONED_READ_CAPACITY = "aws.dynamodb.provisioned_read_capacity" + """ + The value of the `ProvisionedThroughput.ReadCapacityUnits` request parameter. + """ + + AWS_DYNAMODB_PROVISIONED_WRITE_CAPACITY = "aws.dynamodb.provisioned_write_capacity" + """ + The value of the `ProvisionedThroughput.WriteCapacityUnits` request parameter. + """ + + AWS_DYNAMODB_CONSISTENT_READ = "aws.dynamodb.consistent_read" + """ + The value of the `ConsistentRead` request parameter. + """ + + AWS_DYNAMODB_PROJECTION = "aws.dynamodb.projection" + """ + The value of the `ProjectionExpression` request parameter. + """ + + AWS_DYNAMODB_LIMIT = "aws.dynamodb.limit" + """ + The value of the `Limit` request parameter. + """ + + AWS_DYNAMODB_ATTRIBUTES_TO_GET = "aws.dynamodb.attributes_to_get" + """ + The value of the `AttributesToGet` request parameter. + """ + + AWS_DYNAMODB_INDEX_NAME = "aws.dynamodb.index_name" + """ + The value of the `IndexName` request parameter. + """ + + AWS_DYNAMODB_SELECT = "aws.dynamodb.select" + """ + The value of the `Select` request parameter. + """ + + AWS_DYNAMODB_GLOBAL_SECONDARY_INDEXES = "aws.dynamodb.global_secondary_indexes" + """ + The JSON-serialized value of each item of the `GlobalSecondaryIndexes` request field. + """ + + AWS_DYNAMODB_LOCAL_SECONDARY_INDEXES = "aws.dynamodb.local_secondary_indexes" + """ + The JSON-serialized value of each item of the `LocalSecondaryIndexes` request field. + """ + + AWS_DYNAMODB_EXCLUSIVE_START_TABLE = "aws.dynamodb.exclusive_start_table" + """ + The value of the `ExclusiveStartTableName` request parameter. + """ + + AWS_DYNAMODB_TABLE_COUNT = "aws.dynamodb.table_count" + """ + The the number of items in the `TableNames` response parameter. + """ + + AWS_DYNAMODB_SCAN_FORWARD = "aws.dynamodb.scan_forward" + """ + The value of the `ScanIndexForward` request parameter. + """ + + AWS_DYNAMODB_SEGMENT = "aws.dynamodb.segment" + """ + The value of the `Segment` request parameter. + """ + + AWS_DYNAMODB_TOTAL_SEGMENTS = "aws.dynamodb.total_segments" + """ + The value of the `TotalSegments` request parameter. + """ + + AWS_DYNAMODB_COUNT = "aws.dynamodb.count" + """ + The value of the `Count` response parameter. + """ + + AWS_DYNAMODB_SCANNED_COUNT = "aws.dynamodb.scanned_count" + """ + The value of the `ScannedCount` response parameter. + """ + + AWS_DYNAMODB_ATTRIBUTE_DEFINITIONS = "aws.dynamodb.attribute_definitions" + """ + The JSON-serialized value of each item in the `AttributeDefinitions` request field. + """ + + AWS_DYNAMODB_GLOBAL_SECONDARY_INDEX_UPDATES = "aws.dynamodb.global_secondary_index_updates" + """ + The JSON-serialized value of each item in the the `GlobalSecondaryIndexUpdates` request field. + """ + + AWS_S3_BUCKET = "aws.s3.bucket" + """ + The S3 bucket name the request refers to. Corresponds to the `--bucket` parameter of the [S3 API](https://docs.aws.amazon.com/cli/latest/reference/s3api/index.html) operations. + Note: The `bucket` attribute is applicable to all S3 operations that reference a bucket, i.e. that require the bucket name as a mandatory parameter. + This applies to almost all S3 operations except `list-buckets`. + """ + + AWS_S3_KEY = "aws.s3.key" + """ + The S3 object key the request refers to. Corresponds to the `--key` parameter of the [S3 API](https://docs.aws.amazon.com/cli/latest/reference/s3api/index.html) operations. + Note: The `key` attribute is applicable to all object-related S3 operations, i.e. that require the object key as a mandatory parameter. + This applies in particular to the following operations: + + - [copy-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/copy-object.html) + - [delete-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/delete-object.html) + - [get-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/get-object.html) + - [head-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/head-object.html) + - [put-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/put-object.html) + - [restore-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/restore-object.html) + - [select-object-content](https://docs.aws.amazon.com/cli/latest/reference/s3api/select-object-content.html) + - [abort-multipart-upload](https://docs.aws.amazon.com/cli/latest/reference/s3api/abort-multipart-upload.html) + - [complete-multipart-upload](https://docs.aws.amazon.com/cli/latest/reference/s3api/complete-multipart-upload.html) + - [create-multipart-upload](https://docs.aws.amazon.com/cli/latest/reference/s3api/create-multipart-upload.html) + - [list-parts](https://docs.aws.amazon.com/cli/latest/reference/s3api/list-parts.html) + - [upload-part](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part.html) + - [upload-part-copy](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part-copy.html). + """ + + AWS_S3_COPY_SOURCE = "aws.s3.copy_source" + """ + The source object (in the form `bucket`/`key`) for the copy operation. + Note: The `copy_source` attribute applies to S3 copy operations and corresponds to the `--copy-source` parameter + of the [copy-object operation within the S3 API](https://docs.aws.amazon.com/cli/latest/reference/s3api/copy-object.html). + This applies in particular to the following operations: + + - [copy-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/copy-object.html) + - [upload-part-copy](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part-copy.html). + """ + + AWS_S3_UPLOAD_ID = "aws.s3.upload_id" + """ + Upload ID that identifies the multipart upload. + Note: The `upload_id` attribute applies to S3 multipart-upload operations and corresponds to the `--upload-id` parameter + of the [S3 API](https://docs.aws.amazon.com/cli/latest/reference/s3api/index.html) multipart operations. + This applies in particular to the following operations: + + - [abort-multipart-upload](https://docs.aws.amazon.com/cli/latest/reference/s3api/abort-multipart-upload.html) + - [complete-multipart-upload](https://docs.aws.amazon.com/cli/latest/reference/s3api/complete-multipart-upload.html) + - [list-parts](https://docs.aws.amazon.com/cli/latest/reference/s3api/list-parts.html) + - [upload-part](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part.html) + - [upload-part-copy](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part-copy.html). + """ + + AWS_S3_DELETE = "aws.s3.delete" + """ + The delete request container that specifies the objects to be deleted. + Note: The `delete` attribute is only applicable to the [delete-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/delete-object.html) operation. + The `delete` attribute corresponds to the `--delete` parameter of the + [delete-objects operation within the S3 API](https://docs.aws.amazon.com/cli/latest/reference/s3api/delete-objects.html). + """ + + AWS_S3_PART_NUMBER = "aws.s3.part_number" + """ + The part number of the part being uploaded in a multipart-upload operation. This is a positive integer between 1 and 10,000. + Note: The `part_number` attribute is only applicable to the [upload-part](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part.html) + and [upload-part-copy](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part-copy.html) operations. + The `part_number` attribute corresponds to the `--part-number` parameter of the + [upload-part operation within the S3 API](https://docs.aws.amazon.com/cli/latest/reference/s3api/upload-part.html). + """ + + GRAPHQL_OPERATION_NAME = "graphql.operation.name" + """ + The name of the operation being executed. + """ + + GRAPHQL_OPERATION_TYPE = "graphql.operation.type" + """ + The type of the operation being executed. + """ + + GRAPHQL_DOCUMENT = "graphql.document" + """ + The GraphQL document being executed. + Note: The value may be sanitized to exclude sensitive information. + """ + + MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY = "messaging.rabbitmq.destination.routing_key" + """ + RabbitMQ message routing key. + """ + + MESSAGING_KAFKA_MESSAGE_KEY = "messaging.kafka.message.key" + """ + Message keys in Kafka are used for grouping alike messages to ensure they're processed on the same partition. They differ from `messaging.message.id` in that they're not unique. If the key is `null`, the attribute MUST NOT be set. + Note: If the key type is not string, it's string representation has to be supplied for the attribute. If the key has no unambiguous, canonical string form, don't include its value. + """ + + MESSAGING_KAFKA_CONSUMER_GROUP = "messaging.kafka.consumer.group" + """ + Name of the Kafka Consumer Group that is handling the message. Only applies to consumers, not producers. + """ + + MESSAGING_KAFKA_DESTINATION_PARTITION = "messaging.kafka.destination.partition" + """ + Partition the message is sent to. + """ + + MESSAGING_KAFKA_MESSAGE_OFFSET = "messaging.kafka.message.offset" + """ + The offset of a record in the corresponding Kafka partition. + """ + + MESSAGING_KAFKA_MESSAGE_TOMBSTONE = "messaging.kafka.message.tombstone" + """ + A boolean that is true if the message is a tombstone. + """ + + MESSAGING_ROCKETMQ_NAMESPACE = "messaging.rocketmq.namespace" + """ + Namespace of RocketMQ resources, resources in different namespaces are individual. + """ + + MESSAGING_ROCKETMQ_CLIENT_GROUP = "messaging.rocketmq.client_group" + """ + Name of the RocketMQ producer/consumer group that is handling the message. The client type is identified by the SpanKind. + """ + + MESSAGING_ROCKETMQ_MESSAGE_DELIVERY_TIMESTAMP = "messaging.rocketmq.message.delivery_timestamp" + """ + The timestamp in milliseconds that the delay message is expected to be delivered to consumer. + """ + + MESSAGING_ROCKETMQ_MESSAGE_DELAY_TIME_LEVEL = "messaging.rocketmq.message.delay_time_level" + """ + The delay time level for delay message, which determines the message delay time. + """ + + MESSAGING_ROCKETMQ_MESSAGE_GROUP = "messaging.rocketmq.message.group" + """ + It is essential for FIFO message. Messages that belong to the same message group are always processed one by one within the same consumer group. + """ + + MESSAGING_ROCKETMQ_MESSAGE_TYPE = "messaging.rocketmq.message.type" + """ + Type of message. + """ + + MESSAGING_ROCKETMQ_MESSAGE_TAG = "messaging.rocketmq.message.tag" + """ + The secondary classifier of message besides topic. + """ + + MESSAGING_ROCKETMQ_MESSAGE_KEYS = "messaging.rocketmq.message.keys" + """ + Key(s) of message, another way to mark message besides message id. + """ + + MESSAGING_ROCKETMQ_CONSUMPTION_MODEL = "messaging.rocketmq.consumption_model" + """ + Model of message consumption. This only applies to consumer spans. + """ + + RPC_GRPC_STATUS_CODE = "rpc.grpc.status_code" + """ + The [numeric status code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC request. + """ + + RPC_JSONRPC_VERSION = "rpc.jsonrpc.version" + """ + Protocol version as in `jsonrpc` property of request/response. Since JSON-RPC 1.0 does not specify this, the value can be omitted. + """ + + RPC_JSONRPC_REQUEST_ID = "rpc.jsonrpc.request_id" + """ + `id` property of request or response. Since protocol allows id to be int, string, `null` or missing (for notifications), value is expected to be cast to string for simplicity. Use empty string in case of `null` value. Omit entirely if this is a notification. + """ + + RPC_JSONRPC_ERROR_CODE = "rpc.jsonrpc.error_code" + """ + `error.code` property of response if it is an error response. + """ + + RPC_JSONRPC_ERROR_MESSAGE = "rpc.jsonrpc.error_message" + """ + `error.message` property of response if it is an error response. + """ + + MESSAGE_TYPE = "message.type" + """ + Whether this is a received or sent message. + """ + + MESSAGE_ID = "message.id" + """ + MUST be calculated as two different counters starting from `1` one for sent messages and one for received message. + Note: This way we guarantee that the values will be consistent between different implementations. + """ + + MESSAGE_COMPRESSED_SIZE = "message.compressed_size" + """ + Compressed size of the message in bytes. + """ + + MESSAGE_UNCOMPRESSED_SIZE = "message.uncompressed_size" + """ + Uncompressed size of the message in bytes. + """ + + RPC_CONNECT_RPC_ERROR_CODE = "rpc.connect_rpc.error_code" + """ + The [error codes](https://connect.build/docs/protocol/#error-codes) of the Connect request. Error codes are always string values. + """ + + EXCEPTION_ESCAPED = "exception.escaped" + """ + SHOULD be set to true if the exception event is recorded at a point where it is known that the exception is escaping the scope of the span. + Note: An exception is considered to have escaped (or left) the scope of a span, + if that span is ended while the exception is still logically "in flight". + This may be actually "in flight" in some languages (e.g. if the exception + is passed to a Context manager's `__exit__` method in Python) but will + usually be caught at the point of recording the exception in most languages. + + It is usually not possible to determine at the point where an exception is thrown + whether it will escape the scope of a span. + However, it is trivial to know that an exception + will escape, if one checks for an active exception just before ending the span, + as done in the [example above](#recording-an-exception). + + It follows that an exception may still escape the scope of the span + even if the `exception.escaped` attribute was not set or set to false, + since the event might have been recorded at a time where it was not + clear whether the exception will escape. + """ + + URL_FRAGMENT = "url.fragment" + """ + The [URI fragment](https://www.rfc-editor.org/rfc/rfc3986#section-3.5) component. + """ + + # Manually defined deprecated attributes + + NET_PEER_IP = "net.peer.ip" + """ + Deprecated, use the `client.socket.address` attribute. + """ + + NET_HOST_IP = "net.host.ip" + """ + Deprecated, use the `server.socket.address` attribute. + """ + + HTTP_SERVER_NAME = "http.server_name" + """ + Deprecated, use the `server.address` attribute. + """ + + HTTP_HOST = "http.host" + """ + Deprecated, use the `server.address` and `server.port` attributes. + """ + + HTTP_RETRY_COUNT = "http.retry_count" + """ + Deprecated, use the `http.resend_count` attribute. + """ + + HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED = "http.request_content_length_uncompressed" + """ + Deprecated, use the `http.request.body.size` attribute. + """ + + HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED = "http.response_content_length_uncompressed" + """ + Deprecated, use the `http.response.body.size` attribute. + """ + + MESSAGING_DESTINATION = "messaging.destination" + """ + Deprecated, use the `messaging.destination.name` attribute. + """ + + MESSAGING_DESTINATION_KIND = "messaging.destination_kind" + """ + Deprecated. + """ + + MESSAGING_TEMP_DESTINATION = "messaging.temp_destination" + """ + Deprecated. Use `messaging.destination.temporary` attribute. + """ + + MESSAGING_PROTOCOL = "messaging.protocol" + """ + Deprecated. Use `network.protocol.name` attribute. + """ + + MESSAGING_PROTOCOL_VERSION = "messaging.protocol_version" + """ + Deprecated. Use `network.protocol.version` attribute. + """ + + MESSAGING_URL = "messaging.url" + """ + Deprecated. Use `server.address` and `server.port` attributes. + """ + + MESSAGING_CONVERSATION_ID = "messaging.conversation_id" + """ + Deprecated. Use `messaging.message.conversation.id` attribute. + """ + + MESSAGING_KAFKA_PARTITION = "messaging.kafka.partition" + """ + Deprecated. Use `messaging.kafka.destination.partition` attribute. + """ + + FAAS_EXECUTION = "faas.execution" + """ + Deprecated. Use `faas.invocation_id` attribute. + """ + + HTTP_USER_AGENT = "http.user_agent" + """ + Deprecated. Use `user_agent.original` attribute. + """ + + MESSAGING_RABBITMQ_ROUTING_KEY = "messaging.rabbitmq.routing_key" + """ + Deprecated. Use `messaging.rabbitmq.destination.routing_key` attribute. + """ + + MESSAGING_KAFKA_TOMBSTONE = "messaging.kafka.tombstone" + """ + Deprecated. Use `messaging.kafka.destination.tombstone` attribute. + """ + + NET_APP_PROTOCOL_NAME = "net.app.protocol.name" + """ + Deprecated. Use `network.protocol.name` attribute. + """ + + NET_APP_PROTOCOL_VERSION = "net.app.protocol.version" + """ + Deprecated. Use `network.protocol.version` attribute. + """ + + HTTP_CLIENT_IP = "http.client_ip" + """ + Deprecated. Use `client.address` attribute. + """ + + HTTP_FLAVOR = "http.flavor" + """ + Deprecated. Use `network.protocol.name` and `network.protocol.version` attributes. + """ + + NET_HOST_CONNECTION_TYPE = "net.host.connection.type" + """ + Deprecated. Use `network.connection.type` attribute. + """ + + NET_HOST_CONNECTION_SUBTYPE = "net.host.connection.subtype" + """ + Deprecated. Use `network.connection.subtype` attribute. + """ + + NET_HOST_CARRIER_NAME = "net.host.carrier.name" + """ + Deprecated. Use `network.carrier.name` attribute. + """ + + NET_HOST_CARRIER_MCC = "net.host.carrier.mcc" + """ + Deprecated. Use `network.carrier.mcc` attribute. + """ + + NET_HOST_CARRIER_MNC = "net.host.carrier.mnc" + """ + Deprecated. Use `network.carrier.mnc` attribute. + """ + + MESSAGING_CONSUMER_ID = "messaging.consumer_id" + """ + Deprecated. Use `messaging.client_id` attribute. + """ + + MESSAGING_KAFKA_CLIENT_ID = "messaging.kafka.client_id" + """ + Deprecated. Use `messaging.client_id` attribute. + """ + + MESSAGING_ROCKETMQ_CLIENT_ID = "messaging.rocketmq.client_id" + """ + Deprecated. Use `messaging.client_id` attribute. + """ + + +@deprecated( + "Removed from the specification in favor of `network.protocol.name` and `network.protocol.version` attributes. Deprecated since version 1.18.0.", +) +class HttpFlavorValues(Enum): + HTTP_1_0 = "1.0" + + HTTP_1_1 = "1.1" + + HTTP_2_0 = "2.0" + + HTTP_3_0 = "3.0" + + SPDY = "SPDY" + + QUIC = "QUIC" + + +@deprecated( + "Removed from the specification. Deprecated since version 1.18.0.", +) +class MessagingDestinationKindValues(Enum): + QUEUE = "queue" + """A message sent to a queue.""" + + TOPIC = "topic" + """A message sent to a topic.""" + + +@deprecated( + "Renamed to NetworkConnectionTypeValues. Deprecated since version 1.21.0.", +) +class NetHostConnectionTypeValues(Enum): + WIFI = "wifi" + """wifi.""" + + WIRED = "wired" + """wired.""" + + CELL = "cell" + """cell.""" + + UNAVAILABLE = "unavailable" + """unavailable.""" + + UNKNOWN = "unknown" + """unknown.""" + + +@deprecated( + "Renamed to NetworkConnectionSubtypeValues. Deprecated since version 1.21.0.", +) +class NetHostConnectionSubtypeValues(Enum): + GPRS = "gprs" + """GPRS.""" + + EDGE = "edge" + """EDGE.""" + + UMTS = "umts" + """UMTS.""" + + CDMA = "cdma" + """CDMA.""" + + EVDO_0 = "evdo_0" + """EVDO Rel. 0.""" + + EVDO_A = "evdo_a" + """EVDO Rev. A.""" + + CDMA2000_1XRTT = "cdma2000_1xrtt" + """CDMA2000 1XRTT.""" + + HSDPA = "hsdpa" + """HSDPA.""" + + HSUPA = "hsupa" + """HSUPA.""" + + HSPA = "hspa" + """HSPA.""" + + IDEN = "iden" + """IDEN.""" + + EVDO_B = "evdo_b" + """EVDO Rev. B.""" + + LTE = "lte" + """LTE.""" + + EHRPD = "ehrpd" + """EHRPD.""" + + HSPAP = "hspap" + """HSPAP.""" + + GSM = "gsm" + """GSM.""" + + TD_SCDMA = "td_scdma" + """TD-SCDMA.""" + + IWLAN = "iwlan" + """IWLAN.""" + + NR = "nr" + """5G NR (New Radio).""" + + NRNSA = "nrnsa" + """5G NRNSA (New Radio Non-Standalone).""" + + LTE_CA = "lte_ca" + """LTE CA.""" + + +@deprecated( + "Use :py:const:`opentelemetry.semconv.attributes.NetworkTransportValues` instead. Deprecated since version 1.25.0.", +) +class NetTransportValues(Enum): + IP_TCP = "ip_tcp" + """ip_tcp.""" + + IP_UDP = "ip_udp" + """ip_udp.""" + + PIPE = "pipe" + """Named or anonymous pipe.""" + + INPROC = "inproc" + """In-process communication.""" + + OTHER = "other" + """Something else (non IP-based).""" + + +@deprecated( + "Use :py:const:`opentelemetry.semconv.attributes.NetworkType` instead. Deprecated since version 1.25.0.", +) +class NetSockFamilyValues(Enum): + INET = "inet" + """IPv4 address.""" + + INET6 = "inet6" + """IPv6 address.""" + + UNIX = "unix" + """Unix domain socket path.""" + + +@deprecated( + "Use :py:const:`opentelemetry.semconv.attributes.HttpRequestMethodValues` instead. Deprecated since version 1.25.0.", +) +class HttpRequestMethodValues(Enum): + CONNECT = "CONNECT" + """CONNECT method.""" + + DELETE = "DELETE" + """DELETE method.""" + + GET = "GET" + """GET method.""" + + HEAD = "HEAD" + """HEAD method.""" + + OPTIONS = "OPTIONS" + """OPTIONS method.""" + + PATCH = "PATCH" + """PATCH method.""" + + POST = "POST" + """POST method.""" + + PUT = "PUT" + """PUT method.""" + + TRACE = "TRACE" + """TRACE method.""" + + OTHER = "_OTHER" + """Any HTTP method that the instrumentation has no prior knowledge of.""" + + +@deprecated("Removed from the specification. Deprecated since version 1.25.0.") +class EventDomainValues(Enum): + BROWSER = "browser" + """Events from browser apps.""" + + DEVICE = "device" + """Events from mobile apps.""" + + K8S = "k8s" + """Events from Kubernetes.""" + + +@deprecated( + "Use :py:const:`opentelemetry.semconv._incubating.attributes.LogIostreamValues` instead. Deprecated since version 1.25.0.", +) +class LogIostreamValues(Enum): + STDOUT = "stdout" + """Logs from stdout stream.""" + + STDERR = "stderr" + """Events from stderr stream.""" + + +@deprecated("Removed from the specification. Deprecated since version 1.25.0.") +class TypeValues(Enum): + HEAP = "heap" + """Heap memory.""" + + NON_HEAP = "non_heap" + """Non-heap memory.""" + + +@deprecated( + "Use :py:const:`opentelemetry.semconv._incubating.attributes.OpentracingRefTypeValues` instead. Deprecated since version 1.25.0.", +) +class OpentracingRefTypeValues(Enum): + CHILD_OF = "child_of" + """The parent Span depends on the child Span in some capacity.""" + + FOLLOWS_FROM = "follows_from" + """The parent Span does not depend in any way on the result of the child Span.""" + + +class DbSystemValues(Enum): + OTHER_SQL = "other_sql" + """Some other SQL database. Fallback only. See notes.""" + + MSSQL = "mssql" + """Microsoft SQL Server.""" + + MSSQLCOMPACT = "mssqlcompact" + """Microsoft SQL Server Compact.""" + + MYSQL = "mysql" + """MySQL.""" + + ORACLE = "oracle" + """Oracle Database.""" + + DB2 = "db2" + """IBM Db2.""" + + POSTGRESQL = "postgresql" + """PostgreSQL.""" + + REDSHIFT = "redshift" + """Amazon Redshift.""" + + HIVE = "hive" + """Apache Hive.""" + + CLOUDSCAPE = "cloudscape" + """Cloudscape.""" + + HSQLDB = "hsqldb" + """HyperSQL DataBase.""" + + PROGRESS = "progress" + """Progress Database.""" + + MAXDB = "maxdb" + """SAP MaxDB.""" + + HANADB = "hanadb" + """SAP HANA.""" + + INGRES = "ingres" + """Ingres.""" + + FIRSTSQL = "firstsql" + """FirstSQL.""" + + EDB = "edb" + """EnterpriseDB.""" + + CACHE = "cache" + """InterSystems Caché.""" + + ADABAS = "adabas" + """Adabas (Adaptable Database System).""" + + FIREBIRD = "firebird" + """Firebird.""" + + DERBY = "derby" + """Apache Derby.""" + + FILEMAKER = "filemaker" + """FileMaker.""" + + INFORMIX = "informix" + """Informix.""" + + INSTANTDB = "instantdb" + """InstantDB.""" + + INTERBASE = "interbase" + """InterBase.""" + + MARIADB = "mariadb" + """MariaDB.""" + + NETEZZA = "netezza" + """Netezza.""" + + PERVASIVE = "pervasive" + """Pervasive PSQL.""" + + POINTBASE = "pointbase" + """PointBase.""" + + SQLITE = "sqlite" + """SQLite.""" + + SYBASE = "sybase" + """Sybase.""" + + TERADATA = "teradata" + """Teradata.""" + + VERTICA = "vertica" + """Vertica.""" + + H2 = "h2" + """H2.""" + + COLDFUSION = "coldfusion" + """ColdFusion IMQ.""" + + CASSANDRA = "cassandra" + """Apache Cassandra.""" + + HBASE = "hbase" + """Apache HBase.""" + + MONGODB = "mongodb" + """MongoDB.""" + + REDIS = "redis" + """Redis.""" + + COUCHBASE = "couchbase" + """Couchbase.""" + + COUCHDB = "couchdb" + """CouchDB.""" + + COSMOSDB = "cosmosdb" + """Microsoft Azure Cosmos DB.""" + + DYNAMODB = "dynamodb" + """Amazon DynamoDB.""" + + NEO4J = "neo4j" + """Neo4j.""" + + GEODE = "geode" + """Apache Geode.""" + + ELASTICSEARCH = "elasticsearch" + """Elasticsearch.""" + + MEMCACHED = "memcached" + """Memcached.""" + + COCKROACHDB = "cockroachdb" + """CockroachDB.""" + + OPENSEARCH = "opensearch" + """OpenSearch.""" + + CLICKHOUSE = "clickhouse" + """ClickHouse.""" + + SPANNER = "spanner" + """Cloud Spanner.""" + + TRINO = "trino" + """Trino.""" + + +class NetworkTransportValues(Enum): + TCP = "tcp" + """TCP.""" + + UDP = "udp" + """UDP.""" + + PIPE = "pipe" + """Named or anonymous pipe. See note below.""" + + UNIX = "unix" + """Unix domain socket.""" + + +class NetworkTypeValues(Enum): + IPV4 = "ipv4" + """IPv4.""" + + IPV6 = "ipv6" + """IPv6.""" + + +class DbCassandraConsistencyLevelValues(Enum): + ALL = "all" + """all.""" + + EACH_QUORUM = "each_quorum" + """each_quorum.""" + + QUORUM = "quorum" + """quorum.""" + + LOCAL_QUORUM = "local_quorum" + """local_quorum.""" + + ONE = "one" + """one.""" + + TWO = "two" + """two.""" + + THREE = "three" + """three.""" + + LOCAL_ONE = "local_one" + """local_one.""" + + ANY = "any" + """any.""" + + SERIAL = "serial" + """serial.""" + + LOCAL_SERIAL = "local_serial" + """local_serial.""" + + +class DbCosmosdbOperationTypeValues(Enum): + INVALID = "Invalid" + """invalid.""" + + CREATE = "Create" + """create.""" + + PATCH = "Patch" + """patch.""" + + READ = "Read" + """read.""" + + READ_FEED = "ReadFeed" + """read_feed.""" + + DELETE = "Delete" + """delete.""" + + REPLACE = "Replace" + """replace.""" + + EXECUTE = "Execute" + """execute.""" + + QUERY = "Query" + """query.""" + + HEAD = "Head" + """head.""" + + HEAD_FEED = "HeadFeed" + """head_feed.""" + + UPSERT = "Upsert" + """upsert.""" + + BATCH = "Batch" + """batch.""" + + QUERY_PLAN = "QueryPlan" + """query_plan.""" + + EXECUTE_JAVASCRIPT = "ExecuteJavaScript" + """execute_javascript.""" + + +class DbCosmosdbConnectionModeValues(Enum): + GATEWAY = "gateway" + """Gateway (HTTP) connections mode.""" + + DIRECT = "direct" + """Direct connection.""" + + +class OtelStatusCodeValues(Enum): + OK = "OK" + """The operation has been validated by an Application developer or Operator to have completed successfully.""" + + ERROR = "ERROR" + """The operation contains an error.""" + + +class FaasTriggerValues(Enum): + DATASOURCE = "datasource" + """A response to some data source operation such as a database or filesystem read/write.""" + + HTTP = "http" + """To provide an answer to an inbound HTTP request.""" + + PUBSUB = "pubsub" + """A function is set to be executed when messages are sent to a messaging system.""" + + TIMER = "timer" + """A function is scheduled to be executed regularly.""" + + OTHER = "other" + """If none of the others apply.""" + + +class FaasDocumentOperationValues(Enum): + INSERT = "insert" + """When a new object is created.""" + + EDIT = "edit" + """When an object is modified.""" + + DELETE = "delete" + """When an object is deleted.""" + + +class MessagingOperationValues(Enum): + PUBLISH = "publish" + """publish.""" + + RECEIVE = "receive" + """receive.""" + + PROCESS = "process" + """process.""" + + +class FaasInvokedProviderValues(Enum): + ALIBABA_CLOUD = "alibaba_cloud" + """Alibaba Cloud.""" + + AWS = "aws" + """Amazon Web Services.""" + + AZURE = "azure" + """Microsoft Azure.""" + + GCP = "gcp" + """Google Cloud Platform.""" + + TENCENT_CLOUD = "tencent_cloud" + """Tencent Cloud.""" + + +class NetworkConnectionTypeValues(Enum): + WIFI = "wifi" + """wifi.""" + + WIRED = "wired" + """wired.""" + + CELL = "cell" + """cell.""" + + UNAVAILABLE = "unavailable" + """unavailable.""" + + UNKNOWN = "unknown" + """unknown.""" + + +class NetworkConnectionSubtypeValues(Enum): + GPRS = "gprs" + """GPRS.""" + + EDGE = "edge" + """EDGE.""" + + UMTS = "umts" + """UMTS.""" + + CDMA = "cdma" + """CDMA.""" + + EVDO_0 = "evdo_0" + """EVDO Rel. 0.""" + + EVDO_A = "evdo_a" + """EVDO Rev. A.""" + + CDMA2000_1XRTT = "cdma2000_1xrtt" + """CDMA2000 1XRTT.""" + + HSDPA = "hsdpa" + """HSDPA.""" + + HSUPA = "hsupa" + """HSUPA.""" + + HSPA = "hspa" + """HSPA.""" + + IDEN = "iden" + """IDEN.""" + + EVDO_B = "evdo_b" + """EVDO Rev. B.""" + + LTE = "lte" + """LTE.""" + + EHRPD = "ehrpd" + """EHRPD.""" + + HSPAP = "hspap" + """HSPAP.""" + + GSM = "gsm" + """GSM.""" + + TD_SCDMA = "td_scdma" + """TD-SCDMA.""" + + IWLAN = "iwlan" + """IWLAN.""" + + NR = "nr" + """5G NR (New Radio).""" + + NRNSA = "nrnsa" + """5G NRNSA (New Radio Non-Standalone).""" + + LTE_CA = "lte_ca" + """LTE CA.""" + + +class RpcSystemValues(Enum): + GRPC = "grpc" + """gRPC.""" + + JAVA_RMI = "java_rmi" + """Java RMI.""" + + DOTNET_WCF = "dotnet_wcf" + """.NET WCF.""" + + APACHE_DUBBO = "apache_dubbo" + """Apache Dubbo.""" + + CONNECT_RPC = "connect_rpc" + """Connect RPC.""" + + +class GraphqlOperationTypeValues(Enum): + QUERY = "query" + """GraphQL query.""" + + MUTATION = "mutation" + """GraphQL mutation.""" + + SUBSCRIPTION = "subscription" + """GraphQL subscription.""" + + +class MessagingRocketmqMessageTypeValues(Enum): + NORMAL = "normal" + """Normal message.""" + + FIFO = "fifo" + """FIFO message.""" + + DELAY = "delay" + """Delay message.""" + + TRANSACTION = "transaction" + """Transaction message.""" + + +class MessagingRocketmqConsumptionModelValues(Enum): + CLUSTERING = "clustering" + """Clustering consumption model.""" + + BROADCASTING = "broadcasting" + """Broadcasting consumption model.""" + + +class RpcGrpcStatusCodeValues(Enum): + OK = 0 + """OK.""" + + CANCELLED = 1 + """CANCELLED.""" + + UNKNOWN = 2 + """UNKNOWN.""" + + INVALID_ARGUMENT = 3 + """INVALID_ARGUMENT.""" + + DEADLINE_EXCEEDED = 4 + """DEADLINE_EXCEEDED.""" + + NOT_FOUND = 5 + """NOT_FOUND.""" + + ALREADY_EXISTS = 6 + """ALREADY_EXISTS.""" + + PERMISSION_DENIED = 7 + """PERMISSION_DENIED.""" + + RESOURCE_EXHAUSTED = 8 + """RESOURCE_EXHAUSTED.""" + + FAILED_PRECONDITION = 9 + """FAILED_PRECONDITION.""" + + ABORTED = 10 + """ABORTED.""" + + OUT_OF_RANGE = 11 + """OUT_OF_RANGE.""" + + UNIMPLEMENTED = 12 + """UNIMPLEMENTED.""" + + INTERNAL = 13 + """INTERNAL.""" + + UNAVAILABLE = 14 + """UNAVAILABLE.""" + + DATA_LOSS = 15 + """DATA_LOSS.""" + + UNAUTHENTICATED = 16 + """UNAUTHENTICATED.""" + + +class MessageTypeValues(Enum): + SENT = "SENT" + """sent.""" + + RECEIVED = "RECEIVED" + """received.""" + + +class RpcConnectRpcErrorCodeValues(Enum): + CANCELLED = "cancelled" + """cancelled.""" + + UNKNOWN = "unknown" + """unknown.""" + + INVALID_ARGUMENT = "invalid_argument" + """invalid_argument.""" + + DEADLINE_EXCEEDED = "deadline_exceeded" + """deadline_exceeded.""" + + NOT_FOUND = "not_found" + """not_found.""" + + ALREADY_EXISTS = "already_exists" + """already_exists.""" + + PERMISSION_DENIED = "permission_denied" + """permission_denied.""" + + RESOURCE_EXHAUSTED = "resource_exhausted" + """resource_exhausted.""" + + FAILED_PRECONDITION = "failed_precondition" + """failed_precondition.""" + + ABORTED = "aborted" + """aborted.""" + + OUT_OF_RANGE = "out_of_range" + """out_of_range.""" + + UNIMPLEMENTED = "unimplemented" + """unimplemented.""" + + INTERNAL = "internal" + """internal.""" + + UNAVAILABLE = "unavailable" + """unavailable.""" + + DATA_LOSS = "data_loss" + """data_loss.""" + + UNAUTHENTICATED = "unauthenticated" + """unauthenticated.""" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/trace/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/trace/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c4169b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/trace/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/version/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/semconv/version/__init__.py new file mode 100644 index 0000000..d3741ff --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/semconv/version/__init__.py @@ -0,0 +1,4 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +__version__ = "0.66b0" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/semconv/version/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/semconv/version/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..229788d Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/semconv/version/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py new file mode 100644 index 0000000..cb8a540 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py @@ -0,0 +1,661 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +""" +The OpenTelemetry tracing API describes the classes used to generate +distributed traces. + +The :class:`.Tracer` class controls access to the execution context, and +manages span creation. Each operation in a trace is represented by a +:class:`.Span`, which records the start, end time, and metadata associated with +the operation. + +This module provides abstract (i.e. unimplemented) classes required for +tracing, and a concrete no-op :class:`.NonRecordingSpan` that allows applications +to use the API package alone without a supporting implementation. + +To get a tracer, you need to provide the package name from which you are +calling the tracer APIs to OpenTelemetry by calling `TracerProvider.get_tracer` +with the calling module name and the version of your package. + +The tracer supports creating spans that are "attached" or "detached" from the +context. New spans are "attached" to the context in that they are +created as children of the currently active span, and the newly-created span +can optionally become the new active span:: + + from opentelemetry import trace + + tracer = trace.get_tracer(__name__) + + # Create a new root span, set it as the current span in context + with tracer.start_as_current_span("parent"): + # Attach a new child and update the current span + with tracer.start_as_current_span("child"): + do_work(): + # Close child span, set parent as current + # Close parent span, set default span as current + +When creating a span that's "detached" from the context the active span doesn't +change, and the caller is responsible for managing the span's lifetime:: + + # Explicit parent span assignment is done via the Context + from opentelemetry.trace import set_span_in_context + + context = set_span_in_context(parent) + child = tracer.start_span("child", context=context) + + try: + do_work(span=child) + finally: + child.end() + +Applications should generally use a single global TracerProvider, and use +either implicit or explicit context propagation consistently throughout. + +.. versionadded:: 0.1.0 +.. versionchanged:: 0.3.0 + `TracerProvider` was introduced and the global ``tracer`` getter was + replaced by ``tracer_provider``. +.. versionchanged:: 0.5.0 + ``tracer_provider`` was replaced by `get_tracer_provider`, + ``set_preferred_tracer_provider_implementation`` was replaced by + `set_tracer_provider`. +""" + +import os +from abc import ABC, abstractmethod +from collections.abc import Iterator, Sequence +from enum import Enum +from logging import getLogger +from typing import cast + +from typing_extensions import deprecated + +from opentelemetry import context as context_api +from opentelemetry.attributes import BoundedAttributes +from opentelemetry.context.context import Context +from opentelemetry.environment_variables import OTEL_PYTHON_TRACER_PROVIDER +from opentelemetry.trace.propagation import ( + _SPAN_KEY, + get_current_span, + set_span_in_context, +) +from opentelemetry.trace.span import ( + DEFAULT_TRACE_OPTIONS, + DEFAULT_TRACE_STATE, + INVALID_SPAN, + INVALID_SPAN_CONTEXT, + INVALID_SPAN_ID, + INVALID_TRACE_ID, + NonRecordingSpan, + Span, + SpanContext, + TraceFlags, + TraceState, + format_span_id, + format_trace_id, +) +from opentelemetry.trace.status import Status, StatusCode +from opentelemetry.util import types +from opentelemetry.util._decorator import _agnosticcontextmanager +from opentelemetry.util._once import Once +from opentelemetry.util._providers import _load_provider + +logger = getLogger(__name__) + + +class _LinkBase(ABC): + def __init__(self, context: "SpanContext") -> None: + self._context = context + + @property + def context(self) -> "SpanContext": + return self._context + + @property + @abstractmethod + def attributes(self) -> types.Attributes: + pass + + +class Link(_LinkBase): + """A link to a `Span`. The attributes of a Link are immutable. + + Args: + context: `SpanContext` of the `Span` to link to. + attributes: Link's attributes. + """ + + def __init__( + self, + context: "SpanContext", + attributes: types.Attributes = None, + ) -> None: + super().__init__(context) + self._attributes = attributes + + @property + def attributes(self) -> types.Attributes: + return self._attributes + + @property + def dropped_attributes(self) -> int: + if isinstance(self._attributes, BoundedAttributes): + return self._attributes.dropped + return 0 + + +_Links = Sequence[Link] | None + + +class SpanKind(Enum): + """Specifies additional details on how this span relates to its parent span. + + Note that this enumeration is experimental and likely to change. See + https://github.com/open-telemetry/opentelemetry-specification/pull/226. + """ + + #: Default value. Indicates that the span is used internally in the + # application. + INTERNAL = 0 + + #: Indicates that the span describes an operation that handles a remote + # request. + SERVER = 1 + + #: Indicates that the span describes a request to some remote service. + CLIENT = 2 + + #: Indicates that the span describes a producer sending a message to a + #: broker. Unlike client and server, there is usually no direct critical + #: path latency relationship between producer and consumer spans. + PRODUCER = 3 + + #: Indicates that the span describes a consumer receiving a message from a + #: broker. Unlike client and server, there is usually no direct critical + #: path latency relationship between producer and consumer spans. + CONSUMER = 4 + + +class TracerProvider(ABC): + @abstractmethod + def get_tracer( + self, + instrumenting_module_name: str, + instrumenting_library_version: str | None = None, + schema_url: str | None = None, + attributes: types.Attributes = None, + ) -> "Tracer": + """Returns a `Tracer` for use by the given instrumentation library. + + For any two calls it is undefined whether the same or different + `Tracer` instances are returned, even for different library names. + + This function may return different `Tracer` types (e.g. a no-op tracer + vs. a functional tracer). + + Args: + instrumenting_module_name: The uniquely identifiable name for instrumentation + scope, such as instrumentation library, package, module or class name. + ``__name__`` should be avoided as this can result in + different tracer names if the tracers are in different files. + It is better to use a fixed string that can be imported where + needed and used consistently as the name of the tracer. + + This should *not* be the name of the module that is + instrumented but the name of the module doing the instrumentation. + E.g., instead of ``"requests"``, use + ``"opentelemetry.instrumentation.requests"``. + + instrumenting_library_version: Optional. The version string of the + instrumenting library. Usually this should be the same as + ``importlib.metadata.version(instrumenting_library_name)``. + + schema_url: Optional. Specifies the Schema URL of the emitted telemetry. + attributes: Optional. Specifies the attributes of the emitted telemetry. + """ + + +class NoOpTracerProvider(TracerProvider): + """The default TracerProvider, used when no implementation is available. + + All operations are no-op. + """ + + def get_tracer( + self, + instrumenting_module_name: str, + instrumenting_library_version: str | None = None, + schema_url: str | None = None, + attributes: types.Attributes = None, + ) -> "Tracer": + # pylint:disable=no-self-use,unused-argument + return NoOpTracer() + + +@deprecated("You should use NoOpTracerProvider. Deprecated since version 1.9.0.") +class _DefaultTracerProvider(NoOpTracerProvider): + """The default TracerProvider, used when no implementation is available. + + All operations are no-op. + """ + + +class ProxyTracerProvider(TracerProvider): + def get_tracer( + self, + instrumenting_module_name: str, + instrumenting_library_version: str | None = None, + schema_url: str | None = None, + attributes: types.Attributes = None, + ) -> "Tracer": + if _TRACER_PROVIDER: + return _TRACER_PROVIDER.get_tracer( + instrumenting_module_name, + instrumenting_library_version, + schema_url, + attributes, + ) + return ProxyTracer( + instrumenting_module_name, + instrumenting_library_version, + schema_url, + attributes, + ) + + +class Tracer(ABC): + """Handles span creation and in-process context propagation. + + This class provides methods for manipulating the context, creating spans, + and controlling spans' lifecycles. + """ + + @abstractmethod + def start_span( + self, + name: str, + context: Context | None = None, + kind: SpanKind = SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: _Links = None, + start_time: int | None = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + ) -> "Span": + """Starts a span. + + Create a new span. Start the span without setting it as the current + span in the context. To start the span and use the context in a single + method, see :meth:`start_as_current_span`. + + By default the current span in the context will be used as parent, but an + explicit context can also be specified, by passing in a `Context` containing + a current `Span`. If there is no current span in the global `Context` or in + the specified context, the created span will be a root span. + + The span can be used as a context manager. On exiting the context manager, + the span's end() method will be called. + + Example:: + + # trace.get_current_span() will be used as the implicit parent. + # If none is found, the created span will be a root instance. + with tracer.start_span("one") as child: + child.add_event("child's event") + + Args: + name: The name of the span to be created. + context: An optional Context containing the span's parent. Defaults to the + global context. + kind: The span's kind (relationship to parent). Note that is + meaningful even if there is no parent. + attributes: The span's attributes. + links: Links span to other spans + start_time: Sets the start time of a span + record_exception: Whether to record any exceptions raised within the + context as error event on the span. + set_status_on_exception: Only relevant if the returned span is used + in a with/context manager. Defines whether the span status will + be automatically set to ERROR when an uncaught exception is + raised in the span with block. The span status won't be set by + this mechanism if it was previously set manually. + + Returns: + The newly-created span. + """ + + @_agnosticcontextmanager + @abstractmethod + def start_as_current_span( + self, + name: str, + context: Context | None = None, + kind: SpanKind = SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: _Links = None, + start_time: int | None = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + end_on_exit: bool = True, + ) -> Iterator["Span"]: + """Context manager for creating a new span and set it + as the current span in this tracer's context. + + Exiting the context manager will call the span's end method, + as well as return the current span to its previous value by + returning to the previous context. + + Example:: + + with tracer.start_as_current_span("one") as parent: + parent.add_event("parent's event") + with tracer.start_as_current_span("two") as child: + child.add_event("child's event") + trace.get_current_span() # returns child + trace.get_current_span() # returns parent + trace.get_current_span() # returns previously active span + + This is a convenience method for creating spans attached to the + tracer's context. Applications that need more control over the span + lifetime should use :meth:`start_span` instead. For example:: + + with tracer.start_as_current_span(name) as span: + do_work() + + is equivalent to:: + + span = tracer.start_span(name) + with opentelemetry.trace.use_span(span, end_on_exit=True): + do_work() + + This can also be used as a decorator:: + + @tracer.start_as_current_span("name") + def function(): ... + + + function() + + Args: + name: The name of the span to be created. + context: An optional Context containing the span's parent. Defaults to the + global context. + kind: The span's kind (relationship to parent). Note that is + meaningful even if there is no parent. + attributes: The span's attributes. + links: Links span to other spans + start_time: Sets the start time of a span + record_exception: Whether to record any exceptions raised within the + context as error event on the span. + set_status_on_exception: Only relevant if the returned span is used + in a with/context manager. Defines whether the span status will + be automatically set to ERROR when an uncaught exception is + raised in the span with block. The span status won't be set by + this mechanism if it was previously set manually. + end_on_exit: Whether to end the span automatically when leaving the + context manager. + + Yields: + The newly-created span. + """ + + +class ProxyTracer(Tracer): + # pylint: disable=W0222,signature-differs + def __init__( + self, + instrumenting_module_name: str, + instrumenting_library_version: str | None = None, + schema_url: str | None = None, + attributes: types.Attributes = None, + ): + self._instrumenting_module_name = instrumenting_module_name + self._instrumenting_library_version = instrumenting_library_version + self._schema_url = schema_url + self._attributes = attributes + self._real_tracer: Tracer | None = None + self._noop_tracer = NoOpTracer() + + @property + def _tracer(self) -> Tracer: + if self._real_tracer: + return self._real_tracer + + if _TRACER_PROVIDER: + self._real_tracer = _TRACER_PROVIDER.get_tracer( + self._instrumenting_module_name, + self._instrumenting_library_version, + self._schema_url, + self._attributes, + ) + return self._real_tracer + return self._noop_tracer + + def start_span(self, *args, **kwargs) -> Span: # type: ignore + return self._tracer.start_span(*args, **kwargs) # type: ignore + + @_agnosticcontextmanager # type: ignore + def start_as_current_span(self, *args, **kwargs) -> Iterator[Span]: + with self._tracer.start_as_current_span(*args, **kwargs) as span: # type: ignore + yield span + + +class NoOpTracer(Tracer): + """The default Tracer, used when no Tracer implementation is available. + + All operations are no-op. + """ + + def start_span( + self, + name: str, + context: Context | None = None, + kind: SpanKind = SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: _Links = None, + start_time: int | None = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + ) -> "Span": + current_span = get_current_span(context) + if isinstance(current_span, NonRecordingSpan): + return current_span + parent_span_context = current_span.get_span_context() + if parent_span_context is not None and not isinstance(parent_span_context, SpanContext): + logger.warning( + "Invalid span context for %s: %s", + current_span, + parent_span_context, + ) + return INVALID_SPAN + + return NonRecordingSpan(context=parent_span_context) + + @_agnosticcontextmanager + def start_as_current_span( + self, + name: str, + context: Context | None = None, + kind: SpanKind = SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: _Links = None, + start_time: int | None = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + end_on_exit: bool = True, + ) -> Iterator["Span"]: + span = self.start_span( + name=name, + context=context, + kind=kind, + attributes=attributes, + links=links, + start_time=start_time, + record_exception=record_exception, + set_status_on_exception=set_status_on_exception, + ) + with use_span( + span, + end_on_exit=end_on_exit, + record_exception=record_exception, + set_status_on_exception=set_status_on_exception, + ) as span: + yield span + + +@deprecated("You should use NoOpTracer. Deprecated since version 1.9.0.") +class _DefaultTracer(NoOpTracer): + """The default Tracer, used when no Tracer implementation is available. + + All operations are no-op. + """ + + +_TRACER_PROVIDER_SET_ONCE = Once() +_TRACER_PROVIDER: TracerProvider | None = None +_PROXY_TRACER_PROVIDER = ProxyTracerProvider() + + +def get_tracer( + instrumenting_module_name: str, + instrumenting_library_version: str | None = None, + tracer_provider: TracerProvider | None = None, + schema_url: str | None = None, + attributes: types.Attributes = None, +) -> "Tracer": + """Returns a `Tracer` for use by the given instrumentation library. + + This function is a convenience wrapper for + opentelemetry.trace.TracerProvider.get_tracer. + + If tracer_provider is omitted the current configured one is used. + """ + if tracer_provider is None: + tracer_provider = get_tracer_provider() + return tracer_provider.get_tracer( + instrumenting_module_name, + instrumenting_library_version, + schema_url, + attributes, + ) + + +def _set_tracer_provider(tracer_provider: TracerProvider, log: bool) -> None: + def set_tp() -> None: + global _TRACER_PROVIDER # pylint: disable=global-statement + _TRACER_PROVIDER = tracer_provider + + did_set = _TRACER_PROVIDER_SET_ONCE.do_once(set_tp) + + if log and not did_set: + logger.warning("Overriding of current TracerProvider is not allowed") + + +def set_tracer_provider(tracer_provider: TracerProvider) -> None: + """Sets the current global :class:`~.TracerProvider` object. + + This can only be done once, a warning will be logged if any further attempt + is made. + """ + _set_tracer_provider(tracer_provider, log=True) + + +def get_tracer_provider() -> TracerProvider: + """Gets the current global :class:`~.TracerProvider` object.""" + if _TRACER_PROVIDER is None: + # if a global tracer provider has not been set either via code or env + # vars, return a proxy tracer provider + if OTEL_PYTHON_TRACER_PROVIDER not in os.environ: + return _PROXY_TRACER_PROVIDER + + tracer_provider: TracerProvider = _load_provider(OTEL_PYTHON_TRACER_PROVIDER, "tracer_provider") + _set_tracer_provider(tracer_provider, log=False) + # _TRACER_PROVIDER will have been set by one thread + return cast("TracerProvider", _TRACER_PROVIDER) + + +@_agnosticcontextmanager +def use_span( + span: Span, + end_on_exit: bool = False, + record_exception: bool = True, + set_status_on_exception: bool = True, +) -> Iterator[Span]: + """Takes a non-active span and activates it in the current context. + + Args: + span: The span that should be activated in the current context. + end_on_exit: Whether to end the span automatically when leaving the + context manager scope. + record_exception: Whether to record any exceptions raised within the + context as error event on the span. + set_status_on_exception: Only relevant if the returned span is used + in a with/context manager. Defines whether the span status will + be automatically set to ERROR when an uncaught exception is + raised in the span with block. The span status won't be set by + this mechanism if it was previously set manually. + """ + try: + token = context_api.attach(context_api.set_value(_SPAN_KEY, span)) + try: + yield span + finally: + context_api.detach(token) + + # Record only exceptions that inherit Exception class but not BaseException, because + # classes that directly inherit BaseException are not technically errors, e.g. GeneratorExit. + # See https://github.com/open-telemetry/opentelemetry-python/issues/4484 + except Exception as exc: # pylint: disable=broad-exception-caught + if isinstance(span, Span) and span.is_recording(): + # Record the exception as an event + if record_exception: + span.record_exception(exc) + + # Set status in case exception was raised + if set_status_on_exception: + span.set_status( + Status( + status_code=StatusCode.ERROR, + description=f"{type(exc).__name__}: {exc}", + ) + ) + + # This causes parent spans to set their status to ERROR and to record + # an exception as an event if a child span raises an exception even if + # such child span was started with both record_exception and + # set_status_on_exception attributes set to False. + raise + + finally: + if end_on_exit: + span.end() + + +__all__ = [ + "DEFAULT_TRACE_OPTIONS", + "DEFAULT_TRACE_STATE", + "INVALID_SPAN", + "INVALID_SPAN_CONTEXT", + "INVALID_SPAN_ID", + "INVALID_TRACE_ID", + "Link", + "NonRecordingSpan", + "Span", + "SpanContext", + "SpanKind", + "Status", + "StatusCode", + "TraceFlags", + "TraceState", + "Tracer", + "TracerProvider", + "format_span_id", + "format_trace_id", + "get_current_span", + "get_tracer", + "get_tracer_provider", + "set_span_in_context", + "set_tracer_provider", + "use_span", +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/trace/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/trace/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a9fc756 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/trace/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/trace/__pycache__/span.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/trace/__pycache__/span.cpython-312.pyc new file mode 100644 index 0000000..61db5df Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/trace/__pycache__/span.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/trace/__pycache__/status.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/trace/__pycache__/status.cpython-312.pyc new file mode 100644 index 0000000..9b6de47 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/trace/__pycache__/status.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/__init__.py new file mode 100644 index 0000000..93a0916 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/__init__.py @@ -0,0 +1,37 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from opentelemetry.context import create_key, get_value, set_value +from opentelemetry.context.context import Context +from opentelemetry.trace.span import INVALID_SPAN, Span + +SPAN_KEY = "current-span" +_SPAN_KEY = create_key("current-span") + + +def set_span_in_context(span: Span, context: Context | None = None) -> Context: + """Set the span in the given context. + + Args: + span: The Span to set. + context: a Context object. if one is not passed, the + default current context is used instead. + """ + ctx = set_value(_SPAN_KEY, span, context=context) + return ctx + + +def get_current_span(context: Context | None = None) -> Span: + """Retrieve the current span. + + Args: + context: A Context object. If one is not passed, the + default current context is used instead. + + Returns: + The Span set in the context if it exists. INVALID_SPAN otherwise. + """ + span = get_value(_SPAN_KEY, context=context) + if span is None or not isinstance(span, Span): + return INVALID_SPAN + return span diff --git a/venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..08c16e3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/__pycache__/tracecontext.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/__pycache__/tracecontext.cpython-312.pyc new file mode 100644 index 0000000..35d0a9a Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/__pycache__/tracecontext.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/tracecontext.py b/venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/tracecontext.py new file mode 100644 index 0000000..bb41c13 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/trace/propagation/tracecontext.py @@ -0,0 +1,99 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +# +import re + +from opentelemetry import trace +from opentelemetry.context.context import Context +from opentelemetry.propagators import textmap +from opentelemetry.trace import format_span_id, format_trace_id +from opentelemetry.trace.span import TraceState + + +class TraceContextTextMapPropagator(textmap.TextMapPropagator): + """Extracts and injects using w3c TraceContext's headers.""" + + _TRACEPARENT_HEADER_NAME = "traceparent" + _TRACESTATE_HEADER_NAME = "tracestate" + _TRACEPARENT_HEADER_FORMAT = "^[ \t]*([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})" + "(-.*)?[ \t]*$" + _TRACEPARENT_HEADER_FORMAT_RE = re.compile(_TRACEPARENT_HEADER_FORMAT) + + def extract( + self, + carrier: textmap.CarrierT, + context: Context | None = None, + getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter, + ) -> Context: + """Extracts SpanContext from the carrier. + + See `opentelemetry.propagators.textmap.TextMapPropagator.extract` + """ + if context is None: + context = Context() + + header = getter.get(carrier, self._TRACEPARENT_HEADER_NAME) + + if not header: + return context + + match = re.search(self._TRACEPARENT_HEADER_FORMAT_RE, header[0]) + if not match: + return context + + version: str = match.group(1) + trace_id: str = match.group(2) + span_id: str = match.group(3) + trace_flags: str = match.group(4) + + if trace_id == "0" * 32 or span_id == "0" * 16: + return context + + if version == "00": + if match.group(5): # type: ignore + return context + if version == "ff": + return context + + tracestate_headers = getter.get(carrier, self._TRACESTATE_HEADER_NAME) + if tracestate_headers is None: + tracestate = None + else: + tracestate = TraceState.from_header(tracestate_headers) + + span_context = trace.SpanContext( + trace_id=int(trace_id, 16), + span_id=int(span_id, 16), + is_remote=True, + trace_flags=trace.TraceFlags(int(trace_flags, 16)), + trace_state=tracestate, + ) + return trace.set_span_in_context(trace.NonRecordingSpan(span_context), context) + + def inject( + self, + carrier: textmap.CarrierT, + context: Context | None = None, + setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter, + ) -> None: + """Injects SpanContext into the carrier. + + See `opentelemetry.propagators.textmap.TextMapPropagator.inject` + """ + span = trace.get_current_span(context) + span_context = span.get_span_context() + if span_context == trace.INVALID_SPAN_CONTEXT: + return + traceparent_string = f"00-{format_trace_id(span_context.trace_id)}-{format_span_id(span_context.span_id)}-{span_context.trace_flags:02x}" + setter.set(carrier, self._TRACEPARENT_HEADER_NAME, traceparent_string) + if span_context.trace_state: + tracestate_string = span_context.trace_state.to_header() + setter.set(carrier, self._TRACESTATE_HEADER_NAME, tracestate_string) + + @property + def fields(self) -> set[str]: + """Returns a set with the fields set in `inject`. + + See + `opentelemetry.propagators.textmap.TextMapPropagator.fields` + """ + return {self._TRACEPARENT_HEADER_NAME, self._TRACESTATE_HEADER_NAME} diff --git a/venv/lib/python3.12/site-packages/opentelemetry/trace/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/trace/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/trace/span.py b/venv/lib/python3.12/site-packages/opentelemetry/trace/span.py new file mode 100644 index 0000000..85f114c --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/trace/span.py @@ -0,0 +1,616 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import abc +import logging +import re +import types as python_types +import typing +import warnings +from collections.abc import Iterator, Mapping, Sequence + +from opentelemetry.trace.status import Status, StatusCode +from opentelemetry.util import types + +# The key MUST begin with a lowercase letter or a digit, +# and can only contain lowercase letters (a-z), digits (0-9), +# underscores (_), dashes (-), asterisks (*), and forward slashes (/). +# For multi-tenant vendor scenarios, an at sign (@) can be used to +# prefix the vendor name. Vendors SHOULD set the tenant ID +# at the beginning of the key. + +# key = ( lcalpha ) 0*255( lcalpha / DIGIT / "_" / "-"/ "*" / "/" ) +# key = ( lcalpha / DIGIT ) 0*240( lcalpha / DIGIT / "_" / "-"/ "*" / "/" ) "@" lcalpha 0*13( lcalpha / DIGIT / "_" / "-"/ "*" / "/" ) +# lcalpha = %x61-7A ; a-z + +_KEY_FORMAT = ( + r"[a-z][_0-9a-z\-\*\/]{0,255}|" + r"[a-z0-9][_0-9a-z\-\*\/]{0,240}@[a-z][_0-9a-z\-\*\/]{0,13}" +) +_KEY_PATTERN = re.compile(_KEY_FORMAT) + +# The value is an opaque string containing up to 256 printable +# ASCII [RFC0020] characters (i.e., the range 0x20 to 0x7E) +# except comma (,) and (=). +# value = 0*255(chr) nblk-chr +# nblk-chr = %x21-2B / %x2D-3C / %x3E-7E +# chr = %x20 / nblk-chr + +_VALUE_FORMAT = r"[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]" +_VALUE_PATTERN = re.compile(_VALUE_FORMAT) + + +_TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS = 32 +_delimiter_pattern = re.compile(r"[ \t]*,[ \t]*") +_member_pattern = re.compile(f"({_KEY_FORMAT})(=)({_VALUE_FORMAT})[ \t]*") +_logger = logging.getLogger(__name__) + + +def _is_valid_pair(key: str, value: str) -> bool: + return ( + isinstance(key, str) + and _KEY_PATTERN.fullmatch(key) is not None + and isinstance(value, str) + and _VALUE_PATTERN.fullmatch(value) is not None + ) + + +class Span(abc.ABC): + """A span represents a single operation within a trace.""" + + @abc.abstractmethod + def end(self, end_time: int | None = None) -> None: + """Sets the current time as the span's end time. + + The span's end time is the wall time at which the operation finished. + + Only the first call to `end` should modify the span, and + implementations are free to ignore or raise on further calls. + """ + + @abc.abstractmethod + def get_span_context(self) -> SpanContext: + """Gets the span's SpanContext. + + Get an immutable, serializable identifier for this span that can be + used to create new child spans. + + Returns: + A :class:`opentelemetry.trace.SpanContext` with a copy of this span's immutable state. + """ + + @abc.abstractmethod + def set_attributes(self, attributes: Mapping[str, types.AnyValue]) -> None: + """Sets Attributes. + + Sets Attributes with the key and value passed as arguments dict. + + Note: The behavior of `None` value attributes is undefined, and hence + strongly discouraged. It is also preferred to set attributes at span + creation, instead of calling this method later since samplers can only + consider information already present during span creation. + """ + + @abc.abstractmethod + def set_attribute(self, key: str, value: types.AnyValue) -> None: + """Sets an Attribute. + + Sets a single Attribute with the key and value passed as arguments. + + Note: The behavior of `None` value attributes is undefined, and hence + strongly discouraged. It is also preferred to set attributes at span + creation, instead of calling this method later since samplers can only + consider information already present during span creation. + """ + + @abc.abstractmethod + def add_event( + self, + name: str, + attributes: types.Attributes = None, + timestamp: int | None = None, + ) -> None: + """Adds an `Event`. + + Adds a single `Event` with the name and, optionally, a timestamp and + attributes passed as arguments. Implementations should generate a + timestamp if the `timestamp` argument is omitted. + """ + + def add_link( # pylint: disable=no-self-use + self, + context: SpanContext, + attributes: types.Attributes = None, + ) -> None: + """Adds a `Link`. + + Adds a single `Link` with the `SpanContext` of the span to link to and, + optionally, attributes passed as arguments. Implementations may ignore + calls with an invalid span context if both attributes and TraceState + are empty. + + Note: It is preferred to add links at span creation, instead of calling + this method later since samplers can only consider information already + present during span creation. + """ + warnings.warn( + "Span.add_link() not implemented and will be a no-op. " + "Use opentelemetry-sdk >= 1.23 to add links after span creation" + ) + + @abc.abstractmethod + def update_name(self, name: str) -> None: + """Updates the `Span` name. + + This will override the name provided via :func:`opentelemetry.trace.Tracer.start_span`. + + Upon this update, any sampling behavior based on Span name will depend + on the implementation. + """ + + @abc.abstractmethod + def is_recording(self) -> bool: + """Returns whether this span will be recorded. + + Returns true if this Span is active and recording information like + events with the add_event operation and attributes using set_attribute. + """ + + @abc.abstractmethod + def set_status( + self, + status: Status | StatusCode, + description: str | None = None, + ) -> None: + """Sets the Status of the Span. If used, this will override the default + Span status. + """ + + @abc.abstractmethod + def record_exception( + self, + exception: BaseException, + attributes: types.Attributes = None, + timestamp: int | None = None, + escaped: bool = False, + ) -> None: + """Records an exception as a span event.""" + + def __enter__(self) -> Span: + """Invoked when `Span` is used as a context manager. + + Returns the `Span` itself. + """ + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: python_types.TracebackType | None, + ) -> None: + """Ends context manager and calls `end` on the `Span`.""" + + self.end() + + +class TraceFlags(int): + """A bitmask that represents options specific to the trace. + + Supported flags: + - "sampled" (``0x01``): Indicates the trace may have been sampled upstream. + - "random-trace-id" (``0x02``): Indicates the trace ID was generated + randomly, with at least the 7 rightmost bytes (56 bits) selected with + uniform distribution. + + See the `W3C Trace Context - Traceparent`_ spec for details. + + .. _W3C Trace Context - Traceparent: + https://www.w3.org/TR/trace-context-2/#trace-flags + """ + + DEFAULT = 0x00 + SAMPLED = 0x01 + RANDOM_TRACE_ID = 0x02 + + @classmethod + def get_default(cls) -> TraceFlags: + return cls(cls.DEFAULT) + + @property + def sampled(self) -> bool: + return bool(self & TraceFlags.SAMPLED) + + @property + def random_trace_id(self) -> bool: + return bool(self & TraceFlags.RANDOM_TRACE_ID) + + +DEFAULT_TRACE_OPTIONS = TraceFlags.get_default() + + +class TraceState(Mapping[str, str]): + """A list of key-value pairs representing vendor-specific trace info. + + Keys and values are strings of up to 256 printable US-ASCII characters. + Implementations should conform to the `W3C Trace Context - Tracestate`_ + spec, which describes additional restrictions on valid field values. + + .. _W3C Trace Context - Tracestate: + https://www.w3.org/TR/trace-context/#tracestate-field + """ + + def __init__( + self, + entries: Sequence[tuple[str, str]] | None = None, + ) -> None: + self._dict = {} # type: dict[str, str] + if entries is None: + return + if len(entries) > _TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS: + _logger.warning( + "There can't be more than %s key/value pairs.", + _TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS, + ) + return + + for key, value in entries: + if _is_valid_pair(key, value): + if key in self._dict: + _logger.warning("Duplicate key: %s found.", key) + continue + self._dict[key] = value + else: + _logger.warning("Invalid key/value pair (%s, %s) found.", key, value) + + def __contains__(self, item: object) -> bool: + return item in self._dict + + def __getitem__(self, key: str) -> str: + return self._dict[key] + + def __iter__(self) -> Iterator[str]: + return iter(self._dict) + + def __len__(self) -> int: + return len(self._dict) + + def __repr__(self) -> str: + pairs = [f"{{key={key}, value={value}}}" for key, value in self._dict.items()] + return str(pairs) + + def add(self, key: str, value: str) -> TraceState: + """Adds a key-value pair to tracestate. The provided pair should + adhere to w3c tracestate identifiers format. + + Args: + key: A valid tracestate key to add + value: A valid tracestate value to add + + Returns: + A new TraceState with the modifications applied. + + If the provided key-value pair is invalid or results in tracestate + that violates tracecontext specification, they are discarded and + same tracestate will be returned. + """ + if not _is_valid_pair(key, value): + _logger.warning("Invalid key/value pair (%s, %s) found.", key, value) + return self + # There can be a maximum of 32 pairs + if len(self) >= _TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS: + _logger.warning("There can't be more 32 key/value pairs.") + return self + # Duplicate entries are not allowed + if key in self._dict: + _logger.warning("The provided key %s already exists.", key) + return self + new_state = [(key, value)] + list(self._dict.items()) + return TraceState(new_state) + + def update(self, key: str, value: str) -> TraceState: + """Updates a key-value pair in tracestate. The provided pair should + adhere to w3c tracestate identifiers format. + + Note: + This method performs an "upsert": if ``key`` is not already present + it is added (when the tracestate is below the 32-entry limit), + otherwise its value is updated. This upsert behaviour is intentional + but goes beyond what the OpenTelemetry specification defines for + ``update`` (https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#tracestate), + and is kept for backwards compatibility with callers that rely on it. + + Args: + key: A valid tracestate key to update + value: A valid tracestate value to update for key + + Returns: + A new TraceState with the modifications applied. + + If the provided pair is invalid, or adding a new key would exceed + the maximum of 32 key/value pairs, they are discarded and the same + tracestate is returned unchanged. Updating an existing key is always + allowed, even at the maximum. + """ + if not _is_valid_pair(key, value): + _logger.warning("Invalid key/value pair (%s, %s) found.", key, value) + return self + # Adding a new key at the maximum would push the tracestate over the + # limit and cause the constructor to drop every entry. Return unchanged + # instead of silently discarding existing state. + if key not in self._dict and len(self._dict) >= _TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS: + _logger.warning("There can't be more 32 key/value pairs.") + return self + prev_state = self._dict.copy() + prev_state.pop(key, None) + new_state = [(key, value), *prev_state.items()] + return TraceState(new_state) + + def delete(self, key: str) -> TraceState: + """Deletes a key-value from tracestate. + + Args: + key: A valid tracestate key to remove key-value pair from tracestate + + Returns: + A new TraceState with the modifications applied. + + If the provided key-value pair is invalid or results in tracestate + that violates tracecontext specification, they are discarded and + same tracestate will be returned. + """ + if key not in self._dict: + _logger.warning("The provided key %s doesn't exist.", key) + return self + prev_state = self._dict.copy() + prev_state.pop(key) + new_state = list(prev_state.items()) + return TraceState(new_state) + + def to_header(self) -> str: + """Creates a w3c tracestate header from a TraceState. + + Returns: + A string that adheres to the w3c tracestate + header format. + """ + return ",".join(key + "=" + value for key, value in self._dict.items()) + + @classmethod + def from_header(cls, header_list: list[str]) -> TraceState: + """Parses one or more w3c tracestate header into a TraceState. + + Args: + header_list: one or more w3c tracestate headers. + + Returns: + A valid TraceState that contains values extracted from + the tracestate header. + + If the format of one headers is illegal, all values will + be discarded and an empty tracestate will be returned. + + If the number of keys is beyond the maximum, all values + will be discarded and an empty tracestate will be returned. + """ + pairs = {} # type: dict[str, str] + for header in header_list: + members: list[str] = re.split(_delimiter_pattern, header) + for member in members: + # empty members are valid, but no need to process further. + if not member: + continue + match = _member_pattern.fullmatch(member) + if not match: + _logger.warning( + "Member doesn't match the w3c identifiers format %s", + member, + ) + return cls() + groups: tuple[str, ...] = match.groups() + key, _eq, value = groups + # duplicate keys are not legal in header + if key in pairs: + return cls() + pairs[key] = value + return cls(list(pairs.items())) + + @classmethod + def get_default(cls) -> TraceState: + return cls() + + def keys(self) -> typing.KeysView[str]: + return self._dict.keys() + + def items(self) -> typing.ItemsView[str, str]: + return self._dict.items() + + def values(self) -> typing.ValuesView[str]: + return self._dict.values() + + +DEFAULT_TRACE_STATE = TraceState.get_default() +_TRACE_ID_MAX_VALUE = 2**128 - 1 +_SPAN_ID_MAX_VALUE = 2**64 - 1 + + +class SpanContext(tuple[int, int, bool, "TraceFlags", "TraceState", bool]): + """The state of a Span to propagate between processes. + + This class includes the immutable attributes of a :class:`.Span` that must + be propagated to a span's children and across process boundaries. + + Args: + trace_id: The ID of the trace that this span belongs to. + span_id: This span's ID. + is_remote: True if propagated from a remote parent. + trace_flags: Trace options to propagate. + trace_state: Tracing-system-specific info to propagate. + """ + + def __new__( + cls, + trace_id: int, + span_id: int, + is_remote: bool, + trace_flags: TraceFlags | None = DEFAULT_TRACE_OPTIONS, + trace_state: TraceState | None = DEFAULT_TRACE_STATE, + ) -> SpanContext: + if trace_flags is None: + trace_flags = DEFAULT_TRACE_OPTIONS + if trace_state is None: + trace_state = DEFAULT_TRACE_STATE + + is_valid = ( + INVALID_TRACE_ID < trace_id <= _TRACE_ID_MAX_VALUE and INVALID_SPAN_ID < span_id <= _SPAN_ID_MAX_VALUE + ) + + return tuple.__new__( + cls, + (trace_id, span_id, is_remote, trace_flags, trace_state, is_valid), + ) + + def __getnewargs__( + self, + ) -> tuple[int, int, bool, TraceFlags, TraceState]: + return ( + self.trace_id, + self.span_id, + self.is_remote, + self.trace_flags, + self.trace_state, + ) + + @property + def trace_id(self) -> int: + return self[0] # pylint: disable=unsubscriptable-object + + @property + def span_id(self) -> int: + return self[1] # pylint: disable=unsubscriptable-object + + @property + def is_remote(self) -> bool: + return self[2] # pylint: disable=unsubscriptable-object + + @property + def trace_flags(self) -> TraceFlags: + return self[3] # pylint: disable=unsubscriptable-object + + @property + def trace_state(self) -> TraceState: + return self[4] # pylint: disable=unsubscriptable-object + + @property + def is_valid(self) -> bool: + return self[5] # pylint: disable=unsubscriptable-object + + def __setattr__(self, *args: str) -> None: + _logger.debug("Immutable type, ignoring call to set attribute", stack_info=True) + + def __delattr__(self, *args: str) -> None: + _logger.debug( + "Immutable type, ignoring call to delete attribute", + stack_info=True, + ) + + def __repr__(self) -> str: + return f"{type(self).__name__}(trace_id=0x{format_trace_id(self.trace_id)}, span_id=0x{format_span_id(self.span_id)}, trace_flags=0x{self.trace_flags:02x}, trace_state={self.trace_state!r}, is_remote={self.is_remote})" + + +class NonRecordingSpan(Span): + """The Span that is used when no Span implementation is available. + + All operations are no-op except context propagation. + """ + + def __init__(self, context: SpanContext) -> None: + self._context = context + + def get_span_context(self) -> SpanContext: + return self._context + + def is_recording(self) -> bool: + return False + + def end(self, end_time: int | None = None) -> None: + pass + + def set_attributes(self, attributes: Mapping[str, types.AnyValue]) -> None: + pass + + def set_attribute(self, key: str, value: types.AnyValue) -> None: + pass + + def add_event( + self, + name: str, + attributes: types.Attributes = None, + timestamp: int | None = None, + ) -> None: + pass + + def add_link( + self, + context: SpanContext, + attributes: types.Attributes = None, + ) -> None: + pass + + def update_name(self, name: str) -> None: + pass + + def set_status( + self, + status: Status | StatusCode, + description: str | None = None, + ) -> None: + pass + + def record_exception( + self, + exception: BaseException, + attributes: types.Attributes = None, + timestamp: int | None = None, + escaped: bool = False, + ) -> None: + pass + + def __repr__(self) -> str: + return f"NonRecordingSpan({self._context!r})" + + +INVALID_SPAN_ID = 0x0000000000000000 +INVALID_TRACE_ID = 0x00000000000000000000000000000000 +INVALID_SPAN_CONTEXT = SpanContext( + trace_id=INVALID_TRACE_ID, + span_id=INVALID_SPAN_ID, + is_remote=False, + trace_flags=DEFAULT_TRACE_OPTIONS, + trace_state=DEFAULT_TRACE_STATE, +) +INVALID_SPAN = NonRecordingSpan(INVALID_SPAN_CONTEXT) + + +def format_trace_id(trace_id: int) -> str: + """Convenience trace ID formatting method + Args: + trace_id: Trace ID int + + Returns: + The trace ID (16 bytes) cast to a 32-character hexadecimal string + """ + return format(trace_id, "032x") + + +def format_span_id(span_id: int) -> str: + """Convenience span ID formatting method + Args: + span_id: Span ID int + + Returns: + The span ID (8 bytes) cast to a 16-character hexadecimal string + """ + return format(span_id, "016x") diff --git a/venv/lib/python3.12/site-packages/opentelemetry/trace/status.py b/venv/lib/python3.12/site-packages/opentelemetry/trace/status.py new file mode 100644 index 0000000..88c07a6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/trace/status.py @@ -0,0 +1,68 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import enum +import logging + +logger = logging.getLogger(__name__) + + +class StatusCode(enum.Enum): + """Represents the canonical set of status codes of a finished Span.""" + + UNSET = 0 + """The default status.""" + + OK = 1 + """The operation has been validated by an Application developer or Operator to have completed successfully.""" + + ERROR = 2 + """The operation contains an error.""" + + +class Status: + """Represents the status of a finished Span. + + Args: + status_code: The canonical status code that describes the result + status of the operation. + description: An optional description of the status. + """ + + def __init__( + self, + status_code: StatusCode = StatusCode.UNSET, + description: str | None = None, + ): + self._status_code = status_code + self._description = None + + if description: + if not isinstance(description, str): + logger.warning("Invalid status description type, expected str") + return + if status_code is not StatusCode.ERROR: + logger.warning("description should only be set when status_code is set to StatusCode.ERROR") + return + + self._description = description + + @property + def status_code(self) -> StatusCode: + """Represents the canonical status code of a finished Span.""" + return self._status_code + + @property + def description(self) -> str | None: + """Status description""" + return self._description + + @property + def is_ok(self) -> bool: + """Returns false if this represents an error, true otherwise.""" + return self.is_unset or self._status_code is StatusCode.OK + + @property + def is_unset(self) -> bool: + """Returns true if unset, false otherwise.""" + return self._status_code is StatusCode.UNSET diff --git a/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/_decorator.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/_decorator.cpython-312.pyc new file mode 100644 index 0000000..a040b92 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/_decorator.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/_importlib_metadata.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/_importlib_metadata.cpython-312.pyc new file mode 100644 index 0000000..6478778 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/_importlib_metadata.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/_once.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/_once.cpython-312.pyc new file mode 100644 index 0000000..1ec24d4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/_once.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/_providers.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/_providers.cpython-312.pyc new file mode 100644 index 0000000..771195c Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/_providers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/re.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/re.cpython-312.pyc new file mode 100644 index 0000000..d1d02ca Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/re.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/types.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/types.cpython-312.pyc new file mode 100644 index 0000000..f300943 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/util/__pycache__/types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/util/_decorator.py b/venv/lib/python3.12/site-packages/opentelemetry/util/_decorator.py new file mode 100644 index 0000000..b724b9f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/util/_decorator.py @@ -0,0 +1,75 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +import contextlib +import functools +import inspect +from collections.abc import Callable, Iterator +from typing import TYPE_CHECKING, Generic, TypeVar + +V = TypeVar("V") +R = TypeVar("R") # Return type +Pargs = TypeVar("Pargs") # Generic type for arguments +Pkwargs = TypeVar("Pkwargs") # Generic type for arguments + +# We don't actually depend on typing_extensions but we can use it in CI with this conditional +# import. ParamSpec can be imported directly from typing after python 3.9 is dropped +# https://peps.python.org/pep-0612/. +if TYPE_CHECKING: + from typing_extensions import ParamSpec + + P = ParamSpec("P") # Generic type for all arguments + + +class _AgnosticContextManager( + contextlib._GeneratorContextManager[R], + Generic[R], +): # pylint: disable=protected-access + """Context manager that can decorate both async and sync functions. + + This is an overridden version of the contextlib._GeneratorContextManager + class that will decorate async functions with an async context manager + to end the span AFTER the entire async function coroutine finishes. + + Else it will report near zero spans durations for async functions. + + We are overriding the contextlib._GeneratorContextManager class as + reimplementing it is a lot of code to maintain and this class (even if it's + marked as protected) doesn't seems like to be evolving a lot. + + For more information, see: + https://github.com/open-telemetry/opentelemetry-python/pull/3633 + """ + + def __enter__(self) -> R: + """Reimplementing __enter__ to avoid the type error. + + The original __enter__ method returns Any type, but we want to return R. + """ + del self.args, self.kwds, self.func # type: ignore + try: + return next(self.gen) # type: ignore + except StopIteration: + raise RuntimeError("generator didn't yield") from None + + def __call__(self, func: V) -> V: # pyright: ignore [reportIncompatibleMethodOverride] + if inspect.iscoroutinefunction(func): + + @functools.wraps(func) # type: ignore + async def async_wrapper(*args: Pargs, **kwargs: Pkwargs) -> R: # pyright: ignore [reportInvalidTypeVarUse] + with self._recreate_cm(): # type: ignore + return await func(*args, **kwargs) # type: ignore + + return async_wrapper # type: ignore + return super().__call__(func) # type: ignore + + +def _agnosticcontextmanager( + func: "Callable[P, Iterator[R]]", +) -> "Callable[P, _AgnosticContextManager[R]]": + @functools.wraps(func) + def helper(*args: Pargs, **kwargs: Pkwargs) -> _AgnosticContextManager[R]: # pyright: ignore [reportInvalidTypeVarUse] + return _AgnosticContextManager(func, args, kwargs) # pyright: ignore [reportArgumentType] + + # Ignoring the type to keep the original signature of the function + return helper # type: ignore[return-value] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/util/_importlib_metadata.py b/venv/lib/python3.12/site-packages/opentelemetry/util/_importlib_metadata.py new file mode 100644 index 0000000..656afbb --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/util/_importlib_metadata.py @@ -0,0 +1,61 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +""" +Caching and compatibility wrapper for standard library ``importlib.metadata``. + +This module caches ``entry_points()`` to avoid reloading entry points from disk on every call. +It also normalizes minor differences across python versions 3.10+. References to issues: + +- https://github.com/open-telemetry/opentelemetry-python/pull/4735 +- https://github.com/open-telemetry/opentelemetry-python/pull/5203 +""" + +import itertools +from functools import cache +from importlib.metadata import ( + Distribution, + EntryPoint, + EntryPoints, + PackageNotFoundError, + distributions, + requires, + version, +) +from importlib.metadata import entry_points as original_entry_points +from typing import Any + + +def _as_entry_points(eps: Any) -> EntryPoints: + # Python versions greater than 3.11 return EntryPoints. + if isinstance(eps, EntryPoints): + return eps + # In Python 3.10 and 3.11 entry_points() returns + # a dict-like SelectableGroups object. + # Use dict.values() instead of eps.values() to avoid the DeprecationWarning + # that SelectableGroups raises when calling .values(). + if isinstance(eps, dict): + return EntryPoints(itertools.chain.from_iterable(dict.values(eps))) + # This case should be unreachable, but is included as a fallback. + return EntryPoints(ep for group in eps.groups for ep in eps.select(group=group)) + + +@cache +def _original_entry_points_cached() -> EntryPoints: + return _as_entry_points(original_entry_points()) + + +def entry_points(**params) -> EntryPoints: + return _original_entry_points_cached().select(**params) + + +__all__ = [ + "Distribution", + "EntryPoint", + "EntryPoints", + "PackageNotFoundError", + "distributions", + "entry_points", + "requires", + "version", +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/util/_once.py b/venv/lib/python3.12/site-packages/opentelemetry/util/_once.py new file mode 100644 index 0000000..59f5623 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/util/_once.py @@ -0,0 +1,36 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from collections.abc import Callable +from threading import Lock + + +class Once: + """Execute a function exactly once and block all callers until the function returns + + Same as golang's `sync.Once `_ + """ + + def __init__(self) -> None: + self._lock = Lock() + self._done = False + + def do_once(self, func: Callable[[], None]) -> bool: + """Execute ``func`` if it hasn't been executed or return. + + Will block until ``func`` has been called by one thread. + + Returns: + Whether or not ``func`` was executed in this call + """ + + # fast path, try to avoid locking + if self._done: + return False + + with self._lock: + if not self._done: + func() + self._done = True + return True + return False diff --git a/venv/lib/python3.12/site-packages/opentelemetry/util/_providers.py b/venv/lib/python3.12/site-packages/opentelemetry/util/_providers.py new file mode 100644 index 0000000..ef4d234 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/util/_providers.py @@ -0,0 +1,42 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from logging import getLogger +from os import environ +from typing import TYPE_CHECKING, TypeVar, cast + +if TYPE_CHECKING: + from opentelemetry.metrics import MeterProvider + from opentelemetry.trace import TracerProvider + +Provider = TypeVar("Provider", "TracerProvider", "MeterProvider") + +logger = getLogger(__name__) + + +def _load_provider(provider_environment_variable: str, provider: str) -> Provider: # type: ignore[type-var] + # pylint: disable=import-outside-toplevel,no-name-in-module + from opentelemetry.util._importlib_metadata import ( # noqa: PLC0415 + entry_points, + ) + + try: + provider_name = cast( + str, + environ.get(provider_environment_variable, f"default_{provider}"), + ) + + return cast( + Provider, + next( # type: ignore + iter( # type: ignore + entry_points( # type: ignore + group=f"opentelemetry_{provider}", + name=provider_name, + ) + ) + ).load()(), + ) + except Exception: # pylint: disable=broad-exception-caught + logger.exception("Failed to load configured provider %s", provider) + raise diff --git a/venv/lib/python3.12/site-packages/opentelemetry/util/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/util/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry/util/re.py b/venv/lib/python3.12/site-packages/opentelemetry/util/re.py new file mode 100644 index 0000000..d2bc558 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/util/re.py @@ -0,0 +1,93 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from collections.abc import Mapping +from logging import getLogger +from re import compile, split +from urllib.parse import unquote + +from typing_extensions import deprecated + +_logger = getLogger(__name__) + +# The following regexes reference this spec: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#specifying-headers-via-environment-variables + +# Optional whitespace +_OWS = r"[ \t]*" +# A key contains printable US-ASCII characters except: SP and "(),/:;<=>?@[\]{} +_KEY_FORMAT = r"[\x21\x23-\x27\x2a\x2b\x2d\x2e\x30-\x39\x41-\x5a\x5e-\x7a\x7c\x7e]+" +# A value contains a URL-encoded UTF-8 string. The encoded form can contain any +# printable US-ASCII characters (0x20-0x7f) other than SP, DEL, and ",;/ +_VALUE_FORMAT = r"[\x21\x23-\x2b\x2d-\x3a\x3c-\x5b\x5d-\x7e]*" +# Like above with SP included +_LIBERAL_VALUE_FORMAT = r"[\x20\x21\x23-\x2b\x2d-\x3a\x3c-\x5b\x5d-\x7e]*" +# A key-value is key=value, with optional whitespace surrounding key and value +_KEY_VALUE_FORMAT = rf"{_OWS}{_KEY_FORMAT}{_OWS}={_OWS}{_VALUE_FORMAT}{_OWS}" + +_HEADER_PATTERN = compile(_KEY_VALUE_FORMAT) +_LIBERAL_HEADER_PATTERN = compile(rf"{_OWS}{_KEY_FORMAT}{_OWS}={_OWS}{_LIBERAL_VALUE_FORMAT}{_OWS}") +_DELIMITER_PATTERN = compile(r"[ \t]*,[ \t]*") + +_BAGGAGE_PROPERTY_FORMAT = rf"{_KEY_VALUE_FORMAT}|{_OWS}{_KEY_FORMAT}{_OWS}" + +_INVALID_HEADER_ERROR_MESSAGE_STRICT_TEMPLATE = ( + "Header format invalid! Header values in environment variables must be " + "URL encoded per the OpenTelemetry Protocol Exporter specification: %s" +) + +_INVALID_HEADER_ERROR_MESSAGE_LIBERAL_TEMPLATE = ( + "Header format invalid! Header values in environment variables must be " + "URL encoded per the OpenTelemetry Protocol Exporter specification or " + "a comma separated list of name=value occurrences: %s" +) + +# pylint: disable=invalid-name + + +@deprecated("You should use parse_env_headers. Deprecated since version 1.15.0.") +def parse_headers(s: str) -> Mapping[str, str]: + return parse_env_headers(s) + + +def parse_env_headers(s: str, liberal: bool = False) -> Mapping[str, str]: + """ + Parse ``s``, which is a ``str`` instance containing HTTP headers encoded + for use in ENV variables per the W3C Baggage HTTP header format at + https://www.w3.org/TR/baggage/#baggage-http-header-format, except that + additional semi-colon delimited metadata is not supported. + If ``liberal`` is True we try to parse ``s`` anyway to be more compatible + with other languages SDKs that accept non URL-encoded headers by default. + """ + headers: dict[str, str] = {} + headers_list: list[str] = split(_DELIMITER_PATTERN, s) + for header in headers_list: + if not header: # empty string + continue + header_match = _HEADER_PATTERN.fullmatch(header.strip()) + if not header_match and not liberal: + _logger.warning(_INVALID_HEADER_ERROR_MESSAGE_STRICT_TEMPLATE, header) + continue + + if header_match: + match_string: str = header_match.string + # value may contain any number of `=` + name, value = match_string.split("=", 1) + name = unquote(name).strip().lower() + value = unquote(value).strip() + headers[name] = value + else: + # this is not url-encoded and does not match the spec but we decided to be + # liberal in what we accept to match other languages SDKs behaviour + liberal_header_match = _LIBERAL_HEADER_PATTERN.fullmatch(header.strip()) + if not liberal_header_match: + _logger.warning(_INVALID_HEADER_ERROR_MESSAGE_LIBERAL_TEMPLATE, header) + continue + + liberal_match_string: str = liberal_header_match.string + # value may contain any number of `=` + name, value = liberal_match_string.split("=", 1) + name = name.strip().lower() + value = value.strip() + headers[name] = value + + return headers diff --git a/venv/lib/python3.12/site-packages/opentelemetry/util/types.py b/venv/lib/python3.12/site-packages/opentelemetry/util/types.py new file mode 100644 index 0000000..4456a47 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/util/types.py @@ -0,0 +1,26 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +from collections.abc import Mapping, Sequence + +# This is the implementation of the "Any" type as specified by the specifications of OpenTelemetry data model for logs. +# For more details, refer to the OTel specification: +# https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#type-any +AnyValue = AttributeValue = str | bool | int | float | bytes | Sequence["AnyValue"] | Mapping[str, "AnyValue"] | None +Attributes = Mapping[str, AnyValue] | None + +# Deprecated type, do not use. +AttributesAsKey = tuple[ + tuple[ + str, + str + | bool + | int + | float + | tuple[str | None, ...] + | tuple[bool | None, ...] + | tuple[int | None, ...] + | tuple[float | None, ...], + ], + ..., +] diff --git a/venv/lib/python3.12/site-packages/opentelemetry/version/__init__.py b/venv/lib/python3.12/site-packages/opentelemetry/version/__init__.py new file mode 100644 index 0000000..178452f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry/version/__init__.py @@ -0,0 +1,4 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +__version__ = "1.45.0" diff --git a/venv/lib/python3.12/site-packages/opentelemetry/version/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/opentelemetry/version/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4a64f54 Binary files /dev/null and b/venv/lib/python3.12/site-packages/opentelemetry/version/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/opentelemetry/version/py.typed b/venv/lib/python3.12/site-packages/opentelemetry/version/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/METADATA new file mode 100644 index 0000000..869b202 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/METADATA @@ -0,0 +1,43 @@ +Metadata-Version: 2.5 +Name: opentelemetry-api +Version: 1.45.0 +Summary: OpenTelemetry Python API +Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python/tree/main/opentelemetry-api +Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python +Author-email: OpenTelemetry Authors +License-Expression: Apache-2.0 +License-File: LICENSE +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: OpenTelemetry +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Typing :: Typed +Requires-Python: >=3.10 +Requires-Dist: typing-extensions>=4.5.0 +Description-Content-Type: text/x-rst + +OpenTelemetry Python API +============================================================================ + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-api.svg + :target: https://pypi.org/project/opentelemetry-api/ + +Installation +------------ + +:: + + pip install opentelemetry-api + +References +---------- + +* `OpenTelemetry Project `_ diff --git a/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/RECORD new file mode 100644 index 0000000..968e1a6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/RECORD @@ -0,0 +1,78 @@ +opentelemetry/_logs/__init__.py,sha256=yoj8QE2wCBRWnzEL3j3EodS-6NNi3v03Jyk4tNgf6PI,1366 +opentelemetry/_logs/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/_logs/_internal/__init__.py,sha256=jtvwv2zCD3gSTVhH4Z-fpzuHsaKtOyH2ZGMkYHdY3Oc,15469 +opentelemetry/_logs/_internal/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/_logs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/_logs/severity/__init__.py,sha256=GbGwrjYDcuJJzxXDAA4mVCDL6q1hAdGEPkzii-QpL5s,1007 +opentelemetry/_logs/severity/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/attributes/__init__.py,sha256=QORv3TOCioidox5Tmt05s7VSilaYNPalqoXWwcTUj1c,9503 +opentelemetry/attributes/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/attributes/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/baggage/__init__.py,sha256=TOqMC99oSIheKxfjV8dc6c-SeaPnEBaa1YhiELiDj1s,3564 +opentelemetry/baggage/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/baggage/propagation/__init__.py,sha256=vt0qo_g_tB7qhPZrGhEGf0FiyU0SNrZD701iDHXrUks,5607 +opentelemetry/baggage/propagation/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/baggage/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/context/__init__.py,sha256=qXnD9C6hTA6DCfKenPmy-Ur3_2PpY-jTIR560cyLDsY,4710 +opentelemetry/context/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/context/__pycache__/context.cpython-312.pyc,, +opentelemetry/context/__pycache__/contextvars_context.cpython-312.pyc,, +opentelemetry/context/context.py,sha256=-UbauXKz4i__60j67rBHqTMbKwxp30t1rL1Jt-3bk7s,1722 +opentelemetry/context/contextvars_context.py,sha256=WOTfi3HOb_NcgxLrF4jiRM1TZkGa2A6uSqIwoHC_dps,1299 +opentelemetry/context/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/environment_variables/__init__.py,sha256=wjBIKwPYfnI8XNCQ3J804I6BCV6tTxMvly6gLF_CMt8,1859 +opentelemetry/environment_variables/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/environment_variables/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/metrics/__init__.py,sha256=iqAfDbjCHPeyXwhMgfD1koYfT4f7MR2URksBJ-ACMxc,3068 +opentelemetry/metrics/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/metrics/_internal/__init__.py,sha256=PJ__GCrrY9nzy805E4imEL5sF7Iy2-Y88IG9FeF5_Bc,30867 +opentelemetry/metrics/_internal/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/metrics/_internal/__pycache__/instrument.cpython-312.pyc,, +opentelemetry/metrics/_internal/__pycache__/observation.cpython-312.pyc,, +opentelemetry/metrics/_internal/instrument.py,sha256=bs-saRuXJ7yA-fYBhOS7cMOVR96mM2zglbqyx8j2-yM,15981 +opentelemetry/metrics/_internal/observation.py,sha256=88Jfh1-uB_rvDtlsnZfxY0halvFwncxsW3nwhcl7b1Y,1378 +opentelemetry/metrics/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/propagate/__init__.py,sha256=De78IJUlOjUTvQBwKnRtO3S8HvyJyXOx9e_sdeigw-s,6303 +opentelemetry/propagate/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/propagate/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/propagators/__pycache__/_envcarrier.cpython-312.pyc,, +opentelemetry/propagators/__pycache__/composite.cpython-312.pyc,, +opentelemetry/propagators/__pycache__/textmap.cpython-312.pyc,, +opentelemetry/propagators/_envcarrier.py,sha256=SWMs72AcFr_wIrRyL_tZX7uLt3TFaiZbv4yn6TWXbKY,3117 +opentelemetry/propagators/composite.py,sha256=a0YBRtSuoQ9EEeKPjLMHsHh1wXmswEVU6lGVTv-li80,2724 +opentelemetry/propagators/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/propagators/textmap.py,sha256=2c2FOzDTCf232A2I2axynjknv6ARs4RxO-W7YnzcbjM,6017 +opentelemetry/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/trace/__init__.py,sha256=OV4oD43CXLX_qP3qlGsA08dkI8du4jm9zK7K0VExf3g,23188 +opentelemetry/trace/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/trace/__pycache__/span.cpython-312.pyc,, +opentelemetry/trace/__pycache__/status.cpython-312.pyc,, +opentelemetry/trace/propagation/__init__.py,sha256=fwZOzspAfxpCEROkp_grc77HKpXKbxpwE5QNsLuySV0,1136 +opentelemetry/trace/propagation/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/trace/propagation/__pycache__/tracecontext.cpython-312.pyc,, +opentelemetry/trace/propagation/tracecontext.py,sha256=223SmuVKhIiZj4lJuKZYBMsVQpUD6i8ahu7XZgS65E8,3553 +opentelemetry/trace/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/trace/span.py,sha256=sIu1CweW4k5oSwutMXQbXjnu9GZ1Mk7udFvnLwAFCec,20552 +opentelemetry/trace/status.py,sha256=p-gl51HssR-wnnAE0yW_wC08wyfrPtsEWZEIaZoA4BQ,1959 +opentelemetry/util/__pycache__/_decorator.cpython-312.pyc,, +opentelemetry/util/__pycache__/_importlib_metadata.cpython-312.pyc,, +opentelemetry/util/__pycache__/_once.cpython-312.pyc,, +opentelemetry/util/__pycache__/_providers.cpython-312.pyc,, +opentelemetry/util/__pycache__/re.cpython-312.pyc,, +opentelemetry/util/__pycache__/types.cpython-312.pyc,, +opentelemetry/util/_decorator.py,sha256=toFquN41ueWBmdlltyjh0Rno2gbhGWfzbQ5bNRzM98Q,2991 +opentelemetry/util/_importlib_metadata.py,sha256=7jP_tP9ga_1Q9p_AVGRQtsnxJUVWn3XJLzTiYOR4v8I,1833 +opentelemetry/util/_once.py,sha256=ESEc6pw8eAIZ2EMV2ZKEpbyfzF44UmGhkW42qniua6g,941 +opentelemetry/util/_providers.py,sha256=lhAq7em_Fpnv0i4mY9IsgmM-Fck_6vWGruZjeq7Oyrg,1317 +opentelemetry/util/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/util/re.py,sha256=Wz0B6CQe6aaT7w6v2IoAiuVnrKvLFXg1u_ZvbvKg2D0,4068 +opentelemetry/util/types.py,sha256=X1wZV1qCHOxa1KhH_J2z744BOf5imrg0D_CCMKEeVjE,866 +opentelemetry/version/__init__.py,sha256=SxyMcEaovfqNmTxEAGRSXVPfJcv8vuEXNQTu5PiZOjg,100 +opentelemetry/version/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/version/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry_api-1.45.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +opentelemetry_api-1.45.0.dist-info/METADATA,sha256=oFEG06aGJERIGRmDodOHAScBkLq3aHKY-YETXjh7ojQ,1411 +opentelemetry_api-1.45.0.dist-info/RECORD,, +opentelemetry_api-1.45.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87 +opentelemetry_api-1.45.0.dist-info/entry_points.txt,sha256=dxPq0YRbQDSwl8QkR-I9A38rbbfKQG5h2uNFjpvU6V4,573 +opentelemetry_api-1.45.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 diff --git a/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/WHEEL new file mode 100644 index 0000000..39dc805 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.32.4 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/entry_points.txt new file mode 100644 index 0000000..6279616 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/entry_points.txt @@ -0,0 +1,15 @@ +[opentelemetry_context] +contextvars_context = opentelemetry.context.contextvars_context:ContextVarsRuntimeContext + +[opentelemetry_environment_variables] +api = opentelemetry.environment_variables + +[opentelemetry_meter_provider] +default_meter_provider = opentelemetry.metrics:NoOpMeterProvider + +[opentelemetry_propagator] +baggage = opentelemetry.baggage.propagation:W3CBaggagePropagator +tracecontext = opentelemetry.trace.propagation.tracecontext:TraceContextTextMapPropagator + +[opentelemetry_tracer_provider] +default_tracer_provider = opentelemetry.trace:NoOpTracerProvider diff --git a/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_api-1.45.0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/METADATA b/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/METADATA new file mode 100644 index 0000000..dc4ba7e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/METADATA @@ -0,0 +1,64 @@ +Metadata-Version: 2.5 +Name: opentelemetry-exporter-http-transport +Version: 0.66b0 +Summary: OpenTelemetry Exporters HTTP transport +Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python/tree/main/exporter/opentelemetry-exporter-http-transport +Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python +Author-email: OpenTelemetry Authors +License-Expression: Apache-2.0 +License-File: LICENSE +Classifier: Development Status :: 4 - Beta +Classifier: Framework :: OpenTelemetry +Classifier: Framework :: OpenTelemetry :: Exporters +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.10 +Requires-Dist: opentelemetry-api~=1.15 +Provides-Extra: requests +Requires-Dist: requests~=2.25; extra == 'requests' +Provides-Extra: urllib3 +Requires-Dist: urllib3>=1.26; extra == 'urllib3' +Description-Content-Type: text/x-rst + +OpenTelemetry Exporters HTTP Transport +====================================== + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-exporter-http-transport.svg + :target: https://pypi.org/project/opentelemetry-exporter-http-transport/ + +This package provides shared HTTP transport abstractions used by OpenTelemetry exporters. + +The package has **no required dependencies**. The ``requests`` and ``urllib3`` +transports are available as optional extras. + +Installation +------------ + +Core package (no HTTP backend included):: + + pip install opentelemetry-exporter-http-transport + +With the ``requests`` backend:: + + pip install opentelemetry-exporter-http-transport[requests] + +With the ``urllib3`` backend:: + + pip install opentelemetry-exporter-http-transport[urllib3] + + +References +---------- + +* `OpenTelemetry `_ +* `OpenTelemetry Protocol Specification `_ +* `requests `_ +* `urllib3 `_ diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/RECORD b/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/RECORD new file mode 100644 index 0000000..eedccbf --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/RECORD @@ -0,0 +1,17 @@ +opentelemetry/exporter/http/transport/__init__.py,sha256=C7zfQBL8UD9DV7CLu1-H8KPjVaZBcg2R0swwhJaBgyA,2517 +opentelemetry/exporter/http/transport/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/exporter/http/transport/__pycache__/_base.cpython-312.pyc,, +opentelemetry/exporter/http/transport/__pycache__/_requests.cpython-312.pyc,, +opentelemetry/exporter/http/transport/__pycache__/_urllib3.cpython-312.pyc,, +opentelemetry/exporter/http/transport/_base.py,sha256=_moaGC3FbSTVZQ2y7WGklq76Ezhxk7ckfRJvKl7Zcwc,2821 +opentelemetry/exporter/http/transport/_requests.py,sha256=9BY7eiys5nu37LCeQwjOq9ZMvRgR5A05DNqUBJ0xtow,3328 +opentelemetry/exporter/http/transport/_urllib3.py,sha256=duvbcoaDGnDUHlupvyBwzt-ws6iNWTTOk3pcOFwPZxM,3994 +opentelemetry/exporter/http/transport/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/exporter/http/transport/version/__init__.py,sha256=KwYGGkuL2hTn_9n8uwsZGL0Qk_b8OYRBjsy-X1aysMY,100 +opentelemetry/exporter/http/transport/version/__pycache__/__init__.cpython-312.pyc,, +opentelemetry_exporter_http_transport-0.66b0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +opentelemetry_exporter_http_transport-0.66b0.dist-info/METADATA,sha256=K6rtSkqCkJdPz-H8jfahueqpXbLyJLAu7mTKCSczE5E,2335 +opentelemetry_exporter_http_transport-0.66b0.dist-info/RECORD,, +opentelemetry_exporter_http_transport-0.66b0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87 +opentelemetry_exporter_http_transport-0.66b0.dist-info/entry_points.txt,sha256=ym3X1kBWkiEPLmr-yTZ8XE4quzwGoHTNup_YTEnZsHc,190 +opentelemetry_exporter_http_transport-0.66b0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/WHEEL new file mode 100644 index 0000000..39dc805 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.32.4 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/entry_points.txt new file mode 100644 index 0000000..64c9d68 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[opentelemetry_http_transport] +requests = opentelemetry.exporter.http.transport._requests:RequestsHTTPTransport +urllib3 = opentelemetry.exporter.http.transport._urllib3:Urllib3HTTPTransport diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_http_transport-0.66b0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/METADATA b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/METADATA new file mode 100644 index 0000000..190f51c --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/METADATA @@ -0,0 +1,52 @@ +Metadata-Version: 2.5 +Name: opentelemetry-exporter-otlp-common +Version: 0.66b0 +Summary: OpenTelemetry OTLP HTTP export utilities +Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python/tree/main/exporter/opentelemetry-exporter-otlp-common +Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python +Author-email: OpenTelemetry Authors +License-Expression: Apache-2.0 +License-File: LICENSE +Classifier: Development Status :: 4 - Beta +Classifier: Framework :: OpenTelemetry +Classifier: Framework :: OpenTelemetry :: Exporters +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.10 +Requires-Dist: opentelemetry-sdk~=1.45.0 +Provides-Extra: http +Requires-Dist: opentelemetry-exporter-http-transport==0.66b0; extra == 'http' +Description-Content-Type: text/x-rst + +OpenTelemetry OTLP Utilities +=============================== + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-exporter-otlp-common.svg + :target: https://pypi.org/project/opentelemetry-exporter-otlp-common/ + +OpenTelemetry OTLP export utilities. + +This package is intended to be used by OpenTelemetry signal exporters (traces, metrics, +logs) that send telemetry with OTLP. Currently, all functionality in this package +is marked as internal and is not intended for use directly by application developers. + +Installation +------------ + +:: + + pip install opentelemetry-exporter-otlp-common + +References +---------- + +* `OpenTelemetry `_ +* `OTLP Specification `_ diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/RECORD b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/RECORD new file mode 100644 index 0000000..009f6ae --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/RECORD @@ -0,0 +1,16 @@ +opentelemetry/exporter/otlp/common/__init__.py,sha256=P6A4Imb0ORUG3bkuZKVSS2lcwJBWxiaYga-WREo7E6M,76 +opentelemetry/exporter/otlp/common/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/exporter/otlp/common/__pycache__/_aggregation.cpython-312.pyc,, +opentelemetry/exporter/otlp/common/__pycache__/_exporter_metrics.cpython-312.pyc,, +opentelemetry/exporter/otlp/common/__pycache__/http.cpython-312.pyc,, +opentelemetry/exporter/otlp/common/_aggregation.py,sha256=8koXk0XXC9tccHryHqa8nQ0sAt3k51KuWCZp-IqmPuw,4149 +opentelemetry/exporter/otlp/common/_exporter_metrics.py,sha256=IcC0OpJjtknU9ENkf73QCo-2LEnAGwyTTYrf7tbe3A8,5319 +opentelemetry/exporter/otlp/common/http.py,sha256=wYM01MjtGJ9Ngk68FeMb7oG2Pov3icFX_HvtbTqDZWA,8477 +opentelemetry/exporter/otlp/common/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/exporter/otlp/common/version/__init__.py,sha256=KwYGGkuL2hTn_9n8uwsZGL0Qk_b8OYRBjsy-X1aysMY,100 +opentelemetry/exporter/otlp/common/version/__pycache__/__init__.cpython-312.pyc,, +opentelemetry_exporter_otlp_common-0.66b0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +opentelemetry_exporter_otlp_common-0.66b0.dist-info/METADATA,sha256=WYkMwlMYsVYeU5vMXsPvqiiX0VLSBCnkqoWRNjaGoYQ,1946 +opentelemetry_exporter_otlp_common-0.66b0.dist-info/RECORD,, +opentelemetry_exporter_otlp_common-0.66b0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87 +opentelemetry_exporter_otlp_common-0.66b0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/WHEEL new file mode 100644 index 0000000..39dc805 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.32.4 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_common-0.66b0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/METADATA new file mode 100644 index 0000000..81e21d0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/METADATA @@ -0,0 +1,51 @@ +Metadata-Version: 2.5 +Name: opentelemetry-exporter-otlp-proto-common +Version: 1.45.0 +Summary: OpenTelemetry Protobuf encoding +Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python/tree/main/exporter/opentelemetry-exporter-otlp-proto-common +Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python +Author-email: OpenTelemetry Authors +License-Expression: Apache-2.0 +License-File: LICENSE +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: OpenTelemetry +Classifier: Framework :: OpenTelemetry :: Exporters +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.10 +Requires-Dist: opentelemetry-proto==1.45.0 +Description-Content-Type: text/x-rst + +OpenTelemetry Protobuf Encoding +=============================== + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-exporter-otlp-proto-common.svg + :target: https://pypi.org/project/opentelemetry-exporter-otlp-proto-common/ + +This library is provided as a convenience to encode to Protobuf. Currently used by: + +* opentelemetry-exporter-otlp-proto-grpc +* opentelemetry-exporter-otlp-proto-http + + +Installation +------------ + +:: + + pip install opentelemetry-exporter-otlp-proto-common + + +References +---------- + +* `OpenTelemetry `_ +* `OpenTelemetry Protocol Specification `_ diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/RECORD new file mode 100644 index 0000000..147a775 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/RECORD @@ -0,0 +1,24 @@ +opentelemetry/exporter/otlp/proto/common/__init__.py,sha256=u0ZRJD91ds8nPAft7qJXDgtlox4ihzjE4nbC-6j7o2I,178 +opentelemetry/exporter/otlp/proto/common/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/common/__pycache__/_log_encoder.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/common/__pycache__/metrics_encoder.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/common/__pycache__/trace_encoder.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/common/_internal/__init__.py,sha256=jIhaf5zlR1__gR5NLHoMwizFPVpWlXQfX8eXVzzemz0,3912 +opentelemetry/exporter/otlp/proto/common/_internal/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py,sha256=_ZHxZkBK1zpuhI9bkkpnhEDvanILeJnal_h2I7PP41g,3167 +opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py,sha256=RZ0qYZOXvy1U1et34M2z1HaVrtoMTLF8YPLzqDWi9l8,13623 +opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py,sha256=KGsRQOJ_nCJnla1TbbQjpngUiMjk6kOBz5HuKCyq5Jg,6146 +opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/common/_log_encoder.py,sha256=oB5tUVTUjsLdyWe-RWrG9KAQQkEFhgQxK7IrXEPlt6U,202 +opentelemetry/exporter/otlp/proto/common/metrics_encoder.py,sha256=-be49dpJM4hCpjEQeJCXekYgCrtB4P7mzuG0Qh27_3w,211 +opentelemetry/exporter/otlp/proto/common/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/exporter/otlp/proto/common/trace_encoder.py,sha256=ipZgLyntenRVLYaAdBbpQxCdve_CUMzrEwXem6tkIWM,205 +opentelemetry/exporter/otlp/proto/common/version/__init__.py,sha256=SxyMcEaovfqNmTxEAGRSXVPfJcv8vuEXNQTu5PiZOjg,100 +opentelemetry/exporter/otlp/proto/common/version/__pycache__/__init__.cpython-312.pyc,, +opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/METADATA,sha256=6qs7E5XboWgBsPvW3CAtKX-OzluKzkqtGZxNS31aLWg,1824 +opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/RECORD,, +opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87 +opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/WHEEL new file mode 100644 index 0000000..39dc805 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.32.4 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_common-1.45.0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/METADATA new file mode 100644 index 0000000..3230ab7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/METADATA @@ -0,0 +1,75 @@ +Metadata-Version: 2.5 +Name: opentelemetry-exporter-otlp-proto-http +Version: 1.45.0 +Summary: OpenTelemetry Collector Protobuf over HTTP Exporter +Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python/tree/main/exporter/opentelemetry-exporter-otlp-proto-http +Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python +Author-email: OpenTelemetry Authors +License-Expression: Apache-2.0 +License-File: LICENSE +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: OpenTelemetry +Classifier: Framework :: OpenTelemetry :: Exporters +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.10 +Requires-Dist: googleapis-common-protos~=1.52 +Requires-Dist: opentelemetry-api~=1.15 +Requires-Dist: opentelemetry-exporter-http-transport[urllib3]==0.66b0 +Requires-Dist: opentelemetry-exporter-otlp-common==0.66b0 +Requires-Dist: opentelemetry-exporter-otlp-proto-common==1.45.0 +Requires-Dist: opentelemetry-proto==1.45.0 +Requires-Dist: opentelemetry-sdk~=1.45.0 +Requires-Dist: typing-extensions>=4.5.0 +Provides-Extra: gcp-auth +Requires-Dist: opentelemetry-exporter-credential-provider-gcp>=0.59b0; extra == 'gcp-auth' +Provides-Extra: requests +Requires-Dist: opentelemetry-exporter-http-transport[requests]==0.66b0; extra == 'requests' +Requires-Dist: requests~=2.7; extra == 'requests' +Description-Content-Type: text/x-rst + +OpenTelemetry Collector Protobuf over HTTP Exporter +=================================================== + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-exporter-otlp-proto-http.svg + :target: https://pypi.org/project/opentelemetry-exporter-otlp-proto-http/ + +This library allows to export data to the OpenTelemetry Collector using the OpenTelemetry Protocol using Protobuf over HTTP. + +Installation +------------ + +:: + + pip install opentelemetry-exporter-otlp-proto-http + +By default, exports are sent over ``urllib3``. To use ``requests`` instead, install the +``requests`` extra and explicitly pass a ``requests.Session`` to the exporter: + +:: + + pip install opentelemetry-exporter-otlp-proto-http[requests] + +.. code-block:: python + + import requests + from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter + + exporter = OTLPSpanExporter(session=requests.Session()) + + +References +---------- + +* `OpenTelemetry Collector Exporter `_ +* `OpenTelemetry Collector `_ +* `OpenTelemetry `_ +* `OpenTelemetry Protocol Specification `_ diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/RECORD new file mode 100644 index 0000000..74c8cef --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/RECORD @@ -0,0 +1,21 @@ +opentelemetry/exporter/otlp/proto/http/__init__.py,sha256=m-DsRSI0umQPHKIjG3UtPlXrOi9rntH4R7J-7qFxnvo,2170 +opentelemetry/exporter/otlp/proto/http/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/http/_common/__init__.py,sha256=w-tg3l2lpL3cJfvAz6SPjGByecUoaEjUdFNMalKzvpQ,7105 +opentelemetry/exporter/otlp/proto/http/_common/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py,sha256=RJNm5Wy6WOobD-YQ4J8U7uMDnF9ea95ELPkOXObeBFA,9247 +opentelemetry/exporter/otlp/proto/http/_log_exporter/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/http/metric_exporter/__init__.py,sha256=MLJOCpsQQ6ydnXcAptIjHLl5qpxyJxaOWiutuj9TMcI,26255 +opentelemetry/exporter/otlp/proto/http/metric_exporter/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/http/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py,sha256=VEDSkCAaXZl2EeaykZQV1jIb_5P2m3kZEIv4pzQD-Rk,9015 +opentelemetry/exporter/otlp/proto/http/trace_exporter/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py,sha256=vh8rdnPdSAYCbtRPar8fE4MJOXbbBwKCasSCu7SPnh0,1780 +opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/exporter/otlp/proto/http/version/__init__.py,sha256=SxyMcEaovfqNmTxEAGRSXVPfJcv8vuEXNQTu5PiZOjg,100 +opentelemetry/exporter/otlp/proto/http/version/__pycache__/__init__.cpython-312.pyc,, +opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/METADATA,sha256=0EbfXQT-lup9ugbQ3ASLMV93veLpAllXYSyYMUhneTI,3115 +opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/RECORD,, +opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87 +opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/entry_points.txt,sha256=WOPQvujWzUUMIYKy8EI0C5Z_DC42MahQqP20_oL67B8,365 +opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/WHEEL new file mode 100644 index 0000000..39dc805 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.32.4 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/entry_points.txt new file mode 100644 index 0000000..bac8436 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/entry_points.txt @@ -0,0 +1,8 @@ +[opentelemetry_logs_exporter] +otlp_proto_http = opentelemetry.exporter.otlp.proto.http._log_exporter:OTLPLogExporter + +[opentelemetry_metrics_exporter] +otlp_proto_http = opentelemetry.exporter.otlp.proto.http.metric_exporter:OTLPMetricExporter + +[opentelemetry_traces_exporter] +otlp_proto_http = opentelemetry.exporter.otlp.proto.http.trace_exporter:OTLPSpanExporter diff --git a/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_exporter_otlp_proto_http-1.45.0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/METADATA b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/METADATA new file mode 100644 index 0000000..a6e99fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/METADATA @@ -0,0 +1,183 @@ +Metadata-Version: 2.5 +Name: opentelemetry-instrumentation +Version: 0.66b0 +Summary: Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python +Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/opentelemetry-instrumentation +Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python-contrib +Author-email: OpenTelemetry Authors +License-Expression: Apache-2.0 +License-File: LICENSE +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.10 +Requires-Dist: opentelemetry-api~=1.4 +Requires-Dist: opentelemetry-semantic-conventions==0.66b0 +Requires-Dist: packaging>=18.0 +Requires-Dist: wrapt<3.0.0,>=1.0.0 +Description-Content-Type: text/x-rst + +OpenTelemetry Instrumentation +============================= + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation.svg + :target: https://pypi.org/project/opentelemetry-instrumentation/ + +Installation +------------ + +:: + + pip install opentelemetry-instrumentation + + +This package provides commands that help automatically instrument a program: + +.. note:: + You need to install a distro package to get auto instrumentation working. The ``opentelemetry-distro`` + package contains the default distro and configurator and automatically configures some of the common options for users. + For more info about ``opentelemetry-distro`` check `here `__ + :: + + pip install "opentelemetry-distro[otlp]" + + When creating a custom distro and/or configurator, be sure to add entry points for each under ``opentelemetry_distro`` and ``opentelemetry_configurator`` respectfully. + If you have entry points for multiple distros or configurators present in your environment, you should specify the entry point name of the distro and configurator you want to be used via the ``OTEL_PYTHON_DISTRO`` and ``OTEL_PYTHON_CONFIGURATOR`` environment variables. + + +opentelemetry-bootstrap +----------------------- + +:: + + opentelemetry-bootstrap [-a |--action=][install|requirements] + +This command install default instrumentation packages and detects active Python site-packages +to figure out which instrumentation packages the user might want to install. By default, it +prints out a list of the default and detected instrumentation packages that can be added to a +requirements.txt file. It also supports installing the packages when run with +:code:`--action=install` or :code:`-a install` flag. All default and detectable +instrumentation packages are defined `here `__. + + +opentelemetry-instrument +------------------------ + +:: + + opentelemetry-instrument python program.py + +The instrument command will try to automatically detect packages used by your python program +and when possible, apply automatic tracing instrumentation on them. This means your program +will get automatic distributed tracing without having to make any code changes. This will +also configure a global tracer and tracing exporter as well as a meter and meter exporter. +By default, the instrument command will use the OTLP exporter but this can be overridden. + +The command supports the following configuration options as CLI arguments and environment +variables: + + +* ``--traces_exporter`` or ``OTEL_TRACES_EXPORTER`` +* ``--metrics_exporter`` or ``OTEL_METRICS_EXPORTER`` +* ``--distro`` or ``OTEL_PYTHON_DISTRO`` +* ``--configurator`` or ``OTEL_PYTHON_CONFIGURATOR`` + +The exporter options define what exporter destination to use and can be set to one or more +exporter names (see below). You can pass multiple values to configure multiple exporters +(e.g., ``zipkin_json,otlp``). + + - Defaults to ``otlp``. + - Can be set to ``none`` to disable automatic tracer initialization. + - Can be set to ``console`` to display JSON results locally. + +Trace exporter names: + + - jaeger_proto + - jaeger_thrift + - opencensus + - otlp + - otlp_proto_grpc (``deprecated``) + - otlp_proto_http (``deprecated``) + - zipkin_json + - zipkin_proto + +Metric exporter names: + + - otlp + - otlp_proto_grpc (``deprecated``) + - prometheus + +Note: The default transport protocol for ``otlp`` is gRPC. + +* ``--id-generator`` or ``OTEL_PYTHON_ID_GENERATOR`` + +Used to specify which IDs Generator to use for the global Tracer Provider. By default, it +will use the random IDs generator. + +The code in ``program.py`` needs to use one of the packages for which there is +an OpenTelemetry integration. For a list of the available integrations please +check `here `_ + +* ``OTEL_PYTHON_DISABLED_INSTRUMENTATIONS`` + +If set by the user, opentelemetry-instrument will read this environment variable to disable specific instrumentations. +e.g OTEL_PYTHON_DISABLED_INSTRUMENTATIONS="requests,django" + +If the variables contains ``*`` as member no instrumentation will be enabled. + +* ``OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH`` + +If set by the user to ``patch_all`` , opentelemetry instrument will call the gevent monkeypatching method ``patch_all``. +This is considered experimental but can be useful to instrument gevent applications. +e.g OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH=patch_all + + +Examples +^^^^^^^^ + +:: + + opentelemetry-instrument --traces_exporter console flask run --port=3000 + +The above command will pass ``--traces_exporter console`` to the instrument command and ``--port=3000`` to ``flask run``. + +:: + + opentelemetry-instrument --traces_exporter zipkin_json,otlp celery -A tasks worker --loglevel=info + +The above command will configure global trace provider, attach zipkin and otlp exporters to it and then +start celery with the rest of the arguments. + +:: + + opentelemetry-instrument --id_generator random flask run --port=3000 + +The above command will configure the global trace provider to use the Random IDs Generator, and then +pass ``--port=3000`` to ``flask run``. + +Programmatic Auto-instrumentation +--------------------------------- + +:: + + from opentelemetry.instrumentation import auto_instrumentation + auto_instrumentation.initialize() + + +If you are in an environment where you cannot use opentelemetry-instrument to inject auto-instrumentation you can do so programmatically with +the code above. Please note that some instrumentations may require the ``initialize()`` method to be called before the library they +instrument is imported. + +References +---------- + +* `OpenTelemetry Project `_ diff --git a/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/RECORD b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/RECORD new file mode 100644 index 0000000..bcfdde5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/RECORD @@ -0,0 +1,45 @@ +../../../bin/opentelemetry-bootstrap,sha256=-_ru_pcwhiRxVugZAOE9-u5oqOijw_QYpzLrZLfloTY,209 +../../../bin/opentelemetry-instrument,sha256=9mH3E1U-GeQI20CdQ0EprRxi0ITqQjV7Th0GKwh65Do,220 +opentelemetry/instrumentation/__pycache__/_semconv.cpython-312.pyc,, +opentelemetry/instrumentation/__pycache__/bootstrap.cpython-312.pyc,, +opentelemetry/instrumentation/__pycache__/bootstrap_gen.cpython-312.pyc,, +opentelemetry/instrumentation/__pycache__/cidict.cpython-312.pyc,, +opentelemetry/instrumentation/__pycache__/dependencies.cpython-312.pyc,, +opentelemetry/instrumentation/__pycache__/distro.cpython-312.pyc,, +opentelemetry/instrumentation/__pycache__/environment_variables.cpython-312.pyc,, +opentelemetry/instrumentation/__pycache__/instrumentor.cpython-312.pyc,, +opentelemetry/instrumentation/__pycache__/log_utils.cpython-312.pyc,, +opentelemetry/instrumentation/__pycache__/propagators.cpython-312.pyc,, +opentelemetry/instrumentation/__pycache__/sqlcommenter_utils.cpython-312.pyc,, +opentelemetry/instrumentation/__pycache__/utils.cpython-312.pyc,, +opentelemetry/instrumentation/__pycache__/version.cpython-312.pyc,, +opentelemetry/instrumentation/_labeler/__init__.py,sha256=4w_piLN07MAdzB0JGujLgoO4fPdpLKlUeHxxsNjW46Q,2436 +opentelemetry/instrumentation/_labeler/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/instrumentation/_labeler/_internal/__init__.py,sha256=Vtjkkf0tdp4AGSVI4eU7VwAPFK9x0zWxZ9EnJ8i9SWg,8603 +opentelemetry/instrumentation/_labeler/_internal/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/instrumentation/_semconv.py,sha256=H4lRSo9UQb8NOeS9z_MF5B8t5mjZznJ4qedZD4UUWA0,22773 +opentelemetry/instrumentation/auto_instrumentation/__init__.py,sha256=wlEi0XyeUmzaJtzJNWGwt--5hbK9J6IYjOOfweti2fQ,6245 +opentelemetry/instrumentation/auto_instrumentation/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/instrumentation/auto_instrumentation/__pycache__/_load.cpython-312.pyc,, +opentelemetry/instrumentation/auto_instrumentation/__pycache__/sitecustomize.cpython-312.pyc,, +opentelemetry/instrumentation/auto_instrumentation/_load.py,sha256=M7pzrNSmCxz1pB96vcMjuVaQcz1Ch1f-vD-ePjRJTgU,6485 +opentelemetry/instrumentation/auto_instrumentation/sitecustomize.py,sha256=mGic_0KU1X1NpZBfKJHE4dYbDdQ5I8COPmyk0fyYtWI,165 +opentelemetry/instrumentation/bootstrap.py,sha256=LRZwU2rZEZJBuB3z7UL2bA2RIpBzxbqt1gLj71FxFxg,4802 +opentelemetry/instrumentation/bootstrap_gen.py,sha256=CCHigvwNR_fp7ahHpf65kzftIgKb9Hl52n8hxUo6IeQ,6055 +opentelemetry/instrumentation/cidict.py,sha256=HPaBOiogNcwl7Uf9U7zWAdAOiFPRJiHXYbyi4ZKo2Kg,2158 +opentelemetry/instrumentation/dependencies.py,sha256=QOdPaqu4LQmMIx_Tcg34VsuM6vSOJs5cldVK1fKFWIc,7769 +opentelemetry/instrumentation/distro.py,sha256=AfSXTTz8lRTZlLNsvwaT1jo27sodHeYefjcfTLUEyPw,1660 +opentelemetry/instrumentation/environment_variables.py,sha256=SBWCy3UMMceOUoNFhPDBErW0Z1-G6xi9LOStL0S5DRU,831 +opentelemetry/instrumentation/instrumentor.py,sha256=eG-578cLJmBpoNPGCh534tMAUg42X1fcYopk859XxZM,4560 +opentelemetry/instrumentation/log_utils.py,sha256=0y1VPgwqf1pct2AOw2FC-H0bmg6KAKs4xG_ymiL2dh0,1997 +opentelemetry/instrumentation/propagators.py,sha256=TBM-3Wk1AzxdOc1zWajWI_ZPj7x87szkXb0TjMjN6kQ,3528 +opentelemetry/instrumentation/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/instrumentation/sqlcommenter_utils.py,sha256=HWFOdPTwfZp9vfSAUq0uaDQCEc9l4CiSvqdhiYxy2FQ,2404 +opentelemetry/instrumentation/utils.py,sha256=QuM3xjNu2KfBrd2dHztyLS5imx4G5Rru2HcZapW2zrg,6842 +opentelemetry/instrumentation/version.py,sha256=KwYGGkuL2hTn_9n8uwsZGL0Qk_b8OYRBjsy-X1aysMY,100 +opentelemetry_instrumentation-0.66b0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +opentelemetry_instrumentation-0.66b0.dist-info/METADATA,sha256=_LcD32CmLPZKl7DNMvfTH2okHlErODEWOxqWMBRRRGU,7195 +opentelemetry_instrumentation-0.66b0.dist-info/RECORD,, +opentelemetry_instrumentation-0.66b0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87 +opentelemetry_instrumentation-0.66b0.dist-info/entry_points.txt,sha256=iVv3t5REB0O58tFUEQQXYLrTCa1VVOFUXfrbvUk6_aU,279 +opentelemetry_instrumentation-0.66b0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 diff --git a/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/WHEEL new file mode 100644 index 0000000..39dc805 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.32.4 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/entry_points.txt new file mode 100644 index 0000000..1b2fbca --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/entry_points.txt @@ -0,0 +1,6 @@ +[console_scripts] +opentelemetry-bootstrap = opentelemetry.instrumentation.bootstrap:run +opentelemetry-instrument = opentelemetry.instrumentation.auto_instrumentation:run + +[opentelemetry_environment_variables] +instrumentation = opentelemetry.instrumentation.environment_variables diff --git a/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation-0.66b0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/METADATA b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/METADATA new file mode 100644 index 0000000..49d1e5d --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/METADATA @@ -0,0 +1,51 @@ +Metadata-Version: 2.5 +Name: opentelemetry-instrumentation-threading +Version: 0.66b0 +Summary: Thread context propagation support for OpenTelemetry +Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-threading +Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python-contrib +Author-email: OpenTelemetry Authors +License-Expression: Apache-2.0 +License-File: LICENSE +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.10 +Requires-Dist: opentelemetry-api~=1.12 +Requires-Dist: opentelemetry-instrumentation==0.66b0 +Requires-Dist: wrapt<3.0.0,>=1.0.0 +Provides-Extra: instruments +Description-Content-Type: text/x-rst + +OpenTelemetry threading Instrumentation +======================================= + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-threading.svg + :target: https://pypi.org/project/opentelemetry-instrumentation-threading/ + +This library provides instrumentation for the `threading` module to ensure that +the OpenTelemetry context is propagated across threads. It is important to note +that this instrumentation does not produce any telemetry data on its own. It +merely ensures that the context is correctly propagated when threads are used. + +Installation +------------ + +:: + + pip install opentelemetry-instrumentation-threading + +References +---------- + +* `OpenTelemetry Threading Tracing `_ +* `OpenTelemetry Project `_ diff --git a/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/RECORD b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/RECORD new file mode 100644 index 0000000..463c954 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/RECORD @@ -0,0 +1,12 @@ +opentelemetry/instrumentation/threading/__init__.py,sha256=1m86nhX4MxMatkLsUTXY64K9r_L09FvtPD6AOBFU19Y,5455 +opentelemetry/instrumentation/threading/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/instrumentation/threading/__pycache__/package.cpython-312.pyc,, +opentelemetry/instrumentation/threading/__pycache__/version.cpython-312.pyc,, +opentelemetry/instrumentation/threading/package.py,sha256=SgaDzffUjYy5BtFPZmzT7b6oHBXXIP_nmXMLW1EOcq4,122 +opentelemetry/instrumentation/threading/version.py,sha256=KwYGGkuL2hTn_9n8uwsZGL0Qk_b8OYRBjsy-X1aysMY,100 +opentelemetry_instrumentation_threading-0.66b0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +opentelemetry_instrumentation_threading-0.66b0.dist-info/METADATA,sha256=nLz4d89b3PFR_7ClNlytMIiQBsxMfT4EWo57o0AsfmI,2106 +opentelemetry_instrumentation_threading-0.66b0.dist-info/RECORD,, +opentelemetry_instrumentation_threading-0.66b0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87 +opentelemetry_instrumentation_threading-0.66b0.dist-info/entry_points.txt,sha256=ROg9siTOulFwwJJ6aof4zS7q7ubKdgcPxiCMXQJ_o5U,103 +opentelemetry_instrumentation_threading-0.66b0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 diff --git a/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/WHEEL new file mode 100644 index 0000000..39dc805 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.32.4 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/entry_points.txt new file mode 100644 index 0000000..5f4cf22 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[opentelemetry_instrumentor] +threading = opentelemetry.instrumentation.threading:ThreadingInstrumentor diff --git a/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_instrumentation_threading-0.66b0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/METADATA new file mode 100644 index 0000000..9b1254e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/METADATA @@ -0,0 +1,63 @@ +Metadata-Version: 2.5 +Name: opentelemetry-proto +Version: 1.45.0 +Summary: OpenTelemetry Python Proto +Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python/tree/main/opentelemetry-proto +Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python +Author-email: OpenTelemetry Authors +License-Expression: Apache-2.0 +License-File: LICENSE +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: OpenTelemetry +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.10 +Requires-Dist: protobuf<8.0,>=5.0 +Description-Content-Type: text/x-rst + +OpenTelemetry Python Proto +========================== + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-proto.svg + :target: https://pypi.org/project/opentelemetry-proto/ + +This library contains the generated code for OpenTelemetry protobuf data model. The code in the current +package was generated using the v1.10.0 release_ of opentelemetry-proto. + +.. _release: https://github.com/open-telemetry/opentelemetry-proto/releases/tag/v1.10.0 + +Installation +------------ + +:: + + pip install opentelemetry-proto + +Code Generation +--------------- + +These files were generated automatically from code in opentelemetry-proto_. +To regenerate the code, run ``../scripts/proto_codegen.sh``. + +To build against a new release or specific commit of opentelemetry-proto_, +update the ``PROTO_REPO_BRANCH_OR_COMMIT`` variable in +``../scripts/proto_codegen.sh``. Then run the script and commit the changes +as well as any fixes needed in the OTLP exporter. + +.. _opentelemetry-proto: https://github.com/open-telemetry/opentelemetry-proto + + +References +---------- + +* `OpenTelemetry Project `_ +* `OpenTelemetry Proto `_ +* `proto_codegen.sh script `_ diff --git a/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/RECORD new file mode 100644 index 0000000..087817f --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/RECORD @@ -0,0 +1,74 @@ +opentelemetry/proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/collector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/collector/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/collector/logs/v1/__pycache__/logs_service_pb2.cpython-312.pyc,, +opentelemetry/proto/collector/logs/v1/__pycache__/logs_service_pb2_grpc.cpython-312.pyc,, +opentelemetry/proto/collector/logs/v1/logs_service_pb2.py,sha256=vzqY-ZH2ObPzgg43o4dBPPDPkEzR4uKtsReBSKp9pXU,2649 +opentelemetry/proto/collector/logs/v1/logs_service_pb2.pyi,sha256=f1rnyAlAgvrgBPV53HUHV_LefbNyZktjsaDevBi2bCU,4942 +opentelemetry/proto/collector/logs/v1/logs_service_pb2_grpc.py,sha256=j42OB_1XBx1YsAQovzP3tpFW-Zw2KijRKr5GY86d3us,4778 +opentelemetry/proto/collector/metrics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/collector/metrics/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/collector/metrics/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/collector/metrics/v1/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/collector/metrics/v1/__pycache__/metrics_service_pb2.cpython-312.pyc,, +opentelemetry/proto/collector/metrics/v1/__pycache__/metrics_service_pb2_grpc.cpython-312.pyc,, +opentelemetry/proto/collector/metrics/v1/metrics_service_pb2.py,sha256=mLnVSRVLwmDoQjSJKxHWKQNJKnrh4rOj0TveHW0Dy2I,2785 +opentelemetry/proto/collector/metrics/v1/metrics_service_pb2.pyi,sha256=3uvMhyhbEP5o32XQH_l0Bw5jESn0GaxHm5M8ioIPchk,5017 +opentelemetry/proto/collector/metrics/v1/metrics_service_pb2_grpc.py,sha256=WWWNksqlFydP-hm-umjdI55BiUMpTWwQo1WGSFkWPGI,4679 +opentelemetry/proto/collector/profiles/v1development/__pycache__/profiles_service_pb2.cpython-312.pyc,, +opentelemetry/proto/collector/profiles/v1development/__pycache__/profiles_service_pb2_grpc.cpython-312.pyc,, +opentelemetry/proto/collector/profiles/v1development/profiles_service_pb2.py,sha256=u9pmHLhGb_1xZCULUOEmpCpBHBX5xgxb7Ag3Ap0RPCY,3129 +opentelemetry/proto/collector/profiles/v1development/profiles_service_pb2.pyi,sha256=fT8amTWZqXw1ZdLuvJh1jI7Xmki1cnM11BLEVDV8zQo,5572 +opentelemetry/proto/collector/profiles/v1development/profiles_service_pb2_grpc.py,sha256=2ixVTOmV3kCo3AUeaIpbCrp6SSLtPckzYHSxkdZfJGs,4836 +opentelemetry/proto/collector/trace/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/collector/trace/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/collector/trace/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/collector/trace/v1/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/collector/trace/v1/__pycache__/trace_service_pb2.cpython-312.pyc,, +opentelemetry/proto/collector/trace/v1/__pycache__/trace_service_pb2_grpc.cpython-312.pyc,, +opentelemetry/proto/collector/trace/v1/trace_service_pb2.py,sha256=_-Mpq7a56F5zfut-67i4-umWiMiFQbiD3kl0-Kr-l_8,2698 +opentelemetry/proto/collector/trace/v1/trace_service_pb2.pyi,sha256=GzlSDEbbeMKKzezsvQnLTXRApfU-scQUZ446kcRtbPA,4931 +opentelemetry/proto/collector/trace/v1/trace_service_pb2_grpc.py,sha256=gvRwTU_GnaNGSi4VC5AuOTOmXv-uemGFYROmTV7YiW8,4812 +opentelemetry/proto/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/common/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/common/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/common/v1/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/common/v1/__pycache__/common_pb2.cpython-312.pyc,, +opentelemetry/proto/common/v1/common_pb2.py,sha256=cUpy1Efeekjr8SdnQ6Um1bvfa1YQEJvm5f1q5wfa_vg,3244 +opentelemetry/proto/common/v1/common_pb2.pyi,sha256=YCUsm4416nmBwJvhDR0hJbrtzFbEnPtQ6qAlKoj546A,12252 +opentelemetry/proto/logs/v1/__pycache__/logs_pb2.cpython-312.pyc,, +opentelemetry/proto/logs/v1/logs_pb2.py,sha256=Fk1Mc6HMRSAndZ9jbTf4mfl7DchJctQoZxN2vxVePE4,4709 +opentelemetry/proto/logs/v1/logs_pb2.pyi,sha256=7wAK7F-MoZTeNZBz-9PCLLCHfIy2-TJYjLpU-GoI1VA,17504 +opentelemetry/proto/metrics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/metrics/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/metrics/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/metrics/v1/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/metrics/v1/__pycache__/metrics_pb2.cpython-312.pyc,, +opentelemetry/proto/metrics/v1/metrics_pb2.py,sha256=3FdKVo1m01cuJ07v_QnOTtT-1IXTSeZDf1Biops3JWY,9468 +opentelemetry/proto/metrics/v1/metrics_pb2.pyi,sha256=qDk6IUH57frCPXS4I21nC_pVy4h_57-Ay2yBN2lRUz0,56770 +opentelemetry/proto/profiles/v1development/__pycache__/profiles_pb2.cpython-312.pyc,, +opentelemetry/proto/profiles/v1development/profiles_pb2.py,sha256=TDw0jXOPKuuw6YHm85V5Hv_4GxRKDgOYdbkGNPGXIOw,6853 +opentelemetry/proto/profiles/v1development/profiles_pb2.pyi,sha256=EcyD-Uu_-QAiKINRs8oJOgauE-IGHMbRyPxo6MHISDg,38013 +opentelemetry/proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/resource/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/resource/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/resource/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/resource/v1/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/resource/v1/__pycache__/resource_pb2.cpython-312.pyc,, +opentelemetry/proto/resource/v1/resource_pb2.py,sha256=82iZgfG1nx0XLR7722je9p1Y6ZeTVYWQgH6ZhEQcCPU,1864 +opentelemetry/proto/resource/v1/resource_pb2.pyi,sha256=Dp9ld35WUlQqgeWqPY41CKcEft4yosD0j3YB74-Klms,2802 +opentelemetry/proto/trace/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/trace/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/trace/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/proto/trace/v1/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/proto/trace/v1/__pycache__/trace_pb2.cpython-312.pyc,, +opentelemetry/proto/trace/v1/trace_pb2.py,sha256=FgD63A7upWYrw0aeNe3qJxrgNxN23Il-gal8TTY9uRk,5493 +opentelemetry/proto/trace/v1/trace_pb2.pyi,sha256=cHTwwXDe9EVMmwVjDK-gqmaWr_Pxgx3bbrTN53BqZOU,28268 +opentelemetry/proto/version/__init__.py,sha256=SxyMcEaovfqNmTxEAGRSXVPfJcv8vuEXNQTu5PiZOjg,100 +opentelemetry/proto/version/__pycache__/__init__.cpython-312.pyc,, +opentelemetry_proto-1.45.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +opentelemetry_proto-1.45.0.dist-info/METADATA,sha256=8SaBC0VSiTb_FhwVzdn9VSIz7f0gBdE-Z1EWKccOokA,2319 +opentelemetry_proto-1.45.0.dist-info/RECORD,, +opentelemetry_proto-1.45.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87 +opentelemetry_proto-1.45.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 diff --git a/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/WHEEL new file mode 100644 index 0000000..39dc805 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.32.4 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_proto-1.45.0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/METADATA new file mode 100644 index 0000000..58f6bf1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/METADATA @@ -0,0 +1,47 @@ +Metadata-Version: 2.5 +Name: opentelemetry-sdk +Version: 1.45.0 +Summary: OpenTelemetry Python SDK +Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python/tree/main/opentelemetry-sdk +Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python +Author-email: OpenTelemetry Authors +License-Expression: Apache-2.0 +License-File: LICENSE +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: OpenTelemetry +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Typing :: Typed +Requires-Python: >=3.10 +Requires-Dist: opentelemetry-api==1.45.0 +Requires-Dist: opentelemetry-semantic-conventions==0.66b0 +Requires-Dist: typing-extensions>=4.5.0 +Provides-Extra: file-configuration +Requires-Dist: opentelemetry-configuration==0.66b0; extra == 'file-configuration' +Description-Content-Type: text/x-rst + +OpenTelemetry Python SDK +============================================================================ + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-sdk.svg + :target: https://pypi.org/project/opentelemetry-sdk/ + +Installation +------------ + +:: + + pip install opentelemetry-sdk + +References +---------- + +* `OpenTelemetry Project `_ diff --git a/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/RECORD new file mode 100644 index 0000000..5a1708d --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/RECORD @@ -0,0 +1,132 @@ +opentelemetry/sdk/__init__.pyi,sha256=kQMbMw8wLQtWJ1bVBm7XoI06B_4Fv0un5hv3FKwrgRQ,669 +opentelemetry/sdk/_configuration/__init__.py,sha256=AeaHLCJQnzCdMh1B-KJiSTy2EGgWO3Np9m3UY63jT6c,24620 +opentelemetry/sdk/_configuration/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/_logs/__init__.py,sha256=2yfo59OYw3Z9AljN8I1SCqEs_ZyyrYSMBaj9M30txak,786 +opentelemetry/sdk/_logs/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/_logs/_internal/__init__.py,sha256=OFj_PzBRbbZMFPFIwJ-gq01Oph5GNqP4lpzbaGd1E7Q,39833 +opentelemetry/sdk/_logs/_internal/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/_logs/_internal/__pycache__/_exceptions.cpython-312.pyc,, +opentelemetry/sdk/_logs/_internal/__pycache__/_logger_metrics.cpython-312.pyc,, +opentelemetry/sdk/_logs/_internal/_exceptions.py,sha256=ITjs6DD_-_sf1qoLHT5VES03gf66Lmdtt0rrRSA1QuU,3659 +opentelemetry/sdk/_logs/_internal/_logger_metrics.py,sha256=bChcbNxmDaxnO939vBMhZR7194-7KUZ4AJCoGuh6-kQ,905 +opentelemetry/sdk/_logs/_internal/export/__init__.py,sha256=t06efrol-fxnxHpWrlxv5aqCN0qWs3rHmU1xJLe0f4I,14362 +opentelemetry/sdk/_logs/_internal/export/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/_logs/_internal/export/__pycache__/in_memory_log_exporter.cpython-312.pyc,, +opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py,sha256=t08c4a_yJI0ia-ubw9ZrV1OVYBt170hX5guwjFsqwqA,1570 +opentelemetry/sdk/_logs/export/__init__.py,sha256=WbEkLkk6x2Jv3sN5O4nYyQGTiLA5l2wIzMyXZaJbnCI,842 +opentelemetry/sdk/_logs/export/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/_shared_internal/__init__.py,sha256=lLMjS6yXfZt7SrbMOVHtg0T4KfM65Pj3tSw7fCYfzjo,9367 +opentelemetry/sdk/_shared_internal/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/_shared_internal/__pycache__/_processor_metrics.cpython-312.pyc,, +opentelemetry/sdk/_shared_internal/_processor_metrics.py,sha256=Zz4M8qkt04ELBG7Sll-VKXeu6hHdWqNVHWMniIxNucQ,4669 +opentelemetry/sdk/environment_variables/__init__.py,sha256=qH4bgD4J4MW2qnK-FL6pS5lPt4C5a4uEXi79VVHKcxM,32116 +opentelemetry/sdk/environment_variables/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/environment_variables/__pycache__/_internal.cpython-312.pyc,, +opentelemetry/sdk/environment_variables/_internal.py,sha256=lt7RHgF5D6goj4Ddp-j1Rmqy00F8DNQxmhR0DxN3IKE,698 +opentelemetry/sdk/error_handler/__init__.py,sha256=EbsUfYDLrKFvybLZ5ZSwlZSXxYIr4T5a4m3nd_J6rlI,4064 +opentelemetry/sdk/error_handler/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/metrics/__init__.py,sha256=g87rxd0lyGpiPBhXswwtkcII5iyraYIso5ryQD_nPmA,1314 +opentelemetry/sdk/metrics/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/__init__.py,sha256=32aaU6Y1JAP-IlBDwspIRvkN27HUefmvTKwxrHHJe_A,25417 +opentelemetry/sdk/metrics/_internal/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/__pycache__/_view_instrument_match.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/__pycache__/aggregation.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/__pycache__/exceptions.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/__pycache__/instrument.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/__pycache__/measurement.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/__pycache__/measurement_consumer.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/__pycache__/metric_reader_storage.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/__pycache__/point.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/__pycache__/sdk_configuration.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/__pycache__/view.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/_view_instrument_match.py,sha256=05L6NcUnvCsiARlWT566ictFlVafmzI3I8gqq1g6kiw,7058 +opentelemetry/sdk/metrics/_internal/aggregation.py,sha256=eJxv7FD2HIEW-Nfl2YhbuP_s3eZAtJmyv_A0qZA0AiY,50766 +opentelemetry/sdk/metrics/_internal/exceptions.py,sha256=gTB3kWIOhUgThxj18h7XOT7iWiJU1Cf61PfdBrWcUQo,167 +opentelemetry/sdk/metrics/_internal/exemplar/__init__.py,sha256=tuDHwC94fgqZKgBFGiMyHKAzi8HfA3v_5lMHPYw6wMQ,710 +opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/exemplar.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/exemplar_filter.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/exemplar/__pycache__/exemplar_reservoir.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/exemplar/exemplar.py,sha256=7UsmNzyQ8zdGh04BFQUXbQsgsSbR2g9V8X0bHaw77Fc,1253 +opentelemetry/sdk/metrics/_internal/exemplar/exemplar_filter.py,sha256=bsMzy-lftcaagVzImzE6PLhUJL79bOhXJS9k06ZGTlU,4070 +opentelemetry/sdk/metrics/_internal/exemplar/exemplar_reservoir.py,sha256=o5nEimpLhZH9BH63sx3rVfLwDvakOGr_-t8NOfMoGYI,9714 +opentelemetry/sdk/metrics/_internal/exponential_histogram/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/sdk/metrics/_internal/exponential_histogram/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/exponential_histogram/__pycache__/buckets.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/exponential_histogram/buckets.py,sha256=HiDU8hCLfxP0y4t95dCPUSNiQn27PSQsl33puFTWdJA,5511 +opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__init__.py,sha256=ee8_ZP0FYMlWVc2eBhBmesnQt80HeNsv8brzLcpFF28,3481 +opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/errors.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/exponent_mapping.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/ieee_754.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__pycache__/logarithm_mapping.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/errors.py,sha256=C25l_iAHS6oz2wCehlKtOoB1pNt4NYZG7_so_Mzm9cs,378 +opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/exponent_mapping.py,sha256=UG05HVg8vcyP7y2y_CLi1Oqzwu58R9FhjWoCmPNmVbo,6448 +opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/ieee_754.md,sha256=8Nf8FGbZi26c6KckxIsJHH2sa0hJZ24QCeOmE9huJLg,4980 +opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/ieee_754.py,sha256=V4j8BNgpOfHXu-Do1frW4uAeIOFuBjCmDO66wz-798o,4974 +opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/logarithm_mapping.py,sha256=2Jcz8cThKXlgif_h3i0wReu91tMGOPhF-EshwMHR624,5475 +opentelemetry/sdk/metrics/_internal/export/__init__.py,sha256=Z1cMQ7mdcrrH8aJNjCa6Cx2c2h3hFMdFGmp4C5ujfSo,21153 +opentelemetry/sdk/metrics/_internal/export/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/export/__pycache__/_metric_reader_metrics.cpython-312.pyc,, +opentelemetry/sdk/metrics/_internal/export/_metric_reader_metrics.py,sha256=r6nmYf4xpm09rI3BU2ZuwhUxMMlyzYJ5MibdKcGycXA,1607 +opentelemetry/sdk/metrics/_internal/instrument.py,sha256=9Oyu3zX7YTVCGSoO8ONX4motwTXYLowfLqWx7xKkRfk,12749 +opentelemetry/sdk/metrics/_internal/measurement.py,sha256=NRZiSstX07VfNUe9X10zO-0NYErC3HV29UWUe8remyM,1800 +opentelemetry/sdk/metrics/_internal/measurement_consumer.py,sha256=7mhuFep1D2RPicF94I0qlTAoY765MpPNw29OKGz0c2s,5680 +opentelemetry/sdk/metrics/_internal/metric_reader_storage.py,sha256=RTz_vQ9zDwuJQLZC_jHBBFEDwbsBl-rJfntzmKuU8kI,10880 +opentelemetry/sdk/metrics/_internal/point.py,sha256=5XZwgXEo32LmNlI-jEEV-7_2Gqwei9EXRdrLTthZxGA,7096 +opentelemetry/sdk/metrics/_internal/sdk_configuration.py,sha256=vdzQ1B46etMXb5UNDSKvNkNHoYZGcGzn4WiK2SAK0W0,519 +opentelemetry/sdk/metrics/_internal/view.py,sha256=dfuBlfw6pT4qVEN2M3Tm55XOrqsUTqOliaxLr6m0WoA,7238 +opentelemetry/sdk/metrics/export/__init__.py,sha256=WN5AcKi5loia362DnDFL2CDOueDsoZY4ck5-cJRwtyE,1249 +opentelemetry/sdk/metrics/export/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/metrics/view/__init__.py,sha256=R0f9vu0DCVcsAXj88wU0ybQevnVyxlVTd4TyfglDRJ0,622 +opentelemetry/sdk/metrics/view/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/sdk/resources/__init__.py,sha256=T3yx6D6g6CCIjgAmZUWiTOfsbaQQBP6byMchJNBInh4,29491 +opentelemetry/sdk/resources/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/trace/__init__.py,sha256=J8-cygfFp22tzfPeXWawY66hHUV4Os_UiBDPxc6jjbA,51652 +opentelemetry/sdk/trace/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/trace/__pycache__/_tracer_metrics.cpython-312.pyc,, +opentelemetry/sdk/trace/__pycache__/id_generator.cpython-312.pyc,, +opentelemetry/sdk/trace/__pycache__/sampling.cpython-312.pyc,, +opentelemetry/sdk/trace/_sampling_experimental/__init__.py,sha256=pCS4MkSEvtUf1EOH1OlR_2M00VqyCPV53wsmIwSyp4Y,680 +opentelemetry/sdk/trace/_sampling_experimental/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_always_off.cpython-312.pyc,, +opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_always_on.cpython-312.pyc,, +opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_composable.cpython-312.pyc,, +opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_parent_threshold.cpython-312.pyc,, +opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_rule_based.cpython-312.pyc,, +opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_sampler.cpython-312.pyc,, +opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_trace_state.cpython-312.pyc,, +opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_traceid_ratio.cpython-312.pyc,, +opentelemetry/sdk/trace/_sampling_experimental/__pycache__/_util.cpython-312.pyc,, +opentelemetry/sdk/trace/_sampling_experimental/_always_off.py,sha256=3pcI5oTk3v8qH1Xyc45Zl7Lo9seHGqKnSgr0ZHqBEkA,1271 +opentelemetry/sdk/trace/_sampling_experimental/_always_on.py,sha256=S6_ZwgX_qT7rygvi5SkwKYGddc92Lkx4AdoesLm6wC8,1220 +opentelemetry/sdk/trace/_sampling_experimental/_composable.py,sha256=MfGQN_Gv4eRvnhqK_V1QAST4DIgbEkTw0JxDpigzy0E,1626 +opentelemetry/sdk/trace/_sampling_experimental/_parent_threshold.py,sha256=_lZDPGZnApsl4JYWMvWW8fAPvCYbw-B1V8B8AxqAuKA,2761 +opentelemetry/sdk/trace/_sampling_experimental/_rule_based.py,sha256=b4EQ4Iyt-u4Ehcimmy9cKBRb3bIltbljdABrx9VKCJQ,7864 +opentelemetry/sdk/trace/_sampling_experimental/_sampler.py,sha256=LVfP_3SPXwREka8pdAdohdFxoiroyCrNNW7RTaPbjac,3003 +opentelemetry/sdk/trace/_sampling_experimental/_trace_state.py,sha256=Ai5VF1pKASpFrSaD8k3eyi1pNmLV3gNycIA6THlu4yc,3474 +opentelemetry/sdk/trace/_sampling_experimental/_traceid_ratio.py,sha256=SFCQVK2gcZ97kMJbEQR2J9kY4o4LoHaR2BAR30fAeJU,2146 +opentelemetry/sdk/trace/_sampling_experimental/_util.py,sha256=Tfz0TIx770vIMc6SUqdIDfJWZzK-YHuXPz8hv9c_8og,690 +opentelemetry/sdk/trace/_tracer_metrics.py,sha256=qg3qsUjbXsZr7OGOm6J1uzrrS5YEOKG3UUVa1eaUejk,2859 +opentelemetry/sdk/trace/export/__init__.py,sha256=6l6TqmPIdzPsbP4M29doZmhlChXEJLTN0vQoGsFQz-8,10674 +opentelemetry/sdk/trace/export/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/trace/export/__pycache__/in_memory_span_exporter.cpython-312.pyc,, +opentelemetry/sdk/trace/export/in_memory_span_exporter.py,sha256=Z_yud5fTsgrxyG26zRuPKCLtxGNdOlRVRE_QguzOFdQ,1902 +opentelemetry/sdk/trace/id_generator.py,sha256=xKx13eRuGF3QO7-HYEa_XE2tdAnv2zCkXbkEpfc9_Rs,2440 +opentelemetry/sdk/trace/sampling.py,sha256=6JX2LVZZtslPoKErF6_rePHGg-EbASAfAyl3d3qcbV4,18044 +opentelemetry/sdk/util/__init__.py,sha256=IapJX3Bi41Leu_GcvQTfP9SRR0gF0YgdYvJvbgcU4-g,3983 +opentelemetry/sdk/util/__init__.pyi,sha256=yfy1EhRbfHggPu9Ic2sguU2CiHieOZNY7qZQbEvQoDk,2452 +opentelemetry/sdk/util/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/sdk/util/__pycache__/_configurator.cpython-312.pyc,, +opentelemetry/sdk/util/__pycache__/instrumentation.cpython-312.pyc,, +opentelemetry/sdk/util/_configurator.py,sha256=mnIO2HrMIn7k_NNC5CCfFsqBkMDBONmEgZADhNBOS2Q,924 +opentelemetry/sdk/util/instrumentation.py,sha256=illoRgFC-pLJCEDMb0nxdj8f7EOih5T2kZvXT1bu3-M,5097 +opentelemetry/sdk/version/__init__.py,sha256=SxyMcEaovfqNmTxEAGRSXVPfJcv8vuEXNQTu5PiZOjg,100 +opentelemetry/sdk/version/__pycache__/__init__.cpython-312.pyc,, +opentelemetry_sdk-1.45.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +opentelemetry_sdk-1.45.0.dist-info/METADATA,sha256=kR2kdqerx7gtt9zlbfV9kv_DtWRJ7EkqSWZ72hfZO5M,1627 +opentelemetry_sdk-1.45.0.dist-info/RECORD,, +opentelemetry_sdk-1.45.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87 +opentelemetry_sdk-1.45.0.dist-info/entry_points.txt,sha256=6_k5JTKydBw_zDRvkFHbYs9d_lZrXNf60RA3Wg-v894,1544 +opentelemetry_sdk-1.45.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 diff --git a/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/WHEEL new file mode 100644 index 0000000..39dc805 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.32.4 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/entry_points.txt new file mode 100644 index 0000000..377a918 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/entry_points.txt @@ -0,0 +1,38 @@ +[opentelemetry_environment_variables] +sdk = opentelemetry.sdk.environment_variables + +[opentelemetry_id_generator] +random = opentelemetry.sdk.trace.id_generator:RandomIdGenerator + +[opentelemetry_logger_provider] +sdk_logger_provider = opentelemetry.sdk._logs:LoggerProvider + +[opentelemetry_logs_exporter] +console = opentelemetry.sdk._logs.export:ConsoleLogRecordExporter + +[opentelemetry_meter_provider] +sdk_meter_provider = opentelemetry.sdk.metrics:MeterProvider + +[opentelemetry_metrics_exporter] +console = opentelemetry.sdk.metrics.export:ConsoleMetricExporter + +[opentelemetry_resource_detector] +host = opentelemetry.sdk.resources:_HostResourceDetector +os = opentelemetry.sdk.resources:OsResourceDetector +otel = opentelemetry.sdk.resources:OTELResourceDetector +process = opentelemetry.sdk.resources:ProcessResourceDetector +service_instance = opentelemetry.sdk.resources:ServiceInstanceIdResourceDetector + +[opentelemetry_tracer_provider] +sdk_tracer_provider = opentelemetry.sdk.trace:TracerProvider + +[opentelemetry_traces_exporter] +console = opentelemetry.sdk.trace.export:ConsoleSpanExporter + +[opentelemetry_traces_sampler] +always_off = opentelemetry.sdk.trace.sampling:_AlwaysOff +always_on = opentelemetry.sdk.trace.sampling:_AlwaysOn +parentbased_always_off = opentelemetry.sdk.trace.sampling:_ParentBasedAlwaysOff +parentbased_always_on = opentelemetry.sdk.trace.sampling:_ParentBasedAlwaysOn +parentbased_traceidratio = opentelemetry.sdk.trace.sampling:ParentBasedTraceIdRatio +traceidratio = opentelemetry.sdk.trace.sampling:TraceIdRatioBased diff --git a/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_sdk-1.45.0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/METADATA b/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/METADATA new file mode 100644 index 0000000..f9f1116 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/METADATA @@ -0,0 +1,61 @@ +Metadata-Version: 2.5 +Name: opentelemetry-semantic-conventions +Version: 0.66b0 +Summary: OpenTelemetry Semantic Conventions +Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python/tree/main/opentelemetry-semantic-conventions +Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python +Author-email: OpenTelemetry Authors +License-Expression: Apache-2.0 +License-File: LICENSE +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: OpenTelemetry +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.10 +Requires-Dist: opentelemetry-api==1.45.0 +Requires-Dist: typing-extensions>=4.5.0 +Description-Content-Type: text/x-rst + +OpenTelemetry Semantic Conventions +================================== + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-semantic-conventions.svg + :target: https://pypi.org/project/opentelemetry-semantic-conventions/ + +This library contains generated code for the semantic conventions defined by the OpenTelemetry specification. + +Installation +------------ + +:: + + pip install opentelemetry-semantic-conventions + +Code Generation +--------------- + +These files were generated automatically from code in semconv_. +To regenerate the code, run ``../scripts/semconv/generate.sh``. + +To build against a new release or specific commit of opentelemetry-specification_, +update the ``SPEC_VERSION`` variable in +``../scripts/semconv/generate.sh``. Then run the script and commit the changes. + +.. _opentelemetry-specification: https://github.com/open-telemetry/opentelemetry-specification +.. _semconv: https://github.com/open-telemetry/opentelemetry-python/tree/main/scripts/semconv + + +References +---------- + +* `OpenTelemetry Project `_ +* `OpenTelemetry Semantic Conventions Definitions `_ +* `generate.sh script `_ diff --git a/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/RECORD b/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/RECORD new file mode 100644 index 0000000..0202686 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/RECORD @@ -0,0 +1,260 @@ +opentelemetry/semconv/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/semconv/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/semconv/__pycache__/schemas.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/app_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/artifact_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/aws_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/az_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/azure_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/browser_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/cassandra_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/cicd_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/client_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/cloud_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/cloudevents_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/cloudfoundry_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/code_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/container_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/cpu_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/cpython_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/db_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/deployment_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/destination_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/device_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/disk_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/dns_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/elasticsearch_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/enduser_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/error_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/event_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/exception_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/faas_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/feature_flag_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/file_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/gcp_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/gen_ai_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/geo_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/graphql_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/heroku_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/host_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/http_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/hw_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/jsonrpc_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/k8s_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/linux_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/log_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/mainframe_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/mcp_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/message_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/messaging_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/net_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/network_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/nfs_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/oci_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/onc_rpc_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/openai_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/openshift_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/opentracing_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/oracle_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/oracle_cloud_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/os_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/otel_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/other_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/peer_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/pool_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/pprof_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/process_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/profile_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/rpc_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/security_rule_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/server_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/service_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/session_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/source_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/system_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/telemetry_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/test_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/thread_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/tls_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/url_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/user_agent_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/user_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/vcs_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/webengine_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/__pycache__/zos_attributes.cpython-312.pyc,, +opentelemetry/semconv/_incubating/attributes/app_attributes.py,sha256=P8mUQ1oRbx5k3K4tda4oyrip5pq4mHXioBQdfj4A4Kw,4246 +opentelemetry/semconv/_incubating/attributes/artifact_attributes.py,sha256=BWaa7073yEjJGEPMUvVteNhAmvMUkpWsbgV0rkpMMDw,2597 +opentelemetry/semconv/_incubating/attributes/aws_attributes.py,sha256=BbkHv1AkMDbHGfKquwwKEyJw7MZcjK2BwxC2FlxZ-0E,13563 +opentelemetry/semconv/_incubating/attributes/az_attributes.py,sha256=P1YRtme2GlxySyahXWobD0NycZDlw1oa_qFSWdsmI28,325 +opentelemetry/semconv/_incubating/attributes/azure_attributes.py,sha256=HAZy6l-3vzcrdBVidJbxD78jAwk2CTVr-c2Df5KiszA,2561 +opentelemetry/semconv/_incubating/attributes/browser_attributes.py,sha256=OWV-7EkuV__CfdAlEMeap2RJqCuVZwm92Ov2t70oLHQ,4650 +opentelemetry/semconv/_incubating/attributes/cassandra_attributes.py,sha256=KCIGMbC9J9qMrNW-ntUDk-QWO0SGbr0QIkzgprQQDY4,1574 +opentelemetry/semconv/_incubating/attributes/cicd_attributes.py,sha256=bVfzQF-28Yc-nX5oQy50dNeIefWTYyQui4HgzSKqj0k,5562 +opentelemetry/semconv/_incubating/attributes/client_attributes.py,sha256=758TN5RYiOVa0bfHoS74v_PTir-WLRJA7EPgl8OkSOo,411 +opentelemetry/semconv/_incubating/attributes/cloud_attributes.py,sha256=FSvDiACrOdugPYJgs08sA9sFyxRIcdxXzoaXyW5dWq0,6937 +opentelemetry/semconv/_incubating/attributes/cloudevents_attributes.py,sha256=Ch4R2Tl6T2doilp36kFfQXwq2za27UwpRIVTfEXKyeY,1205 +opentelemetry/semconv/_incubating/attributes/cloudfoundry_attributes.py,sha256=LpMvzzDjiNj7epgMEfFM9w8joFZLvvEiq8FEKW-uiJQ,4210 +opentelemetry/semconv/_incubating/attributes/code_attributes.py,sha256=AQ5lLJqLIqeaFcGvN9xo5v1mfWw-beRSCuF4yMq5xhw,1504 +opentelemetry/semconv/_incubating/attributes/container_attributes.py,sha256=e4rGdoFrKcs4GhLj08_AqZa1IE2HY6QEiZx61dwP4ZI,4596 +opentelemetry/semconv/_incubating/attributes/cpu_attributes.py,sha256=oj68vbiGU3fIaKA0Vvxjl4yK9GhfW_XJsdsp5PYvN5Y,638 +opentelemetry/semconv/_incubating/attributes/cpython_attributes.py,sha256=DU8zkHtw07vS-lyk5Yrpedy3j0_tFQNochXYa3ELQyw,418 +opentelemetry/semconv/_incubating/attributes/db_attributes.py,sha256=hzz3mX_yIRY3NPsjxRc8SSCSqQAZmBTdZxiokzqEacA,17689 +opentelemetry/semconv/_incubating/attributes/deployment_attributes.py,sha256=86LIfwncym9fmhKOM4L_VZXZlAEkyRcCofZGP1fXRhA,1754 +opentelemetry/semconv/_incubating/attributes/destination_attributes.py,sha256=X22a67gNOcOeTf-mwxAGZs0vnlcgowzbF9GpJ_ziIoU,586 +opentelemetry/semconv/_incubating/attributes/device_attributes.py,sha256=fzhNZwcIeXgVeUb0Z9AADP8aYrwWaMQUeoT6StyYxxA,2196 +opentelemetry/semconv/_incubating/attributes/disk_attributes.py,sha256=DtHjMTZpvgrF8ASCP1bG8OlDVzlQCSsginkCpKY0Xno,321 +opentelemetry/semconv/_incubating/attributes/dns_attributes.py,sha256=mN7zYPi88Ktxfkc2ISj8JErPc_WowJ9gJ-Q8WYzbDBM,408 +opentelemetry/semconv/_incubating/attributes/elasticsearch_attributes.py,sha256=T41ptbS4CWhdj1G97i7ggh9VxZ86XkFn117-6lHkQU8,263 +opentelemetry/semconv/_incubating/attributes/enduser_attributes.py,sha256=xd5lBu6tkTVWOdxW-WwyN1y3iJtssgeWS8Wz_ykVm_w,886 +opentelemetry/semconv/_incubating/attributes/error_attributes.py,sha256=dXSIJ3bIYhyG7CyuZiU18D3roV8z-wH732DYZMHXsBI,795 +opentelemetry/semconv/_incubating/attributes/event_attributes.py,sha256=FOhHsPAYrUS_9mXp6t8Pv-hbdWrBa6x6e3hRVB88hsA,305 +opentelemetry/semconv/_incubating/attributes/exception_attributes.py,sha256=kzCoQRISTYuYtVL4I6YTSsysGeExPLhcCjifoDVP38w,787 +opentelemetry/semconv/_incubating/attributes/faas_attributes.py,sha256=hvm24AzaqXKt8X8yXNX9Dvs80VQls3-V6igFAduQFbY,5669 +opentelemetry/semconv/_incubating/attributes/feature_flag_attributes.py,sha256=UVczQVsBFTZWZVMItp9aOw0UyVvm_JzJGCy6ACGe0ao,4775 +opentelemetry/semconv/_incubating/attributes/file_attributes.py,sha256=oVCPESDjMygWqpZ4S4wE44k5PxFQq1GmnO9MXY595Ts,4191 +opentelemetry/semconv/_incubating/attributes/gcp_attributes.py,sha256=5Og9_dJGiUuECY9bOq965I-mi3UWYygzCj2sS62ZUqY,8678 +opentelemetry/semconv/_incubating/attributes/gen_ai_attributes.py,sha256=bYWAiNAC8dFDeLZtvGGJnA2xdaZz6sDe4FwJOHSaesg,19231 +opentelemetry/semconv/_incubating/attributes/geo_attributes.py,sha256=UtAokzOaQJjRYCZgbleNprLIIHmruAqSsr7QbwjPqnw,1465 +opentelemetry/semconv/_incubating/attributes/graphql_attributes.py,sha256=n7efocnfKlgdSGBTyDLVoKTvAqEPWwoqhKsftZ9JVl8,735 +opentelemetry/semconv/_incubating/attributes/heroku_attributes.py,sha256=Wo5QMXw1zdKy7U4gW1busc5_QXph2iV9jV5g_j6D3CI,417 +opentelemetry/semconv/_incubating/attributes/host_attributes.py,sha256=36zw9hjUUPtLsZsv3wVKTuOOO3J_JTGjDV3VrMamXAE,3101 +opentelemetry/semconv/_incubating/attributes/http_attributes.py,sha256=1XWmSyZdL_Lql4ThSGdX_G_WF1hWKR2hsoI1EH0ADqw,6744 +opentelemetry/semconv/_incubating/attributes/hw_attributes.py,sha256=WYaieaeaQx8M-PVTKCf2QaLIDU1QCIk2oOAPDSP1rco,5376 +opentelemetry/semconv/_incubating/attributes/jsonrpc_attributes.py,sha256=8JtSTw6TnGTjEM3YL8TMEi1ffbvN-m31iIHbRy_-BME,796 +opentelemetry/semconv/_incubating/attributes/k8s_attributes.py,sha256=mHwfFQGax3FMoTkhuaD4iL9EPODv-LmVXp1nLrj9vlU,28571 +opentelemetry/semconv/_incubating/attributes/linux_attributes.py,sha256=4CgsPx5A-c6pSgJIPMvdTg-ss20tVpHwVgwfomfaads,564 +opentelemetry/semconv/_incubating/attributes/log_attributes.py,sha256=z4nTtkl73Ag-3m08-RYjqcWhvAXTfF-w9RA3L6yGP5U,1581 +opentelemetry/semconv/_incubating/attributes/mainframe_attributes.py,sha256=eMxOeJwOeY_MMpDP84GPuket_fuafzg3A-FD28cdL1o,248 +opentelemetry/semconv/_incubating/attributes/mcp_attributes.py,sha256=QIpFRexKCKO70xFxA5_61folw4hJ2ZF92P7qIHwjCXY,3887 +opentelemetry/semconv/_incubating/attributes/message_attributes.py,sha256=yL_AtSWFCORyiIplfWyi_dQ7uT0GrjVn-xMB1-VmSak,817 +opentelemetry/semconv/_incubating/attributes/messaging_attributes.py,sha256=zlaQLj9UciJlUe1CHYCZ--02M4r-g2Dg_bgtcFGi8gY,12481 +opentelemetry/semconv/_incubating/attributes/net_attributes.py,sha256=HQgfJQjljIofP06Cwd_6PP2s02h03QXp47Eghec8loQ,2421 +opentelemetry/semconv/_incubating/attributes/network_attributes.py,sha256=wXSCyb2FltwSV_BI6NnAOh9x11yOSEgrN6kyk2s5zd4,6163 +opentelemetry/semconv/_incubating/attributes/nfs_attributes.py,sha256=aV6SiYS_5onGhM94hv-iZqnxivSgE4HkxPQFcCZx_t4,383 +opentelemetry/semconv/_incubating/attributes/oci_attributes.py,sha256=_cjCVgWE8EY_8t17AMtC2AOBKFKVIxtX8pF8hEOLcGs,667 +opentelemetry/semconv/_incubating/attributes/onc_rpc_attributes.py,sha256=HXFNacQ8N5O5gYio_hcYAiCPkfXvGXvNvIqjaKCR-h0,465 +opentelemetry/semconv/_incubating/attributes/openai_attributes.py,sha256=RdxO_-upeIhsKszoY8WKg7vY9OUcbax185cvvDXp6hE,1982 +opentelemetry/semconv/_incubating/attributes/openshift_attributes.py,sha256=TW_Q4kOb96JPkRrRQT8BzN83E2feXH4vDLYeg8u3Anc,313 +opentelemetry/semconv/_incubating/attributes/opentracing_attributes.py,sha256=XJ0z4FvbRBdPtiAvl1QLudpgx-KPjOX6w_3osoWqVb8,540 +opentelemetry/semconv/_incubating/attributes/oracle_attributes.py,sha256=BBXbwFyNg4PBQkPYo-hwNzHExcJUBV9BLQhRlCDT43w,2149 +opentelemetry/semconv/_incubating/attributes/oracle_cloud_attributes.py,sha256=N3nWDA93dduzLooff6XU4NrAfzOaABPASR2rZi8Rs28,378 +opentelemetry/semconv/_incubating/attributes/os_attributes.py,sha256=eFZmXrSiNbcIePbNoMoSWrsOdvTP5f3agYqEk2BGeNg,1350 +opentelemetry/semconv/_incubating/attributes/otel_attributes.py,sha256=9JZAa_3IIG0XWNjlQYaYKl6uFSXA5UDidSPFolWhrBw,6390 +opentelemetry/semconv/_incubating/attributes/other_attributes.py,sha256=FYXiSUNVKPznZbIIrURWe52av-K7d9_q8kOl2f-1fBo,439 +opentelemetry/semconv/_incubating/attributes/peer_attributes.py,sha256=xP9mfl1WWWl2j6CVjtoIU7U8jLdMUfK2lOcd97GmThM,193 +opentelemetry/semconv/_incubating/attributes/pool_attributes.py,sha256=eR18pjkRGqRxwjnVON39OjmxdvVW_WO_hI-FfGEhgHA,200 +opentelemetry/semconv/_incubating/attributes/pprof_attributes.py,sha256=xf64y9zWrHUPiB9nuuK3n7KYAQgf8GkA6lOm3bN_tw8,2458 +opentelemetry/semconv/_incubating/attributes/process_attributes.py,sha256=IsNmbgUYZ1kDekemPrxsWpa5msOMp8Oav6AYdHtAl90,8376 +opentelemetry/semconv/_incubating/attributes/profile_attributes.py,sha256=JSxm1PhbcXMnuDCS9U4a009JIsznXpyFEJa-rjrHqgU,1639 +opentelemetry/semconv/_incubating/attributes/rpc_attributes.py,sha256=1y6trolRUPbAFIVJ0D5GPbgQvhW5XC2JSsX1eNm2toM,8692 +opentelemetry/semconv/_incubating/attributes/security_rule_attributes.py,sha256=C0GbvlAGmKE5J1tS3yUGweTPEnMz2GihaDIjrBcZWHo,1491 +opentelemetry/semconv/_incubating/attributes/server_attributes.py,sha256=Pz6tEvmMaleL6hw-Vryqnv1eaPuh_GL0hktf4ZCoWjs,411 +opentelemetry/semconv/_incubating/attributes/service_attributes.py,sha256=4YezIUOLVAM8LFCAlvumIRlP1i-uc8nI9s9c3NL67b4,2160 +opentelemetry/semconv/_incubating/attributes/session_attributes.py,sha256=amUxlU4vL3sYYpkw3R4B-bGfTDOv3KYKSt_IlDlJHaM,292 +opentelemetry/semconv/_incubating/attributes/source_attributes.py,sha256=QV_HQ3FAvU2rR0J294wIit5ynXFbwzv8SYP6Yb_C5n4,551 +opentelemetry/semconv/_incubating/attributes/system_attributes.py,sha256=9oC5uQZViF03n3VxMFKPHrCJcmHBSNmPgKhLE_TKC3E,5154 +opentelemetry/semconv/_incubating/attributes/telemetry_attributes.py,sha256=YQiDNJz9Ww6p_uM_kpiQ9Oe1fIad_K2lg6fZbdFrB8U,3336 +opentelemetry/semconv/_incubating/attributes/test_attributes.py,sha256=8MmNMOof0qSTJ5Ksyvekum8V0WIbHfCRgG3omXDkFtE,1061 +opentelemetry/semconv/_incubating/attributes/thread_attributes.py,sha256=Bj7P63hxIugl_UfdebfLnHSwQv56o5gwiKUwkJYBwNs,953 +opentelemetry/semconv/_incubating/attributes/tls_attributes.py,sha256=n5teM-XGmenniGDzG91Dklh3v1tPSiOkTpV-EXYeBIg,6148 +opentelemetry/semconv/_incubating/attributes/url_attributes.py,sha256=W-AiZQAjmqc6CdLOxz6J0CA7l3QKGgIzaLh-JhbvdMA,3670 +opentelemetry/semconv/_incubating/attributes/user_agent_attributes.py,sha256=46fsqHK_DbjXlaus9Ay9d7EkpaaiWNIivHE98erWkAo,2593 +opentelemetry/semconv/_incubating/attributes/user_attributes.py,sha256=tUHS2Uz4K8zgp_JxO2MBNgLlHldgdY7R33uCtaBWjLc,676 +opentelemetry/semconv/_incubating/attributes/vcs_attributes.py,sha256=egSke62oryHqoekzf_n4Eey7V5n5pFfHCIh95uWqAIc,8708 +opentelemetry/semconv/_incubating/attributes/webengine_attributes.py,sha256=-iJDXycLqS8V-HMB1ovIf6DhQC4A5n5iPlqAii3orxw,421 +opentelemetry/semconv/_incubating/attributes/zos_attributes.py,sha256=st3daTGyWCmgNRavPH4O7hgVVqFq2ABAS0quJdlNkfY,433 +opentelemetry/semconv/_incubating/metrics/__pycache__/azure_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/cicd_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/container_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/cpu_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/cpython_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/db_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/dns_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/faas_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/gen_ai_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/http_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/hw_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/k8s_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/mcp_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/messaging_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/nfs_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/openshift_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/otel_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/process_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/rpc_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/system_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/__pycache__/vcs_metrics.cpython-312.pyc,, +opentelemetry/semconv/_incubating/metrics/azure_metrics.py,sha256=dmFt0gKN4mjEtgGwAdCYYQX0kF0Fh5X-0RXSBR2GAas,1394 +opentelemetry/semconv/_incubating/metrics/cicd_metrics.py,sha256=Hwv0C0bP074V99WhNs43769C_1nZSv7moKMXvQXXyRw,3156 +opentelemetry/semconv/_incubating/metrics/container_metrics.py,sha256=FsBx1iOwtEuXjlDrD9HfJ8fpKKmCsEI8wtJvzUHOZls,11065 +opentelemetry/semconv/_incubating/metrics/cpu_metrics.py,sha256=ybnrEqfZK0meqep7FbZT4GfiV5Ht3gDc4M08hrUEGVY,1662 +opentelemetry/semconv/_incubating/metrics/cpython_metrics.py,sha256=IgKhRV8tLN0-VVj7CFHXURKkvDGOXcvdF79ofn3FHTQ,2231 +opentelemetry/semconv/_incubating/metrics/db_metrics.py,sha256=nKcZIxDjXLLa_61qCJ3PO2SXsE9i1vrjIkp2kIqEg80,11726 +opentelemetry/semconv/_incubating/metrics/dns_metrics.py,sha256=S50oAQ2QTPLFtruu5UgxhFl59yjb1NE2U8yD5egbWNw,577 +opentelemetry/semconv/_incubating/metrics/faas_metrics.py,sha256=VRaVpwNIeXEE3SUMl_kSfm_XYdOXlGv71k8ISjk2rhk,3741 +opentelemetry/semconv/_incubating/metrics/gen_ai_metrics.py,sha256=O8RurQc59yBc_zJoRsIVAJ_agYQ7m3rzwivdCE8ycSQ,4551 +opentelemetry/semconv/_incubating/metrics/http_metrics.py,sha256=zyO10QNCCIz12jH7ELTbCCK9i-9RhU8j2AmxAAIil_0,6141 +opentelemetry/semconv/_incubating/metrics/hw_metrics.py,sha256=N_ZwhWwoiBxlgO84jBh0UZig5mY2a26SUsrT8Nl3MX0,20917 +opentelemetry/semconv/_incubating/metrics/k8s_metrics.py,sha256=MOs3PfUHcENZKUipIhOcFnE_euxibQmii7anZAAgZio,114479 +opentelemetry/semconv/_incubating/metrics/mcp_metrics.py,sha256=0BWdGaCWtFwIosJJCaAADKBbFPoDlwFM8792d7XxCgk,2640 +opentelemetry/semconv/_incubating/metrics/messaging_metrics.py,sha256=eP6jjM8J6fkogH00VSFxNZU6TRDCCtGUEF9qDBou2oA,5702 +opentelemetry/semconv/_incubating/metrics/nfs_metrics.py,sha256=U5em8cju4vd-pPp7vLuX4egYVo9DSQTY-WBdCw-27UE,9614 +opentelemetry/semconv/_incubating/metrics/openshift_metrics.py,sha256=e_Plr1DIhMZPxtASO6noe0vsEHKAQanoRY9S3sl4G4g,20412 +opentelemetry/semconv/_incubating/metrics/otel_metrics.py,sha256=izFZMLFAvAhEXT8EbWdcnMHY_LlHlSih6aUekm2iixY,20501 +opentelemetry/semconv/_incubating/metrics/process_metrics.py,sha256=9u3NTCjrJU0Hld5FIUo5mLaat4u3A06aQAzThxjYW2g,8110 +opentelemetry/semconv/_incubating/metrics/rpc_metrics.py,sha256=IXAyUrHggDVXSZuBxLwTaItTHnaVPxgKqcFZ3BTsVGs,5894 +opentelemetry/semconv/_incubating/metrics/system_metrics.py,sha256=FzVO5DcfLMK2XEqluKNydQQWCogkdSsgX35ty0yvKgU,26250 +opentelemetry/semconv/_incubating/metrics/vcs_metrics.py,sha256=Om7cs9nqw2SHxgvjcENIrPDklspilAsxqa5vmXkAsUE,7488 +opentelemetry/semconv/attributes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/semconv/attributes/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/client_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/code_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/container_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/db_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/deployment_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/error_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/exception_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/http_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/k8s_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/network_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/otel_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/server_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/service_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/telemetry_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/url_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/__pycache__/user_agent_attributes.cpython-312.pyc,, +opentelemetry/semconv/attributes/client_attributes.py,sha256=3XugmNbZRFwlPwiX3X8tdsDzcZNPr-wI5-PxfvtXRxg,752 +opentelemetry/semconv/attributes/code_attributes.py,sha256=09ZESrdbGtcnFUFLLqhac-Odj1aBF-R9MKcrmKYXIy4,2856 +opentelemetry/semconv/attributes/container_attributes.py,sha256=y9JAlMbNWLrVyjbUzOrdTdmQ84cI-3GgcsF_3kNom5A,1235 +opentelemetry/semconv/attributes/db_attributes.py,sha256=PmEF-0W9KGr3z33sDibDzggBO9XYN_u1PeAyvSli7Xc,6670 +opentelemetry/semconv/attributes/deployment_attributes.py,sha256=ANLIW_VHZGMHKhHoaOKQp89-82pempjkM8Rems4m_tA,1039 +opentelemetry/semconv/attributes/error_attributes.py,sha256=_SfeQMcQKW8v7zxrw4aQEMgJFxSTlbW3vV8Uspom0N8,1615 +opentelemetry/semconv/attributes/exception_attributes.py,sha256=WfT62ExNUjesOd9SBU3Aemc4rMVKVCqxfWkp7RlCF4Y,1180 +opentelemetry/semconv/attributes/http_attributes.py,sha256=p9HzoGkuXdrAb_48ugo36K3fxzmJjh1fAeaMBTDH4bU,6735 +opentelemetry/semconv/attributes/k8s_attributes.py,sha256=3X4BFsB6U6Ln_VdqZTD_ZylI0o3ovlXMAMR56pnwfOI,13432 +opentelemetry/semconv/attributes/network_attributes.py,sha256=8PktP1mrV-i_1vXj9psXyU-Z-E-IIf8DfnJOsj2GZGc,2227 +opentelemetry/semconv/attributes/otel_attributes.py,sha256=onA0-oqNlXXnjeTVNO4V0TXf1s7XOQF2T1LtHW_DjG8,1274 +opentelemetry/semconv/attributes/server_attributes.py,sha256=C4hvr69ItOM88MmCvp4roXtGKWBiUDzh72RaXLAr2Pk,740 +opentelemetry/semconv/attributes/service_attributes.py,sha256=OFJyi_G49RSySJA41CJnu4Yu6hcESwreTFXGUnhS4Lg,3482 +opentelemetry/semconv/attributes/telemetry_attributes.py,sha256=swHjmX28h4aBxD_bh5a482BkFPUsLl0OLxKR4irnbOg,2587 +opentelemetry/semconv/attributes/url_attributes.py,sha256=TNcc533K3gcCx-QNaLRzFWW1Wscf3N0J5TrI3_sc2co,4427 +opentelemetry/semconv/attributes/user_agent_attributes.py,sha256=38zEhXdfkhNA3wRNBd6G2lvtPIk8NKwa71x2ZMv_lec,282 +opentelemetry/semconv/metrics/__init__.py,sha256=EjYmzjCdBg7k3oEDcNGlDaqsaHouTIR3W111R3Brkp8,5188 +opentelemetry/semconv/metrics/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/semconv/metrics/__pycache__/db_metrics.cpython-312.pyc,, +opentelemetry/semconv/metrics/__pycache__/http_metrics.cpython-312.pyc,, +opentelemetry/semconv/metrics/db_metrics.py,sha256=578S6E6iFbwWu7G83daYaX5zmfUK11FZV8zU8K_4yww,315 +opentelemetry/semconv/metrics/http_metrics.py,sha256=jNZDd8h7n0KI2glXK05BNDHpjhRfd9rm5BYHoBGDqqk,386 +opentelemetry/semconv/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +opentelemetry/semconv/resource/__init__.py,sha256=DYyETRcylvH9bO2S06du9Bc-O_zrz-xU5CD7XL-mIRU,32470 +opentelemetry/semconv/resource/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/semconv/schemas.py,sha256=UdpdR1jHB9GNW8JwAPDZ3rCPJ9aRlNMcHAFr5S4Y6kQ,3057 +opentelemetry/semconv/trace/__init__.py,sha256=XaFv6YmSHJMpk_0HnBB2Nt5qRNo5EPjRuZLhsf7U1Ik,68626 +opentelemetry/semconv/trace/__pycache__/__init__.cpython-312.pyc,, +opentelemetry/semconv/version/__init__.py,sha256=KwYGGkuL2hTn_9n8uwsZGL0Qk_b8OYRBjsy-X1aysMY,100 +opentelemetry/semconv/version/__pycache__/__init__.cpython-312.pyc,, +opentelemetry_semantic_conventions-0.66b0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +opentelemetry_semantic_conventions-0.66b0.dist-info/METADATA,sha256=kf_1c5J1fxE4TSEx2K6G1h88Hj0-2FtIW4UGvC3sYiE,2408 +opentelemetry_semantic_conventions-0.66b0.dist-info/RECORD,, +opentelemetry_semantic_conventions-0.66b0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87 +opentelemetry_semantic_conventions-0.66b0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 diff --git a/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/WHEEL new file mode 100644 index 0000000..39dc805 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.32.4 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/opentelemetry_semantic_conventions-0.66b0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/METADATA b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/METADATA new file mode 100644 index 0000000..9a85a75 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/METADATA @@ -0,0 +1,112 @@ +Metadata-Version: 2.4 +Name: packaging +Version: 26.3 +Summary: Core utilities for Python packages +Author-email: Donald Stufft +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-Expression: Apache-2.0 OR BSD-2-Clause +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: 3.15 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Python :: Free Threading :: 4 - Resilient +Classifier: Typing :: Typed +License-File: LICENSE +License-File: LICENSE.APACHE +License-File: LICENSE.BSD +Project-URL: Documentation, https://packaging.pypa.io/ +Project-URL: Source, https://github.com/pypa/packaging + +packaging +========= + +.. start-intro + +Reusable core utilities for various Python Packaging +`interoperability specifications `_. + +This library provides utilities that implement the interoperability +specifications which have clearly one correct behaviour (eg: :pep:`440`) +or benefit greatly from having a single shared implementation (eg: :pep:`425`). + +.. end-intro + +The ``packaging`` project includes the following: version handling, specifiers, +markers, requirements, tags, metadata, lockfiles, utilities. + +Documentation +------------- + +The `documentation`_ provides information and the API for the following: + +- Version Handling +- Specifiers +- Markers +- Licenses +- Requirements +- Metadata +- Tags +- Lockfiles (pylock) +- Direct URL helpers +- Dependency groups +- Errors +- Utilities + +Installation +------------ + +Use ``pip`` to install these utilities:: + + pip install packaging + +The ``packaging`` library uses calendar-based versioning (``YY.N``). + +Discussion +---------- + +If you run into bugs, you can file them in our `issue tracker`_. + +You can also join discussions on `GitHub Discussions`_ to ask questions or get involved. + +.. _`documentation`: https://packaging.pypa.io/ +.. _`issue tracker`: https://github.com/pypa/packaging/issues +.. _`GitHub Discussions`: https://github.com/pypa/packaging/discussions + + +Code of Conduct +--------------- + +Everyone interacting in the packaging project's codebases, issue trackers, chat +rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_. + +.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md + +Contributing +------------ + +The ``CONTRIBUTING.rst`` file outlines how to contribute to this project as +well as how to report a potential security issue. The documentation for this +project also covers information about `project development`_ and `security`_. + +.. _`project development`: https://packaging.pypa.io/en/latest/development/ +.. _`security`: https://packaging.pypa.io/en/latest/security/ + +Project History +--------------- + +Please review the ``CHANGELOG.rst`` file or the `Changelog documentation`_ for +recent changes and project history. + +.. _`Changelog documentation`: https://packaging.pypa.io/en/latest/changelog/ + diff --git a/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/RECORD b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/RECORD new file mode 100644 index 0000000..14e67c0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/RECORD @@ -0,0 +1,52 @@ +packaging-26.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +packaging-26.3.dist-info/METADATA,sha256=cP24n8TUqaBDv3NyuJcrzIg_3f80q1Xpz4DXOHU4R2M,3544 +packaging-26.3.dist-info/RECORD,, +packaging-26.3.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +packaging-26.3.dist-info/licenses/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197 +packaging-26.3.dist-info/licenses/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174 +packaging-26.3.dist-info/licenses/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344 +packaging/__init__.py,sha256=bnWM3QAossrXTHObmtycnwbwVJMjBigLh4v-WdOSGbU,494 +packaging/__pycache__/__init__.cpython-312.pyc,, +packaging/__pycache__/_elffile.cpython-312.pyc,, +packaging/__pycache__/_manylinux.cpython-312.pyc,, +packaging/__pycache__/_musllinux.cpython-312.pyc,, +packaging/__pycache__/_parser.cpython-312.pyc,, +packaging/__pycache__/_ranges.cpython-312.pyc,, +packaging/__pycache__/_structures.cpython-312.pyc,, +packaging/__pycache__/_tokenizer.cpython-312.pyc,, +packaging/__pycache__/dependency_groups.cpython-312.pyc,, +packaging/__pycache__/direct_url.cpython-312.pyc,, +packaging/__pycache__/errors.cpython-312.pyc,, +packaging/__pycache__/markers.cpython-312.pyc,, +packaging/__pycache__/metadata.cpython-312.pyc,, +packaging/__pycache__/pylock.cpython-312.pyc,, +packaging/__pycache__/ranges.cpython-312.pyc,, +packaging/__pycache__/requirements.cpython-312.pyc,, +packaging/__pycache__/specifiers.cpython-312.pyc,, +packaging/__pycache__/tags.cpython-312.pyc,, +packaging/__pycache__/utils.cpython-312.pyc,, +packaging/__pycache__/version.cpython-312.pyc,, +packaging/_elffile.py,sha256=HwJfVMVz8ahXQMT72QjMHR-wwXfBQ3fr9fCdbNU2h4A,3236 +packaging/_manylinux.py,sha256=W26EMkxCU4q0zbFI4kMP26NvBEJ7Dv2lW-oIOjX6YTY,10000 +packaging/_musllinux.py,sha256=Ayj7gnsRcMd99MEH1Jvo3403JiEDSQph6Kytii-crf8,2774 +packaging/_parser.py,sha256=iKNIEVIJMnSX5FO2NeXbCZ_VkvkzXXEMcxOynNswxQw,12639 +packaging/_ranges.py,sha256=t7WV8uaSrrNpfFRbhcJMu8tFUvKtxtkFmqgtCHfoFrc,30850 +packaging/_structures.py,sha256=60jRbF78p8z5MKnNd6cAprgOadCJHV0DlmUmRBqFZcs,1109 +packaging/_tokenizer.py,sha256=zCXlfA1WTInhUHNHHW4cJkKZypRvsXwOHkr1TvdxPuY,5573 +packaging/dependency_groups.py,sha256=UwnMVkyjAi37WUgW6dWTuwnN80IIwfuMXsmvtsZwJ4w,11224 +packaging/direct_url.py,sha256=hDuqldBMu0kojenZ_XfulG6GLLYDEj5-RqaeFx-n0Gs,11645 +packaging/errors.py,sha256=6hfEYXAf8v_IF65-lFadJOMIieBP2xIKtyEXjG1nGIs,2680 +packaging/licenses/__init__.py,sha256=WQ0S7uc92-xTtOnuW_xk2cs6rLSUDo68uiYHV85WdAs,7859 +packaging/licenses/__pycache__/__init__.cpython-312.pyc,, +packaging/licenses/__pycache__/_spdx.cpython-312.pyc,, +packaging/licenses/_spdx.py,sha256=WW7DXiyg68up_YND_wpRYlr1SHhiV4FfJLQffghhMxQ,51122 +packaging/markers.py,sha256=bL6GDTXZjStZnn8Oxz_qsSTgFXEbML6QReZ4ySlA2cw,20554 +packaging/metadata.py,sha256=k0FJ9CClRJ5vN2qmHW8moII-uCzHxqHIzeWqe-Q9D2M,42059 +packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +packaging/pylock.py,sha256=G84Kn-Y-Qrh-jO0F2icrrxamiqcEs3GEBeJzWmESRnw,35232 +packaging/ranges.py,sha256=tHgc6RPl3_ngeQbyBYXFTDAo4ff_VjQYKOedUi3b-rY,83054 +packaging/requirements.py,sha256=RZR26yEH_jDmsTEAUaW9tEx9O3944Oh7raJoKCy6YvU,7040 +packaging/specifiers.py,sha256=ZhrTHd9F_7hh0mJnNPTj1mlmApuP23lYa7EFgl2NnVk,52798 +packaging/tags.py,sha256=DnF6CCp5Yg15zwMhtCDfFVk8SRb82gPCfLnYLgfvXW4,38302 +packaging/utils.py,sha256=oTdCzZmU7spa37AknRJkCMR4Am6x9Iy5evvy_p8o7MY,12215 +packaging/version.py,sha256=VYsvtQ_RmMZgpMCC-WuJGcVlkRtrS24H80trYMB0vpc,39040 diff --git a/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/WHEEL b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/WHEEL new file mode 100644 index 0000000..d8b9936 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/licenses/LICENSE new file mode 100644 index 0000000..6f62d44 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/licenses/LICENSE @@ -0,0 +1,3 @@ +This software is made available under the terms of *either* of the licenses +found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made +under the terms of *both* these licenses. diff --git a/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/licenses/LICENSE.APACHE b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/licenses/LICENSE.APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/licenses/LICENSE.APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/licenses/LICENSE.BSD b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/licenses/LICENSE.BSD new file mode 100644 index 0000000..42ce7b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging-26.3.dist-info/licenses/LICENSE.BSD @@ -0,0 +1,23 @@ +Copyright (c) Donald Stufft and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/packaging/__init__.py b/venv/lib/python3.12/site-packages/packaging/__init__.py new file mode 100644 index 0000000..5a96915 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/__init__.py @@ -0,0 +1,15 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "26.3" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD-2-Clause or Apache-2.0" +__copyright__ = f"2014 {__author__}" diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b725a05 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/_elffile.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/_elffile.cpython-312.pyc new file mode 100644 index 0000000..9024893 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/_elffile.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/_manylinux.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/_manylinux.cpython-312.pyc new file mode 100644 index 0000000..39d018e Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/_manylinux.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/_musllinux.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/_musllinux.cpython-312.pyc new file mode 100644 index 0000000..57e8c86 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/_musllinux.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/_parser.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/_parser.cpython-312.pyc new file mode 100644 index 0000000..e306b35 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/_parser.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/_ranges.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/_ranges.cpython-312.pyc new file mode 100644 index 0000000..4ae1884 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/_ranges.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/_structures.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/_structures.cpython-312.pyc new file mode 100644 index 0000000..4cd20c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/_structures.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/_tokenizer.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/_tokenizer.cpython-312.pyc new file mode 100644 index 0000000..6be2aac Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/_tokenizer.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/dependency_groups.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/dependency_groups.cpython-312.pyc new file mode 100644 index 0000000..cbd950b Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/dependency_groups.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/direct_url.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/direct_url.cpython-312.pyc new file mode 100644 index 0000000..d87a263 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/direct_url.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/errors.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/errors.cpython-312.pyc new file mode 100644 index 0000000..f0070f0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/errors.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/markers.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/markers.cpython-312.pyc new file mode 100644 index 0000000..bdc8d90 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/markers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/metadata.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/metadata.cpython-312.pyc new file mode 100644 index 0000000..e691673 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/metadata.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/pylock.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/pylock.cpython-312.pyc new file mode 100644 index 0000000..b420d97 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/pylock.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/ranges.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/ranges.cpython-312.pyc new file mode 100644 index 0000000..1377f6f Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/ranges.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/requirements.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/requirements.cpython-312.pyc new file mode 100644 index 0000000..8a02b7d Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/requirements.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/specifiers.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/specifiers.cpython-312.pyc new file mode 100644 index 0000000..d8cdeb1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/specifiers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/tags.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/tags.cpython-312.pyc new file mode 100644 index 0000000..29fb571 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/tags.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..33d5249 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/__pycache__/version.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/__pycache__/version.cpython-312.pyc new file mode 100644 index 0000000..a00dbc8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/__pycache__/version.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/_elffile.py b/venv/lib/python3.12/site-packages/packaging/_elffile.py new file mode 100644 index 0000000..1ce081d --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/_elffile.py @@ -0,0 +1,108 @@ +""" +ELF file parser. + +This provides a class ``ELFFile`` that parses an ELF executable in a similar +interface to ``ZipFile``. Only the read interface is implemented. + +ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html +""" + +from __future__ import annotations + +import enum +import os +import struct +from typing import IO + + +class ELFInvalid(ValueError): + pass + + +class EIClass(enum.IntEnum): + C32 = 1 + C64 = 2 + + +class EIData(enum.IntEnum): + Lsb = 1 + Msb = 2 + + +class EMachine(enum.IntEnum): + I386 = 3 + S390 = 22 + Arm = 40 + X8664 = 62 + AArch64 = 183 + + +class ELFFile: + """ + Representation of an ELF executable. + """ + + def __init__(self, f: IO[bytes]) -> None: + self._f = f + + try: + ident = self._read("16B") + except struct.error as e: + raise ELFInvalid("unable to parse identification") from e + magic = bytes(ident[:4]) + if magic != b"\x7fELF": + raise ELFInvalid(f"invalid magic: {magic!r}") + + self.capacity = ident[4] # Format for program header (bitness). + self.encoding = ident[5] # Data structure encoding (endianness). + + try: + # e_fmt: Format for the ELF header. + # p_fmt: Format for a program header. + # p_idx: Indexes to find p_type, p_offset, and p_filesz. + e_fmt, self._p_fmt, self._p_idx = { + (1, 1): ("HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB. + (2, 1): ("HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB. + }[(self.capacity, self.encoding)] + except KeyError as e: + raise ELFInvalid( + f"unrecognized capacity ({self.capacity}) or encoding ({self.encoding})" + ) from e + + try: + ( + _, + self.machine, # Architecture type. + _, + _, + self._e_phoff, # Offset of program header. + _, + self.flags, # Processor-specific flags. + _, + self._e_phentsize, # Size of a program header entry. + self._e_phnum, # Number of program headers. + ) = self._read(e_fmt) + except struct.error as e: + raise ELFInvalid("unable to parse machine and section information") from e + + def _read(self, fmt: str) -> tuple[int, ...]: + return struct.unpack(fmt, self._f.read(struct.calcsize(fmt))) + + @property + def interpreter(self) -> str | None: + """ + The path recorded in the ``PT_INTERP`` section header. + """ + for index in range(self._e_phnum): + self._f.seek(self._e_phoff + self._e_phentsize * index) + try: + data = self._read(self._p_fmt) + except struct.error: + continue + if data[self._p_idx[0]] != 3: # Not PT_INTERP. + continue + self._f.seek(data[self._p_idx[1]]) + return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0") + return None diff --git a/venv/lib/python3.12/site-packages/packaging/_manylinux.py b/venv/lib/python3.12/site-packages/packaging/_manylinux.py new file mode 100644 index 0000000..86de1e6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/_manylinux.py @@ -0,0 +1,278 @@ +from __future__ import annotations + +import collections +import contextlib +import functools +import os +import re +import sys +import warnings +from typing import TYPE_CHECKING, NamedTuple + +from ._elffile import EIClass, EIData, ELFFile, EMachine + +if TYPE_CHECKING: + import types + from collections.abc import Generator, Iterator, Sequence + +EF_ARM_ABIMASK = 0xFF000000 +EF_ARM_ABI_VER5 = 0x05000000 +EF_ARM_ABI_FLOAT_HARD = 0x00000400 + +_ALLOWED_ARCHS = { + "x86_64", + "aarch64", + "ppc64", + "ppc64le", + "s390x", + "loongarch64", + "riscv64", +} + + +@contextlib.contextmanager +def _parse_elf(path: str) -> Generator[ELFFile | None, None, None]: + try: + with open(path, "rb") as f: + yield ELFFile(f) + except (OSError, TypeError, ValueError): + yield None + + +def _is_linux_armhf(executable: str) -> bool: + # hard-float ABI can be detected from the ELF header of the running + # process + # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.Arm + and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 + and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD + ) + + +def _is_linux_i686(executable: str) -> bool: + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.I386 + ) + + +def _have_compatible_abi(executable: str, archs: Sequence[str]) -> bool: + if "armv7l" in archs: + return _is_linux_armhf(executable) + if "i686" in archs: + return _is_linux_i686(executable) + return any(arch in _ALLOWED_ARCHS for arch in archs) + + +# If glibc ever changes its major version, we need to know what the last +# minor version was, so we can build the complete list of all versions. +# For now, guess what the highest minor version might be, assume it will +# be 50 for testing. Once this actually happens, update the dictionary +# with the actual value. +_LAST_GLIBC_MINOR: dict[int, int] = collections.defaultdict(lambda: 50) + + +class _GLibCVersion(NamedTuple): + major: int + minor: int + + +def _glibc_version_string_confstr() -> str | None: + """ + Primary implementation of glibc_version_string using os.confstr. + """ + # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely + # to be broken or missing. This strategy is used in the standard library + # platform module. + # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 + try: + # Should be a string like "glibc 2.17". + version_string: str | None = os.confstr("CS_GNU_LIBC_VERSION") + assert version_string is not None + _, version = version_string.rsplit() + except (AssertionError, AttributeError, OSError, ValueError): + # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... + return None + return version + + +def _glibc_version_string_ctypes() -> str | None: + """ + Fallback implementation of glibc_version_string using ctypes. + """ + try: + import ctypes # noqa: PLC0415 + except ImportError: + return None + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + # + # We must also handle the special case where the executable is not a + # dynamically linked executable. This can occur when using musl libc, + # for example. In this situation, dlopen() will error, leading to an + # OSError. Interestingly, at least in the case of musl, there is no + # errno set on the OSError. The single string argument used to construct + # OSError comes from libc itself and is therefore not portable to + # hard code here. In any case, failure to call dlopen() means we + # can proceed, so we bail on our attempt. + try: + process_namespace = ctypes.CDLL(None) + except OSError: + return None + + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5". + gnu_get_libc_version.restype = ctypes.c_char_p + # A c_char_p restype comes back as bytes, so decode to text. + version_str: str | bytes = gnu_get_libc_version() + if isinstance(version_str, bytes): + version_str = version_str.decode("ascii") + + return version_str + + +def _glibc_version_string() -> str | None: + """Returns glibc version string, or None if not using glibc.""" + return _glibc_version_string_confstr() or _glibc_version_string_ctypes() + + +def _parse_glibc_version(version_str: str) -> _GLibCVersion: + """Parse glibc version. + + We use a regexp instead of str.split because we want to discard any + random junk that might come after the minor version -- this might happen + in patched/forked versions of glibc (e.g. Linaro's version of glibc + uses version strings like "2.20-2014.11"). See gh-3588. + """ + m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) + if not m: + warnings.warn( + f"Expected glibc version with 2 components major.minor, got: {version_str}", + RuntimeWarning, + stacklevel=2, + ) + return _GLibCVersion(-1, -1) + return _GLibCVersion(int(m.group("major")), int(m.group("minor"))) + + +@functools.lru_cache +def _get_glibc_version() -> _GLibCVersion: + version_str = _glibc_version_string() + if version_str is None: + return _GLibCVersion(-1, -1) + return _parse_glibc_version(version_str) + + +# From PEP 513, PEP 600 +@functools.lru_cache(maxsize=1) +def _get_manylinux_module() -> types.ModuleType | None: + """Return the ``_manylinux`` C extension module, or None if unavailable. + + The result is cached for the lifetime of the process, since the presence + of the module does not change while running. + """ + try: + return __import__("_manylinux") + except ImportError: + return None + + +def _is_compatible(arch: str, version: _GLibCVersion) -> bool: + sys_glibc = _get_glibc_version() + if sys_glibc < version: + return False + # Check for presence of _manylinux module. + manylinux_mod = _get_manylinux_module() + if manylinux_mod is None: + return True + if hasattr(manylinux_mod, "manylinux_compatible"): + result = manylinux_mod.manylinux_compatible(version[0], version[1], arch) + if result is not None: + return bool(result) + return True + if version == _GLibCVersion(2, 5) and hasattr( + manylinux_mod, "manylinux1_compatible" + ): + return bool(manylinux_mod.manylinux1_compatible) + if version == _GLibCVersion(2, 12) and hasattr( + manylinux_mod, "manylinux2010_compatible" + ): + return bool(manylinux_mod.manylinux2010_compatible) + if version == _GLibCVersion(2, 17) and hasattr( + manylinux_mod, "manylinux2014_compatible" + ): + return bool(manylinux_mod.manylinux2014_compatible) + return True + + +_LEGACY_MANYLINUX_MAP: dict[_GLibCVersion, str] = { + # CentOS 7 w/ glibc 2.17 (PEP 599) + _GLibCVersion(2, 17): "manylinux2014", + # CentOS 6 w/ glibc 2.12 (PEP 571) + _GLibCVersion(2, 12): "manylinux2010", + # CentOS 5 w/ glibc 2.5 (PEP 513) + _GLibCVersion(2, 5): "manylinux1", +} + + +def platform_tags(archs: Sequence[str]) -> Iterator[str]: + """Generate manylinux tags compatible to the current platform. + + :param archs: Sequence of compatible architectures. + The first one shall be the closest to the actual architecture and be the part of + platform tag after the ``linux_`` prefix, e.g. ``x86_64``. + The ``linux_`` prefix is assumed as a prerequisite for the current platform to + be manylinux-compatible. + + :returns: An iterator of compatible manylinux tags. + """ + if not _have_compatible_abi(sys.executable, archs): + return + # Oldest glibc to be supported regardless of architecture is (2, 17). + too_old_glibc2 = _GLibCVersion(2, 16) + if set(archs) & {"x86_64", "i686"}: + # On x86/i686 also oldest glibc to be supported is (2, 5). + too_old_glibc2 = _GLibCVersion(2, 4) + current_glibc = _GLibCVersion(*_get_glibc_version()) + glibc_max_list = [current_glibc] + # We can assume compatibility across glibc major versions. + # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 + # + # Build a list of maximum glibc versions so that we can + # output the canonical list of all glibc from current_glibc + # down to too_old_glibc2, including all intermediary versions. + for glibc_major in range(current_glibc.major - 1, 1, -1): + glibc_minor = _LAST_GLIBC_MINOR[glibc_major] + glibc_max_list.append(_GLibCVersion(glibc_major, glibc_minor)) + for arch in archs: + for glibc_max in glibc_max_list: + if glibc_max.major == too_old_glibc2.major: + min_minor = too_old_glibc2.minor + else: + # For other glibc major versions oldest supported is (x, 0). + min_minor = -1 + for glibc_minor in range(glibc_max.minor, min_minor, -1): + glibc_version = _GLibCVersion(glibc_max.major, glibc_minor) + if _is_compatible(arch, glibc_version): + yield "manylinux_{}_{}_{}".format(*glibc_version, arch) + + # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. + if legacy_tag := _LEGACY_MANYLINUX_MAP.get(glibc_version): + yield f"{legacy_tag}_{arch}" diff --git a/venv/lib/python3.12/site-packages/packaging/_musllinux.py b/venv/lib/python3.12/site-packages/packaging/_musllinux.py new file mode 100644 index 0000000..74c4136 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/_musllinux.py @@ -0,0 +1,88 @@ +"""PEP 656 support. + +This module implements logic to detect if the currently running Python is +linked against musl, and what musl version is used. +""" + +from __future__ import annotations + +import functools +import re +import subprocess +import sys +from typing import TYPE_CHECKING, NamedTuple + +from ._elffile import ELFFile + +if TYPE_CHECKING: + from collections.abc import Iterator, Sequence + + +class _MuslVersion(NamedTuple): + major: int + minor: int + + +def _parse_musl_version(output: str) -> _MuslVersion | None: + lines = [n for n in (n.strip() for n in output.splitlines()) if n] + if len(lines) < 2 or lines[0][:4] != "musl": + return None + m = re.match(r"Version (\d+)\.(\d+)", lines[1]) + if not m: + return None + return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2))) + + +@functools.lru_cache +def _get_musl_version(executable: str) -> _MuslVersion | None: + """Detect currently-running musl runtime version. + + This is done by checking the specified executable's dynamic linking + information, and invoking the loader to parse its output for a version + string. If the loader is musl, the output would be something like:: + + musl libc (x86_64) + Version 1.2.2 + Dynamic Program Loader + """ + try: + with open(executable, "rb") as f: + ld = ELFFile(f).interpreter + except (OSError, TypeError, ValueError): + return None + if ld is None or "musl" not in ld: + return None + proc = subprocess.run([ld], check=False, stderr=subprocess.PIPE, text=True) + return _parse_musl_version(proc.stderr) + + +def platform_tags(archs: Sequence[str]) -> Iterator[str]: + """Generate musllinux tags compatible to the current platform. + + :param archs: Sequence of compatible architectures. + The first one shall be the closest to the actual architecture and be the part of + platform tag after the ``linux_`` prefix, e.g. ``x86_64``. + The ``linux_`` prefix is assumed as a prerequisite for the current platform to + be musllinux-compatible. + + :returns: An iterator of compatible musllinux tags. + """ + sys_musl = _get_musl_version(sys.executable) + if sys_musl is None: # Python not dynamically linked against musl. + return + for arch in archs: + for minor in range(sys_musl.minor, -1, -1): + yield f"musllinux_{sys_musl.major}_{minor}_{arch}" + + +if __name__ == "__main__": # pragma: no cover + import sysconfig + + plat = sysconfig.get_platform() + assert plat.startswith("linux-"), "not linux" + + print("plat:", plat) + print("musl:", _get_musl_version(sys.executable)) + print("tags:", end=" ") + for t in platform_tags([re.sub(r"[.-]", "_", plat.split("-", 1)[-1])]): + print(t, end="\n ") diff --git a/venv/lib/python3.12/site-packages/packaging/_parser.py b/venv/lib/python3.12/site-packages/packaging/_parser.py new file mode 100644 index 0000000..9331b4b --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/_parser.py @@ -0,0 +1,418 @@ +"""Handwritten parser of dependency specifiers. + +The docstring for each __parse_* function contains EBNF-inspired grammar representing +the implementation. +""" + +from __future__ import annotations + +import ast +from collections.abc import Sequence +from typing import Literal, NamedTuple, Union + +from ._tokenizer import DEFAULT_RULES, ParserSyntaxError, Tokenizer + + +class Node: + __slots__ = ("value",) + + def __init__(self, value: str) -> None: + self.value = value + + def __str__(self) -> str: + return self.value + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}({self.value!r})>" + + def serialize(self) -> str: + raise NotImplementedError + + def __getstate__(self) -> str: + # Return just the value string for compactness and stability. + return self.value + + def _restore_value(self, value: object) -> None: + if not isinstance(value, str): + raise TypeError( + f"Cannot restore {self.__class__.__name__} value from {value!r}" + ) + self.value = value + + def __setstate__(self, state: object) -> None: + if isinstance(state, str): + # New format (26.2+): just the value string. + self._restore_value(state) + return + if isinstance(state, tuple) and len(state) == 2: + # Old format (packaging <= 26.0, __slots__): (None, {slot: value}). + _, slot_dict = state + if isinstance(slot_dict, dict) and "value" in slot_dict: + self._restore_value(slot_dict["value"]) + return + if isinstance(state, dict) and "value" in state: + # Old format (packaging <= 25.0, no __slots__): plain __dict__. + self._restore_value(state["value"]) + return + raise TypeError(f"Cannot restore {self.__class__.__name__} from {state!r}") + + +class Variable(Node): + __slots__ = () + + def serialize(self) -> str: + return str(self) + + +class Value(Node): + __slots__ = () + + def serialize(self) -> str: + value = str(self) + if '"' not in value: + return f'"{value}"' + if "'" not in value: + return f"'{value}'" + raise ValueError( + "Cannot serialize marker value containing both quote characters" + ) + + +class Op(Node): + __slots__ = () + + def serialize(self) -> str: + return str(self) + + +MarkerLogical = Literal["and", "or"] +MarkerVar = Union[Variable, Value] +MarkerItem = tuple[MarkerVar, Op, MarkerVar] +MarkerAtom = Union[MarkerItem, Sequence["MarkerAtom"]] +MarkerList = list[Union["MarkerList", MarkerAtom, MarkerLogical]] + + +class ParsedRequirement(NamedTuple): + name: str + url: str + extras: list[str] + specifier: str + marker: MarkerList | None + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for dependency specifier +# -------------------------------------------------------------------------------------- +def parse_requirement(source: str) -> ParsedRequirement: + return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement: + """ + requirement = WS? IDENTIFIER WS? extras WS? requirement_details + """ + tokenizer.consume("WS") + + name_token = tokenizer.expect( + "IDENTIFIER", expected="package name at the start of dependency specifier" + ) + name = name_token.text + tokenizer.consume("WS") + + extras = _parse_extras(tokenizer) + tokenizer.consume("WS") + + url, specifier, marker = _parse_requirement_details(tokenizer) + tokenizer.expect("END", expected="end of dependency specifier") + + return ParsedRequirement(name, url, extras, specifier, marker) + + +def _parse_requirement_details( + tokenizer: Tokenizer, +) -> tuple[str, str, MarkerList | None]: + """ + requirement_details = AT URL (WS requirement_marker?)? + | specifier WS? (requirement_marker)? + """ + + specifier = "" + url = "" + marker = None + + if tokenizer.check("AT"): + tokenizer.read() + tokenizer.consume("WS") + + url_start = tokenizer.position + url = tokenizer.expect("URL", expected="URL after @").text + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + tokenizer.expect("WS", expected="whitespace after URL") + + # The input might end after whitespace. + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, + span_start=url_start, + expected="semicolon (after URL and whitespace)", + ) + else: + specifier_start = tokenizer.position + specifier = _parse_specifier(tokenizer) + tokenizer.consume("WS") + + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, + span_start=specifier_start, + expected=( + "comma (within version specifier), semicolon (after version specifier)" + if specifier + else "semicolon (after name with no version specifier)" + ), + ) + + return (url, specifier, marker) + + +def _parse_requirement_marker( + tokenizer: Tokenizer, *, span_start: int, expected: str +) -> MarkerList: + """ + requirement_marker = SEMICOLON marker WS? + """ + + if not tokenizer.check("SEMICOLON"): + tokenizer.raise_syntax_error( + f"Expected {expected} or end", + span_start=span_start, + span_end=None, + ) + tokenizer.read() + + marker = _parse_marker(tokenizer) + tokenizer.consume("WS") + + return marker + + +def _parse_extras(tokenizer: Tokenizer) -> list[str]: + """ + extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)? + """ + if not tokenizer.check("LEFT_BRACKET", peek=True): + return [] + + with tokenizer.enclosing_tokens( + "LEFT_BRACKET", + "RIGHT_BRACKET", + around="extras", + ): + tokenizer.consume("WS") + extras = _parse_extras_list(tokenizer) + tokenizer.consume("WS") + + return extras + + +def _parse_extras_list(tokenizer: Tokenizer) -> list[str]: + """ + extras_list = identifier (wsp* ',' wsp* identifier)* + """ + extras: list[str] = [] + + if not tokenizer.check("IDENTIFIER"): + return extras + + extras.append(tokenizer.read().text) + + while True: + tokenizer.consume("WS") + if tokenizer.check("IDENTIFIER", peek=True): + tokenizer.raise_syntax_error("Expected comma between extra names") + elif not tokenizer.check("COMMA"): + break + + tokenizer.read() + tokenizer.consume("WS") + + extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma") + extras.append(extra_token.text) + + return extras + + +def _parse_specifier(tokenizer: Tokenizer) -> str: + """ + specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS + | WS? version_many WS? + """ + with tokenizer.enclosing_tokens( + "LEFT_PARENTHESIS", + "RIGHT_PARENTHESIS", + around="version specifier", + ): + tokenizer.consume("WS") + parsed_specifiers = _parse_version_many(tokenizer) + tokenizer.consume("WS") + + return parsed_specifiers + + +def _parse_version_many(tokenizer: Tokenizer) -> str: + """ + version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)? + """ + parsed_specifiers = "" + while tokenizer.check("SPECIFIER"): + span_start = tokenizer.position + specifier = tokenizer.read().text + parsed_specifiers += specifier + if tokenizer.check("VERSION_PREFIX_TRAIL", peek=True): + message = ".* suffix can only be used with `==` or `!=` operators" + if specifier.startswith("!=") or ( + specifier.startswith("==") and not specifier.startswith("===") + ): + message = ( + ".* suffix cannot be used with pre-release, post-release, " + "dev or local versions" + ) + tokenizer.raise_syntax_error( + message, + span_start=span_start, + span_end=tokenizer.position + 1, + ) + if tokenizer.check("VERSION_LOCAL_LABEL_TRAIL", peek=True): + tokenizer.raise_syntax_error( + "Local version label can only be used with `==` or `!=` operators", + span_start=span_start, + span_end=tokenizer.position, + ) + tokenizer.consume("WS") + if not tokenizer.check("COMMA"): + break + parsed_specifiers += tokenizer.read().text + tokenizer.consume("WS") + + return parsed_specifiers + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for marker expression +# -------------------------------------------------------------------------------------- +def parse_marker(source: str) -> MarkerList: + return _parse_full_marker(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_full_marker(tokenizer: Tokenizer) -> MarkerList: + retval = _parse_marker(tokenizer) + tokenizer.expect("END", expected="end of marker expression") + return retval + + +def _parse_marker(tokenizer: Tokenizer) -> MarkerList: + """ + marker = marker_atom (BOOLOP marker_atom)+ + """ + expression = [_parse_marker_atom(tokenizer)] + while tokenizer.check("BOOLOP"): + token = tokenizer.read() + expr_right = _parse_marker_atom(tokenizer) + expression.extend((token.text, expr_right)) + return expression + + +def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom: + """ + marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS? + | WS? marker_item WS? + """ + + tokenizer.consume("WS") + if tokenizer.check("LEFT_PARENTHESIS", peek=True): + with tokenizer.enclosing_tokens( + "LEFT_PARENTHESIS", + "RIGHT_PARENTHESIS", + around="marker expression", + ): + tokenizer.consume("WS") + marker: MarkerAtom = _parse_marker(tokenizer) + tokenizer.consume("WS") + else: + marker = _parse_marker_item(tokenizer) + tokenizer.consume("WS") + return marker + + +def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem: + """ + marker_item = WS? marker_var WS? marker_op WS? marker_var WS? + """ + tokenizer.consume("WS") + marker_var_left = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + marker_op = _parse_marker_op(tokenizer) + tokenizer.consume("WS") + marker_var_right = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + return (marker_var_left, marker_op, marker_var_right) + + +def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: # noqa: RET503 + """ + marker_var = VARIABLE | QUOTED_STRING + """ + if tokenizer.check("VARIABLE"): + return process_env_var(tokenizer.read().text.replace(".", "_")) + elif tokenizer.check("QUOTED_STRING"): + token = tokenizer.read() + try: + return process_python_str(token.text) + except (SyntaxError, ValueError) as exc: + raise ParserSyntaxError( + "Invalid quoted string", + source=tokenizer.source, + span=(token.position, token.position + len(token.text)), + ) from exc + else: + tokenizer.raise_syntax_error( + message="Expected a marker variable or quoted string" + ) + + +def process_env_var(env_var: str) -> Variable: + if env_var in ("platform_python_implementation", "python_implementation"): + return Variable("platform_python_implementation") + else: + return Variable(env_var) + + +def process_python_str(python_str: str) -> Value: + value = ast.literal_eval(python_str) + return Value(str(value)) + + +def _parse_marker_op(tokenizer: Tokenizer) -> Op: + """ + marker_op = IN | NOT IN | OP + """ + if tokenizer.check("IN"): + tokenizer.read() + return Op("in") + elif tokenizer.check("NOT"): + tokenizer.read() + tokenizer.expect("WS", expected="whitespace after 'not'") + tokenizer.expect("IN", expected="'in' after 'not'") + return Op("not in") + elif tokenizer.check("OP"): + return Op(tokenizer.read().text) + else: + return tokenizer.raise_syntax_error( + "Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in" + ) diff --git a/venv/lib/python3.12/site-packages/packaging/_ranges.py b/venv/lib/python3.12/site-packages/packaging/_ranges.py new file mode 100644 index 0000000..5370ec8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/_ranges.py @@ -0,0 +1,836 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +"""Private version-range helpers used by :mod:`packaging.specifiers`.""" + +from __future__ import annotations + +import enum +import functools +from typing import ( + TYPE_CHECKING, + Any, + Final, +) + +from .version import InvalidVersion, Version + +if TYPE_CHECKING: + from collections.abc import Callable, Iterable, Iterator, Sequence + from typing import Union + + # Total-order key for comparing two boundaries (boundary-vs-boundary only). + # The post slot may be ``_BOUNDARY_INF`` for an AFTER_POSTS boundary. + _BoundaryOrderSuffix = tuple[int, int, int, Union[int, float], int, int] + _BoundaryOrderKey = tuple[int, tuple[int, ...], _BoundaryOrderSuffix, float] + +__all__ = [ + "FULL_RANGE", + "bounds_for_spec", + "coerce_version", + "filter_by_ranges", + "intersect_ranges", + "intersect_specifier_bounds", + "least_version_above", + "matches_bounds_only", + "range_is_empty", + "ranges_are_prerelease_only", + "resolve_prereleases", + "standard_ranges", + "wildcard_ranges", +] + +#: The smallest possible PEP 440 version. No valid version is less than this. +MIN_VERSION: Final[Version] = Version("0.dev0") + +#: The smallest non-pre-release version, i.e. the nearest non-pre-release at or +#: above the ``-inf`` floor. +MIN_RELEASE: Final[Version] = Version("0") + +#: Sorts above any real post number and any local label, so a boundary can be +#: ordered above the version family it covers when two boundaries are compared. +_BOUNDARY_INF: Final[float] = float("inf") + + +class BoundaryKind(enum.Enum): + """Where a boundary marker sits in the version ordering.""" + + AFTER_LOCALS = enum.auto() # after V+local, before V.post0 + AFTER_POSTS = enum.auto() # after V.postN, before next release + + +@functools.total_ordering +class BoundaryVersion: + """A point on the version line between two real PEP 440 versions. + + Relative to a base version V:: + + V < V+local < AFTER_LOCALS(V) < V.post0 < AFTER_POSTS(V) + + AFTER_LOCALS is the upper bound of ``<=V``, ``==V``, ``!=V`` (no + local), and the lower bound of the upper-side range of ``!=V``. + AFTER_POSTS is the lower bound of ``>V`` (V final or pre-release), + excluding V's post-releases per PEP 440. + """ + + __slots__ = ( + "_cached_dev", + "_cached_epoch", + "_cached_post", + "_cached_pre", + "_cached_trimmed_release", + "kind", + "version", + ) + + def __init__(self, version: Version, kind: BoundaryKind) -> None: + self.version = version + self.kind = kind + self._cached_trimmed_release = trim_release(version.release) + self._cached_epoch = version.epoch + self._cached_pre = version.pre + self._cached_post = version.post + self._cached_dev = version.dev + + def _is_family(self, other: Version) -> bool: + """Is ``other`` a version that this boundary sorts above?""" + if other.epoch != self._cached_epoch: + return False + # Inline release-trim comparison: other.release matches the + # trimmed release iff its leading slice is equal and any extra + # components are zero. Avoids trim_release's tuple allocation. + other_release = other.release + trimmed_release = self._cached_trimmed_release + trimmed_length = len(trimmed_release) + if len(other_release) < trimmed_length: + return False + if other_release[:trimmed_length] != trimmed_release: + return False + for i in range(trimmed_length, len(other_release)): + if other_release[i] != 0: + return False + if other.pre != self._cached_pre: + return False + if self.kind == BoundaryKind.AFTER_LOCALS: + # Local family: same public version, any local label. + return other.post == self._cached_post and other.dev == self._cached_dev + # Post family: V itself + any post-release of V. + return other.dev == self._cached_dev or other.post is not None + + def _order_key(self) -> _BoundaryOrderKey: + """Sort key placing this boundary just above the versions it covers. + + It extends ``V``'s comparison key ``(epoch, release, suffix)`` with + a trailing ``_BOUNDARY_INF`` local component, so the key sorts after + ``V`` and every ``V+local`` (whose keys carry a real, finite local + segment). ``suffix`` is the 6-int comparison suffix + ``(pre_rank, pre_n, post_rank, post_n, dev_rank, dev_n)``. + + For an AFTER_POSTS boundary the suffix is replaced with one whose + post number is ``_BOUNDARY_INF``, so the key also sorts after every + ``V.postN``. An AFTER_LOCALS boundary uses ``V``'s suffix unchanged. + """ + version_key = self.version._key + suffix: _BoundaryOrderSuffix = version_key[2] + + if self.kind == BoundaryKind.AFTER_POSTS: + suffix = (suffix[0], suffix[1], 1, _BOUNDARY_INF, 1, 0) + + return version_key[0], version_key[1], suffix, _BOUNDARY_INF + + def __eq__(self, other: object) -> bool: + # Key off the order key so equality matches the ``<`` / ``>`` order: + # ``AFTER_POSTS(1.0)`` and ``AFTER_POSTS(1.0.post1)`` are the same point. + if isinstance(other, BoundaryVersion): + return self._order_key() == other._order_key() + return NotImplemented + + def __lt__(self, other: BoundaryVersion | Version) -> bool: + if isinstance(other, BoundaryVersion): + return self._order_key() < other._order_key() + # boundary < other_version iff V < other AND other not in family. + # The cheap V >= other path short-circuits before the family check. + if not (self.version < other): + return False + return not self._is_family(other) + + def __gt__(self, other: BoundaryVersion | Version) -> bool: + # Defined directly to bypass functools.total_ordering's + # NotImplemented round-trip on reflected ``Version < boundary``. + if isinstance(other, BoundaryVersion): + return self._order_key() > other._order_key() + if self.version >= other: + return True + return self._is_family(other) + + def __hash__(self) -> int: + # Keyed to ``__eq__`` (the order key), so equal boundaries hash equal. + return hash(self._order_key()) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.version!r}, {self.kind.name})" + + +if TYPE_CHECKING: + _VersionOrBoundary = Union[Version, BoundaryVersion, None] + + +@functools.total_ordering +class LowerBound: + """Lower bound of a version range. + + A version *v* of ``None`` means unbounded below (-inf). + At equal versions, ``[v`` sorts before ``(v`` because an inclusive + bound starts earlier. + """ + + __slots__ = ("_above", "inclusive", "version") + + def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None: + self.version = version + self.inclusive = inclusive + # Pre-bind a predicate "is parsed at or above this lower + # bound?" for the hot filter / contains loops. One direct + # call per check, no operator-dispatch chain. + if version is None: + self._above: Callable[[Version], bool] | None = None + elif isinstance(version, BoundaryVersion): + # >V produces an AFTER_POSTS lower bound; the upper-side + # range of !=V produces an AFTER_LOCALS lower bound. + if version.kind == BoundaryKind.AFTER_POSTS: + self._above = _make_above_after_posts(version.version) + else: + self._above = _make_above_after_locals(version.version) + elif inclusive: + self._above = version.__le__ + else: + self._above = version.__lt__ + + def __eq__(self, other: object) -> bool: + if not isinstance(other, LowerBound): + return NotImplemented + return self.version == other.version and self.inclusive == other.inclusive + + def __lt__(self, other: LowerBound) -> bool: + if not isinstance(other, LowerBound): + return NotImplemented + # -inf < anything (except -inf itself). + if self.version is None: + return other.version is not None + if other.version is None: + return False + if self.version != other.version: + return self.version < other.version + # [v < (v: inclusive starts earlier. + return self.inclusive and not other.inclusive + + def __hash__(self) -> int: + return hash((self.version, self.inclusive)) + + def __repr__(self) -> str: + bracket = "[" if self.inclusive else "(" + return f"<{self.__class__.__name__} {bracket}{self.version!r}>" + + +@functools.total_ordering +class UpperBound: + """Upper bound of a version range. + + A version *v* of ``None`` means unbounded above (+inf). + At equal versions, ``v)`` sorts before ``v]`` because an exclusive + bound ends earlier. + """ + + __slots__ = ("_below", "inclusive", "version") + + def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None: + self.version = version + self.inclusive = inclusive + # Pre-bind a predicate "is parsed at or below this upper + # bound?". See LowerBound for the rationale. + if version is None: + self._below: Callable[[Version], bool] | None = None + elif isinstance(version, BoundaryVersion): + # Standard specifiers only ever produce AFTER_LOCALS upper + # bounds (from <=V / ==V / !=V with no local). + if version.kind == BoundaryKind.AFTER_LOCALS: + self._below = _make_below_after_locals(version.version) + else: + # An AFTER_POSTS upper is not produced by any specifier, but + # range algebra reaches it: complementing ``>V`` flips the + # ``AFTER_POSTS(V)`` lower into this upper bound. + self._below = version.__ge__ + elif inclusive: + self._below = version.__ge__ + else: + self._below = version.__gt__ + + def __eq__(self, other: object) -> bool: + if not isinstance(other, UpperBound): + return NotImplemented + return self.version == other.version and self.inclusive == other.inclusive + + def __lt__(self, other: UpperBound) -> bool: + if not isinstance(other, UpperBound): + return NotImplemented + # Nothing < +inf (except +inf itself). + if self.version is None: + return False + if other.version is None: + return True + if self.version != other.version: + return self.version < other.version + # v) < v]: exclusive ends earlier. + return not self.inclusive and other.inclusive + + def __hash__(self) -> int: + return hash((self.version, self.inclusive)) + + def __repr__(self) -> str: + bracket = "]" if self.inclusive else ")" + return f"<{self.__class__.__name__} {self.version!r}{bracket}>" + + +if TYPE_CHECKING: + #: A single contiguous interval as a (lower, upper) bound pair. + Interval = tuple[LowerBound, UpperBound] + + +NEG_INF: Final[LowerBound] = LowerBound(None, False) +POS_INF: Final[UpperBound] = UpperBound(None, False) +FULL_RANGE: Final[tuple[Interval]] = ((NEG_INF, POS_INF),) + + +def trim_release(release: tuple[int, ...]) -> tuple[int, ...]: + """Strip trailing zeros from a release tuple for normalized comparison.""" + end = len(release) + while end > 1 and release[end - 1] == 0: + end -= 1 + return release if end == len(release) else release[:end] + + +def _next_prefix_dev0(version: Version) -> Version: + """Smallest version in the next prefix: 1.2 -> 1.3.dev0.""" + release = (*version.release[:-1], version.release[-1] + 1) + return Version.from_parts(epoch=version.epoch, release=release, dev=0) + + +def _base_dev0(version: Version) -> Version: + """The .dev0 of a version's base release: 1.2 -> 1.2.dev0.""" + return Version.from_parts(epoch=version.epoch, release=version.release, dev=0) + + +def coerce_version(version: Version | str) -> Version | None: + if not isinstance(version, Version): + try: + version = Version(version) + except InvalidVersion: + return None + return version + + +def _make_above_after_posts(version: Version) -> Callable[[Version], bool]: + """Predicate ``parsed > AFTER_POSTS(V)`` for a lower bound. + + Per PEP 440, ``>V`` excludes V's post-releases unless V is itself + a post-release. AFTER_POSTS sits above V and every V.postN (with + or without local), and just below the next release. + """ + version_ge = version.__ge__ + version_epoch = version.epoch + version_pre = version.pre + version_release_trimmed = trim_release(version.release) + trimmed_length = len(version_release_trimmed) + + def above(parsed: Version) -> bool: + if version_ge(parsed): + return False + # parsed > V cmpkey-wise: above the boundary iff NOT in V's + # post family. + if parsed.epoch != version_epoch: + return True + parsed_release = parsed.release + if len(parsed_release) < trimmed_length: + return True + if parsed_release[:trimmed_length] != version_release_trimmed: + return True + for i in range(trimmed_length, len(parsed_release)): + if parsed_release[i] != 0: + return True + if parsed.pre != version_pre: + return True + + # Same release and pre as V: parsed is in V's post family (V itself, + # V+local, or V.postN), which the boundary sits above. A V.devN + # (different dev, no post) sorts before V and was already caught by + # ``version_ge`` above, so the answer here is always "not above". + return False + + return above + + +def _make_above_after_locals(version: Version) -> Callable[[Version], bool]: + """Predicate ``parsed > AFTER_LOCALS(V)`` for a lower bound. + + Used by the upper-side range of ``!=V`` (when V has no local + segment). AFTER_LOCALS sits above V and every ``V+local`` but + just below ``V.post0``. + """ + version_ge = version.__ge__ + version_epoch = version.epoch + version_pre = version.pre + version_post = version.post + version_dev = version.dev + version_release_trimmed = trim_release(version.release) + trimmed_length = len(version_release_trimmed) + + def above(parsed: Version) -> bool: + if version_ge(parsed): + return False + # parsed > V cmpkey-wise: above the boundary iff NOT in V's + # local family (same public version, any local segment). + if parsed.epoch != version_epoch: + return True + parsed_release = parsed.release + if len(parsed_release) < trimmed_length: + return True + if parsed_release[:trimmed_length] != version_release_trimmed: + return True + for i in range(trimmed_length, len(parsed_release)): + if parsed_release[i] != 0: + return True + if parsed.pre != version_pre: + return True + if parsed.post != version_post: + return True + return parsed.dev != version_dev + + return above + + +def _make_below_after_locals(version: Version) -> Callable[[Version], bool]: + """Predicate ``parsed <= AFTER_LOCALS(V)`` for an upper bound. + + Used by ``<=V``, ``==V``, ``!=V`` (no local). ``parsed`` is at or + below the boundary when it is at or below V cmpkey-wise, or when + it is in V's local family. + """ + version_ge = version.__ge__ + version_epoch = version.epoch + version_pre = version.pre + version_post = version.post + version_dev = version.dev + version_release_trimmed = trim_release(version.release) + trimmed_length = len(version_release_trimmed) + + def below(parsed: Version) -> bool: + if version_ge(parsed): + return True + # parsed > V cmpkey-wise: below the boundary iff in V's local + # family. + if parsed.epoch != version_epoch: + return False + parsed_release = parsed.release + if len(parsed_release) < trimmed_length: + return False + if parsed_release[:trimmed_length] != version_release_trimmed: + return False + for i in range(trimmed_length, len(parsed_release)): + if parsed_release[i] != 0: + return False + if parsed.pre != version_pre: + return False + if parsed.post != version_post: + return False + return parsed.dev == version_dev + + return below + + +def least_version_above(boundary: BoundaryVersion) -> Version | None: + """Smallest real version strictly above *boundary*, or ``None`` if none exists.""" + base = boundary.version + + if boundary.kind == BoundaryKind.AFTER_LOCALS: + # AFTER_LOCALS(V) sits just below V.post0, so its least successor is + # V.post0.dev0 (V.dev(N+1) if V has a dev, V.post(N+1).dev0 if a post). + if base.dev is not None: + return base.__replace__(dev=base.dev + 1, local=None) + next_post = (base.post + 1) if base.post is not None else 0 + return base.__replace__(post=next_post, dev=0, local=None) + + # AFTER_POSTS(V): a pre-release V steps to the next pre-release's .dev0; + # a final-release AFTER_POSTS has no least successor. + if base.pre is not None: + kind, number = base.pre + return base.__replace__(pre=(kind, number + 1), post=None, dev=0, local=None) + + return None + + +def range_is_empty(lower: LowerBound, upper: UpperBound) -> bool: + """True when the range defined by *lower* and *upper* contains no versions. + + A boundary lower sits just below the next real version, so an ordered pair + is still empty when the upper excludes that least successor: + ``(AFTER_POSTS(1.0a1), 1.0a2.dev0)`` holds no version. + """ + if upper.version is None: + return False + + if lower.version is None: + # Nothing sorts below MIN_VERSION, so an exclusive upper at or below it + # leaves an empty floor interval such as ``(-inf, 0.dev0)``. + return ( + not upper.inclusive + and isinstance(upper.version, Version) + and upper.version <= MIN_VERSION + ) + + if isinstance(lower.version, BoundaryVersion): + successor = least_version_above(lower.version) + if successor is not None: + if upper.version == successor: + return not upper.inclusive + return upper.version < successor + + if lower.version == upper.version: + return not (lower.inclusive and upper.inclusive) + + return lower.version > upper.version + + +def intersect_ranges( + left: Sequence[Interval], + right: Sequence[Interval], +) -> list[Interval]: + """Intersect two sorted, non-overlapping range lists (two-pointer merge).""" + result: list[Interval] = [] + left_index = right_index = 0 + while left_index < len(left) and right_index < len(right): + left_lower, left_upper = left[left_index] + right_lower, right_upper = right[right_index] + + lower = max(left_lower, right_lower) + upper = min(left_upper, right_upper) + + if not range_is_empty(lower, upper): + result.append((lower, upper)) + + # Advance whichever side has the smaller upper bound. + if left_upper < right_upper: + left_index += 1 + else: + right_index += 1 + + return result + + +def filter_by_ranges( + ranges: Sequence[Interval], + iterable: Iterable[Any], + key: Callable[[Any], Version | str] | None, + prereleases: bool | None, + region: Sequence[Interval] = (), +) -> Iterator[Any]: + """Filter *iterable* against precomputed version *ranges*. + + With ``prereleases=None``, the PEP 440 default applies: pre-releases are + excluded unless no final matches, in which case buffered pre-releases come + out at the end. A pre-release inside the opt-in ``region`` is the exception: + it is force-admitted in place, as ``prereleases=True`` would yield it. A + force-admitted pre-release is not a final, so it never suppresses the buffer. + """ + if prereleases is None: + prerelease_buffer: list[Any] = [] + found_final = False + + if len(ranges) == 1: + # Hot path: most specifiers and small SpecifierSets reduce to + # a single contiguous range. + lower, upper = ranges[0] + above = lower._above + below = upper._below + for item in iterable: + parsed = coerce_version(item if key is None else key(item)) + if parsed is None: + continue + if above is not None and not above(parsed): + continue + if below is not None and not below(parsed): + continue + if not parsed.is_prerelease: + found_final = True + yield item + elif region and matches_bounds_only(region, parsed): + yield item + elif not found_final: + prerelease_buffer.append(item) + if not found_final: + yield from prerelease_buffer + return + + for item in iterable: + parsed = coerce_version(item if key is None else key(item)) + if parsed is None: + continue + for lower, upper in ranges: + above = lower._above + if above is not None and not above(parsed): + break + below = upper._below + if below is None or below(parsed): + if not parsed.is_prerelease: + found_final = True + yield item + elif region and matches_bounds_only(region, parsed): + yield item + elif not found_final: + prerelease_buffer.append(item) + break + if not found_final: + yield from prerelease_buffer + return + + exclude_prereleases = prereleases is False + + if len(ranges) == 1: + # Hot path: most specifiers and small SpecifierSets reduce to + # a single contiguous range. + lower, upper = ranges[0] + above = lower._above + below = upper._below + for item in iterable: + parsed = coerce_version(item if key is None else key(item)) + if parsed is None: + continue + if exclude_prereleases and parsed.is_prerelease: + continue + if above is not None and not above(parsed): + continue + if below is None or below(parsed): + yield item + return + + for item in iterable: + parsed = coerce_version(item if key is None else key(item)) + if parsed is None: + continue + if exclude_prereleases and parsed.is_prerelease: + continue + for lower, upper in ranges: + above = lower._above + if above is not None and not above(parsed): + break + below = upper._below + if below is None or below(parsed): + yield item + break + + +def _nearest_release_above_prerelease(version: Version) -> Version: + """Smallest non-pre-release at or above a pre-release *version*.""" + if version.pre is not None: + # An a/b/rc pre-release drops to its final release, which outranks + # every post-release of that pre-release (1.0a1.post0 -> 1.0). + return version.__replace__(pre=None, post=None, dev=None, local=None) + + # A dev-only release keeps its post-release (1.0.post0.dev0 -> 1.0.post0, + # whose final 1.0 sorts below it). + return version.__replace__(dev=None, local=None) + + +def _lowest_release_at_or_above(value: Version | BoundaryVersion | None) -> Version: + """Smallest non-pre-release version at or above *value*. + + ``None`` is the ``-inf`` floor, whose nearest non-pre-release is + :data:`MIN_RELEASE`. + """ + if value is None: + return MIN_RELEASE + if isinstance(value, BoundaryVersion): + inner_version = value.version + if inner_version.is_prerelease: + return _nearest_release_above_prerelease(inner_version) + # AFTER_LOCALS(1.0) -> nearest non-pre is 1.0.post0 + # AFTER_LOCALS(1.0.post0) -> nearest non-pre is 1.0.post1 + next_post = (inner_version.post + 1) if inner_version.post is not None else 0 + return inner_version.__replace__(post=next_post, local=None) + + if not value.is_prerelease: + return value + + return _nearest_release_above_prerelease(value) + + +def ranges_are_prerelease_only(ranges: Sequence[Interval]) -> bool: + """True when every range in *ranges* contains only pre-releases. + + Used to detect unsatisfiable specifier sets when ``prereleases=False``: + if every range is pre-release-only, every contained version is excluded. + """ + for lower, upper in ranges: + nearest = _lowest_release_at_or_above(lower.version) + if upper.version is None or nearest < upper.version: + return False + if nearest == upper.version and upper.inclusive: + return False + return True + + +def wildcard_ranges(op: str, base: Version) -> list[Interval]: + """Ranges for ==V.* and !=V.*. + + ==1.2.* -> [1.2.dev0, 1.3.dev0); !=1.2.* -> complement. + """ + lower = _base_dev0(base) + upper = _next_prefix_dev0(base) + if op == "==": + return [(LowerBound(lower, True), UpperBound(upper, False))] + # != + return [ + (NEG_INF, UpperBound(lower, False)), + (LowerBound(upper, True), POS_INF), + ] + + +def standard_ranges(op: str, version: Version, has_local: bool) -> list[Interval]: + """Ranges for the standard PEP 440 operators (no wildcard, no ===). + + *has_local* indicates whether the spec string included a ``+local`` + segment; relevant only for ``==`` / ``!=`` to decide whether the + upper bound includes V's local family. + """ + if op == ">=": + return [(LowerBound(version, True), POS_INF)] + + if op == "<=": + return [ + ( + NEG_INF, + UpperBound(BoundaryVersion(version, BoundaryKind.AFTER_LOCALS), True), + ) + ] + + if op == ">": + if version.dev is not None: + # >V.devN: dev versions have no post-releases, so the + # next real version is V.dev(N+1). + lower_bound = version.__replace__(dev=version.dev + 1, local=None) + return [(LowerBound(lower_bound, True), POS_INF)] + if version.post is not None: + # >V.postN: next real version is V.post(N+1).dev0. + lower_bound = version.__replace__(post=version.post + 1, dev=0, local=None) + return [(LowerBound(lower_bound, True), POS_INF)] + # >V (final or pre-release V): exclude V itself, V+local, and + # every V.postN per PEP 440. + return [ + ( + LowerBound(BoundaryVersion(version, BoundaryKind.AFTER_POSTS), False), + POS_INF, + ) + ] + + if op == "<": + # list[Interval]: + """Ranges for one specifier's ``(op, version_str)``. + + Dispatches between the wildcard and standard builders. ``version`` is the + parsed ``version_str`` (its base, without the trailing ``.*``, for + wildcards). ``===`` is not handled here; its match is a literal string + compared in :mod:`packaging.specifiers`. + """ + if version_str.endswith(".*"): + return wildcard_ranges(op, version) + + return standard_ranges(op, version, "+" in version_str) + + +def intersect_specifier_bounds( + per_specifier_ranges: Iterable[Sequence[Interval]], +) -> Sequence[Interval]: + """Intersect each specifier's ranges into a single sequence. + + Short-circuits once the running intersection is empty, since no later + specifier can revive it. Callers must pass at least one specifier. + """ + result: Sequence[Interval] | None = None + for sub in per_specifier_ranges: + if result is None: + result = sub + else: + result = intersect_ranges(result, sub) + if not result: + break + + if result is None: # pragma: no cover - callers guard non-empty input + raise RuntimeError("intersect_specifier_bounds called with no specifiers") + + return result + + +def matches_bounds_only(ranges: Sequence[Interval], version: Version) -> bool: + """Whether ``version`` falls within any of ``ranges``. + + The pure bounds membership test, for a single already-parsed version with + no pre-release policy applied. ``ranges`` are sorted and non-overlapping, + so a version below one range's lower bound is below every later range too. + """ + for lower, upper in ranges: + above = lower._above + if above is not None and not above(version): + return False + + below = upper._below + if below is None or below(version): + return True + + return False + + +def resolve_prereleases( + configured: bool | None, autodetected: bool | None +) -> bool | None: + """Resolve a specifier's effective default pre-release policy. + + An explicit ``configured`` value wins; otherwise an autodetected ``True`` + propagates and anything else falls back to the PEP 440 default (``None``). + """ + if configured is not None: + return configured + + if autodetected: + return True + + return None diff --git a/venv/lib/python3.12/site-packages/packaging/_structures.py b/venv/lib/python3.12/site-packages/packaging/_structures.py new file mode 100644 index 0000000..4306784 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/_structures.py @@ -0,0 +1,33 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +"""Backward-compatibility shim for unpickling Version objects serialized before +packaging 26.1. + +Old pickles reference ``packaging._structures.InfinityType`` and +``packaging._structures.NegativeInfinityType``. This module provides minimal +stand-in classes so that ``pickle.loads()`` can resolve those references. +The deserialized objects are not used for comparisons — ``Version.__setstate__`` +discards the stale ``_key`` cache and recomputes it from the core version fields. +""" + +from __future__ import annotations + + +class InfinityType: + """Stand-in for the removed ``InfinityType`` used in old comparison keys.""" + + def __repr__(self) -> str: + return "Infinity" + + +class NegativeInfinityType: + """Stand-in for the removed ``NegativeInfinityType`` used in old comparison keys.""" + + def __repr__(self) -> str: + return "-Infinity" + + +Infinity = InfinityType() +NegativeInfinity = NegativeInfinityType() diff --git a/venv/lib/python3.12/site-packages/packaging/_tokenizer.py b/venv/lib/python3.12/site-packages/packaging/_tokenizer.py new file mode 100644 index 0000000..db7cd17 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/_tokenizer.py @@ -0,0 +1,200 @@ +from __future__ import annotations + +import contextlib +import re +from dataclasses import dataclass +from typing import TYPE_CHECKING, NoReturn + +from .specifiers import Specifier + +if TYPE_CHECKING: + from collections.abc import Generator, Mapping + + +@dataclass +class Token: + __slots__ = ("name", "position", "text") + + name: str + text: str + position: int + + +class ParserSyntaxError(Exception): + """The provided source text could not be parsed correctly.""" + + def __init__( + self, + message: str, + *, + source: str, + span: tuple[int, int], + ) -> None: + self.span = span + self.message = message + self.source = source + + super().__init__() + + def __str__(self) -> str: + marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^" + return f"{self.message}\n {self.source}\n {marker}" + + +DEFAULT_RULES: dict[str, re.Pattern[str]] = { + "LEFT_PARENTHESIS": re.compile(r"\("), + "RIGHT_PARENTHESIS": re.compile(r"\)"), + "LEFT_BRACKET": re.compile(r"\["), + "RIGHT_BRACKET": re.compile(r"\]"), + "SEMICOLON": re.compile(r";"), + "COMMA": re.compile(r","), + "QUOTED_STRING": re.compile( + r""" + ( + ('[^']*') + | + ("[^"]*") + ) + """, + re.VERBOSE, + ), + "OP": re.compile(r"(===|==|~=|!=|<=|>=|<|>)"), + "BOOLOP": re.compile(r"\b(or|and)\b"), + "IN": re.compile(r"\bin\b"), + "NOT": re.compile(r"\bnot\b"), + "VARIABLE": re.compile( + r""" + \b( + python_version + |python_full_version + |os[._]name + |sys[._]platform + |platform_(release|system) + |platform[._](version|machine|python_implementation) + |python_implementation + |implementation_(name|version) + |extras? + |dependency_groups + )\b + """, + re.VERBOSE, + ), + "SPECIFIER": re.compile( + Specifier._specifier_regex_str, + re.VERBOSE | re.IGNORECASE, + ), + "AT": re.compile(r"\@"), + "URL": re.compile(r"[^ \t]+"), + "IDENTIFIER": re.compile(r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b"), + "VERSION_PREFIX_TRAIL": re.compile(r"\.\*"), + "VERSION_LOCAL_LABEL_TRAIL": re.compile(r"\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*"), + "WS": re.compile(r"[ \t]+"), + "END": re.compile(r"\Z"), +} + + +class Tokenizer: + """Context-sensitive token parsing. + + Provides methods to examine the input stream to check whether the next token + matches. + """ + + __slots__ = ("next_token", "position", "rules", "source") + + def __init__( + self, + source: str, + *, + rules: Mapping[str, re.Pattern[str]], + ) -> None: + self.source = source + self.rules = rules + self.next_token: Token | None = None + self.position = 0 + + def consume(self, name: str) -> None: + """Move beyond provided token name, if at current position.""" + if self.check(name): + self.read() + + def check(self, name: str, *, peek: bool = False) -> bool: + """Check whether the next token has the provided name. + + By default, if the check succeeds, the token *must* be read before + another check. If `peek` is set to `True`, the token is not loaded and + would need to be checked again. + """ + assert self.next_token is None, ( + f"Cannot check for {name!r}, already have {self.next_token!r}" + ) + assert name in self.rules, f"Unknown token name: {name!r}" + + expression = self.rules[name] + + match = expression.match(self.source, self.position) + if match is None: + return False + if not peek: + self.next_token = Token(name, match[0], self.position) + return True + + def expect(self, name: str, *, expected: str) -> Token: + """Expect a certain token name next, failing with a syntax error otherwise. + + The token is read and returned. + """ + if not self.check(name): + raise self.raise_syntax_error(f"Expected {expected}") + return self.read() + + def read(self) -> Token: + """Consume the next token and return it.""" + token = self.next_token + assert token is not None + + self.position += len(token.text) + self.next_token = None + + return token + + def raise_syntax_error( + self, + message: str, + *, + span_start: int | None = None, + span_end: int | None = None, + ) -> NoReturn: + """Raise ParserSyntaxError at the given position.""" + span = ( + self.position if span_start is None else span_start, + self.position if span_end is None else span_end, + ) + raise ParserSyntaxError( + message, + source=self.source, + span=span, + ) + + @contextlib.contextmanager + def enclosing_tokens( + self, open_token: str, close_token: str, *, around: str + ) -> Generator[None, None, None]: + if self.check(open_token): + open_position = self.position + self.read() + else: + open_position = None + + yield + + if open_position is None: + return + + if not self.check(close_token): + self.raise_syntax_error( + f"Expected matching {close_token} for {open_token}, after {around}", + span_start=open_position, + ) + + self.read() diff --git a/venv/lib/python3.12/site-packages/packaging/dependency_groups.py b/venv/lib/python3.12/site-packages/packaging/dependency_groups.py new file mode 100644 index 0000000..0ad2d01 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/dependency_groups.py @@ -0,0 +1,335 @@ +from __future__ import annotations + +import re +from collections.abc import Mapping, Sequence + +from .errors import _ErrorCollector +from .requirements import InvalidRequirement, Requirement + +__all__ = [ + "CyclicDependencyGroup", + "DependencyGroupInclude", + "DependencyGroupResolver", + "DuplicateGroupNames", + "InvalidDependencyGroupObject", + "resolve_dependency_groups", +] + + +def __dir__() -> list[str]: + return __all__ + + +# ----------- +# Error Types +# ----------- + + +class DuplicateGroupNames(ValueError): + """ + The same dependency groups were defined twice, with different non-normalized names. + + .. versionadded:: 26.1 + """ + + +class CyclicDependencyGroup(ValueError): + """ + The dependency group includes form a cycle. + + .. versionadded:: 26.1 + """ + + def __init__(self, requested_group: str, group: str, include_group: str) -> None: + self.requested_group = requested_group + self.group = group + self.include_group = include_group + + if include_group == group: + reason = f"{group} includes itself" + else: + reason = f"{include_group} -> {group}, {group} -> {include_group}" + super().__init__( + "Cyclic dependency group include while resolving " + f"{requested_group}: {reason}" + ) + + # Support pickling; ``args`` does not match ``__init__``'s signature. + def __reduce__(self) -> tuple[type[CyclicDependencyGroup], tuple[str, str, str]]: + return (self.__class__, (self.requested_group, self.group, self.include_group)) + + +# in the PEP 735 spec, the tables in dependency group lists were described as +# "Dependency Object Specifiers", but the only defined type of object was a +# "Dependency Group Include" -- hence the naming of this error as "Object" +class InvalidDependencyGroupObject(ValueError): + """ + A member of a dependency group was identified as a dict, but was not in a valid + format. + + .. versionadded:: 26.1 + """ + + +# ------------------------ +# Object Model & Interface +# ------------------------ + + +class DependencyGroupInclude: + """ + A reference to another dependency group by name. + + .. versionadded:: 26.1 + """ + + __slots__ = ("include_group",) + + def __init__(self, include_group: str) -> None: + """ + Initialize a DependencyGroupInclude. + + :param include_group: The name of the group referred to by this include. + """ + self.include_group = include_group + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.include_group!r})" + + +class DependencyGroupResolver: + """ + A resolver for Dependency Group data. + + This class handles caching, name normalization, cycle detection, and other + parsing requirements. There are only two public methods for exploring the data: + ``lookup()`` and ``resolve()``. + + :param dependency_groups: A mapping, as provided via pyproject + ``[dependency-groups]``. + + .. versionadded:: 26.1 + """ + + def __init__( + self, + dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]], + ) -> None: + errors = _ErrorCollector() + + self.dependency_groups = _normalize_group_names(dependency_groups, errors) + + # a map of group names to parsed data + self._parsed_groups: dict[ + str, tuple[Requirement | DependencyGroupInclude, ...] + ] = {} + # a map of group names to their ancestors, used for cycle detection + self._include_graph_ancestors: dict[str, tuple[str, ...]] = {} + # a cache of completed resolutions to Requirement lists + self._resolve_cache: dict[str, tuple[Requirement, ...]] = {} + + errors.finalize("[dependency-groups] data was invalid") + + def lookup(self, group: str) -> tuple[Requirement | DependencyGroupInclude, ...]: + """ + Lookup a group name, returning the parsed dependency data for that group. + This will not resolve includes. + + :param group: the name of the group to lookup + """ + group = _normalize_name(group) + + with _ErrorCollector().on_exit( + f"[dependency-groups] data for {group!r} was malformed" + ) as errors: + return self._parse_group(group, errors) + + def resolve(self, group: str) -> tuple[Requirement, ...]: + """ + Resolve a dependency group to a list of requirements. + + :param group: the name of the group to resolve + """ + group = _normalize_name(group) + + with _ErrorCollector().on_exit( + f"[dependency-groups] data for {group!r} was malformed" + ) as errors: + return self._resolve(group, group, errors) + + def _resolve( + self, group: str, requested_group: str, errors: _ErrorCollector + ) -> tuple[Requirement, ...]: + """ + This is a helper for cached resolution to strings. It preserves the name of the + group which the user initially requested in order to present a clearer error in + the event that a cycle is detected. + + :param group: The normalized name of the group to resolve. + :param requested_group: The group which was used in the original, user-facing + request. + """ + if group in self._resolve_cache: + return self._resolve_cache[group] + + parsed = self._parse_group(group, errors) + + resolved_group = [] + + for item in parsed: + if isinstance(item, Requirement): + resolved_group.append(item) + elif isinstance(item, DependencyGroupInclude): + include_group = _normalize_name(item.include_group) + + # if a group is cyclic, record the error + # otherwise, follow the include_group reference + # + # this allows us to examine all includes in a group, even in the + # presence of errors + if include_group in self._include_graph_ancestors.get(group, ()): + errors.error( + CyclicDependencyGroup( + requested_group, group, item.include_group + ) + ) + else: + self._include_graph_ancestors[include_group] = ( + *self._include_graph_ancestors.get(group, ()), + group, + ) + resolved_group.extend( + self._resolve(include_group, requested_group, errors) + ) + else: # pragma: no cover + raise NotImplementedError( + f"Invalid dependency group item after parse: {item}" + ) + + # in the event that errors were detected, present the group as empty and do not + # cache the result + # this ensures that repeated access to a cyclic group will raise multiple errors + if errors.errors: + return () + + self._resolve_cache[group] = tuple(resolved_group) + return self._resolve_cache[group] + + def _parse_group( + self, group: str, errors: _ErrorCollector + ) -> tuple[Requirement | DependencyGroupInclude, ...]: + # short circuit -- never do the work twice + if group in self._parsed_groups: + return self._parsed_groups[group] + + if group not in self.dependency_groups: + errors.error(LookupError(f"Dependency group '{group}' not found")) + return () + + raw_group = self.dependency_groups[group] + if isinstance(raw_group, str): + errors.error( + TypeError( + f"Dependency group {group!r} contained a string rather than a list." + ) + ) + return () + + if not isinstance(raw_group, Sequence): + errors.error( + TypeError(f"Dependency group {group!r} is not a sequence type.") + ) + return () + + elements: list[Requirement | DependencyGroupInclude] = [] + for item in raw_group: + if isinstance(item, str): + # packaging.requirements.Requirement parsing ensures that this is a + # valid PEP 508 Dependency Specifier. Collect InvalidRequirement + # if it throws that. + with errors.collect(InvalidRequirement): + elements.append(Requirement(item)) + elif isinstance(item, Mapping): + if tuple(item.keys()) != ("include-group",): + errors.error( + InvalidDependencyGroupObject( + f"Invalid dependency group item: {item!r}" + ) + ) + else: + include_group = item["include-group"] + if not isinstance(include_group, str): + msg = ( + "Dependency group include-group value is not a string: " + f"{item!r}" + ) + errors.error(TypeError(msg)) + else: + elements.append( + DependencyGroupInclude(include_group=include_group) + ) + else: + errors.error(TypeError(f"Invalid dependency group item: {item!r}")) + + if errors.errors: + return () + + self._parsed_groups[group] = tuple(elements) + return self._parsed_groups[group] + + +# -------------------- +# Functional Interface +# -------------------- + + +def resolve_dependency_groups( + dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]], /, *groups: str +) -> tuple[str, ...]: + """ + Resolve a dependency group to a tuple of requirements, as strings. + + :param dependency_groups: the parsed contents of the ``[dependency-groups]`` table + from ``pyproject.toml`` + :param groups: the name of the group(s) to resolve + + .. versionadded:: 26.1 + """ + resolver = DependencyGroupResolver(dependency_groups) + return tuple(str(r) for group in groups for r in resolver.resolve(group)) + + +# ---------------- +# internal helpers +# ---------------- + + +_NORMALIZE_PATTERN = re.compile(r"[-_.]+") + + +def _normalize_name(name: str) -> str: + return _NORMALIZE_PATTERN.sub("-", name).lower() + + +def _normalize_group_names( + dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]], + errors: _ErrorCollector, +) -> dict[str, Sequence[str | Mapping[str, str]]]: + original_names: dict[str, list[str]] = {} + normalized_groups: dict[str, Sequence[str | Mapping[str, str]]] = {} + + for group_name, value in dependency_groups.items(): + normed_group_name = _normalize_name(group_name) + original_names.setdefault(normed_group_name, []).append(group_name) + normalized_groups[normed_group_name] = value + + for normed_name, names in original_names.items(): + if len(names) > 1: + errors.error( + DuplicateGroupNames( + "Duplicate dependency group names: " + f"{normed_name} ({', '.join(names)})" + ) + ) + + return normalized_groups diff --git a/venv/lib/python3.12/site-packages/packaging/direct_url.py b/venv/lib/python3.12/site-packages/packaging/direct_url.py new file mode 100644 index 0000000..4de7acc --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/direct_url.py @@ -0,0 +1,349 @@ +from __future__ import annotations + +import dataclasses +import re +import urllib.parse +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any, Protocol, TypeVar + +if TYPE_CHECKING: # pragma: no cover + import sys + from collections.abc import Collection + from urllib.parse import SplitResult + + if sys.version_info >= (3, 11): + from typing import Self + else: + from typing_extensions import Self + +__all__ = [ + "ArchiveInfo", + "DirInfo", + "DirectUrl", + "DirectUrlValidationError", + "VcsInfo", +] + + +def __dir__() -> list[str]: + return __all__ + + +_T = TypeVar("_T") + + +class _FromMappingProtocol(Protocol): # pragma: no cover + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: ... + + +_FromMappingProtocolT = TypeVar("_FromMappingProtocolT", bound=_FromMappingProtocol) + + +def _json_dict_factory(data: list[tuple[str, Any]]) -> dict[str, Any]: + return {key: value for key, value in data if value is not None} + + +def _get(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T | None: + """Get a value from the dictionary and verify it's the expected type.""" + if (value := d.get(key)) is None: + return None + if not isinstance(value, expected_type): + raise DirectUrlValidationError( + f"Unexpected type {type(value).__name__} " + f"(expected {expected_type.__name__})", + context=key, + ) + return value + + +def _get_required(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T: + """Get a required value from the dictionary and verify it's the expected type.""" + if (value := _get(d, expected_type, key)) is None: + raise _DirectUrlRequiredKeyError(key) + return value + + +def _get_object( + d: Mapping[str, Any], target_type: type[_FromMappingProtocolT], key: str +) -> _FromMappingProtocolT | None: + """Get a dictionary value from the dictionary and convert it to a dataclass.""" + if (value := _get(d, Mapping, key)) is None: # type: ignore[type-abstract] + return None + try: + return target_type._from_dict(value) + except Exception as e: + raise DirectUrlValidationError(e, context=key) from e + + +_PEP610_USER_PASS_ENV_VARS_REGEX = re.compile( + r"^\$\{[A-Za-z0-9-_]+\}(:\$\{[A-Za-z0-9-_]+\})?$" +) + + +def _strip_auth_from_netloc(netloc: str, safe_user_passwords: Collection[str]) -> str: + if "@" not in netloc: + return netloc + user_pass, netloc_no_user_pass = netloc.rsplit("@", 1) + if user_pass in safe_user_passwords: + return netloc + if _PEP610_USER_PASS_ENV_VARS_REGEX.match(user_pass): + return netloc + return netloc_no_user_pass + + +def _strip_url(url: str, safe_user_passwords: Collection[str]) -> str: + """url with user:password part removed unless it is formed with + environment variables as specified in PEP 610, or it is a safe user:password + such as `git`. + """ + parsed_url = urllib.parse.urlsplit(url) + netloc = _strip_auth_from_netloc(parsed_url.netloc, safe_user_passwords) + return urllib.parse.urlunsplit( + ( + parsed_url.scheme, + netloc, + parsed_url.path, + parsed_url.query, + parsed_url.fragment, + ) + ) + + +def _file_url_has_absolute_path(parsed_url: SplitResult) -> bool: + return parsed_url.path.startswith("/") + + +class DirectUrlValidationError(Exception): + """Raised when when input data is not spec-compliant. + + .. versionadded:: 26.1 + """ + + context: str | None = None + message: str + + def __init__( + self, + cause: str | Exception, + *, + context: str | None = None, + ) -> None: + if isinstance(cause, DirectUrlValidationError): + if cause.context: + self.context = ( + f"{context}.{cause.context}" if context else cause.context + ) + else: + self.context = context # pragma: no cover + self.message = cause.message + else: + self.context = context + self.message = str(cause) + + def __str__(self) -> str: + if self.context: + return f"{self.message} in {self.context!r}" + return self.message + + +class _DirectUrlRequiredKeyError(DirectUrlValidationError): + def __init__(self, key: str) -> None: + super().__init__("Missing required value", context=key) + + +@dataclasses.dataclass(frozen=True, init=False) +class VcsInfo: + """The version control information of a :class:`DirectUrl`.""" + + vcs: str + commit_id: str + requested_revision: str | None = None + + def __init__( + self, + *, + vcs: str, + commit_id: str, + requested_revision: str | None = None, + ) -> None: + object.__setattr__(self, "vcs", vcs) + object.__setattr__(self, "commit_id", commit_id) + object.__setattr__(self, "requested_revision", requested_revision) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + # We can't validate vcs value because is not closed. + return cls( + vcs=_get_required(d, str, "vcs"), + requested_revision=_get(d, str, "requested_revision"), + commit_id=_get_required(d, str, "commit_id"), + ) + + +@dataclasses.dataclass(frozen=True, init=False) +class ArchiveInfo: + """The archive information of a :class:`DirectUrl`.""" + + hashes: Mapping[str, str] | None = None + + def __init__( + self, + *, + hashes: Mapping[str, str] | None = None, + ) -> None: + object.__setattr__(self, "hashes", hashes) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + hashes = _get(d, Mapping, "hashes") # type: ignore[type-abstract] + if hashes is not None and not all(isinstance(h, str) for h in hashes.values()): + raise DirectUrlValidationError( + "Hash values must be strings", context="hashes" + ) + legacy_hash = _get(d, str, "hash") + if legacy_hash is not None: + if "=" not in legacy_hash: + raise DirectUrlValidationError( + "Invalid hash format (expected '=')", + context="hash", + ) + hash_algorithm, hash_value = legacy_hash.split("=", 1) + if hashes is None: + # if `hashes` are not present, we can derive it from the legacy `hash` + hashes = {hash_algorithm: hash_value} + else: + # if `hashes` are present, the legacy `hash` must match one of them + if hash_algorithm not in hashes: + raise DirectUrlValidationError( + f"Algorithm {hash_algorithm!r} used in hash field " + f"is not present in hashes field", + context="hashes", + ) + if hashes[hash_algorithm] != hash_value: + raise DirectUrlValidationError( + f"Algorithm {hash_algorithm!r} used in hash field " + f"has different value in hashes field", + context="hash", + ) + return cls(hashes=hashes) + + +@dataclasses.dataclass(frozen=True, init=False) +class DirInfo: + """The local directory information of a :class:`DirectUrl`.""" + + editable: bool | None = None + + def __init__( + self, + *, + editable: bool | None = None, + ) -> None: + object.__setattr__(self, "editable", editable) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + return cls( + editable=_get(d, bool, "editable"), + ) + + +@dataclasses.dataclass(frozen=True, init=False) +class DirectUrl: + """A class representing a direct URL. + + .. versionadded:: 26.1 + """ + + url: str + archive_info: ArchiveInfo | None = None + vcs_info: VcsInfo | None = None + dir_info: DirInfo | None = None + subdirectory: str | None = None # XXX Path or str? + + def __init__( + self, + *, + url: str, + archive_info: ArchiveInfo | None = None, + vcs_info: VcsInfo | None = None, + dir_info: DirInfo | None = None, + subdirectory: str | None = None, + ) -> None: + object.__setattr__(self, "url", url) + object.__setattr__(self, "archive_info", archive_info) + object.__setattr__(self, "vcs_info", vcs_info) + object.__setattr__(self, "dir_info", dir_info) + object.__setattr__(self, "subdirectory", subdirectory) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + direct_url = cls( + url=_get_required(d, str, "url"), + archive_info=_get_object(d, ArchiveInfo, "archive_info"), + vcs_info=_get_object(d, VcsInfo, "vcs_info"), + dir_info=_get_object(d, DirInfo, "dir_info"), + subdirectory=_get(d, str, "subdirectory"), + ) + if ( + bool(direct_url.vcs_info) + + bool(direct_url.archive_info) + + bool(direct_url.dir_info) + ) != 1: + raise DirectUrlValidationError( + "Exactly one of vcs_info, archive_info, dir_info must be present" + ) + if direct_url.dir_info is not None: + parsed_url = urllib.parse.urlsplit(direct_url.url) + if parsed_url.scheme != "file": + raise DirectUrlValidationError( + "URL scheme must be file:// when dir_info is present", + context="url", + ) + if not _file_url_has_absolute_path(parsed_url): + raise DirectUrlValidationError( + "File URL must be absolute when dir_info is present", + context="url", + ) + # XXX subdirectory must be relative, can we, should we validate that here? + return direct_url + + @classmethod + def from_dict(cls, d: Mapping[str, Any], /) -> Self: + """Create and validate a DirectUrl instance from a JSON dictionary.""" + return cls._from_dict(d) + + def to_dict( + self, + *, + generate_legacy_hash: bool = False, + strip_user_password: bool = True, + safe_user_passwords: Collection[str] = ("git",), + ) -> Mapping[str, Any]: + """Convert the DirectUrl instance to a JSON dictionary. + + :param generate_legacy_hash: If True, include a legacy `hash` field in + `archive_info` for backward compatibility with tools that don't + support the `hashes` field. + :param strip_user_password: If True, strip user:password from the URL + unless it is formed with environment variables as specified in PEP + 610, or it is a safe user:password such as `git`. + :param safe_user_passwords: A collection of user:password strings that + should not be stripped from the URL even if `strip_user_password` is + True. + """ + res = dataclasses.asdict(self, dict_factory=_json_dict_factory) + if generate_legacy_hash and self.archive_info and self.archive_info.hashes: + hash_algorithm, hash_value = next(iter(self.archive_info.hashes.items())) + res["archive_info"]["hash"] = f"{hash_algorithm}={hash_value}" + if strip_user_password: + res["url"] = _strip_url(self.url, safe_user_passwords) + return res + + def validate(self) -> None: + """Validate the DirectUrl instance against the specification. + + Raises :class:`DirectUrlValidationError` if invalid. + """ + self.from_dict(self.to_dict()) diff --git a/venv/lib/python3.12/site-packages/packaging/errors.py b/venv/lib/python3.12/site-packages/packaging/errors.py new file mode 100644 index 0000000..d1d47cf --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/errors.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +import contextlib +import dataclasses +import sys +import typing + +__all__ = ["ExceptionGroup"] + + +def __dir__() -> list[str]: + return __all__ + + +if sys.version_info >= (3, 11): # pragma: no cover + from builtins import ExceptionGroup +else: # pragma: no cover + + class ExceptionGroup(Exception): + """A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11. + + If :external:exc:`ExceptionGroup` is already defined by Python itself, + that version is used instead. + """ + + message: str + exceptions: list[Exception] + + def __init__(self, message: str, exceptions: list[Exception]) -> None: + self.message = message + self.exceptions = exceptions + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})" + + +@dataclasses.dataclass +class _ErrorCollector: + """ + Collect errors into ExceptionGroups. + + Used like this: + + collector = _ErrorCollector() + # Add a single exception + collector.error(ValueError("one")) + + # Supports nesting, including combining ExceptionGroups + with collector.collect(): + raise ValueError("two") + collector.finalize("Found some errors") + + Since making a collector and then calling finalize later is a common pattern, + a convenience method ``on_exit`` is provided. + """ + + errors: list[Exception] = dataclasses.field(default_factory=list, init=False) + + def finalize(self, msg: str) -> None: + """Raise a group exception if there are any errors.""" + if self.errors: + raise ExceptionGroup(msg, self.errors) + + @contextlib.contextmanager + def on_exit(self, msg: str) -> typing.Generator[_ErrorCollector, None, None]: + """ + Calls finalize if no uncollected errors were present. + + Uncollected errors are raised normally. + """ + yield self + self.finalize(msg) + + @contextlib.contextmanager + def collect(self, *err_cls: type[Exception]) -> typing.Generator[None, None, None]: + """ + Context manager to collect errors into the error list. + + Must be inside loops, as only one error can be collected at a time. + """ + error_classes = err_cls or (Exception,) + try: + yield + except ExceptionGroup as error: + self.errors.extend(error.exceptions) + except error_classes as error: + self.errors.append(error) + + def error( + self, + error: Exception, + ) -> None: + """Add an error to the list.""" + self.errors.append(error) diff --git a/venv/lib/python3.12/site-packages/packaging/licenses/__init__.py b/venv/lib/python3.12/site-packages/packaging/licenses/__init__.py new file mode 100644 index 0000000..f95ac02 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/licenses/__init__.py @@ -0,0 +1,198 @@ +####################################################################################### +# +# Adapted from: +# https://github.com/pypa/hatch/blob/5352e44/backend/src/hatchling/licenses/parse.py +# +# MIT License +# +# Copyright (c) 2017-present Ofek Lev +# +# 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. +# +# +# With additional allowance of arbitrary `LicenseRef-` identifiers, not just +# `LicenseRef-Public-Domain` and `LicenseRef-Proprietary`. +# +####################################################################################### +from __future__ import annotations + +import re +from typing import NewType, cast + +from ._spdx import EXCEPTIONS, LICENSES + +__all__ = [ + "InvalidLicenseExpression", + "NormalizedLicenseExpression", + "canonicalize_license_expression", +] + + +# Simple __dir__ implementation since there are no public submodules +def __dir__() -> list[str]: + return __all__ + + +license_ref_allowed = re.compile("^[A-Za-z0-9.-]+$") + +NormalizedLicenseExpression = NewType("NormalizedLicenseExpression", str) +""" +A :class:`typing.NewType` of :class:`str`, representing a normalized +License-Expression. +""" + + +class InvalidLicenseExpression(ValueError): + """Raised when a license-expression string is invalid + + >>> from packaging.licenses import canonicalize_license_expression + >>> canonicalize_license_expression("invalid") + Traceback (most recent call last): + ... + packaging.licenses.InvalidLicenseExpression: Unknown license: 'invalid' + """ + + +def canonicalize_license_expression( + raw_license_expression: str, +) -> NormalizedLicenseExpression: + """ + This function takes a valid License-Expression, and returns the normalized + form of it. + + The return type is typed as :class:`NormalizedLicenseExpression`. This + allows type checkers to help require that a string has passed through this + function before use. + + :param str raw_license_expression: The License-Expression to canonicalize. + :raises InvalidLicenseExpression: If the License-Expression is invalid due to an + invalid/unknown license identifier or invalid syntax. + + .. doctest:: + + >>> from packaging.licenses import canonicalize_license_expression + >>> canonicalize_license_expression("mit") + 'MIT' + >>> canonicalize_license_expression("mit and (apache-2.0 or bsd-2-clause)") + 'MIT AND (Apache-2.0 OR BSD-2-Clause)' + >>> canonicalize_license_expression("(mit") + Traceback (most recent call last): + ... + InvalidLicenseExpression: Invalid license expression: '(mit' + >>> canonicalize_license_expression("Use-it-after-midnight") + Traceback (most recent call last): + ... + InvalidLicenseExpression: Unknown license: 'Use-it-after-midnight' + """ + if not raw_license_expression: + message = f"Invalid license expression: {raw_license_expression!r}" + raise InvalidLicenseExpression(message) + + # Pad any parentheses so tokenization can be achieved by merely splitting on + # whitespace. + license_expression = raw_license_expression.replace("(", " ( ").replace(")", " ) ") + licenseref_prefix = "LicenseRef-" + license_refs = { + ref.lower(): "LicenseRef-" + ref[len(licenseref_prefix) :] + for ref in license_expression.split() + if ref.lower().startswith(licenseref_prefix.lower()) + } + + # Normalize to lower case so we can look up licenses/exceptions + # and so boolean operators are Python-compatible. + license_expression = license_expression.lower() + + tokens = license_expression.split() + + # Rather than implementing a parenthesis/boolean logic parser, create an + # expression that Python can parse. Everything that is not involved with the + # grammar itself is replaced with the placeholder `False` and the resultant + # expression should become a valid Python expression. + python_tokens = [] + for token in tokens: + if token not in {"or", "and", "with", "(", ")"}: + python_tokens.append("False") + elif token == "with": + python_tokens.append("or") + elif ( + token == "(" + and python_tokens + and python_tokens[-1] not in {"or", "and", "("} + ) or (token == ")" and python_tokens and python_tokens[-1] == "("): + message = f"Invalid license expression: {raw_license_expression!r}" + raise InvalidLicenseExpression(message) + else: + python_tokens.append(token) + + python_expression = " ".join(python_tokens) + try: + compile(python_expression, "", "eval") + except SyntaxError: + message = f"Invalid license expression: {raw_license_expression!r}" + raise InvalidLicenseExpression(message) from None + + # Take a final pass to check for unknown licenses/exceptions. + normalized_tokens = [] + last_license_start = False + for index, token in enumerate(tokens): + if token in {"or", "and", "with", "(", ")"}: + if token == "with" and ( + not last_license_start + or index + 1 == len(tokens) + or tokens[index + 1] in {"or", "and", "with", "(", ")"} + ): + message = f"Invalid license expression: {raw_license_expression!r}" + raise InvalidLicenseExpression(message) + normalized_tokens.append(token.upper()) + last_license_start = False + continue + + if normalized_tokens and normalized_tokens[-1] == "WITH": + if token not in EXCEPTIONS: + message = f"Unknown license exception: {token!r}" + raise InvalidLicenseExpression(message) + + normalized_tokens.append(EXCEPTIONS[token]["id"]) + last_license_start = False + else: + if token.endswith("+"): + final_token = token[:-1] + suffix = "+" + else: + final_token = token + suffix = "" + + if final_token.startswith("licenseref-"): + license_ref_id = final_token[len("licenseref-") :] + if suffix or not license_ref_allowed.match(license_ref_id): + message = f"Invalid licenseref: {token!r}" + raise InvalidLicenseExpression(message) + normalized_tokens.append(license_refs[final_token]) + else: + if final_token not in LICENSES: + message = f"Unknown license: {final_token!r}" + raise InvalidLicenseExpression(message) + normalized_tokens.append(LICENSES[final_token]["id"] + suffix) + last_license_start = True + + normalized_expression = " ".join(normalized_tokens) + + return cast( + "NormalizedLicenseExpression", + normalized_expression.replace("( ", "(").replace(" )", ")"), + ) diff --git a/venv/lib/python3.12/site-packages/packaging/licenses/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/licenses/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..19799c5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/licenses/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/licenses/__pycache__/_spdx.cpython-312.pyc b/venv/lib/python3.12/site-packages/packaging/licenses/__pycache__/_spdx.cpython-312.pyc new file mode 100644 index 0000000..06fc949 Binary files /dev/null and b/venv/lib/python3.12/site-packages/packaging/licenses/__pycache__/_spdx.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/packaging/licenses/_spdx.py b/venv/lib/python3.12/site-packages/packaging/licenses/_spdx.py new file mode 100644 index 0000000..a277af2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/licenses/_spdx.py @@ -0,0 +1,799 @@ + +from __future__ import annotations + +from typing import TypedDict + +class SPDXLicense(TypedDict): + id: str + deprecated: bool + +class SPDXException(TypedDict): + id: str + deprecated: bool + + +VERSION = '3.27.0' + +LICENSES: dict[str, SPDXLicense] = { + '0bsd': {'id': '0BSD', 'deprecated': False}, + '3d-slicer-1.0': {'id': '3D-Slicer-1.0', 'deprecated': False}, + 'aal': {'id': 'AAL', 'deprecated': False}, + 'abstyles': {'id': 'Abstyles', 'deprecated': False}, + 'adacore-doc': {'id': 'AdaCore-doc', 'deprecated': False}, + 'adobe-2006': {'id': 'Adobe-2006', 'deprecated': False}, + 'adobe-display-postscript': {'id': 'Adobe-Display-PostScript', 'deprecated': False}, + 'adobe-glyph': {'id': 'Adobe-Glyph', 'deprecated': False}, + 'adobe-utopia': {'id': 'Adobe-Utopia', 'deprecated': False}, + 'adsl': {'id': 'ADSL', 'deprecated': False}, + 'afl-1.1': {'id': 'AFL-1.1', 'deprecated': False}, + 'afl-1.2': {'id': 'AFL-1.2', 'deprecated': False}, + 'afl-2.0': {'id': 'AFL-2.0', 'deprecated': False}, + 'afl-2.1': {'id': 'AFL-2.1', 'deprecated': False}, + 'afl-3.0': {'id': 'AFL-3.0', 'deprecated': False}, + 'afmparse': {'id': 'Afmparse', 'deprecated': False}, + 'agpl-1.0': {'id': 'AGPL-1.0', 'deprecated': True}, + 'agpl-1.0-only': {'id': 'AGPL-1.0-only', 'deprecated': False}, + 'agpl-1.0-or-later': {'id': 'AGPL-1.0-or-later', 'deprecated': False}, + 'agpl-3.0': {'id': 'AGPL-3.0', 'deprecated': True}, + 'agpl-3.0-only': {'id': 'AGPL-3.0-only', 'deprecated': False}, + 'agpl-3.0-or-later': {'id': 'AGPL-3.0-or-later', 'deprecated': False}, + 'aladdin': {'id': 'Aladdin', 'deprecated': False}, + 'amd-newlib': {'id': 'AMD-newlib', 'deprecated': False}, + 'amdplpa': {'id': 'AMDPLPA', 'deprecated': False}, + 'aml': {'id': 'AML', 'deprecated': False}, + 'aml-glslang': {'id': 'AML-glslang', 'deprecated': False}, + 'ampas': {'id': 'AMPAS', 'deprecated': False}, + 'antlr-pd': {'id': 'ANTLR-PD', 'deprecated': False}, + 'antlr-pd-fallback': {'id': 'ANTLR-PD-fallback', 'deprecated': False}, + 'any-osi': {'id': 'any-OSI', 'deprecated': False}, + 'any-osi-perl-modules': {'id': 'any-OSI-perl-modules', 'deprecated': False}, + 'apache-1.0': {'id': 'Apache-1.0', 'deprecated': False}, + 'apache-1.1': {'id': 'Apache-1.1', 'deprecated': False}, + 'apache-2.0': {'id': 'Apache-2.0', 'deprecated': False}, + 'apafml': {'id': 'APAFML', 'deprecated': False}, + 'apl-1.0': {'id': 'APL-1.0', 'deprecated': False}, + 'app-s2p': {'id': 'App-s2p', 'deprecated': False}, + 'apsl-1.0': {'id': 'APSL-1.0', 'deprecated': False}, + 'apsl-1.1': {'id': 'APSL-1.1', 'deprecated': False}, + 'apsl-1.2': {'id': 'APSL-1.2', 'deprecated': False}, + 'apsl-2.0': {'id': 'APSL-2.0', 'deprecated': False}, + 'arphic-1999': {'id': 'Arphic-1999', 'deprecated': False}, + 'artistic-1.0': {'id': 'Artistic-1.0', 'deprecated': False}, + 'artistic-1.0-cl8': {'id': 'Artistic-1.0-cl8', 'deprecated': False}, + 'artistic-1.0-perl': {'id': 'Artistic-1.0-Perl', 'deprecated': False}, + 'artistic-2.0': {'id': 'Artistic-2.0', 'deprecated': False}, + 'artistic-dist': {'id': 'Artistic-dist', 'deprecated': False}, + 'aspell-ru': {'id': 'Aspell-RU', 'deprecated': False}, + 'aswf-digital-assets-1.0': {'id': 'ASWF-Digital-Assets-1.0', 'deprecated': False}, + 'aswf-digital-assets-1.1': {'id': 'ASWF-Digital-Assets-1.1', 'deprecated': False}, + 'baekmuk': {'id': 'Baekmuk', 'deprecated': False}, + 'bahyph': {'id': 'Bahyph', 'deprecated': False}, + 'barr': {'id': 'Barr', 'deprecated': False}, + 'bcrypt-solar-designer': {'id': 'bcrypt-Solar-Designer', 'deprecated': False}, + 'beerware': {'id': 'Beerware', 'deprecated': False}, + 'bitstream-charter': {'id': 'Bitstream-Charter', 'deprecated': False}, + 'bitstream-vera': {'id': 'Bitstream-Vera', 'deprecated': False}, + 'bittorrent-1.0': {'id': 'BitTorrent-1.0', 'deprecated': False}, + 'bittorrent-1.1': {'id': 'BitTorrent-1.1', 'deprecated': False}, + 'blessing': {'id': 'blessing', 'deprecated': False}, + 'blueoak-1.0.0': {'id': 'BlueOak-1.0.0', 'deprecated': False}, + 'boehm-gc': {'id': 'Boehm-GC', 'deprecated': False}, + 'boehm-gc-without-fee': {'id': 'Boehm-GC-without-fee', 'deprecated': False}, + 'borceux': {'id': 'Borceux', 'deprecated': False}, + 'brian-gladman-2-clause': {'id': 'Brian-Gladman-2-Clause', 'deprecated': False}, + 'brian-gladman-3-clause': {'id': 'Brian-Gladman-3-Clause', 'deprecated': False}, + 'bsd-1-clause': {'id': 'BSD-1-Clause', 'deprecated': False}, + 'bsd-2-clause': {'id': 'BSD-2-Clause', 'deprecated': False}, + 'bsd-2-clause-darwin': {'id': 'BSD-2-Clause-Darwin', 'deprecated': False}, + 'bsd-2-clause-first-lines': {'id': 'BSD-2-Clause-first-lines', 'deprecated': False}, + 'bsd-2-clause-freebsd': {'id': 'BSD-2-Clause-FreeBSD', 'deprecated': True}, + 'bsd-2-clause-netbsd': {'id': 'BSD-2-Clause-NetBSD', 'deprecated': True}, + 'bsd-2-clause-patent': {'id': 'BSD-2-Clause-Patent', 'deprecated': False}, + 'bsd-2-clause-pkgconf-disclaimer': {'id': 'BSD-2-Clause-pkgconf-disclaimer', 'deprecated': False}, + 'bsd-2-clause-views': {'id': 'BSD-2-Clause-Views', 'deprecated': False}, + 'bsd-3-clause': {'id': 'BSD-3-Clause', 'deprecated': False}, + 'bsd-3-clause-acpica': {'id': 'BSD-3-Clause-acpica', 'deprecated': False}, + 'bsd-3-clause-attribution': {'id': 'BSD-3-Clause-Attribution', 'deprecated': False}, + 'bsd-3-clause-clear': {'id': 'BSD-3-Clause-Clear', 'deprecated': False}, + 'bsd-3-clause-flex': {'id': 'BSD-3-Clause-flex', 'deprecated': False}, + 'bsd-3-clause-hp': {'id': 'BSD-3-Clause-HP', 'deprecated': False}, + 'bsd-3-clause-lbnl': {'id': 'BSD-3-Clause-LBNL', 'deprecated': False}, + 'bsd-3-clause-modification': {'id': 'BSD-3-Clause-Modification', 'deprecated': False}, + 'bsd-3-clause-no-military-license': {'id': 'BSD-3-Clause-No-Military-License', 'deprecated': False}, + 'bsd-3-clause-no-nuclear-license': {'id': 'BSD-3-Clause-No-Nuclear-License', 'deprecated': False}, + 'bsd-3-clause-no-nuclear-license-2014': {'id': 'BSD-3-Clause-No-Nuclear-License-2014', 'deprecated': False}, + 'bsd-3-clause-no-nuclear-warranty': {'id': 'BSD-3-Clause-No-Nuclear-Warranty', 'deprecated': False}, + 'bsd-3-clause-open-mpi': {'id': 'BSD-3-Clause-Open-MPI', 'deprecated': False}, + 'bsd-3-clause-sun': {'id': 'BSD-3-Clause-Sun', 'deprecated': False}, + 'bsd-4-clause': {'id': 'BSD-4-Clause', 'deprecated': False}, + 'bsd-4-clause-shortened': {'id': 'BSD-4-Clause-Shortened', 'deprecated': False}, + 'bsd-4-clause-uc': {'id': 'BSD-4-Clause-UC', 'deprecated': False}, + 'bsd-4.3reno': {'id': 'BSD-4.3RENO', 'deprecated': False}, + 'bsd-4.3tahoe': {'id': 'BSD-4.3TAHOE', 'deprecated': False}, + 'bsd-advertising-acknowledgement': {'id': 'BSD-Advertising-Acknowledgement', 'deprecated': False}, + 'bsd-attribution-hpnd-disclaimer': {'id': 'BSD-Attribution-HPND-disclaimer', 'deprecated': False}, + 'bsd-inferno-nettverk': {'id': 'BSD-Inferno-Nettverk', 'deprecated': False}, + 'bsd-protection': {'id': 'BSD-Protection', 'deprecated': False}, + 'bsd-source-beginning-file': {'id': 'BSD-Source-beginning-file', 'deprecated': False}, + 'bsd-source-code': {'id': 'BSD-Source-Code', 'deprecated': False}, + 'bsd-systemics': {'id': 'BSD-Systemics', 'deprecated': False}, + 'bsd-systemics-w3works': {'id': 'BSD-Systemics-W3Works', 'deprecated': False}, + 'bsl-1.0': {'id': 'BSL-1.0', 'deprecated': False}, + 'busl-1.1': {'id': 'BUSL-1.1', 'deprecated': False}, + 'bzip2-1.0.5': {'id': 'bzip2-1.0.5', 'deprecated': True}, + 'bzip2-1.0.6': {'id': 'bzip2-1.0.6', 'deprecated': False}, + 'c-uda-1.0': {'id': 'C-UDA-1.0', 'deprecated': False}, + 'cal-1.0': {'id': 'CAL-1.0', 'deprecated': False}, + 'cal-1.0-combined-work-exception': {'id': 'CAL-1.0-Combined-Work-Exception', 'deprecated': False}, + 'caldera': {'id': 'Caldera', 'deprecated': False}, + 'caldera-no-preamble': {'id': 'Caldera-no-preamble', 'deprecated': False}, + 'catharon': {'id': 'Catharon', 'deprecated': False}, + 'catosl-1.1': {'id': 'CATOSL-1.1', 'deprecated': False}, + 'cc-by-1.0': {'id': 'CC-BY-1.0', 'deprecated': False}, + 'cc-by-2.0': {'id': 'CC-BY-2.0', 'deprecated': False}, + 'cc-by-2.5': {'id': 'CC-BY-2.5', 'deprecated': False}, + 'cc-by-2.5-au': {'id': 'CC-BY-2.5-AU', 'deprecated': False}, + 'cc-by-3.0': {'id': 'CC-BY-3.0', 'deprecated': False}, + 'cc-by-3.0-at': {'id': 'CC-BY-3.0-AT', 'deprecated': False}, + 'cc-by-3.0-au': {'id': 'CC-BY-3.0-AU', 'deprecated': False}, + 'cc-by-3.0-de': {'id': 'CC-BY-3.0-DE', 'deprecated': False}, + 'cc-by-3.0-igo': {'id': 'CC-BY-3.0-IGO', 'deprecated': False}, + 'cc-by-3.0-nl': {'id': 'CC-BY-3.0-NL', 'deprecated': False}, + 'cc-by-3.0-us': {'id': 'CC-BY-3.0-US', 'deprecated': False}, + 'cc-by-4.0': {'id': 'CC-BY-4.0', 'deprecated': False}, + 'cc-by-nc-1.0': {'id': 'CC-BY-NC-1.0', 'deprecated': False}, + 'cc-by-nc-2.0': {'id': 'CC-BY-NC-2.0', 'deprecated': False}, + 'cc-by-nc-2.5': {'id': 'CC-BY-NC-2.5', 'deprecated': False}, + 'cc-by-nc-3.0': {'id': 'CC-BY-NC-3.0', 'deprecated': False}, + 'cc-by-nc-3.0-de': {'id': 'CC-BY-NC-3.0-DE', 'deprecated': False}, + 'cc-by-nc-4.0': {'id': 'CC-BY-NC-4.0', 'deprecated': False}, + 'cc-by-nc-nd-1.0': {'id': 'CC-BY-NC-ND-1.0', 'deprecated': False}, + 'cc-by-nc-nd-2.0': {'id': 'CC-BY-NC-ND-2.0', 'deprecated': False}, + 'cc-by-nc-nd-2.5': {'id': 'CC-BY-NC-ND-2.5', 'deprecated': False}, + 'cc-by-nc-nd-3.0': {'id': 'CC-BY-NC-ND-3.0', 'deprecated': False}, + 'cc-by-nc-nd-3.0-de': {'id': 'CC-BY-NC-ND-3.0-DE', 'deprecated': False}, + 'cc-by-nc-nd-3.0-igo': {'id': 'CC-BY-NC-ND-3.0-IGO', 'deprecated': False}, + 'cc-by-nc-nd-4.0': {'id': 'CC-BY-NC-ND-4.0', 'deprecated': False}, + 'cc-by-nc-sa-1.0': {'id': 'CC-BY-NC-SA-1.0', 'deprecated': False}, + 'cc-by-nc-sa-2.0': {'id': 'CC-BY-NC-SA-2.0', 'deprecated': False}, + 'cc-by-nc-sa-2.0-de': {'id': 'CC-BY-NC-SA-2.0-DE', 'deprecated': False}, + 'cc-by-nc-sa-2.0-fr': {'id': 'CC-BY-NC-SA-2.0-FR', 'deprecated': False}, + 'cc-by-nc-sa-2.0-uk': {'id': 'CC-BY-NC-SA-2.0-UK', 'deprecated': False}, + 'cc-by-nc-sa-2.5': {'id': 'CC-BY-NC-SA-2.5', 'deprecated': False}, + 'cc-by-nc-sa-3.0': {'id': 'CC-BY-NC-SA-3.0', 'deprecated': False}, + 'cc-by-nc-sa-3.0-de': {'id': 'CC-BY-NC-SA-3.0-DE', 'deprecated': False}, + 'cc-by-nc-sa-3.0-igo': {'id': 'CC-BY-NC-SA-3.0-IGO', 'deprecated': False}, + 'cc-by-nc-sa-4.0': {'id': 'CC-BY-NC-SA-4.0', 'deprecated': False}, + 'cc-by-nd-1.0': {'id': 'CC-BY-ND-1.0', 'deprecated': False}, + 'cc-by-nd-2.0': {'id': 'CC-BY-ND-2.0', 'deprecated': False}, + 'cc-by-nd-2.5': {'id': 'CC-BY-ND-2.5', 'deprecated': False}, + 'cc-by-nd-3.0': {'id': 'CC-BY-ND-3.0', 'deprecated': False}, + 'cc-by-nd-3.0-de': {'id': 'CC-BY-ND-3.0-DE', 'deprecated': False}, + 'cc-by-nd-4.0': {'id': 'CC-BY-ND-4.0', 'deprecated': False}, + 'cc-by-sa-1.0': {'id': 'CC-BY-SA-1.0', 'deprecated': False}, + 'cc-by-sa-2.0': {'id': 'CC-BY-SA-2.0', 'deprecated': False}, + 'cc-by-sa-2.0-uk': {'id': 'CC-BY-SA-2.0-UK', 'deprecated': False}, + 'cc-by-sa-2.1-jp': {'id': 'CC-BY-SA-2.1-JP', 'deprecated': False}, + 'cc-by-sa-2.5': {'id': 'CC-BY-SA-2.5', 'deprecated': False}, + 'cc-by-sa-3.0': {'id': 'CC-BY-SA-3.0', 'deprecated': False}, + 'cc-by-sa-3.0-at': {'id': 'CC-BY-SA-3.0-AT', 'deprecated': False}, + 'cc-by-sa-3.0-de': {'id': 'CC-BY-SA-3.0-DE', 'deprecated': False}, + 'cc-by-sa-3.0-igo': {'id': 'CC-BY-SA-3.0-IGO', 'deprecated': False}, + 'cc-by-sa-4.0': {'id': 'CC-BY-SA-4.0', 'deprecated': False}, + 'cc-pddc': {'id': 'CC-PDDC', 'deprecated': False}, + 'cc-pdm-1.0': {'id': 'CC-PDM-1.0', 'deprecated': False}, + 'cc-sa-1.0': {'id': 'CC-SA-1.0', 'deprecated': False}, + 'cc0-1.0': {'id': 'CC0-1.0', 'deprecated': False}, + 'cddl-1.0': {'id': 'CDDL-1.0', 'deprecated': False}, + 'cddl-1.1': {'id': 'CDDL-1.1', 'deprecated': False}, + 'cdl-1.0': {'id': 'CDL-1.0', 'deprecated': False}, + 'cdla-permissive-1.0': {'id': 'CDLA-Permissive-1.0', 'deprecated': False}, + 'cdla-permissive-2.0': {'id': 'CDLA-Permissive-2.0', 'deprecated': False}, + 'cdla-sharing-1.0': {'id': 'CDLA-Sharing-1.0', 'deprecated': False}, + 'cecill-1.0': {'id': 'CECILL-1.0', 'deprecated': False}, + 'cecill-1.1': {'id': 'CECILL-1.1', 'deprecated': False}, + 'cecill-2.0': {'id': 'CECILL-2.0', 'deprecated': False}, + 'cecill-2.1': {'id': 'CECILL-2.1', 'deprecated': False}, + 'cecill-b': {'id': 'CECILL-B', 'deprecated': False}, + 'cecill-c': {'id': 'CECILL-C', 'deprecated': False}, + 'cern-ohl-1.1': {'id': 'CERN-OHL-1.1', 'deprecated': False}, + 'cern-ohl-1.2': {'id': 'CERN-OHL-1.2', 'deprecated': False}, + 'cern-ohl-p-2.0': {'id': 'CERN-OHL-P-2.0', 'deprecated': False}, + 'cern-ohl-s-2.0': {'id': 'CERN-OHL-S-2.0', 'deprecated': False}, + 'cern-ohl-w-2.0': {'id': 'CERN-OHL-W-2.0', 'deprecated': False}, + 'cfitsio': {'id': 'CFITSIO', 'deprecated': False}, + 'check-cvs': {'id': 'check-cvs', 'deprecated': False}, + 'checkmk': {'id': 'checkmk', 'deprecated': False}, + 'clartistic': {'id': 'ClArtistic', 'deprecated': False}, + 'clips': {'id': 'Clips', 'deprecated': False}, + 'cmu-mach': {'id': 'CMU-Mach', 'deprecated': False}, + 'cmu-mach-nodoc': {'id': 'CMU-Mach-nodoc', 'deprecated': False}, + 'cnri-jython': {'id': 'CNRI-Jython', 'deprecated': False}, + 'cnri-python': {'id': 'CNRI-Python', 'deprecated': False}, + 'cnri-python-gpl-compatible': {'id': 'CNRI-Python-GPL-Compatible', 'deprecated': False}, + 'coil-1.0': {'id': 'COIL-1.0', 'deprecated': False}, + 'community-spec-1.0': {'id': 'Community-Spec-1.0', 'deprecated': False}, + 'condor-1.1': {'id': 'Condor-1.1', 'deprecated': False}, + 'copyleft-next-0.3.0': {'id': 'copyleft-next-0.3.0', 'deprecated': False}, + 'copyleft-next-0.3.1': {'id': 'copyleft-next-0.3.1', 'deprecated': False}, + 'cornell-lossless-jpeg': {'id': 'Cornell-Lossless-JPEG', 'deprecated': False}, + 'cpal-1.0': {'id': 'CPAL-1.0', 'deprecated': False}, + 'cpl-1.0': {'id': 'CPL-1.0', 'deprecated': False}, + 'cpol-1.02': {'id': 'CPOL-1.02', 'deprecated': False}, + 'cronyx': {'id': 'Cronyx', 'deprecated': False}, + 'crossword': {'id': 'Crossword', 'deprecated': False}, + 'cryptoswift': {'id': 'CryptoSwift', 'deprecated': False}, + 'crystalstacker': {'id': 'CrystalStacker', 'deprecated': False}, + 'cua-opl-1.0': {'id': 'CUA-OPL-1.0', 'deprecated': False}, + 'cube': {'id': 'Cube', 'deprecated': False}, + 'curl': {'id': 'curl', 'deprecated': False}, + 'cve-tou': {'id': 'cve-tou', 'deprecated': False}, + 'd-fsl-1.0': {'id': 'D-FSL-1.0', 'deprecated': False}, + 'dec-3-clause': {'id': 'DEC-3-Clause', 'deprecated': False}, + 'diffmark': {'id': 'diffmark', 'deprecated': False}, + 'dl-de-by-2.0': {'id': 'DL-DE-BY-2.0', 'deprecated': False}, + 'dl-de-zero-2.0': {'id': 'DL-DE-ZERO-2.0', 'deprecated': False}, + 'doc': {'id': 'DOC', 'deprecated': False}, + 'docbook-dtd': {'id': 'DocBook-DTD', 'deprecated': False}, + 'docbook-schema': {'id': 'DocBook-Schema', 'deprecated': False}, + 'docbook-stylesheet': {'id': 'DocBook-Stylesheet', 'deprecated': False}, + 'docbook-xml': {'id': 'DocBook-XML', 'deprecated': False}, + 'dotseqn': {'id': 'Dotseqn', 'deprecated': False}, + 'drl-1.0': {'id': 'DRL-1.0', 'deprecated': False}, + 'drl-1.1': {'id': 'DRL-1.1', 'deprecated': False}, + 'dsdp': {'id': 'DSDP', 'deprecated': False}, + 'dtoa': {'id': 'dtoa', 'deprecated': False}, + 'dvipdfm': {'id': 'dvipdfm', 'deprecated': False}, + 'ecl-1.0': {'id': 'ECL-1.0', 'deprecated': False}, + 'ecl-2.0': {'id': 'ECL-2.0', 'deprecated': False}, + 'ecos-2.0': {'id': 'eCos-2.0', 'deprecated': True}, + 'efl-1.0': {'id': 'EFL-1.0', 'deprecated': False}, + 'efl-2.0': {'id': 'EFL-2.0', 'deprecated': False}, + 'egenix': {'id': 'eGenix', 'deprecated': False}, + 'elastic-2.0': {'id': 'Elastic-2.0', 'deprecated': False}, + 'entessa': {'id': 'Entessa', 'deprecated': False}, + 'epics': {'id': 'EPICS', 'deprecated': False}, + 'epl-1.0': {'id': 'EPL-1.0', 'deprecated': False}, + 'epl-2.0': {'id': 'EPL-2.0', 'deprecated': False}, + 'erlpl-1.1': {'id': 'ErlPL-1.1', 'deprecated': False}, + 'etalab-2.0': {'id': 'etalab-2.0', 'deprecated': False}, + 'eudatagrid': {'id': 'EUDatagrid', 'deprecated': False}, + 'eupl-1.0': {'id': 'EUPL-1.0', 'deprecated': False}, + 'eupl-1.1': {'id': 'EUPL-1.1', 'deprecated': False}, + 'eupl-1.2': {'id': 'EUPL-1.2', 'deprecated': False}, + 'eurosym': {'id': 'Eurosym', 'deprecated': False}, + 'fair': {'id': 'Fair', 'deprecated': False}, + 'fbm': {'id': 'FBM', 'deprecated': False}, + 'fdk-aac': {'id': 'FDK-AAC', 'deprecated': False}, + 'ferguson-twofish': {'id': 'Ferguson-Twofish', 'deprecated': False}, + 'frameworx-1.0': {'id': 'Frameworx-1.0', 'deprecated': False}, + 'freebsd-doc': {'id': 'FreeBSD-DOC', 'deprecated': False}, + 'freeimage': {'id': 'FreeImage', 'deprecated': False}, + 'fsfap': {'id': 'FSFAP', 'deprecated': False}, + 'fsfap-no-warranty-disclaimer': {'id': 'FSFAP-no-warranty-disclaimer', 'deprecated': False}, + 'fsful': {'id': 'FSFUL', 'deprecated': False}, + 'fsfullr': {'id': 'FSFULLR', 'deprecated': False}, + 'fsfullrsd': {'id': 'FSFULLRSD', 'deprecated': False}, + 'fsfullrwd': {'id': 'FSFULLRWD', 'deprecated': False}, + 'fsl-1.1-alv2': {'id': 'FSL-1.1-ALv2', 'deprecated': False}, + 'fsl-1.1-mit': {'id': 'FSL-1.1-MIT', 'deprecated': False}, + 'ftl': {'id': 'FTL', 'deprecated': False}, + 'furuseth': {'id': 'Furuseth', 'deprecated': False}, + 'fwlw': {'id': 'fwlw', 'deprecated': False}, + 'game-programming-gems': {'id': 'Game-Programming-Gems', 'deprecated': False}, + 'gcr-docs': {'id': 'GCR-docs', 'deprecated': False}, + 'gd': {'id': 'GD', 'deprecated': False}, + 'generic-xts': {'id': 'generic-xts', 'deprecated': False}, + 'gfdl-1.1': {'id': 'GFDL-1.1', 'deprecated': True}, + 'gfdl-1.1-invariants-only': {'id': 'GFDL-1.1-invariants-only', 'deprecated': False}, + 'gfdl-1.1-invariants-or-later': {'id': 'GFDL-1.1-invariants-or-later', 'deprecated': False}, + 'gfdl-1.1-no-invariants-only': {'id': 'GFDL-1.1-no-invariants-only', 'deprecated': False}, + 'gfdl-1.1-no-invariants-or-later': {'id': 'GFDL-1.1-no-invariants-or-later', 'deprecated': False}, + 'gfdl-1.1-only': {'id': 'GFDL-1.1-only', 'deprecated': False}, + 'gfdl-1.1-or-later': {'id': 'GFDL-1.1-or-later', 'deprecated': False}, + 'gfdl-1.2': {'id': 'GFDL-1.2', 'deprecated': True}, + 'gfdl-1.2-invariants-only': {'id': 'GFDL-1.2-invariants-only', 'deprecated': False}, + 'gfdl-1.2-invariants-or-later': {'id': 'GFDL-1.2-invariants-or-later', 'deprecated': False}, + 'gfdl-1.2-no-invariants-only': {'id': 'GFDL-1.2-no-invariants-only', 'deprecated': False}, + 'gfdl-1.2-no-invariants-or-later': {'id': 'GFDL-1.2-no-invariants-or-later', 'deprecated': False}, + 'gfdl-1.2-only': {'id': 'GFDL-1.2-only', 'deprecated': False}, + 'gfdl-1.2-or-later': {'id': 'GFDL-1.2-or-later', 'deprecated': False}, + 'gfdl-1.3': {'id': 'GFDL-1.3', 'deprecated': True}, + 'gfdl-1.3-invariants-only': {'id': 'GFDL-1.3-invariants-only', 'deprecated': False}, + 'gfdl-1.3-invariants-or-later': {'id': 'GFDL-1.3-invariants-or-later', 'deprecated': False}, + 'gfdl-1.3-no-invariants-only': {'id': 'GFDL-1.3-no-invariants-only', 'deprecated': False}, + 'gfdl-1.3-no-invariants-or-later': {'id': 'GFDL-1.3-no-invariants-or-later', 'deprecated': False}, + 'gfdl-1.3-only': {'id': 'GFDL-1.3-only', 'deprecated': False}, + 'gfdl-1.3-or-later': {'id': 'GFDL-1.3-or-later', 'deprecated': False}, + 'giftware': {'id': 'Giftware', 'deprecated': False}, + 'gl2ps': {'id': 'GL2PS', 'deprecated': False}, + 'glide': {'id': 'Glide', 'deprecated': False}, + 'glulxe': {'id': 'Glulxe', 'deprecated': False}, + 'glwtpl': {'id': 'GLWTPL', 'deprecated': False}, + 'gnuplot': {'id': 'gnuplot', 'deprecated': False}, + 'gpl-1.0': {'id': 'GPL-1.0', 'deprecated': True}, + 'gpl-1.0+': {'id': 'GPL-1.0+', 'deprecated': True}, + 'gpl-1.0-only': {'id': 'GPL-1.0-only', 'deprecated': False}, + 'gpl-1.0-or-later': {'id': 'GPL-1.0-or-later', 'deprecated': False}, + 'gpl-2.0': {'id': 'GPL-2.0', 'deprecated': True}, + 'gpl-2.0+': {'id': 'GPL-2.0+', 'deprecated': True}, + 'gpl-2.0-only': {'id': 'GPL-2.0-only', 'deprecated': False}, + 'gpl-2.0-or-later': {'id': 'GPL-2.0-or-later', 'deprecated': False}, + 'gpl-2.0-with-autoconf-exception': {'id': 'GPL-2.0-with-autoconf-exception', 'deprecated': True}, + 'gpl-2.0-with-bison-exception': {'id': 'GPL-2.0-with-bison-exception', 'deprecated': True}, + 'gpl-2.0-with-classpath-exception': {'id': 'GPL-2.0-with-classpath-exception', 'deprecated': True}, + 'gpl-2.0-with-font-exception': {'id': 'GPL-2.0-with-font-exception', 'deprecated': True}, + 'gpl-2.0-with-gcc-exception': {'id': 'GPL-2.0-with-GCC-exception', 'deprecated': True}, + 'gpl-3.0': {'id': 'GPL-3.0', 'deprecated': True}, + 'gpl-3.0+': {'id': 'GPL-3.0+', 'deprecated': True}, + 'gpl-3.0-only': {'id': 'GPL-3.0-only', 'deprecated': False}, + 'gpl-3.0-or-later': {'id': 'GPL-3.0-or-later', 'deprecated': False}, + 'gpl-3.0-with-autoconf-exception': {'id': 'GPL-3.0-with-autoconf-exception', 'deprecated': True}, + 'gpl-3.0-with-gcc-exception': {'id': 'GPL-3.0-with-GCC-exception', 'deprecated': True}, + 'graphics-gems': {'id': 'Graphics-Gems', 'deprecated': False}, + 'gsoap-1.3b': {'id': 'gSOAP-1.3b', 'deprecated': False}, + 'gtkbook': {'id': 'gtkbook', 'deprecated': False}, + 'gutmann': {'id': 'Gutmann', 'deprecated': False}, + 'haskellreport': {'id': 'HaskellReport', 'deprecated': False}, + 'hdf5': {'id': 'HDF5', 'deprecated': False}, + 'hdparm': {'id': 'hdparm', 'deprecated': False}, + 'hidapi': {'id': 'HIDAPI', 'deprecated': False}, + 'hippocratic-2.1': {'id': 'Hippocratic-2.1', 'deprecated': False}, + 'hp-1986': {'id': 'HP-1986', 'deprecated': False}, + 'hp-1989': {'id': 'HP-1989', 'deprecated': False}, + 'hpnd': {'id': 'HPND', 'deprecated': False}, + 'hpnd-dec': {'id': 'HPND-DEC', 'deprecated': False}, + 'hpnd-doc': {'id': 'HPND-doc', 'deprecated': False}, + 'hpnd-doc-sell': {'id': 'HPND-doc-sell', 'deprecated': False}, + 'hpnd-export-us': {'id': 'HPND-export-US', 'deprecated': False}, + 'hpnd-export-us-acknowledgement': {'id': 'HPND-export-US-acknowledgement', 'deprecated': False}, + 'hpnd-export-us-modify': {'id': 'HPND-export-US-modify', 'deprecated': False}, + 'hpnd-export2-us': {'id': 'HPND-export2-US', 'deprecated': False}, + 'hpnd-fenneberg-livingston': {'id': 'HPND-Fenneberg-Livingston', 'deprecated': False}, + 'hpnd-inria-imag': {'id': 'HPND-INRIA-IMAG', 'deprecated': False}, + 'hpnd-intel': {'id': 'HPND-Intel', 'deprecated': False}, + 'hpnd-kevlin-henney': {'id': 'HPND-Kevlin-Henney', 'deprecated': False}, + 'hpnd-markus-kuhn': {'id': 'HPND-Markus-Kuhn', 'deprecated': False}, + 'hpnd-merchantability-variant': {'id': 'HPND-merchantability-variant', 'deprecated': False}, + 'hpnd-mit-disclaimer': {'id': 'HPND-MIT-disclaimer', 'deprecated': False}, + 'hpnd-netrek': {'id': 'HPND-Netrek', 'deprecated': False}, + 'hpnd-pbmplus': {'id': 'HPND-Pbmplus', 'deprecated': False}, + 'hpnd-sell-mit-disclaimer-xserver': {'id': 'HPND-sell-MIT-disclaimer-xserver', 'deprecated': False}, + 'hpnd-sell-regexpr': {'id': 'HPND-sell-regexpr', 'deprecated': False}, + 'hpnd-sell-variant': {'id': 'HPND-sell-variant', 'deprecated': False}, + 'hpnd-sell-variant-mit-disclaimer': {'id': 'HPND-sell-variant-MIT-disclaimer', 'deprecated': False}, + 'hpnd-sell-variant-mit-disclaimer-rev': {'id': 'HPND-sell-variant-MIT-disclaimer-rev', 'deprecated': False}, + 'hpnd-uc': {'id': 'HPND-UC', 'deprecated': False}, + 'hpnd-uc-export-us': {'id': 'HPND-UC-export-US', 'deprecated': False}, + 'htmltidy': {'id': 'HTMLTIDY', 'deprecated': False}, + 'ibm-pibs': {'id': 'IBM-pibs', 'deprecated': False}, + 'icu': {'id': 'ICU', 'deprecated': False}, + 'iec-code-components-eula': {'id': 'IEC-Code-Components-EULA', 'deprecated': False}, + 'ijg': {'id': 'IJG', 'deprecated': False}, + 'ijg-short': {'id': 'IJG-short', 'deprecated': False}, + 'imagemagick': {'id': 'ImageMagick', 'deprecated': False}, + 'imatix': {'id': 'iMatix', 'deprecated': False}, + 'imlib2': {'id': 'Imlib2', 'deprecated': False}, + 'info-zip': {'id': 'Info-ZIP', 'deprecated': False}, + 'inner-net-2.0': {'id': 'Inner-Net-2.0', 'deprecated': False}, + 'innosetup': {'id': 'InnoSetup', 'deprecated': False}, + 'intel': {'id': 'Intel', 'deprecated': False}, + 'intel-acpi': {'id': 'Intel-ACPI', 'deprecated': False}, + 'interbase-1.0': {'id': 'Interbase-1.0', 'deprecated': False}, + 'ipa': {'id': 'IPA', 'deprecated': False}, + 'ipl-1.0': {'id': 'IPL-1.0', 'deprecated': False}, + 'isc': {'id': 'ISC', 'deprecated': False}, + 'isc-veillard': {'id': 'ISC-Veillard', 'deprecated': False}, + 'jam': {'id': 'Jam', 'deprecated': False}, + 'jasper-2.0': {'id': 'JasPer-2.0', 'deprecated': False}, + 'jove': {'id': 'jove', 'deprecated': False}, + 'jpl-image': {'id': 'JPL-image', 'deprecated': False}, + 'jpnic': {'id': 'JPNIC', 'deprecated': False}, + 'json': {'id': 'JSON', 'deprecated': False}, + 'kastrup': {'id': 'Kastrup', 'deprecated': False}, + 'kazlib': {'id': 'Kazlib', 'deprecated': False}, + 'knuth-ctan': {'id': 'Knuth-CTAN', 'deprecated': False}, + 'lal-1.2': {'id': 'LAL-1.2', 'deprecated': False}, + 'lal-1.3': {'id': 'LAL-1.3', 'deprecated': False}, + 'latex2e': {'id': 'Latex2e', 'deprecated': False}, + 'latex2e-translated-notice': {'id': 'Latex2e-translated-notice', 'deprecated': False}, + 'leptonica': {'id': 'Leptonica', 'deprecated': False}, + 'lgpl-2.0': {'id': 'LGPL-2.0', 'deprecated': True}, + 'lgpl-2.0+': {'id': 'LGPL-2.0+', 'deprecated': True}, + 'lgpl-2.0-only': {'id': 'LGPL-2.0-only', 'deprecated': False}, + 'lgpl-2.0-or-later': {'id': 'LGPL-2.0-or-later', 'deprecated': False}, + 'lgpl-2.1': {'id': 'LGPL-2.1', 'deprecated': True}, + 'lgpl-2.1+': {'id': 'LGPL-2.1+', 'deprecated': True}, + 'lgpl-2.1-only': {'id': 'LGPL-2.1-only', 'deprecated': False}, + 'lgpl-2.1-or-later': {'id': 'LGPL-2.1-or-later', 'deprecated': False}, + 'lgpl-3.0': {'id': 'LGPL-3.0', 'deprecated': True}, + 'lgpl-3.0+': {'id': 'LGPL-3.0+', 'deprecated': True}, + 'lgpl-3.0-only': {'id': 'LGPL-3.0-only', 'deprecated': False}, + 'lgpl-3.0-or-later': {'id': 'LGPL-3.0-or-later', 'deprecated': False}, + 'lgpllr': {'id': 'LGPLLR', 'deprecated': False}, + 'libpng': {'id': 'Libpng', 'deprecated': False}, + 'libpng-1.6.35': {'id': 'libpng-1.6.35', 'deprecated': False}, + 'libpng-2.0': {'id': 'libpng-2.0', 'deprecated': False}, + 'libselinux-1.0': {'id': 'libselinux-1.0', 'deprecated': False}, + 'libtiff': {'id': 'libtiff', 'deprecated': False}, + 'libutil-david-nugent': {'id': 'libutil-David-Nugent', 'deprecated': False}, + 'liliq-p-1.1': {'id': 'LiLiQ-P-1.1', 'deprecated': False}, + 'liliq-r-1.1': {'id': 'LiLiQ-R-1.1', 'deprecated': False}, + 'liliq-rplus-1.1': {'id': 'LiLiQ-Rplus-1.1', 'deprecated': False}, + 'linux-man-pages-1-para': {'id': 'Linux-man-pages-1-para', 'deprecated': False}, + 'linux-man-pages-copyleft': {'id': 'Linux-man-pages-copyleft', 'deprecated': False}, + 'linux-man-pages-copyleft-2-para': {'id': 'Linux-man-pages-copyleft-2-para', 'deprecated': False}, + 'linux-man-pages-copyleft-var': {'id': 'Linux-man-pages-copyleft-var', 'deprecated': False}, + 'linux-openib': {'id': 'Linux-OpenIB', 'deprecated': False}, + 'loop': {'id': 'LOOP', 'deprecated': False}, + 'lpd-document': {'id': 'LPD-document', 'deprecated': False}, + 'lpl-1.0': {'id': 'LPL-1.0', 'deprecated': False}, + 'lpl-1.02': {'id': 'LPL-1.02', 'deprecated': False}, + 'lppl-1.0': {'id': 'LPPL-1.0', 'deprecated': False}, + 'lppl-1.1': {'id': 'LPPL-1.1', 'deprecated': False}, + 'lppl-1.2': {'id': 'LPPL-1.2', 'deprecated': False}, + 'lppl-1.3a': {'id': 'LPPL-1.3a', 'deprecated': False}, + 'lppl-1.3c': {'id': 'LPPL-1.3c', 'deprecated': False}, + 'lsof': {'id': 'lsof', 'deprecated': False}, + 'lucida-bitmap-fonts': {'id': 'Lucida-Bitmap-Fonts', 'deprecated': False}, + 'lzma-sdk-9.11-to-9.20': {'id': 'LZMA-SDK-9.11-to-9.20', 'deprecated': False}, + 'lzma-sdk-9.22': {'id': 'LZMA-SDK-9.22', 'deprecated': False}, + 'mackerras-3-clause': {'id': 'Mackerras-3-Clause', 'deprecated': False}, + 'mackerras-3-clause-acknowledgment': {'id': 'Mackerras-3-Clause-acknowledgment', 'deprecated': False}, + 'magaz': {'id': 'magaz', 'deprecated': False}, + 'mailprio': {'id': 'mailprio', 'deprecated': False}, + 'makeindex': {'id': 'MakeIndex', 'deprecated': False}, + 'man2html': {'id': 'man2html', 'deprecated': False}, + 'martin-birgmeier': {'id': 'Martin-Birgmeier', 'deprecated': False}, + 'mcphee-slideshow': {'id': 'McPhee-slideshow', 'deprecated': False}, + 'metamail': {'id': 'metamail', 'deprecated': False}, + 'minpack': {'id': 'Minpack', 'deprecated': False}, + 'mips': {'id': 'MIPS', 'deprecated': False}, + 'miros': {'id': 'MirOS', 'deprecated': False}, + 'mit': {'id': 'MIT', 'deprecated': False}, + 'mit-0': {'id': 'MIT-0', 'deprecated': False}, + 'mit-advertising': {'id': 'MIT-advertising', 'deprecated': False}, + 'mit-click': {'id': 'MIT-Click', 'deprecated': False}, + 'mit-cmu': {'id': 'MIT-CMU', 'deprecated': False}, + 'mit-enna': {'id': 'MIT-enna', 'deprecated': False}, + 'mit-feh': {'id': 'MIT-feh', 'deprecated': False}, + 'mit-festival': {'id': 'MIT-Festival', 'deprecated': False}, + 'mit-khronos-old': {'id': 'MIT-Khronos-old', 'deprecated': False}, + 'mit-modern-variant': {'id': 'MIT-Modern-Variant', 'deprecated': False}, + 'mit-open-group': {'id': 'MIT-open-group', 'deprecated': False}, + 'mit-testregex': {'id': 'MIT-testregex', 'deprecated': False}, + 'mit-wu': {'id': 'MIT-Wu', 'deprecated': False}, + 'mitnfa': {'id': 'MITNFA', 'deprecated': False}, + 'mmixware': {'id': 'MMIXware', 'deprecated': False}, + 'motosoto': {'id': 'Motosoto', 'deprecated': False}, + 'mpeg-ssg': {'id': 'MPEG-SSG', 'deprecated': False}, + 'mpi-permissive': {'id': 'mpi-permissive', 'deprecated': False}, + 'mpich2': {'id': 'mpich2', 'deprecated': False}, + 'mpl-1.0': {'id': 'MPL-1.0', 'deprecated': False}, + 'mpl-1.1': {'id': 'MPL-1.1', 'deprecated': False}, + 'mpl-2.0': {'id': 'MPL-2.0', 'deprecated': False}, + 'mpl-2.0-no-copyleft-exception': {'id': 'MPL-2.0-no-copyleft-exception', 'deprecated': False}, + 'mplus': {'id': 'mplus', 'deprecated': False}, + 'ms-lpl': {'id': 'MS-LPL', 'deprecated': False}, + 'ms-pl': {'id': 'MS-PL', 'deprecated': False}, + 'ms-rl': {'id': 'MS-RL', 'deprecated': False}, + 'mtll': {'id': 'MTLL', 'deprecated': False}, + 'mulanpsl-1.0': {'id': 'MulanPSL-1.0', 'deprecated': False}, + 'mulanpsl-2.0': {'id': 'MulanPSL-2.0', 'deprecated': False}, + 'multics': {'id': 'Multics', 'deprecated': False}, + 'mup': {'id': 'Mup', 'deprecated': False}, + 'naist-2003': {'id': 'NAIST-2003', 'deprecated': False}, + 'nasa-1.3': {'id': 'NASA-1.3', 'deprecated': False}, + 'naumen': {'id': 'Naumen', 'deprecated': False}, + 'nbpl-1.0': {'id': 'NBPL-1.0', 'deprecated': False}, + 'ncbi-pd': {'id': 'NCBI-PD', 'deprecated': False}, + 'ncgl-uk-2.0': {'id': 'NCGL-UK-2.0', 'deprecated': False}, + 'ncl': {'id': 'NCL', 'deprecated': False}, + 'ncsa': {'id': 'NCSA', 'deprecated': False}, + 'net-snmp': {'id': 'Net-SNMP', 'deprecated': True}, + 'netcdf': {'id': 'NetCDF', 'deprecated': False}, + 'newsletr': {'id': 'Newsletr', 'deprecated': False}, + 'ngpl': {'id': 'NGPL', 'deprecated': False}, + 'ngrep': {'id': 'ngrep', 'deprecated': False}, + 'nicta-1.0': {'id': 'NICTA-1.0', 'deprecated': False}, + 'nist-pd': {'id': 'NIST-PD', 'deprecated': False}, + 'nist-pd-fallback': {'id': 'NIST-PD-fallback', 'deprecated': False}, + 'nist-software': {'id': 'NIST-Software', 'deprecated': False}, + 'nlod-1.0': {'id': 'NLOD-1.0', 'deprecated': False}, + 'nlod-2.0': {'id': 'NLOD-2.0', 'deprecated': False}, + 'nlpl': {'id': 'NLPL', 'deprecated': False}, + 'nokia': {'id': 'Nokia', 'deprecated': False}, + 'nosl': {'id': 'NOSL', 'deprecated': False}, + 'noweb': {'id': 'Noweb', 'deprecated': False}, + 'npl-1.0': {'id': 'NPL-1.0', 'deprecated': False}, + 'npl-1.1': {'id': 'NPL-1.1', 'deprecated': False}, + 'nposl-3.0': {'id': 'NPOSL-3.0', 'deprecated': False}, + 'nrl': {'id': 'NRL', 'deprecated': False}, + 'ntia-pd': {'id': 'NTIA-PD', 'deprecated': False}, + 'ntp': {'id': 'NTP', 'deprecated': False}, + 'ntp-0': {'id': 'NTP-0', 'deprecated': False}, + 'nunit': {'id': 'Nunit', 'deprecated': True}, + 'o-uda-1.0': {'id': 'O-UDA-1.0', 'deprecated': False}, + 'oar': {'id': 'OAR', 'deprecated': False}, + 'occt-pl': {'id': 'OCCT-PL', 'deprecated': False}, + 'oclc-2.0': {'id': 'OCLC-2.0', 'deprecated': False}, + 'odbl-1.0': {'id': 'ODbL-1.0', 'deprecated': False}, + 'odc-by-1.0': {'id': 'ODC-By-1.0', 'deprecated': False}, + 'offis': {'id': 'OFFIS', 'deprecated': False}, + 'ofl-1.0': {'id': 'OFL-1.0', 'deprecated': False}, + 'ofl-1.0-no-rfn': {'id': 'OFL-1.0-no-RFN', 'deprecated': False}, + 'ofl-1.0-rfn': {'id': 'OFL-1.0-RFN', 'deprecated': False}, + 'ofl-1.1': {'id': 'OFL-1.1', 'deprecated': False}, + 'ofl-1.1-no-rfn': {'id': 'OFL-1.1-no-RFN', 'deprecated': False}, + 'ofl-1.1-rfn': {'id': 'OFL-1.1-RFN', 'deprecated': False}, + 'ogc-1.0': {'id': 'OGC-1.0', 'deprecated': False}, + 'ogdl-taiwan-1.0': {'id': 'OGDL-Taiwan-1.0', 'deprecated': False}, + 'ogl-canada-2.0': {'id': 'OGL-Canada-2.0', 'deprecated': False}, + 'ogl-uk-1.0': {'id': 'OGL-UK-1.0', 'deprecated': False}, + 'ogl-uk-2.0': {'id': 'OGL-UK-2.0', 'deprecated': False}, + 'ogl-uk-3.0': {'id': 'OGL-UK-3.0', 'deprecated': False}, + 'ogtsl': {'id': 'OGTSL', 'deprecated': False}, + 'oldap-1.1': {'id': 'OLDAP-1.1', 'deprecated': False}, + 'oldap-1.2': {'id': 'OLDAP-1.2', 'deprecated': False}, + 'oldap-1.3': {'id': 'OLDAP-1.3', 'deprecated': False}, + 'oldap-1.4': {'id': 'OLDAP-1.4', 'deprecated': False}, + 'oldap-2.0': {'id': 'OLDAP-2.0', 'deprecated': False}, + 'oldap-2.0.1': {'id': 'OLDAP-2.0.1', 'deprecated': False}, + 'oldap-2.1': {'id': 'OLDAP-2.1', 'deprecated': False}, + 'oldap-2.2': {'id': 'OLDAP-2.2', 'deprecated': False}, + 'oldap-2.2.1': {'id': 'OLDAP-2.2.1', 'deprecated': False}, + 'oldap-2.2.2': {'id': 'OLDAP-2.2.2', 'deprecated': False}, + 'oldap-2.3': {'id': 'OLDAP-2.3', 'deprecated': False}, + 'oldap-2.4': {'id': 'OLDAP-2.4', 'deprecated': False}, + 'oldap-2.5': {'id': 'OLDAP-2.5', 'deprecated': False}, + 'oldap-2.6': {'id': 'OLDAP-2.6', 'deprecated': False}, + 'oldap-2.7': {'id': 'OLDAP-2.7', 'deprecated': False}, + 'oldap-2.8': {'id': 'OLDAP-2.8', 'deprecated': False}, + 'olfl-1.3': {'id': 'OLFL-1.3', 'deprecated': False}, + 'oml': {'id': 'OML', 'deprecated': False}, + 'openpbs-2.3': {'id': 'OpenPBS-2.3', 'deprecated': False}, + 'openssl': {'id': 'OpenSSL', 'deprecated': False}, + 'openssl-standalone': {'id': 'OpenSSL-standalone', 'deprecated': False}, + 'openvision': {'id': 'OpenVision', 'deprecated': False}, + 'opl-1.0': {'id': 'OPL-1.0', 'deprecated': False}, + 'opl-uk-3.0': {'id': 'OPL-UK-3.0', 'deprecated': False}, + 'opubl-1.0': {'id': 'OPUBL-1.0', 'deprecated': False}, + 'oset-pl-2.1': {'id': 'OSET-PL-2.1', 'deprecated': False}, + 'osl-1.0': {'id': 'OSL-1.0', 'deprecated': False}, + 'osl-1.1': {'id': 'OSL-1.1', 'deprecated': False}, + 'osl-2.0': {'id': 'OSL-2.0', 'deprecated': False}, + 'osl-2.1': {'id': 'OSL-2.1', 'deprecated': False}, + 'osl-3.0': {'id': 'OSL-3.0', 'deprecated': False}, + 'padl': {'id': 'PADL', 'deprecated': False}, + 'parity-6.0.0': {'id': 'Parity-6.0.0', 'deprecated': False}, + 'parity-7.0.0': {'id': 'Parity-7.0.0', 'deprecated': False}, + 'pddl-1.0': {'id': 'PDDL-1.0', 'deprecated': False}, + 'php-3.0': {'id': 'PHP-3.0', 'deprecated': False}, + 'php-3.01': {'id': 'PHP-3.01', 'deprecated': False}, + 'pixar': {'id': 'Pixar', 'deprecated': False}, + 'pkgconf': {'id': 'pkgconf', 'deprecated': False}, + 'plexus': {'id': 'Plexus', 'deprecated': False}, + 'pnmstitch': {'id': 'pnmstitch', 'deprecated': False}, + 'polyform-noncommercial-1.0.0': {'id': 'PolyForm-Noncommercial-1.0.0', 'deprecated': False}, + 'polyform-small-business-1.0.0': {'id': 'PolyForm-Small-Business-1.0.0', 'deprecated': False}, + 'postgresql': {'id': 'PostgreSQL', 'deprecated': False}, + 'ppl': {'id': 'PPL', 'deprecated': False}, + 'psf-2.0': {'id': 'PSF-2.0', 'deprecated': False}, + 'psfrag': {'id': 'psfrag', 'deprecated': False}, + 'psutils': {'id': 'psutils', 'deprecated': False}, + 'python-2.0': {'id': 'Python-2.0', 'deprecated': False}, + 'python-2.0.1': {'id': 'Python-2.0.1', 'deprecated': False}, + 'python-ldap': {'id': 'python-ldap', 'deprecated': False}, + 'qhull': {'id': 'Qhull', 'deprecated': False}, + 'qpl-1.0': {'id': 'QPL-1.0', 'deprecated': False}, + 'qpl-1.0-inria-2004': {'id': 'QPL-1.0-INRIA-2004', 'deprecated': False}, + 'radvd': {'id': 'radvd', 'deprecated': False}, + 'rdisc': {'id': 'Rdisc', 'deprecated': False}, + 'rhecos-1.1': {'id': 'RHeCos-1.1', 'deprecated': False}, + 'rpl-1.1': {'id': 'RPL-1.1', 'deprecated': False}, + 'rpl-1.5': {'id': 'RPL-1.5', 'deprecated': False}, + 'rpsl-1.0': {'id': 'RPSL-1.0', 'deprecated': False}, + 'rsa-md': {'id': 'RSA-MD', 'deprecated': False}, + 'rscpl': {'id': 'RSCPL', 'deprecated': False}, + 'ruby': {'id': 'Ruby', 'deprecated': False}, + 'ruby-pty': {'id': 'Ruby-pty', 'deprecated': False}, + 'sax-pd': {'id': 'SAX-PD', 'deprecated': False}, + 'sax-pd-2.0': {'id': 'SAX-PD-2.0', 'deprecated': False}, + 'saxpath': {'id': 'Saxpath', 'deprecated': False}, + 'scea': {'id': 'SCEA', 'deprecated': False}, + 'schemereport': {'id': 'SchemeReport', 'deprecated': False}, + 'sendmail': {'id': 'Sendmail', 'deprecated': False}, + 'sendmail-8.23': {'id': 'Sendmail-8.23', 'deprecated': False}, + 'sendmail-open-source-1.1': {'id': 'Sendmail-Open-Source-1.1', 'deprecated': False}, + 'sgi-b-1.0': {'id': 'SGI-B-1.0', 'deprecated': False}, + 'sgi-b-1.1': {'id': 'SGI-B-1.1', 'deprecated': False}, + 'sgi-b-2.0': {'id': 'SGI-B-2.0', 'deprecated': False}, + 'sgi-opengl': {'id': 'SGI-OpenGL', 'deprecated': False}, + 'sgp4': {'id': 'SGP4', 'deprecated': False}, + 'shl-0.5': {'id': 'SHL-0.5', 'deprecated': False}, + 'shl-0.51': {'id': 'SHL-0.51', 'deprecated': False}, + 'simpl-2.0': {'id': 'SimPL-2.0', 'deprecated': False}, + 'sissl': {'id': 'SISSL', 'deprecated': False}, + 'sissl-1.2': {'id': 'SISSL-1.2', 'deprecated': False}, + 'sl': {'id': 'SL', 'deprecated': False}, + 'sleepycat': {'id': 'Sleepycat', 'deprecated': False}, + 'smail-gpl': {'id': 'SMAIL-GPL', 'deprecated': False}, + 'smlnj': {'id': 'SMLNJ', 'deprecated': False}, + 'smppl': {'id': 'SMPPL', 'deprecated': False}, + 'snia': {'id': 'SNIA', 'deprecated': False}, + 'snprintf': {'id': 'snprintf', 'deprecated': False}, + 'sofa': {'id': 'SOFA', 'deprecated': False}, + 'softsurfer': {'id': 'softSurfer', 'deprecated': False}, + 'soundex': {'id': 'Soundex', 'deprecated': False}, + 'spencer-86': {'id': 'Spencer-86', 'deprecated': False}, + 'spencer-94': {'id': 'Spencer-94', 'deprecated': False}, + 'spencer-99': {'id': 'Spencer-99', 'deprecated': False}, + 'spl-1.0': {'id': 'SPL-1.0', 'deprecated': False}, + 'ssh-keyscan': {'id': 'ssh-keyscan', 'deprecated': False}, + 'ssh-openssh': {'id': 'SSH-OpenSSH', 'deprecated': False}, + 'ssh-short': {'id': 'SSH-short', 'deprecated': False}, + 'ssleay-standalone': {'id': 'SSLeay-standalone', 'deprecated': False}, + 'sspl-1.0': {'id': 'SSPL-1.0', 'deprecated': False}, + 'standardml-nj': {'id': 'StandardML-NJ', 'deprecated': True}, + 'sugarcrm-1.1.3': {'id': 'SugarCRM-1.1.3', 'deprecated': False}, + 'sul-1.0': {'id': 'SUL-1.0', 'deprecated': False}, + 'sun-ppp': {'id': 'Sun-PPP', 'deprecated': False}, + 'sun-ppp-2000': {'id': 'Sun-PPP-2000', 'deprecated': False}, + 'sunpro': {'id': 'SunPro', 'deprecated': False}, + 'swl': {'id': 'SWL', 'deprecated': False}, + 'swrule': {'id': 'swrule', 'deprecated': False}, + 'symlinks': {'id': 'Symlinks', 'deprecated': False}, + 'tapr-ohl-1.0': {'id': 'TAPR-OHL-1.0', 'deprecated': False}, + 'tcl': {'id': 'TCL', 'deprecated': False}, + 'tcp-wrappers': {'id': 'TCP-wrappers', 'deprecated': False}, + 'termreadkey': {'id': 'TermReadKey', 'deprecated': False}, + 'tgppl-1.0': {'id': 'TGPPL-1.0', 'deprecated': False}, + 'thirdeye': {'id': 'ThirdEye', 'deprecated': False}, + 'threeparttable': {'id': 'threeparttable', 'deprecated': False}, + 'tmate': {'id': 'TMate', 'deprecated': False}, + 'torque-1.1': {'id': 'TORQUE-1.1', 'deprecated': False}, + 'tosl': {'id': 'TOSL', 'deprecated': False}, + 'tpdl': {'id': 'TPDL', 'deprecated': False}, + 'tpl-1.0': {'id': 'TPL-1.0', 'deprecated': False}, + 'trustedqsl': {'id': 'TrustedQSL', 'deprecated': False}, + 'ttwl': {'id': 'TTWL', 'deprecated': False}, + 'ttyp0': {'id': 'TTYP0', 'deprecated': False}, + 'tu-berlin-1.0': {'id': 'TU-Berlin-1.0', 'deprecated': False}, + 'tu-berlin-2.0': {'id': 'TU-Berlin-2.0', 'deprecated': False}, + 'ubuntu-font-1.0': {'id': 'Ubuntu-font-1.0', 'deprecated': False}, + 'ucar': {'id': 'UCAR', 'deprecated': False}, + 'ucl-1.0': {'id': 'UCL-1.0', 'deprecated': False}, + 'ulem': {'id': 'ulem', 'deprecated': False}, + 'umich-merit': {'id': 'UMich-Merit', 'deprecated': False}, + 'unicode-3.0': {'id': 'Unicode-3.0', 'deprecated': False}, + 'unicode-dfs-2015': {'id': 'Unicode-DFS-2015', 'deprecated': False}, + 'unicode-dfs-2016': {'id': 'Unicode-DFS-2016', 'deprecated': False}, + 'unicode-tou': {'id': 'Unicode-TOU', 'deprecated': False}, + 'unixcrypt': {'id': 'UnixCrypt', 'deprecated': False}, + 'unlicense': {'id': 'Unlicense', 'deprecated': False}, + 'unlicense-libtelnet': {'id': 'Unlicense-libtelnet', 'deprecated': False}, + 'unlicense-libwhirlpool': {'id': 'Unlicense-libwhirlpool', 'deprecated': False}, + 'upl-1.0': {'id': 'UPL-1.0', 'deprecated': False}, + 'urt-rle': {'id': 'URT-RLE', 'deprecated': False}, + 'vim': {'id': 'Vim', 'deprecated': False}, + 'vostrom': {'id': 'VOSTROM', 'deprecated': False}, + 'vsl-1.0': {'id': 'VSL-1.0', 'deprecated': False}, + 'w3c': {'id': 'W3C', 'deprecated': False}, + 'w3c-19980720': {'id': 'W3C-19980720', 'deprecated': False}, + 'w3c-20150513': {'id': 'W3C-20150513', 'deprecated': False}, + 'w3m': {'id': 'w3m', 'deprecated': False}, + 'watcom-1.0': {'id': 'Watcom-1.0', 'deprecated': False}, + 'widget-workshop': {'id': 'Widget-Workshop', 'deprecated': False}, + 'wsuipa': {'id': 'Wsuipa', 'deprecated': False}, + 'wtfpl': {'id': 'WTFPL', 'deprecated': False}, + 'wwl': {'id': 'wwl', 'deprecated': False}, + 'wxwindows': {'id': 'wxWindows', 'deprecated': True}, + 'x11': {'id': 'X11', 'deprecated': False}, + 'x11-distribute-modifications-variant': {'id': 'X11-distribute-modifications-variant', 'deprecated': False}, + 'x11-swapped': {'id': 'X11-swapped', 'deprecated': False}, + 'xdebug-1.03': {'id': 'Xdebug-1.03', 'deprecated': False}, + 'xerox': {'id': 'Xerox', 'deprecated': False}, + 'xfig': {'id': 'Xfig', 'deprecated': False}, + 'xfree86-1.1': {'id': 'XFree86-1.1', 'deprecated': False}, + 'xinetd': {'id': 'xinetd', 'deprecated': False}, + 'xkeyboard-config-zinoviev': {'id': 'xkeyboard-config-Zinoviev', 'deprecated': False}, + 'xlock': {'id': 'xlock', 'deprecated': False}, + 'xnet': {'id': 'Xnet', 'deprecated': False}, + 'xpp': {'id': 'xpp', 'deprecated': False}, + 'xskat': {'id': 'XSkat', 'deprecated': False}, + 'xzoom': {'id': 'xzoom', 'deprecated': False}, + 'ypl-1.0': {'id': 'YPL-1.0', 'deprecated': False}, + 'ypl-1.1': {'id': 'YPL-1.1', 'deprecated': False}, + 'zed': {'id': 'Zed', 'deprecated': False}, + 'zeeff': {'id': 'Zeeff', 'deprecated': False}, + 'zend-2.0': {'id': 'Zend-2.0', 'deprecated': False}, + 'zimbra-1.3': {'id': 'Zimbra-1.3', 'deprecated': False}, + 'zimbra-1.4': {'id': 'Zimbra-1.4', 'deprecated': False}, + 'zlib': {'id': 'Zlib', 'deprecated': False}, + 'zlib-acknowledgement': {'id': 'zlib-acknowledgement', 'deprecated': False}, + 'zpl-1.1': {'id': 'ZPL-1.1', 'deprecated': False}, + 'zpl-2.0': {'id': 'ZPL-2.0', 'deprecated': False}, + 'zpl-2.1': {'id': 'ZPL-2.1', 'deprecated': False}, +} + +EXCEPTIONS: dict[str, SPDXException] = { + '389-exception': {'id': '389-exception', 'deprecated': False}, + 'asterisk-exception': {'id': 'Asterisk-exception', 'deprecated': False}, + 'asterisk-linking-protocols-exception': {'id': 'Asterisk-linking-protocols-exception', 'deprecated': False}, + 'autoconf-exception-2.0': {'id': 'Autoconf-exception-2.0', 'deprecated': False}, + 'autoconf-exception-3.0': {'id': 'Autoconf-exception-3.0', 'deprecated': False}, + 'autoconf-exception-generic': {'id': 'Autoconf-exception-generic', 'deprecated': False}, + 'autoconf-exception-generic-3.0': {'id': 'Autoconf-exception-generic-3.0', 'deprecated': False}, + 'autoconf-exception-macro': {'id': 'Autoconf-exception-macro', 'deprecated': False}, + 'bison-exception-1.24': {'id': 'Bison-exception-1.24', 'deprecated': False}, + 'bison-exception-2.2': {'id': 'Bison-exception-2.2', 'deprecated': False}, + 'bootloader-exception': {'id': 'Bootloader-exception', 'deprecated': False}, + 'cgal-linking-exception': {'id': 'CGAL-linking-exception', 'deprecated': False}, + 'classpath-exception-2.0': {'id': 'Classpath-exception-2.0', 'deprecated': False}, + 'clisp-exception-2.0': {'id': 'CLISP-exception-2.0', 'deprecated': False}, + 'cryptsetup-openssl-exception': {'id': 'cryptsetup-OpenSSL-exception', 'deprecated': False}, + 'digia-qt-lgpl-exception-1.1': {'id': 'Digia-Qt-LGPL-exception-1.1', 'deprecated': False}, + 'digirule-foss-exception': {'id': 'DigiRule-FOSS-exception', 'deprecated': False}, + 'ecos-exception-2.0': {'id': 'eCos-exception-2.0', 'deprecated': False}, + 'erlang-otp-linking-exception': {'id': 'erlang-otp-linking-exception', 'deprecated': False}, + 'fawkes-runtime-exception': {'id': 'Fawkes-Runtime-exception', 'deprecated': False}, + 'fltk-exception': {'id': 'FLTK-exception', 'deprecated': False}, + 'fmt-exception': {'id': 'fmt-exception', 'deprecated': False}, + 'font-exception-2.0': {'id': 'Font-exception-2.0', 'deprecated': False}, + 'freertos-exception-2.0': {'id': 'freertos-exception-2.0', 'deprecated': False}, + 'gcc-exception-2.0': {'id': 'GCC-exception-2.0', 'deprecated': False}, + 'gcc-exception-2.0-note': {'id': 'GCC-exception-2.0-note', 'deprecated': False}, + 'gcc-exception-3.1': {'id': 'GCC-exception-3.1', 'deprecated': False}, + 'gmsh-exception': {'id': 'Gmsh-exception', 'deprecated': False}, + 'gnat-exception': {'id': 'GNAT-exception', 'deprecated': False}, + 'gnome-examples-exception': {'id': 'GNOME-examples-exception', 'deprecated': False}, + 'gnu-compiler-exception': {'id': 'GNU-compiler-exception', 'deprecated': False}, + 'gnu-javamail-exception': {'id': 'gnu-javamail-exception', 'deprecated': False}, + 'gpl-3.0-389-ds-base-exception': {'id': 'GPL-3.0-389-ds-base-exception', 'deprecated': False}, + 'gpl-3.0-interface-exception': {'id': 'GPL-3.0-interface-exception', 'deprecated': False}, + 'gpl-3.0-linking-exception': {'id': 'GPL-3.0-linking-exception', 'deprecated': False}, + 'gpl-3.0-linking-source-exception': {'id': 'GPL-3.0-linking-source-exception', 'deprecated': False}, + 'gpl-cc-1.0': {'id': 'GPL-CC-1.0', 'deprecated': False}, + 'gstreamer-exception-2005': {'id': 'GStreamer-exception-2005', 'deprecated': False}, + 'gstreamer-exception-2008': {'id': 'GStreamer-exception-2008', 'deprecated': False}, + 'harbour-exception': {'id': 'harbour-exception', 'deprecated': False}, + 'i2p-gpl-java-exception': {'id': 'i2p-gpl-java-exception', 'deprecated': False}, + 'independent-modules-exception': {'id': 'Independent-modules-exception', 'deprecated': False}, + 'kicad-libraries-exception': {'id': 'KiCad-libraries-exception', 'deprecated': False}, + 'lgpl-3.0-linking-exception': {'id': 'LGPL-3.0-linking-exception', 'deprecated': False}, + 'libpri-openh323-exception': {'id': 'libpri-OpenH323-exception', 'deprecated': False}, + 'libtool-exception': {'id': 'Libtool-exception', 'deprecated': False}, + 'linux-syscall-note': {'id': 'Linux-syscall-note', 'deprecated': False}, + 'llgpl': {'id': 'LLGPL', 'deprecated': False}, + 'llvm-exception': {'id': 'LLVM-exception', 'deprecated': False}, + 'lzma-exception': {'id': 'LZMA-exception', 'deprecated': False}, + 'mif-exception': {'id': 'mif-exception', 'deprecated': False}, + 'mxml-exception': {'id': 'mxml-exception', 'deprecated': False}, + 'nokia-qt-exception-1.1': {'id': 'Nokia-Qt-exception-1.1', 'deprecated': True}, + 'ocaml-lgpl-linking-exception': {'id': 'OCaml-LGPL-linking-exception', 'deprecated': False}, + 'occt-exception-1.0': {'id': 'OCCT-exception-1.0', 'deprecated': False}, + 'openjdk-assembly-exception-1.0': {'id': 'OpenJDK-assembly-exception-1.0', 'deprecated': False}, + 'openvpn-openssl-exception': {'id': 'openvpn-openssl-exception', 'deprecated': False}, + 'pcre2-exception': {'id': 'PCRE2-exception', 'deprecated': False}, + 'polyparse-exception': {'id': 'polyparse-exception', 'deprecated': False}, + 'ps-or-pdf-font-exception-20170817': {'id': 'PS-or-PDF-font-exception-20170817', 'deprecated': False}, + 'qpl-1.0-inria-2004-exception': {'id': 'QPL-1.0-INRIA-2004-exception', 'deprecated': False}, + 'qt-gpl-exception-1.0': {'id': 'Qt-GPL-exception-1.0', 'deprecated': False}, + 'qt-lgpl-exception-1.1': {'id': 'Qt-LGPL-exception-1.1', 'deprecated': False}, + 'qwt-exception-1.0': {'id': 'Qwt-exception-1.0', 'deprecated': False}, + 'romic-exception': {'id': 'romic-exception', 'deprecated': False}, + 'rrdtool-floss-exception-2.0': {'id': 'RRDtool-FLOSS-exception-2.0', 'deprecated': False}, + 'sane-exception': {'id': 'SANE-exception', 'deprecated': False}, + 'shl-2.0': {'id': 'SHL-2.0', 'deprecated': False}, + 'shl-2.1': {'id': 'SHL-2.1', 'deprecated': False}, + 'stunnel-exception': {'id': 'stunnel-exception', 'deprecated': False}, + 'swi-exception': {'id': 'SWI-exception', 'deprecated': False}, + 'swift-exception': {'id': 'Swift-exception', 'deprecated': False}, + 'texinfo-exception': {'id': 'Texinfo-exception', 'deprecated': False}, + 'u-boot-exception-2.0': {'id': 'u-boot-exception-2.0', 'deprecated': False}, + 'ubdl-exception': {'id': 'UBDL-exception', 'deprecated': False}, + 'universal-foss-exception-1.0': {'id': 'Universal-FOSS-exception-1.0', 'deprecated': False}, + 'vsftpd-openssl-exception': {'id': 'vsftpd-openssl-exception', 'deprecated': False}, + 'wxwindows-exception-3.1': {'id': 'WxWindows-exception-3.1', 'deprecated': False}, + 'x11vnc-openssl-exception': {'id': 'x11vnc-openssl-exception', 'deprecated': False}, +} diff --git a/venv/lib/python3.12/site-packages/packaging/markers.py b/venv/lib/python3.12/site-packages/packaging/markers.py new file mode 100644 index 0000000..51b6036 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/markers.py @@ -0,0 +1,576 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import functools +import operator +import os +import platform +import sys +from collections.abc import Set as AbstractSet +from typing import TYPE_CHECKING, Callable, Literal, TypedDict, Union, cast + +from ._parser import MarkerAtom, MarkerList, Op, Value, Variable +from ._parser import parse_marker as _parse_marker +from ._tokenizer import ParserSyntaxError +from .specifiers import InvalidSpecifier, Specifier +from .utils import canonicalize_name + +if TYPE_CHECKING: + from collections.abc import Mapping + +__all__ = [ + "Environment", + "EvaluateContext", + "InvalidMarker", + "Marker", + "UndefinedComparison", + "UndefinedEnvironmentName", + "default_environment", +] + + +def __dir__() -> list[str]: + return __all__ + + +Operator = Callable[[str, Union[str, AbstractSet[str]]], bool] +EvaluateContext = Literal["metadata", "lock_file", "requirement"] +"""A ``typing.Literal`` enumerating valid marker evaluation contexts. + +Valid values for the ``context`` passed to :meth:`Marker.evaluate` are: + +* ``"metadata"`` (for core metadata; default) +* ``"lock_file"`` (for lock files) +* ``"requirement"`` (i.e. all other situations) + +.. versionadded:: 25.0 +""" + +MARKERS_ALLOWING_SET = {"extras", "dependency_groups"} +MARKERS_REQUIRING_VERSION = { + "implementation_version", + "platform_release", + "python_full_version", + "python_version", +} + + +class InvalidMarker(ValueError): + """Raised when attempting to create a :class:`Marker` from invalid input. + + This error indicates that the given marker string does not conform to the + :ref:`specification of dependency specifiers `. + """ + + +class UndefinedComparison(ValueError): + """Raised when evaluating an unsupported marker comparison. + + This can happen when marker values are compared as versions but do not + conform to the :ref:`specification of version specifiers + `. + """ + + +class UndefinedEnvironmentName(KeyError): + """Raised when evaluating a marker that references a missing environment key. + + Subclasses :class:`KeyError` so that code catching the bare ``KeyError`` that + a missing environment lookup historically produced keeps working. + + .. versionchanged:: 26.3 + Now subclasses :class:`KeyError` (was :class:`ValueError`) and is raised by + :meth:`Marker.evaluate` for missing environment keys, where a bare + ``KeyError`` was raised before. + """ + + +class Environment(TypedDict): + """ + A dictionary that represents a Python environment as captured by + :func:`default_environment`. All fields are required. + """ + + implementation_name: str + """The implementation's identifier, e.g. ``'cpython'``.""" + + implementation_version: str + """ + The implementation's version, e.g. ``'3.13.0a2'`` for CPython 3.13.0a2, or + ``'7.3.13'`` for PyPy3.10 v7.3.13. + """ + + os_name: str + """ + The value of :py:data:`os.name`. The name of the operating system dependent module + imported, e.g. ``'posix'``. + """ + + platform_machine: str + """ + Returns the machine type, e.g. ``'i386'``. + + An empty string if the value cannot be determined. + """ + + platform_release: str + """ + The system's release, e.g. ``'2.2.0'`` or ``'NT'``. + + An empty string if the value cannot be determined. + """ + + platform_system: str + """ + The system/OS name, e.g. ``'Linux'``, ``'Windows'`` or ``'Java'``. + + An empty string if the value cannot be determined. + """ + + platform_version: str + """ + The system's release version, e.g. ``'#3 on degas'``. + + An empty string if the value cannot be determined. + """ + + python_full_version: str + """ + The Python version as string ``'major.minor.patchlevel'``. + + Note that unlike the Python :py:data:`sys.version`, this value will always include + the patchlevel (it defaults to 0). + """ + + platform_python_implementation: str + """ + A string identifying the Python implementation, e.g. ``'CPython'``. + """ + + python_version: str + """The Python version as string ``'major.minor'``.""" + + sys_platform: str + """ + This string contains a platform identifier that can be used to append + platform-specific components to :py:data:`sys.path`, for instance. + + For Unix systems, except on Linux and AIX, this is the lowercased OS name as + returned by ``uname -s`` with the first part of the version as returned by + ``uname -r`` appended, e.g. ``'sunos5'`` or ``'freebsd8'``, at the time when Python + was built. + """ + + +def _normalize_extras( + result: MarkerList | MarkerAtom | str, +) -> MarkerList | MarkerAtom | str: + if isinstance(result, list): + return [_normalize_extras(r) for r in result] + if not isinstance(result, tuple): + return result + + lhs, op, rhs = result + if isinstance(lhs, Variable) and lhs.value == "extra" and isinstance(rhs, Value): + normalized_extra = canonicalize_name(rhs.value) + rhs = Value(normalized_extra) + elif isinstance(rhs, Variable) and rhs.value == "extra" and isinstance(lhs, Value): + normalized_extra = canonicalize_name(lhs.value) + lhs = Value(normalized_extra) + elif ( + isinstance(rhs, Variable) + and rhs.value in MARKERS_ALLOWING_SET + and isinstance(lhs, Value) + ): + # PEP 685 (extras) / PEP 735 (dependency_groups): the set-valued membership + # literal must also be normalized. evaluate() already canonicalizes both + # operands for these keys (see _normalize), so normalizing the literal at + # parse time keeps __str__/__eq__/__hash__ consistent with evaluate() -- e.g. + # Marker('"Foo" in extras') and Marker('"foo" in extras') must compare and + # hash equal (the membership variable is always the right-hand operand). + lhs = Value(canonicalize_name(lhs.value)) + return lhs, op, rhs + + +def _normalize_extra_values(results: MarkerList) -> MarkerList: + """ + Normalize extra values. + """ + + return [_normalize_extras(r) for r in results] + + +def _format_marker( + marker: list[str] | MarkerAtom | str, first: bool | None = True +) -> str: + assert isinstance(marker, (list, tuple, str)) + + # Unwrap a redundant [[...]] wrapper, but keep the nesting context so a + # nested group keeps the parentheses its and/or precedence needs. + if ( + isinstance(marker, list) + and len(marker) == 1 + and isinstance(marker[0], (list, tuple)) + ): + return _format_marker(marker[0], first=first) + + if isinstance(marker, list): + inner = (_format_marker(m, first=False) for m in marker) + if first: + return " ".join(inner) + else: + return "(" + " ".join(inner) + ")" + elif isinstance(marker, tuple): + return " ".join([m.serialize() for m in marker]) + else: + return marker + + +_operators: dict[str, Operator] = { + "in": lambda lhs, rhs: lhs in rhs, + "not in": lambda lhs, rhs: lhs not in rhs, + "<": lambda _lhs, _rhs: False, + "<=": operator.eq, + "==": operator.eq, + "!=": operator.ne, + ">=": operator.eq, + ">": lambda _lhs, _rhs: False, +} + + +def _eval_op(lhs: str, op: Op, rhs: str | AbstractSet[str], *, key: str) -> bool: + op_str = op.serialize() + if key in MARKERS_REQUIRING_VERSION: + try: + spec = Specifier(f"{op_str}{rhs}") + except InvalidSpecifier: + pass + else: + return spec.contains(lhs, prereleases=True) + + oper: Operator | None = _operators.get(op_str) + if oper is None: + raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") + + return oper(lhs, rhs) + + +def _normalize( + lhs: str, rhs: str | AbstractSet[str], key: str +) -> tuple[str, str | AbstractSet[str]]: + # PEP 685 - Comparison of extra names for optional distribution dependencies + # https://peps.python.org/pep-0685/ + # > When comparing extra names, tools MUST normalize the names being + # > compared using the semantics outlined in PEP 503 for names + if key == "extra": + assert isinstance(rhs, str), "extra value must be a string" + # Both sides are normalized at this point already + return (lhs, rhs) + if key in MARKERS_ALLOWING_SET: + if isinstance(rhs, str): # pragma: no cover + return (canonicalize_name(lhs), canonicalize_name(rhs)) + else: + return (canonicalize_name(lhs), {canonicalize_name(v) for v in rhs}) + + # other environment markers don't have such standards + return lhs, rhs + + +def _lookup_environment( + environment: dict[str, str | AbstractSet[str]], key: str +) -> str | AbstractSet[str]: + try: + return environment[key] + except KeyError: + raise UndefinedEnvironmentName(key) from None + + +def _evaluate_markers( + markers: MarkerList, environment: dict[str, str | AbstractSet[str]] +) -> bool: + groups: list[list[bool]] = [[]] + + for marker in markers: + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + environment_key = lhs.value + lhs_value = _lookup_environment(environment, environment_key) + rhs_value = rhs.value + else: + lhs_value = lhs.value + environment_key = rhs.value + rhs_value = _lookup_environment(environment, environment_key) + + if not isinstance(lhs_value, str): + raise UndefinedComparison( + f"Set-valued marker {environment_key!r} can only be used " + f'with the membership form (e.g. "" in ' + f"{environment_key}); it cannot appear on the left-hand " + f"side of {op.serialize()!r}." + ) + lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key) + groups[-1].append(_eval_op(lhs_value, op, rhs_value, key=environment_key)) + elif marker == "or": + groups.append([]) + elif marker == "and": + pass + else: # pragma: nocover + raise TypeError(f"Unexpected marker {marker!r}") + + return any(all(item) for item in groups) + + +def _format_full_version(info: sys._version_info) -> str: + version = f"{info.major}.{info.minor}.{info.micro}" + kind = info.releaselevel + if kind != "final": + version += kind[0] + str(info.serial) + return version + + +@functools.cache +def _cached_default_environment() -> Environment: + """Build the default marker environment for the current Python process. + + The values are derived from process-constant data (the running interpreter + and the host platform), so this is cached and built only once. The result is + shared between callers and must never be mutated; :func:`default_environment` + returns a fresh copy. + """ + iver = _format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + return { + "implementation_name": implementation_name, + "implementation_version": iver, + "os_name": os.name, + "platform_machine": platform.machine(), + "platform_release": platform.release(), + "platform_system": platform.system(), + "platform_version": platform.version(), + "python_full_version": platform.python_version(), + "platform_python_implementation": platform.python_implementation(), + "python_version": ".".join(platform.python_version_tuple()[:2]), + "sys_platform": sys.platform, + } + + +def default_environment() -> Environment: + """Return the default marker environment for the current Python process. + + This is the base environment used by :meth:`Marker.evaluate`. A fresh copy + is returned on every call so callers may freely mutate the result; a shallow + copy suffices because all values are immutable strings. + + .. versionchanged:: 26.3 + The environment is computed once per process and cached, since it is + derived from process-constant data. Patching ``platform``/``sys``/``os`` + after the first call has no effect; pass an explicit ``environment`` to + :meth:`Marker.evaluate` to evaluate against different values. + """ + return cast("Environment", dict(_cached_default_environment())) + + +class Marker: + """Represents a parsed dependency marker expression. + + Marker expressions are parsed according to the + :ref:`specification of dependency specifiers `. + + :param marker: The string representation of a marker expression. + :raises InvalidMarker: If ``marker`` cannot be parsed. + + Instances are safe to serialize with :mod:`pickle`. They use a stable + format so the same pickle can be loaded in future packaging releases. + + .. versionchanged:: 26.2 + + Added a stable pickle format. Pickles created with packaging 26.2+ can + be unpickled with future releases. Backward compatibility with pickles + from packaging < 26.2 is supported but may be removed in a future + release. + """ + + __slots__ = ("_markers",) + + def __init__(self, marker: str) -> None: + # Note: We create a Marker object without calling this constructor in + # packaging.requirements.Requirement. If any additional logic is + # added here, make sure to mirror/adapt Requirement. + + # If this fails and throws an error, the repr still expects _markers to + # be defined. + self._markers: MarkerList = [] + + try: + self._markers = _normalize_extra_values(_parse_marker(marker)) + # The attribute `_markers` can be described in terms of a recursive type: + # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]] + # + # For example, the following expression: + # python_version > "3.6" or (python_version == "3.6" and os_name == "unix") + # + # is parsed into: + # [ + # (, ')>, ), + # 'and', + # [ + # (, , ), + # 'or', + # (, , ) + # ] + # ] + except ParserSyntaxError as e: + raise InvalidMarker(str(e)) from e + + @classmethod + def _from_markers(cls, markers: MarkerList) -> Marker: + """Create a Marker instance from a pre-parsed marker tree. + + This avoids re-parsing serialised marker strings when combining markers. + """ + new = cls.__new__(cls) + new._markers = markers + return new + + def __str__(self) -> str: + return _format_marker(self._markers) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}({str(self)!r})>" + + def __hash__(self) -> int: + return hash(str(self)) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Marker): + return NotImplemented + + return str(self) == str(other) + + def __getstate__(self) -> str: + # Return the marker expression string for compactness and stability. + # Internal Node objects are excluded; the string is re-parsed on load. + return str(self) + + def __setstate__(self, state: object) -> None: + if isinstance(state, str): + # New format (26.2+): just the marker expression string. + try: + self._markers = _normalize_extra_values(_parse_marker(state)) + except ParserSyntaxError as exc: + raise TypeError(f"Cannot restore Marker from {state!r}") from exc + return + if isinstance(state, dict) and "_markers" in state: + # Old format (packaging <= 26.1, no __slots__): plain __dict__. + markers = state["_markers"] + if isinstance(markers, list): + self._markers = markers + return + if isinstance(state, tuple) and len(state) == 2: + # Old format (packaging <= 26.1, __slots__): (None, {slot: value}). + _, slot_dict = state + if isinstance(slot_dict, dict) and "_markers" in slot_dict: + markers = slot_dict["_markers"] + if isinstance(markers, list): + self._markers = markers + return + raise TypeError(f"Cannot restore Marker from {state!r}") + + def __and__(self, other: Marker) -> Marker: + """Combine this marker with another using ``and``. + + .. versionadded:: 26.1 + """ + if not isinstance(other, Marker): + return NotImplemented + return self._from_markers([self._markers, "and", other._markers]) + + def __or__(self, other: Marker) -> Marker: + """Combine this marker with another using ``or``. + + .. versionadded:: 26.1 + """ + if not isinstance(other, Marker): + return NotImplemented + return self._from_markers([self._markers, "or", other._markers]) + + def evaluate( + self, + environment: Mapping[str, str | AbstractSet[str]] | None = None, + context: EvaluateContext = "metadata", + ) -> bool: + """Evaluate a marker. + + Return the boolean from evaluating this marker against the environment. + The environment is determined from the current Python process unless + passed in explicitly. + + :param environment: Mapping containing keys and values to override the + detected environment. + :param EvaluateContext context: The context in which the marker is + evaluated, which influences what marker names are considered valid. + Accepted values are ``"metadata"`` (for core metadata; default), + ``"lock_file"``, and ``"requirement"`` (i.e. all other situations). + :raises UndefinedComparison: If the marker uses a comparison on values + that are not valid versions per the :ref:`specification of version + specifiers `. + :raises UndefinedEnvironmentName: If the marker references a value that + is missing from the evaluation environment. + :returns: ``True`` if the marker matches, otherwise ``False``. + + .. versionchanged:: 25.0 + Added the ``context`` parameter, which influences which marker names + are considered valid. + """ + current_environment = cast( + "dict[str, str | AbstractSet[str]]", default_environment() + ) + if context == "lock_file": + current_environment |= { + "extras": frozenset(), + "dependency_groups": frozenset(), + } + elif context == "metadata": + current_environment["extra"] = "" + + if environment is not None: + current_environment |= environment + if "extra" in current_environment: + # The API used to allow setting extra to None. We need to handle + # this case for backwards compatibility. Also skip running + # normalize name if extra is empty. + extra = cast("str | None", current_environment["extra"]) + current_environment["extra"] = canonicalize_name(extra) if extra else "" + + return _evaluate_markers( + self._markers, _repair_python_full_version(current_environment) + ) + + +def _pep440_python_full_version(python_full_version: str) -> str: + """ + Work around platform.python_version() returning something that is not PEP 440 + compliant for non-tagged Python builds. + """ + if python_full_version.endswith("+"): + return f"{python_full_version}local" + return python_full_version + + +def _repair_python_full_version( + env: dict[str, str | AbstractSet[str]], +) -> dict[str, str | AbstractSet[str]]: + """ + Work around platform.python_version() returning something that is not PEP 440 + compliant for non-tagged Python builds. + """ + python_full_version = cast("str", env["python_full_version"]) + env["python_full_version"] = _pep440_python_full_version(python_full_version) + return env diff --git a/venv/lib/python3.12/site-packages/packaging/metadata.py b/venv/lib/python3.12/site-packages/packaging/metadata.py new file mode 100644 index 0000000..1061105 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/metadata.py @@ -0,0 +1,1054 @@ +from __future__ import annotations + +import email.header +import email.message +import email.parser +import email.policy +import keyword +import pathlib +import re +import typing +from typing import ( + Any, + Callable, + Generic, + Literal, + TypedDict, + cast, +) + +from . import licenses, requirements, specifiers, utils +from . import version as version_module +from .errors import ExceptionGroup, _ErrorCollector + +if typing.TYPE_CHECKING: + from .licenses import NormalizedLicenseExpression + from .version import Version + +T = typing.TypeVar("T") + + +__all__ = [ + "ExceptionGroup", # Keep this for a bit (makes mypy happy w/ 26.0 compat) + "InvalidMetadata", + "Metadata", + "RFC822Message", + "RFC822Policy", + "RawMetadata", + "parse_email", +] + + +def __dir__() -> list[str]: + return __all__ + + +class InvalidMetadata(ValueError): + """A metadata field contains invalid data. + + .. versionadded:: 23.2 + """ + + field: str + """The name of the field that contains invalid data.""" + + def __init__(self, field: str, message: str) -> None: + self.field = field + super().__init__(message) + + # Support pickling; ``args`` does not match ``__init__``'s signature. + def __reduce__(self) -> tuple[type[InvalidMetadata], tuple[str, str]]: + return (self.__class__, (self.field, self.args[0])) + + +# The RawMetadata class attempts to make as few assumptions about the underlying +# serialization formats as possible. The idea is that as long as a serialization +# formats offer some very basic primitives in *some* way then we can support +# serializing to and from that format. +class RawMetadata(TypedDict, total=False): + """A dictionary of raw core metadata. + + Each field in core metadata maps to a key of this dictionary (when data is + provided). The key is lower-case and underscores are used instead of dashes + compared to the equivalent core metadata field. Any core metadata field that + can be specified multiple times or can hold multiple values in a single + field have a key with a plural name. See :class:`Metadata` whose attributes + match the keys of this dictionary. + + Core metadata fields that can be specified multiple times are stored as a + list or dict depending on which is appropriate for the field. Any fields + which hold multiple values in a single field are stored as a list. All fields + are considered optional. + """ + + # Metadata 1.0 - PEP 241 + metadata_version: str + name: str + version: str + platforms: list[str] + summary: str + description: str + keywords: list[str] + home_page: str + author: str + author_email: str + license: str + + # Metadata 1.1 - PEP 314 + supported_platforms: list[str] + download_url: str + classifiers: list[str] + requires: list[str] + provides: list[str] + obsoletes: list[str] + + # Metadata 1.2 - PEP 345 + maintainer: str + maintainer_email: str + requires_dist: list[str] + provides_dist: list[str] + obsoletes_dist: list[str] + requires_python: str + requires_external: list[str] + project_urls: dict[str, str] + + # Metadata 2.0 + # PEP 426 attempted to completely revamp the metadata format + # but got stuck without ever being able to build consensus on + # it and ultimately ended up withdrawn. + # + # However, a number of tools had started emitting METADATA with + # `2.0` Metadata-Version, so for historical reasons, this version + # was skipped. + + # Metadata 2.1 - PEP 566 + description_content_type: str + provides_extra: list[str] + + # Metadata 2.2 - PEP 643 + dynamic: list[str] + + # Metadata 2.3 - PEP 685 + # No new fields were added in PEP 685, just some edge case were + # tightened up to provide better interoperability. + + # Metadata 2.4 - PEP 639 + license_expression: str + """.. versionadded:: 24.2""" + license_files: list[str] + """.. versionadded:: 24.2""" + + # Metadata 2.5 - PEP 794 + import_names: list[str] + """.. versionadded:: 26.0""" + import_namespaces: list[str] + """.. versionadded:: 26.0""" + + # Metadata 2.6 - PEP 808 (no new fields, behavior change for Dynamic) + + +# 'keywords' is special as it's a string in the core metadata spec, but we +# represent it as a list. +_STRING_FIELDS = { + "author", + "author_email", + "description", + "description_content_type", + "download_url", + "home_page", + "license", + "license_expression", + "maintainer", + "maintainer_email", + "metadata_version", + "name", + "requires_python", + "summary", + "version", +} + +_LIST_FIELDS = { + "classifiers", + "dynamic", + "license_files", + "obsoletes", + "obsoletes_dist", + "platforms", + "provides", + "provides_dist", + "provides_extra", + "requires", + "requires_dist", + "requires_external", + "supported_platforms", + "import_names", + "import_namespaces", +} + +_DICT_FIELDS = { + "project_urls", +} + + +def _parse_keywords(data: str) -> list[str]: + """Split a string of comma-separated keywords into a list of keywords.""" + return [k.strip() for k in data.split(",")] + + +def _parse_project_urls(data: list[str]) -> dict[str, str]: + """Parse a list of label/URL string pairings separated by a comma.""" + urls = {} + for pair in data: + # Our logic is slightly tricky here as we want to try and do + # *something* reasonable with malformed data. + # + # The main thing that we have to worry about, is data that does + # not have a ',' at all to split the label from the Value. There + # isn't a singular right answer here, and we will fail validation + # later on (if the caller is validating) so it doesn't *really* + # matter, but since the missing value has to be an empty str + # and our return value is dict[str, str], if we let the key + # be the missing value, then they'd have multiple '' values that + # overwrite each other in a accumulating dict. + # + # The other potential issue is that it's possible to have the + # same label multiple times in the metadata, with no solid "right" + # answer with what to do in that case. As such, we'll do the only + # thing we can, which is treat the field as unparsable and add it + # to our list of unparsed fields. + # + # TODO: The spec doesn't say anything about if the keys should be + # considered case sensitive or not... logically they should + # be case-preserving and case-insensitive, but doing that + # would open up more cases where we might have duplicate + # entries. + label, _, url = (s.strip() for s in pair.partition(",")) + + if label in urls: + # The label already exists in our set of urls, so this field + # is unparsable, and we can just add the whole thing to our + # unparsable data and stop processing it. + raise KeyError("duplicate labels in project urls") + urls[label] = url + + return urls + + +def _get_payload(msg: email.message.Message, source: bytes | str) -> str: + """Get the body of the message.""" + # If our source is a str, then our caller has managed encodings for us, + # and we don't need to deal with it. + if isinstance(source, str): + payload = msg.get_payload() + # A multipart payload makes get_payload() return a list of messages + # rather than a str; route it to ``unparsed``. + if not isinstance(payload, str): + raise ValueError("payload is not a string") # noqa: TRY004 + return payload + # If our source is a bytes, then we're managing the encoding and we need + # to deal with it. + else: + bpayload = msg.get_payload(decode=True) + # A multipart payload makes get_payload(decode=True) return None; + # route it to ``unparsed``. + if not isinstance(bpayload, bytes): + raise ValueError("payload in an invalid encoding") # noqa: TRY004 + try: + return bpayload.decode("utf8", "strict") + except UnicodeDecodeError as exc: + raise ValueError("payload in an invalid encoding") from exc + + +# The various parse_FORMAT functions here are intended to be as lenient as +# possible in their parsing, while still returning a correctly typed +# RawMetadata. +# +# To aid in this, we also generally want to do as little touching of the +# data as possible, except where there are possibly some historic holdovers +# that make valid data awkward to work with. +# +# While this is a lower level, intermediate format than our ``Metadata`` +# class, some light touch ups can make a massive difference in usability. + +# Map METADATA fields to RawMetadata. +_EMAIL_TO_RAW_MAPPING = { + "author": "author", + "author-email": "author_email", + "classifier": "classifiers", + "description": "description", + "description-content-type": "description_content_type", + "download-url": "download_url", + "dynamic": "dynamic", + "home-page": "home_page", + "import-name": "import_names", + "import-namespace": "import_namespaces", + "keywords": "keywords", + "license": "license", + "license-expression": "license_expression", + "license-file": "license_files", + "maintainer": "maintainer", + "maintainer-email": "maintainer_email", + "metadata-version": "metadata_version", + "name": "name", + "obsoletes": "obsoletes", + "obsoletes-dist": "obsoletes_dist", + "platform": "platforms", + "project-url": "project_urls", + "provides": "provides", + "provides-dist": "provides_dist", + "provides-extra": "provides_extra", + "requires": "requires", + "requires-dist": "requires_dist", + "requires-external": "requires_external", + "requires-python": "requires_python", + "summary": "summary", + "supported-platform": "supported_platforms", + "version": "version", +} +_RAW_TO_EMAIL_MAPPING = {raw: email for email, raw in _EMAIL_TO_RAW_MAPPING.items()} + + +# A bare "\r" makes the email generator raise ``HeaderWriteError``, and on +# CPython releases without the CVE-2024-6923 fix any ``str.splitlines`` +# boundary ends the header line, so fold all of them, not just "\n". +_LINE_BOUNDARY_RE = re.compile(r"\r\n|[\n\r\v\f\x1c\x1d\x1e\x85\u2028\u2029]") + + +# This class is for writing RFC822 messages +class RFC822Policy(email.policy.EmailPolicy): + """ + This is :class:`email.policy.EmailPolicy`, but with a simple ``header_store_parse`` + implementation that handles multi-line values, and some nice defaults. + + .. versionadded:: 26.0 + """ + + utf8 = True + mangle_from_ = False + max_line_length = 0 + + def header_store_parse(self, name: str, value: str) -> tuple[str, str]: + size = len(name) + 2 + value = _LINE_BOUNDARY_RE.sub("\n" + " " * size, value) + return (name, value) + + +# This class is for writing RFC822 messages +class RFC822Message(email.message.EmailMessage): + """ + This is :class:`email.message.EmailMessage` with two small changes: it defaults to + our `RFC822Policy`, and it correctly writes unicode when being called + with `bytes()`. + + .. versionadded:: 26.0 + """ + + def __init__(self) -> None: + super().__init__(policy=RFC822Policy()) + + def as_bytes( + self, unixfrom: bool = False, policy: email.policy.Policy | None = None + ) -> bytes: + """ + Return the bytes representation of the message. + + This handles unicode encoding. + """ + return self.as_string(unixfrom, policy=policy).encode("utf-8") + + +def parse_email(data: bytes | str) -> tuple[RawMetadata, dict[str, list[str]]]: + """Parse a distribution's metadata stored as email headers (e.g. from ``METADATA``). + + This function returns a two-item tuple of dicts. The first dict is of + recognized fields from the core metadata specification. Fields that can be + parsed and translated into Python's built-in types are converted + appropriately. All other fields are left as-is. Fields that are allowed to + appear multiple times are stored as lists. + + The second dict contains all other fields from the metadata. This includes + any unrecognized fields. It also includes any fields which are expected to + be parsed into a built-in type but were not formatted appropriately. Finally, + any fields that are expected to appear only once but are repeated are + included in this dict. + + """ + raw: dict[str, str | list[str] | dict[str, str]] = {} + unparsed: dict[str, list[str]] = {} + + if isinstance(data, str): + parsed = email.parser.Parser(policy=email.policy.compat32).parsestr(data) + else: + parsed = email.parser.BytesParser(policy=email.policy.compat32).parsebytes(data) + + # We have to wrap parsed.keys() in a set, because in the case of multiple + # values for a key (a list), the key will appear multiple times in the + # list of keys, but we're avoiding that by using get_all(). + for name_with_case in frozenset(parsed.keys()): + # Header names in RFC are case insensitive, so we'll normalize to all + # lower case to make comparisons easier. + name = name_with_case.lower() + + # We use get_all() here, even for fields that aren't multiple use, + # because otherwise someone could have e.g. two Name fields, and we + # would just silently ignore it rather than doing something about it. + headers = parsed.get_all(name) or [] + + # The way the email module works when parsing bytes is that it + # unconditionally decodes the bytes as ascii using the surrogateescape + # handler. When you pull that data back out (such as with get_all() ), + # it looks to see if the str has any surrogate escapes, and if it does + # it wraps it in a Header object instead of returning the string. + # + # As such, we'll look for those Header objects, and fix up the encoding. + value = [] + # Flag if we have run into any issues processing the headers, thus + # signalling that the data belongs in 'unparsed'. + valid_encoding = True + for h in headers: + # It's unclear if this can return more types than just a Header or + # a str, so we'll just assert here to make sure. + assert isinstance(h, (email.header.Header, str)) + + # If it's a header object, we need to do our little dance to get + # the real data out of it. In cases where there is invalid data + # we're going to end up with mojibake, but there's no obvious, good + # way around that without reimplementing parts of the Header object + # ourselves. + # + # That should be fine since, if mojibacked happens, this key is + # going into the unparsed dict anyways. + if isinstance(h, email.header.Header): + # The Header object stores it's data as chunks, and each chunk + # can be independently encoded, so we'll need to check each + # of them. + chunks: list[tuple[bytes, str | None]] = [] + for binary, _encoding in email.header.decode_header(h): + try: + binary.decode("utf8", "strict") + except UnicodeDecodeError: + # Enable mojibake. + encoding = "latin1" + valid_encoding = False + else: + encoding = "utf8" + chunks.append((binary, encoding)) + + # Turn our chunks back into a Header object, then let that + # Header object do the right thing to turn them into a + # string for us. + value.append(str(email.header.make_header(chunks))) + # This is already a string, so just add it. + else: + value.append(h) + + # We've processed all of our values to get them into a list of str, + # but we may have mojibake data, in which case this is an unparsed + # field. + if not valid_encoding: + unparsed[name] = value + continue + + raw_name = _EMAIL_TO_RAW_MAPPING.get(name) + if raw_name is None: + # This is a bit of a weird situation, we've encountered a key that + # we don't know what it means, so we don't know whether it's meant + # to be a list or not. + # + # Since we can't really tell one way or another, we'll just leave it + # as a list, even though it may be a single item list, because that's + # what makes the most sense for email headers. + unparsed[name] = value + continue + + # If this is one of our string fields, then we'll check to see if our + # value is a list of a single item. If it is then we'll assume that + # it was emitted as a single string, and unwrap the str from inside + # the list. + # + # If it's any other kind of data, then we haven't the faintest clue + # what we should parse it as, and we have to just add it to our list + # of unparsed stuff. + if raw_name in _STRING_FIELDS and len(value) == 1: + raw[raw_name] = value[0] + # If this is import_names, we need to special case the empty field + # case, which converts to an empty list instead of None. We can't let + # the empty case slip through, as it will fail validation. + elif raw_name == "import_names" and value == [""]: + raw[raw_name] = [] + # If this is one of our list of string fields, then we can just assign + # the value, since email *only* has strings, and our get_all() call + # above ensures that this is a list. + elif raw_name in _LIST_FIELDS: + raw[raw_name] = value + # Special Case: Keywords + # The keywords field is implemented in the metadata spec as a str, + # but it conceptually is a list of strings, and is serialized using + # ", ".join(keywords), so we'll do some light data massaging to turn + # this into what it logically is. + elif raw_name == "keywords" and len(value) == 1: + raw[raw_name] = _parse_keywords(value[0]) + # Special Case: Project-URL + # The project urls is implemented in the metadata spec as a list of + # specially-formatted strings that represent a key and a value, which + # is fundamentally a mapping, however the email format doesn't support + # mappings in a sane way, so it was crammed into a list of strings + # instead. + # + # We will do a little light data massaging to turn this into a map as + # it logically should be. + elif raw_name == "project_urls": + try: + raw[raw_name] = _parse_project_urls(value) + except KeyError: + unparsed[name] = value + # Nothing that we've done has managed to parse this, so it'll just + # throw it in our unparsable data and move on. + else: + unparsed[name] = value + + # We need to support getting the Description from the message payload in + # addition to getting it from the the headers. This does mean, though, there + # is the possibility of it being set both ways, in which case we put both + # in 'unparsed' since we don't know which is right. + try: + payload = _get_payload(parsed, data) + except ValueError: + unparsed.setdefault("description", []).append( + parsed.get_payload(decode=isinstance(data, bytes)) # type: ignore[call-overload] + ) + else: + if payload: + # Check to see if we've already got a description, if so then both + # it, and this body move to unparsable. + if "description" in raw: + description_header = cast("str", raw.pop("description")) + unparsed.setdefault("description", []).extend( + [description_header, payload] + ) + elif "description" in unparsed: + unparsed["description"].append(payload) + else: + raw["description"] = payload + + # We need to cast our `raw` to a metadata, because a TypedDict only support + # literal key names, but we're computing our key names on purpose, but the + # way this function is implemented, our `TypedDict` can only have valid key + # names. + return cast("RawMetadata", raw), unparsed + + +_NOT_FOUND = object() + + +# Keep the two values in sync. +_VALID_METADATA_VERSIONS = [ + "1.0", + "1.1", + "1.2", + "2.1", + "2.2", + "2.3", + "2.4", + "2.5", + "2.6", +] +_MetadataVersion = Literal[ + "1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6" +] + +_REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"]) + + +class _Validator(Generic[T]): + """Validate a metadata field. + + All _process_*() methods correspond to a core metadata field. The method is + called with the field's raw value. If the raw value is valid it is returned + in its "enriched" form (e.g. ``version.Version`` for the ``Version`` field). + If the raw value is invalid, :exc:`InvalidMetadata` is raised (with a cause + as appropriate). + """ + + name: str + raw_name: str + added: _MetadataVersion + + def __init__( + self, + *, + added: _MetadataVersion = "1.0", + ) -> None: + self.added = added + + def __set_name__(self, _owner: Metadata, name: str) -> None: + self.name = name + self.raw_name = _RAW_TO_EMAIL_MAPPING[name] + + def __get__(self, instance: Metadata, _owner: type[Metadata]) -> T: + # With Python 3.8, the caching can be replaced with functools.cached_property(). + # No need to check the cache as attribute lookup will resolve into the + # instance's __dict__ before __get__ is called. + cache = instance.__dict__ + value = instance._raw.get(self.name) + + # To make the _process_* methods easier, we'll check if the value is None + # and if this field is NOT a required attribute, and if both of those + # things are true, we'll skip the the converter. This will mean that the + # converters never have to deal with the None union. + if self.name in _REQUIRED_ATTRS or value is not None: + try: + converter: Callable[[Any], T] = getattr(self, f"_process_{self.name}") + except AttributeError: + pass + else: + value = converter(value) + + cache[self.name] = value + try: + del instance._raw[self.name] # type: ignore[misc] + except KeyError: + pass + + return cast("T", value) + + def _invalid_metadata( + self, msg: str, cause: Exception | None = None + ) -> InvalidMetadata: + exc = InvalidMetadata(self.raw_name, msg) + exc.__cause__ = cause + return exc + + def _process_metadata_version(self, value: str) -> _MetadataVersion: + # Implicitly makes Metadata-Version required. + if value not in _VALID_METADATA_VERSIONS: + raise self._invalid_metadata(f"{value!r} is not a valid metadata version") + return cast("_MetadataVersion", value) + + def _process_name(self, value: str) -> str: + if not value: + raise self._invalid_metadata(f"{self.raw_name!r} is a required field") + # Validate the name as a side-effect. + try: + utils.canonicalize_name(value, validate=True) + except utils.InvalidName as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {self.raw_name!r}", cause=exc + ) from exc + else: + return value + + def _process_version(self, value: str) -> Version: + if not value: + raise self._invalid_metadata(f"{self.raw_name!r} is a required field") + try: + return version_module.parse(value) + except version_module.InvalidVersion as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {self.raw_name!r}", cause=exc + ) from exc + + def _process_summary(self, value: str) -> str: + """Check the field contains no line breaks.""" + if _LINE_BOUNDARY_RE.search(value): + raise self._invalid_metadata(f"{self.raw_name!r} must be a single line") + return value + + def _process_description_content_type(self, value: str) -> str: + content_types = {"text/plain", "text/x-rst", "text/markdown"} + invalid_msg = ( + f"{self.raw_name!r} must be one of {list(content_types)}, not {value!r}" + ) + message = email.message.EmailMessage() + try: + message["content-type"] = value + # The email parser can raise IndexError on malformed RFC 2231 + # parameters such as "text/plain; x*". + except (ValueError, IndexError) as exc: + msg = f"{value!r} is not a valid content type for {self.raw_name!r}" + raise self._invalid_metadata(msg, cause=exc) from exc + content_type_header = message["content-type"] + if content_type_header.defects: + defect = content_type_header.defects[0] + msg = ( + f"{value!r} is not a valid content type for {self.raw_name!r}: {defect}" + ) + raise self._invalid_metadata(msg, cause=defect) from defect + + content_type, parameters = ( + # Defaults to `text/plain` if parsing failed. + message.get_content_type().lower(), + content_type_header.params, + ) + # Check if content-type is valid or defaulted to `text/plain` and thus was + # not parseable. + if content_type not in content_types or content_type not in value.lower(): + raise self._invalid_metadata(invalid_msg) + + charset = parameters.get("charset", "UTF-8") + if charset.lower() != "utf-8": + raise self._invalid_metadata( + f"{self.raw_name!r} can only specify the UTF-8 charset, not {charset!r}" + ) + + markdown_variants = {"GFM", "CommonMark"} + variant = parameters.get("variant", "GFM") # Use an acceptable default. + if content_type == "text/markdown" and variant not in markdown_variants: + raise self._invalid_metadata( + f"valid Markdown variants for {self.raw_name!r} are " + f"{list(markdown_variants)}, not {variant!r}", + ) + return value + + def _process_dynamic(self, value: list[str]) -> list[str]: + dynamic_fields = list(map(str.lower, value)) + for dynamic_field in dynamic_fields: + if dynamic_field in {"name", "version", "metadata-version"}: + raise self._invalid_metadata( + f"{dynamic_field!r} is not allowed as a dynamic field" + ) + elif dynamic_field not in _EMAIL_TO_RAW_MAPPING: + raise self._invalid_metadata( + f"{dynamic_field!r} is not a valid dynamic field" + ) + return dynamic_fields + + def _process_provides_extra( + self, + value: list[str], + ) -> list[utils.NormalizedName]: + normalized_names = [] + try: + for name in value: + normalized_names.append(utils.canonicalize_name(name, validate=True)) + except utils.InvalidName as exc: + raise self._invalid_metadata( + f"{name!r} is invalid for {self.raw_name!r}", cause=exc + ) from exc + else: + return normalized_names + + def _process_requires_python(self, value: str) -> specifiers.SpecifierSet: + try: + return specifiers.SpecifierSet(value) + except specifiers.InvalidSpecifier as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {self.raw_name!r}", cause=exc + ) from exc + + def _process_requires_dist( + self, + value: list[str], + ) -> list[requirements.Requirement]: + reqs = [] + try: + for req in value: + reqs.append(requirements.Requirement(req)) + except requirements.InvalidRequirement as exc: + raise self._invalid_metadata( + f"{req!r} is invalid for {self.raw_name!r}", cause=exc + ) from exc + else: + return reqs + + def _process_license_expression(self, value: str) -> NormalizedLicenseExpression: + try: + return licenses.canonicalize_license_expression(value) + except ValueError as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {self.raw_name!r}", cause=exc + ) from exc + + def _process_license_files(self, value: list[str]) -> list[str]: + paths = [] + for path in value: + if ".." in path: + raise self._invalid_metadata( + f"{path!r} is invalid for {self.raw_name!r}, " + "parent directory indicators are not allowed" + ) + if "*" in path: + raise self._invalid_metadata( + f"{path!r} is invalid for {self.raw_name!r}, paths must be resolved" + ) + if ( + pathlib.PurePosixPath(path).is_absolute() + or pathlib.PureWindowsPath(path).is_absolute() + ): + raise self._invalid_metadata( + f"{path!r} is invalid for {self.raw_name!r}, paths must be relative" + ) + if pathlib.PureWindowsPath(path).as_posix() != path: + raise self._invalid_metadata( + f"{path!r} is invalid for {self.raw_name!r}, " + "paths must use '/' delimiter" + ) + paths.append(path) + return paths + + def _process_import_names(self, value: list[str]) -> list[str]: + for import_name in value: + name, semicolon, private = import_name.partition(";") + name = name.rstrip() + for identifier in name.split("."): + if not identifier.isidentifier(): + raise self._invalid_metadata( + f"{name!r} is invalid for {self.raw_name!r}; " + f"{identifier!r} is not a valid identifier" + ) + elif keyword.iskeyword(identifier): + raise self._invalid_metadata( + f"{name!r} is invalid for {self.raw_name!r}; " + f"{identifier!r} is a keyword" + ) + if semicolon and private.lstrip() != "private": + raise self._invalid_metadata( + f"{import_name!r} is invalid for {self.raw_name!r}; " + "the only valid option is 'private'" + ) + return value + + _process_import_namespaces = _process_import_names + + +class Metadata: + """Representation of distribution metadata. + + Compared to :class:`RawMetadata`, this class provides objects representing + metadata fields instead of only using built-in types. Any invalid metadata + will cause :exc:`InvalidMetadata` to be raised (with a + :py:attr:`~BaseException.__cause__` attribute as appropriate). + + .. versionadded:: 23.2 + + .. versionchanged:: 24.0 + Optional attributes now return None when the field is absent instead of + raising. + """ + + _raw: RawMetadata + + @classmethod + def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> Metadata: + """Create an instance from :class:`RawMetadata`. + + If *validate* is true, all metadata will be validated and all related + exceptions will be gathered and raised as an :class:`ExceptionGroup`; + otherwise, validation happens per attribute when it is accessed. + """ + ins = cls() + ins._raw = data.copy() # Mutations occur due to caching enriched values. + + if validate: + collector = _ErrorCollector() + metadata_version = None + with collector.collect(InvalidMetadata): + metadata_version = ins.metadata_version + metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version) + + # Make sure to check for the fields that are present, the required + # fields (so their absence can be reported). + fields_to_check = frozenset(ins._raw) | _REQUIRED_ATTRS + # Remove fields that have already been checked. + fields_to_check -= {"metadata_version"} + + for key in fields_to_check: + try: + if metadata_version: + # Can't use getattr() as that triggers descriptor protocol which + # will fail due to no value for the instance argument. + try: + field_metadata_version = cls.__dict__[key].added + except KeyError: + exc = InvalidMetadata(key, f"unrecognized field: {key!r}") + collector.error(exc) + continue + field_age = _VALID_METADATA_VERSIONS.index( + field_metadata_version + ) + if field_age > metadata_age: + field = _RAW_TO_EMAIL_MAPPING[key] + exc = InvalidMetadata( + field, + f"{field} introduced in metadata version " + f"{field_metadata_version}, not {metadata_version}", + ) + collector.error(exc) + continue + getattr(ins, key) + except InvalidMetadata as exc: + collector.error(exc) + + collector.finalize("invalid metadata") + + return ins + + @classmethod + def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata: + """Parse metadata from email headers. + + If *validate* is true, the metadata will be validated. All exceptions + related to validation will be gathered and raised as an :class:`ExceptionGroup`. + """ + raw, unparsed = parse_email(data) + + if validate: + with _ErrorCollector().on_exit("invalid or unparsed metadata") as collector: + for unparsed_key in unparsed: + if unparsed_key in _EMAIL_TO_RAW_MAPPING: + message = f"{unparsed_key!r} has invalid data" + else: + message = f"unrecognized field: {unparsed_key!r}" + collector.error(InvalidMetadata(unparsed_key, message)) + try: + validated = cls.from_raw(raw, validate=validate) + except ExceptionGroup as exc_group: + # The no-branch pragmas cover arcs only seen by Python 3.9. + for exc in exc_group.exceptions: # pragma: no branch + # A required field reported above as unparsed is absent + # from `raw`, so skip from_raw's duplicate "missing" + # complaint. + if not ( + isinstance(exc, InvalidMetadata) + and exc.field in unparsed + and _EMAIL_TO_RAW_MAPPING.get(exc.field) not in raw + ): + collector.error(exc) + else: + if not collector.errors: # pragma: no branch + return validated + + return cls.from_raw(raw, validate=validate) + + metadata_version: _Validator[_MetadataVersion] = _Validator() + """:external:ref:`core-metadata-metadata-version` + (required; validated to be a valid metadata version)""" + # `name` is not normalized/typed to NormalizedName so as to provide access to + # the original/raw name. + name: _Validator[str] = _Validator() + """:external:ref:`core-metadata-name` + (required; validated using :func:`~packaging.utils.canonicalize_name` and its + *validate* parameter)""" + version: _Validator[Version] = _Validator() + """:external:ref:`core-metadata-version` (required)""" + dynamic: _Validator[list[str] | None] = _Validator( + added="2.2", + ) + """:external:ref:`core-metadata-dynamic` + (validated against core metadata field names and lowercased)""" + platforms: _Validator[list[str] | None] = _Validator() + """:external:ref:`core-metadata-platform`""" + supported_platforms: _Validator[list[str] | None] = _Validator(added="1.1") + """:external:ref:`core-metadata-supported-platform`""" + summary: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-summary` (validated to contain no newlines)""" + description: _Validator[str | None] = _Validator() # TODO 2.1: can be in body + """:external:ref:`core-metadata-description`""" + description_content_type: _Validator[str | None] = _Validator(added="2.1") + """:external:ref:`core-metadata-description-content-type` (validated)""" + keywords: _Validator[list[str] | None] = _Validator() + """:external:ref:`core-metadata-keywords`""" + home_page: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-home-page`""" + download_url: _Validator[str | None] = _Validator(added="1.1") + """:external:ref:`core-metadata-download-url`""" + author: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-author`""" + author_email: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-author-email`""" + maintainer: _Validator[str | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-maintainer`""" + maintainer_email: _Validator[str | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-maintainer-email`""" + license: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-license`""" + license_expression: _Validator[NormalizedLicenseExpression | None] = _Validator( + added="2.4" + ) + """:external:ref:`core-metadata-license-expression` + + .. versionadded:: 24.2 + """ + license_files: _Validator[list[str] | None] = _Validator(added="2.4") + """:external:ref:`core-metadata-license-file` + + .. versionadded:: 24.2 + """ + classifiers: _Validator[list[str] | None] = _Validator(added="1.1") + """:external:ref:`core-metadata-classifier`""" + requires_dist: _Validator[list[requirements.Requirement] | None] = _Validator( + added="1.2" + ) + """:external:ref:`core-metadata-requires-dist`""" + requires_python: _Validator[specifiers.SpecifierSet | None] = _Validator( + added="1.2" + ) + """:external:ref:`core-metadata-requires-python`""" + # Because `Requires-External` allows for non-PEP 440 version specifiers, we + # don't do any processing on the values. + requires_external: _Validator[list[str] | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-requires-external`""" + project_urls: _Validator[dict[str, str] | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-project-url`""" + # PEP 685 lets us raise an error if an extra doesn't pass `Name` validation + # regardless of metadata version. + provides_extra: _Validator[list[utils.NormalizedName] | None] = _Validator( + added="2.1", + ) + """:external:ref:`core-metadata-provides-extra`""" + provides_dist: _Validator[list[str] | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-provides-dist`""" + obsoletes_dist: _Validator[list[str] | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-obsoletes-dist`""" + import_names: _Validator[list[str] | None] = _Validator(added="2.5") + """:external:ref:`core-metadata-import-name` + + .. versionadded:: 26.0 + """ + import_namespaces: _Validator[list[str] | None] = _Validator(added="2.5") + """:external:ref:`core-metadata-import-namespace` + + .. versionadded:: 26.0 + """ + requires: _Validator[list[str] | None] = _Validator(added="1.1") + """``Requires`` (deprecated)""" + provides: _Validator[list[str] | None] = _Validator(added="1.1") + """``Provides`` (deprecated)""" + obsoletes: _Validator[list[str] | None] = _Validator(added="1.1") + """``Obsoletes`` (deprecated)""" + + def as_rfc822(self) -> RFC822Message: + """ + Return an RFC822 message with the metadata. + + .. versionadded:: 26.0 + """ + message = RFC822Message() + self._write_metadata(message) + return message + + def _write_metadata(self, message: RFC822Message) -> None: + """ + Return an RFC822 message with the metadata. + """ + for name, validator in self.__class__.__dict__.items(): + if isinstance(validator, _Validator) and name != "description": + value = getattr(self, name) + email_name = _RAW_TO_EMAIL_MAPPING[name] + if value is not None: + if email_name == "project-url": + for label, url in value.items(): + message[email_name] = f"{label}, {url}" + elif email_name == "keywords": + message[email_name] = ",".join(value) + elif email_name == "import-name" and value == []: + message[email_name] = "" + elif isinstance(value, list): + for item in value: + message[email_name] = str(item) + else: + message[email_name] = str(value) + + # The description is a special case because it is in the body of the message. + if self.description is not None: + message.set_payload(self.description) diff --git a/venv/lib/python3.12/site-packages/packaging/py.typed b/venv/lib/python3.12/site-packages/packaging/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/packaging/pylock.py b/venv/lib/python3.12/site-packages/packaging/pylock.py new file mode 100644 index 0000000..b797623 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/pylock.py @@ -0,0 +1,943 @@ +from __future__ import annotations + +import dataclasses +import logging +import re +from collections.abc import Mapping, Sequence +from dataclasses import dataclass +from datetime import datetime +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Protocol, + TypeVar, + cast, +) +from urllib.parse import unquote, urlparse + +from .markers import ( + Environment, + Marker, + _pep440_python_full_version, + default_environment, +) +from .specifiers import SpecifierSet +from .tags import create_compatible_tags_selector, sys_tags +from .utils import ( + NormalizedName, + is_normalized_name, + parse_sdist_filename, + parse_wheel_filename, +) +from .version import Version + +if TYPE_CHECKING: # pragma: no cover + from collections.abc import Collection, Iterator + from pathlib import Path + + from typing_extensions import Self + + from .tags import Tag + +_logger = logging.getLogger(__name__) + +__all__ = [ + "Package", + "PackageArchive", + "PackageDirectory", + "PackageSdist", + "PackageVcs", + "PackageWheel", + "Pylock", + "PylockSelectError", + "PylockUnsupportedVersionError", + "PylockValidationError", + "is_valid_pylock_path", +] + + +def __dir__() -> list[str]: + return __all__ + + +_T = TypeVar("_T") +_T2 = TypeVar("_T2") + + +class _FromMappingProtocol(Protocol): # pragma: no cover + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: ... + + +_FromMappingProtocolT = TypeVar("_FromMappingProtocolT", bound=_FromMappingProtocol) + + +_PYLOCK_FILE_NAME_RE = re.compile(r"^pylock\.([^.]+)\.toml$") + + +def is_valid_pylock_path(path: Path) -> bool: + """Check if the given path is a valid pylock file path.""" + return path.name == "pylock.toml" or bool(_PYLOCK_FILE_NAME_RE.match(path.name)) + + +def _toml_key(key: str) -> str: + return key.replace("_", "-") + + +def _toml_value(key: str, value: Any) -> Any: # noqa: ANN401 + if isinstance(value, (Version, Marker, SpecifierSet)): + return str(value) + if isinstance(value, Sequence) and key == "environments": + return [str(v) for v in value] + return value + + +def _toml_dict_factory(data: list[tuple[str, Any]]) -> dict[str, Any]: + return { + _toml_key(key): _toml_value(key, value) + for key, value in data + if value is not None + } + + +def _get(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T | None: + """Get a value from the dictionary and verify it's the expected type.""" + if (value := d.get(key)) is None: + return None + if not isinstance(value, expected_type) or ( + # Special case: bool is a subclass of int, but TOML distinguishes the two + expected_type is int and isinstance(value, bool) + ): + raise PylockValidationError( + f"Unexpected type {type(value).__name__} " + f"(expected {expected_type.__name__})", + context=key, + ) + return value + + +def _get_required(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T: + """Get a required value from the dictionary and verify it's the expected type.""" + if (value := _get(d, expected_type, key)) is None: + raise _PylockRequiredKeyError(key) + return value + + +def _get_sequence( + d: Mapping[str, Any], expected_item_type: type[_T], key: str +) -> Sequence[_T] | None: + """Get a list value from the dictionary and verify it's the expected items type.""" + if (value := _get(d, Sequence, key)) is None: # type: ignore[type-abstract] + return None + if isinstance(value, (str, bytes)): + # special case: str and bytes are Sequences, but we want to reject it + raise PylockValidationError( + f"Unexpected type {type(value).__name__} (expected Sequence)", + context=key, + ) + for i, item in enumerate(value): + if not isinstance(item, expected_item_type): + raise PylockValidationError( + f"Unexpected type {type(item).__name__} " + f"(expected {expected_item_type.__name__})", + context=f"{key}[{i}]", + ) + return value + + +def _get_as( + d: Mapping[str, Any], + expected_type: type[_T], + target_type: Callable[[_T], _T2], + key: str, +) -> _T2 | None: + """Get a value from the dictionary, verify it's the expected type, + and convert to the target type. + + This assumes the target_type constructor accepts the value. + """ + if (value := _get(d, expected_type, key)) is None: + return None + try: + return target_type(value) + except Exception as e: + raise PylockValidationError(e, context=key) from e + + +def _get_required_as( + d: Mapping[str, Any], + expected_type: type[_T], + target_type: Callable[[_T], _T2], + key: str, +) -> _T2: + """Get a required value from the dict, verify it's the expected type, + and convert to the target type.""" + if (value := _get_as(d, expected_type, target_type, key)) is None: + raise _PylockRequiredKeyError(key) + return value + + +def _get_sequence_as( + d: Mapping[str, Any], + expected_item_type: type[_T], + target_item_type: Callable[[_T], _T2], + key: str, +) -> list[_T2] | None: + """Get list value from dictionary and verify expected items type.""" + if (value := _get_sequence(d, expected_item_type, key)) is None: + return None + result = [] + try: + for item in value: + typed_item = target_item_type(item) + result.append(typed_item) + except Exception as e: + raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e + return result + + +def _get_object( + d: Mapping[str, Any], target_type: type[_FromMappingProtocolT], key: str +) -> _FromMappingProtocolT | None: + """Get a dictionary value from the dictionary and convert it to a dataclass.""" + if (value := _get(d, Mapping, key)) is None: # type: ignore[type-abstract] + return None + try: + return target_type._from_dict(value) + except Exception as e: + raise PylockValidationError(e, context=key) from e + + +def _get_sequence_of_objects( + d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str +) -> list[_FromMappingProtocolT] | None: + """Get a list value from the dictionary and convert its items to a dataclass.""" + if (value := _get_sequence(d, Mapping, key)) is None: # type: ignore[type-abstract] + return None + result: list[_FromMappingProtocolT] = [] + try: + for item in value: + typed_item = target_item_type._from_dict(item) + result.append(typed_item) + except Exception as e: + raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e + return result + + +def _get_required_sequence_of_objects( + d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str +) -> Sequence[_FromMappingProtocolT]: + """Get a required list value from the dictionary and convert its items to a + dataclass.""" + if (result := _get_sequence_of_objects(d, target_item_type, key)) is None: + raise _PylockRequiredKeyError(key) + return result + + +def _validate_normalized_name(name: str) -> NormalizedName: + """Validate that a string is a NormalizedName.""" + if not is_normalized_name(name): + raise PylockValidationError(f"Name {name!r} is not normalized") + return NormalizedName(name) + + +def _validate_path_url(path: str | None, url: str | None) -> None: + if not path and not url: + raise PylockValidationError("path or url must be provided") + + +def _path_name(path: str | None) -> str | None: + if not path: + return None + # If the path is relative it MAY use POSIX-style path separators explicitly + # for portability + if "/" in path: + return path.rsplit("/", 1)[-1] + elif "\\" in path: + return path.rsplit("\\", 1)[-1] + else: + return path + + +def _url_name(url: str | None) -> str | None: + if not url: + return None + url_path = urlparse(url).path + # The last path component is percent-encoded, so decode it to the file name + return unquote(url_path.rsplit("/", 1)[-1]) + + +def _validate_hashes(hashes: Mapping[str, Any]) -> Mapping[str, Any]: + if not hashes: + raise PylockValidationError("At least one hash must be provided") + if not all(isinstance(hash_val, str) for hash_val in hashes.values()): + raise PylockValidationError("Hash values must be strings") + return hashes + + +class PylockValidationError(Exception): + """Raised when when input data is not spec-compliant.""" + + context: str | None = None + message: str + + def __init__( + self, + cause: str | Exception, + *, + context: str | None = None, + ) -> None: + if isinstance(cause, PylockValidationError): + if cause.context: + self.context = ( + f"{context}.{cause.context}" if context else cause.context + ) + else: + self.context = context + self.message = cause.message + else: + self.context = context + self.message = str(cause) + + def __str__(self) -> str: + if self.context: + return f"{self.message} in {self.context!r}" + return self.message + + +class _PylockRequiredKeyError(PylockValidationError): + def __init__(self, key: str) -> None: + super().__init__("Missing required value", context=key) + + +class PylockUnsupportedVersionError(PylockValidationError): + """Raised when encountering an unsupported `lock_version`.""" + + +class PylockSelectError(Exception): + """Base exception for errors raised by :meth:`Pylock.select`. + + .. versionadded:: 26.1 + """ + + +@dataclass(frozen=True, init=False) +class PackageVcs: + type: str + url: str | None = None + path: str | None = None + requested_revision: str | None = None + commit_id: str # type: ignore[misc] + subdirectory: str | None = None + + def __init__( + self, + *, + type: str, + url: str | None = None, + path: str | None = None, + requested_revision: str | None = None, + commit_id: str, + subdirectory: str | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "type", type) + object.__setattr__(self, "url", url) + object.__setattr__(self, "path", path) + object.__setattr__(self, "requested_revision", requested_revision) + object.__setattr__(self, "commit_id", commit_id) + object.__setattr__(self, "subdirectory", subdirectory) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package_vcs = cls( + type=_get_required(d, str, "type"), + url=_get(d, str, "url"), + path=_get(d, str, "path"), + requested_revision=_get(d, str, "requested-revision"), + commit_id=_get_required(d, str, "commit-id"), + subdirectory=_get(d, str, "subdirectory"), + ) + _validate_path_url(package_vcs.path, package_vcs.url) + return package_vcs + + +@dataclass(frozen=True, init=False) +class PackageDirectory: + path: str + editable: bool | None = None + subdirectory: str | None = None + + def __init__( + self, + *, + path: str, + editable: bool | None = None, + subdirectory: str | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "path", path) + object.__setattr__(self, "editable", editable) + object.__setattr__(self, "subdirectory", subdirectory) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + return cls( + path=_get_required(d, str, "path"), + editable=_get(d, bool, "editable"), + subdirectory=_get(d, str, "subdirectory"), + ) + + +@dataclass(frozen=True, init=False) +class PackageArchive: + url: str | None = None + path: str | None = None + size: int | None = None + upload_time: datetime | None = None + hashes: Mapping[str, str] # type: ignore[misc] + subdirectory: str | None = None + + def __init__( + self, + *, + url: str | None = None, + path: str | None = None, + size: int | None = None, + upload_time: datetime | None = None, + hashes: Mapping[str, str], + subdirectory: str | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "url", url) + object.__setattr__(self, "path", path) + object.__setattr__(self, "size", size) + object.__setattr__(self, "upload_time", upload_time) + object.__setattr__(self, "hashes", hashes) + object.__setattr__(self, "subdirectory", subdirectory) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package_archive = cls( + url=_get(d, str, "url"), + path=_get(d, str, "path"), + size=_get(d, int, "size"), + upload_time=_get(d, datetime, "upload-time"), + hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract] + subdirectory=_get(d, str, "subdirectory"), + ) + _validate_path_url(package_archive.path, package_archive.url) + return package_archive + + +@dataclass(frozen=True, init=False) +class PackageSdist: + name: str | None = None + upload_time: datetime | None = None + url: str | None = None + path: str | None = None + size: int | None = None + hashes: Mapping[str, str] # type: ignore[misc] + + def __init__( + self, + *, + name: str | None = None, + upload_time: datetime | None = None, + url: str | None = None, + path: str | None = None, + size: int | None = None, + hashes: Mapping[str, str], + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "name", name) + object.__setattr__(self, "upload_time", upload_time) + object.__setattr__(self, "url", url) + object.__setattr__(self, "path", path) + object.__setattr__(self, "size", size) + object.__setattr__(self, "hashes", hashes) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package_sdist = cls( + name=_get(d, str, "name"), + upload_time=_get(d, datetime, "upload-time"), + url=_get(d, str, "url"), + path=_get(d, str, "path"), + size=_get(d, int, "size"), + hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract] + ) + _validate_path_url(package_sdist.path, package_sdist.url) + return package_sdist + + @property + def filename(self) -> str: + """Get the filename of the sdist. + + .. versionadded:: 26.1 + """ + filename = self.name or _path_name(self.path) or _url_name(self.url) + if not filename: + raise PylockValidationError("Cannot determine sdist filename") + return filename + + +@dataclass(frozen=True, init=False) +class PackageWheel: + name: str | None = None + upload_time: datetime | None = None + url: str | None = None + path: str | None = None + size: int | None = None + hashes: Mapping[str, str] # type: ignore[misc] + + def __init__( + self, + *, + name: str | None = None, + upload_time: datetime | None = None, + url: str | None = None, + path: str | None = None, + size: int | None = None, + hashes: Mapping[str, str], + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "name", name) + object.__setattr__(self, "upload_time", upload_time) + object.__setattr__(self, "url", url) + object.__setattr__(self, "path", path) + object.__setattr__(self, "size", size) + object.__setattr__(self, "hashes", hashes) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package_wheel = cls( + name=_get(d, str, "name"), + upload_time=_get(d, datetime, "upload-time"), + url=_get(d, str, "url"), + path=_get(d, str, "path"), + size=_get(d, int, "size"), + hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract] + ) + _validate_path_url(package_wheel.path, package_wheel.url) + return package_wheel + + @property + def filename(self) -> str: + """Get the filename of the wheel.""" + filename = self.name or _path_name(self.path) or _url_name(self.url) + if not filename: + raise PylockValidationError("Cannot determine wheel filename") + return filename + + +@dataclass(frozen=True, init=False) +class Package: + name: NormalizedName + version: Version | None = None + marker: Marker | None = None + requires_python: SpecifierSet | None = None + dependencies: Sequence[Mapping[str, Any]] | None = None + vcs: PackageVcs | None = None + directory: PackageDirectory | None = None + archive: PackageArchive | None = None + index: str | None = None + sdist: PackageSdist | None = None + wheels: Sequence[PackageWheel] | None = None + attestation_identities: Sequence[Mapping[str, Any]] | None = None + tool: Mapping[str, Any] | None = None + + def __init__( + self, + *, + name: NormalizedName, + version: Version | None = None, + marker: Marker | None = None, + requires_python: SpecifierSet | None = None, + dependencies: Sequence[Mapping[str, Any]] | None = None, + vcs: PackageVcs | None = None, + directory: PackageDirectory | None = None, + archive: PackageArchive | None = None, + index: str | None = None, + sdist: PackageSdist | None = None, + wheels: Sequence[PackageWheel] | None = None, + attestation_identities: Sequence[Mapping[str, Any]] | None = None, + tool: Mapping[str, Any] | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "name", name) + object.__setattr__(self, "version", version) + object.__setattr__(self, "marker", marker) + object.__setattr__(self, "requires_python", requires_python) + object.__setattr__(self, "dependencies", dependencies) + object.__setattr__(self, "vcs", vcs) + object.__setattr__(self, "directory", directory) + object.__setattr__(self, "archive", archive) + object.__setattr__(self, "index", index) + object.__setattr__(self, "sdist", sdist) + object.__setattr__(self, "wheels", wheels) + object.__setattr__(self, "attestation_identities", attestation_identities) + object.__setattr__(self, "tool", tool) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package = cls( + name=_get_required_as(d, str, _validate_normalized_name, "name"), + version=_get_as(d, str, Version, "version"), + requires_python=_get_as(d, str, SpecifierSet, "requires-python"), + dependencies=_get_sequence(d, Mapping, "dependencies"), # type: ignore[type-abstract] + marker=_get_as(d, str, Marker, "marker"), + vcs=_get_object(d, PackageVcs, "vcs"), + directory=_get_object(d, PackageDirectory, "directory"), + archive=_get_object(d, PackageArchive, "archive"), + index=_get(d, str, "index"), + sdist=_get_object(d, PackageSdist, "sdist"), + wheels=_get_sequence_of_objects(d, PackageWheel, "wheels"), + attestation_identities=_get_sequence(d, Mapping, "attestation-identities"), # type: ignore[type-abstract] + tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract] + ) + distributions = bool(package.sdist) + len(package.wheels or []) + direct_urls = ( + bool(package.vcs) + bool(package.directory) + bool(package.archive) + ) + if distributions > 0 and direct_urls > 0: + raise PylockValidationError( + "None of vcs, directory, archive must be set if sdist or wheels are set" + ) + if distributions == 0 and direct_urls != 1: + raise PylockValidationError( + "Exactly one of vcs, directory, archive must be set " + "if sdist and wheels are not set" + ) + for i, wheel in enumerate(package.wheels or []): + try: + (name, version, _, _) = parse_wheel_filename(wheel.filename) + except Exception as e: + raise PylockValidationError( + f"Invalid wheel filename {wheel.filename!r}", + context=f"wheels[{i}]", + ) from e + if name != package.name: + raise PylockValidationError( + f"Name in {wheel.filename!r} is not consistent with " + f"package name {package.name!r}", + context=f"wheels[{i}]", + ) + if package.version and version != package.version: + raise PylockValidationError( + f"Version in {wheel.filename!r} is not consistent with " + f"package version {str(package.version)!r}", + context=f"wheels[{i}]", + ) + if package.sdist: + try: + name, version = parse_sdist_filename(package.sdist.filename) + except Exception as e: + raise PylockValidationError( + f"Invalid sdist filename {package.sdist.filename!r}", + context="sdist", + ) from e + if name != package.name: + raise PylockValidationError( + f"Name in {package.sdist.filename!r} is not consistent with " + f"package name {package.name!r}", + context="sdist", + ) + if package.version and version != package.version: + raise PylockValidationError( + f"Version in {package.sdist.filename!r} is not consistent with " + f"package version {str(package.version)!r}", + context="sdist", + ) + try: + for i, attestation_identity in enumerate( # noqa: B007 + package.attestation_identities or [] + ): + _get_required(attestation_identity, str, "kind") + except Exception as e: + raise PylockValidationError( + e, context=f"attestation-identities[{i}]" + ) from e + return package + + @property + def is_direct(self) -> bool: + return not (self.sdist or self.wheels) + + +@dataclass(frozen=True, init=False) +class Pylock: + """A class representing a pylock file.""" + + lock_version: Version + environments: Sequence[Marker] | None = None + requires_python: SpecifierSet | None = None + extras: Sequence[NormalizedName] | None = None + dependency_groups: Sequence[str] | None = None + default_groups: Sequence[str] | None = None + created_by: str # type: ignore[misc] + packages: Sequence[Package] # type: ignore[misc] + tool: Mapping[str, Any] | None = None + + def __init__( + self, + *, + lock_version: Version, + environments: Sequence[Marker] | None = None, + requires_python: SpecifierSet | None = None, + extras: Sequence[NormalizedName] | None = None, + dependency_groups: Sequence[str] | None = None, + default_groups: Sequence[str] | None = None, + created_by: str, + packages: Sequence[Package], + tool: Mapping[str, Any] | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "lock_version", lock_version) + object.__setattr__(self, "environments", environments) + object.__setattr__(self, "requires_python", requires_python) + object.__setattr__(self, "extras", extras) + object.__setattr__(self, "dependency_groups", dependency_groups) + object.__setattr__(self, "default_groups", default_groups) + object.__setattr__(self, "created_by", created_by) + object.__setattr__(self, "packages", packages) + object.__setattr__(self, "tool", tool) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + pylock = cls( + lock_version=_get_required_as(d, str, Version, "lock-version"), + environments=_get_sequence_as(d, str, Marker, "environments"), + extras=_get_sequence_as(d, str, _validate_normalized_name, "extras"), + dependency_groups=_get_sequence(d, str, "dependency-groups"), + default_groups=_get_sequence(d, str, "default-groups"), + created_by=_get_required(d, str, "created-by"), + requires_python=_get_as(d, str, SpecifierSet, "requires-python"), + packages=_get_required_sequence_of_objects(d, Package, "packages"), + tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract] + ) + if not Version("1") <= pylock.lock_version < Version("2"): + raise PylockUnsupportedVersionError( + f"pylock version {pylock.lock_version} is not supported" + ) + if pylock.lock_version > Version("1.0"): + _logger.warning( + "pylock minor version %s is not supported", pylock.lock_version + ) + return pylock + + @classmethod + def from_dict(cls, d: Mapping[str, Any], /) -> Self: + """Create and validate a Pylock instance from a TOML dictionary. + + Raises :class:`PylockValidationError` if the input data is not + spec-compliant. + """ + return cls._from_dict(d) + + def to_dict(self) -> Mapping[str, Any]: + """Convert the Pylock instance to a TOML dictionary.""" + return dataclasses.asdict(self, dict_factory=_toml_dict_factory) + + def validate(self) -> None: + """Validate the Pylock instance against the specification. + + Raises :class:`PylockValidationError` otherwise.""" + self.from_dict(self.to_dict()) + + def select( + self, + *, + environment: Environment | None = None, + tags: Sequence[Tag] | None = None, + extras: Collection[str] | None = None, + dependency_groups: Collection[str] | None = None, + prefer_sdist_predicate: Callable[[NormalizedName], bool] | None = None, + ) -> Iterator[ + tuple[ + Package, + PackageVcs + | PackageDirectory + | PackageArchive + | PackageWheel + | PackageSdist, + ] + ]: + """Select what to install from the lock file. + + The *environment* and *tags* parameters represent the environment being + selected for. If unspecified, ``packaging.markers.default_environment()`` and + ``packaging.tags.sys_tags()`` are used. + + The *extras* parameter represents the extras to install. + + The *dependency_groups* parameter represents the groups to install. If + unspecified, the default groups are used. + + The *prefer_sdist_predicate* parameter is called for packages with a source + distribution. If it returns ``True``, the source distribution is selected + before attempting wheel compatibility. If no source distribution is + available, wheel selection proceeds as usual without calling the predicate. + + This method must be used on valid Pylock instances (i.e. one obtained + from :meth:`Pylock.from_dict` or if constructed manually, after calling + :meth:`Pylock.validate`). + + .. versionadded:: 26.1 + + .. versionchanged:: 26.3 + Added the *prefer_sdist_predicate* parameter. + """ + compatible_tags_selector = create_compatible_tags_selector( + tags if tags is not None else sys_tags() + ) + + # #. Gather the extras and dependency groups to install and set ``extras`` and + # ``dependency_groups`` for marker evaluation, respectively. + # + # #. ``extras`` SHOULD be set to the empty set by default. + # #. ``dependency_groups`` SHOULD be the set created from + # :ref:`pylock-default-groups` by default. + env = cast( + "dict[str, str | frozenset[str]]", + dict( + environment or {}, # Marker.evaluate will fill-up + extras=frozenset(extras or []), + dependency_groups=frozenset( + (self.default_groups or []) + if dependency_groups is None # to allow selecting no group + else dependency_groups + ), + ), + ) + env_python_full_version = _pep440_python_full_version( + environment["python_full_version"] + if environment + else default_environment()["python_full_version"] + ) + + # #. Check if the metadata version specified by :ref:`pylock-lock-version` is + # supported; an error or warning MUST be raised as appropriate. + # Covered by lock.validate() which is a precondition for this method. + + # #. If :ref:`pylock-requires-python` is specified, check that the environment + # being installed for meets the requirement; an error MUST be raised if it is + # not met. + if self.requires_python and not self.requires_python.contains( + env_python_full_version, + ): + raise PylockSelectError( + f"python_full_version {env_python_full_version!r} " + f"in provided environment does not satisfy the Python version " + f"requirement {str(self.requires_python)!r}" + ) + + # #. If :ref:`pylock-environments` is specified, check that at least one of the + # environment marker expressions is satisfied; an error MUST be raised if no + # expression is satisfied. + if self.environments: + for env_marker in self.environments: + if env_marker.evaluate( + cast("dict[str, str]", environment or {}), context="requirement" + ): + break + else: + raise PylockSelectError( + "Provided environment does not satisfy any of the " + "environments specified in the lock file" + ) + + # #. For each package listed in :ref:`pylock-packages`: + selected_packages_by_name: dict[str, tuple[int, Package]] = {} + for package_index, package in enumerate(self.packages): + # #. If :ref:`pylock-packages-marker` is specified, check if it is + # satisfied;if it isn't, skip to the next package. + if package.marker and not package.marker.evaluate(env, context="lock_file"): + continue + + # #. If :ref:`pylock-packages-requires-python` is specified, check if it is + # satisfied; an error MUST be raised if it isn't. + if package.requires_python and not package.requires_python.contains( + env_python_full_version, + ): + raise PylockSelectError( + f"python_full_version {env_python_full_version!r} " + f"in provided environment does not satisfy the Python version " + f"requirement {str(package.requires_python)!r} for package " + f"{package.name!r} at packages[{package_index}]" + ) + + # #. Check that no other conflicting instance of the package has been slated + # to be installed; an error about the ambiguity MUST be raised otherwise. + if package.name in selected_packages_by_name: + raise PylockSelectError( + f"Multiple packages with the name {package.name!r} are " + f"selected at packages[{package_index}] and " + f"packages[{selected_packages_by_name[package.name][0]}]" + ) + + # #. Check that the source of the package is specified appropriately (i.e. + # there are no conflicting sources in the package entry); + # an error MUST be raised if any issues are found. + # Covered by lock.validate() which is a precondition for this method. + + # #. Add the package to the set of packages to install. + selected_packages_by_name[package.name] = (package_index, package) + + # #. For each package to be installed: + for package_index, package in selected_packages_by_name.values(): + # - If :ref:`pylock-packages-vcs` is set: + if package.vcs is not None: + yield package, package.vcs + + # - Else if :ref:`pylock-packages-directory` is set: + elif package.directory is not None: + yield package, package.directory + + # - Else if :ref:`pylock-packages-archive` is set: + elif package.archive is not None: + yield package, package.archive + + # - Else if source preference selects an available + # :ref:`pylock-packages-sdist`: + elif ( + package.sdist is not None + and prefer_sdist_predicate is not None + and prefer_sdist_predicate(package.name) + ): + yield package, package.sdist + + # - Else if there are entries for :ref:`pylock-packages-wheels`: + elif package.wheels: + # #. Look for the appropriate wheel file based on + # :ref:`pylock-packages-wheels-name`; if one is not found then move + # on to :ref:`pylock-packages-sdist` or an error MUST be raised about + # a lack of source for the project. + best_wheel = next( + compatible_tags_selector( + (wheel, parse_wheel_filename(wheel.filename)[-1]) + for wheel in package.wheels + ), + None, + ) + if best_wheel: + yield package, best_wheel + elif package.sdist is not None: + yield package, package.sdist + else: + raise PylockSelectError( + f"No wheel found matching the provided tags " + f"for package {package.name!r} " + f"at packages[{package_index}], " + f"and no sdist available as a fallback" + ) + + # - Else if no :ref:`pylock-packages-wheels` file is found or + # :ref:`pylock-packages-sdist` is solely set: + elif package.sdist is not None: + yield package, package.sdist + + else: + # Covered by lock.validate() which is a precondition for this method. + raise NotImplementedError # pragma: no cover diff --git a/venv/lib/python3.12/site-packages/packaging/ranges.py b/venv/lib/python3.12/site-packages/packaging/ranges.py new file mode 100644 index 0000000..8144a65 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/ranges.py @@ -0,0 +1,2067 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +"""Public :class:`VersionRange` API. + +A set-algebra view of the versions accepted by a +:class:`~packaging.specifiers.SpecifierSet`. Ranges support intersection, +union, complement, and difference; membership and filtering match the +originating specifier set; and conversion back to a +:class:`~packaging.specifiers.SpecifierSet` is available where a PEP 440 form +exists. + +.. testsetup:: + + from packaging.ranges import VersionRange + from packaging.specifiers import SpecifierSet + from packaging.version import Version +""" + +from __future__ import annotations + +import enum +import typing +from typing import ( + TYPE_CHECKING, + Any, + TypeVar, + Union, +) + +from ._ranges import ( + FULL_RANGE, + MIN_VERSION, + NEG_INF, + POS_INF, + BoundaryKind, + BoundaryVersion, + LowerBound, + UpperBound, + coerce_version, + filter_by_ranges, + intersect_ranges, + least_version_above, + matches_bounds_only, + range_is_empty, + ranges_are_prerelease_only, + trim_release, +) +from .version import Version + +if TYPE_CHECKING: + from collections.abc import Callable, Iterable, Iterator, Sequence + + from ._ranges import Interval + from .specifiers import SpecifierSet + + +__all__ = ["VersionRange"] + +T = TypeVar("T") +UnparsedVersion = Union[Version, str] +UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) + +#: The most ``!=`` exclusion fragments (``!=V`` points or ``!=P.*`` prefixes) +#: that :meth:`VersionRange.to_specifier_set` will materialize to spell a +#: single gap or run. Every site that expands a version-number-driven chain +#: charges it against this cap, and chains that spell one gap together share +#: it (see :func:`_decompose_dev0_gap` and :func:`_encode_gap`), +#: so no gap ever materializes more than this many exclusions. Past the cap +#: the recovery returns ``None`` rather than emit the unbounded chain a range +#: such as ``==5.* | ==1000000.*`` would otherwise drive. +_MAX_EXCLUSION_RUN = 128 + + +class _SetOp(enum.Enum): + """The binary set operation ``_combine_literals`` resolves over ``===`` literals.""" + + INTERSECTION = enum.auto() + UNION = enum.auto() + DIFFERENCE = enum.auto() + + +def __dir__() -> list[str]: + return __all__ + + +# Range algebra: intersection and the empty-interval test live in the engine +# (``intersect_ranges`` / ``range_is_empty``); union and complement are only +# needed here, so they live in this module. + + +def _union_ranges( + left: Sequence[Interval], + right: Sequence[Interval], +) -> list[Interval]: + """Union two sorted, non-overlapping interval lists. + + A linear merge over the two pre-sorted inputs followed by a single + coalescing pass: adjacent or overlapping intervals collapse so the result + is itself sorted and non-overlapping. + """ + if not left: + return list(right) + if not right: + return list(left) + + merged_input: list[Interval] = [] + left_index = right_index = 0 + while left_index < len(left) and right_index < len(right): + if left[left_index][0] <= right[right_index][0]: + merged_input.append(left[left_index]) + left_index += 1 + else: + merged_input.append(right[right_index]) + right_index += 1 + merged_input.extend(left[left_index:]) + merged_input.extend(right[right_index:]) + + merged: list[Interval] = [merged_input[0]] + for lower, upper in merged_input[1:]: + prev_lower, prev_upper = merged[-1] + + if ( + prev_upper.version is None + or lower.version is None + or prev_upper.version > lower.version + ): + overlaps = True + elif prev_upper.version == lower.version: + overlaps = prev_upper.inclusive or lower.inclusive + else: + # An ordering gap may still hold no version when the two bounds + # straddle a synthetic boundary; merge across an empty gap to + # stay canonical. + gap_lower = LowerBound(prev_upper.version, not prev_upper.inclusive) + gap_upper = UpperBound(lower.version, not lower.inclusive) + overlaps = range_is_empty(gap_lower, gap_upper) + + if overlaps: + merged[-1] = (prev_lower, max(prev_upper, upper)) + else: + merged.append((lower, upper)) + + return merged + + +def _complement_ranges(ranges: Sequence[Interval]) -> list[Interval]: + """Complement a sorted, non-overlapping interval list. + + Yields the gaps between intervals plus a leading gap before the first and + a trailing gap after the last. Bound inclusivity flips so that + complement-of-complement round-trips back to the input. + """ + if not ranges: + return list(FULL_RANGE) + + result: list[Interval] = [] + prev_upper: UpperBound | None = None + + for lower, upper in ranges: + if prev_upper is None: + # Leading gap below the first interval. Every range reaching here is + # floor-canonical: ``_canonical_floor`` has already folded an + # inclusive lower at or below ``0.dev0`` into ``-inf``. So a finite + # first lower always leaves a non-empty gap down to ``-inf``, while a + # ``-inf`` lower leaves no leading gap at all. + if lower.version is not None: + gap_upper = UpperBound(lower.version, not lower.inclusive) + result.append((NEG_INF, gap_upper)) + else: + gap_lower = LowerBound(prev_upper.version, not prev_upper.inclusive) + gap_upper = UpperBound(lower.version, not lower.inclusive) + # Input intervals are canonical (sorted, disjoint, non-touching), + # so the gap between two of them always holds at least one version. + result.append((gap_lower, gap_upper)) + prev_upper = upper + + # The empty-input early return guarantees the loop ran. + assert prev_upper is not None + if prev_upper.version is not None: + gap_lower = LowerBound(prev_upper.version, not prev_upper.inclusive) + result.append((gap_lower, POS_INF)) + + return result + + +def _canonical_floor(bounds: tuple[Interval, ...]) -> tuple[Interval, ...]: + """Collapse the PEP 440 floor in a sorted interval list. + + Only the first interval can touch ``0.dev0`` (the minimum version). An + inclusive lower at or below it admits everything below, the same as + ``-inf``, so ``>=0.dev0`` becomes the one canonical full range. An + exclusive upper at or below it leaves the interval empty, so it is dropped. + """ + if not bounds: + return bounds + + lower, upper = bounds[0] + if range_is_empty(NEG_INF, upper): + return bounds[1:] + + if ( + lower.inclusive + and isinstance(lower.version, Version) + and lower.version <= MIN_VERSION + ): + return ((NEG_INF, upper), *bounds[1:]) + + return bounds + + +def _predecessor_boundary(version: Version) -> BoundaryVersion | None: + """The boundary whose least successor is *version*, or ``None``. + + Inverse of :func:`~packaging._ranges.least_version_above`. A plain version + that is exactly such a successor (``1.0a2.dev0`` sits just above + ``AFTER_POSTS(1.0a1)``) folds back to that boundary, so ``>=1.0a2.dev0`` and + ``>1.0a1`` share one form. The proposed boundary is confirmed by + round-tripping through ``least_version_above``. + """ + # Only a least successor carries a dev segment, so nothing else can fold. + if version.dev is None: + return None + + candidate: BoundaryVersion | None = None + if version.pre is not None and version.dev == 0 and version.post is None: + # 1.0a2.dev0 -> AFTER_POSTS(1.0a1) + kind, number = version.pre + if number >= 1: + candidate = BoundaryVersion( + version.__replace__(pre=(kind, number - 1), dev=None), + BoundaryKind.AFTER_POSTS, + ) + elif version.dev >= 1: + # 1.0.dev3 -> AFTER_LOCALS(1.0.dev2) + candidate = BoundaryVersion( + version.__replace__(dev=version.dev - 1), BoundaryKind.AFTER_LOCALS + ) + elif version.dev == 0 and version.post is not None: + # 1.0.post1.dev0 -> AFTER_LOCALS(1.0.post0); 1.0.post0.dev0 -> AFTER_LOCALS(1.0) + base = ( + version.__replace__(post=None, dev=None) + if version.post == 0 + else version.__replace__(post=version.post - 1, dev=None) + ) + candidate = BoundaryVersion(base, BoundaryKind.AFTER_LOCALS) + + if candidate is not None and least_version_above(candidate) == version: + return candidate + return None + + +def _canonicalize(bounds: tuple[Interval, ...]) -> tuple[Interval, ...]: + """Fold least-successor bounds to their boundary form. + + ``>=1.0a2.dev0`` and ``>1.0a1`` denote the same set, so both must reduce to + one representation for ``==`` and ``hash`` to agree. An inclusive lower or + exclusive upper sitting on a boundary's least successor becomes that + boundary; the engine's emptiness check has already dropped the synthetic + gaps such intervals would otherwise leave. + """ + result: list[Interval] = [] + for lower, upper in bounds: + new_lower, new_upper = lower, upper + + if isinstance(lower.version, Version) and lower.inclusive: + boundary = _predecessor_boundary(lower.version) + if boundary is not None: + new_lower = LowerBound(boundary, inclusive=False) + + if isinstance(upper.version, Version) and not upper.inclusive: + boundary = _predecessor_boundary(upper.version) + if boundary is not None: + new_upper = UpperBound(boundary, inclusive=True) + + result.append((new_lower, new_upper)) + return tuple(result) + + +def _struct_admits( + bounds: tuple[Interval, ...], admit_arbitrary: bool, literal: str +) -> bool: + """True when the bounds (plus arbitrary admission) admit ``literal``. + + Skips the explicit admit/reject sets, which the caller layers on top. A + non-version string matches via ``admit_arbitrary`` only on full bounds; + on narrower bounds the flag is metadata only. + """ + parsed = coerce_version(literal) + if parsed is None: + return admit_arbitrary and bounds == FULL_RANGE + + return matches_bounds_only(bounds, parsed) + + +# Repr helpers: + + +def _bound_version_str(value: BoundaryVersion | Version) -> str: + """Printout for a bound's inner value, kind-tagged for boundaries.""" + if isinstance(value, BoundaryVersion): + return f"{value.version}[{value.kind.name}]" + return str(value) + + +def _format_lower(bound: LowerBound) -> str: + if bound.version is None: + return "(-inf" + bracket = "[" if bound.inclusive else "(" + return f"{bracket}{_bound_version_str(bound.version)}" + + +def _format_upper(bound: UpperBound) -> str: + if bound.version is None: + return "+inf)" + bracket = "]" if bound.inclusive else ")" + return f"{_bound_version_str(bound.version)}{bracket}" + + +def _format_intervals(intervals: Sequence[Interval]) -> str: + """Render a sorted interval list as ``lower, upper | lower, upper``.""" + return " | ".join( + f"{_format_lower(lower)}, {_format_upper(upper)}" for lower, upper in intervals + ) + + +# ``to_specifier_set`` recovery: encode a range's interval list back into +# specifier fragments. Each helper returns ``None`` when its shape has no +# PEP 440 form. The ``keep_dev0`` argument threaded through is a spelling +# mode, not a pre-release policy: false emits a prerelease-free form (no +# synthetic ``.dev0``, so the recovered range has an empty opt-in region), +# true keeps the ``.dev0`` markers (so the range opts its bounds in). +# ``to_specifier_set`` encodes in both modes and keeps whichever round-trips. +# +# Bound and interval encoding: turn one interval's bounds into fragments. + + +def _is_dev0_version(version: Version) -> bool: + """True when version is exactly ``X[.Y]*.dev0`` (the shape `` list[str] | None: + """A prerelease-free spelling for an inclusive ``[version`` lower, or ``None``. + + Several ``[V`` lowers come from an operator whose own spelling carries no + synthetic ``.dev0``. Recovering that spelling gives the range an empty opt-in + region, so it is offered in the prerelease-free spelling mode (see + :meth:`VersionRange.to_specifier_set`). + """ + if version.dev != 0 or version.pre is not None or version.local is not None: + return None + + # ``[B.post(k).dev0`` is the lower ``>B.post(k-1)`` builds (k >= 1). + if version.post is not None: + if version.post < 1: + return None + return [f">{version.__replace__(post=version.post - 1, dev=None)}"] + + # ``[F.dev0`` is family F's base. The prefix P just below F has + # ``==P.* == [P.dev0, F.dev0)``, so ``>=P,!=P.*`` lands exactly on ``[F.dev0``. + family = trim_release(version.release) + last = family[-1] + if last < 1: + return None + + below_release = (*family[:-1], last - 1) + below = Version.from_parts(epoch=version.epoch, release=below_release) + + # At the epoch-0 floor ``==P.*`` already reaches ``0.dev0``, so the ``>=P`` + # half is redundant: ``[1.dev0, +inf)`` is plain ``!=0.*``. + if version.epoch == 0 and not any(below_release): + return [f"!={below}.*"] + + return [f">={below}", f"!={below}.*"] + + +def _epoch_floor_lower( + lower: LowerBound, upper: UpperBound +) -> tuple[Version, int, bool] | None: + """The ``E!0`` family of a lower sitting on an epoch>0 zero-family floor. + + An epoch>0 zero-family base such as ``1!0.dev0`` has no ``>=P,!=P.*`` spelling + since no version sorts below ``E!0`` within the epoch. While the interval + stays within ``==E!0.*`` it is that wildcard, trimmed by the upper and with a + leading ``.dev`` run excluded: an ``AFTER_LOCALS(E!0.dev(k))`` lower drops + ``E!0.dev0..E!0.dev(k)``, a plain inclusive ``E!0.dev0`` lower drops none. + Returns the ``E!0`` family, how many leading ``.dev`` releases to exclude, and + whether the upper sits at the family cap (so ``==E!0.*`` needs no upper), else + ``None``. + """ + version = lower.version + if isinstance(version, BoundaryVersion): + if version.kind != BoundaryKind.AFTER_LOCALS: + return None + version = version.version + if version.dev is None: + return None + excluded_devs = version.dev + 1 + elif isinstance(version, Version) and lower.inclusive: + # A plain inclusive lower only reaches the floor as ``>=E!0.dev0``; + # higher ``.dev`` would have canonicalized to an AFTER_LOCALS boundary. + if version.dev != 0: + return None + excluded_devs = 0 + else: + return None + + # Only the bare ``E!0`` floor of a non-zero epoch qualifies. + if version.epoch == 0: + return None + if version.pre is not None or version.post is not None or version.local is not None: + return None + if any(trim_release(version.release)): + return None + + # ``==E!0.*`` spans ``[E!0.dev0, E!1.dev0)``; it fits only below that cap. + next_family = Version.from_parts(epoch=version.epoch, release=(1,), dev=0) + cap = UpperBound(next_family, False) + if upper > cap: + return None + + family = Version.from_parts(epoch=version.epoch, release=(0,)) + return family, excluded_devs, upper == cap + + +def _dev_family_anchor(family: Version) -> list[str] | None: + """Prerelease-free fragments for ``[family, ..)``, or ``None`` if it has none. + + ``family`` is an ``X.dev0``. The floor gives ``[]`` (every version); a release + base its ``_clean_lower`` family-floor spelling (``!=0.*`` ...); an ``X.post0`` + base ``>=X,!=X``. A pre-release base has no prerelease-free spelling. + """ + if family <= MIN_VERSION: + return [] + clean = _clean_lower(family) + if clean is not None: + return clean + if family.pre is None and family.post == 0: + base = family.__replace__(post=None, dev=None) + return [f">={base}", f"!={base}"] + return None + + +def _encode_lower(lower: LowerBound, keep_dev0: bool) -> list[str] | None: + """Encode a lower bound as specifier fragments, or ``None``. + + ``[]`` for ``-inf``. An ``AFTER_POSTS(V)`` lower is ``>V``. An + ``AFTER_LOCALS(V)`` lower is the set ``[successor, ..)`` and emits ``>=V,!=V``, + except in the prerelease-free spelling mode (``keep_dev0`` false), where it + recovers a spelling with no synthetic ``.dev0`` when one exists: + ``>3.8.post1`` for a post release, or a dev family's anchor plus the dev run + up to V for a ``.dev`` release. + """ + lower_version = lower.version + if lower_version is None: + return [] + + if isinstance(lower_version, BoundaryVersion): + if lower_version.kind == BoundaryKind.AFTER_POSTS: + # AFTER_POSTS only ever appears as an exclusive ``>V`` lower. + return [f">{lower_version.version}"] + inner = lower_version.version + if inner <= MIN_VERSION: + # The ``(-inf, V)`` side was dropped at the floor, so the lone + # ``(AFTER_LOCALS(0.dev0), +inf)`` interval is exactly ``!=0.dev0``. + return [f"!={inner}"] + # An ``(AFTER_LOCALS(V), ..)`` lower is the set ``[successor, ..)``. In + # the prerelease-free mode, recover a spelling with no synthetic ``.dev0`` + # so the range's opt-in region stays empty. + if not keep_dev0: + if inner.dev is not None: + # A ``.dev`` V makes ``[successor, ..)`` its dev family's + # prerelease-free anchor minus the finite dev run up to ``V`` + # (e.g. ``>=1.0,!=1.0,!=1.0.post0.dev0``), carrying no synthetic + # ``.dev0``. + family = inner.__replace__(dev=0) + anchor = _dev_family_anchor(family) + if anchor is not None: + if inner.dev + 1 > _MAX_EXCLUSION_RUN: + return None + run = [ + f"!={family.__replace__(dev=d)}" for d in range(inner.dev + 1) + ] + return anchor + run + else: + # A ``.post`` release recovers its prerelease-free ``>`` spelling + # from the successor (``>3.8.post1`` for ``AFTER_LOCALS(3.8.post1)``). + successor = least_version_above(lower_version) + clean = _clean_lower(successor) if successor is not None else None + if clean is not None: + return clean + # Otherwise it is ``[V, ..)`` minus V's local family, i.e. ``>=V,!=V``. + # The prerelease-free recoveries above have already returned in that mode; + # a residual ``.dev`` V here opts pre-releases in, so this spelling round + # trips only for a range whose opt-in region wants it. + return [f">={inner}", f"!={inner}"] + + if not lower.inclusive: + return None + + # In the prerelease-free mode a ``.dev0`` lower prefers its clean spelling. + if not keep_dev0: + clean = _clean_lower(lower_version) + if clean is not None: + return clean + return [f">={lower_version}"] + + +def _encode_upper(upper: UpperBound, keep_dev0: bool) -> list[str] | None: + """Encode an upper bound as specifier fragments, or ``None``. + + ``[]`` for ``+inf``. In the prerelease-free spelling mode (``keep_dev0`` + false) the `` Version | None: + """If ``[lower, upper)`` is the ``==V.*`` shape, return ``V``.""" + if isinstance(lower.version, BoundaryVersion) or isinstance( + upper.version, BoundaryVersion + ): + return None + if lower.version is None or upper.version is None: + return None + if not lower.inclusive or upper.inclusive: + return None + if not (_is_dev0_version(lower.version) and _is_dev0_version(upper.version)): + return None + if lower.version.epoch != upper.version.epoch: + return None + + lower_release = trim_release(lower.version.release) + upper_release = trim_release(upper.version.release) + padded_length = max(len(lower_release), len(upper_release)) + assert padded_length > 0 + lower_release += (0,) * (padded_length - len(lower_release)) + upper_release += (0,) * (padded_length - len(upper_release)) + + if lower_release[:-1] != upper_release[:-1]: + return None + + # A genuine ``==V.*`` spans one family: the upper is exactly the next prefix. + # A wider span like ``[3.8.dev0, 3.14.dev0)`` shares the prefix but is not a + # single wildcard, so it falls through to the generic ``>=...,<...`` form. + if upper_release[-1] != lower_release[-1] + 1: + return None + + return lower.version.__replace__(release=lower_release, dev=None) + + +def _encode_interval( + lower: LowerBound, upper: UpperBound, keep_dev0: bool +) -> list[str] | None: + """Encode one interval as specifier fragments, or ``None``. + + Special-cases the ``==V`` singleton (``[V, AFTER_LOCALS(V)]`` for a plain + ``V``, and ``[V+local, V+local]`` for a local one) and the ``==V.*`` shape + so the fragment is one equality rather than a bound pair. + """ + # ``[V+local, V+local]`` (an inclusive local point) is the singleton ``==V+local``. + if ( + lower.version is not None + and upper.version is not None + and not isinstance(lower.version, BoundaryVersion) + and not isinstance(upper.version, BoundaryVersion) + and lower.inclusive + and upper.inclusive + and lower.version == upper.version + and lower.version.local is not None + ): + return [f"=={lower.version}"] + + # ``[V, AFTER_LOCALS(V)]`` (V without a local) is the singleton ``==V``, + # which also matches V's local family: one equality, not ``>=V,<=V``. + if ( + isinstance(lower.version, Version) + and lower.inclusive + and upper.inclusive + and isinstance(upper.version, BoundaryVersion) + and upper.version.kind == BoundaryKind.AFTER_LOCALS + and upper.version.version == lower.version + ): + return [f"=={lower.version}"] + + wildcard = _detect_equal_wildcard(lower, upper) + if wildcard is not None: + return [f"=={wildcard}.*"] + + # A ``[E!0.dev0`` lower has no prerelease-free ``>=`` spelling; within its own + # family it is ``==E!0.*`` trimmed by the upper. + floor = _epoch_floor_lower(lower, upper) if not keep_dev0 else None + if floor is not None: + family, excluded_devs, upper_at_cap = floor + if excluded_devs > _MAX_EXCLUSION_RUN: + return None + parts = [f"=={family}.*"] + parts.extend(f"!={family.__replace__(dev=d)}" for d in range(excluded_devs)) + + # ``==E!0.*`` already caps at the next family; add the upper only if tighter. + if not upper_at_cap: + upper_parts = _encode_upper(upper, keep_dev0) + if upper_parts is None: + return None + parts.extend(upper_parts) + + return parts + + lower_parts = _encode_lower(lower, keep_dev0) + if lower_parts is None: + return None + + upper_parts = _encode_upper(upper, keep_dev0) + if upper_parts is None: + return None + + return lower_parts + upper_parts + + +# Gap encoding: spell the gap between two adjacent intervals as exclusions. + + +def _detect_not_equal( + left_upper: UpperBound, right_lower: LowerBound +) -> list[Version] | None: + """If the gap between two intervals is a ``!=V`` chain, list its points. + + A plain exclusive left upper names the first excluded V directly; an inclusive + boundary left upper names it via its least successor. Adjacent exclusions + (``V`` and its immediate successors) share a single gap spanning a contiguous + dev run, so one gap can name a short chain: ``!=1.0,!=1.0.post0.dev0`` is one + gap from ``1.0`` up to ``AFTER_LOCALS(1.0.post0.dev0)``. + """ + if isinstance(left_upper.version, BoundaryVersion): + # A boundary upper is always inclusive; its least successor is the first + # excluded V (``None`` for a final AFTER_POSTS, which names no point). + first = least_version_above(left_upper.version) + if first is None: + return None + elif left_upper.version is None or left_upper.inclusive: + return None + else: + first = left_upper.version + + if not isinstance(right_lower.version, BoundaryVersion): + if ( + right_lower.version is not None + and not right_lower.inclusive + and right_lower.version == first + and first.local is not None + ): + return [first] + return None + + if right_lower.version.kind != BoundaryKind.AFTER_LOCALS: + return None + + # The right interval resumes just above the last excluded V and its locals. + last = right_lower.version.version + if first == last: + return [first] + + # Adjacent exclusions: the successor of ``first`` opens a ``.dev`` family and + # every later point is a higher ``.dev`` in that same family, so the gap is + # exactly ``first`` plus a contiguous dev run up to ``last``. Any other gap + # (e.g. ``2.3`` to ``AFTER_LOCALS(2.7)`` from complementing ``>=2.3,<=2.7``) + # spans a whole interval and fails this test. + second = least_version_above(BoundaryVersion(first, BoundaryKind.AFTER_LOCALS)) + if ( + second is not None + and second.dev is not None + and last.dev is not None + and last.dev >= second.dev + and last.__replace__(dev=second.dev) == second + ): + # ``first`` and the run spell this gap together, so they share the cap. + if last.dev - second.dev + 2 > _MAX_EXCLUSION_RUN: + return None + run = (second.__replace__(dev=d) for d in range(second.dev, last.dev + 1)) + return [first, *run] + return None + + +def _decompose_dev0_gap( + lower_trim: tuple[int, ...], + upper_trim: tuple[int, ...], + epoch: int, + budget: int = _MAX_EXCLUSION_RUN, +) -> list[Version] | None: + """Decompose the gap ``[L.dev0, U.dev0)`` into wildcard prefixes. + + ``lower_trim``/``upper_trim`` are trimmed release tuples with + ``lower_trim < upper_trim`` lexicographically. The chain sweeps at the + first differing level. The gap is undecomposable when L has trailing + components below that level (the chain cannot escape L's subtree), or when + the chain, summed across levels, would exceed ``budget`` prefixes. + """ + diff = 0 + while ( + diff < len(lower_trim) + and diff < len(upper_trim) + and lower_trim[diff] == upper_trim[diff] + ): + diff += 1 + + if len(lower_trim) > diff + 1: + return None + + common = lower_trim[:diff] + lower_val = lower_trim[diff] if len(lower_trim) > diff else 0 + upper_val = upper_trim[diff] + + span = upper_val - lower_val + if span > budget: + return None + + fragments = [ + Version.from_parts(epoch=epoch, release=(*common, segment)) + for segment in range(lower_val, upper_val) + ] + + if len(upper_trim) == diff + 1: + return fragments + + # Recurse into the next release component, charging at least one to the budget + # per level (not just the span), so a run of zero-span levels (a release with + # many trailing components) exhausts the budget and returns None instead of + # recursing past the interpreter's stack limit. + tail = _decompose_dev0_gap( + (*common, upper_val), upper_trim, epoch, budget - max(span, 1) + ) + if tail is None: + return None + return fragments + tail + + +def _encode_gap(left_upper: UpperBound, right_lower: LowerBound) -> list[str] | None: + """Encode the gap between two adjacent intervals as ``!=`` fragments. + + A point chain becomes ``!=V`` fragments and a dev0 family span becomes + ``!=P.*`` prefixes, followed by a leading dev run in the last family when + the gap ends inside it. Any other gap has no exclusion form and returns + ``None``. + """ + # Cheapest first: a plain ``!=V`` chain never pays for the budgeted + # wildcard sweep below. + points = _detect_not_equal(left_upper, right_lower) + if points is not None: + return [f"!={point}" for point in points] + + # Both wildcard shapes start at an exclusive family base ``L.dev0``. + left_v = left_upper.version + if ( + not isinstance(left_v, Version) + or left_upper.inclusive + or not _is_dev0_version(left_v) + ): + return None + + right_v = right_lower.version + if isinstance(right_v, Version) and right_lower.inclusive: + # ``[L.dev0, U.dev0)`` is a pure ``!=P.*`` chain up to U's family. + upper_dev0 = right_v + run_length = 0 + budget = _MAX_EXCLUSION_RUN + elif ( + isinstance(right_v, BoundaryVersion) + and right_v.kind == BoundaryKind.AFTER_LOCALS + and not right_lower.inclusive + ): + # ``[L.dev0, AFTER_LOCALS(U.dev(k))]`` ends inside U's family: the + # ``!=P.*`` chain, then the ``U.dev0..U.dev(k)`` run. Both spell the + # gap together, so they share one ``_MAX_EXCLUSION_RUN`` budget. + upper = right_v.version + if upper.dev is None or upper.dev + 1 > _MAX_EXCLUSION_RUN: + return None + upper_dev0 = upper.__replace__(dev=0) + run_length = upper.dev + 1 + budget = _MAX_EXCLUSION_RUN - run_length + else: + return None + + # ``U`` must be a plain release base in L's epoch, above L. + # ``_is_dev0_version`` on ``U.dev0`` rejects any pre/post/local on ``U``. + if not _is_dev0_version(upper_dev0): + return None + if left_v.epoch != upper_dev0.epoch or left_v >= upper_dev0: + return None + + prefixes = _decompose_dev0_gap( + trim_release(left_v.release), + trim_release(upper_dev0.release), + left_v.epoch, + budget, + ) + if prefixes is None: + return None + + exclusions = [f"!={prefix}.*" for prefix in prefixes] + exclusions.extend(f"!={upper_dev0.__replace__(dev=d)}" for d in range(run_length)) + return exclusions + + +def _encode_gaps(bounds: Sequence[Interval]) -> list[str] | None: + """Encode every between-interval gap as ``!=`` fragments, or ``None``. + + When each gap has an exclusion spelling, the intervals fuse into one + contiguous span (``==1.* | ==3.*`` is ``!=0.*,!=2.*,<4``): the outer + interval across all the bounds plus these exclusions. A gap with no + exclusion spelling makes the bounds a disjoint union, which no single + set expresses. + """ + exclusions: list[str] = [] + + for index in range(1, len(bounds)): + gap = _encode_gap(bounds[index - 1][1], bounds[index][0]) + if gap is None: + return None + exclusions.extend(gap) + + return exclusions + + +def _tighten_no_prereleases(bounds: tuple[Interval, ...]) -> tuple[Interval, ...]: + """Snap the range's final upper out of the pre-release band ``False`` drops. + + An exclusive upper at a final ``V`` admits the versions in ``[V.dev0, V)`` at + the bounds level, but a ``prereleases=False`` policy filters them all out, so + it accepts the same releases as ``>> r = SpecifierSet(">=1.0,<2.0").to_range() + >>> "1.5" in r + True + >>> "2.0" in r + False + >>> SpecifierSet(">=2.0,<1.0").to_range().is_empty + True + + PEP 440's ``===`` operator matches a candidate string verbatim + (case-insensitive) rather than a set of versions. Ranges built from + ``===`` specifiers still support membership, set operations, and conversion + back to a :class:`~packaging.specifiers.SpecifierSet`; matching follows the + literal-equality rule. A ``===`` literal that names a pre-release is + admitted under the default policy by both :meth:`contains` and + :meth:`filter`, since it was named outright. + + .. versionadded:: 26.3 + """ + + __slots__ = ( + "_admit", + "_admit_arbitrary", + "_bounds", + "_pre_region", + "_prereleases_configured", + "_reject", + ) + + #: The disjoint, sorted, non-overlapping interval list. + _bounds: tuple[Interval, ...] + + #: Whether this range matches non-version strings as well as versions. + #: True only by construction on ``SpecifierSet("")`` / :meth:`full`. The flag + #: rides set algebra but is inert except at full bounds (see + #: :meth:`_arbitrary_active`). An intersection or difference that shrinks + #: the bounds drops it (``full() & ~full()`` is plain empty, and + #: ``full() - r == full() & ~r``); :meth:`complement` and a union of + #: empty-bounds operands keep it, so ``~~full() == full()`` and + #: ``~full() | ~full() == ~full()``. Part of equality, since membership + #: reads it. + _admit_arbitrary: bool + + #: Case-folded strings the range admits in addition to its bounds. + #: ``===wat`` produces ``_admit = {"wat"}``. + _admit: frozenset[str] + + #: Case-folded strings the range rejects (overrides ``_admit`` and the + #: bounds). Populated by :meth:`complement` of an admit-bearing range and by + #: literal resolution in :meth:`_combine_literals`. + _reject: frozenset[str] + + #: Sorted, disjoint intervals where pre-releases are force-admitted under + #: the PEP 440 default policy (a ``None`` ``prereleases`` argument and no + #: configured override). The opt-in flows only from the pre-release-naming + #: specifiers that built the range. :meth:`_build` clips the region to the + #: bounds, so it is always a subset of them: an opt-in that overflowed its + #: own cap cannot ride a later union into versions no specifier asked for. + #: :meth:`union` and :meth:`intersection` accumulate the operands' clipped + #: regions and re-clip to the result bounds; :meth:`difference` keeps only + #: the minuend's; and :meth:`complement` drops it, since an exclusion grants + #: no opt-in. Equality keys on the clipped region, so it stays a congruence. + _pre_region: tuple[Interval, ...] + + #: Raw configured pre-release override of the originating specifier set + #: (an explicit ``True`` / ``False``, else ``None``). When set, :meth:`_build` + #: forces ``_pre_region`` empty since the policy governs globally. + #: :meth:`intersection` and :meth:`union` require it to match on both + #: operands. Part of equality. + _prereleases_configured: bool | None + + def __new__(cls, *args: object, **kwargs: object) -> VersionRange: # noqa: PYI034 + raise TypeError( + "cannot create 'VersionRange' instances directly; use " + "SpecifierSet.to_range(), VersionRange.full(), " + "VersionRange.empty(), or VersionRange.singleton() instead" + ) + + @classmethod + def _build( + cls, + bounds: tuple[Interval, ...], + admit: frozenset[str] = frozenset(), + reject: frozenset[str] = frozenset(), + admit_arbitrary: bool = False, + *, + pre_region: tuple[Interval, ...] = (), + prereleases_configured: bool | None = None, + ) -> VersionRange: + """Internal factory; bypasses :meth:`__new__`. + + Canonicalizes the bounds so equal version sets share one representation, + then drops admit literals the bounds already admit and reject literals + the bounds do not match anyway. Reject wins over admit on overlap. The + pre-release policy is set here and never reassigned afterwards; + ``pre_region`` is canonicalized like the bounds and clipped to them, + or dropped when a configured policy makes it inert. + """ + bounds = _canonicalize(bounds) + + if admit and reject: + admit = admit - reject + if admit: + admit = frozenset( + literal + for literal in admit + if not _struct_admits(bounds, admit_arbitrary, literal) + ) + if reject: + reject = frozenset( + literal + for literal in reject + if _struct_admits(bounds, admit_arbitrary, literal) + ) + + instance = object.__new__(cls) + instance._bounds = bounds + instance._admit = admit + instance._reject = reject + instance._admit_arbitrary = admit_arbitrary + instance._prereleases_configured = prereleases_configured + + # A configured policy makes the region inert, so drop it. Otherwise fold + # least-successor bounds (_from_specifier_set passes the region unfolded), + # so ``>1.0a1`` and ``>=1.0a2.dev0`` carry the same region, then clip it + # to the bounds so the opt-in never reaches past the range's own versions. + if prereleases_configured is not None or not pre_region: + instance._pre_region = () + else: + instance._pre_region = tuple( + intersect_ranges(_canonicalize(pre_region), bounds) + ) + + return instance + + def _has_literals(self) -> bool: + return bool(self._admit) or bool(self._reject) + + def _arbitrary_active(self) -> bool: + """True when ``_admit_arbitrary`` actually admits non-version strings. + + The flag rides through set algebra but only fires admission on full + bounds. Intersection and difference drop it when the bounds shrink, so + away from full bounds it survives only on empty-bounds ranges, where + it keeps ``~~full() == full()`` and union idempotent. + """ + return self._admit_arbitrary and self._bounds == FULL_RANGE + + def _is_plain(self) -> bool: + """True when membership is decided by ``_bounds`` alone, enabling the + bounds-only fast paths in :meth:`is_subset` and :meth:`is_disjoint`. + """ + return ( + not self._has_literals() + and not self._admit_arbitrary + and self._prereleases_configured is not False + ) + + def _check_policy_compat(self, other: VersionRange) -> None: + """Refuse combining ranges with different pre-release policies.""" + if not isinstance(other, VersionRange): + raise TypeError(f"expected VersionRange, got {type(other).__name__}") + if self._prereleases_configured != other._prereleases_configured: + raise ValueError( + "Cannot combine VersionRange operands with different " + f"pre-release policies: {self._prereleases_configured!r} " + f"and {other._prereleases_configured!r}" + ) + + def _merged_region(self, other: VersionRange) -> tuple[Interval, ...]: + """Union of ``self`` and ``other``'s opt-in regions. + + Used by :meth:`union` and :meth:`intersection`; :meth:`_build` clips the + merge to the result bounds. A configured operand carries an empty region, + so it contributes nothing to the merge. + """ + # Reuse an operand's canonical tuple when only one side has a region; + # an empty side contributes nothing to the union. + if not other._pre_region: + return self._pre_region + if not self._pre_region: + return other._pre_region + + # Both sides carry a region; merge them. _build re-canonicalizes and + # clips, so the plain union is fine here. + return tuple(_union_ranges(self._pre_region, other._pre_region)) + + def _with_policy( + self, *, pre_region: tuple[Interval, ...], configured: bool | None + ) -> VersionRange: + """A structural copy of this range carrying the given pre-release policy.""" + return self._build( + self._bounds, + admit=self._admit, + reject=self._reject, + admit_arbitrary=self._admit_arbitrary, + pre_region=pre_region, + prereleases_configured=configured, + ) + + @classmethod + def empty(cls, *, prereleases: bool | None = None) -> VersionRange: + """Return the empty range. No version satisfies it. + + >>> VersionRange.empty().is_empty + True + >>> "1.0" in VersionRange.empty() + False + """ + return cls._build((), prereleases_configured=prereleases) + + @classmethod + def full( + cls, *, admit_arbitrary: bool = True, prereleases: bool | None = None + ) -> VersionRange: + """Return the full range. Every PEP 440 version satisfies it. + + ``admit_arbitrary=False`` restricts the range to PEP 440 versions only + (matching the same versions as ``SpecifierSet(">=0.dev0").to_range()``); + its complement is :meth:`empty`. The flag propagates through set algebra + and is part of equality. Default ``True`` so that ``r & full()`` + preserves ``r``'s own flag structurally. + + >>> "1.0" in VersionRange.full() + True + >>> "wat" in VersionRange.full() + True + >>> "wat" in VersionRange.full(admit_arbitrary=False) + False + """ + return cls._build( + FULL_RANGE, + admit_arbitrary=admit_arbitrary, + prereleases_configured=prereleases, + ) + + @classmethod + def singleton( + cls, version: Version | str, *, prereleases: bool | None = None + ) -> VersionRange: + """Return the strict singleton range ``{version}``. + + Built as the closed interval ``[version, version]`` with strict + equality. ``Specifier("==V")`` matches ``V+local`` too, so the strict + singleton is narrower: + + >>> "1.0+local" in VersionRange.singleton("1.0") + False + >>> "1.0+local" in SpecifierSet("==1.0").to_range() + True + + :raises packaging.version.InvalidVersion: if version is a string that + does not parse as a PEP 440 version. + """ + if not isinstance(version, Version): + version = Version(version) + + lower = LowerBound(version, True) + upper = UpperBound(version, True) + + # Collapse the floor: nothing sorts below ``MIN_VERSION``, so the + # ``0.dev0`` singleton is ``(-inf, 0.dev0]`` in canonical form. + return cls._build( + _canonical_floor(((lower, upper),)), + prereleases_configured=prereleases, + ) + + def intersection(self, other: VersionRange) -> VersionRange: + """Range containing exactly the versions in both self and other. + + Both operands must share the same configured pre-release policy; + otherwise :exc:`ValueError` is raised. + + >>> a = SpecifierSet(">=1.0").to_range() + >>> b = SpecifierSet("<2.0").to_range() + >>> a.intersection(b) == SpecifierSet(">=1.0,<2.0").to_range() + True + """ + self._check_policy_compat(other) + + configured = self._prereleases_configured + new_bounds = tuple(intersect_ranges(self._bounds, other._bounds)) + new_region = self._merged_region(other) + + # An empty intersection (e.g. ``full() & ~full()``) is the empty range, + # so it drops the arbitrary flag, agreeing with difference when the + # subtrahend consumes the bounds. + combined_arb = ( + self._admit_arbitrary and other._admit_arbitrary and bool(new_bounds) + ) + + if not self._has_literals() and not other._has_literals(): + return self._build( + new_bounds, + admit_arbitrary=combined_arb, + pre_region=new_region, + prereleases_configured=configured, + ) + + return self._combine_literals( + other, + new_bounds, + op=_SetOp.INTERSECTION, + admit_arbitrary=combined_arb, + pre_region=new_region, + prereleases_configured=configured, + ) + + def union(self, other: VersionRange) -> VersionRange: + """Range containing every version in self or other. + + Both operands must share the same configured pre-release policy; + otherwise :exc:`ValueError` is raised. + + >>> a = VersionRange.singleton("1.0") + >>> b = VersionRange.singleton("2.0") + >>> "1.0" in a.union(b) and "2.0" in a.union(b) + True + >>> "1.5" in a.union(b) + False + """ + self._check_policy_compat(other) + + configured = self._prereleases_configured + new_bounds = tuple(_union_ranges(self._bounds, other._bounds)) + new_region = self._merged_region(other) + + # An empty-bounds operand (e.g. ``~full()``) carries an inert arbitrary + # flag only to keep complement an involution; it admits nothing, so it + # must not revive arbitrary admission as the union re-widens the bounds. + if new_bounds: + combined_arb = (self._admit_arbitrary and bool(self._bounds)) or ( + other._admit_arbitrary and bool(other._bounds) + ) + else: + # Nothing widened, so keeping the flags keeps ``r | r == r``. + combined_arb = self._admit_arbitrary or other._admit_arbitrary + + if not self._has_literals() and not other._has_literals(): + return self._build( + new_bounds, + admit_arbitrary=combined_arb, + pre_region=new_region, + prereleases_configured=configured, + ) + + return self._combine_literals( + other, + new_bounds, + op=_SetOp.UNION, + admit_arbitrary=combined_arb, + pre_region=new_region, + prereleases_configured=configured, + ) + + def complement(self) -> VersionRange: + """Range containing every version not in self. + + Preserves the configured pre-release policy. On the version set, double + negation holds for a range with no ``===`` literals (the arbitrary-string + flag round-trips, so ``~~full() == full()``); for ``===`` ranges + complement is one-way. The opt-in region is not restored (see below). + + The opt-in region is dropped: a complement is an exclusion, and an + exclusion expresses no pre-release preference. This is what lets + ``a & ~b`` shed ``b``'s opt-in, so an excluded ``b`` never force-admits a + pre-release into the result. Complement stays involutive on the version + set, but not on the opt-in region: ``~~r`` covers the same versions as + ``r`` yet force-admits none of its pre-releases. + + >>> r = SpecifierSet(">=1.0").to_range() + >>> "0.5" in r.complement() + True + >>> "1.5" in r.complement() + False + >>> r.complement().complement() == r + True + """ + # Complement swaps literal admission: what the range rejects, its + # complement admits. + return self._build( + tuple(_complement_ranges(self._bounds)), + admit=self._reject, + reject=self._admit, + admit_arbitrary=self._admit_arbitrary, + pre_region=(), + prereleases_configured=self._prereleases_configured, + ) + + def difference(self, other: VersionRange) -> VersionRange: + """Range containing the versions in self but not in other. + + Matches ``self & ~other`` on the version set and the opt-in region; + ``other`` acts as a bounds-only exclusion that grants no opt-in. The + arbitrary-string flag survives only when ``other`` removed no versions: + a difference that shrinks the bounds forgets it, as ``self & ~other`` + would, so no later widening union can revive it. They still part on + ``===`` literals, whose complement is one-way: a ``===`` literal stays + when ``self`` admits it and ``other`` does not. Both operands must + share the same configured pre-release policy (as :meth:`intersection` + and :meth:`union` require); otherwise :exc:`ValueError` is raised. + ``a - empty()`` returns a range equal to ``a``. + + >>> a = SpecifierSet(">=1.0").to_range() + >>> b = SpecifierSet(">=2.0").to_range() + >>> "1.5" in a.difference(b) + True + >>> "2.0" in a.difference(b) + False + >>> a.difference(VersionRange.empty()) == a + True + """ + self._check_policy_compat(other) + + # Subtracting a nothing-admitting set is a no-op; return self unchanged. + if not other._bounds and not other._admit: + return self + + # Bound complement is two-way, so subtracting other's versions is an + # intersection with its gaps. + new_bounds = tuple( + intersect_ranges(self._bounds, _complement_ranges(other._bounds)) + ) + + # Match ``self & ~other`` on the opt-in region: a complement carries no + # opt-in, so only ``self``'s region survives. ``other`` acts as a + # bounds-only exclusion. A configured ``self`` keeps no region. + new_region: tuple[Interval, ...] = () + if self._prereleases_configured is None: + new_region = self._pre_region + + # Keep self's arbitrary admission only when subtracting removed no + # versions. A difference that shrinks the bounds forgets the flag, as + # ``self & ~other`` would, so no later widening union can revive an + # admission neither operand had. + combined_arb = self._admit_arbitrary and new_bounds == self._bounds + + if not self._has_literals() and not other._has_literals(): + return self._build( + new_bounds, + admit_arbitrary=combined_arb, + pre_region=new_region, + prereleases_configured=self._prereleases_configured, + ) + + return self._combine_literals( + other, + new_bounds, + op=_SetOp.DIFFERENCE, + admit_arbitrary=combined_arb, + pre_region=new_region, + prereleases_configured=self._prereleases_configured, + ) + + def _combine_literals( + self, + other: VersionRange, + new_bounds: tuple[Interval, ...], + *, + op: _SetOp, + admit_arbitrary: bool, + pre_region: tuple[Interval, ...], + prereleases_configured: bool | None, + ) -> VersionRange: + """Resolve admit/reject for ``self`` ``op`` ``other`` over their literals.""" + admits: set[str] = set() + rejects: set[str] = set() + + # Each literal is decided independently of the others. + for literal in self._admit | self._reject | other._admit | other._reject: + self_in = self._matches_literal(literal) + other_in = other._matches_literal(literal) + + if op is _SetOp.INTERSECTION: + want = self_in and other_in + elif op is _SetOp.UNION: + want = self_in or other_in + else: + want = self_in and not other_in + + if want: + admits.add(literal) + else: + rejects.add(literal) + + return self._build( + new_bounds, + admit=frozenset(admits), + reject=frozenset(rejects), + admit_arbitrary=admit_arbitrary, + pre_region=pre_region, + prereleases_configured=prereleases_configured, + ) + + def _matches_literal(self, literal: str) -> bool: + """Whether literal (case-folded) matches this range's predicate.""" + if literal in self._reject: + return False + if literal in self._admit: + return True + + parsed = coerce_version(literal) + if parsed is None: + return self._arbitrary_active() + return matches_bounds_only(self._bounds, parsed) + + def __and__(self, other: object) -> VersionRange: + """Operator alias for :meth:`intersection`.""" + if not isinstance(other, VersionRange): + return NotImplemented + return self.intersection(other) + + def __or__(self, other: object) -> VersionRange: + """Operator alias for :meth:`union`.""" + if not isinstance(other, VersionRange): + return NotImplemented + return self.union(other) + + def __invert__(self) -> VersionRange: + """Operator alias for :meth:`complement`.""" + return self.complement() + + def __sub__(self, other: object) -> VersionRange: + """Operator alias for :meth:`difference`.""" + if not isinstance(other, VersionRange): + return NotImplemented + return self.difference(other) + + def is_subset(self, other: VersionRange) -> bool: + """Return whether every member of self is also a member of other. + + On versions and ``===`` literals this is + ``self.difference(other).is_empty``: subtracting other leaves nothing + behind. A live arbitrary admission (the flag at full bounds) is only a + subset of another live one. + + Both operands must share the same configured pre-release policy; + otherwise :exc:`ValueError` is raised. + + >>> inner = SpecifierSet(">=1.5,<1.8").to_range() + >>> outer = SpecifierSet(">=1.0,<2.0").to_range() + >>> inner.is_subset(outer) + True + >>> outer.is_subset(inner) + False + >>> VersionRange.empty().is_subset(outer) + True + """ + self._check_policy_compat(other) + + # A live arbitrary admission has non-version strings as members, which + # no bounds cover; only another live admission contains them. + if self._arbitrary_active() and not other._arbitrary_active(): + return False + + # Plain ranges: subset reduces to bounds containment, no algebra needed. + if self._is_plain() and other._is_plain(): + return not intersect_ranges(self._bounds, _complement_ranges(other._bounds)) + + # difference (unlike intersection with the one-way complement) resolves + # ``===`` literals against both operands, so it stays correct for them. + return self.difference(other).is_empty + + def is_superset(self, other: VersionRange) -> bool: + """Return whether every member of other is also a member of self. + + The mirror of :meth:`is_subset`: ``a.is_superset(b)`` is + ``b.is_subset(a)``. + + Both operands must share the same configured pre-release policy; + otherwise :exc:`ValueError` is raised. + + >>> outer = SpecifierSet(">=1.0,<2.0").to_range() + >>> outer.is_superset(SpecifierSet(">=1.5,<1.8").to_range()) + True + """ + # Type-guards a non-VersionRange other before delegating to is_subset. + self._check_policy_compat(other) + return other.is_subset(self) + + def is_disjoint(self, other: VersionRange) -> bool: + """Return whether self and other share no member. + + Equivalent to ``(self & other).is_empty``. + + Both operands must share the same configured pre-release policy; + otherwise :exc:`ValueError` is raised. + + >>> a = SpecifierSet(">=1.0,<2.0").to_range() + >>> a.is_disjoint(SpecifierSet(">=2.0,<3.0").to_range()) + True + >>> a.is_disjoint(SpecifierSet(">=1.5,<2.5").to_range()) + False + """ + self._check_policy_compat(other) + + # Plain ranges: disjointness is an empty bounds intersection. + if self._is_plain() and other._is_plain(): + return not intersect_ranges(self._bounds, other._bounds) + return self.intersection(other).is_empty + + def _same_releases(self, other: VersionRange) -> bool: + """Whether self and other admit the same non-pre-release versions. + + Used by :meth:`to_specifier_set` under a ``prereleases=False`` policy, + where pre-releases are unobservable: the symmetric difference is empty + exactly when the two ranges accept the same releases. Both operands + carry that policy, so the difference below reads emptiness through it. + """ + return self.difference(other).is_empty and other.difference(self).is_empty + + @typing.overload + def filter( + self, + iterable: Iterable[UnparsedVersionVar], + prereleases: bool | None = None, + key: None = ..., + ) -> Iterator[UnparsedVersionVar]: ... + + @typing.overload + def filter( + self, + iterable: Iterable[T], + prereleases: bool | None = None, + key: Callable[[T], UnparsedVersion] = ..., + ) -> Iterator[T]: ... + + def filter( + self, + iterable: Iterable[Any], + prereleases: bool | None = None, + key: Callable[[Any], Version | str] | None = None, + ) -> Iterator[Any]: + """Yield items from iterable whose version falls inside the range. + + With prereleases ``None`` the PEP 440 default applies: pre-releases are + buffered and only emitted if no final release in iterable is in range, + except that a pre-release inside the autodetected opt-in region, or named + outright by a ``===`` literal, is force-admitted in place (as + ``prereleases=True`` would yield it). A flushed buffer comes after + every in-place yield, so the output is not version-sorted. + + The signature mirrors + :meth:`~packaging.specifiers.SpecifierSet.filter`. + + >>> r = SpecifierSet(">=1.0,<2.0").to_range() + >>> list(r.filter(["0.9", "1.5", "2.0"])) + ['1.5'] + """ + region: tuple[Interval, ...] = () + if prereleases is None: + # The region applies only under the autodetect default; a configured + # policy governs instead (and then ``_pre_region`` is already empty). + prereleases = self._prereleases_configured + region = self._pre_region + + arbitrary_active = self._arbitrary_active() + if not self._admit and not self._reject and not arbitrary_active: + # A region spanning the whole bounds force-admits every in-bounds + # pre-release, i.e. ``prereleases=True``; take the cheaper no-buffer + # path. (Confined to this branch: the admission path orders arbitrary + # strings differently under True than under the region.) + if region and region == self._bounds: + return filter_by_ranges(self._bounds, iterable, key, True) + return filter_by_ranges(self._bounds, iterable, key, prereleases, region) + return self._filter_with_admission( + iterable, key, prereleases, arbitrary_active, region + ) + + def _filter_with_admission( + self, + iterable: Iterable[Any], + key: Callable[[Any], Version | str] | None, + prereleases: bool | None, + arbitrary_active: bool, + region: tuple[Interval, ...], + ) -> Iterator[Any]: + """Filter for ranges with admit/reject literals or live arbitrary + admission (including the universal ``SpecifierSet("")`` range).""" + admit_set = self._admit + reject_set = self._reject + + def admit(item: Any) -> tuple[bool, Version | None, bool]: # noqa: ANN401 + raw: Version | str = item if key is None else key(item) + raw_lower = str(raw).lower() + + if reject_set and raw_lower in reject_set: + return False, None, False + if admit_set and raw_lower in admit_set: + # An explicit ``===`` literal names this version outright. + return True, coerce_version(raw), True + + parsed = coerce_version(raw) + if parsed is None: + return arbitrary_active, None, False + if not matches_bounds_only(self._bounds, parsed): + return False, None, False + return True, parsed, False + + if prereleases is True: + for item in iterable: + ok, _, _ = admit(item) + if ok: + yield item + return + + if prereleases is False: + for item in iterable: + ok, parsed, _ = admit(item) + if not ok: + continue + if parsed is not None and parsed.is_prerelease: + continue + yield item + return + + # PEP 440 default: emit finals eagerly and buffer the other pre-releases, + # releasing the buffer only if no final ever matches. + all_nonfinal: list[Any] = [] + arbitrary_strings: list[Any] = [] + found_final = False + + for item in iterable: + ok, parsed, by_literal = admit(item) + if not ok: + continue + + if parsed is None: + if found_final: + yield item + else: + arbitrary_strings.append(item) + all_nonfinal.append(item) + continue + + if not parsed.is_prerelease: + if not found_final: + yield from arbitrary_strings + arbitrary_strings.clear() + found_final = True + yield item + continue + + # A pre-release is force-admitted when it is named outright by a + # ``===`` literal or falls in the opt-in region, as ``prereleases=True`` + # would yield it; otherwise the PEP 440 default buffers it. + if by_literal or (region and matches_bounds_only(region, parsed)): + yield item + continue + + if not found_final: + all_nonfinal.append(item) + + if not found_final: + yield from all_nonfinal + + @classmethod + def _from_specifier_set(cls, specifier_set: SpecifierSet) -> VersionRange: + """Build the range accepted by ``specifier_set``. + + Friend constructor for :meth:`~packaging.specifiers.SpecifierSet.to_range`. + The intersection of every specifier in the set: an empty set yields the + full range, an unsatisfiable set yields the empty range, and ``===`` + specifiers contribute literal-string admission. + """ + if not specifier_set: + result = cls.full() + elif not specifier_set._has_arbitrary: + result = cls._build( + bounds=_canonical_floor(tuple(specifier_set._get_ranges())) + ) + else: + result = cls.full() + for spec in specifier_set: + if spec.operator == "===": + operand = cls._build( + bounds=(), admit=frozenset({spec.version.lower()}) + ) + else: + operand = cls._build( + bounds=_canonical_floor(tuple(spec._to_ranges())) + ) + result = result.intersection(operand) + + # Each pre-release-naming specifier opts its own versions in; their union, + # clipped to the set's bounds by _build, is the region. Clipping refolds + # under intersection, so a set built directly equals one built by + # intersecting its specifiers one at a time. + region: list[Interval] = [] + if specifier_set._prereleases is None: # a configured policy has no region + for spec in specifier_set: + # ``===`` literals are not a range; filter force-admits them. + if spec.operator != "===" and spec.prereleases: + spec_bounds = _canonical_floor(tuple(spec._to_ranges())) + region = _union_ranges(region, spec_bounds) + + return result._with_policy( + pre_region=tuple(region), + configured=specifier_set._prereleases, + ) + + def to_specifier_set(self) -> SpecifierSet | None: + """Return a :class:`~packaging.specifiers.SpecifierSet` matching the same + versions as self, or ``None`` if no single set expresses it. + + PEP 440 has no syntax for the strict singleton ``{V}`` (an exclusive + plain-version bound), a disjoint union of two or more intervals, or a + partial pre-release opt-in region, so ranges built by set algebra often + return ``None``. A gap that takes more than ``_MAX_EXCLUSION_RUN`` + contiguous ``!=`` exclusions to spell returns ``None`` too, + rather than a pathologically long chain; reaching that cap takes either + set algebra or a specifier set that already spells the gap out with + over a hundred contiguous ``!=N.*`` exclusions. An empty range maps to + ``SpecifierSet("<0")``, unless it still carries the arbitrary-string + flag (which no set reproduces), and a full range that admits arbitrary + strings maps to ``SpecifierSet("")``. + + A range built from a :class:`~packaging.specifiers.SpecifierSet` + re-encodes, short of that exclusion cap. The result is the simplest + candidate whose own + :meth:`~packaging.specifiers.SpecifierSet.to_range` reproduces self + exactly (bounds, ``===`` literals, and the opt-in region are all part of + equality), so it filters the same versions. Two cases relax that + exactness without changing what is filtered: an empty range recovers as + the canonical empty range (same versions, none, but not self's bounds), + and under a ``prereleases=False`` policy the result need only match self's + releases, so ``(-inf, 3.14)`` recovers as the tighter ``<3.14`` rather + than ``!=3.14,<=3.14``. + + Each call encodes a handful of candidate spellings and keeps the + simplest one that verifies, where verifying means parsing the candidate + and round-tripping it through + :meth:`~packaging.specifiers.SpecifierSet.to_range`. The work grows + with the number of intervals and exclusions in the range, and the + result is not cached, so convert once and reuse the returned set rather + than converting per candidate version in a hot loop. + + >>> str(SpecifierSet(">=1.0,<2.0").to_range().to_specifier_set()) + '<2.0,>=1.0' + >>> str(SpecifierSet("==1.0").to_range().to_specifier_set()) + '==1.0' + >>> VersionRange.singleton("1.5").to_specifier_set() is None + True + """ + from .specifiers import InvalidSpecifier, SpecifierSet # noqa: PLC0415 + + configured = self._prereleases_configured + + if self._reject: + return None + if self._admit_arbitrary and self._bounds != FULL_RANGE: + return None + if self.is_empty: + # Every member-free spelling accepts the same versions (none), so the + # canonical ``<0`` stands in for all of them; a configured policy + # rides along it. + return SpecifierSet("<0", prereleases=configured) + + if not self._bounds: + # Pure ``===`` literals; only a single literal has a single-set form. + if len(self._admit) != 1: + return None + (literal,) = self._admit + bases = [f"==={literal}"] + elif self._admit: + # Bounds plus literals cannot be one set. + return None + elif self._bounds == FULL_RANGE: + bases = ["" if self._admit_arbitrary else ">=0.dev0"] + else: + # Under ``prereleases=False`` an exclusive final upper admits the same + # releases as ``=0.dev0`` floor restores a ``True`` opt-in that + # rode on a floor the clean encoding dropped (e.g. ``>=0.dev0,!=1.0``). + # It always recovers the whole bounds as the opt-in region, so it can + # only round-trip when self opts everything in. + add_floor = configured is None and self._pre_region == self._bounds + + # Keep the simplest candidate that recovers self. ``==`` compares bounds, + # literals, and the opt-in region, so a candidate that would filter + # differently, or an op-built range with no single-set form, is rejected + # below. Under ``prereleases=False`` a candidate need only match self's + # releases (policies never mix, so the excluded pre-releases are + # unobservable), which admits the tightened spellings above. + best: SpecifierSet | None = None + best_key = (0, 0) + + for base in bases: + candidates = [base] + if add_floor: + candidates.append(f"{base},>=0.dev0" if base else ">=0.dev0") + + for spec_str in candidates: + # A ``===`` literal can hold a comma (its arbitrary version + # excludes only whitespace, ``;`` and ``)``), which the set + # string splits on. Such a literal has no single specifier-set + # spelling, so drop the unparsable candidate and let the range + # fall through to ``None`` rather than raise. + try: + recovered = SpecifierSet(spec_str, prereleases=configured) + except InvalidSpecifier: + continue + # Fewest fragments, then shortest string. Rank before the round + # trip so a candidate that cannot beat the best skips the check + # (its ``==`` and, under ``False``, two ``difference`` calls). + key = (len(recovered), len(str(recovered))) + if best is not None and key >= best_key: + continue + + # Accept an exact round trip, or (under ``False``) one that only + # matches the releases the policy leaves observable. + candidate = recovered.to_range() + matches = candidate == self or ( + configured is False and self._same_releases(candidate) + ) + if matches: + best, best_key = recovered, key + + return best + + @property + def is_empty(self) -> bool: + """``True`` if no version or string satisfies this range. + + Agrees with :meth:`~packaging.specifiers.SpecifierSet.is_unsatisfiable`, + including the pre-release policy: a range whose only members are + pre-releases is empty when that policy excludes them. + + >>> SpecifierSet(">=2,<1").to_range().is_empty + True + >>> SpecifierSet(">=1,<2").to_range().is_empty + False + >>> SpecifierSet("==1.0a1", prereleases=False).to_range().is_empty + True + """ + # An arbitrary-string admission or a surviving ``===`` literal is a + # member; a literal that is a pre-release is dropped when the policy is. + if self._arbitrary_active(): + return False + + excludes_prereleases = self._prereleases_configured is False + for literal in self._admit: + if excludes_prereleases: + parsed = coerce_version(literal) + if parsed is not None and parsed.is_prerelease: + continue + return False + + if not self._bounds: + return True + + return excludes_prereleases and ranges_are_prerelease_only(self._bounds) + + def contains( + self, + item: Version | str, + prereleases: bool | None = None, + installed: bool | None = None, + ) -> bool: + """Return whether item is contained in this range. + + :param item: a version string or :class:`~packaging.version.Version`. + :param prereleases: whether to match pre-releases. ``None`` (default) + uses the range's own policy. + :param installed: when ``True``, accept a pre-release item even if the + range would not otherwise allow it. + + Unlike :meth:`filter`, this does not consult the autodetected pre-release + opt-in region; it reads only the configured policy. This mirrors + :meth:`~packaging.specifiers.SpecifierSet.contains` versus + :meth:`~packaging.specifiers.SpecifierSet.filter`. + + Unparsable strings do not match, except where the full + ``SpecifierSet`` would also match: the full range admits any string, + and a ``===`` range admits items equal to the literal + case-insensitively. + + >>> r = SpecifierSet(">=1.0,<2.0").to_range() + >>> r.contains("1.5") + True + >>> r.contains("2.0") + False + + :raises TypeError: if item is not a str or Version. + """ + if not isinstance(item, (str, Version)): + raise TypeError( + f"VersionRange.contains() expected str or Version, " + f"got {type(item).__name__}" + ) + + parsed: Version | None = item if isinstance(item, Version) else None + if installed and parsed is None: + parsed = coerce_version(item) + if installed and parsed is not None and parsed.is_prerelease: + prereleases = True + + effective_pre = ( + self._prereleases_configured if prereleases is None else prereleases + ) + + if self._admit or self._reject: + item_str = str(item).lower() + if item_str in self._reject: + return False + if item_str in self._admit: + if effective_pre is False: + literal_parsed = coerce_version(item_str) + if literal_parsed is not None and literal_parsed.is_prerelease: + return False + return True + + if not isinstance(item, Version): + if parsed is None: + parsed = coerce_version(item) + if parsed is None: + return self._arbitrary_active() + item = parsed + + if effective_pre is False and item.is_prerelease: + return False + return matches_bounds_only(self._bounds, item) + + def __contains__(self, item: Version | str) -> bool: + """Return whether item is contained in this range. + + Forwards to :meth:`contains` with default arguments. + + >>> "1.5" in SpecifierSet(">=1.0,<2.0").to_range() + True + """ + return self.contains(item) + + def __eq__(self, other: object) -> bool: + """Structural equality. + + Compares the bounds, the ``===`` admit/reject literals, the + arbitrary-string flag, the configured pre-release policy, and the + opt-in region, not just the version set. Keying on the region makes + equality a congruence (equal ranges stay equal under further operations), + so equal implies same :meth:`contains` and :meth:`filter`, but not the + converse: an empty range keeps the flag it was built with, so two empty + ranges need not be equal. + + Different specifiers for the same range fold to one canonical form: + + >>> SpecifierSet(">1.0a1").to_range() == SpecifierSet(">=1.0a2.dev0").to_range() + True + + The opt-in region is part of equality, so ``<=1.0`` (no pre-releases) and + ``<1.0.post0.dev0`` (autodetects a ``.dev`` opt-in) cover the same + versions yet compare unequal: + + >>> le, lt = SpecifierSet("<=1.0"), SpecifierSet("<1.0.post0.dev0") + >>> le.to_range() == lt.to_range() + False + + >>> r = SpecifierSet(">=1.0,<2.0").to_range() + >>> r == SpecifierSet(">=1.0,<2.0").to_range() + True + """ + if not isinstance(other, VersionRange): + return NotImplemented + return ( + self._bounds == other._bounds + and self._admit == other._admit + and self._reject == other._reject + and self._admit_arbitrary == other._admit_arbitrary + and self._prereleases_configured == other._prereleases_configured + and self._pre_region == other._pre_region + ) + + def __hash__(self) -> int: + return hash( + ( + self._bounds, + self._admit, + self._reject, + self._admit_arbitrary, + self._prereleases_configured, + self._pre_region, + ) + ) + + def __repr__(self) -> str: + """Human-readable representation for debugging. + + >>> SpecifierSet(">=1.0,<2.0").to_range() + + >>> SpecifierSet("").to_range() + + >>> SpecifierSet(">=2.0,<1.0").to_range() + + """ + # Body: the bounds and any ``===``-admitted literals. + parts: list[str] = [] + if self._bounds: + parts.append(_format_intervals(self._bounds)) + if self._admit: + parts.append("{" + ", ".join(sorted(self._admit)) + "}") + body = " | ".join(parts) if parts else "(empty)" + + # Rejected literals subtract from the body. + if self._reject: + body = f"{body} \\ {{{', '.join(sorted(self._reject))}}}" + + # Tail: the policy flags carried alongside the version set. + tail = "" + if self._admit_arbitrary: + tail += " arbitrary" + if self._prereleases_configured is not None: + tail += f" pre={self._prereleases_configured}" + if self._pre_region: + tail += f" pre-region={_format_intervals(self._pre_region)!r}" + + return f"<{self.__class__.__name__} {body!r}{tail}>" diff --git a/venv/lib/python3.12/site-packages/packaging/requirements.py b/venv/lib/python3.12/site-packages/packaging/requirements.py new file mode 100644 index 0000000..adcc8ad --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/requirements.py @@ -0,0 +1,192 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import annotations + +from typing import TYPE_CHECKING + +from ._parser import parse_requirement as _parse_requirement +from ._tokenizer import ParserSyntaxError +from .markers import Marker, _normalize_extra_values +from .specifiers import InvalidSpecifier, SpecifierSet +from .utils import canonicalize_name + +if TYPE_CHECKING: + from collections.abc import Iterator + +__all__ = [ + "InvalidRequirement", + "Requirement", +] + + +def __dir__() -> list[str]: + return __all__ + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + + .. versionadded:: 16.1 + """ + + +class Requirement: + """Parse a requirement. + + Parse a given requirement string into its parts, such as name, specifier, + URL, and extras. Raises InvalidRequirement on a badly-formed requirement + string. + + .. versionadded:: 16.1 + + .. versionchanged:: 22.0 + Added equality (``__eq__``) and hashing (``__hash__``) so requirements + can be compared and stored in sets / dicts. + + .. versionchanged:: 23.2 + Equality and hashing began canonicalizing requirement names, so + requirements whose names differ only by normalization (e.g. + ``Requirement("Foo")`` vs ``Requirement("foo")``) now compare and hash + equal. + + Instances are safe to serialize with :mod:`pickle`. They use a stable + format so the same pickle can be loaded in future packaging releases. + + .. versionchanged:: 26.2 + + Added a stable pickle format. Pickles created with packaging 26.2+ can + be unpickled with future releases. Backward compatibility with pickles + from packaging < 26.2 is supported but may be removed in a future + release. + + .. versionchanged:: 26.3 + + The dedicated pickle support introduced in 26.2 did not preserve the + specifier's explicit :attr:`~packaging.specifiers.SpecifierSet.prereleases` + override; it is now included again. + + Equality and hashing normalize requirement names, extras, and + equivalent specifiers. The string representation still preserves the + parsed name and extras spelling. + """ + + # TODO: Can we test whether something is contained within a requirement? + # If so how do we do that? Do we need to test against the _name_ of + # the thing as well as the version? What about the markers? + # TODO: Can we normalize the name and extra name? + + __slots__ = ("extras", "marker", "name", "specifier", "url") + + def __init__(self, requirement_string: str) -> None: + try: + parsed = _parse_requirement(requirement_string) + except ParserSyntaxError as e: + raise InvalidRequirement(str(e)) from e + + self.name: str = parsed.name + self.url: str | None = parsed.url or None + self.extras: set[str] = set(parsed.extras) + try: + self.specifier: SpecifierSet = SpecifierSet(parsed.specifier) + except InvalidSpecifier as e: + raise InvalidRequirement(str(e)) from e + self.marker: Marker | None = None + if parsed.marker is not None: + self.marker = Marker.__new__(Marker) + self.marker._markers = _normalize_extra_values(parsed.marker) + + def _iter_parts(self, name: str) -> Iterator[str]: + yield name + + if self.extras: + formatted_extras = ",".join(sorted(self.extras)) + yield f"[{formatted_extras}]" + + if self.specifier: + yield str(self.specifier) + + if self.url: + yield f" @ {self.url}" + if self.marker: + yield " " + + if self.marker: + yield f"; {self.marker}" + + def __getstate__(self) -> tuple[str, bool | None]: + # Return the requirement string for compactness and stability, paired + # with the specifier's explicit prereleases override, which is not + # captured by the string form. Re-parsed on load to reconstruct all + # other fields. + return (str(self), self.specifier._prereleases) + + def __setstate__(self, state: object) -> None: + if isinstance(state, str): + # Format (26.2): just the requirement string. + requirement_string: str = state + prereleases: bool | None = None + elif ( + isinstance(state, tuple) + and len(state) == 2 + and isinstance(state[0], str) + and (state[1] is None or isinstance(state[1], bool)) + ): + # New format (26.3+): (requirement string, specifier prereleases). + requirement_string, prereleases = state + elif isinstance(state, dict) and state.keys() >= set(self.__slots__): + # Old format (packaging <= 26.1, no __slots__): plain __dict__. + for key in self.__slots__: + setattr(self, key, state[key]) + return + else: + raise TypeError(f"Cannot restore Requirement from {state!r}") + + try: + tmp = Requirement(requirement_string) + except InvalidRequirement as exc: + raise TypeError(f"Cannot restore Requirement from {state!r}") from exc + self.name = tmp.name + self.url = tmp.url + self.extras = tmp.extras + self.specifier = tmp.specifier + self.specifier._prereleases = prereleases + self.marker = tmp.marker + + def __str__(self) -> str: + return "".join(self._iter_parts(self.name)) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}({str(self)!r})>" + + def __hash__(self) -> int: + # Mirror __eq__ by hashing the canonical specifier object rather than + # its raw string. ``_iter_parts`` yields ``str(self.specifier)``, which + # is non-canonical, so trailing-zero-equivalent requirements such as + # ``foo==1.0.0`` and ``foo==1.0.0.0`` (which compare equal) would + # otherwise hash differently, breaking the hash/__eq__ invariant. + return hash( + ( + canonicalize_name(self.name), + frozenset(canonicalize_name(e) for e in self.extras), + self.specifier, + self.url, + self.marker, + ) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Requirement): + return NotImplemented + + # Extras must be normalized before comparison as per PEP 685. + self_extras = frozenset(canonicalize_name(e) for e in self.extras) + other_extras = frozenset(canonicalize_name(e) for e in other.extras) + return ( + canonicalize_name(self.name) == canonicalize_name(other.name) + and self_extras == other_extras + and self.specifier == other.specifier + and self.url == other.url + and self.marker == other.marker + ) diff --git a/venv/lib/python3.12/site-packages/packaging/specifiers.py b/venv/lib/python3.12/site-packages/packaging/specifiers.py new file mode 100644 index 0000000..a6a61af --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/specifiers.py @@ -0,0 +1,1457 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +""" +.. testsetup:: + + from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier + from packaging.version import Version +""" + +from __future__ import annotations + +import abc +import re +import typing +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Final, + TypeVar, + Union, +) + +from ._ranges import ( + FULL_RANGE, + bounds_for_spec, + coerce_version, + filter_by_ranges, + intersect_specifier_bounds, + matches_bounds_only, + ranges_are_prerelease_only, + resolve_prereleases, + trim_release, +) +from .utils import canonicalize_version +from .version import Version + +if TYPE_CHECKING: + import sys + from collections.abc import Iterable, Iterator, Sequence + + if sys.version_info >= (3, 10): + from typing import TypeGuard + else: + from typing_extensions import TypeGuard + + from . import ranges + from ._ranges import Interval + + +__all__ = [ + "BaseSpecifier", + "InvalidSpecifier", + "Specifier", + "SpecifierSet", +] + + +def __dir__() -> list[str]: + return __all__ + + +def _validate_spec(spec: object, /) -> TypeGuard[tuple[str, str]]: + return ( + isinstance(spec, tuple) + and len(spec) == 2 + and isinstance(spec[0], str) + and isinstance(spec[1], str) + ) + + +def _validate_pre(pre: object, /) -> TypeGuard[bool | None]: + return pre is None or isinstance(pre, bool) + + +T = TypeVar("T") +UnparsedVersion = Union[Version, str] +UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) + + +# Operators whose result is just a direct Version comparison, given a parsed +# item with no local. ``<=``/``==``/``!=`` need that no-local guard because +# PEP 440 strips locals on those; ``>=`` works regardless. +_DIRECT_COMPARE_OPS: dict[str, Callable[[Version, Version], bool]] = { + ">=": Version.__ge__, + "<=": Version.__le__, + "==": Version.__eq__, + "!=": Version.__ne__, +} + + +def _fast_match(specifier: Specifier, parsed: Version) -> bool | None: + """Match ``parsed`` against ``specifier`` without building a range. + + Handles ``>=``, ``<=``, ``==``, ``!=``, ``<``, ``>`` when the spec is + not a wildcard and ``parsed`` has no local. Returns ``None`` when the + range path must be used. Pre-release policy is left to the caller. + """ + op_str, ver_str = specifier._spec + if ver_str.endswith(".*") or parsed.local is not None: + return None + + direct_compare = _DIRECT_COMPARE_OPS.get(op_str) + if direct_compare is not None: + return direct_compare(parsed, specifier._require_spec_version(ver_str)) + + if op_str in ("<", ">"): + spec_v = specifier._require_spec_version(ver_str) + # ``V`` carve out V's family (pre/dev/post); that only + # matters when parsed shares V's epoch and trimmed release. + # Otherwise a direct cmpkey comparison is correct. + if parsed.epoch != spec_v.epoch or trim_release(parsed.release) != trim_release( + spec_v.release + ): + return parsed < spec_v if op_str == "<" else parsed > spec_v + return None + + return None + + +class InvalidSpecifier(ValueError): + """ + Raised when attempting to create a :class:`Specifier` with a specifier + string that is invalid. + + >>> Specifier("lolwat") + Traceback (most recent call last): + ... + packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat' + """ + + +class BaseSpecifier(metaclass=abc.ABCMeta): + """ + Abstract base class for :class:`Specifier` and :class:`SpecifierSet`. + """ + + __slots__ = () + __match_args__ = ("_str",) + + @property + def _str(self) -> str: + """Internal property for match_args""" + return str(self) + + @abc.abstractmethod + def __str__(self) -> str: + """ + Returns the str representation of this Specifier-like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self) -> int: + """ + Returns a hash value for this Specifier-like object. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Returns a boolean representing whether or not the two Specifier-like + objects are equal. + + :param other: The other object to check against. + """ + + @property + @abc.abstractmethod + def prereleases(self) -> bool | None: + """Whether or not pre-releases as a whole are allowed. + + This can be set to either ``True`` or ``False`` to explicitly enable or disable + prereleases or it can be set to ``None`` (the default) to use default semantics. + """ + + @prereleases.setter # noqa: B027 + def prereleases(self, value: bool) -> None: + """Setter for :attr:`prereleases`. + + :param value: The value to set. + """ + + @abc.abstractmethod + def contains(self, item: str, prereleases: bool | None = None) -> bool: + """ + Determines if the given item is contained within this specifier. + """ + + @typing.overload + def filter( + self, + iterable: Iterable[UnparsedVersionVar], + prereleases: bool | None = None, + key: None = ..., + ) -> Iterator[UnparsedVersionVar]: ... + + @typing.overload + def filter( + self, + iterable: Iterable[T], + prereleases: bool | None = None, + key: Callable[[T], UnparsedVersion] = ..., + ) -> Iterator[T]: ... + + @abc.abstractmethod + def filter( + self, + iterable: Iterable[Any], + prereleases: bool | None = None, + key: Callable[[Any], UnparsedVersion] | None = None, + ) -> Iterator[Any]: + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class Specifier(BaseSpecifier): + """This class abstracts handling of version specifiers. + + .. tip:: + + It is generally not required to instantiate this manually. You should instead + prefer to work with :class:`SpecifierSet` instead, which can parse + comma-separated version specifiers (which is what package metadata contains). + + Instances are safe to serialize with :mod:`pickle`. They use a stable + format so the same pickle can be loaded in future packaging releases. + + .. versionchanged:: 26.2 + + Added a stable pickle format. Pickles created with packaging 26.2+ can + be unpickled with future releases. Backward compatibility with pickles + from packaging < 26.2 is supported but may be removed in a future + release. + """ + + __slots__ = ( + "_prereleases", + "_ranges", + "_spec", + "_spec_version", + ) + + _specifier_regex_str = r""" + (?: + (?: + # The identity operators allow for an escape hatch that will + # do an exact string match of the version you wish to install. + # This will not be parsed by PEP 440 and we cannot determine + # any semantic meaning from it. This operator is discouraged + # but included entirely as an escape hatch. + === # Only match for the identity operator + \s* + [^\s;)]* # The arbitrary version can be just about anything, + # we match everything except for whitespace, a + # semi-colon for marker support, and a closing paren + # since versions can be enclosed in them. + ) + | + (?: + # The (non)equality operators allow for wild card and local + # versions to be specified so we have to define these two + # operators separately to enable that. + (?:==|!=) # Only match for equals and not equals + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + + # You cannot use a wild card and a pre-release, post-release, a dev or + # local version together so group them with a | and make them optional. + (?: + \.\* # Wild card syntax of .* + | + (?a: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?a: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?a:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + )? + ) + | + (?: + # The compatible operator requires at least two digits in the + # release segment. + (?:~=) # Only match for the compatible operator + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) + (?: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + | + (?: + # All other operators only allow a sub set of what the + # (non)equality operators do. Specifically they do not allow + # local versions to be specified nor do they allow the prefix + # matching wild cards. + (?:<=|>=|<|>) + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?a: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?a: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + ) + """ + + _regex = re.compile( + r"\s*" + _specifier_regex_str + r"\s*", re.VERBOSE | re.IGNORECASE + ) + + # Legacy unused attribute, kept for backward compatibility + _operators: Final = { + "~=": "compatible", + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + "===": "arbitrary", + } + + def __init__(self, spec: str = "", prereleases: bool | None = None) -> None: + """Initialize a Specifier instance. + + :param spec: + The string representation of a specifier which will be parsed and + normalized before use. + :param prereleases: + This tells the specifier if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + :raises InvalidSpecifier: + If the given specifier is invalid (i.e. bad syntax). + """ + if not self._regex.fullmatch(spec): + raise InvalidSpecifier(f"Invalid specifier: {spec!r}") + + spec = spec.strip() + if spec.startswith("==="): + operator, version = spec[:3], spec[3:].strip() + elif spec.startswith(("~=", "==", "!=", "<=", ">=")): + operator, version = spec[:2], spec[2:].strip() + else: + operator, version = spec[:1], spec[1:].strip() + + self._spec: tuple[str, str] = (operator, version) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + # Specifier version cache + self._spec_version: tuple[str, Version] | None = None + + # Version range cache (populated by _to_ranges) + self._ranges: Sequence[Interval] | None = None + + def _get_spec_version(self, version: str) -> Version | None: + """One element cache, as only one spec Version is needed per Specifier.""" + if self._spec_version is not None and self._spec_version[0] == version: + return self._spec_version[1] + + version_specifier = coerce_version(version) + if version_specifier is None: + return None + + self._spec_version = (version, version_specifier) + return version_specifier + + def _require_spec_version(self, version: str) -> Version: + """Get spec version, asserting it's valid (not for === operator). + + This method should only be called for operators where version + strings are guaranteed to be valid PEP 440 versions (not ===). + """ + spec_version = self._get_spec_version(version) + assert spec_version is not None + return spec_version + + def _to_ranges(self) -> Sequence[Interval]: + """Convert this specifier to sorted, non-overlapping version ranges. + + Each standard operator maps to one or two ranges. ``===`` is + modeled as full range (actual check done separately). Cached. + """ + if self._ranges is not None: + return self._ranges + + op = self.operator + ver_str = self.version + + if op == "===": + result: Sequence[Interval] = FULL_RANGE + else: + version = self._require_spec_version(ver_str.removesuffix(".*")) + result = bounds_for_spec(op, ver_str, version) + + self._ranges = result + return result + + @property + def prereleases(self) -> bool | None: + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Only the "!=" operator does not imply prereleases when + # the version in the specifier is a prerelease. + operator, version_str = self._spec + if operator == "!=": + return False + + # The == specifier with trailing .* cannot include prereleases + # e.g. "==1.0a1.*" is not valid. + if operator == "==" and version_str.endswith(".*"): + return False + + # "===" can have arbitrary string versions, so we cannot parse + # those, we take prereleases as unknown (None) for those. + version = self._get_spec_version(version_str) + if version is None: + return None + + # For all other operators, use the check if spec Version + # object implies pre-releases. + return version.is_prerelease + + @prereleases.setter + def prereleases(self, value: bool | None) -> None: + self._prereleases = value + + def __getstate__(self) -> tuple[tuple[str, str], bool | None]: + # Return state as a 2-item tuple for compactness: + # ((operator, version), prereleases) + # Cache members are excluded and will be recomputed on demand. + return (self._spec, self._prereleases) + + def __setstate__(self, state: object) -> None: + # Always discard cached values - they will be recomputed on demand. + self._spec_version = None + self._ranges = None + + if isinstance(state, tuple): + if len(state) == 2: + # New format (26.2+): ((operator, version), prereleases) + spec, prereleases = state + if _validate_spec(spec) and _validate_pre(prereleases): + self._spec = spec + self._prereleases = prereleases + return + if len(state) == 2 and isinstance(state[1], dict): + # Format (packaging 26.0-26.1): (None, {slot: value}). + _, slot_dict = state + spec = slot_dict.get("_spec") + prereleases = slot_dict.get("_prereleases", "invalid") + if _validate_spec(spec) and _validate_pre(prereleases): + self._spec = spec + self._prereleases = prereleases + return + if isinstance(state, dict): + # Old format (packaging <= 25.x, no __slots__): state is a plain dict. + spec = state.get("_spec") + prereleases = state.get("_prereleases", "invalid") + if _validate_spec(spec) and _validate_pre(prereleases): + self._spec = spec + self._prereleases = prereleases + return + + raise TypeError(f"Cannot restore Specifier from {state!r}") + + @property + def operator(self) -> str: + """The operator of this specifier. + + >>> Specifier("==1.2.3").operator + '==' + """ + return self._spec[0] + + @property + def version(self) -> str: + """The version of this specifier. + + >>> Specifier("==1.2.3").version + '1.2.3' + """ + return self._spec[1] + + def __repr__(self) -> str: + """A representation of the Specifier that shows all internal state. + + >>> Specifier('>=1.0.0') + =1.0.0')> + >>> Specifier('>=1.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> Specifier('>=1.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"<{self.__class__.__name__}({str(self)!r}{pre})>" + + def __str__(self) -> str: + """A string representation of the Specifier that can be round-tripped. + + >>> str(Specifier('>=1.0.0')) + '>=1.0.0' + >>> str(Specifier('>=1.0.0', prereleases=False)) + '>=1.0.0' + """ + return "{}{}".format(*self._spec) + + @property + def _canonical_spec(self) -> tuple[str, str]: + operator, version = self._spec + if operator == "===" or version.endswith(".*"): + return operator, version + + spec_version = self._require_spec_version(version) + + canonical_version = canonicalize_version( + spec_version, strip_trailing_zero=(operator != "~=") + ) + + return operator, canonical_version + + def __hash__(self) -> int: + return hash(self._canonical_spec) + + def __eq__(self, other: object) -> bool: + """Whether or not the two Specifier-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0") + True + >>> (Specifier("==1.2.3", prereleases=False) == + ... Specifier("==1.2.3", prereleases=True)) + True + >>> Specifier("==1.2.3") == "==1.2.3" + True + >>> Specifier("==1.2.3") == Specifier("==1.2.4") + False + >>> Specifier("==1.2.3") == Specifier("~=1.2.3") + False + """ + if isinstance(other, str): + try: + other = self.__class__(str(other)) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._canonical_spec == other._canonical_spec + + def __contains__(self, item: str | Version) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in Specifier(">=1.2.3") + True + >>> Version("1.2.3") in Specifier(">=1.2.3") + True + >>> "1.0.0" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3") + True + >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True) + True + """ + return self.contains(item) + + def contains(self, item: UnparsedVersion, prereleases: bool | None = None) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: + The item to check for, which can be a version string or a + :class:`~packaging.version.Version` instance. + :param prereleases: + Whether or not to match prereleases with this Specifier. If set to + ``None`` (the default), it will follow the recommendation from + :pep:`440` and match prereleases, as there are no other versions. + + >>> Specifier(">=1.2.3").contains("1.2.3") + True + >>> Specifier(">=1.2.3").contains(Version("1.2.3")) + True + >>> Specifier(">=1.2.3").contains("1.0.0") + False + >>> Specifier(">=1.2.3").contains("1.3.0a1") + True + >>> Specifier(">=1.2.3", prereleases=False).contains("1.3.0a1") + False + >>> Specifier(">=1.2.3").contains("1.3.0a1") + True + + .. versionchanged:: 26.0 + + With ``prereleases=None``, a prerelease now matches. A single + version has no alternatives, so the :pep:`440` rule to accept + prereleases when nothing else satisfies the specifier applies. + Earlier versions rejected it. An unparsable version now returns + ``False`` instead of raising :exc:`~packaging.version.InvalidVersion`. + """ + # ``===`` compares the raw string, so a Version parse here would + # be wasted. + if self._spec[0] == "===": + return bool(list(self.filter([item], prereleases=prereleases))) + + parsed = coerce_version(item) + if parsed is None: + # Standard operators never match an unparsable input. + return False + + if prereleases is None: + prereleases = resolve_prereleases(self._prereleases, self.prereleases) + + if prereleases is False and parsed.is_prerelease: + return False + + # ``_fast_match`` answers the simple operators without building a + # range; otherwise fall back to the engine's bounds membership. + match = _fast_match(self, parsed) + if match is not None: + return match + + return matches_bounds_only(self._to_ranges(), parsed) + + @typing.overload + def filter( + self, + iterable: Iterable[UnparsedVersionVar], + prereleases: bool | None = None, + key: None = ..., + ) -> Iterator[UnparsedVersionVar]: ... + + @typing.overload + def filter( + self, + iterable: Iterable[T], + prereleases: bool | None = None, + key: Callable[[T], UnparsedVersion] = ..., + ) -> Iterator[T]: ... + + def filter( + self, + iterable: Iterable[Any], + prereleases: bool | None = None, + key: Callable[[Any], UnparsedVersion] | None = None, + ) -> Iterator[Any]: + """Filter items in the given iterable, that match the specifier. + + :param iterable: + An iterable that can contain version strings and + :class:`~packaging.version.Version` instances. The items in the + iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will follow the recommendation from :pep:`440` + and match prereleases if there are no other versions. + :param key: + A callable that takes a single argument (an item from the iterable) and + returns a version string or :class:`~packaging.version.Version` + instance to be used for filtering. + + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")])) + ['1.2.3', '1.3', ] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"])) + ['1.5a1'] + >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + >>> list(Specifier(">=1.2.3").filter( + ... [{"ver": "1.2"}, {"ver": "1.3"}], + ... key=lambda x: x["ver"])) + [{'ver': '1.3'}] + + .. versionchanged:: 26.1 + + Added the ``key`` parameter. + """ + if prereleases is None: + prereleases = resolve_prereleases(self._prereleases, self.prereleases) + + if self.operator == "===": + spec_lower = self.version.lower() + matches = ( + item + for item in iterable + if str(item if key is None else key(item)).lower() == spec_lower + ) + return _apply_prereleases_filter(matches, key, prereleases) + + return filter_by_ranges(self._to_ranges(), iterable, key, prereleases) + + +def _apply_prereleases_filter( + matches: Iterable[Any], + key: Callable[[Any], UnparsedVersion] | None, + prereleases: bool | None, +) -> Iterator[Any]: + """Apply ``prereleases=`` handling to an already-matched iterable. + + ``None`` means PEP 440 default (buffer pre-releases until a final + appears); ``True`` yields everything; ``False`` drops pre-releases. + """ + if prereleases is None: + return _pep440_filter_prereleases(matches, key) + if prereleases: + return iter(matches) + return ( + item + for item in matches + if (parsed := coerce_version(item if key is None else key(item))) is None + or not parsed.is_prerelease + ) + + +class SpecifierSet(BaseSpecifier): + """This class abstracts handling of a set of version specifiers. + + It can be passed a single specifier (``>=3.0``), a comma-separated list of + specifiers (``>=3.0,!=3.1``), or no specifier at all. + + Instances are safe to serialize with :mod:`pickle`. They use a stable + format so the same pickle can be loaded in future packaging + releases. + + .. versionchanged:: 26.2 + + Added a stable pickle format. Pickles created with + packaging 26.2+ can be unpickled with future releases. + Backward compatibility with pickles from + packaging < 26.2 is supported but may be removed in a future + release. + """ + + __slots__ = ( + "_canonicalized", + "_has_arbitrary", + "_is_unsatisfiable", + "_prereleases", + "_ranges", + "_specs", + ) + + def __init__( + self, + specifiers: str | Iterable[Specifier] = "", + prereleases: bool | None = None, + ) -> None: + """Initialize a SpecifierSet instance. + + :param specifiers: + The string representation of a specifier or a comma-separated list of + specifiers which will be parsed and normalized before use. + May also be an iterable of ``Specifier`` instances, which will be used + as is. + :param prereleases: + This tells the SpecifierSet if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + + :raises InvalidSpecifier: + If the given ``specifiers`` are not parseable than this exception will be + raised. + """ + + if isinstance(specifiers, str): + # Split on `,` to break each individual specifier into its own item, and + # strip each item to remove leading/trailing whitespace. + split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + + self._specs: tuple[Specifier, ...] = tuple(map(Specifier, split_specifiers)) + # Fast substring check; avoids iterating parsed specs. + self._has_arbitrary = "===" in specifiers + else: + self._specs = tuple(specifiers) + # Substring check works for both Specifier objects and plain + # strings (setuptools passes lists of strings). + self._has_arbitrary = any("===" in str(s) for s in self._specs) + + self._canonicalized = len(self._specs) <= 1 + self._is_unsatisfiable: bool | None = None + self._ranges: Sequence[Interval] | None = None + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + def _canonical_specs(self) -> tuple[Specifier, ...]: + """Deduplicate, sort, and cache specs for order-sensitive operations.""" + if not self._canonicalized: + self._specs = tuple(dict.fromkeys(sorted(self._specs, key=str))) + self._canonicalized = True + return self._specs + + @property + def prereleases(self) -> bool | None: + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + if any(s.prereleases for s in self._specs): + return True + + return None + + @prereleases.setter + def prereleases(self, value: bool | None) -> None: + self._prereleases = value + self._is_unsatisfiable = None + + def __getstate__(self) -> tuple[tuple[Specifier, ...], bool | None]: + # Return state as a 2-item tuple for compactness: + # (specs, prereleases) + # Cache members are excluded and will be recomputed on demand. + return (self._specs, self._prereleases) + + def __setstate__(self, state: object) -> None: + # Always discard cached values - they will be recomputed on demand. + self._ranges = None + self._is_unsatisfiable = None + + if isinstance(state, tuple): + if len(state) == 2: + # New format (26.2+): (specs, prereleases) + specs, prereleases = state + if ( + isinstance(specs, tuple) + and all(isinstance(s, Specifier) for s in specs) + and _validate_pre(prereleases) + ): + self._specs = specs + self._prereleases = prereleases + self._canonicalized = len(specs) <= 1 + self._has_arbitrary = any("===" in str(s) for s in specs) + return + if len(state) == 2 and isinstance(state[1], dict): + # Format (packaging 26.0-26.1): (None, {slot: value}). + _, slot_dict = state + specs = slot_dict.get("_specs", ()) + prereleases = slot_dict.get("_prereleases") + # Convert frozenset to tuple (26.0 stored as frozenset) + if isinstance(specs, frozenset): + specs = tuple(sorted(specs, key=str)) + if ( + isinstance(specs, tuple) + and all(isinstance(s, Specifier) for s in specs) + and _validate_pre(prereleases) + ): + self._specs = specs + self._prereleases = prereleases + self._canonicalized = len(self._specs) <= 1 + self._has_arbitrary = any("===" in str(s) for s in self._specs) + return + if isinstance(state, dict): + # Old format (packaging <= 25.x, no __slots__): state is a plain dict. + specs = state.get("_specs", ()) + prereleases = state.get("_prereleases") + # Convert frozenset to tuple (26.0 stored as frozenset) + if isinstance(specs, frozenset): + specs = tuple(sorted(specs, key=str)) + if ( + isinstance(specs, tuple) + and all(isinstance(s, Specifier) for s in specs) + and _validate_pre(prereleases) + ): + self._specs = specs + self._prereleases = prereleases + self._canonicalized = len(self._specs) <= 1 + self._has_arbitrary = any("===" in str(s) for s in self._specs) + return + + raise TypeError(f"Cannot restore SpecifierSet from {state!r}") + + def __repr__(self) -> str: + """A representation of the specifier set that shows all internal state. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> SpecifierSet('>=1.0.0,!=2.0.0') + =1.0.0')> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"<{self.__class__.__name__}({str(self)!r}{pre})>" + + def __str__(self) -> str: + """A string representation of the specifier set that can be round-tripped. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> str(SpecifierSet(">=1.0.0,!=1.0.1")) + '!=1.0.1,>=1.0.0' + >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) + '!=1.0.1,>=1.0.0' + """ + return ",".join(str(s) for s in self._canonical_specs()) + + def __hash__(self) -> int: + return hash(self._canonical_specs()) + + def __and__(self, other: SpecifierSet | str) -> SpecifierSet: + """Return a SpecifierSet which is a combination of the two sets. + + :param other: The other object to combine with. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1' + =1.0.0')> + >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1') + =1.0.0')> + """ + if isinstance(other, str): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = self._specs + other._specs + specifier._canonicalized = len(specifier._specs) <= 1 + specifier._has_arbitrary = self._has_arbitrary or other._has_arbitrary + + # Combine prerelease settings: use common or non-None value + if self._prereleases is None or self._prereleases == other._prereleases: + specifier._prereleases = other._prereleases + elif other._prereleases is None: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease overrides." + ) + + return specifier + + def __eq__(self, other: object) -> bool: + """Whether or not the two SpecifierSet-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) == + ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1" + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2") + False + """ + if isinstance(other, (str, Specifier)): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._canonical_specs() == other._canonical_specs() + + def __len__(self) -> int: + """Returns the number of specifiers in this specifier set.""" + return len(self._specs) + + def __iter__(self) -> Iterator[Specifier]: + """ + Returns an iterator over all the underlying :class:`Specifier` instances + in this specifier set. + + >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str) + [, =1.0.0')>] + """ + return iter(self._specs) + + def _get_ranges(self) -> Sequence[Interval]: + """Intersect all specifiers into a single sequence of version ranges. + + Empty when unsatisfiable. Callers must ensure ``self._specs`` + is non-empty. + """ + if self._ranges is not None: + return self._ranges + + self._ranges = intersect_specifier_bounds(s._to_ranges() for s in self._specs) + return self._ranges + + def is_unsatisfiable(self) -> bool: + """Check whether this specifier set can never be satisfied. + + Returns True if no version can satisfy all specifiers simultaneously. + + >>> SpecifierSet(">=2.0,<1.0").is_unsatisfiable() + True + >>> SpecifierSet(">=1.0,<2.0").is_unsatisfiable() + False + >>> SpecifierSet("").is_unsatisfiable() + False + >>> SpecifierSet("==1.0,!=1.0").is_unsatisfiable() + True + + .. versionadded:: 26.1 + """ + cached = self._is_unsatisfiable + if cached is not None: + return cached + + if not self._specs: + self._is_unsatisfiable = False + return False + + result = not self._get_ranges() + + if not result: + result = self._check_arbitrary_unsatisfiable() + + if not result and self.prereleases is False: + result = ranges_are_prerelease_only(self._get_ranges()) + + self._is_unsatisfiable = result + return result + + def _check_arbitrary_unsatisfiable(self) -> bool: + """Check === (arbitrary equality) specs for unsatisfiability. + + === uses case-insensitive string comparison, so the only candidate + that can match ``===V`` is the literal string V. This method + checks whether that candidate is excluded by other specifiers. + """ + arbitrary = [s for s in self._specs if s.operator == "==="] + if not arbitrary: + return False + + # Multiple === must agree on the same string (case-insensitive). + first = arbitrary[0].version.lower() + if any(s.version.lower() != first for s in arbitrary[1:]): + return True + + # The sole candidate is the === version string. Check whether + # it can satisfy every standard spec. + candidate = coerce_version(arbitrary[0].version) + + # With prereleases=False, a prerelease candidate is excluded + # by contains() before the === string check even runs. + if ( + self.prereleases is False + and candidate is not None + and candidate.is_prerelease + ): + return True + + standard = [s for s in self._specs if s.operator != "==="] + if not standard: + return False + + if candidate is None: + # Unparsable string cannot satisfy any standard spec. + return True + + return not all(s.contains(candidate) for s in standard) + + def to_range(self) -> ranges.VersionRange: + """Return the :class:`~packaging.ranges.VersionRange` this set accepts. + + An empty set yields the full range; an unsatisfiable set yields the + empty range. ``===`` specifiers contribute literal-string admission. + + >>> SpecifierSet(">=1.0,<2.0").to_range() + + + .. versionadded:: 26.3 + """ + from .ranges import VersionRange # noqa: PLC0415 + + return VersionRange._from_specifier_set(self) + + def _check_relation_operand(self, other: object) -> None: + if not isinstance(other, SpecifierSet): + raise TypeError("expected a SpecifierSet") + if self._has_arbitrary or other._has_arbitrary: + raise ValueError("set relations do not support === specifiers") + + def is_subset(self, other: SpecifierSet) -> bool: + """Return whether every version matching this set also matches other. + + :raises ValueError: + If either set uses ``===`` specifiers, or the two sets were + given different ``prereleases`` arguments (unset on one side + counts as different). + :raises TypeError: + If other is not a :class:`SpecifierSet`. + + >>> SpecifierSet(">=3.12,<3.13").is_subset(SpecifierSet(">=3.12")) + True + >>> SpecifierSet(">=3.12").is_subset(SpecifierSet(">=3.12,<3.13")) + False + + .. versionadded:: 26.3 + """ + self._check_relation_operand(other) + return self.to_range().is_subset(other.to_range()) + + def is_superset(self, other: SpecifierSet) -> bool: + """Return whether every version matching other also matches this set. + + :raises ValueError: + If either set uses ``===`` specifiers, or the two sets were + given different ``prereleases`` arguments (unset on one side + counts as different). + :raises TypeError: + If other is not a :class:`SpecifierSet`. + + >>> SpecifierSet(">=3.12").is_superset(SpecifierSet(">=3.12,<3.13")) + True + + .. versionadded:: 26.3 + """ + self._check_relation_operand(other) + return self.to_range().is_superset(other.to_range()) + + def is_disjoint(self, other: SpecifierSet) -> bool: + """Return whether this set and other share no matching versions. + + :raises ValueError: + If either set uses ``===`` specifiers, or the two sets were + given different ``prereleases`` arguments (unset on one side + counts as different). + :raises TypeError: + If other is not a :class:`SpecifierSet`. + + >>> SpecifierSet("<3.12").is_disjoint(SpecifierSet(">=3.12")) + True + >>> SpecifierSet("<3.12").is_disjoint(SpecifierSet(">=3.11")) + False + + .. versionadded:: 26.3 + """ + self._check_relation_operand(other) + return self.to_range().is_disjoint(other.to_range()) + + def __contains__(self, item: UnparsedVersion) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True) + True + """ + return self.contains(item) + + def contains( + self, + item: UnparsedVersion, + prereleases: bool | None = None, + installed: bool | None = None, + ) -> bool: + """Return whether or not the item is contained in this SpecifierSet. + + :param item: + The item to check for, which can be a version string or a + :class:`~packaging.version.Version` instance. + :param prereleases: + Whether or not to match prereleases with this SpecifierSet. If set to + ``None`` (the default), it will follow the recommendation from :pep:`440` + and match prereleases, as there are no other versions. + :param installed: + Whether or not the item is installed. If set to ``True``, it will + accept prerelease versions even if the specifier does not allow them. + + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3")) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False).contains("1.3.0a1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True) + True + + .. versionchanged:: 26.0 + + With ``prereleases=None``, a prerelease now matches. A single + version has no alternatives, so the :pep:`440` rule to accept + prereleases when nothing else satisfies the specifiers applies. + Earlier versions rejected it. An unparsable version now returns + ``False`` instead of raising :exc:`~packaging.version.InvalidVersion`. + """ + version = coerce_version(item) + + if version is not None and installed and version.is_prerelease: + prereleases = True + + # When item is a string and === is involved, keep it as-is + # so the comparison isn't done against the normalized form. + if version is None or (self._has_arbitrary and not isinstance(item, Version)): + check_item = item + else: + check_item = version + + # Fast path: a parseable, local-free version against a rangelike set. + # A local on ``version`` needs PEP 440 stripping that the range path + # applies. + if ( + version is not None + and not self._has_arbitrary + and version.local is None + and self._specs + ): + if version.is_prerelease and ( + prereleases is False + or (prereleases is None and self._prereleases is False) + ): + return False + + bounds = self._ranges + if bounds is None: + # Per-spec ``_fast_match`` answers a set of simple specifiers + # without folding anything. If a spec needs the range path, + # fold the intersected bounds once and cache them so repeated + # checks on the same set stay cheap. + for spec in self._specs: + match = _fast_match(spec, version) + if match is None: + break + if not match: + return False + else: + return True + + bounds = self._ranges = self._get_ranges() + + return matches_bounds_only(bounds, version) + + return bool(list(self.filter([check_item], prereleases=prereleases))) + + @typing.overload + def filter( + self, + iterable: Iterable[UnparsedVersionVar], + prereleases: bool | None = None, + key: None = ..., + ) -> Iterator[UnparsedVersionVar]: ... + + @typing.overload + def filter( + self, + iterable: Iterable[T], + prereleases: bool | None = None, + key: Callable[[T], UnparsedVersion] = ..., + ) -> Iterator[T]: ... + + def filter( + self, + iterable: Iterable[Any], + prereleases: bool | None = None, + key: Callable[[Any], UnparsedVersion] | None = None, + ) -> Iterator[Any]: + """Filter items in the given iterable, that match the specifiers in this set. + + :param iterable: + An iterable that can contain version strings and + :class:`~packaging.version.Version` instances. The items in the + iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will follow the recommendation from :pep:`440` + and match prereleases if there are no other versions. + :param key: + A callable that takes a single argument (an item from the iterable) and + returns a version string or :class:`~packaging.version.Version` + instance to be used for filtering. + + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")])) + ['1.3', ] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"])) + ['1.5a1'] + >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + >>> list(SpecifierSet(">=1.2.3").filter( + ... [{"ver": "1.2"}, {"ver": "1.3"}], + ... key=lambda x: x["ver"])) + [{'ver': '1.3'}] + + An "empty" SpecifierSet will filter items based on the presence of prerelease + versions in the set. + + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet("").filter(["1.5a1"])) + ['1.5a1'] + >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + + .. versionchanged:: 26.0 + + Prerelease filtering now follows the PEP 440 recommendation of + yielding prereleases only when no final release is present. + + .. versionchanged:: 26.1 + + Added the ``key`` parameter. + """ + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None and self.prereleases is not None: + prereleases = self.prereleases + + if self._specs: + if self._has_arbitrary: + # Slow path for === + specs = self._specs + matches = ( + item + for item in iterable + if all( + s.contains(item if key is None else key(item), prereleases=True) + for s in specs + ) + ) + return _apply_prereleases_filter(matches, key, prereleases) + + ranges = self._ranges + if ranges is None: + ranges = self._get_ranges() + return filter_by_ranges(ranges, iterable, key, prereleases) + + # Empty SpecifierSet. + return _apply_prereleases_filter(iterable, key, prereleases) + + +def _pep440_filter_prereleases( + iterable: Iterable[Any], key: Callable[[Any], UnparsedVersion] | None +) -> Iterator[Any]: + """Filter per PEP 440: exclude prereleases unless no finals exist.""" + # Two lists used: + # * all_nonfinal to preserve order if no finals exist + # * arbitrary_strings for streaming when first final found + all_nonfinal: list[Any] = [] + arbitrary_strings: list[Any] = [] + + found_final = False + for item in iterable: + parsed = coerce_version(item if key is None else key(item)) + + if parsed is None: + # Arbitrary strings are always included as it is not + # possible to determine if they are prereleases, + # and they have already passed all specifiers. + if found_final: + yield item + else: + arbitrary_strings.append(item) + all_nonfinal.append(item) + continue + + if not parsed.is_prerelease: + # Final release found - flush arbitrary strings, then yield + if not found_final: + yield from arbitrary_strings + found_final = True + yield item + continue + + # Prerelease - buffer if no finals yet, otherwise skip + if not found_final: + all_nonfinal.append(item) + + # No finals found - yield all buffered items + if not found_final: + yield from all_nonfinal diff --git a/venv/lib/python3.12/site-packages/packaging/tags.py b/venv/lib/python3.12/site-packages/packaging/tags.py new file mode 100644 index 0000000..dd1ea91 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/tags.py @@ -0,0 +1,1053 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import logging +import operator +import platform +import re +import struct +import subprocess +import sys +import sysconfig +from collections.abc import Iterable, Iterator, Sequence +from importlib.machinery import EXTENSION_SUFFIXES +from typing import ( + TYPE_CHECKING, + TypeVar, + cast, +) + +from . import _manylinux, _musllinux + +if TYPE_CHECKING: + from collections.abc import Callable + from collections.abc import Set as AbstractSet + + +__all__ = [ + "INTERPRETER_SHORT_NAMES", + "AppleVersion", + "InvalidTag", + "PythonVersion", + "Tag", + "TooManyTagsError", + "UnsortedTagsError", + "android_platforms", + "compatible_tags", + "cpython_tags", + "create_compatible_tags_selector", + "generic_tags", + "interpreter_name", + "interpreter_version", + "ios_platforms", + "mac_platforms", + "parse_tag", + "platform_tags", + "pure_python_tags", + "sys_tags", +] + + +def __dir__() -> list[str]: + return __all__ + + +logger = logging.getLogger(__name__) + +PythonVersion = Sequence[int] +""" +A sequence of integers describing a Python version, e.g. ``(3, 13)``. + +.. versionadded:: 20.0 +""" + +AppleVersion = tuple[int, int] +""" +A ``(major, minor)`` integer pair describing an Apple OS version. + +.. versionadded:: 24.2 +""" +_T = TypeVar("_T") + +INTERPRETER_SHORT_NAMES: dict[str, str] = { + "python": "py", # Generic. + "cpython": "cp", + "pypy": "pp", + "ironpython": "ip", + "jython": "jy", +} + + +# This function can be unit tested without reloading the module +# (Unlike _32_BIT_INTERPRETER) +def _compute_32_bit_interpreter() -> bool: + return struct.calcsize("P") == 4 + + +_32_BIT_INTERPRETER = _compute_32_bit_interpreter() + + +class UnsortedTagsError(ValueError): + """ + Raised when a tag component is not in sorted order per PEP 425. + + .. versionadded:: 26.1 + """ + + +class InvalidTag(ValueError): + """ + Raised when an interpreter component is not an identifier, a tag component + is empty, or a tag does not have exactly three components. + + .. versionadded:: 26.3 + """ + + +class TooManyTagsError(ValueError): + """ + Raised when a compressed tag set exceeds the configured limit. + + .. versionadded:: 26.3 + """ + + +class Tag: + """ + A representation of the tag triple for a wheel. + + Instances are considered immutable and thus are hashable. Equality checking + is also supported. + + Instances are safe to serialize with :mod:`pickle`. They use a stable + format so the same pickle can be loaded in future packaging releases. + + .. versionchanged:: 26.2 + + Added a stable pickle format. Pickles created with packaging 26.2+ can + be unpickled with future releases. Backward compatibility with pickles + from packaging < 26.2 is supported but may be removed in a future + release. + """ + + __slots__ = ["_abi", "_hash", "_interpreter", "_platform"] + + def __init__(self, interpreter: str, abi: str, platform: str) -> None: + """ + :param str interpreter: The interpreter name, e.g. ``"py"`` + (see :attr:`INTERPRETER_SHORT_NAMES` for mapping + well-known interpreter names to their short names). + :param str abi: The ABI that a wheel supports, e.g. ``"cp37m"``. + :param str platform: The OS/platform the wheel supports, + e.g. ``"win_amd64"``. + """ + self._interpreter = interpreter.lower() + self._abi = abi.lower() + self._platform = platform.lower() + # The __hash__ of every single element in a Set[Tag] will be evaluated each time + # that a set calls its `.disjoint()` method, which may be called hundreds of + # times when scanning a page of links for packages with tags matching that + # Set[Tag]. Pre-computing the value here produces significant speedups for + # downstream consumers. + self._hash = hash((self._interpreter, self._abi, self._platform)) + + @property + def interpreter(self) -> str: + """ + The interpreter name, e.g. ``"py"`` (see + :attr:`INTERPRETER_SHORT_NAMES` for mapping well-known interpreter + names to their short names). + """ + return self._interpreter + + @property + def abi(self) -> str: + """ + The supported ABI. + """ + return self._abi + + @property + def platform(self) -> str: + """ + The OS/platform. + """ + return self._platform + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Tag): + return NotImplemented + + return ( + (self._hash == other._hash) # Short-circuit ASAP for perf reasons. + and (self._platform == other._platform) + and (self._abi == other._abi) + and (self._interpreter == other._interpreter) + ) + + def __hash__(self) -> int: + return self._hash + + def __str__(self) -> str: + return f"{self._interpreter}-{self._abi}-{self._platform}" + + def __repr__(self) -> str: + return f"<{self} @ {id(self)}>" + + def __getstate__(self) -> tuple[str, str, str]: + # Return state as a 3-item tuple: (interpreter, abi, platform). + # Cache member _hash is excluded and will be recomputed. + return (self._interpreter, self._abi, self._platform) + + def __setstate__(self, state: object) -> None: + if isinstance(state, tuple): + if len(state) == 3 and all(isinstance(s, str) for s in state): + # New format (26.2+): (interpreter, abi, platform) + self._interpreter, self._abi, self._platform = state + self._hash = hash((self._interpreter, self._abi, self._platform)) + return + if len(state) == 2 and isinstance(state[1], dict): + # Old format (packaging <= 26.1, __slots__): (None, {slot: value}). + _, slots = state + try: + interpreter = slots["_interpreter"] + abi = slots["_abi"] + platform = slots["_platform"] + except KeyError: + raise TypeError(f"Cannot restore Tag from {state!r}") from None + if not all( + isinstance(value, str) for value in (interpreter, abi, platform) + ): + raise TypeError(f"Cannot restore Tag from {state!r}") + self._interpreter = interpreter.lower() + self._abi = abi.lower() + self._platform = platform.lower() + self._hash = hash((self._interpreter, self._abi, self._platform)) + return + raise TypeError(f"Cannot restore Tag from {state!r}") + + +def parse_tag( + tag: str, *, validate_order: bool = False, limit: int | None = None +) -> frozenset[Tag]: + """ + Parses the provided tag (e.g. `py3-none-any`) into a frozenset of + :class:`Tag` instances. + + Returning a set is required due to the possibility that the tag is a + `compressed tag set`_, e.g. ``"py2.py3-none-any"`` which supports both + Python 2 and Python 3. + + If **validate_order** is true, compressed tag set components are checked + to be in sorted order as required by PEP 425. + + If **limit** is not ``None``, the compressed tag set can generate at most + that many tags. + + :param str tag: The tag to parse, e.g. ``"py3-none-any"``. + :param bool validate_order: Check whether compressed tag set components + are in sorted order. + :param int | None limit: The maximum number of tags to parse. + :raises UnsortedTagsError: If **validate_order** is true and any compressed tag + set component is not in sorted order. + :raises InvalidTag: If the interpreter field is not an identifier; if the + interpreter, ABI, or platform field (or any member of a compressed tag + set) is empty; or if the tag does not have exactly three components. + :raises TooManyTagsError: If **limit** is not ``None`` and the compressed tag + set would generate more than **limit** tags. + :raises ValueError: If **limit** is negative. + + .. versionadded:: 26.1 + The *validate_order* parameter. + + .. versionadded:: 26.3 + Raises :class:`InvalidTag` when an interpreter component is not an + identifier, a tag component is empty, or a tag does not have exactly + three components. + Added the *limit* parameter. Raises :class:`TooManyTagsError` if the compressed + tag set would generate more than *limit* tags. + """ + + if limit is not None and limit < 0: + raise ValueError("limit must be non-negative") + + component_parts = [component.split(".") for component in tag.split("-")] + for parts in component_parts: + if "" in parts: + component = ".".join(parts) + raise InvalidTag(f"Tag {tag!r} has an empty component: {component!r}") + if validate_order and parts != sorted(parts): + component = ".".join(parts) + raise UnsortedTagsError( + f"Tag component {component!r} is not in sorted order per PEP 425" + ) + + tag_count = 1 + for parts in component_parts: + tag_count *= len(parts) + + if limit is not None and tag_count > limit: + raise TooManyTagsError( + f"Compressed tag set would generate {tag_count} tags, exceeding " + f"limit {limit}" + ) + + try: + interpreters, abis, platforms = component_parts + except ValueError as exc: + raise InvalidTag(f"Tag {tag!r} must have exactly three components") from exc + for interpreter in interpreters: + if not interpreter.isidentifier(): + raise InvalidTag(f"Tag {tag!r} has an invalid interpreter: {interpreter!r}") + return frozenset( + Tag(interpreter, abi, platform_) + for interpreter in interpreters + for abi in abis + for platform_ in platforms + ) + + +def _get_config_var(name: str, warn: bool = False) -> int | str | None: + value: int | str | None = sysconfig.get_config_var(name) + if value is None and warn: + logger.debug( + "Config variable '%s' is unset, Python ABI tag may be incorrect", name + ) + return value + + +def _normalize_string(string: str) -> str: + return string.replace(".", "_").replace("-", "_").replace(" ", "_") + + +def _is_threaded_cpython(abis: list[str]) -> bool: + """ + Determine if the ABI corresponds to a threaded (`--disable-gil`) build. + + The threaded builds are indicated by a "t" in the abiflags. + """ + if len(abis) == 0: + return False + # expect e.g., cp313 + m = re.match(r"cp\d+(.*)", abis[0]) + if not m: + return False + abiflags = m.group(1) + return "t" in abiflags + + +def _abi3_applies(python_version: PythonVersion, threading: bool) -> bool: + """ + Determine if the Python version supports abi3. + + PEP 384 was first implemented in Python 3.2. The free-threaded + builds do not support abi3. + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading + + +def _abi3t_applies(python_version: PythonVersion, threading: bool) -> bool: + """ + Determine if the Python version supports abi3t. + + PEP 803 was first implemented in Python 3.15 but, per PEP 803, this + returns tags going back to Python 3.2 to mirror the abi3 + implementation and leave open the possibility of abi3t wheels + supporting older Python versions. + + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) and threading + + +def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> list[str]: + py_version = tuple(py_version) # To allow for version comparison. + abis = [] + version = _version_nodot(py_version[:2]) + threading = debug = pymalloc = ucs4 = "" + with_debug = _get_config_var("Py_DEBUG", warn) + has_refcount = hasattr(sys, "gettotalrefcount") + # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled + # extension modules is the best option. + # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 + has_ext = "_d.pyd" in EXTENSION_SUFFIXES + if with_debug or (with_debug is None and (has_refcount or has_ext)): + debug = "d" + if py_version >= (3, 13) and _get_config_var("Py_GIL_DISABLED", warn): + threading = "t" + if py_version < (3, 8): + with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) + if with_pymalloc or with_pymalloc is None: + pymalloc = "m" + if py_version < (3, 3): + unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) + if unicode_size == 4 or ( + unicode_size is None and sys.maxunicode == 0x10FFFF + ): + ucs4 = "u" + elif debug: + # Debug builds can also load "normal" extension modules. + # We can also assume no UCS-4 or pymalloc requirement. + abis.append(f"cp{version}{threading}") + abis.insert(0, f"cp{version}{threading}{debug}{pymalloc}{ucs4}") + return abis + + +def cpython_tags( + python_version: PythonVersion | None = None, + abis: Iterable[str] | None = None, + platforms: Iterable[str] | None = None, + *, + warn: bool = False, +) -> Iterator[Tag]: + """ + Yields the tags for the CPython interpreter. + + The specific tags generated are: + + - ``cp--`` + - ``cp--`` + - ``cp-none-`` + - ``cp--`` where "older version" is all older + minor versions down to Python 3.2 (when ``abi3`` was introduced) + + If ``python_version`` only provides a major-only version then only + user-provided ABIs via ``abis`` and the ``none`` ABI will be used. + + The ``stable_abi`` will be either ``abi3`` or ``abi3t`` if `abi` is a + GIL-enabled ABI like `"cp315"` or a free-threaded ABI like `"cp315t"`, + respectively. + + :param Sequence python_version: A one- or two-item sequence representing the + targeted Python version. Defaults to + ``sys.version_info[:2]``. + :param Iterable abis: Iterable of compatible ABIs. Defaults to the ABIs + compatible with the current system. + :param Iterable platforms: Iterable of compatible platforms. Defaults to the + platforms compatible with the current system. + :param bool warn: Whether warnings should be logged. Defaults to ``False``. + + .. versionadded:: 20.0 + """ + if not python_version: + python_version = sys.version_info[:2] + + interpreter = f"cp{_version_nodot(python_version[:2])}" + + if abis is None: + abis = _cpython_abis(python_version, warn) if len(python_version) > 1 else [] + abis = list(abis) + threading = _is_threaded_cpython(abis) + # Stable ABIs and 'none' are explicitly handled later. + explicit_abis = ("abi3", "abi3t", "none") if threading else ("abi3", "none") + for explicit_abi in explicit_abis: + try: + abis.remove(explicit_abi) + except ValueError: # noqa: PERF203 + pass + + platforms = list(platforms or platform_tags()) + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + + use_abi3 = _abi3_applies(python_version, threading) + use_abi3t = _abi3t_applies(python_version, threading) + if use_abi3: + yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) + if use_abi3t: + yield from (Tag(interpreter, "abi3t", platform_) for platform_ in platforms) + + yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) + + if use_abi3 or use_abi3t: + for minor_version in range(python_version[1] - 1, 1, -1): + for platform_ in platforms: + version = _version_nodot((python_version[0], minor_version)) + interpreter = f"cp{version}" + if use_abi3: + yield Tag(interpreter, "abi3", platform_) + if use_abi3t: + # Support for abi3t was introduced in Python 3.15, but in + # principle abi3t wheels are possible for older limited API + # versions, so allow things like ("cp37", "abi3t", "platform") + yield Tag(interpreter, "abi3t", platform_) + + +def _generic_abi() -> list[str]: + """ + Return the ABI tag based on EXT_SUFFIX. + """ + # The following are examples of `EXT_SUFFIX`. + # We want to keep the parts which are related to the ABI and remove the + # parts which are related to the platform: + # - linux: '.cpython-310-x86_64-linux-gnu.so' => cp310 + # - mac: '.cpython-310-darwin.so' => cp310 + # - win: '.cp310-win_amd64.pyd' => cp310 + # - win: '.pyd' => cp37 (uses _cpython_abis()) + # - pypy: '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73 + # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib' + # => graalpy_38_native + + ext_suffix = _get_config_var("EXT_SUFFIX", warn=True) + if not isinstance(ext_suffix, str) or not ext_suffix.startswith("."): + raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')") + parts = ext_suffix.split(".") + if len(parts) < 3: + # CPython3.7 and earlier uses ".pyd" on Windows. + return _cpython_abis(sys.version_info[:2]) + soabi = parts[1] + if soabi.startswith("cpython"): + # non-windows + cpython_parts = soabi.split("-") + if len(cpython_parts) < 2 or not cpython_parts[1]: + raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')") + abi = "cp" + cpython_parts[1] + elif soabi.startswith("cp"): + # windows + abi = soabi.split("-")[0] + elif soabi.startswith("pypy"): + abi = "-".join(soabi.split("-")[:2]) + elif soabi.startswith("graalpy"): + abi = "-".join(soabi.split("-")[:3]) + elif soabi: + # pyston, ironpython, others? + abi = soabi + else: + return [] + return [_normalize_string(abi)] + + +def generic_tags( + interpreter: str | None = None, + abis: Iterable[str] | None = None, + platforms: Iterable[str] | None = None, + *, + warn: bool = False, +) -> Iterator[Tag]: + """ + Yields the tags for an interpreter which requires no specialization. + + This function should be used if one of the other interpreter-specific + functions provided by this module is not appropriate (i.e. not calculating + tags for a CPython interpreter). + + The specific tags generated are: + + - ``--`` + + The ``"none"`` ABI will be added if it was not explicitly provided. + + :param str interpreter: The name of the interpreter. Defaults to being + calculated. + :param Iterable abis: Iterable of compatible ABIs. Defaults to the ABIs + compatible with the current system. + :param Iterable platforms: Iterable of compatible platforms. Defaults to the + platforms compatible with the current system. + :param bool warn: Whether warnings should be logged. Defaults to ``False``. + + .. versionadded:: 20.0 + """ + if not interpreter: + interp_name = interpreter_name() + interp_version = interpreter_version(warn=warn) + interpreter = f"{interp_name}{interp_version}" + abis = _generic_abi() if abis is None else list(abis) + platforms = list(platforms or platform_tags()) + if "none" not in abis: + abis.append("none") + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + + +def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: + """ + Yields Python versions in descending order. + + After the latest version, the major-only version will be yielded, and then + all previous versions of that major version. + """ + if len(py_version) > 1: + yield f"py{_version_nodot(py_version[:2])}" + yield f"py{py_version[0]}" + if len(py_version) > 1: + for minor in range(py_version[1] - 1, -1, -1): + yield f"py{_version_nodot((py_version[0], minor))}" + + +def pure_python_tags( + python_version: PythonVersion | None = None, +) -> Iterator[Tag]: + """ + Yields the pure-Python tags compatible with ``python_version``. + + The tags use the ``"none"`` ABI and ``"any"`` platform, so their + generation does not depend on the running platform. + + .. versionadded:: 26.3 + + :param Sequence python_version: A one- or two-item sequence representing the + compatible version of Python. Defaults to + ``sys.version_info[:2]``. + :raises ValueError: If ``python_version`` is an empty sequence. + """ + if python_version is None: + python_version = sys.version_info[:2] + elif not python_version: + raise ValueError("python_version must contain at least one item") + for version in _py_interpreter_range(python_version): + yield Tag(version, "none", "any") + + +def compatible_tags( + python_version: PythonVersion | None = None, + interpreter: str | None = None, + platforms: Iterable[str] | None = None, +) -> Iterator[Tag]: + """ + Yields the tags for an interpreter compatible with the Python version + specified by ``python_version``. + + The specific tags generated are: + + - ``py*-none-`` + - ``-none-any`` if ``interpreter`` is provided + - ``py*-none-any`` + + :param Sequence python_version: A one- or two-item sequence representing the + compatible version of Python. Defaults to + ``sys.version_info[:2]``. + :param str interpreter: The name of the interpreter (if known), e.g. + ``"cp38"``. Defaults to the current interpreter. + :param Iterable platforms: Iterable of compatible platforms. Defaults to the + platforms compatible with the current system. + + .. versionadded:: 20.0 + """ + if not python_version: + python_version = sys.version_info[:2] + platforms = list(platforms or platform_tags()) + for version in _py_interpreter_range(python_version): + for platform_ in platforms: + yield Tag(version, "none", platform_) + if interpreter: + yield Tag(interpreter, "none", "any") + yield from pure_python_tags(python_version) + + +def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: + if not is_32bit: + return arch + + if arch.startswith("ppc"): + return "ppc" + + return "i386" + + +def _mac_binary_formats(version: AppleVersion, cpu_arch: str) -> list[str]: + formats = [cpu_arch] + if cpu_arch == "x86_64": + if version < (10, 4): + return [] + formats.extend(["intel", "fat64", "fat3"]) + + elif cpu_arch == "i386": + if version < (10, 4): + return [] + formats.extend(["intel", "fat3", "fat"]) + + elif cpu_arch == "ppc64": + # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? + if version > (10, 5) or version < (10, 4): + return [] + formats.append("fat64") + + elif cpu_arch == "ppc": + if version > (10, 6): + return [] + formats.extend(["fat3", "fat"]) + + if cpu_arch in {"arm64", "x86_64"}: + formats.append("universal2") + + if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: + formats.append("universal") + + return formats + + +def mac_platforms( + version: AppleVersion | None = None, arch: str | None = None +) -> Iterator[str]: + """ + Yields the :attr:`~Tag.platform` tags for macOS. + + The `version` parameter is a two-item tuple specifying the macOS version to + generate platform tags for. The `arch` parameter is the CPU architecture to + generate platform tags for. Both parameters default to the appropriate value + for the current system. + + :param tuple version: A two-item tuple representing the version of macOS. + Defaults to the current system's version. + :param str arch: The CPU architecture. Defaults to the architecture of the + current system, e.g. ``"x86_64"``. + + .. note:: + Equivalent support for the other major platforms is purposefully not + provided: + + - On Windows, platform compatibility is statically specified + - On Linux, code must be run on the system itself to determine + compatibility + + .. versionadded:: 20.0 + """ + if version is None or arch is None: + version_str, _, cpu_arch = platform.mac_ver() + if version is None: + version = cast("AppleVersion", tuple(map(int, version_str.split(".")[:2]))) + if version == (10, 16): + # When built against an older macOS SDK, Python will report macOS 10.16 + # instead of the real version. + version_str = subprocess.run( + [ + sys.executable, + "-sS", + "-c", + "import platform; print(platform.mac_ver()[0])", + ], + check=True, + env={"SYSTEM_VERSION_COMPAT": "0"}, + stdout=subprocess.PIPE, + text=True, + ).stdout + version = cast( + "AppleVersion", tuple(map(int, version_str.split(".")[:2])) + ) + if arch is None: + arch = _mac_arch(cpu_arch) + + if (10, 0) <= version < (11, 0): + # Prior to Mac OS 11, each yearly release of Mac OS bumped the + # "minor" version number. The major version was always 10. + major_version = 10 + for minor_version in range(version[1], -1, -1): + compat_version = major_version, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield f"macosx_{major_version}_{minor_version}_{binary_format}" + + if version >= (11, 0): + # Starting with Mac OS 11, each yearly release bumps the major version + # number. The minor versions are now the midyear updates. + minor_version = 0 + for major_version in range(version[0], 10, -1): + compat_version = major_version, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield f"macosx_{major_version}_{minor_version}_{binary_format}" + + # Mac OS 11 on x86_64 is compatible with binaries from previous releases. + # Arm64 support was introduced in 11.0, so no Arm binaries from previous + # releases exist. + # + # However, the "universal2" binary format can have a + # macOS version earlier than 11.0 when the x86_64 part of the binary supports + # that version of macOS. + major_version = 10 + if arch == "x86_64": + for minor_version in range(16, 3, -1): + compat_version = major_version, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield f"macosx_{major_version}_{minor_version}_{binary_format}" + else: + for minor_version in range(16, 3, -1): + compat_version = major_version, minor_version + binary_format = "universal2" + yield f"macosx_{major_version}_{minor_version}_{binary_format}" + + +def ios_platforms( + version: AppleVersion | None = None, multiarch: str | None = None +) -> Iterator[str]: + """ + + Yields the :attr:`~Tag.platform` tags for iOS. + + :param tuple version: A two-item tuple representing the version of iOS. + Defaults to the current system's version. + :param str multiarch: The CPU architecture+ABI to be used. This should be in + the format by ``sys.implementation._multiarch`` (e.g., + ``arm64_iphoneos`` or ``x86_64_iphonesimulator``). + Defaults to the current system's multiarch value. + + .. note:: + Behavior of this method is undefined if invoked on non-iOS platforms + without providing explicit version and multiarch arguments. + + .. versionadded:: 24.2 + """ + if version is None: + # if iOS is the current platform, ios_ver *must* be defined. However, + # it won't exist for CPython versions before 3.13, which causes a mypy + # error. + _, release, _, _ = platform.ios_ver() # type: ignore[attr-defined, unused-ignore] + version = cast("AppleVersion", tuple(map(int, release.split(".")[:2]))) + + if multiarch is None: + multiarch = sys.implementation._multiarch + multiarch = multiarch.replace("-", "_") + + ios_platform_template = "ios_{major}_{minor}_{multiarch}" + + # Consider any iOS major.minor version from the version requested, down to + # 12.0. 12.0 is the first iOS version that is known to have enough features + # to support CPython. Consider every possible minor release up to X.9. There + # highest the minor has ever gone is 8 (14.8 and 15.8) but having some extra + # candidates that won't ever match doesn't really hurt, and it saves us from + # having to keep an explicit list of known iOS versions in the code. Return + # the results descending order of version number. + + # If the requested major version is less than 12, there won't be any matches. + if version[0] < 12: + return + + # Consider the actual X.Y version that was requested. + yield ios_platform_template.format( + major=version[0], minor=version[1], multiarch=multiarch + ) + + # Consider every minor version from X.0 to the minor version prior to the + # version requested by the platform. + for minor in range(version[1] - 1, -1, -1): + yield ios_platform_template.format( + major=version[0], minor=minor, multiarch=multiarch + ) + + for major in range(version[0] - 1, 11, -1): + for minor in range(9, -1, -1): + yield ios_platform_template.format( + major=major, minor=minor, multiarch=multiarch + ) + + +def android_platforms( + api_level: int | None = None, abi: str | None = None +) -> Iterator[str]: + """ + Yields the :attr:`~Tag.platform` tags for Android. If this function is invoked on + non-Android platforms, the ``api_level`` and ``abi`` arguments are required. + + :param int api_level: The maximum `API level + `__ to return. Defaults + to the current system's version, as returned by ``platform.android_ver``. + :param str abi: The `Android ABI `__, + e.g. ``arm64_v8a``. Defaults to the current system's ABI , as returned by + ``sysconfig.get_platform``. Hyphens and periods will be replaced with + underscores. + + .. versionadded:: 25.0 + """ + if platform.system() != "Android" and (api_level is None or abi is None): + raise TypeError( + "on non-Android platforms, the api_level and abi arguments are required" + ) + + if api_level is None: + # Python 3.13 was the first version to return platform.system() == "Android", + # and also the first version to define platform.android_ver(). + api_level = platform.android_ver().api_level # type: ignore[attr-defined] + + if abi is None: + abi = sysconfig.get_platform().split("-")[-1] + abi = _normalize_string(abi) + + # 16 is the minimum API level known to have enough features to support CPython + # without major patching. Yield every API level from the maximum down to the + # minimum, inclusive. + min_api_level = 16 + for ver in range(api_level, min_api_level - 1, -1): + yield f"android_{ver}_{abi}" + + +def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: + linux = _normalize_string(sysconfig.get_platform()) + if not linux.startswith("linux_"): + # we should never be here, just yield the sysconfig one and return + yield linux + return + if is_32bit: + if linux == "linux_x86_64": + linux = "linux_i686" + elif linux == "linux_aarch64": + linux = "linux_armv8l" + _, arch = linux.split("_", 1) + archs = {"armv8l": ["armv8l", "armv7l"]}.get(arch, [arch]) + for arch in archs: + yield f"linux_{arch}" + yield from _manylinux.platform_tags(archs) + yield from _musllinux.platform_tags(archs) + + +def _emscripten_platforms() -> Iterator[str]: + pyemscripten_platform_version = sysconfig.get_config_var( + "PYEMSCRIPTEN_PLATFORM_VERSION" + ) + if pyemscripten_platform_version: + yield f"pyemscripten_{pyemscripten_platform_version}_wasm32" + yield from _generic_platforms() + + +def _generic_platforms() -> Iterator[str]: + yield _normalize_string(sysconfig.get_platform()) + + +def platform_tags() -> Iterator[str]: + """ + Yields the :attr:`~Tag.platform` tags for the running interpreter. + + .. versionadded:: 21.1 + """ + if platform.system() == "Darwin": + return mac_platforms() + elif platform.system() == "iOS": + return ios_platforms() + elif platform.system() == "Android": + return android_platforms() + elif platform.system() == "Linux": + return _linux_platforms() + elif platform.system() == "Emscripten": + return _emscripten_platforms() + else: + return _generic_platforms() + + +def interpreter_name() -> str: + """ + Returns the name of the running interpreter. + + Some implementations have a reserved, two-letter abbreviation which will + be returned when appropriate. + + This typically acts as the prefix to the :attr:`~Tag.interpreter` tag. + + .. versionadded:: 20.0 + """ + name = sys.implementation.name + return INTERPRETER_SHORT_NAMES.get(name) or name + + +def interpreter_version(*, warn: bool = False) -> str: + """ + Returns the running interpreter's version. + + This typically acts as the suffix to the :attr:`~Tag.interpreter` tag. + + :param bool warn: Whether warnings should be logged. Defaults to ``False``. + + .. versionadded:: 20.0 + """ + version = _get_config_var("py_version_nodot", warn=warn) + return str(version) if version else _version_nodot(sys.version_info[:2]) + + +def _version_nodot(version: PythonVersion) -> str: + return "".join(map(str, version)) + + +def sys_tags(*, warn: bool = False) -> Iterator[Tag]: + """ + Yields the sequence of tag triples that the running interpreter supports. + + The iterable is ordered so that the best-matching tag is first in the + sequence. The exact preferential order to tags is interpreter-specific, but + in general the tag importance is in the order of: + + 1. Interpreter + 2. Platform + 3. ABI + + This order is due to the fact that an ABI is inherently tied to the + platform, but platform-specific code is not necessarily tied to the ABI. The + interpreter is the most important tag as it dictates basic support for any + wheel. + + The function returns an iterable in order to allow for the possible + short-circuiting of tag generation if the entire sequence is not necessary + and tag calculation happens to be expensive. + + :param bool warn: Whether warnings should be logged. Defaults to ``False``. + + .. versionchanged:: 21.3 + Added the `pp3-none-any` tag (:issue:`311`). + .. versionchanged:: 26.1 + Added the `abi3t` tag (:issue:`1099`). + .. versionchanged:: 26.3 + Native ``linux_*`` platform tags are now ordered before ``manylinux`` + and ``musllinux`` tags (:issue:`160`). + """ + + interp_name = interpreter_name() + if interp_name == "cp": + yield from cpython_tags(warn=warn) + else: + yield from generic_tags(warn=warn) + + if interp_name == "pp": + interp = "pp3" + elif interp_name == "cp": + interp = "cp" + interpreter_version(warn=warn) + else: + interp = None + yield from compatible_tags(interpreter=interp) + + +def create_compatible_tags_selector( + tags: Iterable[Tag], +) -> Callable[[Iterable[tuple[_T, AbstractSet[Tag]]]], Iterator[_T]]: + """Create a callable to select things compatible with supported tags. + + This function accepts an ordered sequence of tags, with the preferred + tags first. + + The returned callable accepts an iterable of tuples (thing, set[Tag]), + and returns an iterator of things, with the things with the best + matching tags first. + + Example to select compatible wheel filenames: + + >>> from packaging import tags + >>> from packaging.utils import parse_wheel_filename + >>> selector = tags.create_compatible_tags_selector(tags.sys_tags()) + >>> filenames = ["foo-1.0-py3-none-any.whl", "foo-1.0-py2-none-any.whl"] + >>> list(selector([ + ... (filename, parse_wheel_filename(filename)[-1]) for filename in filenames + ... ])) + ['foo-1.0-py3-none-any.whl'] + + .. versionadded:: 26.1 + """ + tag_ranks: dict[Tag, int] = {} + for rank, tag in enumerate(tags): + tag_ranks.setdefault(tag, rank) # ignore duplicate tags, keep first + supported_tags = tag_ranks.keys() + + def selector( + tagged_things: Iterable[tuple[_T, AbstractSet[Tag]]], + ) -> Iterator[_T]: + ranked_things: list[tuple[_T, int]] = [] + for thing, thing_tags in tagged_things: + supported_thing_tags = thing_tags & supported_tags + if supported_thing_tags: + thing_rank = min(tag_ranks[t] for t in supported_thing_tags) + ranked_things.append((thing, thing_rank)) + return iter( + thing for thing, _ in sorted(ranked_things, key=operator.itemgetter(1)) + ) + + return selector diff --git a/venv/lib/python3.12/site-packages/packaging/utils.py b/venv/lib/python3.12/site-packages/packaging/utils.py new file mode 100644 index 0000000..2f0ce20 --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/utils.py @@ -0,0 +1,370 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import re +from typing import NewType, Union, cast + +from .tags import InvalidTag, Tag, UnsortedTagsError, parse_tag +from .version import InvalidVersion, Version, _TrimmedRelease + +__all__ = [ + "BuildTag", + "InvalidName", + "InvalidSdistFilename", + "InvalidWheelFilename", + "NormalizedName", + "canonicalize_name", + "canonicalize_version", + "is_normalized_name", + "parse_sdist_filename", + "parse_wheel_filename", +] + + +def __dir__() -> list[str]: + return __all__ + + +BuildTag = Union[tuple[()], tuple[int, str]] +""" +A wheel build tag: an empty tuple, or a ``(build number, build tag suffix)`` pair. + +.. versionadded:: 20.9 +""" + +NormalizedName = NewType("NormalizedName", str) +""" +A :class:`typing.NewType` of :class:`str`, representing a normalized name. + +.. versionadded:: 20.4 +""" + + +class InvalidName(ValueError): + """ + An invalid distribution name; users should refer to the packaging user guide. + + .. versionadded:: 23.2 + """ + + +class InvalidWheelFilename(ValueError): + """ + An invalid wheel filename was found, users should refer to PEP 427. + + .. versionadded:: 20.9 + """ + + +class InvalidSdistFilename(ValueError): + """ + An invalid sdist filename was found, users should refer to the packaging user guide. + + .. versionadded:: 20.9 + """ + + +# Core metadata spec for `Name` +_validate_regex = re.compile( + r"[a-z0-9]|[a-z0-9][a-z0-9._-]*[a-z0-9]", re.IGNORECASE | re.ASCII +) +_normalized_regex = re.compile(r"[a-z0-9]+(?:-[a-z0-9]+)*", re.ASCII) +# PEP 427: The build number must start with a digit. +_build_tag_regex = re.compile(r"(\d+)(.*)", re.ASCII) +# PEP 427: Valid characters for an escaped project name in a wheel filename. +# Requires at least one character so an empty project name is rejected. +_wheel_name_regex = re.compile(r"^[\w._]+\Z", re.UNICODE) + + +def canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName: + """ + This function takes a valid Python package or extra name, and returns the + normalized form of it. + + The return type is typed as :class:`NormalizedName`. This allows type + checkers to help require that a string has passed through this function + before use. + + If **validate** is true, then the function will check if **name** is a valid + distribution name before normalizing. + + :param str name: The name to normalize. + :param bool validate: Check whether the name is a valid distribution name. + :raises InvalidName: If **validate** is true and the name is not an + acceptable distribution name. + + >>> from packaging.utils import canonicalize_name + >>> canonicalize_name("Django") + 'django' + >>> canonicalize_name("oslo.concurrency") + 'oslo-concurrency' + >>> canonicalize_name("requests") + 'requests' + + .. versionadded:: 16.2 + + .. versionchanged:: 20.4 + The return type was changed to :class:`NormalizedName`. + + .. versionchanged:: 23.2 + Added the *validate* keyword parameter. + """ + if validate and not _validate_regex.fullmatch(name): + raise InvalidName(f"name is invalid: {name!r}") + # Ensure all ``.`` and ``_`` are ``-`` + # Emulates ``re.sub(r"[-_.]+", "-", name).lower()`` from PEP 503 + # Much faster than re, and even faster than str.translate + value = name.lower().replace("_", "-").replace(".", "-") + # Condense repeats (faster than regex) + while "--" in value: + value = value.replace("--", "-") + return cast("NormalizedName", value) + + +def is_normalized_name(name: str) -> bool: + """ + Check if a name is a normalized project name (i.e. a valid name that + :func:`canonicalize_name` would roundtrip to the same value). + + The roundtrip only characterizes normalized names for *valid* names. A name + must start and end with an ASCII letter or digit, which + :func:`canonicalize_name` does not enforce: it leaves a leading or trailing + hyphen in place, so such a name roundtrips without being normalized. + + :param str name: The name to check. + + >>> from packaging.utils import canonicalize_name, is_normalized_name + >>> is_normalized_name("requests") + True + >>> is_normalized_name("Django") + False + >>> canonicalize_name("_not_legal") + '-not-legal' + >>> is_normalized_name("-not-legal") # roundtrips, but not a valid name + False + + .. versionadded:: 23.2 + """ + return _normalized_regex.fullmatch(name) is not None + + +def canonicalize_version( + version: Version | str, *, strip_trailing_zero: bool = True +) -> str: + """Return a canonical form of a version as a string. + + This function takes a string representing a package version (or a + :class:`~packaging.version.Version` instance), and returns the + normalized form of it. By default, it strips trailing zeros from + the release segment. + + >>> from packaging.utils import canonicalize_version + >>> canonicalize_version('1.0.1') + '1.0.1' + + Per PEP 625, versions may have multiple canonical forms, differing + only by trailing zeros. + + >>> canonicalize_version('1.0.0') + '1' + >>> canonicalize_version('1.0.0', strip_trailing_zero=False) + '1.0.0' + + Invalid versions are returned unaltered. + + >>> canonicalize_version('foo bar baz') + 'foo bar baz' + + >>> canonicalize_version('1.4.0.0.0') + '1.4' + + .. versionadded:: 17.1 + + .. versionchanged:: 21.0 + The return type was narrowed to :class:`str`. + + .. versionchanged:: 22.0 + Added the *strip_trailing_zero* keyword parameter. + """ + if isinstance(version, str): + try: + version = Version(version) + except InvalidVersion: + return str(version) + return str(_TrimmedRelease(version) if strip_trailing_zero else version) + + +def parse_wheel_filename( + filename: str, + *, + validate_order: bool = False, +) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]: + """ + This function takes the filename of a wheel file, and parses it, + returning a tuple of name, version, build number, and tags. + + The name part of the tuple is normalized and typed as + :class:`NormalizedName`. The version portion is an instance of + :class:`~packaging.version.Version`. The build number is ``()`` if + there is no build number in the wheel filename, otherwise a + two-item tuple of an integer for the leading digits and + a string for the rest of the build number. The tags portion is a + frozen set of :class:`~packaging.tags.Tag` instances (as the tag + string format allows multiple tags to be combined into a single + string). + + If **validate_order** is true, compressed tag set components are + checked to be in sorted order as required by PEP 425. + + :param str filename: The name of the wheel file. + :param bool validate_order: Check whether compressed tag set components + are in sorted order. + :raises InvalidWheelFilename: If the filename in question + does not follow the :ref:`wheel specification + `. + + >>> from packaging.utils import parse_wheel_filename + >>> from packaging.tags import Tag + >>> from packaging.version import Version + >>> name, ver, build, tags = parse_wheel_filename("foo-1.0-py3-none-any.whl") + >>> name + 'foo' + >>> ver == Version('1.0') + True + >>> tags == {Tag("py3", "none", "any")} + True + >>> not build + True + + .. versionadded:: 20.9 + + .. versionchanged:: 23.2 + Raises :class:`InvalidWheelFilename` when the version component is invalid. + + .. versionadded:: 26.1 + The *validate_order* parameter. + + .. versionchanged:: 26.3 + Raises :class:`InvalidWheelFilename` when an interpreter component is + not an identifier, a tag set component is empty, or the project name is + empty. + """ + if not filename.endswith(".whl"): + raise InvalidWheelFilename( + f"Invalid wheel filename (extension must be '.whl'): {filename!r}" + ) + + filename = filename[:-4] + dashes = filename.count("-") + if dashes not in (4, 5): + raise InvalidWheelFilename( + f"Invalid wheel filename (wrong number of parts): {filename!r}" + ) + + parts = filename.split("-", dashes - 2) + name_part = parts[0] + # See PEP 427 for the rules on escaping the project name. + if "__" in name_part or _wheel_name_regex.match(name_part) is None: + raise InvalidWheelFilename(f"Invalid project name: {filename!r}") + name = canonicalize_name(name_part) + + try: + version = Version(parts[1]) + except InvalidVersion as e: + raise InvalidWheelFilename( + f"Invalid wheel filename (invalid version): {filename!r}" + ) from e + + if dashes == 5: + build_part = parts[2] + build_match = _build_tag_regex.match(build_part) + if build_match is None: + raise InvalidWheelFilename( + f"Invalid build number: {build_part} in {filename!r}" + ) + build = cast("BuildTag", (int(build_match.group(1)), build_match.group(2))) + else: + build = () + tag_str = parts[-1] + try: + tags = parse_tag(tag_str, validate_order=validate_order) + except UnsortedTagsError: + raise InvalidWheelFilename( + f"Invalid wheel filename (compressed tag set components must be in " + f"sorted order per PEP 425): {filename!r}" + ) from None + except InvalidTag: + raise InvalidWheelFilename( + f"Invalid wheel filename (invalid tag component): {filename!r}" + ) from None + return (name, version, build, tags) + + +def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]: + """ + This function takes the filename of a sdist file (as specified + in the `Source distribution format`_ documentation), and parses + it, returning a tuple of the normalized name and version as + represented by an instance of :class:`~packaging.version.Version`. + + :param str filename: The name of the sdist file. + :raises InvalidSdistFilename: If the filename does not end + with an sdist extension (``.zip`` or ``.tar.gz``), if it does not + contain a dash separating the name and the version of the distribution, + if the project name is empty, or if the version portion is not a valid + version. + + >>> from packaging.utils import parse_sdist_filename + >>> from packaging.version import Version + >>> name, ver = parse_sdist_filename("foo-1.0.tar.gz") + >>> name + 'foo' + >>> ver == Version('1.0') + True + + .. versionadded:: 20.9 + + .. versionchanged:: 21.0 + Added support for ``.zip`` source distributions. + + .. versionchanged:: 23.2 + Raises :class:`InvalidSdistFilename` when the version component is invalid. + + .. versionchanged:: 26.3 + Raises :class:`InvalidSdistFilename` on an empty project name. + + .. _Source distribution format: https://packaging.python.org/specifications/source-distribution-format/#source-distribution-file-name + """ + if filename.endswith(".tar.gz"): + file_stem = filename[: -len(".tar.gz")] + elif filename.endswith(".zip"): + file_stem = filename[: -len(".zip")] + else: + raise InvalidSdistFilename( + f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" + f" {filename!r}" + ) + + # We are requiring a PEP 440 version, which cannot contain dashes, + # so we split on the last dash. + name_part, sep, version_part = file_stem.rpartition("-") + if not sep: + raise InvalidSdistFilename(f"Invalid sdist filename: {filename!r}") + if not name_part: + raise InvalidSdistFilename( + f"Invalid sdist filename (empty project name): {filename!r}" + ) + + name = canonicalize_name(name_part) + + try: + version = Version(version_part) + except InvalidVersion as e: + raise InvalidSdistFilename( + f"Invalid sdist filename (invalid version): {filename!r}" + ) from e + + return (name, version) diff --git a/venv/lib/python3.12/site-packages/packaging/version.py b/venv/lib/python3.12/site-packages/packaging/version.py new file mode 100644 index 0000000..5a964bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/packaging/version.py @@ -0,0 +1,1252 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +""" +.. testsetup:: + + from packaging.version import parse, normalize_pre, Version, _cmpkey +""" + +from __future__ import annotations + +import re +import sys +import typing +from typing import ( + Any, + Callable, + Literal, + NamedTuple, + SupportsInt, + TypedDict, + Union, +) + +if typing.TYPE_CHECKING: + from typing_extensions import Self, Unpack + +if sys.version_info >= (3, 13): # pragma: no cover + from warnings import deprecated as _deprecated +elif typing.TYPE_CHECKING: + from typing_extensions import deprecated as _deprecated +else: # pragma: no cover + import functools + import warnings + + def _deprecated(message: str) -> object: + def decorator(func: Callable[[...], object]) -> object: + @functools.wraps(func) + def wrapper(*args: object, **kwargs: object) -> object: + warnings.warn( + message, + category=DeprecationWarning, + stacklevel=2, + ) + return func(*args, **kwargs) + + return wrapper + + return decorator + + +_LETTER_NORMALIZATION = { + "alpha": "a", + "beta": "b", + "c": "rc", + "pre": "rc", + "preview": "rc", + "rev": "post", + "r": "post", +} + +__all__ = ["VERSION_PATTERN", "InvalidVersion", "Version", "normalize_pre", "parse"] + + +def __dir__() -> list[str]: + return __all__ + + +LocalType = tuple[Union[int, str], ...] + +CmpLocalType = tuple[tuple[int, str], ...] +CmpSuffix = tuple[int, int, int, int, int, int] +CmpKey = Union[ + tuple[int, tuple[int, ...], CmpSuffix], + tuple[int, tuple[int, ...], CmpSuffix, CmpLocalType], +] +VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool] + + +class _VersionReplace(TypedDict, total=False): + epoch: int | None + release: tuple[int, ...] | None + pre: tuple[str, int] | None + post: int | None + dev: int | None + local: str | None + + +def normalize_pre(letter: str, /) -> str: + """Normalize the pre-release segment of a version string. + + Returns a lowercase version of the string if not a known pre-release + identifier. + + >>> normalize_pre('alpha') + 'a' + >>> normalize_pre('BETA') + 'b' + >>> normalize_pre('rc') + 'rc' + + :param letter: + + .. versionadded:: 26.1 + """ + letter = letter.lower() + return _LETTER_NORMALIZATION.get(letter, letter) + + +def parse(version: str) -> Version: + """Parse the given version string. + + This is identical to the :class:`Version` constructor. + + >>> parse('1.0.dev1') + + + :param version: The version string to parse. + :raises InvalidVersion: When the version string is not a valid version. + """ + return Version(version) + + +class InvalidVersion(ValueError): + """Raised when a version string is not a valid version. + + >>> Version("invalid") + Traceback (most recent call last): + ... + packaging.version.InvalidVersion: Invalid version: 'invalid' + """ + + +class _BaseVersion: + __slots__ = () + + # This can also be a normal member (see the packaging_legacy package); + # we are just requiring it to be readable. Actually defining a property + # has runtime effect on subclasses, so it's typing only. + if typing.TYPE_CHECKING: + + @property + def _key(self) -> tuple[Any, ...]: ... + + def __hash__(self) -> int: + return hash(self._key) + + # Please keep the duplicated `isinstance` check + # in the six comparisons hereunder + # unless you find a way to avoid adding overhead function calls. + def __lt__(self, other: _BaseVersion) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key < other._key + + def __le__(self, other: _BaseVersion) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key <= other._key + + def __eq__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key == other._key + + def __ge__(self, other: _BaseVersion) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key >= other._key + + def __gt__(self, other: _BaseVersion) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key > other._key + + def __ne__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key != other._key + + +# Deliberately not anchored to the start and end of the string, to make it +# easier for 3rd party code to reuse + +# Note that ++ doesn't behave identically on CPython and PyPy, so not using it here +_VERSION_PATTERN = r""" + v?+ # optional leading v + (?a: + (?:(?P[0-9]+)!)?+ # epoch + (?P[0-9]+(?:\.[0-9]+)*+) # release segment + (?P
                                          # pre-release
+            [._-]?+
+            (?Palpha|a|beta|b|preview|pre|c|rc)
+            [._-]?+
+            (?P[0-9]+)?
+        )?+
+        (?P                                         # post release
+            (?:-(?P[0-9]+))
+            |
+            (?:
+                [._-]?
+                (?Ppost|rev|r)
+                [._-]?
+                (?P[0-9]+)?
+            )
+        )?+
+        (?P                                          # dev release
+            [._-]?+
+            (?Pdev)
+            [._-]?+
+            (?P[0-9]+)?
+        )?+
+    )
+    (?a:\+
+        (?P                                        # local version
+            [a-z0-9]+
+            (?:[._-][a-z0-9]+)*+
+        )
+    )?+
+"""
+
+_VERSION_PATTERN_OLD = _VERSION_PATTERN.replace("*+", "*").replace("?+", "?")
+
+# Possessive qualifiers were added in Python 3.11.
+# CPython 3.11.0-3.11.4 had a bug: https://github.com/python/cpython/pull/107795
+# Older PyPy also had a bug.
+VERSION_PATTERN = (
+    _VERSION_PATTERN_OLD
+    if (sys.implementation.name == "cpython" and sys.version_info < (3, 11, 5))
+    or (sys.implementation.name == "pypy" and sys.version_info < (3, 11, 13))
+    or sys.version_info < (3, 11)
+    else _VERSION_PATTERN
+)
+"""
+A string containing the regular expression used to match a valid version.
+
+The pattern is not anchored at either end, and is intended for embedding in larger
+expressions (for example, matching a version number as part of a file name). The
+regular expression should be compiled with the ``re.VERBOSE`` and ``re.IGNORECASE``
+flags set.
+
+.. versionchanged:: 26.0
+
+   The regex now uses possessive qualifiers on Python 3.11 if they are
+   supported (CPython 3.11.5+, PyPy 3.11.13+).
+
+:meta hide-value:
+"""
+
+
+# Validation pattern for local version in replace()
+_LOCAL_PATTERN = re.compile(r"[a-z0-9]+(?:[._-][a-z0-9]+)*", re.IGNORECASE | re.ASCII)
+
+# Fast path: If a version has only digits and dots then we
+# can skip the regex and parse it as a release segment
+_SIMPLE_VERSION_INDICATORS = frozenset(".0123456789")
+
+
+def _validate_epoch(value: object, /) -> int:
+    epoch = value or 0
+    if isinstance(epoch, int) and epoch >= 0:
+        return epoch
+    msg = f"epoch must be non-negative integer, got {epoch}"
+    raise InvalidVersion(msg)
+
+
+def _validate_release(value: object, /) -> tuple[int, ...]:
+    release = (0,) if value is None else value
+    if (
+        isinstance(release, tuple)
+        and len(release) > 0
+        and all(isinstance(i, int) and i >= 0 for i in release)
+    ):
+        return release
+    msg = f"release must be a non-empty tuple of non-negative integers, got {release}"
+    raise InvalidVersion(msg)
+
+
+def _validate_pre(value: object, /) -> tuple[Literal["a", "b", "rc"], int] | None:
+    if value is None:
+        return value
+    if isinstance(value, tuple) and len(value) == 2:
+        letter, number = value
+        # The letter must be a string before it can be normalized.
+        if (
+            isinstance(letter, str)
+            and (normalized := normalize_pre(letter)) in {"a", "b", "rc"}
+            and isinstance(number, int)
+            and number >= 0
+        ):
+            # type checkers can't infer the Literal type here on letter
+            return (normalized, number)  # type: ignore[return-value]
+    msg = f"pre must be a tuple of ('a'|'b'|'rc', non-negative int), got {value}"
+    raise InvalidVersion(msg)
+
+
+def _validate_post(value: object, /) -> tuple[Literal["post"], int] | None:
+    if value is None:
+        return value
+    if isinstance(value, int) and value >= 0:
+        return ("post", value)
+    msg = f"post must be non-negative integer, got {value}"
+    raise InvalidVersion(msg)
+
+
+def _validate_dev(value: object, /) -> tuple[Literal["dev"], int] | None:
+    if value is None:
+        return value
+    if isinstance(value, int) and value >= 0:
+        return ("dev", value)
+    msg = f"dev must be non-negative integer, got {value}"
+    raise InvalidVersion(msg)
+
+
+def _validate_local(value: object, /) -> LocalType | None:
+    if value is None:
+        return value
+    if isinstance(value, str) and _LOCAL_PATTERN.fullmatch(value):
+        return _parse_local_version(value)
+    msg = f"local must be a valid version string, got {value!r}"
+    raise InvalidVersion(msg)
+
+
+# Backward compatibility for internals before 26.0. Do not use.
+class _Version(NamedTuple):
+    epoch: int
+    release: tuple[int, ...]
+    dev: tuple[Literal["dev"], int] | None
+    pre: tuple[Literal["a", "b", "rc"], int] | None
+    post: tuple[Literal["post"], int] | None
+    local: LocalType | None
+
+
+class Version(_BaseVersion):
+    """This class abstracts handling of a project's versions.
+
+    A :class:`Version` instance is comparison aware and can be compared and
+    sorted using the standard Python interfaces.
+
+    >>> v1 = Version("1.0a5")
+    >>> v2 = Version("1.0")
+    >>> v1
+    
+    >>> v2
+    
+    >>> v1 < v2
+    True
+    >>> v1 == v2
+    False
+    >>> v1 > v2
+    False
+    >>> v1 >= v2
+    False
+    >>> v1 <= v2
+    True
+
+    :class:`Version` is immutable; use :meth:`__replace__` to change
+    part of a version.
+
+    Instances are safe to serialize with :mod:`pickle`. They use a stable
+    format so the same pickle can be loaded in future packaging releases.
+
+    .. versionchanged:: 26.2
+
+        Added a stable pickle format. Pickles created with packaging 26.2+ can
+        be unpickled with future releases.  Backward compatibility with pickles
+        from packaging < 26.2 is supported but may be removed in a future
+        release.
+    """
+
+    __slots__ = (
+        "_dev",
+        "_epoch",
+        "_hash_cache",
+        "_key_cache",
+        "_local",
+        "_post",
+        "_pre",
+        "_release",
+    )
+    __match_args__ = ("_str",)
+    """
+    Pattern matching is supported on Python 3.10+.
+
+    .. versionadded:: 26.0
+
+    :meta hide-value:
+    """
+
+    _regex = re.compile(r"\s*" + VERSION_PATTERN + r"\s*", re.VERBOSE | re.IGNORECASE)
+
+    _epoch: int
+    _release: tuple[int, ...]
+    _dev: tuple[Literal["dev"], int] | None
+    _pre: tuple[Literal["a", "b", "rc"], int] | None
+    _post: tuple[Literal["post"], int] | None
+    _local: LocalType | None
+
+    _hash_cache: int | None
+    _key_cache: CmpKey | None
+
+    def __init__(self, version: str) -> None:
+        """Initialize a Version object.
+
+        :param version:
+            The string representation of a version which will be parsed and normalized
+            before use.
+        :raises InvalidVersion:
+            If the ``version`` does not conform to PEP 440 in any way then this
+            exception will be raised.
+        """
+        try:
+            is_simple = _SIMPLE_VERSION_INDICATORS.issuperset(version)
+        except TypeError:
+            raise InvalidVersion(f"Invalid version: {version!r}") from None
+
+        if is_simple:
+            try:
+                self._release = tuple(map(int, version.split(".")))
+            except AttributeError:
+                raise InvalidVersion(f"Invalid version: {version!r}") from None
+            except ValueError:
+                # Empty parts (from "1..2", ".1", etc.) are invalid versions.
+                # Any other ValueError (e.g. int str-digits limit) should
+                # propagate to the caller.
+                if "" in version.split("."):
+                    raise InvalidVersion(f"Invalid version: {version!r}") from None
+                # TODO: remove "no cover" when Python 3.9 is dropped.
+                raise  # pragma: no cover
+
+            self._epoch = 0
+            self._pre = None
+            self._post = None
+            self._dev = None
+            self._local = None
+            self._key_cache = None
+            self._hash_cache = None
+            return
+
+        # Validate the version and parse it into pieces
+        try:
+            match = self._regex.fullmatch(version)
+        except TypeError:
+            raise InvalidVersion(f"Invalid version: {version!r}") from None
+        if not match:
+            raise InvalidVersion(f"Invalid version: {version!r}")
+        self._epoch = int(match.group("epoch")) if match.group("epoch") else 0
+        self._release = tuple(map(int, match.group("release").split(".")))
+        # We can type ignore the assignments below because the regex guarantees
+        # the correct strings
+        self._pre = _parse_letter_version(match.group("pre_l"), match.group("pre_n"))  # type: ignore[assignment]
+        self._post = _parse_letter_version(  # type: ignore[assignment]
+            match.group("post_l"), match.group("post_n1") or match.group("post_n2")
+        )
+        self._dev = _parse_letter_version(match.group("dev_l"), match.group("dev_n"))  # type: ignore[assignment]
+        self._local = _parse_local_version(match.group("local"))
+
+        # Key which will be used for sorting
+        self._key_cache = None
+        self._hash_cache = None
+
+    @classmethod
+    def from_parts(
+        cls,
+        *,
+        epoch: int = 0,
+        release: tuple[int, ...],
+        pre: tuple[str, int] | None = None,
+        post: int | None = None,
+        dev: int | None = None,
+        local: str | None = None,
+    ) -> Self:
+        """
+        Return a new version composed of the various parts.
+
+        This allows you to build a version without going though a string and
+        running a regular expression. It normalizes pre-release strings. The
+        ``release=`` keyword argument is required.
+
+        >>> Version.from_parts(release=(1,2,3))
+        
+        >>> Version.from_parts(release=(0,1,0), pre=("b", 1))
+        
+
+        :param epoch:
+        :param release: This version tuple is required
+
+        .. versionadded:: 26.1
+        """
+        _epoch = _validate_epoch(epoch)
+        _release = _validate_release(release)
+        _pre = _validate_pre(pre) if pre is not None else None
+        _post = _validate_post(post) if post is not None else None
+        _dev = _validate_dev(dev) if dev is not None else None
+        _local = _validate_local(local) if local is not None else None
+
+        new_version = cls.__new__(cls)
+        new_version._key_cache = None
+        new_version._hash_cache = None
+        new_version._epoch = _epoch
+        new_version._release = _release
+        new_version._pre = _pre
+        new_version._post = _post
+        new_version._dev = _dev
+        new_version._local = _local
+
+        return new_version
+
+    def __replace__(self, **kwargs: Unpack[_VersionReplace]) -> Self:
+        """
+        __replace__(*, epoch=..., release=..., pre=..., post=..., dev=..., local=...)
+
+        Return a new version with parts replaced.
+
+        This returns a new version (unless no parts were changed). The
+        pre-release is normalized. Setting a value to ``None`` clears it.
+
+        >>> v = Version("1.2.3")
+        >>> v.__replace__(pre=("a", 1))
+        
+
+        :param int | None epoch:
+        :param tuple[int, ...] | None release:
+        :param tuple[str, int] | None pre:
+        :param int | None post:
+        :param int | None dev:
+        :param str | None local:
+
+        .. versionadded:: 26.0
+        .. versionchanged:: 26.1
+
+           The pre-release portion is now normalized.
+        """
+        epoch = _validate_epoch(kwargs["epoch"]) if "epoch" in kwargs else self._epoch
+        release = (
+            _validate_release(kwargs["release"])
+            if "release" in kwargs
+            else self._release
+        )
+        pre = _validate_pre(kwargs["pre"]) if "pre" in kwargs else self._pre
+        post = _validate_post(kwargs["post"]) if "post" in kwargs else self._post
+        dev = _validate_dev(kwargs["dev"]) if "dev" in kwargs else self._dev
+        local = _validate_local(kwargs["local"]) if "local" in kwargs else self._local
+
+        if (
+            epoch == self._epoch
+            and release == self._release
+            and pre == self._pre
+            and post == self._post
+            and dev == self._dev
+            and local == self._local
+        ):
+            return self
+
+        new_version = self.__class__.__new__(self.__class__)
+        new_version._key_cache = None
+        new_version._hash_cache = None
+        new_version._epoch = epoch
+        new_version._release = release
+        new_version._pre = pre
+        new_version._post = post
+        new_version._dev = dev
+        new_version._local = local
+
+        return new_version
+
+    @property
+    def _key(self) -> CmpKey:
+        if self._key_cache is None:
+            self._key_cache = _cmpkey(
+                self._epoch,
+                self._release,
+                self._pre,
+                self._post,
+                self._dev,
+                self._local,
+            )
+        return self._key_cache
+
+    # __hash__ must be defined when __eq__ is overridden,
+    # otherwise Python sets __hash__ to None.
+    def __hash__(self) -> int:
+        if (cached_hash := self._hash_cache) is not None:
+            return cached_hash
+
+        if (key := self._key_cache) is None:
+            self._key_cache = key = _cmpkey(
+                self._epoch,
+                self._release,
+                self._pre,
+                self._post,
+                self._dev,
+                self._local,
+            )
+        self._hash_cache = cached_hash = hash(key)
+        return cached_hash
+
+    # Override comparison methods to use direct _key_cache access
+    # This is faster than property access, especially before Python 3.12
+    def __lt__(self, other: _BaseVersion) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache < other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__lt__(other)
+
+    def __le__(self, other: _BaseVersion) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache <= other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__le__(other)
+
+    def __eq__(self, other: object) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache == other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__eq__(other)
+
+    def __ge__(self, other: _BaseVersion) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache >= other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__ge__(other)
+
+    def __gt__(self, other: _BaseVersion) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache > other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__gt__(other)
+
+    def __ne__(self, other: object) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache != other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__ne__(other)
+
+    def __getstate__(
+        self,
+    ) -> tuple[
+        int,
+        tuple[int, ...],
+        tuple[str, int] | None,
+        tuple[str, int] | None,
+        tuple[str, int] | None,
+        LocalType | None,
+    ]:
+        # Return state as a 6-item tuple for compactness:
+        #   (epoch, release, pre, post, dev, local)
+        # Cache members are excluded and will be recomputed on demand
+        return (
+            self._epoch,
+            self._release,
+            self._pre,
+            self._post,
+            self._dev,
+            self._local,
+        )
+
+    def __setstate__(self, state: object) -> None:
+        # Always discard cached values — they may contain stale references
+        # (e.g. packaging._structures.InfinityType from pre-26.1 pickles)
+        # and will be recomputed on demand from the core fields above.
+        self._key_cache = None
+        self._hash_cache = None
+
+        if isinstance(state, tuple):
+            if len(state) == 6:
+                # New format (26.2+): (epoch, release, pre, post, dev, local)
+                (
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                ) = state
+                return
+            if len(state) == 2:
+                # Format (packaging 26.0-26.1): (None, {slot: value}).
+                _, slot_dict = state
+                if isinstance(slot_dict, dict):
+                    self._epoch = slot_dict["_epoch"]
+                    self._release = slot_dict["_release"]
+                    self._pre = slot_dict.get("_pre")
+                    self._post = slot_dict.get("_post")
+                    self._dev = slot_dict.get("_dev")
+                    self._local = slot_dict.get("_local")
+                    return
+        if isinstance(state, dict):
+            # Old format (packaging <= 25.x, no __slots__): state is a plain
+            # dict with "_version" (_Version NamedTuple) and "_key" entries.
+            version_nt = state.get("_version")
+            if version_nt is not None:
+                self._epoch = version_nt.epoch
+                self._release = version_nt.release
+                self._pre = version_nt.pre
+                self._post = version_nt.post
+                self._dev = version_nt.dev
+                self._local = version_nt.local
+                return
+
+        raise TypeError(f"Cannot restore Version from {state!r}")
+
+    @property
+    @_deprecated("Version._version is private and will be removed soon")
+    def _version(self) -> _Version:
+        return _Version(
+            self._epoch, self._release, self._dev, self._pre, self._post, self._local
+        )
+
+    @_version.setter
+    @_deprecated("Version._version is private and will be removed soon")
+    def _version(self, value: _Version) -> None:
+        self._epoch = value.epoch
+        self._release = value.release
+        self._dev = value.dev
+        self._pre = value.pre
+        self._post = value.post
+        self._local = value.local
+        self._key_cache = None
+        self._hash_cache = None
+
+    def __repr__(self) -> str:
+        """A representation of the Version that shows all internal state.
+
+        >>> Version('1.0.0')
+        
+        """
+        return f"<{self.__class__.__name__}({str(self)!r})>"
+
+    def __str__(self) -> str:
+        """A string representation of the version that can be round-tripped.
+
+        >>> str(Version("1.0a5"))
+        '1.0a5'
+        """
+        # This is a hot function, so not calling self.base_version
+        version = ".".join(map(str, self.release))
+
+        # Epoch
+        if self.epoch:
+            version = f"{self.epoch}!{version}"
+
+        # Pre-release
+        if self.pre is not None:
+            version += "".join(map(str, self.pre))
+
+        # Post-release
+        if self.post is not None:
+            version += f".post{self.post}"
+
+        # Development release
+        if self.dev is not None:
+            version += f".dev{self.dev}"
+
+        # Local version segment
+        if self.local is not None:
+            version += f"+{self.local}"
+
+        return version
+
+    @property
+    def _str(self) -> str:
+        """Internal property for match_args"""
+        return str(self)
+
+    @property
+    def epoch(self) -> int:
+        """The epoch of the version.
+
+        >>> Version("2.0.0").epoch
+        0
+        >>> Version("1!2.0.0").epoch
+        1
+        """
+        return self._epoch
+
+    @property
+    def release(self) -> tuple[int, ...]:
+        """The components of the "release" segment of the version.
+
+        >>> Version("1.2.3").release
+        (1, 2, 3)
+        >>> Version("2.0.0").release
+        (2, 0, 0)
+        >>> Version("1!2.0.0.post0").release
+        (2, 0, 0)
+
+        Includes trailing zeroes but not the epoch or any pre-release / development /
+        post-release suffixes.
+        """
+        return self._release
+
+    @property
+    def pre(self) -> tuple[Literal["a", "b", "rc"], int] | None:
+        """The pre-release segment of the version.
+
+        >>> print(Version("1.2.3").pre)
+        None
+        >>> Version("1.2.3a1").pre
+        ('a', 1)
+        >>> Version("1.2.3b1").pre
+        ('b', 1)
+        >>> Version("1.2.3rc1").pre
+        ('rc', 1)
+        """
+        return self._pre
+
+    @property
+    def post(self) -> int | None:
+        """The post-release number of the version.
+
+        >>> print(Version("1.2.3").post)
+        None
+        >>> Version("1.2.3.post1").post
+        1
+        """
+        return self._post[1] if self._post else None
+
+    @property
+    def dev(self) -> int | None:
+        """The development number of the version.
+
+        >>> print(Version("1.2.3").dev)
+        None
+        >>> Version("1.2.3.dev1").dev
+        1
+        """
+        return self._dev[1] if self._dev else None
+
+    @property
+    def local(self) -> str | None:
+        """The local version segment of the version.
+
+        >>> print(Version("1.2.3").local)
+        None
+        >>> Version("1.2.3+abc").local
+        'abc'
+        """
+        if self._local:
+            return ".".join(str(x) for x in self._local)
+        else:
+            return None
+
+    @property
+    def public(self) -> str:
+        """The public portion of the version.
+
+        This returns a string. If you want a :class:`Version` again and care
+        about performance, use ``v.__replace__(local=None)`` instead.
+
+        >>> Version("1.2.3").public
+        '1.2.3'
+        >>> Version("1.2.3+abc").public
+        '1.2.3'
+        >>> Version("1!1.2.3dev1+abc").public
+        '1!1.2.3.dev1'
+        """
+        return str(self).split("+", 1)[0]
+
+    @property
+    def base_version(self) -> str:
+        """The "base version" of the version.
+
+        This returns a string. If you want a :class:`Version` again and care
+        about performance, use
+        ``v.__replace__(pre=None, post=None, dev=None, local=None)`` instead.
+
+        >>> Version("1.2.3").base_version
+        '1.2.3'
+        >>> Version("1.2.3+abc").base_version
+        '1.2.3'
+        >>> Version("1!1.2.3dev1+abc").base_version
+        '1!1.2.3'
+
+        The "base version" is the public version of the project without any pre or post
+        release markers.
+        """
+        release_segment = ".".join(map(str, self.release))
+        return f"{self.epoch}!{release_segment}" if self.epoch else release_segment
+
+    @property
+    def is_prerelease(self) -> bool:
+        """Whether this version is a pre-release.
+
+        >>> Version("1.2.3").is_prerelease
+        False
+        >>> Version("1.2.3a1").is_prerelease
+        True
+        >>> Version("1.2.3b1").is_prerelease
+        True
+        >>> Version("1.2.3rc1").is_prerelease
+        True
+        >>> Version("1.2.3dev1").is_prerelease
+        True
+        """
+        return self.dev is not None or self.pre is not None
+
+    @property
+    def is_postrelease(self) -> bool:
+        """Whether this version is a post-release.
+
+        >>> Version("1.2.3").is_postrelease
+        False
+        >>> Version("1.2.3.post1").is_postrelease
+        True
+        """
+        return self.post is not None
+
+    @property
+    def is_devrelease(self) -> bool:
+        """Whether this version is a development release.
+
+        >>> Version("1.2.3").is_devrelease
+        False
+        >>> Version("1.2.3.dev1").is_devrelease
+        True
+        """
+        return self.dev is not None
+
+    @property
+    def major(self) -> int:
+        """The first item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").major
+        1
+
+        .. versionadded:: 20.0
+        """
+        return self.release[0] if len(self.release) >= 1 else 0
+
+    @property
+    def minor(self) -> int:
+        """The second item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").minor
+        2
+        >>> Version("1").minor
+        0
+
+        .. versionadded:: 20.0
+        """
+        return self.release[1] if len(self.release) >= 2 else 0
+
+    @property
+    def micro(self) -> int:
+        """The third item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").micro
+        3
+        >>> Version("1").micro
+        0
+
+        .. versionadded:: 20.0
+        """
+        return self.release[2] if len(self.release) >= 3 else 0
+
+
+class _TrimmedRelease(Version):
+    __slots__ = ()
+
+    def __init__(self, version: str | Version) -> None:
+        if isinstance(version, Version):
+            self._epoch = version._epoch
+            self._release = version._release
+            self._dev = version._dev
+            self._pre = version._pre
+            self._post = version._post
+            self._local = version._local
+            self._key_cache = version._key_cache
+            self._hash_cache = version._hash_cache
+            return
+        super().__init__(version)  # pragma: no cover
+
+    @property
+    def release(self) -> tuple[int, ...]:
+        """
+        Release segment without any trailing zeros.
+
+        >>> _TrimmedRelease('1.0.0').release
+        (1,)
+        >>> _TrimmedRelease('0.0').release
+        (0,)
+        """
+        # This leaves one 0.
+        rel = super().release
+        len_release = len(rel)
+        i = len_release
+        while i > 1 and rel[i - 1] == 0:
+            i -= 1
+        return rel if i == len_release else rel[:i]
+
+
+def _parse_letter_version(
+    letter: str | None, number: str | bytes | SupportsInt | None
+) -> tuple[str, int] | None:
+    if letter:
+        # We normalize any letters to their lower case form
+        letter = letter.lower()
+
+        # We consider some words to be alternate spellings of other words and
+        # in those cases we want to normalize the spellings to our preferred
+        # spelling.
+        letter = _LETTER_NORMALIZATION.get(letter, letter)
+
+        # We consider there to be an implicit 0 in a pre-release if there is
+        # not a numeral associated with it.
+        return letter, int(number or 0)
+
+    if number:
+        # We assume if we are given a number, but we are not given a letter
+        # then this is using the implicit post release syntax (e.g. 1.0-1)
+        return "post", int(number)
+
+    return None
+
+
+_local_version_separators = re.compile(r"[\._-]")
+
+
+def _parse_local_version(local: str | None) -> LocalType | None:
+    """
+    Takes a string like ``"abc.1.twelve"`` and turns it into
+    ``("abc", 1, "twelve")``.
+    """
+    if local is not None:
+        return tuple(
+            part.lower() if not part.isdigit() else int(part)
+            for part in _local_version_separators.split(local)
+        )
+    return None
+
+
+# Sort ranks for pre-release: dev-only < a < b < rc < stable (no pre-release).
+_PRE_RANK = {"a": 0, "b": 1, "rc": 2}
+_PRE_RANK_DEV_ONLY = -1  # sorts before a(0)
+_PRE_RANK_STABLE = 3  # sorts after rc(2)
+
+# In local version segments, strings sort before ints per PEP 440.
+_LOCAL_STR_RANK = -1  # sorts before all non-negative ints
+
+# Pre-computed suffix for stable releases (no pre, post, or dev segments).
+# See _cmpkey() for the suffix layout.
+_STABLE_SUFFIX = (_PRE_RANK_STABLE, 0, 0, 0, 1, 0)
+
+
+def _cmpkey(
+    epoch: int,
+    release: tuple[int, ...],
+    pre: tuple[str, int] | None,
+    post: tuple[str, int] | None,
+    dev: tuple[str, int] | None,
+    local: LocalType | None,
+) -> CmpKey:
+    """Build a comparison key for PEP 440 ordering.
+
+    Returns ``(epoch, release, suffix)`` or
+    ``(epoch, release, suffix, local)`` so that plain tuple
+    comparison gives the correct order.
+
+    Trailing zeros are stripped from the release so that ``1.0.0 == 1``.
+
+    The suffix is a flat 6-int tuple that encodes pre/post/dev:
+    ``(pre_rank, pre_n, post_rank, post_n, dev_rank, dev_n)``
+
+    pre_rank: dev-only=-1, a=0, b=1, rc=2, no-pre=3
+        Dev-only releases (no pre or post) get -1 so they sort before
+        any alpha/beta/rc.  Releases without a pre-release tag get 3
+        so they sort after rc.
+    post_rank: no-post=0, post=1
+        Releases without a post segment sort before those with one.
+    dev_rank: dev=0, no-dev=1
+        Releases without a dev segment sort after those with one.
+
+    Local segments use ``(n, "")`` for ints and ``(-1, s)`` for strings,
+    following PEP 440: strings sort before ints, strings compare
+    lexicographically, ints compare numerically, and shorter segments
+    sort before longer when prefixes match.  Versions without a local
+    segment sort before those with one (3-tuple < 4-tuple).
+
+    >>> _cmpkey(0, (1, 0, 0), None, None, None, None)
+    (0, (1,), (3, 0, 0, 0, 1, 0))
+    >>> _cmpkey(0, (1,), ("a", 1), None, None, None)
+    (0, (1,), (0, 1, 0, 0, 1, 0))
+    >>> _cmpkey(0, (1,), None, None, None, ("ubuntu", 1))
+    (0, (1,), (3, 0, 0, 0, 1, 0), ((-1, 'ubuntu'), (1, '')))
+    """
+    # Strip trailing zeros: 1.0.0 compares equal to 1.
+    len_release = len(release)
+    i = len_release
+    while i and release[i - 1] == 0:
+        i -= 1
+    trimmed = release if i == len_release else release[:i]
+
+    # Fast path: stable release with no local segment.
+    if pre is None and post is None and dev is None and local is None:
+        return epoch, trimmed, _STABLE_SUFFIX
+
+    if pre is None and post is None and dev is not None:
+        # dev-only (e.g. 1.0.dev1) sorts before all pre-releases.
+        pre_rank, pre_n = _PRE_RANK_DEV_ONLY, 0
+    elif pre is None:
+        pre_rank, pre_n = _PRE_RANK_STABLE, 0
+    else:
+        pre_rank, pre_n = _PRE_RANK[pre[0]], pre[1]
+
+    post_rank = 0 if post is None else 1
+    post_n = 0 if post is None else post[1]
+
+    dev_rank = 1 if dev is None else 0
+    dev_n = 0 if dev is None else dev[1]
+
+    suffix = (pre_rank, pre_n, post_rank, post_n, dev_rank, dev_n)
+
+    if local is None:
+        return epoch, trimmed, suffix
+
+    cmp_local: CmpLocalType = tuple(
+        (seg, "") if isinstance(seg, int) else (_LOCAL_STR_RANK, seg) for seg in local
+    )
+    return epoch, trimmed, suffix, cmp_local
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/METADATA b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/METADATA
new file mode 100644
index 0000000..29576e2
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/METADATA
@@ -0,0 +1,110 @@
+Metadata-Version: 2.4
+Name: pip
+Version: 26.2.1
+Summary: The PyPA recommended tool for installing Python packages.
+Author-email: The pip developers 
+Requires-Python: >=3.10
+Description-Content-Type: text/x-rst
+License-Expression: MIT
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Topic :: Software Development :: Build Tools
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
+Classifier: Programming Language :: Python :: 3.15
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+License-File: AUTHORS.txt
+License-File: LICENSE.txt
+License-File: src/pip/_vendor/cachecontrol/LICENSE.txt
+License-File: src/pip/_vendor/certifi/LICENSE
+License-File: src/pip/_vendor/distlib/LICENSE.txt
+License-File: src/pip/_vendor/distro/LICENSE
+License-File: src/pip/_vendor/idna/LICENSE.md
+License-File: src/pip/_vendor/msgpack/COPYING
+License-File: src/pip/_vendor/packaging/LICENSE
+License-File: src/pip/_vendor/packaging/LICENSE.APACHE
+License-File: src/pip/_vendor/packaging/LICENSE.BSD
+License-File: src/pip/_vendor/pkg_resources/LICENSE
+License-File: src/pip/_vendor/platformdirs/LICENSE
+License-File: src/pip/_vendor/pygments/LICENSE
+License-File: src/pip/_vendor/pyproject_hooks/LICENSE
+License-File: src/pip/_vendor/requests/LICENSE
+License-File: src/pip/_vendor/resolvelib/LICENSE
+License-File: src/pip/_vendor/rich/LICENSE
+License-File: src/pip/_vendor/tomli/LICENSE
+License-File: src/pip/_vendor/tomli_w/LICENSE
+License-File: src/pip/_vendor/truststore/LICENSE
+License-File: src/pip/_vendor/urllib3/LICENSE.txt
+Project-URL: Changelog, https://pip.pypa.io/en/stable/news/
+Project-URL: Documentation, https://pip.pypa.io
+Project-URL: Homepage, https://pip.pypa.io/
+Project-URL: Source, https://github.com/pypa/pip
+
+pip - The Python Package Installer
+==================================
+
+.. |pypi-version| image:: https://img.shields.io/pypi/v/pip.svg
+   :target: https://pypi.org/project/pip/
+   :alt: PyPI
+
+.. |python-versions| image:: https://img.shields.io/pypi/pyversions/pip
+   :target: https://pypi.org/project/pip
+   :alt: PyPI - Python Version
+
+.. |docs-badge| image:: https://readthedocs.org/projects/pip/badge/?version=latest
+   :target: https://pip.pypa.io/en/latest
+   :alt: Documentation
+
+|pypi-version| |python-versions| |docs-badge|
+
+Pip is the `package installer`_ for Python. You can use pip to install packages from the `Python Package Index`_ and other indexes.
+
+Please take a look at our documentation for how to install and use pip:
+
+* `Installation`_
+* `Usage`_
+
+We release updates regularly, with a new version every 3 months. Find more details in our documentation:
+
+* `Release notes`_
+* `Release process`_
+
+If you find bugs, need help, or want to talk to the developers, please use our mailing lists or chat rooms:
+
+* `Issue tracking`_
+* `Discourse channel`_
+* `User IRC`_
+
+If you want to get involved, head over to GitHub to get the source code, look at our development documentation and feel free to jump on the developer mailing lists and chat rooms:
+
+* `GitHub page`_
+* `Development documentation`_
+* `Development IRC`_
+
+Code of Conduct
+---------------
+
+Everyone interacting in the pip project's codebases, issue trackers, chat
+rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.
+
+.. _package installer: https://packaging.python.org/guides/tool-recommendations/
+.. _Python Package Index: https://pypi.org
+.. _Installation: https://pip.pypa.io/en/stable/installation/
+.. _Usage: https://pip.pypa.io/en/stable/
+.. _Release notes: https://pip.pypa.io/en/stable/news.html
+.. _Release process: https://pip.pypa.io/en/latest/development/release-process/
+.. _GitHub page: https://github.com/pypa/pip
+.. _Development documentation: https://pip.pypa.io/en/latest/development
+.. _Issue tracking: https://github.com/pypa/pip/issues
+.. _Discourse channel: https://discuss.python.org/c/packaging
+.. _User IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa
+.. _Development IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa-dev
+.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md
+
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/RECORD
new file mode 100644
index 0000000..68bd800
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/RECORD
@@ -0,0 +1,885 @@
+../../../bin/pip,sha256=vg-SR6MFfnhO5pGLDN41ceMMMY4KYsYm2E_HA-7BQnY,246
+../../../bin/pip3,sha256=vg-SR6MFfnhO5pGLDN41ceMMMY4KYsYm2E_HA-7BQnY,246
+../../../bin/pip3.12,sha256=vg-SR6MFfnhO5pGLDN41ceMMMY4KYsYm2E_HA-7BQnY,246
+pip-26.2.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+pip-26.2.1.dist-info/METADATA,sha256=OlYQBYIoQfLEX0QvbHp1BRcsjDdsn-J02HbIrJEpaMs,4617
+pip-26.2.1.dist-info/RECORD,,
+pip-26.2.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip-26.2.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
+pip-26.2.1.dist-info/entry_points.txt,sha256=Vhf8s0IYgX37mtd4vGL73BPcxdKnqeCFPzB5-d30x8o,84
+pip-26.2.1.dist-info/licenses/AUTHORS.txt,sha256=TrH7FwXi3viY6BvLoZw6rIBiDSe6NWnWpt97RinRtcI,12139
+pip-26.2.1.dist-info/licenses/LICENSE.txt,sha256=Y0MApmnUmurmWxLGxIySTFGkzfPR_whtw0VtyLyqIQQ,1093
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt,sha256=hu7uh74qQ_P_H1ZJb0UfaSQ5JvAl_tuwM2ZsMExMFhs,558
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE,sha256=6TcW2mucDVpKHfYP5pWzcPBpVgPSH2-D8FPkLPwQyvc,989
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt,sha256=gI4QyKarjesUn_mz-xn0R6gICUYG1xKpylf-rTVSWZ0,14531
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE,sha256=y16Ofl9KOYjhBjwULGDcLfdWBfTEZRXnduOspt-XbhQ,11325
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md,sha256=GppPDj1HmickDd1ZqRN6ZqtKD539yMphiMwL_YUYfwQ,1541
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/msgpack/COPYING,sha256=SS3tuoXaWHL3jmCRvNH-pHTWYNNay03ulkuKqz8AdCc,614
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE,sha256=KeD9YukphQ6G6yjD_czwzv30-pSHkBHP-z0NS-1tTbY,1089
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE,sha256=qdZvHVJt8C4p3Oc0NtNOVuhjL0bCdbvf_HBWnogvnxc,1331
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE,sha256=GyKwSbUmfW38I6Z79KhNjsBLn9-xpR02DkK0NCyLQVQ,1081
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE,sha256=84j9OMrRMRLB3A9mm76A5_hFQe26-3LzAw0sp2QsPJ0,751
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE,sha256=3u18F6QxgVgZCj6iOcyHmlpQJxzruYrnAl9I--WNyhU,1056
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE,sha256=M757fo-k_Rmxdg4ajtimaL2rhSyRtpLdQUJLy3Jan8o,1086
+pip-26.2.1.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt,sha256=Ew46ZNX91dCWp1JpRjSn2d8oRGnehuVzIQAmgEHj1oY,1093
+pip/__init__.py,sha256=BKp2zqsPQ0HiktquNWN_L4cYsYMxPML7WI0TBMucBEM,355
+pip/__main__.py,sha256=rOZRtrXjDBzY24niaxTnd9ZHHWL7B0EawVdLoJ3nI6c,874
+pip/__pip-runner__.py,sha256=720Mt6h07Uce52v80EOF__JYauoLw9b7Pfs_5B91isg,1451
+pip/__pycache__/__init__.cpython-312.pyc,,
+pip/__pycache__/__main__.cpython-312.pyc,,
+pip/__pycache__/__pip-runner__.cpython-312.pyc,,
+pip/_internal/__init__.py,sha256=S7i9Dn9aSZS0MG-2Wrve3dV9TImPzvQn5jjhp9t_uf0,511
+pip/_internal/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/__pycache__/cache.cpython-312.pyc,,
+pip/_internal/__pycache__/configuration.cpython-312.pyc,,
+pip/_internal/__pycache__/exceptions.cpython-312.pyc,,
+pip/_internal/__pycache__/main.cpython-312.pyc,,
+pip/_internal/__pycache__/pyproject.cpython-312.pyc,,
+pip/_internal/__pycache__/self_outdated_check.cpython-312.pyc,,
+pip/_internal/__pycache__/wheel_builder.cpython-312.pyc,,
+pip/_internal/build_env/__init__.py,sha256=AI5x2Kfv1exG1IbsNHuIV1agqNgstTj_LWpbwWLq8ok,788
+pip/_internal/build_env/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/build_env/__pycache__/base.cpython-312.pyc,,
+pip/_internal/build_env/__pycache__/installer.cpython-312.pyc,,
+pip/_internal/build_env/__pycache__/noop.cpython-312.pyc,,
+pip/_internal/build_env/__pycache__/venv.cpython-312.pyc,,
+pip/_internal/build_env/__pycache__/virtual.cpython-312.pyc,,
+pip/_internal/build_env/base.py,sha256=7RHW5X-QSVWHeK7_5Uei9Gbq0q1pAXHF9tq7Mj3wtTs,3440
+pip/_internal/build_env/installer.py,sha256=eMUytQCdBiQ9TgkaGeTAzq_IVBqLHyD9OJ_kNTIjjcM,13046
+pip/_internal/build_env/noop.py,sha256=FXk9ZFTTN0s1cecZE6ycN2ZznBh75Ok2I3QXF8HTo6I,1001
+pip/_internal/build_env/venv.py,sha256=PsDl0PpmB9jRNaIawhy6Xk9R8WcYoAogLS2afmZ9AQc,5692
+pip/_internal/build_env/virtual.py,sha256=HsVRzzhgBGyy06DZBoO0PW_3kIEacy9S4TI5H1q72Hk,4858
+pip/_internal/cache.py,sha256=eqMQVfpD8x7GcCO0V0z73mnjPIuSapMFoQ_WRE7iLY8,10457
+pip/_internal/cli/__init__.py,sha256=Iqg_tKA771XuMO1P4t_sDHnSKPzkUb9D0DqunAmw_ko,131
+pip/_internal/cli/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc,,
+pip/_internal/cli/__pycache__/base_command.cpython-312.pyc,,
+pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc,,
+pip/_internal/cli/__pycache__/command_context.cpython-312.pyc,,
+pip/_internal/cli/__pycache__/index_command.cpython-312.pyc,,
+pip/_internal/cli/__pycache__/main.cpython-312.pyc,,
+pip/_internal/cli/__pycache__/main_parser.cpython-312.pyc,,
+pip/_internal/cli/__pycache__/parser.cpython-312.pyc,,
+pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc,,
+pip/_internal/cli/__pycache__/req_command.cpython-312.pyc,,
+pip/_internal/cli/__pycache__/spinners.cpython-312.pyc,,
+pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc,,
+pip/_internal/cli/autocompletion.py,sha256=gFtmiFhHccizyx4JPJnEKik-uftU7kwrUvm72cSFPHI,7353
+pip/_internal/cli/base_command.py,sha256=yF5_mQEIrLG2G0nvymZS0nLhiDg4B0T89W5iX78H558,9431
+pip/_internal/cli/cmdoptions.py,sha256=aBNxaIy4YV4AfjAqrG8RoZFcv7kwWzRAzg5mnZmOGDw,41180
+pip/_internal/cli/command_context.py,sha256=kmu3EWZbfBega1oDamnGJTA_UaejhIQNuMj2CVmMXu0,817
+pip/_internal/cli/index_command.py,sha256=_Qzc9oHnButVvD0wxYcHfe0uJZqRWOtIg5HaQpnSNeM,7504
+pip/_internal/cli/main.py,sha256=O10guzBqF2MykAaZYo6sua097dXqC3nozKodtTOpHDc,3209
+pip/_internal/cli/main_parser.py,sha256=7PhEfZ8AXiWUpTfB_KJMngI2sF8XIXl52alI1LZ_e_M,4368
+pip/_internal/cli/parser.py,sha256=WvRHuI2MdFp187BWrY_D17L02asq9jmCnng81af52SQ,14147
+pip/_internal/cli/progress_bars.py,sha256=13U3AY3DYwdEIwPYLtUn5B4QcpJUApLWCkfJ-u_PqzY,4706
+pip/_internal/cli/req_command.py,sha256=wW-mRcPqPczeqxp9SQ_yh3YfrRAYAlEAZextlLcDbUc,18952
+pip/_internal/cli/spinners.py,sha256=EJzZIZNyUtJljp3-WjcsyIrqxW-HUsfWzhuW84n_Tqw,7362
+pip/_internal/cli/status_codes.py,sha256=9QhNe6hHqsiw5y5wufiNKBOgJjV0HF3I3P2K21nj-OM,136
+pip/_internal/commands/__init__.py,sha256=aNeCbQurGWihfhQq7BqaLXHqWDQ0i3I04OS7kxK6plQ,4026
+pip/_internal/commands/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/cache.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/check.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/completion.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/configuration.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/debug.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/download.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/freeze.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/hash.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/help.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/index.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/inspect.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/install.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/list.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/lock.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/search.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/show.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/uninstall.cpython-312.pyc,,
+pip/_internal/commands/__pycache__/wheel.cpython-312.pyc,,
+pip/_internal/commands/cache.py,sha256=YLZrLWNvChl0pImBkoSZGGD3fb2MfdYGpYF4J0aQs6w,9251
+pip/_internal/commands/check.py,sha256=hVFBQezQ3zj4EydoWbFQj_afPUppMt7r9JPAlY22U6Y,2244
+pip/_internal/commands/completion.py,sha256=LjvRIZ6QUiDXJL3IOMFeD-_J97HfjMGgEk0j2tWGu1U,4565
+pip/_internal/commands/configuration.py,sha256=5M6KM30G3i6oMHAsVUSWP4E93cSSJQoJ6evURC754E8,10132
+pip/_internal/commands/debug.py,sha256=tCWUOEyz2HdlLGTvzNqSZIAJQkQYCy58zBTdfIWyzSo,6556
+pip/_internal/commands/download.py,sha256=cuXEj9PmoZ4kCsxm_BaZ_OkmIsV_gluLiOVSuOO2MS0,5404
+pip/_internal/commands/freeze.py,sha256=Lg8EOokxnRZrwdLWJgLpBWxNUt7-HrkWlEC05e-vBww,3100
+pip/_internal/commands/hash.py,sha256=GO9pRN3wXC2kQaovK57TaLYBMc3IltOH92O6QEw6YE0,1679
+pip/_internal/commands/help.py,sha256=Bz3LcjNQXkz4Cu__pL4CZ86o4-HNLZj1NZWdlJhjuu0,1108
+pip/_internal/commands/index.py,sha256=ny_hGzXuZHzjsq4CIJh_6YfZGVJm7G7LqQQdKTibJ_Q,5608
+pip/_internal/commands/inspect.py,sha256=iiAM-JuL_Z1-QP34cgAloZPSr50Mc7wAvuFDRzHQrQk,3185
+pip/_internal/commands/install.py,sha256=DREULA-TGbCKhqBBLkrbDKztyQ557QbDe6ZJUYuh2yk,33761
+pip/_internal/commands/list.py,sha256=W-FyxXTrHJhqRl4ZoqFp68_5GAwC9GYuY6fhFTJdHzU,13908
+pip/_internal/commands/lock.py,sha256=VhpNRWItLdqywSOIqXg3vuIyf59cozh09-Z6Z9cMiLM,5987
+pip/_internal/commands/search.py,sha256=zbMsX_YASj6kXA6XIBgTDv0bGK51xG-CV3IynZJcE-c,5782
+pip/_internal/commands/show.py,sha256=91_XljhCHWyCNfoFBa4XdacbumQgoNbhkcHHk_gcW9Y,8247
+pip/_internal/commands/uninstall.py,sha256=CsOihqvb6ZA6O67L70oXeoLHeOfNzMM88H9g-9aocgw,3868
+pip/_internal/commands/wheel.py,sha256=5m3NvVKatqLDOVoC_4WOFiDDrvQRjB52jkbvpbZApug,6187
+pip/_internal/configuration.py,sha256=0eEdh1xTlOHhoCrTtwHYM5DEj7BbTmhIaCaNNj3srBM,14562
+pip/_internal/distributions/__init__.py,sha256=Hq6kt6gXBgjNit5hTTWLAzeCNOKoB-N0pGYSqehrli8,858
+pip/_internal/distributions/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/distributions/__pycache__/base.cpython-312.pyc,,
+pip/_internal/distributions/__pycache__/installed.cpython-312.pyc,,
+pip/_internal/distributions/__pycache__/sdist.cpython-312.pyc,,
+pip/_internal/distributions/__pycache__/wheel.cpython-312.pyc,,
+pip/_internal/distributions/base.py,sha256=BjYazg2EhbEgrO1W_JD6jEZIDVlUpA_WZkqU-NvCBkQ,1907
+pip/_internal/distributions/installed.py,sha256=QPDhCqc-GTGiWZAAOFaVKMETg1m_kYuf6RBO9ZZlDqY,994
+pip/_internal/distributions/sdist.py,sha256=hof85VbuXa_NydvFZj6zOtnxuPY15tsuCJVirLuNT-U,7783
+pip/_internal/distributions/wheel.py,sha256=qOMVYzKT3tJsO0QundLjADcFiW_RODui4K51XRqMru8,1429
+pip/_internal/exceptions.py,sha256=Zdu17pVbD1eI_yjg52Zd0ygAWDy8LtXS1Atfvn1pPts,38302
+pip/_internal/index/__init__.py,sha256=tzwMH_fhQeubwMqHdSivasg1cRgTSbNg2CiMVnzMmyU,29
+pip/_internal/index/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/index/__pycache__/collector.cpython-312.pyc,,
+pip/_internal/index/__pycache__/package_finder.cpython-312.pyc,,
+pip/_internal/index/__pycache__/sources.cpython-312.pyc,,
+pip/_internal/index/collector.py,sha256=bu-NBVToIeyYEbcv0_gtInAxsCK3E0yI6Je6yxAwE5k,16578
+pip/_internal/index/package_finder.py,sha256=RoWK5rPkOcmOdiWEXhnk9aaFDgaj4QEE70FeQokqZf4,43149
+pip/_internal/index/sources.py,sha256=fqqVli93Lr-HNk-wFD67yyfZzS6WeDkSZGv1RFw63QE,8621
+pip/_internal/locations/__init__.py,sha256=iP9yVZn_4iPuNcaUNh2A_vJLcvUZ-8y4zvKLDVEqaM0,14022
+pip/_internal/locations/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/locations/__pycache__/_distutils.cpython-312.pyc,,
+pip/_internal/locations/__pycache__/_sysconfig.cpython-312.pyc,,
+pip/_internal/locations/__pycache__/base.cpython-312.pyc,,
+pip/_internal/locations/_distutils.py,sha256=jpFj4V00rD9IR3vA9TqrGkwcdNVFc58LsChZavge9JY,5975
+pip/_internal/locations/_sysconfig.py,sha256=HoZYQ1sVQD3IRpQCp4TpdTQLNrDFFzI9zRjXrTYnJWY,7788
+pip/_internal/locations/base.py,sha256=5mVcXh9jnSepEO25aG2U-qZWguzeoOPIbT0mRiBDDME,2548
+pip/_internal/main.py,sha256=1cHqjsfFCrMFf3B5twzocxTJUdHMLoXUpy5lJoFqUi8,338
+pip/_internal/metadata/__init__.py,sha256=vp-JAxiWg_-l5F8AT0Jcey72uUnh8CDwwol9-KktHZ8,5824
+pip/_internal/metadata/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/metadata/__pycache__/_json.cpython-312.pyc,,
+pip/_internal/metadata/__pycache__/base.cpython-312.pyc,,
+pip/_internal/metadata/__pycache__/pkg_resources.cpython-312.pyc,,
+pip/_internal/metadata/_json.py,sha256=hNvnMHOXLAyNlzirWhPL9Nx2CvCqa1iRma6Osq1YfV8,2711
+pip/_internal/metadata/base.py,sha256=_o9lMyHmia4Ea9fhceyB9rp3jUUU8k7FmlhXB7N9_tM,25642
+pip/_internal/metadata/importlib/__init__.py,sha256=jUUidoxnHcfITHHaAWG1G2i5fdBYklv_uJcjo2x7VYE,135
+pip/_internal/metadata/importlib/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/metadata/importlib/__pycache__/_compat.cpython-312.pyc,,
+pip/_internal/metadata/importlib/__pycache__/_dists.cpython-312.pyc,,
+pip/_internal/metadata/importlib/__pycache__/_envs.cpython-312.pyc,,
+pip/_internal/metadata/importlib/_compat.py,sha256=sneVh4_6WxQZK4ljdl3ylVuP-q0ttSqbgl9mWt0HnOg,2804
+pip/_internal/metadata/importlib/_dists.py,sha256=akX5xiLB_3dvbeb2gHL8i7wORi01_Cnty0HnKFQAF0Y,8717
+pip/_internal/metadata/importlib/_envs.py,sha256=ulCRDEXYeinwS5AXL-Quj7C4Erk20kHhIwBmE2P3BYA,5590
+pip/_internal/metadata/pkg_resources.py,sha256=NO76ZrfR2-LKJTyaXrmQoGhmJMArALvacrlZHViSDT8,10544
+pip/_internal/models/__init__.py,sha256=AjmCEBxX_MH9f_jVjIGNCFJKYCYeSEe18yyvNx4uRKQ,62
+pip/_internal/models/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/models/__pycache__/candidate.cpython-312.pyc,,
+pip/_internal/models/__pycache__/direct_url.cpython-312.pyc,,
+pip/_internal/models/__pycache__/format_control.cpython-312.pyc,,
+pip/_internal/models/__pycache__/index.cpython-312.pyc,,
+pip/_internal/models/__pycache__/installation_report.cpython-312.pyc,,
+pip/_internal/models/__pycache__/link.cpython-312.pyc,,
+pip/_internal/models/__pycache__/release_control.cpython-312.pyc,,
+pip/_internal/models/__pycache__/scheme.cpython-312.pyc,,
+pip/_internal/models/__pycache__/search_scope.cpython-312.pyc,,
+pip/_internal/models/__pycache__/selection_prefs.cpython-312.pyc,,
+pip/_internal/models/__pycache__/target_python.cpython-312.pyc,,
+pip/_internal/models/__pycache__/wheel.cpython-312.pyc,,
+pip/_internal/models/candidate.py,sha256=MvANZJiHkw6Y4T-oYSM8710G6yYB4_DwofoD7BAmrnM,824
+pip/_internal/models/direct_url.py,sha256=9RS3TQAXknwLd8JOOZWlbBV6WZFs9qn-YAYk0VQ8R_Q,944
+pip/_internal/models/format_control.py,sha256=PwemYG1L27BM0f1KP61rm24wShENFyxqlD1TWu34alc,2471
+pip/_internal/models/index.py,sha256=tYnL8oxGi4aSNWur0mG8DAP7rC6yuha_MwJO8xw0crI,1030
+pip/_internal/models/installation_report.py,sha256=U4MlXWFB-8ev_yheuMO9T2m_y5b4C-hOzVaoHVobl38,2846
+pip/_internal/models/link.py,sha256=SSXtFO3YAreUYY2R0PqtKXbAe1vV9Bqt5r_qPN218zE,23474
+pip/_internal/models/release_control.py,sha256=31Jh-ZHsTIBZLe-7uPNoYLiRSWXAjEI1jGNxwqLKd4A,3365
+pip/_internal/models/scheme.py,sha256=G-O9KElabcXqbPwfE_66lzX5fGahh3Gu6DAkz_9ZhJw,558
+pip/_internal/models/search_scope.py,sha256=_i-Gj_w_FwZAvTs7WhjslBDD70J-8hmIdjXByI8uEZQ,4461
+pip/_internal/models/selection_prefs.py,sha256=0teekwSVxW5MOk0WPG5Novw5q_XJjxijcd75kZO6E9g,1503
+pip/_internal/models/target_python.py,sha256=I0eFS-eia3kwhrOvgsphFZtNAB2IwXZ9Sr9fp6IjBP4,4243
+pip/_internal/models/wheel.py,sha256=1SdfDvN7ALTsbyZ9EOsNy1GPirP1n6EjHyzPrZyLSh8,2920
+pip/_internal/network/__init__.py,sha256=FMy06P__y6jMjUc8z3ZcQdKF-pmZ2zM14_vBeHPGhUI,49
+pip/_internal/network/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/network/__pycache__/auth.cpython-312.pyc,,
+pip/_internal/network/__pycache__/cache.cpython-312.pyc,,
+pip/_internal/network/__pycache__/download.cpython-312.pyc,,
+pip/_internal/network/__pycache__/lazy_wheel.cpython-312.pyc,,
+pip/_internal/network/__pycache__/session.cpython-312.pyc,,
+pip/_internal/network/__pycache__/utils.cpython-312.pyc,,
+pip/_internal/network/__pycache__/xmlrpc.cpython-312.pyc,,
+pip/_internal/network/auth.py,sha256=TBpwe7zcaPuBdBldjAOZhf20lmkacpIgGA-AqLcr8eU,21545
+pip/_internal/network/cache.py,sha256=6DlMXJ7pb35M1vgtTK0-L_kiHJFxf7t9zrfeJ9vqO34,4862
+pip/_internal/network/download.py,sha256=SVrmKao567lfs8RaCdyuOitzIRCZp9Rf3RcDj_o8vkg,14618
+pip/_internal/network/lazy_wheel.py,sha256=y9gVksdJCSjnLfYzs_m3DYUAtl3hc_k-xFPDBd9DgOs,7646
+pip/_internal/network/session.py,sha256=2KqMv5Hu_PFRYRkDBhSjIbqbsBp0lXv4tqoLKnM2K6o,19924
+pip/_internal/network/utils.py,sha256=OTA1vwFtu2Ee1zWrqLysBpRQGTjvXxCJJUTEqy7hNcA,8128
+pip/_internal/network/xmlrpc.py,sha256=_-Rnk3vOff8uF9hAGmT6SLALflY1gMBcbGwS12fb_Y4,1830
+pip/_internal/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_internal/operations/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/operations/__pycache__/check.cpython-312.pyc,,
+pip/_internal/operations/__pycache__/freeze.cpython-312.pyc,,
+pip/_internal/operations/__pycache__/prepare.cpython-312.pyc,,
+pip/_internal/operations/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc,,
+pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc,,
+pip/_internal/operations/build/__pycache__/metadata_editable.cpython-312.pyc,,
+pip/_internal/operations/build/__pycache__/wheel.cpython-312.pyc,,
+pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc,,
+pip/_internal/operations/build/build_tracker.py,sha256=W3b5cmkMWPaE6QIwfzsTayJo7-OlxFHWDxfPuax1KcE,4771
+pip/_internal/operations/build/metadata.py,sha256=INHaeiRfOiLYCXApfDNRo9Cw2xI4VwTc0KItvfdfOjk,1421
+pip/_internal/operations/build/metadata_editable.py,sha256=oWudMsnjy4loO_Jy7g4N9nxsnaEX_iDlVRgCy7pu1rs,1509
+pip/_internal/operations/build/wheel.py,sha256=3bP-nNiJ4S8JvMaBnyessXQUBhxTqt1GBx6DQ1iPJDY,1136
+pip/_internal/operations/build/wheel_editable.py,sha256=q3kfElclM6FutVbFwE87JOTpVWt5ixDf3_UkHAIVfz4,1478
+pip/_internal/operations/check.py,sha256=k7mTjLb5yZbidYw8Nt9vu7_u1L1ekLOzUECeN4qcNsA,5891
+pip/_internal/operations/freeze.py,sha256=PDdY-y_ZtZZJLAKcaWPIGRKAGW7DXR48f0aMRU0j7BA,9854
+pip/_internal/operations/install/__init__.py,sha256=ak-UETcQPKlFZaWoYKWu5QVXbpFBvg0sXc3i0O4vSYY,50
+pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/operations/install/__pycache__/wheel.cpython-312.pyc,,
+pip/_internal/operations/install/wheel.py,sha256=qhFwJSShLm4quY8zIss_t-bff0YV49HTNAKfu4fSpic,29078
+pip/_internal/operations/prepare.py,sha256=zLkVMSDu9cUR0KnDm3bZ3QvIeaErhJKrr109UyDMlKQ,35030
+pip/_internal/pyproject.py,sha256=J-sTWqC-XfsKQgz9m1bypMWZPHItsSHzIN_NWeIRmhM,4555
+pip/_internal/req/__init__.py,sha256=x3uB7ZW-l_MOPm8ldF8Gi0dh5C8W23dB_wOH2NmWpFc,3140
+pip/_internal/req/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/req/__pycache__/constructors.cpython-312.pyc,,
+pip/_internal/req/__pycache__/pep723.cpython-312.pyc,,
+pip/_internal/req/__pycache__/req_dependency_group.cpython-312.pyc,,
+pip/_internal/req/__pycache__/req_file.cpython-312.pyc,,
+pip/_internal/req/__pycache__/req_install.cpython-312.pyc,,
+pip/_internal/req/__pycache__/req_set.cpython-312.pyc,,
+pip/_internal/req/__pycache__/req_uninstall.cpython-312.pyc,,
+pip/_internal/req/constructors.py,sha256=fwILg3OiIK_f13RUGX5zQyz4YasUMp7MC6u9uPvoGzw,22276
+pip/_internal/req/pep723.py,sha256=JsG1p3CaVcW8cjclD2kDj7d9qtVrNQaevY_UnKh00tk,1242
+pip/_internal/req/req_dependency_group.py,sha256=PrWKtlwI8xbWnWEKjXs9RkrpQx2_h02ABefYT0ZdeQg,3145
+pip/_internal/req/req_file.py,sha256=dmGBSxZ9dj1eOq2NXv94bor0u1n8P4-AvmJL-vYzIsE,20644
+pip/_internal/req/req_install.py,sha256=r289__WNVaTxpRtLFUz3FpNvdGv8yYa87G-QkP5DsJc,32173
+pip/_internal/req/req_set.py,sha256=awkqIXnYA4Prmsj0Qb3zhqdbYUmXd-1o0P-KZ3mvRQs,2828
+pip/_internal/req/req_uninstall.py,sha256=t5HSYuLoLxnF7jrcIBDjTEP-sjjr8O4fZouEqOkVM_c,24276
+pip/_internal/resolution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_internal/resolution/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/resolution/__pycache__/base.cpython-312.pyc,,
+pip/_internal/resolution/base.py,sha256=J9Fq3kFOjFj9gBShYQtldEFPrRGSWtsp-rO9rIc4W4g,573
+pip/_internal/resolution/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_internal/resolution/legacy/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/resolution/legacy/__pycache__/resolver.cpython-312.pyc,,
+pip/_internal/resolution/legacy/resolver.py,sha256=2CVLIZLm_nXkfJTt5RKtIbIxvCkn9Gavf4Yp9c1IjDU,24110
+pip/_internal/resolution/resolvelib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/resolution/resolvelib/__pycache__/base.cpython-312.pyc,,
+pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-312.pyc,,
+pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-312.pyc,,
+pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-312.pyc,,
+pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-312.pyc,,
+pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-312.pyc,,
+pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-312.pyc,,
+pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-312.pyc,,
+pip/_internal/resolution/resolvelib/base.py,sha256=fo7ojoQtgjmQH5ca3VyKJAO3p5sHnihMDw79Z8_31-U,5900
+pip/_internal/resolution/resolvelib/candidates.py,sha256=SEkpEH6T6zl7Ve7Dw9oIS6tnREj-1U_q4U-2xEH0ISc,20848
+pip/_internal/resolution/resolvelib/factory.py,sha256=rqwnD4GWEdVgG7nGP6OGj493yiGW6jZr0cW_msMOK_U,36162
+pip/_internal/resolution/resolvelib/found_candidates.py,sha256=F55YtXekKUbVwphlXska01LkdTGVgx1qONlLK7BX-HM,6005
+pip/_internal/resolution/resolvelib/provider.py,sha256=Nkx4A16MOAxjCj9jFm8wmlMk5cbWh1YK2-UYWTKEJCw,12310
+pip/_internal/resolution/resolvelib/reporter.py,sha256=tEC7MF8IqGU4Ww9t61YVfNFKtoaQpb6-AwLZptJz1VE,3918
+pip/_internal/resolution/resolvelib/requirements.py,sha256=Izl9n8nc188lA1BSPS8QxfudfDQPHgngw-ij6hXt0nQ,8239
+pip/_internal/resolution/resolvelib/resolver.py,sha256=wM3CcRTYPdjnQ1ai3YP6htFmkE32o1xx2RgjU-ykAjc,13849
+pip/_internal/self_outdated_check.py,sha256=9XxOXPqsZlKceTP2tZS7I7rXjodnqd3GrIQ8U0_L8BM,8097
+pip/_internal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_internal/utils/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/_jaraco_text.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/_log.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/appdirs.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/compat.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/compatibility_tags.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/datetime.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/deprecation.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/direct_url_helpers.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/egg_link.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/entrypoints.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/filesystem.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/filetypes.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/glibc.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/hashes.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/logging.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/misc.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/packaging.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/pylock.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/retry.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/subprocess.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/temp_dir.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/unpacking.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/urls.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/virtualenv.cpython-312.pyc,,
+pip/_internal/utils/__pycache__/wheel.cpython-312.pyc,,
+pip/_internal/utils/_jaraco_text.py,sha256=M15uUPIh5NpP1tdUGBxRau6q1ZAEtI8-XyLEETscFfE,3350
+pip/_internal/utils/_log.py,sha256=-jHLOE_THaZz5BFcCnoSL9EYAtJ0nXem49s9of4jvKw,1015
+pip/_internal/utils/appdirs.py,sha256=LrzDPZMKVh0rubtCx9vu3XlZbLCSug6VSj4Qsvt66BA,1681
+pip/_internal/utils/compat.py,sha256=i2JUZXGrqP-ZoD8t4gwckBA3KYNUWCluvRDXScts5fE,2601
+pip/_internal/utils/compatibility_tags.py,sha256=DiNSLqpuruXUamGQwOJ2WZByDGLTGaXi9O-Xf8fOi34,6630
+pip/_internal/utils/datetime.py,sha256=kuJOf1mW8G5tRFN6jWardddS-9qSaR53lK1jmx3NTZY,868
+pip/_internal/utils/deprecation.py,sha256=IQFumZa-XWJ3UoPnX4duwbf79CTzOZD_Vk1PkivAu4g,4537
+pip/_internal/utils/direct_url_helpers.py,sha256=WCCPJnmoPHz7kiYePrEcVAen2lDpCT9WQVImoHHWCO8,3363
+pip/_internal/utils/egg_link.py,sha256=YWfsrbmfcrfWgqQYy6OuIjsyb9IfL1q_2v4zsms1WjI,2459
+pip/_internal/utils/entrypoints.py,sha256=uPjAyShKObdotjQjJUzprQ6r3xQvDIZwUYfHHqZ7Dok,3324
+pip/_internal/utils/filesystem.py,sha256=gig73bOOS1NnDB94EDDvdDnHjQoeRz_TlfQPiCV7Me0,6812
+pip/_internal/utils/filetypes.py,sha256=sEMa38qaqjvx1Zid3OCAUja31BOBU-USuSMPBvU3yjo,689
+pip/_internal/utils/glibc.py,sha256=sEh8RJJLYSdRvTqAO4THVPPA-YSDVLD4SI9So-bxX1U,3726
+pip/_internal/utils/hashes.py,sha256=38-bCOJSHippQ7r9RttrMHxb2mv3EARt1Gw8kFmW73g,5040
+pip/_internal/utils/logging.py,sha256=hGTjBuIjNZxydV6vs4woxAbhkHI8L-9uCO14OCvPlA8,13919
+pip/_internal/utils/misc.py,sha256=28LWQFvFWmrpJqMcV0soif1xnl3_gb25B3gG_iU5zhM,25408
+pip/_internal/utils/packaging.py,sha256=s5tpUmFumwV0H9JSTzryrIY4JwQM8paGt7Sm7eNwt2Y,1601
+pip/_internal/utils/pylock.py,sha256=XQTZ1ggh7yfpHfRdbvjSAe1peOtg2Z2xFrUeZ7wl4Ts,10290
+pip/_internal/utils/retry.py,sha256=oOK3VGgKRKxSkXDPARf73cnQS3hDtnE9-feAg4AlNZE,1488
+pip/_internal/utils/subprocess.py,sha256=nqNz2p3G4QANCCBcaJkqIQvQUYA50TwjZnoYjiyPxs8,9001
+pip/_internal/utils/temp_dir.py,sha256=TDiL9BzEp2eHalQLH0rhyM3m2xuydEg9c6t1h4bT5l8,9303
+pip/_internal/utils/unpacking.py,sha256=5i9Sn__SNElhoCXnLt2SvQaQ-SnTzcUaDH1_JI1THUw,14789
+pip/_internal/utils/urls.py,sha256=JQWrRAoXHBpPsY9Aij61bsZjMQLCcuyrUO1R6W_BwIo,1647
+pip/_internal/utils/virtualenv.py,sha256=9od4LsceDynUSsLTEXXgtumlUQ8VvKp86b2xzK0Yu3g,2272
+pip/_internal/utils/wheel.py,sha256=YdRuj6MicG-Q9Mg03FbUv1WTLam6Lc7AgijY4voVyis,4468
+pip/_internal/vcs/__init__.py,sha256=UAqvzpbi0VbZo3Ub6skEeZAw-ooIZR-zX_WpCbxyCoU,596
+pip/_internal/vcs/__pycache__/__init__.cpython-312.pyc,,
+pip/_internal/vcs/__pycache__/bazaar.cpython-312.pyc,,
+pip/_internal/vcs/__pycache__/git.cpython-312.pyc,,
+pip/_internal/vcs/__pycache__/mercurial.cpython-312.pyc,,
+pip/_internal/vcs/__pycache__/subversion.cpython-312.pyc,,
+pip/_internal/vcs/__pycache__/versioncontrol.cpython-312.pyc,,
+pip/_internal/vcs/bazaar.py,sha256=3W1eHjkYx2vc6boeb2NBh4I_rlGAXM-vrzfNhLm1Rxg,3734
+pip/_internal/vcs/git.py,sha256=L52rqJN--Kq0kjAR8_9Si8RJEcNlULJdk6Vipg8ZlfA,19273
+pip/_internal/vcs/mercurial.py,sha256=w1ZJWLKqNP1onEjkfjlwBVnMqPZNSIER8ayjQcnTq4w,5575
+pip/_internal/vcs/subversion.py,sha256=uUgdPvxmvEB8Qwtjr0Hc0XgFjbiNi5cbvI4vARLOJXo,11787
+pip/_internal/vcs/versioncontrol.py,sha256=JsUgTG53DkJPy5mXVBH7uqeFQyaL69707ibts5uCBYA,22579
+pip/_internal/wheel_builder.py,sha256=kdPvAcxfknA8B_4g6hPDjB7C4uzL_cV5Vde2Oj2Ipzk,9146
+pip/_vendor/README.rst,sha256=t7IinjaiuwUh812XmVApQHJb8pTw33U8A9URqy6GlF4,9222
+pip/_vendor/__init__.py,sha256=WzusPTGWIMeQQWSVJ0h2rafGkVTa9WKJ2HT-2-EoZrU,4907
+pip/_vendor/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/bom.cdx.json,sha256=eX4gCqf_8W-tn7Le6uzlCisr7GI-tsN1kXbu5s7hkyg,5319
+pip/_vendor/cachecontrol/LICENSE.txt,sha256=hu7uh74qQ_P_H1ZJb0UfaSQ5JvAl_tuwM2ZsMExMFhs,558
+pip/_vendor/cachecontrol/__init__.py,sha256=GxwRkm_TQBtPZpfpVK9r6S9dAy2DVnVgDVHJKTiPZ1k,820
+pip/_vendor/cachecontrol/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-312.pyc,,
+pip/_vendor/cachecontrol/__pycache__/adapter.cpython-312.pyc,,
+pip/_vendor/cachecontrol/__pycache__/cache.cpython-312.pyc,,
+pip/_vendor/cachecontrol/__pycache__/controller.cpython-312.pyc,,
+pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-312.pyc,,
+pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-312.pyc,,
+pip/_vendor/cachecontrol/__pycache__/serialize.cpython-312.pyc,,
+pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-312.pyc,,
+pip/_vendor/cachecontrol/_cmd.py,sha256=iist2EpzJvDVIhMAxXq8iFnTBsiZAd6iplxfmNboNyk,1737
+pip/_vendor/cachecontrol/adapter.py,sha256=W-HW-l01gyCsnxkOyCbqx7sxrWYoBbKrDsKkVVQN6NE,6586
+pip/_vendor/cachecontrol/cache.py,sha256=OXwv7Fn2AwnKNiahJHnjtvaKLndvVLv_-zO-ltlV9qI,1953
+pip/_vendor/cachecontrol/caches/__init__.py,sha256=dtrrroK5BnADR1GWjCZ19aZ0tFsMfvFBtLQQU1sp_ag,303
+pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-312.pyc,,
+pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-312.pyc,,
+pip/_vendor/cachecontrol/caches/file_cache.py,sha256=d8upFmy_zwaCmlbWEVBlLXFddt8Zw8c5SFpxeOZsdfw,4117
+pip/_vendor/cachecontrol/caches/redis_cache.py,sha256=9rmqwtYu_ljVkW6_oLqbC7EaX_a8YT_yLuna-eS0dgo,1386
+pip/_vendor/cachecontrol/controller.py,sha256=xBauC-vUSu5GsJsxD4-W-JaKqqbBz0MN6Zv8PA2N8hI,19102
+pip/_vendor/cachecontrol/filewrapper.py,sha256=DhxC_rSk-beKdbsYhfvBUDovQHX9r3gHH_jP9-q_mKk,4354
+pip/_vendor/cachecontrol/heuristics.py,sha256=gqMXU8w0gQuEQiSdu3Yg-0vd9kW7nrWKbLca75rheGE,4881
+pip/_vendor/cachecontrol/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_vendor/cachecontrol/serialize.py,sha256=HQd2IllQ05HzPkVLMXTF2uX5mjEQjDBkxCqUJUODpZk,5163
+pip/_vendor/cachecontrol/wrapper.py,sha256=hsGc7g8QGQTT-4f8tgz3AM5qwScg6FO0BSdLSRdEvpU,1417
+pip/_vendor/certifi/LICENSE,sha256=6TcW2mucDVpKHfYP5pWzcPBpVgPSH2-D8FPkLPwQyvc,989
+pip/_vendor/certifi/__init__.py,sha256=-W1R_y8WCaSkT1tdjuxH_zTBZY1YH6xQgdN1nbBajOE,94
+pip/_vendor/certifi/__main__.py,sha256=1k3Cr95vCxxGRGDljrW3wMdpZdL3Nhf0u1n-k2qdsCY,255
+pip/_vendor/certifi/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/certifi/__pycache__/__main__.cpython-312.pyc,,
+pip/_vendor/certifi/__pycache__/core.cpython-312.pyc,,
+pip/_vendor/certifi/cacert.pem,sha256=u8fpwB11UbuKFZtd7dmJuO484QWv9SK2jrGwG_hUyrA,234354
+pip/_vendor/certifi/core.py,sha256=gu_ECVI1m3Rq0ytpsNE61hgQGcKaOAt9Rs9G8KsTCOI,3442
+pip/_vendor/certifi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_vendor/distlib/LICENSE.txt,sha256=gI4QyKarjesUn_mz-xn0R6gICUYG1xKpylf-rTVSWZ0,14531
+pip/_vendor/distlib/__init__.py,sha256=tmYThB1xmgfRjRtyy8YP0i0ymrF2n6eVswcwK6OYbcc,625
+pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc,,
+pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc,,
+pip/_vendor/distlib/__pycache__/scripts.cpython-312.pyc,,
+pip/_vendor/distlib/__pycache__/util.cpython-312.pyc,,
+pip/_vendor/distlib/compat.py,sha256=HTzhOY98c_fyMVE7xtVuZZ-AVkN4-RGes-hWmvuwquk,40605
+pip/_vendor/distlib/resources.py,sha256=wZt_Ako8TY-L6ZIAmP42J7MNX0XTEF6gkM0Om5F7UPY,11350
+pip/_vendor/distlib/scripts.py,sha256=rVm4A-YfGQDpsORwOy7AwepyRTayuy6rc-MnaCMBAcM,18835
+pip/_vendor/distlib/t32.exe,sha256=a0GV5kCoWsMutvliiCKmIgV98eRZ33wXoS-XrqvJQVs,97792
+pip/_vendor/distlib/t64-arm.exe,sha256=68TAa32V504xVBnufojh0PcenpR3U4wAqTqf-MZqbPw,182784
+pip/_vendor/distlib/t64.exe,sha256=gaYY8hy4fbkHYTTnA4i26ct8IQZzkBG2pRdy0iyuBrc,108032
+pip/_vendor/distlib/util.py,sha256=n2QDXvB0XtwrtehygnwdLgLkLia58sZjzgZPKyD6rAE,68167
+pip/_vendor/distlib/w32.exe,sha256=R4csx3-OGM9kL4aPIzQKRo5TfmRSHZo6QWyLhDhNBks,91648
+pip/_vendor/distlib/w64-arm.exe,sha256=xdyYhKj0WDcVUOCb05blQYvzdYIKMbmJn2SZvzkcey4,168448
+pip/_vendor/distlib/w64.exe,sha256=ejGf-rojoBfXseGLpya6bFTFPWRG21X5KvU8J5iU-K0,101888
+pip/_vendor/distro/LICENSE,sha256=y16Ofl9KOYjhBjwULGDcLfdWBfTEZRXnduOspt-XbhQ,11325
+pip/_vendor/distro/__init__.py,sha256=2fHjF-SfgPvjyNZ1iHh_wjqWdR_Yo5ODHwZC0jLBPhc,981
+pip/_vendor/distro/__main__.py,sha256=bu9d3TifoKciZFcqRBuygV3GSuThnVD_m2IK4cz96Vs,64
+pip/_vendor/distro/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/distro/__pycache__/__main__.cpython-312.pyc,,
+pip/_vendor/distro/__pycache__/distro.cpython-312.pyc,,
+pip/_vendor/distro/distro.py,sha256=XqbefacAhDT4zr_trnbA15eY8vdK4GTghgmvUGrEM_4,49430
+pip/_vendor/distro/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_vendor/idna/LICENSE.md,sha256=GppPDj1HmickDd1ZqRN6ZqtKD539yMphiMwL_YUYfwQ,1541
+pip/_vendor/idna/__init__.py,sha256=MPqNDLZbXqGaNdXxAFhiqFPKEQXju2jNQhCey6-5eJM,868
+pip/_vendor/idna/__main__.py,sha256=4JMK66Wj4uLZTKbF-sT3LAxOsr6buig77PmOkJCRRxw,83
+pip/_vendor/idna/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/idna/__pycache__/__main__.cpython-312.pyc,,
+pip/_vendor/idna/__pycache__/cli.cpython-312.pyc,,
+pip/_vendor/idna/__pycache__/codec.cpython-312.pyc,,
+pip/_vendor/idna/__pycache__/compat.cpython-312.pyc,,
+pip/_vendor/idna/__pycache__/core.cpython-312.pyc,,
+pip/_vendor/idna/__pycache__/idnadata.cpython-312.pyc,,
+pip/_vendor/idna/__pycache__/intranges.cpython-312.pyc,,
+pip/_vendor/idna/__pycache__/package_data.cpython-312.pyc,,
+pip/_vendor/idna/__pycache__/uts46data.cpython-312.pyc,,
+pip/_vendor/idna/cli.py,sha256=swqJLMNc8Uzs60KziNpbWnHuqlG3WRQwJSbo4n8xDAo,4139
+pip/_vendor/idna/codec.py,sha256=JRbo-f7pEkLdWeiH89Z72UR4VBYhvKDFrQBeNX6sRDE,5040
+pip/_vendor/idna/compat.py,sha256=AepA39ceRHxkfHP41-FvKW5Ki-f4PfUZ90RUMlCNdmo,1353
+pip/_vendor/idna/core.py,sha256=SfOr1xO3PoE0RDYx7bMciAnjiyjJPbPw_93AB5IUYOw,24685
+pip/_vendor/idna/idnadata.py,sha256=Af-mo8WBmkhAK6TyXKOQH88OX0mQNDKtdL7UWtQpppk,44862
+pip/_vendor/idna/intranges.py,sha256=g49scLSkqJtAhLmOODa7hVHriSjmb60tiTsEoocJdBI,1851
+pip/_vendor/idna/package_data.py,sha256=TeI94EqAFAFaXfBJwsOPUMLn2969uirPa-DaeaceAyU,21
+pip/_vendor/idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_vendor/idna/uts46data.py,sha256=jujNz5QqWMcJf-XYLv4X1jBvb5FlI0t6-e1mILsgbPk,234325
+pip/_vendor/msgpack/COPYING,sha256=SS3tuoXaWHL3jmCRvNH-pHTWYNNay03ulkuKqz8AdCc,614
+pip/_vendor/msgpack/__init__.py,sha256=RA8gcqK17YpkxBnNwXJVa1oa2LygWDgfF1nA1NPw3mo,1109
+pip/_vendor/msgpack/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/msgpack/__pycache__/exceptions.cpython-312.pyc,,
+pip/_vendor/msgpack/__pycache__/ext.cpython-312.pyc,,
+pip/_vendor/msgpack/__pycache__/fallback.cpython-312.pyc,,
+pip/_vendor/msgpack/exceptions.py,sha256=dCTWei8dpkrMsQDcjQk74ATl9HsIBH0ybt8zOPNqMYc,1081
+pip/_vendor/msgpack/ext.py,sha256=kteJv03n9tYzd5oo3xYopVTo4vRaAxonBQQJhXohZZo,5726
+pip/_vendor/msgpack/fallback.py,sha256=0g1Pzp0vtmBEmJ5w9F3s_-JMVURP8RS4G1cc5TRaAsI,32390
+pip/_vendor/packaging/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197
+pip/_vendor/packaging/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
+pip/_vendor/packaging/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344
+pip/_vendor/packaging/__init__.py,sha256=QhMEdPu2XogrJzV3S0KWS6t7l0I9k8EeDRJl4fnw87s,494
+pip/_vendor/packaging/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/_parser.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/_structures.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/dependency_groups.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/direct_url.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/errors.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/markers.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/metadata.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/pylock.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/requirements.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/tags.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/utils.cpython-312.pyc,,
+pip/_vendor/packaging/__pycache__/version.cpython-312.pyc,,
+pip/_vendor/packaging/_elffile.py,sha256=-sKkptYqzYw2-x3QByJa5mB4rfPWu1pxkZHRx1WAFCY,3211
+pip/_vendor/packaging/_manylinux.py,sha256=Hf6nB0cOrayEs96-p3oIXAgGnFquv20DO5l-o2_Xnv0,9559
+pip/_vendor/packaging/_musllinux.py,sha256=Z6swjH3MA7XS3qXnmMN7QPhqP3fnoYI0eQ18e9-HgAE,2707
+pip/_vendor/packaging/_parser.py,sha256=Kf2nsDw4c54X82pY8ba4F02Bve6OygGMAjL-Begqcew,11698
+pip/_vendor/packaging/_structures.py,sha256=60jRbF78p8z5MKnNd6cAprgOadCJHV0DlmUmRBqFZcs,1109
+pip/_vendor/packaging/_tokenizer.py,sha256=tFU2Wr-ZZJdAbkXLEJo7qUQDJaIkfft9DqaifiEND7A,5391
+pip/_vendor/packaging/dependency_groups.py,sha256=XZIAVFK9uHG4RCGprmJn3VInUWMesxha_kytJuMO9eY,10218
+pip/_vendor/packaging/direct_url.py,sha256=eKmbDiPP1sLV4Mj_kCSZqqknrIyVO9Sr7JpF8KCjp4U,10917
+pip/_vendor/packaging/errors.py,sha256=6hfEYXAf8v_IF65-lFadJOMIieBP2xIKtyEXjG1nGIs,2680
+pip/_vendor/packaging/licenses/__init__.py,sha256=_Jx0XRiD_58palsWnyLrLuh59ZpGCPIPXLKdZo9OJvQ,7293
+pip/_vendor/packaging/licenses/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/packaging/licenses/__pycache__/_spdx.cpython-312.pyc,,
+pip/_vendor/packaging/licenses/_spdx.py,sha256=WW7DXiyg68up_YND_wpRYlr1SHhiV4FfJLQffghhMxQ,51122
+pip/_vendor/packaging/markers.py,sha256=QixBVcb9D2HjwEYiuhpNkbqk9znPRbU8zNX0kR1RIrU,17067
+pip/_vendor/packaging/metadata.py,sha256=crAh0E3GVGVqPlu6EdRFsaG-Y6UYznTUqjuGKRGPv6c,38770
+pip/_vendor/packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_vendor/packaging/pylock.py,sha256=G_1gncTmDbRLY1jo4VDI9Uw-b5IErh_Q9V_BbVJTmD8,33890
+pip/_vendor/packaging/requirements.py,sha256=Q-BdEHVW5K785GBXt7RcP4UEsdIKWoWFqlrHjR4WV50,4395
+pip/_vendor/packaging/specifiers.py,sha256=3XBcSslm-YQEEEO_zrv4F6dR5wRxs4ErJvJ0vjNBfGM,71550
+pip/_vendor/packaging/tags.py,sha256=ANYHZxYQVp9BlOQYOHU5ArDNVIpTFl4KlbocYVBWnFs,34236
+pip/_vendor/packaging/utils.py,sha256=M7-JMKic2sP1YtV_8aW7eVGB-x3ADuKCiSrsVeCd2Uo,9848
+pip/_vendor/packaging/version.py,sha256=Mcu7Tf6Y1i0gQ4FXv4t0g8cbL5joAEshjgIpz_2vISI,38393
+pip/_vendor/pkg_resources/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+pip/_vendor/pkg_resources/__init__.py,sha256=vbTJ0_ruUgGxQjlEqsruFmiNPVyh2t9q-zyTDT053xI,124451
+pip/_vendor/pkg_resources/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/platformdirs/LICENSE,sha256=KeD9YukphQ6G6yjD_czwzv30-pSHkBHP-z0NS-1tTbY,1089
+pip/_vendor/platformdirs/__init__.py,sha256=gBmL_J_KdPgKiwM3dHCo-y-8CbS06QCa1XIhJ9bIgew,32389
+pip/_vendor/platformdirs/__main__.py,sha256=DQ1o1E1l0Ghuitj4uQJKAPwkcKbGzkzO0sbhzd9RbUc,1773
+pip/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc,,
+pip/_vendor/platformdirs/__pycache__/_xdg.cpython-312.pyc,,
+pip/_vendor/platformdirs/__pycache__/android.cpython-312.pyc,,
+pip/_vendor/platformdirs/__pycache__/api.cpython-312.pyc,,
+pip/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc,,
+pip/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc,,
+pip/_vendor/platformdirs/__pycache__/version.cpython-312.pyc,,
+pip/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc,,
+pip/_vendor/platformdirs/_xdg.py,sha256=5w6S07vVabEROhaUB_X7i0hmLNbdYtC9aEvPbsEIS8M,7894
+pip/_vendor/platformdirs/android.py,sha256=BBj3wfw-dfuVxsCsKsvFByKVspRDgfv8gwIzhpE1JRQ,11566
+pip/_vendor/platformdirs/api.py,sha256=EaIsFgXGu8cDTb41WM3hse_SrIoOUht8_TCo5XdOOSM,14887
+pip/_vendor/platformdirs/macos.py,sha256=PWudeDZAY9UuwLP_5rN3exSRFtYdY542hDhjah4dSWA,9538
+pip/_vendor/platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_vendor/platformdirs/unix.py,sha256=X7Cip1xOHFOnSVveqKWzU1P7jn4zuDGpRgmVxJnk4vk,13317
+pip/_vendor/platformdirs/version.py,sha256=dJvdm7L_7wobEcxrNoJtH7CtmYcG_VR5yNmtBshJ-xs,522
+pip/_vendor/platformdirs/windows.py,sha256=YOdSGNhdpxm_ydZq7uG74i9mX2_WWbcMSA4fobXwL0M,15798
+pip/_vendor/pygments/LICENSE,sha256=qdZvHVJt8C4p3Oc0NtNOVuhjL0bCdbvf_HBWnogvnxc,1331
+pip/_vendor/pygments/__init__.py,sha256=Xnd_7fJUqU_6D0bxXW0qS9M_OE9G6VW8YNPbGLWoKwQ,2986
+pip/_vendor/pygments/__main__.py,sha256=IqAQ_836Kv2CTx8r6Ie6duRQuYSPl85sbYomEwIsY14,356
+pip/_vendor/pygments/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/__main__.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/console.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/filter.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/formatter.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/lexer.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/modeline.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/plugin.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/regexopt.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/scanner.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/sphinxext.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/style.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/token.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/unistring.cpython-312.pyc,,
+pip/_vendor/pygments/__pycache__/util.cpython-312.pyc,,
+pip/_vendor/pygments/console.py,sha256=C189JAwhC1Qh0AKgshzb1wVDzFqBnv1VZ45kTEDIO30,1721
+pip/_vendor/pygments/filter.py,sha256=1dnbkq2AdC3AkHt3DaXwnOkTBLChl1kR6naSLwnr_tc,1913
+pip/_vendor/pygments/filters/__init__.py,sha256=Prd6xUK_7kw0rQv_OXPY3871RzwtWKGp_g7SddYHBXg,40397
+pip/_vendor/pygments/filters/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/pygments/formatter.py,sha256=EuAXPX6c3XQkCt3lD0VhSPSZByG7YQk8HrCwxlf-CZQ,4393
+pip/_vendor/pygments/formatters/__init__.py,sha256=EYLDCIgBHL_DkuhNRQZlHpS2_UE5-MfsGL1hMcOa95w,5388
+pip/_vendor/pygments/formatters/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-312.pyc,,
+pip/_vendor/pygments/formatters/_mapping.py,sha256=1Cw37FuQlNacnxRKmtlPX4nyLoX9_ttko5ZwscNUZZ4,4176
+pip/_vendor/pygments/lexer.py,sha256=wSXDzt2fNZJSrlfdIfnKVlheRpFkszkPmgwBJevbBNo,35394
+pip/_vendor/pygments/lexers/__init__.py,sha256=s8I_tVlotrM8cFTGlnjw45UY5sj5jhDwdQzOOjhzgNg,12118
+pip/_vendor/pygments/lexers/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/pygments/lexers/__pycache__/_mapping.cpython-312.pyc,,
+pip/_vendor/pygments/lexers/__pycache__/python.cpython-312.pyc,,
+pip/_vendor/pygments/lexers/_mapping.py,sha256=wGAYjOBprDVZA01eocwcLJ6L8biM9rP2jxwr1bQJ96M,77934
+pip/_vendor/pygments/lexers/python.py,sha256=TqyfQOPhCYPx35apQL65CAj9k-4BxuzLq8MDfdm2jvE,54250
+pip/_vendor/pygments/modeline.py,sha256=me8g5rySidvPtMBOrDy2O1sMqZd02lBHMY5KDZefUws,1008
+pip/_vendor/pygments/plugin.py,sha256=P6zIw-vkSQ0k1WKHrzbBTjpwlCZPbR-6Qe_dO9Bjdu0,1928
+pip/_vendor/pygments/regexopt.py,sha256=d2hTvazlow5zzZIOCVnfeEG2CY0GrY_igH1kCSSf7ow,3308
+pip/_vendor/pygments/scanner.py,sha256=DtoLi1pOKpNu-6jiakJpSQLUi_ep29SQQhv7oAwYWME,3095
+pip/_vendor/pygments/sphinxext.py,sha256=RflpnfXB4rZLYyZ-OQ_igntVOlB8jG42xFYgEVIyHf8,7984
+pip/_vendor/pygments/style.py,sha256=wJSXOXjehGYxI6e3DGaUtONeWuhmTfswfkysrL9Was0,6423
+pip/_vendor/pygments/styles/__init__.py,sha256=kYdCGK8gZ1V8wfFQETOzpPmZTJ1nMB0b1iNlU8dHQ9E,2045
+pip/_vendor/pygments/styles/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/pygments/styles/__pycache__/_mapping.cpython-312.pyc,,
+pip/_vendor/pygments/styles/_mapping.py,sha256=6lovFUE29tz6EsV3XYY4hgozJ7q1JL7cfO3UOlgnS8w,3312
+pip/_vendor/pygments/token.py,sha256=DVil5T2ltHkTgQTUYy1dMtgyigjavCs1ypE4Qf2CrGo,6229
+pip/_vendor/pygments/unistring.py,sha256=Z4w4HfOVUhueCURRkfhAqQ2b-69UzkhY4xuHevYEy5g,63211
+pip/_vendor/pygments/util.py,sha256=zk935tJSpwSA9zxNmV1TuhcEP0MfboXiRZSGQnLPEqk,10046
+pip/_vendor/pyproject_hooks/LICENSE,sha256=GyKwSbUmfW38I6Z79KhNjsBLn9-xpR02DkK0NCyLQVQ,1081
+pip/_vendor/pyproject_hooks/__init__.py,sha256=cPB_a9LXz5xvsRbX1o2qyAdjLatZJdQ_Lc5McNX-X7Y,691
+pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-312.pyc,,
+pip/_vendor/pyproject_hooks/_impl.py,sha256=jY-raxnmyRyB57ruAitrJRUzEexuAhGTpgMygqx67Z4,14936
+pip/_vendor/pyproject_hooks/_in_process/__init__.py,sha256=MJNPpfIxcO-FghxpBbxkG1rFiQf6HOUbV4U5mq0HFns,557
+pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-312.pyc,,
+pip/_vendor/pyproject_hooks/_in_process/_in_process.py,sha256=qcXMhmx__MIJq10gGHW3mA4Tl8dy8YzHMccwnNoKlw0,12216
+pip/_vendor/pyproject_hooks/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_vendor/requests/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
+pip/_vendor/requests/__init__.py,sha256=nC2attFwdoYN-oE6-vQW4ciuvIu7J2P_TU1qQ2DcueE,5873
+pip/_vendor/requests/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/__version__.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/_internal_utils.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/_types.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/adapters.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/api.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/auth.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/certs.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/compat.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/cookies.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/exceptions.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/help.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/hooks.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/models.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/packages.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/sessions.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/status_codes.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/structures.cpython-312.pyc,,
+pip/_vendor/requests/__pycache__/utils.cpython-312.pyc,,
+pip/_vendor/requests/__version__.py,sha256=q7qQZwsDcKPcQNl8LepGo11L7JQf8Q18PPMJjvBKF_k,435
+pip/_vendor/requests/_internal_utils.py,sha256=TH2NEyyYmPx9cV5HPzrHR4XdxKuW0skkD4eDXcbZgf8,1542
+pip/_vendor/requests/_types.py,sha256=0G33n1SiecDMP67FlsRGJG1a0U7f1SnrMo3vvWKkSok,5838
+pip/_vendor/requests/adapters.py,sha256=5kSKhXlJuR_enrOjd3khSGpRBj1WqCh08Qssu0CEZ8M,28208
+pip/_vendor/requests/api.py,sha256=TRVICsBG8Ikgl5joZQR270oo6-b4G0AHWPjvQuxrVQk,7152
+pip/_vendor/requests/auth.py,sha256=6TlRpVLUw8X9m4Sl4dSbA8qrpHI8T_ES2mBTYA7IWmU,12233
+pip/_vendor/requests/certs.py,sha256=eD1G0RoMZ3kA0lydkw7oo0lmcKqvD1hhz6yOlgSKm8w,442
+pip/_vendor/requests/compat.py,sha256=1UheFGomceWe7aysgboYWxcEOH_HpQFmKhIyPiAanog,2035
+pip/_vendor/requests/cookies.py,sha256=ChzmFA8rlCBSLFCA41bmifp5bCUi9VyhCCb17Y4Kcjw,21549
+pip/_vendor/requests/exceptions.py,sha256=Am6y4ncoBptpmesOjRxc5iiuBDYjx3UN1oRVBlNgRyE,4576
+pip/_vendor/requests/help.py,sha256=TVWejkCROwKexHHB44KCZSr-c0o5XakNL7LT1UJlRy8,4114
+pip/_vendor/requests/hooks.py,sha256=69igJHXTGg5HOo9VPpUB_0NkW5VjiFrVKETnpj8Ndqs,1138
+pip/_vendor/requests/models.py,sha256=99d7WF4pR35NHo9jrbGiBocZ3LCMsloVU0DAcQ49x3Q,41848
+pip/_vendor/requests/packages.py,sha256=_ZQDCJTJ8SP3kVWunSqBsRZNPzj2c1WFVqbdr08pz3U,1057
+pip/_vendor/requests/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_vendor/requests/sessions.py,sha256=lvuzC7vwalmlJo0TtXiFFJdWqj8xaVtcFeQd17svZ6Y,34248
+pip/_vendor/requests/status_codes.py,sha256=GVD0fInPGAGXh-B9jOSPZtazjmIvfZTXCpAkf-5sBA4,4351
+pip/_vendor/requests/structures.py,sha256=upRgw5B48l5vHSokrJQaxvjS7pcZf6jI0MJi2KHmegI,4134
+pip/_vendor/requests/utils.py,sha256=u7MICyzQTvfUa3asg9nBvcrnhSIPEc6qhJSxiGGwTOk,36334
+pip/_vendor/resolvelib/LICENSE,sha256=84j9OMrRMRLB3A9mm76A5_hFQe26-3LzAw0sp2QsPJ0,751
+pip/_vendor/resolvelib/__init__.py,sha256=yoX-d4STvwGGCiQRE5cJC9Cter69SgVgqClxOCvSP7M,541
+pip/_vendor/resolvelib/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/resolvelib/__pycache__/providers.cpython-312.pyc,,
+pip/_vendor/resolvelib/__pycache__/reporters.cpython-312.pyc,,
+pip/_vendor/resolvelib/__pycache__/structs.cpython-312.pyc,,
+pip/_vendor/resolvelib/providers.py,sha256=pIWJbIdJJ9GFtNbtwTH0Ia43Vj6hYCEJj2DOLue15FM,8914
+pip/_vendor/resolvelib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_vendor/resolvelib/reporters.py,sha256=pNJf4nFxLpAeKxlBUi2GEj0a2Ij1nikY0UabTKXesT4,2037
+pip/_vendor/resolvelib/resolvers/__init__.py,sha256=728M3EvmnPbVXS7ExXlv2kMu6b7wEsoPutEfl-uVk_I,640
+pip/_vendor/resolvelib/resolvers/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/resolvelib/resolvers/__pycache__/abstract.cpython-312.pyc,,
+pip/_vendor/resolvelib/resolvers/__pycache__/criterion.cpython-312.pyc,,
+pip/_vendor/resolvelib/resolvers/__pycache__/exceptions.cpython-312.pyc,,
+pip/_vendor/resolvelib/resolvers/__pycache__/resolution.cpython-312.pyc,,
+pip/_vendor/resolvelib/resolvers/abstract.py,sha256=CNeQPnpAudY77nmzOkONSmAgRlzIf06X-X9mvRYODms,1543
+pip/_vendor/resolvelib/resolvers/criterion.py,sha256=lcmZGv5sKHOnFD_RzZwvlGSj19MeA-5rCMpdf2Sgw7Y,1768
+pip/_vendor/resolvelib/resolvers/exceptions.py,sha256=ln_jaQtgLlRUSFY627yiHG2gD7AgaXzRKaElFVh7fDQ,1768
+pip/_vendor/resolvelib/resolvers/resolution.py,sha256=3J_zkW-sD3EY-BlNXjyln__njpyH5n0UZJT6uV7CheA,24212
+pip/_vendor/resolvelib/structs.py,sha256=pu-EJiR2IBITr2SQeNPRa0rXhjlStfmO_GEgAhr3004,6420
+pip/_vendor/rich/LICENSE,sha256=3u18F6QxgVgZCj6iOcyHmlpQJxzruYrnAl9I--WNyhU,1056
+pip/_vendor/rich/__init__.py,sha256=dRxjIL-SbFVY0q3IjSMrfgBTHrm1LZDgLOygVBwiYZc,6090
+pip/_vendor/rich/__main__.py,sha256=e_aVC-tDzarWQW9SuZMuCgBr6ODV_iDNV2Wh2xkxOlw,7896
+pip/_vendor/rich/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/__main__.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_cell_widths.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_emoji_codes.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_emoji_replace.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_export_format.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_extension.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_fileno.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_inspect.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_log_render.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_loop.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_null_file.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_palettes.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_pick.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_ratio.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_spinners.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_stack.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_timer.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_win32_console.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_windows.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_windows_renderer.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/_wrap.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/abc.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/align.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/ansi.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/bar.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/box.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/cells.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/color.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/color_triplet.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/columns.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/console.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/constrain.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/containers.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/control.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/default_styles.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/diagnose.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/emoji.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/errors.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/file_proxy.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/filesize.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/highlighter.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/json.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/jupyter.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/layout.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/live.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/live_render.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/logging.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/markup.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/measure.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/padding.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/pager.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/palette.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/panel.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/pretty.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/progress.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/progress_bar.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/prompt.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/protocol.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/region.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/repr.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/rule.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/scope.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/screen.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/segment.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/spinner.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/status.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/style.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/styled.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/syntax.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/table.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/terminal_theme.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/text.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/theme.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/themes.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/traceback.cpython-312.pyc,,
+pip/_vendor/rich/__pycache__/tree.cpython-312.pyc,,
+pip/_vendor/rich/_cell_widths.py,sha256=fbmeyetEdHjzE_Vx2l1uK7tnPOhMs2X1lJfO3vsKDpA,10209
+pip/_vendor/rich/_emoji_codes.py,sha256=hu1VL9nbVdppJrVoijVshRlcRRe_v3dju3Mmd2sKZdY,140235
+pip/_vendor/rich/_emoji_replace.py,sha256=n-kcetsEUx2ZUmhQrfeMNc-teeGhpuSQ5F8VPBsyvDo,1064
+pip/_vendor/rich/_export_format.py,sha256=RI08pSrm5tBSzPMvnbTqbD9WIalaOoN5d4M1RTmLq1Y,2128
+pip/_vendor/rich/_extension.py,sha256=Xt47QacCKwYruzjDi-gOBq724JReDj9Cm9xUi5fr-34,265
+pip/_vendor/rich/_fileno.py,sha256=HWZxP5C2ajMbHryvAQZseflVfQoGzsKOHzKGsLD8ynQ,799
+pip/_vendor/rich/_inspect.py,sha256=ROT0PLC2GMWialWZkqJIjmYq7INRijQQkoSokWTaAiI,9656
+pip/_vendor/rich/_log_render.py,sha256=1ByI0PA1ZpxZY3CGJOK54hjlq4X-Bz_boIjIqCd8Kns,3225
+pip/_vendor/rich/_loop.py,sha256=hV_6CLdoPm0va22Wpw4zKqM0RYsz3TZxXj0PoS-9eDQ,1236
+pip/_vendor/rich/_null_file.py,sha256=ADGKp1yt-k70FMKV6tnqCqecB-rSJzp-WQsD7LPL-kg,1394
+pip/_vendor/rich/_palettes.py,sha256=cdev1JQKZ0JvlguV9ipHgznTdnvlIzUFDBb0It2PzjI,7063
+pip/_vendor/rich/_pick.py,sha256=evDt8QN4lF5CiwrUIXlOJCntitBCOsI3ZLPEIAVRLJU,423
+pip/_vendor/rich/_ratio.py,sha256=IOtl78sQCYZsmHyxhe45krkb68u9xVz7zFsXVJD-b2Y,5325
+pip/_vendor/rich/_spinners.py,sha256=U2r1_g_1zSjsjiUdAESc2iAMc3i4ri_S8PYP6kQ5z1I,19919
+pip/_vendor/rich/_stack.py,sha256=-C8OK7rxn3sIUdVwxZBBpeHhIzX0eI-VM3MemYfaXm0,351
+pip/_vendor/rich/_timer.py,sha256=zelxbT6oPFZnNrwWPpc1ktUeAT-Vc4fuFcRZLQGLtMI,417
+pip/_vendor/rich/_win32_console.py,sha256=BSaDRIMwBLITn_m0mTRLPqME5q-quGdSMuYMpYeYJwc,22755
+pip/_vendor/rich/_windows.py,sha256=aBwaD_S56SbgopIvayVmpk0Y28uwY2C5Bab1wl3Bp-I,1925
+pip/_vendor/rich/_windows_renderer.py,sha256=t74ZL3xuDCP3nmTp9pH1L5LiI2cakJuQRQleHCJerlk,2783
+pip/_vendor/rich/_wrap.py,sha256=FlSsom5EX0LVkA3KWy34yHnCfLtqX-ZIepXKh-70rpc,3404
+pip/_vendor/rich/abc.py,sha256=ON-E-ZqSSheZ88VrKX2M3PXpFbGEUUZPMa_Af0l-4f0,890
+pip/_vendor/rich/align.py,sha256=dg-7uY0ukMLLlUEsBDRLva22_sQgIJD4BK0dmZHFHug,10324
+pip/_vendor/rich/ansi.py,sha256=Avs1LHbSdcyOvDOdpELZUoULcBiYewY76eNBp6uFBhs,6921
+pip/_vendor/rich/bar.py,sha256=ldbVHOzKJOnflVNuv1xS7g6dLX2E3wMnXkdPbpzJTcs,3263
+pip/_vendor/rich/box.py,sha256=kmavBc_dn73L_g_8vxWSwYJD2uzBXOUFTtJOfpbczcM,10686
+pip/_vendor/rich/cells.py,sha256=KrQkj5-LghCCpJLSNQIyAZjndc4bnEqOEmi5YuZ9UCY,5130
+pip/_vendor/rich/color.py,sha256=3HSULVDj7qQkXUdFWv78JOiSZzfy5y1nkcYhna296V0,18211
+pip/_vendor/rich/color_triplet.py,sha256=3lhQkdJbvWPoLDO-AnYImAWmJvV5dlgYNCVZ97ORaN4,1054
+pip/_vendor/rich/columns.py,sha256=HUX0KcMm9dsKNi11fTbiM_h2iDtl8ySCaVcxlalEzq8,7131
+pip/_vendor/rich/console.py,sha256=t9azZpmRMVU5cphVBZSShNsmBxd2-IAWcTTlhor-E1s,100849
+pip/_vendor/rich/constrain.py,sha256=1VIPuC8AgtKWrcncQrjBdYqA3JVWysu6jZo1rrh7c7Q,1288
+pip/_vendor/rich/containers.py,sha256=c_56TxcedGYqDepHBMTuZdUIijitAQgnox-Qde0Z1qo,5502
+pip/_vendor/rich/control.py,sha256=EUTSUFLQbxY6Zmo_sdM-5Ls323vIHTBfN8TPulqeHUY,6487
+pip/_vendor/rich/default_styles.py,sha256=khQFqqaoDs3bprMqWpHw8nO5UpG2DN6QtuTd6LzZwYc,8257
+pip/_vendor/rich/diagnose.py,sha256=fJl1TItRn19gGwouqTg-8zPUW3YqQBqGltrfPQs1H9w,1025
+pip/_vendor/rich/emoji.py,sha256=Wd4bQubZdSy6-PyrRQNuMHtn2VkljK9uPZPVlu2cmx0,2367
+pip/_vendor/rich/errors.py,sha256=5pP3Kc5d4QJ_c0KFsxrfyhjiPVe7J1zOqSFbFAzcV-Y,642
+pip/_vendor/rich/file_proxy.py,sha256=Tl9THMDZ-Pk5Wm8sI1gGg_U5DhusmxD-FZ0fUbcU0W0,1683
+pip/_vendor/rich/filesize.py,sha256=_iz9lIpRgvW7MNSeCZnLg-HwzbP4GETg543WqD8SFs0,2484
+pip/_vendor/rich/highlighter.py,sha256=G_sn-8DKjM1sEjLG_oc4ovkWmiUpWvj8bXi0yed2LnY,9586
+pip/_vendor/rich/json.py,sha256=vVEoKdawoJRjAFayPwXkMBPLy7RSTs-f44wSQDR2nJ0,5031
+pip/_vendor/rich/jupyter.py,sha256=QyoKoE_8IdCbrtiSHp9TsTSNyTHY0FO5whE7jOTd9UE,3252
+pip/_vendor/rich/layout.py,sha256=ajkSFAtEVv9EFTcFs-w4uZfft7nEXhNzL7ZVdgrT5rI,14004
+pip/_vendor/rich/live.py,sha256=tF3ukAAJZ_N2ZbGclqZ-iwLoIoZ8f0HHUz79jAyJqj8,15180
+pip/_vendor/rich/live_render.py,sha256=It_39YdzrBm8o3LL0kaGorPFg-BfZWAcrBjLjFokbx4,3521
+pip/_vendor/rich/logging.py,sha256=5KaPPSMP9FxcXPBcKM4cGd_zW78PMgf-YbMVnvfSw0o,12468
+pip/_vendor/rich/markup.py,sha256=3euGKP5s41NCQwaSjTnJxus5iZMHjxpIM0W6fCxra38,8451
+pip/_vendor/rich/measure.py,sha256=HmrIJX8sWRTHbgh8MxEay_83VkqNW_70s8aKP5ZcYI8,5305
+pip/_vendor/rich/padding.py,sha256=KVEI3tOwo9sgK1YNSuH__M1_jUWmLZwRVV_KmOtVzyM,4908
+pip/_vendor/rich/pager.py,sha256=SO_ETBFKbg3n_AgOzXm41Sv36YxXAyI3_R-KOY2_uSc,828
+pip/_vendor/rich/palette.py,sha256=lInvR1ODDT2f3UZMfL1grq7dY_pDdKHw4bdUgOGaM4Y,3396
+pip/_vendor/rich/panel.py,sha256=9sQl00hPIqH5G2gALQo4NepFwpP0k9wT-s_gOms5pIc,11157
+pip/_vendor/rich/pretty.py,sha256=gy3S72u4FRg2ytoo7N1ZDWDIvB4unbzd5iUGdgm-8fc,36391
+pip/_vendor/rich/progress.py,sha256=CUc2lkU-X59mVdGfjMCBkZeiGPL3uxdONjhNJF2T7wY,60408
+pip/_vendor/rich/progress_bar.py,sha256=mZTPpJUwcfcdgQCTTz3kyY-fc79ddLwtx6Ghhxfo064,8162
+pip/_vendor/rich/prompt.py,sha256=l0RhQU-0UVTV9e08xW1BbIj0Jq2IXyChX4lC0lFNzt4,12447
+pip/_vendor/rich/protocol.py,sha256=5hHHDDNHckdk8iWH5zEbi-zuIVSF5hbU2jIo47R7lTE,1391
+pip/_vendor/rich/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_vendor/rich/region.py,sha256=rNT9xZrVZTYIXZC0NYn41CJQwYNbR-KecPOxTgQvB8Y,166
+pip/_vendor/rich/repr.py,sha256=5MZJZmONgC6kud-QW-_m1okXwL2aR6u6y-pUcUCJz28,4431
+pip/_vendor/rich/rule.py,sha256=0fNaS_aERa3UMRc3T5WMpN_sumtDxfaor2y3of1ftBk,4602
+pip/_vendor/rich/scope.py,sha256=TMUU8qo17thyqQCPqjDLYpg_UU1k5qVd-WwiJvnJVas,2843
+pip/_vendor/rich/screen.py,sha256=YoeReESUhx74grqb0mSSb9lghhysWmFHYhsbMVQjXO8,1591
+pip/_vendor/rich/segment.py,sha256=otnKeKGEV-WRlQVosfJVeFDcDxAKHpvJ_hLzSu5lumM,24743
+pip/_vendor/rich/spinner.py,sha256=onIhpKlljRHppTZasxO8kXgtYyCHUkpSgKglRJ3o51g,4214
+pip/_vendor/rich/status.py,sha256=kkPph3YeAZBo-X-4wPp8gTqZyU466NLwZBA4PZTTewo,4424
+pip/_vendor/rich/style.py,sha256=W9Ccy8Py8lNICtlfcp-ryzMTuQaGxAU3av7-g5fHu0s,26990
+pip/_vendor/rich/styled.py,sha256=eZNnzGrI4ki_54pgY3Oj0T-x3lxdXTYh4_ryDB24wBU,1258
+pip/_vendor/rich/syntax.py,sha256=eDKIRwl--eZ0Lwo2da2RRtfutXGavrJO61Cl5OkS59U,36371
+pip/_vendor/rich/table.py,sha256=ZmT7V7MMCOYKw7TGY9SZLyYDf6JdM-WVf07FdVuVhTI,40049
+pip/_vendor/rich/terminal_theme.py,sha256=1j5-ufJfnvlAo5Qsi_ACZiXDmwMXzqgmFByObT9-yJY,3370
+pip/_vendor/rich/text.py,sha256=AO7JPCz6-gaN1thVLXMBntEmDPVYFgFNG1oM61_sanU,47552
+pip/_vendor/rich/theme.py,sha256=oNyhXhGagtDlbDye3tVu3esWOWk0vNkuxFw-_unlaK0,3771
+pip/_vendor/rich/themes.py,sha256=0xgTLozfabebYtcJtDdC5QkX5IVUEaviqDUJJh4YVFk,102
+pip/_vendor/rich/traceback.py,sha256=c0WmB_L04_UfZbLaoH982_U_s7eosxKMUiAVmDPdRYU,35861
+pip/_vendor/rich/tree.py,sha256=yWnQ6rAvRGJ3qZGqBrxS2SW2TKBTNrP0SdY8QxOFPuw,9451
+pip/_vendor/tomli/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072
+pip/_vendor/tomli/__init__.py,sha256=nrBC18DbXRTCFo7ElG5BDeWpHJzOhokvXk215GM8Z2I,314
+pip/_vendor/tomli/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/tomli/__pycache__/_parser.cpython-312.pyc,,
+pip/_vendor/tomli/__pycache__/_re.cpython-312.pyc,,
+pip/_vendor/tomli/__pycache__/_types.cpython-312.pyc,,
+pip/_vendor/tomli/_parser.py,sha256=pBIjTIa_cQs2HglDJ2lh8OJfptfDa6eg5-7Iej4BjHs,26440
+pip/_vendor/tomli/_re.py,sha256=oSNZ_ilFI6chEuQ01YRSoUydBQr_okF_mSdHTkFmv90,3396
+pip/_vendor/tomli/_types.py,sha256=-GTG2VUqkpxwMqzmVO4F7ybKddIbAnuAHXfmWQcTi3Q,254
+pip/_vendor/tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
+pip/_vendor/tomli_w/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072
+pip/_vendor/tomli_w/__init__.py,sha256=0F8yDtXx3Uunhm874KrAcP76srsM98y7WyHQwCulZbo,169
+pip/_vendor/tomli_w/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/tomli_w/__pycache__/_writer.cpython-312.pyc,,
+pip/_vendor/tomli_w/_writer.py,sha256=dsifFS2xYf1i76mmRyfz9y125xC7Z_HQ845ZKhJsYXs,6961
+pip/_vendor/tomli_w/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
+pip/_vendor/truststore/LICENSE,sha256=M757fo-k_Rmxdg4ajtimaL2rhSyRtpLdQUJLy3Jan8o,1086
+pip/_vendor/truststore/__init__.py,sha256=Bu7kqkmpunhLsj5xCu8gT_25ktoPXcSnwe8VHk1GmJo,1320
+pip/_vendor/truststore/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/truststore/__pycache__/_api.cpython-312.pyc,,
+pip/_vendor/truststore/__pycache__/_macos.cpython-312.pyc,,
+pip/_vendor/truststore/__pycache__/_openssl.cpython-312.pyc,,
+pip/_vendor/truststore/__pycache__/_ssl_constants.cpython-312.pyc,,
+pip/_vendor/truststore/__pycache__/_windows.cpython-312.pyc,,
+pip/_vendor/truststore/_api.py,sha256=CYJCV5BTfttZYfqY3movdMBE-8az7uhET_LYbKT2Nn4,11413
+pip/_vendor/truststore/_macos.py,sha256=nZlLkOmszUE0g6ryRwBVGY5COzPyudcsiJtDWarM5LQ,20503
+pip/_vendor/truststore/_openssl.py,sha256=zB-SQvJydks7tQ0yIwrP6GD3fQNSSaPiq7zw4yF5T40,2412
+pip/_vendor/truststore/_ssl_constants.py,sha256=NUD4fVKdSD02ri7-db0tnO0VqLP9aHuzmStcW7tAl08,1130
+pip/_vendor/truststore/_windows.py,sha256=rAHyKYD8M7t-bXfG8VgOVa3TpfhVhbt4rZQlO45YuP8,17993
+pip/_vendor/truststore/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_vendor/urllib3/LICENSE.txt,sha256=Ew46ZNX91dCWp1JpRjSn2d8oRGnehuVzIQAmgEHj1oY,1093
+pip/_vendor/urllib3/__init__.py,sha256=JMo1tg1nIV1AeJ2vENC_Txfl0e5h6Gzl9DGVk1rWRbo,6979
+pip/_vendor/urllib3/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/urllib3/__pycache__/_base_connection.cpython-312.pyc,,
+pip/_vendor/urllib3/__pycache__/_collections.cpython-312.pyc,,
+pip/_vendor/urllib3/__pycache__/_request_methods.cpython-312.pyc,,
+pip/_vendor/urllib3/__pycache__/_version.cpython-312.pyc,,
+pip/_vendor/urllib3/__pycache__/connection.cpython-312.pyc,,
+pip/_vendor/urllib3/__pycache__/connectionpool.cpython-312.pyc,,
+pip/_vendor/urllib3/__pycache__/exceptions.cpython-312.pyc,,
+pip/_vendor/urllib3/__pycache__/fields.cpython-312.pyc,,
+pip/_vendor/urllib3/__pycache__/filepost.cpython-312.pyc,,
+pip/_vendor/urllib3/__pycache__/poolmanager.cpython-312.pyc,,
+pip/_vendor/urllib3/__pycache__/response.cpython-312.pyc,,
+pip/_vendor/urllib3/_base_connection.py,sha256=HzcSEHexgDrRUr60jNniB2KQjdw97SedD5luRfHtCXg,5580
+pip/_vendor/urllib3/_collections.py,sha256=aOVm2mKilvuvT1efAGtkA7pi65C1NC3HJfpFxvYBS8A,17522
+pip/_vendor/urllib3/_request_methods.py,sha256=gCeF85SO_UU4WoPwYHIoz_tw-eM_EVOkLFp8OFsC7DA,9931
+pip/_vendor/urllib3/_version.py,sha256=-gMrKhsPiOj0XzUezVFa59d1S6QgQiKgQT5gGgyM8-w,520
+pip/_vendor/urllib3/connection.py,sha256=Zos3qxKDW9-GQ6aVqfBQVRM5soB0IjHxY_xXWpJaFTI,42786
+pip/_vendor/urllib3/connectionpool.py,sha256=sGFnddXYwlx7KC4JCP1gKvdNGLNK-YTCJDdGACGj3Y8,44164
+pip/_vendor/urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc,,
+pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-312.pyc,,
+pip/_vendor/urllib3/contrib/emscripten/__init__.py,sha256=ZruXaWKVzAEJdqNH3NEh0mrHrw--2VZYb0zX0RonNZA,870
+pip/_vendor/urllib3/contrib/emscripten/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/urllib3/contrib/emscripten/__pycache__/connection.cpython-312.pyc,,
+pip/_vendor/urllib3/contrib/emscripten/__pycache__/fetch.cpython-312.pyc,,
+pip/_vendor/urllib3/contrib/emscripten/__pycache__/request.cpython-312.pyc,,
+pip/_vendor/urllib3/contrib/emscripten/__pycache__/response.cpython-312.pyc,,
+pip/_vendor/urllib3/contrib/emscripten/connection.py,sha256=giElsBoUsKVURbZzb8GCrJmqW23Xnvj2aNyQVF42slg,8960
+pip/_vendor/urllib3/contrib/emscripten/emscripten_fetch_worker.js,sha256=z1k3zZ4_hDKd3-tN7wzz8LHjHC2pxN_uu8B3k9D9A3c,3677
+pip/_vendor/urllib3/contrib/emscripten/fetch.py,sha256=5xcd--viFxZd2nBy0aK73dtJ9Tsh1yYZU_SUXwnwibk,23520
+pip/_vendor/urllib3/contrib/emscripten/request.py,sha256=mL28szy1KvE3NJhWor5jNmarp8gwplDU-7gwGZY5g0Q,566
+pip/_vendor/urllib3/contrib/emscripten/response.py,sha256=CDpY0GFoluR3IxECkM2gH5uDfYDM0EL1FWr7B76wtgM,9719
+pip/_vendor/urllib3/contrib/pyopenssl.py,sha256=Pi_qwvdl83tNpuj1k5pCpP_gmxlk4BfFrTlAeg-qZ_U,19760
+pip/_vendor/urllib3/contrib/socks.py,sha256=rSuklfha4-vto-8qSLhSPmf5lmRPIYuqdAxKe9bg2RA,7639
+pip/_vendor/urllib3/exceptions.py,sha256=hQPHoqo4yw46esNSVkciVQXVUgAxWAglsh6MbyQta-Q,9945
+pip/_vendor/urllib3/fields.py,sha256=aGLFAVZpVU-FbJlllve4Ahg0-pH9ZZBQ2Iz7lfpkjkM,10801
+pip/_vendor/urllib3/filepost.py,sha256=U8eNZ-mpKKHhrlbHEEiTxxgK16IejhEa7uz42yqA_dI,2388
+pip/_vendor/urllib3/http2/__init__.py,sha256=xzrASH7R5ANRkPJOot5lGnATOq3KKuyXzI42rcnwmqs,1741
+pip/_vendor/urllib3/http2/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/urllib3/http2/__pycache__/connection.cpython-312.pyc,,
+pip/_vendor/urllib3/http2/__pycache__/probe.cpython-312.pyc,,
+pip/_vendor/urllib3/http2/connection.py,sha256=bHMH6fNvatwXPrKqrcn74yA3pUWcqPDppnK1LcKCbP8,12578
+pip/_vendor/urllib3/http2/probe.py,sha256=nnAkqbhAakOiF75rz7W0udZ38Eeh_uD8fjV74N73FEI,3014
+pip/_vendor/urllib3/poolmanager.py,sha256=grIrETG1rgHYBaQau1yN4xxfK8U9CKVRYmwBXW_kdSo,23929
+pip/_vendor/urllib3/py.typed,sha256=UaCuPFa3H8UAakbt-5G8SPacldTOGvJv18pPjUJ5gDY,93
+pip/_vendor/urllib3/response.py,sha256=DzBM-dzl6x_h2EZGc9iw0gbOkQ36bolC7WESH4TurH8,53031
+pip/_vendor/urllib3/util/__init__.py,sha256=-qeS0QceivazvBEKDNFCAI-6ACcdDOE4TMvo7SLNlAQ,1001
+pip/_vendor/urllib3/util/__pycache__/__init__.cpython-312.pyc,,
+pip/_vendor/urllib3/util/__pycache__/connection.cpython-312.pyc,,
+pip/_vendor/urllib3/util/__pycache__/proxy.cpython-312.pyc,,
+pip/_vendor/urllib3/util/__pycache__/request.cpython-312.pyc,,
+pip/_vendor/urllib3/util/__pycache__/response.cpython-312.pyc,,
+pip/_vendor/urllib3/util/__pycache__/retry.cpython-312.pyc,,
+pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-312.pyc,,
+pip/_vendor/urllib3/util/__pycache__/ssl_match_hostname.cpython-312.pyc,,
+pip/_vendor/urllib3/util/__pycache__/ssltransport.cpython-312.pyc,,
+pip/_vendor/urllib3/util/__pycache__/timeout.cpython-312.pyc,,
+pip/_vendor/urllib3/util/__pycache__/url.cpython-312.pyc,,
+pip/_vendor/urllib3/util/__pycache__/util.cpython-312.pyc,,
+pip/_vendor/urllib3/util/__pycache__/wait.cpython-312.pyc,,
+pip/_vendor/urllib3/util/connection.py,sha256=JjO722lzHlzLXPTkr9ZWBdhseXnMVjMSb1DJLVrXSnQ,4444
+pip/_vendor/urllib3/util/proxy.py,sha256=seP8-Q5B6bB0dMtwPj-YcZZQ30vHuLqRu-tI0JZ2fzs,1148
+pip/_vendor/urllib3/util/request.py,sha256=p9Ki9eo1tFBO-jqV_7KmmJ60RKqoY2r4ao0SmaHLyOs,8086
+pip/_vendor/urllib3/util/response.py,sha256=vQE639uoEhj1vpjEdxu5lNIhJCSUZkd7pqllUI0BZOA,3374
+pip/_vendor/urllib3/util/retry.py,sha256=2YnSX-_FecMShD61Mx5s68J0_btUHZrrc_BkFVRS1P4,19577
+pip/_vendor/urllib3/util/ssl_.py,sha256=Oqe3rIhUU3e3GVgZob2hxmR8Q0ZDhrhESPlPP_GLaVQ,17742
+pip/_vendor/urllib3/util/ssl_match_hostname.py,sha256=Ft44KJzTzGMmKff_ZXP91li2V4WhmvEy16PKBcv4vZk,5479
+pip/_vendor/urllib3/util/ssltransport.py,sha256=Ez4O8pR_vT8dan_FvqBYS6dgDfBXEMfVfrzcdUoWfi4,8847
+pip/_vendor/urllib3/util/timeout.py,sha256=vsUJRpO0nfKk-y1OKlgFGY1ONJGPgkaZ7B7kruEpVYw,10363
+pip/_vendor/urllib3/util/url.py,sha256=PEDQMypidude0nAZctLLiFK9epN-LPnSH7KpOLLwqH0,15256
+pip/_vendor/urllib3/util/util.py,sha256=j3lbZK1jPyiwD34T8IgJzdWEZVT-4E-0vYIJi9UjeNA,1146
+pip/_vendor/urllib3/util/wait.py,sha256=_ph8IrUR3sqPqi0OopQgJUlH4wzkGeM5CiyA7XGGtmI,4423
+pip/_vendor/vendor.txt,sha256=PfDJYBv6Lg3KI8xXSk-QCk1_2tBgqyA_q4NUNQ7ZMyA,317
+pip/py.typed,sha256=EBVvvPRTn_eIpz5e5QztSCdrMX7Qwd7VP93RSoIlZ2I,286
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/REQUESTED b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/WHEEL b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/WHEEL
new file mode 100644
index 0000000..d8b9936
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/WHEEL
@@ -0,0 +1,4 @@
+Wheel-Version: 1.0
+Generator: flit 3.12.0
+Root-Is-Purelib: true
+Tag: py3-none-any
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/entry_points.txt
new file mode 100644
index 0000000..c6436d2
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/entry_points.txt
@@ -0,0 +1,4 @@
+[console_scripts]
+pip=pip._internal.cli.main:main
+pip3=pip._internal.cli.main:main
+
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/AUTHORS.txt b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/AUTHORS.txt
new file mode 100644
index 0000000..691991f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/AUTHORS.txt
@@ -0,0 +1,888 @@
+@Switch01
+A_Rog
+Aakanksha Agrawal
+Aarni Koskela
+abdul rawoof
+Abdul Rawoof Khan
+Abhinav Sagar
+ABHYUDAY PRATAP SINGH
+abs51295
+AceGentile
+Adam Chainz
+Adam Tse
+Adam Turner
+Adam Wentz
+admin
+Adolfo Ochagavía
+Adrien Morison
+Agus
+ahayrapetyan
+Ahilya
+AinsworthK
+Akash Srivastava
+Alan Yee
+Albert Tugushev
+Albert-Guan
+albertg
+Alberto Sottile
+Aleks Bunin
+Ales Erjavec
+Alessandro Molina
+Alethea Flowers
+Alex Gaynor
+Alex Grönholm
+Alex Hedges
+Alex Loosley
+Alex Morega
+Alex Stachowiak
+Alexander Regueiro
+Alexander Shtyrov
+Alexandre Conrad
+Alexey Popravka
+Aleš Erjavec
+Alli
+Aman
+Ami Fischman
+Ananya Maiti
+Anatoly Techtonik
+Anders Kaseorg
+Andre Aguiar
+Andrea Corna
+Andreas Lutro
+Andrei Geacar
+Andrew Gaul
+Andrew Shymanel
+Andrey Bienkowski
+Andrey Bulgakov
+Andrés Delfino
+Andy Freeland
+Andy Kluger
+Ani Hayrapetyan
+Aniruddha Basak
+Anish Tambe
+Anrs Hu
+Anthony Sottile
+Antoine Lambert
+Antoine Musso
+Anton Ovchinnikov
+Anton Patrushev
+Anton Zelenov
+Antonio Alvarado Hernandez
+Antony Lee
+Antti Kaihola
+Anubhav Patel
+Anudit Nagar
+Anuj Godase
+AQNOUCH Mohammed
+AraHaan
+Aren Siekmeier
+arena
+arenasys
+Arindam Choudhury
+Armin Ronacher
+Arnon Yaari
+Artem
+Arun Babu Neelicattu
+Ashley Manton
+Ashwin Ramaswami
+atse
+Atsushi Odagiri
+Avinash Karhana
+Avner Cohen
+Awit (Ah-Wit) Ghirmai
+Baptiste Mispelon
+Barney Gale
+barneygale
+Bartek Ogryczak
+Bastian Venthur
+Ben Bodenmiller
+Ben Darnell
+Ben Hoyt
+Ben Mares
+Ben Rosser
+Bence Nagy
+Benjamin Peterson
+Benjamin VanEvery
+Benoit Pierre
+Berker Peksag
+Bernard
+Bernard Tyers
+Bernardo B. Marques
+Bernhard M. Wiedemann
+Bertil Hatt
+Bhavam Vidyarthi
+Blazej Michalik
+Bogdan Opanchuk
+BorisZZZ
+Brad Erickson
+Bradley Ayers
+Bradley Reynolds
+Branch Vincent
+Brandon L. Reiss
+Brandt Bucher
+Brannon Dorsey
+Brett Randall
+Brett Rosen
+Brian Cristante
+Brian Rosner
+briantracy
+BrownTruck
+Bruno Oliveira
+Bruno Renié
+Bruno S
+Bstrdsmkr
+Buck Golemon
+burrows
+Bussonnier Matthias
+bwoodsend
+c22
+Caleb Brown
+Caleb Martinez
+Calvin Smith
+Carl Meyer
+Carlos Liam
+Carol Willing
+Carter Thayer
+Cass
+Chandrasekhar Atina
+Charlie Marsh
+charwick
+Chih-Hsuan Yen
+Chris Brinker
+Chris Hunt
+Chris Jerdonek
+Chris Kuehl
+Chris Markiewicz
+Chris McDonough
+Chris Pawley
+Chris Pryer
+Chris Wolfe
+Christian Clauss
+Christian Heimes
+Christian Oudard
+Christoph Reiter
+Christopher Hunt
+Christopher Snyder
+chrysle
+cjc7373
+Clark Boylan
+Claudio Jolowicz
+Clay McClure
+Cody
+Cody Soyland
+Colin Watson
+Collin Anderson
+Connor Osborn
+Cooper Lees
+Cooper Ry Lees
+Cory Benfield
+Cory Wright
+Craig Kerstiens
+Cristian Sorinel
+Cristina
+Cristina Muñoz
+ctg123
+Curtis Doty
+cytolentino
+Daan De Meyer
+Dale
+Damian
+Damian Quiroga
+Damian Shaw
+Dan Black
+Dan Savilonis
+Dan Sully
+Dane Hillard
+daniel
+Daniel Collins
+Daniel Hahler
+Daniel Hollas
+Daniel Holth
+Daniel Jost
+Daniel Katz
+Daniel Shaulov
+Daniele Esposti
+Daniele Nicolodi
+Daniele Procida
+Daniil Konovalenko
+Danny Hermes
+Danny McClanahan
+Darren Kavanagh
+Dav Clark
+Dave Abrahams
+Dave Jones
+David Aguilar
+David Black
+David Bordeynik
+David Caro
+David D Lowe
+David Evans
+David Hewitt
+David Linke
+David Poggi
+David Poznik
+David Pursehouse
+David Runge
+David Tucker
+David Wales
+Davidovich
+Dayna Blackwell
+ddelange
+Deepak Sharma
+Deepyaman Datta
+Denis Roussel (ACSONE)
+Denise Yu
+dependabot[bot]
+dereksz
+derwolfe
+Desetude
+developer
+Devesh Kumar
+Devesh Kumar Singh
+devsagul
+Dewank Pant
+Diego Caraballo
+Diego Ramirez
+DiegoCaraballo
+Dimitri Merejkowsky
+Dimitri Papadopoulos
+Dimitri Papadopoulos Orfanos
+Dirk Stolle
+dkjsone
+Dmitrii Sutiagin
+Dmitry Gladkov
+Dmitry Volodin
+Domen Kožar
+Dominic Davis-Foster
+Donald Stufft
+Dongweiming
+doron zarhi
+Dos Moonen
+Douglas Thor
+DrFeathers
+Dustin Ingram
+Dustin Rodrigues
+Dwayne Bailey
+Ed Morley
+Edgar Ramírez
+Edgar Ramírez Mondragón
+Ee Durbin
+Efflam Lemaillet
+efflamlemaillet
+Eitan Adler
+ekristina
+elainechan
+Eli Schwartz
+Elisha Hollander
+Ellen Marie Dash
+Emil Burzo
+Emil Styrke
+Emmanuel Arias
+Endoh Takanao
+enoch
+Erdinc Mutlu
+Eric Cousineau
+Eric Gillingham
+Eric Hanchrow
+Eric Hopper
+Erik M. Bray
+Erik Rose
+Erwin Janssen
+Eugene Vereshchagin
+everdimension
+Federico
+Felipe Peter
+Felix Yan
+fiber-space
+Filip Kokosiński
+Filipe Laíns
+Finn Womack
+finnagin
+Flavio Amurrio
+Florian Briand
+Florian Rathgeber
+Francesco
+Francesco Montesano
+Fredrik Orderud
+Fredrik Roubert
+Frost Ming
+Gabriel Curio
+Gabriel de Perthuis
+Garry Polley
+gavin
+gdanielson
+Gene Wood
+Geoffrey Sneddon
+George Margaritis
+George Song
+Georgi Valkov
+Georgy Pchelkin
+Gertjan van Zwieten
+ghost
+Giancarlo Cicellyn Comneno
+Giftlin Rajaiah
+gizmoguy1
+gkdoc
+Godefroid Chapelle
+Gopinath M
+GOTO Hayato
+gousaiyang
+gpiks
+Greg Roodt
+Greg Ward
+Guido Diepen
+Guilherme Espada
+Guillaume Seguin
+gutsytechster
+Guy Rozendorn
+Guy Tuval
+gzpan123
+Hanjun Kim
+Hari Charan
+Harsh Vardhan
+Harsha Sai
+harupy
+Harutaka Kawamura
+Hasan-8326
+hauntsaninja
+Henrich Hartzer
+Henry Schreiner
+Herbert Pfennig
+Holly Stotelmyer
+Honnix
+Hsiaoming Yang
+Hugo
+Hugo Lopes Tavares
+Hugo van Kemenade
+Hugues Bruant
+Hynek Schlawack
+iamsrp-deshaw
+Ian Bicking
+Ian Cordasco
+Ian Lee
+Ian Stapleton Cordasco
+Ian Wienand
+Igor Kuzmitshov
+Igor Sobreira
+Ikko Ashimine
+Ilan Schnell
+Illia Volochii
+Ilya Abdolmanafi
+Ilya Baryshev
+Inada Naoki
+Ionel Cristian Mărieș
+Ionel Maries Cristian
+Itamar Turner-Trauring
+iTrooz
+Ivan Pozdeev
+J. Nick Koston
+Jacob Kim
+Jacob Walls
+Jaime Sanz
+Jake Lishman
+jakirkham
+Jakub Kuczys
+Jakub Stasiak
+Jakub Vysoky
+Jakub Wilk
+James
+James Cleveland
+James Curtin
+James Firth
+James Gerity
+James Polley
+Jan Pokorný
+Jannis Leidel
+Jarek Potiuk
+jarondl
+Jason Curtis
+Jason R. Coombs
+JasonMo
+JasonMo1
+Jay Graves
+Jean Abou Samra
+Jean-Christophe Fillion-Robin
+Jeff Barber
+Jeff Dairiki
+Jeff Widman
+Jelmer Vernooij
+jenix21
+Jeremy Fleischman
+Jeremy Stanley
+Jeremy Zafran
+Jesse Rittner
+Jiashuo Li
+Jim Fisher
+Jim Garrison
+Jinzhe Zeng
+Jiun Bae
+Jivan Amara
+Joa
+Joe Bylund
+Joe Michelini
+Johannes Altmanninger
+John Paton
+John Sirois
+John T. Wodder II
+John-Scott Atlakson
+johnthagen
+Jon Banafato
+Jon Dufresne
+Jon Parise
+Jonas Nockert
+Jonathan Herbert
+Joonatan Partanen
+Joost Molenaar
+Jorge Niedbalski
+Joseph Bylund
+Joseph Long
+Josh Bronson
+Josh Cannon
+Josh Hansen
+Josh Schneier
+Joshua
+JoshuaPerdue
+Jost Migenda
+Juan Luis Cano Rodríguez
+Juanjo Bazán
+Judah Rand
+Julian Berman
+Julian Gethmann
+Julien Demoor
+Julien Stephan
+July Tikhonov
+Jussi Kukkonen
+Justin van Heek
+jwg4
+Jyrki Pulliainen
+Kai Chen
+Kai Mueller
+Kamal Bin Mustafa
+Karolina Surma
+kasium
+kaustav haldar
+Kaz Nishimura
+keanemind
+Keith Maxwell
+Kelsey Hightower
+Kenneth Belitzky
+Kenneth Reitz
+Kevin Burke
+Kevin Carter
+Kevin Frommelt
+Kevin R Patterson
+Kevin Turcios
+Kexuan Sun
+Kit Randel
+Klaas van Schelven
+KOLANICH
+konstin
+kpinc
+Krishan Bhasin
+Krishna Oza
+Kumar McMillan
+Kuntal Majumder
+Kurt McKee
+Kyle Persohn
+lakshmanaram
+Laszlo Kiss-Kollar
+Laurent Bristiel
+Laurent LAPORTE
+Laurie O
+Laurie Opperman
+layday
+Leon Sasson
+Lev Givon
+Lincoln de Sousa
+Lipis
+lorddavidiii
+Loren Carvalho
+Louis Liu
+Lucas Cimon
+Ludovic Gasc
+Luis Carlos
+Luis Medel
+Lukas Geiger
+Lukas Juhrich
+Luke Macken
+Luo Jiebin
+luojiebin
+luz.paz
+László Kiss Kollár
+M00nL1ght
+MajorTanya
+Malcolm Smith
+Marc Abramowitz
+Marc Tamlyn
+Marcus Smith
+Mariatta
+Mark Kohler
+Mark McLoughlin
+Mark Williams
+Markus Hametner
+Martey Dodoo
+Martin Fischer
+Martin Häcker
+Martin Pavlasek
+Masaki
+Masklinn
+Matej Stuchlik
+Mateusz Sokół
+Mathew Jennings
+Mathieu Bridon
+Mathieu Kniewallner
+Matt Bacchi
+Matt Good
+Matt Maker
+Matt Robenolt
+Matt Wozniski
+matthew
+Matthew Einhorn
+Matthew Feickert
+Matthew Gilliard
+Matthew Hughes
+Matthew Iversen
+Matthew Treinish
+Matthew Trumbell
+Matthew Willson
+Matthias Bussonnier
+mattip
+Maurits van Rees
+Max W Chase
+Maxim Kurnikov
+Maxime Rouyrre
+mayeut
+mbaluna
+Md Sujauddin Sekh
+mdebi
+Meet Vasita
+memoselyk
+meowmeowcat
+Michael
+Michael Aquilina
+Michael E. Karpeles
+Michael Klich
+Michael Mintz
+Michael Williamson
+michaelpacer
+Michał Górny
+Mickaël Schoentgen
+Miguel Araujo Perez
+Mihir Singh
+Mike
+Mike Hendricks
+Min RK
+MinRK
+Miro Hrončok
+Mohsen Fooladnezhad
+Monica Baluna
+montefra
+Monty Taylor
+morotti
+mrKazzila
+Muha Ajjan
+MUTHUSRIHEMADHARSHINI S A
+Nadav Wexler
+Nahuel Ambrosini
+Nate Coraor
+Nate Prewitt
+Nathan Houghton
+Nathaniel J. Smith
+Nehal J Wani
+Neil Botelho
+Nguyễn Gia Phong
+Nicholas Serra
+Nick Coghlan
+Nick Stenning
+Nick Timkovich
+Nicolas Bock
+Nicole Harris
+Nikhil Benesch
+Nikhil Ladha
+Nikita Chepanov
+Nikolay Korolev
+Nipunn Koorapati
+Nitesh Sharma
+Niyas Sait
+Noah
+Noah Gorny
+Nok Lam Chan
+Norbert Manthey
+Nothing-991
+Nowell Strite
+NtaleGrey
+nucccc
+nvdv
+OBITORASU
+Ofek Lev
+ofrinevo
+Oleg Burnaev
+Oliver Freund
+Oliver Jeeves
+Oliver Mannion
+Oliver Tonnhofer
+Olivier Girardot
+Olivier Grisel
+Ollie Rutherfurd
+Omar Abdalgwad
+OMOTO Kenji
+Omry Yadan
+onlinejudge95
+Oren Held
+Oscar Benjamin
+oxygen dioxide
+Oz N Tiram
+Pachwenko
+Paresh Joshi
+Patrick Dubroy
+Patrick Jenkins
+Patrick Lawson
+patricktokeeffe
+Patrik Kopkan
+Paul Ganssle
+Paul Kehrer
+Paul Moore
+Paul Nasrat
+Paul Oswald
+Paul van der Linden
+Paulus Schoutsen
+Pavel Safronov
+Pavithra Eswaramoorthy
+Pawel Jasinski
+Paweł Szramowski
+Pekka Klärck
+Peter Gessler
+Peter Lisák
+Peter Shen
+Peter Waller
+Petr Viktorin
+petr-tik
+Phaneendra Chiruvella
+Phil Elson
+Phil Freo
+Phil Pennock
+Phil Whelan
+Philip Jägenstedt
+Philip Molloy
+Philippe Ombredanne
+Pi Delport
+Pierre-Yves Rofes
+Pieter Degroote
+pip
+Prabakaran Kumaresshan
+Prabhjyotsing Surjit Singh Sodhi
+Prabhu Marappan
+Pradyun Gedam
+Prashant Sharma
+Pratik Mallya
+pre-commit-ci[bot]
+Preet Thakkar
+Preston Holmes
+Przemek Wrzos
+Pulkit Goyal
+q0w
+Qiangning Hong
+Qiming Xu
+qraqras
+Quentin Lee
+Quentin Pradet
+R. David Murray
+Rafael Caricio
+RAHUL KUMAR
+rahulkotargasthi
+Ralf Schmitt
+Ran Benita
+Randy Döring
+Razzi Abuissa
+rdb
+Reece Dunham
+Remi Rampin
+Rene Dudfield
+Riccardo Magliocchetti
+Riccardo Schirone
+Richard Jones
+Richard Si
+Ricky Ng-Adam
+Rishi
+rmorotti
+RobberPhex
+Robert Collins
+Robert McGibbon
+Robert Pollak
+Robert T. McGibbon
+robin elisha robinson
+Rodney, Tiara
+Roey Berman
+Rohan Jain
+Roman Bogorodskiy
+Roman Donchenko
+Romuald Brunet
+ronaudinho
+Ronny Pfannschmidt
+Rory McCann
+Ross Brattain
+Roy Wellington Ⅳ
+RoyS
+Ruairidh MacLeod
+Russell Keith-Magee
+Ryan Shepherd
+Ryan Wooden
+ryneeverett
+Ryuma Asai
+S. Guliaev
+Sachi King
+Sai Sridhar Tarra
+Salvatore Rinchiera
+sandeepkiran-js
+Sander Van Balen
+Savio Jomton
+schlamar
+Scott Kitterman
+Sean
+seanj
+Sebastian Höffner
+Sebastian Jordan
+Sebastian Schaetz
+Segev Finer
+SeongSoo Cho
+Sepehr Rasouli
+sepehrrasooli
+Sergey Vasilyev
+Seth Michael Larson
+Seth Woodworth
+Shahar Epstein
+Shantanu
+shenxianpeng
+shireenrao
+Shivam Shrey
+Shivansh-007
+Shixian Sheng
+Shlomi Fish
+Shovan Maity
+Shubham Nagure
+Simeon Visser
+Simon Cross
+Simon Pichugin
+sinoroc
+sinscary
+snook92
+socketubs
+Sorin Sbarnea
+Srinivas Nyayapati
+Srishti Hegde
+Stavros Korokithakis
+Stefan Scherfke
+Stefano Rivera
+Stephan Erb
+Stephane Chazelas
+Stephen Payne
+Stephen Rosen
+stepshal
+Steve (Gadget) Barnes
+Steve Barnes
+Steve Dower
+Steve Kowalik
+Steven Myint
+Steven Silvester
+stonebig
+studioj
+Stéphane Bidoul
+Stéphane Bidoul (ACSONE)
+Stéphane Klein
+Sumana Harihareswara
+Surbhi Sharma
+Sviatoslav Sydorenko
+Sviatoslav Sydorenko (Святослав Сидоренко)
+Swat009
+Sylvain
+S​age Abdullah
+Takayuki SHIMIZUKAWA
+Taneli Hukkinen
+tbeswick
+Terrance
+Thiago
+Thijs Triemstra
+Thomas Fenzl
+Thomas Grainger
+Thomas Guettler
+Thomas Johansson
+Thomas Kluyver
+Thomas Smith
+Thomas VINCENT
+Tim D. Smith
+Tim Gates
+Tim Harder
+Tim Heap
+tim smith
+tinruufu
+Tobias Hermann
+Tom Forbes
+Tom Freudenheim
+Tom V
+Tomas Hrnciar
+Tomas Orsava
+Tomer Chachamu
+Tommi Enenkel | AnB
+Tomáš Hrnčiar
+Tony Beswick
+Tony Narlock
+Tony Zhaocheng Tan
+TonyBeswick
+toonarmycaptain
+Toshio Kuratomi
+toxinu
+Travis Swicegood
+Tushar Sadhwani
+Tzu-ping Chung
+Valentin Haenel
+Victor Stinner
+victorvpaulo
+Vikram - Google
+Viktor Szépe
+Ville Skyttä
+Vinay Sajip
+Vincent Fazio
+Vincent Philippon
+Vinicyus Macedo
+Vipul Kumar
+Vitaly Babiy
+Vladimir Fokow
+Vladimir Rutsky
+W. Trevor King
+Weida Hong
+Wil Tan
+Wilfred Hughes
+William Edwards
+William ML Leslie
+William T Olson
+William Woodruff
+Wilson Mo
+wim glenn
+Winson Luk
+Wolfgang Maier
+Wu Zhenyu
+XAMES3
+Xavier Fernandez
+Xianpeng Shen
+xoviat
+xtreak
+YAMAMOTO Takashi
+Yash
+Yashraj
+Yen Chi Hsuan
+Yeray Diaz Diaz
+Youngkwang Yang
+Yoval P
+Yu Jian
+Yuan Jing Vincent Yan
+Yuki Kobayashi
+Yusuke Hayashi
+Zachary Ware
+zackzack38
+Zearin
+Zhiping Deng
+ziebam
+Zvezdan Petkovic
+Łukasz Langa
+Роман Донченко
+Семён Марьясин
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/LICENSE.txt b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/LICENSE.txt
new file mode 100644
index 0000000..8e7b65e
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2008-present The pip developers (see AUTHORS.txt file)
+
+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.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt
new file mode 100644
index 0000000..d8b3b56
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt
@@ -0,0 +1,13 @@
+Copyright 2012-2021  Eric Larson
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE
new file mode 100644
index 0000000..62b076c
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE
@@ -0,0 +1,20 @@
+This package contains a modified version of ca-bundle.crt:
+
+ca-bundle.crt -- Bundle of CA Root Certificates
+
+This is a bundle of X.509 certificates of public Certificate Authorities
+(CA). These were automatically extracted from Mozilla's root certificates
+file (certdata.txt).  This file can be found in the mozilla source tree:
+https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt
+It contains the certificates in PEM format and therefore
+can be directly used with curl / libcurl / php_curl, or with
+an Apache+mod_ssl webserver for SSL client authentication.
+Just configure this file as the SSLCACertificateFile.#
+
+***** BEGIN LICENSE BLOCK *****
+This Source Code Form is subject to the terms of the Mozilla Public License,
+v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain
+one at http://mozilla.org/MPL/2.0/.
+
+***** END LICENSE BLOCK *****
+@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt
new file mode 100644
index 0000000..c31ac56
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt
@@ -0,0 +1,284 @@
+A. HISTORY OF THE SOFTWARE
+==========================
+
+Python was created in the early 1990s by Guido van Rossum at Stichting
+Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
+as a successor of a language called ABC.  Guido remains Python's
+principal author, although it includes many contributions from others.
+
+In 1995, Guido continued his work on Python at the Corporation for
+National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
+in Reston, Virginia where he released several versions of the
+software.
+
+In May 2000, Guido and the Python core development team moved to
+BeOpen.com to form the BeOpen PythonLabs team.  In October of the same
+year, the PythonLabs team moved to Digital Creations (now Zope
+Corporation, see http://www.zope.com).  In 2001, the Python Software
+Foundation (PSF, see http://www.python.org/psf/) was formed, a
+non-profit organization created specifically to own Python-related
+Intellectual Property.  Zope Corporation is a sponsoring member of
+the PSF.
+
+All Python releases are Open Source (see http://www.opensource.org for
+the Open Source Definition).  Historically, most, but not all, Python
+releases have also been GPL-compatible; the table below summarizes
+the various releases.
+
+    Release         Derived     Year        Owner       GPL-
+                    from                                compatible? (1)
+
+    0.9.0 thru 1.2              1991-1995   CWI         yes
+    1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes
+    1.6             1.5.2       2000        CNRI        no
+    2.0             1.6         2000        BeOpen.com  no
+    1.6.1           1.6         2001        CNRI        yes (2)
+    2.1             2.0+1.6.1   2001        PSF         no
+    2.0.1           2.0+1.6.1   2001        PSF         yes
+    2.1.1           2.1+2.0.1   2001        PSF         yes
+    2.2             2.1.1       2001        PSF         yes
+    2.1.2           2.1.1       2002        PSF         yes
+    2.1.3           2.1.2       2002        PSF         yes
+    2.2.1           2.2         2002        PSF         yes
+    2.2.2           2.2.1       2002        PSF         yes
+    2.2.3           2.2.2       2003        PSF         yes
+    2.3             2.2.2       2002-2003   PSF         yes
+    2.3.1           2.3         2002-2003   PSF         yes
+    2.3.2           2.3.1       2002-2003   PSF         yes
+    2.3.3           2.3.2       2002-2003   PSF         yes
+    2.3.4           2.3.3       2004        PSF         yes
+    2.3.5           2.3.4       2005        PSF         yes
+    2.4             2.3         2004        PSF         yes
+    2.4.1           2.4         2005        PSF         yes
+    2.4.2           2.4.1       2005        PSF         yes
+    2.4.3           2.4.2       2006        PSF         yes
+    2.4.4           2.4.3       2006        PSF         yes
+    2.5             2.4         2006        PSF         yes
+    2.5.1           2.5         2007        PSF         yes
+    2.5.2           2.5.1       2008        PSF         yes
+    2.5.3           2.5.2       2008        PSF         yes
+    2.6             2.5         2008        PSF         yes
+    2.6.1           2.6         2008        PSF         yes
+    2.6.2           2.6.1       2009        PSF         yes
+    2.6.3           2.6.2       2009        PSF         yes
+    2.6.4           2.6.3       2009        PSF         yes
+    2.6.5           2.6.4       2010        PSF         yes
+    3.0             2.6         2008        PSF         yes
+    3.0.1           3.0         2009        PSF         yes
+    3.1             3.0.1       2009        PSF         yes
+    3.1.1           3.1         2009        PSF         yes
+    3.1.2           3.1         2010        PSF         yes
+    3.2             3.1         2010        PSF         yes
+
+Footnotes:
+
+(1) GPL-compatible doesn't mean that we're distributing Python under
+    the GPL.  All Python licenses, unlike the GPL, let you distribute
+    a modified version without making your changes open source.  The
+    GPL-compatible licenses make it possible to combine Python with
+    other software that is released under the GPL; the others don't.
+
+(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
+    because its license has a choice of law clause.  According to
+    CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
+    is "not incompatible" with the GPL.
+
+Thanks to the many outside volunteers who have worked under Guido's
+direction to make these releases possible.
+
+
+B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
+===============================================================
+
+PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+--------------------------------------------
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation
+("PSF"), and the Individual or Organization ("Licensee") accessing and
+otherwise using this software ("Python") in source or binary form and
+its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF hereby
+grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+analyze, test, perform and/or display publicly, prepare derivative works,
+distribute, and otherwise use Python alone or in any derivative version,
+provided, however, that PSF's License Agreement and PSF's notice of copyright,
+i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+Python Software Foundation; All Rights Reserved" are retained in Python alone or
+in any derivative version prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python.
+
+4. PSF is making Python available to Licensee on an "AS IS"
+basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between PSF and
+Licensee.  This License Agreement does not grant permission to use PSF
+trademarks or trade name in a trademark sense to endorse or promote
+products or services of Licensee, or any third party.
+
+8. By copying, installing or otherwise using Python, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
+-------------------------------------------
+
+BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
+
+1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
+office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
+Individual or Organization ("Licensee") accessing and otherwise using
+this software in source or binary form and its associated
+documentation ("the Software").
+
+2. Subject to the terms and conditions of this BeOpen Python License
+Agreement, BeOpen hereby grants Licensee a non-exclusive,
+royalty-free, world-wide license to reproduce, analyze, test, perform
+and/or display publicly, prepare derivative works, distribute, and
+otherwise use the Software alone or in any derivative version,
+provided, however, that the BeOpen Python License is retained in the
+Software, alone or in any derivative version prepared by Licensee.
+
+3. BeOpen is making the Software available to Licensee on an "AS IS"
+basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
+SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
+AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
+DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+5. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+6. This License Agreement shall be governed by and interpreted in all
+respects by the law of the State of California, excluding conflict of
+law provisions.  Nothing in this License Agreement shall be deemed to
+create any relationship of agency, partnership, or joint venture
+between BeOpen and Licensee.  This License Agreement does not grant
+permission to use BeOpen trademarks or trade names in a trademark
+sense to endorse or promote products or services of Licensee, or any
+third party.  As an exception, the "BeOpen Python" logos available at
+http://www.pythonlabs.com/logos.html may be used according to the
+permissions granted on that web page.
+
+7. By copying, installing or otherwise using the software, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
+---------------------------------------
+
+1. This LICENSE AGREEMENT is between the Corporation for National
+Research Initiatives, having an office at 1895 Preston White Drive,
+Reston, VA 20191 ("CNRI"), and the Individual or Organization
+("Licensee") accessing and otherwise using Python 1.6.1 software in
+source or binary form and its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, CNRI
+hereby grants Licensee a nonexclusive, royalty-free, world-wide
+license to reproduce, analyze, test, perform and/or display publicly,
+prepare derivative works, distribute, and otherwise use Python 1.6.1
+alone or in any derivative version, provided, however, that CNRI's
+License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
+1995-2001 Corporation for National Research Initiatives; All Rights
+Reserved" are retained in Python 1.6.1 alone or in any derivative
+version prepared by Licensee.  Alternately, in lieu of CNRI's License
+Agreement, Licensee may substitute the following text (omitting the
+quotes): "Python 1.6.1 is made available subject to the terms and
+conditions in CNRI's License Agreement.  This Agreement together with
+Python 1.6.1 may be located on the Internet using the following
+unique, persistent identifier (known as a handle): 1895.22/1013.  This
+Agreement may also be obtained from a proxy server on the Internet
+using the following URL: http://hdl.handle.net/1895.22/1013".
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python 1.6.1 or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python 1.6.1.
+
+4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
+basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. This License Agreement shall be governed by the federal
+intellectual property law of the United States, including without
+limitation the federal copyright law, and, to the extent such
+U.S. federal law does not apply, by the law of the Commonwealth of
+Virginia, excluding Virginia's conflict of law provisions.
+Notwithstanding the foregoing, with regard to derivative works based
+on Python 1.6.1 that incorporate non-separable material that was
+previously distributed under the GNU General Public License (GPL), the
+law of the Commonwealth of Virginia shall govern this License
+Agreement only as to issues arising under or with respect to
+Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this
+License Agreement shall be deemed to create any relationship of
+agency, partnership, or joint venture between CNRI and Licensee.  This
+License Agreement does not grant permission to use CNRI trademarks or
+trade name in a trademark sense to endorse or promote products or
+services of Licensee, or any third party.
+
+8. By clicking on the "ACCEPT" button where indicated, or by copying,
+installing or otherwise using Python 1.6.1, Licensee agrees to be
+bound by the terms and conditions of this License Agreement.
+
+        ACCEPT
+
+
+CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
+--------------------------------------------------
+
+Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
+The Netherlands.  All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE
new file mode 100644
index 0000000..e06d208
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE
@@ -0,0 +1,202 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md
new file mode 100644
index 0000000..f706835
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md
@@ -0,0 +1,31 @@
+BSD 3-Clause License
+
+Copyright (c) 2013-2026, Kim Davies and contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/msgpack/COPYING b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/msgpack/COPYING
new file mode 100644
index 0000000..f067af3
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/msgpack/COPYING
@@ -0,0 +1,14 @@
+Copyright (C) 2008-2011 INADA Naoki 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE
new file mode 100644
index 0000000..6f62d44
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE
@@ -0,0 +1,3 @@
+This software is made available under the terms of *either* of the licenses
+found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made
+under the terms of *both* these licenses.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE
new file mode 100644
index 0000000..f433b1a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE
@@ -0,0 +1,177 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD
new file mode 100644
index 0000000..42ce7b7
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD
@@ -0,0 +1,23 @@
+Copyright (c) Donald Stufft and individual contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE
new file mode 100644
index 0000000..1bb5a44
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE
@@ -0,0 +1,17 @@
+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.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE
new file mode 100644
index 0000000..f35fed9
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2010-202x The platformdirs developers
+
+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.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE
new file mode 100644
index 0000000..446a1a8
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2006-2022 by the respective authors (see AUTHORS file).
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE
new file mode 100644
index 0000000..b0ae9db
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 Thomas Kluyver
+
+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.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE
new file mode 100644
index 0000000..67db858
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE
@@ -0,0 +1,175 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE
new file mode 100644
index 0000000..b907776
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE
@@ -0,0 +1,13 @@
+Copyright (c) 2018, Tzu-ping Chung 
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE
new file mode 100644
index 0000000..4415505
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2020 Will McGugan
+
+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.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE
new file mode 100644
index 0000000..e859590
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Taneli Hukkinen
+
+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.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE
new file mode 100644
index 0000000..e859590
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Taneli Hukkinen
+
+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.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE
new file mode 100644
index 0000000..7ec568c
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2022 Seth Michael Larson
+
+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.
diff --git a/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt
new file mode 100644
index 0000000..e6183d0
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2008-2020 Andrey Petrov and contributors.
+
+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.
diff --git a/venv/lib/python3.12/site-packages/pip/__init__.py b/venv/lib/python3.12/site-packages/pip/__init__.py
new file mode 100644
index 0000000..451ca2f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/__init__.py
@@ -0,0 +1,13 @@
+from __future__ import annotations
+
+__version__ = "26.2.1"
+
+
+def main(args: list[str] | None = None) -> int:
+    """This is an internal API only meant for use by pip's own console scripts.
+
+    For additional details, see https://github.com/pypa/pip/issues/7498.
+    """
+    from pip._internal.utils.entrypoints import _wrapper
+
+    return _wrapper(args)
diff --git a/venv/lib/python3.12/site-packages/pip/__main__.py b/venv/lib/python3.12/site-packages/pip/__main__.py
new file mode 100644
index 0000000..094361c
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/__main__.py
@@ -0,0 +1,24 @@
+import os
+import sys
+
+# Remove '' and current working directory from the first entry
+# of sys.path, if present to avoid using current directory
+# in pip commands check, freeze, install, list and show,
+# when invoked as python -m pip 
+if sys.path[0] in ("", os.getcwd()):
+    sys.path.pop(0)
+
+# If we are running from a wheel, add the wheel to sys.path
+# This allows the usage python pip-*.whl/pip install pip-*.whl
+if not __spec__ or __spec__.parent == "":
+    # __file__ is pip-*.whl/pip/__main__.py
+    # first dirname call strips of '/__main__.py', second strips off '/pip'
+    # Resulting path is the name of the wheel itself
+    # Add that to sys.path so we can import pip
+    path = os.path.dirname(os.path.dirname(__file__))
+    sys.path.insert(0, path)
+
+if __name__ == "__main__":
+    from pip._internal.cli.main import main as _main
+
+    sys.exit(_main())
diff --git a/venv/lib/python3.12/site-packages/pip/__pip-runner__.py b/venv/lib/python3.12/site-packages/pip/__pip-runner__.py
new file mode 100644
index 0000000..a1aee81
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/__pip-runner__.py
@@ -0,0 +1,50 @@
+"""Execute exactly this copy of pip, within a different environment.
+
+This file is named as it is, to ensure that this module can't be imported via
+an import statement.
+"""
+
+# /!\ This version compatibility check section must be Python 2 compatible. /!\
+
+import sys
+
+# Copied from pyproject.toml
+PYTHON_REQUIRES = (3, 10)
+
+
+def version_str(version):  # type: ignore
+    return ".".join(str(v) for v in version)
+
+
+if sys.version_info[:2] < PYTHON_REQUIRES:
+    raise SystemExit(
+        "This version of pip does not support python {} (requires >={}).".format(
+            version_str(sys.version_info[:2]), version_str(PYTHON_REQUIRES)
+        )
+    )
+
+# From here on, we can use Python 3 features, but the syntax must remain
+# Python 2 compatible.
+
+import runpy  # noqa: E402
+from importlib.machinery import PathFinder  # noqa: E402
+from os.path import dirname  # noqa: E402
+
+PIP_SOURCES_ROOT = dirname(dirname(__file__))
+
+
+class PipImportRedirectingFinder:
+    @classmethod
+    def find_spec(self, fullname, path=None, target=None):  # type: ignore
+        if fullname != "pip":
+            return None
+
+        spec = PathFinder.find_spec(fullname, [PIP_SOURCES_ROOT], target)
+        assert spec, (PIP_SOURCES_ROOT, fullname)
+        return spec
+
+
+sys.meta_path.insert(0, PipImportRedirectingFinder())
+
+assert __name__ == "__main__", "Cannot run __pip-runner__.py as a non-main module"
+runpy.run_module("pip", run_name="__main__", alter_sys=True)
diff --git a/venv/lib/python3.12/site-packages/pip/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..6e46273
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/__pycache__/__main__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/__pycache__/__main__.cpython-312.pyc
new file mode 100644
index 0000000..7bd3247
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/__pycache__/__main__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/__pycache__/__pip-runner__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/__pycache__/__pip-runner__.cpython-312.pyc
new file mode 100644
index 0000000..caa3a38
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/__pycache__/__pip-runner__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/__init__.py
new file mode 100755
index 0000000..24d0baf
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/__init__.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+from pip._internal.utils import _log
+
+# init_logging() must be called before any call to logging.getLogger()
+# which happens at import of most modules.
+_log.init_logging()
+
+
+def main(args: list[str] | None = None) -> int:
+    """This is preserved for old console scripts that may still be referencing
+    it.
+
+    For additional details, see https://github.com/pypa/pip/issues/7498.
+    """
+    from pip._internal.utils.entrypoints import _wrapper
+
+    return _wrapper(args)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..a64e6ff
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/cache.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/cache.cpython-312.pyc
new file mode 100644
index 0000000..4b94f97
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/cache.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/configuration.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/configuration.cpython-312.pyc
new file mode 100644
index 0000000..319d9e4
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/configuration.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/exceptions.cpython-312.pyc
new file mode 100644
index 0000000..bcacf75
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/exceptions.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/main.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/main.cpython-312.pyc
new file mode 100644
index 0000000..af54b96
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/main.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/pyproject.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/pyproject.cpython-312.pyc
new file mode 100644
index 0000000..d299878
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/pyproject.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-312.pyc
new file mode 100644
index 0000000..3564869
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-312.pyc
new file mode 100644
index 0000000..0365d1b
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/build_env/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__init__.py
new file mode 100644
index 0000000..194e274
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__init__.py
@@ -0,0 +1,25 @@
+"""Build environments used for isolation during build backend calls."""
+
+from pip._internal.build_env.base import (
+    BuildEnvironment,
+    BuildEnvironmentInstaller,
+    BuildIsolationMode,
+)
+from pip._internal.build_env.installer import (
+    InprocessBuildEnvironmentInstaller,
+    SubprocessBuildEnvironmentInstaller,
+)
+from pip._internal.build_env.noop import NoOpBuildEnvironment
+from pip._internal.build_env.venv import VenvBuildEnvironment
+from pip._internal.build_env.virtual import VirtualBuildEnvironment
+
+__all__ = [
+    "BuildEnvironment",
+    "BuildEnvironmentInstaller",
+    "BuildIsolationMode",
+    "InprocessBuildEnvironmentInstaller",
+    "NoOpBuildEnvironment",
+    "SubprocessBuildEnvironmentInstaller",
+    "VenvBuildEnvironment",
+    "VirtualBuildEnvironment",
+]
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..f9fb2df
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/base.cpython-312.pyc
new file mode 100644
index 0000000..e48986e
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/base.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/installer.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/installer.cpython-312.pyc
new file mode 100644
index 0000000..3dfcd2f
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/installer.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/noop.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/noop.cpython-312.pyc
new file mode 100644
index 0000000..9fd2f7e
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/noop.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/venv.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/venv.cpython-312.pyc
new file mode 100644
index 0000000..5d546d8
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/venv.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/virtual.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/virtual.cpython-312.pyc
new file mode 100644
index 0000000..cf5695d
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/build_env/__pycache__/virtual.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/build_env/base.py b/venv/lib/python3.12/site-packages/pip/_internal/build_env/base.py
new file mode 100644
index 0000000..ce26d9c
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/build_env/base.py
@@ -0,0 +1,103 @@
+from __future__ import annotations
+
+import abc
+from collections.abc import Iterable
+from contextlib import AbstractContextManager as ContextManager
+from typing import TYPE_CHECKING, Literal, Protocol
+
+from pip._vendor.packaging.version import Version
+
+from pip._internal.locations import get_scheme
+from pip._internal.metadata import get_default_environment, get_environment
+from pip._internal.utils.packaging import get_requirement
+
+if TYPE_CHECKING:
+    from pip._internal.req.req_install import InstallRequirement
+
+
+BuildIsolationMode = Literal["off", "virtual", "venv"]
+
+
+def _dedup(a: str, b: str) -> tuple[str] | tuple[str, str]:
+    return (a, b) if a != b else (a,)
+
+
+class Prefix:
+    # TODO: simplify this data model when the legacy subprocess installer is removed
+    def __init__(self, path: str, *, venv_executable: str | None = None) -> None:
+        self.path = path
+        self.setup = False
+        scheme = get_scheme("", prefix=path)
+        self.bin_dir = scheme.scripts
+        self.lib_dirs = _dedup(scheme.purelib, scheme.platlib)
+        self.venv_executable = venv_executable
+
+
+class BuildEnvironmentInstaller(Protocol):
+    """
+    Interface for installing build dependencies into an isolated build
+    environment.
+    """
+
+    def install(
+        self,
+        requirements: Iterable[str],
+        prefix: Prefix,
+        *,
+        kind: str,
+        for_req: InstallRequirement | None,
+    ) -> None: ...
+
+
+class BuildEnvironment(ContextManager[None], metaclass=abc.ABCMeta):
+    """Creates and manages an isolated environment to install build deps"""
+
+    lib_dirs: list[str]
+    python_executable: str
+
+    @abc.abstractmethod
+    def __init__(self, installer: BuildEnvironmentInstaller): ...
+
+    def check_requirements(
+        self, reqs: Iterable[str]
+    ) -> tuple[set[tuple[str, str]], set[str]]:
+        """Return 2 sets:
+        - conflicting requirements: set of (installed, wanted) reqs tuples
+        - missing requirements: set of reqs
+        """
+        missing = set()
+        conflicting = set()
+        if reqs:
+            env = (
+                get_environment(self.lib_dirs)
+                if hasattr(self, "lib_dirs")
+                else get_default_environment()
+            )
+            for req_str in reqs:
+                req = get_requirement(req_str)
+                # We're explicitly evaluating with an empty extra value, since build
+                # environments are not provided any mechanism to select specific extras.
+                if req.marker is not None and not req.marker.evaluate({"extra": ""}):
+                    continue
+                dist = env.get_distribution(req.name)
+                if not dist:
+                    missing.add(req_str)
+                    continue
+                if isinstance(dist.version, Version):
+                    installed_req_str = f"{req.name}=={dist.version}"
+                else:
+                    installed_req_str = f"{req.name}==={dist.version}"
+                if not req.specifier.contains(dist.version, prereleases=True):
+                    conflicting.add((installed_req_str, req_str))
+                # FIXME: Consider direct URL?
+        return conflicting, missing
+
+    @abc.abstractmethod
+    def install_requirements(
+        self,
+        requirements: Iterable[str],
+        prefix_as_string: str,
+        *,
+        kind: str,
+        for_req: InstallRequirement | None = None,
+    ) -> None: ...
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/build_env/installer.py b/venv/lib/python3.12/site-packages/pip/_internal/build_env/installer.py
new file mode 100644
index 0000000..790ad5f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/build_env/installer.py
@@ -0,0 +1,333 @@
+from __future__ import annotations
+
+import logging
+import sys
+import textwrap
+from collections.abc import Iterable, Sequence
+from contextlib import AbstractContextManager as ContextManager
+from contextlib import nullcontext
+from io import StringIO
+from typing import TYPE_CHECKING
+
+from pip._internal.build_env.base import Prefix
+from pip._internal.cli.spinners import open_rich_spinner, open_spinner
+from pip._internal.exceptions import (
+    BuildDependencyInstallError,
+    DiagnosticPipError,
+    InstallWheelBuildError,
+    PipError,
+)
+from pip._internal.metadata import get_environment
+from pip._internal.utils.logging import VERBOSE, capture_logging
+from pip._internal.utils.misc import get_runnable_pip
+from pip._internal.utils.subprocess import call_subprocess
+from pip._internal.utils.temp_dir import TempDirectory
+
+if TYPE_CHECKING:
+    from pip._internal.cache import WheelCache
+    from pip._internal.index.package_finder import PackageFinder
+    from pip._internal.operations.build.build_tracker import BuildTracker
+    from pip._internal.req.req_install import InstallRequirement
+    from pip._internal.resolution.base import BaseResolver
+
+
+logger = logging.getLogger(__name__)
+
+
+class SubprocessBuildEnvironmentInstaller:
+    """
+    Install build dependencies by calling pip in a subprocess.
+    """
+
+    def __init__(
+        self,
+        finder: PackageFinder,
+        build_constraints: list[str] | None = None,
+    ) -> None:
+        self.finder = finder
+        self._build_constraints = build_constraints or []
+
+    def install(
+        self,
+        requirements: Iterable[str],
+        prefix: Prefix,
+        *,
+        kind: str,
+        for_req: InstallRequirement | None,
+    ) -> None:
+        finder = self.finder
+        args: list[str] = [
+            get_runnable_pip(),
+            "install",
+            # HACK: --prefix shouldn't be necessary for venv environments, but
+            # we set it anyway so if it's set via an envvar or configuration
+            # file, it won't break things, *sigh*.
+            "--prefix",
+            prefix.path,
+            "--no-user",
+            "--no-warn-script-location",
+            "--disable-pip-version-check",
+            # As the build environment is ephemeral, it's wasteful to
+            # pre-compile everything, especially as not every Python
+            # module will be used/compiled in most cases.
+            "--no-compile",
+            # The prefix specified two lines above, thus
+            # target from config file or env var should be ignored
+            "--target",
+            "",
+        ]
+        if prefix.venv_executable:
+            args.insert(0, prefix.venv_executable)
+        else:
+            args.insert(0, sys.executable)
+            args.append("--ignore-installed")
+
+        if logger.getEffectiveLevel() <= logging.DEBUG:
+            args.append("-vv")
+        elif logger.getEffectiveLevel() <= VERBOSE:
+            args.append("-v")
+        for format_control in ("no_binary", "only_binary"):
+            formats = getattr(finder.format_control, format_control)
+            args.extend(
+                (
+                    "--" + format_control.replace("_", "-"),
+                    ",".join(sorted(formats or {":none:"})),
+                )
+            )
+
+        if finder.release_control is not None:
+            # Use ordered args to preserve the user's original command-line order
+            # This is important because later flags can override earlier ones
+            for attr_name, value in finder.release_control.get_ordered_args():
+                args.extend(("--" + attr_name.replace("_", "-"), value))
+
+        index_urls = finder.index_urls
+        if index_urls:
+            args.extend(["-i", index_urls[0]])
+            for extra_index in index_urls[1:]:
+                args.extend(["--extra-index-url", extra_index])
+        else:
+            args.append("--no-index")
+        for link in finder.find_links:
+            args.extend(["--find-links", link])
+
+        # is not None: forward an empty --proxy "" (disable proxying) too.
+        if finder.proxy is not None:
+            args.extend(["--proxy", finder.proxy])
+        if finder.no_proxy_env:
+            args.append("--no-proxy-env")
+        for host in finder.trusted_hosts:
+            args.extend(["--trusted-host", host])
+        if finder.custom_cert:
+            args.extend(["--cert", finder.custom_cert])
+        if finder.client_cert:
+            args.extend(["--client-cert", finder.client_cert])
+        if finder.prefer_binary:
+            args.append("--prefer-binary")
+        if finder.refresh_package:
+            args.extend(["--refresh-package", ",".join(finder.refresh_package)])
+
+        # Only build constraints apply in the isolated build environment.
+        # _PIP_IN_BUILD_IGNORE_CONSTRAINTS tells the subprocess to ignore the
+        # regular constraints it inherits (via PIP_CONSTRAINT or config files).
+        # Build constraints reach it through --build-constraint, which also
+        # constrains any nested builds.
+        for constraint_file in self._build_constraints:
+            args.extend(["--build-constraint", constraint_file])
+
+        if finder.uploaded_prior_to:
+            args.extend(["--uploaded-prior-to", finder.uploaded_prior_to.isoformat()])
+        args.append("--")
+        args.extend(requirements)
+
+        identify_requirement = (
+            f" for {for_req.name}" if for_req and for_req.name else ""
+        )
+        with open_spinner(f"Installing {kind}") as spinner:
+            call_subprocess(
+                args,
+                command_desc=f"installing {kind}{identify_requirement}",
+                spinner=spinner,
+                extra_environ={"_PIP_IN_BUILD_IGNORE_CONSTRAINTS": "1"},
+            )
+
+
+class InprocessBuildEnvironmentInstaller:
+    """
+    Install build dependencies via the already running pip process.
+
+    This contains a stripped down version of the install command with
+    only the logic necessary for installing build dependencies. The
+    finder, session, build tracker, and wheel cache are reused, but new
+    instances of everything else are created as needed.
+
+    Options are inherited from the parent install command unless
+    they don't make sense for build dependencies (in which case, they
+    are hard-coded, see comments below).
+    """
+
+    # TODO: this plays poorly with venv-based build environments, but cannot be
+    # fixed until pip gains better support for operating within a Python
+    # environment that isn't the running environment.
+
+    def __init__(
+        self,
+        *,
+        finder: PackageFinder,
+        build_tracker: BuildTracker,
+        wheel_cache: WheelCache,
+        build_constraints: Sequence[InstallRequirement] = (),
+        verbosity: int = 0,
+    ) -> None:
+        from pip._internal.operations.prepare import RequirementPreparer
+
+        self._finder = finder
+        self._build_constraints = build_constraints
+        self._wheel_cache = wheel_cache
+        self._level = 0
+
+        build_dir = TempDirectory(kind="build-env-install", globally_managed=True)
+        self._preparer = RequirementPreparer(
+            build_isolation_installer=self,
+            # Inherited options or state.
+            finder=finder,
+            session=finder._link_collector.session,
+            build_dir=build_dir.path,
+            build_tracker=build_tracker,
+            verbosity=verbosity,
+            # This is irrelevant as it only applies to editable requirements.
+            src_dir="",
+            # Hard-coded options (that should NOT be inherited).
+            download_dir=None,
+            build_isolation="virtual",
+            check_build_deps=False,
+            progress_bar="off",
+            # TODO: hash-checking should be extended to build deps, but that is
+            # deferred for later as it'd be a breaking change.
+            require_hashes=False,
+            use_user_site=False,
+            lazy_wheel=False,
+            legacy_resolver=False,
+            allow_editables=True,
+        )
+
+    def install(
+        self,
+        requirements: Iterable[str],
+        prefix: Prefix,
+        *,
+        kind: str,
+        for_req: InstallRequirement | None,
+    ) -> None:
+        """Install entrypoint. Manages output capturing and error handling."""
+        capture_logs = not logger.isEnabledFor(VERBOSE) and self._level == 0
+        if capture_logs:
+            # Hide the logs from the installation of build dependencies.
+            # They will be shown only if an error occurs.
+            capture_ctx: ContextManager[StringIO] = capture_logging()
+            spinner: ContextManager[None] = open_rich_spinner(f"Installing {kind}")
+        else:
+            # Otherwise, pass-through all logs (with a header).
+            capture_ctx, spinner = nullcontext(StringIO()), nullcontext()
+            logger.info("Installing %s ...", kind)
+
+        try:
+            self._level += 1
+            with spinner, capture_ctx as stream:
+                self._install_impl(requirements, prefix)
+
+        except DiagnosticPipError as exc:
+            # Format similar to a nested subprocess error, where the
+            # causing error is shown first, followed by the build error.
+            logger.info(textwrap.dedent(stream.getvalue()))
+            logger.error("%s", exc, extra={"rich": True})
+            logger.info("")
+            raise BuildDependencyInstallError(
+                for_req, requirements, cause=exc, log_lines=None
+            )
+
+        except Exception as exc:
+            logs: list[str] | None = textwrap.dedent(stream.getvalue()).splitlines()
+            if not capture_logs:
+                # If logs aren't being captured, then display the error inline
+                # with the rest of the logs.
+                logs = None
+                if isinstance(exc, PipError):
+                    logger.error("%s", exc)
+                else:
+                    logger.exception("pip crashed unexpectedly")
+            raise BuildDependencyInstallError(
+                for_req, requirements, cause=exc, log_lines=logs
+            )
+
+        finally:
+            self._level -= 1
+
+    def _install_impl(self, requirements: Iterable[str], prefix: Prefix) -> None:
+        """Core build dependency install logic."""
+        from pip._internal.commands.install import installed_packages_summary
+        from pip._internal.req import install_given_reqs
+        from pip._internal.req.constructors import install_req_from_line
+        from pip._internal.wheel_builder import build
+
+        ireqs = [install_req_from_line(req, user_supplied=True) for req in requirements]
+        ireqs.extend(self._build_constraints)
+
+        resolver = self._make_resolver()
+        resolved_set = resolver.resolve(ireqs, check_supported_wheels=True)
+        self._preparer.prepare_linked_requirements_more(
+            resolved_set.requirements.values()
+        )
+
+        reqs_to_build = [
+            r for r in resolved_set.requirements_to_install if not r.is_wheel
+        ]
+        _, build_failures = build(
+            reqs_to_build, self._wheel_cache, verify=True, allow_editables=True
+        )
+        if build_failures:
+            raise InstallWheelBuildError(build_failures)
+
+        installed = install_given_reqs(
+            resolver.get_installation_order(resolved_set),
+            prefix=prefix.path,
+            # Hard-coded options (that should NOT be inherited).
+            root=None,
+            home=None,
+            warn_script_location=False,
+            use_user_site=False,
+            # As the build environment is ephemeral, it's wasteful to
+            # pre-compile everything since not all modules will be used.
+            pycompile=False,
+            progress_bar="off",
+            # Link console scripts to the build env's interpreter, not pip's.
+            script_executable=prefix.venv_executable,
+        )
+
+        env = get_environment(list(prefix.lib_dirs))
+        if summary := installed_packages_summary(installed, env):
+            logger.info(summary)
+
+    def _make_resolver(self) -> BaseResolver:
+        """Create a new resolver for one time use."""
+        # Legacy installer never used the legacy resolver so create a
+        # resolvelib resolver directly. Yuck.
+        from pip._internal.req.constructors import install_req_from_req_string
+        from pip._internal.resolution.resolvelib.resolver import Resolver
+
+        return Resolver(
+            make_install_req=install_req_from_req_string,
+            # Inherited state.
+            preparer=self._preparer,
+            finder=self._finder,
+            wheel_cache=self._wheel_cache,
+            # Hard-coded options (that should NOT be inherited).
+            ignore_requires_python=False,
+            use_user_site=False,
+            ignore_dependencies=False,
+            only_dependencies=False,
+            ignore_installed=True,
+            force_reinstall=False,
+            upgrade_strategy="to-satisfy-only",
+            py_version_info=None,
+        )
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/build_env/noop.py b/venv/lib/python3.12/site-packages/pip/_internal/build_env/noop.py
new file mode 100644
index 0000000..c9ee81c
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/build_env/noop.py
@@ -0,0 +1,42 @@
+from __future__ import annotations
+
+import sys
+from collections.abc import Iterable
+from types import TracebackType
+from typing import TYPE_CHECKING
+
+from pip._internal.build_env.base import BuildEnvironment
+
+if TYPE_CHECKING:
+    from pip._internal.req.req_install import InstallRequirement
+
+
+class NoOpBuildEnvironment(BuildEnvironment):
+    """A no-op drop-in replacement for BuildEnvironment"""
+
+    def __init__(self) -> None:
+        self.python_executable = sys.executable
+
+    def __enter__(self) -> None:
+        pass
+
+    def __exit__(
+        self,
+        exc_type: type[BaseException] | None,
+        exc_val: BaseException | None,
+        exc_tb: TracebackType | None,
+    ) -> None:
+        pass
+
+    def cleanup(self) -> None:
+        pass
+
+    def install_requirements(
+        self,
+        requirements: Iterable[str],
+        prefix_as_string: str,
+        *,
+        kind: str,
+        for_req: InstallRequirement | None = None,
+    ) -> None:
+        raise NotImplementedError()
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/build_env/venv.py b/venv/lib/python3.12/site-packages/pip/_internal/build_env/venv.py
new file mode 100644
index 0000000..d6d7c50
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/build_env/venv.py
@@ -0,0 +1,145 @@
+from __future__ import annotations
+
+import os
+import sys
+import sysconfig
+from collections.abc import Iterable
+from types import TracebackType
+from typing import TYPE_CHECKING
+
+from pip._internal.build_env.base import (
+    BuildEnvironment,
+    BuildEnvironmentInstaller,
+    Prefix,
+)
+from pip._internal.exceptions import VenvCreationError, VenvImportError
+from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
+
+if TYPE_CHECKING:
+    from pip._internal.req.req_install import InstallRequirement
+
+
+def _get_venv_path_from_sysconfig(name: str, env_dir: str) -> str:
+    vars = {
+        "base": env_dir,
+        "platbase": env_dir,
+    }
+    return sysconfig.get_path(name, scheme="venv", vars=vars)
+
+
+class VenvBuildEnvironment(BuildEnvironment):
+    """A venv-based build environment."""
+
+    def __init__(self, installer: BuildEnvironmentInstaller) -> None:
+        # We defer this import because certain distributions of Python do not include
+        # a functional venv out of the box.
+        try:
+            import venv
+        except ImportError:
+            raise VenvImportError
+
+        self._env_path = TempDirectory(
+            kind=tempdir_kinds.BUILD_ENV, globally_managed=True
+        ).path
+        # Use symlinks to support relocatable Python installations on POSIX, including
+        # python-build-standalone. This matches upstream venv CLI's behaviour.
+        env = venv.EnvBuilder(symlinks=(os.name != "nt"))
+        try:
+            context = env.ensure_directories(self._env_path)
+            env.create(self._env_path)
+        except OSError as e:
+            raise VenvCreationError(str(e))
+
+        if sys.version_info >= (3, 12):
+            # The context object was only documented in Python 3.12
+            self.lib_dirs = [context.lib_path]
+            self._bin_path = context.bin_path
+        elif sys.version_info[:2] == (3, 11):
+            # On Python 3.11, we can use sysconfig.
+            self.lib_dirs = [_get_venv_path_from_sysconfig("purelib", self._env_path)]
+            self._bin_path = _get_venv_path_from_sysconfig("scripts", self._env_path)
+        else:
+            # Otherwise, we need to manually construct all the paths... sigh.
+            if sys.platform == "win32":
+                libpath = os.path.join(self._env_path, "Lib", "site-packages")
+            else:
+                python = "pypy" if sys.implementation.name == "pypy" else "python"
+                libpath = os.path.join(
+                    self._env_path,
+                    "lib",
+                    f"{python}{sys.version_info.major}.{sys.version_info.minor}",
+                    "site-packages",
+                )
+            self.lib_dirs = [libpath]
+            # Same reasoning for try-except as for python_executable below.
+            try:
+                self._bin_path = context.bin_path
+            except AttributeError:
+                scripts_dir = "Scripts" if os.name == "nt" else "bin"
+                self._bin_path = os.path.join(self._env_path, scripts_dir)
+
+        # There are enough ways trying to construct the Python executable path can go
+        # wrong that we're better off assuming that the context object has the right
+        # attributes, and only when they don't exist do we try to guess.
+        #
+        # These attributes seem to exist in every CPython version after 3.10.1 and
+        # are documented to exist on 3.12 and higher.
+        try:
+            self.python_executable = context.env_exec_cmd
+        except AttributeError:
+            try:
+                self.python_executable = context.env_exe
+            except AttributeError:
+                executable_name = "python.exe" if os.name == "nt" else "python"
+                self.python_executable = os.path.join(self._bin_path, executable_name)
+
+        self._save_env: dict[str, str | None] = {}
+        self._installer = installer
+
+        if not os.path.exists(self.python_executable):
+            # This error is only likely on Windows due to interference from AV software.
+            raise VenvCreationError(
+                f"Python executable failed to copy to {self.python_executable}"
+            )
+
+    def __enter__(self) -> None:
+        # We want backend calls to be able to use binaries installed as if this
+        # virtual environment was "activated".
+        self._save_env = {
+            name: os.environ.get(name, None) for name in ("PATH", "PYTHONPATH")
+        }
+
+        new_path = [self._bin_path]
+        if old_path := self._save_env["PATH"]:
+            new_path.extend(old_path.split(os.pathsep))
+        # However, we don't want a pre-existing PYTHONPATH to influence the
+        # backend calls.
+        os.environ.update({"PATH": os.pathsep.join(new_path), "PYTHONPATH": ""})
+
+    def __exit__(
+        self,
+        exc_type: type[BaseException] | None,
+        exc_val: BaseException | None,
+        exc_tb: TracebackType | None,
+    ) -> None:
+        for varname, old_value in self._save_env.items():
+            if old_value is None:
+                os.environ.pop(varname, None)
+            else:
+                os.environ[varname] = old_value
+
+    def install_requirements(
+        self,
+        requirements: Iterable[str],
+        prefix_as_string: str,
+        *,
+        kind: str,
+        for_req: InstallRequirement | None = None,
+    ) -> None:
+        if not requirements:
+            return
+
+        # TODO: when better support for installing to arbitrary Python environments
+        # is added, replace this prefix hack with that.
+        prefix = Prefix(self._env_path, venv_executable=self.python_executable)
+        self._installer.install(requirements, prefix, kind=kind, for_req=for_req)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/build_env/virtual.py b/venv/lib/python3.12/site-packages/pip/_internal/build_env/virtual.py
new file mode 100644
index 0000000..2f06c6c
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/build_env/virtual.py
@@ -0,0 +1,139 @@
+from __future__ import annotations
+
+import os
+import site
+import sys
+import textwrap
+from collections import OrderedDict
+from collections.abc import Iterable
+from types import TracebackType
+from typing import TYPE_CHECKING
+
+from pip._internal.build_env.base import (
+    BuildEnvironment,
+    BuildEnvironmentInstaller,
+    Prefix,
+)
+from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
+
+if TYPE_CHECKING:
+    from pip._internal.req.req_install import InstallRequirement
+
+
+def get_system_sitepackages() -> set[str]:
+    """Get system site packages, as a normalized set of strings."""
+    system_sites = site.getsitepackages()
+    return {os.path.normcase(path) for path in system_sites}
+
+
+class VirtualBuildEnvironment(BuildEnvironment):
+    """Legacy build environment implementation.
+
+    Patches sys.path and uses sitecustomize.py to isolate Python processes. It has
+    known bugs and weird edge cases, but it exists as a fallback for platforms where
+    the venv module is unavailable or dysfunctional.
+    """
+
+    def __init__(self, installer: BuildEnvironmentInstaller) -> None:
+        self.python_executable = sys.executable
+        self.installer = installer
+        temp_dir = TempDirectory(kind=tempdir_kinds.BUILD_ENV, globally_managed=True)
+
+        self._prefixes = OrderedDict(
+            (name, Prefix(os.path.join(temp_dir.path, name)))
+            for name in ("normal", "overlay")
+        )
+
+        self._bin_dirs: list[str] = []
+        self.lib_dirs: list[str] = []
+        for prefix in reversed(list(self._prefixes.values())):
+            self._bin_dirs.append(prefix.bin_dir)
+            self.lib_dirs.extend(prefix.lib_dirs)
+
+        # Customize site to:
+        # - ensure .pth files are honored
+        # - prevent access to system site packages
+        system_sites = get_system_sitepackages()
+
+        self._site_dir = os.path.join(temp_dir.path, "site")
+        if not os.path.exists(self._site_dir):
+            os.mkdir(self._site_dir)
+        with open(
+            os.path.join(self._site_dir, "sitecustomize.py"), "w", encoding="utf-8"
+        ) as fp:
+            fp.write(textwrap.dedent("""
+                import os, site, sys
+
+                # First, discover all system-sites related paths.
+                original_sys_path = sys.path[:]
+                # Clear sys.path so addsitedir() will add system site paths and paths
+                # added by contained .pth files to sys.path reliably. This is necessary
+                # since Python 3.15, which notably no longer re-executes .pth files for
+                # known paths.
+                sys.path = []
+                known_paths = set()
+                for path in {system_sites!r}:
+                    site.addsitedir(path, known_paths=known_paths)
+                system_paths = set(os.path.normcase(path) for path in sys.path)
+
+                # Drop discovered system-sites related paths.
+                original_sys_path = [
+                    path for path in original_sys_path
+                    if os.path.normcase(path) not in system_paths
+                ]
+                sys.path = original_sys_path
+
+                # Second, add lib directories.
+                # ensuring .pth file are processed.
+                for path in {lib_dirs!r}:
+                    assert not path in sys.path
+                    site.addsitedir(path)
+                """).format(system_sites=system_sites, lib_dirs=self.lib_dirs))
+
+    def __enter__(self) -> None:
+        self._save_env = {
+            name: os.environ.get(name, None)
+            for name in ("PATH", "PYTHONNOUSERSITE", "PYTHONPATH")
+        }
+
+        path = self._bin_dirs[:]
+        old_path = self._save_env["PATH"]
+        if old_path:
+            path.extend(old_path.split(os.pathsep))
+
+        pythonpath = [self._site_dir]
+
+        os.environ.update(
+            {
+                "PATH": os.pathsep.join(path),
+                "PYTHONNOUSERSITE": "1",
+                "PYTHONPATH": os.pathsep.join(pythonpath),
+            }
+        )
+
+    def __exit__(
+        self,
+        exc_type: type[BaseException] | None,
+        exc_val: BaseException | None,
+        exc_tb: TracebackType | None,
+    ) -> None:
+        for varname, old_value in self._save_env.items():
+            if old_value is None:
+                os.environ.pop(varname, None)
+            else:
+                os.environ[varname] = old_value
+
+    def install_requirements(
+        self,
+        requirements: Iterable[str],
+        prefix_as_string: str,
+        *,
+        kind: str,
+        for_req: InstallRequirement | None = None,
+    ) -> None:
+        prefix = self._prefixes[prefix_as_string]
+        assert not prefix.setup
+        prefix.setup = True
+        if not requirements:
+            return
+        self.installer.install(requirements, prefix, kind=kind, for_req=for_req)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cache.py b/venv/lib/python3.12/site-packages/pip/_internal/cache.py
new file mode 100644
index 0000000..462231a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cache.py
@@ -0,0 +1,294 @@
+"""Cache Management"""
+
+from __future__ import annotations
+
+import hashlib
+import json
+import logging
+import os
+from pathlib import Path
+from typing import Any
+
+from pip._vendor.packaging.tags import Tag, interpreter_name, interpreter_version
+from pip._vendor.packaging.utils import canonicalize_name
+
+from pip._internal.exceptions import InvalidWheelFilename
+from pip._internal.models.direct_url import DirectUrl
+from pip._internal.models.link import Link
+from pip._internal.models.wheel import Wheel
+from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
+from pip._internal.utils.urls import path_to_url
+
+logger = logging.getLogger(__name__)
+
+ORIGIN_JSON_NAME = "origin.json"
+
+
+def _hash_dict(d: dict[str, str]) -> str:
+    """Return a stable sha224 of a dictionary."""
+    s = json.dumps(d, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
+    return hashlib.sha224(s.encode("ascii")).hexdigest()
+
+
+class Cache:
+    """An abstract class - provides cache directories for data from links
+
+    :param cache_dir: The root of the cache.
+    """
+
+    def __init__(self, cache_dir: str) -> None:
+        super().__init__()
+        assert not cache_dir or os.path.isabs(cache_dir)
+        self.cache_dir = cache_dir or None
+
+    def _get_cache_path_parts(self, link: Link) -> list[str]:
+        """Get parts of part that must be os.path.joined with cache_dir"""
+
+        # We want to generate an url to use as our cache key, we don't want to
+        # just reuse the URL because it might have other items in the fragment
+        # and we don't care about those.
+        key_parts = {"url": link.url_without_fragment}
+        if link.hash_name is not None and link.hash is not None:
+            key_parts[link.hash_name] = link.hash
+        if link.subdirectory_fragment:
+            key_parts["subdirectory"] = link.subdirectory_fragment
+
+        # Include interpreter name, major and minor version in cache key
+        # to cope with ill-behaved sdists that build a different wheel
+        # depending on the python version their setup.py is being run on,
+        # and don't encode the difference in compatibility tags.
+        # https://github.com/pypa/pip/issues/7296
+        key_parts["interpreter_name"] = interpreter_name()
+        key_parts["interpreter_version"] = interpreter_version()
+
+        # Encode our key url with sha224, we'll use this because it has similar
+        # security properties to sha256, but with a shorter total output (and
+        # thus less secure). However the differences don't make a lot of
+        # difference for our use case here.
+        hashed = _hash_dict(key_parts)
+
+        # We want to nest the directories some to prevent having a ton of top
+        # level directories where we might run out of sub directories on some
+        # FS.
+        parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]]
+
+        return parts
+
+    def _get_candidates(self, link: Link, canonical_package_name: str) -> list[Any]:
+        can_not_cache = not self.cache_dir or not canonical_package_name or not link
+        if can_not_cache:
+            return []
+
+        path = self.get_path_for_link(link)
+        if os.path.isdir(path):
+            return [(candidate, path) for candidate in os.listdir(path)]
+        return []
+
+    def get_path_for_link(self, link: Link) -> str:
+        """Return a directory to store cached items in for link."""
+        raise NotImplementedError()
+
+    def get(
+        self,
+        link: Link,
+        package_name: str | None,
+        supported_tags: list[Tag],
+    ) -> Link:
+        """Returns a link to a cached item if it exists, otherwise returns the
+        passed link.
+        """
+        raise NotImplementedError()
+
+
+class SimpleWheelCache(Cache):
+    """A cache of wheels for future installs."""
+
+    def __init__(self, cache_dir: str) -> None:
+        super().__init__(cache_dir)
+
+    def get_path_for_link(self, link: Link) -> str:
+        """Return a directory to store cached wheels for link
+
+        Because there are M wheels for any one sdist, we provide a directory
+        to cache them in, and then consult that directory when looking up
+        cache hits.
+
+        We only insert things into the cache if they have plausible version
+        numbers, so that we don't contaminate the cache with things that were
+        not unique. E.g. ./package might have dozens of installs done for it
+        and build a version of 0.0...and if we built and cached a wheel, we'd
+        end up using the same wheel even if the source has been edited.
+
+        :param link: The link of the sdist for which this will cache wheels.
+        """
+        parts = self._get_cache_path_parts(link)
+        assert self.cache_dir
+        # Store wheels within the root cache_dir
+        return os.path.join(self.cache_dir, "wheels", *parts)
+
+    def get(
+        self,
+        link: Link,
+        package_name: str | None,
+        supported_tags: list[Tag],
+    ) -> Link:
+        candidates = []
+
+        if link.is_existing_dir():
+            return link
+        if not package_name:
+            return link
+
+        canonical_package_name = canonicalize_name(package_name)
+        supported_tags_set = set(supported_tags)
+        for wheel_name, wheel_dir in self._get_candidates(link, canonical_package_name):
+            try:
+                wheel = Wheel(wheel_name)
+            except InvalidWheelFilename:
+                continue
+            if wheel.name != canonical_package_name:
+                logger.debug(
+                    "Ignoring cached wheel %s for %s as it "
+                    "does not match the expected distribution name %s.",
+                    wheel_name,
+                    link,
+                    package_name,
+                )
+                continue
+            if not wheel.supported(supported_tags_set):
+                # Built for a different python/arch/etc
+                continue
+            candidates.append(
+                (
+                    wheel.support_index_min(supported_tags),
+                    wheel_name,
+                    wheel_dir,
+                )
+            )
+
+        if not candidates:
+            return link
+
+        _, wheel_name, wheel_dir = min(candidates)
+        return Link(path_to_url(os.path.join(wheel_dir, wheel_name)))
+
+
+class EphemWheelCache(SimpleWheelCache):
+    """A SimpleWheelCache that creates it's own temporary cache directory"""
+
+    def __init__(self) -> None:
+        self._temp_dir = TempDirectory(
+            kind=tempdir_kinds.EPHEM_WHEEL_CACHE,
+            globally_managed=True,
+        )
+
+        super().__init__(self._temp_dir.path)
+
+
+class CacheEntry:
+    def __init__(
+        self,
+        link: Link,
+        persistent: bool,
+    ):
+        self.link = link
+        self.persistent = persistent
+        self.origin: DirectUrl | None = None
+        origin_direct_url_path = Path(self.link.file_path).parent / ORIGIN_JSON_NAME
+        if origin_direct_url_path.exists():
+            try:
+                self.origin = DirectUrl.from_json(
+                    origin_direct_url_path.read_text(encoding="utf-8")
+                )
+            except Exception as e:
+                logger.warning(
+                    "Ignoring invalid cache entry origin file %s for %s (%s)",
+                    origin_direct_url_path,
+                    link.filename,
+                    e,
+                )
+
+
+class WheelCache(Cache):
+    """Wraps EphemWheelCache and SimpleWheelCache into a single Cache
+
+    This Cache allows for gracefully degradation, using the ephem wheel cache
+    when a certain link is not found in the simple wheel cache first.
+    """
+
+    def __init__(self, cache_dir: str) -> None:
+        super().__init__(cache_dir)
+        self._wheel_cache = SimpleWheelCache(cache_dir)
+        self._ephem_cache = EphemWheelCache()
+
+    def get_path_for_link(self, link: Link) -> str:
+        return self._wheel_cache.get_path_for_link(link)
+
+    def get_ephem_path_for_link(self, link: Link) -> str:
+        return self._ephem_cache.get_path_for_link(link)
+
+    def get(
+        self,
+        link: Link,
+        package_name: str | None,
+        supported_tags: list[Tag],
+    ) -> Link:
+        cache_entry = self.get_cache_entry(link, package_name, supported_tags)
+        if cache_entry is None:
+            return link
+        return cache_entry.link
+
+    def get_cache_entry(
+        self,
+        link: Link,
+        package_name: str | None,
+        supported_tags: list[Tag],
+    ) -> CacheEntry | None:
+        """Returns a CacheEntry with a link to a cached item if it exists or
+        None. The cache entry indicates if the item was found in the persistent
+        or ephemeral cache.
+        """
+        retval = self._wheel_cache.get(
+            link=link,
+            package_name=package_name,
+            supported_tags=supported_tags,
+        )
+        if retval is not link:
+            return CacheEntry(retval, persistent=True)
+
+        retval = self._ephem_cache.get(
+            link=link,
+            package_name=package_name,
+            supported_tags=supported_tags,
+        )
+        if retval is not link:
+            return CacheEntry(retval, persistent=False)
+
+        return None
+
+    @staticmethod
+    def record_download_origin(cache_dir: str, download_info: DirectUrl) -> None:
+        origin_path = Path(cache_dir) / ORIGIN_JSON_NAME
+        if origin_path.exists():
+            try:
+                origin = DirectUrl.from_json(origin_path.read_text(encoding="utf-8"))
+            except Exception as e:
+                logger.warning(
+                    "Could not read origin file %s in cache entry (%s). "
+                    "Will attempt to overwrite it.",
+                    origin_path,
+                    e,
+                )
+            else:
+                # TODO: use DirectUrl.equivalent when
+                # https://github.com/pypa/pip/pull/10564 is merged.
+                if origin.url != download_info.url:
+                    logger.warning(
+                        "Origin URL %s in cache entry %s does not match download URL "
+                        "%s. This is likely a pip bug or a cache corruption issue. "
+                        "Will overwrite it with the new value.",
+                        origin.url,
+                        cache_dir,
+                        download_info.url,
+                    )
+        origin_path.write_text(download_info.to_json(), encoding="utf-8")
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/cli/__init__.py
new file mode 100644
index 0000000..5fcddf5
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cli/__init__.py
@@ -0,0 +1,3 @@
+"""Subpackage containing all of pip's command line interface related code"""
+
+# This file intentionally does not import submodules
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..6859feb
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc
new file mode 100644
index 0000000..097cd57
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-312.pyc
new file mode 100644
index 0000000..31babc4
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc
new file mode 100644
index 0000000..becd311
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-312.pyc
new file mode 100644
index 0000000..8747f50
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/index_command.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/index_command.cpython-312.pyc
new file mode 100644
index 0000000..58a9404
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/index_command.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main.cpython-312.pyc
new file mode 100644
index 0000000..2f4e216
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-312.pyc
new file mode 100644
index 0000000..25bf698
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/parser.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/parser.cpython-312.pyc
new file mode 100644
index 0000000..48e81ec
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/parser.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc
new file mode 100644
index 0000000..45b082a
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-312.pyc
new file mode 100644
index 0000000..832637f
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-312.pyc
new file mode 100644
index 0000000..5635009
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc
new file mode 100644
index 0000000..1513260
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/autocompletion.py b/venv/lib/python3.12/site-packages/pip/_internal/cli/autocompletion.py
new file mode 100644
index 0000000..4da020a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cli/autocompletion.py
@@ -0,0 +1,187 @@
+"""Logic that powers autocompletion installed by ``pip completion``."""
+
+from __future__ import annotations
+
+import optparse
+import os
+import sys
+from collections.abc import Iterable
+from itertools import chain
+from typing import Any
+
+from pip._internal.cli.main_parser import create_main_parser
+from pip._internal.commands import commands_dict, create_command
+
+
+def autocomplete() -> None:
+    """Entry Point for completion of main and subcommand options."""
+    # Don't complete if user hasn't sourced bash_completion file.
+    if "PIP_AUTO_COMPLETE" not in os.environ:
+        return
+    # Don't complete if autocompletion environment variables
+    # are not present
+    if not os.environ.get("COMP_WORDS") or not os.environ.get("COMP_CWORD"):
+        return
+    cwords = os.environ["COMP_WORDS"].split()[1:]
+    cword = int(os.environ["COMP_CWORD"])
+    try:
+        current = cwords[cword - 1]
+    except IndexError:
+        current = ""
+
+    parser = create_main_parser()
+    subcommands = list(commands_dict)
+    options = []
+
+    # subcommand
+    subcommand_name: str | None = None
+    for word in cwords:
+        if word in subcommands:
+            subcommand_name = word
+            break
+    # subcommand options
+    if subcommand_name is not None:
+        # special case: 'help' subcommand has no options
+        if subcommand_name == "help":
+            sys.exit(1)
+        # special case: list locally installed dists for show and uninstall
+        should_list_installed = not current.startswith("-") and subcommand_name in [
+            "show",
+            "uninstall",
+        ]
+        if should_list_installed:
+            # NOTE: this import is deferred until absolutely necessary as it's slow,
+            # and it's important that autocompletion is fast.
+            from pip._internal.metadata import get_default_environment
+
+            env = get_default_environment()
+            lc = current.lower()
+            installed = [
+                dist.canonical_name
+                for dist in env.iter_installed_distributions(local_only=True)
+                if dist.canonical_name.startswith(lc)
+                and dist.canonical_name not in cwords[1:]
+            ]
+            # if there are no dists installed, fall back to option completion
+            if installed:
+                for dist in installed:
+                    print(dist)
+                sys.exit(1)
+
+        should_list_installables = (
+            not current.startswith("-") and subcommand_name == "install"
+        )
+        if should_list_installables:
+            for path in auto_complete_paths(current, "path"):
+                print(path)
+            sys.exit(1)
+
+        subcommand = create_command(subcommand_name)
+
+        for opt in subcommand.parser.option_list_all:
+            if opt.help != optparse.SUPPRESS_HELP:
+                options += [
+                    (opt_str, opt.nargs) for opt_str in opt._long_opts + opt._short_opts
+                ]
+
+        # filter out previously specified options from available options
+        prev_opts = [x.split("=")[0] for x in cwords[1 : cword - 1]]
+        options = [(x, v) for (x, v) in options if x not in prev_opts]
+        # filter options by current input
+        options = [(k, v) for k, v in options if k.startswith(current)]
+        # get completion type given cwords and available subcommand options
+        completion_type = get_path_completion_type(
+            cwords,
+            cword,
+            subcommand.parser.option_list_all,
+        )
+        # get completion files and directories if ``completion_type`` is
+        # ````, ```` or ````
+        if completion_type:
+            paths = auto_complete_paths(current, completion_type)
+            options = [(path, 0) for path in paths]
+        for option in options:
+            opt_label = option[0]
+            # append '=' to options which require args
+            if option[1] and option[0][:2] == "--":
+                opt_label += "="
+            print(opt_label)
+
+        # Complete sub-commands (unless one is already given).
+        if not any(name in cwords for name in subcommand.handler_map()):
+            for handler_name in subcommand.handler_map():
+                if handler_name.startswith(current):
+                    print(handler_name)
+    else:
+        # show main parser options only when necessary
+
+        opts = [i.option_list for i in parser.option_groups]
+        opts.append(parser.option_list)
+        flattened_opts = chain.from_iterable(opts)
+        if current.startswith("-"):
+            for opt in flattened_opts:
+                if opt.help != optparse.SUPPRESS_HELP:
+                    subcommands += opt._long_opts + opt._short_opts
+        else:
+            # get completion type given cwords and all available options
+            completion_type = get_path_completion_type(cwords, cword, flattened_opts)
+            if completion_type:
+                subcommands = list(auto_complete_paths(current, completion_type))
+
+        print(" ".join([x for x in subcommands if x.startswith(current)]))
+    sys.exit(1)
+
+
+def get_path_completion_type(
+    cwords: list[str], cword: int, opts: Iterable[Any]
+) -> str | None:
+    """Get the type of path completion (``file``, ``dir``, ``path`` or None)
+
+    :param cwords: same as the environmental variable ``COMP_WORDS``
+    :param cword: same as the environmental variable ``COMP_CWORD``
+    :param opts: The available options to check
+    :return: path completion type (``file``, ``dir``, ``path`` or None)
+    """
+    if cword < 2 or not cwords[cword - 2].startswith("-"):
+        return None
+    for opt in opts:
+        if opt.help == optparse.SUPPRESS_HELP:
+            continue
+        for o in str(opt).split("/"):
+            if cwords[cword - 2].split("=")[0] == o:
+                if not opt.metavar or any(
+                    x in ("path", "file", "dir") for x in opt.metavar.split("/")
+                ):
+                    return opt.metavar
+    return None
+
+
+def auto_complete_paths(current: str, completion_type: str) -> Iterable[str]:
+    """If ``completion_type`` is ``file`` or ``path``, list all regular files
+    and directories starting with ``current``; otherwise only list directories
+    starting with ``current``.
+
+    :param current: The word to be completed
+    :param completion_type: path completion type(``file``, ``path`` or ``dir``)
+    :return: A generator of regular files and/or directories
+    """
+    directory, filename = os.path.split(current)
+    current_path = os.path.abspath(directory)
+    # Don't complete paths if they can't be accessed
+    if not os.access(current_path, os.R_OK):
+        return
+    filename = os.path.normcase(filename)
+    # list all files that start with ``filename``
+    file_list = (
+        x for x in os.listdir(current_path) if os.path.normcase(x).startswith(filename)
+    )
+    for f in file_list:
+        opt = os.path.join(current_path, f)
+        comp_file = os.path.normcase(os.path.join(directory, f))
+        # complete regular files when there is not ```` after option
+        # complete directories when there is ````, ```` or
+        # ````after option
+        if completion_type != "dir" and os.path.isfile(opt):
+            yield comp_file
+        elif os.path.isdir(opt):
+            yield os.path.join(comp_file, "")
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/base_command.py b/venv/lib/python3.12/site-packages/pip/_internal/cli/base_command.py
new file mode 100644
index 0000000..d74d192
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cli/base_command.py
@@ -0,0 +1,261 @@
+"""Base Command class, and related routines"""
+
+from __future__ import annotations
+
+import contextlib
+import logging
+import logging.config
+import optparse
+import os
+import sys
+import traceback
+from collections.abc import Callable, Iterator
+from optparse import Values
+
+from pip._vendor.rich import reconfigure
+from pip._vendor.rich import traceback as rich_traceback
+
+from pip._internal.cli import cmdoptions
+from pip._internal.cli.command_context import CommandContextMixIn
+from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
+from pip._internal.cli.status_codes import (
+    BROKEN_STDOUT,
+    ERROR,
+    PREVIOUS_BUILD_DIR_ERROR,
+    UNKNOWN_ERROR,
+    VIRTUALENV_NOT_FOUND,
+)
+from pip._internal.exceptions import (
+    BadCommand,
+    CommandError,
+    DiagnosticPipError,
+    InstallationError,
+    NetworkConnectionError,
+    PreviousBuildDirError,
+)
+from pip._internal.utils.filesystem import check_path_owner
+from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging
+from pip._internal.utils.misc import get_prog, normalize_path
+from pip._internal.utils.temp_dir import TempDirectoryTypeRegistry as TempDirRegistry
+from pip._internal.utils.temp_dir import global_tempdir_manager, tempdir_registry
+from pip._internal.utils.virtualenv import running_under_virtualenv
+
+__all__ = ["Command"]
+
+logger = logging.getLogger(__name__)
+
+
+class Command(CommandContextMixIn):
+    usage: str = ""
+    ignore_require_venv: bool = False
+
+    def __init__(self, name: str, summary: str, isolated: bool = False) -> None:
+        super().__init__()
+
+        self.name = name
+        self.summary = summary
+        self.parser = ConfigOptionParser(
+            usage=self.usage,
+            prog=f"{get_prog()} {name}",
+            formatter=UpdatingDefaultsHelpFormatter(),
+            add_help_option=False,
+            name=name,
+            description=self.__doc__,
+            isolated=isolated,
+        )
+
+        self.tempdir_registry: TempDirRegistry | None = None
+
+        # Commands should add options to this option group
+        optgroup_name = f"{self.name.capitalize()} Options"
+        self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name)
+
+        # Add the general options
+        gen_opts = cmdoptions.make_option_group(
+            cmdoptions.general_group,
+            self.parser,
+        )
+        self.parser.add_option_group(gen_opts)
+
+        self.add_options()
+
+    def add_options(self) -> None:
+        pass
+
+    @contextlib.contextmanager
+    def pip_version_check(self, options: Values, args: list[str]) -> Iterator[None]:
+        """
+        This is a no-op so that commands by default do not do the pip version
+        check.
+        """
+        # Make sure we do the pip version check if the index_group options
+        # are present.
+        assert not hasattr(options, "no_index")
+        yield
+
+    def run(self, options: Values, args: list[str]) -> int:
+        raise NotImplementedError
+
+    def _run_wrapper(self, level_number: int, options: Values, args: list[str]) -> int:
+        def _inner_run() -> int:
+            with self.pip_version_check(options, args):
+                return self.run(options, args)
+
+        if options.debug_mode:
+            rich_traceback.install(show_locals=True)
+            return _inner_run()
+
+        try:
+            status = _inner_run()
+            assert isinstance(status, int)
+            return status
+        except DiagnosticPipError as exc:
+            logger.error("%s", exc, extra={"rich": True})
+            logger.debug("Exception information:", exc_info=True)
+
+            return ERROR
+        except PreviousBuildDirError as exc:
+            logger.critical(str(exc))
+            logger.debug("Exception information:", exc_info=True)
+
+            return PREVIOUS_BUILD_DIR_ERROR
+        except (
+            InstallationError,
+            BadCommand,
+            NetworkConnectionError,
+        ) as exc:
+            logger.critical(str(exc))
+            logger.debug("Exception information:", exc_info=True)
+
+            return ERROR
+        except CommandError as exc:
+            logger.critical("%s", exc)
+            logger.debug("Exception information:", exc_info=True)
+
+            return ERROR
+        except BrokenStdoutLoggingError:
+            # stdout is broken; write to stderr directly. Use os.write, not
+            # sys.stderr.write, so a full pipe buffer returns EPIPE instead
+            # of deadlocking (Windows anonymous pipes are ~4KB).
+            try:
+                os.write(2, b"ERROR: Pipe to stdout was broken\n")
+                if level_number <= logging.DEBUG:
+                    encoding = getattr(sys.stderr, "encoding", None) or "utf-8"
+                    os.write(
+                        2, traceback.format_exc().encode(encoding, "backslashreplace")
+                    )
+            except OSError:
+                pass
+
+            # redirect stdout to os.devnull so that the exit cleanup doesn't
+            # produce "BrokenPipeError" when exiting.
+            devnull = os.open(os.devnull, os.O_WRONLY)
+            try:
+                os.dup2(devnull, 1)
+            finally:
+                os.close(devnull)
+
+            return BROKEN_STDOUT
+        except KeyboardInterrupt:
+            logger.critical("Operation cancelled by user")
+            logger.debug("Exception information:", exc_info=True)
+
+            return ERROR
+        except BaseException:
+            logger.critical("Exception:", exc_info=True)
+
+            return UNKNOWN_ERROR
+
+    def parse_args(self, args: list[str]) -> tuple[Values, list[str]]:
+        # factored out for testability
+        return self.parser.parse_args(args)
+
+    def main(self, args: list[str]) -> int:
+        try:
+            with self.main_context():
+                return self._main(args)
+        finally:
+            logging.shutdown()
+
+    def _main(self, args: list[str]) -> int:
+        # We must initialize this before the tempdir manager, otherwise the
+        # configuration would not be accessible by the time we clean up the
+        # tempdir manager.
+        self.tempdir_registry = self.enter_context(tempdir_registry())
+        # Intentionally set as early as possible so globally-managed temporary
+        # directories are available to the rest of the code.
+        self.enter_context(global_tempdir_manager())
+
+        options, args = self.parse_args(args)
+
+        # Set verbosity so that it can be used elsewhere.
+        self.verbosity = options.verbose - options.quiet
+        if options.debug_mode:
+            self.verbosity = 2
+
+        if hasattr(options, "progress_bar") and options.progress_bar == "auto":
+            options.progress_bar = "on" if self.verbosity >= 0 else "off"
+
+        reconfigure(no_color=options.no_color)
+        level_number = setup_logging(
+            verbosity=self.verbosity,
+            no_color=options.no_color,
+            user_log_file=options.log,
+        )
+
+        always_enabled_features = set(options.features_enabled) & set(
+            cmdoptions.ALWAYS_ENABLED_FEATURES
+        )
+        if always_enabled_features:
+            logger.warning(
+                "The following features are always enabled: %s. ",
+                ", ".join(sorted(always_enabled_features)),
+            )
+
+        # Make sure that the --python argument isn't specified after the
+        # subcommand. We can tell, because if --python was specified,
+        # we should only reach this point if we're running in the created
+        # subprocess, which has the _PIP_RUNNING_IN_SUBPROCESS environment
+        # variable set.
+        if options.python and "_PIP_RUNNING_IN_SUBPROCESS" not in os.environ:
+            logger.critical(
+                "The --python option must be placed before the pip subcommand name"
+            )
+            sys.exit(ERROR)
+
+        # TODO: Try to get these passing down from the command?
+        #       without resorting to os.environ to hold these.
+        #       This also affects isolated builds and it should.
+
+        if options.no_input:
+            os.environ["PIP_NO_INPUT"] = "1"
+
+        if options.exists_action:
+            os.environ["PIP_EXISTS_ACTION"] = " ".join(options.exists_action)
+
+        if options.require_venv and not self.ignore_require_venv:
+            # If a venv is required check if it can really be found
+            if not running_under_virtualenv():
+                logger.critical("Could not find an activated virtualenv (required).")
+                sys.exit(VIRTUALENV_NOT_FOUND)
+
+        if options.cache_dir:
+            options.cache_dir = normalize_path(options.cache_dir)
+            if not check_path_owner(options.cache_dir):
+                logger.warning(
+                    "The directory '%s' or its parent directory is not owned "
+                    "or is not writable by the current user. The cache "
+                    "has been disabled. Check the permissions and owner of "
+                    "that directory. If executing pip with sudo, you should "
+                    "use sudo's -H flag.",
+                    options.cache_dir,
+                )
+                options.cache_dir = None
+
+        return self._run_wrapper(level_number, options, args)
+
+    def handler_map(self) -> dict[str, Callable[[Values, list[str]], None]]:
+        """
+        map of names to handler actions for commands with sub-actions
+        """
+        return {}
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py b/venv/lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py
new file mode 100644
index 0000000..2b99c79
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py
@@ -0,0 +1,1409 @@
+"""
+shared options and groups
+
+The principle here is to define options once, but *not* instantiate them
+globally. One reason being that options with action='append' can carry state
+between parses. pip parses general options twice internally, and shouldn't
+pass on state. To be consistent, all options will follow this design.
+"""
+
+# The following comment should be removed at some point in the future.
+# mypy: strict-optional=False
+from __future__ import annotations
+
+import logging
+import os
+import pathlib
+import re
+import textwrap
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from functools import partial
+from optparse import SUPPRESS_HELP, Option, OptionGroup, OptionParser, Values
+from textwrap import dedent
+from typing import Any
+
+from pip._vendor.packaging.utils import canonicalize_name
+
+from pip._internal.cli.parser import ConfigOptionParser
+from pip._internal.exceptions import CommandError
+from pip._internal.locations import USER_CACHE_DIR, get_src_prefix
+from pip._internal.models.format_control import FormatControl
+from pip._internal.models.index import PyPI
+from pip._internal.models.release_control import ReleaseControl
+from pip._internal.models.target_python import TargetPython
+from pip._internal.utils.datetime import parse_iso_datetime
+from pip._internal.utils.hashes import STRONG_HASHES
+from pip._internal.utils.misc import strtobool
+
+logger = logging.getLogger(__name__)
+
+
+def raise_option_error(parser: OptionParser, option: Option, msg: str) -> None:
+    """
+    Raise an option parsing error using parser.error().
+
+    Args:
+      parser: an OptionParser instance.
+      option: an Option instance.
+      msg: the error text.
+    """
+    msg = f"{option} error: {msg}"
+    msg = textwrap.fill(" ".join(msg.split()))
+    parser.error(msg)
+
+
+def make_option_group(group: dict[str, Any], parser: ConfigOptionParser) -> OptionGroup:
+    """
+    Return an OptionGroup object
+    group  -- assumed to be dict with 'name' and 'options' keys
+    parser -- an optparse Parser
+    """
+    option_group = OptionGroup(parser, group["name"])
+    for option in group["options"]:
+        option_group.add_option(option())
+    return option_group
+
+
+def check_only_deps_option_does_not_conflict(options: Values) -> None:
+    """Function for determining if --only-deps and other incompatible options are
+    specified.
+
+    :param options: The OptionParser options.
+    """
+    if not options.only_dependencies:
+        return
+    conflicts = []
+    if options.ignore_dependencies:
+        conflicts.append("'--no-deps'")
+    if "legacy-resolver" in options.deprecated_features_enabled:
+        conflicts.append("'--use-deprecated legacy-resolver'")
+    if options.requirements:
+        conflicts.append("'--requirement'")
+    if options.requirements_from_scripts:
+        conflicts.append("'--requirements-from-script'")
+    if options.dependency_groups:
+        conflicts.append("'--group'")
+    if conflicts:
+        if len(conflicts) > 1:
+            conflicts[-1] = "or " + conflicts[-1]
+        conflict_message = ", ".join(conflicts)
+        raise CommandError(
+            f"Cannot use '--only-dependencies' in combination with {conflict_message}. "
+            "If this is unexpected, please refer to the user guide:\n"
+            "\n"
+            "    https://pip.pypa.io/en/stable/user_guide/#installing-only-dependencies"
+        )
+
+
+def check_dist_restriction(options: Values, check_target: bool = False) -> None:
+    """Function for determining if custom platform options are allowed.
+
+    :param options: The OptionParser options.
+    :param check_target: Whether or not to check if --target is being used.
+    """
+    dist_restriction_set = any(
+        [
+            options.python_version,
+            options.platforms,
+            options.abis,
+            options.implementation,
+        ]
+    )
+
+    binary_only = FormatControl(set(), {":all:"})
+    sdist_dependencies_allowed = (
+        options.format_control != binary_only and not options.ignore_dependencies
+    )
+
+    # Installations or downloads using dist restrictions must not combine
+    # source distributions and dist-specific wheels, as they are not
+    # guaranteed to be locally compatible.
+    if dist_restriction_set and sdist_dependencies_allowed:
+        raise CommandError(
+            "When restricting platform and interpreter constraints using "
+            "--python-version, --platform, --abi, or --implementation, "
+            "either --no-deps must be set, or --only-binary=:all: must be "
+            "set and --no-binary must not be set (or must be set to "
+            ":none:)."
+        )
+
+    if check_target:
+        if not options.dry_run and dist_restriction_set and not options.target_dir:
+            raise CommandError(
+                "Can not use any platform or abi specific options unless "
+                "installing via '--target' or using '--dry-run'"
+            )
+
+    if dist_restriction_set:
+        # Lazy import to keep CLI startup fast
+        from pip._internal.utils import pylock as pylock_utils
+
+        for filename in options.requirements:
+            if pylock_utils.is_valid_pylock_filename(filename):
+                raise CommandError(
+                    "Platform and interpreter constraints using "
+                    "--python-version, --platform, --abi, or --implementation, "
+                    f"are not supported when selecting requirements from {filename!r}"
+                )
+
+
+def check_build_constraints(options: Values) -> None:
+    """Function for validating build constraints options.
+
+    :param options: The OptionParser options.
+    """
+    if hasattr(options, "build_constraints") and options.build_constraints:
+        if not options.build_isolation:
+            raise CommandError(
+                "--build-constraint cannot be used with --no-build-isolation."
+            )
+
+        # Import here to avoid circular imports
+        from pip._internal.network.session import PipSession
+        from pip._internal.req.req_file import get_file_content
+
+        # Eagerly check build constraints file contents
+        # is valid so that we don't fail in when trying
+        # to check constraints in isolated build process
+        with PipSession() as session:
+            for constraint_file in options.build_constraints:
+                get_file_content(constraint_file, session)
+
+
+def _path_option_check(option: Option, opt: str, value: str) -> str:
+    return os.path.expanduser(value)
+
+
+def _package_name_option_check(option: Option, opt: str, value: str) -> str:
+    return canonicalize_name(value)
+
+
+class PipOption(Option):
+    TYPES = Option.TYPES + ("path", "package_name")
+    TYPE_CHECKER = Option.TYPE_CHECKER.copy()
+    TYPE_CHECKER["package_name"] = _package_name_option_check
+    TYPE_CHECKER["path"] = _path_option_check
+
+
+###########
+# options #
+###########
+
+help_: Callable[..., Option] = partial(
+    Option,
+    "-h",
+    "--help",
+    dest="help",
+    action="help",
+    help="Show help.",
+)
+
+debug_mode: Callable[..., Option] = partial(
+    Option,
+    "--debug",
+    dest="debug_mode",
+    action="store_true",
+    default=False,
+    help=(
+        "Let unhandled exceptions propagate outside the main subroutine, "
+        "instead of logging them to stderr."
+    ),
+)
+
+isolated_mode: Callable[..., Option] = partial(
+    Option,
+    "--isolated",
+    dest="isolated_mode",
+    action="store_true",
+    default=False,
+    help=(
+        "Run pip in an isolated mode, ignoring environment variables and user "
+        "configuration."
+    ),
+)
+
+require_virtualenv: Callable[..., Option] = partial(
+    Option,
+    "--require-virtualenv",
+    "--require-venv",
+    dest="require_venv",
+    action="store_true",
+    default=False,
+    help=(
+        "Allow pip to only run in a virtual environment; exit with an error otherwise."
+    ),
+)
+
+override_externally_managed: Callable[..., Option] = partial(
+    Option,
+    "--break-system-packages",
+    dest="override_externally_managed",
+    action="store_true",
+    help="Allow pip to modify an EXTERNALLY-MANAGED Python installation",
+)
+
+python: Callable[..., Option] = partial(
+    Option,
+    "--python",
+    dest="python",
+    help="Run pip with the specified Python interpreter.",
+)
+
+verbose: Callable[..., Option] = partial(
+    Option,
+    "-v",
+    "--verbose",
+    dest="verbose",
+    action="count",
+    default=0,
+    help="Give more output. Option is additive, and can be used up to 3 times.",
+)
+
+no_color: Callable[..., Option] = partial(
+    Option,
+    "--no-color",
+    dest="no_color",
+    action="store_true",
+    default=False,
+    help="Suppress colored output.",
+)
+
+version: Callable[..., Option] = partial(
+    Option,
+    "-V",
+    "--version",
+    dest="version",
+    action="store_true",
+    help="Show version and exit.",
+)
+
+quiet: Callable[..., Option] = partial(
+    Option,
+    "-q",
+    "--quiet",
+    dest="quiet",
+    action="count",
+    default=0,
+    help=(
+        "Give less output. Option is additive, and can be used up to 3"
+        " times (corresponding to WARNING, ERROR, and CRITICAL logging"
+        " levels)."
+    ),
+)
+
+progress_bar: Callable[..., Option] = partial(
+    Option,
+    "--progress-bar",
+    dest="progress_bar",
+    type="choice",
+    choices=["auto", "on", "off", "raw"],
+    default="auto",
+    help=(
+        "Specify whether the progress bar should be used. In 'auto'"
+        " mode, --quiet will suppress all progress bars."
+        " [auto, on, off, raw] (default: auto)"
+    ),
+)
+
+log: Callable[..., Option] = partial(
+    PipOption,
+    "--log",
+    "--log-file",
+    "--local-log",
+    dest="log",
+    metavar="path",
+    type="path",
+    help="Path to a verbose appending log.",
+)
+
+no_input: Callable[..., Option] = partial(
+    Option,
+    # Don't ask for input
+    "--no-input",
+    dest="no_input",
+    action="store_true",
+    default=False,
+    help="Disable prompting for input.",
+)
+
+keyring_provider: Callable[..., Option] = partial(
+    Option,
+    "--keyring-provider",
+    dest="keyring_provider",
+    choices=["auto", "disabled", "import", "subprocess"],
+    default="auto",
+    help=(
+        "Enable the credential lookup via the keyring library if user input is allowed."
+        " Specify which mechanism to use [auto, disabled, import, subprocess]."
+        " (default: %default)"
+    ),
+)
+
+proxy: Callable[..., Option] = partial(
+    Option,
+    "--proxy",
+    dest="proxy",
+    type="str",
+    default=None,
+    help="Specify a proxy in the form scheme://[user:passwd@]proxy.server:port.",
+)
+
+no_proxy_env: Callable[..., Option] = partial(
+    Option,
+    "--no-proxy-env",
+    dest="no_proxy_env",
+    action="store_true",
+    default=False,
+    help="Do not read proxy configuration from environment variables.",
+)
+
+retries: Callable[..., Option] = partial(
+    Option,
+    "--retries",
+    dest="retries",
+    type="int",
+    default=5,
+    help="Maximum attempts to establish a new HTTP connection. (default: %default)",
+)
+
+resume_retries: Callable[..., Option] = partial(
+    Option,
+    "--resume-retries",
+    dest="resume_retries",
+    type="int",
+    default=5,
+    help="Maximum attempts to resume or restart an incomplete download. "
+    "(default: %default)",
+)
+
+timeout: Callable[..., Option] = partial(
+    Option,
+    "--timeout",
+    "--default-timeout",
+    metavar="sec",
+    dest="timeout",
+    type="float",
+    default=15,
+    help="Set the socket timeout (default %default seconds).",
+)
+
+
+def exists_action() -> Option:
+    return Option(
+        # Option when path already exist
+        "--exists-action",
+        dest="exists_action",
+        type="choice",
+        choices=["s", "i", "w", "b", "a"],
+        default=[],
+        action="append",
+        metavar="action",
+        help="Default action when a path already exists: "
+        "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.",
+    )
+
+
+cert: Callable[..., Option] = partial(
+    PipOption,
+    "--cert",
+    dest="cert",
+    type="path",
+    metavar="path",
+    help=(
+        "Path to PEM-encoded CA certificate bundle. "
+        "If provided, overrides the default. "
+        "See 'SSL Certificate Verification' in pip documentation "
+        "for more information."
+    ),
+)
+
+client_cert: Callable[..., Option] = partial(
+    PipOption,
+    "--client-cert",
+    dest="client_cert",
+    type="path",
+    default=None,
+    metavar="path",
+    help="Path to SSL client certificate, a single file containing the "
+    "private key and the certificate in PEM format.",
+)
+
+index_url: Callable[..., Option] = partial(
+    Option,
+    "-i",
+    "--index-url",
+    "--pypi-url",
+    dest="index_url",
+    metavar="URL",
+    default=PyPI.simple_url,
+    help="Base URL of the Python Package Index (default %default). "
+    "This should point to a repository compliant with PEP 503 "
+    "(the simple repository API) or a local directory laid out "
+    "in the same format.",
+)
+
+
+def extra_index_url() -> Option:
+    return Option(
+        "--extra-index-url",
+        dest="extra_index_urls",
+        metavar="URL",
+        action="append",
+        default=[],
+        help="Extra URLs of package indexes to use in addition to "
+        "--index-url. Should follow the same rules as "
+        "--index-url.",
+    )
+
+
+no_index: Callable[..., Option] = partial(
+    Option,
+    "--no-index",
+    dest="no_index",
+    action="store_true",
+    default=False,
+    help="Ignore package index (only looking at --find-links URLs instead).",
+)
+
+
+def find_links() -> Option:
+    return Option(
+        "-f",
+        "--find-links",
+        dest="find_links",
+        action="append",
+        default=[],
+        metavar="url",
+        help="If a URL or path to an html file, then parse for links to "
+        "archives such as sdist (.tar.gz) or wheel (.whl) files. "
+        "If a local path or file:// URL that's a directory, "
+        "then look for archives in the directory listing. "
+        "Links to VCS project URLs are not supported.",
+    )
+
+
+def _handle_uploaded_prior_to(
+    option: Option, opt: str, value: str, parser: OptionParser
+) -> None:
+    """
+    This is an optparse.Option callback for the --uploaded-prior-to option.
+
+    Accepts either an ISO 8601 datetime string (e.g., '2023-01-01T00:00:00Z')
+    or a strict subset of ISO 8601 durations: PnD where n is a number of days
+    (e.g., 'P7D' for 7 days ago).
+
+    Note: This option only works with indexes that provide upload-time metadata
+    as specified in the simple repository API:
+    https://packaging.python.org/en/latest/specifications/simple-repository-api/
+    """
+    if value is None:
+        return None
+
+    # Try ISO 8601 duration in PnD format. The leading 'P' disambiguates
+    # from absolute datetimes. Only whole days are supported; the format may
+    # be extended to more of the ISO 8601 duration syntax in the future if
+    # a real need is presented.
+    match = re.match(r"^P(\d+)D$", value, re.ASCII)
+    if match:
+        days = int(match.group(1))
+        parser.values.uploaded_prior_to = datetime.now(timezone.utc) - timedelta(
+            days=days
+        )
+        return
+
+    try:
+        uploaded_prior_to = parse_iso_datetime(value)
+        # Use local timezone if no offset is given in the ISO string.
+        if uploaded_prior_to.tzinfo is None:
+            uploaded_prior_to = uploaded_prior_to.astimezone()
+        parser.values.uploaded_prior_to = uploaded_prior_to
+    except ValueError as exc:
+        msg = (
+            f"invalid value: {value!r}: {exc}. "
+            f"Expected an ISO 8601 datetime string "
+            f"(e.g., '2023-01-01' or '2023-01-01T00:00:00Z') "
+            f"or a duration in days (e.g., 'P3D')"
+        )
+        raise_option_error(parser, option=option, msg=msg)
+
+
+def uploaded_prior_to() -> Option:
+    return Option(
+        "--uploaded-prior-to",
+        dest="uploaded_prior_to",
+        metavar="datetime_or_duration",
+        action="callback",
+        callback=_handle_uploaded_prior_to,
+        type="str",
+        help=(
+            "Only consider packages uploaded prior to the given value. "
+            "Accepts an ISO 8601 datetime (e.g., '2023-01-01T00:00:00Z', "
+            "uses local timezone if none specified) or a duration in days "
+            "(e.g., 'P3D' for packages uploaded at least 3 days ago). "
+            "Only effective when using indexes that provide "
+            "upload-time metadata."
+        ),
+    )
+
+
+def trusted_host() -> Option:
+    return Option(
+        "--trusted-host",
+        dest="trusted_hosts",
+        action="append",
+        metavar="HOSTNAME",
+        default=[],
+        help="Mark this host or host:port pair as trusted, even though it "
+        "does not have valid or any HTTPS.",
+    )
+
+
+def constraints() -> Option:
+    return Option(
+        "-c",
+        "--constraint",
+        dest="constraints",
+        action="append",
+        default=[],
+        metavar="file",
+        help="Constrain versions using the given constraints file. "
+        "This option can be used multiple times.",
+    )
+
+
+def build_constraints() -> Option:
+    return Option(
+        "--build-constraint",
+        dest="build_constraints",
+        action="append",
+        type="str",
+        default=[],
+        metavar="file",
+        help=(
+            "Constrain build dependencies using the given constraints file. "
+            "This option can be used multiple times."
+        ),
+    )
+
+
+def requirements() -> Option:
+    return Option(
+        "-r",
+        "--requirement",
+        dest="requirements",
+        action="append",
+        default=[],
+        metavar="file",
+        help=(
+            "Install from the given requirements file. "
+            "The file or URL can be in pip's requirements.txt format, "
+            "or pylock.toml format. pylock.toml support is experimental. "
+            "This option can be used multiple times."
+        ),
+    )
+
+
+def requirements_from_scripts() -> Option:
+    return Option(
+        "--requirements-from-script",
+        action="append",
+        default=[],
+        dest="requirements_from_scripts",
+        metavar="file",
+        help="Install dependencies of the given script file "
+        "as defined by PEP 723 inline metadata. ",
+    )
+
+
+def editable() -> Option:
+    return Option(
+        "-e",
+        "--editable",
+        dest="editables",
+        action="append",
+        default=[],
+        metavar="path/url",
+        help=(
+            "Install a project in editable mode (i.e. setuptools "
+            '"develop mode") from a local project path or a VCS url.'
+        ),
+    )
+
+
+def _handle_src(option: Option, opt_str: str, value: str, parser: OptionParser) -> None:
+    value = os.path.abspath(value)
+    setattr(parser.values, option.dest, value)
+
+
+src: Callable[..., Option] = partial(
+    PipOption,
+    "--src",
+    "--source",
+    "--source-dir",
+    "--source-directory",
+    dest="src_dir",
+    type="path",
+    metavar="dir",
+    default=get_src_prefix(),
+    action="callback",
+    callback=_handle_src,
+    help="Directory to check out editable projects into. "
+    'The default in a virtualenv is "/src". '
+    'The default for global installs is "/src".',
+)
+
+
+def _get_format_control(values: Values, option: Option) -> Any:
+    """Get a format_control object."""
+    return getattr(values, option.dest)
+
+
+def _handle_no_binary(
+    option: Option, opt_str: str, value: str, parser: OptionParser
+) -> None:
+    existing = _get_format_control(parser.values, option)
+    FormatControl.handle_mutual_excludes(
+        value,
+        existing.no_binary,
+        existing.only_binary,
+    )
+
+
+def _handle_only_binary(
+    option: Option, opt_str: str, value: str, parser: OptionParser
+) -> None:
+    existing = _get_format_control(parser.values, option)
+    FormatControl.handle_mutual_excludes(
+        value,
+        existing.only_binary,
+        existing.no_binary,
+    )
+
+
+def no_binary() -> Option:
+    format_control = FormatControl(set(), set())
+    return Option(
+        "--no-binary",
+        dest="format_control",
+        action="callback",
+        callback=_handle_no_binary,
+        type="str",
+        default=format_control,
+        help="Do not download binary packages. Cached binary packages may still "
+        "be used. Can be supplied multiple times, and each time adds to "
+        "the existing value. Accepts either ':all:' to disable all binary "
+        "packages, ':none:' to empty the set (notice the colons), or one "
+        "or more package names with commas between them (no colons). "
+        "Note that some packages are tricky to compile and may fail to "
+        "install when this option is used on them.",
+    )
+
+
+def only_binary() -> Option:
+    format_control = FormatControl(set(), set())
+    return Option(
+        "--only-binary",
+        dest="format_control",
+        action="callback",
+        callback=_handle_only_binary,
+        type="str",
+        default=format_control,
+        help="Do not use source packages. Can be supplied multiple times, and "
+        'each time adds to the existing value. Accepts either ":all:" to '
+        'disable all source packages, ":none:" to empty the set, or one '
+        "or more package names with commas between them. Packages "
+        "without binary distributions will fail to install when this "
+        "option is used on them.",
+    )
+
+
+def _get_release_control(values: Values, option: Option) -> Any:
+    """Get a release_control object."""
+    return getattr(values, option.dest)
+
+
+def _handle_all_releases(
+    option: Option, opt_str: str, value: str, parser: OptionParser
+) -> None:
+    existing = _get_release_control(parser.values, option)
+    existing.handle_mutual_excludes(
+        value,
+        existing.all_releases,
+        existing.only_final,
+        "all_releases",
+    )
+
+
+def _handle_only_final(
+    option: Option, opt_str: str, value: str, parser: OptionParser
+) -> None:
+    existing = _get_release_control(parser.values, option)
+    existing.handle_mutual_excludes(
+        value,
+        existing.only_final,
+        existing.all_releases,
+        "only_final",
+    )
+
+
+def all_releases() -> Option:
+    release_control = ReleaseControl(set(), set())
+    return Option(
+        "--all-releases",
+        dest="release_control",
+        action="callback",
+        callback=_handle_all_releases,
+        type="str",
+        default=release_control,
+        help="Allow all release types (including pre-releases) for a package. "
+        "Can be supplied multiple times, and each time adds to the existing "
+        'value. Accepts either ":all:" to allow pre-releases for all '
+        'packages, ":none:" to empty the set (notice the colons), or one or '
+        "more package names with commas between them (no colons). Cannot be "
+        "used with --pre.",
+    )
+
+
+def only_final() -> Option:
+    release_control = ReleaseControl(set(), set())
+    return Option(
+        "--only-final",
+        dest="release_control",
+        action="callback",
+        callback=_handle_only_final,
+        type="str",
+        default=release_control,
+        help="Only allow final releases (no pre-releases) for a package. Can be "
+        "supplied multiple times, and each time adds to the existing value. "
+        'Accepts either ":all:" to disable pre-releases for all packages, '
+        '":none:" to empty the set, or one or more package names with commas '
+        "between them. Cannot be used with --pre.",
+    )
+
+
+def check_release_control_exclusive(options: Values) -> None:
+    """
+    Raise an error if --pre is used with --all-releases or --only-final,
+    and transform --pre into --all-releases :all: if used alone.
+    """
+    if not hasattr(options, "pre") or not options.pre:
+        return
+
+    release_control = options.release_control
+    if release_control.all_releases or release_control.only_final:
+        raise CommandError("--pre cannot be used with --all-releases or --only-final.")
+
+    # Transform --pre into --all-releases :all:
+    release_control.all_releases.add(":all:")
+
+
+platforms: Callable[..., Option] = partial(
+    Option,
+    "--platform",
+    dest="platforms",
+    metavar="platform",
+    action="append",
+    default=None,
+    help=(
+        "Only use wheels compatible with . Defaults to the "
+        "platform of the running system. Use this option multiple times to "
+        "specify multiple platforms supported by the target interpreter."
+    ),
+)
+
+
+# This was made a separate function for unit-testing purposes.
+def _convert_python_version(value: str) -> tuple[tuple[int, ...], str | None]:
+    """
+    Convert a version string like "3", "37", or "3.7.3" into a tuple of ints.
+
+    :return: A 2-tuple (version_info, error_msg), where `error_msg` is
+        non-None if and only if there was a parsing error.
+    """
+    if not value:
+        # The empty string is the same as not providing a value.
+        return (None, None)
+
+    parts = value.split(".")
+    if len(parts) > 3:
+        return ((), "at most three version parts are allowed")
+
+    if len(parts) == 1:
+        # Then we are in the case of "3" or "37".
+        value = parts[0]
+        if len(value) > 1:
+            parts = [value[0], value[1:]]
+
+    try:
+        version_info = tuple(int(part) for part in parts)
+    except ValueError:
+        return ((), "each version part must be an integer")
+
+    return (version_info, None)
+
+
+def _handle_python_version(
+    option: Option, opt_str: str, value: str, parser: OptionParser
+) -> None:
+    """
+    Handle a provided --python-version value.
+    """
+    version_info, error_msg = _convert_python_version(value)
+    if error_msg is not None:
+        msg = f"invalid --python-version value: {value!r}: {error_msg}"
+        raise_option_error(parser, option=option, msg=msg)
+
+    parser.values.python_version = version_info
+
+
+python_version: Callable[..., Option] = partial(
+    Option,
+    "--python-version",
+    dest="python_version",
+    metavar="python_version",
+    action="callback",
+    callback=_handle_python_version,
+    type="str",
+    default=None,
+    help=dedent("""\
+    The Python interpreter version to use for wheel and "Requires-Python"
+    compatibility checks. Defaults to a version derived from the running
+    interpreter. The version can be specified using up to three dot-separated
+    integers (e.g. "3" for 3.0.0, "3.7" for 3.7.0, or "3.7.3"). A major-minor
+    version can also be given as a string without dots (e.g. "37" for 3.7.0).
+    """),
+)
+
+
+implementation: Callable[..., Option] = partial(
+    Option,
+    "--implementation",
+    dest="implementation",
+    metavar="implementation",
+    default=None,
+    help=(
+        "Only use wheels compatible with Python "
+        "implementation , e.g. 'pp', 'jy', 'cp', "
+        " or 'ip'. If not specified, then the current "
+        "interpreter implementation is used.  Use 'py' to force "
+        "implementation-agnostic wheels."
+    ),
+)
+
+
+abis: Callable[..., Option] = partial(
+    Option,
+    "--abi",
+    dest="abis",
+    metavar="abi",
+    action="append",
+    default=None,
+    help=(
+        "Only use wheels compatible with Python abi , e.g. 'pypy_41'. "
+        "If not specified, then the current interpreter abi tag is used. "
+        "Use this option multiple times to specify multiple abis supported "
+        "by the target interpreter. Generally you will need to specify "
+        "--implementation, --platform, and --python-version when using this "
+        "option."
+    ),
+)
+
+
+def add_target_python_options(cmd_opts: OptionGroup) -> None:
+    cmd_opts.add_option(platforms())
+    cmd_opts.add_option(python_version())
+    cmd_opts.add_option(implementation())
+    cmd_opts.add_option(abis())
+
+
+def make_target_python(options: Values) -> TargetPython:
+    target_python = TargetPython(
+        platforms=options.platforms,
+        py_version_info=options.python_version,
+        abis=options.abis,
+        implementation=options.implementation,
+    )
+
+    return target_python
+
+
+def prefer_binary() -> Option:
+    return Option(
+        "--prefer-binary",
+        dest="prefer_binary",
+        action="store_true",
+        default=False,
+        help=(
+            "Prefer binary packages over source packages, even if the "
+            "source packages are newer."
+        ),
+    )
+
+
+cache_dir: Callable[..., Option] = partial(
+    PipOption,
+    "--cache-dir",
+    dest="cache_dir",
+    default=USER_CACHE_DIR,
+    metavar="dir",
+    type="path",
+    help="Store the cache data in .",
+)
+
+
+def _handle_no_cache_dir(
+    option: Option, opt: str, value: str, parser: OptionParser
+) -> None:
+    """
+    Process a value provided for the --no-cache-dir option.
+
+    This is an optparse.Option callback for the --no-cache-dir option.
+    """
+    # The value argument will be None if --no-cache-dir is passed via the
+    # command-line, since the option doesn't accept arguments.  However,
+    # the value can be non-None if the option is triggered e.g. by an
+    # environment variable, like PIP_NO_CACHE_DIR=true.
+    if value is not None:
+        # Then parse the string value to get argument error-checking.
+        try:
+            strtobool(value)
+        except ValueError as exc:
+            raise_option_error(parser, option=option, msg=str(exc))
+
+    # Originally, setting PIP_NO_CACHE_DIR to a value that strtobool()
+    # converted to 0 (like "false" or "no") caused cache_dir to be disabled
+    # rather than enabled (logic would say the latter).  Thus, we disable
+    # the cache directory not just on values that parse to True, but (for
+    # backwards compatibility reasons) also on values that parse to False.
+    # In other words, always set it to False if the option is provided in
+    # some (valid) form.
+    parser.values.cache_dir = False
+
+
+no_cache: Callable[..., Option] = partial(
+    Option,
+    "--no-cache-dir",
+    dest="cache_dir",
+    action="callback",
+    callback=_handle_no_cache_dir,
+    help="Disable the cache.",
+)
+
+no_deps: Callable[..., Option] = partial(
+    Option,
+    "--no-deps",
+    "--no-dependencies",
+    dest="ignore_dependencies",
+    action="store_true",
+    default=False,
+    help="Don't install package dependencies.",
+)
+
+
+def _handle_refresh_package(
+    option: Option, opt_str: str, value: str, parser: OptionParser
+) -> None:
+    if value.startswith("-"):
+        raise CommandError("--refresh-package option requires 1 argument.")
+
+    existing: set[str] = getattr(parser.values, option.dest)
+
+    new = value.split(",")
+    while ":all:" in new:
+        existing.clear()
+        existing.add(":all:")
+        del new[: new.index(":all:") + 1]
+        if ":none:" not in new:
+            return
+
+    for name in new:
+        if name == ":none:":
+            existing.clear()
+        else:
+            existing.add(canonicalize_name(name))
+
+
+def refresh_package() -> Option:
+    return Option(
+        "--refresh-package",
+        dest="refresh_package",
+        action="callback",
+        callback=_handle_refresh_package,
+        type="str",
+        default=set(),
+        help="Refresh package index information for the given packages instead "
+        "of using cached responses. Accepts ':all:' to apply "
+        "to all packages, or a comma-separated list of package names.",
+    )
+
+
+only_deps: Callable[..., Option] = partial(
+    Option,
+    "--only-deps",
+    "--only-dependencies",
+    dest="only_dependencies",
+    action="store_true",
+    default=False,
+    help=(
+        "Take only the dependencies of the provided requirements into account, "
+        "not the requirements themselves. Cannot be used in combination with "
+        "--no-deps, --group, --requirement, or --requirements-from-script. "
+        "No user-supplied requirements will be handled, even if they were "
+        "dependencies of other user-supplied requirements."
+    ),
+)
+
+
+def _handle_dependency_group(
+    option: Option, opt: str, value: str, parser: OptionParser
+) -> None:
+    """
+    Process a value provided for the --group option.
+
+    Splits on the rightmost ":", and validates that the path (if present) ends
+    in `pyproject.toml`. Defaults the path to `pyproject.toml` when one is not given.
+
+    `:` cannot appear in dependency group names, so this is a safe and simple parse.
+
+    This is an optparse.Option callback for the dependency_groups option.
+    """
+    path, sep, groupname = value.rpartition(":")
+    if not sep:
+        path = "pyproject.toml"
+    else:
+        # check for 'pyproject.toml' filenames using pathlib
+        if pathlib.PurePath(path).name != "pyproject.toml":
+            msg = "group paths use 'pyproject.toml' filenames"
+            raise_option_error(parser, option=option, msg=msg)
+
+    parser.values.dependency_groups.append((path, groupname))
+
+
+dependency_groups: Callable[..., Option] = partial(
+    Option,
+    "--group",
+    dest="dependency_groups",
+    default=[],
+    type=str,
+    action="callback",
+    callback=_handle_dependency_group,
+    metavar="[path:]group",
+    help='Install a named dependency-group from a "pyproject.toml" file. '
+    'If a path is given, the name of the file must be "pyproject.toml". '
+    'Defaults to using "pyproject.toml" in the current directory.',
+)
+
+ignore_requires_python: Callable[..., Option] = partial(
+    Option,
+    "--ignore-requires-python",
+    dest="ignore_requires_python",
+    action="store_true",
+    help="Ignore the Requires-Python information.",
+)
+
+
+no_build_isolation: Callable[..., Option] = partial(
+    Option,
+    "--no-build-isolation",
+    dest="build_isolation",
+    action="store_false",
+    default=True,
+    help="Disable isolation when building a modern source distribution. "
+    "Build dependencies specified by PEP 518 must be already installed "
+    "if this option is used.",
+)
+
+check_build_deps: Callable[..., Option] = partial(
+    Option,
+    "--check-build-dependencies",
+    dest="check_build_deps",
+    action="store_true",
+    default=False,
+    help="Check the build dependencies.",
+)
+
+
+use_pep517: Any = partial(
+    Option,
+    "--use-pep517",
+    dest="use_pep517",
+    action="store_true",
+    default=True,
+    help=SUPPRESS_HELP,
+)
+
+
+def _handle_config_settings(
+    option: Option, opt_str: str, value: str, parser: OptionParser
+) -> None:
+    key, sep, val = value.partition("=")
+    if sep != "=":
+        parser.error(f"Arguments to {opt_str} must be of the form KEY=VAL")
+    dest = getattr(parser.values, option.dest)
+    if dest is None:
+        dest = {}
+        setattr(parser.values, option.dest, dest)
+    if key in dest:
+        if isinstance(dest[key], list):
+            dest[key].append(val)
+        else:
+            dest[key] = [dest[key], val]
+    else:
+        dest[key] = val
+
+
+config_settings: Callable[..., Option] = partial(
+    Option,
+    "-C",
+    "--config-settings",
+    dest="config_settings",
+    type=str,
+    action="callback",
+    callback=_handle_config_settings,
+    metavar="settings",
+    help="Configuration settings to be passed to the build backend. "
+    "Settings take the form KEY=VALUE. Use multiple --config-settings options "
+    "to pass multiple keys to the backend.",
+)
+
+no_clean: Callable[..., Option] = partial(
+    Option,
+    "--no-clean",
+    action="store_true",
+    default=False,
+    help="Don't clean up build directories.",
+)
+
+pre: Callable[..., Option] = partial(
+    Option,
+    "--pre",
+    action="store_true",
+    default=False,
+    help="Include pre-release and development versions. By default, "
+    "pip only finds stable versions.",
+)
+
+json: Callable[..., Option] = partial(
+    Option,
+    "--json",
+    action="store_true",
+    default=False,
+    help="Output data in a machine-readable JSON format.",
+)
+
+disable_pip_version_check: Callable[..., Option] = partial(
+    Option,
+    "--disable-pip-version-check",
+    dest="disable_pip_version_check",
+    action="store_true",
+    default=False,
+    help="Don't periodically check PyPI to determine whether a new version "
+    "of pip is available for download. Implied with --no-index.",
+)
+
+root_user_action: Callable[..., Option] = partial(
+    Option,
+    "--root-user-action",
+    dest="root_user_action",
+    default="warn",
+    choices=["warn", "ignore"],
+    help="Action if pip is run as a root user [warn, ignore] (default: warn)",
+)
+
+
+def _handle_merge_hash(
+    option: Option, opt_str: str, value: str, parser: OptionParser
+) -> None:
+    """Given a value spelled "algo:digest", append the digest to a list
+    pointed to in a dict by the algo name."""
+    if not parser.values.hashes:
+        parser.values.hashes = {}
+    try:
+        algo, digest = value.split(":", 1)
+    except ValueError:
+        parser.error(
+            f"Arguments to {opt_str} must be a hash name "
+            "followed by a value, like --hash=sha256:"
+            "abcde..."
+        )
+    if algo not in STRONG_HASHES:
+        parser.error(
+            "Allowed hash algorithms for {} are {}.".format(
+                opt_str, ", ".join(STRONG_HASHES)
+            )
+        )
+    parser.values.hashes.setdefault(algo, []).append(digest)
+
+
+hash: Callable[..., Option] = partial(
+    Option,
+    "--hash",
+    # Hash values eventually end up in InstallRequirement.hashes due to
+    # __dict__ copying in process_line().
+    dest="hashes",
+    action="callback",
+    callback=_handle_merge_hash,
+    type="string",
+    help="Verify that the package's archive matches this "
+    "hash before installing. Example: --hash=sha256:abcdef...",
+)
+
+
+require_hashes: Callable[..., Option] = partial(
+    Option,
+    "--require-hashes",
+    dest="require_hashes",
+    action="store_true",
+    default=False,
+    help="Require a hash to check each requirement against, for "
+    "repeatable installs. This option is implied when any package in a "
+    "requirements file has a --hash option.",
+)
+
+
+no_require_hashes: Callable[..., Option] = partial(
+    Option,
+    "--no-require-hashes",
+    dest="no_require_hashes",
+    action="store_true",
+    default=False,
+    help="Do not automatically enable --require-hashes "
+    "when encountering a requirement with hashes.",
+)
+
+
+list_path: Callable[..., Option] = partial(
+    PipOption,
+    "--path",
+    dest="path",
+    type="path",
+    action="append",
+    help="Restrict to the specified installation path for listing "
+    "packages (can be used multiple times).",
+)
+
+
+def check_list_path_option(options: Values) -> None:
+    if options.path and (options.user or options.local):
+        raise CommandError("Cannot combine '--path' with '--user' or '--local'")
+
+
+list_exclude: Callable[..., Option] = partial(
+    PipOption,
+    "--exclude",
+    dest="excludes",
+    action="append",
+    metavar="package",
+    type="package_name",
+    help="Exclude specified package from the output",
+)
+
+
+no_python_version_warning: Callable[..., Option] = partial(
+    Option,
+    "--no-python-version-warning",
+    dest="no_python_version_warning",
+    action="store_true",
+    default=False,
+    help=SUPPRESS_HELP,  # No-op, a hold-over from the Python 2->3 transition.
+)
+
+
+# Features that are now always on. A warning is printed if they are used.
+ALWAYS_ENABLED_FEATURES = [
+    "truststore",  # always on since 24.2
+    "no-binary-enable-wheel-cache",  # always on since 23.1
+    "build-constraint",  # always on since 26.2
+]
+
+use_new_feature: Callable[..., Option] = partial(
+    Option,
+    "--use-feature",
+    dest="features_enabled",
+    metavar="feature",
+    action="append",
+    default=[],
+    choices=[
+        "fast-deps",
+        "inprocess-build-deps",
+        "venv-isolation",
+    ]
+    + ALWAYS_ENABLED_FEATURES,
+    help="Enable new functionality, that may be backward incompatible.",
+)
+
+use_deprecated_feature: Callable[..., Option] = partial(
+    Option,
+    "--use-deprecated",
+    dest="deprecated_features_enabled",
+    metavar="feature",
+    action="append",
+    default=[],
+    choices=[
+        "legacy-resolver",
+        "legacy-certs",
+    ],
+    help=("Enable deprecated functionality, that will be removed in the future."),
+)
+
+##########
+# groups #
+##########
+
+general_group: dict[str, Any] = {
+    "name": "General Options",
+    "options": [
+        help_,
+        debug_mode,
+        isolated_mode,
+        require_virtualenv,
+        python,
+        verbose,
+        version,
+        quiet,
+        log,
+        no_input,
+        keyring_provider,
+        proxy,
+        no_proxy_env,
+        retries,
+        timeout,
+        exists_action,
+        trusted_host,
+        cert,
+        client_cert,
+        cache_dir,
+        no_cache,
+        disable_pip_version_check,
+        no_color,
+        no_python_version_warning,
+        use_new_feature,
+        use_deprecated_feature,
+        resume_retries,
+    ],
+}
+
+index_group: dict[str, Any] = {
+    "name": "Package Index Options",
+    "options": [
+        index_url,
+        extra_index_url,
+        no_index,
+        refresh_package,
+        find_links,
+        uploaded_prior_to,
+    ],
+}
+
+package_selection_group: dict[str, Any] = {
+    "name": "Package Selection Options",
+    "options": [
+        pre,
+        all_releases,
+        only_final,
+        no_binary,
+        only_binary,
+        prefer_binary,
+    ],
+}
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/command_context.py b/venv/lib/python3.12/site-packages/pip/_internal/cli/command_context.py
new file mode 100644
index 0000000..9c167bd
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cli/command_context.py
@@ -0,0 +1,28 @@
+from collections.abc import Generator
+from contextlib import AbstractContextManager, ExitStack, contextmanager
+from typing import TypeVar
+
+_T = TypeVar("_T", covariant=True)
+
+
+class CommandContextMixIn:
+    def __init__(self) -> None:
+        super().__init__()
+        self._in_main_context = False
+        self._main_context = ExitStack()
+
+    @contextmanager
+    def main_context(self) -> Generator[None, None, None]:
+        assert not self._in_main_context
+
+        self._in_main_context = True
+        try:
+            with self._main_context:
+                yield
+        finally:
+            self._in_main_context = False
+
+    def enter_context(self, context_provider: AbstractContextManager[_T]) -> _T:
+        assert self._in_main_context
+
+        return self._main_context.enter_context(context_provider)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/index_command.py b/venv/lib/python3.12/site-packages/pip/_internal/cli/index_command.py
new file mode 100644
index 0000000..e1345ae
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cli/index_command.py
@@ -0,0 +1,217 @@
+"""
+Contains command classes which may interact with an index / the network.
+
+Unlike its sister module, req_command, this module still uses lazy imports
+so commands which don't always hit the network (e.g. list w/o --outdated or
+--uptodate) don't need waste time importing PipSession and friends.
+"""
+
+from __future__ import annotations
+
+import contextlib
+import logging
+import os
+from collections.abc import Iterator
+from functools import lru_cache
+from optparse import Values
+from typing import TYPE_CHECKING
+
+from pip._vendor import certifi
+
+from pip._internal.cli.base_command import Command
+from pip._internal.cli.command_context import CommandContextMixIn
+
+if TYPE_CHECKING:
+    from ssl import SSLContext
+
+    from pip._vendor.packaging.utils import NormalizedName
+
+    from pip._internal.network.session import PipSession
+    from pip._internal.self_outdated_check import UpgradePrompt
+
+logger = logging.getLogger(__name__)
+
+
+@lru_cache
+def _create_truststore_ssl_context() -> SSLContext | None:
+    try:
+        import ssl
+    except ImportError:
+        logger.warning("Disabling truststore since ssl support is missing")
+        return None
+
+    try:
+        from pip._vendor import truststore
+    except ImportError:
+        logger.warning("Disabling truststore because platform isn't supported")
+        return None
+
+    ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+    ctx.load_verify_locations(certifi.where())
+    return ctx
+
+
+class SessionCommandMixin(CommandContextMixIn):
+    """
+    A class mixin for command classes needing _build_session().
+    """
+
+    def __init__(self) -> None:
+        super().__init__()
+        self._session: PipSession | None = None
+
+    @classmethod
+    def _get_index_urls(cls, options: Values) -> list[str] | None:
+        """Return a list of index urls from user-provided options."""
+        index_urls = []
+        if not getattr(options, "no_index", False):
+            url = getattr(options, "index_url", None)
+            if url:
+                index_urls.append(url)
+        urls = getattr(options, "extra_index_urls", None)
+        if urls:
+            index_urls.extend(urls)
+        # Return None rather than an empty list
+        return index_urls or None
+
+    def get_default_session(self, options: Values) -> PipSession:
+        """Get a default-managed session."""
+        if self._session is None:
+            self._session = self.enter_context(self._build_session(options))
+            # there's no type annotation on requests.Session, so it's
+            # automatically ContextManager[Any] and self._session becomes Any,
+            # then https://github.com/python/mypy/issues/7696 kicks in
+            assert self._session is not None
+        return self._session
+
+    def _build_session(
+        self,
+        options: Values,
+        retries: int | None = None,
+        timeout: int | None = None,
+    ) -> PipSession:
+        from pip._internal.network.session import PipSession
+
+        cache_dir = options.cache_dir
+        assert not cache_dir or os.path.isabs(cache_dir)
+
+        if "legacy-certs" not in options.deprecated_features_enabled:
+            ssl_context = _create_truststore_ssl_context()
+        else:
+            ssl_context = None
+
+        session = PipSession(
+            cache=os.path.join(cache_dir, "http-v2") if cache_dir else None,
+            retries=retries if retries is not None else options.retries,
+            resume_retries=options.resume_retries,
+            trusted_hosts=options.trusted_hosts,
+            index_urls=self._get_index_urls(options),
+            ssl_context=ssl_context,
+            refresh_package=getattr(options, "refresh_package", set()),
+        )
+
+        # Handle custom ca-bundles from the user
+        if options.cert:
+            session.verify = options.cert
+
+        # Handle SSL client certificate
+        if options.client_cert:
+            session.cert = options.client_cert
+
+        # Handle timeouts
+        if options.timeout or timeout:
+            session.timeout = timeout if timeout is not None else options.timeout
+
+        # An explicit --proxy (including "" to disable proxying) or --no-proxy-env
+        # makes the session ignore environment proxies; unset --proxy is None.
+        if options.proxy is not None or options.no_proxy_env:
+            if options.proxy:
+                session.proxies = {
+                    "http": options.proxy,
+                    "https": options.proxy,
+                }
+            session.trust_env = False
+            # Keep "" so an empty --proxy reaches build subprocesses.
+            session.pip_proxy = options.proxy
+            session.pip_no_proxy_env = options.no_proxy_env
+
+        # Determine if we can prompt the user for authentication or not
+        session.auth.prompting = not options.no_input
+        session.auth.keyring_provider = options.keyring_provider
+
+        return session
+
+
+def _pip_self_version_check_fetch(
+    session: PipSession, options: Values
+) -> UpgradePrompt | None:
+    from pip._internal.self_outdated_check import pip_self_version_check_fetch
+
+    return pip_self_version_check_fetch(session, options)
+
+
+def _pip_self_version_check_emit(upgrade_prompt: UpgradePrompt | None) -> None:
+    from pip._internal.self_outdated_check import pip_self_version_check_emit
+
+    pip_self_version_check_emit(upgrade_prompt)
+
+
+class IndexGroupCommand(Command, SessionCommandMixin):
+    """
+    Abstract base class for commands with the index_group options.
+
+    This also corresponds to the commands that permit the pip version check.
+    """
+
+    def should_exclude_prerelease(
+        self, options: Values, package_name: NormalizedName
+    ) -> bool:
+        """
+        Determine if pre-releases should be excluded for a package.
+        """
+        # Check per-package release control settings
+        if options.release_control:
+            allow_prereleases = options.release_control.allows_prereleases(package_name)
+            if allow_prereleases is True:
+                return False  # Include pre-releases
+            elif allow_prereleases is False:
+                return True  # Exclude pre-releases
+
+        # No specific setting: exclude prereleases by default
+        return True
+
+    @contextlib.contextmanager
+    def pip_version_check(self, options: Values, args: list[str]) -> Iterator[None]:
+        """
+        Do the pip version check if not disabled.
+
+        This overrides the default behavior of not doing the check.
+        """
+        # Make sure the index_group options are present.
+        assert hasattr(options, "no_index")
+
+        if options.disable_pip_version_check or options.no_index:
+            yield
+            return
+
+        upgrade_prompt: UpgradePrompt | None = None
+        try:
+            session = self._build_session(
+                options,
+                retries=0,
+                timeout=min(5, options.timeout),
+            )
+            with session:
+                upgrade_prompt = _pip_self_version_check_fetch(session, options)
+        except Exception:
+            logger.warning("There was an error checking the latest version of pip.")
+            logger.debug("See below for error", exc_info=True)
+
+        try:
+            yield
+        finally:
+            try:
+                _pip_self_version_check_emit(upgrade_prompt)
+            except Exception:
+                logger.warning("There was an error checking the latest version of pip.")
+                logger.debug("See below for error", exc_info=True)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/main.py b/venv/lib/python3.12/site-packages/pip/_internal/cli/main.py
new file mode 100644
index 0000000..86ea4d5
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cli/main.py
@@ -0,0 +1,86 @@
+"""Primary application entrypoint."""
+
+from __future__ import annotations
+
+import locale
+import logging
+import os
+import sys
+import warnings
+
+logger = logging.getLogger(__name__)
+
+
+# Do not import and use main() directly! Using it directly is actively
+# discouraged by pip's maintainers. The name, location and behavior of
+# this function is subject to change, so calling it directly is not
+# portable across different pip versions.
+
+# In addition, running pip in-process is unsupported and unsafe. This is
+# elaborated in detail at
+# https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program.
+# That document also provides suggestions that should work for nearly
+# all users that are considering importing and using main() directly.
+
+# However, we know that certain users will still want to invoke pip
+# in-process. If you understand and accept the implications of using pip
+# in an unsupported manner, the best approach is to use runpy to avoid
+# depending on the exact location of this entry point.
+
+# The following example shows how to use runpy to invoke pip in that
+# case:
+#
+#     sys.argv = ["pip", your, args, here]
+#     runpy.run_module("pip", run_name="__main__")
+#
+# Note that this will exit the process after running, unlike a direct
+# call to main. As it is not safe to do any processing after calling
+# main, this should not be an issue in practice.
+
+
+def main(args: list[str] | None = None) -> int:
+    # NOTE: Lazy imports to speed up import of this module,
+    # which is imported from the pip console script. This doesn't
+    # speed up normal pip execution, but might be important in the future
+    # if we use ``multiprocessing`` module,
+    # which imports __main__ for each spawned subprocess.
+    from pip._internal.cli.autocompletion import autocomplete
+    from pip._internal.cli.main_parser import parse_command
+    from pip._internal.commands import create_command
+    from pip._internal.exceptions import PipError
+    from pip._internal.utils import deprecation
+
+    if args is None:
+        args = sys.argv[1:]
+
+    # Suppress the pkg_resources deprecation warning
+    # Note - we use a module of .*pkg_resources to cover
+    # the normal case (pip._vendor.pkg_resources) and the
+    # devendored case (a bare pkg_resources)
+    warnings.filterwarnings(
+        action="ignore", category=DeprecationWarning, module=".*pkg_resources"
+    )
+
+    # Configure our deprecation warnings to be sent through loggers
+    deprecation.install_warning_logger()
+
+    autocomplete()
+
+    try:
+        cmd_name, cmd_args = parse_command(args)
+    except PipError as exc:
+        sys.stderr.write(f"ERROR: {exc}")
+        sys.stderr.write(os.linesep)
+        sys.exit(1)
+
+    # Needed for locale encoding detection to work
+    # in pip._internal.utils.encoding.auto_decode
+    # TODO: Re-evaluate whether this is still needed once pip drops Python 3.10.
+    try:
+        locale.setlocale(locale.LC_ALL, "")
+    except locale.Error as e:
+        # setlocale can apparently crash if locale are uninitialized
+        logger.debug("Ignoring error %s when setting locale", e)
+    command = create_command(cmd_name, isolated=("--isolated" in cmd_args))
+
+    return command.main(cmd_args)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/main_parser.py b/venv/lib/python3.12/site-packages/pip/_internal/cli/main_parser.py
new file mode 100644
index 0000000..903c891
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cli/main_parser.py
@@ -0,0 +1,135 @@
+"""A single place for constructing and exposing the main parser"""
+
+from __future__ import annotations
+
+import os
+import subprocess
+import sys
+
+from pip._vendor.rich.markup import escape
+
+from pip._internal.cli import cmdoptions
+from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
+from pip._internal.commands import commands_dict, get_similar_commands
+from pip._internal.exceptions import CommandError
+from pip._internal.utils.misc import get_pip_version, get_prog, get_runnable_pip
+
+__all__ = ["create_main_parser", "parse_command"]
+
+
+def create_main_parser() -> ConfigOptionParser:
+    """Creates and returns the main parser for pip's CLI"""
+
+    parser = ConfigOptionParser(
+        usage="\n%prog  [options]",
+        add_help_option=False,
+        formatter=UpdatingDefaultsHelpFormatter(),
+        name="global",
+        prog=get_prog(),
+    )
+    parser.disable_interspersed_args()
+
+    parser.version = get_pip_version()
+
+    # add the general options
+    gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser)
+    parser.add_option_group(gen_opts)
+
+    # so the help formatter knows
+    parser.main = True  # type: ignore
+
+    # create command listing for description
+    description = [""] + [
+        f"[optparse.longargs]{name:27}[/] {escape(command_info.summary)}"
+        for name, command_info in commands_dict.items()
+    ]
+    parser.description = "\n".join(description)
+
+    return parser
+
+
+def identify_python_interpreter(python: str) -> str | None:
+    # If the named file exists, use it.
+    # If it's a directory, assume it's a virtual environment and
+    # look for the environment's Python executable.
+    if os.path.exists(python):
+        if os.path.isdir(python):
+            # bin/python for Unix, Scripts/python.exe for Windows
+            # Try both in case of odd cases like cygwin.
+            for exe in ("bin/python", "Scripts/python.exe"):
+                py = os.path.join(python, exe)
+                if os.path.exists(py):
+                    return py
+        else:
+            return python
+
+    # Could not find the interpreter specified
+    return None
+
+
+def parse_command(args: list[str]) -> tuple[str, list[str]]:
+    parser = create_main_parser()
+
+    # Note: parser calls disable_interspersed_args(), so the result of this
+    # call is to split the initial args into the general options before the
+    # subcommand and everything else.
+    # For example:
+    #  args: ['--timeout=5', 'install', '--user', 'INITools']
+    #  general_options: ['--timeout==5']
+    #  args_else: ['install', '--user', 'INITools']
+    general_options, args_else = parser.parse_args(args)
+
+    # --python
+    if general_options.python and "_PIP_RUNNING_IN_SUBPROCESS" not in os.environ:
+        # Re-invoke pip using the specified Python interpreter
+        interpreter = identify_python_interpreter(general_options.python)
+        if interpreter is None:
+            raise CommandError(
+                f"Could not locate Python interpreter {general_options.python}"
+            )
+
+        pip_cmd = [
+            interpreter,
+            get_runnable_pip(),
+        ]
+        pip_cmd.extend(args)
+
+        # Set a flag so the child doesn't re-invoke itself, causing
+        # an infinite loop.
+        os.environ["_PIP_RUNNING_IN_SUBPROCESS"] = "1"
+        returncode = 0
+        try:
+            proc = subprocess.run(pip_cmd)
+            returncode = proc.returncode
+        except (subprocess.SubprocessError, OSError) as exc:
+            raise CommandError(f"Failed to run pip under {interpreter}: {exc}")
+        sys.exit(returncode)
+
+    # --version
+    if general_options.version:
+        sys.stdout.write(parser.version)
+        sys.stdout.write(os.linesep)
+        sys.exit()
+
+    # pip || pip help -> print_help()
+    if not args_else or (args_else[0] == "help" and len(args_else) == 1):
+        parser.print_help()
+        sys.exit()
+
+    # the subcommand name
+    cmd_name = args_else[0]
+
+    if cmd_name not in commands_dict:
+        guess = get_similar_commands(cmd_name)
+
+        msg = [f'unknown command "{cmd_name}"']
+        if guess:
+            msg.append(f'maybe you meant "{guess}"')
+
+        raise CommandError(" - ".join(msg))
+
+    # all the args without the subcommand
+    cmd_args = args[:]
+    cmd_args.remove(cmd_name)
+
+    return cmd_name, cmd_args
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/parser.py b/venv/lib/python3.12/site-packages/pip/_internal/cli/parser.py
new file mode 100644
index 0000000..4df249a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cli/parser.py
@@ -0,0 +1,366 @@
+"""Base option parser setup"""
+
+from __future__ import annotations
+
+import logging
+import optparse
+import os
+import re
+import shutil
+import sys
+import textwrap
+from collections.abc import Generator
+from contextlib import suppress
+from typing import Any, NoReturn
+
+from pip._vendor.rich.markup import escape
+from pip._vendor.rich.theme import Theme
+
+from pip._internal.cli.status_codes import UNKNOWN_ERROR
+from pip._internal.configuration import Configuration, ConfigurationError
+from pip._internal.utils.logging import PipConsole
+from pip._internal.utils.misc import redact_auth_from_url, strtobool
+
+logger = logging.getLogger(__name__)
+
+
+class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
+    """A prettier/less verbose help formatter for optparse."""
+
+    styles = {
+        "optparse.shortargs": "green",
+        "optparse.longargs": "cyan",
+        "optparse.groups": "bold blue",
+        "optparse.metavar": "yellow",
+    }
+    highlights = {
+        r"\s(-{1}[\w]+[\w-]*)": "shortargs",  # highlight -letter as short args
+        r"\s(-{2}[\w]+[\w-]*)": "longargs",  # highlight --words as long args
+    }
+
+    def __init__(self, *args: Any, **kwargs: Any) -> None:
+        # help position must be aligned with __init__.parseopts.description
+        kwargs["max_help_position"] = 30
+        kwargs["indent_increment"] = 1
+        kwargs["width"] = shutil.get_terminal_size()[0] - 2
+        super().__init__(*args, **kwargs)
+
+    def format_option_strings(self, option: optparse.Option) -> str:
+        """Return a comma-separated list of option strings and metavars."""
+        opts = []
+
+        if option._short_opts:
+            opts.append(f"[optparse.shortargs]{option._short_opts[0]}[/]")
+        if option._long_opts:
+            opts.append(f"[optparse.longargs]{option._long_opts[0]}[/]")
+        if len(opts) > 1:
+            opts.insert(1, ", ")
+
+        if option.takes_value():
+            assert option.dest is not None
+            metavar = option.metavar or option.dest.lower()
+            opts.append(f" [optparse.metavar]<{escape(metavar.lower())}>[/]")
+
+        return "".join(opts)
+
+    def format_option(self, option: optparse.Option) -> str:
+        """Overridden method with Rich support."""
+        # fmt: off
+        result = []
+        opts = self.option_strings[option]
+        opt_width = self.help_position - self.current_indent - 2
+        # Remove the rich style tags before calculating width during
+        # text wrap calculations. Also store the length removed to adjust
+        # the padding in the else branch.
+        stripped = re.sub(r"(\[[a-z.]+\])|(\[\/\])", "", opts)
+        style_tag_length = len(opts) - len(stripped)
+        if len(stripped) > opt_width:
+            opts = "%*s%s\n" % (self.current_indent, "", opts)  # noqa: UP031
+            indent_first = self.help_position
+        else:                       # start help on same line as opts
+            opts = "%*s%-*s  " % (self.current_indent, "",      # noqa: UP031
+                                  opt_width + style_tag_length, opts)
+            indent_first = 0
+        result.append(opts)
+        if option.help:
+            help_text = self.expand_default(option)
+            help_lines = textwrap.wrap(help_text, self.help_width)
+            result.append("%*s%s\n" % (indent_first, "", help_lines[0]))  # noqa: UP031
+            result.extend(["%*s%s\n" % (self.help_position, "", line)     # noqa: UP031
+                           for line in help_lines[1:]])
+        elif opts[-1] != "\n":
+            result.append("\n")
+        return "".join(result)
+        # fmt: on
+
+    def format_heading(self, heading: str) -> str:
+        if heading == "Options":
+            return ""
+        return "[optparse.groups]" + escape(heading) + ":[/]\n"
+
+    def format_usage(self, usage: str) -> str:
+        """
+        Ensure there is only one newline between usage and the first heading
+        if there is no description.
+        """
+        contents = self.indent_lines(textwrap.dedent(usage), "  ")
+        msg = f"\n[optparse.groups]Usage:[/] {escape(contents)}\n"
+        return msg
+
+    def format_description(self, description: str | None) -> str:
+        # leave full control over description to us
+        if description:
+            if hasattr(self.parser, "main"):
+                label = "[optparse.groups]Commands:[/]"
+            else:
+                label = "[optparse.groups]Description:[/]"
+
+            # some doc strings have initial newlines, some don't
+            description = description.lstrip("\n")
+            # some doc strings have final newlines and spaces, some don't
+            description = description.rstrip()
+            # dedent, then reindent
+            description = self.indent_lines(textwrap.dedent(description), "  ")
+            description = f"{label}\n{description}\n"
+            return description
+        else:
+            return ""
+
+    def format_epilog(self, epilog: str | None) -> str:
+        # leave full control over epilog to us
+        if epilog:
+            return escape(epilog)
+        else:
+            return ""
+
+    def expand_default(self, option: optparse.Option) -> str:
+        """Overridden HelpFormatter.expand_default() which colorizes flags."""
+        help = escape(super().expand_default(option))
+        for regex, style in self.highlights.items():
+            help = re.sub(regex, rf"[optparse.{style}] \1[/]", help)
+        return help
+
+    def indent_lines(self, text: str, indent: str) -> str:
+        new_lines = [indent + line for line in text.split("\n")]
+        return "\n".join(new_lines)
+
+
+class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter):
+    """Custom help formatter for use in ConfigOptionParser.
+
+    This is updates the defaults before expanding them, allowing
+    them to show up correctly in the help listing.
+
+    Also redact auth from url type options
+    """
+
+    def expand_default(self, option: optparse.Option) -> str:
+        default_values = None
+        if self.parser is not None:
+            assert isinstance(self.parser, ConfigOptionParser)
+            self.parser._update_defaults(self.parser.defaults)
+            assert option.dest is not None
+            default_values = self.parser.defaults.get(option.dest)
+        help_text = super().expand_default(option)
+
+        if default_values and option.metavar == "URL":
+            if isinstance(default_values, str):
+                default_values = [default_values]
+
+            # If its not a list, we should abort and just return the help text
+            if not isinstance(default_values, list):
+                default_values = []
+
+            for val in default_values:
+                help_text = help_text.replace(val, redact_auth_from_url(val))
+
+        return help_text
+
+
+class CustomOptionParser(optparse.OptionParser):
+    def insert_option_group(
+        self, idx: int, *args: Any, **kwargs: Any
+    ) -> optparse.OptionGroup:
+        """Insert an OptionGroup at a given position."""
+        group = self.add_option_group(*args, **kwargs)
+
+        self.option_groups.pop()
+        self.option_groups.insert(idx, group)
+
+        return group
+
+    @property
+    def option_list_all(self) -> list[optparse.Option]:
+        """Get a list of all options, including those in option groups."""
+        res = self.option_list[:]
+        for i in self.option_groups:
+            res.extend(i.option_list)
+
+        return res
+
+
+class ConfigOptionParser(CustomOptionParser):
+    """Custom option parser which updates its defaults by checking the
+    configuration files and environmental variables"""
+
+    def __init__(
+        self,
+        *args: Any,
+        name: str,
+        isolated: bool = False,
+        **kwargs: Any,
+    ) -> None:
+        self.name = name
+        self.config = Configuration(isolated)
+
+        assert self.name
+        super().__init__(*args, **kwargs)
+
+    def check_default(self, option: optparse.Option, key: str, val: Any) -> Any:
+        try:
+            return option.check_value(key, val)
+        except optparse.OptionValueError as exc:
+            print(f"An error occurred during configuration: {exc}")
+            sys.exit(3)
+
+    def _get_ordered_configuration_items(
+        self,
+    ) -> Generator[tuple[str, Any], None, None]:
+        # Configuration gives keys in an unordered manner. Order them.
+        override_order = ["global", self.name, ":env:"]
+
+        # Pool the options into different groups
+        # Use a dict because we need to implement the fallthrough logic after PR 12201
+        # was merged which removed the fallthrough logic for options
+        section_items_dict: dict[str, dict[str, Any]] = {
+            name: {} for name in override_order
+        }
+
+        for _, value in self.config.items():
+            for section_key, val in value.items():
+
+                section, key = section_key.split(".", 1)
+                if section in override_order:
+                    section_items_dict[section][key] = val
+
+        # Now that we a dict of items per section, convert to list of tuples
+        # Make sure we completely remove empty values again
+        section_items = {
+            name: [(k, v) for k, v in section_items_dict[name].items() if v]
+            for name in override_order
+        }
+
+        # Yield each group in their override order
+        for section in override_order:
+            yield from section_items[section]
+
+    def _update_defaults(self, defaults: dict[str, Any]) -> dict[str, Any]:
+        """Updates the given defaults with values from the config files and
+        the environ. Does a little special handling for certain types of
+        options (lists)."""
+
+        # Accumulate complex default state.
+        self.values = optparse.Values(self.defaults)
+        late_eval = set()
+        # Then set the options with those values
+        for key, val in self._get_ordered_configuration_items():
+            # '--' because configuration supports only long names
+            option = self.get_option("--" + key)
+
+            # Ignore options not present in this parser. E.g. non-globals put
+            # in [global] by users that want them to apply to all applicable
+            # commands.
+            if option is None:
+                continue
+
+            assert option.dest is not None
+
+            if option.action in ("store_true", "store_false"):
+                try:
+                    val = strtobool(val)
+                except ValueError:
+                    self.error(
+                        f"{val} is not a valid value for {key} option, "
+                        "please specify a boolean value like yes/no, "
+                        "true/false or 1/0 instead."
+                    )
+            elif option.action == "count":
+                with suppress(ValueError):
+                    val = strtobool(val)
+                with suppress(ValueError):
+                    val = int(val)
+                if not isinstance(val, int) or val < 0:
+                    self.error(
+                        f"{val} is not a valid value for {key} option, "
+                        "please instead specify either a non-negative integer "
+                        "or a boolean value like yes/no or false/true "
+                        "which is equivalent to 1/0."
+                    )
+            elif option.action == "append":
+                val = val.split()
+                val = [self.check_default(option, key, v) for v in val]
+            elif option.action == "callback":
+                assert option.callback is not None
+                late_eval.add(option.dest)
+                opt_str = option.get_opt_string()
+                val = option.convert_value(opt_str, val)
+                # From take_action
+                args = option.callback_args or ()
+                kwargs = option.callback_kwargs or {}
+                option.callback(option, opt_str, val, self, *args, **kwargs)
+            else:
+                val = self.check_default(option, key, val)
+
+            defaults[option.dest] = val
+
+        for key in late_eval:
+            defaults[key] = getattr(self.values, key)
+        self.values = None
+        return defaults
+
+    def get_default_values(self) -> optparse.Values:
+        """Overriding to make updating the defaults after instantiation of
+        the option parser possible, _update_defaults() does the dirty work."""
+        if not self.process_default_values:
+            # Old, pre-Optik 1.5 behaviour.
+            return optparse.Values(self.defaults)
+
+        # Load the configuration, or error out in case of an error
+        try:
+            self.config.load()
+        except ConfigurationError as err:
+            self.exit(UNKNOWN_ERROR, str(err))
+
+        defaults = self._update_defaults(self.defaults.copy())  # ours
+        for option in self._get_all_options():
+            assert option.dest is not None
+            default = defaults.get(option.dest)
+            if isinstance(default, str):
+                opt_str = option.get_opt_string()
+                defaults[option.dest] = option.check_value(opt_str, default)
+        return optparse.Values(defaults)
+
+    def error(self, msg: str) -> NoReturn:
+        self.print_usage(sys.stderr)
+        self.exit(UNKNOWN_ERROR, f"{msg}\n")
+
+    def _create_console_for_help(self, file: Any = None) -> PipConsole:
+        # This is unfortunate but necessary since arguments may have not been
+        # parsed yet at this point, so detect --no-color manually.
+        no_color = (
+            "--no-color" in sys.argv
+            or bool(strtobool(os.environ.get("PIP_NO_COLOR", "no") or "no"))
+            or "NO_COLOR" in os.environ
+        )
+        return PipConsole(
+            theme=Theme(PrettyHelpFormatter.styles), no_color=no_color, file=file
+        )
+
+    def print_usage(self, file: Any = None) -> None:
+        if self.usage:
+            console = self._create_console_for_help(file)
+            console.print(self.get_usage(), highlight=False)
+
+    def print_help(self, file: Any = None) -> None:
+        console = self._create_console_for_help(file)
+        console.print(self.format_help().rstrip(), highlight=False)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/progress_bars.py b/venv/lib/python3.12/site-packages/pip/_internal/cli/progress_bars.py
new file mode 100644
index 0000000..f276c7e
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cli/progress_bars.py
@@ -0,0 +1,153 @@
+from __future__ import annotations
+
+import functools
+import sys
+from collections.abc import Callable, Generator, Iterable, Iterator
+from typing import TYPE_CHECKING, Literal, TypeVar
+
+from pip._vendor.rich.progress import (
+    BarColumn,
+    DownloadColumn,
+    FileSizeColumn,
+    MofNCompleteColumn,
+    Progress,
+    ProgressColumn,
+    SpinnerColumn,
+    TextColumn,
+    TimeElapsedColumn,
+    TimeRemainingColumn,
+    TransferSpeedColumn,
+)
+
+from pip._internal.cli.spinners import RateLimiter
+from pip._internal.utils.logging import get_console, get_indentation
+
+if TYPE_CHECKING:
+    from pip._internal.req.req_install import InstallRequirement
+
+T = TypeVar("T")
+ProgressRenderer = Callable[[Iterable[T]], Iterator[T]]
+BarType = Literal["on", "off", "raw"]
+
+
+def _rich_download_progress_bar(
+    iterable: Iterable[bytes],
+    *,
+    bar_type: BarType,
+    size: int | None,
+    initial_progress: int | None = None,
+) -> Generator[bytes, None, None]:
+    assert bar_type == "on", "This should only be used in the default mode."
+
+    if not size:
+        total = float("inf")
+        columns: tuple[ProgressColumn, ...] = (
+            TextColumn("[progress.description]{task.description}"),
+            SpinnerColumn("line", speed=1.5),
+            FileSizeColumn(),
+            TransferSpeedColumn(),
+            TimeElapsedColumn(),
+        )
+    else:
+        total = size
+        columns = (
+            TextColumn("[progress.description]{task.description}"),
+            BarColumn(),
+            DownloadColumn(),
+            TransferSpeedColumn(),
+            TextColumn("{task.fields[time_description]}"),
+            TimeRemainingColumn(elapsed_when_finished=True),
+        )
+
+    progress = Progress(*columns, refresh_per_second=5)
+    task_id = progress.add_task(
+        " " * (get_indentation() + 2), total=total, time_description="eta"
+    )
+    if initial_progress is not None:
+        progress.update(task_id, advance=initial_progress)
+    with progress:
+        for chunk in iterable:
+            yield chunk
+            progress.update(task_id, advance=len(chunk))
+        progress.update(task_id, time_description="")
+
+
+def _rich_install_progress_bar(
+    iterable: Iterable[InstallRequirement], *, total: int
+) -> Iterator[InstallRequirement]:
+    columns = (
+        TextColumn("{task.fields[indent]}"),
+        BarColumn(),
+        MofNCompleteColumn(),
+        TextColumn("{task.description}"),
+    )
+    console = get_console()
+
+    bar = Progress(*columns, refresh_per_second=6, console=console, transient=True)
+    # Hiding the progress bar at initialization forces a refresh cycle to occur
+    # until the bar appears, avoiding very short flashes.
+    task = bar.add_task("", total=total, indent=" " * get_indentation(), visible=False)
+    with bar:
+        for req in iterable:
+            bar.update(task, description=rf"\[{req.name}]", visible=True)
+            yield req
+            bar.advance(task)
+
+
+def _raw_progress_bar(
+    iterable: Iterable[bytes],
+    *,
+    size: int | None,
+    initial_progress: int | None = None,
+) -> Generator[bytes, None, None]:
+    def write_progress(current: int, total: int) -> None:
+        sys.stdout.write(f"Progress {current} of {total}\n")
+        sys.stdout.flush()
+
+    current = initial_progress or 0
+    total = size or 0
+    rate_limiter = RateLimiter(0.25)
+
+    write_progress(current, total)
+    for chunk in iterable:
+        current += len(chunk)
+        if rate_limiter.ready() or current == total:
+            write_progress(current, total)
+            rate_limiter.reset()
+        yield chunk
+
+
+def get_download_progress_renderer(
+    *, bar_type: BarType, size: int | None = None, initial_progress: int | None = None
+) -> ProgressRenderer[bytes]:
+    """Get an object that can be used to render the download progress.
+
+    Returns a callable, that takes an iterable to "wrap".
+    """
+    if bar_type == "on":
+        return functools.partial(
+            _rich_download_progress_bar,
+            bar_type=bar_type,
+            size=size,
+            initial_progress=initial_progress,
+        )
+    elif bar_type == "raw":
+        return functools.partial(
+            _raw_progress_bar,
+            size=size,
+            initial_progress=initial_progress,
+        )
+    else:
+        return iter  # no-op, when passed an iterator
+
+
+def get_install_progress_renderer(
+    *, bar_type: BarType, total: int
+) -> ProgressRenderer[InstallRequirement]:
+    """Get an object that can be used to render the install progress.
+    Returns a callable, that takes an iterable to "wrap".
+    """
+    if bar_type == "on":
+        return functools.partial(_rich_install_progress_bar, total=total)
+    else:
+        return iter
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/req_command.py b/venv/lib/python3.12/site-packages/pip/_internal/cli/req_command.py
new file mode 100644
index 0000000..f58a970
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cli/req_command.py
@@ -0,0 +1,504 @@
+"""Contains the RequirementCommand base class.
+
+This class is in a separate module so the commands that do not always
+need PackageFinder capability don't unnecessarily import the
+PackageFinder machinery and all its vendored dependencies, etc.
+"""
+
+from __future__ import annotations
+
+import logging
+import os
+from collections.abc import Callable
+from functools import partial
+from optparse import Values
+from typing import Any, TypeVar
+
+from pip._internal.build_env import (
+    BuildEnvironmentInstaller,
+    BuildIsolationMode,
+    InprocessBuildEnvironmentInstaller,
+    SubprocessBuildEnvironmentInstaller,
+)
+from pip._internal.cache import WheelCache
+from pip._internal.cli import cmdoptions
+from pip._internal.cli.cmdoptions import make_target_python
+from pip._internal.cli.index_command import IndexGroupCommand
+from pip._internal.cli.index_command import SessionCommandMixin as SessionCommandMixin
+from pip._internal.exceptions import (
+    CommandError,
+    PreviousBuildDirError,
+    UnsupportedPythonVersion,
+)
+from pip._internal.index.collector import LinkCollector
+from pip._internal.index.package_finder import PackageFinder
+from pip._internal.models.selection_prefs import SelectionPreferences
+from pip._internal.models.target_python import TargetPython
+from pip._internal.network.session import PipSession
+from pip._internal.operations.build.build_tracker import BuildTracker
+from pip._internal.operations.prepare import RequirementPreparer
+from pip._internal.req.constructors import (
+    install_req_from_editable,
+    install_req_from_line,
+    install_req_from_parsed_requirement,
+    install_req_from_pylock_package,
+    install_req_from_req_string,
+)
+from pip._internal.req.pep723 import PEP723Exception, pep723_metadata
+from pip._internal.req.req_dependency_group import parse_dependency_groups
+from pip._internal.req.req_file import parse_requirements
+from pip._internal.req.req_install import InstallRequirement
+from pip._internal.resolution.base import BaseResolver
+from pip._internal.utils.packaging import check_requires_python
+from pip._internal.utils.pylock import (
+    is_valid_pylock_filename,
+    select_from_pylock_path_or_url,
+)
+from pip._internal.utils.temp_dir import (
+    TempDirectory,
+    TempDirectoryTypeRegistry,
+    tempdir_kinds,
+)
+
+logger = logging.getLogger(__name__)
+
+
+def should_ignore_regular_constraints() -> bool:
+    """
+    Whether this process should ignore the regular constraints it inherits.
+
+    The parent pip sets ``_PIP_IN_BUILD_IGNORE_CONSTRAINTS`` whenever it starts
+    an isolated build in a subprocess, so that regular constraints the build
+    inherits (such as via ``PIP_CONSTRAINT`` or config files) do not affect it.
+    Only build constraints apply there.
+    """
+
+    return os.environ.get("_PIP_IN_BUILD_IGNORE_CONSTRAINTS") == "1"
+
+
+KEEPABLE_TEMPDIR_TYPES = [
+    tempdir_kinds.BUILD_ENV,
+    tempdir_kinds.EPHEM_WHEEL_CACHE,
+    tempdir_kinds.REQ_BUILD,
+]
+
+
+_CommandT = TypeVar("_CommandT", bound="RequirementCommand")
+
+
+def with_cleanup(
+    func: Callable[[_CommandT, Values, list[str]], int],
+) -> Callable[[_CommandT, Values, list[str]], int]:
+    """Decorator for common logic related to managing temporary
+    directories.
+    """
+
+    def configure_tempdir_registry(registry: TempDirectoryTypeRegistry) -> None:
+        for t in KEEPABLE_TEMPDIR_TYPES:
+            registry.set_delete(t, False)
+
+    def wrapper(self: _CommandT, options: Values, args: list[str]) -> int:
+        assert self.tempdir_registry is not None
+        if options.no_clean:
+            configure_tempdir_registry(self.tempdir_registry)
+
+        try:
+            return func(self, options, args)
+        except PreviousBuildDirError:
+            # This kind of conflict can occur when the user passes an explicit
+            # build directory with a pre-existing folder. In that case we do
+            # not want to accidentally remove it.
+            configure_tempdir_registry(self.tempdir_registry)
+            raise
+
+    return wrapper
+
+
+def parse_constraint_files(
+    constraint_files: list[str],
+    finder: PackageFinder,
+    options: Values,
+    session: PipSession,
+) -> list[InstallRequirement]:
+    requirements = []
+    for filename in constraint_files:
+        for parsed_req in parse_requirements(
+            filename,
+            constraint=True,
+            finder=finder,
+            options=options,
+            session=session,
+        ):
+            req_to_add = install_req_from_parsed_requirement(
+                parsed_req,
+                isolated=options.isolated_mode,
+                user_supplied=False,
+            )
+            requirements.append(req_to_add)
+
+    return requirements
+
+
+class RequirementCommand(IndexGroupCommand):
+    def __init__(self, *args: Any, **kw: Any) -> None:
+        super().__init__(*args, **kw)
+
+        self.cmd_opts.add_option(cmdoptions.dependency_groups())
+        self.cmd_opts.add_option(cmdoptions.no_clean())
+
+    @staticmethod
+    def determine_resolver_variant(options: Values) -> str:
+        """Determines which resolver should be used, based on the given options."""
+        if "legacy-resolver" in options.deprecated_features_enabled:
+            return "legacy"
+
+        return "resolvelib"
+
+    @classmethod
+    def make_requirement_preparer(
+        cls,
+        temp_build_dir: TempDirectory,
+        options: Values,
+        build_tracker: BuildTracker,
+        session: PipSession,
+        finder: PackageFinder,
+        *,
+        allow_editables: bool,
+        use_user_site: bool,
+        download_dir: str | None = None,
+        verbosity: int = 0,
+    ) -> RequirementPreparer:
+        """
+        Create a RequirementPreparer instance for the given parameters.
+        """
+        temp_build_dir_path = temp_build_dir.path
+        assert temp_build_dir_path is not None
+        legacy_resolver = False
+
+        resolver_variant = cls.determine_resolver_variant(options)
+        if resolver_variant == "resolvelib":
+            lazy_wheel = "fast-deps" in options.features_enabled
+            if lazy_wheel:
+                logger.warning(
+                    "pip is using lazily downloaded wheels using HTTP "
+                    "range requests to obtain dependency information. "
+                    "This experimental feature is enabled through "
+                    "--use-feature=fast-deps and it is not ready for "
+                    "production."
+                )
+        else:
+            legacy_resolver = True
+            lazy_wheel = False
+            if "fast-deps" in options.features_enabled:
+                logger.warning(
+                    "fast-deps has no effect when used with the legacy resolver."
+                )
+
+        # Handle build constraints
+        build_constraints = getattr(options, "build_constraints", [])
+
+        env_installer: BuildEnvironmentInstaller
+        if "inprocess-build-deps" in options.features_enabled:
+            build_constraint_reqs = parse_constraint_files(
+                build_constraints, finder, options, session
+            )
+            env_installer = InprocessBuildEnvironmentInstaller(
+                finder=finder,
+                build_tracker=build_tracker,
+                build_constraints=build_constraint_reqs,
+                verbosity=verbosity,
+                wheel_cache=WheelCache(options.cache_dir),
+            )
+        else:
+            env_installer = SubprocessBuildEnvironmentInstaller(
+                finder,
+                build_constraints=build_constraints,
+            )
+
+        if not options.build_isolation:
+            build_isolation: BuildIsolationMode = "off"
+        elif "venv-isolation" in options.features_enabled:
+            build_isolation = "venv"
+            if "inprocess-build-deps" in options.features_enabled:
+                logger.warning(
+                    "Using --use-feature 'inprocess-build-deps' and 'venv-isolation'"
+                    " together is currently partially supported. If you encounter"
+                    " broken behaviour, use only one feature."
+                )
+        else:
+            build_isolation = "virtual"
+
+        return RequirementPreparer(
+            build_dir=temp_build_dir_path,
+            src_dir=options.src_dir,
+            download_dir=download_dir,
+            build_isolation=build_isolation,
+            build_isolation_installer=env_installer,
+            check_build_deps=options.check_build_deps,
+            build_tracker=build_tracker,
+            session=session,
+            progress_bar=options.progress_bar,
+            finder=finder,
+            require_hashes=options.require_hashes,
+            use_user_site=use_user_site,
+            lazy_wheel=lazy_wheel,
+            verbosity=verbosity,
+            legacy_resolver=legacy_resolver,
+            allow_editables=allow_editables,
+        )
+
+    @classmethod
+    def make_resolver(
+        cls,
+        preparer: RequirementPreparer,
+        finder: PackageFinder,
+        options: Values,
+        wheel_cache: WheelCache | None = None,
+        use_user_site: bool = False,
+        ignore_installed: bool = True,
+        ignore_requires_python: bool = False,
+        force_reinstall: bool = False,
+        upgrade_strategy: str = "to-satisfy-only",
+        py_version_info: tuple[int, ...] | None = None,
+    ) -> BaseResolver:
+        """
+        Create a Resolver instance for the given parameters.
+        """
+        make_install_req = partial(
+            install_req_from_req_string,
+            isolated=options.isolated_mode,
+        )
+        resolver_variant = cls.determine_resolver_variant(options)
+        # The long import name and duplicated invocation is needed to convince
+        # Mypy into correctly typechecking. Otherwise it would complain the
+        # "Resolver" class being redefined.
+        if resolver_variant == "resolvelib":
+            import pip._internal.resolution.resolvelib.resolver
+
+            return pip._internal.resolution.resolvelib.resolver.Resolver(
+                preparer=preparer,
+                finder=finder,
+                wheel_cache=wheel_cache,
+                make_install_req=make_install_req,
+                use_user_site=use_user_site,
+                ignore_dependencies=options.ignore_dependencies,
+                only_dependencies=options.only_dependencies,
+                ignore_installed=ignore_installed,
+                ignore_requires_python=ignore_requires_python,
+                force_reinstall=force_reinstall,
+                upgrade_strategy=upgrade_strategy,
+                py_version_info=py_version_info,
+            )
+        import pip._internal.resolution.legacy.resolver
+
+        return pip._internal.resolution.legacy.resolver.Resolver(
+            preparer=preparer,
+            finder=finder,
+            wheel_cache=wheel_cache,
+            make_install_req=make_install_req,
+            use_user_site=use_user_site,
+            ignore_dependencies=options.ignore_dependencies,
+            only_dependencies=options.only_dependencies,
+            ignore_installed=ignore_installed,
+            ignore_requires_python=ignore_requires_python,
+            force_reinstall=force_reinstall,
+            upgrade_strategy=upgrade_strategy,
+            py_version_info=py_version_info,
+        )
+
+    def get_requirements(
+        self,
+        args: list[str],
+        options: Values,
+        finder: PackageFinder,
+        session: PipSession,
+    ) -> list[InstallRequirement]:
+        """
+        Parse command-line arguments into the corresponding requirements.
+        """
+        requirements: list[InstallRequirement] = []
+
+        if should_ignore_regular_constraints():
+            # Inside an isolated build subprocess: apply the build constraints
+            # (forwarded via --build-constraint) instead of the inherited ones.
+            constraint_files = getattr(options, "build_constraints", [])
+        else:
+            constraint_files = options.constraints
+
+        constraints = parse_constraint_files(constraint_files, finder, options, session)
+        requirements.extend(constraints)
+
+        for req in args:
+            if not req.strip():
+                continue
+            req_to_add = install_req_from_line(
+                req,
+                comes_from=None,
+                isolated=options.isolated_mode,
+                user_supplied=True,
+                config_settings=getattr(options, "config_settings", None),
+            )
+            requirements.append(req_to_add)
+
+        if options.dependency_groups:
+            for req in parse_dependency_groups(options.dependency_groups):
+                req_to_add = install_req_from_req_string(
+                    req,
+                    isolated=options.isolated_mode,
+                    user_supplied=True,
+                )
+                requirements.append(req_to_add)
+
+        for req in options.editables:
+            req_to_add = install_req_from_editable(
+                req,
+                user_supplied=True,
+                isolated=options.isolated_mode,
+                config_settings=getattr(options, "config_settings", None),
+            )
+            requirements.append(req_to_add)
+
+        # NOTE: options.require_hashes may be set if --require-hashes is True
+        for filename in options.requirements:
+            if is_valid_pylock_filename(filename):
+                logger.warning(
+                    "Using pylock.toml as a requirements source "
+                    "is an experimental feature. "
+                    "It may be removed/changed in a future release "
+                    "without prior warning."
+                )
+                for package, package_dist in select_from_pylock_path_or_url(
+                    filename, session=session
+                ):
+                    req_to_add, locked_link = install_req_from_pylock_package(
+                        package,
+                        package_dist,
+                        filename,
+                        user_supplied=True,
+                    )
+                    requirements.append(req_to_add)
+                    if locked_link:
+                        finder.add_locked_link(package.name, locked_link)
+                continue
+            for parsed_req in parse_requirements(
+                filename, finder=finder, options=options, session=session
+            ):
+                req_to_add = install_req_from_parsed_requirement(
+                    parsed_req,
+                    isolated=options.isolated_mode,
+                    user_supplied=True,
+                    config_settings=(
+                        parsed_req.options.get("config_settings")
+                        if parsed_req.options
+                        else None
+                    ),
+                )
+                requirements.append(req_to_add)
+
+        if options.requirements_from_scripts:
+            if len(options.requirements_from_scripts) > 1:
+                raise CommandError("--requirements-from-script can only be given once")
+
+            script = options.requirements_from_scripts[0]
+            try:
+                script_metadata = pep723_metadata(script)
+            except PEP723Exception as exc:
+                raise CommandError(exc.msg)
+
+            script_requires_python = script_metadata.get("requires-python", "")
+
+            if script_requires_python and not options.ignore_requires_python:
+                target_python = make_target_python(options)
+
+                if not check_requires_python(
+                    requires_python=script_requires_python,
+                    version_info=target_python.py_version_info,
+                ):
+                    raise UnsupportedPythonVersion(
+                        f"Script {script!r} requires a different Python: "
+                        f"{target_python.py_version} not in {script_requires_python!r}"
+                    )
+
+            for req in script_metadata.get("dependencies", []):
+                req_to_add = install_req_from_req_string(
+                    req,
+                    isolated=options.isolated_mode,
+                    user_supplied=True,
+                )
+                requirements.append(req_to_add)
+
+        if options.require_hashes and options.no_require_hashes:
+            raise CommandError(
+                "--require-hashes and --no-require-hashes are mutually exclusive"
+            )
+
+        # If any requirement has hash options, enable hash checking for all
+        # requirements, unless this mechanism has been explicitly disabled
+        # with --no-require-hashes.
+        if not options.no_require_hashes and any(
+            req.has_hash_options for req in requirements
+        ):
+            options.require_hashes = True
+
+        if not (
+            args
+            or options.editables
+            or options.requirements
+            or options.dependency_groups
+            or options.requirements_from_scripts
+        ):
+            opts = {"name": self.name}
+            if options.find_links:
+                raise CommandError(
+                    "You must give at least one requirement to {name} "
+                    '(maybe you meant "pip {name} {links}"?)'.format(
+                        **dict(opts, links=" ".join(options.find_links))
+                    )
+                )
+            else:
+                raise CommandError(
+                    "You must give at least one requirement to {name} "
+                    '(see "pip help {name}")'.format(**opts)
+                )
+
+        return requirements
+
+    @staticmethod
+    def trace_basic_info(finder: PackageFinder) -> None:
+        """
+        Trace basic information about the provided objects.
+        """
+        # Display where finder is looking for packages
+        search_scope = finder.search_scope
+        locations = search_scope.get_formatted_locations()
+        if locations:
+            logger.info(locations)
+
+    def _build_package_finder(
+        self,
+        options: Values,
+        session: PipSession,
+        target_python: TargetPython | None = None,
+        ignore_requires_python: bool = False,
+    ) -> PackageFinder:
+        """
+        Create a package finder appropriate to this requirement command.
+
+        :param ignore_requires_python: Whether to ignore incompatible
+            "Requires-Python" values in links. Defaults to False.
+        """
+        link_collector = LinkCollector.create(session, options=options)
+        selection_prefs = SelectionPreferences(
+            allow_yanked=True,
+            format_control=options.format_control,
+            release_control=options.release_control,
+            prefer_binary=options.prefer_binary,
+            ignore_requires_python=ignore_requires_python,
+        )
+
+        return PackageFinder.create(
+            link_collector=link_collector,
+            selection_prefs=selection_prefs,
+            target_python=target_python,
+            uploaded_prior_to=options.uploaded_prior_to,
+        )
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/spinners.py b/venv/lib/python3.12/site-packages/pip/_internal/cli/spinners.py
new file mode 100644
index 0000000..58aad28
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cli/spinners.py
@@ -0,0 +1,235 @@
+from __future__ import annotations
+
+import contextlib
+import itertools
+import logging
+import sys
+import time
+from collections.abc import Generator
+from typing import IO, Final
+
+from pip._vendor.rich.console import (
+    Console,
+    ConsoleOptions,
+    RenderableType,
+    RenderResult,
+)
+from pip._vendor.rich.live import Live
+from pip._vendor.rich.measure import Measurement
+from pip._vendor.rich.text import Text
+
+from pip._internal.utils.compat import WINDOWS
+from pip._internal.utils.logging import get_console, get_indentation
+
+logger = logging.getLogger(__name__)
+
+SPINNER_CHARS: Final = r"-\|/"
+SPINS_PER_SECOND: Final = 8
+
+
+class SpinnerInterface:
+    def spin(self) -> None:
+        raise NotImplementedError()
+
+    def finish(self, final_status: str) -> None:
+        raise NotImplementedError()
+
+
+class InteractiveSpinner(SpinnerInterface):
+    def __init__(
+        self,
+        message: str,
+        file: IO[str] | None = None,
+        spin_chars: str = SPINNER_CHARS,
+        # Empirically, 8 updates/second looks nice
+        min_update_interval_seconds: float = 1 / SPINS_PER_SECOND,
+    ):
+        self._message = message
+        if file is None:
+            file = sys.stdout
+        self._file = file
+        self._rate_limiter = RateLimiter(min_update_interval_seconds)
+        self._finished = False
+
+        self._spin_cycle = itertools.cycle(spin_chars)
+
+        self._file.write(" " * get_indentation() + self._message + " ... ")
+        self._width = 0
+
+    def _write(self, status: str) -> None:
+        assert not self._finished
+        # Erase what we wrote before by backspacing to the beginning, writing
+        # spaces to overwrite the old text, and then backspacing again
+        backup = "\b" * self._width
+        self._file.write(backup + " " * self._width + backup)
+        # Now we have a blank slate to add our status
+        self._file.write(status)
+        self._width = len(status)
+        self._file.flush()
+        self._rate_limiter.reset()
+
+    def spin(self) -> None:
+        if self._finished:
+            return
+        if not self._rate_limiter.ready():
+            return
+        self._write(next(self._spin_cycle))
+
+    def finish(self, final_status: str) -> None:
+        if self._finished:
+            return
+        self._write(final_status)
+        self._file.write("\n")
+        self._file.flush()
+        self._finished = True
+
+
+# Used for dumb terminals, non-interactive installs (no tty), etc.
+# We still print updates occasionally (once every 60 seconds by default) to
+# act as a keep-alive for systems like Travis-CI that take lack-of-output as
+# an indication that a task has frozen.
+class NonInteractiveSpinner(SpinnerInterface):
+    def __init__(self, message: str, min_update_interval_seconds: float = 60.0) -> None:
+        self._message = message
+        self._finished = False
+        self._rate_limiter = RateLimiter(min_update_interval_seconds)
+        self._update("started")
+
+    def _update(self, status: str) -> None:
+        assert not self._finished
+        self._rate_limiter.reset()
+        logger.info("%s: %s", self._message, status)
+
+    def spin(self) -> None:
+        if self._finished:
+            return
+        if not self._rate_limiter.ready():
+            return
+        self._update("still running...")
+
+    def finish(self, final_status: str) -> None:
+        if self._finished:
+            return
+        self._update(f"finished with status '{final_status}'")
+        self._finished = True
+
+
+class RateLimiter:
+    def __init__(self, min_update_interval_seconds: float) -> None:
+        self._min_update_interval_seconds = min_update_interval_seconds
+        self._last_update: float = 0
+
+    def ready(self) -> bool:
+        now = time.time()
+        delta = now - self._last_update
+        return delta >= self._min_update_interval_seconds
+
+    def reset(self) -> None:
+        self._last_update = time.time()
+
+
+@contextlib.contextmanager
+def open_spinner(message: str) -> Generator[SpinnerInterface, None, None]:
+    # Interactive spinner goes directly to sys.stdout rather than being routed
+    # through the logging system, but it acts like it has level INFO,
+    # i.e. it's only displayed if we're at level INFO or better.
+    # Non-interactive spinner goes through the logging system, so it is always
+    # in sync with logging configuration.
+    if sys.stdout.isatty() and logger.getEffectiveLevel() <= logging.INFO:
+        spinner: SpinnerInterface = InteractiveSpinner(message)
+    else:
+        spinner = NonInteractiveSpinner(message)
+    try:
+        with hidden_cursor(sys.stdout):
+            yield spinner
+    except KeyboardInterrupt:
+        spinner.finish("canceled")
+        raise
+    except Exception:
+        spinner.finish("error")
+        raise
+    else:
+        spinner.finish("done")
+
+
+class _PipRichSpinner:
+    """
+    Custom rich spinner that matches the style of the legacy spinners.
+
+    (*) Updates will be handled in a background thread by a rich live panel
+        which will call render() automatically at the appropriate time.
+    """
+
+    def __init__(self, label: str) -> None:
+        self.label = label
+        self._spin_cycle = itertools.cycle(SPINNER_CHARS)
+        self._spinner_text = ""
+        self._finished = False
+        self._indent = get_indentation() * " "
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        yield self.render()
+
+    def __rich_measure__(
+        self, console: Console, options: ConsoleOptions
+    ) -> Measurement:
+        text = self.render()
+        return Measurement.get(console, options, text)
+
+    def render(self) -> RenderableType:
+        if not self._finished:
+            self._spinner_text = next(self._spin_cycle)
+
+        return Text.assemble(self._indent, self.label, " ... ", self._spinner_text)
+
+    def finish(self, status: str) -> None:
+        """Stop spinning and set a final status message."""
+        self._spinner_text = status
+        self._finished = True
+
+
+@contextlib.contextmanager
+def open_rich_spinner(label: str, console: Console | None = None) -> Generator[None]:
+    if not logger.isEnabledFor(logging.INFO):
+        # Don't show spinner if --quiet is given.
+        yield
+        return
+
+    console = console or get_console()
+    spinner = _PipRichSpinner(label)
+    with Live(spinner, refresh_per_second=SPINS_PER_SECOND, console=console):
+        try:
+            yield
+        except KeyboardInterrupt:
+            spinner.finish("canceled")
+            raise
+        except Exception:
+            spinner.finish("error")
+            raise
+        else:
+            spinner.finish("done")
+
+
+HIDE_CURSOR = "\x1b[?25l"
+SHOW_CURSOR = "\x1b[?25h"
+
+
+@contextlib.contextmanager
+def hidden_cursor(file: IO[str]) -> Generator[None, None, None]:
+    # The Windows terminal does not support the hide/show cursor ANSI codes,
+    # even via colorama. So don't even try.
+    if WINDOWS:
+        yield
+    # We don't want to clutter the output with control characters if we're
+    # writing to a file, or if the user is running with --quiet.
+    # See https://github.com/pypa/pip/issues/3418
+    elif not file.isatty() or logger.getEffectiveLevel() > logging.INFO:
+        yield
+    else:
+        file.write(HIDE_CURSOR)
+        try:
+            yield
+        finally:
+            file.write(SHOW_CURSOR)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/cli/status_codes.py b/venv/lib/python3.12/site-packages/pip/_internal/cli/status_codes.py
new file mode 100644
index 0000000..2b2ecab
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/cli/status_codes.py
@@ -0,0 +1,7 @@
+SUCCESS = 0
+ERROR = 1
+UNKNOWN_ERROR = 2
+VIRTUALENV_NOT_FOUND = 3
+PREVIOUS_BUILD_DIR_ERROR = 4
+NO_MATCHES_FOUND = 23
+BROKEN_STDOUT = 120
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/__init__.py
new file mode 100644
index 0000000..bedeca9
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/__init__.py
@@ -0,0 +1,139 @@
+"""
+Package containing all pip commands
+"""
+
+from __future__ import annotations
+
+import importlib
+from collections import namedtuple
+from typing import Any
+
+from pip._internal.cli.base_command import Command
+
+CommandInfo = namedtuple("CommandInfo", "module_path, class_name, summary")
+
+# This dictionary does a bunch of heavy lifting for help output:
+# - Enables avoiding additional (costly) imports for presenting `--help`.
+# - The ordering matters for help display.
+#
+# Even though the module path starts with the same "pip._internal.commands"
+# prefix, the full path makes testing easier (specifically when modifying
+# `commands_dict` in test setup / teardown).
+commands_dict: dict[str, CommandInfo] = {
+    "install": CommandInfo(
+        "pip._internal.commands.install",
+        "InstallCommand",
+        "Install packages.",
+    ),
+    "lock": CommandInfo(
+        "pip._internal.commands.lock",
+        "LockCommand",
+        "Generate a lock file.",
+    ),
+    "download": CommandInfo(
+        "pip._internal.commands.download",
+        "DownloadCommand",
+        "Download packages.",
+    ),
+    "uninstall": CommandInfo(
+        "pip._internal.commands.uninstall",
+        "UninstallCommand",
+        "Uninstall packages.",
+    ),
+    "freeze": CommandInfo(
+        "pip._internal.commands.freeze",
+        "FreezeCommand",
+        "Output installed packages in requirements format.",
+    ),
+    "inspect": CommandInfo(
+        "pip._internal.commands.inspect",
+        "InspectCommand",
+        "Inspect the python environment.",
+    ),
+    "list": CommandInfo(
+        "pip._internal.commands.list",
+        "ListCommand",
+        "List installed packages.",
+    ),
+    "show": CommandInfo(
+        "pip._internal.commands.show",
+        "ShowCommand",
+        "Show information about installed packages.",
+    ),
+    "check": CommandInfo(
+        "pip._internal.commands.check",
+        "CheckCommand",
+        "Verify installed packages have compatible dependencies.",
+    ),
+    "config": CommandInfo(
+        "pip._internal.commands.configuration",
+        "ConfigurationCommand",
+        "Manage local and global configuration.",
+    ),
+    "search": CommandInfo(
+        "pip._internal.commands.search",
+        "SearchCommand",
+        "Search PyPI for packages.",
+    ),
+    "cache": CommandInfo(
+        "pip._internal.commands.cache",
+        "CacheCommand",
+        "Inspect and manage pip's wheel cache.",
+    ),
+    "index": CommandInfo(
+        "pip._internal.commands.index",
+        "IndexCommand",
+        "Inspect information available from package indexes.",
+    ),
+    "wheel": CommandInfo(
+        "pip._internal.commands.wheel",
+        "WheelCommand",
+        "Build wheels from your requirements.",
+    ),
+    "hash": CommandInfo(
+        "pip._internal.commands.hash",
+        "HashCommand",
+        "Compute hashes of package archives.",
+    ),
+    "completion": CommandInfo(
+        "pip._internal.commands.completion",
+        "CompletionCommand",
+        "A helper command used for command completion.",
+    ),
+    "debug": CommandInfo(
+        "pip._internal.commands.debug",
+        "DebugCommand",
+        "Show information useful for debugging.",
+    ),
+    "help": CommandInfo(
+        "pip._internal.commands.help",
+        "HelpCommand",
+        "Show help for commands.",
+    ),
+}
+
+
+def create_command(name: str, **kwargs: Any) -> Command:
+    """
+    Create an instance of the Command class with the given name.
+    """
+    module_path, class_name, summary = commands_dict[name]
+    module = importlib.import_module(module_path)
+    command_class = getattr(module, class_name)
+    command = command_class(name=name, summary=summary, **kwargs)
+
+    return command
+
+
+def get_similar_commands(name: str) -> str | None:
+    """Command name auto-correct."""
+    from difflib import get_close_matches
+
+    name = name.lower()
+
+    close_commands = get_close_matches(name, commands_dict.keys())
+
+    if close_commands:
+        return close_commands[0]
+    else:
+        return None
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..a188a0a
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/cache.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/cache.cpython-312.pyc
new file mode 100644
index 0000000..1fda332
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/cache.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/check.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/check.cpython-312.pyc
new file mode 100644
index 0000000..af3c95e
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/check.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/completion.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/completion.cpython-312.pyc
new file mode 100644
index 0000000..77f8ffe
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/completion.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-312.pyc
new file mode 100644
index 0000000..9f8b129
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/debug.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/debug.cpython-312.pyc
new file mode 100644
index 0000000..952ba65
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/debug.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/download.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/download.cpython-312.pyc
new file mode 100644
index 0000000..68b820b
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/download.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-312.pyc
new file mode 100644
index 0000000..fa76fa8
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/hash.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/hash.cpython-312.pyc
new file mode 100644
index 0000000..6eee655
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/hash.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/help.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/help.cpython-312.pyc
new file mode 100644
index 0000000..c446ff3
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/help.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/index.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/index.cpython-312.pyc
new file mode 100644
index 0000000..9243740
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/index.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/inspect.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/inspect.cpython-312.pyc
new file mode 100644
index 0000000..79b4f06
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/inspect.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/install.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/install.cpython-312.pyc
new file mode 100644
index 0000000..f1300dd
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/install.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/list.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/list.cpython-312.pyc
new file mode 100644
index 0000000..3e4ef6d
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/list.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/lock.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/lock.cpython-312.pyc
new file mode 100644
index 0000000..03984d5
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/lock.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/search.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/search.cpython-312.pyc
new file mode 100644
index 0000000..c311658
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/search.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/show.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/show.cpython-312.pyc
new file mode 100644
index 0000000..965fd91
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/show.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-312.pyc
new file mode 100644
index 0000000..961a97d
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-312.pyc
new file mode 100644
index 0000000..7ec9731
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/cache.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/cache.py
new file mode 100644
index 0000000..232ace8
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/cache.py
@@ -0,0 +1,256 @@
+import os
+import textwrap
+from collections.abc import Callable
+from optparse import Values
+
+from pip._internal.cli.base_command import Command
+from pip._internal.cli.status_codes import ERROR, SUCCESS
+from pip._internal.exceptions import CommandError, PipError
+from pip._internal.utils import filesystem
+from pip._internal.utils.logging import getLogger
+from pip._internal.utils.misc import format_size
+
+logger = getLogger(__name__)
+
+
+class CacheCommand(Command):
+    """
+    Inspect and manage pip's wheel cache.
+
+    Subcommands:
+
+    - dir: Show the cache directory.
+    - info: Show information about the cache.
+    - list: List filenames of packages stored in the cache.
+    - remove: Remove one or more package from the cache.
+    - purge: Remove all items from the cache.
+
+    ```` can be a glob expression or a package name.
+    """
+
+    ignore_require_venv = True
+    usage = """
+        %prog dir
+        %prog info
+        %prog list [] [--format=[human, abspath]]
+        %prog remove 
+        %prog purge
+    """
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(
+            "--format",
+            action="store",
+            dest="list_format",
+            default="human",
+            choices=("human", "abspath"),
+            help="Select the output format among: human (default) or abspath",
+        )
+
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    def handler_map(self) -> dict[str, Callable[[Values, list[str]], None]]:
+        return {
+            "dir": self.get_cache_dir,
+            "info": self.get_cache_info,
+            "list": self.list_cache_items,
+            "remove": self.remove_cache_items,
+            "purge": self.purge_cache,
+        }
+
+    def run(self, options: Values, args: list[str]) -> int:
+        handler_map = self.handler_map()
+
+        if not options.cache_dir:
+            logger.error("pip cache commands can not function since cache is disabled.")
+            return ERROR
+
+        # Determine action
+        if not args or args[0] not in handler_map:
+            logger.error(
+                "Need an action (%s) to perform.",
+                ", ".join(sorted(handler_map)),
+            )
+            return ERROR
+
+        action = args[0]
+
+        # Error handling happens here, not in the action-handlers.
+        try:
+            handler_map[action](options, args[1:])
+        except PipError as e:
+            logger.error(e.args[0])
+            return ERROR
+
+        return SUCCESS
+
+    def get_cache_dir(self, options: Values, args: list[str]) -> None:
+        if args:
+            raise CommandError("Too many arguments")
+
+        logger.info(options.cache_dir)
+
+    def get_cache_info(self, options: Values, args: list[str]) -> None:
+        if args:
+            raise CommandError("Too many arguments")
+
+        num_http_files = len(self._find_http_files(options))
+        num_packages = len(self._find_wheels(options, "*"))
+
+        http_cache_location = self._cache_dir(options, "http-v2")
+        old_http_cache_location = self._cache_dir(options, "http")
+        wheels_cache_location = self._cache_dir(options, "wheels")
+        http_cache_size = filesystem.format_size(
+            filesystem.directory_size(http_cache_location)
+            + filesystem.directory_size(old_http_cache_location)
+        )
+        wheels_cache_size = filesystem.format_directory_size(wheels_cache_location)
+
+        message = (
+            textwrap.dedent("""
+                    Package index page cache location (pip v23.3+): {http_cache_location}
+                    Package index page cache location (older pips): {old_http_cache_location}
+                    Package index page cache size: {http_cache_size}
+                    Number of HTTP files: {num_http_files}
+                    Locally built wheels location: {wheels_cache_location}
+                    Locally built wheels size: {wheels_cache_size}
+                    Number of locally built wheels: {package_count}
+                """)  # noqa: E501
+            .format(
+                http_cache_location=http_cache_location,
+                old_http_cache_location=old_http_cache_location,
+                http_cache_size=http_cache_size,
+                num_http_files=num_http_files,
+                wheels_cache_location=wheels_cache_location,
+                package_count=num_packages,
+                wheels_cache_size=wheels_cache_size,
+            )
+            .strip()
+        )
+
+        logger.info(message)
+
+    def list_cache_items(self, options: Values, args: list[str]) -> None:
+        if len(args) > 1:
+            raise CommandError("Too many arguments")
+
+        if args:
+            pattern = args[0]
+        else:
+            pattern = "*"
+
+        files = self._find_wheels(options, pattern)
+        if options.list_format == "human":
+            self.format_for_human(files)
+        else:
+            self.format_for_abspath(files)
+
+    def format_for_human(self, files: list[str]) -> None:
+        if not files:
+            logger.info("No locally built wheels cached.")
+            return
+
+        results = []
+        for filename in files:
+            wheel = os.path.basename(filename)
+            size = filesystem.format_file_size(filename)
+            results.append(f" - {wheel} ({size})")
+        logger.info("Cache contents:\n")
+        logger.info("\n".join(sorted(results)))
+
+    def format_for_abspath(self, files: list[str]) -> None:
+        if files:
+            logger.info("\n".join(sorted(files)))
+
+    def remove_cache_items(self, options: Values, args: list[str]) -> None:
+        if len(args) > 1:
+            raise CommandError("Too many arguments")
+
+        if not args:
+            raise CommandError("Please provide a pattern")
+
+        files = self._find_wheels(options, args[0])
+
+        no_matching_msg = "No matching packages"
+        if args[0] == "*":
+            # Only fetch http files if no specific pattern given
+            files += self._find_http_files(options)
+        else:
+            # Add the pattern to the log message
+            no_matching_msg += f' for pattern "{args[0]}"'
+
+        if not files:
+            logger.warning(no_matching_msg)
+
+        bytes_removed = 0
+        for filename in files:
+            bytes_removed += os.stat(filename).st_size
+            os.unlink(filename)
+            logger.verbose("Removed %s", filename)
+
+        http_dirs = filesystem.subdirs_without_files(self._cache_dir(options, "http"))
+        http_v2_dirs = filesystem.subdirs_without_files(
+            self._cache_dir(options, "http-v2")
+        )
+        wheel_dirs = filesystem.subdirs_without_wheels(
+            self._cache_dir(options, "wheels")
+        )
+        dirs = [*http_dirs, *http_v2_dirs, *wheel_dirs]
+
+        for subdir in dirs:
+            try:
+                for file in subdir.iterdir():
+                    file.unlink(missing_ok=True)
+                subdir.rmdir()
+            except FileNotFoundError:
+                # If the directory is already gone, that's fine.
+                pass
+            logger.verbose("Removed %s", subdir)
+
+        # selfcheck.json is no longer used by pip.
+        selfcheck_json = self._cache_dir(options, "selfcheck.json")
+        if os.path.isfile(selfcheck_json):
+            os.remove(selfcheck_json)
+            logger.verbose("Removed legacy selfcheck.json file")
+
+        logger.info("Files removed: %s (%s)", len(files), format_size(bytes_removed))
+        logger.info("Directories removed: %s", len(dirs))
+
+    def purge_cache(self, options: Values, args: list[str]) -> None:
+        if args:
+            raise CommandError("Too many arguments")
+
+        return self.remove_cache_items(options, ["*"])
+
+    def _cache_dir(self, options: Values, subdir: str) -> str:
+        return os.path.join(options.cache_dir, subdir)
+
+    def _find_http_files(self, options: Values) -> list[str]:
+        old_http_dir = self._cache_dir(options, "http")
+        new_http_dir = self._cache_dir(options, "http-v2")
+        return filesystem.find_files(old_http_dir, "*") + filesystem.find_files(
+            new_http_dir, "*"
+        )
+
+    def _find_wheels(self, options: Values, pattern: str) -> list[str]:
+        wheel_dir = self._cache_dir(options, "wheels")
+
+        # The wheel filename format, as specified in PEP 427, is:
+        #     {distribution}-{version}(-{build})?-{python}-{abi}-{platform}.whl
+        #
+        # Additionally, non-alphanumeric values in the distribution are
+        # normalized to underscores (_), meaning hyphens can never occur
+        # before `-{version}`.
+        #
+        # Given that information:
+        # - If the pattern we're given contains a hyphen (-), the user is
+        #   providing at least the version. Thus, we can just append `*.whl`
+        #   to match the rest of it.
+        # - If the pattern we're given doesn't contain a hyphen (-), the
+        #   user is only providing the name. Thus, we append `-*.whl` to
+        #   match the hyphen before the version, followed by anything else.
+        #
+        # PEP 427: https://www.python.org/dev/peps/pep-0427/
+        pattern = pattern + ("*.whl" if "-" in pattern else "-*.whl")
+
+        return filesystem.find_files(wheel_dir, pattern)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/check.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/check.py
new file mode 100644
index 0000000..516757e
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/check.py
@@ -0,0 +1,66 @@
+import logging
+from optparse import Values
+
+from pip._internal.cli.base_command import Command
+from pip._internal.cli.status_codes import ERROR, SUCCESS
+from pip._internal.metadata import get_default_environment
+from pip._internal.operations.check import (
+    check_package_set,
+    check_unsupported,
+    create_package_set_from_installed,
+)
+from pip._internal.utils.compatibility_tags import get_supported
+from pip._internal.utils.misc import write_output
+
+logger = logging.getLogger(__name__)
+
+
+class CheckCommand(Command):
+    """Verify installed packages have compatible dependencies."""
+
+    ignore_require_venv = True
+    usage = """
+      %prog [options]"""
+
+    def run(self, options: Values, args: list[str]) -> int:
+        package_set, parsing_probs = create_package_set_from_installed()
+        missing, conflicting = check_package_set(package_set)
+        unsupported = list(
+            check_unsupported(
+                get_default_environment().iter_installed_distributions(),
+                get_supported(),
+            )
+        )
+
+        for project_name in missing:
+            version = package_set[project_name].version
+            for dependency in missing[project_name]:
+                write_output(
+                    "%s %s requires %s, which is not installed.",
+                    project_name,
+                    version,
+                    dependency[0],
+                )
+
+        for project_name in conflicting:
+            version = package_set[project_name].version
+            for dep_name, dep_version, req in conflicting[project_name]:
+                write_output(
+                    "%s %s has requirement %s, but you have %s %s.",
+                    project_name,
+                    version,
+                    req,
+                    dep_name,
+                    dep_version,
+                )
+        for package in unsupported:
+            write_output(
+                "%s %s is not supported on this platform",
+                package.raw_name,
+                package.version,
+            )
+        if missing or conflicting or parsing_probs or unsupported:
+            return ERROR
+        else:
+            write_output("No broken requirements found.")
+            return SUCCESS
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/completion.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/completion.py
new file mode 100644
index 0000000..7ba205d
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/completion.py
@@ -0,0 +1,136 @@
+import sys
+import textwrap
+from optparse import Values
+
+from pip._internal.cli.base_command import Command
+from pip._internal.cli.status_codes import SUCCESS
+from pip._internal.utils.misc import get_prog
+
+BASE_COMPLETION = """
+# pip {shell} completion start{script}# pip {shell} completion end
+"""
+
+COMPLETION_SCRIPTS = {
+    "bash": """
+        _pip_completion()
+        {{
+            local IFS=$' \\t\\n'
+            COMPREPLY=( $( COMP_WORDS="${{COMP_WORDS[*]}}" \\
+                           COMP_CWORD=$COMP_CWORD \\
+                           PIP_AUTO_COMPLETE=1 "$1" 2>/dev/null ) )
+        }}
+        complete -o default -F _pip_completion {prog}
+    """,
+    "zsh": """
+        #compdef -P pip[0-9.]#
+        __pip() {{
+          compadd $( COMP_WORDS="$words[*]" \\
+                     COMP_CWORD=$((CURRENT-1)) \\
+                     PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null )
+        }}
+        if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
+          # autoload from fpath, call function directly
+          __pip "$@"
+        else
+          # eval/source/. command, register function for later
+          compdef __pip -P 'pip[0-9.]#'
+        fi
+    """,
+    "fish": """
+        function __fish_complete_pip
+            set -lx COMP_WORDS \\
+                (commandline --current-process --tokenize --cut-at-cursor) \\
+                (commandline --current-token --cut-at-cursor)
+            set -lx COMP_CWORD (math (count $COMP_WORDS) - 1)
+            set -lx PIP_AUTO_COMPLETE 1
+            set -l completions
+            if string match -q '2.*' $version
+                set completions (eval $COMP_WORDS[1])
+            else
+                set completions ($COMP_WORDS[1])
+            end
+            string split \\  -- $completions
+        end
+        complete -fa "(__fish_complete_pip)" -c {prog}
+    """,
+    "powershell": """
+        if ((Test-Path Function:\\TabExpansion) -and -not `
+            (Test-Path Function:\\_pip_completeBackup)) {{
+            Rename-Item Function:\\TabExpansion _pip_completeBackup
+        }}
+        function TabExpansion($line, $lastWord) {{
+            $lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart()
+            if ($lastBlock.StartsWith("{prog} ")) {{
+                $Env:COMP_WORDS=$lastBlock
+                $Env:COMP_CWORD=$lastBlock.Split().Length - 1
+                $Env:PIP_AUTO_COMPLETE=1
+                (& {prog}).Split()
+                Remove-Item Env:COMP_WORDS
+                Remove-Item Env:COMP_CWORD
+                Remove-Item Env:PIP_AUTO_COMPLETE
+            }}
+            elseif (Test-Path Function:\\_pip_completeBackup) {{
+                # Fall back on existing tab expansion
+                _pip_completeBackup $line $lastWord
+            }}
+        }}
+    """,
+}
+
+
+class CompletionCommand(Command):
+    """A helper command to be used for command completion."""
+
+    ignore_require_venv = True
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(
+            "--bash",
+            "-b",
+            action="store_const",
+            const="bash",
+            dest="shell",
+            help="Emit completion code for bash",
+        )
+        self.cmd_opts.add_option(
+            "--zsh",
+            "-z",
+            action="store_const",
+            const="zsh",
+            dest="shell",
+            help="Emit completion code for zsh",
+        )
+        self.cmd_opts.add_option(
+            "--fish",
+            "-f",
+            action="store_const",
+            const="fish",
+            dest="shell",
+            help="Emit completion code for fish",
+        )
+        self.cmd_opts.add_option(
+            "--powershell",
+            "-p",
+            action="store_const",
+            const="powershell",
+            dest="shell",
+            help="Emit completion code for powershell",
+        )
+
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    def run(self, options: Values, args: list[str]) -> int:
+        """Prints the completion code of the given shell"""
+        shells = COMPLETION_SCRIPTS.keys()
+        shell_options = ["--" + shell for shell in sorted(shells)]
+        if options.shell in shells:
+            script = textwrap.dedent(
+                COMPLETION_SCRIPTS.get(options.shell, "").format(prog=get_prog())
+            )
+            print(BASE_COMPLETION.format(script=script, shell=options.shell))
+            return SUCCESS
+        else:
+            sys.stderr.write(
+                "ERROR: You must pass {}\n".format(" or ".join(shell_options))
+            )
+            return SUCCESS
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/configuration.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/configuration.py
new file mode 100644
index 0000000..4fa860f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/configuration.py
@@ -0,0 +1,289 @@
+from __future__ import annotations
+
+import logging
+import os
+import subprocess
+from collections.abc import Callable
+from optparse import Values
+from typing import Any
+
+from pip._internal.cli.base_command import Command
+from pip._internal.cli.status_codes import ERROR, SUCCESS
+from pip._internal.configuration import (
+    Configuration,
+    Kind,
+    get_configuration_files,
+    kinds,
+)
+from pip._internal.exceptions import PipError
+from pip._internal.utils.logging import indent_log
+from pip._internal.utils.misc import get_prog, write_output
+
+logger = logging.getLogger(__name__)
+
+
+class ConfigurationCommand(Command):
+    """
+    Manage local and global configuration.
+
+    Subcommands:
+
+    - list: List the active configuration (or from the file specified)
+    - edit: Edit the configuration file in an editor
+    - get: Get the value associated with command.option
+    - set: Set the command.option=value
+    - unset: Unset the value associated with command.option
+    - debug: List the configuration files and values defined under them
+
+    Configuration keys should be dot separated command and option name,
+    with the special prefix "global" affecting any command. For example,
+    "pip config set global.index-url https://example.org/" would configure
+    the index url for all commands, but "pip config set download.timeout 10"
+    would configure a 10 second timeout only for "pip download" commands.
+
+    If none of --user, --global and --site are passed, a virtual
+    environment configuration file is used if one is active and the file
+    exists. Otherwise, all modifications happen to the user file by
+    default.
+    """
+
+    ignore_require_venv = True
+    usage = """
+        %prog [] list
+        %prog [] [--editor ] edit
+
+        %prog [] get command.option
+        %prog [] set command.option value
+        %prog [] unset command.option
+        %prog [] debug
+    """
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(
+            "--editor",
+            dest="editor",
+            action="store",
+            default=None,
+            help=(
+                "Editor to use to edit the file. Uses VISUAL or EDITOR "
+                "environment variables if not provided."
+            ),
+        )
+
+        self.cmd_opts.add_option(
+            "--global",
+            dest="global_file",
+            action="store_true",
+            default=False,
+            help="Use the system-wide configuration file only",
+        )
+
+        self.cmd_opts.add_option(
+            "--user",
+            dest="user_file",
+            action="store_true",
+            default=False,
+            help="Use the user configuration file only",
+        )
+
+        self.cmd_opts.add_option(
+            "--site",
+            dest="site_file",
+            action="store_true",
+            default=False,
+            help="Use the current environment configuration file only",
+        )
+
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    def handler_map(self) -> dict[str, Callable[[Values, list[str]], None]]:
+        return {
+            "list": self.list_values,
+            "edit": self.open_in_editor,
+            "get": self.get_name,
+            "set": self.set_name_value,
+            "unset": self.unset_name,
+            "debug": self.list_config_values,
+        }
+
+    def run(self, options: Values, args: list[str]) -> int:
+        handler_map = self.handler_map()
+
+        # Determine action
+        if not args or args[0] not in handler_map:
+            logger.error(
+                "Need an action (%s) to perform.",
+                ", ".join(sorted(handler_map)),
+            )
+            return ERROR
+
+        action = args[0]
+
+        # Determine which configuration files are to be loaded
+        #    Depends on whether the command is modifying.
+        try:
+            load_only = self._determine_file(
+                options, need_value=(action in ["get", "set", "unset", "edit"])
+            )
+        except PipError as e:
+            logger.error(e.args[0])
+            return ERROR
+
+        # Load a new configuration
+        self.configuration = Configuration(
+            isolated=options.isolated_mode, load_only=load_only
+        )
+        self.configuration.load()
+
+        # Error handling happens here, not in the action-handlers.
+        try:
+            handler_map[action](options, args[1:])
+        except PipError as e:
+            logger.error(e.args[0])
+            return ERROR
+
+        return SUCCESS
+
+    def _determine_file(self, options: Values, need_value: bool) -> Kind | None:
+        file_options = [
+            key
+            for key, value in (
+                (kinds.USER, options.user_file),
+                (kinds.GLOBAL, options.global_file),
+                (kinds.SITE, options.site_file),
+            )
+            if value
+        ]
+
+        if not file_options:
+            if not need_value:
+                return None
+            # Default to user, unless there's a site file.
+            elif any(
+                os.path.exists(site_config_file)
+                for site_config_file in get_configuration_files()[kinds.SITE]
+            ):
+                return kinds.SITE
+            else:
+                return kinds.USER
+        elif len(file_options) == 1:
+            return file_options[0]
+
+        raise PipError(
+            "Need exactly one file to operate upon "
+            "(--user, --site, --global) to perform."
+        )
+
+    def list_values(self, options: Values, args: list[str]) -> None:
+        self._get_n_args(args, "list", n=0)
+
+        for key, value in sorted(self.configuration.items()):
+            for key, value in sorted(value.items()):
+                write_output("%s=%r", key, value)
+
+    def get_name(self, options: Values, args: list[str]) -> None:
+        key = self._get_n_args(args, "get [name]", n=1)
+        value = self.configuration.get_value(key)
+
+        write_output("%s", value)
+
+    def set_name_value(self, options: Values, args: list[str]) -> None:
+        key, value = self._get_n_args(args, "set [name] [value]", n=2)
+        self.configuration.set_value(key, value)
+
+        self._save_configuration()
+
+    def unset_name(self, options: Values, args: list[str]) -> None:
+        key = self._get_n_args(args, "unset [name]", n=1)
+        self.configuration.unset_value(key)
+
+        self._save_configuration()
+
+    def list_config_values(self, options: Values, args: list[str]) -> None:
+        """List config key-value pairs across different config files"""
+        self._get_n_args(args, "debug", n=0)
+
+        self.print_env_var_values()
+        # Iterate over config files and print if they exist, and the
+        # key-value pairs present in them if they do
+        for variant, files in sorted(self.configuration.iter_config_files()):
+            write_output("%s:", variant)
+            for fname in files:
+                with indent_log():
+                    file_exists = os.path.exists(fname)
+                    write_output("%s, exists: %r", fname, file_exists)
+                    if file_exists:
+                        self.print_config_file_values(variant, fname)
+
+    def print_config_file_values(self, variant: Kind, fname: str) -> None:
+        """Get key-value pairs from the file of a variant"""
+        for name, value in self.configuration.get_values_in_config(variant).items():
+            with indent_log():
+                if name == fname:
+                    for confname, confvalue in value.items():
+                        write_output("%s: %s", confname, confvalue)
+
+    def print_env_var_values(self) -> None:
+        """Get key-values pairs present as environment variables"""
+        write_output("%s:", "env_var")
+        with indent_log():
+            for key, value in sorted(self.configuration.get_environ_vars()):
+                env_var = f"PIP_{key.upper()}"
+                write_output("%s=%r", env_var, value)
+
+    def open_in_editor(self, options: Values, args: list[str]) -> None:
+        editor = self._determine_editor(options)
+
+        fname = self.configuration.get_file_to_edit()
+        if fname is None:
+            raise PipError("Could not determine appropriate file.")
+        elif '"' in fname:
+            # This shouldn't happen, unless we see a username like that.
+            # If that happens, we'd appreciate a pull request fixing this.
+            raise PipError(
+                f'Can not open an editor for a file name containing "\n{fname}'
+            )
+
+        try:
+            subprocess.check_call(f'{editor} "{fname}"', shell=True)
+        except FileNotFoundError as e:
+            if not e.filename:
+                e.filename = editor
+            raise
+        except subprocess.CalledProcessError as e:
+            raise PipError(f"Editor Subprocess exited with exit code {e.returncode}")
+
+    def _get_n_args(self, args: list[str], example: str, n: int) -> Any:
+        """Helper to make sure the command got the right number of arguments"""
+        if len(args) != n:
+            msg = (
+                f"Got unexpected number of arguments, expected {n}. "
+                f'(example: "{get_prog()} config {example}")'
+            )
+            raise PipError(msg)
+
+        if n == 1:
+            return args[0]
+        else:
+            return args
+
+    def _save_configuration(self) -> None:
+        # We successfully ran a modifying command. Need to save the
+        # configuration.
+        try:
+            self.configuration.save()
+        except Exception:
+            logger.exception(
+                "Unable to save configuration. Please report this as a bug."
+            )
+            raise PipError("Internal Error.")
+
+    def _determine_editor(self, options: Values) -> str:
+        if options.editor is not None:
+            return options.editor
+        elif "VISUAL" in os.environ:
+            return os.environ["VISUAL"]
+        elif "EDITOR" in os.environ:
+            return os.environ["EDITOR"]
+        else:
+            raise PipError("Could not determine editor to use.")
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/debug.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/debug.py
new file mode 100644
index 0000000..4e8178d
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/debug.py
@@ -0,0 +1,195 @@
+from __future__ import annotations
+
+import logging
+import os
+import sys
+from optparse import Values
+from types import ModuleType
+from typing import Any
+
+import pip._vendor
+from pip._vendor.certifi import where
+from pip._vendor.packaging.version import parse as parse_version
+
+from pip._internal.cli import cmdoptions
+from pip._internal.cli.base_command import Command
+from pip._internal.cli.cmdoptions import make_target_python
+from pip._internal.cli.status_codes import SUCCESS
+from pip._internal.configuration import Configuration
+from pip._internal.metadata import get_environment
+from pip._internal.utils.compat import get_locale_encoding, open_text_resource
+from pip._internal.utils.logging import indent_log
+from pip._internal.utils.misc import get_pip_version
+
+logger = logging.getLogger(__name__)
+
+
+def show_value(name: str, value: Any) -> None:
+    logger.info("%s: %s", name, value)
+
+
+def show_sys_implementation() -> None:
+    logger.info("sys.implementation:")
+    implementation_name = sys.implementation.name
+    with indent_log():
+        show_value("name", implementation_name)
+
+
+def create_vendor_txt_map() -> dict[str, str]:
+    with open_text_resource("pip._vendor", "vendor.txt") as f:
+        # Purge non version specifying lines.
+        # Also, remove any space prefix or suffixes (including comments).
+        lines = [
+            line.strip().split(" ", 1)[0] for line in f.readlines() if "==" in line
+        ]
+
+    # Transform into "module" -> version dict.
+    return dict(line.split("==", 1) for line in lines)
+
+
+def get_module_from_module_name(module_name: str) -> ModuleType | None:
+    # Module name can be uppercase in vendor.txt for some reason...
+    module_name = module_name.lower().replace("-", "_")
+    # PATCH: setuptools is actually only pkg_resources.
+    if module_name == "setuptools":
+        module_name = "pkg_resources"
+
+    __import__(f"pip._vendor.{module_name}", globals(), locals(), level=0)
+    return getattr(pip._vendor, module_name)
+
+
+def get_vendor_version_from_module(module_name: str) -> str | None:
+    module = get_module_from_module_name(module_name)
+    version = getattr(module, "__version__", None)
+
+    if module and not version:
+        # Try to find version in debundled module info.
+        assert module.__file__ is not None
+        env = get_environment([os.path.dirname(module.__file__)])
+        dist = env.get_distribution(module_name)
+        if dist:
+            version = str(dist.version)
+
+    return version
+
+
+def show_actual_vendor_versions(vendor_txt_versions: dict[str, str]) -> None:
+    """Log the actual version and print extra info if there is
+    a conflict or if the actual version could not be imported.
+    """
+    for module_name, expected_version in vendor_txt_versions.items():
+        extra_message = ""
+        actual_version = get_vendor_version_from_module(module_name)
+        if not actual_version:
+            extra_message = (
+                " (Unable to locate actual module version, using"
+                " vendor.txt specified version)"
+            )
+            actual_version = expected_version
+        elif parse_version(actual_version) != parse_version(expected_version):
+            extra_message = (
+                " (CONFLICT: vendor.txt suggests version should"
+                f" be {expected_version})"
+            )
+        logger.info("%s==%s%s", module_name, actual_version, extra_message)
+
+
+def show_vendor_versions() -> None:
+    logger.info("vendored library versions:")
+
+    vendor_txt_versions = create_vendor_txt_map()
+    with indent_log():
+        show_actual_vendor_versions(vendor_txt_versions)
+
+
+def show_tags(options: Values) -> None:
+    tag_limit = 10
+
+    target_python = make_target_python(options)
+    tags = target_python.get_sorted_tags()
+
+    # Display the target options that were explicitly provided.
+    formatted_target = target_python.format_given()
+    suffix = ""
+    if formatted_target:
+        suffix = f" (target: {formatted_target})"
+
+    msg = f"Compatible tags: {len(tags)}{suffix}"
+    logger.info(msg)
+
+    if options.verbose < 1 and len(tags) > tag_limit:
+        tags_limited = True
+        tags = tags[:tag_limit]
+    else:
+        tags_limited = False
+
+    with indent_log():
+        for tag in tags:
+            logger.info(str(tag))
+
+        if tags_limited:
+            msg = f"...\n[First {tag_limit} tags shown. Pass --verbose to show all.]"
+            logger.info(msg)
+
+
+def ca_bundle_info(config: Configuration) -> str:
+    levels = {key.split(".", 1)[0] for _, options in config.items() for key in options}
+    if not levels:
+        return "Not specified"
+
+    levels_that_override_global = ["install", "wheel", "download"]
+    global_overriding_level = [
+        level for level in levels if level in levels_that_override_global
+    ]
+    if not global_overriding_level:
+        return "global"
+
+    if "global" in levels:
+        levels.remove("global")
+    return ", ".join(levels)
+
+
+class DebugCommand(Command):
+    """
+    Display debug information.
+    """
+
+    usage = """
+      %prog """
+    ignore_require_venv = True
+
+    def add_options(self) -> None:
+        cmdoptions.add_target_python_options(self.cmd_opts)
+        self.parser.insert_option_group(0, self.cmd_opts)
+        self.parser.config.load()
+
+    def run(self, options: Values, args: list[str]) -> int:
+        logger.warning(
+            "This command is only meant for debugging. "
+            "Do not use this with automation for parsing and getting these "
+            "details, since the output and options of this command may "
+            "change without notice."
+        )
+        show_value("pip version", get_pip_version())
+        show_value("sys.version", sys.version)
+        show_value("sys.executable", sys.executable)
+        show_value("sys.getdefaultencoding", sys.getdefaultencoding())
+        show_value("sys.getfilesystemencoding", sys.getfilesystemencoding())
+        show_value(
+            "locale.getencoding",
+            get_locale_encoding(),
+        )
+        show_value("sys.platform", sys.platform)
+        show_sys_implementation()
+
+        show_value("'cert' config value", ca_bundle_info(self.parser.config))
+        show_value("REQUESTS_CA_BUNDLE", os.environ.get("REQUESTS_CA_BUNDLE"))
+        show_value("CURL_CA_BUNDLE", os.environ.get("CURL_CA_BUNDLE"))
+        show_value("pip._vendor.certifi.where()", where())
+        show_value("pip._vendor.DEBUNDLED", pip._vendor.DEBUNDLED)
+
+        show_vendor_versions()
+
+        show_tags(options)
+
+        return SUCCESS
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/download.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/download.py
new file mode 100644
index 0000000..8b34982
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/download.py
@@ -0,0 +1,150 @@
+import logging
+import os
+from optparse import Values
+
+from pip._internal.cli import cmdoptions
+from pip._internal.cli.cmdoptions import make_target_python
+from pip._internal.cli.req_command import RequirementCommand, with_cleanup
+from pip._internal.cli.status_codes import SUCCESS
+from pip._internal.operations.build.build_tracker import get_build_tracker
+from pip._internal.utils.misc import ensure_dir, normalize_path, write_output
+from pip._internal.utils.temp_dir import TempDirectory
+
+logger = logging.getLogger(__name__)
+
+
+class DownloadCommand(RequirementCommand):
+    """
+    Download packages from:
+
+    - PyPI (and other indexes) using requirement specifiers.
+    - VCS project urls.
+    - Local project directories.
+    - Local or remote source archives.
+
+    pip also supports downloading from "requirements files", which provide
+    an easy way to specify a whole environment to be downloaded.
+    """
+
+    usage = """
+      %prog [options]  [package-index-options] ...
+      %prog [options] -r  [package-index-options] ...
+      %prog [options]  ...
+      %prog [options]  ...
+      %prog [options]  ..."""
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(cmdoptions.constraints())
+        self.cmd_opts.add_option(cmdoptions.build_constraints())
+        self.cmd_opts.add_option(cmdoptions.requirements())
+        self.cmd_opts.add_option(cmdoptions.requirements_from_scripts())
+        self.cmd_opts.add_option(cmdoptions.no_deps())
+        self.cmd_opts.add_option(cmdoptions.only_deps())
+        self.cmd_opts.add_option(cmdoptions.src())
+        self.cmd_opts.add_option(cmdoptions.require_hashes())
+        self.cmd_opts.add_option(cmdoptions.no_require_hashes())
+        self.cmd_opts.add_option(cmdoptions.progress_bar())
+        self.cmd_opts.add_option(cmdoptions.no_build_isolation())
+        self.cmd_opts.add_option(cmdoptions.use_pep517())
+        self.cmd_opts.add_option(cmdoptions.check_build_deps())
+        self.cmd_opts.add_option(cmdoptions.ignore_requires_python())
+
+        self.cmd_opts.add_option(
+            "-d",
+            "--dest",
+            "--destination-dir",
+            "--destination-directory",
+            dest="download_dir",
+            metavar="dir",
+            default=os.curdir,
+            help="Download packages into .",
+        )
+
+        cmdoptions.add_target_python_options(self.cmd_opts)
+
+        index_opts = cmdoptions.make_option_group(
+            cmdoptions.index_group,
+            self.parser,
+        )
+
+        selection_opts = cmdoptions.make_option_group(
+            cmdoptions.package_selection_group,
+            self.parser,
+        )
+
+        self.parser.insert_option_group(0, index_opts)
+        self.parser.insert_option_group(0, selection_opts)
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    @with_cleanup
+    def run(self, options: Values, args: list[str]) -> int:
+        options.ignore_installed = True
+        # editable doesn't really make sense for `pip download`, but the bowels
+        # of the RequirementSet code require that property.
+        options.editables = []
+
+        cmdoptions.check_dist_restriction(options)
+        cmdoptions.check_build_constraints(options)
+        cmdoptions.check_release_control_exclusive(options)
+        cmdoptions.check_only_deps_option_does_not_conflict(options)
+
+        options.download_dir = normalize_path(options.download_dir)
+        ensure_dir(options.download_dir)
+
+        session = self.get_default_session(options)
+
+        target_python = make_target_python(options)
+        finder = self._build_package_finder(
+            options=options,
+            session=session,
+            target_python=target_python,
+            ignore_requires_python=options.ignore_requires_python,
+        )
+
+        build_tracker = self.enter_context(get_build_tracker())
+
+        directory = TempDirectory(
+            delete=not options.no_clean,
+            kind="download",
+            globally_managed=True,
+        )
+
+        reqs = self.get_requirements(args, options, finder, session)
+
+        preparer = self.make_requirement_preparer(
+            temp_build_dir=directory,
+            options=options,
+            build_tracker=build_tracker,
+            session=session,
+            finder=finder,
+            download_dir=options.download_dir,
+            use_user_site=False,
+            verbosity=self.verbosity,
+            allow_editables=False,
+        )
+
+        resolver = self.make_resolver(
+            preparer=preparer,
+            finder=finder,
+            options=options,
+            ignore_requires_python=options.ignore_requires_python,
+            py_version_info=options.python_version,
+        )
+
+        self.trace_basic_info(finder)
+
+        requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
+
+        preparer.prepare_linked_requirements_more(requirement_set.requirements.values())
+
+        downloaded: list[str] = []
+        for req in requirement_set.requirements.values():
+            if req.satisfied_by is None:
+                assert req.name is not None
+                preparer.save_linked_requirement(req)
+                downloaded.append(req.name)
+
+        if downloaded:
+            write_output("Successfully downloaded %s", " ".join(downloaded))
+
+        return SUCCESS
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/freeze.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/freeze.py
new file mode 100644
index 0000000..ab501db
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/freeze.py
@@ -0,0 +1,107 @@
+import sys
+from optparse import Values
+
+from pip._internal.cli import cmdoptions
+from pip._internal.cli.base_command import Command
+from pip._internal.cli.status_codes import SUCCESS
+from pip._internal.metadata.base import stdlib_pkgs
+from pip._internal.operations.freeze import freeze
+
+
+def _should_suppress_build_backends() -> bool:
+    return sys.version_info < (3, 12)
+
+
+def _dev_pkgs() -> set[str]:
+    pkgs = {"pip"}
+
+    if _should_suppress_build_backends():
+        pkgs |= {"setuptools", "distribute", "wheel"}
+
+    return pkgs
+
+
+class FreezeCommand(Command):
+    """
+    Output installed packages in requirements format.
+
+    packages are listed in a case-insensitive sorted order.
+    """
+
+    ignore_require_venv = True
+    usage = """
+      %prog [options]"""
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(
+            "-r",
+            "--requirement",
+            dest="requirements",
+            action="append",
+            default=[],
+            metavar="file",
+            help=(
+                "Use the order in the given requirements file and its "
+                "comments when generating output. This option can be "
+                "used multiple times."
+            ),
+        )
+        self.cmd_opts.add_option(
+            "-l",
+            "--local",
+            dest="local",
+            action="store_true",
+            default=False,
+            help=(
+                "If in a virtualenv that has global access, do not output "
+                "globally-installed packages."
+            ),
+        )
+        self.cmd_opts.add_option(
+            "--user",
+            dest="user",
+            action="store_true",
+            default=False,
+            help="Only output packages installed in user-site.",
+        )
+        self.cmd_opts.add_option(cmdoptions.list_path())
+        self.cmd_opts.add_option(
+            "--all",
+            dest="freeze_all",
+            action="store_true",
+            help=(
+                "Do not skip these packages in the output:"
+                " {}".format(", ".join(_dev_pkgs()))
+            ),
+        )
+        self.cmd_opts.add_option(
+            "--exclude-editable",
+            dest="exclude_editable",
+            action="store_true",
+            help="Exclude editable package from output.",
+        )
+        self.cmd_opts.add_option(cmdoptions.list_exclude())
+
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    def run(self, options: Values, args: list[str]) -> int:
+        skip = set(stdlib_pkgs)
+        if not options.freeze_all:
+            skip.update(_dev_pkgs())
+
+        if options.excludes:
+            skip.update(options.excludes)
+
+        cmdoptions.check_list_path_option(options)
+
+        for line in freeze(
+            requirement=options.requirements,
+            local_only=options.local,
+            user_only=options.user,
+            paths=options.path,
+            isolated=options.isolated_mode,
+            skip=skip,
+            exclude_editable=options.exclude_editable,
+        ):
+            sys.stdout.write(line + "\n")
+        return SUCCESS
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/hash.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/hash.py
new file mode 100644
index 0000000..271a4c9
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/hash.py
@@ -0,0 +1,58 @@
+import hashlib
+import logging
+import sys
+from optparse import Values
+
+from pip._internal.cli.base_command import Command
+from pip._internal.cli.status_codes import ERROR, SUCCESS
+from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES
+from pip._internal.utils.misc import read_chunks, write_output
+
+logger = logging.getLogger(__name__)
+
+
+class HashCommand(Command):
+    """
+    Compute a hash of a local package archive.
+
+    These can be used with --hash in a requirements file to do repeatable
+    installs.
+    """
+
+    usage = "%prog [options]  ..."
+    ignore_require_venv = True
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(
+            "-a",
+            "--algorithm",
+            dest="algorithm",
+            choices=STRONG_HASHES,
+            action="store",
+            default=FAVORITE_HASH,
+            help="The hash algorithm to use: one of {}".format(
+                ", ".join(STRONG_HASHES)
+            ),
+        )
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    def run(self, options: Values, args: list[str]) -> int:
+        if not args:
+            self.parser.print_usage(sys.stderr)
+            return ERROR
+
+        algorithm = options.algorithm
+        for path in args:
+            write_output(
+                "%s:\n--hash=%s:%s", path, algorithm, _hash_of_file(path, algorithm)
+            )
+        return SUCCESS
+
+
+def _hash_of_file(path: str, algorithm: str) -> str:
+    """Return the hash digest of a file."""
+    with open(path, "rb") as archive:
+        hash = hashlib.new(algorithm)
+        for chunk in read_chunks(archive):
+            hash.update(chunk)
+    return hash.hexdigest()
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/help.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/help.py
new file mode 100644
index 0000000..2ae658f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/help.py
@@ -0,0 +1,40 @@
+from optparse import Values
+
+from pip._internal.cli.base_command import Command
+from pip._internal.cli.status_codes import SUCCESS
+from pip._internal.exceptions import CommandError
+
+
+class HelpCommand(Command):
+    """Show help for commands"""
+
+    usage = """
+      %prog """
+    ignore_require_venv = True
+
+    def run(self, options: Values, args: list[str]) -> int:
+        from pip._internal.commands import (
+            commands_dict,
+            create_command,
+            get_similar_commands,
+        )
+
+        try:
+            # 'pip help' with no args is handled by pip.__init__.parseopt()
+            cmd_name = args[0]  # the command we need help for
+        except IndexError:
+            return SUCCESS
+
+        if cmd_name not in commands_dict:
+            guess = get_similar_commands(cmd_name)
+
+            msg = [f'unknown command "{cmd_name}"']
+            if guess:
+                msg.append(f'maybe you meant "{guess}"')
+
+            raise CommandError(" - ".join(msg))
+
+        command = create_command(cmd_name)
+        command.parser.print_help()
+
+        return SUCCESS
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/index.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/index.py
new file mode 100644
index 0000000..e444b9a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/index.py
@@ -0,0 +1,173 @@
+from __future__ import annotations
+
+import json
+import logging
+from collections.abc import Callable, Iterable
+from optparse import Values
+from typing import Any
+
+from pip._vendor.packaging.utils import canonicalize_name
+from pip._vendor.packaging.version import Version
+
+from pip._internal.cli import cmdoptions
+from pip._internal.cli.req_command import IndexGroupCommand
+from pip._internal.cli.status_codes import ERROR, SUCCESS
+from pip._internal.commands.search import (
+    get_installed_distribution,
+    print_dist_installation_info,
+)
+from pip._internal.exceptions import (
+    CommandError,
+    DiagnosticPipError,
+    DistributionNotFound,
+    PipError,
+)
+from pip._internal.index.collector import LinkCollector
+from pip._internal.index.package_finder import PackageFinder
+from pip._internal.models.selection_prefs import SelectionPreferences
+from pip._internal.models.target_python import TargetPython
+from pip._internal.network.session import PipSession
+from pip._internal.utils.misc import write_output
+
+logger = logging.getLogger(__name__)
+
+
+class IndexCommand(IndexGroupCommand):
+    """
+    Inspect information available from package indexes.
+    """
+
+    ignore_require_venv = True
+    usage = """
+        %prog versions 
+    """
+
+    def add_options(self) -> None:
+        cmdoptions.add_target_python_options(self.cmd_opts)
+
+        self.cmd_opts.add_option(cmdoptions.ignore_requires_python())
+        self.cmd_opts.add_option(cmdoptions.json())
+
+        index_opts = cmdoptions.make_option_group(
+            cmdoptions.index_group,
+            self.parser,
+        )
+
+        selection_opts = cmdoptions.make_option_group(
+            cmdoptions.package_selection_group,
+            self.parser,
+        )
+
+        self.parser.insert_option_group(0, index_opts)
+        self.parser.insert_option_group(0, selection_opts)
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    def handler_map(self) -> dict[str, Callable[[Values, list[str]], None]]:
+        return {
+            "versions": self.get_available_package_versions,
+        }
+
+    def run(self, options: Values, args: list[str]) -> int:
+        cmdoptions.check_release_control_exclusive(options)
+
+        handler_map = self.handler_map()
+
+        # Determine action
+        if not args or args[0] not in handler_map:
+            logger.error(
+                "Need an action (%s) to perform.",
+                ", ".join(sorted(handler_map)),
+            )
+            return ERROR
+
+        action = args[0]
+
+        # Error handling happens here, not in the action-handlers.
+        try:
+            handler_map[action](options, args[1:])
+        except DiagnosticPipError:
+            raise
+        except PipError as e:
+            logger.error(e.args[0])
+            return ERROR
+
+        return SUCCESS
+
+    def _build_package_finder(
+        self,
+        options: Values,
+        session: PipSession,
+        target_python: TargetPython | None = None,
+        ignore_requires_python: bool = False,
+    ) -> PackageFinder:
+        """
+        Create a package finder appropriate to the index command.
+        """
+        link_collector = LinkCollector.create(session, options=options)
+
+        # Pass allow_yanked=False to ignore yanked versions.
+        selection_prefs = SelectionPreferences(
+            allow_yanked=False,
+            release_control=options.release_control,
+            format_control=options.format_control,
+            ignore_requires_python=ignore_requires_python,
+        )
+
+        return PackageFinder.create(
+            link_collector=link_collector,
+            selection_prefs=selection_prefs,
+            target_python=target_python,
+            uploaded_prior_to=options.uploaded_prior_to,
+        )
+
+    def get_available_package_versions(self, options: Values, args: list[Any]) -> None:
+        if len(args) != 1:
+            raise CommandError("You need to specify exactly one argument")
+
+        target_python = cmdoptions.make_target_python(options)
+        query = args[0]
+
+        with self._build_session(options) as session:
+            finder = self._build_package_finder(
+                options=options,
+                session=session,
+                target_python=target_python,
+                ignore_requires_python=options.ignore_requires_python,
+            )
+
+            versions: Iterable[Version] = (
+                candidate.version for candidate in finder.find_all_candidates(query)
+            )
+
+            if self.should_exclude_prerelease(options, canonicalize_name(query)):
+                versions = (
+                    version for version in versions if not version.is_prerelease
+                )
+            versions = set(versions)
+
+            if not versions:
+                raise DistributionNotFound(
+                    f"No matching distribution found for {query}"
+                )
+
+            formatted_versions = [str(ver) for ver in sorted(versions, reverse=True)]
+            latest = formatted_versions[0]
+
+        dist = get_installed_distribution(query)
+
+        if options.json:
+            structured_output = {
+                "name": query,
+                "versions": formatted_versions,
+                "latest": latest,
+            }
+
+            if dist is not None:
+                structured_output["installed_version"] = str(dist.version)
+
+            write_output(json.dumps(structured_output))
+
+        else:
+            write_output(f"{query} ({latest})")
+            write_output("Available versions: {}".format(", ".join(formatted_versions)))
+            print_dist_installation_info(latest, dist)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/inspect.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/inspect.py
new file mode 100644
index 0000000..53b4f00
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/inspect.py
@@ -0,0 +1,92 @@
+import logging
+from optparse import Values
+from typing import Any
+
+from pip._vendor.packaging.markers import default_environment
+from pip._vendor.rich import print_json
+
+from pip import __version__
+from pip._internal.cli import cmdoptions
+from pip._internal.cli.base_command import Command
+from pip._internal.cli.status_codes import SUCCESS
+from pip._internal.metadata import BaseDistribution, get_environment
+from pip._internal.metadata.base import stdlib_pkgs
+from pip._internal.utils.urls import path_to_url
+
+logger = logging.getLogger(__name__)
+
+
+class InspectCommand(Command):
+    """
+    Inspect the content of a Python environment and produce a report in JSON format.
+    """
+
+    ignore_require_venv = True
+    usage = """
+      %prog [options]"""
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(
+            "--local",
+            action="store_true",
+            default=False,
+            help=(
+                "If in a virtualenv that has global access, do not list "
+                "globally-installed packages."
+            ),
+        )
+        self.cmd_opts.add_option(
+            "--user",
+            dest="user",
+            action="store_true",
+            default=False,
+            help="Only output packages installed in user-site.",
+        )
+        self.cmd_opts.add_option(cmdoptions.list_path())
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    def run(self, options: Values, args: list[str]) -> int:
+        cmdoptions.check_list_path_option(options)
+        dists = get_environment(options.path).iter_installed_distributions(
+            local_only=options.local,
+            user_only=options.user,
+            skip=set(stdlib_pkgs),
+        )
+        output = {
+            "version": "1",
+            "pip_version": __version__,
+            "installed": [self._dist_to_dict(dist) for dist in dists],
+            "environment": default_environment(),
+            # TODO tags? scheme?
+        }
+        print_json(data=output)
+        return SUCCESS
+
+    def _dist_to_dict(self, dist: BaseDistribution) -> dict[str, Any]:
+        res: dict[str, Any] = {
+            "metadata": dist.metadata_dict,
+            "metadata_location": dist.info_location,
+        }
+        # direct_url. Note that we don't have download_info (as in the installation
+        # report) since it is not recorded in installed metadata.
+        direct_url = dist.direct_url
+        if direct_url is not None:
+            res["direct_url"] = direct_url.to_dict_compat()
+        else:
+            # Emulate direct_url for legacy editable installs.
+            editable_project_location = dist.editable_project_location
+            if editable_project_location is not None:
+                res["direct_url"] = {
+                    "url": path_to_url(editable_project_location),
+                    "dir_info": {
+                        "editable": True,
+                    },
+                }
+        # installer
+        installer = dist.installer
+        if dist.installer:
+            res["installer"] = installer
+        # requested
+        if dist.installed_with_dist_info:
+            res["requested"] = dist.requested
+        return res
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/install.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/install.py
new file mode 100644
index 0000000..e1482b9
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/install.py
@@ -0,0 +1,904 @@
+from __future__ import annotations
+
+import contextlib
+import errno
+import json
+import operator
+import os
+import shutil
+import site
+import sys
+from collections.abc import Iterator
+from optparse import SUPPRESS_HELP, Values
+from pathlib import Path
+from typing import Any
+
+from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
+from pip._vendor.packaging.utils import canonicalize_name
+from pip._vendor.requests.exceptions import InvalidProxyURL
+from pip._vendor.rich import print_json
+
+# Eagerly import self_outdated_check to avoid crashes. Otherwise,
+# this module would be imported *after* pip was replaced, resulting
+# in crashes if the new self_outdated_check module was incompatible
+# with the rest of pip that's already imported, or allowing a
+# wheel to execute arbitrary code on install by replacing
+# self_outdated_check.
+import pip._internal.self_outdated_check  # noqa: F401
+from pip._internal.cache import WheelCache
+from pip._internal.cli import cmdoptions
+from pip._internal.cli.cmdoptions import make_target_python
+from pip._internal.cli.req_command import (
+    RequirementCommand,
+    with_cleanup,
+)
+from pip._internal.cli.status_codes import ERROR, SUCCESS
+from pip._internal.exceptions import (
+    CommandError,
+    InstallationError,
+    InstallWheelBuildError,
+)
+from pip._internal.locations import get_scheme
+from pip._internal.metadata import BaseEnvironment, get_environment
+from pip._internal.models.installation_report import InstallationReport
+from pip._internal.operations.build.build_tracker import get_build_tracker
+from pip._internal.operations.check import ConflictDetails, check_install_conflicts
+from pip._internal.req import InstallationResult, install_given_reqs
+from pip._internal.req.req_install import (
+    InstallRequirement,
+)
+from pip._internal.utils.compat import WINDOWS
+from pip._internal.utils.deprecation import deprecated
+from pip._internal.utils.filesystem import test_writable_dir
+from pip._internal.utils.logging import getLogger
+from pip._internal.utils.misc import (
+    check_externally_managed,
+    ensure_dir,
+    get_pip_version,
+    protect_pip_from_modification_on_windows,
+    warn_if_run_as_root,
+    write_output,
+)
+from pip._internal.utils.temp_dir import TempDirectory
+from pip._internal.utils.virtualenv import (
+    running_under_virtualenv,
+    virtualenv_no_global,
+)
+from pip._internal.wheel_builder import build
+
+logger = getLogger(__name__)
+
+
+_IMPORT_AUDIT_HOOK_INSTALLED = False
+_MISSING_MODULES: set[str] = set()
+
+# Non-stdlib modules pip (or its vendored dependencies) may import lazily
+# after installation has started. Importing them eagerly keeps the audit
+# hook from misattributing them to a freshly installed distribution.
+_EAGER_IMPORTS: tuple[str, ...] = (
+    "pip._internal.operations.install.wheel",
+    # Used by rich when emitting output to a legacy Windows console.
+    "pip._vendor.rich._windows_renderer",
+)
+
+
+# Imports of standard library modules are always safe: they cannot be
+# shadowed by a distribution pip has just installed.
+_STDLIB_MODULE_NAMES: frozenset[str] = frozenset(sys.stdlib_module_names) | frozenset(
+    sys.builtin_module_names
+)
+
+
+def _prevent_import_hook(name: str, args: tuple[Any, ...]) -> None:
+    if name != "import":
+        return
+    module = args[0]
+    if module in _MISSING_MODULES:
+        raise ImportError(f"No module named {module!r}")
+    if module.partition(".")[0] in _STDLIB_MODULE_NAMES:
+        return
+    deprecated(
+        reason=f"Unexpected import of {module!r} after pip install started.",
+        replacement=None,
+        gone_in="26.3",
+        issue=13842,
+        include_source=True,
+        stacklevel=3,
+    )
+
+
+def _eagerly_import_modules() -> None:
+    """Import modules pip uses lazily so the audit hook ignores them later."""
+    for module in _EAGER_IMPORTS:
+        try:
+            __import__(module)
+        except ImportError:
+            # Record the module as missing so the hook can raise ImportError
+            # instead of trying to import it again.
+            _MISSING_MODULES.add(module)
+
+
+def _prevent_further_imports() -> None:
+    """Install an audit hook that warns on unexpected imports after pip install starts.
+
+    Eagerly pre-imports the known lazy imports first so the hook only fires
+    on genuinely unexpected modules.
+    """
+    global _IMPORT_AUDIT_HOOK_INSTALLED
+    if _IMPORT_AUDIT_HOOK_INSTALLED:
+        return
+
+    _IMPORT_AUDIT_HOOK_INSTALLED = True
+    sys.addaudithook(_prevent_import_hook)
+
+
+def _arg_refers_to_pip(arg: str) -> bool:
+    try:
+        req = Requirement(arg)
+    except InvalidRequirement:
+        return False
+    return canonicalize_name(req.name) == "pip"
+
+
+class InstallCommand(RequirementCommand):
+    """
+    Install packages from:
+
+    - PyPI (and other indexes) using requirement specifiers.
+    - VCS project urls.
+    - Local project directories.
+    - Local or remote source archives.
+
+    pip also supports installing from "requirements files", which provide
+    an easy way to specify a whole environment to be installed.
+    """
+
+    usage = """
+      %prog [options]  [package-index-options] ...
+      %prog [options] -r  [package-index-options] ...
+      %prog [options] [-e]  ...
+      %prog [options] [-e]  ...
+      %prog [options]  ..."""
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(cmdoptions.requirements())
+        self.cmd_opts.add_option(cmdoptions.constraints())
+        self.cmd_opts.add_option(cmdoptions.build_constraints())
+        self.cmd_opts.add_option(cmdoptions.requirements_from_scripts())
+        self.cmd_opts.add_option(cmdoptions.no_deps())
+        self.cmd_opts.add_option(cmdoptions.only_deps())
+
+        self.cmd_opts.add_option(cmdoptions.editable())
+        self.cmd_opts.add_option(
+            "--dry-run",
+            action="store_true",
+            dest="dry_run",
+            default=False,
+            help=(
+                "Don't actually install anything, just print what would be. "
+                "Can be used in combination with --ignore-installed "
+                "to 'resolve' the requirements."
+            ),
+        )
+        self.cmd_opts.add_option(
+            "-t",
+            "--target",
+            dest="target_dir",
+            metavar="dir",
+            default=None,
+            help=(
+                "Install packages into . "
+                "By default this will not replace existing files/folders in "
+                ". Use --upgrade to replace existing packages in  "
+                "with new versions."
+            ),
+        )
+        cmdoptions.add_target_python_options(self.cmd_opts)
+
+        self.cmd_opts.add_option(
+            "--user",
+            dest="use_user_site",
+            action="store_true",
+            help=(
+                "Install to the Python user install directory for your "
+                "platform. Typically ~/.local/, or %APPDATA%\\Python on "
+                "Windows. (See the Python documentation for site.USER_BASE "
+                "for full details.)"
+            ),
+        )
+        self.cmd_opts.add_option(
+            "--no-user",
+            dest="use_user_site",
+            action="store_false",
+            help=SUPPRESS_HELP,
+        )
+        self.cmd_opts.add_option(
+            "--root",
+            dest="root_path",
+            metavar="dir",
+            default=None,
+            help="Install everything relative to this alternate root directory.",
+        )
+        self.cmd_opts.add_option(
+            "--prefix",
+            dest="prefix_path",
+            metavar="dir",
+            default=None,
+            help=(
+                "Installation prefix where lib, bin and other top-level "
+                "folders are placed. Note that the resulting installation may "
+                "contain scripts and other resources which reference the "
+                "Python interpreter of pip, and not that of ``--prefix``. "
+                "See also the ``--python`` option if the intention is to "
+                "install packages into another (possibly pip-free) "
+                "environment."
+            ),
+        )
+
+        self.cmd_opts.add_option(cmdoptions.src())
+
+        self.cmd_opts.add_option(
+            "-U",
+            "--upgrade",
+            dest="upgrade",
+            action="store_true",
+            help=(
+                "Upgrade all specified packages to the newest available "
+                "version. The handling of dependencies depends on the "
+                "upgrade-strategy used."
+            ),
+        )
+
+        self.cmd_opts.add_option(
+            "--upgrade-strategy",
+            dest="upgrade_strategy",
+            default="only-if-needed",
+            choices=["only-if-needed", "eager"],
+            help=(
+                "Determines how dependency upgrading should be handled "
+                "[default: %default]. "
+                '"eager" - dependencies are upgraded regardless of '
+                "whether the currently installed version satisfies the "
+                "requirements of the upgraded package(s). "
+                '"only-if-needed" -  are upgraded only when they do not '
+                "satisfy the requirements of the upgraded package(s)."
+            ),
+        )
+
+        self.cmd_opts.add_option(
+            "--force-reinstall",
+            dest="force_reinstall",
+            action="store_true",
+            help="Reinstall all packages even if they are already up-to-date.",
+        )
+
+        self.cmd_opts.add_option(
+            "-I",
+            "--ignore-installed",
+            dest="ignore_installed",
+            action="store_true",
+            help=(
+                "Ignore the installed packages, overwriting them. "
+                "This can break your system if the existing package "
+                "is of a different version or was installed "
+                "with a different package manager!"
+            ),
+        )
+
+        self.cmd_opts.add_option(cmdoptions.ignore_requires_python())
+        self.cmd_opts.add_option(cmdoptions.no_build_isolation())
+        self.cmd_opts.add_option(cmdoptions.use_pep517())
+        self.cmd_opts.add_option(cmdoptions.check_build_deps())
+        self.cmd_opts.add_option(cmdoptions.override_externally_managed())
+
+        self.cmd_opts.add_option(cmdoptions.config_settings())
+
+        self.cmd_opts.add_option(
+            "--compile",
+            action="store_true",
+            dest="compile",
+            default=True,
+            help="Compile Python source files to bytecode",
+        )
+
+        self.cmd_opts.add_option(
+            "--no-compile",
+            action="store_false",
+            dest="compile",
+            help="Do not compile Python source files to bytecode",
+        )
+
+        self.cmd_opts.add_option(
+            "--no-warn-script-location",
+            action="store_false",
+            dest="warn_script_location",
+            default=True,
+            help="Do not warn when installing scripts outside PATH",
+        )
+        self.cmd_opts.add_option(
+            "--no-warn-conflicts",
+            action="store_false",
+            dest="warn_about_conflicts",
+            default=True,
+            help="Do not warn about broken dependencies",
+        )
+        self.cmd_opts.add_option(cmdoptions.require_hashes())
+        self.cmd_opts.add_option(cmdoptions.no_require_hashes())
+        self.cmd_opts.add_option(cmdoptions.progress_bar())
+        self.cmd_opts.add_option(cmdoptions.root_user_action())
+
+        index_opts = cmdoptions.make_option_group(
+            cmdoptions.index_group,
+            self.parser,
+        )
+
+        selection_opts = cmdoptions.make_option_group(
+            cmdoptions.package_selection_group,
+            self.parser,
+        )
+
+        self.parser.insert_option_group(0, index_opts)
+        self.parser.insert_option_group(0, selection_opts)
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+        self.cmd_opts.add_option(
+            "--report",
+            dest="json_report_file",
+            metavar="file",
+            default=None,
+            help=(
+                "Generate a JSON file describing what pip did to install "
+                "the provided requirements. "
+                "Can be used in combination with --dry-run and --ignore-installed "
+                "to 'resolve' the requirements. "
+                "When - is used as file name it writes to stdout. "
+                "When writing to stdout, please combine with the --quiet option "
+                "to avoid mixing pip logging output with JSON output."
+            ),
+        )
+
+    @contextlib.contextmanager
+    def pip_version_check(self, options: Values, args: list[str]) -> Iterator[None]:
+        # Skip the self-version check when pip itself is a requirement. The
+        # running pip may be replaced mid-command, and the upgrade prompt
+        # is redundant.
+        if any(_arg_refers_to_pip(arg) for arg in args):
+            yield
+            return
+        with super().pip_version_check(options, args):
+            yield
+
+    @with_cleanup
+    def run(self, options: Values, args: list[str]) -> int:
+        if options.use_user_site and options.target_dir is not None:
+            raise CommandError("Can not combine '--user' and '--target'")
+
+        # Check whether the environment we're installing into is externally
+        # managed, as specified in PEP 668. Specifying --root, --target, or
+        # --prefix disables the check, since there's no reliable way to locate
+        # the EXTERNALLY-MANAGED file for those cases. An exception is also
+        # made specifically for "--dry-run --report" for convenience.
+        installing_into_current_environment = (
+            not (options.dry_run and options.json_report_file)
+            and options.root_path is None
+            and options.target_dir is None
+            and options.prefix_path is None
+        )
+        if (
+            installing_into_current_environment
+            and not options.override_externally_managed
+        ):
+            check_externally_managed()
+
+        upgrade_strategy = "to-satisfy-only"
+        if options.upgrade:
+            upgrade_strategy = options.upgrade_strategy
+
+        cmdoptions.check_only_deps_option_does_not_conflict(options)
+        cmdoptions.check_build_constraints(options)
+        cmdoptions.check_dist_restriction(options, check_target=True)
+        cmdoptions.check_release_control_exclusive(options)
+
+        logger.verbose("Using %s", get_pip_version())
+        options.use_user_site = decide_user_install(
+            options.use_user_site,
+            prefix_path=options.prefix_path,
+            target_dir=options.target_dir,
+            root_path=options.root_path,
+            isolated_mode=options.isolated_mode,
+        )
+
+        target_temp_dir: TempDirectory | None = None
+        target_temp_dir_path: str | None = None
+        if options.target_dir:
+            options.ignore_installed = True
+            options.target_dir = os.path.abspath(options.target_dir)
+            if (
+                # fmt: off
+                os.path.exists(options.target_dir) and
+                not os.path.isdir(options.target_dir)
+                # fmt: on
+            ):
+                raise CommandError(
+                    "Target path exists but is not a directory, will not continue."
+                )
+
+            # Create a target directory for using with the target option
+            target_temp_dir = TempDirectory(kind="target")
+            target_temp_dir_path = target_temp_dir.path
+            self.enter_context(target_temp_dir)
+
+        session = self.get_default_session(options)
+
+        target_python = make_target_python(options)
+        finder = self._build_package_finder(
+            options=options,
+            session=session,
+            target_python=target_python,
+            ignore_requires_python=options.ignore_requires_python,
+        )
+        build_tracker = self.enter_context(get_build_tracker())
+
+        directory = TempDirectory(
+            delete=not options.no_clean,
+            kind="install",
+            globally_managed=True,
+        )
+
+        try:
+            reqs = self.get_requirements(args, options, finder, session)
+
+            wheel_cache = WheelCache(options.cache_dir)
+
+            preparer = self.make_requirement_preparer(
+                temp_build_dir=directory,
+                options=options,
+                build_tracker=build_tracker,
+                session=session,
+                finder=finder,
+                use_user_site=options.use_user_site,
+                verbosity=self.verbosity,
+                allow_editables=True,
+            )
+            resolver = self.make_resolver(
+                preparer=preparer,
+                finder=finder,
+                options=options,
+                wheel_cache=wheel_cache,
+                use_user_site=options.use_user_site,
+                ignore_installed=options.ignore_installed,
+                ignore_requires_python=options.ignore_requires_python,
+                force_reinstall=options.force_reinstall,
+                upgrade_strategy=upgrade_strategy,
+                py_version_info=options.python_version,
+            )
+
+            self.trace_basic_info(finder)
+
+            requirement_set = resolver.resolve(
+                reqs, check_supported_wheels=not options.target_dir
+            )
+
+            if options.json_report_file:
+                report = InstallationReport(requirement_set.requirements_to_install)
+                if options.json_report_file == "-":
+                    print_json(data=report.to_dict())
+                else:
+                    with open(options.json_report_file, "w", encoding="utf-8") as f:
+                        json.dump(report.to_dict(), f, indent=2, ensure_ascii=False)
+
+            if options.dry_run:
+                would_install_items = sorted(
+                    (r.metadata["name"], r.metadata["version"])
+                    for r in requirement_set.requirements_to_install
+                )
+                if would_install_items:
+                    write_output(
+                        "Would install %s",
+                        " ".join("-".join(item) for item in would_install_items),
+                    )
+                return SUCCESS
+
+            # If there is any more preparation to do for the actual installation, do
+            # so now. This includes actually downloading the files in the case that
+            # we have been using PEP-658 metadata so far.
+            preparer.prepare_linked_requirements_more(
+                requirement_set.requirements.values()
+            )
+
+            try:
+                pip_req = requirement_set.get_requirement("pip")
+            except KeyError:
+                modifying_pip = False
+            else:
+                # If we're not replacing an already installed pip,
+                # we're not modifying it.
+                modifying_pip = pip_req.satisfied_by is None
+            protect_pip_from_modification_on_windows(modifying_pip=modifying_pip)
+
+            reqs_to_build = [
+                r for r in requirement_set.requirements_to_install if not r.is_wheel
+            ]
+
+            _, build_failures = build(
+                reqs_to_build,
+                wheel_cache=wheel_cache,
+                verify=True,
+                allow_editables=True,
+            )
+
+            if build_failures:
+                raise InstallWheelBuildError(build_failures)
+
+            to_install = resolver.get_installation_order(requirement_set)
+
+            # Check for conflicts in the package set we're installing.
+            conflicts: ConflictDetails | None = None
+            should_warn_about_conflicts = (
+                not options.ignore_dependencies and options.warn_about_conflicts
+            )
+            if should_warn_about_conflicts:
+                conflicts = self._determine_conflicts(to_install)
+
+            # Don't warn about script install locations if
+            # --target or --prefix has been specified
+            warn_script_location = options.warn_script_location
+            if options.target_dir or options.prefix_path:
+                warn_script_location = False
+
+            # Warn on late imports so we don't silently pick up a module
+            # from a distribution pip is about to install.
+            try:
+                _eagerly_import_modules()
+            finally:
+                _prevent_further_imports()
+
+            installed = install_given_reqs(
+                to_install,
+                root=options.root_path,
+                home=target_temp_dir_path,
+                prefix=options.prefix_path,
+                warn_script_location=warn_script_location,
+                use_user_site=options.use_user_site,
+                pycompile=options.compile,
+                progress_bar=options.progress_bar,
+            )
+
+            lib_locations = get_lib_location_guesses(
+                user=options.use_user_site,
+                home=target_temp_dir_path,
+                root=options.root_path,
+                prefix=options.prefix_path,
+                isolated=options.isolated_mode,
+            )
+            env = get_environment(lib_locations)
+
+            if conflicts is not None:
+                self._warn_about_conflicts(
+                    conflicts,
+                    resolver_variant=self.determine_resolver_variant(options),
+                )
+            if summary := installed_packages_summary(installed, env):
+                write_output(summary)
+        except OSError as error:
+            show_traceback = self.verbosity >= 1
+
+            message = create_os_error_message(
+                error,
+                show_traceback,
+                options.use_user_site,
+            )
+            logger.error(message, exc_info=show_traceback)
+
+            return ERROR
+
+        if options.target_dir:
+            assert target_temp_dir
+            self._handle_target_dir(
+                options.target_dir, target_temp_dir, options.upgrade
+            )
+        if options.root_user_action == "warn":
+            warn_if_run_as_root()
+        return SUCCESS
+
+    def _handle_target_dir(
+        self, target_dir: str, target_temp_dir: TempDirectory, upgrade: bool
+    ) -> None:
+        ensure_dir(target_dir)
+
+        # Checking both purelib and platlib directories for installed
+        # packages to be moved to target directory
+        lib_dir_list = []
+
+        # Checking both purelib and platlib directories for installed
+        # packages to be moved to target directory
+        scheme = get_scheme("", home=target_temp_dir.path)
+        purelib_dir = scheme.purelib
+        platlib_dir = scheme.platlib
+        data_dir = scheme.data
+
+        if os.path.exists(purelib_dir):
+            lib_dir_list.append(purelib_dir)
+        if os.path.exists(platlib_dir) and platlib_dir != purelib_dir:
+            lib_dir_list.append(platlib_dir)
+        if os.path.exists(data_dir):
+            lib_dir_list.append(data_dir)
+
+        for lib_dir in lib_dir_list:
+            for item in os.listdir(lib_dir):
+                if lib_dir == data_dir:
+                    ddir = os.path.join(data_dir, item)
+                    if any(s.startswith(ddir) for s in lib_dir_list[:-1]):
+                        continue
+                target_item_dir = os.path.join(target_dir, item)
+                if os.path.exists(target_item_dir):
+                    if not upgrade:
+                        logger.warning(
+                            "Target directory %s already exists. Specify "
+                            "--upgrade to force replacement.",
+                            target_item_dir,
+                        )
+                        continue
+                    if os.path.islink(target_item_dir):
+                        logger.warning(
+                            "Target directory %s already exists and is "
+                            "a link. pip will not automatically replace "
+                            "links, please remove if replacement is "
+                            "desired.",
+                            target_item_dir,
+                        )
+                        continue
+                    if os.path.isdir(target_item_dir):
+                        shutil.rmtree(target_item_dir)
+                    else:
+                        os.remove(target_item_dir)
+
+                shutil.move(os.path.join(lib_dir, item), target_item_dir)
+
+    def _determine_conflicts(
+        self, to_install: list[InstallRequirement]
+    ) -> ConflictDetails | None:
+        try:
+            return check_install_conflicts(to_install)
+        except Exception:
+            logger.exception(
+                "Error while checking for conflicts. Please file an issue on "
+                "pip's issue tracker: https://github.com/pypa/pip/issues/new"
+            )
+            return None
+
+    def _warn_about_conflicts(
+        self, conflict_details: ConflictDetails, resolver_variant: str
+    ) -> None:
+        package_set, (missing, conflicting) = conflict_details
+        if not missing and not conflicting:
+            return
+
+        parts: list[str] = []
+        if resolver_variant == "legacy":
+            parts.append(
+                "pip's legacy dependency resolver does not consider dependency "
+                "conflicts when selecting packages. This behaviour is the "
+                "source of the following dependency conflicts."
+            )
+        else:
+            assert resolver_variant == "resolvelib"
+            parts.append(
+                "pip's dependency resolver does not currently take into account "
+                "all the packages that are installed. This behaviour is the "
+                "source of the following dependency conflicts."
+            )
+
+        # NOTE: There is some duplication here, with commands/check.py
+        for project_name in missing:
+            version = package_set[project_name][0]
+            for dependency in missing[project_name]:
+                message = (
+                    f"{project_name} {version} requires {dependency[1]}, "
+                    "which is not installed."
+                )
+                parts.append(message)
+
+        for project_name in conflicting:
+            version = package_set[project_name][0]
+            for dep_name, dep_version, req in conflicting[project_name]:
+                message = (
+                    "{name} {version} requires {requirement}, but {you} have "
+                    "{dep_name} {dep_version} which is incompatible."
+                ).format(
+                    name=project_name,
+                    version=version,
+                    requirement=req,
+                    dep_name=dep_name,
+                    dep_version=dep_version,
+                    you=("you" if resolver_variant == "resolvelib" else "you'll"),
+                )
+                parts.append(message)
+
+        logger.critical("\n".join(parts))
+
+
+def installed_packages_summary(
+    installed: list[InstallationResult], env: BaseEnvironment
+) -> str:
+    # Format a summary of installed packages, with extra care to
+    # display a package name as it was requested by the user.
+    installed.sort(key=operator.attrgetter("name"))
+    summary = []
+    installed_versions = {}
+    for distribution in env.iter_all_distributions():
+        installed_versions[distribution.canonical_name] = distribution.version
+    for package in installed:
+        display_name = package.name
+        version = installed_versions.get(canonicalize_name(display_name), None)
+        if version:
+            text = f"{display_name}-{version}"
+        else:
+            text = display_name
+        summary.append(text)
+
+    if not summary:
+        return ""
+    return f"Successfully installed {' '.join(summary)}"
+
+
+def get_lib_location_guesses(
+    user: bool = False,
+    home: str | None = None,
+    root: str | None = None,
+    isolated: bool = False,
+    prefix: str | None = None,
+) -> list[str]:
+    scheme = get_scheme(
+        "",
+        user=user,
+        home=home,
+        root=root,
+        isolated=isolated,
+        prefix=prefix,
+    )
+    return [scheme.purelib, scheme.platlib]
+
+
+def site_packages_writable(root: str | None, isolated: bool) -> bool:
+    return all(
+        test_writable_dir(d)
+        for d in set(get_lib_location_guesses(root=root, isolated=isolated))
+    )
+
+
+def decide_user_install(
+    use_user_site: bool | None,
+    prefix_path: str | None = None,
+    target_dir: str | None = None,
+    root_path: str | None = None,
+    isolated_mode: bool = False,
+) -> bool:
+    """Determine whether to do a user install based on the input options.
+
+    If use_user_site is False, no additional checks are done.
+    If use_user_site is True, it is checked for compatibility with other
+    options.
+    If use_user_site is None, the default behaviour depends on the environment,
+    which is provided by the other arguments.
+    """
+    # In some cases (config from tox), use_user_site can be set to an integer
+    # rather than a bool, which 'use_user_site is False' wouldn't catch.
+    if (use_user_site is not None) and (not use_user_site):
+        logger.debug("Non-user install by explicit request")
+        return False
+
+    # If we have been asked for a user install explicitly, check compatibility.
+    if use_user_site:
+        if prefix_path:
+            raise CommandError(
+                "Can not combine '--user' and '--prefix' as they imply "
+                "different installation locations"
+            )
+        if virtualenv_no_global():
+            raise InstallationError(
+                "Can not perform a '--user' install. User site-packages "
+                "are not visible in this virtualenv."
+            )
+        # Catch all remaining cases which honour the site.ENABLE_USER_SITE
+        # value, such as a plain Python installation (e.g. no virtualenv).
+        if not site.ENABLE_USER_SITE:
+            raise InstallationError(
+                "Can not perform a '--user' install. User site-packages "
+                "are disabled for this Python."
+            )
+        logger.debug("User install by explicit request")
+        return True
+
+    # If we are here, user installs have not been explicitly requested/avoided
+    assert use_user_site is None
+
+    # user install incompatible with --prefix/--target
+    if prefix_path or target_dir:
+        logger.debug("Non-user install due to --prefix or --target option")
+        return False
+
+    # If user installs are not enabled, choose a non-user install
+    if not site.ENABLE_USER_SITE:
+        logger.debug("Non-user install because user site-packages disabled")
+        return False
+
+    # If we have permission for a non-user install, do that,
+    # otherwise do a user install.
+    if site_packages_writable(root=root_path, isolated=isolated_mode):
+        logger.debug("Non-user install because site-packages writeable")
+        return False
+
+    logger.info(
+        "Defaulting to user installation because normal site-packages "
+        "is not writeable"
+    )
+    return True
+
+
+def create_os_error_message(
+    error: OSError, show_traceback: bool, using_user_site: bool
+) -> str:
+    """Format an error message for an OSError
+
+    It may occur anytime during the execution of the install command.
+    """
+    parts = []
+
+    # Mention the error if we are not going to show a traceback
+    parts.append("Could not install packages due to an OSError")
+    if not show_traceback:
+        parts.append(": ")
+        parts.append(str(error))
+    else:
+        parts.append(".")
+
+    # Spilt the error indication from a helper message (if any)
+    parts[-1] += "\n"
+
+    # Suggest useful actions to the user:
+    #  (1) using user site-packages or (2) verifying the permissions
+    if error.errno == errno.EACCES:
+        user_option_part = "Consider using the `--user` option"
+        permissions_part = "Check the permissions"
+
+        if not running_under_virtualenv() and not using_user_site:
+            parts.extend(
+                [
+                    user_option_part,
+                    " or ",
+                    permissions_part.lower(),
+                ]
+            )
+        else:
+            parts.append(permissions_part)
+        parts.append(".\n")
+
+    # Suggest to check "pip config debug" in case of invalid proxy
+    if type(error) is InvalidProxyURL:
+        parts.append(
+            'Consider checking your local proxy configuration with "pip config debug"'
+        )
+        parts.append(".\n")
+
+    # On Windows, errors like EINVAL or ENOENT may occur
+    # if a file or folder name exceeds 255 characters,
+    # or if the full path exceeds 260 characters and long path support isn't enabled.
+    # This condition checks for such cases and adds a hint to the error output.
+
+    if WINDOWS and error.errno in (errno.EINVAL, errno.ENOENT) and error.filename:
+        if any(len(part) > 255 for part in Path(error.filename).parts):
+            parts.append(
+                "HINT: This error might be caused by a file or folder name exceeding "
+                "255 characters, which is a Windows limitation even if long paths "
+                "are enabled.\n "
+            )
+        if len(error.filename) > 260:
+            parts.append(
+                "HINT: This error might have occurred since "
+                "this system does not have Windows Long Path "
+                "support enabled. You can find information on "
+                "how to enable this at "
+                "https://pip.pypa.io/warnings/enable-long-paths\n"
+            )
+    return "".join(parts).strip() + "\n"
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/list.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/list.py
new file mode 100644
index 0000000..3d82d07
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/list.py
@@ -0,0 +1,408 @@
+from __future__ import annotations
+
+import contextlib
+import json
+import logging
+from collections.abc import Generator, Iterator, Sequence
+from email.parser import Parser
+from optparse import Values
+from typing import TYPE_CHECKING, cast
+
+from pip._vendor.packaging.utils import canonicalize_name
+from pip._vendor.packaging.version import InvalidVersion, Version
+
+from pip._internal.cli import cmdoptions
+from pip._internal.cli.index_command import IndexGroupCommand
+from pip._internal.cli.status_codes import SUCCESS
+from pip._internal.exceptions import CommandError
+from pip._internal.metadata import BaseDistribution, get_environment
+from pip._internal.metadata.base import stdlib_pkgs
+from pip._internal.models.selection_prefs import SelectionPreferences
+from pip._internal.utils.misc import tabulate, write_output
+
+if TYPE_CHECKING:
+    from pip._internal.index.package_finder import PackageFinder
+    from pip._internal.network.session import PipSession
+
+    class _DistWithLatestInfo(BaseDistribution):
+        """Give the distribution object a couple of extra fields.
+
+        These will be populated during ``get_outdated()``. This is dirty but
+        makes the rest of the code much cleaner.
+        """
+
+        latest_version: Version
+        latest_filetype: str
+
+    _ProcessedDists = Sequence[_DistWithLatestInfo]
+
+
+logger = logging.getLogger(__name__)
+
+
+class ListCommand(IndexGroupCommand):
+    """
+    List installed packages, including editables.
+
+    Packages are listed in a case-insensitive sorted order.
+    """
+
+    ignore_require_venv = True
+    usage = """
+      %prog [options]"""
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(
+            "-o",
+            "--outdated",
+            action="store_true",
+            default=False,
+            help="List outdated packages",
+        )
+        self.cmd_opts.add_option(
+            "-u",
+            "--uptodate",
+            action="store_true",
+            default=False,
+            help="List uptodate packages",
+        )
+        self.cmd_opts.add_option(
+            "-e",
+            "--editable",
+            action="store_true",
+            default=False,
+            help="List editable projects.",
+        )
+        self.cmd_opts.add_option(
+            "-l",
+            "--local",
+            action="store_true",
+            default=False,
+            help=(
+                "If in a virtualenv that has global access, do not list "
+                "globally-installed packages."
+            ),
+        )
+        self.cmd_opts.add_option(
+            "--user",
+            dest="user",
+            action="store_true",
+            default=False,
+            help="Only output packages installed in user-site.",
+        )
+        self.cmd_opts.add_option(cmdoptions.list_path())
+
+        self.cmd_opts.add_option(
+            "--format",
+            action="store",
+            dest="list_format",
+            default="columns",
+            choices=("columns", "freeze", "json"),
+            help=(
+                "Select the output format among: columns (default), freeze, or json. "
+                "The 'freeze' format cannot be used with the --outdated option."
+            ),
+        )
+
+        self.cmd_opts.add_option(
+            "--not-required",
+            action="store_true",
+            dest="not_required",
+            help="List packages that are not dependencies of installed packages.",
+        )
+
+        self.cmd_opts.add_option(
+            "--exclude-editable",
+            action="store_false",
+            dest="include_editable",
+            help="Exclude editable package from output.",
+        )
+        self.cmd_opts.add_option(
+            "--include-editable",
+            action="store_true",
+            dest="include_editable",
+            help="Include editable package in output.",
+            default=True,
+        )
+        self.cmd_opts.add_option(cmdoptions.list_exclude())
+        index_opts = cmdoptions.make_option_group(cmdoptions.index_group, self.parser)
+
+        selection_opts = cmdoptions.make_option_group(
+            cmdoptions.package_selection_group,
+            self.parser,
+        )
+
+        self.parser.insert_option_group(0, index_opts)
+        self.parser.insert_option_group(0, selection_opts)
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    @contextlib.contextmanager
+    def pip_version_check(self, options: Values, args: list[str]) -> Iterator[None]:
+        if not (options.outdated or options.uptodate):
+            yield
+            return
+        with super().pip_version_check(options, args):
+            yield
+
+    def _build_package_finder(
+        self, options: Values, session: PipSession
+    ) -> PackageFinder:
+        """
+        Create a package finder appropriate to this list command.
+        """
+        # Lazy import the heavy index modules as most list invocations won't need 'em.
+        from pip._internal.index.collector import LinkCollector
+        from pip._internal.index.package_finder import PackageFinder
+
+        link_collector = LinkCollector.create(session, options=options)
+
+        # Pass allow_yanked=False to ignore yanked versions.
+        selection_prefs = SelectionPreferences(
+            allow_yanked=False,
+            release_control=options.release_control,
+            format_control=options.format_control,
+            prefer_binary=options.prefer_binary,
+        )
+
+        return PackageFinder.create(
+            link_collector=link_collector,
+            selection_prefs=selection_prefs,
+            uploaded_prior_to=options.uploaded_prior_to,
+        )
+
+    def run(self, options: Values, args: list[str]) -> int:
+        cmdoptions.check_release_control_exclusive(options)
+
+        if options.outdated and options.uptodate:
+            raise CommandError("Options --outdated and --uptodate cannot be combined.")
+
+        if options.outdated and options.list_format == "freeze":
+            raise CommandError(
+                "List format 'freeze' cannot be used with the --outdated option."
+            )
+
+        cmdoptions.check_list_path_option(options)
+
+        packages: _ProcessedDists = [
+            cast("_DistWithLatestInfo", d)
+            for d in get_environment(options.path).iter_installed_distributions(
+                local_only=options.local,
+                user_only=options.user,
+                editables_only=options.editable,
+                include_editables=options.include_editable,
+                skip=set(stdlib_pkgs),
+            )
+        ]
+
+        # get_not_required must be called firstly in order to find and
+        # filter out all dependencies correctly. Otherwise a package
+        # can't be identified as requirement because some parent packages
+        # could be filtered out before.
+        if options.not_required:
+            packages = self.get_not_required(packages, options)
+
+        if options.excludes:
+            excluded_names = {canonicalize_name(n) for n in options.excludes}
+            packages = [
+                dist for dist in packages if dist.canonical_name not in excluded_names
+            ]
+
+        if options.outdated:
+            packages = self.get_outdated(packages, options)
+        elif options.uptodate:
+            packages = self.get_uptodate(packages, options)
+
+        self.output_package_listing(packages, options)
+        return SUCCESS
+
+    def get_outdated(
+        self, packages: _ProcessedDists, options: Values
+    ) -> _ProcessedDists:
+        return [
+            dist
+            for dist in self.iter_packages_latest_infos(packages, options)
+            if dist.latest_version > dist.version
+        ]
+
+    def get_uptodate(
+        self, packages: _ProcessedDists, options: Values
+    ) -> _ProcessedDists:
+        return [
+            dist
+            for dist in self.iter_packages_latest_infos(packages, options)
+            if dist.latest_version == dist.version
+        ]
+
+    def get_not_required(
+        self, packages: _ProcessedDists, options: Values
+    ) -> _ProcessedDists:
+        dep_keys = {
+            canonicalize_name(dep.name)
+            for dist in packages
+            for dep in (dist.iter_dependencies() or ())
+        }
+
+        # Create a set to remove duplicate packages, and cast it to a list
+        # to keep the return type consistent with get_outdated and
+        # get_uptodate
+        return list({pkg for pkg in packages if pkg.canonical_name not in dep_keys})
+
+    def iter_packages_latest_infos(
+        self, packages: _ProcessedDists, options: Values
+    ) -> Generator[_DistWithLatestInfo, None, None]:
+        with self._build_session(options) as session:
+            finder = self._build_package_finder(options, session)
+
+            def latest_info(
+                dist: _DistWithLatestInfo,
+            ) -> _DistWithLatestInfo | None:
+                all_candidates = finder.find_all_candidates(dist.canonical_name)
+                if self.should_exclude_prerelease(options, dist.canonical_name):
+                    all_candidates = [
+                        candidate
+                        for candidate in all_candidates
+                        if not candidate.version.is_prerelease
+                    ]
+
+                evaluator = finder.make_candidate_evaluator(
+                    project_name=dist.canonical_name,
+                )
+                best_candidate = evaluator.sort_best_candidate(all_candidates)
+                if best_candidate is None:
+                    return None
+
+                remote_version = best_candidate.version
+                if best_candidate.link.is_wheel:
+                    typ = "wheel"
+                else:
+                    typ = "sdist"
+                dist.latest_version = remote_version
+                dist.latest_filetype = typ
+                return dist
+
+            for dist in map(latest_info, packages):
+                if dist is not None:
+                    yield dist
+
+    def output_package_listing(
+        self, packages: _ProcessedDists, options: Values
+    ) -> None:
+        packages = sorted(
+            packages,
+            key=lambda dist: dist.canonical_name,
+        )
+        if options.list_format == "columns" and packages:
+            data, header = format_for_columns(packages, options)
+            self.output_package_listing_columns(data, header)
+        elif options.list_format == "freeze":
+            for dist in packages:
+                try:
+                    req_string = f"{dist.raw_name}=={dist.version}"
+                except InvalidVersion:
+                    req_string = f"{dist.raw_name}==={dist.raw_version}"
+                if options.verbose >= 1:
+                    write_output("%s (%s)", req_string, dist.location)
+                else:
+                    write_output(req_string)
+        elif options.list_format == "json":
+            write_output(format_for_json(packages, options))
+
+    def output_package_listing_columns(
+        self, data: list[list[str]], header: list[str]
+    ) -> None:
+        # insert the header first: we need to know the size of column names
+        if len(data) > 0:
+            data.insert(0, header)
+
+        pkg_strings, sizes = tabulate(data)
+
+        # Create and add a separator.
+        if len(data) > 0:
+            pkg_strings.insert(1, " ".join("-" * x for x in sizes))
+
+        for val in pkg_strings:
+            write_output(val)
+
+
+def format_for_columns(
+    pkgs: _ProcessedDists, options: Values
+) -> tuple[list[list[str]], list[str]]:
+    """
+    Convert the package data into something usable
+    by output_package_listing_columns.
+    """
+    header = ["Package", "Version"]
+
+    running_outdated = options.outdated
+    if running_outdated:
+        header.extend(["Latest", "Type"])
+
+    def wheel_build_tag(dist: BaseDistribution) -> str | None:
+        try:
+            wheel_file = dist.read_text("WHEEL")
+        except FileNotFoundError:
+            return None
+        return Parser().parsestr(wheel_file).get("Build")
+
+    build_tags = [wheel_build_tag(p) for p in pkgs]
+    has_build_tags = any(build_tags)
+    if has_build_tags:
+        header.append("Build")
+
+    has_editables = any(x.editable for x in pkgs)
+    if has_editables:
+        header.append("Editable project location")
+
+    if options.verbose >= 1:
+        header.append("Location")
+    if options.verbose >= 1:
+        header.append("Installer")
+
+    data = []
+    for i, proj in enumerate(pkgs):
+        # if we're working on the 'outdated' list, separate out the
+        # latest_version and type
+        row = [proj.raw_name, proj.raw_version]
+
+        if running_outdated:
+            row.append(str(proj.latest_version))
+            row.append(proj.latest_filetype)
+
+        if has_build_tags:
+            row.append(build_tags[i] or "")
+
+        if has_editables:
+            row.append(proj.editable_project_location or "")
+
+        if options.verbose >= 1:
+            row.append(proj.location or "")
+        if options.verbose >= 1:
+            row.append(proj.installer)
+
+        data.append(row)
+
+    return data, header
+
+
+def format_for_json(packages: _ProcessedDists, options: Values) -> str:
+    data = []
+    for dist in packages:
+        try:
+            version = str(dist.version)
+        except InvalidVersion:
+            version = dist.raw_version
+        info = {
+            "name": dist.raw_name,
+            "version": version,
+        }
+        if options.verbose >= 1:
+            info["location"] = dist.location or ""
+            info["installer"] = dist.installer
+        if options.outdated:
+            info["latest_version"] = str(dist.latest_version)
+            info["latest_filetype"] = dist.latest_filetype
+        editable_project_location = dist.editable_project_location
+        if editable_project_location:
+            info["editable_project_location"] = editable_project_location
+        data.append(info)
+    return json.dumps(data)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/lock.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/lock.py
new file mode 100644
index 0000000..73df864
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/lock.py
@@ -0,0 +1,173 @@
+import sys
+from optparse import Values
+from pathlib import Path
+
+from pip._vendor import tomli_w
+from pip._vendor.packaging.pylock import is_valid_pylock_path
+
+from pip._internal.cache import WheelCache
+from pip._internal.cli import cmdoptions
+from pip._internal.cli.req_command import (
+    RequirementCommand,
+    with_cleanup,
+)
+from pip._internal.cli.status_codes import SUCCESS
+from pip._internal.operations.build.build_tracker import get_build_tracker
+from pip._internal.utils.logging import getLogger
+from pip._internal.utils.misc import (
+    get_pip_version,
+)
+from pip._internal.utils.pylock import pylock_from_install_requirements
+from pip._internal.utils.temp_dir import TempDirectory
+
+logger = getLogger(__name__)
+
+
+class LockCommand(RequirementCommand):
+    """
+    EXPERIMENTAL - Lock packages and their dependencies from:
+
+    - PyPI (and other indexes) using requirement specifiers.
+    - VCS project urls.
+    - Local project directories.
+    - Local or remote source archives.
+
+    pip also supports locking from "requirements files", which provide an easy
+    way to specify a whole environment to be installed.
+
+    The generated lock file is only guaranteed to be valid for the current
+    python version and platform.
+    """
+
+    usage = """
+      %prog [options] [-e]  ...
+      %prog [options]  [package-index-options] ...
+      %prog [options] -r  [package-index-options] ...
+      %prog [options]  ..."""
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(
+            cmdoptions.PipOption(
+                "--output",
+                "-o",
+                dest="output_file",
+                metavar="path",
+                type="path",
+                default="pylock.toml",
+                help="Lock file name (default=pylock.toml). Use - for stdout.",
+            )
+        )
+        self.cmd_opts.add_option(cmdoptions.requirements())
+        self.cmd_opts.add_option(cmdoptions.requirements_from_scripts())
+        self.cmd_opts.add_option(cmdoptions.constraints())
+        self.cmd_opts.add_option(cmdoptions.build_constraints())
+        self.cmd_opts.add_option(cmdoptions.no_deps())
+        self.cmd_opts.add_option(cmdoptions.only_deps())
+
+        self.cmd_opts.add_option(cmdoptions.editable())
+
+        self.cmd_opts.add_option(cmdoptions.src())
+
+        self.cmd_opts.add_option(cmdoptions.ignore_requires_python())
+        self.cmd_opts.add_option(cmdoptions.no_build_isolation())
+        self.cmd_opts.add_option(cmdoptions.use_pep517())
+        self.cmd_opts.add_option(cmdoptions.check_build_deps())
+
+        self.cmd_opts.add_option(cmdoptions.config_settings())
+
+        self.cmd_opts.add_option(cmdoptions.require_hashes())
+        self.cmd_opts.add_option(cmdoptions.no_require_hashes())
+        self.cmd_opts.add_option(cmdoptions.progress_bar())
+
+        index_opts = cmdoptions.make_option_group(
+            cmdoptions.index_group,
+            self.parser,
+        )
+
+        selection_opts = cmdoptions.make_option_group(
+            cmdoptions.package_selection_group,
+            self.parser,
+        )
+
+        self.parser.insert_option_group(0, index_opts)
+        self.parser.insert_option_group(0, selection_opts)
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    @with_cleanup
+    def run(self, options: Values, args: list[str]) -> int:
+        logger.verbose("Using %s", get_pip_version())
+
+        logger.warning(
+            "pip lock is currently an experimental command. "
+            "It may be removed/changed in a future release "
+            "without prior warning."
+        )
+
+        cmdoptions.check_build_constraints(options)
+        cmdoptions.check_release_control_exclusive(options)
+        cmdoptions.check_only_deps_option_does_not_conflict(options)
+
+        session = self.get_default_session(options)
+
+        finder = self._build_package_finder(
+            options=options,
+            session=session,
+            ignore_requires_python=options.ignore_requires_python,
+        )
+        build_tracker = self.enter_context(get_build_tracker())
+
+        directory = TempDirectory(
+            delete=not options.no_clean,
+            kind="install",
+            globally_managed=True,
+        )
+
+        reqs = self.get_requirements(args, options, finder, session)
+
+        wheel_cache = WheelCache(options.cache_dir)
+
+        preparer = self.make_requirement_preparer(
+            temp_build_dir=directory,
+            options=options,
+            build_tracker=build_tracker,
+            session=session,
+            finder=finder,
+            use_user_site=False,
+            verbosity=self.verbosity,
+            allow_editables=True,
+        )
+        resolver = self.make_resolver(
+            preparer=preparer,
+            finder=finder,
+            options=options,
+            wheel_cache=wheel_cache,
+            use_user_site=False,
+            ignore_installed=True,
+            ignore_requires_python=options.ignore_requires_python,
+            upgrade_strategy="to-satisfy-only",
+        )
+
+        self.trace_basic_info(finder)
+
+        requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
+
+        if options.output_file == "-":
+            base_dir = Path.cwd()
+        else:
+            output_file_path = Path(options.output_file)
+            if not is_valid_pylock_path(output_file_path):
+                logger.warning(
+                    "%s is not a valid lock file name.",
+                    output_file_path,
+                )
+            base_dir = output_file_path.parent
+        pylock = pylock_from_install_requirements(
+            requirement_set.requirements.values(), base_dir=base_dir
+        )
+        pylock_toml = tomli_w.dumps(pylock.to_dict())
+        if options.output_file == "-":
+            sys.stdout.write(pylock_toml)
+        else:
+            output_file_path.write_text(pylock_toml, encoding="utf-8")
+
+        return SUCCESS
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/search.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/search.py
new file mode 100644
index 0000000..b8dbc27
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/search.py
@@ -0,0 +1,178 @@
+from __future__ import annotations
+
+import logging
+import shutil
+import sys
+import textwrap
+import xmlrpc.client
+from collections import OrderedDict
+from optparse import Values
+from typing import TypedDict
+
+from pip._vendor.packaging.version import parse as parse_version
+
+from pip._internal.cli.base_command import Command
+from pip._internal.cli.req_command import SessionCommandMixin
+from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS
+from pip._internal.exceptions import CommandError
+from pip._internal.metadata import get_default_environment
+from pip._internal.metadata.base import BaseDistribution
+from pip._internal.models.index import PyPI
+from pip._internal.network.xmlrpc import PipXmlrpcTransport
+from pip._internal.utils.logging import indent_log
+from pip._internal.utils.misc import write_output
+
+
+class TransformedHit(TypedDict):
+    name: str
+    summary: str
+    versions: list[str]
+
+
+logger = logging.getLogger(__name__)
+
+
+class SearchCommand(Command, SessionCommandMixin):
+    """Search for PyPI packages whose name or summary contains ."""
+
+    usage = """
+      %prog [options] """
+    ignore_require_venv = True
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(
+            "-i",
+            "--index",
+            dest="index",
+            metavar="URL",
+            default=PyPI.pypi_url,
+            help="Base URL of Python Package Index (default %default)",
+        )
+
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    def run(self, options: Values, args: list[str]) -> int:
+        if not args:
+            raise CommandError("Missing required argument (search query).")
+        query = args
+        pypi_hits = self.search(query, options)
+        hits = transform_hits(pypi_hits)
+
+        terminal_width = None
+        if sys.stdout.isatty():
+            terminal_width = shutil.get_terminal_size()[0]
+
+        print_results(hits, terminal_width=terminal_width)
+        if pypi_hits:
+            return SUCCESS
+        return NO_MATCHES_FOUND
+
+    def search(self, query: list[str], options: Values) -> list[dict[str, str]]:
+        index_url = options.index
+
+        session = self.get_default_session(options)
+
+        transport = PipXmlrpcTransport(index_url, session)
+        pypi = xmlrpc.client.ServerProxy(index_url, transport)
+        try:
+            hits = pypi.search({"name": query, "summary": query}, "or")
+        except xmlrpc.client.Fault as fault:
+            message = (
+                f"XMLRPC request failed [code: {fault.faultCode}]\n{fault.faultString}"
+            )
+            raise CommandError(message)
+        assert isinstance(hits, list)
+        return hits
+
+
+def transform_hits(hits: list[dict[str, str]]) -> list[TransformedHit]:
+    """
+    The list from pypi is really a list of versions. We want a list of
+    packages with the list of versions stored inline. This converts the
+    list from pypi into one we can use.
+    """
+    packages: dict[str, TransformedHit] = OrderedDict()
+    for hit in hits:
+        name = hit["name"]
+        summary = hit["summary"]
+        version = hit["version"]
+
+        if name not in packages.keys():
+            packages[name] = {
+                "name": name,
+                "summary": summary,
+                "versions": [version],
+            }
+        else:
+            packages[name]["versions"].append(version)
+
+            # if this is the highest version, replace summary and score
+            if version == highest_version(packages[name]["versions"]):
+                packages[name]["summary"] = summary
+
+    return list(packages.values())
+
+
+def print_dist_installation_info(latest: str, dist: BaseDistribution | None) -> None:
+    if dist is not None:
+        with indent_log():
+            if dist.version == latest:
+                write_output("INSTALLED: %s (latest)", dist.version)
+            else:
+                write_output("INSTALLED: %s", dist.version)
+                if parse_version(latest).pre:
+                    write_output(
+                        "LATEST:    %s (pre-release; install"
+                        " with `pip install --pre`)",
+                        latest,
+                    )
+                else:
+                    write_output("LATEST:    %s", latest)
+
+
+def get_installed_distribution(name: str) -> BaseDistribution | None:
+    env = get_default_environment()
+    return env.get_distribution(name)
+
+
+def print_results(
+    hits: list[TransformedHit],
+    name_column_width: int | None = None,
+    terminal_width: int | None = None,
+) -> None:
+    if not hits:
+        return
+    if name_column_width is None:
+        name_column_width = (
+            max(
+                [
+                    len(hit["name"]) + len(highest_version(hit.get("versions", ["-"])))
+                    for hit in hits
+                ]
+            )
+            + 4
+        )
+
+    for hit in hits:
+        name = hit["name"]
+        summary = hit["summary"] or ""
+        latest = highest_version(hit.get("versions", ["-"]))
+        if terminal_width is not None:
+            target_width = terminal_width - name_column_width - 5
+            if target_width > 10:
+                # wrap and indent summary to fit terminal
+                summary_lines = textwrap.wrap(summary, target_width)
+                summary = ("\n" + " " * (name_column_width + 3)).join(summary_lines)
+
+        name_latest = f"{name} ({latest})"
+        line = f"{name_latest:{name_column_width}} - {summary}"
+        try:
+            write_output(line)
+            dist = get_installed_distribution(name)
+            print_dist_installation_info(latest, dist)
+        except UnicodeEncodeError:
+            pass
+
+
+def highest_version(versions: list[str]) -> str:
+    return max(versions, key=parse_version)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/show.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/show.py
new file mode 100644
index 0000000..40601a2
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/show.py
@@ -0,0 +1,237 @@
+from __future__ import annotations
+
+import logging
+import string
+from collections.abc import Generator, Iterable, Iterator
+from optparse import Values
+from typing import NamedTuple
+
+from pip._vendor.packaging.requirements import InvalidRequirement
+from pip._vendor.packaging.utils import canonicalize_name
+
+from pip._internal.cli.base_command import Command
+from pip._internal.cli.status_codes import ERROR, SUCCESS
+from pip._internal.metadata import BaseDistribution, get_default_environment
+from pip._internal.utils.misc import write_output
+
+logger = logging.getLogger(__name__)
+
+
+def normalize_project_url_label(label: str) -> str:
+    # This logic is from PEP 753 (Well-known Project URLs in Metadata).
+    chars_to_remove = string.punctuation + string.whitespace
+    removal_map = str.maketrans("", "", chars_to_remove)
+    return label.translate(removal_map).lower()
+
+
+class ShowCommand(Command):
+    """
+    Show information about one or more installed packages.
+
+    The output is in RFC-compliant mail header format.
+    """
+
+    usage = """
+      %prog [options]  ..."""
+    ignore_require_venv = True
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(
+            "-f",
+            "--files",
+            dest="files",
+            action="store_true",
+            default=False,
+            help="Show the full list of installed files for each package.",
+        )
+
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    def run(self, options: Values, args: list[str]) -> int:
+        if not args:
+            logger.warning("ERROR: Please provide a package name or names.")
+            return ERROR
+        query = args
+
+        results = search_packages_info(query, include_files=options.files)
+        if not print_results(
+            results, list_files=options.files, verbose=options.verbose
+        ):
+            return ERROR
+        return SUCCESS
+
+
+class _PackageInfo(NamedTuple):
+    name: str
+    version: str
+    location: str
+    editable_project_location: str | None
+    requires: list[str]
+    required_by: list[str]
+    installer: str
+    metadata_version: str
+    classifiers: list[str]
+    summary: str
+    homepage: str
+    project_urls: list[str]
+    author: str
+    author_email: str
+    license: str
+    license_expression: str
+    entry_points: list[str]
+    files: list[str] | None
+
+
+def search_packages_info(
+    query: list[str],
+    include_files: bool,
+) -> Generator[_PackageInfo, None, None]:
+    """
+    Gather details from installed distributions. Print distribution name,
+    version, location, and installed files. Installed files requires a
+    pip generated 'installed-files.txt' in the distributions '.egg-info'
+    directory.
+    """
+    env = get_default_environment()
+
+    installed = {dist.canonical_name: dist for dist in env.iter_all_distributions()}
+    query_names = [canonicalize_name(name) for name in query]
+    missing = sorted(
+        [name for name, pkg in zip(query, query_names) if pkg not in installed]
+    )
+    if missing:
+        logger.warning("Package(s) not found: %s", ", ".join(missing))
+
+    def _get_requiring_packages(current_dist: BaseDistribution) -> Iterator[str]:
+        return (
+            dist.metadata["Name"] or "UNKNOWN"
+            for dist in installed.values()
+            if current_dist.canonical_name
+            in {canonicalize_name(d.name) for d in dist.iter_dependencies()}
+        )
+
+    for query_name in query_names:
+        try:
+            dist = installed[query_name]
+        except KeyError:
+            continue
+
+        try:
+            requires = sorted(
+                # Avoid duplicates in requirements (e.g. due to environment markers).
+                {req.name for req in dist.iter_dependencies()},
+                key=str.lower,
+            )
+        except InvalidRequirement:
+            requires = sorted(dist.iter_raw_dependencies(), key=str.lower)
+
+        try:
+            required_by = sorted(_get_requiring_packages(dist), key=str.lower)
+        except InvalidRequirement:
+            required_by = ["#N/A"]
+
+        try:
+            entry_points_text = dist.read_text("entry_points.txt")
+            entry_points = entry_points_text.splitlines(keepends=False)
+        except FileNotFoundError:
+            entry_points = []
+
+        files: list[str] | None = None
+        if include_files:
+            files_iter = dist.iter_declared_entries()
+            if files_iter is not None:
+                files = sorted(files_iter)
+
+        metadata = dist.metadata
+
+        project_urls = metadata.get_all("Project-URL", [])
+        homepage = metadata.get("Home-page", "")
+        if not homepage:
+            # It's common that there is a "homepage" Project-URL, but Home-page
+            # remains unset (especially as PEP 621 doesn't surface the field).
+            for url in project_urls:
+                url_label, url = url.split(",", maxsplit=1)
+                normalized_label = normalize_project_url_label(url_label)
+                if normalized_label == "homepage":
+                    homepage = url.strip()
+                    break
+
+        yield _PackageInfo(
+            name=dist.raw_name,
+            version=dist.raw_version,
+            location=dist.location or "",
+            editable_project_location=dist.editable_project_location,
+            requires=requires,
+            required_by=required_by,
+            installer=dist.installer,
+            metadata_version=dist.metadata_version or "",
+            classifiers=metadata.get_all("Classifier", []),
+            summary=metadata.get("Summary", ""),
+            homepage=homepage,
+            project_urls=project_urls,
+            author=metadata.get("Author", ""),
+            author_email=metadata.get("Author-email", ""),
+            license=metadata.get("License", ""),
+            license_expression=metadata.get("License-Expression", ""),
+            entry_points=entry_points,
+            files=files,
+        )
+
+
+def print_results(
+    distributions: Iterable[_PackageInfo],
+    list_files: bool,
+    verbose: bool,
+) -> bool:
+    """
+    Print the information from installed distributions found.
+    """
+    results_printed = False
+    for i, dist in enumerate(distributions):
+        results_printed = True
+        if i > 0:
+            write_output("---")
+
+        metadata_version = dist.metadata_version
+        metadata_version_tuple = (
+            tuple(map(int, metadata_version.split("."))) if metadata_version else ()
+        )
+
+        write_output("Name: %s", dist.name)
+        write_output("Version: %s", dist.version)
+        write_output("Summary: %s", dist.summary)
+        write_output("Home-page: %s", dist.homepage)
+        write_output("Author: %s", dist.author)
+        write_output("Author-email: %s", dist.author_email)
+        if metadata_version_tuple >= (2, 4) and dist.license_expression:
+            write_output("License-Expression: %s", dist.license_expression)
+        else:
+            write_output("License: %s", dist.license)
+        write_output("Location: %s", dist.location)
+        if dist.editable_project_location is not None:
+            write_output(
+                "Editable project location: %s", dist.editable_project_location
+            )
+        write_output("Requires: %s", ", ".join(dist.requires))
+        write_output("Required-by: %s", ", ".join(dist.required_by))
+
+        if verbose:
+            write_output("Metadata-Version: %s", dist.metadata_version)
+            write_output("Installer: %s", dist.installer)
+            write_output("Classifiers:")
+            for classifier in dist.classifiers:
+                write_output("  %s", classifier)
+            write_output("Entry-points:")
+            for entry in dist.entry_points:
+                write_output("  %s", entry.strip())
+            write_output("Project-URLs:")
+            for project_url in dist.project_urls:
+                write_output("  %s", project_url)
+        if list_files:
+            write_output("Files:")
+            if dist.files is None:
+                write_output("Cannot locate RECORD or installed-files.txt")
+            else:
+                for line in dist.files:
+                    write_output("  %s", line.strip())
+    return results_printed
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/uninstall.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/uninstall.py
new file mode 100644
index 0000000..9c4f031
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/uninstall.py
@@ -0,0 +1,113 @@
+import logging
+from optparse import Values
+
+from pip._vendor.packaging.utils import canonicalize_name
+
+from pip._internal.cli import cmdoptions
+from pip._internal.cli.base_command import Command
+from pip._internal.cli.index_command import SessionCommandMixin
+from pip._internal.cli.status_codes import SUCCESS
+from pip._internal.exceptions import InstallationError
+from pip._internal.req import parse_requirements
+from pip._internal.req.constructors import (
+    install_req_from_line,
+    install_req_from_parsed_requirement,
+)
+from pip._internal.utils.misc import (
+    check_externally_managed,
+    protect_pip_from_modification_on_windows,
+    warn_if_run_as_root,
+)
+
+logger = logging.getLogger(__name__)
+
+
+class UninstallCommand(Command, SessionCommandMixin):
+    """
+    Uninstall packages.
+
+    pip is able to uninstall most installed packages. Known exceptions are:
+
+    - Pure distutils packages installed with ``python setup.py install``, which
+      leave behind no metadata to determine what files were installed.
+    - Script wrappers installed by ``python setup.py develop``.
+    """
+
+    usage = """
+      %prog [options]  ...
+      %prog [options] -r  ..."""
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(
+            "-r",
+            "--requirement",
+            dest="requirements",
+            action="append",
+            default=[],
+            metavar="file",
+            help=(
+                "Uninstall all the packages listed in the given requirements "
+                "file.  This option can be used multiple times."
+            ),
+        )
+        self.cmd_opts.add_option(
+            "-y",
+            "--yes",
+            dest="yes",
+            action="store_true",
+            help="Don't ask for confirmation of uninstall deletions.",
+        )
+        self.cmd_opts.add_option(cmdoptions.root_user_action())
+        self.cmd_opts.add_option(cmdoptions.override_externally_managed())
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    def run(self, options: Values, args: list[str]) -> int:
+        session = self.get_default_session(options)
+
+        reqs_to_uninstall = {}
+        for name in args:
+            req = install_req_from_line(
+                name,
+                isolated=options.isolated_mode,
+            )
+            if req.name:
+                reqs_to_uninstall[canonicalize_name(req.name)] = req
+            else:
+                logger.warning(
+                    "Invalid requirement: %r ignored -"
+                    " the uninstall command expects named"
+                    " requirements.",
+                    name,
+                )
+        for filename in options.requirements:
+            for parsed_req in parse_requirements(
+                filename, options=options, session=session
+            ):
+                req = install_req_from_parsed_requirement(
+                    parsed_req, isolated=options.isolated_mode
+                )
+                if req.name:
+                    reqs_to_uninstall[canonicalize_name(req.name)] = req
+        if not reqs_to_uninstall:
+            raise InstallationError(
+                f"You must give at least one requirement to {self.name} (see "
+                f'"pip help {self.name}")'
+            )
+
+        if not options.override_externally_managed:
+            check_externally_managed()
+
+        protect_pip_from_modification_on_windows(
+            modifying_pip="pip" in reqs_to_uninstall
+        )
+
+        for req in reqs_to_uninstall.values():
+            uninstall_pathset = req.uninstall(
+                auto_confirm=options.yes,
+                verbose=self.verbosity > 0,
+            )
+            if uninstall_pathset:
+                uninstall_pathset.commit()
+        if options.root_user_action == "warn":
+            warn_if_run_as_root()
+        return SUCCESS
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/commands/wheel.py b/venv/lib/python3.12/site-packages/pip/_internal/commands/wheel.py
new file mode 100644
index 0000000..ee25b82
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/commands/wheel.py
@@ -0,0 +1,177 @@
+import logging
+import os
+import shutil
+from optparse import Values
+
+from pip._internal.cache import WheelCache
+from pip._internal.cli import cmdoptions
+from pip._internal.cli.req_command import RequirementCommand, with_cleanup
+from pip._internal.cli.status_codes import SUCCESS
+from pip._internal.exceptions import CommandError
+from pip._internal.operations.build.build_tracker import get_build_tracker
+from pip._internal.req.req_install import (
+    InstallRequirement,
+)
+from pip._internal.utils.misc import ensure_dir, normalize_path
+from pip._internal.utils.temp_dir import TempDirectory
+from pip._internal.wheel_builder import build
+
+logger = logging.getLogger(__name__)
+
+
+class WheelCommand(RequirementCommand):
+    """
+    Build Wheel archives for your requirements and dependencies.
+
+    Wheel is a built-package format, and offers the advantage of not
+    recompiling your software during every install. For more details, see the
+    wheel docs: https://wheel.readthedocs.io/en/latest/
+
+    'pip wheel' uses the build system interface as described here:
+    https://pip.pypa.io/en/stable/reference/build-system/
+
+    """
+
+    usage = """
+      %prog [options]  ...
+      %prog [options] -r  ...
+      %prog [options] [-e]  ...
+      %prog [options] [-e]  ...
+      %prog [options]  ..."""
+
+    def add_options(self) -> None:
+        self.cmd_opts.add_option(
+            "-w",
+            "--wheel-dir",
+            dest="wheel_dir",
+            metavar="dir",
+            default=os.curdir,
+            help=(
+                "Build wheels into , where the default is the "
+                "current working directory."
+            ),
+        )
+        self.cmd_opts.add_option(cmdoptions.no_build_isolation())
+        self.cmd_opts.add_option(cmdoptions.use_pep517())
+        self.cmd_opts.add_option(cmdoptions.check_build_deps())
+        self.cmd_opts.add_option(cmdoptions.constraints())
+        self.cmd_opts.add_option(cmdoptions.build_constraints())
+        self.cmd_opts.add_option(cmdoptions.editable())
+        self.cmd_opts.add_option(cmdoptions.requirements())
+        self.cmd_opts.add_option(cmdoptions.requirements_from_scripts())
+        self.cmd_opts.add_option(cmdoptions.src())
+        self.cmd_opts.add_option(cmdoptions.ignore_requires_python())
+        self.cmd_opts.add_option(cmdoptions.no_deps())
+        self.cmd_opts.add_option(cmdoptions.only_deps())
+        self.cmd_opts.add_option(cmdoptions.progress_bar())
+
+        self.cmd_opts.add_option(
+            "--no-verify",
+            dest="no_verify",
+            action="store_true",
+            default=False,
+            help="Don't verify if built wheel is valid.",
+        )
+
+        self.cmd_opts.add_option(cmdoptions.config_settings())
+
+        self.cmd_opts.add_option(cmdoptions.require_hashes())
+        self.cmd_opts.add_option(cmdoptions.no_require_hashes())
+
+        index_opts = cmdoptions.make_option_group(
+            cmdoptions.index_group,
+            self.parser,
+        )
+
+        selection_opts = cmdoptions.make_option_group(
+            cmdoptions.package_selection_group,
+            self.parser,
+        )
+
+        self.parser.insert_option_group(0, index_opts)
+        self.parser.insert_option_group(0, selection_opts)
+        self.parser.insert_option_group(0, self.cmd_opts)
+
+    @with_cleanup
+    def run(self, options: Values, args: list[str]) -> int:
+        cmdoptions.check_build_constraints(options)
+        cmdoptions.check_release_control_exclusive(options)
+        cmdoptions.check_only_deps_option_does_not_conflict(options)
+
+        session = self.get_default_session(options)
+
+        finder = self._build_package_finder(options, session)
+
+        options.wheel_dir = normalize_path(options.wheel_dir)
+        ensure_dir(options.wheel_dir)
+
+        build_tracker = self.enter_context(get_build_tracker())
+
+        directory = TempDirectory(
+            delete=not options.no_clean,
+            kind="wheel",
+            globally_managed=True,
+        )
+
+        reqs = self.get_requirements(args, options, finder, session)
+
+        wheel_cache = WheelCache(options.cache_dir)
+
+        preparer = self.make_requirement_preparer(
+            temp_build_dir=directory,
+            options=options,
+            build_tracker=build_tracker,
+            session=session,
+            finder=finder,
+            download_dir=options.wheel_dir,
+            use_user_site=False,
+            verbosity=self.verbosity,
+            allow_editables=False,
+        )
+
+        resolver = self.make_resolver(
+            preparer=preparer,
+            finder=finder,
+            options=options,
+            wheel_cache=wheel_cache,
+            ignore_requires_python=options.ignore_requires_python,
+        )
+
+        self.trace_basic_info(finder)
+
+        requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
+
+        preparer.prepare_linked_requirements_more(requirement_set.requirements.values())
+
+        reqs_to_build: list[InstallRequirement] = []
+        for req in requirement_set.requirements.values():
+            if req.is_wheel:
+                preparer.save_linked_requirement(req)
+            else:
+                reqs_to_build.append(req)
+
+        # build wheels
+        build_successes, build_failures = build(
+            reqs_to_build,
+            wheel_cache=wheel_cache,
+            verify=(not options.no_verify),
+            allow_editables=False,
+        )
+        for req in build_successes:
+            assert req.link and req.link.is_wheel
+            assert req.local_file_path
+            # copy from cache to target directory
+            try:
+                shutil.copy(req.local_file_path, options.wheel_dir)
+            except OSError as e:
+                logger.warning(
+                    "Failed to copy built wheel %s to %s: %s",
+                    req.name,
+                    options.wheel_dir,
+                    e,
+                )
+                build_failures.append(req)
+        if len(build_failures) != 0:
+            raise CommandError("Failed to build one or more wheels")
+
+        return SUCCESS
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/configuration.py b/venv/lib/python3.12/site-packages/pip/_internal/configuration.py
new file mode 100644
index 0000000..661d25e
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/configuration.py
@@ -0,0 +1,395 @@
+"""Configuration management setup
+
+Some terminology:
+- name
+  As written in config files.
+- value
+  Value associated with a name
+- key
+  Name combined with it's section (section.name)
+- variant
+  A single word describing where the configuration key-value pair came from
+"""
+
+from __future__ import annotations
+
+import configparser
+import os
+import sys
+from collections.abc import Iterable
+from typing import Any, NewType
+
+from pip._internal.exceptions import (
+    ConfigurationError,
+    ConfigurationFileCouldNotBeLoaded,
+)
+from pip._internal.utils import appdirs
+from pip._internal.utils.compat import WINDOWS, get_locale_encoding
+from pip._internal.utils.logging import getLogger
+from pip._internal.utils.misc import ensure_dir, enum
+
+RawConfigParser = configparser.RawConfigParser  # Shorthand
+Kind = NewType("Kind", str)
+
+CONFIG_BASENAME = "pip.ini" if WINDOWS else "pip.conf"
+ENV_NAMES_IGNORED = "version", "help"
+
+# The kinds of configurations there are.
+kinds = enum(
+    USER="user",  # User Specific
+    GLOBAL="global",  # System Wide
+    SITE="site",  # [Virtual] Environment Specific
+    ENV="env",  # from PIP_CONFIG_FILE
+    ENV_VAR="env-var",  # from Environment Variables
+)
+OVERRIDE_ORDER = kinds.GLOBAL, kinds.USER, kinds.SITE, kinds.ENV, kinds.ENV_VAR
+VALID_LOAD_ONLY = kinds.USER, kinds.GLOBAL, kinds.SITE
+
+logger = getLogger(__name__)
+
+
+# NOTE: Maybe use the optionx attribute to normalize keynames.
+def _normalize_name(name: str) -> str:
+    """Make a name consistent regardless of source (environment or file)"""
+    name = name.lower().replace("_", "-")
+    name = name.removeprefix("--")  # only prefer long opts
+    return name
+
+
+def _disassemble_key(name: str) -> list[str]:
+    if "." not in name:
+        error_message = (
+            "Key does not contain dot separated section and key. "
+            f"Perhaps you wanted to use 'global.{name}' instead?"
+        )
+        raise ConfigurationError(error_message)
+    return name.split(".", 1)
+
+
+def get_configuration_files() -> dict[Kind, list[str]]:
+    global_config_files = [
+        os.path.join(path, CONFIG_BASENAME) for path in appdirs.site_config_dirs("pip")
+    ]
+
+    site_config_file = os.path.join(sys.prefix, CONFIG_BASENAME)
+    legacy_config_file = os.path.join(
+        os.path.expanduser("~"),
+        "pip" if WINDOWS else ".pip",
+        CONFIG_BASENAME,
+    )
+    new_config_file = os.path.join(appdirs.user_config_dir("pip"), CONFIG_BASENAME)
+    return {
+        kinds.GLOBAL: global_config_files,
+        kinds.SITE: [site_config_file],
+        kinds.USER: [legacy_config_file, new_config_file],
+    }
+
+
+class Configuration:
+    """Handles management of configuration.
+
+    Provides an interface to accessing and managing configuration files.
+
+    This class converts provides an API that takes "section.key-name" style
+    keys and stores the value associated with it as "key-name" under the
+    section "section".
+
+    This allows for a clean interface wherein the both the section and the
+    key-name are preserved in an easy to manage form in the configuration files
+    and the data stored is also nice.
+    """
+
+    def __init__(self, isolated: bool, load_only: Kind | None = None) -> None:
+        super().__init__()
+
+        if load_only is not None and load_only not in VALID_LOAD_ONLY:
+            raise ConfigurationError(
+                "Got invalid value for load_only - should be one of {}".format(
+                    ", ".join(map(repr, VALID_LOAD_ONLY))
+                )
+            )
+        self.isolated = isolated
+        self.load_only = load_only
+
+        # Because we keep track of where we got the data from
+        self._parsers: dict[Kind, list[tuple[str, RawConfigParser]]] = {
+            variant: [] for variant in OVERRIDE_ORDER
+        }
+        self._config: dict[Kind, dict[str, dict[str, Any]]] = {
+            variant: {} for variant in OVERRIDE_ORDER
+        }
+        self._modified_parsers: list[tuple[str, RawConfigParser]] = []
+
+    def load(self) -> None:
+        """Loads configuration from configuration files and environment"""
+        self._load_config_files()
+        if not self.isolated:
+            self._load_environment_vars()
+
+    def get_file_to_edit(self) -> str | None:
+        """Returns the file with highest priority in configuration"""
+        assert self.load_only is not None, "Need to be specified a file to be editing"
+
+        try:
+            return self._get_parser_to_modify()[0]
+        except IndexError:
+            return None
+
+    def items(self) -> Iterable[tuple[str, Any]]:
+        """Returns key-value pairs like dict.items() representing the loaded
+        configuration
+        """
+        return self._dictionary.items()
+
+    def get_value(self, key: str) -> Any:
+        """Get a value from the configuration."""
+        orig_key = key
+        key = _normalize_name(key)
+        try:
+            clean_config: dict[str, Any] = {}
+            for file_values in self._dictionary.values():
+                clean_config.update(file_values)
+            return clean_config[key]
+        except KeyError:
+            # disassembling triggers a more useful error message than simply
+            # "No such key" in the case that the key isn't in the form command.option
+            _disassemble_key(key)
+            raise ConfigurationError(f"No such key - {orig_key}")
+
+    def set_value(self, key: str, value: Any) -> None:
+        """Modify a value in the configuration."""
+        key = _normalize_name(key)
+        self._ensure_have_load_only()
+
+        assert self.load_only
+        fname, parser = self._get_parser_to_modify()
+
+        if parser is not None:
+            section, name = _disassemble_key(key)
+
+            # Modify the parser and the configuration
+            if not parser.has_section(section):
+                parser.add_section(section)
+            parser.set(section, name, value)
+
+        self._config[self.load_only].setdefault(fname, {})
+        self._config[self.load_only][fname][key] = value
+        self._mark_as_modified(fname, parser)
+
+    def unset_value(self, key: str) -> None:
+        """Unset a value in the configuration."""
+        orig_key = key
+        key = _normalize_name(key)
+        self._ensure_have_load_only()
+
+        assert self.load_only
+        fname, parser = self._get_parser_to_modify()
+
+        if (
+            key not in self._config[self.load_only][fname]
+            and key not in self._config[self.load_only]
+        ):
+            raise ConfigurationError(f"No such key - {orig_key}")
+
+        if parser is not None:
+            section, name = _disassemble_key(key)
+            if not (
+                parser.has_section(section) and parser.remove_option(section, name)
+            ):
+                # The option was not removed.
+                raise ConfigurationError(
+                    "Fatal Internal error [id=1]. Please report as a bug."
+                )
+
+            # The section may be empty after the option was removed.
+            if not parser.items(section):
+                parser.remove_section(section)
+            self._mark_as_modified(fname, parser)
+        try:
+            del self._config[self.load_only][fname][key]
+        except KeyError:
+            del self._config[self.load_only][key]
+
+    def save(self) -> None:
+        """Save the current in-memory state."""
+        self._ensure_have_load_only()
+
+        for fname, parser in self._modified_parsers:
+            logger.info("Writing to %s", fname)
+
+            # Ensure directory exists.
+            ensure_dir(os.path.dirname(fname))
+
+            # Ensure directory's permission(need to be writeable)
+            try:
+                with open(fname, "w") as f:
+                    parser.write(f)
+            except OSError as error:
+                raise ConfigurationError(
+                    f"An error occurred while writing to the configuration file "
+                    f"{fname}: {error}"
+                )
+
+    #
+    # Private routines
+    #
+
+    def _ensure_have_load_only(self) -> None:
+        if self.load_only is None:
+            raise ConfigurationError("Needed a specific file to be modifying.")
+        logger.debug("Will be working with %s variant only", self.load_only)
+
+    @property
+    def _dictionary(self) -> dict[str, dict[str, Any]]:
+        """A dictionary representing the loaded configuration."""
+        # NOTE: Dictionaries are not populated if not loaded. So, conditionals
+        #       are not needed here.
+        retval = {}
+
+        for variant in OVERRIDE_ORDER:
+            retval.update(self._config[variant])
+
+        return retval
+
+    def _load_config_files(self) -> None:
+        """Loads configuration from configuration files"""
+        config_files = dict(self.iter_config_files())
+        if config_files[kinds.ENV][0:1] == [os.devnull]:
+            logger.debug(
+                "Skipping loading configuration files due to "
+                "environment's PIP_CONFIG_FILE being os.devnull"
+            )
+            return
+
+        for variant, files in config_files.items():
+            for fname in files:
+                # If there's specific variant set in `load_only`, load only
+                # that variant, not the others.
+                if self.load_only is not None and variant != self.load_only:
+                    logger.debug("Skipping file '%s' (variant: %s)", fname, variant)
+                    continue
+
+                parser = self._load_file(variant, fname)
+
+                # Keeping track of the parsers used
+                self._parsers[variant].append((fname, parser))
+
+    def _load_file(self, variant: Kind, fname: str) -> RawConfigParser:
+        logger.verbose("For variant '%s', will try loading '%s'", variant, fname)
+        parser = self._construct_parser(fname)
+
+        for section in parser.sections():
+            items = parser.items(section)
+            self._config[variant].setdefault(fname, {})
+            self._config[variant][fname].update(self._normalized_keys(section, items))
+
+        return parser
+
+    def _construct_parser(self, fname: str) -> RawConfigParser:
+        parser = configparser.RawConfigParser()
+        # If there is no such file, don't bother reading it but create the
+        # parser anyway, to hold the data.
+        # Doing this is useful when modifying and saving files, where we don't
+        # need to construct a parser.
+        if os.path.exists(fname):
+            locale_encoding = get_locale_encoding()
+            try:
+                parser.read(fname, encoding=locale_encoding)
+            except UnicodeDecodeError:
+                # See https://github.com/pypa/pip/issues/4963
+                raise ConfigurationFileCouldNotBeLoaded(
+                    reason=f"contains invalid {locale_encoding} characters",
+                    fname=fname,
+                )
+            except configparser.Error as error:
+                # See https://github.com/pypa/pip/issues/4893
+                raise ConfigurationFileCouldNotBeLoaded(error=error)
+        return parser
+
+    def _load_environment_vars(self) -> None:
+        """Loads configuration from environment variables"""
+        self._config[kinds.ENV_VAR].setdefault(":env:", {})
+        self._config[kinds.ENV_VAR][":env:"].update(
+            self._normalized_keys(":env:", self.get_environ_vars())
+        )
+
+    def _normalized_keys(
+        self, section: str, items: Iterable[tuple[str, Any]]
+    ) -> dict[str, Any]:
+        """Normalizes items to construct a dictionary with normalized keys.
+
+        This routine is where the names become keys and are made the same
+        regardless of source - configuration files or environment.
+        """
+        normalized = {}
+        for name, val in items:
+            key = section + "." + _normalize_name(name)
+            normalized[key] = val
+        return normalized
+
+    def get_environ_vars(self) -> Iterable[tuple[str, str]]:
+        """Returns a generator with all environmental vars with prefix PIP_"""
+        for key, val in os.environ.items():
+            if key.startswith("PIP_"):
+                name = key[4:].lower()
+                if name not in ENV_NAMES_IGNORED:
+                    yield name, val
+
+    # XXX: This is patched in the tests.
+    def iter_config_files(self) -> Iterable[tuple[Kind, list[str]]]:
+        """Yields variant and configuration files associated with it.
+
+        This should be treated like items of a dictionary. The order
+        here doesn't affect what gets overridden. That is controlled
+        by OVERRIDE_ORDER. However this does control the order they are
+        displayed to the user. It's probably most ergonomic to display
+        things in the same order as OVERRIDE_ORDER
+        """
+        # SMELL: Move the conditions out of this function
+
+        env_config_file = os.environ.get("PIP_CONFIG_FILE", None)
+        config_files = get_configuration_files()
+
+        yield kinds.GLOBAL, config_files[kinds.GLOBAL]
+
+        # per-user config is not loaded when env_config_file exists
+        should_load_user_config = not self.isolated and not (
+            env_config_file and os.path.exists(env_config_file)
+        )
+        if should_load_user_config:
+            # The legacy config file is overridden by the new config file
+            yield kinds.USER, config_files[kinds.USER]
+
+        # virtualenv config
+        yield kinds.SITE, config_files[kinds.SITE]
+
+        if env_config_file is not None:
+            yield kinds.ENV, [env_config_file]
+        else:
+            yield kinds.ENV, []
+
+    def get_values_in_config(self, variant: Kind) -> dict[str, Any]:
+        """Get values present in a config file"""
+        return self._config[variant]
+
+    def _get_parser_to_modify(self) -> tuple[str, RawConfigParser]:
+        # Determine which parser to modify
+        assert self.load_only
+        parsers = self._parsers[self.load_only]
+        if not parsers:
+            # This should not happen if everything works correctly.
+            raise ConfigurationError(
+                "Fatal Internal error [id=2]. Please report as a bug."
+            )
+
+        # Use the highest priority parser.
+        return parsers[-1]
+
+    # XXX: This is patched in the tests.
+    def _mark_as_modified(self, fname: str, parser: RawConfigParser) -> None:
+        file_parser_tuple = (fname, parser)
+        if file_parser_tuple not in self._modified_parsers:
+            self._modified_parsers.append(file_parser_tuple)
+
+    def __repr__(self) -> str:
+        return f"{self.__class__.__name__}({self._dictionary!r})"
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/distributions/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/distributions/__init__.py
new file mode 100644
index 0000000..9a89a83
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/distributions/__init__.py
@@ -0,0 +1,21 @@
+from pip._internal.distributions.base import AbstractDistribution
+from pip._internal.distributions.sdist import SourceDistribution
+from pip._internal.distributions.wheel import WheelDistribution
+from pip._internal.req.req_install import InstallRequirement
+
+
+def make_distribution_for_install_requirement(
+    install_req: InstallRequirement,
+) -> AbstractDistribution:
+    """Returns a Distribution for the given InstallRequirement"""
+    # Editable requirements will always be source distributions. They use the
+    # legacy logic until we create a modern standard for them.
+    if install_req.editable:
+        return SourceDistribution(install_req)
+
+    # If it's a wheel, it's a WheelDistribution
+    if install_req.is_wheel:
+        return WheelDistribution(install_req)
+
+    # Otherwise, a SourceDistribution
+    return SourceDistribution(install_req)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..8a219d3
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/base.cpython-312.pyc
new file mode 100644
index 0000000..0309f53
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/base.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-312.pyc
new file mode 100644
index 0000000..096fc36
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-312.pyc
new file mode 100644
index 0000000..46d9239
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-312.pyc
new file mode 100644
index 0000000..cc5860c
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/distributions/base.py b/venv/lib/python3.12/site-packages/pip/_internal/distributions/base.py
new file mode 100644
index 0000000..66cc542
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/distributions/base.py
@@ -0,0 +1,57 @@
+from __future__ import annotations
+
+import abc
+from typing import TYPE_CHECKING
+
+from pip._internal.metadata.base import BaseDistribution
+from pip._internal.req import InstallRequirement
+
+if TYPE_CHECKING:
+    from pip._internal.build_env import BuildEnvironmentInstaller, BuildIsolationMode
+
+
+class AbstractDistribution(metaclass=abc.ABCMeta):
+    """A base class for handling installable artifacts.
+
+    The requirements for anything installable are as follows:
+
+     - we must be able to determine the requirement name
+       (or we can't correctly handle the non-upgrade case).
+
+     - for packages with setup requirements, we must also be able
+       to determine their requirements without installing additional
+       packages (for the same reason as run-time dependencies)
+
+     - we must be able to create a Distribution object exposing the
+       above metadata.
+
+     - if we need to do work in the build tracker, we must be able to generate a unique
+       string to identify the requirement in the build tracker.
+    """
+
+    def __init__(self, req: InstallRequirement) -> None:
+        super().__init__()
+        self.req = req
+
+    @property
+    @abc.abstractmethod
+    def build_tracker_id(self) -> str | None:
+        """A string that uniquely identifies this requirement to the build tracker.
+
+        If None, then this dist has no work to do in the build tracker, and
+        ``.prepare_distribution_metadata()`` will not be called."""
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def get_metadata_distribution(self) -> BaseDistribution:
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def prepare_distribution_metadata(
+        self,
+        build_env_installer: BuildEnvironmentInstaller,
+        build_isolation: BuildIsolationMode,
+        check_build_deps: bool,
+        allow_editables: bool,
+    ) -> None:
+        raise NotImplementedError()
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/distributions/installed.py b/venv/lib/python3.12/site-packages/pip/_internal/distributions/installed.py
new file mode 100644
index 0000000..06cff25
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/distributions/installed.py
@@ -0,0 +1,34 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from pip._internal.distributions.base import AbstractDistribution
+from pip._internal.metadata import BaseDistribution
+
+if TYPE_CHECKING:
+    from pip._internal.build_env import BuildEnvironmentInstaller, BuildIsolationMode
+
+
+class InstalledDistribution(AbstractDistribution):
+    """Represents an installed package.
+
+    This does not need any preparation as the required information has already
+    been computed.
+    """
+
+    @property
+    def build_tracker_id(self) -> str | None:
+        return None
+
+    def get_metadata_distribution(self) -> BaseDistribution:
+        assert self.req.satisfied_by is not None, "not actually installed"
+        return self.req.satisfied_by
+
+    def prepare_distribution_metadata(
+        self,
+        build_env_installer: BuildEnvironmentInstaller,
+        build_isolation: BuildIsolationMode,
+        check_build_deps: bool,
+        allow_editables: bool,
+    ) -> None:
+        pass
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/distributions/sdist.py b/venv/lib/python3.12/site-packages/pip/_internal/distributions/sdist.py
new file mode 100644
index 0000000..fad25db
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/distributions/sdist.py
@@ -0,0 +1,193 @@
+from __future__ import annotations
+
+import logging
+from collections.abc import Iterable
+from contextlib import nullcontext
+from typing import TYPE_CHECKING
+
+from pip._internal.build_env import (
+    BuildIsolationMode,
+    NoOpBuildEnvironment,
+    VenvBuildEnvironment,
+    VirtualBuildEnvironment,
+)
+from pip._internal.distributions.base import AbstractDistribution
+from pip._internal.exceptions import InstallationError
+from pip._internal.metadata import BaseDistribution
+from pip._internal.utils.subprocess import runner_with_spinner_message
+
+if TYPE_CHECKING:
+    from pip._internal.build_env import BuildEnvironmentInstaller
+
+logger = logging.getLogger(__name__)
+
+
+class SourceDistribution(AbstractDistribution):
+    """Represents a source distribution.
+
+    The preparation step for these needs metadata for the packages to be
+    generated.
+    """
+
+    @property
+    def build_tracker_id(self) -> str | None:
+        """Identify this requirement uniquely by its link."""
+        assert self.req.link
+        return self.req.link.url_without_fragment
+
+    def get_metadata_distribution(self) -> BaseDistribution:
+        return self.req.get_dist()
+
+    def prepare_distribution_metadata(
+        self,
+        build_env_installer: BuildEnvironmentInstaller,
+        build_isolation: BuildIsolationMode,
+        check_build_deps: bool,
+        allow_editables: bool,
+    ) -> None:
+        # Load pyproject.toml and set up backend environment
+        self.req.load_pyproject_toml()
+        self._prepare_build_env(build_isolation, build_env_installer)
+
+        # Set up the build isolation, if this requirement should be isolated
+        if build_isolation != "off":
+            # Setup an isolated environment and install the build backend static
+            # requirements in it.
+            self._prepare_build_backend(build_isolation)
+            # Check that the build backend supports PEP 660. This cannot be done
+            # earlier because we need to setup the build backend to verify it
+            # supports build_editable, nor can it be done later, because we want
+            # to avoid installing build requirements needlessly.
+            self.req.editable_sanity_check()
+            # Install the dynamic build requirements.
+            self._install_build_reqs(
+                build_env_installer, build_isolation, allow_editables=allow_editables
+            )
+        else:
+            # When not using build isolation, we still need to check that
+            # the build backend supports PEP 660.
+            self.req.editable_sanity_check()
+        # Check if the current environment provides build dependencies
+        if check_build_deps:
+            pyproject_requires = self.req.pyproject_requires
+            assert pyproject_requires is not None
+            conflicting, missing = self.req.build_env.check_requirements(
+                pyproject_requires
+            )
+            if conflicting:
+                self._raise_conflicts("the backend dependencies", conflicting)
+            if missing:
+                self._raise_missing_reqs(missing)
+        self.req.prepare_metadata(allow_editables)
+
+    def _prepare_build_env(
+        self,
+        build_isolation: BuildIsolationMode,
+        build_env_installer: BuildEnvironmentInstaller,
+    ) -> None:
+        if build_isolation == "virtual":
+            self.req.build_env = VirtualBuildEnvironment(build_env_installer)
+        elif build_isolation == "venv":
+            self.req.build_env = VenvBuildEnvironment(build_env_installer)
+
+        self.req.configure_backend(self.req.build_env.python_executable)
+
+    def _prepare_build_backend(self, build_isolation: BuildIsolationMode) -> None:
+        # Install the pyproject.toml declared build-time requirements.
+        pyproject_requires = self.req.pyproject_requires
+        assert pyproject_requires is not None
+        assert not isinstance(self.req.build_env, NoOpBuildEnvironment)
+
+        context = self.req.build_env if build_isolation == "venv" else nullcontext()
+        with context:
+            self.req.build_env.install_requirements(
+                pyproject_requires,
+                "overlay",
+                kind="build dependencies",
+                for_req=self.req,
+            )
+        conflicting, missing = self.req.build_env.check_requirements(
+            self.req.requirements_to_check
+        )
+        if conflicting:
+            self._raise_conflicts("PEP 517/518 supported requirements", conflicting)
+        if missing:
+            logger.warning(
+                "Missing build requirements in pyproject.toml for %s.",
+                self.req,
+            )
+            logger.warning(
+                "The project does not specify a build backend, and "
+                "pip cannot fall back to setuptools without %s.",
+                " and ".join(map(repr, sorted(missing))),
+            )
+
+    def _get_build_requires_wheel(self) -> Iterable[str]:
+        with self.req.build_env:
+            runner = runner_with_spinner_message("Getting requirements to build wheel")
+            backend = self.req.pep517_backend
+            assert backend is not None
+            with backend.subprocess_runner(runner):
+                return backend.get_requires_for_build_wheel()
+
+    def _get_build_requires_editable(self) -> Iterable[str]:
+        with self.req.build_env:
+            runner = runner_with_spinner_message(
+                "Getting requirements to build editable"
+            )
+            backend = self.req.pep517_backend
+            assert backend is not None
+            with backend.subprocess_runner(runner):
+                return backend.get_requires_for_build_editable()
+
+    def _install_build_reqs(
+        self,
+        build_env_installer: BuildEnvironmentInstaller,
+        build_isolation: BuildIsolationMode,
+        allow_editables: bool,
+    ) -> None:
+        # Install any extra build dependencies that the backend requests.
+        # This must be done in a second pass, as the pyproject.toml
+        # dependencies must be installed before we can call the backend.
+        if (
+            self.req.editable
+            and allow_editables
+            and self.req.supports_pyproject_editable
+        ):
+            build_reqs = self._get_build_requires_editable()
+        else:
+            build_reqs = self._get_build_requires_wheel()
+        conflicting, missing = self.req.build_env.check_requirements(build_reqs)
+        if conflicting:
+            self._raise_conflicts("the backend dependencies", conflicting)
+        context = self.req.build_env if build_isolation == "venv" else nullcontext()
+        with context:
+            self.req.build_env.install_requirements(
+                missing, "normal", kind="backend dependencies", for_req=self.req
+            )
+
+    def _raise_conflicts(
+        self, conflicting_with: str, conflicting_reqs: set[tuple[str, str]]
+    ) -> None:
+        format_string = (
+            "Some build dependencies for {requirement} "
+            "conflict with {conflicting_with}: {description}."
+        )
+        error_message = format_string.format(
+            requirement=self.req,
+            conflicting_with=conflicting_with,
+            description=", ".join(
+                f"{installed} is incompatible with {wanted}"
+                for installed, wanted in sorted(conflicting_reqs)
+            ),
+        )
+        raise InstallationError(error_message)
+
+    def _raise_missing_reqs(self, missing: set[str]) -> None:
+        format_string = (
+            "Some build dependencies for {requirement} are missing: {missing}."
+        )
+        error_message = format_string.format(
+            requirement=self.req, missing=", ".join(map(repr, sorted(missing)))
+        )
+        raise InstallationError(error_message)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/distributions/wheel.py b/venv/lib/python3.12/site-packages/pip/_internal/distributions/wheel.py
new file mode 100644
index 0000000..8f2a217
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/distributions/wheel.py
@@ -0,0 +1,45 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from pip._vendor.packaging.utils import canonicalize_name
+
+from pip._internal.distributions.base import AbstractDistribution
+from pip._internal.metadata import (
+    BaseDistribution,
+    FilesystemWheel,
+    get_wheel_distribution,
+)
+
+if TYPE_CHECKING:
+    from pip._internal.build_env import BuildEnvironmentInstaller, BuildIsolationMode
+
+
+class WheelDistribution(AbstractDistribution):
+    """Represents a wheel distribution.
+
+    This does not need any preparation as wheels can be directly unpacked.
+    """
+
+    @property
+    def build_tracker_id(self) -> str | None:
+        return None
+
+    def get_metadata_distribution(self) -> BaseDistribution:
+        """Loads the metadata from the wheel file into memory and returns a
+        Distribution that uses it, not relying on the wheel file or
+        requirement.
+        """
+        assert self.req.local_file_path, "Set as part of preparation during download"
+        assert self.req.name, "Wheels are never unnamed"
+        wheel = FilesystemWheel(self.req.local_file_path)
+        return get_wheel_distribution(wheel, canonicalize_name(self.req.name))
+
+    def prepare_distribution_metadata(
+        self,
+        build_env_installer: BuildEnvironmentInstaller,
+        build_isolation: BuildIsolationMode,
+        check_build_deps: bool,
+        allow_editables: bool,
+    ) -> None:
+        pass
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/exceptions.py b/venv/lib/python3.12/site-packages/pip/_internal/exceptions.py
new file mode 100644
index 0000000..b2d1e1d
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/exceptions.py
@@ -0,0 +1,1145 @@
+"""Exceptions used throughout package.
+
+This module MUST NOT try to import from anything within `pip._internal` to
+operate. This is expected to be importable from any/all files within the
+subpackage and, thus, should not depend on them.
+"""
+
+from __future__ import annotations
+
+import configparser
+import contextlib
+import locale
+import logging
+import os
+import pathlib
+import re
+import sys
+import traceback
+from collections.abc import Iterable, Iterator
+from itertools import chain, groupby, repeat
+from typing import TYPE_CHECKING, Literal
+
+from pip._vendor.packaging.requirements import InvalidRequirement
+from pip._vendor.packaging.version import InvalidVersion
+from pip._vendor.rich.console import Console, ConsoleOptions, RenderResult
+from pip._vendor.rich.markup import escape
+from pip._vendor.rich.text import Text
+
+if TYPE_CHECKING:
+    from hashlib import _Hash
+
+    from pip._vendor import urllib3
+    from pip._vendor.requests.models import PreparedRequest, Request, Response
+
+    from pip._internal.metadata import BaseDistribution
+    from pip._internal.models.link import Link
+    from pip._internal.network.download import _FileDownload
+    from pip._internal.req.req_install import InstallRequirement
+
+logger = logging.getLogger(__name__)
+
+
+#
+# Scaffolding
+#
+def _is_kebab_case(s: str) -> bool:
+    return re.match(r"^[a-z]+(-[a-z]+)*$", s) is not None
+
+
+def _prefix_with_indent(
+    s: Text | str,
+    console: Console,
+    *,
+    prefix: str,
+    indent: str,
+) -> Text:
+    if isinstance(s, Text):
+        text = s
+    else:
+        text = console.render_str(s)
+
+    return console.render_str(prefix, overflow="ignore") + console.render_str(
+        f"\n{indent}", overflow="ignore"
+    ).join(text.split(allow_blank=True))
+
+
+class PipError(Exception):
+    """The base pip error."""
+
+
+class DiagnosticPipError(PipError):
+    """An error, that presents diagnostic information to the user.
+
+    This contains a bunch of logic, to enable pretty presentation of our error
+    messages. Each error gets a unique reference. Each error can also include
+    additional context, a hint and/or a note -- which are presented with the
+    main error message in a consistent style.
+
+    This is adapted from the error output styling in `sphinx-theme-builder`.
+    """
+
+    reference: str
+
+    def __init__(
+        self,
+        *,
+        kind: Literal["error", "warning"] = "error",
+        reference: str | None = None,
+        message: str | Text,
+        context: str | Text | None,
+        hint_stmt: str | Text | None,
+        note_stmt: str | Text | None = None,
+        link: str | None = None,
+    ) -> None:
+        # Ensure a proper reference is provided.
+        if reference is None:
+            assert hasattr(self, "reference"), "error reference not provided!"
+            reference = self.reference
+        assert _is_kebab_case(reference), "error reference must be kebab-case!"
+
+        self.kind = kind
+        self.reference = reference
+
+        self.message = message
+        self.context = context
+
+        self.note_stmt = note_stmt
+        self.hint_stmt = hint_stmt
+
+        self.link = link
+
+        super().__init__(f"<{self.__class__.__name__}: {self.reference}>")
+
+    def __repr__(self) -> str:
+        return (
+            f"<{self.__class__.__name__}("
+            f"reference={self.reference!r}, "
+            f"message={self.message!r}, "
+            f"context={self.context!r}, "
+            f"note_stmt={self.note_stmt!r}, "
+            f"hint_stmt={self.hint_stmt!r}"
+            ")>"
+        )
+
+    def __rich_console__(
+        self,
+        console: Console,
+        options: ConsoleOptions,
+    ) -> RenderResult:
+        colour = "red" if self.kind == "error" else "yellow"
+
+        yield f"[{colour} bold]{self.kind}[/]: [bold]{self.reference}[/]"
+        yield ""
+
+        if not options.ascii_only:
+            # Present the main message, with relevant context indented.
+            if self.context is not None:
+                yield _prefix_with_indent(
+                    self.message,
+                    console,
+                    prefix=f"[{colour}]×[/] ",
+                    indent=f"[{colour}]│[/] ",
+                )
+                yield _prefix_with_indent(
+                    self.context,
+                    console,
+                    prefix=f"[{colour}]╰─>[/] ",
+                    indent=f"[{colour}]   [/] ",
+                )
+            else:
+                yield _prefix_with_indent(
+                    self.message,
+                    console,
+                    prefix="[red]×[/] ",
+                    indent="  ",
+                )
+        else:
+            yield self.message
+            if self.context is not None:
+                yield ""
+                yield self.context
+
+        if self.note_stmt is not None or self.hint_stmt is not None:
+            yield ""
+
+        if self.note_stmt is not None:
+            yield _prefix_with_indent(
+                self.note_stmt,
+                console,
+                prefix="[magenta bold]note[/]: ",
+                indent="      ",
+            )
+        if self.hint_stmt is not None:
+            yield _prefix_with_indent(
+                self.hint_stmt,
+                console,
+                prefix="[cyan bold]hint[/]: ",
+                indent="      ",
+            )
+
+        if self.link is not None:
+            yield ""
+            yield f"Link: {self.link}"
+
+
+#
+# Actual Errors
+#
+class ConfigurationError(PipError):
+    """General exception in configuration"""
+
+
+class InstallationError(PipError):
+    """General exception during installation"""
+
+
+class FailedToPrepareCandidate(InstallationError):
+    """Raised when we fail to prepare a candidate (i.e. fetch and generate metadata).
+
+    This is intentionally not a diagnostic error, since the output will be presented
+    above this error, when this occurs. This should instead present information to the
+    user.
+    """
+
+    def __init__(
+        self, *, package_name: str, requirement_chain: str, failed_step: str
+    ) -> None:
+        super().__init__(f"Failed to build '{package_name}' when {failed_step.lower()}")
+        self.package_name = package_name
+        self.requirement_chain = requirement_chain
+        self.failed_step = failed_step
+
+
+class MissingPyProjectBuildRequires(DiagnosticPipError):
+    """Raised when pyproject.toml has `build-system`, but no `build-system.requires`."""
+
+    reference = "missing-pyproject-build-system-requires"
+
+    def __init__(self, *, package: str) -> None:
+        super().__init__(
+            message=f"Can not process {escape(package)}",
+            context=Text(
+                "This package has an invalid pyproject.toml file.\n"
+                "The [build-system] table is missing the mandatory `requires` key."
+            ),
+            note_stmt="This is an issue with the package mentioned above, not pip.",
+            hint_stmt=Text("See PEP 518 for the detailed specification."),
+        )
+
+
+class InvalidPyProjectBuildRequires(DiagnosticPipError):
+    """Raised when pyproject.toml an invalid `build-system.requires`."""
+
+    reference = "invalid-pyproject-build-system-requires"
+
+    def __init__(self, *, package: str, reason: str) -> None:
+        super().__init__(
+            message=f"Can not process {escape(package)}",
+            context=Text(
+                "This package has an invalid `build-system.requires` key in "
+                f"pyproject.toml.\n{reason}"
+            ),
+            note_stmt="This is an issue with the package mentioned above, not pip.",
+            hint_stmt=Text("See PEP 518 for the detailed specification."),
+        )
+
+
+class NoneMetadataError(PipError):
+    """Raised when accessing a Distribution's "METADATA" or "PKG-INFO".
+
+    This signifies an inconsistency, when the Distribution claims to have
+    the metadata file (if not, raise ``FileNotFoundError`` instead), but is
+    not actually able to produce its content. This may be due to permission
+    errors.
+    """
+
+    def __init__(
+        self,
+        dist: BaseDistribution,
+        metadata_name: str,
+    ) -> None:
+        """
+        :param dist: A Distribution object.
+        :param metadata_name: The name of the metadata being accessed
+            (can be "METADATA" or "PKG-INFO").
+        """
+        self.dist = dist
+        self.metadata_name = metadata_name
+
+    def __str__(self) -> str:
+        # Use `dist` in the error message because its stringification
+        # includes more information, like the version and location.
+        return f"None {self.metadata_name} metadata found for distribution: {self.dist}"
+
+
+class UserInstallationInvalid(InstallationError):
+    """A --user install is requested on an environment without user site."""
+
+    def __str__(self) -> str:
+        return "User base directory is not specified"
+
+
+class InvalidSchemeCombination(InstallationError):
+    def __str__(self) -> str:
+        before = ", ".join(str(a) for a in self.args[:-1])
+        return f"Cannot set {before} and {self.args[-1]} together"
+
+
+class DistributionNotFound(InstallationError):
+    """Raised when a distribution cannot be found to satisfy a requirement"""
+
+
+class RequirementsFileParseError(InstallationError):
+    """Raised when a general error occurs parsing a requirements file line."""
+
+
+class BestVersionAlreadyInstalled(PipError):
+    """Raised when the most up-to-date version of a package is already
+    installed."""
+
+
+class BadCommand(PipError):
+    """Raised when virtualenv or a command is not found"""
+
+
+class CommandError(PipError):
+    """Raised when there is an error in command-line arguments"""
+
+
+class PreviousBuildDirError(PipError):
+    """Raised when there's a previous conflicting build directory"""
+
+
+class NetworkConnectionError(PipError):
+    """HTTP connection error"""
+
+    def __init__(
+        self,
+        error_msg: str,
+        response: Response | None = None,
+        request: Request | PreparedRequest | None = None,
+    ) -> None:
+        """
+        Initialize NetworkConnectionError with  `request` and `response`
+        objects.
+        """
+        self.response = response
+        self.request = request
+        self.error_msg = error_msg
+        if (
+            self.response is not None
+            and not self.request
+            and hasattr(response, "request")
+        ):
+            self.request = self.response.request
+        super().__init__(error_msg, response, request)
+
+    def __str__(self) -> str:
+        return str(self.error_msg)
+
+
+class ConnectionFailedError(DiagnosticPipError):
+    reference = "connection-failed"
+
+    def __init__(self, url: str, host: str, error: Exception) -> None:
+        from http.client import RemoteDisconnected
+
+        from pip._vendor.urllib3.exceptions import (
+            NameResolutionError,
+            NewConnectionError,
+            ProtocolError,
+        )
+
+        details = str(error)
+        if isinstance(error, NameResolutionError):
+            parts = details.split("Failed to resolve ", maxsplit=1)
+            if len(parts) == 2:
+                details = "Failed to resolve IP address for " + parts[1]
+        elif isinstance(error, NewConnectionError):
+            parts = details.split("Failed to establish a new connection: ", maxsplit=1)
+            if len(parts) == 2:
+                _, details = parts
+        elif isinstance(error, ProtocolError):
+            try:
+                reason = error.args[1]
+            except IndexError:
+                pass
+            else:
+                if isinstance(reason, (RemoteDisconnected, ConnectionResetError)):
+                    details = (
+                        "the connection was closed without a reply from the server."
+                    )
+
+        super().__init__(
+            message=(
+                f"Failed to connect to [magenta]{escape(host)}[/] while fetching "
+                f"{escape(url)}"
+            ),
+            context=Text(details),
+            hint_stmt=(
+                "Are you connected to the Internet? If so, check whether your system "
+                f"can connect to [magenta]{escape(host)}[/] before trying again. "
+                "There may be a firewall or proxy that's preventing the connection."
+            ),
+        )
+
+
+class ConnectionTimeoutError(DiagnosticPipError):
+    reference = "connection-timeout"
+
+    def __init__(
+        self,
+        url: str,
+        host: str,
+        *,
+        kind: Literal["connect", "read"],
+        timeout: float,
+    ) -> None:
+        context = Text.assemble(
+            (host, "magenta"), f" didn't respond within {timeout} seconds"
+        )
+        if kind == "connect":
+            context.append(" (while establishing a connection)")
+        super().__init__(
+            message=f"Unable to fetch {escape(url)}",
+            context=context,
+            hint_stmt=(
+                "This is probably a temporary issue with the remote server or the "
+                "network connection. If this error persists, check the network "
+                "configuration. There may be a firewall or proxy that's preventing "
+                "the connection."
+            ),
+        )
+
+
+class SSLMissingError(DiagnosticPipError):
+    reference = "ssl-missing"
+
+    def __init__(self, url: str) -> None:
+        super().__init__(
+            message=f"Failed to establish a secure connection for {escape(url)}",
+            context="The 'ssl' module is unavailable but required for HTTPS URLs",
+            hint_stmt=None,
+        )
+
+
+class SSLVerificationError(DiagnosticPipError):
+    reference = "ssl-verification-failed"
+
+    def __init__(self, url: str, host: str, error: urllib3.exceptions.SSLError) -> None:
+        message = (
+            "Failed to establish a secure connection to "
+            f"[magenta]{escape(host)}[/] while fetching {escape(url)}"
+        )
+        hint = "You may need to use --cert or check your proxy/firewall configuration"
+        super().__init__(message=message, context=Text(str(error)), hint_stmt=hint)
+
+
+class ProxyConnectionError(DiagnosticPipError):
+    reference = "proxy-connection-failed"
+
+    def __init__(
+        self, url: str, proxy: str, error: urllib3.exceptions.ProxyError
+    ) -> None:
+        super().__init__(
+            message=(
+                "Failed to connect to proxy "
+                f"[magenta]{escape(proxy)}[/] while fetching {escape(url)}"
+            ),
+            context=Text(str(error)),
+            hint_stmt="This is likely a proxy configuration issue.",
+        )
+
+
+class InvalidWheelFilename(InstallationError):
+    """Invalid wheel filename."""
+
+
+class UnsupportedWheel(InstallationError):
+    """Unsupported wheel."""
+
+
+class InvalidWheel(InstallationError):
+    """Invalid (e.g. corrupt) wheel."""
+
+    def __init__(self, location: str, name: str):
+        self.location = location
+        self.name = name
+
+    def __str__(self) -> str:
+        return f"Wheel '{self.name}' located at {self.location} is invalid."
+
+
+class MetadataInconsistent(InstallationError):
+    """Built metadata contains inconsistent information.
+
+    This is raised when the metadata contains values (e.g. name and version)
+    that do not match the information previously obtained from sdist filename,
+    user-supplied ``#egg=`` value, or an install requirement name.
+    """
+
+    def __init__(
+        self, ireq: InstallRequirement, field: str, f_val: str, m_val: str
+    ) -> None:
+        self.ireq = ireq
+        self.field = field
+        self.f_val = f_val
+        self.m_val = m_val
+
+    def __str__(self) -> str:
+        return (
+            f"Requested {self.ireq} has inconsistent {self.field}: "
+            f"expected {self.f_val!r}, but metadata has {self.m_val!r}"
+        )
+
+
+class SidecarMetadataInconsistent(MetadataInconsistent):
+    """The wheel's METADATA disagrees with its PEP 658 ``.metadata`` file.
+
+    Raised after the wheel has been downloaded and hash-verified, when a
+    resolver-affecting field in the wheel's embedded ``METADATA`` does not
+    match the value taken from the remote ``.metadata`` sidecar that drove
+    resolution. ``f_val`` is the sidecar value, ``m_val`` is the wheel value.
+    """
+
+    def __str__(self) -> str:
+        return (
+            f"Requested {self.ireq} has inconsistent {self.field} between "
+            f"its PEP 658 .metadata file and the wheel's METADATA: "
+            f"sidecar has {self.f_val!r}, wheel has {self.m_val!r}"
+        )
+
+
+class MetadataInvalid(InstallationError):
+    """Metadata is invalid."""
+
+    def __init__(self, ireq: InstallRequirement, error: str) -> None:
+        self.ireq = ireq
+        self.error = error
+
+    def __str__(self) -> str:
+        return f"Requested {self.ireq} has invalid metadata: {self.error}"
+
+
+class InstallationSubprocessError(DiagnosticPipError, InstallationError):
+    """A subprocess call failed."""
+
+    reference = "subprocess-exited-with-error"
+
+    def __init__(
+        self,
+        *,
+        command_description: str,
+        exit_code: int,
+        output_lines: list[str] | None,
+    ) -> None:
+        if output_lines is None:
+            output_prompt = Text("No available output.")
+        else:
+            output_prompt = (
+                Text.from_markup(f"[red][{len(output_lines)} lines of output][/]\n")
+                + Text("".join(output_lines))
+                + Text.from_markup(R"[red]\[end of output][/]")
+            )
+
+        super().__init__(
+            message=(
+                f"[green]{escape(command_description)}[/] did not run successfully.\n"
+                f"exit code: {exit_code}"
+            ),
+            context=output_prompt,
+            hint_stmt=None,
+            note_stmt=(
+                "This error originates from a subprocess, and is likely not a "
+                "problem with pip."
+            ),
+        )
+
+        self.command_description = command_description
+        self.exit_code = exit_code
+
+    def __str__(self) -> str:
+        return f"{self.command_description} exited with {self.exit_code}"
+
+
+class MetadataGenerationFailed(DiagnosticPipError, InstallationError):
+    reference = "metadata-generation-failed"
+
+    def __init__(
+        self,
+        *,
+        package_details: str,
+    ) -> None:
+        super().__init__(
+            message="Encountered error while generating package metadata.",
+            context=escape(package_details),
+            hint_stmt="See above for details.",
+            note_stmt="This is an issue with the package mentioned above, not pip.",
+        )
+
+    def __str__(self) -> str:
+        return "metadata generation failed"
+
+
+class HashErrors(InstallationError):
+    """Multiple HashError instances rolled into one for reporting"""
+
+    def __init__(self) -> None:
+        self.errors: list[HashError] = []
+
+    def append(self, error: HashError) -> None:
+        self.errors.append(error)
+
+    def __str__(self) -> str:
+        lines = []
+        self.errors.sort(key=lambda e: e.order)
+        for cls, errors_of_cls in groupby(self.errors, lambda e: e.__class__):
+            lines.append(cls.head)
+            lines.extend(e.body() for e in errors_of_cls)
+        if lines:
+            return "\n".join(lines)
+        return ""
+
+    def __bool__(self) -> bool:
+        return bool(self.errors)
+
+
+class HashError(InstallationError):
+    """
+    A failure to verify a package against known-good hashes
+
+    :cvar order: An int sorting hash exception classes by difficulty of
+        recovery (lower being harder), so the user doesn't bother fretting
+        about unpinned packages when he has deeper issues, like VCS
+        dependencies, to deal with. Also keeps error reports in a
+        deterministic order.
+    :cvar head: A section heading for display above potentially many
+        exceptions of this kind
+    :ivar req: The InstallRequirement that triggered this error. This is
+        pasted on after the exception is instantiated, because it's not
+        typically available earlier.
+
+    """
+
+    req: InstallRequirement | None = None
+    head = ""
+    order: int = -1
+
+    def body(self) -> str:
+        """Return a summary of me for display under the heading.
+
+        This default implementation simply prints a description of the
+        triggering requirement.
+
+        :param req: The InstallRequirement that provoked this error, with
+            its link already populated by the resolver's _populate_link().
+
+        """
+        return f"    {self._requirement_name()}"
+
+    def __str__(self) -> str:
+        return f"{self.head}\n{self.body()}"
+
+    def _requirement_name(self) -> str:
+        """Return a description of the requirement that triggered me.
+
+        This default implementation returns long description of the req, with
+        line numbers
+
+        """
+        return str(self.req) if self.req else "unknown package"
+
+
+class VcsHashUnsupported(HashError):
+    """A hash was provided for a version-control-system-based requirement, but
+    we don't have a method for hashing those."""
+
+    order = 0
+    head = (
+        "Can't verify hashes for these requirements because we don't "
+        "have a way to hash version control repositories:"
+    )
+
+
+class DirectoryUrlHashUnsupported(HashError):
+    """A hash was provided for a version-control-system-based requirement, but
+    we don't have a method for hashing those."""
+
+    order = 1
+    head = (
+        "Can't verify hashes for these file:// requirements because they "
+        "point to directories:"
+    )
+
+
+class HashMissing(HashError):
+    """A hash was needed for a requirement but is absent."""
+
+    order = 2
+    head = (
+        "Hashes are required in --require-hashes mode, but they are "
+        "missing from some requirements. Here is a list of those "
+        "requirements along with the hashes their downloaded archives "
+        "actually had. Add lines like these to your requirements files to "
+        "prevent tampering. (If you did not enable --require-hashes "
+        "manually, note that it turns on automatically when any package "
+        "has a hash.)"
+    )
+
+    def __init__(self, gotten_hash: str) -> None:
+        """
+        :param gotten_hash: The hash of the (possibly malicious) archive we
+            just downloaded
+        """
+        self.gotten_hash = gotten_hash
+
+    def body(self) -> str:
+        # Dodge circular import.
+        from pip._internal.utils.hashes import FAVORITE_HASH
+
+        package = None
+        if self.req:
+            # In the case of URL-based requirements, display the original URL
+            # seen in the requirements file rather than the package name,
+            # so the output can be directly copied into the requirements file.
+            package = (
+                self.req.original_link
+                if self.req.is_direct
+                # In case someone feeds something downright stupid
+                # to InstallRequirement's constructor.
+                else getattr(self.req, "req", None)
+            )
+        return "    {} --hash={}:{}".format(
+            package or "unknown package", FAVORITE_HASH, self.gotten_hash
+        )
+
+
+class HashUnpinned(HashError):
+    """A requirement had a hash specified but was not pinned to a specific
+    version."""
+
+    order = 3
+    head = (
+        "In --require-hashes mode, all requirements must have their "
+        "versions pinned with ==. These do not:"
+    )
+
+
+class HashMismatch(HashError):
+    """
+    Distribution file hash values don't match.
+
+    :ivar package_name: The name of the package that triggered the hash
+        mismatch. Feel free to write to this after the exception is raise to
+        improve its error message.
+
+    """
+
+    order = 4
+    head = (
+        "THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS "
+        "FILE. If you have updated the package versions, please update "
+        "the hashes. Otherwise, examine the package contents carefully; "
+        "someone may have tampered with them."
+    )
+
+    def __init__(self, allowed: dict[str, list[str]], gots: dict[str, _Hash]) -> None:
+        """
+        :param allowed: A dict of algorithm names pointing to lists of allowed
+            hex digests
+        :param gots: A dict of algorithm names pointing to hashes we
+            actually got from the files under suspicion
+        """
+        self.allowed = allowed
+        self.gots = gots
+
+    def body(self) -> str:
+        return f"    {self._requirement_name()}:\n{self._hash_comparison()}"
+
+    def _hash_comparison(self) -> str:
+        """
+        Return a comparison of actual and expected hash values.
+
+        Example::
+
+               Expected sha256 abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde
+                            or 123451234512345123451234512345123451234512345
+                    Got        bcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdef
+
+        """
+
+        def hash_then_or(hash_name: str) -> chain[str]:
+            # For now, all the decent hashes have 6-char names, so we can get
+            # away with hard-coding space literals.
+            return chain([hash_name], repeat("    or"))
+
+        lines: list[str] = []
+        for hash_name, expecteds in self.allowed.items():
+            prefix = hash_then_or(hash_name)
+            lines.extend((f"        Expected {next(prefix)} {e}") for e in expecteds)
+            lines.append(
+                f"             Got        {self.gots[hash_name].hexdigest()}\n"
+            )
+        return "\n".join(lines)
+
+
+class UnsupportedPythonVersion(InstallationError):
+    """Unsupported python version according to Requires-Python package
+    metadata."""
+
+
+class ConfigurationFileCouldNotBeLoaded(ConfigurationError):
+    """When there are errors while loading a configuration file"""
+
+    def __init__(
+        self,
+        reason: str = "could not be loaded",
+        fname: str | None = None,
+        error: configparser.Error | None = None,
+    ) -> None:
+        super().__init__(error)
+        self.reason = reason
+        self.fname = fname
+        self.error = error
+
+    def __str__(self) -> str:
+        if self.fname is not None:
+            message_part = f" in {self.fname}."
+        else:
+            assert self.error is not None
+            message_part = f".\n{self.error}\n"
+        return f"Configuration file {self.reason}{message_part}"
+
+
+_DEFAULT_EXTERNALLY_MANAGED_ERROR = f"""\
+The Python environment under {sys.prefix} is managed externally, and may not be
+manipulated by the user. Please use specific tooling from the distributor of
+the Python installation to interact with this environment instead.
+"""
+
+
+class ExternallyManagedEnvironment(DiagnosticPipError):
+    """The current environment is externally managed.
+
+    This is raised when the current environment is externally managed, as
+    defined by `PEP 668`_. The ``EXTERNALLY-MANAGED`` configuration is checked
+    and displayed when the error is bubbled up to the user.
+
+    :param error: The error message read from ``EXTERNALLY-MANAGED``.
+    """
+
+    reference = "externally-managed-environment"
+
+    def __init__(self, error: str | None) -> None:
+        if error is None:
+            context = Text(_DEFAULT_EXTERNALLY_MANAGED_ERROR)
+        else:
+            context = Text(error)
+        super().__init__(
+            message="This environment is externally managed",
+            context=context,
+            note_stmt=(
+                "If you believe this is a mistake, please contact your "
+                "Python installation or OS distribution provider. "
+                "You can override this, at the risk of breaking your Python "
+                "installation or OS, by passing --break-system-packages."
+            ),
+            hint_stmt=Text("See PEP 668 for the detailed specification."),
+        )
+
+    @staticmethod
+    def _iter_externally_managed_error_keys() -> Iterator[str]:
+        # LC_MESSAGES is in POSIX, but not the C standard. The most common
+        # platform that does not implement this category is Windows, where
+        # using other categories for console message localization is equally
+        # unreliable, so we fall back to the locale-less vendor message. This
+        # can always be re-evaluated when a vendor proposes a new alternative.
+        try:
+            category = locale.LC_MESSAGES
+        except AttributeError:
+            lang: str | None = None
+        else:
+            lang, _ = locale.getlocale(category)
+        if lang is not None:
+            yield f"Error-{lang}"
+            for sep in ("-", "_"):
+                before, found, _ = lang.partition(sep)
+                if not found:
+                    continue
+                yield f"Error-{before}"
+        yield "Error"
+
+    @classmethod
+    def from_config(
+        cls,
+        config: pathlib.Path | str,
+    ) -> ExternallyManagedEnvironment:
+        parser = configparser.ConfigParser(interpolation=None)
+        try:
+            parser.read(config, encoding="utf-8")
+            section = parser["externally-managed"]
+            for key in cls._iter_externally_managed_error_keys():
+                with contextlib.suppress(KeyError):
+                    return cls(section[key])
+        except KeyError:
+            pass
+        except (OSError, UnicodeDecodeError, configparser.ParsingError):
+            from pip._internal.utils._log import VERBOSE
+
+            exc_info = logger.isEnabledFor(VERBOSE)
+            logger.warning("Failed to read %s", config, exc_info=exc_info)
+        return cls(None)
+
+
+class UninstallMissingRecord(DiagnosticPipError):
+    reference = "uninstall-no-record-file"
+
+    def __init__(self, *, distribution: BaseDistribution) -> None:
+        installer = distribution.installer
+        if not installer or installer == "pip":
+            dep = f"{distribution.raw_name}=={distribution.version}"
+            hint = Text.assemble(
+                "You might be able to recover from this via: ",
+                (f"pip install --ignore-installed --no-deps {dep}", "green"),
+            )
+        else:
+            hint = Text(
+                f"The package was installed by {installer}. "
+                "You should check if it can uninstall the package."
+            )
+
+        super().__init__(
+            message=Text(f"Cannot uninstall {distribution}"),
+            context=(
+                "The package's contents are unknown: "
+                f"no RECORD file was found for {distribution.raw_name}."
+            ),
+            hint_stmt=hint,
+        )
+
+
+class LegacyDistutilsInstall(DiagnosticPipError):
+    reference = "uninstall-distutils-installed-package"
+
+    def __init__(self, *, distribution: BaseDistribution) -> None:
+        super().__init__(
+            message=Text(f"Cannot uninstall {distribution}"),
+            context=(
+                "It is a distutils installed project and thus we cannot accurately "
+                "determine which files belong to it which would lead to only a partial "
+                "uninstall."
+            ),
+            hint_stmt=None,
+        )
+
+
+class InvalidInstalledPackage(DiagnosticPipError):
+    reference = "invalid-installed-package"
+
+    def __init__(
+        self,
+        *,
+        dist: BaseDistribution,
+        invalid_exc: InvalidRequirement | InvalidVersion,
+    ) -> None:
+        installed_location = dist.installed_location
+
+        if isinstance(invalid_exc, InvalidRequirement):
+            invalid_type = "requirement"
+        else:
+            invalid_type = "version"
+
+        super().__init__(
+            message=Text(
+                f"Cannot process installed package {dist} "
+                + (f"in {installed_location!r} " if installed_location else "")
+                + f"because it has an invalid {invalid_type}:\n{invalid_exc.args[0]}"
+            ),
+            context=(
+                "Starting with pip 24.1, packages with invalid "
+                f"{invalid_type}s can not be processed."
+            ),
+            hint_stmt="To proceed this package must be uninstalled.",
+        )
+
+
+class IncompleteDownloadError(DiagnosticPipError):
+    """Raised when the downloader receives fewer bytes than advertised
+    in the Content-Length header."""
+
+    reference = "incomplete-download"
+
+    def __init__(self, download: _FileDownload) -> None:
+        # Dodge circular import.
+        from pip._internal.utils.misc import format_size
+
+        assert download.size is not None
+        download_status = (
+            f"{format_size(download.bytes_received)}/{format_size(download.size)}"
+        )
+        if download.reattempts:
+            retry_status = f"after {download.reattempts + 1} attempts "
+            hint = "Use --resume-retries to configure resume attempt limit."
+        else:
+            # Download retrying is not enabled.
+            retry_status = ""
+            hint = "Consider using --resume-retries to enable download resumption."
+        message = Text(
+            f"Download failed {retry_status}because not enough bytes "
+            f"were received ({download_status})"
+        )
+
+        super().__init__(
+            message=message,
+            context=f"URL: {download.link.redacted_url}",
+            hint_stmt=hint,
+            note_stmt="This is an issue with network connectivity, not pip.",
+        )
+
+
+class ResolutionTooDeepError(DiagnosticPipError):
+    """Raised when the dependency resolver exceeds the maximum recursion depth."""
+
+    reference = "resolution-too-deep"
+
+    def __init__(self) -> None:
+        super().__init__(
+            message="Dependency resolution exceeded maximum depth",
+            context=(
+                "Pip cannot resolve the current dependencies as the dependency graph "
+                "is too complex for pip to solve efficiently."
+            ),
+            hint_stmt=(
+                "Try adding lower bounds to constrain your dependencies, "
+                "for example: 'package>=2.0.0' instead of just 'package'. "
+            ),
+            link="https://pip.pypa.io/en/stable/topics/dependency-resolution/#handling-resolution-too-deep-errors",
+        )
+
+
+class InstallWheelBuildError(DiagnosticPipError):
+    reference = "failed-wheel-build-for-install"
+
+    def __init__(self, failed: list[InstallRequirement]) -> None:
+        super().__init__(
+            message=(
+                "Failed to build installable wheels for some "
+                "pyproject.toml based projects"
+            ),
+            context=", ".join(r.name for r in failed),  # type: ignore
+            hint_stmt=None,
+        )
+
+
+class InvalidEggFragment(DiagnosticPipError):
+    reference = "invalid-egg-fragment"
+
+    def __init__(self, link: Link, fragment: str) -> None:
+        hint = ""
+        if ">" in fragment or "=" in fragment or "<" in fragment:
+            hint = (
+                "Version specifiers are silently ignored for URL references. "
+                "Remove them. "
+            )
+        if "[" in fragment and "]" in fragment:
+            hint += "Try using the Direct URL requirement syntax: 'name[extra] @ URL'"
+
+        if not hint:
+            hint = "Egg fragments can only be a valid project name."
+
+        super().__init__(
+            message=f"The '{escape(fragment)}' egg fragment is invalid",
+            context=f"from '{escape(str(link))}'",
+            hint_stmt=escape(hint),
+        )
+
+
+class BuildDependencyInstallError(DiagnosticPipError):
+    """Raised when build dependencies cannot be installed."""
+
+    reference = "failed-build-dependency-install"
+
+    def __init__(
+        self,
+        req: InstallRequirement | None,
+        build_reqs: Iterable[str],
+        *,
+        cause: Exception,
+        log_lines: list[str] | None,
+    ) -> None:
+        if isinstance(cause, PipError):
+            note = "This is likely not a problem with pip."
+        else:
+            note = (
+                "pip crashed unexpectedly. Please file an issue on pip's issue "
+                "tracker: https://github.com/pypa/pip/issues/new"
+            )
+
+        if log_lines is None:
+            # No logs are available, they must have been printed earlier.
+            context = Text("See above for more details.")
+        else:
+            if isinstance(cause, PipError):
+                log_lines.append(f"ERROR: {cause}")
+            else:
+                # Split rendered error into real lines without trailing newlines.
+                log_lines.extend(
+                    "".join(traceback.format_exception(cause)).splitlines()
+                )
+
+            context = Text.assemble(
+                f"Installing {' '.join(build_reqs)}\n",
+                (f"[{len(log_lines)} lines of output]\n", "red"),
+                "\n".join(log_lines),
+                ("\n[end of output]", "red"),
+            )
+
+        message = Text("Cannot install build dependencies", "green")
+        if req:
+            message += Text(f" for {req}")
+        super().__init__(
+            message=message, context=context, hint_stmt=None, note_stmt=note
+        )
+
+
+class VenvImportError(DiagnosticPipError):
+    """Raised when 'venv' can't be imported."""
+
+    reference = "venv-import-error"
+
+    def __init__(self) -> None:
+        if sys.platform != "linux":
+            hint_stmt = None
+        else:
+            hint_stmt = (
+                "If this is an OS-provided Python, it's likely that your OS "
+                "package maintainers have split Python's standard library across "
+                "multiple OS packages."
+            )
+        super().__init__(
+            message="Cannot import the 'venv' module of the Python standard library",
+            context=(
+                "This is a symptom of a broken/modified Python, which cannot be used "
+                "with pip."
+            ),
+            note_stmt="This is an issue with the Python installation itself, not pip.",
+            hint_stmt=hint_stmt,
+        )
+
+
+class VenvCreationError(DiagnosticPipError):
+    """Raised when a virtual environment can't be created."""
+
+    reference = "venv-creation-error"
+
+    def __init__(self, context: str) -> None:
+        if os.name == "nt":
+            hint = "This may be caused by running antivirus software."
+        else:
+            hint = None
+        super().__init__(
+            message="Cannot create a virtual environment",
+            context=Text(context),
+            hint_stmt=hint,
+        )
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/index/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/index/__init__.py
new file mode 100644
index 0000000..197dd75
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/index/__init__.py
@@ -0,0 +1 @@
+"""Index interaction code"""
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..6831cb4
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/collector.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/collector.cpython-312.pyc
new file mode 100644
index 0000000..bfb1b40
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/collector.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-312.pyc
new file mode 100644
index 0000000..2c699c7
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/sources.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/sources.cpython-312.pyc
new file mode 100644
index 0000000..b9bf3a1
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/sources.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/index/collector.py b/venv/lib/python3.12/site-packages/pip/_internal/index/collector.py
new file mode 100644
index 0000000..a77c398
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/index/collector.py
@@ -0,0 +1,510 @@
+"""
+The main purpose of this module is to expose LinkCollector.collect_sources().
+"""
+
+from __future__ import annotations
+
+import collections
+import email.message
+import functools
+import itertools
+import json
+import logging
+import os
+import urllib.parse
+from collections.abc import Callable, Iterable, MutableMapping, Sequence
+from dataclasses import dataclass
+from html.parser import HTMLParser
+from optparse import Values
+from typing import (
+    NamedTuple,
+    Protocol,
+)
+
+from pip._vendor.packaging.utils import canonicalize_name
+from pip._vendor.requests import Response
+from pip._vendor.requests.exceptions import RetryError
+
+from pip._internal.exceptions import (
+    ConnectionFailedError,
+    ConnectionTimeoutError,
+    NetworkConnectionError,
+    ProxyConnectionError,
+    SSLMissingError,
+    SSLVerificationError,
+)
+from pip._internal.models.link import Link
+from pip._internal.models.search_scope import SearchScope
+from pip._internal.network.session import PipSession
+from pip._internal.network.utils import raise_for_status
+from pip._internal.utils.filetypes import is_archive_file
+from pip._internal.utils.misc import redact_auth_from_url
+from pip._internal.utils.urls import url_to_path
+from pip._internal.vcs import vcs
+
+from .sources import CandidatesFromPage, LinkSource, build_source
+
+logger = logging.getLogger(__name__)
+
+ResponseHeaders = MutableMapping[str, str]
+
+
+def _match_vcs_scheme(url: str) -> str | None:
+    """Look for VCS schemes in the URL.
+
+    Returns the matched VCS scheme, or None if there's no match.
+    """
+    for scheme in vcs.schemes:
+        if url.lower().startswith(scheme) and url[len(scheme)] in "+:":
+            return scheme
+    return None
+
+
+class _NotAPIContent(Exception):
+    def __init__(self, content_type: str, request_desc: str) -> None:
+        super().__init__(content_type, request_desc)
+        self.content_type = content_type
+        self.request_desc = request_desc
+
+
+def _ensure_api_header(response: Response) -> None:
+    """
+    Check the Content-Type header to ensure the response contains a Simple
+    API Response.
+
+    Raises `_NotAPIContent` if the content type is not a valid content-type.
+    """
+    content_type = response.headers.get("Content-Type", "Unknown")
+
+    content_type_l = content_type.lower()
+    if content_type_l.startswith(
+        (
+            "text/html",
+            "application/vnd.pypi.simple.v1+html",
+            "application/vnd.pypi.simple.v1+json",
+        )
+    ):
+        return
+
+    raise _NotAPIContent(content_type, response.request.method)  # type: ignore[arg-type]
+
+
+class _NotHTTP(Exception):
+    pass
+
+
+def _ensure_api_response(url: str, session: PipSession) -> None:
+    """
+    Send a HEAD request to the URL, and ensure the response contains a simple
+    API Response.
+
+    Raises `_NotHTTP` if the URL is not available for a HEAD request, or
+    `_NotAPIContent` if the content type is not a valid content type.
+    """
+    scheme, netloc, path, query, fragment = urllib.parse.urlsplit(url)
+    if scheme not in {"http", "https"}:
+        raise _NotHTTP()
+
+    resp = session.head(url, allow_redirects=True)
+    raise_for_status(resp)
+
+    _ensure_api_header(resp)
+
+
+def _get_simple_response(
+    url: str,
+    session: PipSession,
+    force_revalidate: bool = False,
+) -> Response:
+    """Access an Simple API response with GET, and return the response.
+
+    This consists of three parts:
+
+    1. If the URL looks suspiciously like an archive, send a HEAD first to
+       check the Content-Type is HTML or Simple API, to avoid downloading a
+       large file. Raise `_NotHTTP` if the content type cannot be determined, or
+       `_NotAPIContent` if it is not HTML or a Simple API.
+    2. Actually perform the request. Raise HTTP exceptions on network failures.
+    3. Check the Content-Type header to make sure we got a Simple API response,
+       and raise `_NotAPIContent` otherwise.
+    """
+    if is_archive_file(Link(url).filename):
+        _ensure_api_response(url, session=session)
+
+    logger.debug("Getting page %s", redact_auth_from_url(url))
+
+    headers = {
+        "Accept": ", ".join(
+            [
+                "application/vnd.pypi.simple.v1+json",
+                "application/vnd.pypi.simple.v1+html; q=0.1",
+                "text/html; q=0.01",
+            ]
+        ),
+    }
+
+    if force_revalidate:
+        # Using max-age=0 rather than no-cache still supports conditional
+        # requests, minimizing traffic when the page hasn't changed.
+        # See pypa/pip#5670.
+        logger.debug("Refreshing package index response.")
+        headers["Cache-Control"] = "max-age=0"
+
+    resp = session.get(url, headers=headers)
+    raise_for_status(resp)
+
+    # The check for archives above only works if the url ends with
+    # something that looks like an archive. However that is not a
+    # requirement of an url. Unless we issue a HEAD request on every
+    # url we cannot know ahead of time for sure if something is a
+    # Simple API response or not. However we can check after we've
+    # downloaded it.
+    _ensure_api_header(resp)
+
+    logger.debug(
+        "Fetched page %s as %s",
+        redact_auth_from_url(url),
+        resp.headers.get("Content-Type", "Unknown"),
+    )
+
+    return resp
+
+
+def _get_encoding_from_headers(headers: ResponseHeaders) -> str | None:
+    """Determine if we have any encoding information in our headers."""
+    if headers and "Content-Type" in headers:
+        m = email.message.Message()
+        m["content-type"] = headers["Content-Type"]
+        charset = m.get_param("charset")
+        if charset:
+            return str(charset)
+    return None
+
+
+class CacheablePageContent:
+    def __init__(self, page: IndexContent) -> None:
+        assert page.cache_link_parsing
+        self.page = page
+
+    def __eq__(self, other: object) -> bool:
+        return isinstance(other, type(self)) and self.page.url == other.page.url
+
+    def __hash__(self) -> int:
+        return hash(self.page.url)
+
+
+class ParseLinks(Protocol):
+    def __call__(self, page: IndexContent) -> Iterable[Link]: ...
+
+
+def with_cached_index_content(fn: ParseLinks) -> ParseLinks:
+    """
+    Given a function that parses an Iterable[Link] from an IndexContent, cache the
+    function's result (keyed by CacheablePageContent), unless the IndexContent
+    `page` has `page.cache_link_parsing == False`.
+    """
+
+    @functools.cache
+    def wrapper(cacheable_page: CacheablePageContent) -> list[Link]:
+        return list(fn(cacheable_page.page))
+
+    @functools.wraps(fn)
+    def wrapper_wrapper(page: IndexContent) -> list[Link]:
+        if page.cache_link_parsing:
+            return wrapper(CacheablePageContent(page))
+        return list(fn(page))
+
+    return wrapper_wrapper
+
+
+@with_cached_index_content
+def parse_links(page: IndexContent) -> Iterable[Link]:
+    """
+    Parse a Simple API's Index Content, and yield its anchor elements as Link objects.
+    """
+
+    content_type_l = page.content_type.lower()
+    if content_type_l.startswith("application/vnd.pypi.simple.v1+json"):
+        data = json.loads(page.content)
+        for file in data.get("files", []):
+            link = Link.from_json(file, page.url)
+            if link is None:
+                continue
+            yield link
+        return
+
+    parser = HTMLLinkParser(page.url)
+    encoding = page.encoding or "utf-8"
+    parser.feed(page.content.decode(encoding))
+
+    url = page.url
+    base_url = parser.base_url or url
+    for anchor in parser.anchors:
+        link = Link.from_element(anchor, page_url=url, base_url=base_url)
+        if link is None:
+            continue
+        yield link
+
+
+@dataclass(frozen=True)
+class IndexContent:
+    """Represents one response (or page), along with its URL.
+
+    :param encoding: the encoding to decode the given content.
+    :param url: the URL from which the HTML was downloaded.
+    :param cache_link_parsing: whether links parsed from this page's url
+                               should be cached. PyPI index urls should
+                               have this set to False, for example.
+    """
+
+    content: bytes
+    content_type: str
+    encoding: str | None
+    url: str
+    cache_link_parsing: bool = True
+
+    def __str__(self) -> str:
+        return redact_auth_from_url(self.url)
+
+
+class HTMLLinkParser(HTMLParser):
+    """
+    HTMLParser that keeps the first base HREF and a list of all anchor
+    elements' attributes.
+    """
+
+    def __init__(self, url: str) -> None:
+        super().__init__(convert_charrefs=True)
+
+        self.url: str = url
+        self.base_url: str | None = None
+        self.anchors: list[dict[str, str | None]] = []
+
+    def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
+        if tag == "base" and self.base_url is None:
+            href = self.get_href(attrs)
+            if href is not None:
+                self.base_url = href
+        elif tag == "a":
+            self.anchors.append(dict(attrs))
+
+    def get_href(self, attrs: list[tuple[str, str | None]]) -> str | None:
+        for name, value in attrs:
+            if name == "href":
+                return value
+        return None
+
+
+def _handle_get_simple_fail(
+    link: Link,
+    reason: str | Exception,
+    meth: Callable[..., None] | None = None,
+) -> None:
+    if meth is None:
+        meth = logger.debug
+    meth("Could not fetch URL %s: %s - skipping", link, reason)
+
+
+def _make_index_content(
+    response: Response, cache_link_parsing: bool = True
+) -> IndexContent:
+    encoding = _get_encoding_from_headers(response.headers)
+    return IndexContent(
+        response.content,
+        response.headers["Content-Type"],
+        encoding=encoding,
+        url=response.url,
+        cache_link_parsing=cache_link_parsing,
+    )
+
+
+def _get_index_content(
+    link: Link,
+    *,
+    session: PipSession,
+    force_revalidate: bool = False,
+) -> IndexContent | None:
+    url = link.url.split("#", 1)[0]
+
+    # Check for VCS schemes that do not support lookup as web pages.
+    vcs_scheme = _match_vcs_scheme(url)
+    if vcs_scheme:
+        logger.warning(
+            "Cannot look at %s URL %s because it does not support lookup as web pages.",
+            vcs_scheme,
+            link,
+        )
+        return None
+
+    # Tack index.html onto file:// URLs that point to directories
+    if url.startswith("file:") and os.path.isdir(url_to_path(url)):
+        # add trailing slash if not present so urljoin doesn't trim
+        # final segment
+        if not url.endswith("/"):
+            url += "/"
+        # TODO: In the future, it would be nice if pip supported PEP 691
+        #       style responses in the file:// URLs, however there's no
+        #       standard file extension for application/vnd.pypi.simple.v1+json
+        #       so we'll need to come up with something on our own.
+        url = urllib.parse.urljoin(url, "index.html")
+        logger.debug(" file: URL is directory, getting %s", url)
+
+    try:
+        resp = _get_simple_response(
+            url, session=session, force_revalidate=force_revalidate
+        )
+    except _NotHTTP:
+        logger.warning(
+            "Skipping page %s because it looks like an archive, and cannot "
+            "be checked by a HTTP HEAD request.",
+            link,
+        )
+    except _NotAPIContent as exc:
+        logger.warning(
+            "Skipping page %s because the %s request got Content-Type: %s. "
+            "The only supported Content-Types are application/vnd.pypi.simple.v1+json, "
+            "application/vnd.pypi.simple.v1+html, and text/html",
+            link,
+            exc.request_desc,
+            exc.content_type,
+        )
+    except (RetryError, NetworkConnectionError) as exc:
+        _handle_get_simple_fail(link, exc)
+    except (SSLVerificationError, SSLMissingError) as exc:
+        reason = f"There was a problem confirming the ssl certificate: {exc.context}"
+        _handle_get_simple_fail(link, reason, meth=logger.info)
+    except ConnectionFailedError as exc:
+        _handle_get_simple_fail(link, f"connection error: {exc.context}")
+    except ProxyConnectionError as exc:
+        _handle_get_simple_fail(link, f"proxy connection error: {exc.context}")
+    except ConnectionTimeoutError as exc:
+        _handle_get_simple_fail(link, str(exc.context))
+    else:
+        return _make_index_content(resp, cache_link_parsing=link.cache_link_parsing)
+    return None
+
+
+class CollectedSources(NamedTuple):
+    find_links: Sequence[LinkSource | None]
+    index_urls: Sequence[LinkSource | None]
+
+
+class LinkCollector:
+    """
+    Responsible for collecting Link objects from all configured locations,
+    making network requests as needed.
+
+    The class's main method is its collect_sources() method.
+    """
+
+    def __init__(
+        self,
+        session: PipSession,
+        search_scope: SearchScope,
+    ) -> None:
+        self.search_scope = search_scope
+        self.session = session
+
+    @classmethod
+    def create(
+        cls,
+        session: PipSession,
+        options: Values,
+        suppress_no_index: bool = False,
+    ) -> LinkCollector:
+        """
+        :param session: The Session to use to make requests.
+        :param suppress_no_index: Whether to ignore the --no-index option
+            when constructing the SearchScope object.
+        """
+        index_urls = [options.index_url] + options.extra_index_urls
+        if options.no_index and not suppress_no_index:
+            logger.debug(
+                "Ignoring indexes: %s",
+                ",".join(redact_auth_from_url(url) for url in index_urls),
+            )
+            index_urls = []
+
+        # Make sure find_links is a list before passing to create().
+        find_links = options.find_links or []
+
+        search_scope = SearchScope.create(
+            find_links=find_links,
+            index_urls=index_urls,
+            no_index=options.no_index,
+        )
+        link_collector = LinkCollector(
+            session=session,
+            search_scope=search_scope,
+        )
+        return link_collector
+
+    @property
+    def find_links(self) -> list[str]:
+        return self.search_scope.find_links
+
+    def fetch_response(
+        self, location: Link, package_name: str | None = None
+    ) -> IndexContent | None:
+        """
+        Fetch an HTML page containing package links.
+        """
+        # By default we may return cached /simple/ responses. Users can force
+        # revalidation to ensure newly uploaded distributions become visible
+        # immediately after publication. See pypa/pip#5670.
+        force_revalidate = self.session.refresh_package
+        should_force_revalidate = ":all:" in force_revalidate or (
+            package_name is not None
+            and canonicalize_name(package_name) in force_revalidate
+        )
+        return _get_index_content(
+            location,
+            session=self.session,
+            force_revalidate=should_force_revalidate,
+        )
+
+    def collect_sources(
+        self,
+        project_name: str,
+        candidates_from_page: CandidatesFromPage,
+    ) -> CollectedSources:
+        # The OrderedDict calls deduplicate sources by URL.
+        index_url_sources = collections.OrderedDict(
+            build_source(
+                loc,
+                candidates_from_page=candidates_from_page,
+                page_validator=self.session.is_secure_origin,
+                expand_dir=False,
+                cache_link_parsing=False,
+                project_name=project_name,
+            )
+            for loc in self.search_scope.get_index_urls_locations(project_name)
+        ).values()
+        find_links_sources = collections.OrderedDict(
+            build_source(
+                loc,
+                candidates_from_page=candidates_from_page,
+                page_validator=self.session.is_secure_origin,
+                expand_dir=True,
+                cache_link_parsing=True,
+                project_name=project_name,
+            )
+            for loc in self.find_links
+        ).values()
+
+        if logger.isEnabledFor(logging.DEBUG):
+            lines = [
+                f"* {s.link}"
+                for s in itertools.chain(find_links_sources, index_url_sources)
+                if s is not None and s.link is not None
+            ]
+            lines = [
+                f"{len(lines)} location(s) to search "
+                f"for versions of {project_name}:"
+            ] + lines
+            logger.debug("\n".join(lines))
+
+        return CollectedSources(
+            find_links=list(find_links_sources),
+            index_urls=list(index_url_sources),
+        )
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/index/package_finder.py b/venv/lib/python3.12/site-packages/pip/_internal/index/package_finder.py
new file mode 100644
index 0000000..cf4a2ee
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/index/package_finder.py
@@ -0,0 +1,1152 @@
+"""Routines related to PyPI, indexes"""
+
+from __future__ import annotations
+
+import datetime
+import enum
+import functools
+import itertools
+import logging
+import re
+from collections.abc import Iterable
+from dataclasses import dataclass
+from typing import (
+    TYPE_CHECKING,
+)
+
+from pip._vendor.packaging import specifiers
+from pip._vendor.packaging.tags import Tag
+from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
+from pip._vendor.packaging.version import InvalidVersion, _BaseVersion
+from pip._vendor.packaging.version import parse as parse_version
+
+from pip._internal.exceptions import (
+    BestVersionAlreadyInstalled,
+    DistributionNotFound,
+    InstallationError,
+    InvalidWheelFilename,
+    UnsupportedWheel,
+)
+from pip._internal.index.collector import LinkCollector, parse_links
+from pip._internal.metadata import select_backend
+from pip._internal.models.candidate import InstallationCandidate
+from pip._internal.models.format_control import FormatControl
+from pip._internal.models.link import Link
+from pip._internal.models.release_control import ReleaseControl
+from pip._internal.models.search_scope import SearchScope
+from pip._internal.models.selection_prefs import SelectionPreferences
+from pip._internal.models.target_python import TargetPython
+from pip._internal.models.wheel import Wheel
+from pip._internal.req import InstallRequirement
+from pip._internal.utils._log import getLogger
+from pip._internal.utils.filetypes import WHEEL_EXTENSION
+from pip._internal.utils.hashes import Hashes
+from pip._internal.utils.logging import indent_log
+from pip._internal.utils.misc import build_netloc
+from pip._internal.utils.packaging import check_requires_python
+from pip._internal.utils.unpacking import SUPPORTED_EXTENSIONS
+
+if TYPE_CHECKING:
+    from typing import TypeGuard
+
+__all__ = ["FormatControl", "BestCandidateResult", "PackageFinder"]
+
+
+logger = getLogger(__name__)
+
+BuildTag = tuple[()] | tuple[int, str]
+CandidateSortingKey = tuple[int, int, int, _BaseVersion, int | None, BuildTag]
+
+
+def _check_link_requires_python(
+    link: Link,
+    version_info: tuple[int, int, int],
+    ignore_requires_python: bool = False,
+) -> bool:
+    """
+    Return whether the given Python version is compatible with a link's
+    "Requires-Python" value.
+
+    :param version_info: A 3-tuple of ints representing the Python
+        major-minor-micro version to check.
+    :param ignore_requires_python: Whether to ignore the "Requires-Python"
+        value if the given Python version isn't compatible.
+    """
+    try:
+        is_compatible = check_requires_python(
+            link.requires_python,
+            version_info=version_info,
+        )
+    except specifiers.InvalidSpecifier:
+        logger.debug(
+            "Ignoring invalid Requires-Python (%r) for link: %s",
+            link.requires_python,
+            link,
+        )
+    else:
+        if not is_compatible:
+            version = ".".join(map(str, version_info))
+            if not ignore_requires_python:
+                logger.verbose(
+                    "Link requires a different Python (%s not in: %r): %s",
+                    version,
+                    link.requires_python,
+                    link,
+                )
+                return False
+
+            logger.debug(
+                "Ignoring failed Requires-Python check (%s not in: %r) for link: %s",
+                version,
+                link.requires_python,
+                link,
+            )
+
+    return True
+
+
+class LinkType(enum.Enum):
+    candidate = enum.auto()
+    different_project = enum.auto()
+    yanked = enum.auto()
+    format_unsupported = enum.auto()
+    format_invalid = enum.auto()
+    platform_mismatch = enum.auto()
+    requires_python_mismatch = enum.auto()
+    upload_too_late = enum.auto()
+    upload_time_missing = enum.auto()
+
+
+class LinkEvaluator:
+    """
+    Responsible for evaluating links for a particular project.
+    """
+
+    _py_version_re = re.compile(r"-py([123]\.?[0-9]?)$")
+
+    # Don't include an allow_yanked default value to make sure each call
+    # site considers whether yanked releases are allowed. This also causes
+    # that decision to be made explicit in the calling code, which helps
+    # people when reading the code.
+    def __init__(
+        self,
+        project_name: str,
+        canonical_name: NormalizedName,
+        formats: frozenset[str],
+        target_python: TargetPython,
+        allow_yanked: bool,
+        ignore_requires_python: bool = False,
+        uploaded_prior_to: datetime.datetime | None = None,
+    ) -> None:
+        """
+        :param project_name: The user supplied package name.
+        :param canonical_name: The canonical package name.
+        :param formats: The formats allowed for this package. Should be a set
+            with 'binary' or 'source' or both in it.
+        :param target_python: The target Python interpreter to use when
+            evaluating link compatibility. This is used, for example, to
+            check wheel compatibility, as well as when checking the Python
+            version, e.g. the Python version embedded in a link filename
+            (or egg fragment) and against an HTML link's optional PEP 503
+            "data-requires-python" attribute.
+        :param allow_yanked: Whether files marked as yanked (in the sense
+            of PEP 592) are permitted to be candidates for install.
+        :param ignore_requires_python: Whether to ignore incompatible
+            PEP 503 "data-requires-python" values in HTML links. Defaults
+            to False.
+        :param uploaded_prior_to: If set, only allow links uploaded prior to
+            the given datetime.
+        """
+
+        self._allow_yanked = allow_yanked
+        self._canonical_name = canonical_name
+        self._ignore_requires_python = ignore_requires_python
+        self._formats = formats
+        self._target_python = target_python
+        self._uploaded_prior_to = uploaded_prior_to
+
+        self.project_name = project_name
+
+    def evaluate_link(self, link: Link) -> tuple[LinkType, str]:
+        """
+        Determine whether a link is a candidate for installation.
+
+        :return: A tuple (result, detail), where *result* is an enum
+            representing whether the evaluation found a candidate, or the reason
+            why one is not found. If a candidate is found, *detail* will be the
+            candidate's version string; if one is not found, it contains the
+            reason the link fails to qualify.
+        """
+        version = None
+        if link.is_yanked and not self._allow_yanked:
+            reason = link.yanked_reason or ""
+            return (LinkType.yanked, f"yanked for reason: {reason}")
+
+        if link.egg_fragment:
+            egg_info = link.egg_fragment
+            ext = link.ext
+        else:
+            egg_info, ext = link.splitext()
+            if not ext:
+                return (LinkType.format_unsupported, "not a file")
+            if ext not in SUPPORTED_EXTENSIONS:
+                return (
+                    LinkType.format_unsupported,
+                    f"unsupported archive format: {ext}",
+                )
+            if "binary" not in self._formats and ext == WHEEL_EXTENSION:
+                reason = f"No binaries permitted for {self.project_name}"
+                return (LinkType.format_unsupported, reason)
+            if "macosx10" in link.path and ext == ".zip":
+                return (LinkType.format_unsupported, "macosx10 one")
+            if ext == WHEEL_EXTENSION:
+                try:
+                    wheel = Wheel(link.filename)
+                except InvalidWheelFilename:
+                    return (
+                        LinkType.format_invalid,
+                        "invalid wheel filename",
+                    )
+                if wheel.name != self._canonical_name:
+                    reason = f"wrong project name (not {self.project_name})"
+                    return (LinkType.different_project, reason)
+
+                supported_tags = self._target_python.get_unsorted_tags()
+                if not wheel.supported(supported_tags):
+                    # Include the wheel's tags in the reason string to
+                    # simplify troubleshooting compatibility issues.
+                    file_tags = ", ".join(wheel.get_formatted_file_tags())
+                    reason = (
+                        f"none of the wheel's tags ({file_tags}) are compatible "
+                        f"(run pip debug --verbose to show compatible tags)"
+                    )
+                    return (LinkType.platform_mismatch, reason)
+
+                version = wheel.version
+
+        # Check upload-time filter after verifying the link is a package file.
+        # Skip this check for local files, as --uploaded-prior-to only applies
+        # to packages from indexes.
+        if self._uploaded_prior_to is not None and not link.is_file:
+            if link.upload_time is None:
+                if link.comes_from:
+                    index_info = f"Index {link.comes_from}"
+                else:
+                    index_info = "Index"
+
+                return (
+                    LinkType.upload_time_missing,
+                    f"{index_info} does not provide upload-time metadata.",
+                )
+            elif link.upload_time >= self._uploaded_prior_to:
+                return (
+                    LinkType.upload_too_late,
+                    f"Upload time {link.upload_time} not "
+                    f"prior to {self._uploaded_prior_to}",
+                )
+
+        # This should be up by the self.ok_binary check, but see issue 2700.
+        if "source" not in self._formats and ext != WHEEL_EXTENSION:
+            reason = f"No sources permitted for {self.project_name}"
+            return (LinkType.format_unsupported, reason)
+
+        if not version:
+            version = _extract_version_from_fragment(
+                egg_info,
+                self._canonical_name,
+            )
+        if not version:
+            reason = f"Missing project version for {self.project_name}"
+            return (LinkType.format_invalid, reason)
+
+        match = self._py_version_re.search(version)
+        if match:
+            version = version[: match.start()]
+            py_version = match.group(1)
+            if py_version != self._target_python.py_version:
+                return (
+                    LinkType.platform_mismatch,
+                    "Python version is incorrect",
+                )
+
+        supports_python = _check_link_requires_python(
+            link,
+            version_info=self._target_python.py_version_info,
+            ignore_requires_python=self._ignore_requires_python,
+        )
+        if not supports_python:
+            requires_python = link.requires_python
+            if requires_python:
+
+                def get_version_sort_key(v: str) -> tuple[int, ...]:
+                    return tuple(int(s) for s in v.split(".") if s.isdigit())
+
+                requires_python = ",".join(
+                    sorted(
+                        (str(s) for s in specifiers.SpecifierSet(requires_python)),
+                        key=get_version_sort_key,
+                    )
+                )
+            reason = f"{version} Requires-Python {requires_python}"
+            return (LinkType.requires_python_mismatch, reason)
+
+        logger.debug("Found link %s, version: %s", link, version)
+
+        return (LinkType.candidate, version)
+
+
+def filter_unallowed_hashes(
+    candidates: list[InstallationCandidate],
+    hashes: Hashes | None,
+    project_name: str,
+) -> list[InstallationCandidate]:
+    """
+    Filter out candidates whose hashes aren't allowed, and return a new
+    list of candidates.
+
+    If at least one candidate has an allowed hash, then all candidates with
+    either an allowed hash or no hash specified are returned.  Otherwise,
+    the given candidates are returned.
+
+    Including the candidates with no hash specified when there is a match
+    allows a warning to be logged if there is a more preferred candidate
+    with no hash specified.  Returning all candidates in the case of no
+    matches lets pip report the hash of the candidate that would otherwise
+    have been installed (e.g. permitting the user to more easily update
+    their requirements file with the desired hash).
+    """
+    if not hashes:
+        logger.debug(
+            "Given no hashes to check %s links for project %r: "
+            "discarding no candidates",
+            len(candidates),
+            project_name,
+        )
+        # Make sure we're not returning back the given value.
+        return list(candidates)
+
+    matches_or_no_digest = []
+    # Collect the non-matches for logging purposes.
+    non_matches = []
+    match_count = 0
+    for candidate in candidates:
+        link = candidate.link
+        if not link.has_hash:
+            pass
+        elif link.is_hash_allowed(hashes=hashes):
+            match_count += 1
+        else:
+            non_matches.append(candidate)
+            continue
+
+        matches_or_no_digest.append(candidate)
+
+    if match_count:
+        filtered = matches_or_no_digest
+    else:
+        # Make sure we're not returning back the given value.
+        filtered = list(candidates)
+
+    if len(filtered) == len(candidates):
+        discard_message = "discarding no candidates"
+    else:
+        discard_message = "discarding {} non-matches:\n  {}".format(
+            len(non_matches),
+            "\n  ".join(str(candidate.link) for candidate in non_matches),
+        )
+
+    logger.debug(
+        "Checked %s links for project %r against %s hashes "
+        "(%s matches, %s no digest): %s",
+        len(candidates),
+        project_name,
+        hashes.digest_count,
+        match_count,
+        len(matches_or_no_digest) - match_count,
+        discard_message,
+    )
+
+    return filtered
+
+
+@dataclass
+class CandidatePreferences:
+    """
+    Encapsulates some of the preferences for filtering and sorting
+    InstallationCandidate objects.
+    """
+
+    prefer_binary: bool = False
+    release_control: ReleaseControl | None = None
+
+
+@dataclass(frozen=True)
+class BestCandidateResult:
+    """A collection of candidates, returned by `PackageFinder.find_best_candidate`.
+
+    This class is only intended to be instantiated by CandidateEvaluator's
+    `compute_best_candidate()` method.
+
+    :param all_candidates: A sequence of all available candidates found.
+    :param applicable_candidates: The applicable candidates.
+    :param best_candidate: The most preferred candidate found, or None
+        if no applicable candidates were found.
+    """
+
+    all_candidates: list[InstallationCandidate]
+    applicable_candidates: list[InstallationCandidate]
+    best_candidate: InstallationCandidate | None
+
+    def __post_init__(self) -> None:
+        assert set(self.applicable_candidates) <= set(self.all_candidates)
+
+        if self.best_candidate is None:
+            assert not self.applicable_candidates
+        else:
+            assert self.best_candidate in self.applicable_candidates
+
+
+class CandidateEvaluator:
+    """
+    Responsible for filtering and sorting candidates for installation based
+    on what tags are valid.
+    """
+
+    @classmethod
+    def create(
+        cls,
+        project_name: str,
+        target_python: TargetPython | None = None,
+        prefer_binary: bool = False,
+        release_control: ReleaseControl | None = None,
+        specifier: specifiers.BaseSpecifier | None = None,
+        hashes: Hashes | None = None,
+    ) -> CandidateEvaluator:
+        """Create a CandidateEvaluator object.
+
+        :param target_python: The target Python interpreter to use when
+            checking compatibility. If None (the default), a TargetPython
+            object will be constructed from the running Python.
+        :param specifier: An optional object implementing `filter`
+            (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable
+            versions.
+        :param hashes: An optional collection of allowed hashes.
+        """
+        if target_python is None:
+            target_python = TargetPython()
+        if specifier is None:
+            specifier = specifiers.SpecifierSet()
+
+        supported_tags = target_python.get_sorted_tags()
+
+        return cls(
+            project_name=project_name,
+            supported_tags=supported_tags,
+            specifier=specifier,
+            prefer_binary=prefer_binary,
+            release_control=release_control,
+            hashes=hashes,
+        )
+
+    def __init__(
+        self,
+        project_name: str,
+        supported_tags: list[Tag],
+        specifier: specifiers.BaseSpecifier,
+        prefer_binary: bool = False,
+        release_control: ReleaseControl | None = None,
+        hashes: Hashes | None = None,
+    ) -> None:
+        """
+        :param supported_tags: The PEP 425 tags supported by the target
+            Python in order of preference (most preferred first).
+        """
+        self._release_control = release_control
+        self._hashes = hashes
+        self._prefer_binary = prefer_binary
+        self._project_name = project_name
+        self._specifier = specifier
+        self._supported_tags = supported_tags
+        # Since the index of the tag in the _supported_tags list is used
+        # as a priority, precompute a map from tag to index/priority to be
+        # used in wheel.find_most_preferred_tag.
+        self._wheel_tag_preferences = {
+            tag: idx for idx, tag in enumerate(supported_tags)
+        }
+
+    def get_applicable_candidates(
+        self,
+        candidates: list[InstallationCandidate],
+    ) -> list[InstallationCandidate]:
+        """
+        Return the applicable candidates from a list of candidates.
+        """
+        # Using None infers from the specifier instead.
+        if self._release_control is not None:
+            allow_prereleases = self._release_control.allows_prereleases(
+                canonicalize_name(self._project_name)
+            )
+        else:
+            allow_prereleases = None
+        specifier = self._specifier
+        # When using the pkg_resources backend we turn the version object into
+        # a str here because otherwise when we're debundled but setuptools isn't,
+        # Python will see packaging.version.Version and
+        # pkg_resources._vendor.packaging.version.Version as different
+        # types. This way we'll use a str as a common data interchange
+        # format. If we stop using the pkg_resources provided specifier
+        # and start using our own, we can drop the cast to str().
+        applicable_candidates = specifier.filter(
+            candidates,
+            prereleases=allow_prereleases,
+            key=lambda c: (
+                str(c.version)
+                if select_backend().NAME == "pkg_resources"
+                else c.version
+            ),
+        )
+        filtered_applicable_candidates = filter_unallowed_hashes(
+            candidates=list(applicable_candidates),
+            hashes=self._hashes,
+            project_name=self._project_name,
+        )
+
+        return sorted(filtered_applicable_candidates, key=self._sort_key)
+
+    def _sort_key(self, candidate: InstallationCandidate) -> CandidateSortingKey:
+        """
+        Function to pass as the `key` argument to a call to sorted() to sort
+        InstallationCandidates by preference.
+
+        Returns a tuple such that tuples sorting as greater using Python's
+        default comparison operator are more preferred.
+
+        The preference is as follows:
+
+        First and foremost, candidates with allowed (matching) hashes are
+        always preferred over candidates without matching hashes. This is
+        because e.g. if the only candidate with an allowed hash is yanked,
+        we still want to use that candidate.
+
+        Second, excepting hash considerations, candidates that have been
+        yanked (in the sense of PEP 592) are always less preferred than
+        candidates that haven't been yanked. Then:
+
+        If not finding wheels, they are sorted by version only.
+        If finding wheels, then the sort order is by version, then:
+          1. existing installs
+          2. wheels ordered via Wheel.support_index_min(self._supported_tags)
+          3. source archives
+        If prefer_binary was set, then all wheels are sorted above sources.
+
+        Note: it was considered to embed this logic into the Link
+              comparison operators, but then different sdist links
+              with the same version, would have to be considered equal
+        """
+        valid_tags = self._supported_tags
+        support_num = len(valid_tags)
+        build_tag: BuildTag = ()
+        binary_preference = 0
+        link = candidate.link
+        if link.is_wheel:
+            # can raise InvalidWheelFilename
+            wheel = Wheel(link.filename)
+            try:
+                pri = -(
+                    wheel.find_most_preferred_tag(
+                        valid_tags, self._wheel_tag_preferences
+                    )
+                )
+            except ValueError:
+                raise UnsupportedWheel(
+                    f"{wheel.filename} is not a supported wheel for this platform. It "
+                    "can't be sorted."
+                )
+            if self._prefer_binary:
+                binary_preference = 1
+            build_tag = wheel.build_tag
+        else:  # sdist
+            pri = -(support_num)
+        has_allowed_hash = int(link.is_hash_allowed(self._hashes))
+        yank_value = -1 * int(link.is_yanked)  # -1 for yanked.
+        return (
+            has_allowed_hash,
+            yank_value,
+            binary_preference,
+            candidate.version,
+            pri,
+            build_tag,
+        )
+
+    def sort_best_candidate(
+        self,
+        candidates: list[InstallationCandidate],
+    ) -> InstallationCandidate | None:
+        """
+        Return the best candidate per the instance's sort order, or None if
+        no candidate is acceptable.
+        """
+        if not candidates:
+            return None
+        best_candidate = max(candidates, key=self._sort_key)
+        return best_candidate
+
+    def compute_best_candidate(
+        self,
+        candidates: list[InstallationCandidate],
+    ) -> BestCandidateResult:
+        """
+        Compute and return a `BestCandidateResult` instance.
+        """
+        applicable_candidates = self.get_applicable_candidates(candidates)
+
+        best_candidate = self.sort_best_candidate(applicable_candidates)
+
+        return BestCandidateResult(
+            candidates,
+            applicable_candidates=applicable_candidates,
+            best_candidate=best_candidate,
+        )
+
+
+class PackageFinder:
+    """This finds packages.
+
+    This is meant to match easy_install's technique for looking for
+    packages, by reading pages and looking for appropriate links.
+    """
+
+    def __init__(
+        self,
+        link_collector: LinkCollector,
+        target_python: TargetPython,
+        allow_yanked: bool,
+        format_control: FormatControl | None = None,
+        candidate_prefs: CandidatePreferences | None = None,
+        ignore_requires_python: bool = False,
+        uploaded_prior_to: datetime.datetime | None = None,
+    ) -> None:
+        """
+        This constructor is primarily meant to be used by the create() class
+        method and from tests.
+
+        :param format_control: A FormatControl object, used to control
+            the selection of source packages / binary packages when consulting
+            the index and links.
+        :param candidate_prefs: Options to use when creating a
+            CandidateEvaluator object.
+        """
+        if candidate_prefs is None:
+            candidate_prefs = CandidatePreferences()
+
+        format_control = format_control or FormatControl(set(), set())
+
+        self._allow_yanked = allow_yanked
+        self._candidate_prefs = candidate_prefs
+        self._ignore_requires_python = ignore_requires_python
+        self._link_collector = link_collector
+        self._target_python = target_python
+        self._uploaded_prior_to = uploaded_prior_to
+
+        self.format_control = format_control
+
+        # Collects the detail strings for links skipped due to Requires-Python
+        # incompatibility.  Used by requires_python_skipped_reasons() to build
+        # the error message when resolution fails.
+        self._requires_python_skipped: set[str] = set()
+
+        # Cache of the result of finding candidates
+        self._all_candidates: dict[str, list[InstallationCandidate]] = {}
+        self._best_candidates: dict[
+            tuple[str, specifiers.BaseSpecifier | None, Hashes | None],
+            BestCandidateResult,
+        ] = {}
+
+        # projects for which a link is locked from a pylock
+        self._locked_links: dict[NormalizedName, Link] = {}
+
+    # Don't include an allow_yanked default value to make sure each call
+    # site considers whether yanked releases are allowed. This also causes
+    # that decision to be made explicit in the calling code, which helps
+    # people when reading the code.
+    @classmethod
+    def create(
+        cls,
+        link_collector: LinkCollector,
+        selection_prefs: SelectionPreferences,
+        target_python: TargetPython | None = None,
+        uploaded_prior_to: datetime.datetime | None = None,
+    ) -> PackageFinder:
+        """Create a PackageFinder.
+
+        :param selection_prefs: The candidate selection preferences, as a
+            SelectionPreferences object.
+        :param target_python: The target Python interpreter to use when
+            checking compatibility. If None (the default), a TargetPython
+            object will be constructed from the running Python.
+        :param uploaded_prior_to: If set, only find links uploaded prior
+            to the given datetime.
+        """
+        if target_python is None:
+            target_python = TargetPython()
+
+        candidate_prefs = CandidatePreferences(
+            prefer_binary=selection_prefs.prefer_binary,
+            release_control=selection_prefs.release_control,
+        )
+
+        return cls(
+            candidate_prefs=candidate_prefs,
+            link_collector=link_collector,
+            target_python=target_python,
+            allow_yanked=selection_prefs.allow_yanked,
+            format_control=selection_prefs.format_control,
+            ignore_requires_python=selection_prefs.ignore_requires_python,
+            uploaded_prior_to=uploaded_prior_to,
+        )
+
+    @property
+    def target_python(self) -> TargetPython:
+        return self._target_python
+
+    @property
+    def search_scope(self) -> SearchScope:
+        return self._link_collector.search_scope
+
+    @search_scope.setter
+    def search_scope(self, search_scope: SearchScope) -> None:
+        self._link_collector.search_scope = search_scope
+
+    @property
+    def find_links(self) -> list[str]:
+        return self._link_collector.find_links
+
+    @property
+    def index_urls(self) -> list[str]:
+        return self.search_scope.index_urls
+
+    @property
+    def refresh_package(self) -> set[str]:
+        return self._link_collector.session.refresh_package
+
+    @property
+    def proxy(self) -> str | None:
+        return self._link_collector.session.pip_proxy
+
+    @property
+    def no_proxy_env(self) -> bool:
+        return self._link_collector.session.pip_no_proxy_env
+
+    @property
+    def trusted_hosts(self) -> Iterable[str]:
+        for host_port in self._link_collector.session.pip_trusted_origins:
+            yield build_netloc(*host_port)
+
+    @property
+    def custom_cert(self) -> str | None:
+        # session.verify is either a boolean (use default bundle/no SSL
+        # verification) or a string path to a custom CA bundle to use. We only
+        # care about the latter.
+        verify = self._link_collector.session.verify
+        return verify if isinstance(verify, str) else None
+
+    @property
+    def client_cert(self) -> str | None:
+        cert = self._link_collector.session.cert
+        assert not isinstance(cert, tuple), "pip only supports PEM client certs"
+        return cert
+
+    @property
+    def release_control(self) -> ReleaseControl | None:
+        return self._candidate_prefs.release_control
+
+    def set_release_control(self, release_control: ReleaseControl) -> None:
+        self._candidate_prefs.release_control = release_control
+
+    @property
+    def prefer_binary(self) -> bool:
+        return self._candidate_prefs.prefer_binary
+
+    def set_prefer_binary(self) -> None:
+        self._candidate_prefs.prefer_binary = True
+
+    @property
+    def uploaded_prior_to(self) -> datetime.datetime | None:
+        return self._uploaded_prior_to
+
+    def requires_python_skipped_reasons(self) -> list[str]:
+        return sorted(self._requires_python_skipped)
+
+    def make_link_evaluator(self, project_name: str) -> LinkEvaluator:
+        canonical_name = canonicalize_name(project_name)
+        formats = self.format_control.get_allowed_formats(canonical_name)
+
+        return LinkEvaluator(
+            project_name=project_name,
+            canonical_name=canonical_name,
+            formats=formats,
+            target_python=self._target_python,
+            allow_yanked=self._allow_yanked,
+            ignore_requires_python=self._ignore_requires_python,
+            uploaded_prior_to=self._uploaded_prior_to,
+        )
+
+    def _sort_links(self, links: Iterable[Link]) -> list[Link]:
+        """
+        Returns elements of links in order, non-egg links first, egg links
+        second, while eliminating duplicates
+        """
+        eggs, no_eggs = [], []
+        seen: set[Link] = set()
+        for link in links:
+            if link not in seen:
+                seen.add(link)
+                if link.egg_fragment:
+                    eggs.append(link)
+                else:
+                    no_eggs.append(link)
+        return no_eggs + eggs
+
+    def _log_skipped_link(self, link: Link, result: LinkType, detail: str) -> None:
+        # Put the link at the end so the reason is more visible and because
+        # the link string is usually very long.
+        logger.debug("Skipping link: %s: %s", detail, link)
+        if result == LinkType.requires_python_mismatch:
+            self._requires_python_skipped.add(detail)
+
+    def get_install_candidate(
+        self, link_evaluator: LinkEvaluator, link: Link
+    ) -> InstallationCandidate | None:
+        """
+        If the link is a candidate for install, convert it to an
+        InstallationCandidate and return it. Otherwise, return None.
+        """
+        result, detail = link_evaluator.evaluate_link(link)
+        if result == LinkType.upload_time_missing:
+            # Fail immediately if the index doesn't provide upload-time
+            # when --uploaded-prior-to is specified
+            raise InstallationError(detail)
+        if result != LinkType.candidate:
+            self._log_skipped_link(link, result, detail)
+            return None
+
+        try:
+            return InstallationCandidate(
+                name=link_evaluator.project_name,
+                link=link,
+                version=detail,
+            )
+        except InvalidVersion:
+            return None
+
+    def evaluate_links(
+        self, link_evaluator: LinkEvaluator, links: Iterable[Link]
+    ) -> list[InstallationCandidate]:
+        """
+        Convert links that are candidates to InstallationCandidate objects.
+        """
+        candidates = []
+        for link in self._sort_links(links):
+            candidate = self.get_install_candidate(link_evaluator, link)
+            if candidate is not None:
+                candidates.append(candidate)
+
+        return candidates
+
+    def process_project_url(
+        self, project_url: Link, link_evaluator: LinkEvaluator
+    ) -> list[InstallationCandidate]:
+        logger.debug(
+            "Fetching project page and analyzing links: %s",
+            project_url,
+        )
+        index_response = self._link_collector.fetch_response(
+            project_url, package_name=link_evaluator.project_name
+        )
+        if index_response is None:
+            return []
+
+        page_links = list(parse_links(index_response))
+
+        with indent_log():
+            package_links = self.evaluate_links(
+                link_evaluator,
+                links=page_links,
+            )
+
+        return package_links
+
+    def find_all_candidates(self, project_name: str) -> list[InstallationCandidate]:
+        """Find all available InstallationCandidate for project_name
+
+        This checks index_urls and find_links, unless a locked link is known
+        for that project.
+        All versions found are returned as an InstallationCandidate list.
+
+        See LinkEvaluator.evaluate_link() for details on which files
+        are accepted.
+        """
+        if project_name in self._all_candidates:
+            return self._all_candidates[project_name]
+
+        link_evaluator = self.make_link_evaluator(project_name)
+
+        if locked_link := self._locked_links.get(canonicalize_name(project_name)):
+            # If a locked link is known for that project, do not check
+            # index_urls nor find_links. We don't use get_install_candidate here,
+            # because if a locked link is unsupported (due to format control,
+            # release control or otherwise), we want to error out immediately
+            # instead of ignoring it.
+            result, detail = link_evaluator.evaluate_link(locked_link)
+            if result != LinkType.candidate:
+                raise InstallationError(
+                    f"Could not install locked package {project_name!r} "
+                    f"from {locked_link.comes_from!r}: {detail}"
+                )
+            self._all_candidates[project_name] = [
+                InstallationCandidate(project_name, detail, locked_link, locked=True)
+            ]
+            return self._all_candidates[project_name]
+
+        collected_sources = self._link_collector.collect_sources(
+            project_name=project_name,
+            candidates_from_page=functools.partial(
+                self.process_project_url,
+                link_evaluator=link_evaluator,
+            ),
+        )
+
+        page_candidates_it = itertools.chain.from_iterable(
+            source.page_candidates()
+            for sources in collected_sources
+            for source in sources
+            if source is not None
+        )
+        page_candidates = list(page_candidates_it)
+
+        file_links_it = itertools.chain.from_iterable(
+            source.file_links()
+            for sources in collected_sources
+            for source in sources
+            if source is not None
+        )
+        file_candidates = self.evaluate_links(
+            link_evaluator,
+            sorted(file_links_it, reverse=True),
+        )
+
+        if logger.isEnabledFor(logging.DEBUG) and file_candidates:
+            paths = []
+            for candidate in file_candidates:
+                assert candidate.link.url  # we need to have a URL
+                try:
+                    paths.append(candidate.link.file_path)
+                except Exception:
+                    paths.append(candidate.link.url)  # it's not a local file
+
+            logger.debug("Local files found: %s", ", ".join(paths))
+
+        # This is an intentional priority ordering
+        self._all_candidates[project_name] = file_candidates + page_candidates
+
+        return self._all_candidates[project_name]
+
+    def make_candidate_evaluator(
+        self,
+        project_name: str,
+        specifier: specifiers.BaseSpecifier | None = None,
+        hashes: Hashes | None = None,
+    ) -> CandidateEvaluator:
+        """Create a CandidateEvaluator object to use."""
+        candidate_prefs = self._candidate_prefs
+        return CandidateEvaluator.create(
+            project_name=project_name,
+            target_python=self._target_python,
+            prefer_binary=candidate_prefs.prefer_binary,
+            release_control=candidate_prefs.release_control,
+            specifier=specifier,
+            hashes=hashes,
+        )
+
+    def find_best_candidate(
+        self,
+        project_name: str,
+        specifier: specifiers.BaseSpecifier | None = None,
+        hashes: Hashes | None = None,
+    ) -> BestCandidateResult:
+        """Find matches for the given project and specifier.
+
+        :param specifier: An optional object implementing `filter`
+            (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable
+            versions.
+
+        :return: A `BestCandidateResult` instance.
+        """
+        if (project_name, specifier, hashes) in self._best_candidates:
+            return self._best_candidates[project_name, specifier, hashes]
+
+        candidates = self.find_all_candidates(project_name)
+        candidate_evaluator = self.make_candidate_evaluator(
+            project_name=project_name,
+            specifier=specifier,
+            hashes=hashes,
+        )
+        self._best_candidates[project_name, specifier, hashes] = (
+            candidate_evaluator.compute_best_candidate(candidates)
+        )
+
+        return self._best_candidates[project_name, specifier, hashes]
+
+    def find_requirement(
+        self, req: InstallRequirement, upgrade: bool
+    ) -> InstallationCandidate | None:
+        """Try to find a Link matching req
+
+        Expects req, an InstallRequirement and upgrade, a boolean
+        Returns a InstallationCandidate if found,
+        Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise
+        """
+        name = req.name
+        assert name is not None, "find_requirement() called with no name"
+
+        hashes = req.hashes(trust_internet=False)
+        best_candidate_result = self.find_best_candidate(
+            name,
+            specifier=req.specifier,
+            hashes=hashes,
+        )
+        best_candidate = best_candidate_result.best_candidate
+
+        installed_version: _BaseVersion | None = None
+        if req.satisfied_by is not None:
+            installed_version = req.satisfied_by.version
+
+        def _format_versions(cand_iter: Iterable[InstallationCandidate]) -> str:
+            # This repeated parse_version and str() conversion is needed to
+            # handle different vendoring sources from pip and pkg_resources.
+            # If we stop using the pkg_resources provided specifier and start
+            # using our own, we can drop the cast to str().
+            return (
+                ", ".join(
+                    sorted(
+                        {str(c.version) for c in cand_iter},
+                        key=parse_version,
+                    )
+                )
+                or "none"
+            )
+
+        if installed_version is None and best_candidate is None:
+            # Check if only final releases are allowed for this package
+            version_type = "version"
+            if self.release_control is not None:
+                allows_pre = self.release_control.allows_prereleases(
+                    canonicalize_name(name)
+                )
+                if allows_pre is False:
+                    version_type = "final version"
+
+            logger.critical(
+                "Could not find a %s that satisfies the requirement %s "
+                "(from versions: %s)",
+                version_type,
+                req,
+                _format_versions(best_candidate_result.all_candidates),
+            )
+
+            raise DistributionNotFound(f"No matching distribution found for {req}")
+
+        def _should_install_candidate(
+            candidate: InstallationCandidate | None,
+        ) -> TypeGuard[InstallationCandidate]:
+            if installed_version is None:
+                return True
+            if best_candidate is None:
+                return False
+            return best_candidate.version > installed_version
+
+        if not upgrade and installed_version is not None:
+            if _should_install_candidate(best_candidate):
+                logger.debug(
+                    "Existing installed version (%s) satisfies requirement "
+                    "(most up-to-date version is %s)",
+                    installed_version,
+                    best_candidate.version,
+                )
+            else:
+                logger.debug(
+                    "Existing installed version (%s) is most up-to-date and "
+                    "satisfies requirement",
+                    installed_version,
+                )
+            return None
+
+        if _should_install_candidate(best_candidate):
+            logger.debug(
+                "Using version %s (newest of versions: %s)",
+                best_candidate.version,
+                _format_versions(best_candidate_result.applicable_candidates),
+            )
+            return best_candidate
+
+        # We have an existing version, and its the best version
+        logger.debug(
+            "Installed version (%s) is most up-to-date (past versions: %s)",
+            installed_version,
+            _format_versions(best_candidate_result.applicable_candidates),
+        )
+        raise BestVersionAlreadyInstalled
+
+    def add_locked_link(self, project_name: NormalizedName, locked_link: Link) -> None:
+        assert not self._all_candidates
+        if project_name in self._locked_links:
+            raise InstallationError(
+                f"Multiple locked links provided for {project_name}: "
+                f"{self._locked_links[project_name]} and {locked_link}"
+            )
+
+        self._locked_links[project_name] = locked_link
+
+
+def _find_name_version_sep(fragment: str, canonical_name: str) -> int:
+    """Find the separator's index based on the package's canonical name.
+
+    :param fragment: A + filename "fragment" (stem) or
+        egg fragment.
+    :param canonical_name: The package's canonical name.
+
+    This function is needed since the canonicalized name does not necessarily
+    have the same length as the egg info's name part. An example::
+
+    >>> fragment = 'foo__bar-1.0'
+    >>> canonical_name = 'foo-bar'
+    >>> _find_name_version_sep(fragment, canonical_name)
+    8
+    """
+    # Project name and version must be separated by one single dash. Find all
+    # occurrences of dashes; if the string in front of it matches the canonical
+    # name, this is the one separating the name and version parts.
+    for i, c in enumerate(fragment):
+        if c != "-":
+            continue
+        if canonicalize_name(fragment[:i]) == canonical_name:
+            return i
+    raise ValueError(f"{fragment} does not match {canonical_name}")
+
+
+def _extract_version_from_fragment(fragment: str, canonical_name: str) -> str | None:
+    """Parse the version string from a + filename
+    "fragment" (stem) or egg fragment.
+
+    :param fragment: The string to parse. E.g. foo-2.1
+    :param canonical_name: The canonicalized name of the package this
+        belongs to.
+    """
+    try:
+        version_start = _find_name_version_sep(fragment, canonical_name) + 1
+    except ValueError:
+        return None
+    version = fragment[version_start:]
+    if not version:
+        return None
+    return version
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/index/sources.py b/venv/lib/python3.12/site-packages/pip/_internal/index/sources.py
new file mode 100644
index 0000000..6e7c007
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/index/sources.py
@@ -0,0 +1,286 @@
+from __future__ import annotations
+
+import logging
+import mimetypes
+import os
+from collections import defaultdict
+from collections.abc import Callable, Iterable
+
+from pip._vendor.packaging.utils import (
+    InvalidSdistFilename,
+    InvalidWheelFilename,
+    canonicalize_name,
+    parse_sdist_filename,
+    parse_wheel_filename,
+)
+
+from pip._internal.models.candidate import InstallationCandidate
+from pip._internal.models.link import Link
+from pip._internal.utils.urls import path_to_url, url_to_path
+from pip._internal.vcs import is_url
+
+logger = logging.getLogger(__name__)
+
+FoundCandidates = Iterable[InstallationCandidate]
+FoundLinks = Iterable[Link]
+CandidatesFromPage = Callable[[Link], Iterable[InstallationCandidate]]
+PageValidator = Callable[[Link], bool]
+
+
+class LinkSource:
+    @property
+    def link(self) -> Link | None:
+        """Returns the underlying link, if there's one."""
+        raise NotImplementedError()
+
+    def page_candidates(self) -> FoundCandidates:
+        """Candidates found by parsing an archive listing HTML file."""
+        raise NotImplementedError()
+
+    def file_links(self) -> FoundLinks:
+        """Links found by specifying archives directly."""
+        raise NotImplementedError()
+
+
+def _is_html_file(file_url: str) -> bool:
+    return mimetypes.guess_type(file_url, strict=False)[0] == "text/html"
+
+
+class _FlatDirectoryToUrls:
+    """Scans directory and caches results"""
+
+    def __init__(self, path: str) -> None:
+        self._path = path
+        self._page_candidates: list[str] = []
+        self._project_name_to_urls: dict[str, list[str]] = defaultdict(list)
+        self._scanned_directory = False
+
+    def _scan_directory(self) -> None:
+        """Scans directory once and populates both page_candidates
+        and project_name_to_urls at the same time
+        """
+        for entry in os.scandir(self._path):
+            url = path_to_url(entry.path)
+            if _is_html_file(url):
+                self._page_candidates.append(url)
+                continue
+
+            # File must have a valid wheel or sdist name,
+            # otherwise not worth considering as a package
+            try:
+                project_filename = parse_wheel_filename(entry.name)[0]
+            except InvalidWheelFilename:
+                try:
+                    project_filename = parse_sdist_filename(entry.name)[0]
+                except InvalidSdistFilename:
+                    continue
+
+            self._project_name_to_urls[project_filename].append(url)
+        self._scanned_directory = True
+
+    @property
+    def page_candidates(self) -> list[str]:
+        if not self._scanned_directory:
+            self._scan_directory()
+
+        return self._page_candidates
+
+    @property
+    def project_name_to_urls(self) -> dict[str, list[str]]:
+        if not self._scanned_directory:
+            self._scan_directory()
+
+        return self._project_name_to_urls
+
+
+class _FlatDirectorySource(LinkSource):
+    """Link source specified by ``--find-links=``.
+
+    This looks the content of the directory, and returns:
+
+    * ``page_candidates``: Links listed on each HTML file in the directory.
+    * ``file_candidates``: Archives in the directory.
+    """
+
+    _paths_to_urls: dict[str, _FlatDirectoryToUrls] = {}
+
+    def __init__(
+        self,
+        candidates_from_page: CandidatesFromPage,
+        path: str,
+        project_name: str,
+    ) -> None:
+        self._candidates_from_page = candidates_from_page
+        self._project_name = canonicalize_name(project_name)
+
+        # Get existing instance of _FlatDirectoryToUrls if it exists
+        if path in self._paths_to_urls:
+            self._path_to_urls = self._paths_to_urls[path]
+        else:
+            self._path_to_urls = _FlatDirectoryToUrls(path=path)
+            self._paths_to_urls[path] = self._path_to_urls
+
+    @property
+    def link(self) -> Link | None:
+        return None
+
+    def page_candidates(self) -> FoundCandidates:
+        for url in self._path_to_urls.page_candidates:
+            yield from self._candidates_from_page(Link(url))
+
+    def file_links(self) -> FoundLinks:
+        for url in self._path_to_urls.project_name_to_urls[self._project_name]:
+            yield Link(url)
+
+
+class _LocalFileSource(LinkSource):
+    """``--find-links=`` or ``--[extra-]index-url=``.
+
+    If a URL is supplied, it must be a ``file:`` URL. If a path is supplied to
+    the option, it is converted to a URL first. This returns:
+
+    * ``page_candidates``: Links listed on an HTML file.
+    * ``file_candidates``: The non-HTML file.
+    """
+
+    def __init__(
+        self,
+        candidates_from_page: CandidatesFromPage,
+        link: Link,
+    ) -> None:
+        self._candidates_from_page = candidates_from_page
+        self._link = link
+
+    @property
+    def link(self) -> Link | None:
+        return self._link
+
+    def page_candidates(self) -> FoundCandidates:
+        if not _is_html_file(self._link.url):
+            return
+        yield from self._candidates_from_page(self._link)
+
+    def file_links(self) -> FoundLinks:
+        if _is_html_file(self._link.url):
+            return
+        yield self._link
+
+
+class _RemoteFileSource(LinkSource):
+    """``--find-links=`` or ``--[extra-]index-url=``.
+
+    This returns:
+
+    * ``page_candidates``: Links listed on an HTML file.
+    * ``file_candidates``: The non-HTML file.
+    """
+
+    def __init__(
+        self,
+        candidates_from_page: CandidatesFromPage,
+        page_validator: PageValidator,
+        link: Link,
+    ) -> None:
+        self._candidates_from_page = candidates_from_page
+        self._page_validator = page_validator
+        self._link = link
+
+    @property
+    def link(self) -> Link | None:
+        return self._link
+
+    def page_candidates(self) -> FoundCandidates:
+        if not self._page_validator(self._link):
+            return
+        yield from self._candidates_from_page(self._link)
+
+    def file_links(self) -> FoundLinks:
+        yield self._link
+
+
+class _IndexDirectorySource(LinkSource):
+    """``--[extra-]index-url=``.
+
+    This is treated like a remote URL; ``candidates_from_page`` contains logic
+    for this by appending ``index.html`` to the link.
+    """
+
+    def __init__(
+        self,
+        candidates_from_page: CandidatesFromPage,
+        link: Link,
+    ) -> None:
+        self._candidates_from_page = candidates_from_page
+        self._link = link
+
+    @property
+    def link(self) -> Link | None:
+        return self._link
+
+    def page_candidates(self) -> FoundCandidates:
+        yield from self._candidates_from_page(self._link)
+
+    def file_links(self) -> FoundLinks:
+        return ()
+
+
+def build_source(
+    location: str,
+    *,
+    candidates_from_page: CandidatesFromPage,
+    page_validator: PageValidator,
+    expand_dir: bool,
+    cache_link_parsing: bool,
+    project_name: str,
+) -> tuple[str | None, LinkSource | None]:
+    path: str | None = None
+    url: str | None = None
+    if os.path.exists(location):  # Is a local path.
+        url = path_to_url(location)
+        path = location
+    elif location.startswith("file:"):  # A file: URL.
+        url = location
+        path = url_to_path(location)
+    elif is_url(location):
+        url = location
+
+    if url is None:
+        msg = (
+            "Location '%s' is ignored: "
+            "it is either a non-existing path or lacks a specific scheme."
+        )
+        logger.warning(msg, location)
+        return (None, None)
+
+    if path is None:
+        source: LinkSource = _RemoteFileSource(
+            candidates_from_page=candidates_from_page,
+            page_validator=page_validator,
+            link=Link(url, cache_link_parsing=cache_link_parsing),
+        )
+        return (url, source)
+
+    if os.path.isdir(path):
+        if expand_dir:
+            source = _FlatDirectorySource(
+                candidates_from_page=candidates_from_page,
+                path=path,
+                project_name=project_name,
+            )
+        else:
+            source = _IndexDirectorySource(
+                candidates_from_page=candidates_from_page,
+                link=Link(url, cache_link_parsing=cache_link_parsing),
+            )
+        return (url, source)
+    elif os.path.isfile(path):
+        source = _LocalFileSource(
+            candidates_from_page=candidates_from_page,
+            link=Link(url, cache_link_parsing=cache_link_parsing),
+        )
+        return (url, source)
+    logger.warning(
+        "Location '%s' is ignored: it is neither a file nor a directory.",
+        location,
+    )
+    return (url, None)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/locations/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/locations/__init__.py
new file mode 100644
index 0000000..ec15d1d
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/locations/__init__.py
@@ -0,0 +1,438 @@
+from __future__ import annotations
+
+import functools
+import logging
+import os
+import pathlib
+import sys
+import sysconfig
+
+from pip._internal.models.scheme import SCHEME_KEYS, Scheme
+from pip._internal.utils.compat import WINDOWS
+from pip._internal.utils.deprecation import deprecated
+from pip._internal.utils.virtualenv import running_under_virtualenv
+
+from . import _sysconfig
+from .base import (
+    USER_CACHE_DIR,
+    get_major_minor_version,
+    get_src_prefix,
+    is_osx_framework,
+    site_packages,
+    user_site,
+)
+
+__all__ = [
+    "USER_CACHE_DIR",
+    "get_bin_prefix",
+    "get_bin_user",
+    "get_major_minor_version",
+    "get_platlib",
+    "get_purelib",
+    "get_scheme",
+    "get_src_prefix",
+    "site_packages",
+    "user_site",
+]
+
+
+logger = logging.getLogger(__name__)
+
+
+_PLATLIBDIR: str = getattr(sys, "platlibdir", "lib")
+
+
+def _should_use_sysconfig() -> bool:
+    """This function determines the value of _USE_SYSCONFIG.
+
+    By default, pip uses sysconfig.
+    But Python distributors can override this decision by setting:
+        sysconfig._PIP_USE_SYSCONFIG = True / False
+    Rationale in https://github.com/pypa/pip/issues/10647
+
+    This is a function for testability, but should be constant during any one
+    run.
+    """
+    return bool(getattr(sysconfig, "_PIP_USE_SYSCONFIG", True))
+
+
+_USE_SYSCONFIG = _should_use_sysconfig()
+
+if not _USE_SYSCONFIG:
+    # Import distutils lazily to avoid deprecation warnings,
+    # but import it soon enough that it is in memory and available during
+    # a pip reinstall.
+    from . import _distutils
+
+# Be noisy about incompatibilities if this platforms "should" be using
+# sysconfig, but is explicitly opting out and using distutils instead.
+if _USE_SYSCONFIG:
+    _MISMATCH_LEVEL = logging.DEBUG
+else:
+    _MISMATCH_LEVEL = logging.WARNING
+
+
+def _looks_like_bpo_44860() -> bool:
+    """The resolution to bpo-44860 will change this incorrect platlib.
+
+    See .
+    """
+    from distutils.command.install import INSTALL_SCHEMES
+
+    try:
+        unix_user_platlib = INSTALL_SCHEMES["unix_user"]["platlib"]
+    except KeyError:
+        return False
+    return unix_user_platlib == "$usersite"
+
+
+def _looks_like_red_hat_patched_platlib_purelib(scheme: dict[str, str]) -> bool:
+    platlib = scheme["platlib"]
+    if "/$platlibdir/" in platlib:
+        platlib = platlib.replace("/$platlibdir/", f"/{_PLATLIBDIR}/")
+    if "/lib64/" not in platlib:
+        return False
+    unpatched = platlib.replace("/lib64/", "/lib/")
+    return unpatched.replace("$platbase/", "$base/") == scheme["purelib"]
+
+
+@functools.cache
+def _looks_like_red_hat_lib() -> bool:
+    """Red Hat patches platlib in unix_prefix and unix_home, but not purelib.
+
+    This is the only way I can see to tell a Red Hat-patched Python.
+    """
+    from distutils.command.install import INSTALL_SCHEMES
+
+    return all(
+        k in INSTALL_SCHEMES
+        and _looks_like_red_hat_patched_platlib_purelib(INSTALL_SCHEMES[k])
+        for k in ("unix_prefix", "unix_home")
+    )
+
+
+@functools.cache
+def _looks_like_debian_scheme() -> bool:
+    """Debian adds two additional schemes."""
+    from distutils.command.install import INSTALL_SCHEMES
+
+    return "deb_system" in INSTALL_SCHEMES and "unix_local" in INSTALL_SCHEMES
+
+
+@functools.cache
+def _looks_like_red_hat_scheme() -> bool:
+    """Red Hat patches ``sys.prefix`` and ``sys.exec_prefix``.
+
+    Red Hat's ``00251-change-user-install-location.patch`` changes the install
+    command's ``prefix`` and ``exec_prefix`` to append ``"/local"``. This is
+    (fortunately?) done quite unconditionally, so we create a default command
+    object without any configuration to detect this.
+    """
+    from distutils.command.install import install
+    from distutils.dist import Distribution
+
+    cmd = install(Distribution())
+    cmd.finalize_options()
+    return (
+        cmd.exec_prefix == f"{os.path.normpath(sys.exec_prefix)}/local"
+        and cmd.prefix == f"{os.path.normpath(sys.prefix)}/local"
+    )
+
+
+@functools.cache
+def _looks_like_slackware_scheme() -> bool:
+    """Slackware patches sysconfig but fails to patch distutils and site.
+
+    Slackware changes sysconfig's user scheme to use ``"lib64"`` for the lib
+    path, but does not do the same to the site module.
+    """
+    if user_site is None:  # User-site not available.
+        return False
+    try:
+        paths = sysconfig.get_paths(scheme="posix_user", expand=False)
+    except KeyError:  # User-site not available.
+        return False
+    return "/lib64/" in paths["purelib"] and "/lib64/" not in user_site
+
+
+@functools.cache
+def _looks_like_msys2_mingw_scheme() -> bool:
+    """MSYS2 patches distutils and sysconfig to use a UNIX-like scheme.
+
+    However, MSYS2 incorrectly patches sysconfig ``nt`` scheme. The fix is
+    likely going to be included in their 3.10 release, so we ignore the warning.
+    See msys2/MINGW-packages#9319.
+
+    MSYS2 MINGW's patch uses lowercase ``"lib"`` instead of the usual uppercase,
+    and is missing the final ``"site-packages"``.
+    """
+    paths = sysconfig.get_paths("nt", expand=False)
+    return all(
+        "Lib" not in p and "lib" in p and not p.endswith("site-packages")
+        for p in (paths[key] for key in ("platlib", "purelib"))
+    )
+
+
+@functools.cache
+def _warn_mismatched(old: pathlib.Path, new: pathlib.Path, *, key: str) -> None:
+    issue_url = "https://github.com/pypa/pip/issues/10151"
+    message = (
+        "Value for %s does not match. Please report this to <%s>"
+        "\ndistutils: %s"
+        "\nsysconfig: %s"
+    )
+    logger.log(_MISMATCH_LEVEL, message, key, issue_url, old, new)
+
+
+def _warn_if_mismatch(old: pathlib.Path, new: pathlib.Path, *, key: str) -> bool:
+    if old == new:
+        return False
+    _warn_mismatched(old, new, key=key)
+    return True
+
+
+@functools.cache
+def _log_context(
+    *,
+    user: bool = False,
+    home: str | None = None,
+    root: str | None = None,
+    prefix: str | None = None,
+) -> None:
+    parts = [
+        "Additional context:",
+        "user = %r",
+        "home = %r",
+        "root = %r",
+        "prefix = %r",
+    ]
+
+    logger.log(_MISMATCH_LEVEL, "\n".join(parts), user, home, root, prefix)
+
+
+def get_scheme(
+    dist_name: str,
+    user: bool = False,
+    home: str | None = None,
+    root: str | None = None,
+    isolated: bool = False,
+    prefix: str | None = None,
+) -> Scheme:
+    new = _sysconfig.get_scheme(
+        dist_name,
+        user=user,
+        home=home,
+        root=root,
+        isolated=isolated,
+        prefix=prefix,
+    )
+    if _USE_SYSCONFIG:
+        return new
+
+    old = _distutils.get_scheme(
+        dist_name,
+        user=user,
+        home=home,
+        root=root,
+        isolated=isolated,
+        prefix=prefix,
+    )
+
+    warning_contexts = []
+    for k in SCHEME_KEYS:
+        old_v = pathlib.Path(getattr(old, k))
+        new_v = pathlib.Path(getattr(new, k))
+
+        if old_v == new_v:
+            continue
+
+        # distutils incorrectly put PyPy packages under ``site-packages/python``
+        # in the ``posix_home`` scheme, but PyPy devs said they expect the
+        # directory name to be ``pypy`` instead. So we treat this as a bug fix
+        # and not warn about it. See bpo-43307 and python/cpython#24628.
+        skip_pypy_special_case = (
+            sys.implementation.name == "pypy"
+            and home is not None
+            and k in ("platlib", "purelib")
+            and old_v.parent == new_v.parent
+            and old_v.name.startswith("python")
+            and new_v.name.startswith("pypy")
+        )
+        if skip_pypy_special_case:
+            continue
+
+        # sysconfig's ``osx_framework_user`` does not include ``pythonX.Y`` in
+        # the ``include`` value, but distutils's ``headers`` does. We'll let
+        # CPython decide whether this is a bug or feature. See bpo-43948.
+        skip_osx_framework_user_special_case = (
+            user
+            and is_osx_framework()
+            and k == "headers"
+            and old_v.parent.parent == new_v.parent
+            and old_v.parent.name.startswith("python")
+        )
+        if skip_osx_framework_user_special_case:
+            continue
+
+        # On Red Hat and derived Linux distributions, distutils is patched to
+        # use "lib64" instead of "lib" for platlib.
+        if k == "platlib" and _looks_like_red_hat_lib():
+            continue
+
+        # sysconfig's posix_user scheme sets platlib against
+        # sys.platlibdir, but distutils's unix_user incorrectly continues
+        # using the same $usersite for both platlib and purelib. This creates a
+        # mismatch when sys.platlibdir is not "lib".
+        skip_bpo_44860 = (
+            user
+            and k == "platlib"
+            and not WINDOWS
+            and _PLATLIBDIR != "lib"
+            and _looks_like_bpo_44860()
+        )
+        if skip_bpo_44860:
+            continue
+
+        # Slackware incorrectly patches posix_user to use lib64 instead of lib,
+        # but not usersite to match the location.
+        skip_slackware_user_scheme = (
+            user
+            and k in ("platlib", "purelib")
+            and not WINDOWS
+            and _looks_like_slackware_scheme()
+        )
+        if skip_slackware_user_scheme:
+            continue
+
+        # Both Debian and Red Hat patch Python to place the system site under
+        # /usr/local instead of /usr. Debian also places lib in dist-packages
+        # instead of site-packages, but the /usr/local check should cover it.
+        skip_linux_system_special_case = (
+            not (user or home or prefix or running_under_virtualenv())
+            and old_v.parts[1:3] == ("usr", "local")
+            and len(new_v.parts) > 1
+            and new_v.parts[1] == "usr"
+            and (len(new_v.parts) < 3 or new_v.parts[2] != "local")
+            and (_looks_like_red_hat_scheme() or _looks_like_debian_scheme())
+        )
+        if skip_linux_system_special_case:
+            continue
+
+        # MSYS2 MINGW's sysconfig patch does not include the "site-packages"
+        # part of the path. This is incorrect and will be fixed in MSYS.
+        skip_msys2_mingw_bug = (
+            WINDOWS and k in ("platlib", "purelib") and _looks_like_msys2_mingw_scheme()
+        )
+        if skip_msys2_mingw_bug:
+            continue
+
+        # CPython's POSIX install script invokes pip (via ensurepip) against the
+        # interpreter located in the source tree, not the install site. This
+        # triggers special logic in sysconfig that's not present in distutils.
+        # https://github.com/python/cpython/blob/8c21941ddaf/Lib/sysconfig.py#L178-L194
+        skip_cpython_build = (
+            sysconfig.is_python_build(check_home=True)
+            and not WINDOWS
+            and k in ("headers", "include", "platinclude")
+        )
+        if skip_cpython_build:
+            continue
+
+        warning_contexts.append((old_v, new_v, f"scheme.{k}"))
+
+    if not warning_contexts:
+        return old
+
+    # Check if this path mismatch is caused by distutils config files. Those
+    # files will no longer work once we switch to sysconfig, so this raises a
+    # deprecation message for them.
+    default_old = _distutils.distutils_scheme(
+        dist_name,
+        user,
+        home,
+        root,
+        isolated,
+        prefix,
+        ignore_config_files=True,
+    )
+    if any(default_old[k] != getattr(old, k) for k in SCHEME_KEYS):
+        deprecated(
+            reason=(
+                "Configuring installation scheme with distutils config files "
+                "is deprecated and will no longer work in the near future. If you "
+                "are using a Homebrew or Linuxbrew Python, please see discussion "
+                "at https://github.com/Homebrew/homebrew-core/issues/76621"
+            ),
+            replacement=None,
+            gone_in=None,
+        )
+        return old
+
+    # Post warnings about this mismatch so user can report them back.
+    for old_v, new_v, key in warning_contexts:
+        _warn_mismatched(old_v, new_v, key=key)
+    _log_context(user=user, home=home, root=root, prefix=prefix)
+
+    return old
+
+
+def get_bin_prefix() -> str:
+    new = _sysconfig.get_bin_prefix()
+    if _USE_SYSCONFIG:
+        return new
+
+    old = _distutils.get_bin_prefix()
+    if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="bin_prefix"):
+        _log_context()
+    return old
+
+
+def get_bin_user() -> str:
+    return _sysconfig.get_scheme("", user=True).scripts
+
+
+def _looks_like_deb_system_dist_packages(value: str) -> bool:
+    """Check if the value is Debian's APT-controlled dist-packages.
+
+    Debian's ``distutils.sysconfig.get_python_lib()`` implementation returns the
+    default package path controlled by APT, but does not patch ``sysconfig`` to
+    do the same. This is similar to the bug worked around in ``get_scheme()``,
+    but here the default is ``deb_system`` instead of ``unix_local``. Ultimately
+    we can't do anything about this Debian bug, and this detection allows us to
+    skip the warning when needed.
+    """
+    if not _looks_like_debian_scheme():
+        return False
+    if value == "/usr/lib/python3/dist-packages":
+        return True
+    return False
+
+
+def get_purelib() -> str:
+    """Return the default pure-Python lib location."""
+    new = _sysconfig.get_purelib()
+    if _USE_SYSCONFIG:
+        return new
+
+    old = _distutils.get_purelib()
+    if _looks_like_deb_system_dist_packages(old):
+        return old
+    if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="purelib"):
+        _log_context()
+    return old
+
+
+def get_platlib() -> str:
+    """Return the default platform-shared lib location."""
+    new = _sysconfig.get_platlib()
+    if _USE_SYSCONFIG:
+        return new
+
+    from . import _distutils
+
+    old = _distutils.get_platlib()
+    if _looks_like_deb_system_dist_packages(old):
+        return old
+    if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="platlib"):
+        _log_context()
+    return old
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..8256a93
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_distutils.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_distutils.cpython-312.pyc
new file mode 100644
index 0000000..402ae75
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_distutils.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_sysconfig.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_sysconfig.cpython-312.pyc
new file mode 100644
index 0000000..e15c73c
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_sysconfig.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/base.cpython-312.pyc
new file mode 100644
index 0000000..2fd4e8d
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/base.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/locations/_distutils.py b/venv/lib/python3.12/site-packages/pip/_internal/locations/_distutils.py
new file mode 100644
index 0000000..28c066b
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/locations/_distutils.py
@@ -0,0 +1,173 @@
+"""Locations where we look for configs, install stuff, etc"""
+
+# The following comment should be removed at some point in the future.
+# mypy: strict-optional=False
+
+# If pip's going to use distutils, it should not be using the copy that setuptools
+# might have injected into the environment. This is done by removing the injected
+# shim, if it's injected.
+#
+# See https://github.com/pypa/pip/issues/8761 for the original discussion and
+# rationale for why this is done within pip.
+from __future__ import annotations
+
+try:
+    __import__("_distutils_hack").remove_shim()
+except (ImportError, AttributeError):
+    pass
+
+import logging
+import os
+import sys
+from distutils.cmd import Command as DistutilsCommand
+from distutils.command.install import SCHEME_KEYS
+from distutils.command.install import install as distutils_install_command
+from distutils.sysconfig import get_python_lib
+
+from pip._internal.models.scheme import Scheme
+from pip._internal.utils.compat import WINDOWS
+from pip._internal.utils.virtualenv import running_under_virtualenv
+
+from .base import get_major_minor_version
+
+logger = logging.getLogger(__name__)
+
+
+def distutils_scheme(
+    dist_name: str,
+    user: bool = False,
+    home: str | None = None,
+    root: str | None = None,
+    isolated: bool = False,
+    prefix: str | None = None,
+    *,
+    ignore_config_files: bool = False,
+) -> dict[str, str]:
+    """
+    Return a distutils install scheme
+    """
+    from distutils.dist import Distribution
+
+    dist_args: dict[str, str | list[str]] = {"name": dist_name}
+    if isolated:
+        dist_args["script_args"] = ["--no-user-cfg"]
+
+    d = Distribution(dist_args)
+    if not ignore_config_files:
+        try:
+            d.parse_config_files()
+        except UnicodeDecodeError:
+            paths = d.find_config_files()
+            logger.warning(
+                "Ignore distutils configs in %s due to encoding errors.",
+                ", ".join(os.path.basename(p) for p in paths),
+            )
+    obj: DistutilsCommand | None = None
+    obj = d.get_command_obj("install", create=True)
+    assert obj is not None
+    i: distutils_install_command = obj
+    # NOTE: setting user or home has the side-effect of creating the home dir
+    # or user base for installations during finalize_options()
+    # ideally, we'd prefer a scheme class that has no side-effects.
+    assert not (user and prefix), f"user={user} prefix={prefix}"
+    assert not (home and prefix), f"home={home} prefix={prefix}"
+    i.user = user or i.user
+    if user or home:
+        i.prefix = ""
+    i.prefix = prefix or i.prefix
+    i.home = home or i.home
+    i.root = root or i.root
+    i.finalize_options()
+
+    scheme: dict[str, str] = {}
+    for key in SCHEME_KEYS:
+        scheme[key] = getattr(i, "install_" + key)
+
+    # install_lib specified in setup.cfg should install *everything*
+    # into there (i.e. it takes precedence over both purelib and
+    # platlib).  Note, i.install_lib is *always* set after
+    # finalize_options(); we only want to override here if the user
+    # has explicitly requested it hence going back to the config
+    if "install_lib" in d.get_option_dict("install"):
+        scheme.update({"purelib": i.install_lib, "platlib": i.install_lib})
+
+    if running_under_virtualenv():
+        if home:
+            prefix = home
+        elif user:
+            prefix = i.install_userbase
+        else:
+            prefix = i.prefix
+        scheme["headers"] = os.path.join(
+            prefix,
+            "include",
+            "site",
+            f"python{get_major_minor_version()}",
+            dist_name,
+        )
+
+        if root is not None:
+            path_no_drive = os.path.splitdrive(os.path.abspath(scheme["headers"]))[1]
+            scheme["headers"] = os.path.join(root, path_no_drive[1:])
+
+    return scheme
+
+
+def get_scheme(
+    dist_name: str,
+    user: bool = False,
+    home: str | None = None,
+    root: str | None = None,
+    isolated: bool = False,
+    prefix: str | None = None,
+) -> Scheme:
+    """
+    Get the "scheme" corresponding to the input parameters. The distutils
+    documentation provides the context for the available schemes:
+    https://docs.python.org/3/install/index.html#alternate-installation
+
+    :param dist_name: the name of the package to retrieve the scheme for, used
+        in the headers scheme path
+    :param user: indicates to use the "user" scheme
+    :param home: indicates to use the "home" scheme and provides the base
+        directory for the same
+    :param root: root under which other directories are re-based
+    :param isolated: equivalent to --no-user-cfg, i.e. do not consider
+        ~/.pydistutils.cfg (posix) or ~/pydistutils.cfg (non-posix) for
+        scheme paths
+    :param prefix: indicates to use the "prefix" scheme and provides the
+        base directory for the same
+    """
+    scheme = distutils_scheme(dist_name, user, home, root, isolated, prefix)
+    return Scheme(
+        platlib=scheme["platlib"],
+        purelib=scheme["purelib"],
+        headers=scheme["headers"],
+        scripts=scheme["scripts"],
+        data=scheme["data"],
+    )
+
+
+def get_bin_prefix() -> str:
+    # XXX: In old virtualenv versions, sys.prefix can contain '..' components,
+    # so we need to call normpath to eliminate them.
+    prefix = os.path.normpath(sys.prefix)
+    if WINDOWS:
+        bin_py = os.path.join(prefix, "Scripts")
+        # buildout uses 'bin' on Windows too?
+        if not os.path.exists(bin_py):
+            bin_py = os.path.join(prefix, "bin")
+        return bin_py
+    # Forcing to use /usr/local/bin for standard macOS framework installs
+    # Also log to ~/Library/Logs/ for use with the Console.app log viewer
+    if sys.platform[:6] == "darwin" and prefix[:16] == "/System/Library/":
+        return "/usr/local/bin"
+    return os.path.join(prefix, "bin")
+
+
+def get_purelib() -> str:
+    return get_python_lib(plat_specific=False)
+
+
+def get_platlib() -> str:
+    return get_python_lib(plat_specific=True)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/locations/_sysconfig.py b/venv/lib/python3.12/site-packages/pip/_internal/locations/_sysconfig.py
new file mode 100644
index 0000000..129dd11
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/locations/_sysconfig.py
@@ -0,0 +1,218 @@
+from __future__ import annotations
+
+import logging
+import os
+import sys
+import sysconfig
+from collections.abc import Callable
+
+from pip._internal.exceptions import InvalidSchemeCombination, UserInstallationInvalid
+from pip._internal.models.scheme import SCHEME_KEYS, Scheme
+from pip._internal.utils.virtualenv import running_under_virtualenv
+
+from .base import change_root, get_major_minor_version, is_osx_framework
+
+logger = logging.getLogger(__name__)
+
+
+# Notes on _infer_* functions.
+# Unfortunately ``get_default_scheme()`` didn't exist before 3.10, so there's no
+# way to ask things like "what is the '_prefix' scheme on this platform". These
+# functions try to answer that with some heuristics while accounting for ad-hoc
+# platforms not covered by CPython's default sysconfig implementation. If the
+# ad-hoc implementation does not fully implement sysconfig, we'll fall back to
+# a POSIX scheme.
+
+_AVAILABLE_SCHEMES = set(sysconfig.get_scheme_names())
+
+_PREFERRED_SCHEME_API: Callable[[str], str] | None = getattr(
+    sysconfig, "get_preferred_scheme", None
+)
+
+
+def _should_use_osx_framework_prefix() -> bool:
+    """Check for Apple's ``osx_framework_library`` scheme.
+
+    Python distributed by Apple's Command Line Tools has this special scheme
+    that's used when:
+
+    * This is a framework build.
+    * We are installing into the system prefix.
+
+    This does not account for ``pip install --prefix`` (also means we're not
+    installing to the system prefix), which should use ``posix_prefix``, but
+    logic here means ``_infer_prefix()`` outputs ``osx_framework_library``. But
+    since ``prefix`` is not available for ``sysconfig.get_default_scheme()``,
+    which is the stdlib replacement for ``_infer_prefix()``, presumably Apple
+    wouldn't be able to magically switch between ``osx_framework_library`` and
+    ``posix_prefix``. ``_infer_prefix()`` returning ``osx_framework_library``
+    means its behavior is consistent whether we use the stdlib implementation
+    or our own, and we deal with this special case in ``get_scheme()`` instead.
+    """
+    return (
+        "osx_framework_library" in _AVAILABLE_SCHEMES
+        and not running_under_virtualenv()
+        and is_osx_framework()
+    )
+
+
+def _infer_prefix() -> str:
+    """Try to find a prefix scheme for the current platform.
+
+    This tries:
+
+    * A special ``osx_framework_library`` for Python distributed by Apple's
+      Command Line Tools, when not running in a virtual environment.
+    * Implementation + OS, used by PyPy on Windows (``pypy_nt``).
+    * Implementation without OS, used by PyPy on POSIX (``pypy``).
+    * OS + "prefix", used by CPython on POSIX (``posix_prefix``).
+    * Just the OS name, used by CPython on Windows (``nt``).
+
+    If none of the above works, fall back to ``posix_prefix``.
+    """
+    if _PREFERRED_SCHEME_API:
+        return _PREFERRED_SCHEME_API("prefix")
+    if _should_use_osx_framework_prefix():
+        return "osx_framework_library"
+    implementation_suffixed = f"{sys.implementation.name}_{os.name}"
+    if implementation_suffixed in _AVAILABLE_SCHEMES:
+        return implementation_suffixed
+    if sys.implementation.name in _AVAILABLE_SCHEMES:
+        return sys.implementation.name
+    suffixed = f"{os.name}_prefix"
+    if suffixed in _AVAILABLE_SCHEMES:
+        return suffixed
+    if os.name in _AVAILABLE_SCHEMES:  # On Windows, prefx is just called "nt".
+        return os.name
+    return "posix_prefix"
+
+
+def _infer_user() -> str:
+    """Try to find a user scheme for the current platform."""
+    if _PREFERRED_SCHEME_API:
+        return _PREFERRED_SCHEME_API("user")
+    if is_osx_framework() and not running_under_virtualenv():
+        suffixed = "osx_framework_user"
+    else:
+        suffixed = f"{os.name}_user"
+    if suffixed in _AVAILABLE_SCHEMES:
+        return suffixed
+    if "posix_user" not in _AVAILABLE_SCHEMES:  # User scheme unavailable.
+        raise UserInstallationInvalid()
+    return "posix_user"
+
+
+def _infer_home() -> str:
+    """Try to find a home for the current platform."""
+    if _PREFERRED_SCHEME_API:
+        return _PREFERRED_SCHEME_API("home")
+    suffixed = f"{os.name}_home"
+    if suffixed in _AVAILABLE_SCHEMES:
+        return suffixed
+    return "posix_home"
+
+
+# Update these keys if the user sets a custom home.
+_HOME_KEYS = [
+    "installed_base",
+    "base",
+    "installed_platbase",
+    "platbase",
+    "prefix",
+    "exec_prefix",
+]
+if sysconfig.get_config_var("userbase") is not None:
+    _HOME_KEYS.append("userbase")
+
+
+def get_scheme(
+    dist_name: str,
+    user: bool = False,
+    home: str | None = None,
+    root: str | None = None,
+    isolated: bool = False,
+    prefix: str | None = None,
+) -> Scheme:
+    """
+    Get the "scheme" corresponding to the input parameters.
+
+    :param dist_name: the name of the package to retrieve the scheme for, used
+        in the headers scheme path
+    :param user: indicates to use the "user" scheme
+    :param home: indicates to use the "home" scheme
+    :param root: root under which other directories are re-based
+    :param isolated: ignored, but kept for distutils compatibility (where
+        this controls whether the user-site pydistutils.cfg is honored)
+    :param prefix: indicates to use the "prefix" scheme and provides the
+        base directory for the same
+    """
+    if user and prefix:
+        raise InvalidSchemeCombination("--user", "--prefix")
+    if home and prefix:
+        raise InvalidSchemeCombination("--home", "--prefix")
+
+    if home is not None:
+        scheme_name = _infer_home()
+    elif user:
+        scheme_name = _infer_user()
+    else:
+        scheme_name = _infer_prefix()
+
+    # Special case: When installing into a custom prefix, use posix_prefix
+    # instead of osx_framework_library. See _should_use_osx_framework_prefix()
+    # docstring for details.
+    if prefix is not None and scheme_name == "osx_framework_library":
+        scheme_name = "posix_prefix"
+
+    if home is not None:
+        variables = {k: home for k in _HOME_KEYS}
+    elif prefix is not None:
+        variables = {k: prefix for k in _HOME_KEYS}
+    else:
+        variables = {}
+
+    paths = sysconfig.get_paths(scheme=scheme_name, vars=variables)
+
+    # Logic here is very arbitrary, we're doing it for compatibility, don't ask.
+    # 1. Pip historically uses a special header path in virtual environments.
+    # 2. If the distribution name is not known, distutils uses 'UNKNOWN'. We
+    #    only do the same when not running in a virtual environment because
+    #    pip's historical header path logic (see point 1) did not do this.
+    if running_under_virtualenv():
+        if user:
+            base = variables.get("userbase", sys.prefix)
+        else:
+            base = variables.get("base", sys.prefix)
+        python_xy = f"python{get_major_minor_version()}"
+        paths["include"] = os.path.join(base, "include", "site", python_xy)
+    elif not dist_name:
+        dist_name = "UNKNOWN"
+
+    scheme = Scheme(
+        platlib=paths["platlib"],
+        purelib=paths["purelib"],
+        headers=os.path.join(paths["include"], dist_name),
+        scripts=paths["scripts"],
+        data=paths["data"],
+    )
+    if root is not None:
+        converted_keys = {}
+        for key in SCHEME_KEYS:
+            converted_keys[key] = change_root(root, getattr(scheme, key))
+        scheme = Scheme(**converted_keys)
+    return scheme
+
+
+def get_bin_prefix() -> str:
+    # Forcing to use /usr/local/bin for standard macOS framework installs.
+    if sys.platform[:6] == "darwin" and sys.prefix[:16] == "/System/Library/":
+        return "/usr/local/bin"
+    return sysconfig.get_paths()["scripts"]
+
+
+def get_purelib() -> str:
+    return sysconfig.get_paths()["purelib"]
+
+
+def get_platlib() -> str:
+    return sysconfig.get_paths()["platlib"]
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/locations/base.py b/venv/lib/python3.12/site-packages/pip/_internal/locations/base.py
new file mode 100644
index 0000000..f8d13cd
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/locations/base.py
@@ -0,0 +1,82 @@
+from __future__ import annotations
+
+import functools
+import os
+import site
+import sys
+import sysconfig
+
+from pip._internal.exceptions import InstallationError
+from pip._internal.utils import appdirs
+from pip._internal.utils.virtualenv import running_under_virtualenv
+
+# Application Directories
+USER_CACHE_DIR = appdirs.user_cache_dir("pip")
+
+# FIXME doesn't account for venv linked to global site-packages
+site_packages: str = sysconfig.get_path("purelib")
+
+
+def get_major_minor_version() -> str:
+    """
+    Return the major-minor version of the current Python as a string, e.g.
+    "3.7" or "3.10".
+    """
+    return "{}.{}".format(*sys.version_info)
+
+
+def change_root(new_root: str, pathname: str) -> str:
+    """Return 'pathname' with 'new_root' prepended.
+
+    If 'pathname' is relative, this is equivalent to os.path.join(new_root, pathname).
+    Otherwise, it requires making 'pathname' relative and then joining the
+    two, which is tricky on DOS/Windows and Mac OS.
+
+    This is borrowed from Python's standard library's distutils module.
+    """
+    if os.name == "posix":
+        if not os.path.isabs(pathname):
+            return os.path.join(new_root, pathname)
+        else:
+            return os.path.join(new_root, pathname[1:])
+
+    elif os.name == "nt":
+        drive, path = os.path.splitdrive(pathname)
+        if path[0] == "\\":
+            path = path[1:]
+        return os.path.join(new_root, path)
+
+    else:
+        raise InstallationError(
+            f"Unknown platform: {os.name}\n"
+            "Can not change root path prefix on unknown platform."
+        )
+
+
+def get_src_prefix() -> str:
+    if running_under_virtualenv():
+        src_prefix = os.path.join(sys.prefix, "src")
+    else:
+        # FIXME: keep src in cwd for now (it is not a temporary folder)
+        try:
+            src_prefix = os.path.join(os.getcwd(), "src")
+        except OSError:
+            # In case the current working directory has been renamed or deleted
+            sys.exit("The folder you are executing pip from can no longer be found.")
+
+    # under macOS + virtualenv sys.prefix is not properly resolved
+    # it is something like /path/to/python/bin/..
+    return os.path.abspath(src_prefix)
+
+
+try:
+    # Use getusersitepackages if this is present, as it ensures that the
+    # value is initialised properly.
+    user_site: str | None = site.getusersitepackages()
+except AttributeError:
+    user_site = site.USER_SITE
+
+
+@functools.cache
+def is_osx_framework() -> bool:
+    return bool(sysconfig.get_config_var("PYTHONFRAMEWORK"))
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/main.py b/venv/lib/python3.12/site-packages/pip/_internal/main.py
new file mode 100644
index 0000000..ec52c4e
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/main.py
@@ -0,0 +1,12 @@
+from __future__ import annotations
+
+
+def main(args: list[str] | None = None) -> int:
+    """This is preserved for old console scripts that may still be referencing
+    it.
+
+    For additional details, see https://github.com/pypa/pip/issues/7498.
+    """
+    from pip._internal.utils.entrypoints import _wrapper
+
+    return _wrapper(args)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/metadata/__init__.py
new file mode 100644
index 0000000..1c24efc
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/metadata/__init__.py
@@ -0,0 +1,169 @@
+from __future__ import annotations
+
+import contextlib
+import functools
+import os
+import sys
+from typing import TYPE_CHECKING, Literal, Protocol, cast
+
+from pip._internal.utils.deprecation import deprecated
+from pip._internal.utils.misc import strtobool
+
+from .base import BaseDistribution, BaseEnvironment, FilesystemWheel, MemoryWheel, Wheel
+
+if TYPE_CHECKING:
+    from pip._vendor.packaging.utils import NormalizedName
+
+__all__ = [
+    "BaseDistribution",
+    "BaseEnvironment",
+    "FilesystemWheel",
+    "MemoryWheel",
+    "Wheel",
+    "get_default_environment",
+    "get_environment",
+    "get_wheel_distribution",
+    "select_backend",
+]
+
+
+def _should_use_importlib_metadata() -> bool:
+    """Whether to use the ``importlib.metadata`` or ``pkg_resources`` backend.
+
+    By default, pip uses ``importlib.metadata`` on Python 3.11+, and
+    ``pkg_resources`` otherwise. Up to Python 3.13, This can be
+    overridden by a couple of ways:
+
+    * If environment variable ``_PIP_USE_IMPORTLIB_METADATA`` is set, it
+      dictates whether ``importlib.metadata`` is used, for Python <3.14.
+    * On Python 3.11, 3.12 and 3.13, Python distributors can patch
+      ``importlib.metadata`` to add a global constant
+      ``_PIP_USE_IMPORTLIB_METADATA = False``. This makes pip use
+      ``pkg_resources`` (unless the user set the aforementioned environment
+      variable to *True*).
+
+    On Python 3.14+, the ``pkg_resources`` backend cannot be used.
+    """
+    if sys.version_info >= (3, 14):
+        # On Python >=3.14 we only support importlib.metadata.
+        return True
+    with contextlib.suppress(KeyError, ValueError):
+        # On Python <3.14, if the environment variable is set, we obey what it says.
+        return bool(strtobool(os.environ["_PIP_USE_IMPORTLIB_METADATA"]))
+    if sys.version_info < (3, 11):
+        # On Python <3.11, we always use pkg_resources, unless the environment
+        # variable was set.
+        return False
+    # On Python 3.11, 3.12 and 3.13, we check if the global constant is set.
+    import importlib.metadata
+
+    return bool(getattr(importlib.metadata, "_PIP_USE_IMPORTLIB_METADATA", True))
+
+
+def _emit_pkg_resources_deprecation_if_needed() -> None:
+    if sys.version_info < (3, 11):
+        # All pip versions supporting Python<=3.11 will support pkg_resources,
+        # and pkg_resources is the default for these, so let's not bother users.
+        return
+
+    import importlib.metadata
+
+    if hasattr(importlib.metadata, "_PIP_USE_IMPORTLIB_METADATA"):
+        # The Python distributor has set the global constant, so we don't
+        # warn, since it is not a user decision.
+        return
+
+    # The user has decided to use pkg_resources, so we warn.
+    deprecated(
+        reason="Using the pkg_resources metadata backend is deprecated.",
+        replacement=(
+            "to use the default importlib.metadata backend, "
+            "by unsetting the _PIP_USE_IMPORTLIB_METADATA environment variable"
+        ),
+        gone_in="26.3",
+        issue=13317,
+    )
+
+
+class Backend(Protocol):
+    NAME: Literal["importlib", "pkg_resources"]
+    Distribution: type[BaseDistribution]
+    Environment: type[BaseEnvironment]
+
+
+@functools.cache
+def select_backend() -> Backend:
+    if _should_use_importlib_metadata():
+        from . import importlib
+
+        return cast(Backend, importlib)
+
+    _emit_pkg_resources_deprecation_if_needed()
+
+    from . import pkg_resources
+
+    return cast(Backend, pkg_resources)
+
+
+def get_default_environment() -> BaseEnvironment:
+    """Get the default representation for the current environment.
+
+    This returns an Environment instance from the chosen backend. The default
+    Environment instance should be built from ``sys.path`` and may use caching
+    to share instance state across calls.
+    """
+    return select_backend().Environment.default()
+
+
+def get_environment(paths: list[str] | None) -> BaseEnvironment:
+    """Get a representation of the environment specified by ``paths``.
+
+    This returns an Environment instance from the chosen backend based on the
+    given import paths. The backend must build a fresh instance representing
+    the state of installed distributions when this function is called.
+    """
+    return select_backend().Environment.from_paths(paths)
+
+
+def get_directory_distribution(directory: str) -> BaseDistribution:
+    """Get the distribution metadata representation in the specified directory.
+
+    This returns a Distribution instance from the chosen backend based on
+    the given on-disk ``.dist-info`` directory.
+    """
+    return select_backend().Distribution.from_directory(directory)
+
+
+def get_wheel_distribution(
+    wheel: Wheel, canonical_name: NormalizedName
+) -> BaseDistribution:
+    """Get the representation of the specified wheel's distribution metadata.
+
+    This returns a Distribution instance from the chosen backend based on
+    the given wheel's ``.dist-info`` directory.
+
+    :param canonical_name: Normalized project name of the given wheel.
+    """
+    return select_backend().Distribution.from_wheel(wheel, canonical_name)
+
+
+def get_metadata_distribution(
+    metadata_contents: bytes,
+    filename: str,
+    canonical_name: str,
+) -> BaseDistribution:
+    """Get the dist representation of the specified METADATA file contents.
+
+    This returns a Distribution instance from the chosen backend sourced from the data
+    in `metadata_contents`.
+
+    :param metadata_contents: Contents of a METADATA file within a dist, or one served
+                              via PEP 658.
+    :param filename: Filename for the dist this metadata represents.
+    :param canonical_name: Normalized project name of the given dist.
+    """
+    return select_backend().Distribution.from_metadata_file_contents(
+        metadata_contents,
+        filename,
+        canonical_name,
+    )
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..bb3b1a7
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/_json.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/_json.cpython-312.pyc
new file mode 100644
index 0000000..9ca723e
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/_json.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/base.cpython-312.pyc
new file mode 100644
index 0000000..bb46e7e
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/base.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/pkg_resources.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/pkg_resources.cpython-312.pyc
new file mode 100644
index 0000000..fd2f913
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/pkg_resources.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/_json.py b/venv/lib/python3.12/site-packages/pip/_internal/metadata/_json.py
new file mode 100644
index 0000000..b39ac05
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/metadata/_json.py
@@ -0,0 +1,87 @@
+# Extracted from https://github.com/pfmoore/pkg_metadata
+from __future__ import annotations
+
+from email.header import Header, decode_header, make_header
+from email.message import Message
+from typing import Any, cast
+
+METADATA_FIELDS = [
+    # Name, Multiple-Use
+    ("Metadata-Version", False),
+    ("Name", False),
+    ("Version", False),
+    ("Dynamic", True),
+    ("Platform", True),
+    ("Supported-Platform", True),
+    ("Summary", False),
+    ("Description", False),
+    ("Description-Content-Type", False),
+    ("Keywords", False),
+    ("Home-page", False),
+    ("Download-URL", False),
+    ("Author", False),
+    ("Author-email", False),
+    ("Maintainer", False),
+    ("Maintainer-email", False),
+    ("License", False),
+    ("License-Expression", False),
+    ("License-File", True),
+    ("Classifier", True),
+    ("Requires-Dist", True),
+    ("Requires-Python", False),
+    ("Requires-External", True),
+    ("Project-URL", True),
+    ("Provides-Extra", True),
+    ("Provides-Dist", True),
+    ("Obsoletes-Dist", True),
+]
+
+
+def json_name(field: str) -> str:
+    return field.lower().replace("-", "_")
+
+
+def msg_to_json(msg: Message) -> dict[str, Any]:
+    """Convert a Message object into a JSON-compatible dictionary."""
+
+    def sanitise_header(h: Header | str) -> str:
+        if isinstance(h, Header):
+            chunks = []
+            for bytes, encoding in decode_header(h):
+                if encoding == "unknown-8bit":
+                    try:
+                        # See if UTF-8 works
+                        bytes.decode("utf-8")
+                        encoding = "utf-8"
+                    except UnicodeDecodeError:
+                        # If not, latin1 at least won't fail
+                        encoding = "latin1"
+                chunks.append((bytes, encoding))
+            return str(make_header(chunks))
+        return str(h)
+
+    result = {}
+    for field, multi in METADATA_FIELDS:
+        if field not in msg:
+            continue
+        key = json_name(field)
+        if multi:
+            value: str | list[str] = [
+                sanitise_header(v) for v in msg.get_all(field)  # type: ignore
+            ]
+        else:
+            value = sanitise_header(msg.get(field))  # type: ignore
+            if key == "keywords":
+                # Accept both comma-separated and space-separated
+                # forms, for better compatibility with old data.
+                if "," in value:
+                    value = [v.strip() for v in value.split(",")]
+                else:
+                    value = value.split()
+        result[key] = value
+
+    payload = cast(str, msg.get_payload())
+    if payload:
+        result["description"] = payload
+
+    return result
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/base.py b/venv/lib/python3.12/site-packages/pip/_internal/metadata/base.py
new file mode 100644
index 0000000..76fb963
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/metadata/base.py
@@ -0,0 +1,690 @@
+from __future__ import annotations
+
+import csv
+import email.message
+import functools
+import json
+import logging
+import pathlib
+import re
+import zipfile
+from collections.abc import Collection, Container, Iterable, Iterator
+from typing import (
+    IO,
+    Any,
+    NamedTuple,
+    Protocol,
+)
+
+from pip._vendor.packaging.requirements import Requirement
+from pip._vendor.packaging.specifiers import InvalidSpecifier, SpecifierSet
+from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
+from pip._vendor.packaging.version import Version
+
+from pip._internal.exceptions import NoneMetadataError
+from pip._internal.locations import site_packages, user_site
+from pip._internal.models.direct_url import (
+    DIRECT_URL_METADATA_NAME,
+    DirectUrl,
+    DirectUrlValidationError,
+)
+from pip._internal.utils.egg_link import egg_link_path_from_sys_path
+from pip._internal.utils.misc import is_local, normalize_path
+from pip._internal.utils.urls import url_to_path
+
+from ._json import msg_to_json
+
+# packages in the stdlib that may have installation metadata, but should not be
+# considered 'installed'.  this theoretically could be determined based on
+# dist.location (py27:`sysconfig.get_paths()['stdlib']`,
+# py26:sysconfig.get_config_vars('LIBDEST')), but fear platform variation may
+# make this ineffective, so hard-coding
+stdlib_pkgs = {"python", "wsgiref", "argparse"}
+
+
+InfoPath = str | pathlib.PurePath
+
+logger = logging.getLogger(__name__)
+
+
+class BaseEntryPoint(Protocol):
+    @property
+    def name(self) -> str:
+        raise NotImplementedError()
+
+    @property
+    def value(self) -> str:
+        raise NotImplementedError()
+
+    @property
+    def group(self) -> str:
+        raise NotImplementedError()
+
+
+def _convert_installed_files_path(
+    entry: tuple[str, ...],
+    info: tuple[str, ...],
+) -> str:
+    """Convert a legacy installed-files.txt path into modern RECORD path.
+
+    The legacy format stores paths relative to the info directory, while the
+    modern format stores paths relative to the package root, e.g. the
+    site-packages directory.
+
+    :param entry: Path parts of the installed-files.txt entry.
+    :param info: Path parts of the egg-info directory relative to package root.
+    :returns: The converted entry.
+
+    For best compatibility with symlinks, this does not use ``abspath()`` or
+    ``Path.resolve()``, but tries to work with path parts:
+
+    1. While ``entry`` starts with ``..``, remove the equal amounts of parts
+       from ``info``; if ``info`` is empty, start appending ``..`` instead.
+    2. Join the two directly.
+    """
+    while entry and entry[0] == "..":
+        if not info or info[-1] == "..":
+            info += ("..",)
+        else:
+            info = info[:-1]
+        entry = entry[1:]
+    return str(pathlib.Path(*info, *entry))
+
+
+class RequiresEntry(NamedTuple):
+    requirement: str
+    extra: str
+    marker: str
+
+
+class BaseDistribution(Protocol):
+    @classmethod
+    def from_directory(cls, directory: str) -> BaseDistribution:
+        """Load the distribution from a metadata directory.
+
+        :param directory: Path to a metadata directory, e.g. ``.dist-info``.
+        """
+        raise NotImplementedError()
+
+    @classmethod
+    def from_metadata_file_contents(
+        cls,
+        metadata_contents: bytes,
+        filename: str,
+        project_name: str,
+    ) -> BaseDistribution:
+        """Load the distribution from the contents of a METADATA file.
+
+        This is used to implement PEP 658 by generating a "shallow" dist object that can
+        be used for resolution without downloading or building the actual dist yet.
+
+        :param metadata_contents: The contents of a METADATA file.
+        :param filename: File name for the dist with this metadata.
+        :param project_name: Name of the project this dist represents.
+        """
+        raise NotImplementedError()
+
+    @classmethod
+    def from_wheel(cls, wheel: Wheel, name: str) -> BaseDistribution:
+        """Load the distribution from a given wheel.
+
+        :param wheel: A concrete wheel definition.
+        :param name: File name of the wheel.
+
+        :raises InvalidWheel: Whenever loading of the wheel causes a
+            :py:exc:`zipfile.BadZipFile` exception to be thrown.
+        :raises UnsupportedWheel: If the wheel is a valid zip, but malformed
+            internally.
+        """
+        raise NotImplementedError()
+
+    def __repr__(self) -> str:
+        return f"{self.raw_name} {self.raw_version} ({self.location})"
+
+    def __str__(self) -> str:
+        return f"{self.raw_name} {self.raw_version}"
+
+    @property
+    def location(self) -> str | None:
+        """Where the distribution is loaded from.
+
+        A string value is not necessarily a filesystem path, since distributions
+        can be loaded from other sources, e.g. arbitrary zip archives. ``None``
+        means the distribution is created in-memory.
+
+        Do not canonicalize this value with e.g. ``pathlib.Path.resolve()``. If
+        this is a symbolic link, we want to preserve the relative path between
+        it and files in the distribution.
+        """
+        raise NotImplementedError()
+
+    @functools.cached_property
+    def editable_project_location(self) -> str | None:
+        """The project location for editable distributions.
+
+        This is the directory where pyproject.toml or setup.py is located.
+        None if the distribution is not installed in editable mode.
+        """
+        direct_url = self.direct_url
+        if direct_url:
+            if direct_url.is_local_editable():
+                return url_to_path(direct_url.url)
+        else:
+            # Search for an .egg-link file by walking sys.path, as it was
+            # done before by dist_is_editable().
+            egg_link_path = egg_link_path_from_sys_path(self.raw_name)
+            if egg_link_path:
+                # TODO: get project location from second line of egg_link file
+                #       (https://github.com/pypa/pip/issues/10243)
+                return self.location
+        return None
+
+    @property
+    def installed_location(self) -> str | None:
+        """The distribution's "installed" location.
+
+        This should generally be a ``site-packages`` directory. This is
+        usually ``dist.location``, except for legacy develop-installed packages,
+        where ``dist.location`` is the source code location, and this is where
+        the ``.egg-link`` file is.
+
+        The returned location is normalized (in particular, with symlinks removed).
+        """
+        raise NotImplementedError()
+
+    @property
+    def info_location(self) -> str | None:
+        """Location of the .[egg|dist]-info directory or file.
+
+        Similarly to ``location``, a string value is not necessarily a
+        filesystem path. ``None`` means the distribution is created in-memory.
+
+        For a modern .dist-info installation on disk, this should be something
+        like ``{location}/{raw_name}-{version}.dist-info``.
+
+        Do not canonicalize this value with e.g. ``pathlib.Path.resolve()``. If
+        this is a symbolic link, we want to preserve the relative path between
+        it and other files in the distribution.
+        """
+        raise NotImplementedError()
+
+    @property
+    def installed_by_distutils(self) -> bool:
+        """Whether this distribution is installed with legacy distutils format.
+
+        A distribution installed with "raw" distutils not patched by setuptools
+        uses one single file at ``info_location`` to store metadata. We need to
+        treat this specially on uninstallation.
+        """
+        info_location = self.info_location
+        if not info_location:
+            return False
+        return pathlib.Path(info_location).is_file()
+
+    @property
+    def installed_as_egg(self) -> bool:
+        """Whether this distribution is installed as an egg.
+
+        This usually indicates the distribution was installed by (older versions
+        of) easy_install.
+        """
+        location = self.location
+        if not location:
+            return False
+        # XXX if the distribution is a zipped egg, location has a trailing /
+        # so we resort to pathlib.Path to check the suffix in a reliable way.
+        return pathlib.Path(location).suffix == ".egg"
+
+    @property
+    def installed_with_setuptools_egg_info(self) -> bool:
+        """Whether this distribution is installed with the ``.egg-info`` format.
+
+        This usually indicates the distribution was installed with setuptools
+        with an old pip version or with ``single-version-externally-managed``.
+
+        Note that this ensure the metadata store is a directory. distutils can
+        also installs an ``.egg-info``, but as a file, not a directory. This
+        property is *False* for that case. Also see ``installed_by_distutils``.
+        """
+        info_location = self.info_location
+        if not info_location:
+            return False
+        if not info_location.endswith(".egg-info"):
+            return False
+        return pathlib.Path(info_location).is_dir()
+
+    @property
+    def installed_with_dist_info(self) -> bool:
+        """Whether this distribution is installed with the "modern format".
+
+        This indicates a "modern" installation, e.g. storing metadata in the
+        ``.dist-info`` directory. This applies to installations made by
+        setuptools (but through pip, not directly), or anything using the
+        standardized build backend interface (PEP 517).
+        """
+        info_location = self.info_location
+        if not info_location:
+            return False
+        if not info_location.endswith(".dist-info"):
+            return False
+        return pathlib.Path(info_location).is_dir()
+
+    @property
+    def canonical_name(self) -> NormalizedName:
+        raise NotImplementedError()
+
+    @property
+    def version(self) -> Version:
+        raise NotImplementedError()
+
+    @property
+    def raw_version(self) -> str:
+        raise NotImplementedError()
+
+    @property
+    def setuptools_filename(self) -> str:
+        """Convert a project name to its setuptools-compatible filename.
+
+        This is a copy of ``pkg_resources.to_filename()`` for compatibility.
+        """
+        return self.raw_name.replace("-", "_")
+
+    @property
+    def direct_url(self) -> DirectUrl | None:
+        """Obtain a DirectUrl from this distribution.
+
+        Returns None if the distribution has no `direct_url.json` metadata,
+        or if `direct_url.json` is invalid.
+        """
+        try:
+            content = self.read_text(DIRECT_URL_METADATA_NAME)
+        except FileNotFoundError:
+            return None
+        try:
+            return DirectUrl.from_json(content)
+        except (
+            UnicodeDecodeError,
+            json.JSONDecodeError,
+            DirectUrlValidationError,
+        ) as e:
+            logger.warning(
+                "Error parsing %s for %s: %s",
+                DIRECT_URL_METADATA_NAME,
+                self.canonical_name,
+                e,
+            )
+            return None
+
+    @property
+    def installer(self) -> str:
+        try:
+            installer_text = self.read_text("INSTALLER")
+        except (OSError, ValueError, NoneMetadataError):
+            return ""  # Fail silently if the installer file cannot be read.
+        for line in installer_text.splitlines():
+            cleaned_line = line.strip()
+            if cleaned_line:
+                return cleaned_line
+        return ""
+
+    @property
+    def requested(self) -> bool:
+        return self.is_file("REQUESTED")
+
+    @property
+    def editable(self) -> bool:
+        return bool(self.editable_project_location)
+
+    @property
+    def local(self) -> bool:
+        """If distribution is installed in the current virtual environment.
+
+        Always True if we're not in a virtualenv.
+        """
+        if self.installed_location is None:
+            return False
+        return is_local(self.installed_location)
+
+    @property
+    def in_usersite(self) -> bool:
+        if self.installed_location is None or user_site is None:
+            return False
+        return self.installed_location.startswith(normalize_path(user_site))
+
+    @property
+    def in_site_packages(self) -> bool:
+        if self.installed_location is None or site_packages is None:
+            return False
+        return self.installed_location.startswith(normalize_path(site_packages))
+
+    def is_file(self, path: InfoPath) -> bool:
+        """Check whether an entry in the info directory is a file."""
+        raise NotImplementedError()
+
+    def iter_distutils_script_names(self) -> Iterator[str]:
+        """Find distutils 'scripts' entries metadata.
+
+        If 'scripts' is supplied in ``setup.py``, distutils records those in the
+        installed distribution's ``scripts`` directory, a file for each script.
+        """
+        raise NotImplementedError()
+
+    def read_text(self, path: InfoPath) -> str:
+        """Read a file in the info directory.
+
+        :raise FileNotFoundError: If ``path`` does not exist in the directory.
+        :raise NoneMetadataError: If ``path`` exists in the info directory, but
+            cannot be read.
+        """
+        raise NotImplementedError()
+
+    def iter_entry_points(self) -> Iterable[BaseEntryPoint]:
+        raise NotImplementedError()
+
+    def _metadata_impl(self) -> email.message.Message:
+        raise NotImplementedError()
+
+    @functools.cached_property
+    def metadata(self) -> email.message.Message:
+        """Metadata of distribution parsed from e.g. METADATA or PKG-INFO.
+
+        This should return an empty message if the metadata file is unavailable.
+
+        :raises NoneMetadataError: If the metadata file is available, but does
+            not contain valid metadata.
+        """
+        metadata = self._metadata_impl()
+        self._add_egg_info_requires(metadata)
+        return metadata
+
+    @property
+    def metadata_dict(self) -> dict[str, Any]:
+        """PEP 566 compliant JSON-serializable representation of METADATA or PKG-INFO.
+
+        This should return an empty dict if the metadata file is unavailable.
+
+        :raises NoneMetadataError: If the metadata file is available, but does
+            not contain valid metadata.
+        """
+        return msg_to_json(self.metadata)
+
+    @property
+    def metadata_version(self) -> str | None:
+        """Value of "Metadata-Version:" in distribution metadata, if available."""
+        return self.metadata.get("Metadata-Version")
+
+    @property
+    def raw_name(self) -> str:
+        """Value of "Name:" in distribution metadata."""
+        # The metadata should NEVER be missing the Name: key, but if it somehow
+        # does, fall back to the known canonical name.
+        return self.metadata.get("Name", self.canonical_name)
+
+    @property
+    def requires_python(self) -> SpecifierSet:
+        """Value of "Requires-Python:" in distribution metadata.
+
+        If the key does not exist or contains an invalid value, an empty
+        SpecifierSet should be returned.
+        """
+        value = self.metadata.get("Requires-Python")
+        if value is None:
+            return SpecifierSet()
+        try:
+            # Convert to str to satisfy the type checker; this can be a Header object.
+            spec = SpecifierSet(str(value))
+        except InvalidSpecifier as e:
+            message = "Package %r has an invalid Requires-Python: %s"
+            logger.warning(message, self.raw_name, e)
+            return SpecifierSet()
+        return spec
+
+    def iter_dependencies(self, extras: Collection[str] = ()) -> Iterable[Requirement]:
+        """Dependencies of this distribution.
+
+        For modern .dist-info distributions, this is the collection of
+        "Requires-Dist:" entries in distribution metadata.
+        """
+        raise NotImplementedError()
+
+    def iter_raw_dependencies(self) -> Iterable[str]:
+        """Raw Requires-Dist metadata."""
+        return self.metadata.get_all("Requires-Dist", [])
+
+    def iter_provided_extras(self) -> Iterable[NormalizedName]:
+        """Extras provided by this distribution.
+
+        For modern .dist-info distributions, this is the collection of
+        "Provides-Extra:" entries in distribution metadata.
+
+        The return value of this function is expected to be normalised names,
+        per PEP 685, with the returned value being handled appropriately by
+        `iter_dependencies`.
+        """
+        raise NotImplementedError()
+
+    def _iter_declared_entries_from_record(self) -> Iterator[str] | None:
+        try:
+            text = self.read_text("RECORD")
+        except FileNotFoundError:
+            return None
+        # This extra Path-str cast normalizes entries.
+        return (str(pathlib.Path(row[0])) for row in csv.reader(text.splitlines()))
+
+    def _iter_declared_entries_from_legacy(self) -> Iterator[str] | None:
+        try:
+            text = self.read_text("installed-files.txt")
+        except FileNotFoundError:
+            return None
+        paths = (p for p in text.splitlines(keepends=False) if p)
+        root = self.location
+        info = self.info_location
+        if root is None or info is None:
+            return paths
+        try:
+            info_rel = pathlib.Path(info).relative_to(root)
+        except ValueError:  # info is not relative to root.
+            return paths
+        if not info_rel.parts:  # info *is* root.
+            return paths
+        return (
+            _convert_installed_files_path(pathlib.Path(p).parts, info_rel.parts)
+            for p in paths
+        )
+
+    def iter_declared_entries(self) -> Iterator[str] | None:
+        """Iterate through file entries declared in this distribution.
+
+        For modern .dist-info distributions, this is the files listed in the
+        ``RECORD`` metadata file. For legacy setuptools distributions, this
+        comes from ``installed-files.txt``, with entries normalized to be
+        compatible with the format used by ``RECORD``.
+
+        :return: An iterator for listed entries, or None if the distribution
+            contains neither ``RECORD`` nor ``installed-files.txt``.
+        """
+        return (
+            self._iter_declared_entries_from_record()
+            or self._iter_declared_entries_from_legacy()
+        )
+
+    def _iter_requires_txt_entries(self) -> Iterator[RequiresEntry]:
+        """Parse a ``requires.txt`` in an egg-info directory.
+
+        This is an INI-ish format where an egg-info stores dependencies. A
+        section name describes extra other environment markers, while each entry
+        is an arbitrary string (not a key-value pair) representing a dependency
+        as a requirement string (no markers).
+
+        There is a construct in ``importlib.metadata`` called ``Sectioned`` that
+        does mostly the same, but the format is currently considered private.
+        """
+        try:
+            content = self.read_text("requires.txt")
+        except FileNotFoundError:
+            return
+        extra = marker = ""  # Section-less entries don't have markers.
+        for line in content.splitlines():
+            line = line.strip()
+            if not line or line.startswith("#"):  # Comment; ignored.
+                continue
+            if line.startswith("[") and line.endswith("]"):  # A section header.
+                extra, _, marker = line.strip("[]").partition(":")
+                continue
+            yield RequiresEntry(requirement=line, extra=extra, marker=marker)
+
+    def _iter_egg_info_extras(self) -> Iterable[str]:
+        """Get extras from the egg-info directory."""
+        known_extras = {""}
+        for entry in self._iter_requires_txt_entries():
+            extra = canonicalize_name(entry.extra)
+            if extra in known_extras:
+                continue
+            known_extras.add(extra)
+            yield extra
+
+    def _iter_egg_info_dependencies(self) -> Iterable[str]:
+        """Get distribution dependencies from the egg-info directory.
+
+        To ease parsing, this converts a legacy dependency entry into a PEP 508
+        requirement string. Like ``_iter_requires_txt_entries()``, there is code
+        in ``importlib.metadata`` that does mostly the same, but not do exactly
+        what we need.
+
+        Namely, ``importlib.metadata`` does not normalize the extra name before
+        putting it into the requirement string, which causes marker comparison
+        to fail because the dist-info format do normalize. This is consistent in
+        all currently available PEP 517 backends, although not standardized.
+        """
+        for entry in self._iter_requires_txt_entries():
+            extra = canonicalize_name(entry.extra)
+            if extra and entry.marker:
+                marker = f'({entry.marker}) and extra == "{extra}"'
+            elif extra:
+                marker = f'extra == "{extra}"'
+            elif entry.marker:
+                marker = entry.marker
+            else:
+                marker = ""
+            if marker:
+                yield f"{entry.requirement} ; {marker}"
+            else:
+                yield entry.requirement
+
+    def _add_egg_info_requires(self, metadata: email.message.Message) -> None:
+        """Add egg-info requires.txt information to the metadata."""
+        if not metadata.get_all("Requires-Dist"):
+            for dep in self._iter_egg_info_dependencies():
+                metadata["Requires-Dist"] = dep
+        if not metadata.get_all("Provides-Extra"):
+            for extra in self._iter_egg_info_extras():
+                metadata["Provides-Extra"] = extra
+
+
+class BaseEnvironment:
+    """An environment containing distributions to introspect."""
+
+    @classmethod
+    def default(cls) -> BaseEnvironment:
+        raise NotImplementedError()
+
+    @classmethod
+    def from_paths(cls, paths: list[str] | None) -> BaseEnvironment:
+        raise NotImplementedError()
+
+    def get_distribution(self, name: str) -> BaseDistribution | None:
+        """Given a requirement name, return the installed distributions.
+
+        The name may not be normalized. The implementation must canonicalize
+        it for lookup.
+        """
+        raise NotImplementedError()
+
+    def _iter_distributions(self) -> Iterator[BaseDistribution]:
+        """Iterate through installed distributions.
+
+        This function should be implemented by subclass, but never called
+        directly. Use the public ``iter_distribution()`` instead, which
+        implements additional logic to make sure the distributions are valid.
+        """
+        raise NotImplementedError()
+
+    def iter_all_distributions(self) -> Iterator[BaseDistribution]:
+        """Iterate through all installed distributions without any filtering."""
+        for dist in self._iter_distributions():
+            # Make sure the distribution actually comes from a valid Python
+            # packaging distribution. Pip's AdjacentTempDirectory leaves folders
+            # e.g. ``~atplotlib.dist-info`` if cleanup was interrupted. The
+            # valid project name pattern is taken from PEP 508.
+            project_name_valid = re.match(
+                r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$",
+                dist.canonical_name,
+                flags=re.IGNORECASE,
+            )
+            if not project_name_valid:
+                logger.warning(
+                    "Ignoring invalid distribution %s (%s)",
+                    dist.canonical_name,
+                    dist.location,
+                )
+                continue
+            yield dist
+
+    def iter_installed_distributions(
+        self,
+        local_only: bool = True,
+        skip: Container[str] = stdlib_pkgs,
+        include_editables: bool = True,
+        editables_only: bool = False,
+        user_only: bool = False,
+    ) -> Iterator[BaseDistribution]:
+        """Return a list of installed distributions.
+
+        This is based on ``iter_all_distributions()`` with additional filtering
+        options. Note that ``iter_installed_distributions()`` without arguments
+        is *not* equal to ``iter_all_distributions()``, since some of the
+        configurations exclude packages by default.
+
+        :param local_only: If True (default), only return installations
+        local to the current virtualenv, if in a virtualenv.
+        :param skip: An iterable of canonicalized project names to ignore;
+            defaults to ``stdlib_pkgs``.
+        :param include_editables: If False, don't report editables.
+        :param editables_only: If True, only report editables.
+        :param user_only: If True, only report installations in the user
+        site directory.
+        """
+        it = self.iter_all_distributions()
+        if local_only:
+            it = (d for d in it if d.local)
+        if not include_editables:
+            it = (d for d in it if not d.editable)
+        if editables_only:
+            it = (d for d in it if d.editable)
+        if user_only:
+            it = (d for d in it if d.in_usersite)
+        return (d for d in it if d.canonical_name not in skip)
+
+
+class Wheel(Protocol):
+    location: str
+
+    def as_zipfile(self) -> zipfile.ZipFile:
+        raise NotImplementedError()
+
+
+class FilesystemWheel(Wheel):
+    def __init__(self, location: str) -> None:
+        self.location = location
+
+    def as_zipfile(self) -> zipfile.ZipFile:
+        return zipfile.ZipFile(self.location, allowZip64=True)
+
+
+class MemoryWheel(Wheel):
+    def __init__(self, location: str, stream: IO[bytes]) -> None:
+        self.location = location
+        self.stream = stream
+
+    def as_zipfile(self) -> zipfile.ZipFile:
+        return zipfile.ZipFile(self.stream, allowZip64=True)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__init__.py
new file mode 100644
index 0000000..a779138
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__init__.py
@@ -0,0 +1,6 @@
+from ._dists import Distribution
+from ._envs import Environment
+
+__all__ = ["NAME", "Distribution", "Environment"]
+
+NAME = "importlib"
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..9316b58
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_compat.cpython-312.pyc
new file mode 100644
index 0000000..3d3b3cc
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_compat.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_dists.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_dists.cpython-312.pyc
new file mode 100644
index 0000000..549f23c
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_dists.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_envs.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_envs.cpython-312.pyc
new file mode 100644
index 0000000..64afcc4
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_envs.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_compat.py b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_compat.py
new file mode 100644
index 0000000..7de614d
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_compat.py
@@ -0,0 +1,87 @@
+from __future__ import annotations
+
+import importlib.metadata
+import os
+from typing import Any, Protocol, cast
+
+from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
+
+
+class BadMetadata(ValueError):
+    def __init__(self, dist: importlib.metadata.Distribution, *, reason: str) -> None:
+        self.dist = dist
+        self.reason = reason
+
+    def __str__(self) -> str:
+        return f"Bad metadata in {self.dist} ({self.reason})"
+
+
+class BasePath(Protocol):
+    """A protocol that various path objects conform.
+
+    This exists because importlib.metadata uses both ``pathlib.Path`` and
+    ``zipfile.Path``, and we need a common base for type hints (Union does not
+    work well since ``zipfile.Path`` is too new for our linter setup).
+
+    This does not mean to be exhaustive, but only contains things that present
+    in both classes *that we need*.
+    """
+
+    @property
+    def name(self) -> str:
+        raise NotImplementedError()
+
+    @property
+    def parent(self) -> BasePath:
+        raise NotImplementedError()
+
+
+def get_info_location(d: importlib.metadata.Distribution) -> BasePath | None:
+    """Find the path to the distribution's metadata directory.
+
+    HACK: This relies on importlib.metadata's private ``_path`` attribute. Not
+    all distributions exist on disk, so importlib.metadata is correct to not
+    expose the attribute as public. But pip's code base is old and not as clean,
+    so we do this to avoid having to rewrite too many things. Hopefully we can
+    eliminate this some day.
+    """
+    return getattr(d, "_path", None)
+
+
+def parse_name_and_version_from_info_directory(
+    dist: importlib.metadata.Distribution,
+) -> tuple[str | None, str | None]:
+    """Get a name and version from the metadata directory name.
+
+    This is much faster than reading distribution metadata.
+    """
+    info_location = get_info_location(dist)
+    if info_location is None:
+        return None, None
+
+    stem, suffix = os.path.splitext(info_location.name)
+    if suffix == ".dist-info":
+        name, sep, version = stem.partition("-")
+        if sep:
+            return name, version
+
+    if suffix == ".egg-info":
+        name = stem.split("-", 1)[0]
+        return name, None
+
+    return None, None
+
+
+def get_dist_canonical_name(dist: importlib.metadata.Distribution) -> NormalizedName:
+    """Get the distribution's normalized name.
+
+    The ``name`` attribute is only available in Python 3.10 or later. We are
+    targeting exactly that, but Mypy does not know this.
+    """
+    if name := parse_name_and_version_from_info_directory(dist)[0]:
+        return canonicalize_name(name)
+
+    name = cast(Any, dist).name
+    if not isinstance(name, str):
+        raise BadMetadata(dist, reason="invalid metadata entry 'name'")
+    return canonicalize_name(name)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_dists.py b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_dists.py
new file mode 100644
index 0000000..98a3cd3
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_dists.py
@@ -0,0 +1,235 @@
+from __future__ import annotations
+
+import email.message
+import importlib.metadata
+import pathlib
+import zipfile
+from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence
+from os import PathLike
+from typing import (
+    cast,
+)
+
+from pip._vendor.packaging.requirements import Requirement
+from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
+from pip._vendor.packaging.version import Version
+from pip._vendor.packaging.version import parse as parse_version
+
+from pip._internal.exceptions import InvalidWheel, UnsupportedWheel
+from pip._internal.metadata.base import (
+    BaseDistribution,
+    BaseEntryPoint,
+    InfoPath,
+    Wheel,
+)
+from pip._internal.utils.misc import normalize_path
+from pip._internal.utils.packaging import get_requirement
+from pip._internal.utils.temp_dir import TempDirectory
+from pip._internal.utils.wheel import parse_wheel, read_wheel_metadata_file
+
+from ._compat import (
+    BadMetadata,
+    BasePath,
+    get_dist_canonical_name,
+    parse_name_and_version_from_info_directory,
+)
+
+
+class WheelDistribution(importlib.metadata.Distribution):
+    """An ``importlib.metadata.Distribution`` read from a wheel.
+
+    Although ``importlib.metadata.PathDistribution`` accepts ``zipfile.Path``,
+    its implementation is too "lazy" for pip's needs (we can't keep the ZipFile
+    handle open for the entire lifetime of the distribution object).
+
+    This implementation eagerly reads the entire metadata directory into the
+    memory instead, and operates from that.
+    """
+
+    def __init__(
+        self,
+        files: Mapping[pathlib.PurePosixPath, bytes],
+        info_location: pathlib.PurePosixPath,
+    ) -> None:
+        self._files = files
+        self.info_location = info_location
+
+    @classmethod
+    def from_zipfile(
+        cls,
+        zf: zipfile.ZipFile,
+        name: str,
+        location: str,
+    ) -> WheelDistribution:
+        info_dir, _ = parse_wheel(zf, name)
+        paths = (
+            (name, pathlib.PurePosixPath(name.split("/", 1)[-1]))
+            for name in zf.namelist()
+            if name.startswith(f"{info_dir}/")
+        )
+        files = {
+            relpath: read_wheel_metadata_file(zf, fullpath)
+            for fullpath, relpath in paths
+        }
+        info_location = pathlib.PurePosixPath(location, info_dir)
+        return cls(files, info_location)
+
+    def iterdir(self, path: InfoPath) -> Iterator[pathlib.PurePosixPath]:
+        # Only allow iterating through the metadata directory.
+        if pathlib.PurePosixPath(str(path)) in self._files:
+            return iter(self._files)
+        raise FileNotFoundError(path)
+
+    def read_text(self, filename: str) -> str | None:
+        try:
+            data = self._files[pathlib.PurePosixPath(filename)]
+        except KeyError:
+            return None
+        try:
+            text = data.decode("utf-8")
+        except UnicodeDecodeError as e:
+            wheel = self.info_location.parent
+            error = f"Error decoding metadata for {wheel}: {e} in {filename} file"
+            raise UnsupportedWheel(error)
+        return text
+
+    def locate_file(self, path: str | PathLike[str]) -> pathlib.Path:
+        # This method doesn't make sense for our in-memory wheel, but the API
+        # requires us to define it.
+        raise NotImplementedError
+
+
+class Distribution(BaseDistribution):
+    def __init__(
+        self,
+        dist: importlib.metadata.Distribution,
+        info_location: BasePath | None,
+        installed_location: BasePath | None,
+    ) -> None:
+        self._dist = dist
+        self._info_location = info_location
+        self._installed_location = installed_location
+
+    @classmethod
+    def from_directory(cls, directory: str) -> BaseDistribution:
+        info_location = pathlib.Path(directory)
+        dist = importlib.metadata.Distribution.at(info_location)
+        return cls(dist, info_location, info_location.parent)
+
+    @classmethod
+    def from_metadata_file_contents(
+        cls,
+        metadata_contents: bytes,
+        filename: str,
+        project_name: str,
+    ) -> BaseDistribution:
+        # Generate temp dir to contain the metadata file, and write the file contents.
+        temp_dir = pathlib.Path(
+            TempDirectory(kind="metadata", globally_managed=True).path
+        )
+        metadata_path = temp_dir / "METADATA"
+        metadata_path.write_bytes(metadata_contents)
+        # Construct dist pointing to the newly created directory.
+        dist = importlib.metadata.Distribution.at(metadata_path.parent)
+        return cls(dist, metadata_path.parent, None)
+
+    @classmethod
+    def from_wheel(cls, wheel: Wheel, name: str) -> BaseDistribution:
+        try:
+            with wheel.as_zipfile() as zf:
+                dist = WheelDistribution.from_zipfile(zf, name, wheel.location)
+        except zipfile.BadZipFile as e:
+            raise InvalidWheel(wheel.location, name) from e
+        return cls(dist, dist.info_location, pathlib.PurePosixPath(wheel.location))
+
+    @property
+    def location(self) -> str | None:
+        if self._info_location is None:
+            return None
+        return str(self._info_location.parent)
+
+    @property
+    def info_location(self) -> str | None:
+        if self._info_location is None:
+            return None
+        return str(self._info_location)
+
+    @property
+    def installed_location(self) -> str | None:
+        if self._installed_location is None:
+            return None
+        return normalize_path(str(self._installed_location))
+
+    @property
+    def canonical_name(self) -> NormalizedName:
+        return get_dist_canonical_name(self._dist)
+
+    @property
+    def version(self) -> Version:
+        try:
+            version = (
+                parse_name_and_version_from_info_directory(self._dist)[1]
+                or self._dist.version
+            )
+            return parse_version(version)
+        except TypeError:
+            raise BadMetadata(self._dist, reason="invalid metadata entry `version`")
+
+    @property
+    def raw_version(self) -> str:
+        return self.metadata["Version"]
+
+    def is_file(self, path: InfoPath) -> bool:
+        return self._dist.read_text(str(path)) is not None
+
+    def iter_distutils_script_names(self) -> Iterator[str]:
+        # A distutils installation is always "flat" (not in e.g. egg form), so
+        # if this distribution's info location is NOT a pathlib.Path (but e.g.
+        # zipfile.Path), it can never contain any distutils scripts.
+        if not isinstance(self._info_location, pathlib.Path):
+            return
+        for child in self._info_location.joinpath("scripts").iterdir():
+            yield child.name
+
+    def read_text(self, path: InfoPath) -> str:
+        content = self._dist.read_text(str(path))
+        if content is None:
+            raise FileNotFoundError(path)
+        return content
+
+    def iter_entry_points(self) -> Iterable[BaseEntryPoint]:
+        # importlib.metadata's EntryPoint structure satisfies BaseEntryPoint.
+        return self._dist.entry_points
+
+    def _metadata_impl(self) -> email.message.Message:
+        # From Python 3.10+, importlib.metadata declares PackageMetadata as the
+        # return type. This protocol is unfortunately a disaster now and misses
+        # a ton of fields that we need, including get() and get_payload(). We
+        # rely on the implementation that the object is actually a Message now,
+        # until upstream can improve the protocol. (python/cpython#94952)
+        metadata = self._dist.metadata
+        # From Python 3.15+, importlib.metadata may return None when no
+        # metadata file (METADATA or PKG-INFO) exists in the distribution
+        # directory. (python/cpython#132947)
+        if metadata is None:
+            return email.message.Message()
+        return cast(email.message.Message, metadata)
+
+    def iter_provided_extras(self) -> Iterable[NormalizedName]:
+        return [
+            canonicalize_name(extra)
+            for extra in self.metadata.get_all("Provides-Extra", [])
+        ]
+
+    def iter_dependencies(self, extras: Collection[str] = ()) -> Iterable[Requirement]:
+        contexts: Sequence[dict[str, str]] = [{"extra": e} for e in extras]
+        for req_string in self.metadata.get_all("Requires-Dist", []):
+            # strip() because email.message.Message.get_all() may return a leading \n
+            # in case a long header was wrapped.
+            req = get_requirement(req_string.strip())
+            if not req.marker:
+                yield req
+            elif not extras and req.marker.evaluate({"extra": ""}):
+                yield req
+            elif any(req.marker.evaluate(context) for context in contexts):
+                yield req
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_envs.py b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_envs.py
new file mode 100644
index 0000000..be732a9
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_envs.py
@@ -0,0 +1,148 @@
+from __future__ import annotations
+
+import importlib.metadata
+import logging
+import os
+import pathlib
+import sys
+import zipfile
+from collections.abc import Iterator, Sequence
+
+from pip._vendor.packaging.utils import (
+    InvalidWheelFilename,
+    NormalizedName,
+    canonicalize_name,
+    parse_wheel_filename,
+)
+
+from pip._internal.metadata.base import BaseDistribution, BaseEnvironment
+from pip._internal.utils.filetypes import WHEEL_EXTENSION
+
+from ._compat import BadMetadata, BasePath, get_dist_canonical_name, get_info_location
+from ._dists import Distribution
+
+logger = logging.getLogger(__name__)
+
+# Used to avoid emitting duplicate invalid distribution metadata warnings for
+# the same dist-info directory.
+_warned_bad_metadata: set[BasePath | None] = set()
+
+
+def _looks_like_wheel(location: str) -> bool:
+    if not location.endswith(WHEEL_EXTENSION):
+        return False
+    if not os.path.isfile(location):
+        return False
+    try:
+        parse_wheel_filename(os.path.basename(location))
+    except InvalidWheelFilename:
+        return False
+    return zipfile.is_zipfile(location)
+
+
+class _DistributionFinder:
+    """Finder to locate distributions.
+
+    The main purpose of this class is to memoize found distributions' names, so
+    only one distribution is returned for each package name. At lot of pip code
+    assumes this (because it is setuptools's behavior), and not doing the same
+    can potentially cause a distribution in lower precedence path to override a
+    higher precedence one if the caller is not careful.
+
+    Eventually we probably want to make it possible to see lower precedence
+    installations as well. It's useful feature, after all.
+    """
+
+    FoundResult = tuple[importlib.metadata.Distribution, BasePath | None]
+
+    def __init__(self) -> None:
+        self._found_names: set[NormalizedName] = set()
+
+    def _find_impl(self, location: str) -> Iterator[FoundResult]:
+        """Find distributions in a location."""
+        # Skip looking inside a wheel. Since a package inside a wheel is not
+        # always valid (due to .data directories etc.), its .dist-info entry
+        # should not be considered an installed distribution.
+        if _looks_like_wheel(location):
+            return
+        # To know exactly where we find a distribution, we have to feed in the
+        # paths one by one, instead of dumping the list to importlib.metadata.
+        for dist in importlib.metadata.distributions(path=[location]):
+            info_location = get_info_location(dist)
+            try:
+                name = get_dist_canonical_name(dist)
+            except BadMetadata as e:
+                if info_location not in _warned_bad_metadata:
+                    logger.warning("Skipping %s due to %s", info_location, e.reason)
+                    _warned_bad_metadata.add(info_location)
+                continue
+            if name in self._found_names:
+                continue
+            self._found_names.add(name)
+            yield dist, info_location
+
+    def find(self, location: str) -> Iterator[BaseDistribution]:
+        """Find distributions in a location.
+
+        The path can be either a directory, or a ZIP archive.
+        """
+        for dist, info_location in self._find_impl(location):
+            if info_location is None:
+                installed_location: BasePath | None = None
+            else:
+                installed_location = info_location.parent
+            yield Distribution(dist, info_location, installed_location)
+
+    def find_legacy_editables(self, location: str) -> Iterator[BaseDistribution]:
+        """Read location in egg-link files and return distributions in there.
+
+        The path should be a directory; otherwise this returns nothing. This
+        follows how setuptools does this for compatibility. The first non-empty
+        line in the egg-link is read as a path (resolved against the egg-link's
+        containing directory if relative). Distributions found at that linked
+        location are returned.
+        """
+        path = pathlib.Path(location)
+        if not path.is_dir():
+            return
+        for child in path.iterdir():
+            if child.suffix != ".egg-link":
+                continue
+            with child.open() as f:
+                lines = (line.strip() for line in f)
+                target_rel = next((line for line in lines if line), "")
+            if not target_rel:
+                continue
+            target_location = str(path.joinpath(target_rel))
+            for dist, info_location in self._find_impl(target_location):
+                yield Distribution(dist, info_location, path)
+
+
+class Environment(BaseEnvironment):
+    def __init__(self, paths: Sequence[str]) -> None:
+        self._paths = paths
+
+    @classmethod
+    def default(cls) -> BaseEnvironment:
+        return cls(sys.path)
+
+    @classmethod
+    def from_paths(cls, paths: list[str] | None) -> BaseEnvironment:
+        if paths is None:
+            return cls(sys.path)
+        return cls(paths)
+
+    def _iter_distributions(self) -> Iterator[BaseDistribution]:
+        finder = _DistributionFinder()
+        for location in self._paths:
+            yield from finder.find(location)
+            yield from finder.find_legacy_editables(location)
+
+    def get_distribution(self, name: str) -> BaseDistribution | None:
+        canonical_name = canonicalize_name(name)
+        matches = (
+            distribution
+            for distribution in self.iter_all_distributions()
+            if distribution.canonical_name == canonical_name
+        )
+        return next(matches, None)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/metadata/pkg_resources.py b/venv/lib/python3.12/site-packages/pip/_internal/metadata/pkg_resources.py
new file mode 100644
index 0000000..89fce8b
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/metadata/pkg_resources.py
@@ -0,0 +1,298 @@
+from __future__ import annotations
+
+import email.message
+import email.parser
+import logging
+import os
+import zipfile
+from collections.abc import Collection, Iterable, Iterator, Mapping
+from typing import (
+    NamedTuple,
+)
+
+from pip._vendor import pkg_resources
+from pip._vendor.packaging.requirements import Requirement
+from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
+from pip._vendor.packaging.version import Version
+from pip._vendor.packaging.version import parse as parse_version
+
+from pip._internal.exceptions import InvalidWheel, NoneMetadataError, UnsupportedWheel
+from pip._internal.utils.egg_link import egg_link_path_from_location
+from pip._internal.utils.misc import display_path, normalize_path
+from pip._internal.utils.wheel import parse_wheel, read_wheel_metadata_file
+
+from .base import (
+    BaseDistribution,
+    BaseEntryPoint,
+    BaseEnvironment,
+    InfoPath,
+    Wheel,
+)
+
+__all__ = ["NAME", "Distribution", "Environment"]
+
+logger = logging.getLogger(__name__)
+
+NAME = "pkg_resources"
+
+
+class EntryPoint(NamedTuple):
+    name: str
+    value: str
+    group: str
+
+
+class InMemoryMetadata:
+    """IMetadataProvider that reads metadata files from a dictionary.
+
+    This also maps metadata decoding exceptions to our internal exception type.
+    """
+
+    def __init__(self, metadata: Mapping[str, bytes], wheel_name: str) -> None:
+        self._metadata = metadata
+        self._wheel_name = wheel_name
+
+    def has_metadata(self, name: str) -> bool:
+        return name in self._metadata
+
+    def get_metadata(self, name: str) -> str:
+        try:
+            return self._metadata[name].decode()
+        except UnicodeDecodeError as e:
+            # Augment the default error with the origin of the file.
+            raise UnsupportedWheel(
+                f"Error decoding metadata for {self._wheel_name}: {e} in {name} file"
+            )
+
+    def get_metadata_lines(self, name: str) -> Iterable[str]:
+        return pkg_resources.yield_lines(self.get_metadata(name))
+
+    def metadata_isdir(self, name: str) -> bool:
+        return False
+
+    def metadata_listdir(self, name: str) -> list[str]:
+        return []
+
+    def run_script(self, script_name: str, namespace: str) -> None:
+        pass
+
+
+class Distribution(BaseDistribution):
+    def __init__(self, dist: pkg_resources.Distribution) -> None:
+        self._dist = dist
+        # This is populated lazily, to avoid loading metadata for all possible
+        # distributions eagerly.
+        self.__extra_mapping: Mapping[NormalizedName, str] | None = None
+
+    @property
+    def _extra_mapping(self) -> Mapping[NormalizedName, str]:
+        if self.__extra_mapping is None:
+            self.__extra_mapping = {
+                canonicalize_name(extra): extra for extra in self._dist.extras
+            }
+
+        return self.__extra_mapping
+
+    @classmethod
+    def from_directory(cls, directory: str) -> BaseDistribution:
+        dist_dir = directory.rstrip(os.sep)
+
+        # Build a PathMetadata object, from path to metadata. :wink:
+        base_dir, dist_dir_name = os.path.split(dist_dir)
+        metadata = pkg_resources.PathMetadata(base_dir, dist_dir)
+
+        # Determine the correct Distribution object type.
+        if dist_dir.endswith(".egg-info"):
+            dist_cls = pkg_resources.Distribution
+            dist_name = os.path.splitext(dist_dir_name)[0]
+        else:
+            assert dist_dir.endswith(".dist-info")
+            dist_cls = pkg_resources.DistInfoDistribution
+            dist_name = os.path.splitext(dist_dir_name)[0].split("-")[0]
+
+        dist = dist_cls(base_dir, project_name=dist_name, metadata=metadata)
+        return cls(dist)
+
+    @classmethod
+    def from_metadata_file_contents(
+        cls,
+        metadata_contents: bytes,
+        filename: str,
+        project_name: str,
+    ) -> BaseDistribution:
+        metadata_dict = {
+            "METADATA": metadata_contents,
+        }
+        dist = pkg_resources.DistInfoDistribution(
+            location=filename,
+            metadata=InMemoryMetadata(metadata_dict, filename),
+            project_name=project_name,
+        )
+        return cls(dist)
+
+    @classmethod
+    def from_wheel(cls, wheel: Wheel, name: str) -> BaseDistribution:
+        try:
+            with wheel.as_zipfile() as zf:
+                info_dir, _ = parse_wheel(zf, name)
+                metadata_dict = {
+                    path.split("/", 1)[-1]: read_wheel_metadata_file(zf, path)
+                    for path in zf.namelist()
+                    if path.startswith(f"{info_dir}/")
+                }
+        except zipfile.BadZipFile as e:
+            raise InvalidWheel(wheel.location, name) from e
+        except UnsupportedWheel as e:
+            raise UnsupportedWheel(f"{name} has an invalid wheel, {e}")
+        dist = pkg_resources.DistInfoDistribution(
+            location=wheel.location,
+            metadata=InMemoryMetadata(metadata_dict, wheel.location),
+            project_name=name,
+        )
+        return cls(dist)
+
+    @property
+    def location(self) -> str | None:
+        return self._dist.location
+
+    @property
+    def installed_location(self) -> str | None:
+        egg_link = egg_link_path_from_location(self.raw_name)
+        if egg_link:
+            location = egg_link
+        elif self.location:
+            location = self.location
+        else:
+            return None
+        return normalize_path(location)
+
+    @property
+    def info_location(self) -> str | None:
+        return self._dist.egg_info
+
+    @property
+    def installed_by_distutils(self) -> bool:
+        # A distutils-installed distribution is provided by FileMetadata. This
+        # provider has a "path" attribute not present anywhere else. Not the
+        # best introspection logic, but pip has been doing this for a long time.
+        try:
+            return bool(self._dist._provider.path)
+        except AttributeError:
+            return False
+
+    @property
+    def canonical_name(self) -> NormalizedName:
+        return canonicalize_name(self._dist.project_name)
+
+    @property
+    def version(self) -> Version:
+        return parse_version(self._dist.version)
+
+    @property
+    def raw_version(self) -> str:
+        return self._dist.version
+
+    def is_file(self, path: InfoPath) -> bool:
+        return self._dist.has_metadata(str(path))
+
+    def iter_distutils_script_names(self) -> Iterator[str]:
+        yield from self._dist.metadata_listdir("scripts")
+
+    def read_text(self, path: InfoPath) -> str:
+        name = str(path)
+        if not self._dist.has_metadata(name):
+            raise FileNotFoundError(name)
+        content = self._dist.get_metadata(name)
+        if content is None:
+            raise NoneMetadataError(self, name)
+        return content
+
+    def iter_entry_points(self) -> Iterable[BaseEntryPoint]:
+        for group, entries in self._dist.get_entry_map().items():
+            for name, entry_point in entries.items():
+                name, _, value = str(entry_point).partition("=")
+                yield EntryPoint(name=name.strip(), value=value.strip(), group=group)
+
+    def _metadata_impl(self) -> email.message.Message:
+        """
+        :raises NoneMetadataError: if the distribution reports `has_metadata()`
+            True but `get_metadata()` returns None.
+        """
+        if isinstance(self._dist, pkg_resources.DistInfoDistribution):
+            metadata_name = "METADATA"
+        else:
+            metadata_name = "PKG-INFO"
+        try:
+            metadata = self.read_text(metadata_name)
+        except FileNotFoundError:
+            if self.location:
+                displaying_path = display_path(self.location)
+            else:
+                displaying_path = repr(self.location)
+            logger.warning("No metadata found in %s", displaying_path)
+            metadata = ""
+        feed_parser = email.parser.FeedParser()
+        feed_parser.feed(metadata)
+        return feed_parser.close()
+
+    def iter_dependencies(self, extras: Collection[str] = ()) -> Iterable[Requirement]:
+        if extras:
+            relevant_extras = set(self._extra_mapping) & set(
+                map(canonicalize_name, extras)
+            )
+            extras = [self._extra_mapping[extra] for extra in relevant_extras]
+        return self._dist.requires(extras)
+
+    def iter_provided_extras(self) -> Iterable[NormalizedName]:
+        return self._extra_mapping.keys()
+
+
+class Environment(BaseEnvironment):
+    def __init__(self, ws: pkg_resources.WorkingSet) -> None:
+        self._ws = ws
+
+    @classmethod
+    def default(cls) -> BaseEnvironment:
+        return cls(pkg_resources.working_set)
+
+    @classmethod
+    def from_paths(cls, paths: list[str] | None) -> BaseEnvironment:
+        return cls(pkg_resources.WorkingSet(paths))
+
+    def _iter_distributions(self) -> Iterator[BaseDistribution]:
+        for dist in self._ws:
+            yield Distribution(dist)
+
+    def _search_distribution(self, name: str) -> BaseDistribution | None:
+        """Find a distribution matching the ``name`` in the environment.
+
+        This searches from *all* distributions available in the environment, to
+        match the behavior of ``pkg_resources.get_distribution()``.
+        """
+        canonical_name = canonicalize_name(name)
+        for dist in self.iter_all_distributions():
+            if dist.canonical_name == canonical_name:
+                return dist
+        return None
+
+    def get_distribution(self, name: str) -> BaseDistribution | None:
+        # Search the distribution by looking through the working set.
+        dist = self._search_distribution(name)
+        if dist:
+            return dist
+
+        # If distribution could not be found, call working_set.require to
+        # update the working set, and try to find the distribution again.
+        # This might happen for e.g. when you install a package twice, once
+        # using setup.py develop and again using setup.py install. Now when
+        # running pip uninstall twice, the package gets removed from the
+        # working set in the first uninstall, so we have to populate the
+        # working set again so that pip knows about it and the packages gets
+        # picked up and is successfully uninstalled the second time too.
+        try:
+            # We didn't pass in any version specifiers, so this can never
+            # raise pkg_resources.VersionConflict.
+            self._ws.require(name)
+        except pkg_resources.DistributionNotFound:
+            return None
+        return self._search_distribution(name)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/models/__init__.py
new file mode 100644
index 0000000..7b1fc29
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/models/__init__.py
@@ -0,0 +1 @@
+"""A package that contains models that represent entities."""
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..6365cef
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/candidate.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/candidate.cpython-312.pyc
new file mode 100644
index 0000000..ef366c8
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/candidate.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-312.pyc
new file mode 100644
index 0000000..b5f569b
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/format_control.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/format_control.cpython-312.pyc
new file mode 100644
index 0000000..89f70db
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/format_control.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/index.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/index.cpython-312.pyc
new file mode 100644
index 0000000..fcd9b16
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/index.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/installation_report.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/installation_report.cpython-312.pyc
new file mode 100644
index 0000000..16cf7ee
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/installation_report.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/link.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/link.cpython-312.pyc
new file mode 100644
index 0000000..af50670
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/link.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/release_control.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/release_control.cpython-312.pyc
new file mode 100644
index 0000000..8a79762
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/release_control.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/scheme.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/scheme.cpython-312.pyc
new file mode 100644
index 0000000..9964b55
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/scheme.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-312.pyc
new file mode 100644
index 0000000..2c7509b
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-312.pyc
new file mode 100644
index 0000000..b4c84f2
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/target_python.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/target_python.cpython-312.pyc
new file mode 100644
index 0000000..28579da
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/target_python.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/wheel.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/wheel.cpython-312.pyc
new file mode 100644
index 0000000..7e0563a
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/wheel.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/candidate.py b/venv/lib/python3.12/site-packages/pip/_internal/models/candidate.py
new file mode 100644
index 0000000..0639466
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/models/candidate.py
@@ -0,0 +1,27 @@
+from dataclasses import dataclass
+
+from pip._vendor.packaging.version import Version
+from pip._vendor.packaging.version import parse as parse_version
+
+from pip._internal.models.link import Link
+
+
+@dataclass(frozen=True, slots=True)
+class InstallationCandidate:
+    """Represents a potential "candidate" for installation."""
+
+    name: str
+    version: Version
+    link: Link
+    locked: bool
+
+    def __init__(
+        self, name: str, version: str, link: Link, locked: bool = False
+    ) -> None:
+        object.__setattr__(self, "name", name)
+        object.__setattr__(self, "version", parse_version(version))
+        object.__setattr__(self, "link", link)
+        object.__setattr__(self, "locked", locked)
+
+    def __str__(self) -> str:
+        return f"{self.name!r} candidate (version {self.version} at {self.link})"
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/direct_url.py b/venv/lib/python3.12/site-packages/pip/_internal/models/direct_url.py
new file mode 100644
index 0000000..a2dfb34
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/models/direct_url.py
@@ -0,0 +1,42 @@
+"""PEP 610"""
+
+from __future__ import annotations
+
+import json
+from typing import Any
+
+from pip._vendor.packaging.direct_url import (
+    ArchiveInfo,
+    DirectUrlValidationError,
+    DirInfo,
+    VcsInfo,
+)
+from pip._vendor.packaging.direct_url import (
+    DirectUrl as PackagingDirectUrl,
+)
+
+__all__ = [
+    "ArchiveInfo",
+    "DirInfo",
+    "DirectUrl",
+    "DirectUrlValidationError",
+    "DIRECT_URL_METADATA_NAME",
+    "VcsInfo",
+]
+
+DIRECT_URL_METADATA_NAME = "direct_url.json"
+
+
+class DirectUrl(PackagingDirectUrl):
+    def to_dict_compat(self) -> dict[str, Any]:
+        return dict(super().to_dict(generate_legacy_hash=True))
+
+    @classmethod
+    def from_json(cls, s: str) -> DirectUrl:
+        return cls.from_dict(json.loads(s))
+
+    def to_json(self) -> str:
+        return json.dumps(self.to_dict_compat(), sort_keys=True)
+
+    def is_local_editable(self) -> bool:
+        return bool(self.dir_info and self.dir_info.editable)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/format_control.py b/venv/lib/python3.12/site-packages/pip/_internal/models/format_control.py
new file mode 100644
index 0000000..9f07e3f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/models/format_control.py
@@ -0,0 +1,78 @@
+from __future__ import annotations
+
+from pip._vendor.packaging.utils import canonicalize_name
+
+from pip._internal.exceptions import CommandError
+
+
+class FormatControl:
+    """Helper for managing formats from which a package can be installed."""
+
+    __slots__ = ["no_binary", "only_binary"]
+
+    def __init__(
+        self,
+        no_binary: set[str] | None = None,
+        only_binary: set[str] | None = None,
+    ) -> None:
+        if no_binary is None:
+            no_binary = set()
+        if only_binary is None:
+            only_binary = set()
+
+        self.no_binary = no_binary
+        self.only_binary = only_binary
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, self.__class__):
+            return NotImplemented
+
+        if self.__slots__ != other.__slots__:
+            return False
+
+        return all(getattr(self, k) == getattr(other, k) for k in self.__slots__)
+
+    def __repr__(self) -> str:
+        return f"{self.__class__.__name__}({self.no_binary}, {self.only_binary})"
+
+    @staticmethod
+    def handle_mutual_excludes(value: str, target: set[str], other: set[str]) -> None:
+        if value.startswith("-"):
+            raise CommandError(
+                "--no-binary / --only-binary option requires 1 argument."
+            )
+        new = value.split(",")
+        while ":all:" in new:
+            other.clear()
+            target.clear()
+            target.add(":all:")
+            del new[: new.index(":all:") + 1]
+            # Without a none, we want to discard everything as :all: covers it
+            if ":none:" not in new:
+                return
+        for name in new:
+            if name == ":none:":
+                target.clear()
+                continue
+            name = canonicalize_name(name)
+            other.discard(name)
+            target.add(name)
+
+    def get_allowed_formats(self, canonical_name: str) -> frozenset[str]:
+        result = {"binary", "source"}
+        if canonical_name in self.only_binary:
+            result.discard("source")
+        elif canonical_name in self.no_binary:
+            result.discard("binary")
+        elif ":all:" in self.only_binary:
+            result.discard("source")
+        elif ":all:" in self.no_binary:
+            result.discard("binary")
+        return frozenset(result)
+
+    def disallow_binaries(self) -> None:
+        self.handle_mutual_excludes(
+            ":all:",
+            self.no_binary,
+            self.only_binary,
+        )
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/index.py b/venv/lib/python3.12/site-packages/pip/_internal/models/index.py
new file mode 100644
index 0000000..b94c325
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/models/index.py
@@ -0,0 +1,28 @@
+import urllib.parse
+
+
+class PackageIndex:
+    """Represents a Package Index and provides easier access to endpoints"""
+
+    __slots__ = ["url", "netloc", "simple_url", "pypi_url", "file_storage_domain"]
+
+    def __init__(self, url: str, file_storage_domain: str) -> None:
+        super().__init__()
+        self.url = url
+        self.netloc = urllib.parse.urlsplit(url).netloc
+        self.simple_url = self._url_for_path("simple")
+        self.pypi_url = self._url_for_path("pypi")
+
+        # This is part of a temporary hack used to block installs of PyPI
+        # packages which depend on external urls only necessary until PyPI can
+        # block such packages themselves
+        self.file_storage_domain = file_storage_domain
+
+    def _url_for_path(self, path: str) -> str:
+        return urllib.parse.urljoin(self.url, path)
+
+
+PyPI = PackageIndex("https://pypi.org/", file_storage_domain="files.pythonhosted.org")
+TestPyPI = PackageIndex(
+    "https://test.pypi.org/", file_storage_domain="test-files.pythonhosted.org"
+)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/installation_report.py b/venv/lib/python3.12/site-packages/pip/_internal/models/installation_report.py
new file mode 100644
index 0000000..145e2cf
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/models/installation_report.py
@@ -0,0 +1,57 @@
+from collections.abc import Sequence
+from typing import Any
+
+from pip._vendor.packaging.markers import default_environment
+
+from pip import __version__
+from pip._internal.req.req_install import InstallRequirement
+
+
+class InstallationReport:
+    def __init__(self, install_requirements: Sequence[InstallRequirement]):
+        self._install_requirements = install_requirements
+
+    @classmethod
+    def _install_req_to_dict(cls, ireq: InstallRequirement) -> dict[str, Any]:
+        assert ireq.download_info, f"No download_info for {ireq}"
+        res = {
+            # PEP 610 json for the download URL. download_info.archive_info.hashes may
+            # be absent when the requirement was installed from the wheel cache
+            # and the cache entry was populated by an older pip version that did not
+            # record origin.json.
+            "download_info": ireq.download_info.to_dict_compat(),
+            # is_direct is true if the requirement was a direct URL reference (which
+            # includes editable requirements), and false if the requirement was
+            # downloaded from a PEP 503 index or --find-links.
+            "is_direct": ireq.is_direct,
+            # is_yanked is true if the requirement was yanked from the index, but
+            # was still selected by pip to conform to PEP 592.
+            "is_yanked": ireq.link.is_yanked if ireq.link else False,
+            # requested is true if the requirement was specified by the user (aka
+            # top level requirement), and false if it was installed as a dependency of a
+            # requirement. https://peps.python.org/pep-0376/#requested
+            "requested": ireq.user_supplied,
+            # PEP 566 json encoding for metadata
+            # https://www.python.org/dev/peps/pep-0566/#json-compatible-metadata
+            "metadata": ireq.get_dist().metadata_dict,
+        }
+        if ireq.user_supplied and ireq.extras:
+            # For top level requirements, the list of requested extras, if any.
+            res["requested_extras"] = sorted(ireq.extras)
+        return res
+
+    def to_dict(self) -> dict[str, Any]:
+        return {
+            "version": "1",
+            "pip_version": __version__,
+            "install": [
+                self._install_req_to_dict(ireq) for ireq in self._install_requirements
+            ],
+            # https://peps.python.org/pep-0508/#environment-markers
+            # TODO: currently, the resolver uses the default environment to evaluate
+            # environment markers, so that is what we report here. In the future, it
+            # should also take into account options such as --python-version or
+            # --platform, perhaps under the form of an environment_override field?
+            # https://github.com/pypa/pip/issues/11198
+            "environment": default_environment(),
+        }
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/link.py b/venv/lib/python3.12/site-packages/pip/_internal/models/link.py
new file mode 100644
index 0000000..cbbe945
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/models/link.py
@@ -0,0 +1,657 @@
+from __future__ import annotations
+
+import datetime
+import functools
+import itertools
+import logging
+import os
+import posixpath
+import re
+import urllib.parse
+from collections.abc import Mapping
+from dataclasses import dataclass
+from typing import (
+    Any,
+    NamedTuple,
+    NewType,
+)
+
+from pip._internal.exceptions import InvalidEggFragment
+from pip._internal.utils.datetime import parse_iso_datetime
+from pip._internal.utils.filetypes import WHEEL_EXTENSION
+from pip._internal.utils.hashes import Hashes
+from pip._internal.utils.misc import (
+    pairwise,
+    redact_auth_from_url,
+    split_auth_from_netloc,
+    splitext,
+)
+from pip._internal.utils.urls import path_to_url, url_to_path
+
+logger = logging.getLogger(__name__)
+
+
+# A single path component: percent-decoded once and reduced to a basename, so it
+# contains no path separator and is not a ``.`` or ``..`` reference. The empty
+# string means "no component".
+PathComponent = NewType("PathComponent", str)
+
+
+def _to_path_component(name: str) -> PathComponent:
+    """Reduce ``name`` to a single path component, or ``""`` if it has none.
+
+    ``os.path.basename`` drops any directory part, drive letter, or separator;
+    a ``.``, ``..``, or empty result is not a component and becomes ``""``.
+    """
+    name = os.path.basename(name)
+    if name in ("", os.curdir, os.pardir):
+        return PathComponent("")
+
+    return PathComponent(name)
+
+
+def as_path_component(name: str) -> PathComponent:
+    """Like ``_to_path_component`` but reject the empty result.
+
+    Use where a file is about to be written, so a missing name is an error
+    rather than a silent fallback to the directory itself.
+    """
+    component = _to_path_component(name)
+    if not component:
+        raise ValueError(f"Unexpected file name derived from URL: {name!r}")
+
+    return component
+
+
+def join_within_directory(directory: str, component: PathComponent) -> str:
+    """Join a single path ``component`` onto ``directory``.
+
+    ``component`` is a :data:`PathComponent`, so by type it has no separator and
+    is not a ``.`` or ``..`` reference; the result can never escape ``directory``.
+    Requiring ``PathComponent`` rather than ``str`` lets the type checker enforce
+    at the call site that the name was reduced to a safe component beforehand.
+    """
+    return os.path.join(directory, component)
+
+
+# Order matters, earlier hashes have a precedence over later hashes for what
+# we will pick to use.
+_SUPPORTED_HASHES = ("sha512", "sha384", "sha256", "sha224", "sha1", "md5")
+
+
+@dataclass(frozen=True)
+class LinkHash:
+    """Links to content may have embedded hash values. This class parses those.
+
+    `name` must be any member of `_SUPPORTED_HASHES`.
+
+    This class can be converted to and from `ArchiveInfo`. While ArchiveInfo intends to
+    be JSON-serializable to conform to PEP 610, this class contains the logic for
+    parsing a hash name and value for correctness, and then checking whether that hash
+    conforms to a schema with `.is_hash_allowed()`."""
+
+    name: str
+    value: str
+
+    _hash_url_fragment_re = re.compile(
+        # NB: we do not validate that the second group (.*) is a valid hex
+        # digest. Instead, we simply keep that string in this class, and then check it
+        # against Hashes when hash-checking is needed. This is easier to debug than
+        # proactively discarding an invalid hex digest, as we handle incorrect hashes
+        # and malformed hashes in the same place.
+        r"[#&]({choices})=([^&]*)".format(
+            choices="|".join(re.escape(hash_name) for hash_name in _SUPPORTED_HASHES)
+        ),
+    )
+
+    def __post_init__(self) -> None:
+        assert self.name in _SUPPORTED_HASHES
+
+    @classmethod
+    @functools.cache
+    def find_hash_url_fragment(cls, url: str) -> LinkHash | None:
+        """Search a string for a checksum algorithm name and encoded output value."""
+        match = cls._hash_url_fragment_re.search(url)
+        if match is None:
+            return None
+        name, value = match.groups()
+        return cls(name=name, value=value)
+
+    def as_dict(self) -> dict[str, str]:
+        return {self.name: self.value}
+
+    def as_hashes(self) -> Hashes:
+        """Return a Hashes instance which checks only for the current hash."""
+        return Hashes({self.name: [self.value]})
+
+    def is_hash_allowed(self, hashes: Hashes | None) -> bool:
+        """
+        Return True if the current hash is allowed by `hashes`.
+        """
+        if hashes is None:
+            return False
+        return hashes.is_hash_allowed(self.name, hex_digest=self.value)
+
+
+@dataclass(frozen=True)
+class MetadataFile:
+    """Information about a core metadata file associated with a distribution."""
+
+    hashes: dict[str, str] | None
+
+    def __post_init__(self) -> None:
+        if self.hashes is not None:
+            assert all(name in _SUPPORTED_HASHES for name in self.hashes)
+
+
+def supported_hashes(hashes: dict[str, str] | None) -> dict[str, str] | None:
+    # Remove any unsupported hash types from the mapping. If this leaves no
+    # supported hashes, return None
+    if hashes is None:
+        return None
+    hashes = {n: v for n, v in hashes.items() if n in _SUPPORTED_HASHES}
+    if not hashes:
+        return None
+    return hashes
+
+
+def _clean_url_path_part(part: str) -> str:
+    """
+    Clean a "part" of a URL path (i.e. after splitting on "@" characters).
+    """
+    # We unquote prior to quoting to make sure nothing is double quoted.
+    return urllib.parse.quote(urllib.parse.unquote(part))
+
+
+def _clean_file_url_path(part: str) -> str:
+    """
+    Clean the first part of a URL path that corresponds to a local
+    filesystem path (i.e. the first part after splitting on "@" characters).
+    """
+    import urllib.request
+
+    # We unquote prior to quoting to make sure nothing is double quoted.
+    # Also, on Windows the path part might contain a drive letter which
+    # should not be quoted. On Linux where drive letters do not
+    # exist, the colon should be quoted. We rely on urllib.request
+    # to do the right thing here.
+    ret = urllib.request.pathname2url(urllib.request.url2pathname(part))
+    if ret.startswith("///"):
+        # Remove any URL authority section, leaving only the URL path.
+        ret = ret.removeprefix("//")
+    return ret
+
+
+# percent-encoded:                   /
+_reserved_chars_re = re.compile("(@|%2F)", re.IGNORECASE)
+
+
+def _clean_url_path(path: str, is_local_path: bool) -> str:
+    """
+    Clean the path portion of a URL.
+    """
+    if is_local_path:
+        clean_func = _clean_file_url_path
+    else:
+        clean_func = _clean_url_path_part
+
+    # Split on the reserved characters prior to cleaning so that
+    # revision strings in VCS URLs are properly preserved.
+    parts = _reserved_chars_re.split(path)
+
+    cleaned_parts = []
+    for to_clean, reserved in pairwise(itertools.chain(parts, [""])):
+        cleaned_parts.append(clean_func(to_clean))
+        # Normalize %xx escapes (e.g. %2f -> %2F)
+        cleaned_parts.append(reserved.upper())
+
+    return "".join(cleaned_parts)
+
+
+def _ensure_quoted_url(url: str) -> str:
+    """
+    Make sure a link is fully quoted.
+    For example, if ' ' occurs in the URL, it will be replaced with "%20",
+    and without double-quoting other characters.
+    """
+    # Split the URL into parts according to the general structure
+    # `scheme://netloc/path?query#fragment`.
+    result = urllib.parse.urlsplit(url)
+    # If the netloc is empty, then the URL refers to a local filesystem path.
+    is_local_path = not result.netloc
+    path = _clean_url_path(result.path, is_local_path=is_local_path)
+    # Temporarily replace scheme with file to ensure the URL generated by
+    # urlunsplit() contains an empty netloc (file://) as per RFC 1738.
+    ret = urllib.parse.urlunsplit(result._replace(scheme="file", path=path))
+    ret = result.scheme + ret[4:]  # Restore original scheme.
+    return ret
+
+
+def _absolute_link_url(base_url: str, url: str) -> str:
+    """
+    A faster implementation of urllib.parse.urljoin with a shortcut
+    for absolute http/https URLs.
+    """
+    if url.startswith(("https://", "http://")):
+        return url
+    else:
+        return urllib.parse.urljoin(base_url, url)
+
+
+@functools.total_ordering
+class Link:
+    """Represents a parsed link from a Package Index's simple URL"""
+
+    __slots__ = [
+        "_parsed_url",
+        "_url",
+        "_path",
+        "_hashes",
+        "comes_from",
+        "requires_python",
+        "yanked_reason",
+        "metadata_file_data",
+        "upload_time",
+        "cache_link_parsing",
+        "egg_fragment",
+    ]
+
+    def __init__(
+        self,
+        url: str,
+        comes_from: str | None = None,
+        requires_python: str | None = None,
+        yanked_reason: str | None = None,
+        metadata_file_data: MetadataFile | None = None,
+        upload_time: datetime.datetime | None = None,
+        cache_link_parsing: bool = True,
+        hashes: Mapping[str, str] | None = None,
+    ) -> None:
+        """
+        :param url: url of the resource pointed to (href of the link)
+        :param comes_from: URL or string indicating where the link was found.
+        :param requires_python: String containing the `Requires-Python`
+            metadata field, specified in PEP 345. This may be specified by
+            a data-requires-python attribute in the HTML link tag, as
+            described in PEP 503.
+        :param yanked_reason: the reason the file has been yanked, if the
+            file has been yanked, or None if the file hasn't been yanked.
+            This is the value of the "data-yanked" attribute, if present, in
+            a simple repository HTML link. If the file has been yanked but
+            no reason was provided, this should be the empty string. See
+            PEP 592 for more information and the specification.
+        :param metadata_file_data: the metadata attached to the file, or None if
+            no such metadata is provided. This argument, if not None, indicates
+            that a separate metadata file exists, and also optionally supplies
+            hashes for that file.
+        :param upload_time: upload time of the file, or None if the information
+            is not available from the server.
+        :param cache_link_parsing: A flag that is used elsewhere to determine
+            whether resources retrieved from this link should be cached. PyPI
+            URLs should generally have this set to False, for example.
+        :param hashes: A mapping of hash names to digests to allow us to
+            determine the validity of a download.
+        """
+
+        # The comes_from, requires_python, and metadata_file_data arguments are
+        # only used by classmethods of this class, and are not used in client
+        # code directly.
+
+        # url can be a UNC windows share
+        if url.startswith("\\\\"):
+            url = path_to_url(url)
+
+        self._parsed_url = urllib.parse.urlsplit(url)
+        # Store the url as a private attribute to prevent accidentally
+        # trying to set a new value.
+        self._url = url
+        # The .path property is hot, so calculate its value ahead of time.
+        self._path = urllib.parse.unquote(self._parsed_url.path)
+
+        link_hash = LinkHash.find_hash_url_fragment(url)
+        hashes_from_link = {} if link_hash is None else link_hash.as_dict()
+        if hashes is None:
+            self._hashes = hashes_from_link
+        else:
+            self._hashes = {**hashes, **hashes_from_link}
+
+        self.comes_from = comes_from
+        self.requires_python = requires_python if requires_python else None
+        self.yanked_reason = yanked_reason
+        self.metadata_file_data = metadata_file_data
+        self.upload_time = upload_time
+
+        self.cache_link_parsing = cache_link_parsing
+        self.egg_fragment = self._egg_fragment()
+
+    @classmethod
+    def from_json(
+        cls,
+        file_data: dict[str, Any],
+        page_url: str,
+    ) -> Link | None:
+        """
+        Convert an pypi json document from a simple repository page into a Link.
+        """
+        file_url = file_data.get("url")
+        if file_url is None:
+            return None
+
+        url = _ensure_quoted_url(_absolute_link_url(page_url, file_url))
+        pyrequire = file_data.get("requires-python")
+        yanked_reason = file_data.get("yanked")
+        hashes = file_data.get("hashes", {})
+
+        # PEP 714: Indexes must use the name core-metadata, but
+        # clients should support the old name as a fallback for compatibility.
+        metadata_info = file_data.get("core-metadata")
+        if metadata_info is None:
+            metadata_info = file_data.get("dist-info-metadata")
+
+        if upload_time_data := file_data.get("upload-time"):
+            upload_time = parse_iso_datetime(upload_time_data)
+        else:
+            upload_time = None
+
+        # The metadata info value may be a boolean, or a dict of hashes.
+        if isinstance(metadata_info, dict):
+            # The file exists, and hashes have been supplied
+            metadata_file_data = MetadataFile(supported_hashes(metadata_info))
+        elif metadata_info:
+            # The file exists, but there are no hashes
+            metadata_file_data = MetadataFile(None)
+        else:
+            # False or not present: the file does not exist
+            metadata_file_data = None
+
+        # The Link.yanked_reason expects an empty string instead of a boolean.
+        if yanked_reason and not isinstance(yanked_reason, str):
+            yanked_reason = ""
+        # The Link.yanked_reason expects None instead of False.
+        elif not yanked_reason:
+            yanked_reason = None
+
+        return cls(
+            url,
+            comes_from=page_url,
+            requires_python=pyrequire,
+            yanked_reason=yanked_reason,
+            hashes=hashes,
+            metadata_file_data=metadata_file_data,
+            upload_time=upload_time,
+        )
+
+    @classmethod
+    def from_element(
+        cls,
+        anchor_attribs: dict[str, str | None],
+        page_url: str,
+        base_url: str,
+    ) -> Link | None:
+        """
+        Convert an anchor element's attributes in a simple repository page to a Link.
+        """
+        href = anchor_attribs.get("href")
+        if not href:
+            return None
+
+        url = _ensure_quoted_url(_absolute_link_url(base_url, href))
+        pyrequire = anchor_attribs.get("data-requires-python")
+        yanked_reason = anchor_attribs.get("data-yanked")
+
+        # PEP 714: Indexes must use the name data-core-metadata, but
+        # clients should support the old name as a fallback for compatibility.
+        metadata_info = anchor_attribs.get("data-core-metadata")
+        if metadata_info is None:
+            metadata_info = anchor_attribs.get("data-dist-info-metadata")
+        # The metadata info value may be the string "true", or a string of
+        # the form "hashname=hashval"
+        if metadata_info == "true":
+            # The file exists, but there are no hashes
+            metadata_file_data = MetadataFile(None)
+        elif metadata_info is None:
+            # The file does not exist
+            metadata_file_data = None
+        else:
+            # The file exists, and hashes have been supplied
+            hashname, sep, hashval = metadata_info.partition("=")
+            if sep == "=":
+                metadata_file_data = MetadataFile(supported_hashes({hashname: hashval}))
+            else:
+                # Error - data is wrong. Treat as no hashes supplied.
+                logger.debug(
+                    "Index returned invalid data-dist-info-metadata value: %s",
+                    metadata_info,
+                )
+                metadata_file_data = MetadataFile(None)
+
+        return cls(
+            url,
+            comes_from=page_url,
+            requires_python=pyrequire,
+            yanked_reason=yanked_reason,
+            metadata_file_data=metadata_file_data,
+        )
+
+    def __str__(self) -> str:
+        if self.requires_python:
+            rp = f" (requires-python:{self.requires_python})"
+        else:
+            rp = ""
+        if self.comes_from:
+            return f"{self.redacted_url} (from {self.comes_from}){rp}"
+        else:
+            return self.redacted_url
+
+    def __repr__(self) -> str:
+        return f""
+
+    def __hash__(self) -> int:
+        return hash(self.url)
+
+    def __eq__(self, other: Any) -> bool:
+        if not isinstance(other, Link):
+            return NotImplemented
+        return self.url == other.url
+
+    def __lt__(self, other: Any) -> bool:
+        if not isinstance(other, Link):
+            return NotImplemented
+        return self.url < other.url
+
+    @property
+    def url(self) -> str:
+        return self._url
+
+    @property
+    def redacted_url(self) -> str:
+        return redact_auth_from_url(self.url)
+
+    @property
+    def filename(self) -> PathComponent:
+        name = _to_path_component(posixpath.basename(self.path.rstrip("/")))
+        if name:
+            return name
+
+        # No component in the path; fall back to the netloc, dropping any auth.
+        return _to_path_component(split_auth_from_netloc(self.netloc)[0])
+
+    @property
+    def file_path(self) -> str:
+        return url_to_path(self.url)
+
+    @property
+    def scheme(self) -> str:
+        return self._parsed_url.scheme
+
+    @property
+    def netloc(self) -> str:
+        """
+        This can contain auth information.
+        """
+        return self._parsed_url.netloc
+
+    @property
+    def path(self) -> str:
+        return self._path
+
+    def splitext(self) -> tuple[str, str]:
+        return splitext(posixpath.basename(self.path.rstrip("/")))
+
+    @property
+    def ext(self) -> str:
+        return self.splitext()[1]
+
+    @property
+    def url_without_fragment(self) -> str:
+        scheme, netloc, path, query, fragment = self._parsed_url
+        return urllib.parse.urlunsplit((scheme, netloc, path, query, ""))
+
+    _egg_fragment_re = re.compile(r"[#&]egg=([^&]*)")
+
+    # Per PEP 508.
+    _project_name_re = re.compile(
+        r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE
+    )
+
+    def _egg_fragment(self) -> str | None:
+        match = self._egg_fragment_re.search(self._url)
+        if not match:
+            return None
+
+        # An egg fragment looks like a PEP 508 project name, along with
+        # an optional extras specifier. Anything else is invalid.
+        project_name = match.group(1)
+        if not self._project_name_re.match(project_name):
+            raise InvalidEggFragment(self, project_name)
+
+        return project_name
+
+    _subdirectory_fragment_re = re.compile(r"[#&]subdirectory=([^&]*)")
+
+    @property
+    def subdirectory_fragment(self) -> str | None:
+        match = self._subdirectory_fragment_re.search(self._url)
+        if not match:
+            return None
+        return match.group(1)
+
+    def metadata_link(self) -> Link | None:
+        """Return a link to the associated core metadata file (if any)."""
+        if self.metadata_file_data is None:
+            return None
+        metadata_url = f"{self.url_without_fragment}.metadata"
+        if self.metadata_file_data.hashes is None:
+            return Link(metadata_url)
+        return Link(metadata_url, hashes=self.metadata_file_data.hashes)
+
+    def as_hashes(self) -> Hashes:
+        return Hashes({k: [v] for k, v in self._hashes.items()})
+
+    @property
+    def hash(self) -> str | None:
+        return next(iter(self._hashes.values()), None)
+
+    @property
+    def hash_name(self) -> str | None:
+        return next(iter(self._hashes), None)
+
+    @property
+    def show_url(self) -> str:
+        return posixpath.basename(self._url.split("#", 1)[0].split("?", 1)[0])
+
+    @property
+    def is_file(self) -> bool:
+        return self.scheme == "file"
+
+    def is_existing_dir(self) -> bool:
+        return self.is_file and os.path.isdir(self.file_path)
+
+    @property
+    def is_wheel(self) -> bool:
+        return self.ext == WHEEL_EXTENSION
+
+    @property
+    def is_vcs(self) -> bool:
+        from pip._internal.vcs import vcs
+
+        return self.scheme in vcs.all_schemes
+
+    @property
+    def is_yanked(self) -> bool:
+        return self.yanked_reason is not None
+
+    @property
+    def has_hash(self) -> bool:
+        return bool(self._hashes)
+
+    def is_hash_allowed(self, hashes: Hashes | None) -> bool:
+        """
+        Return True if the link has a hash and it is allowed by `hashes`.
+        """
+        if hashes is None:
+            return False
+        return any(hashes.is_hash_allowed(k, v) for k, v in self._hashes.items())
+
+
+class _CleanResult(NamedTuple):
+    """Convert link for equivalency check.
+
+    This is used in the resolver to check whether two URL-specified requirements
+    likely point to the same distribution and can be considered equivalent. This
+    equivalency logic avoids comparing URLs literally, which can be too strict
+    (e.g. "a=1&b=2" vs "b=2&a=1") and produce conflicts unexpecting to users.
+
+    Currently this does three things:
+
+    1. Drop the basic auth part. This is technically wrong since a server can
+       serve different content based on auth, but if it does that, it is even
+       impossible to guarantee two URLs without auth are equivalent, since
+       the user can input different auth information when prompted. So the
+       practical solution is to assume the auth doesn't affect the response.
+    2. Parse the query to avoid the ordering issue. Note that ordering under the
+       same key in the query are NOT cleaned; i.e. "a=1&a=2" and "a=2&a=1" are
+       still considered different.
+    3. Explicitly drop most of the fragment part, except ``subdirectory=`` and
+       hash values, since it should have no impact the downloaded content. Note
+       that this drops the "egg=" part historically used to denote the requested
+       project (and extras), which is wrong in the strictest sense, but too many
+       people are supplying it inconsistently to cause superfluous resolution
+       conflicts, so we choose to also ignore them.
+    """
+
+    parsed: urllib.parse.SplitResult
+    query: dict[str, list[str]]
+    subdirectory: str
+    hashes: dict[str, str]
+
+
+def _clean_link(link: Link) -> _CleanResult:
+    parsed = link._parsed_url
+    netloc = parsed.netloc.rsplit("@", 1)[-1]
+    # According to RFC 8089, an empty host in file: means localhost.
+    if parsed.scheme == "file" and not netloc:
+        netloc = "localhost"
+    fragment = urllib.parse.parse_qs(parsed.fragment)
+    if "egg" in fragment:
+        logger.debug("Ignoring egg= fragment in %s", link)
+    try:
+        # If there are multiple subdirectory values, use the first one.
+        # This matches the behavior of Link.subdirectory_fragment.
+        subdirectory = fragment["subdirectory"][0]
+    except (IndexError, KeyError):
+        subdirectory = ""
+    # If there are multiple hash values under the same algorithm, use the
+    # first one. This matches the behavior of Link.hash_value.
+    hashes = {k: fragment[k][0] for k in _SUPPORTED_HASHES if k in fragment}
+    return _CleanResult(
+        parsed=parsed._replace(netloc=netloc, query="", fragment=""),
+        query=urllib.parse.parse_qs(parsed.query),
+        subdirectory=subdirectory,
+        hashes=hashes,
+    )
+
+
+@functools.cache
+def links_equivalent(link1: Link, link2: Link) -> bool:
+    return _clean_link(link1) == _clean_link(link2)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/release_control.py b/venv/lib/python3.12/site-packages/pip/_internal/models/release_control.py
new file mode 100644
index 0000000..d432fa3
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/models/release_control.py
@@ -0,0 +1,91 @@
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+
+from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
+
+from pip._internal.exceptions import CommandError
+
+
+@dataclass(slots=True)
+class ReleaseControl:
+    """Helper for managing which release types can be installed."""
+
+    all_releases: set[str] = field(default_factory=set)
+    only_final: set[str] = field(default_factory=set)
+    _order: list[tuple[str, str]] = field(
+        init=False, default_factory=list, compare=False, repr=False
+    )
+
+    def handle_mutual_excludes(
+        self, value: str, target: set[str], other: set[str], attr_name: str
+    ) -> None:
+        """Parse and apply release control option value.
+
+        Processes comma-separated package names or special values `:all:` and `:none:`.
+
+        When adding packages to target, they're removed from other to maintain mutual
+        exclusivity between all_releases and only_final. All operations are tracked in
+        order so that the original command-line argument sequence can be reconstructed
+        when passing options to build subprocesses.
+        """
+        if value.startswith("-"):
+            raise CommandError(
+                "--all-releases / --only-final option requires 1 argument."
+            )
+        new = value.split(",")
+        while ":all:" in new:
+            other.clear()
+            target.clear()
+            target.add(":all:")
+            # Track :all: in order
+            self._order.append((attr_name, ":all:"))
+            del new[: new.index(":all:") + 1]
+            # Without a none, we want to discard everything as :all: covers it
+            if ":none:" not in new:
+                return
+        for name in new:
+            if name == ":none:":
+                target.clear()
+                # Track :none: in order
+                self._order.append((attr_name, ":none:"))
+                continue
+            name = canonicalize_name(name)
+            other.discard(name)
+            target.add(name)
+            # Track package-specific setting in order
+            self._order.append((attr_name, name))
+
+    def get_ordered_args(self) -> list[tuple[str, str]]:
+        """
+        Get ordered list of (flag_name, value) tuples for reconstructing CLI args.
+
+        Returns:
+            List of tuples where each tuple is (attribute_name, value).
+            The attribute_name is either 'all_releases' or 'only_final'.
+
+        Example:
+            [("all_releases", ":all:"), ("only_final", "simple")]
+            would be reconstructed as:
+            ["--all-releases", ":all:", "--only-final", "simple"]
+        """
+        return self._order[:]
+
+    def allows_prereleases(self, canonical_name: NormalizedName) -> bool | None:
+        """
+        Determine if pre-releases are allowed for a package.
+
+        Returns:
+            True: Pre-releases are allowed (package in all_releases)
+            False: Only final releases allowed (package in only_final)
+            None: No specific setting, use default behavior
+        """
+        if canonical_name in self.all_releases:
+            return True
+        elif canonical_name in self.only_final:
+            return False
+        elif ":all:" in self.all_releases:
+            return True
+        elif ":all:" in self.only_final:
+            return False
+        return None
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/scheme.py b/venv/lib/python3.12/site-packages/pip/_internal/models/scheme.py
new file mode 100644
index 0000000..5ed7057
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/models/scheme.py
@@ -0,0 +1,23 @@
+"""
+For types associated with installation schemes.
+
+For a general overview of available schemes and their context, see
+https://docs.python.org/3/install/index.html#alternate-installation.
+"""
+
+from dataclasses import dataclass
+
+SCHEME_KEYS = ["platlib", "purelib", "headers", "scripts", "data"]
+
+
+@dataclass(frozen=True, slots=True)
+class Scheme:
+    """A Scheme holds paths which are used as the base directories for
+    artifacts associated with a Python package.
+    """
+
+    platlib: str
+    purelib: str
+    headers: str
+    scripts: str
+    data: str
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/search_scope.py b/venv/lib/python3.12/site-packages/pip/_internal/models/search_scope.py
new file mode 100644
index 0000000..b3aaa18
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/models/search_scope.py
@@ -0,0 +1,124 @@
+import itertools
+import logging
+import os
+import posixpath
+import urllib.parse
+from dataclasses import dataclass
+
+from pip._vendor.packaging.utils import canonicalize_name
+
+from pip._internal.models.index import PyPI
+from pip._internal.utils.compat import has_tls
+from pip._internal.utils.misc import normalize_path, redact_auth_from_url
+
+logger = logging.getLogger(__name__)
+
+
+@dataclass(frozen=True, slots=True)
+class SearchScope:
+    """
+    Encapsulates the locations that pip is configured to search.
+    """
+
+    find_links: list[str]
+    index_urls: list[str]
+    no_index: bool
+
+    @classmethod
+    def create(
+        cls,
+        find_links: list[str],
+        index_urls: list[str],
+        no_index: bool,
+    ) -> "SearchScope":
+        """
+        Create a SearchScope object after normalizing the `find_links`.
+        """
+        # Build find_links. If an argument starts with ~, it may be
+        # a local file relative to a home directory. So try normalizing
+        # it and if it exists, use the normalized version.
+        # This is deliberately conservative - it might be fine just to
+        # blindly normalize anything starting with a ~...
+        built_find_links: list[str] = []
+        for link in find_links:
+            if link.startswith("~"):
+                new_link = normalize_path(link)
+                if os.path.exists(new_link):
+                    link = new_link
+            built_find_links.append(link)
+
+        # If we don't have TLS enabled, then WARN if anyplace we're looking
+        # relies on TLS.
+        if not has_tls():
+            for link in itertools.chain(index_urls, built_find_links):
+                parsed = urllib.parse.urlparse(link)
+                if parsed.scheme == "https":
+                    logger.warning(
+                        "pip is configured with locations that require "
+                        "TLS/SSL, however the ssl module in Python is not "
+                        "available."
+                    )
+                    break
+
+        return cls(
+            find_links=built_find_links,
+            index_urls=index_urls,
+            no_index=no_index,
+        )
+
+    def get_formatted_locations(self) -> str:
+        lines = []
+        redacted_index_urls = []
+        if self.index_urls and self.index_urls != [PyPI.simple_url]:
+            for url in self.index_urls:
+                redacted_index_url = redact_auth_from_url(url)
+
+                # Parse the URL
+                purl = urllib.parse.urlsplit(redacted_index_url)
+
+                # URL is generally invalid if scheme and netloc is missing
+                # there are issues with Python and URL parsing, so this test
+                # is a bit crude. See bpo-20271, bpo-23505. Python doesn't
+                # always parse invalid URLs correctly - it should raise
+                # exceptions for malformed URLs
+                if not purl.scheme and not purl.netloc:
+                    logger.warning(
+                        'The index url "%s" seems invalid, please provide a scheme.',
+                        redacted_index_url,
+                    )
+
+                redacted_index_urls.append(redacted_index_url)
+
+            lines.append(
+                "Looking in indexes: {}".format(", ".join(redacted_index_urls))
+            )
+
+        if self.find_links:
+            lines.append(
+                "Looking in links: {}".format(
+                    ", ".join(redact_auth_from_url(url) for url in self.find_links)
+                )
+            )
+        return "\n".join(lines)
+
+    def get_index_urls_locations(self, project_name: str) -> list[str]:
+        """Returns the locations found via self.index_urls
+
+        Checks the url_name on the main (first in the list) index and
+        use this url_name to produce all locations
+        """
+
+        def mkurl_pypi_url(url: str) -> str:
+            loc = posixpath.join(
+                url, urllib.parse.quote(canonicalize_name(project_name))
+            )
+            # For maximum compatibility with easy_install, ensure the path
+            # ends in a trailing slash.  Although this isn't in the spec
+            # (and PyPI can handle it without the slash) some other index
+            # implementations might break if they relied on easy_install's
+            # behavior.
+            if not loc.endswith("/"):
+                loc = loc + "/"
+            return loc
+
+        return [mkurl_pypi_url(url) for url in self.index_urls]
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/selection_prefs.py b/venv/lib/python3.12/site-packages/pip/_internal/models/selection_prefs.py
new file mode 100644
index 0000000..3473031
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/models/selection_prefs.py
@@ -0,0 +1,36 @@
+from __future__ import annotations
+
+from dataclasses import dataclass
+
+from pip._internal.models.format_control import FormatControl
+from pip._internal.models.release_control import ReleaseControl
+
+
+@dataclass(slots=True)
+class SelectionPreferences:
+    """
+    Encapsulates the candidate selection preferences for downloading
+    and installing files.
+
+    :param allow_yanked: Whether files marked as yanked (in the sense
+        of PEP 592) are permitted to be candidates for install.
+    :param release_control: A ReleaseControl object or None. Used to control
+        whether pre-releases are allowed for specific packages.
+    :param format_control: A FormatControl object or None. Used to control
+        the selection of source packages / binary packages when consulting
+        the index and links.
+    :param prefer_binary: Whether to prefer an old, but valid, binary
+        dist over a new source dist.
+    :param ignore_requires_python: Whether to ignore incompatible
+        "Requires-Python" values in links. Defaults to False.
+    """
+
+    # Don't include an allow_yanked default value to make sure each call
+    # site considers whether yanked releases are allowed. This also causes
+    # that decision to be made explicit in the calling code, which helps
+    # people when reading the code.
+    allow_yanked: bool
+    release_control: ReleaseControl | None = None
+    format_control: FormatControl | None = None
+    prefer_binary: bool = False
+    ignore_requires_python: bool = False
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/target_python.py b/venv/lib/python3.12/site-packages/pip/_internal/models/target_python.py
new file mode 100644
index 0000000..8c38392
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/models/target_python.py
@@ -0,0 +1,122 @@
+from __future__ import annotations
+
+import sys
+
+from pip._vendor.packaging.tags import Tag
+
+from pip._internal.utils.compatibility_tags import get_supported, version_info_to_nodot
+from pip._internal.utils.misc import normalize_version_info
+
+
+class TargetPython:
+    """
+    Encapsulates the properties of a Python interpreter one is targeting
+    for a package install, download, etc.
+    """
+
+    __slots__ = [
+        "_given_py_version_info",
+        "abis",
+        "implementation",
+        "platforms",
+        "py_version",
+        "py_version_info",
+        "_valid_tags",
+        "_valid_tags_set",
+    ]
+
+    def __init__(
+        self,
+        platforms: list[str] | None = None,
+        py_version_info: tuple[int, ...] | None = None,
+        abis: list[str] | None = None,
+        implementation: str | None = None,
+    ) -> None:
+        """
+        :param platforms: A list of strings or None. If None, searches for
+            packages that are supported by the current system. Otherwise, will
+            find packages that can be built on the platforms passed in. These
+            packages will only be downloaded for distribution: they will
+            not be built locally.
+        :param py_version_info: An optional tuple of ints representing the
+            Python version information to use (e.g. `sys.version_info[:3]`).
+            This can have length 1, 2, or 3 when provided.
+        :param abis: A list of strings or None. This is passed to
+            compatibility_tags.py's get_supported() function as is.
+        :param implementation: A string or None. This is passed to
+            compatibility_tags.py's get_supported() function as is.
+        """
+        # Store the given py_version_info for when we call get_supported().
+        self._given_py_version_info = py_version_info
+
+        if py_version_info is None:
+            py_version_info = sys.version_info[:3]
+        else:
+            py_version_info = normalize_version_info(py_version_info)
+
+        py_version = ".".join(map(str, py_version_info[:2]))
+
+        self.abis = abis
+        self.implementation = implementation
+        self.platforms = platforms
+        self.py_version = py_version
+        self.py_version_info = py_version_info
+
+        # This is used to cache the return value of get_(un)sorted_tags.
+        self._valid_tags: list[Tag] | None = None
+        self._valid_tags_set: set[Tag] | None = None
+
+    def format_given(self) -> str:
+        """
+        Format the given, non-None attributes for display.
+        """
+        display_version = None
+        if self._given_py_version_info is not None:
+            display_version = ".".join(
+                str(part) for part in self._given_py_version_info
+            )
+
+        key_values = [
+            ("platforms", self.platforms),
+            ("version_info", display_version),
+            ("abis", self.abis),
+            ("implementation", self.implementation),
+        ]
+        return " ".join(
+            f"{key}={value!r}" for key, value in key_values if value is not None
+        )
+
+    def get_sorted_tags(self) -> list[Tag]:
+        """
+        Return the supported PEP 425 tags to check wheel candidates against.
+
+        The tags are returned in order of preference (most preferred first).
+        """
+        if self._valid_tags is None:
+            # Pass versions=None if no py_version_info was given since
+            # versions=None uses special default logic.
+            py_version_info = self._given_py_version_info
+            if py_version_info is None:
+                version = None
+            else:
+                version = version_info_to_nodot(py_version_info)
+
+            tags = get_supported(
+                version=version,
+                platforms=self.platforms,
+                abis=self.abis,
+                impl=self.implementation,
+            )
+            self._valid_tags = tags
+
+        return self._valid_tags
+
+    def get_unsorted_tags(self) -> set[Tag]:
+        """Exactly the same as get_sorted_tags, but returns a set.
+
+        This is important for performance.
+        """
+        if self._valid_tags_set is None:
+            self._valid_tags_set = set(self.get_sorted_tags())
+
+        return self._valid_tags_set
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/models/wheel.py b/venv/lib/python3.12/site-packages/pip/_internal/models/wheel.py
new file mode 100644
index 0000000..fbd4902
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/models/wheel.py
@@ -0,0 +1,80 @@
+"""Represents a wheel file and provides access to the various parts of the
+name that have meaning.
+"""
+
+from __future__ import annotations
+
+from collections.abc import Iterable
+
+from pip._vendor.packaging.tags import Tag
+from pip._vendor.packaging.utils import (
+    InvalidWheelFilename as _PackagingInvalidWheelFilename,
+)
+from pip._vendor.packaging.utils import parse_wheel_filename
+
+from pip._internal.exceptions import InvalidWheelFilename
+
+
+class Wheel:
+    """A wheel file"""
+
+    def __init__(self, filename: str) -> None:
+        self.filename = filename
+
+        try:
+            wheel_info = parse_wheel_filename(filename)
+        except _PackagingInvalidWheelFilename as e:
+            raise InvalidWheelFilename(e.args[0]) from None
+
+        self.name, _version, self.build_tag, self.file_tags = wheel_info
+        self.version = str(_version)
+
+    def get_formatted_file_tags(self) -> list[str]:
+        """Return the wheel's tags as a sorted list of strings."""
+        return sorted(str(tag) for tag in self.file_tags)
+
+    def support_index_min(self, tags: list[Tag]) -> int:
+        """Return the lowest index that one of the wheel's file_tag combinations
+        achieves in the given list of supported tags.
+
+        For example, if there are 8 supported tags and one of the file tags
+        is first in the list, then return 0.
+
+        :param tags: the PEP 425 tags to check the wheel against, in order
+            with most preferred first.
+
+        :raises ValueError: If none of the wheel's file tags match one of
+            the supported tags.
+        """
+        try:
+            return next(i for i, t in enumerate(tags) if t in self.file_tags)
+        except StopIteration:
+            raise ValueError()
+
+    def find_most_preferred_tag(
+        self, tags: list[Tag], tag_to_priority: dict[Tag, int]
+    ) -> int:
+        """Return the priority of the most preferred tag that one of the wheel's file
+        tag combinations achieves in the given list of supported tags using the given
+        tag_to_priority mapping, where lower priorities are more-preferred.
+
+        This is used in place of support_index_min in some cases in order to avoid
+        an expensive linear scan of a large list of tags.
+
+        :param tags: the PEP 425 tags to check the wheel against.
+        :param tag_to_priority: a mapping from tag to priority of that tag, where
+            lower is more preferred.
+
+        :raises ValueError: If none of the wheel's file tags match one of
+            the supported tags.
+        """
+        return min(
+            tag_to_priority[tag] for tag in self.file_tags if tag in tag_to_priority
+        )
+
+    def supported(self, tags: Iterable[Tag]) -> bool:
+        """Return whether the wheel is compatible with one of the given tags.
+
+        :param tags: the PEP 425 tags to check the wheel against.
+        """
+        return not self.file_tags.isdisjoint(tags)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/network/__init__.py
new file mode 100644
index 0000000..0ae1f56
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/network/__init__.py
@@ -0,0 +1 @@
+"""Contains purely network-related utilities."""
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..0a0a356
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/auth.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/auth.cpython-312.pyc
new file mode 100644
index 0000000..60c77d8
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/auth.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/cache.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/cache.cpython-312.pyc
new file mode 100644
index 0000000..d3d98cd
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/cache.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/download.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/download.cpython-312.pyc
new file mode 100644
index 0000000..b62596e
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/download.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-312.pyc
new file mode 100644
index 0000000..3f20c0c
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/session.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/session.cpython-312.pyc
new file mode 100644
index 0000000..9e1c29b
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/session.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/utils.cpython-312.pyc
new file mode 100644
index 0000000..18053c8
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/utils.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-312.pyc
new file mode 100644
index 0000000..c51eb03
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/auth.py b/venv/lib/python3.12/site-packages/pip/_internal/network/auth.py
new file mode 100644
index 0000000..99b929a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/network/auth.py
@@ -0,0 +1,587 @@
+"""Network Authentication Helpers
+
+Contains interface (MultiDomainBasicAuth) and associated glue code for
+providing credentials in the context of network requests.
+"""
+
+from __future__ import annotations
+
+import json
+import logging
+import os
+import shutil
+import subprocess
+import sysconfig
+import typing
+import urllib.parse
+from abc import ABC, abstractmethod
+from functools import cache
+from os.path import commonpath
+from pathlib import Path
+from typing import Any, NamedTuple
+
+from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth
+from pip._vendor.requests.utils import get_netrc_auth
+
+from pip._internal.utils.logging import getLogger
+from pip._internal.utils.misc import (
+    ask,
+    ask_input,
+    ask_password,
+    remove_auth_from_url,
+    split_auth_netloc_from_url,
+)
+from pip._internal.vcs.versioncontrol import AuthInfo
+
+if typing.TYPE_CHECKING:
+    from pip._vendor.requests import PreparedRequest
+    from pip._vendor.requests.models import Response
+
+logger = getLogger(__name__)
+
+KEYRING_DISABLED = False
+
+
+class Credentials(NamedTuple):
+    url: str
+    username: str
+    password: str
+
+
+class KeyRingBaseProvider(ABC):
+    """Keyring base provider interface"""
+
+    has_keyring: bool
+
+    @abstractmethod
+    def get_auth_info(self, url: str, username: str | None) -> AuthInfo | None: ...
+
+    @abstractmethod
+    def save_auth_info(self, url: str, username: str, password: str) -> None: ...
+
+
+class KeyRingNullProvider(KeyRingBaseProvider):
+    """Keyring null provider"""
+
+    has_keyring = False
+
+    def get_auth_info(self, url: str, username: str | None) -> AuthInfo | None:
+        return None
+
+    def save_auth_info(self, url: str, username: str, password: str) -> None:
+        return None
+
+
+class KeyRingPythonProvider(KeyRingBaseProvider):
+    """Keyring interface which uses locally imported `keyring`"""
+
+    has_keyring = True
+
+    def __init__(self) -> None:
+        import keyring
+
+        self.keyring = keyring
+
+    def get_auth_info(self, url: str, username: str | None) -> AuthInfo | None:
+        # Support keyring's get_credential interface which supports getting
+        # credentials without a username. This is only available for
+        # keyring>=15.2.0.
+        if hasattr(self.keyring, "get_credential"):
+            logger.debug("Getting credentials from keyring for %s", url)
+            cred = self.keyring.get_credential(url, username)
+            if cred is not None:
+                return cred.username, cred.password
+            return None
+
+        if username is not None:
+            logger.debug("Getting password from keyring for %s", url)
+            password = self.keyring.get_password(url, username)
+            if password:
+                return username, password
+        return None
+
+    def save_auth_info(self, url: str, username: str, password: str) -> None:
+        self.keyring.set_password(url, username, password)
+
+
+class KeyRingCliProvider(KeyRingBaseProvider):
+    """Provider which uses `keyring` cli
+
+    Instead of calling the keyring package installed alongside pip
+    we call keyring on the command line which will enable pip to
+    use which ever installation of keyring is available first in
+    PATH.
+    """
+
+    has_keyring = True
+
+    def __init__(self, cmd: str) -> None:
+        self.keyring = cmd
+
+    def get_auth_info(self, url: str, username: str | None) -> AuthInfo | None:
+        return self._get_creds(url, username)
+
+    def save_auth_info(self, url: str, username: str, password: str) -> None:
+        return self._set_password(url, username, password)
+
+    def _get_creds(self, service_name: str, username: str | None) -> AuthInfo | None:
+        """Mirror the implementation of keyring.get_credential using cli"""
+        if self.keyring is None:
+            return None
+
+        cmd = [self.keyring, "--mode=creds", "--output=json", "get", service_name]
+        if username is not None:
+            cmd.append(username)
+
+        env = os.environ.copy()
+        env["PYTHONIOENCODING"] = "utf-8"
+        res = subprocess.run(  # noqa: UP022
+            cmd,
+            stdin=subprocess.DEVNULL,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            env=env,
+        )
+
+        # Detect if the user is running an outdated version of keyring without support
+        # for querying credentials without username
+        errs = res.stderr.decode("utf-8")
+        if (
+            res.returncode == 2
+            and "unrecognized arguments" in errs
+            and "--mode=creds" in errs
+        ):
+            raise RuntimeError(
+                "Keyring util is outdated; must be at least version 25.2.1, "
+                "please upgrade it"
+            )
+
+        if res.returncode:
+            return None
+
+        data = json.loads(res.stdout.decode("utf-8"))
+        return (data["username"], data["password"])
+
+    def _set_password(self, service_name: str, username: str, password: str) -> None:
+        """Mirror the implementation of keyring.set_password using cli"""
+        if self.keyring is None:
+            return None
+        env = os.environ.copy()
+        env["PYTHONIOENCODING"] = "utf-8"
+        subprocess.run(
+            [self.keyring, "set", service_name, username],
+            input=f"{password}{os.linesep}".encode(),
+            env=env,
+            check=True,
+        )
+        return None
+
+
+@cache
+def get_keyring_provider(provider: str) -> KeyRingBaseProvider:
+    logger.verbose("Keyring provider requested: %s", provider)
+
+    # keyring has previously failed and been disabled
+    if KEYRING_DISABLED:
+        provider = "disabled"
+    if provider in ["import", "auto"]:
+        try:
+            impl = KeyRingPythonProvider()
+            logger.verbose("Keyring provider set: import")
+            return impl
+        except ImportError:
+            pass
+        except Exception as exc:
+            # In the event of an unexpected exception
+            # we should warn the user
+            msg = "Installed copy of keyring fails with exception %s"
+            if provider == "auto":
+                msg = msg + ", trying to find a keyring executable as a fallback"
+            logger.warning(msg, exc, exc_info=logger.isEnabledFor(logging.DEBUG))
+    if provider in ["subprocess", "auto"]:
+        cli = shutil.which("keyring")
+        if cli and cli.startswith(sysconfig.get_path("scripts")):
+            # all code within this function is stolen from shutil.which implementation
+            @typing.no_type_check
+            def PATH_as_shutil_which_determines_it() -> str:
+                path = os.environ.get("PATH", None)
+                if path is None:
+                    try:
+                        path = os.confstr("CS_PATH")
+                    except (AttributeError, ValueError):
+                        # os.confstr() or CS_PATH is not available
+                        path = os.defpath
+                # bpo-35755: Don't use os.defpath if the PATH environment variable is
+                # set to an empty string
+
+                return path
+
+            scripts = Path(sysconfig.get_path("scripts"))
+
+            paths = []
+            for path in PATH_as_shutil_which_determines_it().split(os.pathsep):
+                p = Path(path)
+                try:
+                    if not p.samefile(scripts):
+                        paths.append(path)
+                except FileNotFoundError:
+                    pass
+
+            path = os.pathsep.join(paths)
+
+            cli = shutil.which("keyring", path=path)
+
+        if cli:
+            logger.verbose("Keyring provider set: subprocess with executable %s", cli)
+            return KeyRingCliProvider(cli)
+
+    logger.verbose("Keyring provider set: disabled")
+    return KeyRingNullProvider()
+
+
+class MultiDomainBasicAuth(AuthBase):
+    def __init__(
+        self,
+        prompting: bool = True,
+        index_urls: list[str] | None = None,
+        keyring_provider: str = "auto",
+    ) -> None:
+        self.prompting = prompting
+        self.index_urls = index_urls
+        self.keyring_provider = keyring_provider
+        self.passwords: dict[str, AuthInfo] = {}
+        # When the user is prompted to enter credentials and keyring is
+        # available, we will offer to save them. If the user accepts,
+        # this value is set to the credentials they entered. After the
+        # request authenticates, the caller should call
+        # ``save_credentials`` to save these.
+        self._credentials_to_save: Credentials | None = None
+
+    @property
+    def keyring_provider(self) -> KeyRingBaseProvider:
+        return get_keyring_provider(self._keyring_provider)
+
+    @keyring_provider.setter
+    def keyring_provider(self, provider: str) -> None:
+        # The free function get_keyring_provider has been decorated with
+        # functools.cache. If an exception occurs in get_keyring_auth that
+        # cache will be cleared and keyring disabled, take that into account
+        # if you want to remove this indirection.
+        self._keyring_provider = provider
+
+    @property
+    def use_keyring(self) -> bool:
+        # We won't use keyring when --no-input is passed unless
+        # a specific provider is requested because it might require
+        # user interaction
+        return self.prompting or self._keyring_provider not in ["auto", "disabled"]
+
+    def _get_keyring_auth(
+        self,
+        url: str | None,
+        username: str | None,
+    ) -> AuthInfo | None:
+        """Return the tuple auth for a given url from keyring."""
+        # Do nothing if no url was provided
+        if not url:
+            return None
+
+        try:
+            return self.keyring_provider.get_auth_info(url, username)
+        except Exception as exc:
+            # Log the full exception (with stacktrace) at debug, so it'll only
+            # show up when running in verbose mode.
+            logger.debug("Keyring is skipped due to an exception", exc_info=True)
+            # Always log a shortened version of the exception.
+            logger.warning(
+                "Keyring is skipped due to an exception: %s",
+                str(exc),
+            )
+            global KEYRING_DISABLED
+            KEYRING_DISABLED = True
+            get_keyring_provider.cache_clear()
+            return None
+
+    def _get_index_url(self, url: str) -> str | None:
+        """Return the original index URL matching the requested URL.
+
+        Cached or dynamically generated credentials may work against
+        the original index URL rather than just the netloc.
+
+        The provided url should have had its username and password
+        removed already. If the original index url had credentials then
+        they will be included in the return value.
+
+        Returns None if no matching index was found, or if --no-index
+        was specified by the user.
+        """
+        if not url or not self.index_urls:
+            return None
+
+        url = remove_auth_from_url(url).rstrip("/") + "/"
+        parsed_url = urllib.parse.urlsplit(url)
+
+        candidates = []
+
+        for index in self.index_urls:
+            index = index.rstrip("/") + "/"
+            parsed_index = urllib.parse.urlsplit(remove_auth_from_url(index))
+            if parsed_url == parsed_index:
+                return index
+
+            if parsed_url.netloc != parsed_index.netloc:
+                continue
+
+            candidate = urllib.parse.urlsplit(index)
+            candidates.append(candidate)
+
+        if not candidates:
+            return None
+
+        candidates.sort(
+            reverse=True,
+            key=lambda candidate: len(
+                commonpath(
+                    [
+                        parsed_url.path,
+                        candidate.path,
+                    ]
+                )
+            ),
+        )
+
+        return urllib.parse.urlunsplit(candidates[0])
+
+    def _get_new_credentials(
+        self,
+        original_url: str,
+        *,
+        allow_netrc: bool = True,
+        allow_keyring: bool = False,
+    ) -> AuthInfo:
+        """Find and return credentials for the specified URL."""
+        # Split the credentials and netloc from the url.
+        url, netloc, url_user_password = split_auth_netloc_from_url(
+            original_url,
+        )
+
+        # Start with the credentials embedded in the url
+        username, password = url_user_password
+        if username is not None and password is not None:
+            logger.debug("Found credentials in url for %s", netloc)
+            return url_user_password
+
+        # Find a matching index url for this request
+        index_url = self._get_index_url(url)
+        if index_url:
+            # Split the credentials from the url.
+            index_info = split_auth_netloc_from_url(index_url)
+            if index_info:
+                index_url, _, index_url_user_password = index_info
+                logger.debug("Found index url %s", index_url)
+
+        # If an index URL was found, try its embedded credentials
+        if index_url and index_url_user_password[0] is not None:
+            username, password = index_url_user_password
+            if username is not None and password is not None:
+                logger.debug("Found credentials in index url for %s", netloc)
+                return index_url_user_password
+
+        # Get creds from netrc if we still don't have them
+        if allow_netrc:
+            netrc_auth = get_netrc_auth(original_url)
+            if netrc_auth:
+                logger.debug("Found credentials in netrc for %s", netloc)
+                return netrc_auth
+
+        # If we don't have a password and keyring is available, use it.
+        if allow_keyring:
+            # The index url is more specific than the netloc, so try it first
+            # fmt: off
+            kr_auth = (
+                self._get_keyring_auth(index_url, username) or
+                self._get_keyring_auth(netloc, username)
+            )
+            # fmt: on
+            if kr_auth:
+                logger.debug("Found credentials in keyring for %s", netloc)
+                return kr_auth
+
+        return username, password
+
+    def _get_url_and_credentials(
+        self, original_url: str
+    ) -> tuple[str, str | None, str | None]:
+        """Return the credentials to use for the provided URL.
+
+        If allowed, netrc and keyring may be used to obtain the
+        correct credentials.
+
+        Returns (url_without_credentials, username, password). Note
+        that even if the original URL contains credentials, this
+        function may return a different username and password.
+        """
+        url, netloc, _ = split_auth_netloc_from_url(original_url)
+
+        # Try to get credentials from original url
+        username, password = self._get_new_credentials(original_url)
+
+        # If credentials not found, use any stored credentials for this netloc.
+        # Do this if either the username or the password is missing.
+        # This accounts for the situation in which the user has specified
+        # the username in the index url, but the password comes from keyring.
+        if (username is None or password is None) and netloc in self.passwords:
+            un, pw = self.passwords[netloc]
+            # It is possible that the cached credentials are for a different username,
+            # in which case the cache should be ignored.
+            if username is None or username == un:
+                username, password = un, pw
+
+        if username is not None or password is not None:
+            # Convert the username and password if they're None, so that
+            # this netloc will show up as "cached" in the conditional above.
+            # Further, HTTPBasicAuth doesn't accept None, so it makes sense to
+            # cache the value that is going to be used.
+            username = username or ""
+            password = password or ""
+
+            # Store any acquired credentials.
+            self.passwords[netloc] = (username, password)
+
+        assert (
+            # Credentials were found
+            (username is not None and password is not None)
+            # Credentials were not found
+            or (username is None and password is None)
+        ), f"Could not load credentials from url: {original_url}"
+
+        return url, username, password
+
+    def __call__(self, req: PreparedRequest) -> PreparedRequest:
+        # Get credentials for this request
+        assert req.url is not None
+        url, username, password = self._get_url_and_credentials(req.url)
+
+        # Set the url of the request to the url without any credentials
+        req.url = url
+
+        if username is not None and password is not None:
+            # Send the basic auth with this request
+            req = HTTPBasicAuth(username, password)(req)
+
+        # Attach a hook to handle 401 responses
+        req.register_hook("response", self.handle_401)
+
+        return req
+
+    # Factored out to allow for easy patching in tests
+    def _prompt_for_password(self, netloc: str) -> tuple[str | None, str | None, bool]:
+        username = ask_input(f"User for {netloc}: ") if self.prompting else None
+        if not username:
+            return None, None, False
+        if self.use_keyring:
+            auth = self._get_keyring_auth(netloc, username)
+            if auth and auth[0] is not None and auth[1] is not None:
+                return auth[0], auth[1], False
+        password = ask_password("Password: ")
+        return username, password, True
+
+    # Factored out to allow for easy patching in tests
+    def _should_save_password_to_keyring(self) -> bool:
+        if (
+            not self.prompting
+            or not self.use_keyring
+            or not self.keyring_provider.has_keyring
+        ):
+            return False
+        return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y"
+
+    def handle_401(self, resp: Response, **kwargs: Any) -> Response:
+        # We only care about 401 responses, anything else we want to just
+        #   pass through the actual response
+        if resp.status_code != 401:
+            return resp
+
+        # Look for credentials for the (possibly redirected) URL. Credentials
+        # embedded in the URL -- e.g. carried in the ``Location`` of a
+        # cross-origin redirect, whose ``Authorization`` header requests strips
+        # -- are always honoured, since recovering them needs no user
+        # interaction. Keyring is only consulted when it is enabled, because it
+        # may require interaction and is therefore disabled under --no-input.
+        username, password = self._get_new_credentials(
+            resp.url,
+            allow_netrc=False,
+            allow_keyring=self.use_keyring,
+        )
+
+        # We are not able to prompt the user so simply return the response
+        if not self.prompting and not username and not password:
+            return resp
+
+        parsed = urllib.parse.urlparse(resp.url)
+
+        # Prompt the user for a new username and password
+        save = False
+        if not username and not password:
+            username, password, save = self._prompt_for_password(parsed.netloc)
+
+        # Store the new username and password to use for future requests
+        self._credentials_to_save = None
+        if username is not None and password is not None:
+            self.passwords[parsed.netloc] = (username, password)
+
+            # Prompt to save the password to keyring
+            if save and self._should_save_password_to_keyring():
+                self._credentials_to_save = Credentials(
+                    url=parsed.netloc,
+                    username=username,
+                    password=password,
+                )
+
+        # Consume content and release the original connection to allow our new
+        #   request to reuse the same one.
+        # The result of the assignment isn't used, it's just needed to consume
+        # the content.
+        _ = resp.content
+        resp.raw.release_conn()
+
+        # Add our new username and password to the request
+        req = HTTPBasicAuth(username or "", password or "")(resp.request)
+        req.register_hook("response", self.warn_on_401)
+
+        # On successful request, save the credentials that were used to
+        # keyring. (Note that if the user responded "no" above, this member
+        # is not set and nothing will be saved.)
+        if self._credentials_to_save:
+            req.register_hook("response", self.save_credentials)
+
+        # Send our new request
+        new_resp = resp.connection.send(req, **kwargs)
+        new_resp.history.append(resp)
+
+        return new_resp
+
+    def warn_on_401(self, resp: Response, **kwargs: Any) -> None:
+        """Response callback to warn about incorrect credentials."""
+        if resp.status_code == 401:
+            logger.warning(
+                "401 Error, Credentials not correct for %s",
+                resp.request.url,
+            )
+
+    def save_credentials(self, resp: Response, **kwargs: Any) -> None:
+        """Response callback to save credentials on success."""
+        assert (
+            self.keyring_provider.has_keyring
+        ), "should never reach here without keyring"
+
+        creds = self._credentials_to_save
+        self._credentials_to_save = None
+        if creds and resp.status_code < 400:
+            try:
+                logger.info("Saving credentials to keyring")
+                self.keyring_provider.save_auth_info(
+                    creds.url, creds.username, creds.password
+                )
+            except Exception:
+                logger.exception("Failed to save credentials")
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/cache.py b/venv/lib/python3.12/site-packages/pip/_internal/network/cache.py
new file mode 100644
index 0000000..bff9707
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/network/cache.py
@@ -0,0 +1,128 @@
+"""HTTP cache implementation."""
+
+from __future__ import annotations
+
+import os
+import shutil
+from collections.abc import Callable, Generator
+from contextlib import contextmanager
+from datetime import datetime
+from typing import Any, BinaryIO
+
+from pip._vendor.cachecontrol.cache import SeparateBodyBaseCache
+from pip._vendor.cachecontrol.caches import SeparateBodyFileCache
+from pip._vendor.requests.models import Response
+
+from pip._internal.utils.filesystem import (
+    adjacent_tmp_file,
+    copy_directory_permissions,
+    replace,
+)
+from pip._internal.utils.misc import ensure_dir
+
+
+def is_from_cache(response: Response) -> bool:
+    return getattr(response, "from_cache", False)
+
+
+@contextmanager
+def suppressed_cache_errors() -> Generator[None, None, None]:
+    """If we can't access the cache then we can just skip caching and process
+    requests as if caching wasn't enabled.
+    """
+    try:
+        yield
+    except OSError:
+        pass
+
+
+class SafeFileCache(SeparateBodyBaseCache):
+    """
+    A file based cache which is safe to use even when the target directory may
+    not be accessible or writable.
+
+    There is a race condition when two processes try to write and/or read the
+    same entry at the same time, since each entry consists of two separate
+    files (https://github.com/psf/cachecontrol/issues/324).  We therefore have
+    additional logic that makes sure that both files to be present before
+    returning an entry; this fixes the read side of the race condition.
+
+    For the write side, we assume that the server will only ever return the
+    same data for the same URL, which ought to be the case for files pip is
+    downloading.  PyPI does not have a mechanism to swap out a wheel for
+    another wheel, for example.  If this assumption is not true, the
+    CacheControl issue will need to be fixed.
+    """
+
+    def __init__(self, directory: str) -> None:
+        assert directory is not None, "Cache directory must not be None."
+        super().__init__()
+        self.directory = directory
+
+    def _get_cache_path(self, name: str) -> str:
+        # From cachecontrol.caches.file_cache.FileCache._fn, brought into our
+        # class for backwards-compatibility and to avoid using a non-public
+        # method.
+        hashed = SeparateBodyFileCache.encode(name)
+        parts = list(hashed[:5]) + [hashed]
+        return os.path.join(self.directory, *parts)
+
+    def get(self, key: str) -> bytes | None:
+        # The cache entry is only valid if both metadata and body exist.
+        metadata_path = self._get_cache_path(key)
+        body_path = metadata_path + ".body"
+        if not (os.path.exists(metadata_path) and os.path.exists(body_path)):
+            return None
+        with suppressed_cache_errors():
+            with open(metadata_path, "rb") as f:
+                return f.read()
+
+    def _write_to_file(self, path: str, writer_func: Callable[[BinaryIO], Any]) -> None:
+        """Common file writing logic with proper permissions and atomic replacement."""
+        with suppressed_cache_errors():
+            ensure_dir(os.path.dirname(path))
+
+            with adjacent_tmp_file(path) as f:
+                writer_func(f)
+                # Inherit the read/write permissions of the cache directory
+                # to enable multi-user cache use-cases.
+                copy_directory_permissions(self.directory, f)
+
+            replace(f.name, path)
+
+    def _write(self, path: str, data: bytes) -> None:
+        self._write_to_file(path, lambda f: f.write(data))
+
+    def _write_from_io(self, path: str, source_file: BinaryIO) -> None:
+        self._write_to_file(path, lambda f: shutil.copyfileobj(source_file, f))
+
+    def set(
+        self, key: str, value: bytes, expires: int | datetime | None = None
+    ) -> None:
+        path = self._get_cache_path(key)
+        self._write(path, value)
+
+    def delete(self, key: str) -> None:
+        path = self._get_cache_path(key)
+        with suppressed_cache_errors():
+            os.remove(path)
+        with suppressed_cache_errors():
+            os.remove(path + ".body")
+
+    def get_body(self, key: str) -> BinaryIO | None:
+        # The cache entry is only valid if both metadata and body exist.
+        metadata_path = self._get_cache_path(key)
+        body_path = metadata_path + ".body"
+        if not (os.path.exists(metadata_path) and os.path.exists(body_path)):
+            return None
+        with suppressed_cache_errors():
+            return open(body_path, "rb")
+
+    def set_body(self, key: str, body: bytes) -> None:
+        path = self._get_cache_path(key) + ".body"
+        self._write(path, body)
+
+    def set_body_from_io(self, key: str, body_file: BinaryIO) -> None:
+        """Set the body of the cache entry from a file object."""
+        path = self._get_cache_path(key) + ".body"
+        self._write_from_io(path, body_file)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/download.py b/venv/lib/python3.12/site-packages/pip/_internal/network/download.py
new file mode 100644
index 0000000..521abb7
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/network/download.py
@@ -0,0 +1,387 @@
+"""Download files with progress indicators."""
+
+from __future__ import annotations
+
+import email.message
+import logging
+import mimetypes
+import os
+from collections.abc import Iterable, Mapping
+from dataclasses import dataclass
+from http import HTTPStatus
+from typing import BinaryIO
+
+from pip._vendor.requests import PreparedRequest
+from pip._vendor.requests.models import Response
+from pip._vendor.urllib3 import HTTPResponse as URLlib3Response
+from pip._vendor.urllib3._collections import HTTPHeaderDict
+from pip._vendor.urllib3.exceptions import ProtocolError, ReadTimeoutError
+
+from pip._internal.cli.progress_bars import BarType, get_download_progress_renderer
+from pip._internal.exceptions import (
+    ConnectionFailedError,
+    ConnectionTimeoutError,
+    IncompleteDownloadError,
+    NetworkConnectionError,
+    ProxyConnectionError,
+    SSLVerificationError,
+)
+from pip._internal.models.link import (
+    Link,
+    PathComponent,
+    as_path_component,
+    join_within_directory,
+)
+from pip._internal.network.cache import SafeFileCache, is_from_cache
+from pip._internal.network.session import CacheControlAdapter, PipSession
+from pip._internal.network.utils import HEADERS, raise_for_status, response_chunks
+from pip._internal.utils.misc import format_size, redact_auth_from_url, splitext
+
+logger = logging.getLogger(__name__)
+
+
+def _get_http_response_size(resp: Response) -> int | None:
+    try:
+        size = int(resp.headers["content-length"])
+    except (ValueError, KeyError, TypeError):
+        return None
+    # A negative length would make _FileDownload.is_incomplete() report a
+    # truncated download as complete, so treat it as unknown instead.
+    if size < 0:
+        return None
+    return size
+
+
+def _get_http_response_etag_or_last_modified(resp: Response) -> str | None:
+    """
+    Return either the ETag or Last-Modified header (or None if neither exists).
+    The return value can be used in an If-Range header.
+    """
+    return resp.headers.get("etag", resp.headers.get("last-modified"))
+
+
+def _log_download(
+    resp: Response,
+    link: Link,
+    progress_bar: BarType,
+    total_length: int | None,
+    range_start: int | None = 0,
+) -> Iterable[bytes]:
+    if logger.getEffectiveLevel() > logging.INFO:
+        url = link.url_without_fragment
+    else:
+        url = link.show_url
+
+    logged_url = redact_auth_from_url(url)
+
+    if total_length:
+        if range_start:
+            logged_url = (
+                f"{logged_url} ({format_size(range_start)}/{format_size(total_length)})"
+            )
+        else:
+            logged_url = f"{logged_url} ({format_size(total_length)})"
+
+    if is_from_cache(resp):
+        logger.info("Using cached %s", logged_url)
+    elif range_start:
+        logger.info("Resuming download %s", logged_url)
+    else:
+        logger.info("Downloading %s", logged_url)
+
+    if logger.getEffectiveLevel() > logging.INFO:
+        show_progress = False
+    elif is_from_cache(resp):
+        show_progress = False
+    elif not total_length:
+        show_progress = True
+    elif total_length > (512 * 1024):
+        show_progress = True
+    else:
+        show_progress = False
+
+    chunks = response_chunks(resp)
+
+    if not show_progress:
+        return chunks
+
+    renderer = get_download_progress_renderer(
+        bar_type=progress_bar, size=total_length, initial_progress=range_start
+    )
+    return renderer(chunks)
+
+
+def sanitize_content_filename(filename: str) -> str:
+    """
+    Sanitize the "filename" value from a Content-Disposition header.
+    """
+    return os.path.basename(filename)
+
+
+def parse_content_disposition(content_disposition: str, default_filename: str) -> str:
+    """
+    Parse the "filename" value from a Content-Disposition header, and
+    return the default filename if the result is empty.
+    """
+    m = email.message.Message()
+    m["content-type"] = content_disposition
+    filename = m.get_param("filename")
+    if filename:
+        # We need to sanitize the filename to prevent directory traversal
+        # in case the filename contains ".." path parts.
+        filename = sanitize_content_filename(str(filename))
+    return filename or default_filename
+
+
+def _get_http_response_filename(resp: Response, link: Link) -> PathComponent:
+    """Get an ideal filename from the given HTTP response, falling back to
+    the link filename if not provided.
+
+    The result is validated as a single path component, so it can be joined onto
+    a download directory without escaping it.
+    """
+    filename: str = link.filename  # fallback
+    # Have a look at the Content-Disposition header for a better guess
+    content_disposition = resp.headers.get("content-disposition")
+    if content_disposition:
+        filename = parse_content_disposition(content_disposition, filename)
+    ext: str | None = splitext(filename)[1]
+    if not ext:
+        ext = mimetypes.guess_extension(resp.headers.get("content-type", ""))
+        if ext:
+            filename += ext
+    if not ext and link.url != resp.url:
+        ext = os.path.splitext(resp.url)[1]
+        if ext:
+            filename += ext
+    return as_path_component(filename)
+
+
+@dataclass
+class _FileDownload:
+    """Stores the state of a single link download."""
+
+    link: Link
+    output_file: BinaryIO
+    size: int | None
+    bytes_received: int = 0
+    reattempts: int = 0
+
+    def is_incomplete(self) -> bool:
+        return bool(self.size is not None and self.bytes_received < self.size)
+
+    def write_chunk(self, data: bytes) -> None:
+        self.bytes_received += len(data)
+        self.output_file.write(data)
+
+    def reset_file(self) -> None:
+        """Delete any saved data and reset progress to zero."""
+        self.output_file.seek(0)
+        self.output_file.truncate()
+        self.bytes_received = 0
+
+
+class Downloader:
+    def __init__(
+        self,
+        session: PipSession,
+        progress_bar: BarType,
+    ) -> None:
+        self._session = session
+        self._progress_bar = progress_bar
+        self._resume_retries = session.resume_retries
+        assert (
+            self._resume_retries >= 0
+        ), "Number of max resume retries must be bigger or equal to zero"
+
+    def batch(
+        self, links: Iterable[Link], location: str
+    ) -> Iterable[tuple[Link, tuple[str, str]]]:
+        """Convenience method to download multiple links."""
+        for link in links:
+            filepath, content_type = self(link, location)
+            yield link, (filepath, content_type)
+
+    def __call__(self, link: Link, location: str) -> tuple[str, str]:
+        """Download a link and save it under location."""
+        resp = self._http_get(link)
+        download_size = _get_http_response_size(resp)
+
+        filepath = join_within_directory(
+            location, _get_http_response_filename(resp, link)
+        )
+        with open(filepath, "wb") as content_file:
+            download = _FileDownload(link, content_file, download_size)
+            self._process_response(download, resp)
+            if download.is_incomplete():
+                self._attempt_resumes_or_redownloads(download, resp)
+
+        content_type = resp.headers.get("Content-Type", "")
+        return filepath, content_type
+
+    def _process_response(self, download: _FileDownload, resp: Response) -> None:
+        """Download and save chunks from a response."""
+        chunks = _log_download(
+            resp,
+            download.link,
+            self._progress_bar,
+            download.size,
+            range_start=download.bytes_received,
+        )
+        try:
+            for chunk in chunks:
+                download.write_chunk(chunk)
+        except (ReadTimeoutError, ProtocolError) as e:
+            # If the download size is not known, then give up downloading the file.
+            if download.size is None:
+                raise e
+
+            logger.warning("Connection interrupted while downloading.")
+
+    def _attempt_resumes_or_redownloads(
+        self, download: _FileDownload, first_resp: Response
+    ) -> None:
+        """Attempt to resume/restart the download if connection was dropped."""
+
+        while download.reattempts < self._resume_retries and download.is_incomplete():
+            assert download.size is not None
+            download.reattempts += 1
+            logger.warning(
+                "Attempting to resume incomplete download (%s/%s, attempt %d)",
+                format_size(download.bytes_received),
+                format_size(download.size),
+                download.reattempts,
+            )
+
+            try:
+                resume_resp = self._http_get_resume(download, should_match=first_resp)
+                # Fallback: if the server responded with 200 (i.e., the file has
+                # since been modified or range requests are unsupported) or any
+                # other unexpected status, restart the download from the beginning.
+                must_restart = resume_resp.status_code != HTTPStatus.PARTIAL_CONTENT
+                if must_restart:
+                    download.reset_file()
+                    download.size = _get_http_response_size(resume_resp)
+                    first_resp = resume_resp
+                else:
+                    # If the resume request starts at the wrong location, fail
+                    # outright since the server is misbehaving.
+                    content_range = resume_resp.headers.get("Content-Range", "")
+                    resumed_at = content_range.lower().partition("bytes ")[2]
+                    resumed_at = resumed_at.partition("-")[0]
+                    if resumed_at and resumed_at != str(download.bytes_received):
+                        raise IncompleteDownloadError(download)
+
+                self._process_response(download, resume_resp)
+            except (
+                ConnectionFailedError,
+                ConnectionTimeoutError,
+                ProxyConnectionError,
+                SSLVerificationError,
+                ReadTimeoutError,
+                ProtocolError,
+                OSError,
+            ):
+                # The error handling here is tricky, a few notes:
+                #
+                # - The diagnostic connection errors are raised by our custom
+                #   requests.request() connection exception handler.
+                # - ProtocolError is raised by urllib3 when the returned data length
+                #   doesn't match the response Content-Length.
+                # - ReadTimeoutError / ProtocolError come straight from urllib3 (via
+                #   process_response) and aren't caught by our connection exception
+                #   handler since they occur while *streaming* a response.
+                continue
+
+        # No more resume attempts. Raise an error if the download is still incomplete.
+        if download.is_incomplete():
+            os.remove(download.output_file.name)
+            raise IncompleteDownloadError(download)
+
+        # If we successfully completed the download via resume, manually cache it
+        # as a complete response to enable future caching
+        if download.reattempts > 0:
+            self._cache_resumed_download(download, first_resp)
+
+    def _cache_resumed_download(
+        self, download: _FileDownload, original_response: Response
+    ) -> None:
+        """
+        Manually cache a file that was successfully downloaded via resume retries.
+
+        cachecontrol doesn't cache 206 (Partial Content) responses, since they
+        are not complete files. This method manually adds the final file to the
+        cache as though it was downloaded in a single request, so that future
+        requests can use the cache.
+        """
+        url = download.link.url_without_fragment
+        adapter = self._session.get_adapter(url)
+
+        # Check if the adapter is the CacheControlAdapter (i.e. caching is enabled)
+        if not isinstance(adapter, CacheControlAdapter):
+            logger.debug(
+                "Skipping resume download caching: no cache controller for %s", url
+            )
+            return
+
+        # Check SafeFileCache is being used
+        assert isinstance(
+            adapter.cache, SafeFileCache
+        ), "separate body cache not in use!"
+
+        synthetic_request = PreparedRequest()
+        synthetic_request.prepare(method="GET", url=url, headers={})
+
+        synthetic_response_headers = HTTPHeaderDict()
+        for key, value in original_response.headers.items():
+            if key.lower() not in ["content-range", "content-length"]:
+                synthetic_response_headers[key] = value
+        synthetic_response_headers["content-length"] = str(download.size)
+
+        synthetic_response = URLlib3Response(
+            body="",
+            headers=synthetic_response_headers,
+            status=200,
+            preload_content=False,
+        )
+
+        # Save metadata and then stream the file contents to cache.
+        cache_url = adapter.controller.cache_url(url)
+        metadata_blob = adapter.controller.serializer.dumps(
+            synthetic_request, synthetic_response, b""
+        )
+        adapter.cache.set(cache_url, metadata_blob)
+        download.output_file.flush()
+        with open(download.output_file.name, "rb") as f:
+            adapter.cache.set_body_from_io(cache_url, f)
+
+        logger.debug(
+            "Cached resumed download as complete response for future use: %s", url
+        )
+
+    def _http_get_resume(
+        self, download: _FileDownload, should_match: Response
+    ) -> Response:
+        """Issue a HTTP range request to resume the download."""
+        # To better understand the download resumption logic, see the mdn web docs:
+        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Range_requests
+        headers = HEADERS.copy()
+        headers["Range"] = f"bytes={download.bytes_received}-"
+        # If possible, use a conditional range request to avoid corrupted
+        # downloads caused by the remote file changing in-between.
+        if identifier := _get_http_response_etag_or_last_modified(should_match):
+            headers["If-Range"] = identifier
+        return self._http_get(download.link, headers)
+
+    def _http_get(self, link: Link, headers: Mapping[str, str] = HEADERS) -> Response:
+        target_url = link.url_without_fragment
+        try:
+            resp = self._session.get(target_url, headers=headers, stream=True)
+            raise_for_status(resp)
+        except NetworkConnectionError as e:
+            assert e.response is not None
+            logger.critical(
+                "HTTP error %s while getting %s", e.response.status_code, link
+            )
+            raise
+        return resp
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/lazy_wheel.py b/venv/lib/python3.12/site-packages/pip/_internal/network/lazy_wheel.py
new file mode 100644
index 0000000..0039833
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/network/lazy_wheel.py
@@ -0,0 +1,215 @@
+"""Lazy ZIP over HTTP"""
+
+from __future__ import annotations
+
+__all__ = ["HTTPRangeRequestUnsupported", "dist_from_wheel_url"]
+
+from bisect import bisect_left, bisect_right
+from collections.abc import Generator
+from contextlib import contextmanager
+from tempfile import NamedTemporaryFile
+from typing import Any
+from zipfile import BadZipFile, ZipFile
+
+from pip._vendor.packaging.utils import NormalizedName
+from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response
+
+from pip._internal.metadata import BaseDistribution, MemoryWheel, get_wheel_distribution
+from pip._internal.network.session import PipSession
+from pip._internal.network.utils import HEADERS, raise_for_status, response_chunks
+
+
+class HTTPRangeRequestUnsupported(Exception):
+    pass
+
+
+def dist_from_wheel_url(
+    name: NormalizedName, url: str, session: PipSession
+) -> BaseDistribution:
+    """Return a distribution object from the given wheel URL.
+
+    This uses HTTP range requests to only fetch the portion of the wheel
+    containing metadata, just enough for the object to be constructed.
+    If such requests are not supported, HTTPRangeRequestUnsupported
+    is raised.
+    """
+    with LazyZipOverHTTP(url, session) as zf:
+        # For read-only ZIP files, ZipFile only needs methods read,
+        # seek, seekable and tell, not the whole IO protocol.
+        wheel = MemoryWheel(zf.name, zf)  # type: ignore
+        # After context manager exit, wheel.name
+        # is an invalid file by intention.
+        return get_wheel_distribution(wheel, name)
+
+
+class LazyZipOverHTTP:
+    """File-like object mapped to a ZIP file over HTTP.
+
+    This uses HTTP range requests to lazily fetch the file's content,
+    which is supposed to be fed to ZipFile.  If such requests are not
+    supported by the server, raise HTTPRangeRequestUnsupported
+    during initialization.
+    """
+
+    def __init__(
+        self, url: str, session: PipSession, chunk_size: int = CONTENT_CHUNK_SIZE
+    ) -> None:
+        head = session.head(url, headers=HEADERS)
+        raise_for_status(head)
+        assert head.status_code == 200
+        self._session, self._url, self._chunk_size = session, url, chunk_size
+        self._length = int(head.headers["Content-Length"])
+        self._file = NamedTemporaryFile()
+        self.truncate(self._length)
+        self._left: list[int] = []
+        self._right: list[int] = []
+        if "bytes" not in head.headers.get("Accept-Ranges", "none"):
+            raise HTTPRangeRequestUnsupported("range request is not supported")
+        self._check_zip()
+
+    @property
+    def mode(self) -> str:
+        """Opening mode, which is always rb."""
+        return "rb"
+
+    @property
+    def name(self) -> str:
+        """Path to the underlying file."""
+        return self._file.name
+
+    def seekable(self) -> bool:
+        """Return whether random access is supported, which is True."""
+        return True
+
+    def close(self) -> None:
+        """Close the file."""
+        self._file.close()
+
+    @property
+    def closed(self) -> bool:
+        """Whether the file is closed."""
+        return self._file.closed
+
+    def read(self, size: int = -1) -> bytes:
+        """Read up to size bytes from the object and return them.
+
+        As a convenience, if size is unspecified or -1,
+        all bytes until EOF are returned.  Fewer than
+        size bytes may be returned if EOF is reached.
+        """
+        download_size = max(size, self._chunk_size)
+        start, length = self.tell(), self._length
+        stop = length if size < 0 else min(start + download_size, length)
+        start = max(0, stop - download_size)
+        self._download(start, stop - 1)
+        return self._file.read(size)
+
+    def readable(self) -> bool:
+        """Return whether the file is readable, which is True."""
+        return True
+
+    def seek(self, offset: int, whence: int = 0) -> int:
+        """Change stream position and return the new absolute position.
+
+        Seek to offset relative position indicated by whence:
+        * 0: Start of stream (the default).  pos should be >= 0;
+        * 1: Current position - pos may be negative;
+        * 2: End of stream - pos usually negative.
+        """
+        return self._file.seek(offset, whence)
+
+    def tell(self) -> int:
+        """Return the current position."""
+        return self._file.tell()
+
+    def truncate(self, size: int | None = None) -> int:
+        """Resize the stream to the given size in bytes.
+
+        If size is unspecified resize to the current position.
+        The current stream position isn't changed.
+
+        Return the new file size.
+        """
+        return self._file.truncate(size)
+
+    def writable(self) -> bool:
+        """Return False."""
+        return False
+
+    def __enter__(self) -> LazyZipOverHTTP:
+        self._file.__enter__()
+        return self
+
+    def __exit__(self, *exc: Any) -> None:
+        self._file.__exit__(*exc)
+
+    @contextmanager
+    def _stay(self) -> Generator[None, None, None]:
+        """Return a context manager keeping the position.
+
+        At the end of the block, seek back to original position.
+        """
+        pos = self.tell()
+        try:
+            yield
+        finally:
+            self.seek(pos)
+
+    def _check_zip(self) -> None:
+        """Check and download until the file is a valid ZIP."""
+        end = self._length - 1
+        for start in reversed(range(0, end, self._chunk_size)):
+            self._download(start, end)
+            with self._stay():
+                try:
+                    # For read-only ZIP files, ZipFile only needs
+                    # methods read, seek, seekable and tell.
+                    ZipFile(self)
+                except BadZipFile:
+                    pass
+                else:
+                    break
+
+    def _stream_response(
+        self, start: int, end: int, base_headers: dict[str, str] = HEADERS
+    ) -> Response:
+        """Return HTTP response to a range request from start to end."""
+        headers = base_headers.copy()
+        headers["Range"] = f"bytes={start}-{end}"
+        # TODO: Get range requests to be correctly cached
+        headers["Cache-Control"] = "no-cache"
+        return self._session.get(self._url, headers=headers, stream=True)
+
+    def _merge(
+        self, start: int, end: int, left: int, right: int
+    ) -> Generator[tuple[int, int], None, None]:
+        """Return a generator of intervals to be fetched.
+
+        Args:
+            start (int): Start of needed interval
+            end (int): End of needed interval
+            left (int): Index of first overlapping downloaded data
+            right (int): Index after last overlapping downloaded data
+        """
+        lslice, rslice = self._left[left:right], self._right[left:right]
+        i = start = min([start] + lslice[:1])
+        end = max([end] + rslice[-1:])
+        for j, k in zip(lslice, rslice):
+            if j > i:
+                yield i, j - 1
+            i = k + 1
+        if i <= end:
+            yield i, end
+        self._left[left:right], self._right[left:right] = [start], [end]
+
+    def _download(self, start: int, end: int) -> None:
+        """Download bytes from start to end inclusively."""
+        with self._stay():
+            left = bisect_left(self._right, start)
+            right = bisect_right(self._left, end)
+            for start, end in self._merge(start, end, left, right):
+                response = self._stream_response(start, end)
+                response.raise_for_status()
+                self.seek(start)
+                for chunk in response_chunks(response, self._chunk_size):
+                    self._file.write(chunk)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/session.py b/venv/lib/python3.12/site-packages/pip/_internal/network/session.py
new file mode 100644
index 0000000..5ddbb2a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/network/session.py
@@ -0,0 +1,532 @@
+"""PipSession and supporting code, containing all pip-specific
+network request configuration and behavior.
+"""
+
+from __future__ import annotations
+
+import email.utils
+import functools
+import io
+import ipaddress
+import json
+import logging
+import mimetypes
+import os
+import platform
+import shutil
+import subprocess
+import sys
+import urllib.parse
+import warnings
+from collections.abc import Generator, Mapping, Sequence
+from typing import (
+    TYPE_CHECKING,
+    Any,
+)
+
+from pip._vendor import requests, urllib3
+from pip._vendor.cachecontrol import CacheControlAdapter as _BaseCacheControlAdapter
+from pip._vendor.requests.adapters import DEFAULT_POOLBLOCK, BaseAdapter
+from pip._vendor.requests.adapters import HTTPAdapter as _BaseHTTPAdapter
+from pip._vendor.requests.models import PreparedRequest, Response
+from pip._vendor.requests.structures import CaseInsensitiveDict
+from pip._vendor.urllib3.connectionpool import ConnectionPool
+from pip._vendor.urllib3.exceptions import InsecureRequestWarning
+
+from pip import __version__
+from pip._internal.exceptions import SSLMissingError
+from pip._internal.metadata import get_default_environment
+from pip._internal.models.link import Link
+from pip._internal.network.auth import MultiDomainBasicAuth
+from pip._internal.network.cache import SafeFileCache
+from pip._internal.network.utils import raise_connection_error
+
+# Import ssl from compat so the initial import occurs in only one place.
+from pip._internal.utils.compat import has_tls
+from pip._internal.utils.glibc import libc_ver
+from pip._internal.utils.misc import (
+    build_url_from_netloc,
+    looks_like_ci,
+    parse_netloc,
+    redact_auth_from_url,
+)
+from pip._internal.utils.urls import url_to_path
+
+if TYPE_CHECKING:
+    from ssl import SSLContext
+
+    from pip._vendor.urllib3 import ProxyManager
+
+
+logger = logging.getLogger(__name__)
+
+SecureOrigin = tuple[str, str, int | str | None]
+
+
+# Ignore warning raised when using --trusted-host.
+warnings.filterwarnings("ignore", category=InsecureRequestWarning)
+
+
+SECURE_ORIGINS: list[SecureOrigin] = [
+    # protocol, hostname, port
+    # Taken from Chrome's list of secure origins (See: http://bit.ly/1qrySKC)
+    ("https", "*", "*"),
+    ("*", "localhost", "*"),
+    ("*", "127.0.0.0/8", "*"),
+    ("*", "::1/128", "*"),
+    ("file", "*", None),
+    # ssh is always secure.
+    ("ssh", "*", "*"),
+]
+
+
+@functools.lru_cache(maxsize=1)
+def user_agent() -> str:
+    """
+    Return a string representing the user agent.
+    """
+    data: dict[str, Any] = {
+        "installer": {"name": "pip", "version": __version__},
+        "python": platform.python_version(),
+        "implementation": {
+            "name": platform.python_implementation(),
+        },
+    }
+
+    if data["implementation"]["name"] == "CPython":
+        data["implementation"]["version"] = platform.python_version()
+    elif data["implementation"]["name"] == "PyPy":
+        pypy_version_info = sys.pypy_version_info  # type: ignore
+        if pypy_version_info.releaselevel == "final":
+            pypy_version_info = pypy_version_info[:3]
+        data["implementation"]["version"] = ".".join(
+            [str(x) for x in pypy_version_info]
+        )
+    elif data["implementation"]["name"] == "Jython":
+        # Complete Guess
+        data["implementation"]["version"] = platform.python_version()
+    elif data["implementation"]["name"] == "IronPython":
+        # Complete Guess
+        data["implementation"]["version"] = platform.python_version()
+
+    if sys.platform.startswith("linux"):
+        from pip._vendor import distro
+
+        linux_distribution = distro.name(), distro.version(), distro.codename()
+        distro_infos: dict[str, Any] = dict(
+            filter(
+                lambda x: x[1],
+                zip(["name", "version", "id"], linux_distribution),
+            )
+        )
+        libc = dict(
+            filter(
+                lambda x: x[1],
+                zip(["lib", "version"], libc_ver()),
+            )
+        )
+        if libc:
+            distro_infos["libc"] = libc
+        if distro_infos:
+            data["distro"] = distro_infos
+
+    if sys.platform.startswith("darwin") and platform.mac_ver()[0]:
+        data["distro"] = {"name": "macOS", "version": platform.mac_ver()[0]}
+
+    if platform.system():
+        data.setdefault("system", {})["name"] = platform.system()
+
+    if platform.release():
+        data.setdefault("system", {})["release"] = platform.release()
+
+    if platform.machine():
+        data["cpu"] = platform.machine()
+
+    if has_tls():
+        import _ssl as ssl
+
+        data["openssl_version"] = ssl.OPENSSL_VERSION
+
+    setuptools_dist = get_default_environment().get_distribution("setuptools")
+    if setuptools_dist is not None:
+        data["setuptools_version"] = str(setuptools_dist.version)
+
+    if shutil.which("rustc") is not None:
+        # If for any reason `rustc --version` fails, silently ignore it
+        try:
+            rustc_output = subprocess.check_output(
+                ["rustc", "--version"], stderr=subprocess.STDOUT, timeout=0.5
+            )
+        except Exception:
+            pass
+        else:
+            if rustc_output.startswith(b"rustc "):
+                # The format of `rustc --version` is:
+                # `b'rustc 1.52.1 (9bc8c42bb 2021-05-09)\n'`
+                # We extract just the middle (1.52.1) part
+                data["rustc_version"] = rustc_output.split(b" ")[1].decode()
+
+    # Use None rather than False so as not to give the impression that
+    # pip knows it is not being run under CI.  Rather, it is a null or
+    # inconclusive result.  Also, we include some value rather than no
+    # value to make it easier to know that the check has been run.
+    data["ci"] = True if looks_like_ci() else None
+
+    user_data = os.environ.get("PIP_USER_AGENT_USER_DATA")
+    if user_data is not None:
+        data["user_data"] = user_data
+
+    return "{data[installer][name]}/{data[installer][version]} {json}".format(
+        data=data,
+        json=json.dumps(data, separators=(",", ":"), sort_keys=True),
+    )
+
+
+class LocalFSAdapter(BaseAdapter):
+    def send(
+        self,
+        request: PreparedRequest,
+        stream: bool = False,
+        timeout: float | tuple[float | None, float | None] | None = None,
+        verify: bool | str = True,
+        cert: bytes | str | tuple[bytes | str, bytes | str] | None = None,
+        proxies: Mapping[str, str] | None = None,
+    ) -> Response:
+        assert request.url is not None
+        pathname = url_to_path(request.url)
+
+        resp = Response()
+        resp.status_code = 200
+        resp.url = request.url
+
+        try:
+            stats = os.stat(pathname)
+        except OSError as exc:
+            # format the exception raised as a io.BytesIO object,
+            # to return a better error message:
+            resp.status_code = 404
+            resp.reason = type(exc).__name__
+            resp.raw = io.BytesIO(f"{resp.reason}: {exc}".encode())
+        else:
+            modified = email.utils.formatdate(stats.st_mtime, usegmt=True)
+            content_type = mimetypes.guess_type(pathname)[0] or "text/plain"
+            resp.headers = CaseInsensitiveDict(
+                {
+                    "Content-Type": content_type,
+                    "Content-Length": str(stats.st_size),
+                    "Last-Modified": modified,
+                }
+            )
+
+            resp.raw = open(pathname, "rb")
+            resp.close = resp.raw.close  # type: ignore[method-assign]
+
+        return resp
+
+    def close(self) -> None:
+        pass
+
+
+class _SSLContextAdapterMixin:
+    """Mixin to add the ``ssl_context`` constructor argument to HTTP adapters.
+
+    The additional argument is forwarded directly to the pool manager. This allows us
+    to dynamically decide what SSL store to use at runtime, which is used to implement
+    the optional ``truststore`` backend.
+    """
+
+    def __init__(
+        self,
+        *,
+        ssl_context: SSLContext | None = None,
+        **kwargs: Any,
+    ) -> None:
+        self._ssl_context = ssl_context
+        super().__init__(**kwargs)
+
+    def init_poolmanager(
+        self,
+        connections: int,
+        maxsize: int,
+        block: bool = DEFAULT_POOLBLOCK,
+        **pool_kwargs: Any,
+    ) -> None:
+        if self._ssl_context is not None:
+            pool_kwargs.setdefault("ssl_context", self._ssl_context)
+        super().init_poolmanager(  # type: ignore[misc]
+            connections=connections,
+            maxsize=maxsize,
+            block=block,
+            **pool_kwargs,
+        )
+
+    def proxy_manager_for(self, proxy: str, **proxy_kwargs: Any) -> ProxyManager:
+        # Proxy manager replaces the pool manager, so inject our SSL
+        # context here too. https://github.com/pypa/pip/issues/13288
+        if self._ssl_context is not None:
+            proxy_kwargs.setdefault("ssl_context", self._ssl_context)
+            # For HTTPS proxies, urllib3 also opens a separate TLS connection
+            # to the proxy itself (before tunnelling to the destination) and
+            # uses "proxy_ssl_context" for that handshake.
+            # https://github.com/pypa/pip/issues/13465
+            proxy_kwargs.setdefault("proxy_ssl_context", self._ssl_context)
+        return super().proxy_manager_for(proxy, **proxy_kwargs)  # type: ignore[misc]
+
+
+class HTTPAdapter(_SSLContextAdapterMixin, _BaseHTTPAdapter):
+    pass
+
+
+class CacheControlAdapter(_SSLContextAdapterMixin, _BaseCacheControlAdapter):
+    pass
+
+
+class InsecureHTTPAdapter(HTTPAdapter):
+    def cert_verify(
+        self,
+        conn: ConnectionPool,
+        url: str,
+        verify: bool | str,
+        cert: str | tuple[str, str] | None,
+    ) -> None:
+        super().cert_verify(conn=conn, url=url, verify=False, cert=cert)
+
+
+class InsecureCacheControlAdapter(CacheControlAdapter):
+    def cert_verify(
+        self,
+        conn: ConnectionPool,
+        url: str,
+        verify: bool | str,
+        cert: str | tuple[str, str] | None,
+    ) -> None:
+        super().cert_verify(conn=conn, url=url, verify=False, cert=cert)
+
+
+class PipSession(requests.Session):
+    timeout: int | None = None
+
+    def __init__(
+        self,
+        *args: Any,
+        retries: int = 0,
+        resume_retries: int = 0,
+        cache: str | None = None,
+        trusted_hosts: Sequence[str] = (),
+        index_urls: list[str] | None = None,
+        ssl_context: SSLContext | None = None,
+        refresh_package: set[str] | None = None,
+        **kwargs: Any,
+    ) -> None:
+        """
+        :param trusted_hosts: Domains not to emit warnings for when not using
+            HTTPS.
+        """
+        super().__init__(*args, **kwargs)
+
+        # Namespace the attribute with "pip_" just in case to prevent
+        # possible conflicts with the base class.
+        self.pip_trusted_origins: list[tuple[str, int | None]] = []
+        # "" disables proxying; None means no --proxy was given.
+        self.pip_proxy: str | None = None
+        self.pip_no_proxy_env = False
+        self.refresh_package: set[str] = refresh_package or set()
+
+        # Attach our User Agent to the request
+        self.headers["User-Agent"] = user_agent()
+
+        # Pin Accept-Encoding so it doesn't vary with zstd availability (Python
+        # 3.14+ or backports.zstd); a varying value misses the cache for "Vary:
+        # Accept-Encoding" responses shared across interpreters (pypa/pip#13979).
+        self.headers["Accept-Encoding"] = "gzip, deflate"
+
+        # Attach our Authentication handler to the session
+        self.auth: MultiDomainBasicAuth = MultiDomainBasicAuth(index_urls=index_urls)
+
+        # Create our urllib3.Retry instance which will allow us to customize
+        # how we handle retries.
+        retries = urllib3.Retry(
+            # Set the total number of retries that a particular request can
+            # have.
+            total=retries,
+            # A 503 error from PyPI typically means that the Fastly -> Origin
+            # connection got interrupted in some way. A 503 error in general
+            # is typically considered a transient error so we'll go ahead and
+            # retry it.
+            # A 500 may indicate transient error in Amazon S3
+            # A 502 may be a transient error from a CDN like CloudFlare or CloudFront
+            # A 520 or 527 - may indicate transient error in CloudFlare
+            status_forcelist=[500, 502, 503, 520, 527],
+            # Add a small amount of back off between failed requests in
+            # order to prevent hammering the service.
+            backoff_factor=0.25,
+        )  # type: ignore
+        self.resume_retries = resume_retries
+
+        # Our Insecure HTTPAdapter disables HTTPS validation. It does not
+        # support caching so we'll use it for all http:// URLs.
+        # If caching is disabled, we will also use it for
+        # https:// hosts that we've marked as ignoring
+        # TLS errors for (trusted-hosts).
+        insecure_adapter = InsecureHTTPAdapter(max_retries=retries)
+
+        # We want to _only_ cache responses on securely fetched origins or when
+        # the host is specified as trusted. We do this because
+        # we can't validate the response of an insecurely/untrusted fetched
+        # origin, and we don't want someone to be able to poison the cache and
+        # require manual eviction from the cache to fix it.
+        self._trusted_host_adapter: InsecureCacheControlAdapter | InsecureHTTPAdapter
+        if cache:
+            secure_adapter: _BaseHTTPAdapter = CacheControlAdapter(
+                cache=SafeFileCache(cache),
+                max_retries=retries,
+                ssl_context=ssl_context,
+            )
+            self._trusted_host_adapter = InsecureCacheControlAdapter(
+                cache=SafeFileCache(cache),
+                max_retries=retries,
+            )
+        else:
+            secure_adapter = HTTPAdapter(max_retries=retries, ssl_context=ssl_context)
+            self._trusted_host_adapter = insecure_adapter
+
+        self.mount("https://", secure_adapter)
+        self.mount("http://", insecure_adapter)
+
+        # Enable file:// urls
+        self.mount("file://", LocalFSAdapter())
+
+        for host in trusted_hosts:
+            self.add_trusted_host(host, suppress_logging=True)
+
+    def update_index_urls(self, new_index_urls: list[str]) -> None:
+        """
+        :param new_index_urls: New index urls to update the authentication
+            handler with.
+        """
+        self.auth.index_urls = new_index_urls
+
+    def add_trusted_host(
+        self, host: str, source: str | None = None, suppress_logging: bool = False
+    ) -> None:
+        """
+        :param host: It is okay to provide a host that has previously been
+            added.
+        :param source: An optional source string, for logging where the host
+            string came from.
+        """
+        if not suppress_logging:
+            msg = f"adding trusted host: {host!r}"
+            if source is not None:
+                msg += f" (from {source})"
+            logger.info(msg)
+
+        parsed_host, parsed_port = parse_netloc(host)
+        if parsed_host is None:
+            raise ValueError(f"Trusted host URL must include a host part: {host!r}")
+        if (parsed_host, parsed_port) not in self.pip_trusted_origins:
+            self.pip_trusted_origins.append((parsed_host, parsed_port))
+
+        self.mount(
+            build_url_from_netloc(host, scheme="http") + "/", self._trusted_host_adapter
+        )
+        self.mount(build_url_from_netloc(host) + "/", self._trusted_host_adapter)
+        if not parsed_port:
+            self.mount(
+                build_url_from_netloc(host, scheme="http") + ":",
+                self._trusted_host_adapter,
+            )
+            # Mount wildcard ports for the same host.
+            self.mount(build_url_from_netloc(host) + ":", self._trusted_host_adapter)
+
+    def iter_secure_origins(self) -> Generator[SecureOrigin, None, None]:
+        yield from SECURE_ORIGINS
+        for host, port in self.pip_trusted_origins:
+            yield ("*", host, "*" if port is None else port)
+
+    def is_secure_origin(self, location: Link) -> bool:
+        # Determine if this url used a secure transport mechanism
+        parsed = urllib.parse.urlparse(str(location))
+        origin_protocol, origin_host, origin_port = (
+            parsed.scheme,
+            parsed.hostname,
+            parsed.port,
+        )
+
+        # The protocol to use to see if the protocol matches.
+        # Don't count the repository type as part of the protocol: in
+        # cases such as "git+ssh", only use "ssh". (I.e., Only verify against
+        # the last scheme.)
+        origin_protocol = origin_protocol.rsplit("+", 1)[-1]
+
+        # Determine if our origin is a secure origin by looking through our
+        # hardcoded list of secure origins, as well as any additional ones
+        # configured on this PackageFinder instance.
+        for secure_origin in self.iter_secure_origins():
+            secure_protocol, secure_host, secure_port = secure_origin
+            if origin_protocol != secure_protocol and secure_protocol != "*":
+                continue
+
+            try:
+                addr = ipaddress.ip_address(origin_host or "")
+                network = ipaddress.ip_network(secure_host)
+            except ValueError:
+                # We don't have both a valid address or a valid network, so
+                # we'll check this origin against hostnames.
+                if (
+                    origin_host
+                    and origin_host.lower() != secure_host.lower()
+                    and secure_host != "*"
+                ):
+                    continue
+            else:
+                # We have a valid address and network, so see if the address
+                # is contained within the network.
+                if addr not in network:
+                    continue
+
+            # Check to see if the port matches.
+            if (
+                origin_port != secure_port
+                and secure_port != "*"
+                and secure_port is not None
+            ):
+                continue
+
+            # If we've gotten here, then this origin matches the current
+            # secure origin and we should return True
+            return True
+
+        # If we've gotten to this point, then the origin isn't secure and we
+        # will not accept it as a valid location to search. We will however
+        # log a warning that we are ignoring it.
+        logger.warning(
+            "The repository located at %s is not a trusted or secure host and "
+            "is being ignored. If this repository is available via HTTPS we "
+            "recommend you use HTTPS instead, otherwise you may silence "
+            "this warning and allow it anyway with '--trusted-host %s'.",
+            origin_host,
+            origin_host,
+        )
+
+        return False
+
+    def request(self, method: str, url: str, *args: Any, **kwargs: Any) -> Response:  # type: ignore[override]
+        # Allow setting a default timeout on a session
+        kwargs.setdefault("timeout", self.timeout)
+        # Allow setting a default proxies on a session
+        kwargs.setdefault("proxies", self.proxies)
+
+        # Dispatch the actual request
+        try:
+            return super().request(method, url, *args, **kwargs)
+        except (requests.ConnectionError, requests.Timeout) as e:
+            request = getattr(e, "request", None)
+            failed_url = getattr(request, "url", None) or url
+            raise_connection_error(e, url=failed_url, timeout=kwargs["timeout"])
+        except ImportError as e:
+            if "ssl" in str(e).lower():
+                # Unfortunately, if this TLS error was the result of a redirect from
+                # a HTTP to a HTTPS url, we don't know what the final url was.
+                raise SSLMissingError(redact_auth_from_url(url))
+            raise
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/utils.py b/venv/lib/python3.12/site-packages/pip/_internal/network/utils.py
new file mode 100644
index 0000000..0f50607
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/network/utils.py
@@ -0,0 +1,206 @@
+from collections.abc import Generator
+from typing import Literal, NoReturn, TypeAlias, cast
+from urllib.parse import urlsplit
+
+from pip._vendor import requests, urllib3
+from pip._vendor.requests.models import Response
+from pip._vendor.urllib3.exceptions import TimeoutStateError
+
+from pip._internal.exceptions import (
+    ConnectionFailedError,
+    ConnectionTimeoutError,
+    NetworkConnectionError,
+    ProxyConnectionError,
+    SSLVerificationError,
+)
+from pip._internal.utils.misc import redact_auth_from_url
+
+TimeoutValue: TypeAlias = (
+    float | tuple[float | None, float | None] | urllib3.util.Timeout | None
+)
+
+# The following comments and HTTP headers were originally added by
+# Donald Stufft in git commit 22c562429a61bb77172039e480873fb239dd8c03.
+#
+# We use Accept-Encoding: identity here because requests defaults to
+# accepting compressed responses. This breaks in a variety of ways
+# depending on how the server is configured.
+# - Some servers will notice that the file isn't a compressible file
+#   and will leave the file alone and with an empty Content-Encoding
+# - Some servers will notice that the file is already compressed and
+#   will leave the file alone, adding a Content-Encoding: gzip header
+# - Some servers won't notice anything at all and will take a file
+#   that's already been compressed and compress it again, and set
+#   the Content-Encoding: gzip header
+# By setting this to request only the identity encoding we're hoping
+# to eliminate the third case.  Hopefully there does not exist a server
+# which when given a file will notice it is already compressed and that
+# you're not asking for a compressed file and will then decompress it
+# before sending because if that's the case I don't think it'll ever be
+# possible to make this work.
+HEADERS: dict[str, str] = {"Accept-Encoding": "identity"}
+
+DOWNLOAD_CHUNK_SIZE = 256 * 1024
+
+
+def raise_for_status(resp: Response) -> None:
+    http_error_msg = ""
+    if isinstance(resp.reason, bytes):
+        # We attempt to decode utf-8 first because some servers
+        # choose to localize their reason strings. If the string
+        # isn't utf-8, we fall back to iso-8859-1 for all other
+        # encodings.
+        try:
+            reason = resp.reason.decode("utf-8")
+        except UnicodeDecodeError:
+            reason = resp.reason.decode("iso-8859-1")
+    else:
+        reason = resp.reason
+
+    if 400 <= resp.status_code < 500:
+        http_error_msg = (
+            f"{resp.status_code} Client Error: {reason} for url: {resp.url}"
+        )
+
+    elif 500 <= resp.status_code < 600:
+        http_error_msg = (
+            f"{resp.status_code} Server Error: {reason} for url: {resp.url}"
+        )
+
+    if http_error_msg:
+        raise NetworkConnectionError(http_error_msg, response=resp)
+
+
+def response_chunks(
+    response: Response, chunk_size: int = DOWNLOAD_CHUNK_SIZE
+) -> Generator[bytes, None, None]:
+    """Given a requests Response, provide the data chunks."""
+    try:
+        # Special case for urllib3.
+        for chunk in response.raw.stream(
+            chunk_size,
+            # We use decode_content=False here because we don't
+            # want urllib3 to mess with the raw bytes we get
+            # from the server. If we decompress inside of
+            # urllib3 then we cannot verify the checksum
+            # because the checksum will be of the compressed
+            # file. This breakage will only occur if the
+            # server adds a Content-Encoding header, which
+            # depends on how the server was configured:
+            # - Some servers will notice that the file isn't a
+            #   compressible file and will leave the file alone
+            #   and with an empty Content-Encoding
+            # - Some servers will notice that the file is
+            #   already compressed and will leave the file
+            #   alone and will add a Content-Encoding: gzip
+            #   header
+            # - Some servers won't notice anything at all and
+            #   will take a file that's already been compressed
+            #   and compress it again and set the
+            #   Content-Encoding: gzip header
+            #
+            # By setting this not to decode automatically we
+            # hope to eliminate problems with the second case.
+            decode_content=False,
+        ):
+            yield chunk
+    except AttributeError:
+        # Standard file-like object.
+        while True:
+            chunk = response.raw.read(chunk_size)
+            if not chunk:
+                break
+            yield chunk
+
+
+def _parse_timeout(timeout: TimeoutValue, kind: Literal["connect", "read"]) -> float:
+    connect_timeout: float | None
+    read_timeout: float | None
+    if isinstance(timeout, tuple):
+        connect_timeout, read_timeout = timeout
+    elif isinstance(timeout, urllib3.util.Timeout):
+        connect_timeout = cast(float | None, timeout.connect_timeout)
+        try:
+            read_timeout = timeout.read_timeout
+        except TimeoutStateError:
+            read_timeout = None
+    else:
+        connect_timeout = read_timeout = timeout
+
+    if kind == "connect":
+        assert connect_timeout is not None
+        return connect_timeout
+    else:
+        assert read_timeout is not None
+        return read_timeout
+
+
+def _raise_timeout_error(
+    reason: urllib3.exceptions.TimeoutError,
+    url: str,
+    host: str,
+    timeout: TimeoutValue,
+) -> NoReturn:
+    if isinstance(reason, urllib3.exceptions.ConnectTimeoutError):
+        raise ConnectionTimeoutError(
+            url, host, kind="connect", timeout=_parse_timeout(timeout, "connect")
+        )
+    else:
+        raise ConnectionTimeoutError(
+            url, host, kind="read", timeout=_parse_timeout(timeout, "read")
+        )
+
+
+def raise_connection_error(
+    error: requests.ConnectionError | requests.Timeout,
+    *,
+    url: str,
+    timeout: TimeoutValue,
+) -> NoReturn:
+    """Raise a specific error for a given connection error, if possible.
+
+    Note: requests.ConnectionError is the parent class of
+          requests.ProxyError, requests.SSLError, and requests.ConnectTimeout
+          so these errors are also handled here.
+    """
+    url = redact_auth_from_url(url)
+    raw_hostname = urlsplit(url).hostname or urlsplit(url).netloc
+    reason = error.args[0] if error.args else error
+
+    # NewConnectionError is a subclass of TimeoutError for some reason...
+    if isinstance(reason, urllib3.exceptions.TimeoutError) and not isinstance(
+        reason, urllib3.exceptions.NewConnectionError
+    ):
+        # A bare timeout error can occur during non-streamed responses. Don't
+        # ask me how.
+        _raise_timeout_error(reason, url, raw_hostname, timeout)
+    if isinstance(reason, urllib3.exceptions.SSLError):
+        # A bare SSL error can occur during non-streamed responses, after the
+        # initial connection and TLS handshake have completed.
+        raise SSLVerificationError(url, raw_hostname, reason)
+
+    # At this point, all errors should be wrapped in MaxRetryError.
+    if not isinstance(reason, urllib3.exceptions.MaxRetryError):
+        raise ConnectionFailedError(url, raw_hostname, reason)
+
+    max_retry_error = reason
+    assert isinstance(max_retry_error.pool, urllib3.connectionpool.HTTPConnectionPool)
+    host = max_retry_error.pool.host
+    proxy = max_retry_error.pool.proxy
+    # Narrow the reason further to the specific error from the last retry.
+    reason = max_retry_error.reason
+
+    if isinstance(reason, urllib3.exceptions.SSLError):
+        raise SSLVerificationError(url, host, reason)
+    if isinstance(reason, urllib3.exceptions.TimeoutError) and not isinstance(
+        reason, urllib3.exceptions.NewConnectionError
+    ):
+        _raise_timeout_error(reason, url, host, timeout)
+    if isinstance(reason, urllib3.exceptions.ProxyError):
+        assert proxy is not None
+        raise ProxyConnectionError(url, redact_auth_from_url(str(proxy)), reason)
+
+    # Unknown error, give up and raise a generic error.
+    raise ConnectionFailedError(
+        url, host, reason if isinstance(reason, Exception) else max_retry_error
+    )
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/network/xmlrpc.py b/venv/lib/python3.12/site-packages/pip/_internal/network/xmlrpc.py
new file mode 100644
index 0000000..f4bddb4
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/network/xmlrpc.py
@@ -0,0 +1,61 @@
+"""xmlrpclib.Transport implementation"""
+
+import logging
+import urllib.parse
+import xmlrpc.client
+from typing import TYPE_CHECKING
+
+from pip._internal.exceptions import NetworkConnectionError
+from pip._internal.network.session import PipSession
+from pip._internal.network.utils import raise_for_status
+
+if TYPE_CHECKING:
+    from xmlrpc.client import _HostType, _Marshallable
+
+    from _typeshed import SizedBuffer
+
+logger = logging.getLogger(__name__)
+
+
+class PipXmlrpcTransport(xmlrpc.client.Transport):
+    """Provide a `xmlrpclib.Transport` implementation via a `PipSession`
+    object.
+    """
+
+    def __init__(
+        self, index_url: str, session: PipSession, use_datetime: bool = False
+    ) -> None:
+        super().__init__(use_datetime)
+        index_parts = urllib.parse.urlparse(index_url)
+        self._scheme = index_parts.scheme
+        self._session = session
+
+    def request(
+        self,
+        host: "_HostType",
+        handler: str,
+        request_body: "SizedBuffer",
+        verbose: bool = False,
+    ) -> tuple["_Marshallable", ...]:
+        assert isinstance(host, str)
+        parts = (self._scheme, host, handler, None, None, None)
+        url = urllib.parse.urlunparse(parts)
+        try:
+            headers = {"Content-Type": "text/xml"}
+            response = self._session.post(
+                url,
+                data=request_body,
+                headers=headers,
+                stream=True,
+            )
+            raise_for_status(response)
+            self.verbose = verbose
+            return self.parse_response(response.raw)
+        except NetworkConnectionError as exc:
+            assert exc.response
+            logger.critical(
+                "HTTP error %s while getting %s",
+                exc.response.status_code,
+                url,
+            )
+            raise
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/operations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..4457915
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/check.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/check.cpython-312.pyc
new file mode 100644
index 0000000..83e148d
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/check.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-312.pyc
new file mode 100644
index 0000000..d4f4e09
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-312.pyc
new file mode 100644
index 0000000..a769491
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..6182dc4
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc
new file mode 100644
index 0000000..c50b144
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc
new file mode 100644
index 0000000..04b1fd7
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_editable.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_editable.cpython-312.pyc
new file mode 100644
index 0000000..3bdbe58
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_editable.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-312.pyc
new file mode 100644
index 0000000..7a26a8a
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc
new file mode 100644
index 0000000..89af82c
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/build/build_tracker.py b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/build_tracker.py
new file mode 100644
index 0000000..cbb4e4f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/build_tracker.py
@@ -0,0 +1,140 @@
+from __future__ import annotations
+
+import contextlib
+import hashlib
+import logging
+import os
+from collections.abc import Generator
+from types import TracebackType
+
+from pip._internal.req.req_install import InstallRequirement
+from pip._internal.utils.temp_dir import TempDirectory
+
+logger = logging.getLogger(__name__)
+
+
+@contextlib.contextmanager
+def update_env_context_manager(**changes: str) -> Generator[None, None, None]:
+    target = os.environ
+
+    # Save values from the target and change them.
+    non_existent_marker = object()
+    saved_values: dict[str, object | str] = {}
+    for name, new_value in changes.items():
+        try:
+            saved_values[name] = target[name]
+        except KeyError:
+            saved_values[name] = non_existent_marker
+        target[name] = new_value
+
+    try:
+        yield
+    finally:
+        # Restore original values in the target.
+        for name, original_value in saved_values.items():
+            if original_value is non_existent_marker:
+                del target[name]
+            else:
+                assert isinstance(original_value, str)  # for mypy
+                target[name] = original_value
+
+
+@contextlib.contextmanager
+def get_build_tracker() -> Generator[BuildTracker, None, None]:
+    root = os.environ.get("PIP_BUILD_TRACKER")
+    with contextlib.ExitStack() as ctx:
+        if root is None:
+            root = ctx.enter_context(TempDirectory(kind="build-tracker")).path
+            ctx.enter_context(update_env_context_manager(PIP_BUILD_TRACKER=root))
+            logger.debug("Initialized build tracking at %s", root)
+
+        with BuildTracker(root) as tracker:
+            yield tracker
+
+
+class TrackerId(str):
+    """Uniquely identifying string provided to the build tracker."""
+
+
+class BuildTracker:
+    """Ensure that an sdist cannot request itself as a setup requirement.
+
+    When an sdist is prepared, it identifies its setup requirements in the
+    context of ``BuildTracker.track()``. If a requirement shows up recursively, this
+    raises an exception.
+
+    This stops fork bombs embedded in malicious packages."""
+
+    def __init__(self, root: str) -> None:
+        self._root = root
+        self._entries: dict[TrackerId, InstallRequirement] = {}
+        logger.debug("Created build tracker: %s", self._root)
+
+    def __enter__(self) -> BuildTracker:
+        logger.debug("Entered build tracker: %s", self._root)
+        return self
+
+    def __exit__(
+        self,
+        exc_type: type[BaseException] | None,
+        exc_val: BaseException | None,
+        exc_tb: TracebackType | None,
+    ) -> None:
+        self.cleanup()
+
+    def _entry_path(self, key: TrackerId) -> str:
+        hashed = hashlib.sha224(key.encode()).hexdigest()
+        return os.path.join(self._root, hashed)
+
+    def add(self, req: InstallRequirement, key: TrackerId) -> None:
+        """Add an InstallRequirement to build tracking."""
+
+        # Get the file to write information about this requirement.
+        entry_path = self._entry_path(key)
+
+        # Try reading from the file. If it exists and can be read from, a build
+        # is already in progress, so a LookupError is raised.
+        try:
+            with open(entry_path) as fp:
+                contents = fp.read()
+        except FileNotFoundError:
+            pass
+        else:
+            message = f"{req.link} is already being built: {contents}"
+            raise LookupError(message)
+
+        # If we're here, req should really not be building already.
+        assert key not in self._entries
+
+        # Start tracking this requirement.
+        with open(entry_path, "w", encoding="utf-8") as fp:
+            fp.write(str(req))
+        self._entries[key] = req
+
+        logger.debug("Added %s to build tracker %r", req, self._root)
+
+    def remove(self, req: InstallRequirement, key: TrackerId) -> None:
+        """Remove an InstallRequirement from build tracking."""
+
+        # Delete the created file and the corresponding entry.
+        os.unlink(self._entry_path(key))
+        del self._entries[key]
+
+        logger.debug("Removed %s from build tracker %r", req, self._root)
+
+    def cleanup(self) -> None:
+        for key, req in list(self._entries.items()):
+            self.remove(req, key)
+
+        logger.debug("Removed build tracker: %r", self._root)
+
+    @contextlib.contextmanager
+    def track(self, req: InstallRequirement, key: str) -> Generator[None, None, None]:
+        """Ensure that `key` cannot install itself as a setup requirement.
+
+        :raises LookupError: If `key` was already provided in a parent invocation of
+                             the context introduced by this method."""
+        tracker_id = TrackerId(key)
+        self.add(req, tracker_id)
+        yield
+        self.remove(req, tracker_id)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata.py b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata.py
new file mode 100644
index 0000000..a546809
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata.py
@@ -0,0 +1,38 @@
+"""Metadata generation logic for source distributions."""
+
+import os
+
+from pip._vendor.pyproject_hooks import BuildBackendHookCaller
+
+from pip._internal.build_env import BuildEnvironment
+from pip._internal.exceptions import (
+    InstallationSubprocessError,
+    MetadataGenerationFailed,
+)
+from pip._internal.utils.subprocess import runner_with_spinner_message
+from pip._internal.utils.temp_dir import TempDirectory
+
+
+def generate_metadata(
+    build_env: BuildEnvironment, backend: BuildBackendHookCaller, details: str
+) -> str:
+    """Generate metadata using mechanisms described in PEP 517.
+
+    Returns the generated metadata directory.
+    """
+    metadata_tmpdir = TempDirectory(kind="modern-metadata", globally_managed=True)
+
+    metadata_dir = metadata_tmpdir.path
+
+    with build_env:
+        # Note that BuildBackendHookCaller implements a fallback for
+        # prepare_metadata_for_build_wheel, so we don't have to
+        # consider the possibility that this hook doesn't exist.
+        runner = runner_with_spinner_message("Preparing metadata (pyproject.toml)")
+        with backend.subprocess_runner(runner):
+            try:
+                distinfo_dir = backend.prepare_metadata_for_build_wheel(metadata_dir)
+            except InstallationSubprocessError as error:
+                raise MetadataGenerationFailed(package_details=details) from error
+
+    return os.path.join(metadata_dir, distinfo_dir)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_editable.py b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_editable.py
new file mode 100644
index 0000000..27ecd7d
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_editable.py
@@ -0,0 +1,41 @@
+"""Metadata generation logic for source distributions."""
+
+import os
+
+from pip._vendor.pyproject_hooks import BuildBackendHookCaller
+
+from pip._internal.build_env import BuildEnvironment
+from pip._internal.exceptions import (
+    InstallationSubprocessError,
+    MetadataGenerationFailed,
+)
+from pip._internal.utils.subprocess import runner_with_spinner_message
+from pip._internal.utils.temp_dir import TempDirectory
+
+
+def generate_editable_metadata(
+    build_env: BuildEnvironment, backend: BuildBackendHookCaller, details: str
+) -> str:
+    """Generate metadata using mechanisms described in PEP 660.
+
+    Returns the generated metadata directory.
+    """
+    metadata_tmpdir = TempDirectory(kind="modern-metadata", globally_managed=True)
+
+    metadata_dir = metadata_tmpdir.path
+
+    with build_env:
+        # Note that BuildBackendHookCaller implements a fallback for
+        # prepare_metadata_for_build_wheel/editable, so we don't have to
+        # consider the possibility that this hook doesn't exist.
+        runner = runner_with_spinner_message(
+            "Preparing editable metadata (pyproject.toml)"
+        )
+        with backend.subprocess_runner(runner):
+            try:
+                distinfo_dir = backend.prepare_metadata_for_build_editable(metadata_dir)
+            except InstallationSubprocessError as error:
+                raise MetadataGenerationFailed(package_details=details) from error
+
+    assert distinfo_dir is not None
+    return os.path.join(metadata_dir, distinfo_dir)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel.py b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel.py
new file mode 100644
index 0000000..8595e45
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel.py
@@ -0,0 +1,38 @@
+from __future__ import annotations
+
+import logging
+import os
+
+from pip._vendor.pyproject_hooks import BuildBackendHookCaller
+
+from pip._internal.utils.subprocess import runner_with_spinner_message
+
+logger = logging.getLogger(__name__)
+
+
+def build_wheel_pep517(
+    name: str,
+    backend: BuildBackendHookCaller,
+    metadata_directory: str,
+    wheel_directory: str,
+) -> str | None:
+    """Build one InstallRequirement using the PEP 517 build process.
+
+    Returns path to wheel if successfully built. Otherwise, returns None.
+    """
+    assert metadata_directory is not None
+    try:
+        logger.debug("Destination directory: %s", wheel_directory)
+
+        runner = runner_with_spinner_message(
+            f"Building wheel for {name} (pyproject.toml)"
+        )
+        with backend.subprocess_runner(runner):
+            wheel_name = backend.build_wheel(
+                wheel_directory=wheel_directory,
+                metadata_directory=metadata_directory,
+            )
+    except Exception:
+        logger.error("Failed building wheel for %s", name)
+        return None
+    return os.path.join(wheel_directory, wheel_name)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_editable.py b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_editable.py
new file mode 100644
index 0000000..f00d9b2
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_editable.py
@@ -0,0 +1,47 @@
+from __future__ import annotations
+
+import logging
+import os
+
+from pip._vendor.pyproject_hooks import BuildBackendHookCaller, HookMissing
+
+from pip._internal.utils.subprocess import runner_with_spinner_message
+
+logger = logging.getLogger(__name__)
+
+
+def build_wheel_editable(
+    name: str,
+    backend: BuildBackendHookCaller,
+    metadata_directory: str,
+    wheel_directory: str,
+) -> str | None:
+    """Build one InstallRequirement using the PEP 660 build process.
+
+    Returns path to wheel if successfully built. Otherwise, returns None.
+    """
+    assert metadata_directory is not None
+    try:
+        logger.debug("Destination directory: %s", wheel_directory)
+
+        runner = runner_with_spinner_message(
+            f"Building editable for {name} (pyproject.toml)"
+        )
+        with backend.subprocess_runner(runner):
+            try:
+                wheel_name = backend.build_editable(
+                    wheel_directory=wheel_directory,
+                    metadata_directory=metadata_directory,
+                )
+            except HookMissing as e:
+                logger.error(
+                    "Cannot build editable %s because the build "
+                    "backend does not have the %s hook",
+                    name,
+                    e,
+                )
+                return None
+    except Exception:
+        logger.error("Failed building editable for %s", name)
+        return None
+    return os.path.join(wheel_directory, wheel_name)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/check.py b/venv/lib/python3.12/site-packages/pip/_internal/operations/check.py
new file mode 100644
index 0000000..00cb6c5
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/operations/check.py
@@ -0,0 +1,174 @@
+"""Validation of dependencies of packages"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable, Generator, Iterable
+from contextlib import suppress
+from email.parser import Parser
+from functools import reduce
+from typing import (
+    NamedTuple,
+)
+
+from pip._vendor.packaging.requirements import Requirement
+from pip._vendor.packaging.tags import Tag, parse_tag
+from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
+from pip._vendor.packaging.version import Version
+
+from pip._internal.distributions import make_distribution_for_install_requirement
+from pip._internal.metadata import get_default_environment
+from pip._internal.metadata.base import BaseDistribution
+from pip._internal.req.req_install import InstallRequirement
+
+logger = logging.getLogger(__name__)
+
+
+class PackageDetails(NamedTuple):
+    version: Version
+    dependencies: list[Requirement]
+
+
+# Shorthands
+PackageSet = dict[NormalizedName, PackageDetails]
+Missing = tuple[NormalizedName, Requirement]
+Conflicting = tuple[NormalizedName, Version, Requirement]
+
+MissingDict = dict[NormalizedName, list[Missing]]
+ConflictingDict = dict[NormalizedName, list[Conflicting]]
+CheckResult = tuple[MissingDict, ConflictingDict]
+ConflictDetails = tuple[PackageSet, CheckResult]
+
+
+def create_package_set_from_installed() -> tuple[PackageSet, bool]:
+    """Converts a list of distributions into a PackageSet."""
+    package_set = {}
+    problems = False
+    env = get_default_environment()
+    for dist in env.iter_installed_distributions(local_only=False, skip=()):
+        name = dist.canonical_name
+        try:
+            dependencies = list(dist.iter_dependencies())
+            package_set[name] = PackageDetails(dist.version, dependencies)
+        except (OSError, ValueError) as e:
+            # Don't crash on unreadable or broken metadata.
+            logger.warning("Error parsing dependencies of %s: %s", name, e)
+            problems = True
+    return package_set, problems
+
+
+def check_package_set(
+    package_set: PackageSet, should_ignore: Callable[[str], bool] | None = None
+) -> CheckResult:
+    """Check if a package set is consistent
+
+    If should_ignore is passed, it should be a callable that takes a
+    package name and returns a boolean.
+    """
+
+    missing = {}
+    conflicting = {}
+
+    for package_name, package_detail in package_set.items():
+        # Info about dependencies of package_name
+        missing_deps: set[Missing] = set()
+        conflicting_deps: set[Conflicting] = set()
+
+        if should_ignore and should_ignore(package_name):
+            continue
+
+        for req in package_detail.dependencies:
+            name = canonicalize_name(req.name)
+
+            # Check if it's missing
+            if name not in package_set:
+                missed = True
+                if req.marker is not None:
+                    missed = req.marker.evaluate({"extra": ""})
+                if missed:
+                    missing_deps.add((name, req))
+                continue
+
+            # Check if there's a conflict
+            version = package_set[name].version
+            if not req.specifier.contains(version, prereleases=True):
+                conflicting_deps.add((name, version, req))
+
+        if missing_deps:
+            missing[package_name] = sorted(missing_deps, key=str)
+        if conflicting_deps:
+            conflicting[package_name] = sorted(conflicting_deps, key=str)
+
+    return missing, conflicting
+
+
+def check_install_conflicts(to_install: list[InstallRequirement]) -> ConflictDetails:
+    """For checking if the dependency graph would be consistent after \
+    installing given requirements
+    """
+    # Start from the current state
+    package_set, _ = create_package_set_from_installed()
+    # Install packages
+    would_be_installed = _simulate_installation_of(to_install, package_set)
+
+    # Only warn about directly-dependent packages; create a whitelist of them
+    whitelist = _create_whitelist(would_be_installed, package_set)
+
+    return (
+        package_set,
+        check_package_set(
+            package_set, should_ignore=lambda name: name not in whitelist
+        ),
+    )
+
+
+def check_unsupported(
+    packages: Iterable[BaseDistribution],
+    supported_tags: Iterable[Tag],
+) -> Generator[BaseDistribution, None, None]:
+    for p in packages:
+        with suppress(FileNotFoundError):
+            wheel_file = p.read_text("WHEEL")
+            wheel_tags: frozenset[Tag] = reduce(
+                frozenset.union,
+                map(parse_tag, Parser().parsestr(wheel_file).get_all("Tag", [])),
+                frozenset(),
+            )
+            if wheel_tags.isdisjoint(supported_tags):
+                yield p
+
+
+def _simulate_installation_of(
+    to_install: list[InstallRequirement], package_set: PackageSet
+) -> set[NormalizedName]:
+    """Computes the version of packages after installing to_install."""
+    # Keep track of packages that were installed
+    installed = set()
+
+    # Modify it as installing requirement_set would (assuming no errors)
+    for inst_req in to_install:
+        abstract_dist = make_distribution_for_install_requirement(inst_req)
+        dist = abstract_dist.get_metadata_distribution()
+        name = dist.canonical_name
+        package_set[name] = PackageDetails(dist.version, list(dist.iter_dependencies()))
+
+        installed.add(name)
+
+    return installed
+
+
+def _create_whitelist(
+    would_be_installed: set[NormalizedName], package_set: PackageSet
+) -> set[NormalizedName]:
+    packages_affected = set(would_be_installed)
+
+    for package_name in package_set:
+        if package_name in packages_affected:
+            continue
+
+        for req in package_set[package_name].dependencies:
+            if canonicalize_name(req.name) in would_be_installed:
+                packages_affected.add(package_name)
+                break
+
+    return packages_affected
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/freeze.py b/venv/lib/python3.12/site-packages/pip/_internal/operations/freeze.py
new file mode 100644
index 0000000..486a833
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/operations/freeze.py
@@ -0,0 +1,259 @@
+from __future__ import annotations
+
+import collections
+import logging
+import os
+from collections.abc import Container, Generator, Iterable
+from dataclasses import dataclass, field
+from typing import NamedTuple
+
+from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
+from pip._vendor.packaging.version import InvalidVersion
+
+from pip._internal.exceptions import BadCommand, InstallationError
+from pip._internal.metadata import BaseDistribution, get_environment
+from pip._internal.req.constructors import (
+    install_req_from_editable,
+    install_req_from_line,
+)
+from pip._internal.req.req_file import COMMENT_RE
+from pip._internal.utils.direct_url_helpers import direct_url_as_pep440_direct_reference
+
+logger = logging.getLogger(__name__)
+
+
+class _EditableInfo(NamedTuple):
+    requirement: str
+    comments: list[str]
+
+
+def freeze(
+    requirement: list[str] | None = None,
+    local_only: bool = False,
+    user_only: bool = False,
+    paths: list[str] | None = None,
+    isolated: bool = False,
+    exclude_editable: bool = False,
+    skip: Container[str] = (),
+) -> Generator[str, None, None]:
+    installations: dict[str, FrozenRequirement] = {}
+
+    dists = get_environment(paths).iter_installed_distributions(
+        local_only=local_only,
+        skip=(),
+        user_only=user_only,
+    )
+    for dist in dists:
+        req = FrozenRequirement.from_dist(dist)
+        if exclude_editable and req.editable:
+            continue
+        installations[req.canonical_name] = req
+
+    if requirement:
+        # the options that don't get turned into an InstallRequirement
+        # should only be emitted once, even if the same option is in multiple
+        # requirements files, so we need to keep track of what has been emitted
+        # so that we don't emit it again if it's seen again
+        emitted_options: set[str] = set()
+        # keep track of which files a requirement is in so that we can
+        # give an accurate warning if a requirement appears multiple times.
+        req_files: dict[str, list[str]] = collections.defaultdict(list)
+        for req_file_path in requirement:
+            with open(req_file_path) as req_file:
+                for line in req_file:
+                    if (
+                        not line.strip()
+                        or line.strip().startswith("#")
+                        or line.startswith(
+                            (
+                                "-r",
+                                "--requirement",
+                                "-f",
+                                "--find-links",
+                                "-i",
+                                "--index-url",
+                                "--pre",
+                                "--trusted-host",
+                                "--process-dependency-links",
+                                "--extra-index-url",
+                                "--use-feature",
+                            )
+                        )
+                    ):
+                        line = line.rstrip()
+                        if line not in emitted_options:
+                            emitted_options.add(line)
+                            yield line
+                        continue
+
+                    if line.startswith(("-e", "--editable")):
+                        if line.startswith("-e"):
+                            line = line[2:].strip()
+                        else:
+                            line = line[len("--editable") :].strip().lstrip("=")
+                        line_req = install_req_from_editable(
+                            line,
+                            isolated=isolated,
+                        )
+                    else:
+                        line_req = install_req_from_line(
+                            COMMENT_RE.sub("", line).strip(),
+                            isolated=isolated,
+                        )
+
+                    if not line_req.name:
+                        logger.info(
+                            "Skipping line in requirement file [%s] because "
+                            "it's not clear what it would install: %s",
+                            req_file_path,
+                            line.strip(),
+                        )
+                        logger.info(
+                            "  (add #egg=PackageName to the URL to avoid"
+                            " this warning)"
+                        )
+                    else:
+                        line_req_canonical_name = canonicalize_name(line_req.name)
+                        if line_req_canonical_name not in installations:
+                            # either it's not installed, or it is installed
+                            # but has been processed already
+                            if not req_files[line_req.name]:
+                                logger.warning(
+                                    "Requirement file [%s] contains %s, but "
+                                    "package %r is not installed",
+                                    req_file_path,
+                                    COMMENT_RE.sub("", line).strip(),
+                                    line_req.name,
+                                )
+                            else:
+                                req_files[line_req.name].append(req_file_path)
+                        else:
+                            yield str(installations[line_req_canonical_name]).rstrip()
+                            del installations[line_req_canonical_name]
+                            req_files[line_req.name].append(req_file_path)
+
+        # Warn about requirements that were included multiple times (in a
+        # single requirements file or in different requirements files).
+        for name, files in req_files.items():
+            if len(files) > 1:
+                logger.warning(
+                    "Requirement %s included multiple times [%s]",
+                    name,
+                    ", ".join(sorted(set(files))),
+                )
+
+        yield ("## The following requirements were added by pip freeze:")
+    for installation in sorted(installations.values(), key=lambda x: x.name.lower()):
+        if installation.canonical_name not in skip:
+            yield str(installation).rstrip()
+
+
+def _format_as_name_version(dist: BaseDistribution) -> str:
+    try:
+        dist_version = dist.version
+    except InvalidVersion:
+        # legacy version
+        return f"{dist.raw_name}==={dist.raw_version}"
+    else:
+        return f"{dist.raw_name}=={dist_version}"
+
+
+def _get_editable_info(dist: BaseDistribution) -> _EditableInfo:
+    """
+    Compute and return values (req, comments) for use in
+    FrozenRequirement.from_dist().
+    """
+    editable_project_location = dist.editable_project_location
+    assert editable_project_location
+    location = os.path.normcase(os.path.abspath(editable_project_location))
+
+    from pip._internal.vcs import RemoteNotFoundError, RemoteNotValidError, vcs
+
+    vcs_backend = vcs.get_backend_for_dir(location)
+
+    if vcs_backend is None:
+        display = _format_as_name_version(dist)
+        logger.debug(
+            'No VCS found for editable requirement "%s" in: %r',
+            display,
+            location,
+        )
+        return _EditableInfo(
+            requirement=location,
+            comments=[f"# Editable install with no version control ({display})"],
+        )
+
+    vcs_name = type(vcs_backend).__name__
+
+    try:
+        req = vcs_backend.get_src_requirement(location, dist.raw_name)
+    except RemoteNotFoundError:
+        display = _format_as_name_version(dist)
+        return _EditableInfo(
+            requirement=location,
+            comments=[f"# Editable {vcs_name} install with no remote ({display})"],
+        )
+    except RemoteNotValidError as ex:
+        display = _format_as_name_version(dist)
+        return _EditableInfo(
+            requirement=location,
+            comments=[
+                f"# Editable {vcs_name} install ({display}) with either a deleted "
+                f"local remote or invalid URI:",
+                f"# '{ex.url}'",
+            ],
+        )
+    except BadCommand:
+        logger.warning(
+            "cannot determine version of editable source in %s "
+            "(%s command not found in path)",
+            location,
+            vcs_backend.name,
+        )
+        return _EditableInfo(requirement=location, comments=[])
+    except InstallationError as exc:
+        logger.warning("Error when trying to get requirement for VCS system %s", exc)
+    else:
+        return _EditableInfo(requirement=req, comments=[])
+
+    logger.warning("Could not determine repository location of %s", location)
+
+    return _EditableInfo(
+        requirement=location,
+        comments=["## !! Could not determine repository location"],
+    )
+
+
+@dataclass(frozen=True)
+class FrozenRequirement:
+    name: str
+    req: str
+    editable: bool
+    comments: Iterable[str] = field(default_factory=tuple)
+
+    @property
+    def canonical_name(self) -> NormalizedName:
+        return canonicalize_name(self.name)
+
+    @classmethod
+    def from_dist(cls, dist: BaseDistribution) -> FrozenRequirement:
+        editable = dist.editable
+        if editable:
+            req, comments = _get_editable_info(dist)
+        else:
+            comments = []
+            direct_url = dist.direct_url
+            if direct_url:
+                # if PEP 610 metadata is present, use it
+                req = direct_url_as_pep440_direct_reference(direct_url, dist.raw_name)
+            else:
+                # name==version requirement
+                req = _format_as_name_version(dist)
+
+        return cls(dist.raw_name, req, editable, comments=comments)
+
+    def __str__(self) -> str:
+        req = self.req
+        if self.editable:
+            req = f"-e {req}"
+        return "\n".join(list(self.comments) + [str(req)]) + "\n"
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/install/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/operations/install/__init__.py
new file mode 100644
index 0000000..2645a4a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/operations/install/__init__.py
@@ -0,0 +1 @@
+"""For modules related to installing packages."""
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..0e4c675
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-312.pyc
new file mode 100644
index 0000000..0f5c67e
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/install/wheel.py b/venv/lib/python3.12/site-packages/pip/_internal/operations/install/wheel.py
new file mode 100644
index 0000000..c026005
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/operations/install/wheel.py
@@ -0,0 +1,764 @@
+"""Support for installing and building the "wheel" binary package format."""
+
+from __future__ import annotations
+
+import collections
+import compileall
+import contextlib
+import csv
+import importlib
+import logging
+import os.path
+import re
+import shutil
+import sys
+import textwrap
+import warnings
+from base64 import urlsafe_b64encode
+from collections.abc import Callable, Generator, Iterable, Iterator, Sequence
+from email.message import Message
+from itertools import chain, filterfalse, starmap
+from pathlib import Path
+from typing import (
+    IO,
+    Any,
+    BinaryIO,
+    NewType,
+    Protocol,
+    cast,
+)
+from zipfile import ZipFile, ZipInfo
+
+from pip._vendor.distlib.scripts import ScriptMaker
+from pip._vendor.distlib.util import get_export_entry
+from pip._vendor.packaging.utils import canonicalize_name
+
+from pip._internal.exceptions import InstallationError
+from pip._internal.locations import get_major_minor_version
+from pip._internal.metadata import (
+    BaseDistribution,
+    FilesystemWheel,
+    get_wheel_distribution,
+)
+from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, DirectUrl
+from pip._internal.models.scheme import SCHEME_KEYS, Scheme
+from pip._internal.utils.filesystem import adjacent_tmp_file, replace
+from pip._internal.utils.misc import StreamWrapper, ensure_dir, hash_file, partition
+from pip._internal.utils.unpacking import (
+    current_umask,
+    is_within_directory,
+    set_extracted_file_to_default_mode_plus_executable,
+    zip_item_is_executable,
+)
+from pip._internal.utils.wheel import parse_wheel
+
+
+class File(Protocol):
+    src_record_path: RecordPath
+    dest_path: str
+    changed: bool
+
+    def save(self) -> None:
+        pass
+
+
+logger = logging.getLogger(__name__)
+
+RecordPath = NewType("RecordPath", str)
+InstalledCSVRow = tuple[RecordPath, str, int | str]
+
+
+def rehash(path: str, blocksize: int = 1 << 20) -> tuple[str, str]:
+    """Return (encoded_digest, length) for path using hashlib.sha256()"""
+    h, length = hash_file(path, blocksize)
+    digest = "sha256=" + urlsafe_b64encode(h.digest()).decode("latin1").rstrip("=")
+    return (digest, str(length))
+
+
+def csv_io_kwargs(mode: str) -> dict[str, Any]:
+    """Return keyword arguments to properly open a CSV file
+    in the given mode.
+    """
+    return {"mode": mode, "newline": "", "encoding": "utf-8"}
+
+
+def fix_script(path: str) -> bool:
+    """Replace #!python with #!/path/to/python
+    Return True if file was changed.
+    """
+    # XXX RECORD hashes will need to be updated
+    assert os.path.isfile(path)
+
+    with open(path, "rb") as script:
+        firstline = script.readline()
+        if not firstline.startswith(b"#!python"):
+            return False
+        exename = sys.executable.encode(sys.getfilesystemencoding())
+        firstline = b"#!" + exename + os.linesep.encode("ascii")
+        rest = script.read()
+    with open(path, "wb") as script:
+        script.write(firstline)
+        script.write(rest)
+    return True
+
+
+def wheel_root_is_purelib(metadata: Message) -> bool:
+    return metadata.get("Root-Is-Purelib", "").lower() == "true"
+
+
+def get_entrypoints(dist: BaseDistribution) -> tuple[dict[str, str], dict[str, str]]:
+    console_scripts = {}
+    gui_scripts = {}
+    for entry_point in dist.iter_entry_points():
+        if entry_point.group == "console_scripts":
+            console_scripts[entry_point.name] = entry_point.value
+        elif entry_point.group == "gui_scripts":
+            gui_scripts[entry_point.name] = entry_point.value
+    return console_scripts, gui_scripts
+
+
+def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> str | None:
+    """Determine if any scripts are not on PATH and format a warning.
+    Returns a warning message if one or more scripts are not on PATH,
+    otherwise None.
+    """
+    if not scripts:
+        return None
+
+    # Group scripts by the path they were installed in
+    grouped_by_dir: dict[Path, set[str]] = collections.defaultdict(set)
+    for destfile in scripts:
+        dest_path = Path(destfile)
+        parent_dir = dest_path.parent.resolve()
+        script_name = dest_path.name
+        grouped_by_dir[parent_dir].add(script_name)
+
+    # We don't want to warn for directories that are on PATH.
+    not_warn_dirs = [
+        Path(i).resolve() for i in os.environ.get("PATH", "").split(os.pathsep)
+    ]
+    # If an executable sits with sys.executable, we don't warn for it.
+    #     This covers the case of venv invocations without activating the venv.
+    not_warn_dirs.append(Path(sys.executable).parent.resolve())
+    warn_for: dict[Path, set[str]] = {
+        parent_dir: scripts
+        for parent_dir, scripts in grouped_by_dir.items()
+        if parent_dir not in not_warn_dirs
+    }
+    if not warn_for:
+        return None
+
+    # Format a message
+    msg_lines = []
+    for parent_dir, dir_scripts in warn_for.items():
+        sorted_scripts: list[str] = sorted(dir_scripts)
+        if len(sorted_scripts) == 1:
+            start_text = f"script {sorted_scripts[0]} is"
+        else:
+            start_text = "scripts {} are".format(
+                ", ".join(sorted_scripts[:-1]) + " and " + sorted_scripts[-1]
+            )
+
+        msg_lines.append(
+            f"The {start_text} installed in '{parent_dir}' which is not on PATH."
+        )
+
+    last_line_fmt = (
+        "Consider adding {} to PATH or, if you prefer "
+        "to suppress this warning, use --no-warn-script-location."
+    )
+    if len(msg_lines) == 1:
+        msg_lines.append(last_line_fmt.format("this directory"))
+    else:
+        msg_lines.append(last_line_fmt.format("these directories"))
+
+    # Add a note if any directory starts with ~
+    warn_for_tilde = any(
+        i[0] == "~" for i in os.environ.get("PATH", "").split(os.pathsep) if i
+    )
+    if warn_for_tilde:
+        tilde_warning_msg = (
+            "NOTE: The current PATH contains path(s) starting with `~`, "
+            "which may not be expanded by all applications."
+        )
+        msg_lines.append(tilde_warning_msg)
+
+    # Returns the formatted multiline message
+    return "\n".join(msg_lines)
+
+
+def _normalized_outrows(
+    outrows: Iterable[InstalledCSVRow],
+) -> list[tuple[str, str, str]]:
+    """Normalize the given rows of a RECORD file.
+
+    Items in each row are converted into str. Rows are then sorted to make
+    the value more predictable for tests.
+
+    Each row is a 3-tuple (path, hash, size) and corresponds to a record of
+    a RECORD file (see PEP 376 and PEP 427 for details).  For the rows
+    passed to this function, the size can be an integer as an int or string,
+    or the empty string.
+    """
+    # Normally, there should only be one row per path, in which case the
+    # second and third elements don't come into play when sorting.
+    # However, in cases in the wild where a path might happen to occur twice,
+    # we don't want the sort operation to trigger an error (but still want
+    # determinism).  Since the third element can be an int or string, we
+    # coerce each element to a string to avoid a TypeError in this case.
+    # For additional background, see--
+    # https://github.com/pypa/pip/issues/5868
+    return sorted(
+        (record_path, hash_, str(size)) for record_path, hash_, size in outrows
+    )
+
+
+def _record_to_fs_path(record_path: RecordPath, lib_dir: str) -> str:
+    return os.path.join(lib_dir, record_path)
+
+
+def _fs_to_record_path(path: str, lib_dir: str) -> RecordPath:
+    # On Windows, do not handle relative paths if they belong to different
+    # logical disks
+    if os.path.splitdrive(path)[0].lower() == os.path.splitdrive(lib_dir)[0].lower():
+        path = os.path.relpath(path, lib_dir)
+
+    path = path.replace(os.path.sep, "/")
+    return cast("RecordPath", path)
+
+
+def get_csv_rows_for_installed(
+    old_csv_rows: list[list[str]],
+    installed: dict[RecordPath, RecordPath],
+    changed: set[RecordPath],
+    generated: list[str],
+    lib_dir: str,
+) -> list[InstalledCSVRow]:
+    """
+    :param installed: A map from archive RECORD path to installation RECORD
+        path.
+    """
+    installed_rows: list[InstalledCSVRow] = []
+    for row in old_csv_rows:
+        if len(row) > 3:
+            logger.warning("RECORD line has more than three elements: %s", row)
+        old_record_path = cast("RecordPath", row[0])
+        new_record_path = installed.pop(old_record_path, old_record_path)
+        if new_record_path in changed:
+            digest, length = rehash(_record_to_fs_path(new_record_path, lib_dir))
+        else:
+            digest = row[1] if len(row) > 1 else ""
+            length = row[2] if len(row) > 2 else ""
+        installed_rows.append((new_record_path, digest, length))
+    for f in generated:
+        path = _fs_to_record_path(f, lib_dir)
+        digest, length = rehash(f)
+        installed_rows.append((path, digest, length))
+    return installed_rows + [
+        (installed_record_path, "", "") for installed_record_path in installed.values()
+    ]
+
+
+def get_console_script_specs(console: dict[str, str]) -> list[str]:
+    """
+    Given the mapping from entrypoint name to callable, return the relevant
+    console script specs.
+    """
+    # Don't mutate caller's version
+    console = console.copy()
+
+    scripts_to_generate = []
+
+    # Special case pip and setuptools to generate versioned wrappers
+    #
+    # The issue is that some projects (specifically, pip and setuptools) use
+    # code in setup.py to create "versioned" entry points - pip2.7 on Python
+    # 2.7, pip3.3 on Python 3.3, etc. But these entry points are baked into
+    # the wheel metadata at build time, and so if the wheel is installed with
+    # a *different* version of Python the entry points will be wrong. The
+    # correct fix for this is to enhance the metadata to be able to describe
+    # such versioned entry points.
+    # Currently, projects using versioned entry points will either have
+    # incorrect versioned entry points, or they will not be able to distribute
+    # "universal" wheels (i.e., they will need a wheel per Python version).
+    #
+    # Because setuptools and pip are bundled with _ensurepip and virtualenv,
+    # we need to use universal wheels. As a workaround, we
+    # override the versioned entry points in the wheel and generate the
+    # correct ones.
+    #
+    # To add the level of hack in this section of code, in order to support
+    # ensurepip this code will look for an ``ENSUREPIP_OPTIONS`` environment
+    # variable which will control which version scripts get installed.
+    #
+    # ENSUREPIP_OPTIONS=altinstall
+    #   - Only pipX.Y and easy_install-X.Y will be generated and installed
+    # ENSUREPIP_OPTIONS=install
+    #   - pipX.Y, pipX, easy_install-X.Y will be generated and installed. Note
+    #     that this option is technically if ENSUREPIP_OPTIONS is set and is
+    #     not altinstall
+    # DEFAULT
+    #   - The default behavior is to install pip, pipX, pipX.Y, easy_install
+    #     and easy_install-X.Y.
+    pip_script = console.pop("pip", None)
+    if pip_script:
+        if "ENSUREPIP_OPTIONS" not in os.environ:
+            scripts_to_generate.append("pip = " + pip_script)
+
+        if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall":
+            scripts_to_generate.append(f"pip{sys.version_info[0]} = {pip_script}")
+
+        scripts_to_generate.append(f"pip{get_major_minor_version()} = {pip_script}")
+        # Delete any other versioned pip entry points
+        pip_ep = [k for k in console if re.match(r"pip(\d+(\.\d+)?)?$", k)]
+        for k in pip_ep:
+            del console[k]
+    easy_install_script = console.pop("easy_install", None)
+    if easy_install_script:
+        if "ENSUREPIP_OPTIONS" not in os.environ:
+            scripts_to_generate.append("easy_install = " + easy_install_script)
+
+        scripts_to_generate.append(
+            f"easy_install-{get_major_minor_version()} = {easy_install_script}"
+        )
+        # Delete any other versioned easy_install entry points
+        easy_install_ep = [
+            k for k in console if re.match(r"easy_install(-\d+\.\d+)?$", k)
+        ]
+        for k in easy_install_ep:
+            del console[k]
+
+    # Generate the console entry points specified in the wheel
+    scripts_to_generate.extend(starmap("{} = {}".format, console.items()))
+
+    return scripts_to_generate
+
+
+class ZipBackedFile:
+    def __init__(
+        self, src_record_path: RecordPath, dest_path: str, zip_file: ZipFile
+    ) -> None:
+        self.src_record_path = src_record_path
+        self.dest_path = dest_path
+        self._zip_file = zip_file
+        self.changed = False
+
+    def _getinfo(self) -> ZipInfo:
+        return self._zip_file.getinfo(self.src_record_path)
+
+    def save(self) -> None:
+        # When we open the output file below, any existing file is truncated
+        # before we start writing the new contents. This is fine in most
+        # cases, but can cause a segfault if pip has loaded a shared
+        # object (e.g. from pyopenssl through its vendored urllib3)
+        # Since the shared object is mmap'd an attempt to call a
+        # symbol in it will then cause a segfault. Unlinking the file
+        # allows writing of new contents while allowing the process to
+        # continue to use the old copy.
+        if os.path.exists(self.dest_path):
+            os.unlink(self.dest_path)
+
+        zipinfo = self._getinfo()
+
+        # optimization: the file is created by open(),
+        # skip the decompression when there is 0 bytes to decompress.
+        with open(self.dest_path, "wb") as dest:
+            if zipinfo.file_size > 0:
+                with self._zip_file.open(zipinfo) as f:
+                    blocksize = min(zipinfo.file_size, 1024 * 1024)
+                    shutil.copyfileobj(f, dest, blocksize)
+
+        if zip_item_is_executable(zipinfo):
+            set_extracted_file_to_default_mode_plus_executable(self.dest_path)
+
+
+class ScriptFile:
+    def __init__(self, file: File) -> None:
+        self._file = file
+        self.src_record_path = self._file.src_record_path
+        self.dest_path = self._file.dest_path
+        self.changed = False
+
+    def save(self) -> None:
+        self._file.save()
+        self.changed = fix_script(self.dest_path)
+
+
+class MissingCallableSuffix(InstallationError):
+    def __init__(self, entry_point: str) -> None:
+        super().__init__(
+            f"Invalid script entry point: {entry_point} - A callable "
+            "suffix is required. See https://packaging.python.org/"
+            "specifications/entry-points/#use-for-scripts for more "
+            "information."
+        )
+
+
+def _raise_for_invalid_entrypoint(specification: str, scripts_dir: str) -> None:
+    entry = get_export_entry(specification)
+    if entry is None:
+        return
+
+    if entry.suffix is None:
+        raise MissingCallableSuffix(str(entry))
+
+    # distlib joins the entry point name onto the scripts directory, so a name
+    # with path separators or ``..`` components can resolve elsewhere. The script
+    # must resolve to a path strictly inside the scripts directory.
+    dest = os.path.join(scripts_dir, entry.name)
+    resolves_to_scripts_dir = os.path.abspath(dest) == os.path.abspath(scripts_dir)
+    if resolves_to_scripts_dir or not is_within_directory(scripts_dir, dest):
+        raise InstallationError(
+            f"Invalid script entry point name {entry.name!r}: the script "
+            f"would be installed outside the scripts directory ({scripts_dir})."
+        )
+
+
+class PipScriptMaker(ScriptMaker):
+    # Override distlib's default script template with one that
+    # doesn't import `re` module, allowing scripts to load faster.
+    script_template = textwrap.dedent("""\
+        import sys
+        from %(module)s import %(import_name)s
+        if __name__ == '__main__':
+            sys.argv[0] = sys.argv[0].removesuffix('.exe')
+            sys.exit(%(func)s())
+""")
+
+    def make(
+        self, specification: str, options: dict[str, Any] | None = None
+    ) -> list[str]:
+        _raise_for_invalid_entrypoint(specification, self.target_dir)
+        return super().make(specification, options)
+
+
+def _install_wheel(  # noqa: C901, PLR0915 function is too long
+    name: str,
+    wheel_zip: ZipFile,
+    wheel_path: str,
+    scheme: Scheme,
+    pycompile: bool = True,
+    warn_script_location: bool = True,
+    direct_url: DirectUrl | None = None,
+    requested: bool = False,
+    script_executable: str | None = None,
+) -> None:
+    """Install a wheel.
+
+    :param name: Name of the project to install
+    :param wheel_zip: open ZipFile for wheel being installed
+    :param scheme: Distutils scheme dictating the install directories
+    :param req_description: String used in place of the requirement, for
+        logging
+    :param pycompile: Whether to byte-compile installed Python files
+    :param warn_script_location: Whether to check that scripts are installed
+        into a directory on PATH
+    :param script_executable: Python executable to use for console scripts
+    :raises UnsupportedWheel:
+        * when the directory holds an unpacked wheel with incompatible
+          Wheel-Version
+        * when the .dist-info dir does not match the wheel
+    """
+    info_dir, metadata = parse_wheel(wheel_zip, name)
+
+    if wheel_root_is_purelib(metadata):
+        lib_dir = scheme.purelib
+    else:
+        lib_dir = scheme.platlib
+
+    # Record details of the files moved
+    #   installed = files copied from the wheel to the destination
+    #   changed = files changed while installing (scripts #! line typically)
+    #   generated = files newly generated during the install (script wrappers)
+    installed: dict[RecordPath, RecordPath] = {}
+    changed: set[RecordPath] = set()
+    generated: list[str] = []
+
+    def record_installed(
+        srcfile: RecordPath, destfile: str, modified: bool = False
+    ) -> None:
+        """Map archive RECORD paths to installation RECORD paths."""
+        newpath = _fs_to_record_path(destfile, lib_dir)
+        installed[srcfile] = newpath
+        if modified:
+            changed.add(newpath)
+
+    def is_dir_path(path: RecordPath) -> bool:
+        return path.endswith("/")
+
+    def assert_no_path_traversal(dest_dir_path: str, target_path: str) -> None:
+        if not is_within_directory(dest_dir_path, target_path):
+            message = (
+                "The wheel {!r} has a file {!r} trying to install"
+                " outside the target directory {!r}"
+            )
+            raise InstallationError(
+                message.format(wheel_path, target_path, dest_dir_path)
+            )
+
+    def root_scheme_file_maker(
+        zip_file: ZipFile, dest: str
+    ) -> Callable[[RecordPath], File]:
+        def make_root_scheme_file(record_path: RecordPath) -> File:
+            normed_path = os.path.normpath(record_path)
+            dest_path = os.path.join(dest, normed_path)
+            assert_no_path_traversal(dest, dest_path)
+            return ZipBackedFile(record_path, dest_path, zip_file)
+
+        return make_root_scheme_file
+
+    def data_scheme_file_maker(
+        zip_file: ZipFile, scheme: Scheme
+    ) -> Callable[[RecordPath], File]:
+        scheme_paths = {key: getattr(scheme, key) for key in SCHEME_KEYS}
+
+        def make_data_scheme_file(record_path: RecordPath) -> File:
+            normed_path = os.path.normpath(record_path)
+            try:
+                _, scheme_key, dest_subpath = normed_path.split(os.path.sep, 2)
+            except ValueError:
+                message = (
+                    f"Unexpected file in {wheel_path}: {record_path!r}. .data directory"
+                    " contents should be named like: '/'."
+                )
+                raise InstallationError(message)
+
+            try:
+                scheme_path = scheme_paths[scheme_key]
+            except KeyError:
+                valid_scheme_keys = ", ".join(sorted(scheme_paths))
+                message = (
+                    f"Unknown scheme key used in {wheel_path}: {scheme_key} "
+                    f"(for file {record_path!r}). .data directory contents "
+                    f"should be in subdirectories named with a valid scheme "
+                    f"key ({valid_scheme_keys})"
+                )
+                raise InstallationError(message)
+
+            dest_path = os.path.join(scheme_path, dest_subpath)
+            assert_no_path_traversal(scheme_path, dest_path)
+            return ZipBackedFile(record_path, dest_path, zip_file)
+
+        return make_data_scheme_file
+
+    def is_data_scheme_path(path: RecordPath) -> bool:
+        return path.split("/", 1)[0].endswith(".data")
+
+    paths = cast(list[RecordPath], wheel_zip.namelist())
+    file_paths = filterfalse(is_dir_path, paths)
+    root_scheme_paths, data_scheme_paths = partition(is_data_scheme_path, file_paths)
+
+    make_root_scheme_file = root_scheme_file_maker(wheel_zip, lib_dir)
+    files: Iterator[File] = map(make_root_scheme_file, root_scheme_paths)
+
+    def is_script_scheme_path(path: RecordPath) -> bool:
+        parts = path.split("/", 2)
+        return len(parts) > 2 and parts[0].endswith(".data") and parts[1] == "scripts"
+
+    other_scheme_paths, script_scheme_paths = partition(
+        is_script_scheme_path, data_scheme_paths
+    )
+
+    make_data_scheme_file = data_scheme_file_maker(wheel_zip, scheme)
+    other_scheme_files = map(make_data_scheme_file, other_scheme_paths)
+    files = chain(files, other_scheme_files)
+
+    # Get the defined entry points
+    distribution = get_wheel_distribution(
+        FilesystemWheel(wheel_path),
+        canonicalize_name(name),
+    )
+    console, gui = get_entrypoints(distribution)
+
+    def is_entrypoint_wrapper(file: File) -> bool:
+        # EP, EP.exe and EP-script.py are scripts generated for
+        # entry point EP by setuptools
+        path = file.dest_path
+        name = os.path.basename(path)
+        if name.lower().endswith(".exe"):
+            matchname = name[:-4]
+        elif name.lower().endswith("-script.py"):
+            matchname = name[:-10]
+        elif name.lower().endswith(".pya"):
+            matchname = name[:-4]
+        else:
+            matchname = name
+        # Ignore setuptools-generated scripts
+        return matchname in console or matchname in gui
+
+    script_scheme_files: Iterator[File] = map(
+        make_data_scheme_file, script_scheme_paths
+    )
+    script_scheme_files = filterfalse(is_entrypoint_wrapper, script_scheme_files)
+    script_scheme_files = map(ScriptFile, script_scheme_files)
+    files = chain(files, script_scheme_files)
+
+    existing_parents = set()
+    for file in files:
+        # directory creation is lazy and after file filtering
+        # to ensure we don't install empty dirs; empty dirs can't be
+        # uninstalled.
+        parent_dir = os.path.dirname(file.dest_path)
+        if parent_dir not in existing_parents:
+            ensure_dir(parent_dir)
+            existing_parents.add(parent_dir)
+        file.save()
+        record_installed(file.src_record_path, file.dest_path, file.changed)
+
+    def pyc_source_file_paths() -> Generator[str, None, None]:
+        # We de-duplicate installation paths, since there can be overlap (e.g.
+        # file in .data maps to same location as file in wheel root).
+        # Sorting installation paths makes it easier to reproduce and debug
+        # issues related to permissions on existing files.
+        for installed_path in sorted(set(installed.values())):
+            full_installed_path = os.path.join(lib_dir, installed_path)
+            if not os.path.isfile(full_installed_path):
+                continue
+            if not full_installed_path.endswith(".py"):
+                continue
+            yield full_installed_path
+
+    def pyc_output_path(path: str) -> str:
+        """Return the path the pyc file would have been written to."""
+        return importlib.util.cache_from_source(path)
+
+    # Compile all of the pyc files for the installed files
+    if pycompile:
+        with contextlib.redirect_stdout(
+            StreamWrapper.from_stream(sys.stdout)
+        ) as stdout:
+            with warnings.catch_warnings():
+                warnings.filterwarnings("ignore")
+                for path in pyc_source_file_paths():
+                    success = compileall.compile_file(path, force=True, quiet=True)
+                    if success:
+                        pyc_path = pyc_output_path(path)
+                        assert os.path.exists(pyc_path)
+                        pyc_record_path = cast(
+                            "RecordPath", pyc_path.replace(os.path.sep, "/")
+                        )
+                        record_installed(pyc_record_path, pyc_path)
+        logger.debug(stdout.getvalue())
+
+    maker = PipScriptMaker(None, scheme.scripts)
+
+    # Embed the target environment's interpreter in console-script launchers
+    # rather than the one running pip, so an in-process install into another
+    # environment (e.g. a venv build environment) produces working launchers.
+    if script_executable is not None:
+        maker.executable = script_executable  # type: ignore  # it's untyped in distlib
+
+    # Ensure old scripts are overwritten.
+    # See https://github.com/pypa/pip/issues/1800
+    maker.clobber = True
+
+    # Ensure we don't generate any variants for scripts because this is almost
+    # never what somebody wants.
+    # See https://bitbucket.org/pypa/distlib/issue/35/
+    maker.variants = {""}
+
+    # This is required because otherwise distlib creates scripts that are not
+    # executable.
+    # See https://bitbucket.org/pypa/distlib/issue/32/
+    maker.set_mode = True
+
+    # Generate the console and GUI entry points specified in the wheel
+    scripts_to_generate = get_console_script_specs(console)
+
+    gui_scripts_to_generate = list(starmap("{} = {}".format, gui.items()))
+
+    generated_console_scripts = maker.make_multiple(scripts_to_generate)
+    generated.extend(generated_console_scripts)
+
+    generated.extend(maker.make_multiple(gui_scripts_to_generate, {"gui": True}))
+
+    if warn_script_location:
+        msg = message_about_scripts_not_on_PATH(generated_console_scripts)
+        if msg is not None:
+            logger.warning(msg)
+
+    generated_file_mode = 0o666 & ~current_umask()
+
+    @contextlib.contextmanager
+    def _generate_file(path: str, **kwargs: Any) -> Generator[BinaryIO, None, None]:
+        with adjacent_tmp_file(path, **kwargs) as f:
+            yield f
+        os.chmod(f.name, generated_file_mode)
+        replace(f.name, path)
+
+    dest_info_dir = os.path.join(lib_dir, info_dir)
+
+    # Record pip as the installer
+    installer_path = os.path.join(dest_info_dir, "INSTALLER")
+    with _generate_file(installer_path) as installer_file:
+        installer_file.write(b"pip\n")
+    generated.append(installer_path)
+
+    # Record the PEP 610 direct URL reference
+    if direct_url is not None:
+        direct_url_path = os.path.join(dest_info_dir, DIRECT_URL_METADATA_NAME)
+        with _generate_file(direct_url_path) as direct_url_file:
+            direct_url_file.write(direct_url.to_json().encode("utf-8"))
+        generated.append(direct_url_path)
+
+    # Record the REQUESTED file
+    if requested:
+        requested_path = os.path.join(dest_info_dir, "REQUESTED")
+        with open(requested_path, "wb"):
+            pass
+        generated.append(requested_path)
+
+    record_text = distribution.read_text("RECORD")
+    record_rows = list(csv.reader(record_text.splitlines()))
+
+    rows = get_csv_rows_for_installed(
+        record_rows,
+        installed=installed,
+        changed=changed,
+        generated=generated,
+        lib_dir=lib_dir,
+    )
+
+    # Record details of all files installed
+    record_path = os.path.join(dest_info_dir, "RECORD")
+
+    with _generate_file(record_path, **csv_io_kwargs("w")) as record_file:
+        # Explicitly cast to typing.IO[str] as a workaround for the mypy error:
+        # "writer" has incompatible type "BinaryIO"; expected "_Writer"
+        writer = csv.writer(cast("IO[str]", record_file))
+        writer.writerows(_normalized_outrows(rows))
+
+
+@contextlib.contextmanager
+def req_error_context(req_description: str) -> Generator[None, None, None]:
+    try:
+        yield
+    except InstallationError as e:
+        message = f"For req: {req_description}. {e.args[0]}"
+        raise InstallationError(message) from e
+
+
+def install_wheel(
+    name: str,
+    wheel_path: str,
+    scheme: Scheme,
+    req_description: str,
+    pycompile: bool = True,
+    warn_script_location: bool = True,
+    direct_url: DirectUrl | None = None,
+    requested: bool = False,
+    script_executable: str | None = None,
+) -> None:
+    with ZipFile(wheel_path, allowZip64=True) as z:
+        with req_error_context(req_description):
+            _install_wheel(
+                name=name,
+                wheel_zip=z,
+                wheel_path=wheel_path,
+                scheme=scheme,
+                pycompile=pycompile,
+                warn_script_location=warn_script_location,
+                direct_url=direct_url,
+                requested=requested,
+                script_executable=script_executable,
+            )
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/operations/prepare.py b/venv/lib/python3.12/site-packages/pip/_internal/operations/prepare.py
new file mode 100644
index 0000000..34a07a1
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/operations/prepare.py
@@ -0,0 +1,896 @@
+"""Prepares a distribution for installation"""
+
+# The following comment should be removed at some point in the future.
+# mypy: strict-optional=False
+from __future__ import annotations
+
+import mimetypes
+import os
+import shutil
+from collections.abc import Iterable
+from dataclasses import dataclass
+from pathlib import Path
+from typing import TYPE_CHECKING
+
+from pip._vendor.packaging.requirements import InvalidRequirement
+from pip._vendor.packaging.utils import canonicalize_name
+
+from pip._internal.build_env import BuildEnvironmentInstaller, BuildIsolationMode
+from pip._internal.distributions import make_distribution_for_install_requirement
+from pip._internal.distributions.installed import InstalledDistribution
+from pip._internal.exceptions import (
+    DirectoryUrlHashUnsupported,
+    HashMismatch,
+    HashUnpinned,
+    InstallationError,
+    MetadataInconsistent,
+    MetadataInvalid,
+    NetworkConnectionError,
+    SidecarMetadataInconsistent,
+    VcsHashUnsupported,
+)
+from pip._internal.index.package_finder import PackageFinder
+from pip._internal.metadata import BaseDistribution, get_metadata_distribution
+from pip._internal.models.direct_url import ArchiveInfo, DirectUrl
+from pip._internal.models.link import Link, join_within_directory
+from pip._internal.models.wheel import Wheel
+from pip._internal.network.download import Downloader
+from pip._internal.network.lazy_wheel import (
+    HTTPRangeRequestUnsupported,
+    dist_from_wheel_url,
+)
+from pip._internal.network.session import PipSession
+from pip._internal.operations.build.build_tracker import BuildTracker
+from pip._internal.req.req_install import InstallRequirement
+from pip._internal.utils._log import getLogger
+from pip._internal.utils.direct_url_helpers import (
+    direct_url_for_editable,
+    direct_url_from_link,
+)
+from pip._internal.utils.hashes import Hashes, MissingHashes
+from pip._internal.utils.logging import indent_log
+from pip._internal.utils.misc import (
+    display_path,
+    hash_file,
+    hide_url,
+    redact_auth_from_requirement,
+)
+from pip._internal.utils.packaging import get_requirement
+from pip._internal.utils.temp_dir import TempDirectory
+from pip._internal.utils.unpacking import unpack_file
+from pip._internal.vcs import vcs
+
+if TYPE_CHECKING:
+    from pip._internal.cli.progress_bars import BarType
+
+logger = getLogger(__name__)
+
+
+def _get_prepared_distribution(
+    req: InstallRequirement,
+    build_tracker: BuildTracker,
+    build_env_installer: BuildEnvironmentInstaller,
+    build_isolation: BuildIsolationMode,
+    check_build_deps: bool,
+    allow_editables: bool,
+) -> BaseDistribution:
+    """Prepare a distribution for installation."""
+    abstract_dist = make_distribution_for_install_requirement(req)
+    tracker_id = abstract_dist.build_tracker_id
+    if tracker_id is not None:
+        with build_tracker.track(req, tracker_id):
+            abstract_dist.prepare_distribution_metadata(
+                build_env_installer, build_isolation, check_build_deps, allow_editables
+            )
+    return abstract_dist.get_metadata_distribution()
+
+
+def unpack_vcs_link(link: Link, location: str, verbosity: int) -> None:
+    vcs_backend = vcs.get_backend_for_scheme(link.scheme)
+    assert vcs_backend is not None
+    vcs_backend.unpack(location, url=hide_url(link.url), verbosity=verbosity)
+
+
+@dataclass
+class File:
+    path: str
+    content_type: str | None = None
+
+    def __post_init__(self) -> None:
+        if self.content_type is None:
+            # Try to guess the file's MIME type. If the system MIME tables
+            # can't be loaded, give up.
+            try:
+                self.content_type = mimetypes.guess_type(self.path)[0]
+            except OSError:
+                pass
+
+
+def get_http_url(
+    link: Link,
+    download: Downloader,
+    download_dir: str | None = None,
+    hashes: Hashes | None = None,
+) -> File:
+    temp_dir = TempDirectory(kind="unpack", globally_managed=True)
+    # If a download dir is specified, is the file already downloaded there?
+    already_downloaded_path = None
+    if download_dir:
+        already_downloaded_path = _check_download_dir(link, download_dir, hashes)
+
+    if already_downloaded_path:
+        from_path = already_downloaded_path
+        content_type = None
+    else:
+        # let's download to a tmp dir
+        from_path, content_type = download(link, temp_dir.path)
+        if hashes:
+            hashes.check_against_path(from_path)
+
+    return File(from_path, content_type)
+
+
+def get_file_url(
+    link: Link, download_dir: str | None = None, hashes: Hashes | None = None
+) -> File:
+    """Get file and optionally check its hash."""
+    # If a download dir is specified, is the file already there and valid?
+    already_downloaded_path = None
+    if download_dir:
+        already_downloaded_path = _check_download_dir(link, download_dir, hashes)
+
+    if already_downloaded_path:
+        from_path = already_downloaded_path
+    else:
+        from_path = link.file_path
+
+    # If --require-hashes is off, `hashes` is either empty, the
+    # link's embedded hash, or MissingHashes; it is required to
+    # match. If --require-hashes is on, we are satisfied by any
+    # hash in `hashes` matching: a URL-based or an option-based
+    # one; no internet-sourced hash will be in `hashes`.
+    if hashes:
+        hashes.check_against_path(from_path)
+    return File(from_path, None)
+
+
+def unpack_url(
+    link: Link,
+    location: str,
+    download: Downloader,
+    verbosity: int,
+    download_dir: str | None = None,
+    hashes: Hashes | None = None,
+) -> File | None:
+    """Unpack link into location, downloading if required.
+
+    :param hashes: A Hashes object, one of whose embedded hashes must match,
+        or HashMismatch will be raised. If the Hashes is empty, no matches are
+        required, and unhashable types of requirements (like VCS ones, which
+        would ordinarily raise HashUnsupported) are allowed.
+    """
+    # non-editable vcs urls
+    if link.is_vcs:
+        unpack_vcs_link(link, location, verbosity=verbosity)
+        return None
+
+    assert not link.is_existing_dir()
+
+    # file urls
+    if link.is_file:
+        file = get_file_url(link, download_dir, hashes=hashes)
+
+    # http urls
+    else:
+        file = get_http_url(
+            link,
+            download,
+            download_dir,
+            hashes=hashes,
+        )
+
+    # unpack the archive to the build dir location. even when only downloading
+    # archives, they have to be unpacked to parse dependencies, except wheels
+    if not link.is_wheel:
+        unpack_file(file.path, location, file.content_type)
+
+    return file
+
+
+def _check_download_dir(
+    link: Link,
+    download_dir: str,
+    hashes: Hashes | None,
+    warn_on_hash_mismatch: bool = True,
+) -> str | None:
+    """Check download_dir for previously downloaded file with correct hash
+    If a correct file is found return its path else None
+    """
+    download_path = join_within_directory(download_dir, link.filename)
+
+    if not os.path.exists(download_path):
+        return None
+
+    # If already downloaded, does its hash match?
+    logger.info("File was already downloaded %s", download_path)
+    if hashes:
+        try:
+            hashes.check_against_path(download_path)
+        except HashMismatch:
+            if warn_on_hash_mismatch:
+                logger.warning(
+                    "Previously-downloaded file %s has bad hash. Re-downloading.",
+                    download_path,
+                )
+            os.unlink(download_path)
+            return None
+    return download_path
+
+
+def _canonicalize_requirement(raw: str) -> str:
+    """Return a normalized form of a PEP 508 requirement string.
+
+    Used to compare ``Requires-Dist`` entries from two sources of metadata
+    for the same distribution without being confused by superficial
+    differences in name casing, extra casing, or extras ordering. May raise
+    ``InvalidRequirement`` if ``raw`` is not a valid PEP 508 string.
+
+    TODO: once https://github.com/pypa/packaging/pull/1278 is released and
+    vendored, ``Requirement.__eq__`` will canonicalize requested extras and
+    this manual normalization can be dropped in favour of comparing
+    ``Requirement`` objects directly.
+    """
+    parsed = get_requirement(raw)
+    parts: list[str] = [canonicalize_name(parsed.name)]
+    if parsed.extras:
+        normalized_extras = sorted(canonicalize_name(e) for e in parsed.extras)
+        parts.append(f"[{','.join(normalized_extras)}]")
+    if parsed.specifier:
+        parts.append(str(parsed.specifier))
+    if parsed.url:
+        parts.append(f" @ {parsed.url}")
+    if parsed.marker:
+        parts.append(f"; {parsed.marker}")
+    return "".join(parts)
+
+
+def _canonical_requires(
+    req: InstallRequirement, dist: BaseDistribution, source: str
+) -> frozenset[str]:
+    """Return the canonicalized ``Requires-Dist`` entries of ``dist``.
+
+    ``source`` describes which metadata file ``dist`` was parsed from, for
+    use in error messages.
+    """
+    canonical: set[str] = set()
+    for raw in dist.iter_raw_dependencies():
+        try:
+            # strip() because a folded metadata header may be returned
+            # with a leading newline; iter_dependencies() strips for the
+            # same reason.
+            canonical.add(_canonicalize_requirement(raw.strip()))
+        except InvalidRequirement as e:
+            raise MetadataInvalid(req, f"Requires-Dist in {source}: {e}")
+    return frozenset(canonical)
+
+
+def _check_sidecar_matches_wheel(
+    req: InstallRequirement,
+    sidecar_dist: BaseDistribution,
+    wheel_dist: BaseDistribution,
+) -> None:
+    """Check that a .metadata-based distribution matches the wheel's METADATA.
+
+    Compare ``Name``, ``Version``, ``Requires-Dist``, ``Requires-Python``
+    and ``Provides-Extra`` between the two and abort the install on any
+    mismatch as PEP 658 requires the metadata files "MUST be identical".
+
+    While the PEP doesn't mandate that consumers enforce the identical
+    requirement, it's good nonetheless to check to prevent confusing
+    behaviour when an index misbehaves.
+
+    Also note for name and version, pip usually rejects wheels if they're
+    inconsistent already. Checking them again here is purely defensive.
+    """
+
+    sidecar_name = canonicalize_name(sidecar_dist.raw_name)
+    wheel_name = canonicalize_name(wheel_dist.raw_name)
+    if sidecar_name != wheel_name:
+        raise SidecarMetadataInconsistent(req, "Name", sidecar_name, wheel_name)
+
+    if sidecar_dist.version != wheel_dist.version:
+        raise SidecarMetadataInconsistent(
+            req,
+            "Version",
+            str(sidecar_dist.version),
+            str(wheel_dist.version),
+        )
+
+    # For multi-use fields, only report the symmetric difference to avoid
+    # unnecessarily flagging matching values.
+    sidecar_requires = _canonical_requires(
+        req, sidecar_dist, "the PEP 658 .metadata file"
+    )
+    wheel_requires = _canonical_requires(req, wheel_dist, "the wheel's METADATA")
+    if sidecar_requires != wheel_requires:
+        raise SidecarMetadataInconsistent(
+            req,
+            "Requires-Dist",
+            ", ".join(sorted(sidecar_requires - wheel_requires)),
+            ", ".join(sorted(wheel_requires - sidecar_requires)),
+        )
+
+    if sidecar_dist.requires_python != wheel_dist.requires_python:
+        raise SidecarMetadataInconsistent(
+            req,
+            "Requires-Python",
+            str(sidecar_dist.requires_python),
+            str(wheel_dist.requires_python),
+        )
+
+    sidecar_extras = frozenset(sidecar_dist.iter_provided_extras())
+    wheel_extras = frozenset(wheel_dist.iter_provided_extras())
+    if sidecar_extras != wheel_extras:
+        raise SidecarMetadataInconsistent(
+            req,
+            "Provides-Extra",
+            ", ".join(sorted(sidecar_extras - wheel_extras)),
+            ", ".join(sorted(wheel_extras - sidecar_extras)),
+        )
+
+
+class RequirementPreparer:
+    """Prepares a Requirement"""
+
+    def __init__(
+        self,
+        *,
+        build_dir: str,
+        download_dir: str | None,
+        src_dir: str,
+        build_isolation: BuildIsolationMode,
+        build_isolation_installer: BuildEnvironmentInstaller,
+        check_build_deps: bool,
+        build_tracker: BuildTracker,
+        session: PipSession,
+        progress_bar: BarType,
+        finder: PackageFinder,
+        require_hashes: bool,
+        use_user_site: bool,
+        lazy_wheel: bool,
+        verbosity: int,
+        legacy_resolver: bool,
+        allow_editables: bool,
+    ) -> None:
+        super().__init__()
+
+        self.src_dir = src_dir
+        self.build_dir = build_dir
+        self.build_tracker = build_tracker
+        self._session = session
+        self._download = Downloader(session, progress_bar)
+        self.finder = finder
+
+        # Where still-packed archives should be written to. If None, they are
+        # not saved, and are deleted immediately after unpacking.
+        self.download_dir = download_dir
+
+        # Is build isolation allowed?
+        self.build_isolation = build_isolation
+        self.build_env_installer = build_isolation_installer
+
+        # Should check build dependencies?
+        self.check_build_deps = check_build_deps
+
+        # Should hash-checking be required?
+        self.require_hashes = require_hashes
+
+        # Should install in user site-packages?
+        self.use_user_site = use_user_site
+
+        # Should wheels be downloaded lazily?
+        self.use_lazy_wheel = lazy_wheel
+
+        # How verbose should underlying tooling be?
+        self.verbosity = verbosity
+
+        # Are we using the legacy resolver?
+        self.legacy_resolver = legacy_resolver
+
+        # Memoized downloaded files, as mapping of url: path.
+        self._downloaded: dict[str, str] = {}
+
+        # Previous "header" printed for a link-based InstallRequirement
+        self._previous_requirement_header = ("", "")
+
+        # Do we allow preparing editable wheels?
+        # When using pip wheel, editables must still produce regular wheels
+        self.allow_editables = allow_editables
+
+    def _log_preparing_link(self, req: InstallRequirement) -> None:
+        """Provide context for the requirement being prepared."""
+        if req.link.is_file and not req.is_wheel_from_cache:
+            message = "Processing %s"
+            information = str(display_path(req.link.file_path))
+        else:
+            message = "Collecting %s"
+            information = redact_auth_from_requirement(req.req) if req.req else str(req)
+
+        # If we used req.req, inject requirement source if available (this
+        # would already be included if we used req directly)
+        if req.req and req.comes_from:
+            if isinstance(req.comes_from, str):
+                comes_from: str | None = req.comes_from
+            else:
+                comes_from = req.comes_from.from_path()
+            if comes_from:
+                information += f" (from {comes_from})"
+
+        if (message, information) != self._previous_requirement_header:
+            self._previous_requirement_header = (message, information)
+            logger.info(message, information)
+
+        if req.is_wheel_from_cache:
+            with indent_log():
+                logger.info("Using cached %s", req.link.filename)
+
+    def _ensure_link_req_src_dir(
+        self, req: InstallRequirement, parallel_builds: bool
+    ) -> None:
+        """Ensure source_dir of a linked InstallRequirement."""
+        # Since source_dir is only set for editable requirements.
+        if req.link.is_wheel:
+            # We don't need to unpack wheels, so no need for a source
+            # directory.
+            return
+        assert req.source_dir is None
+        if req.link.is_existing_dir():
+            # build local directories in-tree
+            req.source_dir = req.link.file_path
+            return
+
+        # We always delete unpacked sdists after pip runs.
+        req.ensure_has_source_dir(
+            self.build_dir,
+            autodelete=True,
+            parallel_builds=parallel_builds,
+        )
+        req.ensure_pristine_source_checkout()
+
+    def _get_linked_req_hashes(self, req: InstallRequirement) -> Hashes:
+        # By the time this is called, the requirement's link should have
+        # been checked so we can tell what kind of requirements req is
+        # and raise some more informative errors than otherwise.
+        # (For example, we can raise VcsHashUnsupported for a VCS URL
+        # rather than HashMissing.)
+        if not self.require_hashes:
+            return req.hashes(trust_internet=True)
+
+        # We could check these first 2 conditions inside unpack_url
+        # and save repetition of conditions, but then we would
+        # report less-useful error messages for unhashable
+        # requirements, complaining that there's no hash provided.
+        if req.link.is_vcs:
+            raise VcsHashUnsupported()
+        if req.link.is_existing_dir():
+            raise DirectoryUrlHashUnsupported()
+
+        # Unpinned packages are asking for trouble when a new version
+        # is uploaded.  This isn't a security check, but it saves users
+        # a surprising hash mismatch in the future.
+        # file:/// URLs aren't pinnable, so don't complain about them
+        # not being pinned.
+        if not req.is_direct and not req.is_pinned:
+            raise HashUnpinned()
+
+        # If known-good hashes are missing for this requirement,
+        # shim it with a facade object that will provoke hash
+        # computation and then raise a HashMissing exception
+        # showing the user what the hash should be.
+        return req.hashes(trust_internet=False) or MissingHashes()
+
+    def _fetch_metadata_only(
+        self,
+        req: InstallRequirement,
+    ) -> BaseDistribution | None:
+        if self.legacy_resolver:
+            logger.debug(
+                "Metadata-only fetching is not used in the legacy resolver",
+            )
+            return None
+        if self.require_hashes:
+            logger.debug(
+                "Metadata-only fetching is not used as hash checking is required",
+            )
+            return None
+        # Try PEP 658 metadata first, then fall back to lazy wheel if unavailable.
+        return self._fetch_metadata_using_link_data_attr(
+            req
+        ) or self._fetch_metadata_using_lazy_wheel(req.link)
+
+    def _fetch_metadata_using_link_data_attr(
+        self,
+        req: InstallRequirement,
+    ) -> BaseDistribution | None:
+        """Fetch metadata from the data-dist-info-metadata attribute, if possible."""
+        # (1) Get the link to the metadata file, if provided by the backend.
+        metadata_link = req.link.metadata_link()
+        if metadata_link is None:
+            return None
+        assert req.req is not None
+        logger.verbose(
+            "Obtaining dependency information for %s from %s",
+            req.req,
+            metadata_link,
+        )
+        # (2) Download the contents of the METADATA file, separate from the dist itself.
+        metadata_file = get_http_url(
+            metadata_link,
+            self._download,
+            hashes=metadata_link.as_hashes(),
+        )
+        with open(metadata_file.path, "rb") as f:
+            metadata_contents = f.read()
+        # (3) Generate a dist just from those file contents.
+        metadata_dist = get_metadata_distribution(
+            metadata_contents,
+            req.link.filename,
+            req.req.name,
+        )
+        # (4) Ensure the Name: field from the METADATA file matches the name from the
+        #     install requirement.
+        #
+        #     NB: raw_name will fall back to the name from the install requirement if
+        #     the Name: field is not present, but it's noted in the raw_name docstring
+        #     that that should NEVER happen anyway.
+        if canonicalize_name(metadata_dist.raw_name) != canonicalize_name(req.req.name):
+            raise MetadataInconsistent(
+                req, "Name", req.req.name, metadata_dist.raw_name
+            )
+        return metadata_dist
+
+    def _fetch_metadata_using_lazy_wheel(
+        self,
+        link: Link,
+    ) -> BaseDistribution | None:
+        """Fetch metadata using lazy wheel, if possible."""
+        # --use-feature=fast-deps must be provided.
+        if not self.use_lazy_wheel:
+            return None
+        if link.is_file or not link.is_wheel:
+            logger.debug(
+                "Lazy wheel is not used as %r does not point to a remote wheel",
+                link,
+            )
+            return None
+
+        wheel = Wheel(link.filename)
+        name = wheel.name
+        logger.info(
+            "Obtaining dependency information from %s %s",
+            name,
+            wheel.version,
+        )
+        url = link.url.split("#", 1)[0]
+        try:
+            return dist_from_wheel_url(name, url, self._session)
+        except HTTPRangeRequestUnsupported:
+            logger.debug("%s does not support range requests", url)
+            return None
+
+    def _complete_partial_requirements(
+        self,
+        partially_downloaded_reqs: Iterable[InstallRequirement],
+        parallel_builds: bool = False,
+    ) -> None:
+        """Download any requirements which were only fetched by metadata."""
+        # Download to a temporary directory. These will be copied over as
+        # needed for downstream 'download', 'wheel', and 'install' commands.
+        temp_dir = TempDirectory(kind="unpack", globally_managed=True).path
+
+        # Map each link to the requirement that owns it. This allows us to set
+        # `req.local_file_path` on the appropriate requirement after passing
+        # all the links at once into BatchDownloader.
+        links_to_fully_download: dict[Link, InstallRequirement] = {}
+        for req in partially_downloaded_reqs:
+            assert req.link
+            links_to_fully_download[req.link] = req
+
+        batch_download = self._download.batch(links_to_fully_download.keys(), temp_dir)
+        for link, (filepath, _) in batch_download:
+            logger.debug("Downloading link %s to %s", link, filepath)
+            req = links_to_fully_download[link]
+            # Record the downloaded file path so wheel reqs can extract a Distribution
+            # in .get_dist().
+            req.local_file_path = filepath
+            # Record that the file is downloaded so we don't do it again in
+            # _prepare_linked_requirement().
+            self._downloaded[req.link.url] = filepath
+
+            # If this is an sdist, we need to unpack it after downloading, but the
+            # .source_dir won't be set up until we are in _prepare_linked_requirement().
+            # Add the downloaded archive to the install requirement to unpack after
+            # preparing the source dir.
+            if not req.is_wheel:
+                req.needs_unpacked_archive(Path(filepath))
+
+        # This step is necessary to ensure all lazy wheels are processed
+        # successfully by the 'download', 'wheel', and 'install' commands.
+        for req in partially_downloaded_reqs:
+            self._prepare_linked_requirement(req, parallel_builds)
+
+    def prepare_linked_requirement(
+        self, req: InstallRequirement, parallel_builds: bool = False
+    ) -> BaseDistribution:
+        """Prepare a requirement to be obtained from req.link."""
+        assert req.link
+        self._log_preparing_link(req)
+        with indent_log():
+            # Check if the relevant file is already available
+            # in the download directory
+            file_path = None
+            if self.download_dir is not None and req.link.is_wheel:
+                hashes = self._get_linked_req_hashes(req)
+                file_path = _check_download_dir(
+                    req.link,
+                    self.download_dir,
+                    hashes,
+                    # When a locally built wheel has been found in cache, we don't warn
+                    # about re-downloading when the already downloaded wheel hash does
+                    # not match. This is because the hash must be checked against the
+                    # original link, not the cached link. It that case the already
+                    # downloaded file will be removed and re-fetched from cache (which
+                    # implies a hash check against the cache entry's origin.json).
+                    warn_on_hash_mismatch=not req.is_wheel_from_cache,
+                )
+
+            if file_path is not None:
+                # The file is already available, so mark it as downloaded
+                self._downloaded[req.link.url] = file_path
+            else:
+                # The file is not available, attempt to fetch only metadata
+                metadata_dist = self._fetch_metadata_only(req)
+                if metadata_dist is not None:
+                    req.needs_more_preparation = True
+                    req.set_dist(metadata_dist)
+                    # Ensure download_info is available even in dry-run mode
+                    if req.download_info is None:
+                        req.download_info = direct_url_from_link(
+                            req.link, req.source_dir
+                        )
+                    return metadata_dist
+
+            # None of the optimizations worked, fully prepare the requirement
+            return self._prepare_linked_requirement(req, parallel_builds)
+
+    def prepare_linked_requirements_more(
+        self, reqs: Iterable[InstallRequirement], parallel_builds: bool = False
+    ) -> None:
+        """Prepare linked requirements more, if needed."""
+        reqs = [req for req in reqs if req.needs_more_preparation]
+        for req in reqs:
+            # Determine if any of these requirements were already downloaded.
+            if self.download_dir is not None and req.link.is_wheel:
+                hashes = self._get_linked_req_hashes(req)
+                file_path = _check_download_dir(req.link, self.download_dir, hashes)
+                if file_path is not None:
+                    self._downloaded[req.link.url] = file_path
+                    req.needs_more_preparation = False
+
+        # Prepare requirements we found were already downloaded for some
+        # reason. The other downloads will be completed separately.
+        partially_downloaded_reqs: list[InstallRequirement] = []
+        for req in reqs:
+            if req.needs_more_preparation:
+                partially_downloaded_reqs.append(req)
+            else:
+                self._prepare_linked_requirement(req, parallel_builds)
+
+        # TODO: separate this part out from RequirementPreparer when the v1
+        # resolver can be removed!
+        self._complete_partial_requirements(
+            partially_downloaded_reqs,
+            parallel_builds=parallel_builds,
+        )
+
+    def _prepare_linked_requirement(
+        self, req: InstallRequirement, parallel_builds: bool
+    ) -> BaseDistribution:
+        assert req.link
+        link = req.link
+
+        hashes = self._get_linked_req_hashes(req)
+
+        if hashes and req.is_wheel_from_cache:
+            assert req.download_info is not None
+            assert link.is_wheel
+            assert link.is_file
+            # We need to verify hashes, and we have found the requirement in the cache
+            # of locally built wheels.
+            if (
+                req.download_info.archive_info
+                and req.download_info.archive_info.hashes
+                and hashes.has_one_of(req.download_info.archive_info.hashes)
+            ):
+                # At this point we know the requirement was built from a hashable source
+                # artifact, and we verified that the cache entry's hash of the original
+                # artifact matches one of the hashes we expect. We don't verify hashes
+                # against the cached wheel, because the wheel is not the original.
+                hashes = None
+            else:
+                logger.warning(
+                    "The hashes of the source archive found in cache entry "
+                    "don't match, ignoring cached built wheel "
+                    "and re-downloading source."
+                )
+                req.link = req.cached_wheel_source_link
+                link = req.link
+
+        self._ensure_link_req_src_dir(req, parallel_builds)
+
+        if link.is_existing_dir():
+            local_file = None
+        elif link.url not in self._downloaded:
+            try:
+                local_file = unpack_url(
+                    link,
+                    req.source_dir,
+                    self._download,
+                    self.verbosity,
+                    self.download_dir,
+                    hashes,
+                )
+            except NetworkConnectionError as exc:
+                raise InstallationError(
+                    f"Could not install requirement {req} because of HTTP "
+                    f"error {exc} for URL {link}"
+                )
+        else:
+            file_path = self._downloaded[link.url]
+            if hashes:
+                hashes.check_against_path(file_path)
+            local_file = File(file_path, content_type=None)
+
+        # If download_info is set, we got it from the wheel cache.
+        if req.download_info is None:
+            # Editables don't go through this function (see
+            # prepare_editable_requirement).
+            assert not req.editable
+            req.download_info = direct_url_from_link(link, req.source_dir)
+            # Make sure we have a hash in download_info. If we got it as part of the
+            # URL, it will have been verified and we can rely on it. Otherwise we
+            # compute it from the downloaded file.
+            # FIXME: https://github.com/pypa/pip/issues/11943
+            if (
+                req.download_info.archive_info
+                and not req.download_info.archive_info.hashes
+                and local_file
+            ):
+                hash = hash_file(local_file.path)[0].hexdigest()
+                # We populate archive_info.hashes. For backward compatibility,
+                # the legacy hash field will be generated when converting to JSON.
+                req.download_info = DirectUrl(
+                    url=req.download_info.url,
+                    archive_info=ArchiveInfo(hashes={"sha256": hash}),
+                    subdirectory=req.download_info.subdirectory,
+                )
+
+        # For use in later processing,
+        # preserve the file path on the requirement.
+        if local_file:
+            req.local_file_path = local_file.path
+
+        dist = _get_prepared_distribution(
+            req,
+            self.build_tracker,
+            self.build_env_installer,
+            self.build_isolation,
+            self.check_build_deps,
+            self.allow_editables,
+        )
+
+        # If a PEP 658 .metadata file was used, check that fields relevant for
+        # dependency resolution match with the wheel's METADATA file.
+        #
+        # NOTE: PEP 658 also permits .metadata files for source distributions,
+        # but PyPI doesn't serve such files. In addition, an sdist's metadata
+        # is generated at build time and may legitimately differ from what the
+        # index declared, so it's been decided to skip this check for sdists.
+        # This can change later if needed.
+        #
+        # TODO: this is a hack for checking whether a distribution is metadata-
+        # only or not. If/when we refactor distributions to delineate between
+        # metadata-only and concrete distributions, clean this up.
+        if (
+            link.is_wheel
+            and req._distribution is not None
+            and req._distribution is not dist
+            and link.metadata_link() is not None
+        ):
+            _check_sidecar_matches_wheel(req, req._distribution, dist)
+
+        return dist
+
+    def save_linked_requirement(self, req: InstallRequirement) -> None:
+        assert self.download_dir is not None
+        assert req.link is not None
+        link = req.link
+        if link.is_vcs or (link.is_existing_dir() and req.editable):
+            # Make a .zip of the source_dir we already created.
+            req.archive(self.download_dir)
+            return
+
+        if link.is_existing_dir():
+            logger.debug(
+                "Not copying link to destination directory "
+                "since it is a directory: %s",
+                link,
+            )
+            return
+        if req.local_file_path is None:
+            # No distribution was downloaded for this requirement.
+            return
+
+        download_location = join_within_directory(self.download_dir, link.filename)
+        if not os.path.exists(download_location):
+            shutil.copy(req.local_file_path, download_location)
+            download_path = display_path(download_location)
+            logger.info("Saved %s", download_path)
+
+    def prepare_editable_requirement(
+        self,
+        req: InstallRequirement,
+    ) -> BaseDistribution:
+        """Prepare an editable requirement."""
+        assert req.editable, "cannot prepare a non-editable req as editable"
+
+        logger.info("Obtaining %s", req)
+
+        with indent_log():
+            if self.require_hashes:
+                raise InstallationError(
+                    f"The editable requirement {req} cannot be installed when "
+                    "requiring hashes, because there is no single file to "
+                    "hash."
+                )
+            req.ensure_has_source_dir(self.src_dir)
+            req.update_editable()
+            assert req.source_dir
+            req.download_info = direct_url_for_editable(req.unpacked_source_directory)
+
+            dist = _get_prepared_distribution(
+                req,
+                self.build_tracker,
+                self.build_env_installer,
+                self.build_isolation,
+                self.check_build_deps,
+                self.allow_editables,
+            )
+
+            req.check_if_exists(self.use_user_site)
+
+        return dist
+
+    def prepare_installed_requirement(
+        self,
+        req: InstallRequirement,
+        skip_reason: str,
+    ) -> BaseDistribution:
+        """Prepare an already-installed requirement."""
+        assert req.satisfied_by, "req should have been satisfied but isn't"
+        assert skip_reason is not None, (
+            "did not get skip reason skipped but req.satisfied_by "
+            f"is set to {req.satisfied_by}"
+        )
+        logger.info(
+            "Requirement %s: %s (%s)", skip_reason, req, req.satisfied_by.version
+        )
+        with indent_log():
+            if self.require_hashes:
+                logger.debug(
+                    "Since it is already installed, we are trusting this "
+                    "package without checking its hash. To ensure a "
+                    "completely repeatable environment, install into an "
+                    "empty virtualenv."
+                )
+            return InstalledDistribution(req).get_metadata_distribution()
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/pyproject.py b/venv/lib/python3.12/site-packages/pip/_internal/pyproject.py
new file mode 100644
index 0000000..8c2f722
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/pyproject.py
@@ -0,0 +1,123 @@
+from __future__ import annotations
+
+import os
+from collections import namedtuple
+from typing import Any
+
+from pip._vendor.packaging.requirements import InvalidRequirement
+
+from pip._internal.exceptions import (
+    InstallationError,
+    InvalidPyProjectBuildRequires,
+    MissingPyProjectBuildRequires,
+)
+from pip._internal.utils.compat import tomllib
+from pip._internal.utils.packaging import get_requirement
+
+
+def _is_list_of_str(obj: Any) -> bool:
+    return isinstance(obj, list) and all(isinstance(item, str) for item in obj)
+
+
+def make_pyproject_path(unpacked_source_directory: str) -> str:
+    return os.path.join(unpacked_source_directory, "pyproject.toml")
+
+
+BuildSystemDetails = namedtuple(
+    "BuildSystemDetails", ["requires", "backend", "check", "backend_path"]
+)
+
+
+def load_pyproject_toml(
+    pyproject_toml: str, setup_py: str, req_name: str
+) -> BuildSystemDetails:
+    """Load the pyproject.toml file.
+
+    Parameters:
+        pyproject_toml - Location of the project's pyproject.toml file
+        setup_py - Location of the project's setup.py file
+        req_name - The name of the requirement we're processing (for
+                   error reporting)
+
+    Returns:
+        None if we should use the legacy code path, otherwise a tuple
+        (
+            requirements from pyproject.toml,
+            name of PEP 517 backend,
+            requirements we should check are installed after setting
+                up the build environment
+            directory paths to import the backend from (backend-path),
+                relative to the project root.
+        )
+    """
+    has_pyproject = os.path.isfile(pyproject_toml)
+    has_setup = os.path.isfile(setup_py)
+
+    if not has_pyproject and not has_setup:
+        raise InstallationError(
+            f"{req_name} does not appear to be a Python project: "
+            f"neither 'setup.py' nor 'pyproject.toml' found."
+        )
+
+    if has_pyproject:
+        with open(pyproject_toml, encoding="utf-8") as f:
+            pp_toml = tomllib.loads(f.read())
+        build_system = pp_toml.get("build-system")
+    else:
+        build_system = None
+
+    if build_system is None:
+        # In the absence of any explicit backend specification, we
+        # assume the setuptools backend that most closely emulates the
+        # traditional direct setup.py execution, and require wheel and
+        # a version of setuptools that supports that backend.
+
+        build_system = {
+            "requires": ["setuptools>=40.8.0"],
+            "build-backend": "setuptools.build_meta:__legacy__",
+        }
+
+    # Ensure that the build-system section in pyproject.toml conforms
+    # to PEP 518.
+
+    # Specifying the build-system table but not the requires key is invalid
+    if "requires" not in build_system:
+        raise MissingPyProjectBuildRequires(package=req_name)
+
+    # Error out if requires is not a list of strings
+    requires = build_system["requires"]
+    if not _is_list_of_str(requires):
+        raise InvalidPyProjectBuildRequires(
+            package=req_name,
+            reason="It is not a list of strings.",
+        )
+
+    # Each requirement must be valid as per PEP 508
+    for requirement in requires:
+        try:
+            get_requirement(requirement)
+        except InvalidRequirement as error:
+            raise InvalidPyProjectBuildRequires(
+                package=req_name,
+                reason=f"It contains an invalid requirement: {requirement!r}",
+            ) from error
+
+    backend = build_system.get("build-backend")
+    backend_path = build_system.get("backend-path", [])
+    check: list[str] = []
+    if backend is None:
+        # If the user didn't specify a backend, we assume they want to use
+        # the setuptools backend. But we can't be sure they have included
+        # a version of setuptools which supplies the backend. So we
+        # make a note to check that this requirement is present once
+        # we have set up the environment.
+        # This is quite a lot of work to check for a very specific case. But
+        # the problem is, that case is potentially quite common - projects that
+        # adopted PEP 518 early for the ability to specify requirements to
+        # execute setup.py, but never considered needing to mention the build
+        # tools themselves. The original PEP 518 code had a similar check (but
+        # implemented in a different way).
+        backend = "setuptools.build_meta:__legacy__"
+        check = ["setuptools>=40.8.0"]
+
+    return BuildSystemDetails(requires, backend, check, backend_path)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/req/__init__.py
new file mode 100644
index 0000000..ed2ca30
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/req/__init__.py
@@ -0,0 +1,105 @@
+from __future__ import annotations
+
+import collections
+import logging
+from collections.abc import Generator
+from dataclasses import dataclass
+
+from pip._internal.cli.progress_bars import BarType, get_install_progress_renderer
+from pip._internal.utils.logging import indent_log
+
+from .req_file import parse_requirements
+from .req_install import InstallRequirement
+from .req_set import RequirementSet
+
+__all__ = [
+    "RequirementSet",
+    "InstallRequirement",
+    "parse_requirements",
+    "install_given_reqs",
+]
+
+logger = logging.getLogger(__name__)
+
+
+@dataclass(frozen=True)
+class InstallationResult:
+    name: str
+
+
+def _validate_requirements(
+    requirements: list[InstallRequirement],
+) -> Generator[tuple[str, InstallRequirement], None, None]:
+    for req in requirements:
+        assert req.name, f"invalid to-be-installed requirement: {req}"
+        yield req.name, req
+
+
+def install_given_reqs(
+    requirements: list[InstallRequirement],
+    root: str | None,
+    home: str | None,
+    prefix: str | None,
+    warn_script_location: bool,
+    use_user_site: bool,
+    pycompile: bool,
+    progress_bar: BarType,
+    script_executable: str | None = None,
+) -> list[InstallationResult]:
+    """
+    Install everything in the given list.
+
+    (to be called after having downloaded and unpacked the packages)
+    """
+    to_install = collections.OrderedDict(_validate_requirements(requirements))
+
+    if to_install:
+        logger.info(
+            "Installing collected packages: %s",
+            ", ".join(to_install.keys()),
+        )
+
+    installed = []
+
+    show_progress = logger.isEnabledFor(logging.INFO) and len(to_install) > 1
+
+    items = iter(to_install.values())
+    if show_progress:
+        renderer = get_install_progress_renderer(
+            bar_type=progress_bar, total=len(to_install)
+        )
+        items = renderer(items)
+
+    with indent_log():
+        for requirement in items:
+            req_name = requirement.name
+            assert req_name is not None
+            if requirement.should_reinstall:
+                logger.info("Attempting uninstall: %s", req_name)
+                with indent_log():
+                    uninstalled_pathset = requirement.uninstall(auto_confirm=True)
+            else:
+                uninstalled_pathset = None
+
+            try:
+                requirement.install(
+                    root=root,
+                    home=home,
+                    prefix=prefix,
+                    warn_script_location=warn_script_location,
+                    use_user_site=use_user_site,
+                    pycompile=pycompile,
+                    script_executable=script_executable,
+                )
+            except Exception:
+                # if install did not succeed, rollback previous uninstall
+                if uninstalled_pathset and not requirement.install_succeeded:
+                    uninstalled_pathset.rollback()
+                raise
+            else:
+                if uninstalled_pathset and requirement.install_succeeded:
+                    uninstalled_pathset.commit()
+
+            installed.append(InstallationResult(req_name))
+
+    return installed
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..200450e
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/constructors.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/constructors.cpython-312.pyc
new file mode 100644
index 0000000..2e3ffcd
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/constructors.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/pep723.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/pep723.cpython-312.pyc
new file mode 100644
index 0000000..42357c8
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/pep723.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_dependency_group.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_dependency_group.cpython-312.pyc
new file mode 100644
index 0000000..456bc05
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_dependency_group.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_file.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_file.cpython-312.pyc
new file mode 100644
index 0000000..75c653b
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_file.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_install.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_install.cpython-312.pyc
new file mode 100644
index 0000000..73f0f48
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_install.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_set.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_set.cpython-312.pyc
new file mode 100644
index 0000000..da16765
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_set.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-312.pyc
new file mode 100644
index 0000000..9580582
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/constructors.py b/venv/lib/python3.12/site-packages/pip/_internal/req/constructors.py
new file mode 100644
index 0000000..4915894
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/req/constructors.py
@@ -0,0 +1,662 @@
+"""Backing implementation for InstallRequirement's various constructors
+
+The idea here is that these formed a major chunk of InstallRequirement's size
+so, moving them and support code dedicated to them outside of that class
+helps creates for better understandability for the rest of the code.
+
+These are meant to be used elsewhere within pip to create instances of
+InstallRequirement.
+"""
+
+from __future__ import annotations
+
+import copy
+import logging
+import os
+import re
+from collections.abc import Collection, Mapping
+from dataclasses import dataclass
+
+from pip._vendor.packaging import pylock
+from pip._vendor.packaging.markers import InvalidMarker, Marker
+from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
+from pip._vendor.packaging.utils import parse_sdist_filename, parse_wheel_filename
+
+from pip._internal.exceptions import InstallationError
+from pip._internal.models.index import PyPI, TestPyPI
+from pip._internal.models.link import Link
+from pip._internal.models.wheel import Wheel
+from pip._internal.req.req_file import ParsedRequirement
+from pip._internal.req.req_install import InstallRequirement
+from pip._internal.utils.filetypes import is_archive_file
+from pip._internal.utils.misc import is_installable_dir
+from pip._internal.utils.packaging import get_requirement
+from pip._internal.utils.pylock import (
+    package_archive_requirement_url,
+    package_directory_requirement_url,
+    package_sdist_requirement_url,
+    package_vcs_requirement_url,
+    package_wheel_requirement_url,
+)
+from pip._internal.utils.urls import path_to_url
+from pip._internal.vcs import is_url, vcs
+
+__all__ = [
+    "install_req_from_editable",
+    "install_req_from_line",
+    "parse_editable",
+]
+
+logger = logging.getLogger(__name__)
+
+# All standard version specifier operators
+# https://packaging.python.org/en/latest/specifications/version-specifiers/#id5
+operators = ("~=", "==", "!=", "<=", ">=", "<", ">", "===")
+
+
+def strip_extras(path: str) -> tuple[str, set[str]]:
+    m = re.match(r"^(.+)(\[[^\]]+\])$", path)
+    extras = None
+    if m:
+        path_no_extras = m.group(1).rstrip()
+        extras = m.group(2)
+    else:
+        path_no_extras = path
+
+    return path_no_extras, _convert_extras(extras)
+
+
+def _convert_extras(extras: str | None) -> set[str]:
+    if not extras:
+        return set()
+    return get_requirement("placeholder" + extras.lower()).extras
+
+
+def _set_requirement_extras(req: Requirement, new_extras: set[str]) -> Requirement:
+    """
+    Returns a new requirement based on the given one, with the supplied extras. If the
+    given requirement already has extras those are replaced (or dropped if no new extras
+    are given).
+    """
+    match: re.Match[str] | None = re.fullmatch(
+        # see https://peps.python.org/pep-0508/#complete-grammar
+        r"([\w\t .-]+)(\[[^\]]*\])?(.*)",
+        str(req),
+        flags=re.ASCII,
+    )
+    # ireq.req is a valid requirement so the regex should always match
+    assert (
+        match is not None
+    ), f"regex match on requirement {req} failed, this should never happen"
+    pre: str | None = match.group(1)
+    post: str | None = match.group(3)
+    assert (
+        pre is not None and post is not None
+    ), f"regex group selection for requirement {req} failed, this should never happen"
+    extras: str = "[{}]".format(",".join(sorted(new_extras)) if new_extras else "")
+    return get_requirement(f"{pre}{extras}{post}")
+
+
+def _parse_direct_url_editable(editable_req: str) -> tuple[str | None, str, set[str]]:
+    try:
+        req = Requirement(editable_req)
+    except InvalidRequirement:
+        pass
+    else:
+        if req.url:
+            # Join the marker back into the name part. This will be parsed out
+            # later into a Requirement again.
+            if req.marker:
+                name = f"{req.name} ; {req.marker}"
+            else:
+                name = req.name
+            return (name, req.url, req.extras)
+
+    raise ValueError
+
+
+def _parse_pip_syntax_editable(editable_req: str) -> tuple[str | None, str, set[str]]:
+    url = editable_req
+
+    # If a file path is specified with extras, strip off the extras.
+    url_no_extras, extras = strip_extras(url)
+
+    if os.path.isdir(url_no_extras):
+        # Treating it as code that has already been checked out
+        url_no_extras = path_to_url(url_no_extras)
+
+    if url_no_extras.lower().startswith("file:"):
+        package_name = Link(url_no_extras).egg_fragment
+        return (package_name, url_no_extras, extras)
+
+    for version_control in vcs:
+        if url.lower().startswith(f"{version_control}:"):
+            url = f"{version_control}+{url}"
+            break
+
+    return Link(url).egg_fragment, url, set()
+
+
+def parse_editable(editable_req: str) -> tuple[str | None, str, set[str]]:
+    """Parses an editable requirement into:
+        - a requirement name with environment markers
+        - an URL
+        - extras
+    Accepted requirements:
+        - svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir
+        - local_path[some_extra]
+        - Foobar[extra] @ svn+http://blahblah@rev#subdirectory=subdir ; markers
+    """
+    try:
+        package_name, url, extras = _parse_direct_url_editable(editable_req)
+    except ValueError:
+        package_name, url, extras = _parse_pip_syntax_editable(editable_req)
+
+    link = Link(url)
+
+    if not link.is_vcs and not link.url.startswith("file:"):
+        backends = ", ".join(vcs.all_schemes)
+        raise InstallationError(
+            f"{editable_req} is not a valid editable requirement. "
+            f"It should either be a path to a local project or a VCS URL "
+            f"(beginning with {backends})."
+        )
+
+    # The project name can be inferred from local file URIs easily.
+    if not package_name and not link.url.startswith("file:"):
+        raise InstallationError(
+            f"Could not detect requirement name for '{editable_req}', "
+            "please specify one with your_package_name @ URL"
+        )
+    return package_name, url, extras
+
+
+def check_first_requirement_in_file(filename: str) -> None:
+    """Check if file is parsable as a requirements file.
+
+    This is heavily based on ``pkg_resources.parse_requirements``, but
+    simplified to just check the first meaningful line.
+
+    :raises InvalidRequirement: If the first meaningful line cannot be parsed
+        as an requirement.
+    """
+    with open(filename, encoding="utf-8", errors="ignore") as f:
+        # Create a steppable iterator, so we can handle \-continuations.
+        lines = (
+            line
+            for line in (line.strip() for line in f)
+            if line and not line.startswith("#")  # Skip blank lines/comments.
+        )
+
+        for line in lines:
+            # Drop comments -- a hash without a space may be in a URL.
+            if " #" in line:
+                line = line[: line.find(" #")]
+            # If there is a line continuation, drop it, and append the next line.
+            if line.endswith("\\"):
+                line = line[:-2].strip() + next(lines, "")
+            get_requirement(line)
+            return
+
+
+def deduce_helpful_msg(req: str) -> str:
+    """Returns helpful msg in case requirements file does not exist,
+    or cannot be parsed.
+
+    :params req: Requirements file path
+    """
+    if not os.path.exists(req):
+        return f" File '{req}' does not exist."
+    msg = " The path does exist. "
+    # Try to parse and check if it is a requirements file.
+    try:
+        check_first_requirement_in_file(req)
+    except InvalidRequirement:
+        logger.debug("Cannot parse '%s' as requirements file", req)
+    else:
+        msg += (
+            f"The argument you provided "
+            f"({req}) appears to be a"
+            f" requirements file. If that is the"
+            f" case, use the '-r' flag to install"
+            f" the packages specified within it."
+        )
+    return msg
+
+
+@dataclass(frozen=True)
+class RequirementParts:
+    requirement: Requirement | None
+    link: Link | None
+    markers: Marker | None
+    extras: set[str]
+
+
+def parse_req_from_editable(editable_req: str) -> RequirementParts:
+    name, url, extras_override = parse_editable(editable_req)
+
+    if name is not None:
+        try:
+            req: Requirement | None = get_requirement(name)
+        except InvalidRequirement as exc:
+            raise InstallationError(f"Invalid requirement: {name!r}: {exc}")
+    else:
+        req = None
+
+    link = Link(url)
+
+    return RequirementParts(req, link, None, extras_override)
+
+
+# ---- The actual constructors follow ----
+
+
+def install_req_from_editable(
+    editable_req: str,
+    comes_from: InstallRequirement | str | None = None,
+    *,
+    isolated: bool = False,
+    hash_options: dict[str, list[str]] | None = None,
+    constraint: bool = False,
+    user_supplied: bool = False,
+    config_settings: dict[str, str | list[str]] | None = None,
+) -> InstallRequirement:
+    if constraint:
+        raise InstallationError("Editable requirements are not allowed as constraints")
+
+    parts = parse_req_from_editable(editable_req)
+    return InstallRequirement(
+        parts.requirement,
+        comes_from=comes_from,
+        user_supplied=user_supplied,
+        editable=True,
+        link=parts.link,
+        constraint=constraint,
+        isolated=isolated,
+        hash_options=hash_options,
+        config_settings=config_settings,
+        extras=parts.extras,
+    )
+
+
+def _looks_like_path(name: str) -> bool:
+    """Checks whether the string "looks like" a path on the filesystem.
+
+    This does not check whether the target actually exists, only judge from the
+    appearance.
+
+    Returns true if any of the following conditions is true:
+    * a path separator is found (either os.path.sep or os.path.altsep);
+    * a dot is found (which represents the current directory).
+    """
+    if os.path.sep in name:
+        return True
+    if os.path.altsep is not None and os.path.altsep in name:
+        return True
+    if name.startswith("."):
+        return True
+    return False
+
+
+def _get_url_from_path(path: str, name: str) -> str | None:
+    """
+    First, it checks whether a provided path is an installable directory. If it
+    is, returns the path.
+
+    If false, check if the path is an archive file (such as a .whl).
+    The function checks if the path is a file. If false, if the path has
+    an @, it will treat it as a PEP 440 URL requirement and return the path.
+    """
+    if _looks_like_path(name) and os.path.isdir(path):
+        if is_installable_dir(path):
+            return path_to_url(path)
+        # TODO: The is_installable_dir test here might not be necessary
+        #       now that it is done in load_pyproject_toml too.
+        raise InstallationError(
+            f"Directory {name!r} is not installable. Neither 'setup.py' "
+            "nor 'pyproject.toml' found."
+        )
+    if not is_archive_file(path):
+        return None
+    if os.path.isfile(path):
+        return path_to_url(path)
+    urlreq_parts = name.split("@", 1)
+    if len(urlreq_parts) >= 2 and not _looks_like_path(urlreq_parts[0]):
+        # If the path contains '@' and the part before it does not look
+        # like a path, try to treat it as a PEP 440 URL req instead.
+        return None
+    logger.warning(
+        "Requirement %r looks like a filename, but the file does not exist",
+        name,
+    )
+    return path_to_url(path)
+
+
+def parse_req_from_line(name: str, line_source: str | None) -> RequirementParts:
+    if is_url(name):
+        marker_sep = "; "
+    else:
+        marker_sep = ";"
+    if marker_sep in name:
+        name, markers_as_string = name.split(marker_sep, 1)
+        markers_as_string = markers_as_string.strip()
+        if not markers_as_string:
+            markers = None
+        else:
+            try:
+                markers = Marker(markers_as_string)
+            except InvalidMarker as exc:
+                raise InstallationError(f"Invalid requirement: {name.strip()!r}: {exc}")
+    else:
+        markers = None
+    name = name.strip()
+    req_as_string = None
+    path = os.path.normpath(os.path.abspath(name))
+    link = None
+
+    if is_url(name):
+        link = Link(name)
+        extras: set[str] = set()
+    else:
+        p, extras = strip_extras(path)
+        url = _get_url_from_path(p, name)
+        if url is not None:
+            link = Link(url)
+
+    # it's a local file, dir, or url
+    if link:
+        # Handle relative file URLs
+        if link.scheme == "file" and re.search(r"\.\./", link.url):
+            link = Link(path_to_url(os.path.normpath(os.path.abspath(link.path))))
+        # wheel file
+        if link.is_wheel:
+            wheel = Wheel(link.filename)  # can raise InvalidWheelFilename
+            req_as_string = f"{wheel.name}=={wheel.version}"
+        else:
+            # set the req to the egg fragment.  when it's not there, this
+            # will become an 'unnamed' requirement
+            req_as_string = link.egg_fragment
+
+    # a requirement specifier
+    else:
+        req_as_string = name
+
+    def with_source(text: str) -> str:
+        if not line_source:
+            return text
+        return f"{text} (from {line_source})"
+
+    def _parse_req_string(req_as_string: str) -> Requirement:
+        try:
+            return get_requirement(req_as_string)
+        except InvalidRequirement as exc:
+            if os.path.sep in req_as_string:
+                add_msg = "It looks like a path."
+                add_msg += deduce_helpful_msg(req_as_string)
+            elif "=" in req_as_string and not any(
+                op in req_as_string for op in operators
+            ):
+                add_msg = "= is not a valid operator. Did you mean == ?"
+            else:
+                add_msg = ""
+            msg = with_source(f"Invalid requirement: {req_as_string!r}: {exc}")
+            if add_msg:
+                msg += f"\nHint: {add_msg}"
+            raise InstallationError(msg)
+
+    if req_as_string is not None:
+        req: Requirement | None = _parse_req_string(req_as_string)
+    else:
+        req = None
+
+    return RequirementParts(req, link, markers, extras)
+
+
+def install_req_from_line(
+    name: str,
+    comes_from: str | InstallRequirement | None = None,
+    *,
+    isolated: bool = False,
+    hash_options: dict[str, list[str]] | None = None,
+    constraint: bool = False,
+    line_source: str | None = None,
+    user_supplied: bool = False,
+    config_settings: dict[str, str | list[str]] | None = None,
+) -> InstallRequirement:
+    """Creates an InstallRequirement from a name, which might be a
+    requirement, directory containing 'setup.py', filename, or URL.
+
+    :param line_source: An optional string describing where the line is from,
+        for logging purposes in case of an error.
+    """
+    parts = parse_req_from_line(name, line_source)
+
+    return InstallRequirement(
+        parts.requirement,
+        comes_from,
+        link=parts.link,
+        markers=parts.markers,
+        isolated=isolated,
+        hash_options=hash_options,
+        config_settings=config_settings,
+        constraint=constraint,
+        extras=parts.extras,
+        user_supplied=user_supplied,
+    )
+
+
+def install_req_from_req_string(
+    req_string: str,
+    comes_from: InstallRequirement | None = None,
+    isolated: bool = False,
+    user_supplied: bool = False,
+) -> InstallRequirement:
+    try:
+        req = get_requirement(req_string)
+    except InvalidRequirement as exc:
+        raise InstallationError(f"Invalid requirement: {req_string!r}: {exc}")
+
+    domains_not_allowed = [
+        PyPI.file_storage_domain,
+        TestPyPI.file_storage_domain,
+    ]
+    if (
+        req.url
+        and comes_from
+        and comes_from.link
+        and comes_from.link.netloc in domains_not_allowed
+    ):
+        # Explicitly disallow pypi packages that depend on external urls
+        raise InstallationError(
+            "Packages installed from PyPI cannot depend on packages "
+            "which are not also hosted on PyPI.\n"
+            f"{comes_from.name} depends on {req} "
+        )
+
+    return InstallRequirement(
+        req,
+        comes_from,
+        isolated=isolated,
+        user_supplied=user_supplied,
+    )
+
+
+def install_req_from_parsed_requirement(
+    parsed_req: ParsedRequirement,
+    isolated: bool = False,
+    user_supplied: bool = False,
+    config_settings: dict[str, str | list[str]] | None = None,
+) -> InstallRequirement:
+    if parsed_req.is_editable:
+        req = install_req_from_editable(
+            parsed_req.requirement,
+            comes_from=parsed_req.comes_from,
+            constraint=parsed_req.constraint,
+            isolated=isolated,
+            user_supplied=user_supplied,
+            config_settings=config_settings,
+        )
+
+    else:
+        req = install_req_from_line(
+            parsed_req.requirement,
+            comes_from=parsed_req.comes_from,
+            isolated=isolated,
+            hash_options=(
+                parsed_req.options.get("hashes", {}) if parsed_req.options else {}
+            ),
+            constraint=parsed_req.constraint,
+            line_source=parsed_req.line_source,
+            user_supplied=user_supplied,
+            config_settings=config_settings,
+        )
+    return req
+
+
+def install_req_from_link_and_ireq(
+    link: Link, ireq: InstallRequirement
+) -> InstallRequirement:
+    return InstallRequirement(
+        req=ireq.req,
+        comes_from=ireq.comes_from,
+        editable=ireq.editable,
+        link=link,
+        markers=ireq.markers,
+        isolated=ireq.isolated,
+        hash_options=ireq.hash_options,
+        config_settings=ireq.config_settings,
+        user_supplied=ireq.user_supplied,
+    )
+
+
+def install_req_drop_extras(ireq: InstallRequirement) -> InstallRequirement:
+    """
+    Creates a new InstallationRequirement using the given template but without
+    any extras. Sets the original requirement as the new one's parent
+    (comes_from).
+    """
+    return InstallRequirement(
+        req=(
+            _set_requirement_extras(ireq.req, set()) if ireq.req is not None else None
+        ),
+        comes_from=ireq,
+        editable=ireq.editable,
+        link=ireq.link,
+        markers=ireq.markers,
+        isolated=ireq.isolated,
+        hash_options=ireq.hash_options,
+        constraint=ireq.constraint,
+        extras=[],
+        config_settings=ireq.config_settings,
+        user_supplied=ireq.user_supplied,
+    )
+
+
+def install_req_extend_extras(
+    ireq: InstallRequirement,
+    extras: Collection[str],
+) -> InstallRequirement:
+    """
+    Returns a copy of an installation requirement with some additional extras.
+    Makes a shallow copy of the ireq object.
+    """
+    result = copy.copy(ireq)
+    result.extras = {*ireq.extras, *extras}
+    result.req = (
+        _set_requirement_extras(ireq.req, result.extras)
+        if ireq.req is not None
+        else None
+    )
+    return result
+
+
+def _pylock_hashes_to_hash_options(hashes: Mapping[str, str]) -> dict[str, list[str]]:
+    return {k: [v] for k, v in hashes.items()}
+
+
+def install_req_from_pylock_package(
+    package: pylock.Package,
+    package_dist: (
+        pylock.PackageVcs
+        | pylock.PackageArchive
+        | pylock.PackageDirectory
+        | pylock.PackageSdist
+        | pylock.PackageWheel
+    ),
+    pylock_path_or_url: str,
+    user_supplied: bool,
+) -> tuple[InstallRequirement, Link | None]:
+    """Construct an InstallRequirement from a pylock package and artifact.
+
+    If the artifact is a Sdist or Wheel, also return a locked Link which
+    is meant to override candidates from indexes or --find-links.
+    """
+    # TODO: validate file size
+    if isinstance(package_dist, pylock.PackageVcs):
+        req_url = package_vcs_requirement_url(pylock_path_or_url, package_dist)
+        return (
+            InstallRequirement(
+                req=Requirement(f"{package.name} @ {req_url}"),
+                comes_from=pylock_path_or_url,
+                user_supplied=user_supplied,
+            ),
+            None,
+        )
+    elif isinstance(package_dist, pylock.PackageArchive):
+        req_url = package_archive_requirement_url(pylock_path_or_url, package_dist)
+        return (
+            InstallRequirement(
+                req=Requirement(f"{package.name} @ {req_url}"),
+                comes_from=pylock_path_or_url,
+                hash_options=_pylock_hashes_to_hash_options(package_dist.hashes),
+                user_supplied=user_supplied,
+            ),
+            None,
+        )
+    elif isinstance(package_dist, pylock.PackageDirectory):
+        req_url = package_directory_requirement_url(pylock_path_or_url, package_dist)
+        if package_dist.editable:
+            return (
+                install_req_from_editable(
+                    req_url,
+                    comes_from=pylock_path_or_url,
+                    user_supplied=user_supplied,
+                ),
+                None,
+            )
+        else:
+            return (
+                install_req_from_line(
+                    req_url,
+                    comes_from=pylock_path_or_url,
+                    user_supplied=user_supplied,
+                ),
+                None,
+            )
+    else:
+        # wheel or sdist
+        version = package.version
+        if isinstance(package_dist, pylock.PackageWheel):
+            if not version:
+                _, version, _, _ = parse_wheel_filename(package_dist.filename)
+            requirement_url = package_wheel_requirement_url(
+                pylock_path_or_url, package_dist
+            )
+        elif isinstance(package_dist, pylock.PackageSdist):
+            if not version:
+                _, version = parse_sdist_filename(package_dist.filename)
+            requirement_url = package_sdist_requirement_url(
+                pylock_path_or_url, package_dist
+            )
+        ireq = InstallRequirement(
+            req=Requirement(f"{package.name}=={version}"),
+            comes_from=pylock_path_or_url,
+            hash_options=_pylock_hashes_to_hash_options(package_dist.hashes),
+            user_supplied=user_supplied,
+        )
+        locked_link = Link(
+            requirement_url,
+            comes_from=pylock_path_or_url,
+            upload_time=package_dist.upload_time,
+        )
+        return ireq, locked_link
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/pep723.py b/venv/lib/python3.12/site-packages/pip/_internal/req/pep723.py
new file mode 100644
index 0000000..2e0c1b9
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/req/pep723.py
@@ -0,0 +1,41 @@
+import re
+from typing import Any
+
+from pip._internal.utils.compat import tomllib
+
+REGEX = r"(?m)^# /// (?P[a-zA-Z0-9-]+)$\s(?P(^#(| .*)$\s)+)^# ///$"
+
+
+class PEP723Exception(ValueError):
+    """Raised to indicate a problem when parsing PEP 723 metadata from a script"""
+
+    def __init__(self, msg: str) -> None:
+        self.msg = msg
+
+
+def pep723_metadata(scriptfile: str) -> dict[str, Any]:
+    with open(scriptfile, encoding="utf8") as f:
+        script = f.read()
+
+    name = "script"
+    matches = list(
+        filter(lambda m: m.group("type") == name, re.finditer(REGEX, script))
+    )
+
+    if len(matches) > 1:
+        raise PEP723Exception(f"Multiple {name!r} blocks found in {scriptfile!r}")
+    elif len(matches) == 1:
+        content = "".join(
+            line[2:] if line.startswith("# ") else line[1:]
+            for line in matches[0].group("content").splitlines(keepends=True)
+        )
+        try:
+            metadata = tomllib.loads(content)
+        except Exception as exc:
+            raise PEP723Exception(f"Failed to parse TOML in {scriptfile!r}") from exc
+    else:
+        raise PEP723Exception(
+            f"File does not contain {name!r} metadata: {scriptfile!r}"
+        )
+
+    return metadata
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/req_dependency_group.py b/venv/lib/python3.12/site-packages/pip/_internal/req/req_dependency_group.py
new file mode 100644
index 0000000..eb9d802
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/req/req_dependency_group.py
@@ -0,0 +1,86 @@
+from collections.abc import Iterable, Iterator
+from typing import Any
+
+from pip._vendor.packaging.dependency_groups import DependencyGroupResolver
+from pip._vendor.packaging.errors import ExceptionGroup
+
+from pip._internal.exceptions import InstallationError
+from pip._internal.utils.compat import tomllib
+
+
+def parse_dependency_groups(groups: list[tuple[str, str]]) -> list[str]:
+    """
+    Parse dependency groups data as provided via the CLI, in a `[path:]group` syntax.
+
+    Raises InstallationErrors if anything goes wrong.
+    """
+    resolvers = _build_resolvers(path for (path, _) in groups)
+    return list(_resolve_all_groups(resolvers, groups))
+
+
+def _resolve_all_groups(
+    resolvers: dict[str, DependencyGroupResolver], groups: list[tuple[str, str]]
+) -> Iterator[str]:
+    """
+    Run all resolution, converting any error from `DependencyGroupResolver` into
+    an InstallationError.
+    """
+    for path, groupname in groups:
+        resolver = resolvers[path]
+        try:
+            yield from (str(req) for req in resolver.resolve(groupname))
+        except ExceptionGroup as eg:
+            # Convert ExceptionGroup to a single InstallationError with all messages
+            messages = [str(e) for e in eg.exceptions]
+            raise InstallationError(
+                f"[dependency-groups] resolution failed for '{groupname}' "
+                f"from '{path}': {'; '.join(messages)}"
+            ) from eg
+
+
+def _build_resolvers(paths: Iterable[str]) -> dict[str, Any]:
+    resolvers = {}
+    for path in paths:
+        if path in resolvers:
+            continue
+
+        pyproject = _load_pyproject(path)
+        if "dependency-groups" not in pyproject:
+            raise InstallationError(
+                f"[dependency-groups] table was missing from '{path}'. "
+                "Cannot resolve '--group' option."
+            )
+        raw_dependency_groups = pyproject["dependency-groups"]
+        if not isinstance(raw_dependency_groups, dict):
+            raise InstallationError(
+                f"[dependency-groups] table was malformed in {path}. "
+                "Cannot resolve '--group' option."
+            )
+
+        try:
+            resolvers[path] = DependencyGroupResolver(raw_dependency_groups)
+        except ExceptionGroup as eg:
+            # Handle ExceptionGroup from resolver initialization
+            messages = [str(e) for e in eg.exceptions]
+            raise InstallationError(
+                f"[dependency-groups] data was invalid in {path}: {'; '.join(messages)}"
+            ) from eg
+
+    return resolvers
+
+
+def _load_pyproject(path: str) -> dict[str, Any]:
+    """
+    This helper loads a pyproject.toml as TOML.
+
+    It raises an InstallationError if the operation fails.
+    """
+    try:
+        with open(path, "rb") as fp:
+            return tomllib.load(fp)
+    except FileNotFoundError:
+        raise InstallationError(f"{path} not found. Cannot resolve '--group' option.")
+    except tomllib.TOMLDecodeError as e:
+        raise InstallationError(f"Error parsing {path}: {e}") from e
+    except OSError as e:
+        raise InstallationError(f"Error reading {path}: {e}") from e
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/req_file.py b/venv/lib/python3.12/site-packages/pip/_internal/req/req_file.py
new file mode 100644
index 0000000..400bb86
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/req/req_file.py
@@ -0,0 +1,622 @@
+"""
+Requirements file parsing
+"""
+
+from __future__ import annotations
+
+import codecs
+import logging
+import optparse
+import os
+import re
+import shlex
+import sys
+import urllib.parse
+from collections.abc import Callable, Generator, Iterable
+from dataclasses import dataclass
+from optparse import Values
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    NoReturn,
+)
+
+from pip._internal.cli import cmdoptions
+from pip._internal.exceptions import InstallationError, RequirementsFileParseError
+from pip._internal.models.release_control import ReleaseControl
+from pip._internal.models.search_scope import SearchScope
+from pip._internal.utils.compat import get_locale_encoding
+
+if TYPE_CHECKING:
+    from pip._internal.index.package_finder import PackageFinder
+    from pip._internal.network.session import PipSession
+
+__all__ = ["parse_requirements"]
+
+ReqFileLines = Iterable[tuple[int, str]]
+
+LineParser = Callable[[str], tuple[str, Values]]
+
+SCHEME_RE = re.compile(r"^(http|https|file):", re.I)
+COMMENT_RE = re.compile(r"(^|\s+)#.*$")
+
+# Matches environment variable-style values in '${MY_VARIABLE_1}' with the
+# variable name consisting of only uppercase letters, digits or the '_'
+# (underscore). This follows the POSIX standard defined in IEEE Std 1003.1,
+# 2013 Edition.
+ENV_VAR_RE = re.compile(r"(?P\$\{(?P[A-Z0-9_]+)\})")
+
+SUPPORTED_OPTIONS: list[Callable[..., optparse.Option]] = [
+    cmdoptions.index_url,
+    cmdoptions.extra_index_url,
+    cmdoptions.no_index,
+    cmdoptions.constraints,
+    cmdoptions.requirements,
+    cmdoptions.editable,
+    cmdoptions.find_links,
+    cmdoptions.no_binary,
+    cmdoptions.only_binary,
+    cmdoptions.prefer_binary,
+    cmdoptions.require_hashes,
+    cmdoptions.no_require_hashes,
+    cmdoptions.pre,
+    cmdoptions.all_releases,
+    cmdoptions.only_final,
+    cmdoptions.trusted_host,
+    cmdoptions.use_new_feature,
+]
+
+# options to be passed to requirements
+SUPPORTED_OPTIONS_REQ: list[Callable[..., optparse.Option]] = [
+    cmdoptions.hash,
+    cmdoptions.config_settings,
+]
+
+SUPPORTED_OPTIONS_EDITABLE_REQ: list[Callable[..., optparse.Option]] = [
+    cmdoptions.config_settings,
+]
+
+
+# the 'dest' string values
+SUPPORTED_OPTIONS_REQ_DEST = [str(o().dest) for o in SUPPORTED_OPTIONS_REQ]
+SUPPORTED_OPTIONS_EDITABLE_REQ_DEST = [
+    str(o().dest) for o in SUPPORTED_OPTIONS_EDITABLE_REQ
+]
+
+# order of BOMS is important: codecs.BOM_UTF16_LE is a prefix of codecs.BOM_UTF32_LE
+# so data.startswith(BOM_UTF16_LE) would be true for UTF32_LE data
+BOMS: list[tuple[bytes, str]] = [
+    (codecs.BOM_UTF8, "utf-8"),
+    (codecs.BOM_UTF32, "utf-32"),
+    (codecs.BOM_UTF32_BE, "utf-32-be"),
+    (codecs.BOM_UTF32_LE, "utf-32-le"),
+    (codecs.BOM_UTF16, "utf-16"),
+    (codecs.BOM_UTF16_BE, "utf-16-be"),
+    (codecs.BOM_UTF16_LE, "utf-16-le"),
+]
+
+PEP263_ENCODING_RE = re.compile(rb"coding[:=]\s*([-\w.]+)")
+DEFAULT_ENCODING = "utf-8"
+
+logger = logging.getLogger(__name__)
+
+
+@dataclass(frozen=True, slots=True)
+class ParsedRequirement:
+    requirement: str
+    is_editable: bool
+    comes_from: str
+    constraint: bool
+    options: dict[str, Any] | None
+    line_source: str | None
+
+
+@dataclass(frozen=True, slots=True)
+class ParsedLine:
+    filename: str
+    lineno: int
+    args: str
+    opts: Values
+    constraint: bool
+
+    @property
+    def is_editable(self) -> bool:
+        return bool(self.opts.editables)
+
+    @property
+    def requirement(self) -> str | None:
+        if self.args:
+            return self.args
+        elif self.is_editable:
+            # We don't support multiple -e on one line
+            return self.opts.editables[0]
+        return None
+
+
+def parse_requirements(
+    filename: str,
+    session: PipSession,
+    finder: PackageFinder | None = None,
+    options: optparse.Values | None = None,
+    constraint: bool = False,
+) -> Generator[ParsedRequirement, None, None]:
+    """Parse a requirements file and yield ParsedRequirement instances.
+
+    :param filename:    Path or url of requirements file.
+    :param session:     PipSession instance.
+    :param finder:      Instance of pip.index.PackageFinder.
+    :param options:     cli options.
+    :param constraint:  If true, parsing a constraint file rather than
+        requirements file.
+    """
+    line_parser = get_line_parser(finder)
+    parser = RequirementsFileParser(session, line_parser)
+
+    for parsed_line in parser.parse(filename, constraint):
+        parsed_req = handle_line(
+            parsed_line, options=options, finder=finder, session=session
+        )
+        if parsed_req is not None:
+            yield parsed_req
+
+
+def preprocess(content: str) -> ReqFileLines:
+    """Split, filter, and join lines, and return a line iterator
+
+    :param content: the content of the requirements file
+    """
+    lines_enum: ReqFileLines = enumerate(content.splitlines(), start=1)
+    lines_enum = join_lines(lines_enum)
+    lines_enum = ignore_comments(lines_enum)
+    lines_enum = expand_env_variables(lines_enum)
+    return lines_enum
+
+
+def handle_requirement_line(
+    line: ParsedLine,
+    options: optparse.Values | None = None,
+) -> ParsedRequirement:
+    # preserve for the nested code path
+    line_comes_from = "{} {} (line {})".format(
+        "-c" if line.constraint else "-r",
+        line.filename,
+        line.lineno,
+    )
+
+    assert line.requirement is not None
+
+    # get the options that apply to requirements
+    if line.is_editable:
+        supported_dest = SUPPORTED_OPTIONS_EDITABLE_REQ_DEST
+    else:
+        supported_dest = SUPPORTED_OPTIONS_REQ_DEST
+    req_options = {}
+    for dest in supported_dest:
+        if dest in line.opts.__dict__ and line.opts.__dict__[dest]:
+            req_options[dest] = line.opts.__dict__[dest]
+
+    line_source = f"line {line.lineno} of {line.filename}"
+    return ParsedRequirement(
+        requirement=line.requirement,
+        is_editable=line.is_editable,
+        comes_from=line_comes_from,
+        constraint=line.constraint,
+        options=req_options,
+        line_source=line_source,
+    )
+
+
+def handle_option_line(
+    opts: Values,
+    filename: str,
+    lineno: int,
+    finder: PackageFinder | None = None,
+    options: optparse.Values | None = None,
+    session: PipSession | None = None,
+) -> None:
+    if opts.hashes:
+        logger.warning(
+            "%s line %s has --hash but no requirement, and will be ignored.",
+            filename,
+            lineno,
+        )
+
+    if options:
+        # percolate options upward
+        if opts.require_hashes:
+            options.require_hashes = opts.require_hashes
+        if opts.no_require_hashes:
+            options.no_require_hashes = opts.no_require_hashes
+        if opts.features_enabled:
+            options.features_enabled.extend(
+                f for f in opts.features_enabled if f not in options.features_enabled
+            )
+
+    # set finder options
+    if finder:
+        find_links = finder.find_links
+        index_urls = finder.index_urls
+        no_index = finder.search_scope.no_index
+        if opts.no_index is True:
+            no_index = True
+            index_urls = []
+        if opts.index_url and not no_index:
+            index_urls = [opts.index_url]
+        if opts.extra_index_urls and not no_index:
+            index_urls.extend(opts.extra_index_urls)
+        if opts.find_links:
+            # FIXME: it would be nice to keep track of the source
+            # of the find_links: support a find-links local path
+            # relative to a requirements file.
+            value = opts.find_links[0]
+            req_dir = os.path.dirname(os.path.abspath(filename))
+            relative_to_reqs_file = os.path.join(req_dir, value)
+            if os.path.exists(relative_to_reqs_file):
+                value = relative_to_reqs_file
+            find_links.append(value)
+
+        if session:
+            # We need to update the auth urls in session
+            session.update_index_urls(index_urls)
+
+        search_scope = SearchScope(
+            find_links=find_links,
+            index_urls=index_urls,
+            no_index=no_index,
+        )
+        finder.search_scope = search_scope
+
+        # Transform --pre into --all-releases :all:
+        if opts.pre:
+            if not opts.release_control:
+                opts.release_control = ReleaseControl()
+            opts.release_control.all_releases.add(":all:")
+
+        if opts.release_control:
+            if not finder.release_control:
+                # First time seeing release_control, set it on finder
+                finder.set_release_control(opts.release_control)
+
+        if opts.prefer_binary:
+            finder.set_prefer_binary()
+
+        if session:
+            for host in opts.trusted_hosts or []:
+                source = f"line {lineno} of {filename}"
+                session.add_trusted_host(host, source=source)
+
+
+def handle_line(
+    line: ParsedLine,
+    options: optparse.Values | None = None,
+    finder: PackageFinder | None = None,
+    session: PipSession | None = None,
+) -> ParsedRequirement | None:
+    """Handle a single parsed requirements line; This can result in
+    creating/yielding requirements, or updating the finder.
+
+    :param line:        The parsed line to be processed.
+    :param options:     CLI options.
+    :param finder:      The finder - updated by non-requirement lines.
+    :param session:     The session - updated by non-requirement lines.
+
+    Returns a ParsedRequirement object if the line is a requirement line,
+    otherwise returns None.
+
+    For lines that contain requirements, the only options that have an effect
+    are from SUPPORTED_OPTIONS_REQ, and they are scoped to the
+    requirement. Other options from SUPPORTED_OPTIONS may be present, but are
+    ignored.
+
+    For lines that do not contain requirements, the only options that have an
+    effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may
+    be present, but are ignored. These lines may contain multiple options
+    (although our docs imply only one is supported), and all our parsed and
+    affect the finder.
+    """
+
+    if line.requirement is not None:
+        parsed_req = handle_requirement_line(line, options)
+        return parsed_req
+    else:
+        handle_option_line(
+            line.opts,
+            line.filename,
+            line.lineno,
+            finder,
+            options,
+            session,
+        )
+        return None
+
+
+class RequirementsFileParser:
+    def __init__(
+        self,
+        session: PipSession,
+        line_parser: LineParser,
+    ) -> None:
+        self._session = session
+        self._line_parser = line_parser
+
+    def parse(
+        self, filename: str, constraint: bool
+    ) -> Generator[ParsedLine, None, None]:
+        """Parse a given file, yielding parsed lines."""
+        yield from self._parse_and_recurse(
+            filename, constraint, [{os.path.abspath(filename): None}]
+        )
+
+    def _parse_and_recurse(
+        self,
+        filename: str,
+        constraint: bool,
+        parsed_files_stack: list[dict[str, str | None]],
+    ) -> Generator[ParsedLine, None, None]:
+        for line in self._parse_file(filename, constraint):
+            if line.requirement is None and (
+                line.opts.requirements or line.opts.constraints
+            ):
+                # parse a nested requirements file
+                if line.opts.requirements:
+                    req_path = line.opts.requirements[0]
+                    nested_constraint = False
+                else:
+                    req_path = line.opts.constraints[0]
+                    nested_constraint = True
+
+                # original file is over http
+                if SCHEME_RE.search(filename):
+                    # do a url join so relative paths work
+                    req_path = urllib.parse.urljoin(filename, req_path)
+                # original file and nested file are paths
+                elif not SCHEME_RE.search(req_path):
+                    # do a join so relative paths work
+                    # and then abspath so that we can identify recursive references
+                    req_path = os.path.abspath(
+                        os.path.join(
+                            os.path.dirname(filename),
+                            req_path,
+                        )
+                    )
+                parsed_files = parsed_files_stack[0]
+                if req_path in parsed_files:
+                    initial_file = parsed_files[req_path]
+                    tail = (
+                        f" and again in {initial_file}"
+                        if initial_file is not None
+                        else ""
+                    )
+                    raise RequirementsFileParseError(
+                        f"{req_path} recursively references itself in {filename}{tail}"
+                    )
+                # Keeping a track where was each file first included in
+                new_parsed_files = parsed_files.copy()
+                new_parsed_files[req_path] = filename
+                yield from self._parse_and_recurse(
+                    req_path, nested_constraint, [new_parsed_files, *parsed_files_stack]
+                )
+            else:
+                yield line
+
+    def _parse_file(
+        self, filename: str, constraint: bool
+    ) -> Generator[ParsedLine, None, None]:
+        _, content = get_file_content(filename, self._session, constraint=constraint)
+
+        lines_enum = preprocess(content)
+
+        for line_number, line in lines_enum:
+            try:
+                args_str, opts = self._line_parser(line)
+            except OptionParsingError as e:
+                # add offending line
+                msg = f"Invalid requirement: {line}\n{e.msg}"
+                raise RequirementsFileParseError(msg)
+
+            yield ParsedLine(
+                filename,
+                line_number,
+                args_str,
+                opts,
+                constraint,
+            )
+
+
+def get_line_parser(finder: PackageFinder | None) -> LineParser:
+    def parse_line(line: str) -> tuple[str, Values]:
+        # Build new parser for each line since it accumulates appendable
+        # options.
+        parser = build_parser()
+        defaults = parser.get_default_values()
+        defaults.index_url = None
+        if finder:
+            defaults.format_control = finder.format_control
+            defaults.release_control = finder.release_control
+
+        args_str, options_str = break_args_options(line)
+
+        try:
+            options = shlex.split(options_str)
+        except ValueError as e:
+            raise OptionParsingError(f"Could not split options: {options_str}") from e
+
+        opts, _ = parser.parse_args(options, defaults)
+
+        return args_str, opts
+
+    return parse_line
+
+
+def break_args_options(line: str) -> tuple[str, str]:
+    """Break up the line into an args and options string.  We only want to shlex
+    (and then optparse) the options, not the args.  args can contain markers
+    which are corrupted by shlex.
+    """
+    tokens = line.split(" ")
+    args = []
+    options = tokens[:]
+    for token in tokens:
+        if token.startswith(("-", "--")):
+            break
+        else:
+            args.append(token)
+            options.pop(0)
+    return " ".join(args), " ".join(options)
+
+
+class OptionParsingError(Exception):
+    def __init__(self, msg: str) -> None:
+        self.msg = msg
+
+
+def build_parser() -> optparse.OptionParser:
+    """
+    Return a parser for parsing requirement lines
+    """
+    parser = optparse.OptionParser(add_help_option=False)
+
+    option_factories = SUPPORTED_OPTIONS + SUPPORTED_OPTIONS_REQ
+    for option_factory in option_factories:
+        option = option_factory()
+        parser.add_option(option)
+
+    # By default optparse sys.exits on parsing errors. We want to wrap
+    # that in our own exception.
+    def parser_exit(self: Any, msg: str) -> NoReturn:
+        raise OptionParsingError(msg)
+
+    # NOTE: mypy disallows assigning to a method
+    #       https://github.com/python/mypy/issues/2427
+    parser.exit = parser_exit  # type: ignore
+
+    return parser
+
+
+def join_lines(lines_enum: ReqFileLines) -> ReqFileLines:
+    """Joins a line ending in '\' with the previous line (except when following
+    comments).  The joined line takes on the index of the first line.
+    """
+    primary_line_number = None
+    new_line: list[str] = []
+    for line_number, line in lines_enum:
+        if not line.endswith("\\") or COMMENT_RE.match(line):
+            if COMMENT_RE.match(line):
+                # this ensures comments are always matched later
+                line = " " + line
+            if new_line:
+                new_line.append(line)
+                assert primary_line_number is not None
+                yield primary_line_number, "".join(new_line)
+                new_line = []
+            else:
+                yield line_number, line
+        else:
+            if not new_line:
+                primary_line_number = line_number
+            new_line.append(line.strip("\\"))
+
+    # last line contains \
+    if new_line:
+        assert primary_line_number is not None
+        yield primary_line_number, "".join(new_line)
+
+
+def ignore_comments(lines_enum: ReqFileLines) -> ReqFileLines:
+    """
+    Strips comments and filter empty lines.
+    """
+    for line_number, line in lines_enum:
+        line = COMMENT_RE.sub("", line)
+        line = line.strip()
+        if line:
+            yield line_number, line
+
+
+def expand_env_variables(lines_enum: ReqFileLines) -> ReqFileLines:
+    """Replace all environment variables that can be retrieved via `os.getenv`.
+
+    The only allowed format for environment variables defined in the
+    requirement file is `${MY_VARIABLE_1}` to ensure two things:
+
+    1. Strings that contain a `$` aren't accidentally (partially) expanded.
+    2. Ensure consistency across platforms for requirement files.
+
+    These points are the result of a discussion on the `github pull
+    request #3514 `_.
+
+    Valid characters in variable names follow the `POSIX standard
+    `_ and are limited
+    to uppercase letter, digits and the `_` (underscore).
+    """
+    for line_number, line in lines_enum:
+        for env_var, var_name in ENV_VAR_RE.findall(line):
+            value = os.getenv(var_name)
+            if not value:
+                continue
+
+            line = line.replace(env_var, value)
+
+        yield line_number, line
+
+
+def get_file_content(
+    url: str, session: PipSession, *, constraint: bool = False
+) -> tuple[str, str]:
+    """Gets the content of a file; it may be a filename, file: URL, or
+    http: URL.  Returns (location, content).  Content is unicode.
+    Respects # -*- coding: declarations on the retrieved files.
+
+    :param url:         File path or url.
+    :param session:     PipSession instance.
+    """
+    scheme = urllib.parse.urlsplit(url).scheme
+    # Pip has special support for file:// URLs (LocalFSAdapter).
+    if scheme in ["http", "https", "file"]:
+        # Delay importing heavy network modules until absolutely necessary.
+        from pip._internal.network.utils import raise_for_status
+
+        resp = session.get(url)
+        raise_for_status(resp)
+        return resp.url, resp.text
+
+    # Assume this is a bare path.
+    try:
+        with open(url, "rb") as f:
+            raw_content = f.read()
+    except OSError as exc:
+        kind = "constraint" if constraint else "requirements"
+        raise InstallationError(f"Could not open {kind} file: {exc}")
+
+    content = _decode_req_file(raw_content, url)
+
+    return url, content
+
+
+def _decode_req_file(data: bytes, url: str) -> str:
+    for bom, encoding in BOMS:
+        if data.startswith(bom):
+            return data[len(bom) :].decode(encoding)
+
+    for line in data.split(b"\n")[:2]:
+        if line[0:1] == b"#":
+            result = PEP263_ENCODING_RE.search(line)
+            if result is not None:
+                encoding = result.groups()[0].decode("ascii")
+                return data.decode(encoding)
+
+    try:
+        return data.decode(DEFAULT_ENCODING)
+    except UnicodeDecodeError:
+        locale_encoding = get_locale_encoding() or sys.getdefaultencoding()
+        logging.warning(
+            "unable to decode data from %s with default encoding %s, "
+            "falling back to encoding from locale: %s. "
+            "If this is intentional you should specify the encoding with a "
+            "PEP-263 style comment, e.g. '# -*- coding: %s -*-'",
+            url,
+            DEFAULT_ENCODING,
+            locale_encoding,
+            locale_encoding,
+        )
+        return data.decode(locale_encoding)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/req_install.py b/venv/lib/python3.12/site-packages/pip/_internal/req/req_install.py
new file mode 100644
index 0000000..f20707f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/req/req_install.py
@@ -0,0 +1,842 @@
+from __future__ import annotations
+
+import functools
+import logging
+import os
+import shutil
+import sys
+import uuid
+import zipfile
+from collections.abc import Collection, Iterable
+from optparse import Values
+from pathlib import Path
+from typing import Any
+
+from pip._vendor.packaging.markers import Marker
+from pip._vendor.packaging.requirements import Requirement
+from pip._vendor.packaging.specifiers import SpecifierSet
+from pip._vendor.packaging.utils import canonicalize_name
+from pip._vendor.packaging.version import Version
+from pip._vendor.packaging.version import parse as parse_version
+from pip._vendor.pyproject_hooks import BuildBackendHookCaller
+
+from pip._internal.build_env import BuildEnvironment, NoOpBuildEnvironment
+from pip._internal.exceptions import InstallationError, PreviousBuildDirError
+from pip._internal.locations import get_scheme
+from pip._internal.metadata import (
+    BaseDistribution,
+    get_default_environment,
+    get_directory_distribution,
+    get_wheel_distribution,
+)
+from pip._internal.metadata.base import FilesystemWheel
+from pip._internal.models.direct_url import DirectUrl
+from pip._internal.models.link import Link
+from pip._internal.operations.build.metadata import generate_metadata
+from pip._internal.operations.build.metadata_editable import generate_editable_metadata
+from pip._internal.pyproject import load_pyproject_toml, make_pyproject_path
+from pip._internal.req.req_uninstall import UninstallPathSet
+from pip._internal.utils.deprecation import deprecated
+from pip._internal.utils.hashes import Hashes
+from pip._internal.utils.misc import (
+    ConfiguredBuildBackendHookCaller,
+    ask_path_exists,
+    backup_dir,
+    display_path,
+    hide_url,
+    is_installable_dir,
+    redact_auth_from_requirement,
+    redact_auth_from_url,
+)
+from pip._internal.utils.packaging import get_requirement
+from pip._internal.utils.subprocess import runner_with_spinner_message
+from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
+from pip._internal.utils.unpacking import unpack_file
+from pip._internal.utils.virtualenv import running_under_virtualenv
+from pip._internal.vcs import vcs
+
+logger = logging.getLogger(__name__)
+
+
+class InstallRequirement:
+    """
+    Represents something that may be installed later on, may have information
+    about where to fetch the relevant requirement and also contains logic for
+    installing the said requirement.
+    """
+
+    def __init__(
+        self,
+        req: Requirement | None,
+        comes_from: str | InstallRequirement | None,
+        editable: bool = False,
+        link: Link | None = None,
+        markers: Marker | None = None,
+        isolated: bool = False,
+        *,
+        hash_options: dict[str, list[str]] | None = None,
+        config_settings: dict[str, str | list[str]] | None = None,
+        constraint: bool = False,
+        extras: Collection[str] = (),
+        user_supplied: bool = False,
+    ) -> None:
+        assert req is None or isinstance(req, Requirement), req
+        self.req = req
+        self.comes_from = comes_from
+        self.constraint = constraint
+        self.editable = editable
+
+        # source_dir is the local directory where the linked requirement is
+        # located, or unpacked. In case unpacking is needed, creating and
+        # populating source_dir is done by the RequirementPreparer. Note this
+        # is not necessarily the directory where pyproject.toml or setup.py is
+        # located - that one is obtained via unpacked_source_directory.
+        self.source_dir: str | None = None
+        if self.editable:
+            assert link
+            if link.is_file:
+                self.source_dir = os.path.normpath(os.path.abspath(link.file_path))
+
+        # original_link is the direct URL that was provided by the user for the
+        # requirement, either directly or via a constraints file.
+        if link is None and req and req.url:
+            # PEP 508 URL requirement
+            link = Link(req.url)
+        self.link = self.original_link = link
+
+        # When this InstallRequirement is a wheel obtained from the cache of locally
+        # built wheels, this is the source link corresponding to the cache entry, which
+        # was used to download and build the cached wheel.
+        self.cached_wheel_source_link: Link | None = None
+
+        # Information about the location of the artifact that was downloaded . This
+        # property is guaranteed to be set in resolver results.
+        self.download_info: DirectUrl | None = None
+
+        # Path to any downloaded or already-existing package.
+        self.local_file_path: str | None = None
+        if self.link and self.link.is_file:
+            self.local_file_path = self.link.file_path
+
+        if extras:
+            self.extras = extras
+        elif req:
+            self.extras = req.extras
+        else:
+            self.extras = set()
+        if markers is None and req:
+            markers = req.marker
+        self.markers = markers
+
+        # This holds the Distribution object if this requirement is already installed.
+        self.satisfied_by: BaseDistribution | None = None
+        # Whether the installation process should try to uninstall an existing
+        # distribution before installing this requirement.
+        self.should_reinstall = False
+        # Temporary build location
+        self._temp_build_dir: TempDirectory | None = None
+        # Set to True after successful installation
+        self.install_succeeded: bool | None = None
+        # Supplied options
+        self.hash_options = hash_options if hash_options else {}
+        self.config_settings = config_settings
+        # Set to True after successful preparation of this requirement
+        self.prepared = False
+        # User supplied requirement are explicitly requested for installation
+        # by the user via CLI arguments or requirements files, as opposed to,
+        # e.g. dependencies, extras or constraints.
+        self.user_supplied = user_supplied
+
+        self.isolated = isolated
+        self.build_env: BuildEnvironment = NoOpBuildEnvironment()
+
+        # For PEP 517, the directory where we request the project metadata
+        # gets stored. We need this to pass to build_wheel, so the backend
+        # can ensure that the wheel matches the metadata (see the PEP for
+        # details).
+        self.metadata_directory: str | None = None
+
+        # The cached metadata distribution that this requirement represents.
+        # See get_dist / set_dist.
+        self._distribution: BaseDistribution | None = None
+
+        # The static build requirements (from pyproject.toml)
+        self.pyproject_requires: list[str] | None = None
+
+        # Build requirements that we will check are available
+        self.requirements_to_check: list[str] = []
+
+        # The PEP 517 backend we should use to build the project
+        self._pep517_backend_spec: str
+        self._pep517_backend_path: str | None
+        self.pep517_backend: BuildBackendHookCaller | None = None
+
+        # This requirement needs more preparation before it can be built
+        self.needs_more_preparation = False
+
+        # This requirement needs to be unpacked before it can be installed.
+        self._archive_source: Path | None = None
+
+    def __str__(self) -> str:
+        if self.req:
+            s = redact_auth_from_requirement(self.req)
+            if self.link:
+                s += f" from {redact_auth_from_url(self.link.url)}"
+        elif self.link:
+            s = redact_auth_from_url(self.link.url)
+        else:
+            s = ""
+        if self.satisfied_by is not None:
+            if self.satisfied_by.location is not None:
+                location = display_path(self.satisfied_by.location)
+            else:
+                location = ""
+            s += f" in {location}"
+        if self.comes_from:
+            if isinstance(self.comes_from, str):
+                comes_from: str | None = self.comes_from
+            else:
+                comes_from = self.comes_from.from_path()
+            if comes_from:
+                s += f" (from {comes_from})"
+        return s
+
+    def __repr__(self) -> str:
+        return (
+            f"<{self.__class__.__name__} object: "
+            f"{str(self)} editable={self.editable!r}>"
+        )
+
+    def format_debug(self) -> str:
+        """An un-tested helper for getting state, for debugging."""
+        attributes = vars(self)
+        names = sorted(attributes)
+
+        state = (f"{attr}={attributes[attr]!r}" for attr in sorted(names))
+        return "<{name} object: {{{state}}}>".format(
+            name=self.__class__.__name__,
+            state=", ".join(state),
+        )
+
+    # Things that are valid for all kinds of requirements?
+    @property
+    def name(self) -> str | None:
+        if self.req is None:
+            return None
+        return self.req.name
+
+    @functools.cached_property
+    def supports_pyproject_editable(self) -> bool:
+        assert self.pep517_backend
+        with self.build_env:
+            runner = runner_with_spinner_message(
+                "Checking if build backend supports build_editable"
+            )
+            with self.pep517_backend.subprocess_runner(runner):
+                return "build_editable" in self.pep517_backend._supported_features()
+
+    @property
+    def specifier(self) -> SpecifierSet:
+        assert self.req is not None
+        return self.req.specifier
+
+    @property
+    def is_direct(self) -> bool:
+        """Whether this requirement was specified as a direct URL."""
+        return self.original_link is not None
+
+    @property
+    def is_pinned(self) -> bool:
+        """Return whether I am pinned to an exact version.
+
+        For example, some-package==1.2 is pinned; some-package>1.2 is not.
+        """
+        assert self.req is not None
+        specifiers = self.req.specifier
+        return len(specifiers) == 1 and next(iter(specifiers)).operator in {"==", "==="}
+
+    def match_markers(self, extras_requested: Iterable[str] | None = None) -> bool:
+        if not extras_requested:
+            # Provide an extra to safely evaluate the markers
+            # without matching any extra
+            extras_requested = ("",)
+        if self.markers is not None:
+            return any(
+                self.markers.evaluate({"extra": extra}) for extra in extras_requested
+            )
+        else:
+            return True
+
+    @property
+    def has_hash_options(self) -> bool:
+        """Return whether any known-good hashes are specified as options.
+
+        These activate --require-hashes mode; hashes specified as part of a
+        URL do not.
+
+        """
+        return bool(self.hash_options)
+
+    def hashes(self, trust_internet: bool = True) -> Hashes:
+        """Return a hash-comparer that considers my option- and URL-based
+        hashes to be known-good.
+
+        Hashes in URLs--ones embedded in the requirements file, not ones
+        downloaded from an index server--are almost peers with ones from
+        flags. They satisfy --require-hashes (whether it was implicitly or
+        explicitly activated) but do not activate it. md5 and sha224 are not
+        allowed in flags, which should nudge people toward good algos. We
+        always OR all hashes together, even ones from URLs.
+
+        :param trust_internet: Whether to trust URL-based (#md5=...) hashes
+            downloaded from the internet, as by populate_link()
+
+        """
+        good_hashes = self.hash_options.copy()
+        if trust_internet:
+            link = self.link
+        elif self.is_direct and self.user_supplied:
+            link = self.original_link
+        else:
+            link = None
+        if link and link.hash:
+            assert link.hash_name is not None
+            good_hashes.setdefault(link.hash_name, []).append(link.hash)
+        return Hashes(good_hashes)
+
+    def from_path(self) -> str | None:
+        """Format a nice indicator to show where this "comes from" """
+        if self.req is None:
+            return None
+        s = str(self.req)
+        if self.comes_from:
+            comes_from: str | None
+            if isinstance(self.comes_from, str):
+                comes_from = self.comes_from
+            else:
+                comes_from = self.comes_from.from_path()
+            if comes_from:
+                s += "->" + comes_from
+        return s
+
+    def ensure_build_location(
+        self, build_dir: str, autodelete: bool, parallel_builds: bool
+    ) -> str:
+        assert build_dir is not None
+        if self._temp_build_dir is not None:
+            assert self._temp_build_dir.path
+            return self._temp_build_dir.path
+        if self.req is None:
+            # Some systems have /tmp as a symlink which confuses custom
+            # builds (such as numpy). Thus, we ensure that the real path
+            # is returned.
+            self._temp_build_dir = TempDirectory(
+                kind=tempdir_kinds.REQ_BUILD, globally_managed=True
+            )
+
+            return self._temp_build_dir.path
+
+        # This is the only remaining place where we manually determine the path
+        # for the temporary directory. It is only needed for editables where
+        # it is the value of the --src option.
+
+        # When parallel builds are enabled, add a UUID to the build directory
+        # name so multiple builds do not interfere with each other.
+        dir_name: str = canonicalize_name(self.req.name)
+        if parallel_builds:
+            dir_name = f"{dir_name}_{uuid.uuid4().hex}"
+
+        # FIXME: Is there a better place to create the build_dir? (hg and bzr
+        # need this)
+        if not os.path.exists(build_dir):
+            logger.debug("Creating directory %s", build_dir)
+            os.makedirs(build_dir)
+        actual_build_dir = os.path.join(build_dir, dir_name)
+        # `None` indicates that we respect the globally-configured deletion
+        # settings, which is what we actually want when auto-deleting.
+        delete_arg = None if autodelete else False
+        return TempDirectory(
+            path=actual_build_dir,
+            delete=delete_arg,
+            kind=tempdir_kinds.REQ_BUILD,
+            globally_managed=True,
+        ).path
+
+    def _set_requirement(self) -> None:
+        """Set requirement after generating metadata."""
+        assert self.req is None
+        assert self.metadata is not None
+        assert self.source_dir is not None
+
+        # Construct a Requirement object from the generated metadata
+        if isinstance(parse_version(self.metadata["Version"]), Version):
+            op = "=="
+        else:
+            op = "==="
+
+        self.req = get_requirement(
+            "".join(
+                [
+                    self.metadata["Name"],
+                    op,
+                    self.metadata["Version"],
+                ]
+            )
+        )
+
+    def warn_on_mismatching_name(self) -> None:
+        assert self.req is not None
+        metadata_name = canonicalize_name(self.metadata["Name"])
+        if canonicalize_name(self.req.name) == metadata_name:
+            # Everything is fine.
+            return
+
+        # If we're here, there's a mismatch. Log a warning about it.
+        logger.warning(
+            "Generating metadata for package %s "
+            "produced metadata for project name %s. Fix your "
+            "#egg=%s fragments.",
+            self.name,
+            metadata_name,
+            self.name,
+        )
+        self.req = get_requirement(metadata_name)
+
+    def check_if_exists(self, use_user_site: bool) -> None:
+        """Find an installed distribution that satisfies or conflicts
+        with this requirement, and set self.satisfied_by or
+        self.should_reinstall appropriately.
+        """
+        if self.req is None:
+            return
+        existing_dist = get_default_environment().get_distribution(self.req.name)
+        if not existing_dist:
+            return
+
+        version_compatible = self.req.specifier.contains(
+            existing_dist.version,
+            prereleases=True,
+        )
+        if not version_compatible:
+            self.satisfied_by = None
+            if use_user_site:
+                if existing_dist.in_usersite:
+                    self.should_reinstall = True
+                elif running_under_virtualenv() and existing_dist.in_site_packages:
+                    raise InstallationError(
+                        f"Will not install to the user site because it will "
+                        f"lack sys.path precedence to {existing_dist.raw_name} "
+                        f"in {existing_dist.location}"
+                    )
+            else:
+                self.should_reinstall = True
+        else:
+            if self.editable:
+                self.should_reinstall = True
+                # when installing editables, nothing pre-existing should ever
+                # satisfy
+                self.satisfied_by = None
+            else:
+                self.satisfied_by = existing_dist
+
+    # Things valid for wheels
+    @property
+    def is_wheel(self) -> bool:
+        if not self.link:
+            return False
+        return self.link.is_wheel
+
+    @property
+    def is_wheel_from_cache(self) -> bool:
+        # When True, it means that this InstallRequirement is a local wheel file in the
+        # cache of locally built wheels.
+        return self.cached_wheel_source_link is not None
+
+    # Things valid for sdists
+    @property
+    def unpacked_source_directory(self) -> str:
+        assert self.source_dir, f"No source dir for {self}"
+        return os.path.join(
+            self.source_dir, self.link and self.link.subdirectory_fragment or ""
+        )
+
+    @property
+    def setup_py_path(self) -> str:
+        assert self.source_dir, f"No source dir for {self}"
+        setup_py = os.path.join(self.unpacked_source_directory, "setup.py")
+
+        return setup_py
+
+    @property
+    def pyproject_toml_path(self) -> str:
+        assert self.source_dir, f"No source dir for {self}"
+        return make_pyproject_path(self.unpacked_source_directory)
+
+    def load_pyproject_toml(self) -> None:
+        """Load the pyproject.toml file.
+
+        After calling this routine, all of the attributes related to PEP 517
+        processing for this requirement have been set.
+        """
+        pyproject_toml_data = load_pyproject_toml(
+            self.pyproject_toml_path, self.setup_py_path, str(self)
+        )
+        assert pyproject_toml_data
+        requires, backend, check, backend_path = pyproject_toml_data
+        self.requirements_to_check = check
+        self.pyproject_requires = requires
+        self._pep517_backend_spec = backend
+        self._pep517_backend_path = backend_path
+
+    def configure_backend(self, python_executable: str) -> None:
+        """Set up the build backend hook caller.
+
+        This is done separately after pyproject.toml loading as the backend
+        need to be called with the build environment's Python executable,
+        which can vary."""
+        self.pep517_backend = ConfiguredBuildBackendHookCaller(
+            self,
+            self.unpacked_source_directory,
+            self._pep517_backend_spec,
+            backend_path=self._pep517_backend_path,
+            python_executable=python_executable,
+        )
+
+    def editable_sanity_check(self) -> None:
+        """Check that an editable requirement if valid for use with PEP 517/518.
+
+        This verifies that an editable has a build backend that supports PEP 660.
+        """
+        if self.editable and not self.supports_pyproject_editable:
+            raise InstallationError(
+                f"Project {self} uses a build backend "
+                f"that is missing the 'build_editable' hook, so "
+                f"it cannot be installed in editable mode. "
+                f"Consider using a build backend that supports PEP 660."
+            )
+
+    def prepare_metadata(self, allow_editables: bool) -> None:
+        """Ensure that project metadata is available.
+
+        Under PEP 517 and PEP 660, call the backend hook to prepare the metadata.
+        Under legacy processing, call setup.py egg-info.
+        """
+        assert self.source_dir, f"No source dir for {self}"
+        details = self.name or f"from {self.link}"
+
+        assert self.pep517_backend is not None
+        if self.editable and allow_editables and self.supports_pyproject_editable:
+            self.metadata_directory = generate_editable_metadata(
+                build_env=self.build_env,
+                backend=self.pep517_backend,
+                details=details,
+            )
+        else:
+            self.metadata_directory = generate_metadata(
+                build_env=self.build_env,
+                backend=self.pep517_backend,
+                details=details,
+            )
+
+        # Act on the newly generated metadata, based on the name and version.
+        if not self.name:
+            self._set_requirement()
+        else:
+            self.warn_on_mismatching_name()
+
+        self.assert_source_matches_version()
+
+    @property
+    def metadata(self) -> Any:
+        if not hasattr(self, "_metadata"):
+            self._metadata = self.get_dist().metadata
+
+        return self._metadata
+
+    def set_dist(self, distribution: BaseDistribution) -> None:
+        self._distribution = distribution
+
+    def get_dist(self) -> BaseDistribution:
+        if self._distribution is not None:
+            return self._distribution
+        elif self.metadata_directory:
+            return get_directory_distribution(self.metadata_directory)
+        elif self.local_file_path and self.is_wheel:
+            assert self.req is not None
+            return get_wheel_distribution(
+                FilesystemWheel(self.local_file_path),
+                canonicalize_name(self.req.name),
+            )
+        raise AssertionError(
+            f"InstallRequirement {self} has no metadata directory and no wheel: "
+            f"can't make a distribution."
+        )
+
+    def assert_source_matches_version(self) -> None:
+        assert self.source_dir, f"No source dir for {self}"
+        version = self.metadata["version"]
+        if self.req and self.req.specifier and version not in self.req.specifier:
+            logger.warning(
+                "Requested %s, but installing version %s",
+                self,
+                version,
+            )
+        else:
+            logger.debug(
+                "Source in %s has version %s, which satisfies requirement %s",
+                display_path(self.source_dir),
+                version,
+                self,
+            )
+
+    # For both source distributions and editables
+    def ensure_has_source_dir(
+        self,
+        parent_dir: str,
+        autodelete: bool = False,
+        parallel_builds: bool = False,
+    ) -> None:
+        """Ensure that a source_dir is set.
+
+        This will create a temporary build dir if the name of the requirement
+        isn't known yet.
+
+        :param parent_dir: The ideal pip parent_dir for the source_dir.
+            Generally src_dir for editables and build_dir for sdists.
+        :return: self.source_dir
+        """
+        if self.source_dir is None:
+            self.source_dir = self.ensure_build_location(
+                parent_dir,
+                autodelete=autodelete,
+                parallel_builds=parallel_builds,
+            )
+
+    def needs_unpacked_archive(self, archive_source: Path) -> None:
+        assert self._archive_source is None
+        self._archive_source = archive_source
+
+    def ensure_pristine_source_checkout(self) -> None:
+        """Ensure the source directory has not yet been built in."""
+        assert self.source_dir is not None
+        if self._archive_source is not None:
+            unpack_file(str(self._archive_source), self.source_dir)
+        elif is_installable_dir(self.source_dir):
+            # If a checkout exists, it's unwise to keep going.
+            # version inconsistencies are logged later, but do not fail
+            # the installation.
+            raise PreviousBuildDirError(
+                f"pip can't proceed with requirements '{self}' due to a "
+                f"pre-existing build directory ({self.source_dir}). This is likely "
+                "due to a previous installation that failed . pip is "
+                "being responsible and not assuming it can delete this. "
+                "Please delete it and try again."
+            )
+
+    # For editable installations
+    def update_editable(self) -> None:
+        if not self.link:
+            logger.debug(
+                "Cannot update repository at %s; repository location is unknown",
+                self.source_dir,
+            )
+            return
+        assert self.editable
+        assert self.source_dir
+        if self.link.scheme == "file":
+            # Static paths don't get updated
+            return
+        vcs_backend = vcs.get_backend_for_scheme(self.link.scheme)
+        # Editable requirements are validated in Requirement constructors.
+        # So here, if it's neither a path nor a valid VCS URL, it's a bug.
+        assert vcs_backend, f"Unsupported VCS URL {self.link.url}"
+        hidden_url = hide_url(self.link.url)
+        vcs_backend.obtain(self.source_dir, url=hidden_url, verbosity=0)
+
+    # Top-level Actions
+    def uninstall(
+        self, auto_confirm: bool = False, verbose: bool = False
+    ) -> UninstallPathSet | None:
+        """
+        Uninstall the distribution currently satisfying this requirement.
+
+        Prompts before removing or modifying files unless
+        ``auto_confirm`` is True.
+
+        Refuses to delete or modify files outside of ``sys.prefix`` -
+        thus uninstallation within a virtual environment can only
+        modify that virtual environment, even if the virtualenv is
+        linked to global site-packages.
+
+        """
+        assert self.req
+        dist = get_default_environment().get_distribution(self.req.name)
+        if not dist:
+            logger.warning("Skipping %s as it is not installed.", self.name)
+            return None
+        logger.info("Found existing installation: %s", dist)
+
+        uninstalled_pathset = UninstallPathSet.from_dist(dist)
+        uninstalled_pathset.remove(auto_confirm, verbose)
+        return uninstalled_pathset
+
+    def _get_archive_name(self, path: str, parentdir: str, rootdir: str) -> str:
+        def _clean_zip_name(name: str, prefix: str) -> str:
+            assert name.startswith(
+                prefix + os.path.sep
+            ), f"name {name!r} doesn't start with prefix {prefix!r}"
+            name = name[len(prefix) + 1 :]
+            name = name.replace(os.path.sep, "/")
+            return name
+
+        assert self.req is not None
+        path = os.path.join(parentdir, path)
+        name = _clean_zip_name(path, rootdir)
+        return self.req.name + "/" + name
+
+    def archive(self, build_dir: str | None) -> None:
+        """Saves archive to provided build_dir.
+
+        Used for saving downloaded VCS requirements as part of `pip download`.
+        """
+        assert self.source_dir
+        if build_dir is None:
+            return
+
+        create_archive = True
+        archive_name = "{}-{}.zip".format(self.name, self.metadata["version"])
+        archive_path = os.path.join(build_dir, archive_name)
+
+        if os.path.exists(archive_path):
+            response = ask_path_exists(
+                f"The file {display_path(archive_path)} exists. (i)gnore, (w)ipe, "
+                "(b)ackup, (a)bort ",
+                ("i", "w", "b", "a"),
+            )
+            if response == "i":
+                create_archive = False
+            elif response == "w":
+                logger.warning("Deleting %s", display_path(archive_path))
+                os.remove(archive_path)
+            elif response == "b":
+                dest_file = backup_dir(archive_path)
+                logger.warning(
+                    "Backing up %s to %s",
+                    display_path(archive_path),
+                    display_path(dest_file),
+                )
+                shutil.move(archive_path, dest_file)
+            elif response == "a":
+                sys.exit(-1)
+
+        if not create_archive:
+            return
+
+        zip_output = zipfile.ZipFile(
+            archive_path,
+            "w",
+            zipfile.ZIP_DEFLATED,
+            allowZip64=True,
+        )
+        with zip_output:
+            dir = os.path.normcase(os.path.abspath(self.unpacked_source_directory))
+            for dirpath, dirnames, filenames in os.walk(dir):
+                for dirname in dirnames:
+                    dir_arcname = self._get_archive_name(
+                        dirname,
+                        parentdir=dirpath,
+                        rootdir=dir,
+                    )
+                    zipdir = zipfile.ZipInfo(dir_arcname + "/")
+                    zipdir.external_attr = 0x1ED << 16  # 0o755
+                    zip_output.writestr(zipdir, "")
+                for filename in filenames:
+                    file_arcname = self._get_archive_name(
+                        filename,
+                        parentdir=dirpath,
+                        rootdir=dir,
+                    )
+                    filename = os.path.join(dirpath, filename)
+                    zip_output.write(filename, file_arcname)
+
+        logger.info("Saved %s", display_path(archive_path))
+
+    def install(
+        self,
+        root: str | None = None,
+        home: str | None = None,
+        prefix: str | None = None,
+        warn_script_location: bool = True,
+        use_user_site: bool = False,
+        pycompile: bool = True,
+        script_executable: str | None = None,
+    ) -> None:
+        # Lazy import to avoid transitively importing `_vendor.distlib.compat`
+        # which in turn imports `urllib.request` which is slow.
+        # During an actual installation, `urllib.request` will end up imported anyway,
+        # but `req.req_install` (this module) is also imported from commands that
+        # don't actually install anything (e.g. `pip freeze` or `pip show`).
+        from pip._internal.operations.install.wheel import install_wheel
+
+        assert self.req is not None
+        scheme = get_scheme(
+            self.req.name,
+            user=use_user_site,
+            home=home,
+            root=root,
+            isolated=self.isolated,
+            prefix=prefix,
+        )
+
+        assert self.is_wheel
+        assert self.local_file_path
+
+        install_wheel(
+            self.req.name,
+            self.local_file_path,
+            scheme=scheme,
+            req_description=str(self.req),
+            pycompile=pycompile,
+            warn_script_location=warn_script_location,
+            direct_url=self.download_info if self.is_direct else None,
+            requested=self.user_supplied,
+            script_executable=script_executable,
+        )
+        self.install_succeeded = True
+
+
+def check_invalid_constraint_type(req: InstallRequirement) -> str:
+    # Check for unsupported forms
+    problem = ""
+    if not req.name:
+        problem = "Unnamed requirements are not allowed as constraints"
+    elif req.editable:
+        problem = "Editable requirements are not allowed as constraints"
+    elif req.extras:
+        problem = "Constraints cannot have extras"
+
+    if problem:
+        deprecated(
+            reason=(
+                "Constraints are only allowed to take the form of a package "
+                "name and a version specifier. Other forms were originally "
+                "permitted as an accident of the implementation, but were "
+                "undocumented. The new implementation of the resolver no "
+                "longer supports these forms."
+            ),
+            replacement="replacing the constraint with a requirement",
+            # No plan yet for when the new resolver becomes default
+            gone_in=None,
+            issue=8210,
+        )
+
+    return problem
+
+
+def _has_option(options: Values, reqs: list[InstallRequirement], option: str) -> bool:
+    if getattr(options, option, None):
+        return True
+    for req in reqs:
+        if getattr(req, option, None):
+            return True
+    return False
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/req_set.py b/venv/lib/python3.12/site-packages/pip/_internal/req/req_set.py
new file mode 100644
index 0000000..3451b24
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/req/req_set.py
@@ -0,0 +1,81 @@
+import logging
+from collections import OrderedDict
+
+from pip._vendor.packaging.utils import canonicalize_name
+
+from pip._internal.req.req_install import InstallRequirement
+
+logger = logging.getLogger(__name__)
+
+
+class RequirementSet:
+    def __init__(self, check_supported_wheels: bool = True) -> None:
+        """Create a RequirementSet."""
+
+        self.requirements: dict[str, InstallRequirement] = OrderedDict()
+        self.check_supported_wheels = check_supported_wheels
+
+        self.unnamed_requirements: list[InstallRequirement] = []
+
+    def __str__(self) -> str:
+        requirements = sorted(
+            (req for req in self.requirements.values() if not req.comes_from),
+            key=lambda req: canonicalize_name(req.name or ""),
+        )
+        return " ".join(str(req.req) for req in requirements)
+
+    def __repr__(self) -> str:
+        requirements = sorted(
+            self.requirements.values(),
+            key=lambda req: canonicalize_name(req.name or ""),
+        )
+
+        format_string = "<{classname} object; {count} requirement(s): {reqs}>"
+        return format_string.format(
+            classname=self.__class__.__name__,
+            count=len(requirements),
+            reqs=", ".join(str(req.req) for req in requirements),
+        )
+
+    def add_unnamed_requirement(self, install_req: InstallRequirement) -> None:
+        assert not install_req.name
+        self.unnamed_requirements.append(install_req)
+
+    def add_named_requirement(self, install_req: InstallRequirement) -> None:
+        assert install_req.name
+
+        project_name = canonicalize_name(install_req.name)
+        self.requirements[project_name] = install_req
+
+    def has_requirement(self, name: str) -> bool:
+        project_name = canonicalize_name(name)
+
+        return (
+            project_name in self.requirements
+            and not self.requirements[project_name].constraint
+        )
+
+    def get_requirement(self, name: str) -> InstallRequirement:
+        project_name = canonicalize_name(name)
+
+        if project_name in self.requirements:
+            return self.requirements[project_name]
+
+        raise KeyError(f"No project with the name {name!r}")
+
+    @property
+    def all_requirements(self) -> list[InstallRequirement]:
+        return self.unnamed_requirements + list(self.requirements.values())
+
+    @property
+    def requirements_to_install(self) -> list[InstallRequirement]:
+        """Return the list of requirements that need to be installed.
+
+        TODO remove this property together with the legacy resolver, since the new
+             resolver only returns requirements that need to be installed.
+        """
+        return [
+            install_req
+            for install_req in self.all_requirements
+            if not install_req.constraint and not install_req.satisfied_by
+        ]
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/req/req_uninstall.py b/venv/lib/python3.12/site-packages/pip/_internal/req/req_uninstall.py
new file mode 100644
index 0000000..0459c7b
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/req/req_uninstall.py
@@ -0,0 +1,644 @@
+from __future__ import annotations
+
+import functools
+import os
+import sys
+import sysconfig
+from collections.abc import Callable, Generator, Iterable
+from importlib.util import cache_from_source
+from typing import Any
+
+from pip._internal.exceptions import LegacyDistutilsInstall, UninstallMissingRecord
+from pip._internal.locations import get_bin_prefix, get_bin_user
+from pip._internal.metadata import BaseDistribution
+from pip._internal.utils.compat import WINDOWS
+from pip._internal.utils.egg_link import egg_link_path_from_location
+from pip._internal.utils.logging import getLogger, indent_log
+from pip._internal.utils.misc import ask, normalize_path, renames, rmtree
+from pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory
+from pip._internal.utils.virtualenv import running_under_virtualenv
+
+logger = getLogger(__name__)
+
+
+def _script_names(
+    bin_dir: str, script_name: str, is_gui: bool
+) -> Generator[str, None, None]:
+    """Create the fully qualified name of the files created by
+    {console,gui}_scripts for the given ``dist``.
+    Returns the list of file names
+    """
+    exe_name = os.path.join(bin_dir, script_name)
+    yield exe_name
+    if not WINDOWS:
+        return
+    yield f"{exe_name}.exe"
+    yield f"{exe_name}.exe.manifest"
+    if is_gui:
+        yield f"{exe_name}-script.pyw"
+    else:
+        yield f"{exe_name}-script.py"
+
+
+def _unique(
+    fn: Callable[..., Generator[Any, None, None]],
+) -> Callable[..., Generator[Any, None, None]]:
+    @functools.wraps(fn)
+    def unique(*args: Any, **kw: Any) -> Generator[Any, None, None]:
+        seen: set[Any] = set()
+        for item in fn(*args, **kw):
+            if item not in seen:
+                seen.add(item)
+                yield item
+
+    return unique
+
+
+@_unique
+def uninstallation_paths(dist: BaseDistribution) -> Generator[str, None, None]:
+    """
+    Yield all the uninstallation paths for dist based on RECORD-without-.py[co]
+
+    Yield paths to all the files in RECORD. For each .py file in RECORD, add
+    the .pyc and .pyo in the same directory.
+
+    UninstallPathSet.add() takes care of the __pycache__ .py[co].
+
+    If RECORD is not found, raises an error,
+    with possible information from the INSTALLER file.
+
+    https://packaging.python.org/specifications/recording-installed-packages/
+    """
+    location = dist.location
+    assert location is not None, "not installed"
+
+    entries = dist.iter_declared_entries()
+    if entries is None:
+        raise UninstallMissingRecord(distribution=dist)
+
+    for entry in entries:
+        path = os.path.join(location, entry)
+        yield path
+        if path.endswith(".py"):
+            dn, fn = os.path.split(path)
+            base = fn[:-3]
+            path = os.path.join(dn, base + ".pyc")
+            yield path
+            path = os.path.join(dn, base + ".pyo")
+            yield path
+
+
+def compact(paths: Iterable[str]) -> set[str]:
+    """Compact a path set to contain the minimal number of paths
+    necessary to contain all paths in the set. If /a/path/ and
+    /a/path/to/a/file.txt are both in the set, leave only the
+    shorter path."""
+
+    sep = os.path.sep
+    short_paths: set[str] = set()
+    prefixes: set[str] = set()
+
+    for path in sorted(paths, key=len):
+        current = path[:-1] if path.endswith(sep + "*") else path
+        parent = current.rstrip(sep)
+        while parent:
+            if parent in prefixes:
+                break
+            next_parent = os.path.dirname(parent).rstrip(sep)
+            parent = "" if next_parent == parent else next_parent
+        else:
+            short_paths.add(path)
+            prefixes.add(current.rstrip(sep))
+    return short_paths
+
+
+def compress_for_rename(paths: Iterable[str]) -> set[str]:
+    """Returns a set containing the paths that need to be renamed.
+
+    This set may include directories when the original sequence of paths
+    included every file on disk.
+    """
+    case_map = {os.path.normcase(p): p for p in paths}
+    remaining = set(case_map)
+    unchecked = sorted({os.path.split(p)[0] for p in case_map.values()}, key=len)
+    wildcards: set[str] = set()
+
+    def norm_join(*a: str) -> str:
+        return os.path.normcase(os.path.join(*a))
+
+    for root in unchecked:
+        if any(os.path.normcase(root).startswith(w) for w in wildcards):
+            # This directory has already been handled.
+            continue
+
+        all_files: set[str] = set()
+        all_subdirs: set[str] = set()
+        for dirname, subdirs, files in os.walk(root):
+            all_subdirs.update(norm_join(root, dirname, d) for d in subdirs)
+            all_files.update(norm_join(root, dirname, f) for f in files)
+        # If all the files we found are in our remaining set of files to
+        # remove, then remove them from the latter set and add a wildcard
+        # for the directory.
+        if not (all_files - remaining):
+            remaining.difference_update(all_files)
+            wildcards.add(root + os.sep)
+
+    return set(map(case_map.__getitem__, remaining)) | wildcards
+
+
+def compress_for_output_listing(paths: Iterable[str]) -> tuple[set[str], set[str]]:
+    """Returns a tuple of 2 sets of which paths to display to user
+
+    The first set contains paths that would be deleted. Files of a package
+    are not added and the top-level directory of the package has a '*' added
+    at the end - to signify that all it's contents are removed.
+
+    The second set contains files that would have been skipped in the above
+    folders.
+    """
+
+    will_remove = set(paths)
+    will_skip = set()
+
+    # Determine folders and files
+    folders = set()
+    files = set()
+    for path in will_remove:
+        if path.endswith(".pyc"):
+            continue
+        if path.endswith("__init__.py") or ".dist-info" in path:
+            folders.add(os.path.dirname(path))
+        files.add(path)
+
+    _normcased_files = set(map(os.path.normcase, files))
+
+    folders = compact(folders)
+
+    # This walks the tree using os.walk to not miss extra folders
+    # that might get added.
+    for folder in folders:
+        for dirpath, _, dirfiles in os.walk(folder):
+            for fname in dirfiles:
+                if fname.endswith(".pyc"):
+                    continue
+
+                file_ = os.path.join(dirpath, fname)
+                if (
+                    os.path.isfile(file_)
+                    and os.path.normcase(file_) not in _normcased_files
+                ):
+                    # We are skipping this file. Add it to the set.
+                    will_skip.add(file_)
+
+    will_remove = files | {os.path.join(folder, "*") for folder in folders}
+
+    return will_remove, will_skip
+
+
+class StashedUninstallPathSet:
+    """A set of file rename operations to stash files while
+    tentatively uninstalling them."""
+
+    def __init__(self) -> None:
+        # Mapping from source file root to [Adjacent]TempDirectory
+        # for files under that directory.
+        self._save_dirs: dict[str, TempDirectory] = {}
+        # (old path, new path) tuples for each move that may need
+        # to be undone.
+        self._moves: list[tuple[str, str]] = []
+
+    def _get_directory_stash(self, path: str) -> str:
+        """Stashes a directory.
+
+        Directories are stashed adjacent to their original location if
+        possible, or else moved/copied into the user's temp dir."""
+
+        try:
+            save_dir: TempDirectory = AdjacentTempDirectory(path)
+        except OSError:
+            save_dir = TempDirectory(kind="uninstall")
+        self._save_dirs[os.path.normcase(path)] = save_dir
+
+        return save_dir.path
+
+    def _get_file_stash(self, path: str) -> str:
+        """Stashes a file.
+
+        If no root has been provided, one will be created for the directory
+        in the user's temp directory."""
+        path = os.path.normcase(path)
+        head, old_head = os.path.dirname(path), None
+        save_dir = None
+
+        while head != old_head:
+            try:
+                save_dir = self._save_dirs[head]
+                break
+            except KeyError:
+                pass
+            head, old_head = os.path.dirname(head), head
+        else:
+            # Did not find any suitable root
+            head = os.path.dirname(path)
+            save_dir = TempDirectory(kind="uninstall")
+            self._save_dirs[head] = save_dir
+
+        relpath = os.path.relpath(path, head)
+        if relpath and relpath != os.path.curdir:
+            return os.path.join(save_dir.path, relpath)
+        return save_dir.path
+
+    def stash(self, path: str) -> str:
+        """Stashes the directory or file and returns its new location.
+        Handle symlinks as files to avoid modifying the symlink targets.
+        """
+        path_is_dir = os.path.isdir(path) and not os.path.islink(path)
+        if path_is_dir:
+            new_path = self._get_directory_stash(path)
+        else:
+            new_path = self._get_file_stash(path)
+
+        self._moves.append((path, new_path))
+        if path_is_dir and os.path.isdir(new_path):
+            # If we're moving a directory, we need to
+            # remove the destination first or else it will be
+            # moved to inside the existing directory.
+            # We just created new_path ourselves, so it will
+            # be removable.
+            os.rmdir(new_path)
+        renames(path, new_path)
+        return new_path
+
+    def commit(self) -> None:
+        """Commits the uninstall by removing stashed files."""
+        for save_dir in self._save_dirs.values():
+            save_dir.cleanup()
+        self._moves = []
+        self._save_dirs = {}
+
+    def rollback(self) -> None:
+        """Undoes the uninstall by moving stashed files back."""
+        for p in self._moves:
+            logger.info("Moving to %s\n from %s", *p)
+
+        for new_path, path in self._moves:
+            try:
+                logger.debug("Replacing %s from %s", new_path, path)
+                if os.path.isfile(new_path) or os.path.islink(new_path):
+                    os.unlink(new_path)
+                elif os.path.isdir(new_path):
+                    rmtree(new_path)
+                renames(path, new_path)
+            except OSError as ex:
+                logger.error("Failed to restore %s", new_path)
+                logger.debug("Exception: %s", ex)
+
+        self.commit()
+
+    @property
+    def can_rollback(self) -> bool:
+        return bool(self._moves)
+
+
+class UninstallPathSet:
+    """A set of file paths to be removed in the uninstallation of a
+    requirement."""
+
+    def __init__(self, dist: BaseDistribution) -> None:
+        self._paths: set[str] = set()
+        self._refuse: set[str] = set()
+        self._pth: dict[str, UninstallPthEntries] = {}
+        self._dist = dist
+        self._moved_paths = StashedUninstallPathSet()
+        # Create local cache of normalize_path results. Creating an UninstallPathSet
+        # can result in hundreds/thousands of redundant calls to normalize_path with
+        # the same args, which hurts performance.
+        self._normalize_path_cached = functools.lru_cache(normalize_path)
+
+    def _permitted(self, path: str) -> bool:
+        """
+        Return True if the given path is one we are permitted to
+        remove/modify, False otherwise.
+
+        """
+        # aka is_local, but caching normalized sys.prefix
+        if not running_under_virtualenv():
+            return True
+        return path.startswith(self._normalize_path_cached(sys.prefix))
+
+    def add(self, path: str) -> None:
+        head, tail = os.path.split(path)
+
+        # we normalize the head to resolve parent directory symlinks, but not
+        # the tail, since we only want to uninstall symlinks, not their targets
+        path = os.path.join(self._normalize_path_cached(head), os.path.normcase(tail))
+
+        if not os.path.exists(path):
+            return
+        if self._permitted(path):
+            self._paths.add(path)
+        else:
+            self._refuse.add(path)
+
+        # __pycache__ files can show up after 'installed-files.txt' is created,
+        # due to imports
+        if os.path.splitext(path)[1] == ".py":
+            self.add(cache_from_source(path))
+
+    def add_pth(self, pth_file: str, entry: str) -> None:
+        pth_file = self._normalize_path_cached(pth_file)
+        if self._permitted(pth_file):
+            if pth_file not in self._pth:
+                self._pth[pth_file] = UninstallPthEntries(pth_file)
+            self._pth[pth_file].add(entry)
+        else:
+            self._refuse.add(pth_file)
+
+    def remove(self, auto_confirm: bool = False, verbose: bool = False) -> None:
+        """Remove paths in ``self._paths`` with confirmation (unless
+        ``auto_confirm`` is True)."""
+
+        if not self._paths:
+            logger.info(
+                "Can't uninstall '%s'. No files were found to uninstall.",
+                self._dist.raw_name,
+            )
+            return
+
+        dist_name_version = f"{self._dist.raw_name}-{self._dist.raw_version}"
+        logger.info("Uninstalling %s:", dist_name_version)
+
+        with indent_log():
+            if auto_confirm or self._allowed_to_proceed(verbose):
+                moved = self._moved_paths
+
+                for_rename = compress_for_rename(self._paths)
+
+                for path in sorted(compact(for_rename)):
+                    moved.stash(path)
+                    logger.verbose("Removing file or directory %s", path)
+
+                for pth in self._pth.values():
+                    pth.remove()
+
+                logger.info("Successfully uninstalled %s", dist_name_version)
+
+    def _allowed_to_proceed(self, verbose: bool) -> bool:
+        """Display which files would be deleted and prompt for confirmation"""
+
+        def _display(msg: str, paths: Iterable[str]) -> None:
+            if not paths:
+                return
+
+            logger.info(msg)
+            with indent_log():
+                for path in sorted(compact(paths)):
+                    logger.info(path)
+
+        if not verbose:
+            will_remove, will_skip = compress_for_output_listing(self._paths)
+        else:
+            # In verbose mode, display all the files that are going to be
+            # deleted.
+            will_remove = set(self._paths)
+            will_skip = set()
+
+        _display("Would remove:", will_remove)
+        _display("Would not remove (might be manually added):", will_skip)
+        _display("Would not remove (outside of prefix):", self._refuse)
+        if verbose:
+            _display("Will actually move:", compress_for_rename(self._paths))
+
+        return ask("Proceed (Y/n)? ", ("y", "n", "")) != "n"
+
+    def rollback(self) -> None:
+        """Rollback the changes previously made by remove()."""
+        if not self._moved_paths.can_rollback:
+            logger.error(
+                "Can't roll back %s; was not uninstalled",
+                self._dist.raw_name,
+            )
+            return
+        logger.info("Rolling back uninstall of %s", self._dist.raw_name)
+        self._moved_paths.rollback()
+        for pth in self._pth.values():
+            pth.rollback()
+
+    def commit(self) -> None:
+        """Remove temporary save dir: rollback will no longer be possible."""
+        self._moved_paths.commit()
+
+    @classmethod
+    def from_dist(cls, dist: BaseDistribution) -> UninstallPathSet:
+        dist_location = dist.location
+        info_location = dist.info_location
+        if dist_location is None:
+            logger.info(
+                "Not uninstalling %s since it is not installed",
+                dist.canonical_name,
+            )
+            return cls(dist)
+
+        normalized_dist_location = normalize_path(dist_location)
+        if not dist.local:
+            logger.info(
+                "Not uninstalling %s at %s, outside environment %s",
+                dist.canonical_name,
+                normalized_dist_location,
+                sys.prefix,
+            )
+            return cls(dist)
+
+        if normalized_dist_location in {
+            p
+            for p in {sysconfig.get_path("stdlib"), sysconfig.get_path("platstdlib")}
+            if p
+        }:
+            logger.info(
+                "Not uninstalling %s at %s, as it is in the standard library.",
+                dist.canonical_name,
+                normalized_dist_location,
+            )
+            return cls(dist)
+
+        paths_to_remove = cls(dist)
+        develop_egg_link = egg_link_path_from_location(dist.raw_name)
+
+        # Distribution is installed with metadata in a "flat" .egg-info
+        # directory. This means it is not a modern .dist-info installation, an
+        # egg, or legacy editable.
+        setuptools_flat_installation = (
+            dist.installed_with_setuptools_egg_info
+            and info_location is not None
+            and os.path.exists(info_location)
+            # If dist is editable and the location points to a ``.egg-info``,
+            # we are in fact in the legacy editable case.
+            and not info_location.endswith(f"{dist.setuptools_filename}.egg-info")
+        )
+
+        # Uninstall cases order do matter as in the case of 2 installs of the
+        # same package, pip needs to uninstall the currently detected version
+        if setuptools_flat_installation:
+            if info_location is not None:
+                paths_to_remove.add(info_location)
+            installed_files = dist.iter_declared_entries()
+            if installed_files is not None:
+                for installed_file in installed_files:
+                    paths_to_remove.add(os.path.join(dist_location, installed_file))
+            # FIXME: need a test for this elif block
+            # occurs with --single-version-externally-managed/--record outside
+            # of pip
+            elif dist.is_file("top_level.txt"):
+                try:
+                    namespace_packages = dist.read_text("namespace_packages.txt")
+                except FileNotFoundError:
+                    namespaces = []
+                else:
+                    namespaces = namespace_packages.splitlines(keepends=False)
+                for top_level_pkg in [
+                    p
+                    for p in dist.read_text("top_level.txt").splitlines()
+                    if p and p not in namespaces
+                ]:
+                    path = os.path.join(dist_location, top_level_pkg)
+                    paths_to_remove.add(path)
+                    paths_to_remove.add(f"{path}.py")
+                    paths_to_remove.add(f"{path}.pyc")
+                    paths_to_remove.add(f"{path}.pyo")
+
+        elif dist.installed_by_distutils:
+            raise LegacyDistutilsInstall(distribution=dist)
+
+        elif dist.installed_as_egg:
+            # package installed by easy_install
+            # We cannot match on dist.egg_name because it can slightly vary
+            # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg
+            # XXX We use normalized_dist_location because dist_location my contain
+            # a trailing / if the distribution is a zipped egg
+            # (which is not a directory).
+            paths_to_remove.add(normalized_dist_location)
+            easy_install_egg = os.path.split(normalized_dist_location)[1]
+            easy_install_pth = os.path.join(
+                os.path.dirname(normalized_dist_location),
+                "easy-install.pth",
+            )
+            paths_to_remove.add_pth(easy_install_pth, "./" + easy_install_egg)
+
+        elif dist.installed_with_dist_info:
+            for path in uninstallation_paths(dist):
+                paths_to_remove.add(path)
+
+        elif develop_egg_link:
+            # PEP 660 modern editable is handled in the ``.dist-info`` case
+            # above, so this only covers the setuptools-style editable.
+            with open(develop_egg_link) as fh:
+                link_pointer = os.path.normcase(fh.readline().strip())
+                normalized_link_pointer = paths_to_remove._normalize_path_cached(
+                    link_pointer
+                )
+            assert os.path.samefile(
+                normalized_link_pointer, normalized_dist_location
+            ), (
+                f"Egg-link {develop_egg_link} (to {link_pointer}) does not match "
+                f"installed location of {dist.raw_name} (at {dist_location})"
+            )
+            paths_to_remove.add(develop_egg_link)
+            easy_install_pth = os.path.join(
+                os.path.dirname(develop_egg_link), "easy-install.pth"
+            )
+            paths_to_remove.add_pth(easy_install_pth, dist_location)
+
+        else:
+            logger.debug(
+                "Not sure how to uninstall: %s - Check: %s",
+                dist,
+                dist_location,
+            )
+
+        if dist.in_usersite:
+            bin_dir = get_bin_user()
+        else:
+            bin_dir = get_bin_prefix()
+
+        # find distutils scripts= scripts
+        try:
+            for script in dist.iter_distutils_script_names():
+                paths_to_remove.add(os.path.join(bin_dir, script))
+                if WINDOWS:
+                    paths_to_remove.add(os.path.join(bin_dir, f"{script}.bat"))
+        except (FileNotFoundError, NotADirectoryError):
+            pass
+
+        # find console_scripts and gui_scripts
+        def iter_scripts_to_remove(
+            dist: BaseDistribution,
+            bin_dir: str,
+        ) -> Generator[str, None, None]:
+            for entry_point in dist.iter_entry_points():
+                if entry_point.group == "console_scripts":
+                    yield from _script_names(bin_dir, entry_point.name, False)
+                elif entry_point.group == "gui_scripts":
+                    yield from _script_names(bin_dir, entry_point.name, True)
+
+        for s in iter_scripts_to_remove(dist, bin_dir):
+            paths_to_remove.add(s)
+
+        return paths_to_remove
+
+
+class UninstallPthEntries:
+    def __init__(self, pth_file: str) -> None:
+        self.file = pth_file
+        self.entries: set[str] = set()
+        self._saved_lines: list[bytes] | None = None
+
+    def add(self, entry: str) -> None:
+        entry = os.path.normcase(entry)
+        # On Windows, os.path.normcase converts the entry to use
+        # backslashes.  This is correct for entries that describe absolute
+        # paths outside of site-packages, but all the others use forward
+        # slashes.
+        # os.path.splitdrive is used instead of os.path.isabs because isabs
+        # treats non-absolute paths with drive letter markings like c:foo\bar
+        # as absolute paths. It also does not recognize UNC paths if they don't
+        # have more than "\\sever\share". Valid examples: "\\server\share\" or
+        # "\\server\share\folder".
+        if WINDOWS and not os.path.splitdrive(entry)[0]:
+            entry = entry.replace("\\", "/")
+        self.entries.add(entry)
+
+    def remove(self) -> None:
+        logger.verbose("Removing pth entries from %s:", self.file)
+
+        # If the file doesn't exist, log a warning and return
+        if not os.path.isfile(self.file):
+            logger.warning("Cannot remove entries from nonexistent file %s", self.file)
+            return
+        with open(self.file, "rb") as fh:
+            # windows uses '\r\n' with py3k, but uses '\n' with py2.x
+            lines = fh.readlines()
+            self._saved_lines = lines
+        if any(b"\r\n" in line for line in lines):
+            endline = "\r\n"
+        else:
+            endline = "\n"
+        # handle missing trailing newline
+        if lines and not lines[-1].endswith(endline.encode("utf-8")):
+            lines[-1] = lines[-1] + endline.encode("utf-8")
+        for entry in self.entries:
+            try:
+                logger.verbose("Removing entry: %s", entry)
+                lines.remove((entry + endline).encode("utf-8"))
+            except ValueError:
+                pass
+        with open(self.file, "wb") as fh:
+            fh.writelines(lines)
+
+    def rollback(self) -> bool:
+        if self._saved_lines is None:
+            logger.error("Cannot roll back changes to %s, none were made", self.file)
+            return False
+        logger.debug("Rolling %s back to previous state", self.file)
+        with open(self.file, "wb") as fh:
+            fh.writelines(self._saved_lines)
+        return True
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/resolution/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..5ca74af
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/base.cpython-312.pyc
new file mode 100644
index 0000000..bf6e051
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/base.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/base.py b/venv/lib/python3.12/site-packages/pip/_internal/resolution/base.py
new file mode 100644
index 0000000..3e35de7
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/resolution/base.py
@@ -0,0 +1,20 @@
+from collections.abc import Callable
+
+from pip._internal.req.req_install import InstallRequirement
+from pip._internal.req.req_set import RequirementSet
+
+InstallRequirementProvider = Callable[
+    [str, InstallRequirement | None], InstallRequirement
+]
+
+
+class BaseResolver:
+    def resolve(
+        self, root_reqs: list[InstallRequirement], check_supported_wheels: bool
+    ) -> RequirementSet:
+        raise NotImplementedError()
+
+    def get_installation_order(
+        self, req_set: RequirementSet
+    ) -> list[InstallRequirement]:
+        raise NotImplementedError()
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..8ff9de3
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-312.pyc
new file mode 100644
index 0000000..634b739
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/resolver.py b/venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/resolver.py
new file mode 100644
index 0000000..f2b0379
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/resolver.py
@@ -0,0 +1,598 @@
+"""Dependency Resolution
+
+The dependency resolution in pip is performed as follows:
+
+for top-level requirements:
+    a. only one spec allowed per project, regardless of conflicts or not.
+       otherwise a "double requirement" exception is raised
+    b. they override sub-dependency requirements.
+for sub-dependencies
+    a. "first found, wins" (where the order is breadth first)
+"""
+
+from __future__ import annotations
+
+import logging
+import sys
+from collections import defaultdict
+from collections.abc import Iterable
+from itertools import chain
+
+from pip._vendor.packaging import specifiers
+from pip._vendor.packaging.requirements import Requirement
+
+from pip._internal.cache import WheelCache
+from pip._internal.exceptions import (
+    BestVersionAlreadyInstalled,
+    DistributionNotFound,
+    HashError,
+    HashErrors,
+    InstallationError,
+    NoneMetadataError,
+    UnsupportedPythonVersion,
+)
+from pip._internal.index.package_finder import PackageFinder
+from pip._internal.metadata import BaseDistribution
+from pip._internal.models.link import Link
+from pip._internal.models.wheel import Wheel
+from pip._internal.operations.prepare import RequirementPreparer
+from pip._internal.req.req_install import (
+    InstallRequirement,
+    check_invalid_constraint_type,
+)
+from pip._internal.req.req_set import RequirementSet
+from pip._internal.resolution.base import BaseResolver, InstallRequirementProvider
+from pip._internal.utils import compatibility_tags
+from pip._internal.utils.compatibility_tags import get_supported
+from pip._internal.utils.direct_url_helpers import direct_url_from_link
+from pip._internal.utils.logging import indent_log
+from pip._internal.utils.misc import normalize_version_info
+from pip._internal.utils.packaging import check_requires_python
+
+logger = logging.getLogger(__name__)
+
+DiscoveredDependencies = defaultdict[str | None, list[InstallRequirement]]
+
+
+def _check_dist_requires_python(
+    dist: BaseDistribution,
+    version_info: tuple[int, int, int],
+    ignore_requires_python: bool = False,
+) -> None:
+    """
+    Check whether the given Python version is compatible with a distribution's
+    "Requires-Python" value.
+
+    :param version_info: A 3-tuple of ints representing the Python
+        major-minor-micro version to check.
+    :param ignore_requires_python: Whether to ignore the "Requires-Python"
+        value if the given Python version isn't compatible.
+
+    :raises UnsupportedPythonVersion: When the given Python version isn't
+        compatible.
+    """
+    # This idiosyncratically converts the SpecifierSet to str and let
+    # check_requires_python then parse it again into SpecifierSet. But this
+    # is the legacy resolver so I'm just not going to bother refactoring.
+    try:
+        requires_python = str(dist.requires_python)
+    except FileNotFoundError as e:
+        raise NoneMetadataError(dist, str(e))
+    try:
+        is_compatible = check_requires_python(
+            requires_python,
+            version_info=version_info,
+        )
+    except specifiers.InvalidSpecifier as exc:
+        logger.warning(
+            "Package %r has an invalid Requires-Python: %s", dist.raw_name, exc
+        )
+        return
+
+    if is_compatible:
+        return
+
+    version = ".".join(map(str, version_info))
+    if ignore_requires_python:
+        logger.debug(
+            "Ignoring failed Requires-Python check for package %r: %s not in %r",
+            dist.raw_name,
+            version,
+            requires_python,
+        )
+        return
+
+    raise UnsupportedPythonVersion(
+        f"Package {dist.raw_name!r} requires a different Python: "
+        f"{version} not in {requires_python!r}"
+    )
+
+
+class Resolver(BaseResolver):
+    """Resolves which packages need to be installed/uninstalled to perform \
+    the requested operation without breaking the requirements of any package.
+    """
+
+    _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"}
+
+    def __init__(
+        self,
+        preparer: RequirementPreparer,
+        finder: PackageFinder,
+        wheel_cache: WheelCache | None,
+        make_install_req: InstallRequirementProvider,
+        use_user_site: bool,
+        ignore_dependencies: bool,
+        only_dependencies: bool,  # unused, for compatibility with new resolver
+        ignore_installed: bool,
+        ignore_requires_python: bool,
+        force_reinstall: bool,
+        upgrade_strategy: str,
+        py_version_info: tuple[int, ...] | None = None,
+    ) -> None:
+        super().__init__()
+        assert upgrade_strategy in self._allowed_strategies
+
+        if py_version_info is None:
+            py_version_info = sys.version_info[:3]
+        else:
+            py_version_info = normalize_version_info(py_version_info)
+
+        self._py_version_info = py_version_info
+
+        self.preparer = preparer
+        self.finder = finder
+        self.wheel_cache = wheel_cache
+
+        self.upgrade_strategy = upgrade_strategy
+        self.force_reinstall = force_reinstall
+        self.ignore_dependencies = ignore_dependencies
+        self.ignore_installed = ignore_installed
+        self.ignore_requires_python = ignore_requires_python
+        self.use_user_site = use_user_site
+        self._make_install_req = make_install_req
+
+        self._discovered_dependencies: DiscoveredDependencies = defaultdict(list)
+
+    def resolve(
+        self, root_reqs: list[InstallRequirement], check_supported_wheels: bool
+    ) -> RequirementSet:
+        """Resolve what operations need to be done
+
+        As a side-effect of this method, the packages (and their dependencies)
+        are downloaded, unpacked and prepared for installation. This
+        preparation is done by ``pip.operations.prepare``.
+
+        Once PyPI has static dependency metadata available, it would be
+        possible to move the preparation to become a step separated from
+        dependency resolution.
+        """
+        requirement_set = RequirementSet(check_supported_wheels=check_supported_wheels)
+        for req in root_reqs:
+            if req.constraint:
+                check_invalid_constraint_type(req)
+            self._add_requirement_to_set(requirement_set, req)
+
+        # Actually prepare the files, and collect any exceptions. Most hash
+        # exceptions cannot be checked ahead of time, because
+        # _populate_link() needs to be called before we can make decisions
+        # based on link type.
+        discovered_reqs: list[InstallRequirement] = []
+        hash_errors = HashErrors()
+        for req in chain(requirement_set.all_requirements, discovered_reqs):
+            try:
+                discovered_reqs.extend(self._resolve_one(requirement_set, req))
+            except HashError as exc:
+                exc.req = req
+                hash_errors.append(exc)
+
+        if hash_errors:
+            raise hash_errors
+
+        return requirement_set
+
+    def _add_requirement_to_set(
+        self,
+        requirement_set: RequirementSet,
+        install_req: InstallRequirement,
+        parent_req_name: str | None = None,
+        extras_requested: Iterable[str] | None = None,
+    ) -> tuple[list[InstallRequirement], InstallRequirement | None]:
+        """Add install_req as a requirement to install.
+
+        :param parent_req_name: The name of the requirement that needed this
+            added. The name is used because when multiple unnamed requirements
+            resolve to the same name, we could otherwise end up with dependency
+            links that point outside the Requirements set. parent_req must
+            already be added. Note that None implies that this is a user
+            supplied requirement, vs an inferred one.
+        :param extras_requested: an iterable of extras used to evaluate the
+            environment markers.
+        :return: Additional requirements to scan. That is either [] if
+            the requirement is not applicable, or [install_req] if the
+            requirement is applicable and has just been added.
+        """
+        # If the markers do not match, ignore this requirement.
+        if not install_req.match_markers(extras_requested):
+            logger.info(
+                "Ignoring %s: markers '%s' don't match your environment",
+                install_req.name,
+                install_req.markers,
+            )
+            return [], None
+
+        # If the wheel is not supported, raise an error.
+        # Should check this after filtering out based on environment markers to
+        # allow specifying different wheels based on the environment/OS, in a
+        # single requirements file.
+        if install_req.link and install_req.link.is_wheel:
+            wheel = Wheel(install_req.link.filename)
+            tags = compatibility_tags.get_supported()
+            if requirement_set.check_supported_wheels and not wheel.supported(tags):
+                raise InstallationError(
+                    f"{wheel.filename} is not a supported wheel on this platform."
+                )
+
+        # This next bit is really a sanity check.
+        assert (
+            not install_req.user_supplied or parent_req_name is None
+        ), "a user supplied req shouldn't have a parent"
+
+        # Unnamed requirements are scanned again and the requirement won't be
+        # added as a dependency until after scanning.
+        if not install_req.name:
+            requirement_set.add_unnamed_requirement(install_req)
+            return [install_req], None
+
+        try:
+            existing_req: InstallRequirement | None = requirement_set.get_requirement(
+                install_req.name
+            )
+        except KeyError:
+            existing_req = None
+
+        has_conflicting_requirement = (
+            parent_req_name is None
+            and existing_req
+            and not existing_req.constraint
+            and existing_req.extras == install_req.extras
+            and existing_req.req
+            and install_req.req
+            and existing_req.req.specifier != install_req.req.specifier
+        )
+        if has_conflicting_requirement:
+            raise InstallationError(
+                f"Double requirement given: {install_req} "
+                f"(already in {existing_req}, name={install_req.name!r})"
+            )
+
+        # When no existing requirement exists, add the requirement as a
+        # dependency and it will be scanned again after.
+        if not existing_req:
+            requirement_set.add_named_requirement(install_req)
+            # We'd want to rescan this requirement later
+            return [install_req], install_req
+
+        # Assume there's no need to scan, and that we've already
+        # encountered this for scanning.
+        if install_req.constraint or not existing_req.constraint:
+            return [], existing_req
+
+        does_not_satisfy_constraint = install_req.link and not (
+            existing_req.link and install_req.link.path == existing_req.link.path
+        )
+        if does_not_satisfy_constraint:
+            raise InstallationError(
+                f"Could not satisfy constraints for '{install_req.name}': "
+                "installation from path or url cannot be "
+                "constrained to a version"
+            )
+        # If we're now installing a constraint, mark the existing
+        # object for real installation.
+        existing_req.constraint = False
+        # If we're now installing a user supplied requirement,
+        # mark the existing object as such.
+        if install_req.user_supplied:
+            existing_req.user_supplied = True
+        existing_req.extras = tuple(
+            sorted(set(existing_req.extras) | set(install_req.extras))
+        )
+        logger.debug(
+            "Setting %s extras to: %s",
+            existing_req,
+            existing_req.extras,
+        )
+        # Return the existing requirement for addition to the parent and
+        # scanning again.
+        return [existing_req], existing_req
+
+    def _is_upgrade_allowed(self, req: InstallRequirement) -> bool:
+        if self.upgrade_strategy == "to-satisfy-only":
+            return False
+        elif self.upgrade_strategy == "eager":
+            return True
+        else:
+            assert self.upgrade_strategy == "only-if-needed"
+            return req.user_supplied or req.constraint
+
+    def _set_req_to_reinstall(self, req: InstallRequirement) -> None:
+        """
+        Set a requirement to be installed.
+        """
+        # Don't uninstall the conflict if doing a user install and the
+        # conflict is not a user install.
+        assert req.satisfied_by is not None
+        if not self.use_user_site or req.satisfied_by.in_usersite:
+            req.should_reinstall = True
+        req.satisfied_by = None
+
+    def _check_skip_installed(self, req_to_install: InstallRequirement) -> str | None:
+        """Check if req_to_install should be skipped.
+
+        This will check if the req is installed, and whether we should upgrade
+        or reinstall it, taking into account all the relevant user options.
+
+        After calling this req_to_install will only have satisfied_by set to
+        None if the req_to_install is to be upgraded/reinstalled etc. Any
+        other value will be a dist recording the current thing installed that
+        satisfies the requirement.
+
+        Note that for vcs urls and the like we can't assess skipping in this
+        routine - we simply identify that we need to pull the thing down,
+        then later on it is pulled down and introspected to assess upgrade/
+        reinstalls etc.
+
+        :return: A text reason for why it was skipped, or None.
+        """
+        if self.ignore_installed:
+            return None
+
+        req_to_install.check_if_exists(self.use_user_site)
+        if not req_to_install.satisfied_by:
+            return None
+
+        if self.force_reinstall:
+            self._set_req_to_reinstall(req_to_install)
+            return None
+
+        if not self._is_upgrade_allowed(req_to_install):
+            if self.upgrade_strategy == "only-if-needed":
+                return "already satisfied, skipping upgrade"
+            return "already satisfied"
+
+        # Check for the possibility of an upgrade.  For link-based
+        # requirements we have to pull the tree down and inspect to assess
+        # the version #, so it's handled way down.
+        if not req_to_install.link:
+            try:
+                self.finder.find_requirement(req_to_install, upgrade=True)
+            except BestVersionAlreadyInstalled:
+                # Then the best version is installed.
+                return "already up-to-date"
+            except DistributionNotFound:
+                # No distribution found, so we squash the error.  It will
+                # be raised later when we re-try later to do the install.
+                # Why don't we just raise here?
+                pass
+
+        self._set_req_to_reinstall(req_to_install)
+        return None
+
+    def _find_requirement_link(self, req: InstallRequirement) -> Link | None:
+        upgrade = self._is_upgrade_allowed(req)
+        best_candidate = self.finder.find_requirement(req, upgrade)
+        if not best_candidate:
+            return None
+
+        # Log a warning per PEP 592 if necessary before returning.
+        link = best_candidate.link
+        if link.is_yanked:
+            reason = link.yanked_reason or ""
+            msg = (
+                # Mark this as a unicode string to prevent
+                # "UnicodeEncodeError: 'ascii' codec can't encode character"
+                # in Python 2 when the reason contains non-ascii characters.
+                "The candidate selected for download or install is a "
+                f"yanked version: {best_candidate}\n"
+                f"Reason for being yanked: {reason}"
+            )
+            logger.warning(msg)
+
+        return link
+
+    def _populate_link(self, req: InstallRequirement) -> None:
+        """Ensure that if a link can be found for this, that it is found.
+
+        Note that req.link may still be None - if the requirement is already
+        installed and not needed to be upgraded based on the return value of
+        _is_upgrade_allowed().
+
+        If preparer.require_hashes is True, don't use the wheel cache, because
+        cached wheels, always built locally, have different hashes than the
+        files downloaded from the index server and thus throw false hash
+        mismatches. Furthermore, cached wheels at present have nondeterministic
+        contents due to file modification times.
+        """
+        if req.link is None:
+            req.link = self._find_requirement_link(req)
+
+        if self.wheel_cache is None or self.preparer.require_hashes:
+            return
+
+        assert req.link is not None, "_find_requirement_link unexpectedly returned None"
+        cache_entry = self.wheel_cache.get_cache_entry(
+            link=req.link,
+            package_name=req.name,
+            supported_tags=get_supported(),
+        )
+        if cache_entry is not None:
+            logger.debug("Using cached wheel link: %s", cache_entry.link)
+            if req.link is req.original_link and cache_entry.persistent:
+                req.cached_wheel_source_link = req.link
+            if cache_entry.origin is not None:
+                req.download_info = cache_entry.origin
+            else:
+                # Legacy cache entry that does not have origin.json.
+                # download_info may miss the archive_info.hashes field.
+                req.download_info = direct_url_from_link(
+                    req.link, link_is_in_wheel_cache=cache_entry.persistent
+                )
+            req.link = cache_entry.link
+
+    def _get_dist_for(self, req: InstallRequirement) -> BaseDistribution:
+        """Takes a InstallRequirement and returns a single AbstractDist \
+        representing a prepared variant of the same.
+        """
+        if req.editable:
+            return self.preparer.prepare_editable_requirement(req)
+
+        # satisfied_by is only evaluated by calling _check_skip_installed,
+        # so it must be None here.
+        assert req.satisfied_by is None
+        skip_reason = self._check_skip_installed(req)
+
+        if req.satisfied_by:
+            return self.preparer.prepare_installed_requirement(req, skip_reason)
+
+        # We eagerly populate the link, since that's our "legacy" behavior.
+        self._populate_link(req)
+        dist = self.preparer.prepare_linked_requirement(req)
+
+        # NOTE
+        # The following portion is for determining if a certain package is
+        # going to be re-installed/upgraded or not and reporting to the user.
+        # This should probably get cleaned up in a future refactor.
+
+        # req.req is only avail after unpack for URL
+        # pkgs repeat check_if_exists to uninstall-on-upgrade
+        # (#14)
+        if not self.ignore_installed:
+            req.check_if_exists(self.use_user_site)
+
+        if req.satisfied_by:
+            should_modify = (
+                self.upgrade_strategy != "to-satisfy-only"
+                or self.force_reinstall
+                or self.ignore_installed
+                or req.link.scheme == "file"
+            )
+            if should_modify:
+                self._set_req_to_reinstall(req)
+            else:
+                logger.info(
+                    "Requirement already satisfied (use --upgrade to upgrade): %s",
+                    req,
+                )
+        return dist
+
+    def _resolve_one(
+        self,
+        requirement_set: RequirementSet,
+        req_to_install: InstallRequirement,
+    ) -> list[InstallRequirement]:
+        """Prepare a single requirements file.
+
+        :return: A list of additional InstallRequirements to also install.
+        """
+        # Tell user what we are doing for this requirement:
+        # obtain (editable), skipping, processing (local url), collecting
+        # (remote url or package name)
+        if req_to_install.constraint or req_to_install.prepared:
+            return []
+
+        req_to_install.prepared = True
+
+        # Parse and return dependencies
+        dist = self._get_dist_for(req_to_install)
+        # This will raise UnsupportedPythonVersion if the given Python
+        # version isn't compatible with the distribution's Requires-Python.
+        _check_dist_requires_python(
+            dist,
+            version_info=self._py_version_info,
+            ignore_requires_python=self.ignore_requires_python,
+        )
+
+        more_reqs: list[InstallRequirement] = []
+
+        def add_req(subreq: Requirement, extras_requested: Iterable[str]) -> None:
+            # This idiosyncratically converts the Requirement to str and let
+            # make_install_req then parse it again into Requirement. But this is
+            # the legacy resolver so I'm just not going to bother refactoring.
+            sub_install_req = self._make_install_req(str(subreq), req_to_install)
+            parent_req_name = req_to_install.name
+            to_scan_again, add_to_parent = self._add_requirement_to_set(
+                requirement_set,
+                sub_install_req,
+                parent_req_name=parent_req_name,
+                extras_requested=extras_requested,
+            )
+            if parent_req_name and add_to_parent:
+                self._discovered_dependencies[parent_req_name].append(add_to_parent)
+            more_reqs.extend(to_scan_again)
+
+        with indent_log():
+            # We add req_to_install before its dependencies, so that we
+            # can refer to it when adding dependencies.
+            assert req_to_install.name is not None
+            if not requirement_set.has_requirement(req_to_install.name):
+                # 'unnamed' requirements will get added here
+                # 'unnamed' requirements can only come from being directly
+                # provided by the user.
+                assert req_to_install.user_supplied
+                self._add_requirement_to_set(
+                    requirement_set, req_to_install, parent_req_name=None
+                )
+
+            if not self.ignore_dependencies:
+                if req_to_install.extras:
+                    logger.debug(
+                        "Installing extra requirements: %r",
+                        ",".join(req_to_install.extras),
+                    )
+                missing_requested = sorted(
+                    set(req_to_install.extras) - set(dist.iter_provided_extras())
+                )
+                for missing in missing_requested:
+                    logger.warning(
+                        "%s %s does not provide the extra '%s'",
+                        dist.raw_name,
+                        dist.version,
+                        missing,
+                    )
+
+                available_requested = sorted(
+                    set(dist.iter_provided_extras()) & set(req_to_install.extras)
+                )
+                for subreq in dist.iter_dependencies(available_requested):
+                    add_req(subreq, extras_requested=available_requested)
+
+        return more_reqs
+
+    def get_installation_order(
+        self, req_set: RequirementSet
+    ) -> list[InstallRequirement]:
+        """Create the installation order.
+
+        The installation order is topological - requirements are installed
+        before the requiring thing. We break cycles at an arbitrary point,
+        and make no other guarantees.
+        """
+        # The current implementation, which we may change at any point
+        # installs the user specified things in the order given, except when
+        # dependencies must come earlier to achieve topological order.
+        order = []
+        ordered_reqs: set[InstallRequirement] = set()
+
+        def schedule(req: InstallRequirement) -> None:
+            if req.satisfied_by or req in ordered_reqs:
+                return
+            if req.constraint:
+                return
+            ordered_reqs.add(req)
+            for dep in self._discovered_dependencies[req.name]:
+                schedule(dep)
+            order.append(req)
+
+        for install_req in req_set.requirements.values():
+            schedule(install_req)
+        return order
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..e1dd43b
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-312.pyc
new file mode 100644
index 0000000..021ef3d
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-312.pyc
new file mode 100644
index 0000000..6e0b7f3
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-312.pyc
new file mode 100644
index 0000000..65489e0
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-312.pyc
new file mode 100644
index 0000000..82065d3
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-312.pyc
new file mode 100644
index 0000000..4969f64
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-312.pyc
new file mode 100644
index 0000000..2adabd6
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-312.pyc
new file mode 100644
index 0000000..c0e6d2b
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-312.pyc
new file mode 100644
index 0000000..1aeabab
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/base.py b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/base.py
new file mode 100644
index 0000000..9d75183
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/base.py
@@ -0,0 +1,164 @@
+from __future__ import annotations
+
+from collections.abc import Iterable
+from dataclasses import dataclass
+from typing import Optional
+
+from pip._vendor.packaging.specifiers import SpecifierSet
+from pip._vendor.packaging.utils import NormalizedName
+from pip._vendor.packaging.version import Version
+
+from pip._internal.models.link import Link, links_equivalent
+from pip._internal.req.req_install import InstallRequirement
+from pip._internal.utils.hashes import Hashes
+
+CandidateLookup = tuple[Optional["Candidate"], InstallRequirement | None]
+
+
+def format_name(project: NormalizedName, extras: frozenset[NormalizedName]) -> str:
+    if not extras:
+        return project
+    extras_expr = ",".join(sorted(extras))
+    return f"{project}[{extras_expr}]"
+
+
+@dataclass(frozen=True)
+class Constraint:
+    specifier: SpecifierSet
+    hashes: Hashes
+    hash_options: dict[str, list[str]]
+    links: frozenset[Link]
+
+    @classmethod
+    def empty(cls) -> Constraint:
+        return Constraint(SpecifierSet(), Hashes(), {}, frozenset())
+
+    @classmethod
+    def from_ireq(cls, ireq: InstallRequirement) -> Constraint:
+        links = frozenset([ireq.link]) if ireq.link else frozenset()
+        hash_options = {alg: list(v) for alg, v in ireq.hash_options.items()}
+        return Constraint(
+            ireq.specifier,
+            ireq.hashes(trust_internet=False),
+            hash_options,
+            links,
+        )
+
+    def __bool__(self) -> bool:
+        return bool(self.specifier) or bool(self.hashes) or bool(self.links)
+
+    def __and__(self, other: InstallRequirement) -> Constraint:
+        if not isinstance(other, InstallRequirement):
+            return NotImplemented
+        specifier = self.specifier & other.specifier
+        hashes = self.hashes & other.hashes(trust_internet=False)
+        if not self.hash_options:
+            hash_options = {alg: list(v) for alg, v in other.hash_options.items()}
+        elif not other.hash_options:
+            hash_options = {alg: list(v) for alg, v in self.hash_options.items()}
+        else:
+            hash_options = {
+                alg: [v for v in other.hash_options[alg] if v in self.hash_options[alg]]
+                for alg in self.hash_options.keys() & other.hash_options.keys()
+            }
+        links = self.links
+        if other.link:
+            links = links.union([other.link])
+        return Constraint(specifier, hashes, hash_options, links)
+
+    def is_satisfied_by(self, candidate: Candidate) -> bool:
+        # Reject if there are any mismatched URL constraints on this package.
+        if self.links and not all(_match_link(link, candidate) for link in self.links):
+            return False
+        # We can safely always allow prereleases here since PackageFinder
+        # already implements the prerelease logic, and would have filtered out
+        # prerelease candidates if the user does not expect them.
+        return self.specifier.contains(candidate.version, prereleases=True)
+
+    def format_for_error(self) -> str:
+        s = str(self.specifier)
+        if self.links:
+            s += f" (from {', '.join(str(link) for link in self.links)})"
+        return s
+
+
+class Requirement:
+    @property
+    def project_name(self) -> NormalizedName:
+        """The "project name" of a requirement.
+
+        This is different from ``name`` if this requirement contains extras,
+        in which case ``name`` would contain the ``[...]`` part, while this
+        refers to the name of the project.
+        """
+        raise NotImplementedError("Subclass should override")
+
+    @property
+    def name(self) -> str:
+        """The name identifying this requirement in the resolver.
+
+        This is different from ``project_name`` if this requirement contains
+        extras, where ``project_name`` would not contain the ``[...]`` part.
+        """
+        raise NotImplementedError("Subclass should override")
+
+    def is_satisfied_by(self, candidate: Candidate) -> bool:
+        return False
+
+    def get_candidate_lookup(self) -> CandidateLookup:
+        raise NotImplementedError("Subclass should override")
+
+    def format_for_error(self) -> str:
+        raise NotImplementedError("Subclass should override")
+
+
+def _match_link(link: Link, candidate: Candidate) -> bool:
+    if candidate.source_link:
+        return links_equivalent(link, candidate.source_link)
+    return False
+
+
+class Candidate:
+    @property
+    def project_name(self) -> NormalizedName:
+        """The "project name" of the candidate.
+
+        This is different from ``name`` if this candidate contains extras,
+        in which case ``name`` would contain the ``[...]`` part, while this
+        refers to the name of the project.
+        """
+        raise NotImplementedError("Override in subclass")
+
+    @property
+    def name(self) -> str:
+        """The name identifying this candidate in the resolver.
+
+        This is different from ``project_name`` if this candidate contains
+        extras, where ``project_name`` would not contain the ``[...]`` part.
+        """
+        raise NotImplementedError("Override in subclass")
+
+    @property
+    def version(self) -> Version:
+        raise NotImplementedError("Override in subclass")
+
+    @property
+    def is_installed(self) -> bool:
+        raise NotImplementedError("Override in subclass")
+
+    @property
+    def is_editable(self) -> bool:
+        raise NotImplementedError("Override in subclass")
+
+    @property
+    def source_link(self) -> Link | None:
+        raise NotImplementedError("Override in subclass")
+
+    def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]:
+        raise NotImplementedError("Override in subclass")
+
+    def get_install_requirement(self) -> InstallRequirement | None:
+        raise NotImplementedError("Override in subclass")
+
+    def format_for_error(self) -> str:
+        raise NotImplementedError("Subclass should override")
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py
new file mode 100644
index 0000000..b8b853c
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py
@@ -0,0 +1,598 @@
+from __future__ import annotations
+
+import logging
+import sys
+from collections.abc import Iterable
+from typing import TYPE_CHECKING, Any, Union, cast
+
+from pip._vendor.packaging.requirements import InvalidRequirement
+from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
+from pip._vendor.packaging.version import Version
+
+from pip._internal.exceptions import (
+    FailedToPrepareCandidate,
+    HashError,
+    InstallationSubprocessError,
+    InvalidInstalledPackage,
+    MetadataInconsistent,
+    MetadataInvalid,
+)
+from pip._internal.metadata import BaseDistribution
+from pip._internal.models.link import Link, links_equivalent
+from pip._internal.models.wheel import Wheel
+from pip._internal.req.constructors import (
+    install_req_from_editable,
+    install_req_from_line,
+)
+from pip._internal.req.req_install import InstallRequirement
+from pip._internal.utils.direct_url_helpers import direct_url_from_link
+from pip._internal.utils.misc import normalize_version_info
+
+from .base import Candidate, Requirement, format_name
+
+if TYPE_CHECKING:
+    from .factory import Factory
+
+logger = logging.getLogger(__name__)
+
+BaseCandidate = Union[
+    "AlreadyInstalledCandidate",
+    "EditableCandidate",
+    "LinkCandidate",
+]
+
+# Avoid conflicting with the PyPI package "Python".
+REQUIRES_PYTHON_IDENTIFIER = cast(NormalizedName, "")
+
+
+def as_base_candidate(candidate: Candidate) -> BaseCandidate | None:
+    """The runtime version of BaseCandidate."""
+    base_candidate_classes = (
+        AlreadyInstalledCandidate,
+        EditableCandidate,
+        LinkCandidate,
+    )
+    if isinstance(candidate, base_candidate_classes):
+        return candidate
+    return None
+
+
+def make_install_req_from_link(
+    link: Link,
+    template: InstallRequirement,
+    version: Version | None = None,
+) -> InstallRequirement:
+    assert not template.editable, "template is editable"
+    if version is not None and template.req and template.hash_options:
+        # When hashes are provided via constraints for an unpinned requirement,
+        # the resulting install requirement must appear pinned so that the
+        # hash-checking logic does not reject it as HashUnpinned.
+        line = f"{template.req.name}=={version}"
+    elif template.req:
+        line = str(template.req)
+    else:
+        line = link.url
+    ireq = install_req_from_line(
+        line,
+        user_supplied=template.user_supplied,
+        comes_from=template.comes_from,
+        isolated=template.isolated,
+        constraint=template.constraint,
+        hash_options=template.hash_options,
+        config_settings=template.config_settings,
+    )
+    ireq.original_link = template.original_link
+    ireq.link = link
+    ireq.extras = template.extras
+    return ireq
+
+
+def make_install_req_from_editable(
+    link: Link, template: InstallRequirement
+) -> InstallRequirement:
+    assert template.editable, "template not editable"
+    if template.name:
+        req_string = f"{template.name} @ {link.url}"
+    else:
+        req_string = link.url
+    ireq = install_req_from_editable(
+        req_string,
+        user_supplied=template.user_supplied,
+        comes_from=template.comes_from,
+        isolated=template.isolated,
+        constraint=template.constraint,
+        hash_options=template.hash_options,
+        config_settings=template.config_settings,
+    )
+    ireq.extras = template.extras
+    return ireq
+
+
+def _make_install_req_from_dist(
+    dist: BaseDistribution, template: InstallRequirement
+) -> InstallRequirement:
+    if template.req:
+        line = str(template.req)
+    elif template.link:
+        line = f"{dist.canonical_name} @ {template.link.url}"
+    else:
+        line = f"{dist.canonical_name}=={dist.version}"
+    ireq = install_req_from_line(
+        line,
+        user_supplied=template.user_supplied,
+        comes_from=template.comes_from,
+        isolated=template.isolated,
+        constraint=template.constraint,
+        hash_options=template.hash_options,
+        config_settings=template.config_settings,
+    )
+    ireq.satisfied_by = dist
+    return ireq
+
+
+class _InstallRequirementBackedCandidate(Candidate):
+    """A candidate backed by an ``InstallRequirement``.
+
+    This represents a package request with the target not being already
+    in the environment, and needs to be fetched and installed. The backing
+    ``InstallRequirement`` is responsible for most of the leg work; this
+    class exposes appropriate information to the resolver.
+
+    :param link: The link passed to the ``InstallRequirement``. The backing
+        ``InstallRequirement`` will use this link to fetch the distribution.
+    :param source_link: The link this candidate "originates" from. This is
+        different from ``link`` when the link is found in the wheel cache.
+        ``link`` would point to the wheel cache, while this points to the
+        found remote link (e.g. from pypi.org).
+    """
+
+    dist: BaseDistribution
+    is_installed = False
+
+    def __init__(
+        self,
+        link: Link,
+        source_link: Link,
+        ireq: InstallRequirement,
+        factory: Factory,
+        name: NormalizedName | None = None,
+        version: Version | None = None,
+    ) -> None:
+        self._link = link
+        self._source_link = source_link
+        self._factory = factory
+        self._ireq = ireq
+        self._name = name
+        self._version = version
+        self.dist = self._prepare()
+        self._hash: int | None = None
+
+    def __str__(self) -> str:
+        return f"{self.name} {self.version}"
+
+    def __repr__(self) -> str:
+        return f"{self.__class__.__name__}({str(self._link)!r})"
+
+    def __hash__(self) -> int:
+        if self._hash is not None:
+            return self._hash
+
+        self._hash = hash((self.__class__, self._link))
+        return self._hash
+
+    def __eq__(self, other: Any) -> bool:
+        if isinstance(other, self.__class__):
+            return links_equivalent(self._link, other._link)
+        return False
+
+    @property
+    def source_link(self) -> Link | None:
+        return self._source_link
+
+    @property
+    def project_name(self) -> NormalizedName:
+        """The normalised name of the project the candidate refers to"""
+        if self._name is None:
+            self._name = self.dist.canonical_name
+        return self._name
+
+    @property
+    def name(self) -> str:
+        return self.project_name
+
+    @property
+    def version(self) -> Version:
+        if self._version is None:
+            self._version = self.dist.version
+        return self._version
+
+    def format_for_error(self) -> str:
+        return (
+            f"{self.name} {self.version} "
+            f"(from {'editable ' if self.is_editable else ''}"
+            f"{self._link.file_path if self._link.is_file else self._link})"
+        )
+
+    def _prepare_distribution(self) -> BaseDistribution:
+        raise NotImplementedError("Override in subclass")
+
+    def _check_metadata_consistency(self, dist: BaseDistribution) -> None:
+        """Check for consistency of project name and version of dist."""
+        if self._name is not None and self._name != dist.canonical_name:
+            raise MetadataInconsistent(
+                self._ireq,
+                "name",
+                self._name,
+                dist.canonical_name,
+            )
+        if self._version is not None and self._version != dist.version:
+            raise MetadataInconsistent(
+                self._ireq,
+                "version",
+                str(self._version),
+                str(dist.version),
+            )
+        # check dependencies are valid
+        # TODO performance: this means we iterate the dependencies at least twice,
+        # we may want to cache parsed Requires-Dist
+        try:
+            list(dist.iter_dependencies(list(dist.iter_provided_extras())))
+        except InvalidRequirement as e:
+            raise MetadataInvalid(self._ireq, str(e))
+
+    def _prepare(self) -> BaseDistribution:
+        try:
+            dist = self._prepare_distribution()
+        except HashError as e:
+            # Provide HashError the underlying ireq that caused it. This
+            # provides context for the resulting error message to show the
+            # offending line to the user.
+            e.req = self._ireq
+            raise
+        except InstallationSubprocessError as exc:
+            if isinstance(self._ireq.comes_from, InstallRequirement):
+                request_chain = self._ireq.comes_from.from_path()
+            else:
+                request_chain = self._ireq.comes_from
+
+            if request_chain is None:
+                request_chain = "directly requested"
+
+            raise FailedToPrepareCandidate(
+                package_name=self._ireq.name or str(self._link),
+                requirement_chain=request_chain,
+                failed_step=exc.command_description,
+            )
+
+        self._check_metadata_consistency(dist)
+        return dist
+
+    def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]:
+        # Emit the Requires-Python requirement first to fail fast on
+        # unsupported candidates and avoid pointless downloads/preparation.
+        yield self._factory.make_requires_python_requirement(self.dist.requires_python)
+        requires = self.dist.iter_dependencies() if with_requires else ()
+        for r in requires:
+            yield from self._factory.make_requirements_from_spec(str(r), self._ireq)
+
+    def get_install_requirement(self) -> InstallRequirement | None:
+        return self._ireq
+
+
+class LinkCandidate(_InstallRequirementBackedCandidate):
+    is_editable = False
+
+    def __init__(
+        self,
+        link: Link,
+        template: InstallRequirement,
+        factory: Factory,
+        name: NormalizedName | None = None,
+        version: Version | None = None,
+    ) -> None:
+        source_link = link
+        cache_entry = factory.get_wheel_cache_entry(source_link, name)
+        if cache_entry is not None:
+            logger.debug("Using cached wheel link: %s", cache_entry.link)
+            link = cache_entry.link
+        ireq = make_install_req_from_link(link, template, version=version)
+        assert ireq.link == link
+        if ireq.link.is_wheel and not ireq.link.is_file:
+            wheel = Wheel(ireq.link.filename)
+            wheel_name = wheel.name
+            assert name == wheel_name, f"{name!r} != {wheel_name!r} for wheel"
+            # Version may not be present for PEP 508 direct URLs
+            if version is not None:
+                wheel_version = Version(wheel.version)
+                assert (
+                    version == wheel_version
+                ), f"{version!r} != {wheel_version!r} for wheel {name}"
+
+        if cache_entry is not None:
+            assert ireq.link.is_wheel
+            assert ireq.link.is_file
+            if cache_entry.persistent and template.link is template.original_link:
+                ireq.cached_wheel_source_link = source_link
+            if cache_entry.origin is not None:
+                ireq.download_info = cache_entry.origin
+            else:
+                # Legacy cache entry that does not have origin.json.
+                # download_info may miss the archive_info.hashes field.
+                ireq.download_info = direct_url_from_link(
+                    source_link, link_is_in_wheel_cache=cache_entry.persistent
+                )
+
+        super().__init__(
+            link=link,
+            source_link=source_link,
+            ireq=ireq,
+            factory=factory,
+            name=name,
+            version=version,
+        )
+
+    def _prepare_distribution(self) -> BaseDistribution:
+        preparer = self._factory.preparer
+        return preparer.prepare_linked_requirement(self._ireq, parallel_builds=True)
+
+
+class EditableCandidate(_InstallRequirementBackedCandidate):
+    is_editable = True
+
+    def __init__(
+        self,
+        link: Link,
+        template: InstallRequirement,
+        factory: Factory,
+        name: NormalizedName | None = None,
+        version: Version | None = None,
+    ) -> None:
+        super().__init__(
+            link=link,
+            source_link=link,
+            ireq=make_install_req_from_editable(link, template),
+            factory=factory,
+            name=name,
+            version=version,
+        )
+
+    def _prepare_distribution(self) -> BaseDistribution:
+        return self._factory.preparer.prepare_editable_requirement(self._ireq)
+
+
+class AlreadyInstalledCandidate(Candidate):
+    is_installed = True
+    source_link = None
+
+    def __init__(
+        self,
+        dist: BaseDistribution,
+        template: InstallRequirement,
+        factory: Factory,
+    ) -> None:
+        self.dist = dist
+        self._ireq = _make_install_req_from_dist(dist, template)
+        self._factory = factory
+        self._version = None
+
+        # This is just logging some messages, so we can do it eagerly.
+        # The returned dist would be exactly the same as self.dist because we
+        # set satisfied_by in _make_install_req_from_dist.
+        # TODO: Supply reason based on force_reinstall and upgrade_strategy.
+        skip_reason = "already satisfied"
+        factory.preparer.prepare_installed_requirement(self._ireq, skip_reason)
+
+    def __str__(self) -> str:
+        return str(self.dist)
+
+    def __repr__(self) -> str:
+        return f"{self.__class__.__name__}({self.dist!r})"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, AlreadyInstalledCandidate):
+            return NotImplemented
+        return self.name == other.name and self.version == other.version
+
+    def __hash__(self) -> int:
+        return hash((self.name, self.version))
+
+    @property
+    def project_name(self) -> NormalizedName:
+        return self.dist.canonical_name
+
+    @property
+    def name(self) -> str:
+        return self.project_name
+
+    @property
+    def version(self) -> Version:
+        if self._version is None:
+            self._version = self.dist.version
+        return self._version
+
+    @property
+    def is_editable(self) -> bool:
+        return self.dist.editable
+
+    def format_for_error(self) -> str:
+        return f"{self.name} {self.version} (Installed)"
+
+    def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]:
+        if not with_requires:
+            return
+
+        try:
+            for r in self.dist.iter_dependencies():
+                yield from self._factory.make_requirements_from_spec(str(r), self._ireq)
+        except InvalidRequirement as exc:
+            raise InvalidInstalledPackage(dist=self.dist, invalid_exc=exc) from None
+
+    def get_install_requirement(self) -> InstallRequirement | None:
+        return None
+
+
+class ExtrasCandidate(Candidate):
+    """A candidate that has 'extras', indicating additional dependencies.
+
+    Requirements can be for a project with dependencies, something like
+    foo[extra].  The extras don't affect the project/version being installed
+    directly, but indicate that we need additional dependencies. We model that
+    by having an artificial ExtrasCandidate that wraps the "base" candidate.
+
+    The ExtrasCandidate differs from the base in the following ways:
+
+    1. It has a unique name, of the form foo[extra]. This causes the resolver
+       to treat it as a separate node in the dependency graph.
+    2. When we're getting the candidate's dependencies,
+       a) We specify that we want the extra dependencies as well.
+       b) We add a dependency on the base candidate.
+          See below for why this is needed.
+    3. We return None for the underlying InstallRequirement, as the base
+       candidate will provide it, and we don't want to end up with duplicates.
+
+    The dependency on the base candidate is needed so that the resolver can't
+    decide that it should recommend foo[extra1] version 1.0 and foo[extra2]
+    version 2.0. Having those candidates depend on foo=1.0 and foo=2.0
+    respectively forces the resolver to recognise that this is a conflict.
+    """
+
+    def __init__(
+        self,
+        base: BaseCandidate,
+        extras: frozenset[str],
+        *,
+        comes_from: InstallRequirement | None = None,
+    ) -> None:
+        """
+        :param comes_from: the InstallRequirement that led to this candidate if it
+            differs from the base's InstallRequirement. This will often be the
+            case in the sense that this candidate's requirement has the extras
+            while the base's does not. Unlike the InstallRequirement backed
+            candidates, this requirement is used solely for reporting purposes,
+            it does not do any leg work.
+        """
+        self.base = base
+        self.extras = frozenset(canonicalize_name(e) for e in extras)
+        self._comes_from = comes_from if comes_from is not None else self.base._ireq
+
+    def __str__(self) -> str:
+        name, rest = str(self.base).split(" ", 1)
+        return "{}[{}] {}".format(name, ",".join(self.extras), rest)
+
+    def __repr__(self) -> str:
+        return f"{self.__class__.__name__}(base={self.base!r}, extras={self.extras!r})"
+
+    def __hash__(self) -> int:
+        return hash((self.base, self.extras))
+
+    def __eq__(self, other: Any) -> bool:
+        if isinstance(other, self.__class__):
+            return self.base == other.base and self.extras == other.extras
+        return False
+
+    @property
+    def project_name(self) -> NormalizedName:
+        return self.base.project_name
+
+    @property
+    def name(self) -> str:
+        """The normalised name of the project the candidate refers to"""
+        return format_name(self.base.project_name, self.extras)
+
+    @property
+    def version(self) -> Version:
+        return self.base.version
+
+    def format_for_error(self) -> str:
+        return "{} [{}]".format(
+            self.base.format_for_error(), ", ".join(sorted(self.extras))
+        )
+
+    @property
+    def is_installed(self) -> bool:
+        return self.base.is_installed
+
+    @property
+    def is_editable(self) -> bool:
+        return self.base.is_editable
+
+    @property
+    def source_link(self) -> Link | None:
+        return self.base.source_link
+
+    def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]:
+        factory = self.base._factory
+
+        # Add a dependency on the exact base
+        # (See note 2b in the class docstring)
+        yield factory.make_requirement_from_candidate(self.base)
+        if not with_requires:
+            return
+
+        # The user may have specified extras that the candidate doesn't
+        # support. We ignore any unsupported extras here.
+        valid_extras = self.extras.intersection(self.base.dist.iter_provided_extras())
+        invalid_extras = self.extras.difference(self.base.dist.iter_provided_extras())
+        for extra in sorted(invalid_extras):
+            logger.warning(
+                "%s %s does not provide the extra '%s'",
+                self.base.name,
+                self.version,
+                extra,
+            )
+
+        for r in self.base.dist.iter_dependencies(valid_extras):
+            yield from factory.make_requirements_from_spec(
+                str(r),
+                self._comes_from,
+                valid_extras,
+            )
+
+    def get_install_requirement(self) -> InstallRequirement | None:
+        # We don't return anything here, because we always
+        # depend on the base candidate, and we'll get the
+        # install requirement from that.
+        return None
+
+
+class RequiresPythonCandidate(Candidate):
+    is_installed = False
+    source_link = None
+
+    def __init__(self, py_version_info: tuple[int, ...] | None) -> None:
+        if py_version_info is not None:
+            version_info = normalize_version_info(py_version_info)
+        else:
+            version_info = sys.version_info[:3]
+        self._version = Version(".".join(str(c) for c in version_info))
+
+    # We don't need to implement __eq__() and __ne__() since there is always
+    # only one RequiresPythonCandidate in a resolution, i.e. the host Python.
+    # The built-in object.__eq__() and object.__ne__() do exactly what we want.
+
+    def __str__(self) -> str:
+        return f"Python {self._version}"
+
+    def __repr__(self) -> str:
+        return f"{self.__class__.__name__}({self._version!r})"
+
+    @property
+    def project_name(self) -> NormalizedName:
+        return REQUIRES_PYTHON_IDENTIFIER
+
+    @property
+    def name(self) -> str:
+        return REQUIRES_PYTHON_IDENTIFIER
+
+    @property
+    def version(self) -> Version:
+        return self._version
+
+    def format_for_error(self) -> str:
+        return f"Python {self.version}"
+
+    def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]:
+        return ()
+
+    def get_install_requirement(self) -> InstallRequirement | None:
+        return None
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/factory.py b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/factory.py
new file mode 100644
index 0000000..c9fb839
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/factory.py
@@ -0,0 +1,901 @@
+from __future__ import annotations
+
+import contextlib
+import copy
+import functools
+import logging
+from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
+from typing import (
+    TYPE_CHECKING,
+    NamedTuple,
+    Protocol,
+    TypeVar,
+    cast,
+)
+
+from pip._vendor.packaging.requirements import InvalidRequirement
+from pip._vendor.packaging.specifiers import SpecifierSet
+from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
+from pip._vendor.packaging.version import InvalidVersion, Version
+from pip._vendor.resolvelib import ResolutionImpossible
+from pip._vendor.rich.markup import escape
+
+from pip._internal.cache import CacheEntry, WheelCache
+from pip._internal.exceptions import (
+    DistributionNotFound,
+    InstallationError,
+    InvalidInstalledPackage,
+    MetadataInconsistent,
+    MetadataInvalid,
+    UnsupportedPythonVersion,
+    UnsupportedWheel,
+)
+from pip._internal.index.package_finder import PackageFinder
+from pip._internal.metadata import BaseDistribution, get_default_environment
+from pip._internal.models.link import Link
+from pip._internal.models.wheel import Wheel
+from pip._internal.operations.prepare import RequirementPreparer
+from pip._internal.req.constructors import (
+    install_req_drop_extras,
+    install_req_from_link_and_ireq,
+)
+from pip._internal.req.req_install import (
+    InstallRequirement,
+    check_invalid_constraint_type,
+)
+from pip._internal.resolution.base import InstallRequirementProvider
+from pip._internal.utils.compatibility_tags import get_supported
+from pip._internal.utils.hashes import Hashes
+from pip._internal.utils.packaging import get_requirement
+from pip._internal.utils.virtualenv import running_under_virtualenv
+
+from .base import Candidate, Constraint, Requirement
+from .candidates import (
+    AlreadyInstalledCandidate,
+    BaseCandidate,
+    EditableCandidate,
+    ExtrasCandidate,
+    LinkCandidate,
+    RequiresPythonCandidate,
+    as_base_candidate,
+)
+from .found_candidates import FoundCandidates, IndexCandidateInfo
+from .requirements import (
+    ExplicitRequirement,
+    RequiresPythonRequirement,
+    SpecifierRequirement,
+    SpecifierWithoutExtrasRequirement,
+    UnsatisfiableRequirement,
+)
+
+if TYPE_CHECKING:
+
+    class ConflictCause(Protocol):
+        requirement: RequiresPythonRequirement
+        parent: Candidate
+
+
+logger = logging.getLogger(__name__)
+
+C = TypeVar("C")
+Cache = dict[Link, C]
+
+
+class CollectedRootRequirements(NamedTuple):
+    requirements: list[Requirement]
+    constraints: dict[str, Constraint]
+    user_requested: dict[str, int]
+
+
+class Factory:
+    def __init__(
+        self,
+        finder: PackageFinder,
+        preparer: RequirementPreparer,
+        make_install_req: InstallRequirementProvider,
+        wheel_cache: WheelCache | None,
+        use_user_site: bool,
+        force_reinstall: bool,
+        ignore_installed: bool,
+        ignore_requires_python: bool,
+        py_version_info: tuple[int, ...] | None = None,
+    ) -> None:
+        self._finder = finder
+        self.preparer = preparer
+        self._wheel_cache = wheel_cache
+        self._python_candidate = RequiresPythonCandidate(py_version_info)
+        self._make_install_req_from_spec = make_install_req
+        self._use_user_site = use_user_site
+        self._force_reinstall = force_reinstall
+        self._ignore_requires_python = ignore_requires_python
+
+        self._build_failures: Cache[InstallationError] = {}
+        self._link_candidate_cache: Cache[LinkCandidate] = {}
+        self._editable_candidate_cache: Cache[EditableCandidate] = {}
+        self._installed_candidate_cache: dict[str, AlreadyInstalledCandidate] = {}
+        self._extras_candidate_cache: dict[
+            tuple[int, frozenset[NormalizedName]], ExtrasCandidate
+        ] = {}
+        self._supported_tags_cache = get_supported()
+
+        if not ignore_installed:
+            env = get_default_environment()
+            self._installed_dists = {
+                dist.canonical_name: dist
+                for dist in env.iter_installed_distributions(local_only=False)
+            }
+        else:
+            self._installed_dists = {}
+
+    @property
+    def force_reinstall(self) -> bool:
+        return self._force_reinstall
+
+    def _fail_if_link_is_unsupported_wheel(self, link: Link) -> None:
+        if not link.is_wheel:
+            return
+        wheel = Wheel(link.filename)
+        if wheel.supported(self._finder.target_python.get_unsorted_tags()):
+            return
+        msg = f"{link.filename} is not a supported wheel on this platform."
+        raise UnsupportedWheel(msg)
+
+    def _make_extras_candidate(
+        self,
+        base: BaseCandidate,
+        extras: frozenset[str],
+        *,
+        comes_from: InstallRequirement | None = None,
+    ) -> ExtrasCandidate:
+        cache_key = (id(base), frozenset(canonicalize_name(e) for e in extras))
+        try:
+            candidate = self._extras_candidate_cache[cache_key]
+        except KeyError:
+            candidate = ExtrasCandidate(base, extras, comes_from=comes_from)
+            self._extras_candidate_cache[cache_key] = candidate
+        return candidate
+
+    def _make_candidate_from_dist(
+        self,
+        dist: BaseDistribution,
+        extras: frozenset[str],
+        template: InstallRequirement,
+    ) -> Candidate:
+        try:
+            base = self._installed_candidate_cache[dist.canonical_name]
+        except KeyError:
+            base = AlreadyInstalledCandidate(dist, template, factory=self)
+            self._installed_candidate_cache[dist.canonical_name] = base
+        if not extras:
+            return base
+        return self._make_extras_candidate(base, extras, comes_from=template)
+
+    def _make_candidate_from_link(
+        self,
+        link: Link,
+        extras: frozenset[str],
+        template: InstallRequirement,
+        name: NormalizedName | None,
+        version: Version | None,
+    ) -> Candidate | None:
+        base: BaseCandidate | None = self._make_base_candidate_from_link(
+            link, template, name, version
+        )
+        if not extras or base is None:
+            return base
+        return self._make_extras_candidate(base, extras, comes_from=template)
+
+    def _make_base_candidate_from_link(
+        self,
+        link: Link,
+        template: InstallRequirement,
+        name: NormalizedName | None,
+        version: Version | None,
+    ) -> BaseCandidate | None:
+        # TODO: Check already installed candidate, and use it if the link and
+        # editable flag match.
+
+        if link in self._build_failures:
+            # We already tried this candidate before, and it does not build.
+            # Don't bother trying again.
+            return None
+
+        if template.editable:
+            if link not in self._editable_candidate_cache:
+                try:
+                    self._editable_candidate_cache[link] = EditableCandidate(
+                        link,
+                        template,
+                        factory=self,
+                        name=name,
+                        version=version,
+                    )
+                except (MetadataInconsistent, MetadataInvalid) as e:
+                    logger.info(
+                        "Discarding [blue underline]%s[/]: [yellow]%s[reset]",
+                        escape(str(link)),
+                        escape(str(e)),
+                        extra={"markup": True},
+                    )
+                    self._build_failures[link] = e
+                    return None
+
+            return self._editable_candidate_cache[link]
+        else:
+            if link not in self._link_candidate_cache:
+                try:
+                    self._link_candidate_cache[link] = LinkCandidate(
+                        link,
+                        template,
+                        factory=self,
+                        name=name,
+                        version=version,
+                    )
+                except MetadataInconsistent as e:
+                    logger.info(
+                        "Discarding [blue underline]%s[/]: [yellow]%s[reset]",
+                        escape(str(link)),
+                        escape(str(e)),
+                        extra={"markup": True},
+                    )
+                    self._build_failures[link] = e
+                    return None
+            return self._link_candidate_cache[link]
+
+    def _iter_found_candidates(
+        self,
+        ireqs: Sequence[InstallRequirement],
+        specifier: SpecifierSet,
+        hashes: Hashes,
+        prefers_installed: bool,
+        incompatible_ids: set[int],
+        constraint_hash_options: dict[str, list[str]] | None = None,
+    ) -> Iterable[Candidate]:
+        if not ireqs:
+            return ()
+
+        # The InstallRequirement implementation requires us to give it a
+        # "template". Here we just choose the first requirement to represent
+        # all of them.
+        # Hopefully the Project model can correct this mismatch in the future.
+        template = ireqs[0]
+        assert template.req, "Candidates found on index must be PEP 508"
+        if (
+            constraint_hash_options
+            and not template.hash_options
+            and any(constraint_hash_options.values())
+        ):
+            template = copy.copy(template)
+            template.hash_options = {
+                k: list(v) for k, v in constraint_hash_options.items()
+            }
+        assert template.req  # to prevent mypy from being confused by the copy
+        name = canonicalize_name(template.req.name)
+
+        extras: frozenset[str] = frozenset()
+        for ireq in ireqs:
+            assert ireq.req, "Candidates found on index must be PEP 508"
+            specifier &= ireq.req.specifier
+            hashes &= ireq.hashes(trust_internet=False)
+            extras |= frozenset(ireq.extras)
+
+        def _get_installed_candidate() -> Candidate | None:
+            """Get the candidate for the currently-installed version."""
+            # If --force-reinstall is set, we want the version from the index
+            # instead, so we "pretend" there is nothing installed.
+            if self._force_reinstall:
+                return None
+            try:
+                installed_dist = self._installed_dists[name]
+            except KeyError:
+                return None
+
+            try:
+                # Don't use the installed distribution if its version
+                # does not fit the current dependency graph.
+                if not specifier.contains(installed_dist.version, prereleases=True):
+                    return None
+            except InvalidVersion as e:
+                raise InvalidInstalledPackage(dist=installed_dist, invalid_exc=e)
+
+            candidate = self._make_candidate_from_dist(
+                dist=installed_dist,
+                extras=extras,
+                template=template,
+            )
+            # The candidate is a known incompatibility. Don't use it.
+            if id(candidate) in incompatible_ids:
+                return None
+            return candidate
+
+        def iter_index_candidate_infos() -> Iterator[IndexCandidateInfo]:
+            result = self._finder.find_best_candidate(
+                project_name=name,
+                specifier=specifier,
+                hashes=hashes,
+            )
+            icans = result.applicable_candidates
+
+            # PEP 592: Yanked releases are ignored unless the specifier
+            # explicitly pins a version (via '==' or '===') that can be
+            # solely satisfied by a yanked release.
+            all_yanked = all(ican.link.is_yanked for ican in icans)
+
+            def is_pinned(specifier: SpecifierSet) -> bool:
+                for sp in specifier:
+                    if sp.operator == "===":
+                        return True
+                    if sp.operator != "==":
+                        continue
+                    if sp.version.endswith(".*"):
+                        continue
+                    return True
+                return False
+
+            pinned = is_pinned(specifier)
+
+            # PackageFinder returns earlier versions first, so we reverse.
+            for ican in reversed(icans):
+                if not (all_yanked and pinned) and ican.link.is_yanked:
+                    continue
+                func = functools.partial(
+                    self._make_candidate_from_link,
+                    link=ican.link,
+                    extras=extras,
+                    template=template,
+                    name=name,
+                    version=ican.version,
+                )
+                yield ican.version, func
+
+        return FoundCandidates(
+            iter_index_candidate_infos,
+            _get_installed_candidate(),
+            prefers_installed,
+            incompatible_ids,
+        )
+
+    def _iter_explicit_candidates_from_base(
+        self,
+        base_requirements: Iterable[Requirement],
+        extras: frozenset[str],
+    ) -> Iterator[Candidate]:
+        """Produce explicit candidates from the base given an extra-ed package.
+
+        :param base_requirements: Requirements known to the resolver. The
+            requirements are guaranteed to not have extras.
+        :param extras: The extras to inject into the explicit requirements'
+            candidates.
+        """
+        for req in base_requirements:
+            lookup_cand, _ = req.get_candidate_lookup()
+            if lookup_cand is None:  # Not explicit.
+                continue
+            # We've stripped extras from the identifier, and should always
+            # get a BaseCandidate here, unless there's a bug elsewhere.
+            base_cand = as_base_candidate(lookup_cand)
+            assert base_cand is not None, "no extras here"
+            yield self._make_extras_candidate(base_cand, extras)
+
+    def _iter_candidates_from_constraints(
+        self,
+        identifier: str,
+        constraint: Constraint,
+        template: InstallRequirement,
+    ) -> Iterator[Candidate]:
+        """Produce explicit candidates from constraints.
+
+        This creates "fake" InstallRequirement objects that are basically clones
+        of what "should" be the template, but with original_link set to link.
+        """
+        extras: frozenset[str] = frozenset()
+        base_identifier = identifier
+        with contextlib.suppress(InvalidRequirement):
+            parsed_requirement = get_requirement(identifier)
+            if parsed_requirement.name != identifier:
+                base_identifier = canonicalize_name(parsed_requirement.name)
+                extras = frozenset(parsed_requirement.extras)
+
+        for link in constraint.links:
+            self._fail_if_link_is_unsupported_wheel(link)
+            base_candidate = self._make_base_candidate_from_link(
+                link,
+                template=install_req_from_link_and_ireq(link, template),
+                name=canonicalize_name(base_identifier),
+                version=None,
+            )
+            if base_candidate is None:
+                continue
+            if extras:
+                yield self._make_extras_candidate(base_candidate, extras)
+            else:
+                yield base_candidate
+
+    def find_candidates(
+        self,
+        identifier: str,
+        requirements: Mapping[str, Iterable[Requirement]],
+        incompatibilities: Mapping[str, Iterator[Candidate]],
+        constraint: Constraint,
+        prefers_installed: bool,
+        is_satisfied_by: Callable[[Requirement, Candidate], bool],
+    ) -> Iterable[Candidate]:
+        # Collect basic lookup information from the requirements.
+        explicit_candidates: set[Candidate] = set()
+        ireqs: list[InstallRequirement] = []
+        for req in requirements[identifier]:
+            cand, ireq = req.get_candidate_lookup()
+            if cand is not None:
+                explicit_candidates.add(cand)
+            if ireq is not None:
+                ireqs.append(ireq)
+
+        # If the current identifier contains extras, add requires and explicit
+        # candidates from entries from extra-less identifier.
+        with contextlib.suppress(InvalidRequirement):
+            parsed_requirement = get_requirement(identifier)
+            if parsed_requirement.name != identifier:
+                explicit_candidates.update(
+                    self._iter_explicit_candidates_from_base(
+                        requirements.get(parsed_requirement.name, ()),
+                        frozenset(parsed_requirement.extras),
+                    ),
+                )
+                for req in requirements.get(parsed_requirement.name, []):
+                    _, ireq = req.get_candidate_lookup()
+                    if ireq is not None:
+                        ireqs.append(ireq)
+
+        # Add explicit candidates from constraints. We only do this if there are
+        # known ireqs, which represent requirements not already explicit. If
+        # there are no ireqs, we're constraining already-explicit requirements,
+        # which is handled later when we return the explicit candidates.
+        if ireqs:
+            try:
+                explicit_candidates.update(
+                    self._iter_candidates_from_constraints(
+                        identifier,
+                        constraint,
+                        template=ireqs[0],
+                    ),
+                )
+            except UnsupportedWheel:
+                # If we're constrained to install a wheel incompatible with the
+                # target architecture, no candidates will ever be valid.
+                return ()
+
+        # Since we cache all the candidates, incompatibility identification
+        # can be made quicker by comparing only the id() values.
+        incompat_ids = {id(c) for c in incompatibilities.get(identifier, ())}
+
+        # If none of the requirements want an explicit candidate, we can ask
+        # the finder for candidates.
+        if not explicit_candidates:
+            return self._iter_found_candidates(
+                ireqs,
+                constraint.specifier,
+                constraint.hashes,
+                prefers_installed,
+                incompat_ids,
+                constraint.hash_options,
+            )
+
+        return (
+            c
+            for c in explicit_candidates
+            if id(c) not in incompat_ids
+            and constraint.is_satisfied_by(c)
+            and all(is_satisfied_by(req, c) for req in requirements[identifier])
+        )
+
+    def _make_requirements_from_install_req(
+        self, ireq: InstallRequirement, requested_extras: Iterable[str]
+    ) -> Iterator[Requirement]:
+        """
+        Returns requirement objects associated with the given InstallRequirement. In
+        most cases this will be a single object but the following special cases exist:
+            - the InstallRequirement has markers that do not apply -> result is empty
+            - the InstallRequirement has both a constraint (or link) and extras
+                -> result is split in two requirement objects: one with the constraint
+                (or link) and one with the extra. This allows centralized constraint
+                handling for the base, resulting in fewer candidate rejections.
+        """
+        if not ireq.match_markers(requested_extras):
+            logger.info(
+                "Ignoring %s: markers '%s' don't match your environment",
+                ireq.name,
+                ireq.markers,
+            )
+        elif not ireq.link:
+            if ireq.extras and ireq.req is not None and ireq.req.specifier:
+                yield SpecifierWithoutExtrasRequirement(ireq)
+            yield SpecifierRequirement(ireq)
+        else:
+            self._fail_if_link_is_unsupported_wheel(ireq.link)
+            # Always make the link candidate for the base requirement to make it
+            # available to `find_candidates` for explicit candidate lookup for any
+            # set of extras.
+            # The extras are required separately via a second requirement.
+            cand = self._make_base_candidate_from_link(
+                ireq.link,
+                template=install_req_drop_extras(ireq) if ireq.extras else ireq,
+                name=canonicalize_name(ireq.name) if ireq.name else None,
+                version=None,
+            )
+            if cand is None:
+                # There's no way we can satisfy a URL requirement if the underlying
+                # candidate fails to build. An unnamed URL must be user-supplied, so
+                # we fail eagerly. If the URL is named, an unsatisfiable requirement
+                # can make the resolver do the right thing, either backtrack (and
+                # maybe find some other requirement that's buildable) or raise a
+                # ResolutionImpossible eventually.
+                if not ireq.name:
+                    raise self._build_failures[ireq.link]
+                yield UnsatisfiableRequirement(canonicalize_name(ireq.name))
+            else:
+                # require the base from the link
+                yield self.make_requirement_from_candidate(cand)
+                if ireq.extras:
+                    # require the extras on top of the base candidate
+                    yield self.make_requirement_from_candidate(
+                        self._make_extras_candidate(cand, frozenset(ireq.extras))
+                    )
+
+    def collect_root_requirements(
+        self, root_ireqs: list[InstallRequirement]
+    ) -> CollectedRootRequirements:
+        collected = CollectedRootRequirements([], {}, {})
+        for i, ireq in enumerate(root_ireqs):
+            if ireq.constraint:
+                # Ensure we only accept valid constraints
+                problem = check_invalid_constraint_type(ireq)
+                if problem:
+                    raise InstallationError(problem)
+                if not ireq.match_markers():
+                    continue
+                assert ireq.name, "Constraint must be named"
+                name = canonicalize_name(ireq.name)
+                if name in collected.constraints:
+                    collected.constraints[name] &= ireq
+                else:
+                    collected.constraints[name] = Constraint.from_ireq(ireq)
+            else:
+                reqs = list(
+                    self._make_requirements_from_install_req(
+                        ireq,
+                        requested_extras=(),
+                    )
+                )
+                if not reqs:
+                    continue
+                template = reqs[0]
+                if ireq.user_supplied and template.name not in collected.user_requested:
+                    collected.user_requested[template.name] = i
+                collected.requirements.extend(reqs)
+        # Put requirements with extras at the end of the root requires. This does not
+        # affect resolvelib's picking preference but it does affect its initial criteria
+        # population: by putting extras at the end we enable the candidate finder to
+        # present resolvelib with a smaller set of candidates to resolvelib, already
+        # taking into account any non-transient constraints on the associated base. This
+        # means resolvelib will have fewer candidates to visit and reject.
+        # Python's list sort is stable, meaning relative order is kept for objects with
+        # the same key.
+        collected.requirements.sort(key=lambda r: r.name != r.project_name)
+        return collected
+
+    def make_requirement_from_candidate(
+        self, candidate: Candidate
+    ) -> ExplicitRequirement:
+        return ExplicitRequirement(candidate)
+
+    def make_requirements_from_spec(
+        self,
+        specifier: str,
+        comes_from: InstallRequirement | None,
+        requested_extras: Iterable[str] = (),
+    ) -> Iterator[Requirement]:
+        """
+        Returns requirement objects associated with the given specifier. In most cases
+        this will be a single object but the following special cases exist:
+            - the specifier has markers that do not apply -> result is empty
+            - the specifier has both a constraint and extras -> result is split
+                in two requirement objects: one with the constraint and one with the
+                extra. This allows centralized constraint handling for the base,
+                resulting in fewer candidate rejections.
+        """
+        ireq = self._make_install_req_from_spec(specifier, comes_from)
+        return self._make_requirements_from_install_req(ireq, requested_extras)
+
+    def make_requires_python_requirement(
+        self,
+        specifier: SpecifierSet,
+    ) -> Requirement | None:
+        if self._ignore_requires_python:
+            return None
+        # Don't bother creating a dependency for an empty Requires-Python.
+        if not str(specifier):
+            return None
+        return RequiresPythonRequirement(specifier, self._python_candidate)
+
+    def get_wheel_cache_entry(self, link: Link, name: str | None) -> CacheEntry | None:
+        """Look up the link in the wheel cache.
+
+        If ``preparer.require_hashes`` is True, don't use the wheel cache,
+        because cached wheels, always built locally, have different hashes
+        than the files downloaded from the index server and thus throw false
+        hash mismatches. Furthermore, cached wheels at present have
+        nondeterministic contents due to file modification times.
+        """
+        if self._wheel_cache is None:
+            return None
+        return self._wheel_cache.get_cache_entry(
+            link=link,
+            package_name=name,
+            supported_tags=self._supported_tags_cache,
+        )
+
+    def get_dist_to_uninstall(self, candidate: Candidate) -> BaseDistribution | None:
+        # TODO: Are there more cases this needs to return True? Editable?
+        dist = self._installed_dists.get(candidate.project_name)
+        if dist is None:  # Not installed, no uninstallation required.
+            return None
+
+        # We're installing into global site. The current installation must
+        # be uninstalled, no matter it's in global or user site, because the
+        # user site installation has precedence over global.
+        if not self._use_user_site:
+            return dist
+
+        # We're installing into user site. Remove the user site installation.
+        if dist.in_usersite:
+            return dist
+
+        # We're installing into user site, but the installed incompatible
+        # package is in global site. We can't uninstall that, and would let
+        # the new user installation to "shadow" it. But shadowing won't work
+        # in virtual environments, so we error out.
+        if running_under_virtualenv() and dist.in_site_packages:
+            message = (
+                f"Will not install to the user site because it will lack "
+                f"sys.path precedence to {dist.raw_name} in {dist.location}"
+            )
+            raise InstallationError(message)
+        return None
+
+    def _report_requires_python_error(
+        self, causes: Sequence[ConflictCause]
+    ) -> UnsupportedPythonVersion:
+        assert causes, "Requires-Python error reported with no cause"
+
+        version = self._python_candidate.version
+
+        if len(causes) == 1:
+            specifier = str(causes[0].requirement.specifier)
+            message = (
+                f"Package {causes[0].parent.name!r} requires a different "
+                f"Python: {version} not in {specifier!r}"
+            )
+            return UnsupportedPythonVersion(message)
+
+        message = f"Packages require a different Python. {version} not in:"
+        for cause in causes:
+            package = cause.parent.format_for_error()
+            specifier = str(cause.requirement.specifier)
+            message += f"\n{specifier!r} (required by {package})"
+        return UnsupportedPythonVersion(message)
+
+    def _report_single_requirement_conflict(
+        self, req: Requirement, parent: Candidate | None
+    ) -> DistributionNotFound:
+        if parent is None:
+            req_disp = str(req)
+        else:
+            req_disp = f"{req} (from {parent.name})"
+
+        cands = self._finder.find_all_candidates(req.project_name)
+        skipped_by_requires_python = self._finder.requires_python_skipped_reasons()
+
+        versions_set: set[Version] = set()
+        yanked_versions_set: set[Version] = set()
+        for c in cands:
+            is_yanked = c.link.is_yanked if c.link else False
+            if is_yanked:
+                yanked_versions_set.add(c.version)
+            else:
+                versions_set.add(c.version)
+
+        versions = [str(v) for v in sorted(versions_set)]
+        yanked_versions = [str(v) for v in sorted(yanked_versions_set)]
+
+        if yanked_versions:
+            # Saying "version X is yanked" isn't entirely accurate.
+            # https://github.com/pypa/pip/issues/11745#issuecomment-1402805842
+            logger.critical(
+                "Ignored the following yanked versions: %s",
+                ", ".join(yanked_versions) or "none",
+            )
+        if skipped_by_requires_python:
+            logger.critical(
+                "Ignored the following versions that require a different python "
+                "version: %s",
+                "; ".join(skipped_by_requires_python) or "none",
+            )
+
+        # Check if only final releases are allowed for this package
+        version_type = "version"
+        allows_pre = None
+        if self._finder.release_control is not None:
+            allows_pre = self._finder.release_control.allows_prereleases(
+                canonicalize_name(req.project_name)
+            )
+            if allows_pre is False:
+                version_type = "final version"
+
+        if len(cands) == 1 and cands[0].locked:
+            # The package finder ensures that requirements from pylock files
+            # have exactly one candidate. So we can provide a specific error
+            # message in this case.
+            if cands[0].version.is_prerelease and allows_pre is False:
+                logger.critical(
+                    "A pre-release version %s is specified in a provided lock file "
+                    "for %s but only final versions are allowed",
+                    cands[0].version,
+                    cands[0].name,
+                )
+            else:
+                logger.critical(
+                    "The requirement %s is not compatible with "
+                    "version %s specified in a provided lock file",
+                    req_disp,
+                    ", ".join(versions),
+                )
+        else:
+            logger.critical(
+                "Could not find a %s that satisfies the requirement %s "
+                "(from versions: %s)",
+                version_type,
+                req_disp,
+                ", ".join(versions) or "none",
+            )
+        if str(req) == "requirements.txt":
+            logger.info(
+                "HINT: You are attempting to install a package literally "
+                'named "requirements.txt" (which cannot exist). Consider '
+                "using the '-r' flag to install the packages listed in "
+                "requirements.txt"
+            )
+
+        return DistributionNotFound(f"No matching distribution found for {req}")
+
+    def _has_any_candidates(self, project_name: str) -> bool:
+        """
+        Check if there are any candidates available for the project name.
+        """
+        return any(
+            self.find_candidates(
+                project_name,
+                requirements={project_name: []},
+                incompatibilities={},
+                constraint=Constraint.empty(),
+                prefers_installed=True,
+                is_satisfied_by=lambda r, c: True,
+            )
+        )
+
+    def get_installation_error(
+        self,
+        e: ResolutionImpossible[Requirement, Candidate],
+        constraints: dict[str, Constraint],
+    ) -> InstallationError:
+        assert e.causes, "Installation error reported with no cause"
+
+        # If one of the things we can't solve is "we need Python X.Y",
+        # that is what we report.
+        requires_python_causes = [
+            cause
+            for cause in e.causes
+            if isinstance(cause.requirement, RequiresPythonRequirement)
+            and not cause.requirement.is_satisfied_by(self._python_candidate)
+        ]
+        if requires_python_causes:
+            # The comprehension above makes sure all Requirement instances are
+            # RequiresPythonRequirement, so let's cast for convenience.
+            return self._report_requires_python_error(
+                cast("Sequence[ConflictCause]", requires_python_causes),
+            )
+
+        # Otherwise, we have a set of causes which can't all be satisfied
+        # at once.
+
+        # The simplest case is when we have *one* cause that can't be
+        # satisfied. We just report that case.
+        if len(e.causes) == 1:
+            req, parent = next(iter(e.causes))
+            if req.name not in constraints:
+                return self._report_single_requirement_conflict(req, parent)
+
+        # OK, we now have a list of requirements that can't all be
+        # satisfied at once.
+
+        # A couple of formatting helpers
+        def text_join(parts: list[str]) -> str:
+            if len(parts) == 1:
+                return parts[0]
+
+            return ", ".join(parts[:-1]) + " and " + parts[-1]
+
+        def describe_trigger(parent: Candidate) -> str:
+            ireq = parent.get_install_requirement()
+            if not ireq or not ireq.comes_from:
+                return f"{parent.name}=={parent.version}"
+            if isinstance(ireq.comes_from, InstallRequirement):
+                return str(ireq.comes_from.name)
+            return str(ireq.comes_from)
+
+        triggers = set()
+        for req, parent in e.causes:
+            if parent is None:
+                # This is a root requirement, so we can report it directly
+                trigger = req.format_for_error()
+            else:
+                trigger = describe_trigger(parent)
+            triggers.add(trigger)
+
+        if triggers:
+            info = text_join(sorted(triggers))
+        else:
+            info = "the requested packages"
+
+        msg = (
+            f"Cannot install {info} because these package versions "
+            "have conflicting dependencies."
+        )
+        logger.critical(msg)
+        msg = "\nThe conflict is caused by:"
+
+        relevant_constraints = set()
+        for req, parent in e.causes:
+            if req.name in constraints:
+                relevant_constraints.add(req.name)
+            msg = msg + "\n    "
+            if parent:
+                msg = msg + f"{parent.name} {parent.version} depends on "
+            else:
+                msg = msg + "The user requested "
+            msg = msg + req.format_for_error()
+        for key in relevant_constraints:
+            constraint_text = f"{key}{constraints[key].format_for_error()}"
+            msg += f"\n    The user requested (constraint) {constraint_text}"
+
+        # Check for causes that had no candidates
+        causes = set()
+        for req, _ in e.causes:
+            causes.add(req.name)
+
+        no_candidates = {c for c in causes if not self._has_any_candidates(c)}
+        if no_candidates:
+            msg = (
+                msg
+                + "\n\n"
+                + "Additionally, some packages in these conflicts have no "
+                + "matching distributions available for your environment:"
+                + "\n    "
+                + "\n    ".join(sorted(no_candidates))
+            )
+
+        msg = (
+            msg
+            + "\n\n"
+            + "To fix this you could try to:\n"
+            + "1. loosen the range of package versions you've specified\n"
+            + "2. remove package versions to allow pip to attempt to solve "
+            + "the dependency conflict\n"
+        )
+
+        logger.info(msg)
+
+        return DistributionNotFound(
+            "ResolutionImpossible: for help visit "
+            "https://pip.pypa.io/en/latest/topics/dependency-resolution/"
+            "#dealing-with-dependency-conflicts"
+        )
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py
new file mode 100644
index 0000000..6701673
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py
@@ -0,0 +1,166 @@
+"""Utilities to lazily create and visit candidates found.
+
+Creating and visiting a candidate is a *very* costly operation. It involves
+fetching, extracting, potentially building modules from source, and verifying
+distribution metadata. It is therefore crucial for performance to keep
+everything here lazy all the way down, so we only touch candidates that we
+absolutely need, and not "download the world" when we only need one version of
+something.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable, Iterator, Sequence
+from typing import Any
+
+from pip._vendor.packaging.version import _BaseVersion
+
+from pip._internal.exceptions import MetadataInvalid
+
+from .base import Candidate
+
+logger = logging.getLogger(__name__)
+
+IndexCandidateInfo = tuple[_BaseVersion, Callable[[], Candidate | None]]
+
+
+def _iter_built(infos: Iterator[IndexCandidateInfo]) -> Iterator[Candidate]:
+    """Iterator for ``FoundCandidates``.
+
+    This iterator is used when the package is not already installed. Candidates
+    from index come later in their normal ordering.
+    """
+    versions_found: set[_BaseVersion] = set()
+    for version, func in infos:
+        if version in versions_found:
+            continue
+        try:
+            candidate = func()
+        except MetadataInvalid as e:
+            logger.warning(
+                "Ignoring version %s of %s since it has invalid metadata:\n"
+                "%s\n"
+                "Please use pip<24.1 if you need to use this version.",
+                version,
+                e.ireq.name,
+                e,
+            )
+            # Mark version as found to avoid trying other candidates with the same
+            # version, since they most likely have invalid metadata as well.
+            versions_found.add(version)
+        else:
+            if candidate is None:
+                continue
+            yield candidate
+            versions_found.add(version)
+
+
+def _iter_built_with_prepended(
+    installed: Candidate, infos: Iterator[IndexCandidateInfo]
+) -> Iterator[Candidate]:
+    """Iterator for ``FoundCandidates``.
+
+    This iterator is used when the resolver prefers the already-installed
+    candidate and NOT to upgrade. The installed candidate is therefore
+    always yielded first, and candidates from index come later in their
+    normal ordering, except skipped when the version is already installed.
+    """
+    yield installed
+    versions_found: set[_BaseVersion] = {installed.version}
+    for version, func in infos:
+        if version in versions_found:
+            continue
+        candidate = func()
+        if candidate is None:
+            continue
+        yield candidate
+        versions_found.add(version)
+
+
+def _iter_built_with_inserted(
+    installed: Candidate, infos: Iterator[IndexCandidateInfo]
+) -> Iterator[Candidate]:
+    """Iterator for ``FoundCandidates``.
+
+    This iterator is used when the resolver prefers to upgrade an
+    already-installed package. Candidates from index are returned in their
+    normal ordering, except replaced when the version is already installed.
+
+    The implementation iterates through and yields other candidates, inserting
+    the installed candidate exactly once before we start yielding older or
+    equivalent candidates, or after all other candidates if they are all newer.
+    """
+    versions_found: set[_BaseVersion] = set()
+    for version, func in infos:
+        if version in versions_found:
+            continue
+        # If the installed candidate is better, yield it first.
+        if installed.version >= version:
+            yield installed
+            versions_found.add(installed.version)
+        candidate = func()
+        if candidate is None:
+            continue
+        yield candidate
+        versions_found.add(version)
+
+    # If the installed candidate is older than all other candidates.
+    if installed.version not in versions_found:
+        yield installed
+
+
+class FoundCandidates(Sequence[Candidate]):
+    """A lazy sequence to provide candidates to the resolver.
+
+    The intended usage is to return this from `find_matches()` so the resolver
+    can iterate through the sequence multiple times, but only access the index
+    page when remote packages are actually needed. This improve performances
+    when suitable candidates are already installed on disk.
+    """
+
+    def __init__(
+        self,
+        get_infos: Callable[[], Iterator[IndexCandidateInfo]],
+        installed: Candidate | None,
+        prefers_installed: bool,
+        incompatible_ids: set[int],
+    ):
+        self._get_infos = get_infos
+        self._installed = installed
+        self._prefers_installed = prefers_installed
+        self._incompatible_ids = incompatible_ids
+        self._bool: bool | None = None
+
+    def __getitem__(self, index: Any) -> Any:
+        # Implemented to satisfy the ABC check. This is not needed by the
+        # resolver, and should not be used by the provider either (for
+        # performance reasons).
+        raise NotImplementedError("don't do this")
+
+    def __iter__(self) -> Iterator[Candidate]:
+        infos = self._get_infos()
+        if not self._installed:
+            iterator = _iter_built(infos)
+        elif self._prefers_installed:
+            iterator = _iter_built_with_prepended(self._installed, infos)
+        else:
+            iterator = _iter_built_with_inserted(self._installed, infos)
+        return (c for c in iterator if id(c) not in self._incompatible_ids)
+
+    def __len__(self) -> int:
+        # Implemented to satisfy the ABC check. This is not needed by the
+        # resolver, and should not be used by the provider either (for
+        # performance reasons).
+        raise NotImplementedError("don't do this")
+
+    def __bool__(self) -> bool:
+        if self._bool is not None:
+            return self._bool
+
+        if self._prefers_installed and self._installed:
+            self._bool = True
+            return True
+
+        self._bool = any(self)
+        return self._bool
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/provider.py b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/provider.py
new file mode 100644
index 0000000..766fa84
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/provider.py
@@ -0,0 +1,309 @@
+from __future__ import annotations
+
+import math
+from collections import defaultdict
+from collections.abc import Iterable, Iterator, Mapping, Sequence
+from functools import cache
+from typing import (
+    TYPE_CHECKING,
+    TypeVar,
+)
+
+from pip._vendor.resolvelib.providers import AbstractProvider
+
+from pip._internal.req.req_install import InstallRequirement
+
+from .base import Candidate, Constraint, Requirement
+from .candidates import REQUIRES_PYTHON_IDENTIFIER
+from .factory import Factory
+from .requirements import ExplicitRequirement
+
+if TYPE_CHECKING:
+    from pip._vendor.resolvelib.providers import Preference
+    from pip._vendor.resolvelib.resolvers import RequirementInformation
+
+    PreferenceInformation = RequirementInformation[Requirement, Candidate]
+
+    _ProviderBase = AbstractProvider[Requirement, Candidate, str]
+else:
+    _ProviderBase = AbstractProvider
+
+_CONFLICT_PRIORITY_THRESHOLD = 5
+
+# Notes on the relationship between the provider, the factory, and the
+# candidate and requirement classes.
+#
+# The provider is a direct implementation of the resolvelib class. Its role
+# is to deliver the API that resolvelib expects.
+#
+# Rather than work with completely abstract "requirement" and "candidate"
+# concepts as resolvelib does, pip has concrete classes implementing these two
+# ideas. The API of Requirement and Candidate objects are defined in the base
+# classes, but essentially map fairly directly to the equivalent provider
+# methods. In particular, `find_matches` and `is_satisfied_by` are
+# requirement methods, and `get_dependencies` is a candidate method.
+#
+# The factory is the interface to pip's internal mechanisms. It is stateless,
+# and is created by the resolver and held as a property of the provider. It is
+# responsible for creating Requirement and Candidate objects, and provides
+# services to those objects (access to pip's finder and preparer).
+
+
+D = TypeVar("D")
+V = TypeVar("V")
+
+
+def _get_with_identifier(
+    mapping: Mapping[str, V],
+    identifier: str,
+    default: D,
+) -> D | V:
+    """Get item from a package name lookup mapping with a resolver identifier.
+
+    This extra logic is needed when the target mapping is keyed by package
+    name, which cannot be directly looked up with an identifier (which may
+    contain requested extras). Additional logic is added to also look up a value
+    by "cleaning up" the extras from the identifier.
+    """
+    if identifier in mapping:
+        return mapping[identifier]
+    # HACK: Theoretically we should check whether this identifier is a valid
+    # "NAME[EXTRAS]" format, and parse out the name part with packaging or
+    # some regular expression. But since pip's resolver only spits out three
+    # kinds of identifiers: normalized PEP 503 names, normalized names plus
+    # extras, and Requires-Python, we can cheat a bit here.
+    name, open_bracket, _ = identifier.partition("[")
+    if open_bracket and name in mapping:
+        return mapping[name]
+    return default
+
+
+class PipProvider(_ProviderBase):
+    """Pip's provider implementation for resolvelib.
+
+    :params constraints: A mapping of constraints specified by the user. Keys
+        are canonicalized project names.
+    :params ignore_dependencies: Whether the user specified ``--no-deps``.
+    :params only_dependencies: Whether the user specified ``--only-deps``.
+    :params upgrade_strategy: The user-specified upgrade strategy.
+    :params user_requested: A set of canonicalized package names that the user
+        supplied for pip to install/upgrade.
+    """
+
+    def __init__(
+        self,
+        factory: Factory,
+        constraints: dict[str, Constraint],
+        ignore_dependencies: bool,
+        only_dependencies: bool,
+        upgrade_strategy: str,
+        user_requested: dict[str, int],
+    ) -> None:
+        self._factory = factory
+        self._constraints = constraints
+        self._ignore_dependencies = ignore_dependencies
+        self._only_dependencies = only_dependencies
+        self._upgrade_strategy = upgrade_strategy
+        self._user_requested = user_requested
+        self._conflict_counts: defaultdict[str, int] = defaultdict(int)
+        self._conflict_promoted: set[str] = set()
+
+    @property
+    def constraints(self) -> dict[str, Constraint]:
+        """Public view of user-specified constraints.
+
+        Exposes the provider's constraints mapping without encouraging
+        external callers to reach into private attributes.
+        """
+        return self._constraints
+
+    def identify(self, requirement_or_candidate: Requirement | Candidate) -> str:
+        return requirement_or_candidate.name
+
+    def narrow_requirement_selection(
+        self,
+        identifiers: Iterable[str],
+        resolutions: Mapping[str, Candidate],
+        candidates: Mapping[str, Iterator[Candidate]],
+        information: Mapping[str, Iterator[PreferenceInformation]],
+        backtrack_causes: Sequence[PreferenceInformation],
+    ) -> Iterable[str]:
+        """Produce a subset of identifiers that should be considered before others.
+
+        Currently pip narrows the following selection:
+            * Requires-Python, if present is always returned by itself
+            * Backtrack causes are considered next because they can be identified
+              in linear time here, whereas because get_preference() is called
+              for each identifier, it would be quadratic to check for them there.
+              Further, the current backtrack causes likely need to be resolved
+              before other requirements as a resolution can't be found while
+              there is a conflict.
+            * Identifiers that repeatedly appear as not-yet-pinned in conflicts
+              get promoted so they are resolved earlier. This lets their
+              constraints take effect before other packages pick a version.
+        """
+        backtrack_identifiers = set()
+        for info in backtrack_causes:
+            names = [info.requirement.name]
+            if info.parent is not None:
+                names.append(info.parent.name)
+            for name in names:
+                backtrack_identifiers.add(name)
+                if name not in resolutions:
+                    self._conflict_counts[name] += 1
+                    if self._conflict_counts[name] >= _CONFLICT_PRIORITY_THRESHOLD:
+                        self._conflict_promoted.add(name)
+
+        current_backtrack_causes = []
+        promoted = []
+        for identifier in identifiers:
+            if identifier == REQUIRES_PYTHON_IDENTIFIER:
+                return [identifier]
+
+            if identifier in backtrack_identifiers:
+                current_backtrack_causes.append(identifier)
+                continue
+
+            if identifier in self._conflict_promoted:
+                promoted.append(identifier)
+                continue
+
+        if current_backtrack_causes:
+            return current_backtrack_causes
+
+        if promoted:
+            return promoted
+
+        return identifiers
+
+    def get_preference(
+        self,
+        identifier: str,
+        resolutions: Mapping[str, Candidate],
+        candidates: Mapping[str, Iterator[Candidate]],
+        information: Mapping[str, Iterable[PreferenceInformation]],
+        backtrack_causes: Sequence[PreferenceInformation],
+    ) -> Preference:
+        """Produce a sort key for given requirement based on preference.
+
+        The lower the return value is, the more preferred this group of
+        arguments is.
+
+        Currently pip considers the following in order:
+
+        * Any requirement that is "direct", e.g., points to an explicit URL.
+        * Any requirement that is "pinned", i.e., contains the operator ``===``
+          or ``==`` without a wildcard.
+        * Any requirement that imposes an upper version limit, i.e., contains the
+          operator ``<``, ``<=``, ``~=``, or ``==`` with a wildcard. Because
+          pip prioritizes the latest version, preferring explicit upper bounds
+          can rule out infeasible candidates sooner. This does not imply that
+          upper bounds are good practice; they can make dependency management
+          and resolution harder.
+        * Order user-specified requirements as they are specified, placing
+          other requirements afterward.
+        * Any "non-free" requirement, i.e., one that contains at least one
+          operator, such as ``>=`` or ``!=``.
+        * Alphabetical order for consistency (aids debuggability).
+        """
+        try:
+            next(iter(information[identifier]))
+        except StopIteration:
+            # There is no information for this identifier, so there's no known
+            # candidates.
+            has_information = False
+        else:
+            has_information = True
+
+        if not has_information:
+            direct = False
+            ireqs: tuple[InstallRequirement | None, ...] = ()
+        else:
+            # Go through the information and for each requirement,
+            # check if it's explicit (e.g., a direct link) and get the
+            # InstallRequirement (the second element) from get_candidate_lookup()
+            directs, ireqs = zip(
+                *(
+                    (isinstance(r, ExplicitRequirement), r.get_candidate_lookup()[1])
+                    for r, _ in information[identifier]
+                )
+            )
+            direct = any(directs)
+
+        operators: list[tuple[str, str]] = [
+            (specifier.operator, specifier.version)
+            for specifier_set in (ireq.specifier for ireq in ireqs if ireq)
+            for specifier in specifier_set
+        ]
+
+        pinned = any(((op[:2] == "==") and ("*" not in ver)) for op, ver in operators)
+        upper_bounded = any(
+            ((op in ("<", "<=", "~=")) or (op == "==" and "*" in ver))
+            for op, ver in operators
+        )
+        unfree = bool(operators)
+        requested_order = self._user_requested.get(identifier, math.inf)
+
+        conflict_promoted = identifier in self._conflict_promoted
+
+        return (
+            not conflict_promoted,
+            not direct,
+            not pinned,
+            not upper_bounded,
+            requested_order,
+            not unfree,
+            identifier,
+        )
+
+    def find_matches(
+        self,
+        identifier: str,
+        requirements: Mapping[str, Iterator[Requirement]],
+        incompatibilities: Mapping[str, Iterator[Candidate]],
+    ) -> Iterable[Candidate]:
+        def _eligible_for_upgrade(identifier: str) -> bool:
+            """Are upgrades allowed for this project?
+
+            This checks the upgrade strategy, and whether the project was one
+            that the user specified in the command line, in order to decide
+            whether we should upgrade if there's a newer version available.
+
+            (Note that we don't need access to the `--upgrade` flag, because
+            an upgrade strategy of "to-satisfy-only" means that `--upgrade`
+            was not specified).
+            """
+            if self._upgrade_strategy == "eager":
+                return True
+            elif self._upgrade_strategy == "only-if-needed":
+                user_order = _get_with_identifier(
+                    self._user_requested,
+                    identifier,
+                    default=None,
+                )
+                return user_order is not None
+            return False
+
+        constraint = _get_with_identifier(
+            self._constraints,
+            identifier,
+            default=Constraint.empty(),
+        )
+        return self._factory.find_candidates(
+            identifier=identifier,
+            requirements=requirements,
+            constraint=constraint,
+            prefers_installed=(not _eligible_for_upgrade(identifier)),
+            incompatibilities=incompatibilities,
+            is_satisfied_by=self.is_satisfied_by,
+        )
+
+    @staticmethod
+    @cache
+    def is_satisfied_by(requirement: Requirement, candidate: Candidate) -> bool:
+        return requirement.is_satisfied_by(candidate)
+
+    def get_dependencies(self, candidate: Candidate) -> Iterable[Requirement]:
+        with_requires = not self._ignore_dependencies
+        # iter_dependencies() can perform nontrivial work so delay until needed.
+        return (r for r in candidate.iter_dependencies(with_requires) if r is not None)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/reporter.py b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/reporter.py
new file mode 100644
index 0000000..de0deb2
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/reporter.py
@@ -0,0 +1,98 @@
+from __future__ import annotations
+
+from collections import defaultdict
+from collections.abc import Mapping
+from logging import getLogger
+from typing import Any
+
+from pip._vendor.resolvelib.reporters import BaseReporter
+
+from .base import Candidate, Constraint, Requirement
+
+logger = getLogger(__name__)
+
+
+class PipReporter(BaseReporter[Requirement, Candidate, str]):
+    def __init__(self, constraints: Mapping[str, Constraint] | None = None) -> None:
+        self.reject_count_by_package: defaultdict[str, int] = defaultdict(int)
+        self._constraints = constraints or {}
+
+        self._messages_at_reject_count = {
+            1: (
+                "pip is looking at multiple versions of {package_name} to "
+                "determine which version is compatible with other "
+                "requirements. This could take a while."
+            ),
+            8: (
+                "pip is still looking at multiple versions of {package_name} to "
+                "determine which version is compatible with other "
+                "requirements. This could take a while."
+            ),
+            13: (
+                "This is taking longer than usual. You might need to provide "
+                "the dependency resolver with stricter constraints to reduce "
+                "runtime. See https://pip.pypa.io/warnings/backtracking for "
+                "guidance. If you want to abort this run, press Ctrl + C."
+            ),
+        }
+
+    def rejecting_candidate(self, criterion: Any, candidate: Candidate) -> None:
+        """Report a candidate being rejected.
+
+        Logs both the rejection count message (if applicable) and details about
+        the requirements and constraints that caused the rejection.
+        """
+        self.reject_count_by_package[candidate.name] += 1
+
+        count = self.reject_count_by_package[candidate.name]
+        if count in self._messages_at_reject_count:
+            message = self._messages_at_reject_count[count]
+            logger.info("INFO: %s", message.format(package_name=candidate.name))
+
+        msg = "Will try a different candidate, due to conflict:"
+        for req_info in criterion.information:
+            req, parent = req_info.requirement, req_info.parent
+            msg += "\n    "
+            if parent:
+                msg += f"{parent.name} {parent.version} depends on "
+            else:
+                msg += "The user requested "
+            msg += req.format_for_error()
+
+        # Add any relevant constraints
+        if self._constraints:
+            name = candidate.name
+            constraint = self._constraints.get(name)
+            if constraint and constraint.specifier:
+                constraint_text = f"{name}{constraint.format_for_error()}"
+                msg += f"\n    The user requested (constraint) {constraint_text}"
+
+        logger.debug(msg)
+
+
+class PipDebuggingReporter(BaseReporter[Requirement, Candidate, str]):
+    """A reporter that does an info log for every event it sees."""
+
+    def starting(self) -> None:
+        logger.info("Reporter.starting()")
+
+    def starting_round(self, index: int) -> None:
+        logger.info("Reporter.starting_round(%r)", index)
+
+    def ending_round(self, index: int, state: Any) -> None:
+        logger.info("Reporter.ending_round(%r, state)", index)
+        logger.debug("Reporter.ending_round(%r, %r)", index, state)
+
+    def ending(self, state: Any) -> None:
+        logger.info("Reporter.ending(%r)", state)
+
+    def adding_requirement(
+        self, requirement: Requirement, parent: Candidate | None
+    ) -> None:
+        logger.info("Reporter.adding_requirement(%r, %r)", requirement, parent)
+
+    def rejecting_candidate(self, criterion: Any, candidate: Candidate) -> None:
+        logger.info("Reporter.rejecting_candidate(%r, %r)", criterion, candidate)
+
+    def pinning(self, candidate: Candidate) -> None:
+        logger.info("Reporter.pinning(%r)", candidate)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/requirements.py b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/requirements.py
new file mode 100644
index 0000000..9c5e233
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/requirements.py
@@ -0,0 +1,251 @@
+from __future__ import annotations
+
+from typing import Any
+
+from pip._vendor.packaging.specifiers import SpecifierSet
+from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
+
+from pip._internal.req.constructors import install_req_drop_extras
+from pip._internal.req.req_install import InstallRequirement
+
+from .base import Candidate, CandidateLookup, Requirement, format_name
+
+
+class ExplicitRequirement(Requirement):
+    def __init__(self, candidate: Candidate) -> None:
+        self.candidate = candidate
+
+    def __str__(self) -> str:
+        return str(self.candidate)
+
+    def __repr__(self) -> str:
+        return f"{self.__class__.__name__}({self.candidate!r})"
+
+    def __hash__(self) -> int:
+        return hash(self.candidate)
+
+    def __eq__(self, other: Any) -> bool:
+        if not isinstance(other, ExplicitRequirement):
+            return False
+        return self.candidate == other.candidate
+
+    @property
+    def project_name(self) -> NormalizedName:
+        # No need to canonicalize - the candidate did this
+        return self.candidate.project_name
+
+    @property
+    def name(self) -> str:
+        # No need to canonicalize - the candidate did this
+        return self.candidate.name
+
+    def format_for_error(self) -> str:
+        return self.candidate.format_for_error()
+
+    def get_candidate_lookup(self) -> CandidateLookup:
+        return self.candidate, None
+
+    def is_satisfied_by(self, candidate: Candidate) -> bool:
+        return candidate == self.candidate
+
+
+class SpecifierRequirement(Requirement):
+    def __init__(self, ireq: InstallRequirement) -> None:
+        assert ireq.link is None, "This is a link, not a specifier"
+        self._ireq = ireq
+        self._equal_cache: str | None = None
+        self._hash: int | None = None
+        self._extras = frozenset(canonicalize_name(e) for e in self._ireq.extras)
+
+    @property
+    def _equal(self) -> str:
+        if self._equal_cache is not None:
+            return self._equal_cache
+
+        self._equal_cache = str(self._ireq)
+        return self._equal_cache
+
+    def __str__(self) -> str:
+        return str(self._ireq.req)
+
+    def __repr__(self) -> str:
+        return f"{self.__class__.__name__}({str(self._ireq.req)!r})"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, SpecifierRequirement):
+            return NotImplemented
+        return self._equal == other._equal
+
+    def __hash__(self) -> int:
+        if self._hash is not None:
+            return self._hash
+
+        self._hash = hash(self._equal)
+        return self._hash
+
+    @property
+    def project_name(self) -> NormalizedName:
+        assert self._ireq.req, "Specifier-backed ireq is always PEP 508"
+        return canonicalize_name(self._ireq.req.name)
+
+    @property
+    def name(self) -> str:
+        return format_name(self.project_name, self._extras)
+
+    def format_for_error(self) -> str:
+        # Convert comma-separated specifiers into "A, B, ..., F and G"
+        # This makes the specifier a bit more "human readable", without
+        # risking a change in meaning. (Hopefully! Not all edge cases have
+        # been checked)
+        parts = [s.strip() for s in str(self).split(",")]
+        if len(parts) == 0:
+            return ""
+        elif len(parts) == 1:
+            return parts[0]
+
+        return ", ".join(parts[:-1]) + " and " + parts[-1]
+
+    def get_candidate_lookup(self) -> CandidateLookup:
+        return None, self._ireq
+
+    def is_satisfied_by(self, candidate: Candidate) -> bool:
+        assert candidate.name == self.name, (
+            f"Internal issue: Candidate is not for this requirement "
+            f"{candidate.name} vs {self.name}"
+        )
+        # We can safely always allow prereleases here since PackageFinder
+        # already implements the prerelease logic, and would have filtered out
+        # prerelease candidates if the user does not expect them.
+        assert self._ireq.req, "Specifier-backed ireq is always PEP 508"
+        spec = self._ireq.req.specifier
+        return spec.contains(candidate.version, prereleases=True)
+
+
+class SpecifierWithoutExtrasRequirement(SpecifierRequirement):
+    """
+    Requirement backed by an install requirement on a base package.
+    Trims extras from its install requirement if there are any.
+    """
+
+    def __init__(self, ireq: InstallRequirement) -> None:
+        assert ireq.link is None, "This is a link, not a specifier"
+        self._ireq = install_req_drop_extras(ireq)
+        self._equal_cache: str | None = None
+        self._hash: int | None = None
+        self._extras = frozenset(canonicalize_name(e) for e in self._ireq.extras)
+
+    @property
+    def _equal(self) -> str:
+        if self._equal_cache is not None:
+            return self._equal_cache
+
+        self._equal_cache = str(self._ireq)
+        return self._equal_cache
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, SpecifierWithoutExtrasRequirement):
+            return NotImplemented
+        return self._equal == other._equal
+
+    def __hash__(self) -> int:
+        if self._hash is not None:
+            return self._hash
+
+        self._hash = hash(self._equal)
+        return self._hash
+
+
+class RequiresPythonRequirement(Requirement):
+    """A requirement representing Requires-Python metadata."""
+
+    def __init__(self, specifier: SpecifierSet, match: Candidate) -> None:
+        self.specifier = specifier
+        self._specifier_string = str(specifier)  # for faster __eq__
+        self._hash: int | None = None
+        self._candidate = match
+
+        # Pre-compute candidate lookup to avoid repeated specifier checks
+        if specifier.contains(match.version, prereleases=True):
+            self._candidate_lookup: CandidateLookup = (match, None)
+        else:
+            self._candidate_lookup = (None, None)
+
+    def __str__(self) -> str:
+        return f"Python {self.specifier}"
+
+    def __repr__(self) -> str:
+        return f"{self.__class__.__name__}({str(self.specifier)!r})"
+
+    def __hash__(self) -> int:
+        if self._hash is not None:
+            return self._hash
+
+        self._hash = hash((self._specifier_string, self._candidate))
+        return self._hash
+
+    def __eq__(self, other: Any) -> bool:
+        if not isinstance(other, RequiresPythonRequirement):
+            return False
+        return (
+            self._specifier_string == other._specifier_string
+            and self._candidate == other._candidate
+        )
+
+    @property
+    def project_name(self) -> NormalizedName:
+        return self._candidate.project_name
+
+    @property
+    def name(self) -> str:
+        return self._candidate.name
+
+    def format_for_error(self) -> str:
+        return str(self)
+
+    def get_candidate_lookup(self) -> CandidateLookup:
+        return self._candidate_lookup
+
+    def is_satisfied_by(self, candidate: Candidate) -> bool:
+        assert candidate.name == self._candidate.name, "Not Python candidate"
+        # We can safely always allow prereleases here since PackageFinder
+        # already implements the prerelease logic, and would have filtered out
+        # prerelease candidates if the user does not expect them.
+        return self.specifier.contains(candidate.version, prereleases=True)
+
+
+class UnsatisfiableRequirement(Requirement):
+    """A requirement that cannot be satisfied."""
+
+    def __init__(self, name: NormalizedName) -> None:
+        self._name = name
+
+    def __str__(self) -> str:
+        return f"{self._name} (unavailable)"
+
+    def __repr__(self) -> str:
+        return f"{self.__class__.__name__}({str(self._name)!r})"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, UnsatisfiableRequirement):
+            return NotImplemented
+        return self._name == other._name
+
+    def __hash__(self) -> int:
+        return hash(self._name)
+
+    @property
+    def project_name(self) -> NormalizedName:
+        return self._name
+
+    @property
+    def name(self) -> str:
+        return self._name
+
+    def format_for_error(self) -> str:
+        return str(self)
+
+    def get_candidate_lookup(self) -> CandidateLookup:
+        return None, None
+
+    def is_satisfied_by(self, candidate: Candidate) -> bool:
+        return False
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/resolver.py b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/resolver.py
new file mode 100644
index 0000000..6e49c9e
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/resolver.py
@@ -0,0 +1,341 @@
+from __future__ import annotations
+
+import contextlib
+import functools
+import logging
+import os
+from typing import TYPE_CHECKING, cast
+
+from pip._vendor.packaging.utils import canonicalize_name
+from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible, ResolutionTooDeep
+from pip._vendor.resolvelib import Resolver as RLResolver
+from pip._vendor.resolvelib.structs import DirectedGraph
+
+from pip._internal.cache import WheelCache
+from pip._internal.exceptions import ResolutionTooDeepError
+from pip._internal.index.package_finder import PackageFinder
+from pip._internal.operations.prepare import RequirementPreparer
+from pip._internal.req.constructors import install_req_extend_extras
+from pip._internal.req.req_install import InstallRequirement
+from pip._internal.req.req_set import RequirementSet
+from pip._internal.resolution.base import BaseResolver, InstallRequirementProvider
+from pip._internal.resolution.resolvelib.provider import PipProvider
+from pip._internal.resolution.resolvelib.reporter import (
+    PipDebuggingReporter,
+    PipReporter,
+)
+from pip._internal.utils.packaging import get_requirement
+
+from .base import Candidate, Requirement
+from .factory import Factory
+
+if TYPE_CHECKING:
+    from pip._vendor.resolvelib.resolvers import Result as RLResult
+
+    Result = RLResult[Requirement, Candidate, str]
+
+
+logger = logging.getLogger(__name__)
+
+
+class Resolver(BaseResolver):
+    _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"}
+
+    def __init__(
+        self,
+        preparer: RequirementPreparer,
+        finder: PackageFinder,
+        wheel_cache: WheelCache | None,
+        make_install_req: InstallRequirementProvider,
+        use_user_site: bool,
+        ignore_dependencies: bool,
+        only_dependencies: bool,
+        ignore_installed: bool,
+        ignore_requires_python: bool,
+        force_reinstall: bool,
+        upgrade_strategy: str,
+        py_version_info: tuple[int, ...] | None = None,
+    ):
+        super().__init__()
+        assert upgrade_strategy in self._allowed_strategies
+        assert not (ignore_dependencies and only_dependencies)
+
+        self.factory = Factory(
+            finder=finder,
+            preparer=preparer,
+            make_install_req=make_install_req,
+            wheel_cache=wheel_cache,
+            use_user_site=use_user_site,
+            force_reinstall=force_reinstall,
+            ignore_installed=ignore_installed,
+            ignore_requires_python=ignore_requires_python,
+            py_version_info=py_version_info,
+        )
+        self.ignore_dependencies = ignore_dependencies
+        self.only_dependencies = only_dependencies
+        self.upgrade_strategy = upgrade_strategy
+        self._result: Result | None = None
+
+    def resolve(
+        self, root_reqs: list[InstallRequirement], check_supported_wheels: bool
+    ) -> RequirementSet:
+        collected = self.factory.collect_root_requirements(root_reqs)
+        provider = PipProvider(
+            factory=self.factory,
+            constraints=collected.constraints,
+            ignore_dependencies=self.ignore_dependencies,
+            only_dependencies=self.only_dependencies,
+            upgrade_strategy=self.upgrade_strategy,
+            user_requested=collected.user_requested,
+        )
+        if "PIP_RESOLVER_DEBUG" in os.environ:
+            reporter: BaseReporter[Requirement, Candidate, str] = PipDebuggingReporter()
+        else:
+            reporter = PipReporter(constraints=provider.constraints)
+
+        resolver: RLResolver[Requirement, Candidate, str] = RLResolver(
+            provider,
+            reporter,
+        )
+
+        try:
+            limit_how_complex_resolution_can_be = 200000
+            result = self._result = resolver.resolve(
+                collected.requirements, max_rounds=limit_how_complex_resolution_can_be
+            )
+
+        except ResolutionImpossible as e:
+            error = self.factory.get_installation_error(
+                cast("ResolutionImpossible[Requirement, Candidate]", e),
+                collected.constraints,
+            )
+            raise error from e
+        except ResolutionTooDeep:
+            raise ResolutionTooDeepError from None
+
+        req_set = RequirementSet(check_supported_wheels=check_supported_wheels)
+        # process candidates with extras last to ensure their base equivalent is
+        # already in the req_set if appropriate.
+        # Python's sort is stable so using a binary key function keeps relative order
+        # within both subsets.
+        for candidate in sorted(
+            result.mapping.values(), key=lambda c: c.name != c.project_name
+        ):
+            ireq = candidate.get_install_requirement()
+            if ireq is None:
+                if candidate.name != candidate.project_name:
+                    # extend existing req's extras
+                    with contextlib.suppress(KeyError):
+                        req = req_set.get_requirement(candidate.project_name)
+                        req_set.add_named_requirement(
+                            install_req_extend_extras(
+                                req, get_requirement(candidate.name).extras
+                            )
+                        )
+                continue
+
+            # Check if there is already an installation under the same name,
+            # and set a flag for later stages to uninstall it, if needed.
+            installed_dist = self.factory.get_dist_to_uninstall(candidate)
+            if installed_dist is None:
+                # There is no existing installation -- nothing to uninstall.
+                ireq.should_reinstall = False
+            elif self.factory.force_reinstall:
+                # The --force-reinstall flag is set -- reinstall.
+                ireq.should_reinstall = True
+            elif installed_dist.version != candidate.version:
+                # The installation is different in version -- reinstall.
+                ireq.should_reinstall = True
+            elif candidate.is_editable or installed_dist.editable:
+                # The incoming distribution is editable, or different in
+                # editable-ness to installation -- reinstall.
+                ireq.should_reinstall = True
+            elif candidate.source_link and candidate.source_link.is_file:
+                # The incoming distribution is under file://
+                if candidate.source_link.is_wheel:
+                    # is a local wheel -- do nothing.
+                    logger.info(
+                        "%s is already installed with the same version as the "
+                        "provided wheel. Use --force-reinstall to force an "
+                        "installation of the wheel.",
+                        ireq.name,
+                    )
+                    continue
+
+                # is a local sdist or path -- reinstall
+                ireq.should_reinstall = True
+            else:
+                continue
+
+            link = candidate.source_link
+            if link and link.is_yanked:
+                # The reason can contain non-ASCII characters, Unicode
+                # is required for Python 2.
+                msg = (
+                    "The candidate selected for download or install is a "
+                    "yanked version: {name!r} candidate (version {version} "
+                    "at {link})\nReason for being yanked: {reason}"
+                ).format(
+                    name=candidate.name,
+                    version=candidate.version,
+                    link=link,
+                    reason=link.yanked_reason or "",
+                )
+                logger.warning(msg)
+
+            req_set.add_named_requirement(ireq)
+
+        if self.only_dependencies:
+            for requested in collected.user_requested:
+                project_name = requested.partition("[")[0]
+                req_set.requirements.pop(project_name, None)
+
+        return req_set
+
+    def get_installation_order(
+        self, req_set: RequirementSet
+    ) -> list[InstallRequirement]:
+        """Get order for installation of requirements in RequirementSet.
+
+        The returned list contains a requirement before another that depends on
+        it. This helps ensure that the environment is kept consistent as they
+        get installed one-by-one.
+
+        The current implementation creates a topological ordering of the
+        dependency graph, giving more weight to packages with less
+        or no dependencies, while breaking any cycles in the graph at
+        arbitrary points. We make no guarantees about where the cycle
+        would be broken, other than it *would* be broken.
+        """
+        assert self._result is not None, "must call resolve() first"
+
+        if not req_set.requirements:
+            # Nothing is left to install, so we do not need an order.
+            return []
+
+        graph = self._result.graph
+        weights = get_topological_weights(graph, set(req_set.requirements.keys()))
+
+        sorted_items = sorted(
+            req_set.requirements.items(),
+            key=functools.partial(_req_set_item_sorter, weights=weights),
+            reverse=True,
+        )
+        return [ireq for _, ireq in sorted_items]
+
+
+def get_topological_weights(
+    graph: DirectedGraph[str | None], requirement_keys: set[str]
+) -> dict[str | None, int]:
+    """Assign weights to each node based on how "deep" they are.
+
+    This implementation may change at any point in the future without prior
+    notice.
+
+    We first simplify the dependency graph by pruning any leaves and giving them
+    the highest weight: a package without any dependencies should be installed
+    first. This is done again and again in the same way, giving ever less weight
+    to the newly found leaves. The loop stops when no leaves are left: all
+    remaining packages have at least one dependency left in the graph.
+
+    Then we continue with the remaining graph, by taking the length for the
+    longest path to any node from root, ignoring any paths that contain a single
+    node twice (i.e. cycles). This is done through a depth-first search through
+    the graph, while keeping track of the path to the node.
+
+    Cycles in the graph result would result in node being revisited while also
+    being on its own path. In this case, take no action. This helps ensure we
+    don't get stuck in a cycle.
+
+    When assigning weight, the longer path (i.e. larger length) is preferred.
+
+    We are only interested in the weights of packages that are in the
+    requirement_keys.
+    """
+    path: set[str | None] = set()
+    weights: dict[str | None, list[int]] = {}
+
+    def visit(node: str | None) -> None:
+        if node in path:
+            # We hit a cycle, so we'll break it here.
+            return
+
+        # The walk is exponential and for pathologically connected graphs (which
+        # are the ones most likely to contain cycles in the first place) it can
+        # take until the heat-death of the universe. To counter this we limit
+        # the number of attempts to visit (i.e. traverse through) any given
+        # node. We choose a value here which gives decent enough coverage for
+        # fairly well behaved graphs, and still limits the walk complexity to be
+        # linear in nature.
+        cur_weights = weights.get(node, [])
+        if len(cur_weights) >= 5:
+            return
+
+        # Time to visit the children!
+        path.add(node)
+        for child in graph.iter_children(node):
+            visit(child)
+        path.remove(node)
+
+        if node not in requirement_keys:
+            return
+
+        cur_weights.append(len(path))
+        weights[node] = cur_weights
+
+    # Simplify the graph, pruning leaves that have no dependencies. This is
+    # needed for large graphs (say over 200 packages) because the `visit`
+    # function is slower for large/densely connected graphs, taking minutes.
+    # See https://github.com/pypa/pip/issues/10557
+    # We repeat the pruning step until we have no more leaves to remove.
+    while True:
+        leaves = set()
+        for key in graph:
+            if key is None:
+                continue
+            for _child in graph.iter_children(key):
+                # This means we have at least one child
+                break
+            else:
+                # No child.
+                leaves.add(key)
+        if not leaves:
+            # We are done simplifying.
+            break
+        # Calculate the weight for the leaves.
+        weight = len(graph) - 1
+        for leaf in leaves:
+            if leaf not in requirement_keys:
+                continue
+            weights[leaf] = [weight]
+        # Remove the leaves from the graph, making it simpler.
+        for leaf in leaves:
+            graph.remove(leaf)
+
+    # Visit the remaining graph, this will only have nodes to handle if the
+    # graph had a cycle in it, which the pruning step above could not handle.
+    # `None` is guaranteed to be the root node by resolvelib.
+    visit(None)
+
+    # Sanity check: all requirement keys should be in the weights,
+    # and no other keys should be in the weights.
+    difference = set(weights.keys()).difference(requirement_keys)
+    assert not difference, difference
+
+    # Now give back all the weights, choosing the largest ones from what we
+    # accumulated.
+    return {node: max(wgts) for (node, wgts) in weights.items()}
+
+
+def _req_set_item_sorter(
+    item: tuple[str, InstallRequirement],
+    weights: dict[str | None, int],
+) -> tuple[int, str]:
+    """Key function used to sort install requirements for installation.
+
+    Based on the "weight" mapping calculated in ``get_installation_order()``.
+    The canonical package name is returned as the second member as a tie-
+    breaker to ensure the result is predictable, which is useful in tests.
+    """
+    name = canonicalize_name(item[0])
+    return weights[name], name
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/self_outdated_check.py b/venv/lib/python3.12/site-packages/pip/_internal/self_outdated_check.py
new file mode 100644
index 0000000..4b5248e
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/self_outdated_check.py
@@ -0,0 +1,246 @@
+from __future__ import annotations
+
+import datetime
+import hashlib
+import json
+import logging
+import optparse
+import os.path
+import sys
+from dataclasses import dataclass
+
+from pip._vendor.packaging.version import Version
+from pip._vendor.packaging.version import parse as parse_version
+from pip._vendor.rich.console import Group
+from pip._vendor.rich.markup import escape
+from pip._vendor.rich.text import Text
+
+from pip._internal.index.collector import LinkCollector
+from pip._internal.index.package_finder import PackageFinder
+from pip._internal.metadata import get_default_environment
+from pip._internal.models.release_control import ReleaseControl
+from pip._internal.models.selection_prefs import SelectionPreferences
+from pip._internal.network.session import PipSession
+from pip._internal.utils.compat import WINDOWS
+from pip._internal.utils.datetime import parse_iso_datetime
+from pip._internal.utils.entrypoints import (
+    get_best_invocation_for_this_pip,
+    get_best_invocation_for_this_python,
+)
+from pip._internal.utils.filesystem import (
+    adjacent_tmp_file,
+    check_path_owner,
+    copy_directory_permissions,
+    replace,
+)
+from pip._internal.utils.misc import (
+    ExternallyManagedEnvironment,
+    check_externally_managed,
+    ensure_dir,
+)
+
+_WEEK = datetime.timedelta(days=7)
+
+logger = logging.getLogger(__name__)
+
+
+def _get_statefile_name(key: str) -> str:
+    key_bytes = key.encode()
+    name = hashlib.sha224(key_bytes).hexdigest()
+    return name
+
+
+class SelfCheckState:
+    def __init__(self, cache_dir: str) -> None:
+        self._state: dict[str, str] = {}
+        self._statefile_path = None
+
+        # Try to load the existing state
+        if cache_dir:
+            self._statefile_path = os.path.join(
+                cache_dir, "selfcheck", _get_statefile_name(self.key)
+            )
+            try:
+                with open(self._statefile_path, encoding="utf-8") as statefile:
+                    self._state = json.load(statefile)
+            except (OSError, ValueError, KeyError):
+                # Explicitly suppressing exceptions, since we don't want to
+                # error out if the cache file is invalid.
+                pass
+
+    @property
+    def key(self) -> str:
+        return sys.prefix
+
+    def get(self, current_time: datetime.datetime) -> str | None:
+        """Check if we have a not-outdated version loaded already."""
+        if not self._state:
+            return None
+
+        if "last_check" not in self._state:
+            return None
+
+        if "pypi_version" not in self._state:
+            return None
+
+        # Determine if we need to refresh the state
+        last_check = parse_iso_datetime(self._state["last_check"])
+        time_since_last_check = current_time - last_check
+        if time_since_last_check > _WEEK:
+            return None
+
+        return self._state["pypi_version"]
+
+    def set(self, pypi_version: str, current_time: datetime.datetime) -> None:
+        # If we do not have a path to cache in, don't bother saving.
+        if not self._statefile_path:
+            return
+
+        statefile_directory = os.path.dirname(self._statefile_path)
+
+        # Check to make sure that we own the directory
+        if not check_path_owner(statefile_directory):
+            return
+
+        # Now that we've ensured the directory is owned by this user, we'll go
+        # ahead and make sure that all our directories are created.
+        ensure_dir(statefile_directory)
+
+        state = {
+            # Include the key so it's easy to tell which pip wrote the
+            # file.
+            "key": self.key,
+            "last_check": current_time.isoformat(),
+            "pypi_version": pypi_version,
+        }
+
+        text = json.dumps(state, sort_keys=True, separators=(",", ":"))
+
+        with adjacent_tmp_file(self._statefile_path) as f:
+            f.write(text.encode())
+            copy_directory_permissions(statefile_directory, f)
+
+        try:
+            # Since we have a prefix-specific state file, we can just
+            # overwrite whatever is there, no need to check.
+            replace(f.name, self._statefile_path)
+        except OSError:
+            # Best effort.
+            pass
+
+
+@dataclass
+class UpgradePrompt:
+    old: str
+    new: str
+
+    def __rich__(self) -> Group:
+        if WINDOWS:
+            pip_cmd = f"{get_best_invocation_for_this_python()} -m pip"
+        else:
+            pip_cmd = get_best_invocation_for_this_pip()
+
+        notice = "[bold][[reset][blue]notice[reset][bold]][reset]"
+        return Group(
+            Text(),
+            Text.from_markup(
+                f"{notice} A new release of pip is available: "
+                f"[red]{self.old}[reset] -> [green]{self.new}[reset]"
+            ),
+            Text.from_markup(
+                f"{notice} To update, run: "
+                f"[green]{escape(pip_cmd)} install --upgrade pip"
+            ),
+        )
+
+
+def _get_current_remote_pip_version(
+    session: PipSession, options: optparse.Values
+) -> str | None:
+    # Lets use PackageFinder to see what the latest pip version is
+    link_collector = LinkCollector.create(
+        session,
+        options=options,
+        suppress_no_index=True,
+    )
+
+    # Pass allow_yanked=False so we don't suggest upgrading to a
+    # yanked version.
+    selection_prefs = SelectionPreferences(
+        allow_yanked=False,
+        release_control=ReleaseControl(only_final={"pip"}),
+    )
+
+    finder = PackageFinder.create(
+        link_collector=link_collector,
+        selection_prefs=selection_prefs,
+    )
+    best_candidate = finder.find_best_candidate("pip").best_candidate
+    if best_candidate is None:
+        return None
+
+    return str(best_candidate.version)
+
+
+def _compute_upgrade_prompt(
+    local_version: Version, remote_version_str: str, installed_by_pip: bool
+) -> UpgradePrompt | None:
+    remote_version = parse_version(remote_version_str)
+    logger.debug("Remote version of pip: %s", remote_version)
+    logger.debug("Local version of pip:  %s", local_version)
+    logger.debug("Was pip installed by pip? %s", installed_by_pip)
+
+    if not installed_by_pip:
+        return None  # Only suggest upgrade if pip is installed by pip.
+
+    local_version_is_older = (
+        local_version < remote_version
+        and local_version.base_version != remote_version.base_version
+    )
+    if local_version_is_older:
+        return UpgradePrompt(old=str(local_version), new=remote_version_str)
+
+    return None
+
+
+def pip_self_version_check_fetch(
+    session: PipSession, options: optparse.Values
+) -> UpgradePrompt | None:
+    """Compute the pip upgrade prompt, if any, before the command runs.
+
+    Limit the frequency of checks to once per week. State is stored either in
+    the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix
+    of the pip script path.
+
+    Pair with :func:`pip_self_version_check_emit`, which displays the prompt
+    after the command body runs.
+    """
+    installed_dist = get_default_environment().get_distribution("pip")
+    if not installed_dist:
+        return None
+    try:
+        check_externally_managed()
+    except ExternallyManagedEnvironment:
+        return None
+
+    state = SelfCheckState(cache_dir=options.cache_dir)
+    current_time = datetime.datetime.now(datetime.timezone.utc)
+    remote_version_str = state.get(current_time)
+    if remote_version_str is None:
+        remote_version_str = _get_current_remote_pip_version(session, options)
+        if remote_version_str is None:
+            logger.debug("No remote pip version found")
+            return None
+        state.set(remote_version_str, current_time)
+
+    return _compute_upgrade_prompt(
+        local_version=installed_dist.version,
+        remote_version_str=remote_version_str,
+        installed_by_pip=installed_dist.installer == "pip",
+    )
+
+
+def pip_self_version_check_emit(upgrade_prompt: UpgradePrompt | None) -> None:
+    """Emit the upgrade prompt captured by :func:`pip_self_version_check_fetch`."""
+    if upgrade_prompt is not None:
+        logger.warning("%s", upgrade_prompt, extra={"rich": True})
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..4bb9b97
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_jaraco_text.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_jaraco_text.cpython-312.pyc
new file mode 100644
index 0000000..0fe7a8f
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_jaraco_text.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_log.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_log.cpython-312.pyc
new file mode 100644
index 0000000..a43f76e
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_log.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-312.pyc
new file mode 100644
index 0000000..7e7567f
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compat.cpython-312.pyc
new file mode 100644
index 0000000..98fe91c
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compat.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-312.pyc
new file mode 100644
index 0000000..24b74f6
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-312.pyc
new file mode 100644
index 0000000..4bc774d
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-312.pyc
new file mode 100644
index 0000000..3ebdf92
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-312.pyc
new file mode 100644
index 0000000..43c3b6f
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/egg_link.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/egg_link.cpython-312.pyc
new file mode 100644
index 0000000..392fcb0
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/egg_link.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-312.pyc
new file mode 100644
index 0000000..894adb6
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-312.pyc
new file mode 100644
index 0000000..a2dc1de
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-312.pyc
new file mode 100644
index 0000000..b73a9bc
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-312.pyc
new file mode 100644
index 0000000..9e64154
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-312.pyc
new file mode 100644
index 0000000..a23d654
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/logging.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/logging.cpython-312.pyc
new file mode 100644
index 0000000..841e008
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/logging.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/misc.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/misc.cpython-312.pyc
new file mode 100644
index 0000000..237b637
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/misc.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-312.pyc
new file mode 100644
index 0000000..f715e32
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/pylock.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/pylock.cpython-312.pyc
new file mode 100644
index 0000000..f8f11d1
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/pylock.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/retry.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/retry.cpython-312.pyc
new file mode 100644
index 0000000..7edf42f
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/retry.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-312.pyc
new file mode 100644
index 0000000..eadfc9e
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-312.pyc
new file mode 100644
index 0000000..e464bde
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-312.pyc
new file mode 100644
index 0000000..7ec261c
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/urls.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/urls.cpython-312.pyc
new file mode 100644
index 0000000..394a349
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/urls.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-312.pyc
new file mode 100644
index 0000000..74248fe
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-312.pyc
new file mode 100644
index 0000000..9aa3e9f
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/_jaraco_text.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/_jaraco_text.py
new file mode 100644
index 0000000..6ccf53b
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/_jaraco_text.py
@@ -0,0 +1,109 @@
+"""Functions brought over from jaraco.text.
+
+These functions are not supposed to be used within `pip._internal`. These are
+helper functions brought over from `jaraco.text` to enable vendoring newer
+copies of `pkg_resources` without having to vendor `jaraco.text` and its entire
+dependency cone; something that our vendoring setup is not currently capable of
+handling.
+
+License reproduced from original source below:
+
+Copyright Jason R. Coombs
+
+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.
+"""
+
+import functools
+import itertools
+
+
+def _nonblank(str):
+    return str and not str.startswith("#")
+
+
+@functools.singledispatch
+def yield_lines(iterable):
+    r"""
+    Yield valid lines of a string or iterable.
+
+    >>> list(yield_lines(''))
+    []
+    >>> list(yield_lines(['foo', 'bar']))
+    ['foo', 'bar']
+    >>> list(yield_lines('foo\nbar'))
+    ['foo', 'bar']
+    >>> list(yield_lines('\nfoo\n#bar\nbaz #comment'))
+    ['foo', 'baz #comment']
+    >>> list(yield_lines(['foo\nbar', 'baz', 'bing\n\n\n']))
+    ['foo', 'bar', 'baz', 'bing']
+    """
+    return itertools.chain.from_iterable(map(yield_lines, iterable))
+
+
+@yield_lines.register(str)
+def _(text):
+    return filter(_nonblank, map(str.strip, text.splitlines()))
+
+
+def drop_comment(line):
+    """
+    Drop comments.
+
+    >>> drop_comment('foo # bar')
+    'foo'
+
+    A hash without a space may be in a URL.
+
+    >>> drop_comment('http://example.com/foo#bar')
+    'http://example.com/foo#bar'
+    """
+    return line.partition(" #")[0]
+
+
+def join_continuation(lines):
+    r"""
+    Join lines continued by a trailing backslash.
+
+    >>> list(join_continuation(['foo \\', 'bar', 'baz']))
+    ['foobar', 'baz']
+    >>> list(join_continuation(['foo \\', 'bar', 'baz']))
+    ['foobar', 'baz']
+    >>> list(join_continuation(['foo \\', 'bar \\', 'baz']))
+    ['foobarbaz']
+
+    Not sure why, but...
+    The character preceding the backslash is also elided.
+
+    >>> list(join_continuation(['goo\\', 'dly']))
+    ['godly']
+
+    A terrible idea, but...
+    If no line is available to continue, suppress the lines.
+
+    >>> list(join_continuation(['foo', 'bar\\', 'baz\\']))
+    ['foo']
+    """
+    lines = iter(lines)
+    for item in lines:
+        while item.endswith("\\"):
+            try:
+                item = item[:-2].strip() + next(lines)
+            except StopIteration:
+                return
+        yield item
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/_log.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/_log.py
new file mode 100644
index 0000000..92c4c6a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/_log.py
@@ -0,0 +1,38 @@
+"""Customize logging
+
+Defines custom logger class for the `logger.verbose(...)` method.
+
+init_logging() must be called before any other modules that call logging.getLogger.
+"""
+
+import logging
+from typing import Any, cast
+
+# custom log level for `--verbose` output
+# between DEBUG and INFO
+VERBOSE = 15
+
+
+class VerboseLogger(logging.Logger):
+    """Custom Logger, defining a verbose log-level
+
+    VERBOSE is between INFO and DEBUG.
+    """
+
+    def verbose(self, msg: str, *args: Any, **kwargs: Any) -> None:
+        return self.log(VERBOSE, msg, *args, **kwargs)
+
+
+def getLogger(name: str) -> VerboseLogger:
+    """logging.getLogger, but ensures our VerboseLogger class is returned"""
+    return cast(VerboseLogger, logging.getLogger(name))
+
+
+def init_logging() -> None:
+    """Register our VerboseLogger and VERBOSE log level.
+
+    Should be called before any calls to getLogger(),
+    i.e. in pip._internal.__init__
+    """
+    logging.setLoggerClass(VerboseLogger)
+    logging.addLevelName(VERBOSE, "VERBOSE")
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/appdirs.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/appdirs.py
new file mode 100644
index 0000000..4152528
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/appdirs.py
@@ -0,0 +1,52 @@
+"""
+This code wraps the vendored appdirs module to so the return values are
+compatible for the current pip code base.
+
+The intention is to rewrite current usages gradually, keeping the tests pass,
+and eventually drop this after all usages are changed.
+"""
+
+import os
+import sys
+
+from pip._vendor import platformdirs as _appdirs
+
+
+def user_cache_dir(appname: str) -> str:
+    return _appdirs.user_cache_dir(appname, appauthor=False)
+
+
+def _macos_user_config_dir(appname: str, roaming: bool = True) -> str:
+    # Use ~/Application Support/pip, if the directory exists.
+    path = _appdirs.user_data_dir(appname, appauthor=False, roaming=roaming)
+    if os.path.isdir(path):
+        return path
+
+    # Use a Linux-like ~/.config/pip, by default.
+    linux_like_path = "~/.config/"
+    if appname:
+        linux_like_path = os.path.join(linux_like_path, appname)
+
+    return os.path.expanduser(linux_like_path)
+
+
+def user_config_dir(appname: str, roaming: bool = True) -> str:
+    if sys.platform == "darwin":
+        return _macos_user_config_dir(appname, roaming)
+
+    return _appdirs.user_config_dir(appname, appauthor=False, roaming=roaming)
+
+
+# for the discussion regarding site_config_dir locations
+# see 
+def site_config_dirs(appname: str) -> list[str]:
+    if sys.platform == "darwin":
+        dirval = _appdirs.site_data_dir(appname, appauthor=False, multipath=True)
+        return dirval.split(os.pathsep)
+
+    dirval = _appdirs.site_config_dir(appname, appauthor=False, multipath=True)
+    if sys.platform == "win32":
+        return [dirval]
+
+    # Unix-y system. Look in /etc as well.
+    return dirval.split(os.pathsep) + ["/etc"]
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/compat.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/compat.py
new file mode 100644
index 0000000..b3f70cf
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/compat.py
@@ -0,0 +1,91 @@
+"""Stuff that differs in different Python versions and platform
+distributions."""
+
+import importlib.resources
+import locale
+import logging
+import os
+import sys
+from typing import IO
+
+__all__ = ["get_locale_encoding", "get_path_uid", "tomllib", "WINDOWS"]
+
+
+logger = logging.getLogger(__name__)
+
+
+def has_tls() -> bool:
+    try:
+        import _ssl  # noqa: F401  # ignore unused
+
+        return True
+    except ImportError:
+        pass
+
+    from pip._vendor.urllib3.util import IS_PYOPENSSL
+
+    return IS_PYOPENSSL
+
+
+def get_locale_encoding() -> str:
+    """Return the locale encoding.
+
+    Uses ``locale.getencoding()`` when available (Python 3.11+) to avoid the
+    ``EncodingWarning`` that ``locale.getpreferredencoding(False)`` raises
+    under UTF-8 Mode.
+    """
+    # TODO: Remove the 3.10 fallback once pip drops Python 3.10 support.
+    if sys.version_info >= (3, 11):
+        return locale.getencoding()
+    return locale.getpreferredencoding(False)
+
+
+def get_path_uid(path: str) -> int:
+    """
+    Return path's uid.
+
+    Does not follow symlinks:
+        https://github.com/pypa/pip/pull/935#discussion_r5307003
+
+    Placed this function in compat due to differences on AIX and
+    Jython, that should eventually go away.
+
+    :raises OSError: When path is a symlink or can't be read.
+    """
+    if hasattr(os, "O_NOFOLLOW"):
+        fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
+        file_uid = os.fstat(fd).st_uid
+        os.close(fd)
+    else:  # AIX and Jython
+        # WARNING: time of check vulnerability, but best we can do w/o NOFOLLOW
+        if not os.path.islink(path):
+            # older versions of Jython don't have `os.fstat`
+            file_uid = os.stat(path).st_uid
+        else:
+            # raise OSError for parity with os.O_NOFOLLOW above
+            raise OSError(f"{path} is a symlink; Will not return uid for symlinks")
+    return file_uid
+
+
+# The importlib.resources.open_text function was deprecated in 3.11 with suggested
+# replacement we use below.
+if sys.version_info < (3, 11):
+    open_text_resource = importlib.resources.open_text
+else:
+
+    def open_text_resource(
+        package: str, resource: str, encoding: str = "utf-8", errors: str = "strict"
+    ) -> IO[str]:
+        return (importlib.resources.files(package) / resource).open(
+            "r", encoding=encoding, errors=errors
+        )
+
+
+if sys.version_info >= (3, 11):
+    import tomllib
+else:
+    from pip._vendor import tomli as tomllib
+
+
+# windows detection, covers cpython and ironpython
+WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt")
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/compatibility_tags.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/compatibility_tags.py
new file mode 100644
index 0000000..6d98171
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/compatibility_tags.py
@@ -0,0 +1,201 @@
+"""Generate and work with PEP 425 Compatibility Tags."""
+
+from __future__ import annotations
+
+import re
+
+from pip._vendor.packaging.tags import (
+    PythonVersion,
+    Tag,
+    android_platforms,
+    compatible_tags,
+    cpython_tags,
+    generic_tags,
+    interpreter_name,
+    interpreter_version,
+    ios_platforms,
+    mac_platforms,
+)
+
+_apple_arch_pat = re.compile(r"(.+)_(\d+)_(\d+)_(.+)")
+
+
+def version_info_to_nodot(version_info: tuple[int, ...]) -> str:
+    # Only use up to the first two numbers.
+    return "".join(map(str, version_info[:2]))
+
+
+def _mac_platforms(arch: str) -> list[str]:
+    match = _apple_arch_pat.match(arch)
+    if match:
+        name, major, minor, actual_arch = match.groups()
+        mac_version = (int(major), int(minor))
+        arches = [
+            # Since we have always only checked that the platform starts
+            # with "macosx", for backwards-compatibility we extract the
+            # actual prefix provided by the user in case they provided
+            # something like "macosxcustom_". It may be good to remove
+            # this as undocumented or deprecate it in the future.
+            "{}_{}".format(name, arch[len("macosx_") :])
+            for arch in mac_platforms(mac_version, actual_arch)
+        ]
+    else:
+        # arch pattern didn't match (?!)
+        arches = [arch]
+    return arches
+
+
+def _ios_platforms(arch: str) -> list[str]:
+    match = _apple_arch_pat.match(arch)
+    if match:
+        name, major, minor, actual_multiarch = match.groups()
+        ios_version = (int(major), int(minor))
+        arches = [
+            # Since we have always only checked that the platform starts
+            # with "ios", for backwards-compatibility we extract the
+            # actual prefix provided by the user in case they provided
+            # something like "ioscustom_". It may be good to remove
+            # this as undocumented or deprecate it in the future.
+            "{}_{}".format(name, arch[len("ios_") :])
+            for arch in ios_platforms(ios_version, actual_multiarch)
+        ]
+    else:
+        # arch pattern didn't match (?!)
+        arches = [arch]
+    return arches
+
+
+def _android_platforms(arch: str) -> list[str]:
+    match = re.fullmatch(r"android_(\d+)_(.+)", arch)
+    if match:
+        api_level, abi = match.groups()
+        return list(android_platforms(int(api_level), abi))
+    else:
+        # arch pattern didn't match (?!)
+        return [arch]
+
+
+def _custom_manylinux_platforms(arch: str) -> list[str]:
+    arches = [arch]
+    arch_prefix, arch_sep, arch_suffix = arch.partition("_")
+    if arch_prefix == "manylinux2014":
+        # manylinux1/manylinux2010 wheels run on most manylinux2014 systems
+        # with the exception of wheels depending on ncurses. PEP 599 states
+        # manylinux1/manylinux2010 wheels should be considered
+        # manylinux2014 wheels:
+        # https://www.python.org/dev/peps/pep-0599/#backwards-compatibility-with-manylinux2010-wheels
+        if arch_suffix in {"i686", "x86_64"}:
+            arches.append("manylinux2010" + arch_sep + arch_suffix)
+            arches.append("manylinux1" + arch_sep + arch_suffix)
+    elif arch_prefix == "manylinux2010":
+        # manylinux1 wheels run on most manylinux2010 systems with the
+        # exception of wheels depending on ncurses. PEP 571 states
+        # manylinux1 wheels should be considered manylinux2010 wheels:
+        # https://www.python.org/dev/peps/pep-0571/#backwards-compatibility-with-manylinux1-wheels
+        arches.append("manylinux1" + arch_sep + arch_suffix)
+    return arches
+
+
+def _get_custom_platforms(arch: str) -> list[str]:
+    arch_prefix, arch_sep, arch_suffix = arch.partition("_")
+    if arch.startswith("macosx"):
+        arches = _mac_platforms(arch)
+    elif arch.startswith("ios"):
+        arches = _ios_platforms(arch)
+    elif arch_prefix == "android":
+        arches = _android_platforms(arch)
+    elif arch_prefix in ["manylinux2014", "manylinux2010"]:
+        arches = _custom_manylinux_platforms(arch)
+    else:
+        arches = [arch]
+    return arches
+
+
+def _expand_allowed_platforms(platforms: list[str] | None) -> list[str] | None:
+    if not platforms:
+        return None
+
+    seen = set()
+    result = []
+
+    for p in platforms:
+        if p in seen:
+            continue
+        additions = [c for c in _get_custom_platforms(p) if c not in seen]
+        seen.update(additions)
+        result.extend(additions)
+
+    return result
+
+
+def _get_python_version(version: str) -> PythonVersion:
+    if len(version) > 1:
+        return int(version[0]), int(version[1:])
+    else:
+        return (int(version[0]),)
+
+
+def _get_custom_interpreter(
+    implementation: str | None = None, version: str | None = None
+) -> str:
+    if implementation is None:
+        implementation = interpreter_name()
+    if version is None:
+        version = interpreter_version()
+    return f"{implementation}{version}"
+
+
+def get_supported(
+    version: str | None = None,
+    platforms: list[str] | None = None,
+    impl: str | None = None,
+    abis: list[str] | None = None,
+) -> list[Tag]:
+    """Return a list of supported tags for each version specified in
+    `versions`.
+
+    :param version: a string version, of the form "33" or "32",
+        or None. The version will be assumed to support our ABI.
+    :param platform: specify a list of platforms you want valid
+        tags for, or None. If None, use the local system platform.
+    :param impl: specify the exact implementation you want valid
+        tags for, or None. If None, use the local interpreter impl.
+    :param abis: specify a list of abis you want valid
+        tags for, or None. If None, use the local interpreter abi.
+    """
+    supported: list[Tag] = []
+
+    python_version: PythonVersion | None = None
+    if version is not None:
+        python_version = _get_python_version(version)
+
+    interpreter = _get_custom_interpreter(impl, version)
+
+    platforms = _expand_allowed_platforms(platforms)
+
+    is_cpython = (impl or interpreter_name()) == "cp"
+    if is_cpython:
+        supported.extend(
+            cpython_tags(
+                python_version=python_version,
+                abis=abis,
+                platforms=platforms,
+            )
+        )
+    else:
+        supported.extend(
+            generic_tags(
+                interpreter=interpreter,
+                abis=abis,
+                platforms=platforms,
+            )
+        )
+    supported.extend(
+        compatible_tags(
+            python_version=python_version,
+            interpreter=interpreter,
+            platforms=platforms,
+        )
+    )
+
+    return supported
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/datetime.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/datetime.py
new file mode 100644
index 0000000..f8870ea
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/datetime.py
@@ -0,0 +1,28 @@
+"""For when pip wants to check the date or time."""
+
+import datetime
+import sys
+
+
+def today_is_later_than(year: int, month: int, day: int) -> bool:
+    today = datetime.date.today()
+    given = datetime.date(year, month, day)
+
+    return today > given
+
+
+def parse_iso_datetime(isodate: str) -> datetime.datetime:
+    """Convert an ISO format string to a datetime.
+
+    Handles the format 2020-01-22T14:24:01Z (trailing Z)
+    which is not supported by older versions of fromisoformat.
+    """
+    # Python 3.11+ supports Z suffix natively in fromisoformat
+    if sys.version_info >= (3, 11):
+        return datetime.datetime.fromisoformat(isodate)
+    else:
+        return datetime.datetime.fromisoformat(
+            isodate.replace("Z", "+00:00")
+            if isodate.endswith("Z") and ("T" in isodate or " " in isodate.strip())
+            else isodate
+        )
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/deprecation.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/deprecation.py
new file mode 100644
index 0000000..c272671
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/deprecation.py
@@ -0,0 +1,144 @@
+"""
+A module that implements tooling to enable easy warnings about deprecations.
+"""
+
+from __future__ import annotations
+
+import logging
+import os
+import warnings
+from typing import Any, TextIO
+
+from pip._vendor.packaging.version import parse
+
+from pip import __version__ as current_version  # NOTE: tests patch this name.
+
+DEPRECATION_MSG_PREFIX = "DEPRECATION: "
+
+
+class PipDeprecationWarning(Warning):
+    include_source: bool = False
+
+
+_original_showwarning: Any = None
+
+
+# Warnings <-> Logging Integration
+def _showwarning(
+    message: Warning | str,
+    category: type[Warning],
+    filename: str,
+    lineno: int,
+    file: TextIO | None = None,
+    line: str | None = None,
+) -> None:
+    if file is not None:
+        if _original_showwarning is not None:
+            _original_showwarning(message, category, filename, lineno, file, line)
+    elif issubclass(category, PipDeprecationWarning):
+        # We use a specially named logger which will handle all of the
+        # deprecation messages for pip.
+        logger = logging.getLogger("pip._internal.deprecations")
+        if isinstance(message, PipDeprecationWarning) and message.include_source:
+            logger.warning("%s (%s:%s)", message, filename, lineno)
+        else:
+            logger.warning(message)
+    else:
+        _original_showwarning(message, category, filename, lineno, file, line)
+
+
+def install_warning_logger() -> None:
+    # Enable our Deprecation Warnings
+    # If we're running pip test suite, promote the PipDeprecationWarning into errors.
+    if os.environ.get("_PIP_TEST_ENV", None):
+        warnings.simplefilter("error", PipDeprecationWarning)
+    else:
+        warnings.simplefilter("default", PipDeprecationWarning, append=True)
+
+    global _original_showwarning
+
+    if _original_showwarning is None:
+        _original_showwarning = warnings.showwarning
+        warnings.showwarning = _showwarning
+
+
+def deprecated(
+    *,
+    reason: str,
+    replacement: str | None,
+    gone_in: str | None,
+    feature_flag: str | None = None,
+    issue: int | None = None,
+    stacklevel: int = 2,
+    include_source: bool = False,
+) -> None:
+    """Helper to deprecate existing functionality.
+
+    reason:
+        Textual reason shown to the user about why this functionality has
+        been deprecated. Should be a complete sentence.
+    replacement:
+        Textual suggestion shown to the user about what alternative
+        functionality they can use.
+    gone_in:
+        The version of pip does this functionality should get removed in.
+        Raises an error if pip's current version is greater than or equal to
+        this.
+    feature_flag:
+        Command-line flag of the form --use-feature={feature_flag} for testing
+        upcoming functionality.
+    issue:
+        Issue number on the tracker that would serve as a useful place for
+        users to find related discussion and provide feedback.
+    stacklevel:
+        How many frames up the call stack to attribute the warning to.
+        Defaults to 2 (the caller of deprecated()).
+    include_source:
+        If True, include the source filename and line number in the warning
+        output. Useful when the warning originates from external code.
+    """
+
+    # Determine whether or not the feature is already gone in this version.
+    is_gone = gone_in is not None and parse(current_version) >= parse(gone_in)
+
+    message_parts = [
+        (reason, f"{DEPRECATION_MSG_PREFIX}{{}}"),
+        (
+            gone_in,
+            (
+                "pip {} will enforce this behaviour change."
+                if not is_gone
+                else "Since pip {}, this is no longer supported."
+            ),
+        ),
+        (
+            replacement,
+            "A possible replacement is {}.",
+        ),
+        (
+            feature_flag,
+            (
+                "You can use the flag --use-feature={} to test the upcoming behaviour."
+                if not is_gone
+                else None
+            ),
+        ),
+        (
+            issue,
+            "Discussion can be found at https://github.com/pypa/pip/issues/{}",
+        ),
+    ]
+
+    message = " ".join(
+        format_str.format(value)
+        for value, format_str in message_parts
+        if format_str is not None and value is not None
+    )
+
+    # Raise as an error if this behaviour is deprecated.
+    if is_gone:
+        raise PipDeprecationWarning(message)
+
+    warning = PipDeprecationWarning(message)
+    warning.include_source = include_source
+    warnings.warn(warning, stacklevel=stacklevel)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/direct_url_helpers.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/direct_url_helpers.py
new file mode 100644
index 0000000..f2d6e90
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/direct_url_helpers.py
@@ -0,0 +1,92 @@
+from __future__ import annotations
+
+from pip._internal.models.direct_url import ArchiveInfo, DirectUrl, DirInfo, VcsInfo
+from pip._internal.models.link import Link
+from pip._internal.utils.urls import path_to_url
+from pip._internal.vcs import vcs
+
+
+def direct_url_as_pep440_direct_reference(direct_url: DirectUrl, name: str) -> str:
+    """Convert a DirectUrl to a pip requirement string."""
+    direct_url.validate()  # if invalid, this is a pip bug
+    requirement = name + " @ "
+    fragments = []
+    if direct_url.vcs_info:
+        requirement += (
+            f"{direct_url.vcs_info.vcs}+{direct_url.url}"
+            f"@{direct_url.vcs_info.commit_id}"
+        )
+    elif direct_url.archive_info:
+        requirement += direct_url.url
+        if direct_url.archive_info.hashes:
+            hash_algorithm, hash_value = next(
+                iter(direct_url.archive_info.hashes.items())
+            )
+            fragments.append(f"{hash_algorithm}={hash_value}")
+    else:
+        assert direct_url.dir_info
+        requirement += direct_url.url
+    if direct_url.subdirectory:
+        fragments.append("subdirectory=" + direct_url.subdirectory)
+    if fragments:
+        requirement += "#" + "&".join(fragments)
+    return requirement
+
+
+def direct_url_for_editable(source_dir: str) -> DirectUrl:
+    return DirectUrl(
+        url=path_to_url(source_dir),
+        dir_info=DirInfo(editable=True),
+    )
+
+
+def direct_url_from_link(
+    link: Link, source_dir: str | None = None, link_is_in_wheel_cache: bool = False
+) -> DirectUrl:
+    if link.is_vcs:
+        vcs_backend = vcs.get_backend_for_scheme(link.scheme)
+        assert vcs_backend
+        url, requested_revision, _ = vcs_backend.get_url_rev_and_auth(
+            link.url_without_fragment
+        )
+        # For VCS links, we need to find out and add commit_id.
+        if link_is_in_wheel_cache:
+            # If the requested VCS link corresponds to a cached
+            # wheel, it means the requested revision was an
+            # immutable commit hash, otherwise it would not have
+            # been cached. In that case we don't have a source_dir
+            # with the VCS checkout.
+            assert requested_revision
+            commit_id = requested_revision
+        else:
+            # If the wheel was not in cache, it means we have
+            # had to checkout from VCS to build and we have a source_dir
+            # which we can inspect to find out the commit id.
+            assert source_dir
+            commit_id = vcs_backend.get_revision(source_dir)
+        return DirectUrl(
+            url=url,
+            vcs_info=VcsInfo(
+                vcs=vcs_backend.name,
+                commit_id=commit_id,
+                requested_revision=requested_revision,
+            ),
+            subdirectory=link.subdirectory_fragment,
+        )
+    elif link.is_existing_dir():
+        return DirectUrl(
+            url=link.url_without_fragment,
+            dir_info=DirInfo(),
+            subdirectory=link.subdirectory_fragment,
+        )
+    else:
+        if link.hash_name:
+            assert link.hash
+            hashes = {link.hash_name: link.hash}
+        else:
+            hashes = None
+        return DirectUrl(
+            url=link.url_without_fragment,
+            archive_info=ArchiveInfo(hashes=hashes),
+            subdirectory=link.subdirectory_fragment,
+        )
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/egg_link.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/egg_link.py
new file mode 100644
index 0000000..dc85a58
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/egg_link.py
@@ -0,0 +1,81 @@
+from __future__ import annotations
+
+import os
+import re
+import sys
+
+from pip._internal.locations import site_packages, user_site
+from pip._internal.utils.virtualenv import (
+    running_under_virtualenv,
+    virtualenv_no_global,
+)
+
+__all__ = [
+    "egg_link_path_from_sys_path",
+    "egg_link_path_from_location",
+]
+
+
+def _egg_link_names(raw_name: str) -> list[str]:
+    """
+    Convert a Name metadata value to a .egg-link name, by applying
+    the same substitution as pkg_resources's safe_name function.
+    Note: we cannot use canonicalize_name because it has a different logic.
+
+    We also look for the raw name (without normalization) as setuptools 69 changed
+    the way it names .egg-link files (https://github.com/pypa/setuptools/issues/4167).
+    """
+    return [
+        re.sub("[^A-Za-z0-9.]+", "-", raw_name) + ".egg-link",
+        f"{raw_name}.egg-link",
+    ]
+
+
+def egg_link_path_from_sys_path(raw_name: str) -> str | None:
+    """
+    Look for a .egg-link file for project name, by walking sys.path.
+    """
+    egg_link_names = _egg_link_names(raw_name)
+    for path_item in sys.path:
+        for egg_link_name in egg_link_names:
+            egg_link = os.path.join(path_item, egg_link_name)
+            if os.path.isfile(egg_link):
+                return egg_link
+    return None
+
+
+def egg_link_path_from_location(raw_name: str) -> str | None:
+    """
+    Return the path for the .egg-link file if it exists, otherwise, None.
+
+    There's 3 scenarios:
+    1) not in a virtualenv
+       try to find in site.USER_SITE, then site_packages
+    2) in a no-global virtualenv
+       try to find in site_packages
+    3) in a yes-global virtualenv
+       try to find in site_packages, then site.USER_SITE
+       (don't look in global location)
+
+    For #1 and #3, there could be odd cases, where there's an egg-link in 2
+    locations.
+
+    This method will just return the first one found.
+    """
+    sites: list[str] = []
+    if running_under_virtualenv():
+        sites.append(site_packages)
+        if not virtualenv_no_global() and user_site:
+            sites.append(user_site)
+    else:
+        if user_site:
+            sites.append(user_site)
+        sites.append(site_packages)
+
+    egg_link_names = _egg_link_names(raw_name)
+    for site in sites:
+        for egg_link_name in egg_link_names:
+            egglink = os.path.join(site, egg_link_name)
+            if os.path.isfile(egglink):
+                return egglink
+    return None
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/entrypoints.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/entrypoints.py
new file mode 100644
index 0000000..e3a150e
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/entrypoints.py
@@ -0,0 +1,88 @@
+from __future__ import annotations
+
+import itertools
+import os
+import shutil
+import sys
+
+from pip._internal.cli.main import main
+from pip._internal.utils.compat import WINDOWS
+
+_EXECUTABLE_NAMES = [
+    "pip",
+    f"pip{sys.version_info.major}",
+    f"pip{sys.version_info.major}.{sys.version_info.minor}",
+]
+if WINDOWS:
+    _allowed_extensions = {"", ".exe"}
+    _EXECUTABLE_NAMES = [
+        "".join(parts)
+        for parts in itertools.product(_EXECUTABLE_NAMES, _allowed_extensions)
+    ]
+
+
+def _wrapper(args: list[str] | None = None) -> int:
+    """Central wrapper for all old entrypoints.
+
+    Historically pip has had several entrypoints defined. Because of issues
+    arising from PATH, sys.path, multiple Pythons, their interactions, and most
+    of them having a pip installed, users suffer every time an entrypoint gets
+    moved.
+
+    To alleviate this pain, and provide a mechanism for warning users and
+    directing them to an appropriate place for help, we now define all of
+    our old entrypoints as wrappers for the current one.
+    """
+    sys.stderr.write(
+        "WARNING: pip is being invoked by an old script wrapper. This will "
+        "fail in a future version of pip.\n"
+        "Please see https://github.com/pypa/pip/issues/5599 for advice on "
+        "fixing the underlying issue.\n"
+        "To avoid this problem you can invoke Python with '-m pip' instead of "
+        "running pip directly.\n"
+    )
+    return main(args)
+
+
+def get_best_invocation_for_this_pip() -> str:
+    """Try to figure out the best way to invoke pip in the current environment."""
+    binary_directory = "Scripts" if WINDOWS else "bin"
+    binary_prefix = os.path.join(sys.prefix, binary_directory)
+
+    # Try to use pip[X[.Y]] names, if those executables for this environment are
+    # the first on PATH with that name.
+    path_parts = os.path.normcase(os.environ.get("PATH", "")).split(os.pathsep)
+    exe_are_in_PATH = os.path.normcase(binary_prefix) in path_parts
+    if exe_are_in_PATH:
+        for exe_name in _EXECUTABLE_NAMES:
+            found_executable = shutil.which(exe_name)
+            binary_executable = os.path.join(binary_prefix, exe_name)
+            if (
+                found_executable
+                and os.path.exists(binary_executable)
+                and os.path.samefile(
+                    found_executable,
+                    binary_executable,
+                )
+            ):
+                return exe_name
+
+    # Use the `-m` invocation, if there's no "nice" invocation.
+    return f"{get_best_invocation_for_this_python()} -m pip"
+
+
+def get_best_invocation_for_this_python() -> str:
+    """Try to figure out the best way to invoke the current Python."""
+    exe = sys.executable
+    exe_name = os.path.basename(exe)
+
+    # Try to use the basename, if it's the first executable.
+    found_executable = shutil.which(exe_name)
+    # Virtual environments often symlink to their parent Python binaries, but we don't
+    # want to treat the Python binaries as equivalent when the environment's Python is
+    # not on PATH (not activated). Thus, we don't follow symlinks.
+    if found_executable and os.path.samestat(os.lstat(found_executable), os.lstat(exe)):
+        return exe_name
+
+    # Use the full executable name, because we couldn't find something simpler.
+    return exe
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/filesystem.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/filesystem.py
new file mode 100644
index 0000000..806aae0
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/filesystem.py
@@ -0,0 +1,201 @@
+from __future__ import annotations
+
+import fnmatch
+import os
+import os.path
+import random
+import sys
+from collections.abc import Callable, Generator
+from contextlib import contextmanager
+from pathlib import Path
+from tempfile import NamedTemporaryFile
+from typing import Any, BinaryIO, cast
+
+from pip._internal.utils.compat import get_path_uid
+from pip._internal.utils.misc import format_size
+from pip._internal.utils.retry import retry
+
+
+def check_path_owner(path: str) -> bool:
+    # If we don't have a way to check the effective uid of this process, then
+    # we'll just assume that we own the directory.
+    if sys.platform == "win32" or not hasattr(os, "geteuid"):
+        return True
+
+    assert os.path.isabs(path)
+
+    previous = None
+    while path != previous:
+        if os.path.lexists(path):
+            # Check if path is writable by current user.
+            if os.geteuid() == 0:
+                # Special handling for root user in order to handle properly
+                # cases where users use sudo without -H flag.
+                try:
+                    path_uid = get_path_uid(path)
+                except OSError:
+                    return False
+                return path_uid == 0
+            else:
+                return os.access(path, os.W_OK)
+        else:
+            previous, path = path, os.path.dirname(path)
+    return False  # assume we don't own the path
+
+
+@contextmanager
+def adjacent_tmp_file(path: str, **kwargs: Any) -> Generator[BinaryIO, None, None]:
+    """Return a file-like object pointing to a tmp file next to path.
+
+    The file is created securely and is ensured to be written to disk
+    after the context reaches its end.
+
+    kwargs will be passed to tempfile.NamedTemporaryFile to control
+    the way the temporary file will be opened.
+    """
+    with NamedTemporaryFile(
+        delete=False,
+        dir=os.path.dirname(path),
+        prefix=os.path.basename(path),
+        suffix=".tmp",
+        **kwargs,
+    ) as f:
+        result = cast(BinaryIO, f)
+        try:
+            yield result
+        finally:
+            result.flush()
+            os.fsync(result.fileno())
+
+
+replace = retry(stop_after_delay=1, wait=0.25)(os.replace)
+
+
+# test_writable_dir and _test_writable_dir_win are copied from Flit,
+# with the author's agreement to also place them under pip's license.
+def test_writable_dir(path: str) -> bool:
+    """Check if a directory is writable.
+
+    Uses os.access() on POSIX, tries creating files on Windows.
+    """
+    # If the directory doesn't exist, find the closest parent that does.
+    while not os.path.isdir(path):
+        parent = os.path.dirname(path)
+        if parent == path:
+            break  # Should never get here, but infinite loops are bad
+        path = parent
+
+    if os.name == "posix":
+        return os.access(path, os.W_OK)
+
+    return _test_writable_dir_win(path)
+
+
+def _test_writable_dir_win(path: str) -> bool:
+    # os.access doesn't work on Windows: http://bugs.python.org/issue2528
+    # and we can't use tempfile: http://bugs.python.org/issue22107
+    basename = "accesstest_deleteme_fishfingers_custard_"
+    alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"
+    for _ in range(10):
+        name = basename + "".join(random.choice(alphabet) for _ in range(6))
+        file = os.path.join(path, name)
+        try:
+            fd = os.open(file, os.O_RDWR | os.O_CREAT | os.O_EXCL)
+        except FileExistsError:
+            pass
+        except PermissionError:
+            # This could be because there's a directory with the same name.
+            # But it's highly unlikely there's a directory called that,
+            # so we'll assume it's because the parent dir is not writable.
+            # This could as well be because the parent dir is not readable,
+            # due to non-privileged user access.
+            return False
+        else:
+            os.close(fd)
+            os.unlink(file)
+            return True
+
+    # This should never be reached
+    raise OSError("Unexpected condition testing for writable directory")
+
+
+def find_files(path: str, pattern: str) -> list[str]:
+    """Returns a list of absolute paths of files beneath path, recursively,
+    with filenames which match the UNIX-style shell glob pattern."""
+    result: list[str] = []
+    for root, _, files in os.walk(path):
+        matches = fnmatch.filter(files, pattern)
+        result.extend(os.path.join(root, f) for f in matches)
+    return result
+
+
+def file_size(path: str) -> int | float:
+    # If it's a symlink, return 0.
+    if os.path.islink(path):
+        return 0
+    return os.path.getsize(path)
+
+
+def format_file_size(path: str) -> str:
+    return format_size(file_size(path))
+
+
+def directory_size(path: str) -> int | float:
+    size = 0.0
+    for root, _dirs, files in os.walk(path):
+        for filename in files:
+            file_path = os.path.join(root, filename)
+            size += file_size(file_path)
+    return size
+
+
+def format_directory_size(path: str) -> str:
+    return format_size(directory_size(path))
+
+
+def copy_directory_permissions(directory: str, target_file: BinaryIO) -> None:
+    mode = (
+        os.stat(directory).st_mode & 0o666  # select read/write permissions of directory
+        | 0o600  # set owner read/write permissions
+    )
+    # Change permissions only if there is no risk of following a symlink.
+    if os.chmod in os.supports_fd:
+        os.chmod(target_file.fileno(), mode)
+    elif os.chmod in os.supports_follow_symlinks:
+        os.chmod(target_file.name, mode, follow_symlinks=False)
+
+
+def _subdirs_without_generic(
+    path: str, predicate: Callable[[str, list[str]], bool]
+) -> Generator[Path]:
+    """Yields every subdirectory of +path+ that has no files matching the
+    predicate under it."""
+
+    directories = []
+    excluded: set[Path] = set()
+
+    for root_str, _, filenames in os.walk(Path(path).resolve()):
+        root = Path(root_str)
+        if predicate(root_str, filenames):
+            # This directory should be excluded, so exclude it and all of its
+            # parent directories.
+            # The last item in root.parents is ".", so we ignore it.
+            excluded.update(root.parents[:-1])
+            excluded.add(root)
+        directories.append(root)
+
+    for d in sorted(directories, reverse=True):
+        if d not in excluded:
+            yield d
+
+
+def subdirs_without_files(path: str) -> Generator[Path]:
+    """Yields every subdirectory of +path+ that has no files under it."""
+    return _subdirs_without_generic(path, lambda root, filenames: len(filenames) > 0)
+
+
+def subdirs_without_wheels(path: str) -> Generator[Path]:
+    """Yields every subdirectory of +path+ that has no .whl files under it."""
+    return _subdirs_without_generic(
+        path, lambda root, filenames: any(x.endswith(".whl") for x in filenames)
+    )
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/filetypes.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/filetypes.py
new file mode 100644
index 0000000..2b8baad
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/filetypes.py
@@ -0,0 +1,24 @@
+"""Filetype information."""
+
+from pip._internal.utils.misc import splitext
+
+WHEEL_EXTENSION = ".whl"
+BZ2_EXTENSIONS: tuple[str, ...] = (".tar.bz2", ".tbz")
+XZ_EXTENSIONS: tuple[str, ...] = (
+    ".tar.xz",
+    ".txz",
+    ".tlz",
+    ".tar.lz",
+    ".tar.lzma",
+)
+ZIP_EXTENSIONS: tuple[str, ...] = (".zip", WHEEL_EXTENSION)
+TAR_EXTENSIONS: tuple[str, ...] = (".tar.gz", ".tgz", ".tar")
+ARCHIVE_EXTENSIONS = ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS
+
+
+def is_archive_file(name: str) -> bool:
+    """Return True if `name` is a considered as an archive file."""
+    ext = splitext(name)[1].lower()
+    if ext in ARCHIVE_EXTENSIONS:
+        return True
+    return False
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/glibc.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/glibc.py
new file mode 100644
index 0000000..2cb3013
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/glibc.py
@@ -0,0 +1,102 @@
+from __future__ import annotations
+
+import os
+import sys
+
+
+def glibc_version_string() -> str | None:
+    "Returns glibc version string, or None if not using glibc."
+    return glibc_version_string_confstr() or glibc_version_string_ctypes()
+
+
+def glibc_version_string_confstr() -> str | None:
+    "Primary implementation of glibc_version_string using os.confstr."
+    # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely
+    # to be broken or missing. This strategy is used in the standard library
+    # platform module:
+    # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183
+    if sys.platform == "win32":
+        return None
+    try:
+        gnu_libc_version = os.confstr("CS_GNU_LIBC_VERSION")
+        if gnu_libc_version is None:
+            return None
+        # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17":
+        _, version = gnu_libc_version.split()
+    except (AttributeError, OSError, ValueError):
+        # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)...
+        return None
+    return version
+
+
+def glibc_version_string_ctypes() -> str | None:
+    "Fallback implementation of glibc_version_string using ctypes."
+
+    try:
+        import ctypes
+    except ImportError:
+        return None
+
+    # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen
+    # manpage says, "If filename is NULL, then the returned handle is for the
+    # main program". This way we can let the linker do the work to figure out
+    # which libc our process is actually using.
+    #
+    # We must also handle the special case where the executable is not a
+    # dynamically linked executable. This can occur when using musl libc,
+    # for example. In this situation, dlopen() will error, leading to an
+    # OSError. Interestingly, at least in the case of musl, there is no
+    # errno set on the OSError. The single string argument used to construct
+    # OSError comes from libc itself and is therefore not portable to
+    # hard code here. In any case, failure to call dlopen() means we
+    # can't proceed, so we bail on our attempt.
+    try:
+        process_namespace = ctypes.CDLL(None)
+    except OSError:
+        return None
+
+    try:
+        gnu_get_libc_version = process_namespace.gnu_get_libc_version
+    except AttributeError:
+        # Symbol doesn't exist -> therefore, we are not linked to
+        # glibc.
+        return None
+
+    # Call gnu_get_libc_version, which returns a string like "2.5"
+    gnu_get_libc_version.restype = ctypes.c_char_p
+    version_str: str = gnu_get_libc_version()
+    # py2 / py3 compatibility:
+    if not isinstance(version_str, str):
+        version_str = version_str.decode("ascii")
+
+    return version_str
+
+
+# platform.libc_ver regularly returns completely nonsensical glibc
+# versions. E.g. on my computer, platform says:
+#
+#   ~$ python2.7 -c 'import platform; print(platform.libc_ver())'
+#   ('glibc', '2.7')
+#   ~$ python3.5 -c 'import platform; print(platform.libc_ver())'
+#   ('glibc', '2.9')
+#
+# But the truth is:
+#
+#   ~$ ldd --version
+#   ldd (Debian GLIBC 2.22-11) 2.22
+#
+# This is unfortunate, because it means that the linehaul data on libc
+# versions that was generated by pip 8.1.2 and earlier is useless and
+# misleading. Solution: instead of using platform, use our code that actually
+# works.
+def libc_ver() -> tuple[str, str]:
+    """Try to determine the glibc version
+
+    Returns a tuple of strings (lib, version) which default to empty strings
+    in case the lookup fails.
+    """
+    glibc_version = glibc_version_string()
+    if glibc_version is None:
+        return ("", "")
+    else:
+        return ("glibc", glibc_version)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/hashes.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/hashes.py
new file mode 100644
index 0000000..43f14cf
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/hashes.py
@@ -0,0 +1,150 @@
+from __future__ import annotations
+
+import hashlib
+from collections.abc import Iterable
+from typing import TYPE_CHECKING, BinaryIO, NoReturn
+
+from pip._internal.exceptions import HashMismatch, HashMissing, InstallationError
+from pip._internal.utils.misc import read_chunks
+
+if TYPE_CHECKING:
+    from collections.abc import Mapping
+    from hashlib import _Hash
+
+# The recommended hash algo of the moment. Change this whenever the state of
+# the art changes; it won't hurt backward compatibility.
+FAVORITE_HASH = "sha256"
+
+
+# Names of hashlib algorithms allowed by the --hash option and ``pip hash``
+# Currently, those are the ones at least as collision-resistant as sha256.
+STRONG_HASHES = ["sha256", "sha384", "sha512"]
+
+
+class Hashes:
+    """A wrapper that builds multiple hashes at once and checks them against
+    known-good values
+
+    """
+
+    def __init__(self, hashes: dict[str, list[str]] | None = None) -> None:
+        """
+        :param hashes: A dict of algorithm names pointing to lists of allowed
+            hex digests
+        """
+        allowed = {}
+        if hashes is not None:
+            for alg, keys in hashes.items():
+                # Make sure values are always sorted (to ease equality checks)
+                allowed[alg] = [k.lower() for k in sorted(keys)]
+        self._allowed = allowed
+
+    def __and__(self, other: Hashes) -> Hashes:
+        if not isinstance(other, Hashes):
+            return NotImplemented
+
+        # If either of the Hashes object is entirely empty (i.e. no hash
+        # specified at all), all hashes from the other object are allowed.
+        if not other:
+            return self
+        if not self:
+            return other
+
+        # Otherwise only hashes that present in both objects are allowed.
+        new = {}
+        for alg, values in other._allowed.items():
+            if alg not in self._allowed:
+                continue
+            new[alg] = [v for v in values if v in self._allowed[alg]]
+        return Hashes(new)
+
+    @property
+    def digest_count(self) -> int:
+        return sum(len(digests) for digests in self._allowed.values())
+
+    def is_hash_allowed(self, hash_name: str, hex_digest: str) -> bool:
+        """Return whether the given hex digest is allowed."""
+        return hex_digest in self._allowed.get(hash_name, [])
+
+    def check_against_chunks(self, chunks: Iterable[bytes]) -> None:
+        """Check good hashes against ones built from iterable of chunks of
+        data.
+
+        Raise HashMismatch if none match.
+
+        """
+        gots = {}
+        for hash_name in self._allowed.keys():
+            try:
+                gots[hash_name] = hashlib.new(hash_name)
+            except (ValueError, TypeError):
+                raise InstallationError(f"Unknown hash name: {hash_name}")
+
+        for chunk in chunks:
+            for hash in gots.values():
+                hash.update(chunk)
+
+        for hash_name, got in gots.items():
+            if got.hexdigest() in self._allowed[hash_name]:
+                return
+        self._raise(gots)
+
+    def _raise(self, gots: dict[str, _Hash]) -> NoReturn:
+        raise HashMismatch(self._allowed, gots)
+
+    def check_against_file(self, file: BinaryIO) -> None:
+        """Check good hashes against a file-like object
+
+        Raise HashMismatch if none match.
+
+        """
+        return self.check_against_chunks(read_chunks(file))
+
+    def check_against_path(self, path: str) -> None:
+        with open(path, "rb") as file:
+            return self.check_against_file(file)
+
+    def has_one_of(self, hashes: Mapping[str, str]) -> bool:
+        """Return whether any of the given hashes are allowed."""
+        for hash_name, hex_digest in hashes.items():
+            if self.is_hash_allowed(hash_name, hex_digest):
+                return True
+        return False
+
+    def __bool__(self) -> bool:
+        """Return whether I know any known-good hashes."""
+        return bool(self._allowed)
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, Hashes):
+            return NotImplemented
+        return self._allowed == other._allowed
+
+    def __hash__(self) -> int:
+        return hash(
+            ",".join(
+                sorted(
+                    ":".join((alg, digest))
+                    for alg, digest_list in self._allowed.items()
+                    for digest in digest_list
+                )
+            )
+        )
+
+
+class MissingHashes(Hashes):
+    """A workalike for Hashes used when we're missing a hash for a requirement
+
+    It computes the actual hash of the requirement and raises a HashMissing
+    exception showing it to the user.
+
+    """
+
+    def __init__(self) -> None:
+        """Don't offer the ``hashes`` kwarg."""
+        # Pass our favorite hash in to generate a "gotten hash". With the
+        # empty list, it will never match, so an error will always raise.
+        super().__init__(hashes={FAVORITE_HASH: []})
+
+    def _raise(self, gots: dict[str, _Hash]) -> NoReturn:
+        raise HashMissing(gots[FAVORITE_HASH].hexdigest())
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/logging.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/logging.py
new file mode 100644
index 0000000..233ce4e
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/logging.py
@@ -0,0 +1,404 @@
+from __future__ import annotations
+
+import contextlib
+import errno
+import logging
+import logging.handlers
+import os
+import sys
+import threading
+from collections.abc import Generator
+from dataclasses import dataclass
+from io import StringIO, TextIOWrapper
+from logging import Filter
+from typing import Any, ClassVar
+
+from pip._vendor.rich.console import (
+    Console,
+    ConsoleOptions,
+    ConsoleRenderable,
+    RenderableType,
+    RenderResult,
+    RichCast,
+)
+from pip._vendor.rich.highlighter import NullHighlighter
+from pip._vendor.rich.logging import RichHandler
+from pip._vendor.rich.segment import Segment
+from pip._vendor.rich.style import Style
+
+from pip._internal.utils._log import VERBOSE, getLogger
+from pip._internal.utils.compat import WINDOWS
+from pip._internal.utils.deprecation import DEPRECATION_MSG_PREFIX
+from pip._internal.utils.misc import StreamWrapper, ensure_dir, looks_like_ci
+
+_log_state = threading.local()
+_stdout_console = None
+_stderr_console = None
+subprocess_logger = getLogger("pip.subprocessor")
+
+
+class BrokenStdoutLoggingError(Exception):
+    """
+    Raised if BrokenPipeError occurs for the stdout stream while logging.
+    """
+
+
+def _is_broken_pipe_error(exc_class: type[BaseException], exc: BaseException) -> bool:
+    if exc_class is BrokenPipeError:
+        return True
+
+    # On Windows, a broken pipe can show up as EINVAL rather than EPIPE:
+    # https://bugs.python.org/issue19612
+    # https://bugs.python.org/issue30418
+    if not WINDOWS:
+        return False
+
+    return isinstance(exc, OSError) and exc.errno in (errno.EINVAL, errno.EPIPE)
+
+
+@contextlib.contextmanager
+def capture_logging() -> Generator[StringIO, None, None]:
+    """Capture all pip logs in a buffer temporarily."""
+    # Patching sys.std(out|err) directly is not viable as the caller
+    # may want to emit non-logging output (e.g. a rich spinner). To
+    # avoid capturing that, temporarily patch the root logging handlers
+    # to use new rich consoles that write to a StringIO.
+    handlers = {}
+    for handler in logging.getLogger().handlers:
+        if isinstance(handler, RichPipStreamHandler):
+            # Also store the handler's original console so it can be
+            # restored on context exit.
+            handlers[handler] = handler.console
+
+    fake_stream = StreamWrapper.from_stream(sys.stdout)
+    if not handlers:
+        yield fake_stream
+        return
+
+    # HACK: grab no_color attribute from a random handler console since
+    # it's a global option anyway.
+    no_color = next(iter(handlers.values())).no_color
+    fake_console = PipConsole(file=fake_stream, no_color=no_color, soft_wrap=True)
+    try:
+        for handler in handlers:
+            handler.console = fake_console
+        yield fake_stream
+    finally:
+        for handler, original_console in handlers.items():
+            handler.console = original_console
+
+
+@contextlib.contextmanager
+def indent_log(num: int = 2) -> Generator[None, None, None]:
+    """
+    A context manager which will cause the log output to be indented for any
+    log messages emitted inside it.
+    """
+    # For thread-safety
+    _log_state.indentation = get_indentation()
+    _log_state.indentation += num
+    try:
+        yield
+    finally:
+        _log_state.indentation -= num
+
+
+def get_indentation() -> int:
+    return getattr(_log_state, "indentation", 0)
+
+
+class IndentingFormatter(logging.Formatter):
+    default_time_format = "%Y-%m-%dT%H:%M:%S"
+
+    def __init__(
+        self,
+        *args: Any,
+        add_timestamp: bool = False,
+        **kwargs: Any,
+    ) -> None:
+        """
+        A logging.Formatter that obeys the indent_log() context manager.
+
+        :param add_timestamp: A bool indicating output lines should be prefixed
+            with their record's timestamp.
+        """
+        self.add_timestamp = add_timestamp
+        super().__init__(*args, **kwargs)
+
+    def get_message_start(self, formatted: str, levelno: int) -> str:
+        """
+        Return the start of the formatted log message (not counting the
+        prefix to add to each line).
+        """
+        if levelno < logging.WARNING:
+            return ""
+        if formatted.startswith(DEPRECATION_MSG_PREFIX):
+            # Then the message already has a prefix.  We don't want it to
+            # look like "WARNING: DEPRECATION: ...."
+            return ""
+        if levelno < logging.ERROR:
+            return "WARNING: "
+
+        return "ERROR: "
+
+    def format(self, record: logging.LogRecord) -> str:
+        """
+        Calls the standard formatter, but will indent all of the log message
+        lines by our current indentation level.
+        """
+        formatted = super().format(record)
+        message_start = self.get_message_start(formatted, record.levelno)
+        formatted = message_start + formatted
+
+        prefix = ""
+        if self.add_timestamp:
+            prefix = f"{self.formatTime(record)} "
+        prefix += " " * get_indentation()
+        formatted = "".join([prefix + line for line in formatted.splitlines(True)])
+        return formatted
+
+
+@dataclass
+class IndentedRenderable:
+    renderable: RenderableType
+    indent: int
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        segments = console.render(self.renderable, options)
+        lines = Segment.split_lines(segments)
+        for line in lines:
+            yield Segment(" " * self.indent)
+            yield from line
+            yield Segment("\n")
+
+
+class PipConsole(Console):
+    def on_broken_pipe(self) -> None:
+        # Reraise the original exception, rich 13.8.0+ exits by default
+        # instead, preventing our handler from firing.
+        raise BrokenPipeError() from None
+
+
+def get_console(*, stderr: bool = False) -> Console:
+    if stderr:
+        assert _stderr_console is not None, "stderr rich console is missing!"
+        return _stderr_console
+    else:
+        assert _stdout_console is not None, "stdout rich console is missing!"
+        return _stdout_console
+
+
+class RichPipStreamHandler(RichHandler):
+    KEYWORDS: ClassVar[list[str] | None] = []
+
+    def __init__(self, console: Console) -> None:
+        super().__init__(
+            console=console,
+            show_time=False,
+            show_level=False,
+            show_path=False,
+            highlighter=NullHighlighter(),
+        )
+
+    # Our custom override on Rich's logger, to make things work as we need them to.
+    def emit(self, record: logging.LogRecord) -> None:
+        style: Style | None = None
+
+        # If we are given a diagnostic error to present, present it with indentation.
+        if getattr(record, "rich", False):
+            assert isinstance(record.args, tuple)
+            (rich_renderable,) = record.args
+            assert isinstance(
+                rich_renderable, (ConsoleRenderable, RichCast, str)
+            ), f"{rich_renderable} is not rich-console-renderable"
+
+            renderable: RenderableType = IndentedRenderable(
+                rich_renderable, indent=get_indentation()
+            )
+        else:
+            message = self.format(record)
+            renderable = self.render_message(record, message)
+            if record.levelno is not None:
+                if record.levelno >= logging.ERROR:
+                    style = Style(color="red")
+                elif record.levelno >= logging.WARNING:
+                    style = Style(color="yellow")
+
+        try:
+            self.console.print(renderable, overflow="ignore", crop=False, style=style)
+        except Exception:
+            self.handleError(record)
+
+    def handleError(self, record: logging.LogRecord) -> None:
+        """Called when logging is unable to log some output."""
+
+        exc_class, exc = sys.exc_info()[:2]
+        # If a broken pipe occurred while calling write() or flush() on the
+        # stdout stream in logging's Handler.emit(), then raise our special
+        # exception so we can handle it in main() instead of logging the
+        # broken pipe error and continuing.
+        if (
+            exc_class
+            and exc
+            and self.console.file is sys.stdout
+            and _is_broken_pipe_error(exc_class, exc)
+        ):
+            raise BrokenStdoutLoggingError()
+
+        return super().handleError(record)
+
+
+class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler):
+    def _open(self) -> TextIOWrapper:
+        ensure_dir(os.path.dirname(self.baseFilename))
+        return super()._open()
+
+
+class MaxLevelFilter(Filter):
+    def __init__(self, level: int) -> None:
+        self.level = level
+
+    def filter(self, record: logging.LogRecord) -> bool:
+        return record.levelno < self.level
+
+
+class ExcludeLoggerFilter(Filter):
+    """
+    A logging Filter that excludes records from a logger (or its children).
+    """
+
+    def filter(self, record: logging.LogRecord) -> bool:
+        # The base Filter class allows only records from a logger (or its
+        # children).
+        return not super().filter(record)
+
+
+def setup_logging(verbosity: int, no_color: bool, user_log_file: str | None) -> int:
+    """Configures and sets up all of the logging
+
+    Returns the requested logging level, as its integer value.
+    """
+
+    # Determine the level to be logging at.
+    if verbosity >= 2:
+        level_number = logging.DEBUG
+    elif verbosity == 1:
+        level_number = VERBOSE
+    elif verbosity == -1:
+        level_number = logging.WARNING
+    elif verbosity == -2:
+        level_number = logging.ERROR
+    elif verbosity <= -3:
+        level_number = logging.CRITICAL
+    else:
+        level_number = logging.INFO
+
+    level = logging.getLevelName(level_number)
+
+    # The "root" logger should match the "console" level *unless* we also need
+    # to log to a user log file.
+    include_user_log = user_log_file is not None
+    if include_user_log:
+        additional_log_file = user_log_file
+        root_level = "DEBUG"
+    else:
+        additional_log_file = "/dev/null"
+        root_level = level
+
+    # Disable any logging besides WARNING unless we have DEBUG level logging
+    # enabled for vendored libraries.
+    vendored_log_level = "WARNING" if level in ["INFO", "ERROR"] else "DEBUG"
+
+    # Shorthands for clarity
+    handler_classes = {
+        "stream": "pip._internal.utils.logging.RichPipStreamHandler",
+        "file": "pip._internal.utils.logging.BetterRotatingFileHandler",
+    }
+    handlers = ["console", "console_errors", "console_subprocess"] + (
+        ["user_log"] if include_user_log else []
+    )
+    global _stdout_console, stderr_console
+    console_options: dict[str, Any] = {"no_color": no_color, "soft_wrap": True}
+    if looks_like_ci():
+        # Don't animate progress bars and status spinners when running
+        # in CI. A lot of CI workflows use FORCE_COLOR and similar envvars
+        # so their CI output remains colorized, but this causes rich to
+        # assume a fully interactive terminal, resulting in any animated
+        # output to span many lines which is distracting (and unhelpful).
+        console_options["force_interactive"] = False
+    _stdout_console = PipConsole(file=sys.stdout, **console_options)
+    _stderr_console = PipConsole(file=sys.stderr, **console_options)
+
+    logging.config.dictConfig(
+        {
+            "version": 1,
+            "disable_existing_loggers": False,
+            "filters": {
+                "exclude_warnings": {
+                    "()": "pip._internal.utils.logging.MaxLevelFilter",
+                    "level": logging.WARNING,
+                },
+                "restrict_to_subprocess": {
+                    "()": "logging.Filter",
+                    "name": subprocess_logger.name,
+                },
+                "exclude_subprocess": {
+                    "()": "pip._internal.utils.logging.ExcludeLoggerFilter",
+                    "name": subprocess_logger.name,
+                },
+            },
+            "formatters": {
+                "indent": {
+                    "()": IndentingFormatter,
+                    "format": "%(message)s",
+                },
+                "indent_with_timestamp": {
+                    "()": IndentingFormatter,
+                    "format": "%(message)s",
+                    "add_timestamp": True,
+                },
+            },
+            "handlers": {
+                "console": {
+                    "level": level,
+                    "class": handler_classes["stream"],
+                    "console": _stdout_console,
+                    "filters": ["exclude_subprocess", "exclude_warnings"],
+                    "formatter": "indent",
+                },
+                "console_errors": {
+                    "level": "WARNING",
+                    "class": handler_classes["stream"],
+                    "console": _stderr_console,
+                    "filters": ["exclude_subprocess"],
+                    "formatter": "indent",
+                },
+                # A handler responsible for logging to the console messages
+                # from the "subprocessor" logger.
+                "console_subprocess": {
+                    "level": level,
+                    "class": handler_classes["stream"],
+                    "console": _stderr_console,
+                    "filters": ["restrict_to_subprocess"],
+                    "formatter": "indent",
+                },
+                "user_log": {
+                    "level": "DEBUG",
+                    "class": handler_classes["file"],
+                    "filename": additional_log_file,
+                    "encoding": "utf-8",
+                    "delay": True,
+                    "formatter": "indent_with_timestamp",
+                },
+            },
+            "root": {
+                "level": root_level,
+                "handlers": handlers,
+            },
+            "loggers": {"pip._vendor": {"level": vendored_log_level}},
+        }
+    )
+
+    return level_number
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/misc.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/misc.py
new file mode 100644
index 0000000..c676029
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/misc.py
@@ -0,0 +1,815 @@
+from __future__ import annotations
+
+import errno
+import getpass
+import hashlib
+import logging
+import os
+import pathlib
+import posixpath
+import shutil
+import stat
+import sys
+import sysconfig
+import urllib.parse
+from collections.abc import Callable, Generator, Iterable, Iterator, Mapping, Sequence
+from dataclasses import dataclass
+from functools import partial
+from io import StringIO
+from itertools import filterfalse, tee, zip_longest
+from pathlib import Path
+from types import FunctionType, TracebackType
+from typing import (
+    Any,
+    BinaryIO,
+    TextIO,
+    TypeVar,
+    cast,
+)
+
+from pip._vendor.packaging.requirements import Requirement
+from pip._vendor.pyproject_hooks import BuildBackendHookCaller
+
+from pip import __file__ as pip_location
+from pip import __version__
+from pip._internal.exceptions import CommandError, ExternallyManagedEnvironment
+from pip._internal.locations import get_major_minor_version
+from pip._internal.utils.compat import WINDOWS
+from pip._internal.utils.retry import retry
+from pip._internal.utils.virtualenv import running_under_virtualenv
+
+__all__ = [
+    "rmtree",
+    "display_path",
+    "backup_dir",
+    "ask",
+    "splitext",
+    "format_size",
+    "is_installable_dir",
+    "normalize_path",
+    "renames",
+    "get_prog",
+    "ensure_dir",
+    "remove_auth_from_url",
+    "check_externally_managed",
+    "looks_like_ci",
+    "ConfiguredBuildBackendHookCaller",
+]
+
+logger = logging.getLogger(__name__)
+
+T = TypeVar("T")
+ExcInfo = tuple[type[BaseException], BaseException, TracebackType]
+VersionInfo = tuple[int, int, int]
+NetlocTuple = tuple[str, tuple[str | None, str | None]]
+OnExc = Callable[[FunctionType, Path, BaseException], Any]
+OnErr = Callable[[FunctionType, Path, ExcInfo], Any]
+
+FILE_CHUNK_SIZE = 1024 * 1024
+# These are environment variables present when running under various
+# CI systems.  For each variable, some CI systems that use the variable
+# are indicated.  The collection was chosen so that for each of a number
+# of popular systems, at least one of the environment variables is used.
+# This list is used to provide some indication of and lower bound for
+# CI traffic to PyPI.  Thus, it is okay if the list is not comprehensive.
+# For more background, see: https://github.com/pypa/pip/issues/5499
+CI_ENVIRONMENT_VARIABLES = (
+    # Azure Pipelines
+    "BUILD_BUILDID",
+    # Jenkins
+    "BUILD_ID",
+    # AppVeyor, CircleCI, Codeship, Gitlab CI, Shippable, Travis CI
+    "CI",
+    # Explicit environment variable.
+    "PIP_IS_CI",
+)
+
+
+def get_pip_version() -> str:
+    pip_pkg_dir = os.path.join(os.path.dirname(__file__), "..", "..")
+    pip_pkg_dir = os.path.abspath(pip_pkg_dir)
+
+    return f"pip {__version__} from {pip_pkg_dir} (python {get_major_minor_version()})"
+
+
+def get_runnable_pip() -> str:
+    """Get a file to pass to a Python executable, to run the currently-running pip.
+
+    This is used to run a pip subprocess, for installing requirements into the build
+    environment.
+    """
+    source = pathlib.Path(pip_location).resolve().parent
+
+    if not source.is_dir():
+        # This would happen if someone is using pip from inside a zip file. In that
+        # case, we can use that directly.
+        return str(source)
+
+    return os.fsdecode(source / "__pip-runner__.py")
+
+
+def normalize_version_info(py_version_info: tuple[int, ...]) -> tuple[int, int, int]:
+    """
+    Convert a tuple of ints representing a Python version to one of length
+    three.
+
+    :param py_version_info: a tuple of ints representing a Python version,
+        or None to specify no version. The tuple can have any length.
+
+    :return: a tuple of length three if `py_version_info` is non-None.
+        Otherwise, return `py_version_info` unchanged (i.e. None).
+    """
+    if len(py_version_info) < 3:
+        py_version_info += (3 - len(py_version_info)) * (0,)
+    elif len(py_version_info) > 3:
+        py_version_info = py_version_info[:3]
+
+    return cast("VersionInfo", py_version_info)
+
+
+def ensure_dir(path: str) -> None:
+    """os.path.makedirs without EEXIST."""
+    try:
+        os.makedirs(path)
+    except OSError as e:
+        # Windows can raise spurious ENOTEMPTY errors. See #6426.
+        if e.errno != errno.EEXIST and e.errno != errno.ENOTEMPTY:
+            raise
+
+
+def get_prog() -> str:
+    try:
+        prog = os.path.basename(sys.argv[0])
+        if prog in ("__main__.py", "-c"):
+            return f"{sys.executable} -m pip"
+        else:
+            return prog
+    except (AttributeError, TypeError, IndexError):
+        pass
+    return "pip"
+
+
+# Retry every half second for up to 3 seconds
+@retry(stop_after_delay=3, wait=0.5)
+def rmtree(dir: str, ignore_errors: bool = False, onexc: OnExc | None = None) -> None:
+    if ignore_errors:
+        onexc = _onerror_ignore
+    if onexc is None:
+        onexc = _onerror_reraise
+    handler: OnErr = partial(rmtree_errorhandler, onexc=onexc)
+    if sys.version_info >= (3, 12):
+        # See https://docs.python.org/3.12/whatsnew/3.12.html#shutil.
+        shutil.rmtree(dir, onexc=handler)  # type: ignore
+    else:
+        shutil.rmtree(dir, onerror=handler)  # type: ignore
+
+
+def _onerror_ignore(*_args: Any) -> None:
+    pass
+
+
+def _onerror_reraise(*_args: Any) -> None:
+    raise  # noqa: PLE0704 - Bare exception used to reraise existing exception
+
+
+def rmtree_errorhandler(
+    func: FunctionType,
+    path: Path,
+    exc_info: ExcInfo | BaseException,
+    *,
+    onexc: OnExc = _onerror_reraise,
+) -> None:
+    """
+    `rmtree` error handler to 'force' a file remove (i.e. like `rm -f`).
+
+    * If a file is readonly then it's write flag is set and operation is
+      retried.
+
+    * `onerror` is the original callback from `rmtree(... onerror=onerror)`
+      that is chained at the end if the "rm -f" still fails.
+    """
+    try:
+        st_mode = os.stat(path).st_mode
+    except OSError:
+        # it's equivalent to os.path.exists
+        return
+
+    if not st_mode & stat.S_IWRITE:
+        # convert to read/write
+        try:
+            os.chmod(path, st_mode | stat.S_IWRITE)
+        except OSError:
+            pass
+        else:
+            # use the original function to repeat the operation
+            try:
+                func(path)
+                return
+            except OSError:
+                pass
+
+    if not isinstance(exc_info, BaseException):
+        _, exc_info, _ = exc_info
+    onexc(func, path, exc_info)
+
+
+def display_path(path: str) -> str:
+    """Gives the display value for a given path, making it relative to cwd
+    if possible."""
+    try:
+        relative = Path(path).relative_to(Path.cwd())
+    except ValueError:
+        # If the path isn't relative to the CWD, leave it alone
+        return path
+    return os.path.join(".", relative)
+
+
+def backup_dir(dir: str, ext: str = ".bak") -> str:
+    """Figure out the name of a directory to back up the given dir to
+    (adding .bak, .bak2, etc)"""
+    n = 1
+    extension = ext
+    while os.path.exists(dir + extension):
+        n += 1
+        extension = ext + str(n)
+    return dir + extension
+
+
+def ask_path_exists(message: str, options: Iterable[str]) -> str:
+    for action in os.environ.get("PIP_EXISTS_ACTION", "").split():
+        if action in options:
+            return action
+    return ask(message, options)
+
+
+def _check_no_input(message: str) -> None:
+    """Raise an error if no input is allowed."""
+    if os.environ.get("PIP_NO_INPUT"):
+        raise Exception(
+            f"No input was expected ($PIP_NO_INPUT set); question: {message}"
+        )
+
+
+def ask(message: str, options: Iterable[str]) -> str:
+    """Ask the message interactively, with the given possible responses"""
+    while 1:
+        _check_no_input(message)
+        response = input(message)
+        response = response.strip().lower()
+        if response not in options:
+            print(
+                "Your response ({!r}) was not one of the expected responses: "
+                "{}".format(response, ", ".join(options))
+            )
+        else:
+            return response
+
+
+def ask_input(message: str) -> str:
+    """Ask for input interactively."""
+    _check_no_input(message)
+    return input(message)
+
+
+def ask_password(message: str) -> str:
+    """Ask for a password interactively."""
+    _check_no_input(message)
+    return getpass.getpass(message)
+
+
+def strtobool(val: str) -> int:
+    """Convert a string representation of truth to true (1) or false (0).
+
+    True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
+    are 'n', 'no', 'f', 'false', 'off', and '0'.  Raises ValueError if
+    'val' is anything else.
+    """
+    val = val.lower()
+    if val in ("y", "yes", "t", "true", "on", "1"):
+        return 1
+    elif val in ("n", "no", "f", "false", "off", "0"):
+        return 0
+    else:
+        raise ValueError(f"invalid truth value {val!r}")
+
+
+def format_size(bytes: float) -> str:
+    if bytes > 1000 * 1000:
+        return f"{bytes / 1000.0 / 1000:.1f} MB"
+    elif bytes > 10 * 1000:
+        return f"{int(bytes / 1000)} kB"
+    elif bytes > 1000:
+        return f"{bytes / 1000.0:.1f} kB"
+    else:
+        return f"{int(bytes)} bytes"
+
+
+def tabulate(rows: Iterable[Iterable[Any]]) -> tuple[list[str], list[int]]:
+    """Return a list of formatted rows and a list of column sizes.
+
+    For example::
+
+    >>> tabulate([['foobar', 2000], [0xdeadbeef]])
+    (['foobar     2000', '3735928559'], [10, 4])
+    """
+    rows = [tuple(map(str, row)) for row in rows]
+    sizes = [max(map(len, col)) for col in zip_longest(*rows, fillvalue="")]
+    table = [" ".join(map(str.ljust, row, sizes)).rstrip() for row in rows]
+    return table, sizes
+
+
+def is_installable_dir(path: str) -> bool:
+    """Is path is a directory containing pyproject.toml or setup.py?
+
+    If pyproject.toml exists, this is a PEP 517 project. Otherwise we look for
+    a legacy setuptools layout by identifying setup.py. We don't check for the
+    setup.cfg because using it without setup.py is only available for PEP 517
+    projects, which are already covered by the pyproject.toml check.
+    """
+    if not os.path.isdir(path):
+        return False
+    if os.path.isfile(os.path.join(path, "pyproject.toml")):
+        return True
+    if os.path.isfile(os.path.join(path, "setup.py")):
+        return True
+    return False
+
+
+def read_chunks(
+    file: BinaryIO, size: int = FILE_CHUNK_SIZE
+) -> Generator[bytes, None, None]:
+    """Yield pieces of data from a file-like object until EOF."""
+    while True:
+        chunk = file.read(size)
+        if not chunk:
+            break
+        yield chunk
+
+
+def normalize_path(path: str, resolve_symlinks: bool = True) -> str:
+    """
+    Convert a path to its canonical, case-normalized, absolute version.
+
+    """
+    path = os.path.expanduser(path)
+    if resolve_symlinks:
+        path = os.path.realpath(path)
+    else:
+        path = os.path.abspath(path)
+    return os.path.normcase(path)
+
+
+def splitext(path: str) -> tuple[str, str]:
+    """Like os.path.splitext, but take off .tar too"""
+    base, ext = posixpath.splitext(path)
+    if base.lower().endswith(".tar"):
+        ext = base[-4:] + ext
+        base = base[:-4]
+    return base, ext
+
+
+def renames(old: str, new: str) -> None:
+    """Like os.renames(), but handles renaming across devices."""
+    # Implementation borrowed from os.renames().
+    head, tail = os.path.split(new)
+    if head and tail and not os.path.exists(head):
+        os.makedirs(head)
+
+    shutil.move(old, new)
+
+    head, tail = os.path.split(old)
+    if head and tail:
+        try:
+            os.removedirs(head)
+        except OSError:
+            pass
+
+
+def is_local(path: str) -> bool:
+    """
+    Return True if path is within sys.prefix, if we're running in a virtualenv.
+
+    If we're not in a virtualenv, all paths are considered "local."
+
+    Caution: this function assumes the head of path has been normalized
+    with normalize_path.
+    """
+    if not running_under_virtualenv():
+        return True
+    return path.startswith(normalize_path(sys.prefix))
+
+
+def write_output(msg: Any, *args: Any) -> None:
+    logger.info(msg, *args)
+
+
+class StreamWrapper(StringIO):
+    orig_stream: TextIO
+
+    @classmethod
+    def from_stream(cls, orig_stream: TextIO) -> StreamWrapper:
+        ret = cls()
+        ret.orig_stream = orig_stream
+        return ret
+
+    # compileall.compile_dir() needs stdout.encoding to print to stdout
+    # type ignore is because TextIOBase.encoding is writeable
+    @property
+    def encoding(self) -> str:  # type: ignore
+        return self.orig_stream.encoding
+
+
+# Simulates an enum
+def enum(*sequential: Any, **named: Any) -> type[Any]:
+    enums = dict(zip(sequential, range(len(sequential))), **named)
+    reverse = {value: key for key, value in enums.items()}
+    enums["reverse_mapping"] = reverse
+    return type("Enum", (), enums)
+
+
+def build_netloc(host: str, port: int | None) -> str:
+    """
+    Build a netloc from a host-port pair
+    """
+    if port is None:
+        return host
+    if ":" in host:
+        # Only wrap host with square brackets when it is IPv6
+        host = f"[{host}]"
+    return f"{host}:{port}"
+
+
+def build_url_from_netloc(netloc: str, scheme: str = "https") -> str:
+    """
+    Build a full URL from a netloc.
+    """
+    if netloc.count(":") >= 2 and "@" not in netloc and "[" not in netloc:
+        # It must be a bare IPv6 address, so wrap it with brackets.
+        netloc = f"[{netloc}]"
+    return f"{scheme}://{netloc}"
+
+
+def parse_netloc(netloc: str) -> tuple[str | None, int | None]:
+    """
+    Return the host-port pair from a netloc.
+    """
+    url = build_url_from_netloc(netloc)
+    parsed = urllib.parse.urlparse(url)
+    return parsed.hostname, parsed.port
+
+
+def split_auth_from_netloc(netloc: str) -> NetlocTuple:
+    """
+    Parse out and remove the auth information from a netloc.
+
+    Returns: (netloc, (username, password)).
+    """
+    if "@" not in netloc:
+        return netloc, (None, None)
+
+    # Split from the right because that's how urllib.parse.urlsplit()
+    # behaves if more than one @ is present (which can be checked using
+    # the password attribute of urlsplit()'s return value).
+    auth, netloc = netloc.rsplit("@", 1)
+    pw: str | None = None
+    if ":" in auth:
+        # Split from the left because that's how urllib.parse.urlsplit()
+        # behaves if more than one : is present (which again can be checked
+        # using the password attribute of the return value)
+        user, pw = auth.split(":", 1)
+    else:
+        user, pw = auth, None
+
+    user = urllib.parse.unquote(user)
+    if pw is not None:
+        pw = urllib.parse.unquote(pw)
+
+    return netloc, (user, pw)
+
+
+def redact_netloc(netloc: str) -> str:
+    """
+    Replace the sensitive data in a netloc with "****", if it exists.
+
+    For example:
+        - "user:pass@example.com" returns "user:****@example.com"
+        - "accesstoken@example.com" returns "****@example.com"
+    """
+    netloc, (user, password) = split_auth_from_netloc(netloc)
+    if user is None:
+        return netloc
+    if password is None:
+        user = "****"
+        password = ""
+    else:
+        user = urllib.parse.quote(user)
+        password = ":****"
+    return f"{user}{password}@{netloc}"
+
+
+def _transform_url(
+    url: str, transform_netloc: Callable[[str], tuple[Any, ...]]
+) -> tuple[str, NetlocTuple]:
+    """Transform and replace netloc in a url.
+
+    transform_netloc is a function taking the netloc and returning a
+    tuple. The first element of this tuple is the new netloc. The
+    entire tuple is returned.
+
+    Returns a tuple containing the transformed url as item 0 and the
+    original tuple returned by transform_netloc as item 1.
+    """
+    purl = urllib.parse.urlsplit(url)
+    netloc_tuple = transform_netloc(purl.netloc)
+    # stripped url
+    url_pieces = (purl.scheme, netloc_tuple[0], purl.path, purl.query, purl.fragment)
+    surl = urllib.parse.urlunsplit(url_pieces)
+    return surl, cast("NetlocTuple", netloc_tuple)
+
+
+def _get_netloc(netloc: str) -> NetlocTuple:
+    return split_auth_from_netloc(netloc)
+
+
+def _redact_netloc(netloc: str) -> tuple[str]:
+    return (redact_netloc(netloc),)
+
+
+def split_auth_netloc_from_url(
+    url: str,
+) -> tuple[str, str, tuple[str | None, str | None]]:
+    """
+    Parse a url into separate netloc, auth, and url with no auth.
+
+    Returns: (url_without_auth, netloc, (username, password))
+    """
+    url_without_auth, (netloc, auth) = _transform_url(url, _get_netloc)
+    return url_without_auth, netloc, auth
+
+
+def remove_auth_from_url(url: str) -> str:
+    """Return a copy of url with 'username:password@' removed."""
+    # username/pass params are passed to subversion through flags
+    # and are not recognized in the url.
+    return _transform_url(url, _get_netloc)[0]
+
+
+def redact_auth_from_url(url: str) -> str:
+    """Replace the password in a given url with ****."""
+    return _transform_url(url, _redact_netloc)[0]
+
+
+def redact_auth_from_requirement(req: Requirement) -> str:
+    """Replace the password in a given requirement url with ****."""
+    if not req.url:
+        return str(req)
+    return str(req).replace(req.url, redact_auth_from_url(req.url))
+
+
+@dataclass(frozen=True)
+class HiddenText:
+    secret: str
+    redacted: str
+
+    def __repr__(self) -> str:
+        return f""
+
+    def __str__(self) -> str:
+        return self.redacted
+
+    def __eq__(self, other: object) -> bool:
+        # Equality is particularly useful for testing.
+        if type(self) is type(other):
+            # The string being used for redaction doesn't also have to match,
+            # just the raw, original string.
+            return self.secret == other.secret
+        return NotImplemented
+
+    # Disable hashing, since we have a custom __eq__ and don't need hash-ability
+    # (yet). The only required property of hashing is that objects which compare
+    # equal have the same hash value.
+    __hash__ = None  # type: ignore[assignment]
+
+
+def hide_value(value: str) -> HiddenText:
+    return HiddenText(value, redacted="****")
+
+
+def hide_url(url: str) -> HiddenText:
+    redacted = redact_auth_from_url(url)
+    return HiddenText(url, redacted=redacted)
+
+
+def protect_pip_from_modification_on_windows(modifying_pip: bool) -> None:
+    """Protection of pip.exe from modification on Windows
+
+    On Windows, any operation modifying pip should be run as:
+        python -m pip ...
+    """
+    pip_names = [
+        "pip",
+        f"pip{sys.version_info.major}",
+        f"pip{sys.version_info.major}.{sys.version_info.minor}",
+    ]
+
+    # See https://github.com/pypa/pip/issues/1299 for more discussion
+    should_show_use_python_msg = (
+        modifying_pip and WINDOWS and os.path.basename(sys.argv[0]) in pip_names
+    )
+
+    if should_show_use_python_msg:
+        new_command = [sys.executable, "-m", "pip"] + sys.argv[1:]
+        raise CommandError(
+            "To modify pip, please run the following command:\n{}".format(
+                " ".join(new_command)
+            )
+        )
+
+
+def check_externally_managed() -> None:
+    """Check whether the current environment is externally managed.
+
+    If the ``EXTERNALLY-MANAGED`` config file is found, the current environment
+    is considered externally managed, and an ExternallyManagedEnvironment is
+    raised.
+    """
+    if running_under_virtualenv():
+        return
+    marker = os.path.join(sysconfig.get_path("stdlib"), "EXTERNALLY-MANAGED")
+    if not os.path.isfile(marker):
+        return
+    raise ExternallyManagedEnvironment.from_config(marker)
+
+
+def is_console_interactive() -> bool:
+    """Is this console interactive?"""
+    return sys.stdin is not None and sys.stdin.isatty()
+
+
+def hash_file(path: str, blocksize: int = 1 << 20) -> tuple[Any, int]:
+    """Return (hash, length) for path using hashlib.sha256()"""
+
+    h = hashlib.sha256()
+    length = 0
+    with open(path, "rb") as f:
+        for block in read_chunks(f, size=blocksize):
+            length += len(block)
+            h.update(block)
+    return h, length
+
+
+def pairwise(iterable: Iterable[Any]) -> Iterator[tuple[Any, Any]]:
+    """
+    Return paired elements.
+
+    For example:
+        s -> (s0, s1), (s2, s3), (s4, s5), ...
+    """
+    iterable = iter(iterable)
+    return zip_longest(iterable, iterable)
+
+
+def partition(
+    pred: Callable[[T], bool], iterable: Iterable[T]
+) -> tuple[Iterable[T], Iterable[T]]:
+    """
+    Use a predicate to partition entries into false entries and true entries,
+    like
+
+        partition(is_odd, range(10)) --> 0 2 4 6 8   and  1 3 5 7 9
+    """
+    t1, t2 = tee(iterable)
+    return filterfalse(pred, t1), filter(pred, t2)
+
+
+class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
+    def __init__(
+        self,
+        config_holder: Any,
+        source_dir: str,
+        build_backend: str,
+        backend_path: str | None = None,
+        runner: Callable[..., None] | None = None,
+        python_executable: str | None = None,
+    ):
+        super().__init__(
+            source_dir, build_backend, backend_path, runner, python_executable
+        )
+        self.config_holder = config_holder
+
+    def build_wheel(
+        self,
+        wheel_directory: str,
+        config_settings: Mapping[str, Any] | None = None,
+        metadata_directory: str | None = None,
+    ) -> str:
+        cs = self.config_holder.config_settings
+        return super().build_wheel(
+            wheel_directory, config_settings=cs, metadata_directory=metadata_directory
+        )
+
+    def build_sdist(
+        self,
+        sdist_directory: str,
+        config_settings: Mapping[str, Any] | None = None,
+    ) -> str:
+        cs = self.config_holder.config_settings
+        return super().build_sdist(sdist_directory, config_settings=cs)
+
+    def build_editable(
+        self,
+        wheel_directory: str,
+        config_settings: Mapping[str, Any] | None = None,
+        metadata_directory: str | None = None,
+    ) -> str:
+        cs = self.config_holder.config_settings
+        return super().build_editable(
+            wheel_directory, config_settings=cs, metadata_directory=metadata_directory
+        )
+
+    def get_requires_for_build_wheel(
+        self, config_settings: Mapping[str, Any] | None = None
+    ) -> Sequence[str]:
+        cs = self.config_holder.config_settings
+        return super().get_requires_for_build_wheel(config_settings=cs)
+
+    def get_requires_for_build_sdist(
+        self, config_settings: Mapping[str, Any] | None = None
+    ) -> Sequence[str]:
+        cs = self.config_holder.config_settings
+        return super().get_requires_for_build_sdist(config_settings=cs)
+
+    def get_requires_for_build_editable(
+        self, config_settings: Mapping[str, Any] | None = None
+    ) -> Sequence[str]:
+        cs = self.config_holder.config_settings
+        return super().get_requires_for_build_editable(config_settings=cs)
+
+    def prepare_metadata_for_build_wheel(
+        self,
+        metadata_directory: str,
+        config_settings: Mapping[str, Any] | None = None,
+        _allow_fallback: bool = True,
+    ) -> str:
+        cs = self.config_holder.config_settings
+        return super().prepare_metadata_for_build_wheel(
+            metadata_directory=metadata_directory,
+            config_settings=cs,
+            _allow_fallback=_allow_fallback,
+        )
+
+    def prepare_metadata_for_build_editable(
+        self,
+        metadata_directory: str,
+        config_settings: Mapping[str, Any] | None = None,
+        _allow_fallback: bool = True,
+    ) -> str | None:
+        cs = self.config_holder.config_settings
+        return super().prepare_metadata_for_build_editable(
+            metadata_directory=metadata_directory,
+            config_settings=cs,
+            _allow_fallback=_allow_fallback,
+        )
+
+
+def warn_if_run_as_root() -> None:
+    """Output a warning for sudo users on Unix.
+
+    In a virtual environment, sudo pip still writes to virtualenv.
+    On Windows, users may run pip as Administrator without issues.
+    This warning only applies to Unix root users outside of virtualenv.
+    """
+    if running_under_virtualenv():
+        return
+    if not hasattr(os, "getuid"):
+        return
+    # On Windows, there are no "system managed" Python packages. Installing as
+    # Administrator via pip is the correct way of updating system environments.
+    #
+    # We choose sys.platform over utils.compat.WINDOWS here to enable Mypy platform
+    # checks: https://mypy.readthedocs.io/en/stable/common_issues.html
+    if sys.platform == "win32" or sys.platform == "cygwin":
+        return
+
+    if os.getuid() != 0:
+        return
+
+    logger.warning(
+        "Running pip as the 'root' user can result in broken permissions and "
+        "conflicting behaviour with the system package manager, possibly "
+        "rendering your system unusable. "
+        "It is recommended to use a virtual environment instead: "
+        "https://pip.pypa.io/warnings/venv. "
+        "Use the --root-user-action option if you know what you are doing and "
+        "want to suppress this warning."
+    )
+
+
+def looks_like_ci() -> bool:
+    """
+    Return whether it looks like pip is running under CI.
+    """
+    # We don't use the method of checking for a tty (e.g. using isatty())
+    # because some CI systems mimic a tty (e.g. Travis CI).  Thus that
+    # method doesn't provide definitive information in either direction.
+    return any(name in os.environ for name in CI_ENVIRONMENT_VARIABLES)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/packaging.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/packaging.py
new file mode 100644
index 0000000..3cbc049
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/packaging.py
@@ -0,0 +1,44 @@
+from __future__ import annotations
+
+import functools
+import logging
+
+from pip._vendor.packaging import specifiers, version
+from pip._vendor.packaging.requirements import Requirement
+
+logger = logging.getLogger(__name__)
+
+
+@functools.lru_cache(maxsize=32)
+def check_requires_python(
+    requires_python: str | None, version_info: tuple[int, ...]
+) -> bool:
+    """
+    Check if the given Python version matches a "Requires-Python" specifier.
+
+    :param version_info: A 3-tuple of ints representing a Python
+        major-minor-micro version to check (e.g. `sys.version_info[:3]`).
+
+    :return: `True` if the given Python version satisfies the requirement.
+        Otherwise, return `False`.
+
+    :raises InvalidSpecifier: If `requires_python` has an invalid format.
+    """
+    if requires_python is None:
+        # The package provides no information
+        return True
+    requires_python_specifier = specifiers.SpecifierSet(requires_python)
+
+    python_version = version.parse(".".join(map(str, version_info)))
+    return python_version in requires_python_specifier
+
+
+@functools.lru_cache(maxsize=10000)
+def get_requirement(req_string: str) -> Requirement:
+    """Construct a packaging.Requirement object with caching"""
+    # Parsing requirement strings is expensive, and is also expected to happen
+    # with a low diversity of different arguments (at least relative the number
+    # constructed). This method adds a cache to requirement object creation to
+    # minimize repeated parsing of the same string to construct equivalent
+    # Requirement objects.
+    return Requirement(req_string)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/pylock.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/pylock.py
new file mode 100644
index 0000000..70df986
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/pylock.py
@@ -0,0 +1,299 @@
+from __future__ import annotations
+
+import os
+import re
+from collections.abc import Iterable, Iterator
+from pathlib import Path
+from typing import TYPE_CHECKING
+from urllib.parse import urljoin, urlsplit
+
+from pip._vendor.packaging.pylock import (
+    Package,
+    PackageArchive,
+    PackageDirectory,
+    PackageSdist,
+    PackageVcs,
+    PackageWheel,
+    Pylock,
+    is_valid_pylock_path,
+)
+from pip._vendor.packaging.version import Version
+
+from pip._internal.exceptions import DiagnosticPipError, InstallationError
+from pip._internal.models.link import Link
+from pip._internal.utils.compat import tomllib
+from pip._internal.utils.urls import path_to_url, url_to_path
+
+if TYPE_CHECKING:
+    from pip._internal.network.session import PipSession
+    from pip._internal.req.req_install import InstallRequirement
+
+
+def _pylock_package_from_install_requirement(
+    ireq: InstallRequirement, base_dir: Path
+) -> Package:
+    base_dir = base_dir.resolve()
+    dist = ireq.get_dist()
+    download_info = ireq.download_info
+    assert download_info
+    package_version = None
+    package_vcs = None
+    package_directory = None
+    package_archive = None
+    package_sdist = None
+    package_wheels = None
+    if ireq.is_direct:
+        if download_info.vcs_info:
+            package_vcs = PackageVcs(
+                type=download_info.vcs_info.vcs,
+                url=download_info.url,
+                path=None,
+                requested_revision=download_info.vcs_info.requested_revision,
+                commit_id=download_info.vcs_info.commit_id,
+                subdirectory=download_info.subdirectory,
+            )
+        elif download_info.dir_info:
+            package_directory = PackageDirectory(
+                path=(
+                    Path(url_to_path(download_info.url))
+                    .resolve()
+                    .relative_to(base_dir)
+                    .as_posix()
+                ),
+                editable=(
+                    download_info.dir_info.editable
+                    if download_info.dir_info.editable
+                    else None
+                ),
+                subdirectory=download_info.subdirectory,
+            )
+        elif download_info.archive_info:
+            if not download_info.archive_info.hashes:
+                raise NotImplementedError()
+            package_archive = PackageArchive(
+                url=download_info.url,
+                path=None,
+                hashes=download_info.archive_info.hashes,
+                subdirectory=download_info.subdirectory,
+            )
+        else:
+            # should never happen
+            raise NotImplementedError()
+    else:
+        package_version = dist.version
+        if download_info.archive_info:
+            if not download_info.archive_info.hashes:
+                raise NotImplementedError()
+            link = Link(download_info.url)
+            if link.is_wheel:
+                package_wheels = [
+                    PackageWheel(
+                        name=link.filename,
+                        url=download_info.url,
+                        hashes=download_info.archive_info.hashes,
+                    )
+                ]
+            else:
+                package_sdist = PackageSdist(
+                    name=link.filename,
+                    url=download_info.url,
+                    hashes=download_info.archive_info.hashes,
+                )
+        else:
+            # should never happen
+            raise NotImplementedError()
+    return Package(
+        name=dist.canonical_name,
+        version=package_version,
+        vcs=package_vcs,
+        directory=package_directory,
+        archive=package_archive,
+        sdist=package_sdist,
+        wheels=package_wheels,
+    )
+
+
+def pylock_from_install_requirements(
+    install_requirements: Iterable[InstallRequirement], base_dir: Path
+) -> Pylock:
+    return Pylock(
+        lock_version=Version("1.0"),
+        created_by="pip",
+        packages=sorted(
+            (
+                _pylock_package_from_install_requirement(ireq, base_dir)
+                for ireq in install_requirements
+            ),
+            key=lambda p: p.name,
+        ),
+    )
+
+
+_SCHEME_RE = re.compile("^(http|https|file)://", re.IGNORECASE)
+
+
+def _is_url(s: str) -> bool:
+    return bool(_SCHEME_RE.match(s))
+
+
+def is_valid_pylock_filename(filename: str) -> bool:
+    if _is_url(filename):
+        path = Path(urlsplit(filename).path.rpartition("/")[-1])
+    else:
+        path = Path(filename)
+    return is_valid_pylock_path(path)
+
+
+def _package_dist_url(
+    pylock_path_or_url: str, path: str | None, url: str | None
+) -> str:
+    """Compute an url from a Pylock package path and url.
+
+    Give priority to path over url. If path is relative,
+    compute an url using the pylock file location as base.
+    """
+    if path is not None:
+        if not os.path.isabs(path):
+            # relative path, join to pylock location
+            if _is_url(pylock_path_or_url):
+                dist_url = urljoin(pylock_path_or_url, path)
+                # os.path.isabs does not treat a scheme-carrying value like
+                # "file:..." as absolute, so it reaches here and urljoin honors
+                # its scheme, discarding the pylock base. Only keep the result
+                # if its scheme and host still match the lock's own.
+                base = urlsplit(pylock_path_or_url)
+                target = urlsplit(dist_url)
+                if (target.scheme, target.netloc) != (base.scheme, base.netloc):
+                    raise InstallationError(
+                        f"Path {path!r} in pylock file obtained from a URL "
+                        f"resolves outside its location: {pylock_path_or_url!r}"
+                    )
+                return dist_url
+            else:
+                return path_to_url(
+                    os.path.join(os.path.dirname(pylock_path_or_url), path)
+                )
+        else:
+            # absolute path, reject if pylock comes from a URL
+            if _is_url(pylock_path_or_url):
+                raise InstallationError(
+                    f"Absolute paths are not supported in pylock files obtained "
+                    f"from a URL: {path!r} in {pylock_path_or_url!r}"
+                )
+            return path_to_url(path)
+    else:
+        assert url is not None  # guaranteed by packaging.pylock validation
+        return url
+
+
+def package_vcs_requirement_url(
+    pylock_path_or_url: str, package_vcs: PackageVcs
+) -> str:
+    dist_url = _package_dist_url(pylock_path_or_url, package_vcs.path, package_vcs.url)
+    url = f"{package_vcs.type}+{dist_url}@{package_vcs.commit_id}"
+    if package_vcs.subdirectory:
+        if "#" in url:
+            raise InstallationError(
+                f"Package URL {url!r} cannot contain fragments in combination "
+                f"with subdirectory field (in {pylock_path_or_url!r})"
+            )
+        url += "#subdirectory=" + package_vcs.subdirectory
+    return url
+
+
+def package_archive_requirement_url(
+    pylock_path_or_url: str, package_archive: PackageArchive
+) -> str:
+    url = _package_dist_url(
+        pylock_path_or_url, package_archive.path, package_archive.url
+    )
+    if package_archive.subdirectory:
+        if "#" in url:
+            raise InstallationError(
+                f"Package URL {url!r} cannot contain fragments in combination "
+                f"with subdirectory field (in {pylock_path_or_url!r})"
+            )
+        url += "#subdirectory=" + package_archive.subdirectory
+    return url
+
+
+def package_directory_requirement_url(
+    pylock_path_or_url: str, package_directory: PackageDirectory
+) -> str:
+    if _is_url(pylock_path_or_url) and not pylock_path_or_url.startswith("file://"):
+        raise InstallationError(
+            f"Directory entries are not supported in remote pylock.toml "
+            f"{pylock_path_or_url!r}"
+        )
+    url = _package_dist_url(pylock_path_or_url, package_directory.path, None)
+    assert url.startswith("file://")
+    if not url.endswith("/"):
+        url += "/"
+    if package_directory.subdirectory:
+        url += package_directory.subdirectory
+        if not url.endswith("/"):
+            url += "/"
+    return url
+
+
+def package_sdist_requirement_url(
+    pylock_path_or_url: str, package_sdist: PackageSdist
+) -> str:
+    return _package_dist_url(pylock_path_or_url, package_sdist.path, package_sdist.url)
+
+
+def package_wheel_requirement_url(
+    pylock_path_or_url: str, package_wheel: PackageWheel
+) -> str:
+    return _package_dist_url(pylock_path_or_url, package_wheel.path, package_wheel.url)
+
+
+def _get_pylock_path_or_url_content(path_or_url: str, session: PipSession) -> str:
+    # TODO: refactor - this is similar to req_file.get_file_content
+    scheme = urlsplit(path_or_url).scheme
+    # Pip has special support for file:// URLs (LocalFSAdapter).
+    if scheme in ["http", "https", "file"]:
+        # Delay importing heavy network modules until absolutely necessary.
+        from pip._internal.network.utils import raise_for_status
+
+        resp = session.get(path_or_url)
+        raise_for_status(resp)
+        return resp.text
+
+    # Assume this is a bare path.
+    return Path(path_or_url).read_text(encoding="utf-8")
+
+
+def select_from_pylock_path_or_url(
+    pylock_path_or_url: str,
+    session: PipSession,
+) -> Iterator[
+    tuple[
+        Package,
+        PackageVcs | PackageDirectory | PackageArchive | PackageWheel | PackageSdist,
+    ]
+]:
+    try:
+        pylock_content = _get_pylock_path_or_url_content(pylock_path_or_url, session)
+    except DiagnosticPipError:
+        raise
+    except Exception as exc:
+        raise InstallationError(
+            f"Error reading pylock file {pylock_path_or_url!r}: {exc}"
+        ) from exc
+
+    try:
+        lock = Pylock.from_dict(tomllib.loads(pylock_content))
+    except Exception as exc:
+        raise InstallationError(
+            f"Invalid pylock file {pylock_path_or_url!r}: {exc}"
+        ) from exc
+
+    try:
+        # TODO: for completeness, pylock.select should support preferring sdist
+        # over wheels to support --no-binary
+        yield from lock.select()
+    except Exception as exc:
+        raise InstallationError(
+            f"Cannot select requirements from pylock file {pylock_path_or_url!r}: {exc}"
+        ) from exc
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/retry.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/retry.py
new file mode 100644
index 0000000..1392c8f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/retry.py
@@ -0,0 +1,46 @@
+from __future__ import annotations
+
+import functools
+from collections.abc import Callable
+from time import perf_counter, sleep
+from typing import TYPE_CHECKING, TypeVar
+
+if TYPE_CHECKING:
+    from typing_extensions import ParamSpec
+
+    T = TypeVar("T")
+    P = ParamSpec("P")
+
+
+def retry(
+    wait: float, stop_after_delay: float
+) -> Callable[[Callable[P, T]], Callable[P, T]]:
+    """Decorator to automatically retry a function on error.
+
+    If the function raises, the function is recalled with the same arguments
+    until it returns or the time limit is reached. When the time limit is
+    surpassed, the last exception raised is reraised.
+
+    :param wait: The time to wait after an error before retrying, in seconds.
+    :param stop_after_delay: The time limit after which retries will cease,
+        in seconds.
+    """
+
+    def wrapper(func: Callable[P, T]) -> Callable[P, T]:
+
+        @functools.wraps(func)
+        def retry_wrapped(*args: P.args, **kwargs: P.kwargs) -> T:
+            # The performance counter is monotonic on all platforms we care
+            # about and has much better resolution than time.monotonic().
+            start_time = perf_counter()
+            while True:
+                try:
+                    return func(*args, **kwargs)
+                except Exception:
+                    if perf_counter() - start_time > stop_after_delay:
+                        raise
+                    sleep(wait)
+
+        return retry_wrapped
+
+    return wrapper
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/subprocess.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/subprocess.py
new file mode 100644
index 0000000..06f4564
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/subprocess.py
@@ -0,0 +1,249 @@
+from __future__ import annotations
+
+import logging
+import os
+import shlex
+import subprocess
+from collections.abc import Callable, Iterable, Mapping
+from typing import Any, Literal
+
+from pip._vendor.rich.markup import escape
+
+from pip._internal.cli.spinners import SpinnerInterface, open_spinner
+from pip._internal.exceptions import InstallationSubprocessError
+from pip._internal.utils.logging import VERBOSE, subprocess_logger
+from pip._internal.utils.misc import HiddenText
+
+CommandArgs = list[str | HiddenText]
+
+
+def make_command(*args: str | HiddenText | CommandArgs) -> CommandArgs:
+    """
+    Create a CommandArgs object.
+    """
+    command_args: CommandArgs = []
+    for arg in args:
+        # Check for list instead of CommandArgs since CommandArgs is
+        # only known during type-checking.
+        if isinstance(arg, list):
+            command_args.extend(arg)
+        else:
+            # Otherwise, arg is str or HiddenText.
+            command_args.append(arg)
+
+    return command_args
+
+
+def format_command_args(args: list[str] | CommandArgs) -> str:
+    """
+    Format command arguments for display.
+    """
+    # For HiddenText arguments, display the redacted form by calling str().
+    # Also, we don't apply str() to arguments that aren't HiddenText since
+    # this can trigger a UnicodeDecodeError in Python 2 if the argument
+    # has type unicode and includes a non-ascii character.  (The type
+    # checker doesn't ensure the annotations are correct in all cases.)
+    return " ".join(
+        shlex.quote(str(arg)) if isinstance(arg, HiddenText) else shlex.quote(arg)
+        for arg in args
+    )
+
+
+def reveal_command_args(args: list[str] | CommandArgs) -> list[str]:
+    """
+    Return the arguments in their raw, unredacted form.
+    """
+    return [arg.secret if isinstance(arg, HiddenText) else arg for arg in args]
+
+
+def call_subprocess(
+    cmd: list[str] | CommandArgs,
+    show_stdout: bool = False,
+    cwd: str | None = None,
+    on_returncode: Literal["raise", "warn", "ignore"] = "raise",
+    extra_ok_returncodes: Iterable[int] | None = None,
+    extra_environ: Mapping[str, Any] | None = None,
+    unset_environ: Iterable[str] | None = None,
+    spinner: SpinnerInterface | None = None,
+    log_failed_cmd: bool | None = True,
+    stdout_only: bool | None = False,
+    *,
+    command_desc: str,
+) -> str:
+    """
+    Args:
+      show_stdout: if true, use INFO to log the subprocess's stderr and
+        stdout streams.  Otherwise, use DEBUG.  Defaults to False.
+      extra_ok_returncodes: an iterable of integer return codes that are
+        acceptable, in addition to 0. Defaults to None, which means [].
+      unset_environ: an iterable of environment variable names to unset
+        prior to calling subprocess.Popen().
+      log_failed_cmd: if false, failed commands are not logged, only raised.
+      stdout_only: if true, return only stdout, else return both. When true,
+        logging of both stdout and stderr occurs when the subprocess has
+        terminated, else logging occurs as subprocess output is produced.
+    """
+    if extra_ok_returncodes is None:
+        extra_ok_returncodes = []
+    if unset_environ is None:
+        unset_environ = []
+    # Most places in pip use show_stdout=False. What this means is--
+    #
+    # - We connect the child's output (combined stderr and stdout) to a
+    #   single pipe, which we read.
+    # - We log this output to stderr at DEBUG level as it is received.
+    # - If DEBUG logging isn't enabled (e.g. if --verbose logging wasn't
+    #   requested), then we show a spinner so the user can still see the
+    #   subprocess is in progress.
+    # - If the subprocess exits with an error, we log the output to stderr
+    #   at ERROR level if it hasn't already been displayed to the console
+    #   (e.g. if --verbose logging wasn't enabled).  This way we don't log
+    #   the output to the console twice.
+    #
+    # If show_stdout=True, then the above is still done, but with DEBUG
+    # replaced by INFO.
+    if show_stdout:
+        # Then log the subprocess output at INFO level.
+        log_subprocess: Callable[..., None] = subprocess_logger.info
+        used_level = logging.INFO
+    else:
+        # Then log the subprocess output using VERBOSE.  This also ensures
+        # it will be logged to the log file (aka user_log), if enabled.
+        log_subprocess = subprocess_logger.verbose
+        used_level = VERBOSE
+
+    # Whether the subprocess will be visible in the console.
+    showing_subprocess = subprocess_logger.getEffectiveLevel() <= used_level
+
+    # Only use the spinner if we're not showing the subprocess output
+    # and we have a spinner.
+    use_spinner = not showing_subprocess and spinner is not None
+
+    log_subprocess("Running command %s", command_desc)
+    env = os.environ.copy()
+    if extra_environ:
+        env.update(extra_environ)
+    for name in unset_environ:
+        env.pop(name, None)
+    try:
+        proc = subprocess.Popen(
+            # Convert HiddenText objects to the underlying str.
+            reveal_command_args(cmd),
+            stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.STDOUT if not stdout_only else subprocess.PIPE,
+            cwd=cwd,
+            env=env,
+            encoding="locale",
+            errors="backslashreplace",
+        )
+    except Exception as exc:
+        if log_failed_cmd:
+            subprocess_logger.critical(
+                "Error %s while executing command %s",
+                exc,
+                command_desc,
+            )
+        raise
+    all_output = []
+    if not stdout_only:
+        assert proc.stdout
+        assert proc.stdin
+        proc.stdin.close()
+        # In this mode, stdout and stderr are in the same pipe.
+        while True:
+            line: str = proc.stdout.readline()
+            if not line:
+                break
+            line = line.rstrip()
+            all_output.append(line + "\n")
+
+            # Show the line immediately.
+            log_subprocess(line)
+            # Update the spinner.
+            if use_spinner:
+                assert spinner
+                spinner.spin()
+        try:
+            proc.wait()
+        finally:
+            if proc.stdout:
+                proc.stdout.close()
+        output = "".join(all_output)
+    else:
+        # In this mode, stdout and stderr are in different pipes.
+        # We must use communicate() which is the only safe way to read both.
+        out, err = proc.communicate()
+        # log line by line to preserve pip log indenting
+        for out_line in out.splitlines():
+            log_subprocess(out_line)
+        all_output.append(out)
+        for err_line in err.splitlines():
+            log_subprocess(err_line)
+        all_output.append(err)
+        output = out
+
+    proc_had_error = proc.returncode and proc.returncode not in extra_ok_returncodes
+    if use_spinner:
+        assert spinner
+        if proc_had_error:
+            spinner.finish("error")
+        else:
+            spinner.finish("done")
+    if proc_had_error:
+        if on_returncode == "raise":
+            error = InstallationSubprocessError(
+                command_description=command_desc,
+                exit_code=proc.returncode,
+                output_lines=all_output if not showing_subprocess else None,
+            )
+            if log_failed_cmd:
+                subprocess_logger.error("%s", error, extra={"rich": True})
+                subprocess_logger.verbose(
+                    "[bold magenta]full command[/]: [blue]%s[/]",
+                    escape(format_command_args(cmd)),
+                    extra={"markup": True},
+                )
+                subprocess_logger.verbose(
+                    "[bold magenta]cwd[/]: %s",
+                    escape(cwd or "[inherit]"),
+                    extra={"markup": True},
+                )
+
+            raise error
+        elif on_returncode == "warn":
+            subprocess_logger.warning(
+                'Command "%s" had error code %s in %s',
+                command_desc,
+                proc.returncode,
+                cwd,
+            )
+        elif on_returncode == "ignore":
+            pass
+        else:
+            raise ValueError(f"Invalid value: on_returncode={on_returncode!r}")
+    return output
+
+
+def runner_with_spinner_message(message: str) -> Callable[..., None]:
+    """Provide a subprocess_runner that shows a spinner message.
+
+    Intended for use with for BuildBackendHookCaller. Thus, the runner has
+    an API that matches what's expected by BuildBackendHookCaller.subprocess_runner.
+    """
+
+    def runner(
+        cmd: list[str],
+        cwd: str | None = None,
+        extra_environ: Mapping[str, Any] | None = None,
+    ) -> None:
+        with open_spinner(message) as spinner:
+            call_subprocess(
+                cmd,
+                command_desc=message,
+                cwd=cwd,
+                extra_environ=extra_environ,
+                spinner=spinner,
+            )
+
+    return runner
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/temp_dir.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/temp_dir.py
new file mode 100644
index 0000000..336c618
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/temp_dir.py
@@ -0,0 +1,293 @@
+from __future__ import annotations
+
+import errno
+import itertools
+import logging
+import os.path
+import tempfile
+import traceback
+from collections.abc import Callable, Generator
+from contextlib import ExitStack, contextmanager
+from pathlib import Path
+from typing import (
+    Any,
+    TypeVar,
+)
+
+from pip._internal.utils.misc import enum, rmtree
+
+logger = logging.getLogger(__name__)
+
+_T = TypeVar("_T", bound="TempDirectory")
+
+
+# Kinds of temporary directories. Only needed for ones that are
+# globally-managed.
+tempdir_kinds = enum(
+    BUILD_ENV="build-env",
+    EPHEM_WHEEL_CACHE="ephem-wheel-cache",
+    REQ_BUILD="req-build",
+)
+
+
+_tempdir_manager: ExitStack | None = None
+
+
+@contextmanager
+def global_tempdir_manager() -> Generator[None, None, None]:
+    global _tempdir_manager
+    with ExitStack() as stack:
+        old_tempdir_manager, _tempdir_manager = _tempdir_manager, stack
+        try:
+            yield
+        finally:
+            _tempdir_manager = old_tempdir_manager
+
+
+class TempDirectoryTypeRegistry:
+    """Manages temp directory behavior"""
+
+    def __init__(self) -> None:
+        self._should_delete: dict[str, bool] = {}
+
+    def set_delete(self, kind: str, value: bool) -> None:
+        """Indicate whether a TempDirectory of the given kind should be
+        auto-deleted.
+        """
+        self._should_delete[kind] = value
+
+    def get_delete(self, kind: str) -> bool:
+        """Get configured auto-delete flag for a given TempDirectory type,
+        default True.
+        """
+        return self._should_delete.get(kind, True)
+
+
+_tempdir_registry: TempDirectoryTypeRegistry | None = None
+
+
+@contextmanager
+def tempdir_registry() -> Generator[TempDirectoryTypeRegistry, None, None]:
+    """Provides a scoped global tempdir registry that can be used to dictate
+    whether directories should be deleted.
+    """
+    global _tempdir_registry
+    old_tempdir_registry = _tempdir_registry
+    _tempdir_registry = TempDirectoryTypeRegistry()
+    try:
+        yield _tempdir_registry
+    finally:
+        _tempdir_registry = old_tempdir_registry
+
+
+class _Default:
+    pass
+
+
+_default = _Default()
+
+
+class TempDirectory:
+    """Helper class that owns and cleans up a temporary directory.
+
+    This class can be used as a context manager or as an OO representation of a
+    temporary directory.
+
+    Attributes:
+        path
+            Location to the created temporary directory
+        delete
+            Whether the directory should be deleted when exiting
+            (when used as a contextmanager)
+
+    Methods:
+        cleanup()
+            Deletes the temporary directory
+
+    When used as a context manager, if the delete attribute is True, on
+    exiting the context the temporary directory is deleted.
+    """
+
+    def __init__(
+        self,
+        path: str | None = None,
+        delete: bool | None | _Default = _default,
+        kind: str = "temp",
+        globally_managed: bool = False,
+        ignore_cleanup_errors: bool = True,
+    ):
+        super().__init__()
+
+        if delete is _default:
+            if path is not None:
+                # If we were given an explicit directory, resolve delete option
+                # now.
+                delete = False
+            else:
+                # Otherwise, we wait until cleanup and see what
+                # tempdir_registry says.
+                delete = None
+
+        # The only time we specify path is in for editables where it
+        # is the value of the --src option.
+        if path is None:
+            path = self._create(kind)
+
+        self._path = path
+        self._deleted = False
+        self.delete = delete
+        self.kind = kind
+        self.ignore_cleanup_errors = ignore_cleanup_errors
+
+        if globally_managed:
+            assert _tempdir_manager is not None
+            _tempdir_manager.enter_context(self)
+
+    @property
+    def path(self) -> str:
+        assert not self._deleted, f"Attempted to access deleted path: {self._path}"
+        return self._path
+
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__} {self.path!r}>"
+
+    def __enter__(self: _T) -> _T:
+        return self
+
+    def __exit__(self, exc: Any, value: Any, tb: Any) -> None:
+        if self.delete is not None:
+            delete = self.delete
+        elif _tempdir_registry:
+            delete = _tempdir_registry.get_delete(self.kind)
+        else:
+            delete = True
+
+        if delete:
+            self.cleanup()
+
+    def _create(self, kind: str) -> str:
+        """Create a temporary directory and store its path in self.path"""
+        # We realpath here because some systems have their default tmpdir
+        # symlinked to another directory.  This tends to confuse build
+        # scripts, so we canonicalize the path by traversing potential
+        # symlinks here.
+        path = os.path.realpath(tempfile.mkdtemp(prefix=f"pip-{kind}-"))
+        logger.debug("Created temporary directory: %s", path)
+        return path
+
+    def cleanup(self) -> None:
+        """Remove the temporary directory created and reset state"""
+        self._deleted = True
+        if not os.path.exists(self._path):
+            return
+
+        errors: list[BaseException] = []
+
+        def onerror(
+            func: Callable[..., Any],
+            path: Path,
+            exc_val: BaseException,
+        ) -> None:
+            """Log a warning for a `rmtree` error and continue"""
+            formatted_exc = "\n".join(
+                traceback.format_exception_only(type(exc_val), exc_val)
+            )
+            formatted_exc = formatted_exc.rstrip()  # remove trailing new line
+            if func in (os.unlink, os.remove, os.rmdir):
+                logger.debug(
+                    "Failed to remove a temporary file '%s' due to %s.\n",
+                    path,
+                    formatted_exc,
+                )
+            else:
+                logger.debug("%s failed with %s.", func.__qualname__, formatted_exc)
+            errors.append(exc_val)
+
+        if self.ignore_cleanup_errors:
+            try:
+                # first try with @retry; retrying to handle ephemeral errors
+                rmtree(self._path, ignore_errors=False)
+            except OSError:
+                # last pass ignore/log all errors
+                rmtree(self._path, onexc=onerror)
+            if errors:
+                logger.warning(
+                    "Failed to remove contents in a temporary directory '%s'.\n"
+                    "You can safely remove it manually.",
+                    self._path,
+                )
+        else:
+            rmtree(self._path)
+
+
+class AdjacentTempDirectory(TempDirectory):
+    """Helper class that creates a temporary directory adjacent to a real one.
+
+    Attributes:
+        original
+            The original directory to create a temp directory for.
+        path
+            After calling create() or entering, contains the full
+            path to the temporary directory.
+        delete
+            Whether the directory should be deleted when exiting
+            (when used as a contextmanager)
+
+    """
+
+    # The characters that may be used to name the temp directory
+    # We always prepend a ~ and then rotate through these until
+    # a usable name is found.
+    # pkg_resources raises a different error for .dist-info folder
+    # with leading '-' and invalid metadata
+    LEADING_CHARS = "-~.=%0123456789"
+
+    def __init__(self, original: str, delete: bool | None = None) -> None:
+        self.original = original.rstrip("/\\")
+        super().__init__(delete=delete)
+
+    @classmethod
+    def _generate_names(cls, name: str) -> Generator[str, None, None]:
+        """Generates a series of temporary names.
+
+        The algorithm replaces the leading characters in the name
+        with ones that are valid filesystem characters, but are not
+        valid package names (for both Python and pip definitions of
+        package).
+        """
+        for i in range(1, len(name)):
+            for candidate in itertools.combinations_with_replacement(
+                cls.LEADING_CHARS, i - 1
+            ):
+                new_name = "~" + "".join(candidate) + name[i:]
+                if new_name != name:
+                    yield new_name
+
+        # If we make it this far, we will have to make a longer name
+        for i in range(len(cls.LEADING_CHARS)):
+            for candidate in itertools.combinations_with_replacement(
+                cls.LEADING_CHARS, i
+            ):
+                new_name = "~" + "".join(candidate) + name
+                if new_name != name:
+                    yield new_name
+
+    def _create(self, kind: str) -> str:
+        root, name = os.path.split(self.original)
+        for candidate in self._generate_names(name):
+            path = os.path.join(root, candidate)
+            try:
+                os.mkdir(path)
+            except OSError as ex:
+                # Continue if the name exists already
+                if ex.errno != errno.EEXIST:
+                    raise
+            else:
+                path = os.path.realpath(path)
+                break
+        else:
+            # Final fallback on the default behavior.
+            path = os.path.realpath(tempfile.mkdtemp(prefix=f"pip-{kind}-"))
+
+        logger.debug("Created temporary directory: %s", path)
+        return path
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/unpacking.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/unpacking.py
new file mode 100644
index 0000000..5771648
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/unpacking.py
@@ -0,0 +1,407 @@
+"""Utilities related archives."""
+
+from __future__ import annotations
+
+import logging
+import os
+import shutil
+import stat
+import sys
+import tarfile
+import zipfile
+from collections.abc import Iterable
+from zipfile import ZipInfo
+
+from pip._internal.exceptions import InstallationError
+from pip._internal.utils.filetypes import (
+    BZ2_EXTENSIONS,
+    TAR_EXTENSIONS,
+    XZ_EXTENSIONS,
+    ZIP_EXTENSIONS,
+)
+from pip._internal.utils.misc import ensure_dir
+
+logger = logging.getLogger(__name__)
+
+
+SUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS
+
+try:
+    import bz2  # noqa
+
+    SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS
+except ImportError:
+    logger.debug("bz2 module is not available")
+
+try:
+    # Only for Python 3.3+
+    import lzma  # noqa
+
+    SUPPORTED_EXTENSIONS += XZ_EXTENSIONS
+except ImportError:
+    logger.debug("lzma module is not available")
+
+
+def current_umask() -> int:
+    """Get the current umask which involves having to set it temporarily."""
+    mask = os.umask(0)
+    os.umask(mask)
+    return mask
+
+
+def split_leading_dir(path: str) -> list[str]:
+    path = path.lstrip("/").lstrip("\\")
+    if "/" in path and (
+        ("\\" in path and path.find("/") < path.find("\\")) or "\\" not in path
+    ):
+        return path.split("/", 1)
+    elif "\\" in path:
+        return path.split("\\", 1)
+    else:
+        return [path, ""]
+
+
+def has_leading_dir(paths: Iterable[str]) -> bool:
+    """Returns true if all the paths have the same leading path name
+    (i.e., everything is in one subdirectory in an archive)"""
+    common_prefix = None
+    for path in paths:
+        prefix, rest = split_leading_dir(path)
+        if not prefix:
+            return False
+        elif common_prefix is None:
+            common_prefix = prefix
+        elif prefix != common_prefix:
+            return False
+    return True
+
+
+def is_within_directory(
+    directory: str, target: str, *, resolve_symlinks: bool = False
+) -> bool:
+    """
+    Return true if the absolute path of target is within the directory
+    (including when target is equal to the directory).
+
+    When ``resolve_symlinks`` is true, resolve symlinks before comparing so
+    traversal through a symlink (e.g. "link/../file") is also caught.
+    """
+    if resolve_symlinks:
+        abs_directory = os.path.realpath(directory)
+        abs_target = os.path.realpath(target)
+    else:
+        abs_directory = os.path.abspath(directory)
+        abs_target = os.path.abspath(target)
+
+    return abs_target == abs_directory or abs_target.startswith(abs_directory + os.sep)
+
+
+def _get_default_mode_plus_executable() -> int:
+    return 0o777 & ~current_umask() | 0o111
+
+
+def set_extracted_file_to_default_mode_plus_executable(path: str) -> None:
+    """
+    Make file present at path have execute for user/group/world
+    (chmod +x) is no-op on windows per python docs
+    """
+    os.chmod(path, _get_default_mode_plus_executable())
+
+
+def zip_item_is_executable(info: ZipInfo) -> bool:
+    mode = info.external_attr >> 16
+    # if mode and regular file and any execute permissions for
+    # user/group/world?
+    return bool(mode and stat.S_ISREG(mode) and mode & 0o111)
+
+
+def unzip_file(filename: str, location: str, flatten: bool = True) -> None:
+    """
+    Unzip the file (with path `filename`) to the destination `location`.  All
+    files are written based on system defaults and umask (i.e. permissions are
+    not preserved), except that regular file members with any execute
+    permissions (user, group, or world) have "chmod +x" applied after being
+    written. Note that for windows, any execute changes using os.chmod are
+    no-ops per the python docs.
+    """
+    ensure_dir(location)
+    zipfp = open(filename, "rb")
+    try:
+        zip = zipfile.ZipFile(zipfp, allowZip64=True)
+        leading = has_leading_dir(zip.namelist()) and flatten
+        for info in zip.infolist():
+            name = info.filename
+            fn = name
+            if leading:
+                fn = split_leading_dir(name)[1]
+            fn = os.path.join(location, fn)
+            dir = os.path.dirname(fn)
+            if not is_within_directory(location, fn):
+                message = (
+                    "The zip file ({}) has a file ({}) trying to install "
+                    "outside target directory ({})"
+                )
+                raise InstallationError(message.format(filename, fn, location))
+            if fn.endswith(("/", "\\")):
+                # A directory
+                ensure_dir(fn)
+            else:
+                ensure_dir(dir)
+                # Don't use read() to avoid allocating an arbitrarily large
+                # chunk of memory for the file's content
+                fp = zip.open(name)
+                try:
+                    with open(fn, "wb") as destfp:
+                        shutil.copyfileobj(fp, destfp)
+                finally:
+                    fp.close()
+                    if zip_item_is_executable(info):
+                        set_extracted_file_to_default_mode_plus_executable(fn)
+    finally:
+        zipfp.close()
+
+
+def untar_file(filename: str, location: str) -> None:
+    """
+    Untar the file (with path `filename`) to the destination `location`.
+    All files are written based on system defaults and umask (i.e. permissions
+    are not preserved), except that regular file members with any execute
+    permissions (user, group, or world) have "chmod +x" applied on top of the
+    default.  Note that for windows, any execute changes using os.chmod are
+    no-ops per the python docs.
+    """
+    ensure_dir(location)
+    if filename.lower().endswith(".gz") or filename.lower().endswith(".tgz"):
+        mode = "r:gz"
+    elif filename.lower().endswith(BZ2_EXTENSIONS):
+        mode = "r:bz2"
+    elif filename.lower().endswith(XZ_EXTENSIONS):
+        mode = "r:xz"
+    elif filename.lower().endswith(".tar"):
+        mode = "r"
+    else:
+        logger.warning(
+            "Cannot determine compression type for file %s",
+            filename,
+        )
+        mode = "r:*"
+
+    tar = tarfile.open(filename, mode, encoding="utf-8")  # type: ignore
+    try:
+        leading = has_leading_dir([member.name for member in tar.getmembers()])
+
+        # PEP 706 added `tarfile.data_filter`, and made some other changes to
+        # Python's tarfile module (see below). The features were backported to
+        # security releases.
+        try:
+            data_filter = tarfile.data_filter
+        except AttributeError:
+            _untar_without_filter(filename, location, tar, leading)
+        else:
+            default_mode_plus_executable = _get_default_mode_plus_executable()
+
+            if leading:
+                # Strip the leading directory from all files in the archive,
+                # including hardlink targets (which are relative to the
+                # unpack location).
+                for member in tar.getmembers():
+                    name_lead, name_rest = split_leading_dir(member.name)
+                    member.name = name_rest
+                    if member.islnk():
+                        lnk_lead, lnk_rest = split_leading_dir(member.linkname)
+                        if lnk_lead == name_lead:
+                            member.linkname = lnk_rest
+
+            def pip_filter(member: tarfile.TarInfo, path: str) -> tarfile.TarInfo:
+                orig_mode = member.mode
+                try:
+                    try:
+                        member = data_filter(member, location)
+                    except tarfile.LinkOutsideDestinationError:
+                        if sys.version_info[:3] in {
+                            (3, 9, 17),
+                            (3, 10, 12),
+                            (3, 11, 4),
+                        }:
+                            # The tarfile filter in specific Python versions
+                            # raises LinkOutsideDestinationError on valid input
+                            # (https://github.com/python/cpython/issues/107845)
+                            # Ignore the error there, but do use the
+                            # more lax `tar_filter`
+                            member = tarfile.tar_filter(member, location)
+                        else:
+                            raise
+                except tarfile.TarError as exc:
+                    message = "Invalid member in the tar file {}: {}"
+                    # Filter error messages mention the member name.
+                    # No need to add it here.
+                    raise InstallationError(
+                        message.format(
+                            filename,
+                            exc,
+                        )
+                    )
+                if member.isfile() and orig_mode & 0o111:
+                    member.mode = default_mode_plus_executable
+                else:
+                    # See PEP 706 note above.
+                    # The PEP changed this from `int` to `Optional[int]`,
+                    # where None means "use the default". Mypy doesn't
+                    # know this yet.
+                    member.mode = None  # type: ignore [assignment]
+                return member
+
+            tar.extractall(location, filter=pip_filter)
+
+    finally:
+        tar.close()
+
+
+def is_symlink_target_in_tar(tar: tarfile.TarFile, tarinfo: tarfile.TarInfo) -> bool:
+    """Check if the file pointed to by the symbolic link is in the tar archive"""
+    linkname = os.path.join(os.path.dirname(tarinfo.name), tarinfo.linkname)
+
+    linkname = os.path.normpath(linkname)
+    linkname = linkname.replace("\\", "/")
+
+    try:
+        tar.getmember(linkname)
+        return True
+    except KeyError:
+        return False
+
+
+def _untar_without_filter(
+    filename: str,
+    location: str,
+    tar: tarfile.TarFile,
+    leading: bool,
+) -> None:
+    """Fallback for Python without tarfile.data_filter"""
+    # NOTE: This function can be removed once pip requires CPython ≥ 3.12.​
+    # PEP 706 added tarfile.data_filter, made tarfile extraction operations more secure.
+    # This feature is fully supported from CPython 3.12 onward.
+    for member in tar.getmembers():
+        fn = member.name
+        if leading:
+            fn = split_leading_dir(fn)[1]
+        path = os.path.join(location, fn)
+
+        # The plain check rejects textual ".." escapes; resolving symlinks also
+        # catches a later member redirected outside by an earlier member's
+        # symlink (e.g. "link/../file").
+        if not is_within_directory(location, path) or not is_within_directory(
+            location, path, resolve_symlinks=True
+        ):
+            message = (
+                "The tar file ({}) has a file ({}) trying to install "
+                "outside target directory ({})"
+            )
+            raise InstallationError(message.format(filename, path, location))
+        if member.isdir():
+            ensure_dir(path)
+        elif member.issym():
+            # Reject symlinks resolving outside the destination, so a later
+            # member cannot be written through them.
+            target = os.path.join(os.path.dirname(path), member.linkname)
+            if not is_within_directory(location, target, resolve_symlinks=True):
+                message = (
+                    "The tar file ({}) has a file ({}) trying to install "
+                    "outside target directory ({})"
+                )
+                raise InstallationError(
+                    message.format(filename, member.name, member.linkname)
+                )
+            if not is_symlink_target_in_tar(tar, member):
+                message = (
+                    "The tar file ({}) has a file ({}) trying to install "
+                    "outside target directory ({})"
+                )
+                raise InstallationError(
+                    message.format(filename, member.name, member.linkname)
+                )
+            try:
+                tar._extract_member(member, path)
+            except Exception as exc:
+                # Some corrupt tar files seem to produce this
+                # (specifically bad symlinks)
+                logger.warning(
+                    "In the tar file %s the member %s is invalid: %s",
+                    filename,
+                    member.name,
+                    exc,
+                )
+                continue
+        else:
+            try:
+                fp = tar.extractfile(member)
+            except (KeyError, AttributeError) as exc:
+                # Some corrupt tar files seem to produce this
+                # (specifically bad symlinks)
+                logger.warning(
+                    "In the tar file %s the member %s is invalid: %s",
+                    filename,
+                    member.name,
+                    exc,
+                )
+                continue
+            ensure_dir(os.path.dirname(path))
+            assert fp is not None
+            with open(path, "wb") as destfp:
+                shutil.copyfileobj(fp, destfp)
+            fp.close()
+            # Update the timestamp (useful for cython compiled files)
+            tar.utime(member, path)
+            # member have any execute permissions for user/group/world?
+            if member.mode & 0o111:
+                set_extracted_file_to_default_mode_plus_executable(path)
+
+
+def unpack_file(
+    filename: str,
+    location: str,
+    content_type: str | None = None,
+) -> None:
+    """Unpack ``filename`` into ``location``.
+
+    Archive format is chosen in order of decreasing reliability:
+    ``content_type``, then filename extension, then magic signature
+    (unambiguous matches only).
+    """
+    filename = os.path.realpath(filename)
+    zip_flatten = not filename.endswith(".whl")
+
+    def _unzip() -> None:
+        unzip_file(filename, location, flatten=zip_flatten)
+
+    def _untar() -> None:
+        untar_file(filename, location)
+
+    if content_type == "application/zip":
+        return _unzip()
+    if content_type == "application/x-gzip":
+        return _untar()
+
+    if filename.lower().endswith(ZIP_EXTENSIONS):
+        return _unzip()
+    if filename.lower().endswith(TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS):
+        return _untar()
+
+    # avoid ambiguous case where both signature checks return True
+    is_zipfile = zipfile.is_zipfile(filename)
+    is_tarfile = tarfile.is_tarfile(filename)
+    if is_zipfile and not is_tarfile:
+        return _unzip()
+    if is_tarfile and not is_zipfile:
+        return _untar()
+    if is_zipfile and is_tarfile:
+        logger.error("Ambiguous file signature in %s.", filename)
+
+    logger.critical(
+        "Cannot unpack file %s (downloaded from %s, content-type: %s); "
+        "cannot detect archive format",
+        filename,
+        location,
+        content_type,
+    )
+    raise InstallationError(f"Cannot determine archive format of {location}")
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/urls.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/urls.py
new file mode 100644
index 0000000..2df515d
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/urls.py
@@ -0,0 +1,58 @@
+import os
+import string
+import urllib.parse  # noqa: F401
+
+from .compat import WINDOWS
+
+
+def path_to_url(path: str) -> str:
+    """
+    Convert a path to a file: URL.  The path will be made absolute and have
+    quoted path parts.
+    """
+    import urllib.request
+
+    path = os.path.normpath(os.path.abspath(path))
+    url = urllib.parse.urljoin("file://", urllib.request.pathname2url(path))
+    return url
+
+
+def url_to_path(url: str) -> str:
+    """
+    Convert a file: URL to a path.
+    """
+    import urllib.request
+
+    assert url.startswith(
+        "file:"
+    ), f"You can only turn file: urls into filenames (not {url!r})"
+
+    _, netloc, path, _, _ = urllib.parse.urlsplit(url)
+
+    if not netloc or netloc == "localhost":
+        # According to RFC 8089, same as empty authority.
+        netloc = ""
+    elif WINDOWS:
+        # If we have a UNC path, prepend UNC share notation.
+        netloc = "\\\\" + netloc
+    else:
+        raise ValueError(
+            f"non-local file URIs are not supported on this platform: {url!r}"
+        )
+
+    path = urllib.request.url2pathname(netloc + path)
+
+    # On Windows, urlsplit parses the path as something like "/C:/Users/foo".
+    # This creates issues for path-related functions like io.open(), so we try
+    # to detect and strip the leading slash.
+    if (
+        WINDOWS
+        and not netloc  # Not UNC.
+        and len(path) >= 3
+        and path[0] == "/"  # Leading slash to strip.
+        and path[1] in string.ascii_letters  # Drive letter.
+        and path[2:4] in (":", ":/")  # Colon + end of string, or colon + absolute path.
+    ):
+        path = path[1:]
+
+    return path
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/virtualenv.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/virtualenv.py
new file mode 100644
index 0000000..fa94813
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/virtualenv.py
@@ -0,0 +1,71 @@
+from __future__ import annotations
+
+import logging
+import os
+import re
+import sys
+
+logger = logging.getLogger(__name__)
+_INCLUDE_SYSTEM_SITE_PACKAGES_REGEX = re.compile(
+    r"include-system-site-packages\s*=\s*(?Ptrue|false)"
+)
+
+
+def running_under_virtualenv() -> bool:
+    """Checks if sys.base_prefix and sys.prefix match.
+
+    This handles PEP 405 compliant virtual environments.
+    """
+    return sys.prefix != getattr(sys, "base_prefix", sys.prefix)
+
+
+def _get_pyvenv_cfg_lines() -> list[str] | None:
+    """Reads {sys.prefix}/pyvenv.cfg and returns its contents as list of lines
+
+    Returns None, if it could not read/access the file.
+    """
+    pyvenv_cfg_file = os.path.join(sys.prefix, "pyvenv.cfg")
+    try:
+        # Although PEP 405 does not specify, the built-in venv module always
+        # writes with UTF-8. (pypa/pip#8717)
+        with open(pyvenv_cfg_file, encoding="utf-8") as f:
+            return f.read().splitlines()  # avoids trailing newlines
+    except OSError:
+        return None
+
+
+def _no_global_under_venv() -> bool:
+    """Check `{sys.prefix}/pyvenv.cfg` for system site-packages inclusion
+
+    PEP 405 specifies that when system site-packages are not supposed to be
+    visible from a virtual environment, `pyvenv.cfg` must contain the following
+    line:
+
+        include-system-site-packages = false
+
+    Additionally, log a warning if accessing the file fails.
+    """
+    cfg_lines = _get_pyvenv_cfg_lines()
+    if cfg_lines is None:
+        # We're not in a "sane" venv, so assume there is no system
+        # site-packages access (since that's PEP 405's default state).
+        logger.warning(
+            "Could not access 'pyvenv.cfg' despite a virtual environment "
+            "being active. Assuming global site-packages is not accessible "
+            "in this environment."
+        )
+        return True
+
+    for line in cfg_lines:
+        match = _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX.match(line)
+        if match is not None and match.group("value") == "false":
+            return True
+    return False
+
+
+def virtualenv_no_global() -> bool:
+    """Returns a boolean, whether running in venv with no system site-packages."""
+    if running_under_virtualenv():
+        return _no_global_under_venv()
+
+    return False
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/utils/wheel.py b/venv/lib/python3.12/site-packages/pip/_internal/utils/wheel.py
new file mode 100644
index 0000000..789e736
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/utils/wheel.py
@@ -0,0 +1,132 @@
+"""Support functions for working with wheel files."""
+
+import logging
+from email.message import Message
+from email.parser import Parser
+from zipfile import BadZipFile, ZipFile
+
+from pip._vendor.packaging.utils import canonicalize_name
+
+from pip._internal.exceptions import UnsupportedWheel
+
+VERSION_COMPATIBLE = (1, 0)
+
+
+logger = logging.getLogger(__name__)
+
+
+def parse_wheel(wheel_zip: ZipFile, name: str) -> tuple[str, Message]:
+    """Extract information from the provided wheel, ensuring it meets basic
+    standards.
+
+    Returns the name of the .dist-info directory and the parsed WHEEL metadata.
+    """
+    try:
+        info_dir = wheel_dist_info_dir(wheel_zip, name)
+        metadata = wheel_metadata(wheel_zip, info_dir)
+        version = wheel_version(metadata)
+    except UnsupportedWheel as e:
+        raise UnsupportedWheel(f"{name} has an invalid wheel, {e}")
+
+    check_compatibility(version, name)
+
+    return info_dir, metadata
+
+
+def wheel_dist_info_dir(source: ZipFile, name: str) -> str:
+    """Returns the name of the contained .dist-info directory.
+
+    Raises AssertionError or UnsupportedWheel if not found, >1 found, or
+    it doesn't match the provided name.
+    """
+    # Zip file path separators must be /
+    subdirs = {p.split("/", 1)[0] for p in source.namelist()}
+
+    info_dirs = [s for s in subdirs if s.endswith(".dist-info")]
+
+    if not info_dirs:
+        raise UnsupportedWheel(".dist-info directory not found")
+
+    if len(info_dirs) > 1:
+        raise UnsupportedWheel(
+            "multiple .dist-info directories found: {}".format(", ".join(info_dirs))
+        )
+
+    info_dir = info_dirs[0]
+
+    info_dir_name = canonicalize_name(info_dir)
+    canonical_name = canonicalize_name(name)
+    if not info_dir_name.startswith(canonical_name):
+        raise UnsupportedWheel(
+            f".dist-info directory {info_dir!r} does not start with {canonical_name!r}"
+        )
+
+    return info_dir
+
+
+def read_wheel_metadata_file(source: ZipFile, path: str) -> bytes:
+    try:
+        return source.read(path)
+        # BadZipFile for general corruption, KeyError for missing entry,
+        # and RuntimeError for password-protected files
+    except (BadZipFile, KeyError, RuntimeError) as e:
+        raise UnsupportedWheel(f"could not read {path!r} file: {e!r}")
+
+
+def wheel_metadata(source: ZipFile, dist_info_dir: str) -> Message:
+    """Return the WHEEL metadata of an extracted wheel, if possible.
+    Otherwise, raise UnsupportedWheel.
+    """
+    path = f"{dist_info_dir}/WHEEL"
+    # Zip file path separators must be /
+    wheel_contents = read_wheel_metadata_file(source, path)
+
+    try:
+        wheel_text = wheel_contents.decode()
+    except UnicodeDecodeError as e:
+        raise UnsupportedWheel(f"error decoding {path!r}: {e!r}")
+
+    # FeedParser (used by Parser) does not raise any exceptions. The returned
+    # message may have .defects populated, but for backwards-compatibility we
+    # currently ignore them.
+    return Parser().parsestr(wheel_text)
+
+
+def wheel_version(wheel_data: Message) -> tuple[int, ...]:
+    """Given WHEEL metadata, return the parsed Wheel-Version.
+    Otherwise, raise UnsupportedWheel.
+    """
+    version_text = wheel_data["Wheel-Version"]
+    if version_text is None:
+        raise UnsupportedWheel("WHEEL is missing Wheel-Version")
+
+    version = version_text.strip()
+
+    try:
+        return tuple(map(int, version.split(".")))
+    except ValueError:
+        raise UnsupportedWheel(f"invalid Wheel-Version: {version!r}")
+
+
+def check_compatibility(version: tuple[int, ...], name: str) -> None:
+    """Raises errors or warns if called with an incompatible Wheel-Version.
+
+    pip should refuse to install a Wheel-Version that's a major series
+    ahead of what it's compatible with (e.g 2.0 > 1.1); and warn when
+    installing a version only minor version ahead (e.g 1.2 > 1.1).
+
+    version: a 2-tuple representing a Wheel-Version (Major, Minor)
+    name: name of wheel or package to raise exception about
+
+    :raises UnsupportedWheel: when an incompatible Wheel-Version is given
+    """
+    if version[0] > VERSION_COMPATIBLE[0]:
+        raise UnsupportedWheel(
+            "{}'s Wheel-Version ({}) is not compatible with this version "
+            "of pip".format(name, ".".join(map(str, version)))
+        )
+    elif version > VERSION_COMPATIBLE:
+        logger.warning(
+            "Installing from a newer Wheel-Version (%s)",
+            ".".join(map(str, version)),
+        )
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/vcs/__init__.py b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__init__.py
new file mode 100644
index 0000000..b6beddb
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__init__.py
@@ -0,0 +1,15 @@
+# Expose a limited set of classes and functions so callers outside of
+# the vcs package don't need to import deeper than `pip._internal.vcs`.
+# (The test directory may still need to import from a vcs sub-package.)
+# Import all vcs modules to register each VCS in the VcsSupport object.
+import pip._internal.vcs.bazaar
+import pip._internal.vcs.git
+import pip._internal.vcs.mercurial
+import pip._internal.vcs.subversion  # noqa: F401
+from pip._internal.vcs.versioncontrol import (  # noqa: F401
+    RemoteNotFoundError,
+    RemoteNotValidError,
+    is_url,
+    make_vcs_requirement_url,
+    vcs,
+)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..b39b68a
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-312.pyc
new file mode 100644
index 0000000..e198a17
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/git.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/git.cpython-312.pyc
new file mode 100644
index 0000000..3653475
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/git.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-312.pyc
new file mode 100644
index 0000000..79bebb9
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-312.pyc
new file mode 100644
index 0000000..f015ff9
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-312.pyc
new file mode 100644
index 0000000..4f4b9f3
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/vcs/bazaar.py b/venv/lib/python3.12/site-packages/pip/_internal/vcs/bazaar.py
new file mode 100644
index 0000000..3a8a21e
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/vcs/bazaar.py
@@ -0,0 +1,130 @@
+from __future__ import annotations
+
+import logging
+
+from pip._internal.utils.misc import HiddenText, display_path
+from pip._internal.utils.subprocess import make_command
+from pip._internal.utils.urls import path_to_url
+from pip._internal.vcs.versioncontrol import (
+    AuthInfo,
+    RemoteNotFoundError,
+    RevOptions,
+    VersionControl,
+    vcs,
+)
+
+logger = logging.getLogger(__name__)
+
+
+class Bazaar(VersionControl):
+    name = "bzr"
+    dirname = ".bzr"
+    repo_name = "branch"
+    schemes = (
+        "bzr+http",
+        "bzr+https",
+        "bzr+ssh",
+        "bzr+sftp",
+        "bzr+ftp",
+        "bzr+lp",
+        "bzr+file",
+    )
+
+    @staticmethod
+    def get_base_rev_args(rev: str) -> list[str]:
+        return ["-r", rev]
+
+    def fetch_new(
+        self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
+    ) -> None:
+        rev_display = rev_options.to_display()
+        logger.info(
+            "Checking out %s%s to %s",
+            url,
+            rev_display,
+            display_path(dest),
+        )
+        if verbosity <= 0:
+            flags = ["--quiet"]
+        elif verbosity == 1:
+            flags = []
+        else:
+            flags = [f"-{'v'*verbosity}"]
+        cmd_args = make_command(
+            "checkout", "--lightweight", *flags, rev_options.to_args(), url, dest
+        )
+        self.run_command(cmd_args)
+
+    def switch(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        self.run_command(make_command("switch", url), cwd=dest)
+
+    def update(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        flags = []
+
+        if verbosity <= 0:
+            flags.append("-q")
+
+        output = self.run_command(
+            make_command("info"), show_stdout=False, stdout_only=True, cwd=dest
+        )
+        if output.startswith("Standalone "):
+            # Older versions of pip used to create standalone branches.
+            # Convert the standalone branch to a checkout by calling "bzr bind".
+            cmd_args = make_command("bind", *flags, url)
+            self.run_command(cmd_args, cwd=dest)
+
+        cmd_args = make_command("update", *flags, rev_options.to_args())
+        self.run_command(cmd_args, cwd=dest)
+
+    @classmethod
+    def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]:
+        # hotfix the URL scheme after removing bzr+ from bzr+ssh:// re-add it
+        url, rev, user_pass = super().get_url_rev_and_auth(url)
+        if url.startswith("ssh://"):
+            url = "bzr+" + url
+        return url, rev, user_pass
+
+    @classmethod
+    def get_remote_url(cls, location: str) -> str:
+        urls = cls.run_command(
+            ["info"], show_stdout=False, stdout_only=True, cwd=location
+        )
+        for line in urls.splitlines():
+            line = line.strip()
+            for x in ("checkout of branch: ", "parent branch: "):
+                if line.startswith(x):
+                    repo = line.split(x)[1]
+                    if cls._is_local_repository(repo):
+                        return path_to_url(repo)
+                    return repo
+        raise RemoteNotFoundError
+
+    @classmethod
+    def get_revision(cls, location: str) -> str:
+        revision = cls.run_command(
+            ["revno"],
+            show_stdout=False,
+            stdout_only=True,
+            cwd=location,
+        )
+        return revision.splitlines()[-1]
+
+    @classmethod
+    def is_commit_id_equal(cls, dest: str, name: str | None) -> bool:
+        """Always assume the versions don't match"""
+        return False
+
+
+vcs.register(Bazaar)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/vcs/git.py b/venv/lib/python3.12/site-packages/pip/_internal/vcs/git.py
new file mode 100644
index 0000000..5cc393f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/vcs/git.py
@@ -0,0 +1,574 @@
+from __future__ import annotations
+
+import logging
+import os.path
+import pathlib
+import re
+import urllib.parse
+from dataclasses import replace
+from typing import Any
+
+from pip._internal.exceptions import BadCommand, InstallationError
+from pip._internal.utils.misc import HiddenText, display_path, hide_url, strtobool
+from pip._internal.utils.subprocess import make_command
+from pip._internal.vcs.versioncontrol import (
+    AuthInfo,
+    RemoteNotFoundError,
+    RemoteNotValidError,
+    RevOptions,
+    VersionControl,
+    find_path_to_project_root_from_repo_root,
+    vcs,
+)
+
+urlsplit = urllib.parse.urlsplit
+urlunsplit = urllib.parse.urlunsplit
+
+
+logger = logging.getLogger(__name__)
+
+
+GIT_VERSION_REGEX = re.compile(
+    r"^git version "  # Prefix.
+    r"(\d+)"  # Major.
+    r"\.(\d+)"  # Dot, minor.
+    r"(?:\.(\d+))?"  # Optional dot, patch.
+    r".*$"  # Suffix, including any pre- and post-release segments we don't care about.
+)
+
+HASH_REGEX = re.compile("^[a-fA-F0-9]{40}$")
+
+# SCP (Secure copy protocol) shorthand. e.g. 'git@example.com:foo/bar.git'
+SCP_REGEX = re.compile(
+    r"""^
+    # Optional user, e.g. 'git@'
+    (\w+@)?
+    # Server, e.g. 'github.com'.
+    ([^/:]+):
+    # The server-side path. e.g. 'user/project.git'. Must start with an
+    # alphanumeric character so as not to be confusable with a Windows paths
+    # like 'C:/foo/bar' or 'C:\foo\bar'.
+    (\w[^:]*)
+    $""",
+    re.VERBOSE,
+)
+
+
+def looks_like_hash(sha: str) -> bool:
+    return bool(HASH_REGEX.match(sha))
+
+
+class Git(VersionControl):
+    name = "git"
+    dirname = ".git"
+    repo_name = "clone"
+    schemes = (
+        "git+http",
+        "git+https",
+        "git+ssh",
+        "git+git",
+        "git+file",
+    )
+    # Prevent the user's environment variables from interfering with pip:
+    # https://github.com/pypa/pip/issues/1130
+    unset_environ = ("GIT_DIR", "GIT_WORK_TREE")
+    default_arg_rev = "HEAD"
+
+    @staticmethod
+    def get_base_rev_args(rev: str) -> list[str]:
+        return [rev]
+
+    @classmethod
+    def run_command(cls, *args: Any, **kwargs: Any) -> str:
+        if os.environ.get("PIP_NO_INPUT"):
+            extra_environ = kwargs.get("extra_environ", {})
+            extra_environ["GIT_TERMINAL_PROMPT"] = "0"
+            extra_environ["GIT_SSH_COMMAND"] = "ssh -oBatchMode=yes"
+            kwargs["extra_environ"] = extra_environ
+        return super().run_command(*args, **kwargs)
+
+    def is_immutable_rev_checkout(self, url: str, dest: str) -> bool:
+        _, rev_options = self.get_url_rev_options(hide_url(url))
+        if not rev_options.rev:
+            return False
+        if not self.is_commit_id_equal(dest, rev_options.rev):
+            # the current commit is different from rev,
+            # which means rev was something else than a commit hash
+            return False
+        # return False in the rare case rev is both a commit hash
+        # and a tag or a branch; we don't want to cache in that case
+        # because that branch/tag could point to something else in the future
+        is_tag_or_branch = bool(self.get_revision_sha(dest, rev_options.rev)[0])
+        return not is_tag_or_branch
+
+    def get_git_version(self) -> tuple[int, ...]:
+        version = self.run_command(
+            ["version"],
+            command_desc="git version",
+            show_stdout=False,
+            stdout_only=True,
+        )
+        match = GIT_VERSION_REGEX.match(version)
+        if not match:
+            logger.warning("Can't parse git version: %s", version)
+            return ()
+        return (int(match.group(1)), int(match.group(2)))
+
+    @classmethod
+    def get_current_branch(cls, location: str) -> str | None:
+        """
+        Return the current branch, or None if HEAD isn't at a branch
+        (e.g. detached HEAD).
+        """
+        # git-symbolic-ref exits with empty stdout if "HEAD" is a detached
+        # HEAD rather than a symbolic ref.  In addition, the -q causes the
+        # command to exit with status code 1 instead of 128 in this case
+        # and to suppress the message to stderr.
+        args = ["symbolic-ref", "-q", "HEAD"]
+        output = cls.run_command(
+            args,
+            extra_ok_returncodes=(1,),
+            show_stdout=False,
+            stdout_only=True,
+            cwd=location,
+        )
+        ref = output.strip()
+
+        if ref.startswith("refs/heads/"):
+            return ref[len("refs/heads/") :]
+
+        return None
+
+    @classmethod
+    def get_revision_sha(cls, dest: str, rev: str) -> tuple[str | None, bool]:
+        """
+        Return (sha_or_none, is_branch), where sha_or_none is a commit hash
+        if the revision names a remote branch or tag, otherwise None.
+
+        Args:
+          dest: the repository directory.
+          rev: the revision name.
+        """
+        # Pass rev to pre-filter the list.
+        output = cls.run_command(
+            ["show-ref", rev],
+            cwd=dest,
+            show_stdout=False,
+            stdout_only=True,
+            on_returncode="ignore",
+        )
+        refs = {}
+        # NOTE: We do not use splitlines here since that would split on other
+        #       unicode separators, which can be maliciously used to install a
+        #       different revision.
+        for line in output.strip().split("\n"):
+            line = line.rstrip("\r")
+            if not line:
+                continue
+            try:
+                ref_sha, ref_name = line.split(" ", maxsplit=2)
+            except ValueError:
+                # Include the offending line to simplify troubleshooting if
+                # this error ever occurs.
+                raise ValueError(f"unexpected show-ref line: {line!r}")
+
+            refs[ref_name] = ref_sha
+
+        branch_ref = f"refs/remotes/origin/{rev}"
+        tag_ref = f"refs/tags/{rev}"
+
+        sha = refs.get(branch_ref)
+        if sha is not None:
+            return (sha, True)
+
+        sha = refs.get(tag_ref)
+
+        return (sha, False)
+
+    @classmethod
+    def _should_fetch(cls, dest: str, rev: str) -> bool:
+        """
+        Return true if rev is a ref or is a commit that we don't have locally.
+
+        Branches and tags are not considered in this method because they are
+        assumed to be always available locally (which is a normal outcome of
+        ``git clone`` and ``git fetch --tags``).
+        """
+        if rev.startswith("refs/"):
+            # Always fetch remote refs.
+            return True
+
+        if not looks_like_hash(rev):
+            # Git fetch would fail with abbreviated commits.
+            return False
+
+        if cls.has_commit(dest, rev):
+            # Don't fetch if we have the commit locally.
+            return False
+
+        return True
+
+    @classmethod
+    def resolve_revision(
+        cls, dest: str, url: HiddenText, rev_options: RevOptions
+    ) -> RevOptions:
+        """
+        Resolve a revision to a new RevOptions object with the SHA1 of the
+        branch, tag, or ref if found.
+
+        Args:
+          rev_options: a RevOptions object.
+        """
+        rev = rev_options.arg_rev
+        # The arg_rev property's implementation for Git ensures that the
+        # rev return value is always non-None.
+        assert rev is not None
+
+        sha, is_branch = cls.get_revision_sha(dest, rev)
+
+        if sha is not None:
+            rev_options = rev_options.make_new(sha)
+            rev_options = replace(rev_options, branch_name=(rev if is_branch else None))
+
+            return rev_options
+
+        # Do not show a warning for the common case of something that has
+        # the form of a Git commit hash.
+        if not looks_like_hash(rev):
+            logger.info(
+                "Did not find branch or tag '%s', assuming revision or ref.",
+                rev,
+            )
+
+        if not cls._should_fetch(dest, rev):
+            return rev_options
+
+        # fetch the requested revision
+        cls.run_command(
+            make_command("fetch", "-q", url, rev_options.to_args()),
+            cwd=dest,
+        )
+        # Change the revision to the SHA of the ref we fetched
+        sha = cls.get_revision(dest, rev="FETCH_HEAD")
+        rev_options = rev_options.make_new(sha)
+
+        return rev_options
+
+    @classmethod
+    def is_commit_id_equal(cls, dest: str, name: str | None) -> bool:
+        """
+        Return whether the current commit hash equals the given name.
+
+        Args:
+          dest: the repository directory.
+          name: a string name.
+        """
+        if not name:
+            # Then avoid an unnecessary subprocess call.
+            return False
+
+        return cls.get_revision(dest) == name
+
+    def fetch_new(
+        self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
+    ) -> None:
+        rev_display = rev_options.to_display()
+        logger.info("Cloning %s%s to %s", url, rev_display, display_path(dest))
+        if verbosity <= 0:
+            flags: tuple[str, ...] = ("--quiet",)
+        elif verbosity == 1:
+            flags = ()
+        else:
+            flags = ("--verbose", "--progress")
+        if self.get_git_version() >= (2, 17) and not strtobool(
+            os.environ.get("PIP_NO_PARTIAL_CLONE_FOR_BROKEN_GIT_SERVER", "no")
+        ):
+            # Git added support for partial clone in 2.17
+            # https://git-scm.com/docs/partial-clone
+            # Speeds up cloning by functioning without a complete copy of repository
+            self.run_command(
+                make_command(
+                    "clone",
+                    "--filter=blob:none",
+                    *flags,
+                    url,
+                    dest,
+                )
+            )
+        else:
+            self.run_command(make_command("clone", *flags, url, dest))
+
+        if rev_options.rev:
+            # Then a specific revision was requested.
+            rev_options = self.resolve_revision(dest, url, rev_options)
+            branch_name = getattr(rev_options, "branch_name", None)
+            logger.debug("Rev options %s, branch_name %s", rev_options, branch_name)
+            if branch_name is None:
+                # Only do a checkout if the current commit id doesn't match
+                # the requested revision.
+                if not self.is_commit_id_equal(dest, rev_options.rev):
+                    cmd_args = make_command(
+                        "checkout",
+                        "-q",
+                        rev_options.to_args(),
+                    )
+                    self.run_command(cmd_args, cwd=dest)
+            elif self.get_current_branch(dest) != branch_name:
+                # Then a specific branch was requested, and that branch
+                # is not yet checked out.
+                track_branch = f"origin/{branch_name}"
+                cmd_args = [
+                    "checkout",
+                    "-b",
+                    branch_name,
+                    "--track",
+                    track_branch,
+                ]
+                self.run_command(cmd_args, cwd=dest)
+        else:
+            sha = self.get_revision(dest)
+            rev_options = rev_options.make_new(sha)
+
+        logger.info("Resolved %s to commit %s", url, rev_options.rev)
+
+        #: repo may contain submodules
+        self.update_submodules(dest, verbosity=verbosity)
+
+    def switch(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        self.run_command(
+            make_command("config", "remote.origin.url", url),
+            cwd=dest,
+        )
+
+        extra_flags = []
+
+        if verbosity <= 0:
+            extra_flags.append("-q")
+
+        cmd_args = make_command("checkout", *extra_flags, rev_options.to_args())
+        self.run_command(cmd_args, cwd=dest)
+
+        self.update_submodules(dest, verbosity=verbosity)
+
+    def update(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        extra_flags = []
+
+        if verbosity <= 0:
+            extra_flags.append("-q")
+
+        # First fetch changes from the default remote
+        if self.get_git_version() >= (1, 9):
+            # fetch tags in addition to everything else
+            self.run_command(["fetch", "--tags", *extra_flags], cwd=dest)
+        else:
+            self.run_command(["fetch", *extra_flags], cwd=dest)
+        # Then reset to wanted revision (maybe even origin/master)
+        rev_options = self.resolve_revision(dest, url, rev_options)
+        cmd_args = make_command(
+            "reset",
+            "--hard",
+            *extra_flags,
+            rev_options.to_args(),
+        )
+        self.run_command(cmd_args, cwd=dest)
+        #: update submodules
+        self.update_submodules(dest, verbosity=verbosity)
+
+    @classmethod
+    def get_remote_url(cls, location: str) -> str:
+        """
+        Return URL of the first remote encountered.
+
+        Raises RemoteNotFoundError if the repository does not have a remote
+        url configured.
+        """
+        # We need to pass 1 for extra_ok_returncodes since the command
+        # exits with return code 1 if there are no matching lines.
+        stdout = cls.run_command(
+            ["config", "--get-regexp", r"remote\..*\.url"],
+            extra_ok_returncodes=(1,),
+            show_stdout=False,
+            stdout_only=True,
+            cwd=location,
+        )
+        remotes = stdout.splitlines()
+        try:
+            found_remote = remotes[0]
+        except IndexError:
+            raise RemoteNotFoundError
+
+        for remote in remotes:
+            if remote.startswith("remote.origin.url "):
+                found_remote = remote
+                break
+        url = found_remote.split(" ")[1]
+        return cls._git_remote_to_pip_url(url.strip())
+
+    @staticmethod
+    def _git_remote_to_pip_url(url: str) -> str:
+        """
+        Convert a remote url from what git uses to what pip accepts.
+
+        There are 3 legal forms **url** may take:
+
+            1. A fully qualified url: ssh://git@example.com/foo/bar.git
+            2. A local project.git folder: /path/to/bare/repository.git
+            3. SCP shorthand for form 1: git@example.com:foo/bar.git
+
+        Form 1 is output as-is. Form 2 must be converted to URI and form 3 must
+        be converted to form 1.
+
+        See the corresponding test test_git_remote_url_to_pip() for examples of
+        sample inputs/outputs.
+        """
+        if re.match(r"\w+://", url):
+            # This is already valid. Pass it though as-is.
+            return url
+        if os.path.exists(url):
+            # A local bare remote (git clone --mirror).
+            # Needs a file:// prefix.
+            return pathlib.Path(url).as_uri()
+        scp_match = SCP_REGEX.match(url)
+        if scp_match:
+            # Add an ssh:// prefix and replace the ':' with a '/'.
+            return scp_match.expand(r"ssh://\1\2/\3")
+        # Otherwise, bail out.
+        raise RemoteNotValidError(url)
+
+    @classmethod
+    def has_commit(cls, location: str, rev: str) -> bool:
+        """
+        Check if rev is a commit that is available in the local repository.
+        """
+        try:
+            cls.run_command(
+                ["rev-parse", "-q", "--verify", rev + "^{commit}"],
+                cwd=location,
+                log_failed_cmd=False,
+            )
+        except InstallationError:
+            return False
+        else:
+            return True
+
+    @classmethod
+    def get_revision(cls, location: str, rev: str | None = None) -> str:
+        if rev is None:
+            rev = "HEAD"
+        current_rev = cls.run_command(
+            ["rev-parse", rev],
+            show_stdout=False,
+            stdout_only=True,
+            cwd=location,
+        )
+        return current_rev.strip()
+
+    @classmethod
+    def get_subdirectory(cls, location: str) -> str | None:
+        """
+        Return the path to Python project root, relative to the repo root.
+        Return None if the project root is in the repo root.
+        """
+        # find the repo root
+        git_dir = cls.run_command(
+            ["rev-parse", "--git-dir"],
+            show_stdout=False,
+            stdout_only=True,
+            cwd=location,
+        ).strip()
+        if not os.path.isabs(git_dir):
+            git_dir = os.path.join(location, git_dir)
+        repo_root = os.path.abspath(os.path.join(git_dir, ".."))
+        return find_path_to_project_root_from_repo_root(location, repo_root)
+
+    @classmethod
+    def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]:
+        """
+        Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'.
+        That's required because although they use SSH they sometimes don't
+        work with a ssh:// scheme (e.g. GitHub). But we need a scheme for
+        parsing. Hence we remove it again afterwards and return it as a stub.
+        """
+        import urllib.request
+
+        # Works around an apparent Git bug
+        # (see https://article.gmane.org/gmane.comp.version-control.git/146500)
+        scheme, netloc, path, query, fragment = urlsplit(url)
+        if scheme.endswith("file"):
+            initial_slashes = path[: -len(path.lstrip("/"))]
+            newpath = initial_slashes + urllib.request.url2pathname(path).replace(
+                "\\", "/"
+            ).lstrip("/")
+            after_plus = scheme.find("+") + 1
+            url = scheme[:after_plus] + urlunsplit(
+                (scheme[after_plus:], netloc, newpath, query, fragment),
+            )
+
+        if "://" not in url:
+            assert "file:" not in url
+            url = url.replace("git+", "git+ssh://")
+            url, rev, user_pass = super().get_url_rev_and_auth(url)
+            url = url.replace("ssh://", "")
+        else:
+            url, rev, user_pass = super().get_url_rev_and_auth(url)
+
+        return url, rev, user_pass
+
+    @classmethod
+    def update_submodules(cls, location: str, verbosity: int = 0) -> None:
+        argv = ["submodule", "update", "--init", "--recursive"]
+
+        if verbosity <= 0:
+            argv.append("-q")
+
+        if not os.path.exists(os.path.join(location, ".gitmodules")):
+            return
+        cls.run_command(
+            argv,
+            cwd=location,
+        )
+
+    @classmethod
+    def get_repository_root(cls, location: str) -> str | None:
+        loc = super().get_repository_root(location)
+        if loc:
+            return loc
+        try:
+            r = cls.run_command(
+                ["rev-parse", "--show-toplevel"],
+                cwd=location,
+                show_stdout=False,
+                stdout_only=True,
+                on_returncode="raise",
+                log_failed_cmd=False,
+            )
+        except BadCommand:
+            logger.debug(
+                "could not determine if %s is under git control "
+                "because git is not available",
+                location,
+            )
+            return None
+        except InstallationError:
+            return None
+        return os.path.normpath(r.rstrip("\r\n"))
+
+    @staticmethod
+    def should_add_vcs_url_prefix(repo_url: str) -> bool:
+        """In either https or ssh form, requirements must be prefixed with git+."""
+        return True
+
+
+vcs.register(Git)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/vcs/mercurial.py b/venv/lib/python3.12/site-packages/pip/_internal/vcs/mercurial.py
new file mode 100644
index 0000000..c875803
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/vcs/mercurial.py
@@ -0,0 +1,186 @@
+from __future__ import annotations
+
+import configparser
+import logging
+import os
+
+from pip._internal.exceptions import BadCommand, InstallationError
+from pip._internal.utils.misc import HiddenText, display_path
+from pip._internal.utils.subprocess import make_command
+from pip._internal.utils.urls import path_to_url
+from pip._internal.vcs.versioncontrol import (
+    RevOptions,
+    VersionControl,
+    find_path_to_project_root_from_repo_root,
+    vcs,
+)
+
+logger = logging.getLogger(__name__)
+
+
+class Mercurial(VersionControl):
+    name = "hg"
+    dirname = ".hg"
+    repo_name = "clone"
+    schemes = (
+        "hg+file",
+        "hg+http",
+        "hg+https",
+        "hg+ssh",
+        "hg+static-http",
+    )
+
+    @staticmethod
+    def get_base_rev_args(rev: str) -> list[str]:
+        return [f"--rev={rev}"]
+
+    def fetch_new(
+        self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
+    ) -> None:
+        rev_display = rev_options.to_display()
+        logger.info(
+            "Cloning hg %s%s to %s",
+            url,
+            rev_display,
+            display_path(dest),
+        )
+        if verbosity <= 0:
+            flags: tuple[str, ...] = ("--quiet",)
+        elif verbosity == 1:
+            flags = ()
+        elif verbosity == 2:
+            flags = ("--verbose",)
+        else:
+            flags = ("--verbose", "--debug")
+        self.run_command(make_command("clone", "--noupdate", *flags, url, dest))
+        self.run_command(
+            make_command("update", *flags, rev_options.to_args()),
+            cwd=dest,
+        )
+
+    def switch(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        extra_flags = []
+        repo_config = os.path.join(dest, self.dirname, "hgrc")
+        config = configparser.RawConfigParser()
+
+        if verbosity <= 0:
+            extra_flags.append("-q")
+
+        try:
+            config.read(repo_config)
+            config.set("paths", "default", url.secret)
+            with open(repo_config, "w") as config_file:
+                config.write(config_file)
+        except (OSError, configparser.NoSectionError) as exc:
+            logger.warning("Could not switch Mercurial repository to %s: %s", url, exc)
+        else:
+            cmd_args = make_command("update", *extra_flags, rev_options.to_args())
+            self.run_command(cmd_args, cwd=dest)
+
+    def update(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        extra_flags = []
+
+        if verbosity <= 0:
+            extra_flags.append("-q")
+
+        self.run_command(["pull", *extra_flags], cwd=dest)
+        cmd_args = make_command("update", *extra_flags, rev_options.to_args())
+        self.run_command(cmd_args, cwd=dest)
+
+    @classmethod
+    def get_remote_url(cls, location: str) -> str:
+        url = cls.run_command(
+            ["showconfig", "paths.default"],
+            show_stdout=False,
+            stdout_only=True,
+            cwd=location,
+        ).strip()
+        if cls._is_local_repository(url):
+            url = path_to_url(url)
+        return url.strip()
+
+    @classmethod
+    def get_revision(cls, location: str) -> str:
+        """
+        Return the repository-local changeset revision number, as an integer.
+        """
+        current_revision = cls.run_command(
+            ["parents", "--template={rev}"],
+            show_stdout=False,
+            stdout_only=True,
+            cwd=location,
+        ).strip()
+        return current_revision
+
+    @classmethod
+    def get_requirement_revision(cls, location: str) -> str:
+        """
+        Return the changeset identification hash, as a 40-character
+        hexadecimal string
+        """
+        current_rev_hash = cls.run_command(
+            ["parents", "--template={node}"],
+            show_stdout=False,
+            stdout_only=True,
+            cwd=location,
+        ).strip()
+        return current_rev_hash
+
+    @classmethod
+    def is_commit_id_equal(cls, dest: str, name: str | None) -> bool:
+        """Always assume the versions don't match"""
+        return False
+
+    @classmethod
+    def get_subdirectory(cls, location: str) -> str | None:
+        """
+        Return the path to Python project root, relative to the repo root.
+        Return None if the project root is in the repo root.
+        """
+        # find the repo root
+        repo_root = cls.run_command(
+            ["root"], show_stdout=False, stdout_only=True, cwd=location
+        ).strip()
+        if not os.path.isabs(repo_root):
+            repo_root = os.path.abspath(os.path.join(location, repo_root))
+        return find_path_to_project_root_from_repo_root(location, repo_root)
+
+    @classmethod
+    def get_repository_root(cls, location: str) -> str | None:
+        loc = super().get_repository_root(location)
+        if loc:
+            return loc
+        try:
+            r = cls.run_command(
+                ["root"],
+                cwd=location,
+                show_stdout=False,
+                stdout_only=True,
+                on_returncode="raise",
+                log_failed_cmd=False,
+            )
+        except BadCommand:
+            logger.debug(
+                "could not determine if %s is under hg control "
+                "because hg is not available",
+                location,
+            )
+            return None
+        except InstallationError:
+            return None
+        return os.path.normpath(r.rstrip("\r\n"))
+
+
+vcs.register(Mercurial)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/vcs/subversion.py b/venv/lib/python3.12/site-packages/pip/_internal/vcs/subversion.py
new file mode 100644
index 0000000..579f428
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/vcs/subversion.py
@@ -0,0 +1,335 @@
+from __future__ import annotations
+
+import logging
+import os
+import re
+
+from pip._internal.utils.misc import (
+    HiddenText,
+    display_path,
+    is_console_interactive,
+    is_installable_dir,
+    split_auth_from_netloc,
+)
+from pip._internal.utils.subprocess import CommandArgs, make_command
+from pip._internal.vcs.versioncontrol import (
+    AuthInfo,
+    RemoteNotFoundError,
+    RevOptions,
+    VersionControl,
+    vcs,
+)
+
+logger = logging.getLogger(__name__)
+
+_svn_xml_url_re = re.compile('url="([^"]+)"')
+_svn_rev_re = re.compile(r'committed-rev="(\d+)"')
+_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"')
+_svn_info_xml_url_re = re.compile(r"(.*)")
+
+
+class Subversion(VersionControl):
+    name = "svn"
+    dirname = ".svn"
+    repo_name = "checkout"
+    schemes = ("svn+ssh", "svn+http", "svn+https", "svn+svn", "svn+file")
+
+    @classmethod
+    def should_add_vcs_url_prefix(cls, remote_url: str) -> bool:
+        return True
+
+    @staticmethod
+    def get_base_rev_args(rev: str) -> list[str]:
+        return ["-r", rev]
+
+    @classmethod
+    def get_revision(cls, location: str) -> str:
+        """
+        Return the maximum revision for all files under a given location
+        """
+        # Note: taken from setuptools.command.egg_info
+        revision = 0
+
+        for base, dirs, _ in os.walk(location):
+            if cls.dirname not in dirs:
+                dirs[:] = []
+                continue  # no sense walking uncontrolled subdirs
+            dirs.remove(cls.dirname)
+            entries_fn = os.path.join(base, cls.dirname, "entries")
+            if not os.path.exists(entries_fn):
+                # FIXME: should we warn?
+                continue
+
+            dirurl, localrev = cls._get_svn_url_rev(base)
+
+            if base == location:
+                assert dirurl is not None
+                base = dirurl + "/"  # save the root url
+            elif not dirurl or not dirurl.startswith(base):
+                dirs[:] = []
+                continue  # not part of the same svn tree, skip it
+            revision = max(revision, localrev)
+        return str(revision)
+
+    @classmethod
+    def get_netloc_and_auth(
+        cls, netloc: str, scheme: str
+    ) -> tuple[str, tuple[str | None, str | None]]:
+        """
+        This override allows the auth information to be passed to svn via the
+        --username and --password options instead of via the URL.
+        """
+        if scheme == "ssh":
+            # The --username and --password options can't be used for
+            # svn+ssh URLs, so keep the auth information in the URL.
+            return super().get_netloc_and_auth(netloc, scheme)
+
+        return split_auth_from_netloc(netloc)
+
+    @classmethod
+    def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]:
+        # hotfix the URL scheme after removing svn+ from svn+ssh:// re-add it
+        url, rev, user_pass = super().get_url_rev_and_auth(url)
+        if url.startswith("ssh://"):
+            url = "svn+" + url
+        return url, rev, user_pass
+
+    @staticmethod
+    def make_rev_args(username: str | None, password: HiddenText | None) -> CommandArgs:
+        extra_args: CommandArgs = []
+        if username:
+            extra_args += ["--username", username]
+        if password:
+            extra_args += ["--password", password]
+
+        return extra_args
+
+    @classmethod
+    def get_remote_url(cls, location: str) -> str:
+        # In cases where the source is in a subdirectory, we have to look up in
+        # the location until we find a valid project root.
+        orig_location = location
+        while not is_installable_dir(location):
+            last_location = location
+            location = os.path.dirname(location)
+            if location == last_location:
+                # We've traversed up to the root of the filesystem without
+                # finding a Python project.
+                logger.warning(
+                    "Could not find Python project for directory %s (tried all "
+                    "parent directories)",
+                    orig_location,
+                )
+                raise RemoteNotFoundError
+
+        url, _rev = cls._get_svn_url_rev(location)
+        if url is None:
+            raise RemoteNotFoundError
+
+        return url
+
+    @classmethod
+    def _get_svn_url_rev(cls, location: str) -> tuple[str | None, int]:
+        from pip._internal.exceptions import InstallationError
+
+        entries_path = os.path.join(location, cls.dirname, "entries")
+        if os.path.exists(entries_path):
+            with open(entries_path) as f:
+                data = f.read()
+        else:  # subversion >= 1.7 does not have the 'entries' file
+            data = ""
+
+        url = None
+        if data.startswith(("8", "9", "10")):
+            entries = list(map(str.splitlines, data.split("\n\x0c\n")))
+            del entries[0][0]  # get rid of the '8'
+            url = entries[0][3]
+            revs = [int(d[9]) for d in entries if len(d) > 9 and d[9]] + [0]
+        elif data.startswith("= 1.7
+                # Note that using get_remote_call_options is not necessary here
+                # because `svn info` is being run against a local directory.
+                # We don't need to worry about making sure interactive mode
+                # is being used to prompt for passwords, because passwords
+                # are only potentially needed for remote server requests.
+                xml = cls.run_command(
+                    ["info", "--xml", location],
+                    show_stdout=False,
+                    stdout_only=True,
+                )
+                match = _svn_info_xml_url_re.search(xml)
+                assert match is not None
+                url = match.group(1)
+                revs = [int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml)]
+            except InstallationError:
+                url, revs = None, []
+
+        if revs:
+            rev = max(revs)
+        else:
+            rev = 0
+
+        return url, rev
+
+    @classmethod
+    def is_commit_id_equal(cls, dest: str, name: str | None) -> bool:
+        """Always assume the versions don't match"""
+        return False
+
+    def __init__(self, use_interactive: bool | None = None) -> None:
+        if use_interactive is None:
+            use_interactive = is_console_interactive()
+        self.use_interactive = use_interactive
+
+        # This member is used to cache the fetched version of the current
+        # ``svn`` client.
+        # Special value definitions:
+        #   None: Not evaluated yet.
+        #   Empty tuple: Could not parse version.
+        self._vcs_version: tuple[int, ...] | None = None
+
+        super().__init__()
+
+    def call_vcs_version(self) -> tuple[int, ...]:
+        """Query the version of the currently installed Subversion client.
+
+        :return: A tuple containing the parts of the version information or
+            ``()`` if the version returned from ``svn`` could not be parsed.
+        :raises: BadCommand: If ``svn`` is not installed.
+        """
+        # Example versions:
+        #   svn, version 1.10.3 (r1842928)
+        #      compiled Feb 25 2019, 14:20:39 on x86_64-apple-darwin17.0.0
+        #   svn, version 1.7.14 (r1542130)
+        #      compiled Mar 28 2018, 08:49:13 on x86_64-pc-linux-gnu
+        #   svn, version 1.12.0-SlikSvn (SlikSvn/1.12.0)
+        #      compiled May 28 2019, 13:44:56 on x86_64-microsoft-windows6.2
+        version_prefix = "svn, version "
+        version = self.run_command(["--version"], show_stdout=False, stdout_only=True)
+        if not version.startswith(version_prefix):
+            return ()
+
+        version = version[len(version_prefix) :].split()[0]
+        version_list = version.partition("-")[0].split(".")
+        try:
+            parsed_version = tuple(map(int, version_list))
+        except ValueError:
+            return ()
+
+        return parsed_version
+
+    def get_vcs_version(self) -> tuple[int, ...]:
+        """Return the version of the currently installed Subversion client.
+
+        If the version of the Subversion client has already been queried,
+        a cached value will be used.
+
+        :return: A tuple containing the parts of the version information or
+            ``()`` if the version returned from ``svn`` could not be parsed.
+        :raises: BadCommand: If ``svn`` is not installed.
+        """
+        if self._vcs_version is not None:
+            # Use cached version, if available.
+            # If parsing the version failed previously (empty tuple),
+            # do not attempt to parse it again.
+            return self._vcs_version
+
+        vcs_version = self.call_vcs_version()
+        self._vcs_version = vcs_version
+        return vcs_version
+
+    def get_remote_call_options(self) -> CommandArgs:
+        """Return options to be used on calls to Subversion that contact the server.
+
+        These options are applicable for the following ``svn`` subcommands used
+        in this class.
+
+            - checkout
+            - switch
+            - update
+
+        :return: A list of command line arguments to pass to ``svn``.
+        """
+        if not self.use_interactive:
+            # --non-interactive switch is available since Subversion 0.14.4.
+            # Subversion < 1.8 runs in interactive mode by default.
+            return ["--non-interactive"]
+
+        svn_version = self.get_vcs_version()
+        # By default, Subversion >= 1.8 runs in non-interactive mode if
+        # stdin is not a TTY. Since that is how pip invokes SVN, in
+        # call_subprocess(), pip must pass --force-interactive to ensure
+        # the user can be prompted for a password, if required.
+        #   SVN added the --force-interactive option in SVN 1.8. Since
+        # e.g. RHEL/CentOS 7, which is supported until 2024, ships with
+        # SVN 1.7, pip should continue to support SVN 1.7. Therefore, pip
+        # can't safely add the option if the SVN version is < 1.8 (or unknown).
+        if svn_version >= (1, 8):
+            return ["--force-interactive"]
+
+        return []
+
+    def fetch_new(
+        self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
+    ) -> None:
+        rev_display = rev_options.to_display()
+        logger.info(
+            "Checking out %s%s to %s",
+            url,
+            rev_display,
+            display_path(dest),
+        )
+        if verbosity <= 0:
+            flags = ["--quiet"]
+        else:
+            flags = []
+        cmd_args = make_command(
+            "checkout",
+            *flags,
+            self.get_remote_call_options(),
+            rev_options.to_args(),
+            url,
+            dest,
+        )
+        self.run_command(cmd_args)
+
+    def switch(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        cmd_args = make_command(
+            "switch",
+            self.get_remote_call_options(),
+            rev_options.to_args(),
+            url,
+            dest,
+        )
+        self.run_command(cmd_args)
+
+    def update(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        cmd_args = make_command(
+            "update",
+            self.get_remote_call_options(),
+            rev_options.to_args(),
+            dest,
+        )
+        self.run_command(cmd_args)
+
+
+vcs.register(Subversion)
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/vcs/versioncontrol.py b/venv/lib/python3.12/site-packages/pip/_internal/vcs/versioncontrol.py
new file mode 100644
index 0000000..33c6b7f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/vcs/versioncontrol.py
@@ -0,0 +1,694 @@
+"""Handles all VCS (version control) support"""
+
+from __future__ import annotations
+
+import logging
+import os
+import shutil
+import sys
+import urllib.parse
+from collections.abc import Iterable, Iterator, Mapping
+from dataclasses import dataclass, field
+from typing import (
+    Any,
+    Literal,
+)
+
+from pip._internal.cli.spinners import SpinnerInterface
+from pip._internal.exceptions import BadCommand, InstallationError
+from pip._internal.utils.misc import (
+    HiddenText,
+    ask_path_exists,
+    backup_dir,
+    display_path,
+    hide_url,
+    hide_value,
+    is_installable_dir,
+    rmtree,
+)
+from pip._internal.utils.subprocess import (
+    CommandArgs,
+    call_subprocess,
+    format_command_args,
+    make_command,
+)
+
+__all__ = ["vcs"]
+
+
+logger = logging.getLogger(__name__)
+
+AuthInfo = tuple[str | None, str | None]
+
+
+def is_url(name: str) -> bool:
+    """
+    Return true if the name looks like a URL.
+    """
+    scheme = urllib.parse.urlsplit(name).scheme
+    if not scheme:
+        return False
+    return scheme in ["http", "https", "file", "ftp"] + vcs.all_schemes
+
+
+def make_vcs_requirement_url(
+    repo_url: str, rev: str, project_name: str, subdir: str | None = None
+) -> str:
+    """
+    Return the URL for a VCS requirement.
+
+    Args:
+      repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+").
+      project_name: the (unescaped) project name.
+    """
+    quoted_rev = urllib.parse.quote(rev, "/")
+    egg_project_name = project_name.replace("-", "_")
+    req = f"{repo_url}@{quoted_rev}#egg={egg_project_name}"
+    if subdir:
+        req += f"&subdirectory={subdir}"
+
+    return req
+
+
+def find_path_to_project_root_from_repo_root(
+    location: str, repo_root: str
+) -> str | None:
+    """
+    Find the the Python project's root by searching up the filesystem from
+    `location`. Return the path to project root relative to `repo_root`.
+    Return None if the project root is `repo_root`, or cannot be found.
+    """
+    # find project root.
+    orig_location = location
+    while not is_installable_dir(location):
+        last_location = location
+        location = os.path.dirname(location)
+        if location == last_location:
+            # We've traversed up to the root of the filesystem without
+            # finding a Python project.
+            logger.warning(
+                "Could not find a Python project for directory %s (tried all "
+                "parent directories)",
+                orig_location,
+            )
+            return None
+
+    if os.path.samefile(repo_root, location):
+        return None
+
+    return os.path.relpath(location, repo_root)
+
+
+class RemoteNotFoundError(Exception):
+    pass
+
+
+class RemoteNotValidError(Exception):
+    def __init__(self, url: str):
+        super().__init__(url)
+        self.url = url
+
+
+@dataclass(frozen=True)
+class RevOptions:
+    """
+    Encapsulates a VCS-specific revision to install, along with any VCS
+    install options.
+
+    Args:
+        vc_class: a VersionControl subclass.
+        rev: the name of the revision to install.
+        extra_args: a list of extra options.
+    """
+
+    vc_class: type[VersionControl]
+    rev: str | None = None
+    extra_args: CommandArgs = field(default_factory=list)
+    branch_name: str | None = None
+
+    def __repr__(self) -> str:
+        return f""
+
+    @property
+    def arg_rev(self) -> str | None:
+        if self.rev is None:
+            return self.vc_class.default_arg_rev
+
+        return self.rev
+
+    def to_args(self) -> CommandArgs:
+        """
+        Return the VCS-specific command arguments.
+        """
+        args: CommandArgs = []
+        rev = self.arg_rev
+        if rev is not None:
+            args += self.vc_class.get_base_rev_args(rev)
+        args += self.extra_args
+
+        return args
+
+    def to_display(self) -> str:
+        if not self.rev:
+            return ""
+
+        return f" (to revision {self.rev})"
+
+    def make_new(self, rev: str) -> RevOptions:
+        """
+        Make a copy of the current instance, but with a new rev.
+
+        Args:
+          rev: the name of the revision for the new object.
+        """
+        return self.vc_class.make_rev_options(rev, extra_args=self.extra_args)
+
+
+class VcsSupport:
+    _registry: dict[str, VersionControl] = {}
+    schemes = ["ssh", "git", "hg", "bzr", "sftp", "svn"]
+
+    def __init__(self) -> None:
+        # Register more schemes with urlparse for various version control
+        # systems
+        urllib.parse.uses_netloc.extend(self.schemes)
+        super().__init__()
+
+    def __iter__(self) -> Iterator[str]:
+        return self._registry.__iter__()
+
+    @property
+    def backends(self) -> list[VersionControl]:
+        return list(self._registry.values())
+
+    @property
+    def dirnames(self) -> list[str]:
+        return [backend.dirname for backend in self.backends]
+
+    @property
+    def all_schemes(self) -> list[str]:
+        schemes: list[str] = []
+        for backend in self.backends:
+            schemes.extend(backend.schemes)
+        return schemes
+
+    def register(self, cls: type[VersionControl]) -> None:
+        if not hasattr(cls, "name"):
+            logger.warning("Cannot register VCS %s", cls.__name__)
+            return
+        if cls.name not in self._registry:
+            self._registry[cls.name] = cls()
+            logger.debug("Registered VCS backend: %s", cls.name)
+
+    def unregister(self, name: str) -> None:
+        if name in self._registry:
+            del self._registry[name]
+
+    def get_backend_for_dir(self, location: str) -> VersionControl | None:
+        """
+        Return a VersionControl object if a repository of that type is found
+        at the given directory.
+        """
+        vcs_backends = {}
+        for vcs_backend in self._registry.values():
+            repo_path = vcs_backend.get_repository_root(location)
+            if not repo_path:
+                continue
+            logger.debug("Determine that %s uses VCS: %s", location, vcs_backend.name)
+            vcs_backends[repo_path] = vcs_backend
+
+        if not vcs_backends:
+            return None
+
+        # Choose the VCS in the inner-most directory. Since all repository
+        # roots found here would be either `location` or one of its
+        # parents, the longest path should have the most path components,
+        # i.e. the backend representing the inner-most repository.
+        inner_most_repo_path = max(vcs_backends, key=len)
+        return vcs_backends[inner_most_repo_path]
+
+    def get_backend_for_scheme(self, scheme: str) -> VersionControl | None:
+        """
+        Return a VersionControl object or None.
+        """
+        for vcs_backend in self._registry.values():
+            if scheme in vcs_backend.schemes:
+                return vcs_backend
+        return None
+
+    def get_backend(self, name: str) -> VersionControl | None:
+        """
+        Return a VersionControl object or None.
+        """
+        name = name.lower()
+        return self._registry.get(name)
+
+
+vcs = VcsSupport()
+
+
+class VersionControl:
+    name = ""
+    dirname = ""
+    repo_name = ""
+    # List of supported schemes for this Version Control
+    schemes: tuple[str, ...] = ()
+    # Iterable of environment variable names to pass to call_subprocess().
+    unset_environ: tuple[str, ...] = ()
+    default_arg_rev: str | None = None
+
+    @classmethod
+    def should_add_vcs_url_prefix(cls, remote_url: str) -> bool:
+        """
+        Return whether the vcs prefix (e.g. "git+") should be added to a
+        repository's remote url when used in a requirement.
+        """
+        return not remote_url.lower().startswith(f"{cls.name}:")
+
+    @classmethod
+    def get_subdirectory(cls, location: str) -> str | None:
+        """
+        Return the path to Python project root, relative to the repo root.
+        Return None if the project root is in the repo root.
+        """
+        return None
+
+    @classmethod
+    def get_requirement_revision(cls, repo_dir: str) -> str:
+        """
+        Return the revision string that should be used in a requirement.
+        """
+        return cls.get_revision(repo_dir)
+
+    @classmethod
+    def get_src_requirement(cls, repo_dir: str, project_name: str) -> str:
+        """
+        Return the requirement string to use to redownload the files
+        currently at the given repository directory.
+
+        Args:
+          project_name: the (unescaped) project name.
+
+        The return value has a form similar to the following:
+
+            {repository_url}@{revision}#egg={project_name}
+        """
+        repo_url = cls.get_remote_url(repo_dir)
+
+        if cls.should_add_vcs_url_prefix(repo_url):
+            repo_url = f"{cls.name}+{repo_url}"
+
+        revision = cls.get_requirement_revision(repo_dir)
+        subdir = cls.get_subdirectory(repo_dir)
+        req = make_vcs_requirement_url(repo_url, revision, project_name, subdir=subdir)
+
+        return req
+
+    @staticmethod
+    def get_base_rev_args(rev: str) -> list[str]:
+        """
+        Return the base revision arguments for a vcs command.
+
+        Args:
+          rev: the name of a revision to install.  Cannot be None.
+        """
+        raise NotImplementedError
+
+    def is_immutable_rev_checkout(self, url: str, dest: str) -> bool:
+        """
+        Return true if the commit hash checked out at dest matches
+        the revision in url.
+
+        Always return False, if the VCS does not support immutable commit
+        hashes.
+
+        This method does not check if there are local uncommitted changes
+        in dest after checkout, as pip currently has no use case for that.
+        """
+        return False
+
+    @classmethod
+    def make_rev_options(
+        cls, rev: str | None = None, extra_args: CommandArgs | None = None
+    ) -> RevOptions:
+        """
+        Return a RevOptions object.
+
+        Args:
+          rev: the name of a revision to install.
+          extra_args: a list of extra options.
+        """
+        return RevOptions(cls, rev, extra_args=extra_args or [])
+
+    @classmethod
+    def _is_local_repository(cls, repo: str) -> bool:
+        """
+        posix absolute paths start with os.path.sep,
+        win32 ones start with drive (like c:\\folder)
+        """
+        drive, tail = os.path.splitdrive(repo)
+        return repo.startswith(os.path.sep) or bool(drive)
+
+    @classmethod
+    def get_netloc_and_auth(
+        cls, netloc: str, scheme: str
+    ) -> tuple[str, tuple[str | None, str | None]]:
+        """
+        Parse the repository URL's netloc, and return the new netloc to use
+        along with auth information.
+
+        Args:
+          netloc: the original repository URL netloc.
+          scheme: the repository URL's scheme without the vcs prefix.
+
+        This is mainly for the Subversion class to override, so that auth
+        information can be provided via the --username and --password options
+        instead of through the URL.  For other subclasses like Git without
+        such an option, auth information must stay in the URL.
+
+        Returns: (netloc, (username, password)).
+        """
+        return netloc, (None, None)
+
+    @classmethod
+    def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]:
+        """
+        Parse the repository URL to use, and return the URL, revision,
+        and auth info to use.
+
+        Returns: (url, rev, (username, password)).
+        """
+        scheme, netloc, path, query, frag = urllib.parse.urlsplit(url)
+        if "+" not in scheme:
+            raise ValueError(
+                f"Sorry, {url!r} is a malformed VCS url. "
+                "The format is +://, "
+                "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp"
+            )
+        # Remove the vcs prefix.
+        scheme = scheme.split("+", 1)[1]
+        netloc, user_pass = cls.get_netloc_and_auth(netloc, scheme)
+        rev = None
+        if "@" in path:
+            path, rev = path.rsplit("@", 1)
+            if not rev:
+                raise InstallationError(
+                    f"The URL {url!r} has an empty revision (after @) "
+                    "which is not supported. Include a revision after @ "
+                    "or remove @ from the URL."
+                )
+            rev = urllib.parse.unquote(rev)
+        url = urllib.parse.urlunsplit((scheme, netloc, path, query, ""))
+        return url, rev, user_pass
+
+    @staticmethod
+    def make_rev_args(username: str | None, password: HiddenText | None) -> CommandArgs:
+        """
+        Return the RevOptions "extra arguments" to use in obtain().
+        """
+        return []
+
+    def get_url_rev_options(self, url: HiddenText) -> tuple[HiddenText, RevOptions]:
+        """
+        Return the URL and RevOptions object to use in obtain(),
+        as a tuple (url, rev_options).
+        """
+        secret_url, rev, user_pass = self.get_url_rev_and_auth(url.secret)
+        username, secret_password = user_pass
+        password: HiddenText | None = None
+        if secret_password is not None:
+            password = hide_value(secret_password)
+        extra_args = self.make_rev_args(username, password)
+        rev_options = self.make_rev_options(rev, extra_args=extra_args)
+
+        return hide_url(secret_url), rev_options
+
+    @staticmethod
+    def normalize_url(url: str) -> str:
+        """
+        Normalize a URL for comparison by unquoting it and removing any
+        trailing slash.
+        """
+        return urllib.parse.unquote(url).rstrip("/")
+
+    @classmethod
+    def compare_urls(cls, url1: str, url2: str) -> bool:
+        """
+        Compare two repo URLs for identity, ignoring incidental differences.
+        """
+        return cls.normalize_url(url1) == cls.normalize_url(url2)
+
+    def fetch_new(
+        self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
+    ) -> None:
+        """
+        Fetch a revision from a repository, in the case that this is the
+        first fetch from the repository.
+
+        Args:
+          dest: the directory to fetch the repository to.
+          rev_options: a RevOptions object.
+          verbosity: verbosity level.
+        """
+        raise NotImplementedError
+
+    def switch(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        """
+        Switch the repo at ``dest`` to point to ``URL``.
+
+        Args:
+          rev_options: a RevOptions object.
+        """
+        raise NotImplementedError
+
+    def update(
+        self,
+        dest: str,
+        url: HiddenText,
+        rev_options: RevOptions,
+        verbosity: int = 0,
+    ) -> None:
+        """
+        Update an already-existing repo to the given ``rev_options``.
+
+        Args:
+          rev_options: a RevOptions object.
+        """
+        raise NotImplementedError
+
+    @classmethod
+    def is_commit_id_equal(cls, dest: str, name: str | None) -> bool:
+        """
+        Return whether the id of the current commit equals the given name.
+
+        Args:
+          dest: the repository directory.
+          name: a string name.
+        """
+        raise NotImplementedError
+
+    def obtain(self, dest: str, url: HiddenText, verbosity: int) -> None:
+        """
+        Install or update in editable mode the package represented by this
+        VersionControl object.
+
+        :param dest: the repository directory in which to install or update.
+        :param url: the repository URL starting with a vcs prefix.
+        :param verbosity: verbosity level.
+        """
+        url, rev_options = self.get_url_rev_options(url)
+
+        if not os.path.exists(dest):
+            self.fetch_new(dest, url, rev_options, verbosity=verbosity)
+            return
+
+        rev_display = rev_options.to_display()
+        if self.is_repository_directory(dest):
+            existing_url = self.get_remote_url(dest)
+            if self.compare_urls(existing_url, url.secret):
+                logger.debug(
+                    "%s in %s exists, and has correct URL (%s)",
+                    self.repo_name.title(),
+                    display_path(dest),
+                    url,
+                )
+                if not self.is_commit_id_equal(dest, rev_options.rev):
+                    logger.info(
+                        "Updating %s %s%s",
+                        display_path(dest),
+                        self.repo_name,
+                        rev_display,
+                    )
+                    self.update(dest, url, rev_options, verbosity=verbosity)
+                else:
+                    logger.info("Skipping because already up-to-date.")
+                return
+
+            logger.warning(
+                "%s %s in %s exists with URL %s",
+                self.name,
+                self.repo_name,
+                display_path(dest),
+                existing_url,
+            )
+            prompt = ("(s)witch, (i)gnore, (w)ipe, (b)ackup ", ("s", "i", "w", "b"))
+        else:
+            logger.warning(
+                "Directory %s already exists, and is not a %s %s.",
+                dest,
+                self.name,
+                self.repo_name,
+            )
+            # https://github.com/python/mypy/issues/1174
+            prompt = ("(i)gnore, (w)ipe, (b)ackup ", ("i", "w", "b"))  # type: ignore
+
+        logger.warning(
+            "The plan is to install the %s repository %s",
+            self.name,
+            url,
+        )
+        response = ask_path_exists(f"What to do?  {prompt[0]}", prompt[1])
+
+        if response == "a":
+            sys.exit(-1)
+
+        if response == "w":
+            logger.warning("Deleting %s", display_path(dest))
+            rmtree(dest)
+            self.fetch_new(dest, url, rev_options, verbosity=verbosity)
+            return
+
+        if response == "b":
+            dest_dir = backup_dir(dest)
+            logger.warning("Backing up %s to %s", display_path(dest), dest_dir)
+            shutil.move(dest, dest_dir)
+            self.fetch_new(dest, url, rev_options, verbosity=verbosity)
+            return
+
+        # Do nothing if the response is "i".
+        if response == "s":
+            logger.info(
+                "Switching %s %s to %s%s",
+                self.repo_name,
+                display_path(dest),
+                url,
+                rev_display,
+            )
+            self.switch(dest, url, rev_options, verbosity=verbosity)
+
+    def unpack(self, location: str, url: HiddenText, verbosity: int) -> None:
+        """
+        Clean up current location and download the url repository
+        (and vcs infos) into location
+
+        :param url: the repository URL starting with a vcs prefix.
+        :param verbosity: verbosity level.
+        """
+        if os.path.exists(location):
+            rmtree(location)
+        self.obtain(location, url=url, verbosity=verbosity)
+
+    @classmethod
+    def get_remote_url(cls, location: str) -> str:
+        """
+        Return the url used at location
+
+        Raises RemoteNotFoundError if the repository does not have a remote
+        url configured.
+        """
+        raise NotImplementedError
+
+    @classmethod
+    def get_revision(cls, location: str) -> str:
+        """
+        Return the current commit id of the files at the given location.
+        """
+        raise NotImplementedError
+
+    @classmethod
+    def run_command(
+        cls,
+        cmd: list[str] | CommandArgs,
+        show_stdout: bool = True,
+        cwd: str | None = None,
+        on_returncode: Literal["raise", "warn", "ignore"] = "raise",
+        extra_ok_returncodes: Iterable[int] | None = None,
+        command_desc: str | None = None,
+        extra_environ: Mapping[str, Any] | None = None,
+        spinner: SpinnerInterface | None = None,
+        log_failed_cmd: bool = True,
+        stdout_only: bool = False,
+    ) -> str:
+        """
+        Run a VCS subcommand
+        This is simply a wrapper around call_subprocess that adds the VCS
+        command name, and checks that the VCS is available
+        """
+        cmd = make_command(cls.name, *cmd)
+        if command_desc is None:
+            command_desc = format_command_args(cmd)
+        try:
+            return call_subprocess(
+                cmd,
+                show_stdout,
+                cwd,
+                on_returncode=on_returncode,
+                extra_ok_returncodes=extra_ok_returncodes,
+                command_desc=command_desc,
+                extra_environ=extra_environ,
+                unset_environ=cls.unset_environ,
+                spinner=spinner,
+                log_failed_cmd=log_failed_cmd,
+                stdout_only=stdout_only,
+            )
+        except NotADirectoryError:
+            raise BadCommand(f"Cannot find command {cls.name!r} - invalid PATH")
+        except FileNotFoundError:
+            # errno.ENOENT = no such file or directory
+            # In other words, the VCS executable isn't available
+            raise BadCommand(
+                f"Cannot find command {cls.name!r} - do you have "
+                f"{cls.name!r} installed and in your PATH?"
+            )
+        except PermissionError:
+            # errno.EACCES = Permission denied
+            # This error occurs, for instance, when the command is installed
+            # only for another user. So, the current user don't have
+            # permission to call the other user command.
+            raise BadCommand(
+                f"No permission to execute {cls.name!r} - install it "
+                f"locally, globally (ask admin), or check your PATH. "
+                f"See possible solutions at "
+                f"https://pip.pypa.io/en/latest/reference/pip_freeze/"
+                f"#fixing-permission-denied."
+            )
+
+    @classmethod
+    def is_repository_directory(cls, path: str) -> bool:
+        """
+        Return whether a directory path is a repository directory.
+        """
+        logger.debug("Checking in %s for %s (%s)...", path, cls.dirname, cls.name)
+        return os.path.exists(os.path.join(path, cls.dirname))
+
+    @classmethod
+    def get_repository_root(cls, location: str) -> str | None:
+        """
+        Return the "root" (top-level) directory controlled by the vcs,
+        or `None` if the directory is not in any.
+
+        It is meant to be overridden to implement smarter detection
+        mechanisms for specific vcs.
+
+        This can do more than is_repository_directory() alone. For
+        example, the Git override checks that Git is actually available.
+        """
+        if cls.is_repository_directory(location):
+            return location
+        return None
diff --git a/venv/lib/python3.12/site-packages/pip/_internal/wheel_builder.py b/venv/lib/python3.12/site-packages/pip/_internal/wheel_builder.py
new file mode 100644
index 0000000..8a55261
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_internal/wheel_builder.py
@@ -0,0 +1,265 @@
+"""Orchestrator for building wheels from InstallRequirements."""
+
+from __future__ import annotations
+
+import logging
+import os.path
+import re
+from collections.abc import Iterable
+from tempfile import TemporaryDirectory
+
+from pip._vendor.packaging.utils import canonicalize_name, canonicalize_version
+from pip._vendor.packaging.version import InvalidVersion, Version
+
+from pip._internal.cache import WheelCache
+from pip._internal.exceptions import InvalidWheelFilename, UnsupportedWheel
+from pip._internal.metadata import FilesystemWheel, get_wheel_distribution
+from pip._internal.models.link import Link
+from pip._internal.models.wheel import Wheel
+from pip._internal.operations.build.wheel import build_wheel_pep517
+from pip._internal.operations.build.wheel_editable import build_wheel_editable
+from pip._internal.req.req_install import InstallRequirement
+from pip._internal.utils.logging import indent_log
+from pip._internal.utils.misc import ensure_dir, hash_file
+from pip._internal.utils.urls import path_to_url
+from pip._internal.vcs import vcs
+
+logger = logging.getLogger(__name__)
+
+_egg_info_re = re.compile(r"([a-z0-9_.]+)-([a-z0-9_.!+-]+)", re.IGNORECASE)
+
+BuildResult = tuple[list[InstallRequirement], list[InstallRequirement]]
+
+
+def _contains_egg_info(s: str) -> bool:
+    """Determine whether the string looks like an egg_info.
+
+    :param s: The string to parse. E.g. foo-2.1
+    """
+    return bool(_egg_info_re.search(s))
+
+
+def _should_cache(
+    req: InstallRequirement,
+) -> bool | None:
+    """
+    Return whether a built InstallRequirement can be stored in the persistent
+    wheel cache, assuming the wheel cache is available.
+    """
+    if req.editable or not req.source_dir:
+        # never cache editable requirements
+        return False
+    if req.link and req.link.is_existing_dir():
+        # never cache local directory requirements
+        return False
+
+    if req.link and req.link.is_vcs:
+        # VCS checkout. Do not cache
+        # unless it points to an immutable commit hash.
+        assert not req.editable
+        assert req.source_dir
+        vcs_backend = vcs.get_backend_for_scheme(req.link.scheme)
+        assert vcs_backend
+        if vcs_backend.is_immutable_rev_checkout(req.link.url, req.source_dir):
+            return True
+        return False
+
+    assert req.link
+    base, ext = req.link.splitext()
+    if _contains_egg_info(base):
+        return True
+
+    # Otherwise, do not cache.
+    return False
+
+
+def _get_cache_dir(
+    req: InstallRequirement,
+    wheel_cache: WheelCache,
+) -> str:
+    """Return the persistent or temporary cache directory where the built
+    wheel need to be stored.
+    """
+    cache_available = bool(wheel_cache.cache_dir)
+    assert req.link
+    if cache_available and _should_cache(req):
+        cache_dir = wheel_cache.get_path_for_link(req.link)
+    else:
+        cache_dir = wheel_cache.get_ephem_path_for_link(req.link)
+    return cache_dir
+
+
+def _verify_one(req: InstallRequirement, wheel_path: str) -> None:
+    canonical_name = canonicalize_name(req.name or "")
+    w = Wheel(os.path.basename(wheel_path))
+    if w.name != canonical_name:
+        raise InvalidWheelFilename(
+            f"Wheel has unexpected file name: expected {canonical_name!r}, "
+            f"got {w.name!r}",
+        )
+    dist = get_wheel_distribution(FilesystemWheel(wheel_path), canonical_name)
+    dist_verstr = str(dist.version)
+    if canonicalize_version(dist_verstr) != canonicalize_version(w.version):
+        raise InvalidWheelFilename(
+            f"Wheel has unexpected file name: expected {dist_verstr!r}, "
+            f"got {w.version!r}",
+        )
+    metadata_version_value = dist.metadata_version
+    if metadata_version_value is None:
+        raise UnsupportedWheel("Missing Metadata-Version")
+    try:
+        metadata_version = Version(metadata_version_value)
+    except InvalidVersion:
+        msg = f"Invalid Metadata-Version: {metadata_version_value}"
+        raise UnsupportedWheel(msg)
+    if metadata_version >= Version("1.2") and not isinstance(dist.version, Version):
+        raise UnsupportedWheel(
+            f"Metadata 1.2 mandates PEP 440 version, but {dist_verstr!r} is not"
+        )
+
+
+def _build_one(
+    req: InstallRequirement,
+    output_dir: str,
+    verify: bool,
+    editable: bool,
+) -> str | None:
+    """Build one wheel.
+
+    :return: The filename of the built wheel, or None if the build failed.
+    """
+    artifact = "editable" if editable else "wheel"
+    try:
+        ensure_dir(output_dir)
+    except OSError as e:
+        logger.warning(
+            "Building %s for %s failed: %s",
+            artifact,
+            req.name,
+            e,
+        )
+        return None
+
+    # Install build deps into temporary directory (PEP 518)
+    with req.build_env:
+        wheel_path = _build_one_inside_env(req, output_dir, editable)
+    if wheel_path and verify:
+        try:
+            _verify_one(req, wheel_path)
+        except (InvalidWheelFilename, UnsupportedWheel) as e:
+            logger.warning("Built %s for %s is invalid: %s", artifact, req.name, e)
+            return None
+    return wheel_path
+
+
+def _build_one_inside_env(
+    req: InstallRequirement,
+    output_dir: str,
+    editable: bool,
+) -> str | None:
+    with TemporaryDirectory(dir=output_dir) as wheel_directory:
+        assert req.name
+        assert req.metadata_directory
+        assert req.pep517_backend
+        if editable:
+            wheel_path = build_wheel_editable(
+                name=req.name,
+                backend=req.pep517_backend,
+                metadata_directory=req.metadata_directory,
+                wheel_directory=wheel_directory,
+            )
+        else:
+            wheel_path = build_wheel_pep517(
+                name=req.name,
+                backend=req.pep517_backend,
+                metadata_directory=req.metadata_directory,
+                wheel_directory=wheel_directory,
+            )
+
+        if wheel_path is not None:
+            wheel_name = os.path.basename(wheel_path)
+            dest_path = os.path.join(output_dir, wheel_name)
+            try:
+                wheel_hash, length = hash_file(wheel_path)
+                # We can do a replace here because wheel_path is guaranteed to
+                # be in the same filesystem as output_dir. This will perform an
+                # atomic rename, which is necessary to avoid concurrency issues
+                # when populating the cache.
+                os.replace(wheel_path, dest_path)
+                logger.info(
+                    "Created wheel for %s: filename=%s size=%d sha256=%s",
+                    req.name,
+                    wheel_name,
+                    length,
+                    wheel_hash.hexdigest(),
+                )
+                logger.info("Stored in directory: %s", output_dir)
+                return dest_path
+            except Exception as e:
+                logger.warning(
+                    "Building wheel for %s failed: %s",
+                    req.name,
+                    e,
+                )
+        return None
+
+
+def build(
+    requirements: Iterable[InstallRequirement],
+    wheel_cache: WheelCache,
+    verify: bool,
+    allow_editables: bool,
+) -> BuildResult:
+    """Build wheels.
+
+    :return: The list of InstallRequirement that succeeded to build and
+        the list of InstallRequirement that failed to build.
+    """
+    if not requirements:
+        return [], []
+
+    # Build the wheels.
+    logger.info(
+        "Building wheels for collected packages: %s",
+        ", ".join(req.name for req in requirements),  # type: ignore
+    )
+
+    with indent_log():
+        build_successes, build_failures = [], []
+        for req in requirements:
+            assert req.name
+            cache_dir = _get_cache_dir(req, wheel_cache)
+            wheel_file = _build_one(
+                req,
+                cache_dir,
+                verify,
+                req.editable and allow_editables,
+            )
+            if wheel_file:
+                # Record the download origin in the cache
+                if req.download_info is not None:
+                    # download_info is guaranteed to be set because when we build an
+                    # InstallRequirement it has been through the preparer before, but
+                    # let's be cautious.
+                    wheel_cache.record_download_origin(cache_dir, req.download_info)
+                # Update the link for this.
+                req.link = Link(path_to_url(wheel_file))
+                req.local_file_path = req.link.file_path
+                assert req.link.is_wheel
+                build_successes.append(req)
+            else:
+                build_failures.append(req)
+
+    # notify success/failure
+    if build_successes:
+        logger.info(
+            "Successfully built %s",
+            " ".join([req.name for req in build_successes]),  # type: ignore
+        )
+    if build_failures:
+        logger.info(
+            "Failed to build %s",
+            " ".join([req.name for req in build_failures]),  # type: ignore
+        )
+    # Return a list of requirements that failed to build
+    return build_successes, build_failures
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/README.rst b/venv/lib/python3.12/site-packages/pip/_vendor/README.rst
new file mode 100644
index 0000000..7429a52
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/README.rst
@@ -0,0 +1,178 @@
+================
+Vendoring Policy
+================
+
+* Vendored libraries **MUST** not be modified except as required to
+  successfully vendor them.
+* Vendored libraries **MUST** be released copies of libraries available on
+  PyPI.
+* Vendored libraries **MUST** be available under a license that allows
+  them to be integrated into ``pip``, which is released under the MIT license.
+* Vendored libraries **MUST** be accompanied with LICENSE files.
+* The versions of libraries vendored in pip **MUST** be reflected in
+  ``pip/_vendor/vendor.txt``.
+* Vendored libraries **MUST** function without any build steps such as ``2to3``
+  or compilation of C code, practically this limits to single source 2.x/3.x and
+  pure Python.
+* Any modifications made to libraries **MUST** be noted in
+  ``pip/_vendor/README.rst`` and their corresponding patches **MUST** be
+  included ``tools/vendoring/patches``.
+* Vendored libraries should have corresponding ``vendored()`` entries in
+  ``pip/_vendor/__init__.py``.
+
+Rationale
+=========
+
+Historically pip has not had any dependencies except for ``setuptools`` itself,
+choosing instead to implement any functionality it needed to prevent needing
+a dependency. However, starting with pip 1.5, we began to replace code that was
+implemented inside of pip with reusable libraries from PyPI. This brought the
+typical benefits of reusing libraries instead of reinventing the wheel like
+higher quality and more battle tested code, centralization of bug fixes
+(particularly security sensitive ones), and better/more features for less work.
+
+However, there are several issues with having dependencies in the traditional
+way (via ``install_requires``) for pip. These issues are:
+
+**Fragility**
+   When pip depends on another library to function then if for whatever reason
+   that library either isn't installed or an incompatible version is installed
+   then pip ceases to function. This is of course true for all Python
+   applications, however for every application *except* for pip the way you fix
+   it is by re-running pip. Obviously, when pip can't run, you can't use pip to
+   fix pip, so you're left having to manually resolve dependencies and
+   installing them by hand.
+
+**Making other libraries uninstallable**
+   One of pip's current dependencies is the ``requests`` library, for which pip
+   requires a fairly recent version to run.  If pip depended on ``requests`` in
+   the traditional manner, then we'd either have to maintain compatibility with
+   every ``requests`` version that has ever existed (and ever will), OR allow
+   pip to render certain versions of ``requests`` uninstallable. (The second
+   issue, although technically true for any Python application, is magnified by
+   pip's ubiquity; pip is installed by default in Python, in ``pyvenv``, and in
+   ``virtualenv``.)
+
+**Security**
+   This might seem puzzling at first glance, since vendoring has a tendency to
+   complicate updating dependencies for security updates, and that holds true
+   for pip. However, given the *other* reasons for avoiding dependencies, the
+   alternative is for pip to reinvent the wheel itself.  This is what pip did
+   historically. It forced pip to re-implement its own HTTPS verification
+   routines as a workaround for the Python standard library's lack of SSL
+   validation, which resulted in similar bugs in the validation routine in
+   ``requests`` and ``urllib3``, except that they had to be discovered and
+   fixed independently. Even though we're vendoring, reusing libraries keeps
+   pip more secure by relying on the great work of our dependencies, *and*
+   allowing for faster, easier security fixes by simply pulling in newer
+   versions of dependencies.
+
+**Bootstrapping**
+   Currently most popular methods of installing pip rely on pip's
+   self-contained nature to install pip itself. These tools work by bundling a
+   copy of pip, adding it to ``sys.path``, and then executing that copy of pip.
+   This is done instead of implementing a "mini installer" (to reduce
+   duplication); pip already knows how to install a Python package, and is far
+   more battle-tested than any "mini installer" could ever possibly be.
+
+Many downstream redistributors have policies against this kind of bundling, and
+instead opt to patch the software they distribute to debundle it and make it
+rely on the global versions of the software that they already have packaged
+(which may have its own patches applied to it). We (the pip team) would prefer
+it if pip was *not* debundled in this manner due to the above reasons and
+instead we would prefer it if pip would be left intact as it is now.
+
+In the longer term, if someone has a *portable* solution to the above problems,
+other than the bundling method we currently use, that doesn't add additional
+problems that are unreasonable then we would be happy to consider, and possibly
+switch to said method. This solution must function correctly across all of the
+situation that we expect pip to be used and not mandate some external mechanism
+such as OS packages.
+
+
+Modifications
+=============
+
+* ``setuptools`` is completely stripped to only keep ``pkg_resources``.
+* ``pkg_resources`` has been modified to import its dependencies from
+  ``pip._vendor``, and to use the vendored copy of ``platformdirs``
+  rather than ``appdirs``.
+* ``packaging`` has been modified to import its dependencies from
+  ``pip._vendor``.
+* ``CacheControl`` has been modified to import its dependencies from
+  ``pip._vendor``.
+* ``requests`` has been modified to import its other dependencies from
+  ``pip._vendor`` and to *not* load ``simplejson`` (all platforms) and
+  ``pyopenssl`` (Windows).
+* ``platformdirs`` has been modified to import its submodules from ``pip._vendor.platformdirs``.
+
+Automatic Vendoring
+===================
+
+Vendoring is automated via the `vendoring `_ tool from the content of
+``pip/_vendor/vendor.txt`` and the different patches in
+``tools/vendoring/patches``.
+Launch it via ``vendoring sync . -v`` (requires ``vendoring>=0.2.2``).
+Tool configuration is done via ``pyproject.toml``.
+
+To update the vendored library versions, we have a session defined in ``nox``.
+The command to upgrade everything is::
+
+    nox -s vendoring -- --upgrade-all --skip setuptools
+
+We do not upgrade ``setuptools``, because we only rely on ``pkg_resources``,
+and tracking every ``setuptools`` change is unnecessary for our needs.
+
+
+Managing Local Patches
+======================
+
+The ``vendoring`` tool automatically applies our local patches, but updating,
+the patches sometimes no longer apply cleanly. In that case, the update will
+fail. To resolve this, take the following steps:
+
+1. Revert any incomplete changes in the revendoring branch, to ensure you have
+   a clean starting point.
+2. Run the revendoring of the library with a problem again: ``nox -s vendoring
+   -- --upgrade ``.
+3. This will fail again, but you will have the original source in your working
+   directory. Review the existing patch against the source, and modify the patch
+   to reflect the new version of the source. If you ``git add`` the changes the
+   vendoring made, you can modify the source to reflect the patch file and then
+   generate a new patch with ``git diff``.
+4. Now, revert everything *except* the patch file changes. Leave the modified
+   patch file unstaged but saved in the working tree.
+5. Re-run the vendoring. This time, it should pick up the changed patch file
+   and apply it cleanly. The patch file changes will be committed along with the
+   revendoring, so the new commit should be ready to test and publish as a PR.
+
+
+Debundling
+==========
+
+As mentioned in the rationale, we, the pip team, would prefer it if pip was not
+debundled (other than optionally ``pip/_vendor/requests/cacert.pem``) and that
+pip was left intact. However, if you insist on doing so, we have a
+semi-supported method (that we don't test in our CI) and requires a bit of
+extra work on your end in order to solve the problems described above.
+
+1. Delete everything in ``pip/_vendor/`` **except** for
+   ``pip/_vendor/__init__.py`` and ``pip/_vendor/vendor.txt``.
+2. Generate wheels for each of pip's dependencies (and any of their
+   dependencies) using your patched copies of these libraries. These must be
+   placed somewhere on the filesystem that pip can access (``pip/_vendor`` is
+   the default assumption).
+3. Modify ``pip/_vendor/__init__.py`` so that the ``DEBUNDLED`` variable is
+   ``True``.
+4. Upon installation, the ``INSTALLER`` file in pip's own ``dist-info``
+   directory should be set to something other than ``pip``, so that pip
+   can detect that it wasn't installed using itself.
+5. *(optional)* If you've placed the wheels in a location other than
+   ``pip/_vendor/``, then modify ``pip/_vendor/__init__.py`` so that the
+   ``WHEEL_DIR`` variable points to the location you've placed them.
+6. *(optional)* Update the ``pip_self_version_check`` logic to use the
+   appropriate logic for determining the latest available version of pip and
+   prompt the user with the correct upgrade message.
+
+Note that partial debundling is **NOT** supported. You need to prepare wheels
+for all dependencies for successful debundling.
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/__init__.py
new file mode 100644
index 0000000..34ccb99
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/__init__.py
@@ -0,0 +1,117 @@
+"""
+pip._vendor is for vendoring dependencies of pip to prevent needing pip to
+depend on something external.
+
+Files inside of pip._vendor should be considered immutable and should only be
+updated to versions from upstream.
+"""
+from __future__ import absolute_import
+
+import glob
+import os.path
+import sys
+
+# Downstream redistributors which have debundled our dependencies should also
+# patch this value to be true. This will trigger the additional patching
+# to cause things like "six" to be available as pip.
+DEBUNDLED = False
+
+# By default, look in this directory for a bunch of .whl files which we will
+# add to the beginning of sys.path before attempting to import anything. This
+# is done to support downstream re-distributors like Debian and Fedora who
+# wish to create their own Wheels for our dependencies to aid in debundling.
+WHEEL_DIR = os.path.abspath(os.path.dirname(__file__))
+
+
+# Define a small helper function to alias our vendored modules to the real ones
+# if the vendored ones do not exist. This idea of this was taken from
+# https://github.com/kennethreitz/requests/pull/2567.
+def vendored(modulename):
+    vendored_name = "{0}.{1}".format(__name__, modulename)
+
+    try:
+        __import__(modulename, globals(), locals(), level=0)
+    except ImportError:
+        # We can just silently allow import failures to pass here. If we
+        # got to this point it means that ``import pip._vendor.whatever``
+        # failed and so did ``import whatever``. Since we're importing this
+        # upfront in an attempt to alias imports, not erroring here will
+        # just mean we get a regular import error whenever pip *actually*
+        # tries to import one of these modules to use it, which actually
+        # gives us a better error message than we would have otherwise
+        # gotten.
+        pass
+    else:
+        sys.modules[vendored_name] = sys.modules[modulename]
+        base, head = vendored_name.rsplit(".", 1)
+        setattr(sys.modules[base], head, sys.modules[modulename])
+
+
+# If we're operating in a debundled setup, then we want to go ahead and trigger
+# the aliasing of our vendored libraries as well as looking for wheels to add
+# to our sys.path. This will cause all of this code to be a no-op typically
+# however downstream redistributors can enable it in a consistent way across
+# all platforms.
+if DEBUNDLED:
+    # Actually look inside of WHEEL_DIR to find .whl files and add them to the
+    # front of our sys.path.
+    sys.path[:] = glob.glob(os.path.join(WHEEL_DIR, "*.whl")) + sys.path
+
+    # Actually alias all of our vendored dependencies.
+    vendored("cachecontrol")
+    vendored("certifi")
+    vendored("dependency-groups")
+    vendored("distlib")
+    vendored("distro")
+    vendored("packaging")
+    vendored("packaging.version")
+    vendored("packaging.specifiers")
+    vendored("pkg_resources")
+    vendored("platformdirs")
+    vendored("progress")
+    vendored("pyproject_hooks")
+    vendored("requests")
+    vendored("requests.exceptions")
+    vendored("requests.packages")
+    vendored("requests.packages.urllib3")
+    vendored("requests.packages.urllib3._collections")
+    vendored("requests.packages.urllib3.connection")
+    vendored("requests.packages.urllib3.connectionpool")
+    vendored("requests.packages.urllib3.contrib")
+    vendored("requests.packages.urllib3.contrib.ntlmpool")
+    vendored("requests.packages.urllib3.contrib.pyopenssl")
+    vendored("requests.packages.urllib3.exceptions")
+    vendored("requests.packages.urllib3.fields")
+    vendored("requests.packages.urllib3.filepost")
+    vendored("requests.packages.urllib3.packages")
+    vendored("requests.packages.urllib3.packages.ordered_dict")
+    vendored("requests.packages.urllib3.packages.six")
+    vendored("requests.packages.urllib3.packages.ssl_match_hostname")
+    vendored("requests.packages.urllib3.packages.ssl_match_hostname."
+             "_implementation")
+    vendored("requests.packages.urllib3.poolmanager")
+    vendored("requests.packages.urllib3.request")
+    vendored("requests.packages.urllib3.response")
+    vendored("requests.packages.urllib3.util")
+    vendored("requests.packages.urllib3.util.connection")
+    vendored("requests.packages.urllib3.util.request")
+    vendored("requests.packages.urllib3.util.response")
+    vendored("requests.packages.urllib3.util.retry")
+    vendored("requests.packages.urllib3.util.ssl_")
+    vendored("requests.packages.urllib3.util.timeout")
+    vendored("requests.packages.urllib3.util.url")
+    vendored("resolvelib")
+    vendored("rich")
+    vendored("rich.console")
+    vendored("rich.highlighter")
+    vendored("rich.logging")
+    vendored("rich.markup")
+    vendored("rich.progress")
+    vendored("rich.segment")
+    vendored("rich.style")
+    vendored("rich.text")
+    vendored("rich.traceback")
+    if sys.version_info < (3, 11):
+        vendored("tomli")
+    vendored("truststore")
+    vendored("urllib3")
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..eec7b85
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/bom.cdx.json b/venv/lib/python3.12/site-packages/pip/_vendor/bom.cdx.json
new file mode 100644
index 0000000..7f2d285
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/bom.cdx.json
@@ -0,0 +1,231 @@
+{
+  "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json",
+  "bomFormat": "CycloneDX",
+  "components": [
+    {
+      "bom-ref": "bom-ref:pip",
+      "name": "pip",
+      "type": "library"
+    },
+    {
+      "bom-ref": "pkg:pypi/CacheControl@0.14.4",
+      "name": "CacheControl",
+      "purl": "pkg:pypi/CacheControl@0.14.4",
+      "type": "library",
+      "version": "0.14.4"
+    },
+    {
+      "bom-ref": "pkg:pypi/certifi@2026.6.17",
+      "name": "certifi",
+      "purl": "pkg:pypi/certifi@2026.6.17",
+      "type": "library",
+      "version": "2026.6.17"
+    },
+    {
+      "bom-ref": "pkg:pypi/distlib@0.4.2",
+      "name": "distlib",
+      "purl": "pkg:pypi/distlib@0.4.2",
+      "type": "library",
+      "version": "0.4.2"
+    },
+    {
+      "bom-ref": "pkg:pypi/distro@1.9.0",
+      "name": "distro",
+      "purl": "pkg:pypi/distro@1.9.0",
+      "type": "library",
+      "version": "1.9.0"
+    },
+    {
+      "bom-ref": "pkg:pypi/idna@3.18",
+      "name": "idna",
+      "purl": "pkg:pypi/idna@3.18",
+      "type": "library",
+      "version": "3.18"
+    },
+    {
+      "bom-ref": "pkg:pypi/msgpack@1.1.2",
+      "name": "msgpack",
+      "purl": "pkg:pypi/msgpack@1.1.2",
+      "type": "library",
+      "version": "1.1.2"
+    },
+    {
+      "bom-ref": "pkg:pypi/packaging@26.2",
+      "name": "packaging",
+      "purl": "pkg:pypi/packaging@26.2",
+      "type": "library",
+      "version": "26.2"
+    },
+    {
+      "bom-ref": "pkg:pypi/platformdirs@4.10.0",
+      "name": "platformdirs",
+      "purl": "pkg:pypi/platformdirs@4.10.0",
+      "type": "library",
+      "version": "4.10.0"
+    },
+    {
+      "bom-ref": "pkg:pypi/pygments@2.20.0",
+      "name": "pygments",
+      "purl": "pkg:pypi/pygments@2.20.0",
+      "type": "library",
+      "version": "2.20.0"
+    },
+    {
+      "bom-ref": "pkg:pypi/pyproject-hooks@1.2.0",
+      "name": "pyproject-hooks",
+      "purl": "pkg:pypi/pyproject-hooks@1.2.0",
+      "type": "library",
+      "version": "1.2.0"
+    },
+    {
+      "bom-ref": "pkg:pypi/requests@2.34.2",
+      "name": "requests",
+      "purl": "pkg:pypi/requests@2.34.2",
+      "type": "library",
+      "version": "2.34.2"
+    },
+    {
+      "bom-ref": "pkg:pypi/resolvelib@1.2.1",
+      "name": "resolvelib",
+      "purl": "pkg:pypi/resolvelib@1.2.1",
+      "type": "library",
+      "version": "1.2.1"
+    },
+    {
+      "bom-ref": "pkg:pypi/rich@14.2.0",
+      "name": "rich",
+      "purl": "pkg:pypi/rich@14.2.0",
+      "type": "library",
+      "version": "14.2.0"
+    },
+    {
+      "bom-ref": "pkg:pypi/setuptools@70.3.0",
+      "name": "setuptools",
+      "purl": "pkg:pypi/setuptools@70.3.0",
+      "type": "library",
+      "version": "70.3.0"
+    },
+    {
+      "bom-ref": "pkg:pypi/tomli@2.4.1",
+      "name": "tomli",
+      "purl": "pkg:pypi/tomli@2.4.1",
+      "type": "library",
+      "version": "2.4.1"
+    },
+    {
+      "bom-ref": "pkg:pypi/tomli-w@1.2.0",
+      "name": "tomli-w",
+      "purl": "pkg:pypi/tomli-w@1.2.0",
+      "type": "library",
+      "version": "1.2.0"
+    },
+    {
+      "bom-ref": "pkg:pypi/truststore@0.10.4",
+      "name": "truststore",
+      "purl": "pkg:pypi/truststore@0.10.4",
+      "type": "library",
+      "version": "0.10.4"
+    },
+    {
+      "bom-ref": "pkg:pypi/urllib3@2.7.0",
+      "name": "urllib3",
+      "purl": "pkg:pypi/urllib3@2.7.0",
+      "type": "library",
+      "version": "2.7.0"
+    }
+  ],
+  "dependencies": [
+    {
+      "dependsOn": [
+        "pkg:pypi/CacheControl@0.14.4",
+        "pkg:pypi/certifi@2026.6.17",
+        "pkg:pypi/distlib@0.4.2",
+        "pkg:pypi/distro@1.9.0",
+        "pkg:pypi/idna@3.18",
+        "pkg:pypi/msgpack@1.1.2",
+        "pkg:pypi/packaging@26.2",
+        "pkg:pypi/platformdirs@4.10.0",
+        "pkg:pypi/pygments@2.20.0",
+        "pkg:pypi/pyproject-hooks@1.2.0",
+        "pkg:pypi/requests@2.34.2",
+        "pkg:pypi/resolvelib@1.2.1",
+        "pkg:pypi/rich@14.2.0",
+        "pkg:pypi/setuptools@70.3.0",
+        "pkg:pypi/tomli@2.4.1",
+        "pkg:pypi/tomli-w@1.2.0",
+        "pkg:pypi/truststore@0.10.4",
+        "pkg:pypi/urllib3@2.7.0"
+      ],
+      "ref": "bom-ref:pip"
+    },
+    {
+      "ref": "pkg:pypi/CacheControl@0.14.4"
+    },
+    {
+      "ref": "pkg:pypi/certifi@2026.6.17"
+    },
+    {
+      "ref": "pkg:pypi/distlib@0.4.2"
+    },
+    {
+      "ref": "pkg:pypi/distro@1.9.0"
+    },
+    {
+      "ref": "pkg:pypi/idna@3.18"
+    },
+    {
+      "ref": "pkg:pypi/msgpack@1.1.2"
+    },
+    {
+      "ref": "pkg:pypi/packaging@26.2"
+    },
+    {
+      "ref": "pkg:pypi/platformdirs@4.10.0"
+    },
+    {
+      "ref": "pkg:pypi/pygments@2.20.0"
+    },
+    {
+      "ref": "pkg:pypi/pyproject-hooks@1.2.0"
+    },
+    {
+      "ref": "pkg:pypi/requests@2.34.2"
+    },
+    {
+      "ref": "pkg:pypi/resolvelib@1.2.1"
+    },
+    {
+      "ref": "pkg:pypi/rich@14.2.0"
+    },
+    {
+      "ref": "pkg:pypi/setuptools@70.3.0"
+    },
+    {
+      "ref": "pkg:pypi/tomli@2.4.1"
+    },
+    {
+      "ref": "pkg:pypi/tomli-w@1.2.0"
+    },
+    {
+      "ref": "pkg:pypi/truststore@0.10.4"
+    },
+    {
+      "ref": "pkg:pypi/urllib3@2.7.0"
+    }
+  ],
+  "metadata": {
+    "component": {
+      "bom-ref": "bom-ref:pip",
+      "name": "pip",
+      "type": "library"
+    },
+    "tools": [
+      {
+        "name": "vendoring",
+        "version": "1.4.0"
+      }
+    ]
+  },
+  "specVersion": "1.4",
+  "version": 1
+}
\ No newline at end of file
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/LICENSE.txt b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/LICENSE.txt
new file mode 100644
index 0000000..d8b3b56
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/LICENSE.txt
@@ -0,0 +1,13 @@
+Copyright 2012-2021  Eric Larson
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__init__.py
new file mode 100644
index 0000000..3f8c36f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__init__.py
@@ -0,0 +1,32 @@
+# SPDX-FileCopyrightText: 2015 Eric Larson
+#
+# SPDX-License-Identifier: Apache-2.0
+
+"""CacheControl import Interface.
+
+Make it easy to import from cachecontrol without long namespaces.
+"""
+
+import importlib.metadata
+
+from pip._vendor.cachecontrol.adapter import CacheControlAdapter
+from pip._vendor.cachecontrol.controller import CacheController
+from pip._vendor.cachecontrol.wrapper import CacheControl
+
+__author__ = "Eric Larson"
+__email__ = "eric@ionrock.org"
+# pip patch: this won't work when vendored, so just patch it out as it's unused
+# __version__ = importlib.metadata.version("cachecontrol")
+
+__all__ = [
+    "__author__",
+    "__email__",
+    "__version__",
+    "CacheControlAdapter",
+    "CacheController",
+    "CacheControl",
+]
+
+import logging
+
+logging.getLogger(__name__).addHandler(logging.NullHandler())
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..16aab0b
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-312.pyc
new file mode 100644
index 0000000..3c2d2e4
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-312.pyc
new file mode 100644
index 0000000..980daed
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-312.pyc
new file mode 100644
index 0000000..ae11273
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-312.pyc
new file mode 100644
index 0000000..5992820
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-312.pyc
new file mode 100644
index 0000000..cdea7d7
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-312.pyc
new file mode 100644
index 0000000..783a9da
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-312.pyc
new file mode 100644
index 0000000..ffea0dc
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-312.pyc
new file mode 100644
index 0000000..4c633ea
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/_cmd.py b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/_cmd.py
new file mode 100644
index 0000000..2c84208
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/_cmd.py
@@ -0,0 +1,70 @@
+# SPDX-FileCopyrightText: 2015 Eric Larson
+#
+# SPDX-License-Identifier: Apache-2.0
+from __future__ import annotations
+
+import logging
+from argparse import ArgumentParser
+from typing import TYPE_CHECKING
+
+from pip._vendor import requests
+
+from pip._vendor.cachecontrol.adapter import CacheControlAdapter
+from pip._vendor.cachecontrol.cache import DictCache
+from pip._vendor.cachecontrol.controller import logger
+
+if TYPE_CHECKING:
+    from argparse import Namespace
+
+    from pip._vendor.cachecontrol.controller import CacheController
+
+
+def setup_logging() -> None:
+    logger.setLevel(logging.DEBUG)
+    handler = logging.StreamHandler()
+    logger.addHandler(handler)
+
+
+def get_session() -> requests.Session:
+    adapter = CacheControlAdapter(
+        DictCache(), cache_etags=True, serializer=None, heuristic=None
+    )
+    sess = requests.Session()
+    sess.mount("http://", adapter)
+    sess.mount("https://", adapter)
+
+    sess.cache_controller = adapter.controller  # type: ignore[attr-defined]
+    return sess
+
+
+def get_args() -> Namespace:
+    parser = ArgumentParser()
+    parser.add_argument("url", help="The URL to try and cache")
+    return parser.parse_args()
+
+
+def main() -> None:
+    args = get_args()
+    sess = get_session()
+
+    # Make a request to get a response
+    resp = sess.get(args.url)
+
+    # Turn on logging
+    setup_logging()
+
+    # try setting the cache
+    cache_controller: CacheController = (
+        sess.cache_controller  # type: ignore[attr-defined]
+    )
+    cache_controller.cache_response(resp.request, resp.raw)
+
+    # Now try to get it
+    if cache_controller.cached_request(resp.request):
+        print("Cached!")
+    else:
+        print("Not cached :(")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/adapter.py b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/adapter.py
new file mode 100644
index 0000000..01f1253
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/adapter.py
@@ -0,0 +1,167 @@
+# SPDX-FileCopyrightText: 2015 Eric Larson
+#
+# SPDX-License-Identifier: Apache-2.0
+from __future__ import annotations
+
+import functools
+import weakref
+import zlib
+from typing import TYPE_CHECKING, Any, Collection, Mapping
+
+from pip._vendor.requests.adapters import HTTPAdapter
+
+from pip._vendor.cachecontrol.cache import DictCache
+from pip._vendor.cachecontrol.controller import PERMANENT_REDIRECT_STATUSES, CacheController
+from pip._vendor.cachecontrol.filewrapper import CallbackFileWrapper
+
+if TYPE_CHECKING:
+    from pip._vendor.requests import PreparedRequest, Response
+    from pip._vendor.urllib3 import HTTPResponse
+
+    from pip._vendor.cachecontrol.cache import BaseCache
+    from pip._vendor.cachecontrol.heuristics import BaseHeuristic
+    from pip._vendor.cachecontrol.serialize import Serializer
+
+
+class CacheControlAdapter(HTTPAdapter):
+    invalidating_methods = {"PUT", "PATCH", "DELETE"}
+
+    def __init__(
+        self,
+        cache: BaseCache | None = None,
+        cache_etags: bool = True,
+        controller_class: type[CacheController] | None = None,
+        serializer: Serializer | None = None,
+        heuristic: BaseHeuristic | None = None,
+        cacheable_methods: Collection[str] | None = None,
+        *args: Any,
+        **kw: Any,
+    ) -> None:
+        super().__init__(*args, **kw)
+        self.cache = DictCache() if cache is None else cache
+        self.heuristic = heuristic
+        self.cacheable_methods = cacheable_methods or ("GET",)
+
+        controller_factory = controller_class or CacheController
+        self.controller = controller_factory(
+            self.cache, cache_etags=cache_etags, serializer=serializer
+        )
+
+    def send(
+        self,
+        request: PreparedRequest,
+        stream: bool = False,
+        timeout: None | float | tuple[float, float] | tuple[float, None] = None,
+        verify: bool | str = True,
+        cert: (None | bytes | str | tuple[bytes | str, bytes | str]) = None,
+        proxies: Mapping[str, str] | None = None,
+        cacheable_methods: Collection[str] | None = None,
+    ) -> Response:
+        """
+        Send a request. Use the request information to see if it
+        exists in the cache and cache the response if we need to and can.
+        """
+        cacheable = cacheable_methods or self.cacheable_methods
+        if request.method in cacheable:
+            try:
+                cached_response = self.controller.cached_request(request)
+            except zlib.error:
+                cached_response = None
+            if cached_response:
+                return self.build_response(request, cached_response, from_cache=True)
+
+            # check for etags and add headers if appropriate
+            request.headers.update(self.controller.conditional_headers(request))
+
+        resp = super().send(request, stream, timeout, verify, cert, proxies)
+
+        return resp
+
+    def build_response(  # type: ignore[override]
+        self,
+        request: PreparedRequest,
+        response: HTTPResponse,
+        from_cache: bool = False,
+        cacheable_methods: Collection[str] | None = None,
+    ) -> Response:
+        """
+        Build a response by making a request or using the cache.
+
+        This will end up calling send and returning a potentially
+        cached response
+        """
+        cacheable = cacheable_methods or self.cacheable_methods
+        if not from_cache and request.method in cacheable:
+            # Check for any heuristics that might update headers
+            # before trying to cache.
+            if self.heuristic:
+                response = self.heuristic.apply(response)
+
+            # apply any expiration heuristics
+            if response.status == 304:
+                # We must have sent an ETag request. This could mean
+                # that we've been expired already or that we simply
+                # have an etag. In either case, we want to try and
+                # update the cache if that is the case.
+                cached_response = self.controller.update_cached_response(
+                    request, response
+                )
+
+                if cached_response is not response:
+                    from_cache = True
+
+                # We are done with the server response, read a
+                # possible response body (compliant servers will
+                # not return one, but we cannot be 100% sure) and
+                # release the connection back to the pool.
+                response.read(decode_content=False)
+                response.release_conn()
+
+                response = cached_response
+
+            # We always cache the 301 responses
+            elif int(response.status) in PERMANENT_REDIRECT_STATUSES:
+                self.controller.cache_response(request, response)
+            else:
+                # Wrap the response file with a wrapper that will cache the
+                #   response when the stream has been consumed.
+                response._fp = CallbackFileWrapper(  # type: ignore[assignment]
+                    response._fp,  # type: ignore[arg-type]
+                    functools.partial(
+                        self.controller.cache_response, request, weakref.ref(response)
+                    ),
+                )
+                if response.chunked:
+                    super_update_chunk_length = response.__class__._update_chunk_length
+
+                    def _update_chunk_length(
+                        weak_self: weakref.ReferenceType[HTTPResponse],
+                    ) -> None:
+                        self = weak_self()
+                        if self is None:
+                            return
+
+                        super_update_chunk_length(self)
+                        if self.chunk_left == 0:
+                            self._fp._close()  # type: ignore[union-attr]
+
+                    response._update_chunk_length = functools.partial(  # type: ignore[method-assign]
+                        _update_chunk_length, weakref.ref(response)
+                    )
+
+        resp: Response = super().build_response(request, response)
+
+        # See if we should invalidate the cache.
+        if request.method in self.invalidating_methods and resp.ok:
+            assert request.url is not None
+            cache_url = self.controller.cache_url(request.url)
+            self.cache.delete(cache_url)
+
+        # Give the request a from_cache attr to let people use it
+        resp.from_cache = from_cache  # type: ignore[attr-defined]
+
+        return resp
+
+    def close(self) -> None:
+        self.cache.close()
+        super().close()  # type: ignore[no-untyped-call]
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/cache.py b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/cache.py
new file mode 100644
index 0000000..91598e9
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/cache.py
@@ -0,0 +1,75 @@
+# SPDX-FileCopyrightText: 2015 Eric Larson
+#
+# SPDX-License-Identifier: Apache-2.0
+
+"""
+The cache object API for implementing caches. The default is a thread
+safe in-memory dictionary.
+"""
+
+from __future__ import annotations
+
+from threading import Lock
+from typing import IO, TYPE_CHECKING, MutableMapping
+
+if TYPE_CHECKING:
+    from datetime import datetime
+
+
+class BaseCache:
+    def get(self, key: str) -> bytes | None:
+        raise NotImplementedError()
+
+    def set(
+        self, key: str, value: bytes, expires: int | datetime | None = None
+    ) -> None:
+        raise NotImplementedError()
+
+    def delete(self, key: str) -> None:
+        raise NotImplementedError()
+
+    def close(self) -> None:
+        pass
+
+
+class DictCache(BaseCache):
+    def __init__(self, init_dict: MutableMapping[str, bytes] | None = None) -> None:
+        self.lock = Lock()
+        self.data = init_dict or {}
+
+    def get(self, key: str) -> bytes | None:
+        return self.data.get(key, None)
+
+    def set(
+        self, key: str, value: bytes, expires: int | datetime | None = None
+    ) -> None:
+        with self.lock:
+            self.data.update({key: value})
+
+    def delete(self, key: str) -> None:
+        with self.lock:
+            if key in self.data:
+                self.data.pop(key)
+
+
+class SeparateBodyBaseCache(BaseCache):
+    """
+    In this variant, the body is not stored mixed in with the metadata, but is
+    passed in (as a bytes-like object) in a separate call to ``set_body()``.
+
+    That is, the expected interaction pattern is::
+
+        cache.set(key, serialized_metadata)
+        cache.set_body(key)
+
+    Similarly, the body should be loaded separately via ``get_body()``.
+    """
+
+    def set_body(self, key: str, body: bytes) -> None:
+        raise NotImplementedError()
+
+    def get_body(self, key: str) -> IO[bytes] | None:
+        """
+        Return the body as file-like object.
+        """
+        raise NotImplementedError()
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__init__.py
new file mode 100644
index 0000000..24ff469
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__init__.py
@@ -0,0 +1,8 @@
+# SPDX-FileCopyrightText: 2015 Eric Larson
+#
+# SPDX-License-Identifier: Apache-2.0
+
+from pip._vendor.cachecontrol.caches.file_cache import FileCache, SeparateBodyFileCache
+from pip._vendor.cachecontrol.caches.redis_cache import RedisCache
+
+__all__ = ["FileCache", "SeparateBodyFileCache", "RedisCache"]
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..4c3b37d
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-312.pyc
new file mode 100644
index 0000000..2d603b4
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-312.pyc
new file mode 100644
index 0000000..3cbc24c
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py
new file mode 100644
index 0000000..45c632c
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py
@@ -0,0 +1,145 @@
+# SPDX-FileCopyrightText: 2015 Eric Larson
+#
+# SPDX-License-Identifier: Apache-2.0
+from __future__ import annotations
+
+import hashlib
+import os
+import tempfile
+from textwrap import dedent
+from typing import IO, TYPE_CHECKING
+from pathlib import Path
+
+from pip._vendor.cachecontrol.cache import BaseCache, SeparateBodyBaseCache
+from pip._vendor.cachecontrol.controller import CacheController
+
+if TYPE_CHECKING:
+    from datetime import datetime
+
+    from filelock import BaseFileLock
+
+
+class _FileCacheMixin:
+    """Shared implementation for both FileCache variants."""
+
+    def __init__(
+        self,
+        directory: str | Path,
+        forever: bool = False,
+        filemode: int = 0o0600,
+        dirmode: int = 0o0700,
+        lock_class: type[BaseFileLock] | None = None,
+    ) -> None:
+        try:
+            if lock_class is None:
+                from filelock import FileLock
+
+                lock_class = FileLock
+        except ImportError:
+            notice = dedent(
+                """
+            NOTE: In order to use the FileCache you must have
+            filelock installed. You can install it via pip:
+              pip install cachecontrol[filecache]
+            """
+            )
+            raise ImportError(notice)
+
+        self.directory = directory
+        self.forever = forever
+        self.filemode = filemode
+        self.dirmode = dirmode
+        self.lock_class = lock_class
+
+    @staticmethod
+    def encode(x: str) -> str:
+        return hashlib.sha224(x.encode()).hexdigest()
+
+    def _fn(self, name: str) -> str:
+        # NOTE: This method should not change as some may depend on it.
+        #       See: https://github.com/ionrock/cachecontrol/issues/63
+        hashed = self.encode(name)
+        parts = list(hashed[:5]) + [hashed]
+        return os.path.join(self.directory, *parts)
+
+    def get(self, key: str) -> bytes | None:
+        name = self._fn(key)
+        try:
+            with open(name, "rb") as fh:
+                return fh.read()
+
+        except FileNotFoundError:
+            return None
+
+    def set(
+        self, key: str, value: bytes, expires: int | datetime | None = None
+    ) -> None:
+        name = self._fn(key)
+        self._write(name, value)
+
+    def _write(self, path: str, data: bytes) -> None:
+        """
+        Safely write the data to the given path.
+        """
+        # Make sure the directory exists
+        dirname = os.path.dirname(path)
+        os.makedirs(dirname, self.dirmode, exist_ok=True)
+
+        with self.lock_class(path + ".lock"):
+            # Write our actual file
+            (fd, name) = tempfile.mkstemp(dir=dirname)
+            try:
+                os.write(fd, data)
+            finally:
+                os.close(fd)
+            os.chmod(name, self.filemode)
+            os.replace(name, path)
+
+    def _delete(self, key: str, suffix: str) -> None:
+        name = self._fn(key) + suffix
+        if not self.forever:
+            try:
+                os.remove(name)
+            except FileNotFoundError:
+                pass
+
+
+class FileCache(_FileCacheMixin, BaseCache):
+    """
+    Traditional FileCache: body is stored in memory, so not suitable for large
+    downloads.
+    """
+
+    def delete(self, key: str) -> None:
+        self._delete(key, "")
+
+
+class SeparateBodyFileCache(_FileCacheMixin, SeparateBodyBaseCache):
+    """
+    Memory-efficient FileCache: body is stored in a separate file, reducing
+    peak memory usage.
+    """
+
+    def get_body(self, key: str) -> IO[bytes] | None:
+        name = self._fn(key) + ".body"
+        try:
+            return open(name, "rb")
+        except FileNotFoundError:
+            return None
+
+    def set_body(self, key: str, body: bytes) -> None:
+        name = self._fn(key) + ".body"
+        self._write(name, body)
+
+    def delete(self, key: str) -> None:
+        self._delete(key, "")
+        self._delete(key, ".body")
+
+
+def url_to_file_path(url: str, filecache: FileCache) -> str:
+    """Return the file cache path based on the URL.
+
+    This does not ensure the file exists!
+    """
+    key = CacheController.cache_url(url)
+    return filecache._fn(key)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py
new file mode 100644
index 0000000..f4f68c4
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py
@@ -0,0 +1,48 @@
+# SPDX-FileCopyrightText: 2015 Eric Larson
+#
+# SPDX-License-Identifier: Apache-2.0
+from __future__ import annotations
+
+
+from datetime import datetime, timezone
+from typing import TYPE_CHECKING
+
+from pip._vendor.cachecontrol.cache import BaseCache
+
+if TYPE_CHECKING:
+    from redis import Redis
+
+
+class RedisCache(BaseCache):
+    def __init__(self, conn: Redis[bytes]) -> None:
+        self.conn = conn
+
+    def get(self, key: str) -> bytes | None:
+        return self.conn.get(key)
+
+    def set(
+        self, key: str, value: bytes, expires: int | datetime | None = None
+    ) -> None:
+        if not expires:
+            self.conn.set(key, value)
+        elif isinstance(expires, datetime):
+            now_utc = datetime.now(timezone.utc)
+            if expires.tzinfo is None:
+                now_utc = now_utc.replace(tzinfo=None)
+            delta = expires - now_utc
+            self.conn.setex(key, int(delta.total_seconds()), value)
+        else:
+            self.conn.setex(key, expires, value)
+
+    def delete(self, key: str) -> None:
+        self.conn.delete(key)
+
+    def clear(self) -> None:
+        """Helper for clearing all the keys in a database. Use with
+        caution!"""
+        for key in self.conn.keys():
+            self.conn.delete(key)
+
+    def close(self) -> None:
+        """Redis uses connection pooling, no need to close the connection."""
+        pass
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/controller.py b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/controller.py
new file mode 100644
index 0000000..0bf3801
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/controller.py
@@ -0,0 +1,511 @@
+# SPDX-FileCopyrightText: 2015 Eric Larson
+#
+# SPDX-License-Identifier: Apache-2.0
+
+"""
+The httplib2 algorithms ported for use with requests.
+"""
+
+from __future__ import annotations
+
+import calendar
+import logging
+import re
+import time
+import weakref
+from email.utils import parsedate_tz
+from typing import TYPE_CHECKING, Collection, Mapping
+
+from pip._vendor.requests.structures import CaseInsensitiveDict
+
+from pip._vendor.cachecontrol.cache import DictCache, SeparateBodyBaseCache
+from pip._vendor.cachecontrol.serialize import Serializer
+
+if TYPE_CHECKING:
+    from typing import Literal
+
+    from pip._vendor.requests import PreparedRequest
+    from pip._vendor.urllib3 import HTTPResponse
+
+    from pip._vendor.cachecontrol.cache import BaseCache
+
+logger = logging.getLogger(__name__)
+
+URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?")
+
+PERMANENT_REDIRECT_STATUSES = (301, 308)
+
+
+def parse_uri(uri: str) -> tuple[str, str, str, str, str]:
+    """Parses a URI using the regex given in Appendix B of RFC 3986.
+
+    (scheme, authority, path, query, fragment) = parse_uri(uri)
+    """
+    match = URI.match(uri)
+    assert match is not None
+    groups = match.groups()
+    return (groups[1], groups[3], groups[4], groups[6], groups[8])
+
+
+class CacheController:
+    """An interface to see if request should cached or not."""
+
+    def __init__(
+        self,
+        cache: BaseCache | None = None,
+        cache_etags: bool = True,
+        serializer: Serializer | None = None,
+        status_codes: Collection[int] | None = None,
+    ):
+        self.cache = DictCache() if cache is None else cache
+        self.cache_etags = cache_etags
+        self.serializer = serializer or Serializer()
+        self.cacheable_status_codes = status_codes or (200, 203, 300, 301, 308)
+
+    @classmethod
+    def _urlnorm(cls, uri: str) -> str:
+        """Normalize the URL to create a safe key for the cache"""
+        (scheme, authority, path, query, fragment) = parse_uri(uri)
+        if not scheme or not authority:
+            raise Exception("Only absolute URIs are allowed. uri = %s" % uri)
+
+        scheme = scheme.lower()
+        authority = authority.lower()
+
+        if not path:
+            path = "/"
+
+        # Could do syntax based normalization of the URI before
+        # computing the digest. See Section 6.2.2 of Std 66.
+        request_uri = query and "?".join([path, query]) or path
+        defrag_uri = scheme + "://" + authority + request_uri
+
+        return defrag_uri
+
+    @classmethod
+    def cache_url(cls, uri: str) -> str:
+        return cls._urlnorm(uri)
+
+    def parse_cache_control(self, headers: Mapping[str, str]) -> dict[str, int | None]:
+        known_directives = {
+            # https://tools.ietf.org/html/rfc7234#section-5.2
+            "max-age": (int, True),
+            "max-stale": (int, False),
+            "min-fresh": (int, True),
+            "no-cache": (None, False),
+            "no-store": (None, False),
+            "no-transform": (None, False),
+            "only-if-cached": (None, False),
+            "must-revalidate": (None, False),
+            "public": (None, False),
+            "private": (None, False),
+            "proxy-revalidate": (None, False),
+            "s-maxage": (int, True),
+        }
+
+        cc_headers = headers.get("cache-control", headers.get("Cache-Control", ""))
+
+        retval: dict[str, int | None] = {}
+
+        for cc_directive in cc_headers.split(","):
+            if not cc_directive.strip():
+                continue
+
+            parts = cc_directive.split("=", 1)
+            directive = parts[0].strip()
+
+            try:
+                typ, required = known_directives[directive]
+            except KeyError:
+                logger.debug("Ignoring unknown cache-control directive: %s", directive)
+                continue
+
+            if not typ or not required:
+                retval[directive] = None
+            if typ:
+                try:
+                    retval[directive] = typ(parts[1].strip())
+                except IndexError:
+                    if required:
+                        logger.debug(
+                            "Missing value for cache-control " "directive: %s",
+                            directive,
+                        )
+                except ValueError:
+                    logger.debug(
+                        "Invalid value for cache-control directive " "%s, must be %s",
+                        directive,
+                        typ.__name__,
+                    )
+
+        return retval
+
+    def _load_from_cache(self, request: PreparedRequest) -> HTTPResponse | None:
+        """
+        Load a cached response, or return None if it's not available.
+        """
+        # We do not support caching of partial content: so if the request contains a
+        # Range header then we don't want to load anything from the cache.
+        if "Range" in request.headers:
+            return None
+
+        cache_url = request.url
+        assert cache_url is not None
+        cache_data = self.cache.get(cache_url)
+        if cache_data is None:
+            logger.debug("No cache entry available")
+            return None
+
+        if isinstance(self.cache, SeparateBodyBaseCache):
+            body_file = self.cache.get_body(cache_url)
+        else:
+            body_file = None
+
+        result = self.serializer.loads(request, cache_data, body_file)
+        if result is None:
+            logger.warning("Cache entry deserialization failed, entry ignored")
+        return result
+
+    def cached_request(self, request: PreparedRequest) -> HTTPResponse | Literal[False]:
+        """
+        Return a cached response if it exists in the cache, otherwise
+        return False.
+        """
+        assert request.url is not None
+        cache_url = self.cache_url(request.url)
+        logger.debug('Looking up "%s" in the cache', cache_url)
+        cc = self.parse_cache_control(request.headers)
+
+        # Bail out if the request insists on fresh data
+        if "no-cache" in cc:
+            logger.debug('Request header has "no-cache", cache bypassed')
+            return False
+
+        if "max-age" in cc and cc["max-age"] == 0:
+            logger.debug('Request header has "max_age" as 0, cache bypassed')
+            return False
+
+        # Check whether we can load the response from the cache:
+        resp = self._load_from_cache(request)
+        if not resp:
+            return False
+
+        # If we have a cached permanent redirect, return it immediately. We
+        # don't need to test our response for other headers b/c it is
+        # intrinsically "cacheable" as it is Permanent.
+        #
+        # See:
+        #   https://tools.ietf.org/html/rfc7231#section-6.4.2
+        #
+        # Client can try to refresh the value by repeating the request
+        # with cache busting headers as usual (ie no-cache).
+        if int(resp.status) in PERMANENT_REDIRECT_STATUSES:
+            msg = (
+                "Returning cached permanent redirect response "
+                "(ignoring date and etag information)"
+            )
+            logger.debug(msg)
+            return resp
+
+        headers: CaseInsensitiveDict[str] = CaseInsensitiveDict(resp.headers)
+        if not headers or "date" not in headers:
+            if "etag" not in headers:
+                # Without date or etag, the cached response can never be used
+                # and should be deleted.
+                logger.debug("Purging cached response: no date or etag")
+                self.cache.delete(cache_url)
+            logger.debug("Ignoring cached response: no date")
+            return False
+
+        now = time.time()
+        time_tuple = parsedate_tz(headers["date"])
+        assert time_tuple is not None
+        date = calendar.timegm(time_tuple[:6])
+        current_age = max(0, now - date)
+        logger.debug("Current age based on date: %i", current_age)
+
+        # TODO: There is an assumption that the result will be a
+        #       urllib3 response object. This may not be best since we
+        #       could probably avoid instantiating or constructing the
+        #       response until we know we need it.
+        resp_cc = self.parse_cache_control(headers)
+
+        # determine freshness
+        freshness_lifetime = 0
+
+        # Check the max-age pragma in the cache control header
+        max_age = resp_cc.get("max-age")
+        if max_age is not None:
+            freshness_lifetime = max_age
+            logger.debug("Freshness lifetime from max-age: %i", freshness_lifetime)
+
+        # If there isn't a max-age, check for an expires header
+        elif "expires" in headers:
+            expires = parsedate_tz(headers["expires"])
+            if expires is not None:
+                expire_time = calendar.timegm(expires[:6]) - date
+                freshness_lifetime = max(0, expire_time)
+                logger.debug("Freshness lifetime from expires: %i", freshness_lifetime)
+
+        # Determine if we are setting freshness limit in the
+        # request. Note, this overrides what was in the response.
+        max_age = cc.get("max-age")
+        if max_age is not None:
+            freshness_lifetime = max_age
+            logger.debug(
+                "Freshness lifetime from request max-age: %i", freshness_lifetime
+            )
+
+        min_fresh = cc.get("min-fresh")
+        if min_fresh is not None:
+            # adjust our current age by our min fresh
+            current_age += min_fresh
+            logger.debug("Adjusted current age from min-fresh: %i", current_age)
+
+        # Return entry if it is fresh enough
+        if freshness_lifetime > current_age:
+            logger.debug('The response is "fresh", returning cached response')
+            logger.debug("%i > %i", freshness_lifetime, current_age)
+            return resp
+
+        # we're not fresh. If we don't have an Etag, clear it out
+        if "etag" not in headers:
+            logger.debug('The cached response is "stale" with no etag, purging')
+            self.cache.delete(cache_url)
+
+        # return the original handler
+        return False
+
+    def conditional_headers(self, request: PreparedRequest) -> dict[str, str]:
+        resp = self._load_from_cache(request)
+        new_headers = {}
+
+        if resp:
+            headers: CaseInsensitiveDict[str] = CaseInsensitiveDict(resp.headers)
+
+            if "etag" in headers:
+                new_headers["If-None-Match"] = headers["ETag"]
+
+            if "last-modified" in headers:
+                new_headers["If-Modified-Since"] = headers["Last-Modified"]
+
+        return new_headers
+
+    def _cache_set(
+        self,
+        cache_url: str,
+        request: PreparedRequest,
+        response: HTTPResponse,
+        body: bytes | None = None,
+        expires_time: int | None = None,
+    ) -> None:
+        """
+        Store the data in the cache.
+        """
+        if isinstance(self.cache, SeparateBodyBaseCache):
+            # We pass in the body separately; just put a placeholder empty
+            # string in the metadata.
+            self.cache.set(
+                cache_url,
+                self.serializer.dumps(request, response, b""),
+                expires=expires_time,
+            )
+            # body is None can happen when, for example, we're only updating
+            # headers, as is the case in update_cached_response().
+            if body is not None:
+                self.cache.set_body(cache_url, body)
+        else:
+            self.cache.set(
+                cache_url,
+                self.serializer.dumps(request, response, body),
+                expires=expires_time,
+            )
+
+    def cache_response(
+        self,
+        request: PreparedRequest,
+        response_or_ref: HTTPResponse | weakref.ReferenceType[HTTPResponse],
+        body: bytes | None = None,
+        status_codes: Collection[int] | None = None,
+    ) -> None:
+        """
+        Algorithm for caching requests.
+
+        This assumes a requests Response object.
+        """
+        if isinstance(response_or_ref, weakref.ReferenceType):
+            response = response_or_ref()
+            if response is None:
+                # The weakref can be None only in case the user used streamed request
+                # and did not consume or close it, and holds no reference to requests.Response.
+                # In such case, we don't want to cache the response.
+                return
+        else:
+            response = response_or_ref
+
+        # From httplib2: Don't cache 206's since we aren't going to
+        #                handle byte range requests
+        cacheable_status_codes = status_codes or self.cacheable_status_codes
+        if response.status not in cacheable_status_codes:
+            logger.debug(
+                "Status code %s not in %s", response.status, cacheable_status_codes
+            )
+            return
+
+        response_headers: CaseInsensitiveDict[str] = CaseInsensitiveDict(
+            response.headers
+        )
+
+        if "date" in response_headers:
+            time_tuple = parsedate_tz(response_headers["date"])
+            assert time_tuple is not None
+            date = calendar.timegm(time_tuple[:6])
+        else:
+            date = 0
+
+        # If we've been given a body, our response has a Content-Length, that
+        # Content-Length is valid then we can check to see if the body we've
+        # been given matches the expected size, and if it doesn't we'll just
+        # skip trying to cache it.
+        if (
+            body is not None
+            and "content-length" in response_headers
+            and response_headers["content-length"].isdigit()
+            and int(response_headers["content-length"]) != len(body)
+        ):
+            return
+
+        cc_req = self.parse_cache_control(request.headers)
+        cc = self.parse_cache_control(response_headers)
+
+        assert request.url is not None
+        cache_url = self.cache_url(request.url)
+        logger.debug('Updating cache with response from "%s"', cache_url)
+
+        # Delete it from the cache if we happen to have it stored there
+        no_store = False
+        if "no-store" in cc:
+            no_store = True
+            logger.debug('Response header has "no-store"')
+        if "no-store" in cc_req:
+            no_store = True
+            logger.debug('Request header has "no-store"')
+        if no_store and self.cache.get(cache_url):
+            logger.debug('Purging existing cache entry to honor "no-store"')
+            self.cache.delete(cache_url)
+        if no_store:
+            return
+
+        # https://tools.ietf.org/html/rfc7234#section-4.1:
+        # A Vary header field-value of "*" always fails to match.
+        # Storing such a response leads to a deserialization warning
+        # during cache lookup and is not allowed to ever be served,
+        # so storing it can be avoided.
+        if "*" in response_headers.get("vary", ""):
+            logger.debug('Response header has "Vary: *"')
+            return
+
+        # If we've been given an etag, then keep the response
+        if self.cache_etags and "etag" in response_headers:
+            expires_time = 0
+            if response_headers.get("expires"):
+                expires = parsedate_tz(response_headers["expires"])
+                if expires is not None:
+                    expires_time = calendar.timegm(expires[:6]) - date
+
+            expires_time = max(expires_time, 14 * 86400)
+
+            logger.debug(f"etag object cached for {expires_time} seconds")
+            logger.debug("Caching due to etag")
+            self._cache_set(cache_url, request, response, body, expires_time)
+
+        # Add to the cache any permanent redirects. We do this before looking
+        # that the Date headers.
+        elif int(response.status) in PERMANENT_REDIRECT_STATUSES:
+            logger.debug("Caching permanent redirect")
+            self._cache_set(cache_url, request, response, b"")
+
+        # Add to the cache if the response headers demand it. If there
+        # is no date header then we can't do anything about expiring
+        # the cache.
+        elif "date" in response_headers:
+            time_tuple = parsedate_tz(response_headers["date"])
+            assert time_tuple is not None
+            date = calendar.timegm(time_tuple[:6])
+            # cache when there is a max-age > 0
+            max_age = cc.get("max-age")
+            if max_age is not None and max_age > 0:
+                logger.debug("Caching b/c date exists and max-age > 0")
+                expires_time = max_age
+                self._cache_set(
+                    cache_url,
+                    request,
+                    response,
+                    body,
+                    expires_time,
+                )
+
+            # If the request can expire, it means we should cache it
+            # in the meantime.
+            elif "expires" in response_headers:
+                if response_headers["expires"]:
+                    expires = parsedate_tz(response_headers["expires"])
+                    if expires is not None:
+                        expires_time = calendar.timegm(expires[:6]) - date
+                    else:
+                        expires_time = None
+
+                    logger.debug(
+                        "Caching b/c of expires header. expires in {} seconds".format(
+                            expires_time
+                        )
+                    )
+                    self._cache_set(
+                        cache_url,
+                        request,
+                        response,
+                        body,
+                        expires_time,
+                    )
+
+    def update_cached_response(
+        self, request: PreparedRequest, response: HTTPResponse
+    ) -> HTTPResponse:
+        """On a 304 we will get a new set of headers that we want to
+        update our cached value with, assuming we have one.
+
+        This should only ever be called when we've sent an ETag and
+        gotten a 304 as the response.
+        """
+        assert request.url is not None
+        cache_url = self.cache_url(request.url)
+        cached_response = self._load_from_cache(request)
+
+        if not cached_response:
+            # we didn't have a cached response
+            return response
+
+        # Lets update our headers with the headers from the new request:
+        # http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-4.1
+        #
+        # The server isn't supposed to send headers that would make
+        # the cached body invalid. But... just in case, we'll be sure
+        # to strip out ones we know that might be problematic due to
+        # typical assumptions.
+        excluded_headers = ["content-length"]
+
+        cached_response.headers.update(
+            {
+                k: v
+                for k, v in response.headers.items()
+                if k.lower() not in excluded_headers
+            }
+        )
+
+        # we want a 200 b/c we have content via the cache
+        cached_response.status = 200
+
+        # update our cache
+        self._cache_set(cache_url, request, cached_response)
+
+        return cached_response
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/filewrapper.py b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/filewrapper.py
new file mode 100644
index 0000000..6569fb5
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/filewrapper.py
@@ -0,0 +1,121 @@
+# SPDX-FileCopyrightText: 2015 Eric Larson
+#
+# SPDX-License-Identifier: Apache-2.0
+from __future__ import annotations
+
+import mmap
+from tempfile import NamedTemporaryFile
+from typing import TYPE_CHECKING, Any, Callable
+
+if TYPE_CHECKING:
+    from collections.abc import Buffer
+    from http.client import HTTPResponse
+
+
+class CallbackFileWrapper:
+    """
+    Small wrapper around a fp object which will tee everything read into a
+    buffer, and when that file is closed it will execute a callback with the
+    contents of that buffer.
+
+    All attributes are proxied to the underlying file object.
+
+    This class uses members with a double underscore (__) leading prefix so as
+    not to accidentally shadow an attribute.
+
+    The data is stored in a temporary file until it is all available.  As long
+    as the temporary files directory is disk-based (sometimes it's a
+    memory-backed-``tmpfs`` on Linux), data will be unloaded to disk if memory
+    pressure is high.  For small files the disk usually won't be used at all,
+    it'll all be in the filesystem memory cache, so there should be no
+    performance impact.
+    """
+
+    def __init__(
+        self, fp: HTTPResponse, callback: Callable[[Buffer], None] | None
+    ) -> None:
+        self.__buf = NamedTemporaryFile("rb+", delete=True)
+        self.__fp = fp
+        self.__callback = callback
+
+    def __getattr__(self, name: str) -> Any:
+        # The vagaries of garbage collection means that self.__fp is
+        # not always set.  By using __getattribute__ and the private
+        # name[0] allows looking up the attribute value and raising an
+        # AttributeError when it doesn't exist. This stop things from
+        # infinitely recursing calls to getattr in the case where
+        # self.__fp hasn't been set.
+        #
+        # [0] https://docs.python.org/2/reference/expressions.html#atom-identifiers
+        fp = self.__getattribute__("_CallbackFileWrapper__fp")
+        return getattr(fp, name)
+
+    def __is_fp_closed(self) -> bool:
+        try:
+            return self.__fp.fp is None
+
+        except AttributeError:
+            pass
+
+        try:
+            closed: bool = self.__fp.closed
+            return closed
+
+        except AttributeError:
+            pass
+
+        # We just don't cache it then.
+        # TODO: Add some logging here...
+        return False
+
+    def _close(self) -> None:
+        result: Buffer
+        if self.__callback:
+            if self.__buf.tell() == 0:
+                # Empty file:
+                result = b""
+            else:
+                # Return the data without actually loading it into memory,
+                # relying on Python's buffer API and mmap(). mmap() just gives
+                # a view directly into the filesystem's memory cache, so it
+                # doesn't result in duplicate memory use.
+                self.__buf.seek(0, 0)
+                result = memoryview(
+                    mmap.mmap(self.__buf.fileno(), 0, access=mmap.ACCESS_READ)
+                )
+            self.__callback(result)
+
+        # We assign this to None here, because otherwise we can get into
+        # really tricky problems where the CPython interpreter dead locks
+        # because the callback is holding a reference to something which
+        # has a __del__ method. Setting this to None breaks the cycle
+        # and allows the garbage collector to do it's thing normally.
+        self.__callback = None
+
+        # Closing the temporary file releases memory and frees disk space.
+        # Important when caching big files.
+        self.__buf.close()
+
+    def read(self, amt: int | None = None) -> bytes:
+        data: bytes = self.__fp.read(amt)
+        if data:
+            # We may be dealing with b'', a sign that things are over:
+            # it's passed e.g. after we've already closed self.__buf.
+            self.__buf.write(data)
+        if self.__is_fp_closed():
+            self._close()
+
+        return data
+
+    def _safe_read(self, amt: int) -> bytes:
+        data: bytes = self.__fp._safe_read(amt)  # type: ignore[attr-defined]
+        if amt == 2 and data == b"\r\n":
+            # urllib executes this read to toss the CRLF at the end
+            # of the chunk.
+            return data
+
+        self.__buf.write(data)
+        if self.__is_fp_closed():
+            self._close()
+
+        return data
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/heuristics.py b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/heuristics.py
new file mode 100644
index 0000000..b778c4f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/heuristics.py
@@ -0,0 +1,157 @@
+# SPDX-FileCopyrightText: 2015 Eric Larson
+#
+# SPDX-License-Identifier: Apache-2.0
+from __future__ import annotations
+
+import calendar
+import time
+from datetime import datetime, timedelta, timezone
+from email.utils import formatdate, parsedate, parsedate_tz
+from typing import TYPE_CHECKING, Any, Mapping
+
+if TYPE_CHECKING:
+    from pip._vendor.urllib3 import HTTPResponse
+
+TIME_FMT = "%a, %d %b %Y %H:%M:%S GMT"
+
+
+def expire_after(delta: timedelta, date: datetime | None = None) -> datetime:
+    date = date or datetime.now(timezone.utc)
+    return date + delta
+
+
+def datetime_to_header(dt: datetime) -> str:
+    return formatdate(calendar.timegm(dt.timetuple()))
+
+
+class BaseHeuristic:
+    def warning(self, response: HTTPResponse) -> str | None:
+        """
+        Return a valid 1xx warning header value describing the cache
+        adjustments.
+
+        The response is provided too allow warnings like 113
+        http://tools.ietf.org/html/rfc7234#section-5.5.4 where we need
+        to explicitly say response is over 24 hours old.
+        """
+        return '110 - "Response is Stale"'
+
+    def update_headers(self, response: HTTPResponse) -> dict[str, str]:
+        """Update the response headers with any new headers.
+
+        NOTE: This SHOULD always include some Warning header to
+              signify that the response was cached by the client, not
+              by way of the provided headers.
+        """
+        return {}
+
+    def apply(self, response: HTTPResponse) -> HTTPResponse:
+        updated_headers = self.update_headers(response)
+
+        if updated_headers:
+            response.headers.update(updated_headers)
+            warning_header_value = self.warning(response)
+            if warning_header_value is not None:
+                response.headers.update({"Warning": warning_header_value})
+
+        return response
+
+
+class OneDayCache(BaseHeuristic):
+    """
+    Cache the response by providing an expires 1 day in the
+    future.
+    """
+
+    def update_headers(self, response: HTTPResponse) -> dict[str, str]:
+        headers = {}
+
+        if "expires" not in response.headers:
+            date = parsedate(response.headers["date"])
+            expires = expire_after(
+                timedelta(days=1),
+                date=datetime(*date[:6], tzinfo=timezone.utc),  # type: ignore[index,misc]
+            )
+            headers["expires"] = datetime_to_header(expires)
+            headers["cache-control"] = "public"
+        return headers
+
+
+class ExpiresAfter(BaseHeuristic):
+    """
+    Cache **all** requests for a defined time period.
+    """
+
+    def __init__(self, **kw: Any) -> None:
+        self.delta = timedelta(**kw)
+
+    def update_headers(self, response: HTTPResponse) -> dict[str, str]:
+        expires = expire_after(self.delta)
+        return {"expires": datetime_to_header(expires), "cache-control": "public"}
+
+    def warning(self, response: HTTPResponse) -> str | None:
+        tmpl = "110 - Automatically cached for %s. Response might be stale"
+        return tmpl % self.delta
+
+
+class LastModified(BaseHeuristic):
+    """
+    If there is no Expires header already, fall back on Last-Modified
+    using the heuristic from
+    http://tools.ietf.org/html/rfc7234#section-4.2.2
+    to calculate a reasonable value.
+
+    Firefox also does something like this per
+    https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching_FAQ
+    http://lxr.mozilla.org/mozilla-release/source/netwerk/protocol/http/nsHttpResponseHead.cpp#397
+    Unlike mozilla we limit this to 24-hr.
+    """
+
+    cacheable_by_default_statuses = {
+        200,
+        203,
+        204,
+        206,
+        300,
+        301,
+        404,
+        405,
+        410,
+        414,
+        501,
+    }
+
+    def update_headers(self, resp: HTTPResponse) -> dict[str, str]:
+        headers: Mapping[str, str] = resp.headers
+
+        if "expires" in headers:
+            return {}
+
+        if "cache-control" in headers and headers["cache-control"] != "public":
+            return {}
+
+        if resp.status not in self.cacheable_by_default_statuses:
+            return {}
+
+        if "date" not in headers or "last-modified" not in headers:
+            return {}
+
+        time_tuple = parsedate_tz(headers["date"])
+        assert time_tuple is not None
+        date = calendar.timegm(time_tuple[:6])
+        last_modified = parsedate(headers["last-modified"])
+        if last_modified is None:
+            return {}
+
+        now = time.time()
+        current_age = max(0, now - date)
+        delta = date - calendar.timegm(last_modified)
+        freshness_lifetime = max(0, min(delta / 10, 24 * 3600))
+        if freshness_lifetime <= current_age:
+            return {}
+
+        expires = date + freshness_lifetime
+        return {"expires": time.strftime(TIME_FMT, time.gmtime(expires))}
+
+    def warning(self, resp: HTTPResponse) -> str | None:
+        return None
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/serialize.py b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/serialize.py
new file mode 100644
index 0000000..a49487a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/serialize.py
@@ -0,0 +1,146 @@
+# SPDX-FileCopyrightText: 2015 Eric Larson
+#
+# SPDX-License-Identifier: Apache-2.0
+from __future__ import annotations
+
+import io
+from typing import IO, TYPE_CHECKING, Any, Mapping, cast
+
+from pip._vendor import msgpack
+from pip._vendor.requests.structures import CaseInsensitiveDict
+from pip._vendor.urllib3 import HTTPResponse
+
+if TYPE_CHECKING:
+    from pip._vendor.requests import PreparedRequest
+
+
+class Serializer:
+    serde_version = "4"
+
+    def dumps(
+        self,
+        request: PreparedRequest,
+        response: HTTPResponse,
+        body: bytes | None = None,
+    ) -> bytes:
+        response_headers: CaseInsensitiveDict[str] = CaseInsensitiveDict(
+            response.headers
+        )
+
+        if body is None:
+            # When a body isn't passed in, we'll read the response. We
+            # also update the response with a new file handler to be
+            # sure it acts as though it was never read.
+            body = response.read(decode_content=False)
+            response._fp = io.BytesIO(body)  # type: ignore[assignment]
+            response.length_remaining = len(body)
+
+        data = {
+            "response": {
+                "body": body,  # Empty bytestring if body is stored separately
+                "headers": {str(k): str(v) for k, v in response.headers.items()},
+                "status": response.status,
+                "version": response.version,
+                "reason": str(response.reason),
+                "decode_content": response.decode_content,
+            }
+        }
+
+        # Construct our vary headers
+        data["vary"] = {}
+        if "vary" in response_headers:
+            varied_headers = response_headers["vary"].split(",")
+            for header in varied_headers:
+                header = str(header).strip()
+                header_value = request.headers.get(header, None)
+                if header_value is not None:
+                    header_value = str(header_value)
+                data["vary"][header] = header_value
+
+        return b",".join([f"cc={self.serde_version}".encode(), self.serialize(data)])
+
+    def serialize(self, data: dict[str, Any]) -> bytes:
+        return cast(bytes, msgpack.dumps(data, use_bin_type=True))
+
+    def loads(
+        self,
+        request: PreparedRequest,
+        data: bytes,
+        body_file: IO[bytes] | None = None,
+    ) -> HTTPResponse | None:
+        # Short circuit if we've been given an empty set of data
+        if not data:
+            return None
+
+        # Previous versions of this library supported other serialization
+        # formats, but these have all been removed.
+        if not data.startswith(f"cc={self.serde_version},".encode()):
+            return None
+
+        data = data[5:]
+        return self._loads_v4(request, data, body_file)
+
+    def prepare_response(
+        self,
+        request: PreparedRequest,
+        cached: Mapping[str, Any],
+        body_file: IO[bytes] | None = None,
+    ) -> HTTPResponse | None:
+        """Verify our vary headers match and construct a real urllib3
+        HTTPResponse object.
+        """
+        # Special case the '*' Vary value as it means we cannot actually
+        # determine if the cached response is suitable for this request.
+        # This case is also handled in the controller code when creating
+        # a cache entry, but is left here for backwards compatibility.
+        if "*" in cached.get("vary", {}):
+            return None
+
+        # Ensure that the Vary headers for the cached response match our
+        # request
+        for header, value in cached.get("vary", {}).items():
+            if request.headers.get(header, None) != value:
+                return None
+
+        body_raw = cached["response"].pop("body")
+
+        headers: CaseInsensitiveDict[str] = CaseInsensitiveDict(
+            data=cached["response"]["headers"]
+        )
+        if headers.get("transfer-encoding", "") == "chunked":
+            headers.pop("transfer-encoding")
+
+        cached["response"]["headers"] = headers
+
+        try:
+            body: IO[bytes]
+            if body_file is None:
+                body = io.BytesIO(body_raw)
+            else:
+                body = body_file
+        except TypeError:
+            # This can happen if cachecontrol serialized to v1 format (pickle)
+            # using Python 2. A Python 2 str(byte string) will be unpickled as
+            # a Python 3 str (unicode string), which will cause the above to
+            # fail with:
+            #
+            #     TypeError: 'str' does not support the buffer interface
+            body = io.BytesIO(body_raw.encode("utf8"))
+
+        # Discard any `strict` parameter serialized by older version of cachecontrol.
+        cached["response"].pop("strict", None)
+
+        return HTTPResponse(body=body, preload_content=False, **cached["response"])
+
+    def _loads_v4(
+        self,
+        request: PreparedRequest,
+        data: bytes,
+        body_file: IO[bytes] | None = None,
+    ) -> HTTPResponse | None:
+        try:
+            cached = msgpack.loads(data, raw=False)
+        except ValueError:
+            return None
+
+        return self.prepare_response(request, cached, body_file)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/wrapper.py b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/wrapper.py
new file mode 100644
index 0000000..f618bc3
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/wrapper.py
@@ -0,0 +1,43 @@
+# SPDX-FileCopyrightText: 2015 Eric Larson
+#
+# SPDX-License-Identifier: Apache-2.0
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Collection
+
+from pip._vendor.cachecontrol.adapter import CacheControlAdapter
+from pip._vendor.cachecontrol.cache import DictCache
+
+if TYPE_CHECKING:
+    from pip._vendor import requests
+
+    from pip._vendor.cachecontrol.cache import BaseCache
+    from pip._vendor.cachecontrol.controller import CacheController
+    from pip._vendor.cachecontrol.heuristics import BaseHeuristic
+    from pip._vendor.cachecontrol.serialize import Serializer
+
+
+def CacheControl(
+    sess: requests.Session,
+    cache: BaseCache | None = None,
+    cache_etags: bool = True,
+    serializer: Serializer | None = None,
+    heuristic: BaseHeuristic | None = None,
+    controller_class: type[CacheController] | None = None,
+    adapter_class: type[CacheControlAdapter] | None = None,
+    cacheable_methods: Collection[str] | None = None,
+) -> requests.Session:
+    cache = DictCache() if cache is None else cache
+    adapter_class = adapter_class or CacheControlAdapter
+    adapter = adapter_class(
+        cache,
+        cache_etags=cache_etags,
+        serializer=serializer,
+        heuristic=heuristic,
+        controller_class=controller_class,
+        cacheable_methods=cacheable_methods,
+    )
+    sess.mount("http://", adapter)
+    sess.mount("https://", adapter)
+
+    return sess
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/certifi/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/LICENSE
new file mode 100644
index 0000000..62b076c
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/LICENSE
@@ -0,0 +1,20 @@
+This package contains a modified version of ca-bundle.crt:
+
+ca-bundle.crt -- Bundle of CA Root Certificates
+
+This is a bundle of X.509 certificates of public Certificate Authorities
+(CA). These were automatically extracted from Mozilla's root certificates
+file (certdata.txt).  This file can be found in the mozilla source tree:
+https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt
+It contains the certificates in PEM format and therefore
+can be directly used with curl / libcurl / php_curl, or with
+an Apache+mod_ssl webserver for SSL client authentication.
+Just configure this file as the SSLCACertificateFile.#
+
+***** BEGIN LICENSE BLOCK *****
+This Source Code Form is subject to the terms of the Mozilla Public License,
+v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain
+one at http://mozilla.org/MPL/2.0/.
+
+***** END LICENSE BLOCK *****
+@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__init__.py
new file mode 100644
index 0000000..ed9a74b
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__init__.py
@@ -0,0 +1,4 @@
+from .core import contents, where
+
+__all__ = ["contents", "where"]
+__version__ = "2026.06.17"
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__main__.py b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__main__.py
new file mode 100644
index 0000000..0037634
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__main__.py
@@ -0,0 +1,12 @@
+import argparse
+
+from pip._vendor.certifi import contents, where
+
+parser = argparse.ArgumentParser()
+parser.add_argument("-c", "--contents", action="store_true")
+args = parser.parse_args()
+
+if args.contents:
+    print(contents())
+else:
+    print(where())
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..fd1a53c
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-312.pyc
new file mode 100644
index 0000000..c380e78
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-312.pyc
new file mode 100644
index 0000000..acbca83
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/certifi/cacert.pem b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/cacert.pem
new file mode 100644
index 0000000..1c2dbfe
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/cacert.pem
@@ -0,0 +1,3863 @@
+
+# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited
+# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited
+# Label: "COMODO ECC Certification Authority"
+# Serial: 41578283867086692638256921589707938090
+# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23
+# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11
+# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7
+-----BEGIN CERTIFICATE-----
+MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
+MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
+BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
+IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw
+MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
+ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
+T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR
+FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J
+cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW
+BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
+BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm
+fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
+GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
+-----END CERTIFICATE-----
+
+# Issuer: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services)
+# Subject: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services)
+# Label: "NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny"
+# Serial: 80544274841616
+# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88
+# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91
+# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98
+-----BEGIN CERTIFICATE-----
+MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG
+EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3
+MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl
+cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR
+dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB
+pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM
+b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz
+IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT
+lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz
+AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5
+VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG
+ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2
+BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG
+AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M
+U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh
+bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C
++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC
+bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F
+uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2
+XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd.
+# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd.
+# Label: "Microsec e-Szigno Root CA 2009"
+# Serial: 14014712776195784473
+# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1
+# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e
+# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78
+-----BEGIN CERTIFICATE-----
+MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD
+VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0
+ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G
+CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y
+OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx
+FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp
+Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o
+dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP
+kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc
+cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U
+fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7
+N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC
+xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1
++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
+A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM
+Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG
+SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h
+mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk
+ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775
+tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c
+2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t
+HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW
+-----END CERTIFICATE-----
+
+# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3
+# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3
+# Label: "GlobalSign Root CA - R3"
+# Serial: 4835703278459759426209954
+# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28
+# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad
+# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b
+-----BEGIN CERTIFICATE-----
+MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
+MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
+A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
+RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
+gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
+KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
+QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
+XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
+DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
+LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
+RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
+jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
+6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
+mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
+Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
+WD9f
+-----END CERTIFICATE-----
+
+# Issuer: CN=Izenpe.com O=IZENPE S.A.
+# Subject: CN=Izenpe.com O=IZENPE S.A.
+# Label: "Izenpe.com"
+# Serial: 917563065490389241595536686991402621
+# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73
+# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19
+# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f
+-----BEGIN CERTIFICATE-----
+MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4
+MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6
+ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD
+VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j
+b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq
+scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO
+xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H
+LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX
+uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD
+yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+
+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q
+rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN
+BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L
+hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB
+QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+
+HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu
+Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg
+QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB
+BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
+MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA
+A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb
+laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56
+awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo
+JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw
+LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT
+VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk
+LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb
+UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/
+QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+
+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls
+QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc.
+# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc.
+# Label: "Go Daddy Root Certificate Authority - G2"
+# Serial: 0
+# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01
+# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b
+# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
+EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp
+ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz
+NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH
+EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE
+AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD
+E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH
+/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy
+DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh
+GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR
+tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA
+AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
+FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX
+WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu
+9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr
+gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo
+2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
+LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI
+4uJEvlz36hz1
+-----END CERTIFICATE-----
+
+# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc.
+# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc.
+# Label: "Starfield Root Certificate Authority - G2"
+# Serial: 0
+# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96
+# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e
+# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5
+-----BEGIN CERTIFICATE-----
+MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
+HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
+ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw
+MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
+b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
+aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp
+Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg
+nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1
+HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N
+Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN
+dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0
+HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
+BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G
+CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU
+sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3
+4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg
+8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
+pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
+mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
+-----END CERTIFICATE-----
+
+# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc.
+# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc.
+# Label: "Starfield Services Root Certificate Authority - G2"
+# Serial: 0
+# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2
+# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f
+# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5
+-----BEGIN CERTIFICATE-----
+MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
+HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs
+ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
+MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD
+VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy
+ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy
+dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p
+OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2
+8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K
+Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe
+hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk
+6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw
+DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q
+AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI
+bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB
+ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z
+qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
+iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn
+0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN
+sSi6
+-----END CERTIFICATE-----
+
+# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority
+# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority
+# Label: "Certum Trusted Network CA"
+# Serial: 279744
+# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78
+# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e
+# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e
+-----BEGIN CERTIFICATE-----
+MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM
+MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D
+ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU
+cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3
+WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg
+Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw
+IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH
+UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM
+TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU
+BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM
+kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x
+AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV
+HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y
+sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL
+I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8
+J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY
+VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
+03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
+-----END CERTIFICATE-----
+
+# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA
+# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA
+# Label: "TWCA Root Certification Authority"
+# Serial: 1
+# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79
+# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48
+# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44
+-----BEGIN CERTIFICATE-----
+MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES
+MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU
+V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz
+WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO
+LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE
+AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH
+K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX
+RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z
+rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx
+3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq
+hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC
+MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls
+XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D
+lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn
+aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ
+YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==
+-----END CERTIFICATE-----
+
+# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2
+# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2
+# Label: "Security Communication RootCA2"
+# Serial: 0
+# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43
+# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74
+# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl
+MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe
+U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX
+DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy
+dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj
+YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV
+OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr
+zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM
+VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ
+hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO
+ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw
+awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs
+OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF
+coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc
+okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8
+t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy
+1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/
+SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
+-----END CERTIFICATE-----
+
+# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967
+# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967
+# Label: "Actalis Authentication Root CA"
+# Serial: 6271844772424770508
+# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6
+# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac
+# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66
+-----BEGIN CERTIFICATE-----
+MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE
+BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w
+MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290
+IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC
+SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1
+ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv
+UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX
+4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9
+KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/
+gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb
+rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ
+51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F
+be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe
+KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F
+v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn
+fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7
+jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz
+ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt
+ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL
+e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70
+jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz
+WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V
+SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j
+pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX
+X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok
+fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R
+K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU
+ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU
+LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT
+LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327
+# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327
+# Label: "Buypass Class 2 Root CA"
+# Serial: 2
+# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29
+# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99
+# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48
+-----BEGIN CERTIFICATE-----
+MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
+MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
+Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow
+TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw
+HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB
+BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr
+6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV
+L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91
+1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx
+MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ
+QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB
+arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr
+Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi
+FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS
+P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN
+9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP
+AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz
+uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h
+9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s
+A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t
+OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo
++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7
+KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2
+DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us
+H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ
+I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7
+5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h
+3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz
+Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327
+# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327
+# Label: "Buypass Class 3 Root CA"
+# Serial: 2
+# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec
+# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57
+# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d
+-----BEGIN CERTIFICATE-----
+MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
+MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
+Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow
+TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw
+HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB
+BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y
+ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E
+N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9
+tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX
+0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c
+/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X
+KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY
+zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS
+O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D
+34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP
+K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3
+AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv
+Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj
+QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV
+cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS
+IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2
+HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa
+O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv
+033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u
+dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE
+kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41
+3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD
+u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq
+4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc=
+-----END CERTIFICATE-----
+
+# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
+# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
+# Label: "T-TeleSec GlobalRoot Class 3"
+# Serial: 1
+# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef
+# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1
+# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd
+-----BEGIN CERTIFICATE-----
+MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
+KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
+BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
+YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1
+OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy
+aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50
+ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN
+8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/
+RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4
+hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5
+ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM
+EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj
+QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1
+A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy
+WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ
+1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30
+6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT
+91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml
+e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p
+TpPDpFQUWw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH
+# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH
+# Label: "D-TRUST Root Class 3 CA 2 2009"
+# Serial: 623603
+# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f
+# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0
+# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1
+-----BEGIN CERTIFICATE-----
+MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF
+MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD
+bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha
+ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM
+HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03
+UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42
+tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R
+ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM
+lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp
+/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G
+A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G
+A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj
+dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy
+MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl
+cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js
+L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL
+BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni
+acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0
+o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K
+zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8
+PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y
+Johw1+qRzT65ysCQblrGXnRl11z+o+I=
+-----END CERTIFICATE-----
+
+# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH
+# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH
+# Label: "D-TRUST Root Class 3 CA 2 EV 2009"
+# Serial: 623604
+# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6
+# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83
+# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81
+-----BEGIN CERTIFICATE-----
+MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF
+MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD
+bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw
+NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV
+BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn
+ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0
+3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z
+qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR
+p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8
+HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw
+ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea
+HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw
+Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh
+c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E
+RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt
+dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku
+Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp
+3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05
+nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF
+CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na
+xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX
+KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1
+-----END CERTIFICATE-----
+
+# Issuer: CN=CA Disig Root R2 O=Disig a.s.
+# Subject: CN=CA Disig Root R2 O=Disig a.s.
+# Label: "CA Disig Root R2"
+# Serial: 10572350602393338211
+# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03
+# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71
+# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03
+-----BEGIN CERTIFICATE-----
+MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV
+BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu
+MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy
+MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx
+EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw
+ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe
+NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH
+PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I
+x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe
+QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR
+yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO
+QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912
+H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ
+QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD
+i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs
+nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1
+rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud
+DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI
+hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM
+tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf
+GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb
+lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka
++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal
+TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i
+nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3
+gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr
+G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os
+zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x
+L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL
+-----END CERTIFICATE-----
+
+# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV
+# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV
+# Label: "ACCVRAIZ1"
+# Serial: 6828503384748696800
+# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02
+# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17
+# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13
+-----BEGIN CERTIFICATE-----
+MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE
+AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw
+CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ
+BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND
+VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb
+qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY
+HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo
+G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA
+lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr
+IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/
+0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH
+k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47
+4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO
+m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa
+cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl
+uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI
+KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls
+ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG
+AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2
+VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT
+VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG
+CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA
+cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA
+QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA
+7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA
+cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA
+QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA
+czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu
+aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt
+aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud
+DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF
+BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp
+D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU
+JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m
+AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD
+vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms
+tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH
+7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h
+I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA
+h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF
+d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H
+pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7
+-----END CERTIFICATE-----
+
+# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA
+# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA
+# Label: "TWCA Global Root CA"
+# Serial: 3262
+# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96
+# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65
+# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b
+-----BEGIN CERTIFICATE-----
+MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx
+EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT
+VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5
+NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT
+B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF
+10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz
+0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh
+MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH
+zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc
+46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2
+yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi
+laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP
+oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA
+BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE
+qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm
+4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL
+1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn
+LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF
+H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo
+RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+
+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh
+15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW
+6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW
+nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j
+wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz
+aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy
+KwbQBM0=
+-----END CERTIFICATE-----
+
+# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
+# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
+# Label: "T-TeleSec GlobalRoot Class 2"
+# Serial: 1
+# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a
+# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9
+# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52
+-----BEGIN CERTIFICATE-----
+MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
+KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
+BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
+YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1
+OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy
+aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50
+ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd
+AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC
+FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi
+1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq
+jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ
+wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj
+QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/
+WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy
+NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC
+uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw
+IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6
+g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN
+9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP
+BSeOE6Fuwg==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Atos TrustedRoot 2011 O=Atos
+# Subject: CN=Atos TrustedRoot 2011 O=Atos
+# Label: "Atos TrustedRoot 2011"
+# Serial: 6643877497813316402
+# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56
+# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21
+# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE
+AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG
+EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM
+FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC
+REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp
+Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM
+VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+
+SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ
+4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L
+cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi
+eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV
+HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG
+A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3
+DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j
+vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP
+DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc
+maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D
+lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv
+KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
+-----END CERTIFICATE-----
+
+# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited
+# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited
+# Label: "QuoVadis Root CA 1 G3"
+# Serial: 687049649626669250736271037606554624078720034195
+# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab
+# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67
+# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL
+BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00
+MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV
+wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe
+rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341
+68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh
+4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp
+UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o
+abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc
+3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G
+KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt
+hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO
+Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt
+zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD
+ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC
+MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2
+cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN
+qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5
+YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv
+b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2
+8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k
+NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj
+ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp
+q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt
+nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD
+-----END CERTIFICATE-----
+
+# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited
+# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited
+# Label: "QuoVadis Root CA 2 G3"
+# Serial: 390156079458959257446133169266079962026824725800
+# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06
+# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36
+# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL
+BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00
+MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf
+qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW
+n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym
+c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+
+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1
+o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j
+IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq
+IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz
+8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh
+vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l
+7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG
+cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD
+ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66
+AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC
+roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga
+W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n
+lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE
++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV
+csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd
+dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg
+KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM
+HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4
+WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M
+-----END CERTIFICATE-----
+
+# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited
+# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited
+# Label: "QuoVadis Root CA 3 G3"
+# Serial: 268090761170461462463995952157327242137089239581
+# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7
+# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d
+# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL
+BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00
+MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR
+/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu
+FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR
+U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c
+ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR
+FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k
+A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw
+eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl
+sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp
+VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q
+A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+
+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD
+ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px
+KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI
+FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv
+oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg
+u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP
+0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf
+3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl
+8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+
+DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN
+PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/
+ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0
+-----END CERTIFICATE-----
+
+# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com
+# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com
+# Label: "DigiCert Assured ID Root G2"
+# Serial: 15385348160840213938643033620894905419
+# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d
+# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f
+# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85
+-----BEGIN CERTIFICATE-----
+MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
+b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
+cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA
+n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc
+biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp
+EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA
+bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu
+YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB
+AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW
+BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI
+QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I
+0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni
+lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9
+B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv
+ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo
+IhNzbM8m9Yop5w==
+-----END CERTIFICATE-----
+
+# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com
+# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com
+# Label: "DigiCert Assured ID Root G3"
+# Serial: 15459312981008553731928384953135426796
+# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb
+# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89
+# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2
+-----BEGIN CERTIFICATE-----
+MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw
+CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
+ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg
+RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV
+UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
+Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq
+hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf
+Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q
+RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
+BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD
+AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY
+JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv
+6pZjamVFkpUBtA==
+-----END CERTIFICATE-----
+
+# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com
+# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com
+# Label: "DigiCert Global Root G2"
+# Serial: 4293743540046975378534879503202253541
+# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44
+# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4
+# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f
+-----BEGIN CERTIFICATE-----
+MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
+MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
+b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
+2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
+1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
+q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
+tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
+vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
+BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
+5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
+1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
+NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
+Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
+8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
+pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
+MrY=
+-----END CERTIFICATE-----
+
+# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com
+# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com
+# Label: "DigiCert Global Root G3"
+# Serial: 7089244469030293291760083333884364146
+# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca
+# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e
+# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0
+-----BEGIN CERTIFICATE-----
+MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw
+CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
+ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe
+Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw
+EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
+IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF
+K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG
+fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO
+Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd
+BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx
+AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/
+oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8
+sycX
+-----END CERTIFICATE-----
+
+# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com
+# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com
+# Label: "DigiCert Trusted Root G4"
+# Serial: 7451500558977370777930084869016614236
+# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49
+# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4
+# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88
+-----BEGIN CERTIFICATE-----
+MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg
+RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV
+UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
+Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y
+ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If
+xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV
+ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO
+DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ
+jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/
+CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi
+EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM
+fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY
+uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK
+chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t
+9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD
+ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2
+SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd
++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc
+fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa
+sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N
+cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N
+0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie
+4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI
+r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1
+/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm
+gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+
+-----END CERTIFICATE-----
+
+# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited
+# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited
+# Label: "COMODO RSA Certification Authority"
+# Serial: 101909084537582093308941363524873193117
+# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18
+# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4
+# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34
+-----BEGIN CERTIFICATE-----
+MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB
+hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
+BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5
+MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
+EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
+Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR
+6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X
+pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC
+9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV
+/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf
+Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z
++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w
+qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah
+SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC
+u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf
+Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq
+crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
+FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
+/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl
+wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM
+4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV
+2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna
+FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ
+CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK
+boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke
+jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL
+S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb
+QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl
+0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB
+NVOFBkpdn627G190
+-----END CERTIFICATE-----
+
+# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network
+# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network
+# Label: "USERTrust RSA Certification Authority"
+# Serial: 2645093764781058787591871645665788717
+# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5
+# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e
+# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2
+-----BEGIN CERTIFICATE-----
+MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
+iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
+cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
+BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
+MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
+BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
+aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
+AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
+3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
+tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
+Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
+VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
+79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
+c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
+Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
+c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
+UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
+Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
+BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
+A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
+Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
+VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
+ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
+8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
+iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
+Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
+XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
+qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
+VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
+L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
+jjxDah2nGN59PRbxYvnKkKj9
+-----END CERTIFICATE-----
+
+# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network
+# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network
+# Label: "USERTrust ECC Certification Authority"
+# Serial: 123013823720199481456569720443997572134
+# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1
+# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0
+# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a
+-----BEGIN CERTIFICATE-----
+MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL
+MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl
+eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT
+JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx
+MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
+Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg
+VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo
+I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng
+o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G
+A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD
+VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB
+zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW
+RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
+-----END CERTIFICATE-----
+
+# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5
+# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5
+# Label: "GlobalSign ECC Root CA - R5"
+# Serial: 32785792099990507226680698011560947931244
+# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08
+# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa
+# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24
+-----BEGIN CERTIFICATE-----
+MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk
+MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH
+bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
+DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
+QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
+MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc
+8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke
+hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD
+VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI
+KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg
+515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO
+xwy8p2Fp8fc74SrL+SvzZpA3
+-----END CERTIFICATE-----
+
+# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust
+# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust
+# Label: "IdenTrust Commercial Root CA 1"
+# Serial: 13298821034946342390520003877796839426
+# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7
+# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25
+# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK
+MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu
+VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw
+MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw
+JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT
+3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU
++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp
+S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1
+bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi
+T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL
+vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK
+Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK
+dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT
+c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv
+l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N
+iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD
+ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH
+6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt
+LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93
+nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3
++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK
+W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT
+AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq
+l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG
+4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ
+mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A
+7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H
+-----END CERTIFICATE-----
+
+# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust
+# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust
+# Label: "IdenTrust Public Sector Root CA 1"
+# Serial: 13298821034946342390521976156843933698
+# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba
+# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd
+# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f
+-----BEGIN CERTIFICATE-----
+MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN
+MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu
+VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN
+MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0
+MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7
+ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy
+RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS
+bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF
+/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R
+3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw
+EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy
+9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V
+GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ
+2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV
+WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD
+W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
+BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN
+AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj
+t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV
+DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9
+TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G
+lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW
+mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df
+WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5
++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ
+tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA
+GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv
+8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c
+-----END CERTIFICATE-----
+
+# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority
+# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority
+# Label: "CFCA EV ROOT"
+# Serial: 407555286
+# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30
+# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83
+# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd
+-----BEGIN CERTIFICATE-----
+MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD
+TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx
+MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j
+aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP
+T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03
+sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL
+TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5
+/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp
+7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz
+EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt
+hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP
+a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot
+aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg
+TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV
+PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv
+cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL
+tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd
+BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB
+ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT
+ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL
+jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS
+ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy
+P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19
+xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d
+Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN
+5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe
+/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z
+AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ
+5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
+-----END CERTIFICATE-----
+
+# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed
+# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed
+# Label: "OISTE WISeKey Global Root GB CA"
+# Serial: 157768595616588414422159278966750757568
+# MD5 Fingerprint: a4:eb:b9:61:28:2e:b7:2f:98:b0:35:26:90:99:51:1d
+# SHA1 Fingerprint: 0f:f9:40:76:18:d3:d7:6a:4b:98:f0:a8:35:9e:0c:fd:27:ac:cc:ed
+# SHA256 Fingerprint: 6b:9c:08:e8:6e:b0:f7:67:cf:ad:65:cd:98:b6:21:49:e5:49:4a:67:f5:84:5e:7b:d1:ed:01:9f:27:b8:6b:d6
+-----BEGIN CERTIFICATE-----
+MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt
+MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg
+Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i
+YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x
+CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG
+b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh
+bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3
+HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx
+WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX
+1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk
+u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P
+99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r
+M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB
+BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh
+cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5
+gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO
+ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf
+aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic
+Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM=
+-----END CERTIFICATE-----
+
+# Issuer: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A.
+# Subject: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A.
+# Label: "SZAFIR ROOT CA2"
+# Serial: 357043034767186914217277344587386743377558296292
+# MD5 Fingerprint: 11:64:c1:89:b0:24:b1:8c:b1:07:7e:89:9e:51:9e:99
+# SHA1 Fingerprint: e2:52:fa:95:3f:ed:db:24:60:bd:6e:28:f3:9c:cc:cf:5e:b3:3f:de
+# SHA256 Fingerprint: a1:33:9d:33:28:1a:0b:56:e5:57:d3:d3:2b:1c:e7:f9:36:7e:b0:94:bd:5f:a7:2a:7e:50:04:c8:de:d7:ca:fe
+-----BEGIN CERTIFICATE-----
+MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL
+BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6
+ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw
+NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L
+cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg
+Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN
+QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT
+3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw
+3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6
+3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5
+BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN
+XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
+AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF
+AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw
+8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG
+nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP
+oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy
+d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg
+LvWpCz/UXeHPhJ/iGcJfitYgHuNztw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority
+# Subject: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority
+# Label: "Certum Trusted Network CA 2"
+# Serial: 44979900017204383099463764357512596969
+# MD5 Fingerprint: 6d:46:9e:d9:25:6d:08:23:5b:5e:74:7d:1e:27:db:f2
+# SHA1 Fingerprint: d3:dd:48:3e:2b:bf:4c:05:e8:af:10:f5:fa:76:26:cf:d3:dc:30:92
+# SHA256 Fingerprint: b6:76:f2:ed:da:e8:77:5c:d3:6c:b0:f6:3c:d1:d4:60:39:61:f4:9e:62:65:ba:01:3a:2f:03:07:b6:d0:b8:04
+-----BEGIN CERTIFICATE-----
+MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB
+gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu
+QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG
+A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz
+OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ
+VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3
+b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA
+DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn
+0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB
+OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE
+fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E
+Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m
+o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i
+sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW
+OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez
+Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS
+adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n
+3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC
+AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ
+F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf
+CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29
+XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm
+djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/
+WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb
+AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq
+P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko
+b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj
+XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P
+5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi
+DrW5viSP
+-----END CERTIFICATE-----
+
+# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority
+# Subject: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority
+# Label: "Hellenic Academic and Research Institutions RootCA 2015"
+# Serial: 0
+# MD5 Fingerprint: ca:ff:e2:db:03:d9:cb:4b:e9:0f:ad:84:fd:7b:18:ce
+# SHA1 Fingerprint: 01:0c:06:95:a6:98:19:14:ff:bf:5f:c6:b0:b6:95:ea:29:e9:12:a6
+# SHA256 Fingerprint: a0:40:92:9a:02:ce:53:b4:ac:f4:f2:ff:c6:98:1c:e4:49:6f:75:5e:6d:45:fe:0b:2a:69:2b:cd:52:52:3f:36
+-----BEGIN CERTIFICATE-----
+MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix
+DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k
+IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT
+N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v
+dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG
+A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh
+ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx
+QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1
+dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
+AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA
+4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0
+AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10
+4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C
+ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV
+9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD
+gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6
+Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq
+NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko
+LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc
+Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV
+HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd
+ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I
+XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI
+M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot
+9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V
+Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea
+j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh
+X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ
+l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf
+bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4
+pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK
+e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0
+vm9qp/UsQu0yrbYhnr68
+-----END CERTIFICATE-----
+
+# Issuer: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority
+# Subject: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority
+# Label: "Hellenic Academic and Research Institutions ECC RootCA 2015"
+# Serial: 0
+# MD5 Fingerprint: 81:e5:b4:17:eb:c2:f5:e1:4b:0d:41:7b:49:92:fe:ef
+# SHA1 Fingerprint: 9f:f1:71:8d:92:d5:9a:f3:7d:74:97:b4:bc:6f:84:68:0b:ba:b6:66
+# SHA256 Fingerprint: 44:b5:45:aa:8a:25:e6:5a:73:ca:15:dc:27:fc:36:d2:4c:1c:b9:95:3a:06:65:39:b1:15:82:dc:48:7b:48:33
+-----BEGIN CERTIFICATE-----
+MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN
+BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
+c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl
+bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv
+b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ
+BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj
+YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5
+MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0
+dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg
+QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa
+jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi
+C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep
+lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof
+TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR
+-----END CERTIFICATE-----
+
+# Issuer: CN=ISRG Root X1 O=Internet Security Research Group
+# Subject: CN=ISRG Root X1 O=Internet Security Research Group
+# Label: "ISRG Root X1"
+# Serial: 172886928669790476064670243504169061120
+# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e
+# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8
+# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
+TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
+cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
+WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
+ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
+MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
+h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
+A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
+T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
+B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
+B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
+KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
+OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
+jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
+qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
+rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
+hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
+ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
+3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
+NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
+ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
+TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
+jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
+oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
+4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
+mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
+emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
+-----END CERTIFICATE-----
+
+# Issuer: O=FNMT-RCM OU=AC RAIZ FNMT-RCM
+# Subject: O=FNMT-RCM OU=AC RAIZ FNMT-RCM
+# Label: "AC RAIZ FNMT-RCM"
+# Serial: 485876308206448804701554682760554759
+# MD5 Fingerprint: e2:09:04:b4:d3:bd:d1:a0:14:fd:1a:d2:47:c4:57:1d
+# SHA1 Fingerprint: ec:50:35:07:b2:15:c4:95:62:19:e2:a8:9a:5b:42:99:2c:4c:2c:20
+# SHA256 Fingerprint: eb:c5:57:0c:29:01:8c:4d:67:b1:aa:12:7b:af:12:f7:03:b4:61:1e:bc:17:b7:da:b5:57:38:94:17:9b:93:fa
+-----BEGIN CERTIFICATE-----
+MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx
+CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ
+WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ
+BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG
+Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/
+yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf
+BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz
+WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF
+tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z
+374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC
+IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL
+mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7
+wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS
+MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2
+ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet
+UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw
+AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H
+YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3
+LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD
+nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1
+RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM
+LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf
+77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N
+JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm
+fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp
+6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp
+1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B
+9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok
+RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv
+uu8wd+RU4riEmViAqhOLUTpPSPaLtrM=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Amazon Root CA 1 O=Amazon
+# Subject: CN=Amazon Root CA 1 O=Amazon
+# Label: "Amazon Root CA 1"
+# Serial: 143266978916655856878034712317230054538369994
+# MD5 Fingerprint: 43:c6:bf:ae:ec:fe:ad:2f:18:c6:88:68:30:fc:c8:e6
+# SHA1 Fingerprint: 8d:a7:f9:65:ec:5e:fc:37:91:0f:1c:6e:59:fd:c1:cc:6a:6e:de:16
+# SHA256 Fingerprint: 8e:cd:e6:88:4f:3d:87:b1:12:5b:a3:1a:c3:fc:b1:3d:70:16:de:7f:57:cc:90:4f:e1:cb:97:c6:ae:98:19:6e
+-----BEGIN CERTIFICATE-----
+MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
+ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
+b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
+MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
+b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
+ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
+9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
+IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
+VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
+93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
+jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
+A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
+U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
+N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
+o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
+5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
+rqXRfboQnoZsG4q5WTP468SQvvG5
+-----END CERTIFICATE-----
+
+# Issuer: CN=Amazon Root CA 2 O=Amazon
+# Subject: CN=Amazon Root CA 2 O=Amazon
+# Label: "Amazon Root CA 2"
+# Serial: 143266982885963551818349160658925006970653239
+# MD5 Fingerprint: c8:e5:8d:ce:a8:42:e2:7a:c0:2a:5c:7c:9e:26:bf:66
+# SHA1 Fingerprint: 5a:8c:ef:45:d7:a6:98:59:76:7a:8c:8b:44:96:b5:78:cf:47:4b:1a
+# SHA256 Fingerprint: 1b:a5:b2:aa:8c:65:40:1a:82:96:01:18:f8:0b:ec:4f:62:30:4d:83:ce:c4:71:3a:19:c3:9c:01:1e:a4:6d:b4
+-----BEGIN CERTIFICATE-----
+MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF
+ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
+b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL
+MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
+b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK
+gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ
+W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg
+1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K
+8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r
+2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me
+z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR
+8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj
+mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz
+7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6
++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI
+0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB
+Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm
+UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2
+LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY
++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS
+k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl
+7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm
+btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl
+urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+
+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63
+n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE
+76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H
+9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT
+4PsJYGw=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Amazon Root CA 3 O=Amazon
+# Subject: CN=Amazon Root CA 3 O=Amazon
+# Label: "Amazon Root CA 3"
+# Serial: 143266986699090766294700635381230934788665930
+# MD5 Fingerprint: a0:d4:ef:0b:f7:b5:d8:49:95:2a:ec:f5:c4:fc:81:87
+# SHA1 Fingerprint: 0d:44:dd:8c:3c:8c:1a:1a:58:75:64:81:e9:0f:2e:2a:ff:b3:d2:6e
+# SHA256 Fingerprint: 18:ce:6c:fe:7b:f1:4e:60:b2:e3:47:b8:df:e8:68:cb:31:d0:2e:bb:3a:da:27:15:69:f5:03:43:b4:6d:b3:a4
+-----BEGIN CERTIFICATE-----
+MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5
+MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
+Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
+A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
+Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl
+ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j
+QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr
+ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr
+BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM
+YyRIHN8wfdVoOw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Amazon Root CA 4 O=Amazon
+# Subject: CN=Amazon Root CA 4 O=Amazon
+# Label: "Amazon Root CA 4"
+# Serial: 143266989758080763974105200630763877849284878
+# MD5 Fingerprint: 89:bc:27:d5:eb:17:8d:06:6a:69:d5:fd:89:47:b4:cd
+# SHA1 Fingerprint: f6:10:84:07:d6:f8:bb:67:98:0c:c2:e2:44:c2:eb:ae:1c:ef:63:be
+# SHA256 Fingerprint: e3:5d:28:41:9e:d0:20:25:cf:a6:90:38:cd:62:39:62:45:8d:a5:c6:95:fb:de:a3:c2:2b:0b:fb:25:89:70:92
+-----BEGIN CERTIFICATE-----
+MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5
+MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
+Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
+A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
+Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi
+9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk
+M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB
+/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB
+MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw
+CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW
+1KyLa2tJElMzrdfkviT8tQp21KW8EA==
+-----END CERTIFICATE-----
+
+# Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM
+# Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM
+# Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1"
+# Serial: 1
+# MD5 Fingerprint: dc:00:81:dc:69:2f:3e:2f:b0:3b:f6:3d:5a:91:8e:49
+# SHA1 Fingerprint: 31:43:64:9b:ec:ce:27:ec:ed:3a:3f:0b:8f:0d:e4:e8:91:dd:ee:ca
+# SHA256 Fingerprint: 46:ed:c3:68:90:46:d5:3a:45:3f:b3:10:4a:b8:0d:ca:ec:65:8b:26:60:ea:16:29:dd:7e:86:79:90:64:87:16
+-----BEGIN CERTIFICATE-----
+MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx
+GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp
+bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w
+KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0
+BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy
+dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG
+EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll
+IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU
+QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT
+TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg
+LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7
+a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr
+LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr
+N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X
+YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/
+iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f
+AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH
+V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
+BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh
+AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf
+IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4
+lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c
+8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf
+lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM=
+-----END CERTIFICATE-----
+
+# Issuer: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD.
+# Subject: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD.
+# Label: "GDCA TrustAUTH R5 ROOT"
+# Serial: 9009899650740120186
+# MD5 Fingerprint: 63:cc:d9:3d:34:35:5c:6f:53:a3:e2:08:70:48:1f:b4
+# SHA1 Fingerprint: 0f:36:38:5b:81:1a:25:c3:9b:31:4e:83:ca:e9:34:66:70:cc:74:b4
+# SHA256 Fingerprint: bf:ff:8f:d0:44:33:48:7d:6a:8a:a6:0c:1a:29:76:7a:9f:c2:bb:b0:5e:42:0f:71:3a:13:b9:92:89:1d:38:93
+-----BEGIN CERTIFICATE-----
+MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE
+BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ
+IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0
+MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV
+BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w
+HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj
+Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj
+TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u
+KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj
+qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm
+MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12
+ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP
+zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk
+L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC
+jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA
+HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC
+AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB
+/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg
+p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm
+DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5
+COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry
+L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf
+JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg
+IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io
+2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV
+09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ
+XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq
+T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe
+MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g==
+-----END CERTIFICATE-----
+
+# Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation
+# Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation
+# Label: "SSL.com Root Certification Authority RSA"
+# Serial: 8875640296558310041
+# MD5 Fingerprint: 86:69:12:c0:70:f1:ec:ac:ac:c2:d5:bc:a5:5b:a1:29
+# SHA1 Fingerprint: b7:ab:33:08:d1:ea:44:77:ba:14:80:12:5a:6f:bd:a9:36:49:0c:bb
+# SHA256 Fingerprint: 85:66:6a:56:2e:e0:be:5c:e9:25:c1:d8:89:0a:6f:76:a8:7e:c1:6d:4d:7d:5f:29:ea:74:19:cf:20:12:3b:69
+-----BEGIN CERTIFICATE-----
+MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE
+BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK
+DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp
+Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz
+OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv
+dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv
+bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN
+AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R
+xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX
+qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC
+C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3
+6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh
+/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF
+YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E
+JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc
+US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8
+ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm
++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi
+M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV
+HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G
+A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV
+cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc
+Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs
+PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/
+q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0
+cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr
+a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I
+H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y
+K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu
+nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf
+oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY
+Ic2wBlX7Jz9TkHCpBB5XJ7k=
+-----END CERTIFICATE-----
+
+# Issuer: CN=SSL.com Root Certification Authority ECC O=SSL Corporation
+# Subject: CN=SSL.com Root Certification Authority ECC O=SSL Corporation
+# Label: "SSL.com Root Certification Authority ECC"
+# Serial: 8495723813297216424
+# MD5 Fingerprint: 2e:da:e4:39:7f:9c:8f:37:d1:70:9f:26:17:51:3a:8e
+# SHA1 Fingerprint: c3:19:7c:39:24:e6:54:af:1b:c4:ab:20:95:7a:e2:c3:0e:13:02:6a
+# SHA256 Fingerprint: 34:17:bb:06:cc:60:07:da:1b:96:1c:92:0b:8a:b4:ce:3f:ad:82:0e:4a:a3:0b:9a:cb:c4:a7:4e:bd:ce:bc:65
+-----BEGIN CERTIFICATE-----
+MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC
+VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T
+U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0
+aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz
+WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0
+b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS
+b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB
+BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI
+7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg
+CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud
+EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD
+VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T
+kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+
+gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl
+-----END CERTIFICATE-----
+
+# Issuer: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation
+# Subject: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation
+# Label: "SSL.com EV Root Certification Authority RSA R2"
+# Serial: 6248227494352943350
+# MD5 Fingerprint: e1:1e:31:58:1a:ae:54:53:02:f6:17:6a:11:7b:4d:95
+# SHA1 Fingerprint: 74:3a:f0:52:9b:d0:32:a0:f4:4a:83:cd:d4:ba:a9:7b:7c:2e:c4:9a
+# SHA256 Fingerprint: 2e:7b:f1:6c:c2:24:85:a7:bb:e2:aa:86:96:75:07:61:b0:ae:39:be:3b:2f:e9:d0:cc:6d:4e:f7:34:91:42:5c
+-----BEGIN CERTIFICATE-----
+MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV
+BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE
+CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy
+MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G
+A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD
+DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq
+M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf
+OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa
+4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9
+HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR
+aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA
+b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ
+Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV
+PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO
+pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu
+UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY
+MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV
+HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4
+9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW
+s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5
+Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg
+cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM
+79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz
+/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt
+ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm
+Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK
+QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ
+w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi
+S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07
+mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w==
+-----END CERTIFICATE-----
+
+# Issuer: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation
+# Subject: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation
+# Label: "SSL.com EV Root Certification Authority ECC"
+# Serial: 3182246526754555285
+# MD5 Fingerprint: 59:53:22:65:83:42:01:54:c0:ce:42:b9:5a:7c:f2:90
+# SHA1 Fingerprint: 4c:dd:51:a3:d1:f5:20:32:14:b0:c6:c5:32:23:03:91:c7:46:42:6d
+# SHA256 Fingerprint: 22:a2:c1:f7:bd:ed:70:4c:c1:e7:01:b5:f4:08:c3:10:88:0f:e9:56:b5:de:2a:4a:44:f9:9c:87:3a:25:a7:c8
+-----BEGIN CERTIFICATE-----
+MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC
+VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T
+U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp
+Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx
+NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv
+dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv
+bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49
+AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA
+VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku
+WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP
+MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX
+5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ
+ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg
+h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg==
+-----END CERTIFICATE-----
+
+# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6
+# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6
+# Label: "GlobalSign Root CA - R6"
+# Serial: 1417766617973444989252670301619537
+# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae
+# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1
+# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69
+-----BEGIN CERTIFICATE-----
+MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg
+MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh
+bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx
+MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET
+MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI
+xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k
+ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD
+aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw
+LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw
+1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX
+k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2
+SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h
+bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n
+WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY
+rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce
+MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD
+AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu
+bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN
+nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt
+Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61
+55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj
+vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf
+cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz
+oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp
+nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs
+pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v
+JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R
+8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4
+5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA=
+-----END CERTIFICATE-----
+
+# Issuer: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed
+# Subject: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed
+# Label: "OISTE WISeKey Global Root GC CA"
+# Serial: 44084345621038548146064804565436152554
+# MD5 Fingerprint: a9:d6:b9:2d:2f:93:64:f8:a5:69:ca:91:e9:68:07:23
+# SHA1 Fingerprint: e0:11:84:5e:34:de:be:88:81:b9:9c:f6:16:26:d1:96:1f:c3:b9:31
+# SHA256 Fingerprint: 85:60:f9:1c:36:24:da:ba:95:70:b5:fe:a0:db:e3:6f:f1:1a:83:23:be:94:86:85:4f:b3:f3:4a:55:71:19:8d
+-----BEGIN CERTIFICATE-----
+MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw
+CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91
+bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg
+Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ
+BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu
+ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS
+b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni
+eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W
+p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T
+rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV
+57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg
+Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9
+-----END CERTIFICATE-----
+
+# Issuer: CN=UCA Global G2 Root O=UniTrust
+# Subject: CN=UCA Global G2 Root O=UniTrust
+# Label: "UCA Global G2 Root"
+# Serial: 124779693093741543919145257850076631279
+# MD5 Fingerprint: 80:fe:f0:c4:4a:f0:5c:62:32:9f:1c:ba:78:a9:50:f8
+# SHA1 Fingerprint: 28:f9:78:16:19:7a:ff:18:25:18:aa:44:fe:c1:a0:ce:5c:b6:4c:8a
+# SHA256 Fingerprint: 9b:ea:11:c9:76:fe:01:47:64:c1:be:56:a6:f9:14:b5:a5:60:31:7a:bd:99:88:39:33:82:e5:16:1a:a0:49:3c
+-----BEGIN CERTIFICATE-----
+MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9
+MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH
+bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x
+CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds
+b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr
+b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9
+kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm
+VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R
+VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc
+C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj
+tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY
+D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv
+j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl
+NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6
+iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP
+O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/
+BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV
+ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj
+L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5
+1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl
+1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU
+b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV
+PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj
+y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb
+EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg
+DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI
++Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy
+YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX
+UB+K+wb1whnw0A==
+-----END CERTIFICATE-----
+
+# Issuer: CN=UCA Extended Validation Root O=UniTrust
+# Subject: CN=UCA Extended Validation Root O=UniTrust
+# Label: "UCA Extended Validation Root"
+# Serial: 106100277556486529736699587978573607008
+# MD5 Fingerprint: a1:f3:5f:43:c6:34:9b:da:bf:8c:7e:05:53:ad:96:e2
+# SHA1 Fingerprint: a3:a1:b0:6f:24:61:23:4a:e3:36:a5:c2:37:fc:a6:ff:dd:f0:d7:3a
+# SHA256 Fingerprint: d4:3a:f9:b3:54:73:75:5c:96:84:fc:06:d7:d8:cb:70:ee:5c:28:e7:73:fb:29:4e:b4:1e:e7:17:22:92:4d:24
+-----BEGIN CERTIFICATE-----
+MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH
+MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF
+eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx
+MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV
+BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog
+D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS
+sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop
+O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk
+sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi
+c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj
+VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz
+KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/
+TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G
+sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs
+1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD
+fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T
+AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN
+l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR
+ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ
+VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5
+c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp
+4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s
+t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj
+2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO
+vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C
+xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx
+cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM
+fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax
+-----END CERTIFICATE-----
+
+# Issuer: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036
+# Subject: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036
+# Label: "Certigna Root CA"
+# Serial: 269714418870597844693661054334862075617
+# MD5 Fingerprint: 0e:5c:30:62:27:eb:5b:bc:d7:ae:62:ba:e9:d5:df:77
+# SHA1 Fingerprint: 2d:0d:52:14:ff:9e:ad:99:24:01:74:20:47:6e:6c:85:27:27:f5:43
+# SHA256 Fingerprint: d4:8d:3d:23:ee:db:50:a4:59:e5:51:97:60:1c:27:77:4b:9d:7b:18:c9:4d:5a:05:95:11:a1:02:50:b9:31:68
+-----BEGIN CERTIFICATE-----
+MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw
+WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw
+MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x
+MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD
+VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX
+BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO
+ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M
+CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu
+I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm
+TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh
+C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf
+ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz
+IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT
+Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k
+JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5
+hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB
+GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
+FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of
+1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov
+L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo
+dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr
+aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq
+hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L
+6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG
+HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6
+0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB
+lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi
+o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1
+gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v
+faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63
+Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh
+jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw
+3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0=
+-----END CERTIFICATE-----
+
+# Issuer: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI
+# Subject: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI
+# Label: "emSign Root CA - G1"
+# Serial: 235931866688319308814040
+# MD5 Fingerprint: 9c:42:84:57:dd:cb:0b:a7:2e:95:ad:b6:f3:da:bc:ac
+# SHA1 Fingerprint: 8a:c7:ad:8f:73:ac:4e:c1:b5:75:4d:a5:40:f4:fc:cf:7c:b5:8e:8c
+# SHA256 Fingerprint: 40:f6:af:03:46:a9:9a:a1:cd:1d:55:5a:4e:9c:ce:62:c7:f9:63:46:03:ee:40:66:15:83:3d:c8:c8:d0:03:67
+-----BEGIN CERTIFICATE-----
+MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD
+VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU
+ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH
+MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO
+MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv
+Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz
+f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO
+8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq
+d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM
+tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt
+Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB
+o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD
+AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x
+PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM
+wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d
+GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH
+6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby
+RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx
+iN66zB+Afko=
+-----END CERTIFICATE-----
+
+# Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI
+# Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI
+# Label: "emSign ECC Root CA - G3"
+# Serial: 287880440101571086945156
+# MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40
+# SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1
+# SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b
+-----BEGIN CERTIFICATE-----
+MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG
+EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo
+bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g
+RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ
+TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s
+b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw
+djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0
+WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS
+fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB
+zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq
+hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB
+CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD
++JbNR6iC8hZVdyR+EhCVBCyj
+-----END CERTIFICATE-----
+
+# Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI
+# Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI
+# Label: "emSign Root CA - C1"
+# Serial: 825510296613316004955058
+# MD5 Fingerprint: d8:e3:5d:01:21:fa:78:5a:b0:df:ba:d2:ee:2a:5f:68
+# SHA1 Fingerprint: e7:2e:f1:df:fc:b2:09:28:cf:5d:d4:d5:67:37:b1:51:cb:86:4f:01
+# SHA256 Fingerprint: 12:56:09:aa:30:1d:a0:a2:49:b9:7a:82:39:cb:6a:34:21:6f:44:dc:ac:9f:39:54:b1:42:92:f2:e8:c8:60:8f
+-----BEGIN CERTIFICATE-----
+MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG
+A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg
+SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw
+MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln
+biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v
+dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ
+BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ
+HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH
+3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH
+GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c
+xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1
+aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq
+TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
+BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87
+/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4
+kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG
+YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT
++xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo
+WXzhriKi4gp6D/piq1JM4fHfyr6DDUI=
+-----END CERTIFICATE-----
+
+# Issuer: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI
+# Subject: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI
+# Label: "emSign ECC Root CA - C3"
+# Serial: 582948710642506000014504
+# MD5 Fingerprint: 3e:53:b3:a3:81:ee:d7:10:f8:d3:b0:1d:17:92:f5:d5
+# SHA1 Fingerprint: b6:af:43:c2:9b:81:53:7d:f6:ef:6b:c3:1f:1f:60:15:0c:ee:48:66
+# SHA256 Fingerprint: bc:4d:80:9b:15:18:9d:78:db:3e:1d:8c:f4:f9:72:6a:79:5d:a1:64:3c:a5:f1:35:8e:1d:db:0e:dc:0d:7e:b3
+-----BEGIN CERTIFICATE-----
+MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG
+EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx
+IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw
+MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln
+biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND
+IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci
+MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti
+sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O
+BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB
+Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c
+3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J
+0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Hongkong Post Root CA 3 O=Hongkong Post
+# Subject: CN=Hongkong Post Root CA 3 O=Hongkong Post
+# Label: "Hongkong Post Root CA 3"
+# Serial: 46170865288971385588281144162979347873371282084
+# MD5 Fingerprint: 11:fc:9f:bd:73:30:02:8a:fd:3f:f3:58:b9:cb:20:f0
+# SHA1 Fingerprint: 58:a2:d0:ec:20:52:81:5b:c1:f3:f8:64:02:24:4e:c2:8e:02:4b:02
+# SHA256 Fingerprint: 5a:2f:c0:3f:0c:83:b0:90:bb:fa:40:60:4b:09:88:44:6c:76:36:18:3d:f9:84:6e:17:10:1a:44:7f:b8:ef:d6
+-----BEGIN CERTIFICATE-----
+MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL
+BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ
+SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n
+a29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5
+NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcT
+CUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9u
+Z2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
+AoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFO
+dem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI
+VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV
+9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY
+2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKY
+vLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+Tt
+bNe/JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZb
+x39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+
+l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YK
+TE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKvQv83j+Gj
+Hno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP
+BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e
+i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEw
+DQYJKoZIhvcNAQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG
+7BJ8dNVI0lkUmcDrudHr9EgwW62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCk
+MpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWldy8joRTnU+kLBEUx3XZL7av9YROXr
+gZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov+BS5gLNdTaqX4fnk
+GMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDceqFS
+3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJm
+Ozj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+
+l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6c
+JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP
+L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa
+LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG
+mpv0
+-----END CERTIFICATE-----
+
+# Issuer: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation
+# Subject: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation
+# Label: "Microsoft ECC Root Certificate Authority 2017"
+# Serial: 136839042543790627607696632466672567020
+# MD5 Fingerprint: dd:a1:03:e6:4a:93:10:d1:bf:f0:19:42:cb:fe:ed:67
+# SHA1 Fingerprint: 99:9a:64:c3:7f:f4:7d:9f:ab:95:f1:47:69:89:14:60:ee:c4:c3:c5
+# SHA256 Fingerprint: 35:8d:f3:9d:76:4a:f9:e1:b7:66:e9:c9:72:df:35:2e:e1:5c:fa:c2:27:af:6a:d1:d7:0e:8e:4a:6e:dc:ba:02
+-----BEGIN CERTIFICATE-----
+MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw
+CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD
+VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw
+MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV
+UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy
+b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq
+hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR
+ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb
+hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E
+BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3
+FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV
+L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB
+iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation
+# Subject: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation
+# Label: "Microsoft RSA Root Certificate Authority 2017"
+# Serial: 40975477897264996090493496164228220339
+# MD5 Fingerprint: 10:ff:00:ff:cf:c9:f8:c7:7a:c0:ee:35:8e:c9:0f:47
+# SHA1 Fingerprint: 73:a5:e6:4a:3b:ff:83:16:ff:0e:dc:cc:61:8a:90:6e:4e:ae:4d:74
+# SHA256 Fingerprint: c7:41:f7:0f:4b:2a:8d:88:bf:2e:71:c1:41:22:ef:53:ef:10:eb:a0:cf:a5:e6:4c:fa:20:f4:18:85:30:73:e0
+-----BEGIN CERTIFICATE-----
+MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl
+MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw
+NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
+IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG
+EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N
+aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ
+Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0
+ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1
+HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm
+gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ
+jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc
+aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG
+YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6
+W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K
+UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH
++FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q
+W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/
+BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC
+NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC
+LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC
+gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6
+tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh
+SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2
+TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3
+pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR
+xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp
+GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9
+dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN
+AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB
+RA+GsCyRxj3qrg+E
+-----END CERTIFICATE-----
+
+# Issuer: CN=e-Szigno Root CA 2017 O=Microsec Ltd.
+# Subject: CN=e-Szigno Root CA 2017 O=Microsec Ltd.
+# Label: "e-Szigno Root CA 2017"
+# Serial: 411379200276854331539784714
+# MD5 Fingerprint: de:1f:f6:9e:84:ae:a7:b4:21:ce:1e:58:7d:d1:84:98
+# SHA1 Fingerprint: 89:d4:83:03:4f:9e:9a:48:80:5f:72:37:d4:a9:a6:ef:cb:7c:1f:d1
+# SHA256 Fingerprint: be:b0:0b:30:83:9b:9b:c3:2c:32:e4:44:79:05:95:06:41:f2:64:21:b1:5e:d0:89:19:8b:51:8a:e2:ea:1b:99
+-----BEGIN CERTIFICATE-----
+MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV
+BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk
+LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv
+b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ
+BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg
+THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v
+IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv
+xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H
+Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
+A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB
+eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo
+jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ
++efcMQ==
+-----END CERTIFICATE-----
+
+# Issuer: O=CERTSIGN SA OU=certSIGN ROOT CA G2
+# Subject: O=CERTSIGN SA OU=certSIGN ROOT CA G2
+# Label: "certSIGN Root CA G2"
+# Serial: 313609486401300475190
+# MD5 Fingerprint: 8c:f1:75:8a:c6:19:cf:94:b7:f7:65:20:87:c3:97:c7
+# SHA1 Fingerprint: 26:f9:93:b4:ed:3d:28:27:b0:b9:4b:a7:e9:15:1d:a3:8d:92:e5:32
+# SHA256 Fingerprint: 65:7c:fe:2f:a7:3f:aa:38:46:25:71:f3:32:a2:36:3a:46:fc:e7:02:09:51:71:07:02:cd:fb:b6:ee:da:33:05
+-----BEGIN CERTIFICATE-----
+MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g
+Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ
+BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ
+R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF
+dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw
+vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ
+uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp
+n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs
+cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW
+xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P
+rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF
+DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx
+DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy
+LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C
+eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB
+/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ
+d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq
+kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC
+b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl
+qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0
+OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c
+NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk
+ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO
+pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj
+03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk
+PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE
+1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX
+QRBdJ3NghVdJIgc=
+-----END CERTIFICATE-----
+
+# Issuer: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp.
+# Subject: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp.
+# Label: "NAVER Global Root Certification Authority"
+# Serial: 9013692873798656336226253319739695165984492813
+# MD5 Fingerprint: c8:7e:41:f6:25:3b:f5:09:b3:17:e8:46:3d:bf:d0:9b
+# SHA1 Fingerprint: 8f:6b:f2:a9:27:4a:da:14:a0:c4:f4:8e:61:27:f9:c0:1e:78:5d:d1
+# SHA256 Fingerprint: 88:f4:38:dc:f8:ff:d1:fa:8f:42:91:15:ff:e5:f8:2a:e1:e0:6e:0c:70:c3:75:fa:ad:71:7b:34:a4:9e:72:65
+-----BEGIN CERTIFICATE-----
+MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM
+BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG
+T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0
+aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkx
+CzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVTUyBQTEFURk9STSBD
+b3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVA
+iQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH
+38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE
+HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZaz
+kVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYP
+szuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8Obtoq
+vC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHf
+nZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaG
+YQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo
+0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3a
+CJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzClrkvJE+2K
+AQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I
+36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB
+Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoN
+qo0hV4/GPnrK21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatj
+cu3cvuzHV+YwIHHW1xDBE1UBjCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm
++LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bxhYTeodoS76TiEJd6eN4MUZeoIUCL
+hr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTgE34h5prCy8VCZLQe
+lHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTHD8z7
+p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8
+piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR
+LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX
+5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO
+dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul
+9XXeifdy
+-----END CERTIFICATE-----
+
+# Issuer: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres
+# Subject: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres
+# Label: "AC RAIZ FNMT-RCM SERVIDORES SEGUROS"
+# Serial: 131542671362353147877283741781055151509
+# MD5 Fingerprint: 19:36:9c:52:03:2f:d2:d1:bb:23:cc:dd:1e:12:55:bb
+# SHA1 Fingerprint: 62:ff:d9:9e:c0:65:0d:03:ce:75:93:d2:ed:3f:2d:32:c9:e3:e5:4a
+# SHA256 Fingerprint: 55:41:53:b1:3d:2c:f9:dd:b7:53:bf:be:1a:4e:0a:e0:8d:0a:a4:18:70:58:fe:60:a2:b8:62:b2:e4:b8:7b:cb
+-----BEGIN CERTIFICATE-----
+MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw
+CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw
+FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S
+Q00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5
+MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQL
+DAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNBQyBS
+QUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuB
+BAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LH
+sbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK
+Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu
+SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC
+MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy
+v+c=
+-----END CERTIFICATE-----
+
+# Issuer: CN=GlobalSign Root R46 O=GlobalSign nv-sa
+# Subject: CN=GlobalSign Root R46 O=GlobalSign nv-sa
+# Label: "GlobalSign Root R46"
+# Serial: 1552617688466950547958867513931858518042577
+# MD5 Fingerprint: c4:14:30:e4:fa:66:43:94:2a:6a:1b:24:5f:19:d0:ef
+# SHA1 Fingerprint: 53:a2:b0:4b:ca:6b:d6:45:e6:39:8a:8e:c4:0d:d2:bf:77:c3:a2:90
+# SHA256 Fingerprint: 4f:a3:12:6d:8d:3a:11:d1:c4:85:5a:4f:80:7c:ba:d6:cf:91:9d:3a:5a:88:b0:3b:ea:2c:63:72:d9:3c:40:c9
+-----BEGIN CERTIFICATE-----
+MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA
+MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD
+VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy
+MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt
+c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ
+OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG
+vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud
+316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo
+0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE
+y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF
+zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE
++cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN
+I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs
+x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa
+ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC
+4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
+HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4
+7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg
+JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti
+2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk
+pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF
+FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt
+rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk
+ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5
+u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP
+4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6
+N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3
+vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6
+-----END CERTIFICATE-----
+
+# Issuer: CN=GlobalSign Root E46 O=GlobalSign nv-sa
+# Subject: CN=GlobalSign Root E46 O=GlobalSign nv-sa
+# Label: "GlobalSign Root E46"
+# Serial: 1552617690338932563915843282459653771421763
+# MD5 Fingerprint: b5:b8:66:ed:de:08:83:e3:c9:e2:01:34:06:ac:51:6f
+# SHA1 Fingerprint: 39:b4:6c:d5:fe:80:06:eb:e2:2f:4a:bb:08:33:a0:af:db:b9:dd:84
+# SHA256 Fingerprint: cb:b9:c4:4d:84:b8:04:3e:10:50:ea:31:a6:9f:51:49:55:d7:bf:d2:e2:c6:b4:93:01:01:9a:d6:1d:9f:50:58
+-----BEGIN CERTIFICATE-----
+MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx
+CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD
+ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw
+MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex
+HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq
+R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd
+yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
+DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ
+7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8
++RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A=
+-----END CERTIFICATE-----
+
+# Issuer: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz
+# Subject: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz
+# Label: "ANF Secure Server Root CA"
+# Serial: 996390341000653745
+# MD5 Fingerprint: 26:a6:44:5a:d9:af:4e:2f:b2:1d:b6:65:b0:4e:e8:96
+# SHA1 Fingerprint: 5b:6e:68:d0:cc:15:b6:a0:5f:1e:c1:5f:ae:02:fc:6b:2f:5d:6f:74
+# SHA256 Fingerprint: fb:8f:ec:75:91:69:b9:10:6b:1e:51:16:44:c6:18:c5:13:04:37:3f:6c:06:43:08:8d:8b:ef:fd:1b:99:75:99
+-----BEGIN CERTIFICATE-----
+MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV
+BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk
+YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV
+BAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3QgQ0EwHhcNMTkwOTA0MTAwMDM4WhcN
+MzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEwMQswCQYDVQQGEwJF
+UzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQwEgYD
+VQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9v
+dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCj
+cqQZAZ2cC4Ffc0m6p6zzBE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9q
+yGFOtibBTI3/TO80sh9l2Ll49a2pcbnvT1gdpd50IJeh7WhM3pIXS7yr/2WanvtH
+2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcvB2VSAKduyK9o7PQUlrZX
+H1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXsezx76W0OL
+zc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyR
+p1RMVwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQz
+W7i1o0TJrH93PB0j7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/
+SiOL9V8BY9KHcyi1Swr1+KuCLH5zJTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJn
+LNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe8TZBAQIvfXOn3kLMTOmJDVb3
+n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVOHj1tyRRM4y5B
+u8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj
+o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAO
+BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
+AgEATh65isagmD9uw2nAalxJUqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L
+9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzxj6ptBZNscsdW699QIyjlRRA96Gej
+rw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDtdD+4E5UGUcjohybK
+pFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM5gf0
+vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjq
+OknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ
+/zo1PqVUSlJZS2Db7v54EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ9
+2zg/LFis6ELhDtjTO0wugumDLmsx2d1Hhk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI
++PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2
+MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoo
+tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority
+# Subject: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority
+# Label: "Certum EC-384 CA"
+# Serial: 160250656287871593594747141429395092468
+# MD5 Fingerprint: b6:65:b3:96:60:97:12:a1:ec:4e:e1:3d:a3:c6:c9:f1
+# SHA1 Fingerprint: f3:3e:78:3c:ac:df:f4:a2:cc:ac:67:55:69:56:d7:e5:16:3c:e1:ed
+# SHA256 Fingerprint: 6b:32:80:85:62:53:18:aa:50:d1:73:c9:8d:8b:da:09:d5:7e:27:41:3d:11:4c:f7:87:a0:f5:d0:6c:03:0c:f6
+-----BEGIN CERTIFICATE-----
+MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw
+CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw
+JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT
+EENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0
+WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBT
+LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAX
+BgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATE
+KI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7Tm
+Fy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj
+QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8
+EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J
+UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn
+nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority
+# Subject: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority
+# Label: "Certum Trusted Root CA"
+# Serial: 40870380103424195783807378461123655149
+# MD5 Fingerprint: 51:e1:c2:e7:fe:4c:84:af:59:0e:2f:f4:54:6f:ea:29
+# SHA1 Fingerprint: c8:83:44:c0:18:ae:9f:cc:f1:87:b7:8f:22:d1:c5:d7:45:84:ba:e5
+# SHA256 Fingerprint: fe:76:96:57:38:55:77:3e:37:a9:5e:7a:d4:d9:cc:96:c3:01:57:c1:5d:31:76:5b:a9:b1:57:04:e1:ae:78:fd
+-----BEGIN CERTIFICATE-----
+MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6
+MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu
+MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV
+BAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMw
+MzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEg
+U3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRo
+b3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZ
+n0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q
+p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQq
+NwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF
+8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3
+HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGa
+mqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi
+7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSF
+ytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0P
+qafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khUHL4wvYBQ
+v3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6
+Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1
+vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQAD
+ggIBAEii1QALLtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4
+WxmB82M+w85bj/UvXgF2Ez8sALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvo
+zMrnadyHncI013nR03e4qllY/p0m+jiGPp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR
+5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8CYyqOhNf6DR5UMEQ
+GfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA4kZf
+5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq
+0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D
+P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTM
+qJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP
+0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZf
+E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb
+-----END CERTIFICATE-----
+
+# Issuer: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique
+# Subject: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique
+# Label: "TunTrust Root CA"
+# Serial: 108534058042236574382096126452369648152337120275
+# MD5 Fingerprint: 85:13:b9:90:5b:36:5c:b6:5e:b8:5a:f8:e0:31:57:b4
+# SHA1 Fingerprint: cf:e9:70:84:0f:e0:73:0f:9d:f6:0c:7f:2c:4b:ee:20:46:34:9c:bb
+# SHA256 Fingerprint: 2e:44:10:2a:b5:8c:b8:54:19:45:1c:8e:19:d9:ac:f3:66:2c:af:bc:61:4b:6a:53:96:0a:30:f7:d0:e2:eb:41
+-----BEGIN CERTIFICATE-----
+MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL
+BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg
+Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv
+b3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQG
+EwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9u
+IEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZ
+n56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd
+2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF
+VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZ
+GoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOF
+li/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnU
+r8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2
+eY8fTpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIb
+MlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISg
+jwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB
+7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdOvsC9qnyW
+5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE
+ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0
+90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+z
+xiD2BkewhpMl0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYu
+QEkHDVneixCwSQXi/5E/S7fdAo74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4
+FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH
+22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJpadbGNjHh/PqAulxP
+xOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65xxBzn
+dFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5
+Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b
+nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQ
+CvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZH
+u/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujj
+d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o=
+-----END CERTIFICATE-----
+
+# Issuer: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA
+# Subject: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA
+# Label: "HARICA TLS RSA Root CA 2021"
+# Serial: 76817823531813593706434026085292783742
+# MD5 Fingerprint: 65:47:9b:58:86:dd:2c:f0:fc:a2:84:1f:1e:96:c4:91
+# SHA1 Fingerprint: 02:2d:05:82:fa:88:ce:14:0c:06:79:de:7f:14:10:e9:45:d7:a5:6d
+# SHA256 Fingerprint: d9:5d:0e:8e:da:79:52:5b:f9:be:b1:1b:14:d2:10:0d:32:94:98:5f:0c:62:d9:fa:bd:9c:d9:99:ec:cb:7b:1d
+-----BEGIN CERTIFICATE-----
+MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs
+MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
+c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg
+Um9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUzOFoXDTQ1MDIxMzEwNTUzN1owbDEL
+MAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl
+YXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNBIFJv
+b3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569l
+mwVnlskNJLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE
+4VGC/6zStGndLuwRo0Xua2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uv
+a9of08WRiFukiZLRgeaMOVig1mlDqa2YUlhu2wr7a89o+uOkXjpFc5gH6l8Cct4M
+pbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K5FrZx40d/JiZ+yykgmvw
+Kh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEvdmn8kN3b
+LW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcY
+AuUR0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqB
+AGMUuTNe3QvboEUHGjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYq
+E613TBoYm5EPWNgGVMWX+Ko/IIqmhaZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHr
+W2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQCPxrvrNQKlr9qEgYRtaQQJKQ
+CoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8GA1UdEwEB/wQF
+MAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE
+AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAU
+X15QvWiWkKQUEapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3
+f5Z2EMVGpdAgS1D0NTsY9FVqQRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxaja
+H6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxDQpSbIPDRzbLrLFPCU3hKTwSUQZqP
+JzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcRj88YxeMn/ibvBZ3P
+zzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5vZSt
+jBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0
+/L5H9MG0qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pT
+BGIBnfHAT+7hOtSLIBD6Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79
+aPib8qXPMThcFarmlwDB31qlpzmq6YR/PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YW
+xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU
+63ZTGI0RmLo=
+-----END CERTIFICATE-----
+
+# Issuer: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA
+# Subject: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA
+# Label: "HARICA TLS ECC Root CA 2021"
+# Serial: 137515985548005187474074462014555733966
+# MD5 Fingerprint: ae:f7:4c:e5:66:35:d1:b7:9b:8c:22:93:74:d3:4b:b0
+# SHA1 Fingerprint: bc:b0:c1:9d:e9:98:92:70:19:38:57:e9:8d:a7:b4:5d:6e:ee:01:48
+# SHA256 Fingerprint: 3f:99:cc:47:4a:cf:ce:4d:fe:d5:87:94:66:5e:47:8d:15:47:73:9f:2e:78:0f:1b:b4:ca:9b:13:30:97:d4:01
+-----BEGIN CERTIFICATE-----
+MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw
+CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh
+cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v
+dCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkG
+A1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj
+aCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJvb3Qg
+Q0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7
+KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9Y
+STHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQD
+AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw
+SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN
+nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps
+-----END CERTIFICATE-----
+
+# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
+# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
+# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068"
+# Serial: 1977337328857672817
+# MD5 Fingerprint: 4e:6e:9b:54:4c:ca:b7:fa:48:e4:90:b1:15:4b:1c:a3
+# SHA1 Fingerprint: 0b:be:c2:27:22:49:cb:39:aa:db:35:5c:53:e3:8c:ae:78:ff:b6:fe
+# SHA256 Fingerprint: 57:de:05:83:ef:d2:b2:6e:03:61:da:99:da:9d:f4:64:8d:ef:7e:e8:44:1c:3b:72:8a:fa:9b:cd:e0:f9:b2:6a
+-----BEGIN CERTIFICATE-----
+MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE
+BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h
+cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1
+MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg
+Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9
+thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM
+cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG
+L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i
+NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h
+X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b
+m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy
+Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja
+EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T
+KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF
+6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh
+OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc
+tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd
+IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j
+b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC
+AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw
+ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m
+iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF
+Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ
+hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P
+Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE
+EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV
+1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t
+CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR
+5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw
+f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9
+ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK
+GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV
+-----END CERTIFICATE-----
+
+# Issuer: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd.
+# Subject: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd.
+# Label: "vTrus ECC Root CA"
+# Serial: 630369271402956006249506845124680065938238527194
+# MD5 Fingerprint: de:4b:c1:f5:52:8c:9b:43:e1:3e:8f:55:54:17:8d:85
+# SHA1 Fingerprint: f6:9c:db:b0:fc:f6:02:13:b6:52:32:a6:a3:91:3f:16:70:da:c3:e1
+# SHA256 Fingerprint: 30:fb:ba:2c:32:23:8e:2a:98:54:7a:f9:79:31:e5:50:42:8b:9b:3f:1c:8e:eb:66:33:dc:fa:86:c5:b2:7d:d3
+-----BEGIN CERTIFICATE-----
+MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMw
+RzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAY
+BgNVBAMTEXZUcnVzIEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDcz
+MTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28u
+LEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYwEAYHKoZIzj0CAQYF
+K4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+cToL0
+v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUd
+e4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYD
+VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw
+V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sA
+AjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEG
+GJTO
+-----END CERTIFICATE-----
+
+# Issuer: CN=vTrus Root CA O=iTrusChina Co.,Ltd.
+# Subject: CN=vTrus Root CA O=iTrusChina Co.,Ltd.
+# Label: "vTrus Root CA"
+# Serial: 387574501246983434957692974888460947164905180485
+# MD5 Fingerprint: b8:c9:37:df:fa:6b:31:84:64:c5:ea:11:6a:1b:75:fc
+# SHA1 Fingerprint: 84:1a:69:fb:f5:cd:1a:25:34:13:3d:e3:f8:fc:b8:99:d0:c9:14:b7
+# SHA256 Fingerprint: 8a:71:de:65:59:33:6f:42:6c:26:e5:38:80:d0:0d:88:a1:8d:a4:c6:a9:1f:0d:cb:61:94:e2:06:c5:c9:63:87
+-----BEGIN CERTIFICATE-----
+MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQEL
+BQAwQzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4x
+FjAUBgNVBAMTDXZUcnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMx
+MDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4s
+THRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZotsSKYc
+IrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykU
+AyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+
+GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9
+8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdH
+flqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajt
+J8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim
+0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZN
+pGvu/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQ
+UqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHW
+OXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMB
+AAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet
+8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd
+nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1j
+bhd47F18iMjrjld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvM
+Kar5CKXiNxTKsbhm7xqC5PD48acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIiv
+TDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJnxDHO2zTlJQNgJXtxmOTAGytfdELS
+S8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554WgicEFOwE30z9J4nfr
+I8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4sEb9
+b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNB
+UvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P
+Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929ven
+sBxXVsFy6K2ir40zSbofitzmdHxghm+Hl3s=
+-----END CERTIFICATE-----
+
+# Issuer: CN=ISRG Root X2 O=Internet Security Research Group
+# Subject: CN=ISRG Root X2 O=Internet Security Research Group
+# Label: "ISRG Root X2"
+# Serial: 87493402998870891108772069816698636114
+# MD5 Fingerprint: d3:9e:c4:1e:23:3c:a6:df:cf:a3:7e:6d:e0:14:e6:e5
+# SHA1 Fingerprint: bd:b1:b9:3c:d5:97:8d:45:c6:26:14:55:f8:db:95:c7:5a:d1:53:af
+# SHA256 Fingerprint: 69:72:9b:8e:15:a8:6e:fc:17:7a:57:af:b7:17:1d:fc:64:ad:d2:8c:2f:ca:8c:f1:50:7e:34:45:3c:cb:14:70
+-----BEGIN CERTIFICATE-----
+MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
+CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
+R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
+MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT
+ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw
+EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW
++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9
+ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
+AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI
+zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
+tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
+/q4AaOeMSQ+2b1tbFfLn
+-----END CERTIFICATE-----
+
+# Issuer: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd.
+# Subject: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd.
+# Label: "HiPKI Root CA - G1"
+# Serial: 60966262342023497858655262305426234976
+# MD5 Fingerprint: 69:45:df:16:65:4b:e8:68:9a:8f:76:5f:ff:80:9e:d3
+# SHA1 Fingerprint: 6a:92:e4:a8:ee:1b:ec:96:45:37:e3:29:57:49:cd:96:e3:e5:d2:60
+# SHA256 Fingerprint: f0:15:ce:3c:c2:39:bf:ef:06:4b:e9:f1:d2:c4:17:e1:a0:26:4a:0a:94:be:1f:0c:8d:12:18:64:eb:69:49:cc
+-----BEGIN CERTIFICATE-----
+MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP
+MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0
+ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa
+Fw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3
+YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0o9Qw
+qNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twv
+Vcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6
+lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnz
+Qs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRtU6M9/Aes1MU3guvklQgZ
+KILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfdhSi8MEyr48KxRURHH+CK
+FgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj1jOXTyFj
+HluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDr
+y+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ
+/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgM
+a/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6
+fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
+HQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQDAgGGMA0GCSqG
+SIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi
+7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqc
+SE5XCV0vrPSltJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6Fza
+ZsT0pPBWGTMpWmWSBUdGSquEwx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9Tc
+XzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07QJNBAsNB1CI69aO4I1258EHBGG3zg
+iLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv5wiZqAxeJoBF1Pho
+L5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+GpzjLrF
+Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr
+kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+
+vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU
+YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4
+# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4
+# Label: "GlobalSign ECC Root CA - R4"
+# Serial: 159662223612894884239637590694
+# MD5 Fingerprint: 26:29:f8:6d:e1:88:bf:a2:65:7f:aa:c4:cd:0f:7f:fc
+# SHA1 Fingerprint: 6b:a0:b0:98:e1:71:ef:5a:ad:fe:48:15:80:77:10:f4:bd:6f:0b:28
+# SHA256 Fingerprint: b0:85:d7:0b:96:4f:19:1a:73:e4:af:0d:54:ae:7a:0e:07:aa:fd:af:9b:71:dd:08:62:13:8a:b7:32:5a:24:a2
+-----BEGIN CERTIFICATE-----
+MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD
+VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh
+bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw
+MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g
+UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT
+BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx
+uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV
+HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/
++wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147
+bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm
+-----END CERTIFICATE-----
+
+# Issuer: CN=GTS Root R1 O=Google Trust Services LLC
+# Subject: CN=GTS Root R1 O=Google Trust Services LLC
+# Label: "GTS Root R1"
+# Serial: 159662320309726417404178440727
+# MD5 Fingerprint: 05:fe:d0:bf:71:a8:a3:76:63:da:01:e0:d8:52:dc:40
+# SHA1 Fingerprint: e5:8c:1c:c4:91:3b:38:63:4b:e9:10:6e:e3:ad:8e:6b:9d:d9:81:4a
+# SHA256 Fingerprint: d9:47:43:2a:bd:e7:b7:fa:90:fc:2e:6b:59:10:1b:12:80:e0:e1:c7:e4:e4:0f:a3:c6:88:7f:ff:57:a7:f4:cf
+-----BEGIN CERTIFICATE-----
+MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
+CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
+MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
+MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
+Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo
+27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w
+Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw
+TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl
+qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH
+szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8
+Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk
+MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92
+wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p
+aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN
+VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID
+AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb
+C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe
+QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy
+h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4
+7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J
+ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef
+MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/
+Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT
+6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ
+0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm
+2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb
+bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c
+-----END CERTIFICATE-----
+
+# Issuer: CN=GTS Root R3 O=Google Trust Services LLC
+# Subject: CN=GTS Root R3 O=Google Trust Services LLC
+# Label: "GTS Root R3"
+# Serial: 159662495401136852707857743206
+# MD5 Fingerprint: 3e:e7:9d:58:02:94:46:51:94:e5:e0:22:4a:8b:e7:73
+# SHA1 Fingerprint: ed:e5:71:80:2b:c8:92:b9:5b:83:3c:d2:32:68:3f:09:cd:a0:1e:46
+# SHA256 Fingerprint: 34:d8:a7:3e:e2:08:d9:bc:db:0d:95:65:20:93:4b:4e:40:e6:94:82:59:6e:8b:6f:73:c8:42:6b:01:0a:6f:48
+-----BEGIN CERTIFICATE-----
+MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD
+VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG
+A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw
+WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz
+IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
+AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G
+jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2
+4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
+BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7
+VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm
+ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X
+-----END CERTIFICATE-----
+
+# Issuer: CN=GTS Root R4 O=Google Trust Services LLC
+# Subject: CN=GTS Root R4 O=Google Trust Services LLC
+# Label: "GTS Root R4"
+# Serial: 159662532700760215368942768210
+# MD5 Fingerprint: 43:96:83:77:19:4d:76:b3:9d:65:52:e4:1d:22:a5:e8
+# SHA1 Fingerprint: 77:d3:03:67:b5:e0:0c:15:f6:0c:38:61:df:7c:e1:3b:92:46:4d:47
+# SHA256 Fingerprint: 34:9d:fa:40:58:c5:e2:63:12:3b:39:8a:e7:95:57:3c:4e:13:13:c8:3f:e6:8f:93:55:6c:d5:e8:03:1b:3c:7d
+-----BEGIN CERTIFICATE-----
+MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD
+VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG
+A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw
+WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz
+IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
+AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi
+QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR
+HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
+BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D
+9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8
+p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD
+-----END CERTIFICATE-----
+
+# Issuer: CN=Telia Root CA v2 O=Telia Finland Oyj
+# Subject: CN=Telia Root CA v2 O=Telia Finland Oyj
+# Label: "Telia Root CA v2"
+# Serial: 7288924052977061235122729490515358
+# MD5 Fingerprint: 0e:8f:ac:aa:82:df:85:b1:f4:dc:10:1c:fc:99:d9:48
+# SHA1 Fingerprint: b9:99:cd:d1:73:50:8a:c4:47:05:08:9c:8c:88:fb:be:a0:2b:40:cd
+# SHA256 Fingerprint: 24:2b:69:74:2f:cb:1e:5b:2a:bf:98:89:8b:94:57:21:87:54:4e:5b:4d:99:11:78:65:73:62:1f:6a:74:b8:2c
+-----BEGIN CERTIFICATE-----
+MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx
+CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE
+AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1
+NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZ
+MBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ76zBq
+AMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9
+vVYiQJ3q9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9
+lRdU2HhE8Qx3FZLgmEKnpNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTOD
+n3WhUidhOPFZPY5Q4L15POdslv5e2QJltI5c0BE0312/UqeBAMN/mUWZFdUXyApT
+7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW5olWK8jjfN7j/4nlNW4o
+6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNrRBH0pUPC
+TEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6
+WT0EBXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63R
+DolUK5X6wK0dmBR4M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZI
+pEYslOqodmJHixBTB0hXbOKSTbauBcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGj
+YzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7Wxy+G2CQ5MB0GA1UdDgQWBBRy
+rOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw
+AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ
+8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi
+0f6X+J8wfBj5tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMM
+A8iZGok1GTzTyVR8qPAs5m4HeW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBS
+SRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+Cy748fdHif64W1lZYudogsYMVoe+K
+TTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygCQMez2P2ccGrGKMOF
+6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15h2Er
+3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMt
+Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT
+VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW
+ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA
+rBPuUBQemMc=
+-----END CERTIFICATE-----
+
+# Issuer: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH
+# Subject: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH
+# Label: "D-TRUST BR Root CA 1 2020"
+# Serial: 165870826978392376648679885835942448534
+# MD5 Fingerprint: b5:aa:4b:d5:ed:f7:e3:55:2e:8f:72:0a:f3:75:b8:ed
+# SHA1 Fingerprint: 1f:5b:98:f0:e3:b5:f7:74:3c:ed:e6:b0:36:7d:32:cd:f4:09:41:67
+# SHA256 Fingerprint: e5:9a:aa:81:60:09:c2:2b:ff:5b:25:ba:d3:7d:f3:06:f0:49:79:7c:1f:81:d8:5a:b0:89:e6:57:bd:8f:00:44
+-----BEGIN CERTIFICATE-----
+MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw
+CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS
+VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5
+NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG
+A1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB
+BAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7dPYS
+zuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0
+QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/
+VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g
+PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3Rf
+Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l
+dC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1
+c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO
+PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW
+wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV
+dWNbFJWcHwHP2NVypw87
+-----END CERTIFICATE-----
+
+# Issuer: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH
+# Subject: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH
+# Label: "D-TRUST EV Root CA 1 2020"
+# Serial: 126288379621884218666039612629459926992
+# MD5 Fingerprint: 8c:2d:9d:70:9f:48:99:11:06:11:fb:e9:cb:30:c0:6e
+# SHA1 Fingerprint: 61:db:8c:21:59:69:03:90:d8:7c:9c:12:86:54:cf:9d:3d:f4:dd:07
+# SHA256 Fingerprint: 08:17:0d:1a:a3:64:53:90:1a:2f:95:92:45:e3:47:db:0c:8d:37:ab:aa:bc:56:b8:1a:a1:00:dc:95:89:70:db
+-----BEGIN CERTIFICATE-----
+MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw
+CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS
+VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5
+NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG
+A1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB
+BAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8ZRCC
+/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rD
+wpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3
+OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g
+PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3Rf
+Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l
+dC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1
+c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO
+PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA
+y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb
+gfM0agPnIjhQW+0ZT0MW
+-----END CERTIFICATE-----
+
+# Issuer: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc.
+# Subject: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc.
+# Label: "DigiCert TLS ECC P384 Root G5"
+# Serial: 13129116028163249804115411775095713523
+# MD5 Fingerprint: d3:71:04:6a:43:1c:db:a6:59:e1:a8:a3:aa:c5:71:ed
+# SHA1 Fingerprint: 17:f3:de:5e:9f:0f:19:e9:8e:f6:1f:32:26:6e:20:c4:07:ae:30:ee
+# SHA256 Fingerprint: 01:8e:13:f0:77:25:32:cf:80:9b:d1:b1:72:81:86:72:83:fc:48:c6:e1:3b:e9:c6:98:12:85:4a:49:0c:1b:05
+-----BEGIN CERTIFICATE-----
+MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp
+Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2
+MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
+bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG
+ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS
+7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp
+0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS
+B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49
+BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ
+LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4
+DXZDjC5Ty3zfDBeWUA==
+-----END CERTIFICATE-----
+
+# Issuer: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc.
+# Subject: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc.
+# Label: "DigiCert TLS RSA4096 Root G5"
+# Serial: 11930366277458970227240571539258396554
+# MD5 Fingerprint: ac:fe:f7:34:96:a9:f2:b3:b4:12:4b:e4:27:41:6f:e1
+# SHA1 Fingerprint: a7:88:49:dc:5d:7c:75:8c:8c:de:39:98:56:b3:aa:d0:b2:a5:71:35
+# SHA256 Fingerprint: 37:1a:00:dc:05:33:b3:72:1a:7e:eb:40:e8:41:9e:70:79:9d:2b:0a:0f:2c:1d:80:69:31:65:f7:ce:c4:ad:75
+-----BEGIN CERTIFICATE-----
+MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT
+HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN
+NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs
+IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+
+ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0
+2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp
+wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM
+pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD
+nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po
+sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx
+Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd
+Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX
+KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe
+XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL
+tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv
+TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN
+AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw
+GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H
+PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF
+O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ
+REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik
+AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv
+/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+
+p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw
+MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF
+qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK
+ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+
+-----END CERTIFICATE-----
+
+# Issuer: CN=Certainly Root R1 O=Certainly
+# Subject: CN=Certainly Root R1 O=Certainly
+# Label: "Certainly Root R1"
+# Serial: 188833316161142517227353805653483829216
+# MD5 Fingerprint: 07:70:d4:3e:82:87:a0:fa:33:36:13:f4:fa:33:e7:12
+# SHA1 Fingerprint: a0:50:ee:0f:28:71:f4:27:b2:12:6d:6f:50:96:25:ba:cc:86:42:af
+# SHA256 Fingerprint: 77:b8:2c:d8:64:4c:43:05:f7:ac:c5:cb:15:6b:45:67:50:04:03:3d:51:c6:0c:62:02:a8:e0:c3:34:67:d3:a0
+-----BEGIN CERTIFICATE-----
+MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw
+PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy
+dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9
+MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0
+YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2
+1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT
+vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed
+aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0
+1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5
+r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5
+cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ
+wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ
+6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA
+2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH
+Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR
+eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB
+/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u
+d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr
+PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d
+8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi
+1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd
+rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di
+taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7
+lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj
+yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn
+Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy
+yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n
+wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6
+OV+KmalBWQewLK8=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Certainly Root E1 O=Certainly
+# Subject: CN=Certainly Root E1 O=Certainly
+# Label: "Certainly Root E1"
+# Serial: 8168531406727139161245376702891150584
+# MD5 Fingerprint: 0a:9e:ca:cd:3e:52:50:c6:36:f3:4b:a3:ed:a7:53:e9
+# SHA1 Fingerprint: f9:e1:6d:dc:01:89:cf:d5:82:45:63:3e:c5:37:7d:c2:eb:93:6f:2b
+# SHA256 Fingerprint: b4:58:5f:22:e4:ac:75:6a:4e:86:12:a1:36:1c:5d:9d:03:1a:93:fd:84:fe:bb:77:8f:a3:06:8b:0f:c4:2d:c2
+-----BEGIN CERTIFICATE-----
+MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw
+CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu
+bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ
+BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s
+eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK
++IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2
+QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4
+hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm
+ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG
+BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR
+-----END CERTIFICATE-----
+
+# Issuer: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD.
+# Subject: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD.
+# Label: "Security Communication ECC RootCA1"
+# Serial: 15446673492073852651
+# MD5 Fingerprint: 7e:43:b0:92:68:ec:05:43:4c:98:ab:5d:35:2e:7e:86
+# SHA1 Fingerprint: b8:0e:26:a9:bf:d2:b2:3b:c0:ef:46:c9:ba:c7:bb:f6:1d:0d:41:41
+# SHA256 Fingerprint: e7:4f:bd:a5:5b:d5:64:c4:73:a3:6b:44:1a:a7:99:c8:a6:8e:07:74:40:e8:28:8b:9f:a1:e5:0e:4b:ba:ca:11
+-----BEGIN CERTIFICATE-----
+MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT
+AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD
+VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx
+NjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoT
+HFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNVBAMTIlNlY3VyaXR5
+IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
+AASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+Cnnfdl
+dB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpK
+ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu
+9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O
+be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k=
+-----END CERTIFICATE-----
+
+# Issuer: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY
+# Subject: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY
+# Label: "BJCA Global Root CA1"
+# Serial: 113562791157148395269083148143378328608
+# MD5 Fingerprint: 42:32:99:76:43:33:36:24:35:07:82:9b:28:f9:d0:90
+# SHA1 Fingerprint: d5:ec:8d:7b:4c:ba:79:f4:e7:e8:cb:9d:6b:ae:77:83:10:03:21:6a
+# SHA256 Fingerprint: f3:89:6f:88:fe:7c:0a:88:27:66:a7:fa:6a:d2:74:9f:b5:7a:7f:3e:98:fb:76:9c:1f:a7:b0:9c:2c:44:d5:ae
+-----BEGIN CERTIFICATE-----
+MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBU
+MQswCQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRI
+T1JJVFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAz
+MTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJF
+SUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2Jh
+bCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFmCL3Z
+xRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZ
+spDyRhySsTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O5
+58dnJCNPYwpj9mZ9S1WnP3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgR
+at7GGPZHOiJBhyL8xIkoVNiMpTAK+BcWyqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll
+5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRjeulumijWML3mG90Vr4Tq
+nMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNnMoH1V6XK
+V0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/
+pj+bOT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZO
+z2nxbkRs1CTqjSShGL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXn
+jSXWgXSHRtQpdaJCbPdzied9v3pKH9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+
+WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMBAAGjQjBAMB0GA1UdDgQWBBTF
+7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
+AwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4
+YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3Kli
+awLwQ8hOnThJdMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u
++2D2/VnGKhs/I0qUJDAnyIm860Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88
+X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuhTaRjAv04l5U/BXCga99igUOLtFkN
+SoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW4AB+dAb/OMRyHdOo
+P2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmpGQrI
++pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRz
+znfSxqxx4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9
+eVzYH6Eze9mCUAyTF6ps3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2
+YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4SSPfSKcOYKMryMguTjClPPGAyzQWWYezy
+r/6zcCwupvI=
+-----END CERTIFICATE-----
+
+# Issuer: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY
+# Subject: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY
+# Label: "BJCA Global Root CA2"
+# Serial: 58605626836079930195615843123109055211
+# MD5 Fingerprint: 5e:0a:f6:47:5f:a6:14:e8:11:01:95:3f:4d:01:eb:3c
+# SHA1 Fingerprint: f4:27:86:eb:6e:b8:6d:88:31:67:02:fb:ba:66:a4:53:00:aa:7a:a6
+# SHA256 Fingerprint: 57:4d:f6:93:1e:27:80:39:66:7b:72:0a:fd:c1:60:0f:c2:7e:b6:6d:d3:09:29:79:fb:73:85:64:87:21:28:82
+-----BEGIN CERTIFICATE-----
+MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw
+CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ
+VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy
+MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ
+TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS
+b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B
+IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+
++kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK
+sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
+AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA
+94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B
+43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited
+# Subject: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited
+# Label: "Sectigo Public Server Authentication Root E46"
+# Serial: 88989738453351742415770396670917916916
+# MD5 Fingerprint: 28:23:f8:b2:98:5c:37:16:3b:3e:46:13:4e:b0:b3:01
+# SHA1 Fingerprint: ec:8a:39:6c:40:f0:2e:bc:42:75:d4:9f:ab:1c:1a:5b:67:be:d2:9a
+# SHA256 Fingerprint: c9:0f:26:f0:fb:1b:40:18:b2:22:27:51:9b:5c:a2:b5:3e:2c:a5:b3:be:5c:f1:8e:fe:1b:ef:47:38:0c:53:83
+-----BEGIN CERTIFICATE-----
+MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw
+CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T
+ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN
+MjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYG
+A1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT
+ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccC
+WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+
+6xnOQ6OjQjBAMB0GA1UdDgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8B
+Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa
+qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q
+4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited
+# Subject: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited
+# Label: "Sectigo Public Server Authentication Root R46"
+# Serial: 156256931880233212765902055439220583700
+# MD5 Fingerprint: 32:10:09:52:00:d5:7e:6c:43:df:15:c0:b1:16:93:e5
+# SHA1 Fingerprint: ad:98:f9:f3:e4:7d:75:3b:65:d4:82:b3:a4:52:17:bb:6e:f5:e4:38
+# SHA256 Fingerprint: 7b:b6:47:a6:2a:ee:ac:88:bf:25:7a:a5:22:d0:1f:fe:a3:95:e0:ab:45:c7:3f:93:f6:56:54:ec:38:f2:5a:06
+-----BEGIN CERTIFICATE-----
+MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf
+MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD
+Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw
+HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY
+MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp
+YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa
+ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz
+SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf
+iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X
+ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3
+IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS
+VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE
+SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu
++Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt
+8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L
+HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt
+zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P
+AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c
+mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ
+YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52
+gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA
+Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB
+JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX
+DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui
+TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5
+dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65
+LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp
+0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY
+QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL
+-----END CERTIFICATE-----
+
+# Issuer: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation
+# Subject: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation
+# Label: "SSL.com TLS RSA Root CA 2022"
+# Serial: 148535279242832292258835760425842727825
+# MD5 Fingerprint: d8:4e:c6:59:30:d8:fe:a0:d6:7a:5a:2c:2c:69:78:da
+# SHA1 Fingerprint: ec:2c:83:40:72:af:26:95:10:ff:0e:f2:03:ee:31:70:f6:78:9d:ca
+# SHA256 Fingerprint: 8f:af:7d:2e:2c:b4:70:9b:b8:e0:b3:36:66:bf:75:a5:dd:45:b5:de:48:0f:8e:a8:d4:bf:e6:be:bc:17:f2:ed
+-----BEGIN CERTIFICATE-----
+MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO
+MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD
+DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX
+DTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw
+b3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJvb3QgQ0EgMjAyMjCC
+AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u9nTP
+L3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OY
+t6/wNr/y7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0ins
+S657Lb85/bRi3pZ7QcacoOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3
+PnxEX4MN8/HdIGkWCVDi1FW24IBydm5MR7d1VVm0U3TZlMZBrViKMWYPHqIbKUBO
+L9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDGD6C1vBdOSHtRwvzpXGk3
+R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEWTO6Af77w
+dr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS
++YCk8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYS
+d66UNHsef8JmAOSqg+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoG
+AtUjHBPW6dvbxrB6y3snm/vg1UYk7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2f
+gTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j
+BBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsuN+7jhHonLs0Z
+NbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt
+hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsM
+QtfhWsSWTVTNj8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvf
+R4iyrT7gJ4eLSYwfqUdYe5byiB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJ
+DPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjUo3KUQyxi4U5cMj29TH0ZR6LDSeeW
+P4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqoENjwuSfr98t67wVy
+lrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7EgkaibMOlq
+bLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2w
+AgDHbICivRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3q
+r5nsLFR+jM4uElZI7xc7P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sji
+Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU
+98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA=
+-----END CERTIFICATE-----
+
+# Issuer: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation
+# Subject: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation
+# Label: "SSL.com TLS ECC Root CA 2022"
+# Serial: 26605119622390491762507526719404364228
+# MD5 Fingerprint: 99:d7:5c:f1:51:36:cc:e9:ce:d9:19:2e:77:71:56:c5
+# SHA1 Fingerprint: 9f:5f:d9:1a:54:6d:f5:0c:71:f0:ee:7a:bd:17:49:98:84:73:e2:39
+# SHA256 Fingerprint: c3:2f:fd:9f:46:f9:36:d1:6c:36:73:99:09:59:43:4b:9a:d6:0a:af:bb:9e:7c:f3:36:54:f1:44:cc:1b:a1:43
+-----BEGIN CERTIFICATE-----
+MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw
+CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT
+U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2
+MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh
+dGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3QgQ0EgMjAyMjB2MBAG
+ByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWyJGYm
+acCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFN
+SeR7T5v15wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME
+GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW
+uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp
+15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN
+b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos
+# Subject: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos
+# Label: "Atos TrustedRoot Root CA ECC TLS 2021"
+# Serial: 81873346711060652204712539181482831616
+# MD5 Fingerprint: 16:9f:ad:f1:70:ad:79:d6:ed:29:b4:d1:c5:79:70:a8
+# SHA1 Fingerprint: 9e:bc:75:10:42:b3:02:f3:81:f4:f7:30:62:d4:8f:c3:a7:51:b2:dd
+# SHA256 Fingerprint: b2:fa:e5:3e:14:cc:d7:ab:92:12:06:47:01:ae:27:9c:1d:89:88:fa:cb:77:5f:a8:a0:08:91:4e:66:39:88:a8
+-----BEGIN CERTIFICATE-----
+MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w
+LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w
+CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0
+MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBF
+Q0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMHYwEAYHKoZI
+zj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6KDP/X
+tXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4
+AjJn8ZQSb+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2
+KCXWfeBmmnoJsmo7jjPXNtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMD
+aAAwZQIwW5kp85wxtolrbNa9d+F851F+uDrNozZffPc8dz7kUK2o59JZDCaOMDtu
+CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo
+9H1/IISpQuQo
+-----END CERTIFICATE-----
+
+# Issuer: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos
+# Subject: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos
+# Label: "Atos TrustedRoot Root CA RSA TLS 2021"
+# Serial: 111436099570196163832749341232207667876
+# MD5 Fingerprint: d4:d3:46:b8:9a:c0:9c:76:5d:9e:3a:c3:b9:99:31:d2
+# SHA1 Fingerprint: 18:52:3b:0d:06:37:e4:d6:3a:df:23:e4:98:fb:5b:16:fb:86:74:48
+# SHA256 Fingerprint: 81:a9:08:8e:a5:9f:b3:64:c5:48:a6:f8:55:59:09:9b:6f:04:05:ef:bf:18:e5:32:4e:c9:f4:57:ba:00:11:2f
+-----BEGIN CERTIFICATE-----
+MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBM
+MS4wLAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIx
+MQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00
+MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBD
+QSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMIICIjAN
+BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BBl01Z
+4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYv
+Ye+W/CBGvevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZ
+kmGbzSoXfduP9LVq6hdKZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDs
+GY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt0xU6kGpn8bRrZtkh68rZYnxGEFzedUln
+nkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVKPNe0OwANwI8f4UDErmwh
+3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMYsluMWuPD
+0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzy
+geBYBr3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8
+ANSbhqRAvNncTFd+rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezB
+c6eUWsuSZIKmAMFwoW4sKeFYV+xafJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lI
+pw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
+dEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
+DAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS
+4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPs
+o0UvFJ/1TCplQ3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJ
+qM7F78PRreBrAwA0JrRUITWXAdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuyw
+xfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9GslA9hGCZcbUztVdF5kJHdWoOsAgM
+rr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2VktafcxBPTy+av5EzH4
+AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9qTFsR
+0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuY
+o7Ey7Nmj1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5
+dDTedk+SKlOxJTnbPP/lPqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcE
+oji2jbDwN/zIIX8/syQbPYtuzE2wFg2WHYMfRsCbvUOZ58SWLs5fyQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc.
+# Subject: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc.
+# Label: "TrustAsia Global Root CA G3"
+# Serial: 576386314500428537169965010905813481816650257167
+# MD5 Fingerprint: 30:42:1b:b7:bb:81:75:35:e4:16:4f:53:d2:94:de:04
+# SHA1 Fingerprint: 63:cf:b6:c1:27:2b:56:e4:88:8e:1c:23:9a:b6:2e:81:47:24:c3:c7
+# SHA256 Fingerprint: e0:d3:22:6a:eb:11:63:c2:e4:8f:f9:be:3b:50:b4:c6:43:1b:e7:bb:1e:ac:c5:c3:6b:5d:5e:c5:09:03:9a:08
+-----BEGIN CERTIFICATE-----
+MIIFpTCCA42gAwIBAgIUZPYOZXdhaqs7tOqFhLuxibhxkw8wDQYJKoZIhvcNAQEM
+BQAwWjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dp
+ZXMsIEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHMzAe
+Fw0yMTA1MjAwMjEwMTlaFw00NjA1MTkwMjEwMTlaMFoxCzAJBgNVBAYTAkNOMSUw
+IwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtU
+cnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQDAMYJhkuSUGwoqZdC+BqmHO1ES6nBBruL7dOoKjbmzTNyPtxNS
+T1QY4SxzlZHFZjtqz6xjbYdT8PfxObegQ2OwxANdV6nnRM7EoYNl9lA+sX4WuDqK
+AtCWHwDNBSHvBm3dIZwZQ0WhxeiAysKtQGIXBsaqvPPW5vxQfmZCHzyLpnl5hkA1
+nyDvP+uLRx+PjsXUjrYsyUQE49RDdT/VP68czH5GX6zfZBCK70bwkPAPLfSIC7Ep
+qq+FqklYqL9joDiR5rPmd2jE+SoZhLsO4fWvieylL1AgdB4SQXMeJNnKziyhWTXA
+yB1GJ2Faj/lN03J5Zh6fFZAhLf3ti1ZwA0pJPn9pMRJpxx5cynoTi+jm9WAPzJMs
+hH/x/Gr8m0ed262IPfN2dTPXS6TIi/n1Q1hPy8gDVI+lhXgEGvNz8teHHUGf59gX
+zhqcD0r83ERoVGjiQTz+LISGNzzNPy+i2+f3VANfWdP3kXjHi3dqFuVJhZBFcnAv
+kV34PmVACxmZySYgWmjBNb9Pp1Hx2BErW+Canig7CjoKH8GB5S7wprlppYiU5msT
+f9FkPz2ccEblooV7WIQn3MSAPmeamseaMQ4w7OYXQJXZRe0Blqq/DPNL0WP3E1jA
+uPP6Z92bfW1K/zJMtSU7/xxnD4UiWQWRkUF3gdCFTIcQcf+eQxuulXUtgQIDAQAB
+o2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEDk5PIj7zjKsK5Xf/Ih
+MBY027ySMB0GA1UdDgQWBBRA5OTyI+84yrCuV3/yITAWNNu8kjAOBgNVHQ8BAf8E
+BAMCAQYwDQYJKoZIhvcNAQEMBQADggIBACY7UeFNOPMyGLS0XuFlXsSUT9SnYaP4
+wM8zAQLpw6o1D/GUE3d3NZ4tVlFEbuHGLige/9rsR82XRBf34EzC4Xx8MnpmyFq2
+XFNFV1pF1AWZLy4jVe5jaN/TG3inEpQGAHUNcoTpLrxaatXeL1nHo+zSh2bbt1S1
+JKv0Q3jbSwTEb93mPmY+KfJLaHEih6D4sTNjduMNhXJEIlU/HHzp/LgV6FL6qj6j
+ITk1dImmasI5+njPtqzn59ZW/yOSLlALqbUHM/Q4X6RJpstlcHboCoWASzY9M/eV
+VHUl2qzEc4Jl6VL1XP04lQJqaTDFHApXB64ipCz5xUG3uOyfT0gA+QEEVcys+TIx
+xHWVBqB/0Y0n3bOppHKH/lmLmnp0Ft0WpWIp6zqW3IunaFnT63eROfjXy9mPX1on
+AX1daBli2MjN9LdyR75bl87yraKZk62Uy5P2EgmVtqvXO9A/EcswFi55gORngS1d
+7XB4tmBZrOFdRWOPyN9yaFvqHbgB8X7754qz41SgOAngPN5C8sLtLpvzHzW2Ntjj
+gKGLzZlkD8Kqq7HK9W+eQ42EVJmzbsASZthwEPEGNTNDqJwuuhQxzhB/HIbjj9LV
++Hfsm6vxL2PZQl/gZ4FkkfGXL/xuJvYz+NO1+MRiqzFRJQJ6+N1rZdVtTTDIZbpo
+FGWsJwt0ivKH
+-----END CERTIFICATE-----
+
+# Issuer: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc.
+# Subject: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc.
+# Label: "TrustAsia Global Root CA G4"
+# Serial: 451799571007117016466790293371524403291602933463
+# MD5 Fingerprint: 54:dd:b2:d7:5f:d8:3e:ed:7c:e0:0b:2e:cc:ed:eb:eb
+# SHA1 Fingerprint: 57:73:a5:61:5d:80:b2:e6:ac:38:82:fc:68:07:31:ac:9f:b5:92:5a
+# SHA256 Fingerprint: be:4b:56:cb:50:56:c0:13:6a:52:6d:f4:44:50:8d:aa:36:a0:b5:4f:42:e4:ac:38:f7:2a:f4:70:e4:79:65:4c
+-----BEGIN CERTIFICATE-----
+MIICVTCCAdygAwIBAgIUTyNkuI6XY57GU4HBdk7LKnQV1tcwCgYIKoZIzj0EAwMw
+WjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs
+IEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHNDAeFw0y
+MTA1MjAwMjEwMjJaFw00NjA1MTkwMjEwMjJaMFoxCzAJBgNVBAYTAkNOMSUwIwYD
+VQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtUcnVz
+dEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATx
+s8045CVD5d4ZCbuBeaIVXxVjAd7Cq92zphtnS4CDr5nLrBfbK5bKfFJV4hrhPVbw
+LxYI+hW8m7tH5j/uqOFMjPXTNvk4XatwmkcN4oFBButJ+bAp3TPsUKV/eSm4IJij
+YzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUpbtKl86zK3+kMd6Xg1mD
+pm9xy94wHQYDVR0OBBYEFKW7SpfOsyt/pDHel4NZg6ZvccveMA4GA1UdDwEB/wQE
+AwIBBjAKBggqhkjOPQQDAwNnADBkAjBe8usGzEkxn0AAbbd+NvBNEU/zy4k6LHiR
+UKNbwMp1JvK/kF0LgoxgKJ/GcJpo5PECMFxYDlZ2z1jD1xCMuo6u47xkdUfFVZDj
+/bpV6wfEU6s3qe4hsiFbYI89MvHVI5TWWA==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH
+# Subject: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH
+# Label: "Telekom Security TLS ECC Root 2020"
+# Serial: 72082518505882327255703894282316633856
+# MD5 Fingerprint: c1:ab:fe:6a:10:2c:03:8d:bc:1c:22:32:c0:85:a7:fd
+# SHA1 Fingerprint: c0:f8:96:c5:a9:3b:01:06:21:07:da:18:42:48:bc:e9:9d:88:d5:ec
+# SHA256 Fingerprint: 57:8a:f4:de:d0:85:3f:4e:59:98:db:4a:ea:f9:cb:ea:8d:94:5f:60:b6:20:a3:8d:1a:3c:13:b2:bc:7b:a8:e1
+-----BEGIN CERTIFICATE-----
+MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQsw
+CQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBH
+bWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIw
+MB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIzNTk1OVowYzELMAkGA1UEBhMCREUx
+JzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkGA1UE
+AwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqGSM49
+AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/O
+tdKPD/M12kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDP
+f8iAC8GXs7s1J8nCG6NCMEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6f
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA
+MGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZMo7k+5Dck2TOrbRBR2Di
+z6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdUga/sf+Rn
+27iQ7t0l
+-----END CERTIFICATE-----
+
+# Issuer: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH
+# Subject: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH
+# Label: "Telekom Security TLS RSA Root 2023"
+# Serial: 44676229530606711399881795178081572759
+# MD5 Fingerprint: bf:5b:eb:54:40:cd:48:71:c4:20:8d:7d:de:0a:42:f2
+# SHA1 Fingerprint: 54:d3:ac:b3:bd:57:56:f6:85:9d:ce:e5:c3:21:e2:d4:ad:83:d0:93
+# SHA256 Fingerprint: ef:c6:5c:ad:bb:59:ad:b6:ef:e8:4d:a2:23:11:b3:56:24:b7:1b:3b:1e:a0:da:8b:66:55:17:4e:c8:97:86:46
+-----BEGIN CERTIFICATE-----
+MIIFszCCA5ugAwIBAgIQIZxULej27HF3+k7ow3BXlzANBgkqhkiG9w0BAQwFADBj
+MQswCQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0
+eSBHbWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBSU0EgUm9vdCAy
+MDIzMB4XDTIzMDMyODEyMTY0NVoXDTQ4MDMyNzIzNTk1OVowYzELMAkGA1UEBhMC
+REUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkG
+A1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgUlNBIFJvb3QgMjAyMzCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBAO01oYGA88tKaVvC+1GDrib94W7zgRJ9
+cUD/h3VCKSHtgVIs3xLBGYSJwb3FKNXVS2xE1kzbB5ZKVXrKNoIENqil/Cf2SfHV
+cp6R+SPWcHu79ZvB7JPPGeplfohwoHP89v+1VmLhc2o0mD6CuKyVU/QBoCcHcqMA
+U6DksquDOFczJZSfvkgdmOGjup5czQRxUX11eKvzWarE4GC+j4NSuHUaQTXtvPM6
+Y+mpFEXX5lLRbtLevOP1Czvm4MS9Q2QTps70mDdsipWol8hHD/BeEIvnHRz+sTug
+BTNoBUGCwQMrAcjnj02r6LX2zWtEtefdi+zqJbQAIldNsLGyMcEWzv/9FIS3R/qy
+8XDe24tsNlikfLMR0cN3f1+2JeANxdKz+bi4d9s3cXFH42AYTyS2dTd4uaNir73J
+co4vzLuu2+QVUhkHM/tqty1LkCiCc/4YizWN26cEar7qwU02OxY2kTLvtkCJkUPg
+8qKrBC7m8kwOFjQgrIfBLX7JZkcXFBGk8/ehJImr2BrIoVyxo/eMbcgByU/J7MT8
+rFEz0ciD0cmfHdRHNCk+y7AO+oMLKFjlKdw/fKifybYKu6boRhYPluV75Gp6SG12
+mAWl3G0eQh5C2hrgUve1g8Aae3g1LDj1H/1Joy7SWWO/gLCMk3PLNaaZlSJhZQNg
++y+TS/qanIA7AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtqeX
+gj10hZv3PJ+TmpV5dVKMbUcwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS2
+p5eCPXSFm/c8n5OalXl1UoxtRzANBgkqhkiG9w0BAQwFAAOCAgEAqMxhpr51nhVQ
+pGv7qHBFfLp+sVr8WyP6Cnf4mHGCDG3gXkaqk/QeoMPhk9tLrbKmXauw1GLLXrtm
+9S3ul0A8Yute1hTWjOKWi0FpkzXmuZlrYrShF2Y0pmtjxrlO8iLpWA1WQdH6DErw
+M807u20hOq6OcrXDSvvpfeWxm4bu4uB9tPcy/SKE8YXJN3nptT+/XOR0so8RYgDd
+GGah2XsjX/GO1WfoVNpbOms2b/mBsTNHM3dA+VKq3dSDz4V4mZqTuXNnQkYRIer+
+CqkbGmVps4+uFrb2S1ayLfmlyOw7YqPta9BO1UAJpB+Y1zqlklkg5LB9zVtzaL1t
+xKITDmcZuI1CfmwMmm6gJC3VRRvcxAIU/oVbZZfKTpBQCHpCNfnqwmbU+AGuHrS+
+w6jv/naaoqYfRvaE7fzbzsQCzndILIyy7MMAo+wsVRjBfhnu4S/yrYObnqsZ38aK
+L4x35bcF7DvB7L6Gs4a8wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+lj
+X273CXE2whJdV/LItM3z7gLfEdxquVeEHVlNjM7IDiPCtyaaEBRx/pOyiriA8A4Q
+ntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0o82bNSQ3+pCTE4FCxpgm
+dTdmQRCsu/WU48IxK63nI1bMNSWSs1A=
+-----END CERTIFICATE-----
+
+# Issuer: CN=TWCA CYBER Root CA O=TAIWAN-CA OU=Root CA
+# Subject: CN=TWCA CYBER Root CA O=TAIWAN-CA OU=Root CA
+# Label: "TWCA CYBER Root CA"
+# Serial: 85076849864375384482682434040119489222
+# MD5 Fingerprint: 0b:33:a0:97:52:95:d4:a9:fd:bb:db:6e:a3:55:5b:51
+# SHA1 Fingerprint: f6:b1:1c:1a:83:38:e9:7b:db:b3:a8:c8:33:24:e0:2d:9c:7f:26:66
+# SHA256 Fingerprint: 3f:63:bb:28:14:be:17:4e:c8:b6:43:9c:f0:8d:6d:56:f0:b7:c4:05:88:3a:56:48:a3:34:42:4d:6b:3e:c5:58
+-----BEGIN CERTIFICATE-----
+MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQ
+MQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290
+IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5
+WhcNNDcxMTIyMTU1OTU5WjBQMQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FO
+LUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3Qg
+Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG+Moe2Qkgfh1sTs6P
+40czRJzHyWmqOlt47nDSkvgEs1JSHWdyKKHfi12VCv7qze33Kc7wb3+szT3vsxxF
+avcokPFhV8UMxKNQXd7UtcsZyoC5dc4pztKFIuwCY8xEMCDa6pFbVuYdHNWdZsc/
+34bKS1PE2Y2yHer43CdTo0fhYcx9tbD47nORxc5zb87uEB8aBs/pJ2DFTxnk684i
+JkXXYJndzk834H/nY62wuFm40AZoNWDTNq5xQwTxaWV4fPMf88oon1oglWa0zbfu
+j3ikRRjpJi+NmykosaS3Om251Bw4ckVYsV7r8Cibt4LK/c/WMw+f+5eesRycnupf
+Xtuq3VTpMCEobY5583WSjCb+3MX2w7DfRFlDo7YDKPYIMKoNM+HvnKkHIuNZW0CP
+2oi3aQiotyMuRAlZN1vH4xfyIutuOVLF3lSnmMlLIJXcRolftBL5hSmO68gnFSDA
+S9TMfAxsNAwmmyYxpjyn9tnQS6Jk/zuZQXLB4HCX8SS7K8R0IrGsayIyJNN4KsDA
+oS/xUgXJP+92ZuJF2A09rZXIx4kmyA+upwMu+8Ff+iDhcK2wZSA3M2Cw1a/XDBzC
+kHDXShi8fgGwsOsVHkQGzaRP6AzRwyAQ4VRlnrZR0Bp2a0JaWHY06rc3Ga4udfmW
+5cFZ95RXKSWNOkyrTZpB0F8mAwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYD
+VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSdhWEUfMFib5do5E83QOGt4A1WNzAd
+BgNVHQ4EFgQUnYVhFHzBYm+XaORPN0DhreANVjcwDQYJKoZIhvcNAQEMBQADggIB
+AGSPesRiDrWIzLjHhg6hShbNcAu3p4ULs3a2D6f/CIsLJc+o1IN1KriWiLb73y0t
+tGlTITVX1olNc79pj3CjYcya2x6a4CD4bLubIp1dhDGaLIrdaqHXKGnK/nZVekZn
+68xDiBaiA9a5F/gZbG0jAn/xX9AKKSM70aoK7akXJlQKTcKlTfjF/biBzysseKNn
+TKkHmvPfXvt89YnNdJdhEGoHK4Fa0o635yDRIG4kqIQnoVesqlVYL9zZyvpoBJ7t
+RCT5dEA7IzOrg1oYJkK2bVS1FmAwbLGg+LhBoF1JSdJlBTrq/p1hvIbZv97Tujqx
+f36SNI7JAG7cmL3c7IAFrQI932XtCwP39xaEBDG6k5TY8hL4iuO/Qq+n1M0RFxbI
+Qh0UqEL20kCGoE8jypZFVmAGzbdVAaYBlGX+bgUJurSkquLvWL69J1bY73NxW0Qz
+8ppy6rBePm6pUlvscG21h483XjyMnM7k8M4MZ0HMzvaAq07MTFb1wWFZk7Q+ptq4
+NxKfKjLji7gh7MMrZQzvIt6IKTtM1/r+t+FHvpw+PoP7UV31aPcuIYXcv/Fa4nzX
+xeSDwWrruoBa3lwtcHb4yOWHh8qgnaHlIhInD0Q9HWzq1MKLL295q39QpsQZp6F6
+t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X
+-----END CERTIFICATE-----
+
+# Issuer: CN=SecureSign Root CA14 O=Cybertrust Japan Co., Ltd.
+# Subject: CN=SecureSign Root CA14 O=Cybertrust Japan Co., Ltd.
+# Label: "SecureSign Root CA14"
+# Serial: 575790784512929437950770173562378038616896959179
+# MD5 Fingerprint: 71:0d:72:fa:92:19:65:5e:89:04:ac:16:33:f0:bc:d5
+# SHA1 Fingerprint: dd:50:c0:f7:79:b3:64:2e:74:a2:b8:9d:9f:d3:40:dd:bb:f0:f2:4f
+# SHA256 Fingerprint: 4b:00:9c:10:34:49:4f:9a:b5:6b:ba:3b:a1:d6:27:31:fc:4d:20:d8:95:5a:dc:ec:10:a9:25:60:72:61:e3:38
+-----BEGIN CERTIFICATE-----
+MIIFcjCCA1qgAwIBAgIUZNtaDCBO6Ncpd8hQJ6JaJ90t8sswDQYJKoZIhvcNAQEM
+BQAwUTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28u
+LCBMdGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNDAeFw0yMDA0MDgw
+NzA2MTlaFw00NTA0MDgwNzA2MTlaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpD
+eWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBS
+b290IENBMTQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF0nqh1oq/
+FjHQmNE6lPxauG4iwWL3pwon71D2LrGeaBLwbCRjOfHw3xDG3rdSINVSW0KZnvOg
+vlIfX8xnbacuUKLBl422+JX1sLrcneC+y9/3OPJH9aaakpUqYllQC6KxNedlsmGy
+6pJxaeQp8E+BgQQ8sqVb1MWoWWd7VRxJq3qdwudzTe/NCcLEVxLbAQ4jeQkHO6Lo
+/IrPj8BGJJw4J+CDnRugv3gVEOuGTgpa/d/aLIJ+7sr2KeH6caH3iGicnPCNvg9J
+kdjqOvn90Ghx2+m1K06Ckm9mH+Dw3EzsytHqunQG+bOEkJTRX45zGRBdAuVwpcAQ
+0BB8b8VYSbSwbprafZX1zNoCr7gsfXmPvkPx+SgojQlD+Ajda8iLLCSxjVIHvXib
+y8posqTdDEx5YMaZ0ZPxMBoH064iwurO8YQJzOAUbn8/ftKChazcqRZOhaBgy/ac
+18izju3Gm5h1DVXoX+WViwKkrkMpKBGk5hIwAUt1ax5mnXkvpXYvHUC0bcl9eQjs
+0Wq2XSqypWa9a4X0dFbD9ed1Uigspf9mR6XU/v6eVL9lfgHWMI+lNpyiUBzuOIAB
+SMbHdPTGrMNASRZhdCyvjG817XsYAFs2PJxQDcqSMxDxJklt33UkN4Ii1+iW/RVL
+ApY+B3KVfqs9TC7XyvDf4Fg/LS8EmjijAQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUBpOjCl4oaTeqYR3r6/wtbyPk
+86AwDQYJKoZIhvcNAQEMBQADggIBAJaAcgkGfpzMkwQWu6A6jZJOtxEaCnFxEM0E
+rX+lRVAQZk5KQaID2RFPeje5S+LGjzJmdSX7684/AykmjbgWHfYfM25I5uj4V7Ib
+ed87hwriZLoAymzvftAj63iP/2SbNDefNWWipAA9EiOWWF3KY4fGoweITedpdopT
+zfFP7ELyk+OZpDc8h7hi2/DsHzc/N19DzFGdtfCXwreFamgLRB7lUe6TzktuhsHS
+DCRZNhqfLJGP4xjblJUK7ZGqDpncllPjYYPGFrojutzdfhrGe0K22VoF3Jpf1d+4
+2kd92jjbrDnVHmtsKheMYc2xbXIBw8MgAGJoFjHVdqqGuw6qnsb58Nn4DSEC5MUo
+FlkRudlpcyqSeLiSV5sI8jrlL5WwWLdrIBRtFO8KvH7YVdiI2i/6GaX7i+B/OfVy
+K4XELKzvGUWSTLNhB9xNH27SgRNcmvMSZ4PPmz+Ln52kuaiWA3rF7iDeM9ovnhp6
+dB7h7sxaOgTdsxoEqBRjrLdHEoOabPXm6RUVkRqEGQ6UROcSjiVbgGcZ3GOTEAtl
+Lor6CZpO2oYofaphNdgOpygau1LgePhsumywbrmHXumZNTfxPWQrqaA0k89jL9WB
+365jJ6UeTo3cKXhZ+PmhIIynJkBugnLNeLLIjzwec+fBH7/PzqUqm9tEZDKgu39c
+JRNItX+S
+-----END CERTIFICATE-----
+
+# Issuer: CN=SecureSign Root CA15 O=Cybertrust Japan Co., Ltd.
+# Subject: CN=SecureSign Root CA15 O=Cybertrust Japan Co., Ltd.
+# Label: "SecureSign Root CA15"
+# Serial: 126083514594751269499665114766174399806381178503
+# MD5 Fingerprint: 13:30:fc:c4:62:a6:a9:de:b5:c1:68:af:b5:d2:31:47
+# SHA1 Fingerprint: cb:ba:83:c8:c1:5a:5d:f1:f9:73:6f:ca:d7:ef:28:13:06:4a:07:7d
+# SHA256 Fingerprint: e7:78:f0:f0:95:fe:84:37:29:cd:1a:00:82:17:9e:53:14:a9:c2:91:44:28:05:e1:fb:1d:8f:b6:b8:88:6c:3a
+-----BEGIN CERTIFICATE-----
+MIICIzCCAamgAwIBAgIUFhXHw9hJp75pDIqI7fBw+d23PocwCgYIKoZIzj0EAwMw
+UTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBM
+dGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNTAeFw0yMDA0MDgwODMy
+NTZaFw00NTA0MDgwODMyNTZaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpDeWJl
+cnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBSb290
+IENBMTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQLUHSNZDKZmbPSYAi4Io5GdCx4
+wCtELW1fHcmuS1Iggz24FG1Th2CeX2yF2wYUleDHKP+dX+Sq8bOLbe1PL0vJSpSR
+ZHX+AezB2Ot6lHhWGENfa4HL9rzatAy2KZMIaY+jQjBAMA8GA1UdEwEB/wQFMAMB
+Af8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTrQciu/NWeUUj1vYv0hyCTQSvT
+9DAKBggqhkjOPQQDAwNoADBlAjEA2S6Jfl5OpBEHvVnCB96rMjhTKkZEBhd6zlHp
+4P9mLQlO4E/0BdGF9jVg3PVys0Z9AjBEmEYagoUeYWmJSwdLZrWeqrqgHkHZAXQ6
+bkU6iYAZezKYVWOr62Nuk22rGwlgMU4=
+-----END CERTIFICATE-----
+
+# Issuer: CN=D-TRUST BR Root CA 2 2023 O=D-Trust GmbH
+# Subject: CN=D-TRUST BR Root CA 2 2023 O=D-Trust GmbH
+# Label: "D-TRUST BR Root CA 2 2023"
+# Serial: 153168538924886464690566649552453098598
+# MD5 Fingerprint: e1:09:ed:d3:60:d4:56:1b:47:1f:b7:0c:5f:1b:5f:85
+# SHA1 Fingerprint: 2d:b0:70:ee:71:94:af:69:68:17:db:79:ce:58:9f:a0:6b:96:f7:87
+# SHA256 Fingerprint: 05:52:e6:f8:3f:df:65:e8:fa:96:70:e6:66:df:28:a4:e2:13:40:b5:10:cb:e5:25:66:f9:7c:4f:b9:4b:2b:d1
+-----BEGIN CERTIFICATE-----
+MIIFqTCCA5GgAwIBAgIQczswBEhb2U14LnNLyaHcZjANBgkqhkiG9w0BAQ0FADBI
+MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlE
+LVRSVVNUIEJSIFJvb3QgQ0EgMiAyMDIzMB4XDTIzMDUwOTA4NTYzMVoXDTM4MDUw
+OTA4NTYzMFowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEi
+MCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDIgMjAyMzCCAiIwDQYJKoZIhvcN
+AQEBBQADggIPADCCAgoCggIBAK7/CVmRgApKaOYkP7in5Mg6CjoWzckjYaCTcfKr
+i3OPoGdlYNJUa2NRb0kz4HIHE304zQaSBylSa053bATTlfrdTIzZXcFhfUvnKLNE
+gXtRr90zsWh81k5M/itoucpmacTsXld/9w3HnDY25QdgrMBM6ghs7wZ8T1soegj8
+k12b9py0i4a6Ibn08OhZWiihNIQaJZG2tY/vsvmA+vk9PBFy2OMvhnbFeSzBqZCT
+Rphny4NqoFAjpzv2gTng7fC5v2Xx2Mt6++9zA84A9H3X4F07ZrjcjrqDy4d2A/wl
+2ecjbwb9Z/Pg/4S8R7+1FhhGaRTMBffb00msa8yr5LULQyReS2tNZ9/WtT5PeB+U
+cSTq3nD88ZP+npNa5JRal1QMNXtfbO4AHyTsA7oC9Xb0n9Sa7YUsOCIvx9gvdhFP
+/Wxc6PWOJ4d/GUohR5AdeY0cW/jPSoXk7bNbjb7EZChdQcRurDhaTyN0dKkSw/bS
+uREVMweR2Ds3OmMwBtHFIjYoYiMQ4EbMl6zWK11kJNXuHA7e+whadSr2Y23OC0K+
+0bpwHJwh5Q8xaRfX/Aq03u2AnMuStIv13lmiWAmlY0cL4UEyNEHZmrHZqLAbWt4N
+DfTisl01gLmB1IRpkQLLddCNxbU9CZEJjxShFHR5PtbJFR2kWVki3PaKRT08EtY+
+XTIvAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUZ5Dw1t61
+GNVGKX5cq/ieCLxklRAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRCMEAwPqA8oDqG
+OGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfYnJfcm9vdF9jYV8y
+XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQA097N3U9swFrktpSHxQCF16+tI
+FoE9c+CeJyrrd6kTpGoKWloUMz1oH4Guaf2Mn2VsNELZLdB/eBaxOqwjMa1ef67n
+riv6uvw8l5VAk1/DLQOj7aRvU9f6QA4w9QAgLABMjDu0ox+2v5Eyq6+SmNMW5tTR
+VFxDWy6u71cqqLRvpO8NVhTaIasgdp4D/Ca4nj8+AybmTNudX0KEPUUDAxxZiMrc
+LmEkWqTqJwtzEr5SswrPMhfiHocaFpVIbVrg0M8JkiZmkdijYQ6qgYF/6FKC0ULn
+4B0Y+qSFNueG4A3rvNTJ1jxD8V1Jbn6Bm2m1iWKPiFLY1/4nwSPFyysCu7Ff/vtD
+hQNGvl3GyiEm/9cCnnRK3PgTFbGBVzbLZVzRHTF36SXDw7IyN9XxmAnkbWOACKsG
+koHU6XCPpz+y7YaMgmo1yEJagtFSGkUPFaUA8JR7ZSdXOUPPfH/mvTWze/EZTN46
+ls/pdu4D58JDUjxqgejBWoC9EV2Ta/vH5mQ/u2kc6d0li690yVRAysuTEwrt+2aS
+Ecr1wPrYg1UDfNPFIkZ1cGt5SAYqgpq/5usWDiJFAbzdNpQ0qTUmiteXue4Icr80
+knCDgKs4qllo3UCkGJCy89UDyibK79XH4I9TjvAA46jtn/mtd+ArY0+ew+43u3gJ
+hJ65bvspmZDogNOfJA==
+-----END CERTIFICATE-----
+
+# Issuer: CN=TrustAsia TLS ECC Root CA O=TrustAsia Technologies, Inc.
+# Subject: CN=TrustAsia TLS ECC Root CA O=TrustAsia Technologies, Inc.
+# Label: "TrustAsia TLS ECC Root CA"
+# Serial: 310892014698942880364840003424242768478804666567
+# MD5 Fingerprint: 09:48:04:77:d2:fc:65:93:71:66:b1:11:95:4f:06:8c
+# SHA1 Fingerprint: b5:ec:39:f3:a1:66:37:ae:c3:05:94:57:e2:be:11:be:b7:a1:7f:36
+# SHA256 Fingerprint: c0:07:6b:9e:f0:53:1f:b1:a6:56:d6:7c:4e:be:97:cd:5d:ba:a4:1e:f4:45:98:ac:c2:48:98:78:c9:2d:87:11
+-----BEGIN CERTIFICATE-----
+MIICMTCCAbegAwIBAgIUNnThTXxlE8msg1UloD5Sfi9QaMcwCgYIKoZIzj0EAwMw
+WDELMAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs
+IEluYy4xIjAgBgNVBAMTGVRydXN0QXNpYSBUTFMgRUNDIFJvb3QgQ0EwHhcNMjQw
+NTE1MDU0MTU2WhcNNDQwNTE1MDU0MTU1WjBYMQswCQYDVQQGEwJDTjElMCMGA1UE
+ChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjEiMCAGA1UEAxMZVHJ1c3RB
+c2lhIFRMUyBFQ0MgUm9vdCBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLh/pVs/
+AT598IhtrimY4ZtcU5nb9wj/1WrgjstEpvDBjL1P1M7UiFPoXlfXTr4sP/MSpwDp
+guMqWzJ8S5sUKZ74LYO1644xST0mYekdcouJtgq7nDM1D9rs3qlKH8kzsaNCMEAw
+DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQULIVTu7FDzTLqnqOH/qKYqKaT6RAw
+DgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMFRH18MtYYZI9HlaVQ01
+L18N9mdsd0AaRuf4aFtOJx24mH1/k78ITcTaRTChD15KeAIxAKORh/IRM4PDwYqR
+OkwrULG9IpRdNYlzg8WbGf60oenUoWa2AaU2+dhoYSi3dOGiMQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=TrustAsia TLS RSA Root CA O=TrustAsia Technologies, Inc.
+# Subject: CN=TrustAsia TLS RSA Root CA O=TrustAsia Technologies, Inc.
+# Label: "TrustAsia TLS RSA Root CA"
+# Serial: 160405846464868906657516898462547310235378010780
+# MD5 Fingerprint: 3b:9e:c3:86:0f:34:3c:6b:c5:46:c4:8e:1d:e7:19:12
+# SHA1 Fingerprint: a5:46:50:c5:62:ea:95:9a:1a:a7:04:6f:17:58:c7:29:53:3d:03:fa
+# SHA256 Fingerprint: 06:c0:8d:7d:af:d8:76:97:1e:b1:12:4f:e6:7f:84:7e:c0:c7:a1:58:d3:ea:53:cb:e9:40:e2:ea:97:91:f4:c3
+-----BEGIN CERTIFICATE-----
+MIIFgDCCA2igAwIBAgIUHBjYz+VTPyI1RlNUJDxsR9FcSpwwDQYJKoZIhvcNAQEM
+BQAwWDELMAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dp
+ZXMsIEluYy4xIjAgBgNVBAMTGVRydXN0QXNpYSBUTFMgUlNBIFJvb3QgQ0EwHhcN
+MjQwNTE1MDU0MTU3WhcNNDQwNTE1MDU0MTU2WjBYMQswCQYDVQQGEwJDTjElMCMG
+A1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjEiMCAGA1UEAxMZVHJ1
+c3RBc2lhIFRMUyBSU0EgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
+AgoCggIBAMMWuBtqpERz5dZO9LnPWwvB0ZqB9WOwj0PBuwhaGnrhB3YmH49pVr7+
+NmDQDIPNlOrnxS1cLwUWAp4KqC/lYCZUlviYQB2srp10Zy9U+5RjmOMmSoPGlbYJ
+Q1DNDX3eRA5gEk9bNb2/mThtfWza4mhzH/kxpRkQcwUqwzIZheo0qt1CHjCNP561
+HmHVb70AcnKtEj+qpklz8oYVlQwQX1Fkzv93uMltrOXVmPGZLmzjyUT5tUMnCE32
+ft5EebuyjBza00tsLtbDeLdM1aTk2tyKjg7/D8OmYCYozza/+lcK7Fs/6TAWe8Tb
+xNRkoDD75f0dcZLdKY9BWN4ArTr9PXwaqLEX8E40eFgl1oUh63kd0Nyrz2I8sMeX
+i9bQn9P+PN7F4/w6g3CEIR0JwqH8uyghZVNgepBtljhb//HXeltt08lwSUq6HTrQ
+UNoyIBnkiz/r1RYmNzz7dZ6wB3C4FGB33PYPXFIKvF1tjVEK2sUYyJtt3LCDs3+j
+TnhMmCWr8n4uIF6CFabW2I+s5c0yhsj55NqJ4js+k8UTav/H9xj8Z7XvGCxUq0DT
+bE3txci3OE9kxJRMT6DNrqXGJyV1J23G2pyOsAWZ1SgRxSHUuPzHlqtKZFlhaxP8
+S8ySpg+kUb8OWJDZgoM5pl+z+m6Ss80zDoWo8SnTq1mt1tve1CuBAgMBAAGjQjBA
+MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLgHkXlcBvRG/XtZylomkadFK/hT
+MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQwFAAOCAgEAIZtqBSBdGBanEqT3
+Rz/NyjuujsCCztxIJXgXbODgcMTWltnZ9r96nBO7U5WS/8+S4PPFJzVXqDuiGev4
+iqME3mmL5Dw8veWv0BIb5Ylrc5tvJQJLkIKvQMKtuppgJFqBTQUYo+IzeXoLH5Pt
+7DlK9RME7I10nYEKqG/odv6LTytpEoYKNDbdgptvT+Bz3Ul/KD7JO6NXBNiT2Twp
+2xIQaOHEibgGIOcberyxk2GaGUARtWqFVwHxtlotJnMnlvm5P1vQiJ3koP26TpUJ
+g3933FEFlJ0gcXax7PqJtZwuhfG5WyRasQmr2soaB82G39tp27RIGAAtvKLEiUUj
+pQ7hRGU+isFqMB3iYPg6qocJQrmBktwliJiJ8Xw18WLK7nn4GS/+X/jbh87qqA8M
+pugLoDzga5SYnH+tBuYc6kIQX+ImFTw3OffXvO645e8D7r0i+yiGNFjEWn9hongP
+XvPKnbwbPKfILfanIhHKA9jnZwqKDss1jjQ52MjqjZ9k4DewbNfFj8GQYSbbJIwe
+SsCI3zWQzj8C9GRh3sfIB5XeMhg6j6JCQCTl1jNdfK7vsU1P1FeQNWrcrgSXSYk0
+ly4wBOeY99sLAZDBHwo/+ML+TvrbmnNzFrwFuHnYWa8G5z9nODmxfKuU4CkUpijy
+323imttUQ/hHWKNddBWcwauwxzQ=
+-----END CERTIFICATE-----
+
+# Issuer: CN=D-TRUST EV Root CA 2 2023 O=D-Trust GmbH
+# Subject: CN=D-TRUST EV Root CA 2 2023 O=D-Trust GmbH
+# Label: "D-TRUST EV Root CA 2 2023"
+# Serial: 139766439402180512324132425437959641711
+# MD5 Fingerprint: 96:b4:78:09:f0:09:cb:77:eb:bb:1b:4d:6f:36:bc:b6
+# SHA1 Fingerprint: a5:5b:d8:47:6c:8f:19:f7:4c:f4:6d:6b:b6:c2:79:82:22:df:54:8b
+# SHA256 Fingerprint: 8e:82:21:b2:e7:d4:00:78:36:a1:67:2f:0d:cc:29:9c:33:bc:07:d3:16:f1:32:fa:1a:20:6d:58:71:50:f1:ce
+-----BEGIN CERTIFICATE-----
+MIIFqTCCA5GgAwIBAgIQaSYJfoBLTKCnjHhiU19abzANBgkqhkiG9w0BAQ0FADBI
+MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlE
+LVRSVVNUIEVWIFJvb3QgQ0EgMiAyMDIzMB4XDTIzMDUwOTA5MTAzM1oXDTM4MDUw
+OTA5MTAzMlowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEi
+MCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDIgMjAyMzCCAiIwDQYJKoZIhvcN
+AQEBBQADggIPADCCAgoCggIBANiOo4mAC7JXUtypU0w3uX9jFxPvp1sjW2l1sJkK
+F8GLxNuo4MwxusLyzV3pt/gdr2rElYfXR8mV2IIEUD2BCP/kPbOx1sWy/YgJ25yE
+7CUXFId/MHibaljJtnMoPDT3mfd/06b4HEV8rSyMlD/YZxBTfiLNTiVR8CUkNRFe
+EMbsh2aJgWi6zCudR3Mfvc2RpHJqnKIbGKBv7FD0fUDCqDDPvXPIEysQEx6Lmqg6
+lHPTGGkKSv/BAQP/eX+1SH977ugpbzZMlWGG2Pmic4ruri+W7mjNPU0oQvlFKzIb
+RlUWaqZLKfm7lVa/Rh3sHZMdwGWyH6FDrlaeoLGPaxK3YG14C8qKXO0elg6DpkiV
+jTujIcSuWMYAsoS0I6SWhjW42J7YrDRJmGOVxcttSEfi8i4YHtAxq9107PncjLgc
+jmgjutDzUNzPZY9zOjLHfP7KgiJPvo5iR2blzYfi6NUPGJ/lBHJLRjwQ8kTCZFZx
+TnXonMkmdMV9WdEKWw9t/p51HBjGGjp82A0EzM23RWV6sY+4roRIPrN6TagD4uJ+
+ARZZaBhDM7DS3LAaQzXupdqpRlyuhoFBAUp0JuyfBr/CBTdkdXgpaP3F9ev+R/nk
+hbDhezGdpn9yo7nELC7MmVcOIQxFAZRl62UJxmMiCzNJkkg8/M3OsD6Onov4/knF
+NXJHAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUqvyREBuH
+kV8Wub9PS5FeAByxMoAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRCMEAwPqA8oDqG
+OGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfZXZfcm9vdF9jYV8y
+XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQCTy6UfmRHsmg1fLBWTxj++EI14
+QvBukEdHjqOSMo1wj/Zbjb6JzkcBahsgIIlbyIIQbODnmaprxiqgYzWRaoUlrRc4
+pZt+UPJ26oUFKidBK7GB0aL2QHWpDsvxVUjY7NHss+jOFKE17MJeNRqrphYBBo7q
+3C+jisosketSjl8MmxfPy3MHGcRqwnNU73xDUmPBEcrCRbH0O1P1aa4846XerOhU
+t7KR/aypH/KH5BfGSah82ApB9PI+53c0BFLd6IHyTS9URZ0V4U/M5d40VxDJI3IX
+cI1QcB9WbMy5/zpaT2N6w25lBx2Eof+pDGOJbbJAiDnXH3dotfyc1dZnaVuodNv8
+ifYbMvekJKZ2t0dT741Jj6m2g1qllpBFYfXeA08mD6iL8AOWsKwV0HFaanuU5nCT
+2vFp4LJiTZ6P/4mdm13NRemUAiKN4DV/6PEEeXFsVIP4M7kFMhtYVRFP0OUnR3Hs
+7dpn1mKmS00PaaLJvOwiS5THaJQXfuKOKD62xur1NGyfN4gHONuGcfrNlUhDbqNP
+gofXNJhuS5N5YHVpD/Aa1VP6IQzCP+k/HxiMkl14p3ZnGbuy6n/pcAlWVqOwDAst
+Nl7F6cTVg8uGF5csbBNvh1qvSaYd2804BC5f4ko1Di1L+KIkBI3Y4WNeApI02phh
+XBxvWHZks/wCuPWdCg==
+-----END CERTIFICATE-----
+
+# Issuer: CN=SwissSign RSA TLS Root CA 2022 - 1 O=SwissSign AG
+# Subject: CN=SwissSign RSA TLS Root CA 2022 - 1 O=SwissSign AG
+# Label: "SwissSign RSA TLS Root CA 2022 - 1"
+# Serial: 388078645722908516278762308316089881486363258315
+# MD5 Fingerprint: 16:2e:e4:19:76:81:85:ba:8e:91:58:f1:15:ef:72:39
+# SHA1 Fingerprint: 81:34:0a:be:4c:cd:ce:cc:e7:7d:cc:8a:d4:57:e2:45:a0:77:5d:ce
+# SHA256 Fingerprint: 19:31:44:f4:31:e0:fd:db:74:07:17:d4:de:92:6a:57:11:33:88:4b:43:60:d3:0e:27:29:13:cb:e6:60:ce:41
+-----BEGIN CERTIFICATE-----
+MIIFkzCCA3ugAwIBAgIUQ/oMX04bgBhE79G0TzUfRPSA7cswDQYJKoZIhvcNAQEL
+BQAwUTELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzErMCkGA1UE
+AxMiU3dpc3NTaWduIFJTQSBUTFMgUm9vdCBDQSAyMDIyIC0gMTAeFw0yMjA2MDgx
+MTA4MjJaFw00NzA2MDgxMTA4MjJaMFExCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxT
+d2lzc1NpZ24gQUcxKzApBgNVBAMTIlN3aXNzU2lnbiBSU0EgVExTIFJvb3QgQ0Eg
+MjAyMiAtIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDLKmjiC8NX
+vDVjvHClO/OMPE5Xlm7DTjak9gLKHqquuN6orx122ro10JFwB9+zBvKK8i5VUXu7
+LCTLf5ImgKO0lPaCoaTo+nUdWfMHamFk4saMla+ju45vVs9xzF6BYQ1t8qsCLqSX
+5XH8irCRIFucdFJtrhUnWXjyCcplDn/L9Ovn3KlMd/YrFgSVrpxxpT8q2kFC5zyE
+EPThPYxr4iuRR1VPuFa+Rd4iUU1OKNlfGUEGjw5NBuBwQCMBauTLE5tzrE0USJIt
+/m2n+IdreXXhvhCxqohAWVTXz8TQm0SzOGlkjIHRI36qOTw7D59Ke4LKa2/KIj4x
+0LDQKhySio/YGZxH5D4MucLNvkEM+KRHBdvBFzA4OmnczcNpI/2aDwLOEGrOyvi5
+KaM2iYauC8BPY7kGWUleDsFpswrzd34unYyzJ5jSmY0lpx+Gs6ZUcDj8fV3oT4MM
+0ZPlEuRU2j7yrTrePjxF8CgPBrnh25d7mUWe3f6VWQQvdT/TromZhqwUtKiE+shd
+OxtYk8EXlFXIC+OCeYSf8wCENO7cMdWP8vpPlkwGqnj73mSiI80fPsWMvDdUDrta
+clXvyFu1cvh43zcgTFeRc5JzrBh3Q4IgaezprClG5QtO+DdziZaKHG29777YtvTK
+wP1H8K4LWCDFyB02rpeNUIMmJCn3nTsPBQIDAQABo2MwYTAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBRvjmKLk0Ow4UD2p8P98Q+4
+DxU4pTAdBgNVHQ4EFgQUb45ii5NDsOFA9qfD/fEPuA8VOKUwDQYJKoZIhvcNAQEL
+BQADggIBAKwsKUF9+lz1GpUYvyypiqkkVHX1uECry6gkUSsYP2OprphWKwVDIqO3
+10aewCoSPY6WlkDfDDOLazeROpW7OSltwAJsipQLBwJNGD77+3v1dj2b9l4wBlgz
+Hqp41eZUBDqyggmNzhYzWUUo8aWjlw5DI/0LIICQ/+Mmz7hkkeUFjxOgdg3XNwwQ
+iJb0Pr6VvfHDffCjw3lHC1ySFWPtUnWK50Zpy1FVCypM9fJkT6lc/2cyjlUtMoIc
+gC9qkfjLvH4YoiaoLqNTKIftV+Vlek4ASltOU8liNr3CjlvrzG4ngRhZi0Rjn9UM
+ZfQpZX+RLOV/fuiJz48gy20HQhFRJjKKLjpHE7iNvUcNCfAWpO2Whi4Z2L6MOuhF
+LhG6rlrnub+xzI/goP+4s9GFe3lmozm1O2bYQL7Pt2eLSMkZJVX8vY3PXtpOpvJp
+zv1/THfQwUY1mFwjmwJFQ5Ra3bxHrSL+ul4vkSkphnsh3m5kt8sNjzdbowhq6/Td
+Ao9QAwKxuDdollDruF/UKIqlIgyKhPBZLtU30WHlQnNYKoH3dtvi4k0NX/a3vgW0
+rk4N3hY9A4GzJl5LuEsAz/+MF7psYC0nhzck5npgL7XTgwSqT0N1osGDsieYK7EO
+gLrAhV5Cud+xYJHT6xh+cHiudoO+cVrQkOPKwRYlZ0rwtnu64ZzZ
+-----END CERTIFICATE-----
+
+# Issuer: CN=OISTE Server Root ECC G1 O=OISTE Foundation
+# Subject: CN=OISTE Server Root ECC G1 O=OISTE Foundation
+# Label: "OISTE Server Root ECC G1"
+# Serial: 47819833811561661340092227008453318557
+# MD5 Fingerprint: 42:a7:d2:35:ae:02:92:db:19:76:08:de:2f:05:b4:d4
+# SHA1 Fingerprint: 3b:f6:8b:09:ae:2a:92:7b:ba:e3:8d:3f:11:95:d9:e6:44:0c:45:e2
+# SHA256 Fingerprint: ee:c9:97:c0:c3:0f:21:6f:7e:3b:8b:30:7d:2b:ae:42:41:2d:75:3f:c8:21:9d:af:d1:52:0b:25:72:85:0f:49
+-----BEGIN CERTIFICATE-----
+MIICNTCCAbqgAwIBAgIQI/nD1jWvjyhLH/BU6n6XnTAKBggqhkjOPQQDAzBLMQsw
+CQYDVQQGEwJDSDEZMBcGA1UECgwQT0lTVEUgRm91bmRhdGlvbjEhMB8GA1UEAwwY
+T0lTVEUgU2VydmVyIFJvb3QgRUNDIEcxMB4XDTIzMDUzMTE0NDIyOFoXDTQ4MDUy
+NDE0NDIyN1owSzELMAkGA1UEBhMCQ0gxGTAXBgNVBAoMEE9JU1RFIEZvdW5kYXRp
+b24xITAfBgNVBAMMGE9JU1RFIFNlcnZlciBSb290IEVDQyBHMTB2MBAGByqGSM49
+AgEGBSuBBAAiA2IABBcv+hK8rBjzCvRE1nZCnrPoH7d5qVi2+GXROiFPqOujvqQy
+cvO2Ackr/XeFblPdreqqLiWStukhEaivtUwL85Zgmjvn6hp4LrQ95SjeHIC6XG4N
+2xml4z+cKrhAS93mT6NjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBQ3
+TYhlz/w9itWj8UnATgwQb0K0nDAdBgNVHQ4EFgQUN02IZc/8PYrVo/FJwE4MEG9C
+tJwwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2kAMGYCMQCpKjAd0MKfkFFR
+QD6VVCHNFmb3U2wIFjnQEnx/Yxvf4zgAOdktUyBFCxxgZzFDJe0CMQCSia7pXGKD
+YmH5LVerVrkR3SW+ak5KGoJr3M/TvEqzPNcum9v4KGm8ay3sMaE641c=
+-----END CERTIFICATE-----
+
+# Issuer: CN=OISTE Server Root RSA G1 O=OISTE Foundation
+# Subject: CN=OISTE Server Root RSA G1 O=OISTE Foundation
+# Label: "OISTE Server Root RSA G1"
+# Serial: 113845518112613905024960613408179309848
+# MD5 Fingerprint: 23:a7:9e:d4:70:b8:b9:14:57:41:8a:7e:44:59:e2:68
+# SHA1 Fingerprint: f7:00:34:25:94:88:68:31:e4:34:87:3f:70:fe:86:b3:86:9f:f0:6e
+# SHA256 Fingerprint: 9a:e3:62:32:a5:18:9f:fd:db:35:3d:fd:26:52:0c:01:53:95:d2:27:77:da:c5:9d:b5:7b:98:c0:89:a6:51:e6
+-----BEGIN CERTIFICATE-----
+MIIFgzCCA2ugAwIBAgIQVaXZZ5Qoxu0M+ifdWwFNGDANBgkqhkiG9w0BAQwFADBL
+MQswCQYDVQQGEwJDSDEZMBcGA1UECgwQT0lTVEUgRm91bmRhdGlvbjEhMB8GA1UE
+AwwYT0lTVEUgU2VydmVyIFJvb3QgUlNBIEcxMB4XDTIzMDUzMTE0MzcxNloXDTQ4
+MDUyNDE0MzcxNVowSzELMAkGA1UEBhMCQ0gxGTAXBgNVBAoMEE9JU1RFIEZvdW5k
+YXRpb24xITAfBgNVBAMMGE9JU1RFIFNlcnZlciBSb290IFJTQSBHMTCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBAKqu9KuCz/vlNwvn1ZatkOhLKdxVYOPM
+vLO8LZK55KN68YG0nnJyQ98/qwsmtO57Gmn7KNByXEptaZnwYx4M0rH/1ow00O7b
+rEi56rAUjtgHqSSY3ekJvqgiG1k50SeH3BzN+Puz6+mTeO0Pzjd8JnduodgsIUzk
+ik/HEzxux9UTl7Ko2yRpg1bTacuCErudG/L4NPKYKyqOBGf244ehHa1uzjZ0Dl4z
+O8vbUZeUapU8zhhabkvG/AePLhq5SvdkNCncpo1Q4Y2LS+VIG24ugBA/5J8bZT8R
+tOpXaZ+0AOuFJJkk9SGdl6r7NH8CaxWQrbueWhl/pIzY+m0o/DjH40ytas7ZTpOS
+jswMZ78LS5bOZmdTaMsXEY5Z96ycG7mOaES3GK/m5Q9l3JUJsJMStR8+lKXHiHUh
+sd4JJCpM4rzsTGdHwimIuQq6+cF0zowYJmXa92/GjHtoXAvuY8BeS/FOzJ8vD+Ho
+mnqT8eDI278n5mUpezbgMxVz8p1rhAhoKzYHKyfMeNhqhw5HdPSqoBNdZH702xSu
++zrkL8Fl47l6QGzwBrd7KJvX4V84c5Ss2XCTLdyEr0YconosP4EmQufU2MVshGYR
+i3drVByjtdgQ8K4p92cIiBdcuJd5z+orKu5YM+Vt6SmqZQENghPsJQtdLEByFSnT
+kCz3GkPVavBpAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU
+8snBDw1jALvsRQ5KH7WxszbNDo0wHQYDVR0OBBYEFPLJwQ8NYwC77EUOSh+1sbM2
+zQ6NMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQwFAAOCAgEANGd5sjrG5T33
+I3K5Ce+SrScfoE4KsvXaFwyihdJ+klH9FWXXXGtkFu6KRcoMQzZENdl//nk6HOjG
+5D1rd9QhEOP28yBOqb6J8xycqd+8MDoX0TJD0KqKchxRKEzdNsjkLWd9kYccnbz8
+qyiWXmFcuCIzGEgWUOrKL+mlSdx/PKQZvDatkuK59EvV6wit53j+F8Bdh3foZ3dP
+AGav9LEDOr4SfEE15fSmG0eLy3n31r8Xbk5l8PjaV8GUgeV6Vg27Rn9vkf195hfk
+gSe7BYhW3SCl95gtkRlpMV+bMPKZrXJAlszYd2abtNUOshD+FKrDgHGdPY3ofRRs
+YWSGRqbXVMW215AWRqWFyp464+YTFrYVI8ypKVL9AMb2kI5Wj4kI3Zaq5tNqqYY1
+9tVFeEJKRvwDyF7YZvZFZSS0vod7VSCd9521Kvy5YhnLbDuv0204bKt7ph6N/Ome
+/msVuduCmsuY33OhkKCgxeDoAaijFJzIwZqsFVAzje18KotzlUBDJvyBpCpfOZC3
+J8tRd/iWkx7P8nd9H0aTolkelUTFLXVksNb54Dxp6gS1HAviRkRNQzuXSXERvSS2
+wq1yVAb+axj5d9spLFKebXd7Yv0PTY6YMjAwcRLWJTXjn/hvnLXrahut6hDTlhZy
+BiElxky8j3C7DOReIoMt0r7+hVu05L0=
+-----END CERTIFICATE-----
+
+# Issuer: CN=e-Szigno TLS Root CA 2023 O=Microsec Ltd.
+# Subject: CN=e-Szigno TLS Root CA 2023 O=Microsec Ltd.
+# Label: "e-Szigno TLS Root CA 2023"
+# Serial: 71934828665710877219916191754
+# MD5 Fingerprint: 6a:e9:99:74:a5:da:5e:f1:d9:2e:f2:c8:d1:86:8b:71
+# SHA1 Fingerprint: 6f:9a:d5:d5:df:e8:2c:eb:be:37:07:ee:4f:4f:52:58:29:41:d1:fe
+# SHA256 Fingerprint: b4:91:41:50:2d:00:66:3d:74:0f:2e:7e:c3:40:c5:28:00:96:26:66:12:1a:36:d0:9c:f7:dd:2b:90:38:4f:b4
+-----BEGIN CERTIFICATE-----
+MIICzzCCAjGgAwIBAgINAOhvGHvWOWuYSkmYCjAKBggqhkjOPQQDBDB1MQswCQYD
+VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0
+ZC4xFzAVBgNVBGEMDlZBVEhVLTIzNTg0NDk3MSIwIAYDVQQDDBllLVN6aWdubyBU
+TFMgUm9vdCBDQSAyMDIzMB4XDTIzMDcxNzE0MDAwMFoXDTM4MDcxNzE0MDAwMFow
+dTELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRYwFAYDVQQKDA1NaWNy
+b3NlYyBMdGQuMRcwFQYDVQRhDA5WQVRIVS0yMzU4NDQ5NzEiMCAGA1UEAwwZZS1T
+emlnbm8gVExTIFJvb3QgQ0EgMjAyMzCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAE
+AGgP36J8PKp0iGEKjcJMpQEiFNT3YHdCnAo4YKGMZz6zY+n6kbCLS+Y53wLCMAFS
+AL/fjO1ZrTJlqwlZULUZwmgcAOAFX9pQJhzDrAQixTpN7+lXWDajwRlTEArRzT/v
+SzUaQ49CE0y5LBqcvjC2xN7cS53kpDzLLtmt3999Cd8ukv+ho2MwYTAPBgNVHRMB
+Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUWYQCYlpGePVd3I8K
+ECgj3NXW+0UwHwYDVR0jBBgwFoAUWYQCYlpGePVd3I8KECgj3NXW+0UwCgYIKoZI
+zj0EAwQDgYsAMIGHAkIBLdqu9S54tma4n7Zwf2Z0z+yOfP7AAXmazlIC58PRDHpt
+y7Ve7hekm9sEdu4pKeiv+62sUvTXK9Z3hBC9xdIoaDQCQTV2WnXzkoYI9bIeCvZl
+C9p2x1L/Cx6AcCIwwzPbGO2E14vs7dOoY4G1VnxHx1YwlGhza9IuqbnZLBwpvQy6
+uWWL
+-----END CERTIFICATE-----
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/certifi/core.py b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/core.py
new file mode 100644
index 0000000..2f2f7e0
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/core.py
@@ -0,0 +1,83 @@
+"""
+certifi.py
+~~~~~~~~~~
+
+This module returns the installation location of cacert.pem or its contents.
+"""
+import sys
+import atexit
+
+def exit_cacert_ctx() -> None:
+    _CACERT_CTX.__exit__(None, None, None)  # type: ignore[union-attr]
+
+
+if sys.version_info >= (3, 11):
+
+    from importlib.resources import as_file, files
+
+    _CACERT_CTX = None
+    _CACERT_PATH = None
+
+    def where() -> str:
+        # This is slightly terrible, but we want to delay extracting the file
+        # in cases where we're inside of a zipimport situation until someone
+        # actually calls where(), but we don't want to re-extract the file
+        # on every call of where(), so we'll do it once then store it in a
+        # global variable.
+        global _CACERT_CTX
+        global _CACERT_PATH
+        if _CACERT_PATH is None:
+            # This is slightly janky, the importlib.resources API wants you to
+            # manage the cleanup of this file, so it doesn't actually return a
+            # path, it returns a context manager that will give you the path
+            # when you enter it and will do any cleanup when you leave it. In
+            # the common case of not needing a temporary file, it will just
+            # return the file system location and the __exit__() is a no-op.
+            #
+            # We also have to hold onto the actual context manager, because
+            # it will do the cleanup whenever it gets garbage collected, so
+            # we will also store that at the global level as well.
+            _CACERT_CTX = as_file(files("pip._vendor.certifi").joinpath("cacert.pem"))
+            _CACERT_PATH = str(_CACERT_CTX.__enter__())
+            atexit.register(exit_cacert_ctx)
+
+        return _CACERT_PATH
+
+    def contents() -> str:
+        return files("pip._vendor.certifi").joinpath("cacert.pem").read_text(encoding="ascii")
+
+else:
+
+    from importlib.resources import path as get_path, read_text
+
+    _CACERT_CTX = None
+    _CACERT_PATH = None
+
+    def where() -> str:
+        # This is slightly terrible, but we want to delay extracting the
+        # file in cases where we're inside of a zipimport situation until
+        # someone actually calls where(), but we don't want to re-extract
+        # the file on every call of where(), so we'll do it once then store
+        # it in a global variable.
+        global _CACERT_CTX
+        global _CACERT_PATH
+        if _CACERT_PATH is None:
+            # This is slightly janky, the importlib.resources API wants you
+            # to manage the cleanup of this file, so it doesn't actually
+            # return a path, it returns a context manager that will give
+            # you the path when you enter it and will do any cleanup when
+            # you leave it. In the common case of not needing a temporary
+            # file, it will just return the file system location and the
+            # __exit__() is a no-op.
+            #
+            # We also have to hold onto the actual context manager, because
+            # it will do the cleanup whenever it gets garbage collected, so
+            # we will also store that at the global level as well.
+            _CACERT_CTX = get_path("pip._vendor.certifi", "cacert.pem")
+            _CACERT_PATH = str(_CACERT_CTX.__enter__())
+            atexit.register(exit_cacert_ctx)
+
+        return _CACERT_PATH
+
+    def contents() -> str:
+        return read_text("pip._vendor.certifi", "cacert.pem", encoding="ascii")
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/certifi/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/certifi/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/LICENSE.txt b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/LICENSE.txt
new file mode 100644
index 0000000..c31ac56
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/LICENSE.txt
@@ -0,0 +1,284 @@
+A. HISTORY OF THE SOFTWARE
+==========================
+
+Python was created in the early 1990s by Guido van Rossum at Stichting
+Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
+as a successor of a language called ABC.  Guido remains Python's
+principal author, although it includes many contributions from others.
+
+In 1995, Guido continued his work on Python at the Corporation for
+National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
+in Reston, Virginia where he released several versions of the
+software.
+
+In May 2000, Guido and the Python core development team moved to
+BeOpen.com to form the BeOpen PythonLabs team.  In October of the same
+year, the PythonLabs team moved to Digital Creations (now Zope
+Corporation, see http://www.zope.com).  In 2001, the Python Software
+Foundation (PSF, see http://www.python.org/psf/) was formed, a
+non-profit organization created specifically to own Python-related
+Intellectual Property.  Zope Corporation is a sponsoring member of
+the PSF.
+
+All Python releases are Open Source (see http://www.opensource.org for
+the Open Source Definition).  Historically, most, but not all, Python
+releases have also been GPL-compatible; the table below summarizes
+the various releases.
+
+    Release         Derived     Year        Owner       GPL-
+                    from                                compatible? (1)
+
+    0.9.0 thru 1.2              1991-1995   CWI         yes
+    1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes
+    1.6             1.5.2       2000        CNRI        no
+    2.0             1.6         2000        BeOpen.com  no
+    1.6.1           1.6         2001        CNRI        yes (2)
+    2.1             2.0+1.6.1   2001        PSF         no
+    2.0.1           2.0+1.6.1   2001        PSF         yes
+    2.1.1           2.1+2.0.1   2001        PSF         yes
+    2.2             2.1.1       2001        PSF         yes
+    2.1.2           2.1.1       2002        PSF         yes
+    2.1.3           2.1.2       2002        PSF         yes
+    2.2.1           2.2         2002        PSF         yes
+    2.2.2           2.2.1       2002        PSF         yes
+    2.2.3           2.2.2       2003        PSF         yes
+    2.3             2.2.2       2002-2003   PSF         yes
+    2.3.1           2.3         2002-2003   PSF         yes
+    2.3.2           2.3.1       2002-2003   PSF         yes
+    2.3.3           2.3.2       2002-2003   PSF         yes
+    2.3.4           2.3.3       2004        PSF         yes
+    2.3.5           2.3.4       2005        PSF         yes
+    2.4             2.3         2004        PSF         yes
+    2.4.1           2.4         2005        PSF         yes
+    2.4.2           2.4.1       2005        PSF         yes
+    2.4.3           2.4.2       2006        PSF         yes
+    2.4.4           2.4.3       2006        PSF         yes
+    2.5             2.4         2006        PSF         yes
+    2.5.1           2.5         2007        PSF         yes
+    2.5.2           2.5.1       2008        PSF         yes
+    2.5.3           2.5.2       2008        PSF         yes
+    2.6             2.5         2008        PSF         yes
+    2.6.1           2.6         2008        PSF         yes
+    2.6.2           2.6.1       2009        PSF         yes
+    2.6.3           2.6.2       2009        PSF         yes
+    2.6.4           2.6.3       2009        PSF         yes
+    2.6.5           2.6.4       2010        PSF         yes
+    3.0             2.6         2008        PSF         yes
+    3.0.1           3.0         2009        PSF         yes
+    3.1             3.0.1       2009        PSF         yes
+    3.1.1           3.1         2009        PSF         yes
+    3.1.2           3.1         2010        PSF         yes
+    3.2             3.1         2010        PSF         yes
+
+Footnotes:
+
+(1) GPL-compatible doesn't mean that we're distributing Python under
+    the GPL.  All Python licenses, unlike the GPL, let you distribute
+    a modified version without making your changes open source.  The
+    GPL-compatible licenses make it possible to combine Python with
+    other software that is released under the GPL; the others don't.
+
+(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
+    because its license has a choice of law clause.  According to
+    CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
+    is "not incompatible" with the GPL.
+
+Thanks to the many outside volunteers who have worked under Guido's
+direction to make these releases possible.
+
+
+B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
+===============================================================
+
+PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+--------------------------------------------
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation
+("PSF"), and the Individual or Organization ("Licensee") accessing and
+otherwise using this software ("Python") in source or binary form and
+its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF hereby
+grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+analyze, test, perform and/or display publicly, prepare derivative works,
+distribute, and otherwise use Python alone or in any derivative version,
+provided, however, that PSF's License Agreement and PSF's notice of copyright,
+i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+Python Software Foundation; All Rights Reserved" are retained in Python alone or
+in any derivative version prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python.
+
+4. PSF is making Python available to Licensee on an "AS IS"
+basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between PSF and
+Licensee.  This License Agreement does not grant permission to use PSF
+trademarks or trade name in a trademark sense to endorse or promote
+products or services of Licensee, or any third party.
+
+8. By copying, installing or otherwise using Python, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
+-------------------------------------------
+
+BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
+
+1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
+office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
+Individual or Organization ("Licensee") accessing and otherwise using
+this software in source or binary form and its associated
+documentation ("the Software").
+
+2. Subject to the terms and conditions of this BeOpen Python License
+Agreement, BeOpen hereby grants Licensee a non-exclusive,
+royalty-free, world-wide license to reproduce, analyze, test, perform
+and/or display publicly, prepare derivative works, distribute, and
+otherwise use the Software alone or in any derivative version,
+provided, however, that the BeOpen Python License is retained in the
+Software, alone or in any derivative version prepared by Licensee.
+
+3. BeOpen is making the Software available to Licensee on an "AS IS"
+basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
+SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
+AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
+DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+5. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+6. This License Agreement shall be governed by and interpreted in all
+respects by the law of the State of California, excluding conflict of
+law provisions.  Nothing in this License Agreement shall be deemed to
+create any relationship of agency, partnership, or joint venture
+between BeOpen and Licensee.  This License Agreement does not grant
+permission to use BeOpen trademarks or trade names in a trademark
+sense to endorse or promote products or services of Licensee, or any
+third party.  As an exception, the "BeOpen Python" logos available at
+http://www.pythonlabs.com/logos.html may be used according to the
+permissions granted on that web page.
+
+7. By copying, installing or otherwise using the software, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
+---------------------------------------
+
+1. This LICENSE AGREEMENT is between the Corporation for National
+Research Initiatives, having an office at 1895 Preston White Drive,
+Reston, VA 20191 ("CNRI"), and the Individual or Organization
+("Licensee") accessing and otherwise using Python 1.6.1 software in
+source or binary form and its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, CNRI
+hereby grants Licensee a nonexclusive, royalty-free, world-wide
+license to reproduce, analyze, test, perform and/or display publicly,
+prepare derivative works, distribute, and otherwise use Python 1.6.1
+alone or in any derivative version, provided, however, that CNRI's
+License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
+1995-2001 Corporation for National Research Initiatives; All Rights
+Reserved" are retained in Python 1.6.1 alone or in any derivative
+version prepared by Licensee.  Alternately, in lieu of CNRI's License
+Agreement, Licensee may substitute the following text (omitting the
+quotes): "Python 1.6.1 is made available subject to the terms and
+conditions in CNRI's License Agreement.  This Agreement together with
+Python 1.6.1 may be located on the Internet using the following
+unique, persistent identifier (known as a handle): 1895.22/1013.  This
+Agreement may also be obtained from a proxy server on the Internet
+using the following URL: http://hdl.handle.net/1895.22/1013".
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python 1.6.1 or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python 1.6.1.
+
+4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
+basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. This License Agreement shall be governed by the federal
+intellectual property law of the United States, including without
+limitation the federal copyright law, and, to the extent such
+U.S. federal law does not apply, by the law of the Commonwealth of
+Virginia, excluding Virginia's conflict of law provisions.
+Notwithstanding the foregoing, with regard to derivative works based
+on Python 1.6.1 that incorporate non-separable material that was
+previously distributed under the GNU General Public License (GPL), the
+law of the Commonwealth of Virginia shall govern this License
+Agreement only as to issues arising under or with respect to
+Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this
+License Agreement shall be deemed to create any relationship of
+agency, partnership, or joint venture between CNRI and Licensee.  This
+License Agreement does not grant permission to use CNRI trademarks or
+trade name in a trademark sense to endorse or promote products or
+services of Licensee, or any third party.
+
+8. By clicking on the "ACCEPT" button where indicated, or by copying,
+installing or otherwise using Python 1.6.1, Licensee agrees to be
+bound by the terms and conditions of this License Agreement.
+
+        ACCEPT
+
+
+CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
+--------------------------------------------------
+
+Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
+The Netherlands.  All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__init__.py
new file mode 100644
index 0000000..3ac0c33
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__init__.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012-2024 Vinay Sajip.
+# Licensed to the Python Software Foundation under a contributor agreement.
+# See LICENSE.txt and CONTRIBUTORS.txt.
+#
+import logging
+
+__version__ = '0.4.2'
+
+
+class DistlibException(Exception):
+    pass
+
+
+try:
+    from logging import NullHandler
+except ImportError:  # pragma: no cover
+
+    class NullHandler(logging.Handler):
+
+        def handle(self, record):
+            pass
+
+        def emit(self, record):
+            pass
+
+        def createLock(self):
+            self.lock = None
+
+
+logger = logging.getLogger(__name__)
+logger.addHandler(NullHandler())
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..6f0f425
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc
new file mode 100644
index 0000000..6cdaa46
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc
new file mode 100644
index 0000000..695704f
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-312.pyc
new file mode 100644
index 0000000..3db3684
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-312.pyc
new file mode 100644
index 0000000..0044f2a
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/compat.py b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/compat.py
new file mode 100644
index 0000000..b9c0fec
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/compat.py
@@ -0,0 +1,1107 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2013-2026 Vinay Sajip.
+# Licensed to the Python Software Foundation under a contributor agreement.
+# See LICENSE.txt and CONTRIBUTORS.txt.
+#
+from __future__ import absolute_import
+
+import os
+import re
+import shutil
+import sys
+
+try:
+    import ssl
+except ImportError:  # pragma: no cover
+    ssl = None
+
+if sys.version_info[0] < 3:  # pragma: no cover
+    from StringIO import StringIO
+    string_types = basestring,
+    text_type = unicode
+    from types import FileType as file_type
+    import __builtin__ as builtins
+    import ConfigParser as configparser
+    from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit
+    from urllib import (urlretrieve, quote as _quote, unquote, url2pathname, pathname2url, ContentTooShortError,
+                        splittype)
+
+    def quote(s):
+        if isinstance(s, unicode):
+            s = s.encode('utf-8')
+        return _quote(s)
+
+    import urllib2
+    from urllib2 import (Request, urlopen, URLError, HTTPError, HTTPBasicAuthHandler, HTTPPasswordMgr, HTTPHandler,
+                         HTTPRedirectHandler, build_opener)
+    if ssl:
+        from urllib2 import HTTPSHandler
+    import httplib
+    import xmlrpclib
+    import Queue as queue
+    from HTMLParser import HTMLParser
+    import htmlentitydefs
+    raw_input = raw_input
+    from itertools import ifilter as filter
+    from itertools import ifilterfalse as filterfalse
+
+    # Leaving this around for now, in case it needs resurrecting in some way
+    # _userprog = None
+    # def splituser(host):
+    # """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'."""
+    # global _userprog
+    # if _userprog is None:
+    # import re
+    # _userprog = re.compile('^(.*)@(.*)$')
+
+    # match = _userprog.match(host)
+    # if match: return match.group(1, 2)
+    # return None, host
+
+else:  # pragma: no cover
+    from io import StringIO
+    string_types = str,
+    text_type = str
+    from io import TextIOWrapper as file_type
+    import builtins
+    import configparser
+    from urllib.parse import (urlparse, urlunparse, urljoin, quote, unquote, urlsplit, urlunsplit, splittype)
+    from urllib.request import (urlopen, urlretrieve, Request, url2pathname, pathname2url, HTTPBasicAuthHandler,
+                                HTTPPasswordMgr, HTTPHandler, HTTPRedirectHandler, build_opener)
+    if ssl:
+        from urllib.request import HTTPSHandler
+    from urllib.error import HTTPError, URLError, ContentTooShortError
+    import http.client as httplib
+    import urllib.request as urllib2
+    import xmlrpc.client as xmlrpclib
+    import queue
+    from html.parser import HTMLParser
+    import html.entities as htmlentitydefs
+    raw_input = input
+    from itertools import filterfalse
+    filter = filter
+
+try:
+    from ssl import match_hostname, CertificateError
+except ImportError:  # pragma: no cover
+
+    class CertificateError(ValueError):
+        pass
+
+    def _dnsname_match(dn, hostname, max_wildcards=1):
+        """Matching according to RFC 6125, section 6.4.3
+
+        http://tools.ietf.org/html/rfc6125#section-6.4.3
+        """
+        pats = []
+        if not dn:
+            return False
+
+        parts = dn.split('.')
+        leftmost, remainder = parts[0], parts[1:]
+
+        wildcards = leftmost.count('*')
+        if wildcards > max_wildcards:
+            # Issue #17980: avoid denials of service by refusing more
+            # than one wildcard per fragment.  A survey of established
+            # policy among SSL implementations showed it to be a
+            # reasonable choice.
+            raise CertificateError('Too many wildcards in certificate DNS name: %r' % dn)
+
+        # speed up common case w/o wildcards
+        if not wildcards:
+            return dn.lower() == hostname.lower()
+
+        # RFC 6125, section 6.4.3, subitem 1.
+        # The client SHOULD NOT attempt to match a presented identifier in which
+        # the wildcard character comprises a label other than the left-most label.
+        if leftmost == '*':
+            # When '*' is a fragment by itself, it matches a non-empty dotless
+            # fragment.
+            pats.append('[^.]+')
+        elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
+            # RFC 6125, section 6.4.3, subitem 3.
+            # The client SHOULD NOT attempt to match a presented identifier
+            # where the wildcard character is embedded within an A-label or
+            # U-label of an internationalized domain name.
+            pats.append(re.escape(leftmost))
+        else:
+            # Otherwise, '*' matches any dotless string, e.g. www*
+            pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
+
+        # add the remaining fragments, ignore any wildcards
+        for frag in remainder:
+            pats.append(re.escape(frag))
+
+        pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
+        return pat.match(hostname)
+
+    def match_hostname(cert, hostname):
+        """Verify that *cert* (in decoded format as returned by
+        SSLSocket.getpeercert()) matches the *hostname*.  RFC 2818 and RFC 6125
+        rules are followed, but IP addresses are not accepted for *hostname*.
+
+        CertificateError is raised on failure. On success, the function
+        returns nothing.
+        """
+        if not cert:
+            raise ValueError("empty or no certificate, match_hostname needs a "
+                             "SSL socket or SSL context with either "
+                             "CERT_OPTIONAL or CERT_REQUIRED")
+        dnsnames = []
+        san = cert.get('subjectAltName', ())
+        for key, value in san:
+            if key == 'DNS':
+                if _dnsname_match(value, hostname):
+                    return
+                dnsnames.append(value)
+        if not dnsnames:
+            # The subject is only checked when there is no dNSName entry
+            # in subjectAltName
+            for sub in cert.get('subject', ()):
+                for key, value in sub:
+                    # XXX according to RFC 2818, the most specific Common Name
+                    # must be used.
+                    if key == 'commonName':
+                        if _dnsname_match(value, hostname):
+                            return
+                        dnsnames.append(value)
+        if len(dnsnames) > 1:
+            raise CertificateError("hostname %r "
+                                   "doesn't match either of %s" % (hostname, ', '.join(map(repr, dnsnames))))
+        elif len(dnsnames) == 1:
+            raise CertificateError("hostname %r "
+                                   "doesn't match %r" % (hostname, dnsnames[0]))
+        else:
+            raise CertificateError("no appropriate commonName or "
+                                   "subjectAltName fields were found")
+
+
+try:
+    from types import SimpleNamespace as Container
+except ImportError:  # pragma: no cover
+
+    class Container(object):
+        """
+        A generic container for when multiple values need to be returned
+        """
+
+        def __init__(self, **kwargs):
+            self.__dict__.update(kwargs)
+
+
+try:
+    from shutil import which
+except ImportError:  # pragma: no cover
+    # Implementation from Python 3.3
+    def which(cmd, mode=os.F_OK | os.X_OK, path=None):
+        """Given a command, mode, and a PATH string, return the path which
+        conforms to the given mode on the PATH, or None if there is no such
+        file.
+
+        `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
+        of os.environ.get("PATH"), or can be overridden with a custom search
+        path.
+
+        """
+
+        # Check that a given file can be accessed with the correct mode.
+        # Additionally check that `file` is not a directory, as on Windows
+        # directories pass the os.access check.
+        def _access_check(fn, mode):
+            return (os.path.exists(fn) and os.access(fn, mode) and not os.path.isdir(fn))
+
+        # If we're given a path with a directory part, look it up directly rather
+        # than referring to PATH directories. This includes checking relative to the
+        # current directory, e.g. ./script
+        if os.path.dirname(cmd):
+            if _access_check(cmd, mode):
+                return cmd
+            return None
+
+        if path is None:
+            path = os.environ.get("PATH", os.defpath)
+        if not path:
+            return None
+        path = path.split(os.pathsep)
+
+        if sys.platform == "win32":
+            # The current directory takes precedence on Windows.
+            if os.curdir not in path:
+                path.insert(0, os.curdir)
+
+            # PATHEXT is necessary to check on Windows.
+            pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
+            # See if the given file matches any of the expected path extensions.
+            # This will allow us to short circuit when given "python.exe".
+            # If it does match, only test that one, otherwise we have to try
+            # others.
+            if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
+                files = [cmd]
+            else:
+                files = [cmd + ext for ext in pathext]
+        else:
+            # On other platforms you don't have things like PATHEXT to tell you
+            # what file suffixes are executable, so just pass on cmd as-is.
+            files = [cmd]
+
+        seen = set()
+        for dir in path:
+            normdir = os.path.normcase(dir)
+            if normdir not in seen:
+                seen.add(normdir)
+                for thefile in files:
+                    name = os.path.join(dir, thefile)
+                    if _access_check(name, mode):
+                        return name
+        return None
+
+
+# ZipFile is a context manager in 2.7, but not in 2.6
+
+from zipfile import ZipFile as BaseZipFile
+
+if hasattr(BaseZipFile, '__enter__'):  # pragma: no cover
+    ZipFile = BaseZipFile
+else:  # pragma: no cover
+    from zipfile import ZipExtFile as BaseZipExtFile
+
+    class ZipExtFile(BaseZipExtFile):
+
+        def __init__(self, base):
+            self.__dict__.update(base.__dict__)
+
+        def __enter__(self):
+            return self
+
+        def __exit__(self, *exc_info):
+            self.close()
+            # return None, so if an exception occurred, it will propagate
+
+    class ZipFile(BaseZipFile):
+
+        def __enter__(self):
+            return self
+
+        def __exit__(self, *exc_info):
+            self.close()
+            # return None, so if an exception occurred, it will propagate
+
+        def open(self, *args, **kwargs):
+            base = BaseZipFile.open(self, *args, **kwargs)
+            return ZipExtFile(base)
+
+
+try:
+    from platform import python_implementation
+except ImportError:  # pragma: no cover
+
+    def python_implementation():
+        """Return a string identifying the Python implementation."""
+        if 'PyPy' in sys.version:
+            return 'PyPy'
+        if os.name == 'java':
+            return 'Jython'
+        if sys.version.startswith('IronPython'):
+            return 'IronPython'
+        return 'CPython'
+
+
+import sysconfig
+
+try:
+    callable = callable
+except NameError:  # pragma: no cover
+    from collections.abc import Callable
+
+    def callable(obj):
+        return isinstance(obj, Callable)
+
+
+try:
+    fsencode = os.fsencode
+    fsdecode = os.fsdecode
+except AttributeError:  # pragma: no cover
+    # Issue #99: on some systems (e.g. containerised),
+    # sys.getfilesystemencoding() returns None, and we need a real value,
+    # so fall back to utf-8. From the CPython 2.7 docs relating to Unix and
+    # sys.getfilesystemencoding(): the return value is "the user’s preference
+    # according to the result of nl_langinfo(CODESET), or None if the
+    # nl_langinfo(CODESET) failed."
+    _fsencoding = sys.getfilesystemencoding() or 'utf-8'
+    if _fsencoding == 'mbcs':
+        _fserrors = 'strict'
+    else:
+        _fserrors = 'surrogateescape'
+
+    def fsencode(filename):
+        if isinstance(filename, bytes):
+            return filename
+        elif isinstance(filename, text_type):
+            return filename.encode(_fsencoding, _fserrors)
+        else:
+            raise TypeError("expect bytes or str, not %s" % type(filename).__name__)
+
+    def fsdecode(filename):
+        if isinstance(filename, text_type):
+            return filename
+        elif isinstance(filename, bytes):
+            return filename.decode(_fsencoding, _fserrors)
+        else:
+            raise TypeError("expect bytes or str, not %s" % type(filename).__name__)
+
+
+try:
+    from tokenize import detect_encoding
+except ImportError:  # pragma: no cover
+    from codecs import BOM_UTF8, lookup
+
+    cookie_re = re.compile(r"coding[:=]\s*([-\w.]+)")
+
+    def _get_normal_name(orig_enc):
+        """Imitates get_normal_name in tokenizer.c."""
+        # Only care about the first 12 characters.
+        enc = orig_enc[:12].lower().replace("_", "-")
+        if enc == "utf-8" or enc.startswith("utf-8-"):
+            return "utf-8"
+        if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \
+           enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")):
+            return "iso-8859-1"
+        return orig_enc
+
+    def detect_encoding(readline):
+        """
+        The detect_encoding() function is used to detect the encoding that should
+        be used to decode a Python source file.  It requires one argument, readline,
+        in the same way as the tokenize() generator.
+
+        It will call readline a maximum of twice, and return the encoding used
+        (as a string) and a list of any lines (left as bytes) it has read in.
+
+        It detects the encoding from the presence of a utf-8 bom or an encoding
+        cookie as specified in pep-0263.  If both a bom and a cookie are present,
+        but disagree, a SyntaxError will be raised.  If the encoding cookie is an
+        invalid charset, raise a SyntaxError.  Note that if a utf-8 bom is found,
+        'utf-8-sig' is returned.
+
+        If no encoding is specified, then the default of 'utf-8' will be returned.
+        """
+        try:
+            filename = readline.__self__.name
+        except AttributeError:
+            filename = None
+        bom_found = False
+        encoding = None
+        default = 'utf-8'
+
+        def read_or_stop():
+            try:
+                return readline()
+            except StopIteration:
+                return b''
+
+        def find_cookie(line):
+            try:
+                # Decode as UTF-8. Either the line is an encoding declaration,
+                # in which case it should be pure ASCII, or it must be UTF-8
+                # per default encoding.
+                line_string = line.decode('utf-8')
+            except UnicodeDecodeError:
+                msg = "invalid or missing encoding declaration"
+                if filename is not None:
+                    msg = '{} for {!r}'.format(msg, filename)
+                raise SyntaxError(msg)
+
+            matches = cookie_re.findall(line_string)
+            if not matches:
+                return None
+            encoding = _get_normal_name(matches[0])
+            try:
+                codec = lookup(encoding)
+            except LookupError:
+                # This behaviour mimics the Python interpreter
+                if filename is None:
+                    msg = "unknown encoding: " + encoding
+                else:
+                    msg = "unknown encoding for {!r}: {}".format(filename, encoding)
+                raise SyntaxError(msg)
+
+            if bom_found:
+                if codec.name != 'utf-8':
+                    # This behaviour mimics the Python interpreter
+                    if filename is None:
+                        msg = 'Encoding problem: utf-8'
+                    else:
+                        msg = 'Encoding problem for %r: utf-8' % filename
+                    raise SyntaxError(msg)
+                encoding += '-sig'
+            return encoding
+
+        first = read_or_stop()
+        if first.startswith(BOM_UTF8):
+            bom_found = True
+            first = first[3:]
+            default = 'utf-8-sig'
+        if not first:
+            return default, []
+
+        encoding = find_cookie(first)
+        if encoding:
+            return encoding, [first]
+
+        second = read_or_stop()
+        if not second:
+            return default, [first]
+
+        encoding = find_cookie(second)
+        if encoding:
+            return encoding, [first, second]
+
+        return default, [first, second]
+
+
+# For converting & <-> & etc.
+try:
+    from html import escape
+except ImportError:
+    from cgi import escape
+if sys.version_info[:2] < (3, 4):
+    unescape = HTMLParser().unescape
+else:
+    from html import unescape
+
+try:
+    from collections import ChainMap
+except ImportError:  # pragma: no cover
+    from collections import MutableMapping
+
+    try:
+        from reprlib import recursive_repr as _recursive_repr
+    except ImportError:
+
+        def _recursive_repr(fillvalue='...'):
+            '''
+            Decorator to make a repr function return fillvalue for a recursive
+            call
+            '''
+
+            def decorating_function(user_function):
+                repr_running = set()
+
+                def wrapper(self):
+                    key = id(self), get_ident()
+                    if key in repr_running:
+                        return fillvalue
+                    repr_running.add(key)
+                    try:
+                        result = user_function(self)
+                    finally:
+                        repr_running.discard(key)
+                    return result
+
+                # Can't use functools.wraps() here because of bootstrap issues
+                wrapper.__module__ = getattr(user_function, '__module__')
+                wrapper.__doc__ = getattr(user_function, '__doc__')
+                wrapper.__name__ = getattr(user_function, '__name__')
+                wrapper.__annotations__ = getattr(user_function, '__annotations__', {})
+                return wrapper
+
+            return decorating_function
+
+    class ChainMap(MutableMapping):
+        '''
+        A ChainMap groups multiple dicts (or other mappings) together
+        to create a single, updateable view.
+
+        The underlying mappings are stored in a list.  That list is public and can
+        accessed or updated using the *maps* attribute.  There is no other state.
+
+        Lookups search the underlying mappings successively until a key is found.
+        In contrast, writes, updates, and deletions only operate on the first
+        mapping.
+        '''
+
+        def __init__(self, *maps):
+            '''Initialize a ChainMap by setting *maps* to the given mappings.
+            If no mappings are provided, a single empty dictionary is used.
+
+            '''
+            self.maps = list(maps) or [{}]  # always at least one map
+
+        def __missing__(self, key):
+            raise KeyError(key)
+
+        def __getitem__(self, key):
+            for mapping in self.maps:
+                try:
+                    return mapping[key]  # can't use 'key in mapping' with defaultdict
+                except KeyError:
+                    pass
+            return self.__missing__(key)  # support subclasses that define __missing__
+
+        def get(self, key, default=None):
+            return self[key] if key in self else default
+
+        def __len__(self):
+            return len(set().union(*self.maps))  # reuses stored hash values if possible
+
+        def __iter__(self):
+            return iter(set().union(*self.maps))
+
+        def __contains__(self, key):
+            return any(key in m for m in self.maps)
+
+        def __bool__(self):
+            return any(self.maps)
+
+        @_recursive_repr()
+        def __repr__(self):
+            return '{0.__class__.__name__}({1})'.format(self, ', '.join(map(repr, self.maps)))
+
+        @classmethod
+        def fromkeys(cls, iterable, *args):
+            'Create a ChainMap with a single dict created from the iterable.'
+            return cls(dict.fromkeys(iterable, *args))
+
+        def copy(self):
+            'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]'
+            return self.__class__(self.maps[0].copy(), *self.maps[1:])
+
+        __copy__ = copy
+
+        def new_child(self):  # like Django's Context.push()
+            'New ChainMap with a new dict followed by all previous maps.'
+            return self.__class__({}, *self.maps)
+
+        @property
+        def parents(self):  # like Django's Context.pop()
+            'New ChainMap from maps[1:].'
+            return self.__class__(*self.maps[1:])
+
+        def __setitem__(self, key, value):
+            self.maps[0][key] = value
+
+        def __delitem__(self, key):
+            try:
+                del self.maps[0][key]
+            except KeyError:
+                raise KeyError('Key not found in the first mapping: {!r}'.format(key))
+
+        def popitem(self):
+            'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.'
+            try:
+                return self.maps[0].popitem()
+            except KeyError:
+                raise KeyError('No keys found in the first mapping.')
+
+        def pop(self, key, *args):
+            'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].'
+            try:
+                return self.maps[0].pop(key, *args)
+            except KeyError:
+                raise KeyError('Key not found in the first mapping: {!r}'.format(key))
+
+        def clear(self):
+            'Clear maps[0], leaving maps[1:] intact.'
+            self.maps[0].clear()
+
+
+try:
+    from importlib.util import cache_from_source  # Python >= 3.4
+except ImportError:  # pragma: no cover
+
+    def cache_from_source(path, debug_override=None, optimization=None):
+        assert path.endswith('.py')
+        if debug_override is None:
+            debug_override = __debug__
+        if debug_override:
+            suffix = 'c'
+        else:
+            suffix = 'o'
+        return path + suffix
+
+
+try:
+    from collections import OrderedDict
+except ImportError:  # pragma: no cover
+    # {{{ http://code.activestate.com/recipes/576693/ (r9)
+    # Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy.
+    # Passes Python2.7's test suite and incorporates all the latest updates.
+    try:
+        from thread import get_ident as _get_ident
+    except ImportError:
+        from dummy_thread import get_ident as _get_ident
+
+    try:
+        from _abcoll import KeysView, ValuesView, ItemsView
+    except ImportError:
+        pass
+
+    class OrderedDict(dict):
+        'Dictionary that remembers insertion order'
+
+        # An inherited dict maps keys to values.
+        # The inherited dict provides __getitem__, __len__, __contains__, and get.
+        # The remaining methods are order-aware.
+        # Big-O running times for all methods are the same as for regular dictionaries.
+
+        # The internal self.__map dictionary maps keys to links in a doubly linked list.
+        # The circular doubly linked list starts and ends with a sentinel element.
+        # The sentinel element never gets deleted (this simplifies the algorithm).
+        # Each link is stored as a list of length three:  [PREV, NEXT, KEY].
+
+        def __init__(self, *args, **kwds):
+            '''Initialize an ordered dictionary.  Signature is the same as for
+            regular dictionaries, but keyword arguments are not recommended
+            because their insertion order is arbitrary.
+
+            '''
+            if len(args) > 1:
+                raise TypeError('expected at most 1 arguments, got %d' % len(args))
+            try:
+                self.__root
+            except AttributeError:
+                self.__root = root = []  # sentinel node
+                root[:] = [root, root, None]
+                self.__map = {}
+            self.__update(*args, **kwds)
+
+        def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
+            'od.__setitem__(i, y) <==> od[i]=y'
+            # Setting a new item creates a new link which goes at the end of the linked
+            # list, and the inherited dictionary is updated with the new key/value pair.
+            if key not in self:
+                root = self.__root
+                last = root[0]
+                last[1] = root[0] = self.__map[key] = [last, root, key]
+            dict_setitem(self, key, value)
+
+        def __delitem__(self, key, dict_delitem=dict.__delitem__):
+            'od.__delitem__(y) <==> del od[y]'
+            # Deleting an existing item uses self.__map to find the link which is
+            # then removed by updating the links in the predecessor and successor nodes.
+            dict_delitem(self, key)
+            link_prev, link_next, key = self.__map.pop(key)
+            link_prev[1] = link_next
+            link_next[0] = link_prev
+
+        def __iter__(self):
+            'od.__iter__() <==> iter(od)'
+            root = self.__root
+            curr = root[1]
+            while curr is not root:
+                yield curr[2]
+                curr = curr[1]
+
+        def __reversed__(self):
+            'od.__reversed__() <==> reversed(od)'
+            root = self.__root
+            curr = root[0]
+            while curr is not root:
+                yield curr[2]
+                curr = curr[0]
+
+        def clear(self):
+            'od.clear() -> None.  Remove all items from od.'
+            try:
+                for node in self.__map.itervalues():
+                    del node[:]
+                root = self.__root
+                root[:] = [root, root, None]
+                self.__map.clear()
+            except AttributeError:
+                pass
+            dict.clear(self)
+
+        def popitem(self, last=True):
+            '''od.popitem() -> (k, v), return and remove a (key, value) pair.
+            Pairs are returned in LIFO order if last is true or FIFO order if false.
+
+            '''
+            if not self:
+                raise KeyError('dictionary is empty')
+            root = self.__root
+            if last:
+                link = root[0]
+                link_prev = link[0]
+                link_prev[1] = root
+                root[0] = link_prev
+            else:
+                link = root[1]
+                link_next = link[1]
+                root[1] = link_next
+                link_next[0] = root
+            key = link[2]
+            del self.__map[key]
+            value = dict.pop(self, key)
+            return key, value
+
+        # -- the following methods do not depend on the internal structure --
+
+        def keys(self):
+            'od.keys() -> list of keys in od'
+            return list(self)
+
+        def values(self):
+            'od.values() -> list of values in od'
+            return [self[key] for key in self]
+
+        def items(self):
+            'od.items() -> list of (key, value) pairs in od'
+            return [(key, self[key]) for key in self]
+
+        def iterkeys(self):
+            'od.iterkeys() -> an iterator over the keys in od'
+            return iter(self)
+
+        def itervalues(self):
+            'od.itervalues -> an iterator over the values in od'
+            for k in self:
+                yield self[k]
+
+        def iteritems(self):
+            'od.iteritems -> an iterator over the (key, value) items in od'
+            for k in self:
+                yield (k, self[k])
+
+        def update(*args, **kwds):
+            '''od.update(E, **F) -> None.  Update od from dict/iterable E and F.
+
+            If E is a dict instance, does:           for k in E: od[k] = E[k]
+            If E has a .keys() method, does:         for k in E.keys(): od[k] = E[k]
+            Or if E is an iterable of items, does:   for k, v in E: od[k] = v
+            In either case, this is followed by:     for k, v in F.items(): od[k] = v
+
+            '''
+            if len(args) > 2:
+                raise TypeError('update() takes at most 2 positional '
+                                'arguments (%d given)' % (len(args), ))
+            elif not args:
+                raise TypeError('update() takes at least 1 argument (0 given)')
+            self = args[0]
+            # Make progressively weaker assumptions about "other"
+            other = ()
+            if len(args) == 2:
+                other = args[1]
+            if isinstance(other, dict):
+                for key in other:
+                    self[key] = other[key]
+            elif hasattr(other, 'keys'):
+                for key in other.keys():
+                    self[key] = other[key]
+            else:
+                for key, value in other:
+                    self[key] = value
+            for key, value in kwds.items():
+                self[key] = value
+
+        __update = update  # let subclasses override update without breaking __init__
+
+        __marker = object()
+
+        def pop(self, key, default=__marker):
+            '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
+            If key is not found, d is returned if given, otherwise KeyError is raised.
+
+            '''
+            if key in self:
+                result = self[key]
+                del self[key]
+                return result
+            if default is self.__marker:
+                raise KeyError(key)
+            return default
+
+        def setdefault(self, key, default=None):
+            'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
+            if key in self:
+                return self[key]
+            self[key] = default
+            return default
+
+        def __repr__(self, _repr_running=None):
+            'od.__repr__() <==> repr(od)'
+            if not _repr_running:
+                _repr_running = {}
+            call_key = id(self), _get_ident()
+            if call_key in _repr_running:
+                return '...'
+            _repr_running[call_key] = 1
+            try:
+                if not self:
+                    return '%s()' % (self.__class__.__name__, )
+                return '%s(%r)' % (self.__class__.__name__, self.items())
+            finally:
+                del _repr_running[call_key]
+
+        def __reduce__(self):
+            'Return state information for pickling'
+            items = [[k, self[k]] for k in self]
+            inst_dict = vars(self).copy()
+            for k in vars(OrderedDict()):
+                inst_dict.pop(k, None)
+            if inst_dict:
+                return (self.__class__, (items, ), inst_dict)
+            return self.__class__, (items, )
+
+        def copy(self):
+            'od.copy() -> a shallow copy of od'
+            return self.__class__(self)
+
+        @classmethod
+        def fromkeys(cls, iterable, value=None):
+            '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
+            and values equal to v (which defaults to None).
+
+            '''
+            d = cls()
+            for key in iterable:
+                d[key] = value
+            return d
+
+        def __eq__(self, other):
+            '''od.__eq__(y) <==> od==y.  Comparison to another OD is order-sensitive
+            while comparison to a regular mapping is order-insensitive.
+
+            '''
+            if isinstance(other, OrderedDict):
+                return len(self) == len(other) and self.items() == other.items()
+            return dict.__eq__(self, other)
+
+        def __ne__(self, other):
+            return not self == other
+
+        # -- the following methods are only used in Python 2.7 --
+
+        def viewkeys(self):
+            "od.viewkeys() -> a set-like object providing a view on od's keys"
+            return KeysView(self)
+
+        def viewvalues(self):
+            "od.viewvalues() -> an object providing a view on od's values"
+            return ValuesView(self)
+
+        def viewitems(self):
+            "od.viewitems() -> a set-like object providing a view on od's items"
+            return ItemsView(self)
+
+
+try:
+    from logging.config import BaseConfigurator, valid_ident
+except ImportError:  # pragma: no cover
+    IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
+
+    def valid_ident(s):
+        m = IDENTIFIER.match(s)
+        if not m:
+            raise ValueError('Not a valid Python identifier: %r' % s)
+        return True
+
+    # The ConvertingXXX classes are wrappers around standard Python containers,
+    # and they serve to convert any suitable values in the container. The
+    # conversion converts base dicts, lists and tuples to their wrapped
+    # equivalents, whereas strings which match a conversion format are converted
+    # appropriately.
+    #
+    # Each wrapper should have a configurator attribute holding the actual
+    # configurator to use for conversion.
+
+    class ConvertingDict(dict):
+        """A converting dictionary wrapper."""
+
+        def __getitem__(self, key):
+            value = dict.__getitem__(self, key)
+            result = self.configurator.convert(value)
+            # If the converted value is different, save for next time
+            if value is not result:
+                self[key] = result
+                if type(result) in (ConvertingDict, ConvertingList, ConvertingTuple):
+                    result.parent = self
+                    result.key = key
+            return result
+
+        def get(self, key, default=None):
+            value = dict.get(self, key, default)
+            result = self.configurator.convert(value)
+            # If the converted value is different, save for next time
+            if value is not result:
+                self[key] = result
+                if type(result) in (ConvertingDict, ConvertingList, ConvertingTuple):
+                    result.parent = self
+                    result.key = key
+            return result
+
+    def pop(self, key, default=None):
+        value = dict.pop(self, key, default)
+        result = self.configurator.convert(value)
+        if value is not result:
+            if type(result) in (ConvertingDict, ConvertingList, ConvertingTuple):
+                result.parent = self
+                result.key = key
+        return result
+
+    class ConvertingList(list):
+        """A converting list wrapper."""
+
+        def __getitem__(self, key):
+            value = list.__getitem__(self, key)
+            result = self.configurator.convert(value)
+            # If the converted value is different, save for next time
+            if value is not result:
+                self[key] = result
+                if type(result) in (ConvertingDict, ConvertingList, ConvertingTuple):
+                    result.parent = self
+                    result.key = key
+            return result
+
+        def pop(self, idx=-1):
+            value = list.pop(self, idx)
+            result = self.configurator.convert(value)
+            if value is not result:
+                if type(result) in (ConvertingDict, ConvertingList, ConvertingTuple):
+                    result.parent = self
+            return result
+
+    class ConvertingTuple(tuple):
+        """A converting tuple wrapper."""
+
+        def __getitem__(self, key):
+            value = tuple.__getitem__(self, key)
+            result = self.configurator.convert(value)
+            if value is not result:
+                if type(result) in (ConvertingDict, ConvertingList, ConvertingTuple):
+                    result.parent = self
+                    result.key = key
+            return result
+
+    class BaseConfigurator(object):
+        """
+        The configurator base class which defines some useful defaults.
+        """
+
+        CONVERT_PATTERN = re.compile(r'^(?P[a-z]+)://(?P.*)$')
+
+        WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
+        DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
+        INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
+        DIGIT_PATTERN = re.compile(r'^\d+$')
+
+        value_converters = {
+            'ext': 'ext_convert',
+            'cfg': 'cfg_convert',
+        }
+
+        # We might want to use a different one, e.g. importlib
+        importer = staticmethod(__import__)
+
+        def __init__(self, config):
+            self.config = ConvertingDict(config)
+            self.config.configurator = self
+
+        def resolve(self, s):
+            """
+            Resolve strings to objects using standard import and attribute
+            syntax.
+            """
+            name = s.split('.')
+            used = name.pop(0)
+            try:
+                found = self.importer(used)
+                for frag in name:
+                    used += '.' + frag
+                    try:
+                        found = getattr(found, frag)
+                    except AttributeError:
+                        self.importer(used)
+                        found = getattr(found, frag)
+                return found
+            except ImportError:
+                e, tb = sys.exc_info()[1:]
+                v = ValueError('Cannot resolve %r: %s' % (s, e))
+                v.__cause__, v.__traceback__ = e, tb
+                raise v
+
+        def ext_convert(self, value):
+            """Default converter for the ext:// protocol."""
+            return self.resolve(value)
+
+        def cfg_convert(self, value):
+            """Default converter for the cfg:// protocol."""
+            rest = value
+            m = self.WORD_PATTERN.match(rest)
+            if m is None:
+                raise ValueError("Unable to convert %r" % value)
+            else:
+                rest = rest[m.end():]
+                d = self.config[m.groups()[0]]
+                while rest:
+                    m = self.DOT_PATTERN.match(rest)
+                    if m:
+                        d = d[m.groups()[0]]
+                    else:
+                        m = self.INDEX_PATTERN.match(rest)
+                        if m:
+                            idx = m.groups()[0]
+                            if not self.DIGIT_PATTERN.match(idx):
+                                d = d[idx]
+                            else:
+                                try:
+                                    n = int(idx)  # try as number first (most likely)
+                                    d = d[n]
+                                except TypeError:
+                                    d = d[idx]
+                    if m:
+                        rest = rest[m.end():]
+                    else:
+                        raise ValueError('Unable to convert '
+                                         '%r at %r' % (value, rest))
+            # rest should be empty
+            return d
+
+        def convert(self, value):
+            """
+            Convert values to an appropriate type. dicts, lists and tuples are
+            replaced by their converting alternatives. Strings are checked to
+            see if they have a conversion format and are converted if they do.
+            """
+            if not isinstance(value, ConvertingDict) and isinstance(value, dict):
+                value = ConvertingDict(value)
+                value.configurator = self
+            elif not isinstance(value, ConvertingList) and isinstance(value, list):
+                value = ConvertingList(value)
+                value.configurator = self
+            elif not isinstance(value, ConvertingTuple) and isinstance(value, tuple):
+                value = ConvertingTuple(value)
+                value.configurator = self
+            elif isinstance(value, string_types):
+                m = self.CONVERT_PATTERN.match(value)
+                if m:
+                    d = m.groupdict()
+                    prefix = d['prefix']
+                    converter = self.value_converters.get(prefix, None)
+                    if converter:
+                        suffix = d['suffix']
+                        converter = getattr(self, converter)
+                        value = converter(suffix)
+            return value
+
+        def configure_custom(self, config):
+            """Configure an object with a user-supplied factory."""
+            c = config.pop('()')
+            if not callable(c):
+                c = self.resolve(c)
+            props = config.pop('.', None)
+            # Check for valid identifiers
+            kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
+            result = c(**kwargs)
+            if props:
+                for name, value in props.items():
+                    setattr(result, name, value)
+            return result
+
+        def as_tuple(self, value):
+            """Utility function which converts lists to tuples."""
+            if isinstance(value, list):
+                value = tuple(value)
+            return value
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/resources.py b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/resources.py
new file mode 100644
index 0000000..b908308
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/resources.py
@@ -0,0 +1,372 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2013-2026 Vinay Sajip.
+# Licensed to the Python Software Foundation under a contributor agreement.
+# See LICENSE.txt and CONTRIBUTORS.txt.
+#
+from __future__ import unicode_literals
+
+import bisect
+import io
+import logging
+import os
+import pkgutil
+import sys
+import types
+import zipimport
+
+from . import DistlibException
+from .util import cached_property, get_cache_base, Cache
+
+logger = logging.getLogger(__name__)
+
+cache = None  # created when needed
+
+
+class ResourceCache(Cache):
+
+    def __init__(self, base=None):
+        if base is None:
+            # Use native string to avoid issues on 2.x: see Python #20140.
+            base = os.path.join(get_cache_base(), str('resource-cache'))
+        super(ResourceCache, self).__init__(base)
+
+    def is_stale(self, resource, path):
+        """
+        Is the cache stale for the given resource?
+
+        :param resource: The :class:`Resource` being cached.
+        :param path: The path of the resource in the cache.
+        :return: True if the cache is stale.
+        """
+        # Cache invalidation is a hard problem :-)
+        return True
+
+    def get(self, resource):
+        """
+        Get a resource into the cache,
+
+        :param resource: A :class:`Resource` instance.
+        :return: The pathname of the resource in the cache.
+        """
+        prefix, path = resource.finder.get_cache_info(resource)
+        if prefix is None:
+            result = path
+        else:
+            result = os.path.join(self.base, self.prefix_to_dir(prefix), path)
+            dirname = os.path.dirname(result)
+            if not os.path.isdir(dirname):
+                os.makedirs(dirname)
+            if not os.path.exists(result):
+                stale = True
+            else:
+                stale = self.is_stale(resource, path)
+            if stale:
+                # write the bytes of the resource to the cache location
+                with open(result, 'wb') as f:
+                    f.write(resource.bytes)
+        return result
+
+
+class ResourceBase(object):
+
+    def __init__(self, finder, name):
+        self.finder = finder
+        self.name = name
+
+
+class Resource(ResourceBase):
+    """
+    A class representing an in-package resource, such as a data file. This is
+    not normally instantiated by user code, but rather by a
+    :class:`ResourceFinder` which manages the resource.
+    """
+    is_container = False  # Backwards compatibility
+
+    def as_stream(self):
+        """
+        Get the resource as a stream.
+
+        This is not a property to make it obvious that it returns a new stream
+        each time.
+        """
+        return self.finder.get_stream(self)
+
+    @cached_property
+    def file_path(self):
+        global cache
+        if cache is None:
+            cache = ResourceCache()
+        return cache.get(self)
+
+    @cached_property
+    def bytes(self):
+        return self.finder.get_bytes(self)
+
+    @cached_property
+    def size(self):
+        return self.finder.get_size(self)
+
+
+class ResourceContainer(ResourceBase):
+    is_container = True  # Backwards compatibility
+
+    @cached_property
+    def resources(self):
+        return self.finder.get_resources(self)
+
+
+class ResourceFinder(object):
+    """
+    Resource finder for file system resources.
+    """
+
+    if sys.platform.startswith('java'):
+        skipped_extensions = ('.pyc', '.pyo', '.class')
+    else:
+        skipped_extensions = ('.pyc', '.pyo')
+
+    def __init__(self, module):
+        self.module = module
+        self.loader = getattr(module, '__loader__', None)
+        self.base = os.path.dirname(getattr(module, '__file__', ''))
+
+    def _adjust_path(self, path):
+        return os.path.realpath(path)
+
+    def _is_in_base(self, path):
+        base = self._adjust_path(self.base)
+        if path == base:
+            return True
+        if not base.endswith(os.sep):
+            base = base + os.sep
+        return path.startswith(base)
+
+    def _make_path(self, resource_name):
+        # Issue #50: need to preserve type of path on Python 2.x
+        # like os.path._get_sep
+        if isinstance(resource_name, bytes):  # should only happen on 2.x
+            sep = b'/'
+        else:
+            sep = '/'
+        parts = resource_name.split(sep)
+        parts.insert(0, self.base)
+        result = os.path.join(*parts)
+        result = self._adjust_path(result)
+        # Confine the resolved resource to the package base so a resource
+        # name containing '..' cannot read files outside the package.
+        if not self._is_in_base(result):
+            raise DistlibException('Resource name escapes package: '
+                                   '%r' % resource_name)
+        return result
+
+    def _find(self, path):
+        return os.path.exists(path)
+
+    def get_cache_info(self, resource):
+        return None, resource.path
+
+    def find(self, resource_name):
+        path = self._make_path(resource_name)
+        if not self._find(path):
+            result = None
+        else:
+            if self._is_directory(path):
+                result = ResourceContainer(self, resource_name)
+            else:
+                result = Resource(self, resource_name)
+            result.path = path
+        return result
+
+    def get_stream(self, resource):
+        return open(resource.path, 'rb')
+
+    def get_bytes(self, resource):
+        with open(resource.path, 'rb') as f:
+            return f.read()
+
+    def get_size(self, resource):
+        return os.path.getsize(resource.path)
+
+    def get_resources(self, resource):
+
+        def allowed(f):
+            return (f != '__pycache__' and not f.endswith(self.skipped_extensions))
+
+        return set([f for f in os.listdir(resource.path) if allowed(f)])
+
+    def is_container(self, resource):
+        return self._is_directory(resource.path)
+
+    _is_directory = staticmethod(os.path.isdir)
+
+    def iterator(self, resource_name):
+        resource = self.find(resource_name)
+        if resource is not None:
+            todo = [resource]
+            while todo:
+                resource = todo.pop(0)
+                yield resource
+                if resource.is_container:
+                    rname = resource.name
+                    for name in resource.resources:
+                        if not rname:
+                            new_name = name
+                        else:
+                            new_name = '/'.join([rname, name])
+                        child = self.find(new_name)
+                        if child.is_container:
+                            todo.append(child)
+                        else:
+                            yield child
+
+
+class ZipResourceFinder(ResourceFinder):
+    """
+    Resource finder for resources in .zip files.
+    """
+
+    def __init__(self, module):
+        super(ZipResourceFinder, self).__init__(module)
+        archive = self.loader.archive
+        self.prefix_len = 1 + len(archive)
+        # PyPy doesn't have a _files attr on zipimporter, and you can't set one
+        if hasattr(self.loader, '_files'):
+            self._files = self.loader._files
+        else:
+            self._files = zipimport._zip_directory_cache[archive]
+        self.index = sorted(self._files)
+
+    def _adjust_path(self, path):
+        return path
+
+    def _find(self, path):
+        path = path[self.prefix_len:]
+        if path in self._files:
+            result = True
+        else:
+            if path and path[-1] != os.sep:
+                path = path + os.sep
+            i = bisect.bisect(self.index, path)
+            try:
+                result = self.index[i].startswith(path)
+            except IndexError:
+                result = False
+        if not result:
+            logger.debug('_find failed: %r %r', path, self.loader.prefix)
+        else:
+            logger.debug('_find worked: %r %r', path, self.loader.prefix)
+        return result
+
+    def get_cache_info(self, resource):
+        prefix = self.loader.archive
+        path = resource.path[1 + len(prefix):]
+        return prefix, path
+
+    def get_bytes(self, resource):
+        return self.loader.get_data(resource.path)
+
+    def get_stream(self, resource):
+        return io.BytesIO(self.get_bytes(resource))
+
+    def get_size(self, resource):
+        path = resource.path[self.prefix_len:]
+        return self._files[path][3]
+
+    def get_resources(self, resource):
+        path = resource.path[self.prefix_len:]
+        if path and path[-1] != os.sep:
+            path += os.sep
+        plen = len(path)
+        result = set()
+        i = bisect.bisect(self.index, path)
+        while i < len(self.index):
+            if not self.index[i].startswith(path):
+                break
+            s = self.index[i][plen:]
+            result.add(s.split(os.sep, 1)[0])  # only immediate children
+            i += 1
+        return result
+
+    def _is_directory(self, path):
+        path = path[self.prefix_len:]
+        if path and path[-1] != os.sep:
+            path += os.sep
+        i = bisect.bisect(self.index, path)
+        try:
+            result = self.index[i].startswith(path)
+        except IndexError:
+            result = False
+        return result
+
+
+_finder_registry = {type(None): ResourceFinder, zipimport.zipimporter: ZipResourceFinder}
+
+try:
+    # In Python 3.6, _frozen_importlib -> _frozen_importlib_external
+    try:
+        import _frozen_importlib_external as _fi
+    except ImportError:
+        import _frozen_importlib as _fi
+    _finder_registry[_fi.SourceFileLoader] = ResourceFinder
+    _finder_registry[_fi.FileFinder] = ResourceFinder
+    # See issue #146
+    _finder_registry[_fi.SourcelessFileLoader] = ResourceFinder
+    del _fi
+except (ImportError, AttributeError):
+    pass
+
+
+def register_finder(loader, finder_maker):
+    _finder_registry[type(loader)] = finder_maker
+
+
+_finder_cache = {}
+
+
+def finder(package):
+    """
+    Return a resource finder for a package.
+    :param package: The name of the package.
+    :return: A :class:`ResourceFinder` instance for the package.
+    """
+    if package in _finder_cache:
+        result = _finder_cache[package]
+    else:
+        if package not in sys.modules:
+            __import__(package)
+        module = sys.modules[package]
+        path = getattr(module, '__path__', None)
+        if path is None:
+            raise DistlibException('You cannot get a finder for a module, '
+                                   'only for a package')
+        loader = getattr(module, '__loader__', None)
+        finder_maker = _finder_registry.get(type(loader))
+        if finder_maker is None:
+            raise DistlibException('Unable to locate finder for %r' % package)
+        result = finder_maker(module)
+        _finder_cache[package] = result
+    return result
+
+
+_dummy_module = types.ModuleType(str('__dummy__'))
+
+
+def finder_for_path(path):
+    """
+    Return a resource finder for a path, which should represent a container.
+
+    :param path: The path.
+    :return: A :class:`ResourceFinder` instance for the path.
+    """
+    result = None
+    # calls any path hooks, gets importer into cache
+    pkgutil.get_importer(path)
+    loader = sys.path_importer_cache.get(path)
+    finder = _finder_registry.get(type(loader))
+    if finder:
+        module = _dummy_module
+        module.__file__ = os.path.join(path, '')
+        module.__loader__ = loader
+        result = finder(module)
+    return result
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/scripts.py b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/scripts.py
new file mode 100644
index 0000000..ac9e909
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/scripts.py
@@ -0,0 +1,451 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2013-2026 Vinay Sajip.
+# Licensed to the Python Software Foundation under a contributor agreement.
+# See LICENSE.txt and CONTRIBUTORS.txt.
+#
+from io import BytesIO
+import logging
+import os
+import re
+import struct
+import sys
+import time
+from zipfile import ZipInfo
+
+from . import DistlibException
+from .compat import sysconfig, detect_encoding, ZipFile
+from .resources import finder
+from .util import (FileOperator, get_export_entry, convert_path, get_executable, get_platform, in_venv,
+                   is_in_directory)
+
+logger = logging.getLogger(__name__)
+
+_DEFAULT_MANIFEST = '''
+
+
+ 
+
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+'''.strip()
+
+# check if Python is called on the first line with this expression
+FIRST_LINE_RE = re.compile(b'^#!.*pythonw?[0-9.]*([ \t].*)?$')
+SCRIPT_TEMPLATE = r'''# -*- coding: utf-8 -*-
+import re
+import sys
+if __name__ == '__main__':
+    from %(module)s import %(import_name)s
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(%(func)s())
+'''
+
+# Pre-fetch the contents of all executable wrapper stubs.
+# This is to address https://github.com/pypa/pip/issues/12666.
+# When updating pip, we rename the old pip in place before installing the
+# new version. If we try to fetch a wrapper *after* that rename, the finder
+# machinery will be confused as the package is no longer available at the
+# location where it was imported from. So we load everything into memory in
+# advance.
+
+if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'):
+    # Issue 31: don't hardcode an absolute package name, but
+    # determine it relative to the current package
+    DISTLIB_PACKAGE = __name__.rsplit('.', 1)[0]
+
+    WRAPPERS = {
+        r.name: r.bytes
+        for r in finder(DISTLIB_PACKAGE).iterator("")
+        if r.name.endswith(".exe")
+    }
+
+
+def enquote_executable(executable):
+    if ' ' in executable:
+        # make sure we quote only the executable in case of env
+        # for example /usr/bin/env "/dir with spaces/bin/jython"
+        # instead of "/usr/bin/env /dir with spaces/bin/jython"
+        # otherwise whole
+        if executable.startswith('/usr/bin/env '):
+            env, _executable = executable.split(' ', 1)
+            if ' ' in _executable and not _executable.startswith('"'):
+                executable = '%s "%s"' % (env, _executable)
+        else:
+            if not executable.startswith('"'):
+                executable = '"%s"' % executable
+    return executable
+
+
+# Keep the old name around (for now), as there is at least one project using it!
+_enquote_executable = enquote_executable
+
+
+class ScriptMaker(object):
+    """
+    A class to copy or create scripts from source scripts or callable
+    specifications.
+    """
+    script_template = SCRIPT_TEMPLATE
+
+    executable = None  # for shebangs
+
+    def __init__(self, source_dir, target_dir, add_launchers=True, dry_run=False, fileop=None):
+        self.source_dir = source_dir
+        self.target_dir = target_dir
+        self.add_launchers = add_launchers
+        self.force = False
+        self.clobber = False
+        # It only makes sense to set mode bits on POSIX.
+        self.set_mode = (os.name == 'posix') or (os.name == 'java' and os._name == 'posix')
+        self.variants = set(('', 'X.Y'))
+        self._fileop = fileop or FileOperator(dry_run)
+
+        self._is_nt = os.name == 'nt' or (os.name == 'java' and os._name == 'nt')
+        self.version_info = sys.version_info
+
+    def _get_alternate_executable(self, executable, options):
+        if options.get('gui', False) and self._is_nt:  # pragma: no cover
+            dn, fn = os.path.split(executable)
+            fn = fn.replace('python', 'pythonw')
+            executable = os.path.join(dn, fn)
+        return executable
+
+    if sys.platform.startswith('java'):  # pragma: no cover
+
+        def _is_shell(self, executable):
+            """
+            Determine if the specified executable is a script
+            (contains a #! line)
+            """
+            try:
+                with open(executable) as fp:
+                    return fp.read(2) == '#!'
+            except (OSError, IOError):
+                logger.warning('Failed to open %s', executable)
+                return False
+
+        def _fix_jython_executable(self, executable):
+            if self._is_shell(executable):
+                # Workaround for Jython is not needed on Linux systems.
+                import java
+
+                if java.lang.System.getProperty('os.name') == 'Linux':
+                    return executable
+            elif executable.lower().endswith('jython.exe'):
+                # Use wrapper exe for Jython on Windows
+                return executable
+            return '/usr/bin/env %s' % executable
+
+    def _build_shebang(self, executable, post_interp):
+        """
+        Build a shebang line. In the simple case (on Windows, or a shebang line
+        which is not too long or contains spaces) use a simple formulation for
+        the shebang. Otherwise, use /bin/sh as the executable, with a contrived
+        shebang which allows the script to run either under Python or sh, using
+        suitable quoting. Thanks to Harald Nordgren for his input.
+
+        See also: http://www.in-ulm.de/~mascheck/various/shebang/#length
+                  https://hg.mozilla.org/mozilla-central/file/tip/mach
+        """
+        if os.name != 'posix':
+            simple_shebang = True
+        elif getattr(sys, "cross_compiling", False):
+            # In a cross-compiling environment, the shebang will likely be a
+            # script; this *must* be invoked with the "safe" version of the
+            # shebang, or else using os.exec() to run the entry script will
+            # fail, raising "OSError 8 [Errno 8] Exec format error".
+            simple_shebang = False
+        else:
+            # Add 3 for '#!' prefix and newline suffix.
+            shebang_length = len(executable) + len(post_interp) + 3
+            if sys.platform == 'darwin':
+                max_shebang_length = 512
+            else:
+                max_shebang_length = 127
+            simple_shebang = ((b' ' not in executable) and (shebang_length <= max_shebang_length))
+
+        if simple_shebang:
+            result = b'#!' + executable + post_interp + b'\n'
+        else:
+            result = b'#!/bin/sh\n'
+            result += b"'''exec' " + executable + post_interp + b' "$0" "$@"\n'
+            result += b"' '''\n"
+        return result
+
+    def _get_shebang(self, encoding, post_interp=b'', options=None):
+        enquote = True
+        if self.executable:
+            executable = self.executable
+            enquote = False  # assume this will be taken care of
+        elif not sysconfig.is_python_build():
+            executable = get_executable()
+        elif in_venv():  # pragma: no cover
+            executable = os.path.join(sysconfig.get_path('scripts'), 'python%s' % sysconfig.get_config_var('EXE'))
+        else:  # pragma: no cover
+            if os.name == 'nt':
+                # for Python builds from source on Windows, no Python executables with
+                # a version suffix are created, so we use python.exe
+                executable = os.path.join(sysconfig.get_config_var('BINDIR'),
+                                          'python%s' % (sysconfig.get_config_var('EXE')))
+            else:
+                executable = os.path.join(
+                    sysconfig.get_config_var('BINDIR'),
+                    'python%s%s' % (sysconfig.get_config_var('VERSION'), sysconfig.get_config_var('EXE')))
+        if options:
+            executable = self._get_alternate_executable(executable, options)
+
+        if sys.platform.startswith('java'):  # pragma: no cover
+            executable = self._fix_jython_executable(executable)
+
+        # Normalise case for Windows - COMMENTED OUT
+        # executable = os.path.normcase(executable)
+        # N.B. The normalising operation above has been commented out: See
+        # issue #124. Although paths in Windows are generally case-insensitive,
+        # they aren't always. For example, a path containing a ẞ (which is a
+        # LATIN CAPITAL LETTER SHARP S - U+1E9E) is normcased to ß (which is a
+        # LATIN SMALL LETTER SHARP S' - U+00DF). The two are not considered by
+        # Windows as equivalent in path names.
+
+        # If the user didn't specify an executable, it may be necessary to
+        # cater for executable paths with spaces (not uncommon on Windows)
+        if enquote:
+            executable = enquote_executable(executable)
+        # Issue #51: don't use fsencode, since we later try to
+        # check that the shebang is decodable using utf-8.
+        executable = executable.encode('utf-8')
+        # in case of IronPython, play safe and enable frames support
+        if (sys.platform == 'cli' and '-X:Frames' not in post_interp and
+                '-X:FullFrames' not in post_interp):  # pragma: no cover
+            post_interp += b' -X:Frames'
+        shebang = self._build_shebang(executable, post_interp)
+        # Python parser starts to read a script using UTF-8 until
+        # it gets a #coding:xxx cookie. The shebang has to be the
+        # first line of a file, the #coding:xxx cookie cannot be
+        # written before. So the shebang has to be decodable from
+        # UTF-8.
+        try:
+            shebang.decode('utf-8')
+        except UnicodeDecodeError:  # pragma: no cover
+            raise ValueError('The shebang (%r) is not decodable from utf-8' % shebang)
+        # If the script is encoded to a custom encoding (use a
+        # #coding:xxx cookie), the shebang has to be decodable from
+        # the script encoding too.
+        if encoding != 'utf-8':
+            try:
+                shebang.decode(encoding)
+            except UnicodeDecodeError:  # pragma: no cover
+                raise ValueError('The shebang (%r) is not decodable '
+                                 'from the script encoding (%r)' % (shebang, encoding))
+        return shebang
+
+    def _get_script_text(self, entry):
+        return self.script_template % dict(
+            module=entry.prefix, import_name=entry.suffix.split('.')[0], func=entry.suffix)
+
+    manifest = _DEFAULT_MANIFEST
+
+    def get_manifest(self, exename):
+        base = os.path.basename(exename)
+        return self.manifest % base
+
+    def _write_script(self, names, shebang, script_bytes, filenames, ext):
+        use_launcher = self.add_launchers and self._is_nt
+        if not use_launcher:
+            script_bytes = shebang + script_bytes
+        else:  # pragma: no cover
+            if ext == 'py':
+                launcher = self._get_launcher('t')
+            else:
+                launcher = self._get_launcher('w')
+            stream = BytesIO()
+            with ZipFile(stream, 'w') as zf:
+                source_date_epoch = os.environ.get('SOURCE_DATE_EPOCH')
+                if source_date_epoch:
+                    date_time = time.gmtime(int(source_date_epoch))[:6]
+                    zinfo = ZipInfo(filename='__main__.py', date_time=date_time)
+                    zf.writestr(zinfo, script_bytes)
+                else:
+                    zf.writestr('__main__.py', script_bytes)
+            zip_data = stream.getvalue()
+            script_bytes = launcher + shebang + zip_data
+        for name in names:
+            outname = os.path.abspath(os.path.join(self.target_dir, name))
+            if not is_in_directory(outname, self.target_dir):
+                raise DistlibException('Attempt to escape script directory')
+            if use_launcher:  # pragma: no cover
+                n, e = os.path.splitext(outname)
+                if e.startswith('.py'):
+                    outname = n
+                outname = '%s.exe' % outname
+                try:
+                    self._fileop.write_binary_file(outname, script_bytes)
+                except Exception:
+                    # Failed writing an executable - it might be in use.
+                    logger.warning('Failed to write executable - trying to '
+                                   'use .deleteme logic')
+                    dfname = '%s.deleteme' % outname
+                    if os.path.exists(dfname):
+                        os.remove(dfname)  # Not allowed to fail here
+                    os.rename(outname, dfname)  # nor here
+                    self._fileop.write_binary_file(outname, script_bytes)
+                    logger.debug('Able to replace executable using '
+                                 '.deleteme logic')
+                    try:
+                        os.remove(dfname)
+                    except Exception:
+                        pass  # still in use - ignore error
+            else:
+                if self._is_nt and not outname.endswith('.' + ext):  # pragma: no cover
+                    outname = '%s.%s' % (outname, ext)
+                if os.path.exists(outname) and not self.clobber:
+                    logger.warning('Skipping existing file %s', outname)
+                    continue
+                self._fileop.write_binary_file(outname, script_bytes)
+                if self.set_mode:
+                    self._fileop.set_executable_mode([outname])
+            filenames.append(outname)
+
+    variant_separator = '-'
+
+    def get_script_filenames(self, name):
+        result = set()
+        if '' in self.variants:
+            result.add(name)
+        if 'X' in self.variants:
+            result.add('%s%s' % (name, self.version_info[0]))
+        if 'X.Y' in self.variants:
+            result.add('%s%s%s.%s' % (name, self.variant_separator, self.version_info[0], self.version_info[1]))
+        return result
+
+    def _make_script(self, entry, filenames, options=None):
+        post_interp = b''
+        if options:
+            args = options.get('interpreter_args', [])
+            if args:
+                args = ' %s' % ' '.join(args)
+                post_interp = args.encode('utf-8')
+        shebang = self._get_shebang('utf-8', post_interp, options=options)
+        script = self._get_script_text(entry).encode('utf-8')
+        scriptnames = self.get_script_filenames(entry.name)
+        if options and options.get('gui', False):
+            ext = 'pyw'
+        else:
+            ext = 'py'
+        self._write_script(scriptnames, shebang, script, filenames, ext)
+
+    def _copy_script(self, script, filenames):
+        adjust = False
+        script = os.path.join(self.source_dir, convert_path(script))
+        outname = os.path.join(self.target_dir, os.path.basename(script))
+        if not self.force and not self._fileop.newer(script, outname):
+            logger.debug('not copying %s (up-to-date)', script)
+            return
+
+        # Always open the file, but ignore failures in dry-run mode --
+        # that way, we'll get accurate feedback if we can read the
+        # script.
+        try:
+            f = open(script, 'rb')
+        except IOError:  # pragma: no cover
+            if not self.dry_run:
+                raise
+            f = None
+        else:
+            first_line = f.readline()
+            if not first_line:  # pragma: no cover
+                logger.warning('%s is an empty file (skipping)', script)
+                return
+
+            match = FIRST_LINE_RE.match(first_line.replace(b'\r\n', b'\n'))
+            if match:
+                adjust = True
+                post_interp = match.group(1) or b''
+
+        if not adjust:
+            if f:
+                f.close()
+            self._fileop.copy_file(script, outname)
+            if self.set_mode:
+                self._fileop.set_executable_mode([outname])
+            filenames.append(outname)
+        else:
+            logger.info('copying and adjusting %s -> %s', script, self.target_dir)
+            if not self._fileop.dry_run:
+                encoding, lines = detect_encoding(f.readline)
+                f.seek(0)
+                shebang = self._get_shebang(encoding, post_interp)
+                if b'pythonw' in first_line:  # pragma: no cover
+                    ext = 'pyw'
+                else:
+                    ext = 'py'
+                n = os.path.basename(outname)
+                self._write_script([n], shebang, f.read(), filenames, ext)
+            if f:
+                f.close()
+
+    @property
+    def dry_run(self):
+        return self._fileop.dry_run
+
+    @dry_run.setter
+    def dry_run(self, value):
+        self._fileop.dry_run = value
+
+    if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'):  # pragma: no cover
+        # Executable launcher support.
+        # Launchers are from https://bitbucket.org/vinay.sajip/simple_launcher/
+
+        def _get_launcher(self, kind):
+            if struct.calcsize('P') == 8:  # 64-bit
+                bits = '64'
+            else:
+                bits = '32'
+            platform_suffix = '-arm' if get_platform() == 'win-arm64' else ''
+            name = '%s%s%s.exe' % (kind, bits, platform_suffix)
+            if name not in WRAPPERS:
+                msg = ('Unable to find resource %s in package %s' %
+                       (name, DISTLIB_PACKAGE))
+                raise ValueError(msg)
+            return WRAPPERS[name]
+
+    # Public API follows
+
+    def make(self, specification, options=None):
+        """
+        Make a script.
+
+        :param specification: The specification, which is either a valid export
+                              entry specification (to make a script from a
+                              callable) or a filename (to make a script by
+                              copying from a source location).
+        :param options: A dictionary of options controlling script generation.
+        :return: A list of all absolute pathnames written to.
+        """
+        filenames = []
+        entry = get_export_entry(specification)
+        if entry is None:
+            self._copy_script(specification, filenames)
+        else:
+            self._make_script(entry, filenames, options=options)
+        return filenames
+
+    def make_multiple(self, specifications, options=None):
+        """
+        Take a list of specifications and make scripts from them,
+        :param specifications: A list of specifications.
+        :return: A list of all absolute pathnames written to,
+        """
+        filenames = []
+        for specification in specifications:
+            filenames.extend(self.make(specification, options))
+        return filenames
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/t32.exe b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/t32.exe
new file mode 100644
index 0000000..52154f0
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/t32.exe differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/t64-arm.exe b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/t64-arm.exe
new file mode 100644
index 0000000..e1ab8f8
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/t64-arm.exe differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/t64.exe b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/t64.exe
new file mode 100644
index 0000000..e8bebdb
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/t64.exe differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/util.py b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/util.py
new file mode 100644
index 0000000..b120ca8
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/util.py
@@ -0,0 +1,2021 @@
+#
+# Copyright (C) 2012-2026 The Python Software Foundation.
+# See LICENSE.txt and CONTRIBUTORS.txt.
+#
+import codecs
+from collections import deque
+import contextlib
+import csv
+from glob import iglob as std_iglob
+import io
+import json
+import logging
+import os
+import py_compile
+import re
+import socket
+try:
+    import ssl
+except ImportError:  # pragma: no cover
+    ssl = None
+import subprocess
+import sys
+import tarfile
+import tempfile
+import textwrap
+
+try:
+    import threading
+except ImportError:  # pragma: no cover
+    import dummy_threading as threading
+import time
+
+from . import DistlibException
+from .compat import (string_types, text_type, shutil, raw_input, StringIO, cache_from_source, urlopen, urljoin, httplib,
+                     xmlrpclib, HTTPHandler, BaseConfigurator, valid_ident, Container, configparser, URLError, ZipFile,
+                     fsdecode, unquote, urlparse)
+
+logger = logging.getLogger(__name__)
+
+#
+# Requirement parsing code as per PEP 508
+#
+
+IDENTIFIER = re.compile(r'^([\w\.-]+)\s*')
+VERSION_IDENTIFIER = re.compile(r'^([\w\.*+-]+)\s*')
+COMPARE_OP = re.compile(r'^(<=?|>=?|={2,3}|[~!]=)\s*')
+MARKER_OP = re.compile(r'^((<=?)|(>=?)|={2,3}|[~!]=|in|not\s+in)\s*')
+OR = re.compile(r'^or\b\s*')
+AND = re.compile(r'^and\b\s*')
+NON_SPACE = re.compile(r'(\S+)\s*')
+STRING_CHUNK = re.compile(r'([\s\w\.{}()*+#:;,/?!~`@$%^&=|<>\[\]-]+)')
+
+
+def parse_marker(marker_string):
+    """
+    Parse a marker string and return a dictionary containing a marker expression.
+
+    The dictionary will contain keys "op", "lhs" and "rhs" for non-terminals in
+    the expression grammar, or strings. A string contained in quotes is to be
+    interpreted as a literal string, and a string not contained in quotes is a
+    variable (such as os_name).
+    """
+
+    def marker_var(remaining):
+        # either identifier, or literal string
+        m = IDENTIFIER.match(remaining)
+        if m:
+            result = m.groups()[0]
+            remaining = remaining[m.end():]
+        elif not remaining:
+            raise SyntaxError('unexpected end of input')
+        else:
+            q = remaining[0]
+            if q not in '\'"':
+                raise SyntaxError('invalid expression: %s' % remaining)
+            oq = '\'"'.replace(q, '')
+            remaining = remaining[1:]
+            parts = [q]
+            while remaining:
+                # either a string chunk, or oq, or q to terminate
+                if remaining[0] == q:
+                    break
+                elif remaining[0] == oq:
+                    parts.append(oq)
+                    remaining = remaining[1:]
+                else:
+                    m = STRING_CHUNK.match(remaining)
+                    if not m:
+                        raise SyntaxError('error in string literal: %s' % remaining)
+                    parts.append(m.groups()[0])
+                    remaining = remaining[m.end():]
+            else:
+                s = ''.join(parts)
+                raise SyntaxError('unterminated string: %s' % s)
+            parts.append(q)
+            result = ''.join(parts)
+            remaining = remaining[1:].lstrip()  # skip past closing quote
+        return result, remaining
+
+    def marker_expr(remaining):
+        if remaining and remaining[0] == '(':
+            result, remaining = marker(remaining[1:].lstrip())
+            if remaining[0] != ')':
+                raise SyntaxError('unterminated parenthesis: %s' % remaining)
+            remaining = remaining[1:].lstrip()
+        else:
+            lhs, remaining = marker_var(remaining)
+            while remaining:
+                m = MARKER_OP.match(remaining)
+                if not m:
+                    break
+                op = m.groups()[0]
+                remaining = remaining[m.end():]
+                rhs, remaining = marker_var(remaining)
+                lhs = {'op': op, 'lhs': lhs, 'rhs': rhs}
+            result = lhs
+        return result, remaining
+
+    def marker_and(remaining):
+        lhs, remaining = marker_expr(remaining)
+        while remaining:
+            m = AND.match(remaining)
+            if not m:
+                break
+            remaining = remaining[m.end():]
+            rhs, remaining = marker_expr(remaining)
+            lhs = {'op': 'and', 'lhs': lhs, 'rhs': rhs}
+        return lhs, remaining
+
+    def marker(remaining):
+        lhs, remaining = marker_and(remaining)
+        while remaining:
+            m = OR.match(remaining)
+            if not m:
+                break
+            remaining = remaining[m.end():]
+            rhs, remaining = marker_and(remaining)
+            lhs = {'op': 'or', 'lhs': lhs, 'rhs': rhs}
+        return lhs, remaining
+
+    return marker(marker_string)
+
+
+def parse_requirement(req):
+    """
+    Parse a requirement passed in as a string. Return a Container
+    whose attributes contain the various parts of the requirement.
+    """
+    remaining = req.strip()
+    if not remaining or remaining.startswith('#'):
+        return None
+    m = IDENTIFIER.match(remaining)
+    if not m:
+        raise SyntaxError('name expected: %s' % remaining)
+    distname = m.groups()[0]
+    remaining = remaining[m.end():]
+    extras = mark_expr = versions = uri = None
+    if remaining and remaining[0] == '[':
+        i = remaining.find(']', 1)
+        if i < 0:
+            raise SyntaxError('unterminated extra: %s' % remaining)
+        s = remaining[1:i]
+        remaining = remaining[i + 1:].lstrip()
+        extras = []
+        while s:
+            m = IDENTIFIER.match(s)
+            if not m:
+                raise SyntaxError('malformed extra: %s' % s)
+            extras.append(m.groups()[0])
+            s = s[m.end():]
+            if not s:
+                break
+            if s[0] != ',':
+                raise SyntaxError('comma expected in extras: %s' % s)
+            s = s[1:].lstrip()
+        if not extras:
+            extras = None
+    if remaining:
+        if remaining[0] == '@':
+            # it's a URI
+            remaining = remaining[1:].lstrip()
+            m = NON_SPACE.match(remaining)
+            if not m:
+                raise SyntaxError('invalid URI: %s' % remaining)
+            uri = m.groups()[0]
+            t = urlparse(uri)
+            # there are issues with Python and URL parsing, so this test
+            # is a bit crude. See bpo-20271, bpo-23505. Python doesn't
+            # always parse invalid URLs correctly - it should raise
+            # exceptions for malformed URLs
+            if not (t.scheme and t.netloc):
+                raise SyntaxError('Invalid URL: %s' % uri)
+            remaining = remaining[m.end():].lstrip()
+        else:
+
+            def get_versions(ver_remaining):
+                """
+                Return a list of operator, version tuples if any are
+                specified, else None.
+                """
+                m = COMPARE_OP.match(ver_remaining)
+                versions = None
+                if m:
+                    versions = []
+                    while True:
+                        op = m.groups()[0]
+                        ver_remaining = ver_remaining[m.end():]
+                        m = VERSION_IDENTIFIER.match(ver_remaining)
+                        if not m:
+                            raise SyntaxError('invalid version: %s' % ver_remaining)
+                        v = m.groups()[0]
+                        versions.append((op, v))
+                        ver_remaining = ver_remaining[m.end():]
+                        if not ver_remaining or ver_remaining[0] != ',':
+                            break
+                        ver_remaining = ver_remaining[1:].lstrip()
+                        # Some packages have a trailing comma which would break things
+                        # See issue #148
+                        if not ver_remaining:
+                            break
+                        m = COMPARE_OP.match(ver_remaining)
+                        if not m:
+                            raise SyntaxError('invalid constraint: %s' % ver_remaining)
+                    if not versions:
+                        versions = None
+                return versions, ver_remaining
+
+            if remaining[0] != '(':
+                versions, remaining = get_versions(remaining)
+            else:
+                i = remaining.find(')', 1)
+                if i < 0:
+                    raise SyntaxError('unterminated parenthesis: %s' % remaining)
+                s = remaining[1:i]
+                remaining = remaining[i + 1:].lstrip()
+                # As a special diversion from PEP 508, allow a version number
+                # a.b.c in parentheses as a synonym for ~= a.b.c (because this
+                # is allowed in earlier PEPs)
+                if COMPARE_OP.match(s):
+                    versions, _ = get_versions(s)
+                else:
+                    m = VERSION_IDENTIFIER.match(s)
+                    if not m:
+                        raise SyntaxError('invalid constraint: %s' % s)
+                    v = m.groups()[0]
+                    s = s[m.end():].lstrip()
+                    if s:
+                        raise SyntaxError('invalid constraint: %s' % s)
+                    versions = [('~=', v)]
+
+    if remaining:
+        if remaining[0] != ';':
+            raise SyntaxError('invalid requirement: %s' % remaining)
+        remaining = remaining[1:].lstrip()
+
+        mark_expr, remaining = parse_marker(remaining)
+
+    if remaining and remaining[0] != '#':
+        raise SyntaxError('unexpected trailing data: %s' % remaining)
+
+    if not versions:
+        rs = distname
+    else:
+        rs = '%s %s' % (distname, ', '.join(['%s %s' % con for con in versions]))
+    return Container(name=distname, extras=extras, constraints=versions, marker=mark_expr, url=uri, requirement=rs)
+
+
+def get_resources_dests(resources_root, rules):
+    """Find destinations for resources files"""
+
+    def get_rel_path(root, path):
+        # normalizes and returns a lstripped-/-separated path
+        root = root.replace(os.path.sep, '/')
+        path = path.replace(os.path.sep, '/')
+        assert path.startswith(root)
+        return path[len(root):].lstrip('/')
+
+    destinations = {}
+    for base, suffix, dest in rules:
+        prefix = os.path.join(resources_root, base)
+        for abs_base in iglob(prefix):
+            abs_glob = os.path.join(abs_base, suffix)
+            for abs_path in iglob(abs_glob):
+                resource_file = get_rel_path(resources_root, abs_path)
+                if dest is None:  # remove the entry if it was here
+                    destinations.pop(resource_file, None)
+                else:
+                    rel_path = get_rel_path(abs_base, abs_path)
+                    rel_dest = dest.replace(os.path.sep, '/').rstrip('/')
+                    destinations[resource_file] = rel_dest + '/' + rel_path
+    return destinations
+
+
+def in_venv():
+    if hasattr(sys, 'real_prefix'):
+        # virtualenv venvs
+        result = True
+    else:
+        # PEP 405 venvs
+        result = sys.prefix != getattr(sys, 'base_prefix', sys.prefix)
+    return result
+
+
+def get_executable():
+    # The __PYVENV_LAUNCHER__ dance is apparently no longer needed, as
+    # changes to the stub launcher mean that sys.executable always points
+    # to the stub on OS X
+    #    if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__'
+    #                                     in os.environ):
+    #        result =  os.environ['__PYVENV_LAUNCHER__']
+    #    else:
+    #        result = sys.executable
+    #    return result
+    # Avoid normcasing: see issue #143
+    # result = os.path.normcase(sys.executable)
+    result = sys.executable
+    if not isinstance(result, text_type):
+        result = fsdecode(result)
+    return result
+
+
+def proceed(prompt, allowed_chars, error_prompt=None, default=None):
+    p = prompt
+    while True:
+        s = raw_input(p)
+        p = prompt
+        if not s and default:
+            s = default
+        if s:
+            c = s[0].lower()
+            if c in allowed_chars:
+                break
+            if error_prompt:
+                p = '%c: %s\n%s' % (c, error_prompt, prompt)
+    return c
+
+
+def extract_by_key(d, keys):
+    if isinstance(keys, string_types):
+        keys = keys.split()
+    result = {}
+    for key in keys:
+        if key in d:
+            result[key] = d[key]
+    return result
+
+
+def read_exports(stream):
+    if sys.version_info[0] >= 3:
+        # needs to be a text stream
+        stream = codecs.getreader('utf-8')(stream)
+    # Try to load as JSON, falling back on legacy format
+    data = stream.read()
+    stream = StringIO(data)
+    try:
+        jdata = json.load(stream)
+        result = jdata['extensions']['python.exports']['exports']
+        for group, entries in result.items():
+            for k, v in entries.items():
+                s = '%s = %s' % (k, v)
+                entry = get_export_entry(s)
+                assert entry is not None
+                entries[k] = entry
+        return result
+    except Exception:
+        stream.seek(0, 0)
+
+    def read_stream(cp, stream):
+        if hasattr(cp, 'read_file'):
+            cp.read_file(stream)
+        else:
+            cp.readfp(stream)
+
+    cp = configparser.ConfigParser()
+    try:
+        read_stream(cp, stream)
+    except configparser.MissingSectionHeaderError:
+        stream.close()
+        data = textwrap.dedent(data)
+        stream = StringIO(data)
+        read_stream(cp, stream)
+
+    result = {}
+    for key in cp.sections():
+        result[key] = entries = {}
+        for name, value in cp.items(key):
+            s = '%s = %s' % (name, value)
+            entry = get_export_entry(s)
+            assert entry is not None
+            # entry.dist = self
+            entries[name] = entry
+    return result
+
+
+def write_exports(exports, stream):
+    if sys.version_info[0] >= 3:
+        # needs to be a text stream
+        stream = codecs.getwriter('utf-8')(stream)
+    cp = configparser.ConfigParser()
+    for k, v in exports.items():
+        # TODO check k, v for valid values
+        cp.add_section(k)
+        for entry in v.values():
+            if entry.suffix is None:
+                s = entry.prefix
+            else:
+                s = '%s:%s' % (entry.prefix, entry.suffix)
+            if entry.flags:
+                s = '%s [%s]' % (s, ', '.join(entry.flags))
+            cp.set(k, entry.name, s)
+    cp.write(stream)
+
+
+@contextlib.contextmanager
+def tempdir():
+    td = tempfile.mkdtemp()
+    try:
+        yield td
+    finally:
+        shutil.rmtree(td)
+
+
+@contextlib.contextmanager
+def chdir(d):
+    cwd = os.getcwd()
+    try:
+        os.chdir(d)
+        yield
+    finally:
+        os.chdir(cwd)
+
+
+@contextlib.contextmanager
+def socket_timeout(seconds=15):
+    cto = socket.getdefaulttimeout()
+    try:
+        socket.setdefaulttimeout(seconds)
+        yield
+    finally:
+        socket.setdefaulttimeout(cto)
+
+
+class cached_property(object):
+
+    def __init__(self, func):
+        self.func = func
+        # for attr in ('__name__', '__module__', '__doc__'):
+        #     setattr(self, attr, getattr(func, attr, None))
+
+    def __get__(self, obj, cls=None):
+        if obj is None:
+            return self
+        value = self.func(obj)
+        object.__setattr__(obj, self.func.__name__, value)
+        # obj.__dict__[self.func.__name__] = value = self.func(obj)
+        return value
+
+
+def convert_path(pathname):
+    """Return 'pathname' as a name that will work on the native filesystem.
+
+    The path is split on '/' and put back together again using the current
+    directory separator.  Needed because filenames in the setup script are
+    always supplied in Unix style, and have to be converted to the local
+    convention before we can actually use them in the filesystem.  Raises
+    ValueError on non-Unix-ish systems if 'pathname' either starts or
+    ends with a slash.
+    """
+    if os.sep == '/':
+        return pathname
+    if not pathname:
+        return pathname
+    if pathname[0] == '/':
+        raise ValueError("path '%s' cannot be absolute" % pathname)
+    if pathname[-1] == '/':
+        raise ValueError("path '%s' cannot end with '/'" % pathname)
+
+    paths = pathname.split('/')
+    while os.curdir in paths:
+        paths.remove(os.curdir)
+    if not paths:
+        return os.curdir
+    return os.path.join(*paths)
+
+
+class FileOperator(object):
+
+    def __init__(self, dry_run=False):
+        self.dry_run = dry_run
+        self.ensured = set()
+        self._init_record()
+
+    def _init_record(self):
+        self.record = False
+        self.files_written = set()
+        self.dirs_created = set()
+
+    def record_as_written(self, path):
+        if self.record:
+            self.files_written.add(path)
+
+    def newer(self, source, target):
+        """Tell if the target is newer than the source.
+
+        Returns true if 'source' exists and is more recently modified than
+        'target', or if 'source' exists and 'target' doesn't.
+
+        Returns false if both exist and 'target' is the same age or younger
+        than 'source'. Raise PackagingFileError if 'source' does not exist.
+
+        Note that this test is not very accurate: files created in the same
+        second will have the same "age".
+        """
+        if not os.path.exists(source):
+            raise DistlibException("file '%r' does not exist" % os.path.abspath(source))
+        if not os.path.exists(target):
+            return True
+
+        return os.stat(source).st_mtime > os.stat(target).st_mtime
+
+    def copy_file(self, infile, outfile, check=True):
+        """Copy a file respecting dry-run and force flags.
+        """
+        self.ensure_dir(os.path.dirname(outfile))
+        logger.info('Copying %s to %s', infile, outfile)
+        if not self.dry_run:
+            msg = None
+            if check:
+                if os.path.islink(outfile):
+                    msg = '%s is a symlink' % outfile
+                elif os.path.exists(outfile) and not os.path.isfile(outfile):
+                    msg = '%s is a non-regular file' % outfile
+            if msg:
+                raise ValueError(msg + ' which would be overwritten')
+            shutil.copyfile(infile, outfile)
+        self.record_as_written(outfile)
+
+    def copy_stream(self, instream, outfile, encoding=None):
+        assert not os.path.isdir(outfile)
+        self.ensure_dir(os.path.dirname(outfile))
+        logger.info('Copying stream %s to %s', instream, outfile)
+        if not self.dry_run:
+            if encoding is None:
+                outstream = open(outfile, 'wb')
+            else:
+                outstream = codecs.open(outfile, 'w', encoding=encoding)
+            try:
+                shutil.copyfileobj(instream, outstream)
+            finally:
+                outstream.close()
+        self.record_as_written(outfile)
+
+    def write_binary_file(self, path, data):
+        self.ensure_dir(os.path.dirname(path))
+        if not self.dry_run:
+            if os.path.exists(path):
+                os.remove(path)
+            with open(path, 'wb') as f:
+                f.write(data)
+        self.record_as_written(path)
+
+    def write_text_file(self, path, data, encoding):
+        self.write_binary_file(path, data.encode(encoding))
+
+    def set_mode(self, bits, mask, files):
+        if os.name == 'posix' or (os.name == 'java' and os._name == 'posix'):
+            # Set the executable bits (owner, group, and world) on
+            # all the files specified.
+            for f in files:
+                if self.dry_run:
+                    logger.info("changing mode of %s", f)
+                else:
+                    mode = (os.stat(f).st_mode | bits) & mask
+                    logger.info("changing mode of %s to %o", f, mode)
+                    os.chmod(f, mode)
+
+    set_executable_mode = lambda s, f: s.set_mode(0o555, 0o7777, f)
+
+    def ensure_dir(self, path):
+        path = os.path.abspath(path)
+        if path not in self.ensured and not os.path.exists(path):
+            self.ensured.add(path)
+            d, f = os.path.split(path)
+            self.ensure_dir(d)
+            logger.info('Creating %s' % path)
+            if not self.dry_run:
+                os.mkdir(path)
+            if self.record:
+                self.dirs_created.add(path)
+
+    def byte_compile(self, path, optimize=False, force=False, prefix=None, hashed_invalidation=False):
+        if not optimize:
+            optimization = ''
+        else:
+            optimization = '1'
+        dpath = cache_from_source(path, optimization=optimization)
+        logger.info('Byte-compiling %s to %s', path, dpath)
+        if not self.dry_run:
+            if force or self.newer(path, dpath):
+                if not prefix:
+                    diagpath = None
+                else:
+                    assert path.startswith(prefix)
+                    diagpath = path[len(prefix):]
+            compile_kwargs = {}
+            if hashed_invalidation and hasattr(py_compile, 'PycInvalidationMode'):
+                if not isinstance(hashed_invalidation, py_compile.PycInvalidationMode):
+                    hashed_invalidation = py_compile.PycInvalidationMode.CHECKED_HASH
+                compile_kwargs['invalidation_mode'] = hashed_invalidation
+            py_compile.compile(path, dpath, diagpath, True, **compile_kwargs)  # raise error
+        self.record_as_written(dpath)
+        return dpath
+
+    def ensure_removed(self, path):
+        if os.path.exists(path):
+            if os.path.isdir(path) and not os.path.islink(path):
+                logger.debug('Removing directory tree at %s', path)
+                if not self.dry_run:
+                    shutil.rmtree(path)
+                if self.record:
+                    if path in self.dirs_created:
+                        self.dirs_created.remove(path)
+            else:
+                if os.path.islink(path):
+                    s = 'link'
+                else:
+                    s = 'file'
+                logger.debug('Removing %s %s', s, path)
+                if not self.dry_run:
+                    os.remove(path)
+                if self.record:
+                    if path in self.files_written:
+                        self.files_written.remove(path)
+
+    def is_writable(self, path):
+        result = False
+        while not result:
+            if os.path.exists(path):
+                result = os.access(path, os.W_OK)
+                break
+            parent = os.path.dirname(path)
+            if parent == path:
+                break
+            path = parent
+        return result
+
+    def commit(self):
+        """
+        Commit recorded changes, turn off recording, return
+        changes.
+        """
+        assert self.record
+        result = self.files_written, self.dirs_created
+        self._init_record()
+        return result
+
+    def rollback(self):
+        if not self.dry_run:
+            for f in list(self.files_written):
+                if os.path.exists(f):
+                    os.remove(f)
+            # dirs should all be empty now, except perhaps for
+            # __pycache__ subdirs
+            # reverse so that subdirs appear before their parents
+            dirs = sorted(self.dirs_created, reverse=True)
+            for d in dirs:
+                flist = os.listdir(d)
+                if flist:
+                    assert flist == ['__pycache__']
+                    sd = os.path.join(d, flist[0])
+                    os.rmdir(sd)
+                os.rmdir(d)  # should fail if non-empty
+        self._init_record()
+
+
+def resolve(module_name, dotted_path):
+    if module_name in sys.modules:
+        mod = sys.modules[module_name]
+    else:
+        mod = __import__(module_name)
+    if dotted_path is None:
+        result = mod
+    else:
+        parts = dotted_path.split('.')
+        result = getattr(mod, parts.pop(0))
+        for p in parts:
+            result = getattr(result, p)
+    return result
+
+
+class ExportEntry(object):
+
+    def __init__(self, name, prefix, suffix, flags):
+        self.name = name
+        self.prefix = prefix
+        self.suffix = suffix
+        self.flags = flags
+
+    @cached_property
+    def value(self):
+        return resolve(self.prefix, self.suffix)
+
+    def __repr__(self):  # pragma: no cover
+        return '' % (self.name, self.prefix, self.suffix, self.flags)
+
+    def __eq__(self, other):
+        if not isinstance(other, ExportEntry):
+            result = False
+        else:
+            result = (self.name == other.name and self.prefix == other.prefix and self.suffix == other.suffix
+                      and self.flags == other.flags)
+        return result
+
+    __hash__ = object.__hash__
+
+
+ENTRY_RE = re.compile(
+    r'''(?P([^\[]\S*))
+                      \s*=\s*(?P(\w+)([:\.]\w+)*)
+                      \s*(\[\s*(?P[\w-]+(=\w+)?(,\s*\w+(=\w+)?)*)\s*\])?
+                      ''', re.VERBOSE)
+
+
+def get_export_entry(specification):
+    m = ENTRY_RE.search(specification)
+    if not m:
+        result = None
+        if '[' in specification or ']' in specification:
+            raise DistlibException("Invalid specification "
+                                   "'%s'" % specification)
+    else:
+        d = m.groupdict()
+        name = d['name']
+        path = d['callable']
+        colons = path.count(':')
+        if colons == 0:
+            prefix, suffix = path, None
+        else:
+            if colons != 1:
+                raise DistlibException("Invalid specification "
+                                       "'%s'" % specification)
+            prefix, suffix = path.split(':')
+        flags = d['flags']
+        if flags is None:
+            if '[' in specification or ']' in specification:
+                raise DistlibException("Invalid specification "
+                                       "'%s'" % specification)
+            flags = []
+        else:
+            flags = [f.strip() for f in flags.split(',')]
+        result = ExportEntry(name, prefix, suffix, flags)
+    return result
+
+
+def get_cache_base(suffix=None):
+    """
+    Return the default base location for distlib caches. If the directory does
+    not exist, it is created. Use the suffix provided for the base directory,
+    and default to '.distlib' if it isn't provided.
+
+    On Windows, if LOCALAPPDATA is defined in the environment, then it is
+    assumed to be a directory, and will be the parent directory of the result.
+    On POSIX, and on Windows if LOCALAPPDATA is not defined, the user's home
+    directory - using os.expanduser('~') - will be the parent directory of
+    the result.
+
+    The result is just the directory '.distlib' in the parent directory as
+    determined above, or with the name specified with ``suffix``.
+    """
+    if suffix is None:
+        suffix = '.distlib'
+    if os.name == 'nt' and 'LOCALAPPDATA' in os.environ:
+        result = os.path.expandvars('$localappdata')
+    else:
+        # Assume posix, or old Windows
+        result = os.path.expanduser('~')
+    # we use 'isdir' instead of 'exists', because we want to
+    # fail if there's a file with that name
+    if os.path.isdir(result):
+        usable = os.access(result, os.W_OK)
+        if not usable:
+            logger.warning('Directory exists but is not writable: %s', result)
+    else:
+        try:
+            os.makedirs(result)
+            usable = True
+        except OSError:
+            logger.warning('Unable to create %s', result, exc_info=True)
+            usable = False
+    if not usable:
+        result = tempfile.mkdtemp()
+        logger.warning('Default location unusable, using %s', result)
+    return os.path.join(result, suffix)
+
+
+def path_to_cache_dir(path, use_abspath=True):
+    """
+    Convert an absolute path to a directory name for use in a cache.
+
+    The algorithm used is:
+
+    #. On Windows, any ``':'`` in the drive is replaced with ``'---'``.
+    #. Any occurrence of ``os.sep`` is replaced with ``'--'``.
+    #. ``'.cache'`` is appended.
+    """
+    d, p = os.path.splitdrive(os.path.abspath(path) if use_abspath else path)
+    if d:
+        d = d.replace(':', '---')
+    p = p.replace(os.sep, '--')
+    return d + p + '.cache'
+
+
+def ensure_slash(s):
+    if not s.endswith('/'):
+        return s + '/'
+    return s
+
+
+def parse_credentials(netloc):
+    username = password = None
+    if '@' in netloc:
+        prefix, netloc = netloc.rsplit('@', 1)
+        if ':' not in prefix:
+            username = prefix
+        else:
+            username, password = prefix.split(':', 1)
+    if username:
+        username = unquote(username)
+    if password:
+        password = unquote(password)
+    return username, password, netloc
+
+
+def get_process_umask():
+    result = os.umask(0o22)
+    os.umask(result)
+    return result
+
+
+def is_string_sequence(seq):
+    result = True
+    i = None
+    for i, s in enumerate(seq):
+        if not isinstance(s, string_types):
+            result = False
+            break
+    assert i is not None
+    return result
+
+
+PROJECT_NAME_AND_VERSION = re.compile('([a-z0-9_]+([.-][a-z_][a-z0-9_]*)*)-'
+                                      '([a-z0-9_.+-]+)', re.I)
+PYTHON_VERSION = re.compile(r'-py(\d\.?\d?)')
+
+
+def split_filename(filename, project_name=None):
+    """
+    Extract name, version, python version from a filename (no extension)
+
+    Return name, version, pyver or None
+    """
+    result = None
+    pyver = None
+    filename = unquote(filename).replace(' ', '-')
+    m = PYTHON_VERSION.search(filename)
+    if m:
+        pyver = m.group(1)
+        filename = filename[:m.start()]
+    if project_name and len(filename) > len(project_name) + 1:
+        m = re.match(re.escape(project_name) + r'\b', filename)
+        if m:
+            n = m.end()
+            result = filename[:n], filename[n + 1:], pyver
+    if result is None:
+        m = PROJECT_NAME_AND_VERSION.match(filename)
+        if m:
+            result = m.group(1), m.group(3), pyver
+    return result
+
+
+# Allow spaces in name because of legacy dists like "Twisted Core"
+NAME_VERSION_RE = re.compile(r'(?P[\w .-]+)\s*'
+                             r'\(\s*(?P[^\s)]+)\)$')
+
+
+def parse_name_and_version(p):
+    """
+    A utility method used to get name and version from a string.
+
+    From e.g. a Provides-Dist value.
+
+    :param p: A value in a form 'foo (1.0)'
+    :return: The name and version as a tuple.
+    """
+    m = NAME_VERSION_RE.match(p)
+    if not m:
+        raise DistlibException('Ill-formed name/version string: \'%s\'' % p)
+    d = m.groupdict()
+    return d['name'].strip().lower(), d['ver']
+
+
+def get_extras(requested, available):
+    result = set()
+    requested = set(requested or [])
+    available = set(available or [])
+    if '*' in requested:
+        requested.remove('*')
+        result |= available
+    for r in requested:
+        if r == '-':
+            result.add(r)
+        elif r.startswith('-'):
+            unwanted = r[1:]
+            if unwanted not in available:
+                logger.warning('undeclared extra: %s' % unwanted)
+            if unwanted in result:
+                result.remove(unwanted)
+        else:
+            if r not in available:
+                logger.warning('undeclared extra: %s' % r)
+            result.add(r)
+    return result
+
+
+#
+# Extended metadata functionality
+#
+
+
+def _get_external_data(url):
+    result = {}
+    try:
+        # urlopen might fail if it runs into redirections,
+        # because of Python issue #13696. Fixed in locators
+        # using a custom redirect handler.
+        resp = urlopen(url)
+        headers = resp.info()
+        ct = headers.get('Content-Type')
+        if not ct.startswith('application/json'):
+            logger.debug('Unexpected response for JSON request: %s', ct)
+        else:
+            reader = codecs.getreader('utf-8')(resp)
+            # data = reader.read().decode('utf-8')
+            # result = json.loads(data)
+            result = json.load(reader)
+    except Exception as e:
+        logger.exception('Failed to get external data for %s: %s', url, e)
+    return result
+
+
+_external_data_base_url = 'https://www.red-dove.com/pypi/projects/'
+
+
+def get_project_data(name):
+    url = '%s/%s/project.json' % (name[0].upper(), name)
+    url = urljoin(_external_data_base_url, url)
+    result = _get_external_data(url)
+    return result
+
+
+def get_package_data(name, version):
+    url = '%s/%s/package-%s.json' % (name[0].upper(), name, version)
+    url = urljoin(_external_data_base_url, url)
+    return _get_external_data(url)
+
+
+class Cache(object):
+    """
+    A class implementing a cache for resources that need to live in the file system
+    e.g. shared libraries. This class was moved from resources to here because it
+    could be used by other modules, e.g. the wheel module.
+    """
+
+    def __init__(self, base):
+        """
+        Initialise an instance.
+
+        :param base: The base directory where the cache should be located.
+        """
+        # we use 'isdir' instead of 'exists', because we want to
+        # fail if there's a file with that name
+        if not os.path.isdir(base):  # pragma: no cover
+            os.makedirs(base)
+        if (os.stat(base).st_mode & 0o77) != 0:
+            logger.warning('Directory \'%s\' is not private', base)
+        self.base = os.path.abspath(os.path.normpath(base))
+
+    def prefix_to_dir(self, prefix, use_abspath=True):
+        """
+        Converts a resource prefix to a directory name in the cache.
+        """
+        return path_to_cache_dir(prefix, use_abspath=use_abspath)
+
+    def clear(self):
+        """
+        Clear the cache.
+        """
+        not_removed = []
+        for fn in os.listdir(self.base):
+            fn = os.path.join(self.base, fn)
+            try:
+                if os.path.islink(fn) or os.path.isfile(fn):
+                    os.remove(fn)
+                elif os.path.isdir(fn):
+                    shutil.rmtree(fn)
+            except Exception:
+                not_removed.append(fn)
+        return not_removed
+
+
+class EventMixin(object):
+    """
+    A very simple publish/subscribe system.
+    """
+
+    def __init__(self):
+        self._subscribers = {}
+
+    def add(self, event, subscriber, append=True):
+        """
+        Add a subscriber for an event.
+
+        :param event: The name of an event.
+        :param subscriber: The subscriber to be added (and called when the
+                           event is published).
+        :param append: Whether to append or prepend the subscriber to an
+                       existing subscriber list for the event.
+        """
+        subs = self._subscribers
+        if event not in subs:
+            subs[event] = deque([subscriber])
+        else:
+            sq = subs[event]
+            if append:
+                sq.append(subscriber)
+            else:
+                sq.appendleft(subscriber)
+
+    def remove(self, event, subscriber):
+        """
+        Remove a subscriber for an event.
+
+        :param event: The name of an event.
+        :param subscriber: The subscriber to be removed.
+        """
+        subs = self._subscribers
+        if event not in subs:
+            raise ValueError('No subscribers: %r' % event)
+        subs[event].remove(subscriber)
+
+    def get_subscribers(self, event):
+        """
+        Return an iterator for the subscribers for an event.
+        :param event: The event to return subscribers for.
+        """
+        return iter(self._subscribers.get(event, ()))
+
+    def publish(self, event, *args, **kwargs):
+        """
+        Publish a event and return a list of values returned by its
+        subscribers.
+
+        :param event: The event to publish.
+        :param args: The positional arguments to pass to the event's
+                     subscribers.
+        :param kwargs: The keyword arguments to pass to the event's
+                       subscribers.
+        """
+        result = []
+        for subscriber in self.get_subscribers(event):
+            try:
+                value = subscriber(event, *args, **kwargs)
+            except Exception:
+                logger.exception('Exception during event publication')
+                value = None
+            result.append(value)
+        logger.debug('publish %s: args = %s, kwargs = %s, result = %s', event, args, kwargs, result)
+        return result
+
+
+#
+# Simple sequencing
+#
+class Sequencer(object):
+
+    def __init__(self):
+        self._preds = {}
+        self._succs = {}
+        self._nodes = set()  # nodes with no preds/succs
+
+    def add_node(self, node):
+        self._nodes.add(node)
+
+    def remove_node(self, node, edges=False):
+        if node in self._nodes:
+            self._nodes.remove(node)
+        if edges:
+            for p in set(self._preds.get(node, ())):
+                self.remove(p, node)
+            for s in set(self._succs.get(node, ())):
+                self.remove(node, s)
+            # Remove empties
+            for k, v in list(self._preds.items()):
+                if not v:
+                    del self._preds[k]
+            for k, v in list(self._succs.items()):
+                if not v:
+                    del self._succs[k]
+
+    def add(self, pred, succ):
+        assert pred != succ
+        self._preds.setdefault(succ, set()).add(pred)
+        self._succs.setdefault(pred, set()).add(succ)
+
+    def remove(self, pred, succ):
+        assert pred != succ
+        try:
+            preds = self._preds[succ]
+            succs = self._succs[pred]
+        except KeyError:  # pragma: no cover
+            raise ValueError('%r not a successor of anything' % succ)
+        try:
+            preds.remove(pred)
+            succs.remove(succ)
+        except KeyError:  # pragma: no cover
+            raise ValueError('%r not a successor of %r' % (succ, pred))
+
+    def is_step(self, step):
+        return (step in self._preds or step in self._succs or step in self._nodes)
+
+    def get_steps(self, final):
+        if not self.is_step(final):
+            raise ValueError('Unknown: %r' % final)
+        result = []
+        todo = []
+        seen = set()
+        todo.append(final)
+        while todo:
+            step = todo.pop(0)
+            if step in seen:
+                # if a step was already seen,
+                # move it to the end (so it will appear earlier
+                # when reversed on return) ... but not for the
+                # final step, as that would be confusing for
+                # users
+                if step != final:
+                    result.remove(step)
+                    result.append(step)
+            else:
+                seen.add(step)
+                result.append(step)
+                preds = self._preds.get(step, ())
+                todo.extend(preds)
+        return reversed(result)
+
+    @property
+    def strong_connections(self):
+        # http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
+        index_counter = [0]
+        stack = []
+        lowlinks = {}
+        index = {}
+        result = []
+
+        graph = self._succs
+
+        def strongconnect(node):
+            # set the depth index for this node to the smallest unused index
+            index[node] = index_counter[0]
+            lowlinks[node] = index_counter[0]
+            index_counter[0] += 1
+            stack.append(node)
+
+            # Consider successors
+            try:
+                successors = graph[node]
+            except Exception:
+                successors = []
+            for successor in successors:
+                if successor not in lowlinks:
+                    # Successor has not yet been visited
+                    strongconnect(successor)
+                    lowlinks[node] = min(lowlinks[node], lowlinks[successor])
+                elif successor in stack:
+                    # the successor is in the stack and hence in the current
+                    # strongly connected component (SCC)
+                    lowlinks[node] = min(lowlinks[node], index[successor])
+
+            # If `node` is a root node, pop the stack and generate an SCC
+            if lowlinks[node] == index[node]:
+                connected_component = []
+
+                while True:
+                    successor = stack.pop()
+                    connected_component.append(successor)
+                    if successor == node:
+                        break
+                component = tuple(connected_component)
+                # storing the result
+                result.append(component)
+
+        for node in graph:
+            if node not in lowlinks:
+                strongconnect(node)
+
+        return result
+
+    @property
+    def dot(self):
+        result = ['digraph G {']
+        for succ in self._preds:
+            preds = self._preds[succ]
+            for pred in preds:
+                result.append('  %s -> %s;' % (pred, succ))
+        for node in self._nodes:
+            result.append('  %s;' % node)
+        result.append('}')
+        return '\n'.join(result)
+
+
+#
+# Unarchiving functionality for zip, tar, tgz, tbz, whl
+#
+
+ARCHIVE_EXTENSIONS = ('.tar.gz', '.tar.bz2', '.tar', '.zip', '.tgz', '.tbz', '.whl')
+
+
+def unarchive(archive_filename, dest_dir, format=None, check=True):
+
+    def check_path(path, base=None):
+        if not isinstance(path, text_type):
+            path = path.decode('utf-8')
+        if base is None:
+            base = dest_dir
+        p = os.path.abspath(os.path.join(base, path))
+        if not p.startswith(dest_dir) or p[plen] != os.sep:
+            raise ValueError('path outside destination: %r' % p)
+
+    def check_link(member):
+        # A symlink/hardlink member's name is validated like any other
+        # member, but its target (linkname) is not covered by extractall's
+        # name-based handling. An unchecked target lets a later member be
+        # written through the link to a location outside dest_dir. Validate
+        # the resolved target stays within dest_dir. Symlink targets are
+        # relative to the member's own directory; hardlink targets are
+        # relative to the archive root (i.e. dest_dir).
+        if not (member.issym() or member.islnk()):
+            return
+        if member.issym():
+            link_base = os.path.dirname(os.path.join(dest_dir, member.name))
+        else:
+            link_base = dest_dir
+        check_path(member.linkname, base=link_base)
+
+    dest_dir = os.path.abspath(dest_dir)
+    plen = len(dest_dir)
+    archive = None
+    if format is None:
+        if archive_filename.endswith(('.zip', '.whl')):
+            format = 'zip'
+        elif archive_filename.endswith(('.tar.gz', '.tgz')):
+            format = 'tgz'
+            mode = 'r:gz'
+        elif archive_filename.endswith(('.tar.bz2', '.tbz')):
+            format = 'tbz'
+            mode = 'r:bz2'
+        elif archive_filename.endswith('.tar'):
+            format = 'tar'
+            mode = 'r'
+        else:  # pragma: no cover
+            raise ValueError('Unknown format for %r' % archive_filename)
+    try:
+        if format == 'zip':
+            archive = ZipFile(archive_filename, 'r')
+            if check:
+                names = archive.namelist()
+                for name in names:
+                    check_path(name)
+        else:
+            archive = tarfile.open(archive_filename, mode)
+            if check:
+                for member in archive.getmembers():
+                    check_path(member.name)
+                    check_link(member)
+        if format != 'zip' and sys.version_info[0] < 3:
+            # See Python issue 17153. If the dest path contains Unicode,
+            # tarfile extraction fails on Python 2.x if a member path name
+            # contains non-ASCII characters - it leads to an implicit
+            # bytes -> unicode conversion using ASCII to decode.
+            for tarinfo in archive.getmembers():
+                if not isinstance(tarinfo.name, text_type):
+                    tarinfo.name = tarinfo.name.decode('utf-8')
+
+        # Limit extraction of dangerous items, if this Python
+        # allows it easily. If not, just trust the input.
+        # See: https://docs.python.org/3/library/tarfile.html#extraction-filters
+        def extraction_filter(member, path):
+            """Run tarfile.tar_filter, but raise the expected ValueError"""
+            # This is only called if the current Python has tarfile filters
+            try:
+                return tarfile.tar_filter(member, path)
+            except tarfile.FilterError as exc:
+                raise ValueError(str(exc))
+
+        archive.extraction_filter = extraction_filter
+
+        archive.extractall(dest_dir)
+
+    finally:
+        if archive:
+            archive.close()
+
+
+def zip_dir(directory):
+    """zip a directory tree into a BytesIO object"""
+    result = io.BytesIO()
+    dlen = len(directory)
+    with ZipFile(result, "w") as zf:
+        for root, dirs, files in os.walk(directory):
+            for name in files:
+                full = os.path.join(root, name)
+                rel = root[dlen:]
+                dest = os.path.join(rel, name)
+                zf.write(full, dest)
+    return result
+
+
+#
+# Simple progress bar
+#
+
+UNITS = ('', 'K', 'M', 'G', 'T', 'P')
+
+
+class Progress(object):
+    unknown = 'UNKNOWN'
+
+    def __init__(self, minval=0, maxval=100):
+        assert maxval is None or maxval >= minval
+        self.min = self.cur = minval
+        self.max = maxval
+        self.started = None
+        self.elapsed = 0
+        self.done = False
+
+    def update(self, curval):
+        assert self.min <= curval
+        assert self.max is None or curval <= self.max
+        self.cur = curval
+        now = time.time()
+        if self.started is None:
+            self.started = now
+        else:
+            self.elapsed = now - self.started
+
+    def increment(self, incr):
+        assert incr >= 0
+        self.update(self.cur + incr)
+
+    def start(self):
+        self.update(self.min)
+        return self
+
+    def stop(self):
+        if self.max is not None:
+            self.update(self.max)
+        self.done = True
+
+    @property
+    def maximum(self):
+        return self.unknown if self.max is None else self.max
+
+    @property
+    def percentage(self):
+        if self.done:
+            result = '100 %'
+        elif self.max is None:
+            result = ' ?? %'
+        else:
+            v = 100.0 * (self.cur - self.min) / (self.max - self.min)
+            result = '%3d %%' % v
+        return result
+
+    def format_duration(self, duration):
+        if (duration <= 0) and self.max is None or self.cur == self.min:
+            result = '??:??:??'
+        # elif duration < 1:
+        #     result = '--:--:--'
+        else:
+            result = time.strftime('%H:%M:%S', time.gmtime(duration))
+        return result
+
+    @property
+    def ETA(self):
+        if self.done:
+            prefix = 'Done'
+            t = self.elapsed
+            # import pdb; pdb.set_trace()
+        else:
+            prefix = 'ETA '
+            if self.max is None:
+                t = -1
+            elif self.elapsed == 0 or (self.cur == self.min):
+                t = 0
+            else:
+                # import pdb; pdb.set_trace()
+                t = float(self.max - self.min)
+                t /= self.cur - self.min
+                t = (t - 1) * self.elapsed
+        return '%s: %s' % (prefix, self.format_duration(t))
+
+    @property
+    def speed(self):
+        if self.elapsed == 0:
+            result = 0.0
+        else:
+            result = (self.cur - self.min) / self.elapsed
+        for unit in UNITS:
+            if result < 1000:
+                break
+            result /= 1000.0
+        return '%d %sB/s' % (result, unit)
+
+
+#
+# Glob functionality
+#
+
+RICH_GLOB = re.compile(r'\{([^}]*)\}')
+_CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]')
+_CHECK_MISMATCH_SET = re.compile(r'^[^{]*\}|\{[^}]*$')
+
+
+def iglob(path_glob):
+    """Extended globbing function that supports ** and {opt1,opt2,opt3}."""
+    if _CHECK_RECURSIVE_GLOB.search(path_glob):
+        msg = """invalid glob %r: recursive glob "**" must be used alone"""
+        raise ValueError(msg % path_glob)
+    if _CHECK_MISMATCH_SET.search(path_glob):
+        msg = """invalid glob %r: mismatching set marker '{' or '}'"""
+        raise ValueError(msg % path_glob)
+    return _iglob(path_glob)
+
+
+def _iglob(path_glob):
+    rich_path_glob = RICH_GLOB.split(path_glob, 1)
+    if len(rich_path_glob) > 1:
+        assert len(rich_path_glob) == 3, rich_path_glob
+        prefix, set, suffix = rich_path_glob
+        for item in set.split(','):
+            for path in _iglob(''.join((prefix, item, suffix))):
+                yield path
+    else:
+        if '**' not in path_glob:
+            for item in std_iglob(path_glob):
+                yield item
+        else:
+            prefix, radical = path_glob.split('**', 1)
+            if prefix == '':
+                prefix = '.'
+            if radical == '':
+                radical = '*'
+            else:
+                # we support both
+                radical = radical.lstrip('/')
+                radical = radical.lstrip('\\')
+            for path, dir, files in os.walk(prefix):
+                path = os.path.normpath(path)
+                for fn in _iglob(os.path.join(path, radical)):
+                    yield fn
+
+
+if ssl:
+    from .compat import (HTTPSHandler as BaseHTTPSHandler, match_hostname, CertificateError)
+
+    #
+    # HTTPSConnection which verifies certificates/matches domains
+    #
+
+
+    class HTTPSConnection(httplib.HTTPSConnection):
+        ca_certs = None  # set this to the path to the certs file (.pem)
+        check_domain = True  # only used if ca_certs is not None
+
+        # noinspection PyPropertyAccess
+        def connect(self):
+            sock = socket.create_connection((self.host, self.port), self.timeout)
+            if getattr(self, '_tunnel_host', False):
+                self.sock = sock
+                self._tunnel()
+
+            context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+            if hasattr(ssl, 'OP_NO_SSLv2'):
+                context.options |= ssl.OP_NO_SSLv2
+            if getattr(self, 'cert_file', None):
+                context.load_cert_chain(self.cert_file, self.key_file)
+            kwargs = {}
+            if self.ca_certs:
+                context.verify_mode = ssl.CERT_REQUIRED
+                context.load_verify_locations(cafile=self.ca_certs)
+                if getattr(ssl, 'HAS_SNI', False):
+                    kwargs['server_hostname'] = self.host
+
+            self.sock = context.wrap_socket(sock, **kwargs)
+            if self.ca_certs and self.check_domain:
+                try:
+                    match_hostname(self.sock.getpeercert(), self.host)
+                    logger.debug('Host verified: %s', self.host)
+                except CertificateError:  # pragma: no cover
+                    self.sock.shutdown(socket.SHUT_RDWR)
+                    self.sock.close()
+                    raise
+
+    class HTTPSHandler(BaseHTTPSHandler):
+
+        def __init__(self, ca_certs, check_domain=True):
+            BaseHTTPSHandler.__init__(self)
+            self.ca_certs = ca_certs
+            self.check_domain = check_domain
+
+        def _conn_maker(self, *args, **kwargs):
+            """
+            This is called to create a connection instance. Normally you'd
+            pass a connection class to do_open, but it doesn't actually check for
+            a class, and just expects a callable. As long as we behave just as a
+            constructor would have, we should be OK. If it ever changes so that
+            we *must* pass a class, we'll create an UnsafeHTTPSConnection class
+            which just sets check_domain to False in the class definition, and
+            choose which one to pass to do_open.
+            """
+            result = HTTPSConnection(*args, **kwargs)
+            if self.ca_certs:
+                result.ca_certs = self.ca_certs
+                result.check_domain = self.check_domain
+            return result
+
+        def https_open(self, req):
+            try:
+                return self.do_open(self._conn_maker, req)
+            except URLError as e:
+                if 'certificate verify failed' in str(e.reason):
+                    raise CertificateError('Unable to verify server certificate '
+                                           'for %s' % req.host)
+                else:
+                    raise
+
+    #
+    # To prevent against mixing HTTP traffic with HTTPS (examples: A Man-In-The-
+    # Middle proxy using HTTP listens on port 443, or an index mistakenly serves
+    # HTML containing a http://xyz link when it should be https://xyz),
+    # you can use the following handler class, which does not allow HTTP traffic.
+    #
+    # It works by inheriting from HTTPHandler - so build_opener won't add a
+    # handler for HTTP itself.
+    #
+    class HTTPSOnlyHandler(HTTPSHandler, HTTPHandler):
+
+        def http_open(self, req):
+            raise URLError('Unexpected HTTP request on what should be a secure '
+                           'connection: %s' % req)
+
+
+#
+# XML-RPC with timeouts
+#
+class Transport(xmlrpclib.Transport):
+
+    def __init__(self, timeout, use_datetime=0):
+        self.timeout = timeout
+        xmlrpclib.Transport.__init__(self, use_datetime)
+
+    def make_connection(self, host):
+        h, eh, x509 = self.get_host_info(host)
+        if not self._connection or host != self._connection[0]:
+            self._extra_headers = eh
+            self._connection = host, httplib.HTTPConnection(h)
+        return self._connection[1]
+
+
+if ssl:
+
+    class SafeTransport(xmlrpclib.SafeTransport):
+
+        def __init__(self, timeout, use_datetime=0):
+            self.timeout = timeout
+            xmlrpclib.SafeTransport.__init__(self, use_datetime)
+
+        def make_connection(self, host):
+            h, eh, kwargs = self.get_host_info(host)
+            if not kwargs:
+                kwargs = {}
+            kwargs['timeout'] = self.timeout
+            if not self._connection or host != self._connection[0]:
+                self._extra_headers = eh
+                self._connection = host, httplib.HTTPSConnection(h, None, **kwargs)
+            return self._connection[1]
+
+
+class ServerProxy(xmlrpclib.ServerProxy):
+
+    def __init__(self, uri, **kwargs):
+        self.timeout = timeout = kwargs.pop('timeout', None)
+        # The above classes only come into play if a timeout
+        # is specified
+        if timeout is not None:
+            # scheme = splittype(uri)  # deprecated as of Python 3.8
+            scheme = urlparse(uri)[0]
+            use_datetime = kwargs.get('use_datetime', 0)
+            if scheme == 'https':
+                tcls = SafeTransport
+            else:
+                tcls = Transport
+            kwargs['transport'] = t = tcls(timeout, use_datetime=use_datetime)
+            self.transport = t
+        xmlrpclib.ServerProxy.__init__(self, uri, **kwargs)
+
+
+#
+# CSV functionality. This is provided because on 2.x, the csv module can't
+# handle Unicode. However, we need to deal with Unicode in e.g. RECORD files.
+#
+
+
+def _csv_open(fn, mode, **kwargs):
+    if sys.version_info[0] < 3:
+        mode += 'b'
+    else:
+        kwargs['newline'] = ''
+        # Python 3 determines encoding from locale. Force 'utf-8'
+        # file encoding to match other forced utf-8 encoding
+        kwargs['encoding'] = 'utf-8'
+    return open(fn, mode, **kwargs)
+
+
+class CSVBase(object):
+    defaults = {
+        'delimiter': str(','),  # The strs are used because we need native
+        'quotechar': str('"'),  # str in the csv API (2.x won't take
+        'lineterminator': str('\n')  # Unicode)
+    }
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, *exc_info):
+        self.stream.close()
+
+
+class CSVReader(CSVBase):
+
+    def __init__(self, **kwargs):
+        if 'stream' in kwargs:
+            stream = kwargs['stream']
+            if sys.version_info[0] >= 3:
+                # needs to be a text stream
+                stream = codecs.getreader('utf-8')(stream)
+            self.stream = stream
+        else:
+            self.stream = _csv_open(kwargs['path'], 'r')
+        self.reader = csv.reader(self.stream, **self.defaults)
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        result = next(self.reader)
+        if sys.version_info[0] < 3:
+            for i, item in enumerate(result):
+                if not isinstance(item, text_type):
+                    result[i] = item.decode('utf-8')
+        return result
+
+    __next__ = next
+
+
+class CSVWriter(CSVBase):
+
+    def __init__(self, fn, **kwargs):
+        self.stream = _csv_open(fn, 'w')
+        self.writer = csv.writer(self.stream, **self.defaults)
+
+    def writerow(self, row):
+        if sys.version_info[0] < 3:
+            r = []
+            for item in row:
+                if isinstance(item, text_type):
+                    item = item.encode('utf-8')
+                r.append(item)
+            row = r
+        self.writer.writerow(row)
+
+
+#
+#   Configurator functionality
+#
+
+
+class Configurator(BaseConfigurator):
+
+    value_converters = dict(BaseConfigurator.value_converters)
+    value_converters['inc'] = 'inc_convert'
+
+    def __init__(self, config, base=None):
+        super(Configurator, self).__init__(config)
+        self.base = base or os.getcwd()
+
+    def configure_custom(self, config):
+
+        def convert(o):
+            if isinstance(o, (list, tuple)):
+                result = type(o)([convert(i) for i in o])
+            elif isinstance(o, dict):
+                if '()' in o:
+                    result = self.configure_custom(o)
+                else:
+                    result = {}
+                    for k in o:
+                        result[k] = convert(o[k])
+            else:
+                result = self.convert(o)
+            return result
+
+        c = config.pop('()')
+        if not callable(c):
+            c = self.resolve(c)
+        props = config.pop('.', None)
+        # Check for valid identifiers
+        args = config.pop('[]', ())
+        if args:
+            args = tuple([convert(o) for o in args])
+        items = [(k, convert(config[k])) for k in config if valid_ident(k)]
+        kwargs = dict(items)
+        result = c(*args, **kwargs)
+        if props:
+            for n, v in props.items():
+                setattr(result, n, convert(v))
+        return result
+
+    def __getitem__(self, key):
+        result = self.config[key]
+        if isinstance(result, dict) and '()' in result:
+            self.config[key] = result = self.configure_custom(result)
+        return result
+
+    def inc_convert(self, value):
+        """Default converter for the inc:// protocol."""
+        if not os.path.isabs(value):
+            value = os.path.join(self.base, value)
+        with codecs.open(value, 'r', encoding='utf-8') as f:
+            result = json.load(f)
+        return result
+
+
+class SubprocessMixin(object):
+    """
+    Mixin for running subprocesses and capturing their output
+    """
+
+    def __init__(self, verbose=False, progress=None):
+        self.verbose = verbose
+        self.progress = progress
+
+    def reader(self, stream, context):
+        """
+        Read lines from a subprocess' output stream and either pass to a progress
+        callable (if specified) or write progress information to sys.stderr.
+        """
+        progress = self.progress
+        verbose = self.verbose
+        while True:
+            s = stream.readline()
+            if not s:
+                break
+            if progress is not None:
+                progress(s, context)
+            else:
+                if not verbose:
+                    sys.stderr.write('.')
+                else:
+                    sys.stderr.write(s.decode('utf-8'))
+                sys.stderr.flush()
+        stream.close()
+
+    def run_command(self, cmd, **kwargs):
+        p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
+        t1 = threading.Thread(target=self.reader, args=(p.stdout, 'stdout'))
+        t1.start()
+        t2 = threading.Thread(target=self.reader, args=(p.stderr, 'stderr'))
+        t2.start()
+        p.wait()
+        t1.join()
+        t2.join()
+        if self.progress is not None:
+            self.progress('done.', 'main')
+        elif self.verbose:
+            sys.stderr.write('done.\n')
+        return p
+
+
+def normalize_name(name):
+    """Normalize a python package name a la PEP 503"""
+    # https://peps.python.org/pep-0503/#normalized-names
+    # Emulates re.sub(r"[-_.]+", "-", name).lower()
+    # Much faster than re, and even faster than str.translate
+    value = name.lower().replace("_", "-").replace(".", "-")
+    while "--" in value:
+        value = value.replace("--", "-")
+    return value
+
+
+# def _get_pypirc_command():
+# """
+# Get the distutils command for interacting with PyPI configurations.
+# :return: the command.
+# """
+# from distutils.core import Distribution
+# from distutils.config import PyPIRCCommand
+# d = Distribution()
+# return PyPIRCCommand(d)
+
+
+class PyPIRCFile(object):
+
+    DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/'
+    DEFAULT_REALM = 'pypi'
+
+    def __init__(self, fn=None, url=None):
+        if fn is None:
+            fn = os.path.join(os.path.expanduser('~'), '.pypirc')
+        self.filename = fn
+        self.url = url
+
+    def read(self):
+        result = {}
+
+        if os.path.exists(self.filename):
+            repository = self.url or self.DEFAULT_REPOSITORY
+
+            config = configparser.RawConfigParser()
+            config.read(self.filename)
+            sections = config.sections()
+            if 'distutils' in sections:
+                # let's get the list of servers
+                index_servers = config.get('distutils', 'index-servers')
+                _servers = [server.strip() for server in index_servers.split('\n') if server.strip() != '']
+                if _servers == []:
+                    # nothing set, let's try to get the default pypi
+                    if 'pypi' in sections:
+                        _servers = ['pypi']
+                else:
+                    for server in _servers:
+                        result = {'server': server}
+                        result['username'] = config.get(server, 'username')
+
+                        # optional params
+                        for key, default in (('repository', self.DEFAULT_REPOSITORY), ('realm', self.DEFAULT_REALM),
+                                             ('password', None)):
+                            if config.has_option(server, key):
+                                result[key] = config.get(server, key)
+                            else:
+                                result[key] = default
+
+                        # work around people having "repository" for the "pypi"
+                        # section of their config set to the HTTP (rather than
+                        # HTTPS) URL
+                        if (server == 'pypi' and repository in (self.DEFAULT_REPOSITORY, 'pypi')):
+                            result['repository'] = self.DEFAULT_REPOSITORY
+                        elif (result['server'] != repository and result['repository'] != repository):
+                            result = {}
+            elif 'server-login' in sections:
+                # old format
+                server = 'server-login'
+                if config.has_option(server, 'repository'):
+                    repository = config.get(server, 'repository')
+                else:
+                    repository = self.DEFAULT_REPOSITORY
+                result = {
+                    'username': config.get(server, 'username'),
+                    'password': config.get(server, 'password'),
+                    'repository': repository,
+                    'server': server,
+                    'realm': self.DEFAULT_REALM
+                }
+        return result
+
+    def update(self, username, password):
+        # import pdb; pdb.set_trace()
+        config = configparser.RawConfigParser()
+        fn = self.filename
+        config.read(fn)
+        if not config.has_section('pypi'):
+            config.add_section('pypi')
+        config.set('pypi', 'username', username)
+        config.set('pypi', 'password', password)
+        with open(fn, 'w') as f:
+            config.write(f)
+
+
+def _load_pypirc(index):
+    """
+    Read the PyPI access configuration as supported by distutils.
+    """
+    return PyPIRCFile(url=index.url).read()
+
+
+def _store_pypirc(index):
+    PyPIRCFile().update(index.username, index.password)
+
+
+#
+# get_platform()/get_host_platform() copied from Python 3.10.a0 source, with some minor
+# tweaks
+#
+
+
+def get_host_platform():
+    """Return a string that identifies the current platform.  This is used mainly to
+    distinguish platform-specific build directories and platform-specific built
+    distributions.  Typically includes the OS name and version and the
+    architecture (as supplied by 'os.uname()'), although the exact information
+    included depends on the OS; eg. on Linux, the kernel version isn't
+    particularly important.
+
+    Examples of returned values:
+       linux-i586
+       linux-alpha (?)
+       solaris-2.6-sun4u
+
+    Windows will return one of:
+       win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
+       win32 (all others - specifically, sys.platform is returned)
+
+    For other non-POSIX platforms, currently just returns 'sys.platform'.
+
+    """
+    if os.name == 'nt':
+        if 'amd64' in sys.version.lower():
+            return 'win-amd64'
+        if '(arm)' in sys.version.lower():
+            return 'win-arm32'
+        if '(arm64)' in sys.version.lower():
+            return 'win-arm64'
+        return sys.platform
+
+    # Set for cross builds explicitly
+    if "_PYTHON_HOST_PLATFORM" in os.environ:
+        return os.environ["_PYTHON_HOST_PLATFORM"]
+
+    if os.name != 'posix' or not hasattr(os, 'uname'):
+        # XXX what about the architecture? NT is Intel or Alpha,
+        # Mac OS is M68k or PPC, etc.
+        return sys.platform
+
+    # Try to distinguish various flavours of Unix
+
+    (osname, host, release, version, machine) = os.uname()
+
+    # Convert the OS name to lowercase, remove '/' characters, and translate
+    # spaces (for "Power Macintosh")
+    osname = osname.lower().replace('/', '')
+    machine = machine.replace(' ', '_').replace('/', '-')
+
+    if osname[:5] == 'linux':
+        # At least on Linux/Intel, 'machine' is the processor --
+        # i386, etc.
+        # XXX what about Alpha, SPARC, etc?
+        return "%s-%s" % (osname, machine)
+
+    elif osname[:5] == 'sunos':
+        if release[0] >= '5':  # SunOS 5 == Solaris 2
+            osname = 'solaris'
+            release = '%d.%s' % (int(release[0]) - 3, release[2:])
+            # We can't use 'platform.architecture()[0]' because a
+            # bootstrap problem. We use a dict to get an error
+            # if some suspicious happens.
+            bitness = {2147483647: '32bit', 9223372036854775807: '64bit'}
+            machine += '.%s' % bitness[sys.maxsize]
+        # fall through to standard osname-release-machine representation
+    elif osname[:3] == 'aix':
+        from _aix_support import aix_platform
+        return aix_platform()
+    elif osname[:6] == 'cygwin':
+        osname = 'cygwin'
+        rel_re = re.compile(r'[\d.]+', re.ASCII)
+        m = rel_re.match(release)
+        if m:
+            release = m.group()
+    elif osname[:6] == 'darwin':
+        import _osx_support
+        try:
+            from distutils import sysconfig
+        except ImportError:
+            import sysconfig
+        osname, release, machine = _osx_support.get_platform_osx(sysconfig.get_config_vars(), osname, release, machine)
+
+    return '%s-%s-%s' % (osname, release, machine)
+
+
+_TARGET_TO_PLAT = {
+    'x86': 'win32',
+    'x64': 'win-amd64',
+    'arm': 'win-arm32',
+}
+
+
+def get_platform():
+    if os.name != 'nt':
+        return get_host_platform()
+    cross_compilation_target = os.environ.get('VSCMD_ARG_TGT_ARCH')
+    if cross_compilation_target not in _TARGET_TO_PLAT:
+        return get_host_platform()
+    return _TARGET_TO_PLAT[cross_compilation_target]
+
+
+def is_in_directory(path, target):
+    """
+    Check if a path is inside a target directory. This doesn't check case (might be an issue on Windows)
+    """
+    path = os.path.abspath(path)
+    target = os.path.abspath(target)
+    return path == target or path.startswith(target + os.sep)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/w32.exe b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/w32.exe
new file mode 100644
index 0000000..4ee2d3a
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/w32.exe differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/w64-arm.exe b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/w64-arm.exe
new file mode 100644
index 0000000..951d581
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/w64-arm.exe differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distlib/w64.exe b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/w64.exe
new file mode 100644
index 0000000..5763076
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distlib/w64.exe differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distro/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/distro/LICENSE
new file mode 100644
index 0000000..e06d208
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/distro/LICENSE
@@ -0,0 +1,202 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distro/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/distro/__init__.py
new file mode 100644
index 0000000..7686fe8
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/distro/__init__.py
@@ -0,0 +1,54 @@
+from .distro import (
+    NORMALIZED_DISTRO_ID,
+    NORMALIZED_LSB_ID,
+    NORMALIZED_OS_ID,
+    LinuxDistribution,
+    __version__,
+    build_number,
+    codename,
+    distro_release_attr,
+    distro_release_info,
+    id,
+    info,
+    like,
+    linux_distribution,
+    lsb_release_attr,
+    lsb_release_info,
+    major_version,
+    minor_version,
+    name,
+    os_release_attr,
+    os_release_info,
+    uname_attr,
+    uname_info,
+    version,
+    version_parts,
+)
+
+__all__ = [
+    "NORMALIZED_DISTRO_ID",
+    "NORMALIZED_LSB_ID",
+    "NORMALIZED_OS_ID",
+    "LinuxDistribution",
+    "build_number",
+    "codename",
+    "distro_release_attr",
+    "distro_release_info",
+    "id",
+    "info",
+    "like",
+    "linux_distribution",
+    "lsb_release_attr",
+    "lsb_release_info",
+    "major_version",
+    "minor_version",
+    "name",
+    "os_release_attr",
+    "os_release_info",
+    "uname_attr",
+    "uname_info",
+    "version",
+    "version_parts",
+]
+
+__version__ = __version__
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distro/__main__.py b/venv/lib/python3.12/site-packages/pip/_vendor/distro/__main__.py
new file mode 100644
index 0000000..0c01d5b
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/distro/__main__.py
@@ -0,0 +1,4 @@
+from .distro import main
+
+if __name__ == "__main__":
+    main()
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..b6901b5
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__main__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__main__.cpython-312.pyc
new file mode 100644
index 0000000..f9b8405
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__main__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/distro.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/distro.cpython-312.pyc
new file mode 100644
index 0000000..9371ebc
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/distro.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distro/distro.py b/venv/lib/python3.12/site-packages/pip/_vendor/distro/distro.py
new file mode 100644
index 0000000..78ccdfa
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/distro/distro.py
@@ -0,0 +1,1403 @@
+#!/usr/bin/env python
+# Copyright 2015-2021 Nir Cohen
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+The ``distro`` package (``distro`` stands for Linux Distribution) provides
+information about the Linux distribution it runs on, such as a reliable
+machine-readable distro ID, or version information.
+
+It is the recommended replacement for Python's original
+:py:func:`platform.linux_distribution` function, but it provides much more
+functionality. An alternative implementation became necessary because Python
+3.5 deprecated this function, and Python 3.8 removed it altogether. Its
+predecessor function :py:func:`platform.dist` was already deprecated since
+Python 2.6 and removed in Python 3.8. Still, there are many cases in which
+access to OS distribution information is needed. See `Python issue 1322
+`_ for more information.
+"""
+
+import argparse
+import json
+import logging
+import os
+import re
+import shlex
+import subprocess
+import sys
+import warnings
+from typing import (
+    Any,
+    Callable,
+    Dict,
+    Iterable,
+    Optional,
+    Sequence,
+    TextIO,
+    Tuple,
+    Type,
+)
+
+try:
+    from typing import TypedDict
+except ImportError:
+    # Python 3.7
+    TypedDict = dict
+
+__version__ = "1.9.0"
+
+
+class VersionDict(TypedDict):
+    major: str
+    minor: str
+    build_number: str
+
+
+class InfoDict(TypedDict):
+    id: str
+    version: str
+    version_parts: VersionDict
+    like: str
+    codename: str
+
+
+_UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc")
+_UNIXUSRLIBDIR = os.environ.get("UNIXUSRLIBDIR", "/usr/lib")
+_OS_RELEASE_BASENAME = "os-release"
+
+#: Translation table for normalizing the "ID" attribute defined in os-release
+#: files, for use by the :func:`distro.id` method.
+#:
+#: * Key: Value as defined in the os-release file, translated to lower case,
+#:   with blanks translated to underscores.
+#:
+#: * Value: Normalized value.
+NORMALIZED_OS_ID = {
+    "ol": "oracle",  # Oracle Linux
+    "opensuse-leap": "opensuse",  # Newer versions of OpenSuSE report as opensuse-leap
+}
+
+#: Translation table for normalizing the "Distributor ID" attribute returned by
+#: the lsb_release command, for use by the :func:`distro.id` method.
+#:
+#: * Key: Value as returned by the lsb_release command, translated to lower
+#:   case, with blanks translated to underscores.
+#:
+#: * Value: Normalized value.
+NORMALIZED_LSB_ID = {
+    "enterpriseenterpriseas": "oracle",  # Oracle Enterprise Linux 4
+    "enterpriseenterpriseserver": "oracle",  # Oracle Linux 5
+    "redhatenterpriseworkstation": "rhel",  # RHEL 6, 7 Workstation
+    "redhatenterpriseserver": "rhel",  # RHEL 6, 7 Server
+    "redhatenterprisecomputenode": "rhel",  # RHEL 6 ComputeNode
+}
+
+#: Translation table for normalizing the distro ID derived from the file name
+#: of distro release files, for use by the :func:`distro.id` method.
+#:
+#: * Key: Value as derived from the file name of a distro release file,
+#:   translated to lower case, with blanks translated to underscores.
+#:
+#: * Value: Normalized value.
+NORMALIZED_DISTRO_ID = {
+    "redhat": "rhel",  # RHEL 6.x, 7.x
+}
+
+# Pattern for content of distro release file (reversed)
+_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile(
+    r"(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)"
+)
+
+# Pattern for base file name of distro release file
+_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$")
+
+# Base file names to be looked up for if _UNIXCONFDIR is not readable.
+_DISTRO_RELEASE_BASENAMES = [
+    "SuSE-release",
+    "altlinux-release",
+    "arch-release",
+    "base-release",
+    "centos-release",
+    "fedora-release",
+    "gentoo-release",
+    "mageia-release",
+    "mandrake-release",
+    "mandriva-release",
+    "mandrivalinux-release",
+    "manjaro-release",
+    "oracle-release",
+    "redhat-release",
+    "rocky-release",
+    "sl-release",
+    "slackware-version",
+]
+
+# Base file names to be ignored when searching for distro release file
+_DISTRO_RELEASE_IGNORE_BASENAMES = (
+    "debian_version",
+    "lsb-release",
+    "oem-release",
+    _OS_RELEASE_BASENAME,
+    "system-release",
+    "plesk-release",
+    "iredmail-release",
+    "board-release",
+    "ec2_version",
+)
+
+
+def linux_distribution(full_distribution_name: bool = True) -> Tuple[str, str, str]:
+    """
+    .. deprecated:: 1.6.0
+
+        :func:`distro.linux_distribution()` is deprecated. It should only be
+        used as a compatibility shim with Python's
+        :py:func:`platform.linux_distribution()`. Please use :func:`distro.id`,
+        :func:`distro.version` and :func:`distro.name` instead.
+
+    Return information about the current OS distribution as a tuple
+    ``(id_name, version, codename)`` with items as follows:
+
+    * ``id_name``:  If *full_distribution_name* is false, the result of
+      :func:`distro.id`. Otherwise, the result of :func:`distro.name`.
+
+    * ``version``:  The result of :func:`distro.version`.
+
+    * ``codename``:  The extra item (usually in parentheses) after the
+      os-release version number, or the result of :func:`distro.codename`.
+
+    The interface of this function is compatible with the original
+    :py:func:`platform.linux_distribution` function, supporting a subset of
+    its parameters.
+
+    The data it returns may not exactly be the same, because it uses more data
+    sources than the original function, and that may lead to different data if
+    the OS distribution is not consistent across multiple data sources it
+    provides (there are indeed such distributions ...).
+
+    Another reason for differences is the fact that the :func:`distro.id`
+    method normalizes the distro ID string to a reliable machine-readable value
+    for a number of popular OS distributions.
+    """
+    warnings.warn(
+        "distro.linux_distribution() is deprecated. It should only be used as a "
+        "compatibility shim with Python's platform.linux_distribution(). Please use "
+        "distro.id(), distro.version() and distro.name() instead.",
+        DeprecationWarning,
+        stacklevel=2,
+    )
+    return _distro.linux_distribution(full_distribution_name)
+
+
+def id() -> str:
+    """
+    Return the distro ID of the current distribution, as a
+    machine-readable string.
+
+    For a number of OS distributions, the returned distro ID value is
+    *reliable*, in the sense that it is documented and that it does not change
+    across releases of the distribution.
+
+    This package maintains the following reliable distro ID values:
+
+    ==============  =========================================
+    Distro ID       Distribution
+    ==============  =========================================
+    "ubuntu"        Ubuntu
+    "debian"        Debian
+    "rhel"          RedHat Enterprise Linux
+    "centos"        CentOS
+    "fedora"        Fedora
+    "sles"          SUSE Linux Enterprise Server
+    "opensuse"      openSUSE
+    "amzn"          Amazon Linux
+    "arch"          Arch Linux
+    "buildroot"     Buildroot
+    "cloudlinux"    CloudLinux OS
+    "exherbo"       Exherbo Linux
+    "gentoo"        GenToo Linux
+    "ibm_powerkvm"  IBM PowerKVM
+    "kvmibm"        KVM for IBM z Systems
+    "linuxmint"     Linux Mint
+    "mageia"        Mageia
+    "mandriva"      Mandriva Linux
+    "parallels"     Parallels
+    "pidora"        Pidora
+    "raspbian"      Raspbian
+    "oracle"        Oracle Linux (and Oracle Enterprise Linux)
+    "scientific"    Scientific Linux
+    "slackware"     Slackware
+    "xenserver"     XenServer
+    "openbsd"       OpenBSD
+    "netbsd"        NetBSD
+    "freebsd"       FreeBSD
+    "midnightbsd"   MidnightBSD
+    "rocky"         Rocky Linux
+    "aix"           AIX
+    "guix"          Guix System
+    "altlinux"      ALT Linux
+    ==============  =========================================
+
+    If you have a need to get distros for reliable IDs added into this set,
+    or if you find that the :func:`distro.id` function returns a different
+    distro ID for one of the listed distros, please create an issue in the
+    `distro issue tracker`_.
+
+    **Lookup hierarchy and transformations:**
+
+    First, the ID is obtained from the following sources, in the specified
+    order. The first available and non-empty value is used:
+
+    * the value of the "ID" attribute of the os-release file,
+
+    * the value of the "Distributor ID" attribute returned by the lsb_release
+      command,
+
+    * the first part of the file name of the distro release file,
+
+    The so determined ID value then passes the following transformations,
+    before it is returned by this method:
+
+    * it is translated to lower case,
+
+    * blanks (which should not be there anyway) are translated to underscores,
+
+    * a normalization of the ID is performed, based upon
+      `normalization tables`_. The purpose of this normalization is to ensure
+      that the ID is as reliable as possible, even across incompatible changes
+      in the OS distributions. A common reason for an incompatible change is
+      the addition of an os-release file, or the addition of the lsb_release
+      command, with ID values that differ from what was previously determined
+      from the distro release file name.
+    """
+    return _distro.id()
+
+
+def name(pretty: bool = False) -> str:
+    """
+    Return the name of the current OS distribution, as a human-readable
+    string.
+
+    If *pretty* is false, the name is returned without version or codename.
+    (e.g. "CentOS Linux")
+
+    If *pretty* is true, the version and codename are appended.
+    (e.g. "CentOS Linux 7.1.1503 (Core)")
+
+    **Lookup hierarchy:**
+
+    The name is obtained from the following sources, in the specified order.
+    The first available and non-empty value is used:
+
+    * If *pretty* is false:
+
+      - the value of the "NAME" attribute of the os-release file,
+
+      - the value of the "Distributor ID" attribute returned by the lsb_release
+        command,
+
+      - the value of the "" field of the distro release file.
+
+    * If *pretty* is true:
+
+      - the value of the "PRETTY_NAME" attribute of the os-release file,
+
+      - the value of the "Description" attribute returned by the lsb_release
+        command,
+
+      - the value of the "" field of the distro release file, appended
+        with the value of the pretty version ("" and ""
+        fields) of the distro release file, if available.
+    """
+    return _distro.name(pretty)
+
+
+def version(pretty: bool = False, best: bool = False) -> str:
+    """
+    Return the version of the current OS distribution, as a human-readable
+    string.
+
+    If *pretty* is false, the version is returned without codename (e.g.
+    "7.0").
+
+    If *pretty* is true, the codename in parenthesis is appended, if the
+    codename is non-empty (e.g. "7.0 (Maipo)").
+
+    Some distributions provide version numbers with different precisions in
+    the different sources of distribution information. Examining the different
+    sources in a fixed priority order does not always yield the most precise
+    version (e.g. for Debian 8.2, or CentOS 7.1).
+
+    Some other distributions may not provide this kind of information. In these
+    cases, an empty string would be returned. This behavior can be observed
+    with rolling releases distributions (e.g. Arch Linux).
+
+    The *best* parameter can be used to control the approach for the returned
+    version:
+
+    If *best* is false, the first non-empty version number in priority order of
+    the examined sources is returned.
+
+    If *best* is true, the most precise version number out of all examined
+    sources is returned.
+
+    **Lookup hierarchy:**
+
+    In all cases, the version number is obtained from the following sources.
+    If *best* is false, this order represents the priority order:
+
+    * the value of the "VERSION_ID" attribute of the os-release file,
+    * the value of the "Release" attribute returned by the lsb_release
+      command,
+    * the version number parsed from the "" field of the first line
+      of the distro release file,
+    * the version number parsed from the "PRETTY_NAME" attribute of the
+      os-release file, if it follows the format of the distro release files.
+    * the version number parsed from the "Description" attribute returned by
+      the lsb_release command, if it follows the format of the distro release
+      files.
+    """
+    return _distro.version(pretty, best)
+
+
+def version_parts(best: bool = False) -> Tuple[str, str, str]:
+    """
+    Return the version of the current OS distribution as a tuple
+    ``(major, minor, build_number)`` with items as follows:
+
+    * ``major``:  The result of :func:`distro.major_version`.
+
+    * ``minor``:  The result of :func:`distro.minor_version`.
+
+    * ``build_number``:  The result of :func:`distro.build_number`.
+
+    For a description of the *best* parameter, see the :func:`distro.version`
+    method.
+    """
+    return _distro.version_parts(best)
+
+
+def major_version(best: bool = False) -> str:
+    """
+    Return the major version of the current OS distribution, as a string,
+    if provided.
+    Otherwise, the empty string is returned. The major version is the first
+    part of the dot-separated version string.
+
+    For a description of the *best* parameter, see the :func:`distro.version`
+    method.
+    """
+    return _distro.major_version(best)
+
+
+def minor_version(best: bool = False) -> str:
+    """
+    Return the minor version of the current OS distribution, as a string,
+    if provided.
+    Otherwise, the empty string is returned. The minor version is the second
+    part of the dot-separated version string.
+
+    For a description of the *best* parameter, see the :func:`distro.version`
+    method.
+    """
+    return _distro.minor_version(best)
+
+
+def build_number(best: bool = False) -> str:
+    """
+    Return the build number of the current OS distribution, as a string,
+    if provided.
+    Otherwise, the empty string is returned. The build number is the third part
+    of the dot-separated version string.
+
+    For a description of the *best* parameter, see the :func:`distro.version`
+    method.
+    """
+    return _distro.build_number(best)
+
+
+def like() -> str:
+    """
+    Return a space-separated list of distro IDs of distributions that are
+    closely related to the current OS distribution in regards to packaging
+    and programming interfaces, for example distributions the current
+    distribution is a derivative from.
+
+    **Lookup hierarchy:**
+
+    This information item is only provided by the os-release file.
+    For details, see the description of the "ID_LIKE" attribute in the
+    `os-release man page
+    `_.
+    """
+    return _distro.like()
+
+
+def codename() -> str:
+    """
+    Return the codename for the release of the current OS distribution,
+    as a string.
+
+    If the distribution does not have a codename, an empty string is returned.
+
+    Note that the returned codename is not always really a codename. For
+    example, openSUSE returns "x86_64". This function does not handle such
+    cases in any special way and just returns the string it finds, if any.
+
+    **Lookup hierarchy:**
+
+    * the codename within the "VERSION" attribute of the os-release file, if
+      provided,
+
+    * the value of the "Codename" attribute returned by the lsb_release
+      command,
+
+    * the value of the "" field of the distro release file.
+    """
+    return _distro.codename()
+
+
+def info(pretty: bool = False, best: bool = False) -> InfoDict:
+    """
+    Return certain machine-readable information items about the current OS
+    distribution in a dictionary, as shown in the following example:
+
+    .. sourcecode:: python
+
+        {
+            'id': 'rhel',
+            'version': '7.0',
+            'version_parts': {
+                'major': '7',
+                'minor': '0',
+                'build_number': ''
+            },
+            'like': 'fedora',
+            'codename': 'Maipo'
+        }
+
+    The dictionary structure and keys are always the same, regardless of which
+    information items are available in the underlying data sources. The values
+    for the various keys are as follows:
+
+    * ``id``:  The result of :func:`distro.id`.
+
+    * ``version``:  The result of :func:`distro.version`.
+
+    * ``version_parts -> major``:  The result of :func:`distro.major_version`.
+
+    * ``version_parts -> minor``:  The result of :func:`distro.minor_version`.
+
+    * ``version_parts -> build_number``:  The result of
+      :func:`distro.build_number`.
+
+    * ``like``:  The result of :func:`distro.like`.
+
+    * ``codename``:  The result of :func:`distro.codename`.
+
+    For a description of the *pretty* and *best* parameters, see the
+    :func:`distro.version` method.
+    """
+    return _distro.info(pretty, best)
+
+
+def os_release_info() -> Dict[str, str]:
+    """
+    Return a dictionary containing key-value pairs for the information items
+    from the os-release file data source of the current OS distribution.
+
+    See `os-release file`_ for details about these information items.
+    """
+    return _distro.os_release_info()
+
+
+def lsb_release_info() -> Dict[str, str]:
+    """
+    Return a dictionary containing key-value pairs for the information items
+    from the lsb_release command data source of the current OS distribution.
+
+    See `lsb_release command output`_ for details about these information
+    items.
+    """
+    return _distro.lsb_release_info()
+
+
+def distro_release_info() -> Dict[str, str]:
+    """
+    Return a dictionary containing key-value pairs for the information items
+    from the distro release file data source of the current OS distribution.
+
+    See `distro release file`_ for details about these information items.
+    """
+    return _distro.distro_release_info()
+
+
+def uname_info() -> Dict[str, str]:
+    """
+    Return a dictionary containing key-value pairs for the information items
+    from the distro release file data source of the current OS distribution.
+    """
+    return _distro.uname_info()
+
+
+def os_release_attr(attribute: str) -> str:
+    """
+    Return a single named information item from the os-release file data source
+    of the current OS distribution.
+
+    Parameters:
+
+    * ``attribute`` (string): Key of the information item.
+
+    Returns:
+
+    * (string): Value of the information item, if the item exists.
+      The empty string, if the item does not exist.
+
+    See `os-release file`_ for details about these information items.
+    """
+    return _distro.os_release_attr(attribute)
+
+
+def lsb_release_attr(attribute: str) -> str:
+    """
+    Return a single named information item from the lsb_release command output
+    data source of the current OS distribution.
+
+    Parameters:
+
+    * ``attribute`` (string): Key of the information item.
+
+    Returns:
+
+    * (string): Value of the information item, if the item exists.
+      The empty string, if the item does not exist.
+
+    See `lsb_release command output`_ for details about these information
+    items.
+    """
+    return _distro.lsb_release_attr(attribute)
+
+
+def distro_release_attr(attribute: str) -> str:
+    """
+    Return a single named information item from the distro release file
+    data source of the current OS distribution.
+
+    Parameters:
+
+    * ``attribute`` (string): Key of the information item.
+
+    Returns:
+
+    * (string): Value of the information item, if the item exists.
+      The empty string, if the item does not exist.
+
+    See `distro release file`_ for details about these information items.
+    """
+    return _distro.distro_release_attr(attribute)
+
+
+def uname_attr(attribute: str) -> str:
+    """
+    Return a single named information item from the distro release file
+    data source of the current OS distribution.
+
+    Parameters:
+
+    * ``attribute`` (string): Key of the information item.
+
+    Returns:
+
+    * (string): Value of the information item, if the item exists.
+                The empty string, if the item does not exist.
+    """
+    return _distro.uname_attr(attribute)
+
+
+try:
+    from functools import cached_property
+except ImportError:
+    # Python < 3.8
+    class cached_property:  # type: ignore
+        """A version of @property which caches the value.  On access, it calls the
+        underlying function and sets the value in `__dict__` so future accesses
+        will not re-call the property.
+        """
+
+        def __init__(self, f: Callable[[Any], Any]) -> None:
+            self._fname = f.__name__
+            self._f = f
+
+        def __get__(self, obj: Any, owner: Type[Any]) -> Any:
+            assert obj is not None, f"call {self._fname} on an instance"
+            ret = obj.__dict__[self._fname] = self._f(obj)
+            return ret
+
+
+class LinuxDistribution:
+    """
+    Provides information about a OS distribution.
+
+    This package creates a private module-global instance of this class with
+    default initialization arguments, that is used by the
+    `consolidated accessor functions`_ and `single source accessor functions`_.
+    By using default initialization arguments, that module-global instance
+    returns data about the current OS distribution (i.e. the distro this
+    package runs on).
+
+    Normally, it is not necessary to create additional instances of this class.
+    However, in situations where control is needed over the exact data sources
+    that are used, instances of this class can be created with a specific
+    distro release file, or a specific os-release file, or without invoking the
+    lsb_release command.
+    """
+
+    def __init__(
+        self,
+        include_lsb: Optional[bool] = None,
+        os_release_file: str = "",
+        distro_release_file: str = "",
+        include_uname: Optional[bool] = None,
+        root_dir: Optional[str] = None,
+        include_oslevel: Optional[bool] = None,
+    ) -> None:
+        """
+        The initialization method of this class gathers information from the
+        available data sources, and stores that in private instance attributes.
+        Subsequent access to the information items uses these private instance
+        attributes, so that the data sources are read only once.
+
+        Parameters:
+
+        * ``include_lsb`` (bool): Controls whether the
+          `lsb_release command output`_ is included as a data source.
+
+          If the lsb_release command is not available in the program execution
+          path, the data source for the lsb_release command will be empty.
+
+        * ``os_release_file`` (string): The path name of the
+          `os-release file`_ that is to be used as a data source.
+
+          An empty string (the default) will cause the default path name to
+          be used (see `os-release file`_ for details).
+
+          If the specified or defaulted os-release file does not exist, the
+          data source for the os-release file will be empty.
+
+        * ``distro_release_file`` (string): The path name of the
+          `distro release file`_ that is to be used as a data source.
+
+          An empty string (the default) will cause a default search algorithm
+          to be used (see `distro release file`_ for details).
+
+          If the specified distro release file does not exist, or if no default
+          distro release file can be found, the data source for the distro
+          release file will be empty.
+
+        * ``include_uname`` (bool): Controls whether uname command output is
+          included as a data source. If the uname command is not available in
+          the program execution path the data source for the uname command will
+          be empty.
+
+        * ``root_dir`` (string): The absolute path to the root directory to use
+          to find distro-related information files. Note that ``include_*``
+          parameters must not be enabled in combination with ``root_dir``.
+
+        * ``include_oslevel`` (bool): Controls whether (AIX) oslevel command
+          output is included as a data source. If the oslevel command is not
+          available in the program execution path the data source will be
+          empty.
+
+        Public instance attributes:
+
+        * ``os_release_file`` (string): The path name of the
+          `os-release file`_ that is actually used as a data source. The
+          empty string if no distro release file is used as a data source.
+
+        * ``distro_release_file`` (string): The path name of the
+          `distro release file`_ that is actually used as a data source. The
+          empty string if no distro release file is used as a data source.
+
+        * ``include_lsb`` (bool): The result of the ``include_lsb`` parameter.
+          This controls whether the lsb information will be loaded.
+
+        * ``include_uname`` (bool): The result of the ``include_uname``
+          parameter. This controls whether the uname information will
+          be loaded.
+
+        * ``include_oslevel`` (bool): The result of the ``include_oslevel``
+          parameter. This controls whether (AIX) oslevel information will be
+          loaded.
+
+        * ``root_dir`` (string): The result of the ``root_dir`` parameter.
+          The absolute path to the root directory to use to find distro-related
+          information files.
+
+        Raises:
+
+        * :py:exc:`ValueError`: Initialization parameters combination is not
+           supported.
+
+        * :py:exc:`OSError`: Some I/O issue with an os-release file or distro
+          release file.
+
+        * :py:exc:`UnicodeError`: A data source has unexpected characters or
+          uses an unexpected encoding.
+        """
+        self.root_dir = root_dir
+        self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR
+        self.usr_lib_dir = (
+            os.path.join(root_dir, "usr/lib") if root_dir else _UNIXUSRLIBDIR
+        )
+
+        if os_release_file:
+            self.os_release_file = os_release_file
+        else:
+            etc_dir_os_release_file = os.path.join(self.etc_dir, _OS_RELEASE_BASENAME)
+            usr_lib_os_release_file = os.path.join(
+                self.usr_lib_dir, _OS_RELEASE_BASENAME
+            )
+
+            # NOTE: The idea is to respect order **and** have it set
+            #       at all times for API backwards compatibility.
+            if os.path.isfile(etc_dir_os_release_file) or not os.path.isfile(
+                usr_lib_os_release_file
+            ):
+                self.os_release_file = etc_dir_os_release_file
+            else:
+                self.os_release_file = usr_lib_os_release_file
+
+        self.distro_release_file = distro_release_file or ""  # updated later
+
+        is_root_dir_defined = root_dir is not None
+        if is_root_dir_defined and (include_lsb or include_uname or include_oslevel):
+            raise ValueError(
+                "Including subprocess data sources from specific root_dir is disallowed"
+                " to prevent false information"
+            )
+        self.include_lsb = (
+            include_lsb if include_lsb is not None else not is_root_dir_defined
+        )
+        self.include_uname = (
+            include_uname if include_uname is not None else not is_root_dir_defined
+        )
+        self.include_oslevel = (
+            include_oslevel if include_oslevel is not None else not is_root_dir_defined
+        )
+
+    def __repr__(self) -> str:
+        """Return repr of all info"""
+        return (
+            "LinuxDistribution("
+            "os_release_file={self.os_release_file!r}, "
+            "distro_release_file={self.distro_release_file!r}, "
+            "include_lsb={self.include_lsb!r}, "
+            "include_uname={self.include_uname!r}, "
+            "include_oslevel={self.include_oslevel!r}, "
+            "root_dir={self.root_dir!r}, "
+            "_os_release_info={self._os_release_info!r}, "
+            "_lsb_release_info={self._lsb_release_info!r}, "
+            "_distro_release_info={self._distro_release_info!r}, "
+            "_uname_info={self._uname_info!r}, "
+            "_oslevel_info={self._oslevel_info!r})".format(self=self)
+        )
+
+    def linux_distribution(
+        self, full_distribution_name: bool = True
+    ) -> Tuple[str, str, str]:
+        """
+        Return information about the OS distribution that is compatible
+        with Python's :func:`platform.linux_distribution`, supporting a subset
+        of its parameters.
+
+        For details, see :func:`distro.linux_distribution`.
+        """
+        return (
+            self.name() if full_distribution_name else self.id(),
+            self.version(),
+            self._os_release_info.get("release_codename") or self.codename(),
+        )
+
+    def id(self) -> str:
+        """Return the distro ID of the OS distribution, as a string.
+
+        For details, see :func:`distro.id`.
+        """
+
+        def normalize(distro_id: str, table: Dict[str, str]) -> str:
+            distro_id = distro_id.lower().replace(" ", "_")
+            return table.get(distro_id, distro_id)
+
+        distro_id = self.os_release_attr("id")
+        if distro_id:
+            return normalize(distro_id, NORMALIZED_OS_ID)
+
+        distro_id = self.lsb_release_attr("distributor_id")
+        if distro_id:
+            return normalize(distro_id, NORMALIZED_LSB_ID)
+
+        distro_id = self.distro_release_attr("id")
+        if distro_id:
+            return normalize(distro_id, NORMALIZED_DISTRO_ID)
+
+        distro_id = self.uname_attr("id")
+        if distro_id:
+            return normalize(distro_id, NORMALIZED_DISTRO_ID)
+
+        return ""
+
+    def name(self, pretty: bool = False) -> str:
+        """
+        Return the name of the OS distribution, as a string.
+
+        For details, see :func:`distro.name`.
+        """
+        name = (
+            self.os_release_attr("name")
+            or self.lsb_release_attr("distributor_id")
+            or self.distro_release_attr("name")
+            or self.uname_attr("name")
+        )
+        if pretty:
+            name = self.os_release_attr("pretty_name") or self.lsb_release_attr(
+                "description"
+            )
+            if not name:
+                name = self.distro_release_attr("name") or self.uname_attr("name")
+                version = self.version(pretty=True)
+                if version:
+                    name = f"{name} {version}"
+        return name or ""
+
+    def version(self, pretty: bool = False, best: bool = False) -> str:
+        """
+        Return the version of the OS distribution, as a string.
+
+        For details, see :func:`distro.version`.
+        """
+        versions = [
+            self.os_release_attr("version_id"),
+            self.lsb_release_attr("release"),
+            self.distro_release_attr("version_id"),
+            self._parse_distro_release_content(self.os_release_attr("pretty_name")).get(
+                "version_id", ""
+            ),
+            self._parse_distro_release_content(
+                self.lsb_release_attr("description")
+            ).get("version_id", ""),
+            self.uname_attr("release"),
+        ]
+        if self.uname_attr("id").startswith("aix"):
+            # On AIX platforms, prefer oslevel command output.
+            versions.insert(0, self.oslevel_info())
+        elif self.id() == "debian" or "debian" in self.like().split():
+            # On Debian-like, add debian_version file content to candidates list.
+            versions.append(self._debian_version)
+        version = ""
+        if best:
+            # This algorithm uses the last version in priority order that has
+            # the best precision. If the versions are not in conflict, that
+            # does not matter; otherwise, using the last one instead of the
+            # first one might be considered a surprise.
+            for v in versions:
+                if v.count(".") > version.count(".") or version == "":
+                    version = v
+        else:
+            for v in versions:
+                if v != "":
+                    version = v
+                    break
+        if pretty and version and self.codename():
+            version = f"{version} ({self.codename()})"
+        return version
+
+    def version_parts(self, best: bool = False) -> Tuple[str, str, str]:
+        """
+        Return the version of the OS distribution, as a tuple of version
+        numbers.
+
+        For details, see :func:`distro.version_parts`.
+        """
+        version_str = self.version(best=best)
+        if version_str:
+            version_regex = re.compile(r"(\d+)\.?(\d+)?\.?(\d+)?")
+            matches = version_regex.match(version_str)
+            if matches:
+                major, minor, build_number = matches.groups()
+                return major, minor or "", build_number or ""
+        return "", "", ""
+
+    def major_version(self, best: bool = False) -> str:
+        """
+        Return the major version number of the current distribution.
+
+        For details, see :func:`distro.major_version`.
+        """
+        return self.version_parts(best)[0]
+
+    def minor_version(self, best: bool = False) -> str:
+        """
+        Return the minor version number of the current distribution.
+
+        For details, see :func:`distro.minor_version`.
+        """
+        return self.version_parts(best)[1]
+
+    def build_number(self, best: bool = False) -> str:
+        """
+        Return the build number of the current distribution.
+
+        For details, see :func:`distro.build_number`.
+        """
+        return self.version_parts(best)[2]
+
+    def like(self) -> str:
+        """
+        Return the IDs of distributions that are like the OS distribution.
+
+        For details, see :func:`distro.like`.
+        """
+        return self.os_release_attr("id_like") or ""
+
+    def codename(self) -> str:
+        """
+        Return the codename of the OS distribution.
+
+        For details, see :func:`distro.codename`.
+        """
+        try:
+            # Handle os_release specially since distros might purposefully set
+            # this to empty string to have no codename
+            return self._os_release_info["codename"]
+        except KeyError:
+            return (
+                self.lsb_release_attr("codename")
+                or self.distro_release_attr("codename")
+                or ""
+            )
+
+    def info(self, pretty: bool = False, best: bool = False) -> InfoDict:
+        """
+        Return certain machine-readable information about the OS
+        distribution.
+
+        For details, see :func:`distro.info`.
+        """
+        return InfoDict(
+            id=self.id(),
+            version=self.version(pretty, best),
+            version_parts=VersionDict(
+                major=self.major_version(best),
+                minor=self.minor_version(best),
+                build_number=self.build_number(best),
+            ),
+            like=self.like(),
+            codename=self.codename(),
+        )
+
+    def os_release_info(self) -> Dict[str, str]:
+        """
+        Return a dictionary containing key-value pairs for the information
+        items from the os-release file data source of the OS distribution.
+
+        For details, see :func:`distro.os_release_info`.
+        """
+        return self._os_release_info
+
+    def lsb_release_info(self) -> Dict[str, str]:
+        """
+        Return a dictionary containing key-value pairs for the information
+        items from the lsb_release command data source of the OS
+        distribution.
+
+        For details, see :func:`distro.lsb_release_info`.
+        """
+        return self._lsb_release_info
+
+    def distro_release_info(self) -> Dict[str, str]:
+        """
+        Return a dictionary containing key-value pairs for the information
+        items from the distro release file data source of the OS
+        distribution.
+
+        For details, see :func:`distro.distro_release_info`.
+        """
+        return self._distro_release_info
+
+    def uname_info(self) -> Dict[str, str]:
+        """
+        Return a dictionary containing key-value pairs for the information
+        items from the uname command data source of the OS distribution.
+
+        For details, see :func:`distro.uname_info`.
+        """
+        return self._uname_info
+
+    def oslevel_info(self) -> str:
+        """
+        Return AIX' oslevel command output.
+        """
+        return self._oslevel_info
+
+    def os_release_attr(self, attribute: str) -> str:
+        """
+        Return a single named information item from the os-release file data
+        source of the OS distribution.
+
+        For details, see :func:`distro.os_release_attr`.
+        """
+        return self._os_release_info.get(attribute, "")
+
+    def lsb_release_attr(self, attribute: str) -> str:
+        """
+        Return a single named information item from the lsb_release command
+        output data source of the OS distribution.
+
+        For details, see :func:`distro.lsb_release_attr`.
+        """
+        return self._lsb_release_info.get(attribute, "")
+
+    def distro_release_attr(self, attribute: str) -> str:
+        """
+        Return a single named information item from the distro release file
+        data source of the OS distribution.
+
+        For details, see :func:`distro.distro_release_attr`.
+        """
+        return self._distro_release_info.get(attribute, "")
+
+    def uname_attr(self, attribute: str) -> str:
+        """
+        Return a single named information item from the uname command
+        output data source of the OS distribution.
+
+        For details, see :func:`distro.uname_attr`.
+        """
+        return self._uname_info.get(attribute, "")
+
+    @cached_property
+    def _os_release_info(self) -> Dict[str, str]:
+        """
+        Get the information items from the specified os-release file.
+
+        Returns:
+            A dictionary containing all information items.
+        """
+        if os.path.isfile(self.os_release_file):
+            with open(self.os_release_file, encoding="utf-8") as release_file:
+                return self._parse_os_release_content(release_file)
+        return {}
+
+    @staticmethod
+    def _parse_os_release_content(lines: TextIO) -> Dict[str, str]:
+        """
+        Parse the lines of an os-release file.
+
+        Parameters:
+
+        * lines: Iterable through the lines in the os-release file.
+                 Each line must be a unicode string or a UTF-8 encoded byte
+                 string.
+
+        Returns:
+            A dictionary containing all information items.
+        """
+        props = {}
+        lexer = shlex.shlex(lines, posix=True)
+        lexer.whitespace_split = True
+
+        tokens = list(lexer)
+        for token in tokens:
+            # At this point, all shell-like parsing has been done (i.e.
+            # comments processed, quotes and backslash escape sequences
+            # processed, multi-line values assembled, trailing newlines
+            # stripped, etc.), so the tokens are now either:
+            # * variable assignments: var=value
+            # * commands or their arguments (not allowed in os-release)
+            # Ignore any tokens that are not variable assignments
+            if "=" in token:
+                k, v = token.split("=", 1)
+                props[k.lower()] = v
+
+        if "version" in props:
+            # extract release codename (if any) from version attribute
+            match = re.search(r"\((\D+)\)|,\s*(\D+)", props["version"])
+            if match:
+                release_codename = match.group(1) or match.group(2)
+                props["codename"] = props["release_codename"] = release_codename
+
+        if "version_codename" in props:
+            # os-release added a version_codename field.  Use that in
+            # preference to anything else Note that some distros purposefully
+            # do not have code names.  They should be setting
+            # version_codename=""
+            props["codename"] = props["version_codename"]
+        elif "ubuntu_codename" in props:
+            # Same as above but a non-standard field name used on older Ubuntus
+            props["codename"] = props["ubuntu_codename"]
+
+        return props
+
+    @cached_property
+    def _lsb_release_info(self) -> Dict[str, str]:
+        """
+        Get the information items from the lsb_release command output.
+
+        Returns:
+            A dictionary containing all information items.
+        """
+        if not self.include_lsb:
+            return {}
+        try:
+            cmd = ("lsb_release", "-a")
+            stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
+        # Command not found or lsb_release returned error
+        except (OSError, subprocess.CalledProcessError):
+            return {}
+        content = self._to_str(stdout).splitlines()
+        return self._parse_lsb_release_content(content)
+
+    @staticmethod
+    def _parse_lsb_release_content(lines: Iterable[str]) -> Dict[str, str]:
+        """
+        Parse the output of the lsb_release command.
+
+        Parameters:
+
+        * lines: Iterable through the lines of the lsb_release output.
+                 Each line must be a unicode string or a UTF-8 encoded byte
+                 string.
+
+        Returns:
+            A dictionary containing all information items.
+        """
+        props = {}
+        for line in lines:
+            kv = line.strip("\n").split(":", 1)
+            if len(kv) != 2:
+                # Ignore lines without colon.
+                continue
+            k, v = kv
+            props.update({k.replace(" ", "_").lower(): v.strip()})
+        return props
+
+    @cached_property
+    def _uname_info(self) -> Dict[str, str]:
+        if not self.include_uname:
+            return {}
+        try:
+            cmd = ("uname", "-rs")
+            stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
+        except OSError:
+            return {}
+        content = self._to_str(stdout).splitlines()
+        return self._parse_uname_content(content)
+
+    @cached_property
+    def _oslevel_info(self) -> str:
+        if not self.include_oslevel:
+            return ""
+        try:
+            stdout = subprocess.check_output("oslevel", stderr=subprocess.DEVNULL)
+        except (OSError, subprocess.CalledProcessError):
+            return ""
+        return self._to_str(stdout).strip()
+
+    @cached_property
+    def _debian_version(self) -> str:
+        try:
+            with open(
+                os.path.join(self.etc_dir, "debian_version"), encoding="ascii"
+            ) as fp:
+                return fp.readline().rstrip()
+        except FileNotFoundError:
+            return ""
+
+    @staticmethod
+    def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]:
+        if not lines:
+            return {}
+        props = {}
+        match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip())
+        if match:
+            name, version = match.groups()
+
+            # This is to prevent the Linux kernel version from
+            # appearing as the 'best' version on otherwise
+            # identifiable distributions.
+            if name == "Linux":
+                return {}
+            props["id"] = name.lower()
+            props["name"] = name
+            props["release"] = version
+        return props
+
+    @staticmethod
+    def _to_str(bytestring: bytes) -> str:
+        encoding = sys.getfilesystemencoding()
+        return bytestring.decode(encoding)
+
+    @cached_property
+    def _distro_release_info(self) -> Dict[str, str]:
+        """
+        Get the information items from the specified distro release file.
+
+        Returns:
+            A dictionary containing all information items.
+        """
+        if self.distro_release_file:
+            # If it was specified, we use it and parse what we can, even if
+            # its file name or content does not match the expected pattern.
+            distro_info = self._parse_distro_release_file(self.distro_release_file)
+            basename = os.path.basename(self.distro_release_file)
+            # The file name pattern for user-specified distro release files
+            # is somewhat more tolerant (compared to when searching for the
+            # file), because we want to use what was specified as best as
+            # possible.
+            match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
+        else:
+            try:
+                basenames = [
+                    basename
+                    for basename in os.listdir(self.etc_dir)
+                    if basename not in _DISTRO_RELEASE_IGNORE_BASENAMES
+                    and os.path.isfile(os.path.join(self.etc_dir, basename))
+                ]
+                # We sort for repeatability in cases where there are multiple
+                # distro specific files; e.g. CentOS, Oracle, Enterprise all
+                # containing `redhat-release` on top of their own.
+                basenames.sort()
+            except OSError:
+                # This may occur when /etc is not readable but we can't be
+                # sure about the *-release files. Check common entries of
+                # /etc for information. If they turn out to not be there the
+                # error is handled in `_parse_distro_release_file()`.
+                basenames = _DISTRO_RELEASE_BASENAMES
+            for basename in basenames:
+                match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
+                if match is None:
+                    continue
+                filepath = os.path.join(self.etc_dir, basename)
+                distro_info = self._parse_distro_release_file(filepath)
+                # The name is always present if the pattern matches.
+                if "name" not in distro_info:
+                    continue
+                self.distro_release_file = filepath
+                break
+            else:  # the loop didn't "break": no candidate.
+                return {}
+
+        if match is not None:
+            distro_info["id"] = match.group(1)
+
+        # CloudLinux < 7: manually enrich info with proper id.
+        if "cloudlinux" in distro_info.get("name", "").lower():
+            distro_info["id"] = "cloudlinux"
+
+        return distro_info
+
+    def _parse_distro_release_file(self, filepath: str) -> Dict[str, str]:
+        """
+        Parse a distro release file.
+
+        Parameters:
+
+        * filepath: Path name of the distro release file.
+
+        Returns:
+            A dictionary containing all information items.
+        """
+        try:
+            with open(filepath, encoding="utf-8") as fp:
+                # Only parse the first line. For instance, on SLES there
+                # are multiple lines. We don't want them...
+                return self._parse_distro_release_content(fp.readline())
+        except OSError:
+            # Ignore not being able to read a specific, seemingly version
+            # related file.
+            # See https://github.com/python-distro/distro/issues/162
+            return {}
+
+    @staticmethod
+    def _parse_distro_release_content(line: str) -> Dict[str, str]:
+        """
+        Parse a line from a distro release file.
+
+        Parameters:
+        * line: Line from the distro release file. Must be a unicode string
+                or a UTF-8 encoded byte string.
+
+        Returns:
+            A dictionary containing all information items.
+        """
+        matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match(line.strip()[::-1])
+        distro_info = {}
+        if matches:
+            # regexp ensures non-None
+            distro_info["name"] = matches.group(3)[::-1]
+            if matches.group(2):
+                distro_info["version_id"] = matches.group(2)[::-1]
+            if matches.group(1):
+                distro_info["codename"] = matches.group(1)[::-1]
+        elif line:
+            distro_info["name"] = line.strip()
+        return distro_info
+
+
+_distro = LinuxDistribution()
+
+
+def main() -> None:
+    logger = logging.getLogger(__name__)
+    logger.setLevel(logging.DEBUG)
+    logger.addHandler(logging.StreamHandler(sys.stdout))
+
+    parser = argparse.ArgumentParser(description="OS distro info tool")
+    parser.add_argument(
+        "--json", "-j", help="Output in machine readable format", action="store_true"
+    )
+
+    parser.add_argument(
+        "--root-dir",
+        "-r",
+        type=str,
+        dest="root_dir",
+        help="Path to the root filesystem directory (defaults to /)",
+    )
+
+    args = parser.parse_args()
+
+    if args.root_dir:
+        dist = LinuxDistribution(
+            include_lsb=False,
+            include_uname=False,
+            include_oslevel=False,
+            root_dir=args.root_dir,
+        )
+    else:
+        dist = _distro
+
+    if args.json:
+        logger.info(json.dumps(dist.info(), indent=4, sort_keys=True))
+    else:
+        logger.info("Name: %s", dist.name(pretty=True))
+        distribution_version = dist.version(pretty=True)
+        logger.info("Version: %s", distribution_version)
+        distribution_codename = dist.codename()
+        logger.info("Codename: %s", distribution_codename)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/distro/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/distro/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/LICENSE.md b/venv/lib/python3.12/site-packages/pip/_vendor/idna/LICENSE.md
new file mode 100644
index 0000000..f706835
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/idna/LICENSE.md
@@ -0,0 +1,31 @@
+BSD 3-Clause License
+
+Copyright (c) 2013-2026, Kim Davies and contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__init__.py
new file mode 100644
index 0000000..cfdc030
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__init__.py
@@ -0,0 +1,45 @@
+from .core import (
+    IDNABidiError,
+    IDNAError,
+    InvalidCodepoint,
+    InvalidCodepointContext,
+    alabel,
+    check_bidi,
+    check_hyphen_ok,
+    check_initial_combiner,
+    check_label,
+    check_nfc,
+    decode,
+    encode,
+    ulabel,
+    uts46_remap,
+    valid_contextj,
+    valid_contexto,
+    valid_label_length,
+    valid_string_length,
+)
+from .intranges import intranges_contain
+from .package_data import __version__
+
+__all__ = [
+    "__version__",
+    "IDNABidiError",
+    "IDNAError",
+    "InvalidCodepoint",
+    "InvalidCodepointContext",
+    "alabel",
+    "check_bidi",
+    "check_hyphen_ok",
+    "check_initial_combiner",
+    "check_label",
+    "check_nfc",
+    "decode",
+    "encode",
+    "intranges_contain",
+    "ulabel",
+    "uts46_remap",
+    "valid_contextj",
+    "valid_contexto",
+    "valid_label_length",
+    "valid_string_length",
+]
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/__main__.py b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__main__.py
new file mode 100644
index 0000000..dbdd066
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__main__.py
@@ -0,0 +1,6 @@
+import sys
+
+from .cli import main
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..47f10cb
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/__main__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/__main__.cpython-312.pyc
new file mode 100644
index 0000000..9dd9ced
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/__main__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/cli.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/cli.cpython-312.pyc
new file mode 100644
index 0000000..b512cea
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/cli.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-312.pyc
new file mode 100644
index 0000000..b6d8101
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-312.pyc
new file mode 100644
index 0000000..71c329f
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/core.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/core.cpython-312.pyc
new file mode 100644
index 0000000..5498931
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/core.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-312.pyc
new file mode 100644
index 0000000..7bd1126
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-312.pyc
new file mode 100644
index 0000000..bb9e5bb
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-312.pyc
new file mode 100644
index 0000000..ee90044
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-312.pyc
new file mode 100644
index 0000000..c064b91
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/cli.py b/venv/lib/python3.12/site-packages/pip/_vendor/idna/cli.py
new file mode 100644
index 0000000..4acda2c
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/idna/cli.py
@@ -0,0 +1,128 @@
+"""Command-line interface for the :mod:`idna` package.
+
+Invoked via ``python -m idna``. See :func:`main` for the entry point.
+"""
+
+import argparse
+import sys
+from collections.abc import Iterable
+from itertools import chain
+from typing import IO, Optional
+
+from . import IDNAError, decode, encode
+from .core import _alabel_prefix, _unicode_dots_re
+from .package_data import __version__
+
+
+def _looks_like_alabel(s: str) -> bool:
+    """Return True if any label in ``s`` carries the ``xn--`` ACE prefix."""
+    prefix = _alabel_prefix.decode("ascii")
+    return any(label.lower().startswith(prefix) for label in _unicode_dots_re.split(s))
+
+
+def _build_parser() -> argparse.ArgumentParser:
+    parser = argparse.ArgumentParser(
+        prog="python -m idna",
+        description=(
+            "Convert a domain name between its Unicode (U-label) and "
+            "ASCII-compatible (A-label) forms. With no mode flag, the "
+            "direction is chosen from the first input — if it contains "
+            "an xn-- label the stream is decoded, otherwise it is "
+            "encoded — and the same mode is applied to every remaining "
+            "input. UTS #46 mapping is applied by default; pass "
+            "--strict to disable it. When no domains are given on the "
+            "command line and stdin is piped, one domain per line is "
+            "read from stdin."
+        ),
+    )
+    mode = parser.add_mutually_exclusive_group()
+    mode.add_argument(
+        "-e",
+        "--encode",
+        dest="mode",
+        action="store_const",
+        const="encode",
+        help="Encode the input to its ASCII A-label form.",
+    )
+    mode.add_argument(
+        "-d",
+        "--decode",
+        dest="mode",
+        action="store_const",
+        const="decode",
+        help="Decode the input from its ASCII A-label form.",
+    )
+    parser.add_argument(
+        "--strict",
+        action="store_true",
+        help="Disable the default UTS #46 mapping and apply IDNA 2008 rules verbatim.",
+    )
+    parser.add_argument(
+        "--version",
+        action="version",
+        version=f"idna {__version__}",
+    )
+    parser.add_argument(
+        "domain",
+        nargs="*",
+        help="One or more domain names to convert. Omit to read from stdin.",
+    )
+    return parser
+
+
+def _iter_stdin(stream: IO[str]) -> Iterable[str]:
+    """Yield non-empty stripped lines from ``stream``, ignoring blanks."""
+    for line in stream:
+        stripped = line.strip()
+        if stripped:
+            yield stripped
+
+
+def _convert_one(domain: str, mode: str, uts46: bool) -> bool:
+    """Convert ``domain`` and write the result; return ``False`` on failure."""
+    try:
+        if mode == "decode":
+            print(decode(domain, uts46=uts46))
+        else:
+            print(encode(domain, uts46=uts46).decode("ascii"))
+    except IDNAError as err:
+        print(f"idna: {mode} failed for {domain!r}: {err}", file=sys.stderr)
+        return False
+    return True
+
+
+def main(argv: Optional[list[str]] = None) -> int:
+    """Entry point for ``python -m idna``.
+
+    When more than one domain is supplied (via positional arguments or
+    piped stdin) and no mode flag is given, the first input determines
+    the direction and that mode is applied uniformly to the rest.
+
+    :param argv: Argument list excluding the program name. Defaults to
+        :data:`sys.argv` when ``None``.
+    :returns: ``0`` on success, ``1`` if any conversion fails.
+    """
+    parser = _build_parser()
+    args = parser.parse_args(argv)
+    uts46 = not args.strict
+
+    if args.domain:
+        domains: Iterable[str] = args.domain
+    elif not sys.stdin.isatty():
+        domains = _iter_stdin(sys.stdin)
+    else:
+        parser.error("a domain argument is required when stdin is a terminal")
+
+    iterator = iter(domains)
+    first = next(iterator, None)
+    if first is None:
+        return 0
+
+    mode = args.mode or ("decode" if _looks_like_alabel(first) else "encode")
+
+    results = [_convert_one(domain, mode, uts46) for domain in chain([first], iterator)]
+    return 0 if all(results) else 1
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/codec.py b/venv/lib/python3.12/site-packages/pip/_vendor/idna/codec.py
new file mode 100644
index 0000000..83b42fe
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/idna/codec.py
@@ -0,0 +1,159 @@
+import codecs
+from typing import Any, Optional
+
+from .core import IDNAError, _unicode_dots_re, alabel, decode, encode, ulabel
+
+
+class Codec(codecs.Codec):
+    """Stateless IDNA 2008 codec.
+
+    Implements the :class:`codecs.Codec` protocol so that the whole-domain
+    encoder (:func:`idna.encode`) and decoder (:func:`idna.decode`) are
+    accessible through the standard codec machinery as ``"idna2008"``.
+
+    Only the ``"strict"`` error handler is supported; any other handler
+    raises :exc:`~idna.IDNAError`.
+    """
+
+    def encode(self, data: str, errors: str = "strict") -> tuple[bytes, int]:  # ty: ignore[invalid-method-override]
+        if errors != "strict":
+            raise IDNAError(f'Unsupported error handling "{errors}"')
+
+        if not data:
+            return b"", 0
+
+        return encode(data), len(data)
+
+    def decode(self, data: bytes, errors: str = "strict") -> tuple[str, int]:  # ty: ignore[invalid-method-override]
+        if errors != "strict":
+            raise IDNAError(f'Unsupported error handling "{errors}"')
+
+        if not data:
+            return "", 0
+
+        return decode(data), len(data)
+
+
+class IncrementalEncoder(codecs.BufferedIncrementalEncoder):
+    """Incremental IDNA 2008 encoder.
+
+    Buffers a partial trailing label across calls until either the next
+    label separator is seen or ``final=True``, so that streamed input is
+    encoded one whole label at a time. Any of the four Unicode label
+    separators (``U+002E``, ``U+3002``, ``U+FF0E``, ``U+FF61``) ends a
+    label; the result always uses ``U+002E`` as the separator.
+
+    Only the ``"strict"`` error handler is supported.
+    """
+
+    def _buffer_encode(self, data: str, errors: str, final: bool) -> tuple[bytes, int]:  # ty: ignore[invalid-method-override]
+        if errors != "strict":
+            raise IDNAError(f'Unsupported error handling "{errors}"')
+
+        if not data:
+            return b"", 0
+
+        labels = _unicode_dots_re.split(data)
+        trailing_dot = b""
+        if labels:
+            if not labels[-1]:
+                trailing_dot = b"."
+                del labels[-1]
+            elif not final:
+                # Keep potentially unfinished label until the next call
+                del labels[-1]
+                if labels:
+                    trailing_dot = b"."
+
+        result = []
+        size = 0
+        for label in labels:
+            result.append(alabel(label))
+            if size:
+                size += 1
+            size += len(label)
+
+        # Join with U+002E
+        result_bytes = b".".join(result) + trailing_dot
+        size += len(trailing_dot)
+        return result_bytes, size
+
+
+class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
+    """Incremental IDNA 2008 decoder.
+
+    Buffers a partial trailing label across calls until either the next
+    label separator is seen or ``final=True``, so that streamed input is
+    decoded one whole label at a time.
+
+    Only the ``"strict"`` error handler is supported.
+    """
+
+    def _buffer_decode(self, data: Any, errors: str, final: bool) -> tuple[str, int]:  # ty: ignore[invalid-method-override]
+        if errors != "strict":
+            raise IDNAError(f'Unsupported error handling "{errors}"')
+
+        if not data:
+            return ("", 0)
+
+        if not isinstance(data, str):
+            data = str(data, "ascii")
+
+        labels = _unicode_dots_re.split(data)
+        trailing_dot = ""
+        if labels:
+            if not labels[-1]:
+                trailing_dot = "."
+                del labels[-1]
+            elif not final:
+                # Keep potentially unfinished label until the next call
+                del labels[-1]
+                if labels:
+                    trailing_dot = "."
+
+        result = []
+        size = 0
+        for label in labels:
+            result.append(ulabel(label))
+            if size:
+                size += 1
+            size += len(label)
+
+        result_str = ".".join(result) + trailing_dot
+        size += len(trailing_dot)
+        return (result_str, size)
+
+
+class StreamWriter(Codec, codecs.StreamWriter):
+    pass
+
+
+class StreamReader(Codec, codecs.StreamReader):
+    pass
+
+
+def search_function(name: str) -> Optional[codecs.CodecInfo]:
+    """Codec search function registered with :mod:`codecs`.
+
+    Returns a :class:`codecs.CodecInfo` for the ``"idna2008"`` codec name
+    so that ``str.encode("idna2008")`` and ``bytes.decode("idna2008")``
+    invoke the IDNA 2008 codec defined in this module.
+
+    :param name: The codec name being looked up.
+    :returns: A :class:`codecs.CodecInfo` instance if ``name`` is
+        ``"idna2008"``, otherwise ``None``.
+    """
+    if name != "idna2008":
+        return None
+    return codecs.CodecInfo(
+        name=name,
+        encode=Codec().encode,
+        decode=Codec().decode,  # type: ignore
+        incrementalencoder=IncrementalEncoder,
+        incrementaldecoder=IncrementalDecoder,
+        streamwriter=StreamWriter,
+        streamreader=StreamReader,
+    )
+
+
+codecs.register(search_function)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/compat.py b/venv/lib/python3.12/site-packages/pip/_vendor/idna/compat.py
new file mode 100644
index 0000000..1d01e3d
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/idna/compat.py
@@ -0,0 +1,41 @@
+from typing import Any, Union
+
+from .core import decode, encode
+
+
+def ToASCII(label: str) -> bytes:
+    """Compatibility shim for :rfc:`3490` ``ToASCII``.
+
+    Delegates to :func:`idna.encode` (IDNA 2008). Provided to ease porting
+    of code written against the legacy :mod:`encodings.idna` API; new code
+    should call :func:`idna.encode` directly.
+
+    :param label: The label or domain to encode.
+    :returns: The encoded form as ASCII :class:`bytes`.
+    """
+    return encode(label)
+
+
+def ToUnicode(label: Union[bytes, bytearray]) -> str:
+    """Compatibility shim for :rfc:`3490` ``ToUnicode``.
+
+    Delegates to :func:`idna.decode` (IDNA 2008). Provided to ease porting
+    of code written against the legacy :mod:`encodings.idna` API; new code
+    should call :func:`idna.decode` directly.
+
+    :param label: The label or domain to decode.
+    :returns: The decoded Unicode form.
+    """
+    return decode(label)
+
+
+def nameprep(s: Any) -> None:
+    """Stub for :rfc:`3491` Nameprep, which is not used by IDNA 2008.
+
+    IDNA 2008 (:rfc:`5891`) replaces Nameprep with the per-codepoint
+    validity classes from :rfc:`5892`; this function exists only to
+    return a clear error if legacy code attempts to call it.
+
+    :raises NotImplementedError: Always.
+    """
+    raise NotImplementedError("IDNA 2008 does not utilise nameprep protocol")
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/core.py b/venv/lib/python3.12/site-packages/pip/_vendor/idna/core.py
new file mode 100644
index 0000000..1ccbd1f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/idna/core.py
@@ -0,0 +1,648 @@
+import bisect
+import re
+import unicodedata
+import warnings
+from typing import Optional, Union
+
+from . import idnadata
+from .intranges import intranges_contain
+
+_virama_combining_class = 9
+_alabel_prefix = b"xn--"
+_max_input_length = 1024
+_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]")
+
+
+# Bidi category sets from RFC 5893, hoisted out of the per-codepoint loop
+_bidi_rtl_first = frozenset({"R", "AL"})
+_bidi_rtl_categories = frozenset({"R", "AL", "AN"})
+_bidi_rtl_allowed = frozenset({"R", "AL", "AN", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"})
+_bidi_rtl_valid_ending = frozenset({"R", "AL", "EN", "AN"})
+_bidi_rtl_numeric = frozenset({"AN", "EN"})
+_bidi_ltr_allowed = frozenset({"L", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"})
+_bidi_ltr_valid_ending = frozenset({"L", "EN"})
+_bidi_joiner_l_or_d = frozenset({"L", "D"})
+_bidi_joiner_r_or_d = frozenset({"R", "D"})
+
+
+def _joining_type(cp: int) -> Optional[str]:
+    for jt, ranges in idnadata.joining_types.items():
+        if intranges_contain(cp, ranges):
+            return jt
+    return None
+
+
+class IDNAError(UnicodeError):
+    """Base exception for all IDNA-encoding related problems"""
+
+
+class IDNABidiError(IDNAError):
+    """Exception when bidirectional requirements are not satisfied"""
+
+
+class InvalidCodepoint(IDNAError):
+    """Exception when a disallowed or unallocated codepoint is used"""
+
+
+class InvalidCodepointContext(IDNAError):
+    """Exception when the codepoint is not valid in the context it is used"""
+
+
+def _combining_class(cp: int) -> int:
+    v = unicodedata.combining(chr(cp))
+    if v == 0 and not unicodedata.name(chr(cp)):
+        raise ValueError("Unknown character in unicodedata")
+    return v
+
+
+def _is_script(cp: str, script: str) -> bool:
+    return intranges_contain(ord(cp), idnadata.scripts[script])
+
+
+def _punycode(s: str) -> bytes:
+    return s.encode("punycode")
+
+
+def _unot(s: int) -> str:
+    return f"U+{s:04X}"
+
+
+def valid_label_length(label: Union[bytes, str]) -> bool:
+    """Check that a label does not exceed the maximum permitted length.
+
+    Per :rfc:`1035` (and :rfc:`5891` §4.2.4) a DNS label must not exceed
+    63 octets. The argument may be either a :class:`str` (a U-label, where
+    length is measured in characters) or :class:`bytes` (an A-label, where
+    length is measured in octets).
+
+    :param label: The label to check.
+    :returns: ``True`` if the label is within the length limit, otherwise
+        ``False``.
+    """
+    return len(label) <= 63
+
+
+def valid_string_length(domain: Union[bytes, str], trailing_dot: bool) -> bool:
+    """Check that a full domain name does not exceed the maximum length.
+
+    Per :rfc:`1035`, a domain name is limited to 253 octets when no trailing
+    dot is present, or 254 octets when one is included.
+
+    :param domain: The full (possibly multi-label) domain name.
+    :param trailing_dot: ``True`` if ``domain`` includes a trailing ``.``.
+    :returns: ``True`` if the domain is within the length limit, otherwise
+        ``False``.
+    """
+    return len(domain) <= (254 if trailing_dot else 253)
+
+
+def check_bidi(label: str, check_ltr: bool = False) -> bool:
+    """Validate the Bidi Rule from :rfc:`5893` for a single label.
+
+    The Bidi Rule constrains how bidirectional characters (Hebrew, Arabic,
+    etc.) may appear within a label. By default the check is only applied
+    when the label contains at least one right-to-left character (Unicode
+    bidirectional categories ``R``, ``AL``, or ``AN``); set ``check_ltr``
+    to ``True`` to apply it to LTR-only labels as well.
+
+    :param label: The label to validate, as a Unicode string.
+    :param check_ltr: If ``True``, apply the rules even when the label
+        contains no RTL characters.
+    :returns: ``True`` if the label satisfies the Bidi Rule.
+    :raises IDNABidiError: If any of Bidi Rule conditions 1-6 are violated,
+        or if the directional category of a codepoint cannot be determined.
+    """
+    if len(label) > _max_input_length:
+        raise IDNAError("Label too long")
+    # Bidi rules should only be applied if string contains RTL characters
+    bidi_label = False
+    for idx, cp in enumerate(label, 1):
+        direction = unicodedata.bidirectional(cp)
+        if direction == "":
+            # String likely comes from a newer version of Unicode
+            raise IDNABidiError(f"Unknown directionality in label {label!r} at position {idx}")
+        if direction in _bidi_rtl_categories:
+            bidi_label = True
+    if not bidi_label and not check_ltr:
+        return True
+
+    # Bidi rule 1
+    direction = unicodedata.bidirectional(label[0])
+    if direction in _bidi_rtl_first:
+        rtl = True
+    elif direction == "L":
+        rtl = False
+    else:
+        raise IDNABidiError(f"First codepoint in label {label!r} must be directionality L, R or AL")
+
+    valid_ending = False
+    number_type: Optional[str] = None
+    for idx, cp in enumerate(label, 1):
+        direction = unicodedata.bidirectional(cp)
+
+        if rtl:
+            # Bidi rule 2
+            if direction not in _bidi_rtl_allowed:
+                raise IDNABidiError(f"Invalid direction for codepoint at position {idx} in a right-to-left label")
+            # Bidi rule 3
+            if direction in _bidi_rtl_valid_ending:
+                valid_ending = True
+            elif direction != "NSM":
+                valid_ending = False
+            # Bidi rule 4
+            if direction in _bidi_rtl_numeric:
+                if not number_type:
+                    number_type = direction
+                elif number_type != direction:
+                    raise IDNABidiError("Can not mix numeral types in a right-to-left label")
+        else:
+            # Bidi rule 5
+            if direction not in _bidi_ltr_allowed:
+                raise IDNABidiError(f"Invalid direction for codepoint at position {idx} in a left-to-right label")
+            # Bidi rule 6
+            if direction in _bidi_ltr_valid_ending:
+                valid_ending = True
+            elif direction != "NSM":
+                valid_ending = False
+
+    if not valid_ending:
+        raise IDNABidiError("Label ends with illegal codepoint directionality")
+
+    return True
+
+
+def check_initial_combiner(label: str) -> bool:
+    """Reject labels that begin with a combining mark.
+
+    Per :rfc:`5891` §4.2.3.2 a label must not start with a character of
+    Unicode general category ``M`` (Mark).
+
+    :param label: The label to check.
+    :returns: ``True`` if the first character is not a combining mark.
+    :raises IDNAError: If the label begins with a combining character.
+    """
+    if unicodedata.category(label[0])[0] == "M":
+        raise IDNAError("Label begins with an illegal combining character")
+    return True
+
+
+def check_hyphen_ok(label: str) -> bool:
+    """Validate the hyphen restrictions for a label.
+
+    Per :rfc:`5891` §4.2.3.1 a label must not start or end with a hyphen
+    (``U+002D``), and must not have hyphens in both the third and fourth
+    positions (the prefix reserved for A-labels).
+
+    :param label: The label to check.
+    :returns: ``True`` if the hyphen restrictions are satisfied.
+    :raises IDNAError: If any of the hyphen restrictions are violated.
+    """
+    if label[2:4] == "--":
+        raise IDNAError("Label has disallowed hyphens in 3rd and 4th position")
+    if label[0] == "-" or label[-1] == "-":
+        raise IDNAError("Label must not start or end with a hyphen")
+    return True
+
+
+def check_nfc(label: str) -> None:
+    """Require that a label is in Unicode Normalization Form C.
+
+    :param label: The label to check.
+    :raises IDNAError: If ``label`` differs from its NFC normalisation.
+    """
+    if len(label) > _max_input_length:
+        raise IDNAError("Label too long")
+    if unicodedata.normalize("NFC", label) != label:
+        raise IDNAError("Label must be in Normalization Form C")
+
+
+def valid_contextj(label: str, pos: int) -> bool:
+    """Validate the CONTEXTJ rules from :rfc:`5892` Appendix A.
+
+    These rules govern the contextual use of the joiner codepoints
+    ``U+200C`` (ZERO WIDTH NON-JOINER, Appendix A.1) and ``U+200D``
+    (ZERO WIDTH JOINER, Appendix A.2) within a label.
+
+    :param label: The label containing the codepoint.
+    :param pos: Index of the joiner codepoint within ``label``.
+    :returns: ``True`` if the codepoint at ``pos`` satisfies its CONTEXTJ
+        rule, ``False`` otherwise (including when the codepoint at
+        ``pos`` is not a recognised joiner).
+    :raises ValueError: If an adjacent codepoint has no Unicode name when
+        determining its combining class.
+    :raises IDNAError: If ``label`` exceeds the defensive input length limit.
+    """
+    if len(label) > _max_input_length:
+        raise IDNAError("Label too long")
+    cp_value = ord(label[pos])
+
+    if cp_value == 0x200C:
+        if pos > 0 and _combining_class(ord(label[pos - 1])) == _virama_combining_class:
+            return True
+
+        ok = False
+        for i in range(pos - 1, -1, -1):
+            joining_type = _joining_type(ord(label[i]))
+            if joining_type == "T":
+                continue
+            if joining_type in _bidi_joiner_l_or_d:
+                ok = True
+                break
+            break
+
+        if not ok:
+            return False
+
+        ok = False
+        for i in range(pos + 1, len(label)):
+            joining_type = _joining_type(ord(label[i]))
+            if joining_type == "T":
+                continue
+            if joining_type in _bidi_joiner_r_or_d:
+                ok = True
+                break
+            break
+        return ok
+
+    if cp_value == 0x200D:
+        return pos > 0 and _combining_class(ord(label[pos - 1])) == _virama_combining_class
+
+    return False
+
+
+def valid_contexto(label: str, pos: int, exception: bool = False) -> bool:
+    """Validate the CONTEXTO rules from :rfc:`5892` Appendix A.
+
+    Covers the contextual rules for codepoints such as MIDDLE DOT
+    (``U+00B7``), Greek lower numeral sign, Hebrew punctuation, Katakana
+    middle dot, and the Arabic-Indic / Extended Arabic-Indic digit ranges.
+
+    :param label: The label containing the codepoint.
+    :param pos: Index of the codepoint within ``label``.
+    :param exception: Reserved for forward compatibility; currently unused.
+    :returns: ``True`` if the codepoint at ``pos`` satisfies its CONTEXTO
+        rule, ``False`` otherwise (including when the codepoint is not a
+        recognised CONTEXTO codepoint).
+    :raises IDNAError: If ``label`` exceeds the defensive input length limit.
+    """
+    if len(label) > _max_input_length:
+        raise IDNAError("Label too long")
+    cp_value = ord(label[pos])
+
+    if cp_value == 0x00B7:
+        return 0 < pos < len(label) - 1 and ord(label[pos - 1]) == 0x006C and ord(label[pos + 1]) == 0x006C
+
+    if cp_value == 0x0375:
+        if pos < len(label) - 1 and len(label) > 1:
+            return _is_script(label[pos + 1], "Greek")
+        return False
+
+    if cp_value in {0x05F3, 0x05F4}:
+        if pos > 0:
+            return _is_script(label[pos - 1], "Hebrew")
+        return False
+
+    if cp_value == 0x30FB:
+        for cp in label:
+            if cp == "\u30fb":
+                continue
+            if _is_script(cp, "Hiragana") or _is_script(cp, "Katakana") or _is_script(cp, "Han"):
+                return True
+        return False
+
+    if 0x660 <= cp_value <= 0x669:
+        return not any(0x6F0 <= ord(cp) <= 0x06F9 for cp in label)
+
+    if 0x6F0 <= cp_value <= 0x6F9:
+        return not any(0x660 <= ord(cp) <= 0x0669 for cp in label)
+
+    return False
+
+
+def check_label(label: Union[str, bytes, bytearray]) -> None:
+    """Run the full set of IDNA 2008 validity checks on a single label.
+
+    Applies, in order: NFC normalisation (:func:`check_nfc`), hyphen
+    restrictions (:func:`check_hyphen_ok`), the no-leading-combiner rule
+    (:func:`check_initial_combiner`), per-codepoint validity (PVALID,
+    CONTEXTJ, CONTEXTO classes from :rfc:`5892`), and the Bidi Rule
+    (:func:`check_bidi`).
+
+    :param label: The label to validate. ``bytes`` or ``bytearray`` input
+        is decoded as UTF-8 first.
+    :raises IDNAError: If the label is empty or fails a structural rule.
+    :raises InvalidCodepoint: If the label contains a DISALLOWED or
+        UNASSIGNED codepoint.
+    :raises InvalidCodepointContext: If a CONTEXTJ or CONTEXTO codepoint
+        is not valid in its context.
+    :raises IDNABidiError: If the Bidi Rule is violated.
+    """
+    if len(label) > _max_input_length:
+        raise IDNAError("Label too long")
+    if isinstance(label, (bytes, bytearray)):
+        label = label.decode("utf-8")
+    if len(label) == 0:
+        raise IDNAError("Empty Label")
+
+    # Reject on domain length rather than label length so support some UTS 46
+    # use cases, still reducing processing of label contextual rules
+    if not valid_string_length(label, trailing_dot=True):
+        raise IDNAError("Label too long")
+
+    check_nfc(label)
+    check_hyphen_ok(label)
+    check_initial_combiner(label)
+
+    for pos, cp in enumerate(label):
+        cp_value = ord(cp)
+        if intranges_contain(cp_value, idnadata.codepoint_classes["PVALID"]):
+            continue
+        if intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTJ"]):
+            try:
+                if not valid_contextj(label, pos):
+                    raise InvalidCodepointContext(f"Joiner {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}")
+            except ValueError as err:
+                raise IDNAError(
+                    f"Unknown codepoint adjacent to joiner {_unot(cp_value)} at position {pos + 1} in {label!r}"
+                ) from err
+        elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTO"]):
+            if not valid_contexto(label, pos):
+                raise InvalidCodepointContext(f"Codepoint {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}")
+        else:
+            raise InvalidCodepoint(f"Codepoint {_unot(cp_value)} at position {pos + 1} of {label!r} not allowed")
+
+    check_bidi(label)
+
+
+def alabel(label: str) -> bytes:
+    """Convert a single U-label into its A-label form.
+
+    The result is the ASCII-Compatible Encoding (ACE) form per :rfc:`5891`
+    §4: the label is validated, Punycode-encoded, and prefixed with
+    ``xn--``. Pure ASCII labels that are already valid IDNA labels are
+    returned unchanged (as :class:`bytes`).
+
+    :param label: The label to convert, as a Unicode string.
+    :returns: The A-label as ASCII-encoded :class:`bytes`.
+    :raises IDNAError: If the label is invalid or the resulting A-label
+        exceeds 63 octets.
+    """
+    if len(label) > _max_input_length:
+        raise IDNAError("Label too long")
+    try:
+        label_bytes = label.encode("ascii")
+    except UnicodeEncodeError:
+        pass
+    else:
+        ulabel(label_bytes)
+        if not valid_label_length(label_bytes):
+            raise IDNAError("Label too long")
+        return label_bytes
+
+    check_label(label)
+    label_bytes = _alabel_prefix + _punycode(label)
+
+    if not valid_label_length(label_bytes):
+        raise IDNAError("Label too long")
+
+    return label_bytes
+
+
+def ulabel(label: Union[str, bytes, bytearray]) -> str:
+    """Convert a single A-label into its U-label form.
+
+    Performs the inverse of :func:`alabel`: an ``xn--``-prefixed label is
+    Punycode-decoded and validated. Labels that are already Unicode (or
+    plain ASCII without the ACE prefix) are validated and returned as a
+    Unicode string.
+
+    :param label: The label to convert. ``bytes`` or ``bytearray`` input
+        is treated as ASCII.
+    :returns: The U-label as a Unicode string.
+    :raises IDNAError: If the label is malformed or fails validation.
+    """
+    if len(label) > _max_input_length:
+        raise IDNAError("Label too long")
+    if not isinstance(label, (bytes, bytearray)):
+        try:
+            label_bytes = label.encode("ascii")
+        except UnicodeEncodeError:
+            check_label(label)
+            return label
+    else:
+        label_bytes = bytes(label)
+
+    label_bytes = label_bytes.lower()
+    if label_bytes.startswith(_alabel_prefix):
+        label_bytes = label_bytes[len(_alabel_prefix) :]
+        if not label_bytes:
+            raise IDNAError("Malformed A-label, no Punycode eligible content found")
+        if label_bytes.endswith(b"-"):
+            raise IDNAError("A-label must not end with a hyphen")
+    else:
+        check_label(label_bytes)
+        return label_bytes.decode("ascii")
+
+    try:
+        label = label_bytes.decode("punycode")
+    except UnicodeError as err:
+        raise IDNAError("Invalid A-label") from err
+    check_label(label)
+    return label
+
+
+def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str:
+    """Apply the UTS #46 character mapping to a domain string.
+
+    Implements the mapping table from `UTS #46 §4
+    `_: each character is kept,
+    replaced, or rejected based on its status (``V``, ``M``, ``D``, ``3``,
+    ``I``). The result is returned in Normalisation Form C.
+
+    :param domain: The full domain name to remap.
+    :param std3_rules: If ``True``, apply the stricter STD3 ASCII rules
+        (status ``3`` codepoints raise instead of being kept or mapped).
+    :param transitional: If ``True``, use transitional processing (status
+        ``D`` codepoints are mapped instead of kept). Transitional
+        processing has been removed from UTS #46 and this option is
+        retained only for backwards compatibility.
+    :returns: The remapped domain, in Normalisation Form C.
+    :raises InvalidCodepoint: If the domain contains a disallowed
+        codepoint under the chosen rules.
+    :raises IDNAError: If ``domain`` exceeds the defensive input length limit.
+    """
+    if len(domain) > _max_input_length:
+        raise IDNAError("Domain too long")
+    from .uts46data import uts46_replacements, uts46_starts, uts46_statuses
+
+    output = ""
+
+    for pos, char in enumerate(domain):
+        code_point = ord(char)
+        i = code_point if code_point < 256 else bisect.bisect_right(uts46_starts, code_point) - 1
+        status = chr(uts46_statuses[i])
+        replacement: Optional[str] = uts46_replacements[i]
+
+        # UTS #46 §4: V is always valid, D is deviation (kept unless transitional),
+        # 3 is disallowed-STD3 (kept unmapped if std3_rules is off and no mapping).
+        keep_as_is = (
+            status == "V" or (status == "D" and not transitional) or (status == "3" and not std3_rules and replacement is None)
+        )
+        # M is mapped, 3-with-replacement and transitional D fall through to the
+        # same replacement output path.
+        use_replacement = replacement is not None and (
+            status == "M" or (status == "3" and not std3_rules) or (status == "D" and transitional)
+        )
+
+        if keep_as_is:
+            output += char
+        elif use_replacement:
+            assert replacement is not None  # narrowed by use_replacement
+            output += replacement
+        elif status == "I":
+            continue
+        else:
+            raise InvalidCodepoint(f"Codepoint {_unot(code_point)} not allowed at position {pos + 1} in {domain!r}")
+
+    return unicodedata.normalize("NFC", output)
+
+
+def encode(
+    s: Union[str, bytes, bytearray],
+    strict: bool = False,
+    uts46: bool = False,
+    std3_rules: bool = False,
+    transitional: bool = False,
+) -> bytes:
+    """Encode a Unicode domain name into its ASCII (A-label) form.
+
+    Splits the input on label separators (only ``U+002E`` if ``strict`` is
+    set; otherwise also IDEOGRAPHIC FULL STOP ``U+3002``, FULLWIDTH FULL
+    STOP ``U+FF0E``, and HALFWIDTH IDEOGRAPHIC FULL STOP ``U+FF61``),
+    encodes each label with :func:`alabel`, and rejoins them with ``.``.
+    Optionally pre-processes the input through :func:`uts46_remap`.
+
+    :param s: The domain name to encode.
+    :param strict: If ``True``, only ``U+002E`` is recognised as a label
+        separator.
+    :param uts46: If ``True``, apply UTS #46 mapping before encoding.
+    :param std3_rules: Forwarded to :func:`uts46_remap` when ``uts46`` is
+        ``True``.
+    :param transitional: Forwarded to :func:`uts46_remap` when ``uts46``
+        is ``True``. Deprecated: emits a :class:`DeprecationWarning` and
+        will be removed in a future version.
+    :returns: The encoded domain as ASCII :class:`bytes`.
+    :raises IDNAError: If the domain is empty, contains an invalid label,
+        or exceeds the maximum domain length.
+    """
+    if transitional:
+        warnings.warn(
+            "Transitional processing has been removed from UTS #46. "
+            "The transitional argument will be removed in a future version.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+    if not isinstance(s, str):
+        try:
+            s = str(s, "ascii")
+        except (UnicodeDecodeError, TypeError) as err:
+            raise IDNAError("should pass a unicode string to the function rather than a byte string.") from err
+    if len(s) > _max_input_length:
+        raise IDNAError("Domain too long")
+    if uts46:
+        s = uts46_remap(s, std3_rules, transitional)
+
+    # Reject inputs that exceed the maximum DNS domain length up-front
+    # to avoid expensive computation on long inputs.
+    if not valid_string_length(s, trailing_dot=True):
+        raise IDNAError("Domain too long")
+
+    trailing_dot = False
+    result = []
+    labels = s.split(".") if strict else _unicode_dots_re.split(s)
+    if not labels or labels == [""]:
+        raise IDNAError("Empty domain")
+    if labels[-1] == "":
+        del labels[-1]
+        trailing_dot = True
+    for label in labels:
+        s = alabel(label)
+        if s:
+            result.append(s)
+        else:
+            raise IDNAError("Empty label")
+    if trailing_dot:
+        result.append(b"")
+    s = b".".join(result)
+    if not valid_string_length(s, trailing_dot):
+        raise IDNAError("Domain too long")
+    return s
+
+
+def decode(
+    s: Union[str, bytes, bytearray],
+    strict: bool = False,
+    uts46: bool = False,
+    std3_rules: bool = False,
+    display: bool = False,
+) -> str:
+    """Decode an A-label-encoded domain name back to Unicode.
+
+    Splits the input on label separators (see :func:`encode` for the
+    rules), decodes each label with :func:`ulabel`, and rejoins them
+    with ``.``. Optionally pre-processes the input through
+    :func:`uts46_remap`.
+
+    :param s: The domain name to decode.
+    :param strict: If ``True``, only ``U+002E`` is recognised as a label
+        separator.
+    :param uts46: If ``True``, apply UTS #46 mapping before decoding.
+    :param std3_rules: Forwarded to :func:`uts46_remap` when ``uts46`` is
+        ``True``.
+    :param display: If ``True``, any ``xn--`` label that fails IDNA
+        validation is passed through unchanged (lowercased) rather than
+        aborting the whole call. Intended for "decode for display"
+        consumers (e.g. URL libraries, HTTP clients) that want to show
+        the user the label as it appears on the wire when it cannot be
+        rendered as Unicode. Matches the per-label recovery prescribed
+        by UTS #46 §4 and the WHATWG URL "domain to Unicode" algorithm.
+    :returns: The decoded domain as a Unicode string.
+    :raises IDNAError: If the input is not valid ASCII, contains an
+        invalid label, or is empty.
+    """
+    if not isinstance(s, str):
+        try:
+            s = str(s, "ascii")
+        except (UnicodeDecodeError, TypeError) as err:
+            raise IDNAError("Invalid ASCII in A-label") from err
+    if len(s) > _max_input_length:
+        raise IDNAError("Domain too long")
+    if uts46:
+        s = uts46_remap(s, std3_rules, False)
+    # Reject inputs that exceed the maximum DNS domain length up-front
+    # to avoid expensive computation on long inputs.
+    if not valid_string_length(s, trailing_dot=True):
+        raise IDNAError("Domain too long")
+    trailing_dot = False
+    result = []
+    labels = s.split(".") if strict else _unicode_dots_re.split(s)
+    if not labels or labels == [""]:
+        raise IDNAError("Empty domain")
+    if not labels[-1]:
+        del labels[-1]
+        trailing_dot = True
+    for label in labels:
+        try:
+            u = ulabel(label)
+        except IDNAError:
+            if display and label[:4].lower() == "xn--":
+                u = label.lower()
+            else:
+                raise
+        if u:
+            result.append(u)
+        else:
+            raise IDNAError("Empty label")
+    if trailing_dot:
+        result.append("")
+    return ".".join(result)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/idnadata.py b/venv/lib/python3.12/site-packages/pip/_vendor/idna/idnadata.py
new file mode 100644
index 0000000..f2ab388
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/idna/idnadata.py
@@ -0,0 +1,1897 @@
+# This file is automatically generated by tools/idna-data
+
+__version__ = "17.0.0"
+
+scripts = {
+    "Greek": (
+        0x37000000374,
+        0x37500000378,
+        0x37A0000037E,
+        0x37F00000380,
+        0x38400000385,
+        0x38600000387,
+        0x3880000038B,
+        0x38C0000038D,
+        0x38E000003A2,
+        0x3A3000003E2,
+        0x3F000000400,
+        0x1D2600001D2B,
+        0x1D5D00001D62,
+        0x1D6600001D6B,
+        0x1DBF00001DC0,
+        0x1F0000001F16,
+        0x1F1800001F1E,
+        0x1F2000001F46,
+        0x1F4800001F4E,
+        0x1F5000001F58,
+        0x1F5900001F5A,
+        0x1F5B00001F5C,
+        0x1F5D00001F5E,
+        0x1F5F00001F7E,
+        0x1F8000001FB5,
+        0x1FB600001FC5,
+        0x1FC600001FD4,
+        0x1FD600001FDC,
+        0x1FDD00001FF0,
+        0x1FF200001FF5,
+        0x1FF600001FFF,
+        0x212600002127,
+        0xAB650000AB66,
+        0x101400001018F,
+        0x101A0000101A1,
+        0x1D2000001D246,
+    ),
+    "Han": (
+        0x2E8000002E9A,
+        0x2E9B00002EF4,
+        0x2F0000002FD6,
+        0x300500003006,
+        0x300700003008,
+        0x30210000302A,
+        0x30380000303C,
+        0x340000004DC0,
+        0x4E000000A000,
+        0xF9000000FA6E,
+        0xFA700000FADA,
+        0x16FE200016FE4,
+        0x16FF000016FF7,
+        0x200000002A6E0,
+        0x2A7000002B81E,
+        0x2B8200002CEAE,
+        0x2CEB00002EBE1,
+        0x2EBF00002EE5E,
+        0x2F8000002FA1E,
+        0x300000003134B,
+        0x313500003347A,
+    ),
+    "Hebrew": (
+        0x591000005C8,
+        0x5D0000005EB,
+        0x5EF000005F5,
+        0xFB1D0000FB37,
+        0xFB380000FB3D,
+        0xFB3E0000FB3F,
+        0xFB400000FB42,
+        0xFB430000FB45,
+        0xFB460000FB50,
+    ),
+    "Hiragana": (
+        0x304100003097,
+        0x309D000030A0,
+        0x1B0010001B120,
+        0x1B1320001B133,
+        0x1B1500001B153,
+        0x1F2000001F201,
+    ),
+    "Katakana": (
+        0x30A1000030FB,
+        0x30FD00003100,
+        0x31F000003200,
+        0x32D0000032FF,
+        0x330000003358,
+        0xFF660000FF70,
+        0xFF710000FF9E,
+        0x1AFF00001AFF4,
+        0x1AFF50001AFFC,
+        0x1AFFD0001AFFF,
+        0x1B0000001B001,
+        0x1B1200001B123,
+        0x1B1550001B156,
+        0x1B1640001B168,
+    ),
+}
+
+
+joining_types = {
+    "C": (
+        0x64000000641,
+        0x7FA000007FB,
+        0x88300000886,
+        0x180A0000180B,
+        0x200D0000200E,
+    ),
+    "D": (
+        0x62000000621,
+        0x62600000627,
+        0x62800000629,
+        0x62A0000062F,
+        0x63300000640,
+        0x64100000648,
+        0x6490000064B,
+        0x66E00000670,
+        0x67800000688,
+        0x69A000006C0,
+        0x6C1000006C3,
+        0x6CC000006CD,
+        0x6CE000006CF,
+        0x6D0000006D2,
+        0x6FA000006FD,
+        0x6FF00000700,
+        0x71200000715,
+        0x71A0000071E,
+        0x71F00000728,
+        0x7290000072A,
+        0x72B0000072C,
+        0x72D0000072F,
+        0x74E00000759,
+        0x75C0000076B,
+        0x76D00000771,
+        0x77200000773,
+        0x77500000778,
+        0x77A00000780,
+        0x7CA000007EB,
+        0x84100000846,
+        0x84800000849,
+        0x84A00000854,
+        0x85500000856,
+        0x86000000861,
+        0x86200000866,
+        0x86800000869,
+        0x88600000887,
+        0x8890000088E,
+        0x88F00000890,
+        0x8A0000008AA,
+        0x8AF000008B1,
+        0x8B3000008B9,
+        0x8BA000008C9,
+        0x180700001808,
+        0x182000001879,
+        0x1887000018A9,
+        0x18AA000018AB,
+        0xA8400000A872,
+        0x10AC000010AC5,
+        0x10AD300010AD7,
+        0x10AD800010ADD,
+        0x10ADE00010AE1,
+        0x10AEB00010AEF,
+        0x10B8000010B81,
+        0x10B8200010B83,
+        0x10B8600010B89,
+        0x10B8A00010B8C,
+        0x10B8D00010B8E,
+        0x10B9000010B91,
+        0x10BAD00010BAF,
+        0x10D0100010D22,
+        0x10D2300010D24,
+        0x10EC300010EC5,
+        0x10EC600010EC8,
+        0x10F3000010F33,
+        0x10F3400010F45,
+        0x10F5100010F54,
+        0x10F7000010F74,
+        0x10F7600010F82,
+        0x10FB000010FB1,
+        0x10FB200010FB4,
+        0x10FB800010FB9,
+        0x10FBB00010FBD,
+        0x10FBE00010FC0,
+        0x10FC100010FC2,
+        0x10FC400010FC5,
+        0x10FCA00010FCB,
+        0x1E9000001E944,
+    ),
+    "L": (
+        0xA8720000A873,
+        0x10ACD00010ACE,
+        0x10AD700010AD8,
+        0x10D0000010D01,
+        0x10FCB00010FCC,
+    ),
+    "R": (
+        0x62200000626,
+        0x62700000628,
+        0x6290000062A,
+        0x62F00000633,
+        0x64800000649,
+        0x67100000674,
+        0x67500000678,
+        0x6880000069A,
+        0x6C0000006C1,
+        0x6C3000006CC,
+        0x6CD000006CE,
+        0x6CF000006D0,
+        0x6D2000006D4,
+        0x6D5000006D6,
+        0x6EE000006F0,
+        0x71000000711,
+        0x7150000071A,
+        0x71E0000071F,
+        0x72800000729,
+        0x72A0000072B,
+        0x72C0000072D,
+        0x72F00000730,
+        0x74D0000074E,
+        0x7590000075C,
+        0x76B0000076D,
+        0x77100000772,
+        0x77300000775,
+        0x7780000077A,
+        0x84000000841,
+        0x84600000848,
+        0x8490000084A,
+        0x85400000855,
+        0x85600000859,
+        0x86700000868,
+        0x8690000086B,
+        0x87000000883,
+        0x88E0000088F,
+        0x8AA000008AD,
+        0x8AE000008AF,
+        0x8B1000008B3,
+        0x8B9000008BA,
+        0x10AC500010AC6,
+        0x10AC700010AC8,
+        0x10AC900010ACB,
+        0x10ACE00010AD3,
+        0x10ADD00010ADE,
+        0x10AE100010AE2,
+        0x10AE400010AE5,
+        0x10AEF00010AF0,
+        0x10B8100010B82,
+        0x10B8300010B86,
+        0x10B8900010B8A,
+        0x10B8C00010B8D,
+        0x10B8E00010B90,
+        0x10B9100010B92,
+        0x10BA900010BAD,
+        0x10D2200010D23,
+        0x10EC200010EC3,
+        0x10F3300010F34,
+        0x10F5400010F55,
+        0x10F7400010F76,
+        0x10FB400010FB7,
+        0x10FB900010FBB,
+        0x10FBD00010FBE,
+        0x10FC200010FC4,
+        0x10FC900010FCA,
+    ),
+    "T": (
+        0xAD000000AE,
+        0x30000000370,
+        0x4830000048A,
+        0x591000005BE,
+        0x5BF000005C0,
+        0x5C1000005C3,
+        0x5C4000005C6,
+        0x5C7000005C8,
+        0x6100000061B,
+        0x61C0000061D,
+        0x64B00000660,
+        0x67000000671,
+        0x6D6000006DD,
+        0x6DF000006E5,
+        0x6E7000006E9,
+        0x6EA000006EE,
+        0x70F00000710,
+        0x71100000712,
+        0x7300000074B,
+        0x7A6000007B1,
+        0x7EB000007F4,
+        0x7FD000007FE,
+        0x8160000081A,
+        0x81B00000824,
+        0x82500000828,
+        0x8290000082E,
+        0x8590000085C,
+        0x897000008A0,
+        0x8CA000008E2,
+        0x8E300000903,
+        0x93A0000093B,
+        0x93C0000093D,
+        0x94100000949,
+        0x94D0000094E,
+        0x95100000958,
+        0x96200000964,
+        0x98100000982,
+        0x9BC000009BD,
+        0x9C1000009C5,
+        0x9CD000009CE,
+        0x9E2000009E4,
+        0x9FE000009FF,
+        0xA0100000A03,
+        0xA3C00000A3D,
+        0xA4100000A43,
+        0xA4700000A49,
+        0xA4B00000A4E,
+        0xA5100000A52,
+        0xA7000000A72,
+        0xA7500000A76,
+        0xA8100000A83,
+        0xABC00000ABD,
+        0xAC100000AC6,
+        0xAC700000AC9,
+        0xACD00000ACE,
+        0xAE200000AE4,
+        0xAFA00000B00,
+        0xB0100000B02,
+        0xB3C00000B3D,
+        0xB3F00000B40,
+        0xB4100000B45,
+        0xB4D00000B4E,
+        0xB5500000B57,
+        0xB6200000B64,
+        0xB8200000B83,
+        0xBC000000BC1,
+        0xBCD00000BCE,
+        0xC0000000C01,
+        0xC0400000C05,
+        0xC3C00000C3D,
+        0xC3E00000C41,
+        0xC4600000C49,
+        0xC4A00000C4E,
+        0xC5500000C57,
+        0xC6200000C64,
+        0xC8100000C82,
+        0xCBC00000CBD,
+        0xCBF00000CC0,
+        0xCC600000CC7,
+        0xCCC00000CCE,
+        0xCE200000CE4,
+        0xD0000000D02,
+        0xD3B00000D3D,
+        0xD4100000D45,
+        0xD4D00000D4E,
+        0xD6200000D64,
+        0xD8100000D82,
+        0xDCA00000DCB,
+        0xDD200000DD5,
+        0xDD600000DD7,
+        0xE3100000E32,
+        0xE3400000E3B,
+        0xE4700000E4F,
+        0xEB100000EB2,
+        0xEB400000EBD,
+        0xEC800000ECF,
+        0xF1800000F1A,
+        0xF3500000F36,
+        0xF3700000F38,
+        0xF3900000F3A,
+        0xF7100000F7F,
+        0xF8000000F85,
+        0xF8600000F88,
+        0xF8D00000F98,
+        0xF9900000FBD,
+        0xFC600000FC7,
+        0x102D00001031,
+        0x103200001038,
+        0x10390000103B,
+        0x103D0000103F,
+        0x10580000105A,
+        0x105E00001061,
+        0x107100001075,
+        0x108200001083,
+        0x108500001087,
+        0x108D0000108E,
+        0x109D0000109E,
+        0x135D00001360,
+        0x171200001715,
+        0x173200001734,
+        0x175200001754,
+        0x177200001774,
+        0x17B4000017B6,
+        0x17B7000017BE,
+        0x17C6000017C7,
+        0x17C9000017D4,
+        0x17DD000017DE,
+        0x180B0000180E,
+        0x180F00001810,
+        0x188500001887,
+        0x18A9000018AA,
+        0x192000001923,
+        0x192700001929,
+        0x193200001933,
+        0x19390000193C,
+        0x1A1700001A19,
+        0x1A1B00001A1C,
+        0x1A5600001A57,
+        0x1A5800001A5F,
+        0x1A6000001A61,
+        0x1A6200001A63,
+        0x1A6500001A6D,
+        0x1A7300001A7D,
+        0x1A7F00001A80,
+        0x1AB000001ADE,
+        0x1AE000001AEC,
+        0x1B0000001B04,
+        0x1B3400001B35,
+        0x1B3600001B3B,
+        0x1B3C00001B3D,
+        0x1B4200001B43,
+        0x1B6B00001B74,
+        0x1B8000001B82,
+        0x1BA200001BA6,
+        0x1BA800001BAA,
+        0x1BAB00001BAE,
+        0x1BE600001BE7,
+        0x1BE800001BEA,
+        0x1BED00001BEE,
+        0x1BEF00001BF2,
+        0x1C2C00001C34,
+        0x1C3600001C38,
+        0x1CD000001CD3,
+        0x1CD400001CE1,
+        0x1CE200001CE9,
+        0x1CED00001CEE,
+        0x1CF400001CF5,
+        0x1CF800001CFA,
+        0x1DC000001E00,
+        0x200B0000200C,
+        0x200E00002010,
+        0x202A0000202F,
+        0x206000002065,
+        0x206A00002070,
+        0x20D0000020F1,
+        0x2CEF00002CF2,
+        0x2D7F00002D80,
+        0x2DE000002E00,
+        0x302A0000302E,
+        0x30990000309B,
+        0xA66F0000A673,
+        0xA6740000A67E,
+        0xA69E0000A6A0,
+        0xA6F00000A6F2,
+        0xA8020000A803,
+        0xA8060000A807,
+        0xA80B0000A80C,
+        0xA8250000A827,
+        0xA82C0000A82D,
+        0xA8C40000A8C6,
+        0xA8E00000A8F2,
+        0xA8FF0000A900,
+        0xA9260000A92E,
+        0xA9470000A952,
+        0xA9800000A983,
+        0xA9B30000A9B4,
+        0xA9B60000A9BA,
+        0xA9BC0000A9BE,
+        0xA9E50000A9E6,
+        0xAA290000AA2F,
+        0xAA310000AA33,
+        0xAA350000AA37,
+        0xAA430000AA44,
+        0xAA4C0000AA4D,
+        0xAA7C0000AA7D,
+        0xAAB00000AAB1,
+        0xAAB20000AAB5,
+        0xAAB70000AAB9,
+        0xAABE0000AAC0,
+        0xAAC10000AAC2,
+        0xAAEC0000AAEE,
+        0xAAF60000AAF7,
+        0xABE50000ABE6,
+        0xABE80000ABE9,
+        0xABED0000ABEE,
+        0xFB1E0000FB1F,
+        0xFE000000FE10,
+        0xFE200000FE30,
+        0xFEFF0000FF00,
+        0xFFF90000FFFC,
+        0x101FD000101FE,
+        0x102E0000102E1,
+        0x103760001037B,
+        0x10A0100010A04,
+        0x10A0500010A07,
+        0x10A0C00010A10,
+        0x10A3800010A3B,
+        0x10A3F00010A40,
+        0x10AE500010AE7,
+        0x10D2400010D28,
+        0x10D6900010D6E,
+        0x10EAB00010EAD,
+        0x10EFA00010F00,
+        0x10F4600010F51,
+        0x10F8200010F86,
+        0x1100100011002,
+        0x1103800011047,
+        0x1107000011071,
+        0x1107300011075,
+        0x1107F00011082,
+        0x110B3000110B7,
+        0x110B9000110BB,
+        0x110C2000110C3,
+        0x1110000011103,
+        0x111270001112C,
+        0x1112D00011135,
+        0x1117300011174,
+        0x1118000011182,
+        0x111B6000111BF,
+        0x111C9000111CD,
+        0x111CF000111D0,
+        0x1122F00011232,
+        0x1123400011235,
+        0x1123600011238,
+        0x1123E0001123F,
+        0x1124100011242,
+        0x112DF000112E0,
+        0x112E3000112EB,
+        0x1130000011302,
+        0x1133B0001133D,
+        0x1134000011341,
+        0x113660001136D,
+        0x1137000011375,
+        0x113BB000113C1,
+        0x113CE000113CF,
+        0x113D0000113D1,
+        0x113D2000113D3,
+        0x113E1000113E3,
+        0x1143800011440,
+        0x1144200011445,
+        0x1144600011447,
+        0x1145E0001145F,
+        0x114B3000114B9,
+        0x114BA000114BB,
+        0x114BF000114C1,
+        0x114C2000114C4,
+        0x115B2000115B6,
+        0x115BC000115BE,
+        0x115BF000115C1,
+        0x115DC000115DE,
+        0x116330001163B,
+        0x1163D0001163E,
+        0x1163F00011641,
+        0x116AB000116AC,
+        0x116AD000116AE,
+        0x116B0000116B6,
+        0x116B7000116B8,
+        0x1171D0001171E,
+        0x1171F00011720,
+        0x1172200011726,
+        0x117270001172C,
+        0x1182F00011838,
+        0x118390001183B,
+        0x1193B0001193D,
+        0x1193E0001193F,
+        0x1194300011944,
+        0x119D4000119D8,
+        0x119DA000119DC,
+        0x119E0000119E1,
+        0x11A0100011A0B,
+        0x11A3300011A39,
+        0x11A3B00011A3F,
+        0x11A4700011A48,
+        0x11A5100011A57,
+        0x11A5900011A5C,
+        0x11A8A00011A97,
+        0x11A9800011A9A,
+        0x11B6000011B61,
+        0x11B6200011B65,
+        0x11B6600011B67,
+        0x11C3000011C37,
+        0x11C3800011C3E,
+        0x11C3F00011C40,
+        0x11C9200011CA8,
+        0x11CAA00011CB1,
+        0x11CB200011CB4,
+        0x11CB500011CB7,
+        0x11D3100011D37,
+        0x11D3A00011D3B,
+        0x11D3C00011D3E,
+        0x11D3F00011D46,
+        0x11D4700011D48,
+        0x11D9000011D92,
+        0x11D9500011D96,
+        0x11D9700011D98,
+        0x11EF300011EF5,
+        0x11F0000011F02,
+        0x11F3600011F3B,
+        0x11F4000011F41,
+        0x11F4200011F43,
+        0x11F5A00011F5B,
+        0x1343000013441,
+        0x1344700013456,
+        0x1611E0001612A,
+        0x1612D00016130,
+        0x16AF000016AF5,
+        0x16B3000016B37,
+        0x16F4F00016F50,
+        0x16F8F00016F93,
+        0x16FE400016FE5,
+        0x1BC9D0001BC9F,
+        0x1BCA00001BCA4,
+        0x1CF000001CF2E,
+        0x1CF300001CF47,
+        0x1D1670001D16A,
+        0x1D1730001D183,
+        0x1D1850001D18C,
+        0x1D1AA0001D1AE,
+        0x1D2420001D245,
+        0x1DA000001DA37,
+        0x1DA3B0001DA6D,
+        0x1DA750001DA76,
+        0x1DA840001DA85,
+        0x1DA9B0001DAA0,
+        0x1DAA10001DAB0,
+        0x1E0000001E007,
+        0x1E0080001E019,
+        0x1E01B0001E022,
+        0x1E0230001E025,
+        0x1E0260001E02B,
+        0x1E08F0001E090,
+        0x1E1300001E137,
+        0x1E2AE0001E2AF,
+        0x1E2EC0001E2F0,
+        0x1E4EC0001E4F0,
+        0x1E5EE0001E5F0,
+        0x1E6E30001E6E4,
+        0x1E6E60001E6E7,
+        0x1E6EE0001E6F0,
+        0x1E6F50001E6F6,
+        0x1E8D00001E8D7,
+        0x1E9440001E94C,
+        0xE0001000E0002,
+        0xE0020000E0080,
+        0xE0100000E01F0,
+    ),
+}
+
+
+codepoint_classes = {
+    "PVALID": (
+        0x2D0000002E,
+        0x300000003A,
+        0x610000007B,
+        0xDF000000F7,
+        0xF800000100,
+        0x10100000102,
+        0x10300000104,
+        0x10500000106,
+        0x10700000108,
+        0x1090000010A,
+        0x10B0000010C,
+        0x10D0000010E,
+        0x10F00000110,
+        0x11100000112,
+        0x11300000114,
+        0x11500000116,
+        0x11700000118,
+        0x1190000011A,
+        0x11B0000011C,
+        0x11D0000011E,
+        0x11F00000120,
+        0x12100000122,
+        0x12300000124,
+        0x12500000126,
+        0x12700000128,
+        0x1290000012A,
+        0x12B0000012C,
+        0x12D0000012E,
+        0x12F00000130,
+        0x13100000132,
+        0x13500000136,
+        0x13700000139,
+        0x13A0000013B,
+        0x13C0000013D,
+        0x13E0000013F,
+        0x14200000143,
+        0x14400000145,
+        0x14600000147,
+        0x14800000149,
+        0x14B0000014C,
+        0x14D0000014E,
+        0x14F00000150,
+        0x15100000152,
+        0x15300000154,
+        0x15500000156,
+        0x15700000158,
+        0x1590000015A,
+        0x15B0000015C,
+        0x15D0000015E,
+        0x15F00000160,
+        0x16100000162,
+        0x16300000164,
+        0x16500000166,
+        0x16700000168,
+        0x1690000016A,
+        0x16B0000016C,
+        0x16D0000016E,
+        0x16F00000170,
+        0x17100000172,
+        0x17300000174,
+        0x17500000176,
+        0x17700000178,
+        0x17A0000017B,
+        0x17C0000017D,
+        0x17E0000017F,
+        0x18000000181,
+        0x18300000184,
+        0x18500000186,
+        0x18800000189,
+        0x18C0000018E,
+        0x19200000193,
+        0x19500000196,
+        0x1990000019C,
+        0x19E0000019F,
+        0x1A1000001A2,
+        0x1A3000001A4,
+        0x1A5000001A6,
+        0x1A8000001A9,
+        0x1AA000001AC,
+        0x1AD000001AE,
+        0x1B0000001B1,
+        0x1B4000001B5,
+        0x1B6000001B7,
+        0x1B9000001BC,
+        0x1BD000001C4,
+        0x1CE000001CF,
+        0x1D0000001D1,
+        0x1D2000001D3,
+        0x1D4000001D5,
+        0x1D6000001D7,
+        0x1D8000001D9,
+        0x1DA000001DB,
+        0x1DC000001DE,
+        0x1DF000001E0,
+        0x1E1000001E2,
+        0x1E3000001E4,
+        0x1E5000001E6,
+        0x1E7000001E8,
+        0x1E9000001EA,
+        0x1EB000001EC,
+        0x1ED000001EE,
+        0x1EF000001F1,
+        0x1F5000001F6,
+        0x1F9000001FA,
+        0x1FB000001FC,
+        0x1FD000001FE,
+        0x1FF00000200,
+        0x20100000202,
+        0x20300000204,
+        0x20500000206,
+        0x20700000208,
+        0x2090000020A,
+        0x20B0000020C,
+        0x20D0000020E,
+        0x20F00000210,
+        0x21100000212,
+        0x21300000214,
+        0x21500000216,
+        0x21700000218,
+        0x2190000021A,
+        0x21B0000021C,
+        0x21D0000021E,
+        0x21F00000220,
+        0x22100000222,
+        0x22300000224,
+        0x22500000226,
+        0x22700000228,
+        0x2290000022A,
+        0x22B0000022C,
+        0x22D0000022E,
+        0x22F00000230,
+        0x23100000232,
+        0x2330000023A,
+        0x23C0000023D,
+        0x23F00000241,
+        0x24200000243,
+        0x24700000248,
+        0x2490000024A,
+        0x24B0000024C,
+        0x24D0000024E,
+        0x24F000002B0,
+        0x2B9000002C2,
+        0x2C6000002D2,
+        0x2EC000002ED,
+        0x2EE000002EF,
+        0x30000000340,
+        0x34200000343,
+        0x3460000034F,
+        0x35000000370,
+        0x37100000372,
+        0x37300000374,
+        0x37700000378,
+        0x37B0000037E,
+        0x39000000391,
+        0x3AC000003CF,
+        0x3D7000003D8,
+        0x3D9000003DA,
+        0x3DB000003DC,
+        0x3DD000003DE,
+        0x3DF000003E0,
+        0x3E1000003E2,
+        0x3E3000003E4,
+        0x3E5000003E6,
+        0x3E7000003E8,
+        0x3E9000003EA,
+        0x3EB000003EC,
+        0x3ED000003EE,
+        0x3EF000003F0,
+        0x3F3000003F4,
+        0x3F8000003F9,
+        0x3FB000003FD,
+        0x43000000460,
+        0x46100000462,
+        0x46300000464,
+        0x46500000466,
+        0x46700000468,
+        0x4690000046A,
+        0x46B0000046C,
+        0x46D0000046E,
+        0x46F00000470,
+        0x47100000472,
+        0x47300000474,
+        0x47500000476,
+        0x47700000478,
+        0x4790000047A,
+        0x47B0000047C,
+        0x47D0000047E,
+        0x47F00000480,
+        0x48100000482,
+        0x48300000488,
+        0x48B0000048C,
+        0x48D0000048E,
+        0x48F00000490,
+        0x49100000492,
+        0x49300000494,
+        0x49500000496,
+        0x49700000498,
+        0x4990000049A,
+        0x49B0000049C,
+        0x49D0000049E,
+        0x49F000004A0,
+        0x4A1000004A2,
+        0x4A3000004A4,
+        0x4A5000004A6,
+        0x4A7000004A8,
+        0x4A9000004AA,
+        0x4AB000004AC,
+        0x4AD000004AE,
+        0x4AF000004B0,
+        0x4B1000004B2,
+        0x4B3000004B4,
+        0x4B5000004B6,
+        0x4B7000004B8,
+        0x4B9000004BA,
+        0x4BB000004BC,
+        0x4BD000004BE,
+        0x4BF000004C0,
+        0x4C2000004C3,
+        0x4C4000004C5,
+        0x4C6000004C7,
+        0x4C8000004C9,
+        0x4CA000004CB,
+        0x4CC000004CD,
+        0x4CE000004D0,
+        0x4D1000004D2,
+        0x4D3000004D4,
+        0x4D5000004D6,
+        0x4D7000004D8,
+        0x4D9000004DA,
+        0x4DB000004DC,
+        0x4DD000004DE,
+        0x4DF000004E0,
+        0x4E1000004E2,
+        0x4E3000004E4,
+        0x4E5000004E6,
+        0x4E7000004E8,
+        0x4E9000004EA,
+        0x4EB000004EC,
+        0x4ED000004EE,
+        0x4EF000004F0,
+        0x4F1000004F2,
+        0x4F3000004F4,
+        0x4F5000004F6,
+        0x4F7000004F8,
+        0x4F9000004FA,
+        0x4FB000004FC,
+        0x4FD000004FE,
+        0x4FF00000500,
+        0x50100000502,
+        0x50300000504,
+        0x50500000506,
+        0x50700000508,
+        0x5090000050A,
+        0x50B0000050C,
+        0x50D0000050E,
+        0x50F00000510,
+        0x51100000512,
+        0x51300000514,
+        0x51500000516,
+        0x51700000518,
+        0x5190000051A,
+        0x51B0000051C,
+        0x51D0000051E,
+        0x51F00000520,
+        0x52100000522,
+        0x52300000524,
+        0x52500000526,
+        0x52700000528,
+        0x5290000052A,
+        0x52B0000052C,
+        0x52D0000052E,
+        0x52F00000530,
+        0x5590000055A,
+        0x56000000587,
+        0x58800000589,
+        0x591000005BE,
+        0x5BF000005C0,
+        0x5C1000005C3,
+        0x5C4000005C6,
+        0x5C7000005C8,
+        0x5D0000005EB,
+        0x5EF000005F3,
+        0x6100000061B,
+        0x62000000640,
+        0x64100000660,
+        0x66E00000675,
+        0x679000006D4,
+        0x6D5000006DD,
+        0x6DF000006E9,
+        0x6EA000006F0,
+        0x6FA00000700,
+        0x7100000074B,
+        0x74D000007B2,
+        0x7C0000007F6,
+        0x7FD000007FE,
+        0x8000000082E,
+        0x8400000085C,
+        0x8600000086B,
+        0x87000000888,
+        0x88900000890,
+        0x897000008E2,
+        0x8E300000958,
+        0x96000000964,
+        0x96600000970,
+        0x97100000984,
+        0x9850000098D,
+        0x98F00000991,
+        0x993000009A9,
+        0x9AA000009B1,
+        0x9B2000009B3,
+        0x9B6000009BA,
+        0x9BC000009C5,
+        0x9C7000009C9,
+        0x9CB000009CF,
+        0x9D7000009D8,
+        0x9E0000009E4,
+        0x9E6000009F2,
+        0x9FC000009FD,
+        0x9FE000009FF,
+        0xA0100000A04,
+        0xA0500000A0B,
+        0xA0F00000A11,
+        0xA1300000A29,
+        0xA2A00000A31,
+        0xA3200000A33,
+        0xA3500000A36,
+        0xA3800000A3A,
+        0xA3C00000A3D,
+        0xA3E00000A43,
+        0xA4700000A49,
+        0xA4B00000A4E,
+        0xA5100000A52,
+        0xA5C00000A5D,
+        0xA6600000A76,
+        0xA8100000A84,
+        0xA8500000A8E,
+        0xA8F00000A92,
+        0xA9300000AA9,
+        0xAAA00000AB1,
+        0xAB200000AB4,
+        0xAB500000ABA,
+        0xABC00000AC6,
+        0xAC700000ACA,
+        0xACB00000ACE,
+        0xAD000000AD1,
+        0xAE000000AE4,
+        0xAE600000AF0,
+        0xAF900000B00,
+        0xB0100000B04,
+        0xB0500000B0D,
+        0xB0F00000B11,
+        0xB1300000B29,
+        0xB2A00000B31,
+        0xB3200000B34,
+        0xB3500000B3A,
+        0xB3C00000B45,
+        0xB4700000B49,
+        0xB4B00000B4E,
+        0xB5500000B58,
+        0xB5F00000B64,
+        0xB6600000B70,
+        0xB7100000B72,
+        0xB8200000B84,
+        0xB8500000B8B,
+        0xB8E00000B91,
+        0xB9200000B96,
+        0xB9900000B9B,
+        0xB9C00000B9D,
+        0xB9E00000BA0,
+        0xBA300000BA5,
+        0xBA800000BAB,
+        0xBAE00000BBA,
+        0xBBE00000BC3,
+        0xBC600000BC9,
+        0xBCA00000BCE,
+        0xBD000000BD1,
+        0xBD700000BD8,
+        0xBE600000BF0,
+        0xC0000000C0D,
+        0xC0E00000C11,
+        0xC1200000C29,
+        0xC2A00000C3A,
+        0xC3C00000C45,
+        0xC4600000C49,
+        0xC4A00000C4E,
+        0xC5500000C57,
+        0xC5800000C5B,
+        0xC5C00000C5E,
+        0xC6000000C64,
+        0xC6600000C70,
+        0xC8000000C84,
+        0xC8500000C8D,
+        0xC8E00000C91,
+        0xC9200000CA9,
+        0xCAA00000CB4,
+        0xCB500000CBA,
+        0xCBC00000CC5,
+        0xCC600000CC9,
+        0xCCA00000CCE,
+        0xCD500000CD7,
+        0xCDC00000CDF,
+        0xCE000000CE4,
+        0xCE600000CF0,
+        0xCF100000CF4,
+        0xD0000000D0D,
+        0xD0E00000D11,
+        0xD1200000D45,
+        0xD4600000D49,
+        0xD4A00000D4F,
+        0xD5400000D58,
+        0xD5F00000D64,
+        0xD6600000D70,
+        0xD7A00000D80,
+        0xD8100000D84,
+        0xD8500000D97,
+        0xD9A00000DB2,
+        0xDB300000DBC,
+        0xDBD00000DBE,
+        0xDC000000DC7,
+        0xDCA00000DCB,
+        0xDCF00000DD5,
+        0xDD600000DD7,
+        0xDD800000DE0,
+        0xDE600000DF0,
+        0xDF200000DF4,
+        0xE0100000E33,
+        0xE3400000E3B,
+        0xE4000000E4F,
+        0xE5000000E5A,
+        0xE8100000E83,
+        0xE8400000E85,
+        0xE8600000E8B,
+        0xE8C00000EA4,
+        0xEA500000EA6,
+        0xEA700000EB3,
+        0xEB400000EBE,
+        0xEC000000EC5,
+        0xEC600000EC7,
+        0xEC800000ECF,
+        0xED000000EDA,
+        0xEDE00000EE0,
+        0xF0000000F01,
+        0xF0B00000F0C,
+        0xF1800000F1A,
+        0xF2000000F2A,
+        0xF3500000F36,
+        0xF3700000F38,
+        0xF3900000F3A,
+        0xF3E00000F43,
+        0xF4400000F48,
+        0xF4900000F4D,
+        0xF4E00000F52,
+        0xF5300000F57,
+        0xF5800000F5C,
+        0xF5D00000F69,
+        0xF6A00000F6D,
+        0xF7100000F73,
+        0xF7400000F75,
+        0xF7A00000F81,
+        0xF8200000F85,
+        0xF8600000F93,
+        0xF9400000F98,
+        0xF9900000F9D,
+        0xF9E00000FA2,
+        0xFA300000FA7,
+        0xFA800000FAC,
+        0xFAD00000FB9,
+        0xFBA00000FBD,
+        0xFC600000FC7,
+        0x10000000104A,
+        0x10500000109E,
+        0x10D0000010FB,
+        0x10FD00001100,
+        0x120000001249,
+        0x124A0000124E,
+        0x125000001257,
+        0x125800001259,
+        0x125A0000125E,
+        0x126000001289,
+        0x128A0000128E,
+        0x1290000012B1,
+        0x12B2000012B6,
+        0x12B8000012BF,
+        0x12C0000012C1,
+        0x12C2000012C6,
+        0x12C8000012D7,
+        0x12D800001311,
+        0x131200001316,
+        0x13180000135B,
+        0x135D00001360,
+        0x138000001390,
+        0x13A0000013F6,
+        0x14010000166D,
+        0x166F00001680,
+        0x16810000169B,
+        0x16A0000016EB,
+        0x16F1000016F9,
+        0x170000001716,
+        0x171F00001735,
+        0x174000001754,
+        0x17600000176D,
+        0x176E00001771,
+        0x177200001774,
+        0x1780000017B4,
+        0x17B6000017D4,
+        0x17D7000017D8,
+        0x17DC000017DE,
+        0x17E0000017EA,
+        0x18100000181A,
+        0x182000001879,
+        0x1880000018AB,
+        0x18B0000018F6,
+        0x19000000191F,
+        0x19200000192C,
+        0x19300000193C,
+        0x19460000196E,
+        0x197000001975,
+        0x1980000019AC,
+        0x19B0000019CA,
+        0x19D0000019DA,
+        0x1A0000001A1C,
+        0x1A2000001A5F,
+        0x1A6000001A7D,
+        0x1A7F00001A8A,
+        0x1A9000001A9A,
+        0x1AA700001AA8,
+        0x1AB000001ABE,
+        0x1ABF00001ADE,
+        0x1AE000001AEC,
+        0x1B0000001B4D,
+        0x1B5000001B5A,
+        0x1B6B00001B74,
+        0x1B8000001BF4,
+        0x1C0000001C38,
+        0x1C4000001C4A,
+        0x1C4D00001C7E,
+        0x1C8A00001C8B,
+        0x1CD000001CD3,
+        0x1CD400001CFB,
+        0x1D0000001D2C,
+        0x1D2F00001D30,
+        0x1D3B00001D3C,
+        0x1D4E00001D4F,
+        0x1D6B00001D78,
+        0x1D7900001D9B,
+        0x1DC000001E00,
+        0x1E0100001E02,
+        0x1E0300001E04,
+        0x1E0500001E06,
+        0x1E0700001E08,
+        0x1E0900001E0A,
+        0x1E0B00001E0C,
+        0x1E0D00001E0E,
+        0x1E0F00001E10,
+        0x1E1100001E12,
+        0x1E1300001E14,
+        0x1E1500001E16,
+        0x1E1700001E18,
+        0x1E1900001E1A,
+        0x1E1B00001E1C,
+        0x1E1D00001E1E,
+        0x1E1F00001E20,
+        0x1E2100001E22,
+        0x1E2300001E24,
+        0x1E2500001E26,
+        0x1E2700001E28,
+        0x1E2900001E2A,
+        0x1E2B00001E2C,
+        0x1E2D00001E2E,
+        0x1E2F00001E30,
+        0x1E3100001E32,
+        0x1E3300001E34,
+        0x1E3500001E36,
+        0x1E3700001E38,
+        0x1E3900001E3A,
+        0x1E3B00001E3C,
+        0x1E3D00001E3E,
+        0x1E3F00001E40,
+        0x1E4100001E42,
+        0x1E4300001E44,
+        0x1E4500001E46,
+        0x1E4700001E48,
+        0x1E4900001E4A,
+        0x1E4B00001E4C,
+        0x1E4D00001E4E,
+        0x1E4F00001E50,
+        0x1E5100001E52,
+        0x1E5300001E54,
+        0x1E5500001E56,
+        0x1E5700001E58,
+        0x1E5900001E5A,
+        0x1E5B00001E5C,
+        0x1E5D00001E5E,
+        0x1E5F00001E60,
+        0x1E6100001E62,
+        0x1E6300001E64,
+        0x1E6500001E66,
+        0x1E6700001E68,
+        0x1E6900001E6A,
+        0x1E6B00001E6C,
+        0x1E6D00001E6E,
+        0x1E6F00001E70,
+        0x1E7100001E72,
+        0x1E7300001E74,
+        0x1E7500001E76,
+        0x1E7700001E78,
+        0x1E7900001E7A,
+        0x1E7B00001E7C,
+        0x1E7D00001E7E,
+        0x1E7F00001E80,
+        0x1E8100001E82,
+        0x1E8300001E84,
+        0x1E8500001E86,
+        0x1E8700001E88,
+        0x1E8900001E8A,
+        0x1E8B00001E8C,
+        0x1E8D00001E8E,
+        0x1E8F00001E90,
+        0x1E9100001E92,
+        0x1E9300001E94,
+        0x1E9500001E9A,
+        0x1E9C00001E9E,
+        0x1E9F00001EA0,
+        0x1EA100001EA2,
+        0x1EA300001EA4,
+        0x1EA500001EA6,
+        0x1EA700001EA8,
+        0x1EA900001EAA,
+        0x1EAB00001EAC,
+        0x1EAD00001EAE,
+        0x1EAF00001EB0,
+        0x1EB100001EB2,
+        0x1EB300001EB4,
+        0x1EB500001EB6,
+        0x1EB700001EB8,
+        0x1EB900001EBA,
+        0x1EBB00001EBC,
+        0x1EBD00001EBE,
+        0x1EBF00001EC0,
+        0x1EC100001EC2,
+        0x1EC300001EC4,
+        0x1EC500001EC6,
+        0x1EC700001EC8,
+        0x1EC900001ECA,
+        0x1ECB00001ECC,
+        0x1ECD00001ECE,
+        0x1ECF00001ED0,
+        0x1ED100001ED2,
+        0x1ED300001ED4,
+        0x1ED500001ED6,
+        0x1ED700001ED8,
+        0x1ED900001EDA,
+        0x1EDB00001EDC,
+        0x1EDD00001EDE,
+        0x1EDF00001EE0,
+        0x1EE100001EE2,
+        0x1EE300001EE4,
+        0x1EE500001EE6,
+        0x1EE700001EE8,
+        0x1EE900001EEA,
+        0x1EEB00001EEC,
+        0x1EED00001EEE,
+        0x1EEF00001EF0,
+        0x1EF100001EF2,
+        0x1EF300001EF4,
+        0x1EF500001EF6,
+        0x1EF700001EF8,
+        0x1EF900001EFA,
+        0x1EFB00001EFC,
+        0x1EFD00001EFE,
+        0x1EFF00001F08,
+        0x1F1000001F16,
+        0x1F2000001F28,
+        0x1F3000001F38,
+        0x1F4000001F46,
+        0x1F5000001F58,
+        0x1F6000001F68,
+        0x1F7000001F71,
+        0x1F7200001F73,
+        0x1F7400001F75,
+        0x1F7600001F77,
+        0x1F7800001F79,
+        0x1F7A00001F7B,
+        0x1F7C00001F7D,
+        0x1FB000001FB2,
+        0x1FB600001FB7,
+        0x1FC600001FC7,
+        0x1FD000001FD3,
+        0x1FD600001FD8,
+        0x1FE000001FE3,
+        0x1FE400001FE8,
+        0x1FF600001FF7,
+        0x214E0000214F,
+        0x218400002185,
+        0x2C3000002C60,
+        0x2C6100002C62,
+        0x2C6500002C67,
+        0x2C6800002C69,
+        0x2C6A00002C6B,
+        0x2C6C00002C6D,
+        0x2C7100002C72,
+        0x2C7300002C75,
+        0x2C7600002C7C,
+        0x2C8100002C82,
+        0x2C8300002C84,
+        0x2C8500002C86,
+        0x2C8700002C88,
+        0x2C8900002C8A,
+        0x2C8B00002C8C,
+        0x2C8D00002C8E,
+        0x2C8F00002C90,
+        0x2C9100002C92,
+        0x2C9300002C94,
+        0x2C9500002C96,
+        0x2C9700002C98,
+        0x2C9900002C9A,
+        0x2C9B00002C9C,
+        0x2C9D00002C9E,
+        0x2C9F00002CA0,
+        0x2CA100002CA2,
+        0x2CA300002CA4,
+        0x2CA500002CA6,
+        0x2CA700002CA8,
+        0x2CA900002CAA,
+        0x2CAB00002CAC,
+        0x2CAD00002CAE,
+        0x2CAF00002CB0,
+        0x2CB100002CB2,
+        0x2CB300002CB4,
+        0x2CB500002CB6,
+        0x2CB700002CB8,
+        0x2CB900002CBA,
+        0x2CBB00002CBC,
+        0x2CBD00002CBE,
+        0x2CBF00002CC0,
+        0x2CC100002CC2,
+        0x2CC300002CC4,
+        0x2CC500002CC6,
+        0x2CC700002CC8,
+        0x2CC900002CCA,
+        0x2CCB00002CCC,
+        0x2CCD00002CCE,
+        0x2CCF00002CD0,
+        0x2CD100002CD2,
+        0x2CD300002CD4,
+        0x2CD500002CD6,
+        0x2CD700002CD8,
+        0x2CD900002CDA,
+        0x2CDB00002CDC,
+        0x2CDD00002CDE,
+        0x2CDF00002CE0,
+        0x2CE100002CE2,
+        0x2CE300002CE5,
+        0x2CEC00002CED,
+        0x2CEE00002CF2,
+        0x2CF300002CF4,
+        0x2D0000002D26,
+        0x2D2700002D28,
+        0x2D2D00002D2E,
+        0x2D3000002D68,
+        0x2D7F00002D97,
+        0x2DA000002DA7,
+        0x2DA800002DAF,
+        0x2DB000002DB7,
+        0x2DB800002DBF,
+        0x2DC000002DC7,
+        0x2DC800002DCF,
+        0x2DD000002DD7,
+        0x2DD800002DDF,
+        0x2DE000002E00,
+        0x2E2F00002E30,
+        0x300500003008,
+        0x302A0000302E,
+        0x303C0000303D,
+        0x304100003097,
+        0x30990000309B,
+        0x309D0000309F,
+        0x30A1000030FB,
+        0x30FC000030FF,
+        0x310500003130,
+        0x31A0000031C0,
+        0x31F000003200,
+        0x340000004DC0,
+        0x4E000000A48D,
+        0xA4D00000A4FE,
+        0xA5000000A60D,
+        0xA6100000A62C,
+        0xA6410000A642,
+        0xA6430000A644,
+        0xA6450000A646,
+        0xA6470000A648,
+        0xA6490000A64A,
+        0xA64B0000A64C,
+        0xA64D0000A64E,
+        0xA64F0000A650,
+        0xA6510000A652,
+        0xA6530000A654,
+        0xA6550000A656,
+        0xA6570000A658,
+        0xA6590000A65A,
+        0xA65B0000A65C,
+        0xA65D0000A65E,
+        0xA65F0000A660,
+        0xA6610000A662,
+        0xA6630000A664,
+        0xA6650000A666,
+        0xA6670000A668,
+        0xA6690000A66A,
+        0xA66B0000A66C,
+        0xA66D0000A670,
+        0xA6740000A67E,
+        0xA67F0000A680,
+        0xA6810000A682,
+        0xA6830000A684,
+        0xA6850000A686,
+        0xA6870000A688,
+        0xA6890000A68A,
+        0xA68B0000A68C,
+        0xA68D0000A68E,
+        0xA68F0000A690,
+        0xA6910000A692,
+        0xA6930000A694,
+        0xA6950000A696,
+        0xA6970000A698,
+        0xA6990000A69A,
+        0xA69B0000A69C,
+        0xA69E0000A6E6,
+        0xA6F00000A6F2,
+        0xA7170000A720,
+        0xA7230000A724,
+        0xA7250000A726,
+        0xA7270000A728,
+        0xA7290000A72A,
+        0xA72B0000A72C,
+        0xA72D0000A72E,
+        0xA72F0000A732,
+        0xA7330000A734,
+        0xA7350000A736,
+        0xA7370000A738,
+        0xA7390000A73A,
+        0xA73B0000A73C,
+        0xA73D0000A73E,
+        0xA73F0000A740,
+        0xA7410000A742,
+        0xA7430000A744,
+        0xA7450000A746,
+        0xA7470000A748,
+        0xA7490000A74A,
+        0xA74B0000A74C,
+        0xA74D0000A74E,
+        0xA74F0000A750,
+        0xA7510000A752,
+        0xA7530000A754,
+        0xA7550000A756,
+        0xA7570000A758,
+        0xA7590000A75A,
+        0xA75B0000A75C,
+        0xA75D0000A75E,
+        0xA75F0000A760,
+        0xA7610000A762,
+        0xA7630000A764,
+        0xA7650000A766,
+        0xA7670000A768,
+        0xA7690000A76A,
+        0xA76B0000A76C,
+        0xA76D0000A76E,
+        0xA76F0000A770,
+        0xA7710000A779,
+        0xA77A0000A77B,
+        0xA77C0000A77D,
+        0xA77F0000A780,
+        0xA7810000A782,
+        0xA7830000A784,
+        0xA7850000A786,
+        0xA7870000A789,
+        0xA78C0000A78D,
+        0xA78E0000A790,
+        0xA7910000A792,
+        0xA7930000A796,
+        0xA7970000A798,
+        0xA7990000A79A,
+        0xA79B0000A79C,
+        0xA79D0000A79E,
+        0xA79F0000A7A0,
+        0xA7A10000A7A2,
+        0xA7A30000A7A4,
+        0xA7A50000A7A6,
+        0xA7A70000A7A8,
+        0xA7A90000A7AA,
+        0xA7AF0000A7B0,
+        0xA7B50000A7B6,
+        0xA7B70000A7B8,
+        0xA7B90000A7BA,
+        0xA7BB0000A7BC,
+        0xA7BD0000A7BE,
+        0xA7BF0000A7C0,
+        0xA7C10000A7C2,
+        0xA7C30000A7C4,
+        0xA7C80000A7C9,
+        0xA7CA0000A7CB,
+        0xA7CD0000A7CE,
+        0xA7CF0000A7D0,
+        0xA7D10000A7D2,
+        0xA7D30000A7D4,
+        0xA7D50000A7D6,
+        0xA7D70000A7D8,
+        0xA7D90000A7DA,
+        0xA7DB0000A7DC,
+        0xA7F60000A7F8,
+        0xA7FA0000A828,
+        0xA82C0000A82D,
+        0xA8400000A874,
+        0xA8800000A8C6,
+        0xA8D00000A8DA,
+        0xA8E00000A8F8,
+        0xA8FB0000A8FC,
+        0xA8FD0000A92E,
+        0xA9300000A954,
+        0xA9800000A9C1,
+        0xA9CF0000A9DA,
+        0xA9E00000A9FF,
+        0xAA000000AA37,
+        0xAA400000AA4E,
+        0xAA500000AA5A,
+        0xAA600000AA77,
+        0xAA7A0000AAC3,
+        0xAADB0000AADE,
+        0xAAE00000AAF0,
+        0xAAF20000AAF7,
+        0xAB010000AB07,
+        0xAB090000AB0F,
+        0xAB110000AB17,
+        0xAB200000AB27,
+        0xAB280000AB2F,
+        0xAB300000AB5B,
+        0xAB600000AB69,
+        0xABC00000ABEB,
+        0xABEC0000ABEE,
+        0xABF00000ABFA,
+        0xAC000000D7A4,
+        0xFA0E0000FA10,
+        0xFA110000FA12,
+        0xFA130000FA15,
+        0xFA1F0000FA20,
+        0xFA210000FA22,
+        0xFA230000FA25,
+        0xFA270000FA2A,
+        0xFB1E0000FB1F,
+        0xFE200000FE30,
+        0xFE730000FE74,
+        0x100000001000C,
+        0x1000D00010027,
+        0x100280001003B,
+        0x1003C0001003E,
+        0x1003F0001004E,
+        0x100500001005E,
+        0x10080000100FB,
+        0x101FD000101FE,
+        0x102800001029D,
+        0x102A0000102D1,
+        0x102E0000102E1,
+        0x1030000010320,
+        0x1032D00010341,
+        0x103420001034A,
+        0x103500001037B,
+        0x103800001039E,
+        0x103A0000103C4,
+        0x103C8000103D0,
+        0x104280001049E,
+        0x104A0000104AA,
+        0x104D8000104FC,
+        0x1050000010528,
+        0x1053000010564,
+        0x10597000105A2,
+        0x105A3000105B2,
+        0x105B3000105BA,
+        0x105BB000105BD,
+        0x105C0000105F4,
+        0x1060000010737,
+        0x1074000010756,
+        0x1076000010768,
+        0x1078000010781,
+        0x1080000010806,
+        0x1080800010809,
+        0x1080A00010836,
+        0x1083700010839,
+        0x1083C0001083D,
+        0x1083F00010856,
+        0x1086000010877,
+        0x108800001089F,
+        0x108E0000108F3,
+        0x108F4000108F6,
+        0x1090000010916,
+        0x109200001093A,
+        0x109400001095A,
+        0x10980000109B8,
+        0x109BE000109C0,
+        0x10A0000010A04,
+        0x10A0500010A07,
+        0x10A0C00010A14,
+        0x10A1500010A18,
+        0x10A1900010A36,
+        0x10A3800010A3B,
+        0x10A3F00010A40,
+        0x10A6000010A7D,
+        0x10A8000010A9D,
+        0x10AC000010AC8,
+        0x10AC900010AE7,
+        0x10B0000010B36,
+        0x10B4000010B56,
+        0x10B6000010B73,
+        0x10B8000010B92,
+        0x10C0000010C49,
+        0x10CC000010CF3,
+        0x10D0000010D28,
+        0x10D3000010D3A,
+        0x10D4000010D50,
+        0x10D6900010D6E,
+        0x10D6F00010D86,
+        0x10E8000010EAA,
+        0x10EAB00010EAD,
+        0x10EB000010EB2,
+        0x10EC200010EC8,
+        0x10EFA00010F1D,
+        0x10F2700010F28,
+        0x10F3000010F51,
+        0x10F7000010F86,
+        0x10FB000010FC5,
+        0x10FE000010FF7,
+        0x1100000011047,
+        0x1106600011076,
+        0x1107F000110BB,
+        0x110C2000110C3,
+        0x110D0000110E9,
+        0x110F0000110FA,
+        0x1110000011135,
+        0x1113600011140,
+        0x1114400011148,
+        0x1115000011174,
+        0x1117600011177,
+        0x11180000111C5,
+        0x111C9000111CD,
+        0x111CE000111DB,
+        0x111DC000111DD,
+        0x1120000011212,
+        0x1121300011238,
+        0x1123E00011242,
+        0x1128000011287,
+        0x1128800011289,
+        0x1128A0001128E,
+        0x1128F0001129E,
+        0x1129F000112A9,
+        0x112B0000112EB,
+        0x112F0000112FA,
+        0x1130000011304,
+        0x113050001130D,
+        0x1130F00011311,
+        0x1131300011329,
+        0x1132A00011331,
+        0x1133200011334,
+        0x113350001133A,
+        0x1133B00011345,
+        0x1134700011349,
+        0x1134B0001134E,
+        0x1135000011351,
+        0x1135700011358,
+        0x1135D00011364,
+        0x113660001136D,
+        0x1137000011375,
+        0x113800001138A,
+        0x1138B0001138C,
+        0x1138E0001138F,
+        0x11390000113B6,
+        0x113B7000113C1,
+        0x113C2000113C3,
+        0x113C5000113C6,
+        0x113C7000113CB,
+        0x113CC000113D4,
+        0x113E1000113E3,
+        0x114000001144B,
+        0x114500001145A,
+        0x1145E00011462,
+        0x11480000114C6,
+        0x114C7000114C8,
+        0x114D0000114DA,
+        0x11580000115B6,
+        0x115B8000115C1,
+        0x115D8000115DE,
+        0x1160000011641,
+        0x1164400011645,
+        0x116500001165A,
+        0x11680000116B9,
+        0x116C0000116CA,
+        0x116D0000116E4,
+        0x117000001171B,
+        0x1171D0001172C,
+        0x117300001173A,
+        0x1174000011747,
+        0x118000001183B,
+        0x118C0000118EA,
+        0x118FF00011907,
+        0x119090001190A,
+        0x1190C00011914,
+        0x1191500011917,
+        0x1191800011936,
+        0x1193700011939,
+        0x1193B00011944,
+        0x119500001195A,
+        0x119A0000119A8,
+        0x119AA000119D8,
+        0x119DA000119E2,
+        0x119E3000119E5,
+        0x11A0000011A3F,
+        0x11A4700011A48,
+        0x11A5000011A9A,
+        0x11A9D00011A9E,
+        0x11AB000011AF9,
+        0x11B6000011B68,
+        0x11BC000011BE1,
+        0x11BF000011BFA,
+        0x11C0000011C09,
+        0x11C0A00011C37,
+        0x11C3800011C41,
+        0x11C5000011C5A,
+        0x11C7200011C90,
+        0x11C9200011CA8,
+        0x11CA900011CB7,
+        0x11D0000011D07,
+        0x11D0800011D0A,
+        0x11D0B00011D37,
+        0x11D3A00011D3B,
+        0x11D3C00011D3E,
+        0x11D3F00011D48,
+        0x11D5000011D5A,
+        0x11D6000011D66,
+        0x11D6700011D69,
+        0x11D6A00011D8F,
+        0x11D9000011D92,
+        0x11D9300011D99,
+        0x11DA000011DAA,
+        0x11DB000011DDC,
+        0x11DE000011DEA,
+        0x11EE000011EF7,
+        0x11F0000011F11,
+        0x11F1200011F3B,
+        0x11F3E00011F43,
+        0x11F5000011F5B,
+        0x11FB000011FB1,
+        0x120000001239A,
+        0x1248000012544,
+        0x12F9000012FF1,
+        0x1300000013430,
+        0x1344000013456,
+        0x13460000143FB,
+        0x1440000014647,
+        0x161000001613A,
+        0x1680000016A39,
+        0x16A4000016A5F,
+        0x16A6000016A6A,
+        0x16A7000016ABF,
+        0x16AC000016ACA,
+        0x16AD000016AEE,
+        0x16AF000016AF5,
+        0x16B0000016B37,
+        0x16B4000016B44,
+        0x16B5000016B5A,
+        0x16B6300016B78,
+        0x16B7D00016B90,
+        0x16D4000016D6D,
+        0x16D7000016D7A,
+        0x16E6000016E80,
+        0x16EBB00016ED4,
+        0x16F0000016F4B,
+        0x16F4F00016F88,
+        0x16F8F00016FA0,
+        0x16FE000016FE2,
+        0x16FE300016FE5,
+        0x16FF000016FF4,
+        0x1700000018CD6,
+        0x18CFF00018D1F,
+        0x18D8000018DF3,
+        0x1AFF00001AFF4,
+        0x1AFF50001AFFC,
+        0x1AFFD0001AFFF,
+        0x1B0000001B123,
+        0x1B1320001B133,
+        0x1B1500001B153,
+        0x1B1550001B156,
+        0x1B1640001B168,
+        0x1B1700001B2FC,
+        0x1BC000001BC6B,
+        0x1BC700001BC7D,
+        0x1BC800001BC89,
+        0x1BC900001BC9A,
+        0x1BC9D0001BC9F,
+        0x1CF000001CF2E,
+        0x1CF300001CF47,
+        0x1DA000001DA37,
+        0x1DA3B0001DA6D,
+        0x1DA750001DA76,
+        0x1DA840001DA85,
+        0x1DA9B0001DAA0,
+        0x1DAA10001DAB0,
+        0x1DF000001DF1F,
+        0x1DF250001DF2B,
+        0x1E0000001E007,
+        0x1E0080001E019,
+        0x1E01B0001E022,
+        0x1E0230001E025,
+        0x1E0260001E02B,
+        0x1E08F0001E090,
+        0x1E1000001E12D,
+        0x1E1300001E13E,
+        0x1E1400001E14A,
+        0x1E14E0001E14F,
+        0x1E2900001E2AF,
+        0x1E2C00001E2FA,
+        0x1E4D00001E4FA,
+        0x1E5D00001E5FB,
+        0x1E6C00001E6DF,
+        0x1E6E00001E6F6,
+        0x1E6FE0001E700,
+        0x1E7E00001E7E7,
+        0x1E7E80001E7EC,
+        0x1E7ED0001E7EF,
+        0x1E7F00001E7FF,
+        0x1E8000001E8C5,
+        0x1E8D00001E8D7,
+        0x1E9220001E94C,
+        0x1E9500001E95A,
+        0x200000002A6E0,
+        0x2A7000002B81E,
+        0x2B8200002CEAE,
+        0x2CEB00002EBE1,
+        0x2EBF00002EE5E,
+        0x300000003134B,
+        0x313500003347A,
+    ),
+    "CONTEXTJ": (0x200C0000200E,),
+    "CONTEXTO": (
+        0xB7000000B8,
+        0x37500000376,
+        0x5F3000005F5,
+        0x6600000066A,
+        0x6F0000006FA,
+        0x30FB000030FC,
+    ),
+}
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/intranges.py b/venv/lib/python3.12/site-packages/pip/_vendor/idna/intranges.py
new file mode 100644
index 0000000..19d7781
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/idna/intranges.py
@@ -0,0 +1,55 @@
+"""
+Given a list of integers, made up of (hopefully) a small number of long runs
+of consecutive integers, compute a representation of the form
+((start1, end1), (start2, end2) ...). Then answer the question "was x present
+in the original list?" in time O(log(# runs)).
+"""
+
+import bisect
+
+
+def intranges_from_list(list_: list[int]) -> tuple[int, ...]:
+    """Represent a list of integers as a sequence of ranges:
+    ((start_0, end_0), (start_1, end_1), ...), such that the original
+    integers are exactly those x such that start_i <= x < end_i for some i.
+
+    Ranges are encoded as single integers (start << 32 | end), not as tuples.
+    """
+
+    sorted_list = sorted(list_)
+    ranges = []
+    last_write = -1
+    for i in range(len(sorted_list)):
+        if i + 1 < len(sorted_list) and sorted_list[i] == sorted_list[i + 1] - 1:
+            continue
+        current_range = sorted_list[last_write + 1 : i + 1]
+        ranges.append(_encode_range(current_range[0], current_range[-1] + 1))
+        last_write = i
+
+    return tuple(ranges)
+
+
+def _encode_range(start: int, end: int) -> int:
+    return (start << 32) | end
+
+
+def _decode_range(r: int) -> tuple[int, int]:
+    return (r >> 32), (r & ((1 << 32) - 1))
+
+
+def intranges_contain(int_: int, ranges: tuple[int, ...]) -> bool:
+    """Determine if `int_` falls into one of the ranges in `ranges`."""
+    tuple_ = _encode_range(int_, 0)
+    pos = bisect.bisect_left(ranges, tuple_)
+    # we could be immediately ahead of a tuple (start, end)
+    # with start < int_ <= end
+    if pos > 0:
+        left, right = _decode_range(ranges[pos - 1])
+        if left <= int_ < right:
+            return True
+    # or we could be immediately behind a tuple (int_, end)
+    if pos < len(ranges):
+        left, _ = _decode_range(ranges[pos])
+        if left == int_:
+            return True
+    return False
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/package_data.py b/venv/lib/python3.12/site-packages/pip/_vendor/idna/package_data.py
new file mode 100644
index 0000000..94e4039
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/idna/package_data.py
@@ -0,0 +1 @@
+__version__ = "3.18"
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/idna/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/idna/uts46data.py b/venv/lib/python3.12/site-packages/pip/_vendor/idna/uts46data.py
new file mode 100644
index 0000000..f2d931f
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/idna/uts46data.py
@@ -0,0 +1,16896 @@
+# This file is automatically generated by tools/idna-data
+
+from array import array
+from typing import Optional
+
+"""IDNA Mapping Table from UTS46."""
+
+
+__version__ = "17.0.0"
+
+uts46_starts: "array[int]" = array(
+    "I",
+    (
+        0x0,
+        0x1,
+        0x2,
+        0x3,
+        0x4,
+        0x5,
+        0x6,
+        0x7,
+        0x8,
+        0x9,
+        0xA,
+        0xB,
+        0xC,
+        0xD,
+        0xE,
+        0xF,
+        0x10,
+        0x11,
+        0x12,
+        0x13,
+        0x14,
+        0x15,
+        0x16,
+        0x17,
+        0x18,
+        0x19,
+        0x1A,
+        0x1B,
+        0x1C,
+        0x1D,
+        0x1E,
+        0x1F,
+        0x20,
+        0x21,
+        0x22,
+        0x23,
+        0x24,
+        0x25,
+        0x26,
+        0x27,
+        0x28,
+        0x29,
+        0x2A,
+        0x2B,
+        0x2C,
+        0x2D,
+        0x2E,
+        0x2F,
+        0x30,
+        0x31,
+        0x32,
+        0x33,
+        0x34,
+        0x35,
+        0x36,
+        0x37,
+        0x38,
+        0x39,
+        0x3A,
+        0x3B,
+        0x3C,
+        0x3D,
+        0x3E,
+        0x3F,
+        0x40,
+        0x41,
+        0x42,
+        0x43,
+        0x44,
+        0x45,
+        0x46,
+        0x47,
+        0x48,
+        0x49,
+        0x4A,
+        0x4B,
+        0x4C,
+        0x4D,
+        0x4E,
+        0x4F,
+        0x50,
+        0x51,
+        0x52,
+        0x53,
+        0x54,
+        0x55,
+        0x56,
+        0x57,
+        0x58,
+        0x59,
+        0x5A,
+        0x5B,
+        0x5C,
+        0x5D,
+        0x5E,
+        0x5F,
+        0x60,
+        0x61,
+        0x62,
+        0x63,
+        0x64,
+        0x65,
+        0x66,
+        0x67,
+        0x68,
+        0x69,
+        0x6A,
+        0x6B,
+        0x6C,
+        0x6D,
+        0x6E,
+        0x6F,
+        0x70,
+        0x71,
+        0x72,
+        0x73,
+        0x74,
+        0x75,
+        0x76,
+        0x77,
+        0x78,
+        0x79,
+        0x7A,
+        0x7B,
+        0x7C,
+        0x7D,
+        0x7E,
+        0x7F,
+        0x80,
+        0x81,
+        0x82,
+        0x83,
+        0x84,
+        0x85,
+        0x86,
+        0x87,
+        0x88,
+        0x89,
+        0x8A,
+        0x8B,
+        0x8C,
+        0x8D,
+        0x8E,
+        0x8F,
+        0x90,
+        0x91,
+        0x92,
+        0x93,
+        0x94,
+        0x95,
+        0x96,
+        0x97,
+        0x98,
+        0x99,
+        0x9A,
+        0x9B,
+        0x9C,
+        0x9D,
+        0x9E,
+        0x9F,
+        0xA0,
+        0xA1,
+        0xA2,
+        0xA3,
+        0xA4,
+        0xA5,
+        0xA6,
+        0xA7,
+        0xA8,
+        0xA9,
+        0xAA,
+        0xAB,
+        0xAC,
+        0xAD,
+        0xAE,
+        0xAF,
+        0xB0,
+        0xB1,
+        0xB2,
+        0xB3,
+        0xB4,
+        0xB5,
+        0xB6,
+        0xB7,
+        0xB8,
+        0xB9,
+        0xBA,
+        0xBB,
+        0xBC,
+        0xBD,
+        0xBE,
+        0xBF,
+        0xC0,
+        0xC1,
+        0xC2,
+        0xC3,
+        0xC4,
+        0xC5,
+        0xC6,
+        0xC7,
+        0xC8,
+        0xC9,
+        0xCA,
+        0xCB,
+        0xCC,
+        0xCD,
+        0xCE,
+        0xCF,
+        0xD0,
+        0xD1,
+        0xD2,
+        0xD3,
+        0xD4,
+        0xD5,
+        0xD6,
+        0xD7,
+        0xD8,
+        0xD9,
+        0xDA,
+        0xDB,
+        0xDC,
+        0xDD,
+        0xDE,
+        0xDF,
+        0xE0,
+        0xE1,
+        0xE2,
+        0xE3,
+        0xE4,
+        0xE5,
+        0xE6,
+        0xE7,
+        0xE8,
+        0xE9,
+        0xEA,
+        0xEB,
+        0xEC,
+        0xED,
+        0xEE,
+        0xEF,
+        0xF0,
+        0xF1,
+        0xF2,
+        0xF3,
+        0xF4,
+        0xF5,
+        0xF6,
+        0xF7,
+        0xF8,
+        0xF9,
+        0xFA,
+        0xFB,
+        0xFC,
+        0xFD,
+        0xFE,
+        0xFF,
+        0x100,
+        0x101,
+        0x102,
+        0x103,
+        0x104,
+        0x105,
+        0x106,
+        0x107,
+        0x108,
+        0x109,
+        0x10A,
+        0x10B,
+        0x10C,
+        0x10D,
+        0x10E,
+        0x10F,
+        0x110,
+        0x111,
+        0x112,
+        0x113,
+        0x114,
+        0x115,
+        0x116,
+        0x117,
+        0x118,
+        0x119,
+        0x11A,
+        0x11B,
+        0x11C,
+        0x11D,
+        0x11E,
+        0x11F,
+        0x120,
+        0x121,
+        0x122,
+        0x123,
+        0x124,
+        0x125,
+        0x126,
+        0x127,
+        0x128,
+        0x129,
+        0x12A,
+        0x12B,
+        0x12C,
+        0x12D,
+        0x12E,
+        0x12F,
+        0x130,
+        0x131,
+        0x132,
+        0x134,
+        0x135,
+        0x136,
+        0x137,
+        0x139,
+        0x13A,
+        0x13B,
+        0x13C,
+        0x13D,
+        0x13E,
+        0x13F,
+        0x141,
+        0x142,
+        0x143,
+        0x144,
+        0x145,
+        0x146,
+        0x147,
+        0x148,
+        0x149,
+        0x14A,
+        0x14B,
+        0x14C,
+        0x14D,
+        0x14E,
+        0x14F,
+        0x150,
+        0x151,
+        0x152,
+        0x153,
+        0x154,
+        0x155,
+        0x156,
+        0x157,
+        0x158,
+        0x159,
+        0x15A,
+        0x15B,
+        0x15C,
+        0x15D,
+        0x15E,
+        0x15F,
+        0x160,
+        0x161,
+        0x162,
+        0x163,
+        0x164,
+        0x165,
+        0x166,
+        0x167,
+        0x168,
+        0x169,
+        0x16A,
+        0x16B,
+        0x16C,
+        0x16D,
+        0x16E,
+        0x16F,
+        0x170,
+        0x171,
+        0x172,
+        0x173,
+        0x174,
+        0x175,
+        0x176,
+        0x177,
+        0x178,
+        0x179,
+        0x17A,
+        0x17B,
+        0x17C,
+        0x17D,
+        0x17E,
+        0x17F,
+        0x180,
+        0x181,
+        0x182,
+        0x183,
+        0x184,
+        0x185,
+        0x186,
+        0x187,
+        0x188,
+        0x189,
+        0x18A,
+        0x18B,
+        0x18C,
+        0x18E,
+        0x18F,
+        0x190,
+        0x191,
+        0x192,
+        0x193,
+        0x194,
+        0x195,
+        0x196,
+        0x197,
+        0x198,
+        0x199,
+        0x19C,
+        0x19D,
+        0x19E,
+        0x19F,
+        0x1A0,
+        0x1A1,
+        0x1A2,
+        0x1A3,
+        0x1A4,
+        0x1A5,
+        0x1A6,
+        0x1A7,
+        0x1A8,
+        0x1A9,
+        0x1AA,
+        0x1AC,
+        0x1AD,
+        0x1AE,
+        0x1AF,
+        0x1B0,
+        0x1B1,
+        0x1B2,
+        0x1B3,
+        0x1B4,
+        0x1B5,
+        0x1B6,
+        0x1B7,
+        0x1B8,
+        0x1B9,
+        0x1BC,
+        0x1BD,
+        0x1C4,
+        0x1C7,
+        0x1CA,
+        0x1CD,
+        0x1CE,
+        0x1CF,
+        0x1D0,
+        0x1D1,
+        0x1D2,
+        0x1D3,
+        0x1D4,
+        0x1D5,
+        0x1D6,
+        0x1D7,
+        0x1D8,
+        0x1D9,
+        0x1DA,
+        0x1DB,
+        0x1DC,
+        0x1DE,
+        0x1DF,
+        0x1E0,
+        0x1E1,
+        0x1E2,
+        0x1E3,
+        0x1E4,
+        0x1E5,
+        0x1E6,
+        0x1E7,
+        0x1E8,
+        0x1E9,
+        0x1EA,
+        0x1EB,
+        0x1EC,
+        0x1ED,
+        0x1EE,
+        0x1EF,
+        0x1F1,
+        0x1F4,
+        0x1F5,
+        0x1F6,
+        0x1F7,
+        0x1F8,
+        0x1F9,
+        0x1FA,
+        0x1FB,
+        0x1FC,
+        0x1FD,
+        0x1FE,
+        0x1FF,
+        0x200,
+        0x201,
+        0x202,
+        0x203,
+        0x204,
+        0x205,
+        0x206,
+        0x207,
+        0x208,
+        0x209,
+        0x20A,
+        0x20B,
+        0x20C,
+        0x20D,
+        0x20E,
+        0x20F,
+        0x210,
+        0x211,
+        0x212,
+        0x213,
+        0x214,
+        0x215,
+        0x216,
+        0x217,
+        0x218,
+        0x219,
+        0x21A,
+        0x21B,
+        0x21C,
+        0x21D,
+        0x21E,
+        0x21F,
+        0x220,
+        0x221,
+        0x222,
+        0x223,
+        0x224,
+        0x225,
+        0x226,
+        0x227,
+        0x228,
+        0x229,
+        0x22A,
+        0x22B,
+        0x22C,
+        0x22D,
+        0x22E,
+        0x22F,
+        0x230,
+        0x231,
+        0x232,
+        0x233,
+        0x23A,
+        0x23B,
+        0x23C,
+        0x23D,
+        0x23E,
+        0x23F,
+        0x241,
+        0x242,
+        0x243,
+        0x244,
+        0x245,
+        0x246,
+        0x247,
+        0x248,
+        0x249,
+        0x24A,
+        0x24B,
+        0x24C,
+        0x24D,
+        0x24E,
+        0x24F,
+        0x2B0,
+        0x2B1,
+        0x2B2,
+        0x2B3,
+        0x2B4,
+        0x2B5,
+        0x2B6,
+        0x2B7,
+        0x2B8,
+        0x2B9,
+        0x2D8,
+        0x2D9,
+        0x2DA,
+        0x2DB,
+        0x2DC,
+        0x2DD,
+        0x2DE,
+        0x2E0,
+        0x2E1,
+        0x2E2,
+        0x2E3,
+        0x2E4,
+        0x2E5,
+        0x340,
+        0x341,
+        0x342,
+        0x343,
+        0x344,
+        0x345,
+        0x346,
+        0x34F,
+        0x350,
+        0x370,
+        0x371,
+        0x372,
+        0x373,
+        0x374,
+        0x375,
+        0x376,
+        0x377,
+        0x378,
+        0x37A,
+        0x37B,
+        0x37E,
+        0x37F,
+        0x380,
+        0x384,
+        0x385,
+        0x386,
+        0x387,
+        0x388,
+        0x389,
+        0x38A,
+        0x38B,
+        0x38C,
+        0x38D,
+        0x38E,
+        0x38F,
+        0x390,
+        0x391,
+        0x392,
+        0x393,
+        0x394,
+        0x395,
+        0x396,
+        0x397,
+        0x398,
+        0x399,
+        0x39A,
+        0x39B,
+        0x39C,
+        0x39D,
+        0x39E,
+        0x39F,
+        0x3A0,
+        0x3A1,
+        0x3A2,
+        0x3A3,
+        0x3A4,
+        0x3A5,
+        0x3A6,
+        0x3A7,
+        0x3A8,
+        0x3A9,
+        0x3AA,
+        0x3AB,
+        0x3AC,
+        0x3C2,
+        0x3C3,
+        0x3CF,
+        0x3D0,
+        0x3D1,
+        0x3D2,
+        0x3D3,
+        0x3D4,
+        0x3D5,
+        0x3D6,
+        0x3D7,
+        0x3D8,
+        0x3D9,
+        0x3DA,
+        0x3DB,
+        0x3DC,
+        0x3DD,
+        0x3DE,
+        0x3DF,
+        0x3E0,
+        0x3E1,
+        0x3E2,
+        0x3E3,
+        0x3E4,
+        0x3E5,
+        0x3E6,
+        0x3E7,
+        0x3E8,
+        0x3E9,
+        0x3EA,
+        0x3EB,
+        0x3EC,
+        0x3ED,
+        0x3EE,
+        0x3EF,
+        0x3F0,
+        0x3F1,
+        0x3F2,
+        0x3F3,
+        0x3F4,
+        0x3F5,
+        0x3F6,
+        0x3F7,
+        0x3F8,
+        0x3F9,
+        0x3FA,
+        0x3FB,
+        0x3FD,
+        0x3FE,
+        0x3FF,
+        0x400,
+        0x401,
+        0x402,
+        0x403,
+        0x404,
+        0x405,
+        0x406,
+        0x407,
+        0x408,
+        0x409,
+        0x40A,
+        0x40B,
+        0x40C,
+        0x40D,
+        0x40E,
+        0x40F,
+        0x410,
+        0x411,
+        0x412,
+        0x413,
+        0x414,
+        0x415,
+        0x416,
+        0x417,
+        0x418,
+        0x419,
+        0x41A,
+        0x41B,
+        0x41C,
+        0x41D,
+        0x41E,
+        0x41F,
+        0x420,
+        0x421,
+        0x422,
+        0x423,
+        0x424,
+        0x425,
+        0x426,
+        0x427,
+        0x428,
+        0x429,
+        0x42A,
+        0x42B,
+        0x42C,
+        0x42D,
+        0x42E,
+        0x42F,
+        0x430,
+        0x460,
+        0x461,
+        0x462,
+        0x463,
+        0x464,
+        0x465,
+        0x466,
+        0x467,
+        0x468,
+        0x469,
+        0x46A,
+        0x46B,
+        0x46C,
+        0x46D,
+        0x46E,
+        0x46F,
+        0x470,
+        0x471,
+        0x472,
+        0x473,
+        0x474,
+        0x475,
+        0x476,
+        0x477,
+        0x478,
+        0x479,
+        0x47A,
+        0x47B,
+        0x47C,
+        0x47D,
+        0x47E,
+        0x47F,
+        0x480,
+        0x481,
+        0x48A,
+        0x48B,
+        0x48C,
+        0x48D,
+        0x48E,
+        0x48F,
+        0x490,
+        0x491,
+        0x492,
+        0x493,
+        0x494,
+        0x495,
+        0x496,
+        0x497,
+        0x498,
+        0x499,
+        0x49A,
+        0x49B,
+        0x49C,
+        0x49D,
+        0x49E,
+        0x49F,
+        0x4A0,
+        0x4A1,
+        0x4A2,
+        0x4A3,
+        0x4A4,
+        0x4A5,
+        0x4A6,
+        0x4A7,
+        0x4A8,
+        0x4A9,
+        0x4AA,
+        0x4AB,
+        0x4AC,
+        0x4AD,
+        0x4AE,
+        0x4AF,
+        0x4B0,
+        0x4B1,
+        0x4B2,
+        0x4B3,
+        0x4B4,
+        0x4B5,
+        0x4B6,
+        0x4B7,
+        0x4B8,
+        0x4B9,
+        0x4BA,
+        0x4BB,
+        0x4BC,
+        0x4BD,
+        0x4BE,
+        0x4BF,
+        0x4C0,
+        0x4C1,
+        0x4C2,
+        0x4C3,
+        0x4C4,
+        0x4C5,
+        0x4C6,
+        0x4C7,
+        0x4C8,
+        0x4C9,
+        0x4CA,
+        0x4CB,
+        0x4CC,
+        0x4CD,
+        0x4CE,
+        0x4D0,
+        0x4D1,
+        0x4D2,
+        0x4D3,
+        0x4D4,
+        0x4D5,
+        0x4D6,
+        0x4D7,
+        0x4D8,
+        0x4D9,
+        0x4DA,
+        0x4DB,
+        0x4DC,
+        0x4DD,
+        0x4DE,
+        0x4DF,
+        0x4E0,
+        0x4E1,
+        0x4E2,
+        0x4E3,
+        0x4E4,
+        0x4E5,
+        0x4E6,
+        0x4E7,
+        0x4E8,
+        0x4E9,
+        0x4EA,
+        0x4EB,
+        0x4EC,
+        0x4ED,
+        0x4EE,
+        0x4EF,
+        0x4F0,
+        0x4F1,
+        0x4F2,
+        0x4F3,
+        0x4F4,
+        0x4F5,
+        0x4F6,
+        0x4F7,
+        0x4F8,
+        0x4F9,
+        0x4FA,
+        0x4FB,
+        0x4FC,
+        0x4FD,
+        0x4FE,
+        0x4FF,
+        0x500,
+        0x501,
+        0x502,
+        0x503,
+        0x504,
+        0x505,
+        0x506,
+        0x507,
+        0x508,
+        0x509,
+        0x50A,
+        0x50B,
+        0x50C,
+        0x50D,
+        0x50E,
+        0x50F,
+        0x510,
+        0x511,
+        0x512,
+        0x513,
+        0x514,
+        0x515,
+        0x516,
+        0x517,
+        0x518,
+        0x519,
+        0x51A,
+        0x51B,
+        0x51C,
+        0x51D,
+        0x51E,
+        0x51F,
+        0x520,
+        0x521,
+        0x522,
+        0x523,
+        0x524,
+        0x525,
+        0x526,
+        0x527,
+        0x528,
+        0x529,
+        0x52A,
+        0x52B,
+        0x52C,
+        0x52D,
+        0x52E,
+        0x52F,
+        0x530,
+        0x531,
+        0x532,
+        0x533,
+        0x534,
+        0x535,
+        0x536,
+        0x537,
+        0x538,
+        0x539,
+        0x53A,
+        0x53B,
+        0x53C,
+        0x53D,
+        0x53E,
+        0x53F,
+        0x540,
+        0x541,
+        0x542,
+        0x543,
+        0x544,
+        0x545,
+        0x546,
+        0x547,
+        0x548,
+        0x549,
+        0x54A,
+        0x54B,
+        0x54C,
+        0x54D,
+        0x54E,
+        0x54F,
+        0x550,
+        0x551,
+        0x552,
+        0x553,
+        0x554,
+        0x555,
+        0x556,
+        0x557,
+        0x559,
+        0x587,
+        0x588,
+        0x58B,
+        0x58D,
+        0x590,
+        0x591,
+        0x5C8,
+        0x5D0,
+        0x5EB,
+        0x5EF,
+        0x5F5,
+        0x606,
+        0x61C,
+        0x61D,
+        0x675,
+        0x676,
+        0x677,
+        0x678,
+        0x679,
+        0x6DD,
+        0x6DE,
+        0x70E,
+        0x710,
+        0x74B,
+        0x74D,
+        0x7B2,
+        0x7C0,
+        0x7FB,
+        0x7FD,
+        0x82E,
+        0x830,
+        0x83F,
+        0x840,
+        0x85C,
+        0x85E,
+        0x85F,
+        0x860,
+        0x86B,
+        0x870,
+        0x890,
+        0x897,
+        0x8E2,
+        0x8E3,
+        0x958,
+        0x959,
+        0x95A,
+        0x95B,
+        0x95C,
+        0x95D,
+        0x95E,
+        0x95F,
+        0x960,
+        0x984,
+        0x985,
+        0x98D,
+        0x98F,
+        0x991,
+        0x993,
+        0x9A9,
+        0x9AA,
+        0x9B1,
+        0x9B2,
+        0x9B3,
+        0x9B6,
+        0x9BA,
+        0x9BC,
+        0x9C5,
+        0x9C7,
+        0x9C9,
+        0x9CB,
+        0x9CF,
+        0x9D7,
+        0x9D8,
+        0x9DC,
+        0x9DD,
+        0x9DE,
+        0x9DF,
+        0x9E0,
+        0x9E4,
+        0x9E6,
+        0x9FF,
+        0xA01,
+        0xA04,
+        0xA05,
+        0xA0B,
+        0xA0F,
+        0xA11,
+        0xA13,
+        0xA29,
+        0xA2A,
+        0xA31,
+        0xA32,
+        0xA33,
+        0xA34,
+        0xA35,
+        0xA36,
+        0xA37,
+        0xA38,
+        0xA3A,
+        0xA3C,
+        0xA3D,
+        0xA3E,
+        0xA43,
+        0xA47,
+        0xA49,
+        0xA4B,
+        0xA4E,
+        0xA51,
+        0xA52,
+        0xA59,
+        0xA5A,
+        0xA5B,
+        0xA5C,
+        0xA5D,
+        0xA5E,
+        0xA5F,
+        0xA66,
+        0xA77,
+        0xA81,
+        0xA84,
+        0xA85,
+        0xA8E,
+        0xA8F,
+        0xA92,
+        0xA93,
+        0xAA9,
+        0xAAA,
+        0xAB1,
+        0xAB2,
+        0xAB4,
+        0xAB5,
+        0xABA,
+        0xABC,
+        0xAC6,
+        0xAC7,
+        0xACA,
+        0xACB,
+        0xACE,
+        0xAD0,
+        0xAD1,
+        0xAE0,
+        0xAE4,
+        0xAE6,
+        0xAF2,
+        0xAF9,
+        0xB00,
+        0xB01,
+        0xB04,
+        0xB05,
+        0xB0D,
+        0xB0F,
+        0xB11,
+        0xB13,
+        0xB29,
+        0xB2A,
+        0xB31,
+        0xB32,
+        0xB34,
+        0xB35,
+        0xB3A,
+        0xB3C,
+        0xB45,
+        0xB47,
+        0xB49,
+        0xB4B,
+        0xB4E,
+        0xB55,
+        0xB58,
+        0xB5C,
+        0xB5D,
+        0xB5E,
+        0xB5F,
+        0xB64,
+        0xB66,
+        0xB78,
+        0xB82,
+        0xB84,
+        0xB85,
+        0xB8B,
+        0xB8E,
+        0xB91,
+        0xB92,
+        0xB96,
+        0xB99,
+        0xB9B,
+        0xB9C,
+        0xB9D,
+        0xB9E,
+        0xBA0,
+        0xBA3,
+        0xBA5,
+        0xBA8,
+        0xBAB,
+        0xBAE,
+        0xBBA,
+        0xBBE,
+        0xBC3,
+        0xBC6,
+        0xBC9,
+        0xBCA,
+        0xBCE,
+        0xBD0,
+        0xBD1,
+        0xBD7,
+        0xBD8,
+        0xBE6,
+        0xBFB,
+        0xC00,
+        0xC0D,
+        0xC0E,
+        0xC11,
+        0xC12,
+        0xC29,
+        0xC2A,
+        0xC3A,
+        0xC3C,
+        0xC45,
+        0xC46,
+        0xC49,
+        0xC4A,
+        0xC4E,
+        0xC55,
+        0xC57,
+        0xC58,
+        0xC5B,
+        0xC5C,
+        0xC5E,
+        0xC60,
+        0xC64,
+        0xC66,
+        0xC70,
+        0xC77,
+        0xC8D,
+        0xC8E,
+        0xC91,
+        0xC92,
+        0xCA9,
+        0xCAA,
+        0xCB4,
+        0xCB5,
+        0xCBA,
+        0xCBC,
+        0xCC5,
+        0xCC6,
+        0xCC9,
+        0xCCA,
+        0xCCE,
+        0xCD5,
+        0xCD7,
+        0xCDC,
+        0xCDF,
+        0xCE0,
+        0xCE4,
+        0xCE6,
+        0xCF0,
+        0xCF1,
+        0xCF4,
+        0xD00,
+        0xD0D,
+        0xD0E,
+        0xD11,
+        0xD12,
+        0xD45,
+        0xD46,
+        0xD49,
+        0xD4A,
+        0xD50,
+        0xD54,
+        0xD64,
+        0xD66,
+        0xD80,
+        0xD81,
+        0xD84,
+        0xD85,
+        0xD97,
+        0xD9A,
+        0xDB2,
+        0xDB3,
+        0xDBC,
+        0xDBD,
+        0xDBE,
+        0xDC0,
+        0xDC7,
+        0xDCA,
+        0xDCB,
+        0xDCF,
+        0xDD5,
+        0xDD6,
+        0xDD7,
+        0xDD8,
+        0xDE0,
+        0xDE6,
+        0xDF0,
+        0xDF2,
+        0xDF5,
+        0xE01,
+        0xE33,
+        0xE34,
+        0xE3B,
+        0xE3F,
+        0xE5C,
+        0xE81,
+        0xE83,
+        0xE84,
+        0xE85,
+        0xE86,
+        0xE8B,
+        0xE8C,
+        0xEA4,
+        0xEA5,
+        0xEA6,
+        0xEA7,
+        0xEB3,
+        0xEB4,
+        0xEBE,
+        0xEC0,
+        0xEC5,
+        0xEC6,
+        0xEC7,
+        0xEC8,
+        0xECF,
+        0xED0,
+        0xEDA,
+        0xEDC,
+        0xEDD,
+        0xEDE,
+        0xEE0,
+        0xF00,
+        0xF0C,
+        0xF0D,
+        0xF43,
+        0xF44,
+        0xF48,
+        0xF49,
+        0xF4D,
+        0xF4E,
+        0xF52,
+        0xF53,
+        0xF57,
+        0xF58,
+        0xF5C,
+        0xF5D,
+        0xF69,
+        0xF6A,
+        0xF6D,
+        0xF71,
+        0xF73,
+        0xF74,
+        0xF75,
+        0xF76,
+        0xF77,
+        0xF78,
+        0xF79,
+        0xF7A,
+        0xF81,
+        0xF82,
+        0xF93,
+        0xF94,
+        0xF98,
+        0xF99,
+        0xF9D,
+        0xF9E,
+        0xFA2,
+        0xFA3,
+        0xFA7,
+        0xFA8,
+        0xFAC,
+        0xFAD,
+        0xFB9,
+        0xFBA,
+        0xFBD,
+        0xFBE,
+        0xFCD,
+        0xFCE,
+        0xFDB,
+        0x1000,
+        0x10A0,
+        0x10A1,
+        0x10A2,
+        0x10A3,
+        0x10A4,
+        0x10A5,
+        0x10A6,
+        0x10A7,
+        0x10A8,
+        0x10A9,
+        0x10AA,
+        0x10AB,
+        0x10AC,
+        0x10AD,
+        0x10AE,
+        0x10AF,
+        0x10B0,
+        0x10B1,
+        0x10B2,
+        0x10B3,
+        0x10B4,
+        0x10B5,
+        0x10B6,
+        0x10B7,
+        0x10B8,
+        0x10B9,
+        0x10BA,
+        0x10BB,
+        0x10BC,
+        0x10BD,
+        0x10BE,
+        0x10BF,
+        0x10C0,
+        0x10C1,
+        0x10C2,
+        0x10C3,
+        0x10C4,
+        0x10C5,
+        0x10C6,
+        0x10C7,
+        0x10C8,
+        0x10CD,
+        0x10CE,
+        0x10D0,
+        0x10FC,
+        0x10FD,
+        0x115F,
+        0x1161,
+        0x1249,
+        0x124A,
+        0x124E,
+        0x1250,
+        0x1257,
+        0x1258,
+        0x1259,
+        0x125A,
+        0x125E,
+        0x1260,
+        0x1289,
+        0x128A,
+        0x128E,
+        0x1290,
+        0x12B1,
+        0x12B2,
+        0x12B6,
+        0x12B8,
+        0x12BF,
+        0x12C0,
+        0x12C1,
+        0x12C2,
+        0x12C6,
+        0x12C8,
+        0x12D7,
+        0x12D8,
+        0x1311,
+        0x1312,
+        0x1316,
+        0x1318,
+        0x135B,
+        0x135D,
+        0x137D,
+        0x1380,
+        0x139A,
+        0x13A0,
+        0x13F6,
+        0x13F8,
+        0x13F9,
+        0x13FA,
+        0x13FB,
+        0x13FC,
+        0x13FD,
+        0x13FE,
+        0x1400,
+        0x1680,
+        0x1681,
+        0x169D,
+        0x16A0,
+        0x16F9,
+        0x1700,
+        0x1716,
+        0x171F,
+        0x1737,
+        0x1740,
+        0x1754,
+        0x1760,
+        0x176D,
+        0x176E,
+        0x1771,
+        0x1772,
+        0x1774,
+        0x1780,
+        0x17B4,
+        0x17B6,
+        0x17DE,
+        0x17E0,
+        0x17EA,
+        0x17F0,
+        0x17FA,
+        0x1800,
+        0x180B,
+        0x1810,
+        0x181A,
+        0x1820,
+        0x1879,
+        0x1880,
+        0x18AB,
+        0x18B0,
+        0x18F6,
+        0x1900,
+        0x191F,
+        0x1920,
+        0x192C,
+        0x1930,
+        0x193C,
+        0x1940,
+        0x1941,
+        0x1944,
+        0x196E,
+        0x1970,
+        0x1975,
+        0x1980,
+        0x19AC,
+        0x19B0,
+        0x19CA,
+        0x19D0,
+        0x19DB,
+        0x19DE,
+        0x1A1C,
+        0x1A1E,
+        0x1A5F,
+        0x1A60,
+        0x1A7D,
+        0x1A7F,
+        0x1A8A,
+        0x1A90,
+        0x1A9A,
+        0x1AA0,
+        0x1AAE,
+        0x1AB0,
+        0x1ADE,
+        0x1AE0,
+        0x1AEC,
+        0x1B00,
+        0x1B4D,
+        0x1B4E,
+        0x1BF4,
+        0x1BFC,
+        0x1C38,
+        0x1C3B,
+        0x1C4A,
+        0x1C4D,
+        0x1C80,
+        0x1C81,
+        0x1C82,
+        0x1C83,
+        0x1C84,
+        0x1C86,
+        0x1C87,
+        0x1C88,
+        0x1C89,
+        0x1C8A,
+        0x1C8B,
+        0x1C90,
+        0x1C91,
+        0x1C92,
+        0x1C93,
+        0x1C94,
+        0x1C95,
+        0x1C96,
+        0x1C97,
+        0x1C98,
+        0x1C99,
+        0x1C9A,
+        0x1C9B,
+        0x1C9C,
+        0x1C9D,
+        0x1C9E,
+        0x1C9F,
+        0x1CA0,
+        0x1CA1,
+        0x1CA2,
+        0x1CA3,
+        0x1CA4,
+        0x1CA5,
+        0x1CA6,
+        0x1CA7,
+        0x1CA8,
+        0x1CA9,
+        0x1CAA,
+        0x1CAB,
+        0x1CAC,
+        0x1CAD,
+        0x1CAE,
+        0x1CAF,
+        0x1CB0,
+        0x1CB1,
+        0x1CB2,
+        0x1CB3,
+        0x1CB4,
+        0x1CB5,
+        0x1CB6,
+        0x1CB7,
+        0x1CB8,
+        0x1CB9,
+        0x1CBA,
+        0x1CBB,
+        0x1CBD,
+        0x1CBE,
+        0x1CBF,
+        0x1CC0,
+        0x1CC8,
+        0x1CD0,
+        0x1CFB,
+        0x1D00,
+        0x1D2C,
+        0x1D2D,
+        0x1D2E,
+        0x1D2F,
+        0x1D30,
+        0x1D31,
+        0x1D32,
+        0x1D33,
+        0x1D34,
+        0x1D35,
+        0x1D36,
+        0x1D37,
+        0x1D38,
+        0x1D39,
+        0x1D3A,
+        0x1D3B,
+        0x1D3C,
+        0x1D3D,
+        0x1D3E,
+        0x1D3F,
+        0x1D40,
+        0x1D41,
+        0x1D42,
+        0x1D43,
+        0x1D44,
+        0x1D45,
+        0x1D46,
+        0x1D47,
+        0x1D48,
+        0x1D49,
+        0x1D4A,
+        0x1D4B,
+        0x1D4C,
+        0x1D4D,
+        0x1D4E,
+        0x1D4F,
+        0x1D50,
+        0x1D51,
+        0x1D52,
+        0x1D53,
+        0x1D54,
+        0x1D55,
+        0x1D56,
+        0x1D57,
+        0x1D58,
+        0x1D59,
+        0x1D5A,
+        0x1D5B,
+        0x1D5C,
+        0x1D5D,
+        0x1D5E,
+        0x1D5F,
+        0x1D60,
+        0x1D61,
+        0x1D62,
+        0x1D63,
+        0x1D64,
+        0x1D65,
+        0x1D66,
+        0x1D67,
+        0x1D68,
+        0x1D69,
+        0x1D6A,
+        0x1D6B,
+        0x1D78,
+        0x1D79,
+        0x1D9B,
+        0x1D9C,
+        0x1D9D,
+        0x1D9E,
+        0x1D9F,
+        0x1DA0,
+        0x1DA1,
+        0x1DA2,
+        0x1DA3,
+        0x1DA4,
+        0x1DA5,
+        0x1DA6,
+        0x1DA7,
+        0x1DA8,
+        0x1DA9,
+        0x1DAA,
+        0x1DAB,
+        0x1DAC,
+        0x1DAD,
+        0x1DAE,
+        0x1DAF,
+        0x1DB0,
+        0x1DB1,
+        0x1DB2,
+        0x1DB3,
+        0x1DB4,
+        0x1DB5,
+        0x1DB6,
+        0x1DB7,
+        0x1DB8,
+        0x1DB9,
+        0x1DBA,
+        0x1DBB,
+        0x1DBC,
+        0x1DBD,
+        0x1DBE,
+        0x1DBF,
+        0x1DC0,
+        0x1E00,
+        0x1E01,
+        0x1E02,
+        0x1E03,
+        0x1E04,
+        0x1E05,
+        0x1E06,
+        0x1E07,
+        0x1E08,
+        0x1E09,
+        0x1E0A,
+        0x1E0B,
+        0x1E0C,
+        0x1E0D,
+        0x1E0E,
+        0x1E0F,
+        0x1E10,
+        0x1E11,
+        0x1E12,
+        0x1E13,
+        0x1E14,
+        0x1E15,
+        0x1E16,
+        0x1E17,
+        0x1E18,
+        0x1E19,
+        0x1E1A,
+        0x1E1B,
+        0x1E1C,
+        0x1E1D,
+        0x1E1E,
+        0x1E1F,
+        0x1E20,
+        0x1E21,
+        0x1E22,
+        0x1E23,
+        0x1E24,
+        0x1E25,
+        0x1E26,
+        0x1E27,
+        0x1E28,
+        0x1E29,
+        0x1E2A,
+        0x1E2B,
+        0x1E2C,
+        0x1E2D,
+        0x1E2E,
+        0x1E2F,
+        0x1E30,
+        0x1E31,
+        0x1E32,
+        0x1E33,
+        0x1E34,
+        0x1E35,
+        0x1E36,
+        0x1E37,
+        0x1E38,
+        0x1E39,
+        0x1E3A,
+        0x1E3B,
+        0x1E3C,
+        0x1E3D,
+        0x1E3E,
+        0x1E3F,
+        0x1E40,
+        0x1E41,
+        0x1E42,
+        0x1E43,
+        0x1E44,
+        0x1E45,
+        0x1E46,
+        0x1E47,
+        0x1E48,
+        0x1E49,
+        0x1E4A,
+        0x1E4B,
+        0x1E4C,
+        0x1E4D,
+        0x1E4E,
+        0x1E4F,
+        0x1E50,
+        0x1E51,
+        0x1E52,
+        0x1E53,
+        0x1E54,
+        0x1E55,
+        0x1E56,
+        0x1E57,
+        0x1E58,
+        0x1E59,
+        0x1E5A,
+        0x1E5B,
+        0x1E5C,
+        0x1E5D,
+        0x1E5E,
+        0x1E5F,
+        0x1E60,
+        0x1E61,
+        0x1E62,
+        0x1E63,
+        0x1E64,
+        0x1E65,
+        0x1E66,
+        0x1E67,
+        0x1E68,
+        0x1E69,
+        0x1E6A,
+        0x1E6B,
+        0x1E6C,
+        0x1E6D,
+        0x1E6E,
+        0x1E6F,
+        0x1E70,
+        0x1E71,
+        0x1E72,
+        0x1E73,
+        0x1E74,
+        0x1E75,
+        0x1E76,
+        0x1E77,
+        0x1E78,
+        0x1E79,
+        0x1E7A,
+        0x1E7B,
+        0x1E7C,
+        0x1E7D,
+        0x1E7E,
+        0x1E7F,
+        0x1E80,
+        0x1E81,
+        0x1E82,
+        0x1E83,
+        0x1E84,
+        0x1E85,
+        0x1E86,
+        0x1E87,
+        0x1E88,
+        0x1E89,
+        0x1E8A,
+        0x1E8B,
+        0x1E8C,
+        0x1E8D,
+        0x1E8E,
+        0x1E8F,
+        0x1E90,
+        0x1E91,
+        0x1E92,
+        0x1E93,
+        0x1E94,
+        0x1E95,
+        0x1E9A,
+        0x1E9B,
+        0x1E9C,
+        0x1E9E,
+        0x1E9F,
+        0x1EA0,
+        0x1EA1,
+        0x1EA2,
+        0x1EA3,
+        0x1EA4,
+        0x1EA5,
+        0x1EA6,
+        0x1EA7,
+        0x1EA8,
+        0x1EA9,
+        0x1EAA,
+        0x1EAB,
+        0x1EAC,
+        0x1EAD,
+        0x1EAE,
+        0x1EAF,
+        0x1EB0,
+        0x1EB1,
+        0x1EB2,
+        0x1EB3,
+        0x1EB4,
+        0x1EB5,
+        0x1EB6,
+        0x1EB7,
+        0x1EB8,
+        0x1EB9,
+        0x1EBA,
+        0x1EBB,
+        0x1EBC,
+        0x1EBD,
+        0x1EBE,
+        0x1EBF,
+        0x1EC0,
+        0x1EC1,
+        0x1EC2,
+        0x1EC3,
+        0x1EC4,
+        0x1EC5,
+        0x1EC6,
+        0x1EC7,
+        0x1EC8,
+        0x1EC9,
+        0x1ECA,
+        0x1ECB,
+        0x1ECC,
+        0x1ECD,
+        0x1ECE,
+        0x1ECF,
+        0x1ED0,
+        0x1ED1,
+        0x1ED2,
+        0x1ED3,
+        0x1ED4,
+        0x1ED5,
+        0x1ED6,
+        0x1ED7,
+        0x1ED8,
+        0x1ED9,
+        0x1EDA,
+        0x1EDB,
+        0x1EDC,
+        0x1EDD,
+        0x1EDE,
+        0x1EDF,
+        0x1EE0,
+        0x1EE1,
+        0x1EE2,
+        0x1EE3,
+        0x1EE4,
+        0x1EE5,
+        0x1EE6,
+        0x1EE7,
+        0x1EE8,
+        0x1EE9,
+        0x1EEA,
+        0x1EEB,
+        0x1EEC,
+        0x1EED,
+        0x1EEE,
+        0x1EEF,
+        0x1EF0,
+        0x1EF1,
+        0x1EF2,
+        0x1EF3,
+        0x1EF4,
+        0x1EF5,
+        0x1EF6,
+        0x1EF7,
+        0x1EF8,
+        0x1EF9,
+        0x1EFA,
+        0x1EFB,
+        0x1EFC,
+        0x1EFD,
+        0x1EFE,
+        0x1EFF,
+        0x1F08,
+        0x1F09,
+        0x1F0A,
+        0x1F0B,
+        0x1F0C,
+        0x1F0D,
+        0x1F0E,
+        0x1F0F,
+        0x1F10,
+        0x1F16,
+        0x1F18,
+        0x1F19,
+        0x1F1A,
+        0x1F1B,
+        0x1F1C,
+        0x1F1D,
+        0x1F1E,
+        0x1F20,
+        0x1F28,
+        0x1F29,
+        0x1F2A,
+        0x1F2B,
+        0x1F2C,
+        0x1F2D,
+        0x1F2E,
+        0x1F2F,
+        0x1F30,
+        0x1F38,
+        0x1F39,
+        0x1F3A,
+        0x1F3B,
+        0x1F3C,
+        0x1F3D,
+        0x1F3E,
+        0x1F3F,
+        0x1F40,
+        0x1F46,
+        0x1F48,
+        0x1F49,
+        0x1F4A,
+        0x1F4B,
+        0x1F4C,
+        0x1F4D,
+        0x1F4E,
+        0x1F50,
+        0x1F58,
+        0x1F59,
+        0x1F5A,
+        0x1F5B,
+        0x1F5C,
+        0x1F5D,
+        0x1F5E,
+        0x1F5F,
+        0x1F60,
+        0x1F68,
+        0x1F69,
+        0x1F6A,
+        0x1F6B,
+        0x1F6C,
+        0x1F6D,
+        0x1F6E,
+        0x1F6F,
+        0x1F70,
+        0x1F71,
+        0x1F72,
+        0x1F73,
+        0x1F74,
+        0x1F75,
+        0x1F76,
+        0x1F77,
+        0x1F78,
+        0x1F79,
+        0x1F7A,
+        0x1F7B,
+        0x1F7C,
+        0x1F7D,
+        0x1F7E,
+        0x1F80,
+        0x1F81,
+        0x1F82,
+        0x1F83,
+        0x1F84,
+        0x1F85,
+        0x1F86,
+        0x1F87,
+        0x1F88,
+        0x1F89,
+        0x1F8A,
+        0x1F8B,
+        0x1F8C,
+        0x1F8D,
+        0x1F8E,
+        0x1F8F,
+        0x1F90,
+        0x1F91,
+        0x1F92,
+        0x1F93,
+        0x1F94,
+        0x1F95,
+        0x1F96,
+        0x1F97,
+        0x1F98,
+        0x1F99,
+        0x1F9A,
+        0x1F9B,
+        0x1F9C,
+        0x1F9D,
+        0x1F9E,
+        0x1F9F,
+        0x1FA0,
+        0x1FA1,
+        0x1FA2,
+        0x1FA3,
+        0x1FA4,
+        0x1FA5,
+        0x1FA6,
+        0x1FA7,
+        0x1FA8,
+        0x1FA9,
+        0x1FAA,
+        0x1FAB,
+        0x1FAC,
+        0x1FAD,
+        0x1FAE,
+        0x1FAF,
+        0x1FB0,
+        0x1FB2,
+        0x1FB3,
+        0x1FB4,
+        0x1FB5,
+        0x1FB6,
+        0x1FB7,
+        0x1FB8,
+        0x1FB9,
+        0x1FBA,
+        0x1FBB,
+        0x1FBC,
+        0x1FBD,
+        0x1FBE,
+        0x1FBF,
+        0x1FC0,
+        0x1FC1,
+        0x1FC2,
+        0x1FC3,
+        0x1FC4,
+        0x1FC5,
+        0x1FC6,
+        0x1FC7,
+        0x1FC8,
+        0x1FC9,
+        0x1FCA,
+        0x1FCB,
+        0x1FCC,
+        0x1FCD,
+        0x1FCE,
+        0x1FCF,
+        0x1FD0,
+        0x1FD3,
+        0x1FD4,
+        0x1FD6,
+        0x1FD8,
+        0x1FD9,
+        0x1FDA,
+        0x1FDB,
+        0x1FDC,
+        0x1FDD,
+        0x1FDE,
+        0x1FDF,
+        0x1FE0,
+        0x1FE3,
+        0x1FE4,
+        0x1FE8,
+        0x1FE9,
+        0x1FEA,
+        0x1FEB,
+        0x1FEC,
+        0x1FED,
+        0x1FEE,
+        0x1FEF,
+        0x1FF0,
+        0x1FF2,
+        0x1FF3,
+        0x1FF4,
+        0x1FF5,
+        0x1FF6,
+        0x1FF7,
+        0x1FF8,
+        0x1FF9,
+        0x1FFA,
+        0x1FFB,
+        0x1FFC,
+        0x1FFD,
+        0x1FFE,
+        0x1FFF,
+        0x2000,
+        0x200B,
+        0x200C,
+        0x200E,
+        0x2010,
+        0x2011,
+        0x2012,
+        0x2017,
+        0x2018,
+        0x2024,
+        0x2027,
+        0x2028,
+        0x202F,
+        0x2030,
+        0x2033,
+        0x2034,
+        0x2035,
+        0x2036,
+        0x2037,
+        0x2038,
+        0x203C,
+        0x203D,
+        0x203E,
+        0x203F,
+        0x2047,
+        0x2048,
+        0x2049,
+        0x204A,
+        0x2057,
+        0x2058,
+        0x205F,
+        0x2060,
+        0x2065,
+        0x206A,
+        0x2070,
+        0x2071,
+        0x2072,
+        0x2074,
+        0x2075,
+        0x2076,
+        0x2077,
+        0x2078,
+        0x2079,
+        0x207A,
+        0x207B,
+        0x207C,
+        0x207D,
+        0x207E,
+        0x207F,
+        0x2080,
+        0x2081,
+        0x2082,
+        0x2083,
+        0x2084,
+        0x2085,
+        0x2086,
+        0x2087,
+        0x2088,
+        0x2089,
+        0x208A,
+        0x208B,
+        0x208C,
+        0x208D,
+        0x208E,
+        0x208F,
+        0x2090,
+        0x2091,
+        0x2092,
+        0x2093,
+        0x2094,
+        0x2095,
+        0x2096,
+        0x2097,
+        0x2098,
+        0x2099,
+        0x209A,
+        0x209B,
+        0x209C,
+        0x209D,
+        0x20A0,
+        0x20A8,
+        0x20A9,
+        0x20C2,
+        0x20D0,
+        0x20F1,
+        0x2100,
+        0x2101,
+        0x2102,
+        0x2103,
+        0x2104,
+        0x2105,
+        0x2106,
+        0x2107,
+        0x2108,
+        0x2109,
+        0x210A,
+        0x210B,
+        0x210F,
+        0x2110,
+        0x2112,
+        0x2114,
+        0x2115,
+        0x2116,
+        0x2117,
+        0x2119,
+        0x211A,
+        0x211B,
+        0x211E,
+        0x2120,
+        0x2121,
+        0x2122,
+        0x2123,
+        0x2124,
+        0x2125,
+        0x2126,
+        0x2127,
+        0x2128,
+        0x2129,
+        0x212A,
+        0x212B,
+        0x212C,
+        0x212D,
+        0x212E,
+        0x212F,
+        0x2131,
+        0x2132,
+        0x2133,
+        0x2134,
+        0x2135,
+        0x2136,
+        0x2137,
+        0x2138,
+        0x2139,
+        0x213A,
+        0x213B,
+        0x213C,
+        0x213D,
+        0x213F,
+        0x2140,
+        0x2141,
+        0x2145,
+        0x2147,
+        0x2148,
+        0x2149,
+        0x214A,
+        0x2150,
+        0x2151,
+        0x2152,
+        0x2153,
+        0x2154,
+        0x2155,
+        0x2156,
+        0x2157,
+        0x2158,
+        0x2159,
+        0x215A,
+        0x215B,
+        0x215C,
+        0x215D,
+        0x215E,
+        0x215F,
+        0x2160,
+        0x2161,
+        0x2162,
+        0x2163,
+        0x2164,
+        0x2165,
+        0x2166,
+        0x2167,
+        0x2168,
+        0x2169,
+        0x216A,
+        0x216B,
+        0x216C,
+        0x216D,
+        0x216E,
+        0x216F,
+        0x2170,
+        0x2171,
+        0x2172,
+        0x2173,
+        0x2174,
+        0x2175,
+        0x2176,
+        0x2177,
+        0x2178,
+        0x2179,
+        0x217A,
+        0x217B,
+        0x217C,
+        0x217D,
+        0x217E,
+        0x217F,
+        0x2180,
+        0x2183,
+        0x2184,
+        0x2189,
+        0x218A,
+        0x218C,
+        0x2190,
+        0x222C,
+        0x222D,
+        0x222E,
+        0x222F,
+        0x2230,
+        0x2231,
+        0x2329,
+        0x232A,
+        0x232B,
+        0x242A,
+        0x2440,
+        0x244B,
+        0x2460,
+        0x2461,
+        0x2462,
+        0x2463,
+        0x2464,
+        0x2465,
+        0x2466,
+        0x2467,
+        0x2468,
+        0x2469,
+        0x246A,
+        0x246B,
+        0x246C,
+        0x246D,
+        0x246E,
+        0x246F,
+        0x2470,
+        0x2471,
+        0x2472,
+        0x2473,
+        0x2474,
+        0x2475,
+        0x2476,
+        0x2477,
+        0x2478,
+        0x2479,
+        0x247A,
+        0x247B,
+        0x247C,
+        0x247D,
+        0x247E,
+        0x247F,
+        0x2480,
+        0x2481,
+        0x2482,
+        0x2483,
+        0x2484,
+        0x2485,
+        0x2486,
+        0x2487,
+        0x2488,
+        0x249C,
+        0x249D,
+        0x249E,
+        0x249F,
+        0x24A0,
+        0x24A1,
+        0x24A2,
+        0x24A3,
+        0x24A4,
+        0x24A5,
+        0x24A6,
+        0x24A7,
+        0x24A8,
+        0x24A9,
+        0x24AA,
+        0x24AB,
+        0x24AC,
+        0x24AD,
+        0x24AE,
+        0x24AF,
+        0x24B0,
+        0x24B1,
+        0x24B2,
+        0x24B3,
+        0x24B4,
+        0x24B5,
+        0x24B6,
+        0x24B7,
+        0x24B8,
+        0x24B9,
+        0x24BA,
+        0x24BB,
+        0x24BC,
+        0x24BD,
+        0x24BE,
+        0x24BF,
+        0x24C0,
+        0x24C1,
+        0x24C2,
+        0x24C3,
+        0x24C4,
+        0x24C5,
+        0x24C6,
+        0x24C7,
+        0x24C8,
+        0x24C9,
+        0x24CA,
+        0x24CB,
+        0x24CC,
+        0x24CD,
+        0x24CE,
+        0x24CF,
+        0x24D0,
+        0x24D1,
+        0x24D2,
+        0x24D3,
+        0x24D4,
+        0x24D5,
+        0x24D6,
+        0x24D7,
+        0x24D8,
+        0x24D9,
+        0x24DA,
+        0x24DB,
+        0x24DC,
+        0x24DD,
+        0x24DE,
+        0x24DF,
+        0x24E0,
+        0x24E1,
+        0x24E2,
+        0x24E3,
+        0x24E4,
+        0x24E5,
+        0x24E6,
+        0x24E7,
+        0x24E8,
+        0x24E9,
+        0x24EA,
+        0x24EB,
+        0x2A0C,
+        0x2A0D,
+        0x2A74,
+        0x2A75,
+        0x2A76,
+        0x2A77,
+        0x2ADC,
+        0x2ADD,
+        0x2B74,
+        0x2B76,
+        0x2C00,
+        0x2C01,
+        0x2C02,
+        0x2C03,
+        0x2C04,
+        0x2C05,
+        0x2C06,
+        0x2C07,
+        0x2C08,
+        0x2C09,
+        0x2C0A,
+        0x2C0B,
+        0x2C0C,
+        0x2C0D,
+        0x2C0E,
+        0x2C0F,
+        0x2C10,
+        0x2C11,
+        0x2C12,
+        0x2C13,
+        0x2C14,
+        0x2C15,
+        0x2C16,
+        0x2C17,
+        0x2C18,
+        0x2C19,
+        0x2C1A,
+        0x2C1B,
+        0x2C1C,
+        0x2C1D,
+        0x2C1E,
+        0x2C1F,
+        0x2C20,
+        0x2C21,
+        0x2C22,
+        0x2C23,
+        0x2C24,
+        0x2C25,
+        0x2C26,
+        0x2C27,
+        0x2C28,
+        0x2C29,
+        0x2C2A,
+        0x2C2B,
+        0x2C2C,
+        0x2C2D,
+        0x2C2E,
+        0x2C2F,
+        0x2C30,
+        0x2C60,
+        0x2C61,
+        0x2C62,
+        0x2C63,
+        0x2C64,
+        0x2C65,
+        0x2C67,
+        0x2C68,
+        0x2C69,
+        0x2C6A,
+        0x2C6B,
+        0x2C6C,
+        0x2C6D,
+        0x2C6E,
+        0x2C6F,
+        0x2C70,
+        0x2C71,
+        0x2C72,
+        0x2C73,
+        0x2C75,
+        0x2C76,
+        0x2C7C,
+        0x2C7D,
+        0x2C7E,
+        0x2C7F,
+        0x2C80,
+        0x2C81,
+        0x2C82,
+        0x2C83,
+        0x2C84,
+        0x2C85,
+        0x2C86,
+        0x2C87,
+        0x2C88,
+        0x2C89,
+        0x2C8A,
+        0x2C8B,
+        0x2C8C,
+        0x2C8D,
+        0x2C8E,
+        0x2C8F,
+        0x2C90,
+        0x2C91,
+        0x2C92,
+        0x2C93,
+        0x2C94,
+        0x2C95,
+        0x2C96,
+        0x2C97,
+        0x2C98,
+        0x2C99,
+        0x2C9A,
+        0x2C9B,
+        0x2C9C,
+        0x2C9D,
+        0x2C9E,
+        0x2C9F,
+        0x2CA0,
+        0x2CA1,
+        0x2CA2,
+        0x2CA3,
+        0x2CA4,
+        0x2CA5,
+        0x2CA6,
+        0x2CA7,
+        0x2CA8,
+        0x2CA9,
+        0x2CAA,
+        0x2CAB,
+        0x2CAC,
+        0x2CAD,
+        0x2CAE,
+        0x2CAF,
+        0x2CB0,
+        0x2CB1,
+        0x2CB2,
+        0x2CB3,
+        0x2CB4,
+        0x2CB5,
+        0x2CB6,
+        0x2CB7,
+        0x2CB8,
+        0x2CB9,
+        0x2CBA,
+        0x2CBB,
+        0x2CBC,
+        0x2CBD,
+        0x2CBE,
+        0x2CBF,
+        0x2CC0,
+        0x2CC1,
+        0x2CC2,
+        0x2CC3,
+        0x2CC4,
+        0x2CC5,
+        0x2CC6,
+        0x2CC7,
+        0x2CC8,
+        0x2CC9,
+        0x2CCA,
+        0x2CCB,
+        0x2CCC,
+        0x2CCD,
+        0x2CCE,
+        0x2CCF,
+        0x2CD0,
+        0x2CD1,
+        0x2CD2,
+        0x2CD3,
+        0x2CD4,
+        0x2CD5,
+        0x2CD6,
+        0x2CD7,
+        0x2CD8,
+        0x2CD9,
+        0x2CDA,
+        0x2CDB,
+        0x2CDC,
+        0x2CDD,
+        0x2CDE,
+        0x2CDF,
+        0x2CE0,
+        0x2CE1,
+        0x2CE2,
+        0x2CE3,
+        0x2CEB,
+        0x2CEC,
+        0x2CED,
+        0x2CEE,
+        0x2CF2,
+        0x2CF3,
+        0x2CF4,
+        0x2CF9,
+        0x2D26,
+        0x2D27,
+        0x2D28,
+        0x2D2D,
+        0x2D2E,
+        0x2D30,
+        0x2D68,
+        0x2D6F,
+        0x2D70,
+        0x2D71,
+        0x2D7F,
+        0x2D97,
+        0x2DA0,
+        0x2DA7,
+        0x2DA8,
+        0x2DAF,
+        0x2DB0,
+        0x2DB7,
+        0x2DB8,
+        0x2DBF,
+        0x2DC0,
+        0x2DC7,
+        0x2DC8,
+        0x2DCF,
+        0x2DD0,
+        0x2DD7,
+        0x2DD8,
+        0x2DDF,
+        0x2DE0,
+        0x2E5E,
+        0x2E80,
+        0x2E9A,
+        0x2E9B,
+        0x2E9F,
+        0x2EA0,
+        0x2EF3,
+        0x2EF4,
+        0x2F00,
+        0x2F01,
+        0x2F02,
+        0x2F03,
+        0x2F04,
+        0x2F05,
+        0x2F06,
+        0x2F07,
+        0x2F08,
+        0x2F09,
+        0x2F0A,
+        0x2F0B,
+        0x2F0C,
+        0x2F0D,
+        0x2F0E,
+        0x2F0F,
+        0x2F10,
+        0x2F11,
+        0x2F12,
+        0x2F13,
+        0x2F14,
+        0x2F15,
+        0x2F16,
+        0x2F17,
+        0x2F18,
+        0x2F19,
+        0x2F1A,
+        0x2F1B,
+        0x2F1C,
+        0x2F1D,
+        0x2F1E,
+        0x2F1F,
+        0x2F20,
+        0x2F21,
+        0x2F22,
+        0x2F23,
+        0x2F24,
+        0x2F25,
+        0x2F26,
+        0x2F27,
+        0x2F28,
+        0x2F29,
+        0x2F2A,
+        0x2F2B,
+        0x2F2C,
+        0x2F2D,
+        0x2F2E,
+        0x2F2F,
+        0x2F30,
+        0x2F31,
+        0x2F32,
+        0x2F33,
+        0x2F34,
+        0x2F35,
+        0x2F36,
+        0x2F37,
+        0x2F38,
+        0x2F39,
+        0x2F3A,
+        0x2F3B,
+        0x2F3C,
+        0x2F3D,
+        0x2F3E,
+        0x2F3F,
+        0x2F40,
+        0x2F41,
+        0x2F42,
+        0x2F43,
+        0x2F44,
+        0x2F45,
+        0x2F46,
+        0x2F47,
+        0x2F48,
+        0x2F49,
+        0x2F4A,
+        0x2F4B,
+        0x2F4C,
+        0x2F4D,
+        0x2F4E,
+        0x2F4F,
+        0x2F50,
+        0x2F51,
+        0x2F52,
+        0x2F53,
+        0x2F54,
+        0x2F55,
+        0x2F56,
+        0x2F57,
+        0x2F58,
+        0x2F59,
+        0x2F5A,
+        0x2F5B,
+        0x2F5C,
+        0x2F5D,
+        0x2F5E,
+        0x2F5F,
+        0x2F60,
+        0x2F61,
+        0x2F62,
+        0x2F63,
+        0x2F64,
+        0x2F65,
+        0x2F66,
+        0x2F67,
+        0x2F68,
+        0x2F69,
+        0x2F6A,
+        0x2F6B,
+        0x2F6C,
+        0x2F6D,
+        0x2F6E,
+        0x2F6F,
+        0x2F70,
+        0x2F71,
+        0x2F72,
+        0x2F73,
+        0x2F74,
+        0x2F75,
+        0x2F76,
+        0x2F77,
+        0x2F78,
+        0x2F79,
+        0x2F7A,
+        0x2F7B,
+        0x2F7C,
+        0x2F7D,
+        0x2F7E,
+        0x2F7F,
+        0x2F80,
+        0x2F81,
+        0x2F82,
+        0x2F83,
+        0x2F84,
+        0x2F85,
+        0x2F86,
+        0x2F87,
+        0x2F88,
+        0x2F89,
+        0x2F8A,
+        0x2F8B,
+        0x2F8C,
+        0x2F8D,
+        0x2F8E,
+        0x2F8F,
+        0x2F90,
+        0x2F91,
+        0x2F92,
+        0x2F93,
+        0x2F94,
+        0x2F95,
+        0x2F96,
+        0x2F97,
+        0x2F98,
+        0x2F99,
+        0x2F9A,
+        0x2F9B,
+        0x2F9C,
+        0x2F9D,
+        0x2F9E,
+        0x2F9F,
+        0x2FA0,
+        0x2FA1,
+        0x2FA2,
+        0x2FA3,
+        0x2FA4,
+        0x2FA5,
+        0x2FA6,
+        0x2FA7,
+        0x2FA8,
+        0x2FA9,
+        0x2FAA,
+        0x2FAB,
+        0x2FAC,
+        0x2FAD,
+        0x2FAE,
+        0x2FAF,
+        0x2FB0,
+        0x2FB1,
+        0x2FB2,
+        0x2FB3,
+        0x2FB4,
+        0x2FB5,
+        0x2FB6,
+        0x2FB7,
+        0x2FB8,
+        0x2FB9,
+        0x2FBA,
+        0x2FBB,
+        0x2FBC,
+        0x2FBD,
+        0x2FBE,
+        0x2FBF,
+        0x2FC0,
+        0x2FC1,
+        0x2FC2,
+        0x2FC3,
+        0x2FC4,
+        0x2FC5,
+        0x2FC6,
+        0x2FC7,
+        0x2FC8,
+        0x2FC9,
+        0x2FCA,
+        0x2FCB,
+        0x2FCC,
+        0x2FCD,
+        0x2FCE,
+        0x2FCF,
+        0x2FD0,
+        0x2FD1,
+        0x2FD2,
+        0x2FD3,
+        0x2FD4,
+        0x2FD5,
+        0x2FD6,
+        0x3000,
+        0x3001,
+        0x3002,
+        0x3003,
+        0x3036,
+        0x3037,
+        0x3038,
+        0x3039,
+        0x303A,
+        0x303B,
+        0x3040,
+        0x3041,
+        0x3097,
+        0x3099,
+        0x309B,
+        0x309C,
+        0x309D,
+        0x309F,
+        0x30A0,
+        0x30FF,
+        0x3100,
+        0x3105,
+        0x3130,
+        0x3131,
+        0x3132,
+        0x3133,
+        0x3134,
+        0x3135,
+        0x3136,
+        0x3137,
+        0x3138,
+        0x3139,
+        0x313A,
+        0x313B,
+        0x313C,
+        0x313D,
+        0x313E,
+        0x313F,
+        0x3140,
+        0x3141,
+        0x3142,
+        0x3143,
+        0x3144,
+        0x3145,
+        0x3146,
+        0x3147,
+        0x3148,
+        0x3149,
+        0x314A,
+        0x314B,
+        0x314C,
+        0x314D,
+        0x314E,
+        0x314F,
+        0x3150,
+        0x3151,
+        0x3152,
+        0x3153,
+        0x3154,
+        0x3155,
+        0x3156,
+        0x3157,
+        0x3158,
+        0x3159,
+        0x315A,
+        0x315B,
+        0x315C,
+        0x315D,
+        0x315E,
+        0x315F,
+        0x3160,
+        0x3161,
+        0x3162,
+        0x3163,
+        0x3164,
+        0x3165,
+        0x3166,
+        0x3167,
+        0x3168,
+        0x3169,
+        0x316A,
+        0x316B,
+        0x316C,
+        0x316D,
+        0x316E,
+        0x316F,
+        0x3170,
+        0x3171,
+        0x3172,
+        0x3173,
+        0x3174,
+        0x3175,
+        0x3176,
+        0x3177,
+        0x3178,
+        0x3179,
+        0x317A,
+        0x317B,
+        0x317C,
+        0x317D,
+        0x317E,
+        0x317F,
+        0x3180,
+        0x3181,
+        0x3182,
+        0x3183,
+        0x3184,
+        0x3185,
+        0x3186,
+        0x3187,
+        0x3188,
+        0x3189,
+        0x318A,
+        0x318B,
+        0x318C,
+        0x318D,
+        0x318E,
+        0x318F,
+        0x3190,
+        0x3192,
+        0x3193,
+        0x3194,
+        0x3195,
+        0x3196,
+        0x3197,
+        0x3198,
+        0x3199,
+        0x319A,
+        0x319B,
+        0x319C,
+        0x319D,
+        0x319E,
+        0x319F,
+        0x31A0,
+        0x31E6,
+        0x31F0,
+        0x3200,
+        0x3201,
+        0x3202,
+        0x3203,
+        0x3204,
+        0x3205,
+        0x3206,
+        0x3207,
+        0x3208,
+        0x3209,
+        0x320A,
+        0x320B,
+        0x320C,
+        0x320D,
+        0x320E,
+        0x320F,
+        0x3210,
+        0x3211,
+        0x3212,
+        0x3213,
+        0x3214,
+        0x3215,
+        0x3216,
+        0x3217,
+        0x3218,
+        0x3219,
+        0x321A,
+        0x321B,
+        0x321C,
+        0x321D,
+        0x321E,
+        0x321F,
+        0x3220,
+        0x3221,
+        0x3222,
+        0x3223,
+        0x3224,
+        0x3225,
+        0x3226,
+        0x3227,
+        0x3228,
+        0x3229,
+        0x322A,
+        0x322B,
+        0x322C,
+        0x322D,
+        0x322E,
+        0x322F,
+        0x3230,
+        0x3231,
+        0x3232,
+        0x3233,
+        0x3234,
+        0x3235,
+        0x3236,
+        0x3237,
+        0x3238,
+        0x3239,
+        0x323A,
+        0x323B,
+        0x323C,
+        0x323D,
+        0x323E,
+        0x323F,
+        0x3240,
+        0x3241,
+        0x3242,
+        0x3243,
+        0x3244,
+        0x3245,
+        0x3246,
+        0x3247,
+        0x3248,
+        0x3250,
+        0x3251,
+        0x3252,
+        0x3253,
+        0x3254,
+        0x3255,
+        0x3256,
+        0x3257,
+        0x3258,
+        0x3259,
+        0x325A,
+        0x325B,
+        0x325C,
+        0x325D,
+        0x325E,
+        0x325F,
+        0x3260,
+        0x3261,
+        0x3262,
+        0x3263,
+        0x3264,
+        0x3265,
+        0x3266,
+        0x3267,
+        0x3268,
+        0x3269,
+        0x326A,
+        0x326B,
+        0x326C,
+        0x326D,
+        0x326E,
+        0x326F,
+        0x3270,
+        0x3271,
+        0x3272,
+        0x3273,
+        0x3274,
+        0x3275,
+        0x3276,
+        0x3277,
+        0x3278,
+        0x3279,
+        0x327A,
+        0x327B,
+        0x327C,
+        0x327D,
+        0x327E,
+        0x327F,
+        0x3280,
+        0x3281,
+        0x3282,
+        0x3283,
+        0x3284,
+        0x3285,
+        0x3286,
+        0x3287,
+        0x3288,
+        0x3289,
+        0x328A,
+        0x328B,
+        0x328C,
+        0x328D,
+        0x328E,
+        0x328F,
+        0x3290,
+        0x3291,
+        0x3292,
+        0x3293,
+        0x3294,
+        0x3295,
+        0x3296,
+        0x3297,
+        0x3298,
+        0x3299,
+        0x329A,
+        0x329B,
+        0x329C,
+        0x329D,
+        0x329E,
+        0x329F,
+        0x32A0,
+        0x32A1,
+        0x32A2,
+        0x32A3,
+        0x32A4,
+        0x32A5,
+        0x32A6,
+        0x32A7,
+        0x32A8,
+        0x32A9,
+        0x32AA,
+        0x32AB,
+        0x32AC,
+        0x32AD,
+        0x32AE,
+        0x32AF,
+        0x32B0,
+        0x32B1,
+        0x32B2,
+        0x32B3,
+        0x32B4,
+        0x32B5,
+        0x32B6,
+        0x32B7,
+        0x32B8,
+        0x32B9,
+        0x32BA,
+        0x32BB,
+        0x32BC,
+        0x32BD,
+        0x32BE,
+        0x32BF,
+        0x32C0,
+        0x32C1,
+        0x32C2,
+        0x32C3,
+        0x32C4,
+        0x32C5,
+        0x32C6,
+        0x32C7,
+        0x32C8,
+        0x32C9,
+        0x32CA,
+        0x32CB,
+        0x32CC,
+        0x32CD,
+        0x32CE,
+        0x32CF,
+        0x32D0,
+        0x32D1,
+        0x32D2,
+        0x32D3,
+        0x32D4,
+        0x32D5,
+        0x32D6,
+        0x32D7,
+        0x32D8,
+        0x32D9,
+        0x32DA,
+        0x32DB,
+        0x32DC,
+        0x32DD,
+        0x32DE,
+        0x32DF,
+        0x32E0,
+        0x32E1,
+        0x32E2,
+        0x32E3,
+        0x32E4,
+        0x32E5,
+        0x32E6,
+        0x32E7,
+        0x32E8,
+        0x32E9,
+        0x32EA,
+        0x32EB,
+        0x32EC,
+        0x32ED,
+        0x32EE,
+        0x32EF,
+        0x32F0,
+        0x32F1,
+        0x32F2,
+        0x32F3,
+        0x32F4,
+        0x32F5,
+        0x32F6,
+        0x32F7,
+        0x32F8,
+        0x32F9,
+        0x32FA,
+        0x32FB,
+        0x32FC,
+        0x32FD,
+        0x32FE,
+        0x32FF,
+        0x3300,
+        0x3301,
+        0x3302,
+        0x3303,
+        0x3304,
+        0x3305,
+        0x3306,
+        0x3307,
+        0x3308,
+        0x3309,
+        0x330A,
+        0x330B,
+        0x330C,
+        0x330D,
+        0x330E,
+        0x330F,
+        0x3310,
+        0x3311,
+        0x3312,
+        0x3313,
+        0x3314,
+        0x3315,
+        0x3316,
+        0x3317,
+        0x3318,
+        0x3319,
+        0x331A,
+        0x331B,
+        0x331C,
+        0x331D,
+        0x331E,
+        0x331F,
+        0x3320,
+        0x3321,
+        0x3322,
+        0x3323,
+        0x3324,
+        0x3325,
+        0x3326,
+        0x3327,
+        0x3328,
+        0x3329,
+        0x332A,
+        0x332B,
+        0x332C,
+        0x332D,
+        0x332E,
+        0x332F,
+        0x3330,
+        0x3331,
+        0x3332,
+        0x3333,
+        0x3334,
+        0x3335,
+        0x3336,
+        0x3337,
+        0x3338,
+        0x3339,
+        0x333A,
+        0x333B,
+        0x333C,
+        0x333D,
+        0x333E,
+        0x333F,
+        0x3340,
+        0x3341,
+        0x3342,
+        0x3343,
+        0x3344,
+        0x3345,
+        0x3346,
+        0x3347,
+        0x3348,
+        0x3349,
+        0x334A,
+        0x334B,
+        0x334C,
+        0x334D,
+        0x334E,
+        0x334F,
+        0x3350,
+        0x3351,
+        0x3352,
+        0x3353,
+        0x3354,
+        0x3355,
+        0x3356,
+        0x3357,
+        0x3358,
+        0x3359,
+        0x335A,
+        0x335B,
+        0x335C,
+        0x335D,
+        0x335E,
+        0x335F,
+        0x3360,
+        0x3361,
+        0x3362,
+        0x3363,
+        0x3364,
+        0x3365,
+        0x3366,
+        0x3367,
+        0x3368,
+        0x3369,
+        0x336A,
+        0x336B,
+        0x336C,
+        0x336D,
+        0x336E,
+        0x336F,
+        0x3370,
+        0x3371,
+        0x3372,
+        0x3373,
+        0x3374,
+        0x3375,
+        0x3376,
+        0x3377,
+        0x3378,
+        0x3379,
+        0x337A,
+        0x337B,
+        0x337C,
+        0x337D,
+        0x337E,
+        0x337F,
+        0x3380,
+        0x3381,
+        0x3382,
+        0x3383,
+        0x3384,
+        0x3385,
+        0x3386,
+        0x3387,
+        0x3388,
+        0x3389,
+        0x338A,
+        0x338B,
+        0x338C,
+        0x338D,
+        0x338E,
+        0x338F,
+        0x3390,
+        0x3391,
+        0x3392,
+        0x3393,
+        0x3394,
+        0x3395,
+        0x3396,
+        0x3397,
+        0x3398,
+        0x3399,
+        0x339A,
+        0x339B,
+        0x339C,
+        0x339D,
+        0x339E,
+        0x339F,
+        0x33A0,
+        0x33A1,
+        0x33A2,
+        0x33A3,
+        0x33A4,
+        0x33A5,
+        0x33A6,
+        0x33A7,
+        0x33A8,
+        0x33A9,
+        0x33AA,
+        0x33AB,
+        0x33AC,
+        0x33AD,
+        0x33AE,
+        0x33AF,
+        0x33B0,
+        0x33B1,
+        0x33B2,
+        0x33B3,
+        0x33B4,
+        0x33B5,
+        0x33B6,
+        0x33B7,
+        0x33B8,
+        0x33B9,
+        0x33BA,
+        0x33BB,
+        0x33BC,
+        0x33BD,
+        0x33BE,
+        0x33BF,
+        0x33C0,
+        0x33C1,
+        0x33C2,
+        0x33C3,
+        0x33C4,
+        0x33C5,
+        0x33C6,
+        0x33C7,
+        0x33C8,
+        0x33C9,
+        0x33CA,
+        0x33CB,
+        0x33CC,
+        0x33CD,
+        0x33CE,
+        0x33CF,
+        0x33D0,
+        0x33D1,
+        0x33D2,
+        0x33D3,
+        0x33D4,
+        0x33D5,
+        0x33D6,
+        0x33D7,
+        0x33D8,
+        0x33D9,
+        0x33DA,
+        0x33DB,
+        0x33DC,
+        0x33DD,
+        0x33DE,
+        0x33DF,
+        0x33E0,
+        0x33E1,
+        0x33E2,
+        0x33E3,
+        0x33E4,
+        0x33E5,
+        0x33E6,
+        0x33E7,
+        0x33E8,
+        0x33E9,
+        0x33EA,
+        0x33EB,
+        0x33EC,
+        0x33ED,
+        0x33EE,
+        0x33EF,
+        0x33F0,
+        0x33F1,
+        0x33F2,
+        0x33F3,
+        0x33F4,
+        0x33F5,
+        0x33F6,
+        0x33F7,
+        0x33F8,
+        0x33F9,
+        0x33FA,
+        0x33FB,
+        0x33FC,
+        0x33FD,
+        0x33FE,
+        0x33FF,
+        0x3400,
+        0xA48D,
+        0xA490,
+        0xA4C7,
+        0xA4D0,
+        0xA62C,
+        0xA640,
+        0xA641,
+        0xA642,
+        0xA643,
+        0xA644,
+        0xA645,
+        0xA646,
+        0xA647,
+        0xA648,
+        0xA649,
+        0xA64A,
+        0xA64B,
+        0xA64C,
+        0xA64D,
+        0xA64E,
+        0xA64F,
+        0xA650,
+        0xA651,
+        0xA652,
+        0xA653,
+        0xA654,
+        0xA655,
+        0xA656,
+        0xA657,
+        0xA658,
+        0xA659,
+        0xA65A,
+        0xA65B,
+        0xA65C,
+        0xA65D,
+        0xA65E,
+        0xA65F,
+        0xA660,
+        0xA661,
+        0xA662,
+        0xA663,
+        0xA664,
+        0xA665,
+        0xA666,
+        0xA667,
+        0xA668,
+        0xA669,
+        0xA66A,
+        0xA66B,
+        0xA66C,
+        0xA66D,
+        0xA680,
+        0xA681,
+        0xA682,
+        0xA683,
+        0xA684,
+        0xA685,
+        0xA686,
+        0xA687,
+        0xA688,
+        0xA689,
+        0xA68A,
+        0xA68B,
+        0xA68C,
+        0xA68D,
+        0xA68E,
+        0xA68F,
+        0xA690,
+        0xA691,
+        0xA692,
+        0xA693,
+        0xA694,
+        0xA695,
+        0xA696,
+        0xA697,
+        0xA698,
+        0xA699,
+        0xA69A,
+        0xA69B,
+        0xA69C,
+        0xA69D,
+        0xA69E,
+        0xA6F8,
+        0xA700,
+        0xA722,
+        0xA723,
+        0xA724,
+        0xA725,
+        0xA726,
+        0xA727,
+        0xA728,
+        0xA729,
+        0xA72A,
+        0xA72B,
+        0xA72C,
+        0xA72D,
+        0xA72E,
+        0xA72F,
+        0xA732,
+        0xA733,
+        0xA734,
+        0xA735,
+        0xA736,
+        0xA737,
+        0xA738,
+        0xA739,
+        0xA73A,
+        0xA73B,
+        0xA73C,
+        0xA73D,
+        0xA73E,
+        0xA73F,
+        0xA740,
+        0xA741,
+        0xA742,
+        0xA743,
+        0xA744,
+        0xA745,
+        0xA746,
+        0xA747,
+        0xA748,
+        0xA749,
+        0xA74A,
+        0xA74B,
+        0xA74C,
+        0xA74D,
+        0xA74E,
+        0xA74F,
+        0xA750,
+        0xA751,
+        0xA752,
+        0xA753,
+        0xA754,
+        0xA755,
+        0xA756,
+        0xA757,
+        0xA758,
+        0xA759,
+        0xA75A,
+        0xA75B,
+        0xA75C,
+        0xA75D,
+        0xA75E,
+        0xA75F,
+        0xA760,
+        0xA761,
+        0xA762,
+        0xA763,
+        0xA764,
+        0xA765,
+        0xA766,
+        0xA767,
+        0xA768,
+        0xA769,
+        0xA76A,
+        0xA76B,
+        0xA76C,
+        0xA76D,
+        0xA76E,
+        0xA76F,
+        0xA770,
+        0xA771,
+        0xA779,
+        0xA77A,
+        0xA77B,
+        0xA77C,
+        0xA77D,
+        0xA77E,
+        0xA77F,
+        0xA780,
+        0xA781,
+        0xA782,
+        0xA783,
+        0xA784,
+        0xA785,
+        0xA786,
+        0xA787,
+        0xA78B,
+        0xA78C,
+        0xA78D,
+        0xA78E,
+        0xA790,
+        0xA791,
+        0xA792,
+        0xA793,
+        0xA796,
+        0xA797,
+        0xA798,
+        0xA799,
+        0xA79A,
+        0xA79B,
+        0xA79C,
+        0xA79D,
+        0xA79E,
+        0xA79F,
+        0xA7A0,
+        0xA7A1,
+        0xA7A2,
+        0xA7A3,
+        0xA7A4,
+        0xA7A5,
+        0xA7A6,
+        0xA7A7,
+        0xA7A8,
+        0xA7A9,
+        0xA7AA,
+        0xA7AB,
+        0xA7AC,
+        0xA7AD,
+        0xA7AE,
+        0xA7AF,
+        0xA7B0,
+        0xA7B1,
+        0xA7B2,
+        0xA7B3,
+        0xA7B4,
+        0xA7B5,
+        0xA7B6,
+        0xA7B7,
+        0xA7B8,
+        0xA7B9,
+        0xA7BA,
+        0xA7BB,
+        0xA7BC,
+        0xA7BD,
+        0xA7BE,
+        0xA7BF,
+        0xA7C0,
+        0xA7C1,
+        0xA7C2,
+        0xA7C3,
+        0xA7C4,
+        0xA7C5,
+        0xA7C6,
+        0xA7C7,
+        0xA7C8,
+        0xA7C9,
+        0xA7CA,
+        0xA7CB,
+        0xA7CC,
+        0xA7CD,
+        0xA7CE,
+        0xA7CF,
+        0xA7D0,
+        0xA7D1,
+        0xA7D2,
+        0xA7D3,
+        0xA7D4,
+        0xA7D5,
+        0xA7D6,
+        0xA7D7,
+        0xA7D8,
+        0xA7D9,
+        0xA7DA,
+        0xA7DB,
+        0xA7DC,
+        0xA7DD,
+        0xA7F1,
+        0xA7F2,
+        0xA7F3,
+        0xA7F4,
+        0xA7F5,
+        0xA7F6,
+        0xA7F8,
+        0xA7F9,
+        0xA7FA,
+        0xA82D,
+        0xA830,
+        0xA83A,
+        0xA840,
+        0xA878,
+        0xA880,
+        0xA8C6,
+        0xA8CE,
+        0xA8DA,
+        0xA8E0,
+        0xA954,
+        0xA95F,
+        0xA97D,
+        0xA980,
+        0xA9CE,
+        0xA9CF,
+        0xA9DA,
+        0xA9DE,
+        0xA9FF,
+        0xAA00,
+        0xAA37,
+        0xAA40,
+        0xAA4E,
+        0xAA50,
+        0xAA5A,
+        0xAA5C,
+        0xAAC3,
+        0xAADB,
+        0xAAF7,
+        0xAB01,
+        0xAB07,
+        0xAB09,
+        0xAB0F,
+        0xAB11,
+        0xAB17,
+        0xAB20,
+        0xAB27,
+        0xAB28,
+        0xAB2F,
+        0xAB30,
+        0xAB5C,
+        0xAB5D,
+        0xAB5E,
+        0xAB5F,
+        0xAB60,
+        0xAB69,
+        0xAB6A,
+        0xAB6C,
+        0xAB70,
+        0xAB71,
+        0xAB72,
+        0xAB73,
+        0xAB74,
+        0xAB75,
+        0xAB76,
+        0xAB77,
+        0xAB78,
+        0xAB79,
+        0xAB7A,
+        0xAB7B,
+        0xAB7C,
+        0xAB7D,
+        0xAB7E,
+        0xAB7F,
+        0xAB80,
+        0xAB81,
+        0xAB82,
+        0xAB83,
+        0xAB84,
+        0xAB85,
+        0xAB86,
+        0xAB87,
+        0xAB88,
+        0xAB89,
+        0xAB8A,
+        0xAB8B,
+        0xAB8C,
+        0xAB8D,
+        0xAB8E,
+        0xAB8F,
+        0xAB90,
+        0xAB91,
+        0xAB92,
+        0xAB93,
+        0xAB94,
+        0xAB95,
+        0xAB96,
+        0xAB97,
+        0xAB98,
+        0xAB99,
+        0xAB9A,
+        0xAB9B,
+        0xAB9C,
+        0xAB9D,
+        0xAB9E,
+        0xAB9F,
+        0xABA0,
+        0xABA1,
+        0xABA2,
+        0xABA3,
+        0xABA4,
+        0xABA5,
+        0xABA6,
+        0xABA7,
+        0xABA8,
+        0xABA9,
+        0xABAA,
+        0xABAB,
+        0xABAC,
+        0xABAD,
+        0xABAE,
+        0xABAF,
+        0xABB0,
+        0xABB1,
+        0xABB2,
+        0xABB3,
+        0xABB4,
+        0xABB5,
+        0xABB6,
+        0xABB7,
+        0xABB8,
+        0xABB9,
+        0xABBA,
+        0xABBB,
+        0xABBC,
+        0xABBD,
+        0xABBE,
+        0xABBF,
+        0xABC0,
+        0xABEE,
+        0xABF0,
+        0xABFA,
+        0xAC00,
+        0xD7A4,
+        0xD7B0,
+        0xD7C7,
+        0xD7CB,
+        0xD7FC,
+        0xF900,
+        0xF901,
+        0xF902,
+        0xF903,
+        0xF904,
+        0xF905,
+        0xF906,
+        0xF907,
+        0xF909,
+        0xF90A,
+        0xF90B,
+        0xF90C,
+        0xF90D,
+        0xF90E,
+        0xF90F,
+        0xF910,
+        0xF911,
+        0xF912,
+        0xF913,
+        0xF914,
+        0xF915,
+        0xF916,
+        0xF917,
+        0xF918,
+        0xF919,
+        0xF91A,
+        0xF91B,
+        0xF91C,
+        0xF91D,
+        0xF91E,
+        0xF91F,
+        0xF920,
+        0xF921,
+        0xF922,
+        0xF923,
+        0xF924,
+        0xF925,
+        0xF926,
+        0xF927,
+        0xF928,
+        0xF929,
+        0xF92A,
+        0xF92B,
+        0xF92C,
+        0xF92D,
+        0xF92E,
+        0xF92F,
+        0xF930,
+        0xF931,
+        0xF932,
+        0xF933,
+        0xF934,
+        0xF935,
+        0xF936,
+        0xF937,
+        0xF938,
+        0xF939,
+        0xF93A,
+        0xF93B,
+        0xF93C,
+        0xF93D,
+        0xF93E,
+        0xF93F,
+        0xF940,
+        0xF941,
+        0xF942,
+        0xF943,
+        0xF944,
+        0xF945,
+        0xF946,
+        0xF947,
+        0xF948,
+        0xF949,
+        0xF94A,
+        0xF94B,
+        0xF94C,
+        0xF94D,
+        0xF94E,
+        0xF94F,
+        0xF950,
+        0xF951,
+        0xF952,
+        0xF953,
+        0xF954,
+        0xF955,
+        0xF956,
+        0xF957,
+        0xF958,
+        0xF959,
+        0xF95A,
+        0xF95B,
+        0xF95C,
+        0xF95D,
+        0xF95E,
+        0xF95F,
+        0xF960,
+        0xF961,
+        0xF962,
+        0xF963,
+        0xF964,
+        0xF965,
+        0xF966,
+        0xF967,
+        0xF968,
+        0xF969,
+        0xF96A,
+        0xF96B,
+        0xF96C,
+        0xF96D,
+        0xF96E,
+        0xF96F,
+        0xF970,
+        0xF971,
+        0xF972,
+        0xF973,
+        0xF974,
+        0xF975,
+        0xF976,
+        0xF977,
+        0xF978,
+        0xF979,
+        0xF97A,
+        0xF97B,
+        0xF97C,
+        0xF97D,
+        0xF97E,
+        0xF97F,
+        0xF980,
+        0xF981,
+        0xF982,
+        0xF983,
+        0xF984,
+        0xF985,
+        0xF986,
+        0xF987,
+        0xF988,
+        0xF989,
+        0xF98A,
+        0xF98B,
+        0xF98C,
+        0xF98D,
+        0xF98E,
+        0xF98F,
+        0xF990,
+        0xF991,
+        0xF992,
+        0xF993,
+        0xF994,
+        0xF995,
+        0xF996,
+        0xF997,
+        0xF998,
+        0xF999,
+        0xF99A,
+        0xF99B,
+        0xF99C,
+        0xF99D,
+        0xF99E,
+        0xF99F,
+        0xF9A0,
+        0xF9A1,
+        0xF9A2,
+        0xF9A3,
+        0xF9A4,
+        0xF9A5,
+        0xF9A6,
+        0xF9A7,
+        0xF9A8,
+        0xF9A9,
+        0xF9AA,
+        0xF9AB,
+        0xF9AC,
+        0xF9AD,
+        0xF9AE,
+        0xF9AF,
+        0xF9B0,
+        0xF9B1,
+        0xF9B2,
+        0xF9B3,
+        0xF9B4,
+        0xF9B5,
+        0xF9B6,
+        0xF9B7,
+        0xF9B8,
+        0xF9B9,
+        0xF9BA,
+        0xF9BB,
+        0xF9BC,
+        0xF9BD,
+        0xF9BE,
+        0xF9BF,
+        0xF9C0,
+        0xF9C1,
+        0xF9C2,
+        0xF9C3,
+        0xF9C4,
+        0xF9C5,
+        0xF9C6,
+        0xF9C7,
+        0xF9C8,
+        0xF9C9,
+        0xF9CA,
+        0xF9CB,
+        0xF9CC,
+        0xF9CD,
+        0xF9CE,
+        0xF9CF,
+        0xF9D0,
+        0xF9D1,
+        0xF9D2,
+        0xF9D3,
+        0xF9D4,
+        0xF9D5,
+        0xF9D6,
+        0xF9D7,
+        0xF9D8,
+        0xF9D9,
+        0xF9DA,
+        0xF9DB,
+        0xF9DC,
+        0xF9DD,
+        0xF9DE,
+        0xF9DF,
+        0xF9E0,
+        0xF9E1,
+        0xF9E2,
+        0xF9E3,
+        0xF9E4,
+        0xF9E5,
+        0xF9E6,
+        0xF9E7,
+        0xF9E8,
+        0xF9E9,
+        0xF9EA,
+        0xF9EB,
+        0xF9EC,
+        0xF9ED,
+        0xF9EE,
+        0xF9EF,
+        0xF9F0,
+        0xF9F1,
+        0xF9F2,
+        0xF9F3,
+        0xF9F4,
+        0xF9F5,
+        0xF9F6,
+        0xF9F7,
+        0xF9F8,
+        0xF9F9,
+        0xF9FA,
+        0xF9FB,
+        0xF9FC,
+        0xF9FD,
+        0xF9FE,
+        0xF9FF,
+        0xFA00,
+        0xFA01,
+        0xFA02,
+        0xFA03,
+        0xFA04,
+        0xFA05,
+        0xFA06,
+        0xFA07,
+        0xFA08,
+        0xFA09,
+        0xFA0A,
+        0xFA0B,
+        0xFA0C,
+        0xFA0D,
+        0xFA0E,
+        0xFA10,
+        0xFA11,
+        0xFA12,
+        0xFA13,
+        0xFA15,
+        0xFA16,
+        0xFA17,
+        0xFA18,
+        0xFA19,
+        0xFA1A,
+        0xFA1B,
+        0xFA1C,
+        0xFA1D,
+        0xFA1E,
+        0xFA1F,
+        0xFA20,
+        0xFA21,
+        0xFA22,
+        0xFA23,
+        0xFA25,
+        0xFA26,
+        0xFA27,
+        0xFA2A,
+        0xFA2B,
+        0xFA2C,
+        0xFA2D,
+        0xFA2E,
+        0xFA2F,
+        0xFA30,
+        0xFA31,
+        0xFA32,
+        0xFA33,
+        0xFA34,
+        0xFA35,
+        0xFA36,
+        0xFA37,
+        0xFA38,
+        0xFA39,
+        0xFA3A,
+        0xFA3B,
+        0xFA3C,
+        0xFA3D,
+        0xFA3E,
+        0xFA3F,
+        0xFA40,
+        0xFA41,
+        0xFA42,
+        0xFA43,
+        0xFA44,
+        0xFA45,
+        0xFA46,
+        0xFA47,
+        0xFA48,
+        0xFA49,
+        0xFA4A,
+        0xFA4B,
+        0xFA4C,
+        0xFA4D,
+        0xFA4E,
+        0xFA4F,
+        0xFA50,
+        0xFA51,
+        0xFA52,
+        0xFA53,
+        0xFA54,
+        0xFA55,
+        0xFA56,
+        0xFA57,
+        0xFA58,
+        0xFA59,
+        0xFA5A,
+        0xFA5B,
+        0xFA5C,
+        0xFA5D,
+        0xFA5F,
+        0xFA60,
+        0xFA61,
+        0xFA62,
+        0xFA63,
+        0xFA64,
+        0xFA65,
+        0xFA66,
+        0xFA67,
+        0xFA68,
+        0xFA69,
+        0xFA6A,
+        0xFA6B,
+        0xFA6C,
+        0xFA6D,
+        0xFA6E,
+        0xFA70,
+        0xFA71,
+        0xFA72,
+        0xFA73,
+        0xFA74,
+        0xFA75,
+        0xFA76,
+        0xFA77,
+        0xFA78,
+        0xFA79,
+        0xFA7A,
+        0xFA7B,
+        0xFA7C,
+        0xFA7D,
+        0xFA7E,
+        0xFA7F,
+        0xFA80,
+        0xFA81,
+        0xFA82,
+        0xFA83,
+        0xFA84,
+        0xFA85,
+        0xFA86,
+        0xFA87,
+        0xFA88,
+        0xFA89,
+        0xFA8A,
+        0xFA8B,
+        0xFA8C,
+        0xFA8D,
+        0xFA8E,
+        0xFA8F,
+        0xFA90,
+        0xFA91,
+        0xFA92,
+        0xFA93,
+        0xFA94,
+        0xFA95,
+        0xFA96,
+        0xFA97,
+        0xFA98,
+        0xFA99,
+        0xFA9A,
+        0xFA9B,
+        0xFA9C,
+        0xFA9D,
+        0xFA9E,
+        0xFA9F,
+        0xFAA0,
+        0xFAA1,
+        0xFAA2,
+        0xFAA3,
+        0xFAA4,
+        0xFAA5,
+        0xFAA6,
+        0xFAA7,
+        0xFAA8,
+        0xFAA9,
+        0xFAAA,
+        0xFAAB,
+        0xFAAC,
+        0xFAAD,
+        0xFAAE,
+        0xFAAF,
+        0xFAB0,
+        0xFAB1,
+        0xFAB2,
+        0xFAB3,
+        0xFAB4,
+        0xFAB5,
+        0xFAB6,
+        0xFAB7,
+        0xFAB8,
+        0xFAB9,
+        0xFABA,
+        0xFABB,
+        0xFABC,
+        0xFABD,
+        0xFABE,
+        0xFABF,
+        0xFAC0,
+        0xFAC1,
+        0xFAC2,
+        0xFAC3,
+        0xFAC4,
+        0xFAC5,
+        0xFAC6,
+        0xFAC7,
+        0xFAC8,
+        0xFAC9,
+        0xFACA,
+        0xFACB,
+        0xFACC,
+        0xFACD,
+        0xFACE,
+        0xFACF,
+        0xFAD0,
+        0xFAD1,
+        0xFAD2,
+        0xFAD3,
+        0xFAD4,
+        0xFAD5,
+        0xFAD6,
+        0xFAD7,
+        0xFAD8,
+        0xFAD9,
+        0xFADA,
+        0xFB00,
+        0xFB01,
+        0xFB02,
+        0xFB03,
+        0xFB04,
+        0xFB05,
+        0xFB07,
+        0xFB13,
+        0xFB14,
+        0xFB15,
+        0xFB16,
+        0xFB17,
+        0xFB18,
+        0xFB1D,
+        0xFB1E,
+        0xFB1F,
+        0xFB20,
+        0xFB21,
+        0xFB22,
+        0xFB23,
+        0xFB24,
+        0xFB25,
+        0xFB26,
+        0xFB27,
+        0xFB28,
+        0xFB29,
+        0xFB2A,
+        0xFB2B,
+        0xFB2C,
+        0xFB2D,
+        0xFB2E,
+        0xFB2F,
+        0xFB30,
+        0xFB31,
+        0xFB32,
+        0xFB33,
+        0xFB34,
+        0xFB35,
+        0xFB36,
+        0xFB37,
+        0xFB38,
+        0xFB39,
+        0xFB3A,
+        0xFB3B,
+        0xFB3C,
+        0xFB3D,
+        0xFB3E,
+        0xFB3F,
+        0xFB40,
+        0xFB41,
+        0xFB42,
+        0xFB43,
+        0xFB44,
+        0xFB45,
+        0xFB46,
+        0xFB47,
+        0xFB48,
+        0xFB49,
+        0xFB4A,
+        0xFB4B,
+        0xFB4C,
+        0xFB4D,
+        0xFB4E,
+        0xFB4F,
+        0xFB50,
+        0xFB52,
+        0xFB56,
+        0xFB5A,
+        0xFB5E,
+        0xFB62,
+        0xFB66,
+        0xFB6A,
+        0xFB6E,
+        0xFB72,
+        0xFB76,
+        0xFB7A,
+        0xFB7E,
+        0xFB82,
+        0xFB84,
+        0xFB86,
+        0xFB88,
+        0xFB8A,
+        0xFB8C,
+        0xFB8E,
+        0xFB92,
+        0xFB96,
+        0xFB9A,
+        0xFB9E,
+        0xFBA0,
+        0xFBA4,
+        0xFBA6,
+        0xFBAA,
+        0xFBAE,
+        0xFBB0,
+        0xFBB2,
+        0xFBD3,
+        0xFBD7,
+        0xFBD9,
+        0xFBDB,
+        0xFBDD,
+        0xFBDE,
+        0xFBE0,
+        0xFBE2,
+        0xFBE4,
+        0xFBE8,
+        0xFBEA,
+        0xFBEC,
+        0xFBEE,
+        0xFBF0,
+        0xFBF2,
+        0xFBF4,
+        0xFBF6,
+        0xFBF9,
+        0xFBFC,
+        0xFC00,
+        0xFC01,
+        0xFC02,
+        0xFC03,
+        0xFC04,
+        0xFC05,
+        0xFC06,
+        0xFC07,
+        0xFC08,
+        0xFC09,
+        0xFC0A,
+        0xFC0B,
+        0xFC0C,
+        0xFC0D,
+        0xFC0E,
+        0xFC0F,
+        0xFC10,
+        0xFC11,
+        0xFC12,
+        0xFC13,
+        0xFC14,
+        0xFC15,
+        0xFC16,
+        0xFC17,
+        0xFC18,
+        0xFC19,
+        0xFC1A,
+        0xFC1B,
+        0xFC1C,
+        0xFC1D,
+        0xFC1E,
+        0xFC1F,
+        0xFC20,
+        0xFC21,
+        0xFC22,
+        0xFC23,
+        0xFC24,
+        0xFC25,
+        0xFC26,
+        0xFC27,
+        0xFC28,
+        0xFC29,
+        0xFC2A,
+        0xFC2B,
+        0xFC2C,
+        0xFC2D,
+        0xFC2E,
+        0xFC2F,
+        0xFC30,
+        0xFC31,
+        0xFC32,
+        0xFC33,
+        0xFC34,
+        0xFC35,
+        0xFC36,
+        0xFC37,
+        0xFC38,
+        0xFC39,
+        0xFC3A,
+        0xFC3B,
+        0xFC3C,
+        0xFC3D,
+        0xFC3E,
+        0xFC3F,
+        0xFC40,
+        0xFC41,
+        0xFC42,
+        0xFC43,
+        0xFC44,
+        0xFC45,
+        0xFC46,
+        0xFC47,
+        0xFC48,
+        0xFC49,
+        0xFC4A,
+        0xFC4B,
+        0xFC4C,
+        0xFC4D,
+        0xFC4E,
+        0xFC4F,
+        0xFC50,
+        0xFC51,
+        0xFC52,
+        0xFC53,
+        0xFC54,
+        0xFC55,
+        0xFC56,
+        0xFC57,
+        0xFC58,
+        0xFC59,
+        0xFC5A,
+        0xFC5B,
+        0xFC5C,
+        0xFC5D,
+        0xFC5E,
+        0xFC5F,
+        0xFC60,
+        0xFC61,
+        0xFC62,
+        0xFC63,
+        0xFC64,
+        0xFC65,
+        0xFC66,
+        0xFC67,
+        0xFC68,
+        0xFC69,
+        0xFC6A,
+        0xFC6B,
+        0xFC6C,
+        0xFC6D,
+        0xFC6E,
+        0xFC6F,
+        0xFC70,
+        0xFC71,
+        0xFC72,
+        0xFC73,
+        0xFC74,
+        0xFC75,
+        0xFC76,
+        0xFC77,
+        0xFC78,
+        0xFC79,
+        0xFC7A,
+        0xFC7B,
+        0xFC7C,
+        0xFC7D,
+        0xFC7E,
+        0xFC7F,
+        0xFC80,
+        0xFC81,
+        0xFC82,
+        0xFC83,
+        0xFC84,
+        0xFC85,
+        0xFC86,
+        0xFC87,
+        0xFC88,
+        0xFC89,
+        0xFC8A,
+        0xFC8B,
+        0xFC8C,
+        0xFC8D,
+        0xFC8E,
+        0xFC8F,
+        0xFC90,
+        0xFC91,
+        0xFC92,
+        0xFC93,
+        0xFC94,
+        0xFC95,
+        0xFC96,
+        0xFC97,
+        0xFC98,
+        0xFC99,
+        0xFC9A,
+        0xFC9B,
+        0xFC9C,
+        0xFC9D,
+        0xFC9E,
+        0xFC9F,
+        0xFCA0,
+        0xFCA1,
+        0xFCA2,
+        0xFCA3,
+        0xFCA4,
+        0xFCA5,
+        0xFCA6,
+        0xFCA7,
+        0xFCA8,
+        0xFCA9,
+        0xFCAA,
+        0xFCAB,
+        0xFCAC,
+        0xFCAD,
+        0xFCAE,
+        0xFCAF,
+        0xFCB0,
+        0xFCB1,
+        0xFCB2,
+        0xFCB3,
+        0xFCB4,
+        0xFCB5,
+        0xFCB6,
+        0xFCB7,
+        0xFCB8,
+        0xFCB9,
+        0xFCBA,
+        0xFCBB,
+        0xFCBC,
+        0xFCBD,
+        0xFCBE,
+        0xFCBF,
+        0xFCC0,
+        0xFCC1,
+        0xFCC2,
+        0xFCC3,
+        0xFCC4,
+        0xFCC5,
+        0xFCC6,
+        0xFCC7,
+        0xFCC8,
+        0xFCC9,
+        0xFCCA,
+        0xFCCB,
+        0xFCCC,
+        0xFCCD,
+        0xFCCE,
+        0xFCCF,
+        0xFCD0,
+        0xFCD1,
+        0xFCD2,
+        0xFCD3,
+        0xFCD4,
+        0xFCD5,
+        0xFCD6,
+        0xFCD7,
+        0xFCD8,
+        0xFCD9,
+        0xFCDA,
+        0xFCDB,
+        0xFCDC,
+        0xFCDD,
+        0xFCDE,
+        0xFCDF,
+        0xFCE0,
+        0xFCE1,
+        0xFCE2,
+        0xFCE3,
+        0xFCE4,
+        0xFCE5,
+        0xFCE6,
+        0xFCE7,
+        0xFCE8,
+        0xFCE9,
+        0xFCEA,
+        0xFCEB,
+        0xFCEC,
+        0xFCED,
+        0xFCEE,
+        0xFCEF,
+        0xFCF0,
+        0xFCF1,
+        0xFCF2,
+        0xFCF3,
+        0xFCF4,
+        0xFCF5,
+        0xFCF6,
+        0xFCF7,
+        0xFCF8,
+        0xFCF9,
+        0xFCFA,
+        0xFCFB,
+        0xFCFC,
+        0xFCFD,
+        0xFCFE,
+        0xFCFF,
+        0xFD00,
+        0xFD01,
+        0xFD02,
+        0xFD03,
+        0xFD04,
+        0xFD05,
+        0xFD06,
+        0xFD07,
+        0xFD08,
+        0xFD09,
+        0xFD0A,
+        0xFD0B,
+        0xFD0C,
+        0xFD0D,
+        0xFD0E,
+        0xFD0F,
+        0xFD10,
+        0xFD11,
+        0xFD12,
+        0xFD13,
+        0xFD14,
+        0xFD15,
+        0xFD16,
+        0xFD17,
+        0xFD18,
+        0xFD19,
+        0xFD1A,
+        0xFD1B,
+        0xFD1C,
+        0xFD1D,
+        0xFD1E,
+        0xFD1F,
+        0xFD20,
+        0xFD21,
+        0xFD22,
+        0xFD23,
+        0xFD24,
+        0xFD25,
+        0xFD26,
+        0xFD27,
+        0xFD28,
+        0xFD29,
+        0xFD2A,
+        0xFD2B,
+        0xFD2C,
+        0xFD2D,
+        0xFD2E,
+        0xFD2F,
+        0xFD30,
+        0xFD31,
+        0xFD32,
+        0xFD33,
+        0xFD34,
+        0xFD35,
+        0xFD36,
+        0xFD37,
+        0xFD38,
+        0xFD39,
+        0xFD3A,
+        0xFD3B,
+        0xFD3C,
+        0xFD3E,
+        0xFD50,
+        0xFD51,
+        0xFD53,
+        0xFD54,
+        0xFD55,
+        0xFD56,
+        0xFD57,
+        0xFD58,
+        0xFD5A,
+        0xFD5B,
+        0xFD5C,
+        0xFD5D,
+        0xFD5E,
+        0xFD5F,
+        0xFD61,
+        0xFD62,
+        0xFD64,
+        0xFD66,
+        0xFD67,
+        0xFD69,
+        0xFD6A,
+        0xFD6C,
+        0xFD6E,
+        0xFD6F,
+        0xFD71,
+        0xFD73,
+        0xFD74,
+        0xFD75,
+        0xFD76,
+        0xFD78,
+        0xFD79,
+        0xFD7A,
+        0xFD7B,
+        0xFD7C,
+        0xFD7E,
+        0xFD7F,
+        0xFD80,
+        0xFD81,
+        0xFD82,
+        0xFD83,
+        0xFD85,
+        0xFD87,
+        0xFD89,
+        0xFD8A,
+        0xFD8B,
+        0xFD8C,
+        0xFD8D,
+        0xFD8E,
+        0xFD8F,
+        0xFD90,
+        0xFD92,
+        0xFD93,
+        0xFD94,
+        0xFD95,
+        0xFD96,
+        0xFD97,
+        0xFD99,
+        0xFD9A,
+        0xFD9B,
+        0xFD9C,
+        0xFD9E,
+        0xFD9F,
+        0xFDA0,
+        0xFDA1,
+        0xFDA2,
+        0xFDA3,
+        0xFDA4,
+        0xFDA5,
+        0xFDA6,
+        0xFDA7,
+        0xFDA8,
+        0xFDA9,
+        0xFDAA,
+        0xFDAB,
+        0xFDAC,
+        0xFDAD,
+        0xFDAE,
+        0xFDAF,
+        0xFDB0,
+        0xFDB1,
+        0xFDB2,
+        0xFDB3,
+        0xFDB4,
+        0xFDB5,
+        0xFDB6,
+        0xFDB7,
+        0xFDB8,
+        0xFDB9,
+        0xFDBA,
+        0xFDBB,
+        0xFDBC,
+        0xFDBD,
+        0xFDBE,
+        0xFDBF,
+        0xFDC0,
+        0xFDC1,
+        0xFDC2,
+        0xFDC3,
+        0xFDC4,
+        0xFDC5,
+        0xFDC6,
+        0xFDC7,
+        0xFDC8,
+        0xFDD0,
+        0xFDF0,
+        0xFDF1,
+        0xFDF2,
+        0xFDF3,
+        0xFDF4,
+        0xFDF5,
+        0xFDF6,
+        0xFDF7,
+        0xFDF8,
+        0xFDF9,
+        0xFDFA,
+        0xFDFB,
+        0xFDFC,
+        0xFDFD,
+        0xFE00,
+        0xFE10,
+        0xFE11,
+        0xFE12,
+        0xFE13,
+        0xFE14,
+        0xFE15,
+        0xFE16,
+        0xFE17,
+        0xFE18,
+        0xFE19,
+        0xFE20,
+        0xFE30,
+        0xFE31,
+        0xFE32,
+        0xFE33,
+        0xFE35,
+        0xFE36,
+        0xFE37,
+        0xFE38,
+        0xFE39,
+        0xFE3A,
+        0xFE3B,
+        0xFE3C,
+        0xFE3D,
+        0xFE3E,
+        0xFE3F,
+        0xFE40,
+        0xFE41,
+        0xFE42,
+        0xFE43,
+        0xFE44,
+        0xFE45,
+        0xFE47,
+        0xFE48,
+        0xFE49,
+        0xFE4D,
+        0xFE50,
+        0xFE51,
+        0xFE52,
+        0xFE54,
+        0xFE55,
+        0xFE56,
+        0xFE57,
+        0xFE58,
+        0xFE59,
+        0xFE5A,
+        0xFE5B,
+        0xFE5C,
+        0xFE5D,
+        0xFE5E,
+        0xFE5F,
+        0xFE60,
+        0xFE61,
+        0xFE62,
+        0xFE63,
+        0xFE64,
+        0xFE65,
+        0xFE66,
+        0xFE67,
+        0xFE68,
+        0xFE69,
+        0xFE6A,
+        0xFE6B,
+        0xFE6C,
+        0xFE70,
+        0xFE71,
+        0xFE72,
+        0xFE73,
+        0xFE74,
+        0xFE75,
+        0xFE76,
+        0xFE77,
+        0xFE78,
+        0xFE79,
+        0xFE7A,
+        0xFE7B,
+        0xFE7C,
+        0xFE7D,
+        0xFE7E,
+        0xFE7F,
+        0xFE80,
+        0xFE81,
+        0xFE83,
+        0xFE85,
+        0xFE87,
+        0xFE89,
+        0xFE8D,
+        0xFE8F,
+        0xFE93,
+        0xFE95,
+        0xFE99,
+        0xFE9D,
+        0xFEA1,
+        0xFEA5,
+        0xFEA9,
+        0xFEAB,
+        0xFEAD,
+        0xFEAF,
+        0xFEB1,
+        0xFEB5,
+        0xFEB9,
+        0xFEBD,
+        0xFEC1,
+        0xFEC5,
+        0xFEC9,
+        0xFECD,
+        0xFED1,
+        0xFED5,
+        0xFED9,
+        0xFEDD,
+        0xFEE1,
+        0xFEE5,
+        0xFEE9,
+        0xFEED,
+        0xFEEF,
+        0xFEF1,
+        0xFEF5,
+        0xFEF7,
+        0xFEF9,
+        0xFEFB,
+        0xFEFD,
+        0xFEFF,
+        0xFF00,
+        0xFF01,
+        0xFF02,
+        0xFF03,
+        0xFF04,
+        0xFF05,
+        0xFF06,
+        0xFF07,
+        0xFF08,
+        0xFF09,
+        0xFF0A,
+        0xFF0B,
+        0xFF0C,
+        0xFF0D,
+        0xFF0E,
+        0xFF0F,
+        0xFF10,
+        0xFF11,
+        0xFF12,
+        0xFF13,
+        0xFF14,
+        0xFF15,
+        0xFF16,
+        0xFF17,
+        0xFF18,
+        0xFF19,
+        0xFF1A,
+        0xFF1B,
+        0xFF1C,
+        0xFF1D,
+        0xFF1E,
+        0xFF1F,
+        0xFF20,
+        0xFF21,
+        0xFF22,
+        0xFF23,
+        0xFF24,
+        0xFF25,
+        0xFF26,
+        0xFF27,
+        0xFF28,
+        0xFF29,
+        0xFF2A,
+        0xFF2B,
+        0xFF2C,
+        0xFF2D,
+        0xFF2E,
+        0xFF2F,
+        0xFF30,
+        0xFF31,
+        0xFF32,
+        0xFF33,
+        0xFF34,
+        0xFF35,
+        0xFF36,
+        0xFF37,
+        0xFF38,
+        0xFF39,
+        0xFF3A,
+        0xFF3B,
+        0xFF3C,
+        0xFF3D,
+        0xFF3E,
+        0xFF3F,
+        0xFF40,
+        0xFF41,
+        0xFF42,
+        0xFF43,
+        0xFF44,
+        0xFF45,
+        0xFF46,
+        0xFF47,
+        0xFF48,
+        0xFF49,
+        0xFF4A,
+        0xFF4B,
+        0xFF4C,
+        0xFF4D,
+        0xFF4E,
+        0xFF4F,
+        0xFF50,
+        0xFF51,
+        0xFF52,
+        0xFF53,
+        0xFF54,
+        0xFF55,
+        0xFF56,
+        0xFF57,
+        0xFF58,
+        0xFF59,
+        0xFF5A,
+        0xFF5B,
+        0xFF5C,
+        0xFF5D,
+        0xFF5E,
+        0xFF5F,
+        0xFF60,
+        0xFF61,
+        0xFF62,
+        0xFF63,
+        0xFF64,
+        0xFF65,
+        0xFF66,
+        0xFF67,
+        0xFF68,
+        0xFF69,
+        0xFF6A,
+        0xFF6B,
+        0xFF6C,
+        0xFF6D,
+        0xFF6E,
+        0xFF6F,
+        0xFF70,
+        0xFF71,
+        0xFF72,
+        0xFF73,
+        0xFF74,
+        0xFF75,
+        0xFF76,
+        0xFF77,
+        0xFF78,
+        0xFF79,
+        0xFF7A,
+        0xFF7B,
+        0xFF7C,
+        0xFF7D,
+        0xFF7E,
+        0xFF7F,
+        0xFF80,
+        0xFF81,
+        0xFF82,
+        0xFF83,
+        0xFF84,
+        0xFF85,
+        0xFF86,
+        0xFF87,
+        0xFF88,
+        0xFF89,
+        0xFF8A,
+        0xFF8B,
+        0xFF8C,
+        0xFF8D,
+        0xFF8E,
+        0xFF8F,
+        0xFF90,
+        0xFF91,
+        0xFF92,
+        0xFF93,
+        0xFF94,
+        0xFF95,
+        0xFF96,
+        0xFF97,
+        0xFF98,
+        0xFF99,
+        0xFF9A,
+        0xFF9B,
+        0xFF9C,
+        0xFF9D,
+        0xFF9E,
+        0xFF9F,
+        0xFFA0,
+        0xFFA1,
+        0xFFA2,
+        0xFFA3,
+        0xFFA4,
+        0xFFA5,
+        0xFFA6,
+        0xFFA7,
+        0xFFA8,
+        0xFFA9,
+        0xFFAA,
+        0xFFAB,
+        0xFFAC,
+        0xFFAD,
+        0xFFAE,
+        0xFFAF,
+        0xFFB0,
+        0xFFB1,
+        0xFFB2,
+        0xFFB3,
+        0xFFB4,
+        0xFFB5,
+        0xFFB6,
+        0xFFB7,
+        0xFFB8,
+        0xFFB9,
+        0xFFBA,
+        0xFFBB,
+        0xFFBC,
+        0xFFBD,
+        0xFFBE,
+        0xFFBF,
+        0xFFC2,
+        0xFFC3,
+        0xFFC4,
+        0xFFC5,
+        0xFFC6,
+        0xFFC7,
+        0xFFC8,
+        0xFFCA,
+        0xFFCB,
+        0xFFCC,
+        0xFFCD,
+        0xFFCE,
+        0xFFCF,
+        0xFFD0,
+        0xFFD2,
+        0xFFD3,
+        0xFFD4,
+        0xFFD5,
+        0xFFD6,
+        0xFFD7,
+        0xFFD8,
+        0xFFDA,
+        0xFFDB,
+        0xFFDC,
+        0xFFDD,
+        0xFFE0,
+        0xFFE1,
+        0xFFE2,
+        0xFFE3,
+        0xFFE4,
+        0xFFE5,
+        0xFFE6,
+        0xFFE7,
+        0xFFE8,
+        0xFFE9,
+        0xFFEA,
+        0xFFEB,
+        0xFFEC,
+        0xFFED,
+        0xFFEE,
+        0xFFEF,
+        0x10000,
+        0x1000C,
+        0x1000D,
+        0x10027,
+        0x10028,
+        0x1003B,
+        0x1003C,
+        0x1003E,
+        0x1003F,
+        0x1004E,
+        0x10050,
+        0x1005E,
+        0x10080,
+        0x100FB,
+        0x10100,
+        0x10103,
+        0x10107,
+        0x10134,
+        0x10137,
+        0x1018F,
+        0x10190,
+        0x1019D,
+        0x101A0,
+        0x101A1,
+        0x101D0,
+        0x101FE,
+        0x10280,
+        0x1029D,
+        0x102A0,
+        0x102D1,
+        0x102E0,
+        0x102FC,
+        0x10300,
+        0x10324,
+        0x1032D,
+        0x1034B,
+        0x10350,
+        0x1037B,
+        0x10380,
+        0x1039E,
+        0x1039F,
+        0x103C4,
+        0x103C8,
+        0x103D6,
+        0x10400,
+        0x10401,
+        0x10402,
+        0x10403,
+        0x10404,
+        0x10405,
+        0x10406,
+        0x10407,
+        0x10408,
+        0x10409,
+        0x1040A,
+        0x1040B,
+        0x1040C,
+        0x1040D,
+        0x1040E,
+        0x1040F,
+        0x10410,
+        0x10411,
+        0x10412,
+        0x10413,
+        0x10414,
+        0x10415,
+        0x10416,
+        0x10417,
+        0x10418,
+        0x10419,
+        0x1041A,
+        0x1041B,
+        0x1041C,
+        0x1041D,
+        0x1041E,
+        0x1041F,
+        0x10420,
+        0x10421,
+        0x10422,
+        0x10423,
+        0x10424,
+        0x10425,
+        0x10426,
+        0x10427,
+        0x10428,
+        0x1049E,
+        0x104A0,
+        0x104AA,
+        0x104B0,
+        0x104B1,
+        0x104B2,
+        0x104B3,
+        0x104B4,
+        0x104B5,
+        0x104B6,
+        0x104B7,
+        0x104B8,
+        0x104B9,
+        0x104BA,
+        0x104BB,
+        0x104BC,
+        0x104BD,
+        0x104BE,
+        0x104BF,
+        0x104C0,
+        0x104C1,
+        0x104C2,
+        0x104C3,
+        0x104C4,
+        0x104C5,
+        0x104C6,
+        0x104C7,
+        0x104C8,
+        0x104C9,
+        0x104CA,
+        0x104CB,
+        0x104CC,
+        0x104CD,
+        0x104CE,
+        0x104CF,
+        0x104D0,
+        0x104D1,
+        0x104D2,
+        0x104D3,
+        0x104D4,
+        0x104D8,
+        0x104FC,
+        0x10500,
+        0x10528,
+        0x10530,
+        0x10564,
+        0x1056F,
+        0x10570,
+        0x10571,
+        0x10572,
+        0x10573,
+        0x10574,
+        0x10575,
+        0x10576,
+        0x10577,
+        0x10578,
+        0x10579,
+        0x1057A,
+        0x1057B,
+        0x1057C,
+        0x1057D,
+        0x1057E,
+        0x1057F,
+        0x10580,
+        0x10581,
+        0x10582,
+        0x10583,
+        0x10584,
+        0x10585,
+        0x10586,
+        0x10587,
+        0x10588,
+        0x10589,
+        0x1058A,
+        0x1058B,
+        0x1058C,
+        0x1058D,
+        0x1058E,
+        0x1058F,
+        0x10590,
+        0x10591,
+        0x10592,
+        0x10593,
+        0x10594,
+        0x10595,
+        0x10596,
+        0x10597,
+        0x105A2,
+        0x105A3,
+        0x105B2,
+        0x105B3,
+        0x105BA,
+        0x105BB,
+        0x105BD,
+        0x105C0,
+        0x105F4,
+        0x10600,
+        0x10737,
+        0x10740,
+        0x10756,
+        0x10760,
+        0x10768,
+        0x10780,
+        0x10781,
+        0x10782,
+        0x10783,
+        0x10784,
+        0x10785,
+        0x10786,
+        0x10787,
+        0x10788,
+        0x10789,
+        0x1078A,
+        0x1078B,
+        0x1078C,
+        0x1078D,
+        0x1078E,
+        0x1078F,
+        0x10790,
+        0x10791,
+        0x10792,
+        0x10793,
+        0x10794,
+        0x10795,
+        0x10796,
+        0x10797,
+        0x10798,
+        0x10799,
+        0x1079A,
+        0x1079B,
+        0x1079C,
+        0x1079D,
+        0x1079E,
+        0x1079F,
+        0x107A0,
+        0x107A1,
+        0x107A2,
+        0x107A3,
+        0x107A4,
+        0x107A5,
+        0x107A6,
+        0x107A7,
+        0x107A8,
+        0x107A9,
+        0x107AA,
+        0x107AB,
+        0x107AC,
+        0x107AD,
+        0x107AE,
+        0x107AF,
+        0x107B0,
+        0x107B1,
+        0x107B2,
+        0x107B3,
+        0x107B4,
+        0x107B5,
+        0x107B6,
+        0x107B7,
+        0x107B8,
+        0x107B9,
+        0x107BA,
+        0x107BB,
+        0x10800,
+        0x10806,
+        0x10808,
+        0x10809,
+        0x1080A,
+        0x10836,
+        0x10837,
+        0x10839,
+        0x1083C,
+        0x1083D,
+        0x1083F,
+        0x10856,
+        0x10857,
+        0x1089F,
+        0x108A7,
+        0x108B0,
+        0x108E0,
+        0x108F3,
+        0x108F4,
+        0x108F6,
+        0x108FB,
+        0x1091C,
+        0x1091F,
+        0x1093A,
+        0x1093F,
+        0x1095A,
+        0x10980,
+        0x109B8,
+        0x109BC,
+        0x109D0,
+        0x109D2,
+        0x10A04,
+        0x10A05,
+        0x10A07,
+        0x10A0C,
+        0x10A14,
+        0x10A15,
+        0x10A18,
+        0x10A19,
+        0x10A36,
+        0x10A38,
+        0x10A3B,
+        0x10A3F,
+        0x10A49,
+        0x10A50,
+        0x10A59,
+        0x10A60,
+        0x10AA0,
+        0x10AC0,
+        0x10AE7,
+        0x10AEB,
+        0x10AF7,
+        0x10B00,
+        0x10B36,
+        0x10B39,
+        0x10B56,
+        0x10B58,
+        0x10B73,
+        0x10B78,
+        0x10B92,
+        0x10B99,
+        0x10B9D,
+        0x10BA9,
+        0x10BB0,
+        0x10C00,
+        0x10C49,
+        0x10C80,
+        0x10C81,
+        0x10C82,
+        0x10C83,
+        0x10C84,
+        0x10C85,
+        0x10C86,
+        0x10C87,
+        0x10C88,
+        0x10C89,
+        0x10C8A,
+        0x10C8B,
+        0x10C8C,
+        0x10C8D,
+        0x10C8E,
+        0x10C8F,
+        0x10C90,
+        0x10C91,
+        0x10C92,
+        0x10C93,
+        0x10C94,
+        0x10C95,
+        0x10C96,
+        0x10C97,
+        0x10C98,
+        0x10C99,
+        0x10C9A,
+        0x10C9B,
+        0x10C9C,
+        0x10C9D,
+        0x10C9E,
+        0x10C9F,
+        0x10CA0,
+        0x10CA1,
+        0x10CA2,
+        0x10CA3,
+        0x10CA4,
+        0x10CA5,
+        0x10CA6,
+        0x10CA7,
+        0x10CA8,
+        0x10CA9,
+        0x10CAA,
+        0x10CAB,
+        0x10CAC,
+        0x10CAD,
+        0x10CAE,
+        0x10CAF,
+        0x10CB0,
+        0x10CB1,
+        0x10CB2,
+        0x10CB3,
+        0x10CC0,
+        0x10CF3,
+        0x10CFA,
+        0x10D28,
+        0x10D30,
+        0x10D3A,
+        0x10D40,
+        0x10D50,
+        0x10D51,
+        0x10D52,
+        0x10D53,
+        0x10D54,
+        0x10D55,
+        0x10D56,
+        0x10D57,
+        0x10D58,
+        0x10D59,
+        0x10D5A,
+        0x10D5B,
+        0x10D5C,
+        0x10D5D,
+        0x10D5E,
+        0x10D5F,
+        0x10D60,
+        0x10D61,
+        0x10D62,
+        0x10D63,
+        0x10D64,
+        0x10D65,
+        0x10D66,
+        0x10D69,
+        0x10D86,
+        0x10D8E,
+        0x10D90,
+        0x10E60,
+        0x10E7F,
+        0x10E80,
+        0x10EAA,
+        0x10EAB,
+        0x10EAE,
+        0x10EB0,
+        0x10EB2,
+        0x10EC2,
+        0x10EC8,
+        0x10ED0,
+        0x10ED9,
+        0x10EFA,
+        0x10F28,
+        0x10F30,
+        0x10F5A,
+        0x10F70,
+        0x10F8A,
+        0x10FB0,
+        0x10FCC,
+        0x10FE0,
+        0x10FF7,
+        0x11000,
+        0x1104E,
+        0x11052,
+        0x11076,
+        0x1107F,
+        0x110BD,
+        0x110BE,
+        0x110C3,
+        0x110D0,
+        0x110E9,
+        0x110F0,
+        0x110FA,
+        0x11100,
+        0x11135,
+        0x11136,
+        0x11148,
+        0x11150,
+        0x11177,
+        0x11180,
+        0x111E0,
+        0x111E1,
+        0x111F5,
+        0x11200,
+        0x11212,
+        0x11213,
+        0x11242,
+        0x11280,
+        0x11287,
+        0x11288,
+        0x11289,
+        0x1128A,
+        0x1128E,
+        0x1128F,
+        0x1129E,
+        0x1129F,
+        0x112AA,
+        0x112B0,
+        0x112EB,
+        0x112F0,
+        0x112FA,
+        0x11300,
+        0x11304,
+        0x11305,
+        0x1130D,
+        0x1130F,
+        0x11311,
+        0x11313,
+        0x11329,
+        0x1132A,
+        0x11331,
+        0x11332,
+        0x11334,
+        0x11335,
+        0x1133A,
+        0x1133B,
+        0x11345,
+        0x11347,
+        0x11349,
+        0x1134B,
+        0x1134E,
+        0x11350,
+        0x11351,
+        0x11357,
+        0x11358,
+        0x1135D,
+        0x11364,
+        0x11366,
+        0x1136D,
+        0x11370,
+        0x11375,
+        0x11380,
+        0x1138A,
+        0x1138B,
+        0x1138C,
+        0x1138E,
+        0x1138F,
+        0x11390,
+        0x113B6,
+        0x113B7,
+        0x113C1,
+        0x113C2,
+        0x113C3,
+        0x113C5,
+        0x113C6,
+        0x113C7,
+        0x113CB,
+        0x113CC,
+        0x113D6,
+        0x113D7,
+        0x113D9,
+        0x113E1,
+        0x113E3,
+        0x11400,
+        0x1145C,
+        0x1145D,
+        0x11462,
+        0x11480,
+        0x114C8,
+        0x114D0,
+        0x114DA,
+        0x11580,
+        0x115B6,
+        0x115B8,
+        0x115DE,
+        0x11600,
+        0x11645,
+        0x11650,
+        0x1165A,
+        0x11660,
+        0x1166D,
+        0x11680,
+        0x116BA,
+        0x116C0,
+        0x116CA,
+        0x116D0,
+        0x116E4,
+        0x11700,
+        0x1171B,
+        0x1171D,
+        0x1172C,
+        0x11730,
+        0x11747,
+        0x11800,
+        0x1183C,
+        0x118A0,
+        0x118A1,
+        0x118A2,
+        0x118A3,
+        0x118A4,
+        0x118A5,
+        0x118A6,
+        0x118A7,
+        0x118A8,
+        0x118A9,
+        0x118AA,
+        0x118AB,
+        0x118AC,
+        0x118AD,
+        0x118AE,
+        0x118AF,
+        0x118B0,
+        0x118B1,
+        0x118B2,
+        0x118B3,
+        0x118B4,
+        0x118B5,
+        0x118B6,
+        0x118B7,
+        0x118B8,
+        0x118B9,
+        0x118BA,
+        0x118BB,
+        0x118BC,
+        0x118BD,
+        0x118BE,
+        0x118BF,
+        0x118C0,
+        0x118F3,
+        0x118FF,
+        0x11907,
+        0x11909,
+        0x1190A,
+        0x1190C,
+        0x11914,
+        0x11915,
+        0x11917,
+        0x11918,
+        0x11936,
+        0x11937,
+        0x11939,
+        0x1193B,
+        0x11947,
+        0x11950,
+        0x1195A,
+        0x119A0,
+        0x119A8,
+        0x119AA,
+        0x119D8,
+        0x119DA,
+        0x119E5,
+        0x11A00,
+        0x11A48,
+        0x11A50,
+        0x11AA3,
+        0x11AB0,
+        0x11AF9,
+        0x11B00,
+        0x11B0A,
+        0x11B60,
+        0x11B68,
+        0x11BC0,
+        0x11BE2,
+        0x11BF0,
+        0x11BFA,
+        0x11C00,
+        0x11C09,
+        0x11C0A,
+        0x11C37,
+        0x11C38,
+        0x11C46,
+        0x11C50,
+        0x11C6D,
+        0x11C70,
+        0x11C90,
+        0x11C92,
+        0x11CA8,
+        0x11CA9,
+        0x11CB7,
+        0x11D00,
+        0x11D07,
+        0x11D08,
+        0x11D0A,
+        0x11D0B,
+        0x11D37,
+        0x11D3A,
+        0x11D3B,
+        0x11D3C,
+        0x11D3E,
+        0x11D3F,
+        0x11D48,
+        0x11D50,
+        0x11D5A,
+        0x11D60,
+        0x11D66,
+        0x11D67,
+        0x11D69,
+        0x11D6A,
+        0x11D8F,
+        0x11D90,
+        0x11D92,
+        0x11D93,
+        0x11D99,
+        0x11DA0,
+        0x11DAA,
+        0x11DB0,
+        0x11DDC,
+        0x11DE0,
+        0x11DEA,
+        0x11EE0,
+        0x11EF9,
+        0x11F00,
+        0x11F11,
+        0x11F12,
+        0x11F3B,
+        0x11F3E,
+        0x11F5B,
+        0x11FB0,
+        0x11FB1,
+        0x11FC0,
+        0x11FF2,
+        0x11FFF,
+        0x1239A,
+        0x12400,
+        0x1246F,
+        0x12470,
+        0x12475,
+        0x12480,
+        0x12544,
+        0x12F90,
+        0x12FF3,
+        0x13000,
+        0x13430,
+        0x13440,
+        0x13456,
+        0x13460,
+        0x143FB,
+        0x14400,
+        0x14647,
+        0x16100,
+        0x1613A,
+        0x16800,
+        0x16A39,
+        0x16A40,
+        0x16A5F,
+        0x16A60,
+        0x16A6A,
+        0x16A6E,
+        0x16ABF,
+        0x16AC0,
+        0x16ACA,
+        0x16AD0,
+        0x16AEE,
+        0x16AF0,
+        0x16AF6,
+        0x16B00,
+        0x16B46,
+        0x16B50,
+        0x16B5A,
+        0x16B5B,
+        0x16B62,
+        0x16B63,
+        0x16B78,
+        0x16B7D,
+        0x16B90,
+        0x16D40,
+        0x16D7A,
+        0x16E40,
+        0x16E41,
+        0x16E42,
+        0x16E43,
+        0x16E44,
+        0x16E45,
+        0x16E46,
+        0x16E47,
+        0x16E48,
+        0x16E49,
+        0x16E4A,
+        0x16E4B,
+        0x16E4C,
+        0x16E4D,
+        0x16E4E,
+        0x16E4F,
+        0x16E50,
+        0x16E51,
+        0x16E52,
+        0x16E53,
+        0x16E54,
+        0x16E55,
+        0x16E56,
+        0x16E57,
+        0x16E58,
+        0x16E59,
+        0x16E5A,
+        0x16E5B,
+        0x16E5C,
+        0x16E5D,
+        0x16E5E,
+        0x16E5F,
+        0x16E60,
+        0x16E9B,
+        0x16EA0,
+        0x16EA1,
+        0x16EA2,
+        0x16EA3,
+        0x16EA4,
+        0x16EA5,
+        0x16EA6,
+        0x16EA7,
+        0x16EA8,
+        0x16EA9,
+        0x16EAA,
+        0x16EAB,
+        0x16EAC,
+        0x16EAD,
+        0x16EAE,
+        0x16EAF,
+        0x16EB0,
+        0x16EB1,
+        0x16EB2,
+        0x16EB3,
+        0x16EB4,
+        0x16EB5,
+        0x16EB6,
+        0x16EB7,
+        0x16EB8,
+        0x16EB9,
+        0x16EBB,
+        0x16ED4,
+        0x16F00,
+        0x16F4B,
+        0x16F4F,
+        0x16F88,
+        0x16F8F,
+        0x16FA0,
+        0x16FE0,
+        0x16FE5,
+        0x16FF0,
+        0x16FF7,
+        0x17000,
+        0x18CD6,
+        0x18CFF,
+        0x18D1F,
+        0x18D80,
+        0x18DF3,
+        0x1AFF0,
+        0x1AFF4,
+        0x1AFF5,
+        0x1AFFC,
+        0x1AFFD,
+        0x1AFFF,
+        0x1B000,
+        0x1B123,
+        0x1B132,
+        0x1B133,
+        0x1B150,
+        0x1B153,
+        0x1B155,
+        0x1B156,
+        0x1B164,
+        0x1B168,
+        0x1B170,
+        0x1B2FC,
+        0x1BC00,
+        0x1BC6B,
+        0x1BC70,
+        0x1BC7D,
+        0x1BC80,
+        0x1BC89,
+        0x1BC90,
+        0x1BC9A,
+        0x1BC9C,
+        0x1BCA0,
+        0x1BCA4,
+        0x1CC00,
+        0x1CCD6,
+        0x1CCD7,
+        0x1CCD8,
+        0x1CCD9,
+        0x1CCDA,
+        0x1CCDB,
+        0x1CCDC,
+        0x1CCDD,
+        0x1CCDE,
+        0x1CCDF,
+        0x1CCE0,
+        0x1CCE1,
+        0x1CCE2,
+        0x1CCE3,
+        0x1CCE4,
+        0x1CCE5,
+        0x1CCE6,
+        0x1CCE7,
+        0x1CCE8,
+        0x1CCE9,
+        0x1CCEA,
+        0x1CCEB,
+        0x1CCEC,
+        0x1CCED,
+        0x1CCEE,
+        0x1CCEF,
+        0x1CCF0,
+        0x1CCF1,
+        0x1CCF2,
+        0x1CCF3,
+        0x1CCF4,
+        0x1CCF5,
+        0x1CCF6,
+        0x1CCF7,
+        0x1CCF8,
+        0x1CCF9,
+        0x1CCFA,
+        0x1CCFD,
+        0x1CD00,
+        0x1CEB4,
+        0x1CEBA,
+        0x1CED1,
+        0x1CEE0,
+        0x1CEF1,
+        0x1CF00,
+        0x1CF2E,
+        0x1CF30,
+        0x1CF47,
+        0x1CF50,
+        0x1CFC4,
+        0x1D000,
+        0x1D0F6,
+        0x1D100,
+        0x1D127,
+        0x1D129,
+        0x1D15E,
+        0x1D15F,
+        0x1D160,
+        0x1D161,
+        0x1D162,
+        0x1D163,
+        0x1D164,
+        0x1D165,
+        0x1D173,
+        0x1D17B,
+        0x1D1BB,
+        0x1D1BC,
+        0x1D1BD,
+        0x1D1BE,
+        0x1D1BF,
+        0x1D1C0,
+        0x1D1C1,
+        0x1D1EB,
+        0x1D200,
+        0x1D246,
+        0x1D2C0,
+        0x1D2D4,
+        0x1D2E0,
+        0x1D2F4,
+        0x1D300,
+        0x1D357,
+        0x1D360,
+        0x1D379,
+        0x1D400,
+        0x1D401,
+        0x1D402,
+        0x1D403,
+        0x1D404,
+        0x1D405,
+        0x1D406,
+        0x1D407,
+        0x1D408,
+        0x1D409,
+        0x1D40A,
+        0x1D40B,
+        0x1D40C,
+        0x1D40D,
+        0x1D40E,
+        0x1D40F,
+        0x1D410,
+        0x1D411,
+        0x1D412,
+        0x1D413,
+        0x1D414,
+        0x1D415,
+        0x1D416,
+        0x1D417,
+        0x1D418,
+        0x1D419,
+        0x1D41A,
+        0x1D41B,
+        0x1D41C,
+        0x1D41D,
+        0x1D41E,
+        0x1D41F,
+        0x1D420,
+        0x1D421,
+        0x1D422,
+        0x1D423,
+        0x1D424,
+        0x1D425,
+        0x1D426,
+        0x1D427,
+        0x1D428,
+        0x1D429,
+        0x1D42A,
+        0x1D42B,
+        0x1D42C,
+        0x1D42D,
+        0x1D42E,
+        0x1D42F,
+        0x1D430,
+        0x1D431,
+        0x1D432,
+        0x1D433,
+        0x1D434,
+        0x1D435,
+        0x1D436,
+        0x1D437,
+        0x1D438,
+        0x1D439,
+        0x1D43A,
+        0x1D43B,
+        0x1D43C,
+        0x1D43D,
+        0x1D43E,
+        0x1D43F,
+        0x1D440,
+        0x1D441,
+        0x1D442,
+        0x1D443,
+        0x1D444,
+        0x1D445,
+        0x1D446,
+        0x1D447,
+        0x1D448,
+        0x1D449,
+        0x1D44A,
+        0x1D44B,
+        0x1D44C,
+        0x1D44D,
+        0x1D44E,
+        0x1D44F,
+        0x1D450,
+        0x1D451,
+        0x1D452,
+        0x1D453,
+        0x1D454,
+        0x1D455,
+        0x1D456,
+        0x1D457,
+        0x1D458,
+        0x1D459,
+        0x1D45A,
+        0x1D45B,
+        0x1D45C,
+        0x1D45D,
+        0x1D45E,
+        0x1D45F,
+        0x1D460,
+        0x1D461,
+        0x1D462,
+        0x1D463,
+        0x1D464,
+        0x1D465,
+        0x1D466,
+        0x1D467,
+        0x1D468,
+        0x1D469,
+        0x1D46A,
+        0x1D46B,
+        0x1D46C,
+        0x1D46D,
+        0x1D46E,
+        0x1D46F,
+        0x1D470,
+        0x1D471,
+        0x1D472,
+        0x1D473,
+        0x1D474,
+        0x1D475,
+        0x1D476,
+        0x1D477,
+        0x1D478,
+        0x1D479,
+        0x1D47A,
+        0x1D47B,
+        0x1D47C,
+        0x1D47D,
+        0x1D47E,
+        0x1D47F,
+        0x1D480,
+        0x1D481,
+        0x1D482,
+        0x1D483,
+        0x1D484,
+        0x1D485,
+        0x1D486,
+        0x1D487,
+        0x1D488,
+        0x1D489,
+        0x1D48A,
+        0x1D48B,
+        0x1D48C,
+        0x1D48D,
+        0x1D48E,
+        0x1D48F,
+        0x1D490,
+        0x1D491,
+        0x1D492,
+        0x1D493,
+        0x1D494,
+        0x1D495,
+        0x1D496,
+        0x1D497,
+        0x1D498,
+        0x1D499,
+        0x1D49A,
+        0x1D49B,
+        0x1D49C,
+        0x1D49D,
+        0x1D49E,
+        0x1D49F,
+        0x1D4A0,
+        0x1D4A2,
+        0x1D4A3,
+        0x1D4A5,
+        0x1D4A6,
+        0x1D4A7,
+        0x1D4A9,
+        0x1D4AA,
+        0x1D4AB,
+        0x1D4AC,
+        0x1D4AD,
+        0x1D4AE,
+        0x1D4AF,
+        0x1D4B0,
+        0x1D4B1,
+        0x1D4B2,
+        0x1D4B3,
+        0x1D4B4,
+        0x1D4B5,
+        0x1D4B6,
+        0x1D4B7,
+        0x1D4B8,
+        0x1D4B9,
+        0x1D4BA,
+        0x1D4BB,
+        0x1D4BC,
+        0x1D4BD,
+        0x1D4BE,
+        0x1D4BF,
+        0x1D4C0,
+        0x1D4C1,
+        0x1D4C2,
+        0x1D4C3,
+        0x1D4C4,
+        0x1D4C5,
+        0x1D4C6,
+        0x1D4C7,
+        0x1D4C8,
+        0x1D4C9,
+        0x1D4CA,
+        0x1D4CB,
+        0x1D4CC,
+        0x1D4CD,
+        0x1D4CE,
+        0x1D4CF,
+        0x1D4D0,
+        0x1D4D1,
+        0x1D4D2,
+        0x1D4D3,
+        0x1D4D4,
+        0x1D4D5,
+        0x1D4D6,
+        0x1D4D7,
+        0x1D4D8,
+        0x1D4D9,
+        0x1D4DA,
+        0x1D4DB,
+        0x1D4DC,
+        0x1D4DD,
+        0x1D4DE,
+        0x1D4DF,
+        0x1D4E0,
+        0x1D4E1,
+        0x1D4E2,
+        0x1D4E3,
+        0x1D4E4,
+        0x1D4E5,
+        0x1D4E6,
+        0x1D4E7,
+        0x1D4E8,
+        0x1D4E9,
+        0x1D4EA,
+        0x1D4EB,
+        0x1D4EC,
+        0x1D4ED,
+        0x1D4EE,
+        0x1D4EF,
+        0x1D4F0,
+        0x1D4F1,
+        0x1D4F2,
+        0x1D4F3,
+        0x1D4F4,
+        0x1D4F5,
+        0x1D4F6,
+        0x1D4F7,
+        0x1D4F8,
+        0x1D4F9,
+        0x1D4FA,
+        0x1D4FB,
+        0x1D4FC,
+        0x1D4FD,
+        0x1D4FE,
+        0x1D4FF,
+        0x1D500,
+        0x1D501,
+        0x1D502,
+        0x1D503,
+        0x1D504,
+        0x1D505,
+        0x1D506,
+        0x1D507,
+        0x1D508,
+        0x1D509,
+        0x1D50A,
+        0x1D50B,
+        0x1D50D,
+        0x1D50E,
+        0x1D50F,
+        0x1D510,
+        0x1D511,
+        0x1D512,
+        0x1D513,
+        0x1D514,
+        0x1D515,
+        0x1D516,
+        0x1D517,
+        0x1D518,
+        0x1D519,
+        0x1D51A,
+        0x1D51B,
+        0x1D51C,
+        0x1D51D,
+        0x1D51E,
+        0x1D51F,
+        0x1D520,
+        0x1D521,
+        0x1D522,
+        0x1D523,
+        0x1D524,
+        0x1D525,
+        0x1D526,
+        0x1D527,
+        0x1D528,
+        0x1D529,
+        0x1D52A,
+        0x1D52B,
+        0x1D52C,
+        0x1D52D,
+        0x1D52E,
+        0x1D52F,
+        0x1D530,
+        0x1D531,
+        0x1D532,
+        0x1D533,
+        0x1D534,
+        0x1D535,
+        0x1D536,
+        0x1D537,
+        0x1D538,
+        0x1D539,
+        0x1D53A,
+        0x1D53B,
+        0x1D53C,
+        0x1D53D,
+        0x1D53E,
+        0x1D53F,
+        0x1D540,
+        0x1D541,
+        0x1D542,
+        0x1D543,
+        0x1D544,
+        0x1D545,
+        0x1D546,
+        0x1D547,
+        0x1D54A,
+        0x1D54B,
+        0x1D54C,
+        0x1D54D,
+        0x1D54E,
+        0x1D54F,
+        0x1D550,
+        0x1D551,
+        0x1D552,
+        0x1D553,
+        0x1D554,
+        0x1D555,
+        0x1D556,
+        0x1D557,
+        0x1D558,
+        0x1D559,
+        0x1D55A,
+        0x1D55B,
+        0x1D55C,
+        0x1D55D,
+        0x1D55E,
+        0x1D55F,
+        0x1D560,
+        0x1D561,
+        0x1D562,
+        0x1D563,
+        0x1D564,
+        0x1D565,
+        0x1D566,
+        0x1D567,
+        0x1D568,
+        0x1D569,
+        0x1D56A,
+        0x1D56B,
+        0x1D56C,
+        0x1D56D,
+        0x1D56E,
+        0x1D56F,
+        0x1D570,
+        0x1D571,
+        0x1D572,
+        0x1D573,
+        0x1D574,
+        0x1D575,
+        0x1D576,
+        0x1D577,
+        0x1D578,
+        0x1D579,
+        0x1D57A,
+        0x1D57B,
+        0x1D57C,
+        0x1D57D,
+        0x1D57E,
+        0x1D57F,
+        0x1D580,
+        0x1D581,
+        0x1D582,
+        0x1D583,
+        0x1D584,
+        0x1D585,
+        0x1D586,
+        0x1D587,
+        0x1D588,
+        0x1D589,
+        0x1D58A,
+        0x1D58B,
+        0x1D58C,
+        0x1D58D,
+        0x1D58E,
+        0x1D58F,
+        0x1D590,
+        0x1D591,
+        0x1D592,
+        0x1D593,
+        0x1D594,
+        0x1D595,
+        0x1D596,
+        0x1D597,
+        0x1D598,
+        0x1D599,
+        0x1D59A,
+        0x1D59B,
+        0x1D59C,
+        0x1D59D,
+        0x1D59E,
+        0x1D59F,
+        0x1D5A0,
+        0x1D5A1,
+        0x1D5A2,
+        0x1D5A3,
+        0x1D5A4,
+        0x1D5A5,
+        0x1D5A6,
+        0x1D5A7,
+        0x1D5A8,
+        0x1D5A9,
+        0x1D5AA,
+        0x1D5AB,
+        0x1D5AC,
+        0x1D5AD,
+        0x1D5AE,
+        0x1D5AF,
+        0x1D5B0,
+        0x1D5B1,
+        0x1D5B2,
+        0x1D5B3,
+        0x1D5B4,
+        0x1D5B5,
+        0x1D5B6,
+        0x1D5B7,
+        0x1D5B8,
+        0x1D5B9,
+        0x1D5BA,
+        0x1D5BB,
+        0x1D5BC,
+        0x1D5BD,
+        0x1D5BE,
+        0x1D5BF,
+        0x1D5C0,
+        0x1D5C1,
+        0x1D5C2,
+        0x1D5C3,
+        0x1D5C4,
+        0x1D5C5,
+        0x1D5C6,
+        0x1D5C7,
+        0x1D5C8,
+        0x1D5C9,
+        0x1D5CA,
+        0x1D5CB,
+        0x1D5CC,
+        0x1D5CD,
+        0x1D5CE,
+        0x1D5CF,
+        0x1D5D0,
+        0x1D5D1,
+        0x1D5D2,
+        0x1D5D3,
+        0x1D5D4,
+        0x1D5D5,
+        0x1D5D6,
+        0x1D5D7,
+        0x1D5D8,
+        0x1D5D9,
+        0x1D5DA,
+        0x1D5DB,
+        0x1D5DC,
+        0x1D5DD,
+        0x1D5DE,
+        0x1D5DF,
+        0x1D5E0,
+        0x1D5E1,
+        0x1D5E2,
+        0x1D5E3,
+        0x1D5E4,
+        0x1D5E5,
+        0x1D5E6,
+        0x1D5E7,
+        0x1D5E8,
+        0x1D5E9,
+        0x1D5EA,
+        0x1D5EB,
+        0x1D5EC,
+        0x1D5ED,
+        0x1D5EE,
+        0x1D5EF,
+        0x1D5F0,
+        0x1D5F1,
+        0x1D5F2,
+        0x1D5F3,
+        0x1D5F4,
+        0x1D5F5,
+        0x1D5F6,
+        0x1D5F7,
+        0x1D5F8,
+        0x1D5F9,
+        0x1D5FA,
+        0x1D5FB,
+        0x1D5FC,
+        0x1D5FD,
+        0x1D5FE,
+        0x1D5FF,
+        0x1D600,
+        0x1D601,
+        0x1D602,
+        0x1D603,
+        0x1D604,
+        0x1D605,
+        0x1D606,
+        0x1D607,
+        0x1D608,
+        0x1D609,
+        0x1D60A,
+        0x1D60B,
+        0x1D60C,
+        0x1D60D,
+        0x1D60E,
+        0x1D60F,
+        0x1D610,
+        0x1D611,
+        0x1D612,
+        0x1D613,
+        0x1D614,
+        0x1D615,
+        0x1D616,
+        0x1D617,
+        0x1D618,
+        0x1D619,
+        0x1D61A,
+        0x1D61B,
+        0x1D61C,
+        0x1D61D,
+        0x1D61E,
+        0x1D61F,
+        0x1D620,
+        0x1D621,
+        0x1D622,
+        0x1D623,
+        0x1D624,
+        0x1D625,
+        0x1D626,
+        0x1D627,
+        0x1D628,
+        0x1D629,
+        0x1D62A,
+        0x1D62B,
+        0x1D62C,
+        0x1D62D,
+        0x1D62E,
+        0x1D62F,
+        0x1D630,
+        0x1D631,
+        0x1D632,
+        0x1D633,
+        0x1D634,
+        0x1D635,
+        0x1D636,
+        0x1D637,
+        0x1D638,
+        0x1D639,
+        0x1D63A,
+        0x1D63B,
+        0x1D63C,
+        0x1D63D,
+        0x1D63E,
+        0x1D63F,
+        0x1D640,
+        0x1D641,
+        0x1D642,
+        0x1D643,
+        0x1D644,
+        0x1D645,
+        0x1D646,
+        0x1D647,
+        0x1D648,
+        0x1D649,
+        0x1D64A,
+        0x1D64B,
+        0x1D64C,
+        0x1D64D,
+        0x1D64E,
+        0x1D64F,
+        0x1D650,
+        0x1D651,
+        0x1D652,
+        0x1D653,
+        0x1D654,
+        0x1D655,
+        0x1D656,
+        0x1D657,
+        0x1D658,
+        0x1D659,
+        0x1D65A,
+        0x1D65B,
+        0x1D65C,
+        0x1D65D,
+        0x1D65E,
+        0x1D65F,
+        0x1D660,
+        0x1D661,
+        0x1D662,
+        0x1D663,
+        0x1D664,
+        0x1D665,
+        0x1D666,
+        0x1D667,
+        0x1D668,
+        0x1D669,
+        0x1D66A,
+        0x1D66B,
+        0x1D66C,
+        0x1D66D,
+        0x1D66E,
+        0x1D66F,
+        0x1D670,
+        0x1D671,
+        0x1D672,
+        0x1D673,
+        0x1D674,
+        0x1D675,
+        0x1D676,
+        0x1D677,
+        0x1D678,
+        0x1D679,
+        0x1D67A,
+        0x1D67B,
+        0x1D67C,
+        0x1D67D,
+        0x1D67E,
+        0x1D67F,
+        0x1D680,
+        0x1D681,
+        0x1D682,
+        0x1D683,
+        0x1D684,
+        0x1D685,
+        0x1D686,
+        0x1D687,
+        0x1D688,
+        0x1D689,
+        0x1D68A,
+        0x1D68B,
+        0x1D68C,
+        0x1D68D,
+        0x1D68E,
+        0x1D68F,
+        0x1D690,
+        0x1D691,
+        0x1D692,
+        0x1D693,
+        0x1D694,
+        0x1D695,
+        0x1D696,
+        0x1D697,
+        0x1D698,
+        0x1D699,
+        0x1D69A,
+        0x1D69B,
+        0x1D69C,
+        0x1D69D,
+        0x1D69E,
+        0x1D69F,
+        0x1D6A0,
+        0x1D6A1,
+        0x1D6A2,
+        0x1D6A3,
+        0x1D6A4,
+        0x1D6A5,
+        0x1D6A6,
+        0x1D6A8,
+        0x1D6A9,
+        0x1D6AA,
+        0x1D6AB,
+        0x1D6AC,
+        0x1D6AD,
+        0x1D6AE,
+        0x1D6AF,
+        0x1D6B0,
+        0x1D6B1,
+        0x1D6B2,
+        0x1D6B3,
+        0x1D6B4,
+        0x1D6B5,
+        0x1D6B6,
+        0x1D6B7,
+        0x1D6B8,
+        0x1D6B9,
+        0x1D6BA,
+        0x1D6BB,
+        0x1D6BC,
+        0x1D6BD,
+        0x1D6BE,
+        0x1D6BF,
+        0x1D6C0,
+        0x1D6C1,
+        0x1D6C2,
+        0x1D6C3,
+        0x1D6C4,
+        0x1D6C5,
+        0x1D6C6,
+        0x1D6C7,
+        0x1D6C8,
+        0x1D6C9,
+        0x1D6CA,
+        0x1D6CB,
+        0x1D6CC,
+        0x1D6CD,
+        0x1D6CE,
+        0x1D6CF,
+        0x1D6D0,
+        0x1D6D1,
+        0x1D6D2,
+        0x1D6D3,
+        0x1D6D5,
+        0x1D6D6,
+        0x1D6D7,
+        0x1D6D8,
+        0x1D6D9,
+        0x1D6DA,
+        0x1D6DB,
+        0x1D6DC,
+        0x1D6DD,
+        0x1D6DE,
+        0x1D6DF,
+        0x1D6E0,
+        0x1D6E1,
+        0x1D6E2,
+        0x1D6E3,
+        0x1D6E4,
+        0x1D6E5,
+        0x1D6E6,
+        0x1D6E7,
+        0x1D6E8,
+        0x1D6E9,
+        0x1D6EA,
+        0x1D6EB,
+        0x1D6EC,
+        0x1D6ED,
+        0x1D6EE,
+        0x1D6EF,
+        0x1D6F0,
+        0x1D6F1,
+        0x1D6F2,
+        0x1D6F3,
+        0x1D6F4,
+        0x1D6F5,
+        0x1D6F6,
+        0x1D6F7,
+        0x1D6F8,
+        0x1D6F9,
+        0x1D6FA,
+        0x1D6FB,
+        0x1D6FC,
+        0x1D6FD,
+        0x1D6FE,
+        0x1D6FF,
+        0x1D700,
+        0x1D701,
+        0x1D702,
+        0x1D703,
+        0x1D704,
+        0x1D705,
+        0x1D706,
+        0x1D707,
+        0x1D708,
+        0x1D709,
+        0x1D70A,
+        0x1D70B,
+        0x1D70C,
+        0x1D70D,
+        0x1D70F,
+        0x1D710,
+        0x1D711,
+        0x1D712,
+        0x1D713,
+        0x1D714,
+        0x1D715,
+        0x1D716,
+        0x1D717,
+        0x1D718,
+        0x1D719,
+        0x1D71A,
+        0x1D71B,
+        0x1D71C,
+        0x1D71D,
+        0x1D71E,
+        0x1D71F,
+        0x1D720,
+        0x1D721,
+        0x1D722,
+        0x1D723,
+        0x1D724,
+        0x1D725,
+        0x1D726,
+        0x1D727,
+        0x1D728,
+        0x1D729,
+        0x1D72A,
+        0x1D72B,
+        0x1D72C,
+        0x1D72D,
+        0x1D72E,
+        0x1D72F,
+        0x1D730,
+        0x1D731,
+        0x1D732,
+        0x1D733,
+        0x1D734,
+        0x1D735,
+        0x1D736,
+        0x1D737,
+        0x1D738,
+        0x1D739,
+        0x1D73A,
+        0x1D73B,
+        0x1D73C,
+        0x1D73D,
+        0x1D73E,
+        0x1D73F,
+        0x1D740,
+        0x1D741,
+        0x1D742,
+        0x1D743,
+        0x1D744,
+        0x1D745,
+        0x1D746,
+        0x1D747,
+        0x1D749,
+        0x1D74A,
+        0x1D74B,
+        0x1D74C,
+        0x1D74D,
+        0x1D74E,
+        0x1D74F,
+        0x1D750,
+        0x1D751,
+        0x1D752,
+        0x1D753,
+        0x1D754,
+        0x1D755,
+        0x1D756,
+        0x1D757,
+        0x1D758,
+        0x1D759,
+        0x1D75A,
+        0x1D75B,
+        0x1D75C,
+        0x1D75D,
+        0x1D75E,
+        0x1D75F,
+        0x1D760,
+        0x1D761,
+        0x1D762,
+        0x1D763,
+        0x1D764,
+        0x1D765,
+        0x1D766,
+        0x1D767,
+        0x1D768,
+        0x1D769,
+        0x1D76A,
+        0x1D76B,
+        0x1D76C,
+        0x1D76D,
+        0x1D76E,
+        0x1D76F,
+        0x1D770,
+        0x1D771,
+        0x1D772,
+        0x1D773,
+        0x1D774,
+        0x1D775,
+        0x1D776,
+        0x1D777,
+        0x1D778,
+        0x1D779,
+        0x1D77A,
+        0x1D77B,
+        0x1D77C,
+        0x1D77D,
+        0x1D77E,
+        0x1D77F,
+        0x1D780,
+        0x1D781,
+        0x1D783,
+        0x1D784,
+        0x1D785,
+        0x1D786,
+        0x1D787,
+        0x1D788,
+        0x1D789,
+        0x1D78A,
+        0x1D78B,
+        0x1D78C,
+        0x1D78D,
+        0x1D78E,
+        0x1D78F,
+        0x1D790,
+        0x1D791,
+        0x1D792,
+        0x1D793,
+        0x1D794,
+        0x1D795,
+        0x1D796,
+        0x1D797,
+        0x1D798,
+        0x1D799,
+        0x1D79A,
+        0x1D79B,
+        0x1D79C,
+        0x1D79D,
+        0x1D79E,
+        0x1D79F,
+        0x1D7A0,
+        0x1D7A1,
+        0x1D7A2,
+        0x1D7A3,
+        0x1D7A4,
+        0x1D7A5,
+        0x1D7A6,
+        0x1D7A7,
+        0x1D7A8,
+        0x1D7A9,
+        0x1D7AA,
+        0x1D7AB,
+        0x1D7AC,
+        0x1D7AD,
+        0x1D7AE,
+        0x1D7AF,
+        0x1D7B0,
+        0x1D7B1,
+        0x1D7B2,
+        0x1D7B3,
+        0x1D7B4,
+        0x1D7B5,
+        0x1D7B6,
+        0x1D7B7,
+        0x1D7B8,
+        0x1D7B9,
+        0x1D7BA,
+        0x1D7BB,
+        0x1D7BD,
+        0x1D7BE,
+        0x1D7BF,
+        0x1D7C0,
+        0x1D7C1,
+        0x1D7C2,
+        0x1D7C3,
+        0x1D7C4,
+        0x1D7C5,
+        0x1D7C6,
+        0x1D7C7,
+        0x1D7C8,
+        0x1D7C9,
+        0x1D7CA,
+        0x1D7CC,
+        0x1D7CE,
+        0x1D7CF,
+        0x1D7D0,
+        0x1D7D1,
+        0x1D7D2,
+        0x1D7D3,
+        0x1D7D4,
+        0x1D7D5,
+        0x1D7D6,
+        0x1D7D7,
+        0x1D7D8,
+        0x1D7D9,
+        0x1D7DA,
+        0x1D7DB,
+        0x1D7DC,
+        0x1D7DD,
+        0x1D7DE,
+        0x1D7DF,
+        0x1D7E0,
+        0x1D7E1,
+        0x1D7E2,
+        0x1D7E3,
+        0x1D7E4,
+        0x1D7E5,
+        0x1D7E6,
+        0x1D7E7,
+        0x1D7E8,
+        0x1D7E9,
+        0x1D7EA,
+        0x1D7EB,
+        0x1D7EC,
+        0x1D7ED,
+        0x1D7EE,
+        0x1D7EF,
+        0x1D7F0,
+        0x1D7F1,
+        0x1D7F2,
+        0x1D7F3,
+        0x1D7F4,
+        0x1D7F5,
+        0x1D7F6,
+        0x1D7F7,
+        0x1D7F8,
+        0x1D7F9,
+        0x1D7FA,
+        0x1D7FB,
+        0x1D7FC,
+        0x1D7FD,
+        0x1D7FE,
+        0x1D7FF,
+        0x1D800,
+        0x1DA8C,
+        0x1DA9B,
+        0x1DAA0,
+        0x1DAA1,
+        0x1DAB0,
+        0x1DF00,
+        0x1DF1F,
+        0x1DF25,
+        0x1DF2B,
+        0x1E000,
+        0x1E007,
+        0x1E008,
+        0x1E019,
+        0x1E01B,
+        0x1E022,
+        0x1E023,
+        0x1E025,
+        0x1E026,
+        0x1E02B,
+        0x1E030,
+        0x1E031,
+        0x1E032,
+        0x1E033,
+        0x1E034,
+        0x1E035,
+        0x1E036,
+        0x1E037,
+        0x1E038,
+        0x1E039,
+        0x1E03A,
+        0x1E03B,
+        0x1E03C,
+        0x1E03D,
+        0x1E03E,
+        0x1E03F,
+        0x1E040,
+        0x1E041,
+        0x1E042,
+        0x1E043,
+        0x1E044,
+        0x1E045,
+        0x1E046,
+        0x1E047,
+        0x1E048,
+        0x1E049,
+        0x1E04A,
+        0x1E04B,
+        0x1E04C,
+        0x1E04D,
+        0x1E04E,
+        0x1E04F,
+        0x1E050,
+        0x1E051,
+        0x1E052,
+        0x1E053,
+        0x1E054,
+        0x1E055,
+        0x1E056,
+        0x1E057,
+        0x1E058,
+        0x1E059,
+        0x1E05A,
+        0x1E05B,
+        0x1E05C,
+        0x1E05D,
+        0x1E05E,
+        0x1E05F,
+        0x1E060,
+        0x1E061,
+        0x1E062,
+        0x1E063,
+        0x1E064,
+        0x1E065,
+        0x1E066,
+        0x1E067,
+        0x1E068,
+        0x1E069,
+        0x1E06A,
+        0x1E06B,
+        0x1E06C,
+        0x1E06D,
+        0x1E06E,
+        0x1E08F,
+        0x1E090,
+        0x1E100,
+        0x1E12D,
+        0x1E130,
+        0x1E13E,
+        0x1E140,
+        0x1E14A,
+        0x1E14E,
+        0x1E150,
+        0x1E290,
+        0x1E2AF,
+        0x1E2C0,
+        0x1E2FA,
+        0x1E2FF,
+        0x1E300,
+        0x1E4D0,
+        0x1E4FA,
+        0x1E5D0,
+        0x1E5FB,
+        0x1E5FF,
+        0x1E600,
+        0x1E6C0,
+        0x1E6DF,
+        0x1E6E0,
+        0x1E6F6,
+        0x1E6FE,
+        0x1E700,
+        0x1E7E0,
+        0x1E7E7,
+        0x1E7E8,
+        0x1E7EC,
+        0x1E7ED,
+        0x1E7EF,
+        0x1E7F0,
+        0x1E7FF,
+        0x1E800,
+        0x1E8C5,
+        0x1E8C7,
+        0x1E8D7,
+        0x1E900,
+        0x1E901,
+        0x1E902,
+        0x1E903,
+        0x1E904,
+        0x1E905,
+        0x1E906,
+        0x1E907,
+        0x1E908,
+        0x1E909,
+        0x1E90A,
+        0x1E90B,
+        0x1E90C,
+        0x1E90D,
+        0x1E90E,
+        0x1E90F,
+        0x1E910,
+        0x1E911,
+        0x1E912,
+        0x1E913,
+        0x1E914,
+        0x1E915,
+        0x1E916,
+        0x1E917,
+        0x1E918,
+        0x1E919,
+        0x1E91A,
+        0x1E91B,
+        0x1E91C,
+        0x1E91D,
+        0x1E91E,
+        0x1E91F,
+        0x1E920,
+        0x1E921,
+        0x1E922,
+        0x1E94C,
+        0x1E950,
+        0x1E95A,
+        0x1E95E,
+        0x1E960,
+        0x1EC71,
+        0x1ECB5,
+        0x1ED01,
+        0x1ED3E,
+        0x1EE00,
+        0x1EE01,
+        0x1EE02,
+        0x1EE03,
+        0x1EE04,
+        0x1EE05,
+        0x1EE06,
+        0x1EE07,
+        0x1EE08,
+        0x1EE09,
+        0x1EE0A,
+        0x1EE0B,
+        0x1EE0C,
+        0x1EE0D,
+        0x1EE0E,
+        0x1EE0F,
+        0x1EE10,
+        0x1EE11,
+        0x1EE12,
+        0x1EE13,
+        0x1EE14,
+        0x1EE15,
+        0x1EE16,
+        0x1EE17,
+        0x1EE18,
+        0x1EE19,
+        0x1EE1A,
+        0x1EE1B,
+        0x1EE1C,
+        0x1EE1D,
+        0x1EE1E,
+        0x1EE1F,
+        0x1EE20,
+        0x1EE21,
+        0x1EE22,
+        0x1EE23,
+        0x1EE24,
+        0x1EE25,
+        0x1EE27,
+        0x1EE28,
+        0x1EE29,
+        0x1EE2A,
+        0x1EE2B,
+        0x1EE2C,
+        0x1EE2D,
+        0x1EE2E,
+        0x1EE2F,
+        0x1EE30,
+        0x1EE31,
+        0x1EE32,
+        0x1EE33,
+        0x1EE34,
+        0x1EE35,
+        0x1EE36,
+        0x1EE37,
+        0x1EE38,
+        0x1EE39,
+        0x1EE3A,
+        0x1EE3B,
+        0x1EE3C,
+        0x1EE42,
+        0x1EE43,
+        0x1EE47,
+        0x1EE48,
+        0x1EE49,
+        0x1EE4A,
+        0x1EE4B,
+        0x1EE4C,
+        0x1EE4D,
+        0x1EE4E,
+        0x1EE4F,
+        0x1EE50,
+        0x1EE51,
+        0x1EE52,
+        0x1EE53,
+        0x1EE54,
+        0x1EE55,
+        0x1EE57,
+        0x1EE58,
+        0x1EE59,
+        0x1EE5A,
+        0x1EE5B,
+        0x1EE5C,
+        0x1EE5D,
+        0x1EE5E,
+        0x1EE5F,
+        0x1EE60,
+        0x1EE61,
+        0x1EE62,
+        0x1EE63,
+        0x1EE64,
+        0x1EE65,
+        0x1EE67,
+        0x1EE68,
+        0x1EE69,
+        0x1EE6A,
+        0x1EE6B,
+        0x1EE6C,
+        0x1EE6D,
+        0x1EE6E,
+        0x1EE6F,
+        0x1EE70,
+        0x1EE71,
+        0x1EE72,
+        0x1EE73,
+        0x1EE74,
+        0x1EE75,
+        0x1EE76,
+        0x1EE77,
+        0x1EE78,
+        0x1EE79,
+        0x1EE7A,
+        0x1EE7B,
+        0x1EE7C,
+        0x1EE7D,
+        0x1EE7E,
+        0x1EE7F,
+        0x1EE80,
+        0x1EE81,
+        0x1EE82,
+        0x1EE83,
+        0x1EE84,
+        0x1EE85,
+        0x1EE86,
+        0x1EE87,
+        0x1EE88,
+        0x1EE89,
+        0x1EE8A,
+        0x1EE8B,
+        0x1EE8C,
+        0x1EE8D,
+        0x1EE8E,
+        0x1EE8F,
+        0x1EE90,
+        0x1EE91,
+        0x1EE92,
+        0x1EE93,
+        0x1EE94,
+        0x1EE95,
+        0x1EE96,
+        0x1EE97,
+        0x1EE98,
+        0x1EE99,
+        0x1EE9A,
+        0x1EE9B,
+        0x1EE9C,
+        0x1EEA1,
+        0x1EEA2,
+        0x1EEA3,
+        0x1EEA4,
+        0x1EEA5,
+        0x1EEA6,
+        0x1EEA7,
+        0x1EEA8,
+        0x1EEA9,
+        0x1EEAA,
+        0x1EEAB,
+        0x1EEAC,
+        0x1EEAD,
+        0x1EEAE,
+        0x1EEAF,
+        0x1EEB0,
+        0x1EEB1,
+        0x1EEB2,
+        0x1EEB3,
+        0x1EEB4,
+        0x1EEB5,
+        0x1EEB6,
+        0x1EEB7,
+        0x1EEB8,
+        0x1EEB9,
+        0x1EEBA,
+        0x1EEBB,
+        0x1EEBC,
+        0x1EEF0,
+        0x1EEF2,
+        0x1F000,
+        0x1F02C,
+        0x1F030,
+        0x1F094,
+        0x1F0A0,
+        0x1F0AF,
+        0x1F0B1,
+        0x1F0C0,
+        0x1F0C1,
+        0x1F0D0,
+        0x1F0D1,
+        0x1F0F6,
+        0x1F101,
+        0x1F102,
+        0x1F103,
+        0x1F104,
+        0x1F105,
+        0x1F106,
+        0x1F107,
+        0x1F108,
+        0x1F109,
+        0x1F10A,
+        0x1F10B,
+        0x1F110,
+        0x1F111,
+        0x1F112,
+        0x1F113,
+        0x1F114,
+        0x1F115,
+        0x1F116,
+        0x1F117,
+        0x1F118,
+        0x1F119,
+        0x1F11A,
+        0x1F11B,
+        0x1F11C,
+        0x1F11D,
+        0x1F11E,
+        0x1F11F,
+        0x1F120,
+        0x1F121,
+        0x1F122,
+        0x1F123,
+        0x1F124,
+        0x1F125,
+        0x1F126,
+        0x1F127,
+        0x1F128,
+        0x1F129,
+        0x1F12A,
+        0x1F12B,
+        0x1F12C,
+        0x1F12D,
+        0x1F12E,
+        0x1F12F,
+        0x1F130,
+        0x1F131,
+        0x1F132,
+        0x1F133,
+        0x1F134,
+        0x1F135,
+        0x1F136,
+        0x1F137,
+        0x1F138,
+        0x1F139,
+        0x1F13A,
+        0x1F13B,
+        0x1F13C,
+        0x1F13D,
+        0x1F13E,
+        0x1F13F,
+        0x1F140,
+        0x1F141,
+        0x1F142,
+        0x1F143,
+        0x1F144,
+        0x1F145,
+        0x1F146,
+        0x1F147,
+        0x1F148,
+        0x1F149,
+        0x1F14A,
+        0x1F14B,
+        0x1F14C,
+        0x1F14D,
+        0x1F14E,
+        0x1F14F,
+        0x1F150,
+        0x1F16A,
+        0x1F16B,
+        0x1F16C,
+        0x1F16D,
+        0x1F190,
+        0x1F191,
+        0x1F1AE,
+        0x1F1E6,
+        0x1F200,
+        0x1F201,
+        0x1F202,
+        0x1F203,
+        0x1F210,
+        0x1F211,
+        0x1F212,
+        0x1F213,
+        0x1F214,
+        0x1F215,
+        0x1F216,
+        0x1F217,
+        0x1F218,
+        0x1F219,
+        0x1F21A,
+        0x1F21B,
+        0x1F21C,
+        0x1F21D,
+        0x1F21E,
+        0x1F21F,
+        0x1F220,
+        0x1F221,
+        0x1F222,
+        0x1F223,
+        0x1F224,
+        0x1F225,
+        0x1F226,
+        0x1F227,
+        0x1F228,
+        0x1F229,
+        0x1F22A,
+        0x1F22B,
+        0x1F22C,
+        0x1F22D,
+        0x1F22E,
+        0x1F22F,
+        0x1F230,
+        0x1F231,
+        0x1F232,
+        0x1F233,
+        0x1F234,
+        0x1F235,
+        0x1F236,
+        0x1F237,
+        0x1F238,
+        0x1F239,
+        0x1F23A,
+        0x1F23B,
+        0x1F23C,
+        0x1F240,
+        0x1F241,
+        0x1F242,
+        0x1F243,
+        0x1F244,
+        0x1F245,
+        0x1F246,
+        0x1F247,
+        0x1F248,
+        0x1F249,
+        0x1F250,
+        0x1F251,
+        0x1F252,
+        0x1F260,
+        0x1F266,
+        0x1F300,
+        0x1F6D9,
+        0x1F6DC,
+        0x1F6ED,
+        0x1F6F0,
+        0x1F6FD,
+        0x1F700,
+        0x1F7DA,
+        0x1F7E0,
+        0x1F7EC,
+        0x1F7F0,
+        0x1F7F1,
+        0x1F800,
+        0x1F80C,
+        0x1F810,
+        0x1F848,
+        0x1F850,
+        0x1F85A,
+        0x1F860,
+        0x1F888,
+        0x1F890,
+        0x1F8AE,
+        0x1F8B0,
+        0x1F8BC,
+        0x1F8C0,
+        0x1F8C2,
+        0x1F8D0,
+        0x1F8D9,
+        0x1F900,
+        0x1FA58,
+        0x1FA60,
+        0x1FA6E,
+        0x1FA70,
+        0x1FA7D,
+        0x1FA80,
+        0x1FA8B,
+        0x1FA8E,
+        0x1FAC7,
+        0x1FAC8,
+        0x1FAC9,
+        0x1FACD,
+        0x1FADD,
+        0x1FADF,
+        0x1FAEB,
+        0x1FAEF,
+        0x1FAF9,
+        0x1FB00,
+        0x1FB93,
+        0x1FB94,
+        0x1FBF0,
+        0x1FBF1,
+        0x1FBF2,
+        0x1FBF3,
+        0x1FBF4,
+        0x1FBF5,
+        0x1FBF6,
+        0x1FBF7,
+        0x1FBF8,
+        0x1FBF9,
+        0x1FBFA,
+        0x1FBFB,
+        0x20000,
+        0x2A6E0,
+        0x2A700,
+        0x2B81E,
+        0x2B820,
+        0x2CEAE,
+        0x2CEB0,
+        0x2EBE1,
+        0x2EBF0,
+        0x2EE5E,
+        0x2F800,
+        0x2F801,
+        0x2F802,
+        0x2F803,
+        0x2F804,
+        0x2F805,
+        0x2F806,
+        0x2F807,
+        0x2F808,
+        0x2F809,
+        0x2F80A,
+        0x2F80B,
+        0x2F80C,
+        0x2F80D,
+        0x2F80E,
+        0x2F80F,
+        0x2F810,
+        0x2F811,
+        0x2F812,
+        0x2F813,
+        0x2F814,
+        0x2F815,
+        0x2F816,
+        0x2F817,
+        0x2F818,
+        0x2F819,
+        0x2F81A,
+        0x2F81B,
+        0x2F81C,
+        0x2F81D,
+        0x2F81E,
+        0x2F81F,
+        0x2F820,
+        0x2F821,
+        0x2F822,
+        0x2F823,
+        0x2F824,
+        0x2F825,
+        0x2F826,
+        0x2F827,
+        0x2F828,
+        0x2F829,
+        0x2F82A,
+        0x2F82B,
+        0x2F82C,
+        0x2F82D,
+        0x2F82E,
+        0x2F82F,
+        0x2F830,
+        0x2F831,
+        0x2F834,
+        0x2F835,
+        0x2F836,
+        0x2F837,
+        0x2F838,
+        0x2F839,
+        0x2F83A,
+        0x2F83B,
+        0x2F83C,
+        0x2F83D,
+        0x2F83E,
+        0x2F83F,
+        0x2F840,
+        0x2F841,
+        0x2F842,
+        0x2F843,
+        0x2F844,
+        0x2F845,
+        0x2F847,
+        0x2F848,
+        0x2F849,
+        0x2F84A,
+        0x2F84B,
+        0x2F84C,
+        0x2F84D,
+        0x2F84E,
+        0x2F84F,
+        0x2F850,
+        0x2F851,
+        0x2F852,
+        0x2F853,
+        0x2F854,
+        0x2F855,
+        0x2F856,
+        0x2F857,
+        0x2F858,
+        0x2F859,
+        0x2F85A,
+        0x2F85B,
+        0x2F85C,
+        0x2F85D,
+        0x2F85E,
+        0x2F85F,
+        0x2F860,
+        0x2F861,
+        0x2F862,
+        0x2F863,
+        0x2F864,
+        0x2F865,
+        0x2F866,
+        0x2F867,
+        0x2F868,
+        0x2F869,
+        0x2F86A,
+        0x2F86C,
+        0x2F86D,
+        0x2F86E,
+        0x2F86F,
+        0x2F870,
+        0x2F871,
+        0x2F872,
+        0x2F873,
+        0x2F874,
+        0x2F875,
+        0x2F876,
+        0x2F877,
+        0x2F878,
+        0x2F879,
+        0x2F87A,
+        0x2F87B,
+        0x2F87C,
+        0x2F87D,
+        0x2F87E,
+        0x2F87F,
+        0x2F880,
+        0x2F881,
+        0x2F882,
+        0x2F883,
+        0x2F884,
+        0x2F885,
+        0x2F886,
+        0x2F887,
+        0x2F888,
+        0x2F889,
+        0x2F88A,
+        0x2F88B,
+        0x2F88C,
+        0x2F88D,
+        0x2F88E,
+        0x2F88F,
+        0x2F890,
+        0x2F891,
+        0x2F893,
+        0x2F894,
+        0x2F896,
+        0x2F897,
+        0x2F898,
+        0x2F899,
+        0x2F89A,
+        0x2F89B,
+        0x2F89C,
+        0x2F89D,
+        0x2F89E,
+        0x2F89F,
+        0x2F8A0,
+        0x2F8A1,
+        0x2F8A2,
+        0x2F8A3,
+        0x2F8A4,
+        0x2F8A5,
+        0x2F8A6,
+        0x2F8A7,
+        0x2F8A8,
+        0x2F8A9,
+        0x2F8AA,
+        0x2F8AB,
+        0x2F8AC,
+        0x2F8AD,
+        0x2F8AE,
+        0x2F8AF,
+        0x2F8B0,
+        0x2F8B1,
+        0x2F8B2,
+        0x2F8B3,
+        0x2F8B4,
+        0x2F8B5,
+        0x2F8B6,
+        0x2F8B7,
+        0x2F8B8,
+        0x2F8B9,
+        0x2F8BA,
+        0x2F8BB,
+        0x2F8BC,
+        0x2F8BD,
+        0x2F8BE,
+        0x2F8BF,
+        0x2F8C0,
+        0x2F8C1,
+        0x2F8C2,
+        0x2F8C3,
+        0x2F8C4,
+        0x2F8C5,
+        0x2F8C6,
+        0x2F8C7,
+        0x2F8C8,
+        0x2F8C9,
+        0x2F8CA,
+        0x2F8CB,
+        0x2F8CC,
+        0x2F8CD,
+        0x2F8CE,
+        0x2F8CF,
+        0x2F8D0,
+        0x2F8D1,
+        0x2F8D2,
+        0x2F8D3,
+        0x2F8D4,
+        0x2F8D5,
+        0x2F8D6,
+        0x2F8D7,
+        0x2F8D8,
+        0x2F8D9,
+        0x2F8DA,
+        0x2F8DB,
+        0x2F8DC,
+        0x2F8DD,
+        0x2F8DE,
+        0x2F8DF,
+        0x2F8E0,
+        0x2F8E1,
+        0x2F8E2,
+        0x2F8E3,
+        0x2F8E4,
+        0x2F8E5,
+        0x2F8E6,
+        0x2F8E7,
+        0x2F8E8,
+        0x2F8E9,
+        0x2F8EA,
+        0x2F8EB,
+        0x2F8EC,
+        0x2F8ED,
+        0x2F8EE,
+        0x2F8EF,
+        0x2F8F0,
+        0x2F8F1,
+        0x2F8F2,
+        0x2F8F3,
+        0x2F8F4,
+        0x2F8F5,
+        0x2F8F6,
+        0x2F8F7,
+        0x2F8F8,
+        0x2F8F9,
+        0x2F8FA,
+        0x2F8FB,
+        0x2F8FC,
+        0x2F8FD,
+        0x2F8FE,
+        0x2F8FF,
+        0x2F900,
+        0x2F901,
+        0x2F902,
+        0x2F903,
+        0x2F904,
+        0x2F905,
+        0x2F906,
+        0x2F907,
+        0x2F908,
+        0x2F909,
+        0x2F90A,
+        0x2F90B,
+        0x2F90C,
+        0x2F90D,
+        0x2F90E,
+        0x2F90F,
+        0x2F910,
+        0x2F911,
+        0x2F912,
+        0x2F913,
+        0x2F914,
+        0x2F915,
+        0x2F916,
+        0x2F917,
+        0x2F918,
+        0x2F919,
+        0x2F91A,
+        0x2F91B,
+        0x2F91C,
+        0x2F91D,
+        0x2F91E,
+        0x2F91F,
+        0x2F920,
+        0x2F921,
+        0x2F922,
+        0x2F923,
+        0x2F924,
+        0x2F925,
+        0x2F926,
+        0x2F927,
+        0x2F928,
+        0x2F929,
+        0x2F92A,
+        0x2F92B,
+        0x2F92C,
+        0x2F92E,
+        0x2F92F,
+        0x2F930,
+        0x2F931,
+        0x2F932,
+        0x2F933,
+        0x2F934,
+        0x2F935,
+        0x2F936,
+        0x2F937,
+        0x2F938,
+        0x2F939,
+        0x2F93A,
+        0x2F93B,
+        0x2F93C,
+        0x2F93D,
+        0x2F93E,
+        0x2F93F,
+        0x2F940,
+        0x2F941,
+        0x2F942,
+        0x2F943,
+        0x2F944,
+        0x2F945,
+        0x2F946,
+        0x2F948,
+        0x2F949,
+        0x2F94A,
+        0x2F94B,
+        0x2F94C,
+        0x2F94D,
+        0x2F94E,
+        0x2F94F,
+        0x2F950,
+        0x2F951,
+        0x2F952,
+        0x2F953,
+        0x2F954,
+        0x2F955,
+        0x2F956,
+        0x2F957,
+        0x2F958,
+        0x2F959,
+        0x2F95A,
+        0x2F95B,
+        0x2F95C,
+        0x2F95D,
+        0x2F95F,
+        0x2F960,
+        0x2F961,
+        0x2F962,
+        0x2F963,
+        0x2F964,
+        0x2F965,
+        0x2F966,
+        0x2F967,
+        0x2F968,
+        0x2F969,
+        0x2F96A,
+        0x2F96B,
+        0x2F96C,
+        0x2F96D,
+        0x2F96E,
+        0x2F96F,
+        0x2F970,
+        0x2F971,
+        0x2F972,
+        0x2F973,
+        0x2F974,
+        0x2F975,
+        0x2F976,
+        0x2F977,
+        0x2F978,
+        0x2F979,
+        0x2F97A,
+        0x2F97B,
+        0x2F97C,
+        0x2F97D,
+        0x2F97E,
+        0x2F97F,
+        0x2F980,
+        0x2F981,
+        0x2F982,
+        0x2F983,
+        0x2F984,
+        0x2F985,
+        0x2F986,
+        0x2F987,
+        0x2F988,
+        0x2F989,
+        0x2F98A,
+        0x2F98B,
+        0x2F98C,
+        0x2F98D,
+        0x2F98E,
+        0x2F98F,
+        0x2F990,
+        0x2F991,
+        0x2F992,
+        0x2F993,
+        0x2F994,
+        0x2F995,
+        0x2F996,
+        0x2F997,
+        0x2F998,
+        0x2F999,
+        0x2F99A,
+        0x2F99B,
+        0x2F99C,
+        0x2F99D,
+        0x2F99E,
+        0x2F99F,
+        0x2F9A0,
+        0x2F9A1,
+        0x2F9A2,
+        0x2F9A3,
+        0x2F9A4,
+        0x2F9A5,
+        0x2F9A6,
+        0x2F9A7,
+        0x2F9A8,
+        0x2F9A9,
+        0x2F9AA,
+        0x2F9AB,
+        0x2F9AC,
+        0x2F9AD,
+        0x2F9AE,
+        0x2F9AF,
+        0x2F9B0,
+        0x2F9B1,
+        0x2F9B2,
+        0x2F9B3,
+        0x2F9B4,
+        0x2F9B5,
+        0x2F9B6,
+        0x2F9B7,
+        0x2F9B8,
+        0x2F9B9,
+        0x2F9BA,
+        0x2F9BB,
+        0x2F9BC,
+        0x2F9BD,
+        0x2F9BE,
+        0x2F9BF,
+        0x2F9C0,
+        0x2F9C1,
+        0x2F9C2,
+        0x2F9C3,
+        0x2F9C4,
+        0x2F9C5,
+        0x2F9C6,
+        0x2F9C7,
+        0x2F9C8,
+        0x2F9C9,
+        0x2F9CA,
+        0x2F9CB,
+        0x2F9CC,
+        0x2F9CD,
+        0x2F9CE,
+        0x2F9CF,
+        0x2F9D0,
+        0x2F9D1,
+        0x2F9D2,
+        0x2F9D3,
+        0x2F9D4,
+        0x2F9D5,
+        0x2F9D6,
+        0x2F9D7,
+        0x2F9D8,
+        0x2F9D9,
+        0x2F9DA,
+        0x2F9DB,
+        0x2F9DC,
+        0x2F9DD,
+        0x2F9DE,
+        0x2F9DF,
+        0x2F9E0,
+        0x2F9E1,
+        0x2F9E2,
+        0x2F9E3,
+        0x2F9E4,
+        0x2F9E5,
+        0x2F9E6,
+        0x2F9E7,
+        0x2F9E8,
+        0x2F9E9,
+        0x2F9EA,
+        0x2F9EB,
+        0x2F9EC,
+        0x2F9ED,
+        0x2F9EE,
+        0x2F9EF,
+        0x2F9F0,
+        0x2F9F1,
+        0x2F9F2,
+        0x2F9F3,
+        0x2F9F4,
+        0x2F9F5,
+        0x2F9F6,
+        0x2F9F7,
+        0x2F9F8,
+        0x2F9F9,
+        0x2F9FA,
+        0x2F9FB,
+        0x2F9FC,
+        0x2F9FD,
+        0x2F9FE,
+        0x2FA00,
+        0x2FA01,
+        0x2FA02,
+        0x2FA03,
+        0x2FA04,
+        0x2FA05,
+        0x2FA06,
+        0x2FA07,
+        0x2FA08,
+        0x2FA09,
+        0x2FA0A,
+        0x2FA0B,
+        0x2FA0C,
+        0x2FA0D,
+        0x2FA0E,
+        0x2FA0F,
+        0x2FA10,
+        0x2FA11,
+        0x2FA12,
+        0x2FA13,
+        0x2FA14,
+        0x2FA15,
+        0x2FA16,
+        0x2FA17,
+        0x2FA18,
+        0x2FA19,
+        0x2FA1A,
+        0x2FA1B,
+        0x2FA1C,
+        0x2FA1D,
+        0x2FA1E,
+        0x30000,
+        0x3134B,
+        0x31350,
+        0x3347A,
+        0xE0100,
+        0xE01F0,
+    ),
+)
+
+uts46_statuses: bytes = (
+    b"VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV"
+    b"VMMMMMMMMMMMMMMMMMMMMMMMMMMVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV"
+    b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXMVVVVVVVMVMVVIVMVVMMMMVVMMMVMMMV"
+    b"MMMMMMMMMMMMMMMMMMMMMMMVMMMMMMMDVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV"
+    b"MVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMMVMVMVMVMVMMV"
+    b"MVMVMVMMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMMVMVMVMVMM"
+    b"VMVMMVMMMVMMMMVMMVMMMVMMVMMVMVMVMMVMVMVMMVMMMVMVMMVMVMMMMVMVMVMV"
+    b"MVMVMVMVMVMVMVMVMVMVMVMVMVMMVMMMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVM"
+    b"VMVMVMVMVMVMVMVMVMVMVMVMVMVMMVMMVMVMMMMVMVMVMVMVMMMMMMMMMVMMMMMM"
+    b"VMMMMMVMMVMMMVIVMVMVMVMVXMVMMXMMMMMMMXMXMMVMMMMMMMMMMMMMMMMMXMMM"
+    b"MMMMMMVDVMMMMMMMMVMVMVMVMVMVMVMVMVMVMVMVMVMMMVMMVMVMMVMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMVMVMVMVMVMVMVMVMVMVMV"
+    b"MVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMV"
+    b"MVMMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVM"
+    b"VMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVXMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMXVMVXVXVXVXVXVXVMMMMVXVXVXVXVXVXVXVXVXVX"
+    b"VXVXVMMMMMMMMVXVXVXVXVXVXVXVXVXVXVXMMXMVXVXVXVXVXVXVXVMXVMXVXVXV"
+    b"XVXVXVXMMMVXMXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVX"
+    b"VXMMXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXV"
+    b"XVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXV"
+    b"XVMVXVXVXVXVXVXVXVMVXVXVXVXVXMMVXVMVMVXVMVMVMVMVMVXVMVMMMMMVMVMV"
+    b"XVMVMVMVMVMVXVXVXVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMXMXVMV"
+    b"IVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXMMMMMMXVXVXVXVXVXVXVXVXVX"
+    b"VIVXVXVXVIVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVMMM"
+    b"MMMMMMVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMVXVXVMMMV"
+    b"MMMMMMMMMMMVMMMMMMMMMMMMMMMMMMVMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMVMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMVMVMVMVMVMVMVMVMVMVMVMVMVMV"
+    b"MVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMV"
+    b"MVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMMVMVM"
+    b"VMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVM"
+    b"VMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMMMMMMMMVXMMMMMMXVMMMMMMMMVMMMMMM"
+    b"MMVXMMMMMMXVXMXMXMXMVMMMMMMMMVMVMVMVMVMVMVMXMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMVMMMXVMMMMMMMMMMMMMMXVMMMMMMMMMVMXVM"
+    b"MMMXMMMVMVMMMMMMMMXMMMXVMMMMMMMMXMIDXVMVMVXVXMVMMVMMVMVMVMMMVMVM"
+    b"IXIMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMMMMMMMMMMXVMVXVXMMMMVMMMVM"
+    b"MMMMMVMMVMMMVMMMVMVMVMVMMMMVMMMMMMMMMMVMMMMMVMMMMVMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMVMVXVMMVMMVMMVXVXMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMVMMMVMVXVMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMVMMMVMVMVMVMMMMVMVMVMM"
+    b"MMMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMV"
+    b"MVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVXVXVXVXVXMVXVXVXVXVX"
+    b"VXVXVXVXVXVXVXVMVMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMVMVMVMMMVXVXVMMVMVMXV"
+    b"XMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMIMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXVMMMMMMMMMMMMMMVXVMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMXMMMMMMMMMMMMM"
+    b"MMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVXVXVXMVMVMVMVMVMVMVM"
+    b"VMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMMVXV"
+    b"MVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMV"
+    b"MVMVMVMVMVMVMVMVMVMMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMVMMMMMVM"
+    b"MMMMVMVMVMVMVMVMVMVMMMMVMVMMVMVMVMVMVMVMVMVMXMMMMMVMMVXVXVXVXVXV"
+    b"XVXVXVXVXVXVXVXVXVXVXVXVXVXVXVMMMMVMVXMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVXVXVXVXVX"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMVMVMVMMMMMMMMMMVMVMVMMVMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMXMMMMMMXMMMMMXMVMMMMMMMMMMMMMMMMMMMMMMMMXMMMMM"
+    b"XMXMMXMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVXMMMMMMMMMMMMMVIMMXMMM"
+    b"MMMXVXMMMMMMMMMMMMMMMMMMMVMMMMMMXMMMMMMMMMMMMMMMMMMMXMMMMXMMMVMX"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXIXMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMIMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMMMXMMMMM"
+    b"MXMMMMMMXMMMXMMMMMMMXMMMMMMMXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXV"
+    b"XVXVXVXVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVXVXMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMXVXVXVXVMMMMMMMMMMMXMMMMMMMMMMMMMMMXMMM"
+    b"MMMMXMMXVXVXVXVXVXVXVXVXVMMMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMXMMMMMMMMMXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVX"
+    b"VXVXVXVXVXVXVXVXVXVXVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMXVXVXVXVMMMMMMMMMMMMMMMMMMMMMMXVXVXVXVXVXVXVXVXVXVXVXVX"
+    b"VXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVX"
+    b"VXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVX"
+    b"VXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVX"
+    b"VXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMVXMMMMMMMMMMMMMMMMMMMMMMMMMXVXVXVXVXVXVXVXVXVXVXVXVXVX"
+    b"VXVXVXVXVXVXVXVXVXVIXVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVXVXVX"
+    b"VXVXVXVXVXVXVMMMMMMMVIVMMMMMMVXVXVXVXVXVXMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMXMMXMXMMXMMMMXMMMMMMMMMMMMXMXMMMMMMMXMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMXMMMMMMMMXMMMMM"
+    b"MMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMXMMMMMXMXMMMMMMMXMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMVXVXVXVXVXVXVXVXVXVXMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXVXVXVXVXVXVXVXVXVX"
+    b"VXVXVXVXVXVXVXVXVXVXVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVXVXVXVX"
+    b"VXMMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMXMMXMXMXMMMMMMMMMMXMMMMXMXMXMX"
+    b"MXMXMXMMMXMMXMXMXMXMXMXMXMMXMXMMMMXMMMMMMMXMMMMXMMMMXMXMMMMMMMMM"
+    b"MXMMMMMMMMMMMMMMMMMXMMMXMMMMMXMMMMMMMMMMMMMMMMMXVXVXVXVXVXVXVXMM"
+    b"MMMMMMMMVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMVMMMVMVXVMMMXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMXMMMMMMMMMXMMXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVXVX"
+    b"VXVMMMMMMMMMMVXVXVXVXVXVXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
+    b"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXVXVXIX"
+)
+
+uts46_replacements: tuple[Optional[str], ...] = (
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    " ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    " ̈",
+    None,
+    "a",
+    None,
+    None,
+    None,
+    None,
+    " ̄",
+    None,
+    None,
+    "2",
+    "3",
+    " ́",
+    "μ",
+    None,
+    None,
+    " ̧",
+    "1",
+    "o",
+    None,
+    "1⁄4",
+    "1⁄2",
+    "3⁄4",
+    None,
+    "à",
+    "á",
+    "â",
+    "ã",
+    "ä",
+    "å",
+    "æ",
+    "ç",
+    "è",
+    "é",
+    "ê",
+    "ë",
+    "ì",
+    "í",
+    "î",
+    "ï",
+    "ð",
+    "ñ",
+    "ò",
+    "ó",
+    "ô",
+    "õ",
+    "ö",
+    None,
+    "ø",
+    "ù",
+    "ú",
+    "û",
+    "ü",
+    "ý",
+    "þ",
+    "ss",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ā",
+    None,
+    "ă",
+    None,
+    "ą",
+    None,
+    "ć",
+    None,
+    "ĉ",
+    None,
+    "ċ",
+    None,
+    "č",
+    None,
+    "ď",
+    None,
+    "đ",
+    None,
+    "ē",
+    None,
+    "ĕ",
+    None,
+    "ė",
+    None,
+    "ę",
+    None,
+    "ě",
+    None,
+    "ĝ",
+    None,
+    "ğ",
+    None,
+    "ġ",
+    None,
+    "ģ",
+    None,
+    "ĥ",
+    None,
+    "ħ",
+    None,
+    "ĩ",
+    None,
+    "ī",
+    None,
+    "ĭ",
+    None,
+    "į",
+    None,
+    "i̇",
+    None,
+    "ij",
+    "ĵ",
+    None,
+    "ķ",
+    None,
+    "ĺ",
+    None,
+    "ļ",
+    None,
+    "ľ",
+    None,
+    "l·",
+    "ł",
+    None,
+    "ń",
+    None,
+    "ņ",
+    None,
+    "ň",
+    None,
+    "ʼn",
+    "ŋ",
+    None,
+    "ō",
+    None,
+    "ŏ",
+    None,
+    "ő",
+    None,
+    "œ",
+    None,
+    "ŕ",
+    None,
+    "ŗ",
+    None,
+    "ř",
+    None,
+    "ś",
+    None,
+    "ŝ",
+    None,
+    "ş",
+    None,
+    "š",
+    None,
+    "ţ",
+    None,
+    "ť",
+    None,
+    "ŧ",
+    None,
+    "ũ",
+    None,
+    "ū",
+    None,
+    "ŭ",
+    None,
+    "ů",
+    None,
+    "ű",
+    None,
+    "ų",
+    None,
+    "ŵ",
+    None,
+    "ŷ",
+    None,
+    "ÿ",
+    "ź",
+    None,
+    "ż",
+    None,
+    "ž",
+    None,
+    "s",
+    None,
+    "ɓ",
+    "ƃ",
+    None,
+    "ƅ",
+    None,
+    "ɔ",
+    "ƈ",
+    None,
+    "ɖ",
+    "ɗ",
+    "ƌ",
+    None,
+    "ǝ",
+    "ə",
+    "ɛ",
+    "ƒ",
+    None,
+    "ɠ",
+    "ɣ",
+    None,
+    "ɩ",
+    "ɨ",
+    "ƙ",
+    None,
+    "ɯ",
+    "ɲ",
+    None,
+    "ɵ",
+    "ơ",
+    None,
+    "ƣ",
+    None,
+    "ƥ",
+    None,
+    "ʀ",
+    "ƨ",
+    None,
+    "ʃ",
+    None,
+    "ƭ",
+    None,
+    "ʈ",
+    "ư",
+    None,
+    "ʊ",
+    "ʋ",
+    "ƴ",
+    None,
+    "ƶ",
+    None,
+    "ʒ",
+    "ƹ",
+    None,
+    "ƽ",
+    None,
+    "dž",
+    "lj",
+    "nj",
+    "ǎ",
+    None,
+    "ǐ",
+    None,
+    "ǒ",
+    None,
+    "ǔ",
+    None,
+    "ǖ",
+    None,
+    "ǘ",
+    None,
+    "ǚ",
+    None,
+    "ǜ",
+    None,
+    "ǟ",
+    None,
+    "ǡ",
+    None,
+    "ǣ",
+    None,
+    "ǥ",
+    None,
+    "ǧ",
+    None,
+    "ǩ",
+    None,
+    "ǫ",
+    None,
+    "ǭ",
+    None,
+    "ǯ",
+    None,
+    "dz",
+    "ǵ",
+    None,
+    "ƕ",
+    "ƿ",
+    "ǹ",
+    None,
+    "ǻ",
+    None,
+    "ǽ",
+    None,
+    "ǿ",
+    None,
+    "ȁ",
+    None,
+    "ȃ",
+    None,
+    "ȅ",
+    None,
+    "ȇ",
+    None,
+    "ȉ",
+    None,
+    "ȋ",
+    None,
+    "ȍ",
+    None,
+    "ȏ",
+    None,
+    "ȑ",
+    None,
+    "ȓ",
+    None,
+    "ȕ",
+    None,
+    "ȗ",
+    None,
+    "ș",
+    None,
+    "ț",
+    None,
+    "ȝ",
+    None,
+    "ȟ",
+    None,
+    "ƞ",
+    None,
+    "ȣ",
+    None,
+    "ȥ",
+    None,
+    "ȧ",
+    None,
+    "ȩ",
+    None,
+    "ȫ",
+    None,
+    "ȭ",
+    None,
+    "ȯ",
+    None,
+    "ȱ",
+    None,
+    "ȳ",
+    None,
+    "ⱥ",
+    "ȼ",
+    None,
+    "ƚ",
+    "ⱦ",
+    None,
+    "ɂ",
+    None,
+    "ƀ",
+    "ʉ",
+    "ʌ",
+    "ɇ",
+    None,
+    "ɉ",
+    None,
+    "ɋ",
+    None,
+    "ɍ",
+    None,
+    "ɏ",
+    None,
+    "h",
+    "ɦ",
+    "j",
+    "r",
+    "ɹ",
+    "ɻ",
+    "ʁ",
+    "w",
+    "y",
+    None,
+    " ̆",
+    " ̇",
+    " ̊",
+    " ̨",
+    " ̃",
+    " ̋",
+    None,
+    "ɣ",
+    "l",
+    "s",
+    "x",
+    "ʕ",
+    None,
+    "̀",
+    "́",
+    None,
+    "̓",
+    "̈́",
+    "ι",
+    None,
+    None,
+    None,
+    "ͱ",
+    None,
+    "ͳ",
+    None,
+    "ʹ",
+    None,
+    "ͷ",
+    None,
+    None,
+    " ι",
+    None,
+    ";",
+    "ϳ",
+    None,
+    " ́",
+    " ̈́",
+    "ά",
+    "·",
+    "έ",
+    "ή",
+    "ί",
+    None,
+    "ό",
+    None,
+    "ύ",
+    "ώ",
+    None,
+    "α",
+    "β",
+    "γ",
+    "δ",
+    "ε",
+    "ζ",
+    "η",
+    "θ",
+    "ι",
+    "κ",
+    "λ",
+    "μ",
+    "ν",
+    "ξ",
+    "ο",
+    "π",
+    "ρ",
+    None,
+    "σ",
+    "τ",
+    "υ",
+    "φ",
+    "χ",
+    "ψ",
+    "ω",
+    "ϊ",
+    "ϋ",
+    None,
+    "σ",
+    None,
+    "ϗ",
+    "β",
+    "θ",
+    "υ",
+    "ύ",
+    "ϋ",
+    "φ",
+    "π",
+    None,
+    "ϙ",
+    None,
+    "ϛ",
+    None,
+    "ϝ",
+    None,
+    "ϟ",
+    None,
+    "ϡ",
+    None,
+    "ϣ",
+    None,
+    "ϥ",
+    None,
+    "ϧ",
+    None,
+    "ϩ",
+    None,
+    "ϫ",
+    None,
+    "ϭ",
+    None,
+    "ϯ",
+    None,
+    "κ",
+    "ρ",
+    "σ",
+    None,
+    "θ",
+    "ε",
+    None,
+    "ϸ",
+    None,
+    "σ",
+    "ϻ",
+    None,
+    "ͻ",
+    "ͼ",
+    "ͽ",
+    "ѐ",
+    "ё",
+    "ђ",
+    "ѓ",
+    "є",
+    "ѕ",
+    "і",
+    "ї",
+    "ј",
+    "љ",
+    "њ",
+    "ћ",
+    "ќ",
+    "ѝ",
+    "ў",
+    "џ",
+    "а",
+    "б",
+    "в",
+    "г",
+    "д",
+    "е",
+    "ж",
+    "з",
+    "и",
+    "й",
+    "к",
+    "л",
+    "м",
+    "н",
+    "о",
+    "п",
+    "р",
+    "с",
+    "т",
+    "у",
+    "ф",
+    "х",
+    "ц",
+    "ч",
+    "ш",
+    "щ",
+    "ъ",
+    "ы",
+    "ь",
+    "э",
+    "ю",
+    "я",
+    None,
+    "ѡ",
+    None,
+    "ѣ",
+    None,
+    "ѥ",
+    None,
+    "ѧ",
+    None,
+    "ѩ",
+    None,
+    "ѫ",
+    None,
+    "ѭ",
+    None,
+    "ѯ",
+    None,
+    "ѱ",
+    None,
+    "ѳ",
+    None,
+    "ѵ",
+    None,
+    "ѷ",
+    None,
+    "ѹ",
+    None,
+    "ѻ",
+    None,
+    "ѽ",
+    None,
+    "ѿ",
+    None,
+    "ҁ",
+    None,
+    "ҋ",
+    None,
+    "ҍ",
+    None,
+    "ҏ",
+    None,
+    "ґ",
+    None,
+    "ғ",
+    None,
+    "ҕ",
+    None,
+    "җ",
+    None,
+    "ҙ",
+    None,
+    "қ",
+    None,
+    "ҝ",
+    None,
+    "ҟ",
+    None,
+    "ҡ",
+    None,
+    "ң",
+    None,
+    "ҥ",
+    None,
+    "ҧ",
+    None,
+    "ҩ",
+    None,
+    "ҫ",
+    None,
+    "ҭ",
+    None,
+    "ү",
+    None,
+    "ұ",
+    None,
+    "ҳ",
+    None,
+    "ҵ",
+    None,
+    "ҷ",
+    None,
+    "ҹ",
+    None,
+    "һ",
+    None,
+    "ҽ",
+    None,
+    "ҿ",
+    None,
+    "ӏ",
+    "ӂ",
+    None,
+    "ӄ",
+    None,
+    "ӆ",
+    None,
+    "ӈ",
+    None,
+    "ӊ",
+    None,
+    "ӌ",
+    None,
+    "ӎ",
+    None,
+    "ӑ",
+    None,
+    "ӓ",
+    None,
+    "ӕ",
+    None,
+    "ӗ",
+    None,
+    "ә",
+    None,
+    "ӛ",
+    None,
+    "ӝ",
+    None,
+    "ӟ",
+    None,
+    "ӡ",
+    None,
+    "ӣ",
+    None,
+    "ӥ",
+    None,
+    "ӧ",
+    None,
+    "ө",
+    None,
+    "ӫ",
+    None,
+    "ӭ",
+    None,
+    "ӯ",
+    None,
+    "ӱ",
+    None,
+    "ӳ",
+    None,
+    "ӵ",
+    None,
+    "ӷ",
+    None,
+    "ӹ",
+    None,
+    "ӻ",
+    None,
+    "ӽ",
+    None,
+    "ӿ",
+    None,
+    "ԁ",
+    None,
+    "ԃ",
+    None,
+    "ԅ",
+    None,
+    "ԇ",
+    None,
+    "ԉ",
+    None,
+    "ԋ",
+    None,
+    "ԍ",
+    None,
+    "ԏ",
+    None,
+    "ԑ",
+    None,
+    "ԓ",
+    None,
+    "ԕ",
+    None,
+    "ԗ",
+    None,
+    "ԙ",
+    None,
+    "ԛ",
+    None,
+    "ԝ",
+    None,
+    "ԟ",
+    None,
+    "ԡ",
+    None,
+    "ԣ",
+    None,
+    "ԥ",
+    None,
+    "ԧ",
+    None,
+    "ԩ",
+    None,
+    "ԫ",
+    None,
+    "ԭ",
+    None,
+    "ԯ",
+    None,
+    None,
+    "ա",
+    "բ",
+    "գ",
+    "դ",
+    "ե",
+    "զ",
+    "է",
+    "ը",
+    "թ",
+    "ժ",
+    "ի",
+    "լ",
+    "խ",
+    "ծ",
+    "կ",
+    "հ",
+    "ձ",
+    "ղ",
+    "ճ",
+    "մ",
+    "յ",
+    "ն",
+    "շ",
+    "ո",
+    "չ",
+    "պ",
+    "ջ",
+    "ռ",
+    "ս",
+    "վ",
+    "տ",
+    "ր",
+    "ց",
+    "ւ",
+    "փ",
+    "ք",
+    "օ",
+    "ֆ",
+    None,
+    None,
+    "եւ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "اٴ",
+    "وٴ",
+    "ۇٴ",
+    "يٴ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "क़",
+    "ख़",
+    "ग़",
+    "ज़",
+    "ड़",
+    "ढ़",
+    "फ़",
+    "य़",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ড়",
+    "ঢ়",
+    None,
+    "য়",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ਲ਼",
+    None,
+    None,
+    "ਸ਼",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ਖ਼",
+    "ਗ਼",
+    "ਜ਼",
+    None,
+    None,
+    "ਫ਼",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ଡ଼",
+    "ଢ଼",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ํา",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ໍາ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ຫນ",
+    "ຫມ",
+    None,
+    None,
+    None,
+    "་",
+    None,
+    "གྷ",
+    None,
+    None,
+    None,
+    "ཌྷ",
+    None,
+    "དྷ",
+    None,
+    "བྷ",
+    None,
+    "ཛྷ",
+    None,
+    "ཀྵ",
+    None,
+    None,
+    None,
+    "ཱི",
+    None,
+    "ཱུ",
+    "ྲྀ",
+    "ྲཱྀ",
+    "ླྀ",
+    "ླཱྀ",
+    None,
+    "ཱྀ",
+    None,
+    "ྒྷ",
+    None,
+    None,
+    None,
+    "ྜྷ",
+    None,
+    "ྡྷ",
+    None,
+    "ྦྷ",
+    None,
+    "ྫྷ",
+    None,
+    "ྐྵ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ⴀ",
+    "ⴁ",
+    "ⴂ",
+    "ⴃ",
+    "ⴄ",
+    "ⴅ",
+    "ⴆ",
+    "ⴇ",
+    "ⴈ",
+    "ⴉ",
+    "ⴊ",
+    "ⴋ",
+    "ⴌ",
+    "ⴍ",
+    "ⴎ",
+    "ⴏ",
+    "ⴐ",
+    "ⴑ",
+    "ⴒ",
+    "ⴓ",
+    "ⴔ",
+    "ⴕ",
+    "ⴖ",
+    "ⴗ",
+    "ⴘ",
+    "ⴙ",
+    "ⴚ",
+    "ⴛ",
+    "ⴜ",
+    "ⴝ",
+    "ⴞ",
+    "ⴟ",
+    "ⴠ",
+    "ⴡ",
+    "ⴢ",
+    "ⴣ",
+    "ⴤ",
+    "ⴥ",
+    None,
+    "ⴧ",
+    None,
+    "ⴭ",
+    None,
+    None,
+    "ნ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "Ᏸ",
+    "Ᏹ",
+    "Ᏺ",
+    "Ᏻ",
+    "Ᏼ",
+    "Ᏽ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "в",
+    "д",
+    "о",
+    "с",
+    "т",
+    "ъ",
+    "ѣ",
+    "ꙋ",
+    "\u1c8a",
+    None,
+    None,
+    "ა",
+    "ბ",
+    "გ",
+    "დ",
+    "ე",
+    "ვ",
+    "ზ",
+    "თ",
+    "ი",
+    "კ",
+    "ლ",
+    "მ",
+    "ნ",
+    "ო",
+    "პ",
+    "ჟ",
+    "რ",
+    "ს",
+    "ტ",
+    "უ",
+    "ფ",
+    "ქ",
+    "ღ",
+    "ყ",
+    "შ",
+    "ჩ",
+    "ც",
+    "ძ",
+    "წ",
+    "ჭ",
+    "ხ",
+    "ჯ",
+    "ჰ",
+    "ჱ",
+    "ჲ",
+    "ჳ",
+    "ჴ",
+    "ჵ",
+    "ჶ",
+    "ჷ",
+    "ჸ",
+    "ჹ",
+    "ჺ",
+    None,
+    "ჽ",
+    "ჾ",
+    "ჿ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    "a",
+    "æ",
+    "b",
+    None,
+    "d",
+    "e",
+    "ǝ",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    None,
+    "o",
+    "ȣ",
+    "p",
+    "r",
+    "t",
+    "u",
+    "w",
+    "a",
+    "ɐ",
+    "ɑ",
+    "ᴂ",
+    "b",
+    "d",
+    "e",
+    "ə",
+    "ɛ",
+    "ɜ",
+    "g",
+    None,
+    "k",
+    "m",
+    "ŋ",
+    "o",
+    "ɔ",
+    "ᴖ",
+    "ᴗ",
+    "p",
+    "t",
+    "u",
+    "ᴝ",
+    "ɯ",
+    "v",
+    "ᴥ",
+    "β",
+    "γ",
+    "δ",
+    "φ",
+    "χ",
+    "i",
+    "r",
+    "u",
+    "v",
+    "β",
+    "γ",
+    "ρ",
+    "φ",
+    "χ",
+    None,
+    "н",
+    None,
+    "ɒ",
+    "c",
+    "ɕ",
+    "ð",
+    "ɜ",
+    "f",
+    "ɟ",
+    "ɡ",
+    "ɥ",
+    "ɨ",
+    "ɩ",
+    "ɪ",
+    "ᵻ",
+    "ʝ",
+    "ɭ",
+    "ᶅ",
+    "ʟ",
+    "ɱ",
+    "ɰ",
+    "ɲ",
+    "ɳ",
+    "ɴ",
+    "ɵ",
+    "ɸ",
+    "ʂ",
+    "ʃ",
+    "ƫ",
+    "ʉ",
+    "ʊ",
+    "ᴜ",
+    "ʋ",
+    "ʌ",
+    "z",
+    "ʐ",
+    "ʑ",
+    "ʒ",
+    "θ",
+    None,
+    "ḁ",
+    None,
+    "ḃ",
+    None,
+    "ḅ",
+    None,
+    "ḇ",
+    None,
+    "ḉ",
+    None,
+    "ḋ",
+    None,
+    "ḍ",
+    None,
+    "ḏ",
+    None,
+    "ḑ",
+    None,
+    "ḓ",
+    None,
+    "ḕ",
+    None,
+    "ḗ",
+    None,
+    "ḙ",
+    None,
+    "ḛ",
+    None,
+    "ḝ",
+    None,
+    "ḟ",
+    None,
+    "ḡ",
+    None,
+    "ḣ",
+    None,
+    "ḥ",
+    None,
+    "ḧ",
+    None,
+    "ḩ",
+    None,
+    "ḫ",
+    None,
+    "ḭ",
+    None,
+    "ḯ",
+    None,
+    "ḱ",
+    None,
+    "ḳ",
+    None,
+    "ḵ",
+    None,
+    "ḷ",
+    None,
+    "ḹ",
+    None,
+    "ḻ",
+    None,
+    "ḽ",
+    None,
+    "ḿ",
+    None,
+    "ṁ",
+    None,
+    "ṃ",
+    None,
+    "ṅ",
+    None,
+    "ṇ",
+    None,
+    "ṉ",
+    None,
+    "ṋ",
+    None,
+    "ṍ",
+    None,
+    "ṏ",
+    None,
+    "ṑ",
+    None,
+    "ṓ",
+    None,
+    "ṕ",
+    None,
+    "ṗ",
+    None,
+    "ṙ",
+    None,
+    "ṛ",
+    None,
+    "ṝ",
+    None,
+    "ṟ",
+    None,
+    "ṡ",
+    None,
+    "ṣ",
+    None,
+    "ṥ",
+    None,
+    "ṧ",
+    None,
+    "ṩ",
+    None,
+    "ṫ",
+    None,
+    "ṭ",
+    None,
+    "ṯ",
+    None,
+    "ṱ",
+    None,
+    "ṳ",
+    None,
+    "ṵ",
+    None,
+    "ṷ",
+    None,
+    "ṹ",
+    None,
+    "ṻ",
+    None,
+    "ṽ",
+    None,
+    "ṿ",
+    None,
+    "ẁ",
+    None,
+    "ẃ",
+    None,
+    "ẅ",
+    None,
+    "ẇ",
+    None,
+    "ẉ",
+    None,
+    "ẋ",
+    None,
+    "ẍ",
+    None,
+    "ẏ",
+    None,
+    "ẑ",
+    None,
+    "ẓ",
+    None,
+    "ẕ",
+    None,
+    "aʾ",
+    "ṡ",
+    None,
+    "ß",
+    None,
+    "ạ",
+    None,
+    "ả",
+    None,
+    "ấ",
+    None,
+    "ầ",
+    None,
+    "ẩ",
+    None,
+    "ẫ",
+    None,
+    "ậ",
+    None,
+    "ắ",
+    None,
+    "ằ",
+    None,
+    "ẳ",
+    None,
+    "ẵ",
+    None,
+    "ặ",
+    None,
+    "ẹ",
+    None,
+    "ẻ",
+    None,
+    "ẽ",
+    None,
+    "ế",
+    None,
+    "ề",
+    None,
+    "ể",
+    None,
+    "ễ",
+    None,
+    "ệ",
+    None,
+    "ỉ",
+    None,
+    "ị",
+    None,
+    "ọ",
+    None,
+    "ỏ",
+    None,
+    "ố",
+    None,
+    "ồ",
+    None,
+    "ổ",
+    None,
+    "ỗ",
+    None,
+    "ộ",
+    None,
+    "ớ",
+    None,
+    "ờ",
+    None,
+    "ở",
+    None,
+    "ỡ",
+    None,
+    "ợ",
+    None,
+    "ụ",
+    None,
+    "ủ",
+    None,
+    "ứ",
+    None,
+    "ừ",
+    None,
+    "ử",
+    None,
+    "ữ",
+    None,
+    "ự",
+    None,
+    "ỳ",
+    None,
+    "ỵ",
+    None,
+    "ỷ",
+    None,
+    "ỹ",
+    None,
+    "ỻ",
+    None,
+    "ỽ",
+    None,
+    "ỿ",
+    None,
+    "ἀ",
+    "ἁ",
+    "ἂ",
+    "ἃ",
+    "ἄ",
+    "ἅ",
+    "ἆ",
+    "ἇ",
+    None,
+    None,
+    "ἐ",
+    "ἑ",
+    "ἒ",
+    "ἓ",
+    "ἔ",
+    "ἕ",
+    None,
+    None,
+    "ἠ",
+    "ἡ",
+    "ἢ",
+    "ἣ",
+    "ἤ",
+    "ἥ",
+    "ἦ",
+    "ἧ",
+    None,
+    "ἰ",
+    "ἱ",
+    "ἲ",
+    "ἳ",
+    "ἴ",
+    "ἵ",
+    "ἶ",
+    "ἷ",
+    None,
+    None,
+    "ὀ",
+    "ὁ",
+    "ὂ",
+    "ὃ",
+    "ὄ",
+    "ὅ",
+    None,
+    None,
+    None,
+    "ὑ",
+    None,
+    "ὓ",
+    None,
+    "ὕ",
+    None,
+    "ὗ",
+    None,
+    "ὠ",
+    "ὡ",
+    "ὢ",
+    "ὣ",
+    "ὤ",
+    "ὥ",
+    "ὦ",
+    "ὧ",
+    None,
+    "ά",
+    None,
+    "έ",
+    None,
+    "ή",
+    None,
+    "ί",
+    None,
+    "ό",
+    None,
+    "ύ",
+    None,
+    "ώ",
+    None,
+    "ἀι",
+    "ἁι",
+    "ἂι",
+    "ἃι",
+    "ἄι",
+    "ἅι",
+    "ἆι",
+    "ἇι",
+    "ἀι",
+    "ἁι",
+    "ἂι",
+    "ἃι",
+    "ἄι",
+    "ἅι",
+    "ἆι",
+    "ἇι",
+    "ἠι",
+    "ἡι",
+    "ἢι",
+    "ἣι",
+    "ἤι",
+    "ἥι",
+    "ἦι",
+    "ἧι",
+    "ἠι",
+    "ἡι",
+    "ἢι",
+    "ἣι",
+    "ἤι",
+    "ἥι",
+    "ἦι",
+    "ἧι",
+    "ὠι",
+    "ὡι",
+    "ὢι",
+    "ὣι",
+    "ὤι",
+    "ὥι",
+    "ὦι",
+    "ὧι",
+    "ὠι",
+    "ὡι",
+    "ὢι",
+    "ὣι",
+    "ὤι",
+    "ὥι",
+    "ὦι",
+    "ὧι",
+    None,
+    "ὰι",
+    "αι",
+    "άι",
+    None,
+    None,
+    "ᾶι",
+    "ᾰ",
+    "ᾱ",
+    "ὰ",
+    "ά",
+    "αι",
+    " ̓",
+    "ι",
+    " ̓",
+    " ͂",
+    " ̈͂",
+    "ὴι",
+    "ηι",
+    "ήι",
+    None,
+    None,
+    "ῆι",
+    "ὲ",
+    "έ",
+    "ὴ",
+    "ή",
+    "ηι",
+    " ̓̀",
+    " ̓́",
+    " ̓͂",
+    None,
+    "ΐ",
+    None,
+    None,
+    "ῐ",
+    "ῑ",
+    "ὶ",
+    "ί",
+    None,
+    " ̔̀",
+    " ̔́",
+    " ̔͂",
+    None,
+    "ΰ",
+    None,
+    "ῠ",
+    "ῡ",
+    "ὺ",
+    "ύ",
+    "ῥ",
+    " ̈̀",
+    " ̈́",
+    "`",
+    None,
+    "ὼι",
+    "ωι",
+    "ώι",
+    None,
+    None,
+    "ῶι",
+    "ὸ",
+    "ό",
+    "ὼ",
+    "ώ",
+    "ωι",
+    " ́",
+    " ̔",
+    None,
+    " ",
+    None,
+    "",
+    None,
+    None,
+    "‐",
+    None,
+    " ̳",
+    None,
+    None,
+    None,
+    None,
+    " ",
+    None,
+    "′′",
+    "′′′",
+    None,
+    "‵‵",
+    "‵‵‵",
+    None,
+    "!!",
+    None,
+    " ̅",
+    None,
+    "??",
+    "?!",
+    "!?",
+    None,
+    "′′′′",
+    None,
+    " ",
+    None,
+    None,
+    None,
+    "0",
+    "i",
+    None,
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    "+",
+    "−",
+    "=",
+    "(",
+    ")",
+    "n",
+    "0",
+    "1",
+    "2",
+    "3",
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    "+",
+    "−",
+    "=",
+    "(",
+    ")",
+    None,
+    "a",
+    "e",
+    "o",
+    "x",
+    "ə",
+    "h",
+    "k",
+    "l",
+    "m",
+    "n",
+    "p",
+    "s",
+    "t",
+    None,
+    None,
+    "rs",
+    None,
+    None,
+    None,
+    None,
+    "a/c",
+    "a/s",
+    "c",
+    "°c",
+    None,
+    "c/o",
+    "c/u",
+    "ɛ",
+    None,
+    "°f",
+    "g",
+    "h",
+    "ħ",
+    "i",
+    "l",
+    None,
+    "n",
+    "no",
+    None,
+    "p",
+    "q",
+    "r",
+    None,
+    "sm",
+    "tel",
+    "tm",
+    None,
+    "z",
+    None,
+    "ω",
+    None,
+    "z",
+    None,
+    "k",
+    "å",
+    "b",
+    "c",
+    None,
+    "e",
+    "f",
+    "ⅎ",
+    "m",
+    "o",
+    "א",
+    "ב",
+    "ג",
+    "ד",
+    "i",
+    None,
+    "fax",
+    "π",
+    "γ",
+    "π",
+    "∑",
+    None,
+    "d",
+    "e",
+    "i",
+    "j",
+    None,
+    "1⁄7",
+    "1⁄9",
+    "1⁄10",
+    "1⁄3",
+    "2⁄3",
+    "1⁄5",
+    "2⁄5",
+    "3⁄5",
+    "4⁄5",
+    "1⁄6",
+    "5⁄6",
+    "1⁄8",
+    "3⁄8",
+    "5⁄8",
+    "7⁄8",
+    "1⁄",
+    "i",
+    "ii",
+    "iii",
+    "iv",
+    "v",
+    "vi",
+    "vii",
+    "viii",
+    "ix",
+    "x",
+    "xi",
+    "xii",
+    "l",
+    "c",
+    "d",
+    "m",
+    "i",
+    "ii",
+    "iii",
+    "iv",
+    "v",
+    "vi",
+    "vii",
+    "viii",
+    "ix",
+    "x",
+    "xi",
+    "xii",
+    "l",
+    "c",
+    "d",
+    "m",
+    None,
+    "ↄ",
+    None,
+    "0⁄3",
+    None,
+    None,
+    None,
+    "∫∫",
+    "∫∫∫",
+    None,
+    "∮∮",
+    "∮∮∮",
+    None,
+    "〈",
+    "〉",
+    None,
+    None,
+    None,
+    None,
+    "1",
+    "2",
+    "3",
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    "10",
+    "11",
+    "12",
+    "13",
+    "14",
+    "15",
+    "16",
+    "17",
+    "18",
+    "19",
+    "20",
+    "(1)",
+    "(2)",
+    "(3)",
+    "(4)",
+    "(5)",
+    "(6)",
+    "(7)",
+    "(8)",
+    "(9)",
+    "(10)",
+    "(11)",
+    "(12)",
+    "(13)",
+    "(14)",
+    "(15)",
+    "(16)",
+    "(17)",
+    "(18)",
+    "(19)",
+    "(20)",
+    None,
+    "(a)",
+    "(b)",
+    "(c)",
+    "(d)",
+    "(e)",
+    "(f)",
+    "(g)",
+    "(h)",
+    "(i)",
+    "(j)",
+    "(k)",
+    "(l)",
+    "(m)",
+    "(n)",
+    "(o)",
+    "(p)",
+    "(q)",
+    "(r)",
+    "(s)",
+    "(t)",
+    "(u)",
+    "(v)",
+    "(w)",
+    "(x)",
+    "(y)",
+    "(z)",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "0",
+    None,
+    "∫∫∫∫",
+    None,
+    "::=",
+    "==",
+    "===",
+    None,
+    "⫝̸",
+    None,
+    None,
+    None,
+    "ⰰ",
+    "ⰱ",
+    "ⰲ",
+    "ⰳ",
+    "ⰴ",
+    "ⰵ",
+    "ⰶ",
+    "ⰷ",
+    "ⰸ",
+    "ⰹ",
+    "ⰺ",
+    "ⰻ",
+    "ⰼ",
+    "ⰽ",
+    "ⰾ",
+    "ⰿ",
+    "ⱀ",
+    "ⱁ",
+    "ⱂ",
+    "ⱃ",
+    "ⱄ",
+    "ⱅ",
+    "ⱆ",
+    "ⱇ",
+    "ⱈ",
+    "ⱉ",
+    "ⱊ",
+    "ⱋ",
+    "ⱌ",
+    "ⱍ",
+    "ⱎ",
+    "ⱏ",
+    "ⱐ",
+    "ⱑ",
+    "ⱒ",
+    "ⱓ",
+    "ⱔ",
+    "ⱕ",
+    "ⱖ",
+    "ⱗ",
+    "ⱘ",
+    "ⱙ",
+    "ⱚ",
+    "ⱛ",
+    "ⱜ",
+    "ⱝ",
+    "ⱞ",
+    "ⱟ",
+    None,
+    "ⱡ",
+    None,
+    "ɫ",
+    "ᵽ",
+    "ɽ",
+    None,
+    "ⱨ",
+    None,
+    "ⱪ",
+    None,
+    "ⱬ",
+    None,
+    "ɑ",
+    "ɱ",
+    "ɐ",
+    "ɒ",
+    None,
+    "ⱳ",
+    None,
+    "ⱶ",
+    None,
+    "j",
+    "v",
+    "ȿ",
+    "ɀ",
+    "ⲁ",
+    None,
+    "ⲃ",
+    None,
+    "ⲅ",
+    None,
+    "ⲇ",
+    None,
+    "ⲉ",
+    None,
+    "ⲋ",
+    None,
+    "ⲍ",
+    None,
+    "ⲏ",
+    None,
+    "ⲑ",
+    None,
+    "ⲓ",
+    None,
+    "ⲕ",
+    None,
+    "ⲗ",
+    None,
+    "ⲙ",
+    None,
+    "ⲛ",
+    None,
+    "ⲝ",
+    None,
+    "ⲟ",
+    None,
+    "ⲡ",
+    None,
+    "ⲣ",
+    None,
+    "ⲥ",
+    None,
+    "ⲧ",
+    None,
+    "ⲩ",
+    None,
+    "ⲫ",
+    None,
+    "ⲭ",
+    None,
+    "ⲯ",
+    None,
+    "ⲱ",
+    None,
+    "ⲳ",
+    None,
+    "ⲵ",
+    None,
+    "ⲷ",
+    None,
+    "ⲹ",
+    None,
+    "ⲻ",
+    None,
+    "ⲽ",
+    None,
+    "ⲿ",
+    None,
+    "ⳁ",
+    None,
+    "ⳃ",
+    None,
+    "ⳅ",
+    None,
+    "ⳇ",
+    None,
+    "ⳉ",
+    None,
+    "ⳋ",
+    None,
+    "ⳍ",
+    None,
+    "ⳏ",
+    None,
+    "ⳑ",
+    None,
+    "ⳓ",
+    None,
+    "ⳕ",
+    None,
+    "ⳗ",
+    None,
+    "ⳙ",
+    None,
+    "ⳛ",
+    None,
+    "ⳝ",
+    None,
+    "ⳟ",
+    None,
+    "ⳡ",
+    None,
+    "ⳣ",
+    None,
+    "ⳬ",
+    None,
+    "ⳮ",
+    None,
+    "ⳳ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ⵡ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "母",
+    None,
+    "龟",
+    None,
+    "一",
+    "丨",
+    "丶",
+    "丿",
+    "乙",
+    "亅",
+    "二",
+    "亠",
+    "人",
+    "儿",
+    "入",
+    "八",
+    "冂",
+    "冖",
+    "冫",
+    "几",
+    "凵",
+    "刀",
+    "力",
+    "勹",
+    "匕",
+    "匚",
+    "匸",
+    "十",
+    "卜",
+    "卩",
+    "厂",
+    "厶",
+    "又",
+    "口",
+    "囗",
+    "土",
+    "士",
+    "夂",
+    "夊",
+    "夕",
+    "大",
+    "女",
+    "子",
+    "宀",
+    "寸",
+    "小",
+    "尢",
+    "尸",
+    "屮",
+    "山",
+    "巛",
+    "工",
+    "己",
+    "巾",
+    "干",
+    "幺",
+    "广",
+    "廴",
+    "廾",
+    "弋",
+    "弓",
+    "彐",
+    "彡",
+    "彳",
+    "心",
+    "戈",
+    "戶",
+    "手",
+    "支",
+    "攴",
+    "文",
+    "斗",
+    "斤",
+    "方",
+    "无",
+    "日",
+    "曰",
+    "月",
+    "木",
+    "欠",
+    "止",
+    "歹",
+    "殳",
+    "毋",
+    "比",
+    "毛",
+    "氏",
+    "气",
+    "水",
+    "火",
+    "爪",
+    "父",
+    "爻",
+    "爿",
+    "片",
+    "牙",
+    "牛",
+    "犬",
+    "玄",
+    "玉",
+    "瓜",
+    "瓦",
+    "甘",
+    "生",
+    "用",
+    "田",
+    "疋",
+    "疒",
+    "癶",
+    "白",
+    "皮",
+    "皿",
+    "目",
+    "矛",
+    "矢",
+    "石",
+    "示",
+    "禸",
+    "禾",
+    "穴",
+    "立",
+    "竹",
+    "米",
+    "糸",
+    "缶",
+    "网",
+    "羊",
+    "羽",
+    "老",
+    "而",
+    "耒",
+    "耳",
+    "聿",
+    "肉",
+    "臣",
+    "自",
+    "至",
+    "臼",
+    "舌",
+    "舛",
+    "舟",
+    "艮",
+    "色",
+    "艸",
+    "虍",
+    "虫",
+    "血",
+    "行",
+    "衣",
+    "襾",
+    "見",
+    "角",
+    "言",
+    "谷",
+    "豆",
+    "豕",
+    "豸",
+    "貝",
+    "赤",
+    "走",
+    "足",
+    "身",
+    "車",
+    "辛",
+    "辰",
+    "辵",
+    "邑",
+    "酉",
+    "釆",
+    "里",
+    "金",
+    "長",
+    "門",
+    "阜",
+    "隶",
+    "隹",
+    "雨",
+    "靑",
+    "非",
+    "面",
+    "革",
+    "韋",
+    "韭",
+    "音",
+    "頁",
+    "風",
+    "飛",
+    "食",
+    "首",
+    "香",
+    "馬",
+    "骨",
+    "高",
+    "髟",
+    "鬥",
+    "鬯",
+    "鬲",
+    "鬼",
+    "魚",
+    "鳥",
+    "鹵",
+    "鹿",
+    "麥",
+    "麻",
+    "黃",
+    "黍",
+    "黑",
+    "黹",
+    "黽",
+    "鼎",
+    "鼓",
+    "鼠",
+    "鼻",
+    "齊",
+    "齒",
+    "龍",
+    "龜",
+    "龠",
+    None,
+    " ",
+    None,
+    ".",
+    None,
+    "〒",
+    None,
+    "十",
+    "卄",
+    "卅",
+    None,
+    None,
+    None,
+    None,
+    None,
+    " ゙",
+    " ゚",
+    None,
+    "より",
+    None,
+    "コト",
+    None,
+    None,
+    None,
+    "ᄀ",
+    "ᄁ",
+    "ᆪ",
+    "ᄂ",
+    "ᆬ",
+    "ᆭ",
+    "ᄃ",
+    "ᄄ",
+    "ᄅ",
+    "ᆰ",
+    "ᆱ",
+    "ᆲ",
+    "ᆳ",
+    "ᆴ",
+    "ᆵ",
+    "ᄚ",
+    "ᄆ",
+    "ᄇ",
+    "ᄈ",
+    "ᄡ",
+    "ᄉ",
+    "ᄊ",
+    "ᄋ",
+    "ᄌ",
+    "ᄍ",
+    "ᄎ",
+    "ᄏ",
+    "ᄐ",
+    "ᄑ",
+    "ᄒ",
+    "ᅡ",
+    "ᅢ",
+    "ᅣ",
+    "ᅤ",
+    "ᅥ",
+    "ᅦ",
+    "ᅧ",
+    "ᅨ",
+    "ᅩ",
+    "ᅪ",
+    "ᅫ",
+    "ᅬ",
+    "ᅭ",
+    "ᅮ",
+    "ᅯ",
+    "ᅰ",
+    "ᅱ",
+    "ᅲ",
+    "ᅳ",
+    "ᅴ",
+    "ᅵ",
+    None,
+    "ᄔ",
+    "ᄕ",
+    "ᇇ",
+    "ᇈ",
+    "ᇌ",
+    "ᇎ",
+    "ᇓ",
+    "ᇗ",
+    "ᇙ",
+    "ᄜ",
+    "ᇝ",
+    "ᇟ",
+    "ᄝ",
+    "ᄞ",
+    "ᄠ",
+    "ᄢ",
+    "ᄣ",
+    "ᄧ",
+    "ᄩ",
+    "ᄫ",
+    "ᄬ",
+    "ᄭ",
+    "ᄮ",
+    "ᄯ",
+    "ᄲ",
+    "ᄶ",
+    "ᅀ",
+    "ᅇ",
+    "ᅌ",
+    "ᇱ",
+    "ᇲ",
+    "ᅗ",
+    "ᅘ",
+    "ᅙ",
+    "ᆄ",
+    "ᆅ",
+    "ᆈ",
+    "ᆑ",
+    "ᆒ",
+    "ᆔ",
+    "ᆞ",
+    "ᆡ",
+    None,
+    None,
+    "一",
+    "二",
+    "三",
+    "四",
+    "上",
+    "中",
+    "下",
+    "甲",
+    "乙",
+    "丙",
+    "丁",
+    "天",
+    "地",
+    "人",
+    None,
+    None,
+    None,
+    "(ᄀ)",
+    "(ᄂ)",
+    "(ᄃ)",
+    "(ᄅ)",
+    "(ᄆ)",
+    "(ᄇ)",
+    "(ᄉ)",
+    "(ᄋ)",
+    "(ᄌ)",
+    "(ᄎ)",
+    "(ᄏ)",
+    "(ᄐ)",
+    "(ᄑ)",
+    "(ᄒ)",
+    "(가)",
+    "(나)",
+    "(다)",
+    "(라)",
+    "(마)",
+    "(바)",
+    "(사)",
+    "(아)",
+    "(자)",
+    "(차)",
+    "(카)",
+    "(타)",
+    "(파)",
+    "(하)",
+    "(주)",
+    "(오전)",
+    "(오후)",
+    None,
+    "(一)",
+    "(二)",
+    "(三)",
+    "(四)",
+    "(五)",
+    "(六)",
+    "(七)",
+    "(八)",
+    "(九)",
+    "(十)",
+    "(月)",
+    "(火)",
+    "(水)",
+    "(木)",
+    "(金)",
+    "(土)",
+    "(日)",
+    "(株)",
+    "(有)",
+    "(社)",
+    "(名)",
+    "(特)",
+    "(財)",
+    "(祝)",
+    "(労)",
+    "(代)",
+    "(呼)",
+    "(学)",
+    "(監)",
+    "(企)",
+    "(資)",
+    "(協)",
+    "(祭)",
+    "(休)",
+    "(自)",
+    "(至)",
+    "問",
+    "幼",
+    "文",
+    "箏",
+    None,
+    "pte",
+    "21",
+    "22",
+    "23",
+    "24",
+    "25",
+    "26",
+    "27",
+    "28",
+    "29",
+    "30",
+    "31",
+    "32",
+    "33",
+    "34",
+    "35",
+    "ᄀ",
+    "ᄂ",
+    "ᄃ",
+    "ᄅ",
+    "ᄆ",
+    "ᄇ",
+    "ᄉ",
+    "ᄋ",
+    "ᄌ",
+    "ᄎ",
+    "ᄏ",
+    "ᄐ",
+    "ᄑ",
+    "ᄒ",
+    "가",
+    "나",
+    "다",
+    "라",
+    "마",
+    "바",
+    "사",
+    "아",
+    "자",
+    "차",
+    "카",
+    "타",
+    "파",
+    "하",
+    "참고",
+    "주의",
+    "우",
+    None,
+    "一",
+    "二",
+    "三",
+    "四",
+    "五",
+    "六",
+    "七",
+    "八",
+    "九",
+    "十",
+    "月",
+    "火",
+    "水",
+    "木",
+    "金",
+    "土",
+    "日",
+    "株",
+    "有",
+    "社",
+    "名",
+    "特",
+    "財",
+    "祝",
+    "労",
+    "秘",
+    "男",
+    "女",
+    "適",
+    "優",
+    "印",
+    "注",
+    "項",
+    "休",
+    "写",
+    "正",
+    "上",
+    "中",
+    "下",
+    "左",
+    "右",
+    "医",
+    "宗",
+    "学",
+    "監",
+    "企",
+    "資",
+    "協",
+    "夜",
+    "36",
+    "37",
+    "38",
+    "39",
+    "40",
+    "41",
+    "42",
+    "43",
+    "44",
+    "45",
+    "46",
+    "47",
+    "48",
+    "49",
+    "50",
+    "1月",
+    "2月",
+    "3月",
+    "4月",
+    "5月",
+    "6月",
+    "7月",
+    "8月",
+    "9月",
+    "10月",
+    "11月",
+    "12月",
+    "hg",
+    "erg",
+    "ev",
+    "ltd",
+    "ア",
+    "イ",
+    "ウ",
+    "エ",
+    "オ",
+    "カ",
+    "キ",
+    "ク",
+    "ケ",
+    "コ",
+    "サ",
+    "シ",
+    "ス",
+    "セ",
+    "ソ",
+    "タ",
+    "チ",
+    "ツ",
+    "テ",
+    "ト",
+    "ナ",
+    "ニ",
+    "ヌ",
+    "ネ",
+    "ノ",
+    "ハ",
+    "ヒ",
+    "フ",
+    "ヘ",
+    "ホ",
+    "マ",
+    "ミ",
+    "ム",
+    "メ",
+    "モ",
+    "ヤ",
+    "ユ",
+    "ヨ",
+    "ラ",
+    "リ",
+    "ル",
+    "レ",
+    "ロ",
+    "ワ",
+    "ヰ",
+    "ヱ",
+    "ヲ",
+    "令和",
+    "アパート",
+    "アルファ",
+    "アンペア",
+    "アール",
+    "イニング",
+    "インチ",
+    "ウォン",
+    "エスクード",
+    "エーカー",
+    "オンス",
+    "オーム",
+    "カイリ",
+    "カラット",
+    "カロリー",
+    "ガロン",
+    "ガンマ",
+    "ギガ",
+    "ギニー",
+    "キュリー",
+    "ギルダー",
+    "キロ",
+    "キログラム",
+    "キロメートル",
+    "キロワット",
+    "グラム",
+    "グラムトン",
+    "クルゼイロ",
+    "クローネ",
+    "ケース",
+    "コルナ",
+    "コーポ",
+    "サイクル",
+    "サンチーム",
+    "シリング",
+    "センチ",
+    "セント",
+    "ダース",
+    "デシ",
+    "ドル",
+    "トン",
+    "ナノ",
+    "ノット",
+    "ハイツ",
+    "パーセント",
+    "パーツ",
+    "バーレル",
+    "ピアストル",
+    "ピクル",
+    "ピコ",
+    "ビル",
+    "ファラッド",
+    "フィート",
+    "ブッシェル",
+    "フラン",
+    "ヘクタール",
+    "ペソ",
+    "ペニヒ",
+    "ヘルツ",
+    "ペンス",
+    "ページ",
+    "ベータ",
+    "ポイント",
+    "ボルト",
+    "ホン",
+    "ポンド",
+    "ホール",
+    "ホーン",
+    "マイクロ",
+    "マイル",
+    "マッハ",
+    "マルク",
+    "マンション",
+    "ミクロン",
+    "ミリ",
+    "ミリバール",
+    "メガ",
+    "メガトン",
+    "メートル",
+    "ヤード",
+    "ヤール",
+    "ユアン",
+    "リットル",
+    "リラ",
+    "ルピー",
+    "ルーブル",
+    "レム",
+    "レントゲン",
+    "ワット",
+    "0点",
+    "1点",
+    "2点",
+    "3点",
+    "4点",
+    "5点",
+    "6点",
+    "7点",
+    "8点",
+    "9点",
+    "10点",
+    "11点",
+    "12点",
+    "13点",
+    "14点",
+    "15点",
+    "16点",
+    "17点",
+    "18点",
+    "19点",
+    "20点",
+    "21点",
+    "22点",
+    "23点",
+    "24点",
+    "hpa",
+    "da",
+    "au",
+    "bar",
+    "ov",
+    "pc",
+    "dm",
+    "dm2",
+    "dm3",
+    "iu",
+    "平成",
+    "昭和",
+    "大正",
+    "明治",
+    "株式会社",
+    "pa",
+    "na",
+    "μa",
+    "ma",
+    "ka",
+    "kb",
+    "mb",
+    "gb",
+    "cal",
+    "kcal",
+    "pf",
+    "nf",
+    "μf",
+    "μg",
+    "mg",
+    "kg",
+    "hz",
+    "khz",
+    "mhz",
+    "ghz",
+    "thz",
+    "μl",
+    "ml",
+    "dl",
+    "kl",
+    "fm",
+    "nm",
+    "μm",
+    "mm",
+    "cm",
+    "km",
+    "mm2",
+    "cm2",
+    "m2",
+    "km2",
+    "mm3",
+    "cm3",
+    "m3",
+    "km3",
+    "m∕s",
+    "m∕s2",
+    "pa",
+    "kpa",
+    "mpa",
+    "gpa",
+    "rad",
+    "rad∕s",
+    "rad∕s2",
+    "ps",
+    "ns",
+    "μs",
+    "ms",
+    "pv",
+    "nv",
+    "μv",
+    "mv",
+    "kv",
+    "mv",
+    "pw",
+    "nw",
+    "μw",
+    "mw",
+    "kw",
+    "mw",
+    "kω",
+    "mω",
+    None,
+    "bq",
+    "cc",
+    "cd",
+    "c∕kg",
+    None,
+    "db",
+    "gy",
+    "ha",
+    "hp",
+    "in",
+    "kk",
+    "km",
+    "kt",
+    "lm",
+    "ln",
+    "log",
+    "lx",
+    "mb",
+    "mil",
+    "mol",
+    "ph",
+    None,
+    "ppm",
+    "pr",
+    "sr",
+    "sv",
+    "wb",
+    "v∕m",
+    "a∕m",
+    "1日",
+    "2日",
+    "3日",
+    "4日",
+    "5日",
+    "6日",
+    "7日",
+    "8日",
+    "9日",
+    "10日",
+    "11日",
+    "12日",
+    "13日",
+    "14日",
+    "15日",
+    "16日",
+    "17日",
+    "18日",
+    "19日",
+    "20日",
+    "21日",
+    "22日",
+    "23日",
+    "24日",
+    "25日",
+    "26日",
+    "27日",
+    "28日",
+    "29日",
+    "30日",
+    "31日",
+    "gal",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ꙁ",
+    None,
+    "ꙃ",
+    None,
+    "ꙅ",
+    None,
+    "ꙇ",
+    None,
+    "ꙉ",
+    None,
+    "ꙋ",
+    None,
+    "ꙍ",
+    None,
+    "ꙏ",
+    None,
+    "ꙑ",
+    None,
+    "ꙓ",
+    None,
+    "ꙕ",
+    None,
+    "ꙗ",
+    None,
+    "ꙙ",
+    None,
+    "ꙛ",
+    None,
+    "ꙝ",
+    None,
+    "ꙟ",
+    None,
+    "ꙡ",
+    None,
+    "ꙣ",
+    None,
+    "ꙥ",
+    None,
+    "ꙧ",
+    None,
+    "ꙩ",
+    None,
+    "ꙫ",
+    None,
+    "ꙭ",
+    None,
+    "ꚁ",
+    None,
+    "ꚃ",
+    None,
+    "ꚅ",
+    None,
+    "ꚇ",
+    None,
+    "ꚉ",
+    None,
+    "ꚋ",
+    None,
+    "ꚍ",
+    None,
+    "ꚏ",
+    None,
+    "ꚑ",
+    None,
+    "ꚓ",
+    None,
+    "ꚕ",
+    None,
+    "ꚗ",
+    None,
+    "ꚙ",
+    None,
+    "ꚛ",
+    None,
+    "ъ",
+    "ь",
+    None,
+    None,
+    None,
+    "ꜣ",
+    None,
+    "ꜥ",
+    None,
+    "ꜧ",
+    None,
+    "ꜩ",
+    None,
+    "ꜫ",
+    None,
+    "ꜭ",
+    None,
+    "ꜯ",
+    None,
+    "ꜳ",
+    None,
+    "ꜵ",
+    None,
+    "ꜷ",
+    None,
+    "ꜹ",
+    None,
+    "ꜻ",
+    None,
+    "ꜽ",
+    None,
+    "ꜿ",
+    None,
+    "ꝁ",
+    None,
+    "ꝃ",
+    None,
+    "ꝅ",
+    None,
+    "ꝇ",
+    None,
+    "ꝉ",
+    None,
+    "ꝋ",
+    None,
+    "ꝍ",
+    None,
+    "ꝏ",
+    None,
+    "ꝑ",
+    None,
+    "ꝓ",
+    None,
+    "ꝕ",
+    None,
+    "ꝗ",
+    None,
+    "ꝙ",
+    None,
+    "ꝛ",
+    None,
+    "ꝝ",
+    None,
+    "ꝟ",
+    None,
+    "ꝡ",
+    None,
+    "ꝣ",
+    None,
+    "ꝥ",
+    None,
+    "ꝧ",
+    None,
+    "ꝩ",
+    None,
+    "ꝫ",
+    None,
+    "ꝭ",
+    None,
+    "ꝯ",
+    None,
+    "ꝯ",
+    None,
+    "ꝺ",
+    None,
+    "ꝼ",
+    None,
+    "ᵹ",
+    "ꝿ",
+    None,
+    "ꞁ",
+    None,
+    "ꞃ",
+    None,
+    "ꞅ",
+    None,
+    "ꞇ",
+    None,
+    "ꞌ",
+    None,
+    "ɥ",
+    None,
+    "ꞑ",
+    None,
+    "ꞓ",
+    None,
+    "ꞗ",
+    None,
+    "ꞙ",
+    None,
+    "ꞛ",
+    None,
+    "ꞝ",
+    None,
+    "ꞟ",
+    None,
+    "ꞡ",
+    None,
+    "ꞣ",
+    None,
+    "ꞥ",
+    None,
+    "ꞧ",
+    None,
+    "ꞩ",
+    None,
+    "ɦ",
+    "ɜ",
+    "ɡ",
+    "ɬ",
+    "ɪ",
+    None,
+    "ʞ",
+    "ʇ",
+    "ʝ",
+    "ꭓ",
+    "ꞵ",
+    None,
+    "ꞷ",
+    None,
+    "ꞹ",
+    None,
+    "ꞻ",
+    None,
+    "ꞽ",
+    None,
+    "ꞿ",
+    None,
+    "ꟁ",
+    None,
+    "ꟃ",
+    None,
+    "ꞔ",
+    "ʂ",
+    "ᶎ",
+    "ꟈ",
+    None,
+    "ꟊ",
+    None,
+    "ɤ",
+    "\ua7cd",
+    None,
+    "\ua7cf",
+    None,
+    "ꟑ",
+    None,
+    "ꟓ",
+    None,
+    "ꟕ",
+    None,
+    "ꟗ",
+    None,
+    "ꟙ",
+    None,
+    "\ua7db",
+    None,
+    "ƛ",
+    None,
+    "s",
+    "c",
+    "f",
+    "q",
+    "ꟶ",
+    None,
+    "ħ",
+    "œ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ꜧ",
+    "ꬷ",
+    "ɫ",
+    "ꭒ",
+    None,
+    "ʍ",
+    None,
+    None,
+    "Ꭰ",
+    "Ꭱ",
+    "Ꭲ",
+    "Ꭳ",
+    "Ꭴ",
+    "Ꭵ",
+    "Ꭶ",
+    "Ꭷ",
+    "Ꭸ",
+    "Ꭹ",
+    "Ꭺ",
+    "Ꭻ",
+    "Ꭼ",
+    "Ꭽ",
+    "Ꭾ",
+    "Ꭿ",
+    "Ꮀ",
+    "Ꮁ",
+    "Ꮂ",
+    "Ꮃ",
+    "Ꮄ",
+    "Ꮅ",
+    "Ꮆ",
+    "Ꮇ",
+    "Ꮈ",
+    "Ꮉ",
+    "Ꮊ",
+    "Ꮋ",
+    "Ꮌ",
+    "Ꮍ",
+    "Ꮎ",
+    "Ꮏ",
+    "Ꮐ",
+    "Ꮑ",
+    "Ꮒ",
+    "Ꮓ",
+    "Ꮔ",
+    "Ꮕ",
+    "Ꮖ",
+    "Ꮗ",
+    "Ꮘ",
+    "Ꮙ",
+    "Ꮚ",
+    "Ꮛ",
+    "Ꮜ",
+    "Ꮝ",
+    "Ꮞ",
+    "Ꮟ",
+    "Ꮠ",
+    "Ꮡ",
+    "Ꮢ",
+    "Ꮣ",
+    "Ꮤ",
+    "Ꮥ",
+    "Ꮦ",
+    "Ꮧ",
+    "Ꮨ",
+    "Ꮩ",
+    "Ꮪ",
+    "Ꮫ",
+    "Ꮬ",
+    "Ꮭ",
+    "Ꮮ",
+    "Ꮯ",
+    "Ꮰ",
+    "Ꮱ",
+    "Ꮲ",
+    "Ꮳ",
+    "Ꮴ",
+    "Ꮵ",
+    "Ꮶ",
+    "Ꮷ",
+    "Ꮸ",
+    "Ꮹ",
+    "Ꮺ",
+    "Ꮻ",
+    "Ꮼ",
+    "Ꮽ",
+    "Ꮾ",
+    "Ꮿ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "豈",
+    "更",
+    "車",
+    "賈",
+    "滑",
+    "串",
+    "句",
+    "龜",
+    "契",
+    "金",
+    "喇",
+    "奈",
+    "懶",
+    "癩",
+    "羅",
+    "蘿",
+    "螺",
+    "裸",
+    "邏",
+    "樂",
+    "洛",
+    "烙",
+    "珞",
+    "落",
+    "酪",
+    "駱",
+    "亂",
+    "卵",
+    "欄",
+    "爛",
+    "蘭",
+    "鸞",
+    "嵐",
+    "濫",
+    "藍",
+    "襤",
+    "拉",
+    "臘",
+    "蠟",
+    "廊",
+    "朗",
+    "浪",
+    "狼",
+    "郎",
+    "來",
+    "冷",
+    "勞",
+    "擄",
+    "櫓",
+    "爐",
+    "盧",
+    "老",
+    "蘆",
+    "虜",
+    "路",
+    "露",
+    "魯",
+    "鷺",
+    "碌",
+    "祿",
+    "綠",
+    "菉",
+    "錄",
+    "鹿",
+    "論",
+    "壟",
+    "弄",
+    "籠",
+    "聾",
+    "牢",
+    "磊",
+    "賂",
+    "雷",
+    "壘",
+    "屢",
+    "樓",
+    "淚",
+    "漏",
+    "累",
+    "縷",
+    "陋",
+    "勒",
+    "肋",
+    "凜",
+    "凌",
+    "稜",
+    "綾",
+    "菱",
+    "陵",
+    "讀",
+    "拏",
+    "樂",
+    "諾",
+    "丹",
+    "寧",
+    "怒",
+    "率",
+    "異",
+    "北",
+    "磻",
+    "便",
+    "復",
+    "不",
+    "泌",
+    "數",
+    "索",
+    "參",
+    "塞",
+    "省",
+    "葉",
+    "說",
+    "殺",
+    "辰",
+    "沈",
+    "拾",
+    "若",
+    "掠",
+    "略",
+    "亮",
+    "兩",
+    "凉",
+    "梁",
+    "糧",
+    "良",
+    "諒",
+    "量",
+    "勵",
+    "呂",
+    "女",
+    "廬",
+    "旅",
+    "濾",
+    "礪",
+    "閭",
+    "驪",
+    "麗",
+    "黎",
+    "力",
+    "曆",
+    "歷",
+    "轢",
+    "年",
+    "憐",
+    "戀",
+    "撚",
+    "漣",
+    "煉",
+    "璉",
+    "秊",
+    "練",
+    "聯",
+    "輦",
+    "蓮",
+    "連",
+    "鍊",
+    "列",
+    "劣",
+    "咽",
+    "烈",
+    "裂",
+    "說",
+    "廉",
+    "念",
+    "捻",
+    "殮",
+    "簾",
+    "獵",
+    "令",
+    "囹",
+    "寧",
+    "嶺",
+    "怜",
+    "玲",
+    "瑩",
+    "羚",
+    "聆",
+    "鈴",
+    "零",
+    "靈",
+    "領",
+    "例",
+    "禮",
+    "醴",
+    "隸",
+    "惡",
+    "了",
+    "僚",
+    "寮",
+    "尿",
+    "料",
+    "樂",
+    "燎",
+    "療",
+    "蓼",
+    "遼",
+    "龍",
+    "暈",
+    "阮",
+    "劉",
+    "杻",
+    "柳",
+    "流",
+    "溜",
+    "琉",
+    "留",
+    "硫",
+    "紐",
+    "類",
+    "六",
+    "戮",
+    "陸",
+    "倫",
+    "崙",
+    "淪",
+    "輪",
+    "律",
+    "慄",
+    "栗",
+    "率",
+    "隆",
+    "利",
+    "吏",
+    "履",
+    "易",
+    "李",
+    "梨",
+    "泥",
+    "理",
+    "痢",
+    "罹",
+    "裏",
+    "裡",
+    "里",
+    "離",
+    "匿",
+    "溺",
+    "吝",
+    "燐",
+    "璘",
+    "藺",
+    "隣",
+    "鱗",
+    "麟",
+    "林",
+    "淋",
+    "臨",
+    "立",
+    "笠",
+    "粒",
+    "狀",
+    "炙",
+    "識",
+    "什",
+    "茶",
+    "刺",
+    "切",
+    "度",
+    "拓",
+    "糖",
+    "宅",
+    "洞",
+    "暴",
+    "輻",
+    "行",
+    "降",
+    "見",
+    "廓",
+    "兀",
+    "嗀",
+    None,
+    "塚",
+    None,
+    "晴",
+    None,
+    "凞",
+    "猪",
+    "益",
+    "礼",
+    "神",
+    "祥",
+    "福",
+    "靖",
+    "精",
+    "羽",
+    None,
+    "蘒",
+    None,
+    "諸",
+    None,
+    "逸",
+    "都",
+    None,
+    "飯",
+    "飼",
+    "館",
+    "鶴",
+    "郞",
+    "隷",
+    "侮",
+    "僧",
+    "免",
+    "勉",
+    "勤",
+    "卑",
+    "喝",
+    "嘆",
+    "器",
+    "塀",
+    "墨",
+    "層",
+    "屮",
+    "悔",
+    "慨",
+    "憎",
+    "懲",
+    "敏",
+    "既",
+    "暑",
+    "梅",
+    "海",
+    "渚",
+    "漢",
+    "煮",
+    "爫",
+    "琢",
+    "碑",
+    "社",
+    "祉",
+    "祈",
+    "祐",
+    "祖",
+    "祝",
+    "禍",
+    "禎",
+    "穀",
+    "突",
+    "節",
+    "練",
+    "縉",
+    "繁",
+    "署",
+    "者",
+    "臭",
+    "艹",
+    "著",
+    "褐",
+    "視",
+    "謁",
+    "謹",
+    "賓",
+    "贈",
+    "辶",
+    "逸",
+    "難",
+    "響",
+    "頻",
+    "恵",
+    "𤋮",
+    "舘",
+    None,
+    "並",
+    "况",
+    "全",
+    "侀",
+    "充",
+    "冀",
+    "勇",
+    "勺",
+    "喝",
+    "啕",
+    "喙",
+    "嗢",
+    "塚",
+    "墳",
+    "奄",
+    "奔",
+    "婢",
+    "嬨",
+    "廒",
+    "廙",
+    "彩",
+    "徭",
+    "惘",
+    "慎",
+    "愈",
+    "憎",
+    "慠",
+    "懲",
+    "戴",
+    "揄",
+    "搜",
+    "摒",
+    "敖",
+    "晴",
+    "朗",
+    "望",
+    "杖",
+    "歹",
+    "殺",
+    "流",
+    "滛",
+    "滋",
+    "漢",
+    "瀞",
+    "煮",
+    "瞧",
+    "爵",
+    "犯",
+    "猪",
+    "瑱",
+    "甆",
+    "画",
+    "瘝",
+    "瘟",
+    "益",
+    "盛",
+    "直",
+    "睊",
+    "着",
+    "磌",
+    "窱",
+    "節",
+    "类",
+    "絛",
+    "練",
+    "缾",
+    "者",
+    "荒",
+    "華",
+    "蝹",
+    "襁",
+    "覆",
+    "視",
+    "調",
+    "諸",
+    "請",
+    "謁",
+    "諾",
+    "諭",
+    "謹",
+    "變",
+    "贈",
+    "輸",
+    "遲",
+    "醙",
+    "鉶",
+    "陼",
+    "難",
+    "靖",
+    "韛",
+    "響",
+    "頋",
+    "頻",
+    "鬒",
+    "龜",
+    "𢡊",
+    "𢡄",
+    "𣏕",
+    "㮝",
+    "䀘",
+    "䀹",
+    "𥉉",
+    "𥳐",
+    "𧻓",
+    "齃",
+    "龎",
+    None,
+    "ff",
+    "fi",
+    "fl",
+    "ffi",
+    "ffl",
+    "st",
+    None,
+    "մն",
+    "մե",
+    "մի",
+    "վն",
+    "մխ",
+    None,
+    "יִ",
+    None,
+    "ײַ",
+    "ע",
+    "א",
+    "ד",
+    "ה",
+    "כ",
+    "ל",
+    "ם",
+    "ר",
+    "ת",
+    "+",
+    "שׁ",
+    "שׂ",
+    "שּׁ",
+    "שּׂ",
+    "אַ",
+    "אָ",
+    "אּ",
+    "בּ",
+    "גּ",
+    "דּ",
+    "הּ",
+    "וּ",
+    "זּ",
+    None,
+    "טּ",
+    "יּ",
+    "ךּ",
+    "כּ",
+    "לּ",
+    None,
+    "מּ",
+    None,
+    "נּ",
+    "סּ",
+    None,
+    "ףּ",
+    "פּ",
+    None,
+    "צּ",
+    "קּ",
+    "רּ",
+    "שּ",
+    "תּ",
+    "וֹ",
+    "בֿ",
+    "כֿ",
+    "פֿ",
+    "אל",
+    "ٱ",
+    "ٻ",
+    "پ",
+    "ڀ",
+    "ٺ",
+    "ٿ",
+    "ٹ",
+    "ڤ",
+    "ڦ",
+    "ڄ",
+    "ڃ",
+    "چ",
+    "ڇ",
+    "ڍ",
+    "ڌ",
+    "ڎ",
+    "ڈ",
+    "ژ",
+    "ڑ",
+    "ک",
+    "گ",
+    "ڳ",
+    "ڱ",
+    "ں",
+    "ڻ",
+    "ۀ",
+    "ہ",
+    "ھ",
+    "ے",
+    "ۓ",
+    None,
+    "ڭ",
+    "ۇ",
+    "ۆ",
+    "ۈ",
+    "ۇٴ",
+    "ۋ",
+    "ۅ",
+    "ۉ",
+    "ې",
+    "ى",
+    "ئا",
+    "ئە",
+    "ئو",
+    "ئۇ",
+    "ئۆ",
+    "ئۈ",
+    "ئې",
+    "ئى",
+    "ی",
+    "ئج",
+    "ئح",
+    "ئم",
+    "ئى",
+    "ئي",
+    "بج",
+    "بح",
+    "بخ",
+    "بم",
+    "بى",
+    "بي",
+    "تج",
+    "تح",
+    "تخ",
+    "تم",
+    "تى",
+    "تي",
+    "ثج",
+    "ثم",
+    "ثى",
+    "ثي",
+    "جح",
+    "جم",
+    "حج",
+    "حم",
+    "خج",
+    "خح",
+    "خم",
+    "سج",
+    "سح",
+    "سخ",
+    "سم",
+    "صح",
+    "صم",
+    "ضج",
+    "ضح",
+    "ضخ",
+    "ضم",
+    "طح",
+    "طم",
+    "ظم",
+    "عج",
+    "عم",
+    "غج",
+    "غم",
+    "فج",
+    "فح",
+    "فخ",
+    "فم",
+    "فى",
+    "في",
+    "قح",
+    "قم",
+    "قى",
+    "قي",
+    "كا",
+    "كج",
+    "كح",
+    "كخ",
+    "كل",
+    "كم",
+    "كى",
+    "كي",
+    "لج",
+    "لح",
+    "لخ",
+    "لم",
+    "لى",
+    "لي",
+    "مج",
+    "مح",
+    "مخ",
+    "مم",
+    "مى",
+    "مي",
+    "نج",
+    "نح",
+    "نخ",
+    "نم",
+    "نى",
+    "ني",
+    "هج",
+    "هم",
+    "هى",
+    "هي",
+    "يج",
+    "يح",
+    "يخ",
+    "يم",
+    "يى",
+    "يي",
+    "ذٰ",
+    "رٰ",
+    "ىٰ",
+    " ٌّ",
+    " ٍّ",
+    " َّ",
+    " ُّ",
+    " ِّ",
+    " ّٰ",
+    "ئر",
+    "ئز",
+    "ئم",
+    "ئن",
+    "ئى",
+    "ئي",
+    "بر",
+    "بز",
+    "بم",
+    "بن",
+    "بى",
+    "بي",
+    "تر",
+    "تز",
+    "تم",
+    "تن",
+    "تى",
+    "تي",
+    "ثر",
+    "ثز",
+    "ثم",
+    "ثن",
+    "ثى",
+    "ثي",
+    "فى",
+    "في",
+    "قى",
+    "قي",
+    "كا",
+    "كل",
+    "كم",
+    "كى",
+    "كي",
+    "لم",
+    "لى",
+    "لي",
+    "ما",
+    "مم",
+    "نر",
+    "نز",
+    "نم",
+    "نن",
+    "نى",
+    "ني",
+    "ىٰ",
+    "ير",
+    "يز",
+    "يم",
+    "ين",
+    "يى",
+    "يي",
+    "ئج",
+    "ئح",
+    "ئخ",
+    "ئم",
+    "ئه",
+    "بج",
+    "بح",
+    "بخ",
+    "بم",
+    "به",
+    "تج",
+    "تح",
+    "تخ",
+    "تم",
+    "ته",
+    "ثم",
+    "جح",
+    "جم",
+    "حج",
+    "حم",
+    "خج",
+    "خم",
+    "سج",
+    "سح",
+    "سخ",
+    "سم",
+    "صح",
+    "صخ",
+    "صم",
+    "ضج",
+    "ضح",
+    "ضخ",
+    "ضم",
+    "طح",
+    "ظم",
+    "عج",
+    "عم",
+    "غج",
+    "غم",
+    "فج",
+    "فح",
+    "فخ",
+    "فم",
+    "قح",
+    "قم",
+    "كج",
+    "كح",
+    "كخ",
+    "كل",
+    "كم",
+    "لج",
+    "لح",
+    "لخ",
+    "لم",
+    "له",
+    "مج",
+    "مح",
+    "مخ",
+    "مم",
+    "نج",
+    "نح",
+    "نخ",
+    "نم",
+    "نه",
+    "هج",
+    "هم",
+    "هٰ",
+    "يج",
+    "يح",
+    "يخ",
+    "يم",
+    "يه",
+    "ئم",
+    "ئه",
+    "بم",
+    "به",
+    "تم",
+    "ته",
+    "ثم",
+    "ثه",
+    "سم",
+    "سه",
+    "شم",
+    "شه",
+    "كل",
+    "كم",
+    "لم",
+    "نم",
+    "نه",
+    "يم",
+    "يه",
+    "ـَّ",
+    "ـُّ",
+    "ـِّ",
+    "طى",
+    "طي",
+    "عى",
+    "عي",
+    "غى",
+    "غي",
+    "سى",
+    "سي",
+    "شى",
+    "شي",
+    "حى",
+    "حي",
+    "جى",
+    "جي",
+    "خى",
+    "خي",
+    "صى",
+    "صي",
+    "ضى",
+    "ضي",
+    "شج",
+    "شح",
+    "شخ",
+    "شم",
+    "شر",
+    "سر",
+    "صر",
+    "ضر",
+    "طى",
+    "طي",
+    "عى",
+    "عي",
+    "غى",
+    "غي",
+    "سى",
+    "سي",
+    "شى",
+    "شي",
+    "حى",
+    "حي",
+    "جى",
+    "جي",
+    "خى",
+    "خي",
+    "صى",
+    "صي",
+    "ضى",
+    "ضي",
+    "شج",
+    "شح",
+    "شخ",
+    "شم",
+    "شر",
+    "سر",
+    "صر",
+    "ضر",
+    "شج",
+    "شح",
+    "شخ",
+    "شم",
+    "سه",
+    "شه",
+    "طم",
+    "سج",
+    "سح",
+    "سخ",
+    "شج",
+    "شح",
+    "شخ",
+    "طم",
+    "ظم",
+    "اً",
+    None,
+    "تجم",
+    "تحج",
+    "تحم",
+    "تخم",
+    "تمج",
+    "تمح",
+    "تمخ",
+    "جمح",
+    "حمي",
+    "حمى",
+    "سحج",
+    "سجح",
+    "سجى",
+    "سمح",
+    "سمج",
+    "سمم",
+    "صحح",
+    "صمم",
+    "شحم",
+    "شجي",
+    "شمخ",
+    "شمم",
+    "ضحى",
+    "ضخم",
+    "طمح",
+    "طمم",
+    "طمي",
+    "عجم",
+    "عمم",
+    "عمى",
+    "غمم",
+    "غمي",
+    "غمى",
+    "فخم",
+    "قمح",
+    "قمم",
+    "لحم",
+    "لحي",
+    "لحى",
+    "لجج",
+    "لخم",
+    "لمح",
+    "محج",
+    "محم",
+    "محي",
+    "مجح",
+    "مجم",
+    "مخج",
+    "مخم",
+    None,
+    "مجخ",
+    "همج",
+    "همم",
+    "نحم",
+    "نحى",
+    "نجم",
+    "نجى",
+    "نمي",
+    "نمى",
+    "يمم",
+    "بخي",
+    "تجي",
+    "تجى",
+    "تخي",
+    "تخى",
+    "تمي",
+    "تمى",
+    "جمي",
+    "جحى",
+    "جمى",
+    "سخى",
+    "صحي",
+    "شحي",
+    "ضحي",
+    "لجي",
+    "لمي",
+    "يحي",
+    "يجي",
+    "يمي",
+    "ممي",
+    "قمي",
+    "نحي",
+    "قمح",
+    "لحم",
+    "عمي",
+    "كمي",
+    "نجح",
+    "مخي",
+    "لجم",
+    "كمم",
+    "لجم",
+    "نجح",
+    "جحي",
+    "حجي",
+    "مجي",
+    "فمي",
+    "بحي",
+    "كمم",
+    "عجم",
+    "صمم",
+    "سخي",
+    "نجي",
+    None,
+    None,
+    "صلے",
+    "قلے",
+    "الله",
+    "اكبر",
+    "محمد",
+    "صلعم",
+    "رسول",
+    "عليه",
+    "وسلم",
+    "صلى",
+    "صلى الله عليه وسلم",
+    "جل جلاله",
+    "ریال",
+    None,
+    None,
+    ",",
+    "、",
+    None,
+    ":",
+    ";",
+    "!",
+    "?",
+    "〖",
+    "〗",
+    None,
+    None,
+    None,
+    "—",
+    "–",
+    "_",
+    "(",
+    ")",
+    "{",
+    "}",
+    "〔",
+    "〕",
+    "【",
+    "】",
+    "《",
+    "》",
+    "〈",
+    "〉",
+    "「",
+    "」",
+    "『",
+    "』",
+    None,
+    "[",
+    "]",
+    " ̅",
+    "_",
+    ",",
+    "、",
+    None,
+    ";",
+    ":",
+    "?",
+    "!",
+    "—",
+    "(",
+    ")",
+    "{",
+    "}",
+    "〔",
+    "〕",
+    "#",
+    "&",
+    "*",
+    "+",
+    "-",
+    "<",
+    ">",
+    "=",
+    None,
+    "\\",
+    "$",
+    "%",
+    "@",
+    None,
+    " ً",
+    "ـً",
+    " ٌ",
+    None,
+    " ٍ",
+    None,
+    " َ",
+    "ـَ",
+    " ُ",
+    "ـُ",
+    " ِ",
+    "ـِ",
+    " ّ",
+    "ـّ",
+    " ْ",
+    "ـْ",
+    "ء",
+    "آ",
+    "أ",
+    "ؤ",
+    "إ",
+    "ئ",
+    "ا",
+    "ب",
+    "ة",
+    "ت",
+    "ث",
+    "ج",
+    "ح",
+    "خ",
+    "د",
+    "ذ",
+    "ر",
+    "ز",
+    "س",
+    "ش",
+    "ص",
+    "ض",
+    "ط",
+    "ظ",
+    "ع",
+    "غ",
+    "ف",
+    "ق",
+    "ك",
+    "ل",
+    "م",
+    "ن",
+    "ه",
+    "و",
+    "ى",
+    "ي",
+    "لآ",
+    "لأ",
+    "لإ",
+    "لا",
+    None,
+    None,
+    None,
+    "!",
+    '"',
+    "#",
+    "$",
+    "%",
+    "&",
+    "'",
+    "(",
+    ")",
+    "*",
+    "+",
+    ",",
+    "-",
+    ".",
+    "/",
+    "0",
+    "1",
+    "2",
+    "3",
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    ":",
+    ";",
+    "<",
+    "=",
+    ">",
+    "?",
+    "@",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "[",
+    "\\",
+    "]",
+    "^",
+    "_",
+    "`",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "{",
+    "|",
+    "}",
+    "~",
+    "⦅",
+    "⦆",
+    ".",
+    "「",
+    "」",
+    "、",
+    "・",
+    "ヲ",
+    "ァ",
+    "ィ",
+    "ゥ",
+    "ェ",
+    "ォ",
+    "ャ",
+    "ュ",
+    "ョ",
+    "ッ",
+    "ー",
+    "ア",
+    "イ",
+    "ウ",
+    "エ",
+    "オ",
+    "カ",
+    "キ",
+    "ク",
+    "ケ",
+    "コ",
+    "サ",
+    "シ",
+    "ス",
+    "セ",
+    "ソ",
+    "タ",
+    "チ",
+    "ツ",
+    "テ",
+    "ト",
+    "ナ",
+    "ニ",
+    "ヌ",
+    "ネ",
+    "ノ",
+    "ハ",
+    "ヒ",
+    "フ",
+    "ヘ",
+    "ホ",
+    "マ",
+    "ミ",
+    "ム",
+    "メ",
+    "モ",
+    "ヤ",
+    "ユ",
+    "ヨ",
+    "ラ",
+    "リ",
+    "ル",
+    "レ",
+    "ロ",
+    "ワ",
+    "ン",
+    "゙",
+    "゚",
+    None,
+    "ᄀ",
+    "ᄁ",
+    "ᆪ",
+    "ᄂ",
+    "ᆬ",
+    "ᆭ",
+    "ᄃ",
+    "ᄄ",
+    "ᄅ",
+    "ᆰ",
+    "ᆱ",
+    "ᆲ",
+    "ᆳ",
+    "ᆴ",
+    "ᆵ",
+    "ᄚ",
+    "ᄆ",
+    "ᄇ",
+    "ᄈ",
+    "ᄡ",
+    "ᄉ",
+    "ᄊ",
+    "ᄋ",
+    "ᄌ",
+    "ᄍ",
+    "ᄎ",
+    "ᄏ",
+    "ᄐ",
+    "ᄑ",
+    "ᄒ",
+    None,
+    "ᅡ",
+    "ᅢ",
+    "ᅣ",
+    "ᅤ",
+    "ᅥ",
+    "ᅦ",
+    None,
+    "ᅧ",
+    "ᅨ",
+    "ᅩ",
+    "ᅪ",
+    "ᅫ",
+    "ᅬ",
+    None,
+    "ᅭ",
+    "ᅮ",
+    "ᅯ",
+    "ᅰ",
+    "ᅱ",
+    "ᅲ",
+    None,
+    "ᅳ",
+    "ᅴ",
+    "ᅵ",
+    None,
+    "¢",
+    "£",
+    "¬",
+    " ̄",
+    "¦",
+    "¥",
+    "₩",
+    None,
+    "│",
+    "←",
+    "↑",
+    "→",
+    "↓",
+    "■",
+    "○",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "𐐨",
+    "𐐩",
+    "𐐪",
+    "𐐫",
+    "𐐬",
+    "𐐭",
+    "𐐮",
+    "𐐯",
+    "𐐰",
+    "𐐱",
+    "𐐲",
+    "𐐳",
+    "𐐴",
+    "𐐵",
+    "𐐶",
+    "𐐷",
+    "𐐸",
+    "𐐹",
+    "𐐺",
+    "𐐻",
+    "𐐼",
+    "𐐽",
+    "𐐾",
+    "𐐿",
+    "𐑀",
+    "𐑁",
+    "𐑂",
+    "𐑃",
+    "𐑄",
+    "𐑅",
+    "𐑆",
+    "𐑇",
+    "𐑈",
+    "𐑉",
+    "𐑊",
+    "𐑋",
+    "𐑌",
+    "𐑍",
+    "𐑎",
+    "𐑏",
+    None,
+    None,
+    None,
+    None,
+    "𐓘",
+    "𐓙",
+    "𐓚",
+    "𐓛",
+    "𐓜",
+    "𐓝",
+    "𐓞",
+    "𐓟",
+    "𐓠",
+    "𐓡",
+    "𐓢",
+    "𐓣",
+    "𐓤",
+    "𐓥",
+    "𐓦",
+    "𐓧",
+    "𐓨",
+    "𐓩",
+    "𐓪",
+    "𐓫",
+    "𐓬",
+    "𐓭",
+    "𐓮",
+    "𐓯",
+    "𐓰",
+    "𐓱",
+    "𐓲",
+    "𐓳",
+    "𐓴",
+    "𐓵",
+    "𐓶",
+    "𐓷",
+    "𐓸",
+    "𐓹",
+    "𐓺",
+    "𐓻",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "𐖗",
+    "𐖘",
+    "𐖙",
+    "𐖚",
+    "𐖛",
+    "𐖜",
+    "𐖝",
+    "𐖞",
+    "𐖟",
+    "𐖠",
+    "𐖡",
+    None,
+    "𐖣",
+    "𐖤",
+    "𐖥",
+    "𐖦",
+    "𐖧",
+    "𐖨",
+    "𐖩",
+    "𐖪",
+    "𐖫",
+    "𐖬",
+    "𐖭",
+    "𐖮",
+    "𐖯",
+    "𐖰",
+    "𐖱",
+    None,
+    "𐖳",
+    "𐖴",
+    "𐖵",
+    "𐖶",
+    "𐖷",
+    "𐖸",
+    "𐖹",
+    None,
+    "𐖻",
+    "𐖼",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ː",
+    "ˑ",
+    "æ",
+    "ʙ",
+    "ɓ",
+    None,
+    "ʣ",
+    "ꭦ",
+    "ʥ",
+    "ʤ",
+    "ɖ",
+    "ɗ",
+    "ᶑ",
+    "ɘ",
+    "ɞ",
+    "ʩ",
+    "ɤ",
+    "ɢ",
+    "ɠ",
+    "ʛ",
+    "ħ",
+    "ʜ",
+    "ɧ",
+    "ʄ",
+    "ʪ",
+    "ʫ",
+    "ɬ",
+    "𝼄",
+    "ꞎ",
+    "ɮ",
+    "𝼅",
+    "ʎ",
+    "𝼆",
+    "ø",
+    "ɶ",
+    "ɷ",
+    "q",
+    "ɺ",
+    "𝼈",
+    "ɽ",
+    "ɾ",
+    "ʀ",
+    "ʨ",
+    "ʦ",
+    "ꭧ",
+    "ʧ",
+    "ʈ",
+    "ⱱ",
+    None,
+    "ʏ",
+    "ʡ",
+    "ʢ",
+    "ʘ",
+    "ǀ",
+    "ǁ",
+    "ǂ",
+    "𝼊",
+    "𝼞",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "𐳀",
+    "𐳁",
+    "𐳂",
+    "𐳃",
+    "𐳄",
+    "𐳅",
+    "𐳆",
+    "𐳇",
+    "𐳈",
+    "𐳉",
+    "𐳊",
+    "𐳋",
+    "𐳌",
+    "𐳍",
+    "𐳎",
+    "𐳏",
+    "𐳐",
+    "𐳑",
+    "𐳒",
+    "𐳓",
+    "𐳔",
+    "𐳕",
+    "𐳖",
+    "𐳗",
+    "𐳘",
+    "𐳙",
+    "𐳚",
+    "𐳛",
+    "𐳜",
+    "𐳝",
+    "𐳞",
+    "𐳟",
+    "𐳠",
+    "𐳡",
+    "𐳢",
+    "𐳣",
+    "𐳤",
+    "𐳥",
+    "𐳦",
+    "𐳧",
+    "𐳨",
+    "𐳩",
+    "𐳪",
+    "𐳫",
+    "𐳬",
+    "𐳭",
+    "𐳮",
+    "𐳯",
+    "𐳰",
+    "𐳱",
+    "𐳲",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "\U00010d70",
+    "\U00010d71",
+    "\U00010d72",
+    "\U00010d73",
+    "\U00010d74",
+    "\U00010d75",
+    "\U00010d76",
+    "\U00010d77",
+    "\U00010d78",
+    "\U00010d79",
+    "\U00010d7a",
+    "\U00010d7b",
+    "\U00010d7c",
+    "\U00010d7d",
+    "\U00010d7e",
+    "\U00010d7f",
+    "\U00010d80",
+    "\U00010d81",
+    "\U00010d82",
+    "\U00010d83",
+    "\U00010d84",
+    "\U00010d85",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "𑣀",
+    "𑣁",
+    "𑣂",
+    "𑣃",
+    "𑣄",
+    "𑣅",
+    "𑣆",
+    "𑣇",
+    "𑣈",
+    "𑣉",
+    "𑣊",
+    "𑣋",
+    "𑣌",
+    "𑣍",
+    "𑣎",
+    "𑣏",
+    "𑣐",
+    "𑣑",
+    "𑣒",
+    "𑣓",
+    "𑣔",
+    "𑣕",
+    "𑣖",
+    "𑣗",
+    "𑣘",
+    "𑣙",
+    "𑣚",
+    "𑣛",
+    "𑣜",
+    "𑣝",
+    "𑣞",
+    "𑣟",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "𖹠",
+    "𖹡",
+    "𖹢",
+    "𖹣",
+    "𖹤",
+    "𖹥",
+    "𖹦",
+    "𖹧",
+    "𖹨",
+    "𖹩",
+    "𖹪",
+    "𖹫",
+    "𖹬",
+    "𖹭",
+    "𖹮",
+    "𖹯",
+    "𖹰",
+    "𖹱",
+    "𖹲",
+    "𖹳",
+    "𖹴",
+    "𖹵",
+    "𖹶",
+    "𖹷",
+    "𖹸",
+    "𖹹",
+    "𖹺",
+    "𖹻",
+    "𖹼",
+    "𖹽",
+    "𖹾",
+    "𖹿",
+    None,
+    None,
+    "\U00016ebb",
+    "\U00016ebc",
+    "\U00016ebd",
+    "\U00016ebe",
+    "\U00016ebf",
+    "\U00016ec0",
+    "\U00016ec1",
+    "\U00016ec2",
+    "\U00016ec3",
+    "\U00016ec4",
+    "\U00016ec5",
+    "\U00016ec6",
+    "\U00016ec7",
+    "\U00016ec8",
+    "\U00016ec9",
+    "\U00016eca",
+    "\U00016ecb",
+    "\U00016ecc",
+    "\U00016ecd",
+    "\U00016ece",
+    "\U00016ecf",
+    "\U00016ed0",
+    "\U00016ed1",
+    "\U00016ed2",
+    "\U00016ed3",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "0",
+    "1",
+    "2",
+    "3",
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "𝅗𝅥",
+    "𝅘𝅥",
+    "𝅘𝅥𝅮",
+    "𝅘𝅥𝅯",
+    "𝅘𝅥𝅰",
+    "𝅘𝅥𝅱",
+    "𝅘𝅥𝅲",
+    None,
+    None,
+    None,
+    "𝆹𝅥",
+    "𝆺𝅥",
+    "𝆹𝅥𝅮",
+    "𝆺𝅥𝅮",
+    "𝆹𝅥𝅯",
+    "𝆺𝅥𝅯",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    None,
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    None,
+    "c",
+    "d",
+    None,
+    "g",
+    None,
+    "j",
+    "k",
+    None,
+    "n",
+    "o",
+    "p",
+    "q",
+    None,
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    None,
+    "f",
+    None,
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    None,
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    None,
+    "d",
+    "e",
+    "f",
+    "g",
+    None,
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    None,
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    None,
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    None,
+    "d",
+    "e",
+    "f",
+    "g",
+    None,
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    None,
+    "o",
+    None,
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    None,
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "ı",
+    "ȷ",
+    None,
+    "α",
+    "β",
+    "γ",
+    "δ",
+    "ε",
+    "ζ",
+    "η",
+    "θ",
+    "ι",
+    "κ",
+    "λ",
+    "μ",
+    "ν",
+    "ξ",
+    "ο",
+    "π",
+    "ρ",
+    "θ",
+    "σ",
+    "τ",
+    "υ",
+    "φ",
+    "χ",
+    "ψ",
+    "ω",
+    "∇",
+    "α",
+    "β",
+    "γ",
+    "δ",
+    "ε",
+    "ζ",
+    "η",
+    "θ",
+    "ι",
+    "κ",
+    "λ",
+    "μ",
+    "ν",
+    "ξ",
+    "ο",
+    "π",
+    "ρ",
+    "σ",
+    "τ",
+    "υ",
+    "φ",
+    "χ",
+    "ψ",
+    "ω",
+    "∂",
+    "ε",
+    "θ",
+    "κ",
+    "φ",
+    "ρ",
+    "π",
+    "α",
+    "β",
+    "γ",
+    "δ",
+    "ε",
+    "ζ",
+    "η",
+    "θ",
+    "ι",
+    "κ",
+    "λ",
+    "μ",
+    "ν",
+    "ξ",
+    "ο",
+    "π",
+    "ρ",
+    "θ",
+    "σ",
+    "τ",
+    "υ",
+    "φ",
+    "χ",
+    "ψ",
+    "ω",
+    "∇",
+    "α",
+    "β",
+    "γ",
+    "δ",
+    "ε",
+    "ζ",
+    "η",
+    "θ",
+    "ι",
+    "κ",
+    "λ",
+    "μ",
+    "ν",
+    "ξ",
+    "ο",
+    "π",
+    "ρ",
+    "σ",
+    "τ",
+    "υ",
+    "φ",
+    "χ",
+    "ψ",
+    "ω",
+    "∂",
+    "ε",
+    "θ",
+    "κ",
+    "φ",
+    "ρ",
+    "π",
+    "α",
+    "β",
+    "γ",
+    "δ",
+    "ε",
+    "ζ",
+    "η",
+    "θ",
+    "ι",
+    "κ",
+    "λ",
+    "μ",
+    "ν",
+    "ξ",
+    "ο",
+    "π",
+    "ρ",
+    "θ",
+    "σ",
+    "τ",
+    "υ",
+    "φ",
+    "χ",
+    "ψ",
+    "ω",
+    "∇",
+    "α",
+    "β",
+    "γ",
+    "δ",
+    "ε",
+    "ζ",
+    "η",
+    "θ",
+    "ι",
+    "κ",
+    "λ",
+    "μ",
+    "ν",
+    "ξ",
+    "ο",
+    "π",
+    "ρ",
+    "σ",
+    "τ",
+    "υ",
+    "φ",
+    "χ",
+    "ψ",
+    "ω",
+    "∂",
+    "ε",
+    "θ",
+    "κ",
+    "φ",
+    "ρ",
+    "π",
+    "α",
+    "β",
+    "γ",
+    "δ",
+    "ε",
+    "ζ",
+    "η",
+    "θ",
+    "ι",
+    "κ",
+    "λ",
+    "μ",
+    "ν",
+    "ξ",
+    "ο",
+    "π",
+    "ρ",
+    "θ",
+    "σ",
+    "τ",
+    "υ",
+    "φ",
+    "χ",
+    "ψ",
+    "ω",
+    "∇",
+    "α",
+    "β",
+    "γ",
+    "δ",
+    "ε",
+    "ζ",
+    "η",
+    "θ",
+    "ι",
+    "κ",
+    "λ",
+    "μ",
+    "ν",
+    "ξ",
+    "ο",
+    "π",
+    "ρ",
+    "σ",
+    "τ",
+    "υ",
+    "φ",
+    "χ",
+    "ψ",
+    "ω",
+    "∂",
+    "ε",
+    "θ",
+    "κ",
+    "φ",
+    "ρ",
+    "π",
+    "α",
+    "β",
+    "γ",
+    "δ",
+    "ε",
+    "ζ",
+    "η",
+    "θ",
+    "ι",
+    "κ",
+    "λ",
+    "μ",
+    "ν",
+    "ξ",
+    "ο",
+    "π",
+    "ρ",
+    "θ",
+    "σ",
+    "τ",
+    "υ",
+    "φ",
+    "χ",
+    "ψ",
+    "ω",
+    "∇",
+    "α",
+    "β",
+    "γ",
+    "δ",
+    "ε",
+    "ζ",
+    "η",
+    "θ",
+    "ι",
+    "κ",
+    "λ",
+    "μ",
+    "ν",
+    "ξ",
+    "ο",
+    "π",
+    "ρ",
+    "σ",
+    "τ",
+    "υ",
+    "φ",
+    "χ",
+    "ψ",
+    "ω",
+    "∂",
+    "ε",
+    "θ",
+    "κ",
+    "φ",
+    "ρ",
+    "π",
+    "ϝ",
+    None,
+    "0",
+    "1",
+    "2",
+    "3",
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    "0",
+    "1",
+    "2",
+    "3",
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    "0",
+    "1",
+    "2",
+    "3",
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    "0",
+    "1",
+    "2",
+    "3",
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    "0",
+    "1",
+    "2",
+    "3",
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "а",
+    "б",
+    "в",
+    "г",
+    "д",
+    "е",
+    "ж",
+    "з",
+    "и",
+    "к",
+    "л",
+    "м",
+    "о",
+    "п",
+    "р",
+    "с",
+    "т",
+    "у",
+    "ф",
+    "х",
+    "ц",
+    "ч",
+    "ш",
+    "ы",
+    "э",
+    "ю",
+    "ꚉ",
+    "ә",
+    "і",
+    "ј",
+    "ө",
+    "ү",
+    "ӏ",
+    "а",
+    "б",
+    "в",
+    "г",
+    "д",
+    "е",
+    "ж",
+    "з",
+    "и",
+    "к",
+    "л",
+    "о",
+    "п",
+    "с",
+    "у",
+    "ф",
+    "х",
+    "ц",
+    "ч",
+    "ш",
+    "ъ",
+    "ы",
+    "ґ",
+    "і",
+    "ѕ",
+    "џ",
+    "ҫ",
+    "ꙑ",
+    "ұ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "𞤢",
+    "𞤣",
+    "𞤤",
+    "𞤥",
+    "𞤦",
+    "𞤧",
+    "𞤨",
+    "𞤩",
+    "𞤪",
+    "𞤫",
+    "𞤬",
+    "𞤭",
+    "𞤮",
+    "𞤯",
+    "𞤰",
+    "𞤱",
+    "𞤲",
+    "𞤳",
+    "𞤴",
+    "𞤵",
+    "𞤶",
+    "𞤷",
+    "𞤸",
+    "𞤹",
+    "𞤺",
+    "𞤻",
+    "𞤼",
+    "𞤽",
+    "𞤾",
+    "𞤿",
+    "𞥀",
+    "𞥁",
+    "𞥂",
+    "𞥃",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "ا",
+    "ب",
+    "ج",
+    "د",
+    None,
+    "و",
+    "ز",
+    "ح",
+    "ط",
+    "ي",
+    "ك",
+    "ل",
+    "م",
+    "ن",
+    "س",
+    "ع",
+    "ف",
+    "ص",
+    "ق",
+    "ر",
+    "ش",
+    "ت",
+    "ث",
+    "خ",
+    "ذ",
+    "ض",
+    "ظ",
+    "غ",
+    "ٮ",
+    "ں",
+    "ڡ",
+    "ٯ",
+    None,
+    "ب",
+    "ج",
+    None,
+    "ه",
+    None,
+    "ح",
+    None,
+    "ي",
+    "ك",
+    "ل",
+    "م",
+    "ن",
+    "س",
+    "ع",
+    "ف",
+    "ص",
+    "ق",
+    None,
+    "ش",
+    "ت",
+    "ث",
+    "خ",
+    None,
+    "ض",
+    None,
+    "غ",
+    None,
+    "ج",
+    None,
+    "ح",
+    None,
+    "ي",
+    None,
+    "ل",
+    None,
+    "ن",
+    "س",
+    "ع",
+    None,
+    "ص",
+    "ق",
+    None,
+    "ش",
+    None,
+    "خ",
+    None,
+    "ض",
+    None,
+    "غ",
+    None,
+    "ں",
+    None,
+    "ٯ",
+    None,
+    "ب",
+    "ج",
+    None,
+    "ه",
+    None,
+    "ح",
+    "ط",
+    "ي",
+    "ك",
+    None,
+    "م",
+    "ن",
+    "س",
+    "ع",
+    "ف",
+    "ص",
+    "ق",
+    None,
+    "ش",
+    "ت",
+    "ث",
+    "خ",
+    None,
+    "ض",
+    "ظ",
+    "غ",
+    "ٮ",
+    None,
+    "ڡ",
+    None,
+    "ا",
+    "ب",
+    "ج",
+    "د",
+    "ه",
+    "و",
+    "ز",
+    "ح",
+    "ط",
+    "ي",
+    None,
+    "ل",
+    "م",
+    "ن",
+    "س",
+    "ع",
+    "ف",
+    "ص",
+    "ق",
+    "ر",
+    "ش",
+    "ت",
+    "ث",
+    "خ",
+    "ذ",
+    "ض",
+    "ظ",
+    "غ",
+    None,
+    "ب",
+    "ج",
+    "د",
+    None,
+    "و",
+    "ز",
+    "ح",
+    "ط",
+    "ي",
+    None,
+    "ل",
+    "م",
+    "ن",
+    "س",
+    "ع",
+    "ف",
+    "ص",
+    "ق",
+    "ر",
+    "ش",
+    "ت",
+    "ث",
+    "خ",
+    "ذ",
+    "ض",
+    "ظ",
+    "غ",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "0,",
+    "1,",
+    "2,",
+    "3,",
+    "4,",
+    "5,",
+    "6,",
+    "7,",
+    "8,",
+    "9,",
+    None,
+    "(a)",
+    "(b)",
+    "(c)",
+    "(d)",
+    "(e)",
+    "(f)",
+    "(g)",
+    "(h)",
+    "(i)",
+    "(j)",
+    "(k)",
+    "(l)",
+    "(m)",
+    "(n)",
+    "(o)",
+    "(p)",
+    "(q)",
+    "(r)",
+    "(s)",
+    "(t)",
+    "(u)",
+    "(v)",
+    "(w)",
+    "(x)",
+    "(y)",
+    "(z)",
+    "〔s〕",
+    "c",
+    "r",
+    "cd",
+    "wz",
+    None,
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "hv",
+    "mv",
+    "sd",
+    "ss",
+    "ppv",
+    "wc",
+    None,
+    "mc",
+    "md",
+    "mr",
+    None,
+    "dj",
+    None,
+    None,
+    None,
+    "ほか",
+    "ココ",
+    "サ",
+    None,
+    "手",
+    "字",
+    "双",
+    "デ",
+    "二",
+    "多",
+    "解",
+    "天",
+    "交",
+    "映",
+    "無",
+    "料",
+    "前",
+    "後",
+    "再",
+    "新",
+    "初",
+    "終",
+    "生",
+    "販",
+    "声",
+    "吹",
+    "演",
+    "投",
+    "捕",
+    "一",
+    "三",
+    "遊",
+    "左",
+    "中",
+    "右",
+    "指",
+    "走",
+    "打",
+    "禁",
+    "空",
+    "合",
+    "満",
+    "有",
+    "月",
+    "申",
+    "割",
+    "営",
+    "配",
+    None,
+    "〔本〕",
+    "〔三〕",
+    "〔二〕",
+    "〔安〕",
+    "〔点〕",
+    "〔打〕",
+    "〔盗〕",
+    "〔勝〕",
+    "〔敗〕",
+    None,
+    "得",
+    "可",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "0",
+    "1",
+    "2",
+    "3",
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "丽",
+    "丸",
+    "乁",
+    "𠄢",
+    "你",
+    "侮",
+    "侻",
+    "倂",
+    "偺",
+    "備",
+    "僧",
+    "像",
+    "㒞",
+    "𠘺",
+    "免",
+    "兔",
+    "兤",
+    "具",
+    "𠔜",
+    "㒹",
+    "內",
+    "再",
+    "𠕋",
+    "冗",
+    "冤",
+    "仌",
+    "冬",
+    "况",
+    "𩇟",
+    "凵",
+    "刃",
+    "㓟",
+    "刻",
+    "剆",
+    "割",
+    "剷",
+    "㔕",
+    "勇",
+    "勉",
+    "勤",
+    "勺",
+    "包",
+    "匆",
+    "北",
+    "卉",
+    "卑",
+    "博",
+    "即",
+    "卽",
+    "卿",
+    "𠨬",
+    "灰",
+    "及",
+    "叟",
+    "𠭣",
+    "叫",
+    "叱",
+    "吆",
+    "咞",
+    "吸",
+    "呈",
+    "周",
+    "咢",
+    "哶",
+    "唐",
+    "啓",
+    "啣",
+    "善",
+    "喙",
+    "喫",
+    "喳",
+    "嗂",
+    "圖",
+    "嘆",
+    "圗",
+    "噑",
+    "噴",
+    "切",
+    "壮",
+    "城",
+    "埴",
+    "堍",
+    "型",
+    "堲",
+    "報",
+    "墬",
+    "𡓤",
+    "売",
+    "壷",
+    "夆",
+    "多",
+    "夢",
+    "奢",
+    "𡚨",
+    "𡛪",
+    "姬",
+    "娛",
+    "娧",
+    "姘",
+    "婦",
+    "㛮",
+    "㛼",
+    "嬈",
+    "嬾",
+    "𡧈",
+    "寃",
+    "寘",
+    "寧",
+    "寳",
+    "𡬘",
+    "寿",
+    "将",
+    "当",
+    "尢",
+    "㞁",
+    "屠",
+    "屮",
+    "峀",
+    "岍",
+    "𡷤",
+    "嵃",
+    "𡷦",
+    "嵮",
+    "嵫",
+    "嵼",
+    "巡",
+    "巢",
+    "㠯",
+    "巽",
+    "帨",
+    "帽",
+    "幩",
+    "㡢",
+    "𢆃",
+    "㡼",
+    "庰",
+    "庳",
+    "庶",
+    "廊",
+    "𪎒",
+    "廾",
+    "𢌱",
+    "舁",
+    "弢",
+    "㣇",
+    "𣊸",
+    "𦇚",
+    "形",
+    "彫",
+    "㣣",
+    "徚",
+    "忍",
+    "志",
+    "忹",
+    "悁",
+    "㤺",
+    "㤜",
+    "悔",
+    "𢛔",
+    "惇",
+    "慈",
+    "慌",
+    "慎",
+    "慌",
+    "慺",
+    "憎",
+    "憲",
+    "憤",
+    "憯",
+    "懞",
+    "懲",
+    "懶",
+    "成",
+    "戛",
+    "扝",
+    "抱",
+    "拔",
+    "捐",
+    "𢬌",
+    "挽",
+    "拼",
+    "捨",
+    "掃",
+    "揤",
+    "𢯱",
+    "搢",
+    "揅",
+    "掩",
+    "㨮",
+    "摩",
+    "摾",
+    "撝",
+    "摷",
+    "㩬",
+    "敏",
+    "敬",
+    "𣀊",
+    "旣",
+    "書",
+    "晉",
+    "㬙",
+    "暑",
+    "㬈",
+    "㫤",
+    "冒",
+    "冕",
+    "最",
+    "暜",
+    "肭",
+    "䏙",
+    "朗",
+    "望",
+    "朡",
+    "杞",
+    "杓",
+    "𣏃",
+    "㭉",
+    "柺",
+    "枅",
+    "桒",
+    "梅",
+    "𣑭",
+    "梎",
+    "栟",
+    "椔",
+    "㮝",
+    "楂",
+    "榣",
+    "槪",
+    "檨",
+    "𣚣",
+    "櫛",
+    "㰘",
+    "次",
+    "𣢧",
+    "歔",
+    "㱎",
+    "歲",
+    "殟",
+    "殺",
+    "殻",
+    "𣪍",
+    "𡴋",
+    "𣫺",
+    "汎",
+    "𣲼",
+    "沿",
+    "泍",
+    "汧",
+    "洖",
+    "派",
+    "海",
+    "流",
+    "浩",
+    "浸",
+    "涅",
+    "𣴞",
+    "洴",
+    "港",
+    "湮",
+    "㴳",
+    "滋",
+    "滇",
+    "𣻑",
+    "淹",
+    "潮",
+    "𣽞",
+    "𣾎",
+    "濆",
+    "瀹",
+    "瀞",
+    "瀛",
+    "㶖",
+    "灊",
+    "災",
+    "灷",
+    "炭",
+    "𠔥",
+    "煅",
+    "𤉣",
+    "熜",
+    "𤎫",
+    "爨",
+    "爵",
+    "牐",
+    "𤘈",
+    "犀",
+    "犕",
+    "𤜵",
+    "𤠔",
+    "獺",
+    "王",
+    "㺬",
+    "玥",
+    "㺸",
+    "瑇",
+    "瑜",
+    "瑱",
+    "璅",
+    "瓊",
+    "㼛",
+    "甤",
+    "𤰶",
+    "甾",
+    "𤲒",
+    "異",
+    "𢆟",
+    "瘐",
+    "𤾡",
+    "𤾸",
+    "𥁄",
+    "㿼",
+    "䀈",
+    "直",
+    "𥃳",
+    "𥃲",
+    "𥄙",
+    "𥄳",
+    "眞",
+    "真",
+    "睊",
+    "䀹",
+    "瞋",
+    "䁆",
+    "䂖",
+    "𥐝",
+    "硎",
+    "碌",
+    "磌",
+    "䃣",
+    "𥘦",
+    "祖",
+    "𥚚",
+    "𥛅",
+    "福",
+    "秫",
+    "䄯",
+    "穀",
+    "穊",
+    "穏",
+    "𥥼",
+    "𥪧",
+    "竮",
+    "䈂",
+    "𥮫",
+    "篆",
+    "築",
+    "䈧",
+    "𥲀",
+    "糒",
+    "䊠",
+    "糨",
+    "糣",
+    "紀",
+    "𥾆",
+    "絣",
+    "䌁",
+    "緇",
+    "縂",
+    "繅",
+    "䌴",
+    "𦈨",
+    "𦉇",
+    "䍙",
+    "𦋙",
+    "罺",
+    "𦌾",
+    "羕",
+    "翺",
+    "者",
+    "𦓚",
+    "𦔣",
+    "聠",
+    "𦖨",
+    "聰",
+    "𣍟",
+    "䏕",
+    "育",
+    "脃",
+    "䐋",
+    "脾",
+    "媵",
+    "𦞧",
+    "𦞵",
+    "𣎓",
+    "𣎜",
+    "舁",
+    "舄",
+    "辞",
+    "䑫",
+    "芑",
+    "芋",
+    "芝",
+    "劳",
+    "花",
+    "芳",
+    "芽",
+    "苦",
+    "𦬼",
+    "若",
+    "茝",
+    "荣",
+    "莭",
+    "茣",
+    "莽",
+    "菧",
+    "著",
+    "荓",
+    "菊",
+    "菌",
+    "菜",
+    "𦰶",
+    "𦵫",
+    "𦳕",
+    "䔫",
+    "蓱",
+    "蓳",
+    "蔖",
+    "𧏊",
+    "蕤",
+    "𦼬",
+    "䕝",
+    "䕡",
+    "𦾱",
+    "𧃒",
+    "䕫",
+    "虐",
+    "虜",
+    "虧",
+    "虩",
+    "蚩",
+    "蚈",
+    "蜎",
+    "蛢",
+    "蝹",
+    "蜨",
+    "蝫",
+    "螆",
+    "䗗",
+    "蟡",
+    "蠁",
+    "䗹",
+    "衠",
+    "衣",
+    "𧙧",
+    "裗",
+    "裞",
+    "䘵",
+    "裺",
+    "㒻",
+    "𧢮",
+    "𧥦",
+    "䚾",
+    "䛇",
+    "誠",
+    "諭",
+    "變",
+    "豕",
+    "𧲨",
+    "貫",
+    "賁",
+    "贛",
+    "起",
+    "𧼯",
+    "𠠄",
+    "跋",
+    "趼",
+    "跰",
+    "𠣞",
+    "軔",
+    "輸",
+    "𨗒",
+    "𨗭",
+    "邔",
+    "郱",
+    "鄑",
+    "𨜮",
+    "鄛",
+    "鈸",
+    "鋗",
+    "鋘",
+    "鉼",
+    "鏹",
+    "鐕",
+    "𨯺",
+    "開",
+    "䦕",
+    "閷",
+    "𨵷",
+    "䧦",
+    "雃",
+    "嶲",
+    "霣",
+    "𩅅",
+    "𩈚",
+    "䩮",
+    "䩶",
+    "韠",
+    "𩐊",
+    "䪲",
+    "𩒖",
+    "頋",
+    "頩",
+    "𩖶",
+    "飢",
+    "䬳",
+    "餩",
+    "馧",
+    "駂",
+    "駾",
+    "䯎",
+    "𩬰",
+    "鬒",
+    "鱀",
+    "鳽",
+    "䳎",
+    "䳭",
+    "鵧",
+    "𪃎",
+    "䳸",
+    "𪄅",
+    "𪈎",
+    "𪊑",
+    "麻",
+    "䵖",
+    "黹",
+    "黾",
+    "鼅",
+    "鼏",
+    "鼖",
+    "鼻",
+    "𪘀",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/COPYING b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/COPYING
new file mode 100644
index 0000000..f067af3
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/COPYING
@@ -0,0 +1,14 @@
+Copyright (C) 2008-2011 INADA Naoki 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__init__.py
new file mode 100644
index 0000000..f3266b7
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__init__.py
@@ -0,0 +1,55 @@
+# ruff: noqa: F401
+import os
+
+from .exceptions import *  # noqa: F403
+from .ext import ExtType, Timestamp
+
+version = (1, 1, 2)
+__version__ = "1.1.2"
+
+
+if os.environ.get("MSGPACK_PUREPYTHON"):
+    from .fallback import Packer, Unpacker, unpackb
+else:
+    try:
+        from ._cmsgpack import Packer, Unpacker, unpackb
+    except ImportError:
+        from .fallback import Packer, Unpacker, unpackb
+
+
+def pack(o, stream, **kwargs):
+    """
+    Pack object `o` and write it to `stream`
+
+    See :class:`Packer` for options.
+    """
+    packer = Packer(**kwargs)
+    stream.write(packer.pack(o))
+
+
+def packb(o, **kwargs):
+    """
+    Pack object `o` and return packed bytes
+
+    See :class:`Packer` for options.
+    """
+    return Packer(**kwargs).pack(o)
+
+
+def unpack(stream, **kwargs):
+    """
+    Unpack an object from `stream`.
+
+    Raises `ExtraData` when `stream` contains extra bytes.
+    See :class:`Unpacker` for options.
+    """
+    data = stream.read()
+    return unpackb(data, **kwargs)
+
+
+# alias for compatibility to simplejson/marshal/pickle.
+load = unpack
+loads = unpackb
+
+dump = pack
+dumps = packb
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..c2de097
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-312.pyc
new file mode 100644
index 0000000..c90cd8d
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-312.pyc
new file mode 100644
index 0000000..7fc4811
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-312.pyc
new file mode 100644
index 0000000..552edbf
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/exceptions.py b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/exceptions.py
new file mode 100644
index 0000000..d6d2615
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/exceptions.py
@@ -0,0 +1,48 @@
+class UnpackException(Exception):
+    """Base class for some exceptions raised while unpacking.
+
+    NOTE: unpack may raise exception other than subclass of
+    UnpackException.  If you want to catch all error, catch
+    Exception instead.
+    """
+
+
+class BufferFull(UnpackException):
+    pass
+
+
+class OutOfData(UnpackException):
+    pass
+
+
+class FormatError(ValueError, UnpackException):
+    """Invalid msgpack format"""
+
+
+class StackError(ValueError, UnpackException):
+    """Too nested"""
+
+
+# Deprecated.  Use ValueError instead
+UnpackValueError = ValueError
+
+
+class ExtraData(UnpackValueError):
+    """ExtraData is raised when there is trailing data.
+
+    This exception is raised while only one-shot (not streaming)
+    unpack.
+    """
+
+    def __init__(self, unpacked, extra):
+        self.unpacked = unpacked
+        self.extra = extra
+
+    def __str__(self):
+        return "unpack(b) received extra data."
+
+
+# Deprecated.  Use Exception instead to catch all exception during packing.
+PackException = Exception
+PackValueError = ValueError
+PackOverflowError = OverflowError
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/ext.py b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/ext.py
new file mode 100644
index 0000000..9694819
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/ext.py
@@ -0,0 +1,170 @@
+import datetime
+import struct
+from collections import namedtuple
+
+
+class ExtType(namedtuple("ExtType", "code data")):
+    """ExtType represents ext type in msgpack."""
+
+    def __new__(cls, code, data):
+        if not isinstance(code, int):
+            raise TypeError("code must be int")
+        if not isinstance(data, bytes):
+            raise TypeError("data must be bytes")
+        if not 0 <= code <= 127:
+            raise ValueError("code must be 0~127")
+        return super().__new__(cls, code, data)
+
+
+class Timestamp:
+    """Timestamp represents the Timestamp extension type in msgpack.
+
+    When built with Cython, msgpack uses C methods to pack and unpack `Timestamp`.
+    When using pure-Python msgpack, :func:`to_bytes` and :func:`from_bytes` are used to pack and
+    unpack `Timestamp`.
+
+    This class is immutable: Do not override seconds and nanoseconds.
+    """
+
+    __slots__ = ["seconds", "nanoseconds"]
+
+    def __init__(self, seconds, nanoseconds=0):
+        """Initialize a Timestamp object.
+
+        :param int seconds:
+            Number of seconds since the UNIX epoch (00:00:00 UTC Jan 1 1970, minus leap seconds).
+            May be negative.
+
+        :param int nanoseconds:
+            Number of nanoseconds to add to `seconds` to get fractional time.
+            Maximum is 999_999_999.  Default is 0.
+
+        Note: Negative times (before the UNIX epoch) are represented as neg. seconds + pos. ns.
+        """
+        if not isinstance(seconds, int):
+            raise TypeError("seconds must be an integer")
+        if not isinstance(nanoseconds, int):
+            raise TypeError("nanoseconds must be an integer")
+        if not (0 <= nanoseconds < 10**9):
+            raise ValueError("nanoseconds must be a non-negative integer less than 999999999.")
+        self.seconds = seconds
+        self.nanoseconds = nanoseconds
+
+    def __repr__(self):
+        """String representation of Timestamp."""
+        return f"Timestamp(seconds={self.seconds}, nanoseconds={self.nanoseconds})"
+
+    def __eq__(self, other):
+        """Check for equality with another Timestamp object"""
+        if type(other) is self.__class__:
+            return self.seconds == other.seconds and self.nanoseconds == other.nanoseconds
+        return False
+
+    def __ne__(self, other):
+        """not-equals method (see :func:`__eq__()`)"""
+        return not self.__eq__(other)
+
+    def __hash__(self):
+        return hash((self.seconds, self.nanoseconds))
+
+    @staticmethod
+    def from_bytes(b):
+        """Unpack bytes into a `Timestamp` object.
+
+        Used for pure-Python msgpack unpacking.
+
+        :param b: Payload from msgpack ext message with code -1
+        :type b: bytes
+
+        :returns: Timestamp object unpacked from msgpack ext payload
+        :rtype: Timestamp
+        """
+        if len(b) == 4:
+            seconds = struct.unpack("!L", b)[0]
+            nanoseconds = 0
+        elif len(b) == 8:
+            data64 = struct.unpack("!Q", b)[0]
+            seconds = data64 & 0x00000003FFFFFFFF
+            nanoseconds = data64 >> 34
+        elif len(b) == 12:
+            nanoseconds, seconds = struct.unpack("!Iq", b)
+        else:
+            raise ValueError(
+                "Timestamp type can only be created from 32, 64, or 96-bit byte objects"
+            )
+        return Timestamp(seconds, nanoseconds)
+
+    def to_bytes(self):
+        """Pack this Timestamp object into bytes.
+
+        Used for pure-Python msgpack packing.
+
+        :returns data: Payload for EXT message with code -1 (timestamp type)
+        :rtype: bytes
+        """
+        if (self.seconds >> 34) == 0:  # seconds is non-negative and fits in 34 bits
+            data64 = self.nanoseconds << 34 | self.seconds
+            if data64 & 0xFFFFFFFF00000000 == 0:
+                # nanoseconds is zero and seconds < 2**32, so timestamp 32
+                data = struct.pack("!L", data64)
+            else:
+                # timestamp 64
+                data = struct.pack("!Q", data64)
+        else:
+            # timestamp 96
+            data = struct.pack("!Iq", self.nanoseconds, self.seconds)
+        return data
+
+    @staticmethod
+    def from_unix(unix_sec):
+        """Create a Timestamp from posix timestamp in seconds.
+
+        :param unix_float: Posix timestamp in seconds.
+        :type unix_float: int or float
+        """
+        seconds = int(unix_sec // 1)
+        nanoseconds = int((unix_sec % 1) * 10**9)
+        return Timestamp(seconds, nanoseconds)
+
+    def to_unix(self):
+        """Get the timestamp as a floating-point value.
+
+        :returns: posix timestamp
+        :rtype: float
+        """
+        return self.seconds + self.nanoseconds / 1e9
+
+    @staticmethod
+    def from_unix_nano(unix_ns):
+        """Create a Timestamp from posix timestamp in nanoseconds.
+
+        :param int unix_ns: Posix timestamp in nanoseconds.
+        :rtype: Timestamp
+        """
+        return Timestamp(*divmod(unix_ns, 10**9))
+
+    def to_unix_nano(self):
+        """Get the timestamp as a unixtime in nanoseconds.
+
+        :returns: posix timestamp in nanoseconds
+        :rtype: int
+        """
+        return self.seconds * 10**9 + self.nanoseconds
+
+    def to_datetime(self):
+        """Get the timestamp as a UTC datetime.
+
+        :rtype: `datetime.datetime`
+        """
+        utc = datetime.timezone.utc
+        return datetime.datetime.fromtimestamp(0, utc) + datetime.timedelta(
+            seconds=self.seconds, microseconds=self.nanoseconds // 1000
+        )
+
+    @staticmethod
+    def from_datetime(dt):
+        """Create a Timestamp from datetime with tzinfo.
+
+        :rtype: Timestamp
+        """
+        return Timestamp(seconds=int(dt.timestamp()), nanoseconds=dt.microsecond * 1000)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/fallback.py b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/fallback.py
new file mode 100644
index 0000000..b02e47c
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/msgpack/fallback.py
@@ -0,0 +1,929 @@
+"""Fallback pure Python implementation of msgpack"""
+
+import struct
+import sys
+from datetime import datetime as _DateTime
+
+if hasattr(sys, "pypy_version_info"):
+    from __pypy__ import newlist_hint
+    from __pypy__.builders import BytesBuilder
+
+    _USING_STRINGBUILDER = True
+
+    class BytesIO:
+        def __init__(self, s=b""):
+            if s:
+                self.builder = BytesBuilder(len(s))
+                self.builder.append(s)
+            else:
+                self.builder = BytesBuilder()
+
+        def write(self, s):
+            if isinstance(s, memoryview):
+                s = s.tobytes()
+            elif isinstance(s, bytearray):
+                s = bytes(s)
+            self.builder.append(s)
+
+        def getvalue(self):
+            return self.builder.build()
+
+else:
+    from io import BytesIO
+
+    _USING_STRINGBUILDER = False
+
+    def newlist_hint(size):
+        return []
+
+
+from .exceptions import BufferFull, ExtraData, FormatError, OutOfData, StackError
+from .ext import ExtType, Timestamp
+
+EX_SKIP = 0
+EX_CONSTRUCT = 1
+EX_READ_ARRAY_HEADER = 2
+EX_READ_MAP_HEADER = 3
+
+TYPE_IMMEDIATE = 0
+TYPE_ARRAY = 1
+TYPE_MAP = 2
+TYPE_RAW = 3
+TYPE_BIN = 4
+TYPE_EXT = 5
+
+DEFAULT_RECURSE_LIMIT = 511
+
+
+def _check_type_strict(obj, t, type=type, tuple=tuple):
+    if type(t) is tuple:
+        return type(obj) in t
+    else:
+        return type(obj) is t
+
+
+def _get_data_from_buffer(obj):
+    view = memoryview(obj)
+    if view.itemsize != 1:
+        raise ValueError("cannot unpack from multi-byte object")
+    return view
+
+
+def unpackb(packed, **kwargs):
+    """
+    Unpack an object from `packed`.
+
+    Raises ``ExtraData`` when *packed* contains extra bytes.
+    Raises ``ValueError`` when *packed* is incomplete.
+    Raises ``FormatError`` when *packed* is not valid msgpack.
+    Raises ``StackError`` when *packed* contains too nested.
+    Other exceptions can be raised during unpacking.
+
+    See :class:`Unpacker` for options.
+    """
+    unpacker = Unpacker(None, max_buffer_size=len(packed), **kwargs)
+    unpacker.feed(packed)
+    try:
+        ret = unpacker._unpack()
+    except OutOfData:
+        raise ValueError("Unpack failed: incomplete input")
+    except RecursionError:
+        raise StackError
+    if unpacker._got_extradata():
+        raise ExtraData(ret, unpacker._get_extradata())
+    return ret
+
+
+_NO_FORMAT_USED = ""
+_MSGPACK_HEADERS = {
+    0xC4: (1, _NO_FORMAT_USED, TYPE_BIN),
+    0xC5: (2, ">H", TYPE_BIN),
+    0xC6: (4, ">I", TYPE_BIN),
+    0xC7: (2, "Bb", TYPE_EXT),
+    0xC8: (3, ">Hb", TYPE_EXT),
+    0xC9: (5, ">Ib", TYPE_EXT),
+    0xCA: (4, ">f"),
+    0xCB: (8, ">d"),
+    0xCC: (1, _NO_FORMAT_USED),
+    0xCD: (2, ">H"),
+    0xCE: (4, ">I"),
+    0xCF: (8, ">Q"),
+    0xD0: (1, "b"),
+    0xD1: (2, ">h"),
+    0xD2: (4, ">i"),
+    0xD3: (8, ">q"),
+    0xD4: (1, "b1s", TYPE_EXT),
+    0xD5: (2, "b2s", TYPE_EXT),
+    0xD6: (4, "b4s", TYPE_EXT),
+    0xD7: (8, "b8s", TYPE_EXT),
+    0xD8: (16, "b16s", TYPE_EXT),
+    0xD9: (1, _NO_FORMAT_USED, TYPE_RAW),
+    0xDA: (2, ">H", TYPE_RAW),
+    0xDB: (4, ">I", TYPE_RAW),
+    0xDC: (2, ">H", TYPE_ARRAY),
+    0xDD: (4, ">I", TYPE_ARRAY),
+    0xDE: (2, ">H", TYPE_MAP),
+    0xDF: (4, ">I", TYPE_MAP),
+}
+
+
+class Unpacker:
+    """Streaming unpacker.
+
+    Arguments:
+
+    :param file_like:
+        File-like object having `.read(n)` method.
+        If specified, unpacker reads serialized data from it and `.feed()` is not usable.
+
+    :param int read_size:
+        Used as `file_like.read(read_size)`. (default: `min(16*1024, max_buffer_size)`)
+
+    :param bool use_list:
+        If true, unpack msgpack array to Python list.
+        Otherwise, unpack to Python tuple. (default: True)
+
+    :param bool raw:
+        If true, unpack msgpack raw to Python bytes.
+        Otherwise, unpack to Python str by decoding with UTF-8 encoding (default).
+
+    :param int timestamp:
+        Control how timestamp type is unpacked:
+
+            0 - Timestamp
+            1 - float  (Seconds from the EPOCH)
+            2 - int  (Nanoseconds from the EPOCH)
+            3 - datetime.datetime  (UTC).
+
+    :param bool strict_map_key:
+        If true (default), only str or bytes are accepted for map (dict) keys.
+
+    :param object_hook:
+        When specified, it should be callable.
+        Unpacker calls it with a dict argument after unpacking msgpack map.
+        (See also simplejson)
+
+    :param object_pairs_hook:
+        When specified, it should be callable.
+        Unpacker calls it with a list of key-value pairs after unpacking msgpack map.
+        (See also simplejson)
+
+    :param str unicode_errors:
+        The error handler for decoding unicode. (default: 'strict')
+        This option should be used only when you have msgpack data which
+        contains invalid UTF-8 string.
+
+    :param int max_buffer_size:
+        Limits size of data waiting unpacked.  0 means 2**32-1.
+        The default value is 100*1024*1024 (100MiB).
+        Raises `BufferFull` exception when it is insufficient.
+        You should set this parameter when unpacking data from untrusted source.
+
+    :param int max_str_len:
+        Deprecated, use *max_buffer_size* instead.
+        Limits max length of str. (default: max_buffer_size)
+
+    :param int max_bin_len:
+        Deprecated, use *max_buffer_size* instead.
+        Limits max length of bin. (default: max_buffer_size)
+
+    :param int max_array_len:
+        Limits max length of array.
+        (default: max_buffer_size)
+
+    :param int max_map_len:
+        Limits max length of map.
+        (default: max_buffer_size//2)
+
+    :param int max_ext_len:
+        Deprecated, use *max_buffer_size* instead.
+        Limits max size of ext type.  (default: max_buffer_size)
+
+    Example of streaming deserialize from file-like object::
+
+        unpacker = Unpacker(file_like)
+        for o in unpacker:
+            process(o)
+
+    Example of streaming deserialize from socket::
+
+        unpacker = Unpacker()
+        while True:
+            buf = sock.recv(1024**2)
+            if not buf:
+                break
+            unpacker.feed(buf)
+            for o in unpacker:
+                process(o)
+
+    Raises ``ExtraData`` when *packed* contains extra bytes.
+    Raises ``OutOfData`` when *packed* is incomplete.
+    Raises ``FormatError`` when *packed* is not valid msgpack.
+    Raises ``StackError`` when *packed* contains too nested.
+    Other exceptions can be raised during unpacking.
+    """
+
+    def __init__(
+        self,
+        file_like=None,
+        *,
+        read_size=0,
+        use_list=True,
+        raw=False,
+        timestamp=0,
+        strict_map_key=True,
+        object_hook=None,
+        object_pairs_hook=None,
+        list_hook=None,
+        unicode_errors=None,
+        max_buffer_size=100 * 1024 * 1024,
+        ext_hook=ExtType,
+        max_str_len=-1,
+        max_bin_len=-1,
+        max_array_len=-1,
+        max_map_len=-1,
+        max_ext_len=-1,
+    ):
+        if unicode_errors is None:
+            unicode_errors = "strict"
+
+        if file_like is None:
+            self._feeding = True
+        else:
+            if not callable(file_like.read):
+                raise TypeError("`file_like.read` must be callable")
+            self.file_like = file_like
+            self._feeding = False
+
+        #: array of bytes fed.
+        self._buffer = bytearray()
+        #: Which position we currently reads
+        self._buff_i = 0
+
+        # When Unpacker is used as an iterable, between the calls to next(),
+        # the buffer is not "consumed" completely, for efficiency sake.
+        # Instead, it is done sloppily.  To make sure we raise BufferFull at
+        # the correct moments, we have to keep track of how sloppy we were.
+        # Furthermore, when the buffer is incomplete (that is: in the case
+        # we raise an OutOfData) we need to rollback the buffer to the correct
+        # state, which _buf_checkpoint records.
+        self._buf_checkpoint = 0
+
+        if not max_buffer_size:
+            max_buffer_size = 2**31 - 1
+        if max_str_len == -1:
+            max_str_len = max_buffer_size
+        if max_bin_len == -1:
+            max_bin_len = max_buffer_size
+        if max_array_len == -1:
+            max_array_len = max_buffer_size
+        if max_map_len == -1:
+            max_map_len = max_buffer_size // 2
+        if max_ext_len == -1:
+            max_ext_len = max_buffer_size
+
+        self._max_buffer_size = max_buffer_size
+        if read_size > self._max_buffer_size:
+            raise ValueError("read_size must be smaller than max_buffer_size")
+        self._read_size = read_size or min(self._max_buffer_size, 16 * 1024)
+        self._raw = bool(raw)
+        self._strict_map_key = bool(strict_map_key)
+        self._unicode_errors = unicode_errors
+        self._use_list = use_list
+        if not (0 <= timestamp <= 3):
+            raise ValueError("timestamp must be 0..3")
+        self._timestamp = timestamp
+        self._list_hook = list_hook
+        self._object_hook = object_hook
+        self._object_pairs_hook = object_pairs_hook
+        self._ext_hook = ext_hook
+        self._max_str_len = max_str_len
+        self._max_bin_len = max_bin_len
+        self._max_array_len = max_array_len
+        self._max_map_len = max_map_len
+        self._max_ext_len = max_ext_len
+        self._stream_offset = 0
+
+        if list_hook is not None and not callable(list_hook):
+            raise TypeError("`list_hook` is not callable")
+        if object_hook is not None and not callable(object_hook):
+            raise TypeError("`object_hook` is not callable")
+        if object_pairs_hook is not None and not callable(object_pairs_hook):
+            raise TypeError("`object_pairs_hook` is not callable")
+        if object_hook is not None and object_pairs_hook is not None:
+            raise TypeError("object_pairs_hook and object_hook are mutually exclusive")
+        if not callable(ext_hook):
+            raise TypeError("`ext_hook` is not callable")
+
+    def feed(self, next_bytes):
+        assert self._feeding
+        view = _get_data_from_buffer(next_bytes)
+        if len(self._buffer) - self._buff_i + len(view) > self._max_buffer_size:
+            raise BufferFull
+
+        # Strip buffer before checkpoint before reading file.
+        if self._buf_checkpoint > 0:
+            del self._buffer[: self._buf_checkpoint]
+            self._buff_i -= self._buf_checkpoint
+            self._buf_checkpoint = 0
+
+        # Use extend here: INPLACE_ADD += doesn't reliably typecast memoryview in jython
+        self._buffer.extend(view)
+        view.release()
+
+    def _consume(self):
+        """Gets rid of the used parts of the buffer."""
+        self._stream_offset += self._buff_i - self._buf_checkpoint
+        self._buf_checkpoint = self._buff_i
+
+    def _got_extradata(self):
+        return self._buff_i < len(self._buffer)
+
+    def _get_extradata(self):
+        return self._buffer[self._buff_i :]
+
+    def read_bytes(self, n):
+        ret = self._read(n, raise_outofdata=False)
+        self._consume()
+        return ret
+
+    def _read(self, n, raise_outofdata=True):
+        # (int) -> bytearray
+        self._reserve(n, raise_outofdata=raise_outofdata)
+        i = self._buff_i
+        ret = self._buffer[i : i + n]
+        self._buff_i = i + len(ret)
+        return ret
+
+    def _reserve(self, n, raise_outofdata=True):
+        remain_bytes = len(self._buffer) - self._buff_i - n
+
+        # Fast path: buffer has n bytes already
+        if remain_bytes >= 0:
+            return
+
+        if self._feeding:
+            self._buff_i = self._buf_checkpoint
+            raise OutOfData
+
+        # Strip buffer before checkpoint before reading file.
+        if self._buf_checkpoint > 0:
+            del self._buffer[: self._buf_checkpoint]
+            self._buff_i -= self._buf_checkpoint
+            self._buf_checkpoint = 0
+
+        # Read from file
+        remain_bytes = -remain_bytes
+        if remain_bytes + len(self._buffer) > self._max_buffer_size:
+            raise BufferFull
+        while remain_bytes > 0:
+            to_read_bytes = max(self._read_size, remain_bytes)
+            read_data = self.file_like.read(to_read_bytes)
+            if not read_data:
+                break
+            assert isinstance(read_data, bytes)
+            self._buffer += read_data
+            remain_bytes -= len(read_data)
+
+        if len(self._buffer) < n + self._buff_i and raise_outofdata:
+            self._buff_i = 0  # rollback
+            raise OutOfData
+
+    def _read_header(self):
+        typ = TYPE_IMMEDIATE
+        n = 0
+        obj = None
+        self._reserve(1)
+        b = self._buffer[self._buff_i]
+        self._buff_i += 1
+        if b & 0b10000000 == 0:
+            obj = b
+        elif b & 0b11100000 == 0b11100000:
+            obj = -1 - (b ^ 0xFF)
+        elif b & 0b11100000 == 0b10100000:
+            n = b & 0b00011111
+            typ = TYPE_RAW
+            if n > self._max_str_len:
+                raise ValueError(f"{n} exceeds max_str_len({self._max_str_len})")
+            obj = self._read(n)
+        elif b & 0b11110000 == 0b10010000:
+            n = b & 0b00001111
+            typ = TYPE_ARRAY
+            if n > self._max_array_len:
+                raise ValueError(f"{n} exceeds max_array_len({self._max_array_len})")
+        elif b & 0b11110000 == 0b10000000:
+            n = b & 0b00001111
+            typ = TYPE_MAP
+            if n > self._max_map_len:
+                raise ValueError(f"{n} exceeds max_map_len({self._max_map_len})")
+        elif b == 0xC0:
+            obj = None
+        elif b == 0xC2:
+            obj = False
+        elif b == 0xC3:
+            obj = True
+        elif 0xC4 <= b <= 0xC6:
+            size, fmt, typ = _MSGPACK_HEADERS[b]
+            self._reserve(size)
+            if len(fmt) > 0:
+                n = struct.unpack_from(fmt, self._buffer, self._buff_i)[0]
+            else:
+                n = self._buffer[self._buff_i]
+            self._buff_i += size
+            if n > self._max_bin_len:
+                raise ValueError(f"{n} exceeds max_bin_len({self._max_bin_len})")
+            obj = self._read(n)
+        elif 0xC7 <= b <= 0xC9:
+            size, fmt, typ = _MSGPACK_HEADERS[b]
+            self._reserve(size)
+            L, n = struct.unpack_from(fmt, self._buffer, self._buff_i)
+            self._buff_i += size
+            if L > self._max_ext_len:
+                raise ValueError(f"{L} exceeds max_ext_len({self._max_ext_len})")
+            obj = self._read(L)
+        elif 0xCA <= b <= 0xD3:
+            size, fmt = _MSGPACK_HEADERS[b]
+            self._reserve(size)
+            if len(fmt) > 0:
+                obj = struct.unpack_from(fmt, self._buffer, self._buff_i)[0]
+            else:
+                obj = self._buffer[self._buff_i]
+            self._buff_i += size
+        elif 0xD4 <= b <= 0xD8:
+            size, fmt, typ = _MSGPACK_HEADERS[b]
+            if self._max_ext_len < size:
+                raise ValueError(f"{size} exceeds max_ext_len({self._max_ext_len})")
+            self._reserve(size + 1)
+            n, obj = struct.unpack_from(fmt, self._buffer, self._buff_i)
+            self._buff_i += size + 1
+        elif 0xD9 <= b <= 0xDB:
+            size, fmt, typ = _MSGPACK_HEADERS[b]
+            self._reserve(size)
+            if len(fmt) > 0:
+                (n,) = struct.unpack_from(fmt, self._buffer, self._buff_i)
+            else:
+                n = self._buffer[self._buff_i]
+            self._buff_i += size
+            if n > self._max_str_len:
+                raise ValueError(f"{n} exceeds max_str_len({self._max_str_len})")
+            obj = self._read(n)
+        elif 0xDC <= b <= 0xDD:
+            size, fmt, typ = _MSGPACK_HEADERS[b]
+            self._reserve(size)
+            (n,) = struct.unpack_from(fmt, self._buffer, self._buff_i)
+            self._buff_i += size
+            if n > self._max_array_len:
+                raise ValueError(f"{n} exceeds max_array_len({self._max_array_len})")
+        elif 0xDE <= b <= 0xDF:
+            size, fmt, typ = _MSGPACK_HEADERS[b]
+            self._reserve(size)
+            (n,) = struct.unpack_from(fmt, self._buffer, self._buff_i)
+            self._buff_i += size
+            if n > self._max_map_len:
+                raise ValueError(f"{n} exceeds max_map_len({self._max_map_len})")
+        else:
+            raise FormatError("Unknown header: 0x%x" % b)
+        return typ, n, obj
+
+    def _unpack(self, execute=EX_CONSTRUCT):
+        typ, n, obj = self._read_header()
+
+        if execute == EX_READ_ARRAY_HEADER:
+            if typ != TYPE_ARRAY:
+                raise ValueError("Expected array")
+            return n
+        if execute == EX_READ_MAP_HEADER:
+            if typ != TYPE_MAP:
+                raise ValueError("Expected map")
+            return n
+        # TODO should we eliminate the recursion?
+        if typ == TYPE_ARRAY:
+            if execute == EX_SKIP:
+                for i in range(n):
+                    # TODO check whether we need to call `list_hook`
+                    self._unpack(EX_SKIP)
+                return
+            ret = newlist_hint(n)
+            for i in range(n):
+                ret.append(self._unpack(EX_CONSTRUCT))
+            if self._list_hook is not None:
+                ret = self._list_hook(ret)
+            # TODO is the interaction between `list_hook` and `use_list` ok?
+            return ret if self._use_list else tuple(ret)
+        if typ == TYPE_MAP:
+            if execute == EX_SKIP:
+                for i in range(n):
+                    # TODO check whether we need to call hooks
+                    self._unpack(EX_SKIP)
+                    self._unpack(EX_SKIP)
+                return
+            if self._object_pairs_hook is not None:
+                ret = self._object_pairs_hook(
+                    (self._unpack(EX_CONSTRUCT), self._unpack(EX_CONSTRUCT)) for _ in range(n)
+                )
+            else:
+                ret = {}
+                for _ in range(n):
+                    key = self._unpack(EX_CONSTRUCT)
+                    if self._strict_map_key and type(key) not in (str, bytes):
+                        raise ValueError("%s is not allowed for map key" % str(type(key)))
+                    if isinstance(key, str):
+                        key = sys.intern(key)
+                    ret[key] = self._unpack(EX_CONSTRUCT)
+                if self._object_hook is not None:
+                    ret = self._object_hook(ret)
+            return ret
+        if execute == EX_SKIP:
+            return
+        if typ == TYPE_RAW:
+            if self._raw:
+                obj = bytes(obj)
+            else:
+                obj = obj.decode("utf_8", self._unicode_errors)
+            return obj
+        if typ == TYPE_BIN:
+            return bytes(obj)
+        if typ == TYPE_EXT:
+            if n == -1:  # timestamp
+                ts = Timestamp.from_bytes(bytes(obj))
+                if self._timestamp == 1:
+                    return ts.to_unix()
+                elif self._timestamp == 2:
+                    return ts.to_unix_nano()
+                elif self._timestamp == 3:
+                    return ts.to_datetime()
+                else:
+                    return ts
+            else:
+                return self._ext_hook(n, bytes(obj))
+        assert typ == TYPE_IMMEDIATE
+        return obj
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        try:
+            ret = self._unpack(EX_CONSTRUCT)
+            self._consume()
+            return ret
+        except OutOfData:
+            self._consume()
+            raise StopIteration
+        except RecursionError:
+            raise StackError
+
+    next = __next__
+
+    def skip(self):
+        self._unpack(EX_SKIP)
+        self._consume()
+
+    def unpack(self):
+        try:
+            ret = self._unpack(EX_CONSTRUCT)
+        except RecursionError:
+            raise StackError
+        self._consume()
+        return ret
+
+    def read_array_header(self):
+        ret = self._unpack(EX_READ_ARRAY_HEADER)
+        self._consume()
+        return ret
+
+    def read_map_header(self):
+        ret = self._unpack(EX_READ_MAP_HEADER)
+        self._consume()
+        return ret
+
+    def tell(self):
+        return self._stream_offset
+
+
+class Packer:
+    """
+    MessagePack Packer
+
+    Usage::
+
+        packer = Packer()
+        astream.write(packer.pack(a))
+        astream.write(packer.pack(b))
+
+    Packer's constructor has some keyword arguments:
+
+    :param default:
+        When specified, it should be callable.
+        Convert user type to builtin type that Packer supports.
+        See also simplejson's document.
+
+    :param bool use_single_float:
+        Use single precision float type for float. (default: False)
+
+    :param bool autoreset:
+        Reset buffer after each pack and return its content as `bytes`. (default: True).
+        If set this to false, use `bytes()` to get content and `.reset()` to clear buffer.
+
+    :param bool use_bin_type:
+        Use bin type introduced in msgpack spec 2.0 for bytes.
+        It also enables str8 type for unicode. (default: True)
+
+    :param bool strict_types:
+        If set to true, types will be checked to be exact. Derived classes
+        from serializable types will not be serialized and will be
+        treated as unsupported type and forwarded to default.
+        Additionally tuples will not be serialized as lists.
+        This is useful when trying to implement accurate serialization
+        for python types.
+
+    :param bool datetime:
+        If set to true, datetime with tzinfo is packed into Timestamp type.
+        Note that the tzinfo is stripped in the timestamp.
+        You can get UTC datetime with `timestamp=3` option of the Unpacker.
+
+    :param str unicode_errors:
+        The error handler for encoding unicode. (default: 'strict')
+        DO NOT USE THIS!!  This option is kept for very specific usage.
+
+    :param int buf_size:
+        Internal buffer size. This option is used only for C implementation.
+    """
+
+    def __init__(
+        self,
+        *,
+        default=None,
+        use_single_float=False,
+        autoreset=True,
+        use_bin_type=True,
+        strict_types=False,
+        datetime=False,
+        unicode_errors=None,
+        buf_size=None,
+    ):
+        self._strict_types = strict_types
+        self._use_float = use_single_float
+        self._autoreset = autoreset
+        self._use_bin_type = use_bin_type
+        self._buffer = BytesIO()
+        self._datetime = bool(datetime)
+        self._unicode_errors = unicode_errors or "strict"
+        if default is not None and not callable(default):
+            raise TypeError("default must be callable")
+        self._default = default
+
+    def _pack(
+        self,
+        obj,
+        nest_limit=DEFAULT_RECURSE_LIMIT,
+        check=isinstance,
+        check_type_strict=_check_type_strict,
+    ):
+        default_used = False
+        if self._strict_types:
+            check = check_type_strict
+            list_types = list
+        else:
+            list_types = (list, tuple)
+        while True:
+            if nest_limit < 0:
+                raise ValueError("recursion limit exceeded")
+            if obj is None:
+                return self._buffer.write(b"\xc0")
+            if check(obj, bool):
+                if obj:
+                    return self._buffer.write(b"\xc3")
+                return self._buffer.write(b"\xc2")
+            if check(obj, int):
+                if 0 <= obj < 0x80:
+                    return self._buffer.write(struct.pack("B", obj))
+                if -0x20 <= obj < 0:
+                    return self._buffer.write(struct.pack("b", obj))
+                if 0x80 <= obj <= 0xFF:
+                    return self._buffer.write(struct.pack("BB", 0xCC, obj))
+                if -0x80 <= obj < 0:
+                    return self._buffer.write(struct.pack(">Bb", 0xD0, obj))
+                if 0xFF < obj <= 0xFFFF:
+                    return self._buffer.write(struct.pack(">BH", 0xCD, obj))
+                if -0x8000 <= obj < -0x80:
+                    return self._buffer.write(struct.pack(">Bh", 0xD1, obj))
+                if 0xFFFF < obj <= 0xFFFFFFFF:
+                    return self._buffer.write(struct.pack(">BI", 0xCE, obj))
+                if -0x80000000 <= obj < -0x8000:
+                    return self._buffer.write(struct.pack(">Bi", 0xD2, obj))
+                if 0xFFFFFFFF < obj <= 0xFFFFFFFFFFFFFFFF:
+                    return self._buffer.write(struct.pack(">BQ", 0xCF, obj))
+                if -0x8000000000000000 <= obj < -0x80000000:
+                    return self._buffer.write(struct.pack(">Bq", 0xD3, obj))
+                if not default_used and self._default is not None:
+                    obj = self._default(obj)
+                    default_used = True
+                    continue
+                raise OverflowError("Integer value out of range")
+            if check(obj, (bytes, bytearray)):
+                n = len(obj)
+                if n >= 2**32:
+                    raise ValueError("%s is too large" % type(obj).__name__)
+                self._pack_bin_header(n)
+                return self._buffer.write(obj)
+            if check(obj, str):
+                obj = obj.encode("utf-8", self._unicode_errors)
+                n = len(obj)
+                if n >= 2**32:
+                    raise ValueError("String is too large")
+                self._pack_raw_header(n)
+                return self._buffer.write(obj)
+            if check(obj, memoryview):
+                n = obj.nbytes
+                if n >= 2**32:
+                    raise ValueError("Memoryview is too large")
+                self._pack_bin_header(n)
+                return self._buffer.write(obj)
+            if check(obj, float):
+                if self._use_float:
+                    return self._buffer.write(struct.pack(">Bf", 0xCA, obj))
+                return self._buffer.write(struct.pack(">Bd", 0xCB, obj))
+            if check(obj, (ExtType, Timestamp)):
+                if check(obj, Timestamp):
+                    code = -1
+                    data = obj.to_bytes()
+                else:
+                    code = obj.code
+                    data = obj.data
+                assert isinstance(code, int)
+                assert isinstance(data, bytes)
+                L = len(data)
+                if L == 1:
+                    self._buffer.write(b"\xd4")
+                elif L == 2:
+                    self._buffer.write(b"\xd5")
+                elif L == 4:
+                    self._buffer.write(b"\xd6")
+                elif L == 8:
+                    self._buffer.write(b"\xd7")
+                elif L == 16:
+                    self._buffer.write(b"\xd8")
+                elif L <= 0xFF:
+                    self._buffer.write(struct.pack(">BB", 0xC7, L))
+                elif L <= 0xFFFF:
+                    self._buffer.write(struct.pack(">BH", 0xC8, L))
+                else:
+                    self._buffer.write(struct.pack(">BI", 0xC9, L))
+                self._buffer.write(struct.pack("b", code))
+                self._buffer.write(data)
+                return
+            if check(obj, list_types):
+                n = len(obj)
+                self._pack_array_header(n)
+                for i in range(n):
+                    self._pack(obj[i], nest_limit - 1)
+                return
+            if check(obj, dict):
+                return self._pack_map_pairs(len(obj), obj.items(), nest_limit - 1)
+
+            if self._datetime and check(obj, _DateTime) and obj.tzinfo is not None:
+                obj = Timestamp.from_datetime(obj)
+                default_used = 1
+                continue
+
+            if not default_used and self._default is not None:
+                obj = self._default(obj)
+                default_used = 1
+                continue
+
+            if self._datetime and check(obj, _DateTime):
+                raise ValueError(f"Cannot serialize {obj!r} where tzinfo=None")
+
+            raise TypeError(f"Cannot serialize {obj!r}")
+
+    def pack(self, obj):
+        try:
+            self._pack(obj)
+        except:
+            self._buffer = BytesIO()  # force reset
+            raise
+        if self._autoreset:
+            ret = self._buffer.getvalue()
+            self._buffer = BytesIO()
+            return ret
+
+    def pack_map_pairs(self, pairs):
+        self._pack_map_pairs(len(pairs), pairs)
+        if self._autoreset:
+            ret = self._buffer.getvalue()
+            self._buffer = BytesIO()
+            return ret
+
+    def pack_array_header(self, n):
+        if n >= 2**32:
+            raise ValueError
+        self._pack_array_header(n)
+        if self._autoreset:
+            ret = self._buffer.getvalue()
+            self._buffer = BytesIO()
+            return ret
+
+    def pack_map_header(self, n):
+        if n >= 2**32:
+            raise ValueError
+        self._pack_map_header(n)
+        if self._autoreset:
+            ret = self._buffer.getvalue()
+            self._buffer = BytesIO()
+            return ret
+
+    def pack_ext_type(self, typecode, data):
+        if not isinstance(typecode, int):
+            raise TypeError("typecode must have int type.")
+        if not 0 <= typecode <= 127:
+            raise ValueError("typecode should be 0-127")
+        if not isinstance(data, bytes):
+            raise TypeError("data must have bytes type")
+        L = len(data)
+        if L > 0xFFFFFFFF:
+            raise ValueError("Too large data")
+        if L == 1:
+            self._buffer.write(b"\xd4")
+        elif L == 2:
+            self._buffer.write(b"\xd5")
+        elif L == 4:
+            self._buffer.write(b"\xd6")
+        elif L == 8:
+            self._buffer.write(b"\xd7")
+        elif L == 16:
+            self._buffer.write(b"\xd8")
+        elif L <= 0xFF:
+            self._buffer.write(b"\xc7" + struct.pack("B", L))
+        elif L <= 0xFFFF:
+            self._buffer.write(b"\xc8" + struct.pack(">H", L))
+        else:
+            self._buffer.write(b"\xc9" + struct.pack(">I", L))
+        self._buffer.write(struct.pack("B", typecode))
+        self._buffer.write(data)
+
+    def _pack_array_header(self, n):
+        if n <= 0x0F:
+            return self._buffer.write(struct.pack("B", 0x90 + n))
+        if n <= 0xFFFF:
+            return self._buffer.write(struct.pack(">BH", 0xDC, n))
+        if n <= 0xFFFFFFFF:
+            return self._buffer.write(struct.pack(">BI", 0xDD, n))
+        raise ValueError("Array is too large")
+
+    def _pack_map_header(self, n):
+        if n <= 0x0F:
+            return self._buffer.write(struct.pack("B", 0x80 + n))
+        if n <= 0xFFFF:
+            return self._buffer.write(struct.pack(">BH", 0xDE, n))
+        if n <= 0xFFFFFFFF:
+            return self._buffer.write(struct.pack(">BI", 0xDF, n))
+        raise ValueError("Dict is too large")
+
+    def _pack_map_pairs(self, n, pairs, nest_limit=DEFAULT_RECURSE_LIMIT):
+        self._pack_map_header(n)
+        for k, v in pairs:
+            self._pack(k, nest_limit - 1)
+            self._pack(v, nest_limit - 1)
+
+    def _pack_raw_header(self, n):
+        if n <= 0x1F:
+            self._buffer.write(struct.pack("B", 0xA0 + n))
+        elif self._use_bin_type and n <= 0xFF:
+            self._buffer.write(struct.pack(">BB", 0xD9, n))
+        elif n <= 0xFFFF:
+            self._buffer.write(struct.pack(">BH", 0xDA, n))
+        elif n <= 0xFFFFFFFF:
+            self._buffer.write(struct.pack(">BI", 0xDB, n))
+        else:
+            raise ValueError("Raw is too large")
+
+    def _pack_bin_header(self, n):
+        if not self._use_bin_type:
+            return self._pack_raw_header(n)
+        elif n <= 0xFF:
+            return self._buffer.write(struct.pack(">BB", 0xC4, n))
+        elif n <= 0xFFFF:
+            return self._buffer.write(struct.pack(">BH", 0xC5, n))
+        elif n <= 0xFFFFFFFF:
+            return self._buffer.write(struct.pack(">BI", 0xC6, n))
+        else:
+            raise ValueError("Bin is too large")
+
+    def bytes(self):
+        """Return internal buffer contents as bytes object"""
+        return self._buffer.getvalue()
+
+    def reset(self):
+        """Reset internal buffer.
+
+        This method is useful only when autoreset=False.
+        """
+        self._buffer = BytesIO()
+
+    def getbuffer(self):
+        """Return view of internal buffer."""
+        if _USING_STRINGBUILDER:
+            return memoryview(self.bytes())
+        else:
+            return self._buffer.getbuffer()
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE
new file mode 100644
index 0000000..6f62d44
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE
@@ -0,0 +1,3 @@
+This software is made available under the terms of *either* of the licenses
+found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made
+under the terms of *both* these licenses.
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE.APACHE b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE.APACHE
new file mode 100644
index 0000000..f433b1a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE.APACHE
@@ -0,0 +1,177 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE.BSD b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE.BSD
new file mode 100644
index 0000000..42ce7b7
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE.BSD
@@ -0,0 +1,23 @@
+Copyright (c) Donald Stufft and individual contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__init__.py
new file mode 100644
index 0000000..a6bdf59
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__init__.py
@@ -0,0 +1,15 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+__title__ = "packaging"
+__summary__ = "Core utilities for Python packages"
+__uri__ = "https://github.com/pypa/packaging"
+
+__version__ = "26.2"
+
+__author__ = "Donald Stufft and individual contributors"
+__email__ = "donald@stufft.io"
+
+__license__ = "BSD-2-Clause or Apache-2.0"
+__copyright__ = f"2014 {__author__}"
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..645895b
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc
new file mode 100644
index 0000000..64b5f6c
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc
new file mode 100644
index 0000000..c33d219
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc
new file mode 100644
index 0000000..2f233f1
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_parser.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_parser.cpython-312.pyc
new file mode 100644
index 0000000..5718dd6
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_parser.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-312.pyc
new file mode 100644
index 0000000..97a5ad0
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc
new file mode 100644
index 0000000..72861d8
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/dependency_groups.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/dependency_groups.cpython-312.pyc
new file mode 100644
index 0000000..5202577
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/dependency_groups.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/direct_url.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/direct_url.cpython-312.pyc
new file mode 100644
index 0000000..1d3c6dc
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/direct_url.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/errors.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/errors.cpython-312.pyc
new file mode 100644
index 0000000..61845ac
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/errors.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-312.pyc
new file mode 100644
index 0000000..cf007b0
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/metadata.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/metadata.cpython-312.pyc
new file mode 100644
index 0000000..2aa9ee7
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/metadata.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/pylock.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/pylock.cpython-312.pyc
new file mode 100644
index 0000000..c7be071
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/pylock.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-312.pyc
new file mode 100644
index 0000000..795a08c
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc
new file mode 100644
index 0000000..1a63f8a
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/tags.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/tags.cpython-312.pyc
new file mode 100644
index 0000000..a1d0069
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/tags.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-312.pyc
new file mode 100644
index 0000000..728d3d8
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-312.pyc
new file mode 100644
index 0000000..c7420e6
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_elffile.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_elffile.py
new file mode 100644
index 0000000..497b064
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_elffile.py
@@ -0,0 +1,108 @@
+"""
+ELF file parser.
+
+This provides a class ``ELFFile`` that parses an ELF executable in a similar
+interface to ``ZipFile``. Only the read interface is implemented.
+
+ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html
+"""
+
+from __future__ import annotations
+
+import enum
+import os
+import struct
+from typing import IO
+
+
+class ELFInvalid(ValueError):
+    pass
+
+
+class EIClass(enum.IntEnum):
+    C32 = 1
+    C64 = 2
+
+
+class EIData(enum.IntEnum):
+    Lsb = 1
+    Msb = 2
+
+
+class EMachine(enum.IntEnum):
+    I386 = 3
+    S390 = 22
+    Arm = 40
+    X8664 = 62
+    AArc64 = 183
+
+
+class ELFFile:
+    """
+    Representation of an ELF executable.
+    """
+
+    def __init__(self, f: IO[bytes]) -> None:
+        self._f = f
+
+        try:
+            ident = self._read("16B")
+        except struct.error as e:
+            raise ELFInvalid("unable to parse identification") from e
+        magic = bytes(ident[:4])
+        if magic != b"\x7fELF":
+            raise ELFInvalid(f"invalid magic: {magic!r}")
+
+        self.capacity = ident[4]  # Format for program header (bitness).
+        self.encoding = ident[5]  # Data structure encoding (endianness).
+
+        try:
+            # e_fmt: Format for program header.
+            # p_fmt: Format for section header.
+            # p_idx: Indexes to find p_type, p_offset, and p_filesz.
+            e_fmt, self._p_fmt, self._p_idx = {
+                (1, 1): ("HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)),  # 32-bit MSB.
+                (2, 1): ("HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)),  # 64-bit MSB.
+            }[(self.capacity, self.encoding)]
+        except KeyError as e:
+            raise ELFInvalid(
+                f"unrecognized capacity ({self.capacity}) or encoding ({self.encoding})"
+            ) from e
+
+        try:
+            (
+                _,
+                self.machine,  # Architecture type.
+                _,
+                _,
+                self._e_phoff,  # Offset of program header.
+                _,
+                self.flags,  # Processor-specific flags.
+                _,
+                self._e_phentsize,  # Size of section.
+                self._e_phnum,  # Number of sections.
+            ) = self._read(e_fmt)
+        except struct.error as e:
+            raise ELFInvalid("unable to parse machine and section information") from e
+
+    def _read(self, fmt: str) -> tuple[int, ...]:
+        return struct.unpack(fmt, self._f.read(struct.calcsize(fmt)))
+
+    @property
+    def interpreter(self) -> str | None:
+        """
+        The path recorded in the ``PT_INTERP`` section header.
+        """
+        for index in range(self._e_phnum):
+            self._f.seek(self._e_phoff + self._e_phentsize * index)
+            try:
+                data = self._read(self._p_fmt)
+            except struct.error:
+                continue
+            if data[self._p_idx[0]] != 3:  # Not PT_INTERP.
+                continue
+            self._f.seek(data[self._p_idx[1]])
+            return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0")
+        return None
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_manylinux.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_manylinux.py
new file mode 100644
index 0000000..0e79e8a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_manylinux.py
@@ -0,0 +1,262 @@
+from __future__ import annotations
+
+import collections
+import contextlib
+import functools
+import os
+import re
+import sys
+import warnings
+from typing import Generator, Iterator, NamedTuple, Sequence
+
+from ._elffile import EIClass, EIData, ELFFile, EMachine
+
+EF_ARM_ABIMASK = 0xFF000000
+EF_ARM_ABI_VER5 = 0x05000000
+EF_ARM_ABI_FLOAT_HARD = 0x00000400
+
+_ALLOWED_ARCHS = {
+    "x86_64",
+    "aarch64",
+    "ppc64",
+    "ppc64le",
+    "s390x",
+    "loongarch64",
+    "riscv64",
+}
+
+
+# `os.PathLike` not a generic type until Python 3.9, so sticking with `str`
+# as the type for `path` until then.
+@contextlib.contextmanager
+def _parse_elf(path: str) -> Generator[ELFFile | None, None, None]:
+    try:
+        with open(path, "rb") as f:
+            yield ELFFile(f)
+    except (OSError, TypeError, ValueError):
+        yield None
+
+
+def _is_linux_armhf(executable: str) -> bool:
+    # hard-float ABI can be detected from the ELF header of the running
+    # process
+    # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf
+    with _parse_elf(executable) as f:
+        return (
+            f is not None
+            and f.capacity == EIClass.C32
+            and f.encoding == EIData.Lsb
+            and f.machine == EMachine.Arm
+            and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5
+            and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD
+        )
+
+
+def _is_linux_i686(executable: str) -> bool:
+    with _parse_elf(executable) as f:
+        return (
+            f is not None
+            and f.capacity == EIClass.C32
+            and f.encoding == EIData.Lsb
+            and f.machine == EMachine.I386
+        )
+
+
+def _have_compatible_abi(executable: str, archs: Sequence[str]) -> bool:
+    if "armv7l" in archs:
+        return _is_linux_armhf(executable)
+    if "i686" in archs:
+        return _is_linux_i686(executable)
+    return any(arch in _ALLOWED_ARCHS for arch in archs)
+
+
+# If glibc ever changes its major version, we need to know what the last
+# minor version was, so we can build the complete list of all versions.
+# For now, guess what the highest minor version might be, assume it will
+# be 50 for testing. Once this actually happens, update the dictionary
+# with the actual value.
+_LAST_GLIBC_MINOR: dict[int, int] = collections.defaultdict(lambda: 50)
+
+
+class _GLibCVersion(NamedTuple):
+    major: int
+    minor: int
+
+
+def _glibc_version_string_confstr() -> str | None:
+    """
+    Primary implementation of glibc_version_string using os.confstr.
+    """
+    # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely
+    # to be broken or missing. This strategy is used in the standard library
+    # platform module.
+    # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183
+    try:
+        # Should be a string like "glibc 2.17".
+        version_string: str | None = os.confstr("CS_GNU_LIBC_VERSION")
+        assert version_string is not None
+        _, version = version_string.rsplit()
+    except (AssertionError, AttributeError, OSError, ValueError):
+        # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)...
+        return None
+    return version
+
+
+def _glibc_version_string_ctypes() -> str | None:
+    """
+    Fallback implementation of glibc_version_string using ctypes.
+    """
+    try:
+        import ctypes  # noqa: PLC0415
+    except ImportError:
+        return None
+
+    # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen
+    # manpage says, "If filename is NULL, then the returned handle is for the
+    # main program". This way we can let the linker do the work to figure out
+    # which libc our process is actually using.
+    #
+    # We must also handle the special case where the executable is not a
+    # dynamically linked executable. This can occur when using musl libc,
+    # for example. In this situation, dlopen() will error, leading to an
+    # OSError. Interestingly, at least in the case of musl, there is no
+    # errno set on the OSError. The single string argument used to construct
+    # OSError comes from libc itself and is therefore not portable to
+    # hard code here. In any case, failure to call dlopen() means we
+    # can proceed, so we bail on our attempt.
+    try:
+        process_namespace = ctypes.CDLL(None)
+    except OSError:
+        return None
+
+    try:
+        gnu_get_libc_version = process_namespace.gnu_get_libc_version
+    except AttributeError:
+        # Symbol doesn't exist -> therefore, we are not linked to
+        # glibc.
+        return None
+
+    # Call gnu_get_libc_version, which returns a string like "2.5"
+    gnu_get_libc_version.restype = ctypes.c_char_p
+    version_str: str = gnu_get_libc_version()
+    # py2 / py3 compatibility:
+    if not isinstance(version_str, str):
+        version_str = version_str.decode("ascii")
+
+    return version_str
+
+
+def _glibc_version_string() -> str | None:
+    """Returns glibc version string, or None if not using glibc."""
+    return _glibc_version_string_confstr() or _glibc_version_string_ctypes()
+
+
+def _parse_glibc_version(version_str: str) -> _GLibCVersion:
+    """Parse glibc version.
+
+    We use a regexp instead of str.split because we want to discard any
+    random junk that might come after the minor version -- this might happen
+    in patched/forked versions of glibc (e.g. Linaro's version of glibc
+    uses version strings like "2.20-2014.11"). See gh-3588.
+    """
+    m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str)
+    if not m:
+        warnings.warn(
+            f"Expected glibc version with 2 components major.minor, got: {version_str}",
+            RuntimeWarning,
+            stacklevel=2,
+        )
+        return _GLibCVersion(-1, -1)
+    return _GLibCVersion(int(m.group("major")), int(m.group("minor")))
+
+
+@functools.lru_cache
+def _get_glibc_version() -> _GLibCVersion:
+    version_str = _glibc_version_string()
+    if version_str is None:
+        return _GLibCVersion(-1, -1)
+    return _parse_glibc_version(version_str)
+
+
+# From PEP 513, PEP 600
+def _is_compatible(arch: str, version: _GLibCVersion) -> bool:
+    sys_glibc = _get_glibc_version()
+    if sys_glibc < version:
+        return False
+    # Check for presence of _manylinux module.
+    try:
+        import _manylinux  # noqa: PLC0415
+    except ImportError:
+        return True
+    if hasattr(_manylinux, "manylinux_compatible"):
+        result = _manylinux.manylinux_compatible(version[0], version[1], arch)
+        if result is not None:
+            return bool(result)
+        return True
+    if version == _GLibCVersion(2, 5) and hasattr(_manylinux, "manylinux1_compatible"):
+        return bool(_manylinux.manylinux1_compatible)
+    if version == _GLibCVersion(2, 12) and hasattr(
+        _manylinux, "manylinux2010_compatible"
+    ):
+        return bool(_manylinux.manylinux2010_compatible)
+    if version == _GLibCVersion(2, 17) and hasattr(
+        _manylinux, "manylinux2014_compatible"
+    ):
+        return bool(_manylinux.manylinux2014_compatible)
+    return True
+
+
+_LEGACY_MANYLINUX_MAP: dict[_GLibCVersion, str] = {
+    # CentOS 7 w/ glibc 2.17 (PEP 599)
+    _GLibCVersion(2, 17): "manylinux2014",
+    # CentOS 6 w/ glibc 2.12 (PEP 571)
+    _GLibCVersion(2, 12): "manylinux2010",
+    # CentOS 5 w/ glibc 2.5 (PEP 513)
+    _GLibCVersion(2, 5): "manylinux1",
+}
+
+
+def platform_tags(archs: Sequence[str]) -> Iterator[str]:
+    """Generate manylinux tags compatible to the current platform.
+
+    :param archs: Sequence of compatible architectures.
+        The first one shall be the closest to the actual architecture and be the part of
+        platform tag after the ``linux_`` prefix, e.g. ``x86_64``.
+        The ``linux_`` prefix is assumed as a prerequisite for the current platform to
+        be manylinux-compatible.
+
+    :returns: An iterator of compatible manylinux tags.
+    """
+    if not _have_compatible_abi(sys.executable, archs):
+        return
+    # Oldest glibc to be supported regardless of architecture is (2, 17).
+    too_old_glibc2 = _GLibCVersion(2, 16)
+    if set(archs) & {"x86_64", "i686"}:
+        # On x86/i686 also oldest glibc to be supported is (2, 5).
+        too_old_glibc2 = _GLibCVersion(2, 4)
+    current_glibc = _GLibCVersion(*_get_glibc_version())
+    glibc_max_list = [current_glibc]
+    # We can assume compatibility across glibc major versions.
+    # https://sourceware.org/bugzilla/show_bug.cgi?id=24636
+    #
+    # Build a list of maximum glibc versions so that we can
+    # output the canonical list of all glibc from current_glibc
+    # down to too_old_glibc2, including all intermediary versions.
+    for glibc_major in range(current_glibc.major - 1, 1, -1):
+        glibc_minor = _LAST_GLIBC_MINOR[glibc_major]
+        glibc_max_list.append(_GLibCVersion(glibc_major, glibc_minor))
+    for arch in archs:
+        for glibc_max in glibc_max_list:
+            if glibc_max.major == too_old_glibc2.major:
+                min_minor = too_old_glibc2.minor
+            else:
+                # For other glibc major versions oldest supported is (x, 0).
+                min_minor = -1
+            for glibc_minor in range(glibc_max.minor, min_minor, -1):
+                glibc_version = _GLibCVersion(glibc_max.major, glibc_minor)
+                if _is_compatible(arch, glibc_version):
+                    yield "manylinux_{}_{}_{}".format(*glibc_version, arch)
+
+                    # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags.
+                    if legacy_tag := _LEGACY_MANYLINUX_MAP.get(glibc_version):
+                        yield f"{legacy_tag}_{arch}"
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_musllinux.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_musllinux.py
new file mode 100644
index 0000000..4e8116a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_musllinux.py
@@ -0,0 +1,85 @@
+"""PEP 656 support.
+
+This module implements logic to detect if the currently running Python is
+linked against musl, and what musl version is used.
+"""
+
+from __future__ import annotations
+
+import functools
+import re
+import subprocess
+import sys
+from typing import Iterator, NamedTuple, Sequence
+
+from ._elffile import ELFFile
+
+
+class _MuslVersion(NamedTuple):
+    major: int
+    minor: int
+
+
+def _parse_musl_version(output: str) -> _MuslVersion | None:
+    lines = [n for n in (n.strip() for n in output.splitlines()) if n]
+    if len(lines) < 2 or lines[0][:4] != "musl":
+        return None
+    m = re.match(r"Version (\d+)\.(\d+)", lines[1])
+    if not m:
+        return None
+    return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2)))
+
+
+@functools.lru_cache
+def _get_musl_version(executable: str) -> _MuslVersion | None:
+    """Detect currently-running musl runtime version.
+
+    This is done by checking the specified executable's dynamic linking
+    information, and invoking the loader to parse its output for a version
+    string. If the loader is musl, the output would be something like::
+
+        musl libc (x86_64)
+        Version 1.2.2
+        Dynamic Program Loader
+    """
+    try:
+        with open(executable, "rb") as f:
+            ld = ELFFile(f).interpreter
+    except (OSError, TypeError, ValueError):
+        return None
+    if ld is None or "musl" not in ld:
+        return None
+    proc = subprocess.run([ld], check=False, stderr=subprocess.PIPE, text=True)
+    return _parse_musl_version(proc.stderr)
+
+
+def platform_tags(archs: Sequence[str]) -> Iterator[str]:
+    """Generate musllinux tags compatible to the current platform.
+
+    :param archs: Sequence of compatible architectures.
+        The first one shall be the closest to the actual architecture and be the part of
+        platform tag after the ``linux_`` prefix, e.g. ``x86_64``.
+        The ``linux_`` prefix is assumed as a prerequisite for the current platform to
+        be musllinux-compatible.
+
+    :returns: An iterator of compatible musllinux tags.
+    """
+    sys_musl = _get_musl_version(sys.executable)
+    if sys_musl is None:  # Python not dynamically linked against musl.
+        return
+    for arch in archs:
+        for minor in range(sys_musl.minor, -1, -1):
+            yield f"musllinux_{sys_musl.major}_{minor}_{arch}"
+
+
+if __name__ == "__main__":  # pragma: no cover
+    import sysconfig
+
+    plat = sysconfig.get_platform()
+    assert plat.startswith("linux-"), "not linux"
+
+    print("plat:", plat)
+    print("musl:", _get_musl_version(sys.executable))
+    print("tags:", end=" ")
+    for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])):
+        print(t, end="\n      ")
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_parser.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_parser.py
new file mode 100644
index 0000000..d320269
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_parser.py
@@ -0,0 +1,393 @@
+"""Handwritten parser of dependency specifiers.
+
+The docstring for each __parse_* function contains EBNF-inspired grammar representing
+the implementation.
+"""
+
+from __future__ import annotations
+
+import ast
+from typing import List, Literal, NamedTuple, Sequence, Tuple, Union
+
+from ._tokenizer import DEFAULT_RULES, Tokenizer
+
+
+class Node:
+    __slots__ = ("value",)
+
+    def __init__(self, value: str) -> None:
+        self.value = value
+
+    def __str__(self) -> str:
+        return self.value
+
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__}({self.value!r})>"
+
+    def serialize(self) -> str:
+        raise NotImplementedError
+
+    def __getstate__(self) -> str:
+        # Return just the value string for compactness and stability.
+        return self.value
+
+    def _restore_value(self, value: object) -> None:
+        if not isinstance(value, str):
+            raise TypeError(
+                f"Cannot restore {self.__class__.__name__} value from {value!r}"
+            )
+        self.value = value
+
+    def __setstate__(self, state: object) -> None:
+        if isinstance(state, str):
+            # New format (26.2+): just the value string.
+            self._restore_value(state)
+            return
+        if isinstance(state, tuple) and len(state) == 2:
+            # Old format (packaging <= 26.0, __slots__): (None, {slot: value}).
+            _, slot_dict = state
+            if isinstance(slot_dict, dict) and "value" in slot_dict:
+                self._restore_value(slot_dict["value"])
+                return
+        if isinstance(state, dict) and "value" in state:
+            # Old format (packaging <= 25.0, no __slots__): plain __dict__.
+            self._restore_value(state["value"])
+            return
+        raise TypeError(f"Cannot restore {self.__class__.__name__} from {state!r}")
+
+
+class Variable(Node):
+    __slots__ = ()
+
+    def serialize(self) -> str:
+        return str(self)
+
+
+class Value(Node):
+    __slots__ = ()
+
+    def serialize(self) -> str:
+        return f'"{self}"'
+
+
+class Op(Node):
+    __slots__ = ()
+
+    def serialize(self) -> str:
+        return str(self)
+
+
+MarkerLogical = Literal["and", "or"]
+MarkerVar = Union[Variable, Value]
+MarkerItem = Tuple[MarkerVar, Op, MarkerVar]
+MarkerAtom = Union[MarkerItem, Sequence["MarkerAtom"]]
+MarkerList = List[Union["MarkerList", MarkerAtom, MarkerLogical]]
+
+
+class ParsedRequirement(NamedTuple):
+    name: str
+    url: str
+    extras: list[str]
+    specifier: str
+    marker: MarkerList | None
+
+
+# --------------------------------------------------------------------------------------
+# Recursive descent parser for dependency specifier
+# --------------------------------------------------------------------------------------
+def parse_requirement(source: str) -> ParsedRequirement:
+    return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES))
+
+
+def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement:
+    """
+    requirement = WS? IDENTIFIER WS? extras WS? requirement_details
+    """
+    tokenizer.consume("WS")
+
+    name_token = tokenizer.expect(
+        "IDENTIFIER", expected="package name at the start of dependency specifier"
+    )
+    name = name_token.text
+    tokenizer.consume("WS")
+
+    extras = _parse_extras(tokenizer)
+    tokenizer.consume("WS")
+
+    url, specifier, marker = _parse_requirement_details(tokenizer)
+    tokenizer.expect("END", expected="end of dependency specifier")
+
+    return ParsedRequirement(name, url, extras, specifier, marker)
+
+
+def _parse_requirement_details(
+    tokenizer: Tokenizer,
+) -> tuple[str, str, MarkerList | None]:
+    """
+    requirement_details = AT URL (WS requirement_marker?)?
+                        | specifier WS? (requirement_marker)?
+    """
+
+    specifier = ""
+    url = ""
+    marker = None
+
+    if tokenizer.check("AT"):
+        tokenizer.read()
+        tokenizer.consume("WS")
+
+        url_start = tokenizer.position
+        url = tokenizer.expect("URL", expected="URL after @").text
+        if tokenizer.check("END", peek=True):
+            return (url, specifier, marker)
+
+        tokenizer.expect("WS", expected="whitespace after URL")
+
+        # The input might end after whitespace.
+        if tokenizer.check("END", peek=True):
+            return (url, specifier, marker)
+
+        marker = _parse_requirement_marker(
+            tokenizer,
+            span_start=url_start,
+            expected="semicolon (after URL and whitespace)",
+        )
+    else:
+        specifier_start = tokenizer.position
+        specifier = _parse_specifier(tokenizer)
+        tokenizer.consume("WS")
+
+        if tokenizer.check("END", peek=True):
+            return (url, specifier, marker)
+
+        marker = _parse_requirement_marker(
+            tokenizer,
+            span_start=specifier_start,
+            expected=(
+                "comma (within version specifier), semicolon (after version specifier)"
+                if specifier
+                else "semicolon (after name with no version specifier)"
+            ),
+        )
+
+    return (url, specifier, marker)
+
+
+def _parse_requirement_marker(
+    tokenizer: Tokenizer, *, span_start: int, expected: str
+) -> MarkerList:
+    """
+    requirement_marker = SEMICOLON marker WS?
+    """
+
+    if not tokenizer.check("SEMICOLON"):
+        tokenizer.raise_syntax_error(
+            f"Expected {expected} or end",
+            span_start=span_start,
+            span_end=None,
+        )
+    tokenizer.read()
+
+    marker = _parse_marker(tokenizer)
+    tokenizer.consume("WS")
+
+    return marker
+
+
+def _parse_extras(tokenizer: Tokenizer) -> list[str]:
+    """
+    extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)?
+    """
+    if not tokenizer.check("LEFT_BRACKET", peek=True):
+        return []
+
+    with tokenizer.enclosing_tokens(
+        "LEFT_BRACKET",
+        "RIGHT_BRACKET",
+        around="extras",
+    ):
+        tokenizer.consume("WS")
+        extras = _parse_extras_list(tokenizer)
+        tokenizer.consume("WS")
+
+    return extras
+
+
+def _parse_extras_list(tokenizer: Tokenizer) -> list[str]:
+    """
+    extras_list = identifier (wsp* ',' wsp* identifier)*
+    """
+    extras: list[str] = []
+
+    if not tokenizer.check("IDENTIFIER"):
+        return extras
+
+    extras.append(tokenizer.read().text)
+
+    while True:
+        tokenizer.consume("WS")
+        if tokenizer.check("IDENTIFIER", peek=True):
+            tokenizer.raise_syntax_error("Expected comma between extra names")
+        elif not tokenizer.check("COMMA"):
+            break
+
+        tokenizer.read()
+        tokenizer.consume("WS")
+
+        extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma")
+        extras.append(extra_token.text)
+
+    return extras
+
+
+def _parse_specifier(tokenizer: Tokenizer) -> str:
+    """
+    specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS
+              | WS? version_many WS?
+    """
+    with tokenizer.enclosing_tokens(
+        "LEFT_PARENTHESIS",
+        "RIGHT_PARENTHESIS",
+        around="version specifier",
+    ):
+        tokenizer.consume("WS")
+        parsed_specifiers = _parse_version_many(tokenizer)
+        tokenizer.consume("WS")
+
+    return parsed_specifiers
+
+
+def _parse_version_many(tokenizer: Tokenizer) -> str:
+    """
+    version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)?
+    """
+    parsed_specifiers = ""
+    while tokenizer.check("SPECIFIER"):
+        span_start = tokenizer.position
+        parsed_specifiers += tokenizer.read().text
+        if tokenizer.check("VERSION_PREFIX_TRAIL", peek=True):
+            tokenizer.raise_syntax_error(
+                ".* suffix can only be used with `==` or `!=` operators",
+                span_start=span_start,
+                span_end=tokenizer.position + 1,
+            )
+        if tokenizer.check("VERSION_LOCAL_LABEL_TRAIL", peek=True):
+            tokenizer.raise_syntax_error(
+                "Local version label can only be used with `==` or `!=` operators",
+                span_start=span_start,
+                span_end=tokenizer.position,
+            )
+        tokenizer.consume("WS")
+        if not tokenizer.check("COMMA"):
+            break
+        parsed_specifiers += tokenizer.read().text
+        tokenizer.consume("WS")
+
+    return parsed_specifiers
+
+
+# --------------------------------------------------------------------------------------
+# Recursive descent parser for marker expression
+# --------------------------------------------------------------------------------------
+def parse_marker(source: str) -> MarkerList:
+    return _parse_full_marker(Tokenizer(source, rules=DEFAULT_RULES))
+
+
+def _parse_full_marker(tokenizer: Tokenizer) -> MarkerList:
+    retval = _parse_marker(tokenizer)
+    tokenizer.expect("END", expected="end of marker expression")
+    return retval
+
+
+def _parse_marker(tokenizer: Tokenizer) -> MarkerList:
+    """
+    marker = marker_atom (BOOLOP marker_atom)+
+    """
+    expression = [_parse_marker_atom(tokenizer)]
+    while tokenizer.check("BOOLOP"):
+        token = tokenizer.read()
+        expr_right = _parse_marker_atom(tokenizer)
+        expression.extend((token.text, expr_right))
+    return expression
+
+
+def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom:
+    """
+    marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS?
+                | WS? marker_item WS?
+    """
+
+    tokenizer.consume("WS")
+    if tokenizer.check("LEFT_PARENTHESIS", peek=True):
+        with tokenizer.enclosing_tokens(
+            "LEFT_PARENTHESIS",
+            "RIGHT_PARENTHESIS",
+            around="marker expression",
+        ):
+            tokenizer.consume("WS")
+            marker: MarkerAtom = _parse_marker(tokenizer)
+            tokenizer.consume("WS")
+    else:
+        marker = _parse_marker_item(tokenizer)
+    tokenizer.consume("WS")
+    return marker
+
+
+def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem:
+    """
+    marker_item = WS? marker_var WS? marker_op WS? marker_var WS?
+    """
+    tokenizer.consume("WS")
+    marker_var_left = _parse_marker_var(tokenizer)
+    tokenizer.consume("WS")
+    marker_op = _parse_marker_op(tokenizer)
+    tokenizer.consume("WS")
+    marker_var_right = _parse_marker_var(tokenizer)
+    tokenizer.consume("WS")
+    return (marker_var_left, marker_op, marker_var_right)
+
+
+def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar:  # noqa: RET503
+    """
+    marker_var = VARIABLE | QUOTED_STRING
+    """
+    if tokenizer.check("VARIABLE"):
+        return process_env_var(tokenizer.read().text.replace(".", "_"))
+    elif tokenizer.check("QUOTED_STRING"):
+        return process_python_str(tokenizer.read().text)
+    else:
+        tokenizer.raise_syntax_error(
+            message="Expected a marker variable or quoted string"
+        )
+
+
+def process_env_var(env_var: str) -> Variable:
+    if env_var in ("platform_python_implementation", "python_implementation"):
+        return Variable("platform_python_implementation")
+    else:
+        return Variable(env_var)
+
+
+def process_python_str(python_str: str) -> Value:
+    value = ast.literal_eval(python_str)
+    return Value(str(value))
+
+
+def _parse_marker_op(tokenizer: Tokenizer) -> Op:
+    """
+    marker_op = IN | NOT IN | OP
+    """
+    if tokenizer.check("IN"):
+        tokenizer.read()
+        return Op("in")
+    elif tokenizer.check("NOT"):
+        tokenizer.read()
+        tokenizer.expect("WS", expected="whitespace after 'not'")
+        tokenizer.expect("IN", expected="'in' after 'not'")
+        return Op("not in")
+    elif tokenizer.check("OP"):
+        return Op(tokenizer.read().text)
+    else:
+        return tokenizer.raise_syntax_error(
+            "Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in"
+        )
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_structures.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_structures.py
new file mode 100644
index 0000000..4306784
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_structures.py
@@ -0,0 +1,33 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+"""Backward-compatibility shim for unpickling Version objects serialized before
+packaging 26.1.
+
+Old pickles reference ``packaging._structures.InfinityType`` and
+``packaging._structures.NegativeInfinityType``.  This module provides minimal
+stand-in classes so that ``pickle.loads()`` can resolve those references.
+The deserialized objects are not used for comparisons — ``Version.__setstate__``
+discards the stale ``_key`` cache and recomputes it from the core version fields.
+"""
+
+from __future__ import annotations
+
+
+class InfinityType:
+    """Stand-in for the removed ``InfinityType`` used in old comparison keys."""
+
+    def __repr__(self) -> str:
+        return "Infinity"
+
+
+class NegativeInfinityType:
+    """Stand-in for the removed ``NegativeInfinityType`` used in old comparison keys."""
+
+    def __repr__(self) -> str:
+        return "-Infinity"
+
+
+Infinity = InfinityType()
+NegativeInfinity = NegativeInfinityType()
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_tokenizer.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_tokenizer.py
new file mode 100644
index 0000000..5ab891c
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/_tokenizer.py
@@ -0,0 +1,193 @@
+from __future__ import annotations
+
+import contextlib
+import re
+from dataclasses import dataclass
+from typing import Generator, Mapping, NoReturn
+
+from .specifiers import Specifier
+
+
+@dataclass
+class Token:
+    name: str
+    text: str
+    position: int
+
+
+class ParserSyntaxError(Exception):
+    """The provided source text could not be parsed correctly."""
+
+    def __init__(
+        self,
+        message: str,
+        *,
+        source: str,
+        span: tuple[int, int],
+    ) -> None:
+        self.span = span
+        self.message = message
+        self.source = source
+
+        super().__init__()
+
+    def __str__(self) -> str:
+        marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^"
+        return f"{self.message}\n    {self.source}\n    {marker}"
+
+
+DEFAULT_RULES: dict[str, re.Pattern[str]] = {
+    "LEFT_PARENTHESIS": re.compile(r"\("),
+    "RIGHT_PARENTHESIS": re.compile(r"\)"),
+    "LEFT_BRACKET": re.compile(r"\["),
+    "RIGHT_BRACKET": re.compile(r"\]"),
+    "SEMICOLON": re.compile(r";"),
+    "COMMA": re.compile(r","),
+    "QUOTED_STRING": re.compile(
+        r"""
+            (
+                ('[^']*')
+                |
+                ("[^"]*")
+            )
+        """,
+        re.VERBOSE,
+    ),
+    "OP": re.compile(r"(===|==|~=|!=|<=|>=|<|>)"),
+    "BOOLOP": re.compile(r"\b(or|and)\b"),
+    "IN": re.compile(r"\bin\b"),
+    "NOT": re.compile(r"\bnot\b"),
+    "VARIABLE": re.compile(
+        r"""
+            \b(
+                python_version
+                |python_full_version
+                |os[._]name
+                |sys[._]platform
+                |platform_(release|system)
+                |platform[._](version|machine|python_implementation)
+                |python_implementation
+                |implementation_(name|version)
+                |extras?
+                |dependency_groups
+            )\b
+        """,
+        re.VERBOSE,
+    ),
+    "SPECIFIER": re.compile(
+        Specifier._specifier_regex_str,
+        re.VERBOSE | re.IGNORECASE,
+    ),
+    "AT": re.compile(r"\@"),
+    "URL": re.compile(r"[^ \t]+"),
+    "IDENTIFIER": re.compile(r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b"),
+    "VERSION_PREFIX_TRAIL": re.compile(r"\.\*"),
+    "VERSION_LOCAL_LABEL_TRAIL": re.compile(r"\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*"),
+    "WS": re.compile(r"[ \t]+"),
+    "END": re.compile(r"$"),
+}
+
+
+class Tokenizer:
+    """Context-sensitive token parsing.
+
+    Provides methods to examine the input stream to check whether the next token
+    matches.
+    """
+
+    def __init__(
+        self,
+        source: str,
+        *,
+        rules: Mapping[str, re.Pattern[str]],
+    ) -> None:
+        self.source = source
+        self.rules = rules
+        self.next_token: Token | None = None
+        self.position = 0
+
+    def consume(self, name: str) -> None:
+        """Move beyond provided token name, if at current position."""
+        if self.check(name):
+            self.read()
+
+    def check(self, name: str, *, peek: bool = False) -> bool:
+        """Check whether the next token has the provided name.
+
+        By default, if the check succeeds, the token *must* be read before
+        another check. If `peek` is set to `True`, the token is not loaded and
+        would need to be checked again.
+        """
+        assert self.next_token is None, (
+            f"Cannot check for {name!r}, already have {self.next_token!r}"
+        )
+        assert name in self.rules, f"Unknown token name: {name!r}"
+
+        expression = self.rules[name]
+
+        match = expression.match(self.source, self.position)
+        if match is None:
+            return False
+        if not peek:
+            self.next_token = Token(name, match[0], self.position)
+        return True
+
+    def expect(self, name: str, *, expected: str) -> Token:
+        """Expect a certain token name next, failing with a syntax error otherwise.
+
+        The token is *not* read.
+        """
+        if not self.check(name):
+            raise self.raise_syntax_error(f"Expected {expected}")
+        return self.read()
+
+    def read(self) -> Token:
+        """Consume the next token and return it."""
+        token = self.next_token
+        assert token is not None
+
+        self.position += len(token.text)
+        self.next_token = None
+
+        return token
+
+    def raise_syntax_error(
+        self,
+        message: str,
+        *,
+        span_start: int | None = None,
+        span_end: int | None = None,
+    ) -> NoReturn:
+        """Raise ParserSyntaxError at the given position."""
+        span = (
+            self.position if span_start is None else span_start,
+            self.position if span_end is None else span_end,
+        )
+        raise ParserSyntaxError(
+            message,
+            source=self.source,
+            span=span,
+        )
+
+    @contextlib.contextmanager
+    def enclosing_tokens(
+        self, open_token: str, close_token: str, *, around: str
+    ) -> Generator[None, None, None]:
+        if self.check(open_token):
+            open_position = self.position
+            self.read()
+        else:
+            open_position = None
+
+        yield
+
+        if open_position is None:
+            return
+
+        if not self.check(close_token):
+            self.raise_syntax_error(
+                f"Expected matching {close_token} for {open_token}, after {around}",
+                span_start=open_position,
+            )
+
+        self.read()
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/dependency_groups.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/dependency_groups.py
new file mode 100644
index 0000000..413e5cb
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/dependency_groups.py
@@ -0,0 +1,302 @@
+from __future__ import annotations
+
+import re
+from collections.abc import Mapping, Sequence
+
+from .errors import _ErrorCollector
+from .requirements import Requirement
+
+__all__ = [
+    "CyclicDependencyGroup",
+    "DependencyGroupInclude",
+    "DependencyGroupResolver",
+    "DuplicateGroupNames",
+    "InvalidDependencyGroupObject",
+    "resolve_dependency_groups",
+]
+
+
+def __dir__() -> list[str]:
+    return __all__
+
+
+# -----------
+# Error Types
+# -----------
+
+
+class DuplicateGroupNames(ValueError):
+    """
+    The same dependency groups were defined twice, with different non-normalized names.
+    """
+
+
+class CyclicDependencyGroup(ValueError):
+    """
+    The dependency group includes form a cycle.
+    """
+
+    def __init__(self, requested_group: str, group: str, include_group: str) -> None:
+        self.requested_group = requested_group
+        self.group = group
+        self.include_group = include_group
+
+        if include_group == group:
+            reason = f"{group} includes itself"
+        else:
+            reason = f"{include_group} -> {group}, {group} -> {include_group}"
+        super().__init__(
+            "Cyclic dependency group include while resolving "
+            f"{requested_group}: {reason}"
+        )
+
+
+# in the PEP 735 spec, the tables in dependency group lists were described as
+# "Dependency Object Specifiers", but the only defined type of object was a
+# "Dependency Group Include" -- hence the naming of this error as "Object"
+class InvalidDependencyGroupObject(ValueError):
+    """
+    A member of a dependency group was identified as a dict, but was not in a valid
+    format.
+    """
+
+
+# ------------------------
+# Object Model & Interface
+# ------------------------
+
+
+class DependencyGroupInclude:
+    __slots__ = ("include_group",)
+
+    def __init__(self, include_group: str) -> None:
+        """
+        Initialize a DependencyGroupInclude.
+
+        :param include_group: The name of the group referred to by this include.
+        """
+        self.include_group = include_group
+
+    def __repr__(self) -> str:
+        return f"{self.__class__.__name__}({self.include_group!r})"
+
+
+class DependencyGroupResolver:
+    """
+    A resolver for Dependency Group data.
+
+    This class handles caching, name normalization, cycle detection, and other
+    parsing requirements. There are only two public methods for exploring the data:
+    ``lookup()`` and ``resolve()``.
+
+    :param dependency_groups: A mapping, as provided via pyproject
+        ``[dependency-groups]``.
+    """
+
+    def __init__(
+        self,
+        dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]],
+    ) -> None:
+        errors = _ErrorCollector()
+
+        self.dependency_groups = _normalize_group_names(dependency_groups, errors)
+
+        # a map of group names to parsed data
+        self._parsed_groups: dict[
+            str, tuple[Requirement | DependencyGroupInclude, ...]
+        ] = {}
+        # a map of group names to their ancestors, used for cycle detection
+        self._include_graph_ancestors: dict[str, tuple[str, ...]] = {}
+        # a cache of completed resolutions to Requirement lists
+        self._resolve_cache: dict[str, tuple[Requirement, ...]] = {}
+
+        errors.finalize("[dependency-groups] data was invalid")
+
+    def lookup(self, group: str) -> tuple[Requirement | DependencyGroupInclude, ...]:
+        """
+        Lookup a group name, returning the parsed dependency data for that group.
+        This will not resolve includes.
+
+        :param group: the name of the group to lookup
+        """
+        group = _normalize_name(group)
+
+        with _ErrorCollector().on_exit(
+            f"[dependency-groups] data for {group!r} was malformed"
+        ) as errors:
+            return self._parse_group(group, errors)
+
+    def resolve(self, group: str) -> tuple[Requirement, ...]:
+        """
+        Resolve a dependency group to a list of requirements.
+
+        :param group: the name of the group to resolve
+        """
+        group = _normalize_name(group)
+
+        with _ErrorCollector().on_exit(
+            f"[dependency-groups] data for {group!r} was malformed"
+        ) as errors:
+            return self._resolve(group, group, errors)
+
+    def _resolve(
+        self, group: str, requested_group: str, errors: _ErrorCollector
+    ) -> tuple[Requirement, ...]:
+        """
+        This is a helper for cached resolution to strings. It preserves the name of the
+        group which the user initially requested in order to present a clearer error in
+        the event that a cycle is detected.
+
+        :param group: The normalized name of the group to resolve.
+        :param requested_group: The group which was used in the original, user-facing
+            request.
+        """
+        if group in self._resolve_cache:
+            return self._resolve_cache[group]
+
+        parsed = self._parse_group(group, errors)
+
+        resolved_group = []
+
+        for item in parsed:
+            if isinstance(item, Requirement):
+                resolved_group.append(item)
+            elif isinstance(item, DependencyGroupInclude):
+                include_group = _normalize_name(item.include_group)
+
+                # if a group is cyclic, record the error
+                # otherwise, follow the include_group reference
+                #
+                # this allows us to examine all includes in a group, even in the
+                # presence of errors
+                if include_group in self._include_graph_ancestors.get(group, ()):
+                    errors.error(
+                        CyclicDependencyGroup(
+                            requested_group, group, item.include_group
+                        )
+                    )
+                else:
+                    self._include_graph_ancestors[include_group] = (
+                        *self._include_graph_ancestors.get(group, ()),
+                        group,
+                    )
+                    resolved_group.extend(
+                        self._resolve(include_group, requested_group, errors)
+                    )
+            else:  # pragma: no cover
+                raise NotImplementedError(
+                    f"Invalid dependency group item after parse: {item}"
+                )
+
+        # in the event that errors were detected, present the group as empty and do not
+        # cache the result
+        # this ensures that repeated access to a cyclic group will raise multiple errors
+        if errors.errors:
+            return ()
+
+        self._resolve_cache[group] = tuple(resolved_group)
+        return self._resolve_cache[group]
+
+    def _parse_group(
+        self, group: str, errors: _ErrorCollector
+    ) -> tuple[Requirement | DependencyGroupInclude, ...]:
+        # short circuit -- never do the work twice
+        if group in self._parsed_groups:
+            return self._parsed_groups[group]
+
+        if group not in self.dependency_groups:
+            errors.error(LookupError(f"Dependency group '{group}' not found"))
+            return ()
+
+        raw_group = self.dependency_groups[group]
+        if isinstance(raw_group, str):
+            errors.error(
+                TypeError(
+                    f"Dependency group {group!r} contained a string rather than a list."
+                )
+            )
+            return ()
+
+        if not isinstance(raw_group, Sequence):
+            errors.error(
+                TypeError(f"Dependency group {group!r} is not a sequence type.")
+            )
+            return ()
+
+        elements: list[Requirement | DependencyGroupInclude] = []
+        for item in raw_group:
+            if isinstance(item, str):
+                # packaging.requirements.Requirement parsing ensures that this is a
+                # valid PEP 508 Dependency Specifier
+                # raises InvalidRequirement on failure
+                elements.append(Requirement(item))
+            elif isinstance(item, Mapping):
+                if tuple(item.keys()) != ("include-group",):
+                    errors.error(
+                        InvalidDependencyGroupObject(
+                            f"Invalid dependency group item: {item!r}"
+                        )
+                    )
+                else:
+                    include_group = item["include-group"]
+                    elements.append(DependencyGroupInclude(include_group=include_group))
+            else:
+                errors.error(TypeError(f"Invalid dependency group item: {item!r}"))
+
+        self._parsed_groups[group] = tuple(elements)
+        return self._parsed_groups[group]
+
+
+# --------------------
+# Functional Interface
+# --------------------
+
+
+def resolve_dependency_groups(
+    dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]], /, *groups: str
+) -> tuple[str, ...]:
+    """
+    Resolve a dependency group to a tuple of requirements, as strings.
+
+    :param dependency_groups: the parsed contents of the ``[dependency-groups]`` table
+        from ``pyproject.toml``
+    :param groups: the name of the group(s) to resolve
+    """
+    resolver = DependencyGroupResolver(dependency_groups)
+    return tuple(str(r) for group in groups for r in resolver.resolve(group))
+
+
+# ----------------
+# internal helpers
+# ----------------
+
+
+_NORMALIZE_PATTERN = re.compile(r"[-_.]+")
+
+
+def _normalize_name(name: str) -> str:
+    return _NORMALIZE_PATTERN.sub("-", name).lower()
+
+
+def _normalize_group_names(
+    dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]],
+    errors: _ErrorCollector,
+) -> dict[str, Sequence[str | Mapping[str, str]]]:
+    original_names: dict[str, list[str]] = {}
+    normalized_groups: dict[str, Sequence[str | Mapping[str, str]]] = {}
+
+    for group_name, value in dependency_groups.items():
+        normed_group_name = _normalize_name(group_name)
+        original_names.setdefault(normed_group_name, []).append(group_name)
+        normalized_groups[normed_group_name] = value
+
+    for normed_name, names in original_names.items():
+        if len(names) > 1:
+            errors.error(
+                DuplicateGroupNames(
+                    "Duplicate dependency group names: "
+                    f"{normed_name} ({', '.join(names)})"
+                )
+            )
+
+    return normalized_groups
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/direct_url.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/direct_url.py
new file mode 100644
index 0000000..5d1c56c
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/direct_url.py
@@ -0,0 +1,325 @@
+from __future__ import annotations
+
+import dataclasses
+import re
+import urllib.parse
+from collections.abc import Mapping
+from typing import TYPE_CHECKING, Any, Protocol, TypeVar
+
+if TYPE_CHECKING:  # pragma: no cover
+    import sys
+    from collections.abc import Collection
+
+    if sys.version_info >= (3, 11):
+        from typing import Self
+    else:
+        from typing_extensions import Self
+
+__all__ = [
+    "ArchiveInfo",
+    "DirInfo",
+    "DirectUrl",
+    "DirectUrlValidationError",
+    "VcsInfo",
+]
+
+
+def __dir__() -> list[str]:
+    return __all__
+
+
+_T = TypeVar("_T")
+
+
+class _FromMappingProtocol(Protocol):  # pragma: no cover
+    @classmethod
+    def _from_dict(cls, d: Mapping[str, Any]) -> Self: ...
+
+
+_FromMappingProtocolT = TypeVar("_FromMappingProtocolT", bound=_FromMappingProtocol)
+
+
+def _json_dict_factory(data: list[tuple[str, Any]]) -> dict[str, Any]:
+    return {key: value for key, value in data if value is not None}
+
+
+def _get(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T | None:
+    """Get a value from the dictionary and verify it's the expected type."""
+    if (value := d.get(key)) is None:
+        return None
+    if not isinstance(value, expected_type):
+        raise DirectUrlValidationError(
+            f"Unexpected type {type(value).__name__} "
+            f"(expected {expected_type.__name__})",
+            context=key,
+        )
+    return value
+
+
+def _get_required(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T:
+    """Get a required value from the dictionary and verify it's the expected type."""
+    if (value := _get(d, expected_type, key)) is None:
+        raise _DirectUrlRequiredKeyError(key)
+    return value
+
+
+def _get_object(
+    d: Mapping[str, Any], target_type: type[_FromMappingProtocolT], key: str
+) -> _FromMappingProtocolT | None:
+    """Get a dictionary value from the dictionary and convert it to a dataclass."""
+    if (value := _get(d, Mapping, key)) is None:  # type: ignore[type-abstract]
+        return None
+    try:
+        return target_type._from_dict(value)
+    except Exception as e:
+        raise DirectUrlValidationError(e, context=key) from e
+
+
+_PEP610_USER_PASS_ENV_VARS_REGEX = re.compile(
+    r"^\$\{[A-Za-z0-9-_]+\}(:\$\{[A-Za-z0-9-_]+\})?$"
+)
+
+
+def _strip_auth_from_netloc(netloc: str, safe_user_passwords: Collection[str]) -> str:
+    if "@" not in netloc:
+        return netloc
+    user_pass, netloc_no_user_pass = netloc.split("@", 1)
+    if user_pass in safe_user_passwords:
+        return netloc
+    if _PEP610_USER_PASS_ENV_VARS_REGEX.match(user_pass):
+        return netloc
+    return netloc_no_user_pass
+
+
+def _strip_url(url: str, safe_user_passwords: Collection[str]) -> str:
+    """url with user:password part removed unless it is formed with
+    environment variables as specified in PEP 610, or it is a safe user:password
+    such as `git`.
+    """
+    parsed_url = urllib.parse.urlsplit(url)
+    netloc = _strip_auth_from_netloc(parsed_url.netloc, safe_user_passwords)
+    return urllib.parse.urlunsplit(
+        (
+            parsed_url.scheme,
+            netloc,
+            parsed_url.path,
+            parsed_url.query,
+            parsed_url.fragment,
+        )
+    )
+
+
+class DirectUrlValidationError(Exception):
+    """Raised when when input data is not spec-compliant."""
+
+    context: str | None = None
+    message: str
+
+    def __init__(
+        self,
+        cause: str | Exception,
+        *,
+        context: str | None = None,
+    ) -> None:
+        if isinstance(cause, DirectUrlValidationError):
+            if cause.context:
+                self.context = (
+                    f"{context}.{cause.context}" if context else cause.context
+                )
+            else:
+                self.context = context  # pragma: no cover
+            self.message = cause.message
+        else:
+            self.context = context
+            self.message = str(cause)
+
+    def __str__(self) -> str:
+        if self.context:
+            return f"{self.message} in {self.context!r}"
+        return self.message
+
+
+class _DirectUrlRequiredKeyError(DirectUrlValidationError):
+    def __init__(self, key: str) -> None:
+        super().__init__("Missing required value", context=key)
+
+
+@dataclasses.dataclass(frozen=True, init=False)
+class VcsInfo:
+    vcs: str
+    commit_id: str
+    requested_revision: str | None = None
+
+    def __init__(
+        self,
+        *,
+        vcs: str,
+        commit_id: str,
+        requested_revision: str | None = None,
+    ) -> None:
+        object.__setattr__(self, "vcs", vcs)
+        object.__setattr__(self, "commit_id", commit_id)
+        object.__setattr__(self, "requested_revision", requested_revision)
+
+    @classmethod
+    def _from_dict(cls, d: Mapping[str, Any]) -> Self:
+        # We can't validate vcs value because is not closed.
+        return cls(
+            vcs=_get_required(d, str, "vcs"),
+            requested_revision=_get(d, str, "requested_revision"),
+            commit_id=_get_required(d, str, "commit_id"),
+        )
+
+
+@dataclasses.dataclass(frozen=True, init=False)
+class ArchiveInfo:
+    hashes: Mapping[str, str] | None = None
+
+    def __init__(
+        self,
+        *,
+        hashes: Mapping[str, str] | None = None,
+    ) -> None:
+        object.__setattr__(self, "hashes", hashes)
+
+    @classmethod
+    def _from_dict(cls, d: Mapping[str, Any]) -> Self:
+        hashes = _get(d, Mapping, "hashes")  # type: ignore[type-abstract]
+        if hashes is not None and not all(isinstance(h, str) for h in hashes.values()):
+            raise DirectUrlValidationError(
+                "Hash values must be strings", context="hashes"
+            )
+        legacy_hash = _get(d, str, "hash")
+        if legacy_hash is not None:
+            if "=" not in legacy_hash:
+                raise DirectUrlValidationError(
+                    "Invalid hash format (expected '=')",
+                    context="hash",
+                )
+            hash_algorithm, hash_value = legacy_hash.split("=", 1)
+            if hashes is None:
+                # if `hashes` are not present, we can derive it from the legacy `hash`
+                hashes = {hash_algorithm: hash_value}
+            else:
+                # if `hashes` are present, the legacy `hash` must match one of them
+                if hash_algorithm not in hashes:
+                    raise DirectUrlValidationError(
+                        f"Algorithm {hash_algorithm!r} used in hash field "
+                        f"is not present in hashes field",
+                        context="hashes",
+                    )
+                if hashes[hash_algorithm] != hash_value:
+                    raise DirectUrlValidationError(
+                        f"Algorithm {hash_algorithm!r} used in hash field "
+                        f"has different value in hashes field",
+                        context="hash",
+                    )
+        return cls(hashes=hashes)
+
+
+@dataclasses.dataclass(frozen=True, init=False)
+class DirInfo:
+    editable: bool | None = None
+
+    def __init__(
+        self,
+        *,
+        editable: bool | None = None,
+    ) -> None:
+        object.__setattr__(self, "editable", editable)
+
+    @classmethod
+    def _from_dict(cls, d: Mapping[str, Any]) -> Self:
+        return cls(
+            editable=_get(d, bool, "editable"),
+        )
+
+
+@dataclasses.dataclass(frozen=True, init=False)
+class DirectUrl:
+    """A class representing a direct URL."""
+
+    url: str
+    archive_info: ArchiveInfo | None = None
+    vcs_info: VcsInfo | None = None
+    dir_info: DirInfo | None = None
+    subdirectory: str | None = None  # XXX Path or str?
+
+    def __init__(
+        self,
+        *,
+        url: str,
+        archive_info: ArchiveInfo | None = None,
+        vcs_info: VcsInfo | None = None,
+        dir_info: DirInfo | None = None,
+        subdirectory: str | None = None,
+    ) -> None:
+        object.__setattr__(self, "url", url)
+        object.__setattr__(self, "archive_info", archive_info)
+        object.__setattr__(self, "vcs_info", vcs_info)
+        object.__setattr__(self, "dir_info", dir_info)
+        object.__setattr__(self, "subdirectory", subdirectory)
+
+    @classmethod
+    def _from_dict(cls, d: Mapping[str, Any]) -> Self:
+        direct_url = cls(
+            url=_get_required(d, str, "url"),
+            archive_info=_get_object(d, ArchiveInfo, "archive_info"),
+            vcs_info=_get_object(d, VcsInfo, "vcs_info"),
+            dir_info=_get_object(d, DirInfo, "dir_info"),
+            subdirectory=_get(d, str, "subdirectory"),
+        )
+        if (
+            bool(direct_url.vcs_info)
+            + bool(direct_url.archive_info)
+            + bool(direct_url.dir_info)
+        ) != 1:
+            raise DirectUrlValidationError(
+                "Exactly one of vcs_info, archive_info, dir_info must be present"
+            )
+        if direct_url.dir_info is not None and not direct_url.url.startswith("file://"):
+            raise DirectUrlValidationError(
+                "URL scheme must be file:// when dir_info is present",
+                context="url",
+            )
+        # XXX subdirectory must be relative, can we, should we validate that here?
+        return direct_url
+
+    @classmethod
+    def from_dict(cls, d: Mapping[str, Any], /) -> Self:
+        """Create and validate a DirectUrl instance from a JSON dictionary."""
+        return cls._from_dict(d)
+
+    def to_dict(
+        self,
+        *,
+        generate_legacy_hash: bool = False,
+        strip_user_password: bool = True,
+        safe_user_passwords: Collection[str] = ("git",),
+    ) -> Mapping[str, Any]:
+        """Convert the DirectUrl instance to a JSON dictionary.
+
+        :param generate_legacy_hash: If True, include a legacy `hash` field in
+            `archive_info` for backward compatibility with tools that don't
+            support the `hashes` field.
+        :param strip_user_password: If True, strip user:password from the URL
+            unless it is formed with environment variables as specified in PEP
+            610, or it is a safe user:password such as `git`.
+        :param safe_user_passwords: A collection of user:password strings that
+            should not be stripped from the URL even if `strip_user_password` is
+            True.
+        """
+        res = dataclasses.asdict(self, dict_factory=_json_dict_factory)
+        if generate_legacy_hash and self.archive_info and self.archive_info.hashes:
+            hash_algorithm, hash_value = next(iter(self.archive_info.hashes.items()))
+            res["archive_info"]["hash"] = f"{hash_algorithm}={hash_value}"
+        if strip_user_password:
+            res["url"] = _strip_url(self.url, safe_user_passwords)
+        return res
+
+    def validate(self) -> None:
+        """Validate the DirectUrl instance against the specification.
+
+        Raises :class:`DirectUrlValidationError` if invalid.
+        """
+        self.from_dict(self.to_dict())
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/errors.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/errors.py
new file mode 100644
index 0000000..d1d47cf
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/errors.py
@@ -0,0 +1,94 @@
+from __future__ import annotations
+
+import contextlib
+import dataclasses
+import sys
+import typing
+
+__all__ = ["ExceptionGroup"]
+
+
+def __dir__() -> list[str]:
+    return __all__
+
+
+if sys.version_info >= (3, 11):  # pragma: no cover
+    from builtins import ExceptionGroup
+else:  # pragma: no cover
+
+    class ExceptionGroup(Exception):
+        """A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11.
+
+        If :external:exc:`ExceptionGroup` is already defined by Python itself,
+        that version is used instead.
+        """
+
+        message: str
+        exceptions: list[Exception]
+
+        def __init__(self, message: str, exceptions: list[Exception]) -> None:
+            self.message = message
+            self.exceptions = exceptions
+
+        def __repr__(self) -> str:
+            return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})"
+
+
+@dataclasses.dataclass
+class _ErrorCollector:
+    """
+    Collect errors into ExceptionGroups.
+
+    Used like this:
+
+        collector = _ErrorCollector()
+        # Add a single exception
+        collector.error(ValueError("one"))
+
+        # Supports nesting, including combining ExceptionGroups
+        with collector.collect():
+            raise ValueError("two")
+        collector.finalize("Found some errors")
+
+    Since making a collector and then calling finalize later is a common pattern,
+    a convenience method ``on_exit`` is provided.
+    """
+
+    errors: list[Exception] = dataclasses.field(default_factory=list, init=False)
+
+    def finalize(self, msg: str) -> None:
+        """Raise a group exception if there are any errors."""
+        if self.errors:
+            raise ExceptionGroup(msg, self.errors)
+
+    @contextlib.contextmanager
+    def on_exit(self, msg: str) -> typing.Generator[_ErrorCollector, None, None]:
+        """
+        Calls finalize if no uncollected errors were present.
+
+        Uncollected errors are raised normally.
+        """
+        yield self
+        self.finalize(msg)
+
+    @contextlib.contextmanager
+    def collect(self, *err_cls: type[Exception]) -> typing.Generator[None, None, None]:
+        """
+        Context manager to collect errors into the error list.
+
+        Must be inside loops, as only one error can be collected at a time.
+        """
+        error_classes = err_cls or (Exception,)
+        try:
+            yield
+        except ExceptionGroup as error:
+            self.errors.extend(error.exceptions)
+        except error_classes as error:
+            self.errors.append(error)
+
+    def error(
+        self,
+        error: Exception,
+    ) -> None:
+        """Add an error to the list."""
+        self.errors.append(error)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__init__.py
new file mode 100644
index 0000000..36e46ed
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__init__.py
@@ -0,0 +1,186 @@
+#######################################################################################
+#
+# Adapted from:
+#  https://github.com/pypa/hatch/blob/5352e44/backend/src/hatchling/licenses/parse.py
+#
+# MIT License
+#
+# Copyright (c) 2017-present Ofek Lev 
+#
+# 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.
+#
+#
+# With additional allowance of arbitrary `LicenseRef-` identifiers, not just
+# `LicenseRef-Public-Domain` and `LicenseRef-Proprietary`.
+#
+#######################################################################################
+from __future__ import annotations
+
+import re
+from typing import NewType, cast
+
+from ._spdx import EXCEPTIONS, LICENSES
+
+__all__ = [
+    "InvalidLicenseExpression",
+    "NormalizedLicenseExpression",
+    "canonicalize_license_expression",
+]
+
+
+# Simple __dir__ implementation since there are no public submodules
+def __dir__() -> list[str]:
+    return __all__
+
+
+license_ref_allowed = re.compile("^[A-Za-z0-9.-]*$")
+
+NormalizedLicenseExpression = NewType("NormalizedLicenseExpression", str)
+"""
+A :class:`typing.NewType` of :class:`str`, representing a normalized
+License-Expression.
+"""
+
+
+class InvalidLicenseExpression(ValueError):
+    """Raised when a license-expression string is invalid
+
+    >>> from packaging.licenses import canonicalize_license_expression
+    >>> canonicalize_license_expression("invalid")
+    Traceback (most recent call last):
+        ...
+    packaging.licenses.InvalidLicenseExpression: Invalid license expression: 'invalid'
+    """
+
+
+def canonicalize_license_expression(
+    raw_license_expression: str,
+) -> NormalizedLicenseExpression:
+    """
+    This function takes a valid License-Expression, and returns the normalized
+    form of it.
+
+    The return type is typed as :class:`NormalizedLicenseExpression`. This
+    allows type checkers to help require that a string has passed through this
+    function before use.
+
+    :param str raw_license_expression: The License-Expression to canonicalize.
+    :raises InvalidLicenseExpression: If the License-Expression is invalid due to an
+        invalid/unknown license identifier or invalid syntax.
+
+    .. doctest::
+
+        >>> from packaging.licenses import canonicalize_license_expression
+        >>> canonicalize_license_expression("mit")
+        'MIT'
+        >>> canonicalize_license_expression("mit and (apache-2.0 or bsd-2-clause)")
+        'MIT AND (Apache-2.0 OR BSD-2-Clause)'
+        >>> canonicalize_license_expression("(mit")
+        Traceback (most recent call last):
+          ...
+        InvalidLicenseExpression: Invalid license expression: '(mit'
+        >>> canonicalize_license_expression("Use-it-after-midnight")
+        Traceback (most recent call last):
+          ...
+        InvalidLicenseExpression: Unknown license: 'Use-it-after-midnight'
+    """
+    if not raw_license_expression:
+        message = f"Invalid license expression: {raw_license_expression!r}"
+        raise InvalidLicenseExpression(message)
+
+    # Pad any parentheses so tokenization can be achieved by merely splitting on
+    # whitespace.
+    license_expression = raw_license_expression.replace("(", " ( ").replace(")", " ) ")
+    licenseref_prefix = "LicenseRef-"
+    license_refs = {
+        ref.lower(): "LicenseRef-" + ref[len(licenseref_prefix) :]
+        for ref in license_expression.split()
+        if ref.lower().startswith(licenseref_prefix.lower())
+    }
+
+    # Normalize to lower case so we can look up licenses/exceptions
+    # and so boolean operators are Python-compatible.
+    license_expression = license_expression.lower()
+
+    tokens = license_expression.split()
+
+    # Rather than implementing a parenthesis/boolean logic parser, create an
+    # expression that Python can parse. Everything that is not involved with the
+    # grammar itself is replaced with the placeholder `False` and the resultant
+    # expression should become a valid Python expression.
+    python_tokens = []
+    for token in tokens:
+        if token not in {"or", "and", "with", "(", ")"}:
+            python_tokens.append("False")
+        elif token == "with":
+            python_tokens.append("or")
+        elif (
+            token == "("
+            and python_tokens
+            and python_tokens[-1] not in {"or", "and", "("}
+        ) or (token == ")" and python_tokens and python_tokens[-1] == "("):
+            message = f"Invalid license expression: {raw_license_expression!r}"
+            raise InvalidLicenseExpression(message)
+        else:
+            python_tokens.append(token)
+
+    python_expression = " ".join(python_tokens)
+    try:
+        compile(python_expression, "", "eval")
+    except SyntaxError:
+        message = f"Invalid license expression: {raw_license_expression!r}"
+        raise InvalidLicenseExpression(message) from None
+
+    # Take a final pass to check for unknown licenses/exceptions.
+    normalized_tokens = []
+    for token in tokens:
+        if token in {"or", "and", "with", "(", ")"}:
+            normalized_tokens.append(token.upper())
+            continue
+
+        if normalized_tokens and normalized_tokens[-1] == "WITH":
+            if token not in EXCEPTIONS:
+                message = f"Unknown license exception: {token!r}"
+                raise InvalidLicenseExpression(message)
+
+            normalized_tokens.append(EXCEPTIONS[token]["id"])
+        else:
+            if token.endswith("+"):
+                final_token = token[:-1]
+                suffix = "+"
+            else:
+                final_token = token
+                suffix = ""
+
+            if final_token.startswith("licenseref-"):
+                if not license_ref_allowed.match(final_token):
+                    message = f"Invalid licenseref: {final_token!r}"
+                    raise InvalidLicenseExpression(message)
+                normalized_tokens.append(license_refs[final_token] + suffix)
+            else:
+                if final_token not in LICENSES:
+                    message = f"Unknown license: {final_token!r}"
+                    raise InvalidLicenseExpression(message)
+                normalized_tokens.append(LICENSES[final_token]["id"] + suffix)
+
+    normalized_expression = " ".join(normalized_tokens)
+
+    return cast(
+        "NormalizedLicenseExpression",
+        normalized_expression.replace("( ", "(").replace(" )", ")"),
+    )
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..1b9e8a1
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__pycache__/_spdx.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__pycache__/_spdx.cpython-312.pyc
new file mode 100644
index 0000000..ad9366e
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__pycache__/_spdx.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/_spdx.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/_spdx.py
new file mode 100644
index 0000000..a277af2
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/_spdx.py
@@ -0,0 +1,799 @@
+
+from __future__ import annotations
+
+from typing import TypedDict
+
+class SPDXLicense(TypedDict):
+    id: str
+    deprecated: bool
+
+class SPDXException(TypedDict):
+    id: str
+    deprecated: bool
+
+
+VERSION = '3.27.0'
+
+LICENSES: dict[str, SPDXLicense] = {
+    '0bsd': {'id': '0BSD', 'deprecated': False},
+    '3d-slicer-1.0': {'id': '3D-Slicer-1.0', 'deprecated': False},
+    'aal': {'id': 'AAL', 'deprecated': False},
+    'abstyles': {'id': 'Abstyles', 'deprecated': False},
+    'adacore-doc': {'id': 'AdaCore-doc', 'deprecated': False},
+    'adobe-2006': {'id': 'Adobe-2006', 'deprecated': False},
+    'adobe-display-postscript': {'id': 'Adobe-Display-PostScript', 'deprecated': False},
+    'adobe-glyph': {'id': 'Adobe-Glyph', 'deprecated': False},
+    'adobe-utopia': {'id': 'Adobe-Utopia', 'deprecated': False},
+    'adsl': {'id': 'ADSL', 'deprecated': False},
+    'afl-1.1': {'id': 'AFL-1.1', 'deprecated': False},
+    'afl-1.2': {'id': 'AFL-1.2', 'deprecated': False},
+    'afl-2.0': {'id': 'AFL-2.0', 'deprecated': False},
+    'afl-2.1': {'id': 'AFL-2.1', 'deprecated': False},
+    'afl-3.0': {'id': 'AFL-3.0', 'deprecated': False},
+    'afmparse': {'id': 'Afmparse', 'deprecated': False},
+    'agpl-1.0': {'id': 'AGPL-1.0', 'deprecated': True},
+    'agpl-1.0-only': {'id': 'AGPL-1.0-only', 'deprecated': False},
+    'agpl-1.0-or-later': {'id': 'AGPL-1.0-or-later', 'deprecated': False},
+    'agpl-3.0': {'id': 'AGPL-3.0', 'deprecated': True},
+    'agpl-3.0-only': {'id': 'AGPL-3.0-only', 'deprecated': False},
+    'agpl-3.0-or-later': {'id': 'AGPL-3.0-or-later', 'deprecated': False},
+    'aladdin': {'id': 'Aladdin', 'deprecated': False},
+    'amd-newlib': {'id': 'AMD-newlib', 'deprecated': False},
+    'amdplpa': {'id': 'AMDPLPA', 'deprecated': False},
+    'aml': {'id': 'AML', 'deprecated': False},
+    'aml-glslang': {'id': 'AML-glslang', 'deprecated': False},
+    'ampas': {'id': 'AMPAS', 'deprecated': False},
+    'antlr-pd': {'id': 'ANTLR-PD', 'deprecated': False},
+    'antlr-pd-fallback': {'id': 'ANTLR-PD-fallback', 'deprecated': False},
+    'any-osi': {'id': 'any-OSI', 'deprecated': False},
+    'any-osi-perl-modules': {'id': 'any-OSI-perl-modules', 'deprecated': False},
+    'apache-1.0': {'id': 'Apache-1.0', 'deprecated': False},
+    'apache-1.1': {'id': 'Apache-1.1', 'deprecated': False},
+    'apache-2.0': {'id': 'Apache-2.0', 'deprecated': False},
+    'apafml': {'id': 'APAFML', 'deprecated': False},
+    'apl-1.0': {'id': 'APL-1.0', 'deprecated': False},
+    'app-s2p': {'id': 'App-s2p', 'deprecated': False},
+    'apsl-1.0': {'id': 'APSL-1.0', 'deprecated': False},
+    'apsl-1.1': {'id': 'APSL-1.1', 'deprecated': False},
+    'apsl-1.2': {'id': 'APSL-1.2', 'deprecated': False},
+    'apsl-2.0': {'id': 'APSL-2.0', 'deprecated': False},
+    'arphic-1999': {'id': 'Arphic-1999', 'deprecated': False},
+    'artistic-1.0': {'id': 'Artistic-1.0', 'deprecated': False},
+    'artistic-1.0-cl8': {'id': 'Artistic-1.0-cl8', 'deprecated': False},
+    'artistic-1.0-perl': {'id': 'Artistic-1.0-Perl', 'deprecated': False},
+    'artistic-2.0': {'id': 'Artistic-2.0', 'deprecated': False},
+    'artistic-dist': {'id': 'Artistic-dist', 'deprecated': False},
+    'aspell-ru': {'id': 'Aspell-RU', 'deprecated': False},
+    'aswf-digital-assets-1.0': {'id': 'ASWF-Digital-Assets-1.0', 'deprecated': False},
+    'aswf-digital-assets-1.1': {'id': 'ASWF-Digital-Assets-1.1', 'deprecated': False},
+    'baekmuk': {'id': 'Baekmuk', 'deprecated': False},
+    'bahyph': {'id': 'Bahyph', 'deprecated': False},
+    'barr': {'id': 'Barr', 'deprecated': False},
+    'bcrypt-solar-designer': {'id': 'bcrypt-Solar-Designer', 'deprecated': False},
+    'beerware': {'id': 'Beerware', 'deprecated': False},
+    'bitstream-charter': {'id': 'Bitstream-Charter', 'deprecated': False},
+    'bitstream-vera': {'id': 'Bitstream-Vera', 'deprecated': False},
+    'bittorrent-1.0': {'id': 'BitTorrent-1.0', 'deprecated': False},
+    'bittorrent-1.1': {'id': 'BitTorrent-1.1', 'deprecated': False},
+    'blessing': {'id': 'blessing', 'deprecated': False},
+    'blueoak-1.0.0': {'id': 'BlueOak-1.0.0', 'deprecated': False},
+    'boehm-gc': {'id': 'Boehm-GC', 'deprecated': False},
+    'boehm-gc-without-fee': {'id': 'Boehm-GC-without-fee', 'deprecated': False},
+    'borceux': {'id': 'Borceux', 'deprecated': False},
+    'brian-gladman-2-clause': {'id': 'Brian-Gladman-2-Clause', 'deprecated': False},
+    'brian-gladman-3-clause': {'id': 'Brian-Gladman-3-Clause', 'deprecated': False},
+    'bsd-1-clause': {'id': 'BSD-1-Clause', 'deprecated': False},
+    'bsd-2-clause': {'id': 'BSD-2-Clause', 'deprecated': False},
+    'bsd-2-clause-darwin': {'id': 'BSD-2-Clause-Darwin', 'deprecated': False},
+    'bsd-2-clause-first-lines': {'id': 'BSD-2-Clause-first-lines', 'deprecated': False},
+    'bsd-2-clause-freebsd': {'id': 'BSD-2-Clause-FreeBSD', 'deprecated': True},
+    'bsd-2-clause-netbsd': {'id': 'BSD-2-Clause-NetBSD', 'deprecated': True},
+    'bsd-2-clause-patent': {'id': 'BSD-2-Clause-Patent', 'deprecated': False},
+    'bsd-2-clause-pkgconf-disclaimer': {'id': 'BSD-2-Clause-pkgconf-disclaimer', 'deprecated': False},
+    'bsd-2-clause-views': {'id': 'BSD-2-Clause-Views', 'deprecated': False},
+    'bsd-3-clause': {'id': 'BSD-3-Clause', 'deprecated': False},
+    'bsd-3-clause-acpica': {'id': 'BSD-3-Clause-acpica', 'deprecated': False},
+    'bsd-3-clause-attribution': {'id': 'BSD-3-Clause-Attribution', 'deprecated': False},
+    'bsd-3-clause-clear': {'id': 'BSD-3-Clause-Clear', 'deprecated': False},
+    'bsd-3-clause-flex': {'id': 'BSD-3-Clause-flex', 'deprecated': False},
+    'bsd-3-clause-hp': {'id': 'BSD-3-Clause-HP', 'deprecated': False},
+    'bsd-3-clause-lbnl': {'id': 'BSD-3-Clause-LBNL', 'deprecated': False},
+    'bsd-3-clause-modification': {'id': 'BSD-3-Clause-Modification', 'deprecated': False},
+    'bsd-3-clause-no-military-license': {'id': 'BSD-3-Clause-No-Military-License', 'deprecated': False},
+    'bsd-3-clause-no-nuclear-license': {'id': 'BSD-3-Clause-No-Nuclear-License', 'deprecated': False},
+    'bsd-3-clause-no-nuclear-license-2014': {'id': 'BSD-3-Clause-No-Nuclear-License-2014', 'deprecated': False},
+    'bsd-3-clause-no-nuclear-warranty': {'id': 'BSD-3-Clause-No-Nuclear-Warranty', 'deprecated': False},
+    'bsd-3-clause-open-mpi': {'id': 'BSD-3-Clause-Open-MPI', 'deprecated': False},
+    'bsd-3-clause-sun': {'id': 'BSD-3-Clause-Sun', 'deprecated': False},
+    'bsd-4-clause': {'id': 'BSD-4-Clause', 'deprecated': False},
+    'bsd-4-clause-shortened': {'id': 'BSD-4-Clause-Shortened', 'deprecated': False},
+    'bsd-4-clause-uc': {'id': 'BSD-4-Clause-UC', 'deprecated': False},
+    'bsd-4.3reno': {'id': 'BSD-4.3RENO', 'deprecated': False},
+    'bsd-4.3tahoe': {'id': 'BSD-4.3TAHOE', 'deprecated': False},
+    'bsd-advertising-acknowledgement': {'id': 'BSD-Advertising-Acknowledgement', 'deprecated': False},
+    'bsd-attribution-hpnd-disclaimer': {'id': 'BSD-Attribution-HPND-disclaimer', 'deprecated': False},
+    'bsd-inferno-nettverk': {'id': 'BSD-Inferno-Nettverk', 'deprecated': False},
+    'bsd-protection': {'id': 'BSD-Protection', 'deprecated': False},
+    'bsd-source-beginning-file': {'id': 'BSD-Source-beginning-file', 'deprecated': False},
+    'bsd-source-code': {'id': 'BSD-Source-Code', 'deprecated': False},
+    'bsd-systemics': {'id': 'BSD-Systemics', 'deprecated': False},
+    'bsd-systemics-w3works': {'id': 'BSD-Systemics-W3Works', 'deprecated': False},
+    'bsl-1.0': {'id': 'BSL-1.0', 'deprecated': False},
+    'busl-1.1': {'id': 'BUSL-1.1', 'deprecated': False},
+    'bzip2-1.0.5': {'id': 'bzip2-1.0.5', 'deprecated': True},
+    'bzip2-1.0.6': {'id': 'bzip2-1.0.6', 'deprecated': False},
+    'c-uda-1.0': {'id': 'C-UDA-1.0', 'deprecated': False},
+    'cal-1.0': {'id': 'CAL-1.0', 'deprecated': False},
+    'cal-1.0-combined-work-exception': {'id': 'CAL-1.0-Combined-Work-Exception', 'deprecated': False},
+    'caldera': {'id': 'Caldera', 'deprecated': False},
+    'caldera-no-preamble': {'id': 'Caldera-no-preamble', 'deprecated': False},
+    'catharon': {'id': 'Catharon', 'deprecated': False},
+    'catosl-1.1': {'id': 'CATOSL-1.1', 'deprecated': False},
+    'cc-by-1.0': {'id': 'CC-BY-1.0', 'deprecated': False},
+    'cc-by-2.0': {'id': 'CC-BY-2.0', 'deprecated': False},
+    'cc-by-2.5': {'id': 'CC-BY-2.5', 'deprecated': False},
+    'cc-by-2.5-au': {'id': 'CC-BY-2.5-AU', 'deprecated': False},
+    'cc-by-3.0': {'id': 'CC-BY-3.0', 'deprecated': False},
+    'cc-by-3.0-at': {'id': 'CC-BY-3.0-AT', 'deprecated': False},
+    'cc-by-3.0-au': {'id': 'CC-BY-3.0-AU', 'deprecated': False},
+    'cc-by-3.0-de': {'id': 'CC-BY-3.0-DE', 'deprecated': False},
+    'cc-by-3.0-igo': {'id': 'CC-BY-3.0-IGO', 'deprecated': False},
+    'cc-by-3.0-nl': {'id': 'CC-BY-3.0-NL', 'deprecated': False},
+    'cc-by-3.0-us': {'id': 'CC-BY-3.0-US', 'deprecated': False},
+    'cc-by-4.0': {'id': 'CC-BY-4.0', 'deprecated': False},
+    'cc-by-nc-1.0': {'id': 'CC-BY-NC-1.0', 'deprecated': False},
+    'cc-by-nc-2.0': {'id': 'CC-BY-NC-2.0', 'deprecated': False},
+    'cc-by-nc-2.5': {'id': 'CC-BY-NC-2.5', 'deprecated': False},
+    'cc-by-nc-3.0': {'id': 'CC-BY-NC-3.0', 'deprecated': False},
+    'cc-by-nc-3.0-de': {'id': 'CC-BY-NC-3.0-DE', 'deprecated': False},
+    'cc-by-nc-4.0': {'id': 'CC-BY-NC-4.0', 'deprecated': False},
+    'cc-by-nc-nd-1.0': {'id': 'CC-BY-NC-ND-1.0', 'deprecated': False},
+    'cc-by-nc-nd-2.0': {'id': 'CC-BY-NC-ND-2.0', 'deprecated': False},
+    'cc-by-nc-nd-2.5': {'id': 'CC-BY-NC-ND-2.5', 'deprecated': False},
+    'cc-by-nc-nd-3.0': {'id': 'CC-BY-NC-ND-3.0', 'deprecated': False},
+    'cc-by-nc-nd-3.0-de': {'id': 'CC-BY-NC-ND-3.0-DE', 'deprecated': False},
+    'cc-by-nc-nd-3.0-igo': {'id': 'CC-BY-NC-ND-3.0-IGO', 'deprecated': False},
+    'cc-by-nc-nd-4.0': {'id': 'CC-BY-NC-ND-4.0', 'deprecated': False},
+    'cc-by-nc-sa-1.0': {'id': 'CC-BY-NC-SA-1.0', 'deprecated': False},
+    'cc-by-nc-sa-2.0': {'id': 'CC-BY-NC-SA-2.0', 'deprecated': False},
+    'cc-by-nc-sa-2.0-de': {'id': 'CC-BY-NC-SA-2.0-DE', 'deprecated': False},
+    'cc-by-nc-sa-2.0-fr': {'id': 'CC-BY-NC-SA-2.0-FR', 'deprecated': False},
+    'cc-by-nc-sa-2.0-uk': {'id': 'CC-BY-NC-SA-2.0-UK', 'deprecated': False},
+    'cc-by-nc-sa-2.5': {'id': 'CC-BY-NC-SA-2.5', 'deprecated': False},
+    'cc-by-nc-sa-3.0': {'id': 'CC-BY-NC-SA-3.0', 'deprecated': False},
+    'cc-by-nc-sa-3.0-de': {'id': 'CC-BY-NC-SA-3.0-DE', 'deprecated': False},
+    'cc-by-nc-sa-3.0-igo': {'id': 'CC-BY-NC-SA-3.0-IGO', 'deprecated': False},
+    'cc-by-nc-sa-4.0': {'id': 'CC-BY-NC-SA-4.0', 'deprecated': False},
+    'cc-by-nd-1.0': {'id': 'CC-BY-ND-1.0', 'deprecated': False},
+    'cc-by-nd-2.0': {'id': 'CC-BY-ND-2.0', 'deprecated': False},
+    'cc-by-nd-2.5': {'id': 'CC-BY-ND-2.5', 'deprecated': False},
+    'cc-by-nd-3.0': {'id': 'CC-BY-ND-3.0', 'deprecated': False},
+    'cc-by-nd-3.0-de': {'id': 'CC-BY-ND-3.0-DE', 'deprecated': False},
+    'cc-by-nd-4.0': {'id': 'CC-BY-ND-4.0', 'deprecated': False},
+    'cc-by-sa-1.0': {'id': 'CC-BY-SA-1.0', 'deprecated': False},
+    'cc-by-sa-2.0': {'id': 'CC-BY-SA-2.0', 'deprecated': False},
+    'cc-by-sa-2.0-uk': {'id': 'CC-BY-SA-2.0-UK', 'deprecated': False},
+    'cc-by-sa-2.1-jp': {'id': 'CC-BY-SA-2.1-JP', 'deprecated': False},
+    'cc-by-sa-2.5': {'id': 'CC-BY-SA-2.5', 'deprecated': False},
+    'cc-by-sa-3.0': {'id': 'CC-BY-SA-3.0', 'deprecated': False},
+    'cc-by-sa-3.0-at': {'id': 'CC-BY-SA-3.0-AT', 'deprecated': False},
+    'cc-by-sa-3.0-de': {'id': 'CC-BY-SA-3.0-DE', 'deprecated': False},
+    'cc-by-sa-3.0-igo': {'id': 'CC-BY-SA-3.0-IGO', 'deprecated': False},
+    'cc-by-sa-4.0': {'id': 'CC-BY-SA-4.0', 'deprecated': False},
+    'cc-pddc': {'id': 'CC-PDDC', 'deprecated': False},
+    'cc-pdm-1.0': {'id': 'CC-PDM-1.0', 'deprecated': False},
+    'cc-sa-1.0': {'id': 'CC-SA-1.0', 'deprecated': False},
+    'cc0-1.0': {'id': 'CC0-1.0', 'deprecated': False},
+    'cddl-1.0': {'id': 'CDDL-1.0', 'deprecated': False},
+    'cddl-1.1': {'id': 'CDDL-1.1', 'deprecated': False},
+    'cdl-1.0': {'id': 'CDL-1.0', 'deprecated': False},
+    'cdla-permissive-1.0': {'id': 'CDLA-Permissive-1.0', 'deprecated': False},
+    'cdla-permissive-2.0': {'id': 'CDLA-Permissive-2.0', 'deprecated': False},
+    'cdla-sharing-1.0': {'id': 'CDLA-Sharing-1.0', 'deprecated': False},
+    'cecill-1.0': {'id': 'CECILL-1.0', 'deprecated': False},
+    'cecill-1.1': {'id': 'CECILL-1.1', 'deprecated': False},
+    'cecill-2.0': {'id': 'CECILL-2.0', 'deprecated': False},
+    'cecill-2.1': {'id': 'CECILL-2.1', 'deprecated': False},
+    'cecill-b': {'id': 'CECILL-B', 'deprecated': False},
+    'cecill-c': {'id': 'CECILL-C', 'deprecated': False},
+    'cern-ohl-1.1': {'id': 'CERN-OHL-1.1', 'deprecated': False},
+    'cern-ohl-1.2': {'id': 'CERN-OHL-1.2', 'deprecated': False},
+    'cern-ohl-p-2.0': {'id': 'CERN-OHL-P-2.0', 'deprecated': False},
+    'cern-ohl-s-2.0': {'id': 'CERN-OHL-S-2.0', 'deprecated': False},
+    'cern-ohl-w-2.0': {'id': 'CERN-OHL-W-2.0', 'deprecated': False},
+    'cfitsio': {'id': 'CFITSIO', 'deprecated': False},
+    'check-cvs': {'id': 'check-cvs', 'deprecated': False},
+    'checkmk': {'id': 'checkmk', 'deprecated': False},
+    'clartistic': {'id': 'ClArtistic', 'deprecated': False},
+    'clips': {'id': 'Clips', 'deprecated': False},
+    'cmu-mach': {'id': 'CMU-Mach', 'deprecated': False},
+    'cmu-mach-nodoc': {'id': 'CMU-Mach-nodoc', 'deprecated': False},
+    'cnri-jython': {'id': 'CNRI-Jython', 'deprecated': False},
+    'cnri-python': {'id': 'CNRI-Python', 'deprecated': False},
+    'cnri-python-gpl-compatible': {'id': 'CNRI-Python-GPL-Compatible', 'deprecated': False},
+    'coil-1.0': {'id': 'COIL-1.0', 'deprecated': False},
+    'community-spec-1.0': {'id': 'Community-Spec-1.0', 'deprecated': False},
+    'condor-1.1': {'id': 'Condor-1.1', 'deprecated': False},
+    'copyleft-next-0.3.0': {'id': 'copyleft-next-0.3.0', 'deprecated': False},
+    'copyleft-next-0.3.1': {'id': 'copyleft-next-0.3.1', 'deprecated': False},
+    'cornell-lossless-jpeg': {'id': 'Cornell-Lossless-JPEG', 'deprecated': False},
+    'cpal-1.0': {'id': 'CPAL-1.0', 'deprecated': False},
+    'cpl-1.0': {'id': 'CPL-1.0', 'deprecated': False},
+    'cpol-1.02': {'id': 'CPOL-1.02', 'deprecated': False},
+    'cronyx': {'id': 'Cronyx', 'deprecated': False},
+    'crossword': {'id': 'Crossword', 'deprecated': False},
+    'cryptoswift': {'id': 'CryptoSwift', 'deprecated': False},
+    'crystalstacker': {'id': 'CrystalStacker', 'deprecated': False},
+    'cua-opl-1.0': {'id': 'CUA-OPL-1.0', 'deprecated': False},
+    'cube': {'id': 'Cube', 'deprecated': False},
+    'curl': {'id': 'curl', 'deprecated': False},
+    'cve-tou': {'id': 'cve-tou', 'deprecated': False},
+    'd-fsl-1.0': {'id': 'D-FSL-1.0', 'deprecated': False},
+    'dec-3-clause': {'id': 'DEC-3-Clause', 'deprecated': False},
+    'diffmark': {'id': 'diffmark', 'deprecated': False},
+    'dl-de-by-2.0': {'id': 'DL-DE-BY-2.0', 'deprecated': False},
+    'dl-de-zero-2.0': {'id': 'DL-DE-ZERO-2.0', 'deprecated': False},
+    'doc': {'id': 'DOC', 'deprecated': False},
+    'docbook-dtd': {'id': 'DocBook-DTD', 'deprecated': False},
+    'docbook-schema': {'id': 'DocBook-Schema', 'deprecated': False},
+    'docbook-stylesheet': {'id': 'DocBook-Stylesheet', 'deprecated': False},
+    'docbook-xml': {'id': 'DocBook-XML', 'deprecated': False},
+    'dotseqn': {'id': 'Dotseqn', 'deprecated': False},
+    'drl-1.0': {'id': 'DRL-1.0', 'deprecated': False},
+    'drl-1.1': {'id': 'DRL-1.1', 'deprecated': False},
+    'dsdp': {'id': 'DSDP', 'deprecated': False},
+    'dtoa': {'id': 'dtoa', 'deprecated': False},
+    'dvipdfm': {'id': 'dvipdfm', 'deprecated': False},
+    'ecl-1.0': {'id': 'ECL-1.0', 'deprecated': False},
+    'ecl-2.0': {'id': 'ECL-2.0', 'deprecated': False},
+    'ecos-2.0': {'id': 'eCos-2.0', 'deprecated': True},
+    'efl-1.0': {'id': 'EFL-1.0', 'deprecated': False},
+    'efl-2.0': {'id': 'EFL-2.0', 'deprecated': False},
+    'egenix': {'id': 'eGenix', 'deprecated': False},
+    'elastic-2.0': {'id': 'Elastic-2.0', 'deprecated': False},
+    'entessa': {'id': 'Entessa', 'deprecated': False},
+    'epics': {'id': 'EPICS', 'deprecated': False},
+    'epl-1.0': {'id': 'EPL-1.0', 'deprecated': False},
+    'epl-2.0': {'id': 'EPL-2.0', 'deprecated': False},
+    'erlpl-1.1': {'id': 'ErlPL-1.1', 'deprecated': False},
+    'etalab-2.0': {'id': 'etalab-2.0', 'deprecated': False},
+    'eudatagrid': {'id': 'EUDatagrid', 'deprecated': False},
+    'eupl-1.0': {'id': 'EUPL-1.0', 'deprecated': False},
+    'eupl-1.1': {'id': 'EUPL-1.1', 'deprecated': False},
+    'eupl-1.2': {'id': 'EUPL-1.2', 'deprecated': False},
+    'eurosym': {'id': 'Eurosym', 'deprecated': False},
+    'fair': {'id': 'Fair', 'deprecated': False},
+    'fbm': {'id': 'FBM', 'deprecated': False},
+    'fdk-aac': {'id': 'FDK-AAC', 'deprecated': False},
+    'ferguson-twofish': {'id': 'Ferguson-Twofish', 'deprecated': False},
+    'frameworx-1.0': {'id': 'Frameworx-1.0', 'deprecated': False},
+    'freebsd-doc': {'id': 'FreeBSD-DOC', 'deprecated': False},
+    'freeimage': {'id': 'FreeImage', 'deprecated': False},
+    'fsfap': {'id': 'FSFAP', 'deprecated': False},
+    'fsfap-no-warranty-disclaimer': {'id': 'FSFAP-no-warranty-disclaimer', 'deprecated': False},
+    'fsful': {'id': 'FSFUL', 'deprecated': False},
+    'fsfullr': {'id': 'FSFULLR', 'deprecated': False},
+    'fsfullrsd': {'id': 'FSFULLRSD', 'deprecated': False},
+    'fsfullrwd': {'id': 'FSFULLRWD', 'deprecated': False},
+    'fsl-1.1-alv2': {'id': 'FSL-1.1-ALv2', 'deprecated': False},
+    'fsl-1.1-mit': {'id': 'FSL-1.1-MIT', 'deprecated': False},
+    'ftl': {'id': 'FTL', 'deprecated': False},
+    'furuseth': {'id': 'Furuseth', 'deprecated': False},
+    'fwlw': {'id': 'fwlw', 'deprecated': False},
+    'game-programming-gems': {'id': 'Game-Programming-Gems', 'deprecated': False},
+    'gcr-docs': {'id': 'GCR-docs', 'deprecated': False},
+    'gd': {'id': 'GD', 'deprecated': False},
+    'generic-xts': {'id': 'generic-xts', 'deprecated': False},
+    'gfdl-1.1': {'id': 'GFDL-1.1', 'deprecated': True},
+    'gfdl-1.1-invariants-only': {'id': 'GFDL-1.1-invariants-only', 'deprecated': False},
+    'gfdl-1.1-invariants-or-later': {'id': 'GFDL-1.1-invariants-or-later', 'deprecated': False},
+    'gfdl-1.1-no-invariants-only': {'id': 'GFDL-1.1-no-invariants-only', 'deprecated': False},
+    'gfdl-1.1-no-invariants-or-later': {'id': 'GFDL-1.1-no-invariants-or-later', 'deprecated': False},
+    'gfdl-1.1-only': {'id': 'GFDL-1.1-only', 'deprecated': False},
+    'gfdl-1.1-or-later': {'id': 'GFDL-1.1-or-later', 'deprecated': False},
+    'gfdl-1.2': {'id': 'GFDL-1.2', 'deprecated': True},
+    'gfdl-1.2-invariants-only': {'id': 'GFDL-1.2-invariants-only', 'deprecated': False},
+    'gfdl-1.2-invariants-or-later': {'id': 'GFDL-1.2-invariants-or-later', 'deprecated': False},
+    'gfdl-1.2-no-invariants-only': {'id': 'GFDL-1.2-no-invariants-only', 'deprecated': False},
+    'gfdl-1.2-no-invariants-or-later': {'id': 'GFDL-1.2-no-invariants-or-later', 'deprecated': False},
+    'gfdl-1.2-only': {'id': 'GFDL-1.2-only', 'deprecated': False},
+    'gfdl-1.2-or-later': {'id': 'GFDL-1.2-or-later', 'deprecated': False},
+    'gfdl-1.3': {'id': 'GFDL-1.3', 'deprecated': True},
+    'gfdl-1.3-invariants-only': {'id': 'GFDL-1.3-invariants-only', 'deprecated': False},
+    'gfdl-1.3-invariants-or-later': {'id': 'GFDL-1.3-invariants-or-later', 'deprecated': False},
+    'gfdl-1.3-no-invariants-only': {'id': 'GFDL-1.3-no-invariants-only', 'deprecated': False},
+    'gfdl-1.3-no-invariants-or-later': {'id': 'GFDL-1.3-no-invariants-or-later', 'deprecated': False},
+    'gfdl-1.3-only': {'id': 'GFDL-1.3-only', 'deprecated': False},
+    'gfdl-1.3-or-later': {'id': 'GFDL-1.3-or-later', 'deprecated': False},
+    'giftware': {'id': 'Giftware', 'deprecated': False},
+    'gl2ps': {'id': 'GL2PS', 'deprecated': False},
+    'glide': {'id': 'Glide', 'deprecated': False},
+    'glulxe': {'id': 'Glulxe', 'deprecated': False},
+    'glwtpl': {'id': 'GLWTPL', 'deprecated': False},
+    'gnuplot': {'id': 'gnuplot', 'deprecated': False},
+    'gpl-1.0': {'id': 'GPL-1.0', 'deprecated': True},
+    'gpl-1.0+': {'id': 'GPL-1.0+', 'deprecated': True},
+    'gpl-1.0-only': {'id': 'GPL-1.0-only', 'deprecated': False},
+    'gpl-1.0-or-later': {'id': 'GPL-1.0-or-later', 'deprecated': False},
+    'gpl-2.0': {'id': 'GPL-2.0', 'deprecated': True},
+    'gpl-2.0+': {'id': 'GPL-2.0+', 'deprecated': True},
+    'gpl-2.0-only': {'id': 'GPL-2.0-only', 'deprecated': False},
+    'gpl-2.0-or-later': {'id': 'GPL-2.0-or-later', 'deprecated': False},
+    'gpl-2.0-with-autoconf-exception': {'id': 'GPL-2.0-with-autoconf-exception', 'deprecated': True},
+    'gpl-2.0-with-bison-exception': {'id': 'GPL-2.0-with-bison-exception', 'deprecated': True},
+    'gpl-2.0-with-classpath-exception': {'id': 'GPL-2.0-with-classpath-exception', 'deprecated': True},
+    'gpl-2.0-with-font-exception': {'id': 'GPL-2.0-with-font-exception', 'deprecated': True},
+    'gpl-2.0-with-gcc-exception': {'id': 'GPL-2.0-with-GCC-exception', 'deprecated': True},
+    'gpl-3.0': {'id': 'GPL-3.0', 'deprecated': True},
+    'gpl-3.0+': {'id': 'GPL-3.0+', 'deprecated': True},
+    'gpl-3.0-only': {'id': 'GPL-3.0-only', 'deprecated': False},
+    'gpl-3.0-or-later': {'id': 'GPL-3.0-or-later', 'deprecated': False},
+    'gpl-3.0-with-autoconf-exception': {'id': 'GPL-3.0-with-autoconf-exception', 'deprecated': True},
+    'gpl-3.0-with-gcc-exception': {'id': 'GPL-3.0-with-GCC-exception', 'deprecated': True},
+    'graphics-gems': {'id': 'Graphics-Gems', 'deprecated': False},
+    'gsoap-1.3b': {'id': 'gSOAP-1.3b', 'deprecated': False},
+    'gtkbook': {'id': 'gtkbook', 'deprecated': False},
+    'gutmann': {'id': 'Gutmann', 'deprecated': False},
+    'haskellreport': {'id': 'HaskellReport', 'deprecated': False},
+    'hdf5': {'id': 'HDF5', 'deprecated': False},
+    'hdparm': {'id': 'hdparm', 'deprecated': False},
+    'hidapi': {'id': 'HIDAPI', 'deprecated': False},
+    'hippocratic-2.1': {'id': 'Hippocratic-2.1', 'deprecated': False},
+    'hp-1986': {'id': 'HP-1986', 'deprecated': False},
+    'hp-1989': {'id': 'HP-1989', 'deprecated': False},
+    'hpnd': {'id': 'HPND', 'deprecated': False},
+    'hpnd-dec': {'id': 'HPND-DEC', 'deprecated': False},
+    'hpnd-doc': {'id': 'HPND-doc', 'deprecated': False},
+    'hpnd-doc-sell': {'id': 'HPND-doc-sell', 'deprecated': False},
+    'hpnd-export-us': {'id': 'HPND-export-US', 'deprecated': False},
+    'hpnd-export-us-acknowledgement': {'id': 'HPND-export-US-acknowledgement', 'deprecated': False},
+    'hpnd-export-us-modify': {'id': 'HPND-export-US-modify', 'deprecated': False},
+    'hpnd-export2-us': {'id': 'HPND-export2-US', 'deprecated': False},
+    'hpnd-fenneberg-livingston': {'id': 'HPND-Fenneberg-Livingston', 'deprecated': False},
+    'hpnd-inria-imag': {'id': 'HPND-INRIA-IMAG', 'deprecated': False},
+    'hpnd-intel': {'id': 'HPND-Intel', 'deprecated': False},
+    'hpnd-kevlin-henney': {'id': 'HPND-Kevlin-Henney', 'deprecated': False},
+    'hpnd-markus-kuhn': {'id': 'HPND-Markus-Kuhn', 'deprecated': False},
+    'hpnd-merchantability-variant': {'id': 'HPND-merchantability-variant', 'deprecated': False},
+    'hpnd-mit-disclaimer': {'id': 'HPND-MIT-disclaimer', 'deprecated': False},
+    'hpnd-netrek': {'id': 'HPND-Netrek', 'deprecated': False},
+    'hpnd-pbmplus': {'id': 'HPND-Pbmplus', 'deprecated': False},
+    'hpnd-sell-mit-disclaimer-xserver': {'id': 'HPND-sell-MIT-disclaimer-xserver', 'deprecated': False},
+    'hpnd-sell-regexpr': {'id': 'HPND-sell-regexpr', 'deprecated': False},
+    'hpnd-sell-variant': {'id': 'HPND-sell-variant', 'deprecated': False},
+    'hpnd-sell-variant-mit-disclaimer': {'id': 'HPND-sell-variant-MIT-disclaimer', 'deprecated': False},
+    'hpnd-sell-variant-mit-disclaimer-rev': {'id': 'HPND-sell-variant-MIT-disclaimer-rev', 'deprecated': False},
+    'hpnd-uc': {'id': 'HPND-UC', 'deprecated': False},
+    'hpnd-uc-export-us': {'id': 'HPND-UC-export-US', 'deprecated': False},
+    'htmltidy': {'id': 'HTMLTIDY', 'deprecated': False},
+    'ibm-pibs': {'id': 'IBM-pibs', 'deprecated': False},
+    'icu': {'id': 'ICU', 'deprecated': False},
+    'iec-code-components-eula': {'id': 'IEC-Code-Components-EULA', 'deprecated': False},
+    'ijg': {'id': 'IJG', 'deprecated': False},
+    'ijg-short': {'id': 'IJG-short', 'deprecated': False},
+    'imagemagick': {'id': 'ImageMagick', 'deprecated': False},
+    'imatix': {'id': 'iMatix', 'deprecated': False},
+    'imlib2': {'id': 'Imlib2', 'deprecated': False},
+    'info-zip': {'id': 'Info-ZIP', 'deprecated': False},
+    'inner-net-2.0': {'id': 'Inner-Net-2.0', 'deprecated': False},
+    'innosetup': {'id': 'InnoSetup', 'deprecated': False},
+    'intel': {'id': 'Intel', 'deprecated': False},
+    'intel-acpi': {'id': 'Intel-ACPI', 'deprecated': False},
+    'interbase-1.0': {'id': 'Interbase-1.0', 'deprecated': False},
+    'ipa': {'id': 'IPA', 'deprecated': False},
+    'ipl-1.0': {'id': 'IPL-1.0', 'deprecated': False},
+    'isc': {'id': 'ISC', 'deprecated': False},
+    'isc-veillard': {'id': 'ISC-Veillard', 'deprecated': False},
+    'jam': {'id': 'Jam', 'deprecated': False},
+    'jasper-2.0': {'id': 'JasPer-2.0', 'deprecated': False},
+    'jove': {'id': 'jove', 'deprecated': False},
+    'jpl-image': {'id': 'JPL-image', 'deprecated': False},
+    'jpnic': {'id': 'JPNIC', 'deprecated': False},
+    'json': {'id': 'JSON', 'deprecated': False},
+    'kastrup': {'id': 'Kastrup', 'deprecated': False},
+    'kazlib': {'id': 'Kazlib', 'deprecated': False},
+    'knuth-ctan': {'id': 'Knuth-CTAN', 'deprecated': False},
+    'lal-1.2': {'id': 'LAL-1.2', 'deprecated': False},
+    'lal-1.3': {'id': 'LAL-1.3', 'deprecated': False},
+    'latex2e': {'id': 'Latex2e', 'deprecated': False},
+    'latex2e-translated-notice': {'id': 'Latex2e-translated-notice', 'deprecated': False},
+    'leptonica': {'id': 'Leptonica', 'deprecated': False},
+    'lgpl-2.0': {'id': 'LGPL-2.0', 'deprecated': True},
+    'lgpl-2.0+': {'id': 'LGPL-2.0+', 'deprecated': True},
+    'lgpl-2.0-only': {'id': 'LGPL-2.0-only', 'deprecated': False},
+    'lgpl-2.0-or-later': {'id': 'LGPL-2.0-or-later', 'deprecated': False},
+    'lgpl-2.1': {'id': 'LGPL-2.1', 'deprecated': True},
+    'lgpl-2.1+': {'id': 'LGPL-2.1+', 'deprecated': True},
+    'lgpl-2.1-only': {'id': 'LGPL-2.1-only', 'deprecated': False},
+    'lgpl-2.1-or-later': {'id': 'LGPL-2.1-or-later', 'deprecated': False},
+    'lgpl-3.0': {'id': 'LGPL-3.0', 'deprecated': True},
+    'lgpl-3.0+': {'id': 'LGPL-3.0+', 'deprecated': True},
+    'lgpl-3.0-only': {'id': 'LGPL-3.0-only', 'deprecated': False},
+    'lgpl-3.0-or-later': {'id': 'LGPL-3.0-or-later', 'deprecated': False},
+    'lgpllr': {'id': 'LGPLLR', 'deprecated': False},
+    'libpng': {'id': 'Libpng', 'deprecated': False},
+    'libpng-1.6.35': {'id': 'libpng-1.6.35', 'deprecated': False},
+    'libpng-2.0': {'id': 'libpng-2.0', 'deprecated': False},
+    'libselinux-1.0': {'id': 'libselinux-1.0', 'deprecated': False},
+    'libtiff': {'id': 'libtiff', 'deprecated': False},
+    'libutil-david-nugent': {'id': 'libutil-David-Nugent', 'deprecated': False},
+    'liliq-p-1.1': {'id': 'LiLiQ-P-1.1', 'deprecated': False},
+    'liliq-r-1.1': {'id': 'LiLiQ-R-1.1', 'deprecated': False},
+    'liliq-rplus-1.1': {'id': 'LiLiQ-Rplus-1.1', 'deprecated': False},
+    'linux-man-pages-1-para': {'id': 'Linux-man-pages-1-para', 'deprecated': False},
+    'linux-man-pages-copyleft': {'id': 'Linux-man-pages-copyleft', 'deprecated': False},
+    'linux-man-pages-copyleft-2-para': {'id': 'Linux-man-pages-copyleft-2-para', 'deprecated': False},
+    'linux-man-pages-copyleft-var': {'id': 'Linux-man-pages-copyleft-var', 'deprecated': False},
+    'linux-openib': {'id': 'Linux-OpenIB', 'deprecated': False},
+    'loop': {'id': 'LOOP', 'deprecated': False},
+    'lpd-document': {'id': 'LPD-document', 'deprecated': False},
+    'lpl-1.0': {'id': 'LPL-1.0', 'deprecated': False},
+    'lpl-1.02': {'id': 'LPL-1.02', 'deprecated': False},
+    'lppl-1.0': {'id': 'LPPL-1.0', 'deprecated': False},
+    'lppl-1.1': {'id': 'LPPL-1.1', 'deprecated': False},
+    'lppl-1.2': {'id': 'LPPL-1.2', 'deprecated': False},
+    'lppl-1.3a': {'id': 'LPPL-1.3a', 'deprecated': False},
+    'lppl-1.3c': {'id': 'LPPL-1.3c', 'deprecated': False},
+    'lsof': {'id': 'lsof', 'deprecated': False},
+    'lucida-bitmap-fonts': {'id': 'Lucida-Bitmap-Fonts', 'deprecated': False},
+    'lzma-sdk-9.11-to-9.20': {'id': 'LZMA-SDK-9.11-to-9.20', 'deprecated': False},
+    'lzma-sdk-9.22': {'id': 'LZMA-SDK-9.22', 'deprecated': False},
+    'mackerras-3-clause': {'id': 'Mackerras-3-Clause', 'deprecated': False},
+    'mackerras-3-clause-acknowledgment': {'id': 'Mackerras-3-Clause-acknowledgment', 'deprecated': False},
+    'magaz': {'id': 'magaz', 'deprecated': False},
+    'mailprio': {'id': 'mailprio', 'deprecated': False},
+    'makeindex': {'id': 'MakeIndex', 'deprecated': False},
+    'man2html': {'id': 'man2html', 'deprecated': False},
+    'martin-birgmeier': {'id': 'Martin-Birgmeier', 'deprecated': False},
+    'mcphee-slideshow': {'id': 'McPhee-slideshow', 'deprecated': False},
+    'metamail': {'id': 'metamail', 'deprecated': False},
+    'minpack': {'id': 'Minpack', 'deprecated': False},
+    'mips': {'id': 'MIPS', 'deprecated': False},
+    'miros': {'id': 'MirOS', 'deprecated': False},
+    'mit': {'id': 'MIT', 'deprecated': False},
+    'mit-0': {'id': 'MIT-0', 'deprecated': False},
+    'mit-advertising': {'id': 'MIT-advertising', 'deprecated': False},
+    'mit-click': {'id': 'MIT-Click', 'deprecated': False},
+    'mit-cmu': {'id': 'MIT-CMU', 'deprecated': False},
+    'mit-enna': {'id': 'MIT-enna', 'deprecated': False},
+    'mit-feh': {'id': 'MIT-feh', 'deprecated': False},
+    'mit-festival': {'id': 'MIT-Festival', 'deprecated': False},
+    'mit-khronos-old': {'id': 'MIT-Khronos-old', 'deprecated': False},
+    'mit-modern-variant': {'id': 'MIT-Modern-Variant', 'deprecated': False},
+    'mit-open-group': {'id': 'MIT-open-group', 'deprecated': False},
+    'mit-testregex': {'id': 'MIT-testregex', 'deprecated': False},
+    'mit-wu': {'id': 'MIT-Wu', 'deprecated': False},
+    'mitnfa': {'id': 'MITNFA', 'deprecated': False},
+    'mmixware': {'id': 'MMIXware', 'deprecated': False},
+    'motosoto': {'id': 'Motosoto', 'deprecated': False},
+    'mpeg-ssg': {'id': 'MPEG-SSG', 'deprecated': False},
+    'mpi-permissive': {'id': 'mpi-permissive', 'deprecated': False},
+    'mpich2': {'id': 'mpich2', 'deprecated': False},
+    'mpl-1.0': {'id': 'MPL-1.0', 'deprecated': False},
+    'mpl-1.1': {'id': 'MPL-1.1', 'deprecated': False},
+    'mpl-2.0': {'id': 'MPL-2.0', 'deprecated': False},
+    'mpl-2.0-no-copyleft-exception': {'id': 'MPL-2.0-no-copyleft-exception', 'deprecated': False},
+    'mplus': {'id': 'mplus', 'deprecated': False},
+    'ms-lpl': {'id': 'MS-LPL', 'deprecated': False},
+    'ms-pl': {'id': 'MS-PL', 'deprecated': False},
+    'ms-rl': {'id': 'MS-RL', 'deprecated': False},
+    'mtll': {'id': 'MTLL', 'deprecated': False},
+    'mulanpsl-1.0': {'id': 'MulanPSL-1.0', 'deprecated': False},
+    'mulanpsl-2.0': {'id': 'MulanPSL-2.0', 'deprecated': False},
+    'multics': {'id': 'Multics', 'deprecated': False},
+    'mup': {'id': 'Mup', 'deprecated': False},
+    'naist-2003': {'id': 'NAIST-2003', 'deprecated': False},
+    'nasa-1.3': {'id': 'NASA-1.3', 'deprecated': False},
+    'naumen': {'id': 'Naumen', 'deprecated': False},
+    'nbpl-1.0': {'id': 'NBPL-1.0', 'deprecated': False},
+    'ncbi-pd': {'id': 'NCBI-PD', 'deprecated': False},
+    'ncgl-uk-2.0': {'id': 'NCGL-UK-2.0', 'deprecated': False},
+    'ncl': {'id': 'NCL', 'deprecated': False},
+    'ncsa': {'id': 'NCSA', 'deprecated': False},
+    'net-snmp': {'id': 'Net-SNMP', 'deprecated': True},
+    'netcdf': {'id': 'NetCDF', 'deprecated': False},
+    'newsletr': {'id': 'Newsletr', 'deprecated': False},
+    'ngpl': {'id': 'NGPL', 'deprecated': False},
+    'ngrep': {'id': 'ngrep', 'deprecated': False},
+    'nicta-1.0': {'id': 'NICTA-1.0', 'deprecated': False},
+    'nist-pd': {'id': 'NIST-PD', 'deprecated': False},
+    'nist-pd-fallback': {'id': 'NIST-PD-fallback', 'deprecated': False},
+    'nist-software': {'id': 'NIST-Software', 'deprecated': False},
+    'nlod-1.0': {'id': 'NLOD-1.0', 'deprecated': False},
+    'nlod-2.0': {'id': 'NLOD-2.0', 'deprecated': False},
+    'nlpl': {'id': 'NLPL', 'deprecated': False},
+    'nokia': {'id': 'Nokia', 'deprecated': False},
+    'nosl': {'id': 'NOSL', 'deprecated': False},
+    'noweb': {'id': 'Noweb', 'deprecated': False},
+    'npl-1.0': {'id': 'NPL-1.0', 'deprecated': False},
+    'npl-1.1': {'id': 'NPL-1.1', 'deprecated': False},
+    'nposl-3.0': {'id': 'NPOSL-3.0', 'deprecated': False},
+    'nrl': {'id': 'NRL', 'deprecated': False},
+    'ntia-pd': {'id': 'NTIA-PD', 'deprecated': False},
+    'ntp': {'id': 'NTP', 'deprecated': False},
+    'ntp-0': {'id': 'NTP-0', 'deprecated': False},
+    'nunit': {'id': 'Nunit', 'deprecated': True},
+    'o-uda-1.0': {'id': 'O-UDA-1.0', 'deprecated': False},
+    'oar': {'id': 'OAR', 'deprecated': False},
+    'occt-pl': {'id': 'OCCT-PL', 'deprecated': False},
+    'oclc-2.0': {'id': 'OCLC-2.0', 'deprecated': False},
+    'odbl-1.0': {'id': 'ODbL-1.0', 'deprecated': False},
+    'odc-by-1.0': {'id': 'ODC-By-1.0', 'deprecated': False},
+    'offis': {'id': 'OFFIS', 'deprecated': False},
+    'ofl-1.0': {'id': 'OFL-1.0', 'deprecated': False},
+    'ofl-1.0-no-rfn': {'id': 'OFL-1.0-no-RFN', 'deprecated': False},
+    'ofl-1.0-rfn': {'id': 'OFL-1.0-RFN', 'deprecated': False},
+    'ofl-1.1': {'id': 'OFL-1.1', 'deprecated': False},
+    'ofl-1.1-no-rfn': {'id': 'OFL-1.1-no-RFN', 'deprecated': False},
+    'ofl-1.1-rfn': {'id': 'OFL-1.1-RFN', 'deprecated': False},
+    'ogc-1.0': {'id': 'OGC-1.0', 'deprecated': False},
+    'ogdl-taiwan-1.0': {'id': 'OGDL-Taiwan-1.0', 'deprecated': False},
+    'ogl-canada-2.0': {'id': 'OGL-Canada-2.0', 'deprecated': False},
+    'ogl-uk-1.0': {'id': 'OGL-UK-1.0', 'deprecated': False},
+    'ogl-uk-2.0': {'id': 'OGL-UK-2.0', 'deprecated': False},
+    'ogl-uk-3.0': {'id': 'OGL-UK-3.0', 'deprecated': False},
+    'ogtsl': {'id': 'OGTSL', 'deprecated': False},
+    'oldap-1.1': {'id': 'OLDAP-1.1', 'deprecated': False},
+    'oldap-1.2': {'id': 'OLDAP-1.2', 'deprecated': False},
+    'oldap-1.3': {'id': 'OLDAP-1.3', 'deprecated': False},
+    'oldap-1.4': {'id': 'OLDAP-1.4', 'deprecated': False},
+    'oldap-2.0': {'id': 'OLDAP-2.0', 'deprecated': False},
+    'oldap-2.0.1': {'id': 'OLDAP-2.0.1', 'deprecated': False},
+    'oldap-2.1': {'id': 'OLDAP-2.1', 'deprecated': False},
+    'oldap-2.2': {'id': 'OLDAP-2.2', 'deprecated': False},
+    'oldap-2.2.1': {'id': 'OLDAP-2.2.1', 'deprecated': False},
+    'oldap-2.2.2': {'id': 'OLDAP-2.2.2', 'deprecated': False},
+    'oldap-2.3': {'id': 'OLDAP-2.3', 'deprecated': False},
+    'oldap-2.4': {'id': 'OLDAP-2.4', 'deprecated': False},
+    'oldap-2.5': {'id': 'OLDAP-2.5', 'deprecated': False},
+    'oldap-2.6': {'id': 'OLDAP-2.6', 'deprecated': False},
+    'oldap-2.7': {'id': 'OLDAP-2.7', 'deprecated': False},
+    'oldap-2.8': {'id': 'OLDAP-2.8', 'deprecated': False},
+    'olfl-1.3': {'id': 'OLFL-1.3', 'deprecated': False},
+    'oml': {'id': 'OML', 'deprecated': False},
+    'openpbs-2.3': {'id': 'OpenPBS-2.3', 'deprecated': False},
+    'openssl': {'id': 'OpenSSL', 'deprecated': False},
+    'openssl-standalone': {'id': 'OpenSSL-standalone', 'deprecated': False},
+    'openvision': {'id': 'OpenVision', 'deprecated': False},
+    'opl-1.0': {'id': 'OPL-1.0', 'deprecated': False},
+    'opl-uk-3.0': {'id': 'OPL-UK-3.0', 'deprecated': False},
+    'opubl-1.0': {'id': 'OPUBL-1.0', 'deprecated': False},
+    'oset-pl-2.1': {'id': 'OSET-PL-2.1', 'deprecated': False},
+    'osl-1.0': {'id': 'OSL-1.0', 'deprecated': False},
+    'osl-1.1': {'id': 'OSL-1.1', 'deprecated': False},
+    'osl-2.0': {'id': 'OSL-2.0', 'deprecated': False},
+    'osl-2.1': {'id': 'OSL-2.1', 'deprecated': False},
+    'osl-3.0': {'id': 'OSL-3.0', 'deprecated': False},
+    'padl': {'id': 'PADL', 'deprecated': False},
+    'parity-6.0.0': {'id': 'Parity-6.0.0', 'deprecated': False},
+    'parity-7.0.0': {'id': 'Parity-7.0.0', 'deprecated': False},
+    'pddl-1.0': {'id': 'PDDL-1.0', 'deprecated': False},
+    'php-3.0': {'id': 'PHP-3.0', 'deprecated': False},
+    'php-3.01': {'id': 'PHP-3.01', 'deprecated': False},
+    'pixar': {'id': 'Pixar', 'deprecated': False},
+    'pkgconf': {'id': 'pkgconf', 'deprecated': False},
+    'plexus': {'id': 'Plexus', 'deprecated': False},
+    'pnmstitch': {'id': 'pnmstitch', 'deprecated': False},
+    'polyform-noncommercial-1.0.0': {'id': 'PolyForm-Noncommercial-1.0.0', 'deprecated': False},
+    'polyform-small-business-1.0.0': {'id': 'PolyForm-Small-Business-1.0.0', 'deprecated': False},
+    'postgresql': {'id': 'PostgreSQL', 'deprecated': False},
+    'ppl': {'id': 'PPL', 'deprecated': False},
+    'psf-2.0': {'id': 'PSF-2.0', 'deprecated': False},
+    'psfrag': {'id': 'psfrag', 'deprecated': False},
+    'psutils': {'id': 'psutils', 'deprecated': False},
+    'python-2.0': {'id': 'Python-2.0', 'deprecated': False},
+    'python-2.0.1': {'id': 'Python-2.0.1', 'deprecated': False},
+    'python-ldap': {'id': 'python-ldap', 'deprecated': False},
+    'qhull': {'id': 'Qhull', 'deprecated': False},
+    'qpl-1.0': {'id': 'QPL-1.0', 'deprecated': False},
+    'qpl-1.0-inria-2004': {'id': 'QPL-1.0-INRIA-2004', 'deprecated': False},
+    'radvd': {'id': 'radvd', 'deprecated': False},
+    'rdisc': {'id': 'Rdisc', 'deprecated': False},
+    'rhecos-1.1': {'id': 'RHeCos-1.1', 'deprecated': False},
+    'rpl-1.1': {'id': 'RPL-1.1', 'deprecated': False},
+    'rpl-1.5': {'id': 'RPL-1.5', 'deprecated': False},
+    'rpsl-1.0': {'id': 'RPSL-1.0', 'deprecated': False},
+    'rsa-md': {'id': 'RSA-MD', 'deprecated': False},
+    'rscpl': {'id': 'RSCPL', 'deprecated': False},
+    'ruby': {'id': 'Ruby', 'deprecated': False},
+    'ruby-pty': {'id': 'Ruby-pty', 'deprecated': False},
+    'sax-pd': {'id': 'SAX-PD', 'deprecated': False},
+    'sax-pd-2.0': {'id': 'SAX-PD-2.0', 'deprecated': False},
+    'saxpath': {'id': 'Saxpath', 'deprecated': False},
+    'scea': {'id': 'SCEA', 'deprecated': False},
+    'schemereport': {'id': 'SchemeReport', 'deprecated': False},
+    'sendmail': {'id': 'Sendmail', 'deprecated': False},
+    'sendmail-8.23': {'id': 'Sendmail-8.23', 'deprecated': False},
+    'sendmail-open-source-1.1': {'id': 'Sendmail-Open-Source-1.1', 'deprecated': False},
+    'sgi-b-1.0': {'id': 'SGI-B-1.0', 'deprecated': False},
+    'sgi-b-1.1': {'id': 'SGI-B-1.1', 'deprecated': False},
+    'sgi-b-2.0': {'id': 'SGI-B-2.0', 'deprecated': False},
+    'sgi-opengl': {'id': 'SGI-OpenGL', 'deprecated': False},
+    'sgp4': {'id': 'SGP4', 'deprecated': False},
+    'shl-0.5': {'id': 'SHL-0.5', 'deprecated': False},
+    'shl-0.51': {'id': 'SHL-0.51', 'deprecated': False},
+    'simpl-2.0': {'id': 'SimPL-2.0', 'deprecated': False},
+    'sissl': {'id': 'SISSL', 'deprecated': False},
+    'sissl-1.2': {'id': 'SISSL-1.2', 'deprecated': False},
+    'sl': {'id': 'SL', 'deprecated': False},
+    'sleepycat': {'id': 'Sleepycat', 'deprecated': False},
+    'smail-gpl': {'id': 'SMAIL-GPL', 'deprecated': False},
+    'smlnj': {'id': 'SMLNJ', 'deprecated': False},
+    'smppl': {'id': 'SMPPL', 'deprecated': False},
+    'snia': {'id': 'SNIA', 'deprecated': False},
+    'snprintf': {'id': 'snprintf', 'deprecated': False},
+    'sofa': {'id': 'SOFA', 'deprecated': False},
+    'softsurfer': {'id': 'softSurfer', 'deprecated': False},
+    'soundex': {'id': 'Soundex', 'deprecated': False},
+    'spencer-86': {'id': 'Spencer-86', 'deprecated': False},
+    'spencer-94': {'id': 'Spencer-94', 'deprecated': False},
+    'spencer-99': {'id': 'Spencer-99', 'deprecated': False},
+    'spl-1.0': {'id': 'SPL-1.0', 'deprecated': False},
+    'ssh-keyscan': {'id': 'ssh-keyscan', 'deprecated': False},
+    'ssh-openssh': {'id': 'SSH-OpenSSH', 'deprecated': False},
+    'ssh-short': {'id': 'SSH-short', 'deprecated': False},
+    'ssleay-standalone': {'id': 'SSLeay-standalone', 'deprecated': False},
+    'sspl-1.0': {'id': 'SSPL-1.0', 'deprecated': False},
+    'standardml-nj': {'id': 'StandardML-NJ', 'deprecated': True},
+    'sugarcrm-1.1.3': {'id': 'SugarCRM-1.1.3', 'deprecated': False},
+    'sul-1.0': {'id': 'SUL-1.0', 'deprecated': False},
+    'sun-ppp': {'id': 'Sun-PPP', 'deprecated': False},
+    'sun-ppp-2000': {'id': 'Sun-PPP-2000', 'deprecated': False},
+    'sunpro': {'id': 'SunPro', 'deprecated': False},
+    'swl': {'id': 'SWL', 'deprecated': False},
+    'swrule': {'id': 'swrule', 'deprecated': False},
+    'symlinks': {'id': 'Symlinks', 'deprecated': False},
+    'tapr-ohl-1.0': {'id': 'TAPR-OHL-1.0', 'deprecated': False},
+    'tcl': {'id': 'TCL', 'deprecated': False},
+    'tcp-wrappers': {'id': 'TCP-wrappers', 'deprecated': False},
+    'termreadkey': {'id': 'TermReadKey', 'deprecated': False},
+    'tgppl-1.0': {'id': 'TGPPL-1.0', 'deprecated': False},
+    'thirdeye': {'id': 'ThirdEye', 'deprecated': False},
+    'threeparttable': {'id': 'threeparttable', 'deprecated': False},
+    'tmate': {'id': 'TMate', 'deprecated': False},
+    'torque-1.1': {'id': 'TORQUE-1.1', 'deprecated': False},
+    'tosl': {'id': 'TOSL', 'deprecated': False},
+    'tpdl': {'id': 'TPDL', 'deprecated': False},
+    'tpl-1.0': {'id': 'TPL-1.0', 'deprecated': False},
+    'trustedqsl': {'id': 'TrustedQSL', 'deprecated': False},
+    'ttwl': {'id': 'TTWL', 'deprecated': False},
+    'ttyp0': {'id': 'TTYP0', 'deprecated': False},
+    'tu-berlin-1.0': {'id': 'TU-Berlin-1.0', 'deprecated': False},
+    'tu-berlin-2.0': {'id': 'TU-Berlin-2.0', 'deprecated': False},
+    'ubuntu-font-1.0': {'id': 'Ubuntu-font-1.0', 'deprecated': False},
+    'ucar': {'id': 'UCAR', 'deprecated': False},
+    'ucl-1.0': {'id': 'UCL-1.0', 'deprecated': False},
+    'ulem': {'id': 'ulem', 'deprecated': False},
+    'umich-merit': {'id': 'UMich-Merit', 'deprecated': False},
+    'unicode-3.0': {'id': 'Unicode-3.0', 'deprecated': False},
+    'unicode-dfs-2015': {'id': 'Unicode-DFS-2015', 'deprecated': False},
+    'unicode-dfs-2016': {'id': 'Unicode-DFS-2016', 'deprecated': False},
+    'unicode-tou': {'id': 'Unicode-TOU', 'deprecated': False},
+    'unixcrypt': {'id': 'UnixCrypt', 'deprecated': False},
+    'unlicense': {'id': 'Unlicense', 'deprecated': False},
+    'unlicense-libtelnet': {'id': 'Unlicense-libtelnet', 'deprecated': False},
+    'unlicense-libwhirlpool': {'id': 'Unlicense-libwhirlpool', 'deprecated': False},
+    'upl-1.0': {'id': 'UPL-1.0', 'deprecated': False},
+    'urt-rle': {'id': 'URT-RLE', 'deprecated': False},
+    'vim': {'id': 'Vim', 'deprecated': False},
+    'vostrom': {'id': 'VOSTROM', 'deprecated': False},
+    'vsl-1.0': {'id': 'VSL-1.0', 'deprecated': False},
+    'w3c': {'id': 'W3C', 'deprecated': False},
+    'w3c-19980720': {'id': 'W3C-19980720', 'deprecated': False},
+    'w3c-20150513': {'id': 'W3C-20150513', 'deprecated': False},
+    'w3m': {'id': 'w3m', 'deprecated': False},
+    'watcom-1.0': {'id': 'Watcom-1.0', 'deprecated': False},
+    'widget-workshop': {'id': 'Widget-Workshop', 'deprecated': False},
+    'wsuipa': {'id': 'Wsuipa', 'deprecated': False},
+    'wtfpl': {'id': 'WTFPL', 'deprecated': False},
+    'wwl': {'id': 'wwl', 'deprecated': False},
+    'wxwindows': {'id': 'wxWindows', 'deprecated': True},
+    'x11': {'id': 'X11', 'deprecated': False},
+    'x11-distribute-modifications-variant': {'id': 'X11-distribute-modifications-variant', 'deprecated': False},
+    'x11-swapped': {'id': 'X11-swapped', 'deprecated': False},
+    'xdebug-1.03': {'id': 'Xdebug-1.03', 'deprecated': False},
+    'xerox': {'id': 'Xerox', 'deprecated': False},
+    'xfig': {'id': 'Xfig', 'deprecated': False},
+    'xfree86-1.1': {'id': 'XFree86-1.1', 'deprecated': False},
+    'xinetd': {'id': 'xinetd', 'deprecated': False},
+    'xkeyboard-config-zinoviev': {'id': 'xkeyboard-config-Zinoviev', 'deprecated': False},
+    'xlock': {'id': 'xlock', 'deprecated': False},
+    'xnet': {'id': 'Xnet', 'deprecated': False},
+    'xpp': {'id': 'xpp', 'deprecated': False},
+    'xskat': {'id': 'XSkat', 'deprecated': False},
+    'xzoom': {'id': 'xzoom', 'deprecated': False},
+    'ypl-1.0': {'id': 'YPL-1.0', 'deprecated': False},
+    'ypl-1.1': {'id': 'YPL-1.1', 'deprecated': False},
+    'zed': {'id': 'Zed', 'deprecated': False},
+    'zeeff': {'id': 'Zeeff', 'deprecated': False},
+    'zend-2.0': {'id': 'Zend-2.0', 'deprecated': False},
+    'zimbra-1.3': {'id': 'Zimbra-1.3', 'deprecated': False},
+    'zimbra-1.4': {'id': 'Zimbra-1.4', 'deprecated': False},
+    'zlib': {'id': 'Zlib', 'deprecated': False},
+    'zlib-acknowledgement': {'id': 'zlib-acknowledgement', 'deprecated': False},
+    'zpl-1.1': {'id': 'ZPL-1.1', 'deprecated': False},
+    'zpl-2.0': {'id': 'ZPL-2.0', 'deprecated': False},
+    'zpl-2.1': {'id': 'ZPL-2.1', 'deprecated': False},
+}
+
+EXCEPTIONS: dict[str, SPDXException] = {
+    '389-exception': {'id': '389-exception', 'deprecated': False},
+    'asterisk-exception': {'id': 'Asterisk-exception', 'deprecated': False},
+    'asterisk-linking-protocols-exception': {'id': 'Asterisk-linking-protocols-exception', 'deprecated': False},
+    'autoconf-exception-2.0': {'id': 'Autoconf-exception-2.0', 'deprecated': False},
+    'autoconf-exception-3.0': {'id': 'Autoconf-exception-3.0', 'deprecated': False},
+    'autoconf-exception-generic': {'id': 'Autoconf-exception-generic', 'deprecated': False},
+    'autoconf-exception-generic-3.0': {'id': 'Autoconf-exception-generic-3.0', 'deprecated': False},
+    'autoconf-exception-macro': {'id': 'Autoconf-exception-macro', 'deprecated': False},
+    'bison-exception-1.24': {'id': 'Bison-exception-1.24', 'deprecated': False},
+    'bison-exception-2.2': {'id': 'Bison-exception-2.2', 'deprecated': False},
+    'bootloader-exception': {'id': 'Bootloader-exception', 'deprecated': False},
+    'cgal-linking-exception': {'id': 'CGAL-linking-exception', 'deprecated': False},
+    'classpath-exception-2.0': {'id': 'Classpath-exception-2.0', 'deprecated': False},
+    'clisp-exception-2.0': {'id': 'CLISP-exception-2.0', 'deprecated': False},
+    'cryptsetup-openssl-exception': {'id': 'cryptsetup-OpenSSL-exception', 'deprecated': False},
+    'digia-qt-lgpl-exception-1.1': {'id': 'Digia-Qt-LGPL-exception-1.1', 'deprecated': False},
+    'digirule-foss-exception': {'id': 'DigiRule-FOSS-exception', 'deprecated': False},
+    'ecos-exception-2.0': {'id': 'eCos-exception-2.0', 'deprecated': False},
+    'erlang-otp-linking-exception': {'id': 'erlang-otp-linking-exception', 'deprecated': False},
+    'fawkes-runtime-exception': {'id': 'Fawkes-Runtime-exception', 'deprecated': False},
+    'fltk-exception': {'id': 'FLTK-exception', 'deprecated': False},
+    'fmt-exception': {'id': 'fmt-exception', 'deprecated': False},
+    'font-exception-2.0': {'id': 'Font-exception-2.0', 'deprecated': False},
+    'freertos-exception-2.0': {'id': 'freertos-exception-2.0', 'deprecated': False},
+    'gcc-exception-2.0': {'id': 'GCC-exception-2.0', 'deprecated': False},
+    'gcc-exception-2.0-note': {'id': 'GCC-exception-2.0-note', 'deprecated': False},
+    'gcc-exception-3.1': {'id': 'GCC-exception-3.1', 'deprecated': False},
+    'gmsh-exception': {'id': 'Gmsh-exception', 'deprecated': False},
+    'gnat-exception': {'id': 'GNAT-exception', 'deprecated': False},
+    'gnome-examples-exception': {'id': 'GNOME-examples-exception', 'deprecated': False},
+    'gnu-compiler-exception': {'id': 'GNU-compiler-exception', 'deprecated': False},
+    'gnu-javamail-exception': {'id': 'gnu-javamail-exception', 'deprecated': False},
+    'gpl-3.0-389-ds-base-exception': {'id': 'GPL-3.0-389-ds-base-exception', 'deprecated': False},
+    'gpl-3.0-interface-exception': {'id': 'GPL-3.0-interface-exception', 'deprecated': False},
+    'gpl-3.0-linking-exception': {'id': 'GPL-3.0-linking-exception', 'deprecated': False},
+    'gpl-3.0-linking-source-exception': {'id': 'GPL-3.0-linking-source-exception', 'deprecated': False},
+    'gpl-cc-1.0': {'id': 'GPL-CC-1.0', 'deprecated': False},
+    'gstreamer-exception-2005': {'id': 'GStreamer-exception-2005', 'deprecated': False},
+    'gstreamer-exception-2008': {'id': 'GStreamer-exception-2008', 'deprecated': False},
+    'harbour-exception': {'id': 'harbour-exception', 'deprecated': False},
+    'i2p-gpl-java-exception': {'id': 'i2p-gpl-java-exception', 'deprecated': False},
+    'independent-modules-exception': {'id': 'Independent-modules-exception', 'deprecated': False},
+    'kicad-libraries-exception': {'id': 'KiCad-libraries-exception', 'deprecated': False},
+    'lgpl-3.0-linking-exception': {'id': 'LGPL-3.0-linking-exception', 'deprecated': False},
+    'libpri-openh323-exception': {'id': 'libpri-OpenH323-exception', 'deprecated': False},
+    'libtool-exception': {'id': 'Libtool-exception', 'deprecated': False},
+    'linux-syscall-note': {'id': 'Linux-syscall-note', 'deprecated': False},
+    'llgpl': {'id': 'LLGPL', 'deprecated': False},
+    'llvm-exception': {'id': 'LLVM-exception', 'deprecated': False},
+    'lzma-exception': {'id': 'LZMA-exception', 'deprecated': False},
+    'mif-exception': {'id': 'mif-exception', 'deprecated': False},
+    'mxml-exception': {'id': 'mxml-exception', 'deprecated': False},
+    'nokia-qt-exception-1.1': {'id': 'Nokia-Qt-exception-1.1', 'deprecated': True},
+    'ocaml-lgpl-linking-exception': {'id': 'OCaml-LGPL-linking-exception', 'deprecated': False},
+    'occt-exception-1.0': {'id': 'OCCT-exception-1.0', 'deprecated': False},
+    'openjdk-assembly-exception-1.0': {'id': 'OpenJDK-assembly-exception-1.0', 'deprecated': False},
+    'openvpn-openssl-exception': {'id': 'openvpn-openssl-exception', 'deprecated': False},
+    'pcre2-exception': {'id': 'PCRE2-exception', 'deprecated': False},
+    'polyparse-exception': {'id': 'polyparse-exception', 'deprecated': False},
+    'ps-or-pdf-font-exception-20170817': {'id': 'PS-or-PDF-font-exception-20170817', 'deprecated': False},
+    'qpl-1.0-inria-2004-exception': {'id': 'QPL-1.0-INRIA-2004-exception', 'deprecated': False},
+    'qt-gpl-exception-1.0': {'id': 'Qt-GPL-exception-1.0', 'deprecated': False},
+    'qt-lgpl-exception-1.1': {'id': 'Qt-LGPL-exception-1.1', 'deprecated': False},
+    'qwt-exception-1.0': {'id': 'Qwt-exception-1.0', 'deprecated': False},
+    'romic-exception': {'id': 'romic-exception', 'deprecated': False},
+    'rrdtool-floss-exception-2.0': {'id': 'RRDtool-FLOSS-exception-2.0', 'deprecated': False},
+    'sane-exception': {'id': 'SANE-exception', 'deprecated': False},
+    'shl-2.0': {'id': 'SHL-2.0', 'deprecated': False},
+    'shl-2.1': {'id': 'SHL-2.1', 'deprecated': False},
+    'stunnel-exception': {'id': 'stunnel-exception', 'deprecated': False},
+    'swi-exception': {'id': 'SWI-exception', 'deprecated': False},
+    'swift-exception': {'id': 'Swift-exception', 'deprecated': False},
+    'texinfo-exception': {'id': 'Texinfo-exception', 'deprecated': False},
+    'u-boot-exception-2.0': {'id': 'u-boot-exception-2.0', 'deprecated': False},
+    'ubdl-exception': {'id': 'UBDL-exception', 'deprecated': False},
+    'universal-foss-exception-1.0': {'id': 'Universal-FOSS-exception-1.0', 'deprecated': False},
+    'vsftpd-openssl-exception': {'id': 'vsftpd-openssl-exception', 'deprecated': False},
+    'wxwindows-exception-3.1': {'id': 'WxWindows-exception-3.1', 'deprecated': False},
+    'x11vnc-openssl-exception': {'id': 'x11vnc-openssl-exception', 'deprecated': False},
+}
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/markers.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/markers.py
new file mode 100644
index 0000000..6284645
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/markers.py
@@ -0,0 +1,492 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import operator
+import os
+import platform
+import sys
+from typing import AbstractSet, Callable, Literal, Mapping, TypedDict, Union, cast
+
+from ._parser import MarkerAtom, MarkerList, Op, Value, Variable
+from ._parser import parse_marker as _parse_marker
+from ._tokenizer import ParserSyntaxError
+from .specifiers import InvalidSpecifier, Specifier
+from .utils import canonicalize_name
+
+__all__ = [
+    "Environment",
+    "EvaluateContext",
+    "InvalidMarker",
+    "Marker",
+    "UndefinedComparison",
+    "UndefinedEnvironmentName",
+    "default_environment",
+]
+
+
+def __dir__() -> list[str]:
+    return __all__
+
+
+Operator = Callable[[str, Union[str, AbstractSet[str]]], bool]
+EvaluateContext = Literal["metadata", "lock_file", "requirement"]
+"""A ``typing.Literal`` enumerating valid marker evaluation contexts.
+
+Valid values for the ``context`` passed to :meth:`Marker.evaluate` are:
+
+* ``"metadata"`` (for core metadata; default)
+* ``"lock_file"`` (for lock files)
+* ``"requirement"`` (i.e. all other situations)
+"""
+
+MARKERS_ALLOWING_SET = {"extras", "dependency_groups"}
+MARKERS_REQUIRING_VERSION = {
+    "implementation_version",
+    "platform_release",
+    "python_full_version",
+    "python_version",
+}
+
+
+class InvalidMarker(ValueError):
+    """Raised when attempting to create a :class:`Marker` from invalid input.
+
+    This error indicates that the given marker string does not conform to the
+    :ref:`specification of dependency specifiers `.
+    """
+
+
+class UndefinedComparison(ValueError):
+    """Raised when evaluating an unsupported marker comparison.
+
+    This can happen when marker values are compared as versions but do not
+    conform to the :ref:`specification of version specifiers
+    `.
+    """
+
+
+class UndefinedEnvironmentName(ValueError):
+    """Raised when evaluating a marker that references a missing environment key."""
+
+
+class Environment(TypedDict):
+    """
+    A dictionary that represents a Python environment as captured by
+    :func:`default_environment`. All fields are required.
+    """
+
+    implementation_name: str
+    """The implementation's identifier, e.g. ``'cpython'``."""
+
+    implementation_version: str
+    """
+    The implementation's version, e.g. ``'3.13.0a2'`` for CPython 3.13.0a2, or
+    ``'7.3.13'`` for PyPy3.10 v7.3.13.
+    """
+
+    os_name: str
+    """
+    The value of :py:data:`os.name`. The name of the operating system dependent module
+    imported, e.g. ``'posix'``.
+    """
+
+    platform_machine: str
+    """
+    Returns the machine type, e.g. ``'i386'``.
+
+    An empty string if the value cannot be determined.
+    """
+
+    platform_release: str
+    """
+    The system's release, e.g. ``'2.2.0'`` or ``'NT'``.
+
+    An empty string if the value cannot be determined.
+    """
+
+    platform_system: str
+    """
+    The system/OS name, e.g. ``'Linux'``, ``'Windows'`` or ``'Java'``.
+
+    An empty string if the value cannot be determined.
+    """
+
+    platform_version: str
+    """
+    The system's release version, e.g. ``'#3 on degas'``.
+
+    An empty string if the value cannot be determined.
+    """
+
+    python_full_version: str
+    """
+    The Python version as string ``'major.minor.patchlevel'``.
+
+    Note that unlike the Python :py:data:`sys.version`, this value will always include
+    the patchlevel (it defaults to 0).
+    """
+
+    platform_python_implementation: str
+    """
+    A string identifying the Python implementation, e.g. ``'CPython'``.
+    """
+
+    python_version: str
+    """The Python version as string ``'major.minor'``."""
+
+    sys_platform: str
+    """
+    This string contains a platform identifier that can be used to append
+    platform-specific components to :py:data:`sys.path`, for instance.
+
+    For Unix systems, except on Linux and AIX, this is the lowercased OS name as
+    returned by ``uname -s`` with the first part of the version as returned by
+    ``uname -r`` appended, e.g. ``'sunos5'`` or ``'freebsd8'``, at the time when Python
+    was built.
+    """
+
+
+def _normalize_extras(
+    result: MarkerList | MarkerAtom | str,
+) -> MarkerList | MarkerAtom | str:
+    if not isinstance(result, tuple):
+        return result
+
+    lhs, op, rhs = result
+    if isinstance(lhs, Variable) and lhs.value == "extra":
+        normalized_extra = canonicalize_name(rhs.value)
+        rhs = Value(normalized_extra)
+    elif isinstance(rhs, Variable) and rhs.value == "extra":
+        normalized_extra = canonicalize_name(lhs.value)
+        lhs = Value(normalized_extra)
+    return lhs, op, rhs
+
+
+def _normalize_extra_values(results: MarkerList) -> MarkerList:
+    """
+    Normalize extra values.
+    """
+
+    return [_normalize_extras(r) for r in results]
+
+
+def _format_marker(
+    marker: list[str] | MarkerAtom | str, first: bool | None = True
+) -> str:
+    assert isinstance(marker, (list, tuple, str))
+
+    # Sometimes we have a structure like [[...]] which is a single item list
+    # where the single item is itself it's own list. In that case we want skip
+    # the rest of this function so that we don't get extraneous () on the
+    # outside.
+    if (
+        isinstance(marker, list)
+        and len(marker) == 1
+        and isinstance(marker[0], (list, tuple))
+    ):
+        return _format_marker(marker[0])
+
+    if isinstance(marker, list):
+        inner = (_format_marker(m, first=False) for m in marker)
+        if first:
+            return " ".join(inner)
+        else:
+            return "(" + " ".join(inner) + ")"
+    elif isinstance(marker, tuple):
+        return " ".join([m.serialize() for m in marker])
+    else:
+        return marker
+
+
+_operators: dict[str, Operator] = {
+    "in": lambda lhs, rhs: lhs in rhs,
+    "not in": lambda lhs, rhs: lhs not in rhs,
+    "<": lambda _lhs, _rhs: False,
+    "<=": operator.eq,
+    "==": operator.eq,
+    "!=": operator.ne,
+    ">=": operator.eq,
+    ">": lambda _lhs, _rhs: False,
+}
+
+
+def _eval_op(lhs: str, op: Op, rhs: str | AbstractSet[str], *, key: str) -> bool:
+    op_str = op.serialize()
+    if key in MARKERS_REQUIRING_VERSION:
+        try:
+            spec = Specifier(f"{op_str}{rhs}")
+        except InvalidSpecifier:
+            pass
+        else:
+            return spec.contains(lhs, prereleases=True)
+
+    oper: Operator | None = _operators.get(op_str)
+    if oper is None:
+        raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.")
+
+    return oper(lhs, rhs)
+
+
+def _normalize(
+    lhs: str, rhs: str | AbstractSet[str], key: str
+) -> tuple[str, str | AbstractSet[str]]:
+    # PEP 685 - Comparison of extra names for optional distribution dependencies
+    # https://peps.python.org/pep-0685/
+    # > When comparing extra names, tools MUST normalize the names being
+    # > compared using the semantics outlined in PEP 503 for names
+    if key == "extra":
+        assert isinstance(rhs, str), "extra value must be a string"
+        # Both sides are normalized at this point already
+        return (lhs, rhs)
+    if key in MARKERS_ALLOWING_SET:
+        if isinstance(rhs, str):  # pragma: no cover
+            return (canonicalize_name(lhs), canonicalize_name(rhs))
+        else:
+            return (canonicalize_name(lhs), {canonicalize_name(v) for v in rhs})
+
+    # other environment markers don't have such standards
+    return lhs, rhs
+
+
+def _evaluate_markers(
+    markers: MarkerList, environment: dict[str, str | AbstractSet[str]]
+) -> bool:
+    groups: list[list[bool]] = [[]]
+
+    for marker in markers:
+        if isinstance(marker, list):
+            groups[-1].append(_evaluate_markers(marker, environment))
+        elif isinstance(marker, tuple):
+            lhs, op, rhs = marker
+
+            if isinstance(lhs, Variable):
+                environment_key = lhs.value
+                lhs_value = environment[environment_key]
+                rhs_value = rhs.value
+            else:
+                lhs_value = lhs.value
+                environment_key = rhs.value
+                rhs_value = environment[environment_key]
+
+            assert isinstance(lhs_value, str), "lhs must be a string"
+            lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key)
+            groups[-1].append(_eval_op(lhs_value, op, rhs_value, key=environment_key))
+        elif marker == "or":
+            groups.append([])
+        elif marker == "and":
+            pass
+        else:  # pragma: nocover
+            raise TypeError(f"Unexpected marker {marker!r}")
+
+    return any(all(item) for item in groups)
+
+
+def _format_full_version(info: sys._version_info) -> str:
+    version = f"{info.major}.{info.minor}.{info.micro}"
+    kind = info.releaselevel
+    if kind != "final":
+        version += kind[0] + str(info.serial)
+    return version
+
+
+def default_environment() -> Environment:
+    """Return the default marker environment for the current Python process.
+
+    This is the base environment used by :meth:`Marker.evaluate`.
+    """
+    iver = _format_full_version(sys.implementation.version)
+    implementation_name = sys.implementation.name
+    return {
+        "implementation_name": implementation_name,
+        "implementation_version": iver,
+        "os_name": os.name,
+        "platform_machine": platform.machine(),
+        "platform_release": platform.release(),
+        "platform_system": platform.system(),
+        "platform_version": platform.version(),
+        "python_full_version": platform.python_version(),
+        "platform_python_implementation": platform.python_implementation(),
+        "python_version": ".".join(platform.python_version_tuple()[:2]),
+        "sys_platform": sys.platform,
+    }
+
+
+class Marker:
+    """Represents a parsed dependency marker expression.
+
+    Marker expressions are parsed according to the
+    :ref:`specification of dependency specifiers `.
+
+    :param marker: The string representation of a marker expression.
+    :raises InvalidMarker: If ``marker`` cannot be parsed.
+
+    Instances are safe to serialize with :mod:`pickle`. They use a stable
+    format so the same pickle can be loaded in future packaging releases.
+
+    .. versionchanged:: 26.2
+
+        Added a stable pickle format. Pickles created with packaging 26.2+ can
+        be unpickled with future releases.  Backward compatibility with pickles
+        from pip._vendor.packaging < 26.2 is supported but may be removed in a future
+        release.
+    """
+
+    __slots__ = ("_markers",)
+
+    def __init__(self, marker: str) -> None:
+        # Note: We create a Marker object without calling this constructor in
+        #       packaging.requirements.Requirement. If any additional logic is
+        #       added here, make sure to mirror/adapt Requirement.
+
+        # If this fails and throws an error, the repr still expects _markers to
+        # be defined.
+        self._markers: MarkerList = []
+
+        try:
+            self._markers = _normalize_extra_values(_parse_marker(marker))
+            # The attribute `_markers` can be described in terms of a recursive type:
+            # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]]
+            #
+            # For example, the following expression:
+            # python_version > "3.6" or (python_version == "3.6" and os_name == "unix")
+            #
+            # is parsed into:
+            # [
+            #     (, ')>, ),
+            #     'and',
+            #     [
+            #         (, , ),
+            #         'or',
+            #         (, , )
+            #     ]
+            # ]
+        except ParserSyntaxError as e:
+            raise InvalidMarker(str(e)) from e
+
+    @classmethod
+    def _from_markers(cls, markers: MarkerList) -> Marker:
+        """Create a Marker instance from a pre-parsed marker tree.
+
+        This avoids re-parsing serialised marker strings when combining markers.
+        """
+        new = cls.__new__(cls)
+        new._markers = markers
+        return new
+
+    def __str__(self) -> str:
+        return _format_marker(self._markers)
+
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__}({str(self)!r})>"
+
+    def __hash__(self) -> int:
+        return hash(str(self))
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, Marker):
+            return NotImplemented
+
+        return str(self) == str(other)
+
+    def __getstate__(self) -> str:
+        # Return the marker expression string for compactness and stability.
+        # Internal Node objects are excluded; the string is re-parsed on load.
+        return str(self)
+
+    def __setstate__(self, state: object) -> None:
+        if isinstance(state, str):
+            # New format (26.2+): just the marker expression string.
+            try:
+                self._markers = _normalize_extra_values(_parse_marker(state))
+            except ParserSyntaxError as exc:
+                raise TypeError(f"Cannot restore Marker from {state!r}") from exc
+            return
+        if isinstance(state, dict) and "_markers" in state:
+            # Old format (packaging <= 26.1, no __slots__): plain __dict__.
+            markers = state["_markers"]
+            if isinstance(markers, list):
+                self._markers = markers
+                return
+        if isinstance(state, tuple) and len(state) == 2:
+            # Old format (packaging <= 26.1, __slots__): (None, {slot: value}).
+            _, slot_dict = state
+            if isinstance(slot_dict, dict) and "_markers" in slot_dict:
+                markers = slot_dict["_markers"]
+                if isinstance(markers, list):
+                    self._markers = markers
+                    return
+        raise TypeError(f"Cannot restore Marker from {state!r}")
+
+    def __and__(self, other: Marker) -> Marker:
+        if not isinstance(other, Marker):
+            return NotImplemented
+        return self._from_markers([self._markers, "and", other._markers])
+
+    def __or__(self, other: Marker) -> Marker:
+        if not isinstance(other, Marker):
+            return NotImplemented
+        return self._from_markers([self._markers, "or", other._markers])
+
+    def evaluate(
+        self,
+        environment: Mapping[str, str | AbstractSet[str]] | None = None,
+        context: EvaluateContext = "metadata",
+    ) -> bool:
+        """Evaluate a marker.
+
+        Return the boolean from evaluating this marker against the environment.
+        The environment is determined from the current Python process unless
+        passed in explicitly.
+
+        :param environment: Mapping containing keys and values to override the
+           detected environment.
+        :param EvaluateContext context: The context in which the marker is
+            evaluated, which influences what marker names are considered valid.
+            Accepted values are ``"metadata"`` (for core metadata; default),
+            ``"lock_file"``, and ``"requirement"`` (i.e. all other situations).
+        :raises UndefinedComparison: If the marker uses a comparison on values
+            that are not valid versions per the :ref:`specification of version
+            specifiers `.
+        :raises UndefinedEnvironmentName: If the marker references a value that
+            is missing from the evaluation environment.
+        :returns: ``True`` if the marker matches, otherwise ``False``.
+
+        """
+        current_environment = cast(
+            "dict[str, str | AbstractSet[str]]", default_environment()
+        )
+        if context == "lock_file":
+            current_environment.update(
+                extras=frozenset(), dependency_groups=frozenset()
+            )
+        elif context == "metadata":
+            current_environment["extra"] = ""
+
+        if environment is not None:
+            current_environment.update(environment)
+            if "extra" in current_environment:
+                # The API used to allow setting extra to None. We need to handle
+                # this case for backwards compatibility. Also skip running
+                # normalize name if extra is empty.
+                extra = cast("str | None", current_environment["extra"])
+                current_environment["extra"] = canonicalize_name(extra) if extra else ""
+
+        return _evaluate_markers(
+            self._markers, _repair_python_full_version(current_environment)
+        )
+
+
+def _repair_python_full_version(
+    env: dict[str, str | AbstractSet[str]],
+) -> dict[str, str | AbstractSet[str]]:
+    """
+    Work around platform.python_version() returning something that is not PEP 440
+    compliant for non-tagged Python builds.
+    """
+    python_full_version = cast("str", env["python_full_version"])
+    if python_full_version.endswith("+"):
+        env["python_full_version"] = f"{python_full_version}local"
+    return env
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/metadata.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/metadata.py
new file mode 100644
index 0000000..dccb627
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/metadata.py
@@ -0,0 +1,964 @@
+from __future__ import annotations
+
+import email.header
+import email.message
+import email.parser
+import email.policy
+import keyword
+import pathlib
+import typing
+from typing import (
+    Any,
+    Callable,
+    Generic,
+    Literal,
+    TypedDict,
+    cast,
+)
+
+from . import licenses, requirements, specifiers, utils
+from . import version as version_module
+from .errors import ExceptionGroup, _ErrorCollector
+
+if typing.TYPE_CHECKING:
+    from .licenses import NormalizedLicenseExpression
+
+T = typing.TypeVar("T")
+
+
+__all__ = [
+    "ExceptionGroup",  # Keep this for a bit (makes mypy happy w/ 26.0 compat)
+    "InvalidMetadata",
+    "Metadata",
+    "RFC822Message",
+    "RFC822Policy",
+    "RawMetadata",
+    "parse_email",
+]
+
+
+def __dir__() -> list[str]:
+    return __all__
+
+
+class InvalidMetadata(ValueError):
+    """A metadata field contains invalid data."""
+
+    field: str
+    """The name of the field that contains invalid data."""
+
+    def __init__(self, field: str, message: str) -> None:
+        self.field = field
+        super().__init__(message)
+
+
+# The RawMetadata class attempts to make as few assumptions about the underlying
+# serialization formats as possible. The idea is that as long as a serialization
+# formats offer some very basic primitives in *some* way then we can support
+# serializing to and from that format.
+class RawMetadata(TypedDict, total=False):
+    """A dictionary of raw core metadata.
+
+    Each field in core metadata maps to a key of this dictionary (when data is
+    provided). The key is lower-case and underscores are used instead of dashes
+    compared to the equivalent core metadata field. Any core metadata field that
+    can be specified multiple times or can hold multiple values in a single
+    field have a key with a plural name. See :class:`Metadata` whose attributes
+    match the keys of this dictionary.
+
+    Core metadata fields that can be specified multiple times are stored as a
+    list or dict depending on which is appropriate for the field. Any fields
+    which hold multiple values in a single field are stored as a list. All fields
+    are considered optional.
+    """
+
+    # Metadata 1.0 - PEP 241
+    metadata_version: str
+    name: str
+    version: str
+    platforms: list[str]
+    summary: str
+    description: str
+    keywords: list[str]
+    home_page: str
+    author: str
+    author_email: str
+    license: str
+
+    # Metadata 1.1 - PEP 314
+    supported_platforms: list[str]
+    download_url: str
+    classifiers: list[str]
+    requires: list[str]
+    provides: list[str]
+    obsoletes: list[str]
+
+    # Metadata 1.2 - PEP 345
+    maintainer: str
+    maintainer_email: str
+    requires_dist: list[str]
+    provides_dist: list[str]
+    obsoletes_dist: list[str]
+    requires_python: str
+    requires_external: list[str]
+    project_urls: dict[str, str]
+
+    # Metadata 2.0
+    # PEP 426 attempted to completely revamp the metadata format
+    # but got stuck without ever being able to build consensus on
+    # it and ultimately ended up withdrawn.
+    #
+    # However, a number of tools had started emitting METADATA with
+    # `2.0` Metadata-Version, so for historical reasons, this version
+    # was skipped.
+
+    # Metadata 2.1 - PEP 566
+    description_content_type: str
+    provides_extra: list[str]
+
+    # Metadata 2.2 - PEP 643
+    dynamic: list[str]
+
+    # Metadata 2.3 - PEP 685
+    # No new fields were added in PEP 685, just some edge case were
+    # tightened up to provide better interoperability.
+
+    # Metadata 2.4 - PEP 639
+    license_expression: str
+    license_files: list[str]
+
+    # Metadata 2.5 - PEP 794
+    import_names: list[str]
+    import_namespaces: list[str]
+
+
+# 'keywords' is special as it's a string in the core metadata spec, but we
+# represent it as a list.
+_STRING_FIELDS = {
+    "author",
+    "author_email",
+    "description",
+    "description_content_type",
+    "download_url",
+    "home_page",
+    "license",
+    "license_expression",
+    "maintainer",
+    "maintainer_email",
+    "metadata_version",
+    "name",
+    "requires_python",
+    "summary",
+    "version",
+}
+
+_LIST_FIELDS = {
+    "classifiers",
+    "dynamic",
+    "license_files",
+    "obsoletes",
+    "obsoletes_dist",
+    "platforms",
+    "provides",
+    "provides_dist",
+    "provides_extra",
+    "requires",
+    "requires_dist",
+    "requires_external",
+    "supported_platforms",
+    "import_names",
+    "import_namespaces",
+}
+
+_DICT_FIELDS = {
+    "project_urls",
+}
+
+
+def _parse_keywords(data: str) -> list[str]:
+    """Split a string of comma-separated keywords into a list of keywords."""
+    return [k.strip() for k in data.split(",")]
+
+
+def _parse_project_urls(data: list[str]) -> dict[str, str]:
+    """Parse a list of label/URL string pairings separated by a comma."""
+    urls = {}
+    for pair in data:
+        # Our logic is slightly tricky here as we want to try and do
+        # *something* reasonable with malformed data.
+        #
+        # The main thing that we have to worry about, is data that does
+        # not have a ',' at all to split the label from the Value. There
+        # isn't a singular right answer here, and we will fail validation
+        # later on (if the caller is validating) so it doesn't *really*
+        # matter, but since the missing value has to be an empty str
+        # and our return value is dict[str, str], if we let the key
+        # be the missing value, then they'd have multiple '' values that
+        # overwrite each other in a accumulating dict.
+        #
+        # The other potential issue is that it's possible to have the
+        # same label multiple times in the metadata, with no solid "right"
+        # answer with what to do in that case. As such, we'll do the only
+        # thing we can, which is treat the field as unparsable and add it
+        # to our list of unparsed fields.
+        #
+        # TODO: The spec doesn't say anything about if the keys should be
+        #       considered case sensitive or not... logically they should
+        #       be case-preserving and case-insensitive, but doing that
+        #       would open up more cases where we might have duplicate
+        #       entries.
+        label, _, url = (s.strip() for s in pair.partition(","))
+
+        if label in urls:
+            # The label already exists in our set of urls, so this field
+            # is unparsable, and we can just add the whole thing to our
+            # unparsable data and stop processing it.
+            raise KeyError("duplicate labels in project urls")
+        urls[label] = url
+
+    return urls
+
+
+def _get_payload(msg: email.message.Message, source: bytes | str) -> str:
+    """Get the body of the message."""
+    # If our source is a str, then our caller has managed encodings for us,
+    # and we don't need to deal with it.
+    if isinstance(source, str):
+        payload = msg.get_payload()
+        assert isinstance(payload, str)
+        return payload
+    # If our source is a bytes, then we're managing the encoding and we need
+    # to deal with it.
+    else:
+        bpayload = msg.get_payload(decode=True)
+        assert isinstance(bpayload, bytes)
+        try:
+            return bpayload.decode("utf8", "strict")
+        except UnicodeDecodeError as exc:
+            raise ValueError("payload in an invalid encoding") from exc
+
+
+# The various parse_FORMAT functions here are intended to be as lenient as
+# possible in their parsing, while still returning a correctly typed
+# RawMetadata.
+#
+# To aid in this, we also generally want to do as little touching of the
+# data as possible, except where there are possibly some historic holdovers
+# that make valid data awkward to work with.
+#
+# While this is a lower level, intermediate format than our ``Metadata``
+# class, some light touch ups can make a massive difference in usability.
+
+# Map METADATA fields to RawMetadata.
+_EMAIL_TO_RAW_MAPPING = {
+    "author": "author",
+    "author-email": "author_email",
+    "classifier": "classifiers",
+    "description": "description",
+    "description-content-type": "description_content_type",
+    "download-url": "download_url",
+    "dynamic": "dynamic",
+    "home-page": "home_page",
+    "import-name": "import_names",
+    "import-namespace": "import_namespaces",
+    "keywords": "keywords",
+    "license": "license",
+    "license-expression": "license_expression",
+    "license-file": "license_files",
+    "maintainer": "maintainer",
+    "maintainer-email": "maintainer_email",
+    "metadata-version": "metadata_version",
+    "name": "name",
+    "obsoletes": "obsoletes",
+    "obsoletes-dist": "obsoletes_dist",
+    "platform": "platforms",
+    "project-url": "project_urls",
+    "provides": "provides",
+    "provides-dist": "provides_dist",
+    "provides-extra": "provides_extra",
+    "requires": "requires",
+    "requires-dist": "requires_dist",
+    "requires-external": "requires_external",
+    "requires-python": "requires_python",
+    "summary": "summary",
+    "supported-platform": "supported_platforms",
+    "version": "version",
+}
+_RAW_TO_EMAIL_MAPPING = {raw: email for email, raw in _EMAIL_TO_RAW_MAPPING.items()}
+
+
+# This class is for writing RFC822 messages
+class RFC822Policy(email.policy.EmailPolicy):
+    """
+    This is :class:`email.policy.EmailPolicy`, but with a simple ``header_store_parse``
+    implementation that handles multi-line values, and some nice defaults.
+    """
+
+    utf8 = True
+    mangle_from_ = False
+    max_line_length = 0
+
+    def header_store_parse(self, name: str, value: str) -> tuple[str, str]:
+        size = len(name) + 2
+        value = value.replace("\n", "\n" + " " * size)
+        return (name, value)
+
+
+# This class is for writing RFC822 messages
+class RFC822Message(email.message.EmailMessage):
+    """
+    This is :class:`email.message.EmailMessage` with two small changes: it defaults to
+    our `RFC822Policy`, and it correctly writes unicode when being called
+    with `bytes()`.
+    """
+
+    def __init__(self) -> None:
+        super().__init__(policy=RFC822Policy())
+
+    def as_bytes(
+        self, unixfrom: bool = False, policy: email.policy.Policy | None = None
+    ) -> bytes:
+        """
+        Return the bytes representation of the message.
+
+        This handles unicode encoding.
+        """
+        return self.as_string(unixfrom, policy=policy).encode("utf-8")
+
+
+def parse_email(data: bytes | str) -> tuple[RawMetadata, dict[str, list[str]]]:
+    """Parse a distribution's metadata stored as email headers (e.g. from ``METADATA``).
+
+    This function returns a two-item tuple of dicts. The first dict is of
+    recognized fields from the core metadata specification. Fields that can be
+    parsed and translated into Python's built-in types are converted
+    appropriately. All other fields are left as-is. Fields that are allowed to
+    appear multiple times are stored as lists.
+
+    The second dict contains all other fields from the metadata. This includes
+    any unrecognized fields. It also includes any fields which are expected to
+    be parsed into a built-in type but were not formatted appropriately. Finally,
+    any fields that are expected to appear only once but are repeated are
+    included in this dict.
+
+    """
+    raw: dict[str, str | list[str] | dict[str, str]] = {}
+    unparsed: dict[str, list[str]] = {}
+
+    if isinstance(data, str):
+        parsed = email.parser.Parser(policy=email.policy.compat32).parsestr(data)
+    else:
+        parsed = email.parser.BytesParser(policy=email.policy.compat32).parsebytes(data)
+
+    # We have to wrap parsed.keys() in a set, because in the case of multiple
+    # values for a key (a list), the key will appear multiple times in the
+    # list of keys, but we're avoiding that by using get_all().
+    for name_with_case in frozenset(parsed.keys()):
+        # Header names in RFC are case insensitive, so we'll normalize to all
+        # lower case to make comparisons easier.
+        name = name_with_case.lower()
+
+        # We use get_all() here, even for fields that aren't multiple use,
+        # because otherwise someone could have e.g. two Name fields, and we
+        # would just silently ignore it rather than doing something about it.
+        headers = parsed.get_all(name) or []
+
+        # The way the email module works when parsing bytes is that it
+        # unconditionally decodes the bytes as ascii using the surrogateescape
+        # handler. When you pull that data back out (such as with get_all() ),
+        # it looks to see if the str has any surrogate escapes, and if it does
+        # it wraps it in a Header object instead of returning the string.
+        #
+        # As such, we'll look for those Header objects, and fix up the encoding.
+        value = []
+        # Flag if we have run into any issues processing the headers, thus
+        # signalling that the data belongs in 'unparsed'.
+        valid_encoding = True
+        for h in headers:
+            # It's unclear if this can return more types than just a Header or
+            # a str, so we'll just assert here to make sure.
+            assert isinstance(h, (email.header.Header, str))
+
+            # If it's a header object, we need to do our little dance to get
+            # the real data out of it. In cases where there is invalid data
+            # we're going to end up with mojibake, but there's no obvious, good
+            # way around that without reimplementing parts of the Header object
+            # ourselves.
+            #
+            # That should be fine since, if mojibacked happens, this key is
+            # going into the unparsed dict anyways.
+            if isinstance(h, email.header.Header):
+                # The Header object stores it's data as chunks, and each chunk
+                # can be independently encoded, so we'll need to check each
+                # of them.
+                chunks: list[tuple[bytes, str | None]] = []
+                for binary, _encoding in email.header.decode_header(h):
+                    try:
+                        binary.decode("utf8", "strict")
+                    except UnicodeDecodeError:
+                        # Enable mojibake.
+                        encoding = "latin1"
+                        valid_encoding = False
+                    else:
+                        encoding = "utf8"
+                    chunks.append((binary, encoding))
+
+                # Turn our chunks back into a Header object, then let that
+                # Header object do the right thing to turn them into a
+                # string for us.
+                value.append(str(email.header.make_header(chunks)))
+            # This is already a string, so just add it.
+            else:
+                value.append(h)
+
+        # We've processed all of our values to get them into a list of str,
+        # but we may have mojibake data, in which case this is an unparsed
+        # field.
+        if not valid_encoding:
+            unparsed[name] = value
+            continue
+
+        raw_name = _EMAIL_TO_RAW_MAPPING.get(name)
+        if raw_name is None:
+            # This is a bit of a weird situation, we've encountered a key that
+            # we don't know what it means, so we don't know whether it's meant
+            # to be a list or not.
+            #
+            # Since we can't really tell one way or another, we'll just leave it
+            # as a list, even though it may be a single item list, because that's
+            # what makes the most sense for email headers.
+            unparsed[name] = value
+            continue
+
+        # If this is one of our string fields, then we'll check to see if our
+        # value is a list of a single item. If it is then we'll assume that
+        # it was emitted as a single string, and unwrap the str from inside
+        # the list.
+        #
+        # If it's any other kind of data, then we haven't the faintest clue
+        # what we should parse it as, and we have to just add it to our list
+        # of unparsed stuff.
+        if raw_name in _STRING_FIELDS and len(value) == 1:
+            raw[raw_name] = value[0]
+        # If this is import_names, we need to special case the empty field
+        # case, which converts to an empty list instead of None. We can't let
+        # the empty case slip through, as it will fail validation.
+        elif raw_name == "import_names" and value == [""]:
+            raw[raw_name] = []
+        # If this is one of our list of string fields, then we can just assign
+        # the value, since email *only* has strings, and our get_all() call
+        # above ensures that this is a list.
+        elif raw_name in _LIST_FIELDS:
+            raw[raw_name] = value
+        # Special Case: Keywords
+        # The keywords field is implemented in the metadata spec as a str,
+        # but it conceptually is a list of strings, and is serialized using
+        # ", ".join(keywords), so we'll do some light data massaging to turn
+        # this into what it logically is.
+        elif raw_name == "keywords" and len(value) == 1:
+            raw[raw_name] = _parse_keywords(value[0])
+        # Special Case: Project-URL
+        # The project urls is implemented in the metadata spec as a list of
+        # specially-formatted strings that represent a key and a value, which
+        # is fundamentally a mapping, however the email format doesn't support
+        # mappings in a sane way, so it was crammed into a list of strings
+        # instead.
+        #
+        # We will do a little light data massaging to turn this into a map as
+        # it logically should be.
+        elif raw_name == "project_urls":
+            try:
+                raw[raw_name] = _parse_project_urls(value)
+            except KeyError:
+                unparsed[name] = value
+        # Nothing that we've done has managed to parse this, so it'll just
+        # throw it in our unparsable data and move on.
+        else:
+            unparsed[name] = value
+
+    # We need to support getting the Description from the message payload in
+    # addition to getting it from the the headers. This does mean, though, there
+    # is the possibility of it being set both ways, in which case we put both
+    # in 'unparsed' since we don't know which is right.
+    try:
+        payload = _get_payload(parsed, data)
+    except ValueError:
+        unparsed.setdefault("description", []).append(
+            parsed.get_payload(decode=isinstance(data, bytes))  # type: ignore[call-overload]
+        )
+    else:
+        if payload:
+            # Check to see if we've already got a description, if so then both
+            # it, and this body move to unparsable.
+            if "description" in raw:
+                description_header = cast("str", raw.pop("description"))
+                unparsed.setdefault("description", []).extend(
+                    [description_header, payload]
+                )
+            elif "description" in unparsed:
+                unparsed["description"].append(payload)
+            else:
+                raw["description"] = payload
+
+    # We need to cast our `raw` to a metadata, because a TypedDict only support
+    # literal key names, but we're computing our key names on purpose, but the
+    # way this function is implemented, our `TypedDict` can only have valid key
+    # names.
+    return cast("RawMetadata", raw), unparsed
+
+
+_NOT_FOUND = object()
+
+
+# Keep the two values in sync.
+_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4", "2.5"]
+_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4", "2.5"]
+
+_REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"])
+
+
+class _Validator(Generic[T]):
+    """Validate a metadata field.
+
+    All _process_*() methods correspond to a core metadata field. The method is
+    called with the field's raw value. If the raw value is valid it is returned
+    in its "enriched" form (e.g. ``version.Version`` for the ``Version`` field).
+    If the raw value is invalid, :exc:`InvalidMetadata` is raised (with a cause
+    as appropriate).
+    """
+
+    name: str
+    raw_name: str
+    added: _MetadataVersion
+
+    def __init__(
+        self,
+        *,
+        added: _MetadataVersion = "1.0",
+    ) -> None:
+        self.added = added
+
+    def __set_name__(self, _owner: Metadata, name: str) -> None:
+        self.name = name
+        self.raw_name = _RAW_TO_EMAIL_MAPPING[name]
+
+    def __get__(self, instance: Metadata, _owner: type[Metadata]) -> T:
+        # With Python 3.8, the caching can be replaced with functools.cached_property().
+        # No need to check the cache as attribute lookup will resolve into the
+        # instance's __dict__ before __get__ is called.
+        cache = instance.__dict__
+        value = instance._raw.get(self.name)
+
+        # To make the _process_* methods easier, we'll check if the value is None
+        # and if this field is NOT a required attribute, and if both of those
+        # things are true, we'll skip the the converter. This will mean that the
+        # converters never have to deal with the None union.
+        if self.name in _REQUIRED_ATTRS or value is not None:
+            try:
+                converter: Callable[[Any], T] = getattr(self, f"_process_{self.name}")
+            except AttributeError:
+                pass
+            else:
+                value = converter(value)
+
+        cache[self.name] = value
+        try:
+            del instance._raw[self.name]  # type: ignore[misc]
+        except KeyError:
+            pass
+
+        return cast("T", value)
+
+    def _invalid_metadata(
+        self, msg: str, cause: Exception | None = None
+    ) -> InvalidMetadata:
+        exc = InvalidMetadata(
+            self.raw_name, msg.format_map({"field": repr(self.raw_name)})
+        )
+        exc.__cause__ = cause
+        return exc
+
+    def _process_metadata_version(self, value: str) -> _MetadataVersion:
+        # Implicitly makes Metadata-Version required.
+        if value not in _VALID_METADATA_VERSIONS:
+            raise self._invalid_metadata(f"{value!r} is not a valid metadata version")
+        return cast("_MetadataVersion", value)
+
+    def _process_name(self, value: str) -> str:
+        if not value:
+            raise self._invalid_metadata("{field} is a required field")
+        # Validate the name as a side-effect.
+        try:
+            utils.canonicalize_name(value, validate=True)
+        except utils.InvalidName as exc:
+            raise self._invalid_metadata(
+                f"{value!r} is invalid for {{field}}", cause=exc
+            ) from exc
+        else:
+            return value
+
+    def _process_version(self, value: str) -> version_module.Version:
+        if not value:
+            raise self._invalid_metadata("{field} is a required field")
+        try:
+            return version_module.parse(value)
+        except version_module.InvalidVersion as exc:
+            raise self._invalid_metadata(
+                f"{value!r} is invalid for {{field}}", cause=exc
+            ) from exc
+
+    def _process_summary(self, value: str) -> str:
+        """Check the field contains no newlines."""
+        if "\n" in value:
+            raise self._invalid_metadata("{field} must be a single line")
+        return value
+
+    def _process_description_content_type(self, value: str) -> str:
+        content_types = {"text/plain", "text/x-rst", "text/markdown"}
+        message = email.message.EmailMessage()
+        message["content-type"] = value
+
+        content_type, parameters = (
+            # Defaults to `text/plain` if parsing failed.
+            message.get_content_type().lower(),
+            message["content-type"].params,
+        )
+        # Check if content-type is valid or defaulted to `text/plain` and thus was
+        # not parseable.
+        if content_type not in content_types or content_type not in value.lower():
+            raise self._invalid_metadata(
+                f"{{field}} must be one of {list(content_types)}, not {value!r}"
+            )
+
+        charset = parameters.get("charset", "UTF-8")
+        if charset != "UTF-8":
+            raise self._invalid_metadata(
+                f"{{field}} can only specify the UTF-8 charset, not {charset!r}"
+            )
+
+        markdown_variants = {"GFM", "CommonMark"}
+        variant = parameters.get("variant", "GFM")  # Use an acceptable default.
+        if content_type == "text/markdown" and variant not in markdown_variants:
+            raise self._invalid_metadata(
+                f"valid Markdown variants for {{field}} are {list(markdown_variants)}, "
+                f"not {variant!r}",
+            )
+        return value
+
+    def _process_dynamic(self, value: list[str]) -> list[str]:
+        for dynamic_field in map(str.lower, value):
+            if dynamic_field in {"name", "version", "metadata-version"}:
+                raise self._invalid_metadata(
+                    f"{dynamic_field!r} is not allowed as a dynamic field"
+                )
+            elif dynamic_field not in _EMAIL_TO_RAW_MAPPING:
+                raise self._invalid_metadata(
+                    f"{dynamic_field!r} is not a valid dynamic field"
+                )
+        return list(map(str.lower, value))
+
+    def _process_provides_extra(
+        self,
+        value: list[str],
+    ) -> list[utils.NormalizedName]:
+        normalized_names = []
+        try:
+            for name in value:
+                normalized_names.append(utils.canonicalize_name(name, validate=True))
+        except utils.InvalidName as exc:
+            raise self._invalid_metadata(
+                f"{name!r} is invalid for {{field}}", cause=exc
+            ) from exc
+        else:
+            return normalized_names
+
+    def _process_requires_python(self, value: str) -> specifiers.SpecifierSet:
+        try:
+            return specifiers.SpecifierSet(value)
+        except specifiers.InvalidSpecifier as exc:
+            raise self._invalid_metadata(
+                f"{value!r} is invalid for {{field}}", cause=exc
+            ) from exc
+
+    def _process_requires_dist(
+        self,
+        value: list[str],
+    ) -> list[requirements.Requirement]:
+        reqs = []
+        try:
+            for req in value:
+                reqs.append(requirements.Requirement(req))
+        except requirements.InvalidRequirement as exc:
+            raise self._invalid_metadata(
+                f"{req!r} is invalid for {{field}}", cause=exc
+            ) from exc
+        else:
+            return reqs
+
+    def _process_license_expression(self, value: str) -> NormalizedLicenseExpression:
+        try:
+            return licenses.canonicalize_license_expression(value)
+        except ValueError as exc:
+            raise self._invalid_metadata(
+                f"{value!r} is invalid for {{field}}", cause=exc
+            ) from exc
+
+    def _process_license_files(self, value: list[str]) -> list[str]:
+        paths = []
+        for path in value:
+            if ".." in path:
+                raise self._invalid_metadata(
+                    f"{path!r} is invalid for {{field}}, "
+                    "parent directory indicators are not allowed"
+                )
+            if "*" in path:
+                raise self._invalid_metadata(
+                    f"{path!r} is invalid for {{field}}, paths must be resolved"
+                )
+            if (
+                pathlib.PurePosixPath(path).is_absolute()
+                or pathlib.PureWindowsPath(path).is_absolute()
+            ):
+                raise self._invalid_metadata(
+                    f"{path!r} is invalid for {{field}}, paths must be relative"
+                )
+            if pathlib.PureWindowsPath(path).as_posix() != path:
+                raise self._invalid_metadata(
+                    f"{path!r} is invalid for {{field}}, paths must use '/' delimiter"
+                )
+            paths.append(path)
+        return paths
+
+    def _process_import_names(self, value: list[str]) -> list[str]:
+        for import_name in value:
+            name, semicolon, private = import_name.partition(";")
+            name = name.rstrip()
+            for identifier in name.split("."):
+                if not identifier.isidentifier():
+                    raise self._invalid_metadata(
+                        f"{name!r} is invalid for {{field}}; "
+                        f"{identifier!r} is not a valid identifier"
+                    )
+                elif keyword.iskeyword(identifier):
+                    raise self._invalid_metadata(
+                        f"{name!r} is invalid for {{field}}; "
+                        f"{identifier!r} is a keyword"
+                    )
+            if semicolon and private.lstrip() != "private":
+                raise self._invalid_metadata(
+                    f"{import_name!r} is invalid for {{field}}; "
+                    "the only valid option is 'private'"
+                )
+        return value
+
+    _process_import_namespaces = _process_import_names
+
+
+class Metadata:
+    """Representation of distribution metadata.
+
+    Compared to :class:`RawMetadata`, this class provides objects representing
+    metadata fields instead of only using built-in types. Any invalid metadata
+    will cause :exc:`InvalidMetadata` to be raised (with a
+    :py:attr:`~BaseException.__cause__` attribute as appropriate).
+    """
+
+    _raw: RawMetadata
+
+    @classmethod
+    def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> Metadata:
+        """Create an instance from :class:`RawMetadata`.
+
+        If *validate* is true, all metadata will be validated. All exceptions
+        related to validation will be gathered and raised as an :class:`ExceptionGroup`.
+        """
+        ins = cls()
+        ins._raw = data.copy()  # Mutations occur due to caching enriched values.
+
+        if validate:
+            collector = _ErrorCollector()
+            metadata_version = None
+            with collector.collect(InvalidMetadata):
+                metadata_version = ins.metadata_version
+                metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version)
+
+            # Make sure to check for the fields that are present, the required
+            # fields (so their absence can be reported).
+            fields_to_check = frozenset(ins._raw) | _REQUIRED_ATTRS
+            # Remove fields that have already been checked.
+            fields_to_check -= {"metadata_version"}
+
+            for key in fields_to_check:
+                try:
+                    if metadata_version:
+                        # Can't use getattr() as that triggers descriptor protocol which
+                        # will fail due to no value for the instance argument.
+                        try:
+                            field_metadata_version = cls.__dict__[key].added
+                        except KeyError:
+                            exc = InvalidMetadata(key, f"unrecognized field: {key!r}")
+                            collector.error(exc)
+                            continue
+                        field_age = _VALID_METADATA_VERSIONS.index(
+                            field_metadata_version
+                        )
+                        if field_age > metadata_age:
+                            field = _RAW_TO_EMAIL_MAPPING[key]
+                            exc = InvalidMetadata(
+                                field,
+                                f"{field} introduced in metadata version "
+                                f"{field_metadata_version}, not {metadata_version}",
+                            )
+                            collector.error(exc)
+                            continue
+                    getattr(ins, key)
+                except InvalidMetadata as exc:
+                    collector.error(exc)
+
+            collector.finalize("invalid metadata")
+
+        return ins
+
+    @classmethod
+    def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata:
+        """Parse metadata from email headers.
+
+        If *validate* is true, the metadata will be validated. All exceptions
+        related to validation will be gathered and raised as an :class:`ExceptionGroup`.
+        """
+        raw, unparsed = parse_email(data)
+
+        if validate:
+            with _ErrorCollector().on_exit("unparsed") as collector:
+                for unparsed_key in unparsed:
+                    if unparsed_key in _EMAIL_TO_RAW_MAPPING:
+                        message = f"{unparsed_key!r} has invalid data"
+                    else:
+                        message = f"unrecognized field: {unparsed_key!r}"
+                    collector.error(InvalidMetadata(unparsed_key, message))
+
+        try:
+            return cls.from_raw(raw, validate=validate)
+        except ExceptionGroup as exc_group:
+            raise ExceptionGroup(
+                "invalid or unparsed metadata", exc_group.exceptions
+            ) from None
+
+    metadata_version: _Validator[_MetadataVersion] = _Validator()
+    """:external:ref:`core-metadata-metadata-version`
+    (required; validated to be a valid metadata version)"""
+    # `name` is not normalized/typed to NormalizedName so as to provide access to
+    # the original/raw name.
+    name: _Validator[str] = _Validator()
+    """:external:ref:`core-metadata-name`
+    (required; validated using :func:`~packaging.utils.canonicalize_name` and its
+    *validate* parameter)"""
+    version: _Validator[version_module.Version] = _Validator()
+    """:external:ref:`core-metadata-version` (required)"""
+    dynamic: _Validator[list[str] | None] = _Validator(
+        added="2.2",
+    )
+    """:external:ref:`core-metadata-dynamic`
+    (validated against core metadata field names and lowercased)"""
+    platforms: _Validator[list[str] | None] = _Validator()
+    """:external:ref:`core-metadata-platform`"""
+    supported_platforms: _Validator[list[str] | None] = _Validator(added="1.1")
+    """:external:ref:`core-metadata-supported-platform`"""
+    summary: _Validator[str | None] = _Validator()
+    """:external:ref:`core-metadata-summary` (validated to contain no newlines)"""
+    description: _Validator[str | None] = _Validator()  # TODO 2.1: can be in body
+    """:external:ref:`core-metadata-description`"""
+    description_content_type: _Validator[str | None] = _Validator(added="2.1")
+    """:external:ref:`core-metadata-description-content-type` (validated)"""
+    keywords: _Validator[list[str] | None] = _Validator()
+    """:external:ref:`core-metadata-keywords`"""
+    home_page: _Validator[str | None] = _Validator()
+    """:external:ref:`core-metadata-home-page`"""
+    download_url: _Validator[str | None] = _Validator(added="1.1")
+    """:external:ref:`core-metadata-download-url`"""
+    author: _Validator[str | None] = _Validator()
+    """:external:ref:`core-metadata-author`"""
+    author_email: _Validator[str | None] = _Validator()
+    """:external:ref:`core-metadata-author-email`"""
+    maintainer: _Validator[str | None] = _Validator(added="1.2")
+    """:external:ref:`core-metadata-maintainer`"""
+    maintainer_email: _Validator[str | None] = _Validator(added="1.2")
+    """:external:ref:`core-metadata-maintainer-email`"""
+    license: _Validator[str | None] = _Validator()
+    """:external:ref:`core-metadata-license`"""
+    license_expression: _Validator[NormalizedLicenseExpression | None] = _Validator(
+        added="2.4"
+    )
+    """:external:ref:`core-metadata-license-expression`"""
+    license_files: _Validator[list[str] | None] = _Validator(added="2.4")
+    """:external:ref:`core-metadata-license-file`"""
+    classifiers: _Validator[list[str] | None] = _Validator(added="1.1")
+    """:external:ref:`core-metadata-classifier`"""
+    requires_dist: _Validator[list[requirements.Requirement] | None] = _Validator(
+        added="1.2"
+    )
+    """:external:ref:`core-metadata-requires-dist`"""
+    requires_python: _Validator[specifiers.SpecifierSet | None] = _Validator(
+        added="1.2"
+    )
+    """:external:ref:`core-metadata-requires-python`"""
+    # Because `Requires-External` allows for non-PEP 440 version specifiers, we
+    # don't do any processing on the values.
+    requires_external: _Validator[list[str] | None] = _Validator(added="1.2")
+    """:external:ref:`core-metadata-requires-external`"""
+    project_urls: _Validator[dict[str, str] | None] = _Validator(added="1.2")
+    """:external:ref:`core-metadata-project-url`"""
+    # PEP 685 lets us raise an error if an extra doesn't pass `Name` validation
+    # regardless of metadata version.
+    provides_extra: _Validator[list[utils.NormalizedName] | None] = _Validator(
+        added="2.1",
+    )
+    """:external:ref:`core-metadata-provides-extra`"""
+    provides_dist: _Validator[list[str] | None] = _Validator(added="1.2")
+    """:external:ref:`core-metadata-provides-dist`"""
+    obsoletes_dist: _Validator[list[str] | None] = _Validator(added="1.2")
+    """:external:ref:`core-metadata-obsoletes-dist`"""
+    import_names: _Validator[list[str] | None] = _Validator(added="2.5")
+    """:external:ref:`core-metadata-import-name`"""
+    import_namespaces: _Validator[list[str] | None] = _Validator(added="2.5")
+    """:external:ref:`core-metadata-import-namespace`"""
+    requires: _Validator[list[str] | None] = _Validator(added="1.1")
+    """``Requires`` (deprecated)"""
+    provides: _Validator[list[str] | None] = _Validator(added="1.1")
+    """``Provides`` (deprecated)"""
+    obsoletes: _Validator[list[str] | None] = _Validator(added="1.1")
+    """``Obsoletes`` (deprecated)"""
+
+    def as_rfc822(self) -> RFC822Message:
+        """
+        Return an RFC822 message with the metadata.
+        """
+        message = RFC822Message()
+        self._write_metadata(message)
+        return message
+
+    def _write_metadata(self, message: RFC822Message) -> None:
+        """
+        Return an RFC822 message with the metadata.
+        """
+        for name, validator in self.__class__.__dict__.items():
+            if isinstance(validator, _Validator) and name != "description":
+                value = getattr(self, name)
+                email_name = _RAW_TO_EMAIL_MAPPING[name]
+                if value is not None:
+                    if email_name == "project-url":
+                        for label, url in value.items():
+                            message[email_name] = f"{label}, {url}"
+                    elif email_name == "keywords":
+                        message[email_name] = ",".join(value)
+                    elif email_name == "import-name" and value == []:
+                        message[email_name] = ""
+                    elif isinstance(value, list):
+                        for item in value:
+                            message[email_name] = str(item)
+                    else:
+                        message[email_name] = str(value)
+
+        # The description is a special case because it is in the body of the message.
+        if self.description is not None:
+            message.set_payload(self.description)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/pylock.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/pylock.py
new file mode 100644
index 0000000..84e2537
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/pylock.py
@@ -0,0 +1,905 @@
+from __future__ import annotations
+
+import dataclasses
+import logging
+import re
+from collections.abc import Mapping, Sequence
+from dataclasses import dataclass
+from datetime import datetime
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    Protocol,
+    TypeVar,
+    cast,
+)
+from urllib.parse import urlparse
+
+from .markers import Environment, Marker, default_environment
+from .specifiers import SpecifierSet
+from .tags import create_compatible_tags_selector, sys_tags
+from .utils import (
+    NormalizedName,
+    is_normalized_name,
+    parse_sdist_filename,
+    parse_wheel_filename,
+)
+from .version import Version
+
+if TYPE_CHECKING:  # pragma: no cover
+    from collections.abc import Collection, Iterator
+    from pathlib import Path
+
+    from typing_extensions import Self
+
+    from .tags import Tag
+
+_logger = logging.getLogger(__name__)
+
+__all__ = [
+    "Package",
+    "PackageArchive",
+    "PackageDirectory",
+    "PackageSdist",
+    "PackageVcs",
+    "PackageWheel",
+    "Pylock",
+    "PylockUnsupportedVersionError",
+    "PylockValidationError",
+    "is_valid_pylock_path",
+]
+
+
+def __dir__() -> list[str]:
+    return __all__
+
+
+_T = TypeVar("_T")
+_T2 = TypeVar("_T2")
+
+
+class _FromMappingProtocol(Protocol):  # pragma: no cover
+    @classmethod
+    def _from_dict(cls, d: Mapping[str, Any]) -> Self: ...
+
+
+_FromMappingProtocolT = TypeVar("_FromMappingProtocolT", bound=_FromMappingProtocol)
+
+
+_PYLOCK_FILE_NAME_RE = re.compile(r"^pylock\.([^.]+)\.toml$")
+
+
+def is_valid_pylock_path(path: Path) -> bool:
+    """Check if the given path is a valid pylock file path."""
+    return path.name == "pylock.toml" or bool(_PYLOCK_FILE_NAME_RE.match(path.name))
+
+
+def _toml_key(key: str) -> str:
+    return key.replace("_", "-")
+
+
+def _toml_value(key: str, value: Any) -> Any:  # noqa: ANN401
+    if isinstance(value, (Version, Marker, SpecifierSet)):
+        return str(value)
+    if isinstance(value, Sequence) and key == "environments":
+        return [str(v) for v in value]
+    return value
+
+
+def _toml_dict_factory(data: list[tuple[str, Any]]) -> dict[str, Any]:
+    return {
+        _toml_key(key): _toml_value(key, value)
+        for key, value in data
+        if value is not None
+    }
+
+
+def _get(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T | None:
+    """Get a value from the dictionary and verify it's the expected type."""
+    if (value := d.get(key)) is None:
+        return None
+    if not isinstance(value, expected_type):
+        raise PylockValidationError(
+            f"Unexpected type {type(value).__name__} "
+            f"(expected {expected_type.__name__})",
+            context=key,
+        )
+    return value
+
+
+def _get_required(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T:
+    """Get a required value from the dictionary and verify it's the expected type."""
+    if (value := _get(d, expected_type, key)) is None:
+        raise _PylockRequiredKeyError(key)
+    return value
+
+
+def _get_sequence(
+    d: Mapping[str, Any], expected_item_type: type[_T], key: str
+) -> Sequence[_T] | None:
+    """Get a list value from the dictionary and verify it's the expected items type."""
+    if (value := _get(d, Sequence, key)) is None:  # type: ignore[type-abstract]
+        return None
+    if isinstance(value, (str, bytes)):
+        # special case: str and bytes are Sequences, but we want to reject it
+        raise PylockValidationError(
+            f"Unexpected type {type(value).__name__} (expected Sequence)",
+            context=key,
+        )
+    for i, item in enumerate(value):
+        if not isinstance(item, expected_item_type):
+            raise PylockValidationError(
+                f"Unexpected type {type(item).__name__} "
+                f"(expected {expected_item_type.__name__})",
+                context=f"{key}[{i}]",
+            )
+    return value
+
+
+def _get_as(
+    d: Mapping[str, Any],
+    expected_type: type[_T],
+    target_type: Callable[[_T], _T2],
+    key: str,
+) -> _T2 | None:
+    """Get a value from the dictionary, verify it's the expected type,
+    and convert to the target type.
+
+    This assumes the target_type constructor accepts the value.
+    """
+    if (value := _get(d, expected_type, key)) is None:
+        return None
+    try:
+        return target_type(value)
+    except Exception as e:
+        raise PylockValidationError(e, context=key) from e
+
+
+def _get_required_as(
+    d: Mapping[str, Any],
+    expected_type: type[_T],
+    target_type: Callable[[_T], _T2],
+    key: str,
+) -> _T2:
+    """Get a required value from the dict, verify it's the expected type,
+    and convert to the target type."""
+    if (value := _get_as(d, expected_type, target_type, key)) is None:
+        raise _PylockRequiredKeyError(key)
+    return value
+
+
+def _get_sequence_as(
+    d: Mapping[str, Any],
+    expected_item_type: type[_T],
+    target_item_type: Callable[[_T], _T2],
+    key: str,
+) -> list[_T2] | None:
+    """Get list value from dictionary and verify expected items type."""
+    if (value := _get_sequence(d, expected_item_type, key)) is None:
+        return None
+    result = []
+    try:
+        for item in value:
+            typed_item = target_item_type(item)
+            result.append(typed_item)
+    except Exception as e:
+        raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e
+    return result
+
+
+def _get_object(
+    d: Mapping[str, Any], target_type: type[_FromMappingProtocolT], key: str
+) -> _FromMappingProtocolT | None:
+    """Get a dictionary value from the dictionary and convert it to a dataclass."""
+    if (value := _get(d, Mapping, key)) is None:  # type: ignore[type-abstract]
+        return None
+    try:
+        return target_type._from_dict(value)
+    except Exception as e:
+        raise PylockValidationError(e, context=key) from e
+
+
+def _get_sequence_of_objects(
+    d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str
+) -> list[_FromMappingProtocolT] | None:
+    """Get a list value from the dictionary and convert its items to a dataclass."""
+    if (value := _get_sequence(d, Mapping, key)) is None:  # type: ignore[type-abstract]
+        return None
+    result: list[_FromMappingProtocolT] = []
+    try:
+        for item in value:
+            typed_item = target_item_type._from_dict(item)
+            result.append(typed_item)
+    except Exception as e:
+        raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e
+    return result
+
+
+def _get_required_sequence_of_objects(
+    d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str
+) -> Sequence[_FromMappingProtocolT]:
+    """Get a required list value from the dictionary and convert its items to a
+    dataclass."""
+    if (result := _get_sequence_of_objects(d, target_item_type, key)) is None:
+        raise _PylockRequiredKeyError(key)
+    return result
+
+
+def _validate_normalized_name(name: str) -> NormalizedName:
+    """Validate that a string is a NormalizedName."""
+    if not is_normalized_name(name):
+        raise PylockValidationError(f"Name {name!r} is not normalized")
+    return NormalizedName(name)
+
+
+def _validate_path_url(path: str | None, url: str | None) -> None:
+    if not path and not url:
+        raise PylockValidationError("path or url must be provided")
+
+
+def _path_name(path: str | None) -> str | None:
+    if not path:
+        return None
+    # If the path is relative it MAY use POSIX-style path separators explicitly
+    # for portability
+    if "/" in path:
+        return path.rsplit("/", 1)[-1]
+    elif "\\" in path:
+        return path.rsplit("\\", 1)[-1]
+    else:
+        return path
+
+
+def _url_name(url: str | None) -> str | None:
+    if not url:
+        return None
+    url_path = urlparse(url).path
+    return url_path.rsplit("/", 1)[-1]
+
+
+def _validate_hashes(hashes: Mapping[str, Any]) -> Mapping[str, Any]:
+    if not hashes:
+        raise PylockValidationError("At least one hash must be provided")
+    if not all(isinstance(hash_val, str) for hash_val in hashes.values()):
+        raise PylockValidationError("Hash values must be strings")
+    return hashes
+
+
+class PylockValidationError(Exception):
+    """Raised when when input data is not spec-compliant."""
+
+    context: str | None = None
+    message: str
+
+    def __init__(
+        self,
+        cause: str | Exception,
+        *,
+        context: str | None = None,
+    ) -> None:
+        if isinstance(cause, PylockValidationError):
+            if cause.context:
+                self.context = (
+                    f"{context}.{cause.context}" if context else cause.context
+                )
+            else:
+                self.context = context
+            self.message = cause.message
+        else:
+            self.context = context
+            self.message = str(cause)
+
+    def __str__(self) -> str:
+        if self.context:
+            return f"{self.message} in {self.context!r}"
+        return self.message
+
+
+class _PylockRequiredKeyError(PylockValidationError):
+    def __init__(self, key: str) -> None:
+        super().__init__("Missing required value", context=key)
+
+
+class PylockUnsupportedVersionError(PylockValidationError):
+    """Raised when encountering an unsupported `lock_version`."""
+
+
+class PylockSelectError(Exception):
+    """Base exception for errors raised by :meth:`Pylock.select`."""
+
+
+@dataclass(frozen=True, init=False)
+class PackageVcs:
+    type: str
+    url: str | None = None
+    path: str | None = None
+    requested_revision: str | None = None
+    commit_id: str  # type: ignore[misc]
+    subdirectory: str | None = None
+
+    def __init__(
+        self,
+        *,
+        type: str,
+        url: str | None = None,
+        path: str | None = None,
+        requested_revision: str | None = None,
+        commit_id: str,
+        subdirectory: str | None = None,
+    ) -> None:
+        # In Python 3.10+ make dataclass kw_only=True and remove __init__
+        object.__setattr__(self, "type", type)
+        object.__setattr__(self, "url", url)
+        object.__setattr__(self, "path", path)
+        object.__setattr__(self, "requested_revision", requested_revision)
+        object.__setattr__(self, "commit_id", commit_id)
+        object.__setattr__(self, "subdirectory", subdirectory)
+
+    @classmethod
+    def _from_dict(cls, d: Mapping[str, Any]) -> Self:
+        package_vcs = cls(
+            type=_get_required(d, str, "type"),
+            url=_get(d, str, "url"),
+            path=_get(d, str, "path"),
+            requested_revision=_get(d, str, "requested-revision"),
+            commit_id=_get_required(d, str, "commit-id"),
+            subdirectory=_get(d, str, "subdirectory"),
+        )
+        _validate_path_url(package_vcs.path, package_vcs.url)
+        return package_vcs
+
+
+@dataclass(frozen=True, init=False)
+class PackageDirectory:
+    path: str
+    editable: bool | None = None
+    subdirectory: str | None = None
+
+    def __init__(
+        self,
+        *,
+        path: str,
+        editable: bool | None = None,
+        subdirectory: str | None = None,
+    ) -> None:
+        # In Python 3.10+ make dataclass kw_only=True and remove __init__
+        object.__setattr__(self, "path", path)
+        object.__setattr__(self, "editable", editable)
+        object.__setattr__(self, "subdirectory", subdirectory)
+
+    @classmethod
+    def _from_dict(cls, d: Mapping[str, Any]) -> Self:
+        return cls(
+            path=_get_required(d, str, "path"),
+            editable=_get(d, bool, "editable"),
+            subdirectory=_get(d, str, "subdirectory"),
+        )
+
+
+@dataclass(frozen=True, init=False)
+class PackageArchive:
+    url: str | None = None
+    path: str | None = None
+    size: int | None = None
+    upload_time: datetime | None = None
+    hashes: Mapping[str, str]  # type: ignore[misc]
+    subdirectory: str | None = None
+
+    def __init__(
+        self,
+        *,
+        url: str | None = None,
+        path: str | None = None,
+        size: int | None = None,
+        upload_time: datetime | None = None,
+        hashes: Mapping[str, str],
+        subdirectory: str | None = None,
+    ) -> None:
+        # In Python 3.10+ make dataclass kw_only=True and remove __init__
+        object.__setattr__(self, "url", url)
+        object.__setattr__(self, "path", path)
+        object.__setattr__(self, "size", size)
+        object.__setattr__(self, "upload_time", upload_time)
+        object.__setattr__(self, "hashes", hashes)
+        object.__setattr__(self, "subdirectory", subdirectory)
+
+    @classmethod
+    def _from_dict(cls, d: Mapping[str, Any]) -> Self:
+        package_archive = cls(
+            url=_get(d, str, "url"),
+            path=_get(d, str, "path"),
+            size=_get(d, int, "size"),
+            upload_time=_get(d, datetime, "upload-time"),
+            hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"),  # type: ignore[type-abstract]
+            subdirectory=_get(d, str, "subdirectory"),
+        )
+        _validate_path_url(package_archive.path, package_archive.url)
+        return package_archive
+
+
+@dataclass(frozen=True, init=False)
+class PackageSdist:
+    name: str | None = None
+    upload_time: datetime | None = None
+    url: str | None = None
+    path: str | None = None
+    size: int | None = None
+    hashes: Mapping[str, str]  # type: ignore[misc]
+
+    def __init__(
+        self,
+        *,
+        name: str | None = None,
+        upload_time: datetime | None = None,
+        url: str | None = None,
+        path: str | None = None,
+        size: int | None = None,
+        hashes: Mapping[str, str],
+    ) -> None:
+        # In Python 3.10+ make dataclass kw_only=True and remove __init__
+        object.__setattr__(self, "name", name)
+        object.__setattr__(self, "upload_time", upload_time)
+        object.__setattr__(self, "url", url)
+        object.__setattr__(self, "path", path)
+        object.__setattr__(self, "size", size)
+        object.__setattr__(self, "hashes", hashes)
+
+    @classmethod
+    def _from_dict(cls, d: Mapping[str, Any]) -> Self:
+        package_sdist = cls(
+            name=_get(d, str, "name"),
+            upload_time=_get(d, datetime, "upload-time"),
+            url=_get(d, str, "url"),
+            path=_get(d, str, "path"),
+            size=_get(d, int, "size"),
+            hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"),  # type: ignore[type-abstract]
+        )
+        _validate_path_url(package_sdist.path, package_sdist.url)
+        return package_sdist
+
+    @property
+    def filename(self) -> str:
+        """Get the filename of the sdist."""
+        filename = self.name or _path_name(self.path) or _url_name(self.url)
+        if not filename:
+            raise PylockValidationError("Cannot determine sdist filename")
+        return filename
+
+
+@dataclass(frozen=True, init=False)
+class PackageWheel:
+    name: str | None = None
+    upload_time: datetime | None = None
+    url: str | None = None
+    path: str | None = None
+    size: int | None = None
+    hashes: Mapping[str, str]  # type: ignore[misc]
+
+    def __init__(
+        self,
+        *,
+        name: str | None = None,
+        upload_time: datetime | None = None,
+        url: str | None = None,
+        path: str | None = None,
+        size: int | None = None,
+        hashes: Mapping[str, str],
+    ) -> None:
+        # In Python 3.10+ make dataclass kw_only=True and remove __init__
+        object.__setattr__(self, "name", name)
+        object.__setattr__(self, "upload_time", upload_time)
+        object.__setattr__(self, "url", url)
+        object.__setattr__(self, "path", path)
+        object.__setattr__(self, "size", size)
+        object.__setattr__(self, "hashes", hashes)
+
+    @classmethod
+    def _from_dict(cls, d: Mapping[str, Any]) -> Self:
+        package_wheel = cls(
+            name=_get(d, str, "name"),
+            upload_time=_get(d, datetime, "upload-time"),
+            url=_get(d, str, "url"),
+            path=_get(d, str, "path"),
+            size=_get(d, int, "size"),
+            hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"),  # type: ignore[type-abstract]
+        )
+        _validate_path_url(package_wheel.path, package_wheel.url)
+        return package_wheel
+
+    @property
+    def filename(self) -> str:
+        """Get the filename of the wheel."""
+        filename = self.name or _path_name(self.path) or _url_name(self.url)
+        if not filename:
+            raise PylockValidationError("Cannot determine wheel filename")
+        return filename
+
+
+@dataclass(frozen=True, init=False)
+class Package:
+    name: NormalizedName
+    version: Version | None = None
+    marker: Marker | None = None
+    requires_python: SpecifierSet | None = None
+    dependencies: Sequence[Mapping[str, Any]] | None = None
+    vcs: PackageVcs | None = None
+    directory: PackageDirectory | None = None
+    archive: PackageArchive | None = None
+    index: str | None = None
+    sdist: PackageSdist | None = None
+    wheels: Sequence[PackageWheel] | None = None
+    attestation_identities: Sequence[Mapping[str, Any]] | None = None
+    tool: Mapping[str, Any] | None = None
+
+    def __init__(
+        self,
+        *,
+        name: NormalizedName,
+        version: Version | None = None,
+        marker: Marker | None = None,
+        requires_python: SpecifierSet | None = None,
+        dependencies: Sequence[Mapping[str, Any]] | None = None,
+        vcs: PackageVcs | None = None,
+        directory: PackageDirectory | None = None,
+        archive: PackageArchive | None = None,
+        index: str | None = None,
+        sdist: PackageSdist | None = None,
+        wheels: Sequence[PackageWheel] | None = None,
+        attestation_identities: Sequence[Mapping[str, Any]] | None = None,
+        tool: Mapping[str, Any] | None = None,
+    ) -> None:
+        # In Python 3.10+ make dataclass kw_only=True and remove __init__
+        object.__setattr__(self, "name", name)
+        object.__setattr__(self, "version", version)
+        object.__setattr__(self, "marker", marker)
+        object.__setattr__(self, "requires_python", requires_python)
+        object.__setattr__(self, "dependencies", dependencies)
+        object.__setattr__(self, "vcs", vcs)
+        object.__setattr__(self, "directory", directory)
+        object.__setattr__(self, "archive", archive)
+        object.__setattr__(self, "index", index)
+        object.__setattr__(self, "sdist", sdist)
+        object.__setattr__(self, "wheels", wheels)
+        object.__setattr__(self, "attestation_identities", attestation_identities)
+        object.__setattr__(self, "tool", tool)
+
+    @classmethod
+    def _from_dict(cls, d: Mapping[str, Any]) -> Self:
+        package = cls(
+            name=_get_required_as(d, str, _validate_normalized_name, "name"),
+            version=_get_as(d, str, Version, "version"),
+            requires_python=_get_as(d, str, SpecifierSet, "requires-python"),
+            dependencies=_get_sequence(d, Mapping, "dependencies"),  # type: ignore[type-abstract]
+            marker=_get_as(d, str, Marker, "marker"),
+            vcs=_get_object(d, PackageVcs, "vcs"),
+            directory=_get_object(d, PackageDirectory, "directory"),
+            archive=_get_object(d, PackageArchive, "archive"),
+            index=_get(d, str, "index"),
+            sdist=_get_object(d, PackageSdist, "sdist"),
+            wheels=_get_sequence_of_objects(d, PackageWheel, "wheels"),
+            attestation_identities=_get_sequence(d, Mapping, "attestation-identities"),  # type: ignore[type-abstract]
+            tool=_get(d, Mapping, "tool"),  # type: ignore[type-abstract]
+        )
+        distributions = bool(package.sdist) + len(package.wheels or [])
+        direct_urls = (
+            bool(package.vcs) + bool(package.directory) + bool(package.archive)
+        )
+        if distributions > 0 and direct_urls > 0:
+            raise PylockValidationError(
+                "None of vcs, directory, archive must be set if sdist or wheels are set"
+            )
+        if distributions == 0 and direct_urls != 1:
+            raise PylockValidationError(
+                "Exactly one of vcs, directory, archive must be set "
+                "if sdist and wheels are not set"
+            )
+        for i, wheel in enumerate(package.wheels or []):
+            try:
+                (name, version, _, _) = parse_wheel_filename(wheel.filename)
+            except Exception as e:
+                raise PylockValidationError(
+                    f"Invalid wheel filename {wheel.filename!r}",
+                    context=f"wheels[{i}]",
+                ) from e
+            if name != package.name:
+                raise PylockValidationError(
+                    f"Name in {wheel.filename!r} is not consistent with "
+                    f"package name {package.name!r}",
+                    context=f"wheels[{i}]",
+                )
+            if package.version and version != package.version:
+                raise PylockValidationError(
+                    f"Version in {wheel.filename!r} is not consistent with "
+                    f"package version {str(package.version)!r}",
+                    context=f"wheels[{i}]",
+                )
+        if package.sdist:
+            try:
+                name, version = parse_sdist_filename(package.sdist.filename)
+            except Exception as e:
+                raise PylockValidationError(
+                    f"Invalid sdist filename {package.sdist.filename!r}",
+                    context="sdist",
+                ) from e
+            if name != package.name:
+                raise PylockValidationError(
+                    f"Name in {package.sdist.filename!r} is not consistent with "
+                    f"package name {package.name!r}",
+                    context="sdist",
+                )
+            if package.version and version != package.version:
+                raise PylockValidationError(
+                    f"Version in {package.sdist.filename!r} is not consistent with "
+                    f"package version {str(package.version)!r}",
+                    context="sdist",
+                )
+        try:
+            for i, attestation_identity in enumerate(  # noqa: B007
+                package.attestation_identities or []
+            ):
+                _get_required(attestation_identity, str, "kind")
+        except Exception as e:
+            raise PylockValidationError(
+                e, context=f"attestation-identities[{i}]"
+            ) from e
+        return package
+
+    @property
+    def is_direct(self) -> bool:
+        return not (self.sdist or self.wheels)
+
+
+@dataclass(frozen=True, init=False)
+class Pylock:
+    """A class representing a pylock file."""
+
+    lock_version: Version
+    environments: Sequence[Marker] | None = None
+    requires_python: SpecifierSet | None = None
+    extras: Sequence[NormalizedName] | None = None
+    dependency_groups: Sequence[str] | None = None
+    default_groups: Sequence[str] | None = None
+    created_by: str  # type: ignore[misc]
+    packages: Sequence[Package]  # type: ignore[misc]
+    tool: Mapping[str, Any] | None = None
+
+    def __init__(
+        self,
+        *,
+        lock_version: Version,
+        environments: Sequence[Marker] | None = None,
+        requires_python: SpecifierSet | None = None,
+        extras: Sequence[NormalizedName] | None = None,
+        dependency_groups: Sequence[str] | None = None,
+        default_groups: Sequence[str] | None = None,
+        created_by: str,
+        packages: Sequence[Package],
+        tool: Mapping[str, Any] | None = None,
+    ) -> None:
+        # In Python 3.10+ make dataclass kw_only=True and remove __init__
+        object.__setattr__(self, "lock_version", lock_version)
+        object.__setattr__(self, "environments", environments)
+        object.__setattr__(self, "requires_python", requires_python)
+        object.__setattr__(self, "extras", extras)
+        object.__setattr__(self, "dependency_groups", dependency_groups)
+        object.__setattr__(self, "default_groups", default_groups)
+        object.__setattr__(self, "created_by", created_by)
+        object.__setattr__(self, "packages", packages)
+        object.__setattr__(self, "tool", tool)
+
+    @classmethod
+    def _from_dict(cls, d: Mapping[str, Any]) -> Self:
+        pylock = cls(
+            lock_version=_get_required_as(d, str, Version, "lock-version"),
+            environments=_get_sequence_as(d, str, Marker, "environments"),
+            extras=_get_sequence_as(d, str, _validate_normalized_name, "extras"),
+            dependency_groups=_get_sequence(d, str, "dependency-groups"),
+            default_groups=_get_sequence(d, str, "default-groups"),
+            created_by=_get_required(d, str, "created-by"),
+            requires_python=_get_as(d, str, SpecifierSet, "requires-python"),
+            packages=_get_required_sequence_of_objects(d, Package, "packages"),
+            tool=_get(d, Mapping, "tool"),  # type: ignore[type-abstract]
+        )
+        if not Version("1") <= pylock.lock_version < Version("2"):
+            raise PylockUnsupportedVersionError(
+                f"pylock version {pylock.lock_version} is not supported"
+            )
+        if pylock.lock_version > Version("1.0"):
+            _logger.warning(
+                "pylock minor version %s is not supported", pylock.lock_version
+            )
+        return pylock
+
+    @classmethod
+    def from_dict(cls, d: Mapping[str, Any], /) -> Self:
+        """Create and validate a Pylock instance from a TOML dictionary.
+
+        Raises :class:`PylockValidationError` if the input data is not
+        spec-compliant.
+        """
+        return cls._from_dict(d)
+
+    def to_dict(self) -> Mapping[str, Any]:
+        """Convert the Pylock instance to a TOML dictionary."""
+        return dataclasses.asdict(self, dict_factory=_toml_dict_factory)
+
+    def validate(self) -> None:
+        """Validate the Pylock instance against the specification.
+
+        Raises :class:`PylockValidationError` otherwise."""
+        self.from_dict(self.to_dict())
+
+    def select(
+        self,
+        *,
+        environment: Environment | None = None,
+        tags: Sequence[Tag] | None = None,
+        extras: Collection[str] | None = None,
+        dependency_groups: Collection[str] | None = None,
+    ) -> Iterator[
+        tuple[
+            Package,
+            PackageVcs
+            | PackageDirectory
+            | PackageArchive
+            | PackageWheel
+            | PackageSdist,
+        ]
+    ]:
+        """Select what to install from the lock file.
+
+        The *environment* and *tags* parameters represent the environment being
+        selected for. If unspecified, ``packaging.markers.default_environment()`` and
+        ``packaging.tags.sys_tags()`` are used.
+
+        The *extras* parameter represents the extras to install.
+
+        The *dependency_groups* parameter represents the groups to install. If
+        unspecified, the default groups are used.
+
+        This method must be used on valid Pylock instances (i.e. one obtained
+        from :meth:`Pylock.from_dict` or if constructed manually, after calling
+        :meth:`Pylock.validate`).
+        """
+        compatible_tags_selector = create_compatible_tags_selector(tags or sys_tags())
+
+        # #. Gather the extras and dependency groups to install and set ``extras`` and
+        #    ``dependency_groups`` for marker evaluation, respectively.
+        #
+        #    #. ``extras`` SHOULD be set to the empty set by default.
+        #    #. ``dependency_groups`` SHOULD be the set created from
+        #       :ref:`pylock-default-groups` by default.
+        env = cast(
+            "dict[str, str | frozenset[str]]",
+            dict(
+                environment or {},  # Marker.evaluate will fill-up
+                extras=frozenset(extras or []),
+                dependency_groups=frozenset(
+                    (self.default_groups or [])
+                    if dependency_groups is None  # to allow selecting no group
+                    else dependency_groups
+                ),
+            ),
+        )
+        env_python_full_version = (
+            environment["python_full_version"]
+            if environment
+            else default_environment()["python_full_version"]
+        )
+
+        # #. Check if the metadata version specified by :ref:`pylock-lock-version` is
+        #    supported; an error or warning MUST be raised as appropriate.
+        # Covered by lock.validate() which is a precondition for this method.
+
+        # #. If :ref:`pylock-requires-python` is specified, check that the environment
+        #    being installed for meets the requirement; an error MUST be raised if it is
+        #    not met.
+        if self.requires_python and not self.requires_python.contains(
+            env_python_full_version,
+        ):
+            raise PylockSelectError(
+                f"python_full_version {env_python_full_version!r} "
+                f"in provided environment does not satisfy the Python version "
+                f"requirement {str(self.requires_python)!r}"
+            )
+
+        # #. If :ref:`pylock-environments` is specified, check that at least one of the
+        #    environment marker expressions is satisfied; an error MUST be raised if no
+        #    expression is satisfied.
+        if self.environments:
+            for env_marker in self.environments:
+                if env_marker.evaluate(
+                    cast("dict[str, str]", environment or {}), context="requirement"
+                ):
+                    break
+            else:
+                raise PylockSelectError(
+                    "Provided environment does not satisfy any of the "
+                    "environments specified in the lock file"
+                )
+
+        # #. For each package listed in :ref:`pylock-packages`:
+        selected_packages_by_name: dict[str, tuple[int, Package]] = {}
+        for package_index, package in enumerate(self.packages):
+            # #. If :ref:`pylock-packages-marker` is specified, check if it is
+            #    satisfied;if it isn't, skip to the next package.
+            if package.marker and not package.marker.evaluate(env, context="lock_file"):
+                continue
+
+            # #. If :ref:`pylock-packages-requires-python` is specified, check if it is
+            #    satisfied; an error MUST be raised if it isn't.
+            if package.requires_python and not package.requires_python.contains(
+                env_python_full_version,
+            ):
+                raise PylockSelectError(
+                    f"python_full_version {env_python_full_version!r} "
+                    f"in provided environment does not satisfy the Python version "
+                    f"requirement {str(package.requires_python)!r} for package "
+                    f"{package.name!r} at packages[{package_index}]"
+                )
+
+            # #. Check that no other conflicting instance of the package has been slated
+            #    to be installed; an error about the ambiguity MUST be raised otherwise.
+            if package.name in selected_packages_by_name:
+                raise PylockSelectError(
+                    f"Multiple packages with the name {package.name!r} are "
+                    f"selected at packages[{package_index}] and "
+                    f"packages[{selected_packages_by_name[package.name][0]}]"
+                )
+
+            # #. Check that the source of the package is specified appropriately (i.e.
+            #    there are no conflicting sources in the package entry);
+            #    an error MUST be raised if any issues are found.
+            # Covered by lock.validate() which is a precondition for this method.
+
+            # #. Add the package to the set of packages to install.
+            selected_packages_by_name[package.name] = (package_index, package)
+
+        # #. For each package to be installed:
+        for package_index, package in selected_packages_by_name.values():
+            # - If :ref:`pylock-packages-vcs` is set:
+            if package.vcs is not None:
+                yield package, package.vcs
+
+            # - Else if :ref:`pylock-packages-directory` is set:
+            elif package.directory is not None:
+                yield package, package.directory
+
+            # - Else if :ref:`pylock-packages-archive` is set:
+            elif package.archive is not None:
+                yield package, package.archive
+
+            # - Else if there are entries for :ref:`pylock-packages-wheels`:
+            elif package.wheels:
+                # #. Look for the appropriate wheel file based on
+                #    :ref:`pylock-packages-wheels-name`; if one is not found then move
+                #    on to :ref:`pylock-packages-sdist` or an error MUST be raised about
+                #    a lack of source for the project.
+                best_wheel = next(
+                    compatible_tags_selector(
+                        (wheel, parse_wheel_filename(wheel.filename)[-1])
+                        for wheel in package.wheels
+                    ),
+                    None,
+                )
+                if best_wheel:
+                    yield package, best_wheel
+                elif package.sdist is not None:
+                    yield package, package.sdist
+                else:
+                    raise PylockSelectError(
+                        f"No wheel found matching the provided tags "
+                        f"for package {package.name!r} "
+                        f"at packages[{package_index}], "
+                        f"and no sdist available as a fallback"
+                    )
+
+            # - Else if no :ref:`pylock-packages-wheels` file is found or
+            #   :ref:`pylock-packages-sdist` is solely set:
+            elif package.sdist is not None:
+                yield package, package.sdist
+
+            else:
+                # Covered by lock.validate() which is a precondition for this method.
+                raise NotImplementedError  # pragma: no cover
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/requirements.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/requirements.py
new file mode 100644
index 0000000..d5c786d
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/requirements.py
@@ -0,0 +1,129 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+from __future__ import annotations
+
+from typing import Iterator
+
+from ._parser import parse_requirement as _parse_requirement
+from ._tokenizer import ParserSyntaxError
+from .markers import Marker, _normalize_extra_values
+from .specifiers import SpecifierSet
+from .utils import canonicalize_name
+
+__all__ = [
+    "InvalidRequirement",
+    "Requirement",
+]
+
+
+def __dir__() -> list[str]:
+    return __all__
+
+
+class InvalidRequirement(ValueError):
+    """
+    An invalid requirement was found, users should refer to PEP 508.
+    """
+
+
+class Requirement:
+    """Parse a requirement.
+
+    Parse a given requirement string into its parts, such as name, specifier,
+    URL, and extras. Raises InvalidRequirement on a badly-formed requirement
+    string.
+
+    Instances are safe to serialize with :mod:`pickle`. They use a stable
+    format so the same pickle can be loaded in future packaging releases.
+
+    .. versionchanged:: 26.2
+
+        Added a stable pickle format. Pickles created with packaging 26.2+ can
+        be unpickled with future releases.  Backward compatibility with pickles
+        from pip._vendor.packaging < 26.2 is supported but may be removed in a future
+        release.
+    """
+
+    # TODO: Can we test whether something is contained within a requirement?
+    #       If so how do we do that? Do we need to test against the _name_ of
+    #       the thing as well as the version? What about the markers?
+    # TODO: Can we normalize the name and extra name?
+
+    def __init__(self, requirement_string: str) -> None:
+        try:
+            parsed = _parse_requirement(requirement_string)
+        except ParserSyntaxError as e:
+            raise InvalidRequirement(str(e)) from e
+
+        self.name: str = parsed.name
+        self.url: str | None = parsed.url or None
+        self.extras: set[str] = set(parsed.extras or [])
+        self.specifier: SpecifierSet = SpecifierSet(parsed.specifier)
+        self.marker: Marker | None = None
+        if parsed.marker is not None:
+            self.marker = Marker.__new__(Marker)
+            self.marker._markers = _normalize_extra_values(parsed.marker)
+
+    def _iter_parts(self, name: str) -> Iterator[str]:
+        yield name
+
+        if self.extras:
+            formatted_extras = ",".join(sorted(self.extras))
+            yield f"[{formatted_extras}]"
+
+        if self.specifier:
+            yield str(self.specifier)
+
+        if self.url:
+            yield f" @ {self.url}"
+            if self.marker:
+                yield " "
+
+        if self.marker:
+            yield f"; {self.marker}"
+
+    def __getstate__(self) -> str:
+        # Return the requirement string for compactness and stability.
+        # Re-parsed on load to reconstruct all fields.
+        return str(self)
+
+    def __setstate__(self, state: object) -> None:
+        if isinstance(state, str):
+            # New format (26.2+): just the requirement string.
+            try:
+                tmp = Requirement(state)
+            except InvalidRequirement as exc:
+                raise TypeError(f"Cannot restore Requirement from {state!r}") from exc
+            self.name = tmp.name
+            self.url = tmp.url
+            self.extras = tmp.extras
+            self.specifier = tmp.specifier
+            self.marker = tmp.marker
+            return
+        if isinstance(state, dict):
+            # Old format (packaging <= 26.1, no __slots__): plain __dict__.
+            self.__dict__.update(state)
+            return
+        raise TypeError(f"Cannot restore Requirement from {state!r}")
+
+    def __str__(self) -> str:
+        return "".join(self._iter_parts(self.name))
+
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__}({str(self)!r})>"
+
+    def __hash__(self) -> int:
+        return hash(tuple(self._iter_parts(canonicalize_name(self.name))))
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, Requirement):
+            return NotImplemented
+
+        return (
+            canonicalize_name(self.name) == canonicalize_name(other.name)
+            and self.extras == other.extras
+            and self.specifier == other.specifier
+            and self.url == other.url
+            and self.marker == other.marker
+        )
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/specifiers.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/specifiers.py
new file mode 100644
index 0000000..83e0015
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/specifiers.py
@@ -0,0 +1,1943 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+"""
+.. testsetup::
+
+    from pip._vendor.packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier
+    from pip._vendor.packaging.version import Version
+"""
+
+from __future__ import annotations
+
+import abc
+import enum
+import functools
+import itertools
+import re
+import sys
+import typing
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    Final,
+    Iterable,
+    Iterator,
+    Sequence,
+    TypeVar,
+    Union,
+)
+
+from .utils import canonicalize_version
+from .version import InvalidVersion, Version
+
+if sys.version_info >= (3, 10):
+    from typing import TypeGuard  # pragma: no cover
+elif TYPE_CHECKING:
+    from typing_extensions import TypeGuard
+
+__all__ = [
+    "BaseSpecifier",
+    "InvalidSpecifier",
+    "Specifier",
+    "SpecifierSet",
+]
+
+
+def __dir__() -> list[str]:
+    return __all__
+
+
+def _validate_spec(spec: object, /) -> TypeGuard[tuple[str, str]]:
+    return (
+        isinstance(spec, tuple)
+        and len(spec) == 2
+        and isinstance(spec[0], str)
+        and isinstance(spec[1], str)
+    )
+
+
+def _validate_pre(pre: object, /) -> TypeGuard[bool | None]:
+    return pre is None or isinstance(pre, bool)
+
+
+T = TypeVar("T")
+UnparsedVersion = Union[Version, str]
+UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion)
+CallableOperator = Callable[[Version, str], bool]
+
+# The smallest possible PEP 440 version. No valid version is less than this.
+_MIN_VERSION: Final[Version] = Version("0.dev0")
+
+
+def _trim_release(release: tuple[int, ...]) -> tuple[int, ...]:
+    """Strip trailing zeros from a release tuple for normalized comparison."""
+    end = len(release)
+    while end > 1 and release[end - 1] == 0:
+        end -= 1
+    return release if end == len(release) else release[:end]
+
+
+class _BoundaryKind(enum.Enum):
+    """Where a boundary marker sits in the version ordering."""
+
+    AFTER_LOCALS = enum.auto()  # after V+local, before V.post0
+    AFTER_POSTS = enum.auto()  # after V.postN, before next release
+
+
+@functools.total_ordering
+class _BoundaryVersion:
+    """A point on the version line between two real PEP 440 versions.
+
+    Some specifier semantics imply boundaries between real versions:
+    ``<=1.0`` includes ``1.0+local`` and ``>1.0`` excludes
+    ``1.0.post0``.  No real :class:`Version` falls on those boundaries,
+    so this class creates values that sort between the real versions
+    on either side.
+
+    Two kinds exist, shown relative to a base version V::
+
+        V < V+local < AFTER_LOCALS(V) < V.post0 < AFTER_POSTS(V)
+
+    ``AFTER_LOCALS`` sits after V and every V+local, but before
+    V.post0.  Upper bound of ``<=V``, ``==V``, ``!=V``.
+
+    ``AFTER_POSTS`` sits after every V.postN, but before the next
+    release segment.  Lower bound of ``>V`` (final or pre-release V)
+    to exclude post-releases per PEP 440.
+    """
+
+    __slots__ = ("_kind", "_trimmed_release", "version")
+
+    def __init__(self, version: Version, kind: _BoundaryKind) -> None:
+        self.version = version
+        self._kind = kind
+        self._trimmed_release = _trim_release(version.release)
+
+    def _is_family(self, other: Version) -> bool:
+        """Is ``other`` a version that this boundary sorts above?"""
+        v = self.version
+        if not (
+            other.epoch == v.epoch
+            and _trim_release(other.release) == self._trimmed_release
+            and other.pre == v.pre
+        ):
+            return False
+        if self._kind == _BoundaryKind.AFTER_LOCALS:
+            # Local family: exact same public version (any local label).
+            return other.post == v.post and other.dev == v.dev
+        # Post family: same base + any post-release (or identical).
+        return other.dev == v.dev or other.post is not None
+
+    def __eq__(self, other: object) -> bool:
+        if isinstance(other, _BoundaryVersion):
+            return self.version == other.version and self._kind == other._kind
+        return NotImplemented
+
+    def __lt__(self, other: _BoundaryVersion | Version) -> bool:
+        if isinstance(other, _BoundaryVersion):
+            if self.version != other.version:
+                return self.version < other.version
+            return self._kind.value < other._kind.value
+        return not self._is_family(other) and self.version < other
+
+    def __hash__(self) -> int:
+        return hash((self.version, self._kind))
+
+    def __repr__(self) -> str:
+        return f"{self.__class__.__name__}({self.version!r}, {self._kind.name})"
+
+
+@functools.total_ordering
+class _LowerBound:
+    """Lower bound of a version range.
+
+    A version *v* of ``None`` means unbounded below (-inf).
+    At equal versions, ``[v`` sorts before ``(v`` because an inclusive
+    bound starts earlier.
+    """
+
+    __slots__ = ("inclusive", "version")
+
+    def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None:
+        self.version = version
+        self.inclusive = inclusive
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, _LowerBound):
+            return NotImplemented  # pragma: no cover
+        return self.version == other.version and self.inclusive == other.inclusive
+
+    def __lt__(self, other: _LowerBound) -> bool:
+        if not isinstance(other, _LowerBound):  # pragma: no cover
+            return NotImplemented
+        # -inf < anything (except -inf).
+        if self.version is None:
+            return other.version is not None
+        if other.version is None:
+            return False
+        if self.version != other.version:
+            return self.version < other.version
+        # [v < (v: inclusive starts earlier.
+        return self.inclusive and not other.inclusive
+
+    def __hash__(self) -> int:
+        return hash((self.version, self.inclusive))
+
+    def __repr__(self) -> str:
+        bracket = "[" if self.inclusive else "("
+        return f"<{self.__class__.__name__} {bracket}{self.version!r}>"
+
+
+@functools.total_ordering
+class _UpperBound:
+    """Upper bound of a version range.
+
+    A version *v* of ``None`` means unbounded above (+inf).
+    At equal versions, ``v)`` sorts before ``v]`` because an exclusive
+    bound ends earlier.
+    """
+
+    __slots__ = ("inclusive", "version")
+
+    def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None:
+        self.version = version
+        self.inclusive = inclusive
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, _UpperBound):
+            return NotImplemented  # pragma: no cover
+        return self.version == other.version and self.inclusive == other.inclusive
+
+    def __lt__(self, other: _UpperBound) -> bool:
+        if not isinstance(other, _UpperBound):  # pragma: no cover
+            return NotImplemented
+        # Nothing < +inf (except +inf itself).
+        if self.version is None:
+            return False
+        if other.version is None:
+            return True
+        if self.version != other.version:
+            return self.version < other.version
+        # v) < v]: exclusive ends earlier.
+        return not self.inclusive and other.inclusive
+
+    def __hash__(self) -> int:
+        return hash((self.version, self.inclusive))
+
+    def __repr__(self) -> str:
+        bracket = "]" if self.inclusive else ")"
+        return f"<{self.__class__.__name__} {self.version!r}{bracket}>"
+
+
+if typing.TYPE_CHECKING:
+    _VersionOrBoundary = Union[Version, _BoundaryVersion, None]
+
+    #: A single contiguous version range, represented as a
+    #: (lower bound, upper bound) pair.
+    _VersionRange = tuple[_LowerBound, _UpperBound]
+
+_NEG_INF = _LowerBound(None, False)
+_POS_INF = _UpperBound(None, False)
+_FULL_RANGE: tuple[_VersionRange] = ((_NEG_INF, _POS_INF),)
+
+
+def _range_is_empty(lower: _LowerBound, upper: _UpperBound) -> bool:
+    """True when the range defined by *lower* and *upper* contains no versions."""
+    if lower.version is None or upper.version is None:
+        return False
+    if lower.version == upper.version:
+        return not (lower.inclusive and upper.inclusive)
+    return lower.version > upper.version
+
+
+def _intersect_ranges(
+    left: Sequence[_VersionRange],
+    right: Sequence[_VersionRange],
+) -> list[_VersionRange]:
+    """Intersect two sorted, non-overlapping range lists (two-pointer merge)."""
+    result: list[_VersionRange] = []
+    left_index = right_index = 0
+    while left_index < len(left) and right_index < len(right):
+        left_lower, left_upper = left[left_index]
+        right_lower, right_upper = right[right_index]
+
+        lower = max(left_lower, right_lower)
+        upper = min(left_upper, right_upper)
+
+        if not _range_is_empty(lower, upper):
+            result.append((lower, upper))
+
+        # Advance whichever side has the smaller upper bound.
+        if left_upper < right_upper:
+            left_index += 1
+        else:
+            right_index += 1
+
+    return result
+
+
+def _next_prefix_dev0(version: Version) -> Version:
+    """Smallest version in the next prefix: 1.2 -> 1.3.dev0."""
+    release = (*version.release[:-1], version.release[-1] + 1)
+    return Version.from_parts(epoch=version.epoch, release=release, dev=0)
+
+
+def _base_dev0(version: Version) -> Version:
+    """The .dev0 of a version's base release: 1.2 -> 1.2.dev0."""
+    return Version.from_parts(epoch=version.epoch, release=version.release, dev=0)
+
+
+def _coerce_version(version: UnparsedVersion) -> Version | None:
+    if not isinstance(version, Version):
+        try:
+            version = Version(version)
+        except InvalidVersion:
+            return None
+    return version
+
+
+def _public_version(version: Version) -> Version:
+    if version.local is None:
+        return version
+    return version.__replace__(local=None)
+
+
+def _post_base(version: Version) -> Version:
+    """The version that *version* is a post-release of.
+
+    1.0.post1 -> 1.0, 1.0a1.post0 -> 1.0a1, 1.0.post0.dev1 -> 1.0.
+    """
+    return version.__replace__(post=None, dev=None, local=None)
+
+
+def _earliest_prerelease(version: Version) -> Version:
+    """Earliest pre-release of *version*.
+
+    1.2 -> 1.2.dev0, 1.2.post1 -> 1.2.post1.dev0.
+    """
+    return version.__replace__(dev=0, local=None)
+
+
+def _nearest_non_prerelease(
+    v: _VersionOrBoundary,
+) -> Version | None:
+    """Smallest non-pre-release version at or above *v*, or None."""
+    if v is None:
+        return None
+    if isinstance(v, _BoundaryVersion):
+        inner = v.version
+        if inner.is_prerelease:
+            # AFTER_LOCALS(1.0a1) -> nearest non-pre is 1.0
+            return inner.__replace__(pre=None, dev=None, local=None)
+        # AFTER_LOCALS(1.0) -> nearest non-pre is 1.0.post0
+        # AFTER_LOCALS(1.0.post0) -> nearest non-pre is 1.0.post1
+        k = (inner.post + 1) if inner.post is not None else 0
+        return inner.__replace__(post=k, local=None)
+    if not v.is_prerelease:
+        return v
+    # Strip pre/dev to get the final or post-release form.
+    return v.__replace__(pre=None, dev=None, local=None)
+
+
+class InvalidSpecifier(ValueError):
+    """
+    Raised when attempting to create a :class:`Specifier` with a specifier
+    string that is invalid.
+
+    >>> Specifier("lolwat")
+    Traceback (most recent call last):
+        ...
+    packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat'
+    """
+
+
+class BaseSpecifier(metaclass=abc.ABCMeta):
+    __slots__ = ()
+    __match_args__ = ("_str",)
+
+    @property
+    def _str(self) -> str:
+        """Internal property for match_args"""
+        return str(self)
+
+    @abc.abstractmethod
+    def __str__(self) -> str:
+        """
+        Returns the str representation of this Specifier-like object. This
+        should be representative of the Specifier itself.
+        """
+
+    @abc.abstractmethod
+    def __hash__(self) -> int:
+        """
+        Returns a hash value for this Specifier-like object.
+        """
+
+    @abc.abstractmethod
+    def __eq__(self, other: object) -> bool:
+        """
+        Returns a boolean representing whether or not the two Specifier-like
+        objects are equal.
+
+        :param other: The other object to check against.
+        """
+
+    @property
+    @abc.abstractmethod
+    def prereleases(self) -> bool | None:
+        """Whether or not pre-releases as a whole are allowed.
+
+        This can be set to either ``True`` or ``False`` to explicitly enable or disable
+        prereleases or it can be set to ``None`` (the default) to use default semantics.
+        """
+
+    @prereleases.setter  # noqa: B027
+    def prereleases(self, value: bool) -> None:
+        """Setter for :attr:`prereleases`.
+
+        :param value: The value to set.
+        """
+
+    @abc.abstractmethod
+    def contains(self, item: str, prereleases: bool | None = None) -> bool:
+        """
+        Determines if the given item is contained within this specifier.
+        """
+
+    @typing.overload
+    def filter(
+        self,
+        iterable: Iterable[UnparsedVersionVar],
+        prereleases: bool | None = None,
+        key: None = ...,
+    ) -> Iterator[UnparsedVersionVar]: ...
+
+    @typing.overload
+    def filter(
+        self,
+        iterable: Iterable[T],
+        prereleases: bool | None = None,
+        key: Callable[[T], UnparsedVersion] = ...,
+    ) -> Iterator[T]: ...
+
+    @abc.abstractmethod
+    def filter(
+        self,
+        iterable: Iterable[Any],
+        prereleases: bool | None = None,
+        key: Callable[[Any], UnparsedVersion] | None = None,
+    ) -> Iterator[Any]:
+        """
+        Takes an iterable of items and filters them so that only items which
+        are contained within this specifier are allowed in it.
+        """
+
+
+class Specifier(BaseSpecifier):
+    """This class abstracts handling of version specifiers.
+
+    .. tip::
+
+        It is generally not required to instantiate this manually. You should instead
+        prefer to work with :class:`SpecifierSet` instead, which can parse
+        comma-separated version specifiers (which is what package metadata contains).
+
+    Instances are safe to serialize with :mod:`pickle`. They use a stable
+    format so the same pickle can be loaded in future packaging releases.
+
+    .. versionchanged:: 26.2
+
+        Added a stable pickle format. Pickles created with packaging 26.2+ can
+        be unpickled with future releases.  Backward compatibility with pickles
+        from pip._vendor.packaging < 26.2 is supported but may be removed in a future
+        release.
+    """
+
+    __slots__ = (
+        "_prereleases",
+        "_ranges",
+        "_spec",
+        "_spec_version",
+        "_wildcard_split",
+    )
+
+    _specifier_regex_str = r"""
+        (?:
+            (?:
+                # The identity operators allow for an escape hatch that will
+                # do an exact string match of the version you wish to install.
+                # This will not be parsed by PEP 440 and we cannot determine
+                # any semantic meaning from it. This operator is discouraged
+                # but included entirely as an escape hatch.
+                ===  # Only match for the identity operator
+                \s*
+                [^\s;)]*  # The arbitrary version can be just about anything,
+                          # we match everything except for whitespace, a
+                          # semi-colon for marker support, and a closing paren
+                          # since versions can be enclosed in them.
+            )
+            |
+            (?:
+                # The (non)equality operators allow for wild card and local
+                # versions to be specified so we have to define these two
+                # operators separately to enable that.
+                (?:==|!=)            # Only match for equals and not equals
+
+                \s*
+                v?
+                (?:[0-9]+!)?          # epoch
+                [0-9]+(?:\.[0-9]+)*   # release
+
+                # You cannot use a wild card and a pre-release, post-release, a dev or
+                # local version together so group them with a | and make them optional.
+                (?:
+                    \.\*  # Wild card syntax of .*
+                    |
+                    (?a:                                  # pre release
+                        [-_\.]?
+                        (alpha|beta|preview|pre|a|b|c|rc)
+                        [-_\.]?
+                        [0-9]*
+                    )?
+                    (?a:                                  # post release
+                        (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
+                    )?
+                    (?a:[-_\.]?dev[-_\.]?[0-9]*)?         # dev release
+                    (?a:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local
+                )?
+            )
+            |
+            (?:
+                # The compatible operator requires at least two digits in the
+                # release segment.
+                (?:~=)               # Only match for the compatible operator
+
+                \s*
+                v?
+                (?:[0-9]+!)?          # epoch
+                [0-9]+(?:\.[0-9]+)+   # release  (We have a + instead of a *)
+                (?:                   # pre release
+                    [-_\.]?
+                    (alpha|beta|preview|pre|a|b|c|rc)
+                    [-_\.]?
+                    [0-9]*
+                )?
+                (?:                                   # post release
+                    (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
+                )?
+                (?:[-_\.]?dev[-_\.]?[0-9]*)?          # dev release
+            )
+            |
+            (?:
+                # All other operators only allow a sub set of what the
+                # (non)equality operators do. Specifically they do not allow
+                # local versions to be specified nor do they allow the prefix
+                # matching wild cards.
+                (?:<=|>=|<|>)
+
+                \s*
+                v?
+                (?:[0-9]+!)?          # epoch
+                [0-9]+(?:\.[0-9]+)*   # release
+                (?a:                   # pre release
+                    [-_\.]?
+                    (alpha|beta|preview|pre|a|b|c|rc)
+                    [-_\.]?
+                    [0-9]*
+                )?
+                (?a:                                   # post release
+                    (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
+                )?
+                (?a:[-_\.]?dev[-_\.]?[0-9]*)?          # dev release
+            )
+        )
+        """
+
+    _regex = re.compile(
+        r"\s*" + _specifier_regex_str + r"\s*", re.VERBOSE | re.IGNORECASE
+    )
+
+    _operators: Final = {
+        "~=": "compatible",
+        "==": "equal",
+        "!=": "not_equal",
+        "<=": "less_than_equal",
+        ">=": "greater_than_equal",
+        "<": "less_than",
+        ">": "greater_than",
+        "===": "arbitrary",
+    }
+
+    def __init__(self, spec: str = "", prereleases: bool | None = None) -> None:
+        """Initialize a Specifier instance.
+
+        :param spec:
+            The string representation of a specifier which will be parsed and
+            normalized before use.
+        :param prereleases:
+            This tells the specifier if it should accept prerelease versions if
+            applicable or not. The default of ``None`` will autodetect it from the
+            given specifiers.
+        :raises InvalidSpecifier:
+            If the given specifier is invalid (i.e. bad syntax).
+        """
+        if not self._regex.fullmatch(spec):
+            raise InvalidSpecifier(f"Invalid specifier: {spec!r}")
+
+        spec = spec.strip()
+        if spec.startswith("==="):
+            operator, version = spec[:3], spec[3:].strip()
+        elif spec.startswith(("~=", "==", "!=", "<=", ">=")):
+            operator, version = spec[:2], spec[2:].strip()
+        else:
+            operator, version = spec[:1], spec[1:].strip()
+
+        self._spec: tuple[str, str] = (operator, version)
+
+        # Store whether or not this Specifier should accept prereleases
+        self._prereleases = prereleases
+
+        # Specifier version cache
+        self._spec_version: tuple[str, Version] | None = None
+
+        # Populated on first wildcard (==X.*) comparison
+        self._wildcard_split: tuple[list[str], int] | None = None
+
+        # Version range cache (populated by _to_ranges)
+        self._ranges: Sequence[_VersionRange] | None = None
+
+    def _get_spec_version(self, version: str) -> Version | None:
+        """One element cache, as only one spec Version is needed per Specifier."""
+        if self._spec_version is not None and self._spec_version[0] == version:
+            return self._spec_version[1]
+
+        version_specifier = _coerce_version(version)
+        if version_specifier is None:
+            return None
+
+        self._spec_version = (version, version_specifier)
+        return version_specifier
+
+    def _require_spec_version(self, version: str) -> Version:
+        """Get spec version, asserting it's valid (not for === operator).
+
+        This method should only be called for operators where version
+        strings are guaranteed to be valid PEP 440 versions (not ===).
+        """
+        spec_version = self._get_spec_version(version)
+        assert spec_version is not None
+        return spec_version
+
+    def _to_ranges(self) -> Sequence[_VersionRange]:
+        """Convert this specifier to sorted, non-overlapping version ranges.
+
+        Each standard operator maps to one or two ranges.  ``===`` is
+        modeled as full range (actual check done separately).  Cached.
+        """
+        if self._ranges is not None:
+            return self._ranges
+
+        op = self.operator
+        ver_str = self.version
+
+        if op == "===":
+            self._ranges = _FULL_RANGE
+            return _FULL_RANGE
+
+        if ver_str.endswith(".*"):
+            result = self._wildcard_ranges(op, ver_str)
+        else:
+            result = self._standard_ranges(op, ver_str)
+
+        self._ranges = result
+        return result
+
+    def _wildcard_ranges(self, op: str, ver_str: str) -> list[_VersionRange]:
+        # ==1.2.* -> [1.2.dev0, 1.3.dev0);  !=1.2.* -> complement.
+        base = self._require_spec_version(ver_str[:-2])
+        lower = _base_dev0(base)
+        upper = _next_prefix_dev0(base)
+        if op == "==":
+            return [(_LowerBound(lower, True), _UpperBound(upper, False))]
+        # !=
+        return [
+            (_NEG_INF, _UpperBound(lower, False)),
+            (_LowerBound(upper, True), _POS_INF),
+        ]
+
+    def _standard_ranges(self, op: str, ver_str: str) -> list[_VersionRange]:
+        v = self._require_spec_version(ver_str)
+
+        if op == ">=":
+            return [(_LowerBound(v, True), _POS_INF)]
+
+        if op == "<=":
+            return [
+                (
+                    _NEG_INF,
+                    _UpperBound(_BoundaryVersion(v, _BoundaryKind.AFTER_LOCALS), True),
+                )
+            ]
+
+        if op == ">":
+            if v.dev is not None:
+                # >V.devN: dev versions have no post-releases, so the
+                # next real version is V.dev(N+1).
+                lower_ver = v.__replace__(dev=v.dev + 1, local=None)
+                return [(_LowerBound(lower_ver, True), _POS_INF)]
+            if v.post is not None:
+                # >V.postN: next real version is V.post(N+1).dev0.
+                lower_ver = v.__replace__(post=v.post + 1, dev=0, local=None)
+                return [(_LowerBound(lower_ver, True), _POS_INF)]
+            # >V (final or pre-release): skip V+local and all V.postN.
+            return [
+                (
+                    _LowerBound(_BoundaryVersion(v, _BoundaryKind.AFTER_POSTS), False),
+                    _POS_INF,
+                )
+            ]
+
+        if op == "<":
+            #  bool | None:
+        # If there is an explicit prereleases set for this, then we'll just
+        # blindly use that.
+        if self._prereleases is not None:
+            return self._prereleases
+
+        # Only the "!=" operator does not imply prereleases when
+        # the version in the specifier is a prerelease.
+        operator, version_str = self._spec
+        if operator == "!=":
+            return False
+
+        # The == specifier with trailing .* cannot include prereleases
+        # e.g. "==1.0a1.*" is not valid.
+        if operator == "==" and version_str.endswith(".*"):
+            return False
+
+        # "===" can have arbitrary string versions, so we cannot parse
+        # those, we take prereleases as unknown (None) for those.
+        version = self._get_spec_version(version_str)
+        if version is None:
+            return None
+
+        # For all other operators, use the check if spec Version
+        # object implies pre-releases.
+        return version.is_prerelease
+
+    @prereleases.setter
+    def prereleases(self, value: bool | None) -> None:
+        self._prereleases = value
+
+    def __getstate__(self) -> tuple[tuple[str, str], bool | None]:
+        # Return state as a 2-item tuple for compactness:
+        #   ((operator, version), prereleases)
+        # Cache members are excluded and will be recomputed on demand.
+        return (self._spec, self._prereleases)
+
+    def __setstate__(self, state: object) -> None:
+        # Always discard cached values - they will be recomputed on demand.
+        self._spec_version = None
+        self._wildcard_split = None
+        self._ranges = None
+
+        if isinstance(state, tuple):
+            if len(state) == 2:
+                # New format (26.2+): ((operator, version), prereleases)
+                spec, prereleases = state
+                if _validate_spec(spec) and _validate_pre(prereleases):
+                    self._spec = spec
+                    self._prereleases = prereleases
+                    return
+            if len(state) == 2 and isinstance(state[1], dict):
+                # Format (packaging 26.0-26.1): (None, {slot: value}).
+                _, slot_dict = state
+                spec = slot_dict.get("_spec")
+                prereleases = slot_dict.get("_prereleases", "invalid")
+                if _validate_spec(spec) and _validate_pre(prereleases):
+                    self._spec = spec
+                    self._prereleases = prereleases
+                    return
+        if isinstance(state, dict):
+            # Old format (packaging <= 25.x, no __slots__): state is a plain dict.
+            spec = state.get("_spec")
+            prereleases = state.get("_prereleases", "invalid")
+            if _validate_spec(spec) and _validate_pre(prereleases):
+                self._spec = spec
+                self._prereleases = prereleases
+                return
+
+        raise TypeError(f"Cannot restore Specifier from {state!r}")
+
+    @property
+    def operator(self) -> str:
+        """The operator of this specifier.
+
+        >>> Specifier("==1.2.3").operator
+        '=='
+        """
+        return self._spec[0]
+
+    @property
+    def version(self) -> str:
+        """The version of this specifier.
+
+        >>> Specifier("==1.2.3").version
+        '1.2.3'
+        """
+        return self._spec[1]
+
+    def __repr__(self) -> str:
+        """A representation of the Specifier that shows all internal state.
+
+        >>> Specifier('>=1.0.0')
+        =1.0.0')>
+        >>> Specifier('>=1.0.0', prereleases=False)
+        =1.0.0', prereleases=False)>
+        >>> Specifier('>=1.0.0', prereleases=True)
+        =1.0.0', prereleases=True)>
+        """
+        pre = (
+            f", prereleases={self.prereleases!r}"
+            if self._prereleases is not None
+            else ""
+        )
+
+        return f"<{self.__class__.__name__}({str(self)!r}{pre})>"
+
+    def __str__(self) -> str:
+        """A string representation of the Specifier that can be round-tripped.
+
+        >>> str(Specifier('>=1.0.0'))
+        '>=1.0.0'
+        >>> str(Specifier('>=1.0.0', prereleases=False))
+        '>=1.0.0'
+        """
+        return "{}{}".format(*self._spec)
+
+    @property
+    def _canonical_spec(self) -> tuple[str, str]:
+        operator, version = self._spec
+        if operator == "===" or version.endswith(".*"):
+            return operator, version
+
+        spec_version = self._require_spec_version(version)
+
+        canonical_version = canonicalize_version(
+            spec_version, strip_trailing_zero=(operator != "~=")
+        )
+
+        return operator, canonical_version
+
+    def __hash__(self) -> int:
+        return hash(self._canonical_spec)
+
+    def __eq__(self, other: object) -> bool:
+        """Whether or not the two Specifier-like objects are equal.
+
+        :param other: The other object to check against.
+
+        The value of :attr:`prereleases` is ignored.
+
+        >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0")
+        True
+        >>> (Specifier("==1.2.3", prereleases=False) ==
+        ...  Specifier("==1.2.3", prereleases=True))
+        True
+        >>> Specifier("==1.2.3") == "==1.2.3"
+        True
+        >>> Specifier("==1.2.3") == Specifier("==1.2.4")
+        False
+        >>> Specifier("==1.2.3") == Specifier("~=1.2.3")
+        False
+        """
+        if isinstance(other, str):
+            try:
+                other = self.__class__(str(other))
+            except InvalidSpecifier:
+                return NotImplemented
+        elif not isinstance(other, self.__class__):
+            return NotImplemented
+
+        return self._canonical_spec == other._canonical_spec
+
+    def _get_operator(self, op: str) -> CallableOperator:
+        operator_callable: CallableOperator = getattr(
+            self, f"_compare_{self._operators[op]}"
+        )
+        return operator_callable
+
+    def _compare_compatible(self, prospective: Version, spec: str) -> bool:
+        # Compatible releases have an equivalent combination of >= and ==. That
+        # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to
+        # implement this in terms of the other specifiers instead of
+        # implementing it ourselves. The only thing we need to do is construct
+        # the other specifiers.
+
+        # We want everything but the last item in the version, but we want to
+        # ignore suffix segments.
+        prefix = _version_join(
+            list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1]
+        )
+
+        # Add the prefix notation to the end of our string
+        prefix += ".*"
+
+        return (self._compare_greater_than_equal(prospective, spec)) and (
+            self._compare_equal(prospective, prefix)
+        )
+
+    def _get_wildcard_split(self, spec: str) -> tuple[list[str], int]:
+        """Cached split of a wildcard spec into components and numeric length.
+
+        >>> Specifier("==1.*")._get_wildcard_split("1.*")
+        (['0', '1'], 2)
+        >>> Specifier("==3.10.*")._get_wildcard_split("3.10.*")
+        (['0', '3', '10'], 3)
+        """
+        wildcard_split = self._wildcard_split
+        if wildcard_split is None:
+            normalized = canonicalize_version(spec[:-2], strip_trailing_zero=False)
+            split_spec = _version_split(normalized)
+            wildcard_split = (split_spec, _numeric_prefix_len(split_spec))
+            self._wildcard_split = wildcard_split
+        return wildcard_split
+
+    def _compare_equal(self, prospective: Version, spec: str) -> bool:
+        # We need special logic to handle prefix matching
+        if spec.endswith(".*"):
+            split_spec, spec_numeric_len = self._get_wildcard_split(spec)
+
+            # In the case of prefix matching we want to ignore local segment.
+            normalized_prospective = canonicalize_version(
+                _public_version(prospective), strip_trailing_zero=False
+            )
+            # Split the prospective version out by bangs and dots, and pretend
+            # that there is an implicit dot in between a release segment and
+            # a pre-release segment.
+            split_prospective = _version_split(normalized_prospective)
+
+            # 0-pad the prospective version before shortening it to get the correct
+            # shortened version.
+            padded_prospective = _left_pad(split_prospective, spec_numeric_len)
+
+            # Shorten the prospective version to be the same length as the spec
+            # so that we can determine if the specifier is a prefix of the
+            # prospective version or not.
+            shortened_prospective = padded_prospective[: len(split_spec)]
+
+            return shortened_prospective == split_spec
+        else:
+            # Convert our spec string into a Version
+            spec_version = self._require_spec_version(spec)
+
+            # If the specifier does not have a local segment, then we want to
+            # act as if the prospective version also does not have a local
+            # segment.
+            if not spec_version.local:
+                prospective = _public_version(prospective)
+
+            return prospective == spec_version
+
+    def _compare_not_equal(self, prospective: Version, spec: str) -> bool:
+        return not self._compare_equal(prospective, spec)
+
+    def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool:
+        # NB: Local version identifiers are NOT permitted in the version
+        # specifier, so local version labels can be universally removed from
+        # the prospective version.
+        return _public_version(prospective) <= self._require_spec_version(spec)
+
+    def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool:
+        # NB: Local version identifiers are NOT permitted in the version
+        # specifier, so local version labels can be universally removed from
+        # the prospective version.
+        return _public_version(prospective) >= self._require_spec_version(spec)
+
+    def _compare_less_than(self, prospective: Version, spec_str: str) -> bool:
+        # Convert our spec to a Version instance, since we'll want to work with
+        # it as a version.
+        spec = self._require_spec_version(spec_str)
+
+        # Check to see if the prospective version is less than the spec
+        # version. If it's not we can short circuit and just return False now
+        # instead of doing extra unneeded work.
+        if not prospective < spec:
+            return False
+
+        # The spec says: "= _earliest_prerelease(spec)
+        ):
+            return False
+
+        # If we've gotten to here, it means that prospective version is both
+        # less than the spec version *and* it's not a pre-release of the same
+        # version in the spec.
+        return True
+
+    def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool:
+        # Convert our spec to a Version instance, since we'll want to work with
+        # it as a version.
+        spec = self._require_spec_version(spec_str)
+
+        # Check to see if the prospective version is greater than the spec
+        # version. If it's not we can short circuit and just return False now
+        # instead of doing extra unneeded work.
+        if not prospective > spec:
+            return False
+
+        # The spec says: ">V MUST NOT allow a post-release of the specified
+        # version unless the specified version is itself a post-release."
+        if (
+            not spec.is_postrelease
+            and prospective.is_postrelease
+            and _post_base(prospective) == spec
+        ):
+            return False
+
+        # Per the spec: ">V MUST NOT match a local version of the specified
+        # version". A "local version of V" is any version whose public part
+        # equals V. So >1.0a1 must not match 1.0a1+local, but must still
+        # match 1.0a2+local.
+        if prospective.local is not None and _public_version(prospective) == spec:
+            return False
+
+        # If we've gotten to here, it means that prospective version is both
+        # greater than the spec version *and* it's not a pre-release of the
+        # same version in the spec.
+        return True
+
+    def _compare_arbitrary(self, prospective: Version | str, spec: str) -> bool:
+        return str(prospective).lower() == str(spec).lower()
+
+    def __contains__(self, item: str | Version) -> bool:
+        """Return whether or not the item is contained in this specifier.
+
+        :param item: The item to check for.
+
+        This is used for the ``in`` operator and behaves the same as
+        :meth:`contains` with no ``prereleases`` argument passed.
+
+        >>> "1.2.3" in Specifier(">=1.2.3")
+        True
+        >>> Version("1.2.3") in Specifier(">=1.2.3")
+        True
+        >>> "1.0.0" in Specifier(">=1.2.3")
+        False
+        >>> "1.3.0a1" in Specifier(">=1.2.3")
+        True
+        >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True)
+        True
+        """
+        return self.contains(item)
+
+    def contains(self, item: UnparsedVersion, prereleases: bool | None = None) -> bool:
+        """Return whether or not the item is contained in this specifier.
+
+        :param item:
+            The item to check for, which can be a version string or a
+            :class:`Version` instance.
+        :param prereleases:
+            Whether or not to match prereleases with this Specifier. If set to
+            ``None`` (the default), it will follow the recommendation from
+            :pep:`440` and match prereleases, as there are no other versions.
+
+        >>> Specifier(">=1.2.3").contains("1.2.3")
+        True
+        >>> Specifier(">=1.2.3").contains(Version("1.2.3"))
+        True
+        >>> Specifier(">=1.2.3").contains("1.0.0")
+        False
+        >>> Specifier(">=1.2.3").contains("1.3.0a1")
+        True
+        >>> Specifier(">=1.2.3", prereleases=False).contains("1.3.0a1")
+        False
+        >>> Specifier(">=1.2.3").contains("1.3.0a1")
+        True
+        """
+
+        return bool(list(self.filter([item], prereleases=prereleases)))
+
+    @typing.overload
+    def filter(
+        self,
+        iterable: Iterable[UnparsedVersionVar],
+        prereleases: bool | None = None,
+        key: None = ...,
+    ) -> Iterator[UnparsedVersionVar]: ...
+
+    @typing.overload
+    def filter(
+        self,
+        iterable: Iterable[T],
+        prereleases: bool | None = None,
+        key: Callable[[T], UnparsedVersion] = ...,
+    ) -> Iterator[T]: ...
+
+    def filter(
+        self,
+        iterable: Iterable[Any],
+        prereleases: bool | None = None,
+        key: Callable[[Any], UnparsedVersion] | None = None,
+    ) -> Iterator[Any]:
+        """Filter items in the given iterable, that match the specifier.
+
+        :param iterable:
+            An iterable that can contain version strings and :class:`Version` instances.
+            The items in the iterable will be filtered according to the specifier.
+        :param prereleases:
+            Whether or not to allow prereleases in the returned iterator. If set to
+            ``None`` (the default), it will follow the recommendation from :pep:`440`
+            and match prereleases if there are no other versions.
+        :param key:
+            A callable that takes a single argument (an item from the iterable) and
+            returns a version string or :class:`Version` instance to be used for
+            filtering.
+
+        >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
+        ['1.3']
+        >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")]))
+        ['1.2.3', '1.3', ]
+        >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"]))
+        ['1.5a1']
+        >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))
+        ['1.3', '1.5a1']
+        >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
+        ['1.3', '1.5a1']
+        >>> list(Specifier(">=1.2.3").filter(
+        ... [{"ver": "1.2"}, {"ver": "1.3"}],
+        ... key=lambda x: x["ver"]))
+        [{'ver': '1.3'}]
+        """
+        prereleases_versions = []
+        found_non_prereleases = False
+
+        # Determine if to include prereleases by default
+        include_prereleases = (
+            prereleases if prereleases is not None else self.prereleases
+        )
+
+        # Get the matching operator
+        operator_callable = self._get_operator(self.operator)
+
+        # Filter versions
+        for version in iterable:
+            parsed_version = _coerce_version(version if key is None else key(version))
+            match = False
+            if parsed_version is None:
+                # === operator can match arbitrary (non-version) strings
+                if self.operator == "===" and self._compare_arbitrary(
+                    version, self.version
+                ):
+                    yield version
+            elif self.operator == "===":
+                match = self._compare_arbitrary(
+                    version if key is None else key(version), self.version
+                )
+            else:
+                match = operator_callable(parsed_version, self.version)
+
+            if match and parsed_version is not None:
+                # If it's not a prerelease or prereleases are allowed, yield it directly
+                if not parsed_version.is_prerelease or include_prereleases:
+                    found_non_prereleases = True
+                    yield version
+                # Otherwise collect prereleases for potential later use
+                elif prereleases is None and self._prereleases is not False:
+                    prereleases_versions.append(version)
+
+        # If no non-prereleases were found and prereleases weren't
+        # explicitly forbidden, yield the collected prereleases
+        if (
+            not found_non_prereleases
+            and prereleases is None
+            and self._prereleases is not False
+        ):
+            yield from prereleases_versions
+
+
+_prefix_regex = re.compile(r"([0-9]+)((?:a|b|c|rc)[0-9]+)")
+
+
+def _pep440_filter_prereleases(
+    iterable: Iterable[Any], key: Callable[[Any], UnparsedVersion] | None
+) -> Iterator[Any]:
+    """Filter per PEP 440: exclude prereleases unless no finals exist."""
+    # Two lists used:
+    #   * all_nonfinal to preserve order if no finals exist
+    #   * arbitrary_strings for streaming when first final found
+    all_nonfinal: list[Any] = []
+    arbitrary_strings: list[Any] = []
+
+    found_final = False
+    for item in iterable:
+        parsed = _coerce_version(item if key is None else key(item))
+
+        if parsed is None:
+            # Arbitrary strings are always included as it is not
+            # possible to determine if they are prereleases,
+            # and they have already passed all specifiers.
+            if found_final:
+                yield item
+            else:
+                arbitrary_strings.append(item)
+                all_nonfinal.append(item)
+            continue
+
+        if not parsed.is_prerelease:
+            # Final release found - flush arbitrary strings, then yield
+            if not found_final:
+                yield from arbitrary_strings
+                found_final = True
+            yield item
+            continue
+
+        # Prerelease - buffer if no finals yet, otherwise skip
+        if not found_final:
+            all_nonfinal.append(item)
+
+    # No finals found - yield all buffered items
+    if not found_final:
+        yield from all_nonfinal
+
+
+def _version_split(version: str) -> list[str]:
+    """Split version into components.
+
+    The split components are intended for version comparison. The logic does
+    not attempt to retain the original version string, so joining the
+    components back with :func:`_version_join` may not produce the original
+    version string.
+    """
+    result: list[str] = []
+
+    epoch, _, rest = version.rpartition("!")
+    result.append(epoch or "0")
+
+    for item in rest.split("."):
+        match = _prefix_regex.fullmatch(item)
+        if match:
+            result.extend(match.groups())
+        else:
+            result.append(item)
+    return result
+
+
+def _version_join(components: list[str]) -> str:
+    """Join split version components into a version string.
+
+    This function assumes the input came from :func:`_version_split`, where the
+    first component must be the epoch (either empty or numeric), and all other
+    components numeric.
+    """
+    epoch, *rest = components
+    return f"{epoch}!{'.'.join(rest)}"
+
+
+def _is_not_suffix(segment: str) -> bool:
+    return not any(
+        segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post")
+    )
+
+
+def _numeric_prefix_len(split: list[str]) -> int:
+    """Count leading numeric components in a :func:`_version_split` result.
+
+    >>> _numeric_prefix_len(["0", "1", "2", "a1"])
+    3
+    """
+    count = 0
+    for segment in split:
+        if not segment.isdigit():
+            break
+        count += 1
+    return count
+
+
+def _left_pad(split: list[str], target_numeric_len: int) -> list[str]:
+    """Pad a :func:`_version_split` result with ``"0"`` segments to reach
+    ``target_numeric_len`` numeric components.  Suffix segments are preserved.
+
+    >>> _left_pad(["0", "1", "a1"], 4)
+    ['0', '1', '0', '0', 'a1']
+    """
+    numeric_len = _numeric_prefix_len(split)
+    pad_needed = target_numeric_len - numeric_len
+    if pad_needed <= 0:
+        return split
+    return [*split[:numeric_len], *(["0"] * pad_needed), *split[numeric_len:]]
+
+
+def _operator_cost(op_entry: tuple[CallableOperator, str, str]) -> int:
+    """Sort key for Cost Based Ordering of specifier operators in _filter_versions.
+
+    Operators run sequentially on a shrinking candidate set, so operators that
+    reject the most versions should run first to minimize work for later ones.
+
+    Tier 0: Exact equality (==, ===), likely to narrow candidates to one version
+    Tier 1: Range checks (>=, <=, >, <), cheap and usually reject a large portion
+    Tier 2: Wildcard equality (==.*) and compatible release (~=), more expensive
+    Tier 3: Exact !=, cheap but rarely rejects
+    Tier 4: Wildcard !=.*, expensive and rarely rejects
+    """
+    _, ver, op = op_entry
+    if op == "==":
+        return 0 if not ver.endswith(".*") else 2
+    if op in (">=", "<=", ">", "<"):
+        return 1
+    if op == "~=":
+        return 2
+    if op == "!=":
+        return 3 if not ver.endswith(".*") else 4
+    if op == "===":
+        return 0
+
+    raise ValueError(f"Unknown operator: {op!r}")  # pragma: no cover
+
+
+class SpecifierSet(BaseSpecifier):
+    """This class abstracts handling of a set of version specifiers.
+
+    It can be passed a single specifier (``>=3.0``), a comma-separated list of
+    specifiers (``>=3.0,!=3.1``), or no specifier at all.
+
+    Instances are safe to serialize with :mod:`pickle`. They use a stable
+    format so the same pickle can be loaded in future packaging
+    releases.
+
+    .. versionchanged:: 26.2
+
+        Added a stable pickle format. Pickles created with
+        packaging 26.2+ can be unpickled with future releases.
+        Backward compatibility with pickles from
+        packaging < 26.2 is supported but may be removed in a future
+        release.
+    """
+
+    __slots__ = (
+        "_canonicalized",
+        "_has_arbitrary",
+        "_is_unsatisfiable",
+        "_prereleases",
+        "_resolved_ops",
+        "_specs",
+    )
+
+    def __init__(
+        self,
+        specifiers: str | Iterable[Specifier] = "",
+        prereleases: bool | None = None,
+    ) -> None:
+        """Initialize a SpecifierSet instance.
+
+        :param specifiers:
+            The string representation of a specifier or a comma-separated list of
+            specifiers which will be parsed and normalized before use.
+            May also be an iterable of ``Specifier`` instances, which will be used
+            as is.
+        :param prereleases:
+            This tells the SpecifierSet if it should accept prerelease versions if
+            applicable or not. The default of ``None`` will autodetect it from the
+            given specifiers.
+
+        :raises InvalidSpecifier:
+            If the given ``specifiers`` are not parseable than this exception will be
+            raised.
+        """
+
+        if isinstance(specifiers, str):
+            # Split on `,` to break each individual specifier into its own item, and
+            # strip each item to remove leading/trailing whitespace.
+            split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
+
+            self._specs: tuple[Specifier, ...] = tuple(map(Specifier, split_specifiers))
+            # Fast substring check; avoids iterating parsed specs.
+            self._has_arbitrary = "===" in specifiers
+        else:
+            self._specs = tuple(specifiers)
+            # Substring check works for both Specifier objects and plain
+            # strings (setuptools passes lists of strings).
+            self._has_arbitrary = any("===" in str(s) for s in self._specs)
+
+        self._canonicalized = len(self._specs) <= 1
+        self._resolved_ops: list[tuple[CallableOperator, str, str]] | None = None
+
+        # Store our prereleases value so we can use it later to determine if
+        # we accept prereleases or not.
+        self._prereleases = prereleases
+
+        self._is_unsatisfiable: bool | None = None
+
+    def _canonical_specs(self) -> tuple[Specifier, ...]:
+        """Deduplicate, sort, and cache specs for order-sensitive operations."""
+        if not self._canonicalized:
+            self._specs = tuple(dict.fromkeys(sorted(self._specs, key=str)))
+            self._canonicalized = True
+            self._resolved_ops = None
+            self._is_unsatisfiable = None
+        return self._specs
+
+    @property
+    def prereleases(self) -> bool | None:
+        # If we have been given an explicit prerelease modifier, then we'll
+        # pass that through here.
+        if self._prereleases is not None:
+            return self._prereleases
+
+        # If we don't have any specifiers, and we don't have a forced value,
+        # then we'll just return None since we don't know if this should have
+        # pre-releases or not.
+        if not self._specs:
+            return None
+
+        # Otherwise we'll see if any of the given specifiers accept
+        # prereleases, if any of them do we'll return True, otherwise False.
+        if any(s.prereleases for s in self._specs):
+            return True
+
+        return None
+
+    @prereleases.setter
+    def prereleases(self, value: bool | None) -> None:
+        self._prereleases = value
+        self._is_unsatisfiable = None
+
+    def __getstate__(self) -> tuple[tuple[Specifier, ...], bool | None]:
+        # Return state as a 2-item tuple for compactness:
+        #   (specs, prereleases)
+        # Cache members are excluded and will be recomputed on demand.
+        return (self._specs, self._prereleases)
+
+    def __setstate__(self, state: object) -> None:
+        # Always discard cached values - they will be recomputed on demand.
+        self._resolved_ops = None
+        self._is_unsatisfiable = None
+
+        if isinstance(state, tuple):
+            if len(state) == 2:
+                # New format (26.2+): (specs, prereleases)
+                specs, prereleases = state
+                if (
+                    isinstance(specs, tuple)
+                    and all(isinstance(s, Specifier) for s in specs)
+                    and _validate_pre(prereleases)
+                ):
+                    self._specs = specs
+                    self._prereleases = prereleases
+                    self._canonicalized = len(specs) <= 1
+                    self._has_arbitrary = any("===" in str(s) for s in specs)
+                    return
+            if len(state) == 2 and isinstance(state[1], dict):
+                # Format (packaging 26.0-26.1): (None, {slot: value}).
+                _, slot_dict = state
+                specs = slot_dict.get("_specs", ())
+                prereleases = slot_dict.get("_prereleases")
+                # Convert frozenset to tuple (26.0 stored as frozenset)
+                if isinstance(specs, frozenset):
+                    specs = tuple(sorted(specs, key=str))
+                if (
+                    isinstance(specs, tuple)
+                    and all(isinstance(s, Specifier) for s in specs)
+                    and _validate_pre(prereleases)
+                ):
+                    self._specs = specs
+                    self._prereleases = prereleases
+                    self._canonicalized = len(self._specs) <= 1
+                    self._has_arbitrary = any("===" in str(s) for s in self._specs)
+                    return
+        if isinstance(state, dict):
+            # Old format (packaging <= 25.x, no __slots__): state is a plain dict.
+            specs = state.get("_specs", ())
+            prereleases = state.get("_prereleases")
+            # Convert frozenset to tuple (26.0 stored as frozenset)
+            if isinstance(specs, frozenset):
+                specs = tuple(sorted(specs, key=str))
+            if (
+                isinstance(specs, tuple)
+                and all(isinstance(s, Specifier) for s in specs)
+                and _validate_pre(prereleases)
+            ):
+                self._specs = specs
+                self._prereleases = prereleases
+                self._canonicalized = len(self._specs) <= 1
+                self._has_arbitrary = any("===" in str(s) for s in self._specs)
+                return
+
+        raise TypeError(f"Cannot restore SpecifierSet from {state!r}")
+
+    def __repr__(self) -> str:
+        """A representation of the specifier set that shows all internal state.
+
+        Note that the ordering of the individual specifiers within the set may not
+        match the input string.
+
+        >>> SpecifierSet('>=1.0.0,!=2.0.0')
+        =1.0.0')>
+        >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False)
+        =1.0.0', prereleases=False)>
+        >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True)
+        =1.0.0', prereleases=True)>
+        """
+        pre = (
+            f", prereleases={self.prereleases!r}"
+            if self._prereleases is not None
+            else ""
+        )
+
+        return f"<{self.__class__.__name__}({str(self)!r}{pre})>"
+
+    def __str__(self) -> str:
+        """A string representation of the specifier set that can be round-tripped.
+
+        Note that the ordering of the individual specifiers within the set may not
+        match the input string.
+
+        >>> str(SpecifierSet(">=1.0.0,!=1.0.1"))
+        '!=1.0.1,>=1.0.0'
+        >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False))
+        '!=1.0.1,>=1.0.0'
+        """
+        return ",".join(str(s) for s in self._canonical_specs())
+
+    def __hash__(self) -> int:
+        return hash(self._canonical_specs())
+
+    def __and__(self, other: SpecifierSet | str) -> SpecifierSet:
+        """Return a SpecifierSet which is a combination of the two sets.
+
+        :param other: The other object to combine with.
+
+        >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1'
+        =1.0.0')>
+        >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1')
+        =1.0.0')>
+        """
+        if isinstance(other, str):
+            other = SpecifierSet(other)
+        elif not isinstance(other, SpecifierSet):
+            return NotImplemented
+
+        specifier = SpecifierSet()
+        specifier._specs = self._specs + other._specs
+        specifier._canonicalized = len(specifier._specs) <= 1
+        specifier._has_arbitrary = self._has_arbitrary or other._has_arbitrary
+        specifier._resolved_ops = None
+
+        # Combine prerelease settings: use common or non-None value
+        if self._prereleases is None or self._prereleases == other._prereleases:
+            specifier._prereleases = other._prereleases
+        elif other._prereleases is None:
+            specifier._prereleases = self._prereleases
+        else:
+            raise ValueError(
+                "Cannot combine SpecifierSets with True and False prerelease overrides."
+            )
+
+        return specifier
+
+    def __eq__(self, other: object) -> bool:
+        """Whether or not the two SpecifierSet-like objects are equal.
+
+        :param other: The other object to check against.
+
+        The value of :attr:`prereleases` is ignored.
+
+        >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1")
+        True
+        >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) ==
+        ...  SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True))
+        True
+        >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1"
+        True
+        >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0")
+        False
+        >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2")
+        False
+        """
+        if isinstance(other, (str, Specifier)):
+            other = SpecifierSet(str(other))
+        elif not isinstance(other, SpecifierSet):
+            return NotImplemented
+
+        return self._canonical_specs() == other._canonical_specs()
+
+    def __len__(self) -> int:
+        """Returns the number of specifiers in this specifier set."""
+        return len(self._specs)
+
+    def __iter__(self) -> Iterator[Specifier]:
+        """
+        Returns an iterator over all the underlying :class:`Specifier` instances
+        in this specifier set.
+
+        >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str)
+        [, =1.0.0')>]
+        """
+        return iter(self._specs)
+
+    def _get_ranges(self) -> Sequence[_VersionRange]:
+        """Intersect all specifiers into a single list of version ranges.
+
+        Returns an empty list when unsatisfiable.  ``===`` specs are
+        modeled as full range; string matching is checked separately
+        by :meth:`_check_arbitrary_unsatisfiable`.
+        """
+        specs = self._specs
+
+        result: Sequence[_VersionRange] | None = None
+        for s in specs:
+            if result is None:
+                result = s._to_ranges()
+            else:
+                result = _intersect_ranges(result, s._to_ranges())
+                if not result:
+                    break
+
+        if result is None:  # pragma: no cover
+            raise RuntimeError("_get_ranges called with no specs")
+        return result
+
+    def is_unsatisfiable(self) -> bool:
+        """Check whether this specifier set can never be satisfied.
+
+        Returns True if no version can satisfy all specifiers simultaneously.
+
+        >>> SpecifierSet(">=2.0,<1.0").is_unsatisfiable()
+        True
+        >>> SpecifierSet(">=1.0,<2.0").is_unsatisfiable()
+        False
+        >>> SpecifierSet("").is_unsatisfiable()
+        False
+        >>> SpecifierSet("==1.0,!=1.0").is_unsatisfiable()
+        True
+        """
+        cached = self._is_unsatisfiable
+        if cached is not None:
+            return cached
+
+        if not self._specs:
+            self._is_unsatisfiable = False
+            return False
+
+        result = not self._get_ranges()
+
+        if not result:
+            result = self._check_arbitrary_unsatisfiable()
+
+        if not result and self.prereleases is False:
+            result = self._check_prerelease_only_ranges()
+
+        self._is_unsatisfiable = result
+        return result
+
+    def _check_prerelease_only_ranges(self) -> bool:
+        """With prereleases=False, check if every range contains only
+        pre-release versions (which would be excluded from matching)."""
+        for lower, upper in self._get_ranges():
+            nearest = _nearest_non_prerelease(lower.version)
+            if nearest is None:
+                return False
+            if upper.version is None or nearest < upper.version:
+                return False
+            if nearest == upper.version and upper.inclusive:
+                return False
+        return True
+
+    def _check_arbitrary_unsatisfiable(self) -> bool:
+        """Check === (arbitrary equality) specs for unsatisfiability.
+
+        === uses case-insensitive string comparison, so the only candidate
+        that can match ``===V`` is the literal string V.  This method
+        checks whether that candidate is excluded by other specifiers.
+        """
+        arbitrary = [s for s in self._specs if s.operator == "==="]
+        if not arbitrary:
+            return False
+
+        # Multiple === must agree on the same string (case-insensitive).
+        first = arbitrary[0].version.lower()
+        if any(s.version.lower() != first for s in arbitrary[1:]):
+            return True
+
+        # The sole candidate is the === version string.  Check whether
+        # it can satisfy every standard spec.
+        candidate = _coerce_version(arbitrary[0].version)
+
+        # With prereleases=False, a prerelease candidate is excluded
+        # by contains() before the === string check even runs.
+        if (
+            self.prereleases is False
+            and candidate is not None
+            and candidate.is_prerelease
+        ):
+            return True
+
+        standard = [s for s in self._specs if s.operator != "==="]
+        if not standard:
+            return False
+
+        if candidate is None:
+            # Unparsable string cannot satisfy any standard spec.
+            return True
+
+        return not all(s.contains(candidate) for s in standard)
+
+    def __contains__(self, item: UnparsedVersion) -> bool:
+        """Return whether or not the item is contained in this specifier.
+
+        :param item: The item to check for.
+
+        This is used for the ``in`` operator and behaves the same as
+        :meth:`contains` with no ``prereleases`` argument passed.
+
+        >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1")
+        True
+        >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1")
+        True
+        >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1")
+        False
+        >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1")
+        True
+        >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)
+        True
+        """
+        return self.contains(item)
+
+    def contains(
+        self,
+        item: UnparsedVersion,
+        prereleases: bool | None = None,
+        installed: bool | None = None,
+    ) -> bool:
+        """Return whether or not the item is contained in this SpecifierSet.
+
+        :param item:
+            The item to check for, which can be a version string or a
+            :class:`Version` instance.
+        :param prereleases:
+            Whether or not to match prereleases with this SpecifierSet. If set to
+            ``None`` (the default), it will follow the recommendation from :pep:`440`
+            and match prereleases, as there are no other versions.
+        :param installed:
+            Whether or not the item is installed. If set to ``True``, it will
+            accept prerelease versions even if the specifier does not allow them.
+
+        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3")
+        True
+        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3"))
+        True
+        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1")
+        False
+        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1")
+        True
+        >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False).contains("1.3.0a1")
+        False
+        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True)
+        True
+        """
+        version = _coerce_version(item)
+
+        if version is not None and installed and version.is_prerelease:
+            prereleases = True
+
+        # When item is a string and === is involved, keep it as-is
+        # so the comparison isn't done against the normalized form.
+        if version is None or (self._has_arbitrary and not isinstance(item, Version)):
+            check_item = item
+        else:
+            check_item = version
+        return bool(list(self.filter([check_item], prereleases=prereleases)))
+
+    @typing.overload
+    def filter(
+        self,
+        iterable: Iterable[UnparsedVersionVar],
+        prereleases: bool | None = None,
+        key: None = ...,
+    ) -> Iterator[UnparsedVersionVar]: ...
+
+    @typing.overload
+    def filter(
+        self,
+        iterable: Iterable[T],
+        prereleases: bool | None = None,
+        key: Callable[[T], UnparsedVersion] = ...,
+    ) -> Iterator[T]: ...
+
+    def filter(
+        self,
+        iterable: Iterable[Any],
+        prereleases: bool | None = None,
+        key: Callable[[Any], UnparsedVersion] | None = None,
+    ) -> Iterator[Any]:
+        """Filter items in the given iterable, that match the specifiers in this set.
+
+        :param iterable:
+            An iterable that can contain version strings and :class:`Version` instances.
+            The items in the iterable will be filtered according to the specifier.
+        :param prereleases:
+            Whether or not to allow prereleases in the returned iterator. If set to
+            ``None`` (the default), it will follow the recommendation from :pep:`440`
+            and match prereleases if there are no other versions.
+        :param key:
+            A callable that takes a single argument (an item from the iterable) and
+            returns a version string or :class:`Version` instance to be used for
+            filtering.
+
+        >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
+        ['1.3']
+        >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")]))
+        ['1.3', ]
+        >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"]))
+        ['1.5a1']
+        >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))
+        ['1.3', '1.5a1']
+        >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
+        ['1.3', '1.5a1']
+        >>> list(SpecifierSet(">=1.2.3").filter(
+        ... [{"ver": "1.2"}, {"ver": "1.3"}],
+        ... key=lambda x: x["ver"]))
+        [{'ver': '1.3'}]
+
+        An "empty" SpecifierSet will filter items based on the presence of prerelease
+        versions in the set.
+
+        >>> list(SpecifierSet("").filter(["1.3", "1.5a1"]))
+        ['1.3']
+        >>> list(SpecifierSet("").filter(["1.5a1"]))
+        ['1.5a1']
+        >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"]))
+        ['1.3', '1.5a1']
+        >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True))
+        ['1.3', '1.5a1']
+        """
+        # Determine if we're forcing a prerelease or not, if we're not forcing
+        # one for this particular filter call, then we'll use whatever the
+        # SpecifierSet thinks for whether or not we should support prereleases.
+        if prereleases is None and self.prereleases is not None:
+            prereleases = self.prereleases
+
+        # Filter versions that match all specifiers using Cost Based Ordering.
+        if self._specs:
+            # When prereleases is None, we need to let all versions through
+            # the individual filters, then decide about prereleases at the end
+            # based on whether any non-prereleases matched ALL specs.
+
+            # Fast path: single specifier, delegate directly.
+            if len(self._specs) == 1:
+                filtered = self._specs[0].filter(
+                    iterable,
+                    prereleases=True if prereleases is None else prereleases,
+                    key=key,
+                )
+            else:
+                filtered = self._filter_versions(
+                    iterable,
+                    key,
+                    prereleases=True if prereleases is None else prereleases,
+                )
+
+            if prereleases is not None:
+                return filtered
+
+            return _pep440_filter_prereleases(filtered, key)
+
+        # Handle Empty SpecifierSet.
+        if prereleases is True:
+            return iter(iterable)
+
+        if prereleases is False:
+            return (
+                item
+                for item in iterable
+                if (
+                    (version := _coerce_version(item if key is None else key(item)))
+                    is None
+                    or not version.is_prerelease
+                )
+            )
+
+        # PEP 440: exclude prereleases unless no final releases matched
+        return _pep440_filter_prereleases(iterable, key)
+
+    def _filter_versions(
+        self,
+        iterable: Iterable[Any],
+        key: Callable[[Any], UnparsedVersion] | None,
+        prereleases: bool | None = None,
+    ) -> Iterator[Any]:
+        """Filter versions against all specifiers in a single pass.
+
+        Uses Cost Based Ordering: specifiers are sorted by _operator_cost so
+        that cheap range operators reject versions early, avoiding expensive
+        wildcard or compatible operators on versions that would have been
+        rejected anyway.
+        """
+        # Pre-resolve operators and sort (cached after first call).
+        if self._resolved_ops is None:
+            self._resolved_ops = sorted(
+                (
+                    (spec._get_operator(spec.operator), spec.version, spec.operator)
+                    for spec in self._specs
+                ),
+                key=_operator_cost,
+            )
+        ops = self._resolved_ops
+        exclude_prereleases = prereleases is False
+
+        for item in iterable:
+            parsed = _coerce_version(item if key is None else key(item))
+
+            if parsed is None:
+                # Only === can match non-parseable versions.
+                if all(
+                    op == "===" and str(item).lower() == ver.lower()
+                    for _, ver, op in ops
+                ):
+                    yield item
+            elif exclude_prereleases and parsed.is_prerelease:
+                pass
+            elif all(
+                str(item if key is None else key(item)).lower() == ver.lower()
+                if op == "==="
+                else op_fn(parsed, ver)
+                for op_fn, ver, op in ops
+            ):
+                # Short-circuits on the first failing operator.
+                yield item
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/tags.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/tags.py
new file mode 100644
index 0000000..979fbd2
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/tags.py
@@ -0,0 +1,932 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import logging
+import operator
+import platform
+import re
+import struct
+import subprocess
+import sys
+import sysconfig
+from importlib.machinery import EXTENSION_SUFFIXES
+from typing import (
+    TYPE_CHECKING,
+    Iterable,
+    Iterator,
+    Sequence,
+    Tuple,
+    TypeVar,
+    cast,
+)
+
+from . import _manylinux, _musllinux
+
+if TYPE_CHECKING:
+    from collections.abc import Callable, Iterable
+    from typing import AbstractSet
+
+
+__all__ = [
+    "INTERPRETER_SHORT_NAMES",
+    "AppleVersion",
+    "PythonVersion",
+    "Tag",
+    "UnsortedTagsError",
+    "android_platforms",
+    "compatible_tags",
+    "cpython_tags",
+    "create_compatible_tags_selector",
+    "generic_tags",
+    "interpreter_name",
+    "interpreter_version",
+    "ios_platforms",
+    "mac_platforms",
+    "parse_tag",
+    "platform_tags",
+    "sys_tags",
+]
+
+
+def __dir__() -> list[str]:
+    return __all__
+
+
+logger = logging.getLogger(__name__)
+
+PythonVersion = Sequence[int]
+AppleVersion = Tuple[int, int]
+_T = TypeVar("_T")
+
+INTERPRETER_SHORT_NAMES: dict[str, str] = {
+    "python": "py",  # Generic.
+    "cpython": "cp",
+    "pypy": "pp",
+    "ironpython": "ip",
+    "jython": "jy",
+}
+
+
+# This function can be unit tested without reloading the module
+# (Unlike _32_BIT_INTERPRETER)
+def _compute_32_bit_interpreter() -> bool:
+    return struct.calcsize("P") == 4
+
+
+_32_BIT_INTERPRETER = _compute_32_bit_interpreter()
+
+
+class UnsortedTagsError(ValueError):
+    """
+    Raised when a tag component is not in sorted order per PEP 425.
+    """
+
+
+class Tag:
+    """
+    A representation of the tag triple for a wheel.
+
+    Instances are considered immutable and thus are hashable. Equality checking
+    is also supported.
+
+    Instances are safe to serialize with :mod:`pickle`. They use a stable
+    format so the same pickle can be loaded in future packaging releases.
+
+    .. versionchanged:: 26.2
+
+        Added a stable pickle format. Pickles created with packaging 26.2+ can
+        be unpickled with future releases.  Backward compatibility with pickles
+        from pip._vendor.packaging < 26.2 is supported but may be removed in a future
+        release.
+    """
+
+    __slots__ = ["_abi", "_hash", "_interpreter", "_platform"]
+
+    def __init__(self, interpreter: str, abi: str, platform: str) -> None:
+        """
+        :param str interpreter: The interpreter name, e.g. ``"py"``
+                                (see :attr:`INTERPRETER_SHORT_NAMES` for mapping
+                                well-known interpreter names to their short names).
+        :param str abi: The ABI that a wheel supports, e.g. ``"cp37m"``.
+        :param str platform: The OS/platform the wheel supports,
+                            e.g. ``"win_amd64"``.
+        """
+        self._interpreter = interpreter.lower()
+        self._abi = abi.lower()
+        self._platform = platform.lower()
+        # The __hash__ of every single element in a Set[Tag] will be evaluated each time
+        # that a set calls its `.disjoint()` method, which may be called hundreds of
+        # times when scanning a page of links for packages with tags matching that
+        # Set[Tag]. Pre-computing the value here produces significant speedups for
+        # downstream consumers.
+        self._hash = hash((self._interpreter, self._abi, self._platform))
+
+    @property
+    def interpreter(self) -> str:
+        """
+        The interpreter name, e.g. ``"py"`` (see
+        :attr:`INTERPRETER_SHORT_NAMES` for mapping well-known interpreter
+        names to their short names).
+        """
+        return self._interpreter
+
+    @property
+    def abi(self) -> str:
+        """
+        The supported ABI.
+        """
+        return self._abi
+
+    @property
+    def platform(self) -> str:
+        """
+        The OS/platform.
+        """
+        return self._platform
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, Tag):
+            return NotImplemented
+
+        return (
+            (self._hash == other._hash)  # Short-circuit ASAP for perf reasons.
+            and (self._platform == other._platform)
+            and (self._abi == other._abi)
+            and (self._interpreter == other._interpreter)
+        )
+
+    def __hash__(self) -> int:
+        return self._hash
+
+    def __str__(self) -> str:
+        return f"{self._interpreter}-{self._abi}-{self._platform}"
+
+    def __repr__(self) -> str:
+        return f"<{self} @ {id(self)}>"
+
+    def __getstate__(self) -> tuple[str, str, str]:
+        # Return state as a 3-item tuple: (interpreter, abi, platform).
+        # Cache member _hash is excluded and will be recomputed.
+        return (self._interpreter, self._abi, self._platform)
+
+    def __setstate__(self, state: object) -> None:
+        if isinstance(state, tuple):
+            if len(state) == 3 and all(isinstance(s, str) for s in state):
+                # New format (26.2+): (interpreter, abi, platform)
+                self._interpreter, self._abi, self._platform = state
+                self._hash = hash((self._interpreter, self._abi, self._platform))
+                return
+            if len(state) == 2 and isinstance(state[1], dict):
+                # Old format (packaging <= 26.1, __slots__): (None, {slot: value}).
+                _, slots = state
+                try:
+                    interpreter = slots["_interpreter"]
+                    abi = slots["_abi"]
+                    platform = slots["_platform"]
+                except KeyError:
+                    raise TypeError(f"Cannot restore Tag from {state!r}") from None
+                if not all(
+                    isinstance(value, str) for value in (interpreter, abi, platform)
+                ):
+                    raise TypeError(f"Cannot restore Tag from {state!r}")
+                self._interpreter = interpreter.lower()
+                self._abi = abi.lower()
+                self._platform = platform.lower()
+                self._hash = hash((self._interpreter, self._abi, self._platform))
+                return
+        raise TypeError(f"Cannot restore Tag from {state!r}")
+
+
+def parse_tag(tag: str, *, validate_order: bool = False) -> frozenset[Tag]:
+    """
+    Parses the provided tag (e.g. `py3-none-any`) into a frozenset of
+    :class:`Tag` instances.
+
+    Returning a set is required due to the possibility that the tag is a
+    `compressed tag set`_, e.g. ``"py2.py3-none-any"`` which supports both
+    Python 2 and Python 3.
+
+    If **validate_order** is true, compressed tag set components are checked
+    to be in sorted order as required by PEP 425.
+
+    :param str tag: The tag to parse, e.g. ``"py3-none-any"``.
+    :param bool validate_order: Check whether compressed tag set components
+        are in sorted order.
+    :raises UnsortedTagsError: If **validate_order** is true and any compressed tag
+        set component is not in sorted order.
+
+    .. versionadded:: 26.1
+       The *validate_order* parameter.
+    """
+    tags = set()
+    interpreters, abis, platforms = tag.split("-")
+    if validate_order:
+        for component in (interpreters, abis, platforms):
+            parts = component.split(".")
+            if parts != sorted(parts):
+                raise UnsortedTagsError(
+                    f"Tag component {component!r} is not in sorted order per PEP 425"
+                )
+    for interpreter in interpreters.split("."):
+        for abi in abis.split("."):
+            for platform_ in platforms.split("."):
+                tags.add(Tag(interpreter, abi, platform_))
+    return frozenset(tags)
+
+
+def _get_config_var(name: str, warn: bool = False) -> int | str | None:
+    value: int | str | None = sysconfig.get_config_var(name)
+    if value is None and warn:
+        logger.debug(
+            "Config variable '%s' is unset, Python ABI tag may be incorrect", name
+        )
+    return value
+
+
+def _normalize_string(string: str) -> str:
+    return string.replace(".", "_").replace("-", "_").replace(" ", "_")
+
+
+def _is_threaded_cpython(abis: list[str]) -> bool:
+    """
+    Determine if the ABI corresponds to a threaded (`--disable-gil`) build.
+
+    The threaded builds are indicated by a "t" in the abiflags.
+    """
+    if len(abis) == 0:
+        return False
+    # expect e.g., cp313
+    m = re.match(r"cp\d+(.*)", abis[0])
+    if not m:
+        return False
+    abiflags = m.group(1)
+    return "t" in abiflags
+
+
+def _abi3_applies(python_version: PythonVersion, threading: bool) -> bool:
+    """
+    Determine if the Python version supports abi3.
+
+    PEP 384 was first implemented in Python 3.2. The free-threaded
+    builds do not support abi3.
+    """
+    return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading
+
+
+def _abi3t_applies(python_version: PythonVersion, threading: bool) -> bool:
+    """
+    Determine if the Python version supports abi3t.
+
+    PEP 803 was first implemented in Python 3.15 but, per PEP 803, this
+    returns tags going back to Python 3.2 to mirror the abi3
+    implementation and leave open the possibility of abi3t wheels
+    supporting older Python versions.
+
+    """
+    return len(python_version) > 1 and tuple(python_version) >= (3, 2) and threading
+
+
+def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> list[str]:
+    py_version = tuple(py_version)  # To allow for version comparison.
+    abis = []
+    version = _version_nodot(py_version[:2])
+    threading = debug = pymalloc = ucs4 = ""
+    with_debug = _get_config_var("Py_DEBUG", warn)
+    has_refcount = hasattr(sys, "gettotalrefcount")
+    # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled
+    # extension modules is the best option.
+    # https://github.com/pypa/pip/issues/3383#issuecomment-173267692
+    has_ext = "_d.pyd" in EXTENSION_SUFFIXES
+    if with_debug or (with_debug is None and (has_refcount or has_ext)):
+        debug = "d"
+    if py_version >= (3, 13) and _get_config_var("Py_GIL_DISABLED", warn):
+        threading = "t"
+    if py_version < (3, 8):
+        with_pymalloc = _get_config_var("WITH_PYMALLOC", warn)
+        if with_pymalloc or with_pymalloc is None:
+            pymalloc = "m"
+        if py_version < (3, 3):
+            unicode_size = _get_config_var("Py_UNICODE_SIZE", warn)
+            if unicode_size == 4 or (
+                unicode_size is None and sys.maxunicode == 0x10FFFF
+            ):
+                ucs4 = "u"
+    elif debug:
+        # Debug builds can also load "normal" extension modules.
+        # We can also assume no UCS-4 or pymalloc requirement.
+        abis.append(f"cp{version}{threading}")
+    abis.insert(0, f"cp{version}{threading}{debug}{pymalloc}{ucs4}")
+    return abis
+
+
+def cpython_tags(
+    python_version: PythonVersion | None = None,
+    abis: Iterable[str] | None = None,
+    platforms: Iterable[str] | None = None,
+    *,
+    warn: bool = False,
+) -> Iterator[Tag]:
+    """
+    Yields the tags for the CPython interpreter.
+
+    The specific tags generated are:
+
+    - ``cp--``
+    - ``cp--``
+    - ``cp-none-``
+    - ``cp--`` where "older version" is all older
+      minor versions down to Python 3.2 (when ``abi3`` was introduced)
+
+    If ``python_version`` only provides a major-only version then only
+    user-provided ABIs via ``abis`` and the ``none`` ABI will be used.
+
+    The ``stable_abi`` will be either ``abi3`` or ``abi3t`` if `abi` is a
+    GIL-enabled ABI like `"cp315"` or a free-threaded ABI like `"cp315t"`,
+    respectively.
+
+    :param Sequence python_version: A one- or two-item sequence representing the
+                                 targeted Python version. Defaults to
+                                 ``sys.version_info[:2]``.
+    :param Iterable abis: Iterable of compatible ABIs. Defaults to the ABIs
+                          compatible with the current system.
+    :param Iterable platforms: Iterable of compatible platforms. Defaults to the
+                               platforms compatible with the current system.
+    :param bool warn: Whether warnings should be logged. Defaults to ``False``.
+    """
+    if not python_version:
+        python_version = sys.version_info[:2]
+
+    interpreter = f"cp{_version_nodot(python_version[:2])}"
+
+    if abis is None:
+        abis = _cpython_abis(python_version, warn) if len(python_version) > 1 else []
+    abis = list(abis)
+    # 'abi3' and 'none' are explicitly handled later.
+    for explicit_abi in ("abi3", "none"):
+        try:
+            abis.remove(explicit_abi)
+        except ValueError:  # noqa: PERF203
+            pass
+
+    platforms = list(platforms or platform_tags())
+    for abi in abis:
+        for platform_ in platforms:
+            yield Tag(interpreter, abi, platform_)
+
+    threading = _is_threaded_cpython(abis)
+    use_abi3 = _abi3_applies(python_version, threading)
+    use_abi3t = _abi3t_applies(python_version, threading)
+
+    if use_abi3:
+        yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms)
+    if use_abi3t:
+        yield from (Tag(interpreter, "abi3t", platform_) for platform_ in platforms)
+
+    yield from (Tag(interpreter, "none", platform_) for platform_ in platforms)
+
+    if use_abi3 or use_abi3t:
+        for minor_version in range(python_version[1] - 1, 1, -1):
+            for platform_ in platforms:
+                version = _version_nodot((python_version[0], minor_version))
+                interpreter = f"cp{version}"
+                if use_abi3:
+                    yield Tag(interpreter, "abi3", platform_)
+                if use_abi3t:
+                    # Support for abi3t was introduced in Python 3.15, but in
+                    # principle abi3t wheels are possible for older limited API
+                    # versions, so allow things like ("cp37", "abi3t", "platform")
+                    yield Tag(interpreter, "abi3t", platform_)
+
+
+def _generic_abi() -> list[str]:
+    """
+    Return the ABI tag based on EXT_SUFFIX.
+    """
+    # The following are examples of `EXT_SUFFIX`.
+    # We want to keep the parts which are related to the ABI and remove the
+    # parts which are related to the platform:
+    # - linux:   '.cpython-310-x86_64-linux-gnu.so' => cp310
+    # - mac:     '.cpython-310-darwin.so'           => cp310
+    # - win:     '.cp310-win_amd64.pyd'             => cp310
+    # - win:     '.pyd'                             => cp37 (uses _cpython_abis())
+    # - pypy:    '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73
+    # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib'
+    #                                               => graalpy_38_native
+
+    ext_suffix = _get_config_var("EXT_SUFFIX", warn=True)
+    if not isinstance(ext_suffix, str) or ext_suffix[0] != ".":
+        raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')")
+    parts = ext_suffix.split(".")
+    if len(parts) < 3:
+        # CPython3.7 and earlier uses ".pyd" on Windows.
+        return _cpython_abis(sys.version_info[:2])
+    soabi = parts[1]
+    if soabi.startswith("cpython"):
+        # non-windows
+        abi = "cp" + soabi.split("-")[1]
+    elif soabi.startswith("cp"):
+        # windows
+        abi = soabi.split("-")[0]
+    elif soabi.startswith("pypy"):
+        abi = "-".join(soabi.split("-")[:2])
+    elif soabi.startswith("graalpy"):
+        abi = "-".join(soabi.split("-")[:3])
+    elif soabi:
+        # pyston, ironpython, others?
+        abi = soabi
+    else:
+        return []
+    return [_normalize_string(abi)]
+
+
+def generic_tags(
+    interpreter: str | None = None,
+    abis: Iterable[str] | None = None,
+    platforms: Iterable[str] | None = None,
+    *,
+    warn: bool = False,
+) -> Iterator[Tag]:
+    """
+    Yields the tags for an interpreter which requires no specialization.
+
+    This function should be used if one of the other interpreter-specific
+    functions provided by this module is not appropriate (i.e. not calculating
+    tags for a CPython interpreter).
+
+    The specific tags generated are:
+
+    - ``--``
+
+    The ``"none"`` ABI will be added if it was not explicitly provided.
+
+    :param str interpreter: The name of the interpreter. Defaults to being
+                            calculated.
+    :param Iterable abis: Iterable of compatible ABIs. Defaults to the ABIs
+                          compatible with the current system.
+    :param Iterable platforms: Iterable of compatible platforms. Defaults to the
+                               platforms compatible with the current system.
+    :param bool warn: Whether warnings should be logged. Defaults to ``False``.
+    """
+    if not interpreter:
+        interp_name = interpreter_name()
+        interp_version = interpreter_version(warn=warn)
+        interpreter = f"{interp_name}{interp_version}"
+    abis = _generic_abi() if abis is None else list(abis)
+    platforms = list(platforms or platform_tags())
+    if "none" not in abis:
+        abis.append("none")
+    for abi in abis:
+        for platform_ in platforms:
+            yield Tag(interpreter, abi, platform_)
+
+
+def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]:
+    """
+    Yields Python versions in descending order.
+
+    After the latest version, the major-only version will be yielded, and then
+    all previous versions of that major version.
+    """
+    if len(py_version) > 1:
+        yield f"py{_version_nodot(py_version[:2])}"
+    yield f"py{py_version[0]}"
+    if len(py_version) > 1:
+        for minor in range(py_version[1] - 1, -1, -1):
+            yield f"py{_version_nodot((py_version[0], minor))}"
+
+
+def compatible_tags(
+    python_version: PythonVersion | None = None,
+    interpreter: str | None = None,
+    platforms: Iterable[str] | None = None,
+) -> Iterator[Tag]:
+    """
+    Yields the tags for an interpreter compatible with the Python version
+    specified by ``python_version``.
+
+    The specific tags generated are:
+
+    - ``py*-none-``
+    - ``-none-any`` if ``interpreter`` is provided
+    - ``py*-none-any``
+
+    :param Sequence python_version: A one- or two-item sequence representing the
+                                 compatible version of Python. Defaults to
+                                 ``sys.version_info[:2]``.
+    :param str interpreter: The name of the interpreter (if known), e.g.
+                            ``"cp38"``. Defaults to the current interpreter.
+    :param Iterable platforms: Iterable of compatible platforms. Defaults to the
+                               platforms compatible with the current system.
+    """
+    if not python_version:
+        python_version = sys.version_info[:2]
+    platforms = list(platforms or platform_tags())
+    for version in _py_interpreter_range(python_version):
+        for platform_ in platforms:
+            yield Tag(version, "none", platform_)
+    if interpreter:
+        yield Tag(interpreter, "none", "any")
+    for version in _py_interpreter_range(python_version):
+        yield Tag(version, "none", "any")
+
+
+def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str:
+    if not is_32bit:
+        return arch
+
+    if arch.startswith("ppc"):
+        return "ppc"
+
+    return "i386"
+
+
+def _mac_binary_formats(version: AppleVersion, cpu_arch: str) -> list[str]:
+    formats = [cpu_arch]
+    if cpu_arch == "x86_64":
+        if version < (10, 4):
+            return []
+        formats.extend(["intel", "fat64", "fat32"])
+
+    elif cpu_arch == "i386":
+        if version < (10, 4):
+            return []
+        formats.extend(["intel", "fat32", "fat"])
+
+    elif cpu_arch == "ppc64":
+        # TODO: Need to care about 32-bit PPC for ppc64 through 10.2?
+        if version > (10, 5) or version < (10, 4):
+            return []
+        formats.append("fat64")
+
+    elif cpu_arch == "ppc":
+        if version > (10, 6):
+            return []
+        formats.extend(["fat32", "fat"])
+
+    if cpu_arch in {"arm64", "x86_64"}:
+        formats.append("universal2")
+
+    if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}:
+        formats.append("universal")
+
+    return formats
+
+
+def mac_platforms(
+    version: AppleVersion | None = None, arch: str | None = None
+) -> Iterator[str]:
+    """
+    Yields the :attr:`~Tag.platform` tags for macOS.
+
+    The `version` parameter is a two-item tuple specifying the macOS version to
+    generate platform tags for. The `arch` parameter is the CPU architecture to
+    generate platform tags for. Both parameters default to the appropriate value
+    for the current system.
+
+    :param tuple version: A two-item tuple representing the version of macOS.
+                          Defaults to the current system's version.
+    :param str arch: The CPU architecture. Defaults to the architecture of the
+                     current system, e.g. ``"x86_64"``.
+
+    .. note::
+        Equivalent support for the other major platforms is purposefully not
+        provided:
+
+        - On Windows, platform compatibility is statically specified
+        - On Linux, code must be run on the system itself to determine
+          compatibility
+    """
+    version_str, _, cpu_arch = platform.mac_ver()
+    if version is None:
+        version = cast("AppleVersion", tuple(map(int, version_str.split(".")[:2])))
+        if version == (10, 16):
+            # When built against an older macOS SDK, Python will report macOS 10.16
+            # instead of the real version.
+            version_str = subprocess.run(
+                [
+                    sys.executable,
+                    "-sS",
+                    "-c",
+                    "import platform; print(platform.mac_ver()[0])",
+                ],
+                check=True,
+                env={"SYSTEM_VERSION_COMPAT": "0"},
+                stdout=subprocess.PIPE,
+                text=True,
+            ).stdout
+            version = cast("AppleVersion", tuple(map(int, version_str.split(".")[:2])))
+
+    if arch is None:
+        arch = _mac_arch(cpu_arch)
+
+    if (10, 0) <= version < (11, 0):
+        # Prior to Mac OS 11, each yearly release of Mac OS bumped the
+        # "minor" version number.  The major version was always 10.
+        major_version = 10
+        for minor_version in range(version[1], -1, -1):
+            compat_version = major_version, minor_version
+            binary_formats = _mac_binary_formats(compat_version, arch)
+            for binary_format in binary_formats:
+                yield f"macosx_{major_version}_{minor_version}_{binary_format}"
+
+    if version >= (11, 0):
+        # Starting with Mac OS 11, each yearly release bumps the major version
+        # number.   The minor versions are now the midyear updates.
+        minor_version = 0
+        for major_version in range(version[0], 10, -1):
+            compat_version = major_version, minor_version
+            binary_formats = _mac_binary_formats(compat_version, arch)
+            for binary_format in binary_formats:
+                yield f"macosx_{major_version}_{minor_version}_{binary_format}"
+
+    if version >= (11, 0):
+        # Mac OS 11 on x86_64 is compatible with binaries from previous releases.
+        # Arm64 support was introduced in 11.0, so no Arm binaries from previous
+        # releases exist.
+        #
+        # However, the "universal2" binary format can have a
+        # macOS version earlier than 11.0 when the x86_64 part of the binary supports
+        # that version of macOS.
+        major_version = 10
+        if arch == "x86_64":
+            for minor_version in range(16, 3, -1):
+                compat_version = major_version, minor_version
+                binary_formats = _mac_binary_formats(compat_version, arch)
+                for binary_format in binary_formats:
+                    yield f"macosx_{major_version}_{minor_version}_{binary_format}"
+        else:
+            for minor_version in range(16, 3, -1):
+                compat_version = major_version, minor_version
+                binary_format = "universal2"
+                yield f"macosx_{major_version}_{minor_version}_{binary_format}"
+
+
+def ios_platforms(
+    version: AppleVersion | None = None, multiarch: str | None = None
+) -> Iterator[str]:
+    """
+
+    Yields the :attr:`~Tag.platform` tags for iOS.
+
+    :param tuple version: A two-item tuple representing the version of iOS.
+                          Defaults to the current system's version.
+    :param str multiarch: The CPU architecture+ABI to be used. This should be in
+                          the format by ``sys.implementation._multiarch`` (e.g.,
+                          ``arm64_iphoneos`` or ``x86_64_iphonesimulator``).
+                          Defaults to the current system's multiarch value.
+
+    .. note::
+        Behavior of this method is undefined if invoked on non-iOS platforms
+        without providing explicit version and multiarch arguments.
+    """
+    if version is None:
+        # if iOS is the current platform, ios_ver *must* be defined. However,
+        # it won't exist for CPython versions before 3.13, which causes a mypy
+        # error.
+        _, release, _, _ = platform.ios_ver()  # type: ignore[attr-defined, unused-ignore]
+        version = cast("AppleVersion", tuple(map(int, release.split(".")[:2])))
+
+    if multiarch is None:
+        multiarch = sys.implementation._multiarch
+    multiarch = multiarch.replace("-", "_")
+
+    ios_platform_template = "ios_{major}_{minor}_{multiarch}"
+
+    # Consider any iOS major.minor version from the version requested, down to
+    # 12.0. 12.0 is the first iOS version that is known to have enough features
+    # to support CPython. Consider every possible minor release up to X.9. There
+    # highest the minor has ever gone is 8 (14.8 and 15.8) but having some extra
+    # candidates that won't ever match doesn't really hurt, and it saves us from
+    # having to keep an explicit list of known iOS versions in the code. Return
+    # the results descending order of version number.
+
+    # If the requested major version is less than 12, there won't be any matches.
+    if version[0] < 12:
+        return
+
+    # Consider the actual X.Y version that was requested.
+    yield ios_platform_template.format(
+        major=version[0], minor=version[1], multiarch=multiarch
+    )
+
+    # Consider every minor version from X.0 to the minor version prior to the
+    # version requested by the platform.
+    for minor in range(version[1] - 1, -1, -1):
+        yield ios_platform_template.format(
+            major=version[0], minor=minor, multiarch=multiarch
+        )
+
+    for major in range(version[0] - 1, 11, -1):
+        for minor in range(9, -1, -1):
+            yield ios_platform_template.format(
+                major=major, minor=minor, multiarch=multiarch
+            )
+
+
+def android_platforms(
+    api_level: int | None = None, abi: str | None = None
+) -> Iterator[str]:
+    """
+    Yields the :attr:`~Tag.platform` tags for Android. If this function is invoked on
+    non-Android platforms, the ``api_level`` and ``abi`` arguments are required.
+
+    :param int api_level: The maximum `API level
+        `__ to return. Defaults
+        to the current system's version, as returned by ``platform.android_ver``.
+    :param str abi: The `Android ABI `__,
+        e.g. ``arm64_v8a``. Defaults to the current system's ABI , as returned by
+        ``sysconfig.get_platform``. Hyphens and periods will be replaced with
+        underscores.
+    """
+    if platform.system() != "Android" and (api_level is None or abi is None):
+        raise TypeError(
+            "on non-Android platforms, the api_level and abi arguments are required"
+        )
+
+    if api_level is None:
+        # Python 3.13 was the first version to return platform.system() == "Android",
+        # and also the first version to define platform.android_ver().
+        api_level = platform.android_ver().api_level  # type: ignore[attr-defined]
+
+    if abi is None:
+        abi = sysconfig.get_platform().split("-")[-1]
+    abi = _normalize_string(abi)
+
+    # 16 is the minimum API level known to have enough features to support CPython
+    # without major patching. Yield every API level from the maximum down to the
+    # minimum, inclusive.
+    min_api_level = 16
+    for ver in range(api_level, min_api_level - 1, -1):
+        yield f"android_{ver}_{abi}"
+
+
+def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]:
+    linux = _normalize_string(sysconfig.get_platform())
+    if not linux.startswith("linux_"):
+        # we should never be here, just yield the sysconfig one and return
+        yield linux
+        return
+    if is_32bit:
+        if linux == "linux_x86_64":
+            linux = "linux_i686"
+        elif linux == "linux_aarch64":
+            linux = "linux_armv8l"
+    _, arch = linux.split("_", 1)
+    archs = {"armv8l": ["armv8l", "armv7l"]}.get(arch, [arch])
+    yield from _manylinux.platform_tags(archs)
+    yield from _musllinux.platform_tags(archs)
+    for arch in archs:
+        yield f"linux_{arch}"
+
+
+def _emscripten_platforms() -> Iterator[str]:
+    pyemscripten_platform_version = sysconfig.get_config_var(
+        "PYEMSCRIPTEN_PLATFORM_VERSION"
+    )
+    if pyemscripten_platform_version:
+        yield f"pyemscripten_{pyemscripten_platform_version}_wasm32"
+    yield from _generic_platforms()
+
+
+def _generic_platforms() -> Iterator[str]:
+    yield _normalize_string(sysconfig.get_platform())
+
+
+def platform_tags() -> Iterator[str]:
+    """
+    Yields the :attr:`~Tag.platform` tags for the running interpreter.
+    """
+    if platform.system() == "Darwin":
+        return mac_platforms()
+    elif platform.system() == "iOS":
+        return ios_platforms()
+    elif platform.system() == "Android":
+        return android_platforms()
+    elif platform.system() == "Linux":
+        return _linux_platforms()
+    elif platform.system() == "Emscripten":
+        return _emscripten_platforms()
+    else:
+        return _generic_platforms()
+
+
+def interpreter_name() -> str:
+    """
+    Returns the name of the running interpreter.
+
+    Some implementations have a reserved, two-letter abbreviation which will
+    be returned when appropriate.
+
+    This typically acts as the prefix to the :attr:`~Tag.interpreter` tag.
+    """
+    name = sys.implementation.name
+    return INTERPRETER_SHORT_NAMES.get(name) or name
+
+
+def interpreter_version(*, warn: bool = False) -> str:
+    """
+    Returns the running interpreter's version.
+
+    This typically acts as the suffix to the :attr:`~Tag.interpreter` tag.
+
+    :param bool warn: Whether warnings should be logged. Defaults to ``False``.
+    """
+    version = _get_config_var("py_version_nodot", warn=warn)
+    return str(version) if version else _version_nodot(sys.version_info[:2])
+
+
+def _version_nodot(version: PythonVersion) -> str:
+    return "".join(map(str, version))
+
+
+def sys_tags(*, warn: bool = False) -> Iterator[Tag]:
+    """
+    Yields the sequence of tag triples that the running interpreter supports.
+
+    The iterable is ordered so that the best-matching tag is first in the
+    sequence. The exact preferential order to tags is interpreter-specific, but
+    in general the tag importance is in the order of:
+
+    1. Interpreter
+    2. Platform
+    3. ABI
+
+    This order is due to the fact that an ABI is inherently tied to the
+    platform, but platform-specific code is not necessarily tied to the ABI. The
+    interpreter is the most important tag as it dictates basic support for any
+    wheel.
+
+    The function returns an iterable in order to allow for the possible
+    short-circuiting of tag generation if the entire sequence is not necessary
+    and tag calculation happens to be expensive.
+
+    :param bool warn: Whether warnings should be logged. Defaults to ``False``.
+
+    .. versionchanged:: 21.3
+        Added the `pp3-none-any` tag (:issue:`311`).
+    .. versionchanged:: 27.0
+        Added the `abi3t` tag (:issue:`1099`).
+    """
+
+    interp_name = interpreter_name()
+    if interp_name == "cp":
+        yield from cpython_tags(warn=warn)
+    else:
+        yield from generic_tags()
+
+    if interp_name == "pp":
+        interp = "pp3"
+    elif interp_name == "cp":
+        interp = "cp" + interpreter_version(warn=warn)
+    else:
+        interp = None
+    yield from compatible_tags(interpreter=interp)
+
+
+def create_compatible_tags_selector(
+    tags: Iterable[Tag],
+) -> Callable[[Iterable[tuple[_T, AbstractSet[Tag]]]], Iterator[_T]]:
+    """Create a callable to select things compatible with supported tags.
+
+    This function accepts an ordered sequence of tags, with the preferred
+    tags first.
+
+    The returned callable accepts an iterable of tuples (thing, set[Tag]),
+    and returns an iterator of things, with the things with the best
+    matching tags first.
+
+    Example to select compatible wheel filenames:
+
+    >>> from packaging import tags
+    >>> from packaging.utils import parse_wheel_filename
+    >>> selector = tags.create_compatible_tags_selector(tags.sys_tags())
+    >>> filenames = ["foo-1.0-py3-none-any.whl", "foo-1.0-py2-none-any.whl"]
+    >>> list(selector([
+    ...     (filename, parse_wheel_filename(filename)[-1]) for filename in filenames
+    ... ]))
+    ['foo-1.0-py3-none-any.whl']
+
+    .. versionadded:: 26.1
+    """
+    tag_ranks: dict[Tag, int] = {}
+    for rank, tag in enumerate(tags):
+        tag_ranks.setdefault(tag, rank)  # ignore duplicate tags, keep first
+    supported_tags = tag_ranks.keys()
+
+    def selector(
+        tagged_things: Iterable[tuple[_T, AbstractSet[Tag]]],
+    ) -> Iterator[_T]:
+        ranked_things: list[tuple[_T, int]] = []
+        for thing, thing_tags in tagged_things:
+            supported_thing_tags = thing_tags & supported_tags
+            if supported_thing_tags:
+                thing_rank = min(tag_ranks[t] for t in supported_thing_tags)
+                ranked_things.append((thing, thing_rank))
+        return iter(
+            thing for thing, _ in sorted(ranked_things, key=operator.itemgetter(1))
+        )
+
+    return selector
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/utils.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/utils.py
new file mode 100644
index 0000000..cbd3be2
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/utils.py
@@ -0,0 +1,296 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import re
+from typing import NewType, Tuple, Union, cast
+
+from .tags import Tag, UnsortedTagsError, parse_tag
+from .version import InvalidVersion, Version, _TrimmedRelease
+
+__all__ = [
+    "BuildTag",
+    "InvalidName",
+    "InvalidSdistFilename",
+    "InvalidWheelFilename",
+    "NormalizedName",
+    "canonicalize_name",
+    "canonicalize_version",
+    "is_normalized_name",
+    "parse_sdist_filename",
+    "parse_wheel_filename",
+]
+
+
+def __dir__() -> list[str]:
+    return __all__
+
+
+BuildTag = Union[Tuple[()], Tuple[int, str]]
+
+NormalizedName = NewType("NormalizedName", str)
+"""
+A :class:`typing.NewType` of :class:`str`, representing a normalized name.
+"""
+
+
+class InvalidName(ValueError):
+    """
+    An invalid distribution name; users should refer to the packaging user guide.
+    """
+
+
+class InvalidWheelFilename(ValueError):
+    """
+    An invalid wheel filename was found, users should refer to PEP 427.
+    """
+
+
+class InvalidSdistFilename(ValueError):
+    """
+    An invalid sdist filename was found, users should refer to the packaging user guide.
+    """
+
+
+# Core metadata spec for `Name`
+_validate_regex = re.compile(
+    r"[a-z0-9]|[a-z0-9][a-z0-9._-]*[a-z0-9]", re.IGNORECASE | re.ASCII
+)
+_normalized_regex = re.compile(r"[a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9]", re.ASCII)
+# PEP 427: The build number must start with a digit.
+_build_tag_regex = re.compile(r"(\d+)(.*)", re.ASCII)
+
+
+def canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName:
+    """
+    This function takes a valid Python package or extra name, and returns the
+    normalized form of it.
+
+    The return type is typed as :class:`NormalizedName`. This allows type
+    checkers to help require that a string has passed through this function
+    before use.
+
+    If **validate** is true, then the function will check if **name** is a valid
+    distribution name before normalizing.
+
+    :param str name: The name to normalize.
+    :param bool validate: Check whether the name is a valid distribution name.
+    :raises InvalidName: If **validate** is true and the name is not an
+        acceptable distribution name.
+
+    >>> from packaging.utils import canonicalize_name
+    >>> canonicalize_name("Django")
+    'django'
+    >>> canonicalize_name("oslo.concurrency")
+    'oslo-concurrency'
+    >>> canonicalize_name("requests")
+    'requests'
+    """
+    if validate and not _validate_regex.fullmatch(name):
+        raise InvalidName(f"name is invalid: {name!r}")
+    # Ensure all ``.`` and ``_`` are ``-``
+    # Emulates ``re.sub(r"[-_.]+", "-", name).lower()`` from PEP 503
+    # Much faster than re, and even faster than str.translate
+    value = name.lower().replace("_", "-").replace(".", "-")
+    # Condense repeats (faster than regex)
+    while "--" in value:
+        value = value.replace("--", "-")
+    return cast("NormalizedName", value)
+
+
+def is_normalized_name(name: str) -> bool:
+    """
+    Check if a name is already normalized (i.e. :func:`canonicalize_name` would
+    roundtrip to the same value).
+
+    :param str name: The name to check.
+
+    >>> from packaging.utils import is_normalized_name
+    >>> is_normalized_name("requests")
+    True
+    >>> is_normalized_name("Django")
+    False
+    """
+    return _normalized_regex.fullmatch(name) is not None
+
+
+def canonicalize_version(
+    version: Version | str, *, strip_trailing_zero: bool = True
+) -> str:
+    """Return a canonical form of a version as a string.
+
+    This function takes a string representing a package version (or a
+    :class:`~packaging.version.Version` instance), and returns the
+    normalized form of it. By default, it strips trailing zeros from
+    the release segment.
+
+    >>> from packaging.utils import canonicalize_version
+    >>> canonicalize_version('1.0.1')
+    '1.0.1'
+
+    Per PEP 625, versions may have multiple canonical forms, differing
+    only by trailing zeros.
+
+    >>> canonicalize_version('1.0.0')
+    '1'
+    >>> canonicalize_version('1.0.0', strip_trailing_zero=False)
+    '1.0.0'
+
+    Invalid versions are returned unaltered.
+
+    >>> canonicalize_version('foo bar baz')
+    'foo bar baz'
+
+    >>> canonicalize_version('1.4.0.0.0')
+    '1.4'
+    """
+    if isinstance(version, str):
+        try:
+            version = Version(version)
+        except InvalidVersion:
+            return str(version)
+    return str(_TrimmedRelease(version) if strip_trailing_zero else version)
+
+
+def parse_wheel_filename(
+    filename: str,
+    *,
+    validate_order: bool = False,
+) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]:
+    """
+    This function takes the filename of a wheel file, and parses it,
+    returning a tuple of name, version, build number, and tags.
+
+    The name part of the tuple is normalized and typed as
+    :class:`NormalizedName`. The version portion is an instance of
+    :class:`~packaging.version.Version`. The build number is ``()`` if
+    there is no build number in the wheel filename, otherwise a
+    two-item tuple of an integer for the leading digits and
+    a string for the rest of the build number. The tags portion is a
+    frozen set of :class:`~packaging.tags.Tag` instances (as the tag
+    string format allows multiple tags to be combined into a single
+    string).
+
+    If **validate_order** is true, compressed tag set components are
+    checked to be in sorted order as required by PEP 425.
+
+    :param str filename: The name of the wheel file.
+    :param bool validate_order: Check whether compressed tag set components
+        are in sorted order.
+    :raises InvalidWheelFilename: If the filename in question
+        does not follow the :ref:`wheel specification
+        `.
+
+    >>> from packaging.utils import parse_wheel_filename
+    >>> from packaging.tags import Tag
+    >>> from packaging.version import Version
+    >>> name, ver, build, tags = parse_wheel_filename("foo-1.0-py3-none-any.whl")
+    >>> name
+    'foo'
+    >>> ver == Version('1.0')
+    True
+    >>> tags == {Tag("py3", "none", "any")}
+    True
+    >>> not build
+    True
+
+    .. versionadded:: 26.1
+       The *validate_order* parameter.
+    """
+    if not filename.endswith(".whl"):
+        raise InvalidWheelFilename(
+            f"Invalid wheel filename (extension must be '.whl'): {filename!r}"
+        )
+
+    filename = filename[:-4]
+    dashes = filename.count("-")
+    if dashes not in (4, 5):
+        raise InvalidWheelFilename(
+            f"Invalid wheel filename (wrong number of parts): {filename!r}"
+        )
+
+    parts = filename.split("-", dashes - 2)
+    name_part = parts[0]
+    # See PEP 427 for the rules on escaping the project name.
+    if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None:
+        raise InvalidWheelFilename(f"Invalid project name: {filename!r}")
+    name = canonicalize_name(name_part)
+
+    try:
+        version = Version(parts[1])
+    except InvalidVersion as e:
+        raise InvalidWheelFilename(
+            f"Invalid wheel filename (invalid version): {filename!r}"
+        ) from e
+
+    if dashes == 5:
+        build_part = parts[2]
+        build_match = _build_tag_regex.match(build_part)
+        if build_match is None:
+            raise InvalidWheelFilename(
+                f"Invalid build number: {build_part} in {filename!r}"
+            )
+        build = cast("BuildTag", (int(build_match.group(1)), build_match.group(2)))
+    else:
+        build = ()
+    tag_str = parts[-1]
+    try:
+        tags = parse_tag(tag_str, validate_order=validate_order)
+    except UnsortedTagsError:
+        raise InvalidWheelFilename(
+            f"Invalid wheel filename (compressed tag set components must be in "
+            f"sorted order per PEP 425): {filename!r}"
+        ) from None
+    return (name, version, build, tags)
+
+
+def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]:
+    """
+    This function takes the filename of a sdist file (as specified
+    in the `Source distribution format`_ documentation), and parses
+    it, returning a tuple of the normalized name and version as
+    represented by an instance of :class:`~packaging.version.Version`.
+
+    :param str filename: The name of the sdist file.
+    :raises InvalidSdistFilename: If the filename does not end
+        with an sdist extension (``.zip`` or ``.tar.gz``), or if it does not
+        contain a dash separating the name and the version of the distribution.
+
+    >>> from packaging.utils import parse_sdist_filename
+    >>> from packaging.version import Version
+    >>> name, ver = parse_sdist_filename("foo-1.0.tar.gz")
+    >>> name
+    'foo'
+    >>> ver == Version('1.0')
+    True
+
+    .. _Source distribution format: https://packaging.python.org/specifications/source-distribution-format/#source-distribution-file-name
+    """
+    if filename.endswith(".tar.gz"):
+        file_stem = filename[: -len(".tar.gz")]
+    elif filename.endswith(".zip"):
+        file_stem = filename[: -len(".zip")]
+    else:
+        raise InvalidSdistFilename(
+            f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):"
+            f" {filename!r}"
+        )
+
+    # We are requiring a PEP 440 version, which cannot contain dashes,
+    # so we split on the last dash.
+    name_part, sep, version_part = file_stem.rpartition("-")
+    if not sep:
+        raise InvalidSdistFilename(f"Invalid sdist filename: {filename!r}")
+
+    name = canonicalize_name(name_part)
+
+    try:
+        version = Version(version_part)
+    except InvalidVersion as e:
+        raise InvalidSdistFilename(
+            f"Invalid sdist filename (invalid version): {filename!r}"
+        ) from e
+
+    return (name, version)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/packaging/version.py b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/version.py
new file mode 100644
index 0000000..905b47e
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/packaging/version.py
@@ -0,0 +1,1231 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+"""
+.. testsetup::
+
+    from pip._vendor.packaging.version import parse, normalize_pre, Version, _cmpkey
+"""
+
+from __future__ import annotations
+
+import re
+import sys
+import typing
+from typing import (
+    Any,
+    Callable,
+    Literal,
+    NamedTuple,
+    SupportsInt,
+    Tuple,
+    TypedDict,
+    Union,
+)
+
+if typing.TYPE_CHECKING:
+    from typing_extensions import Self, Unpack
+
+if sys.version_info >= (3, 13):  # pragma: no cover
+    from warnings import deprecated as _deprecated
+elif typing.TYPE_CHECKING:
+    from typing_extensions import deprecated as _deprecated
+else:  # pragma: no cover
+    import functools
+    import warnings
+
+    def _deprecated(message: str) -> object:
+        def decorator(func: Callable[[...], object]) -> object:
+            @functools.wraps(func)
+            def wrapper(*args: object, **kwargs: object) -> object:
+                warnings.warn(
+                    message,
+                    category=DeprecationWarning,
+                    stacklevel=2,
+                )
+                return func(*args, **kwargs)
+
+            return wrapper
+
+        return decorator
+
+
+_LETTER_NORMALIZATION = {
+    "alpha": "a",
+    "beta": "b",
+    "c": "rc",
+    "pre": "rc",
+    "preview": "rc",
+    "rev": "post",
+    "r": "post",
+}
+
+__all__ = ["VERSION_PATTERN", "InvalidVersion", "Version", "normalize_pre", "parse"]
+
+
+def __dir__() -> list[str]:
+    return __all__
+
+
+LocalType = Tuple[Union[int, str], ...]
+
+CmpLocalType = Tuple[Tuple[int, str], ...]
+CmpSuffix = Tuple[int, int, int, int, int, int]
+CmpKey = Union[
+    Tuple[int, Tuple[int, ...], CmpSuffix],
+    Tuple[int, Tuple[int, ...], CmpSuffix, CmpLocalType],
+]
+VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool]
+
+
+class _VersionReplace(TypedDict, total=False):
+    epoch: int | None
+    release: tuple[int, ...] | None
+    pre: tuple[str, int] | None
+    post: int | None
+    dev: int | None
+    local: str | None
+
+
+def normalize_pre(letter: str, /) -> str:
+    """Normalize the pre-release segment of a version string.
+
+    Returns a lowercase version of the string if not a known pre-release
+    identifier.
+
+    >>> normalize_pre('alpha')
+    'a'
+    >>> normalize_pre('BETA')
+    'b'
+    >>> normalize_pre('rc')
+    'rc'
+
+    :param letter:
+
+    .. versionadded:: 26.1
+    """
+    letter = letter.lower()
+    return _LETTER_NORMALIZATION.get(letter, letter)
+
+
+def parse(version: str) -> Version:
+    """Parse the given version string.
+
+    This is identical to the :class:`Version` constructor.
+
+    >>> parse('1.0.dev1')
+    
+
+    :param version: The version string to parse.
+    :raises InvalidVersion: When the version string is not a valid version.
+    """
+    return Version(version)
+
+
+class InvalidVersion(ValueError):
+    """Raised when a version string is not a valid version.
+
+    >>> Version("invalid")
+    Traceback (most recent call last):
+        ...
+    packaging.version.InvalidVersion: Invalid version: 'invalid'
+    """
+
+
+class _BaseVersion:
+    __slots__ = ()
+
+    # This can also be a normal member (see the packaging_legacy package);
+    # we are just requiring it to be readable. Actually defining a property
+    # has runtime effect on subclasses, so it's typing only.
+    if typing.TYPE_CHECKING:
+
+        @property
+        def _key(self) -> tuple[Any, ...]: ...
+
+    def __hash__(self) -> int:
+        return hash(self._key)
+
+    # Please keep the duplicated `isinstance` check
+    # in the six comparisons hereunder
+    # unless you find a way to avoid adding overhead function calls.
+    def __lt__(self, other: _BaseVersion) -> bool:
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return self._key < other._key
+
+    def __le__(self, other: _BaseVersion) -> bool:
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return self._key <= other._key
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return self._key == other._key
+
+    def __ge__(self, other: _BaseVersion) -> bool:
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return self._key >= other._key
+
+    def __gt__(self, other: _BaseVersion) -> bool:
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return self._key > other._key
+
+    def __ne__(self, other: object) -> bool:
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return self._key != other._key
+
+
+# Deliberately not anchored to the start and end of the string, to make it
+# easier for 3rd party code to reuse
+
+# Note that ++ doesn't behave identically on CPython and PyPy, so not using it here
+_VERSION_PATTERN = r"""
+    v?+                                                   # optional leading v
+    (?a:
+        (?:(?P[0-9]+)!)?+                          # epoch
+        (?P[0-9]+(?:\.[0-9]+)*+)                 # release segment
+        (?P
                                          # pre-release
+            [._-]?+
+            (?Palpha|a|beta|b|preview|pre|c|rc)
+            [._-]?+
+            (?P[0-9]+)?
+        )?+
+        (?P                                         # post release
+            (?:-(?P[0-9]+))
+            |
+            (?:
+                [._-]?
+                (?Ppost|rev|r)
+                [._-]?
+                (?P[0-9]+)?
+            )
+        )?+
+        (?P                                          # dev release
+            [._-]?+
+            (?Pdev)
+            [._-]?+
+            (?P[0-9]+)?
+        )?+
+    )
+    (?a:\+
+        (?P                                        # local version
+            [a-z0-9]+
+            (?:[._-][a-z0-9]+)*+
+        )
+    )?+
+"""
+
+_VERSION_PATTERN_OLD = _VERSION_PATTERN.replace("*+", "*").replace("?+", "?")
+
+# Possessive qualifiers were added in Python 3.11.
+# CPython 3.11.0-3.11.4 had a bug: https://github.com/python/cpython/pull/107795
+# Older PyPy also had a bug.
+VERSION_PATTERN = (
+    _VERSION_PATTERN_OLD
+    if (sys.implementation.name == "cpython" and sys.version_info < (3, 11, 5))
+    or (sys.implementation.name == "pypy" and sys.version_info < (3, 11, 13))
+    or sys.version_info < (3, 11)
+    else _VERSION_PATTERN
+)
+"""
+A string containing the regular expression used to match a valid version.
+
+The pattern is not anchored at either end, and is intended for embedding in larger
+expressions (for example, matching a version number as part of a file name). The
+regular expression should be compiled with the ``re.VERBOSE`` and ``re.IGNORECASE``
+flags set.
+
+.. versionchanged:: 26.0
+
+   The regex now uses possessive qualifiers on Python 3.11 if they are
+   supported (CPython 3.11.5+, PyPy 3.11.13+).
+
+:meta hide-value:
+"""
+
+
+# Validation pattern for local version in replace()
+_LOCAL_PATTERN = re.compile(r"[a-z0-9]+(?:[._-][a-z0-9]+)*", re.IGNORECASE | re.ASCII)
+
+# Fast path: If a version has only digits and dots then we
+# can skip the regex and parse it as a release segment
+_SIMPLE_VERSION_INDICATORS = frozenset(".0123456789")
+
+
+def _validate_epoch(value: object, /) -> int:
+    epoch = value or 0
+    if isinstance(epoch, int) and epoch >= 0:
+        return epoch
+    msg = f"epoch must be non-negative integer, got {epoch}"
+    raise InvalidVersion(msg)
+
+
+def _validate_release(value: object, /) -> tuple[int, ...]:
+    release = (0,) if value is None else value
+    if (
+        isinstance(release, tuple)
+        and len(release) > 0
+        and all(isinstance(i, int) and i >= 0 for i in release)
+    ):
+        return release
+    msg = f"release must be a non-empty tuple of non-negative integers, got {release}"
+    raise InvalidVersion(msg)
+
+
+def _validate_pre(value: object, /) -> tuple[Literal["a", "b", "rc"], int] | None:
+    if value is None:
+        return value
+    if isinstance(value, tuple) and len(value) == 2:
+        letter, number = value
+        letter = normalize_pre(letter)
+        if letter in {"a", "b", "rc"} and isinstance(number, int) and number >= 0:
+            # type checkers can't infer the Literal type here on letter
+            return (letter, number)  # type: ignore[return-value]
+    msg = f"pre must be a tuple of ('a'|'b'|'rc', non-negative int), got {value}"
+    raise InvalidVersion(msg)
+
+
+def _validate_post(value: object, /) -> tuple[Literal["post"], int] | None:
+    if value is None:
+        return value
+    if isinstance(value, int) and value >= 0:
+        return ("post", value)
+    msg = f"post must be non-negative integer, got {value}"
+    raise InvalidVersion(msg)
+
+
+def _validate_dev(value: object, /) -> tuple[Literal["dev"], int] | None:
+    if value is None:
+        return value
+    if isinstance(value, int) and value >= 0:
+        return ("dev", value)
+    msg = f"dev must be non-negative integer, got {value}"
+    raise InvalidVersion(msg)
+
+
+def _validate_local(value: object, /) -> LocalType | None:
+    if value is None:
+        return value
+    if isinstance(value, str) and _LOCAL_PATTERN.fullmatch(value):
+        return _parse_local_version(value)
+    msg = f"local must be a valid version string, got {value!r}"
+    raise InvalidVersion(msg)
+
+
+# Backward compatibility for internals before 26.0. Do not use.
+class _Version(NamedTuple):
+    epoch: int
+    release: tuple[int, ...]
+    dev: tuple[Literal["dev"], int] | None
+    pre: tuple[Literal["a", "b", "rc"], int] | None
+    post: tuple[Literal["post"], int] | None
+    local: LocalType | None
+
+
+class Version(_BaseVersion):
+    """This class abstracts handling of a project's versions.
+
+    A :class:`Version` instance is comparison aware and can be compared and
+    sorted using the standard Python interfaces.
+
+    >>> v1 = Version("1.0a5")
+    >>> v2 = Version("1.0")
+    >>> v1
+    
+    >>> v2
+    
+    >>> v1 < v2
+    True
+    >>> v1 == v2
+    False
+    >>> v1 > v2
+    False
+    >>> v1 >= v2
+    False
+    >>> v1 <= v2
+    True
+
+    :class:`Version` is immutable; use :meth:`__replace__` to change
+    part of a version.
+
+    Instances are safe to serialize with :mod:`pickle`. They use a stable
+    format so the same pickle can be loaded in future packaging releases.
+
+    .. versionchanged:: 26.2
+
+        Added a stable pickle format. Pickles created with packaging 26.2+ can
+        be unpickled with future releases.  Backward compatibility with pickles
+        from pip._vendor.packaging < 26.2 is supported but may be removed in a future
+        release.
+    """
+
+    __slots__ = (
+        "_dev",
+        "_epoch",
+        "_hash_cache",
+        "_key_cache",
+        "_local",
+        "_post",
+        "_pre",
+        "_release",
+    )
+    __match_args__ = ("_str",)
+    """
+    Pattern matching is supported on Python 3.10+.
+
+    .. versionadded:: 26.0
+
+    :meta hide-value:
+    """
+
+    _regex = re.compile(r"\s*" + VERSION_PATTERN + r"\s*", re.VERBOSE | re.IGNORECASE)
+
+    _epoch: int
+    _release: tuple[int, ...]
+    _dev: tuple[Literal["dev"], int] | None
+    _pre: tuple[Literal["a", "b", "rc"], int] | None
+    _post: tuple[Literal["post"], int] | None
+    _local: LocalType | None
+
+    _hash_cache: int | None
+    _key_cache: CmpKey | None
+
+    def __init__(self, version: str) -> None:
+        """Initialize a Version object.
+
+        :param version:
+            The string representation of a version which will be parsed and normalized
+            before use.
+        :raises InvalidVersion:
+            If the ``version`` does not conform to PEP 440 in any way then this
+            exception will be raised.
+        """
+        if _SIMPLE_VERSION_INDICATORS.issuperset(version):
+            try:
+                self._release = tuple(map(int, version.split(".")))
+            except ValueError:
+                # Empty parts (from "1..2", ".1", etc.) are invalid versions.
+                # Any other ValueError (e.g. int str-digits limit) should
+                # propagate to the caller.
+                if "" in version.split("."):
+                    raise InvalidVersion(f"Invalid version: {version!r}") from None
+                # TODO: remove "no cover" when Python 3.9 is dropped.
+                raise  # pragma: no cover
+
+            self._epoch = 0
+            self._pre = None
+            self._post = None
+            self._dev = None
+            self._local = None
+            self._key_cache = None
+            self._hash_cache = None
+            return
+
+        # Validate the version and parse it into pieces
+        match = self._regex.fullmatch(version)
+        if not match:
+            raise InvalidVersion(f"Invalid version: {version!r}")
+        self._epoch = int(match.group("epoch")) if match.group("epoch") else 0
+        self._release = tuple(map(int, match.group("release").split(".")))
+        # We can type ignore the assignments below because the regex guarantees
+        # the correct strings
+        self._pre = _parse_letter_version(match.group("pre_l"), match.group("pre_n"))  # type: ignore[assignment]
+        self._post = _parse_letter_version(  # type: ignore[assignment]
+            match.group("post_l"), match.group("post_n1") or match.group("post_n2")
+        )
+        self._dev = _parse_letter_version(match.group("dev_l"), match.group("dev_n"))  # type: ignore[assignment]
+        self._local = _parse_local_version(match.group("local"))
+
+        # Key which will be used for sorting
+        self._key_cache = None
+        self._hash_cache = None
+
+    @classmethod
+    def from_parts(
+        cls,
+        *,
+        epoch: int = 0,
+        release: tuple[int, ...],
+        pre: tuple[str, int] | None = None,
+        post: int | None = None,
+        dev: int | None = None,
+        local: str | None = None,
+    ) -> Self:
+        """
+        Return a new version composed of the various parts.
+
+        This allows you to build a version without going though a string and
+        running a regular expression. It normalizes pre-release strings. The
+        ``release=`` keyword argument is required.
+
+        >>> Version.from_parts(release=(1,2,3))
+        
+        >>> Version.from_parts(release=(0,1,0), pre=("b", 1))
+        
+
+        :param epoch:
+        :param release: This version tuple is required
+
+        .. versionadded:: 26.1
+        """
+        _epoch = _validate_epoch(epoch)
+        _release = _validate_release(release)
+        _pre = _validate_pre(pre) if pre is not None else None
+        _post = _validate_post(post) if post is not None else None
+        _dev = _validate_dev(dev) if dev is not None else None
+        _local = _validate_local(local) if local is not None else None
+
+        new_version = cls.__new__(cls)
+        new_version._key_cache = None
+        new_version._hash_cache = None
+        new_version._epoch = _epoch
+        new_version._release = _release
+        new_version._pre = _pre
+        new_version._post = _post
+        new_version._dev = _dev
+        new_version._local = _local
+
+        return new_version
+
+    def __replace__(self, **kwargs: Unpack[_VersionReplace]) -> Self:
+        """
+        __replace__(*, epoch=..., release=..., pre=..., post=..., dev=..., local=...)
+
+        Return a new version with parts replaced.
+
+        This returns a new version (unless no parts were changed). The
+        pre-release is normalized. Setting a value to ``None`` clears it.
+
+        >>> v = Version("1.2.3")
+        >>> v.__replace__(pre=("a", 1))
+        
+
+        :param int | None epoch:
+        :param tuple[int, ...] | None release:
+        :param tuple[str, int] | None pre:
+        :param int | None post:
+        :param int | None dev:
+        :param str | None local:
+
+        .. versionadded:: 26.0
+        .. versionchanged:: 26.1
+
+           The pre-release portion is now normalized.
+        """
+        epoch = _validate_epoch(kwargs["epoch"]) if "epoch" in kwargs else self._epoch
+        release = (
+            _validate_release(kwargs["release"])
+            if "release" in kwargs
+            else self._release
+        )
+        pre = _validate_pre(kwargs["pre"]) if "pre" in kwargs else self._pre
+        post = _validate_post(kwargs["post"]) if "post" in kwargs else self._post
+        dev = _validate_dev(kwargs["dev"]) if "dev" in kwargs else self._dev
+        local = _validate_local(kwargs["local"]) if "local" in kwargs else self._local
+
+        if (
+            epoch == self._epoch
+            and release == self._release
+            and pre == self._pre
+            and post == self._post
+            and dev == self._dev
+            and local == self._local
+        ):
+            return self
+
+        new_version = self.__class__.__new__(self.__class__)
+        new_version._key_cache = None
+        new_version._hash_cache = None
+        new_version._epoch = epoch
+        new_version._release = release
+        new_version._pre = pre
+        new_version._post = post
+        new_version._dev = dev
+        new_version._local = local
+
+        return new_version
+
+    @property
+    def _key(self) -> CmpKey:
+        if self._key_cache is None:
+            self._key_cache = _cmpkey(
+                self._epoch,
+                self._release,
+                self._pre,
+                self._post,
+                self._dev,
+                self._local,
+            )
+        return self._key_cache
+
+    # __hash__ must be defined when __eq__ is overridden,
+    # otherwise Python sets __hash__ to None.
+    def __hash__(self) -> int:
+        if (cached_hash := self._hash_cache) is not None:
+            return cached_hash
+
+        if (key := self._key_cache) is None:
+            self._key_cache = key = _cmpkey(
+                self._epoch,
+                self._release,
+                self._pre,
+                self._post,
+                self._dev,
+                self._local,
+            )
+        self._hash_cache = cached_hash = hash(key)
+        return cached_hash
+
+    # Override comparison methods to use direct _key_cache access
+    # This is faster than property access, especially before Python 3.12
+    def __lt__(self, other: _BaseVersion) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache < other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__lt__(other)
+
+    def __le__(self, other: _BaseVersion) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache <= other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__le__(other)
+
+    def __eq__(self, other: object) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache == other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__eq__(other)
+
+    def __ge__(self, other: _BaseVersion) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache >= other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__ge__(other)
+
+    def __gt__(self, other: _BaseVersion) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache > other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__gt__(other)
+
+    def __ne__(self, other: object) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache != other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__ne__(other)
+
+    def __getstate__(
+        self,
+    ) -> tuple[
+        int,
+        tuple[int, ...],
+        tuple[str, int] | None,
+        tuple[str, int] | None,
+        tuple[str, int] | None,
+        LocalType | None,
+    ]:
+        # Return state as a 6-item tuple for compactness:
+        #   (epoch, release, pre, post, dev, local)
+        # Cache members are excluded and will be recomputed on demand
+        return (
+            self._epoch,
+            self._release,
+            self._pre,
+            self._post,
+            self._dev,
+            self._local,
+        )
+
+    def __setstate__(self, state: object) -> None:
+        # Always discard cached values — they may contain stale references
+        # (e.g. packaging._structures.InfinityType from pre-26.1 pickles)
+        # and will be recomputed on demand from the core fields above.
+        self._key_cache = None
+        self._hash_cache = None
+
+        if isinstance(state, tuple):
+            if len(state) == 6:
+                # New format (26.2+): (epoch, release, pre, post, dev, local)
+                (
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                ) = state
+                return
+            if len(state) == 2:
+                # Format (packaging 26.0-26.1): (None, {slot: value}).
+                _, slot_dict = state
+                if isinstance(slot_dict, dict):
+                    self._epoch = slot_dict["_epoch"]
+                    self._release = slot_dict["_release"]
+                    self._pre = slot_dict.get("_pre")
+                    self._post = slot_dict.get("_post")
+                    self._dev = slot_dict.get("_dev")
+                    self._local = slot_dict.get("_local")
+                    return
+        if isinstance(state, dict):
+            # Old format (packaging <= 25.x, no __slots__): state is a plain
+            # dict with "_version" (_Version NamedTuple) and "_key" entries.
+            version_nt = state.get("_version")
+            if version_nt is not None:
+                self._epoch = version_nt.epoch
+                self._release = version_nt.release
+                self._pre = version_nt.pre
+                self._post = version_nt.post
+                self._dev = version_nt.dev
+                self._local = version_nt.local
+                return
+
+        raise TypeError(f"Cannot restore Version from {state!r}")
+
+    @property
+    @_deprecated("Version._version is private and will be removed soon")
+    def _version(self) -> _Version:
+        return _Version(
+            self._epoch, self._release, self._dev, self._pre, self._post, self._local
+        )
+
+    @_version.setter
+    @_deprecated("Version._version is private and will be removed soon")
+    def _version(self, value: _Version) -> None:
+        self._epoch = value.epoch
+        self._release = value.release
+        self._dev = value.dev
+        self._pre = value.pre
+        self._post = value.post
+        self._local = value.local
+        self._key_cache = None
+        self._hash_cache = None
+
+    def __repr__(self) -> str:
+        """A representation of the Version that shows all internal state.
+
+        >>> Version('1.0.0')
+        
+        """
+        return f"<{self.__class__.__name__}({str(self)!r})>"
+
+    def __str__(self) -> str:
+        """A string representation of the version that can be round-tripped.
+
+        >>> str(Version("1.0a5"))
+        '1.0a5'
+        """
+        # This is a hot function, so not calling self.base_version
+        version = ".".join(map(str, self.release))
+
+        # Epoch
+        if self.epoch:
+            version = f"{self.epoch}!{version}"
+
+        # Pre-release
+        if self.pre is not None:
+            version += "".join(map(str, self.pre))
+
+        # Post-release
+        if self.post is not None:
+            version += f".post{self.post}"
+
+        # Development release
+        if self.dev is not None:
+            version += f".dev{self.dev}"
+
+        # Local version segment
+        if self.local is not None:
+            version += f"+{self.local}"
+
+        return version
+
+    @property
+    def _str(self) -> str:
+        """Internal property for match_args"""
+        return str(self)
+
+    @property
+    def epoch(self) -> int:
+        """The epoch of the version.
+
+        >>> Version("2.0.0").epoch
+        0
+        >>> Version("1!2.0.0").epoch
+        1
+        """
+        return self._epoch
+
+    @property
+    def release(self) -> tuple[int, ...]:
+        """The components of the "release" segment of the version.
+
+        >>> Version("1.2.3").release
+        (1, 2, 3)
+        >>> Version("2.0.0").release
+        (2, 0, 0)
+        >>> Version("1!2.0.0.post0").release
+        (2, 0, 0)
+
+        Includes trailing zeroes but not the epoch or any pre-release / development /
+        post-release suffixes.
+        """
+        return self._release
+
+    @property
+    def pre(self) -> tuple[Literal["a", "b", "rc"], int] | None:
+        """The pre-release segment of the version.
+
+        >>> print(Version("1.2.3").pre)
+        None
+        >>> Version("1.2.3a1").pre
+        ('a', 1)
+        >>> Version("1.2.3b1").pre
+        ('b', 1)
+        >>> Version("1.2.3rc1").pre
+        ('rc', 1)
+        """
+        return self._pre
+
+    @property
+    def post(self) -> int | None:
+        """The post-release number of the version.
+
+        >>> print(Version("1.2.3").post)
+        None
+        >>> Version("1.2.3.post1").post
+        1
+        """
+        return self._post[1] if self._post else None
+
+    @property
+    def dev(self) -> int | None:
+        """The development number of the version.
+
+        >>> print(Version("1.2.3").dev)
+        None
+        >>> Version("1.2.3.dev1").dev
+        1
+        """
+        return self._dev[1] if self._dev else None
+
+    @property
+    def local(self) -> str | None:
+        """The local version segment of the version.
+
+        >>> print(Version("1.2.3").local)
+        None
+        >>> Version("1.2.3+abc").local
+        'abc'
+        """
+        if self._local:
+            return ".".join(str(x) for x in self._local)
+        else:
+            return None
+
+    @property
+    def public(self) -> str:
+        """The public portion of the version.
+
+        This returns a string. If you want a :class:`Version` again and care
+        about performance, use ``v.__replace__(local=None)`` instead.
+
+        >>> Version("1.2.3").public
+        '1.2.3'
+        >>> Version("1.2.3+abc").public
+        '1.2.3'
+        >>> Version("1!1.2.3dev1+abc").public
+        '1!1.2.3.dev1'
+        """
+        return str(self).split("+", 1)[0]
+
+    @property
+    def base_version(self) -> str:
+        """The "base version" of the version.
+
+        This returns a string. If you want a :class:`Version` again and care
+        about performance, use
+        ``v.__replace__(pre=None, post=None, dev=None, local=None)`` instead.
+
+        >>> Version("1.2.3").base_version
+        '1.2.3'
+        >>> Version("1.2.3+abc").base_version
+        '1.2.3'
+        >>> Version("1!1.2.3dev1+abc").base_version
+        '1!1.2.3'
+
+        The "base version" is the public version of the project without any pre or post
+        release markers.
+        """
+        release_segment = ".".join(map(str, self.release))
+        return f"{self.epoch}!{release_segment}" if self.epoch else release_segment
+
+    @property
+    def is_prerelease(self) -> bool:
+        """Whether this version is a pre-release.
+
+        >>> Version("1.2.3").is_prerelease
+        False
+        >>> Version("1.2.3a1").is_prerelease
+        True
+        >>> Version("1.2.3b1").is_prerelease
+        True
+        >>> Version("1.2.3rc1").is_prerelease
+        True
+        >>> Version("1.2.3dev1").is_prerelease
+        True
+        """
+        return self.dev is not None or self.pre is not None
+
+    @property
+    def is_postrelease(self) -> bool:
+        """Whether this version is a post-release.
+
+        >>> Version("1.2.3").is_postrelease
+        False
+        >>> Version("1.2.3.post1").is_postrelease
+        True
+        """
+        return self.post is not None
+
+    @property
+    def is_devrelease(self) -> bool:
+        """Whether this version is a development release.
+
+        >>> Version("1.2.3").is_devrelease
+        False
+        >>> Version("1.2.3.dev1").is_devrelease
+        True
+        """
+        return self.dev is not None
+
+    @property
+    def major(self) -> int:
+        """The first item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").major
+        1
+        """
+        return self.release[0] if len(self.release) >= 1 else 0
+
+    @property
+    def minor(self) -> int:
+        """The second item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").minor
+        2
+        >>> Version("1").minor
+        0
+        """
+        return self.release[1] if len(self.release) >= 2 else 0
+
+    @property
+    def micro(self) -> int:
+        """The third item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").micro
+        3
+        >>> Version("1").micro
+        0
+        """
+        return self.release[2] if len(self.release) >= 3 else 0
+
+
+class _TrimmedRelease(Version):
+    __slots__ = ()
+
+    def __init__(self, version: str | Version) -> None:
+        if isinstance(version, Version):
+            self._epoch = version._epoch
+            self._release = version._release
+            self._dev = version._dev
+            self._pre = version._pre
+            self._post = version._post
+            self._local = version._local
+            self._key_cache = version._key_cache
+            return
+        super().__init__(version)  # pragma: no cover
+
+    @property
+    def release(self) -> tuple[int, ...]:
+        """
+        Release segment without any trailing zeros.
+
+        >>> _TrimmedRelease('1.0.0').release
+        (1,)
+        >>> _TrimmedRelease('0.0').release
+        (0,)
+        """
+        # This leaves one 0.
+        rel = super().release
+        len_release = len(rel)
+        i = len_release
+        while i > 1 and rel[i - 1] == 0:
+            i -= 1
+        return rel if i == len_release else rel[:i]
+
+
+def _parse_letter_version(
+    letter: str | None, number: str | bytes | SupportsInt | None
+) -> tuple[str, int] | None:
+    if letter:
+        # We normalize any letters to their lower case form
+        letter = letter.lower()
+
+        # We consider some words to be alternate spellings of other words and
+        # in those cases we want to normalize the spellings to our preferred
+        # spelling.
+        letter = _LETTER_NORMALIZATION.get(letter, letter)
+
+        # We consider there to be an implicit 0 in a pre-release if there is
+        # not a numeral associated with it.
+        return letter, int(number or 0)
+
+    if number:
+        # We assume if we are given a number, but we are not given a letter
+        # then this is using the implicit post release syntax (e.g. 1.0-1)
+        return "post", int(number)
+
+    return None
+
+
+_local_version_separators = re.compile(r"[\._-]")
+
+
+def _parse_local_version(local: str | None) -> LocalType | None:
+    """
+    Takes a string like ``"abc.1.twelve"`` and turns it into
+    ``("abc", 1, "twelve")``.
+    """
+    if local is not None:
+        return tuple(
+            part.lower() if not part.isdigit() else int(part)
+            for part in _local_version_separators.split(local)
+        )
+    return None
+
+
+# Sort ranks for pre-release: dev-only < a < b < rc < stable (no pre-release).
+_PRE_RANK = {"a": 0, "b": 1, "rc": 2}
+_PRE_RANK_DEV_ONLY = -1  # sorts before a(0)
+_PRE_RANK_STABLE = 3  # sorts after rc(2)
+
+# In local version segments, strings sort before ints per PEP 440.
+_LOCAL_STR_RANK = -1  # sorts before all non-negative ints
+
+# Pre-computed suffix for stable releases (no pre, post, or dev segments).
+# See _cmpkey() for the suffix layout.
+_STABLE_SUFFIX = (_PRE_RANK_STABLE, 0, 0, 0, 1, 0)
+
+
+def _cmpkey(
+    epoch: int,
+    release: tuple[int, ...],
+    pre: tuple[str, int] | None,
+    post: tuple[str, int] | None,
+    dev: tuple[str, int] | None,
+    local: LocalType | None,
+) -> CmpKey:
+    """Build a comparison key for PEP 440 ordering.
+
+    Returns ``(epoch, release, suffix)`` or
+    ``(epoch, release, suffix, local)`` so that plain tuple
+    comparison gives the correct order.
+
+    Trailing zeros are stripped from the release so that ``1.0.0 == 1``.
+
+    The suffix is a flat 6-int tuple that encodes pre/post/dev:
+    ``(pre_rank, pre_n, post_rank, post_n, dev_rank, dev_n)``
+
+    pre_rank: dev-only=-1, a=0, b=1, rc=2, no-pre=3
+        Dev-only releases (no pre or post) get -1 so they sort before
+        any alpha/beta/rc.  Releases without a pre-release tag get 3
+        so they sort after rc.
+    post_rank: no-post=0, post=1
+        Releases without a post segment sort before those with one.
+    dev_rank: dev=0, no-dev=1
+        Releases without a dev segment sort after those with one.
+
+    Local segments use ``(n, "")`` for ints and ``(-1, s)`` for strings,
+    following PEP 440: strings sort before ints, strings compare
+    lexicographically, ints compare numerically, and shorter segments
+    sort before longer when prefixes match.  Versions without a local
+    segment sort before those with one (3-tuple < 4-tuple).
+
+    >>> _cmpkey(0, (1, 0, 0), None, None, None, None)
+    (0, (1,), (3, 0, 0, 0, 1, 0))
+    >>> _cmpkey(0, (1,), ("a", 1), None, None, None)
+    (0, (1,), (0, 1, 0, 0, 1, 0))
+    >>> _cmpkey(0, (1,), None, None, None, ("ubuntu", 1))
+    (0, (1,), (3, 0, 0, 0, 1, 0), ((-1, 'ubuntu'), (1, '')))
+    """
+    # Strip trailing zeros: 1.0.0 compares equal to 1.
+    len_release = len(release)
+    i = len_release
+    while i and release[i - 1] == 0:
+        i -= 1
+    trimmed = release if i == len_release else release[:i]
+
+    # Fast path: stable release with no local segment.
+    if pre is None and post is None and dev is None and local is None:
+        return epoch, trimmed, _STABLE_SUFFIX
+
+    if pre is None and post is None and dev is not None:
+        # dev-only (e.g. 1.0.dev1) sorts before all pre-releases.
+        pre_rank, pre_n = _PRE_RANK_DEV_ONLY, 0
+    elif pre is None:
+        pre_rank, pre_n = _PRE_RANK_STABLE, 0
+    else:
+        pre_rank, pre_n = _PRE_RANK[pre[0]], pre[1]
+
+    post_rank = 0 if post is None else 1
+    post_n = 0 if post is None else post[1]
+
+    dev_rank = 1 if dev is None else 0
+    dev_n = 0 if dev is None else dev[1]
+
+    suffix = (pre_rank, pre_n, post_rank, post_n, dev_rank, dev_n)
+
+    if local is None:
+        return epoch, trimmed, suffix
+
+    cmp_local: CmpLocalType = tuple(
+        (seg, "") if isinstance(seg, int) else (_LOCAL_STR_RANK, seg) for seg in local
+    )
+    return epoch, trimmed, suffix, cmp_local
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/LICENSE
new file mode 100644
index 0000000..1bb5a44
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/LICENSE
@@ -0,0 +1,17 @@
+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.
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/__init__.py
new file mode 100644
index 0000000..72f2b03
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/__init__.py
@@ -0,0 +1,3676 @@
+# TODO: Add Generic type annotations to initialized collections.
+# For now we'd simply use implicit Any/Unknown which would add redundant annotations
+# mypy: disable-error-code="var-annotated"
+"""
+Package resource API
+--------------------
+
+A resource is a logical file contained within a package, or a logical
+subdirectory thereof.  The package resource API expects resource names
+to have their path parts separated with ``/``, *not* whatever the local
+path separator is.  Do not use os.path operations to manipulate resource
+names being passed into the API.
+
+The package resource API is designed to work with normal filesystem packages,
+.egg files, and unpacked .egg files.  It can also work in a limited way with
+.zip files and with custom PEP 302 loaders that support the ``get_data()``
+method.
+
+This module is deprecated. Users are directed to :mod:`importlib.resources`,
+:mod:`importlib.metadata` and :pypi:`packaging` instead.
+"""
+
+from __future__ import annotations
+
+import sys
+
+if sys.version_info < (3, 8):  # noqa: UP036 # Check for unsupported versions
+    raise RuntimeError("Python 3.8 or later is required")
+
+import os
+import io
+import time
+import re
+import types
+from typing import (
+    Any,
+    Literal,
+    Dict,
+    Iterator,
+    Mapping,
+    MutableSequence,
+    NamedTuple,
+    NoReturn,
+    Tuple,
+    Union,
+    TYPE_CHECKING,
+    Protocol,
+    Callable,
+    Iterable,
+    TypeVar,
+    overload,
+)
+import zipfile
+import zipimport
+import warnings
+import stat
+import functools
+import pkgutil
+import operator
+import platform
+import collections
+import plistlib
+import email.parser
+import errno
+import tempfile
+import textwrap
+import inspect
+import ntpath
+import posixpath
+import importlib
+import importlib.abc
+import importlib.machinery
+from pkgutil import get_importer
+
+import _imp
+
+# capture these to bypass sandboxing
+from os import utime
+from os import open as os_open
+from os.path import isdir, split
+
+try:
+    from os import mkdir, rename, unlink
+
+    WRITE_SUPPORT = True
+except ImportError:
+    # no write support, probably under GAE
+    WRITE_SUPPORT = False
+
+from pip._internal.utils._jaraco_text import (
+    yield_lines,
+    drop_comment,
+    join_continuation,
+)
+from pip._vendor.packaging import markers as _packaging_markers
+from pip._vendor.packaging import requirements as _packaging_requirements
+from pip._vendor.packaging import utils as _packaging_utils
+from pip._vendor.packaging import version as _packaging_version
+from pip._vendor.platformdirs import user_cache_dir as _user_cache_dir
+
+if TYPE_CHECKING:
+    from _typeshed import BytesPath, StrPath, StrOrBytesPath
+    from typing_extensions import Self
+
+
+# Patch: Remove deprecation warning from vendored pkg_resources.
+# Setting PYTHONWARNINGS=error to verify builds produce no warnings
+# causes immediate exceptions.
+# See https://github.com/pypa/pip/issues/12243
+
+
+_T = TypeVar("_T")
+_DistributionT = TypeVar("_DistributionT", bound="Distribution")
+# Type aliases
+_NestedStr = Union[str, Iterable[Union[str, Iterable["_NestedStr"]]]]
+_InstallerTypeT = Callable[["Requirement"], "_DistributionT"]
+_InstallerType = Callable[["Requirement"], Union["Distribution", None]]
+_PkgReqType = Union[str, "Requirement"]
+_EPDistType = Union["Distribution", _PkgReqType]
+_MetadataType = Union["IResourceProvider", None]
+_ResolvedEntryPoint = Any  # Can be any attribute in the module
+_ResourceStream = Any  # TODO / Incomplete: A readable file-like object
+# Any object works, but let's indicate we expect something like a module (optionally has __loader__ or __file__)
+_ModuleLike = Union[object, types.ModuleType]
+# Any: Should be _ModuleLike but we end up with issues where _ModuleLike doesn't have _ZipLoaderModule's __loader__
+_ProviderFactoryType = Callable[[Any], "IResourceProvider"]
+_DistFinderType = Callable[[_T, str, bool], Iterable["Distribution"]]
+_NSHandlerType = Callable[[_T, str, str, types.ModuleType], Union[str, None]]
+_AdapterT = TypeVar(
+    "_AdapterT", _DistFinderType[Any], _ProviderFactoryType, _NSHandlerType[Any]
+)
+
+
+# Use _typeshed.importlib.LoaderProtocol once available https://github.com/python/typeshed/pull/11890
+class _LoaderProtocol(Protocol):
+    def load_module(self, fullname: str, /) -> types.ModuleType: ...
+
+
+class _ZipLoaderModule(Protocol):
+    __loader__: zipimport.zipimporter
+
+
+_PEP440_FALLBACK = re.compile(r"^v?(?P(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.I)
+
+
+class PEP440Warning(RuntimeWarning):
+    """
+    Used when there is an issue with a version or specifier not complying with
+    PEP 440.
+    """
+
+
+parse_version = _packaging_version.Version
+
+
+_state_vars: dict[str, str] = {}
+
+
+def _declare_state(vartype: str, varname: str, initial_value: _T) -> _T:
+    _state_vars[varname] = vartype
+    return initial_value
+
+
+def __getstate__() -> dict[str, Any]:
+    state = {}
+    g = globals()
+    for k, v in _state_vars.items():
+        state[k] = g['_sget_' + v](g[k])
+    return state
+
+
+def __setstate__(state: dict[str, Any]) -> dict[str, Any]:
+    g = globals()
+    for k, v in state.items():
+        g['_sset_' + _state_vars[k]](k, g[k], v)
+    return state
+
+
+def _sget_dict(val):
+    return val.copy()
+
+
+def _sset_dict(key, ob, state):
+    ob.clear()
+    ob.update(state)
+
+
+def _sget_object(val):
+    return val.__getstate__()
+
+
+def _sset_object(key, ob, state):
+    ob.__setstate__(state)
+
+
+_sget_none = _sset_none = lambda *args: None
+
+
+def get_supported_platform():
+    """Return this platform's maximum compatible version.
+
+    distutils.util.get_platform() normally reports the minimum version
+    of macOS that would be required to *use* extensions produced by
+    distutils.  But what we want when checking compatibility is to know the
+    version of macOS that we are *running*.  To allow usage of packages that
+    explicitly require a newer version of macOS, we must also know the
+    current version of the OS.
+
+    If this condition occurs for any other platform with a version in its
+    platform strings, this function should be extended accordingly.
+    """
+    plat = get_build_platform()
+    m = macosVersionString.match(plat)
+    if m is not None and sys.platform == "darwin":
+        try:
+            plat = 'macosx-%s-%s' % ('.'.join(_macos_vers()[:2]), m.group(3))
+        except ValueError:
+            # not macOS
+            pass
+    return plat
+
+
+__all__ = [
+    # Basic resource access and distribution/entry point discovery
+    'require',
+    'run_script',
+    'get_provider',
+    'get_distribution',
+    'load_entry_point',
+    'get_entry_map',
+    'get_entry_info',
+    'iter_entry_points',
+    'resource_string',
+    'resource_stream',
+    'resource_filename',
+    'resource_listdir',
+    'resource_exists',
+    'resource_isdir',
+    # Environmental control
+    'declare_namespace',
+    'working_set',
+    'add_activation_listener',
+    'find_distributions',
+    'set_extraction_path',
+    'cleanup_resources',
+    'get_default_cache',
+    # Primary implementation classes
+    'Environment',
+    'WorkingSet',
+    'ResourceManager',
+    'Distribution',
+    'Requirement',
+    'EntryPoint',
+    # Exceptions
+    'ResolutionError',
+    'VersionConflict',
+    'DistributionNotFound',
+    'UnknownExtra',
+    'ExtractionError',
+    # Warnings
+    'PEP440Warning',
+    # Parsing functions and string utilities
+    'parse_requirements',
+    'parse_version',
+    'safe_name',
+    'safe_version',
+    'get_platform',
+    'compatible_platforms',
+    'yield_lines',
+    'split_sections',
+    'safe_extra',
+    'to_filename',
+    'invalid_marker',
+    'evaluate_marker',
+    # filesystem utilities
+    'ensure_directory',
+    'normalize_path',
+    # Distribution "precedence" constants
+    'EGG_DIST',
+    'BINARY_DIST',
+    'SOURCE_DIST',
+    'CHECKOUT_DIST',
+    'DEVELOP_DIST',
+    # "Provider" interfaces, implementations, and registration/lookup APIs
+    'IMetadataProvider',
+    'IResourceProvider',
+    'FileMetadata',
+    'PathMetadata',
+    'EggMetadata',
+    'EmptyProvider',
+    'empty_provider',
+    'NullProvider',
+    'EggProvider',
+    'DefaultProvider',
+    'ZipProvider',
+    'register_finder',
+    'register_namespace_handler',
+    'register_loader_type',
+    'fixup_namespace_packages',
+    'get_importer',
+    # Warnings
+    'PkgResourcesDeprecationWarning',
+    # Deprecated/backward compatibility only
+    'run_main',
+    'AvailableDistributions',
+]
+
+
+class ResolutionError(Exception):
+    """Abstract base for dependency resolution errors"""
+
+    def __repr__(self):
+        return self.__class__.__name__ + repr(self.args)
+
+
+class VersionConflict(ResolutionError):
+    """
+    An already-installed version conflicts with the requested version.
+
+    Should be initialized with the installed Distribution and the requested
+    Requirement.
+    """
+
+    _template = "{self.dist} is installed but {self.req} is required"
+
+    @property
+    def dist(self) -> Distribution:
+        return self.args[0]
+
+    @property
+    def req(self) -> Requirement:
+        return self.args[1]
+
+    def report(self):
+        return self._template.format(**locals())
+
+    def with_context(self, required_by: set[Distribution | str]):
+        """
+        If required_by is non-empty, return a version of self that is a
+        ContextualVersionConflict.
+        """
+        if not required_by:
+            return self
+        args = self.args + (required_by,)
+        return ContextualVersionConflict(*args)
+
+
+class ContextualVersionConflict(VersionConflict):
+    """
+    A VersionConflict that accepts a third parameter, the set of the
+    requirements that required the installed Distribution.
+    """
+
+    _template = VersionConflict._template + ' by {self.required_by}'
+
+    @property
+    def required_by(self) -> set[str]:
+        return self.args[2]
+
+
+class DistributionNotFound(ResolutionError):
+    """A requested distribution was not found"""
+
+    _template = (
+        "The '{self.req}' distribution was not found "
+        "and is required by {self.requirers_str}"
+    )
+
+    @property
+    def req(self) -> Requirement:
+        return self.args[0]
+
+    @property
+    def requirers(self) -> set[str] | None:
+        return self.args[1]
+
+    @property
+    def requirers_str(self):
+        if not self.requirers:
+            return 'the application'
+        return ', '.join(self.requirers)
+
+    def report(self):
+        return self._template.format(**locals())
+
+    def __str__(self):
+        return self.report()
+
+
+class UnknownExtra(ResolutionError):
+    """Distribution doesn't have an "extra feature" of the given name"""
+
+
+_provider_factories: dict[type[_ModuleLike], _ProviderFactoryType] = {}
+
+PY_MAJOR = '{}.{}'.format(*sys.version_info)
+EGG_DIST = 3
+BINARY_DIST = 2
+SOURCE_DIST = 1
+CHECKOUT_DIST = 0
+DEVELOP_DIST = -1
+
+
+def register_loader_type(
+    loader_type: type[_ModuleLike], provider_factory: _ProviderFactoryType
+):
+    """Register `provider_factory` to make providers for `loader_type`
+
+    `loader_type` is the type or class of a PEP 302 ``module.__loader__``,
+    and `provider_factory` is a function that, passed a *module* object,
+    returns an ``IResourceProvider`` for that module.
+    """
+    _provider_factories[loader_type] = provider_factory
+
+
+@overload
+def get_provider(moduleOrReq: str) -> IResourceProvider: ...
+@overload
+def get_provider(moduleOrReq: Requirement) -> Distribution: ...
+def get_provider(moduleOrReq: str | Requirement) -> IResourceProvider | Distribution:
+    """Return an IResourceProvider for the named module or requirement"""
+    if isinstance(moduleOrReq, Requirement):
+        return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0]
+    try:
+        module = sys.modules[moduleOrReq]
+    except KeyError:
+        __import__(moduleOrReq)
+        module = sys.modules[moduleOrReq]
+    loader = getattr(module, '__loader__', None)
+    return _find_adapter(_provider_factories, loader)(module)
+
+
+@functools.lru_cache(maxsize=None)
+def _macos_vers():
+    version = platform.mac_ver()[0]
+    # fallback for MacPorts
+    if version == '':
+        plist = '/System/Library/CoreServices/SystemVersion.plist'
+        if os.path.exists(plist):
+            with open(plist, 'rb') as fh:
+                plist_content = plistlib.load(fh)
+            if 'ProductVersion' in plist_content:
+                version = plist_content['ProductVersion']
+    return version.split('.')
+
+
+def _macos_arch(machine):
+    return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine)
+
+
+def get_build_platform():
+    """Return this platform's string for platform-specific distributions
+
+    XXX Currently this is the same as ``distutils.util.get_platform()``, but it
+    needs some hacks for Linux and macOS.
+    """
+    from sysconfig import get_platform
+
+    plat = get_platform()
+    if sys.platform == "darwin" and not plat.startswith('macosx-'):
+        try:
+            version = _macos_vers()
+            machine = os.uname()[4].replace(" ", "_")
+            return "macosx-%d.%d-%s" % (
+                int(version[0]),
+                int(version[1]),
+                _macos_arch(machine),
+            )
+        except ValueError:
+            # if someone is running a non-Mac darwin system, this will fall
+            # through to the default implementation
+            pass
+    return plat
+
+
+macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)")
+darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)")
+# XXX backward compat
+get_platform = get_build_platform
+
+
+def compatible_platforms(provided: str | None, required: str | None):
+    """Can code for the `provided` platform run on the `required` platform?
+
+    Returns true if either platform is ``None``, or the platforms are equal.
+
+    XXX Needs compatibility checks for Linux and other unixy OSes.
+    """
+    if provided is None or required is None or provided == required:
+        # easy case
+        return True
+
+    # macOS special cases
+    reqMac = macosVersionString.match(required)
+    if reqMac:
+        provMac = macosVersionString.match(provided)
+
+        # is this a Mac package?
+        if not provMac:
+            # this is backwards compatibility for packages built before
+            # setuptools 0.6. All packages built after this point will
+            # use the new macOS designation.
+            provDarwin = darwinVersionString.match(provided)
+            if provDarwin:
+                dversion = int(provDarwin.group(1))
+                macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2))
+                if (
+                    dversion == 7
+                    and macosversion >= "10.3"
+                    or dversion == 8
+                    and macosversion >= "10.4"
+                ):
+                    return True
+            # egg isn't macOS or legacy darwin
+            return False
+
+        # are they the same major version and machine type?
+        if provMac.group(1) != reqMac.group(1) or provMac.group(3) != reqMac.group(3):
+            return False
+
+        # is the required OS major update >= the provided one?
+        if int(provMac.group(2)) > int(reqMac.group(2)):
+            return False
+
+        return True
+
+    # XXX Linux and other platforms' special cases should go here
+    return False
+
+
+@overload
+def get_distribution(dist: _DistributionT) -> _DistributionT: ...
+@overload
+def get_distribution(dist: _PkgReqType) -> Distribution: ...
+def get_distribution(dist: Distribution | _PkgReqType) -> Distribution:
+    """Return a current distribution object for a Requirement or string"""
+    if isinstance(dist, str):
+        dist = Requirement.parse(dist)
+    if isinstance(dist, Requirement):
+        # Bad type narrowing, dist has to be a Requirement here, so get_provider has to return Distribution
+        dist = get_provider(dist)  # type: ignore[assignment]
+    if not isinstance(dist, Distribution):
+        raise TypeError("Expected str, Requirement, or Distribution", dist)
+    return dist
+
+
+def load_entry_point(dist: _EPDistType, group: str, name: str) -> _ResolvedEntryPoint:
+    """Return `name` entry point of `group` for `dist` or raise ImportError"""
+    return get_distribution(dist).load_entry_point(group, name)
+
+
+@overload
+def get_entry_map(
+    dist: _EPDistType, group: None = None
+) -> dict[str, dict[str, EntryPoint]]: ...
+@overload
+def get_entry_map(dist: _EPDistType, group: str) -> dict[str, EntryPoint]: ...
+def get_entry_map(dist: _EPDistType, group: str | None = None):
+    """Return the entry point map for `group`, or the full entry map"""
+    return get_distribution(dist).get_entry_map(group)
+
+
+def get_entry_info(dist: _EPDistType, group: str, name: str):
+    """Return the EntryPoint object for `group`+`name`, or ``None``"""
+    return get_distribution(dist).get_entry_info(group, name)
+
+
+class IMetadataProvider(Protocol):
+    def has_metadata(self, name: str) -> bool:
+        """Does the package's distribution contain the named metadata?"""
+
+    def get_metadata(self, name: str) -> str:
+        """The named metadata resource as a string"""
+
+    def get_metadata_lines(self, name: str) -> Iterator[str]:
+        """Yield named metadata resource as list of non-blank non-comment lines
+
+        Leading and trailing whitespace is stripped from each line, and lines
+        with ``#`` as the first non-blank character are omitted."""
+
+    def metadata_isdir(self, name: str) -> bool:
+        """Is the named metadata a directory?  (like ``os.path.isdir()``)"""
+
+    def metadata_listdir(self, name: str) -> list[str]:
+        """List of metadata names in the directory (like ``os.listdir()``)"""
+
+    def run_script(self, script_name: str, namespace: dict[str, Any]) -> None:
+        """Execute the named script in the supplied namespace dictionary"""
+
+
+class IResourceProvider(IMetadataProvider, Protocol):
+    """An object that provides access to package resources"""
+
+    def get_resource_filename(
+        self, manager: ResourceManager, resource_name: str
+    ) -> str:
+        """Return a true filesystem path for `resource_name`
+
+        `manager` must be a ``ResourceManager``"""
+
+    def get_resource_stream(
+        self, manager: ResourceManager, resource_name: str
+    ) -> _ResourceStream:
+        """Return a readable file-like object for `resource_name`
+
+        `manager` must be a ``ResourceManager``"""
+
+    def get_resource_string(
+        self, manager: ResourceManager, resource_name: str
+    ) -> bytes:
+        """Return the contents of `resource_name` as :obj:`bytes`
+
+        `manager` must be a ``ResourceManager``"""
+
+    def has_resource(self, resource_name: str) -> bool:
+        """Does the package contain the named resource?"""
+
+    def resource_isdir(self, resource_name: str) -> bool:
+        """Is the named resource a directory?  (like ``os.path.isdir()``)"""
+
+    def resource_listdir(self, resource_name: str) -> list[str]:
+        """List of resource names in the directory (like ``os.listdir()``)"""
+
+
+class WorkingSet:
+    """A collection of active distributions on sys.path (or a similar list)"""
+
+    def __init__(self, entries: Iterable[str] | None = None):
+        """Create working set from list of path entries (default=sys.path)"""
+        self.entries: list[str] = []
+        self.entry_keys = {}
+        self.by_key = {}
+        self.normalized_to_canonical_keys = {}
+        self.callbacks = []
+
+        if entries is None:
+            entries = sys.path
+
+        for entry in entries:
+            self.add_entry(entry)
+
+    @classmethod
+    def _build_master(cls):
+        """
+        Prepare the master working set.
+        """
+        ws = cls()
+        try:
+            from __main__ import __requires__
+        except ImportError:
+            # The main program does not list any requirements
+            return ws
+
+        # ensure the requirements are met
+        try:
+            ws.require(__requires__)
+        except VersionConflict:
+            return cls._build_from_requirements(__requires__)
+
+        return ws
+
+    @classmethod
+    def _build_from_requirements(cls, req_spec):
+        """
+        Build a working set from a requirement spec. Rewrites sys.path.
+        """
+        # try it without defaults already on sys.path
+        # by starting with an empty path
+        ws = cls([])
+        reqs = parse_requirements(req_spec)
+        dists = ws.resolve(reqs, Environment())
+        for dist in dists:
+            ws.add(dist)
+
+        # add any missing entries from sys.path
+        for entry in sys.path:
+            if entry not in ws.entries:
+                ws.add_entry(entry)
+
+        # then copy back to sys.path
+        sys.path[:] = ws.entries
+        return ws
+
+    def add_entry(self, entry: str):
+        """Add a path item to ``.entries``, finding any distributions on it
+
+        ``find_distributions(entry, True)`` is used to find distributions
+        corresponding to the path entry, and they are added.  `entry` is
+        always appended to ``.entries``, even if it is already present.
+        (This is because ``sys.path`` can contain the same value more than
+        once, and the ``.entries`` of the ``sys.path`` WorkingSet should always
+        equal ``sys.path``.)
+        """
+        self.entry_keys.setdefault(entry, [])
+        self.entries.append(entry)
+        for dist in find_distributions(entry, True):
+            self.add(dist, entry, False)
+
+    def __contains__(self, dist: Distribution) -> bool:
+        """True if `dist` is the active distribution for its project"""
+        return self.by_key.get(dist.key) == dist
+
+    def find(self, req: Requirement) -> Distribution | None:
+        """Find a distribution matching requirement `req`
+
+        If there is an active distribution for the requested project, this
+        returns it as long as it meets the version requirement specified by
+        `req`.  But, if there is an active distribution for the project and it
+        does *not* meet the `req` requirement, ``VersionConflict`` is raised.
+        If there is no active distribution for the requested project, ``None``
+        is returned.
+        """
+        dist = self.by_key.get(req.key)
+
+        if dist is None:
+            canonical_key = self.normalized_to_canonical_keys.get(req.key)
+
+            if canonical_key is not None:
+                req.key = canonical_key
+                dist = self.by_key.get(canonical_key)
+
+        if dist is not None and dist not in req:
+            # XXX add more info
+            raise VersionConflict(dist, req)
+        return dist
+
+    def iter_entry_points(self, group: str, name: str | None = None):
+        """Yield entry point objects from `group` matching `name`
+
+        If `name` is None, yields all entry points in `group` from all
+        distributions in the working set, otherwise only ones matching
+        both `group` and `name` are yielded (in distribution order).
+        """
+        return (
+            entry
+            for dist in self
+            for entry in dist.get_entry_map(group).values()
+            if name is None or name == entry.name
+        )
+
+    def run_script(self, requires: str, script_name: str):
+        """Locate distribution for `requires` and run `script_name` script"""
+        ns = sys._getframe(1).f_globals
+        name = ns['__name__']
+        ns.clear()
+        ns['__name__'] = name
+        self.require(requires)[0].run_script(script_name, ns)
+
+    def __iter__(self) -> Iterator[Distribution]:
+        """Yield distributions for non-duplicate projects in the working set
+
+        The yield order is the order in which the items' path entries were
+        added to the working set.
+        """
+        seen = set()
+        for item in self.entries:
+            if item not in self.entry_keys:
+                # workaround a cache issue
+                continue
+
+            for key in self.entry_keys[item]:
+                if key not in seen:
+                    seen.add(key)
+                    yield self.by_key[key]
+
+    def add(
+        self,
+        dist: Distribution,
+        entry: str | None = None,
+        insert: bool = True,
+        replace: bool = False,
+    ):
+        """Add `dist` to working set, associated with `entry`
+
+        If `entry` is unspecified, it defaults to the ``.location`` of `dist`.
+        On exit from this routine, `entry` is added to the end of the working
+        set's ``.entries`` (if it wasn't already present).
+
+        `dist` is only added to the working set if it's for a project that
+        doesn't already have a distribution in the set, unless `replace=True`.
+        If it's added, any callbacks registered with the ``subscribe()`` method
+        will be called.
+        """
+        if insert:
+            dist.insert_on(self.entries, entry, replace=replace)
+
+        if entry is None:
+            entry = dist.location
+        keys = self.entry_keys.setdefault(entry, [])
+        keys2 = self.entry_keys.setdefault(dist.location, [])
+        if not replace and dist.key in self.by_key:
+            # ignore hidden distros
+            return
+
+        self.by_key[dist.key] = dist
+        normalized_name = _packaging_utils.canonicalize_name(dist.key)
+        self.normalized_to_canonical_keys[normalized_name] = dist.key
+        if dist.key not in keys:
+            keys.append(dist.key)
+        if dist.key not in keys2:
+            keys2.append(dist.key)
+        self._added_new(dist)
+
+    @overload
+    def resolve(
+        self,
+        requirements: Iterable[Requirement],
+        env: Environment | None,
+        installer: _InstallerTypeT[_DistributionT],
+        replace_conflicting: bool = False,
+        extras: tuple[str, ...] | None = None,
+    ) -> list[_DistributionT]: ...
+    @overload
+    def resolve(
+        self,
+        requirements: Iterable[Requirement],
+        env: Environment | None = None,
+        *,
+        installer: _InstallerTypeT[_DistributionT],
+        replace_conflicting: bool = False,
+        extras: tuple[str, ...] | None = None,
+    ) -> list[_DistributionT]: ...
+    @overload
+    def resolve(
+        self,
+        requirements: Iterable[Requirement],
+        env: Environment | None = None,
+        installer: _InstallerType | None = None,
+        replace_conflicting: bool = False,
+        extras: tuple[str, ...] | None = None,
+    ) -> list[Distribution]: ...
+    def resolve(
+        self,
+        requirements: Iterable[Requirement],
+        env: Environment | None = None,
+        installer: _InstallerType | None | _InstallerTypeT[_DistributionT] = None,
+        replace_conflicting: bool = False,
+        extras: tuple[str, ...] | None = None,
+    ) -> list[Distribution] | list[_DistributionT]:
+        """List all distributions needed to (recursively) meet `requirements`
+
+        `requirements` must be a sequence of ``Requirement`` objects.  `env`,
+        if supplied, should be an ``Environment`` instance.  If
+        not supplied, it defaults to all distributions available within any
+        entry or distribution in the working set.  `installer`, if supplied,
+        will be invoked with each requirement that cannot be met by an
+        already-installed distribution; it should return a ``Distribution`` or
+        ``None``.
+
+        Unless `replace_conflicting=True`, raises a VersionConflict exception
+        if
+        any requirements are found on the path that have the correct name but
+        the wrong version.  Otherwise, if an `installer` is supplied it will be
+        invoked to obtain the correct version of the requirement and activate
+        it.
+
+        `extras` is a list of the extras to be used with these requirements.
+        This is important because extra requirements may look like `my_req;
+        extra = "my_extra"`, which would otherwise be interpreted as a purely
+        optional requirement.  Instead, we want to be able to assert that these
+        requirements are truly required.
+        """
+
+        # set up the stack
+        requirements = list(requirements)[::-1]
+        # set of processed requirements
+        processed = set()
+        # key -> dist
+        best = {}
+        to_activate = []
+
+        req_extras = _ReqExtras()
+
+        # Mapping of requirement to set of distributions that required it;
+        # useful for reporting info about conflicts.
+        required_by = collections.defaultdict(set)
+
+        while requirements:
+            # process dependencies breadth-first
+            req = requirements.pop(0)
+            if req in processed:
+                # Ignore cyclic or redundant dependencies
+                continue
+
+            if not req_extras.markers_pass(req, extras):
+                continue
+
+            dist = self._resolve_dist(
+                req, best, replace_conflicting, env, installer, required_by, to_activate
+            )
+
+            # push the new requirements onto the stack
+            new_requirements = dist.requires(req.extras)[::-1]
+            requirements.extend(new_requirements)
+
+            # Register the new requirements needed by req
+            for new_requirement in new_requirements:
+                required_by[new_requirement].add(req.project_name)
+                req_extras[new_requirement] = req.extras
+
+            processed.add(req)
+
+        # return list of distros to activate
+        return to_activate
+
+    def _resolve_dist(
+        self, req, best, replace_conflicting, env, installer, required_by, to_activate
+    ) -> Distribution:
+        dist = best.get(req.key)
+        if dist is None:
+            # Find the best distribution and add it to the map
+            dist = self.by_key.get(req.key)
+            if dist is None or (dist not in req and replace_conflicting):
+                ws = self
+                if env is None:
+                    if dist is None:
+                        env = Environment(self.entries)
+                    else:
+                        # Use an empty environment and workingset to avoid
+                        # any further conflicts with the conflicting
+                        # distribution
+                        env = Environment([])
+                        ws = WorkingSet([])
+                dist = best[req.key] = env.best_match(
+                    req, ws, installer, replace_conflicting=replace_conflicting
+                )
+                if dist is None:
+                    requirers = required_by.get(req, None)
+                    raise DistributionNotFound(req, requirers)
+            to_activate.append(dist)
+        if dist not in req:
+            # Oops, the "best" so far conflicts with a dependency
+            dependent_req = required_by[req]
+            raise VersionConflict(dist, req).with_context(dependent_req)
+        return dist
+
+    @overload
+    def find_plugins(
+        self,
+        plugin_env: Environment,
+        full_env: Environment | None,
+        installer: _InstallerTypeT[_DistributionT],
+        fallback: bool = True,
+    ) -> tuple[list[_DistributionT], dict[Distribution, Exception]]: ...
+    @overload
+    def find_plugins(
+        self,
+        plugin_env: Environment,
+        full_env: Environment | None = None,
+        *,
+        installer: _InstallerTypeT[_DistributionT],
+        fallback: bool = True,
+    ) -> tuple[list[_DistributionT], dict[Distribution, Exception]]: ...
+    @overload
+    def find_plugins(
+        self,
+        plugin_env: Environment,
+        full_env: Environment | None = None,
+        installer: _InstallerType | None = None,
+        fallback: bool = True,
+    ) -> tuple[list[Distribution], dict[Distribution, Exception]]: ...
+    def find_plugins(
+        self,
+        plugin_env: Environment,
+        full_env: Environment | None = None,
+        installer: _InstallerType | None | _InstallerTypeT[_DistributionT] = None,
+        fallback: bool = True,
+    ) -> tuple[
+        list[Distribution] | list[_DistributionT],
+        dict[Distribution, Exception],
+    ]:
+        """Find all activatable distributions in `plugin_env`
+
+        Example usage::
+
+            distributions, errors = working_set.find_plugins(
+                Environment(plugin_dirlist)
+            )
+            # add plugins+libs to sys.path
+            map(working_set.add, distributions)
+            # display errors
+            print('Could not load', errors)
+
+        The `plugin_env` should be an ``Environment`` instance that contains
+        only distributions that are in the project's "plugin directory" or
+        directories. The `full_env`, if supplied, should be an ``Environment``
+        contains all currently-available distributions.  If `full_env` is not
+        supplied, one is created automatically from the ``WorkingSet`` this
+        method is called on, which will typically mean that every directory on
+        ``sys.path`` will be scanned for distributions.
+
+        `installer` is a standard installer callback as used by the
+        ``resolve()`` method. The `fallback` flag indicates whether we should
+        attempt to resolve older versions of a plugin if the newest version
+        cannot be resolved.
+
+        This method returns a 2-tuple: (`distributions`, `error_info`), where
+        `distributions` is a list of the distributions found in `plugin_env`
+        that were loadable, along with any other distributions that are needed
+        to resolve their dependencies.  `error_info` is a dictionary mapping
+        unloadable plugin distributions to an exception instance describing the
+        error that occurred. Usually this will be a ``DistributionNotFound`` or
+        ``VersionConflict`` instance.
+        """
+
+        plugin_projects = list(plugin_env)
+        # scan project names in alphabetic order
+        plugin_projects.sort()
+
+        error_info: dict[Distribution, Exception] = {}
+        distributions: dict[Distribution, Exception | None] = {}
+
+        if full_env is None:
+            env = Environment(self.entries)
+            env += plugin_env
+        else:
+            env = full_env + plugin_env
+
+        shadow_set = self.__class__([])
+        # put all our entries in shadow_set
+        list(map(shadow_set.add, self))
+
+        for project_name in plugin_projects:
+            for dist in plugin_env[project_name]:
+                req = [dist.as_requirement()]
+
+                try:
+                    resolvees = shadow_set.resolve(req, env, installer)
+
+                except ResolutionError as v:
+                    # save error info
+                    error_info[dist] = v
+                    if fallback:
+                        # try the next older version of project
+                        continue
+                    else:
+                        # give up on this project, keep going
+                        break
+
+                else:
+                    list(map(shadow_set.add, resolvees))
+                    distributions.update(dict.fromkeys(resolvees))
+
+                    # success, no need to try any more versions of this project
+                    break
+
+        sorted_distributions = list(distributions)
+        sorted_distributions.sort()
+
+        return sorted_distributions, error_info
+
+    def require(self, *requirements: _NestedStr):
+        """Ensure that distributions matching `requirements` are activated
+
+        `requirements` must be a string or a (possibly-nested) sequence
+        thereof, specifying the distributions and versions required.  The
+        return value is a sequence of the distributions that needed to be
+        activated to fulfill the requirements; all relevant distributions are
+        included, even if they were already activated in this working set.
+        """
+        needed = self.resolve(parse_requirements(requirements))
+
+        for dist in needed:
+            self.add(dist)
+
+        return needed
+
+    def subscribe(
+        self, callback: Callable[[Distribution], object], existing: bool = True
+    ):
+        """Invoke `callback` for all distributions
+
+        If `existing=True` (default),
+        call on all existing ones, as well.
+        """
+        if callback in self.callbacks:
+            return
+        self.callbacks.append(callback)
+        if not existing:
+            return
+        for dist in self:
+            callback(dist)
+
+    def _added_new(self, dist):
+        for callback in self.callbacks:
+            callback(dist)
+
+    def __getstate__(self):
+        return (
+            self.entries[:],
+            self.entry_keys.copy(),
+            self.by_key.copy(),
+            self.normalized_to_canonical_keys.copy(),
+            self.callbacks[:],
+        )
+
+    def __setstate__(self, e_k_b_n_c):
+        entries, keys, by_key, normalized_to_canonical_keys, callbacks = e_k_b_n_c
+        self.entries = entries[:]
+        self.entry_keys = keys.copy()
+        self.by_key = by_key.copy()
+        self.normalized_to_canonical_keys = normalized_to_canonical_keys.copy()
+        self.callbacks = callbacks[:]
+
+
+class _ReqExtras(Dict["Requirement", Tuple[str, ...]]):
+    """
+    Map each requirement to the extras that demanded it.
+    """
+
+    def markers_pass(self, req: Requirement, extras: tuple[str, ...] | None = None):
+        """
+        Evaluate markers for req against each extra that
+        demanded it.
+
+        Return False if the req has a marker and fails
+        evaluation. Otherwise, return True.
+        """
+        extra_evals = (
+            req.marker.evaluate({'extra': extra})
+            for extra in self.get(req, ()) + (extras or (None,))
+        )
+        return not req.marker or any(extra_evals)
+
+
+class Environment:
+    """Searchable snapshot of distributions on a search path"""
+
+    def __init__(
+        self,
+        search_path: Iterable[str] | None = None,
+        platform: str | None = get_supported_platform(),
+        python: str | None = PY_MAJOR,
+    ):
+        """Snapshot distributions available on a search path
+
+        Any distributions found on `search_path` are added to the environment.
+        `search_path` should be a sequence of ``sys.path`` items.  If not
+        supplied, ``sys.path`` is used.
+
+        `platform` is an optional string specifying the name of the platform
+        that platform-specific distributions must be compatible with.  If
+        unspecified, it defaults to the current platform.  `python` is an
+        optional string naming the desired version of Python (e.g. ``'3.6'``);
+        it defaults to the current version.
+
+        You may explicitly set `platform` (and/or `python`) to ``None`` if you
+        wish to map *all* distributions, not just those compatible with the
+        running platform or Python version.
+        """
+        self._distmap = {}
+        self.platform = platform
+        self.python = python
+        self.scan(search_path)
+
+    def can_add(self, dist: Distribution):
+        """Is distribution `dist` acceptable for this environment?
+
+        The distribution must match the platform and python version
+        requirements specified when this environment was created, or False
+        is returned.
+        """
+        py_compat = (
+            self.python is None
+            or dist.py_version is None
+            or dist.py_version == self.python
+        )
+        return py_compat and compatible_platforms(dist.platform, self.platform)
+
+    def remove(self, dist: Distribution):
+        """Remove `dist` from the environment"""
+        self._distmap[dist.key].remove(dist)
+
+    def scan(self, search_path: Iterable[str] | None = None):
+        """Scan `search_path` for distributions usable in this environment
+
+        Any distributions found are added to the environment.
+        `search_path` should be a sequence of ``sys.path`` items.  If not
+        supplied, ``sys.path`` is used.  Only distributions conforming to
+        the platform/python version defined at initialization are added.
+        """
+        if search_path is None:
+            search_path = sys.path
+
+        for item in search_path:
+            for dist in find_distributions(item):
+                self.add(dist)
+
+    def __getitem__(self, project_name: str) -> list[Distribution]:
+        """Return a newest-to-oldest list of distributions for `project_name`
+
+        Uses case-insensitive `project_name` comparison, assuming all the
+        project's distributions use their project's name converted to all
+        lowercase as their key.
+
+        """
+        distribution_key = project_name.lower()
+        return self._distmap.get(distribution_key, [])
+
+    def add(self, dist: Distribution):
+        """Add `dist` if we ``can_add()`` it and it has not already been added"""
+        if self.can_add(dist) and dist.has_version():
+            dists = self._distmap.setdefault(dist.key, [])
+            if dist not in dists:
+                dists.append(dist)
+                dists.sort(key=operator.attrgetter('hashcmp'), reverse=True)
+
+    @overload
+    def best_match(
+        self,
+        req: Requirement,
+        working_set: WorkingSet,
+        installer: _InstallerTypeT[_DistributionT],
+        replace_conflicting: bool = False,
+    ) -> _DistributionT: ...
+    @overload
+    def best_match(
+        self,
+        req: Requirement,
+        working_set: WorkingSet,
+        installer: _InstallerType | None = None,
+        replace_conflicting: bool = False,
+    ) -> Distribution | None: ...
+    def best_match(
+        self,
+        req: Requirement,
+        working_set: WorkingSet,
+        installer: _InstallerType | None | _InstallerTypeT[_DistributionT] = None,
+        replace_conflicting: bool = False,
+    ) -> Distribution | None:
+        """Find distribution best matching `req` and usable on `working_set`
+
+        This calls the ``find(req)`` method of the `working_set` to see if a
+        suitable distribution is already active.  (This may raise
+        ``VersionConflict`` if an unsuitable version of the project is already
+        active in the specified `working_set`.)  If a suitable distribution
+        isn't active, this method returns the newest distribution in the
+        environment that meets the ``Requirement`` in `req`.  If no suitable
+        distribution is found, and `installer` is supplied, then the result of
+        calling the environment's ``obtain(req, installer)`` method will be
+        returned.
+        """
+        try:
+            dist = working_set.find(req)
+        except VersionConflict:
+            if not replace_conflicting:
+                raise
+            dist = None
+        if dist is not None:
+            return dist
+        for dist in self[req.key]:
+            if dist in req:
+                return dist
+        # try to download/install
+        return self.obtain(req, installer)
+
+    @overload
+    def obtain(
+        self,
+        requirement: Requirement,
+        installer: _InstallerTypeT[_DistributionT],
+    ) -> _DistributionT: ...
+    @overload
+    def obtain(
+        self,
+        requirement: Requirement,
+        installer: Callable[[Requirement], None] | None = None,
+    ) -> None: ...
+    @overload
+    def obtain(
+        self,
+        requirement: Requirement,
+        installer: _InstallerType | None = None,
+    ) -> Distribution | None: ...
+    def obtain(
+        self,
+        requirement: Requirement,
+        installer: Callable[[Requirement], None]
+        | _InstallerType
+        | None
+        | _InstallerTypeT[_DistributionT] = None,
+    ) -> Distribution | None:
+        """Obtain a distribution matching `requirement` (e.g. via download)
+
+        Obtain a distro that matches requirement (e.g. via download).  In the
+        base ``Environment`` class, this routine just returns
+        ``installer(requirement)``, unless `installer` is None, in which case
+        None is returned instead.  This method is a hook that allows subclasses
+        to attempt other ways of obtaining a distribution before falling back
+        to the `installer` argument."""
+        return installer(requirement) if installer else None
+
+    def __iter__(self) -> Iterator[str]:
+        """Yield the unique project names of the available distributions"""
+        for key in self._distmap.keys():
+            if self[key]:
+                yield key
+
+    def __iadd__(self, other: Distribution | Environment):
+        """In-place addition of a distribution or environment"""
+        if isinstance(other, Distribution):
+            self.add(other)
+        elif isinstance(other, Environment):
+            for project in other:
+                for dist in other[project]:
+                    self.add(dist)
+        else:
+            raise TypeError("Can't add %r to environment" % (other,))
+        return self
+
+    def __add__(self, other: Distribution | Environment):
+        """Add an environment or distribution to an environment"""
+        new = self.__class__([], platform=None, python=None)
+        for env in self, other:
+            new += env
+        return new
+
+
+# XXX backward compatibility
+AvailableDistributions = Environment
+
+
+class ExtractionError(RuntimeError):
+    """An error occurred extracting a resource
+
+    The following attributes are available from instances of this exception:
+
+    manager
+        The resource manager that raised this exception
+
+    cache_path
+        The base directory for resource extraction
+
+    original_error
+        The exception instance that caused extraction to fail
+    """
+
+    manager: ResourceManager
+    cache_path: str
+    original_error: BaseException | None
+
+
+class ResourceManager:
+    """Manage resource extraction and packages"""
+
+    extraction_path: str | None = None
+
+    def __init__(self):
+        self.cached_files = {}
+
+    def resource_exists(self, package_or_requirement: _PkgReqType, resource_name: str):
+        """Does the named resource exist?"""
+        return get_provider(package_or_requirement).has_resource(resource_name)
+
+    def resource_isdir(self, package_or_requirement: _PkgReqType, resource_name: str):
+        """Is the named resource an existing directory?"""
+        return get_provider(package_or_requirement).resource_isdir(resource_name)
+
+    def resource_filename(
+        self, package_or_requirement: _PkgReqType, resource_name: str
+    ):
+        """Return a true filesystem path for specified resource"""
+        return get_provider(package_or_requirement).get_resource_filename(
+            self, resource_name
+        )
+
+    def resource_stream(self, package_or_requirement: _PkgReqType, resource_name: str):
+        """Return a readable file-like object for specified resource"""
+        return get_provider(package_or_requirement).get_resource_stream(
+            self, resource_name
+        )
+
+    def resource_string(
+        self, package_or_requirement: _PkgReqType, resource_name: str
+    ) -> bytes:
+        """Return specified resource as :obj:`bytes`"""
+        return get_provider(package_or_requirement).get_resource_string(
+            self, resource_name
+        )
+
+    def resource_listdir(self, package_or_requirement: _PkgReqType, resource_name: str):
+        """List the contents of the named resource directory"""
+        return get_provider(package_or_requirement).resource_listdir(resource_name)
+
+    def extraction_error(self) -> NoReturn:
+        """Give an error message for problems extracting file(s)"""
+
+        old_exc = sys.exc_info()[1]
+        cache_path = self.extraction_path or get_default_cache()
+
+        tmpl = textwrap.dedent(
+            """
+            Can't extract file(s) to egg cache
+
+            The following error occurred while trying to extract file(s)
+            to the Python egg cache:
+
+              {old_exc}
+
+            The Python egg cache directory is currently set to:
+
+              {cache_path}
+
+            Perhaps your account does not have write access to this directory?
+            You can change the cache directory by setting the PYTHON_EGG_CACHE
+            environment variable to point to an accessible directory.
+            """
+        ).lstrip()
+        err = ExtractionError(tmpl.format(**locals()))
+        err.manager = self
+        err.cache_path = cache_path
+        err.original_error = old_exc
+        raise err
+
+    def get_cache_path(self, archive_name: str, names: Iterable[StrPath] = ()):
+        """Return absolute location in cache for `archive_name` and `names`
+
+        The parent directory of the resulting path will be created if it does
+        not already exist.  `archive_name` should be the base filename of the
+        enclosing egg (which may not be the name of the enclosing zipfile!),
+        including its ".egg" extension.  `names`, if provided, should be a
+        sequence of path name parts "under" the egg's extraction location.
+
+        This method should only be called by resource providers that need to
+        obtain an extraction location, and only for names they intend to
+        extract, as it tracks the generated names for possible cleanup later.
+        """
+        extract_path = self.extraction_path or get_default_cache()
+        target_path = os.path.join(extract_path, archive_name + '-tmp', *names)
+        try:
+            _bypass_ensure_directory(target_path)
+        except Exception:
+            self.extraction_error()
+
+        self._warn_unsafe_extraction_path(extract_path)
+
+        self.cached_files[target_path] = True
+        return target_path
+
+    @staticmethod
+    def _warn_unsafe_extraction_path(path):
+        """
+        If the default extraction path is overridden and set to an insecure
+        location, such as /tmp, it opens up an opportunity for an attacker to
+        replace an extracted file with an unauthorized payload. Warn the user
+        if a known insecure location is used.
+
+        See Distribute #375 for more details.
+        """
+        if os.name == 'nt' and not path.startswith(os.environ['windir']):
+            # On Windows, permissions are generally restrictive by default
+            #  and temp directories are not writable by other users, so
+            #  bypass the warning.
+            return
+        mode = os.stat(path).st_mode
+        if mode & stat.S_IWOTH or mode & stat.S_IWGRP:
+            msg = (
+                "Extraction path is writable by group/others "
+                "and vulnerable to attack when "
+                "used with get_resource_filename ({path}). "
+                "Consider a more secure "
+                "location (set with .set_extraction_path or the "
+                "PYTHON_EGG_CACHE environment variable)."
+            ).format(**locals())
+            warnings.warn(msg, UserWarning)
+
+    def postprocess(self, tempname: StrOrBytesPath, filename: StrOrBytesPath):
+        """Perform any platform-specific postprocessing of `tempname`
+
+        This is where Mac header rewrites should be done; other platforms don't
+        have anything special they should do.
+
+        Resource providers should call this method ONLY after successfully
+        extracting a compressed resource.  They must NOT call it on resources
+        that are already in the filesystem.
+
+        `tempname` is the current (temporary) name of the file, and `filename`
+        is the name it will be renamed to by the caller after this routine
+        returns.
+        """
+
+        if os.name == 'posix':
+            # Make the resource executable
+            mode = ((os.stat(tempname).st_mode) | 0o555) & 0o7777
+            os.chmod(tempname, mode)
+
+    def set_extraction_path(self, path: str):
+        """Set the base path where resources will be extracted to, if needed.
+
+        If you do not call this routine before any extractions take place, the
+        path defaults to the return value of ``get_default_cache()``.  (Which
+        is based on the ``PYTHON_EGG_CACHE`` environment variable, with various
+        platform-specific fallbacks.  See that routine's documentation for more
+        details.)
+
+        Resources are extracted to subdirectories of this path based upon
+        information given by the ``IResourceProvider``.  You may set this to a
+        temporary directory, but then you must call ``cleanup_resources()`` to
+        delete the extracted files when done.  There is no guarantee that
+        ``cleanup_resources()`` will be able to remove all extracted files.
+
+        (Note: you may not change the extraction path for a given resource
+        manager once resources have been extracted, unless you first call
+        ``cleanup_resources()``.)
+        """
+        if self.cached_files:
+            raise ValueError("Can't change extraction path, files already extracted")
+
+        self.extraction_path = path
+
+    def cleanup_resources(self, force: bool = False) -> list[str]:
+        """
+        Delete all extracted resource files and directories, returning a list
+        of the file and directory names that could not be successfully removed.
+        This function does not have any concurrency protection, so it should
+        generally only be called when the extraction path is a temporary
+        directory exclusive to a single process.  This method is not
+        automatically called; you must call it explicitly or register it as an
+        ``atexit`` function if you wish to ensure cleanup of a temporary
+        directory used for extractions.
+        """
+        # XXX
+        return []
+
+
+def get_default_cache() -> str:
+    """
+    Return the ``PYTHON_EGG_CACHE`` environment variable
+    or a platform-relevant user cache dir for an app
+    named "Python-Eggs".
+    """
+    return os.environ.get('PYTHON_EGG_CACHE') or _user_cache_dir(appname='Python-Eggs')
+
+
+def safe_name(name: str):
+    """Convert an arbitrary string to a standard distribution name
+
+    Any runs of non-alphanumeric/. characters are replaced with a single '-'.
+    """
+    return re.sub('[^A-Za-z0-9.]+', '-', name)
+
+
+def safe_version(version: str):
+    """
+    Convert an arbitrary string to a standard version string
+    """
+    try:
+        # normalize the version
+        return str(_packaging_version.Version(version))
+    except _packaging_version.InvalidVersion:
+        version = version.replace(' ', '.')
+        return re.sub('[^A-Za-z0-9.]+', '-', version)
+
+
+def _forgiving_version(version):
+    """Fallback when ``safe_version`` is not safe enough
+    >>> parse_version(_forgiving_version('0.23ubuntu1'))
+    
+    >>> parse_version(_forgiving_version('0.23-'))
+    
+    >>> parse_version(_forgiving_version('0.-_'))
+    
+    >>> parse_version(_forgiving_version('42.+?1'))
+    
+    >>> parse_version(_forgiving_version('hello world'))
+    
+    """
+    version = version.replace(' ', '.')
+    match = _PEP440_FALLBACK.search(version)
+    if match:
+        safe = match["safe"]
+        rest = version[len(safe) :]
+    else:
+        safe = "0"
+        rest = version
+    local = f"sanitized.{_safe_segment(rest)}".strip(".")
+    return f"{safe}.dev0+{local}"
+
+
+def _safe_segment(segment):
+    """Convert an arbitrary string into a safe segment"""
+    segment = re.sub('[^A-Za-z0-9.]+', '-', segment)
+    segment = re.sub('-[^A-Za-z0-9]+', '-', segment)
+    return re.sub(r'\.[^A-Za-z0-9]+', '.', segment).strip(".-")
+
+
+def safe_extra(extra: str):
+    """Convert an arbitrary string to a standard 'extra' name
+
+    Any runs of non-alphanumeric characters are replaced with a single '_',
+    and the result is always lowercased.
+    """
+    return re.sub('[^A-Za-z0-9.-]+', '_', extra).lower()
+
+
+def to_filename(name: str):
+    """Convert a project or version name to its filename-escaped form
+
+    Any '-' characters are currently replaced with '_'.
+    """
+    return name.replace('-', '_')
+
+
+def invalid_marker(text: str):
+    """
+    Validate text as a PEP 508 environment marker; return an exception
+    if invalid or False otherwise.
+    """
+    try:
+        evaluate_marker(text)
+    except SyntaxError as e:
+        e.filename = None
+        e.lineno = None
+        return e
+    return False
+
+
+def evaluate_marker(text: str, extra: str | None = None) -> bool:
+    """
+    Evaluate a PEP 508 environment marker.
+    Return a boolean indicating the marker result in this environment.
+    Raise SyntaxError if marker is invalid.
+
+    This implementation uses the 'pyparsing' module.
+    """
+    try:
+        marker = _packaging_markers.Marker(text)
+        return marker.evaluate()
+    except _packaging_markers.InvalidMarker as e:
+        raise SyntaxError(e) from e
+
+
+class NullProvider:
+    """Try to implement resources and metadata for arbitrary PEP 302 loaders"""
+
+    egg_name: str | None = None
+    egg_info: str | None = None
+    loader: _LoaderProtocol | None = None
+
+    def __init__(self, module: _ModuleLike):
+        self.loader = getattr(module, '__loader__', None)
+        self.module_path = os.path.dirname(getattr(module, '__file__', ''))
+
+    def get_resource_filename(self, manager: ResourceManager, resource_name: str):
+        return self._fn(self.module_path, resource_name)
+
+    def get_resource_stream(self, manager: ResourceManager, resource_name: str):
+        return io.BytesIO(self.get_resource_string(manager, resource_name))
+
+    def get_resource_string(
+        self, manager: ResourceManager, resource_name: str
+    ) -> bytes:
+        return self._get(self._fn(self.module_path, resource_name))
+
+    def has_resource(self, resource_name: str):
+        return self._has(self._fn(self.module_path, resource_name))
+
+    def _get_metadata_path(self, name):
+        return self._fn(self.egg_info, name)
+
+    def has_metadata(self, name: str) -> bool:
+        if not self.egg_info:
+            return False
+
+        path = self._get_metadata_path(name)
+        return self._has(path)
+
+    def get_metadata(self, name: str):
+        if not self.egg_info:
+            return ""
+        path = self._get_metadata_path(name)
+        value = self._get(path)
+        try:
+            return value.decode('utf-8')
+        except UnicodeDecodeError as exc:
+            # Include the path in the error message to simplify
+            # troubleshooting, and without changing the exception type.
+            exc.reason += ' in {} file at path: {}'.format(name, path)
+            raise
+
+    def get_metadata_lines(self, name: str) -> Iterator[str]:
+        return yield_lines(self.get_metadata(name))
+
+    def resource_isdir(self, resource_name: str):
+        return self._isdir(self._fn(self.module_path, resource_name))
+
+    def metadata_isdir(self, name: str) -> bool:
+        return bool(self.egg_info and self._isdir(self._fn(self.egg_info, name)))
+
+    def resource_listdir(self, resource_name: str):
+        return self._listdir(self._fn(self.module_path, resource_name))
+
+    def metadata_listdir(self, name: str) -> list[str]:
+        if self.egg_info:
+            return self._listdir(self._fn(self.egg_info, name))
+        return []
+
+    def run_script(self, script_name: str, namespace: dict[str, Any]):
+        script = 'scripts/' + script_name
+        if not self.has_metadata(script):
+            raise ResolutionError(
+                "Script {script!r} not found in metadata at {self.egg_info!r}".format(
+                    **locals()
+                ),
+            )
+
+        script_text = self.get_metadata(script).replace('\r\n', '\n')
+        script_text = script_text.replace('\r', '\n')
+        script_filename = self._fn(self.egg_info, script)
+        namespace['__file__'] = script_filename
+        if os.path.exists(script_filename):
+            source = _read_utf8_with_fallback(script_filename)
+            code = compile(source, script_filename, 'exec')
+            exec(code, namespace, namespace)
+        else:
+            from linecache import cache
+
+            cache[script_filename] = (
+                len(script_text),
+                0,
+                script_text.split('\n'),
+                script_filename,
+            )
+            script_code = compile(script_text, script_filename, 'exec')
+            exec(script_code, namespace, namespace)
+
+    def _has(self, path) -> bool:
+        raise NotImplementedError(
+            "Can't perform this operation for unregistered loader type"
+        )
+
+    def _isdir(self, path) -> bool:
+        raise NotImplementedError(
+            "Can't perform this operation for unregistered loader type"
+        )
+
+    def _listdir(self, path) -> list[str]:
+        raise NotImplementedError(
+            "Can't perform this operation for unregistered loader type"
+        )
+
+    def _fn(self, base: str | None, resource_name: str):
+        if base is None:
+            raise TypeError(
+                "`base` parameter in `_fn` is `None`. Either override this method or check the parameter first."
+            )
+        self._validate_resource_path(resource_name)
+        if resource_name:
+            return os.path.join(base, *resource_name.split('/'))
+        return base
+
+    @staticmethod
+    def _validate_resource_path(path):
+        """
+        Validate the resource paths according to the docs.
+        https://setuptools.pypa.io/en/latest/pkg_resources.html#basic-resource-access
+
+        >>> warned = getfixture('recwarn')
+        >>> warnings.simplefilter('always')
+        >>> vrp = NullProvider._validate_resource_path
+        >>> vrp('foo/bar.txt')
+        >>> bool(warned)
+        False
+        >>> vrp('../foo/bar.txt')
+        >>> bool(warned)
+        True
+        >>> warned.clear()
+        >>> vrp('/foo/bar.txt')
+        >>> bool(warned)
+        True
+        >>> vrp('foo/../../bar.txt')
+        >>> bool(warned)
+        True
+        >>> warned.clear()
+        >>> vrp('foo/f../bar.txt')
+        >>> bool(warned)
+        False
+
+        Windows path separators are straight-up disallowed.
+        >>> vrp(r'\\foo/bar.txt')
+        Traceback (most recent call last):
+        ...
+        ValueError: Use of .. or absolute path in a resource path \
+is not allowed.
+
+        >>> vrp(r'C:\\foo/bar.txt')
+        Traceback (most recent call last):
+        ...
+        ValueError: Use of .. or absolute path in a resource path \
+is not allowed.
+
+        Blank values are allowed
+
+        >>> vrp('')
+        >>> bool(warned)
+        False
+
+        Non-string values are not.
+
+        >>> vrp(None)
+        Traceback (most recent call last):
+        ...
+        AttributeError: ...
+        """
+        invalid = (
+            os.path.pardir in path.split(posixpath.sep)
+            or posixpath.isabs(path)
+            or ntpath.isabs(path)
+            or path.startswith("\\")
+        )
+        if not invalid:
+            return
+
+        msg = "Use of .. or absolute path in a resource path is not allowed."
+
+        # Aggressively disallow Windows absolute paths
+        if (path.startswith("\\") or ntpath.isabs(path)) and not posixpath.isabs(path):
+            raise ValueError(msg)
+
+        # for compatibility, warn; in future
+        # raise ValueError(msg)
+        issue_warning(
+            msg[:-1] + " and will raise exceptions in a future release.",
+            DeprecationWarning,
+        )
+
+    def _get(self, path) -> bytes:
+        if hasattr(self.loader, 'get_data') and self.loader:
+            # Already checked get_data exists
+            return self.loader.get_data(path)  # type: ignore[attr-defined]
+        raise NotImplementedError(
+            "Can't perform this operation for loaders without 'get_data()'"
+        )
+
+
+register_loader_type(object, NullProvider)
+
+
+def _parents(path):
+    """
+    yield all parents of path including path
+    """
+    last = None
+    while path != last:
+        yield path
+        last = path
+        path, _ = os.path.split(path)
+
+
+class EggProvider(NullProvider):
+    """Provider based on a virtual filesystem"""
+
+    def __init__(self, module: _ModuleLike):
+        super().__init__(module)
+        self._setup_prefix()
+
+    def _setup_prefix(self):
+        # Assume that metadata may be nested inside a "basket"
+        # of multiple eggs and use module_path instead of .archive.
+        eggs = filter(_is_egg_path, _parents(self.module_path))
+        egg = next(eggs, None)
+        egg and self._set_egg(egg)
+
+    def _set_egg(self, path: str):
+        self.egg_name = os.path.basename(path)
+        self.egg_info = os.path.join(path, 'EGG-INFO')
+        self.egg_root = path
+
+
+class DefaultProvider(EggProvider):
+    """Provides access to package resources in the filesystem"""
+
+    def _has(self, path) -> bool:
+        return os.path.exists(path)
+
+    def _isdir(self, path) -> bool:
+        return os.path.isdir(path)
+
+    def _listdir(self, path):
+        return os.listdir(path)
+
+    def get_resource_stream(self, manager: object, resource_name: str):
+        return open(self._fn(self.module_path, resource_name), 'rb')
+
+    def _get(self, path) -> bytes:
+        with open(path, 'rb') as stream:
+            return stream.read()
+
+    @classmethod
+    def _register(cls):
+        loader_names = (
+            'SourceFileLoader',
+            'SourcelessFileLoader',
+        )
+        for name in loader_names:
+            loader_cls = getattr(importlib.machinery, name, type(None))
+            register_loader_type(loader_cls, cls)
+
+
+DefaultProvider._register()
+
+
+class EmptyProvider(NullProvider):
+    """Provider that returns nothing for all requests"""
+
+    # A special case, we don't want all Providers inheriting from NullProvider to have a potentially None module_path
+    module_path: str | None = None  # type: ignore[assignment]
+
+    _isdir = _has = lambda self, path: False
+
+    def _get(self, path) -> bytes:
+        return b''
+
+    def _listdir(self, path):
+        return []
+
+    def __init__(self):
+        pass
+
+
+empty_provider = EmptyProvider()
+
+
+class ZipManifests(Dict[str, "MemoizedZipManifests.manifest_mod"]):
+    """
+    zip manifest builder
+    """
+
+    # `path` could be `StrPath | IO[bytes]` but that violates the LSP for `MemoizedZipManifests.load`
+    @classmethod
+    def build(cls, path: str):
+        """
+        Build a dictionary similar to the zipimport directory
+        caches, except instead of tuples, store ZipInfo objects.
+
+        Use a platform-specific path separator (os.sep) for the path keys
+        for compatibility with pypy on Windows.
+        """
+        with zipfile.ZipFile(path) as zfile:
+            items = (
+                (
+                    name.replace('/', os.sep),
+                    zfile.getinfo(name),
+                )
+                for name in zfile.namelist()
+            )
+            return dict(items)
+
+    load = build
+
+
+class MemoizedZipManifests(ZipManifests):
+    """
+    Memoized zipfile manifests.
+    """
+
+    class manifest_mod(NamedTuple):
+        manifest: dict[str, zipfile.ZipInfo]
+        mtime: float
+
+    def load(self, path: str) -> dict[str, zipfile.ZipInfo]:  # type: ignore[override] # ZipManifests.load is a classmethod
+        """
+        Load a manifest at path or return a suitable manifest already loaded.
+        """
+        path = os.path.normpath(path)
+        mtime = os.stat(path).st_mtime
+
+        if path not in self or self[path].mtime != mtime:
+            manifest = self.build(path)
+            self[path] = self.manifest_mod(manifest, mtime)
+
+        return self[path].manifest
+
+
+class ZipProvider(EggProvider):
+    """Resource support for zips and eggs"""
+
+    eagers: list[str] | None = None
+    _zip_manifests = MemoizedZipManifests()
+    # ZipProvider's loader should always be a zipimporter or equivalent
+    loader: zipimport.zipimporter
+
+    def __init__(self, module: _ZipLoaderModule):
+        super().__init__(module)
+        self.zip_pre = self.loader.archive + os.sep
+
+    def _zipinfo_name(self, fspath):
+        # Convert a virtual filename (full path to file) into a zipfile subpath
+        # usable with the zipimport directory cache for our target archive
+        fspath = fspath.rstrip(os.sep)
+        if fspath == self.loader.archive:
+            return ''
+        if fspath.startswith(self.zip_pre):
+            return fspath[len(self.zip_pre) :]
+        raise AssertionError("%s is not a subpath of %s" % (fspath, self.zip_pre))
+
+    def _parts(self, zip_path):
+        # Convert a zipfile subpath into an egg-relative path part list.
+        # pseudo-fs path
+        fspath = self.zip_pre + zip_path
+        if fspath.startswith(self.egg_root + os.sep):
+            return fspath[len(self.egg_root) + 1 :].split(os.sep)
+        raise AssertionError("%s is not a subpath of %s" % (fspath, self.egg_root))
+
+    @property
+    def zipinfo(self):
+        return self._zip_manifests.load(self.loader.archive)
+
+    def get_resource_filename(self, manager: ResourceManager, resource_name: str):
+        if not self.egg_name:
+            raise NotImplementedError(
+                "resource_filename() only supported for .egg, not .zip"
+            )
+        # no need to lock for extraction, since we use temp names
+        zip_path = self._resource_to_zip(resource_name)
+        eagers = self._get_eager_resources()
+        if '/'.join(self._parts(zip_path)) in eagers:
+            for name in eagers:
+                self._extract_resource(manager, self._eager_to_zip(name))
+        return self._extract_resource(manager, zip_path)
+
+    @staticmethod
+    def _get_date_and_size(zip_stat):
+        size = zip_stat.file_size
+        # ymdhms+wday, yday, dst
+        date_time = zip_stat.date_time + (0, 0, -1)
+        # 1980 offset already done
+        timestamp = time.mktime(date_time)
+        return timestamp, size
+
+    # FIXME: 'ZipProvider._extract_resource' is too complex (12)
+    def _extract_resource(self, manager: ResourceManager, zip_path) -> str:  # noqa: C901
+        if zip_path in self._index():
+            for name in self._index()[zip_path]:
+                last = self._extract_resource(manager, os.path.join(zip_path, name))
+            # return the extracted directory name
+            return os.path.dirname(last)
+
+        timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])
+
+        if not WRITE_SUPPORT:
+            raise OSError(
+                '"os.rename" and "os.unlink" are not supported on this platform'
+            )
+        try:
+            if not self.egg_name:
+                raise OSError(
+                    '"egg_name" is empty. This likely means no egg could be found from the "module_path".'
+                )
+            real_path = manager.get_cache_path(self.egg_name, self._parts(zip_path))
+
+            if self._is_current(real_path, zip_path):
+                return real_path
+
+            outf, tmpnam = _mkstemp(
+                ".$extract",
+                dir=os.path.dirname(real_path),
+            )
+            os.write(outf, self.loader.get_data(zip_path))
+            os.close(outf)
+            utime(tmpnam, (timestamp, timestamp))
+            manager.postprocess(tmpnam, real_path)
+
+            try:
+                rename(tmpnam, real_path)
+
+            except OSError:
+                if os.path.isfile(real_path):
+                    if self._is_current(real_path, zip_path):
+                        # the file became current since it was checked above,
+                        #  so proceed.
+                        return real_path
+                    # Windows, del old file and retry
+                    elif os.name == 'nt':
+                        unlink(real_path)
+                        rename(tmpnam, real_path)
+                        return real_path
+                raise
+
+        except OSError:
+            # report a user-friendly error
+            manager.extraction_error()
+
+        return real_path
+
+    def _is_current(self, file_path, zip_path):
+        """
+        Return True if the file_path is current for this zip_path
+        """
+        timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])
+        if not os.path.isfile(file_path):
+            return False
+        stat = os.stat(file_path)
+        if stat.st_size != size or stat.st_mtime != timestamp:
+            return False
+        # check that the contents match
+        zip_contents = self.loader.get_data(zip_path)
+        with open(file_path, 'rb') as f:
+            file_contents = f.read()
+        return zip_contents == file_contents
+
+    def _get_eager_resources(self):
+        if self.eagers is None:
+            eagers = []
+            for name in ('native_libs.txt', 'eager_resources.txt'):
+                if self.has_metadata(name):
+                    eagers.extend(self.get_metadata_lines(name))
+            self.eagers = eagers
+        return self.eagers
+
+    def _index(self):
+        try:
+            return self._dirindex
+        except AttributeError:
+            ind = {}
+            for path in self.zipinfo:
+                parts = path.split(os.sep)
+                while parts:
+                    parent = os.sep.join(parts[:-1])
+                    if parent in ind:
+                        ind[parent].append(parts[-1])
+                        break
+                    else:
+                        ind[parent] = [parts.pop()]
+            self._dirindex = ind
+            return ind
+
+    def _has(self, fspath) -> bool:
+        zip_path = self._zipinfo_name(fspath)
+        return zip_path in self.zipinfo or zip_path in self._index()
+
+    def _isdir(self, fspath) -> bool:
+        return self._zipinfo_name(fspath) in self._index()
+
+    def _listdir(self, fspath):
+        return list(self._index().get(self._zipinfo_name(fspath), ()))
+
+    def _eager_to_zip(self, resource_name: str):
+        return self._zipinfo_name(self._fn(self.egg_root, resource_name))
+
+    def _resource_to_zip(self, resource_name: str):
+        return self._zipinfo_name(self._fn(self.module_path, resource_name))
+
+
+register_loader_type(zipimport.zipimporter, ZipProvider)
+
+
+class FileMetadata(EmptyProvider):
+    """Metadata handler for standalone PKG-INFO files
+
+    Usage::
+
+        metadata = FileMetadata("/path/to/PKG-INFO")
+
+    This provider rejects all data and metadata requests except for PKG-INFO,
+    which is treated as existing, and will be the contents of the file at
+    the provided location.
+    """
+
+    def __init__(self, path: StrPath):
+        self.path = path
+
+    def _get_metadata_path(self, name):
+        return self.path
+
+    def has_metadata(self, name: str) -> bool:
+        return name == 'PKG-INFO' and os.path.isfile(self.path)
+
+    def get_metadata(self, name: str):
+        if name != 'PKG-INFO':
+            raise KeyError("No metadata except PKG-INFO is available")
+
+        with open(self.path, encoding='utf-8', errors="replace") as f:
+            metadata = f.read()
+        self._warn_on_replacement(metadata)
+        return metadata
+
+    def _warn_on_replacement(self, metadata):
+        replacement_char = '�'
+        if replacement_char in metadata:
+            tmpl = "{self.path} could not be properly decoded in UTF-8"
+            msg = tmpl.format(**locals())
+            warnings.warn(msg)
+
+    def get_metadata_lines(self, name: str) -> Iterator[str]:
+        return yield_lines(self.get_metadata(name))
+
+
+class PathMetadata(DefaultProvider):
+    """Metadata provider for egg directories
+
+    Usage::
+
+        # Development eggs:
+
+        egg_info = "/path/to/PackageName.egg-info"
+        base_dir = os.path.dirname(egg_info)
+        metadata = PathMetadata(base_dir, egg_info)
+        dist_name = os.path.splitext(os.path.basename(egg_info))[0]
+        dist = Distribution(basedir, project_name=dist_name, metadata=metadata)
+
+        # Unpacked egg directories:
+
+        egg_path = "/path/to/PackageName-ver-pyver-etc.egg"
+        metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO'))
+        dist = Distribution.from_filename(egg_path, metadata=metadata)
+    """
+
+    def __init__(self, path: str, egg_info: str):
+        self.module_path = path
+        self.egg_info = egg_info
+
+
+class EggMetadata(ZipProvider):
+    """Metadata provider for .egg files"""
+
+    def __init__(self, importer: zipimport.zipimporter):
+        """Create a metadata provider from a zipimporter"""
+
+        self.zip_pre = importer.archive + os.sep
+        self.loader = importer
+        if importer.prefix:
+            self.module_path = os.path.join(importer.archive, importer.prefix)
+        else:
+            self.module_path = importer.archive
+        self._setup_prefix()
+
+
+_distribution_finders: dict[type, _DistFinderType[Any]] = _declare_state(
+    'dict', '_distribution_finders', {}
+)
+
+
+def register_finder(importer_type: type[_T], distribution_finder: _DistFinderType[_T]):
+    """Register `distribution_finder` to find distributions in sys.path items
+
+    `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item
+    handler), and `distribution_finder` is a callable that, passed a path
+    item and the importer instance, yields ``Distribution`` instances found on
+    that path item.  See ``pkg_resources.find_on_path`` for an example."""
+    _distribution_finders[importer_type] = distribution_finder
+
+
+def find_distributions(path_item: str, only: bool = False):
+    """Yield distributions accessible via `path_item`"""
+    importer = get_importer(path_item)
+    finder = _find_adapter(_distribution_finders, importer)
+    return finder(importer, path_item, only)
+
+
+def find_eggs_in_zip(
+    importer: zipimport.zipimporter, path_item: str, only: bool = False
+) -> Iterator[Distribution]:
+    """
+    Find eggs in zip files; possibly multiple nested eggs.
+    """
+    if importer.archive.endswith('.whl'):
+        # wheels are not supported with this finder
+        # they don't have PKG-INFO metadata, and won't ever contain eggs
+        return
+    metadata = EggMetadata(importer)
+    if metadata.has_metadata('PKG-INFO'):
+        yield Distribution.from_filename(path_item, metadata=metadata)
+    if only:
+        # don't yield nested distros
+        return
+    for subitem in metadata.resource_listdir(''):
+        if _is_egg_path(subitem):
+            subpath = os.path.join(path_item, subitem)
+            dists = find_eggs_in_zip(zipimport.zipimporter(subpath), subpath)
+            yield from dists
+        elif subitem.lower().endswith(('.dist-info', '.egg-info')):
+            subpath = os.path.join(path_item, subitem)
+            submeta = EggMetadata(zipimport.zipimporter(subpath))
+            submeta.egg_info = subpath
+            yield Distribution.from_location(path_item, subitem, submeta)
+
+
+register_finder(zipimport.zipimporter, find_eggs_in_zip)
+
+
+def find_nothing(
+    importer: object | None, path_item: str | None, only: bool | None = False
+):
+    return ()
+
+
+register_finder(object, find_nothing)
+
+
+def find_on_path(importer: object | None, path_item, only=False):
+    """Yield distributions accessible on a sys.path directory"""
+    path_item = _normalize_cached(path_item)
+
+    if _is_unpacked_egg(path_item):
+        yield Distribution.from_filename(
+            path_item,
+            metadata=PathMetadata(path_item, os.path.join(path_item, 'EGG-INFO')),
+        )
+        return
+
+    entries = (os.path.join(path_item, child) for child in safe_listdir(path_item))
+
+    # scan for .egg and .egg-info in directory
+    for entry in sorted(entries):
+        fullpath = os.path.join(path_item, entry)
+        factory = dist_factory(path_item, entry, only)
+        yield from factory(fullpath)
+
+
+def dist_factory(path_item, entry, only):
+    """Return a dist_factory for the given entry."""
+    lower = entry.lower()
+    is_egg_info = lower.endswith('.egg-info')
+    is_dist_info = lower.endswith('.dist-info') and os.path.isdir(
+        os.path.join(path_item, entry)
+    )
+    is_meta = is_egg_info or is_dist_info
+    return (
+        distributions_from_metadata
+        if is_meta
+        else find_distributions
+        if not only and _is_egg_path(entry)
+        else resolve_egg_link
+        if not only and lower.endswith('.egg-link')
+        else NoDists()
+    )
+
+
+class NoDists:
+    """
+    >>> bool(NoDists())
+    False
+
+    >>> list(NoDists()('anything'))
+    []
+    """
+
+    def __bool__(self):
+        return False
+
+    def __call__(self, fullpath):
+        return iter(())
+
+
+def safe_listdir(path: StrOrBytesPath):
+    """
+    Attempt to list contents of path, but suppress some exceptions.
+    """
+    try:
+        return os.listdir(path)
+    except (PermissionError, NotADirectoryError):
+        pass
+    except OSError as e:
+        # Ignore the directory if does not exist, not a directory or
+        # permission denied
+        if e.errno not in (errno.ENOTDIR, errno.EACCES, errno.ENOENT):
+            raise
+    return ()
+
+
+def distributions_from_metadata(path: str):
+    root = os.path.dirname(path)
+    if os.path.isdir(path):
+        if len(os.listdir(path)) == 0:
+            # empty metadata dir; skip
+            return
+        metadata: _MetadataType = PathMetadata(root, path)
+    else:
+        metadata = FileMetadata(path)
+    entry = os.path.basename(path)
+    yield Distribution.from_location(
+        root,
+        entry,
+        metadata,
+        precedence=DEVELOP_DIST,
+    )
+
+
+def non_empty_lines(path):
+    """
+    Yield non-empty lines from file at path
+    """
+    for line in _read_utf8_with_fallback(path).splitlines():
+        line = line.strip()
+        if line:
+            yield line
+
+
+def resolve_egg_link(path):
+    """
+    Given a path to an .egg-link, resolve distributions
+    present in the referenced path.
+    """
+    referenced_paths = non_empty_lines(path)
+    resolved_paths = (
+        os.path.join(os.path.dirname(path), ref) for ref in referenced_paths
+    )
+    dist_groups = map(find_distributions, resolved_paths)
+    return next(dist_groups, ())
+
+
+if hasattr(pkgutil, 'ImpImporter'):
+    register_finder(pkgutil.ImpImporter, find_on_path)
+
+register_finder(importlib.machinery.FileFinder, find_on_path)
+
+_namespace_handlers: dict[type, _NSHandlerType[Any]] = _declare_state(
+    'dict', '_namespace_handlers', {}
+)
+_namespace_packages: dict[str | None, list[str]] = _declare_state(
+    'dict', '_namespace_packages', {}
+)
+
+
+def register_namespace_handler(
+    importer_type: type[_T], namespace_handler: _NSHandlerType[_T]
+):
+    """Register `namespace_handler` to declare namespace packages
+
+    `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item
+    handler), and `namespace_handler` is a callable like this::
+
+        def namespace_handler(importer, path_entry, moduleName, module):
+            # return a path_entry to use for child packages
+
+    Namespace handlers are only called if the importer object has already
+    agreed that it can handle the relevant path item, and they should only
+    return a subpath if the module __path__ does not already contain an
+    equivalent subpath.  For an example namespace handler, see
+    ``pkg_resources.file_ns_handler``.
+    """
+    _namespace_handlers[importer_type] = namespace_handler
+
+
+def _handle_ns(packageName, path_item):
+    """Ensure that named package includes a subpath of path_item (if needed)"""
+
+    importer = get_importer(path_item)
+    if importer is None:
+        return None
+
+    # use find_spec (PEP 451) and fall-back to find_module (PEP 302)
+    try:
+        spec = importer.find_spec(packageName)
+    except AttributeError:
+        # capture warnings due to #1111
+        with warnings.catch_warnings():
+            warnings.simplefilter("ignore")
+            loader = importer.find_module(packageName)
+    else:
+        loader = spec.loader if spec else None
+
+    if loader is None:
+        return None
+    module = sys.modules.get(packageName)
+    if module is None:
+        module = sys.modules[packageName] = types.ModuleType(packageName)
+        module.__path__ = []
+        _set_parent_ns(packageName)
+    elif not hasattr(module, '__path__'):
+        raise TypeError("Not a package:", packageName)
+    handler = _find_adapter(_namespace_handlers, importer)
+    subpath = handler(importer, path_item, packageName, module)
+    if subpath is not None:
+        path = module.__path__
+        path.append(subpath)
+        importlib.import_module(packageName)
+        _rebuild_mod_path(path, packageName, module)
+    return subpath
+
+
+def _rebuild_mod_path(orig_path, package_name, module: types.ModuleType):
+    """
+    Rebuild module.__path__ ensuring that all entries are ordered
+    corresponding to their sys.path order
+    """
+    sys_path = [_normalize_cached(p) for p in sys.path]
+
+    def safe_sys_path_index(entry):
+        """
+        Workaround for #520 and #513.
+        """
+        try:
+            return sys_path.index(entry)
+        except ValueError:
+            return float('inf')
+
+    def position_in_sys_path(path):
+        """
+        Return the ordinal of the path based on its position in sys.path
+        """
+        path_parts = path.split(os.sep)
+        module_parts = package_name.count('.') + 1
+        parts = path_parts[:-module_parts]
+        return safe_sys_path_index(_normalize_cached(os.sep.join(parts)))
+
+    new_path = sorted(orig_path, key=position_in_sys_path)
+    new_path = [_normalize_cached(p) for p in new_path]
+
+    if isinstance(module.__path__, list):
+        module.__path__[:] = new_path
+    else:
+        module.__path__ = new_path
+
+
+def declare_namespace(packageName: str):
+    """Declare that package 'packageName' is a namespace package"""
+
+    msg = (
+        f"Deprecated call to `pkg_resources.declare_namespace({packageName!r})`.\n"
+        "Implementing implicit namespace packages (as specified in PEP 420) "
+        "is preferred to `pkg_resources.declare_namespace`. "
+        "See https://setuptools.pypa.io/en/latest/references/"
+        "keywords.html#keyword-namespace-packages"
+    )
+    warnings.warn(msg, DeprecationWarning, stacklevel=2)
+
+    _imp.acquire_lock()
+    try:
+        if packageName in _namespace_packages:
+            return
+
+        path: MutableSequence[str] = sys.path
+        parent, _, _ = packageName.rpartition('.')
+
+        if parent:
+            declare_namespace(parent)
+            if parent not in _namespace_packages:
+                __import__(parent)
+            try:
+                path = sys.modules[parent].__path__
+            except AttributeError as e:
+                raise TypeError("Not a package:", parent) from e
+
+        # Track what packages are namespaces, so when new path items are added,
+        # they can be updated
+        _namespace_packages.setdefault(parent or None, []).append(packageName)
+        _namespace_packages.setdefault(packageName, [])
+
+        for path_item in path:
+            # Ensure all the parent's path items are reflected in the child,
+            # if they apply
+            _handle_ns(packageName, path_item)
+
+    finally:
+        _imp.release_lock()
+
+
+def fixup_namespace_packages(path_item: str, parent: str | None = None):
+    """Ensure that previously-declared namespace packages include path_item"""
+    _imp.acquire_lock()
+    try:
+        for package in _namespace_packages.get(parent, ()):
+            subpath = _handle_ns(package, path_item)
+            if subpath:
+                fixup_namespace_packages(subpath, package)
+    finally:
+        _imp.release_lock()
+
+
+def file_ns_handler(
+    importer: object,
+    path_item: StrPath,
+    packageName: str,
+    module: types.ModuleType,
+):
+    """Compute an ns-package subpath for a filesystem or zipfile importer"""
+
+    subpath = os.path.join(path_item, packageName.split('.')[-1])
+    normalized = _normalize_cached(subpath)
+    for item in module.__path__:
+        if _normalize_cached(item) == normalized:
+            break
+    else:
+        # Only return the path if it's not already there
+        return subpath
+
+
+if hasattr(pkgutil, 'ImpImporter'):
+    register_namespace_handler(pkgutil.ImpImporter, file_ns_handler)
+
+register_namespace_handler(zipimport.zipimporter, file_ns_handler)
+register_namespace_handler(importlib.machinery.FileFinder, file_ns_handler)
+
+
+def null_ns_handler(
+    importer: object,
+    path_item: str | None,
+    packageName: str | None,
+    module: _ModuleLike | None,
+):
+    return None
+
+
+register_namespace_handler(object, null_ns_handler)
+
+
+@overload
+def normalize_path(filename: StrPath) -> str: ...
+@overload
+def normalize_path(filename: BytesPath) -> bytes: ...
+def normalize_path(filename: StrOrBytesPath):
+    """Normalize a file/dir name for comparison purposes"""
+    return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename))))
+
+
+def _cygwin_patch(filename: StrOrBytesPath):  # pragma: nocover
+    """
+    Contrary to POSIX 2008, on Cygwin, getcwd (3) contains
+    symlink components. Using
+    os.path.abspath() works around this limitation. A fix in os.getcwd()
+    would probably better, in Cygwin even more so, except
+    that this seems to be by design...
+    """
+    return os.path.abspath(filename) if sys.platform == 'cygwin' else filename
+
+
+if TYPE_CHECKING:
+    # https://github.com/python/mypy/issues/16261
+    # https://github.com/python/typeshed/issues/6347
+    @overload
+    def _normalize_cached(filename: StrPath) -> str: ...
+    @overload
+    def _normalize_cached(filename: BytesPath) -> bytes: ...
+    def _normalize_cached(filename: StrOrBytesPath) -> str | bytes: ...
+else:
+
+    @functools.lru_cache(maxsize=None)
+    def _normalize_cached(filename):
+        return normalize_path(filename)
+
+
+def _is_egg_path(path):
+    """
+    Determine if given path appears to be an egg.
+    """
+    return _is_zip_egg(path) or _is_unpacked_egg(path)
+
+
+def _is_zip_egg(path):
+    return (
+        path.lower().endswith('.egg')
+        and os.path.isfile(path)
+        and zipfile.is_zipfile(path)
+    )
+
+
+def _is_unpacked_egg(path):
+    """
+    Determine if given path appears to be an unpacked egg.
+    """
+    return path.lower().endswith('.egg') and os.path.isfile(
+        os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+    )
+
+
+def _set_parent_ns(packageName):
+    parts = packageName.split('.')
+    name = parts.pop()
+    if parts:
+        parent = '.'.join(parts)
+        setattr(sys.modules[parent], name, sys.modules[packageName])
+
+
+MODULE = re.compile(r"\w+(\.\w+)*$").match
+EGG_NAME = re.compile(
+    r"""
+    (?P[^-]+) (
+        -(?P[^-]+) (
+            -py(?P[^-]+) (
+                -(?P.+)
+            )?
+        )?
+    )?
+    """,
+    re.VERBOSE | re.IGNORECASE,
+).match
+
+
+class EntryPoint:
+    """Object representing an advertised importable object"""
+
+    def __init__(
+        self,
+        name: str,
+        module_name: str,
+        attrs: Iterable[str] = (),
+        extras: Iterable[str] = (),
+        dist: Distribution | None = None,
+    ):
+        if not MODULE(module_name):
+            raise ValueError("Invalid module name", module_name)
+        self.name = name
+        self.module_name = module_name
+        self.attrs = tuple(attrs)
+        self.extras = tuple(extras)
+        self.dist = dist
+
+    def __str__(self):
+        s = "%s = %s" % (self.name, self.module_name)
+        if self.attrs:
+            s += ':' + '.'.join(self.attrs)
+        if self.extras:
+            s += ' [%s]' % ','.join(self.extras)
+        return s
+
+    def __repr__(self):
+        return "EntryPoint.parse(%r)" % str(self)
+
+    @overload
+    def load(
+        self,
+        require: Literal[True] = True,
+        env: Environment | None = None,
+        installer: _InstallerType | None = None,
+    ) -> _ResolvedEntryPoint: ...
+    @overload
+    def load(
+        self,
+        require: Literal[False],
+        *args: Any,
+        **kwargs: Any,
+    ) -> _ResolvedEntryPoint: ...
+    def load(
+        self,
+        require: bool = True,
+        *args: Environment | _InstallerType | None,
+        **kwargs: Environment | _InstallerType | None,
+    ) -> _ResolvedEntryPoint:
+        """
+        Require packages for this EntryPoint, then resolve it.
+        """
+        if not require or args or kwargs:
+            warnings.warn(
+                "Parameters to load are deprecated.  Call .resolve and "
+                ".require separately.",
+                PkgResourcesDeprecationWarning,
+                stacklevel=2,
+            )
+        if require:
+            # We could pass `env` and `installer` directly,
+            # but keeping `*args` and `**kwargs` for backwards compatibility
+            self.require(*args, **kwargs)  # type: ignore
+        return self.resolve()
+
+    def resolve(self) -> _ResolvedEntryPoint:
+        """
+        Resolve the entry point from its module and attrs.
+        """
+        module = __import__(self.module_name, fromlist=['__name__'], level=0)
+        try:
+            return functools.reduce(getattr, self.attrs, module)
+        except AttributeError as exc:
+            raise ImportError(str(exc)) from exc
+
+    def require(
+        self,
+        env: Environment | None = None,
+        installer: _InstallerType | None = None,
+    ):
+        if not self.dist:
+            error_cls = UnknownExtra if self.extras else AttributeError
+            raise error_cls("Can't require() without a distribution", self)
+
+        # Get the requirements for this entry point with all its extras and
+        # then resolve them. We have to pass `extras` along when resolving so
+        # that the working set knows what extras we want. Otherwise, for
+        # dist-info distributions, the working set will assume that the
+        # requirements for that extra are purely optional and skip over them.
+        reqs = self.dist.requires(self.extras)
+        items = working_set.resolve(reqs, env, installer, extras=self.extras)
+        list(map(working_set.add, items))
+
+    pattern = re.compile(
+        r'\s*'
+        r'(?P.+?)\s*'
+        r'=\s*'
+        r'(?P[\w.]+)\s*'
+        r'(:\s*(?P[\w.]+))?\s*'
+        r'(?P\[.*\])?\s*$'
+    )
+
+    @classmethod
+    def parse(cls, src: str, dist: Distribution | None = None):
+        """Parse a single entry point from string `src`
+
+        Entry point syntax follows the form::
+
+            name = some.module:some.attr [extra1, extra2]
+
+        The entry name and module name are required, but the ``:attrs`` and
+        ``[extras]`` parts are optional
+        """
+        m = cls.pattern.match(src)
+        if not m:
+            msg = "EntryPoint must be in 'name=module:attrs [extras]' format"
+            raise ValueError(msg, src)
+        res = m.groupdict()
+        extras = cls._parse_extras(res['extras'])
+        attrs = res['attr'].split('.') if res['attr'] else ()
+        return cls(res['name'], res['module'], attrs, extras, dist)
+
+    @classmethod
+    def _parse_extras(cls, extras_spec):
+        if not extras_spec:
+            return ()
+        req = Requirement.parse('x' + extras_spec)
+        if req.specs:
+            raise ValueError
+        return req.extras
+
+    @classmethod
+    def parse_group(
+        cls,
+        group: str,
+        lines: _NestedStr,
+        dist: Distribution | None = None,
+    ):
+        """Parse an entry point group"""
+        if not MODULE(group):
+            raise ValueError("Invalid group name", group)
+        this: dict[str, Self] = {}
+        for line in yield_lines(lines):
+            ep = cls.parse(line, dist)
+            if ep.name in this:
+                raise ValueError("Duplicate entry point", group, ep.name)
+            this[ep.name] = ep
+        return this
+
+    @classmethod
+    def parse_map(
+        cls,
+        data: str | Iterable[str] | dict[str, str | Iterable[str]],
+        dist: Distribution | None = None,
+    ):
+        """Parse a map of entry point groups"""
+        _data: Iterable[tuple[str | None, str | Iterable[str]]]
+        if isinstance(data, dict):
+            _data = data.items()
+        else:
+            _data = split_sections(data)
+        maps: dict[str, dict[str, Self]] = {}
+        for group, lines in _data:
+            if group is None:
+                if not lines:
+                    continue
+                raise ValueError("Entry points must be listed in groups")
+            group = group.strip()
+            if group in maps:
+                raise ValueError("Duplicate group name", group)
+            maps[group] = cls.parse_group(group, lines, dist)
+        return maps
+
+
+def _version_from_file(lines):
+    """
+    Given an iterable of lines from a Metadata file, return
+    the value of the Version field, if present, or None otherwise.
+    """
+
+    def is_version_line(line):
+        return line.lower().startswith('version:')
+
+    version_lines = filter(is_version_line, lines)
+    line = next(iter(version_lines), '')
+    _, _, value = line.partition(':')
+    return safe_version(value.strip()) or None
+
+
+class Distribution:
+    """Wrap an actual or potential sys.path entry w/metadata"""
+
+    PKG_INFO = 'PKG-INFO'
+
+    def __init__(
+        self,
+        location: str | None = None,
+        metadata: _MetadataType = None,
+        project_name: str | None = None,
+        version: str | None = None,
+        py_version: str | None = PY_MAJOR,
+        platform: str | None = None,
+        precedence: int = EGG_DIST,
+    ):
+        self.project_name = safe_name(project_name or 'Unknown')
+        if version is not None:
+            self._version = safe_version(version)
+        self.py_version = py_version
+        self.platform = platform
+        self.location = location
+        self.precedence = precedence
+        self._provider = metadata or empty_provider
+
+    @classmethod
+    def from_location(
+        cls,
+        location: str,
+        basename: StrPath,
+        metadata: _MetadataType = None,
+        **kw: int,  # We could set `precedence` explicitly, but keeping this as `**kw` for full backwards and subclassing compatibility
+    ) -> Distribution:
+        project_name, version, py_version, platform = [None] * 4
+        basename, ext = os.path.splitext(basename)
+        if ext.lower() in _distributionImpl:
+            cls = _distributionImpl[ext.lower()]
+
+            match = EGG_NAME(basename)
+            if match:
+                project_name, version, py_version, platform = match.group(
+                    'name', 'ver', 'pyver', 'plat'
+                )
+        return cls(
+            location,
+            metadata,
+            project_name=project_name,
+            version=version,
+            py_version=py_version,
+            platform=platform,
+            **kw,
+        )._reload_version()
+
+    def _reload_version(self):
+        return self
+
+    @property
+    def hashcmp(self):
+        return (
+            self._forgiving_parsed_version,
+            self.precedence,
+            self.key,
+            self.location,
+            self.py_version or '',
+            self.platform or '',
+        )
+
+    def __hash__(self):
+        return hash(self.hashcmp)
+
+    def __lt__(self, other: Distribution):
+        return self.hashcmp < other.hashcmp
+
+    def __le__(self, other: Distribution):
+        return self.hashcmp <= other.hashcmp
+
+    def __gt__(self, other: Distribution):
+        return self.hashcmp > other.hashcmp
+
+    def __ge__(self, other: Distribution):
+        return self.hashcmp >= other.hashcmp
+
+    def __eq__(self, other: object):
+        if not isinstance(other, self.__class__):
+            # It's not a Distribution, so they are not equal
+            return False
+        return self.hashcmp == other.hashcmp
+
+    def __ne__(self, other: object):
+        return not self == other
+
+    # These properties have to be lazy so that we don't have to load any
+    # metadata until/unless it's actually needed.  (i.e., some distributions
+    # may not know their name or version without loading PKG-INFO)
+
+    @property
+    def key(self):
+        try:
+            return self._key
+        except AttributeError:
+            self._key = key = self.project_name.lower()
+            return key
+
+    @property
+    def parsed_version(self):
+        if not hasattr(self, "_parsed_version"):
+            try:
+                self._parsed_version = parse_version(self.version)
+            except _packaging_version.InvalidVersion as ex:
+                info = f"(package: {self.project_name})"
+                if hasattr(ex, "add_note"):
+                    ex.add_note(info)  # PEP 678
+                    raise
+                raise _packaging_version.InvalidVersion(f"{str(ex)} {info}") from None
+
+        return self._parsed_version
+
+    @property
+    def _forgiving_parsed_version(self):
+        try:
+            return self.parsed_version
+        except _packaging_version.InvalidVersion as ex:
+            self._parsed_version = parse_version(_forgiving_version(self.version))
+
+            notes = "\n".join(getattr(ex, "__notes__", []))  # PEP 678
+            msg = f"""!!\n\n
+            *************************************************************************
+            {str(ex)}\n{notes}
+
+            This is a long overdue deprecation.
+            For the time being, `pkg_resources` will use `{self._parsed_version}`
+            as a replacement to avoid breaking existing environments,
+            but no future compatibility is guaranteed.
+
+            If you maintain package {self.project_name} you should implement
+            the relevant changes to adequate the project to PEP 440 immediately.
+            *************************************************************************
+            \n\n!!
+            """
+            warnings.warn(msg, DeprecationWarning)
+
+            return self._parsed_version
+
+    @property
+    def version(self):
+        try:
+            return self._version
+        except AttributeError as e:
+            version = self._get_version()
+            if version is None:
+                path = self._get_metadata_path_for_display(self.PKG_INFO)
+                msg = ("Missing 'Version:' header and/or {} file at path: {}").format(
+                    self.PKG_INFO, path
+                )
+                raise ValueError(msg, self) from e
+
+            return version
+
+    @property
+    def _dep_map(self):
+        """
+        A map of extra to its list of (direct) requirements
+        for this distribution, including the null extra.
+        """
+        try:
+            return self.__dep_map
+        except AttributeError:
+            self.__dep_map = self._filter_extras(self._build_dep_map())
+        return self.__dep_map
+
+    @staticmethod
+    def _filter_extras(dm: dict[str | None, list[Requirement]]):
+        """
+        Given a mapping of extras to dependencies, strip off
+        environment markers and filter out any dependencies
+        not matching the markers.
+        """
+        for extra in list(filter(None, dm)):
+            new_extra: str | None = extra
+            reqs = dm.pop(extra)
+            new_extra, _, marker = extra.partition(':')
+            fails_marker = marker and (
+                invalid_marker(marker) or not evaluate_marker(marker)
+            )
+            if fails_marker:
+                reqs = []
+            new_extra = safe_extra(new_extra) or None
+
+            dm.setdefault(new_extra, []).extend(reqs)
+        return dm
+
+    def _build_dep_map(self):
+        dm = {}
+        for name in 'requires.txt', 'depends.txt':
+            for extra, reqs in split_sections(self._get_metadata(name)):
+                dm.setdefault(extra, []).extend(parse_requirements(reqs))
+        return dm
+
+    def requires(self, extras: Iterable[str] = ()):
+        """List of Requirements needed for this distro if `extras` are used"""
+        dm = self._dep_map
+        deps: list[Requirement] = []
+        deps.extend(dm.get(None, ()))
+        for ext in extras:
+            try:
+                deps.extend(dm[safe_extra(ext)])
+            except KeyError as e:
+                raise UnknownExtra(
+                    "%s has no such extra feature %r" % (self, ext)
+                ) from e
+        return deps
+
+    def _get_metadata_path_for_display(self, name):
+        """
+        Return the path to the given metadata file, if available.
+        """
+        try:
+            # We need to access _get_metadata_path() on the provider object
+            # directly rather than through this class's __getattr__()
+            # since _get_metadata_path() is marked private.
+            path = self._provider._get_metadata_path(name)
+
+        # Handle exceptions e.g. in case the distribution's metadata
+        # provider doesn't support _get_metadata_path().
+        except Exception:
+            return '[could not detect]'
+
+        return path
+
+    def _get_metadata(self, name):
+        if self.has_metadata(name):
+            yield from self.get_metadata_lines(name)
+
+    def _get_version(self):
+        lines = self._get_metadata(self.PKG_INFO)
+        return _version_from_file(lines)
+
+    def activate(self, path: list[str] | None = None, replace: bool = False):
+        """Ensure distribution is importable on `path` (default=sys.path)"""
+        if path is None:
+            path = sys.path
+        self.insert_on(path, replace=replace)
+        if path is sys.path and self.location is not None:
+            fixup_namespace_packages(self.location)
+            for pkg in self._get_metadata('namespace_packages.txt'):
+                if pkg in sys.modules:
+                    declare_namespace(pkg)
+
+    def egg_name(self):
+        """Return what this distribution's standard .egg filename should be"""
+        filename = "%s-%s-py%s" % (
+            to_filename(self.project_name),
+            to_filename(self.version),
+            self.py_version or PY_MAJOR,
+        )
+
+        if self.platform:
+            filename += '-' + self.platform
+        return filename
+
+    def __repr__(self):
+        if self.location:
+            return "%s (%s)" % (self, self.location)
+        else:
+            return str(self)
+
+    def __str__(self):
+        try:
+            version = getattr(self, 'version', None)
+        except ValueError:
+            version = None
+        version = version or "[unknown version]"
+        return "%s %s" % (self.project_name, version)
+
+    def __getattr__(self, attr):
+        """Delegate all unrecognized public attributes to .metadata provider"""
+        if attr.startswith('_'):
+            raise AttributeError(attr)
+        return getattr(self._provider, attr)
+
+    def __dir__(self):
+        return list(
+            set(super().__dir__())
+            | set(attr for attr in self._provider.__dir__() if not attr.startswith('_'))
+        )
+
+    @classmethod
+    def from_filename(
+        cls,
+        filename: StrPath,
+        metadata: _MetadataType = None,
+        **kw: int,  # We could set `precedence` explicitly, but keeping this as `**kw` for full backwards and subclassing compatibility
+    ):
+        return cls.from_location(
+            _normalize_cached(filename), os.path.basename(filename), metadata, **kw
+        )
+
+    def as_requirement(self):
+        """Return a ``Requirement`` that matches this distribution exactly"""
+        if isinstance(self.parsed_version, _packaging_version.Version):
+            spec = "%s==%s" % (self.project_name, self.parsed_version)
+        else:
+            spec = "%s===%s" % (self.project_name, self.parsed_version)
+
+        return Requirement.parse(spec)
+
+    def load_entry_point(self, group: str, name: str) -> _ResolvedEntryPoint:
+        """Return the `name` entry point of `group` or raise ImportError"""
+        ep = self.get_entry_info(group, name)
+        if ep is None:
+            raise ImportError("Entry point %r not found" % ((group, name),))
+        return ep.load()
+
+    @overload
+    def get_entry_map(self, group: None = None) -> dict[str, dict[str, EntryPoint]]: ...
+    @overload
+    def get_entry_map(self, group: str) -> dict[str, EntryPoint]: ...
+    def get_entry_map(self, group: str | None = None):
+        """Return the entry point map for `group`, or the full entry map"""
+        if not hasattr(self, "_ep_map"):
+            self._ep_map = EntryPoint.parse_map(
+                self._get_metadata('entry_points.txt'), self
+            )
+        if group is not None:
+            return self._ep_map.get(group, {})
+        return self._ep_map
+
+    def get_entry_info(self, group: str, name: str):
+        """Return the EntryPoint object for `group`+`name`, or ``None``"""
+        return self.get_entry_map(group).get(name)
+
+    # FIXME: 'Distribution.insert_on' is too complex (13)
+    def insert_on(  # noqa: C901
+        self,
+        path: list[str],
+        loc=None,
+        replace: bool = False,
+    ):
+        """Ensure self.location is on path
+
+        If replace=False (default):
+            - If location is already in path anywhere, do nothing.
+            - Else:
+              - If it's an egg and its parent directory is on path,
+                insert just ahead of the parent.
+              - Else: add to the end of path.
+        If replace=True:
+            - If location is already on path anywhere (not eggs)
+              or higher priority than its parent (eggs)
+              do nothing.
+            - Else:
+              - If it's an egg and its parent directory is on path,
+                insert just ahead of the parent,
+                removing any lower-priority entries.
+              - Else: add it to the front of path.
+        """
+
+        loc = loc or self.location
+        if not loc:
+            return
+
+        nloc = _normalize_cached(loc)
+        bdir = os.path.dirname(nloc)
+        npath = [(p and _normalize_cached(p) or p) for p in path]
+
+        for p, item in enumerate(npath):
+            if item == nloc:
+                if replace:
+                    break
+                else:
+                    # don't modify path (even removing duplicates) if
+                    # found and not replace
+                    return
+            elif item == bdir and self.precedence == EGG_DIST:
+                # if it's an .egg, give it precedence over its directory
+                # UNLESS it's already been added to sys.path and replace=False
+                if (not replace) and nloc in npath[p:]:
+                    return
+                if path is sys.path:
+                    self.check_version_conflict()
+                path.insert(p, loc)
+                npath.insert(p, nloc)
+                break
+        else:
+            if path is sys.path:
+                self.check_version_conflict()
+            if replace:
+                path.insert(0, loc)
+            else:
+                path.append(loc)
+            return
+
+        # p is the spot where we found or inserted loc; now remove duplicates
+        while True:
+            try:
+                np = npath.index(nloc, p + 1)
+            except ValueError:
+                break
+            else:
+                del npath[np], path[np]
+                # ha!
+                p = np
+
+        return
+
+    def check_version_conflict(self):
+        if self.key == 'setuptools':
+            # ignore the inevitable setuptools self-conflicts  :(
+            return
+
+        nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt'))
+        loc = normalize_path(self.location)
+        for modname in self._get_metadata('top_level.txt'):
+            if (
+                modname not in sys.modules
+                or modname in nsp
+                or modname in _namespace_packages
+            ):
+                continue
+            if modname in ('pkg_resources', 'setuptools', 'site'):
+                continue
+            fn = getattr(sys.modules[modname], '__file__', None)
+            if fn and (
+                normalize_path(fn).startswith(loc) or fn.startswith(self.location)
+            ):
+                continue
+            issue_warning(
+                "Module %s was already imported from %s, but %s is being added"
+                " to sys.path" % (modname, fn, self.location),
+            )
+
+    def has_version(self):
+        try:
+            self.version
+        except ValueError:
+            issue_warning("Unbuilt egg for " + repr(self))
+            return False
+        except SystemError:
+            # TODO: remove this except clause when python/cpython#103632 is fixed.
+            return False
+        return True
+
+    def clone(self, **kw: str | int | IResourceProvider | None):
+        """Copy this distribution, substituting in any changed keyword args"""
+        names = 'project_name version py_version platform location precedence'
+        for attr in names.split():
+            kw.setdefault(attr, getattr(self, attr, None))
+        kw.setdefault('metadata', self._provider)
+        # Unsafely unpacking. But keeping **kw for backwards and subclassing compatibility
+        return self.__class__(**kw)  # type:ignore[arg-type]
+
+    @property
+    def extras(self):
+        return [dep for dep in self._dep_map if dep]
+
+
+class EggInfoDistribution(Distribution):
+    def _reload_version(self):
+        """
+        Packages installed by distutils (e.g. numpy or scipy),
+        which uses an old safe_version, and so
+        their version numbers can get mangled when
+        converted to filenames (e.g., 1.11.0.dev0+2329eae to
+        1.11.0.dev0_2329eae). These distributions will not be
+        parsed properly
+        downstream by Distribution and safe_version, so
+        take an extra step and try to get the version number from
+        the metadata file itself instead of the filename.
+        """
+        md_version = self._get_version()
+        if md_version:
+            self._version = md_version
+        return self
+
+
+class DistInfoDistribution(Distribution):
+    """
+    Wrap an actual or potential sys.path entry
+    w/metadata, .dist-info style.
+    """
+
+    PKG_INFO = 'METADATA'
+    EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])")
+
+    @property
+    def _parsed_pkg_info(self):
+        """Parse and cache metadata"""
+        try:
+            return self._pkg_info
+        except AttributeError:
+            metadata = self.get_metadata(self.PKG_INFO)
+            self._pkg_info = email.parser.Parser().parsestr(metadata)
+            return self._pkg_info
+
+    @property
+    def _dep_map(self):
+        try:
+            return self.__dep_map
+        except AttributeError:
+            self.__dep_map = self._compute_dependencies()
+            return self.__dep_map
+
+    def _compute_dependencies(self) -> dict[str | None, list[Requirement]]:
+        """Recompute this distribution's dependencies."""
+        self.__dep_map: dict[str | None, list[Requirement]] = {None: []}
+
+        reqs: list[Requirement] = []
+        # Including any condition expressions
+        for req in self._parsed_pkg_info.get_all('Requires-Dist') or []:
+            reqs.extend(parse_requirements(req))
+
+        def reqs_for_extra(extra):
+            for req in reqs:
+                if not req.marker or req.marker.evaluate({'extra': extra}):
+                    yield req
+
+        common = types.MappingProxyType(dict.fromkeys(reqs_for_extra(None)))
+        self.__dep_map[None].extend(common)
+
+        for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []:
+            s_extra = safe_extra(extra.strip())
+            self.__dep_map[s_extra] = [
+                r for r in reqs_for_extra(extra) if r not in common
+            ]
+
+        return self.__dep_map
+
+
+_distributionImpl = {
+    '.egg': Distribution,
+    '.egg-info': EggInfoDistribution,
+    '.dist-info': DistInfoDistribution,
+}
+
+
+def issue_warning(*args, **kw):
+    level = 1
+    g = globals()
+    try:
+        # find the first stack frame that is *not* code in
+        # the pkg_resources module, to use for the warning
+        while sys._getframe(level).f_globals is g:
+            level += 1
+    except ValueError:
+        pass
+    warnings.warn(stacklevel=level + 1, *args, **kw)
+
+
+def parse_requirements(strs: _NestedStr):
+    """
+    Yield ``Requirement`` objects for each specification in `strs`.
+
+    `strs` must be a string, or a (possibly-nested) iterable thereof.
+    """
+    return map(Requirement, join_continuation(map(drop_comment, yield_lines(strs))))
+
+
+class RequirementParseError(_packaging_requirements.InvalidRequirement):
+    "Compatibility wrapper for InvalidRequirement"
+
+
+class Requirement(_packaging_requirements.Requirement):
+    def __init__(self, requirement_string: str):
+        """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!"""
+        super().__init__(requirement_string)
+        self.unsafe_name = self.name
+        project_name = safe_name(self.name)
+        self.project_name, self.key = project_name, project_name.lower()
+        self.specs = [(spec.operator, spec.version) for spec in self.specifier]
+        # packaging.requirements.Requirement uses a set for its extras. We use a variable-length tuple
+        self.extras: tuple[str] = tuple(map(safe_extra, self.extras))
+        self.hashCmp = (
+            self.key,
+            self.url,
+            self.specifier,
+            frozenset(self.extras),
+            str(self.marker) if self.marker else None,
+        )
+        self.__hash = hash(self.hashCmp)
+
+    def __eq__(self, other: object):
+        return isinstance(other, Requirement) and self.hashCmp == other.hashCmp
+
+    def __ne__(self, other):
+        return not self == other
+
+    def __contains__(self, item: Distribution | str | tuple[str, ...]) -> bool:
+        if isinstance(item, Distribution):
+            if item.key != self.key:
+                return False
+
+            item = item.version
+
+        # Allow prereleases always in order to match the previous behavior of
+        # this method. In the future this should be smarter and follow PEP 440
+        # more accurately.
+        return self.specifier.contains(item, prereleases=True)
+
+    def __hash__(self):
+        return self.__hash
+
+    def __repr__(self):
+        return "Requirement.parse(%r)" % str(self)
+
+    @staticmethod
+    def parse(s: str | Iterable[str]):
+        (req,) = parse_requirements(s)
+        return req
+
+
+def _always_object(classes):
+    """
+    Ensure object appears in the mro even
+    for old-style classes.
+    """
+    if object not in classes:
+        return classes + (object,)
+    return classes
+
+
+def _find_adapter(registry: Mapping[type, _AdapterT], ob: object) -> _AdapterT:
+    """Return an adapter factory for `ob` from `registry`"""
+    types = _always_object(inspect.getmro(getattr(ob, '__class__', type(ob))))
+    for t in types:
+        if t in registry:
+            return registry[t]
+    # _find_adapter would previously return None, and immediately be called.
+    # So we're raising a TypeError to keep backward compatibility if anyone depended on that behaviour.
+    raise TypeError(f"Could not find adapter for {registry} and {ob}")
+
+
+def ensure_directory(path: StrOrBytesPath):
+    """Ensure that the parent directory of `path` exists"""
+    dirname = os.path.dirname(path)
+    os.makedirs(dirname, exist_ok=True)
+
+
+def _bypass_ensure_directory(path):
+    """Sandbox-bypassing version of ensure_directory()"""
+    if not WRITE_SUPPORT:
+        raise OSError('"os.mkdir" not supported on this platform.')
+    dirname, filename = split(path)
+    if dirname and filename and not isdir(dirname):
+        _bypass_ensure_directory(dirname)
+        try:
+            mkdir(dirname, 0o755)
+        except FileExistsError:
+            pass
+
+
+def split_sections(s: _NestedStr) -> Iterator[tuple[str | None, list[str]]]:
+    """Split a string or iterable thereof into (section, content) pairs
+
+    Each ``section`` is a stripped version of the section header ("[section]")
+    and each ``content`` is a list of stripped lines excluding blank lines and
+    comment-only lines.  If there are any such lines before the first section
+    header, they're returned in a first ``section`` of ``None``.
+    """
+    section = None
+    content = []
+    for line in yield_lines(s):
+        if line.startswith("["):
+            if line.endswith("]"):
+                if section or content:
+                    yield section, content
+                section = line[1:-1].strip()
+                content = []
+            else:
+                raise ValueError("Invalid section heading", line)
+        else:
+            content.append(line)
+
+    # wrap up last segment
+    yield section, content
+
+
+def _mkstemp(*args, **kw):
+    old_open = os.open
+    try:
+        # temporarily bypass sandboxing
+        os.open = os_open
+        return tempfile.mkstemp(*args, **kw)
+    finally:
+        # and then put it back
+        os.open = old_open
+
+
+# Silence the PEP440Warning by default, so that end users don't get hit by it
+# randomly just because they use pkg_resources. We want to append the rule
+# because we want earlier uses of filterwarnings to take precedence over this
+# one.
+warnings.filterwarnings("ignore", category=PEP440Warning, append=True)
+
+
+class PkgResourcesDeprecationWarning(Warning):
+    """
+    Base class for warning about deprecations in ``pkg_resources``
+
+    This class is not derived from ``DeprecationWarning``, and as such is
+    visible by default.
+    """
+
+
+# Ported from ``setuptools`` to avoid introducing an import inter-dependency:
+_LOCALE_ENCODING = "locale" if sys.version_info >= (3, 10) else None
+
+
+def _read_utf8_with_fallback(file: str, fallback_encoding=_LOCALE_ENCODING) -> str:
+    """See setuptools.unicode_utils._read_utf8_with_fallback"""
+    try:
+        with open(file, "r", encoding="utf-8") as f:
+            return f.read()
+    except UnicodeDecodeError:  # pragma: no cover
+        msg = f"""\
+        ********************************************************************************
+        `encoding="utf-8"` fails with {file!r}, trying `encoding={fallback_encoding!r}`.
+
+        This fallback behaviour is considered **deprecated** and future versions of
+        `setuptools/pkg_resources` may not implement it.
+
+        Please encode {file!r} with "utf-8" to ensure future builds will succeed.
+
+        If this file was produced by `setuptools` itself, cleaning up the cached files
+        and re-building/re-installing the package with a newer version of `setuptools`
+        (e.g. by updating `build-system.requires` in its `pyproject.toml`)
+        might solve the problem.
+        ********************************************************************************
+        """
+        # TODO: Add a deadline?
+        #       See comment in setuptools.unicode_utils._Utf8EncodingNeeded
+        warnings.warn(msg, PkgResourcesDeprecationWarning, stacklevel=2)
+        with open(file, "r", encoding=fallback_encoding) as f:
+            return f.read()
+
+
+# from jaraco.functools 1.3
+def _call_aside(f, *args, **kwargs):
+    f(*args, **kwargs)
+    return f
+
+
+@_call_aside
+def _initialize(g=globals()):
+    "Set up global resource manager (deliberately not state-saved)"
+    manager = ResourceManager()
+    g['_manager'] = manager
+    g.update(
+        (name, getattr(manager, name))
+        for name in dir(manager)
+        if not name.startswith('_')
+    )
+
+
+@_call_aside
+def _initialize_master_working_set():
+    """
+    Prepare the master working set and make the ``require()``
+    API available.
+
+    This function has explicit effects on the global state
+    of pkg_resources. It is intended to be invoked once at
+    the initialization of this module.
+
+    Invocation by other packages is unsupported and done
+    at their own risk.
+    """
+    working_set = _declare_state('object', 'working_set', WorkingSet._build_master())
+
+    require = working_set.require
+    iter_entry_points = working_set.iter_entry_points
+    add_activation_listener = working_set.subscribe
+    run_script = working_set.run_script
+    # backward compatibility
+    run_main = run_script
+    # Activate all distributions already on sys.path with replace=False and
+    # ensure that all distributions added to the working set in the future
+    # (e.g. by calling ``require()``) will get activated as well,
+    # with higher priority (replace=True).
+    tuple(dist.activate(replace=False) for dist in working_set)
+    add_activation_listener(
+        lambda dist: dist.activate(replace=True),
+        existing=False,
+    )
+    working_set.entries = []
+    # match order
+    list(map(working_set.add_entry, sys.path))
+    globals().update(locals())
+
+
+if TYPE_CHECKING:
+    # All of these are set by the @_call_aside methods above
+    __resource_manager = ResourceManager()  # Won't exist at runtime
+    resource_exists = __resource_manager.resource_exists
+    resource_isdir = __resource_manager.resource_isdir
+    resource_filename = __resource_manager.resource_filename
+    resource_stream = __resource_manager.resource_stream
+    resource_string = __resource_manager.resource_string
+    resource_listdir = __resource_manager.resource_listdir
+    set_extraction_path = __resource_manager.set_extraction_path
+    cleanup_resources = __resource_manager.cleanup_resources
+
+    working_set = WorkingSet()
+    require = working_set.require
+    iter_entry_points = working_set.iter_entry_points
+    add_activation_listener = working_set.subscribe
+    run_script = working_set.run_script
+    run_main = run_script
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..aec96e9
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/LICENSE
new file mode 100644
index 0000000..f35fed9
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2010-202x The platformdirs developers
+
+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.
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__init__.py
new file mode 100644
index 0000000..92fc2da
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__init__.py
@@ -0,0 +1,917 @@
+"""Utilities for determining application-specific dirs.
+
+Provides convenience functions (e.g. :func:`user_data_dir`, :func:`user_config_path`), a :data:`PlatformDirs` class that
+auto-detects the current platform, and the :class:`~platformdirs.api.PlatformDirsABC` base class.
+
+See  for details and usage.
+
+"""
+
+from __future__ import annotations
+
+import os
+import sys
+from typing import TYPE_CHECKING
+
+from .api import PlatformDirsABC
+from .version import __version__
+from .version import __version_tuple__ as __version_info__
+
+if TYPE_CHECKING:
+    from pathlib import Path
+    from typing import Literal
+
+if sys.platform == "win32":
+    from pip._vendor.platformdirs.windows import Windows as _Result
+elif sys.platform == "darwin":
+    from pip._vendor.platformdirs.macos import MacOS as _Result
+else:
+    from pip._vendor.platformdirs.unix import Unix as _Result
+
+
+def _set_platform_dir_class() -> type[PlatformDirsABC]:
+    if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system":
+        if os.getenv("SHELL") or os.getenv("PREFIX"):
+            return _Result
+
+        from pip._vendor.platformdirs.android import _android_folder  # noqa: PLC0415
+
+        if _android_folder() is not None:
+            from pip._vendor.platformdirs.android import Android  # noqa: PLC0415
+
+            return Android  # return to avoid redefinition of a result
+
+    return _Result
+
+
+if TYPE_CHECKING:
+    # Work around mypy issue: https://github.com/python/mypy/issues/10962
+    PlatformDirs = _Result
+else:
+    PlatformDirs = _set_platform_dir_class()  #: Currently active platform
+AppDirs = PlatformDirs  #: Backwards compatibility with appdirs
+
+
+def user_data_dir(  # noqa: PLR0913, PLR0917
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    roaming: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+    use_site_for_root: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :param use_site_for_root: See `use_site_for_root `.
+
+    :returns: data directory tied to the user
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        roaming=roaming,
+        ensure_exists=ensure_exists,
+        use_site_for_root=use_site_for_root,
+    ).user_data_dir
+
+
+def site_data_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    multipath: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param multipath: See `multipath `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: data directory shared by users
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        multipath=multipath,
+        ensure_exists=ensure_exists,
+    ).site_data_dir
+
+
+def user_config_dir(  # noqa: PLR0913, PLR0917
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    roaming: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+    use_site_for_root: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :param use_site_for_root: See `use_site_for_root `.
+
+    :returns: config directory tied to the user
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        roaming=roaming,
+        ensure_exists=ensure_exists,
+        use_site_for_root=use_site_for_root,
+    ).user_config_dir
+
+
+def site_config_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    multipath: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param multipath: See `multipath `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: config directory shared by users
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        multipath=multipath,
+        ensure_exists=ensure_exists,
+    ).site_config_dir
+
+
+def user_cache_dir(  # noqa: PLR0913, PLR0917
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+    use_site_for_root: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+    :param use_site_for_root: See `use_site_for_root `.
+
+    :returns: cache directory tied to the user
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+        use_site_for_root=use_site_for_root,
+    ).user_cache_dir
+
+
+def site_cache_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: cache directory shared by users
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).site_cache_dir
+
+
+def user_state_dir(  # noqa: PLR0913, PLR0917
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    roaming: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+    use_site_for_root: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :param use_site_for_root: See `use_site_for_root `.
+
+    :returns: state directory tied to the user
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        roaming=roaming,
+        ensure_exists=ensure_exists,
+        use_site_for_root=use_site_for_root,
+    ).user_state_dir
+
+
+def site_state_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: state directory shared by users
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        ensure_exists=ensure_exists,
+    ).site_state_dir
+
+
+def user_log_dir(  # noqa: PLR0913, PLR0917
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+    use_site_for_root: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+    :param use_site_for_root: See `use_site_for_root `.
+
+    :returns: log directory tied to the user
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+        use_site_for_root=use_site_for_root,
+    ).user_log_dir
+
+
+def site_log_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: log directory shared by users
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).site_log_dir
+
+
+def user_documents_dir() -> str:
+    """:returns: documents directory tied to the user"""
+    return PlatformDirs().user_documents_dir
+
+
+def user_downloads_dir() -> str:
+    """:returns: downloads directory tied to the user"""
+    return PlatformDirs().user_downloads_dir
+
+
+def user_pictures_dir() -> str:
+    """:returns: pictures directory tied to the user"""
+    return PlatformDirs().user_pictures_dir
+
+
+def user_videos_dir() -> str:
+    """:returns: videos directory tied to the user"""
+    return PlatformDirs().user_videos_dir
+
+
+def user_music_dir() -> str:
+    """:returns: music directory tied to the user"""
+    return PlatformDirs().user_music_dir
+
+
+def user_desktop_dir() -> str:
+    """:returns: desktop directory tied to the user"""
+    return PlatformDirs().user_desktop_dir
+
+
+def user_projects_dir() -> str:
+    """:returns: projects directory tied to the user"""
+    return PlatformDirs().user_projects_dir
+
+
+def user_publicshare_dir() -> str:
+    """:returns: public share directory tied to the user"""
+    return PlatformDirs().user_publicshare_dir
+
+
+def user_templates_dir() -> str:
+    """:returns: templates directory tied to the user"""
+    return PlatformDirs().user_templates_dir
+
+
+def user_fonts_dir() -> str:
+    """:returns: fonts directory tied to the user"""
+    return PlatformDirs().user_fonts_dir
+
+
+def user_preference_dir() -> str:
+    """:returns: preference directory tied to the user"""
+    return PlatformDirs().user_preference_dir
+
+
+def user_bin_dir() -> str:
+    """:returns: bin directory tied to the user"""
+    return PlatformDirs().user_bin_dir
+
+
+def site_bin_dir() -> str:
+    """:returns: bin directory shared by users"""
+    return PlatformDirs().site_bin_dir
+
+
+def user_applications_dir() -> str:
+    """:returns: applications directory tied to the user"""
+    return PlatformDirs().user_applications_dir
+
+
+def site_applications_dir(
+    multipath: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """:param multipath: See `multipath `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: applications directory shared by users
+
+    """
+    return PlatformDirs(
+        multipath=multipath,
+        ensure_exists=ensure_exists,
+    ).site_applications_dir
+
+
+def user_runtime_dir(  # noqa: PLR0913, PLR0917
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+    use_site_for_root: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+    :param use_site_for_root: See `use_site_for_root `.
+
+    :returns: runtime directory tied to the user
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+        use_site_for_root=use_site_for_root,
+    ).user_runtime_dir
+
+
+def site_runtime_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: runtime directory shared by users
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).site_runtime_dir
+
+
+def user_data_path(  # noqa: PLR0913, PLR0917
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    roaming: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+    use_site_for_root: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :param use_site_for_root: See `use_site_for_root `.
+
+    :returns: data path tied to the user
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        roaming=roaming,
+        ensure_exists=ensure_exists,
+        use_site_for_root=use_site_for_root,
+    ).user_data_path
+
+
+def site_data_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    multipath: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param multipath: See `multipath `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: data path shared by users
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        multipath=multipath,
+        ensure_exists=ensure_exists,
+    ).site_data_path
+
+
+def user_config_path(  # noqa: PLR0913, PLR0917
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    roaming: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+    use_site_for_root: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :param use_site_for_root: See `use_site_for_root `.
+
+    :returns: config path tied to the user
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        roaming=roaming,
+        ensure_exists=ensure_exists,
+        use_site_for_root=use_site_for_root,
+    ).user_config_path
+
+
+def site_config_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    multipath: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param multipath: See `multipath `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: config path shared by users
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        multipath=multipath,
+        ensure_exists=ensure_exists,
+    ).site_config_path
+
+
+def site_cache_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: cache path shared by users
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).site_cache_path
+
+
+def user_cache_path(  # noqa: PLR0913, PLR0917
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+    use_site_for_root: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+    :param use_site_for_root: See `use_site_for_root `.
+
+    :returns: cache path tied to the user
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+        use_site_for_root=use_site_for_root,
+    ).user_cache_path
+
+
+def user_state_path(  # noqa: PLR0913, PLR0917
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    roaming: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+    use_site_for_root: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :param use_site_for_root: See `use_site_for_root `.
+
+    :returns: state path tied to the user
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        roaming=roaming,
+        ensure_exists=ensure_exists,
+        use_site_for_root=use_site_for_root,
+    ).user_state_path
+
+
+def site_state_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: state path shared by users
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        ensure_exists=ensure_exists,
+    ).site_state_path
+
+
+def user_log_path(  # noqa: PLR0913, PLR0917
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+    use_site_for_root: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+    :param use_site_for_root: See `use_site_for_root `.
+
+    :returns: log path tied to the user
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+        use_site_for_root=use_site_for_root,
+    ).user_log_path
+
+
+def site_log_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: log path shared by users
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).site_log_path
+
+
+def user_documents_path() -> Path:
+    """:returns: documents path tied to the user"""
+    return PlatformDirs().user_documents_path
+
+
+def user_downloads_path() -> Path:
+    """:returns: downloads path tied to the user"""
+    return PlatformDirs().user_downloads_path
+
+
+def user_pictures_path() -> Path:
+    """:returns: pictures path tied to the user"""
+    return PlatformDirs().user_pictures_path
+
+
+def user_videos_path() -> Path:
+    """:returns: videos path tied to the user"""
+    return PlatformDirs().user_videos_path
+
+
+def user_music_path() -> Path:
+    """:returns: music path tied to the user"""
+    return PlatformDirs().user_music_path
+
+
+def user_desktop_path() -> Path:
+    """:returns: desktop path tied to the user"""
+    return PlatformDirs().user_desktop_path
+
+
+def user_projects_path() -> Path:
+    """:returns: projects path tied to the user"""
+    return PlatformDirs().user_projects_path
+
+
+def user_publicshare_path() -> Path:
+    """:returns: public share path tied to the user"""
+    return PlatformDirs().user_publicshare_path
+
+
+def user_templates_path() -> Path:
+    """:returns: templates path tied to the user"""
+    return PlatformDirs().user_templates_path
+
+
+def user_fonts_path() -> Path:
+    """:returns: fonts path tied to the user"""
+    return PlatformDirs().user_fonts_path
+
+
+def user_preference_path() -> Path:
+    """:returns: preference path tied to the user"""
+    return PlatformDirs().user_preference_path
+
+
+def user_bin_path() -> Path:
+    """:returns: bin path tied to the user"""
+    return PlatformDirs().user_bin_path
+
+
+def site_bin_path() -> Path:
+    """:returns: bin path shared by users"""
+    return PlatformDirs().site_bin_path
+
+
+def user_applications_path() -> Path:
+    """:returns: applications path tied to the user"""
+    return PlatformDirs().user_applications_path
+
+
+def site_applications_path(
+    multipath: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """:param multipath: See `multipath `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: applications path shared by users
+
+    """
+    return PlatformDirs(
+        multipath=multipath,
+        ensure_exists=ensure_exists,
+    ).site_applications_path
+
+
+def user_runtime_path(  # noqa: PLR0913, PLR0917
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+    use_site_for_root: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+    :param use_site_for_root: See `use_site_for_root `.
+
+    :returns: runtime path tied to the user
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+        use_site_for_root=use_site_for_root,
+    ).user_runtime_path
+
+
+def site_runtime_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """:param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+
+    :returns: runtime path shared by users
+
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).site_runtime_path
+
+
+__all__ = [
+    "AppDirs",
+    "PlatformDirs",
+    "PlatformDirsABC",
+    "__version__",
+    "__version_info__",
+    "site_applications_dir",
+    "site_applications_path",
+    "site_bin_dir",
+    "site_bin_path",
+    "site_cache_dir",
+    "site_cache_path",
+    "site_config_dir",
+    "site_config_path",
+    "site_data_dir",
+    "site_data_path",
+    "site_log_dir",
+    "site_log_path",
+    "site_runtime_dir",
+    "site_runtime_path",
+    "site_state_dir",
+    "site_state_path",
+    "user_applications_dir",
+    "user_applications_path",
+    "user_bin_dir",
+    "user_bin_path",
+    "user_cache_dir",
+    "user_cache_path",
+    "user_config_dir",
+    "user_config_path",
+    "user_data_dir",
+    "user_data_path",
+    "user_desktop_dir",
+    "user_desktop_path",
+    "user_documents_dir",
+    "user_documents_path",
+    "user_downloads_dir",
+    "user_downloads_path",
+    "user_fonts_dir",
+    "user_fonts_path",
+    "user_log_dir",
+    "user_log_path",
+    "user_music_dir",
+    "user_music_path",
+    "user_pictures_dir",
+    "user_pictures_path",
+    "user_preference_dir",
+    "user_preference_path",
+    "user_projects_dir",
+    "user_projects_path",
+    "user_publicshare_dir",
+    "user_publicshare_path",
+    "user_runtime_dir",
+    "user_runtime_path",
+    "user_state_dir",
+    "user_state_path",
+    "user_templates_dir",
+    "user_templates_path",
+    "user_videos_dir",
+    "user_videos_path",
+]
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__main__.py b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__main__.py
new file mode 100644
index 0000000..0d1877b
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__main__.py
@@ -0,0 +1,66 @@
+"""Main entry point."""
+
+from __future__ import annotations
+
+from pip._vendor.platformdirs import PlatformDirs, __version__
+
+PROPS = (
+    "user_data_dir",
+    "user_config_dir",
+    "user_cache_dir",
+    "user_state_dir",
+    "user_log_dir",
+    "user_documents_dir",
+    "user_downloads_dir",
+    "user_pictures_dir",
+    "user_videos_dir",
+    "user_music_dir",
+    "user_projects_dir",
+    "user_publicshare_dir",
+    "user_templates_dir",
+    "user_fonts_dir",
+    "user_preference_dir",
+    "user_bin_dir",
+    "site_bin_dir",
+    "user_applications_dir",
+    "user_runtime_dir",
+    "site_data_dir",
+    "site_config_dir",
+    "site_cache_dir",
+    "site_state_dir",
+    "site_log_dir",
+    "site_applications_dir",
+    "site_runtime_dir",
+)
+
+
+def main() -> None:
+    """Run the main entry point."""
+    app_name = "MyApp"
+    app_author = "MyCompany"
+
+    print(f"-- platformdirs {__version__} --")  # noqa: T201
+
+    print("-- app dirs (with optional 'version')")  # noqa: T201
+    dirs = PlatformDirs(app_name, app_author, version="1.0")
+    for prop in PROPS:
+        print(f"{prop}: {getattr(dirs, prop)}")  # noqa: T201
+
+    print("\n-- app dirs (without optional 'version')")  # noqa: T201
+    dirs = PlatformDirs(app_name, app_author)
+    for prop in PROPS:
+        print(f"{prop}: {getattr(dirs, prop)}")  # noqa: T201
+
+    print("\n-- app dirs (without optional 'appauthor')")  # noqa: T201
+    dirs = PlatformDirs(app_name)
+    for prop in PROPS:
+        print(f"{prop}: {getattr(dirs, prop)}")  # noqa: T201
+
+    print("\n-- app dirs (with disabled 'appauthor')")  # noqa: T201
+    dirs = PlatformDirs(app_name, appauthor=False)
+    for prop in PROPS:
+        print(f"{prop}: {getattr(dirs, prop)}")  # noqa: T201
+
+
+if __name__ == "__main__":
+    main()
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..ec1a8ea
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc
new file mode 100644
index 0000000..e0ad3d4
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/_xdg.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/_xdg.cpython-312.pyc
new file mode 100644
index 0000000..9c8e91a
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/_xdg.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/android.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/android.cpython-312.pyc
new file mode 100644
index 0000000..f493ad0
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/android.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/api.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/api.cpython-312.pyc
new file mode 100644
index 0000000..b8ffe3d
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/api.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc
new file mode 100644
index 0000000..d712378
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc
new file mode 100644
index 0000000..e15e942
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/version.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/version.cpython-312.pyc
new file mode 100644
index 0000000..37277d1
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/version.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc
new file mode 100644
index 0000000..191538d
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/_xdg.py b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/_xdg.py
new file mode 100644
index 0000000..b2af284
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/_xdg.py
@@ -0,0 +1,171 @@
+"""XDG environment variable mixin for Unix and macOS."""
+
+from __future__ import annotations
+
+import os
+
+from .api import PlatformDirsABC
+
+
+class XDGMixin(PlatformDirsABC):
+    """Mixin that checks XDG environment variables, falling back to platform-specific defaults via ``super()``."""
+
+    @property
+    def user_data_dir(self) -> str:
+        """:returns: data directory tied to the user, from ``$XDG_DATA_HOME`` if set, else platform default"""
+        if path := os.environ.get("XDG_DATA_HOME", "").strip():
+            return self._append_app_name_and_version(path)
+        return super().user_data_dir
+
+    @property
+    def _site_data_dirs(self) -> list[str]:
+        if xdg_dirs := os.environ.get("XDG_DATA_DIRS", "").strip():
+            return [self._append_app_name_and_version(p) for p in xdg_dirs.split(os.pathsep) if p.strip()]
+        return super()._site_data_dirs
+
+    @property
+    def site_data_dir(self) -> str:
+        """:returns: data directories shared by users, from ``$XDG_DATA_DIRS`` if set, else platform default"""
+        dirs = self._site_data_dirs
+        return os.pathsep.join(dirs) if self.multipath else dirs[0]
+
+    @property
+    def user_config_dir(self) -> str:
+        """:returns: config directory tied to the user, from ``$XDG_CONFIG_HOME`` if set, else platform default"""
+        if path := os.environ.get("XDG_CONFIG_HOME", "").strip():
+            return self._append_app_name_and_version(path)
+        return super().user_config_dir
+
+    @property
+    def _site_config_dirs(self) -> list[str]:
+        if xdg_dirs := os.environ.get("XDG_CONFIG_DIRS", "").strip():
+            return [self._append_app_name_and_version(p) for p in xdg_dirs.split(os.pathsep) if p.strip()]
+        return super()._site_config_dirs
+
+    @property
+    def site_config_dir(self) -> str:
+        """:returns: config directories shared by users, from ``$XDG_CONFIG_DIRS`` if set, else platform default"""
+        dirs = self._site_config_dirs
+        return os.pathsep.join(dirs) if self.multipath else dirs[0]
+
+    @property
+    def user_cache_dir(self) -> str:
+        """:returns: cache directory tied to the user, from ``$XDG_CACHE_HOME`` if set, else platform default"""
+        if path := os.environ.get("XDG_CACHE_HOME", "").strip():
+            return self._append_app_name_and_version(path)
+        return super().user_cache_dir
+
+    @property
+    def user_state_dir(self) -> str:
+        """:returns: state directory tied to the user, from ``$XDG_STATE_HOME`` if set, else platform default"""
+        if path := os.environ.get("XDG_STATE_HOME", "").strip():
+            return self._append_app_name_and_version(path)
+        return super().user_state_dir
+
+    @property
+    def user_runtime_dir(self) -> str:
+        """:returns: runtime directory tied to the user, from ``$XDG_RUNTIME_DIR`` if set, else platform default"""
+        if path := os.environ.get("XDG_RUNTIME_DIR", "").strip():
+            return self._append_app_name_and_version(path)
+        return super().user_runtime_dir
+
+    @property
+    def site_runtime_dir(self) -> str:
+        """:returns: runtime directory shared by users, from ``$XDG_RUNTIME_DIR`` if set, else platform default"""
+        if path := os.environ.get("XDG_RUNTIME_DIR", "").strip():
+            return self._append_app_name_and_version(path)
+        return super().site_runtime_dir
+
+    @property
+    def user_documents_dir(self) -> str:
+        """:returns: documents directory tied to the user, from ``$XDG_DOCUMENTS_DIR`` if set, else platform default"""
+        if path := os.environ.get("XDG_DOCUMENTS_DIR", "").strip():
+            return os.path.expanduser(path)  # noqa: PTH111
+        return super().user_documents_dir
+
+    @property
+    def user_downloads_dir(self) -> str:
+        """:returns: downloads directory tied to the user, from ``$XDG_DOWNLOAD_DIR`` if set, else platform default"""
+        if path := os.environ.get("XDG_DOWNLOAD_DIR", "").strip():
+            return os.path.expanduser(path)  # noqa: PTH111
+        return super().user_downloads_dir
+
+    @property
+    def user_pictures_dir(self) -> str:
+        """:returns: pictures directory tied to the user, from ``$XDG_PICTURES_DIR`` if set, else platform default"""
+        if path := os.environ.get("XDG_PICTURES_DIR", "").strip():
+            return os.path.expanduser(path)  # noqa: PTH111
+        return super().user_pictures_dir
+
+    @property
+    def user_videos_dir(self) -> str:
+        """:returns: videos directory tied to the user, from ``$XDG_VIDEOS_DIR`` if set, else platform default"""
+        if path := os.environ.get("XDG_VIDEOS_DIR", "").strip():
+            return os.path.expanduser(path)  # noqa: PTH111
+        return super().user_videos_dir
+
+    @property
+    def user_music_dir(self) -> str:
+        """:returns: music directory tied to the user, from ``$XDG_MUSIC_DIR`` if set, else platform default"""
+        if path := os.environ.get("XDG_MUSIC_DIR", "").strip():
+            return os.path.expanduser(path)  # noqa: PTH111
+        return super().user_music_dir
+
+    @property
+    def user_desktop_dir(self) -> str:
+        """:returns: desktop directory tied to the user, from ``$XDG_DESKTOP_DIR`` if set, else platform default"""
+        if path := os.environ.get("XDG_DESKTOP_DIR", "").strip():
+            return os.path.expanduser(path)  # noqa: PTH111
+        return super().user_desktop_dir
+
+    @property
+    def user_projects_dir(self) -> str:
+        """:returns: projects directory tied to the user, from ``$XDG_PROJECTS_DIR`` if set, else platform default"""
+        if path := os.environ.get("XDG_PROJECTS_DIR", "").strip():
+            return os.path.expanduser(path)  # noqa: PTH111  # API returns str, not Path
+        return super().user_projects_dir
+
+    @property
+    def user_publicshare_dir(self) -> str:
+        """:returns: public share directory tied to the user, from ``$XDG_PUBLICSHARE_DIR`` if set, else platform default"""
+        if path := os.environ.get("XDG_PUBLICSHARE_DIR", "").strip():
+            return os.path.expanduser(path)  # noqa: PTH111  # API returns str, not Path
+        return super().user_publicshare_dir
+
+    @property
+    def user_templates_dir(self) -> str:
+        """:returns: templates directory tied to the user, from ``$XDG_TEMPLATES_DIR`` if set, else platform default"""
+        if path := os.environ.get("XDG_TEMPLATES_DIR", "").strip():
+            return os.path.expanduser(path)  # noqa: PTH111  # API returns str, not Path
+        return super().user_templates_dir
+
+    @property
+    def user_fonts_dir(self) -> str:
+        """:returns: fonts directory tied to the user, from ``$XDG_DATA_HOME/fonts`` if set, else platform default"""
+        if path := os.environ.get("XDG_DATA_HOME", "").strip():
+            return f"{os.path.expanduser(path)}/fonts"  # noqa: PTH111  # API returns str, not Path
+        return super().user_fonts_dir
+
+    @property
+    def user_applications_dir(self) -> str:
+        """:returns: applications directory tied to the user, from ``$XDG_DATA_HOME`` if set, else platform default"""
+        if path := os.environ.get("XDG_DATA_HOME", "").strip():
+            return os.path.join(os.path.expanduser(path), "applications")  # noqa: PTH111, PTH118
+        return super().user_applications_dir
+
+    @property
+    def _site_applications_dirs(self) -> list[str]:
+        if xdg_dirs := os.environ.get("XDG_DATA_DIRS", "").strip():
+            return [os.path.join(p, "applications") for p in xdg_dirs.split(os.pathsep) if p.strip()]  # noqa: PTH118
+        return super()._site_applications_dirs
+
+    @property
+    def site_applications_dir(self) -> str:
+        """:returns: applications directories shared by users, from ``$XDG_DATA_DIRS`` if set, else platform default"""
+        dirs = self._site_applications_dirs
+        return os.pathsep.join(dirs) if self.multipath else dirs[0]
+
+
+__all__ = [
+    "XDGMixin",
+]
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/android.py b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/android.py
new file mode 100644
index 0000000..885e8c6
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/android.py
@@ -0,0 +1,300 @@
+"""Android."""
+
+from __future__ import annotations
+
+import os
+import re
+import sys
+from functools import lru_cache
+from typing import TYPE_CHECKING, cast
+
+from .api import PlatformDirsABC
+
+
+class Android(PlatformDirsABC):  # noqa: PLR0904
+    """Platform directories for Android.
+
+    Follows the guidance `from here `_. Directories are typically located
+    under the app's private storage (``/data/user///``).
+
+    Makes use of the `appname `, `version
+    `, `opinion `, `ensure_exists
+    `.
+
+    """
+
+    @property
+    def user_data_dir(self) -> str:
+        """:returns: data directory tied to the user, e.g. ``/data/user///files/``"""
+        return self._append_app_name_and_version(cast("str", _android_folder()), "files")
+
+    @property
+    def site_data_dir(self) -> str:
+        """:returns: data directory shared by users, same as `user_data_dir`"""
+        return self.user_data_dir
+
+    @property
+    def user_config_dir(self) -> str:
+        """:returns: config directory tied to the user, e.g. ``/data/user///shared_prefs/``"""
+        return self._append_app_name_and_version(cast("str", _android_folder()), "shared_prefs")
+
+    @property
+    def site_config_dir(self) -> str:
+        """:returns: config directory shared by users, same as `user_config_dir`"""
+        return self.user_config_dir
+
+    @property
+    def user_cache_dir(self) -> str:
+        """:returns: cache directory tied to the user, e.g.,``/data/user///cache/``"""
+        return self._append_app_name_and_version(cast("str", _android_folder()), "cache")
+
+    @property
+    def site_cache_dir(self) -> str:
+        """:returns: cache directory shared by users, same as `user_cache_dir`"""
+        return self.user_cache_dir
+
+    @property
+    def user_state_dir(self) -> str:
+        """:returns: state directory tied to the user, same as `user_data_dir`"""
+        return self.user_data_dir
+
+    @property
+    def site_state_dir(self) -> str:
+        """:returns: state directory shared by users, same as `user_state_dir`"""
+        return self.user_state_dir
+
+    @property
+    def user_log_dir(self) -> str:
+        """:returns: log directory tied to the user, same as `user_cache_dir` if not opinionated else ``log`` in it, e.g. ``/data/user///cache//log``"""
+        path = self.user_cache_dir
+        if self.opinion:
+            path = os.path.join(path, "log")  # noqa: PTH118
+            self._optionally_create_directory(path)
+        return path
+
+    @property
+    def site_log_dir(self) -> str:
+        """:returns: log directory shared by users, same as `user_log_dir`"""
+        return self.user_log_dir
+
+    @property
+    def user_documents_dir(self) -> str:
+        """:returns: documents directory tied to the user e.g. ``/storage/emulated/0/Documents``"""
+        return _android_documents_folder()
+
+    @property
+    def user_downloads_dir(self) -> str:
+        """:returns: downloads directory tied to the user e.g. ``/storage/emulated/0/Downloads``"""
+        return _android_downloads_folder()
+
+    @property
+    def user_pictures_dir(self) -> str:
+        """:returns: pictures directory tied to the user e.g. ``/storage/emulated/0/Pictures``"""
+        return _android_pictures_folder()
+
+    @property
+    def user_videos_dir(self) -> str:
+        """:returns: videos directory tied to the user e.g. ``/storage/emulated/0/DCIM/Camera``"""
+        return _android_videos_folder()
+
+    @property
+    def user_music_dir(self) -> str:
+        """:returns: music directory tied to the user e.g. ``/storage/emulated/0/Music``"""
+        return _android_music_folder()
+
+    @property
+    def user_desktop_dir(self) -> str:
+        """:returns: desktop directory tied to the user e.g. ``/storage/emulated/0/Desktop``"""
+        return "/storage/emulated/0/Desktop"
+
+    @property
+    def user_projects_dir(self) -> str:
+        """:returns: projects directory tied to the user e.g. ``/storage/emulated/0/Projects``"""
+        return "/storage/emulated/0/Projects"
+
+    @property
+    def user_publicshare_dir(self) -> str:
+        """:returns: public share directory tied to the user e.g. ``/storage/emulated/0/Public``"""
+        return "/storage/emulated/0/Public"
+
+    @property
+    def user_templates_dir(self) -> str:
+        """:returns: templates directory tied to the user e.g. ``/storage/emulated/0/Templates``"""
+        return "/storage/emulated/0/Templates"
+
+    @property
+    def user_fonts_dir(self) -> str:
+        """:returns: fonts directory tied to the user e.g. ``/storage/emulated/0/fonts``"""
+        return "/storage/emulated/0/fonts"
+
+    @property
+    def user_preference_dir(self) -> str:
+        """:returns: preference directory tied to the user, same as ``user_config_dir``"""
+        return self.user_config_dir
+
+    @property
+    def user_bin_dir(self) -> str:
+        """:returns: bin directory tied to the user, e.g. ``/data/user///files/bin``"""
+        return os.path.join(cast("str", _android_folder()), "files", "bin")  # noqa: PTH118
+
+    @property
+    def site_bin_dir(self) -> str:
+        """:returns: bin directory shared by users, same as `user_bin_dir`"""
+        return self.user_bin_dir
+
+    @property
+    def user_applications_dir(self) -> str:
+        """:returns: applications directory tied to the user, same as `user_data_dir`"""
+        return self.user_data_dir
+
+    @property
+    def site_applications_dir(self) -> str:
+        """:returns: applications directory shared by users, same as `user_applications_dir`"""
+        return self.user_applications_dir
+
+    @property
+    def user_runtime_dir(self) -> str:
+        """:returns: runtime directory tied to the user, same as `user_cache_dir` if not opinionated else ``tmp`` in it, e.g. ``/data/user///cache//tmp``"""
+        path = self.user_cache_dir
+        if self.opinion:
+            path = os.path.join(path, "tmp")  # noqa: PTH118
+            self._optionally_create_directory(path)
+        return path
+
+    @property
+    def site_runtime_dir(self) -> str:
+        """:returns: runtime directory shared by users, same as `user_runtime_dir`"""
+        return self.user_runtime_dir
+
+
+@lru_cache(maxsize=1)
+def _android_folder() -> str | None:  # noqa: C901
+    """:returns: base folder for the Android OS or None if it cannot be found"""
+    result: str | None = None
+    # type checker isn't happy with our "import android", just don't do this when type checking see
+    # https://stackoverflow.com/a/61394121
+    if not TYPE_CHECKING:
+        try:
+            # First try to get a path to android app using python4android (if available)...
+            from android import mActivity  # noqa: PLC0415
+
+            context = cast("android.content.Context", mActivity.getApplicationContext())  # noqa: F821
+            result = context.getFilesDir().getParentFile().getAbsolutePath()
+        except Exception:  # noqa: BLE001
+            result = None
+    if result is None:
+        try:
+            # ...and fall back to using plain pyjnius, if python4android isn't available or doesn't deliver any useful
+            # result...
+            from jnius import autoclass  # noqa: PLC0415  # ty: ignore[unresolved-import]
+
+            context = autoclass("android.content.Context")
+            result = context.getFilesDir().getParentFile().getAbsolutePath()
+        except Exception:  # noqa: BLE001
+            result = None
+    if result is None:
+        # and if that fails, too, find an android folder looking at path on the sys.path
+        # warning: only works for apps installed under /data, not adopted storage etc.
+        pattern = re.compile(r"/data/(data|user/\d+)/(.+)/files")
+        for path in sys.path:
+            if pattern.match(path):
+                result = path.split("/files")[0]
+                break
+        else:
+            result = None
+    if result is None:
+        # one last try: find an android folder looking at path on the sys.path taking adopted storage paths into
+        # account
+        pattern = re.compile(r"/mnt/expand/[a-fA-F0-9-]{36}/(data|user/\d+)/(.+)/files")
+        for path in sys.path:
+            if pattern.match(path):
+                result = path.split("/files")[0]
+                break
+        else:
+            result = None
+    return result
+
+
+@lru_cache(maxsize=1)
+def _android_documents_folder() -> str:
+    """:returns: documents folder for the Android OS"""
+    # Get directories with pyjnius
+    try:
+        from jnius import autoclass  # noqa: PLC0415  # ty: ignore[unresolved-import]
+
+        context = autoclass("android.content.Context")
+        environment = autoclass("android.os.Environment")
+        documents_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOCUMENTS).getAbsolutePath()
+    except Exception:  # noqa: BLE001
+        documents_dir = "/storage/emulated/0/Documents"
+
+    return documents_dir
+
+
+@lru_cache(maxsize=1)
+def _android_downloads_folder() -> str:
+    """:returns: downloads folder for the Android OS"""
+    # Get directories with pyjnius
+    try:
+        from jnius import autoclass  # noqa: PLC0415  # ty: ignore[unresolved-import]
+
+        context = autoclass("android.content.Context")
+        environment = autoclass("android.os.Environment")
+        downloads_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOWNLOADS).getAbsolutePath()
+    except Exception:  # noqa: BLE001
+        downloads_dir = "/storage/emulated/0/Downloads"
+
+    return downloads_dir
+
+
+@lru_cache(maxsize=1)
+def _android_pictures_folder() -> str:
+    """:returns: pictures folder for the Android OS"""
+    # Get directories with pyjnius
+    try:
+        from jnius import autoclass  # noqa: PLC0415  # ty: ignore[unresolved-import]
+
+        context = autoclass("android.content.Context")
+        environment = autoclass("android.os.Environment")
+        pictures_dir: str = context.getExternalFilesDir(environment.DIRECTORY_PICTURES).getAbsolutePath()
+    except Exception:  # noqa: BLE001
+        pictures_dir = "/storage/emulated/0/Pictures"
+
+    return pictures_dir
+
+
+@lru_cache(maxsize=1)
+def _android_videos_folder() -> str:
+    """:returns: videos folder for the Android OS"""
+    # Get directories with pyjnius
+    try:
+        from jnius import autoclass  # noqa: PLC0415  # ty: ignore[unresolved-import]
+
+        context = autoclass("android.content.Context")
+        environment = autoclass("android.os.Environment")
+        videos_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DCIM).getAbsolutePath()
+    except Exception:  # noqa: BLE001
+        videos_dir = "/storage/emulated/0/DCIM/Camera"
+
+    return videos_dir
+
+
+@lru_cache(maxsize=1)
+def _android_music_folder() -> str:
+    """:returns: music folder for the Android OS"""
+    # Get directories with pyjnius
+    try:
+        from jnius import autoclass  # noqa: PLC0415  # ty: ignore[unresolved-import]
+
+        context = autoclass("android.content.Context")
+        environment = autoclass("android.os.Environment")
+        music_dir: str = context.getExternalFilesDir(environment.DIRECTORY_MUSIC).getAbsolutePath()
+    except Exception:  # noqa: BLE001
+        music_dir = "/storage/emulated/0/Music"
+
+    return music_dir
+
+
+__all__ = [
+    "Android",
+]
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/api.py b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/api.py
new file mode 100644
index 0000000..91ef902
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/api.py
@@ -0,0 +1,463 @@
+"""Base API."""
+
+from __future__ import annotations
+
+import os
+from abc import ABC, abstractmethod
+from pathlib import Path
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from collections.abc import Iterator
+    from typing import Literal
+
+
+class PlatformDirsABC(ABC):  # noqa: PLR0904
+    """Abstract base class defining all platform directory properties, their :class:`~pathlib.Path` variants, and iterators.
+
+    Platform-specific subclasses (e.g. :class:`~platformdirs.windows.Windows`, :class:`~platformdirs.macos.MacOS`,
+    :class:`~platformdirs.unix.Unix`) implement the abstract properties to return the appropriate paths for each
+    operating system.
+
+    """
+
+    def __init__(  # noqa: PLR0913, PLR0917
+        self,
+        appname: str | None = None,
+        appauthor: str | Literal[False] | None = None,
+        version: str | None = None,
+        roaming: bool = False,  # noqa: FBT001, FBT002
+        multipath: bool = False,  # noqa: FBT001, FBT002
+        opinion: bool = True,  # noqa: FBT001, FBT002
+        ensure_exists: bool = False,  # noqa: FBT001, FBT002
+        use_site_for_root: bool = False,  # noqa: FBT001, FBT002
+    ) -> None:
+        """Create a new platform directory.
+
+        :param appname: See `appname`.
+        :param appauthor: See `appauthor`.
+        :param version: See `version`.
+        :param roaming: See `roaming`.
+        :param multipath: See `multipath`.
+        :param opinion: See `opinion`.
+        :param ensure_exists: See `ensure_exists`.
+        :param use_site_for_root: See `use_site_for_root`.
+
+        """
+        self.appname = appname  #: The name of the application.
+        self.appauthor = appauthor
+        """The name of the app author or distributing body for this application.
+
+        Typically, it is the owning company name. Defaults to `appname`. You may pass ``False`` to disable it.
+
+        .. note::
+
+            On Windows, the directory structure is ``//``. When ``appauthor`` is ``None`` (the
+            default), it falls back to ``appname``, resulting in ``//`` (e.g.
+            ``AppData/Local/myapp/myapp``). Pass ``appauthor=False`` to omit the author directory entirely and get
+            ``/``.
+
+        """
+        self.version = version
+        """An optional version path element to append to the path.
+
+        You might want to use this if you want multiple versions of your app to be able to run independently. If used,
+        this would typically be ``.``.
+
+        """
+        self.roaming = roaming
+        """Whether to use the roaming appdata directory on Windows.
+
+        That means that for users on a Windows network setup for roaming profiles, this user data will be synced on
+        login (see `here `_).
+
+        """
+        self.multipath = multipath
+        """An optional parameter which indicates that the entire list of data dirs should be returned.
+
+        By default, the first item would only be returned. Only affects ``site_data_dir`` and ``site_config_dir`` on
+        Unix and macOS.
+
+        """
+        self.opinion = opinion
+        """Whether to use opinionated values.
+
+        When enabled, appends an additional subdirectory for certain directories: e.g. ``Cache`` for cache and ``Logs``
+        for logs on Windows, ``log`` for logs on Unix.
+
+        """
+        self.ensure_exists = ensure_exists
+        """Optionally create the directory (and any missing parents) upon access if it does not exist.
+
+        By default, no directories are created.
+
+        """
+        self.use_site_for_root = use_site_for_root
+        """Whether to redirect ``user_*_dir`` calls to their ``site_*_dir`` equivalents when running as root (uid 0).
+
+        Only has an effect on Unix. Disabled by default for backwards compatibility. When enabled, XDG user environment
+        variables (e.g. ``XDG_DATA_HOME``) are bypassed for the redirected directories.
+
+        """
+
+    def _append_app_name_and_version(self, *base: str) -> str:
+        params = list(base[1:])
+        if self.appname:
+            params.append(self.appname)
+            if self.version:
+                params.append(self.version)
+        path = os.path.join(base[0], *params)  # noqa: PTH118
+        self._optionally_create_directory(path)
+        return path
+
+    def _optionally_create_directory(self, path: str) -> None:
+        if self.ensure_exists:
+            Path(path).mkdir(parents=True, exist_ok=True)
+
+    def _first_item_as_path_if_multipath(self, directory: str) -> Path:
+        if self.multipath:
+            # If multipath is True, the first path is returned.
+            directory = directory.partition(os.pathsep)[0]
+        return Path(directory)
+
+    @property
+    @abstractmethod
+    def user_data_dir(self) -> str:
+        """:returns: data directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def site_data_dir(self) -> str:
+        """:returns: data directory shared by users"""
+
+    @property
+    def _site_data_dirs(self) -> list[str]:
+        raise NotImplementedError
+
+    @property
+    @abstractmethod
+    def user_config_dir(self) -> str:
+        """:returns: config directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def site_config_dir(self) -> str:
+        """:returns: config directory shared by users"""
+
+    @property
+    def _site_config_dirs(self) -> list[str]:
+        raise NotImplementedError
+
+    @property
+    @abstractmethod
+    def user_cache_dir(self) -> str:
+        """:returns: cache directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def site_cache_dir(self) -> str:
+        """:returns: cache directory shared by users"""
+
+    @property
+    @abstractmethod
+    def user_state_dir(self) -> str:
+        """:returns: state directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def site_state_dir(self) -> str:
+        """:returns: state directory shared by users"""
+
+    @property
+    @abstractmethod
+    def user_log_dir(self) -> str:
+        """:returns: log directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def site_log_dir(self) -> str:
+        """:returns: log directory shared by users"""
+
+    @property
+    @abstractmethod
+    def user_documents_dir(self) -> str:
+        """:returns: documents directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_downloads_dir(self) -> str:
+        """:returns: downloads directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_pictures_dir(self) -> str:
+        """:returns: pictures directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_videos_dir(self) -> str:
+        """:returns: videos directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_music_dir(self) -> str:
+        """:returns: music directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_desktop_dir(self) -> str:
+        """:returns: desktop directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_projects_dir(self) -> str:
+        """:returns: projects directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_publicshare_dir(self) -> str:
+        """:returns: public share directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_templates_dir(self) -> str:
+        """:returns: templates directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_fonts_dir(self) -> str:
+        """:returns: fonts directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_preference_dir(self) -> str:
+        """:returns: preference directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_bin_dir(self) -> str:
+        """:returns: bin directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def site_bin_dir(self) -> str:
+        """:returns: bin directory shared by users"""
+
+    @property
+    @abstractmethod
+    def user_applications_dir(self) -> str:
+        """:returns: applications directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def site_applications_dir(self) -> str:
+        """:returns: applications directory shared by users"""
+
+    @property
+    def _site_applications_dirs(self) -> list[str]:
+        raise NotImplementedError
+
+    @property
+    @abstractmethod
+    def user_runtime_dir(self) -> str:
+        """:returns: runtime directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def site_runtime_dir(self) -> str:
+        """:returns: runtime directory shared by users"""
+
+    @property
+    def user_data_path(self) -> Path:
+        """:returns: data path tied to the user"""
+        return Path(self.user_data_dir)
+
+    @property
+    def site_data_path(self) -> Path:
+        """:returns: data path shared by users"""
+        return Path(self.site_data_dir)
+
+    @property
+    def user_config_path(self) -> Path:
+        """:returns: config path tied to the user"""
+        return Path(self.user_config_dir)
+
+    @property
+    def site_config_path(self) -> Path:
+        """:returns: config path shared by users"""
+        return Path(self.site_config_dir)
+
+    @property
+    def user_cache_path(self) -> Path:
+        """:returns: cache path tied to the user"""
+        return Path(self.user_cache_dir)
+
+    @property
+    def site_cache_path(self) -> Path:
+        """:returns: cache path shared by users"""
+        return Path(self.site_cache_dir)
+
+    @property
+    def user_state_path(self) -> Path:
+        """:returns: state path tied to the user"""
+        return Path(self.user_state_dir)
+
+    @property
+    def site_state_path(self) -> Path:
+        """:returns: state path shared by users"""
+        return Path(self.site_state_dir)
+
+    @property
+    def user_log_path(self) -> Path:
+        """:returns: log path tied to the user"""
+        return Path(self.user_log_dir)
+
+    @property
+    def site_log_path(self) -> Path:
+        """:returns: log path shared by users"""
+        return Path(self.site_log_dir)
+
+    @property
+    def user_documents_path(self) -> Path:
+        """:returns: documents path tied to the user"""
+        return Path(self.user_documents_dir)
+
+    @property
+    def user_downloads_path(self) -> Path:
+        """:returns: downloads path tied to the user"""
+        return Path(self.user_downloads_dir)
+
+    @property
+    def user_pictures_path(self) -> Path:
+        """:returns: pictures path tied to the user"""
+        return Path(self.user_pictures_dir)
+
+    @property
+    def user_videos_path(self) -> Path:
+        """:returns: videos path tied to the user"""
+        return Path(self.user_videos_dir)
+
+    @property
+    def user_music_path(self) -> Path:
+        """:returns: music path tied to the user"""
+        return Path(self.user_music_dir)
+
+    @property
+    def user_desktop_path(self) -> Path:
+        """:returns: desktop path tied to the user"""
+        return Path(self.user_desktop_dir)
+
+    @property
+    def user_projects_path(self) -> Path:
+        """:returns: projects path tied to the user"""
+        return Path(self.user_projects_dir)
+
+    @property
+    def user_publicshare_path(self) -> Path:
+        """:returns: public share path tied to the user"""
+        return Path(self.user_publicshare_dir)
+
+    @property
+    def user_templates_path(self) -> Path:
+        """:returns: templates path tied to the user"""
+        return Path(self.user_templates_dir)
+
+    @property
+    def user_fonts_path(self) -> Path:
+        """:returns: fonts path tied to the user"""
+        return Path(self.user_fonts_dir)
+
+    @property
+    def user_preference_path(self) -> Path:
+        """:returns: preference path tied to the user"""
+        return Path(self.user_preference_dir)
+
+    @property
+    def user_bin_path(self) -> Path:
+        """:returns: bin path tied to the user"""
+        return Path(self.user_bin_dir)
+
+    @property
+    def site_bin_path(self) -> Path:
+        """:returns: bin path shared by users"""
+        return Path(self.site_bin_dir)
+
+    @property
+    def user_applications_path(self) -> Path:
+        """:returns: applications path tied to the user"""
+        return Path(self.user_applications_dir)
+
+    @property
+    def site_applications_path(self) -> Path:
+        """:returns: applications path shared by users"""
+        return Path(self.site_applications_dir)
+
+    @property
+    def user_runtime_path(self) -> Path:
+        """:returns: runtime path tied to the user"""
+        return Path(self.user_runtime_dir)
+
+    @property
+    def site_runtime_path(self) -> Path:
+        """:returns: runtime path shared by users"""
+        return Path(self.site_runtime_dir)
+
+    def iter_config_dirs(self) -> Iterator[str]:
+        """:yield: all user and site configuration directories."""
+        yield self.user_config_dir
+        yield self.site_config_dir
+
+    def iter_data_dirs(self) -> Iterator[str]:
+        """:yield: all user and site data directories."""
+        yield self.user_data_dir
+        yield self.site_data_dir
+
+    def iter_cache_dirs(self) -> Iterator[str]:
+        """:yield: all user and site cache directories."""
+        yield self.user_cache_dir
+        yield self.site_cache_dir
+
+    def iter_state_dirs(self) -> Iterator[str]:
+        """:yield: all user and site state directories."""
+        yield self.user_state_dir
+        yield self.site_state_dir
+
+    def iter_log_dirs(self) -> Iterator[str]:
+        """:yield: all user and site log directories."""
+        yield self.user_log_dir
+        yield self.site_log_dir
+
+    def iter_runtime_dirs(self) -> Iterator[str]:
+        """:yield: all user and site runtime directories."""
+        yield self.user_runtime_dir
+        yield self.site_runtime_dir
+
+    def iter_config_paths(self) -> Iterator[Path]:
+        """:yield: all user and site configuration paths."""
+        for path in self.iter_config_dirs():
+            yield Path(path)
+
+    def iter_data_paths(self) -> Iterator[Path]:
+        """:yield: all user and site data paths."""
+        for path in self.iter_data_dirs():
+            yield Path(path)
+
+    def iter_cache_paths(self) -> Iterator[Path]:
+        """:yield: all user and site cache paths."""
+        for path in self.iter_cache_dirs():
+            yield Path(path)
+
+    def iter_state_paths(self) -> Iterator[Path]:
+        """:yield: all user and site state paths."""
+        for path in self.iter_state_dirs():
+            yield Path(path)
+
+    def iter_log_paths(self) -> Iterator[Path]:
+        """:yield: all user and site log paths."""
+        for path in self.iter_log_dirs():
+            yield Path(path)
+
+    def iter_runtime_paths(self) -> Iterator[Path]:
+        """:yield: all user and site runtime paths."""
+        for path in self.iter_runtime_dirs():
+            yield Path(path)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/macos.py b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/macos.py
new file mode 100644
index 0000000..9ebc2a0
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/macos.py
@@ -0,0 +1,223 @@
+"""macOS."""
+
+from __future__ import annotations
+
+import os.path
+import sys
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from collections.abc import Iterator
+
+from ._xdg import XDGMixin
+from .api import PlatformDirsABC
+
+if TYPE_CHECKING:
+    from pathlib import Path
+
+
+class _MacOSDefaults(PlatformDirsABC):  # noqa: PLR0904
+    """Default platform directories for macOS without XDG environment variable overrides.
+
+    Follows the guidance from `Apple's File System Programming Guide
+    `_.
+    The XDG env var handling is in :class:`~platformdirs._xdg.XDGMixin`.
+
+    """
+
+    def _base_user_app_support_dir(self) -> str:
+        return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support"))  # noqa: PTH111
+
+    def _base_site_dirs(self) -> list[str]:
+        is_homebrew = "/opt/python" in sys.prefix
+        homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else ""
+        path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/share")] if is_homebrew else []
+        path_list.append(self._append_app_name_and_version("/Library/Application Support"))
+        return path_list
+
+    @property
+    def user_data_dir(self) -> str:
+        """:returns: data directory tied to the user, e.g. ``~/Library/Application Support/$appname/$version``"""
+        return self._base_user_app_support_dir()
+
+    @property
+    def _site_data_dirs(self) -> list[str]:
+        return self._base_site_dirs()
+
+    @property
+    def site_data_path(self) -> Path:
+        """:returns: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
+        return self._first_item_as_path_if_multipath(self.site_data_dir)
+
+    @property
+    def site_config_path(self) -> Path:
+        """:returns: config path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
+        return self._first_item_as_path_if_multipath(self.site_config_dir)
+
+    @property
+    def user_config_dir(self) -> str:
+        """:returns: config directory tied to the user, same as `user_data_dir`"""
+        return self._base_user_app_support_dir()
+
+    @property
+    def _site_config_dirs(self) -> list[str]:
+        return self._base_site_dirs()
+
+    @property
+    def user_cache_dir(self) -> str:
+        """:returns: cache directory tied to the user, e.g. ``~/Library/Caches/$appname/$version``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches"))  # noqa: PTH111
+
+    @property
+    def site_cache_dir(self) -> str:
+        """:returns: cache directory shared by users, e.g. ``/Library/Caches/$appname/$version``. If we're using a Python binary managed by `Homebrew `_, the directory will be under the Homebrew prefix, e.g. ``$homebrew_prefix/var/cache/$appname/$version``. If `multipath ` is enabled, and we're in Homebrew, the response is a multi-path string separated by ":", e.g. ``$homebrew_prefix/var/cache/$appname/$version:/Library/Caches/$appname/$version``"""
+        is_homebrew = "/opt/python" in sys.prefix
+        homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else ""
+        path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/var/cache")] if is_homebrew else []
+        path_list.append(self._append_app_name_and_version("/Library/Caches"))
+        if self.multipath:
+            return os.pathsep.join(path_list)
+        return path_list[0]
+
+    @property
+    def site_cache_path(self) -> Path:
+        """:returns: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
+        return self._first_item_as_path_if_multipath(self.site_cache_dir)
+
+    @property
+    def user_state_dir(self) -> str:
+        """:returns: state directory tied to the user, same as `user_data_dir`"""
+        return self._base_user_app_support_dir()
+
+    @property
+    def site_state_dir(self) -> str:
+        """:returns: state directory shared by users, same as `site_data_dir`"""
+        return self._base_site_dirs()[0]
+
+    @property
+    def user_log_dir(self) -> str:
+        """:returns: log directory tied to the user, e.g. ``~/Library/Logs/$appname/$version``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/Library/Logs"))  # noqa: PTH111
+
+    @property
+    def site_log_dir(self) -> str:
+        """:returns: log directory shared by users, e.g. ``/Library/Logs/$appname/$version``"""
+        return self._append_app_name_and_version("/Library/Logs")
+
+    @property
+    def user_documents_dir(self) -> str:
+        """:returns: documents directory tied to the user, e.g. ``~/Documents``"""
+        return os.path.expanduser("~/Documents")  # noqa: PTH111
+
+    @property
+    def user_downloads_dir(self) -> str:
+        """:returns: downloads directory tied to the user, e.g. ``~/Downloads``"""
+        return os.path.expanduser("~/Downloads")  # noqa: PTH111
+
+    @property
+    def user_pictures_dir(self) -> str:
+        """:returns: pictures directory tied to the user, e.g. ``~/Pictures``"""
+        return os.path.expanduser("~/Pictures")  # noqa: PTH111
+
+    @property
+    def user_videos_dir(self) -> str:
+        """:returns: videos directory tied to the user, e.g. ``~/Movies``"""
+        return os.path.expanduser("~/Movies")  # noqa: PTH111
+
+    @property
+    def user_music_dir(self) -> str:
+        """:returns: music directory tied to the user, e.g. ``~/Music``"""
+        return os.path.expanduser("~/Music")  # noqa: PTH111
+
+    @property
+    def user_desktop_dir(self) -> str:
+        """:returns: desktop directory tied to the user, e.g. ``~/Desktop``"""
+        return os.path.expanduser("~/Desktop")  # noqa: PTH111
+
+    @property
+    def user_projects_dir(self) -> str:
+        """:returns: projects directory tied to the user, e.g. ``~/Projects``"""
+        return os.path.expanduser("~/Projects")  # noqa: PTH111
+
+    @property
+    def user_publicshare_dir(self) -> str:
+        """:returns: public share directory tied to the user, e.g. ``~/Public``"""
+        return os.path.expanduser("~/Public")  # noqa: PTH111  # API returns str, not Path
+
+    @property
+    def user_templates_dir(self) -> str:
+        """:returns: templates directory tied to the user, e.g. ``~/Templates``"""
+        return os.path.expanduser("~/Templates")  # noqa: PTH111  # API returns str, not Path
+
+    @property
+    def user_fonts_dir(self) -> str:
+        """:returns: fonts directory tied to the user, e.g. ``~/Library/Fonts``"""
+        return os.path.expanduser("~/Library/Fonts")  # noqa: PTH111  # API returns str, not Path
+
+    @property
+    def user_preference_dir(self) -> str:
+        """:returns: preference directory tied to the user, e.g. ``~/Library/Preferences/AppName``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/Library/Preferences"))  # noqa: PTH111  # API returns str, not Path
+
+    @property
+    def user_bin_dir(self) -> str:
+        """:returns: bin directory tied to the user, e.g. ``~/.local/bin``"""
+        return os.path.expanduser("~/.local/bin")  # noqa: PTH111
+
+    @property
+    def site_bin_dir(self) -> str:
+        """:returns: bin directory shared by users, e.g. ``/usr/local/bin``"""
+        return "/usr/local/bin"
+
+    @property
+    def user_applications_dir(self) -> str:
+        """:returns: applications directory tied to the user, e.g. ``~/Applications``"""
+        return os.path.expanduser("~/Applications")  # noqa: PTH111
+
+    @property
+    def _site_applications_dirs(self) -> list[str]:
+        return ["/Applications"]
+
+    @property
+    def site_applications_dir(self) -> str:
+        """:returns: applications directory shared by users, e.g. ``/Applications``"""
+        dirs = self._site_applications_dirs
+        return os.pathsep.join(dirs) if self.multipath else dirs[0]
+
+    @property
+    def user_runtime_dir(self) -> str:
+        """:returns: runtime directory tied to the user, e.g. ``~/Library/Caches/TemporaryItems/$appname/$version``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches/TemporaryItems"))  # noqa: PTH111
+
+    @property
+    def site_runtime_dir(self) -> str:
+        """:returns: runtime directory shared by users, same as `user_runtime_dir`"""
+        return self.user_runtime_dir
+
+    def iter_config_dirs(self) -> Iterator[str]:
+        """:yield: all user and site configuration directories."""
+        yield self.user_config_dir
+        yield from self._site_config_dirs
+
+    def iter_data_dirs(self) -> Iterator[str]:
+        """:yield: all user and site data directories."""
+        yield self.user_data_dir
+        yield from self._site_data_dirs
+
+
+class MacOS(XDGMixin, _MacOSDefaults):
+    """Platform directories for the macOS operating system.
+
+    Follows the guidance from `Apple documentation
+    `_.
+    Makes use of the `appname `, `version
+    `, `ensure_exists `.
+
+    XDG environment variables (e.g. ``$XDG_DATA_HOME``) are supported and take precedence over macOS defaults.
+
+    """
+
+
+__all__ = [
+    "MacOS",
+]
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/unix.py b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/unix.py
new file mode 100644
index 0000000..500d3a4
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/unix.py
@@ -0,0 +1,321 @@
+"""Unix."""
+
+from __future__ import annotations
+
+import os
+import sys
+from configparser import ConfigParser
+from functools import cached_property
+from pathlib import Path
+from tempfile import gettempdir
+from typing import TYPE_CHECKING, NoReturn
+
+from ._xdg import XDGMixin
+from .api import PlatformDirsABC
+
+if TYPE_CHECKING:
+    from collections.abc import Iterator
+
+if sys.platform == "win32":
+
+    def getuid() -> NoReturn:
+        msg = "should only be used on Unix"
+        raise RuntimeError(msg)
+
+else:
+    from os import getuid
+
+
+class _UnixDefaults(PlatformDirsABC):  # noqa: PLR0904
+    """Default directories for Unix/Linux without XDG environment variable overrides.
+
+    The XDG env var handling is in :class:`~platformdirs._xdg.XDGMixin`.
+
+    """
+
+    @cached_property
+    def _use_site(self) -> bool:
+        return self.use_site_for_root and getuid() == 0
+
+    @property
+    def user_data_dir(self) -> str:
+        """:returns: data directory tied to the user, e.g. ``~/.local/share/$appname/$version`` or ``$XDG_DATA_HOME/$appname/$version``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/.local/share"))  # noqa: PTH111
+
+    @property
+    def _site_data_dirs(self) -> list[str]:
+        return [self._append_app_name_and_version("/usr/local/share"), self._append_app_name_and_version("/usr/share")]
+
+    @property
+    def user_config_dir(self) -> str:
+        """:returns: config directory tied to the user, e.g. ``~/.config/$appname/$version`` or ``$XDG_CONFIG_HOME/$appname/$version``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/.config"))  # noqa: PTH111
+
+    @property
+    def _site_config_dirs(self) -> list[str]:
+        return [self._append_app_name_and_version("/etc/xdg")]
+
+    @property
+    def user_cache_dir(self) -> str:
+        """:returns: cache directory tied to the user, e.g. ``~/.cache/$appname/$version`` or ``$XDG_CACHE_HOME/$appname/$version``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/.cache"))  # noqa: PTH111
+
+    @property
+    def site_cache_dir(self) -> str:
+        """:returns: cache directory shared by users, e.g. ``/var/cache/$appname/$version``"""
+        return self._append_app_name_and_version("/var/cache")
+
+    @property
+    def user_state_dir(self) -> str:
+        """:returns: state directory tied to the user, e.g. ``~/.local/state/$appname/$version`` or ``$XDG_STATE_HOME/$appname/$version``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/.local/state"))  # noqa: PTH111
+
+    @property
+    def site_state_dir(self) -> str:
+        """:returns: state directory shared by users, e.g. ``/var/lib/$appname/$version``"""
+        return self._append_app_name_and_version("/var/lib")
+
+    @property
+    def user_log_dir(self) -> str:
+        """:returns: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it"""
+        path = self.user_state_dir
+        if self.opinion:
+            path = os.path.join(path, "log")  # noqa: PTH118
+            self._optionally_create_directory(path)
+        return path
+
+    @property
+    def site_log_dir(self) -> str:
+        """:returns: log directory shared by users, e.g. ``/var/log/$appname/$version``
+
+        Unlike `user_log_dir`, ``opinion`` has no effect since ``/var/log`` is inherently a log directory.
+
+        """
+        return self._append_app_name_and_version("/var/log")
+
+    @property
+    def user_documents_dir(self) -> str:
+        """:returns: documents directory tied to the user, e.g. ``~/Documents``"""
+        return _get_user_media_dir("XDG_DOCUMENTS_DIR", "~/Documents")
+
+    @property
+    def user_downloads_dir(self) -> str:
+        """:returns: downloads directory tied to the user, e.g. ``~/Downloads``"""
+        return _get_user_media_dir("XDG_DOWNLOAD_DIR", "~/Downloads")
+
+    @property
+    def user_pictures_dir(self) -> str:
+        """:returns: pictures directory tied to the user, e.g. ``~/Pictures``"""
+        return _get_user_media_dir("XDG_PICTURES_DIR", "~/Pictures")
+
+    @property
+    def user_videos_dir(self) -> str:
+        """:returns: videos directory tied to the user, e.g. ``~/Videos``"""
+        return _get_user_media_dir("XDG_VIDEOS_DIR", "~/Videos")
+
+    @property
+    def user_music_dir(self) -> str:
+        """:returns: music directory tied to the user, e.g. ``~/Music``"""
+        return _get_user_media_dir("XDG_MUSIC_DIR", "~/Music")
+
+    @property
+    def user_desktop_dir(self) -> str:
+        """:returns: desktop directory tied to the user, e.g. ``~/Desktop``"""
+        return _get_user_media_dir("XDG_DESKTOP_DIR", "~/Desktop")
+
+    @property
+    def user_projects_dir(self) -> str:
+        """:returns: projects directory tied to the user, e.g. ``~/Projects``"""
+        return _get_user_media_dir("XDG_PROJECTS_DIR", "~/Projects")
+
+    @property
+    def user_publicshare_dir(self) -> str:
+        """:returns: public share directory tied to the user, e.g. ``~/Public``"""
+        return _get_user_media_dir("XDG_PUBLICSHARE_DIR", "~/Public")
+
+    @property
+    def user_templates_dir(self) -> str:
+        """:returns: templates directory tied to the user, e.g. ``~/Templates``"""
+        return _get_user_media_dir("XDG_TEMPLATES_DIR", "~/Templates")
+
+    @property
+    def user_fonts_dir(self) -> str:
+        """:returns: fonts directory tied to the user, e.g. ``~/.local/share/fonts``"""
+        return f"{os.path.expanduser('~/.local/share')}/fonts"  # noqa: PTH111  # API returns str, not Path
+
+    @property
+    def user_preference_dir(self) -> str:
+        """:returns: preference directory tied to the user, same as ``user_config_dir``"""
+        return self.user_config_dir
+
+    @property
+    def user_bin_dir(self) -> str:
+        """:returns: bin directory tied to the user, e.g. ``~/.local/bin``"""
+        return os.path.expanduser("~/.local/bin")  # noqa: PTH111
+
+    @property
+    def site_bin_dir(self) -> str:
+        """:returns: bin directory shared by users, e.g. ``/usr/local/bin``"""
+        return "/usr/local/bin"
+
+    @property
+    def user_applications_dir(self) -> str:
+        """:returns: applications directory tied to the user, e.g. ``~/.local/share/applications``"""
+        return os.path.join(os.path.expanduser("~/.local/share"), "applications")  # noqa: PTH111, PTH118
+
+    @property
+    def _site_applications_dirs(self) -> list[str]:
+        return [os.path.join(p, "applications") for p in ["/usr/local/share", "/usr/share"]]  # noqa: PTH118
+
+    @property
+    def site_applications_dir(self) -> str:
+        """:returns: applications directory shared by users, e.g. ``/usr/share/applications``"""
+        dirs = self._site_applications_dirs
+        return os.pathsep.join(dirs) if self.multipath else dirs[0]
+
+    @property
+    def user_runtime_dir(self) -> str:
+        """:returns: runtime directory tied to the user, e.g. ``$XDG_RUNTIME_DIR/$appname/$version``.
+
+        If ``$XDG_RUNTIME_DIR`` is unset, tries the platform default (``/tmp/run/user/$(id -u)`` on OpenBSD, ``/var/run/user/$(id -u)`` on FreeBSD/NetBSD, ``/run/user/$(id -u)`` otherwise). If the default is not writable, falls back to a temporary directory.
+
+        """
+        if sys.platform.startswith("openbsd"):
+            path = f"/tmp/run/user/{getuid()}"  # noqa: S108
+        elif sys.platform.startswith(("freebsd", "netbsd")):
+            path = f"/var/run/user/{getuid()}"
+        else:
+            path = f"/run/user/{getuid()}"
+        if not os.access(path, os.W_OK):
+            path = f"{gettempdir()}/runtime-{getuid()}"
+        return self._append_app_name_and_version(path)
+
+    @property
+    def site_runtime_dir(self) -> str:
+        """:returns: runtime directory shared by users, e.g. ``/run/$appname/$version`` or ``$XDG_RUNTIME_DIR/$appname/$version``.
+
+        Note that this behaves almost exactly like `user_runtime_dir` if ``$XDG_RUNTIME_DIR`` is set, but will fall back to paths associated to the root user instead of a regular logged-in user if it's not set.
+
+        If you wish to ensure that a logged-in root user path is returned e.g. ``/run/user/0``, use `user_runtime_dir` instead.
+
+        For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/$appname/$version`` if ``$XDG_RUNTIME_DIR`` is not set.
+
+        """
+        if sys.platform.startswith(("freebsd", "openbsd", "netbsd")):
+            path = "/var/run"
+        else:
+            path = "/run"
+        return self._append_app_name_and_version(path)
+
+    @property
+    def site_data_path(self) -> Path:
+        """:returns: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
+        return self._first_item_as_path_if_multipath(self.site_data_dir)
+
+    @property
+    def site_config_path(self) -> Path:
+        """:returns: config path shared by users, returns the first item, even if ``multipath`` is set to ``True``"""
+        return self._first_item_as_path_if_multipath(self.site_config_dir)
+
+    @property
+    def site_cache_path(self) -> Path:
+        """:returns: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
+        return self._first_item_as_path_if_multipath(self.site_cache_dir)
+
+    def iter_config_dirs(self) -> Iterator[str]:
+        """:yield: all user and site configuration directories."""
+        if not self._use_site:
+            yield self.user_config_dir
+        yield from self._site_config_dirs
+
+    def iter_data_dirs(self) -> Iterator[str]:
+        """:yield: all user and site data directories."""
+        if not self._use_site:
+            yield self.user_data_dir
+        yield from self._site_data_dirs
+
+
+class Unix(XDGMixin, _UnixDefaults):
+    """On Unix/Linux, we follow the `XDG Basedir Spec `_.
+
+    The spec allows overriding directories with environment variables. The examples shown are the default values,
+    alongside the name of the environment variable that overrides them. Makes use of the `appname
+    `, `version `, `multipath
+    `, `opinion `, `ensure_exists
+    `.
+
+    """
+
+    @property
+    def user_data_dir(self) -> str:
+        """:returns: data directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+        return self.site_data_dir if self._use_site else super().user_data_dir
+
+    @property
+    def user_config_dir(self) -> str:
+        """:returns: config directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+        return self.site_config_dir if self._use_site else super().user_config_dir
+
+    @property
+    def user_cache_dir(self) -> str:
+        """:returns: cache directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+        return self.site_cache_dir if self._use_site else super().user_cache_dir
+
+    @property
+    def user_state_dir(self) -> str:
+        """:returns: state directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+        return self.site_state_dir if self._use_site else super().user_state_dir
+
+    @property
+    def user_log_dir(self) -> str:
+        """:returns: log directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+        return self.site_log_dir if self._use_site else super().user_log_dir
+
+    @property
+    def user_applications_dir(self) -> str:
+        """:returns: applications directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+        return self.site_applications_dir if self._use_site else super().user_applications_dir
+
+    @property
+    def user_runtime_dir(self) -> str:
+        """:returns: runtime directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+        return self.site_runtime_dir if self._use_site else super().user_runtime_dir
+
+    @property
+    def user_bin_dir(self) -> str:
+        """:returns: bin directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
+        return self.site_bin_dir if self._use_site else super().user_bin_dir
+
+
+def _get_user_media_dir(env_var: str, fallback_tilde_path: str) -> str:
+    if media_dir := _get_user_dirs_folder(env_var):
+        return media_dir
+    return os.path.expanduser(fallback_tilde_path)  # noqa: PTH111
+
+
+def _get_user_dirs_folder(key: str) -> str | None:
+    """Return directory from user-dirs.dirs config file.
+
+    See https://freedesktop.org/wiki/Software/xdg-user-dirs/.
+
+    """
+    config_home = os.environ.get("XDG_CONFIG_HOME", "").strip() or os.path.expanduser("~/.config")  # noqa: PTH111
+    user_dirs_config_path = Path(config_home) / "user-dirs.dirs"
+    if user_dirs_config_path.exists():
+        parser = ConfigParser()
+
+        with user_dirs_config_path.open() as stream:
+            parser.read_string(f"[top]\n{stream.read()}")
+
+        if key not in parser["top"]:
+            return None
+
+        path = parser["top"][key].strip('"')
+        return path.replace("$HOME", os.path.expanduser("~"))  # noqa: PTH111
+
+    return None
+
+
+__all__ = [
+    "Unix",
+]
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/version.py b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/version.py
new file mode 100644
index 0000000..a281775
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/version.py
@@ -0,0 +1,24 @@
+# file generated by vcs-versioning
+# don't change, don't track in version control
+from __future__ import annotations
+
+__all__ = [
+    "__version__",
+    "__version_tuple__",
+    "version",
+    "version_tuple",
+    "__commit_id__",
+    "commit_id",
+]
+
+version: str
+__version__: str
+__version_tuple__: tuple[int | str, ...]
+version_tuple: tuple[int | str, ...]
+commit_id: str | None
+__commit_id__: str | None
+
+__version__ = version = '4.10.0'
+__version_tuple__ = version_tuple = (4, 10, 0)
+
+__commit_id__ = commit_id = None
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/windows.py b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/windows.py
new file mode 100644
index 0000000..476ffe7
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/windows.py
@@ -0,0 +1,395 @@
+"""Windows."""
+
+from __future__ import annotations
+
+import os
+import sys
+from pathlib import Path
+from typing import TYPE_CHECKING, Final
+
+from .api import PlatformDirsABC
+
+if TYPE_CHECKING:
+    from collections.abc import Callable
+
+# Not exposed by CPython; defined in the Windows SDK (shlobj_core.h)
+_KF_FLAG_DONT_VERIFY: Final[int] = 0x00004000
+
+
+class Windows(PlatformDirsABC):  # noqa: PLR0904
+    """`MSDN on where to store app data files `_.
+
+    Makes use of the `appname `, `appauthor
+    `, `version `, `roaming
+    `, `opinion `, `ensure_exists
+    `.
+
+    """
+
+    @property
+    def user_data_dir(self) -> str:
+        r""":returns: data directory tied to the user, e.g. ``%USERPROFILE%\AppData\Local\$appauthor\$appname`` (not roaming) or ``%USERPROFILE%\AppData\Roaming\$appauthor\$appname`` (roaming)"""
+        const = "CSIDL_APPDATA" if self.roaming else "CSIDL_LOCAL_APPDATA"
+        path = os.path.normpath(get_win_folder(const))
+        return self._append_parts(path)
+
+    def _append_parts(self, path: str, *, opinion_value: str | None = None) -> str:
+        params = []
+        if self.appname:
+            if self.appauthor is not False:
+                author = self.appauthor or self.appname
+                params.append(author)
+            params.append(self.appname)
+            if opinion_value is not None and self.opinion:
+                params.append(opinion_value)
+            if self.version:
+                params.append(self.version)
+        path = os.path.join(path, *params)  # noqa: PTH118
+        self._optionally_create_directory(path)
+        return path
+
+    @property
+    def site_data_dir(self) -> str:
+        r""":returns: data directory shared by users, e.g. ``C:\ProgramData\$appauthor\$appname``"""
+        path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA"))
+        return self._append_parts(path)
+
+    @property
+    def user_config_dir(self) -> str:
+        """:returns: config directory tied to the user, same as `user_data_dir`"""
+        return self.user_data_dir
+
+    @property
+    def site_config_dir(self) -> str:
+        """:returns: config directory shared by users, same as `site_data_dir`"""
+        return self.site_data_dir
+
+    @property
+    def user_cache_dir(self) -> str:
+        r""":returns: cache directory tied to the user (if opinionated with ``Cache`` folder within ``$appname``) e.g. ``%USERPROFILE%\AppData\Local\$appauthor\$appname\Cache\$version``"""
+        path = os.path.normpath(get_win_folder("CSIDL_LOCAL_APPDATA"))
+        return self._append_parts(path, opinion_value="Cache")
+
+    @property
+    def site_cache_dir(self) -> str:
+        r""":returns: cache directory shared by users, e.g. ``C:\ProgramData\$appauthor\$appname\Cache\$version``"""
+        path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA"))
+        return self._append_parts(path, opinion_value="Cache")
+
+    @property
+    def user_state_dir(self) -> str:
+        """:returns: state directory tied to the user, same as `user_data_dir`"""
+        return self.user_data_dir
+
+    @property
+    def site_state_dir(self) -> str:
+        """:returns: state directory shared by users, same as `site_data_dir`"""
+        return self.site_data_dir
+
+    @property
+    def user_log_dir(self) -> str:
+        """:returns: log directory tied to the user, same as `user_data_dir` if not opinionated else ``Logs`` in it"""
+        path = self.user_data_dir
+        if self.opinion:
+            path = os.path.join(path, "Logs")  # noqa: PTH118
+            self._optionally_create_directory(path)
+        return path
+
+    @property
+    def site_log_dir(self) -> str:
+        """:returns: log directory shared by users, same as `site_data_dir` if not opinionated else ``Logs`` in it"""
+        path = self.site_data_dir
+        if self.opinion:
+            path = os.path.join(path, "Logs")  # noqa: PTH118
+            self._optionally_create_directory(path)
+        return path
+
+    @property
+    def user_documents_dir(self) -> str:
+        r""":returns: documents directory tied to the user e.g. ``%USERPROFILE%\Documents``"""
+        return os.path.normpath(get_win_folder("CSIDL_PERSONAL"))
+
+    @property
+    def user_downloads_dir(self) -> str:
+        r""":returns: downloads directory tied to the user e.g. ``%USERPROFILE%\Downloads``"""
+        return os.path.normpath(get_win_folder("CSIDL_DOWNLOADS"))
+
+    @property
+    def user_pictures_dir(self) -> str:
+        r""":returns: pictures directory tied to the user e.g. ``%USERPROFILE%\Pictures``"""
+        return os.path.normpath(get_win_folder("CSIDL_MYPICTURES"))
+
+    @property
+    def user_videos_dir(self) -> str:
+        r""":returns: videos directory tied to the user e.g. ``%USERPROFILE%\Videos``"""
+        return os.path.normpath(get_win_folder("CSIDL_MYVIDEO"))
+
+    @property
+    def user_music_dir(self) -> str:
+        r""":returns: music directory tied to the user e.g. ``%USERPROFILE%\Music``"""
+        return os.path.normpath(get_win_folder("CSIDL_MYMUSIC"))
+
+    @property
+    def user_desktop_dir(self) -> str:
+        r""":returns: desktop directory tied to the user, e.g. ``%USERPROFILE%\Desktop``"""
+        return os.path.normpath(get_win_folder("CSIDL_DESKTOPDIRECTORY"))
+
+    @property
+    def user_projects_dir(self) -> str:
+        r""":returns: projects directory tied to the user, e.g. ``%USERPROFILE%\Projects``"""
+        return os.path.normpath(os.path.expanduser("~/Projects"))  # noqa: PTH111
+
+    @property
+    def user_publicshare_dir(self) -> str:
+        r""":returns: public share directory e.g. ``C:\Users\Public``"""
+        return os.path.normpath(os.environ.get("PUBLIC", str(Path("~").expanduser().parent / "Public")))
+
+    @property
+    def user_templates_dir(self) -> str:
+        r""":returns: templates directory tied to the user e.g. ``%APPDATA%\Microsoft\Windows\Templates``"""
+        return os.path.normpath(str(Path(get_win_folder("CSIDL_APPDATA")) / "Microsoft" / "Windows" / "Templates"))
+
+    @property
+    def user_fonts_dir(self) -> str:
+        r""":returns: fonts directory tied to the user e.g. ``%LOCALAPPDATA%\Microsoft\Windows\Fonts``"""
+        return os.path.normpath(str(Path(get_win_folder("CSIDL_LOCAL_APPDATA")) / "Microsoft" / "Windows" / "Fonts"))
+
+    @property
+    def user_preference_dir(self) -> str:
+        r""":returns: preference directory tied to the user, same as ``user_config_dir``"""
+        return self.user_config_dir
+
+    @property
+    def user_bin_dir(self) -> str:
+        r""":returns: bin directory tied to the user, e.g. ``%LOCALAPPDATA%\Programs``"""
+        return os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Programs"))  # noqa: PTH118
+
+    @property
+    def site_bin_dir(self) -> str:
+        """:returns: bin directory shared by users, e.g. ``C:\\ProgramData\bin``"""
+        return os.path.normpath(os.path.join(get_win_folder("CSIDL_COMMON_APPDATA"), "bin"))  # noqa: PTH118
+
+    @property
+    def user_applications_dir(self) -> str:
+        r""":returns: applications directory tied to the user, e.g. ``Start Menu\Programs``"""
+        return os.path.normpath(get_win_folder("CSIDL_PROGRAMS"))
+
+    @property
+    def site_applications_dir(self) -> str:
+        r""":returns: applications directory shared by users, e.g. ``C:\ProgramData\Microsoft\Windows\Start Menu\Programs``"""
+        return os.path.normpath(get_win_folder("CSIDL_COMMON_PROGRAMS"))
+
+    @property
+    def user_runtime_dir(self) -> str:
+        r""":returns: runtime directory tied to the user, e.g. ``%USERPROFILE%\AppData\Local\Temp\$appauthor\$appname``"""
+        path = os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Temp"))  # noqa: PTH118
+        return self._append_parts(path)
+
+    @property
+    def site_runtime_dir(self) -> str:
+        """:returns: runtime directory shared by users, same as `user_runtime_dir`"""
+        return self.user_runtime_dir
+
+
+def get_win_folder_from_env_vars(csidl_name: str) -> str:
+    """Get folder from environment variables."""
+    result = get_win_folder_if_csidl_name_not_env_var(csidl_name)
+    if result is not None:
+        return result
+
+    env_var_name = {
+        "CSIDL_APPDATA": "APPDATA",
+        "CSIDL_COMMON_APPDATA": "ALLUSERSPROFILE",
+        "CSIDL_LOCAL_APPDATA": "LOCALAPPDATA",
+    }.get(csidl_name)
+    if env_var_name is None:
+        msg = f"Unknown CSIDL name: {csidl_name}"
+        raise ValueError(msg)
+    result = os.environ.get(env_var_name)
+    if result is None:
+        msg = f"Unset environment variable: {env_var_name}"
+        raise ValueError(msg)
+    return result
+
+
+def get_win_folder_if_csidl_name_not_env_var(csidl_name: str) -> str | None:  # noqa: PLR0911
+    """Get a folder for a CSIDL name that does not exist as an environment variable."""
+    if csidl_name == "CSIDL_PERSONAL":
+        return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Documents")  # noqa: PTH118
+
+    if csidl_name == "CSIDL_DOWNLOADS":
+        return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Downloads")  # noqa: PTH118
+
+    if csidl_name == "CSIDL_MYPICTURES":
+        return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Pictures")  # noqa: PTH118
+
+    if csidl_name == "CSIDL_MYVIDEO":
+        return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Videos")  # noqa: PTH118
+
+    if csidl_name == "CSIDL_MYMUSIC":
+        return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Music")  # noqa: PTH118
+
+    if csidl_name == "CSIDL_PROGRAMS":
+        return os.path.join(  # noqa: PTH118
+            os.path.normpath(os.environ["APPDATA"]),
+            "Microsoft",
+            "Windows",
+            "Start Menu",
+            "Programs",
+        )
+
+    if csidl_name == "CSIDL_COMMON_PROGRAMS":
+        return os.path.join(  # noqa: PTH118
+            os.path.normpath(os.environ.get("PROGRAMDATA", os.environ.get("ALLUSERSPROFILE", "C:\\ProgramData"))),
+            "Microsoft",
+            "Windows",
+            "Start Menu",
+            "Programs",
+        )
+    return None
+
+
+def get_win_folder_from_registry(csidl_name: str) -> str:
+    """Get folder from the registry.
+
+    This is a fallback technique at best. I'm not sure if using the registry for these guarantees us the correct answer
+    for all CSIDL_* names.
+
+    """
+    machine_names = {
+        "CSIDL_COMMON_APPDATA",
+        "CSIDL_COMMON_PROGRAMS",
+    }
+    shell_folder_name = {
+        "CSIDL_APPDATA": "AppData",
+        "CSIDL_COMMON_APPDATA": "Common AppData",
+        "CSIDL_LOCAL_APPDATA": "Local AppData",
+        "CSIDL_PERSONAL": "Personal",
+        "CSIDL_DOWNLOADS": "{374DE290-123F-4565-9164-39C4925E467B}",
+        "CSIDL_MYPICTURES": "My Pictures",
+        "CSIDL_MYVIDEO": "My Video",
+        "CSIDL_MYMUSIC": "My Music",
+        "CSIDL_PROGRAMS": "Programs",
+        "CSIDL_COMMON_PROGRAMS": "Common Programs",
+    }.get(csidl_name)
+    if shell_folder_name is None:
+        msg = f"Unknown CSIDL name: {csidl_name}"
+        raise ValueError(msg)
+    if sys.platform != "win32":  # only needed for mypy type checker to know that this code runs only on Windows
+        raise NotImplementedError
+    import winreg  # noqa: PLC0415
+
+    # Use HKEY_LOCAL_MACHINE for system-wide folders, HKEY_CURRENT_USER for user-specific folders
+    hkey = winreg.HKEY_LOCAL_MACHINE if csidl_name in machine_names else winreg.HKEY_CURRENT_USER
+
+    key = winreg.OpenKey(hkey, r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
+    directory, _ = winreg.QueryValueEx(key, shell_folder_name)
+    return str(directory)
+
+
+_KNOWN_FOLDER_GUIDS: dict[str, str] = {
+    "CSIDL_APPDATA": "{3EB685DB-65F9-4CF6-A03A-E3EF65729F3D}",
+    "CSIDL_COMMON_APPDATA": "{62AB5D82-FDC1-4DC3-A9DD-070D1D495D97}",
+    "CSIDL_LOCAL_APPDATA": "{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}",
+    "CSIDL_PERSONAL": "{FDD39AD0-238F-46AF-ADB4-6C85480369C7}",
+    "CSIDL_MYPICTURES": "{33E28130-4E1E-4676-835A-98395C3BC3BB}",
+    "CSIDL_MYVIDEO": "{18989B1D-99B5-455B-841C-AB7C74E4DDFC}",
+    "CSIDL_MYMUSIC": "{4BD8D571-6D19-48D3-BE97-422220080E43}",
+    "CSIDL_DOWNLOADS": "{374DE290-123F-4565-9164-39C4925E467B}",
+    "CSIDL_DESKTOPDIRECTORY": "{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}",
+    "CSIDL_PROGRAMS": "{A77F5D77-2E2B-44C3-A6A2-ABA601054A51}",
+    "CSIDL_COMMON_PROGRAMS": "{0139D44E-6AFE-49F2-8690-3DAFCAE6FFB8}",
+}
+
+
+def get_win_folder_via_ctypes(csidl_name: str) -> str:
+    """Get folder via :func:`SHGetKnownFolderPath`.
+
+    See https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath.
+
+    """
+    if sys.platform != "win32":  # only needed for type checker to know that this code runs only on Windows
+        raise NotImplementedError
+    from ctypes import HRESULT, POINTER, Structure, WinDLL, byref, create_unicode_buffer, wintypes  # noqa: PLC0415
+
+    class _GUID(Structure):
+        _fields_ = [
+            ("Data1", wintypes.DWORD),
+            ("Data2", wintypes.WORD),
+            ("Data3", wintypes.WORD),
+            ("Data4", wintypes.BYTE * 8),
+        ]
+
+    ole32 = WinDLL("ole32")
+    ole32.CLSIDFromString.restype = HRESULT
+    ole32.CLSIDFromString.argtypes = [wintypes.LPCOLESTR, POINTER(_GUID)]
+    ole32.CoTaskMemFree.restype = None
+    ole32.CoTaskMemFree.argtypes = [wintypes.LPVOID]
+
+    shell32 = WinDLL("shell32")
+    shell32.SHGetKnownFolderPath.restype = HRESULT
+    shell32.SHGetKnownFolderPath.argtypes = [POINTER(_GUID), wintypes.DWORD, wintypes.HANDLE, POINTER(wintypes.LPWSTR)]
+
+    kernel32 = WinDLL("kernel32")
+    kernel32.GetShortPathNameW.restype = wintypes.DWORD
+    kernel32.GetShortPathNameW.argtypes = [wintypes.LPWSTR, wintypes.LPWSTR, wintypes.DWORD]
+
+    folder_guid = _KNOWN_FOLDER_GUIDS.get(csidl_name)
+    if folder_guid is None:
+        msg = f"Unknown CSIDL name: {csidl_name}"
+        raise ValueError(msg)
+
+    guid = _GUID()
+    ole32.CLSIDFromString(folder_guid, byref(guid))
+
+    path_ptr = wintypes.LPWSTR()
+    shell32.SHGetKnownFolderPath(byref(guid), _KF_FLAG_DONT_VERIFY, None, byref(path_ptr))
+    result = path_ptr.value
+    ole32.CoTaskMemFree(path_ptr)
+
+    if result is None:
+        msg = f"SHGetKnownFolderPath returned NULL for {csidl_name}"
+        raise ValueError(msg)
+
+    if any(ord(c) > 255 for c in result):  # noqa: PLR2004
+        buf = create_unicode_buffer(1024)
+        if kernel32.GetShortPathNameW(result, buf, 1024):
+            result = buf.value
+
+    return result
+
+
+def _pick_get_win_folder() -> Callable[[str], str]:
+    """Select the best method to resolve Windows folder paths: ctypes, then registry, then environment variables."""
+    try:
+        import ctypes  # noqa: PLC0415, F401
+    except ImportError:
+        pass
+    else:
+        return get_win_folder_via_ctypes
+    try:
+        import winreg  # noqa: PLC0415, F401
+    except ImportError:
+        return get_win_folder_from_env_vars
+    else:
+        return get_win_folder_from_registry
+
+
+_resolve_win_folder = _pick_get_win_folder()
+
+
+def get_win_folder(csidl_name: str) -> str:
+    """Get a Windows folder path, checking for ``WIN_PD_OVERRIDE_*`` environment variable overrides first.
+
+    For example, ``CSIDL_LOCAL_APPDATA`` can be overridden by setting ``WIN_PD_OVERRIDE_LOCAL_APPDATA``.
+
+    """
+    env_var = f"WIN_PD_OVERRIDE_{csidl_name.removeprefix('CSIDL_')}"
+    if override := os.environ.get(env_var, "").strip():
+        return override
+    return _resolve_win_folder(csidl_name)
+
+
+__all__ = [
+    "Windows",
+]
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/LICENSE
new file mode 100644
index 0000000..446a1a8
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2006-2022 by the respective authors (see AUTHORS file).
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__init__.py
new file mode 100644
index 0000000..974f5bb
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__init__.py
@@ -0,0 +1,82 @@
+"""
+    Pygments
+    ~~~~~~~~
+
+    Pygments is a syntax highlighting package written in Python.
+
+    It is a generic syntax highlighter for general use in all kinds of software
+    such as forum systems, wikis or other applications that need to prettify
+    source code. Highlights are:
+
+    * a wide range of common languages and markup formats is supported
+    * special attention is paid to details, increasing quality by a fair amount
+    * support for new languages and formats are added easily
+    * a number of output formats, presently HTML, LaTeX, RTF, SVG, all image
+      formats that PIL supports, and ANSI sequences
+    * it is usable as a command-line tool and as a library
+    * ... and it highlights even Brainfuck!
+
+    The `Pygments master branch`_ is installable with ``easy_install Pygments==dev``.
+
+    .. _Pygments master branch:
+       https://github.com/pygments/pygments/archive/master.zip#egg=Pygments-dev
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+from io import StringIO, BytesIO
+
+__version__ = '2.20.0'
+__docformat__ = 'restructuredtext'
+
+__all__ = ['lex', 'format', 'highlight']
+
+
+def lex(code, lexer):
+    """
+    Lex `code` with the `lexer` (must be a `Lexer` instance)
+    and return an iterable of tokens. Currently, this only calls
+    `lexer.get_tokens()`.
+    """
+    try:
+        return lexer.get_tokens(code)
+    except TypeError:
+        # Heuristic to catch a common mistake.
+        from pip._vendor.pygments.lexer import RegexLexer
+        if isinstance(lexer, type) and issubclass(lexer, RegexLexer):
+            raise TypeError('lex() argument must be a lexer instance, '
+                            'not a class')
+        raise
+
+
+def format(tokens, formatter, outfile=None):  # pylint: disable=redefined-builtin
+    """
+    Format ``tokens`` (an iterable of tokens) with the formatter ``formatter``
+    (a `Formatter` instance).
+
+    If ``outfile`` is given and a valid file object (an object with a
+    ``write`` method), the result will be written to it, otherwise it
+    is returned as a string.
+    """
+    try:
+        if not outfile:
+            realoutfile = getattr(formatter, 'encoding', None) and BytesIO() or StringIO()
+            formatter.format(tokens, realoutfile)
+            return realoutfile.getvalue()
+        else:
+            formatter.format(tokens, outfile)
+    except TypeError:
+        # Heuristic to catch a common mistake.
+        from pip._vendor.pygments.formatter import Formatter
+        if isinstance(formatter, type) and issubclass(formatter, Formatter):
+            raise TypeError('format() argument must be a formatter instance, '
+                            'not a class')
+        raise
+
+
+def highlight(code, lexer, formatter, outfile=None):
+    """
+    This is the most high-level highlighting function. It combines `lex` and
+    `format` in one function.
+    """
+    return format(lex(code, lexer), formatter, outfile)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__main__.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__main__.py
new file mode 100644
index 0000000..bdafd1a
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__main__.py
@@ -0,0 +1,17 @@
+"""
+    pygments.__main__
+    ~~~~~~~~~~~~~~~~~
+
+    Main entry point for ``python -m pygments``.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import sys
+from pip._vendor.pygments.cmdline import main
+
+try:
+    sys.exit(main(sys.argv))
+except KeyboardInterrupt:
+    sys.exit(1)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..69d29ad
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__init__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__main__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__main__.cpython-312.pyc
new file mode 100644
index 0000000..e7bf351
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__main__.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/console.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/console.cpython-312.pyc
new file mode 100644
index 0000000..6b6c161
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/console.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/filter.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/filter.cpython-312.pyc
new file mode 100644
index 0000000..97ee41c
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/filter.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/formatter.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/formatter.cpython-312.pyc
new file mode 100644
index 0000000..5da73f2
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/formatter.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/lexer.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/lexer.cpython-312.pyc
new file mode 100644
index 0000000..f6766a7
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/lexer.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/modeline.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/modeline.cpython-312.pyc
new file mode 100644
index 0000000..92a5cce
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/modeline.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/plugin.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/plugin.cpython-312.pyc
new file mode 100644
index 0000000..7b19cea
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/plugin.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/regexopt.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/regexopt.cpython-312.pyc
new file mode 100644
index 0000000..2d06ac5
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/regexopt.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/scanner.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/scanner.cpython-312.pyc
new file mode 100644
index 0000000..8a50a7b
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/scanner.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/sphinxext.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/sphinxext.cpython-312.pyc
new file mode 100644
index 0000000..84ef9e7
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/sphinxext.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/style.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/style.cpython-312.pyc
new file mode 100644
index 0000000..d93fd9d
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/style.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/token.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/token.cpython-312.pyc
new file mode 100644
index 0000000..fa311bf
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/token.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/unistring.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/unistring.cpython-312.pyc
new file mode 100644
index 0000000..612468f
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/unistring.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/util.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/util.cpython-312.pyc
new file mode 100644
index 0000000..8424e05
Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/util.cpython-312.pyc differ
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/console.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/console.py
new file mode 100644
index 0000000..d900916
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/console.py
@@ -0,0 +1,70 @@
+"""
+    pygments.console
+    ~~~~~~~~~~~~~~~~
+
+    Format colored console output.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+esc = "\x1b["
+
+codes = {}
+codes[""] = ""
+codes["reset"] = esc + "39;49;00m"
+
+codes["bold"] = esc + "01m"
+codes["faint"] = esc + "02m"
+codes["standout"] = esc + "03m"
+codes["underline"] = esc + "04m"
+codes["blink"] = esc + "05m"
+codes["overline"] = esc + "06m"
+
+dark_colors = ["black", "red", "green", "yellow", "blue",
+               "magenta", "cyan", "gray"]
+light_colors = ["brightblack", "brightred", "brightgreen", "brightyellow", "brightblue",
+                "brightmagenta", "brightcyan", "white"]
+
+x = 30
+for dark, light in zip(dark_colors, light_colors):
+    codes[dark] = esc + "%im" % x
+    codes[light] = esc + "%im" % (60 + x)
+    x += 1
+
+del dark, light, x
+
+codes["white"] = codes["bold"]
+
+
+def reset_color():
+    return codes["reset"]
+
+
+def colorize(color_key, text):
+    return codes[color_key] + text + codes["reset"]
+
+
+def ansiformat(attr, text):
+    """
+    Format ``text`` with a color and/or some attributes::
+
+        color       normal color
+        *color*     bold color
+        _color_     underlined color
+        +color+     blinking color
+    """
+    result = []
+    if attr[:1] == attr[-1:] == '+':
+        result.append(codes['blink'])
+        attr = attr[1:-1]
+    if attr[:1] == attr[-1:] == '*':
+        result.append(codes['bold'])
+        attr = attr[1:-1]
+    if attr[:1] == attr[-1:] == '_':
+        result.append(codes['underline'])
+        attr = attr[1:-1]
+    result.append(codes[attr])
+    result.append(text)
+    result.append(codes['reset'])
+    return ''.join(result)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/filter.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/filter.py
new file mode 100644
index 0000000..6c926c2
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/filter.py
@@ -0,0 +1,70 @@
+"""
+    pygments.filter
+    ~~~~~~~~~~~~~~~
+
+    Module that implements the default filter.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+
+def apply_filters(stream, filters, lexer=None):
+    """
+    Use this method to apply an iterable of filters to
+    a stream. If lexer is given it's forwarded to the
+    filter, otherwise the filter receives `None`.
+    """
+    def _apply(filter_, stream):
+        yield from filter_.filter(lexer, stream)
+    for filter_ in filters:
+        stream = _apply(filter_, stream)
+    return stream
+
+
+def simplefilter(f):
+    """
+    Decorator that converts a function into a filter::
+
+        @simplefilter
+        def lowercase(self, lexer, stream, options):
+            for ttype, value in stream:
+                yield ttype, value.lower()
+    """
+    return type(f.__name__, (FunctionFilter,), {
+        '__module__': getattr(f, '__module__'),
+        '__doc__': f.__doc__,
+        'function': f,
+    })
+
+
+class Filter:
+    """
+    Default filter. Subclass this class or use the `simplefilter`
+    decorator to create own filters.
+    """
+
+    def __init__(self, **options):
+        self.options = options
+
+    def filter(self, lexer, stream):
+        raise NotImplementedError()
+
+
+class FunctionFilter(Filter):
+    """
+    Abstract class used by `simplefilter` to create simple
+    function filters on the fly. The `simplefilter` decorator
+    automatically creates subclasses of this class for
+    functions passed to it.
+    """
+    function = None
+
+    def __init__(self, **options):
+        if not hasattr(self, 'function'):
+            raise TypeError(f'{self.__class__.__name__!r} used without bound function')
+        Filter.__init__(self, **options)
+
+    def filter(self, lexer, stream):
+        # pylint: disable=not-callable
+        yield from self.function(lexer, stream, self.options)
diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__init__.py
new file mode 100644
index 0000000..01e3207
--- /dev/null
+++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__init__.py
@@ -0,0 +1,942 @@
+"""
+    pygments.filters
+    ~~~~~~~~~~~~~~~~
+
+    Module containing filter lookup functions and default
+    filters.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pip._vendor.pygments.token import String, Comment, Keyword, Name, Error, Whitespace, \
+    string_to_tokentype
+from pip._vendor.pygments.filter import Filter
+from pip._vendor.pygments.util import get_list_opt, get_int_opt, get_bool_opt, \
+    get_choice_opt, ClassNotFound, OptionError
+from pip._vendor.pygments.plugin import find_plugin_filters
+
+
+def find_filter_class(filtername):
+    """Lookup a filter by name. Return None if not found."""
+    if filtername in FILTERS:
+        return FILTERS[filtername]
+    for name, cls in find_plugin_filters():
+        if name == filtername:
+            return cls
+    return None
+
+
+def get_filter_by_name(filtername, **options):
+    """Return an instantiated filter.
+
+    Options are passed to the filter initializer if wanted.
+    Raise a ClassNotFound if not found.
+    """
+    cls = find_filter_class(filtername)
+    if cls:
+        return cls(**options)
+    else:
+        raise ClassNotFound(f'filter {filtername!r} not found')
+
+
+def get_all_filters():
+    """Return a generator of all filter names."""
+    yield from FILTERS
+    for name, _ in find_plugin_filters():
+        yield name
+
+
+def _replace_special(ttype, value, regex, specialttype,
+                     replacefunc=lambda x: x):
+    last = 0
+    for match in regex.finditer(value):
+        start, end = match.start(), match.end()
+        if start != last:
+            yield ttype, value[last:start]
+        yield specialttype, replacefunc(value[start:end])
+        last = end
+    if last != len(value):
+        yield ttype, value[last:]
+
+
+class CodeTagFilter(Filter):
+    """Highlight special code tags in comments and docstrings.
+
+    Options accepted:
+
+    `codetags` : list of strings
+       A list of strings that are flagged as code tags.  The default is to
+       highlight ``XXX``, ``TODO``, ``FIXME``, ``BUG`` and ``NOTE``.
+
+    .. versionchanged:: 2.13
+       Now recognizes ``FIXME`` by default.
+    """
+
+    def __init__(self, **options):
+        Filter.__init__(self, **options)
+        tags = get_list_opt(options, 'codetags',
+                            ['XXX', 'TODO', 'FIXME', 'BUG', 'NOTE'])
+        self.tag_re = re.compile(r'\b({})\b'.format('|'.join([
+            re.escape(tag) for tag in tags if tag
+        ])))
+
+    def filter(self, lexer, stream):
+        regex = self.tag_re
+        for ttype, value in stream:
+            if ttype in String.Doc or \
+               ttype in Comment and \
+               ttype not in Comment.Preproc:
+                yield from _replace_special(ttype, value, regex, Comment.Special)
+            else:
+                yield ttype, value
+
+
+class SymbolFilter(Filter):
+    """Convert mathematical symbols into Unicode characters.
+
+    Examples are ``\\`` in Isabelle or
+    ``\\longrightarrow`` in LaTeX.
+
+    This is mostly useful for HTML or console output when you want to
+    approximate the source rendering you'd see in an IDE.
+
+    Options accepted:
+
+    `lang` : string
+       The symbol language. Must be one of ``'isabelle'`` or
+       ``'latex'``.  The default is ``'isabelle'``.
+    """
+
+    latex_symbols = {
+        '\\alpha'                : '\U000003b1',
+        '\\beta'                 : '\U000003b2',
+        '\\gamma'                : '\U000003b3',
+        '\\delta'                : '\U000003b4',
+        '\\varepsilon'           : '\U000003b5',
+        '\\zeta'                 : '\U000003b6',
+        '\\eta'                  : '\U000003b7',
+        '\\vartheta'             : '\U000003b8',
+        '\\iota'                 : '\U000003b9',
+        '\\kappa'                : '\U000003ba',
+        '\\lambda'               : '\U000003bb',
+        '\\mu'                   : '\U000003bc',
+        '\\nu'                   : '\U000003bd',
+        '\\xi'                   : '\U000003be',
+        '\\pi'                   : '\U000003c0',
+        '\\varrho'               : '\U000003c1',
+        '\\sigma'                : '\U000003c3',
+        '\\tau'                  : '\U000003c4',
+        '\\upsilon'              : '\U000003c5',
+        '\\varphi'               : '\U000003c6',
+        '\\chi'                  : '\U000003c7',
+        '\\psi'                  : '\U000003c8',
+        '\\omega'                : '\U000003c9',
+        '\\Gamma'                : '\U00000393',
+        '\\Delta'                : '\U00000394',
+        '\\Theta'                : '\U00000398',
+        '\\Lambda'               : '\U0000039b',
+        '\\Xi'                   : '\U0000039e',
+        '\\Pi'                   : '\U000003a0',
+        '\\Sigma'                : '\U000003a3',
+        '\\Upsilon'              : '\U000003a5',
+        '\\Phi'                  : '\U000003a6',
+        '\\Psi'                  : '\U000003a8',
+        '\\Omega'                : '\U000003a9',
+        '\\leftarrow'            : '\U00002190',
+        '\\longleftarrow'        : '\U000027f5',
+        '\\rightarrow'           : '\U00002192',
+        '\\longrightarrow'       : '\U000027f6',
+        '\\Leftarrow'            : '\U000021d0',
+        '\\Longleftarrow'        : '\U000027f8',
+        '\\Rightarrow'           : '\U000021d2',
+        '\\Longrightarrow'       : '\U000027f9',
+        '\\leftrightarrow'       : '\U00002194',
+        '\\longleftrightarrow'   : '\U000027f7',
+        '\\Leftrightarrow'       : '\U000021d4',
+        '\\Longleftrightarrow'   : '\U000027fa',
+        '\\mapsto'               : '\U000021a6',
+        '\\longmapsto'           : '\U000027fc',
+        '\\relbar'               : '\U00002500',
+        '\\Relbar'               : '\U00002550',
+        '\\hookleftarrow'        : '\U000021a9',
+        '\\hookrightarrow'       : '\U000021aa',
+        '\\leftharpoondown'      : '\U000021bd',
+        '\\rightharpoondown'     : '\U000021c1',
+        '\\leftharpoonup'        : '\U000021bc',
+        '\\rightharpoonup'       : '\U000021c0',
+        '\\rightleftharpoons'    : '\U000021cc',
+        '\\leadsto'              : '\U0000219d',
+        '\\downharpoonleft'      : '\U000021c3',
+        '\\downharpoonright'     : '\U000021c2',
+        '\\upharpoonleft'        : '\U000021bf',
+        '\\upharpoonright'       : '\U000021be',
+        '\\restriction'          : '\U000021be',
+        '\\uparrow'              : '\U00002191',
+        '\\Uparrow'              : '\U000021d1',
+        '\\downarrow'            : '\U00002193',
+        '\\Downarrow'            : '\U000021d3',
+        '\\updownarrow'          : '\U00002195',
+        '\\Updownarrow'          : '\U000021d5',
+        '\\langle'               : '\U000027e8',
+        '\\rangle'               : '\U000027e9',
+        '\\lceil'                : '\U00002308',
+        '\\rceil'                : '\U00002309',
+        '\\lfloor'               : '\U0000230a',
+        '\\rfloor'               : '\U0000230b',
+        '\\flqq'                 : '\U000000ab',
+        '\\frqq'                 : '\U000000bb',
+        '\\bot'                  : '\U000022a5',
+        '\\top'                  : '\U000022a4',
+        '\\wedge'                : '\U00002227',
+        '\\bigwedge'             : '\U000022c0',
+        '\\vee'                  : '\U00002228',
+        '\\bigvee'               : '\U000022c1',
+        '\\forall'               : '\U00002200',
+        '\\exists'               : '\U00002203',
+        '\\nexists'              : '\U00002204',
+        '\\neg'                  : '\U000000ac',
+        '\\Box'                  : '\U000025a1',
+        '\\Diamond'              : '\U000025c7',
+        '\\vdash'                : '\U000022a2',
+        '\\models'               : '\U000022a8',
+        '\\dashv'                : '\U000022a3',
+        '\\surd'                 : '\U0000221a',
+        '\\le'                   : '\U00002264',
+        '\\ge'                   : '\U00002265',
+        '\\ll'                   : '\U0000226a',
+        '\\gg'                   : '\U0000226b',
+        '\\lesssim'              : '\U00002272',
+        '\\gtrsim'               : '\U00002273',
+        '\\lessapprox'           : '\U00002a85',
+        '\\gtrapprox'            : '\U00002a86',
+        '\\in'                   : '\U00002208',
+        '\\notin'                : '\U00002209',
+        '\\subset'               : '\U00002282',
+        '\\supset'               : '\U00002283',
+        '\\subseteq'             : '\U00002286',
+        '\\supseteq'             : '\U00002287',
+        '\\sqsubset'             : '\U0000228f',
+        '\\sqsupset'             : '\U00002290',
+        '\\sqsubseteq'           : '\U00002291',
+        '\\sqsupseteq'           : '\U00002292',
+        '\\cap'                  : '\U00002229',
+        '\\bigcap'               : '\U000022c2',
+        '\\cup'                  : '\U0000222a',
+        '\\bigcup'               : '\U000022c3',
+        '\\sqcup'                : '\U00002294',
+        '\\bigsqcup'             : '\U00002a06',
+        '\\sqcap'                : '\U00002293',
+        '\\Bigsqcap'             : '\U00002a05',
+        '\\setminus'             : '\U00002216',
+        '\\propto'               : '\U0000221d',
+        '\\uplus'                : '\U0000228e',
+        '\\bigplus'              : '\U00002a04',
+        '\\sim'                  : '\U0000223c',
+        '\\doteq'                : '\U00002250',
+        '\\simeq'                : '\U00002243',
+        '\\approx'               : '\U00002248',
+        '\\asymp'                : '\U0000224d',
+        '\\cong'                 : '\U00002245',
+        '\\equiv'                : '\U00002261',
+        '\\Join'                 : '\U000022c8',
+        '\\bowtie'               : '\U00002a1d',
+        '\\prec'                 : '\U0000227a',
+        '\\succ'                 : '\U0000227b',
+        '\\preceq'               : '\U0000227c',
+        '\\succeq'               : '\U0000227d',
+        '\\parallel'             : '\U00002225',
+        '\\mid'                  : '\U000000a6',
+        '\\pm'                   : '\U000000b1',
+        '\\mp'                   : '\U00002213',
+        '\\times'                : '\U000000d7',
+        '\\div'                  : '\U000000f7',
+        '\\cdot'                 : '\U000022c5',
+        '\\star'                 : '\U000022c6',
+        '\\circ'                 : '\U00002218',
+        '\\dagger'               : '\U00002020',
+        '\\ddagger'              : '\U00002021',
+        '\\lhd'                  : '\U000022b2',
+        '\\rhd'                  : '\U000022b3',
+        '\\unlhd'                : '\U000022b4',
+        '\\unrhd'                : '\U000022b5',
+        '\\triangleleft'         : '\U000025c3',
+        '\\triangleright'        : '\U000025b9',
+        '\\triangle'             : '\U000025b3',
+        '\\triangleq'            : '\U0000225c',
+        '\\oplus'                : '\U00002295',
+        '\\bigoplus'             : '\U00002a01',
+        '\\otimes'               : '\U00002297',
+        '\\bigotimes'            : '\U00002a02',
+        '\\odot'                 : '\U00002299',
+        '\\bigodot'              : '\U00002a00',
+        '\\ominus'               : '\U00002296',
+        '\\oslash'               : '\U00002298',
+        '\\dots'                 : '\U00002026',
+        '\\cdots'                : '\U000022ef',
+        '\\sum'                  : '\U00002211',
+        '\\prod'                 : '\U0000220f',
+        '\\coprod'               : '\U00002210',
+        '\\infty'                : '\U0000221e',
+        '\\int'                  : '\U0000222b',
+        '\\oint'                 : '\U0000222e',
+        '\\clubsuit'             : '\U00002663',
+        '\\diamondsuit'          : '\U00002662',
+        '\\heartsuit'            : '\U00002661',
+        '\\spadesuit'            : '\U00002660',
+        '\\aleph'                : '\U00002135',
+        '\\emptyset'             : '\U00002205',
+        '\\nabla'                : '\U00002207',
+        '\\partial'              : '\U00002202',
+        '\\flat'                 : '\U0000266d',
+        '\\natural'              : '\U0000266e',
+        '\\sharp'                : '\U0000266f',
+        '\\angle'                : '\U00002220',
+        '\\copyright'            : '\U000000a9',
+        '\\textregistered'       : '\U000000ae',
+        '\\textonequarter'       : '\U000000bc',
+        '\\textonehalf'          : '\U000000bd',
+        '\\textthreequarters'    : '\U000000be',
+        '\\textordfeminine'      : '\U000000aa',
+        '\\textordmasculine'     : '\U000000ba',
+        '\\euro'                 : '\U000020ac',
+        '\\pounds'               : '\U000000a3',
+        '\\yen'                  : '\U000000a5',
+        '\\textcent'             : '\U000000a2',
+        '\\textcurrency'         : '\U000000a4',
+        '\\textdegree'           : '\U000000b0',
+    }
+
+    isabelle_symbols = {
+        '\\'                 : '\U0001d7ec',
+        '\\'                  : '\U0001d7ed',
+        '\\'                  : '\U0001d7ee',
+        '\\'                : '\U0001d7ef',
+        '\\'                 : '\U0001d7f0',
+        '\\'                 : '\U0001d7f1',
+        '\\'                  : '\U0001d7f2',
+        '\\'                : '\U0001d7f3',
+        '\\'                : '\U0001d7f4',
+        '\\'                 : '\U0001d7f5',
+        '\\'                    : '\U0001d49c',
+        '\\'                    : '\U0000212c',
+        '\\'                    : '\U0001d49e',
+        '\\'                    : '\U0001d49f',
+        '\\'                    : '\U00002130',
+        '\\'                    : '\U00002131',
+        '\\'                    : '\U0001d4a2',
+        '\\'                    : '\U0000210b',
+        '\\'                    : '\U00002110',
+        '\\'                    : '\U0001d4a5',
+        '\\'                    : '\U0001d4a6',
+        '\\'                    : '\U00002112',
+        '\\'                    : '\U00002133',
+        '\\'                    : '\U0001d4a9',
+        '\\'                    : '\U0001d4aa',
+        '\\

' : '\U0001d5c9', + '\\' : '\U0001d5ca', + '\\' : '\U0001d5cb', + '\\' : '\U0001d5cc', + '\\' : '\U0001d5cd', + '\\' : '\U0001d5ce', + '\\' : '\U0001d5cf', + '\\' : '\U0001d5d0', + '\\' : '\U0001d5d1', + '\\' : '\U0001d5d2', + '\\' : '\U0001d5d3', + '\\' : '\U0001d504', + '\\' : '\U0001d505', + '\\' : '\U0000212d', + '\\

' : '\U0001d507', + '\\' : '\U0001d508', + '\\' : '\U0001d509', + '\\' : '\U0001d50a', + '\\' : '\U0000210c', + '\\' : '\U00002111', + '\\' : '\U0001d50d', + '\\' : '\U0001d50e', + '\\' : '\U0001d50f', + '\\' : '\U0001d510', + '\\' : '\U0001d511', + '\\' : '\U0001d512', + '\\' : '\U0001d513', + '\\' : '\U0001d514', + '\\' : '\U0000211c', + '\\' : '\U0001d516', + '\\' : '\U0001d517', + '\\' : '\U0001d518', + '\\' : '\U0001d519', + '\\' : '\U0001d51a', + '\\' : '\U0001d51b', + '\\' : '\U0001d51c', + '\\' : '\U00002128', + '\\' : '\U0001d51e', + '\\' : '\U0001d51f', + '\\' : '\U0001d520', + '\\
' : '\U0001d521', + '\\' : '\U0001d522', + '\\' : '\U0001d523', + '\\' : '\U0001d524', + '\\' : '\U0001d525', + '\\' : '\U0001d526', + '\\' : '\U0001d527', + '\\' : '\U0001d528', + '\\' : '\U0001d529', + '\\' : '\U0001d52a', + '\\' : '\U0001d52b', + '\\' : '\U0001d52c', + '\\' : '\U0001d52d', + '\\' : '\U0001d52e', + '\\' : '\U0001d52f', + '\\' : '\U0001d530', + '\\' : '\U0001d531', + '\\' : '\U0001d532', + '\\' : '\U0001d533', + '\\' : '\U0001d534', + '\\' : '\U0001d535', + '\\' : '\U0001d536', + '\\' : '\U0001d537', + '\\' : '\U000003b1', + '\\' : '\U000003b2', + '\\' : '\U000003b3', + '\\' : '\U000003b4', + '\\' : '\U000003b5', + '\\' : '\U000003b6', + '\\' : '\U000003b7', + '\\' : '\U000003b8', + '\\' : '\U000003b9', + '\\' : '\U000003ba', + '\\' : '\U000003bb', + '\\' : '\U000003bc', + '\\' : '\U000003bd', + '\\' : '\U000003be', + '\\' : '\U000003c0', + '\\' : '\U000003c1', + '\\' : '\U000003c3', + '\\' : '\U000003c4', + '\\' : '\U000003c5', + '\\' : '\U000003c6', + '\\' : '\U000003c7', + '\\' : '\U000003c8', + '\\' : '\U000003c9', + '\\' : '\U00000393', + '\\' : '\U00000394', + '\\' : '\U00000398', + '\\' : '\U0000039b', + '\\' : '\U0000039e', + '\\' : '\U000003a0', + '\\' : '\U000003a3', + '\\' : '\U000003a5', + '\\' : '\U000003a6', + '\\' : '\U000003a8', + '\\' : '\U000003a9', + '\\' : '\U0001d539', + '\\' : '\U00002102', + '\\' : '\U00002115', + '\\' : '\U0000211a', + '\\' : '\U0000211d', + '\\' : '\U00002124', + '\\' : '\U00002190', + '\\' : '\U000027f5', + '\\' : '\U00002192', + '\\' : '\U000027f6', + '\\' : '\U000021d0', + '\\' : '\U000027f8', + '\\' : '\U000021d2', + '\\' : '\U000027f9', + '\\' : '\U00002194', + '\\' : '\U000027f7', + '\\' : '\U000021d4', + '\\' : '\U000027fa', + '\\' : '\U000021a6', + '\\' : '\U000027fc', + '\\' : '\U00002500', + '\\' : '\U00002550', + '\\' : '\U000021a9', + '\\' : '\U000021aa', + '\\' : '\U000021bd', + '\\' : '\U000021c1', + '\\' : '\U000021bc', + '\\' : '\U000021c0', + '\\' : '\U000021cc', + '\\' : '\U0000219d', + '\\' : '\U000021c3', + '\\' : '\U000021c2', + '\\' : '\U000021bf', + '\\' : '\U000021be', + '\\' : '\U000021be', + '\\' : '\U00002237', + '\\' : '\U00002191', + '\\' : '\U000021d1', + '\\' : '\U00002193', + '\\' : '\U000021d3', + '\\' : '\U00002195', + '\\' : '\U000021d5', + '\\' : '\U000027e8', + '\\' : '\U000027e9', + '\\' : '\U00002308', + '\\' : '\U00002309', + '\\' : '\U0000230a', + '\\' : '\U0000230b', + '\\' : '\U00002987', + '\\' : '\U00002988', + '\\' : '\U000027e6', + '\\' : '\U000027e7', + '\\' : '\U00002983', + '\\' : '\U00002984', + '\\' : '\U000000ab', + '\\' : '\U000000bb', + '\\' : '\U000022a5', + '\\' : '\U000022a4', + '\\' : '\U00002227', + '\\' : '\U000022c0', + '\\' : '\U00002228', + '\\' : '\U000022c1', + '\\' : '\U00002200', + '\\' : '\U00002203', + '\\' : '\U00002204', + '\\' : '\U000000ac', + '\\' : '\U000025a1', + '\\' : '\U000025c7', + '\\' : '\U000022a2', + '\\' : '\U000022a8', + '\\' : '\U000022a9', + '\\' : '\U000022ab', + '\\' : '\U000022a3', + '\\' : '\U0000221a', + '\\' : '\U00002264', + '\\' : '\U00002265', + '\\' : '\U0000226a', + '\\' : '\U0000226b', + '\\' : '\U00002272', + '\\' : '\U00002273', + '\\' : '\U00002a85', + '\\' : '\U00002a86', + '\\' : '\U00002208', + '\\' : '\U00002209', + '\\' : '\U00002282', + '\\' : '\U00002283', + '\\' : '\U00002286', + '\\' : '\U00002287', + '\\' : '\U0000228f', + '\\' : '\U00002290', + '\\' : '\U00002291', + '\\' : '\U00002292', + '\\' : '\U00002229', + '\\' : '\U000022c2', + '\\' : '\U0000222a', + '\\' : '\U000022c3', + '\\' : '\U00002294', + '\\' : '\U00002a06', + '\\' : '\U00002293', + '\\' : '\U00002a05', + '\\' : '\U00002216', + '\\' : '\U0000221d', + '\\' : '\U0000228e', + '\\' : '\U00002a04', + '\\' : '\U00002260', + '\\' : '\U0000223c', + '\\' : '\U00002250', + '\\' : '\U00002243', + '\\' : '\U00002248', + '\\' : '\U0000224d', + '\\' : '\U00002245', + '\\' : '\U00002323', + '\\' : '\U00002261', + '\\' : '\U00002322', + '\\' : '\U000022c8', + '\\' : '\U00002a1d', + '\\' : '\U0000227a', + '\\' : '\U0000227b', + '\\' : '\U0000227c', + '\\' : '\U0000227d', + '\\' : '\U00002225', + '\\' : '\U000000a6', + '\\' : '\U000000b1', + '\\' : '\U00002213', + '\\' : '\U000000d7', + '\\
' : '\U000000f7', + '\\' : '\U000022c5', + '\\' : '\U000022c6', + '\\' : '\U00002219', + '\\' : '\U00002218', + '\\' : '\U00002020', + '\\' : '\U00002021', + '\\' : '\U000022b2', + '\\' : '\U000022b3', + '\\' : '\U000022b4', + '\\' : '\U000022b5', + '\\' : '\U000025c3', + '\\' : '\U000025b9', + '\\' : '\U000025b3', + '\\' : '\U0000225c', + '\\' : '\U00002295', + '\\' : '\U00002a01', + '\\' : '\U00002297', + '\\' : '\U00002a02', + '\\' : '\U00002299', + '\\' : '\U00002a00', + '\\' : '\U00002296', + '\\' : '\U00002298', + '\\' : '\U00002026', + '\\' : '\U000022ef', + '\\' : '\U00002211', + '\\' : '\U0000220f', + '\\' : '\U00002210', + '\\' : '\U0000221e', + '\\' : '\U0000222b', + '\\' : '\U0000222e', + '\\' : '\U00002663', + '\\' : '\U00002662', + '\\' : '\U00002661', + '\\' : '\U00002660', + '\\' : '\U00002135', + '\\' : '\U00002205', + '\\' : '\U00002207', + '\\' : '\U00002202', + '\\' : '\U0000266d', + '\\' : '\U0000266e', + '\\' : '\U0000266f', + '\\' : '\U00002220', + '\\' : '\U000000a9', + '\\' : '\U000000ae', + '\\' : '\U000000ad', + '\\' : '\U000000af', + '\\' : '\U000000bc', + '\\' : '\U000000bd', + '\\' : '\U000000be', + '\\' : '\U000000aa', + '\\' : '\U000000ba', + '\\
' : '\U000000a7', + '\\' : '\U000000b6', + '\\' : '\U000000a1', + '\\' : '\U000000bf', + '\\' : '\U000020ac', + '\\' : '\U000000a3', + '\\' : '\U000000a5', + '\\' : '\U000000a2', + '\\' : '\U000000a4', + '\\' : '\U000000b0', + '\\' : '\U00002a3f', + '\\' : '\U00002127', + '\\' : '\U000025ca', + '\\' : '\U00002118', + '\\' : '\U00002240', + '\\' : '\U000022c4', + '\\' : '\U000000b4', + '\\' : '\U00000131', + '\\' : '\U000000a8', + '\\' : '\U000000b8', + '\\' : '\U000002dd', + '\\' : '\U000003f5', + '\\' : '\U000023ce', + '\\' : '\U00002039', + '\\' : '\U0000203a', + '\\' : '\U00002302', + '\\<^sub>' : '\U000021e9', + '\\<^sup>' : '\U000021e7', + '\\<^bold>' : '\U00002759', + '\\<^bsub>' : '\U000021d8', + '\\<^esub>' : '\U000021d9', + '\\<^bsup>' : '\U000021d7', + '\\<^esup>' : '\U000021d6', + } + + lang_map = {'isabelle' : isabelle_symbols, 'latex' : latex_symbols} + + def __init__(self, **options): + Filter.__init__(self, **options) + lang = get_choice_opt(options, 'lang', + ['isabelle', 'latex'], 'isabelle') + self.symbols = self.lang_map[lang] + + def filter(self, lexer, stream): + for ttype, value in stream: + if value in self.symbols: + yield ttype, self.symbols[value] + else: + yield ttype, value + + +class KeywordCaseFilter(Filter): + """Convert keywords to lowercase or uppercase or capitalize them. + + This means first letter uppercase, rest lowercase. + + This can be useful e.g. if you highlight Pascal code and want to adapt the + code to your styleguide. + + Options accepted: + + `case` : string + The casing to convert keywords to. Must be one of ``'lower'``, + ``'upper'`` or ``'capitalize'``. The default is ``'lower'``. + """ + + def __init__(self, **options): + Filter.__init__(self, **options) + case = get_choice_opt(options, 'case', + ['lower', 'upper', 'capitalize'], 'lower') + self.convert = getattr(str, case) + + def filter(self, lexer, stream): + for ttype, value in stream: + if ttype in Keyword: + yield ttype, self.convert(value) + else: + yield ttype, value + + +class NameHighlightFilter(Filter): + """Highlight a normal Name (and Name.*) token with a different token type. + + Example:: + + filter = NameHighlightFilter( + names=['foo', 'bar', 'baz'], + tokentype=Name.Function, + ) + + This would highlight the names "foo", "bar" and "baz" + as functions. `Name.Function` is the default token type. + + Options accepted: + + `names` : list of strings + A list of names that should be given the different token type. + There is no default. + `tokentype` : TokenType or string + A token type or a string containing a token type name that is + used for highlighting the strings in `names`. The default is + `Name.Function`. + """ + + def __init__(self, **options): + Filter.__init__(self, **options) + self.names = set(get_list_opt(options, 'names', [])) + tokentype = options.get('tokentype') + if tokentype: + self.tokentype = string_to_tokentype(tokentype) + else: + self.tokentype = Name.Function + + def filter(self, lexer, stream): + for ttype, value in stream: + if ttype in Name and value in self.names: + yield self.tokentype, value + else: + yield ttype, value + + +class ErrorToken(Exception): + pass + + +class RaiseOnErrorTokenFilter(Filter): + """Raise an exception when the lexer generates an error token. + + Options accepted: + + `excclass` : Exception class + The exception class to raise. + The default is `pygments.filters.ErrorToken`. + + .. versionadded:: 0.8 + """ + + def __init__(self, **options): + Filter.__init__(self, **options) + self.exception = options.get('excclass', ErrorToken) + try: + # issubclass() will raise TypeError if first argument is not a class + if not issubclass(self.exception, Exception): + raise TypeError + except TypeError: + raise OptionError('excclass option is not an exception class') + + def filter(self, lexer, stream): + for ttype, value in stream: + if ttype is Error: + raise self.exception(value) + yield ttype, value + + +class VisibleWhitespaceFilter(Filter): + """Convert tabs, newlines and/or spaces to visible characters. + + Options accepted: + + `spaces` : string or bool + If this is a one-character string, spaces will be replaces by this string. + If it is another true value, spaces will be replaced by ``·`` (unicode + MIDDLE DOT). If it is a false value, spaces will not be replaced. The + default is ``False``. + `tabs` : string or bool + The same as for `spaces`, but the default replacement character is ``»`` + (unicode RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK). The default value + is ``False``. Note: this will not work if the `tabsize` option for the + lexer is nonzero, as tabs will already have been expanded then. + `tabsize` : int + If tabs are to be replaced by this filter (see the `tabs` option), this + is the total number of characters that a tab should be expanded to. + The default is ``8``. + `newlines` : string or bool + The same as for `spaces`, but the default replacement character is ``¶`` + (unicode PILCROW SIGN). The default value is ``False``. + `wstokentype` : bool + If true, give whitespace the special `Whitespace` token type. This allows + styling the visible whitespace differently (e.g. greyed out), but it can + disrupt background colors. The default is ``True``. + + .. versionadded:: 0.8 + """ + + def __init__(self, **options): + Filter.__init__(self, **options) + for name, default in [('spaces', '·'), + ('tabs', '»'), + ('newlines', '¶')]: + opt = options.get(name, False) + if isinstance(opt, str) and len(opt) == 1: + setattr(self, name, opt) + else: + setattr(self, name, (opt and default or '')) + tabsize = get_int_opt(options, 'tabsize', 8) + if self.tabs: + self.tabs += ' ' * (tabsize - 1) + if self.newlines: + self.newlines += '\n' + self.wstt = get_bool_opt(options, 'wstokentype', True) + + def filter(self, lexer, stream): + if self.wstt: + spaces = self.spaces or ' ' + tabs = self.tabs or '\t' + newlines = self.newlines or '\n' + regex = re.compile(r'\s') + + def replacefunc(wschar): + if wschar == ' ': + return spaces + elif wschar == '\t': + return tabs + elif wschar == '\n': + return newlines + return wschar + + for ttype, value in stream: + yield from _replace_special(ttype, value, regex, Whitespace, + replacefunc) + else: + spaces, tabs, newlines = self.spaces, self.tabs, self.newlines + # simpler processing + for ttype, value in stream: + if spaces: + value = value.replace(' ', spaces) + if tabs: + value = value.replace('\t', tabs) + if newlines: + value = value.replace('\n', newlines) + yield ttype, value + + +class GobbleFilter(Filter): + """Gobble source code lines (eats initial characters). + + This filter drops the first ``n`` characters off every line of code. This + may be useful when the source code fed to the lexer is indented by a fixed + amount of space that isn't desired in the output. + + Options accepted: + + `n` : int + The number of characters to gobble. + + .. versionadded:: 1.2 + """ + def __init__(self, **options): + Filter.__init__(self, **options) + self.n = get_int_opt(options, 'n', 0) + + def gobble(self, value, left): + if left < len(value): + return value[left:], 0 + else: + return '', left - len(value) + + def filter(self, lexer, stream): + n = self.n + left = n # How many characters left to gobble. + for ttype, value in stream: + # Remove ``left`` tokens from first line, ``n`` from all others. + parts = value.split('\n') + (parts[0], left) = self.gobble(parts[0], left) + for i in range(1, len(parts)): + (parts[i], left) = self.gobble(parts[i], n) + value = '\n'.join(parts) + + if value != '': + yield ttype, value + + +class TokenMergeFilter(Filter): + """Merge consecutive tokens with the same token type in the output stream. + + .. versionadded:: 1.2 + """ + def __init__(self, **options): + Filter.__init__(self, **options) + + def filter(self, lexer, stream): + current_type = None + current_value = None + for ttype, value in stream: + if ttype is current_type: + current_value += value + else: + if current_type is not None: + yield current_type, current_value + current_type = ttype + current_value = value + if current_type is not None: + yield current_type, current_value + + +FILTERS = { + 'codetagify': CodeTagFilter, + 'keywordcase': KeywordCaseFilter, + 'highlight': NameHighlightFilter, + 'raiseonerror': RaiseOnErrorTokenFilter, + 'whitespace': VisibleWhitespaceFilter, + 'gobble': GobbleFilter, + 'tokenmerge': TokenMergeFilter, + 'symbols': SymbolFilter, +} diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5a7d82c Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatter.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatter.py new file mode 100644 index 0000000..d869b6c --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatter.py @@ -0,0 +1,129 @@ +""" + pygments.formatter + ~~~~~~~~~~~~~~~~~~ + + Base formatter class. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import codecs + +from pip._vendor.pygments.util import get_bool_opt +from pip._vendor.pygments.styles import get_style_by_name + +__all__ = ['Formatter'] + + +def _lookup_style(style): + if isinstance(style, str): + return get_style_by_name(style) + return style + + +class Formatter: + """ + Converts a token stream to text. + + Formatters should have attributes to help selecting them. These + are similar to the corresponding :class:`~pygments.lexer.Lexer` + attributes. + + .. autoattribute:: name + :no-value: + + .. autoattribute:: aliases + :no-value: + + .. autoattribute:: filenames + :no-value: + + You can pass options as keyword arguments to the constructor. + All formatters accept these basic options: + + ``style`` + The style to use, can be a string or a Style subclass + (default: "default"). Not used by e.g. the + TerminalFormatter. + ``full`` + Tells the formatter to output a "full" document, i.e. + a complete self-contained document. This doesn't have + any effect for some formatters (default: false). + ``title`` + If ``full`` is true, the title that should be used to + caption the document (default: ''). + ``encoding`` + If given, must be an encoding name. This will be used to + convert the Unicode token strings to byte strings in the + output. If it is "" or None, Unicode strings will be written + to the output file, which most file-like objects do not + support (default: None). + ``outencoding`` + Overrides ``encoding`` if given. + + """ + + #: Full name for the formatter, in human-readable form. + name = None + + #: A list of short, unique identifiers that can be used to lookup + #: the formatter from a list, e.g. using :func:`.get_formatter_by_name()`. + aliases = [] + + #: A list of fnmatch patterns that match filenames for which this + #: formatter can produce output. The patterns in this list should be unique + #: among all formatters. + filenames = [] + + #: If True, this formatter outputs Unicode strings when no encoding + #: option is given. + unicodeoutput = True + + def __init__(self, **options): + """ + As with lexers, this constructor takes arbitrary optional arguments, + and if you override it, you should first process your own options, then + call the base class implementation. + """ + self.style = _lookup_style(options.get('style', 'default')) + self.full = get_bool_opt(options, 'full', False) + self.title = options.get('title', '') + self.encoding = options.get('encoding', None) or None + if self.encoding in ('guess', 'chardet'): + # can happen for e.g. pygmentize -O encoding=guess + self.encoding = 'utf-8' + self.encoding = options.get('outencoding') or self.encoding + self.options = options + + def get_style_defs(self, arg=''): + """ + This method must return statements or declarations suitable to define + the current style for subsequent highlighted text (e.g. CSS classes + in the `HTMLFormatter`). + + The optional argument `arg` can be used to modify the generation and + is formatter dependent (it is standardized because it can be given on + the command line). + + This method is called by the ``-S`` :doc:`command-line option `, + the `arg` is then given by the ``-a`` option. + """ + return '' + + def format(self, tokensource, outfile): + """ + This method must format the tokens from the `tokensource` iterable and + write the formatted version to the file object `outfile`. + + Formatter options can control how exactly the tokens are converted. + """ + if self.encoding: + # wrap the outfile in a StreamWriter + outfile = codecs.lookup(self.encoding)[3](outfile) + return self.format_unencoded(tokensource, outfile) + + # Allow writing Formatter[str] or Formatter[bytes]. That's equivalent to + # Formatter. This helps when using third-party type stubs from typeshed. + def __class_getitem__(cls, name): + return cls diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__init__.py new file mode 100644 index 0000000..c7c153b --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__init__.py @@ -0,0 +1,157 @@ +""" + pygments.formatters + ~~~~~~~~~~~~~~~~~~~ + + Pygments formatters. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +import sys +import types +import fnmatch +from os.path import basename + +from pip._vendor.pygments.formatters._mapping import FORMATTERS +from pip._vendor.pygments.plugin import find_plugin_formatters +from pip._vendor.pygments.util import ClassNotFound + +__all__ = ['get_formatter_by_name', 'get_formatter_for_filename', + 'get_all_formatters', 'load_formatter_from_file'] + list(FORMATTERS) + +_formatter_cache = {} # classes by name +_pattern_cache = {} + + +def _fn_matches(fn, glob): + """Return whether the supplied file name fn matches pattern filename.""" + if glob not in _pattern_cache: + pattern = _pattern_cache[glob] = re.compile(fnmatch.translate(glob)) + return pattern.match(fn) + return _pattern_cache[glob].match(fn) + + +def _load_formatters(module_name): + """Load a formatter (and all others in the module too).""" + mod = __import__(module_name, None, None, ['__all__']) + for formatter_name in mod.__all__: + cls = getattr(mod, formatter_name) + _formatter_cache[cls.name] = cls + + +def get_all_formatters(): + """Return a generator for all formatter classes.""" + # NB: this returns formatter classes, not info like get_all_lexers(). + for info in FORMATTERS.values(): + if info[1] not in _formatter_cache: + _load_formatters(info[0]) + yield _formatter_cache[info[1]] + for _, formatter in find_plugin_formatters(): + yield formatter + + +def find_formatter_class(alias): + """Lookup a formatter by alias. + + Returns None if not found. + """ + for module_name, name, aliases, _, _ in FORMATTERS.values(): + if alias in aliases: + if name not in _formatter_cache: + _load_formatters(module_name) + return _formatter_cache[name] + for _, cls in find_plugin_formatters(): + if alias in cls.aliases: + return cls + + +def get_formatter_by_name(_alias, **options): + """ + Return an instance of a :class:`.Formatter` subclass that has `alias` in its + aliases list. The formatter is given the `options` at its instantiation. + + Will raise :exc:`pygments.util.ClassNotFound` if no formatter with that + alias is found. + """ + cls = find_formatter_class(_alias) + if cls is None: + raise ClassNotFound(f"no formatter found for name {_alias!r}") + return cls(**options) + + +def load_formatter_from_file(filename, formattername="CustomFormatter", **options): + """ + Return a `Formatter` subclass instance loaded from the provided file, relative + to the current directory. + + The file is expected to contain a Formatter class named ``formattername`` + (by default, CustomFormatter). Users should be very careful with the input, because + this method is equivalent to running ``eval()`` on the input file. The formatter is + given the `options` at its instantiation. + + :exc:`pygments.util.ClassNotFound` is raised if there are any errors loading + the formatter. + + .. versionadded:: 2.2 + """ + try: + # This empty dict will contain the namespace for the exec'd file + custom_namespace = {} + with open(filename, 'rb') as f: + exec(f.read(), custom_namespace) + # Retrieve the class `formattername` from that namespace + if formattername not in custom_namespace: + raise ClassNotFound(f'no valid {formattername} class found in {filename}') + formatter_class = custom_namespace[formattername] + # And finally instantiate it with the options + return formatter_class(**options) + except OSError as err: + raise ClassNotFound(f'cannot read {filename}: {err}') + except ClassNotFound: + raise + except Exception as err: + raise ClassNotFound(f'error when loading custom formatter: {err}') + + +def get_formatter_for_filename(fn, **options): + """ + Return a :class:`.Formatter` subclass instance that has a filename pattern + matching `fn`. The formatter is given the `options` at its instantiation. + + Will raise :exc:`pygments.util.ClassNotFound` if no formatter for that filename + is found. + """ + fn = basename(fn) + for modname, name, _, filenames, _ in FORMATTERS.values(): + for filename in filenames: + if _fn_matches(fn, filename): + if name not in _formatter_cache: + _load_formatters(modname) + return _formatter_cache[name](**options) + for _name, cls in find_plugin_formatters(): + for filename in cls.filenames: + if _fn_matches(fn, filename): + return cls(**options) + raise ClassNotFound(f"no formatter found for file name {fn!r}") + + +class _automodule(types.ModuleType): + """Automatically import formatters.""" + + def __getattr__(self, name): + info = FORMATTERS.get(name) + if info: + _load_formatters(info[0]) + cls = _formatter_cache[info[1]] + setattr(self, name, cls) + return cls + raise AttributeError(name) + + +oldmod = sys.modules[__name__] +newmod = _automodule(__name__) +newmod.__dict__.update(oldmod.__dict__) +sys.modules[__name__] = newmod +del newmod.newmod, newmod.oldmod, newmod.sys, newmod.types diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7babf46 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-312.pyc new file mode 100644 index 0000000..d75b53f Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/_mapping.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/_mapping.py new file mode 100644 index 0000000..72ca840 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/_mapping.py @@ -0,0 +1,23 @@ +# Automatically generated by scripts/gen_mapfiles.py. +# DO NOT EDIT BY HAND; run `tox -e mapfiles` instead. + +FORMATTERS = { + 'BBCodeFormatter': ('pygments.formatters.bbcode', 'BBCode', ('bbcode', 'bb'), (), 'Format tokens with BBcodes. These formatting codes are used by many bulletin boards, so you can highlight your sourcecode with pygments before posting it there.'), + 'BmpImageFormatter': ('pygments.formatters.img', 'img_bmp', ('bmp', 'bitmap'), ('*.bmp',), 'Create a bitmap image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), + 'GifImageFormatter': ('pygments.formatters.img', 'img_gif', ('gif',), ('*.gif',), 'Create a GIF image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), + 'GroffFormatter': ('pygments.formatters.groff', 'groff', ('groff', 'troff', 'roff'), (), 'Format tokens with groff escapes to change their color and font style.'), + 'HtmlFormatter': ('pygments.formatters.html', 'HTML', ('html',), ('*.html', '*.htm'), "Format tokens as HTML 4 ```` tags. By default, the content is enclosed in a ``
`` tag, itself wrapped in a ``
`` tag (but see the `nowrap` option). The ``
``'s CSS class can be set by the `cssclass` option."), + 'IRCFormatter': ('pygments.formatters.irc', 'IRC', ('irc', 'IRC'), (), 'Format tokens with IRC color sequences'), + 'ImageFormatter': ('pygments.formatters.img', 'img', ('img', 'IMG', 'png'), ('*.png',), 'Create a PNG image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), + 'JpgImageFormatter': ('pygments.formatters.img', 'img_jpg', ('jpg', 'jpeg'), ('*.jpg',), 'Create a JPEG image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), + 'LatexFormatter': ('pygments.formatters.latex', 'LaTeX', ('latex', 'tex'), ('*.tex',), 'Format tokens as LaTeX code. This needs the `fancyvrb` and `color` standard packages.'), + 'NullFormatter': ('pygments.formatters.other', 'Text only', ('text', 'null'), ('*.txt',), 'Output the text unchanged without any formatting.'), + 'PangoMarkupFormatter': ('pygments.formatters.pangomarkup', 'Pango Markup', ('pango', 'pangomarkup'), (), 'Format tokens as Pango Markup code. It can then be rendered to an SVG.'), + 'RawTokenFormatter': ('pygments.formatters.other', 'Raw tokens', ('raw', 'tokens'), ('*.raw',), 'Format tokens as a raw representation for storing token streams.'), + 'RtfFormatter': ('pygments.formatters.rtf', 'RTF', ('rtf',), ('*.rtf',), 'Format tokens as RTF markup. This formatter automatically outputs full RTF documents with color information and other useful stuff. Perfect for Copy and Paste into Microsoft(R) Word(R) documents.'), + 'SvgFormatter': ('pygments.formatters.svg', 'SVG', ('svg',), ('*.svg',), 'Format tokens as an SVG graphics file. This formatter is still experimental. Each line of code is a ```` element with explicit ``x`` and ``y`` coordinates containing ```` elements with the individual token styles.'), + 'Terminal256Formatter': ('pygments.formatters.terminal256', 'Terminal256', ('terminal256', 'console256', '256'), (), 'Format tokens with ANSI color sequences, for output in a 256-color terminal or console. Like in `TerminalFormatter` color sequences are terminated at newlines, so that paging the output works correctly.'), + 'TerminalFormatter': ('pygments.formatters.terminal', 'Terminal', ('terminal', 'console'), (), 'Format tokens with ANSI color sequences, for output in a text console. Color sequences are terminated at newlines, so that paging the output works correctly.'), + 'TerminalTrueColorFormatter': ('pygments.formatters.terminal256', 'TerminalTrueColor', ('terminal16m', 'console16m', '16m'), (), 'Format tokens with ANSI color sequences, for output in a true-color terminal or console. Like in `TerminalFormatter` color sequences are terminated at newlines, so that paging the output works correctly.'), + 'TestcaseFormatter': ('pygments.formatters.other', 'Testcase', ('testcase',), (), 'Format tokens as appropriate for a new testcase.'), +} diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexer.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexer.py new file mode 100644 index 0000000..a776bfa --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexer.py @@ -0,0 +1,965 @@ +""" + pygments.lexer + ~~~~~~~~~~~~~~ + + Base lexer classes. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +import sys +import time + +from pip._vendor.pygments.filter import apply_filters, Filter +from pip._vendor.pygments.filters import get_filter_by_name +from pip._vendor.pygments.token import Error, Text, Other, Whitespace, _TokenType +from pip._vendor.pygments.util import get_bool_opt, get_int_opt, get_list_opt, \ + make_analysator, Future, guess_decode +from pip._vendor.pygments.regexopt import regex_opt + +__all__ = ['Lexer', 'RegexLexer', 'ExtendedRegexLexer', 'DelegatingLexer', + 'LexerContext', 'include', 'inherit', 'bygroups', 'using', 'this', + 'default', 'words', 'line_re'] + +line_re = re.compile('.*?\n') + +_encoding_map = [(b'\xef\xbb\xbf', 'utf-8'), + (b'\xff\xfe\0\0', 'utf-32'), + (b'\0\0\xfe\xff', 'utf-32be'), + (b'\xff\xfe', 'utf-16'), + (b'\xfe\xff', 'utf-16be')] + +_default_analyse = staticmethod(lambda x: 0.0) + + +class LexerMeta(type): + """ + This metaclass automagically converts ``analyse_text`` methods into + static methods which always return float values. + """ + + def __new__(mcs, name, bases, d): + if 'analyse_text' in d: + d['analyse_text'] = make_analysator(d['analyse_text']) + return type.__new__(mcs, name, bases, d) + + +class Lexer(metaclass=LexerMeta): + """ + Lexer for a specific language. + + See also :doc:`lexerdevelopment`, a high-level guide to writing + lexers. + + Lexer classes have attributes used for choosing the most appropriate + lexer based on various criteria. + + .. autoattribute:: name + :no-value: + .. autoattribute:: aliases + :no-value: + .. autoattribute:: filenames + :no-value: + .. autoattribute:: alias_filenames + .. autoattribute:: mimetypes + :no-value: + .. autoattribute:: priority + + Lexers included in Pygments should have two additional attributes: + + .. autoattribute:: url + :no-value: + .. autoattribute:: version_added + :no-value: + + Lexers included in Pygments may have additional attributes: + + .. autoattribute:: _example + :no-value: + + You can pass options to the constructor. The basic options recognized + by all lexers and processed by the base `Lexer` class are: + + ``stripnl`` + Strip leading and trailing newlines from the input (default: True). + ``stripall`` + Strip all leading and trailing whitespace from the input + (default: False). + ``ensurenl`` + Make sure that the input ends with a newline (default: True). This + is required for some lexers that consume input linewise. + + .. versionadded:: 1.3 + + ``tabsize`` + If given and greater than 0, expand tabs in the input (default: 0). + ``encoding`` + If given, must be an encoding name. This encoding will be used to + convert the input string to Unicode, if it is not already a Unicode + string (default: ``'guess'``, which uses a simple UTF-8 / Locale / + Latin1 detection. Can also be ``'chardet'`` to use the chardet + library, if it is installed. + ``inencoding`` + Overrides the ``encoding`` if given. + """ + + #: Full name of the lexer, in human-readable form + name = None + + #: A list of short, unique identifiers that can be used to look + #: up the lexer from a list, e.g., using `get_lexer_by_name()`. + aliases = [] + + #: A list of `fnmatch` patterns that match filenames which contain + #: content for this lexer. The patterns in this list should be unique among + #: all lexers. + filenames = [] + + #: A list of `fnmatch` patterns that match filenames which may or may not + #: contain content for this lexer. This list is used by the + #: :func:`.guess_lexer_for_filename()` function, to determine which lexers + #: are then included in guessing the correct one. That means that + #: e.g. every lexer for HTML and a template language should include + #: ``\*.html`` in this list. + alias_filenames = [] + + #: A list of MIME types for content that can be lexed with this lexer. + mimetypes = [] + + #: Priority, should multiple lexers match and no content is provided + priority = 0 + + #: URL of the language specification/definition. Used in the Pygments + #: documentation. Set to an empty string to disable. + url = None + + #: Version of Pygments in which the lexer was added. + version_added = None + + #: Example file name. Relative to the ``tests/examplefiles`` directory. + #: This is used by the documentation generator to show an example. + _example = None + + def __init__(self, **options): + """ + This constructor takes arbitrary options as keyword arguments. + Every subclass must first process its own options and then call + the `Lexer` constructor, since it processes the basic + options like `stripnl`. + + An example looks like this: + + .. sourcecode:: python + + def __init__(self, **options): + self.compress = options.get('compress', '') + Lexer.__init__(self, **options) + + As these options must all be specifiable as strings (due to the + command line usage), there are various utility functions + available to help with that, see `Utilities`_. + """ + self.options = options + self.stripnl = get_bool_opt(options, 'stripnl', True) + self.stripall = get_bool_opt(options, 'stripall', False) + self.ensurenl = get_bool_opt(options, 'ensurenl', True) + self.tabsize = get_int_opt(options, 'tabsize', 0) + self.encoding = options.get('encoding', 'guess') + self.encoding = options.get('inencoding') or self.encoding + self.filters = [] + for filter_ in get_list_opt(options, 'filters', ()): + self.add_filter(filter_) + + def __repr__(self): + if self.options: + return f'' + else: + return f'' + + def add_filter(self, filter_, **options): + """ + Add a new stream filter to this lexer. + """ + if not isinstance(filter_, Filter): + filter_ = get_filter_by_name(filter_, **options) + self.filters.append(filter_) + + def analyse_text(text): + """ + A static method which is called for lexer guessing. + + It should analyse the text and return a float in the range + from ``0.0`` to ``1.0``. If it returns ``0.0``, the lexer + will not be selected as the most probable one, if it returns + ``1.0``, it will be selected immediately. This is used by + `guess_lexer`. + + The `LexerMeta` metaclass automatically wraps this function so + that it works like a static method (no ``self`` or ``cls`` + parameter) and the return value is automatically converted to + `float`. If the return value is an object that is boolean `False` + it's the same as if the return values was ``0.0``. + """ + + def _preprocess_lexer_input(self, text): + """Apply preprocessing such as decoding the input, removing BOM and normalizing newlines.""" + + if not isinstance(text, str): + if self.encoding == 'guess': + text, _ = guess_decode(text) + elif self.encoding == 'chardet': + try: + # pip vendoring note: this code is not reachable by pip, + # removed import of chardet to make it clear. + raise ImportError('chardet is not vendored by pip') + except ImportError as e: + raise ImportError('To enable chardet encoding guessing, ' + 'please install the chardet library ' + 'from http://chardet.feedparser.org/') from e + # check for BOM first + decoded = None + for bom, encoding in _encoding_map: + if text.startswith(bom): + decoded = text[len(bom):].decode(encoding, 'replace') + break + # no BOM found, so use chardet + if decoded is None: + enc = chardet.detect(text[:1024]) # Guess using first 1KB + decoded = text.decode(enc.get('encoding') or 'utf-8', + 'replace') + text = decoded + else: + text = text.decode(self.encoding) + if text.startswith('\ufeff'): + text = text[len('\ufeff'):] + else: + if text.startswith('\ufeff'): + text = text[len('\ufeff'):] + + # text now *is* a unicode string + text = text.replace('\r\n', '\n') + text = text.replace('\r', '\n') + if self.stripall: + text = text.strip() + elif self.stripnl: + text = text.strip('\n') + if self.tabsize > 0: + text = text.expandtabs(self.tabsize) + if self.ensurenl and not text.endswith('\n'): + text += '\n' + + return text + + def get_tokens(self, text, unfiltered=False): + """ + This method is the basic interface of a lexer. It is called by + the `highlight()` function. It must process the text and return an + iterable of ``(tokentype, value)`` pairs from `text`. + + Normally, you don't need to override this method. The default + implementation processes the options recognized by all lexers + (`stripnl`, `stripall` and so on), and then yields all tokens + from `get_tokens_unprocessed()`, with the ``index`` dropped. + + If `unfiltered` is set to `True`, the filtering mechanism is + bypassed even if filters are defined. + """ + text = self._preprocess_lexer_input(text) + + def streamer(): + for _, t, v in self.get_tokens_unprocessed(text): + yield t, v + stream = streamer() + if not unfiltered: + stream = apply_filters(stream, self.filters, self) + return stream + + def get_tokens_unprocessed(self, text): + """ + This method should process the text and return an iterable of + ``(index, tokentype, value)`` tuples where ``index`` is the starting + position of the token within the input text. + + It must be overridden by subclasses. It is recommended to + implement it as a generator to maximize effectiveness. + """ + raise NotImplementedError + + +class DelegatingLexer(Lexer): + """ + This lexer takes two lexer as arguments. A root lexer and + a language lexer. First everything is scanned using the language + lexer, afterwards all ``Other`` tokens are lexed using the root + lexer. + + The lexers from the ``template`` lexer package use this base lexer. + """ + + def __init__(self, _root_lexer, _language_lexer, _needle=Other, **options): + self.root_lexer = _root_lexer(**options) + self.language_lexer = _language_lexer(**options) + self.needle = _needle + Lexer.__init__(self, **options) + + def get_tokens_unprocessed(self, text): + buffered = '' + insertions = [] + lng_buffer = [] + for i, t, v in self.language_lexer.get_tokens_unprocessed(text): + if t is self.needle: + if lng_buffer: + insertions.append((len(buffered), lng_buffer)) + lng_buffer = [] + buffered += v + else: + lng_buffer.append((i, t, v)) + if lng_buffer: + insertions.append((len(buffered), lng_buffer)) + return do_insertions(insertions, + self.root_lexer.get_tokens_unprocessed(buffered)) + + +# ------------------------------------------------------------------------------ +# RegexLexer and ExtendedRegexLexer +# + + +class include(str): # pylint: disable=invalid-name + """ + Indicates that a state should include rules from another state. + """ + pass + + +class _inherit: + """ + Indicates the a state should inherit from its superclass. + """ + def __repr__(self): + return 'inherit' + +inherit = _inherit() # pylint: disable=invalid-name + + +class combined(tuple): # pylint: disable=invalid-name + """ + Indicates a state combined from multiple states. + """ + + def __new__(cls, *args): + return tuple.__new__(cls, args) + + def __init__(self, *args): + # tuple.__init__ doesn't do anything + pass + + +class _PseudoMatch: + """ + A pseudo match object constructed from a string. + """ + + def __init__(self, start, text): + self._text = text + self._start = start + + def start(self, arg=None): + return self._start + + def end(self, arg=None): + return self._start + len(self._text) + + def group(self, arg=None): + if arg: + raise IndexError('No such group') + return self._text + + def groups(self): + return (self._text,) + + def groupdict(self): + return {} + + +def bygroups(*args): + """ + Callback that yields multiple actions for each group in the match. + """ + def callback(lexer, match, ctx=None): + for i, action in enumerate(args): + if action is None: + continue + elif type(action) is _TokenType: + data = match.group(i + 1) + if data: + yield match.start(i + 1), action, data + else: + data = match.group(i + 1) + if data is not None: + if ctx: + ctx.pos = match.start(i + 1) + for item in action(lexer, + _PseudoMatch(match.start(i + 1), data), ctx): + if item: + yield item + if ctx: + ctx.pos = match.end() + return callback + + +class _This: + """ + Special singleton used for indicating the caller class. + Used by ``using``. + """ + +this = _This() + + +def using(_other, **kwargs): + """ + Callback that processes the match with a different lexer. + + The keyword arguments are forwarded to the lexer, except `state` which + is handled separately. + + `state` specifies the state that the new lexer will start in, and can + be an enumerable such as ('root', 'inline', 'string') or a simple + string which is assumed to be on top of the root state. + + Note: For that to work, `_other` must not be an `ExtendedRegexLexer`. + """ + gt_kwargs = {} + if 'state' in kwargs: + s = kwargs.pop('state') + if isinstance(s, (list, tuple)): + gt_kwargs['stack'] = s + else: + gt_kwargs['stack'] = ('root', s) + + if _other is this: + def callback(lexer, match, ctx=None): + # if keyword arguments are given the callback + # function has to create a new lexer instance + if kwargs: + # XXX: cache that somehow + d = dict(lexer.options) + d.update(kwargs) + lx = lexer.__class__(**d) + else: + lx = lexer + s = match.start() + for i, t, v in lx.get_tokens_unprocessed(match.group(), **gt_kwargs): + yield i + s, t, v + if ctx: + ctx.pos = match.end() + else: + def callback(lexer, match, ctx=None): + # XXX: cache that somehow + d = dict(lexer.options) + d.update(kwargs) + lx = _other(**d) + + s = match.start() + for i, t, v in lx.get_tokens_unprocessed(match.group(), **gt_kwargs): + yield i + s, t, v + if ctx: + ctx.pos = match.end() + return callback + + +class default: + """ + Indicates a state or state action (e.g. #pop) to apply. + For example default('#pop') is equivalent to ('', Token, '#pop') + Note that state tuples may be used as well. + + .. versionadded:: 2.0 + """ + def __init__(self, state): + self.state = state + + +class words(Future): + """ + Indicates a list of literal words that is transformed into an optimized + regex that matches any of the words. + + .. versionadded:: 2.0 + """ + def __init__(self, words, prefix='', suffix=''): + self.words = words + self.prefix = prefix + self.suffix = suffix + + def get(self): + return regex_opt(self.words, prefix=self.prefix, suffix=self.suffix) + + +class RegexLexerMeta(LexerMeta): + """ + Metaclass for RegexLexer, creates the self._tokens attribute from + self.tokens on the first instantiation. + """ + + def _process_regex(cls, regex, rflags, state): + """Preprocess the regular expression component of a token definition.""" + if isinstance(regex, Future): + regex = regex.get() + return re.compile(regex, rflags).match + + def _process_token(cls, token): + """Preprocess the token component of a token definition.""" + assert type(token) is _TokenType or callable(token), \ + f'token type must be simple type or callable, not {token!r}' + return token + + def _process_new_state(cls, new_state, unprocessed, processed): + """Preprocess the state transition action of a token definition.""" + if isinstance(new_state, str): + # an existing state + if new_state == '#pop': + return -1 + elif new_state in unprocessed: + return (new_state,) + elif new_state == '#push': + return new_state + elif new_state[:5] == '#pop:': + return -int(new_state[5:]) + else: + assert False, f'unknown new state {new_state!r}' + elif isinstance(new_state, combined): + # combine a new state from existing ones + tmp_state = '_tmp_%d' % cls._tmpname + cls._tmpname += 1 + itokens = [] + for istate in new_state: + assert istate != new_state, f'circular state ref {istate!r}' + itokens.extend(cls._process_state(unprocessed, + processed, istate)) + processed[tmp_state] = itokens + return (tmp_state,) + elif isinstance(new_state, tuple): + # push more than one state + for istate in new_state: + assert (istate in unprocessed or + istate in ('#pop', '#push')), \ + 'unknown new state ' + istate + return new_state + else: + assert False, f'unknown new state def {new_state!r}' + + def _process_state(cls, unprocessed, processed, state): + """Preprocess a single state definition.""" + assert isinstance(state, str), f"wrong state name {state!r}" + assert state[0] != '#', f"invalid state name {state!r}" + if state in processed: + return processed[state] + tokens = processed[state] = [] + rflags = cls.flags + for tdef in unprocessed[state]: + if isinstance(tdef, include): + # it's a state reference + assert tdef != state, f"circular state reference {state!r}" + tokens.extend(cls._process_state(unprocessed, processed, + str(tdef))) + continue + if isinstance(tdef, _inherit): + # should be processed already, but may not in the case of: + # 1. the state has no counterpart in any parent + # 2. the state includes more than one 'inherit' + continue + if isinstance(tdef, default): + new_state = cls._process_new_state(tdef.state, unprocessed, processed) + tokens.append((re.compile('').match, None, new_state)) + continue + + assert type(tdef) is tuple, f"wrong rule def {tdef!r}" + + try: + rex = cls._process_regex(tdef[0], rflags, state) + except Exception as err: + raise ValueError(f"uncompilable regex {tdef[0]!r} in state {state!r} of {cls!r}: {err}") from err + + token = cls._process_token(tdef[1]) + + if len(tdef) == 2: + new_state = None + else: + new_state = cls._process_new_state(tdef[2], + unprocessed, processed) + + tokens.append((rex, token, new_state)) + return tokens + + def process_tokendef(cls, name, tokendefs=None): + """Preprocess a dictionary of token definitions.""" + processed = cls._all_tokens[name] = {} + tokendefs = tokendefs or cls.tokens[name] + for state in list(tokendefs): + cls._process_state(tokendefs, processed, state) + return processed + + def get_tokendefs(cls): + """ + Merge tokens from superclasses in MRO order, returning a single tokendef + dictionary. + + Any state that is not defined by a subclass will be inherited + automatically. States that *are* defined by subclasses will, by + default, override that state in the superclass. If a subclass wishes to + inherit definitions from a superclass, it can use the special value + "inherit", which will cause the superclass' state definition to be + included at that point in the state. + """ + tokens = {} + inheritable = {} + for c in cls.__mro__: + toks = c.__dict__.get('tokens', {}) + + for state, items in toks.items(): + curitems = tokens.get(state) + if curitems is None: + # N.b. because this is assigned by reference, sufficiently + # deep hierarchies are processed incrementally (e.g. for + # A(B), B(C), C(RegexLexer), B will be premodified so X(B) + # will not see any inherits in B). + tokens[state] = items + try: + inherit_ndx = items.index(inherit) + except ValueError: + continue + inheritable[state] = inherit_ndx + continue + + inherit_ndx = inheritable.pop(state, None) + if inherit_ndx is None: + continue + + # Replace the "inherit" value with the items + curitems[inherit_ndx:inherit_ndx+1] = items + try: + # N.b. this is the index in items (that is, the superclass + # copy), so offset required when storing below. + new_inh_ndx = items.index(inherit) + except ValueError: + pass + else: + inheritable[state] = inherit_ndx + new_inh_ndx + + return tokens + + def __call__(cls, *args, **kwds): + """Instantiate cls after preprocessing its token definitions.""" + if '_tokens' not in cls.__dict__: + cls._all_tokens = {} + cls._tmpname = 0 + if hasattr(cls, 'token_variants') and cls.token_variants: + # don't process yet + pass + else: + cls._tokens = cls.process_tokendef('', cls.get_tokendefs()) + + return type.__call__(cls, *args, **kwds) + + +class RegexLexer(Lexer, metaclass=RegexLexerMeta): + """ + Base for simple stateful regular expression-based lexers. + Simplifies the lexing process so that you need only + provide a list of states and regular expressions. + """ + + #: Flags for compiling the regular expressions. + #: Defaults to MULTILINE. + flags = re.MULTILINE + + #: At all time there is a stack of states. Initially, the stack contains + #: a single state 'root'. The top of the stack is called "the current state". + #: + #: Dict of ``{'state': [(regex, tokentype, new_state), ...], ...}`` + #: + #: ``new_state`` can be omitted to signify no state transition. + #: If ``new_state`` is a string, it is pushed on the stack. This ensure + #: the new current state is ``new_state``. + #: If ``new_state`` is a tuple of strings, all of those strings are pushed + #: on the stack and the current state will be the last element of the list. + #: ``new_state`` can also be ``combined('state1', 'state2', ...)`` + #: to signify a new, anonymous state combined from the rules of two + #: or more existing ones. + #: Furthermore, it can be '#pop' to signify going back one step in + #: the state stack, or '#push' to push the current state on the stack + #: again. Note that if you push while in a combined state, the combined + #: state itself is pushed, and not only the state in which the rule is + #: defined. + #: + #: The tuple can also be replaced with ``include('state')``, in which + #: case the rules from the state named by the string are included in the + #: current one. + tokens = {} + + def get_tokens_unprocessed(self, text, stack=('root',)): + """ + Split ``text`` into (tokentype, text) pairs. + + ``stack`` is the initial stack (default: ``['root']``) + """ + pos = 0 + tokendefs = self._tokens + statestack = list(stack) + statetokens = tokendefs[statestack[-1]] + while 1: + for rexmatch, action, new_state in statetokens: + m = rexmatch(text, pos) + if m: + if action is not None: + if type(action) is _TokenType: + yield pos, action, m.group() + else: + yield from action(self, m) + pos = m.end() + if new_state is not None: + # state transition + if isinstance(new_state, tuple): + for state in new_state: + if state == '#pop': + if len(statestack) > 1: + statestack.pop() + elif state == '#push': + statestack.append(statestack[-1]) + else: + statestack.append(state) + elif isinstance(new_state, int): + # pop, but keep at least one state on the stack + # (random code leading to unexpected pops should + # not allow exceptions) + if abs(new_state) >= len(statestack): + del statestack[1:] + else: + del statestack[new_state:] + elif new_state == '#push': + statestack.append(statestack[-1]) + else: + assert False, f"wrong state def: {new_state!r}" + statetokens = tokendefs[statestack[-1]] + break + else: + # We are here only if all state tokens have been considered + # and there was not a match on any of them. + try: + if text[pos] == '\n': + # at EOL, reset state to "root" + statestack = ['root'] + statetokens = tokendefs['root'] + yield pos, Whitespace, '\n' + pos += 1 + continue + yield pos, Error, text[pos] + pos += 1 + except IndexError: + break + + +class LexerContext: + """ + A helper object that holds lexer position data. + """ + + def __init__(self, text, pos, stack=None, end=None): + self.text = text + self.pos = pos + self.end = end or len(text) # end=0 not supported ;-) + self.stack = stack or ['root'] + + def __repr__(self): + return f'LexerContext({self.text!r}, {self.pos!r}, {self.stack!r})' + + +class ExtendedRegexLexer(RegexLexer): + """ + A RegexLexer that uses a context object to store its state. + """ + + def get_tokens_unprocessed(self, text=None, context=None): + """ + Split ``text`` into (tokentype, text) pairs. + If ``context`` is given, use this lexer context instead. + """ + tokendefs = self._tokens + if not context: + ctx = LexerContext(text, 0) + statetokens = tokendefs['root'] + else: + ctx = context + statetokens = tokendefs[ctx.stack[-1]] + text = ctx.text + while 1: + for rexmatch, action, new_state in statetokens: + m = rexmatch(text, ctx.pos, ctx.end) + if m: + if action is not None: + if type(action) is _TokenType: + yield ctx.pos, action, m.group() + ctx.pos = m.end() + else: + yield from action(self, m, ctx) + if not new_state: + # altered the state stack? + statetokens = tokendefs[ctx.stack[-1]] + # CAUTION: callback must set ctx.pos! + if new_state is not None: + # state transition + if isinstance(new_state, tuple): + for state in new_state: + if state == '#pop': + if len(ctx.stack) > 1: + ctx.stack.pop() + elif state == '#push': + ctx.stack.append(ctx.stack[-1]) + else: + ctx.stack.append(state) + elif isinstance(new_state, int): + # see RegexLexer for why this check is made + if abs(new_state) >= len(ctx.stack): + del ctx.stack[1:] + else: + del ctx.stack[new_state:] + elif new_state == '#push': + ctx.stack.append(ctx.stack[-1]) + else: + assert False, f"wrong state def: {new_state!r}" + statetokens = tokendefs[ctx.stack[-1]] + break + else: + try: + if ctx.pos >= ctx.end: + break + if text[ctx.pos] == '\n': + # at EOL, reset state to "root" + ctx.stack = ['root'] + statetokens = tokendefs['root'] + yield ctx.pos, Text, '\n' + ctx.pos += 1 + continue + yield ctx.pos, Error, text[ctx.pos] + ctx.pos += 1 + except IndexError: + break + + +def do_insertions(insertions, tokens): + """ + Helper for lexers which must combine the results of several + sublexers. + + ``insertions`` is a list of ``(index, itokens)`` pairs. + Each ``itokens`` iterable should be inserted at position + ``index`` into the token stream given by the ``tokens`` + argument. + + The result is a combined token stream. + + TODO: clean up the code here. + """ + insertions = iter(insertions) + try: + index, itokens = next(insertions) + except StopIteration: + # no insertions + yield from tokens + return + + realpos = None + insleft = True + + # iterate over the token stream where we want to insert + # the tokens from the insertion list. + for i, t, v in tokens: + # first iteration. store the position of first item + if realpos is None: + realpos = i + oldi = 0 + while insleft and i + len(v) >= index: + tmpval = v[oldi:index - i] + if tmpval: + yield realpos, t, tmpval + realpos += len(tmpval) + for it_index, it_token, it_value in itokens: + yield realpos, it_token, it_value + realpos += len(it_value) + oldi = index - i + try: + index, itokens = next(insertions) + except StopIteration: + insleft = False + break # not strictly necessary + if oldi < len(v): + yield realpos, t, v[oldi:] + realpos += len(v) - oldi + + # leftover tokens + while insleft: + # no normal tokens, set realpos to zero + realpos = realpos or 0 + for p, t, v in itokens: + yield realpos, t, v + realpos += len(v) + try: + index, itokens = next(insertions) + except StopIteration: + insleft = False + break # not strictly necessary + + +class ProfilingRegexLexerMeta(RegexLexerMeta): + """Metaclass for ProfilingRegexLexer, collects regex timing info.""" + + def _process_regex(cls, regex, rflags, state): + if isinstance(regex, words): + rex = regex_opt(regex.words, prefix=regex.prefix, + suffix=regex.suffix) + else: + rex = regex + compiled = re.compile(rex, rflags) + + def match_func(text, pos, endpos=sys.maxsize): + info = cls._prof_data[-1].setdefault((state, rex), [0, 0.0]) + t0 = time.time() + res = compiled.match(text, pos, endpos) + t1 = time.time() + info[0] += 1 + info[1] += t1 - t0 + return res + return match_func + + +class ProfilingRegexLexer(RegexLexer, metaclass=ProfilingRegexLexerMeta): + """Drop-in replacement for RegexLexer that does profiling of its regexes.""" + + _prof_data = [] + _prof_sort_index = 4 # defaults to time per call + + def get_tokens_unprocessed(self, text, stack=('root',)): + # this needs to be a stack, since using(this) will produce nested calls + self.__class__._prof_data.append({}) + yield from RegexLexer.get_tokens_unprocessed(self, text, stack) + rawdata = self.__class__._prof_data.pop() + data = sorted(((s, repr(r).strip('u\'').replace('\\\\', '\\')[:65], + n, 1000 * t, 1000 * t / n) + for ((s, r), (n, t)) in rawdata.items()), + key=lambda x: x[self._prof_sort_index], + reverse=True) + sum_total = sum(x[3] for x in data) + + print() + print('Profiling result for %s lexing %d chars in %.3f ms' % + (self.__class__.__name__, len(text), sum_total)) + print('=' * 110) + print('%-20s %-64s ncalls tottime percall' % ('state', 'regex')) + print('-' * 110) + for d in data: + print('%-20s %-65s %5d %8.4f %8.4f' % d) + print('=' * 110) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__init__.py new file mode 100644 index 0000000..7dcb103 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__init__.py @@ -0,0 +1,362 @@ +""" + pygments.lexers + ~~~~~~~~~~~~~~~ + + Pygments lexers. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +import sys +import types +import fnmatch +from os.path import basename + +from pip._vendor.pygments.lexers._mapping import LEXERS +from pip._vendor.pygments.modeline import get_filetype_from_buffer +from pip._vendor.pygments.plugin import find_plugin_lexers +from pip._vendor.pygments.util import ClassNotFound, guess_decode + +COMPAT = { + 'Python3Lexer': 'PythonLexer', + 'Python3TracebackLexer': 'PythonTracebackLexer', + 'LeanLexer': 'Lean3Lexer', +} + +__all__ = ['get_lexer_by_name', 'get_lexer_for_filename', 'find_lexer_class', + 'guess_lexer', 'load_lexer_from_file'] + list(LEXERS) + list(COMPAT) + +_lexer_cache = {} +_pattern_cache = {} + + +def _fn_matches(fn, glob): + """Return whether the supplied file name fn matches pattern filename.""" + if glob not in _pattern_cache: + pattern = _pattern_cache[glob] = re.compile(fnmatch.translate(glob)) + return pattern.match(fn) + return _pattern_cache[glob].match(fn) + + +def _load_lexers(module_name): + """Load a lexer (and all others in the module too).""" + mod = __import__(module_name, None, None, ['__all__']) + for lexer_name in mod.__all__: + cls = getattr(mod, lexer_name) + _lexer_cache[cls.name] = cls + + +def get_all_lexers(plugins=True): + """Return a generator of tuples in the form ``(name, aliases, + filenames, mimetypes)`` of all know lexers. + + If *plugins* is true (the default), plugin lexers supplied by entrypoints + are also returned. Otherwise, only builtin ones are considered. + """ + for item in LEXERS.values(): + yield item[1:] + if plugins: + for lexer in find_plugin_lexers(): + yield lexer.name, lexer.aliases, lexer.filenames, lexer.mimetypes + + +def find_lexer_class(name): + """ + Return the `Lexer` subclass that with the *name* attribute as given by + the *name* argument. + """ + if name in _lexer_cache: + return _lexer_cache[name] + # lookup builtin lexers + for module_name, lname, aliases, _, _ in LEXERS.values(): + if name == lname: + _load_lexers(module_name) + return _lexer_cache[name] + # continue with lexers from setuptools entrypoints + for cls in find_plugin_lexers(): + if cls.name == name: + return cls + + +def find_lexer_class_by_name(_alias): + """ + Return the `Lexer` subclass that has `alias` in its aliases list, without + instantiating it. + + Like `get_lexer_by_name`, but does not instantiate the class. + + Will raise :exc:`pygments.util.ClassNotFound` if no lexer with that alias is + found. + + .. versionadded:: 2.2 + """ + if not _alias: + raise ClassNotFound(f'no lexer for alias {_alias!r} found') + # lookup builtin lexers + for module_name, name, aliases, _, _ in LEXERS.values(): + if _alias.lower() in aliases: + if name not in _lexer_cache: + _load_lexers(module_name) + return _lexer_cache[name] + # continue with lexers from setuptools entrypoints + for cls in find_plugin_lexers(): + if _alias.lower() in cls.aliases: + return cls + raise ClassNotFound(f'no lexer for alias {_alias!r} found') + + +def get_lexer_by_name(_alias, **options): + """ + Return an instance of a `Lexer` subclass that has `alias` in its + aliases list. The lexer is given the `options` at its + instantiation. + + Will raise :exc:`pygments.util.ClassNotFound` if no lexer with that alias is + found. + """ + if not _alias: + raise ClassNotFound(f'no lexer for alias {_alias!r} found') + + # lookup builtin lexers + for module_name, name, aliases, _, _ in LEXERS.values(): + if _alias.lower() in aliases: + if name not in _lexer_cache: + _load_lexers(module_name) + return _lexer_cache[name](**options) + # continue with lexers from setuptools entrypoints + for cls in find_plugin_lexers(): + if _alias.lower() in cls.aliases: + return cls(**options) + raise ClassNotFound(f'no lexer for alias {_alias!r} found') + + +def load_lexer_from_file(filename, lexername="CustomLexer", **options): + """Load a lexer from a file. + + This method expects a file located relative to the current working + directory, which contains a Lexer class. By default, it expects the + Lexer to be name CustomLexer; you can specify your own class name + as the second argument to this function. + + Users should be very careful with the input, because this method + is equivalent to running eval on the input file. + + Raises ClassNotFound if there are any problems importing the Lexer. + + .. versionadded:: 2.2 + """ + try: + # This empty dict will contain the namespace for the exec'd file + custom_namespace = {} + with open(filename, 'rb') as f: + exec(f.read(), custom_namespace) + # Retrieve the class `lexername` from that namespace + if lexername not in custom_namespace: + raise ClassNotFound(f'no valid {lexername} class found in {filename}') + lexer_class = custom_namespace[lexername] + # And finally instantiate it with the options + return lexer_class(**options) + except OSError as err: + raise ClassNotFound(f'cannot read {filename}: {err}') + except ClassNotFound: + raise + except Exception as err: + raise ClassNotFound(f'error when loading custom lexer: {err}') + + +def find_lexer_class_for_filename(_fn, code=None): + """Get a lexer for a filename. + + If multiple lexers match the filename pattern, use ``analyse_text()`` to + figure out which one is more appropriate. + + Returns None if not found. + """ + matches = [] + fn = basename(_fn) + for modname, name, _, filenames, _ in LEXERS.values(): + for filename in filenames: + if _fn_matches(fn, filename): + if name not in _lexer_cache: + _load_lexers(modname) + matches.append((_lexer_cache[name], filename)) + for cls in find_plugin_lexers(): + for filename in cls.filenames: + if _fn_matches(fn, filename): + matches.append((cls, filename)) + + if isinstance(code, bytes): + # decode it, since all analyse_text functions expect unicode + code = guess_decode(code) + + def get_rating(info): + cls, filename = info + # explicit patterns get a bonus + bonus = '*' not in filename and 0.5 or 0 + # The class _always_ defines analyse_text because it's included in + # the Lexer class. The default implementation returns None which + # gets turned into 0.0. Run scripts/detect_missing_analyse_text.py + # to find lexers which need it overridden. + if code: + return cls.analyse_text(code) + bonus, cls.__name__ + return cls.priority + bonus, cls.__name__ + + if matches: + matches.sort(key=get_rating) + # print "Possible lexers, after sort:", matches + return matches[-1][0] + + +def get_lexer_for_filename(_fn, code=None, **options): + """Get a lexer for a filename. + + Return a `Lexer` subclass instance that has a filename pattern + matching `fn`. The lexer is given the `options` at its + instantiation. + + Raise :exc:`pygments.util.ClassNotFound` if no lexer for that filename + is found. + + If multiple lexers match the filename pattern, use their ``analyse_text()`` + methods to figure out which one is more appropriate. + """ + res = find_lexer_class_for_filename(_fn, code) + if not res: + raise ClassNotFound(f'no lexer for filename {_fn!r} found') + return res(**options) + + +def get_lexer_for_mimetype(_mime, **options): + """ + Return a `Lexer` subclass instance that has `mime` in its mimetype + list. The lexer is given the `options` at its instantiation. + + Will raise :exc:`pygments.util.ClassNotFound` if not lexer for that mimetype + is found. + """ + for modname, name, _, _, mimetypes in LEXERS.values(): + if _mime in mimetypes: + if name not in _lexer_cache: + _load_lexers(modname) + return _lexer_cache[name](**options) + for cls in find_plugin_lexers(): + if _mime in cls.mimetypes: + return cls(**options) + raise ClassNotFound(f'no lexer for mimetype {_mime!r} found') + + +def _iter_lexerclasses(plugins=True): + """Return an iterator over all lexer classes.""" + for key in sorted(LEXERS): + module_name, name = LEXERS[key][:2] + if name not in _lexer_cache: + _load_lexers(module_name) + yield _lexer_cache[name] + if plugins: + yield from find_plugin_lexers() + + +def guess_lexer_for_filename(_fn, _text, **options): + """ + As :func:`guess_lexer()`, but only lexers which have a pattern in `filenames` + or `alias_filenames` that matches `filename` are taken into consideration. + + :exc:`pygments.util.ClassNotFound` is raised if no lexer thinks it can + handle the content. + """ + fn = basename(_fn) + primary = {} + matching_lexers = set() + for lexer in _iter_lexerclasses(): + for filename in lexer.filenames: + if _fn_matches(fn, filename): + matching_lexers.add(lexer) + primary[lexer] = True + for filename in lexer.alias_filenames: + if _fn_matches(fn, filename): + matching_lexers.add(lexer) + primary[lexer] = False + if not matching_lexers: + raise ClassNotFound(f'no lexer for filename {fn!r} found') + if len(matching_lexers) == 1: + return matching_lexers.pop()(**options) + result = [] + for lexer in matching_lexers: + rv = lexer.analyse_text(_text) + if rv == 1.0: + return lexer(**options) + result.append((rv, lexer)) + + def type_sort(t): + # sort by: + # - analyse score + # - is primary filename pattern? + # - priority + # - last resort: class name + return (t[0], primary[t[1]], t[1].priority, t[1].__name__) + result.sort(key=type_sort) + + return result[-1][1](**options) + + +def guess_lexer(_text, **options): + """ + Return a `Lexer` subclass instance that's guessed from the text in + `text`. For that, the :meth:`.analyse_text()` method of every known lexer + class is called with the text as argument, and the lexer which returned the + highest value will be instantiated and returned. + + :exc:`pygments.util.ClassNotFound` is raised if no lexer thinks it can + handle the content. + """ + + if not isinstance(_text, str): + inencoding = options.get('inencoding', options.get('encoding')) + if inencoding: + _text = _text.decode(inencoding or 'utf8') + else: + _text, _ = guess_decode(_text) + + # try to get a vim modeline first + ft = get_filetype_from_buffer(_text) + + if ft is not None: + try: + return get_lexer_by_name(ft, **options) + except ClassNotFound: + pass + + best_lexer = [0.0, None] + for lexer in _iter_lexerclasses(): + rv = lexer.analyse_text(_text) + if rv == 1.0: + return lexer(**options) + if rv > best_lexer[0]: + best_lexer[:] = (rv, lexer) + if not best_lexer[0] or best_lexer[1] is None: + raise ClassNotFound('no lexer matching the text found') + return best_lexer[1](**options) + + +class _automodule(types.ModuleType): + """Automatically import lexers.""" + + def __getattr__(self, name): + info = LEXERS.get(name) + if info: + _load_lexers(info[0]) + cls = _lexer_cache[info[1]] + setattr(self, name, cls) + return cls + if name in COMPAT: + return getattr(self, COMPAT[name]) + raise AttributeError(name) + + +oldmod = sys.modules[__name__] +newmod = _automodule(__name__) +newmod.__dict__.update(oldmod.__dict__) +sys.modules[__name__] = newmod +del newmod.newmod, newmod.oldmod, newmod.sys, newmod.types diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..9ad7397 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/_mapping.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/_mapping.cpython-312.pyc new file mode 100644 index 0000000..a30472b Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/_mapping.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/python.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/python.cpython-312.pyc new file mode 100644 index 0000000..c006dc0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/python.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/_mapping.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/_mapping.py new file mode 100644 index 0000000..3e91bf5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/_mapping.py @@ -0,0 +1,603 @@ +# Automatically generated by scripts/gen_mapfiles.py. +# DO NOT EDIT BY HAND; run `tox -e mapfiles` instead. + +LEXERS = { + 'ABAPLexer': ('pip._vendor.pygments.lexers.business', 'ABAP', ('abap',), ('*.abap', '*.ABAP'), ('text/x-abap',)), + 'AMDGPULexer': ('pip._vendor.pygments.lexers.amdgpu', 'AMDGPU', ('amdgpu',), ('*.isa',), ()), + 'APLLexer': ('pip._vendor.pygments.lexers.apl', 'APL', ('apl',), ('*.apl', '*.aplf', '*.aplo', '*.apln', '*.aplc', '*.apli', '*.dyalog'), ()), + 'AbnfLexer': ('pip._vendor.pygments.lexers.grammar_notation', 'ABNF', ('abnf',), ('*.abnf',), ('text/x-abnf',)), + 'ActionScript3Lexer': ('pip._vendor.pygments.lexers.actionscript', 'ActionScript 3', ('actionscript3', 'as3'), ('*.as',), ('application/x-actionscript3', 'text/x-actionscript3', 'text/actionscript3')), + 'ActionScriptLexer': ('pip._vendor.pygments.lexers.actionscript', 'ActionScript', ('actionscript', 'as'), ('*.as',), ('application/x-actionscript', 'text/x-actionscript', 'text/actionscript')), + 'AdaLexer': ('pip._vendor.pygments.lexers.ada', 'Ada', ('ada', 'ada95', 'ada2005'), ('*.adb', '*.ads', '*.ada'), ('text/x-ada',)), + 'AdlLexer': ('pip._vendor.pygments.lexers.archetype', 'ADL', ('adl',), ('*.adl', '*.adls', '*.adlf', '*.adlx'), ()), + 'AgdaLexer': ('pip._vendor.pygments.lexers.haskell', 'Agda', ('agda',), ('*.agda',), ('text/x-agda',)), + 'AheuiLexer': ('pip._vendor.pygments.lexers.esoteric', 'Aheui', ('aheui',), ('*.aheui',), ()), + 'AlloyLexer': ('pip._vendor.pygments.lexers.dsls', 'Alloy', ('alloy',), ('*.als',), ('text/x-alloy',)), + 'AmbientTalkLexer': ('pip._vendor.pygments.lexers.ambient', 'AmbientTalk', ('ambienttalk', 'ambienttalk/2', 'at'), ('*.at',), ('text/x-ambienttalk',)), + 'AmplLexer': ('pip._vendor.pygments.lexers.ampl', 'Ampl', ('ampl',), ('*.run',), ()), + 'Angular2HtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML + Angular2', ('html+ng2',), ('*.ng2',), ()), + 'Angular2Lexer': ('pip._vendor.pygments.lexers.templates', 'Angular2', ('ng2',), (), ()), + 'AntlrActionScriptLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With ActionScript Target', ('antlr-actionscript', 'antlr-as'), ('*.G', '*.g'), ()), + 'AntlrCSharpLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With C# Target', ('antlr-csharp', 'antlr-c#'), ('*.G', '*.g'), ()), + 'AntlrCppLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With CPP Target', ('antlr-cpp',), ('*.G', '*.g'), ()), + 'AntlrJavaLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With Java Target', ('antlr-java',), ('*.G', '*.g'), ()), + 'AntlrLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR', ('antlr',), (), ()), + 'AntlrObjectiveCLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With ObjectiveC Target', ('antlr-objc',), ('*.G', '*.g'), ()), + 'AntlrPerlLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With Perl Target', ('antlr-perl',), ('*.G', '*.g'), ()), + 'AntlrPythonLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With Python Target', ('antlr-python',), ('*.G', '*.g'), ()), + 'AntlrRubyLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With Ruby Target', ('antlr-ruby', 'antlr-rb'), ('*.G', '*.g'), ()), + 'ApacheConfLexer': ('pip._vendor.pygments.lexers.configs', 'ApacheConf', ('apacheconf', 'aconf', 'apache'), ('.htaccess', 'apache.conf', 'apache2.conf'), ('text/x-apacheconf',)), + 'AppleScriptLexer': ('pip._vendor.pygments.lexers.scripting', 'AppleScript', ('applescript',), ('*.applescript',), ()), + 'ArduinoLexer': ('pip._vendor.pygments.lexers.c_like', 'Arduino', ('arduino',), ('*.ino',), ('text/x-arduino',)), + 'ArrowLexer': ('pip._vendor.pygments.lexers.arrow', 'Arrow', ('arrow',), ('*.arw',), ()), + 'ArturoLexer': ('pip._vendor.pygments.lexers.arturo', 'Arturo', ('arturo', 'art'), ('*.art',), ()), + 'AscLexer': ('pip._vendor.pygments.lexers.asc', 'ASCII armored', ('asc', 'pem'), ('*.asc', '*.pem', 'id_dsa', 'id_ecdsa', 'id_ecdsa_sk', 'id_ed25519', 'id_ed25519_sk', 'id_rsa'), ('application/pgp-keys', 'application/pgp-encrypted', 'application/pgp-signature', 'application/pem-certificate-chain')), + 'Asn1Lexer': ('pip._vendor.pygments.lexers.asn1', 'ASN.1', ('asn1',), ('*.asn1',), ()), + 'AspectJLexer': ('pip._vendor.pygments.lexers.jvm', 'AspectJ', ('aspectj',), ('*.aj',), ('text/x-aspectj',)), + 'AsymptoteLexer': ('pip._vendor.pygments.lexers.graphics', 'Asymptote', ('asymptote', 'asy'), ('*.asy',), ('text/x-asymptote',)), + 'AugeasLexer': ('pip._vendor.pygments.lexers.configs', 'Augeas', ('augeas',), ('*.aug',), ()), + 'AutoItLexer': ('pip._vendor.pygments.lexers.automation', 'AutoIt', ('autoit',), ('*.au3',), ('text/x-autoit',)), + 'AutohotkeyLexer': ('pip._vendor.pygments.lexers.automation', 'autohotkey', ('autohotkey', 'ahk'), ('*.ahk', '*.ahkl'), ('text/x-autohotkey',)), + 'AwkLexer': ('pip._vendor.pygments.lexers.textedit', 'Awk', ('awk', 'gawk', 'mawk', 'nawk'), ('*.awk',), ('application/x-awk',)), + 'BBCBasicLexer': ('pip._vendor.pygments.lexers.basic', 'BBC Basic', ('bbcbasic',), ('*.bbc',), ()), + 'BBCodeLexer': ('pip._vendor.pygments.lexers.markup', 'BBCode', ('bbcode',), (), ('text/x-bbcode',)), + 'BCLexer': ('pip._vendor.pygments.lexers.algebra', 'BC', ('bc',), ('*.bc',), ()), + 'BQNLexer': ('pip._vendor.pygments.lexers.bqn', 'BQN', ('bqn',), ('*.bqn',), ()), + 'BSTLexer': ('pip._vendor.pygments.lexers.bibtex', 'BST', ('bst', 'bst-pybtex'), ('*.bst',), ()), + 'BareLexer': ('pip._vendor.pygments.lexers.bare', 'BARE', ('bare',), ('*.bare',), ()), + 'BaseMakefileLexer': ('pip._vendor.pygments.lexers.make', 'Base Makefile', ('basemake',), (), ()), + 'BashLexer': ('pip._vendor.pygments.lexers.shell', 'Bash', ('bash', 'sh', 'ksh', 'zsh', 'shell', 'openrc'), ('*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass', '*.exheres-0', '*.exlib', '*.zsh', '.bashrc', 'bashrc', '.bash_*', 'bash_*', 'zshrc', '.zshrc', '.kshrc', 'kshrc', 'PKGBUILD'), ('application/x-sh', 'application/x-shellscript', 'text/x-shellscript')), + 'BashSessionLexer': ('pip._vendor.pygments.lexers.shell', 'Bash Session', ('console', 'shell-session'), ('*.sh-session', '*.shell-session'), ('application/x-shell-session', 'application/x-sh-session')), + 'BatchLexer': ('pip._vendor.pygments.lexers.shell', 'Batchfile', ('batch', 'bat', 'dosbatch', 'winbatch'), ('*.bat', '*.cmd'), ('application/x-dos-batch',)), + 'BddLexer': ('pip._vendor.pygments.lexers.bdd', 'Bdd', ('bdd',), ('*.feature',), ('text/x-bdd',)), + 'BefungeLexer': ('pip._vendor.pygments.lexers.esoteric', 'Befunge', ('befunge',), ('*.befunge',), ('application/x-befunge',)), + 'BerryLexer': ('pip._vendor.pygments.lexers.berry', 'Berry', ('berry', 'be'), ('*.be',), ('text/x-berry', 'application/x-berry')), + 'BibTeXLexer': ('pip._vendor.pygments.lexers.bibtex', 'BibTeX', ('bibtex', 'bib'), ('*.bib',), ('text/x-bibtex',)), + 'BlitzBasicLexer': ('pip._vendor.pygments.lexers.basic', 'BlitzBasic', ('blitzbasic', 'b3d', 'bplus'), ('*.bb', '*.decls'), ('text/x-bb',)), + 'BlitzMaxLexer': ('pip._vendor.pygments.lexers.basic', 'BlitzMax', ('blitzmax', 'bmax'), ('*.bmx',), ('text/x-bmx',)), + 'BlueprintLexer': ('pip._vendor.pygments.lexers.blueprint', 'Blueprint', ('blueprint',), ('*.blp',), ('text/x-blueprint',)), + 'BnfLexer': ('pip._vendor.pygments.lexers.grammar_notation', 'BNF', ('bnf',), ('*.bnf',), ('text/x-bnf',)), + 'BoaLexer': ('pip._vendor.pygments.lexers.boa', 'Boa', ('boa',), ('*.boa',), ()), + 'BooLexer': ('pip._vendor.pygments.lexers.dotnet', 'Boo', ('boo',), ('*.boo',), ('text/x-boo',)), + 'BoogieLexer': ('pip._vendor.pygments.lexers.verification', 'Boogie', ('boogie',), ('*.bpl',), ()), + 'BrainfuckLexer': ('pip._vendor.pygments.lexers.esoteric', 'Brainfuck', ('brainfuck', 'bf'), ('*.bf', '*.b'), ('application/x-brainfuck',)), + 'BugsLexer': ('pip._vendor.pygments.lexers.modeling', 'BUGS', ('bugs', 'winbugs', 'openbugs'), ('*.bug',), ()), + 'CAmkESLexer': ('pip._vendor.pygments.lexers.esoteric', 'CAmkES', ('camkes', 'idl4'), ('*.camkes', '*.idl4'), ()), + 'CLexer': ('pip._vendor.pygments.lexers.c_cpp', 'C', ('c',), ('*.c', '*.h', '*.idc', '*.x[bp]m'), ('text/x-chdr', 'text/x-csrc', 'image/x-xbitmap', 'image/x-xpixmap')), + 'CMakeLexer': ('pip._vendor.pygments.lexers.make', 'CMake', ('cmake',), ('*.cmake', 'CMakeLists.txt'), ('text/x-cmake',)), + 'CObjdumpLexer': ('pip._vendor.pygments.lexers.asm', 'c-objdump', ('c-objdump',), ('*.c-objdump',), ('text/x-c-objdump',)), + 'CPSALexer': ('pip._vendor.pygments.lexers.lisp', 'CPSA', ('cpsa',), ('*.cpsa',), ()), + 'CSSUL4Lexer': ('pip._vendor.pygments.lexers.ul4', 'CSS+UL4', ('css+ul4',), ('*.cssul4',), ()), + 'CSharpAspxLexer': ('pip._vendor.pygments.lexers.dotnet', 'aspx-cs', ('aspx-cs',), ('*.aspx', '*.asax', '*.ascx', '*.ashx', '*.asmx', '*.axd'), ()), + 'CSharpLexer': ('pip._vendor.pygments.lexers.dotnet', 'C#', ('csharp', 'c#', 'cs'), ('*.cs',), ('text/x-csharp',)), + 'Ca65Lexer': ('pip._vendor.pygments.lexers.asm', 'ca65 assembler', ('ca65',), ('*.s',), ()), + 'CadlLexer': ('pip._vendor.pygments.lexers.archetype', 'cADL', ('cadl',), ('*.cadl',), ()), + 'CapDLLexer': ('pip._vendor.pygments.lexers.esoteric', 'CapDL', ('capdl',), ('*.cdl',), ()), + 'CapnProtoLexer': ('pip._vendor.pygments.lexers.capnproto', "Cap'n Proto", ('capnp',), ('*.capnp',), ()), + 'CarbonLexer': ('pip._vendor.pygments.lexers.carbon', 'Carbon', ('carbon',), ('*.carbon',), ('text/x-carbon',)), + 'CbmBasicV2Lexer': ('pip._vendor.pygments.lexers.basic', 'CBM BASIC V2', ('cbmbas',), ('*.bas',), ()), + 'CddlLexer': ('pip._vendor.pygments.lexers.cddl', 'CDDL', ('cddl',), ('*.cddl',), ('text/x-cddl',)), + 'CeylonLexer': ('pip._vendor.pygments.lexers.jvm', 'Ceylon', ('ceylon',), ('*.ceylon',), ('text/x-ceylon',)), + 'Cfengine3Lexer': ('pip._vendor.pygments.lexers.configs', 'CFEngine3', ('cfengine3', 'cf3'), ('*.cf',), ()), + 'ChaiscriptLexer': ('pip._vendor.pygments.lexers.scripting', 'ChaiScript', ('chaiscript', 'chai'), ('*.chai',), ('text/x-chaiscript', 'application/x-chaiscript')), + 'ChapelLexer': ('pip._vendor.pygments.lexers.chapel', 'Chapel', ('chapel', 'chpl'), ('*.chpl',), ()), + 'CharmciLexer': ('pip._vendor.pygments.lexers.c_like', 'Charmci', ('charmci',), ('*.ci',), ()), + 'CheetahHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Cheetah', ('html+cheetah', 'html+spitfire', 'htmlcheetah'), (), ('text/html+cheetah', 'text/html+spitfire')), + 'CheetahJavascriptLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Cheetah', ('javascript+cheetah', 'js+cheetah', 'javascript+spitfire', 'js+spitfire'), (), ('application/x-javascript+cheetah', 'text/x-javascript+cheetah', 'text/javascript+cheetah', 'application/x-javascript+spitfire', 'text/x-javascript+spitfire', 'text/javascript+spitfire')), + 'CheetahLexer': ('pip._vendor.pygments.lexers.templates', 'Cheetah', ('cheetah', 'spitfire'), ('*.tmpl', '*.spt'), ('application/x-cheetah', 'application/x-spitfire')), + 'CheetahXmlLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Cheetah', ('xml+cheetah', 'xml+spitfire'), (), ('application/xml+cheetah', 'application/xml+spitfire')), + 'CirruLexer': ('pip._vendor.pygments.lexers.webmisc', 'Cirru', ('cirru',), ('*.cirru',), ('text/x-cirru',)), + 'ClayLexer': ('pip._vendor.pygments.lexers.c_like', 'Clay', ('clay',), ('*.clay',), ('text/x-clay',)), + 'CleanLexer': ('pip._vendor.pygments.lexers.clean', 'Clean', ('clean',), ('*.icl', '*.dcl'), ()), + 'ClojureLexer': ('pip._vendor.pygments.lexers.jvm', 'Clojure', ('clojure', 'clj'), ('*.clj', '*.cljc'), ('text/x-clojure', 'application/x-clojure')), + 'ClojureScriptLexer': ('pip._vendor.pygments.lexers.jvm', 'ClojureScript', ('clojurescript', 'cljs'), ('*.cljs',), ('text/x-clojurescript', 'application/x-clojurescript')), + 'CobolFreeformatLexer': ('pip._vendor.pygments.lexers.business', 'COBOLFree', ('cobolfree',), ('*.cbl', '*.CBL'), ()), + 'CobolLexer': ('pip._vendor.pygments.lexers.business', 'COBOL', ('cobol',), ('*.cob', '*.COB', '*.cpy', '*.CPY'), ('text/x-cobol',)), + 'CodeQLLexer': ('pip._vendor.pygments.lexers.codeql', 'CodeQL', ('codeql', 'ql'), ('*.ql', '*.qll'), ()), + 'CoffeeScriptLexer': ('pip._vendor.pygments.lexers.javascript', 'CoffeeScript', ('coffeescript', 'coffee-script', 'coffee'), ('*.coffee',), ('text/coffeescript',)), + 'ColdfusionCFCLexer': ('pip._vendor.pygments.lexers.templates', 'Coldfusion CFC', ('cfc',), ('*.cfc',), ()), + 'ColdfusionHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'Coldfusion HTML', ('cfm',), ('*.cfm', '*.cfml'), ('application/x-coldfusion',)), + 'ColdfusionLexer': ('pip._vendor.pygments.lexers.templates', 'cfstatement', ('cfs',), (), ()), + 'Comal80Lexer': ('pip._vendor.pygments.lexers.comal', 'COMAL-80', ('comal', 'comal80'), ('*.cml', '*.comal'), ()), + 'CommonLispLexer': ('pip._vendor.pygments.lexers.lisp', 'Common Lisp', ('common-lisp', 'cl', 'lisp'), ('*.cl', '*.lisp'), ('text/x-common-lisp',)), + 'ComponentPascalLexer': ('pip._vendor.pygments.lexers.oberon', 'Component Pascal', ('componentpascal', 'cp'), ('*.cp', '*.cps'), ('text/x-component-pascal',)), + 'CplintLexer': ('pip._vendor.pygments.lexers.cplint', 'cplint', ('cplint',), ('*.ecl', '*.prolog', '*.pro', '*.pl', '*.P', '*.lpad', '*.cpl'), ('text/x-cplint',)), + 'CppLexer': ('pip._vendor.pygments.lexers.c_cpp', 'C++', ('cpp', 'c++'), ('*.cpp', '*.hpp', '*.c++', '*.h++', '*.cc', '*.hh', '*.cxx', '*.hxx', '*.C', '*.H', '*.cp', '*.CPP', '*.tpp', '*.cppm', '*.ixx', '*.mxx'), ('text/x-c++hdr', 'text/x-c++src')), + 'CppObjdumpLexer': ('pip._vendor.pygments.lexers.asm', 'cpp-objdump', ('cpp-objdump', 'c++-objdumb', 'cxx-objdump'), ('*.cpp-objdump', '*.c++-objdump', '*.cxx-objdump'), ('text/x-cpp-objdump',)), + 'CrmshLexer': ('pip._vendor.pygments.lexers.dsls', 'Crmsh', ('crmsh', 'pcmk'), ('*.crmsh', '*.pcmk'), ()), + 'CrocLexer': ('pip._vendor.pygments.lexers.d', 'Croc', ('croc',), ('*.croc',), ('text/x-crocsrc',)), + 'CryptolLexer': ('pip._vendor.pygments.lexers.haskell', 'Cryptol', ('cryptol', 'cry'), ('*.cry',), ('text/x-cryptol',)), + 'CrystalLexer': ('pip._vendor.pygments.lexers.crystal', 'Crystal', ('cr', 'crystal'), ('*.cr',), ('text/x-crystal',)), + 'CsoundDocumentLexer': ('pip._vendor.pygments.lexers.csound', 'Csound Document', ('csound-document', 'csound-csd'), ('*.csd',), ()), + 'CsoundOrchestraLexer': ('pip._vendor.pygments.lexers.csound', 'Csound Orchestra', ('csound', 'csound-orc'), ('*.orc', '*.udo'), ()), + 'CsoundScoreLexer': ('pip._vendor.pygments.lexers.csound', 'Csound Score', ('csound-score', 'csound-sco'), ('*.sco',), ()), + 'CssDjangoLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Django/Jinja', ('css+django', 'css+jinja'), ('*.css.j2', '*.css.jinja2'), ('text/css+django', 'text/css+jinja')), + 'CssErbLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Ruby', ('css+ruby', 'css+erb'), (), ('text/css+ruby',)), + 'CssGenshiLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Genshi Text', ('css+genshitext', 'css+genshi'), (), ('text/css+genshi',)), + 'CssLexer': ('pip._vendor.pygments.lexers.css', 'CSS', ('css',), ('*.css',), ('text/css',)), + 'CssPhpLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+PHP', ('css+php',), (), ('text/css+php',)), + 'CssSmartyLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Smarty', ('css+smarty',), (), ('text/css+smarty',)), + 'CudaLexer': ('pip._vendor.pygments.lexers.c_like', 'CUDA', ('cuda', 'cu'), ('*.cu', '*.cuh'), ('text/x-cuda',)), + 'CypherLexer': ('pip._vendor.pygments.lexers.graph', 'Cypher', ('cypher',), ('*.cyp', '*.cypher'), ()), + 'CythonLexer': ('pip._vendor.pygments.lexers.python', 'Cython', ('cython', 'pyx', 'pyrex'), ('*.pyx', '*.pxd', '*.pxi'), ('text/x-cython', 'application/x-cython')), + 'DLexer': ('pip._vendor.pygments.lexers.d', 'D', ('d',), ('*.d', '*.di'), ('text/x-dsrc',)), + 'DObjdumpLexer': ('pip._vendor.pygments.lexers.asm', 'd-objdump', ('d-objdump',), ('*.d-objdump',), ('text/x-d-objdump',)), + 'DarcsPatchLexer': ('pip._vendor.pygments.lexers.diff', 'Darcs Patch', ('dpatch',), ('*.dpatch', '*.darcspatch'), ()), + 'DartLexer': ('pip._vendor.pygments.lexers.javascript', 'Dart', ('dart',), ('*.dart',), ('text/x-dart',)), + 'Dasm16Lexer': ('pip._vendor.pygments.lexers.asm', 'DASM16', ('dasm16',), ('*.dasm16', '*.dasm'), ('text/x-dasm16',)), + 'DaxLexer': ('pip._vendor.pygments.lexers.dax', 'Dax', ('dax',), ('*.dax',), ()), + 'DebianControlLexer': ('pip._vendor.pygments.lexers.installers', 'Debian Control file', ('debcontrol', 'control'), ('control',), ()), + 'DebianSourcesLexer': ('pip._vendor.pygments.lexers.installers', 'Debian Sources file', ('debian.sources',), ('*.sources',), ()), + 'DelphiLexer': ('pip._vendor.pygments.lexers.pascal', 'Delphi', ('delphi', 'pas', 'pascal', 'objectpascal'), ('*.pas', '*.dpr'), ('text/x-pascal',)), + 'DesktopLexer': ('pip._vendor.pygments.lexers.configs', 'Desktop file', ('desktop',), ('*.desktop',), ('application/x-desktop',)), + 'DevicetreeLexer': ('pip._vendor.pygments.lexers.devicetree', 'Devicetree', ('devicetree', 'dts'), ('*.dts', '*.dtsi'), ('text/x-c',)), + 'DgLexer': ('pip._vendor.pygments.lexers.python', 'dg', ('dg',), ('*.dg',), ('text/x-dg',)), + 'DiffLexer': ('pip._vendor.pygments.lexers.diff', 'Diff', ('diff', 'udiff'), ('*.diff', '*.patch'), ('text/x-diff', 'text/x-patch')), + 'DjangoLexer': ('pip._vendor.pygments.lexers.templates', 'Django/Jinja', ('django', 'jinja'), (), ('application/x-django-templating', 'application/x-jinja')), + 'DnsZoneLexer': ('pip._vendor.pygments.lexers.dns', 'Zone', ('zone',), ('*.zone',), ('text/dns',)), + 'DockerLexer': ('pip._vendor.pygments.lexers.configs', 'Docker', ('docker', 'dockerfile'), ('Dockerfile', '*.docker'), ('text/x-dockerfile-config',)), + 'DtdLexer': ('pip._vendor.pygments.lexers.html', 'DTD', ('dtd',), ('*.dtd',), ('application/xml-dtd',)), + 'DuelLexer': ('pip._vendor.pygments.lexers.webmisc', 'Duel', ('duel', 'jbst', 'jsonml+bst'), ('*.duel', '*.jbst'), ('text/x-duel', 'text/x-jbst')), + 'DylanConsoleLexer': ('pip._vendor.pygments.lexers.dylan', 'Dylan session', ('dylan-console', 'dylan-repl'), ('*.dylan-console',), ('text/x-dylan-console',)), + 'DylanLexer': ('pip._vendor.pygments.lexers.dylan', 'Dylan', ('dylan',), ('*.dylan', '*.dyl', '*.intr'), ('text/x-dylan',)), + 'DylanLidLexer': ('pip._vendor.pygments.lexers.dylan', 'DylanLID', ('dylan-lid', 'lid'), ('*.lid', '*.hdp'), ('text/x-dylan-lid',)), + 'ECLLexer': ('pip._vendor.pygments.lexers.ecl', 'ECL', ('ecl',), ('*.ecl',), ('application/x-ecl',)), + 'ECLexer': ('pip._vendor.pygments.lexers.c_like', 'eC', ('ec',), ('*.ec', '*.eh'), ('text/x-echdr', 'text/x-ecsrc')), + 'EarlGreyLexer': ('pip._vendor.pygments.lexers.javascript', 'Earl Grey', ('earl-grey', 'earlgrey', 'eg'), ('*.eg',), ('text/x-earl-grey',)), + 'EasytrieveLexer': ('pip._vendor.pygments.lexers.scripting', 'Easytrieve', ('easytrieve',), ('*.ezt', '*.mac'), ('text/x-easytrieve',)), + 'EbnfLexer': ('pip._vendor.pygments.lexers.parsers', 'EBNF', ('ebnf',), ('*.ebnf',), ('text/x-ebnf',)), + 'EiffelLexer': ('pip._vendor.pygments.lexers.eiffel', 'Eiffel', ('eiffel',), ('*.e',), ('text/x-eiffel',)), + 'ElixirConsoleLexer': ('pip._vendor.pygments.lexers.erlang', 'Elixir iex session', ('iex',), (), ('text/x-elixir-shellsession',)), + 'ElixirLexer': ('pip._vendor.pygments.lexers.erlang', 'Elixir', ('elixir', 'ex', 'exs'), ('*.ex', '*.eex', '*.exs', '*.leex'), ('text/x-elixir',)), + 'ElmLexer': ('pip._vendor.pygments.lexers.elm', 'Elm', ('elm',), ('*.elm',), ('text/x-elm',)), + 'ElpiLexer': ('pip._vendor.pygments.lexers.elpi', 'Elpi', ('elpi',), ('*.elpi',), ('text/x-elpi',)), + 'EmacsLispLexer': ('pip._vendor.pygments.lexers.lisp', 'EmacsLisp', ('emacs-lisp', 'elisp', 'emacs'), ('*.el',), ('text/x-elisp', 'application/x-elisp')), + 'EmailLexer': ('pip._vendor.pygments.lexers.email', 'E-mail', ('email', 'eml'), ('*.eml',), ('message/rfc822',)), + 'ErbLexer': ('pip._vendor.pygments.lexers.templates', 'ERB', ('erb',), (), ('application/x-ruby-templating',)), + 'ErlangLexer': ('pip._vendor.pygments.lexers.erlang', 'Erlang', ('erlang',), ('*.erl', '*.hrl', '*.es', '*.escript'), ('text/x-erlang',)), + 'ErlangShellLexer': ('pip._vendor.pygments.lexers.erlang', 'Erlang erl session', ('erl',), ('*.erl-sh',), ('text/x-erl-shellsession',)), + 'EvoqueHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Evoque', ('html+evoque',), (), ('text/html+evoque',)), + 'EvoqueLexer': ('pip._vendor.pygments.lexers.templates', 'Evoque', ('evoque',), ('*.evoque',), ('application/x-evoque',)), + 'EvoqueXmlLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Evoque', ('xml+evoque',), (), ('application/xml+evoque',)), + 'ExeclineLexer': ('pip._vendor.pygments.lexers.shell', 'execline', ('execline',), ('*.exec',), ()), + 'EzhilLexer': ('pip._vendor.pygments.lexers.ezhil', 'Ezhil', ('ezhil',), ('*.n',), ('text/x-ezhil',)), + 'FSharpLexer': ('pip._vendor.pygments.lexers.dotnet', 'F#', ('fsharp', 'f#'), ('*.fs', '*.fsi', '*.fsx'), ('text/x-fsharp',)), + 'FStarLexer': ('pip._vendor.pygments.lexers.ml', 'FStar', ('fstar',), ('*.fst', '*.fsti'), ('text/x-fstar',)), + 'FactorLexer': ('pip._vendor.pygments.lexers.factor', 'Factor', ('factor',), ('*.factor',), ('text/x-factor',)), + 'FancyLexer': ('pip._vendor.pygments.lexers.ruby', 'Fancy', ('fancy', 'fy'), ('*.fy', '*.fancypack'), ('text/x-fancysrc',)), + 'FantomLexer': ('pip._vendor.pygments.lexers.fantom', 'Fantom', ('fan',), ('*.fan',), ('application/x-fantom',)), + 'FelixLexer': ('pip._vendor.pygments.lexers.felix', 'Felix', ('felix', 'flx'), ('*.flx', '*.flxh'), ('text/x-felix',)), + 'FennelLexer': ('pip._vendor.pygments.lexers.lisp', 'Fennel', ('fennel', 'fnl'), ('*.fnl', '*.fnlm'), ('text/x-fennel',)), + 'FiftLexer': ('pip._vendor.pygments.lexers.fift', 'Fift', ('fift', 'fif'), ('*.fif',), ()), + 'FishShellLexer': ('pip._vendor.pygments.lexers.shell', 'Fish', ('fish', 'fishshell'), ('*.fish', '*.load'), ('application/x-fish',)), + 'FlatlineLexer': ('pip._vendor.pygments.lexers.dsls', 'Flatline', ('flatline',), (), ('text/x-flatline',)), + 'FloScriptLexer': ('pip._vendor.pygments.lexers.floscript', 'FloScript', ('floscript', 'flo'), ('*.flo',), ()), + 'ForthLexer': ('pip._vendor.pygments.lexers.forth', 'Forth', ('forth',), ('*.frt', '*.fs'), ('application/x-forth',)), + 'FortranFixedLexer': ('pip._vendor.pygments.lexers.fortran', 'FortranFixed', ('fortranfixed',), ('*.f', '*.F'), ()), + 'FortranLexer': ('pip._vendor.pygments.lexers.fortran', 'Fortran', ('fortran', 'f90'), ('*.f03', '*.f90', '*.F03', '*.F90'), ('text/x-fortran',)), + 'FoxProLexer': ('pip._vendor.pygments.lexers.foxpro', 'FoxPro', ('foxpro', 'vfp', 'clipper', 'xbase'), ('*.PRG', '*.prg'), ()), + 'FreeFemLexer': ('pip._vendor.pygments.lexers.freefem', 'Freefem', ('freefem',), ('*.edp',), ('text/x-freefem',)), + 'FuncLexer': ('pip._vendor.pygments.lexers.func', 'FunC', ('func', 'fc'), ('*.fc', '*.func'), ()), + 'FutharkLexer': ('pip._vendor.pygments.lexers.futhark', 'Futhark', ('futhark',), ('*.fut',), ('text/x-futhark',)), + 'GAPConsoleLexer': ('pip._vendor.pygments.lexers.algebra', 'GAP session', ('gap-console', 'gap-repl'), ('*.tst',), ()), + 'GAPLexer': ('pip._vendor.pygments.lexers.algebra', 'GAP', ('gap',), ('*.g', '*.gd', '*.gi', '*.gap'), ()), + 'GDScriptLexer': ('pip._vendor.pygments.lexers.gdscript', 'GDScript', ('gdscript', 'gd'), ('*.gd',), ('text/x-gdscript', 'application/x-gdscript')), + 'GLShaderLexer': ('pip._vendor.pygments.lexers.graphics', 'GLSL', ('glsl',), ('*.vert', '*.frag', '*.geo'), ('text/x-glslsrc',)), + 'GSQLLexer': ('pip._vendor.pygments.lexers.gsql', 'GSQL', ('gsql',), ('*.gsql',), ()), + 'GasLexer': ('pip._vendor.pygments.lexers.asm', 'GAS', ('gas', 'asm'), ('*.s', '*.S'), ('text/x-gas',)), + 'GcodeLexer': ('pip._vendor.pygments.lexers.gcodelexer', 'g-code', ('gcode',), ('*.gcode',), ()), + 'GenshiLexer': ('pip._vendor.pygments.lexers.templates', 'Genshi', ('genshi', 'kid', 'xml+genshi', 'xml+kid'), ('*.kid',), ('application/x-genshi', 'application/x-kid')), + 'GenshiTextLexer': ('pip._vendor.pygments.lexers.templates', 'Genshi Text', ('genshitext',), (), ('application/x-genshi-text', 'text/x-genshi')), + 'GettextLexer': ('pip._vendor.pygments.lexers.textfmts', 'Gettext Catalog', ('pot', 'po'), ('*.pot', '*.po'), ('application/x-gettext', 'text/x-gettext', 'text/gettext')), + 'GherkinLexer': ('pip._vendor.pygments.lexers.testing', 'Gherkin', ('gherkin', 'cucumber'), ('*.feature',), ('text/x-gherkin',)), + 'GleamLexer': ('pip._vendor.pygments.lexers.gleam', 'Gleam', ('gleam',), ('*.gleam',), ('text/x-gleam',)), + 'GnuplotLexer': ('pip._vendor.pygments.lexers.graphics', 'Gnuplot', ('gnuplot',), ('*.plot', '*.plt'), ('text/x-gnuplot',)), + 'GoLexer': ('pip._vendor.pygments.lexers.go', 'Go', ('go', 'golang'), ('*.go',), ('text/x-gosrc',)), + 'GoloLexer': ('pip._vendor.pygments.lexers.jvm', 'Golo', ('golo',), ('*.golo',), ()), + 'GoodDataCLLexer': ('pip._vendor.pygments.lexers.business', 'GoodData-CL', ('gooddata-cl',), ('*.gdc',), ('text/x-gooddata-cl',)), + 'GoogleSqlLexer': ('pip._vendor.pygments.lexers.sql', 'GoogleSQL', ('googlesql', 'zetasql'), ('*.googlesql', '*.googlesql.sql'), ('text/x-google-sql', 'text/x-google-sql-aux')), + 'GosuLexer': ('pip._vendor.pygments.lexers.jvm', 'Gosu', ('gosu',), ('*.gs', '*.gsx', '*.gsp', '*.vark'), ('text/x-gosu',)), + 'GosuTemplateLexer': ('pip._vendor.pygments.lexers.jvm', 'Gosu Template', ('gst',), ('*.gst',), ('text/x-gosu-template',)), + 'GraphQLLexer': ('pip._vendor.pygments.lexers.graphql', 'GraphQL', ('graphql',), ('*.graphql',), ()), + 'GraphvizLexer': ('pip._vendor.pygments.lexers.graphviz', 'Graphviz', ('graphviz', 'dot'), ('*.gv', '*.dot'), ('text/x-graphviz', 'text/vnd.graphviz')), + 'GroffLexer': ('pip._vendor.pygments.lexers.markup', 'Groff', ('groff', 'nroff', 'man'), ('*.[1-9]', '*.man', '*.1p', '*.3pm'), ('application/x-troff', 'text/troff')), + 'GroovyLexer': ('pip._vendor.pygments.lexers.jvm', 'Groovy', ('groovy',), ('*.groovy', '*.gradle'), ('text/x-groovy',)), + 'HLSLShaderLexer': ('pip._vendor.pygments.lexers.graphics', 'HLSL', ('hlsl',), ('*.hlsl', '*.hlsli'), ('text/x-hlsl',)), + 'HTMLUL4Lexer': ('pip._vendor.pygments.lexers.ul4', 'HTML+UL4', ('html+ul4',), ('*.htmlul4',), ()), + 'HamlLexer': ('pip._vendor.pygments.lexers.html', 'Haml', ('haml',), ('*.haml',), ('text/x-haml',)), + 'HandlebarsHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Handlebars', ('html+handlebars',), ('*.handlebars', '*.hbs'), ('text/html+handlebars', 'text/x-handlebars-template')), + 'HandlebarsLexer': ('pip._vendor.pygments.lexers.templates', 'Handlebars', ('handlebars',), (), ()), + 'HareLexer': ('pip._vendor.pygments.lexers.hare', 'Hare', ('hare',), ('*.ha',), ('text/x-hare',)), + 'HaskellLexer': ('pip._vendor.pygments.lexers.haskell', 'Haskell', ('haskell', 'hs'), ('*.hs',), ('text/x-haskell',)), + 'HaxeLexer': ('pip._vendor.pygments.lexers.haxe', 'Haxe', ('haxe', 'hxsl', 'hx'), ('*.hx', '*.hxsl'), ('text/haxe', 'text/x-haxe', 'text/x-hx')), + 'HexdumpLexer': ('pip._vendor.pygments.lexers.hexdump', 'Hexdump', ('hexdump',), (), ()), + 'HsailLexer': ('pip._vendor.pygments.lexers.asm', 'HSAIL', ('hsail', 'hsa'), ('*.hsail',), ('text/x-hsail',)), + 'HspecLexer': ('pip._vendor.pygments.lexers.haskell', 'Hspec', ('hspec',), ('*Spec.hs',), ()), + 'HtmlDjangoLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Django/Jinja', ('html+django', 'html+jinja', 'htmldjango'), ('*.html.j2', '*.htm.j2', '*.xhtml.j2', '*.html.jinja2', '*.htm.jinja2', '*.xhtml.jinja2'), ('text/html+django', 'text/html+jinja')), + 'HtmlGenshiLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Genshi', ('html+genshi', 'html+kid'), (), ('text/html+genshi',)), + 'HtmlLexer': ('pip._vendor.pygments.lexers.html', 'HTML', ('html',), ('*.html', '*.htm', '*.xhtml', '*.xslt'), ('text/html', 'application/xhtml+xml')), + 'HtmlPhpLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+PHP', ('html+php',), ('*.phtml',), ('application/x-php', 'application/x-httpd-php', 'application/x-httpd-php3', 'application/x-httpd-php4', 'application/x-httpd-php5')), + 'HtmlSmartyLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Smarty', ('html+smarty',), (), ('text/html+smarty',)), + 'HttpLexer': ('pip._vendor.pygments.lexers.textfmts', 'HTTP', ('http',), (), ()), + 'HxmlLexer': ('pip._vendor.pygments.lexers.haxe', 'Hxml', ('haxeml', 'hxml'), ('*.hxml',), ()), + 'HyLexer': ('pip._vendor.pygments.lexers.lisp', 'Hy', ('hylang', 'hy'), ('*.hy',), ('text/x-hy', 'application/x-hy')), + 'HybrisLexer': ('pip._vendor.pygments.lexers.scripting', 'Hybris', ('hybris',), ('*.hyb',), ('text/x-hybris', 'application/x-hybris')), + 'IDLLexer': ('pip._vendor.pygments.lexers.idl', 'IDL', ('idl',), ('*.pro',), ('text/idl',)), + 'IconLexer': ('pip._vendor.pygments.lexers.unicon', 'Icon', ('icon',), ('*.icon', '*.ICON'), ()), + 'IdrisLexer': ('pip._vendor.pygments.lexers.haskell', 'Idris', ('idris', 'idr'), ('*.idr',), ('text/x-idris',)), + 'IgorLexer': ('pip._vendor.pygments.lexers.igor', 'Igor', ('igor', 'igorpro'), ('*.ipf',), ('text/ipf',)), + 'Inform6Lexer': ('pip._vendor.pygments.lexers.int_fiction', 'Inform 6', ('inform6', 'i6'), ('*.inf',), ()), + 'Inform6TemplateLexer': ('pip._vendor.pygments.lexers.int_fiction', 'Inform 6 template', ('i6t',), ('*.i6t',), ()), + 'Inform7Lexer': ('pip._vendor.pygments.lexers.int_fiction', 'Inform 7', ('inform7', 'i7'), ('*.ni', '*.i7x'), ()), + 'IniLexer': ('pip._vendor.pygments.lexers.configs', 'INI', ('ini', 'cfg', 'dosini'), ('*.ini', '*.cfg', '*.inf', '.editorconfig'), ('text/x-ini', 'text/inf')), + 'IoLexer': ('pip._vendor.pygments.lexers.iolang', 'Io', ('io',), ('*.io',), ('text/x-iosrc',)), + 'IokeLexer': ('pip._vendor.pygments.lexers.jvm', 'Ioke', ('ioke', 'ik'), ('*.ik',), ('text/x-iokesrc',)), + 'IrcLogsLexer': ('pip._vendor.pygments.lexers.textfmts', 'IRC logs', ('irc',), ('*.weechatlog',), ('text/x-irclog',)), + 'IsabelleLexer': ('pip._vendor.pygments.lexers.theorem', 'Isabelle', ('isabelle',), ('*.thy',), ('text/x-isabelle',)), + 'JLexer': ('pip._vendor.pygments.lexers.j', 'J', ('j',), ('*.ijs',), ('text/x-j',)), + 'JMESPathLexer': ('pip._vendor.pygments.lexers.jmespath', 'JMESPath', ('jmespath', 'jp'), ('*.jp',), ()), + 'JSLTLexer': ('pip._vendor.pygments.lexers.jslt', 'JSLT', ('jslt',), ('*.jslt',), ('text/x-jslt',)), + 'JagsLexer': ('pip._vendor.pygments.lexers.modeling', 'JAGS', ('jags',), ('*.jag', '*.bug'), ()), + 'JanetLexer': ('pip._vendor.pygments.lexers.lisp', 'Janet', ('janet',), ('*.janet', '*.jdn'), ('text/x-janet', 'application/x-janet')), + 'JasminLexer': ('pip._vendor.pygments.lexers.jvm', 'Jasmin', ('jasmin', 'jasminxt'), ('*.j',), ()), + 'JavaLexer': ('pip._vendor.pygments.lexers.jvm', 'Java', ('java',), ('*.java',), ('text/x-java',)), + 'JavascriptDjangoLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Django/Jinja', ('javascript+django', 'js+django', 'javascript+jinja', 'js+jinja'), ('*.js.j2', '*.js.jinja2'), ('application/x-javascript+django', 'application/x-javascript+jinja', 'text/x-javascript+django', 'text/x-javascript+jinja', 'text/javascript+django', 'text/javascript+jinja')), + 'JavascriptErbLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Ruby', ('javascript+ruby', 'js+ruby', 'javascript+erb', 'js+erb'), (), ('application/x-javascript+ruby', 'text/x-javascript+ruby', 'text/javascript+ruby')), + 'JavascriptGenshiLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Genshi Text', ('js+genshitext', 'js+genshi', 'javascript+genshitext', 'javascript+genshi'), (), ('application/x-javascript+genshi', 'text/x-javascript+genshi', 'text/javascript+genshi')), + 'JavascriptLexer': ('pip._vendor.pygments.lexers.javascript', 'JavaScript', ('javascript', 'js'), ('*.js', '*.jsm', '*.mjs', '*.cjs'), ('application/javascript', 'application/x-javascript', 'text/x-javascript', 'text/javascript')), + 'JavascriptPhpLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+PHP', ('javascript+php', 'js+php'), (), ('application/x-javascript+php', 'text/x-javascript+php', 'text/javascript+php')), + 'JavascriptSmartyLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Smarty', ('javascript+smarty', 'js+smarty'), (), ('application/x-javascript+smarty', 'text/x-javascript+smarty', 'text/javascript+smarty')), + 'JavascriptUL4Lexer': ('pip._vendor.pygments.lexers.ul4', 'Javascript+UL4', ('js+ul4',), ('*.jsul4',), ()), + 'JclLexer': ('pip._vendor.pygments.lexers.scripting', 'JCL', ('jcl',), ('*.jcl',), ('text/x-jcl',)), + 'JsgfLexer': ('pip._vendor.pygments.lexers.grammar_notation', 'JSGF', ('jsgf',), ('*.jsgf',), ('application/jsgf', 'application/x-jsgf', 'text/jsgf')), + 'Json5Lexer': ('pip._vendor.pygments.lexers.json5', 'JSON5', ('json5',), ('*.json5',), ()), + 'JsonBareObjectLexer': ('pip._vendor.pygments.lexers.data', 'JSONBareObject', (), (), ()), + 'JsonLdLexer': ('pip._vendor.pygments.lexers.data', 'JSON-LD', ('jsonld', 'json-ld'), ('*.jsonld',), ('application/ld+json',)), + 'JsonLexer': ('pip._vendor.pygments.lexers.data', 'JSON', ('json', 'json-object'), ('*.json', '*.jsonl', '*.ndjson', 'Pipfile.lock', '*.module', '*.xc'), ('application/json', 'application/json-object', 'application/x-ndjson', 'application/jsonl', 'application/json-seq')), + 'JsonnetLexer': ('pip._vendor.pygments.lexers.jsonnet', 'Jsonnet', ('jsonnet',), ('*.jsonnet', '*.libsonnet'), ()), + 'JspLexer': ('pip._vendor.pygments.lexers.templates', 'Java Server Page', ('jsp',), ('*.jsp',), ('application/x-jsp',)), + 'JsxLexer': ('pip._vendor.pygments.lexers.jsx', 'JSX', ('jsx', 'react'), ('*.jsx', '*.react'), ('text/jsx', 'text/typescript-jsx')), + 'JuliaConsoleLexer': ('pip._vendor.pygments.lexers.julia', 'Julia console', ('jlcon', 'julia-repl'), (), ()), + 'JuliaLexer': ('pip._vendor.pygments.lexers.julia', 'Julia', ('julia', 'jl'), ('*.jl',), ('text/x-julia', 'application/x-julia')), + 'JuttleLexer': ('pip._vendor.pygments.lexers.javascript', 'Juttle', ('juttle',), ('*.juttle',), ('application/juttle', 'application/x-juttle', 'text/x-juttle', 'text/juttle')), + 'KLexer': ('pip._vendor.pygments.lexers.q', 'K', ('k',), ('*.k',), ()), + 'KalLexer': ('pip._vendor.pygments.lexers.javascript', 'Kal', ('kal',), ('*.kal',), ('text/kal', 'application/kal')), + 'KconfigLexer': ('pip._vendor.pygments.lexers.configs', 'Kconfig', ('kconfig', 'menuconfig', 'linux-config', 'kernel-config'), ('Kconfig*', '*Config.in*', 'external.in*', 'standard-modules.in'), ('text/x-kconfig',)), + 'KernelLogLexer': ('pip._vendor.pygments.lexers.textfmts', 'Kernel log', ('kmsg', 'dmesg'), ('*.kmsg', '*.dmesg'), ()), + 'KokaLexer': ('pip._vendor.pygments.lexers.haskell', 'Koka', ('koka',), ('*.kk', '*.kki'), ('text/x-koka',)), + 'KotlinLexer': ('pip._vendor.pygments.lexers.jvm', 'Kotlin', ('kotlin',), ('*.kt', '*.kts'), ('text/x-kotlin',)), + 'KuinLexer': ('pip._vendor.pygments.lexers.kuin', 'Kuin', ('kuin',), ('*.kn',), ()), + 'KustoLexer': ('pip._vendor.pygments.lexers.kusto', 'Kusto', ('kql', 'kusto'), ('*.kql', '*.kusto', '.csl'), ()), + 'LSLLexer': ('pip._vendor.pygments.lexers.scripting', 'LSL', ('lsl',), ('*.lsl',), ('text/x-lsl',)), + 'LassoCssLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Lasso', ('css+lasso',), (), ('text/css+lasso',)), + 'LassoHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Lasso', ('html+lasso',), (), ('text/html+lasso', 'application/x-httpd-lasso', 'application/x-httpd-lasso[89]')), + 'LassoJavascriptLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Lasso', ('javascript+lasso', 'js+lasso'), (), ('application/x-javascript+lasso', 'text/x-javascript+lasso', 'text/javascript+lasso')), + 'LassoLexer': ('pip._vendor.pygments.lexers.javascript', 'Lasso', ('lasso', 'lassoscript'), ('*.lasso', '*.lasso[89]'), ('text/x-lasso',)), + 'LassoXmlLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Lasso', ('xml+lasso',), (), ('application/xml+lasso',)), + 'LdaprcLexer': ('pip._vendor.pygments.lexers.ldap', 'LDAP configuration file', ('ldapconf', 'ldaprc'), ('.ldaprc', 'ldaprc', 'ldap.conf'), ('text/x-ldapconf',)), + 'LdifLexer': ('pip._vendor.pygments.lexers.ldap', 'LDIF', ('ldif',), ('*.ldif',), ('text/x-ldif',)), + 'Lean3Lexer': ('pip._vendor.pygments.lexers.lean', 'Lean', ('lean', 'lean3'), ('*.lean',), ('text/x-lean', 'text/x-lean3')), + 'Lean4Lexer': ('pip._vendor.pygments.lexers.lean', 'Lean4', ('lean4',), ('*.lean',), ('text/x-lean4',)), + 'LessCssLexer': ('pip._vendor.pygments.lexers.css', 'LessCss', ('less',), ('*.less',), ('text/x-less-css',)), + 'LighttpdConfLexer': ('pip._vendor.pygments.lexers.configs', 'Lighttpd configuration file', ('lighttpd', 'lighty'), ('lighttpd.conf',), ('text/x-lighttpd-conf',)), + 'LilyPondLexer': ('pip._vendor.pygments.lexers.lilypond', 'LilyPond', ('lilypond',), ('*.ly',), ()), + 'LimboLexer': ('pip._vendor.pygments.lexers.inferno', 'Limbo', ('limbo',), ('*.b',), ('text/limbo',)), + 'LiquidLexer': ('pip._vendor.pygments.lexers.templates', 'liquid', ('liquid',), ('*.liquid',), ()), + 'LiterateAgdaLexer': ('pip._vendor.pygments.lexers.haskell', 'Literate Agda', ('literate-agda', 'lagda'), ('*.lagda',), ('text/x-literate-agda',)), + 'LiterateCryptolLexer': ('pip._vendor.pygments.lexers.haskell', 'Literate Cryptol', ('literate-cryptol', 'lcryptol', 'lcry'), ('*.lcry',), ('text/x-literate-cryptol',)), + 'LiterateHaskellLexer': ('pip._vendor.pygments.lexers.haskell', 'Literate Haskell', ('literate-haskell', 'lhaskell', 'lhs'), ('*.lhs',), ('text/x-literate-haskell',)), + 'LiterateIdrisLexer': ('pip._vendor.pygments.lexers.haskell', 'Literate Idris', ('literate-idris', 'lidris', 'lidr'), ('*.lidr',), ('text/x-literate-idris',)), + 'LiveScriptLexer': ('pip._vendor.pygments.lexers.javascript', 'LiveScript', ('livescript', 'live-script'), ('*.ls',), ('text/livescript',)), + 'LlvmLexer': ('pip._vendor.pygments.lexers.asm', 'LLVM', ('llvm',), ('*.ll',), ('text/x-llvm',)), + 'LlvmMirBodyLexer': ('pip._vendor.pygments.lexers.asm', 'LLVM-MIR Body', ('llvm-mir-body',), (), ()), + 'LlvmMirLexer': ('pip._vendor.pygments.lexers.asm', 'LLVM-MIR', ('llvm-mir',), ('*.mir',), ()), + 'LogosLexer': ('pip._vendor.pygments.lexers.objective', 'Logos', ('logos',), ('*.x', '*.xi', '*.xm', '*.xmi'), ('text/x-logos',)), + 'LogtalkLexer': ('pip._vendor.pygments.lexers.prolog', 'Logtalk', ('logtalk',), ('*.lgt', '*.logtalk'), ('text/x-logtalk',)), + 'LuaLexer': ('pip._vendor.pygments.lexers.scripting', 'Lua', ('lua',), ('*.lua', '*.wlua'), ('text/x-lua', 'application/x-lua')), + 'LuauLexer': ('pip._vendor.pygments.lexers.scripting', 'Luau', ('luau',), ('*.luau',), ()), + 'MCFunctionLexer': ('pip._vendor.pygments.lexers.minecraft', 'MCFunction', ('mcfunction', 'mcf'), ('*.mcfunction',), ('text/mcfunction',)), + 'MCSchemaLexer': ('pip._vendor.pygments.lexers.minecraft', 'MCSchema', ('mcschema',), ('*.mcschema',), ('text/mcschema',)), + 'MIMELexer': ('pip._vendor.pygments.lexers.mime', 'MIME', ('mime',), (), ('multipart/mixed', 'multipart/related', 'multipart/alternative')), + 'MIPSLexer': ('pip._vendor.pygments.lexers.mips', 'MIPS', ('mips',), ('*.mips', '*.MIPS'), ()), + 'MOOCodeLexer': ('pip._vendor.pygments.lexers.scripting', 'MOOCode', ('moocode', 'moo'), ('*.moo',), ('text/x-moocode',)), + 'MSDOSSessionLexer': ('pip._vendor.pygments.lexers.shell', 'MSDOS Session', ('doscon',), (), ()), + 'Macaulay2Lexer': ('pip._vendor.pygments.lexers.macaulay2', 'Macaulay2', ('macaulay2',), ('*.m2',), ()), + 'MakefileLexer': ('pip._vendor.pygments.lexers.make', 'Makefile', ('make', 'makefile', 'mf', 'bsdmake'), ('*.mak', '*.mk', 'Makefile', 'makefile', 'Makefile.*', 'GNUmakefile'), ('text/x-makefile',)), + 'MakoCssLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Mako', ('css+mako',), (), ('text/css+mako',)), + 'MakoHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Mako', ('html+mako',), (), ('text/html+mako',)), + 'MakoJavascriptLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Mako', ('javascript+mako', 'js+mako'), (), ('application/x-javascript+mako', 'text/x-javascript+mako', 'text/javascript+mako')), + 'MakoLexer': ('pip._vendor.pygments.lexers.templates', 'Mako', ('mako',), ('*.mao',), ('application/x-mako',)), + 'MakoXmlLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Mako', ('xml+mako',), (), ('application/xml+mako',)), + 'MapleLexer': ('pip._vendor.pygments.lexers.maple', 'Maple', ('maple',), ('*.mpl', '*.mi', '*.mm'), ('text/x-maple',)), + 'MaqlLexer': ('pip._vendor.pygments.lexers.business', 'MAQL', ('maql',), ('*.maql',), ('text/x-gooddata-maql', 'application/x-gooddata-maql')), + 'MarkdownLexer': ('pip._vendor.pygments.lexers.markup', 'Markdown', ('markdown', 'md'), ('*.md', '*.markdown'), ('text/x-markdown',)), + 'MaskLexer': ('pip._vendor.pygments.lexers.javascript', 'Mask', ('mask',), ('*.mask',), ('text/x-mask',)), + 'MasonLexer': ('pip._vendor.pygments.lexers.templates', 'Mason', ('mason',), ('*.m', '*.mhtml', '*.mc', '*.mi', 'autohandler', 'dhandler'), ('application/x-mason',)), + 'MathematicaLexer': ('pip._vendor.pygments.lexers.algebra', 'Mathematica', ('mathematica', 'mma', 'nb', 'wl', 'wolfram'), ('*.nb', '*.cdf', '*.nbp', '*.ma', '*.wl', '*.wls'), ('application/mathematica', 'application/vnd.wolfram.mathematica', 'application/vnd.wolfram.mathematica.package', 'application/vnd.wolfram.cdf', 'application/vnd.wolfram.wl')), + 'MatlabLexer': ('pip._vendor.pygments.lexers.matlab', 'Matlab', ('matlab',), ('*.m',), ('text/matlab',)), + 'MatlabSessionLexer': ('pip._vendor.pygments.lexers.matlab', 'Matlab session', ('matlabsession',), (), ()), + 'MaximaLexer': ('pip._vendor.pygments.lexers.maxima', 'Maxima', ('maxima', 'macsyma'), ('*.mac', '*.max'), ()), + 'MesonLexer': ('pip._vendor.pygments.lexers.meson', 'Meson', ('meson', 'meson.build'), ('meson.build', 'meson.options', 'meson_options.txt'), ('text/x-meson',)), + 'MiniDLexer': ('pip._vendor.pygments.lexers.d', 'MiniD', ('minid',), (), ('text/x-minidsrc',)), + 'MiniScriptLexer': ('pip._vendor.pygments.lexers.scripting', 'MiniScript', ('miniscript', 'ms'), ('*.ms',), ('text/x-minicript', 'application/x-miniscript')), + 'ModelicaLexer': ('pip._vendor.pygments.lexers.modeling', 'Modelica', ('modelica',), ('*.mo',), ('text/x-modelica',)), + 'Modula2Lexer': ('pip._vendor.pygments.lexers.modula2', 'Modula-2', ('modula2', 'm2'), ('*.def', '*.mod'), ('text/x-modula2',)), + 'MoinWikiLexer': ('pip._vendor.pygments.lexers.markup', 'MoinMoin/Trac Wiki markup', ('trac-wiki', 'moin'), (), ('text/x-trac-wiki',)), + 'MojoLexer': ('pip._vendor.pygments.lexers.mojo', 'Mojo', ('mojo', '🔥'), ('*.mojo', '*.🔥'), ('text/x-mojo', 'application/x-mojo')), + 'MonkeyLexer': ('pip._vendor.pygments.lexers.basic', 'Monkey', ('monkey',), ('*.monkey',), ('text/x-monkey',)), + 'MonteLexer': ('pip._vendor.pygments.lexers.monte', 'Monte', ('monte',), ('*.mt',), ()), + 'MoonScriptLexer': ('pip._vendor.pygments.lexers.scripting', 'MoonScript', ('moonscript', 'moon'), ('*.moon',), ('text/x-moonscript', 'application/x-moonscript')), + 'MoselLexer': ('pip._vendor.pygments.lexers.mosel', 'Mosel', ('mosel',), ('*.mos',), ()), + 'MozPreprocCssLexer': ('pip._vendor.pygments.lexers.markup', 'CSS+mozpreproc', ('css+mozpreproc',), ('*.css.in',), ()), + 'MozPreprocHashLexer': ('pip._vendor.pygments.lexers.markup', 'mozhashpreproc', ('mozhashpreproc',), (), ()), + 'MozPreprocJavascriptLexer': ('pip._vendor.pygments.lexers.markup', 'Javascript+mozpreproc', ('javascript+mozpreproc',), ('*.js.in',), ()), + 'MozPreprocPercentLexer': ('pip._vendor.pygments.lexers.markup', 'mozpercentpreproc', ('mozpercentpreproc',), (), ()), + 'MozPreprocXulLexer': ('pip._vendor.pygments.lexers.markup', 'XUL+mozpreproc', ('xul+mozpreproc',), ('*.xul.in',), ()), + 'MqlLexer': ('pip._vendor.pygments.lexers.c_like', 'MQL', ('mql', 'mq4', 'mq5', 'mql4', 'mql5'), ('*.mq4', '*.mq5', '*.mqh'), ('text/x-mql',)), + 'MscgenLexer': ('pip._vendor.pygments.lexers.dsls', 'Mscgen', ('mscgen', 'msc'), ('*.msc',), ()), + 'MuPADLexer': ('pip._vendor.pygments.lexers.algebra', 'MuPAD', ('mupad',), ('*.mu',), ()), + 'MxmlLexer': ('pip._vendor.pygments.lexers.actionscript', 'MXML', ('mxml',), ('*.mxml',), ()), + 'MySqlLexer': ('pip._vendor.pygments.lexers.sql', 'MySQL', ('mysql',), (), ('text/x-mysql',)), + 'MyghtyCssLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Myghty', ('css+myghty',), (), ('text/css+myghty',)), + 'MyghtyHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Myghty', ('html+myghty',), (), ('text/html+myghty',)), + 'MyghtyJavascriptLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Myghty', ('javascript+myghty', 'js+myghty'), (), ('application/x-javascript+myghty', 'text/x-javascript+myghty', 'text/javascript+mygthy')), + 'MyghtyLexer': ('pip._vendor.pygments.lexers.templates', 'Myghty', ('myghty',), ('*.myt', 'autodelegate'), ('application/x-myghty',)), + 'MyghtyXmlLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Myghty', ('xml+myghty',), (), ('application/xml+myghty',)), + 'NCLLexer': ('pip._vendor.pygments.lexers.ncl', 'NCL', ('ncl',), ('*.ncl',), ('text/ncl',)), + 'NSISLexer': ('pip._vendor.pygments.lexers.installers', 'NSIS', ('nsis', 'nsi', 'nsh'), ('*.nsi', '*.nsh'), ('text/x-nsis',)), + 'NasmLexer': ('pip._vendor.pygments.lexers.asm', 'NASM', ('nasm',), ('*.asm', '*.ASM', '*.nasm'), ('text/x-nasm',)), + 'NasmObjdumpLexer': ('pip._vendor.pygments.lexers.asm', 'objdump-nasm', ('objdump-nasm',), ('*.objdump-intel',), ('text/x-nasm-objdump',)), + 'NemerleLexer': ('pip._vendor.pygments.lexers.dotnet', 'Nemerle', ('nemerle',), ('*.n',), ('text/x-nemerle',)), + 'NesCLexer': ('pip._vendor.pygments.lexers.c_like', 'nesC', ('nesc',), ('*.nc',), ('text/x-nescsrc',)), + 'NestedTextLexer': ('pip._vendor.pygments.lexers.configs', 'NestedText', ('nestedtext', 'nt'), ('*.nt',), ()), + 'NewLispLexer': ('pip._vendor.pygments.lexers.lisp', 'NewLisp', ('newlisp',), ('*.lsp', '*.nl', '*.kif'), ('text/x-newlisp', 'application/x-newlisp')), + 'NewspeakLexer': ('pip._vendor.pygments.lexers.smalltalk', 'Newspeak', ('newspeak',), ('*.ns2',), ('text/x-newspeak',)), + 'NginxConfLexer': ('pip._vendor.pygments.lexers.configs', 'Nginx configuration file', ('nginx',), ('nginx.conf',), ('text/x-nginx-conf',)), + 'NimrodLexer': ('pip._vendor.pygments.lexers.nimrod', 'Nimrod', ('nimrod', 'nim'), ('*.nim', '*.nimrod'), ('text/x-nim',)), + 'NitLexer': ('pip._vendor.pygments.lexers.nit', 'Nit', ('nit',), ('*.nit',), ()), + 'NixLexer': ('pip._vendor.pygments.lexers.nix', 'Nix', ('nixos', 'nix'), ('*.nix',), ('text/x-nix',)), + 'NodeConsoleLexer': ('pip._vendor.pygments.lexers.javascript', 'Node.js REPL console session', ('nodejsrepl',), (), ('text/x-nodejsrepl',)), + 'NotmuchLexer': ('pip._vendor.pygments.lexers.textfmts', 'Notmuch', ('notmuch',), (), ()), + 'NuSMVLexer': ('pip._vendor.pygments.lexers.smv', 'NuSMV', ('nusmv',), ('*.smv',), ()), + 'NumPyLexer': ('pip._vendor.pygments.lexers.python', 'NumPy', ('numpy',), (), ()), + 'NumbaIRLexer': ('pip._vendor.pygments.lexers.numbair', 'Numba_IR', ('numba_ir', 'numbair'), ('*.numba_ir',), ('text/x-numba_ir', 'text/x-numbair')), + 'ObjdumpLexer': ('pip._vendor.pygments.lexers.asm', 'objdump', ('objdump',), ('*.objdump',), ('text/x-objdump',)), + 'ObjectiveCLexer': ('pip._vendor.pygments.lexers.objective', 'Objective-C', ('objective-c', 'objectivec', 'obj-c', 'objc'), ('*.m', '*.h'), ('text/x-objective-c',)), + 'ObjectiveCppLexer': ('pip._vendor.pygments.lexers.objective', 'Objective-C++', ('objective-c++', 'objectivec++', 'obj-c++', 'objc++'), ('*.mm', '*.hh'), ('text/x-objective-c++',)), + 'ObjectiveJLexer': ('pip._vendor.pygments.lexers.javascript', 'Objective-J', ('objective-j', 'objectivej', 'obj-j', 'objj'), ('*.j',), ('text/x-objective-j',)), + 'OcamlLexer': ('pip._vendor.pygments.lexers.ml', 'OCaml', ('ocaml',), ('*.ml', '*.mli', '*.mll', '*.mly'), ('text/x-ocaml',)), + 'OctaveLexer': ('pip._vendor.pygments.lexers.matlab', 'Octave', ('octave',), ('*.m',), ('text/octave',)), + 'OdinLexer': ('pip._vendor.pygments.lexers.archetype', 'ODIN', ('odin',), ('*.odin',), ('text/odin',)), + 'OmgIdlLexer': ('pip._vendor.pygments.lexers.c_like', 'OMG Interface Definition Language', ('omg-idl',), ('*.idl', '*.pidl'), ()), + 'OocLexer': ('pip._vendor.pygments.lexers.ooc', 'Ooc', ('ooc',), ('*.ooc',), ('text/x-ooc',)), + 'OpaLexer': ('pip._vendor.pygments.lexers.ml', 'Opa', ('opa',), ('*.opa',), ('text/x-opa',)), + 'OpenEdgeLexer': ('pip._vendor.pygments.lexers.business', 'OpenEdge ABL', ('openedge', 'abl', 'progress'), ('*.p', '*.cls'), ('text/x-openedge', 'application/x-openedge')), + 'OpenScadLexer': ('pip._vendor.pygments.lexers.openscad', 'OpenSCAD', ('openscad',), ('*.scad',), ('application/x-openscad',)), + 'OrgLexer': ('pip._vendor.pygments.lexers.markup', 'Org Mode', ('org', 'orgmode', 'org-mode'), ('*.org',), ('text/org',)), + 'OutputLexer': ('pip._vendor.pygments.lexers.special', 'Text output', ('output',), (), ()), + 'PacmanConfLexer': ('pip._vendor.pygments.lexers.configs', 'PacmanConf', ('pacmanconf',), ('pacman.conf',), ()), + 'PanLexer': ('pip._vendor.pygments.lexers.dsls', 'Pan', ('pan',), ('*.pan',), ()), + 'ParaSailLexer': ('pip._vendor.pygments.lexers.parasail', 'ParaSail', ('parasail',), ('*.psi', '*.psl'), ('text/x-parasail',)), + 'PawnLexer': ('pip._vendor.pygments.lexers.pawn', 'Pawn', ('pawn',), ('*.p', '*.pwn', '*.inc'), ('text/x-pawn',)), + 'PddlLexer': ('pip._vendor.pygments.lexers.pddl', 'PDDL', ('pddl',), ('*.pddl',), ()), + 'PegLexer': ('pip._vendor.pygments.lexers.grammar_notation', 'PEG', ('peg',), ('*.peg',), ('text/x-peg',)), + 'Perl6Lexer': ('pip._vendor.pygments.lexers.perl', 'Perl6', ('perl6', 'pl6', 'raku'), ('*.pl', '*.pm', '*.nqp', '*.p6', '*.6pl', '*.p6l', '*.pl6', '*.6pm', '*.p6m', '*.pm6', '*.t', '*.raku', '*.rakumod', '*.rakutest', '*.rakudoc'), ('text/x-perl6', 'application/x-perl6')), + 'PerlLexer': ('pip._vendor.pygments.lexers.perl', 'Perl', ('perl', 'pl'), ('*.pl', '*.pm', '*.t', '*.perl'), ('text/x-perl', 'application/x-perl')), + 'PhixLexer': ('pip._vendor.pygments.lexers.phix', 'Phix', ('phix',), ('*.exw',), ('text/x-phix',)), + 'PhpLexer': ('pip._vendor.pygments.lexers.php', 'PHP', ('php', 'php3', 'php4', 'php5'), ('*.php', '*.php[345]', '*.inc'), ('text/x-php',)), + 'PigLexer': ('pip._vendor.pygments.lexers.jvm', 'Pig', ('pig',), ('*.pig',), ('text/x-pig',)), + 'PikeLexer': ('pip._vendor.pygments.lexers.c_like', 'Pike', ('pike',), ('*.pike', '*.pmod'), ('text/x-pike',)), + 'PkgConfigLexer': ('pip._vendor.pygments.lexers.configs', 'PkgConfig', ('pkgconfig',), ('*.pc',), ()), + 'PlPgsqlLexer': ('pip._vendor.pygments.lexers.sql', 'PL/pgSQL', ('plpgsql',), (), ('text/x-plpgsql',)), + 'PointlessLexer': ('pip._vendor.pygments.lexers.pointless', 'Pointless', ('pointless',), ('*.ptls',), ()), + 'PonyLexer': ('pip._vendor.pygments.lexers.pony', 'Pony', ('pony',), ('*.pony',), ()), + 'PortugolLexer': ('pip._vendor.pygments.lexers.pascal', 'Portugol', ('portugol',), ('*.alg', '*.portugol'), ()), + 'PostScriptLexer': ('pip._vendor.pygments.lexers.graphics', 'PostScript', ('postscript', 'postscr'), ('*.ps', '*.eps'), ('application/postscript',)), + 'PostgresConsoleLexer': ('pip._vendor.pygments.lexers.sql', 'PostgreSQL console (psql)', ('psql', 'postgresql-console', 'postgres-console'), (), ('text/x-postgresql-psql',)), + 'PostgresExplainLexer': ('pip._vendor.pygments.lexers.sql', 'PostgreSQL EXPLAIN dialect', ('postgres-explain',), ('*.explain',), ('text/x-postgresql-explain',)), + 'PostgresLexer': ('pip._vendor.pygments.lexers.sql', 'PostgreSQL SQL dialect', ('postgresql', 'postgres'), (), ('text/x-postgresql',)), + 'PovrayLexer': ('pip._vendor.pygments.lexers.graphics', 'POVRay', ('pov',), ('*.pov', '*.inc'), ('text/x-povray',)), + 'PowerShellLexer': ('pip._vendor.pygments.lexers.shell', 'PowerShell', ('powershell', 'pwsh', 'posh', 'ps1', 'psm1'), ('*.ps1', '*.psm1'), ('text/x-powershell',)), + 'PowerShellSessionLexer': ('pip._vendor.pygments.lexers.shell', 'PowerShell Session', ('pwsh-session', 'ps1con'), (), ()), + 'PraatLexer': ('pip._vendor.pygments.lexers.praat', 'Praat', ('praat',), ('*.praat', '*.proc', '*.psc'), ()), + 'ProcfileLexer': ('pip._vendor.pygments.lexers.procfile', 'Procfile', ('procfile',), ('Procfile',), ()), + 'PrologLexer': ('pip._vendor.pygments.lexers.prolog', 'Prolog', ('prolog',), ('*.ecl', '*.prolog', '*.pro', '*.pl'), ('text/x-prolog',)), + 'PromQLLexer': ('pip._vendor.pygments.lexers.promql', 'PromQL', ('promql',), ('*.promql',), ()), + 'PromelaLexer': ('pip._vendor.pygments.lexers.c_like', 'Promela', ('promela',), ('*.pml', '*.prom', '*.prm', '*.promela', '*.pr', '*.pm'), ('text/x-promela',)), + 'PropertiesLexer': ('pip._vendor.pygments.lexers.configs', 'Properties', ('properties', 'jproperties'), ('*.properties',), ('text/x-java-properties',)), + 'ProtoBufLexer': ('pip._vendor.pygments.lexers.dsls', 'Protocol Buffer', ('protobuf', 'proto'), ('*.proto',), ()), + 'PrqlLexer': ('pip._vendor.pygments.lexers.prql', 'PRQL', ('prql',), ('*.prql',), ('application/prql', 'application/x-prql')), + 'PsyshConsoleLexer': ('pip._vendor.pygments.lexers.php', 'PsySH console session for PHP', ('psysh',), (), ()), + 'PtxLexer': ('pip._vendor.pygments.lexers.ptx', 'PTX', ('ptx',), ('*.ptx',), ('text/x-ptx',)), + 'PugLexer': ('pip._vendor.pygments.lexers.html', 'Pug', ('pug', 'jade'), ('*.pug', '*.jade'), ('text/x-pug', 'text/x-jade')), + 'PuppetLexer': ('pip._vendor.pygments.lexers.dsls', 'Puppet', ('puppet',), ('*.pp',), ()), + 'PyPyLogLexer': ('pip._vendor.pygments.lexers.console', 'PyPy Log', ('pypylog', 'pypy'), ('*.pypylog',), ('application/x-pypylog',)), + 'Python2Lexer': ('pip._vendor.pygments.lexers.python', 'Python 2.x', ('python2', 'py2'), (), ('text/x-python2', 'application/x-python2')), + 'Python2TracebackLexer': ('pip._vendor.pygments.lexers.python', 'Python 2.x Traceback', ('py2tb',), ('*.py2tb',), ('text/x-python2-traceback',)), + 'PythonConsoleLexer': ('pip._vendor.pygments.lexers.python', 'Python console session', ('pycon', 'python-console'), (), ('text/x-python-doctest',)), + 'PythonLexer': ('pip._vendor.pygments.lexers.python', 'Python', ('python', 'py', 'sage', 'python3', 'py3', 'bazel', 'starlark', 'pyi'), ('*.py', '*.pyw', '*.pyi', '*.jy', '*.sage', '*.sc', 'SConstruct', 'SConscript', '*.bzl', 'BUCK', 'BUILD', 'BUILD.bazel', 'WORKSPACE', '*.tac', '*.pye'), ('text/x-python', 'application/x-python', 'text/x-python3', 'application/x-python3')), + 'PythonTracebackLexer': ('pip._vendor.pygments.lexers.python', 'Python Traceback', ('pytb', 'py3tb'), ('*.pytb', '*.py3tb'), ('text/x-python-traceback', 'text/x-python3-traceback')), + 'PythonUL4Lexer': ('pip._vendor.pygments.lexers.ul4', 'Python+UL4', ('py+ul4',), ('*.pyul4',), ()), + 'QBasicLexer': ('pip._vendor.pygments.lexers.basic', 'QBasic', ('qbasic', 'basic'), ('*.BAS', '*.bas'), ('text/basic',)), + 'QLexer': ('pip._vendor.pygments.lexers.q', 'Q', ('q',), ('*.q',), ()), + 'QVToLexer': ('pip._vendor.pygments.lexers.qvt', 'QVTO', ('qvto', 'qvt'), ('*.qvto',), ()), + 'QlikLexer': ('pip._vendor.pygments.lexers.qlik', 'Qlik', ('qlik', 'qlikview', 'qliksense', 'qlikscript'), ('*.qvs', '*.qvw'), ()), + 'QmlLexer': ('pip._vendor.pygments.lexers.webmisc', 'QML', ('qml', 'qbs'), ('*.qml', '*.qbs'), ('application/x-qml', 'application/x-qt.qbs+qml')), + 'RConsoleLexer': ('pip._vendor.pygments.lexers.r', 'RConsole', ('rconsole', 'rout'), ('*.Rout',), ()), + 'RNCCompactLexer': ('pip._vendor.pygments.lexers.rnc', 'Relax-NG Compact', ('rng-compact', 'rnc'), ('*.rnc',), ()), + 'RPMSpecLexer': ('pip._vendor.pygments.lexers.installers', 'RPMSpec', ('spec',), ('*.spec',), ('text/x-rpm-spec',)), + 'RacketLexer': ('pip._vendor.pygments.lexers.lisp', 'Racket', ('racket', 'rkt'), ('*.rkt', '*.rktd', '*.rktl'), ('text/x-racket', 'application/x-racket')), + 'RagelCLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel in C Host', ('ragel-c',), ('*.rl',), ()), + 'RagelCppLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel in CPP Host', ('ragel-cpp',), ('*.rl',), ()), + 'RagelDLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel in D Host', ('ragel-d',), ('*.rl',), ()), + 'RagelEmbeddedLexer': ('pip._vendor.pygments.lexers.parsers', 'Embedded Ragel', ('ragel-em',), ('*.rl',), ()), + 'RagelJavaLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel in Java Host', ('ragel-java',), ('*.rl',), ()), + 'RagelLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel', ('ragel',), (), ()), + 'RagelObjectiveCLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel in Objective C Host', ('ragel-objc',), ('*.rl',), ()), + 'RagelRubyLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel in Ruby Host', ('ragel-ruby', 'ragel-rb'), ('*.rl',), ()), + 'RawTokenLexer': ('pip._vendor.pygments.lexers.special', 'Raw token data', (), (), ('application/x-pygments-tokens',)), + 'RdLexer': ('pip._vendor.pygments.lexers.r', 'Rd', ('rd',), ('*.Rd',), ('text/x-r-doc',)), + 'ReasonLexer': ('pip._vendor.pygments.lexers.ml', 'ReasonML', ('reasonml', 'reason'), ('*.re', '*.rei'), ('text/x-reasonml',)), + 'RebolLexer': ('pip._vendor.pygments.lexers.rebol', 'REBOL', ('rebol',), ('*.r', '*.r3', '*.reb'), ('text/x-rebol',)), + 'RedLexer': ('pip._vendor.pygments.lexers.rebol', 'Red', ('red', 'red/system'), ('*.red', '*.reds'), ('text/x-red', 'text/x-red-system')), + 'RedcodeLexer': ('pip._vendor.pygments.lexers.esoteric', 'Redcode', ('redcode',), ('*.cw',), ()), + 'RegeditLexer': ('pip._vendor.pygments.lexers.configs', 'reg', ('registry',), ('*.reg',), ('text/x-windows-registry',)), + 'RegoLexer': ('pip._vendor.pygments.lexers.rego', 'Rego', ('rego',), ('*.rego',), ('text/x-rego',)), + 'RellLexer': ('pip._vendor.pygments.lexers.rell', 'Rell', ('rell',), ('*.rell',), ('text/x-rell',)), + 'ResourceLexer': ('pip._vendor.pygments.lexers.resource', 'ResourceBundle', ('resourcebundle', 'resource'), (), ()), + 'RexxLexer': ('pip._vendor.pygments.lexers.scripting', 'Rexx', ('rexx', 'arexx'), ('*.rexx', '*.rex', '*.rx', '*.arexx'), ('text/x-rexx',)), + 'RhtmlLexer': ('pip._vendor.pygments.lexers.templates', 'RHTML', ('rhtml', 'html+erb', 'html+ruby'), ('*.rhtml',), ('text/html+ruby',)), + 'RideLexer': ('pip._vendor.pygments.lexers.ride', 'Ride', ('ride',), ('*.ride',), ('text/x-ride',)), + 'RitaLexer': ('pip._vendor.pygments.lexers.rita', 'Rita', ('rita',), ('*.rita',), ('text/rita',)), + 'RoboconfGraphLexer': ('pip._vendor.pygments.lexers.roboconf', 'Roboconf Graph', ('roboconf-graph',), ('*.graph',), ()), + 'RoboconfInstancesLexer': ('pip._vendor.pygments.lexers.roboconf', 'Roboconf Instances', ('roboconf-instances',), ('*.instances',), ()), + 'RobotFrameworkLexer': ('pip._vendor.pygments.lexers.robotframework', 'RobotFramework', ('robotframework',), ('*.robot', '*.resource'), ('text/x-robotframework',)), + 'RocqLexer': ('pip._vendor.pygments.lexers.theorem', 'Rocq Prover', ('coq', 'rocq', 'rocq-prover'), ('*.v',), ('text/x-coq', 'text/x-rocq')), + 'RqlLexer': ('pip._vendor.pygments.lexers.sql', 'RQL', ('rql',), ('*.rql',), ('text/x-rql',)), + 'RslLexer': ('pip._vendor.pygments.lexers.dsls', 'RSL', ('rsl',), ('*.rsl',), ('text/rsl',)), + 'RstLexer': ('pip._vendor.pygments.lexers.markup', 'reStructuredText', ('restructuredtext', 'rst', 'rest'), ('*.rst', '*.rest'), ('text/x-rst', 'text/prs.fallenstein.rst')), + 'RtsLexer': ('pip._vendor.pygments.lexers.trafficscript', 'TrafficScript', ('trafficscript', 'rts'), ('*.rts',), ()), + 'RubyConsoleLexer': ('pip._vendor.pygments.lexers.ruby', 'Ruby irb session', ('rbcon', 'irb'), (), ('text/x-ruby-shellsession',)), + 'RubyLexer': ('pip._vendor.pygments.lexers.ruby', 'Ruby', ('ruby', 'rb', 'duby'), ('*.rb', '*.rbw', 'Rakefile', '*.rake', '*.gemspec', '*.rbx', '*.duby', 'Gemfile', 'Vagrantfile'), ('text/x-ruby', 'application/x-ruby')), + 'RustLexer': ('pip._vendor.pygments.lexers.rust', 'Rust', ('rust', 'rs'), ('*.rs', '*.rs.in'), ('text/rust', 'text/x-rust')), + 'SASLexer': ('pip._vendor.pygments.lexers.sas', 'SAS', ('sas',), ('*.SAS', '*.sas'), ('text/x-sas', 'text/sas', 'application/x-sas')), + 'SLexer': ('pip._vendor.pygments.lexers.r', 'S', ('splus', 's', 'r'), ('*.S', '*.R', '.Rhistory', '.Rprofile', '.Renviron'), ('text/S-plus', 'text/S', 'text/x-r-source', 'text/x-r', 'text/x-R', 'text/x-r-history', 'text/x-r-profile')), + 'SMLLexer': ('pip._vendor.pygments.lexers.ml', 'Standard ML', ('sml',), ('*.sml', '*.sig', '*.fun'), ('text/x-standardml', 'application/x-standardml')), + 'SNBTLexer': ('pip._vendor.pygments.lexers.minecraft', 'SNBT', ('snbt',), ('*.snbt',), ('text/snbt',)), + 'SarlLexer': ('pip._vendor.pygments.lexers.jvm', 'SARL', ('sarl',), ('*.sarl',), ('text/x-sarl',)), + 'SassLexer': ('pip._vendor.pygments.lexers.css', 'Sass', ('sass',), ('*.sass',), ('text/x-sass',)), + 'SaviLexer': ('pip._vendor.pygments.lexers.savi', 'Savi', ('savi',), ('*.savi',), ()), + 'ScalaLexer': ('pip._vendor.pygments.lexers.jvm', 'Scala', ('scala',), ('*.scala',), ('text/x-scala',)), + 'ScamlLexer': ('pip._vendor.pygments.lexers.html', 'Scaml', ('scaml',), ('*.scaml',), ('text/x-scaml',)), + 'ScdocLexer': ('pip._vendor.pygments.lexers.scdoc', 'scdoc', ('scdoc', 'scd'), ('*.scd', '*.scdoc'), ()), + 'SchemeLexer': ('pip._vendor.pygments.lexers.lisp', 'Scheme', ('scheme', 'scm'), ('*.scm', '*.ss'), ('text/x-scheme', 'application/x-scheme')), + 'ScilabLexer': ('pip._vendor.pygments.lexers.matlab', 'Scilab', ('scilab',), ('*.sci', '*.sce', '*.tst'), ('text/scilab',)), + 'ScssLexer': ('pip._vendor.pygments.lexers.css', 'SCSS', ('scss',), ('*.scss',), ('text/x-scss',)), + 'SedLexer': ('pip._vendor.pygments.lexers.textedit', 'Sed', ('sed', 'gsed', 'ssed'), ('*.sed', '*.[gs]sed'), ('text/x-sed',)), + 'ShExCLexer': ('pip._vendor.pygments.lexers.rdf', 'ShExC', ('shexc', 'shex'), ('*.shex',), ('text/shex',)), + 'ShenLexer': ('pip._vendor.pygments.lexers.lisp', 'Shen', ('shen',), ('*.shen',), ('text/x-shen', 'application/x-shen')), + 'SieveLexer': ('pip._vendor.pygments.lexers.sieve', 'Sieve', ('sieve',), ('*.siv', '*.sieve'), ()), + 'SilverLexer': ('pip._vendor.pygments.lexers.verification', 'Silver', ('silver',), ('*.sil', '*.vpr'), ()), + 'SingularityLexer': ('pip._vendor.pygments.lexers.configs', 'Singularity', ('singularity',), ('*.def', 'Singularity'), ()), + 'SlashLexer': ('pip._vendor.pygments.lexers.slash', 'Slash', ('slash',), ('*.sla',), ()), + 'SlimLexer': ('pip._vendor.pygments.lexers.webmisc', 'Slim', ('slim',), ('*.slim',), ('text/x-slim',)), + 'SlurmBashLexer': ('pip._vendor.pygments.lexers.shell', 'Slurm', ('slurm', 'sbatch'), ('*.sl',), ()), + 'SmaliLexer': ('pip._vendor.pygments.lexers.dalvik', 'Smali', ('smali',), ('*.smali',), ('text/smali',)), + 'SmalltalkLexer': ('pip._vendor.pygments.lexers.smalltalk', 'Smalltalk', ('smalltalk', 'squeak', 'st'), ('*.st',), ('text/x-smalltalk',)), + 'SmartGameFormatLexer': ('pip._vendor.pygments.lexers.sgf', 'SmartGameFormat', ('sgf',), ('*.sgf',), ()), + 'SmartyLexer': ('pip._vendor.pygments.lexers.templates', 'Smarty', ('smarty',), ('*.tpl',), ('application/x-smarty',)), + 'SmithyLexer': ('pip._vendor.pygments.lexers.smithy', 'Smithy', ('smithy',), ('*.smithy',), ()), + 'SnobolLexer': ('pip._vendor.pygments.lexers.snobol', 'Snobol', ('snobol',), ('*.snobol',), ('text/x-snobol',)), + 'SnowballLexer': ('pip._vendor.pygments.lexers.dsls', 'Snowball', ('snowball',), ('*.sbl',), ()), + 'SolidityLexer': ('pip._vendor.pygments.lexers.solidity', 'Solidity', ('solidity',), ('*.sol',), ()), + 'SoongLexer': ('pip._vendor.pygments.lexers.soong', 'Soong', ('androidbp', 'bp', 'soong'), ('Android.bp',), ()), + 'SophiaLexer': ('pip._vendor.pygments.lexers.sophia', 'Sophia', ('sophia',), ('*.aes',), ()), + 'SourcePawnLexer': ('pip._vendor.pygments.lexers.pawn', 'SourcePawn', ('sp',), ('*.sp',), ('text/x-sourcepawn',)), + 'SourcesListLexer': ('pip._vendor.pygments.lexers.installers', 'Debian Sourcelist', ('debsources', 'sourceslist', 'sources.list'), ('sources.list',), ()), + 'SparqlLexer': ('pip._vendor.pygments.lexers.rdf', 'SPARQL', ('sparql',), ('*.rq', '*.sparql'), ('application/sparql-query',)), + 'SpiceLexer': ('pip._vendor.pygments.lexers.spice', 'Spice', ('spice', 'spicelang'), ('*.spice',), ('text/x-spice',)), + 'SqlJinjaLexer': ('pip._vendor.pygments.lexers.templates', 'SQL+Jinja', ('sql+jinja',), ('*.sql', '*.sql.j2', '*.sql.jinja2'), ()), + 'SqlLexer': ('pip._vendor.pygments.lexers.sql', 'SQL', ('sql',), ('*.sql',), ('text/x-sql',)), + 'SqliteConsoleLexer': ('pip._vendor.pygments.lexers.sql', 'sqlite3con', ('sqlite3',), ('*.sqlite3-console',), ('text/x-sqlite3-console',)), + 'SquidConfLexer': ('pip._vendor.pygments.lexers.configs', 'SquidConf', ('squidconf', 'squid.conf', 'squid'), ('squid.conf',), ('text/x-squidconf',)), + 'SrcinfoLexer': ('pip._vendor.pygments.lexers.srcinfo', 'Srcinfo', ('srcinfo',), ('.SRCINFO',), ()), + 'SspLexer': ('pip._vendor.pygments.lexers.templates', 'Scalate Server Page', ('ssp',), ('*.ssp',), ('application/x-ssp',)), + 'StanLexer': ('pip._vendor.pygments.lexers.modeling', 'Stan', ('stan',), ('*.stan',), ()), + 'StataLexer': ('pip._vendor.pygments.lexers.stata', 'Stata', ('stata', 'do'), ('*.do', '*.ado'), ('text/x-stata', 'text/stata', 'application/x-stata')), + 'SuperColliderLexer': ('pip._vendor.pygments.lexers.supercollider', 'SuperCollider', ('supercollider', 'sc'), ('*.sc', '*.scd'), ('application/supercollider', 'text/supercollider')), + 'SwiftLexer': ('pip._vendor.pygments.lexers.objective', 'Swift', ('swift',), ('*.swift',), ('text/x-swift',)), + 'SwigLexer': ('pip._vendor.pygments.lexers.c_like', 'SWIG', ('swig',), ('*.swg', '*.i'), ('text/swig',)), + 'SystemVerilogLexer': ('pip._vendor.pygments.lexers.hdl', 'systemverilog', ('systemverilog', 'sv'), ('*.sv', '*.svh'), ('text/x-systemverilog',)), + 'SystemdLexer': ('pip._vendor.pygments.lexers.configs', 'Systemd', ('systemd',), ('*.service', '*.socket', '*.device', '*.mount', '*.automount', '*.swap', '*.target', '*.path', '*.timer', '*.slice', '*.scope'), ()), + 'TAPLexer': ('pip._vendor.pygments.lexers.testing', 'TAP', ('tap',), ('*.tap',), ()), + 'TNTLexer': ('pip._vendor.pygments.lexers.tnt', 'Typographic Number Theory', ('tnt',), ('*.tnt',), ()), + 'TOMLLexer': ('pip._vendor.pygments.lexers.configs', 'TOML', ('toml',), ('*.toml', 'Pipfile', 'poetry.lock'), ('application/toml',)), + 'TableGenLexer': ('pip._vendor.pygments.lexers.tablegen', 'TableGen', ('tablegen', 'td'), ('*.td',), ()), + 'TactLexer': ('pip._vendor.pygments.lexers.tact', 'Tact', ('tact',), ('*.tact',), ()), + 'Tads3Lexer': ('pip._vendor.pygments.lexers.int_fiction', 'TADS 3', ('tads3',), ('*.t',), ()), + 'TalLexer': ('pip._vendor.pygments.lexers.tal', 'Tal', ('tal', 'uxntal'), ('*.tal',), ('text/x-uxntal',)), + 'TasmLexer': ('pip._vendor.pygments.lexers.asm', 'TASM', ('tasm',), ('*.asm', '*.ASM', '*.tasm'), ('text/x-tasm',)), + 'TclLexer': ('pip._vendor.pygments.lexers.tcl', 'Tcl', ('tcl',), ('*.tcl', '*.rvt'), ('text/x-tcl', 'text/x-script.tcl', 'application/x-tcl')), + 'TcshLexer': ('pip._vendor.pygments.lexers.shell', 'Tcsh', ('tcsh', 'csh'), ('*.tcsh', '*.csh'), ('application/x-csh',)), + 'TcshSessionLexer': ('pip._vendor.pygments.lexers.shell', 'Tcsh Session', ('tcshcon',), (), ()), + 'TeaTemplateLexer': ('pip._vendor.pygments.lexers.templates', 'Tea', ('tea',), ('*.tea',), ('text/x-tea',)), + 'TealLexer': ('pip._vendor.pygments.lexers.teal', 'teal', ('teal',), ('*.teal',), ()), + 'TeraTermLexer': ('pip._vendor.pygments.lexers.teraterm', 'Tera Term macro', ('teratermmacro', 'teraterm', 'ttl'), ('*.ttl',), ('text/x-teratermmacro',)), + 'TermcapLexer': ('pip._vendor.pygments.lexers.configs', 'Termcap', ('termcap',), ('termcap', 'termcap.src'), ()), + 'TerminfoLexer': ('pip._vendor.pygments.lexers.configs', 'Terminfo', ('terminfo',), ('terminfo', 'terminfo.src'), ()), + 'TerraformLexer': ('pip._vendor.pygments.lexers.configs', 'Terraform', ('terraform', 'tf', 'hcl'), ('*.tf', '*.hcl'), ('application/x-tf', 'application/x-terraform')), + 'TexLexer': ('pip._vendor.pygments.lexers.markup', 'TeX', ('tex', 'latex'), ('*.tex', '*.aux', '*.toc'), ('text/x-tex', 'text/x-latex')), + 'TextLexer': ('pip._vendor.pygments.lexers.special', 'Text only', ('text',), ('*.txt',), ('text/plain',)), + 'ThingsDBLexer': ('pip._vendor.pygments.lexers.thingsdb', 'ThingsDB', ('ti', 'thingsdb'), ('*.ti',), ()), + 'ThriftLexer': ('pip._vendor.pygments.lexers.dsls', 'Thrift', ('thrift',), ('*.thrift',), ('application/x-thrift',)), + 'TiddlyWiki5Lexer': ('pip._vendor.pygments.lexers.markup', 'tiddler', ('tid',), ('*.tid',), ('text/vnd.tiddlywiki',)), + 'TlbLexer': ('pip._vendor.pygments.lexers.tlb', 'Tl-b', ('tlb',), ('*.tlb',), ()), + 'TlsLexer': ('pip._vendor.pygments.lexers.tls', 'TLS Presentation Language', ('tls',), (), ()), + 'TodotxtLexer': ('pip._vendor.pygments.lexers.textfmts', 'Todotxt', ('todotxt',), ('todo.txt', '*.todotxt'), ('text/x-todo',)), + 'TransactSqlLexer': ('pip._vendor.pygments.lexers.sql', 'Transact-SQL', ('tsql', 't-sql'), ('*.sql',), ('text/x-tsql',)), + 'TreetopLexer': ('pip._vendor.pygments.lexers.parsers', 'Treetop', ('treetop',), ('*.treetop', '*.tt'), ()), + 'TsxLexer': ('pip._vendor.pygments.lexers.jsx', 'TSX', ('tsx',), ('*.tsx',), ('text/typescript-tsx',)), + 'TurtleLexer': ('pip._vendor.pygments.lexers.rdf', 'Turtle', ('turtle',), ('*.ttl',), ('text/turtle', 'application/x-turtle')), + 'TwigHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Twig', ('html+twig',), ('*.twig',), ('text/html+twig',)), + 'TwigLexer': ('pip._vendor.pygments.lexers.templates', 'Twig', ('twig',), (), ('application/x-twig',)), + 'TypeScriptLexer': ('pip._vendor.pygments.lexers.javascript', 'TypeScript', ('typescript', 'ts'), ('*.ts',), ('application/x-typescript', 'text/x-typescript')), + 'TypoScriptCssDataLexer': ('pip._vendor.pygments.lexers.typoscript', 'TypoScriptCssData', ('typoscriptcssdata',), (), ()), + 'TypoScriptHtmlDataLexer': ('pip._vendor.pygments.lexers.typoscript', 'TypoScriptHtmlData', ('typoscripthtmldata',), (), ()), + 'TypoScriptLexer': ('pip._vendor.pygments.lexers.typoscript', 'TypoScript', ('typoscript',), ('*.typoscript',), ('text/x-typoscript',)), + 'TypstLexer': ('pip._vendor.pygments.lexers.typst', 'Typst', ('typst',), ('*.typ',), ('text/x-typst',)), + 'UL4Lexer': ('pip._vendor.pygments.lexers.ul4', 'UL4', ('ul4',), ('*.ul4',), ()), + 'UcodeLexer': ('pip._vendor.pygments.lexers.unicon', 'ucode', ('ucode',), ('*.u', '*.u1', '*.u2'), ()), + 'UniconLexer': ('pip._vendor.pygments.lexers.unicon', 'Unicon', ('unicon',), ('*.icn',), ('text/unicon',)), + 'UnixConfigLexer': ('pip._vendor.pygments.lexers.configs', 'Unix/Linux config files', ('unixconfig', 'linuxconfig'), (), ()), + 'UrbiscriptLexer': ('pip._vendor.pygments.lexers.urbi', 'UrbiScript', ('urbiscript',), ('*.u',), ('application/x-urbiscript',)), + 'UrlEncodedLexer': ('pip._vendor.pygments.lexers.html', 'urlencoded', ('urlencoded',), (), ('application/x-www-form-urlencoded',)), + 'UsdLexer': ('pip._vendor.pygments.lexers.usd', 'USD', ('usd', 'usda'), ('*.usd', '*.usda'), ()), + 'VBScriptLexer': ('pip._vendor.pygments.lexers.basic', 'VBScript', ('vbscript',), ('*.vbs', '*.VBS'), ()), + 'VCLLexer': ('pip._vendor.pygments.lexers.varnish', 'VCL', ('vcl',), ('*.vcl',), ('text/x-vclsrc',)), + 'VCLSnippetLexer': ('pip._vendor.pygments.lexers.varnish', 'VCLSnippets', ('vclsnippets', 'vclsnippet'), (), ('text/x-vclsnippet',)), + 'VCTreeStatusLexer': ('pip._vendor.pygments.lexers.console', 'VCTreeStatus', ('vctreestatus',), (), ()), + 'VGLLexer': ('pip._vendor.pygments.lexers.dsls', 'VGL', ('vgl',), ('*.rpf',), ()), + 'ValaLexer': ('pip._vendor.pygments.lexers.c_like', 'Vala', ('vala', 'vapi'), ('*.vala', '*.vapi'), ('text/x-vala',)), + 'VbNetAspxLexer': ('pip._vendor.pygments.lexers.dotnet', 'aspx-vb', ('aspx-vb',), ('*.aspx', '*.asax', '*.ascx', '*.ashx', '*.asmx', '*.axd'), ()), + 'VbNetLexer': ('pip._vendor.pygments.lexers.dotnet', 'VB.net', ('vb.net', 'vbnet', 'lobas', 'oobas', 'sobas', 'visual-basic', 'visualbasic'), ('*.vb', '*.bas'), ('text/x-vbnet', 'text/x-vba')), + 'VelocityHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Velocity', ('html+velocity',), (), ('text/html+velocity',)), + 'VelocityLexer': ('pip._vendor.pygments.lexers.templates', 'Velocity', ('velocity',), ('*.vm', '*.fhtml'), ()), + 'VelocityXmlLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Velocity', ('xml+velocity',), (), ('application/xml+velocity',)), + 'VerifpalLexer': ('pip._vendor.pygments.lexers.verifpal', 'Verifpal', ('verifpal',), ('*.vp',), ('text/x-verifpal',)), + 'VerilogLexer': ('pip._vendor.pygments.lexers.hdl', 'verilog', ('verilog', 'v'), ('*.v',), ('text/x-verilog',)), + 'VhdlLexer': ('pip._vendor.pygments.lexers.hdl', 'vhdl', ('vhdl',), ('*.vhdl', '*.vhd'), ('text/x-vhdl',)), + 'VimLexer': ('pip._vendor.pygments.lexers.textedit', 'VimL', ('vim',), ('*.vim', '.vimrc', '.exrc', '.gvimrc', '_vimrc', '_exrc', '_gvimrc', 'vimrc', 'gvimrc'), ('text/x-vim',)), + 'VisualPrologGrammarLexer': ('pip._vendor.pygments.lexers.vip', 'Visual Prolog Grammar', ('visualprologgrammar',), ('*.vipgrm',), ()), + 'VisualPrologLexer': ('pip._vendor.pygments.lexers.vip', 'Visual Prolog', ('visualprolog',), ('*.pro', '*.cl', '*.i', '*.pack', '*.ph'), ()), + 'VueLexer': ('pip._vendor.pygments.lexers.html', 'Vue', ('vue',), ('*.vue',), ()), + 'VyperLexer': ('pip._vendor.pygments.lexers.vyper', 'Vyper', ('vyper',), ('*.vy',), ()), + 'WDiffLexer': ('pip._vendor.pygments.lexers.diff', 'WDiff', ('wdiff',), ('*.wdiff',), ()), + 'WatLexer': ('pip._vendor.pygments.lexers.webassembly', 'WebAssembly', ('wast', 'wat'), ('*.wat', '*.wast'), ()), + 'WebIDLLexer': ('pip._vendor.pygments.lexers.webidl', 'Web IDL', ('webidl',), ('*.webidl',), ()), + 'WgslLexer': ('pip._vendor.pygments.lexers.wgsl', 'WebGPU Shading Language', ('wgsl',), ('*.wgsl',), ('text/wgsl',)), + 'WhileyLexer': ('pip._vendor.pygments.lexers.whiley', 'Whiley', ('whiley',), ('*.whiley',), ('text/x-whiley',)), + 'WikitextLexer': ('pip._vendor.pygments.lexers.markup', 'Wikitext', ('wikitext', 'mediawiki'), (), ('text/x-wiki',)), + 'WoWTocLexer': ('pip._vendor.pygments.lexers.wowtoc', 'World of Warcraft TOC', ('wowtoc',), ('*.toc',), ()), + 'WrenLexer': ('pip._vendor.pygments.lexers.wren', 'Wren', ('wren',), ('*.wren',), ()), + 'X10Lexer': ('pip._vendor.pygments.lexers.x10', 'X10', ('x10', 'xten'), ('*.x10',), ('text/x-x10',)), + 'XMLUL4Lexer': ('pip._vendor.pygments.lexers.ul4', 'XML+UL4', ('xml+ul4',), ('*.xmlul4',), ()), + 'XQueryLexer': ('pip._vendor.pygments.lexers.webmisc', 'XQuery', ('xquery', 'xqy', 'xq', 'xql', 'xqm'), ('*.xqy', '*.xquery', '*.xq', '*.xql', '*.xqm'), ('text/xquery', 'application/xquery')), + 'XmlDjangoLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Django/Jinja', ('xml+django', 'xml+jinja'), ('*.xml.j2', '*.xml.jinja2'), ('application/xml+django', 'application/xml+jinja')), + 'XmlErbLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Ruby', ('xml+ruby', 'xml+erb'), (), ('application/xml+ruby',)), + 'XmlLexer': ('pip._vendor.pygments.lexers.html', 'XML', ('xml',), ('*.xml', '*.xsl', '*.rss', '*.xslt', '*.xsd', '*.wsdl', '*.wsf', '*.xbrl', '*.pom'), ('text/xml', 'application/xml', 'image/svg+xml', 'application/rss+xml', 'application/atom+xml')), + 'XmlPhpLexer': ('pip._vendor.pygments.lexers.templates', 'XML+PHP', ('xml+php',), (), ('application/xml+php',)), + 'XmlSmartyLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Smarty', ('xml+smarty',), (), ('application/xml+smarty',)), + 'XorgLexer': ('pip._vendor.pygments.lexers.xorg', 'Xorg', ('xorg.conf',), ('xorg.conf',), ()), + 'XppLexer': ('pip._vendor.pygments.lexers.dotnet', 'X++', ('xpp', 'x++'), ('*.xpp',), ()), + 'XsltLexer': ('pip._vendor.pygments.lexers.html', 'XSLT', ('xslt',), ('*.xsl', '*.xslt', '*.xpl'), ('application/xsl+xml', 'application/xslt+xml')), + 'XtendLexer': ('pip._vendor.pygments.lexers.jvm', 'Xtend', ('xtend',), ('*.xtend',), ('text/x-xtend',)), + 'XtlangLexer': ('pip._vendor.pygments.lexers.lisp', 'xtlang', ('extempore',), ('*.xtm',), ()), + 'YamlJinjaLexer': ('pip._vendor.pygments.lexers.templates', 'YAML+Jinja', ('yaml+jinja', 'salt', 'sls'), ('*.sls', '*.yaml.j2', '*.yml.j2', '*.yaml.jinja2', '*.yml.jinja2'), ('text/x-yaml+jinja', 'text/x-sls')), + 'YamlLexer': ('pip._vendor.pygments.lexers.data', 'YAML', ('yaml',), ('*.yaml', '*.yml'), ('text/x-yaml',)), + 'YangLexer': ('pip._vendor.pygments.lexers.yang', 'YANG', ('yang',), ('*.yang',), ('application/yang',)), + 'YaraLexer': ('pip._vendor.pygments.lexers.yara', 'YARA', ('yara', 'yar'), ('*.yar',), ('text/x-yara',)), + 'ZeekLexer': ('pip._vendor.pygments.lexers.dsls', 'Zeek', ('zeek', 'bro'), ('*.zeek', '*.bro'), ()), + 'ZephirLexer': ('pip._vendor.pygments.lexers.php', 'Zephir', ('zephir',), ('*.zep',), ()), + 'ZigLexer': ('pip._vendor.pygments.lexers.zig', 'Zig', ('zig',), ('*.zig',), ('text/zig',)), + 'apdlexer': ('pip._vendor.pygments.lexers.apdlexer', 'ANSYS parametric design language', ('ansys', 'apdl'), ('*.ans',), ()), +} diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/python.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/python.py new file mode 100644 index 0000000..0ec67e5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/python.py @@ -0,0 +1,1204 @@ +""" + pygments.lexers.python + ~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for Python and related languages. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import keyword + +from pip._vendor.pygments.lexer import DelegatingLexer, RegexLexer, include, \ + bygroups, using, default, words, combined, this +from pip._vendor.pygments.util import get_bool_opt, shebang_matches +from pip._vendor.pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Generic, Other, Error, Whitespace +from pip._vendor.pygments import unistring as uni + +__all__ = ['PythonLexer', 'PythonConsoleLexer', 'PythonTracebackLexer', + 'Python2Lexer', 'Python2TracebackLexer', + 'CythonLexer', 'DgLexer', 'NumPyLexer'] + + +class PythonLexer(RegexLexer): + """ + For Python source code (version 3.x). + + .. versionchanged:: 2.5 + This is now the default ``PythonLexer``. It is still available as the + alias ``Python3Lexer``. + """ + + name = 'Python' + url = 'https://www.python.org' + aliases = ['python', 'py', 'sage', 'python3', 'py3', 'bazel', 'starlark', 'pyi'] + filenames = [ + '*.py', + '*.pyw', + # Type stubs + '*.pyi', + # Jython + '*.jy', + # Sage + '*.sage', + # SCons + '*.sc', + 'SConstruct', + 'SConscript', + # Skylark/Starlark (used by Bazel, Buck, and Pants) + '*.bzl', + 'BUCK', + 'BUILD', + 'BUILD.bazel', + 'WORKSPACE', + # Twisted Application infrastructure + '*.tac', + # Execubot level format + '*.pye', + ] + mimetypes = ['text/x-python', 'application/x-python', + 'text/x-python3', 'application/x-python3'] + version_added = '0.10' + + uni_name = f"[{uni.xid_start}][{uni.xid_continue}]*" + + def innerstring_rules(ttype): + return [ + # the old style '%s' % (...) string formatting (still valid in Py3) + (r'%(\(\w+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?' + '[hlL]?[E-GXc-giorsaux%]', String.Interpol), + # the new style '{}'.format(...) string formatting + (r'\{' + r'((\w+)((\.\w+)|(\[[^\]]+\]))*)?' # field name + r'(\![sra])?' # conversion + r'(\:(.?[<>=\^])?[-+ ]?#?0?(\d+)?,?(\.\d+)?[E-GXb-gnosx%]?)?' + r'\}', String.Interpol), + + # backslashes, quotes and formatting signs must be parsed one at a time + (r'[^\\\'"%{\n]+', ttype), + (r'[\'"\\]', ttype), + # unhandled string formatting sign + (r'%|(\{{1,2})', ttype) + # newlines are an error (use "nl" state) + ] + + def fstring_rules(ttype): + return [ + # Assuming that a '}' is the closing brace after format specifier. + # Sadly, this means that we won't detect syntax error. But it's + # more important to parse correct syntax correctly, than to + # highlight invalid syntax. + (r'\}', String.Interpol), + (r'\{', String.Interpol, 'expr-inside-fstring'), + # backslashes, quotes and formatting signs must be parsed one at a time + (r'[^\\\'"{}\n]+', ttype), + (r'[\'"\\]', ttype), + # newlines are an error (use "nl" state) + ] + + tokens = { + 'root': [ + (r'\n', Whitespace), + (r'^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")', + bygroups(Whitespace, String.Affix, String.Doc)), + (r"^(\s*)([rRuUbB]{,2})('''(?:.|\n)*?''')", + bygroups(Whitespace, String.Affix, String.Doc)), + (r'\A#!.+$', Comment.Hashbang), + (r'#.*$', Comment.Single), + (r'\\\n', Text), + (r'\\', Text), + include('keywords'), + include('soft-keywords'), + (r'(def)((?:\s|\\\s)+)', bygroups(Keyword, Whitespace), 'funcname'), + (r'(class)((?:\s|\\\s)+)', bygroups(Keyword, Whitespace), 'classname'), + (r'(from)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Whitespace), + 'fromimport'), + (r'(import)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Whitespace), + 'import'), + include('expr'), + ], + 'expr': [ + # raw f-strings and t-strings + ('(?i)(r[ft]|[ft]r)(""")', + bygroups(String.Affix, String.Double), + combined('rfstringescape', 'tdqf')), + ("(?i)(r[ft]|[ft]r)(''')", + bygroups(String.Affix, String.Single), + combined('rfstringescape', 'tsqf')), + ('(?i)(r[ft]|[ft]r)(")', + bygroups(String.Affix, String.Double), + combined('rfstringescape', 'dqf')), + ("(?i)(r[ft]|[ft]r)(')", + bygroups(String.Affix, String.Single), + combined('rfstringescape', 'sqf')), + # non-raw f-strings and t-strings + ('([fFtT])(""")', bygroups(String.Affix, String.Double), + combined('fstringescape', 'tdqf')), + ("([fFtT])(''')", bygroups(String.Affix, String.Single), + combined('fstringescape', 'tsqf')), + ('([fFtT])(")', bygroups(String.Affix, String.Double), + combined('fstringescape', 'dqf')), + ("([fFtT])(')", bygroups(String.Affix, String.Single), + combined('fstringescape', 'sqf')), + # raw bytes and strings + ('(?i)(rb|br|r)(""")', + bygroups(String.Affix, String.Double), 'tdqs'), + ("(?i)(rb|br|r)(''')", + bygroups(String.Affix, String.Single), 'tsqs'), + ('(?i)(rb|br|r)(")', + bygroups(String.Affix, String.Double), 'dqs'), + ("(?i)(rb|br|r)(')", + bygroups(String.Affix, String.Single), 'sqs'), + # non-raw strings + ('([uU]?)(""")', bygroups(String.Affix, String.Double), + combined('stringescape', 'tdqs')), + ("([uU]?)(''')", bygroups(String.Affix, String.Single), + combined('stringescape', 'tsqs')), + ('([uU]?)(")', bygroups(String.Affix, String.Double), + combined('stringescape', 'dqs')), + ("([uU]?)(')", bygroups(String.Affix, String.Single), + combined('stringescape', 'sqs')), + # non-raw bytes + ('([bB])(""")', bygroups(String.Affix, String.Double), + combined('bytesescape', 'tdqs')), + ("([bB])(''')", bygroups(String.Affix, String.Single), + combined('bytesescape', 'tsqs')), + ('([bB])(")', bygroups(String.Affix, String.Double), + combined('bytesescape', 'dqs')), + ("([bB])(')", bygroups(String.Affix, String.Single), + combined('bytesescape', 'sqs')), + + (r'[^\S\n]+', Text), + include('numbers'), + (r'!=|==|<<|>>|:=|[-~+/*%=<>&^|.]', Operator), + (r'[]{}:(),;[]', Punctuation), + (r'(in|is|and|or|not)\b', Operator.Word), + include('expr-keywords'), + include('builtins'), + include('magicfuncs'), + include('magicvars'), + include('name'), + ], + 'expr-inside-fstring': [ + (r'[{([]', Punctuation, 'expr-inside-fstring-inner'), + # without format specifier + (r'(=\s*)?' # debug (https://bugs.python.org/issue36817) + r'(\![sraf])?' # conversion + r'\}', String.Interpol, '#pop'), + # with format specifier + # we'll catch the remaining '}' in the outer scope + (r'(=\s*)?' # debug (https://bugs.python.org/issue36817) + r'(\![sraf])?' # conversion + r':', String.Interpol, '#pop'), + (r'\s+', Whitespace), # allow new lines + include('expr'), + ], + 'expr-inside-fstring-inner': [ + (r'[{([]', Punctuation, 'expr-inside-fstring-inner'), + (r'[])}]', Punctuation, '#pop'), + (r'\s+', Whitespace), # allow new lines + include('expr'), + ], + 'expr-keywords': [ + # Based on https://docs.python.org/3/reference/expressions.html + (words(( + 'async for', 'await', 'else', 'for', 'if', 'lambda', + 'yield', 'yield from'), suffix=r'\b'), + Keyword), + (words(('True', 'False', 'None'), suffix=r'\b'), Keyword.Constant), + ], + 'keywords': [ + (words(( + 'assert', 'async', 'await', 'break', 'continue', 'del', 'elif', + 'else', 'except', 'finally', 'for', 'global', 'if', 'lambda', + 'pass', 'raise', 'nonlocal', 'return', 'try', 'while', 'yield', + 'yield from', 'as', 'with'), suffix=r'\b'), + Keyword), + (words(('True', 'False', 'None'), suffix=r'\b'), Keyword.Constant), + ], + 'soft-keywords': [ + # `match`, `case` and `_` soft keywords + (r'(^[ \t]*)' # at beginning of line + possible indentation + r'(match|case)\b' # a possible keyword + r'(?![ \t]*(?:' # not followed by... + r'[:,;=^&|@~)\]}]|(?:' + # characters and keywords that mean this isn't + # pattern matching (but None/True/False is ok) + r'|'.join(k for k in keyword.kwlist if k[0].islower()) + r')\b))', + bygroups(Text, Keyword), 'soft-keywords-inner'), + ], + 'soft-keywords-inner': [ + # optional `_` keyword + (r'(\s+)([^\n_]*)(_\b)', bygroups(Whitespace, using(this), Keyword)), + default('#pop') + ], + 'builtins': [ + (words(( + '__import__', 'abs', 'aiter', 'all', 'any', 'bin', 'bool', 'bytearray', + 'breakpoint', 'bytes', 'callable', 'chr', 'classmethod', 'compile', + 'complex', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', + 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', + 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'isinstance', + 'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max', + 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', + 'print', 'property', 'range', 'repr', 'reversed', 'round', 'set', + 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', + 'tuple', 'type', 'vars', 'zip'), prefix=r'(?>|[-~+/*%=<>&^|.]', Operator), + include('keywords'), + (r'(def)((?:\s|\\\s)+)', bygroups(Keyword, Whitespace), 'funcname'), + (r'(class)((?:\s|\\\s)+)', bygroups(Keyword, Whitespace), 'classname'), + (r'(from)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Whitespace), + 'fromimport'), + (r'(import)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Whitespace), + 'import'), + include('builtins'), + include('magicfuncs'), + include('magicvars'), + include('backtick'), + ('([rR]|[uUbB][rR]|[rR][uUbB])(""")', + bygroups(String.Affix, String.Double), 'tdqs'), + ("([rR]|[uUbB][rR]|[rR][uUbB])(''')", + bygroups(String.Affix, String.Single), 'tsqs'), + ('([rR]|[uUbB][rR]|[rR][uUbB])(")', + bygroups(String.Affix, String.Double), 'dqs'), + ("([rR]|[uUbB][rR]|[rR][uUbB])(')", + bygroups(String.Affix, String.Single), 'sqs'), + ('([uUbB]?)(""")', bygroups(String.Affix, String.Double), + combined('stringescape', 'tdqs')), + ("([uUbB]?)(''')", bygroups(String.Affix, String.Single), + combined('stringescape', 'tsqs')), + ('([uUbB]?)(")', bygroups(String.Affix, String.Double), + combined('stringescape', 'dqs')), + ("([uUbB]?)(')", bygroups(String.Affix, String.Single), + combined('stringescape', 'sqs')), + include('name'), + include('numbers'), + ], + 'keywords': [ + (words(( + 'assert', 'break', 'continue', 'del', 'elif', 'else', 'except', + 'exec', 'finally', 'for', 'global', 'if', 'lambda', 'pass', + 'print', 'raise', 'return', 'try', 'while', 'yield', + 'yield from', 'as', 'with'), suffix=r'\b'), + Keyword), + ], + 'builtins': [ + (words(( + '__import__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', + 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', + 'cmp', 'coerce', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod', + 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', + 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id', + 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', + 'list', 'locals', 'long', 'map', 'max', 'min', 'next', 'object', + 'oct', 'open', 'ord', 'pow', 'property', 'range', 'raw_input', 'reduce', + 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', + 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', + 'unichr', 'unicode', 'vars', 'xrange', 'zip'), + prefix=r'(?>> )(.*\n)', bygroups(Generic.Prompt, Other.Code), 'continuations'), + # This happens, e.g., when tracebacks are embedded in documentation; + # trailing whitespaces are often stripped in such contexts. + (r'(>>>)(\n)', bygroups(Generic.Prompt, Whitespace)), + (r'(\^C)?Traceback \(most recent call last\):\n', Other.Traceback, 'traceback'), + # SyntaxError starts with this + (r' File "[^"]+", line \d+', Other.Traceback, 'traceback'), + (r'.*\n', Generic.Output), + ], + 'continuations': [ + (r'(\.\.\. )(.*\n)', bygroups(Generic.Prompt, Other.Code)), + # See above. + (r'(\.\.\.)(\n)', bygroups(Generic.Prompt, Whitespace)), + default('#pop'), + ], + 'traceback': [ + # As soon as we see a traceback, consume everything until the next + # >>> prompt. + (r'(?=>>>( |$))', Text, '#pop'), + (r'(KeyboardInterrupt)(\n)', bygroups(Name.Class, Whitespace)), + (r'.*\n', Other.Traceback), + ], + } + + +class PythonConsoleLexer(DelegatingLexer): + """ + For Python console output or doctests, such as: + + .. sourcecode:: pycon + + >>> a = 'foo' + >>> print(a) + foo + >>> 1 / 0 + Traceback (most recent call last): + File "", line 1, in + ZeroDivisionError: integer division or modulo by zero + + Additional options: + + `python3` + Use Python 3 lexer for code. Default is ``True``. + + .. versionadded:: 1.0 + .. versionchanged:: 2.5 + Now defaults to ``True``. + """ + + name = 'Python console session' + aliases = ['pycon', 'python-console'] + mimetypes = ['text/x-python-doctest'] + url = 'https://python.org' + version_added = '' + + def __init__(self, **options): + python3 = get_bool_opt(options, 'python3', True) + if python3: + pylexer = PythonLexer + tblexer = PythonTracebackLexer + else: + pylexer = Python2Lexer + tblexer = Python2TracebackLexer + # We have two auxiliary lexers. Use DelegatingLexer twice with + # different tokens. TODO: DelegatingLexer should support this + # directly, by accepting a tuplet of auxiliary lexers and a tuple of + # distinguishing tokens. Then we wouldn't need this intermediary + # class. + class _ReplaceInnerCode(DelegatingLexer): + def __init__(self, **options): + super().__init__(pylexer, _PythonConsoleLexerBase, Other.Code, **options) + super().__init__(tblexer, _ReplaceInnerCode, Other.Traceback, **options) + + +class PythonTracebackLexer(RegexLexer): + """ + For Python 3.x tracebacks, with support for chained exceptions. + + .. versionchanged:: 2.5 + This is now the default ``PythonTracebackLexer``. It is still available + as the alias ``Python3TracebackLexer``. + """ + + name = 'Python Traceback' + aliases = ['pytb', 'py3tb'] + filenames = ['*.pytb', '*.py3tb'] + mimetypes = ['text/x-python-traceback', 'text/x-python3-traceback'] + url = 'https://python.org' + version_added = '1.0' + + tokens = { + 'root': [ + (r'\n', Whitespace), + (r'^(\^C)?Traceback \(most recent call last\):\n', Generic.Traceback, 'intb'), + (r'^During handling of the above exception, another ' + r'exception occurred:\n\n', Generic.Traceback), + (r'^The above exception was the direct cause of the ' + r'following exception:\n\n', Generic.Traceback), + (r'^(?= File "[^"]+", line \d+)', Generic.Traceback, 'intb'), + (r'^.*\n', Other), + ], + 'intb': [ + (r'^( File )("[^"]+")(, line )(\d+)(, in )(.+)(\n)', + bygroups(Text, Name.Builtin, Text, Number, Text, Name, Whitespace)), + (r'^( File )("[^"]+")(, line )(\d+)(\n)', + bygroups(Text, Name.Builtin, Text, Number, Whitespace)), + (r'^( )(.+)(\n)', + bygroups(Whitespace, using(PythonLexer), Whitespace), 'markers'), + (r'^([ \t]*)(\.\.\.)(\n)', + bygroups(Whitespace, Comment, Whitespace)), # for doctests... + (r'^([^:]+)(: )(.+)(\n)', + bygroups(Generic.Error, Text, Name, Whitespace), '#pop'), + (r'^([a-zA-Z_][\w.]*)(:?\n)', + bygroups(Generic.Error, Whitespace), '#pop'), + default('#pop'), + ], + 'markers': [ + # Either `PEP 657 ` + # error locations in Python 3.11+, or single-caret markers + # for syntax errors before that. + (r'^( {4,})([~^]+)(\n)', + bygroups(Whitespace, Punctuation.Marker, Whitespace), + '#pop'), + default('#pop'), + ], + } + + +Python3TracebackLexer = PythonTracebackLexer + + +class Python2TracebackLexer(RegexLexer): + """ + For Python tracebacks. + + .. versionchanged:: 2.5 + This class has been renamed from ``PythonTracebackLexer``. + ``PythonTracebackLexer`` now refers to the Python 3 variant. + """ + + name = 'Python 2.x Traceback' + aliases = ['py2tb'] + filenames = ['*.py2tb'] + mimetypes = ['text/x-python2-traceback'] + url = 'https://python.org' + version_added = '0.7' + + tokens = { + 'root': [ + # Cover both (most recent call last) and (innermost last) + # The optional ^C allows us to catch keyboard interrupt signals. + (r'^(\^C)?(Traceback.*\n)', + bygroups(Text, Generic.Traceback), 'intb'), + # SyntaxError starts with this. + (r'^(?= File "[^"]+", line \d+)', Generic.Traceback, 'intb'), + (r'^.*\n', Other), + ], + 'intb': [ + (r'^( File )("[^"]+")(, line )(\d+)(, in )(.+)(\n)', + bygroups(Text, Name.Builtin, Text, Number, Text, Name, Whitespace)), + (r'^( File )("[^"]+")(, line )(\d+)(\n)', + bygroups(Text, Name.Builtin, Text, Number, Whitespace)), + (r'^( )(.+)(\n)', + bygroups(Text, using(Python2Lexer), Whitespace), 'marker'), + (r'^([ \t]*)(\.\.\.)(\n)', + bygroups(Text, Comment, Whitespace)), # for doctests... + (r'^([^:]+)(: )(.+)(\n)', + bygroups(Generic.Error, Text, Name, Whitespace), '#pop'), + (r'^([a-zA-Z_]\w*)(:?\n)', + bygroups(Generic.Error, Whitespace), '#pop') + ], + 'marker': [ + # For syntax errors. + (r'( {4,})(\^)', bygroups(Text, Punctuation.Marker), '#pop'), + default('#pop'), + ], + } + + +class CythonLexer(RegexLexer): + """ + For Pyrex and Cython source code. + """ + + name = 'Cython' + url = 'https://cython.org' + aliases = ['cython', 'pyx', 'pyrex'] + filenames = ['*.pyx', '*.pxd', '*.pxi'] + mimetypes = ['text/x-cython', 'application/x-cython'] + version_added = '1.1' + + tokens = { + 'root': [ + (r'\n', Whitespace), + (r'^(\s*)("""(?:.|\n)*?""")', bygroups(Whitespace, String.Doc)), + (r"^(\s*)('''(?:.|\n)*?''')", bygroups(Whitespace, String.Doc)), + (r'[^\S\n]+', Text), + (r'#.*$', Comment), + (r'[]{}:(),;[]', Punctuation), + (r'\\\n', Whitespace), + (r'\\', Text), + (r'(in|is|and|or|not)\b', Operator.Word), + (r'(<)([a-zA-Z0-9.?]+)(>)', + bygroups(Punctuation, Keyword.Type, Punctuation)), + (r'!=|==|<<|>>|[-~+/*%=<>&^|.?]', Operator), + (r'(from)(\d+)(<=)(\s+)(<)(\d+)(:)', + bygroups(Keyword, Number.Integer, Operator, Whitespace, Operator, + Name, Punctuation)), + include('keywords'), + (r'(def|property)(\s+)', bygroups(Keyword, Whitespace), 'funcname'), + (r'(cp?def)(\s+)', bygroups(Keyword, Whitespace), 'cdef'), + # (should actually start a block with only cdefs) + (r'(cdef)(:)', bygroups(Keyword, Punctuation)), + (r'(class|cppclass|struct)(\s+)', bygroups(Keyword, Whitespace), 'classname'), + (r'(from)(\s+)', bygroups(Keyword, Whitespace), 'fromimport'), + (r'(c?import)(\s+)', bygroups(Keyword, Whitespace), 'import'), + include('builtins'), + include('backtick'), + ('(?:[rR]|[uU][rR]|[rR][uU])"""', String, 'tdqs'), + ("(?:[rR]|[uU][rR]|[rR][uU])'''", String, 'tsqs'), + ('(?:[rR]|[uU][rR]|[rR][uU])"', String, 'dqs'), + ("(?:[rR]|[uU][rR]|[rR][uU])'", String, 'sqs'), + ('[uU]?"""', String, combined('stringescape', 'tdqs')), + ("[uU]?'''", String, combined('stringescape', 'tsqs')), + ('[uU]?"', String, combined('stringescape', 'dqs')), + ("[uU]?'", String, combined('stringescape', 'sqs')), + include('name'), + include('numbers'), + ], + 'keywords': [ + (words(( + 'assert', 'async', 'await', 'break', 'by', 'continue', 'ctypedef', 'del', + 'elif', 'else', 'except', 'except?', 'exec', 'finally', 'for', 'fused', 'gil', + 'global', 'if', 'include', 'lambda', 'namespace', 'new', 'noexcept','nogil', + 'pass', 'print', 'raise', 'return', 'try', 'while', 'yield', 'as', 'with'), + suffix=r'\b'), + Keyword), + (words(('True', 'False', 'None', 'NULL'), suffix=r'\b'), Keyword.Constant), + (r'(DEF|IF|ELIF|ELSE)\b', Comment.Preproc), + ], + 'builtins': [ + (words(( + '__import__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bint', + 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'char', 'chr', + 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'delattr', + 'dict', 'dir', 'divmod', 'double', 'enumerate', 'eval', 'execfile', 'exit', + 'file', 'filter', 'float', 'frozenset', 'getattr', 'globals', + 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', + 'issubclass', 'iter', 'len', 'list', 'locals', 'long', 'map', 'max', + 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'property', + 'Py_ssize_t', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', + 'round', 'set', 'setattr', 'size_t', 'slice', 'sorted', 'staticmethod', + 'ssize_t', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', + 'unsigned', 'vars', 'xrange', 'zip'), prefix=r'(?]? \d* )? : + .* (?: ft | filetype | syn | syntax ) = ( [^:\s]+ ) +''', re.VERBOSE) + + +def get_filetype_from_line(l): # noqa: E741 + m = modeline_re.search(l) + if m: + return m.group(1) + + +def get_filetype_from_buffer(buf, max_lines=5): + """ + Scan the buffer for modelines and return filetype if one is found. + """ + lines = buf.splitlines() + for line in lines[-1:-max_lines-1:-1]: + ret = get_filetype_from_line(line) + if ret: + return ret + for i in range(max_lines, -1, -1): + if i < len(lines): + ret = get_filetype_from_line(lines[i]) + if ret: + return ret + + return None diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/plugin.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/plugin.py new file mode 100644 index 0000000..0d6377f --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/plugin.py @@ -0,0 +1,74 @@ +""" + pygments.plugin + ~~~~~~~~~~~~~~~ + + Pygments plugin interface. + + lexer plugins:: + + [pygments.lexers] + yourlexer = yourmodule:YourLexer + + formatter plugins:: + + [pygments.formatters] + yourformatter = yourformatter:YourFormatter + /.ext = yourformatter:YourFormatter + + As you can see, you can define extensions for the formatter + with a leading slash. + + syntax plugins:: + + [pygments.styles] + yourstyle = yourstyle:YourStyle + + filter plugin:: + + [pygments.filter] + yourfilter = yourfilter:YourFilter + + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import functools +from importlib.metadata import entry_points + +LEXER_ENTRY_POINT = 'pygments.lexers' +FORMATTER_ENTRY_POINT = 'pygments.formatters' +STYLE_ENTRY_POINT = 'pygments.styles' +FILTER_ENTRY_POINT = 'pygments.filters' + + +@functools.cache +def iter_entry_points(group_name): + groups = entry_points() + if hasattr(groups, 'select'): + # New interface in Python 3.10 and newer versions of the + # importlib_metadata backport. + return groups.select(group=group_name) + else: + # Older interface, deprecated in Python 3.10 and recent + # importlib_metadata, but we need it in Python 3.8 and 3.9. + return groups.get(group_name, []) + + +def find_plugin_lexers(): + for entrypoint in iter_entry_points(LEXER_ENTRY_POINT): + yield entrypoint.load() + + +def find_plugin_formatters(): + for entrypoint in iter_entry_points(FORMATTER_ENTRY_POINT): + yield entrypoint.name, entrypoint.load() + + +def find_plugin_styles(): + for entrypoint in iter_entry_points(STYLE_ENTRY_POINT): + yield entrypoint.name, entrypoint.load() + + +def find_plugin_filters(): + for entrypoint in iter_entry_points(FILTER_ENTRY_POINT): + yield entrypoint.name, entrypoint.load() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/regexopt.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/regexopt.py new file mode 100644 index 0000000..9010eb3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/regexopt.py @@ -0,0 +1,102 @@ +""" + pygments.regexopt + ~~~~~~~~~~~~~~~~~ + + An algorithm that generates optimized regexes for matching long lists of + literal strings. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +from re import escape +from itertools import groupby +from operator import itemgetter + +CS_ESCAPE = re.compile(r'[\[\^\\\-\]]') +FIRST_ELEMENT = itemgetter(0) + + +def commonprefix(m): + """Given an iterable of strings, returns the longest common leading substring""" + if not m: + return "" + s1 = min(m) + s2 = max(m) + for i, c in enumerate(s1): + if c != s2[i]: + return s1[:i] + return s1 + + +def make_charset(letters): + return '[' + CS_ESCAPE.sub(lambda m: '\\' + m.group(), ''.join(letters)) + ']' + + +def regex_opt_inner(strings, open_paren): + """Return a regex that matches any string in the sorted list of strings.""" + close_paren = open_paren and ')' or '' + # print strings, repr(open_paren) + if not strings: + # print '-> nothing left' + return '' + first = strings[0] + if len(strings) == 1: + # print '-> only 1 string' + return open_paren + escape(first) + close_paren + if not first: + # print '-> first string empty' + return open_paren + regex_opt_inner(strings[1:], '(?:') \ + + '?' + close_paren + if len(first) == 1: + # multiple one-char strings? make a charset + oneletter = [] + rest = [] + for s in strings: + if len(s) == 1: + oneletter.append(s) + else: + rest.append(s) + if len(oneletter) > 1: # do we have more than one oneletter string? + if rest: + # print '-> 1-character + rest' + return open_paren + regex_opt_inner(rest, '') + '|' \ + + make_charset(oneletter) + close_paren + # print '-> only 1-character' + return open_paren + make_charset(oneletter) + close_paren + prefix = commonprefix(strings) + if prefix: + plen = len(prefix) + # we have a prefix for all strings + # print '-> prefix:', prefix + return open_paren + escape(prefix) \ + + regex_opt_inner([s[plen:] for s in strings], '(?:') \ + + close_paren + # is there a suffix? + strings_rev = [s[::-1] for s in strings] + suffix = commonprefix(strings_rev) + if suffix: + slen = len(suffix) + # print '-> suffix:', suffix[::-1] + return open_paren \ + + regex_opt_inner(sorted(s[:-slen] for s in strings), '(?:') \ + + escape(suffix[::-1]) + close_paren + # recurse on common 1-string prefixes + # print '-> last resort' + return open_paren + \ + '|'.join(regex_opt_inner(list(group[1]), '') + for group in groupby(strings, lambda s: s[0] == first[0])) \ + + close_paren + + +def regex_opt(strings, prefix='', suffix=''): + """Return a compiled regex that matches any string in the given list. + + The strings to match must be literal strings, not regexes. They will be + regex-escaped. + + *prefix* and *suffix* are pre- and appended to the final regex. + """ + strings = sorted(strings) + return prefix + regex_opt_inner(strings, '(') + suffix diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/scanner.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/scanner.py new file mode 100644 index 0000000..067ebfa --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/scanner.py @@ -0,0 +1,104 @@ +""" + pygments.scanner + ~~~~~~~~~~~~~~~~ + + This library implements a regex based scanner. Some languages + like Pascal are easy to parse but have some keywords that + depend on the context. Because of this it's impossible to lex + that just by using a regular expression lexer like the + `RegexLexer`. + + Have a look at the `DelphiLexer` to get an idea of how to use + this scanner. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import re + + +class EndOfText(RuntimeError): + """ + Raise if end of text is reached and the user + tried to call a match function. + """ + + +class Scanner: + """ + Simple scanner + + All method patterns are regular expression strings (not + compiled expressions!) + """ + + def __init__(self, text, flags=0): + """ + :param text: The text which should be scanned + :param flags: default regular expression flags + """ + self.data = text + self.data_length = len(text) + self.start_pos = 0 + self.pos = 0 + self.flags = flags + self.last = None + self.match = None + self._re_cache = {} + + def eos(self): + """`True` if the scanner reached the end of text.""" + return self.pos >= self.data_length + eos = property(eos, eos.__doc__) + + def check(self, pattern): + """ + Apply `pattern` on the current position and return + the match object. (Doesn't touch pos). Use this for + lookahead. + """ + if self.eos: + raise EndOfText() + if pattern not in self._re_cache: + self._re_cache[pattern] = re.compile(pattern, self.flags) + return self._re_cache[pattern].match(self.data, self.pos) + + def test(self, pattern): + """Apply a pattern on the current position and check + if it patches. Doesn't touch pos. + """ + return self.check(pattern) is not None + + def scan(self, pattern): + """ + Scan the text for the given pattern and update pos/match + and related fields. The return value is a boolean that + indicates if the pattern matched. The matched value is + stored on the instance as ``match``, the last value is + stored as ``last``. ``start_pos`` is the position of the + pointer before the pattern was matched, ``pos`` is the + end position. + """ + if self.eos: + raise EndOfText() + if pattern not in self._re_cache: + self._re_cache[pattern] = re.compile(pattern, self.flags) + self.last = self.match + m = self._re_cache[pattern].match(self.data, self.pos) + if m is None: + return False + self.start_pos = m.start() + self.pos = m.end() + self.match = m.group() + return True + + def get_char(self): + """Scan exactly one char.""" + self.scan('.') + + def __repr__(self): + return '<%s %d/%d>' % ( + self.__class__.__name__, + self.pos, + self.data_length + ) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/sphinxext.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/sphinxext.py new file mode 100644 index 0000000..7f2fa74 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/sphinxext.py @@ -0,0 +1,247 @@ +""" + pygments.sphinxext + ~~~~~~~~~~~~~~~~~~ + + Sphinx extension to generate automatic documentation of lexers, + formatters and filters. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import sys + +from docutils import nodes +from docutils.statemachine import ViewList +from docutils.parsers.rst import Directive +from sphinx.util.nodes import nested_parse_with_titles + + +MODULEDOC = ''' +.. module:: %s + +%s +%s +''' + +LEXERDOC = ''' +.. class:: %s + + :Short names: %s + :Filenames: %s + :MIME types: %s + + %s + + %s + +''' + +FMTERDOC = ''' +.. class:: %s + + :Short names: %s + :Filenames: %s + + %s + +''' + +FILTERDOC = ''' +.. class:: %s + + :Name: %s + + %s + +''' + + +class PygmentsDoc(Directive): + """ + A directive to collect all lexers/formatters/filters and generate + autoclass directives for them. + """ + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self): + self.filenames = set() + if self.arguments[0] == 'lexers': + out = self.document_lexers() + elif self.arguments[0] == 'formatters': + out = self.document_formatters() + elif self.arguments[0] == 'filters': + out = self.document_filters() + elif self.arguments[0] == 'lexers_overview': + out = self.document_lexers_overview() + else: + raise Exception('invalid argument for "pygmentsdoc" directive') + node = nodes.compound() + vl = ViewList(out.split('\n'), source='') + nested_parse_with_titles(self.state, vl, node) + for fn in self.filenames: + self.state.document.settings.record_dependencies.add(fn) + return node.children + + def document_lexers_overview(self): + """Generate a tabular overview of all lexers. + + The columns are the lexer name, the extensions handled by this lexer + (or "None"), the aliases and a link to the lexer class.""" + from pip._vendor.pygments.lexers._mapping import LEXERS + from pip._vendor.pygments.lexers import find_lexer_class + out = [] + + table = [] + + def format_link(name, url): + if url: + return f'`{name} <{url}>`_' + return name + + for classname, data in sorted(LEXERS.items(), key=lambda x: x[1][1].lower()): + lexer_cls = find_lexer_class(data[1]) + extensions = lexer_cls.filenames + lexer_cls.alias_filenames + + table.append({ + 'name': format_link(data[1], lexer_cls.url), + 'extensions': ', '.join(extensions).replace('*', '\\*').replace('_', '\\') or 'None', + 'aliases': ', '.join(data[2]), + 'class': f'{data[0]}.{classname}' + }) + + column_names = ['name', 'extensions', 'aliases', 'class'] + column_lengths = [max([len(row[column]) for row in table if row[column]]) + for column in column_names] + + def write_row(*columns): + """Format a table row""" + out = [] + for length, col in zip(column_lengths, columns): + if col: + out.append(col.ljust(length)) + else: + out.append(' '*length) + + return ' '.join(out) + + def write_seperator(): + """Write a table separator row""" + sep = ['='*c for c in column_lengths] + return write_row(*sep) + + out.append(write_seperator()) + out.append(write_row('Name', 'Extension(s)', 'Short name(s)', 'Lexer class')) + out.append(write_seperator()) + for row in table: + out.append(write_row( + row['name'], + row['extensions'], + row['aliases'], + f':class:`~{row["class"]}`')) + out.append(write_seperator()) + + return '\n'.join(out) + + def document_lexers(self): + from pip._vendor.pygments.lexers._mapping import LEXERS + from pip._vendor import pygments + import inspect + import pathlib + + out = [] + modules = {} + moduledocstrings = {} + for classname, data in sorted(LEXERS.items(), key=lambda x: x[0]): + module = data[0] + mod = __import__(module, None, None, [classname]) + self.filenames.add(mod.__file__) + cls = getattr(mod, classname) + if not cls.__doc__: + print(f"Warning: {classname} does not have a docstring.") + docstring = cls.__doc__ + if isinstance(docstring, bytes): + docstring = docstring.decode('utf8') + + example_file = getattr(cls, '_example', None) + if example_file: + p = pathlib.Path(inspect.getabsfile(pygments)).parent.parent /\ + 'tests' / 'examplefiles' / example_file + content = p.read_text(encoding='utf-8') + if not content: + raise Exception( + f"Empty example file '{example_file}' for lexer " + f"{classname}") + + if data[2]: + lexer_name = data[2][0] + docstring += '\n\n .. admonition:: Example\n' + docstring += f'\n .. code-block:: {lexer_name}\n\n' + for line in content.splitlines(): + docstring += f' {line}\n' + + if cls.version_added: + version_line = f'.. versionadded:: {cls.version_added}' + else: + version_line = '' + + modules.setdefault(module, []).append(( + classname, + ', '.join(data[2]) or 'None', + ', '.join(data[3]).replace('*', '\\*').replace('_', '\\') or 'None', + ', '.join(data[4]) or 'None', + docstring, + version_line)) + if module not in moduledocstrings: + moddoc = mod.__doc__ + if isinstance(moddoc, bytes): + moddoc = moddoc.decode('utf8') + moduledocstrings[module] = moddoc + + for module, lexers in sorted(modules.items(), key=lambda x: x[0]): + if moduledocstrings[module] is None: + raise Exception(f"Missing docstring for {module}") + heading = moduledocstrings[module].splitlines()[4].strip().rstrip('.') + out.append(MODULEDOC % (module, heading, '-'*len(heading))) + for data in lexers: + out.append(LEXERDOC % data) + + return ''.join(out) + + def document_formatters(self): + from pip._vendor.pygments.formatters import FORMATTERS + + out = [] + for classname, data in sorted(FORMATTERS.items(), key=lambda x: x[0]): + module = data[0] + mod = __import__(module, None, None, [classname]) + self.filenames.add(mod.__file__) + cls = getattr(mod, classname) + docstring = cls.__doc__ + if isinstance(docstring, bytes): + docstring = docstring.decode('utf8') + heading = cls.__name__ + out.append(FMTERDOC % (heading, ', '.join(data[2]) or 'None', + ', '.join(data[3]).replace('*', '\\*') or 'None', + docstring)) + return ''.join(out) + + def document_filters(self): + from pip._vendor.pygments.filters import FILTERS + + out = [] + for name, cls in FILTERS.items(): + self.filenames.add(sys.modules[cls.__module__].__file__) + docstring = cls.__doc__ + if isinstance(docstring, bytes): + docstring = docstring.decode('utf8') + out.append(FILTERDOC % (cls.__name__, name, docstring)) + return ''.join(out) + + +def setup(app): + app.add_directive('pygmentsdoc', PygmentsDoc) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/style.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/style.py new file mode 100644 index 0000000..04b420d --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/style.py @@ -0,0 +1,203 @@ +""" + pygments.style + ~~~~~~~~~~~~~~ + + Basic style object. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pip._vendor.pygments.token import Token, STANDARD_TYPES + +# Default mapping of ansixxx to RGB colors. +_ansimap = { + # dark + 'ansiblack': '000000', + 'ansired': '7f0000', + 'ansigreen': '007f00', + 'ansiyellow': '7f7fe0', + 'ansiblue': '00007f', + 'ansimagenta': '7f007f', + 'ansicyan': '007f7f', + 'ansigray': 'e5e5e5', + # normal + 'ansibrightblack': '555555', + 'ansibrightred': 'ff0000', + 'ansibrightgreen': '00ff00', + 'ansibrightyellow': 'ffff00', + 'ansibrightblue': '0000ff', + 'ansibrightmagenta': 'ff00ff', + 'ansibrightcyan': '00ffff', + 'ansiwhite': 'ffffff', +} +# mapping of deprecated #ansixxx colors to new color names +_deprecated_ansicolors = { + # dark + '#ansiblack': 'ansiblack', + '#ansidarkred': 'ansired', + '#ansidarkgreen': 'ansigreen', + '#ansibrown': 'ansiyellow', + '#ansidarkblue': 'ansiblue', + '#ansipurple': 'ansimagenta', + '#ansiteal': 'ansicyan', + '#ansilightgray': 'ansigray', + # normal + '#ansidarkgray': 'ansibrightblack', + '#ansired': 'ansibrightred', + '#ansigreen': 'ansibrightgreen', + '#ansiyellow': 'ansibrightyellow', + '#ansiblue': 'ansibrightblue', + '#ansifuchsia': 'ansibrightmagenta', + '#ansiturquoise': 'ansibrightcyan', + '#ansiwhite': 'ansiwhite', +} +ansicolors = set(_ansimap) + + +class StyleMeta(type): + + def __new__(mcs, name, bases, dct): + obj = type.__new__(mcs, name, bases, dct) + for token in STANDARD_TYPES: + if token not in obj.styles: + obj.styles[token] = '' + + def colorformat(text): + if text in ansicolors: + return text + if text[0:1] == '#': + col = text[1:] + if len(col) == 6: + return col + elif len(col) == 3: + return col[0] * 2 + col[1] * 2 + col[2] * 2 + elif text == '': + return '' + elif text.startswith('var') or text.startswith('calc'): + return text + assert False, f"wrong color format {text!r}" + + _styles = obj._styles = {} + + for ttype in obj.styles: + for token in ttype.split(): + if token in _styles: + continue + ndef = _styles.get(token.parent, None) + styledefs = obj.styles.get(token, '').split() + if not ndef or token is None: + ndef = ['', 0, 0, 0, '', '', 0, 0, 0] + elif 'noinherit' in styledefs and token is not Token: + ndef = _styles[Token][:] + else: + ndef = ndef[:] + _styles[token] = ndef + for styledef in obj.styles.get(token, '').split(): + if styledef == 'noinherit': + pass + elif styledef == 'bold': + ndef[1] = 1 + elif styledef == 'nobold': + ndef[1] = 0 + elif styledef == 'italic': + ndef[2] = 1 + elif styledef == 'noitalic': + ndef[2] = 0 + elif styledef == 'underline': + ndef[3] = 1 + elif styledef == 'nounderline': + ndef[3] = 0 + elif styledef[:3] == 'bg:': + ndef[4] = colorformat(styledef[3:]) + elif styledef[:7] == 'border:': + ndef[5] = colorformat(styledef[7:]) + elif styledef == 'roman': + ndef[6] = 1 + elif styledef == 'sans': + ndef[7] = 1 + elif styledef == 'mono': + ndef[8] = 1 + else: + ndef[0] = colorformat(styledef) + + return obj + + def style_for_token(cls, token): + t = cls._styles[token] + ansicolor = bgansicolor = None + color = t[0] + if color in _deprecated_ansicolors: + color = _deprecated_ansicolors[color] + if color in ansicolors: + ansicolor = color + color = _ansimap[color] + bgcolor = t[4] + if bgcolor in _deprecated_ansicolors: + bgcolor = _deprecated_ansicolors[bgcolor] + if bgcolor in ansicolors: + bgansicolor = bgcolor + bgcolor = _ansimap[bgcolor] + + return { + 'color': color or None, + 'bold': bool(t[1]), + 'italic': bool(t[2]), + 'underline': bool(t[3]), + 'bgcolor': bgcolor or None, + 'border': t[5] or None, + 'roman': bool(t[6]) or None, + 'sans': bool(t[7]) or None, + 'mono': bool(t[8]) or None, + 'ansicolor': ansicolor, + 'bgansicolor': bgansicolor, + } + + def list_styles(cls): + return list(cls) + + def styles_token(cls, ttype): + return ttype in cls._styles + + def __iter__(cls): + for token in cls._styles: + yield token, cls.style_for_token(token) + + def __len__(cls): + return len(cls._styles) + + +class Style(metaclass=StyleMeta): + + #: overall background color (``None`` means transparent) + background_color = '#ffffff' + + #: highlight background color + highlight_color = '#ffffcc' + + #: line number font color + line_number_color = 'inherit' + + #: line number background color + line_number_background_color = 'transparent' + + #: special line number font color + line_number_special_color = '#000000' + + #: special line number background color + line_number_special_background_color = '#ffffc0' + + #: Style definitions for individual token types. + styles = {} + + #: user-friendly style name (used when selecting the style, so this + # should be all-lowercase, no spaces, hyphens) + name = 'unnamed' + + aliases = [] + + # Attribute for lexers defined within Pygments. If set + # to True, the style is not shown in the style gallery + # on the website. This is intended for language-specific + # styles. + web_style_gallery_exclude = False diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__init__.py new file mode 100644 index 0000000..29f2110 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__init__.py @@ -0,0 +1,61 @@ +""" + pygments.styles + ~~~~~~~~~~~~~~~ + + Contains built-in styles. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pip._vendor.pygments.plugin import find_plugin_styles +from pip._vendor.pygments.util import ClassNotFound +from pip._vendor.pygments.styles._mapping import STYLES + +#: A dictionary of built-in styles, mapping style names to +#: ``'submodule::classname'`` strings. +#: This list is deprecated. Use `pygments.styles.STYLES` instead +STYLE_MAP = {v[1]: v[0].split('.')[-1] + '::' + k for k, v in STYLES.items()} + +#: Internal reverse mapping to make `get_style_by_name` more efficient +_STYLE_NAME_TO_MODULE_MAP = {v[1]: (v[0], k) for k, v in STYLES.items()} + + +def get_style_by_name(name): + """ + Return a style class by its short name. The names of the builtin styles + are listed in :data:`pygments.styles.STYLE_MAP`. + + Will raise :exc:`pygments.util.ClassNotFound` if no style of that name is + found. + """ + if name in _STYLE_NAME_TO_MODULE_MAP: + mod, cls = _STYLE_NAME_TO_MODULE_MAP[name] + builtin = "yes" + else: + for found_name, style in find_plugin_styles(): + if name == found_name: + return style + # perhaps it got dropped into our styles package + builtin = "" + mod = 'pygments.styles.' + name + cls = name.title() + "Style" + + try: + mod = __import__(mod, None, None, [cls]) + except ImportError: + raise ClassNotFound(f"Could not find style module {mod!r}" + + (builtin and ", though it should be builtin") + + ".") + try: + return getattr(mod, cls) + except AttributeError: + raise ClassNotFound(f"Could not find style class {cls!r} in style module.") + + +def get_all_styles(): + """Return a generator for all styles by name, both builtin and plugin.""" + for v in STYLES.values(): + yield v[1] + for name, _ in find_plugin_styles(): + yield name diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6ec5c6d Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__pycache__/_mapping.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__pycache__/_mapping.cpython-312.pyc new file mode 100644 index 0000000..d770475 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__pycache__/_mapping.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/_mapping.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/_mapping.py new file mode 100644 index 0000000..49a7fae --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/_mapping.py @@ -0,0 +1,54 @@ +# Automatically generated by scripts/gen_mapfiles.py. +# DO NOT EDIT BY HAND; run `tox -e mapfiles` instead. + +STYLES = { + 'AbapStyle': ('pygments.styles.abap', 'abap', ()), + 'AlgolStyle': ('pygments.styles.algol', 'algol', ()), + 'Algol_NuStyle': ('pygments.styles.algol_nu', 'algol_nu', ()), + 'ArduinoStyle': ('pygments.styles.arduino', 'arduino', ()), + 'AutumnStyle': ('pygments.styles.autumn', 'autumn', ()), + 'BlackWhiteStyle': ('pygments.styles.bw', 'bw', ()), + 'BorlandStyle': ('pygments.styles.borland', 'borland', ()), + 'CoffeeStyle': ('pygments.styles.coffee', 'coffee', ()), + 'ColorfulStyle': ('pygments.styles.colorful', 'colorful', ()), + 'DefaultStyle': ('pygments.styles.default', 'default', ()), + 'DraculaStyle': ('pygments.styles.dracula', 'dracula', ()), + 'EmacsStyle': ('pygments.styles.emacs', 'emacs', ()), + 'FriendlyGrayscaleStyle': ('pygments.styles.friendly_grayscale', 'friendly_grayscale', ()), + 'FriendlyStyle': ('pygments.styles.friendly', 'friendly', ()), + 'FruityStyle': ('pygments.styles.fruity', 'fruity', ()), + 'GhDarkStyle': ('pygments.styles.gh_dark', 'github-dark', ()), + 'GruvboxDarkStyle': ('pygments.styles.gruvbox', 'gruvbox-dark', ()), + 'GruvboxLightStyle': ('pygments.styles.gruvbox', 'gruvbox-light', ()), + 'IgorStyle': ('pygments.styles.igor', 'igor', ()), + 'InkPotStyle': ('pygments.styles.inkpot', 'inkpot', ()), + 'LightbulbStyle': ('pygments.styles.lightbulb', 'lightbulb', ()), + 'LilyPondStyle': ('pygments.styles.lilypond', 'lilypond', ()), + 'LovelaceStyle': ('pygments.styles.lovelace', 'lovelace', ()), + 'ManniStyle': ('pygments.styles.manni', 'manni', ()), + 'MaterialStyle': ('pygments.styles.material', 'material', ()), + 'MonokaiStyle': ('pygments.styles.monokai', 'monokai', ()), + 'MurphyStyle': ('pygments.styles.murphy', 'murphy', ()), + 'NativeStyle': ('pygments.styles.native', 'native', ()), + 'NordDarkerStyle': ('pygments.styles.nord', 'nord-darker', ()), + 'NordStyle': ('pygments.styles.nord', 'nord', ()), + 'OneDarkStyle': ('pygments.styles.onedark', 'one-dark', ()), + 'ParaisoDarkStyle': ('pygments.styles.paraiso_dark', 'paraiso-dark', ()), + 'ParaisoLightStyle': ('pygments.styles.paraiso_light', 'paraiso-light', ()), + 'PastieStyle': ('pygments.styles.pastie', 'pastie', ()), + 'PerldocStyle': ('pygments.styles.perldoc', 'perldoc', ()), + 'RainbowDashStyle': ('pygments.styles.rainbow_dash', 'rainbow_dash', ()), + 'RrtStyle': ('pygments.styles.rrt', 'rrt', ()), + 'SasStyle': ('pygments.styles.sas', 'sas', ()), + 'SolarizedDarkStyle': ('pygments.styles.solarized', 'solarized-dark', ()), + 'SolarizedLightStyle': ('pygments.styles.solarized', 'solarized-light', ()), + 'StarofficeStyle': ('pygments.styles.staroffice', 'staroffice', ()), + 'StataDarkStyle': ('pygments.styles.stata_dark', 'stata-dark', ()), + 'StataLightStyle': ('pygments.styles.stata_light', 'stata-light', ()), + 'TangoStyle': ('pygments.styles.tango', 'tango', ()), + 'TracStyle': ('pygments.styles.trac', 'trac', ()), + 'VimStyle': ('pygments.styles.vim', 'vim', ()), + 'VisualStudioStyle': ('pygments.styles.vs', 'vs', ()), + 'XcodeStyle': ('pygments.styles.xcode', 'xcode', ()), + 'ZenburnStyle': ('pygments.styles.zenburn', 'zenburn', ()), +} diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/token.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/token.py new file mode 100644 index 0000000..1f756b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/token.py @@ -0,0 +1,214 @@ +""" + pygments.token + ~~~~~~~~~~~~~~ + + Basic token types and the standard tokens. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + + +class _TokenType(tuple): + parent = None + + def split(self): + buf = [] + node = self + while node is not None: + buf.append(node) + node = node.parent + buf.reverse() + return buf + + def __init__(self, *args): + # no need to call super.__init__ + self.subtypes = set() + + def __contains__(self, val): + return self is val or ( + type(val) is self.__class__ and + val[:len(self)] == self + ) + + def __getattr__(self, val): + if not val or not val[0].isupper(): + return tuple.__getattribute__(self, val) + new = _TokenType(self + (val,)) + setattr(self, val, new) + self.subtypes.add(new) + new.parent = self + return new + + def __repr__(self): + return 'Token' + (self and '.' or '') + '.'.join(self) + + def __copy__(self): + # These instances are supposed to be singletons + return self + + def __deepcopy__(self, memo): + # These instances are supposed to be singletons + return self + + +Token = _TokenType() + +# Special token types +Text = Token.Text +Whitespace = Text.Whitespace +Escape = Token.Escape +Error = Token.Error +# Text that doesn't belong to this lexer (e.g. HTML in PHP) +Other = Token.Other + +# Common token types for source code +Keyword = Token.Keyword +Name = Token.Name +Literal = Token.Literal +String = Literal.String +Number = Literal.Number +Punctuation = Token.Punctuation +Operator = Token.Operator +Comment = Token.Comment + +# Generic types for non-source code +Generic = Token.Generic + +# String and some others are not direct children of Token. +# alias them: +Token.Token = Token +Token.String = String +Token.Number = Number + + +def is_token_subtype(ttype, other): + """ + Return True if ``ttype`` is a subtype of ``other``. + + exists for backwards compatibility. use ``ttype in other`` now. + """ + return ttype in other + + +def string_to_tokentype(s): + """ + Convert a string into a token type:: + + >>> string_to_token('String.Double') + Token.Literal.String.Double + >>> string_to_token('Token.Literal.Number') + Token.Literal.Number + >>> string_to_token('') + Token + + Tokens that are already tokens are returned unchanged: + + >>> string_to_token(String) + Token.Literal.String + """ + if isinstance(s, _TokenType): + return s + if not s: + return Token + node = Token + for item in s.split('.'): + node = getattr(node, item) + return node + + +# Map standard token types to short names, used in CSS class naming. +# If you add a new item, please be sure to run this file to perform +# a consistency check for duplicate values. +STANDARD_TYPES = { + Token: '', + + Text: '', + Whitespace: 'w', + Escape: 'esc', + Error: 'err', + Other: 'x', + + Keyword: 'k', + Keyword.Constant: 'kc', + Keyword.Declaration: 'kd', + Keyword.Namespace: 'kn', + Keyword.Pseudo: 'kp', + Keyword.Reserved: 'kr', + Keyword.Type: 'kt', + + Name: 'n', + Name.Attribute: 'na', + Name.Builtin: 'nb', + Name.Builtin.Pseudo: 'bp', + Name.Class: 'nc', + Name.Constant: 'no', + Name.Decorator: 'nd', + Name.Entity: 'ni', + Name.Exception: 'ne', + Name.Function: 'nf', + Name.Function.Magic: 'fm', + Name.Property: 'py', + Name.Label: 'nl', + Name.Namespace: 'nn', + Name.Other: 'nx', + Name.Tag: 'nt', + Name.Variable: 'nv', + Name.Variable.Class: 'vc', + Name.Variable.Global: 'vg', + Name.Variable.Instance: 'vi', + Name.Variable.Magic: 'vm', + + Literal: 'l', + Literal.Date: 'ld', + + String: 's', + String.Affix: 'sa', + String.Backtick: 'sb', + String.Char: 'sc', + String.Delimiter: 'dl', + String.Doc: 'sd', + String.Double: 's2', + String.Escape: 'se', + String.Heredoc: 'sh', + String.Interpol: 'si', + String.Other: 'sx', + String.Regex: 'sr', + String.Single: 's1', + String.Symbol: 'ss', + + Number: 'm', + Number.Bin: 'mb', + Number.Float: 'mf', + Number.Hex: 'mh', + Number.Integer: 'mi', + Number.Integer.Long: 'il', + Number.Oct: 'mo', + + Operator: 'o', + Operator.Word: 'ow', + + Punctuation: 'p', + Punctuation.Marker: 'pm', + + Comment: 'c', + Comment.Hashbang: 'ch', + Comment.Multiline: 'cm', + Comment.Preproc: 'cp', + Comment.PreprocFile: 'cpf', + Comment.Single: 'c1', + Comment.Special: 'cs', + + Generic: 'g', + Generic.Deleted: 'gd', + Generic.Emph: 'ge', + Generic.Error: 'gr', + Generic.Heading: 'gh', + Generic.Inserted: 'gi', + Generic.Output: 'go', + Generic.Prompt: 'gp', + Generic.Strong: 'gs', + Generic.Subheading: 'gu', + Generic.EmphStrong: 'ges', + Generic.Traceback: 'gt', +} diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/unistring.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/unistring.py new file mode 100644 index 0000000..2a326df --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/unistring.py @@ -0,0 +1,153 @@ +""" + pygments.unistring + ~~~~~~~~~~~~~~~~~~ + + Strings of all Unicode characters of a certain category. + Used for matching in Unicode-aware languages. Run to regenerate. + + Inspired by chartypes_create.py from the MoinMoin project. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +Cc = '\x00-\x1f\x7f-\x9f' + +Cf = '\xad\u0600-\u0605\u061c\u06dd\u070f\u08e2\u180e\u200b-\u200f\u202a-\u202e\u2060-\u2064\u2066-\u206f\ufeff\ufff9-\ufffb\U000110bd\U000110cd\U0001bca0-\U0001bca3\U0001d173-\U0001d17a\U000e0001\U000e0020-\U000e007f' + +Cn = '\u0378-\u0379\u0380-\u0383\u038b\u038d\u03a2\u0530\u0557-\u0558\u058b-\u058c\u0590\u05c8-\u05cf\u05eb-\u05ee\u05f5-\u05ff\u061d\u070e\u074b-\u074c\u07b2-\u07bf\u07fb-\u07fc\u082e-\u082f\u083f\u085c-\u085d\u085f\u086b-\u089f\u08b5\u08be-\u08d2\u0984\u098d-\u098e\u0991-\u0992\u09a9\u09b1\u09b3-\u09b5\u09ba-\u09bb\u09c5-\u09c6\u09c9-\u09ca\u09cf-\u09d6\u09d8-\u09db\u09de\u09e4-\u09e5\u09ff-\u0a00\u0a04\u0a0b-\u0a0e\u0a11-\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a-\u0a3b\u0a3d\u0a43-\u0a46\u0a49-\u0a4a\u0a4e-\u0a50\u0a52-\u0a58\u0a5d\u0a5f-\u0a65\u0a77-\u0a80\u0a84\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba-\u0abb\u0ac6\u0aca\u0ace-\u0acf\u0ad1-\u0adf\u0ae4-\u0ae5\u0af2-\u0af8\u0b00\u0b04\u0b0d-\u0b0e\u0b11-\u0b12\u0b29\u0b31\u0b34\u0b3a-\u0b3b\u0b45-\u0b46\u0b49-\u0b4a\u0b4e-\u0b55\u0b58-\u0b5b\u0b5e\u0b64-\u0b65\u0b78-\u0b81\u0b84\u0b8b-\u0b8d\u0b91\u0b96-\u0b98\u0b9b\u0b9d\u0ba0-\u0ba2\u0ba5-\u0ba7\u0bab-\u0bad\u0bba-\u0bbd\u0bc3-\u0bc5\u0bc9\u0bce-\u0bcf\u0bd1-\u0bd6\u0bd8-\u0be5\u0bfb-\u0bff\u0c0d\u0c11\u0c29\u0c3a-\u0c3c\u0c45\u0c49\u0c4e-\u0c54\u0c57\u0c5b-\u0c5f\u0c64-\u0c65\u0c70-\u0c77\u0c8d\u0c91\u0ca9\u0cb4\u0cba-\u0cbb\u0cc5\u0cc9\u0cce-\u0cd4\u0cd7-\u0cdd\u0cdf\u0ce4-\u0ce5\u0cf0\u0cf3-\u0cff\u0d04\u0d0d\u0d11\u0d45\u0d49\u0d50-\u0d53\u0d64-\u0d65\u0d80-\u0d81\u0d84\u0d97-\u0d99\u0db2\u0dbc\u0dbe-\u0dbf\u0dc7-\u0dc9\u0dcb-\u0dce\u0dd5\u0dd7\u0de0-\u0de5\u0df0-\u0df1\u0df5-\u0e00\u0e3b-\u0e3e\u0e5c-\u0e80\u0e83\u0e85-\u0e86\u0e89\u0e8b-\u0e8c\u0e8e-\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8-\u0ea9\u0eac\u0eba\u0ebe-\u0ebf\u0ec5\u0ec7\u0ece-\u0ecf\u0eda-\u0edb\u0ee0-\u0eff\u0f48\u0f6d-\u0f70\u0f98\u0fbd\u0fcd\u0fdb-\u0fff\u10c6\u10c8-\u10cc\u10ce-\u10cf\u1249\u124e-\u124f\u1257\u1259\u125e-\u125f\u1289\u128e-\u128f\u12b1\u12b6-\u12b7\u12bf\u12c1\u12c6-\u12c7\u12d7\u1311\u1316-\u1317\u135b-\u135c\u137d-\u137f\u139a-\u139f\u13f6-\u13f7\u13fe-\u13ff\u169d-\u169f\u16f9-\u16ff\u170d\u1715-\u171f\u1737-\u173f\u1754-\u175f\u176d\u1771\u1774-\u177f\u17de-\u17df\u17ea-\u17ef\u17fa-\u17ff\u180f\u181a-\u181f\u1879-\u187f\u18ab-\u18af\u18f6-\u18ff\u191f\u192c-\u192f\u193c-\u193f\u1941-\u1943\u196e-\u196f\u1975-\u197f\u19ac-\u19af\u19ca-\u19cf\u19db-\u19dd\u1a1c-\u1a1d\u1a5f\u1a7d-\u1a7e\u1a8a-\u1a8f\u1a9a-\u1a9f\u1aae-\u1aaf\u1abf-\u1aff\u1b4c-\u1b4f\u1b7d-\u1b7f\u1bf4-\u1bfb\u1c38-\u1c3a\u1c4a-\u1c4c\u1c89-\u1c8f\u1cbb-\u1cbc\u1cc8-\u1ccf\u1cfa-\u1cff\u1dfa\u1f16-\u1f17\u1f1e-\u1f1f\u1f46-\u1f47\u1f4e-\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e-\u1f7f\u1fb5\u1fc5\u1fd4-\u1fd5\u1fdc\u1ff0-\u1ff1\u1ff5\u1fff\u2065\u2072-\u2073\u208f\u209d-\u209f\u20c0-\u20cf\u20f1-\u20ff\u218c-\u218f\u2427-\u243f\u244b-\u245f\u2b74-\u2b75\u2b96-\u2b97\u2bc9\u2bff\u2c2f\u2c5f\u2cf4-\u2cf8\u2d26\u2d28-\u2d2c\u2d2e-\u2d2f\u2d68-\u2d6e\u2d71-\u2d7e\u2d97-\u2d9f\u2da7\u2daf\u2db7\u2dbf\u2dc7\u2dcf\u2dd7\u2ddf\u2e4f-\u2e7f\u2e9a\u2ef4-\u2eff\u2fd6-\u2fef\u2ffc-\u2fff\u3040\u3097-\u3098\u3100-\u3104\u3130\u318f\u31bb-\u31bf\u31e4-\u31ef\u321f\u32ff\u4db6-\u4dbf\u9ff0-\u9fff\ua48d-\ua48f\ua4c7-\ua4cf\ua62c-\ua63f\ua6f8-\ua6ff\ua7ba-\ua7f6\ua82c-\ua82f\ua83a-\ua83f\ua878-\ua87f\ua8c6-\ua8cd\ua8da-\ua8df\ua954-\ua95e\ua97d-\ua97f\ua9ce\ua9da-\ua9dd\ua9ff\uaa37-\uaa3f\uaa4e-\uaa4f\uaa5a-\uaa5b\uaac3-\uaada\uaaf7-\uab00\uab07-\uab08\uab0f-\uab10\uab17-\uab1f\uab27\uab2f\uab66-\uab6f\uabee-\uabef\uabfa-\uabff\ud7a4-\ud7af\ud7c7-\ud7ca\ud7fc-\ud7ff\ufa6e-\ufa6f\ufada-\ufaff\ufb07-\ufb12\ufb18-\ufb1c\ufb37\ufb3d\ufb3f\ufb42\ufb45\ufbc2-\ufbd2\ufd40-\ufd4f\ufd90-\ufd91\ufdc8-\ufdef\ufdfe-\ufdff\ufe1a-\ufe1f\ufe53\ufe67\ufe6c-\ufe6f\ufe75\ufefd-\ufefe\uff00\uffbf-\uffc1\uffc8-\uffc9\uffd0-\uffd1\uffd8-\uffd9\uffdd-\uffdf\uffe7\uffef-\ufff8\ufffe-\uffff\U0001000c\U00010027\U0001003b\U0001003e\U0001004e-\U0001004f\U0001005e-\U0001007f\U000100fb-\U000100ff\U00010103-\U00010106\U00010134-\U00010136\U0001018f\U0001019c-\U0001019f\U000101a1-\U000101cf\U000101fe-\U0001027f\U0001029d-\U0001029f\U000102d1-\U000102df\U000102fc-\U000102ff\U00010324-\U0001032c\U0001034b-\U0001034f\U0001037b-\U0001037f\U0001039e\U000103c4-\U000103c7\U000103d6-\U000103ff\U0001049e-\U0001049f\U000104aa-\U000104af\U000104d4-\U000104d7\U000104fc-\U000104ff\U00010528-\U0001052f\U00010564-\U0001056e\U00010570-\U000105ff\U00010737-\U0001073f\U00010756-\U0001075f\U00010768-\U000107ff\U00010806-\U00010807\U00010809\U00010836\U00010839-\U0001083b\U0001083d-\U0001083e\U00010856\U0001089f-\U000108a6\U000108b0-\U000108df\U000108f3\U000108f6-\U000108fa\U0001091c-\U0001091e\U0001093a-\U0001093e\U00010940-\U0001097f\U000109b8-\U000109bb\U000109d0-\U000109d1\U00010a04\U00010a07-\U00010a0b\U00010a14\U00010a18\U00010a36-\U00010a37\U00010a3b-\U00010a3e\U00010a49-\U00010a4f\U00010a59-\U00010a5f\U00010aa0-\U00010abf\U00010ae7-\U00010aea\U00010af7-\U00010aff\U00010b36-\U00010b38\U00010b56-\U00010b57\U00010b73-\U00010b77\U00010b92-\U00010b98\U00010b9d-\U00010ba8\U00010bb0-\U00010bff\U00010c49-\U00010c7f\U00010cb3-\U00010cbf\U00010cf3-\U00010cf9\U00010d28-\U00010d2f\U00010d3a-\U00010e5f\U00010e7f-\U00010eff\U00010f28-\U00010f2f\U00010f5a-\U00010fff\U0001104e-\U00011051\U00011070-\U0001107e\U000110c2-\U000110cc\U000110ce-\U000110cf\U000110e9-\U000110ef\U000110fa-\U000110ff\U00011135\U00011147-\U0001114f\U00011177-\U0001117f\U000111ce-\U000111cf\U000111e0\U000111f5-\U000111ff\U00011212\U0001123f-\U0001127f\U00011287\U00011289\U0001128e\U0001129e\U000112aa-\U000112af\U000112eb-\U000112ef\U000112fa-\U000112ff\U00011304\U0001130d-\U0001130e\U00011311-\U00011312\U00011329\U00011331\U00011334\U0001133a\U00011345-\U00011346\U00011349-\U0001134a\U0001134e-\U0001134f\U00011351-\U00011356\U00011358-\U0001135c\U00011364-\U00011365\U0001136d-\U0001136f\U00011375-\U000113ff\U0001145a\U0001145c\U0001145f-\U0001147f\U000114c8-\U000114cf\U000114da-\U0001157f\U000115b6-\U000115b7\U000115de-\U000115ff\U00011645-\U0001164f\U0001165a-\U0001165f\U0001166d-\U0001167f\U000116b8-\U000116bf\U000116ca-\U000116ff\U0001171b-\U0001171c\U0001172c-\U0001172f\U00011740-\U000117ff\U0001183c-\U0001189f\U000118f3-\U000118fe\U00011900-\U000119ff\U00011a48-\U00011a4f\U00011a84-\U00011a85\U00011aa3-\U00011abf\U00011af9-\U00011bff\U00011c09\U00011c37\U00011c46-\U00011c4f\U00011c6d-\U00011c6f\U00011c90-\U00011c91\U00011ca8\U00011cb7-\U00011cff\U00011d07\U00011d0a\U00011d37-\U00011d39\U00011d3b\U00011d3e\U00011d48-\U00011d4f\U00011d5a-\U00011d5f\U00011d66\U00011d69\U00011d8f\U00011d92\U00011d99-\U00011d9f\U00011daa-\U00011edf\U00011ef9-\U00011fff\U0001239a-\U000123ff\U0001246f\U00012475-\U0001247f\U00012544-\U00012fff\U0001342f-\U000143ff\U00014647-\U000167ff\U00016a39-\U00016a3f\U00016a5f\U00016a6a-\U00016a6d\U00016a70-\U00016acf\U00016aee-\U00016aef\U00016af6-\U00016aff\U00016b46-\U00016b4f\U00016b5a\U00016b62\U00016b78-\U00016b7c\U00016b90-\U00016e3f\U00016e9b-\U00016eff\U00016f45-\U00016f4f\U00016f7f-\U00016f8e\U00016fa0-\U00016fdf\U00016fe2-\U00016fff\U000187f2-\U000187ff\U00018af3-\U0001afff\U0001b11f-\U0001b16f\U0001b2fc-\U0001bbff\U0001bc6b-\U0001bc6f\U0001bc7d-\U0001bc7f\U0001bc89-\U0001bc8f\U0001bc9a-\U0001bc9b\U0001bca4-\U0001cfff\U0001d0f6-\U0001d0ff\U0001d127-\U0001d128\U0001d1e9-\U0001d1ff\U0001d246-\U0001d2df\U0001d2f4-\U0001d2ff\U0001d357-\U0001d35f\U0001d379-\U0001d3ff\U0001d455\U0001d49d\U0001d4a0-\U0001d4a1\U0001d4a3-\U0001d4a4\U0001d4a7-\U0001d4a8\U0001d4ad\U0001d4ba\U0001d4bc\U0001d4c4\U0001d506\U0001d50b-\U0001d50c\U0001d515\U0001d51d\U0001d53a\U0001d53f\U0001d545\U0001d547-\U0001d549\U0001d551\U0001d6a6-\U0001d6a7\U0001d7cc-\U0001d7cd\U0001da8c-\U0001da9a\U0001daa0\U0001dab0-\U0001dfff\U0001e007\U0001e019-\U0001e01a\U0001e022\U0001e025\U0001e02b-\U0001e7ff\U0001e8c5-\U0001e8c6\U0001e8d7-\U0001e8ff\U0001e94b-\U0001e94f\U0001e95a-\U0001e95d\U0001e960-\U0001ec70\U0001ecb5-\U0001edff\U0001ee04\U0001ee20\U0001ee23\U0001ee25-\U0001ee26\U0001ee28\U0001ee33\U0001ee38\U0001ee3a\U0001ee3c-\U0001ee41\U0001ee43-\U0001ee46\U0001ee48\U0001ee4a\U0001ee4c\U0001ee50\U0001ee53\U0001ee55-\U0001ee56\U0001ee58\U0001ee5a\U0001ee5c\U0001ee5e\U0001ee60\U0001ee63\U0001ee65-\U0001ee66\U0001ee6b\U0001ee73\U0001ee78\U0001ee7d\U0001ee7f\U0001ee8a\U0001ee9c-\U0001eea0\U0001eea4\U0001eeaa\U0001eebc-\U0001eeef\U0001eef2-\U0001efff\U0001f02c-\U0001f02f\U0001f094-\U0001f09f\U0001f0af-\U0001f0b0\U0001f0c0\U0001f0d0\U0001f0f6-\U0001f0ff\U0001f10d-\U0001f10f\U0001f16c-\U0001f16f\U0001f1ad-\U0001f1e5\U0001f203-\U0001f20f\U0001f23c-\U0001f23f\U0001f249-\U0001f24f\U0001f252-\U0001f25f\U0001f266-\U0001f2ff\U0001f6d5-\U0001f6df\U0001f6ed-\U0001f6ef\U0001f6fa-\U0001f6ff\U0001f774-\U0001f77f\U0001f7d9-\U0001f7ff\U0001f80c-\U0001f80f\U0001f848-\U0001f84f\U0001f85a-\U0001f85f\U0001f888-\U0001f88f\U0001f8ae-\U0001f8ff\U0001f90c-\U0001f90f\U0001f93f\U0001f971-\U0001f972\U0001f977-\U0001f979\U0001f97b\U0001f9a3-\U0001f9af\U0001f9ba-\U0001f9bf\U0001f9c3-\U0001f9cf\U0001fa00-\U0001fa5f\U0001fa6e-\U0001ffff\U0002a6d7-\U0002a6ff\U0002b735-\U0002b73f\U0002b81e-\U0002b81f\U0002cea2-\U0002ceaf\U0002ebe1-\U0002f7ff\U0002fa1e-\U000e0000\U000e0002-\U000e001f\U000e0080-\U000e00ff\U000e01f0-\U000effff\U000ffffe-\U000fffff\U0010fffe-\U0010ffff' + +Co = '\ue000-\uf8ff\U000f0000-\U000ffffd\U00100000-\U0010fffd' + +Cs = '\ud800-\udbff\\\udc00\udc01-\udfff' + +Ll = 'a-z\xb5\xdf-\xf6\xf8-\xff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137-\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148-\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e-\u0180\u0183\u0185\u0188\u018c-\u018d\u0192\u0195\u0199-\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa-\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9-\u01ba\u01bd-\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc-\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef-\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233-\u0239\u023c\u023f-\u0240\u0242\u0247\u0249\u024b\u024d\u024f-\u0293\u0295-\u02af\u0371\u0373\u0377\u037b-\u037d\u0390\u03ac-\u03ce\u03d0-\u03d1\u03d5-\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef-\u03f3\u03f5\u03f8\u03fb-\u03fc\u0430-\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce-\u04cf\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u04fb\u04fd\u04ff\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0511\u0513\u0515\u0517\u0519\u051b\u051d\u051f\u0521\u0523\u0525\u0527\u0529\u052b\u052d\u052f\u0560-\u0588\u10d0-\u10fa\u10fd-\u10ff\u13f8-\u13fd\u1c80-\u1c88\u1d00-\u1d2b\u1d6b-\u1d77\u1d79-\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95-\u1e9d\u1e9f\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1efb\u1efd\u1eff-\u1f07\u1f10-\u1f15\u1f20-\u1f27\u1f30-\u1f37\u1f40-\u1f45\u1f50-\u1f57\u1f60-\u1f67\u1f70-\u1f7d\u1f80-\u1f87\u1f90-\u1f97\u1fa0-\u1fa7\u1fb0-\u1fb4\u1fb6-\u1fb7\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fc7\u1fd0-\u1fd3\u1fd6-\u1fd7\u1fe0-\u1fe7\u1ff2-\u1ff4\u1ff6-\u1ff7\u210a\u210e-\u210f\u2113\u212f\u2134\u2139\u213c-\u213d\u2146-\u2149\u214e\u2184\u2c30-\u2c5e\u2c61\u2c65-\u2c66\u2c68\u2c6a\u2c6c\u2c71\u2c73-\u2c74\u2c76-\u2c7b\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3-\u2ce4\u2cec\u2cee\u2cf3\u2d00-\u2d25\u2d27\u2d2d\ua641\ua643\ua645\ua647\ua649\ua64b\ua64d\ua64f\ua651\ua653\ua655\ua657\ua659\ua65b\ua65d\ua65f\ua661\ua663\ua665\ua667\ua669\ua66b\ua66d\ua681\ua683\ua685\ua687\ua689\ua68b\ua68d\ua68f\ua691\ua693\ua695\ua697\ua699\ua69b\ua723\ua725\ua727\ua729\ua72b\ua72d\ua72f-\ua731\ua733\ua735\ua737\ua739\ua73b\ua73d\ua73f\ua741\ua743\ua745\ua747\ua749\ua74b\ua74d\ua74f\ua751\ua753\ua755\ua757\ua759\ua75b\ua75d\ua75f\ua761\ua763\ua765\ua767\ua769\ua76b\ua76d\ua76f\ua771-\ua778\ua77a\ua77c\ua77f\ua781\ua783\ua785\ua787\ua78c\ua78e\ua791\ua793-\ua795\ua797\ua799\ua79b\ua79d\ua79f\ua7a1\ua7a3\ua7a5\ua7a7\ua7a9\ua7af\ua7b5\ua7b7\ua7b9\ua7fa\uab30-\uab5a\uab60-\uab65\uab70-\uabbf\ufb00-\ufb06\ufb13-\ufb17\uff41-\uff5a\U00010428-\U0001044f\U000104d8-\U000104fb\U00010cc0-\U00010cf2\U000118c0-\U000118df\U00016e60-\U00016e7f\U0001d41a-\U0001d433\U0001d44e-\U0001d454\U0001d456-\U0001d467\U0001d482-\U0001d49b\U0001d4b6-\U0001d4b9\U0001d4bb\U0001d4bd-\U0001d4c3\U0001d4c5-\U0001d4cf\U0001d4ea-\U0001d503\U0001d51e-\U0001d537\U0001d552-\U0001d56b\U0001d586-\U0001d59f\U0001d5ba-\U0001d5d3\U0001d5ee-\U0001d607\U0001d622-\U0001d63b\U0001d656-\U0001d66f\U0001d68a-\U0001d6a5\U0001d6c2-\U0001d6da\U0001d6dc-\U0001d6e1\U0001d6fc-\U0001d714\U0001d716-\U0001d71b\U0001d736-\U0001d74e\U0001d750-\U0001d755\U0001d770-\U0001d788\U0001d78a-\U0001d78f\U0001d7aa-\U0001d7c2\U0001d7c4-\U0001d7c9\U0001d7cb\U0001e922-\U0001e943' + +Lm = '\u02b0-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0374\u037a\u0559\u0640\u06e5-\u06e6\u07f4-\u07f5\u07fa\u081a\u0824\u0828\u0971\u0e46\u0ec6\u10fc\u17d7\u1843\u1aa7\u1c78-\u1c7d\u1d2c-\u1d6a\u1d78\u1d9b-\u1dbf\u2071\u207f\u2090-\u209c\u2c7c-\u2c7d\u2d6f\u2e2f\u3005\u3031-\u3035\u303b\u309d-\u309e\u30fc-\u30fe\ua015\ua4f8-\ua4fd\ua60c\ua67f\ua69c-\ua69d\ua717-\ua71f\ua770\ua788\ua7f8-\ua7f9\ua9cf\ua9e6\uaa70\uaadd\uaaf3-\uaaf4\uab5c-\uab5f\uff70\uff9e-\uff9f\U00016b40-\U00016b43\U00016f93-\U00016f9f\U00016fe0-\U00016fe1' + +Lo = '\xaa\xba\u01bb\u01c0-\u01c3\u0294\u05d0-\u05ea\u05ef-\u05f2\u0620-\u063f\u0641-\u064a\u066e-\u066f\u0671-\u06d3\u06d5\u06ee-\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u0800-\u0815\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd\u0904-\u0939\u093d\u0950\u0958-\u0961\u0972-\u0980\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc-\u09dd\u09df-\u09e1\u09f0-\u09f1\u09fc\u0a05-\u0a0a\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36\u0a38-\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0-\u0ae1\u0af9\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32-\u0b33\u0b35-\u0b39\u0b3d\u0b5c-\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60-\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0-\u0ce1\u0cf1-\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32-\u0e33\u0e40-\u0e45\u0e81-\u0e82\u0e84\u0e87-\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa-\u0eab\u0ead-\u0eb0\u0eb2-\u0eb3\u0ebd\u0ec0-\u0ec4\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065-\u1066\u106e-\u1070\u1075-\u1081\u108e\u1100-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16f1-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17dc\u1820-\u1842\u1844-\u1878\u1880-\u1884\u1887-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae-\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c77\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5-\u1cf6\u2135-\u2138\u2d30-\u2d67\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3006\u303c\u3041-\u3096\u309f\u30a1-\u30fa\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua014\ua016-\ua48c\ua4d0-\ua4f7\ua500-\ua60b\ua610-\ua61f\ua62a-\ua62b\ua66e\ua6a0-\ua6e5\ua78f\ua7f7\ua7fb-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd-\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9e0-\ua9e4\ua9e7-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa6f\uaa71-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5-\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadc\uaae0-\uaaea\uaaf2\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff66-\uff6f\uff71-\uff9d\uffa0-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc\U00010000-\U0001000b\U0001000d-\U00010026\U00010028-\U0001003a\U0001003c-\U0001003d\U0001003f-\U0001004d\U00010050-\U0001005d\U00010080-\U000100fa\U00010280-\U0001029c\U000102a0-\U000102d0\U00010300-\U0001031f\U0001032d-\U00010340\U00010342-\U00010349\U00010350-\U00010375\U00010380-\U0001039d\U000103a0-\U000103c3\U000103c8-\U000103cf\U00010450-\U0001049d\U00010500-\U00010527\U00010530-\U00010563\U00010600-\U00010736\U00010740-\U00010755\U00010760-\U00010767\U00010800-\U00010805\U00010808\U0001080a-\U00010835\U00010837-\U00010838\U0001083c\U0001083f-\U00010855\U00010860-\U00010876\U00010880-\U0001089e\U000108e0-\U000108f2\U000108f4-\U000108f5\U00010900-\U00010915\U00010920-\U00010939\U00010980-\U000109b7\U000109be-\U000109bf\U00010a00\U00010a10-\U00010a13\U00010a15-\U00010a17\U00010a19-\U00010a35\U00010a60-\U00010a7c\U00010a80-\U00010a9c\U00010ac0-\U00010ac7\U00010ac9-\U00010ae4\U00010b00-\U00010b35\U00010b40-\U00010b55\U00010b60-\U00010b72\U00010b80-\U00010b91\U00010c00-\U00010c48\U00010d00-\U00010d23\U00010f00-\U00010f1c\U00010f27\U00010f30-\U00010f45\U00011003-\U00011037\U00011083-\U000110af\U000110d0-\U000110e8\U00011103-\U00011126\U00011144\U00011150-\U00011172\U00011176\U00011183-\U000111b2\U000111c1-\U000111c4\U000111da\U000111dc\U00011200-\U00011211\U00011213-\U0001122b\U00011280-\U00011286\U00011288\U0001128a-\U0001128d\U0001128f-\U0001129d\U0001129f-\U000112a8\U000112b0-\U000112de\U00011305-\U0001130c\U0001130f-\U00011310\U00011313-\U00011328\U0001132a-\U00011330\U00011332-\U00011333\U00011335-\U00011339\U0001133d\U00011350\U0001135d-\U00011361\U00011400-\U00011434\U00011447-\U0001144a\U00011480-\U000114af\U000114c4-\U000114c5\U000114c7\U00011580-\U000115ae\U000115d8-\U000115db\U00011600-\U0001162f\U00011644\U00011680-\U000116aa\U00011700-\U0001171a\U00011800-\U0001182b\U000118ff\U00011a00\U00011a0b-\U00011a32\U00011a3a\U00011a50\U00011a5c-\U00011a83\U00011a86-\U00011a89\U00011a9d\U00011ac0-\U00011af8\U00011c00-\U00011c08\U00011c0a-\U00011c2e\U00011c40\U00011c72-\U00011c8f\U00011d00-\U00011d06\U00011d08-\U00011d09\U00011d0b-\U00011d30\U00011d46\U00011d60-\U00011d65\U00011d67-\U00011d68\U00011d6a-\U00011d89\U00011d98\U00011ee0-\U00011ef2\U00012000-\U00012399\U00012480-\U00012543\U00013000-\U0001342e\U00014400-\U00014646\U00016800-\U00016a38\U00016a40-\U00016a5e\U00016ad0-\U00016aed\U00016b00-\U00016b2f\U00016b63-\U00016b77\U00016b7d-\U00016b8f\U00016f00-\U00016f44\U00016f50\U00017000-\U000187f1\U00018800-\U00018af2\U0001b000-\U0001b11e\U0001b170-\U0001b2fb\U0001bc00-\U0001bc6a\U0001bc70-\U0001bc7c\U0001bc80-\U0001bc88\U0001bc90-\U0001bc99\U0001e800-\U0001e8c4\U0001ee00-\U0001ee03\U0001ee05-\U0001ee1f\U0001ee21-\U0001ee22\U0001ee24\U0001ee27\U0001ee29-\U0001ee32\U0001ee34-\U0001ee37\U0001ee39\U0001ee3b\U0001ee42\U0001ee47\U0001ee49\U0001ee4b\U0001ee4d-\U0001ee4f\U0001ee51-\U0001ee52\U0001ee54\U0001ee57\U0001ee59\U0001ee5b\U0001ee5d\U0001ee5f\U0001ee61-\U0001ee62\U0001ee64\U0001ee67-\U0001ee6a\U0001ee6c-\U0001ee72\U0001ee74-\U0001ee77\U0001ee79-\U0001ee7c\U0001ee7e\U0001ee80-\U0001ee89\U0001ee8b-\U0001ee9b\U0001eea1-\U0001eea3\U0001eea5-\U0001eea9\U0001eeab-\U0001eebb\U00020000-\U0002a6d6\U0002a700-\U0002b734\U0002b740-\U0002b81d\U0002b820-\U0002cea1\U0002ceb0-\U0002ebe0\U0002f800-\U0002fa1d' + +Lt = '\u01c5\u01c8\u01cb\u01f2\u1f88-\u1f8f\u1f98-\u1f9f\u1fa8-\u1faf\u1fbc\u1fcc\u1ffc' + +Lu = 'A-Z\xc0-\xd6\xd8-\xde\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178-\u0179\u017b\u017d\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018b\u018e-\u0191\u0193-\u0194\u0196-\u0198\u019c-\u019d\u019f-\u01a0\u01a2\u01a4\u01a6-\u01a7\u01a9\u01ac\u01ae-\u01af\u01b1-\u01b3\u01b5\u01b7-\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6-\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a-\u023b\u023d-\u023e\u0241\u0243-\u0246\u0248\u024a\u024c\u024e\u0370\u0372\u0376\u037f\u0386\u0388-\u038a\u038c\u038e-\u038f\u0391-\u03a1\u03a3-\u03ab\u03cf\u03d2-\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9-\u03fa\u03fd-\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0-\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u04fa\u04fc\u04fe\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0510\u0512\u0514\u0516\u0518\u051a\u051c\u051e\u0520\u0522\u0524\u0526\u0528\u052a\u052c\u052e\u0531-\u0556\u10a0-\u10c5\u10c7\u10cd\u13a0-\u13f5\u1c90-\u1cba\u1cbd-\u1cbf\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1e9e\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1efa\u1efc\u1efe\u1f08-\u1f0f\u1f18-\u1f1d\u1f28-\u1f2f\u1f38-\u1f3f\u1f48-\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68-\u1f6f\u1fb8-\u1fbb\u1fc8-\u1fcb\u1fd8-\u1fdb\u1fe8-\u1fec\u1ff8-\u1ffb\u2102\u2107\u210b-\u210d\u2110-\u2112\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u2130-\u2133\u213e-\u213f\u2145\u2183\u2c00-\u2c2e\u2c60\u2c62-\u2c64\u2c67\u2c69\u2c6b\u2c6d-\u2c70\u2c72\u2c75\u2c7e-\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\u2ceb\u2ced\u2cf2\ua640\ua642\ua644\ua646\ua648\ua64a\ua64c\ua64e\ua650\ua652\ua654\ua656\ua658\ua65a\ua65c\ua65e\ua660\ua662\ua664\ua666\ua668\ua66a\ua66c\ua680\ua682\ua684\ua686\ua688\ua68a\ua68c\ua68e\ua690\ua692\ua694\ua696\ua698\ua69a\ua722\ua724\ua726\ua728\ua72a\ua72c\ua72e\ua732\ua734\ua736\ua738\ua73a\ua73c\ua73e\ua740\ua742\ua744\ua746\ua748\ua74a\ua74c\ua74e\ua750\ua752\ua754\ua756\ua758\ua75a\ua75c\ua75e\ua760\ua762\ua764\ua766\ua768\ua76a\ua76c\ua76e\ua779\ua77b\ua77d-\ua77e\ua780\ua782\ua784\ua786\ua78b\ua78d\ua790\ua792\ua796\ua798\ua79a\ua79c\ua79e\ua7a0\ua7a2\ua7a4\ua7a6\ua7a8\ua7aa-\ua7ae\ua7b0-\ua7b4\ua7b6\ua7b8\uff21-\uff3a\U00010400-\U00010427\U000104b0-\U000104d3\U00010c80-\U00010cb2\U000118a0-\U000118bf\U00016e40-\U00016e5f\U0001d400-\U0001d419\U0001d434-\U0001d44d\U0001d468-\U0001d481\U0001d49c\U0001d49e-\U0001d49f\U0001d4a2\U0001d4a5-\U0001d4a6\U0001d4a9-\U0001d4ac\U0001d4ae-\U0001d4b5\U0001d4d0-\U0001d4e9\U0001d504-\U0001d505\U0001d507-\U0001d50a\U0001d50d-\U0001d514\U0001d516-\U0001d51c\U0001d538-\U0001d539\U0001d53b-\U0001d53e\U0001d540-\U0001d544\U0001d546\U0001d54a-\U0001d550\U0001d56c-\U0001d585\U0001d5a0-\U0001d5b9\U0001d5d4-\U0001d5ed\U0001d608-\U0001d621\U0001d63c-\U0001d655\U0001d670-\U0001d689\U0001d6a8-\U0001d6c0\U0001d6e2-\U0001d6fa\U0001d71c-\U0001d734\U0001d756-\U0001d76e\U0001d790-\U0001d7a8\U0001d7ca\U0001e900-\U0001e921' + +Mc = '\u0903\u093b\u093e-\u0940\u0949-\u094c\u094e-\u094f\u0982-\u0983\u09be-\u09c0\u09c7-\u09c8\u09cb-\u09cc\u09d7\u0a03\u0a3e-\u0a40\u0a83\u0abe-\u0ac0\u0ac9\u0acb-\u0acc\u0b02-\u0b03\u0b3e\u0b40\u0b47-\u0b48\u0b4b-\u0b4c\u0b57\u0bbe-\u0bbf\u0bc1-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcc\u0bd7\u0c01-\u0c03\u0c41-\u0c44\u0c82-\u0c83\u0cbe\u0cc0-\u0cc4\u0cc7-\u0cc8\u0cca-\u0ccb\u0cd5-\u0cd6\u0d02-\u0d03\u0d3e-\u0d40\u0d46-\u0d48\u0d4a-\u0d4c\u0d57\u0d82-\u0d83\u0dcf-\u0dd1\u0dd8-\u0ddf\u0df2-\u0df3\u0f3e-\u0f3f\u0f7f\u102b-\u102c\u1031\u1038\u103b-\u103c\u1056-\u1057\u1062-\u1064\u1067-\u106d\u1083-\u1084\u1087-\u108c\u108f\u109a-\u109c\u17b6\u17be-\u17c5\u17c7-\u17c8\u1923-\u1926\u1929-\u192b\u1930-\u1931\u1933-\u1938\u1a19-\u1a1a\u1a55\u1a57\u1a61\u1a63-\u1a64\u1a6d-\u1a72\u1b04\u1b35\u1b3b\u1b3d-\u1b41\u1b43-\u1b44\u1b82\u1ba1\u1ba6-\u1ba7\u1baa\u1be7\u1bea-\u1bec\u1bee\u1bf2-\u1bf3\u1c24-\u1c2b\u1c34-\u1c35\u1ce1\u1cf2-\u1cf3\u1cf7\u302e-\u302f\ua823-\ua824\ua827\ua880-\ua881\ua8b4-\ua8c3\ua952-\ua953\ua983\ua9b4-\ua9b5\ua9ba-\ua9bb\ua9bd-\ua9c0\uaa2f-\uaa30\uaa33-\uaa34\uaa4d\uaa7b\uaa7d\uaaeb\uaaee-\uaaef\uaaf5\uabe3-\uabe4\uabe6-\uabe7\uabe9-\uabea\uabec\U00011000\U00011002\U00011082\U000110b0-\U000110b2\U000110b7-\U000110b8\U0001112c\U00011145-\U00011146\U00011182\U000111b3-\U000111b5\U000111bf-\U000111c0\U0001122c-\U0001122e\U00011232-\U00011233\U00011235\U000112e0-\U000112e2\U00011302-\U00011303\U0001133e-\U0001133f\U00011341-\U00011344\U00011347-\U00011348\U0001134b-\U0001134d\U00011357\U00011362-\U00011363\U00011435-\U00011437\U00011440-\U00011441\U00011445\U000114b0-\U000114b2\U000114b9\U000114bb-\U000114be\U000114c1\U000115af-\U000115b1\U000115b8-\U000115bb\U000115be\U00011630-\U00011632\U0001163b-\U0001163c\U0001163e\U000116ac\U000116ae-\U000116af\U000116b6\U00011720-\U00011721\U00011726\U0001182c-\U0001182e\U00011838\U00011a39\U00011a57-\U00011a58\U00011a97\U00011c2f\U00011c3e\U00011ca9\U00011cb1\U00011cb4\U00011d8a-\U00011d8e\U00011d93-\U00011d94\U00011d96\U00011ef5-\U00011ef6\U00016f51-\U00016f7e\U0001d165-\U0001d166\U0001d16d-\U0001d172' + +Me = '\u0488-\u0489\u1abe\u20dd-\u20e0\u20e2-\u20e4\ua670-\ua672' + +Mn = '\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7-\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d3-\u08e1\u08e3-\u0902\u093a\u093c\u0941-\u0948\u094d\u0951-\u0957\u0962-\u0963\u0981\u09bc\u09c1-\u09c4\u09cd\u09e2-\u09e3\u09fe\u0a01-\u0a02\u0a3c\u0a41-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a70-\u0a71\u0a75\u0a81-\u0a82\u0abc\u0ac1-\u0ac5\u0ac7-\u0ac8\u0acd\u0ae2-\u0ae3\u0afa-\u0aff\u0b01\u0b3c\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b62-\u0b63\u0b82\u0bc0\u0bcd\u0c00\u0c04\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c62-\u0c63\u0c81\u0cbc\u0cbf\u0cc6\u0ccc-\u0ccd\u0ce2-\u0ce3\u0d00-\u0d01\u0d3b-\u0d3c\u0d41-\u0d44\u0d4d\u0d62-\u0d63\u0dca\u0dd2-\u0dd4\u0dd6\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb-\u0ebc\u0ec8-\u0ecd\u0f18-\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039-\u103a\u103d-\u103e\u1058-\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085-\u1086\u108d\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17b4-\u17b5\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u1885-\u1886\u18a9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193b\u1a17-\u1a18\u1a1b\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1ab0-\u1abd\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80-\u1b81\u1ba2-\u1ba5\u1ba8-\u1ba9\u1bab-\u1bad\u1be6\u1be8-\u1be9\u1bed\u1bef-\u1bf1\u1c2c-\u1c33\u1c36-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1cf4\u1cf8-\u1cf9\u1dc0-\u1df9\u1dfb-\u1dff\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302d\u3099-\u309a\ua66f\ua674-\ua67d\ua69e-\ua69f\ua6f0-\ua6f1\ua802\ua806\ua80b\ua825-\ua826\ua8c4-\ua8c5\ua8e0-\ua8f1\ua8ff\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\ua9e5\uaa29-\uaa2e\uaa31-\uaa32\uaa35-\uaa36\uaa43\uaa4c\uaa7c\uaab0\uaab2-\uaab4\uaab7-\uaab8\uaabe-\uaabf\uaac1\uaaec-\uaaed\uaaf6\uabe5\uabe8\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\U000101fd\U000102e0\U00010376-\U0001037a\U00010a01-\U00010a03\U00010a05-\U00010a06\U00010a0c-\U00010a0f\U00010a38-\U00010a3a\U00010a3f\U00010ae5-\U00010ae6\U00010d24-\U00010d27\U00010f46-\U00010f50\U00011001\U00011038-\U00011046\U0001107f-\U00011081\U000110b3-\U000110b6\U000110b9-\U000110ba\U00011100-\U00011102\U00011127-\U0001112b\U0001112d-\U00011134\U00011173\U00011180-\U00011181\U000111b6-\U000111be\U000111c9-\U000111cc\U0001122f-\U00011231\U00011234\U00011236-\U00011237\U0001123e\U000112df\U000112e3-\U000112ea\U00011300-\U00011301\U0001133b-\U0001133c\U00011340\U00011366-\U0001136c\U00011370-\U00011374\U00011438-\U0001143f\U00011442-\U00011444\U00011446\U0001145e\U000114b3-\U000114b8\U000114ba\U000114bf-\U000114c0\U000114c2-\U000114c3\U000115b2-\U000115b5\U000115bc-\U000115bd\U000115bf-\U000115c0\U000115dc-\U000115dd\U00011633-\U0001163a\U0001163d\U0001163f-\U00011640\U000116ab\U000116ad\U000116b0-\U000116b5\U000116b7\U0001171d-\U0001171f\U00011722-\U00011725\U00011727-\U0001172b\U0001182f-\U00011837\U00011839-\U0001183a\U00011a01-\U00011a0a\U00011a33-\U00011a38\U00011a3b-\U00011a3e\U00011a47\U00011a51-\U00011a56\U00011a59-\U00011a5b\U00011a8a-\U00011a96\U00011a98-\U00011a99\U00011c30-\U00011c36\U00011c38-\U00011c3d\U00011c3f\U00011c92-\U00011ca7\U00011caa-\U00011cb0\U00011cb2-\U00011cb3\U00011cb5-\U00011cb6\U00011d31-\U00011d36\U00011d3a\U00011d3c-\U00011d3d\U00011d3f-\U00011d45\U00011d47\U00011d90-\U00011d91\U00011d95\U00011d97\U00011ef3-\U00011ef4\U00016af0-\U00016af4\U00016b30-\U00016b36\U00016f8f-\U00016f92\U0001bc9d-\U0001bc9e\U0001d167-\U0001d169\U0001d17b-\U0001d182\U0001d185-\U0001d18b\U0001d1aa-\U0001d1ad\U0001d242-\U0001d244\U0001da00-\U0001da36\U0001da3b-\U0001da6c\U0001da75\U0001da84\U0001da9b-\U0001da9f\U0001daa1-\U0001daaf\U0001e000-\U0001e006\U0001e008-\U0001e018\U0001e01b-\U0001e021\U0001e023-\U0001e024\U0001e026-\U0001e02a\U0001e8d0-\U0001e8d6\U0001e944-\U0001e94a\U000e0100-\U000e01ef' + +Nd = '0-9\u0660-\u0669\u06f0-\u06f9\u07c0-\u07c9\u0966-\u096f\u09e6-\u09ef\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be6-\u0bef\u0c66-\u0c6f\u0ce6-\u0cef\u0d66-\u0d6f\u0de6-\u0def\u0e50-\u0e59\u0ed0-\u0ed9\u0f20-\u0f29\u1040-\u1049\u1090-\u1099\u17e0-\u17e9\u1810-\u1819\u1946-\u194f\u19d0-\u19d9\u1a80-\u1a89\u1a90-\u1a99\u1b50-\u1b59\u1bb0-\u1bb9\u1c40-\u1c49\u1c50-\u1c59\ua620-\ua629\ua8d0-\ua8d9\ua900-\ua909\ua9d0-\ua9d9\ua9f0-\ua9f9\uaa50-\uaa59\uabf0-\uabf9\uff10-\uff19\U000104a0-\U000104a9\U00010d30-\U00010d39\U00011066-\U0001106f\U000110f0-\U000110f9\U00011136-\U0001113f\U000111d0-\U000111d9\U000112f0-\U000112f9\U00011450-\U00011459\U000114d0-\U000114d9\U00011650-\U00011659\U000116c0-\U000116c9\U00011730-\U00011739\U000118e0-\U000118e9\U00011c50-\U00011c59\U00011d50-\U00011d59\U00011da0-\U00011da9\U00016a60-\U00016a69\U00016b50-\U00016b59\U0001d7ce-\U0001d7ff\U0001e950-\U0001e959' + +Nl = '\u16ee-\u16f0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303a\ua6e6-\ua6ef\U00010140-\U00010174\U00010341\U0001034a\U000103d1-\U000103d5\U00012400-\U0001246e' + +No = '\xb2-\xb3\xb9\xbc-\xbe\u09f4-\u09f9\u0b72-\u0b77\u0bf0-\u0bf2\u0c78-\u0c7e\u0d58-\u0d5e\u0d70-\u0d78\u0f2a-\u0f33\u1369-\u137c\u17f0-\u17f9\u19da\u2070\u2074-\u2079\u2080-\u2089\u2150-\u215f\u2189\u2460-\u249b\u24ea-\u24ff\u2776-\u2793\u2cfd\u3192-\u3195\u3220-\u3229\u3248-\u324f\u3251-\u325f\u3280-\u3289\u32b1-\u32bf\ua830-\ua835\U00010107-\U00010133\U00010175-\U00010178\U0001018a-\U0001018b\U000102e1-\U000102fb\U00010320-\U00010323\U00010858-\U0001085f\U00010879-\U0001087f\U000108a7-\U000108af\U000108fb-\U000108ff\U00010916-\U0001091b\U000109bc-\U000109bd\U000109c0-\U000109cf\U000109d2-\U000109ff\U00010a40-\U00010a48\U00010a7d-\U00010a7e\U00010a9d-\U00010a9f\U00010aeb-\U00010aef\U00010b58-\U00010b5f\U00010b78-\U00010b7f\U00010ba9-\U00010baf\U00010cfa-\U00010cff\U00010e60-\U00010e7e\U00010f1d-\U00010f26\U00010f51-\U00010f54\U00011052-\U00011065\U000111e1-\U000111f4\U0001173a-\U0001173b\U000118ea-\U000118f2\U00011c5a-\U00011c6c\U00016b5b-\U00016b61\U00016e80-\U00016e96\U0001d2e0-\U0001d2f3\U0001d360-\U0001d378\U0001e8c7-\U0001e8cf\U0001ec71-\U0001ecab\U0001ecad-\U0001ecaf\U0001ecb1-\U0001ecb4\U0001f100-\U0001f10c' + +Pc = '_\u203f-\u2040\u2054\ufe33-\ufe34\ufe4d-\ufe4f\uff3f' + +Pd = '\\-\u058a\u05be\u1400\u1806\u2010-\u2015\u2e17\u2e1a\u2e3a-\u2e3b\u2e40\u301c\u3030\u30a0\ufe31-\ufe32\ufe58\ufe63\uff0d' + +Pe = ')\\]}\u0f3b\u0f3d\u169c\u2046\u207e\u208e\u2309\u230b\u232a\u2769\u276b\u276d\u276f\u2771\u2773\u2775\u27c6\u27e7\u27e9\u27eb\u27ed\u27ef\u2984\u2986\u2988\u298a\u298c\u298e\u2990\u2992\u2994\u2996\u2998\u29d9\u29db\u29fd\u2e23\u2e25\u2e27\u2e29\u3009\u300b\u300d\u300f\u3011\u3015\u3017\u3019\u301b\u301e-\u301f\ufd3e\ufe18\ufe36\ufe38\ufe3a\ufe3c\ufe3e\ufe40\ufe42\ufe44\ufe48\ufe5a\ufe5c\ufe5e\uff09\uff3d\uff5d\uff60\uff63' + +Pf = '\xbb\u2019\u201d\u203a\u2e03\u2e05\u2e0a\u2e0d\u2e1d\u2e21' + +Pi = '\xab\u2018\u201b-\u201c\u201f\u2039\u2e02\u2e04\u2e09\u2e0c\u2e1c\u2e20' + +Po = "!-#%-'*,.-/:-;?-@\\\\\xa1\xa7\xb6-\xb7\xbf\u037e\u0387\u055a-\u055f\u0589\u05c0\u05c3\u05c6\u05f3-\u05f4\u0609-\u060a\u060c-\u060d\u061b\u061e-\u061f\u066a-\u066d\u06d4\u0700-\u070d\u07f7-\u07f9\u0830-\u083e\u085e\u0964-\u0965\u0970\u09fd\u0a76\u0af0\u0c84\u0df4\u0e4f\u0e5a-\u0e5b\u0f04-\u0f12\u0f14\u0f85\u0fd0-\u0fd4\u0fd9-\u0fda\u104a-\u104f\u10fb\u1360-\u1368\u166d-\u166e\u16eb-\u16ed\u1735-\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u1805\u1807-\u180a\u1944-\u1945\u1a1e-\u1a1f\u1aa0-\u1aa6\u1aa8-\u1aad\u1b5a-\u1b60\u1bfc-\u1bff\u1c3b-\u1c3f\u1c7e-\u1c7f\u1cc0-\u1cc7\u1cd3\u2016-\u2017\u2020-\u2027\u2030-\u2038\u203b-\u203e\u2041-\u2043\u2047-\u2051\u2053\u2055-\u205e\u2cf9-\u2cfc\u2cfe-\u2cff\u2d70\u2e00-\u2e01\u2e06-\u2e08\u2e0b\u2e0e-\u2e16\u2e18-\u2e19\u2e1b\u2e1e-\u2e1f\u2e2a-\u2e2e\u2e30-\u2e39\u2e3c-\u2e3f\u2e41\u2e43-\u2e4e\u3001-\u3003\u303d\u30fb\ua4fe-\ua4ff\ua60d-\ua60f\ua673\ua67e\ua6f2-\ua6f7\ua874-\ua877\ua8ce-\ua8cf\ua8f8-\ua8fa\ua8fc\ua92e-\ua92f\ua95f\ua9c1-\ua9cd\ua9de-\ua9df\uaa5c-\uaa5f\uaade-\uaadf\uaaf0-\uaaf1\uabeb\ufe10-\ufe16\ufe19\ufe30\ufe45-\ufe46\ufe49-\ufe4c\ufe50-\ufe52\ufe54-\ufe57\ufe5f-\ufe61\ufe68\ufe6a-\ufe6b\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e-\uff0f\uff1a-\uff1b\uff1f-\uff20\uff3c\uff61\uff64-\uff65\U00010100-\U00010102\U0001039f\U000103d0\U0001056f\U00010857\U0001091f\U0001093f\U00010a50-\U00010a58\U00010a7f\U00010af0-\U00010af6\U00010b39-\U00010b3f\U00010b99-\U00010b9c\U00010f55-\U00010f59\U00011047-\U0001104d\U000110bb-\U000110bc\U000110be-\U000110c1\U00011140-\U00011143\U00011174-\U00011175\U000111c5-\U000111c8\U000111cd\U000111db\U000111dd-\U000111df\U00011238-\U0001123d\U000112a9\U0001144b-\U0001144f\U0001145b\U0001145d\U000114c6\U000115c1-\U000115d7\U00011641-\U00011643\U00011660-\U0001166c\U0001173c-\U0001173e\U0001183b\U00011a3f-\U00011a46\U00011a9a-\U00011a9c\U00011a9e-\U00011aa2\U00011c41-\U00011c45\U00011c70-\U00011c71\U00011ef7-\U00011ef8\U00012470-\U00012474\U00016a6e-\U00016a6f\U00016af5\U00016b37-\U00016b3b\U00016b44\U00016e97-\U00016e9a\U0001bc9f\U0001da87-\U0001da8b\U0001e95e-\U0001e95f" + +Ps = '(\\[{\u0f3a\u0f3c\u169b\u201a\u201e\u2045\u207d\u208d\u2308\u230a\u2329\u2768\u276a\u276c\u276e\u2770\u2772\u2774\u27c5\u27e6\u27e8\u27ea\u27ec\u27ee\u2983\u2985\u2987\u2989\u298b\u298d\u298f\u2991\u2993\u2995\u2997\u29d8\u29da\u29fc\u2e22\u2e24\u2e26\u2e28\u2e42\u3008\u300a\u300c\u300e\u3010\u3014\u3016\u3018\u301a\u301d\ufd3f\ufe17\ufe35\ufe37\ufe39\ufe3b\ufe3d\ufe3f\ufe41\ufe43\ufe47\ufe59\ufe5b\ufe5d\uff08\uff3b\uff5b\uff5f\uff62' + +Sc = '$\xa2-\xa5\u058f\u060b\u07fe-\u07ff\u09f2-\u09f3\u09fb\u0af1\u0bf9\u0e3f\u17db\u20a0-\u20bf\ua838\ufdfc\ufe69\uff04\uffe0-\uffe1\uffe5-\uffe6\U0001ecb0' + +Sk = '\\^`\xa8\xaf\xb4\xb8\u02c2-\u02c5\u02d2-\u02df\u02e5-\u02eb\u02ed\u02ef-\u02ff\u0375\u0384-\u0385\u1fbd\u1fbf-\u1fc1\u1fcd-\u1fcf\u1fdd-\u1fdf\u1fed-\u1fef\u1ffd-\u1ffe\u309b-\u309c\ua700-\ua716\ua720-\ua721\ua789-\ua78a\uab5b\ufbb2-\ufbc1\uff3e\uff40\uffe3\U0001f3fb-\U0001f3ff' + +Sm = '+<->|~\xac\xb1\xd7\xf7\u03f6\u0606-\u0608\u2044\u2052\u207a-\u207c\u208a-\u208c\u2118\u2140-\u2144\u214b\u2190-\u2194\u219a-\u219b\u21a0\u21a3\u21a6\u21ae\u21ce-\u21cf\u21d2\u21d4\u21f4-\u22ff\u2320-\u2321\u237c\u239b-\u23b3\u23dc-\u23e1\u25b7\u25c1\u25f8-\u25ff\u266f\u27c0-\u27c4\u27c7-\u27e5\u27f0-\u27ff\u2900-\u2982\u2999-\u29d7\u29dc-\u29fb\u29fe-\u2aff\u2b30-\u2b44\u2b47-\u2b4c\ufb29\ufe62\ufe64-\ufe66\uff0b\uff1c-\uff1e\uff5c\uff5e\uffe2\uffe9-\uffec\U0001d6c1\U0001d6db\U0001d6fb\U0001d715\U0001d735\U0001d74f\U0001d76f\U0001d789\U0001d7a9\U0001d7c3\U0001eef0-\U0001eef1' + +So = '\xa6\xa9\xae\xb0\u0482\u058d-\u058e\u060e-\u060f\u06de\u06e9\u06fd-\u06fe\u07f6\u09fa\u0b70\u0bf3-\u0bf8\u0bfa\u0c7f\u0d4f\u0d79\u0f01-\u0f03\u0f13\u0f15-\u0f17\u0f1a-\u0f1f\u0f34\u0f36\u0f38\u0fbe-\u0fc5\u0fc7-\u0fcc\u0fce-\u0fcf\u0fd5-\u0fd8\u109e-\u109f\u1390-\u1399\u1940\u19de-\u19ff\u1b61-\u1b6a\u1b74-\u1b7c\u2100-\u2101\u2103-\u2106\u2108-\u2109\u2114\u2116-\u2117\u211e-\u2123\u2125\u2127\u2129\u212e\u213a-\u213b\u214a\u214c-\u214d\u214f\u218a-\u218b\u2195-\u2199\u219c-\u219f\u21a1-\u21a2\u21a4-\u21a5\u21a7-\u21ad\u21af-\u21cd\u21d0-\u21d1\u21d3\u21d5-\u21f3\u2300-\u2307\u230c-\u231f\u2322-\u2328\u232b-\u237b\u237d-\u239a\u23b4-\u23db\u23e2-\u2426\u2440-\u244a\u249c-\u24e9\u2500-\u25b6\u25b8-\u25c0\u25c2-\u25f7\u2600-\u266e\u2670-\u2767\u2794-\u27bf\u2800-\u28ff\u2b00-\u2b2f\u2b45-\u2b46\u2b4d-\u2b73\u2b76-\u2b95\u2b98-\u2bc8\u2bca-\u2bfe\u2ce5-\u2cea\u2e80-\u2e99\u2e9b-\u2ef3\u2f00-\u2fd5\u2ff0-\u2ffb\u3004\u3012-\u3013\u3020\u3036-\u3037\u303e-\u303f\u3190-\u3191\u3196-\u319f\u31c0-\u31e3\u3200-\u321e\u322a-\u3247\u3250\u3260-\u327f\u328a-\u32b0\u32c0-\u32fe\u3300-\u33ff\u4dc0-\u4dff\ua490-\ua4c6\ua828-\ua82b\ua836-\ua837\ua839\uaa77-\uaa79\ufdfd\uffe4\uffe8\uffed-\uffee\ufffc-\ufffd\U00010137-\U0001013f\U00010179-\U00010189\U0001018c-\U0001018e\U00010190-\U0001019b\U000101a0\U000101d0-\U000101fc\U00010877-\U00010878\U00010ac8\U0001173f\U00016b3c-\U00016b3f\U00016b45\U0001bc9c\U0001d000-\U0001d0f5\U0001d100-\U0001d126\U0001d129-\U0001d164\U0001d16a-\U0001d16c\U0001d183-\U0001d184\U0001d18c-\U0001d1a9\U0001d1ae-\U0001d1e8\U0001d200-\U0001d241\U0001d245\U0001d300-\U0001d356\U0001d800-\U0001d9ff\U0001da37-\U0001da3a\U0001da6d-\U0001da74\U0001da76-\U0001da83\U0001da85-\U0001da86\U0001ecac\U0001f000-\U0001f02b\U0001f030-\U0001f093\U0001f0a0-\U0001f0ae\U0001f0b1-\U0001f0bf\U0001f0c1-\U0001f0cf\U0001f0d1-\U0001f0f5\U0001f110-\U0001f16b\U0001f170-\U0001f1ac\U0001f1e6-\U0001f202\U0001f210-\U0001f23b\U0001f240-\U0001f248\U0001f250-\U0001f251\U0001f260-\U0001f265\U0001f300-\U0001f3fa\U0001f400-\U0001f6d4\U0001f6e0-\U0001f6ec\U0001f6f0-\U0001f6f9\U0001f700-\U0001f773\U0001f780-\U0001f7d8\U0001f800-\U0001f80b\U0001f810-\U0001f847\U0001f850-\U0001f859\U0001f860-\U0001f887\U0001f890-\U0001f8ad\U0001f900-\U0001f90b\U0001f910-\U0001f93e\U0001f940-\U0001f970\U0001f973-\U0001f976\U0001f97a\U0001f97c-\U0001f9a2\U0001f9b0-\U0001f9b9\U0001f9c0-\U0001f9c2\U0001f9d0-\U0001f9ff\U0001fa60-\U0001fa6d' + +Zl = '\u2028' + +Zp = '\u2029' + +Zs = ' \xa0\u1680\u2000-\u200a\u202f\u205f\u3000' + +xid_continue = '0-9A-Z_a-z\xaa\xb5\xb7\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376-\u0377\u037b-\u037d\u037f\u0386-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u05d0-\u05ea\u05ef-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u07fd\u0800-\u082d\u0840-\u085b\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd\u08d3-\u08e1\u08e3-\u0963\u0966-\u096f\u0971-\u0983\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7-\u09c8\u09cb-\u09ce\u09d7\u09dc-\u09dd\u09df-\u09e3\u09e6-\u09f1\u09fc\u09fe\u0a01-\u0a03\u0a05-\u0a0a\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36\u0a38-\u0a39\u0a3c\u0a3e-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0af9-\u0aff\u0b01-\u0b03\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32-\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47-\u0b48\u0b4b-\u0b4d\u0b56-\u0b57\u0b5c-\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82-\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c00-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c58-\u0c5a\u0c60-\u0c63\u0c66-\u0c6f\u0c80-\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5-\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1-\u0cf2\u0d00-\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d54-\u0d57\u0d5f-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82-\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2-\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81-\u0e82\u0e84\u0e87-\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa-\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18-\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1369-\u1371\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772-\u1773\u1780-\u17d3\u17d7\u17dc-\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1878\u1880-\u18aa\u18b0-\u18f5\u1900-\u191e\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19da\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1ab0-\u1abd\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1cd0-\u1cd2\u1cd4-\u1cf9\u1d00-\u1df9\u1dfb-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u203f-\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099-\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua7b9\ua7f7-\ua827\ua840-\ua873\ua880-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua8fd-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\ua9e0-\ua9fe\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab65\uab70-\uabea\uabec-\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufbb1\ufbd3-\ufc5d\ufc64-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdf9\ufe00-\ufe0f\ufe20-\ufe2f\ufe33-\ufe34\ufe4d-\ufe4f\ufe71\ufe73\ufe77\ufe79\ufe7b\ufe7d\ufe7f-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc\U00010000-\U0001000b\U0001000d-\U00010026\U00010028-\U0001003a\U0001003c-\U0001003d\U0001003f-\U0001004d\U00010050-\U0001005d\U00010080-\U000100fa\U00010140-\U00010174\U000101fd\U00010280-\U0001029c\U000102a0-\U000102d0\U000102e0\U00010300-\U0001031f\U0001032d-\U0001034a\U00010350-\U0001037a\U00010380-\U0001039d\U000103a0-\U000103c3\U000103c8-\U000103cf\U000103d1-\U000103d5\U00010400-\U0001049d\U000104a0-\U000104a9\U000104b0-\U000104d3\U000104d8-\U000104fb\U00010500-\U00010527\U00010530-\U00010563\U00010600-\U00010736\U00010740-\U00010755\U00010760-\U00010767\U00010800-\U00010805\U00010808\U0001080a-\U00010835\U00010837-\U00010838\U0001083c\U0001083f-\U00010855\U00010860-\U00010876\U00010880-\U0001089e\U000108e0-\U000108f2\U000108f4-\U000108f5\U00010900-\U00010915\U00010920-\U00010939\U00010980-\U000109b7\U000109be-\U000109bf\U00010a00-\U00010a03\U00010a05-\U00010a06\U00010a0c-\U00010a13\U00010a15-\U00010a17\U00010a19-\U00010a35\U00010a38-\U00010a3a\U00010a3f\U00010a60-\U00010a7c\U00010a80-\U00010a9c\U00010ac0-\U00010ac7\U00010ac9-\U00010ae6\U00010b00-\U00010b35\U00010b40-\U00010b55\U00010b60-\U00010b72\U00010b80-\U00010b91\U00010c00-\U00010c48\U00010c80-\U00010cb2\U00010cc0-\U00010cf2\U00010d00-\U00010d27\U00010d30-\U00010d39\U00010f00-\U00010f1c\U00010f27\U00010f30-\U00010f50\U00011000-\U00011046\U00011066-\U0001106f\U0001107f-\U000110ba\U000110d0-\U000110e8\U000110f0-\U000110f9\U00011100-\U00011134\U00011136-\U0001113f\U00011144-\U00011146\U00011150-\U00011173\U00011176\U00011180-\U000111c4\U000111c9-\U000111cc\U000111d0-\U000111da\U000111dc\U00011200-\U00011211\U00011213-\U00011237\U0001123e\U00011280-\U00011286\U00011288\U0001128a-\U0001128d\U0001128f-\U0001129d\U0001129f-\U000112a8\U000112b0-\U000112ea\U000112f0-\U000112f9\U00011300-\U00011303\U00011305-\U0001130c\U0001130f-\U00011310\U00011313-\U00011328\U0001132a-\U00011330\U00011332-\U00011333\U00011335-\U00011339\U0001133b-\U00011344\U00011347-\U00011348\U0001134b-\U0001134d\U00011350\U00011357\U0001135d-\U00011363\U00011366-\U0001136c\U00011370-\U00011374\U00011400-\U0001144a\U00011450-\U00011459\U0001145e\U00011480-\U000114c5\U000114c7\U000114d0-\U000114d9\U00011580-\U000115b5\U000115b8-\U000115c0\U000115d8-\U000115dd\U00011600-\U00011640\U00011644\U00011650-\U00011659\U00011680-\U000116b7\U000116c0-\U000116c9\U00011700-\U0001171a\U0001171d-\U0001172b\U00011730-\U00011739\U00011800-\U0001183a\U000118a0-\U000118e9\U000118ff\U00011a00-\U00011a3e\U00011a47\U00011a50-\U00011a83\U00011a86-\U00011a99\U00011a9d\U00011ac0-\U00011af8\U00011c00-\U00011c08\U00011c0a-\U00011c36\U00011c38-\U00011c40\U00011c50-\U00011c59\U00011c72-\U00011c8f\U00011c92-\U00011ca7\U00011ca9-\U00011cb6\U00011d00-\U00011d06\U00011d08-\U00011d09\U00011d0b-\U00011d36\U00011d3a\U00011d3c-\U00011d3d\U00011d3f-\U00011d47\U00011d50-\U00011d59\U00011d60-\U00011d65\U00011d67-\U00011d68\U00011d6a-\U00011d8e\U00011d90-\U00011d91\U00011d93-\U00011d98\U00011da0-\U00011da9\U00011ee0-\U00011ef6\U00012000-\U00012399\U00012400-\U0001246e\U00012480-\U00012543\U00013000-\U0001342e\U00014400-\U00014646\U00016800-\U00016a38\U00016a40-\U00016a5e\U00016a60-\U00016a69\U00016ad0-\U00016aed\U00016af0-\U00016af4\U00016b00-\U00016b36\U00016b40-\U00016b43\U00016b50-\U00016b59\U00016b63-\U00016b77\U00016b7d-\U00016b8f\U00016e40-\U00016e7f\U00016f00-\U00016f44\U00016f50-\U00016f7e\U00016f8f-\U00016f9f\U00016fe0-\U00016fe1\U00017000-\U000187f1\U00018800-\U00018af2\U0001b000-\U0001b11e\U0001b170-\U0001b2fb\U0001bc00-\U0001bc6a\U0001bc70-\U0001bc7c\U0001bc80-\U0001bc88\U0001bc90-\U0001bc99\U0001bc9d-\U0001bc9e\U0001d165-\U0001d169\U0001d16d-\U0001d172\U0001d17b-\U0001d182\U0001d185-\U0001d18b\U0001d1aa-\U0001d1ad\U0001d242-\U0001d244\U0001d400-\U0001d454\U0001d456-\U0001d49c\U0001d49e-\U0001d49f\U0001d4a2\U0001d4a5-\U0001d4a6\U0001d4a9-\U0001d4ac\U0001d4ae-\U0001d4b9\U0001d4bb\U0001d4bd-\U0001d4c3\U0001d4c5-\U0001d505\U0001d507-\U0001d50a\U0001d50d-\U0001d514\U0001d516-\U0001d51c\U0001d51e-\U0001d539\U0001d53b-\U0001d53e\U0001d540-\U0001d544\U0001d546\U0001d54a-\U0001d550\U0001d552-\U0001d6a5\U0001d6a8-\U0001d6c0\U0001d6c2-\U0001d6da\U0001d6dc-\U0001d6fa\U0001d6fc-\U0001d714\U0001d716-\U0001d734\U0001d736-\U0001d74e\U0001d750-\U0001d76e\U0001d770-\U0001d788\U0001d78a-\U0001d7a8\U0001d7aa-\U0001d7c2\U0001d7c4-\U0001d7cb\U0001d7ce-\U0001d7ff\U0001da00-\U0001da36\U0001da3b-\U0001da6c\U0001da75\U0001da84\U0001da9b-\U0001da9f\U0001daa1-\U0001daaf\U0001e000-\U0001e006\U0001e008-\U0001e018\U0001e01b-\U0001e021\U0001e023-\U0001e024\U0001e026-\U0001e02a\U0001e800-\U0001e8c4\U0001e8d0-\U0001e8d6\U0001e900-\U0001e94a\U0001e950-\U0001e959\U0001ee00-\U0001ee03\U0001ee05-\U0001ee1f\U0001ee21-\U0001ee22\U0001ee24\U0001ee27\U0001ee29-\U0001ee32\U0001ee34-\U0001ee37\U0001ee39\U0001ee3b\U0001ee42\U0001ee47\U0001ee49\U0001ee4b\U0001ee4d-\U0001ee4f\U0001ee51-\U0001ee52\U0001ee54\U0001ee57\U0001ee59\U0001ee5b\U0001ee5d\U0001ee5f\U0001ee61-\U0001ee62\U0001ee64\U0001ee67-\U0001ee6a\U0001ee6c-\U0001ee72\U0001ee74-\U0001ee77\U0001ee79-\U0001ee7c\U0001ee7e\U0001ee80-\U0001ee89\U0001ee8b-\U0001ee9b\U0001eea1-\U0001eea3\U0001eea5-\U0001eea9\U0001eeab-\U0001eebb\U00020000-\U0002a6d6\U0002a700-\U0002b734\U0002b740-\U0002b81d\U0002b820-\U0002cea1\U0002ceb0-\U0002ebe0\U0002f800-\U0002fa1d\U000e0100-\U000e01ef' + +xid_start = 'A-Z_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376-\u0377\u037b-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e-\u066f\u0671-\u06d3\u06d5\u06e5-\u06e6\u06ee-\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4-\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc-\u09dd\u09df-\u09e1\u09f0-\u09f1\u09fc\u0a05-\u0a0a\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36\u0a38-\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0-\u0ae1\u0af9\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32-\u0b33\u0b35-\u0b39\u0b3d\u0b5c-\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60-\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0-\u0ce1\u0cf1-\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e40-\u0e46\u0e81-\u0e82\u0e84\u0e87-\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa-\u0eab\u0ead-\u0eb0\u0eb2\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065-\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae-\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5-\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a-\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7b9\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd-\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5-\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab65\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufbb1\ufbd3-\ufc5d\ufc64-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdf9\ufe71\ufe73\ufe77\ufe79\ufe7b\ufe7d\ufe7f-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uff9d\uffa0-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc\U00010000-\U0001000b\U0001000d-\U00010026\U00010028-\U0001003a\U0001003c-\U0001003d\U0001003f-\U0001004d\U00010050-\U0001005d\U00010080-\U000100fa\U00010140-\U00010174\U00010280-\U0001029c\U000102a0-\U000102d0\U00010300-\U0001031f\U0001032d-\U0001034a\U00010350-\U00010375\U00010380-\U0001039d\U000103a0-\U000103c3\U000103c8-\U000103cf\U000103d1-\U000103d5\U00010400-\U0001049d\U000104b0-\U000104d3\U000104d8-\U000104fb\U00010500-\U00010527\U00010530-\U00010563\U00010600-\U00010736\U00010740-\U00010755\U00010760-\U00010767\U00010800-\U00010805\U00010808\U0001080a-\U00010835\U00010837-\U00010838\U0001083c\U0001083f-\U00010855\U00010860-\U00010876\U00010880-\U0001089e\U000108e0-\U000108f2\U000108f4-\U000108f5\U00010900-\U00010915\U00010920-\U00010939\U00010980-\U000109b7\U000109be-\U000109bf\U00010a00\U00010a10-\U00010a13\U00010a15-\U00010a17\U00010a19-\U00010a35\U00010a60-\U00010a7c\U00010a80-\U00010a9c\U00010ac0-\U00010ac7\U00010ac9-\U00010ae4\U00010b00-\U00010b35\U00010b40-\U00010b55\U00010b60-\U00010b72\U00010b80-\U00010b91\U00010c00-\U00010c48\U00010c80-\U00010cb2\U00010cc0-\U00010cf2\U00010d00-\U00010d23\U00010f00-\U00010f1c\U00010f27\U00010f30-\U00010f45\U00011003-\U00011037\U00011083-\U000110af\U000110d0-\U000110e8\U00011103-\U00011126\U00011144\U00011150-\U00011172\U00011176\U00011183-\U000111b2\U000111c1-\U000111c4\U000111da\U000111dc\U00011200-\U00011211\U00011213-\U0001122b\U00011280-\U00011286\U00011288\U0001128a-\U0001128d\U0001128f-\U0001129d\U0001129f-\U000112a8\U000112b0-\U000112de\U00011305-\U0001130c\U0001130f-\U00011310\U00011313-\U00011328\U0001132a-\U00011330\U00011332-\U00011333\U00011335-\U00011339\U0001133d\U00011350\U0001135d-\U00011361\U00011400-\U00011434\U00011447-\U0001144a\U00011480-\U000114af\U000114c4-\U000114c5\U000114c7\U00011580-\U000115ae\U000115d8-\U000115db\U00011600-\U0001162f\U00011644\U00011680-\U000116aa\U00011700-\U0001171a\U00011800-\U0001182b\U000118a0-\U000118df\U000118ff\U00011a00\U00011a0b-\U00011a32\U00011a3a\U00011a50\U00011a5c-\U00011a83\U00011a86-\U00011a89\U00011a9d\U00011ac0-\U00011af8\U00011c00-\U00011c08\U00011c0a-\U00011c2e\U00011c40\U00011c72-\U00011c8f\U00011d00-\U00011d06\U00011d08-\U00011d09\U00011d0b-\U00011d30\U00011d46\U00011d60-\U00011d65\U00011d67-\U00011d68\U00011d6a-\U00011d89\U00011d98\U00011ee0-\U00011ef2\U00012000-\U00012399\U00012400-\U0001246e\U00012480-\U00012543\U00013000-\U0001342e\U00014400-\U00014646\U00016800-\U00016a38\U00016a40-\U00016a5e\U00016ad0-\U00016aed\U00016b00-\U00016b2f\U00016b40-\U00016b43\U00016b63-\U00016b77\U00016b7d-\U00016b8f\U00016e40-\U00016e7f\U00016f00-\U00016f44\U00016f50\U00016f93-\U00016f9f\U00016fe0-\U00016fe1\U00017000-\U000187f1\U00018800-\U00018af2\U0001b000-\U0001b11e\U0001b170-\U0001b2fb\U0001bc00-\U0001bc6a\U0001bc70-\U0001bc7c\U0001bc80-\U0001bc88\U0001bc90-\U0001bc99\U0001d400-\U0001d454\U0001d456-\U0001d49c\U0001d49e-\U0001d49f\U0001d4a2\U0001d4a5-\U0001d4a6\U0001d4a9-\U0001d4ac\U0001d4ae-\U0001d4b9\U0001d4bb\U0001d4bd-\U0001d4c3\U0001d4c5-\U0001d505\U0001d507-\U0001d50a\U0001d50d-\U0001d514\U0001d516-\U0001d51c\U0001d51e-\U0001d539\U0001d53b-\U0001d53e\U0001d540-\U0001d544\U0001d546\U0001d54a-\U0001d550\U0001d552-\U0001d6a5\U0001d6a8-\U0001d6c0\U0001d6c2-\U0001d6da\U0001d6dc-\U0001d6fa\U0001d6fc-\U0001d714\U0001d716-\U0001d734\U0001d736-\U0001d74e\U0001d750-\U0001d76e\U0001d770-\U0001d788\U0001d78a-\U0001d7a8\U0001d7aa-\U0001d7c2\U0001d7c4-\U0001d7cb\U0001e800-\U0001e8c4\U0001e900-\U0001e943\U0001ee00-\U0001ee03\U0001ee05-\U0001ee1f\U0001ee21-\U0001ee22\U0001ee24\U0001ee27\U0001ee29-\U0001ee32\U0001ee34-\U0001ee37\U0001ee39\U0001ee3b\U0001ee42\U0001ee47\U0001ee49\U0001ee4b\U0001ee4d-\U0001ee4f\U0001ee51-\U0001ee52\U0001ee54\U0001ee57\U0001ee59\U0001ee5b\U0001ee5d\U0001ee5f\U0001ee61-\U0001ee62\U0001ee64\U0001ee67-\U0001ee6a\U0001ee6c-\U0001ee72\U0001ee74-\U0001ee77\U0001ee79-\U0001ee7c\U0001ee7e\U0001ee80-\U0001ee89\U0001ee8b-\U0001ee9b\U0001eea1-\U0001eea3\U0001eea5-\U0001eea9\U0001eeab-\U0001eebb\U00020000-\U0002a6d6\U0002a700-\U0002b734\U0002b740-\U0002b81d\U0002b820-\U0002cea1\U0002ceb0-\U0002ebe0\U0002f800-\U0002fa1d' + +cats = ['Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', 'Mc', 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po', 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs'] + +# Generated from unidata 11.0.0 + +def combine(*args): + return ''.join(globals()[cat] for cat in args) + + +def allexcept(*args): + newcats = cats[:] + for arg in args: + newcats.remove(arg) + return ''.join(globals()[cat] for cat in newcats) + + +def _handle_runs(char_list): # pragma: no cover + buf = [] + for c in char_list: + if len(c) == 1: + if buf and buf[-1][1] == chr(ord(c)-1): + buf[-1] = (buf[-1][0], c) + else: + buf.append((c, c)) + else: + buf.append((c, c)) + for a, b in buf: + if a == b: + yield a + else: + yield f'{a}-{b}' + + +if __name__ == '__main__': # pragma: no cover + import unicodedata + + categories = {'xid_start': [], 'xid_continue': []} + + with open(__file__, encoding='utf-8') as fp: + content = fp.read() + + header = content[:content.find('Cc =')] + footer = content[content.find("def combine("):] + + for code in range(0x110000): + c = chr(code) + cat = unicodedata.category(c) + if ord(c) == 0xdc00: + # Hack to avoid combining this combining with the preceding high + # surrogate, 0xdbff, when doing a repr. + c = '\\' + c + elif ord(c) in (0x2d, 0x5b, 0x5c, 0x5d, 0x5e): + # Escape regex metachars. + c = '\\' + c + categories.setdefault(cat, []).append(c) + # XID_START and XID_CONTINUE are special categories used for matching + # identifiers in Python 3. + if c.isidentifier(): + categories['xid_start'].append(c) + if ('a' + c).isidentifier(): + categories['xid_continue'].append(c) + + with open(__file__, 'w', encoding='utf-8') as fp: + fp.write(header) + + for cat in sorted(categories): + val = ''.join(_handle_runs(categories[cat])) + fp.write(f'{cat} = {val!a}\n\n') + + cats = sorted(categories) + cats.remove('xid_start') + cats.remove('xid_continue') + fp.write(f'cats = {cats!r}\n\n') + + fp.write(f'# Generated from unidata {unicodedata.unidata_version}\n\n') + + fp.write(footer) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pygments/util.py b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/util.py new file mode 100644 index 0000000..548d9d7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pygments/util.py @@ -0,0 +1,324 @@ +""" + pygments.util + ~~~~~~~~~~~~~ + + Utility functions. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +from io import TextIOWrapper + + +split_path_re = re.compile(r'[/\\ ]') +doctype_lookup_re = re.compile(r''' + ]*> +''', re.DOTALL | re.MULTILINE | re.VERBOSE) +tag_re = re.compile(r'<(.+?)(\s.*?)?>.*?', + re.IGNORECASE | re.DOTALL | re.MULTILINE) +xml_decl_re = re.compile(r'\s*<\?xml[^>]*\?>', re.I) + + +class ClassNotFound(ValueError): + """Raised if one of the lookup functions didn't find a matching class.""" + + +class OptionError(Exception): + """ + This exception will be raised by all option processing functions if + the type or value of the argument is not correct. + """ + +def get_choice_opt(options, optname, allowed, default=None, normcase=False): + """ + If the key `optname` from the dictionary is not in the sequence + `allowed`, raise an error, otherwise return it. + """ + string = options.get(optname, default) + if normcase: + string = string.lower() + if string not in allowed: + raise OptionError('Value for option {} must be one of {}'.format(optname, ', '.join(map(str, allowed)))) + return string + + +def get_bool_opt(options, optname, default=None): + """ + Intuitively, this is `options.get(optname, default)`, but restricted to + Boolean value. The Booleans can be represented as string, in order to accept + Boolean value from the command line arguments. If the key `optname` is + present in the dictionary `options` and is not associated with a Boolean, + raise an `OptionError`. If it is absent, `default` is returned instead. + + The valid string values for ``True`` are ``1``, ``yes``, ``true`` and + ``on``, the ones for ``False`` are ``0``, ``no``, ``false`` and ``off`` + (matched case-insensitively). + """ + string = options.get(optname, default) + if isinstance(string, bool): + return string + elif isinstance(string, int): + return bool(string) + elif not isinstance(string, str): + raise OptionError(f'Invalid type {string!r} for option {optname}; use ' + '1/0, yes/no, true/false, on/off') + elif string.lower() in ('1', 'yes', 'true', 'on'): + return True + elif string.lower() in ('0', 'no', 'false', 'off'): + return False + else: + raise OptionError(f'Invalid value {string!r} for option {optname}; use ' + '1/0, yes/no, true/false, on/off') + + +def get_int_opt(options, optname, default=None): + """As :func:`get_bool_opt`, but interpret the value as an integer.""" + string = options.get(optname, default) + try: + return int(string) + except TypeError: + raise OptionError(f'Invalid type {string!r} for option {optname}; you ' + 'must give an integer value') + except ValueError: + raise OptionError(f'Invalid value {string!r} for option {optname}; you ' + 'must give an integer value') + +def get_list_opt(options, optname, default=None): + """ + If the key `optname` from the dictionary `options` is a string, + split it at whitespace and return it. If it is already a list + or a tuple, it is returned as a list. + """ + val = options.get(optname, default) + if isinstance(val, str): + return val.split() + elif isinstance(val, (list, tuple)): + return list(val) + else: + raise OptionError(f'Invalid type {val!r} for option {optname}; you ' + 'must give a list value') + + +def docstring_headline(obj): + if not obj.__doc__: + return '' + res = [] + for line in obj.__doc__.strip().splitlines(): + if line.strip(): + res.append(" " + line.strip()) + else: + break + return ''.join(res).lstrip() + + +def make_analysator(f): + """Return a static text analyser function that returns float values.""" + def text_analyse(text): + try: + rv = f(text) + except Exception: + return 0.0 + if not rv: + return 0.0 + try: + return min(1.0, max(0.0, float(rv))) + except (ValueError, TypeError): + return 0.0 + text_analyse.__doc__ = f.__doc__ + return staticmethod(text_analyse) + + +def shebang_matches(text, regex): + r"""Check if the given regular expression matches the last part of the + shebang if one exists. + + >>> from pygments.util import shebang_matches + >>> shebang_matches('#!/usr/bin/env python', r'python(2\.\d)?') + True + >>> shebang_matches('#!/usr/bin/python2.4', r'python(2\.\d)?') + True + >>> shebang_matches('#!/usr/bin/python-ruby', r'python(2\.\d)?') + False + >>> shebang_matches('#!/usr/bin/python/ruby', r'python(2\.\d)?') + False + >>> shebang_matches('#!/usr/bin/startsomethingwith python', + ... r'python(2\.\d)?') + True + + It also checks for common windows executable file extensions:: + + >>> shebang_matches('#!C:\\Python2.4\\Python.exe', r'python(2\.\d)?') + True + + Parameters (``'-f'`` or ``'--foo'`` are ignored so ``'perl'`` does + the same as ``'perl -e'``) + + Note that this method automatically searches the whole string (eg: + the regular expression is wrapped in ``'^$'``) + """ + index = text.find('\n') + if index >= 0: + first_line = text[:index].lower() + else: + first_line = text.lower() + if first_line.startswith('#!'): + try: + found = [x for x in split_path_re.split(first_line[2:].strip()) + if x and not x.startswith('-')][-1] + except IndexError: + return False + regex = re.compile(rf'^{regex}(\.(exe|cmd|bat|bin))?$', re.IGNORECASE) + if regex.search(found) is not None: + return True + return False + + +def doctype_matches(text, regex): + """Check if the doctype matches a regular expression (if present). + + Note that this method only checks the first part of a DOCTYPE. + eg: 'html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"' + """ + m = doctype_lookup_re.search(text) + if m is None: + return False + doctype = m.group(1) + return re.compile(regex, re.I).match(doctype.strip()) is not None + + +def html_doctype_matches(text): + """Check if the file looks like it has a html doctype.""" + return doctype_matches(text, r'html') + + +_looks_like_xml_cache = {} + + +def looks_like_xml(text): + """Check if a doctype exists or if we have some tags.""" + if xml_decl_re.match(text): + return True + key = hash(text) + try: + return _looks_like_xml_cache[key] + except KeyError: + m = doctype_lookup_re.search(text) + if m is not None: + return True + rv = tag_re.search(text[:1000]) is not None + _looks_like_xml_cache[key] = rv + return rv + + +def surrogatepair(c): + """Given a unicode character code with length greater than 16 bits, + return the two 16 bit surrogate pair. + """ + # From example D28 of: + # http://www.unicode.org/book/ch03.pdf + return (0xd7c0 + (c >> 10), (0xdc00 + (c & 0x3ff))) + + +def format_lines(var_name, seq, raw=False, indent_level=0): + """Formats a sequence of strings for output.""" + lines = [] + base_indent = ' ' * indent_level * 4 + inner_indent = ' ' * (indent_level + 1) * 4 + lines.append(base_indent + var_name + ' = (') + if raw: + # These should be preformatted reprs of, say, tuples. + for i in seq: + lines.append(inner_indent + i + ',') + else: + for i in seq: + # Force use of single quotes + r = repr(i + '"') + lines.append(inner_indent + r[:-2] + r[-1] + ',') + lines.append(base_indent + ')') + return '\n'.join(lines) + + +def duplicates_removed(it, already_seen=()): + """ + Returns a list with duplicates removed from the iterable `it`. + + Order is preserved. + """ + lst = [] + seen = set() + for i in it: + if i in seen or i in already_seen: + continue + lst.append(i) + seen.add(i) + return lst + + +class Future: + """Generic class to defer some work. + + Handled specially in RegexLexerMeta, to support regex string construction at + first use. + """ + def get(self): + raise NotImplementedError + + +def guess_decode(text): + """Decode *text* with guessed encoding. + + First try UTF-8; this should fail for non-UTF-8 encodings. + Then try the preferred locale encoding. + Fall back to latin-1, which always works. + """ + try: + text = text.decode('utf-8') + return text, 'utf-8' + except UnicodeDecodeError: + try: + import locale + prefencoding = locale.getpreferredencoding() + text = text.decode(prefencoding) + return text, prefencoding + except (UnicodeDecodeError, LookupError): + text = text.decode('latin1') + return text, 'latin1' + + +def guess_decode_from_terminal(text, term): + """Decode *text* coming from terminal *term*. + + First try the terminal encoding, if given. + Then try UTF-8. Then try the preferred locale encoding. + Fall back to latin-1, which always works. + """ + if getattr(term, 'encoding', None): + try: + text = text.decode(term.encoding) + except UnicodeDecodeError: + pass + else: + return text, term.encoding + return guess_decode(text) + + +def terminal_encoding(term): + """Return our best guess of encoding for the given *term*.""" + if getattr(term, 'encoding', None): + return term.encoding + import locale + return locale.getpreferredencoding() + + +class UnclosingTextIOWrapper(TextIOWrapper): + # Don't close underlying buffer on destruction. + def close(self): + self.flush() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/LICENSE new file mode 100644 index 0000000..b0ae9db --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Thomas Kluyver + +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. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__init__.py new file mode 100644 index 0000000..746b89f --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__init__.py @@ -0,0 +1,31 @@ +"""Wrappers to call pyproject.toml-based build backend hooks. +""" + +from typing import TYPE_CHECKING + +from ._impl import ( + BackendUnavailable, + BuildBackendHookCaller, + HookMissing, + UnsupportedOperation, + default_subprocess_runner, + quiet_subprocess_runner, +) + +__version__ = "1.2.0" +__all__ = [ + "BackendUnavailable", + "BackendInvalid", + "HookMissing", + "UnsupportedOperation", + "default_subprocess_runner", + "quiet_subprocess_runner", + "BuildBackendHookCaller", +] + +BackendInvalid = BackendUnavailable # Deprecated alias, previously a separate exception + +if TYPE_CHECKING: + from ._impl import SubprocessRunner + + __all__ += ["SubprocessRunner"] diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e69f23f Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-312.pyc new file mode 100644 index 0000000..89064c3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_impl.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_impl.py new file mode 100644 index 0000000..d1e9d7b --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_impl.py @@ -0,0 +1,410 @@ +import json +import os +import sys +import tempfile +from contextlib import contextmanager +from os.path import abspath +from os.path import join as pjoin +from subprocess import STDOUT, check_call, check_output +from typing import TYPE_CHECKING, Any, Iterator, Mapping, Optional, Sequence + +from ._in_process import _in_proc_script_path + +if TYPE_CHECKING: + from typing import Protocol + + class SubprocessRunner(Protocol): + """A protocol for the subprocess runner.""" + + def __call__( + self, + cmd: Sequence[str], + cwd: Optional[str] = None, + extra_environ: Optional[Mapping[str, str]] = None, + ) -> None: + ... + + +def write_json(obj: Mapping[str, Any], path: str, **kwargs) -> None: + with open(path, "w", encoding="utf-8") as f: + json.dump(obj, f, **kwargs) + + +def read_json(path: str) -> Mapping[str, Any]: + with open(path, encoding="utf-8") as f: + return json.load(f) + + +class BackendUnavailable(Exception): + """Will be raised if the backend cannot be imported in the hook process.""" + + def __init__( + self, + traceback: str, + message: Optional[str] = None, + backend_name: Optional[str] = None, + backend_path: Optional[Sequence[str]] = None, + ) -> None: + # Preserving arg order for the sake of API backward compatibility. + self.backend_name = backend_name + self.backend_path = backend_path + self.traceback = traceback + super().__init__(message or "Error while importing backend") + + +class HookMissing(Exception): + """Will be raised on missing hooks (if a fallback can't be used).""" + + def __init__(self, hook_name: str) -> None: + super().__init__(hook_name) + self.hook_name = hook_name + + +class UnsupportedOperation(Exception): + """May be raised by build_sdist if the backend indicates that it can't.""" + + def __init__(self, traceback: str) -> None: + self.traceback = traceback + + +def default_subprocess_runner( + cmd: Sequence[str], + cwd: Optional[str] = None, + extra_environ: Optional[Mapping[str, str]] = None, +) -> None: + """The default method of calling the wrapper subprocess. + + This uses :func:`subprocess.check_call` under the hood. + """ + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + + check_call(cmd, cwd=cwd, env=env) + + +def quiet_subprocess_runner( + cmd: Sequence[str], + cwd: Optional[str] = None, + extra_environ: Optional[Mapping[str, str]] = None, +) -> None: + """Call the subprocess while suppressing output. + + This uses :func:`subprocess.check_output` under the hood. + """ + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + + check_output(cmd, cwd=cwd, env=env, stderr=STDOUT) + + +def norm_and_check(source_tree: str, requested: str) -> str: + """Normalise and check a backend path. + + Ensure that the requested backend path is specified as a relative path, + and resolves to a location under the given source tree. + + Return an absolute version of the requested path. + """ + if os.path.isabs(requested): + raise ValueError("paths must be relative") + + abs_source = os.path.abspath(source_tree) + abs_requested = os.path.normpath(os.path.join(abs_source, requested)) + # We have to use commonprefix for Python 2.7 compatibility. So we + # normalise case to avoid problems because commonprefix is a character + # based comparison :-( + norm_source = os.path.normcase(abs_source) + norm_requested = os.path.normcase(abs_requested) + if os.path.commonprefix([norm_source, norm_requested]) != norm_source: + raise ValueError("paths must be inside source tree") + + return abs_requested + + +class BuildBackendHookCaller: + """A wrapper to call the build backend hooks for a source directory.""" + + def __init__( + self, + source_dir: str, + build_backend: str, + backend_path: Optional[Sequence[str]] = None, + runner: Optional["SubprocessRunner"] = None, + python_executable: Optional[str] = None, + ) -> None: + """ + :param source_dir: The source directory to invoke the build backend for + :param build_backend: The build backend spec + :param backend_path: Additional path entries for the build backend spec + :param runner: The :ref:`subprocess runner ` to use + :param python_executable: + The Python executable used to invoke the build backend + """ + if runner is None: + runner = default_subprocess_runner + + self.source_dir = abspath(source_dir) + self.build_backend = build_backend + if backend_path: + backend_path = [norm_and_check(self.source_dir, p) for p in backend_path] + self.backend_path = backend_path + self._subprocess_runner = runner + if not python_executable: + python_executable = sys.executable + self.python_executable = python_executable + + @contextmanager + def subprocess_runner(self, runner: "SubprocessRunner") -> Iterator[None]: + """A context manager for temporarily overriding the default + :ref:`subprocess runner `. + + :param runner: The new subprocess runner to use within the context. + + .. code-block:: python + + hook_caller = BuildBackendHookCaller(...) + with hook_caller.subprocess_runner(quiet_subprocess_runner): + ... + """ + prev = self._subprocess_runner + self._subprocess_runner = runner + try: + yield + finally: + self._subprocess_runner = prev + + def _supported_features(self) -> Sequence[str]: + """Return the list of optional features supported by the backend.""" + return self._call_hook("_supported_features", {}) + + def get_requires_for_build_wheel( + self, + config_settings: Optional[Mapping[str, Any]] = None, + ) -> Sequence[str]: + """Get additional dependencies required for building a wheel. + + :param config_settings: The configuration settings for the build backend + :returns: A list of :pep:`dependency specifiers <508>`. + + .. admonition:: Fallback + + If the build backend does not defined a hook with this name, an + empty list will be returned. + """ + return self._call_hook( + "get_requires_for_build_wheel", {"config_settings": config_settings} + ) + + def prepare_metadata_for_build_wheel( + self, + metadata_directory: str, + config_settings: Optional[Mapping[str, Any]] = None, + _allow_fallback: bool = True, + ) -> str: + """Prepare a ``*.dist-info`` folder with metadata for this project. + + :param metadata_directory: The directory to write the metadata to + :param config_settings: The configuration settings for the build backend + :param _allow_fallback: + Whether to allow the fallback to building a wheel and extracting + the metadata from it. Should be passed as a keyword argument only. + + :returns: Name of the newly created subfolder within + ``metadata_directory``, containing the metadata. + + .. admonition:: Fallback + + If the build backend does not define a hook with this name and + ``_allow_fallback`` is truthy, the backend will be asked to build a + wheel via the ``build_wheel`` hook and the dist-info extracted from + that will be returned. + """ + return self._call_hook( + "prepare_metadata_for_build_wheel", + { + "metadata_directory": abspath(metadata_directory), + "config_settings": config_settings, + "_allow_fallback": _allow_fallback, + }, + ) + + def build_wheel( + self, + wheel_directory: str, + config_settings: Optional[Mapping[str, Any]] = None, + metadata_directory: Optional[str] = None, + ) -> str: + """Build a wheel from this project. + + :param wheel_directory: The directory to write the wheel to + :param config_settings: The configuration settings for the build backend + :param metadata_directory: The directory to reuse existing metadata from + :returns: + The name of the newly created wheel within ``wheel_directory``. + + .. admonition:: Interaction with fallback + + If the ``build_wheel`` hook was called in the fallback for + :meth:`prepare_metadata_for_build_wheel`, the build backend would + not be invoked. Instead, the previously built wheel will be copied + to ``wheel_directory`` and the name of that file will be returned. + """ + if metadata_directory is not None: + metadata_directory = abspath(metadata_directory) + return self._call_hook( + "build_wheel", + { + "wheel_directory": abspath(wheel_directory), + "config_settings": config_settings, + "metadata_directory": metadata_directory, + }, + ) + + def get_requires_for_build_editable( + self, + config_settings: Optional[Mapping[str, Any]] = None, + ) -> Sequence[str]: + """Get additional dependencies required for building an editable wheel. + + :param config_settings: The configuration settings for the build backend + :returns: A list of :pep:`dependency specifiers <508>`. + + .. admonition:: Fallback + + If the build backend does not defined a hook with this name, an + empty list will be returned. + """ + return self._call_hook( + "get_requires_for_build_editable", {"config_settings": config_settings} + ) + + def prepare_metadata_for_build_editable( + self, + metadata_directory: str, + config_settings: Optional[Mapping[str, Any]] = None, + _allow_fallback: bool = True, + ) -> Optional[str]: + """Prepare a ``*.dist-info`` folder with metadata for this project. + + :param metadata_directory: The directory to write the metadata to + :param config_settings: The configuration settings for the build backend + :param _allow_fallback: + Whether to allow the fallback to building a wheel and extracting + the metadata from it. Should be passed as a keyword argument only. + :returns: Name of the newly created subfolder within + ``metadata_directory``, containing the metadata. + + .. admonition:: Fallback + + If the build backend does not define a hook with this name and + ``_allow_fallback`` is truthy, the backend will be asked to build a + wheel via the ``build_editable`` hook and the dist-info + extracted from that will be returned. + """ + return self._call_hook( + "prepare_metadata_for_build_editable", + { + "metadata_directory": abspath(metadata_directory), + "config_settings": config_settings, + "_allow_fallback": _allow_fallback, + }, + ) + + def build_editable( + self, + wheel_directory: str, + config_settings: Optional[Mapping[str, Any]] = None, + metadata_directory: Optional[str] = None, + ) -> str: + """Build an editable wheel from this project. + + :param wheel_directory: The directory to write the wheel to + :param config_settings: The configuration settings for the build backend + :param metadata_directory: The directory to reuse existing metadata from + :returns: + The name of the newly created wheel within ``wheel_directory``. + + .. admonition:: Interaction with fallback + + If the ``build_editable`` hook was called in the fallback for + :meth:`prepare_metadata_for_build_editable`, the build backend + would not be invoked. Instead, the previously built wheel will be + copied to ``wheel_directory`` and the name of that file will be + returned. + """ + if metadata_directory is not None: + metadata_directory = abspath(metadata_directory) + return self._call_hook( + "build_editable", + { + "wheel_directory": abspath(wheel_directory), + "config_settings": config_settings, + "metadata_directory": metadata_directory, + }, + ) + + def get_requires_for_build_sdist( + self, + config_settings: Optional[Mapping[str, Any]] = None, + ) -> Sequence[str]: + """Get additional dependencies required for building an sdist. + + :returns: A list of :pep:`dependency specifiers <508>`. + """ + return self._call_hook( + "get_requires_for_build_sdist", {"config_settings": config_settings} + ) + + def build_sdist( + self, + sdist_directory: str, + config_settings: Optional[Mapping[str, Any]] = None, + ) -> str: + """Build an sdist from this project. + + :returns: + The name of the newly created sdist within ``wheel_directory``. + """ + return self._call_hook( + "build_sdist", + { + "sdist_directory": abspath(sdist_directory), + "config_settings": config_settings, + }, + ) + + def _call_hook(self, hook_name: str, kwargs: Mapping[str, Any]) -> Any: + extra_environ = {"_PYPROJECT_HOOKS_BUILD_BACKEND": self.build_backend} + + if self.backend_path: + backend_path = os.pathsep.join(self.backend_path) + extra_environ["_PYPROJECT_HOOKS_BACKEND_PATH"] = backend_path + + with tempfile.TemporaryDirectory() as td: + hook_input = {"kwargs": kwargs} + write_json(hook_input, pjoin(td, "input.json"), indent=2) + + # Run the hook in a subprocess + with _in_proc_script_path() as script: + python = self.python_executable + self._subprocess_runner( + [python, abspath(str(script)), hook_name, td], + cwd=self.source_dir, + extra_environ=extra_environ, + ) + + data = read_json(pjoin(td, "output.json")) + if data.get("unsupported"): + raise UnsupportedOperation(data.get("traceback", "")) + if data.get("no_backend"): + raise BackendUnavailable( + data.get("traceback", ""), + message=data.get("backend_error", ""), + backend_name=self.build_backend, + backend_path=self.backend_path, + ) + if data.get("hook_missing"): + raise HookMissing(data.get("missing_hook_name") or hook_name) + return data["return_val"] diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py new file mode 100644 index 0000000..906d0ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py @@ -0,0 +1,21 @@ +"""This is a subpackage because the directory is on sys.path for _in_process.py + +The subpackage should stay as empty as possible to avoid shadowing modules that +the backend might import. +""" + +import importlib.resources as resources + +try: + resources.files +except AttributeError: + # Python 3.8 compatibility + def _in_proc_script_path(): + return resources.path(__package__, "_in_process.py") + +else: + + def _in_proc_script_path(): + return resources.as_file( + resources.files(__package__).joinpath("_in_process.py") + ) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..32a03c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-312.pyc new file mode 100644 index 0000000..df90bf4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py new file mode 100644 index 0000000..d689bab --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py @@ -0,0 +1,389 @@ +"""This is invoked in a subprocess to call the build backend hooks. + +It expects: +- Command line args: hook_name, control_dir +- Environment variables: + _PYPROJECT_HOOKS_BUILD_BACKEND=entry.point:spec + _PYPROJECT_HOOKS_BACKEND_PATH=paths (separated with os.pathsep) +- control_dir/input.json: + - {"kwargs": {...}} + +Results: +- control_dir/output.json + - {"return_val": ...} +""" +import json +import os +import os.path +import re +import shutil +import sys +import traceback +from glob import glob +from importlib import import_module +from importlib.machinery import PathFinder +from os.path import join as pjoin + +# This file is run as a script, and `import wrappers` is not zip-safe, so we +# include write_json() and read_json() from wrappers.py. + + +def write_json(obj, path, **kwargs): + with open(path, "w", encoding="utf-8") as f: + json.dump(obj, f, **kwargs) + + +def read_json(path): + with open(path, encoding="utf-8") as f: + return json.load(f) + + +class BackendUnavailable(Exception): + """Raised if we cannot import the backend""" + + def __init__(self, message, traceback=None): + super().__init__(message) + self.message = message + self.traceback = traceback + + +class HookMissing(Exception): + """Raised if a hook is missing and we are not executing the fallback""" + + def __init__(self, hook_name=None): + super().__init__(hook_name) + self.hook_name = hook_name + + +def _build_backend(): + """Find and load the build backend""" + backend_path = os.environ.get("_PYPROJECT_HOOKS_BACKEND_PATH") + ep = os.environ["_PYPROJECT_HOOKS_BUILD_BACKEND"] + mod_path, _, obj_path = ep.partition(":") + + if backend_path: + # Ensure in-tree backend directories have the highest priority when importing. + extra_pathitems = backend_path.split(os.pathsep) + sys.meta_path.insert(0, _BackendPathFinder(extra_pathitems, mod_path)) + + try: + obj = import_module(mod_path) + except ImportError: + msg = f"Cannot import {mod_path!r}" + raise BackendUnavailable(msg, traceback.format_exc()) + + if obj_path: + for path_part in obj_path.split("."): + obj = getattr(obj, path_part) + return obj + + +class _BackendPathFinder: + """Implements the MetaPathFinder interface to locate modules in ``backend-path``. + + Since the environment provided by the frontend can contain all sorts of + MetaPathFinders, the only way to ensure the backend is loaded from the + right place is to prepend our own. + """ + + def __init__(self, backend_path, backend_module): + self.backend_path = backend_path + self.backend_module = backend_module + self.backend_parent, _, _ = backend_module.partition(".") + + def find_spec(self, fullname, _path, _target=None): + if "." in fullname: + # Rely on importlib to find nested modules based on parent's path + return None + + # Ignore other items in _path or sys.path and use backend_path instead: + spec = PathFinder.find_spec(fullname, path=self.backend_path) + if spec is None and fullname == self.backend_parent: + # According to the spec, the backend MUST be loaded from backend-path. + # Therefore, we can halt the import machinery and raise a clean error. + msg = f"Cannot find module {self.backend_module!r} in {self.backend_path!r}" + raise BackendUnavailable(msg) + + return spec + + if sys.version_info >= (3, 8): + + def find_distributions(self, context=None): + # Delayed import: Python 3.7 does not contain importlib.metadata + from importlib.metadata import DistributionFinder, MetadataPathFinder + + context = DistributionFinder.Context(path=self.backend_path) + return MetadataPathFinder.find_distributions(context=context) + + +def _supported_features(): + """Return the list of options features supported by the backend. + + Returns a list of strings. + The only possible value is 'build_editable'. + """ + backend = _build_backend() + features = [] + if hasattr(backend, "build_editable"): + features.append("build_editable") + return features + + +def get_requires_for_build_wheel(config_settings): + """Invoke the optional get_requires_for_build_wheel hook + + Returns [] if the hook is not defined. + """ + backend = _build_backend() + try: + hook = backend.get_requires_for_build_wheel + except AttributeError: + return [] + else: + return hook(config_settings) + + +def get_requires_for_build_editable(config_settings): + """Invoke the optional get_requires_for_build_editable hook + + Returns [] if the hook is not defined. + """ + backend = _build_backend() + try: + hook = backend.get_requires_for_build_editable + except AttributeError: + return [] + else: + return hook(config_settings) + + +def prepare_metadata_for_build_wheel( + metadata_directory, config_settings, _allow_fallback +): + """Invoke optional prepare_metadata_for_build_wheel + + Implements a fallback by building a wheel if the hook isn't defined, + unless _allow_fallback is False in which case HookMissing is raised. + """ + backend = _build_backend() + try: + hook = backend.prepare_metadata_for_build_wheel + except AttributeError: + if not _allow_fallback: + raise HookMissing() + else: + return hook(metadata_directory, config_settings) + # fallback to build_wheel outside the try block to avoid exception chaining + # which can be confusing to users and is not relevant + whl_basename = backend.build_wheel(metadata_directory, config_settings) + return _get_wheel_metadata_from_wheel( + whl_basename, metadata_directory, config_settings + ) + + +def prepare_metadata_for_build_editable( + metadata_directory, config_settings, _allow_fallback +): + """Invoke optional prepare_metadata_for_build_editable + + Implements a fallback by building an editable wheel if the hook isn't + defined, unless _allow_fallback is False in which case HookMissing is + raised. + """ + backend = _build_backend() + try: + hook = backend.prepare_metadata_for_build_editable + except AttributeError: + if not _allow_fallback: + raise HookMissing() + try: + build_hook = backend.build_editable + except AttributeError: + raise HookMissing(hook_name="build_editable") + else: + whl_basename = build_hook(metadata_directory, config_settings) + return _get_wheel_metadata_from_wheel( + whl_basename, metadata_directory, config_settings + ) + else: + return hook(metadata_directory, config_settings) + + +WHEEL_BUILT_MARKER = "PYPROJECT_HOOKS_ALREADY_BUILT_WHEEL" + + +def _dist_info_files(whl_zip): + """Identify the .dist-info folder inside a wheel ZipFile.""" + res = [] + for path in whl_zip.namelist(): + m = re.match(r"[^/\\]+-[^/\\]+\.dist-info/", path) + if m: + res.append(path) + if res: + return res + raise Exception("No .dist-info folder found in wheel") + + +def _get_wheel_metadata_from_wheel(whl_basename, metadata_directory, config_settings): + """Extract the metadata from a wheel. + + Fallback for when the build backend does not + define the 'get_wheel_metadata' hook. + """ + from zipfile import ZipFile + + with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), "wb"): + pass # Touch marker file + + whl_file = os.path.join(metadata_directory, whl_basename) + with ZipFile(whl_file) as zipf: + dist_info = _dist_info_files(zipf) + zipf.extractall(path=metadata_directory, members=dist_info) + return dist_info[0].split("/")[0] + + +def _find_already_built_wheel(metadata_directory): + """Check for a wheel already built during the get_wheel_metadata hook.""" + if not metadata_directory: + return None + metadata_parent = os.path.dirname(metadata_directory) + if not os.path.isfile(pjoin(metadata_parent, WHEEL_BUILT_MARKER)): + return None + + whl_files = glob(os.path.join(metadata_parent, "*.whl")) + if not whl_files: + print("Found wheel built marker, but no .whl files") + return None + if len(whl_files) > 1: + print( + "Found multiple .whl files; unspecified behaviour. " + "Will call build_wheel." + ) + return None + + # Exactly one .whl file + return whl_files[0] + + +def build_wheel(wheel_directory, config_settings, metadata_directory=None): + """Invoke the mandatory build_wheel hook. + + If a wheel was already built in the + prepare_metadata_for_build_wheel fallback, this + will copy it rather than rebuilding the wheel. + """ + prebuilt_whl = _find_already_built_wheel(metadata_directory) + if prebuilt_whl: + shutil.copy2(prebuilt_whl, wheel_directory) + return os.path.basename(prebuilt_whl) + + return _build_backend().build_wheel( + wheel_directory, config_settings, metadata_directory + ) + + +def build_editable(wheel_directory, config_settings, metadata_directory=None): + """Invoke the optional build_editable hook. + + If a wheel was already built in the + prepare_metadata_for_build_editable fallback, this + will copy it rather than rebuilding the wheel. + """ + backend = _build_backend() + try: + hook = backend.build_editable + except AttributeError: + raise HookMissing() + else: + prebuilt_whl = _find_already_built_wheel(metadata_directory) + if prebuilt_whl: + shutil.copy2(prebuilt_whl, wheel_directory) + return os.path.basename(prebuilt_whl) + + return hook(wheel_directory, config_settings, metadata_directory) + + +def get_requires_for_build_sdist(config_settings): + """Invoke the optional get_requires_for_build_wheel hook + + Returns [] if the hook is not defined. + """ + backend = _build_backend() + try: + hook = backend.get_requires_for_build_sdist + except AttributeError: + return [] + else: + return hook(config_settings) + + +class _DummyException(Exception): + """Nothing should ever raise this exception""" + + +class GotUnsupportedOperation(Exception): + """For internal use when backend raises UnsupportedOperation""" + + def __init__(self, traceback): + self.traceback = traceback + + +def build_sdist(sdist_directory, config_settings): + """Invoke the mandatory build_sdist hook.""" + backend = _build_backend() + try: + return backend.build_sdist(sdist_directory, config_settings) + except getattr(backend, "UnsupportedOperation", _DummyException): + raise GotUnsupportedOperation(traceback.format_exc()) + + +HOOK_NAMES = { + "get_requires_for_build_wheel", + "prepare_metadata_for_build_wheel", + "build_wheel", + "get_requires_for_build_editable", + "prepare_metadata_for_build_editable", + "build_editable", + "get_requires_for_build_sdist", + "build_sdist", + "_supported_features", +} + + +def main(): + if len(sys.argv) < 3: + sys.exit("Needs args: hook_name, control_dir") + hook_name = sys.argv[1] + control_dir = sys.argv[2] + if hook_name not in HOOK_NAMES: + sys.exit("Unknown hook: %s" % hook_name) + + # Remove the parent directory from sys.path to avoid polluting the backend + # import namespace with this directory. + here = os.path.dirname(__file__) + if here in sys.path: + sys.path.remove(here) + + hook = globals()[hook_name] + + hook_input = read_json(pjoin(control_dir, "input.json")) + + json_out = {"unsupported": False, "return_val": None} + try: + json_out["return_val"] = hook(**hook_input["kwargs"]) + except BackendUnavailable as e: + json_out["no_backend"] = True + json_out["traceback"] = e.traceback + json_out["backend_error"] = e.message + except GotUnsupportedOperation as e: + json_out["unsupported"] = True + json_out["traceback"] = e.traceback + except HookMissing as e: + json_out["hook_missing"] = True + json_out["missing_hook_name"] = e.hook_name or hook_name + + write_json(json_out, pjoin(control_dir, "output.json"), indent=2) + + +if __name__ == "__main__": + main() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/requests/LICENSE new file mode 100644 index 0000000..67db858 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__init__.py new file mode 100644 index 0000000..22d2c42 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__init__.py @@ -0,0 +1,214 @@ +# __ +# /__) _ _ _ _ _/ _ +# / ( (- (/ (/ (- _) / _) +# / + +""" +Requests HTTP Library +~~~~~~~~~~~~~~~~~~~~~ + +Requests is an HTTP library, written in Python, for human beings. +Basic GET usage: + + >>> import requests + >>> r = requests.get('https://www.python.org') + >>> r.status_code + 200 + >>> b'Python is a programming language' in r.content + True + +... or POST: + + >>> payload = dict(key1='value1', key2='value2') + >>> r = requests.post('https://httpbin.org/post', data=payload) + >>> print(r.text) + { + ... + "form": { + "key1": "value1", + "key2": "value2" + }, + ... + } + +The other HTTP methods are supported - see `requests.api`. Full documentation +is at . + +:copyright: (c) 2017 by Kenneth Reitz. +:license: Apache 2.0, see LICENSE for more details. +""" + +from __future__ import annotations + +import warnings + +from pip._vendor import urllib3 + +from .exceptions import RequestsDependencyWarning + +charset_normalizer_version = None +chardet_version = None + + +def check_compatibility( + urllib3_version: str, + chardet_version: str | None, + charset_normalizer_version: str | None, +) -> None: + urllib3_version_list = urllib3_version.split(".")[:3] + assert urllib3_version_list != ["dev"] # Verify urllib3 isn't installed from git. + + # Sometimes, urllib3 only reports its version as 16.1. + if len(urllib3_version_list) == 2: + urllib3_version_list.append("0") + + # Check urllib3 for compatibility. + major, minor, patch = urllib3_version_list # noqa: F811 + major, minor, patch = int(major), int(minor), int(patch) + # urllib3 >= 1.21.1 + assert major >= 1 + if major == 1: + assert minor >= 21 + + # Check charset_normalizer for compatibility. + if chardet_version: + major, minor, patch = chardet_version.split(".")[:3] + major, minor, patch = int(major), int(minor), int(patch) + # chardet_version >= 3.0.2, < 8.0.0 + assert (3, 0, 2) <= (major, minor, patch) < (8, 0, 0) + elif charset_normalizer_version: + major, minor, patch = charset_normalizer_version.split(".")[:3] + major, minor, patch = int(major), int(minor), int(patch) + # charset_normalizer >= 2.0.0 < 4.0.0 + assert (2, 0, 0) <= (major, minor, patch) < (4, 0, 0) + else: + # pip does not need or use character detection + pass + + +def _check_cryptography(cryptography_version: str) -> None: + # cryptography < 1.3.4 + try: + cryptography_version_list = list(map(int, cryptography_version.split("."))) + except ValueError: + return + + if cryptography_version_list < [1, 3, 4]: + warning = f"Old version of cryptography ({cryptography_version_list}) may cause slowdown." + warnings.warn(warning, RequestsDependencyWarning) + + +# Check imported dependencies for compatibility. +try: + check_compatibility( + urllib3.__version__, # type: ignore[reportPrivateImportUsage] + chardet_version, # type: ignore[reportUnknownArgumentType] + charset_normalizer_version, + ) +except (AssertionError, ValueError): + warnings.warn( + f"urllib3 ({urllib3.__version__}) or chardet " # type: ignore[reportPrivateImportUsage] + f"({chardet_version})/charset_normalizer ({charset_normalizer_version}) " + "doesn't match a supported version!", + RequestsDependencyWarning, + ) + +# Attempt to enable urllib3's fallback for SNI support +# if the standard library doesn't support SNI or the +# 'ssl' library isn't available. +try: + # Note: This logic prevents upgrading cryptography on Windows, if imported + # as part of pip. + from pip._internal.utils.compat import WINDOWS + if not WINDOWS: + raise ImportError("pip internals: don't import cryptography on Windows") + try: + import ssl + except ImportError: + ssl = None + + if not getattr(ssl, "HAS_SNI", False): + from pip._vendor.urllib3.contrib import pyopenssl + + pyopenssl.inject_into_urllib3() + + # Check cryptography version + from cryptography import ( # type: ignore[reportMissingImports] + __version__ as cryptography_version, # type: ignore[reportUnknownVariableType] + ) + + _check_cryptography(cryptography_version) # type: ignore[reportUnknownArgumentType] +except ImportError: + pass + +# urllib3's DependencyWarnings should be silenced. +from pip._vendor.urllib3.exceptions import DependencyWarning + +warnings.simplefilter("ignore", DependencyWarning) + +# Set default logging handler to avoid "No handler found" warnings. +import logging +from logging import NullHandler + +from . import packages, utils +from .__version__ import ( + __author__, + __author_email__, + __build__, + __cake__, + __copyright__, + __description__, + __license__, + __title__, + __url__, + __version__, +) +from .api import delete, get, head, options, patch, post, put, request +from .exceptions import ( + ConnectionError, + ConnectTimeout, + FileModeWarning, + HTTPError, + JSONDecodeError, + ReadTimeout, + RequestException, + Timeout, + TooManyRedirects, + URLRequired, +) +from .models import PreparedRequest, Request, Response +from .sessions import Session, session +from .status_codes import codes + +__all__ = ( + "ConnectionError", + "ConnectTimeout", + "HTTPError", + "JSONDecodeError", + "PreparedRequest", + "ReadTimeout", + "Request", + "RequestException", + "Response", + "Session", + "Timeout", + "TooManyRedirects", + "URLRequired", + "codes", + "delete", + "get", + "head", + "options", + "packages", + "patch", + "post", + "put", + "request", + "session", + "utils", +) + +logging.getLogger(__name__).addHandler(NullHandler()) + +# FileModeWarnings go off per the default. +warnings.simplefilter("default", FileModeWarning, append=True) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..56e2df6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-312.pyc new file mode 100644 index 0000000..d228850 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-312.pyc new file mode 100644 index 0000000..b47c6b6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/_types.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/_types.cpython-312.pyc new file mode 100644 index 0000000..57654d2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/_types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-312.pyc new file mode 100644 index 0000000..84bf076 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/api.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/api.cpython-312.pyc new file mode 100644 index 0000000..ef1f1dc Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/api.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-312.pyc new file mode 100644 index 0000000..3acb624 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-312.pyc new file mode 100644 index 0000000..940440b Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-312.pyc new file mode 100644 index 0000000..ab00e67 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-312.pyc new file mode 100644 index 0000000..c05b344 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..d6fbda6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/help.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/help.cpython-312.pyc new file mode 100644 index 0000000..4fc453f Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/help.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-312.pyc new file mode 100644 index 0000000..1186b90 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/models.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000..070fe7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/models.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-312.pyc new file mode 100644 index 0000000..fa52fd7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-312.pyc new file mode 100644 index 0000000..372bc00 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-312.pyc new file mode 100644 index 0000000..119c379 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-312.pyc new file mode 100644 index 0000000..f8700c9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..bb05f03 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__version__.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__version__.py new file mode 100644 index 0000000..fea814e --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__version__.py @@ -0,0 +1,14 @@ +# .-. .-. .-. . . .-. .-. .-. .-. +# |( |- |.| | | |- `-. | `-. +# ' ' `-' `-`.`-' `-' `-' ' `-' + +__title__ = "requests" +__description__ = "Python HTTP for Humans." +__url__ = "https://requests.readthedocs.io" +__version__ = "2.34.2" +__build__ = 0x023402 +__author__ = "Kenneth Reitz" +__author_email__ = "me@kennethreitz.org" +__license__ = "Apache-2.0" +__copyright__ = "Copyright Kenneth Reitz" +__cake__ = "\u2728 \U0001f370 \u2728" diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/_internal_utils.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/_internal_utils.py new file mode 100644 index 0000000..0466a7d --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/_internal_utils.py @@ -0,0 +1,51 @@ +""" +requests._internal_utils +~~~~~~~~~~~~~~ + +Provides utility functions that are consumed internally by Requests +which depend on extremely few external helpers (such as compat) +""" + +import re + +from .compat import builtin_str + +_VALID_HEADER_NAME_RE_BYTE = re.compile(rb"^[^:\s][^:\r\n]*\Z") +_VALID_HEADER_NAME_RE_STR = re.compile(r"^[^:\s][^:\r\n]*\Z") +_VALID_HEADER_VALUE_RE_BYTE = re.compile(rb"^\S[^\r\n]*\Z|^\Z") +_VALID_HEADER_VALUE_RE_STR = re.compile(r"^\S[^\r\n]*\Z|^\Z") + +_HEADER_VALIDATORS_STR = (_VALID_HEADER_NAME_RE_STR, _VALID_HEADER_VALUE_RE_STR) +_HEADER_VALIDATORS_BYTE = (_VALID_HEADER_NAME_RE_BYTE, _VALID_HEADER_VALUE_RE_BYTE) +HEADER_VALIDATORS = { + bytes: _HEADER_VALIDATORS_BYTE, + str: _HEADER_VALIDATORS_STR, +} + + +def to_native_string(string: str | bytes, encoding: str = "ascii") -> str: + """Given a string object, regardless of type, returns a representation of + that string in the native string type, encoding and decoding where + necessary. This assumes ASCII unless told otherwise. + """ + if isinstance(string, builtin_str): + out = string + else: + out = string.decode(encoding) + + return out + + +def unicode_is_ascii(u_string: str) -> bool: + """Determine if unicode string only contains ASCII characters. + + :param str u_string: unicode string to check. Must be unicode + and not Python 2 `str`. + :rtype: bool + """ + assert isinstance(u_string, str) + try: + u_string.encode("ascii") + return True + except UnicodeEncodeError: + return False diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/_types.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/_types.py new file mode 100644 index 0000000..b2273d6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/_types.py @@ -0,0 +1,183 @@ +""" +requests._types +~~~~~~~~~~~~~~~ + +This module contains type aliases used internally by the Requests library. +These types are not part of the public API and must not be relied upon +by external code. +""" + +from __future__ import annotations + +from collections.abc import Callable, Iterable, Mapping, MutableMapping, Sequence +from typing import ( + TYPE_CHECKING, + Any, + Protocol, + TypeAlias, + TypeVar, + runtime_checkable, +) + +_T_co = TypeVar("_T_co", covariant=True) +_KT_co = TypeVar("_KT_co", covariant=True) +_VT_co = TypeVar("_VT_co", covariant=True) + + +@runtime_checkable +class SupportsRead(Protocol[_T_co]): + def read(self, length: int = ..., /) -> _T_co: ... + + +@runtime_checkable +class SupportsItems(Protocol[_KT_co, _VT_co]): + def items(self) -> Iterable[tuple[_KT_co, _VT_co]]: ... + + +# These are needed at runtime for default_hooks() return type +HookType: TypeAlias = Callable[["Response"], Any] +HooksInputType: TypeAlias = Mapping[str, Iterable[HookType] | HookType] + + +def is_prepared(request: PreparedRequest) -> TypeIs[_ValidatedRequest]: + """Verify a PreparedRequest has been fully prepared.""" + if TYPE_CHECKING: + return request.url is not None and request.method is not None + # noop at runtime to avoid AssertionError + return True + + +if TYPE_CHECKING: + from http.cookiejar import CookieJar + from typing import TypeAlias, TypedDict + + from typing_extensions import ( + Buffer, # TODO: move to collections.abc when Python >= 3.12 + TypeIs, # TODO: move to typing when Python >= 3.13 + ) + + from .auth import AuthBase + from .cookies import RequestsCookieJar + from .models import PreparedRequest, Response + from .structures import CaseInsensitiveDict + + class _ValidatedRequest(PreparedRequest): + """Subtype asserting a PreparedRequest has been fully prepared before calling. + + The override suppression is required because mutable attribute types are + invariant (Liskov), but we only narrow after preparation is complete. This + is the explicit contract for Requests but Python's typing doesn't have a + better way to represent the requirement. + """ + + url: str # type: ignore[reportIncompatibleVariableOverride] + method: str # type: ignore[reportIncompatibleVariableOverride] + + # Type aliases for core API concepts (ordered by request() signature) + UriType: TypeAlias = str | bytes + + _ParamsMappingKeyType: TypeAlias = str | bytes | int | float + _ParamsMappingValueType: TypeAlias = ( + str | bytes | int | float | Iterable[str | bytes | int | float] | None + ) + ParamsType: TypeAlias = ( + SupportsItems[_ParamsMappingKeyType, _ParamsMappingValueType] + | tuple[tuple[_ParamsMappingKeyType, _ParamsMappingValueType], ...] + | Iterable[tuple[_ParamsMappingKeyType, _ParamsMappingValueType]] + | str + | bytes + | None + ) + + KVDataType: TypeAlias = Iterable[tuple[Any, Any]] | SupportsItems[Any, Any] + + RawDataType: TypeAlias = KVDataType | str | bytes + StreamDataType: TypeAlias = SupportsRead[str | bytes] + EncodableDataType: TypeAlias = RawDataType | StreamDataType + + DataType: TypeAlias = ( + KVDataType + | Iterable[bytes | str] + | str + | bytes + | Buffer + | SupportsRead[str | bytes] + | None + ) + + BodyType: TypeAlias = ( + bytes | str | Iterable[bytes | str] | SupportsRead[bytes | str] | None + ) + + HeadersType: TypeAlias = Mapping[str, str | bytes] | None + + CookiesType: TypeAlias = RequestsCookieJar | Mapping[str, str] + + # Building blocks for FilesType + _FileName: TypeAlias = str | None + _FileContent: TypeAlias = SupportsRead[str | bytes] | str | bytes + _FileSpecBasic: TypeAlias = tuple[_FileName, _FileContent] + _FileSpecWithContentType: TypeAlias = tuple[_FileName, _FileContent, str] + _FileSpecWithHeaders: TypeAlias = tuple[ + _FileName, _FileContent, str, CaseInsensitiveDict[str] | Mapping[str, str] + ] + _FileSpec: TypeAlias = ( + _FileContent | _FileSpecBasic | _FileSpecWithContentType | _FileSpecWithHeaders + ) + FilesType: TypeAlias = ( + Mapping[str, _FileSpec] | Iterable[tuple[str, _FileSpec]] | None + ) + + AuthType: TypeAlias = ( + tuple[str, str] | AuthBase | Callable[[PreparedRequest], PreparedRequest] | None + ) + + TimeoutType: TypeAlias = float | tuple[float | None, float | None] | None + ProxiesType: TypeAlias = MutableMapping[str, str] + HooksType: TypeAlias = dict[str, list[HookType]] | None + VerifyType: TypeAlias = bool | str + CertType: TypeAlias = str | tuple[str, str] | None + JsonType: TypeAlias = ( + None + | bool + | int + | float + | str + | Sequence["JsonType"] + | Mapping[str, "JsonType"] + ) + + # TypedDicts for Unpack kwargs (PEP 692) + + class BaseRequestKwargs(TypedDict, total=False): + headers: HeadersType + cookies: RequestsCookieJar | CookieJar | dict[str, str] | None + files: FilesType + auth: AuthType + timeout: TimeoutType + allow_redirects: bool + proxies: dict[str, str] | None + hooks: HooksInputType | None + stream: bool | None + verify: VerifyType | None + cert: CertType + + class RequestKwargs(BaseRequestKwargs, total=False): + """kwargs for request(), options(), head(), delete().""" + + params: ParamsType + data: DataType + json: JsonType + + class GetKwargs(BaseRequestKwargs, total=False): + data: DataType + json: JsonType + + class PostKwargs(BaseRequestKwargs, total=False): + params: ParamsType + + class DataKwargs(BaseRequestKwargs, total=False): + """kwargs for put(), patch().""" + + params: ParamsType + json: JsonType diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/adapters.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/adapters.py new file mode 100644 index 0000000..5bf54af --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/adapters.py @@ -0,0 +1,748 @@ +""" +requests.adapters +~~~~~~~~~~~~~~~~~ + +This module contains the transport adapters that Requests uses to define +and maintain connections. +""" + +from __future__ import annotations + +import os.path +import socket # noqa: F401 # type: ignore[reportUnusedImport] +import typing +import warnings +from typing import Any + +from pip._vendor.urllib3.exceptions import ( + ClosedPoolError, + ConnectTimeoutError, + LocationValueError, + MaxRetryError, + NewConnectionError, + ProtocolError, + ReadTimeoutError, + ResponseError, +) +from pip._vendor.urllib3.exceptions import HTTPError as _HTTPError +from pip._vendor.urllib3.exceptions import InvalidHeader as _InvalidHeader +from pip._vendor.urllib3.exceptions import ProxyError as _ProxyError +from pip._vendor.urllib3.exceptions import SSLError as _SSLError +from pip._vendor.urllib3.poolmanager import PoolManager, proxy_from_url +from pip._vendor.urllib3.util import Timeout as TimeoutSauce +from pip._vendor.urllib3.util import parse_url +from pip._vendor.urllib3.util.retry import Retry + +from .auth import _basic_auth_str # type: ignore[reportPrivateUsage] +from .compat import basestring, urlparse +from .cookies import extract_cookies_to_jar +from .exceptions import ( + ConnectionError, + ConnectTimeout, + InvalidHeader, + InvalidProxyURL, + InvalidSchema, + InvalidURL, + ProxyError, + ReadTimeout, + RetryError, + SSLError, +) +from .models import Response +from .structures import CaseInsensitiveDict +from .utils import ( + DEFAULT_CA_BUNDLE_PATH, + get_auth_from_url, + get_encoding_from_headers, + prepend_scheme_if_needed, + select_proxy, + urldefragauth, +) + +try: + from pip._vendor.urllib3.contrib.socks import SOCKSProxyManager # type: ignore[assignment] +except ImportError: + + def SOCKSProxyManager(*args: Any, **kwargs: Any) -> None: + raise InvalidSchema("Missing dependencies for SOCKS support.") + + +if typing.TYPE_CHECKING: + from pip._vendor.urllib3.connectionpool import HTTPConnectionPool + from pip._vendor.urllib3.poolmanager import PoolManager as _PoolManager + + from . import _types as _t + from .models import PreparedRequest + +from ._types import is_prepared as _is_prepared + +DEFAULT_POOLBLOCK = False +DEFAULT_POOLSIZE = 10 +DEFAULT_RETRIES = 0 +DEFAULT_POOL_TIMEOUT = None + + +def _urllib3_request_context( + request: PreparedRequest, + verify: bool | str | None, + client_cert: tuple[str, str] | str | None, + poolmanager: PoolManager, +) -> tuple[dict[str, Any], dict[str, Any]]: + host_params: dict[str, Any] = {} + pool_kwargs: dict[str, Any] = {} + parsed_request_url = urlparse(request.url) + scheme = parsed_request_url.scheme.lower() + port = parsed_request_url.port + + cert_reqs = "CERT_REQUIRED" + if verify is False: + cert_reqs = "CERT_NONE" + elif isinstance(verify, str): + if not os.path.isdir(verify): + pool_kwargs["ca_certs"] = verify + else: + pool_kwargs["ca_cert_dir"] = verify + pool_kwargs["cert_reqs"] = cert_reqs + if client_cert is not None: + if isinstance(client_cert, tuple) and len(client_cert) == 2: + pool_kwargs["cert_file"] = client_cert[0] + pool_kwargs["key_file"] = client_cert[1] + else: + # According to our docs, we allow users to specify just the client + # cert path + pool_kwargs["cert_file"] = client_cert + host_params = { + "scheme": scheme, + "host": parsed_request_url.hostname, + "port": port, + } + return host_params, pool_kwargs + + +class BaseAdapter: + """The Base Transport Adapter""" + + def __init__(self) -> None: + super().__init__() + + def send( + self, + request: PreparedRequest, + stream: bool = False, + timeout: _t.TimeoutType = None, + verify: _t.VerifyType = True, + cert: _t.CertType = None, + proxies: dict[str, str] | None = None, + ) -> Response: + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + """ + raise NotImplementedError + + def close(self) -> None: + """Cleans up adapter specific items.""" + raise NotImplementedError + + +class HTTPAdapter(BaseAdapter): + """The built-in HTTP Adapter for urllib3. + + Provides a general-case interface for Requests sessions to contact HTTP and + HTTPS urls by implementing the Transport Adapter interface. This class will + usually be created by the :class:`Session ` class under the + covers. + + :param pool_connections: The number of urllib3 connection pools to cache. + :param pool_maxsize: The maximum number of connections to save in the pool. + :param max_retries: The maximum number of retries each connection + should attempt. Note, this applies only to failed DNS lookups, socket + connections and connection timeouts, never to requests where data has + made it to the server. By default, Requests does not retry failed + connections. If you need granular control over the conditions under + which we retry a request, import urllib3's ``Retry`` class and pass + that instead. + :param pool_block: Whether the connection pool should block for connections. + + Usage:: + + >>> import requests + >>> s = requests.Session() + >>> a = requests.adapters.HTTPAdapter(max_retries=3) + >>> s.mount('http://', a) + """ + + __attrs__: list[str] = [ + "max_retries", + "config", + "_pool_connections", + "_pool_maxsize", + "_pool_block", + ] + + max_retries: Retry + config: dict[str, Any] + proxy_manager: dict[str, Any] + _pool_connections: int + _pool_maxsize: int + _pool_block: bool + poolmanager: _PoolManager + + def __init__( + self, + pool_connections: int = DEFAULT_POOLSIZE, + pool_maxsize: int = DEFAULT_POOLSIZE, + max_retries: int | Retry = DEFAULT_RETRIES, + pool_block: bool = DEFAULT_POOLBLOCK, + ) -> None: + if max_retries == DEFAULT_RETRIES: + self.max_retries = Retry(0, read=False) + else: + self.max_retries = Retry.from_int(max_retries) + self.config = {} + self.proxy_manager = {} + + super().__init__() + + self._pool_connections = pool_connections + self._pool_maxsize = pool_maxsize + self._pool_block = pool_block + + self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block) + + def __getstate__(self) -> dict[str, Any]: + return {attr: getattr(self, attr, None) for attr in self.__attrs__} + + def __setstate__(self, state: dict[str, Any]) -> None: + # Can't handle by adding 'proxy_manager' to self.__attrs__ because + # self.poolmanager uses a lambda function, which isn't pickleable. + self.proxy_manager = {} + self.config = {} + + for attr, value in state.items(): + setattr(self, attr, value) + + self.init_poolmanager( + self._pool_connections, self._pool_maxsize, block=self._pool_block + ) + + def init_poolmanager( + self, + connections: int, + maxsize: int, + block: bool = DEFAULT_POOLBLOCK, + **pool_kwargs: Any, + ) -> None: + """Initializes a urllib3 PoolManager. + + This method should not be called from user code, and is only + exposed for use when subclassing the + :class:`HTTPAdapter `. + + :param connections: The number of urllib3 connection pools to cache. + :param maxsize: The maximum number of connections to save in the pool. + :param block: Block when no free connections are available. + :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager. + """ + # save these values for pickling + self._pool_connections = connections + self._pool_maxsize = maxsize + self._pool_block = block + + self.poolmanager = PoolManager( + num_pools=connections, + maxsize=maxsize, + block=block, + **pool_kwargs, + ) + + def proxy_manager_for(self, proxy: str, **proxy_kwargs: Any) -> Any: + """Return urllib3 ProxyManager for the given proxy. + + This method should not be called from user code, and is only + exposed for use when subclassing the + :class:`HTTPAdapter `. + + :param proxy: The proxy to return a urllib3 ProxyManager for. + :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager. + :returns: ProxyManager + :rtype: urllib3.ProxyManager + """ + if proxy in self.proxy_manager: + manager = self.proxy_manager[proxy] + elif proxy.lower().startswith("socks"): + username, password = get_auth_from_url(proxy) + manager = self.proxy_manager[proxy] = SOCKSProxyManager( + proxy, + username=username, + password=password, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs, + ) + else: + proxy_headers = self.proxy_headers(proxy) + manager = self.proxy_manager[proxy] = proxy_from_url( + proxy, + proxy_headers=proxy_headers, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs, + ) + + return manager + + def cert_verify( + self, conn: Any, url: str, verify: _t.VerifyType, cert: _t.CertType + ) -> None: + """Verify a SSL certificate. This method should not be called from user + code, and is only exposed for use when subclassing the + :class:`HTTPAdapter `. + + :param conn: The urllib3 connection object associated with the cert. + :param url: The requested URL. + :param verify: Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use + :param cert: The SSL certificate to verify. + """ + if url.lower().startswith("https") and verify: + cert_loc = None + + # Allow self-specified cert location. + if verify is not True: + cert_loc = verify + + if not cert_loc: + cert_loc = DEFAULT_CA_BUNDLE_PATH + + if not cert_loc or not os.path.exists(cert_loc): + raise OSError( + f"Could not find a suitable TLS CA certificate bundle, " + f"invalid path: {cert_loc}" + ) + + conn.cert_reqs = "CERT_REQUIRED" + + if not os.path.isdir(cert_loc): + conn.ca_certs = cert_loc + else: + conn.ca_cert_dir = cert_loc + else: + conn.cert_reqs = "CERT_NONE" + conn.ca_certs = None + conn.ca_cert_dir = None + + if cert: + if not isinstance(cert, basestring): + conn.cert_file = cert[0] + conn.key_file = cert[1] + else: + conn.cert_file = cert + conn.key_file = None + if conn.cert_file and not os.path.exists(conn.cert_file): + raise OSError( + f"Could not find the TLS certificate file, " + f"invalid path: {conn.cert_file}" + ) + if conn.key_file and not os.path.exists(conn.key_file): + raise OSError( + f"Could not find the TLS key file, invalid path: {conn.key_file}" + ) + + def build_response(self, req: PreparedRequest, resp: Any) -> Response: + """Builds a :class:`Response ` object from a urllib3 + response. This should not be called from user code, and is only exposed + for use when subclassing the + :class:`HTTPAdapter ` + + :param req: The :class:`PreparedRequest ` used to generate the response. + :param resp: The urllib3 response object. + :rtype: requests.Response + """ + assert _is_prepared(req) + response = Response() + + # Fallback to None if there's no status_code, for whatever reason. + response.status_code = getattr(resp, "status", None) # type: ignore[assignment] + + # Make headers case-insensitive. + response.headers = CaseInsensitiveDict(getattr(resp, "headers", {})) + + # Set encoding. + response.encoding = get_encoding_from_headers(response.headers) + response.raw = resp + response.reason = response.raw.reason + + if isinstance(req.url, bytes): + response.url = req.url.decode("utf-8") + else: + response.url = req.url + + # Add new cookies from the server. + extract_cookies_to_jar(response.cookies, req, resp) + + # Give the Response some context. + response.request = req + response.connection = self + + return response + + def build_connection_pool_key_attributes( + self, request: PreparedRequest, verify: _t.VerifyType, cert: _t.CertType = None + ) -> tuple[dict[str, Any], dict[str, Any]]: + """Build the PoolKey attributes used by urllib3 to return a connection. + + This looks at the PreparedRequest, the user-specified verify value, + and the value of the cert parameter to determine what PoolKey values + to use to select a connection from a given urllib3 Connection Pool. + + The SSL related pool key arguments are not consistently set. As of + this writing, use the following to determine what keys may be in that + dictionary: + + * If ``verify`` is ``True``, ``"ssl_context"`` will be set and will be the + default Requests SSL Context + * If ``verify`` is ``False``, ``"ssl_context"`` will not be set but + ``"cert_reqs"`` will be set + * If ``verify`` is a string, (i.e., it is a user-specified trust bundle) + ``"ca_certs"`` will be set if the string is not a directory recognized + by :py:func:`os.path.isdir`, otherwise ``"ca_cert_dir"`` will be + set. + * If ``"cert"`` is specified, ``"cert_file"`` will always be set. If + ``"cert"`` is a tuple with a second item, ``"key_file"`` will also + be present + + To override these settings, one may subclass this class, call this + method and use the above logic to change parameters as desired. For + example, if one wishes to use a custom :py:class:`ssl.SSLContext` one + must both set ``"ssl_context"`` and based on what else they require, + alter the other keys to ensure the desired behaviour. + + :param request: + The PreparedRequest being sent over the connection. + :type request: + :class:`~requests.models.PreparedRequest` + :param verify: + Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use. + :param cert: + (optional) Any user-provided SSL certificate for client + authentication (a.k.a., mTLS). This may be a string (i.e., just + the path to a file which holds both certificate and key) or a + tuple of length 2 with the certificate file path and key file + path. + :returns: + A tuple of two dictionaries. The first is the "host parameters" + portion of the Pool Key including scheme, hostname, and port. The + second is a dictionary of SSLContext related parameters. + """ + return _urllib3_request_context(request, verify, cert, self.poolmanager) + + def get_connection_with_tls_context( + self, + request: PreparedRequest, + verify: _t.VerifyType, + proxies: dict[str, str] | None = None, + cert: _t.CertType = None, + ) -> HTTPConnectionPool: + """Returns a urllib3 connection for the given request and TLS settings. + This should not be called from user code, and is only exposed for use + when subclassing the :class:`HTTPAdapter `. + + :param request: + The :class:`PreparedRequest ` object to be sent + over the connection. + :param verify: + Either a boolean, in which case it controls whether we verify the + server's TLS certificate, or a string, in which case it must be a + path to a CA bundle to use. + :param proxies: + (optional) The proxies dictionary to apply to the request. + :param cert: + (optional) Any user-provided SSL certificate to be used for client + authentication (a.k.a., mTLS). + :rtype: + urllib3.HTTPConnectionPool + """ + assert _is_prepared(request) + + proxy = select_proxy(request.url, proxies) + try: + host_params, pool_kwargs = self.build_connection_pool_key_attributes( + request, + verify, + cert, + ) + except ValueError as e: + raise InvalidURL(e, request=request) + if proxy: + proxy = prepend_scheme_if_needed(proxy, "http") + proxy_url = parse_url(proxy) + if not proxy_url.host: + raise InvalidProxyURL( + "Please check proxy URL. It is malformed " + "and could be missing the host." + ) + proxy_manager = self.proxy_manager_for(proxy) + conn = proxy_manager.connection_from_host( + **host_params, pool_kwargs=pool_kwargs + ) + else: + # Only scheme should be lower case + conn = self.poolmanager.connection_from_host( + **host_params, pool_kwargs=pool_kwargs + ) + + return conn + + def get_connection( + self, url: str, proxies: dict[str, str] | None = None + ) -> HTTPConnectionPool: + """DEPRECATED: Users should move to `get_connection_with_tls_context` + for all subclasses of HTTPAdapter using Requests>=2.32.2. + + Returns a urllib3 connection for the given URL. This should not be + called from user code, and is only exposed for use when subclassing the + :class:`HTTPAdapter `. + + :param url: The URL to connect to. + :param proxies: (optional) A Requests-style dictionary of proxies used on this request. + :rtype: urllib3.HTTPConnectionPool + """ + warnings.warn( + ( + "`get_connection` has been deprecated in favor of " + "`get_connection_with_tls_context`. Custom HTTPAdapter subclasses " + "will need to migrate for Requests>=2.32.2. Please see " + "https://github.com/psf/requests/pull/6710 for more details." + ), + DeprecationWarning, + ) + proxy = select_proxy(url, proxies) + + if proxy: + proxy = prepend_scheme_if_needed(proxy, "http") + proxy_url = parse_url(proxy) + if not proxy_url.host: + raise InvalidProxyURL( + "Please check proxy URL. It is malformed " + "and could be missing the host." + ) + proxy_manager = self.proxy_manager_for(proxy) + conn = proxy_manager.connection_from_url(url) + else: + # Only scheme should be lower case + parsed = urlparse(url) + url = parsed.geturl() + conn = self.poolmanager.connection_from_url(url) + + return conn + + def close(self) -> None: + """Disposes of any internal state. + + Currently, this closes the PoolManager and any active ProxyManager, + which closes any pooled connections. + """ + self.poolmanager.clear() + for proxy in self.proxy_manager.values(): + proxy.clear() + + def request_url( + self, request: PreparedRequest, proxies: dict[str, str] | None + ) -> str: + """Obtain the url to use when making the final request. + + If the message is being sent through a HTTP proxy, the full URL has to + be used. Otherwise, we should only use the path portion of the URL. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter `. + + :param request: The :class:`PreparedRequest ` being sent. + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs. + :rtype: str + """ + assert _is_prepared(request) + + proxy = select_proxy(request.url, proxies) + scheme = urlparse(request.url).scheme + + is_proxied_http_request = proxy and scheme != "https" + using_socks_proxy = False + if proxy: + proxy_scheme = urlparse(proxy).scheme.lower() + using_socks_proxy = proxy_scheme.startswith("socks") + + url = request.path_url + + if is_proxied_http_request and not using_socks_proxy: + url = urldefragauth(request.url) + + return url + + def add_headers(self, request: PreparedRequest, **kwargs: Any) -> None: + """Add any headers needed by the connection. As of v2.0 this does + nothing by default, but is left for overriding by users that subclass + the :class:`HTTPAdapter `. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter `. + + :param request: The :class:`PreparedRequest ` to add headers to. + :param kwargs: The keyword arguments from the call to send(). + """ + pass + + def proxy_headers(self, proxy: str) -> dict[str, str]: + """Returns a dictionary of the headers to add to any request sent + through a proxy. This works with urllib3 magic to ensure that they are + correctly sent to the proxy, rather than in a tunnelled request if + CONNECT is being used. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter `. + + :param proxy: The url of the proxy being used for this request. + :rtype: dict + """ + headers: dict[str, str] = {} + username, password = get_auth_from_url(proxy) + + if username: + headers["Proxy-Authorization"] = _basic_auth_str(username, password) + + return headers + + def send( + self, + request: PreparedRequest, + stream: bool = False, + timeout: _t.TimeoutType = None, + verify: _t.VerifyType = True, + cert: _t.CertType = None, + proxies: dict[str, str] | None = None, + ) -> Response: + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + assert _is_prepared(request) + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + resolved_timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + resolved_timeout = timeout + else: + resolved_timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, # type: ignore[arg-type] # urllib3 stubs don't accept Iterable[bytes | str] + headers=request.headers, # type: ignore[arg-type] # urllib3#3072 + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=resolved_timeout, + chunked=chunked, + ) + + except (ProtocolError, OSError) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + + raise ConnectionError(e, request=request) + + except ClosedPoolError as e: + raise ConnectionError(e, request=request) + + except _ProxyError as e: + raise ProxyError(e) + + except (_SSLError, _HTTPError) as e: + if isinstance(e, _SSLError): + # This branch is for urllib3 versions earlier than v1.22 + raise SSLError(e, request=request) + elif isinstance(e, ReadTimeoutError): + raise ReadTimeout(e, request=request) + elif isinstance(e, _InvalidHeader): + raise InvalidHeader(e, request=request) + else: + raise + + return self.build_response(request, resp) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/api.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/api.py new file mode 100644 index 0000000..eeb3b54 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/api.py @@ -0,0 +1,180 @@ +""" +requests.api +~~~~~~~~~~~~ + +This module implements the Requests API. + +:copyright: (c) 2012 by Kenneth Reitz. +:license: Apache2, see LICENSE for more details. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from . import sessions +from .models import Response + +if TYPE_CHECKING: + from typing_extensions import Unpack + + from . import _types as _t + + +def request( + method: str, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs] +) -> Response: + """Constructs and sends a :class:`Request `. + + :param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``. + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary, list of tuples or bytes to send + in the query string for the :class:`Request`. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. + :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. + :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. + ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` + or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content_type'`` is a string + defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers + to add for the file. + :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. + :param timeout: (optional) How many seconds to wait for the server to send data + before giving up, as a float, or a :ref:`(connect timeout, read + timeout) ` tuple. + :type timeout: float or tuple + :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``. + :type allow_redirects: bool + :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use. Defaults to ``True``. + :param stream: (optional) if ``False``, the response content will be immediately downloaded. + :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. + :return: :class:`Response ` object + :rtype: requests.Response + + Usage:: + + >>> import requests + >>> req = requests.request('GET', 'https://httpbin.org/get') + >>> req + + """ + + # By using the 'with' statement we are sure the session is closed, thus we + # avoid leaving sockets open which can trigger a ResourceWarning in some + # cases, and look like a memory leak in others. + with sessions.Session() as session: + return session.request(method=method, url=url, **kwargs) + + +def get( + url: _t.UriType, params: _t.ParamsType = None, **kwargs: Unpack[_t.GetKwargs] +) -> Response: + r"""Sends a GET request. + + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary, list of tuples or bytes to send + in the query string for the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request("get", url, params=params, **kwargs) + + +def options(url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response: + r"""Sends an OPTIONS request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request("options", url, **kwargs) + + +def head(url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response: + r"""Sends a HEAD request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. If + `allow_redirects` is not provided, it will be set to `False` (as + opposed to the default :meth:`request` behavior). + :return: :class:`Response ` object + :rtype: requests.Response + """ + + kwargs.setdefault("allow_redirects", False) + return request("head", url, **kwargs) + + +def post( + url: _t.UriType, + data: _t.DataType = None, + json: _t.JsonType = None, + **kwargs: Unpack[_t.PostKwargs], +) -> Response: + r"""Sends a POST request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request("post", url, data=data, json=json, **kwargs) + + +def put( + url: _t.UriType, data: _t.DataType = None, **kwargs: Unpack[_t.DataKwargs] +) -> Response: + r"""Sends a PUT request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request("put", url, data=data, **kwargs) + + +def patch( + url: _t.UriType, data: _t.DataType = None, **kwargs: Unpack[_t.DataKwargs] +) -> Response: + r"""Sends a PATCH request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request("patch", url, data=data, **kwargs) + + +def delete(url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response: + r"""Sends a DELETE request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request("delete", url, **kwargs) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/auth.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/auth.py new file mode 100644 index 0000000..2af481d --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/auth.py @@ -0,0 +1,354 @@ +""" +requests.auth +~~~~~~~~~~~~~ + +This module contains the authentication handlers for Requests. +""" + +from __future__ import annotations + +import hashlib +import os +import re +import threading +import time +import warnings +from base64 import b64encode +from typing import TYPE_CHECKING, Any, Final, cast, overload + +from ._internal_utils import to_native_string +from .compat import basestring, str, urlparse +from .cookies import extract_cookies_to_jar +from .utils import parse_dict_header + +if TYPE_CHECKING: + from http.cookiejar import CookieJar + from typing import Any + + from .models import PreparedRequest, Response + +CONTENT_TYPE_FORM_URLENCODED: Final = "application/x-www-form-urlencoded" +CONTENT_TYPE_MULTI_PART: Final = "multipart/form-data" + + +def _basic_auth_str(username: bytes | str, password: bytes | str) -> str: + """Returns a Basic Auth string.""" + + # "I want us to put a big-ol' comment on top of it that + # says that this behaviour is dumb but we need to preserve + # it because people are relying on it." + # - Lukasa + # + # These are here solely to maintain backwards compatibility + # for things like ints. This will be removed in 3.0.0. + if not isinstance(username, basestring): # type: ignore[reportUnnecessaryIsInstance] # runtime guard for non-str/bytes + warnings.warn( + "Non-string usernames will no longer be supported in Requests " + f"3.0.0. Please convert the object you've passed in ({username!r}) to " + "a string or bytes object in the near future to avoid " + "problems.", + category=DeprecationWarning, + ) + username = str(username) + + if not isinstance(password, basestring): # type: ignore[reportUnnecessaryIsInstance] # runtime guard for non-str/bytes + warnings.warn( + "Non-string passwords will no longer be supported in Requests " + f"3.0.0. Please convert the object you've passed in ({type(password)!r}) to " + "a string or bytes object in the near future to avoid " + "problems.", + category=DeprecationWarning, + ) + password = str(password) + # -- End Removal -- + + if isinstance(username, str): + username = username.encode("latin1") + + if isinstance(password, str): + password = password.encode("latin1") + + authstr = "Basic " + to_native_string( + b64encode(b":".join((username, password))).strip() + ) + + return authstr + + +class AuthBase: + """Base class that all auth implementations derive from""" + + def __call__(self, r: PreparedRequest) -> PreparedRequest: + raise NotImplementedError("Auth hooks must be callable.") + + +class HTTPBasicAuth(AuthBase): + """Attaches HTTP Basic Authentication to the given Request object.""" + + username: bytes | str + password: bytes | str + + @overload + def __init__(self, username: str, password: str) -> None: ... + @overload + def __init__(self, username: bytes, password: bytes) -> None: ... + + def __init__(self, username: bytes | str, password: bytes | str) -> None: + self.username = username + self.password = password + + def __eq__(self, other: object) -> bool: + return all( + [ + self.username == getattr(other, "username", None), + self.password == getattr(other, "password", None), + ] + ) + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __call__(self, r: PreparedRequest) -> PreparedRequest: + r.headers["Authorization"] = _basic_auth_str(self.username, self.password) + return r + + +class HTTPProxyAuth(HTTPBasicAuth): + """Attaches HTTP Proxy Authentication to a given Request object.""" + + def __call__(self, r: PreparedRequest) -> PreparedRequest: + r.headers["Proxy-Authorization"] = _basic_auth_str(self.username, self.password) + return r + + +class HTTPDigestAuth(AuthBase): + """Attaches HTTP Digest Authentication to the given Request object.""" + + username: bytes | str + password: bytes | str + _thread_local: threading.local + last_nonce: str + nonce_count: int + chal: dict[str, str] + pos: int | None + num_401_calls: int | None + + @overload + def __init__(self, username: str, password: str) -> None: ... + @overload + def __init__(self, username: bytes, password: bytes) -> None: ... + + def __init__(self, username: bytes | str, password: bytes | str) -> None: + self.username = username + self.password = password + # Keep state in per-thread local storage + self._thread_local = threading.local() + + def init_per_thread_state(self) -> None: + # Ensure state is initialized just once per-thread + if not hasattr(self._thread_local, "init"): + self._thread_local.init = True + self._thread_local.last_nonce = "" + self._thread_local.nonce_count = 0 + self._thread_local.chal = {} + self._thread_local.pos = None + self._thread_local.num_401_calls = None + + def build_digest_header(self, method: str, url: str) -> str | None: + """ + :rtype: str + """ + + realm = self._thread_local.chal["realm"] + nonce = self._thread_local.chal["nonce"] + qop = self._thread_local.chal.get("qop") + algorithm = self._thread_local.chal.get("algorithm") + opaque = self._thread_local.chal.get("opaque") + hash_utf8 = None + + if algorithm is None: + _algorithm = "MD5" + else: + _algorithm = algorithm.upper() + # lambdas assume digest modules are imported at the top level + if _algorithm == "MD5" or _algorithm == "MD5-SESS": + + def md5_utf8(x: str | bytes) -> str: + if isinstance(x, str): + x = x.encode("utf-8") + return hashlib.md5(x, usedforsecurity=False).hexdigest() + + hash_utf8 = md5_utf8 + elif _algorithm == "SHA": + + def sha_utf8(x: str | bytes) -> str: + if isinstance(x, str): + x = x.encode("utf-8") + return hashlib.sha1(x, usedforsecurity=False).hexdigest() + + hash_utf8 = sha_utf8 + elif _algorithm == "SHA-256": + + def sha256_utf8(x: str | bytes) -> str: + if isinstance(x, str): + x = x.encode("utf-8") + return hashlib.sha256(x, usedforsecurity=False).hexdigest() + + hash_utf8 = sha256_utf8 + elif _algorithm == "SHA-512": + + def sha512_utf8(x: str | bytes) -> str: + if isinstance(x, str): + x = x.encode("utf-8") + return hashlib.sha512(x, usedforsecurity=False).hexdigest() + + hash_utf8 = sha512_utf8 + + if hash_utf8 is None: + return None + + def KD(s: str, d: str) -> str: + return hash_utf8(f"{s}:{d}") + + # XXX not implemented yet + entdig = None + p_parsed = urlparse(url) + #: path is request-uri defined in RFC 2616 which should not be empty + path = p_parsed.path or "/" + if p_parsed.query: + path += f"?{p_parsed.query}" + + A1 = f"{self.username}:{realm}:{self.password}" + A2 = f"{method}:{path}" + + HA1 = hash_utf8(A1) + HA2 = hash_utf8(A2) + + if nonce == self._thread_local.last_nonce: + self._thread_local.nonce_count += 1 + else: + self._thread_local.nonce_count = 1 + ncvalue = f"{self._thread_local.nonce_count:08x}" + s = str(self._thread_local.nonce_count).encode("utf-8") + s += nonce.encode("utf-8") + s += time.ctime().encode("utf-8") + s += os.urandom(8) + + cnonce = hashlib.sha1(s, usedforsecurity=False).hexdigest()[:16] + if _algorithm == "MD5-SESS": + HA1 = hash_utf8(f"{HA1}:{nonce}:{cnonce}") # type: ignore[reportConstantRedefinition] # RFC 2617 terminology + + if not qop: + respdig = KD(HA1, f"{nonce}:{HA2}") + elif qop == "auth" or "auth" in qop.split(","): + noncebit = f"{nonce}:{ncvalue}:{cnonce}:auth:{HA2}" + respdig = KD(HA1, noncebit) + else: + # XXX handle auth-int. + return None + + self._thread_local.last_nonce = nonce + + # XXX should the partial digests be encoded too? + base = ( + f'username="{self.username}", realm="{realm}", nonce="{nonce}", ' + f'uri="{path}", response="{respdig}"' + ) + if opaque: + base += f', opaque="{opaque}"' + if algorithm: + base += f', algorithm="{algorithm}"' + if entdig: + base += f', digest="{entdig}"' + if qop: + base += f', qop="auth", nc={ncvalue}, cnonce="{cnonce}"' + + return f"Digest {base}" + + def handle_redirect(self, r: Response, **kwargs: Any) -> None: + """Reset num_401_calls counter on redirects.""" + if r.is_redirect: + self._thread_local.num_401_calls = 1 + + def handle_401(self, r: Response, **kwargs: Any) -> Response: + """ + Takes the given response and tries digest-auth, if needed. + + :rtype: requests.Response + """ + + # If response is not 4xx, do not auth + # See https://github.com/psf/requests/issues/3772 + if not 400 <= r.status_code < 500: + self._thread_local.num_401_calls = 1 + return r + + if self._thread_local.pos is not None: + # Rewind the file position indicator of the body to where + # it was to resend the request. + if (seek := getattr(r.request.body, "seek", None)) is not None: + seek(self._thread_local.pos) + s_auth = r.headers.get("www-authenticate", "") + + if "digest" in s_auth.lower() and self._thread_local.num_401_calls < 2: + self._thread_local.num_401_calls += 1 + pat = re.compile(r"digest ", flags=re.IGNORECASE) + self._thread_local.chal = parse_dict_header(pat.sub("", s_auth, count=1)) + + # Consume content and release the original connection + # to allow our new request to reuse the same one. + r.content + r.close() + prep = r.request.copy() + cookie_jar = cast("CookieJar", prep._cookies) # type: ignore[reportPrivateUsage] + extract_cookies_to_jar(cookie_jar, r.request, r.raw) + prep.prepare_cookies(cookie_jar) + + _digest_auth = self.build_digest_header( + cast(str, prep.method), cast(str, prep.url) + ) + if _digest_auth: + prep.headers["Authorization"] = _digest_auth + _r = r.connection.send(prep, **kwargs) + _r.history.append(r) + _r.request = prep + + return _r + + self._thread_local.num_401_calls = 1 + return r + + def __call__(self, r: PreparedRequest) -> PreparedRequest: + # Initialize per-thread state, if needed + self.init_per_thread_state() + # If we have a saved nonce, skip the 401 + if self._thread_local.last_nonce: + _digest_auth = self.build_digest_header( + cast(str, r.method), cast(str, r.url) + ) + if _digest_auth: + r.headers["Authorization"] = _digest_auth + if (tell := getattr(r.body, "tell", None)) is not None: + self._thread_local.pos = tell() + else: + # In the case of HTTPDigestAuth being reused and the body of + # the previous request was a file-like object, pos has the + # file position of the previous body. Ensure it's set to + # None. + self._thread_local.pos = None + r.register_hook("response", self.handle_401) + r.register_hook("response", self.handle_redirect) + self._thread_local.num_401_calls = 1 + + return r + + def __eq__(self, other: object) -> bool: + return all( + [ + self.username == getattr(other, "username", None), + self.password == getattr(other, "password", None), + ] + ) + + def __ne__(self, other: Any) -> bool: + return not self == other diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/certs.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/certs.py new file mode 100644 index 0000000..c595348 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/certs.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +requests.certs +~~~~~~~~~~~~~~ + +This module returns the preferred default CA certificate bundle. There is +only one — the one from the certifi package. + +If you are packaging Requests, e.g., for a Linux distribution or a managed +environment, you can change the definition of where() to return a separately +packaged CA bundle. +""" + +from pip._vendor.certifi import where + +if __name__ == "__main__": + print(where()) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/compat.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/compat.py new file mode 100644 index 0000000..0cbeae6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/compat.py @@ -0,0 +1,96 @@ +""" +requests.compat +~~~~~~~~~~~~~~~ + +This module previously handled import compatibility issues +between Python 2 and Python 3. It remains for backwards +compatibility until the next major version. +""" + +# pyright: reportUnusedImport=false + +from __future__ import annotations + +import sys + +# ------- +# urllib3 +# ------- +from pip._vendor.urllib3 import ( + __version__ as urllib3_version, # type: ignore[reportPrivateImportUsage] +) + +# Detect which major version of urllib3 is being used. +try: + is_urllib3_1 = int(urllib3_version.split(".")[0]) == 1 +except (TypeError, AttributeError): + # If we can't discern a version, prefer old functionality. + is_urllib3_1 = True + +# ------------------- +# Character Detection +# ------------------- + + +def _resolve_char_detection() -> None: + """Find supported character detection libraries.""" + chardet = None + return chardet + + +chardet = _resolve_char_detection() + +# ------- +# Pythons +# ------- + +# Syntax sugar. +_ver = sys.version_info + +#: Python 2.x? +is_py2 = _ver[0] == 2 + +#: Python 3.x? +is_py3 = _ver[0] == 3 + +# Note: We've patched out simplejson support in pip because it prevents +# upgrading simplejson on Windows. +import json +from json import JSONDecodeError + +# Keep OrderedDict for backwards compatibility. +from collections import OrderedDict +from collections.abc import Callable, Mapping, MutableMapping +from http import cookiejar as cookielib +from http.cookies import Morsel +from io import StringIO + +# -------------- +# Legacy Imports +# -------------- +from urllib.parse import ( + quote, + quote_plus, + unquote, + unquote_plus, + urldefrag, + urlencode, + urljoin, + urlparse, + urlsplit, + urlunparse, +) +from urllib.request import ( + getproxies, + getproxies_environment, + parse_http_list, + proxy_bypass, + proxy_bypass_environment, # type: ignore[attr-defined] # https://github.com/python/cpython/issues/145331 +) + +builtin_str = str +str = str +bytes = bytes +basestring = (str, bytes) +numeric_types = (int, float) +integer_types = (int,) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/cookies.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/cookies.py new file mode 100644 index 0000000..2e3fc21 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/cookies.py @@ -0,0 +1,625 @@ +""" +requests.cookies +~~~~~~~~~~~~~~~~ + +Compatibility code to be able to use `http.cookiejar.CookieJar` with requests. + +requests.utils imports from here, so be careful with imports. +""" + +from __future__ import annotations + +import calendar +import copy +import time +from collections.abc import Iterator, MutableMapping +from http.cookiejar import Cookie, CookieJar, CookiePolicy +from typing import TYPE_CHECKING, Any, TypeVar, overload + +from ._internal_utils import to_native_string +from ._types import is_prepared as _is_prepared +from .compat import Morsel, cookielib, urlparse, urlunparse + +if TYPE_CHECKING: + from _typeshed import SupportsKeysAndGetItem + + from .models import PreparedRequest + +import threading + + +class MockRequest: + """Wraps a `requests.PreparedRequest` to mimic a `urllib2.Request`. + + The code in `http.cookiejar.CookieJar` expects this interface in order to correctly + manage cookie policies, i.e., determine whether a cookie can be set, given the + domains of the request and the cookie. + + The original request object is read-only. The client is responsible for collecting + the new headers via `get_new_headers()` and interpreting them appropriately. You + probably want `get_cookie_header`, defined below. + """ + + type: str + + def __init__(self, request: PreparedRequest) -> None: + assert _is_prepared(request) + self._r = request + self._new_headers: dict[str, str] = {} + self.type = urlparse(self._r.url).scheme + + def get_type(self) -> str: + return self.type + + def get_host(self) -> str: + return urlparse(self._r.url).netloc + + def get_origin_req_host(self) -> str: + return self.get_host() + + def get_full_url(self) -> str: + # Only return the response's URL if the user hadn't set the Host + # header + if not self._r.headers.get("Host"): + return self._r.url + # If they did set it, retrieve it and reconstruct the expected domain + host = to_native_string(self._r.headers["Host"], encoding="utf-8") + parsed = urlparse(self._r.url) + # Reconstruct the URL as we expect it + return urlunparse( + [ + parsed.scheme, + host, + parsed.path, + parsed.params, + parsed.query, + parsed.fragment, + ] + ) + + def is_unverifiable(self) -> bool: + return True + + def has_header(self, name: str) -> bool: + return name in self._r.headers or name in self._new_headers + + def get_header(self, name: str, default: str | None = None) -> str | None: + return self._r.headers.get(name, self._new_headers.get(name, default)) # type: ignore[return-value] + + def add_header(self, key: str, val: str) -> None: + """cookiejar has no legitimate use for this method; add it back if you find one.""" + raise NotImplementedError( + "Cookie headers should be added with add_unredirected_header()" + ) + + def add_unredirected_header(self, name: str, value: str) -> None: + self._new_headers[name] = value + + def get_new_headers(self) -> dict[str, str]: + return self._new_headers + + @property + def unverifiable(self) -> bool: + return self.is_unverifiable() + + @property + def origin_req_host(self) -> str: + return self.get_origin_req_host() + + @property + def host(self) -> str: + return self.get_host() + + +class MockResponse: + """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`. + + ...what? Basically, expose the parsed HTTP headers from the server response + the way `http.cookiejar` expects to see them. + """ + + def __init__(self, headers: Any) -> None: + """Make a MockResponse for `cookiejar` to read. + + :param headers: a httplib.HTTPMessage or analogous carrying the headers + """ + self._headers = headers + + def info(self) -> Any: + return self._headers + + def getheaders(self, name: str) -> Any: + self._headers.getheaders(name) + + +def extract_cookies_to_jar( + jar: CookieJar, request: PreparedRequest, response: Any +) -> None: + """Extract the cookies from the response into a CookieJar. + + :param jar: http.cookiejar.CookieJar (not necessarily a RequestsCookieJar) + :param request: our own requests.Request object + :param response: urllib3.HTTPResponse object + """ + if not (hasattr(response, "_original_response") and response._original_response): + return + # the _original_response field is the wrapped httplib.HTTPResponse object, + req = MockRequest(request) + # pull out the HTTPMessage with the headers and put it in the mock: + res = MockResponse(response._original_response.msg) + jar.extract_cookies(res, req) # type: ignore[arg-type] + + +def get_cookie_header(jar: CookieJar, request: PreparedRequest) -> str | None: + """ + Produce an appropriate Cookie header string to be sent with `request`, or None. + + :rtype: str + """ + r = MockRequest(request) + jar.add_cookie_header(r) # type: ignore[arg-type] + return r.get_new_headers().get("Cookie") + + +def remove_cookie_by_name( + cookiejar: CookieJar, name: str, domain: str | None = None, path: str | None = None +) -> None: + """Unsets a cookie by name, by default over all domains and paths. + + Wraps CookieJar.clear(), is O(n). + """ + clearables: list[tuple[str, str, str]] = [] + for cookie in cookiejar: + if cookie.name != name: + continue + if domain is not None and domain != cookie.domain: + continue + if path is not None and path != cookie.path: + continue + clearables.append((cookie.domain, cookie.path, cookie.name)) + + for domain, path, name in clearables: + cookiejar.clear(domain, path, name) + + +class CookieConflictError(RuntimeError): + """There are two cookies that meet the criteria specified in the cookie jar. + Use .get and .set and include domain and path args in order to be more specific. + """ + + +class RequestsCookieJar(CookieJar, MutableMapping[str, str | None]): # type: ignore[misc] + """Compatibility class; is a http.cookiejar.CookieJar, but exposes a dict + interface. + + This is the CookieJar we create by default for requests and sessions that + don't specify one, since some clients may expect response.cookies and + session.cookies to support dict operations. + + Requests does not use the dict interface internally; it's just for + compatibility with external client code. All requests code should work + out of the box with externally provided instances of ``CookieJar``, e.g. + ``LWPCookieJar`` and ``FileCookieJar``. + + Unlike a regular CookieJar, this class is pickleable. + + .. warning:: dictionary operations that are normally O(1) may be O(n). + """ + + _policy: CookiePolicy + + def get( # type: ignore[override] + self, + name: str, + default: str | None = None, + domain: str | None = None, + path: str | None = None, + ) -> str | None: + """Dict-like get() that also supports optional domain and path args in + order to resolve naming collisions from using one cookie jar over + multiple domains. + + .. warning:: operation is O(n), not O(1). + """ + try: + return self._find_no_duplicates(name, domain, path) + except KeyError: + return default + + def set( + self, name: str, value: str | Morsel[dict[str, str]] | None, **kwargs: Any + ) -> Cookie | None: + """Dict-like set() that also supports optional domain and path args in + order to resolve naming collisions from using one cookie jar over + multiple domains. + """ + # support client code that unsets cookies by assignment of a None value: + if value is None: + remove_cookie_by_name( + self, name, domain=kwargs.get("domain"), path=kwargs.get("path") + ) + return + + if isinstance(value, Morsel): + c = morsel_to_cookie(value) + else: + c = create_cookie(name, value, **kwargs) + self.set_cookie(c) + return c + + def iterkeys(self) -> Iterator[str]: + """Dict-like iterkeys() that returns an iterator of names of cookies + from the jar. + + .. seealso:: itervalues() and iteritems(). + """ + for cookie in iter(self): + yield cookie.name + + def keys(self) -> list[str]: # type: ignore[override] + """Dict-like keys() that returns a list of names of cookies from the + jar. + + .. seealso:: values() and items(). + """ + return list(self.iterkeys()) + + def itervalues(self) -> Iterator[str | None]: + """Dict-like itervalues() that returns an iterator of values of cookies + from the jar. + + .. seealso:: iterkeys() and iteritems(). + """ + for cookie in iter(self): + yield cookie.value + + def values(self) -> list[str | None]: # type: ignore[override] + """Dict-like values() that returns a list of values of cookies from the + jar. + + .. seealso:: keys() and items(). + """ + return list(self.itervalues()) + + def iteritems(self) -> Iterator[tuple[str, str | None]]: + """Dict-like iteritems() that returns an iterator of name-value tuples + from the jar. + + .. seealso:: iterkeys() and itervalues(). + """ + for cookie in iter(self): + yield cookie.name, cookie.value + + def items(self) -> list[tuple[str, str | None]]: # type: ignore[override] + """Dict-like items() that returns a list of name-value tuples from the + jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a + vanilla python dict of key value pairs. + + .. seealso:: keys() and values(). + """ + return list(self.iteritems()) + + def list_domains(self) -> list[str]: + """Utility method to list all the domains in the jar.""" + domains: list[str] = [] + for cookie in iter(self): + if cookie.domain not in domains: + domains.append(cookie.domain) + return domains + + def list_paths(self) -> list[str]: + """Utility method to list all the paths in the jar.""" + paths: list[str] = [] + for cookie in iter(self): + if cookie.path not in paths: + paths.append(cookie.path) + return paths + + def multiple_domains(self) -> bool: + """Returns True if there are multiple domains in the jar. + Returns False otherwise. + + :rtype: bool + """ + domains: list[str] = [] + for cookie in iter(self): + if cookie.domain is not None and cookie.domain in domains: # type: ignore[reportUnnecessaryComparison] # defensive check + return True + domains.append(cookie.domain) + return False # there is only one domain in jar + + def get_dict( + self, domain: str | None = None, path: str | None = None + ) -> dict[str, str | None]: + """Takes as an argument an optional domain and path and returns a plain + old Python dict of name-value pairs of cookies that meet the + requirements. + + :rtype: dict + """ + dictionary: dict[str, str | None] = {} + for cookie in iter(self): + if (domain is None or cookie.domain == domain) and ( + path is None or cookie.path == path + ): + dictionary[cookie.name] = cookie.value + return dictionary + + def __iter__(self) -> Iterator[Cookie]: # type: ignore[override] + """RequestCookieJar's __iter__ comes from CookieJar not MutableMapping.""" + return super().__iter__() + + def __contains__(self, name: object) -> bool: + try: + return super().__contains__(name) + except CookieConflictError: + return True + + def __getitem__(self, name: str) -> str | None: + """Dict-like __getitem__() for compatibility with client code. Throws + exception if there are more than one cookie with name. In that case, + use the more explicit get() method instead. + + .. warning:: operation is O(n), not O(1). + """ + return self._find_no_duplicates(name) + + def __setitem__( + self, name: str, value: str | Morsel[dict[str, str]] | None + ) -> None: + """Dict-like __setitem__ for compatibility with client code. Throws + exception if there is already a cookie of that name in the jar. In that + case, use the more explicit set() method instead. + """ + self.set(name, value) + + def __delitem__(self, name: str) -> None: + """Deletes a cookie given a name. Wraps ``http.cookiejar.CookieJar``'s + ``remove_cookie_by_name()``. + """ + remove_cookie_by_name(self, name) + + def set_cookie(self, cookie: Cookie, *args: Any, **kwargs: Any) -> None: + if ( + (value := cookie.value) is not None + and value.startswith('"') + and value.endswith('"') + ): + cookie.value = value.replace('\\"', "") + return super().set_cookie(cookie, *args, **kwargs) + + def update( # type: ignore[override] + self, other: CookieJar | SupportsKeysAndGetItem[str, str] + ) -> None: + """Updates this jar with cookies from another CookieJar or dict-like""" + if isinstance(other, cookielib.CookieJar): + for cookie in other: + self.set_cookie(copy.copy(cookie)) + else: + super().update(other) + + def _find( + self, name: str, domain: str | None = None, path: str | None = None + ) -> str | None: + """Requests uses this method internally to get cookie values. + + If there are conflicting cookies, _find arbitrarily chooses one. + See _find_no_duplicates if you want an exception thrown if there are + conflicting cookies. + + :param name: a string containing name of cookie + :param domain: (optional) string containing domain of cookie + :param path: (optional) string containing path of cookie + :return: cookie.value + """ + for cookie in iter(self): + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + return cookie.value + + raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}") + + def _find_no_duplicates( + self, name: str, domain: str | None = None, path: str | None = None + ) -> str: + """Both ``__get_item__`` and ``get`` call this function: it's never + used elsewhere in Requests. + + :param name: a string containing name of cookie + :param domain: (optional) string containing domain of cookie + :param path: (optional) string containing path of cookie + :raises KeyError: if cookie is not found + :raises CookieConflictError: if there are multiple cookies + that match name and optionally domain and path + :return: cookie.value + """ + toReturn = None + for cookie in iter(self): + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + if toReturn is not None: + # if there are multiple cookies that meet passed in criteria + raise CookieConflictError( + f"There are multiple cookies with name, {name!r}" + ) + # we will eventually return this as long as no cookie conflict + toReturn = cookie.value + + if toReturn is not None: + return toReturn + raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}") + + def __getstate__(self) -> dict[str, Any]: + """Unlike a normal CookieJar, this class is pickleable.""" + state = self.__dict__.copy() + # remove the unpickleable RLock object + state.pop("_cookies_lock") + return state + + def __setstate__(self, state: dict[str, Any]) -> None: + """Unlike a normal CookieJar, this class is pickleable.""" + self.__dict__.update(state) + if "_cookies_lock" not in self.__dict__: + self._cookies_lock = threading.RLock() + + def copy(self) -> RequestsCookieJar: + """Return a copy of this RequestsCookieJar.""" + new_cj = RequestsCookieJar() + new_cj.set_policy(self.get_policy()) + new_cj.update(self) + return new_cj + + def get_policy(self) -> CookiePolicy: + """Return the CookiePolicy instance used.""" + return self._policy + + +def _copy_cookie_jar(jar: CookieJar | None) -> CookieJar | None: # type: ignore[reportUnusedFunction] # cross-module usage in models.py + if jar is None: + return None + + if copy_method := getattr(jar, "copy", None): + # We're dealing with an instance of RequestsCookieJar + return copy_method() + # We're dealing with a generic CookieJar instance + new_jar = copy.copy(jar) + new_jar.clear() + for cookie in jar: + new_jar.set_cookie(copy.copy(cookie)) + return new_jar + + +def create_cookie(name: str, value: str, **kwargs: Any) -> Cookie: + """Make a cookie from underspecified parameters. + + By default, the pair of `name` and `value` will be set for the domain '' + and sent on every request (this is sometimes called a "supercookie"). + """ + result: dict[str, Any] = { + "version": 0, + "name": name, + "value": value, + "port": None, + "domain": "", + "path": "/", + "secure": False, + "expires": None, + "discard": True, + "comment": None, + "comment_url": None, + "rest": {"HttpOnly": None}, + "rfc2109": False, + } + + badargs = set(kwargs) - set(result) + if badargs: + raise TypeError( + f"create_cookie() got unexpected keyword arguments: {list(badargs)}" + ) + + result.update(kwargs) + result["port_specified"] = bool(result["port"]) + result["domain_specified"] = bool(result["domain"]) + result["domain_initial_dot"] = result["domain"].startswith(".") + result["path_specified"] = bool(result["path"]) + + return cookielib.Cookie(**result) + + +def morsel_to_cookie(morsel: Morsel[Any]) -> Cookie: + """Convert a Morsel object into a Cookie containing the one k/v pair.""" + + expires: int | None = None + if morsel["max-age"]: + try: + expires = int(time.time() + int(morsel["max-age"])) + except ValueError: + raise TypeError(f"max-age: {morsel['max-age']} must be integer") + elif morsel["expires"]: + time_template = "%a, %d-%b-%Y %H:%M:%S GMT" + expires = calendar.timegm(time.strptime(morsel["expires"], time_template)) + return create_cookie( + comment=morsel["comment"], + comment_url=bool(morsel["comment"]), + discard=False, + domain=morsel["domain"], + expires=expires, + name=morsel.key, + path=morsel["path"], + port=None, + rest={"HttpOnly": morsel["httponly"]}, + rfc2109=False, + secure=bool(morsel["secure"]), + value=morsel.value, + version=morsel["version"] or 0, + ) + + +_CookieJarT = TypeVar("_CookieJarT", bound=CookieJar) + + +@overload +def cookiejar_from_dict( + cookie_dict: dict[str, str] | None, + cookiejar: None = None, + overwrite: bool = True, +) -> RequestsCookieJar: ... + + +@overload +def cookiejar_from_dict( + cookie_dict: dict[str, str] | None, + cookiejar: _CookieJarT, + overwrite: bool = True, +) -> _CookieJarT: ... + + +def cookiejar_from_dict( + cookie_dict: dict[str, str] | None, + cookiejar: CookieJar | None = None, + overwrite: bool = True, +) -> CookieJar: + """Returns a CookieJar from a key/value dictionary. + + :param cookie_dict: Dict of key/values to insert into CookieJar. + :param cookiejar: (optional) A cookiejar to add the cookies to. + :param overwrite: (optional) If False, will not replace cookies + already in the jar with new ones. + :rtype: CookieJar + """ + if cookiejar is None: + cookiejar = RequestsCookieJar() + + if cookie_dict is not None: + names_from_jar = [cookie.name for cookie in cookiejar] + for name in cookie_dict: + if overwrite or (name not in names_from_jar): + cookiejar.set_cookie(create_cookie(name, cookie_dict[name])) + + return cookiejar + + +def merge_cookies( + cookiejar: CookieJar, cookies: dict[str, str] | CookieJar | None +) -> CookieJar: + """Add cookies to cookiejar and returns a merged CookieJar. + + :param cookiejar: CookieJar object to add the cookies to. + :param cookies: Dictionary or CookieJar object to be added. + :rtype: CookieJar + """ + if not isinstance(cookiejar, cookielib.CookieJar): # type: ignore[reportUnnecessaryIsInstance] # runtime guard + raise ValueError("You can only merge into CookieJar") + + if isinstance(cookies, dict): + cookiejar = cookiejar_from_dict(cookies, cookiejar=cookiejar, overwrite=False) + elif isinstance(cookies, cookielib.CookieJar): + if update_method := getattr(cookiejar, "update", None): + update_method(cookies) + else: + for cookie_in_jar in cookies: + cookiejar.set_cookie(cookie_in_jar) + + return cookiejar diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/exceptions.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/exceptions.py new file mode 100644 index 0000000..3de8f0d --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/exceptions.py @@ -0,0 +1,162 @@ +""" +requests.exceptions +~~~~~~~~~~~~~~~~~~~ + +This module contains the set of Requests' exceptions. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from pip._vendor.urllib3.exceptions import HTTPError as BaseHTTPError + +from .compat import JSONDecodeError as CompatJSONDecodeError + +if TYPE_CHECKING: + from .models import PreparedRequest, Request, Response + + +class RequestException(IOError): + """There was an ambiguous exception that occurred while handling your + request. + """ + + response: Response | None + request: Request | PreparedRequest | None + + def __init__(self, *args: Any, **kwargs: Any) -> None: + """Initialize RequestException with `request` and `response` objects.""" + response: Response | None = kwargs.pop("response", None) + self.response = response + self.request = kwargs.pop("request", None) + if response is not None and not self.request and hasattr(response, "request"): + self.request = response.request + super().__init__(*args, **kwargs) + + +class InvalidJSONError(RequestException): + """A JSON error occurred.""" + + +class JSONDecodeError(InvalidJSONError, CompatJSONDecodeError): + """Couldn't decode the text into json""" + + def __init__(self, *args: Any, **kwargs: Any) -> None: + """ + Construct the JSONDecodeError instance first with all + args. Then use it's args to construct the IOError so that + the json specific args aren't used as IOError specific args + and the error message from JSONDecodeError is preserved. + """ + CompatJSONDecodeError.__init__(self, *args) + InvalidJSONError.__init__(self, *self.args, **kwargs) + + def __reduce__(self) -> tuple[Any, ...] | str: + """ + The __reduce__ method called when pickling the object must + be the one from the JSONDecodeError (be it json/simplejson) + as it expects all the arguments for instantiation, not just + one like the IOError, and the MRO would by default call the + __reduce__ method from the IOError due to the inheritance order. + """ + return CompatJSONDecodeError.__reduce__(self) + + +class HTTPError(RequestException): + """An HTTP error occurred.""" + + +class ConnectionError(RequestException): + """A Connection error occurred.""" + + +class ProxyError(ConnectionError): + """A proxy error occurred.""" + + +class SSLError(ConnectionError): + """An SSL error occurred.""" + + +class Timeout(RequestException): + """The request timed out. + + Catching this error will catch both + :exc:`~requests.exceptions.ConnectTimeout` and + :exc:`~requests.exceptions.ReadTimeout` errors. + """ + + +class ConnectTimeout(ConnectionError, Timeout): + """The request timed out while trying to connect to the remote server. + + Requests that produced this error are safe to retry. + """ + + +class ReadTimeout(Timeout): + """The server did not send any data in the allotted amount of time.""" + + +class URLRequired(RequestException): + """A valid URL is required to make a request.""" + + +class TooManyRedirects(RequestException): + """Too many redirects.""" + + +class MissingSchema(RequestException, ValueError): + """The URL scheme (e.g. http or https) is missing.""" + + +class InvalidSchema(RequestException, ValueError): + """The URL scheme provided is either invalid or unsupported.""" + + +class InvalidURL(RequestException, ValueError): + """The URL provided was somehow invalid.""" + + +class InvalidHeader(RequestException, ValueError): + """The header value provided was somehow invalid.""" + + +class InvalidProxyURL(InvalidURL): + """The proxy URL provided is invalid.""" + + +class ChunkedEncodingError(RequestException): + """The server declared chunked encoding but sent an invalid chunk.""" + + +class ContentDecodingError(RequestException, BaseHTTPError): + """Failed to decode response content.""" + + +class StreamConsumedError(RequestException, TypeError): + """The content for this response was already consumed.""" + + +class RetryError(RequestException): + """Custom retries logic failed""" + + +class UnrewindableBodyError(RequestException): + """Requests encountered an error when trying to rewind a body.""" + + +# Warnings + + +class RequestsWarning(Warning): + """Base warning for Requests.""" + + +class FileModeWarning(RequestsWarning, DeprecationWarning): + """A file was opened in text mode, but Requests determined its binary length.""" + + +class RequestsDependencyWarning(RequestsWarning): + """An imported dependency doesn't match the expected version range.""" diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/help.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/help.py new file mode 100644 index 0000000..33f2d57 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/help.py @@ -0,0 +1,127 @@ +"""Module containing bug report helper(s).""" + +# pyright: reportUnknownMemberType=false + +import json +import platform +import ssl +import sys +from typing import Any + +from pip._vendor import idna +from pip._vendor import urllib3 + +from . import __version__ as requests_version + +charset_normalizer = None +chardet = None + +try: + from pip._vendor.urllib3.contrib import pyopenssl +except ImportError: + pyopenssl = None + OpenSSL = None + cryptography = None +else: + import cryptography # type: ignore[import-not-found] + import OpenSSL # type: ignore[import-not-found] + + +def _implementation(): + """Return a dict with the Python implementation and version. + + Provide both the name and the version of the Python implementation + currently running. For example, on CPython 3.10.3 it will return + {'name': 'CPython', 'version': '3.10.3'}. + + This function works best on CPython and PyPy: in particular, it probably + doesn't work for Jython or IronPython. Future investigation should be done + to work out the correct shape of the code for those platforms. + """ + implementation = platform.python_implementation() + + if implementation == "CPython": + implementation_version = platform.python_version() + elif implementation == "PyPy": + pypy = sys.pypy_version_info # type: ignore[attr-defined] + implementation_version = f"{pypy.major}.{pypy.minor}.{pypy.micro}" + if sys.pypy_version_info.releaselevel != "final": # type: ignore[attr-defined] + implementation_version = "".join( + [implementation_version, sys.pypy_version_info.releaselevel] # type: ignore[attr-defined] + ) + elif implementation == "Jython": + implementation_version = platform.python_version() # Complete Guess + elif implementation == "IronPython": + implementation_version = platform.python_version() # Complete Guess + else: + implementation_version = "Unknown" + + return {"name": implementation, "version": implementation_version} + + +def info() -> dict[str, Any]: + """Generate information for a bug report.""" + try: + platform_info = { + "system": platform.system(), + "release": platform.release(), + } + except OSError: + platform_info = { + "system": "Unknown", + "release": "Unknown", + } + + implementation_info = _implementation() + urllib3_info = {"version": urllib3.__version__} # type: ignore[reportPrivateImportUsage] + charset_normalizer_info = {"version": None} + chardet_info: dict[str, str | None] = {"version": None} + if charset_normalizer: + charset_normalizer_info = {"version": charset_normalizer.__version__} + if chardet: + chardet_info = {"version": chardet.__version__} + + pyopenssl_info: dict[str, str | None] = { + "version": None, + "openssl_version": "", + } + if OpenSSL: + pyopenssl_info = { + "version": OpenSSL.__version__, + "openssl_version": f"{OpenSSL.SSL.OPENSSL_VERSION_NUMBER:x}", + } + cryptography_info = { + "version": getattr(cryptography, "__version__", ""), + } + idna_info = { + "version": getattr(idna, "__version__", ""), + } + + system_ssl = ssl.OPENSSL_VERSION_NUMBER + system_ssl_info = {"version": f"{system_ssl:x}" if system_ssl is not None else ""} # type: ignore[reportUnnecessaryComparison] + + return { + "platform": platform_info, + "implementation": implementation_info, + "system_ssl": system_ssl_info, + "using_pyopenssl": pyopenssl is not None, + "using_charset_normalizer": chardet is None, + "pyOpenSSL": pyopenssl_info, + "urllib3": urllib3_info, + "chardet": chardet_info, + "charset_normalizer": charset_normalizer_info, + "cryptography": cryptography_info, + "idna": idna_info, + "requests": { + "version": requests_version, + }, + } + + +def main(): + """Pretty-print the bug information as JSON.""" + print(json.dumps(info(), sort_keys=True, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/hooks.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/hooks.py new file mode 100644 index 0000000..11ff9e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/hooks.py @@ -0,0 +1,48 @@ +""" +requests.hooks +~~~~~~~~~~~~~~ + +This module provides the capabilities for the Requests hooks system. + +Available hooks: + +``response``: + The response generated from a Request. +""" + +from __future__ import annotations + +from collections.abc import Callable, Iterable +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from . import _types as _t + from .models import Response + +HOOKS: list[str] = ["response"] + + +def default_hooks() -> dict[str, list[_t.HookType]]: + return {event: [] for event in HOOKS} + + +# TODO: response is the only one + + +def dispatch_hook( + key: str, + hooks: _t.HooksInputType | None, + hook_data: Response, + **kwargs: Any, +) -> Response: + """Dispatches a hook dictionary on a given piece of data.""" + hooks_dict = hooks or {} + hook_list: Iterable[_t.HookType] | _t.HookType | None = hooks_dict.get(key) + if hook_list: + if isinstance(hook_list, Callable): + hook_list = [hook_list] + for hook in hook_list: + _hook_data = hook(hook_data, **kwargs) + if _hook_data is not None: + hook_data = _hook_data + return hook_data diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/models.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/models.py new file mode 100644 index 0000000..3a70ae5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/models.py @@ -0,0 +1,1180 @@ +""" +requests.models +~~~~~~~~~~~~~~~ + +This module contains the primary objects that power Requests. +""" + +from __future__ import annotations + +import datetime + +# Import encoding now, to avoid implicit import later. +# Implicit import within threads may cause LookupError when standard library is in a ZIP, +# such as in Embedded Python. See https://github.com/psf/requests/issues/3578. +import encodings.idna # noqa: F401 # type: ignore[reportUnusedImport] +from collections.abc import Callable, Generator, Iterable, Iterator, Mapping +from io import UnsupportedOperation +from typing import ( + TYPE_CHECKING, + Any, + Final, + Literal, + cast, + overload, +) + +from pip._vendor.urllib3.exceptions import ( + DecodeError, + LocationParseError, + ProtocolError, + ReadTimeoutError, + SSLError, +) +from pip._vendor.urllib3.fields import RequestField +from pip._vendor.urllib3.filepost import encode_multipart_formdata +from pip._vendor.urllib3.util import parse_url + +from ._internal_utils import to_native_string, unicode_is_ascii +from ._types import SupportsRead as _SupportsRead +from .auth import HTTPBasicAuth +from .compat import ( + JSONDecodeError, + basestring, + builtin_str, + chardet, + cookielib, + urlencode, + urlsplit, + urlunparse, +) +from .compat import json as complexjson +from .cookies import ( + _copy_cookie_jar, # type: ignore[reportPrivateUsage] + cookiejar_from_dict, + get_cookie_header, +) +from .exceptions import ( + ChunkedEncodingError, + ConnectionError, + ContentDecodingError, + HTTPError, + InvalidJSONError, + InvalidURL, + MissingSchema, + StreamConsumedError, +) +from .exceptions import JSONDecodeError as RequestsJSONDecodeError +from .exceptions import SSLError as RequestsSSLError +from .hooks import default_hooks +from .status_codes import codes +from .structures import CaseInsensitiveDict +from .utils import ( + check_header_validity, + get_auth_from_url, + guess_filename, + guess_json_utf, + iter_slices, + parse_header_links, + requote_uri, + stream_decode_response_unicode, + super_len, + to_key_val_list, +) + +if TYPE_CHECKING: + from http.cookiejar import CookieJar + + from typing_extensions import Self + + from . import _types as _t + from .adapters import HTTPAdapter + from .cookies import RequestsCookieJar + +#: The set of HTTP status codes that indicate an automatically +#: processable redirect. +REDIRECT_STATI: Final[tuple[int, ...]] = ( # type: ignore[assignment] + codes.moved, # 301 + codes.found, # 302 + codes.other, # 303 + codes.temporary_redirect, # 307 + codes.permanent_redirect, # 308 +) + +DEFAULT_REDIRECT_LIMIT: int = 30 +CONTENT_CHUNK_SIZE: int = 10 * 1024 +ITER_CHUNK_SIZE: int = 512 + + +class RequestEncodingMixin: + url: str | None + + @property + def path_url(self) -> str: + """Build the path URL to use.""" + + url: list[str] = [] + + p = urlsplit(cast(str, self.url)) + + path = p.path + if not path: + path = "/" + + url.append(path) + + query = p.query + if query: + url.append("?") + url.append(query) + + return "".join(url) + + @overload + @staticmethod + def _encode_params(data: str) -> str: ... + + @overload + @staticmethod + def _encode_params(data: bytes) -> bytes: ... + + @overload + @staticmethod + def _encode_params( + data: _t.SupportsRead[str | bytes], + ) -> _t.SupportsRead[str | bytes]: ... + + @overload + @staticmethod + def _encode_params(data: _t.KVDataType) -> str: ... + + @staticmethod + def _encode_params( + data: _t.EncodableDataType, + ) -> str | bytes | _t.SupportsRead[str | bytes]: + """Encode parameters in a piece of data. + + Will successfully encode parameters when passed as a dict or a list of + 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary + if parameters are supplied as a dict. + """ + + if isinstance(data, (str, bytes)): + return data + elif isinstance(data, _SupportsRead): + return data + elif hasattr(data, "__iter__"): + result: list[tuple[bytes, bytes]] = [] + for k, vs in to_key_val_list(data): + if isinstance(vs, basestring) or not hasattr(vs, "__iter__"): + vs = [vs] + for v in vs: + if v is not None: + result.append( + ( + k.encode("utf-8") if isinstance(k, str) else k, + v.encode("utf-8") if isinstance(v, str) else v, + ) + ) + return urlencode(result, doseq=True) + else: + return data # type: ignore[return-value] # unreachable for valid _t.DataType + + @staticmethod + def _encode_files( + files: _t.FilesType, data: _t.RawDataType | None + ) -> tuple[bytes, str]: + """Build the body for a multipart/form-data request. + + Will successfully encode files when passed as a dict or a list of + tuples. Order is retained if data is a list of tuples but arbitrary + if parameters are supplied as a dict. + The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype) + or 4-tuples (filename, fileobj, contentype, custom_headers). + """ + if not files: + raise ValueError("Files must be provided.") + elif isinstance(data, basestring): + raise ValueError("Data must not be a string.") + + new_fields: list[RequestField | tuple[str, bytes]] = [] + fields = to_key_val_list(data or {}) + files = to_key_val_list(files or {}) + + for field, val in fields: + if isinstance(val, basestring) or not hasattr(val, "__iter__"): + val = [val] + for v in val: + if v is not None: + # Don't call str() on bytestrings: in Py3 it all goes wrong. + if not isinstance(v, bytes): + v = str(v) + + new_fields.append( + ( + field.decode("utf-8") + if isinstance(field, bytes) + else field, + v.encode("utf-8") if isinstance(v, str) else v, + ) + ) + + for k, v in files: + # support for explicit filename + ft = None + fh = None + if isinstance(v, (tuple, list)): + if len(v) == 2: + fn, fp = v + elif len(v) == 3: + fn, fp, ft = v + else: + fn, fp, ft, fh = v + else: + fn = guess_filename(v) or k + fp = v + + if isinstance(fp, (str, bytes, bytearray)): + fdata = fp + elif isinstance(fp, _SupportsRead): # type: ignore[reportUnnecessaryIsInstance] # defensive check for untyped callers + fdata = fp.read() + elif fp is None: # type: ignore[reportUnnecessaryComparison] # defensive check for untyped callers + continue + else: + fdata = fp + + rf = RequestField(name=k, data=fdata, filename=fn, headers=fh) + rf.make_multipart(content_type=ft) + new_fields.append(rf) + + body, content_type = encode_multipart_formdata(new_fields) + + return body, content_type + + +class RequestHooksMixin: + hooks: dict[str, list[_t.HookType]] + + def register_hook( + self, event: str, hook: Iterable[_t.HookType] | _t.HookType + ) -> None: + """Properly register a hook.""" + + if event not in self.hooks: + raise ValueError(f'Unsupported event specified, with event name "{event}"') + + if isinstance(hook, Callable): + self.hooks[event].append(hook) + elif hasattr(hook, "__iter__"): + self.hooks[event].extend(h for h in hook if isinstance(h, Callable)) # type: ignore[reportUnnecessaryIsInstance] # defensive runtime filter + + def deregister_hook(self, event: str, hook: _t.HookType) -> bool: + """Deregister a previously registered hook. + Returns True if the hook existed, False if not. + """ + + try: + self.hooks[event].remove(hook) + return True + except ValueError: + return False + + +class Request(RequestHooksMixin): + """A user-created :class:`Request ` object. + + Used to prepare a :class:`PreparedRequest `, which is sent to the server. + + :param method: HTTP method to use. + :param url: URL to send. + :param headers: dictionary of headers to send. + :param files: dictionary of {filename: fileobject} files to multipart upload. + :param data: the body to attach to the request. If a dictionary or + list of tuples ``[(key, value)]`` is provided, form-encoding will + take place. + :param json: json for the body to attach to the request (if files or data is not specified). + :param params: URL parameters to append to the URL. If a dictionary or + list of tuples ``[(key, value)]`` is provided, form-encoding will + take place. + :param auth: Auth handler or (user, pass) tuple. + :param cookies: dictionary or CookieJar of cookies to attach to this request. + :param hooks: dictionary of callback hooks, for internal usage. + + Usage:: + + >>> import requests + >>> req = requests.Request('GET', 'https://httpbin.org/get') + >>> req.prepare() + + """ + + method: str | None + url: _t.UriType | None + headers: Mapping[str, str | bytes] + files: _t.FilesType + data: _t.DataType + json: _t.JsonType + params: _t.ParamsType + auth: _t.AuthType + cookies: RequestsCookieJar | CookieJar | dict[str, str] | None + + def __init__( + self, + method: str | None = None, + url: _t.UriType | None = None, + headers: _t.HeadersType = None, + files: _t.FilesType = None, + data: _t.DataType = None, + params: _t.ParamsType = None, + auth: _t.AuthType = None, + cookies: RequestsCookieJar | CookieJar | dict[str, str] | None = None, + hooks: _t.HooksInputType | None = None, + json: _t.JsonType = None, + ) -> None: + # Default empty dicts for dict params. + data = [] if data is None else data + files = [] if files is None else files + headers = {} if headers is None else headers + params = {} if params is None else params + hooks = {} if hooks is None else hooks + + self.hooks = default_hooks() + for k, v in list(hooks.items()): + self.register_hook(event=k, hook=v) + + self.method = method + self.url = url + self.headers = headers + self.files = files + self.data = data + self.json = json + self.params = params + self.auth = auth + self.cookies = cookies + + def __repr__(self) -> str: + return f"" + + def prepare(self) -> PreparedRequest: + """Constructs a :class:`PreparedRequest ` for transmission and returns it.""" + p = PreparedRequest() + p.prepare( + method=self.method, + url=self.url, + headers=self.headers, + files=self.files, + data=self.data, + json=self.json, + params=self.params, + auth=self.auth, + cookies=self.cookies, + hooks=self.hooks, + ) + return p + + +class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): + """The fully mutable :class:`PreparedRequest ` object, + containing the exact bytes that will be sent to the server. + + Instances are generated from a :class:`Request ` object, and + should not be instantiated manually; doing so may produce undesirable + effects. + + Usage:: + + >>> import requests + >>> req = requests.Request('GET', 'https://httpbin.org/get') + >>> r = req.prepare() + >>> r + + + >>> s = requests.Session() + >>> s.send(r) + + """ + + method: str | None + url: str | None + headers: CaseInsensitiveDict[str | bytes] + _cookies: RequestsCookieJar | CookieJar | None + body: _t.BodyType + hooks: dict[str, list[_t.HookType]] + _body_position: int | object | None + + def __init__(self) -> None: + #: HTTP verb to send to the server. + self.method = None + #: HTTP URL to send the request to. + self.url = None + #: dictionary of HTTP headers. + self.headers = None # type: ignore[assignment] + # The `CookieJar` used to create the Cookie header will be stored here + # after prepare_cookies is called + self._cookies = None + #: request body to send to the server. + self.body = None + #: dictionary of callback hooks, for internal usage. + self.hooks = default_hooks() + #: integer denoting starting position of a readable file-like body. + self._body_position = None + + def prepare( + self, + method: str | None = None, + url: _t.UriType | None = None, + headers: Mapping[str, str | bytes] | None = None, + files: _t.FilesType = None, + data: _t.DataType = None, + params: _t.ParamsType = None, + auth: _t.AuthType = None, + cookies: RequestsCookieJar | CookieJar | dict[str, str] | None = None, + hooks: _t.HooksInputType | None = None, + json: _t.JsonType = None, + ) -> None: + """Prepares the entire request with the given parameters.""" + + url = cast("_t.UriType", url) + self.prepare_method(method) + self.prepare_url(url, params) + self.prepare_headers(headers) + self.prepare_cookies(cookies) + self.prepare_body(data, files, json) + self.prepare_auth(auth, url) + + # Note that prepare_auth must be last to enable authentication schemes + # such as OAuth to work on a fully prepared request. + + # This MUST go after prepare_auth. Authenticators could add a hook + self.prepare_hooks(hooks) + + def __repr__(self) -> str: + return f"" + + def copy(self) -> PreparedRequest: + p = PreparedRequest() + p.method = self.method + p.url = self.url + p.headers = self.headers.copy() if self.headers is not None else None # type: ignore[assignment] + p._cookies = _copy_cookie_jar(self._cookies) + p.body = self.body + p.hooks = self.hooks + p._body_position = self._body_position + return p + + def prepare_method(self, method: str | None) -> None: + """Prepares the given HTTP method.""" + self.method = method + if self.method is not None: + self.method = to_native_string(self.method.upper()) + + @staticmethod + def _get_idna_encoded_host(host: str) -> str: + from pip._vendor import idna + + try: + host = idna.encode(host, uts46=True).decode("utf-8") + except idna.IDNAError: + raise UnicodeError + return host + + def prepare_url( + self, + url: _t.UriType, + params: _t.ParamsType, + ) -> None: + """Prepares the given HTTP URL.""" + #: Accept objects that have string representations. + #: We're unable to blindly call unicode/str functions + #: as this will include the bytestring indicator (b'') + #: on python 3.x. + #: https://github.com/psf/requests/pull/2238 + if isinstance(url, bytes): + url = url.decode("utf8") + else: + url = str(url) + + # Remove leading whitespaces from url + url = url.lstrip() + + # Don't do any URL preparation for non-HTTP schemes like `mailto`, + # `data` etc to work around exceptions from `url_parse`, which + # handles RFC 3986 only. + if ":" in url and not url.lower().startswith("http"): + self.url = url + return + + # Support for unicode domain names and paths. + try: + scheme, auth, host, port, path, query, fragment = parse_url(url) + except LocationParseError as e: + raise InvalidURL(*e.args) + + if not scheme: + raise MissingSchema( + f"Invalid URL {url!r}: No scheme supplied. " + f"Perhaps you meant https://{url}?" + ) + + if not host: + raise InvalidURL(f"Invalid URL {url!r}: No host supplied") + + # In general, we want to try IDNA encoding the hostname if the string contains + # non-ASCII characters. This allows users to automatically get the correct IDNA + # behaviour. For strings containing only ASCII characters, we need to also verify + # it doesn't start with a wildcard (*), before allowing the unencoded hostname. + if not unicode_is_ascii(host): + try: + host = self._get_idna_encoded_host(host) + except UnicodeError: + raise InvalidURL("URL has an invalid label.") + elif host.startswith(("*", ".")): + raise InvalidURL("URL has an invalid label.") + + # Carefully reconstruct the network location + netloc = auth or "" + if netloc: + netloc += "@" + netloc += host + if port: + netloc += f":{port}" + + # Bare domains aren't valid URLs. + if not path: + path = "/" + + if isinstance(params, (str, bytes)): + params = to_native_string(params) + + if params is not None: + enc_params = self._encode_params(params) + else: + enc_params = "" + + if enc_params: + if query: + query = f"{query}&{enc_params}" + else: + query = enc_params + + url = requote_uri(urlunparse((scheme, netloc, path, "", query, fragment))) + self.url = url + + def prepare_headers(self, headers: Mapping[str, str | bytes] | None) -> None: + """Prepares the given HTTP headers.""" + + self.headers = CaseInsensitiveDict() + if headers: + for header in headers.items(): + # Raise exception on invalid header value. + check_header_validity(header) + name, value = header + self.headers[to_native_string(name)] = value + + def prepare_body( + self, data: _t.DataType, files: _t.FilesType, json: _t.JsonType = None + ) -> None: + """Prepares the given HTTP body data.""" + + # Check if file, fo, generator, iterator. + # If not, run through normal process. + + # Nottin' on you. + body = None + content_type = None + + if not data and json is not None: + # urllib3 requires a bytes-like body. Python 2's json.dumps + # provides this natively, but Python 3 gives a Unicode string. + content_type = "application/json" + + try: + body = complexjson.dumps(json, allow_nan=False) + except ValueError as ve: + raise InvalidJSONError(ve, request=self) + + if not isinstance(body, bytes): + body = body.encode("utf-8") + + # data that proxies attributes to underlying objects needs hasattr + is_iterable = isinstance(data, Iterable) or hasattr(data, "__iter__") + if is_iterable and not isinstance(data, (str, bytes, list, tuple, Mapping)): + try: + length = super_len(data) + except (TypeError, AttributeError, UnsupportedOperation): + length = None + + body = data + + if getattr(body, "tell", None) is not None: + # Record the current file position before reading. + # This will allow us to rewind a file in the event + # of a redirect. + try: + self._body_position = body.tell() # type: ignore[union-attr] # guarded by getattr check + except OSError: + # This differentiates from None, allowing us to catch + # a failed `tell()` later when trying to rewind the body + self._body_position = object() + + if files: + raise NotImplementedError( + "Streamed bodies and files are mutually exclusive." + ) + + if length: + self.headers["Content-Length"] = builtin_str(length) + else: + self.headers["Transfer-Encoding"] = "chunked" + else: + # After is_stream filtering, remaining data is raw (not streamed) + raw_data = cast("_t.RawDataType | None", data) + + # Multi-part file uploads. + if files: + (body, content_type) = self._encode_files(files, raw_data) + else: + if raw_data: + body = self._encode_params(raw_data) + if isinstance(data, basestring) or isinstance(data, _SupportsRead): + content_type = None + else: + content_type = "application/x-www-form-urlencoded" + + self.prepare_content_length(body) + + # Add content-type if it wasn't explicitly provided. + if content_type and ("content-type" not in self.headers): + self.headers["Content-Type"] = content_type + + self.body = body # type: ignore[assignment] # body transforms from DataType to BodyType + + def prepare_content_length(self, body: _t.BodyType) -> None: + """Prepare Content-Length header based on request method and body""" + if body is not None: + length = super_len(body) + if length: + # If length exists, set it. Otherwise, we fallback + # to Transfer-Encoding: chunked. + self.headers["Content-Length"] = builtin_str(length) + elif ( + self.method not in ("GET", "HEAD") + and self.headers.get("Content-Length") is None + ): + # Set Content-Length to 0 for methods that can have a body + # but don't provide one. (i.e. not GET or HEAD) + self.headers["Content-Length"] = "0" + + def prepare_auth( + self, + auth: _t.AuthType, + url: _t.UriType = "", + ) -> None: + """Prepares the given HTTP auth data.""" + + # If no Auth is explicitly provided, extract it from the URL first. + if auth is None: + url_auth = get_auth_from_url(cast(str, self.url)) + auth = url_auth if any(url_auth) else None + + if auth: + if isinstance(auth, tuple) and len(auth) == 2: # type: ignore[arg-type] # pyright widens tuple from Callable in AuthType + # special-case basic HTTP auth + auth_handler = HTTPBasicAuth(*auth) # type: ignore[arg-type] # pyright widens tuple from Callable in AuthType + else: + # TODO: can be fixed by flipping the conditionals + auth_handler = cast("Callable[..., PreparedRequest]", auth) + + # Allow auth to make its changes. + r = auth_handler(self) + + # Update self to reflect the auth changes. + self.__dict__.update(r.__dict__) + + # Recompute Content-Length + self.prepare_content_length(self.body) + + def prepare_cookies( + self, cookies: RequestsCookieJar | CookieJar | dict[str, str] | None + ) -> None: + """Prepares the given HTTP cookie data. + + This function eventually generates a ``Cookie`` header from the + given cookies using cookielib. Due to cookielib's design, the header + will not be regenerated if it already exists, meaning this function + can only be called once for the life of the + :class:`PreparedRequest ` object. Any subsequent calls + to ``prepare_cookies`` will have no actual effect, unless the "Cookie" + header is removed beforehand. + """ + if isinstance(cookies, cookielib.CookieJar): + self._cookies = cookies + else: + self._cookies = cookiejar_from_dict(cookies) + + cookies_jar = cast("CookieJar", self._cookies) + cookie_header = get_cookie_header(cookies_jar, self) + if cookie_header is not None: + self.headers["Cookie"] = cookie_header + + def prepare_hooks(self, hooks: _t.HooksInputType | None) -> None: + """Prepares the given hooks.""" + # hooks can be passed as None to the prepare method and to this + # method. To prevent iterating over None, simply use an empty list + # if hooks is False-y + hooks = hooks or {} + for event in hooks: + self.register_hook(event, hooks[event]) + + +class Response: + """The :class:`Response ` object, which contains a + server's response to an HTTP request. + """ + + _content: bytes | Literal[False] | None + _content_consumed: bool + _next: PreparedRequest | None + status_code: int + headers: CaseInsensitiveDict[str] + raw: Any + url: str + encoding: str | None + history: list[Response] + reason: str + cookies: RequestsCookieJar + elapsed: datetime.timedelta + request: PreparedRequest + connection: HTTPAdapter + + __attrs__: list[str] = [ + "_content", + "status_code", + "headers", + "url", + "history", + "encoding", + "reason", + "cookies", + "elapsed", + "request", + ] + + def __init__(self) -> None: + self._content = False + self._content_consumed = False + self._next = None + + #: Integer Code of responded HTTP Status, e.g. 404 or 200. + self.status_code = None # type: ignore[assignment] + + #: Case-insensitive Dictionary of Response Headers. + #: For example, ``headers['content-encoding']`` will return the + #: value of a ``'Content-Encoding'`` response header. + self.headers = CaseInsensitiveDict() + + #: File-like object representation of response (for advanced usage). + #: Use of ``raw`` requires that ``stream=True`` be set on the request. + #: This requirement does not apply for use internally to Requests. + self.raw = None + + #: Final URL location of Response. + self.url = None # type: ignore[assignment] + + #: Encoding to decode with when accessing r.text. + self.encoding = None + + #: A list of :class:`Response ` objects from + #: the history of the Request. Any redirect responses will end + #: up here. The list is sorted from the oldest to the most recent request. + self.history = [] + + #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK". + self.reason = None # type: ignore[assignment] + + #: A CookieJar of Cookies the server sent back. + self.cookies = cookiejar_from_dict({}) + + #: The amount of time elapsed between sending the request + #: and the arrival of the response (as a timedelta). + #: This property specifically measures the time taken between sending + #: the first byte of the request and finishing parsing the headers. It + #: is therefore unaffected by consuming the response content or the + #: value of the ``stream`` keyword argument. + self.elapsed = datetime.timedelta(0) + + #: The :class:`PreparedRequest ` object to which this + #: is a response. + self.request = None # type: ignore[assignment] + + def __enter__(self) -> Self: + return self + + def __exit__(self, *args: Any) -> None: + self.close() + + def __getstate__(self) -> dict[str, Any]: + # Consume everything; accessing the content attribute makes + # sure the content has been fully read. + if not self._content_consumed: + self.content + + return {attr: getattr(self, attr, None) for attr in self.__attrs__} + + def __setstate__(self, state: dict[str, Any]) -> None: + for name, value in state.items(): + setattr(self, name, value) + + # pickled objects do not have .raw + setattr(self, "_content_consumed", True) + setattr(self, "raw", None) + + def __repr__(self) -> str: + return f"" + + def __bool__(self) -> bool: + """Returns True if :attr:`status_code` is less than 400. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code, is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + return self.ok + + def __nonzero__(self) -> bool: + """Returns True if :attr:`status_code` is less than 400. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code, is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + return self.ok + + def __iter__(self) -> Iterator[bytes]: + """Allows you to use a response as an iterator.""" + return self.iter_content(128) + + @property + def ok(self) -> bool: + """Returns True if :attr:`status_code` is less than 400, False if not. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + try: + self.raise_for_status() + except HTTPError: + return False + return True + + @property + def is_redirect(self) -> bool: + """True if this Response is a well-formed HTTP redirect that could have + been processed automatically (by :meth:`Session.resolve_redirects`). + """ + return "location" in self.headers and self.status_code in REDIRECT_STATI + + @property + def is_permanent_redirect(self) -> bool: + """True if this Response one of the permanent versions of redirect.""" + return "location" in self.headers and self.status_code in ( + codes.moved_permanently, + codes.permanent_redirect, + ) + + @property + def next(self) -> PreparedRequest | None: + """Returns a PreparedRequest for the next request in a redirect chain, if there is one.""" + return self._next + + @property + def apparent_encoding(self) -> str | None: + """The apparent encoding, provided by the charset_normalizer or chardet libraries.""" + if chardet is not None: + return chardet.detect(self.content)["encoding"] + else: + # If no character detection library is available, we'll fall back + # to a standard Python utf-8 str. + return "utf-8" + + @overload + def iter_content( + self, chunk_size: int | None = 1, decode_unicode: Literal[False] = False + ) -> Iterator[bytes]: ... + @overload + def iter_content( + self, chunk_size: int | None = 1, *, decode_unicode: Literal[True] + ) -> Iterator[str | bytes]: ... + def iter_content( + self, chunk_size: int | None = 1, decode_unicode: bool = False + ) -> Iterator[str | bytes]: + """Iterates over the response data. When stream=True is set on the + request, this avoids reading the content at once into memory for + large responses. The chunk size is the number of bytes it should + read into memory. This is not necessarily the length of each item + returned as decoding can take place. + + chunk_size must be of type int or None. A value of None will + function differently depending on the value of `stream`. + stream=True will read data as it arrives in whatever size the + chunks are received. If stream=False, data is returned as + a single chunk. + + If decode_unicode is True, content will be decoded using encoding + information from the response. If no encoding information is available, + bytes will be returned. This can be bypassed by manually setting + `encoding` on the response. + """ + + def generate() -> Generator[bytes, None, None]: + # Special case for urllib3. + if hasattr(self.raw, "stream"): + try: + yield from self.raw.stream(chunk_size, decode_content=True) + except ProtocolError as e: + raise ChunkedEncodingError(e) + except DecodeError as e: + raise ContentDecodingError(e) + except ReadTimeoutError as e: + raise ConnectionError(e) + except SSLError as e: + raise RequestsSSLError(e) + else: + # Standard file-like object. + while True: + chunk = self.raw.read(chunk_size) + if not chunk: + break + yield chunk + + self._content_consumed = True + + if self._content_consumed and isinstance(self._content, bool): + raise StreamConsumedError() + elif chunk_size is not None and not isinstance(chunk_size, int): # type: ignore[reportUnnecessaryIsInstance] # runtime guard for untyped callers + raise TypeError( + f"chunk_size must be an int, it is instead a {type(chunk_size)}." + ) + + if self._content_consumed: + # simulate reading small chunks of the content + content = cast(bytes, self._content) + chunks = iter_slices(content, chunk_size) + else: + chunks = generate() + + if decode_unicode: + chunks = stream_decode_response_unicode(chunks, self) + + return chunks + + @overload + def iter_lines( + self, + chunk_size: int = ITER_CHUNK_SIZE, + decode_unicode: Literal[False] = False, + delimiter: bytes | None = None, + ) -> Iterator[bytes]: ... + @overload + def iter_lines( + self, + chunk_size: int = ITER_CHUNK_SIZE, + *, + decode_unicode: Literal[True], + delimiter: str | bytes | None = None, + ) -> Iterator[str | bytes]: ... + def iter_lines( + self, + chunk_size: int = ITER_CHUNK_SIZE, + decode_unicode: bool = False, + delimiter: str | bytes | None = None, + ) -> Iterator[str | bytes]: + """Iterates over the response data, one line at a time. When + stream=True is set on the request, this avoids reading the + content at once into memory for large responses. + + The decode_unicode param works the same as in `iter_content`, with the + same caveats. + + .. note:: This method is not reentrant safe. + """ + + pending: str | bytes | None = None + + for chunk in self.iter_content( + chunk_size=chunk_size, decode_unicode=decode_unicode + ): + if pending is not None: + # TODO: remove cast after iter_lines rewrite + chunk = cast("str | bytes", pending + chunk) # type: ignore[operator] + + if delimiter: + lines = chunk.split(delimiter) # type: ignore[arg-type] + else: + lines = chunk.splitlines() + + if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]: + pending = lines.pop() + else: + pending = None + + yield from lines + + if pending is not None: + yield pending + + @property + def content(self) -> bytes: + """Content of the response, in bytes.""" + + if self._content is False: + # Read the contents. + if self._content_consumed: + raise RuntimeError("The content for this response was already consumed") + + if self.status_code == 0 or self.raw is None: + self._content = None + else: + self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b"" + + self._content_consumed = True + # don't need to release the connection; that's been handled by urllib3 + # since we exhausted the data. + return self._content # type: ignore[return-value] + + @property + def text(self) -> str: + """Content of the response, in unicode. + + If Response.encoding is None, encoding will be guessed using + ``charset_normalizer`` or ``chardet``. + + The encoding of the response content is determined based solely on HTTP + headers, following RFC 2616 to the letter. If you can take advantage of + non-HTTP knowledge to make a better guess at the encoding, you should + set ``r.encoding`` appropriately before accessing this property. + """ + + # Try charset from content-type + content = None + encoding = self.encoding + + if not self.content: + return "" + + # Fallback to auto-detected encoding. + if self.encoding is None: + encoding = self.apparent_encoding + + # Decode unicode from given encoding. + try: + content = str(self.content, encoding or "utf-8", errors="replace") + except (LookupError, TypeError): + # A LookupError is raised if the encoding was not found which could + # indicate a misspelling or similar mistake. + # + # A TypeError can be raised if encoding is None + # + # So we try blindly encoding. + content = str(self.content, errors="replace") + + return content + + def json(self, **kwargs: Any) -> Any: + r"""Decodes the JSON response body (if any) as a Python object. + + This may return a dictionary, list, etc. depending on what is in the response. + + :param \*\*kwargs: Optional arguments that ``json.loads`` takes. + :raises requests.exceptions.JSONDecodeError: If the response body does not + contain valid json. + """ + + if not self.encoding and self.content and len(self.content) > 3: + # No encoding set. JSON RFC 4627 section 3 states we should expect + # UTF-8, -16 or -32. Detect which one to use; If the detection or + # decoding fails, fall back to `self.text` (using charset_normalizer to make + # a best guess). + encoding = guess_json_utf(self.content) + if encoding is not None: + try: + return complexjson.loads(self.content.decode(encoding), **kwargs) + except UnicodeDecodeError: + # Wrong UTF codec detected; usually because it's not UTF-8 + # but some other 8-bit codec. This is an RFC violation, + # and the server didn't bother to tell us what codec *was* + # used. + pass + except JSONDecodeError as e: + raise RequestsJSONDecodeError(e.msg, e.doc, e.pos) + + try: + return complexjson.loads(self.text, **kwargs) + except JSONDecodeError as e: + # Catch JSON-related errors and raise as requests.JSONDecodeError + # This aliases json.JSONDecodeError and simplejson.JSONDecodeError + raise RequestsJSONDecodeError(e.msg, e.doc, e.pos) + + @property + def links(self) -> dict[str, dict[str, str]]: + """Returns the parsed header links of the response, if any.""" + + header = self.headers.get("link") + + resolved_links: dict[str, dict[str, str]] = {} + + if header: + links = parse_header_links(header) + + for link in links: + key = link.get("rel") or link.get("url") + if key is not None: + resolved_links[key] = link + + return resolved_links + + def raise_for_status(self) -> None: + """Raises :class:`HTTPError`, if one occurred.""" + + http_error_msg = "" + if isinstance(self.reason, bytes): + # We attempt to decode utf-8 first because some servers + # choose to localize their reason strings. If the string + # isn't utf-8, we fall back to iso-8859-1 for all other + # encodings. (See PR #3538) + try: + reason = self.reason.decode("utf-8") + except UnicodeDecodeError: + reason = self.reason.decode("iso-8859-1") + else: + reason = self.reason + + if 400 <= self.status_code < 500: + http_error_msg = ( + f"{self.status_code} Client Error: {reason} for url: {self.url}" + ) + + elif 500 <= self.status_code < 600: + http_error_msg = ( + f"{self.status_code} Server Error: {reason} for url: {self.url}" + ) + + if http_error_msg: + raise HTTPError(http_error_msg, response=self) + + def close(self) -> None: + """Releases the connection back to the pool. Once this method has been + called the underlying ``raw`` object must not be accessed again. + + *Note: Should not normally need to be called explicitly.* + """ + if not self._content_consumed: + self.raw.close() + + release_conn = getattr(self.raw, "release_conn", None) + if release_conn is not None: + release_conn() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/packages.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/packages.py new file mode 100644 index 0000000..200c382 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/packages.py @@ -0,0 +1,25 @@ +import sys + +from .compat import chardet + +# This code exists for backwards compatibility reasons. +# I don't like it either. Just look the other way. :) + +for package in ("urllib3", "idna"): + vendored_package = "pip._vendor." + package + locals()[package] = __import__(vendored_package) + # This traversal is apparently necessary such that the identities are + # preserved (requests.packages.urllib3.* is urllib3.*) + for mod in list(sys.modules): + if mod == vendored_package or mod.startswith(vendored_package + '.'): + unprefixed_mod = mod[len("pip._vendor."):] + sys.modules['pip._vendor.requests.packages.' + unprefixed_mod] = sys.modules[mod] + +if chardet is not None: + target = chardet.__name__ + for mod in list(sys.modules): + if mod == target or mod.startswith(f"{target}."): + imported_mod = sys.modules[mod] + sys.modules[f"requests.packages.{mod}"] = imported_mod + mod = mod.replace(target, "chardet") + sys.modules[f"requests.packages.{mod}"] = imported_mod diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/requests/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/sessions.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/sessions.py new file mode 100644 index 0000000..feaf340 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/sessions.py @@ -0,0 +1,920 @@ +""" +requests.sessions +~~~~~~~~~~~~~~~~~ + +This module provides a Session object to manage and persist settings across +requests (cookies, auth, proxies). +""" + +from __future__ import annotations + +import os +import sys +import time +from collections import OrderedDict +from collections.abc import Generator, Mapping, MutableMapping +from datetime import timedelta +from typing import TYPE_CHECKING, Any, cast + +from ._internal_utils import to_native_string +from ._types import is_prepared as _is_prepared +from .adapters import HTTPAdapter +from .auth import _basic_auth_str # type: ignore[reportPrivateUsage] +from .compat import cookielib, urljoin, urlparse +from .cookies import ( + RequestsCookieJar, + cookiejar_from_dict, + extract_cookies_to_jar, + merge_cookies, +) +from .exceptions import ( + ChunkedEncodingError, + ContentDecodingError, + InvalidSchema, + TooManyRedirects, +) +from .hooks import default_hooks, dispatch_hook + +# formerly defined here, reexposed here for backward compatibility +from .models import ( # noqa: F401 + DEFAULT_REDIRECT_LIMIT, + REDIRECT_STATI, # type: ignore[reportUnusedImport] + PreparedRequest, + Request, + Response, +) +from .status_codes import codes +from .structures import CaseInsensitiveDict +from .utils import ( # noqa: F401 + DEFAULT_PORTS, + default_headers, + get_auth_from_url, + get_environ_proxies, + get_netrc_auth, + requote_uri, + resolve_proxies, + rewind_body, + should_bypass_proxies, # type: ignore[reportUnusedImport] # re-export for external consumers + to_key_val_list, +) + +if TYPE_CHECKING: + from http.cookiejar import CookieJar + + from typing_extensions import Self, Unpack + + from . import _types as _t + from .adapters import BaseAdapter + +# Preferred clock, based on which one is more accurate on a given system. +if sys.platform == "win32": + preferred_clock = time.perf_counter +else: + preferred_clock = time.time + + +def merge_setting( + request_setting: Any, session_setting: Any, dict_class: type = OrderedDict +) -> Any: + """Determines appropriate setting for a given request, taking into account + the explicit setting on that request, and the setting in the session. If a + setting is a dictionary, they will be merged together using `dict_class` + """ + + if session_setting is None: + return request_setting + + if request_setting is None: + return session_setting + + # Bypass if not a dictionary (e.g. verify) + if not ( + isinstance(session_setting, Mapping) and isinstance(request_setting, Mapping) + ): + return request_setting + + merged_setting = dict_class(to_key_val_list(session_setting)) # type: ignore[arg-type] # isinstance narrows Any to Mapping[Unknown] + merged_setting.update(to_key_val_list(request_setting)) # type: ignore[arg-type] + + # Remove keys that are set to None. Extract keys first to avoid altering + # the dictionary during iteration. + none_keys = [k for (k, v) in merged_setting.items() if v is None] + for key in none_keys: + del merged_setting[key] + + return merged_setting + + +def merge_hooks( + request_hooks: _t.HooksType, + session_hooks: _t.HooksType, + dict_class: type = OrderedDict, +) -> _t.HooksType: + """Properly merges both requests and session hooks. + + This is necessary because when request_hooks == {'response': []}, the + merge breaks Session hooks entirely. + """ + if session_hooks is None or session_hooks.get("response") == []: + return request_hooks + + if request_hooks is None or request_hooks.get("response") == []: + return session_hooks + + return merge_setting(request_hooks, session_hooks, dict_class) + + +class SessionRedirectMixin: + max_redirects: int + trust_env: bool + cookies: RequestsCookieJar + + def send(self, request: PreparedRequest, **kwargs: Any) -> Response: ... + + def get_redirect_target(self, resp: Response) -> str | None: + """Receives a Response. Returns a redirect URI or ``None``""" + # Due to the nature of how requests processes redirects this method will + # be called at least once upon the original response and at least twice + # on each subsequent redirect response (if any). + # If a custom mixin is used to handle this logic, it may be advantageous + # to cache the redirect location onto the response object as a private + # attribute. + if resp.is_redirect: + location = resp.headers["location"] + # Currently the underlying http module on py3 decode headers + # in latin1, but empirical evidence suggests that latin1 is very + # rarely used with non-ASCII characters in HTTP headers. + # It is more likely to get UTF8 header rather than latin1. + # This causes incorrect handling of UTF8 encoded location headers. + # To solve this, we re-encode the location in latin1. + location = location.encode("latin1") + return to_native_string(location, "utf8") + return None + + def should_strip_auth(self, old_url: str, new_url: str) -> bool: + """Decide whether Authorization header should be removed when redirecting""" + old_parsed = urlparse(old_url) + new_parsed = urlparse(new_url) + if old_parsed.hostname != new_parsed.hostname: + return True + # Special case: allow http -> https redirect when using the standard + # ports. This isn't specified by RFC 7235, but is kept to avoid + # breaking backwards compatibility with older versions of requests + # that allowed any redirects on the same host. + if ( + old_parsed.scheme == "http" + and old_parsed.port in (80, None) + and new_parsed.scheme == "https" + and new_parsed.port in (443, None) + ): + return False + + # Handle default port usage corresponding to scheme. + changed_port = old_parsed.port != new_parsed.port + changed_scheme = old_parsed.scheme != new_parsed.scheme + default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None) + if ( + not changed_scheme + and old_parsed.port in default_port + and new_parsed.port in default_port + ): + return False + + # Standard case: root URI must match + return changed_port or changed_scheme + + def resolve_redirects( + self, + resp: Response, + req: PreparedRequest, + stream: bool = False, + timeout: _t.TimeoutType = None, + verify: _t.VerifyType = True, + cert: _t.CertType = None, + proxies: dict[str, str] | None = None, + yield_requests: bool = False, + **adapter_kwargs: Any, + ) -> Generator[Response, None, None]: + """Receives a Response. Returns a generator of Responses or Requests.""" + + hist: list[Response] = [] # keep track of history + + url = self.get_redirect_target(resp) + previous_fragment = urlparse(req.url).fragment + while url: + prepared_request = req.copy() + + # Update history and keep track of redirects. + resp.history = hist[:] + hist.append(resp) + + try: + resp.content # Consume socket so it can be released + except (ChunkedEncodingError, ContentDecodingError, RuntimeError): + resp.raw.read(decode_content=False) + + if len(resp.history) >= self.max_redirects: + raise TooManyRedirects( + f"Exceeded {self.max_redirects} redirects.", response=resp + ) + + # Release the connection back into the pool. + resp.close() + + # Handle redirection without scheme (see: RFC 1808 Section 4) + if url.startswith("//"): + parsed_rurl = urlparse(resp.url) + url = ":".join([to_native_string(parsed_rurl.scheme), url]) + + # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2) + parsed = urlparse(url) + if parsed.fragment == "" and previous_fragment: + parsed = parsed._replace(fragment=previous_fragment) + elif parsed.fragment: + previous_fragment = parsed.fragment + url = parsed.geturl() + + # Facilitate relative 'location' headers, as allowed by RFC 7231. + # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') + # Compliant with RFC3986, we percent encode the url. + if not parsed.netloc: + url = urljoin(resp.url, requote_uri(url)) + else: + url = requote_uri(url) + + prepared_request.url = to_native_string(url) + + self.rebuild_method(prepared_request, resp) + + # https://github.com/psf/requests/issues/1084 + if resp.status_code not in ( + codes.temporary_redirect, + codes.permanent_redirect, + ): + # https://github.com/psf/requests/issues/3490 + purged_headers = ("Content-Length", "Content-Type", "Transfer-Encoding") + for header in purged_headers: + prepared_request.headers.pop(header, None) + prepared_request.body = None + + headers = prepared_request.headers + headers.pop("Cookie", None) + + # Extract any cookies sent on the response to the cookiejar + # in the new request. Because we've mutated our copied prepared + # request, use the old one that we haven't yet touched. + cookie_jar = cast("CookieJar", prepared_request._cookies) # type: ignore[reportPrivateUsage] + extract_cookies_to_jar(cookie_jar, req, resp.raw) + merge_cookies(cookie_jar, self.cookies) + prepared_request.prepare_cookies(cookie_jar) + + # Rebuild auth and proxy information. + proxies = self.rebuild_proxies(prepared_request, proxies) + self.rebuild_auth(prepared_request, resp) + + # A failed tell() sets `_body_position` to `object()`. This non-None + # value ensures `rewindable` will be True, allowing us to raise an + # UnrewindableBodyError, instead of hanging the connection. + rewindable = prepared_request._body_position is not None and ( # type: ignore[reportPrivateUsage] + "Content-Length" in headers or "Transfer-Encoding" in headers + ) + + # Attempt to rewind consumed file-like object. + if rewindable: + rewind_body(prepared_request) + + # Override the original request. + req = prepared_request + + if yield_requests: + yield req # type: ignore[misc] # Internal use only, returns PreparedRequest + else: + resp = self.send( + req, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + allow_redirects=False, + **adapter_kwargs, + ) + + extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) + + # extract redirect url, if any, for the next loop + url = self.get_redirect_target(resp) + yield resp + + def rebuild_auth( + self, prepared_request: PreparedRequest, response: Response + ) -> None: + """When being redirected we may want to strip authentication from the + request to avoid leaking credentials. This method intelligently removes + and reapplies authentication where possible to avoid credential loss. + """ + original_request = response.request + assert _is_prepared(original_request) + assert _is_prepared(prepared_request) + + headers = prepared_request.headers + original_url = original_request.url + url = prepared_request.url + + if "Authorization" in headers and self.should_strip_auth(original_url, url): + # If we get redirected to a new host, we should strip out any + # authentication headers. + del headers["Authorization"] + + # .netrc might have more auth for us on our new host. + new_auth = get_netrc_auth(url) if self.trust_env else None + if new_auth is not None: + prepared_request.prepare_auth(new_auth) + + def rebuild_proxies( + self, + prepared_request: PreparedRequest, + proxies: dict[str, str] | None, + ) -> dict[str, str]: + """This method re-evaluates the proxy configuration by considering the + environment variables. If we are redirected to a URL covered by + NO_PROXY, we strip the proxy configuration. Otherwise, we set missing + proxy keys for this URL (in case they were stripped by a previous + redirect). + + This method also replaces the Proxy-Authorization header where + necessary. + + :rtype: dict + """ + assert _is_prepared(prepared_request) + headers = prepared_request.headers + scheme = urlparse(prepared_request.url).scheme + new_proxies = resolve_proxies(prepared_request, proxies, self.trust_env) + + if "Proxy-Authorization" in headers: + del headers["Proxy-Authorization"] + + try: + username, password = get_auth_from_url(new_proxies[scheme]) + except KeyError: + username, password = None, None + + # urllib3 handles proxy authorization for us in the standard adapter. + # Avoid appending this to TLS tunneled requests where it may be leaked. + if not scheme.startswith("https") and username and password: + headers["Proxy-Authorization"] = _basic_auth_str(username, password) + + return new_proxies + + def rebuild_method( + self, prepared_request: PreparedRequest, response: Response + ) -> None: + """When being redirected we may want to change the method of the request + based on certain specs or browser behavior. + """ + method = prepared_request.method + + # https://tools.ietf.org/html/rfc7231#section-6.4.4 + if response.status_code == codes.see_other and method != "HEAD": + method = "GET" + + # Do what the browsers do, despite standards... + # First, turn 302s into GETs. + if response.status_code == codes.found and method != "HEAD": + method = "GET" + + # Second, if a POST is responded to with a 301, turn it into a GET. + # This bizarre behaviour is explained in Issue 1704. + if response.status_code == codes.moved and method == "POST": + method = "GET" + + prepared_request.method = method + + +class Session(SessionRedirectMixin): + """A Requests session. + + Provides cookie persistence, connection-pooling, and configuration. + + Basic Usage:: + + >>> import requests + >>> s = requests.Session() + >>> s.get('https://httpbin.org/get') + + + Or as a context manager:: + + >>> with requests.Session() as s: + ... s.get('https://httpbin.org/get') + + """ + + headers: CaseInsensitiveDict[str] + auth: _t.AuthType + proxies: dict[str, str] + hooks: dict[str, list[_t.HookType]] + params: MutableMapping[str, Any] + stream: bool + verify: _t.VerifyType + cert: _t.CertType + max_redirects: int + trust_env: bool + cookies: RequestsCookieJar + adapters: MutableMapping[str, BaseAdapter] + + __attrs__: list[str] = [ + "headers", + "cookies", + "auth", + "proxies", + "hooks", + "params", + "verify", + "cert", + "adapters", + "stream", + "trust_env", + "max_redirects", + ] + + def __init__(self) -> None: + #: A case-insensitive dictionary of headers to be sent on each + #: :class:`Request ` sent from this + #: :class:`Session `. + self.headers = default_headers() + + #: Default Authentication tuple or object to attach to + #: :class:`Request `. + self.auth = None + + #: Dictionary mapping protocol or protocol and host to the URL of the proxy + #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to + #: be used on each :class:`Request `. + self.proxies = {} + + #: Event-handling hooks. + self.hooks = default_hooks() + + #: Dictionary of querystring data to attach to each + #: :class:`Request `. The dictionary values may be lists for + #: representing multivalued query parameters. + self.params = {} + + #: Stream response content default. + self.stream = False + + #: SSL Verification default. + #: Defaults to `True`, requiring requests to verify the TLS certificate at the + #: remote end. + #: If verify is set to `False`, requests will accept any TLS certificate + #: presented by the server, and will ignore hostname mismatches and/or + #: expired certificates, which will make your application vulnerable to + #: man-in-the-middle (MitM) attacks. + #: Only set this to `False` for testing. + #: If verify is set to a string, it must be the path to a CA bundle file + #: that will be used to verify the TLS certificate. + self.verify = True + + #: SSL client certificate default, if String, path to ssl client + #: cert file (.pem). If Tuple, ('cert', 'key') pair. + self.cert = None + + #: Maximum number of redirects allowed. If the request exceeds this + #: limit, a :class:`TooManyRedirects` exception is raised. + #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is + #: 30. + self.max_redirects = DEFAULT_REDIRECT_LIMIT + + #: Trust environment settings for proxy configuration, default + #: authentication and similar. + self.trust_env = True + + #: A CookieJar containing all currently outstanding cookies set on this + #: session. By default it is a + #: :class:`RequestsCookieJar `, but + #: may be any other ``cookielib.CookieJar`` compatible object. + self.cookies = cookiejar_from_dict({}) + + # Default connection adapters. + self.adapters = OrderedDict() + self.mount("https://", HTTPAdapter()) + self.mount("http://", HTTPAdapter()) + + def __enter__(self) -> Self: + return self + + def __exit__(self, *args: Any) -> None: + self.close() + + def prepare_request(self, request: Request) -> PreparedRequest: + """Constructs a :class:`PreparedRequest ` for + transmission and returns it. The :class:`PreparedRequest` has settings + merged from the :class:`Request ` instance and those of the + :class:`Session`. + + :param request: :class:`Request` instance to prepare with this + session's settings. + :rtype: requests.PreparedRequest + """ + url = cast("_t.UriType", request.url) + method = cast(str, request.method) + + cookies = request.cookies or {} + + # Bootstrap CookieJar. + if not isinstance(cookies, cookielib.CookieJar): + cookies = cookiejar_from_dict(cookies) + + # Merge with session cookies + merged_cookies = merge_cookies( + merge_cookies(RequestsCookieJar(), self.cookies), cookies + ) + + # Set environment's basic authentication if not explicitly set. + auth = request.auth + if self.trust_env and not auth and not self.auth: + auth = get_netrc_auth(url) + + p = PreparedRequest() + p.prepare( + method=method.upper(), + url=url, + files=request.files, + data=request.data, + json=request.json, + headers=merge_setting( + request.headers, self.headers, dict_class=CaseInsensitiveDict + ), + params=merge_setting(request.params, self.params), + auth=merge_setting(auth, self.auth), + cookies=merged_cookies, + hooks=merge_hooks(request.hooks, self.hooks), + ) + return p + + def request( + self, + method: str, + url: _t.UriType, + params: _t.ParamsType = None, + data: _t.DataType = None, + headers: _t.HeadersType = None, + cookies: RequestsCookieJar | CookieJar | dict[str, str] | None = None, + files: _t.FilesType = None, + auth: _t.AuthType = None, + timeout: _t.TimeoutType = None, + allow_redirects: bool = True, + proxies: dict[str, str] | None = None, + hooks: _t.HooksInputType | None = None, + stream: bool | None = None, + verify: _t.VerifyType | None = None, + cert: _t.CertType = None, + json: _t.JsonType = None, + ) -> Response: + """Constructs a :class:`Request `, prepares it and sends it. + Returns :class:`Response ` object. + + :param method: method for the new :class:`Request` object. + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary or bytes to be sent in the query + string for the :class:`Request`. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the + :class:`Request`. + :param headers: (optional) Dictionary of HTTP Headers to send with the + :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with the + :class:`Request`. + :param files: (optional) Dictionary of ``'filename': file-like-objects`` + for multipart encoding upload. + :param auth: (optional) Auth tuple or callable to enable + Basic/Digest/Custom HTTP Auth. + :param timeout: (optional) How many seconds to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple + :param allow_redirects: (optional) Set to True by default. + :type allow_redirects: bool + :param proxies: (optional) Dictionary mapping protocol or protocol and + hostname to the URL of the proxy. + :param hooks: (optional) Dictionary mapping hook name to one event or + list of events, event must be callable. + :param stream: (optional) whether to immediately download the response + content. Defaults to ``False``. + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use. Defaults to ``True``. When set to + ``False``, requests will accept any TLS certificate presented by + the server, and will ignore hostname mismatches and/or expired + certificates, which will make your application vulnerable to + man-in-the-middle (MitM) attacks. Setting verify to ``False`` + may be useful during local development or testing. + :param cert: (optional) if String, path to ssl client cert file (.pem). + If Tuple, ('cert', 'key') pair. + :rtype: requests.Response + """ + if isinstance(url, bytes): + url = url.decode("utf-8") + + # Create the Request. + req = Request( + method=method.upper(), + url=url, + headers=headers, + files=files, + data=data or {}, + json=json, + params=params or {}, + auth=auth, + cookies=cookies, + hooks=hooks, + ) + prep = self.prepare_request(req) + + assert _is_prepared(prep) + + proxies = proxies or {} + + settings = self.merge_environment_settings( + prep.url, proxies, stream, verify, cert + ) + + # Send the request. + send_kwargs = { + "timeout": timeout, + "allow_redirects": allow_redirects, + } + send_kwargs.update(settings) + resp = self.send(prep, **send_kwargs) + + return resp + + def get( + self, + url: _t.UriType, + params: _t.ParamsType = None, + **kwargs: Unpack[_t.GetKwargs], + ) -> Response: + r"""Sends a GET request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary, list of tuples or bytes to send + in the query string for the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault("allow_redirects", True) + return self.request("GET", url, params=params, **kwargs) + + def options(self, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response: + r"""Sends a OPTIONS request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault("allow_redirects", True) + return self.request("OPTIONS", url, **kwargs) + + def head(self, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response: + r"""Sends a HEAD request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault("allow_redirects", False) + return self.request("HEAD", url, **kwargs) + + def post( + self, + url: _t.UriType, + data: _t.DataType = None, + json: _t.JsonType = None, + **kwargs: Unpack[_t.PostKwargs], + ) -> Response: + r"""Sends a POST request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request("POST", url, data=data, json=json, **kwargs) + + def put( + self, url: _t.UriType, data: _t.DataType = None, **kwargs: Unpack[_t.DataKwargs] + ) -> Response: + r"""Sends a PUT request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request("PUT", url, data=data, **kwargs) + + def patch( + self, url: _t.UriType, data: _t.DataType = None, **kwargs: Unpack[_t.DataKwargs] + ) -> Response: + r"""Sends a PATCH request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request("PATCH", url, data=data, **kwargs) + + def delete(self, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response: + r"""Sends a DELETE request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request("DELETE", url, **kwargs) + + def send(self, request: PreparedRequest, **kwargs: Any) -> Response: + """Send a given PreparedRequest. + + :rtype: requests.Response + """ + # Set defaults that the hooks can utilize to ensure they always have + # the correct parameters to reproduce the previous request. + kwargs.setdefault("stream", self.stream) + kwargs.setdefault("verify", self.verify) + kwargs.setdefault("cert", self.cert) + if "proxies" not in kwargs: + kwargs["proxies"] = resolve_proxies(request, self.proxies, self.trust_env) + + # It's possible that users might accidentally send a Request object. + # Guard against that specific failure case. + if isinstance(request, Request): + raise ValueError("You can only send PreparedRequests.") + + assert _is_prepared(request) + + # Set up variables needed for resolve_redirects and dispatching of hooks + allow_redirects = kwargs.pop("allow_redirects", True) + stream = kwargs.get("stream") + hooks = request.hooks + + # Get the appropriate adapter to use + adapter = self.get_adapter(url=request.url) + + # Start time (approximately) of the request + start = preferred_clock() + + # Send the request + r = adapter.send(request, **kwargs) + + # Total elapsed time of the request (approximately) + elapsed = preferred_clock() - start + r.elapsed = timedelta(seconds=elapsed) + + # Response manipulation hooks + r = dispatch_hook("response", hooks, r, **kwargs) + + # Persist cookies + if r.history: + # If the hooks create history then we want those cookies too + for resp in r.history: + extract_cookies_to_jar(self.cookies, resp.request, resp.raw) + + extract_cookies_to_jar(self.cookies, request, r.raw) + + # Resolve redirects if allowed. + if allow_redirects: + # Redirect resolving generator. + gen = self.resolve_redirects(r, request, **kwargs) + history = [resp for resp in gen] + else: + history = [] + + # Shuffle things around if there's history. + if history: + # Insert the first (original) request at the start + history.insert(0, r) + # Get the last request made + r = history.pop() + r.history = history + + # If redirects aren't being followed, store the response on the Request for Response.next(). + if not allow_redirects: + try: + r._next = next( # type: ignore[assignment] # yield_requests=True returns PreparedRequest + self.resolve_redirects(r, request, yield_requests=True, **kwargs) + ) + except StopIteration: + pass + + if not stream: + r.content + + return r + + def merge_environment_settings( + self, + url: str, + proxies: dict[str, str] | None, + stream: bool | None, + verify: _t.VerifyType | None, + cert: _t.CertType, + ) -> dict[str, Any]: + """ + Check the environment and merge it with some settings. + + :rtype: dict + """ + # Gather clues from the surrounding environment. + if self.trust_env: + # Set environment's proxies. + no_proxy = proxies.get("no_proxy") if proxies is not None else None + env_proxies = get_environ_proxies(url, no_proxy=no_proxy) + if proxies is not None: + for k, v in env_proxies.items(): + proxies.setdefault(k, v) + + # Look for requests environment configuration + # and be compatible with cURL. + if verify is True or verify is None: + verify = ( + os.environ.get("REQUESTS_CA_BUNDLE") + or os.environ.get("CURL_CA_BUNDLE") + or verify + ) + + # Merge all the kwargs. + proxies = merge_setting(proxies, self.proxies) + stream = merge_setting(stream, self.stream) + verify = merge_setting(verify, self.verify) + cert = merge_setting(cert, self.cert) + + return {"proxies": proxies, "stream": stream, "verify": verify, "cert": cert} + + def get_adapter(self, url: str) -> BaseAdapter: + """ + Returns the appropriate connection adapter for the given URL. + + :rtype: requests.adapters.BaseAdapter + """ + for prefix, adapter in self.adapters.items(): + if url.lower().startswith(prefix.lower()): + return adapter + + # Nothing matches :-/ + raise InvalidSchema(f"No connection adapters were found for {url!r}") + + def close(self) -> None: + """Closes all adapters and as such the session""" + for v in self.adapters.values(): + v.close() + + def mount(self, prefix: str, adapter: BaseAdapter) -> None: + """Registers a connection adapter to a prefix. + + Adapters are sorted in descending order by prefix length. + """ + self.adapters[prefix] = adapter + keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] + + for key in keys_to_move: + self.adapters[key] = self.adapters.pop(key) + + def __getstate__(self) -> dict[str, Any]: + state = {attr: getattr(self, attr, None) for attr in self.__attrs__} + return state + + def __setstate__(self, state: dict[str, Any]) -> None: + for attr, value in state.items(): + setattr(self, attr, value) + + +def session() -> Session: + """ + Returns a :class:`Session` for context-management. + + .. deprecated:: 1.0.0 + + This method has been deprecated since version 1.0.0 and is only kept for + backwards compatibility. New code should use :class:`~requests.sessions.Session` + to create a session. This may be removed at a future date. + + :rtype: Session + """ + return Session() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/status_codes.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/status_codes.py new file mode 100644 index 0000000..6c59d6b --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/status_codes.py @@ -0,0 +1,128 @@ +r""" +The ``codes`` object defines a mapping from common names for HTTP statuses +to their numerical codes, accessible either as attributes or as dictionary +items. + +Example:: + + >>> import requests + >>> requests.codes['temporary_redirect'] + 307 + >>> requests.codes.teapot + 418 + >>> requests.codes['\o/'] + 200 + +Some codes have multiple names, and both upper- and lower-case versions of +the names are allowed. For example, ``codes.ok``, ``codes.OK``, and +``codes.okay`` all correspond to the HTTP status code 200. +""" + +from .structures import LookupDict + +_codes = { + # Informational. + 100: ("continue",), + 101: ("switching_protocols",), + 102: ("processing", "early-hints"), + 103: ("checkpoint",), + 122: ("uri_too_long", "request_uri_too_long"), + 200: ("ok", "okay", "all_ok", "all_okay", "all_good", "\\o/", "✓"), + 201: ("created",), + 202: ("accepted",), + 203: ("non_authoritative_info", "non_authoritative_information"), + 204: ("no_content",), + 205: ("reset_content", "reset"), + 206: ("partial_content", "partial"), + 207: ("multi_status", "multiple_status", "multi_stati", "multiple_stati"), + 208: ("already_reported",), + 226: ("im_used",), + # Redirection. + 300: ("multiple_choices",), + 301: ("moved_permanently", "moved", "\\o-"), + 302: ("found",), + 303: ("see_other", "other"), + 304: ("not_modified",), + 305: ("use_proxy",), + 306: ("switch_proxy",), + 307: ("temporary_redirect", "temporary_moved", "temporary"), + 308: ( + "permanent_redirect", + "resume_incomplete", + "resume", + ), # "resume" and "resume_incomplete" to be removed in 3.0 + # Client Error. + 400: ("bad_request", "bad"), + 401: ("unauthorized",), + 402: ("payment_required", "payment"), + 403: ("forbidden",), + 404: ("not_found", "-o-"), + 405: ("method_not_allowed", "not_allowed"), + 406: ("not_acceptable",), + 407: ("proxy_authentication_required", "proxy_auth", "proxy_authentication"), + 408: ("request_timeout", "timeout"), + 409: ("conflict",), + 410: ("gone",), + 411: ("length_required",), + 412: ("precondition_failed", "precondition"), + 413: ("request_entity_too_large", "content_too_large"), + 414: ("request_uri_too_large", "uri_too_long"), + 415: ("unsupported_media_type", "unsupported_media", "media_type"), + 416: ( + "requested_range_not_satisfiable", + "requested_range", + "range_not_satisfiable", + ), + 417: ("expectation_failed",), + 418: ("im_a_teapot", "teapot", "i_am_a_teapot"), + 421: ("misdirected_request",), + 422: ("unprocessable_entity", "unprocessable", "unprocessable_content"), + 423: ("locked",), + 424: ("failed_dependency", "dependency"), + 425: ("unordered_collection", "unordered", "too_early"), + 426: ("upgrade_required", "upgrade"), + 428: ("precondition_required", "precondition"), + 429: ("too_many_requests", "too_many"), + 431: ("header_fields_too_large", "fields_too_large"), + 444: ("no_response", "none"), + 449: ("retry_with", "retry"), + 450: ("blocked_by_windows_parental_controls", "parental_controls"), + 451: ("unavailable_for_legal_reasons", "legal_reasons"), + 499: ("client_closed_request",), + # Server Error. + 500: ("internal_server_error", "server_error", "/o\\", "✗"), + 501: ("not_implemented",), + 502: ("bad_gateway",), + 503: ("service_unavailable", "unavailable"), + 504: ("gateway_timeout",), + 505: ("http_version_not_supported", "http_version"), + 506: ("variant_also_negotiates",), + 507: ("insufficient_storage",), + 509: ("bandwidth_limit_exceeded", "bandwidth"), + 510: ("not_extended",), + 511: ("network_authentication_required", "network_auth", "network_authentication"), +} + +codes: LookupDict[int] = LookupDict(name="status_codes") + + +def _init(): + for code, titles in _codes.items(): + for title in titles: + setattr(codes, title, code) + if not title.startswith(("\\", "/")): + setattr(codes, title.upper(), code) + + def doc(code: int) -> str: + names = ", ".join(f"``{n}``" for n in _codes[code]) + return "* %d: %s" % (code, names) + + global __doc__ + __doc__ = ( + __doc__ + "\n" + "\n".join(doc(code) for code in sorted(_codes)) + if __doc__ is not None + else None + ) + + +_init() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/structures.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/structures.py new file mode 100644 index 0000000..7675eaf --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/structures.py @@ -0,0 +1,130 @@ +""" +requests.structures +~~~~~~~~~~~~~~~~~~~ + +Data structures that power Requests. +""" + +from __future__ import annotations + +from collections import OrderedDict +from collections.abc import Iterable, Iterator, Mapping +from typing import Any, Generic, TypeVar, overload + +from .compat import MutableMapping + +_VT = TypeVar("_VT") +_D = TypeVar("_D") + + +class CaseInsensitiveDict(MutableMapping[str, _VT], Generic[_VT]): + """A case-insensitive ``dict``-like object. + + Implements all methods and operations of + ``MutableMapping`` as well as dict's ``copy``. Also + provides ``lower_items``. + + All keys are expected to be strings. The structure remembers the + case of the last key to be set, and ``iter(instance)``, + ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()`` + will contain case-sensitive keys. However, querying and contains + testing is case insensitive:: + + cid = CaseInsensitiveDict() + cid['Accept'] = 'application/json' + cid['aCCEPT'] == 'application/json' # True + list(cid) == ['Accept'] # True + + For example, ``headers['content-encoding']`` will return the + value of a ``'Content-Encoding'`` response header, regardless + of how the header name was originally stored. + + If the constructor, ``.update``, or equality comparison + operations are given keys that have equal ``.lower()``s, the + behavior is undefined. + """ + + _store: OrderedDict[str, tuple[str, _VT]] + + def __init__( + self, + data: Mapping[str, _VT] | Iterable[tuple[str, _VT]] | None = None, + **kwargs: _VT, + ) -> None: + self._store = OrderedDict() + if data is None: + data = {} + self.update(data, **kwargs) + + def __setitem__(self, key: str, value: _VT) -> None: + # Use the lowercased key for lookups, but store the actual + # key alongside the value. + self._store[key.lower()] = (key, value) + + def __getitem__(self, key: str) -> _VT: + return self._store[key.lower()][1] + + def __delitem__(self, key: str) -> None: + del self._store[key.lower()] + + def __iter__(self) -> Iterator[str]: + return (casedkey for casedkey, _ in self._store.values()) + + def __len__(self) -> int: + return len(self._store) + + def lower_items(self) -> Iterator[tuple[str, _VT]]: + """Like iteritems(), but with all lowercase keys.""" + return ((lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items()) + + def __eq__(self, other: object) -> bool: + if isinstance(other, Mapping): + other_dict: CaseInsensitiveDict[Any] = CaseInsensitiveDict(other) # type: ignore[reportUnknownArgumentType] + else: + return NotImplemented + # Compare insensitively + return dict(self.lower_items()) == dict(other_dict.lower_items()) + + # Copy is required + def copy(self) -> CaseInsensitiveDict[_VT]: + return CaseInsensitiveDict(self._store.values()) + + def __repr__(self) -> str: + return str(dict(self.items())) + + +class LookupDict(dict[str, _VT]): + """Dictionary lookup object.""" + + name: Any + + def __init__(self, name: Any = None) -> None: + self.name = name + super().__init__() + + def __repr__(self) -> str: + return f"" + + def __getattr__(self, key: str) -> _VT | None: + # We need this for type checkers to infer typing + # on attribute access with status_codes.py + if key in self.__dict__: + return self.__dict__[key] + else: + raise AttributeError( + f"'{type(self).__name__}' object has no attribute '{key}'" + ) + + def __getitem__(self, key: str) -> _VT | None: # type: ignore[override] + # We allow fall-through here, so values default to None + + return self.__dict__.get(key, None) + + @overload + def get(self, key: str, default: None = None) -> _VT | None: ... + + @overload + def get(self, key: str, default: _D | _VT) -> _D | _VT: ... + + def get(self, key: str, default: _D | None = None) -> _VT | _D | None: + return self.__dict__.get(key, default) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/utils.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/utils.py new file mode 100644 index 0000000..e2a2a32 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/utils.py @@ -0,0 +1,1155 @@ +""" +requests.utils +~~~~~~~~~~~~~~ + +This module provides utility functions that are used within Requests +that are also useful for external consumption. +""" + +from __future__ import annotations + +import codecs +import contextlib +import io +import os +import re +import socket +import struct +import sys +import tempfile +import warnings +import zipfile +from collections import OrderedDict +from collections.abc import Generator, Iterable +from typing import ( + TYPE_CHECKING, + Any, + Final, + TypeVar, + cast, + overload, +) + +from pip._vendor.urllib3.util import make_headers, parse_url + +from . import certs +from .__version__ import __version__ + +# to_native_string is unused here, but imported here for backwards compatibility +from ._internal_utils import ( # noqa: F401 + _HEADER_VALIDATORS_BYTE, # type: ignore[reportPrivateUsage] + _HEADER_VALIDATORS_STR, # type: ignore[reportPrivateUsage] + HEADER_VALIDATORS, # type: ignore[reportUnusedImport] + to_native_string, # type: ignore[reportUnusedImport] +) +from ._types import SupportsItems as _SupportsItems +from .compat import ( + Mapping, + bytes, + getproxies, + getproxies_environment, + integer_types, + is_urllib3_1, + proxy_bypass, + proxy_bypass_environment, # type: ignore[attr-defined] # https://github.com/python/cpython/issues/145331 + quote, + str, + unquote, + urlparse, + urlunparse, +) +from .compat import parse_http_list as _parse_list_header +from .cookies import cookiejar_from_dict +from .exceptions import ( + FileModeWarning, + InvalidHeader, + InvalidURL, + UnrewindableBodyError, +) +from .structures import CaseInsensitiveDict + +if TYPE_CHECKING: + from http.cookiejar import CookieJar + from io import BufferedWriter + + from . import _types as _t + from .models import PreparedRequest, Request, Response + +NETRC_FILES: Final = (".netrc", "_netrc") + + +# Certificate is extracted by certifi when needed. +DEFAULT_CA_BUNDLE_PATH: str = certs.where() + + +DEFAULT_PORTS: Final = {"http": 80, "https": 443} + +_KT = TypeVar("_KT") +_VT = TypeVar("_VT") + +# Ensure that ', ' is used to preserve previous delimiter behavior. +DEFAULT_ACCEPT_ENCODING: Final = ", ".join( + re.split(r",\s*", make_headers(accept_encoding=True)["accept-encoding"]) +) + + +if sys.platform == "win32": + # provide a proxy_bypass version on Windows without DNS lookups + + def proxy_bypass_registry(host: str) -> bool: + try: + import winreg + except ImportError: + return False + + try: + internetSettings = winreg.OpenKey( + winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Internet Settings", + ) + # ProxyEnable could be REG_SZ or REG_DWORD, normalizing it + proxyEnable = int(winreg.QueryValueEx(internetSettings, "ProxyEnable")[0]) + # ProxyOverride is almost always a string + proxyOverride = winreg.QueryValueEx(internetSettings, "ProxyOverride")[0] + except (OSError, ValueError): + return False + if not proxyEnable or not proxyOverride: + return False + + # make a check value list from the registry entry: replace the + # '' string by the localhost entry and the corresponding + # canonical entry. + proxyOverride = proxyOverride.split(";") + # filter out empty strings to avoid re.match return true in the following code. + proxyOverride = filter(None, proxyOverride) + # now check if we match one of the registry values. + for test in proxyOverride: + if test == "": + if "." not in host: + return True + test = test.replace(".", r"\.") # mask dots + test = test.replace("*", r".*") # change glob sequence + test = test.replace("?", r".") # change glob char + if re.match(test, host, re.I): + return True + return False + + def proxy_bypass(host: str) -> bool: # noqa + """Return True, if the host should be bypassed. + + Checks proxy settings gathered from the environment, if specified, + or the registry. + """ + if getproxies_environment(): + return proxy_bypass_environment(host) + else: + return proxy_bypass_registry(host) + + +def dict_to_sequence( + d: _t.SupportsItems[Any, Any] | Iterable[tuple[Any, Any]], +) -> Iterable[tuple[Any, Any]]: + """Returns an internal sequence dictionary update.""" + + if isinstance(d, _SupportsItems): + return d.items() + + return d + + +def super_len(o: Any) -> int: + total_length = None + current_position = 0 + + if not is_urllib3_1 and isinstance(o, str): + # urllib3 2.x+ treats all strings as utf-8 instead + # of latin-1 (iso-8859-1) like http.client. + o = o.encode("utf-8") + + if hasattr(o, "__len__"): + total_length = len(o) + + elif hasattr(o, "len"): + total_length = o.len + + elif hasattr(o, "fileno"): + try: + fileno = o.fileno() + except (io.UnsupportedOperation, AttributeError): + # AttributeError is a surprising exception, seeing as how we've just checked + # that `hasattr(o, 'fileno')`. It happens for objects obtained via + # `Tarfile.extractfile()`, per issue 5229. + pass + else: + total_length = os.fstat(fileno).st_size + + # Having used fstat to determine the file length, we need to + # confirm that this file was opened up in binary mode. + if "b" not in o.mode: + warnings.warn( + ( + "Requests has determined the content-length for this " + "request using the binary size of the file: however, the " + "file has been opened in text mode (i.e. without the 'b' " + "flag in the mode). This may lead to an incorrect " + "content-length. In Requests 3.0, support will be removed " + "for files in text mode." + ), + FileModeWarning, + ) + + if hasattr(o, "tell"): + try: + current_position = o.tell() + except OSError: + # This can happen in some weird situations, such as when the file + # is actually a special file descriptor like stdin. In this + # instance, we don't know what the length is, so set it to zero and + # let requests chunk it instead. + if total_length is not None: + current_position = total_length + else: + if hasattr(o, "seek") and total_length is None: + # StringIO and BytesIO have seek but no usable fileno + try: + # seek to end of file + o.seek(0, 2) + total_length = o.tell() + + # seek back to current position to support + # partially read file-like objects + o.seek(current_position or 0) + except OSError: + total_length = 0 + + if total_length is None: + total_length = 0 + + return max(0, total_length - current_position) + + +def get_netrc_auth( + url: _t.UriType, raise_errors: bool = False +) -> tuple[str, str] | None: + """Returns the Requests tuple auth for a given url from netrc.""" + + if isinstance(url, bytes): + url = url.decode("utf-8") + + netrc_file = os.environ.get("NETRC") + if netrc_file is not None: + netrc_locations = (netrc_file,) + else: + netrc_locations = (f"~/{f}" for f in NETRC_FILES) + + try: + from netrc import NetrcParseError, netrc + + netrc_path = None + + for f in netrc_locations: + loc = os.path.expanduser(f) + if os.path.exists(loc): + netrc_path = loc + break + + # Abort early if there isn't one. + if netrc_path is None: + return + + ri = urlparse(url) + host = ri.hostname + + if host is None: + return + + try: + _netrc = netrc(netrc_path).authenticators(host) + if _netrc and any(_netrc): + # Return with login / password + login_i = 0 if _netrc[0] else 1 + return (_netrc[login_i] or "", _netrc[2] or "") + except (NetrcParseError, OSError): + # If there was a parsing error or a permissions issue reading the file, + # we'll just skip netrc auth unless explicitly asked to raise errors. + if raise_errors: + raise + + # App Engine hackiness. + except (ImportError, AttributeError): + pass + + +def guess_filename(obj: Any) -> str | None: + """Tries to guess the filename of the given object.""" + name = getattr(obj, "name", None) + if name and isinstance(name, (str, bytes)) and name[0] != "<" and name[-1] != ">": + return os.path.basename(name) # type: ignore[return-value] # urllib3 accepts bytes but types str only + + +def extract_zipped_paths(path: str) -> str: + """Replace nonexistent paths that look like they refer to a member of a zip + archive with the location of an extracted copy of the target, or else + just return the provided path unchanged. + """ + if os.path.exists(path): + # this is already a valid path, no need to do anything further + return path + + # find the first valid part of the provided path and treat that as a zip archive + # assume the rest of the path is the name of a member in the archive + archive, member = os.path.split(path) + while archive and not os.path.exists(archive): + archive, prefix = os.path.split(archive) + if not prefix: + # If we don't check for an empty prefix after the split (in other words, archive remains unchanged after the split), + # we _can_ end up in an infinite loop on a rare corner case affecting a small number of users + break + member = "/".join([prefix, member]) + + if not zipfile.is_zipfile(archive): + return path + + zip_file = zipfile.ZipFile(archive) + if member not in zip_file.namelist(): + return path + + # we have a valid zip archive and a valid member of that archive + suffix = os.path.splitext(member.split("/")[-1])[-1] + fd, extracted_path = tempfile.mkstemp(suffix=suffix) + try: + os.write(fd, zip_file.read(member)) + finally: + os.close(fd) + + return extracted_path + + +@contextlib.contextmanager +def atomic_open(filename: str) -> Generator[BufferedWriter, None, None]: + """Write a file to the disk in an atomic fashion""" + tmp_descriptor, tmp_name = tempfile.mkstemp(dir=os.path.dirname(filename)) + try: + with os.fdopen(tmp_descriptor, "wb") as tmp_handler: + yield tmp_handler + os.replace(tmp_name, filename) + except BaseException: + os.remove(tmp_name) + raise + + +def from_key_val_list( + value: Mapping[Any, Any] | Iterable[tuple[Any, Any]] | None, +) -> dict[Any, Any] | None: + """Take an object and test to see if it can be represented as a + dictionary. Unless it can not be represented as such, return an + OrderedDict, e.g., + + :: + + >>> from_key_val_list([('key', 'val')]) + OrderedDict([('key', 'val')]) + >>> from_key_val_list('string') + Traceback (most recent call last): + ... + ValueError: cannot encode objects that are not 2-tuples + >>> from_key_val_list({'key': 'val'}) + OrderedDict([('key', 'val')]) + + :rtype: OrderedDict + """ + if value is None: + return None + + if isinstance(value, (str, bytes, bool, int)): + raise ValueError("cannot encode objects that are not 2-tuples") + + return OrderedDict(value) + + +@overload +def to_key_val_list(value: None) -> None: ... +@overload +def to_key_val_list( + value: _t.SupportsItems[_KT, _VT] | Iterable[tuple[_KT, _VT]], +) -> list[tuple[_KT, _VT]]: ... +def to_key_val_list( + value: _t.SupportsItems[_KT, _VT] | Iterable[tuple[_KT, _VT]] | None, +) -> list[tuple[_KT, _VT]] | None: + """Take an object and test to see if it can be represented as a + dictionary. If it can be, return a list of tuples, e.g., + + :: + + >>> to_key_val_list([('key', 'val')]) + [('key', 'val')] + >>> to_key_val_list({'key': 'val'}) + [('key', 'val')] + >>> to_key_val_list('string') + Traceback (most recent call last): + ... + ValueError: cannot encode objects that are not 2-tuples + + :rtype: list + """ + if value is None: + return None + + if isinstance(value, (str, bytes, bool, int)): + raise ValueError("cannot encode objects that are not 2-tuples") + + if isinstance(value, _SupportsItems): + return list(value.items()) + + return list(value) + + +# From mitsuhiko/werkzeug (used with permission). +def parse_list_header(value: str) -> list[str]: + """Parse lists as described by RFC 2068 Section 2. + + In particular, parse comma-separated lists where the elements of + the list may include quoted-strings. A quoted-string could + contain a comma. A non-quoted string could have quotes in the + middle. Quotes are removed automatically after parsing. + + It basically works like :func:`parse_set_header` just that items + may appear multiple times and case sensitivity is preserved. + + The return value is a standard :class:`list`: + + >>> parse_list_header('token, "quoted value"') + ['token', 'quoted value'] + + To create a header from the :class:`list` again, use the + :func:`dump_header` function. + + :param value: a string with a list header. + :return: :class:`list` + :rtype: list + """ + result: list[str] = [] + for item in _parse_list_header(value): + if item[:1] == item[-1:] == '"': + item = unquote_header_value(item[1:-1]) + result.append(item) + return result + + +# From mitsuhiko/werkzeug (used with permission). +def parse_dict_header(value: str) -> dict[str, str | None]: + """Parse lists of key, value pairs as described by RFC 2068 Section 2 and + convert them into a python dict: + + >>> d = parse_dict_header('foo="is a fish", bar="as well"') + >>> type(d) is dict + True + >>> sorted(d.items()) + [('bar', 'as well'), ('foo', 'is a fish')] + + If there is no value for a key it will be `None`: + + >>> parse_dict_header('key_without_value') + {'key_without_value': None} + + To create a header from the :class:`dict` again, use the + :func:`dump_header` function. + + :param value: a string with a dict header. + :return: :class:`dict` + :rtype: dict + """ + result: dict[str, str | None] = {} + for item in _parse_list_header(value): + if "=" not in item: + result[item] = None + continue + name, value = item.split("=", 1) + if value[:1] == value[-1:] == '"': + value = unquote_header_value(value[1:-1]) + result[name] = value + return result + + +# From mitsuhiko/werkzeug (used with permission). +def unquote_header_value(value: str, is_filename: bool = False) -> str: + r"""Unquotes a header value. (Reversal of :func:`quote_header_value`). + This does not use the real unquoting but what browsers are actually + using for quoting. + + :param value: the header value to unquote. + :rtype: str + """ + if value and value[0] == value[-1] == '"': + # this is not the real unquoting, but fixing this so that the + # RFC is met will result in bugs with internet explorer and + # probably some other browsers as well. IE for example is + # uploading files with "C:\foo\bar.txt" as filename + value = value[1:-1] + + # if this is a filename and the starting characters look like + # a UNC path, then just return the value without quotes. Using the + # replace sequence below on a UNC path has the effect of turning + # the leading double slash into a single slash and then + # _fix_ie_filename() doesn't work correctly. See #458. + if not is_filename or value[:2] != "\\\\": + return value.replace("\\\\", "\\").replace('\\"', '"') + return value + + +def dict_from_cookiejar(cj: CookieJar) -> dict[str, str | None]: + """Returns a key/value dictionary from a CookieJar. + + :param cj: CookieJar object to extract cookies from. + :rtype: dict + """ + + cookie_dict = {cookie.name: cookie.value for cookie in cj} + return cookie_dict + + +def add_dict_to_cookiejar(cj: CookieJar, cookie_dict: dict[str, str]) -> CookieJar: + """Returns a CookieJar from a key/value dictionary. + + :param cj: CookieJar to insert cookies into. + :param cookie_dict: Dict of key/values to insert into CookieJar. + :rtype: CookieJar + """ + + return cookiejar_from_dict(cookie_dict, cj) + + +def get_encodings_from_content(content: str) -> list[str]: + """Returns encodings from given content string. + + :param content: bytestring to extract encodings from. + """ + warnings.warn( + ( + "In requests 3.0, get_encodings_from_content will be removed. For " + "more information, please see the discussion on issue #2266. (This" + " warning should only appear once.)" + ), + DeprecationWarning, + ) + + charset_re = re.compile(r']', flags=re.I) + pragma_re = re.compile(r']', flags=re.I) + xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]') + + return ( + charset_re.findall(content) + + pragma_re.findall(content) + + xml_re.findall(content) + ) + + +def _parse_content_type_header(header: str) -> tuple[str, dict[str, Any]]: + """Returns content type and parameters from given header. + + :param header: string + :return: tuple containing content type and dictionary of + parameters. + """ + + tokens = header.split(";") + content_type, params = tokens[0].strip(), tokens[1:] + params_dict: dict[str, str | bool] = {} + strip_chars = "\"' " + + for param in params: + param = param.strip() + if param and (idx := param.find("=")) != -1: + key = param[:idx].strip(strip_chars) + value = param[idx + 1 :].strip(strip_chars) + params_dict[key.lower()] = value + return content_type, params_dict + + +def get_encoding_from_headers(headers: CaseInsensitiveDict[str]) -> str | None: + """Returns encodings from given HTTP Header Dict. + + :param headers: dictionary to extract encoding from. + :rtype: str + """ + + content_type = headers.get("content-type") + + if not content_type: + return None + + content_type, params = _parse_content_type_header(content_type) + + if "charset" in params: + return params["charset"].strip("'\"") + + if "text" in content_type: + return "ISO-8859-1" + + if "application/json" in content_type: + # Assume UTF-8 based on RFC 4627: https://www.ietf.org/rfc/rfc4627.txt since the charset was unset + return "utf-8" + + +def stream_decode_response_unicode( + iterator: Iterable[bytes], r: Response +) -> Generator[str | bytes, None, None]: + """Stream decodes an iterator.""" + + if r.encoding is None: + yield from iterator + return + + decoder = codecs.getincrementaldecoder(r.encoding)(errors="replace") + for chunk in iterator: + rv = decoder.decode(chunk) + if rv: + yield rv + rv = decoder.decode(b"", final=True) + if rv: + yield rv + + +@overload +def iter_slices( + string: bytes, slice_length: int | None +) -> Generator[bytes, None, None]: ... +@overload +def iter_slices( + string: str, slice_length: int | None +) -> Generator[str, None, None]: ... +def iter_slices( + string: bytes | str, slice_length: int | None +) -> Generator[bytes | str, None, None]: + """Iterate over slices of a string.""" + pos = 0 + if slice_length is None or slice_length <= 0: + slice_length = len(string) + while pos < len(string): + yield string[pos : pos + slice_length] + pos += slice_length + + +def get_unicode_from_response(r: Response) -> str | bytes | None: + """Returns the requested content back in unicode. + + :param r: Response object to get unicode content from. + + Tried: + + 1. charset from content-type + 2. fall back and replace all unicode characters + + :rtype: str + """ + warnings.warn( + ( + "In requests 3.0, get_unicode_from_response will be removed. For " + "more information, please see the discussion on issue #2266. (This" + " warning should only appear once.)" + ), + DeprecationWarning, + ) + if r.content is None: # type: ignore[reportUnnecessaryComparison] + return None + + tried_encodings: list[str] = [] + + # Try charset from content-type + encoding = get_encoding_from_headers(r.headers) + + if encoding: + try: + return str(r.content, encoding) + except UnicodeError: + tried_encodings.append(encoding) + + # Fall back: + try: + return str(r.content, encoding or "utf-8", errors="replace") + except TypeError: + return r.content + + +# The unreserved URI characters (RFC 3986) +UNRESERVED_SET: Final = frozenset( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789-._~" +) + + +def unquote_unreserved(uri: str) -> str: + """Un-escape any percent-escape sequences in a URI that are unreserved + characters. This leaves all reserved, illegal and non-ASCII bytes encoded. + + :rtype: str + """ + parts = uri.split("%") + for i in range(1, len(parts)): + h = parts[i][0:2] + if len(h) == 2 and h.isalnum(): + try: + c = chr(int(h, 16)) + except ValueError: + raise InvalidURL(f"Invalid percent-escape sequence: '{h}'") + + if c in UNRESERVED_SET: + parts[i] = c + parts[i][2:] + else: + parts[i] = f"%{parts[i]}" + else: + parts[i] = f"%{parts[i]}" + return "".join(parts) + + +def requote_uri(uri: str) -> str: + """Re-quote the given URI. + + This function passes the given URI through an unquote/quote cycle to + ensure that it is fully and consistently quoted. + + :rtype: str + """ + safe_with_percent = "!#$%&'()*+,/:;=?@[]~" + safe_without_percent = "!#$&'()*+,/:;=?@[]~" + try: + # Unquote only the unreserved characters + # Then quote only illegal characters (do not quote reserved, + # unreserved, or '%') + return quote(unquote_unreserved(uri), safe=safe_with_percent) + except InvalidURL: + # We couldn't unquote the given URI, so let's try quoting it, but + # there may be unquoted '%'s in the URI. We need to make sure they're + # properly quoted so they do not cause issues elsewhere. + return quote(uri, safe=safe_without_percent) + + +def address_in_network(ip: str, net: str) -> bool: + """This function allows you to check if an IP belongs to a network subnet + + Example: returns True if ip = 192.168.1.1 and net = 192.168.1.0/24 + returns False if ip = 192.168.1.1 and net = 192.168.100.0/24 + + :rtype: bool + """ + ipaddr = struct.unpack("=L", socket.inet_aton(ip))[0] + netaddr, bits = net.split("/") + netmask = struct.unpack("=L", socket.inet_aton(dotted_netmask(int(bits))))[0] + network = struct.unpack("=L", socket.inet_aton(netaddr))[0] & netmask + return (ipaddr & netmask) == (network & netmask) + + +def dotted_netmask(mask: int) -> str: + """Converts mask from /xx format to xxx.xxx.xxx.xxx + + Example: if mask is 24 function returns 255.255.255.0 + + :rtype: str + """ + bits = 0xFFFFFFFF ^ (1 << 32 - mask) - 1 + return socket.inet_ntoa(struct.pack(">I", bits)) + + +def is_ipv4_address(string_ip: str) -> bool: + """ + :rtype: bool + """ + try: + socket.inet_aton(string_ip) + except OSError: + return False + return True + + +def is_valid_cidr(string_network: str) -> bool: + """ + Very simple check of the cidr format in no_proxy variable. + + :rtype: bool + """ + if string_network.count("/") == 1: + try: + mask = int(string_network.split("/")[1]) + except ValueError: + return False + + if mask < 1 or mask > 32: + return False + + try: + socket.inet_aton(string_network.split("/")[0]) + except OSError: + return False + else: + return False + return True + + +@contextlib.contextmanager +def set_environ(env_name: str, value: str | None) -> Generator[None, None, None]: + """Set the environment variable 'env_name' to 'value' + + Save previous value, yield, and then restore the previous value stored in + the environment variable 'env_name'. + + If 'value' is None, do nothing""" + value_changed = value is not None + old_value: str | None = None + if value_changed: + old_value = os.environ.get(env_name) + os.environ[env_name] = value + try: + yield + finally: + if value_changed: + if old_value is None: + del os.environ[env_name] + else: + os.environ[env_name] = old_value + + +def should_bypass_proxies(url: str, no_proxy: str | None) -> bool: + """ + Returns whether we should bypass proxies or not. + + :rtype: bool + """ + + # Prioritize lowercase environment variables over uppercase + # to keep a consistent behaviour with other http projects (curl, wget). + def get_proxy(key: str) -> str | None: + return os.environ.get(key) or os.environ.get(key.upper()) + + # First check whether no_proxy is defined. If it is, check that the URL + # we're getting isn't in the no_proxy list. + no_proxy_arg = no_proxy + if no_proxy is None: + no_proxy = get_proxy("no_proxy") + parsed = urlparse(url) + hostname = parsed.hostname + + if hostname is None: + # URLs don't always have hostnames, e.g. file:/// urls. + return True + + if no_proxy: + # We need to check whether we match here. We need to see if we match + # the end of the hostname, both with and without the port. + no_proxy_hosts = (host for host in no_proxy.replace(" ", "").split(",") if host) + + if is_ipv4_address(hostname): + for proxy_ip in no_proxy_hosts: + if is_valid_cidr(proxy_ip): + if address_in_network(hostname, proxy_ip): + return True + elif hostname == proxy_ip: + # If no_proxy ip was defined in plain IP notation instead of cidr notation & + # matches the IP of the index + return True + else: + host_with_port = hostname + if parsed.port: + host_with_port += f":{parsed.port}" + + for host in no_proxy_hosts: + host = host.lstrip(".") + if hostname == host or host_with_port == host: + return True + host = "." + host + if hostname.endswith(host) or host_with_port.endswith(host): + return True + + with set_environ("no_proxy", no_proxy_arg): + try: + bypass = proxy_bypass(hostname) + except (TypeError, socket.gaierror): + bypass = False + + if bypass: + return True + + return False + + +def get_environ_proxies(url: str, no_proxy: str | None = None) -> dict[str, str]: + """ + Return a dict of environment proxies. + + :rtype: dict + """ + if should_bypass_proxies(url, no_proxy=no_proxy): + return {} + else: + return getproxies() + + +def select_proxy(url: str, proxies: dict[str, str] | None) -> str | None: + """Select a proxy for the url, if applicable. + + :param url: The url being for the request + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs + """ + proxies = proxies or {} + urlparts = urlparse(url) + if urlparts.hostname is None: + return proxies.get(urlparts.scheme, proxies.get("all")) + + proxy_keys = [ + urlparts.scheme + "://" + urlparts.hostname, + urlparts.scheme, + "all://" + urlparts.hostname, + "all", + ] + proxy = None + for proxy_key in proxy_keys: + if proxy_key in proxies: + proxy = proxies[proxy_key] + break + + return proxy + + +def resolve_proxies( + request: Request | PreparedRequest, + proxies: dict[str, str] | None, + trust_env: bool = True, +) -> dict[str, str]: + """This method takes proxy information from a request and configuration + input to resolve a mapping of target proxies. This will consider settings + such as NO_PROXY to strip proxy configurations. + + :param request: Request or PreparedRequest + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs + :param trust_env: Boolean declaring whether to trust environment configs + + :rtype: dict + """ + proxies = proxies if proxies is not None else {} + url = cast(str, request.url) + scheme = urlparse(url).scheme + no_proxy = proxies.get("no_proxy") + new_proxies = proxies.copy() + + if trust_env and not should_bypass_proxies(url, no_proxy=no_proxy): + environ_proxies = get_environ_proxies(url, no_proxy=no_proxy) + + proxy = environ_proxies.get(scheme, environ_proxies.get("all")) + + if proxy: + new_proxies.setdefault(scheme, proxy) + return new_proxies + + +def default_user_agent(name: str = "python-requests") -> str: + """ + Return a string representing the default user agent. + + :rtype: str + """ + return f"{name}/{__version__}" + + +def default_headers() -> CaseInsensitiveDict[str]: + """ + :rtype: requests.structures.CaseInsensitiveDict + """ + return CaseInsensitiveDict( + { + "User-Agent": default_user_agent(), + "Accept-Encoding": DEFAULT_ACCEPT_ENCODING, + "Accept": "*/*", + "Connection": "keep-alive", + } + ) + + +def parse_header_links(value: str) -> list[dict[str, str]]: + """Return a list of parsed link headers proxies. + + i.e. Link: ; rel=front; type="image/jpeg",; rel=back;type="image/jpeg" + + :rtype: list + """ + + links: list[dict[str, str]] = [] + + replace_chars = " '\"" + + value = value.strip(replace_chars) + if not value: + return links + + for val in re.split(", *<", value): + try: + url, params = val.split(";", 1) + except ValueError: + url, params = val, "" + + link: dict[str, str] = {"url": url.strip("<> '\"")} + + for param in params.split(";"): + try: + key, value = param.split("=") + except ValueError: + break + + link[key.strip(replace_chars)] = value.strip(replace_chars) + + links.append(link) + + return links + + +# Null bytes; no need to recreate these on each call to guess_json_utf +_null = "\x00".encode("ascii") # encoding to ASCII for Python 3 +_null2 = _null * 2 +_null3 = _null * 3 + + +def guess_json_utf(data: bytes) -> str | None: + """ + :rtype: str + """ + # JSON always starts with two ASCII characters, so detection is as + # easy as counting the nulls and from their location and count + # determine the encoding. Also detect a BOM, if present. + sample = data[:4] + if sample in (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE): + return "utf-32" # BOM included + if sample[:3] == codecs.BOM_UTF8: + return "utf-8-sig" # BOM included, MS style (discouraged) + if sample[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE): + return "utf-16" # BOM included + nullcount = sample.count(_null) + if nullcount == 0: + return "utf-8" + if nullcount == 2: + if sample[::2] == _null2: # 1st and 3rd are null + return "utf-16-be" + if sample[1::2] == _null2: # 2nd and 4th are null + return "utf-16-le" + # Did not detect 2 valid UTF-16 ascii-range characters + if nullcount == 3: + if sample[:3] == _null3: + return "utf-32-be" + if sample[1:] == _null3: + return "utf-32-le" + # Did not detect a valid UTF-32 ascii-range character + return None + + +def prepend_scheme_if_needed(url: str, new_scheme: str) -> str: + """Given a URL that may or may not have a scheme, prepend the given scheme. + Does not replace a present scheme with the one provided as an argument. + + :rtype: str + """ + parsed = parse_url(url) + scheme, auth, _host, _port, path, query, fragment = parsed + + # A defect in urlparse determines that there isn't a netloc present in some + # urls. We previously assumed parsing was overly cautious, and swapped the + # netloc and path. Due to a lack of tests on the original defect, this is + # maintained with parse_url for backwards compatibility. + netloc = parsed.netloc + if not netloc: + netloc, path = path, netloc + + if auth: + # parse_url doesn't provide the netloc with auth + # so we'll add it ourselves. + netloc = cast(str, netloc) + netloc = "@".join([auth, netloc]) + if scheme is None: + scheme = new_scheme + if path is None: + path = "" + + return urlunparse((scheme, netloc, path, "", query, fragment)) + + +def get_auth_from_url(url: str) -> tuple[str, str]: + """Given a url with authentication components, extract them into a tuple of + username,password. + + :rtype: (str,str) + """ + parsed = urlparse(url) + + try: + # except handles parsed.username/password being None + auth = (unquote(parsed.username), unquote(parsed.password)) # type: ignore[arg-type] + except (AttributeError, TypeError): + auth = ("", "") + + return auth + + +def check_header_validity(header: tuple[str | bytes, str | bytes]) -> None: + """Verifies that header parts don't contain leading whitespace + reserved characters, or return characters. + + :param header: tuple, in the format (name, value). + """ + name, value = header + _validate_header_part(header, name, 0) + _validate_header_part(header, value, 1) + + +def _validate_header_part( + header: tuple[str | bytes, str | bytes], + header_part: str | bytes, + header_validator_index: int, +) -> None: + if isinstance(header_part, str): + validator = _HEADER_VALIDATORS_STR[header_validator_index] + elif isinstance(header_part, bytes): # type: ignore[reportUnnecessaryIsInstance] + # runtime guard for non-str/bytes input + validator = _HEADER_VALIDATORS_BYTE[header_validator_index] + else: + raise InvalidHeader( + f"Header part ({header_part!r}) from {header} " + f"must be of type str or bytes, not {type(header_part)}" + ) + + if not validator.match(header_part): # type: ignore[arg-type] + header_kind = "name" if header_validator_index == 0 else "value" + raise InvalidHeader( + f"Invalid leading whitespace, reserved character(s), or return " + f"character(s) in header {header_kind}: {header_part!r}" + ) + + +def urldefragauth(url: str) -> str: + """ + Given a url remove the fragment and the authentication part. + + :rtype: str + """ + scheme, netloc, path, params, query, _fragment = urlparse(url) + + # see func:`prepend_scheme_if_needed` + if not netloc: + netloc, path = path, netloc + + netloc = netloc.rsplit("@", 1)[-1] + + return urlunparse((scheme, netloc, path, params, query, "")) + + +def rewind_body(prepared_request: PreparedRequest) -> None: + """Move file pointer back to its recorded starting position + so it can be read again on redirect. + """ + body_seek = getattr(prepared_request.body, "seek", None) + if body_seek is not None and isinstance( + prepared_request._body_position, # type: ignore[reportPrivateUsage] + integer_types, + ): + try: + body_seek(prepared_request._body_position) # type: ignore[reportPrivateUsage] + except OSError: + raise UnrewindableBodyError( + "An error occurred when rewinding request body for redirect." + ) + else: + raise UnrewindableBodyError("Unable to rewind request body for redirect.") diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/LICENSE new file mode 100644 index 0000000..b907776 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2018, Tzu-ping Chung + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__init__.py new file mode 100644 index 0000000..4c7f815 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__init__.py @@ -0,0 +1,27 @@ +__all__ = [ + "AbstractProvider", + "AbstractResolver", + "BaseReporter", + "InconsistentCandidate", + "RequirementsConflicted", + "ResolutionError", + "ResolutionImpossible", + "ResolutionTooDeep", + "Resolver", + "__version__", +] + +__version__ = "1.2.1" + + +from .providers import AbstractProvider +from .reporters import BaseReporter +from .resolvers import ( + AbstractResolver, + InconsistentCandidate, + RequirementsConflicted, + ResolutionError, + ResolutionImpossible, + ResolutionTooDeep, + Resolver, +) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2b359a8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-312.pyc new file mode 100644 index 0000000..a3e8ebe Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-312.pyc new file mode 100644 index 0000000..73e9954 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-312.pyc new file mode 100644 index 0000000..4524f39 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/providers.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/providers.py new file mode 100644 index 0000000..524e3d8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/providers.py @@ -0,0 +1,196 @@ +from __future__ import annotations + +from typing import ( + TYPE_CHECKING, + Generic, + Iterable, + Iterator, + Mapping, + Sequence, +) + +from .structs import CT, KT, RT, Matches, RequirementInformation + +if TYPE_CHECKING: + from typing import Any, Protocol + + class Preference(Protocol): + def __lt__(self, __other: Any) -> bool: ... + + +class AbstractProvider(Generic[RT, CT, KT]): + """Delegate class to provide the required interface for the resolver.""" + + def identify(self, requirement_or_candidate: RT | CT) -> KT: + """Given a requirement or candidate, return an identifier for it. + + This is used to identify, e.g. whether two requirements + should have their specifier parts merged or a candidate matches a + requirement via ``find_matches()``. + """ + raise NotImplementedError + + def get_preference( + self, + identifier: KT, + resolutions: Mapping[KT, CT], + candidates: Mapping[KT, Iterator[CT]], + information: Mapping[KT, Iterator[RequirementInformation[RT, CT]]], + backtrack_causes: Sequence[RequirementInformation[RT, CT]], + ) -> Preference: + """Produce a sort key for given requirement based on preference. + + As this is a sort key it will be called O(n) times per backtrack + step, where n is the number of `identifier`s, if you have a check + which is expensive in some sense. E.g. It needs to make O(n) checks + per call or takes significant wall clock time, consider using + `narrow_requirement_selection` to filter the `identifier`s, which + is applied before this sort key is called. + + The preference is defined as "I think this requirement should be + resolved first". The lower the return value is, the more preferred + this group of arguments is. + + :param identifier: An identifier as returned by ``identify()``. This + identifies the requirement being considered. + :param resolutions: Mapping of candidates currently pinned by the + resolver. Each key is an identifier, and the value is a candidate. + The candidate may conflict with requirements from ``information``. + :param candidates: Mapping of each dependency's possible candidates. + Each value is an iterator of candidates. + :param information: Mapping of requirement information of each package. + Each value is an iterator of *requirement information*. + :param backtrack_causes: Sequence of *requirement information* that are + the requirements that caused the resolver to most recently + backtrack. + + A *requirement information* instance is a named tuple with two members: + + * ``requirement`` specifies a requirement contributing to the current + list of candidates. + * ``parent`` specifies the candidate that provides (depended on) the + requirement, or ``None`` to indicate a root requirement. + + The preference could depend on various issues, including (not + necessarily in this order): + + * Is this package pinned in the current resolution result? + * How relaxed is the requirement? Stricter ones should probably be + worked on first? (I don't know, actually.) + * How many possibilities are there to satisfy this requirement? Those + with few left should likely be worked on first, I guess? + * Are there any known conflicts for this requirement? We should + probably work on those with the most known conflicts. + + A sortable value should be returned (this will be used as the ``key`` + parameter of the built-in sorting function). The smaller the value is, + the more preferred this requirement is (i.e. the sorting function + is called with ``reverse=False``). + """ + raise NotImplementedError + + def find_matches( + self, + identifier: KT, + requirements: Mapping[KT, Iterator[RT]], + incompatibilities: Mapping[KT, Iterator[CT]], + ) -> Matches[CT]: + """Find all possible candidates that satisfy the given constraints. + + :param identifier: An identifier as returned by ``identify()``. All + candidates returned by this method should produce the same + identifier. + :param requirements: A mapping of requirements that all returned + candidates must satisfy. Each key is an identifier, and the value + an iterator of requirements for that dependency. + :param incompatibilities: A mapping of known incompatibile candidates of + each dependency. Each key is an identifier, and the value an + iterator of incompatibilities known to the resolver. All + incompatibilities *must* be excluded from the return value. + + This should try to get candidates based on the requirements' types. + For VCS, local, and archive requirements, the one-and-only match is + returned, and for a "named" requirement, the index(es) should be + consulted to find concrete candidates for this requirement. + + The return value should produce candidates ordered by preference; the + most preferred candidate should come first. The return type may be one + of the following: + + * A callable that returns an iterator that yields candidates. + * An collection of candidates. + * An iterable of candidates. This will be consumed immediately into a + list of candidates. + """ + raise NotImplementedError + + def is_satisfied_by(self, requirement: RT, candidate: CT) -> bool: + """Whether the given requirement can be satisfied by a candidate. + + The candidate is guaranteed to have been generated from the + requirement. + + A boolean should be returned to indicate whether ``candidate`` is a + viable solution to the requirement. + """ + raise NotImplementedError + + def get_dependencies(self, candidate: CT) -> Iterable[RT]: + """Get dependencies of a candidate. + + This should return a collection of requirements that `candidate` + specifies as its dependencies. + """ + raise NotImplementedError + + def narrow_requirement_selection( + self, + identifiers: Iterable[KT], + resolutions: Mapping[KT, CT], + candidates: Mapping[KT, Iterator[CT]], + information: Mapping[KT, Iterator[RequirementInformation[RT, CT]]], + backtrack_causes: Sequence[RequirementInformation[RT, CT]], + ) -> Iterable[KT]: + """ + An optional method to narrow the selection of requirements being + considered during resolution. This method is called O(1) time per + backtrack step. + + :param identifiers: An iterable of `identifiers` as returned by + ``identify()``. These identify all requirements currently being + considered. + :param resolutions: A mapping of candidates currently pinned by the + resolver. Each key is an identifier, and the value is a candidate + that may conflict with requirements from ``information``. + :param candidates: A mapping of each dependency's possible candidates. + Each value is an iterator of candidates. + :param information: A mapping of requirement information for each package. + Each value is an iterator of *requirement information*. + :param backtrack_causes: A sequence of *requirement information* that are + the requirements causing the resolver to most recently + backtrack. + + A *requirement information* instance is a named tuple with two members: + + * ``requirement`` specifies a requirement contributing to the current + list of candidates. + * ``parent`` specifies the candidate that provides (is depended on for) + the requirement, or ``None`` to indicate a root requirement. + + Must return a non-empty subset of `identifiers`, with the default + implementation being to return `identifiers` unchanged. Those `identifiers` + will then be passed to the sort key `get_preference` to pick the most + prefered requirement to attempt to pin, unless `narrow_requirement_selection` + returns only 1 requirement, in which case that will be used without + calling the sort key `get_preference`. + + This method is designed to be used by the provider to optimize the + dependency resolution, e.g. if a check cost is O(m) and it can be done + against all identifiers at once then filtering the requirement selection + here will cost O(m) but making it part of the sort key in `get_preference` + will cost O(m*n), where n is the number of `identifiers`. + + Returns: + Iterable[KT]: A non-empty subset of `identifiers`. + """ + return identifiers diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/reporters.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/reporters.py new file mode 100644 index 0000000..6c14220 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/reporters.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Collection, Generic + +from .structs import CT, KT, RT, RequirementInformation, State + +if TYPE_CHECKING: + from .resolvers import Criterion + + +class BaseReporter(Generic[RT, CT, KT]): + """Delegate class to provide progress reporting for the resolver.""" + + def starting(self) -> None: + """Called before the resolution actually starts.""" + + def starting_round(self, index: int) -> None: + """Called before each round of resolution starts. + + The index is zero-based. + """ + + def ending_round(self, index: int, state: State[RT, CT, KT]) -> None: + """Called before each round of resolution ends. + + This is NOT called if the resolution ends at this round. Use `ending` + if you want to report finalization. The index is zero-based. + """ + + def ending(self, state: State[RT, CT, KT]) -> None: + """Called before the resolution ends successfully.""" + + def adding_requirement(self, requirement: RT, parent: CT | None) -> None: + """Called when adding a new requirement into the resolve criteria. + + :param requirement: The additional requirement to be applied to filter + the available candidaites. + :param parent: The candidate that requires ``requirement`` as a + dependency, or None if ``requirement`` is one of the root + requirements passed in from ``Resolver.resolve()``. + """ + + def resolving_conflicts( + self, causes: Collection[RequirementInformation[RT, CT]] + ) -> None: + """Called when starting to attempt requirement conflict resolution. + + :param causes: The information on the collision that caused the backtracking. + """ + + def rejecting_candidate(self, criterion: Criterion[RT, CT], candidate: CT) -> None: + """Called when rejecting a candidate during backtracking.""" + + def pinning(self, candidate: CT) -> None: + """Called when adding a candidate to the potential solution.""" diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__init__.py new file mode 100644 index 0000000..b249221 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__init__.py @@ -0,0 +1,27 @@ +from ..structs import RequirementInformation +from .abstract import AbstractResolver, Result +from .criterion import Criterion +from .exceptions import ( + InconsistentCandidate, + RequirementsConflicted, + ResolutionError, + ResolutionImpossible, + ResolutionTooDeep, + ResolverException, +) +from .resolution import Resolution, Resolver + +__all__ = [ + "AbstractResolver", + "Criterion", + "InconsistentCandidate", + "RequirementInformation", + "RequirementsConflicted", + "Resolution", + "ResolutionError", + "ResolutionImpossible", + "ResolutionTooDeep", + "Resolver", + "ResolverException", + "Result", +] diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5465bc9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/abstract.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/abstract.cpython-312.pyc new file mode 100644 index 0000000..c94b52f Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/abstract.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/criterion.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/criterion.cpython-312.pyc new file mode 100644 index 0000000..9dff0dc Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/criterion.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..a298b2d Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/resolution.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/resolution.cpython-312.pyc new file mode 100644 index 0000000..f3436ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__pycache__/resolution.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/abstract.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/abstract.py new file mode 100644 index 0000000..db32b3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/abstract.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +import collections +from typing import TYPE_CHECKING, Any, Generic, Iterable, NamedTuple + +from ..structs import CT, KT, RT, DirectedGraph + +if TYPE_CHECKING: + from ..providers import AbstractProvider + from ..reporters import BaseReporter + from .criterion import Criterion + + class Result(NamedTuple, Generic[RT, CT, KT]): + mapping: dict[KT, CT] + graph: DirectedGraph[KT | None] + criteria: dict[KT, Criterion[RT, CT]] + +else: + Result = collections.namedtuple("Result", ["mapping", "graph", "criteria"]) + + +class AbstractResolver(Generic[RT, CT, KT]): + """The thing that performs the actual resolution work.""" + + base_exception = Exception + + def __init__( + self, + provider: AbstractProvider[RT, CT, KT], + reporter: BaseReporter[RT, CT, KT], + ) -> None: + self.provider = provider + self.reporter = reporter + + def resolve(self, requirements: Iterable[RT], **kwargs: Any) -> Result[RT, CT, KT]: + """Take a collection of constraints, spit out the resolution result. + + This returns a representation of the final resolution state, with one + guarenteed attribute ``mapping`` that contains resolved candidates as + values. The keys are their respective identifiers. + + :param requirements: A collection of constraints. + :param kwargs: Additional keyword arguments that subclasses may accept. + + :raises: ``self.base_exception`` or its subclass. + """ + raise NotImplementedError diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/criterion.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/criterion.py new file mode 100644 index 0000000..ee5019c --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/criterion.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from typing import Collection, Generic, Iterable, Iterator + +from ..structs import CT, RT, RequirementInformation + + +class Criterion(Generic[RT, CT]): + """Representation of possible resolution results of a package. + + This holds three attributes: + + * `information` is a collection of `RequirementInformation` pairs. + Each pair is a requirement contributing to this criterion, and the + candidate that provides the requirement. + * `incompatibilities` is a collection of all known not-to-work candidates + to exclude from consideration. + * `candidates` is a collection containing all possible candidates deducted + from the union of contributing requirements and known incompatibilities. + It should never be empty, except when the criterion is an attribute of a + raised `RequirementsConflicted` (in which case it is always empty). + + .. note:: + This class is intended to be externally immutable. **Do not** mutate + any of its attribute containers. + """ + + def __init__( + self, + candidates: Iterable[CT], + information: Collection[RequirementInformation[RT, CT]], + incompatibilities: Collection[CT], + ) -> None: + self.candidates = candidates + self.information = information + self.incompatibilities = incompatibilities + + def __repr__(self) -> str: + requirements = ", ".join( + f"({req!r}, via={parent!r})" for req, parent in self.information + ) + return f"Criterion({requirements})" + + def iter_requirement(self) -> Iterator[RT]: + return (i.requirement for i in self.information) + + def iter_parent(self) -> Iterator[CT | None]: + return (i.parent for i in self.information) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/exceptions.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/exceptions.py new file mode 100644 index 0000000..35e2755 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/exceptions.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Collection, Generic + +from ..structs import CT, RT, RequirementInformation + +if TYPE_CHECKING: + from .criterion import Criterion + + +class ResolverException(Exception): + """A base class for all exceptions raised by this module. + + Exceptions derived by this class should all be handled in this module. Any + bubbling pass the resolver should be treated as a bug. + """ + + +class RequirementsConflicted(ResolverException, Generic[RT, CT]): + def __init__(self, criterion: Criterion[RT, CT]) -> None: + super().__init__(criterion) + self.criterion = criterion + + def __str__(self) -> str: + return "Requirements conflict: {}".format( + ", ".join(repr(r) for r in self.criterion.iter_requirement()), + ) + + +class InconsistentCandidate(ResolverException, Generic[RT, CT]): + def __init__(self, candidate: CT, criterion: Criterion[RT, CT]): + super().__init__(candidate, criterion) + self.candidate = candidate + self.criterion = criterion + + def __str__(self) -> str: + return "Provided candidate {!r} does not satisfy {}".format( + self.candidate, + ", ".join(repr(r) for r in self.criterion.iter_requirement()), + ) + + +class ResolutionError(ResolverException): + pass + + +class ResolutionImpossible(ResolutionError, Generic[RT, CT]): + def __init__(self, causes: Collection[RequirementInformation[RT, CT]]): + super().__init__(causes) + # causes is a list of RequirementInformation objects + self.causes = causes + + +class ResolutionTooDeep(ResolutionError): + def __init__(self, round_count: int) -> None: + super().__init__(round_count) + self.round_count = round_count diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/resolution.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/resolution.py new file mode 100644 index 0000000..8c13d3b --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/resolution.py @@ -0,0 +1,627 @@ +from __future__ import annotations + +import collections +import itertools +import operator +from typing import TYPE_CHECKING, Generic + +from ..structs import ( + CT, + KT, + RT, + DirectedGraph, + IterableView, + IteratorMapping, + RequirementInformation, + State, + build_iter_view, +) +from .abstract import AbstractResolver, Result +from .criterion import Criterion +from .exceptions import ( + InconsistentCandidate, + RequirementsConflicted, + ResolutionImpossible, + ResolutionTooDeep, + ResolverException, +) + +if TYPE_CHECKING: + from collections.abc import Collection, Iterable, Mapping + + from ..providers import AbstractProvider, Preference + from ..reporters import BaseReporter + +_OPTIMISTIC_BACKJUMPING_RATIO: float = 0.1 + + +def _build_result(state: State[RT, CT, KT]) -> Result[RT, CT, KT]: + mapping = state.mapping + all_keys: dict[int, KT | None] = {id(v): k for k, v in mapping.items()} + all_keys[id(None)] = None + + graph: DirectedGraph[KT | None] = DirectedGraph() + graph.add(None) # Sentinel as root dependencies' parent. + + connected: set[KT | None] = {None} + for key, criterion in state.criteria.items(): + if not _has_route_to_root(state.criteria, key, all_keys, connected): + continue + if key not in graph: + graph.add(key) + for p in criterion.iter_parent(): + try: + pkey = all_keys[id(p)] + except KeyError: + continue + if pkey not in graph: + graph.add(pkey) + graph.connect(pkey, key) + + return Result( + mapping={k: v for k, v in mapping.items() if k in connected}, + graph=graph, + criteria=state.criteria, + ) + + +class Resolution(Generic[RT, CT, KT]): + """Stateful resolution object. + + This is designed as a one-off object that holds information to kick start + the resolution process, and holds the results afterwards. + """ + + def __init__( + self, + provider: AbstractProvider[RT, CT, KT], + reporter: BaseReporter[RT, CT, KT], + ) -> None: + self._p = provider + self._r = reporter + self._states: list[State[RT, CT, KT]] = [] + + # Optimistic backjumping variables + self._optimistic_backjumping_ratio = _OPTIMISTIC_BACKJUMPING_RATIO + self._save_states: list[State[RT, CT, KT]] | None = None + self._optimistic_start_round: int | None = None + + @property + def state(self) -> State[RT, CT, KT]: + try: + return self._states[-1] + except IndexError as e: + raise AttributeError("state") from e + + def _push_new_state(self) -> None: + """Push a new state into history. + + This new state will be used to hold resolution results of the next + coming round. + """ + base = self._states[-1] + state = State( + mapping=base.mapping.copy(), + criteria=base.criteria.copy(), + backtrack_causes=base.backtrack_causes[:], + ) + self._states.append(state) + + def _add_to_criteria( + self, + criteria: dict[KT, Criterion[RT, CT]], + requirement: RT, + parent: CT | None, + ) -> None: + self._r.adding_requirement(requirement=requirement, parent=parent) + + identifier = self._p.identify(requirement_or_candidate=requirement) + criterion = criteria.get(identifier) + if criterion: + incompatibilities = list(criterion.incompatibilities) + else: + incompatibilities = [] + + matches = self._p.find_matches( + identifier=identifier, + requirements=IteratorMapping( + criteria, + operator.methodcaller("iter_requirement"), + {identifier: [requirement]}, + ), + incompatibilities=IteratorMapping( + criteria, + operator.attrgetter("incompatibilities"), + {identifier: incompatibilities}, + ), + ) + + if criterion: + information = list(criterion.information) + information.append(RequirementInformation(requirement, parent)) + else: + information = [RequirementInformation(requirement, parent)] + + criterion = Criterion( + candidates=build_iter_view(matches), + information=information, + incompatibilities=incompatibilities, + ) + if not criterion.candidates: + raise RequirementsConflicted(criterion) + criteria[identifier] = criterion + + def _remove_information_from_criteria( + self, criteria: dict[KT, Criterion[RT, CT]], parents: Collection[KT] + ) -> None: + """Remove information from parents of criteria. + + Concretely, removes all values from each criterion's ``information`` + field that have one of ``parents`` as provider of the requirement. + + :param criteria: The criteria to update. + :param parents: Identifiers for which to remove information from all criteria. + """ + if not parents: + return + for key, criterion in criteria.items(): + criteria[key] = Criterion( + criterion.candidates, + [ + information + for information in criterion.information + if ( + information.parent is None + or self._p.identify(information.parent) not in parents + ) + ], + criterion.incompatibilities, + ) + + def _get_preference(self, name: KT) -> Preference: + return self._p.get_preference( + identifier=name, + resolutions=self.state.mapping, + candidates=IteratorMapping( + self.state.criteria, + operator.attrgetter("candidates"), + ), + information=IteratorMapping( + self.state.criteria, + operator.attrgetter("information"), + ), + backtrack_causes=self.state.backtrack_causes, + ) + + def _is_current_pin_satisfying( + self, name: KT, criterion: Criterion[RT, CT] + ) -> bool: + try: + current_pin = self.state.mapping[name] + except KeyError: + return False + return all( + self._p.is_satisfied_by(requirement=r, candidate=current_pin) + for r in criterion.iter_requirement() + ) + + def _get_updated_criteria(self, candidate: CT) -> dict[KT, Criterion[RT, CT]]: + criteria = self.state.criteria.copy() + for requirement in self._p.get_dependencies(candidate=candidate): + self._add_to_criteria(criteria, requirement, parent=candidate) + return criteria + + def _attempt_to_pin_criterion(self, name: KT) -> list[Criterion[RT, CT]]: + criterion = self.state.criteria[name] + + causes: list[Criterion[RT, CT]] = [] + for candidate in criterion.candidates: + try: + criteria = self._get_updated_criteria(candidate) + except RequirementsConflicted as e: + self._r.rejecting_candidate(e.criterion, candidate) + causes.append(e.criterion) + continue + + # Check the newly-pinned candidate actually works. This should + # always pass under normal circumstances, but in the case of a + # faulty provider, we will raise an error to notify the implementer + # to fix find_matches() and/or is_satisfied_by(). + satisfied = all( + self._p.is_satisfied_by(requirement=r, candidate=candidate) + for r in criterion.iter_requirement() + ) + if not satisfied: + raise InconsistentCandidate(candidate, criterion) + + self._r.pinning(candidate=candidate) + self.state.criteria.update(criteria) + + # Put newly-pinned candidate at the end. This is essential because + # backtracking looks at this mapping to get the last pin. + self.state.mapping.pop(name, None) + self.state.mapping[name] = candidate + + return [] + + # All candidates tried, nothing works. This criterion is a dead + # end, signal for backtracking. + return causes + + def _patch_criteria( + self, incompatibilities_from_broken: list[tuple[KT, list[CT]]] + ) -> bool: + # Create a new state from the last known-to-work one, and apply + # the previously gathered incompatibility information. + for k, incompatibilities in incompatibilities_from_broken: + if not incompatibilities: + continue + try: + criterion = self.state.criteria[k] + except KeyError: + continue + matches = self._p.find_matches( + identifier=k, + requirements=IteratorMapping( + self.state.criteria, + operator.methodcaller("iter_requirement"), + ), + incompatibilities=IteratorMapping( + self.state.criteria, + operator.attrgetter("incompatibilities"), + {k: incompatibilities}, + ), + ) + candidates: IterableView[CT] = build_iter_view(matches) + if not candidates: + return False + incompatibilities.extend(criterion.incompatibilities) + self.state.criteria[k] = Criterion( + candidates=candidates, + information=list(criterion.information), + incompatibilities=incompatibilities, + ) + return True + + def _save_state(self) -> None: + """Save states for potential rollback if optimistic backjumping fails.""" + if self._save_states is None: + self._save_states = [ + State( + mapping=s.mapping.copy(), + criteria=s.criteria.copy(), + backtrack_causes=s.backtrack_causes[:], + ) + for s in self._states + ] + + def _rollback_states(self) -> None: + """Rollback states and disable optimistic backjumping.""" + self._optimistic_backjumping_ratio = 0.0 + if self._save_states: + self._states = self._save_states + self._save_states = None + + def _backjump(self, causes: list[RequirementInformation[RT, CT]]) -> bool: + """Perform backjumping. + + When we enter here, the stack is like this:: + + [ state Z ] + [ state Y ] + [ state X ] + .... earlier states are irrelevant. + + 1. No pins worked for Z, so it does not have a pin. + 2. We want to reset state Y to unpinned, and pin another candidate. + 3. State X holds what state Y was before the pin, but does not + have the incompatibility information gathered in state Y. + + Each iteration of the loop will: + + 1. Identify Z. The incompatibility is not always caused by the latest + state. For example, given three requirements A, B and C, with + dependencies A1, B1 and C1, where A1 and B1 are incompatible: the + last state might be related to C, so we want to discard the + previous state. + 2. Discard Z. + 3. Discard Y but remember its incompatibility information gathered + previously, and the failure we're dealing with right now. + 4. Push a new state Y' based on X, and apply the incompatibility + information from Y to Y'. + 5a. If this causes Y' to conflict, we need to backtrack again. Make Y' + the new Z and go back to step 2. + 5b. If the incompatibilities apply cleanly, end backtracking. + """ + incompatible_reqs: Iterable[CT | RT] = itertools.chain( + (c.parent for c in causes if c.parent is not None), + (c.requirement for c in causes), + ) + incompatible_deps = {self._p.identify(r) for r in incompatible_reqs} + while len(self._states) >= 3: + # Remove the state that triggered backtracking. + del self._states[-1] + + # Optimistically backtrack to a state that caused the incompatibility + broken_state = self.state + while True: + # Retrieve the last candidate pin and known incompatibilities. + try: + broken_state = self._states.pop() + name, candidate = broken_state.mapping.popitem() + except (IndexError, KeyError): + raise ResolutionImpossible(causes) from None + + if ( + not self._optimistic_backjumping_ratio + and name not in incompatible_deps + ): + # For safe backjumping only backjump if the current dependency + # is not the same as the incompatible dependency + break + + # On the first time a non-safe backjump is done the state + # is saved so we can restore it later if the resolution fails + if ( + self._optimistic_backjumping_ratio + and self._save_states is None + and name not in incompatible_deps + ): + self._save_state() + + # If the current dependencies and the incompatible dependencies + # are overlapping then we have likely found a cause of the + # incompatibility + current_dependencies = { + self._p.identify(d) for d in self._p.get_dependencies(candidate) + } + if not current_dependencies.isdisjoint(incompatible_deps): + break + + # Fallback: We should not backtrack to the point where + # broken_state.mapping is empty, so stop backtracking for + # a chance for the resolution to recover + if not broken_state.mapping: + break + + # Guard: We need at least two state to remain to both + # backtrack and push a new state + if len(self._states) <= 1: + raise ResolutionImpossible(causes) + + incompatibilities_from_broken = [ + (k, list(v.incompatibilities)) for k, v in broken_state.criteria.items() + ] + + # Also mark the newly known incompatibility. + incompatibilities_from_broken.append((name, [candidate])) + + self._push_new_state() + success = self._patch_criteria(incompatibilities_from_broken) + + # It works! Let's work on this new state. + if success: + return True + + # State does not work after applying known incompatibilities. + # Try the still previous state. + + # No way to backtrack anymore. + return False + + def _extract_causes( + self, criteron: list[Criterion[RT, CT]] + ) -> list[RequirementInformation[RT, CT]]: + """Extract causes from list of criterion and deduplicate""" + return list({id(i): i for c in criteron for i in c.information}.values()) + + def resolve(self, requirements: Iterable[RT], max_rounds: int) -> State[RT, CT, KT]: + if self._states: + raise RuntimeError("already resolved") + + self._r.starting() + + # Initialize the root state. + self._states = [ + State( + mapping=collections.OrderedDict(), + criteria={}, + backtrack_causes=[], + ) + ] + for r in requirements: + try: + self._add_to_criteria(self.state.criteria, r, parent=None) + except RequirementsConflicted as e: + raise ResolutionImpossible(e.criterion.information) from e + + # The root state is saved as a sentinel so the first ever pin can have + # something to backtrack to if it fails. The root state is basically + # pinning the virtual "root" package in the graph. + self._push_new_state() + + # Variables for optimistic backjumping + optimistic_rounds_cutoff: int | None = None + optimistic_backjumping_start_round: int | None = None + + for round_index in range(max_rounds): + self._r.starting_round(index=round_index) + + # Handle if optimistic backjumping has been running for too long + if self._optimistic_backjumping_ratio and self._save_states is not None: + if optimistic_backjumping_start_round is None: + optimistic_backjumping_start_round = round_index + optimistic_rounds_cutoff = int( + (max_rounds - round_index) * self._optimistic_backjumping_ratio + ) + + if optimistic_rounds_cutoff <= 0: + self._rollback_states() + continue + elif optimistic_rounds_cutoff is not None: + if ( + round_index - optimistic_backjumping_start_round + >= optimistic_rounds_cutoff + ): + self._rollback_states() + continue + + unsatisfied_names = [ + key + for key, criterion in self.state.criteria.items() + if not self._is_current_pin_satisfying(key, criterion) + ] + + # All criteria are accounted for. Nothing more to pin, we are done! + if not unsatisfied_names: + self._r.ending(state=self.state) + return self.state + + # keep track of satisfied names to calculate diff after pinning + satisfied_names = set(self.state.criteria.keys()) - set(unsatisfied_names) + + if len(unsatisfied_names) > 1: + narrowed_unstatisfied_names = list( + self._p.narrow_requirement_selection( + identifiers=unsatisfied_names, + resolutions=self.state.mapping, + candidates=IteratorMapping( + self.state.criteria, + operator.attrgetter("candidates"), + ), + information=IteratorMapping( + self.state.criteria, + operator.attrgetter("information"), + ), + backtrack_causes=self.state.backtrack_causes, + ) + ) + else: + narrowed_unstatisfied_names = unsatisfied_names + + # If there are no unsatisfied names use unsatisfied names + if not narrowed_unstatisfied_names: + raise RuntimeError("narrow_requirement_selection returned 0 names") + + # If there is only 1 unsatisfied name skip calling self._get_preference + if len(narrowed_unstatisfied_names) > 1: + # Choose the most preferred unpinned criterion to try. + name = min(narrowed_unstatisfied_names, key=self._get_preference) + else: + name = narrowed_unstatisfied_names[0] + + failure_criterion = self._attempt_to_pin_criterion(name) + + if failure_criterion: + causes = self._extract_causes(failure_criterion) + # Backjump if pinning fails. The backjump process puts us in + # an unpinned state, so we can work on it in the next round. + self._r.resolving_conflicts(causes=causes) + + try: + success = self._backjump(causes) + except ResolutionImpossible: + if self._optimistic_backjumping_ratio and self._save_states: + failed_optimistic_backjumping = True + else: + raise + else: + failed_optimistic_backjumping = bool( + not success + and self._optimistic_backjumping_ratio + and self._save_states + ) + + if failed_optimistic_backjumping and self._save_states: + self._rollback_states() + else: + self.state.backtrack_causes[:] = causes + + # Dead ends everywhere. Give up. + if not success: + raise ResolutionImpossible(self.state.backtrack_causes) + else: + # discard as information sources any invalidated names + # (unsatisfied names that were previously satisfied) + newly_unsatisfied_names = { + key + for key, criterion in self.state.criteria.items() + if key in satisfied_names + and not self._is_current_pin_satisfying(key, criterion) + } + self._remove_information_from_criteria( + self.state.criteria, newly_unsatisfied_names + ) + # Pinning was successful. Push a new state to do another pin. + self._push_new_state() + + self._r.ending_round(index=round_index, state=self.state) + + raise ResolutionTooDeep(max_rounds) + + +class Resolver(AbstractResolver[RT, CT, KT]): + """The thing that performs the actual resolution work.""" + + base_exception = ResolverException + + def resolve( # type: ignore[override] + self, + requirements: Iterable[RT], + max_rounds: int = 100, + ) -> Result[RT, CT, KT]: + """Take a collection of constraints, spit out the resolution result. + + The return value is a representation to the final resolution result. It + is a tuple subclass with three public members: + + * `mapping`: A dict of resolved candidates. Each key is an identifier + of a requirement (as returned by the provider's `identify` method), + and the value is the resolved candidate. + * `graph`: A `DirectedGraph` instance representing the dependency tree. + The vertices are keys of `mapping`, and each edge represents *why* + a particular package is included. A special vertex `None` is + included to represent parents of user-supplied requirements. + * `criteria`: A dict of "criteria" that hold detailed information on + how edges in the graph are derived. Each key is an identifier of a + requirement, and the value is a `Criterion` instance. + + The following exceptions may be raised if a resolution cannot be found: + + * `ResolutionImpossible`: A resolution cannot be found for the given + combination of requirements. The `causes` attribute of the + exception is a list of (requirement, parent), giving the + requirements that could not be satisfied. + * `ResolutionTooDeep`: The dependency tree is too deeply nested and + the resolver gave up. This is usually caused by a circular + dependency, but you can try to resolve this by increasing the + `max_rounds` argument. + """ + resolution = Resolution(self.provider, self.reporter) + state = resolution.resolve(requirements, max_rounds=max_rounds) + return _build_result(state) + + +def _has_route_to_root( + criteria: Mapping[KT, Criterion[RT, CT]], + key: KT | None, + all_keys: dict[int, KT | None], + connected: set[KT | None], +) -> bool: + if key in connected: + return True + if key not in criteria: + return False + assert key is not None + for p in criteria[key].iter_parent(): + try: + pkey = all_keys[id(p)] + except KeyError: + continue + if pkey in connected: + connected.add(key) + return True + if _has_route_to_root(criteria, pkey, all_keys, connected): + connected.add(key) + return True + return False diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/structs.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/structs.py new file mode 100644 index 0000000..18c74d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/structs.py @@ -0,0 +1,209 @@ +from __future__ import annotations + +import itertools +from collections import namedtuple +from typing import ( + TYPE_CHECKING, + Callable, + Generic, + Iterable, + Iterator, + Mapping, + NamedTuple, + Sequence, + TypeVar, + Union, +) + +KT = TypeVar("KT") # Identifier. +RT = TypeVar("RT") # Requirement. +CT = TypeVar("CT") # Candidate. + +Matches = Union[Iterable[CT], Callable[[], Iterable[CT]]] + +if TYPE_CHECKING: + from .resolvers.criterion import Criterion + + class RequirementInformation(NamedTuple, Generic[RT, CT]): + requirement: RT + parent: CT | None + + class State(NamedTuple, Generic[RT, CT, KT]): + """Resolution state in a round.""" + + mapping: dict[KT, CT] + criteria: dict[KT, Criterion[RT, CT]] + backtrack_causes: list[RequirementInformation[RT, CT]] + +else: + RequirementInformation = namedtuple( + "RequirementInformation", ["requirement", "parent"] + ) + State = namedtuple("State", ["mapping", "criteria", "backtrack_causes"]) + + +class DirectedGraph(Generic[KT]): + """A graph structure with directed edges.""" + + def __init__(self) -> None: + self._vertices: set[KT] = set() + self._forwards: dict[KT, set[KT]] = {} # -> Set[] + self._backwards: dict[KT, set[KT]] = {} # -> Set[] + + def __iter__(self) -> Iterator[KT]: + return iter(self._vertices) + + def __len__(self) -> int: + return len(self._vertices) + + def __contains__(self, key: KT) -> bool: + return key in self._vertices + + def copy(self) -> DirectedGraph[KT]: + """Return a shallow copy of this graph.""" + other = type(self)() + other._vertices = set(self._vertices) + other._forwards = {k: set(v) for k, v in self._forwards.items()} + other._backwards = {k: set(v) for k, v in self._backwards.items()} + return other + + def add(self, key: KT) -> None: + """Add a new vertex to the graph.""" + if key in self._vertices: + raise ValueError("vertex exists") + self._vertices.add(key) + self._forwards[key] = set() + self._backwards[key] = set() + + def remove(self, key: KT) -> None: + """Remove a vertex from the graph, disconnecting all edges from/to it.""" + self._vertices.remove(key) + for f in self._forwards.pop(key): + self._backwards[f].remove(key) + for t in self._backwards.pop(key): + self._forwards[t].remove(key) + + def connected(self, f: KT, t: KT) -> bool: + return f in self._backwards[t] and t in self._forwards[f] + + def connect(self, f: KT, t: KT) -> None: + """Connect two existing vertices. + + Nothing happens if the vertices are already connected. + """ + if t not in self._vertices: + raise KeyError(t) + self._forwards[f].add(t) + self._backwards[t].add(f) + + def iter_edges(self) -> Iterator[tuple[KT, KT]]: + for f, children in self._forwards.items(): + for t in children: + yield f, t + + def iter_children(self, key: KT) -> Iterator[KT]: + return iter(self._forwards[key]) + + def iter_parents(self, key: KT) -> Iterator[KT]: + return iter(self._backwards[key]) + + +class IteratorMapping(Mapping[KT, Iterator[CT]], Generic[RT, CT, KT]): + def __init__( + self, + mapping: Mapping[KT, RT], + accessor: Callable[[RT], Iterable[CT]], + appends: Mapping[KT, Iterable[CT]] | None = None, + ) -> None: + self._mapping = mapping + self._accessor = accessor + self._appends: Mapping[KT, Iterable[CT]] = appends or {} + + def __repr__(self) -> str: + return "IteratorMapping({!r}, {!r}, {!r})".format( + self._mapping, + self._accessor, + self._appends, + ) + + def __bool__(self) -> bool: + return bool(self._mapping or self._appends) + + def __contains__(self, key: object) -> bool: + return key in self._mapping or key in self._appends + + def __getitem__(self, k: KT) -> Iterator[CT]: + try: + v = self._mapping[k] + except KeyError: + return iter(self._appends[k]) + return itertools.chain(self._accessor(v), self._appends.get(k, ())) + + def __iter__(self) -> Iterator[KT]: + more = (k for k in self._appends if k not in self._mapping) + return itertools.chain(self._mapping, more) + + def __len__(self) -> int: + more = sum(1 for k in self._appends if k not in self._mapping) + return len(self._mapping) + more + + +class _FactoryIterableView(Iterable[RT]): + """Wrap an iterator factory returned by `find_matches()`. + + Calling `iter()` on this class would invoke the underlying iterator + factory, making it a "collection with ordering" that can be iterated + through multiple times, but lacks random access methods presented in + built-in Python sequence types. + """ + + def __init__(self, factory: Callable[[], Iterable[RT]]) -> None: + self._factory = factory + self._iterable: Iterable[RT] | None = None + + def __repr__(self) -> str: + return f"{type(self).__name__}({list(self)})" + + def __bool__(self) -> bool: + try: + next(iter(self)) + except StopIteration: + return False + return True + + def __iter__(self) -> Iterator[RT]: + iterable = self._factory() if self._iterable is None else self._iterable + self._iterable, current = itertools.tee(iterable) + return current + + +class _SequenceIterableView(Iterable[RT]): + """Wrap an iterable returned by find_matches(). + + This is essentially just a proxy to the underlying sequence that provides + the same interface as `_FactoryIterableView`. + """ + + def __init__(self, sequence: Sequence[RT]): + self._sequence = sequence + + def __repr__(self) -> str: + return f"{type(self).__name__}({self._sequence})" + + def __bool__(self) -> bool: + return bool(self._sequence) + + def __iter__(self) -> Iterator[RT]: + return iter(self._sequence) + + +def build_iter_view(matches: Matches[CT]) -> Iterable[CT]: + """Build an iterable view from the value returned by `find_matches()`.""" + if callable(matches): + return _FactoryIterableView(matches) + if not isinstance(matches, Sequence): + matches = list(matches) + return _SequenceIterableView(matches) + + +IterableView = Iterable diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/rich/LICENSE new file mode 100644 index 0000000..4415505 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Will McGugan + +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. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__init__.py new file mode 100644 index 0000000..73f58d7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__init__.py @@ -0,0 +1,177 @@ +"""Rich text and beautiful formatting in the terminal.""" + +import os +from typing import IO, TYPE_CHECKING, Any, Callable, Optional, Union + +from ._extension import load_ipython_extension # noqa: F401 + +__all__ = ["get_console", "reconfigure", "print", "inspect", "print_json"] + +if TYPE_CHECKING: + from .console import Console + +# Global console used by alternative print +_console: Optional["Console"] = None + +try: + _IMPORT_CWD = os.path.abspath(os.getcwd()) +except FileNotFoundError: + # Can happen if the cwd has been deleted + _IMPORT_CWD = "" + + +def get_console() -> "Console": + """Get a global :class:`~rich.console.Console` instance. This function is used when Rich requires a Console, + and hasn't been explicitly given one. + + Returns: + Console: A console instance. + """ + global _console + if _console is None: + from .console import Console + + _console = Console() + + return _console + + +def reconfigure(*args: Any, **kwargs: Any) -> None: + """Reconfigures the global console by replacing it with another. + + Args: + *args (Any): Positional arguments for the replacement :class:`~rich.console.Console`. + **kwargs (Any): Keyword arguments for the replacement :class:`~rich.console.Console`. + """ + from pip._vendor.rich.console import Console + + new_console = Console(*args, **kwargs) + _console = get_console() + _console.__dict__ = new_console.__dict__ + + +def print( + *objects: Any, + sep: str = " ", + end: str = "\n", + file: Optional[IO[str]] = None, + flush: bool = False, +) -> None: + r"""Print object(s) supplied via positional arguments. + This function has an identical signature to the built-in print. + For more advanced features, see the :class:`~rich.console.Console` class. + + Args: + sep (str, optional): Separator between printed objects. Defaults to " ". + end (str, optional): Character to write at end of output. Defaults to "\\n". + file (IO[str], optional): File to write to, or None for stdout. Defaults to None. + flush (bool, optional): Has no effect as Rich always flushes output. Defaults to False. + + """ + from .console import Console + + write_console = get_console() if file is None else Console(file=file) + return write_console.print(*objects, sep=sep, end=end) + + +def print_json( + json: Optional[str] = None, + *, + data: Any = None, + indent: Union[None, int, str] = 2, + highlight: bool = True, + skip_keys: bool = False, + ensure_ascii: bool = False, + check_circular: bool = True, + allow_nan: bool = True, + default: Optional[Callable[[Any], Any]] = None, + sort_keys: bool = False, +) -> None: + """Pretty prints JSON. Output will be valid JSON. + + Args: + json (str): A string containing JSON. + data (Any): If json is not supplied, then encode this data. + indent (int, optional): Number of spaces to indent. Defaults to 2. + highlight (bool, optional): Enable highlighting of output: Defaults to True. + skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False. + ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False. + check_circular (bool, optional): Check for circular references. Defaults to True. + allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True. + default (Callable, optional): A callable that converts values that can not be encoded + in to something that can be JSON encoded. Defaults to None. + sort_keys (bool, optional): Sort dictionary keys. Defaults to False. + """ + + get_console().print_json( + json, + data=data, + indent=indent, + highlight=highlight, + skip_keys=skip_keys, + ensure_ascii=ensure_ascii, + check_circular=check_circular, + allow_nan=allow_nan, + default=default, + sort_keys=sort_keys, + ) + + +def inspect( + obj: Any, + *, + console: Optional["Console"] = None, + title: Optional[str] = None, + help: bool = False, + methods: bool = False, + docs: bool = True, + private: bool = False, + dunder: bool = False, + sort: bool = True, + all: bool = False, + value: bool = True, +) -> None: + """Inspect any Python object. + + * inspect() to see summarized info. + * inspect(, methods=True) to see methods. + * inspect(, help=True) to see full (non-abbreviated) help. + * inspect(, private=True) to see private attributes (single underscore). + * inspect(, dunder=True) to see attributes beginning with double underscore. + * inspect(, all=True) to see all attributes. + + Args: + obj (Any): An object to inspect. + title (str, optional): Title to display over inspect result, or None use type. Defaults to None. + help (bool, optional): Show full help text rather than just first paragraph. Defaults to False. + methods (bool, optional): Enable inspection of callables. Defaults to False. + docs (bool, optional): Also render doc strings. Defaults to True. + private (bool, optional): Show private attributes (beginning with underscore). Defaults to False. + dunder (bool, optional): Show attributes starting with double underscore. Defaults to False. + sort (bool, optional): Sort attributes alphabetically. Defaults to True. + all (bool, optional): Show all attributes. Defaults to False. + value (bool, optional): Pretty print value. Defaults to True. + """ + _console = console or get_console() + from pip._vendor.rich._inspect import Inspect + + # Special case for inspect(inspect) + is_inspect = obj is inspect + + _inspect = Inspect( + obj, + title=title, + help=is_inspect or help, + methods=is_inspect or methods, + docs=is_inspect or docs, + private=private, + dunder=dunder, + sort=sort, + all=all, + value=value, + ) + _console.print(_inspect) + + +if __name__ == "__main__": # pragma: no cover + print("Hello, **World**") diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__main__.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__main__.py new file mode 100644 index 0000000..a583f1e --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__main__.py @@ -0,0 +1,245 @@ +import colorsys +import io +from time import process_time + +from pip._vendor.rich import box +from pip._vendor.rich.color import Color +from pip._vendor.rich.console import Console, ConsoleOptions, Group, RenderableType, RenderResult +from pip._vendor.rich.markdown import Markdown +from pip._vendor.rich.measure import Measurement +from pip._vendor.rich.pretty import Pretty +from pip._vendor.rich.segment import Segment +from pip._vendor.rich.style import Style +from pip._vendor.rich.syntax import Syntax +from pip._vendor.rich.table import Table +from pip._vendor.rich.text import Text + + +class ColorBox: + def __rich_console__( + self, console: Console, options: ConsoleOptions + ) -> RenderResult: + for y in range(0, 5): + for x in range(options.max_width): + h = x / options.max_width + l = 0.1 + ((y / 5) * 0.7) + r1, g1, b1 = colorsys.hls_to_rgb(h, l, 1.0) + r2, g2, b2 = colorsys.hls_to_rgb(h, l + 0.7 / 10, 1.0) + bgcolor = Color.from_rgb(r1 * 255, g1 * 255, b1 * 255) + color = Color.from_rgb(r2 * 255, g2 * 255, b2 * 255) + yield Segment("▄", Style(color=color, bgcolor=bgcolor)) + yield Segment.line() + + def __rich_measure__( + self, console: "Console", options: ConsoleOptions + ) -> Measurement: + return Measurement(1, options.max_width) + + +def make_test_card() -> Table: + """Get a renderable that demonstrates a number of features.""" + table = Table.grid(padding=1, pad_edge=True) + table.title = "Rich features" + table.add_column("Feature", no_wrap=True, justify="center", style="bold red") + table.add_column("Demonstration") + + color_table = Table( + box=None, + expand=False, + show_header=False, + show_edge=False, + pad_edge=False, + ) + color_table.add_row( + ( + "✓ [bold green]4-bit color[/]\n" + "✓ [bold blue]8-bit color[/]\n" + "✓ [bold magenta]Truecolor (16.7 million)[/]\n" + "✓ [bold yellow]Dumb terminals[/]\n" + "✓ [bold cyan]Automatic color conversion" + ), + ColorBox(), + ) + + table.add_row("Colors", color_table) + + table.add_row( + "Styles", + "All ansi styles: [bold]bold[/], [dim]dim[/], [italic]italic[/italic], [underline]underline[/], [strike]strikethrough[/], [reverse]reverse[/], and even [blink]blink[/].", + ) + + lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque in metus sed sapien ultricies pretium a at justo. Maecenas luctus velit et auctor maximus." + lorem_table = Table.grid(padding=1, collapse_padding=True) + lorem_table.pad_edge = False + lorem_table.add_row( + Text(lorem, justify="left", style="green"), + Text(lorem, justify="center", style="yellow"), + Text(lorem, justify="right", style="blue"), + Text(lorem, justify="full", style="red"), + ) + table.add_row( + "Text", + Group( + Text.from_markup( + """Word wrap text. Justify [green]left[/], [yellow]center[/], [blue]right[/] or [red]full[/].\n""" + ), + lorem_table, + ), + ) + + def comparison(renderable1: RenderableType, renderable2: RenderableType) -> Table: + table = Table(show_header=False, pad_edge=False, box=None, expand=True) + table.add_column("1", ratio=1) + table.add_column("2", ratio=1) + table.add_row(renderable1, renderable2) + return table + + table.add_row( + "Asian\nlanguage\nsupport", + ":flag_for_china: 该库支持中文,日文和韩文文本!\n:flag_for_japan: ライブラリは中国語、日本語、韓国語のテキストをサポートしています\n:flag_for_south_korea: 이 라이브러리는 중국어, 일본어 및 한국어 텍스트를 지원합니다", + ) + + markup_example = ( + "[bold magenta]Rich[/] supports a simple [i]bbcode[/i]-like [b]markup[/b] for [yellow]color[/], [underline]style[/], and emoji! " + ":+1: :apple: :ant: :bear: :baguette_bread: :bus: " + ) + table.add_row("Markup", markup_example) + + example_table = Table( + show_edge=False, + show_header=True, + expand=False, + row_styles=["none", "dim"], + box=box.SIMPLE, + ) + example_table.add_column("[green]Date", style="green", no_wrap=True) + example_table.add_column("[blue]Title", style="blue") + example_table.add_column( + "[cyan]Production Budget", + style="cyan", + justify="right", + no_wrap=True, + ) + example_table.add_column( + "[magenta]Box Office", + style="magenta", + justify="right", + no_wrap=True, + ) + example_table.add_row( + "Dec 20, 2019", + "Star Wars: The Rise of Skywalker", + "$275,000,000", + "$375,126,118", + ) + example_table.add_row( + "May 25, 2018", + "[b]Solo[/]: A Star Wars Story", + "$275,000,000", + "$393,151,347", + ) + example_table.add_row( + "Dec 15, 2017", + "Star Wars Ep. VIII: The Last Jedi", + "$262,000,000", + "[bold]$1,332,539,889[/bold]", + ) + example_table.add_row( + "May 19, 1999", + "Star Wars Ep. [b]I[/b]: [i]The phantom Menace", + "$115,000,000", + "$1,027,044,677", + ) + + table.add_row("Tables", example_table) + + code = '''\ +def iter_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]: + """Iterate and generate a tuple with a flag for last value.""" + iter_values = iter(values) + try: + previous_value = next(iter_values) + except StopIteration: + return + for value in iter_values: + yield False, previous_value + previous_value = value + yield True, previous_value''' + + pretty_data = { + "foo": [ + 3.1427, + ( + "Paul Atreides", + "Vladimir Harkonnen", + "Thufir Hawat", + ), + ], + "atomic": (False, True, None), + } + table.add_row( + "Syntax\nhighlighting\n&\npretty\nprinting", + comparison( + Syntax(code, "python3", line_numbers=True, indent_guides=True), + Pretty(pretty_data, indent_guides=True), + ), + ) + + markdown_example = """\ +# Markdown + +Supports much of the *markdown* __syntax__! + +- Headers +- Basic formatting: **bold**, *italic*, `code` +- Block quotes +- Lists, and more... + """ + table.add_row( + "Markdown", comparison("[cyan]" + markdown_example, Markdown(markdown_example)) + ) + + table.add_row( + "+more!", + """Progress bars, columns, styled logging handler, tracebacks, etc...""", + ) + return table + + +if __name__ == "__main__": # pragma: no cover + from pip._vendor.rich.panel import Panel + + console = Console( + file=io.StringIO(), + force_terminal=True, + ) + test_card = make_test_card() + + # Print once to warm cache + start = process_time() + console.print(test_card) + pre_cache_taken = round((process_time() - start) * 1000.0, 1) + + console.file = io.StringIO() + + start = process_time() + console.print(test_card) + taken = round((process_time() - start) * 1000.0, 1) + + c = Console(record=True) + c.print(test_card) + + console = Console() + console.print(f"[dim]rendered in [not dim]{pre_cache_taken}ms[/] (cold cache)") + console.print(f"[dim]rendered in [not dim]{taken}ms[/] (warm cache)") + console.print() + console.print( + Panel.fit( + "[b magenta]Hope you enjoy using Rich![/]\n\n" + "Please consider sponsoring me if you get value from my work.\n\n" + "Even the price of a ☕ can brighten my day!\n\n" + "https://github.com/sponsors/willmcgugan", + border_style="red", + title="Help ensure Rich is maintained", + ) + ) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..567fe59 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__main__.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000..46ef4de Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__main__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_cell_widths.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_cell_widths.cpython-312.pyc new file mode 100644 index 0000000..95fff81 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_cell_widths.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_codes.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_codes.cpython-312.pyc new file mode 100644 index 0000000..bf1fe21 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_codes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_replace.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_replace.cpython-312.pyc new file mode 100644 index 0000000..5918c88 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_replace.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_export_format.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_export_format.cpython-312.pyc new file mode 100644 index 0000000..439a566 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_export_format.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_extension.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_extension.cpython-312.pyc new file mode 100644 index 0000000..6cb313a Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_extension.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_fileno.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_fileno.cpython-312.pyc new file mode 100644 index 0000000..fcea6d4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_fileno.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_inspect.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_inspect.cpython-312.pyc new file mode 100644 index 0000000..64c3729 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_inspect.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_log_render.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_log_render.cpython-312.pyc new file mode 100644 index 0000000..a31463b Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_log_render.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_loop.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_loop.cpython-312.pyc new file mode 100644 index 0000000..7b587ac Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_loop.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_null_file.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_null_file.cpython-312.pyc new file mode 100644 index 0000000..8011673 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_null_file.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_palettes.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_palettes.cpython-312.pyc new file mode 100644 index 0000000..0f33927 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_palettes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_pick.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_pick.cpython-312.pyc new file mode 100644 index 0000000..a695031 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_pick.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_ratio.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_ratio.cpython-312.pyc new file mode 100644 index 0000000..20c19de Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_ratio.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_spinners.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_spinners.cpython-312.pyc new file mode 100644 index 0000000..aec14d4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_spinners.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_stack.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_stack.cpython-312.pyc new file mode 100644 index 0000000..747f0d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_stack.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_timer.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_timer.cpython-312.pyc new file mode 100644 index 0000000..105f92a Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_timer.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_win32_console.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_win32_console.cpython-312.pyc new file mode 100644 index 0000000..d2c7c63 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_win32_console.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows.cpython-312.pyc new file mode 100644 index 0000000..7e50852 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows_renderer.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows_renderer.cpython-312.pyc new file mode 100644 index 0000000..94745a4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows_renderer.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_wrap.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_wrap.cpython-312.pyc new file mode 100644 index 0000000..d1286d6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_wrap.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/abc.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/abc.cpython-312.pyc new file mode 100644 index 0000000..5b526dd Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/abc.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/align.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/align.cpython-312.pyc new file mode 100644 index 0000000..e27ac78 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/align.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/ansi.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/ansi.cpython-312.pyc new file mode 100644 index 0000000..b93df94 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/ansi.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/bar.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/bar.cpython-312.pyc new file mode 100644 index 0000000..708446d Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/bar.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/box.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/box.cpython-312.pyc new file mode 100644 index 0000000..a32a426 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/box.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/cells.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/cells.cpython-312.pyc new file mode 100644 index 0000000..a6cbe3f Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/cells.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color.cpython-312.pyc new file mode 100644 index 0000000..a434552 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color_triplet.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color_triplet.cpython-312.pyc new file mode 100644 index 0000000..0ace13b Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color_triplet.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/columns.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/columns.cpython-312.pyc new file mode 100644 index 0000000..90f5dd9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/columns.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/console.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/console.cpython-312.pyc new file mode 100644 index 0000000..66addc4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/console.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/constrain.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/constrain.cpython-312.pyc new file mode 100644 index 0000000..9f1036d Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/constrain.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/containers.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/containers.cpython-312.pyc new file mode 100644 index 0000000..e4ec95a Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/containers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/control.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/control.cpython-312.pyc new file mode 100644 index 0000000..f3bb8b7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/control.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/default_styles.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/default_styles.cpython-312.pyc new file mode 100644 index 0000000..9c63f2b Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/default_styles.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/diagnose.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/diagnose.cpython-312.pyc new file mode 100644 index 0000000..028f842 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/diagnose.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/emoji.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/emoji.cpython-312.pyc new file mode 100644 index 0000000..55af8c6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/emoji.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/errors.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/errors.cpython-312.pyc new file mode 100644 index 0000000..ecb95dd Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/errors.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/file_proxy.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/file_proxy.cpython-312.pyc new file mode 100644 index 0000000..df99f9d Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/file_proxy.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/filesize.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/filesize.cpython-312.pyc new file mode 100644 index 0000000..91921e0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/filesize.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/highlighter.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/highlighter.cpython-312.pyc new file mode 100644 index 0000000..db4da8d Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/highlighter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/json.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/json.cpython-312.pyc new file mode 100644 index 0000000..81d8f24 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/json.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/jupyter.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/jupyter.cpython-312.pyc new file mode 100644 index 0000000..f7be776 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/jupyter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/layout.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/layout.cpython-312.pyc new file mode 100644 index 0000000..a941668 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/layout.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live.cpython-312.pyc new file mode 100644 index 0000000..d5a8f91 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live_render.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live_render.cpython-312.pyc new file mode 100644 index 0000000..ebd540d Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live_render.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/logging.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/logging.cpython-312.pyc new file mode 100644 index 0000000..85765b7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/logging.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/markup.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/markup.cpython-312.pyc new file mode 100644 index 0000000..86f7188 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/markup.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/measure.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/measure.cpython-312.pyc new file mode 100644 index 0000000..58e0a70 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/measure.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/padding.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/padding.cpython-312.pyc new file mode 100644 index 0000000..96a7eba Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/padding.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pager.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pager.cpython-312.pyc new file mode 100644 index 0000000..e38e026 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pager.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/palette.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/palette.cpython-312.pyc new file mode 100644 index 0000000..268c73d Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/palette.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/panel.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/panel.cpython-312.pyc new file mode 100644 index 0000000..a335cc8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/panel.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pretty.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pretty.cpython-312.pyc new file mode 100644 index 0000000..fcbba33 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pretty.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress.cpython-312.pyc new file mode 100644 index 0000000..97b3a7b Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress_bar.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress_bar.cpython-312.pyc new file mode 100644 index 0000000..5f8cac9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress_bar.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/prompt.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/prompt.cpython-312.pyc new file mode 100644 index 0000000..3471407 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/prompt.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/protocol.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/protocol.cpython-312.pyc new file mode 100644 index 0000000..7240258 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/protocol.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/region.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/region.cpython-312.pyc new file mode 100644 index 0000000..dea2388 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/region.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/repr.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/repr.cpython-312.pyc new file mode 100644 index 0000000..9d98926 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/repr.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/rule.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/rule.cpython-312.pyc new file mode 100644 index 0000000..8c8f7fb Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/rule.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/scope.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/scope.cpython-312.pyc new file mode 100644 index 0000000..790c16d Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/scope.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/screen.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/screen.cpython-312.pyc new file mode 100644 index 0000000..b104113 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/screen.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/segment.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/segment.cpython-312.pyc new file mode 100644 index 0000000..c29e156 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/segment.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/spinner.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/spinner.cpython-312.pyc new file mode 100644 index 0000000..bfea102 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/spinner.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/status.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/status.cpython-312.pyc new file mode 100644 index 0000000..1898fe4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/status.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/style.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/style.cpython-312.pyc new file mode 100644 index 0000000..8422bf4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/style.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/styled.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/styled.cpython-312.pyc new file mode 100644 index 0000000..201cda4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/styled.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/syntax.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/syntax.cpython-312.pyc new file mode 100644 index 0000000..670308d Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/syntax.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/table.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/table.cpython-312.pyc new file mode 100644 index 0000000..9ea5425 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/table.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/terminal_theme.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/terminal_theme.cpython-312.pyc new file mode 100644 index 0000000..b711ed0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/terminal_theme.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/text.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/text.cpython-312.pyc new file mode 100644 index 0000000..6776141 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/text.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/theme.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/theme.cpython-312.pyc new file mode 100644 index 0000000..4e87685 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/theme.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/themes.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/themes.cpython-312.pyc new file mode 100644 index 0000000..4fed675 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/themes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/traceback.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/traceback.cpython-312.pyc new file mode 100644 index 0000000..584fd5e Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/traceback.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/tree.cpython-312.pyc b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/tree.cpython-312.pyc new file mode 100644 index 0000000..0f29080 Binary files /dev/null and b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/tree.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_cell_widths.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_cell_widths.py new file mode 100644 index 0000000..608ae3a --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_cell_widths.py @@ -0,0 +1,454 @@ +# Auto generated by make_terminal_widths.py + +CELL_WIDTHS = [ + (0, 0, 0), + (1, 31, -1), + (127, 159, -1), + (173, 173, 0), + (768, 879, 0), + (1155, 1161, 0), + (1425, 1469, 0), + (1471, 1471, 0), + (1473, 1474, 0), + (1476, 1477, 0), + (1479, 1479, 0), + (1536, 1541, 0), + (1552, 1562, 0), + (1564, 1564, 0), + (1611, 1631, 0), + (1648, 1648, 0), + (1750, 1757, 0), + (1759, 1764, 0), + (1767, 1768, 0), + (1770, 1773, 0), + (1807, 1807, 0), + (1809, 1809, 0), + (1840, 1866, 0), + (1958, 1968, 0), + (2027, 2035, 0), + (2045, 2045, 0), + (2070, 2073, 0), + (2075, 2083, 0), + (2085, 2087, 0), + (2089, 2093, 0), + (2137, 2139, 0), + (2192, 2193, 0), + (2200, 2207, 0), + (2250, 2307, 0), + (2362, 2364, 0), + (2366, 2383, 0), + (2385, 2391, 0), + (2402, 2403, 0), + (2433, 2435, 0), + (2492, 2492, 0), + (2494, 2500, 0), + (2503, 2504, 0), + (2507, 2509, 0), + (2519, 2519, 0), + (2530, 2531, 0), + (2558, 2558, 0), + (2561, 2563, 0), + (2620, 2620, 0), + (2622, 2626, 0), + (2631, 2632, 0), + (2635, 2637, 0), + (2641, 2641, 0), + (2672, 2673, 0), + (2677, 2677, 0), + (2689, 2691, 0), + (2748, 2748, 0), + (2750, 2757, 0), + (2759, 2761, 0), + (2763, 2765, 0), + (2786, 2787, 0), + (2810, 2815, 0), + (2817, 2819, 0), + (2876, 2876, 0), + (2878, 2884, 0), + (2887, 2888, 0), + (2891, 2893, 0), + (2901, 2903, 0), + (2914, 2915, 0), + (2946, 2946, 0), + (3006, 3010, 0), + (3014, 3016, 0), + (3018, 3021, 0), + (3031, 3031, 0), + (3072, 3076, 0), + (3132, 3132, 0), + (3134, 3140, 0), + (3142, 3144, 0), + (3146, 3149, 0), + (3157, 3158, 0), + (3170, 3171, 0), + (3201, 3203, 0), + (3260, 3260, 0), + (3262, 3268, 0), + (3270, 3272, 0), + (3274, 3277, 0), + (3285, 3286, 0), + (3298, 3299, 0), + (3315, 3315, 0), + (3328, 3331, 0), + (3387, 3388, 0), + (3390, 3396, 0), + (3398, 3400, 0), + (3402, 3405, 0), + (3415, 3415, 0), + (3426, 3427, 0), + (3457, 3459, 0), + (3530, 3530, 0), + (3535, 3540, 0), + (3542, 3542, 0), + (3544, 3551, 0), + (3570, 3571, 0), + (3633, 3633, 0), + (3636, 3642, 0), + (3655, 3662, 0), + (3761, 3761, 0), + (3764, 3772, 0), + (3784, 3790, 0), + (3864, 3865, 0), + (3893, 3893, 0), + (3895, 3895, 0), + (3897, 3897, 0), + (3902, 3903, 0), + (3953, 3972, 0), + (3974, 3975, 0), + (3981, 3991, 0), + (3993, 4028, 0), + (4038, 4038, 0), + (4139, 4158, 0), + (4182, 4185, 0), + (4190, 4192, 0), + (4194, 4196, 0), + (4199, 4205, 0), + (4209, 4212, 0), + (4226, 4237, 0), + (4239, 4239, 0), + (4250, 4253, 0), + (4352, 4447, 2), + (4448, 4607, 0), + (4957, 4959, 0), + (5906, 5909, 0), + (5938, 5940, 0), + (5970, 5971, 0), + (6002, 6003, 0), + (6068, 6099, 0), + (6109, 6109, 0), + (6155, 6159, 0), + (6277, 6278, 0), + (6313, 6313, 0), + (6432, 6443, 0), + (6448, 6459, 0), + (6679, 6683, 0), + (6741, 6750, 0), + (6752, 6780, 0), + (6783, 6783, 0), + (6832, 6862, 0), + (6912, 6916, 0), + (6964, 6980, 0), + (7019, 7027, 0), + (7040, 7042, 0), + (7073, 7085, 0), + (7142, 7155, 0), + (7204, 7223, 0), + (7376, 7378, 0), + (7380, 7400, 0), + (7405, 7405, 0), + (7412, 7412, 0), + (7415, 7417, 0), + (7616, 7679, 0), + (8203, 8207, 0), + (8232, 8238, 0), + (8288, 8292, 0), + (8294, 8303, 0), + (8400, 8432, 0), + (8986, 8987, 2), + (9001, 9002, 2), + (9193, 9196, 2), + (9200, 9200, 2), + (9203, 9203, 2), + (9725, 9726, 2), + (9748, 9749, 2), + (9800, 9811, 2), + (9855, 9855, 2), + (9875, 9875, 2), + (9889, 9889, 2), + (9898, 9899, 2), + (9917, 9918, 2), + (9924, 9925, 2), + (9934, 9934, 2), + (9940, 9940, 2), + (9962, 9962, 2), + (9970, 9971, 2), + (9973, 9973, 2), + (9978, 9978, 2), + (9981, 9981, 2), + (9989, 9989, 2), + (9994, 9995, 2), + (10024, 10024, 2), + (10060, 10060, 2), + (10062, 10062, 2), + (10067, 10069, 2), + (10071, 10071, 2), + (10133, 10135, 2), + (10160, 10160, 2), + (10175, 10175, 2), + (11035, 11036, 2), + (11088, 11088, 2), + (11093, 11093, 2), + (11503, 11505, 0), + (11647, 11647, 0), + (11744, 11775, 0), + (11904, 11929, 2), + (11931, 12019, 2), + (12032, 12245, 2), + (12272, 12329, 2), + (12330, 12335, 0), + (12336, 12350, 2), + (12353, 12438, 2), + (12441, 12442, 0), + (12443, 12543, 2), + (12549, 12591, 2), + (12593, 12686, 2), + (12688, 12771, 2), + (12783, 12830, 2), + (12832, 12871, 2), + (12880, 19903, 2), + (19968, 42124, 2), + (42128, 42182, 2), + (42607, 42610, 0), + (42612, 42621, 0), + (42654, 42655, 0), + (42736, 42737, 0), + (43010, 43010, 0), + (43014, 43014, 0), + (43019, 43019, 0), + (43043, 43047, 0), + (43052, 43052, 0), + (43136, 43137, 0), + (43188, 43205, 0), + (43232, 43249, 0), + (43263, 43263, 0), + (43302, 43309, 0), + (43335, 43347, 0), + (43360, 43388, 2), + (43392, 43395, 0), + (43443, 43456, 0), + (43493, 43493, 0), + (43561, 43574, 0), + (43587, 43587, 0), + (43596, 43597, 0), + (43643, 43645, 0), + (43696, 43696, 0), + (43698, 43700, 0), + (43703, 43704, 0), + (43710, 43711, 0), + (43713, 43713, 0), + (43755, 43759, 0), + (43765, 43766, 0), + (44003, 44010, 0), + (44012, 44013, 0), + (44032, 55203, 2), + (55216, 55295, 0), + (63744, 64255, 2), + (64286, 64286, 0), + (65024, 65039, 0), + (65040, 65049, 2), + (65056, 65071, 0), + (65072, 65106, 2), + (65108, 65126, 2), + (65128, 65131, 2), + (65279, 65279, 0), + (65281, 65376, 2), + (65504, 65510, 2), + (65529, 65531, 0), + (66045, 66045, 0), + (66272, 66272, 0), + (66422, 66426, 0), + (68097, 68099, 0), + (68101, 68102, 0), + (68108, 68111, 0), + (68152, 68154, 0), + (68159, 68159, 0), + (68325, 68326, 0), + (68900, 68903, 0), + (69291, 69292, 0), + (69373, 69375, 0), + (69446, 69456, 0), + (69506, 69509, 0), + (69632, 69634, 0), + (69688, 69702, 0), + (69744, 69744, 0), + (69747, 69748, 0), + (69759, 69762, 0), + (69808, 69818, 0), + (69821, 69821, 0), + (69826, 69826, 0), + (69837, 69837, 0), + (69888, 69890, 0), + (69927, 69940, 0), + (69957, 69958, 0), + (70003, 70003, 0), + (70016, 70018, 0), + (70067, 70080, 0), + (70089, 70092, 0), + (70094, 70095, 0), + (70188, 70199, 0), + (70206, 70206, 0), + (70209, 70209, 0), + (70367, 70378, 0), + (70400, 70403, 0), + (70459, 70460, 0), + (70462, 70468, 0), + (70471, 70472, 0), + (70475, 70477, 0), + (70487, 70487, 0), + (70498, 70499, 0), + (70502, 70508, 0), + (70512, 70516, 0), + (70709, 70726, 0), + (70750, 70750, 0), + (70832, 70851, 0), + (71087, 71093, 0), + (71096, 71104, 0), + (71132, 71133, 0), + (71216, 71232, 0), + (71339, 71351, 0), + (71453, 71467, 0), + (71724, 71738, 0), + (71984, 71989, 0), + (71991, 71992, 0), + (71995, 71998, 0), + (72000, 72000, 0), + (72002, 72003, 0), + (72145, 72151, 0), + (72154, 72160, 0), + (72164, 72164, 0), + (72193, 72202, 0), + (72243, 72249, 0), + (72251, 72254, 0), + (72263, 72263, 0), + (72273, 72283, 0), + (72330, 72345, 0), + (72751, 72758, 0), + (72760, 72767, 0), + (72850, 72871, 0), + (72873, 72886, 0), + (73009, 73014, 0), + (73018, 73018, 0), + (73020, 73021, 0), + (73023, 73029, 0), + (73031, 73031, 0), + (73098, 73102, 0), + (73104, 73105, 0), + (73107, 73111, 0), + (73459, 73462, 0), + (73472, 73473, 0), + (73475, 73475, 0), + (73524, 73530, 0), + (73534, 73538, 0), + (78896, 78912, 0), + (78919, 78933, 0), + (92912, 92916, 0), + (92976, 92982, 0), + (94031, 94031, 0), + (94033, 94087, 0), + (94095, 94098, 0), + (94176, 94179, 2), + (94180, 94180, 0), + (94192, 94193, 0), + (94208, 100343, 2), + (100352, 101589, 2), + (101632, 101640, 2), + (110576, 110579, 2), + (110581, 110587, 2), + (110589, 110590, 2), + (110592, 110882, 2), + (110898, 110898, 2), + (110928, 110930, 2), + (110933, 110933, 2), + (110948, 110951, 2), + (110960, 111355, 2), + (113821, 113822, 0), + (113824, 113827, 0), + (118528, 118573, 0), + (118576, 118598, 0), + (119141, 119145, 0), + (119149, 119170, 0), + (119173, 119179, 0), + (119210, 119213, 0), + (119362, 119364, 0), + (121344, 121398, 0), + (121403, 121452, 0), + (121461, 121461, 0), + (121476, 121476, 0), + (121499, 121503, 0), + (121505, 121519, 0), + (122880, 122886, 0), + (122888, 122904, 0), + (122907, 122913, 0), + (122915, 122916, 0), + (122918, 122922, 0), + (123023, 123023, 0), + (123184, 123190, 0), + (123566, 123566, 0), + (123628, 123631, 0), + (124140, 124143, 0), + (125136, 125142, 0), + (125252, 125258, 0), + (126980, 126980, 2), + (127183, 127183, 2), + (127374, 127374, 2), + (127377, 127386, 2), + (127488, 127490, 2), + (127504, 127547, 2), + (127552, 127560, 2), + (127568, 127569, 2), + (127584, 127589, 2), + (127744, 127776, 2), + (127789, 127797, 2), + (127799, 127868, 2), + (127870, 127891, 2), + (127904, 127946, 2), + (127951, 127955, 2), + (127968, 127984, 2), + (127988, 127988, 2), + (127992, 127994, 2), + (127995, 127999, 0), + (128000, 128062, 2), + (128064, 128064, 2), + (128066, 128252, 2), + (128255, 128317, 2), + (128331, 128334, 2), + (128336, 128359, 2), + (128378, 128378, 2), + (128405, 128406, 2), + (128420, 128420, 2), + (128507, 128591, 2), + (128640, 128709, 2), + (128716, 128716, 2), + (128720, 128722, 2), + (128725, 128727, 2), + (128732, 128735, 2), + (128747, 128748, 2), + (128756, 128764, 2), + (128992, 129003, 2), + (129008, 129008, 2), + (129292, 129338, 2), + (129340, 129349, 2), + (129351, 129535, 2), + (129648, 129660, 2), + (129664, 129672, 2), + (129680, 129725, 2), + (129727, 129733, 2), + (129742, 129755, 2), + (129760, 129768, 2), + (129776, 129784, 2), + (131072, 196605, 2), + (196608, 262141, 2), + (917505, 917505, 0), + (917536, 917631, 0), + (917760, 917999, 0), +] diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_codes.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_codes.py new file mode 100644 index 0000000..1f2877b --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_codes.py @@ -0,0 +1,3610 @@ +EMOJI = { + "1st_place_medal": "🥇", + "2nd_place_medal": "🥈", + "3rd_place_medal": "🥉", + "ab_button_(blood_type)": "🆎", + "atm_sign": "🏧", + "a_button_(blood_type)": "🅰", + "afghanistan": "🇦🇫", + "albania": "🇦🇱", + "algeria": "🇩🇿", + "american_samoa": "🇦🇸", + "andorra": "🇦🇩", + "angola": "🇦🇴", + "anguilla": "🇦🇮", + "antarctica": "🇦🇶", + "antigua_&_barbuda": "🇦🇬", + "aquarius": "♒", + "argentina": "🇦🇷", + "aries": "♈", + "armenia": "🇦🇲", + "aruba": "🇦🇼", + "ascension_island": "🇦🇨", + "australia": "🇦🇺", + "austria": "🇦🇹", + "azerbaijan": "🇦🇿", + "back_arrow": "🔙", + "b_button_(blood_type)": "🅱", + "bahamas": "🇧🇸", + "bahrain": "🇧🇭", + "bangladesh": "🇧🇩", + "barbados": "🇧🇧", + "belarus": "🇧🇾", + "belgium": "🇧🇪", + "belize": "🇧🇿", + "benin": "🇧🇯", + "bermuda": "🇧🇲", + "bhutan": "🇧🇹", + "bolivia": "🇧🇴", + "bosnia_&_herzegovina": "🇧🇦", + "botswana": "🇧🇼", + "bouvet_island": "🇧🇻", + "brazil": "🇧🇷", + "british_indian_ocean_territory": "🇮🇴", + "british_virgin_islands": "🇻🇬", + "brunei": "🇧🇳", + "bulgaria": "🇧🇬", + "burkina_faso": "🇧🇫", + "burundi": "🇧🇮", + "cl_button": "🆑", + "cool_button": "🆒", + "cambodia": "🇰🇭", + "cameroon": "🇨🇲", + "canada": "🇨🇦", + "canary_islands": "🇮🇨", + "cancer": "♋", + "cape_verde": "🇨🇻", + "capricorn": "♑", + "caribbean_netherlands": "🇧🇶", + "cayman_islands": "🇰🇾", + "central_african_republic": "🇨🇫", + "ceuta_&_melilla": "🇪🇦", + "chad": "🇹🇩", + "chile": "🇨🇱", + "china": "🇨🇳", + "christmas_island": "🇨🇽", + "christmas_tree": "🎄", + "clipperton_island": "🇨🇵", + "cocos_(keeling)_islands": "🇨🇨", + "colombia": "🇨🇴", + "comoros": "🇰🇲", + "congo_-_brazzaville": "🇨🇬", + "congo_-_kinshasa": "🇨🇩", + "cook_islands": "🇨🇰", + "costa_rica": "🇨🇷", + "croatia": "🇭🇷", + "cuba": "🇨🇺", + "curaçao": "🇨🇼", + "cyprus": "🇨🇾", + "czechia": "🇨🇿", + "côte_d’ivoire": "🇨🇮", + "denmark": "🇩🇰", + "diego_garcia": "🇩🇬", + "djibouti": "🇩🇯", + "dominica": "🇩🇲", + "dominican_republic": "🇩🇴", + "end_arrow": "🔚", + "ecuador": "🇪🇨", + "egypt": "🇪🇬", + "el_salvador": "🇸🇻", + "england": "🏴\U000e0067\U000e0062\U000e0065\U000e006e\U000e0067\U000e007f", + "equatorial_guinea": "🇬🇶", + "eritrea": "🇪🇷", + "estonia": "🇪🇪", + "ethiopia": "🇪🇹", + "european_union": "🇪🇺", + "free_button": "🆓", + "falkland_islands": "🇫🇰", + "faroe_islands": "🇫🇴", + "fiji": "🇫🇯", + "finland": "🇫🇮", + "france": "🇫🇷", + "french_guiana": "🇬🇫", + "french_polynesia": "🇵🇫", + "french_southern_territories": "🇹🇫", + "gabon": "🇬🇦", + "gambia": "🇬🇲", + "gemini": "♊", + "georgia": "🇬🇪", + "germany": "🇩🇪", + "ghana": "🇬🇭", + "gibraltar": "🇬🇮", + "greece": "🇬🇷", + "greenland": "🇬🇱", + "grenada": "🇬🇩", + "guadeloupe": "🇬🇵", + "guam": "🇬🇺", + "guatemala": "🇬🇹", + "guernsey": "🇬🇬", + "guinea": "🇬🇳", + "guinea-bissau": "🇬🇼", + "guyana": "🇬🇾", + "haiti": "🇭🇹", + "heard_&_mcdonald_islands": "🇭🇲", + "honduras": "🇭🇳", + "hong_kong_sar_china": "🇭🇰", + "hungary": "🇭🇺", + "id_button": "🆔", + "iceland": "🇮🇸", + "india": "🇮🇳", + "indonesia": "🇮🇩", + "iran": "🇮🇷", + "iraq": "🇮🇶", + "ireland": "🇮🇪", + "isle_of_man": "🇮🇲", + "israel": "🇮🇱", + "italy": "🇮🇹", + "jamaica": "🇯🇲", + "japan": "🗾", + "japanese_acceptable_button": "🉑", + "japanese_application_button": "🈸", + "japanese_bargain_button": "🉐", + "japanese_castle": "🏯", + "japanese_congratulations_button": "㊗", + "japanese_discount_button": "🈹", + "japanese_dolls": "🎎", + "japanese_free_of_charge_button": "🈚", + "japanese_here_button": "🈁", + "japanese_monthly_amount_button": "🈷", + "japanese_no_vacancy_button": "🈵", + "japanese_not_free_of_charge_button": "🈶", + "japanese_open_for_business_button": "🈺", + "japanese_passing_grade_button": "🈴", + "japanese_post_office": "🏣", + "japanese_prohibited_button": "🈲", + "japanese_reserved_button": "🈯", + "japanese_secret_button": "㊙", + "japanese_service_charge_button": "🈂", + "japanese_symbol_for_beginner": "🔰", + "japanese_vacancy_button": "🈳", + "jersey": "🇯🇪", + "jordan": "🇯🇴", + "kazakhstan": "🇰🇿", + "kenya": "🇰🇪", + "kiribati": "🇰🇮", + "kosovo": "🇽🇰", + "kuwait": "🇰🇼", + "kyrgyzstan": "🇰🇬", + "laos": "🇱🇦", + "latvia": "🇱🇻", + "lebanon": "🇱🇧", + "leo": "♌", + "lesotho": "🇱🇸", + "liberia": "🇱🇷", + "libra": "♎", + "libya": "🇱🇾", + "liechtenstein": "🇱🇮", + "lithuania": "🇱🇹", + "luxembourg": "🇱🇺", + "macau_sar_china": "🇲🇴", + "macedonia": "🇲🇰", + "madagascar": "🇲🇬", + "malawi": "🇲🇼", + "malaysia": "🇲🇾", + "maldives": "🇲🇻", + "mali": "🇲🇱", + "malta": "🇲🇹", + "marshall_islands": "🇲🇭", + "martinique": "🇲🇶", + "mauritania": "🇲🇷", + "mauritius": "🇲🇺", + "mayotte": "🇾🇹", + "mexico": "🇲🇽", + "micronesia": "🇫🇲", + "moldova": "🇲🇩", + "monaco": "🇲🇨", + "mongolia": "🇲🇳", + "montenegro": "🇲🇪", + "montserrat": "🇲🇸", + "morocco": "🇲🇦", + "mozambique": "🇲🇿", + "mrs._claus": "🤶", + "mrs._claus_dark_skin_tone": "🤶🏿", + "mrs._claus_light_skin_tone": "🤶🏻", + "mrs._claus_medium-dark_skin_tone": "🤶🏾", + "mrs._claus_medium-light_skin_tone": "🤶🏼", + "mrs._claus_medium_skin_tone": "🤶🏽", + "myanmar_(burma)": "🇲🇲", + "new_button": "🆕", + "ng_button": "🆖", + "namibia": "🇳🇦", + "nauru": "🇳🇷", + "nepal": "🇳🇵", + "netherlands": "🇳🇱", + "new_caledonia": "🇳🇨", + "new_zealand": "🇳🇿", + "nicaragua": "🇳🇮", + "niger": "🇳🇪", + "nigeria": "🇳🇬", + "niue": "🇳🇺", + "norfolk_island": "🇳🇫", + "north_korea": "🇰🇵", + "northern_mariana_islands": "🇲🇵", + "norway": "🇳🇴", + "ok_button": "🆗", + "ok_hand": "👌", + "ok_hand_dark_skin_tone": "👌🏿", + "ok_hand_light_skin_tone": "👌🏻", + "ok_hand_medium-dark_skin_tone": "👌🏾", + "ok_hand_medium-light_skin_tone": "👌🏼", + "ok_hand_medium_skin_tone": "👌🏽", + "on!_arrow": "🔛", + "o_button_(blood_type)": "🅾", + "oman": "🇴🇲", + "ophiuchus": "⛎", + "p_button": "🅿", + "pakistan": "🇵🇰", + "palau": "🇵🇼", + "palestinian_territories": "🇵🇸", + "panama": "🇵🇦", + "papua_new_guinea": "🇵🇬", + "paraguay": "🇵🇾", + "peru": "🇵🇪", + "philippines": "🇵🇭", + "pisces": "♓", + "pitcairn_islands": "🇵🇳", + "poland": "🇵🇱", + "portugal": "🇵🇹", + "puerto_rico": "🇵🇷", + "qatar": "🇶🇦", + "romania": "🇷🇴", + "russia": "🇷🇺", + "rwanda": "🇷🇼", + "réunion": "🇷🇪", + "soon_arrow": "🔜", + "sos_button": "🆘", + "sagittarius": "♐", + "samoa": "🇼🇸", + "san_marino": "🇸🇲", + "santa_claus": "🎅", + "santa_claus_dark_skin_tone": "🎅🏿", + "santa_claus_light_skin_tone": "🎅🏻", + "santa_claus_medium-dark_skin_tone": "🎅🏾", + "santa_claus_medium-light_skin_tone": "🎅🏼", + "santa_claus_medium_skin_tone": "🎅🏽", + "saudi_arabia": "🇸🇦", + "scorpio": "♏", + "scotland": "🏴\U000e0067\U000e0062\U000e0073\U000e0063\U000e0074\U000e007f", + "senegal": "🇸🇳", + "serbia": "🇷🇸", + "seychelles": "🇸🇨", + "sierra_leone": "🇸🇱", + "singapore": "🇸🇬", + "sint_maarten": "🇸🇽", + "slovakia": "🇸🇰", + "slovenia": "🇸🇮", + "solomon_islands": "🇸🇧", + "somalia": "🇸🇴", + "south_africa": "🇿🇦", + "south_georgia_&_south_sandwich_islands": "🇬🇸", + "south_korea": "🇰🇷", + "south_sudan": "🇸🇸", + "spain": "🇪🇸", + "sri_lanka": "🇱🇰", + "st._barthélemy": "🇧🇱", + "st._helena": "🇸🇭", + "st._kitts_&_nevis": "🇰🇳", + "st._lucia": "🇱🇨", + "st._martin": "🇲🇫", + "st._pierre_&_miquelon": "🇵🇲", + "st._vincent_&_grenadines": "🇻🇨", + "statue_of_liberty": "🗽", + "sudan": "🇸🇩", + "suriname": "🇸🇷", + "svalbard_&_jan_mayen": "🇸🇯", + "swaziland": "🇸🇿", + "sweden": "🇸🇪", + "switzerland": "🇨🇭", + "syria": "🇸🇾", + "são_tomé_&_príncipe": "🇸🇹", + "t-rex": "🦖", + "top_arrow": "🔝", + "taiwan": "🇹🇼", + "tajikistan": "🇹🇯", + "tanzania": "🇹🇿", + "taurus": "♉", + "thailand": "🇹🇭", + "timor-leste": "🇹🇱", + "togo": "🇹🇬", + "tokelau": "🇹🇰", + "tokyo_tower": "🗼", + "tonga": "🇹🇴", + "trinidad_&_tobago": "🇹🇹", + "tristan_da_cunha": "🇹🇦", + "tunisia": "🇹🇳", + "turkey": "🦃", + "turkmenistan": "🇹🇲", + "turks_&_caicos_islands": "🇹🇨", + "tuvalu": "🇹🇻", + "u.s._outlying_islands": "🇺🇲", + "u.s._virgin_islands": "🇻🇮", + "up!_button": "🆙", + "uganda": "🇺🇬", + "ukraine": "🇺🇦", + "united_arab_emirates": "🇦🇪", + "united_kingdom": "🇬🇧", + "united_nations": "🇺🇳", + "united_states": "🇺🇸", + "uruguay": "🇺🇾", + "uzbekistan": "🇺🇿", + "vs_button": "🆚", + "vanuatu": "🇻🇺", + "vatican_city": "🇻🇦", + "venezuela": "🇻🇪", + "vietnam": "🇻🇳", + "virgo": "♍", + "wales": "🏴\U000e0067\U000e0062\U000e0077\U000e006c\U000e0073\U000e007f", + "wallis_&_futuna": "🇼🇫", + "western_sahara": "🇪🇭", + "yemen": "🇾🇪", + "zambia": "🇿🇲", + "zimbabwe": "🇿🇼", + "abacus": "🧮", + "adhesive_bandage": "🩹", + "admission_tickets": "🎟", + "adult": "🧑", + "adult_dark_skin_tone": "🧑🏿", + "adult_light_skin_tone": "🧑🏻", + "adult_medium-dark_skin_tone": "🧑🏾", + "adult_medium-light_skin_tone": "🧑🏼", + "adult_medium_skin_tone": "🧑🏽", + "aerial_tramway": "🚡", + "airplane": "✈", + "airplane_arrival": "🛬", + "airplane_departure": "🛫", + "alarm_clock": "⏰", + "alembic": "⚗", + "alien": "👽", + "alien_monster": "👾", + "ambulance": "🚑", + "american_football": "🏈", + "amphora": "🏺", + "anchor": "⚓", + "anger_symbol": "💢", + "angry_face": "😠", + "angry_face_with_horns": "👿", + "anguished_face": "😧", + "ant": "🐜", + "antenna_bars": "📶", + "anxious_face_with_sweat": "😰", + "articulated_lorry": "🚛", + "artist_palette": "🎨", + "astonished_face": "😲", + "atom_symbol": "⚛", + "auto_rickshaw": "🛺", + "automobile": "🚗", + "avocado": "🥑", + "axe": "🪓", + "baby": "👶", + "baby_angel": "👼", + "baby_angel_dark_skin_tone": "👼🏿", + "baby_angel_light_skin_tone": "👼🏻", + "baby_angel_medium-dark_skin_tone": "👼🏾", + "baby_angel_medium-light_skin_tone": "👼🏼", + "baby_angel_medium_skin_tone": "👼🏽", + "baby_bottle": "🍼", + "baby_chick": "🐤", + "baby_dark_skin_tone": "👶🏿", + "baby_light_skin_tone": "👶🏻", + "baby_medium-dark_skin_tone": "👶🏾", + "baby_medium-light_skin_tone": "👶🏼", + "baby_medium_skin_tone": "👶🏽", + "baby_symbol": "🚼", + "backhand_index_pointing_down": "👇", + "backhand_index_pointing_down_dark_skin_tone": "👇🏿", + "backhand_index_pointing_down_light_skin_tone": "👇🏻", + "backhand_index_pointing_down_medium-dark_skin_tone": "👇🏾", + "backhand_index_pointing_down_medium-light_skin_tone": "👇🏼", + "backhand_index_pointing_down_medium_skin_tone": "👇🏽", + "backhand_index_pointing_left": "👈", + "backhand_index_pointing_left_dark_skin_tone": "👈🏿", + "backhand_index_pointing_left_light_skin_tone": "👈🏻", + "backhand_index_pointing_left_medium-dark_skin_tone": "👈🏾", + "backhand_index_pointing_left_medium-light_skin_tone": "👈🏼", + "backhand_index_pointing_left_medium_skin_tone": "👈🏽", + "backhand_index_pointing_right": "👉", + "backhand_index_pointing_right_dark_skin_tone": "👉🏿", + "backhand_index_pointing_right_light_skin_tone": "👉🏻", + "backhand_index_pointing_right_medium-dark_skin_tone": "👉🏾", + "backhand_index_pointing_right_medium-light_skin_tone": "👉🏼", + "backhand_index_pointing_right_medium_skin_tone": "👉🏽", + "backhand_index_pointing_up": "👆", + "backhand_index_pointing_up_dark_skin_tone": "👆🏿", + "backhand_index_pointing_up_light_skin_tone": "👆🏻", + "backhand_index_pointing_up_medium-dark_skin_tone": "👆🏾", + "backhand_index_pointing_up_medium-light_skin_tone": "👆🏼", + "backhand_index_pointing_up_medium_skin_tone": "👆🏽", + "bacon": "🥓", + "badger": "🦡", + "badminton": "🏸", + "bagel": "🥯", + "baggage_claim": "🛄", + "baguette_bread": "🥖", + "balance_scale": "⚖", + "bald": "🦲", + "bald_man": "👨\u200d🦲", + "bald_woman": "👩\u200d🦲", + "ballet_shoes": "🩰", + "balloon": "🎈", + "ballot_box_with_ballot": "🗳", + "ballot_box_with_check": "☑", + "banana": "🍌", + "banjo": "🪕", + "bank": "🏦", + "bar_chart": "📊", + "barber_pole": "💈", + "baseball": "⚾", + "basket": "🧺", + "basketball": "🏀", + "bat": "🦇", + "bathtub": "🛁", + "battery": "🔋", + "beach_with_umbrella": "🏖", + "beaming_face_with_smiling_eyes": "😁", + "bear_face": "🐻", + "bearded_person": "🧔", + "bearded_person_dark_skin_tone": "🧔🏿", + "bearded_person_light_skin_tone": "🧔🏻", + "bearded_person_medium-dark_skin_tone": "🧔🏾", + "bearded_person_medium-light_skin_tone": "🧔🏼", + "bearded_person_medium_skin_tone": "🧔🏽", + "beating_heart": "💓", + "bed": "🛏", + "beer_mug": "🍺", + "bell": "🔔", + "bell_with_slash": "🔕", + "bellhop_bell": "🛎", + "bento_box": "🍱", + "beverage_box": "🧃", + "bicycle": "🚲", + "bikini": "👙", + "billed_cap": "🧢", + "biohazard": "☣", + "bird": "🐦", + "birthday_cake": "🎂", + "black_circle": "⚫", + "black_flag": "🏴", + "black_heart": "🖤", + "black_large_square": "⬛", + "black_medium-small_square": "◾", + "black_medium_square": "◼", + "black_nib": "✒", + "black_small_square": "▪", + "black_square_button": "🔲", + "blond-haired_man": "👱\u200d♂️", + "blond-haired_man_dark_skin_tone": "👱🏿\u200d♂️", + "blond-haired_man_light_skin_tone": "👱🏻\u200d♂️", + "blond-haired_man_medium-dark_skin_tone": "👱🏾\u200d♂️", + "blond-haired_man_medium-light_skin_tone": "👱🏼\u200d♂️", + "blond-haired_man_medium_skin_tone": "👱🏽\u200d♂️", + "blond-haired_person": "👱", + "blond-haired_person_dark_skin_tone": "👱🏿", + "blond-haired_person_light_skin_tone": "👱🏻", + "blond-haired_person_medium-dark_skin_tone": "👱🏾", + "blond-haired_person_medium-light_skin_tone": "👱🏼", + "blond-haired_person_medium_skin_tone": "👱🏽", + "blond-haired_woman": "👱\u200d♀️", + "blond-haired_woman_dark_skin_tone": "👱🏿\u200d♀️", + "blond-haired_woman_light_skin_tone": "👱🏻\u200d♀️", + "blond-haired_woman_medium-dark_skin_tone": "👱🏾\u200d♀️", + "blond-haired_woman_medium-light_skin_tone": "👱🏼\u200d♀️", + "blond-haired_woman_medium_skin_tone": "👱🏽\u200d♀️", + "blossom": "🌼", + "blowfish": "🐡", + "blue_book": "📘", + "blue_circle": "🔵", + "blue_heart": "💙", + "blue_square": "🟦", + "boar": "🐗", + "bomb": "💣", + "bone": "🦴", + "bookmark": "🔖", + "bookmark_tabs": "📑", + "books": "📚", + "bottle_with_popping_cork": "🍾", + "bouquet": "💐", + "bow_and_arrow": "🏹", + "bowl_with_spoon": "🥣", + "bowling": "🎳", + "boxing_glove": "🥊", + "boy": "👦", + "boy_dark_skin_tone": "👦🏿", + "boy_light_skin_tone": "👦🏻", + "boy_medium-dark_skin_tone": "👦🏾", + "boy_medium-light_skin_tone": "👦🏼", + "boy_medium_skin_tone": "👦🏽", + "brain": "🧠", + "bread": "🍞", + "breast-feeding": "🤱", + "breast-feeding_dark_skin_tone": "🤱🏿", + "breast-feeding_light_skin_tone": "🤱🏻", + "breast-feeding_medium-dark_skin_tone": "🤱🏾", + "breast-feeding_medium-light_skin_tone": "🤱🏼", + "breast-feeding_medium_skin_tone": "🤱🏽", + "brick": "🧱", + "bride_with_veil": "👰", + "bride_with_veil_dark_skin_tone": "👰🏿", + "bride_with_veil_light_skin_tone": "👰🏻", + "bride_with_veil_medium-dark_skin_tone": "👰🏾", + "bride_with_veil_medium-light_skin_tone": "👰🏼", + "bride_with_veil_medium_skin_tone": "👰🏽", + "bridge_at_night": "🌉", + "briefcase": "💼", + "briefs": "🩲", + "bright_button": "🔆", + "broccoli": "🥦", + "broken_heart": "💔", + "broom": "🧹", + "brown_circle": "🟤", + "brown_heart": "🤎", + "brown_square": "🟫", + "bug": "🐛", + "building_construction": "🏗", + "bullet_train": "🚅", + "burrito": "🌯", + "bus": "🚌", + "bus_stop": "🚏", + "bust_in_silhouette": "👤", + "busts_in_silhouette": "👥", + "butter": "🧈", + "butterfly": "🦋", + "cactus": "🌵", + "calendar": "📆", + "call_me_hand": "🤙", + "call_me_hand_dark_skin_tone": "🤙🏿", + "call_me_hand_light_skin_tone": "🤙🏻", + "call_me_hand_medium-dark_skin_tone": "🤙🏾", + "call_me_hand_medium-light_skin_tone": "🤙🏼", + "call_me_hand_medium_skin_tone": "🤙🏽", + "camel": "🐫", + "camera": "📷", + "camera_with_flash": "📸", + "camping": "🏕", + "candle": "🕯", + "candy": "🍬", + "canned_food": "🥫", + "canoe": "🛶", + "card_file_box": "🗃", + "card_index": "📇", + "card_index_dividers": "🗂", + "carousel_horse": "🎠", + "carp_streamer": "🎏", + "carrot": "🥕", + "castle": "🏰", + "cat": "🐱", + "cat_face": "🐱", + "cat_face_with_tears_of_joy": "😹", + "cat_face_with_wry_smile": "😼", + "chains": "⛓", + "chair": "🪑", + "chart_decreasing": "📉", + "chart_increasing": "📈", + "chart_increasing_with_yen": "💹", + "cheese_wedge": "🧀", + "chequered_flag": "🏁", + "cherries": "🍒", + "cherry_blossom": "🌸", + "chess_pawn": "♟", + "chestnut": "🌰", + "chicken": "🐔", + "child": "🧒", + "child_dark_skin_tone": "🧒🏿", + "child_light_skin_tone": "🧒🏻", + "child_medium-dark_skin_tone": "🧒🏾", + "child_medium-light_skin_tone": "🧒🏼", + "child_medium_skin_tone": "🧒🏽", + "children_crossing": "🚸", + "chipmunk": "🐿", + "chocolate_bar": "🍫", + "chopsticks": "🥢", + "church": "⛪", + "cigarette": "🚬", + "cinema": "🎦", + "circled_m": "Ⓜ", + "circus_tent": "🎪", + "cityscape": "🏙", + "cityscape_at_dusk": "🌆", + "clamp": "🗜", + "clapper_board": "🎬", + "clapping_hands": "👏", + "clapping_hands_dark_skin_tone": "👏🏿", + "clapping_hands_light_skin_tone": "👏🏻", + "clapping_hands_medium-dark_skin_tone": "👏🏾", + "clapping_hands_medium-light_skin_tone": "👏🏼", + "clapping_hands_medium_skin_tone": "👏🏽", + "classical_building": "🏛", + "clinking_beer_mugs": "🍻", + "clinking_glasses": "🥂", + "clipboard": "📋", + "clockwise_vertical_arrows": "🔃", + "closed_book": "📕", + "closed_mailbox_with_lowered_flag": "📪", + "closed_mailbox_with_raised_flag": "📫", + "closed_umbrella": "🌂", + "cloud": "☁", + "cloud_with_lightning": "🌩", + "cloud_with_lightning_and_rain": "⛈", + "cloud_with_rain": "🌧", + "cloud_with_snow": "🌨", + "clown_face": "🤡", + "club_suit": "♣", + "clutch_bag": "👝", + "coat": "🧥", + "cocktail_glass": "🍸", + "coconut": "🥥", + "coffin": "⚰", + "cold_face": "🥶", + "collision": "💥", + "comet": "☄", + "compass": "🧭", + "computer_disk": "💽", + "computer_mouse": "🖱", + "confetti_ball": "🎊", + "confounded_face": "😖", + "confused_face": "😕", + "construction": "🚧", + "construction_worker": "👷", + "construction_worker_dark_skin_tone": "👷🏿", + "construction_worker_light_skin_tone": "👷🏻", + "construction_worker_medium-dark_skin_tone": "👷🏾", + "construction_worker_medium-light_skin_tone": "👷🏼", + "construction_worker_medium_skin_tone": "👷🏽", + "control_knobs": "🎛", + "convenience_store": "🏪", + "cooked_rice": "🍚", + "cookie": "🍪", + "cooking": "🍳", + "copyright": "©", + "couch_and_lamp": "🛋", + "counterclockwise_arrows_button": "🔄", + "couple_with_heart": "💑", + "couple_with_heart_man_man": "👨\u200d❤️\u200d👨", + "couple_with_heart_woman_man": "👩\u200d❤️\u200d👨", + "couple_with_heart_woman_woman": "👩\u200d❤️\u200d👩", + "cow": "🐮", + "cow_face": "🐮", + "cowboy_hat_face": "🤠", + "crab": "🦀", + "crayon": "🖍", + "credit_card": "💳", + "crescent_moon": "🌙", + "cricket": "🦗", + "cricket_game": "🏏", + "crocodile": "🐊", + "croissant": "🥐", + "cross_mark": "❌", + "cross_mark_button": "❎", + "crossed_fingers": "🤞", + "crossed_fingers_dark_skin_tone": "🤞🏿", + "crossed_fingers_light_skin_tone": "🤞🏻", + "crossed_fingers_medium-dark_skin_tone": "🤞🏾", + "crossed_fingers_medium-light_skin_tone": "🤞🏼", + "crossed_fingers_medium_skin_tone": "🤞🏽", + "crossed_flags": "🎌", + "crossed_swords": "⚔", + "crown": "👑", + "crying_cat_face": "😿", + "crying_face": "😢", + "crystal_ball": "🔮", + "cucumber": "🥒", + "cupcake": "🧁", + "cup_with_straw": "🥤", + "curling_stone": "🥌", + "curly_hair": "🦱", + "curly-haired_man": "👨\u200d🦱", + "curly-haired_woman": "👩\u200d🦱", + "curly_loop": "➰", + "currency_exchange": "💱", + "curry_rice": "🍛", + "custard": "🍮", + "customs": "🛃", + "cut_of_meat": "🥩", + "cyclone": "🌀", + "dagger": "🗡", + "dango": "🍡", + "dashing_away": "💨", + "deaf_person": "🧏", + "deciduous_tree": "🌳", + "deer": "🦌", + "delivery_truck": "🚚", + "department_store": "🏬", + "derelict_house": "🏚", + "desert": "🏜", + "desert_island": "🏝", + "desktop_computer": "🖥", + "detective": "🕵", + "detective_dark_skin_tone": "🕵🏿", + "detective_light_skin_tone": "🕵🏻", + "detective_medium-dark_skin_tone": "🕵🏾", + "detective_medium-light_skin_tone": "🕵🏼", + "detective_medium_skin_tone": "🕵🏽", + "diamond_suit": "♦", + "diamond_with_a_dot": "💠", + "dim_button": "🔅", + "direct_hit": "🎯", + "disappointed_face": "😞", + "diving_mask": "🤿", + "diya_lamp": "🪔", + "dizzy": "💫", + "dizzy_face": "😵", + "dna": "🧬", + "dog": "🐶", + "dog_face": "🐶", + "dollar_banknote": "💵", + "dolphin": "🐬", + "door": "🚪", + "dotted_six-pointed_star": "🔯", + "double_curly_loop": "➿", + "double_exclamation_mark": "‼", + "doughnut": "🍩", + "dove": "🕊", + "down-left_arrow": "↙", + "down-right_arrow": "↘", + "down_arrow": "⬇", + "downcast_face_with_sweat": "😓", + "downwards_button": "🔽", + "dragon": "🐉", + "dragon_face": "🐲", + "dress": "👗", + "drooling_face": "🤤", + "drop_of_blood": "🩸", + "droplet": "💧", + "drum": "🥁", + "duck": "🦆", + "dumpling": "🥟", + "dvd": "📀", + "e-mail": "📧", + "eagle": "🦅", + "ear": "👂", + "ear_dark_skin_tone": "👂🏿", + "ear_light_skin_tone": "👂🏻", + "ear_medium-dark_skin_tone": "👂🏾", + "ear_medium-light_skin_tone": "👂🏼", + "ear_medium_skin_tone": "👂🏽", + "ear_of_corn": "🌽", + "ear_with_hearing_aid": "🦻", + "egg": "🍳", + "eggplant": "🍆", + "eight-pointed_star": "✴", + "eight-spoked_asterisk": "✳", + "eight-thirty": "🕣", + "eight_o’clock": "🕗", + "eject_button": "⏏", + "electric_plug": "🔌", + "elephant": "🐘", + "eleven-thirty": "🕦", + "eleven_o’clock": "🕚", + "elf": "🧝", + "elf_dark_skin_tone": "🧝🏿", + "elf_light_skin_tone": "🧝🏻", + "elf_medium-dark_skin_tone": "🧝🏾", + "elf_medium-light_skin_tone": "🧝🏼", + "elf_medium_skin_tone": "🧝🏽", + "envelope": "✉", + "envelope_with_arrow": "📩", + "euro_banknote": "💶", + "evergreen_tree": "🌲", + "ewe": "🐑", + "exclamation_mark": "❗", + "exclamation_question_mark": "⁉", + "exploding_head": "🤯", + "expressionless_face": "😑", + "eye": "👁", + "eye_in_speech_bubble": "👁️\u200d🗨️", + "eyes": "👀", + "face_blowing_a_kiss": "😘", + "face_savoring_food": "😋", + "face_screaming_in_fear": "😱", + "face_vomiting": "🤮", + "face_with_hand_over_mouth": "🤭", + "face_with_head-bandage": "🤕", + "face_with_medical_mask": "😷", + "face_with_monocle": "🧐", + "face_with_open_mouth": "😮", + "face_with_raised_eyebrow": "🤨", + "face_with_rolling_eyes": "🙄", + "face_with_steam_from_nose": "😤", + "face_with_symbols_on_mouth": "🤬", + "face_with_tears_of_joy": "😂", + "face_with_thermometer": "🤒", + "face_with_tongue": "😛", + "face_without_mouth": "😶", + "factory": "🏭", + "fairy": "🧚", + "fairy_dark_skin_tone": "🧚🏿", + "fairy_light_skin_tone": "🧚🏻", + "fairy_medium-dark_skin_tone": "🧚🏾", + "fairy_medium-light_skin_tone": "🧚🏼", + "fairy_medium_skin_tone": "🧚🏽", + "falafel": "🧆", + "fallen_leaf": "🍂", + "family": "👪", + "family_man_boy": "👨\u200d👦", + "family_man_boy_boy": "👨\u200d👦\u200d👦", + "family_man_girl": "👨\u200d👧", + "family_man_girl_boy": "👨\u200d👧\u200d👦", + "family_man_girl_girl": "👨\u200d👧\u200d👧", + "family_man_man_boy": "👨\u200d👨\u200d👦", + "family_man_man_boy_boy": "👨\u200d👨\u200d👦\u200d👦", + "family_man_man_girl": "👨\u200d👨\u200d👧", + "family_man_man_girl_boy": "👨\u200d👨\u200d👧\u200d👦", + "family_man_man_girl_girl": "👨\u200d👨\u200d👧\u200d👧", + "family_man_woman_boy": "👨\u200d👩\u200d👦", + "family_man_woman_boy_boy": "👨\u200d👩\u200d👦\u200d👦", + "family_man_woman_girl": "👨\u200d👩\u200d👧", + "family_man_woman_girl_boy": "👨\u200d👩\u200d👧\u200d👦", + "family_man_woman_girl_girl": "👨\u200d👩\u200d👧\u200d👧", + "family_woman_boy": "👩\u200d👦", + "family_woman_boy_boy": "👩\u200d👦\u200d👦", + "family_woman_girl": "👩\u200d👧", + "family_woman_girl_boy": "👩\u200d👧\u200d👦", + "family_woman_girl_girl": "👩\u200d👧\u200d👧", + "family_woman_woman_boy": "👩\u200d👩\u200d👦", + "family_woman_woman_boy_boy": "👩\u200d👩\u200d👦\u200d👦", + "family_woman_woman_girl": "👩\u200d👩\u200d👧", + "family_woman_woman_girl_boy": "👩\u200d👩\u200d👧\u200d👦", + "family_woman_woman_girl_girl": "👩\u200d👩\u200d👧\u200d👧", + "fast-forward_button": "⏩", + "fast_down_button": "⏬", + "fast_reverse_button": "⏪", + "fast_up_button": "⏫", + "fax_machine": "📠", + "fearful_face": "😨", + "female_sign": "♀", + "ferris_wheel": "🎡", + "ferry": "⛴", + "field_hockey": "🏑", + "file_cabinet": "🗄", + "file_folder": "📁", + "film_frames": "🎞", + "film_projector": "📽", + "fire": "🔥", + "fire_extinguisher": "🧯", + "firecracker": "🧨", + "fire_engine": "🚒", + "fireworks": "🎆", + "first_quarter_moon": "🌓", + "first_quarter_moon_face": "🌛", + "fish": "🐟", + "fish_cake_with_swirl": "🍥", + "fishing_pole": "🎣", + "five-thirty": "🕠", + "five_o’clock": "🕔", + "flag_in_hole": "⛳", + "flamingo": "🦩", + "flashlight": "🔦", + "flat_shoe": "🥿", + "fleur-de-lis": "⚜", + "flexed_biceps": "💪", + "flexed_biceps_dark_skin_tone": "💪🏿", + "flexed_biceps_light_skin_tone": "💪🏻", + "flexed_biceps_medium-dark_skin_tone": "💪🏾", + "flexed_biceps_medium-light_skin_tone": "💪🏼", + "flexed_biceps_medium_skin_tone": "💪🏽", + "floppy_disk": "💾", + "flower_playing_cards": "🎴", + "flushed_face": "😳", + "flying_disc": "🥏", + "flying_saucer": "🛸", + "fog": "🌫", + "foggy": "🌁", + "folded_hands": "🙏", + "folded_hands_dark_skin_tone": "🙏🏿", + "folded_hands_light_skin_tone": "🙏🏻", + "folded_hands_medium-dark_skin_tone": "🙏🏾", + "folded_hands_medium-light_skin_tone": "🙏🏼", + "folded_hands_medium_skin_tone": "🙏🏽", + "foot": "🦶", + "footprints": "👣", + "fork_and_knife": "🍴", + "fork_and_knife_with_plate": "🍽", + "fortune_cookie": "🥠", + "fountain": "⛲", + "fountain_pen": "🖋", + "four-thirty": "🕟", + "four_leaf_clover": "🍀", + "four_o’clock": "🕓", + "fox_face": "🦊", + "framed_picture": "🖼", + "french_fries": "🍟", + "fried_shrimp": "🍤", + "frog_face": "🐸", + "front-facing_baby_chick": "🐥", + "frowning_face": "☹", + "frowning_face_with_open_mouth": "😦", + "fuel_pump": "⛽", + "full_moon": "🌕", + "full_moon_face": "🌝", + "funeral_urn": "⚱", + "game_die": "🎲", + "garlic": "🧄", + "gear": "⚙", + "gem_stone": "💎", + "genie": "🧞", + "ghost": "👻", + "giraffe": "🦒", + "girl": "👧", + "girl_dark_skin_tone": "👧🏿", + "girl_light_skin_tone": "👧🏻", + "girl_medium-dark_skin_tone": "👧🏾", + "girl_medium-light_skin_tone": "👧🏼", + "girl_medium_skin_tone": "👧🏽", + "glass_of_milk": "🥛", + "glasses": "👓", + "globe_showing_americas": "🌎", + "globe_showing_asia-australia": "🌏", + "globe_showing_europe-africa": "🌍", + "globe_with_meridians": "🌐", + "gloves": "🧤", + "glowing_star": "🌟", + "goal_net": "🥅", + "goat": "🐐", + "goblin": "👺", + "goggles": "🥽", + "gorilla": "🦍", + "graduation_cap": "🎓", + "grapes": "🍇", + "green_apple": "🍏", + "green_book": "📗", + "green_circle": "🟢", + "green_heart": "💚", + "green_salad": "🥗", + "green_square": "🟩", + "grimacing_face": "😬", + "grinning_cat_face": "😺", + "grinning_cat_face_with_smiling_eyes": "😸", + "grinning_face": "😀", + "grinning_face_with_big_eyes": "😃", + "grinning_face_with_smiling_eyes": "😄", + "grinning_face_with_sweat": "😅", + "grinning_squinting_face": "😆", + "growing_heart": "💗", + "guard": "💂", + "guard_dark_skin_tone": "💂🏿", + "guard_light_skin_tone": "💂🏻", + "guard_medium-dark_skin_tone": "💂🏾", + "guard_medium-light_skin_tone": "💂🏼", + "guard_medium_skin_tone": "💂🏽", + "guide_dog": "🦮", + "guitar": "🎸", + "hamburger": "🍔", + "hammer": "🔨", + "hammer_and_pick": "⚒", + "hammer_and_wrench": "🛠", + "hamster_face": "🐹", + "hand_with_fingers_splayed": "🖐", + "hand_with_fingers_splayed_dark_skin_tone": "🖐🏿", + "hand_with_fingers_splayed_light_skin_tone": "🖐🏻", + "hand_with_fingers_splayed_medium-dark_skin_tone": "🖐🏾", + "hand_with_fingers_splayed_medium-light_skin_tone": "🖐🏼", + "hand_with_fingers_splayed_medium_skin_tone": "🖐🏽", + "handbag": "👜", + "handshake": "🤝", + "hatching_chick": "🐣", + "headphone": "🎧", + "hear-no-evil_monkey": "🙉", + "heart_decoration": "💟", + "heart_suit": "♥", + "heart_with_arrow": "💘", + "heart_with_ribbon": "💝", + "heavy_check_mark": "✔", + "heavy_division_sign": "➗", + "heavy_dollar_sign": "💲", + "heavy_heart_exclamation": "❣", + "heavy_large_circle": "⭕", + "heavy_minus_sign": "➖", + "heavy_multiplication_x": "✖", + "heavy_plus_sign": "➕", + "hedgehog": "🦔", + "helicopter": "🚁", + "herb": "🌿", + "hibiscus": "🌺", + "high-heeled_shoe": "👠", + "high-speed_train": "🚄", + "high_voltage": "⚡", + "hiking_boot": "🥾", + "hindu_temple": "🛕", + "hippopotamus": "🦛", + "hole": "🕳", + "honey_pot": "🍯", + "honeybee": "🐝", + "horizontal_traffic_light": "🚥", + "horse": "🐴", + "horse_face": "🐴", + "horse_racing": "🏇", + "horse_racing_dark_skin_tone": "🏇🏿", + "horse_racing_light_skin_tone": "🏇🏻", + "horse_racing_medium-dark_skin_tone": "🏇🏾", + "horse_racing_medium-light_skin_tone": "🏇🏼", + "horse_racing_medium_skin_tone": "🏇🏽", + "hospital": "🏥", + "hot_beverage": "☕", + "hot_dog": "🌭", + "hot_face": "🥵", + "hot_pepper": "🌶", + "hot_springs": "♨", + "hotel": "🏨", + "hourglass_done": "⌛", + "hourglass_not_done": "⏳", + "house": "🏠", + "house_with_garden": "🏡", + "houses": "🏘", + "hugging_face": "🤗", + "hundred_points": "💯", + "hushed_face": "😯", + "ice": "🧊", + "ice_cream": "🍨", + "ice_hockey": "🏒", + "ice_skate": "⛸", + "inbox_tray": "📥", + "incoming_envelope": "📨", + "index_pointing_up": "☝", + "index_pointing_up_dark_skin_tone": "☝🏿", + "index_pointing_up_light_skin_tone": "☝🏻", + "index_pointing_up_medium-dark_skin_tone": "☝🏾", + "index_pointing_up_medium-light_skin_tone": "☝🏼", + "index_pointing_up_medium_skin_tone": "☝🏽", + "infinity": "♾", + "information": "ℹ", + "input_latin_letters": "🔤", + "input_latin_lowercase": "🔡", + "input_latin_uppercase": "🔠", + "input_numbers": "🔢", + "input_symbols": "🔣", + "jack-o-lantern": "🎃", + "jeans": "👖", + "jigsaw": "🧩", + "joker": "🃏", + "joystick": "🕹", + "kaaba": "🕋", + "kangaroo": "🦘", + "key": "🔑", + "keyboard": "⌨", + "keycap_#": "#️⃣", + "keycap_*": "*️⃣", + "keycap_0": "0️⃣", + "keycap_1": "1️⃣", + "keycap_10": "🔟", + "keycap_2": "2️⃣", + "keycap_3": "3️⃣", + "keycap_4": "4️⃣", + "keycap_5": "5️⃣", + "keycap_6": "6️⃣", + "keycap_7": "7️⃣", + "keycap_8": "8️⃣", + "keycap_9": "9️⃣", + "kick_scooter": "🛴", + "kimono": "👘", + "kiss": "💋", + "kiss_man_man": "👨\u200d❤️\u200d💋\u200d👨", + "kiss_mark": "💋", + "kiss_woman_man": "👩\u200d❤️\u200d💋\u200d👨", + "kiss_woman_woman": "👩\u200d❤️\u200d💋\u200d👩", + "kissing_cat_face": "😽", + "kissing_face": "😗", + "kissing_face_with_closed_eyes": "😚", + "kissing_face_with_smiling_eyes": "😙", + "kitchen_knife": "🔪", + "kite": "🪁", + "kiwi_fruit": "🥝", + "koala": "🐨", + "lab_coat": "🥼", + "label": "🏷", + "lacrosse": "🥍", + "lady_beetle": "🐞", + "laptop_computer": "💻", + "large_blue_diamond": "🔷", + "large_orange_diamond": "🔶", + "last_quarter_moon": "🌗", + "last_quarter_moon_face": "🌜", + "last_track_button": "⏮", + "latin_cross": "✝", + "leaf_fluttering_in_wind": "🍃", + "leafy_green": "🥬", + "ledger": "📒", + "left-facing_fist": "🤛", + "left-facing_fist_dark_skin_tone": "🤛🏿", + "left-facing_fist_light_skin_tone": "🤛🏻", + "left-facing_fist_medium-dark_skin_tone": "🤛🏾", + "left-facing_fist_medium-light_skin_tone": "🤛🏼", + "left-facing_fist_medium_skin_tone": "🤛🏽", + "left-right_arrow": "↔", + "left_arrow": "⬅", + "left_arrow_curving_right": "↪", + "left_luggage": "🛅", + "left_speech_bubble": "🗨", + "leg": "🦵", + "lemon": "🍋", + "leopard": "🐆", + "level_slider": "🎚", + "light_bulb": "💡", + "light_rail": "🚈", + "link": "🔗", + "linked_paperclips": "🖇", + "lion_face": "🦁", + "lipstick": "💄", + "litter_in_bin_sign": "🚮", + "lizard": "🦎", + "llama": "🦙", + "lobster": "🦞", + "locked": "🔒", + "locked_with_key": "🔐", + "locked_with_pen": "🔏", + "locomotive": "🚂", + "lollipop": "🍭", + "lotion_bottle": "🧴", + "loudly_crying_face": "😭", + "loudspeaker": "📢", + "love-you_gesture": "🤟", + "love-you_gesture_dark_skin_tone": "🤟🏿", + "love-you_gesture_light_skin_tone": "🤟🏻", + "love-you_gesture_medium-dark_skin_tone": "🤟🏾", + "love-you_gesture_medium-light_skin_tone": "🤟🏼", + "love-you_gesture_medium_skin_tone": "🤟🏽", + "love_hotel": "🏩", + "love_letter": "💌", + "luggage": "🧳", + "lying_face": "🤥", + "mage": "🧙", + "mage_dark_skin_tone": "🧙🏿", + "mage_light_skin_tone": "🧙🏻", + "mage_medium-dark_skin_tone": "🧙🏾", + "mage_medium-light_skin_tone": "🧙🏼", + "mage_medium_skin_tone": "🧙🏽", + "magnet": "🧲", + "magnifying_glass_tilted_left": "🔍", + "magnifying_glass_tilted_right": "🔎", + "mahjong_red_dragon": "🀄", + "male_sign": "♂", + "man": "👨", + "man_and_woman_holding_hands": "👫", + "man_artist": "👨\u200d🎨", + "man_artist_dark_skin_tone": "👨🏿\u200d🎨", + "man_artist_light_skin_tone": "👨🏻\u200d🎨", + "man_artist_medium-dark_skin_tone": "👨🏾\u200d🎨", + "man_artist_medium-light_skin_tone": "👨🏼\u200d🎨", + "man_artist_medium_skin_tone": "👨🏽\u200d🎨", + "man_astronaut": "👨\u200d🚀", + "man_astronaut_dark_skin_tone": "👨🏿\u200d🚀", + "man_astronaut_light_skin_tone": "👨🏻\u200d🚀", + "man_astronaut_medium-dark_skin_tone": "👨🏾\u200d🚀", + "man_astronaut_medium-light_skin_tone": "👨🏼\u200d🚀", + "man_astronaut_medium_skin_tone": "👨🏽\u200d🚀", + "man_biking": "🚴\u200d♂️", + "man_biking_dark_skin_tone": "🚴🏿\u200d♂️", + "man_biking_light_skin_tone": "🚴🏻\u200d♂️", + "man_biking_medium-dark_skin_tone": "🚴🏾\u200d♂️", + "man_biking_medium-light_skin_tone": "🚴🏼\u200d♂️", + "man_biking_medium_skin_tone": "🚴🏽\u200d♂️", + "man_bouncing_ball": "⛹️\u200d♂️", + "man_bouncing_ball_dark_skin_tone": "⛹🏿\u200d♂️", + "man_bouncing_ball_light_skin_tone": "⛹🏻\u200d♂️", + "man_bouncing_ball_medium-dark_skin_tone": "⛹🏾\u200d♂️", + "man_bouncing_ball_medium-light_skin_tone": "⛹🏼\u200d♂️", + "man_bouncing_ball_medium_skin_tone": "⛹🏽\u200d♂️", + "man_bowing": "🙇\u200d♂️", + "man_bowing_dark_skin_tone": "🙇🏿\u200d♂️", + "man_bowing_light_skin_tone": "🙇🏻\u200d♂️", + "man_bowing_medium-dark_skin_tone": "🙇🏾\u200d♂️", + "man_bowing_medium-light_skin_tone": "🙇🏼\u200d♂️", + "man_bowing_medium_skin_tone": "🙇🏽\u200d♂️", + "man_cartwheeling": "🤸\u200d♂️", + "man_cartwheeling_dark_skin_tone": "🤸🏿\u200d♂️", + "man_cartwheeling_light_skin_tone": "🤸🏻\u200d♂️", + "man_cartwheeling_medium-dark_skin_tone": "🤸🏾\u200d♂️", + "man_cartwheeling_medium-light_skin_tone": "🤸🏼\u200d♂️", + "man_cartwheeling_medium_skin_tone": "🤸🏽\u200d♂️", + "man_climbing": "🧗\u200d♂️", + "man_climbing_dark_skin_tone": "🧗🏿\u200d♂️", + "man_climbing_light_skin_tone": "🧗🏻\u200d♂️", + "man_climbing_medium-dark_skin_tone": "🧗🏾\u200d♂️", + "man_climbing_medium-light_skin_tone": "🧗🏼\u200d♂️", + "man_climbing_medium_skin_tone": "🧗🏽\u200d♂️", + "man_construction_worker": "👷\u200d♂️", + "man_construction_worker_dark_skin_tone": "👷🏿\u200d♂️", + "man_construction_worker_light_skin_tone": "👷🏻\u200d♂️", + "man_construction_worker_medium-dark_skin_tone": "👷🏾\u200d♂️", + "man_construction_worker_medium-light_skin_tone": "👷🏼\u200d♂️", + "man_construction_worker_medium_skin_tone": "👷🏽\u200d♂️", + "man_cook": "👨\u200d🍳", + "man_cook_dark_skin_tone": "👨🏿\u200d🍳", + "man_cook_light_skin_tone": "👨🏻\u200d🍳", + "man_cook_medium-dark_skin_tone": "👨🏾\u200d🍳", + "man_cook_medium-light_skin_tone": "👨🏼\u200d🍳", + "man_cook_medium_skin_tone": "👨🏽\u200d🍳", + "man_dancing": "🕺", + "man_dancing_dark_skin_tone": "🕺🏿", + "man_dancing_light_skin_tone": "🕺🏻", + "man_dancing_medium-dark_skin_tone": "🕺🏾", + "man_dancing_medium-light_skin_tone": "🕺🏼", + "man_dancing_medium_skin_tone": "🕺🏽", + "man_dark_skin_tone": "👨🏿", + "man_detective": "🕵️\u200d♂️", + "man_detective_dark_skin_tone": "🕵🏿\u200d♂️", + "man_detective_light_skin_tone": "🕵🏻\u200d♂️", + "man_detective_medium-dark_skin_tone": "🕵🏾\u200d♂️", + "man_detective_medium-light_skin_tone": "🕵🏼\u200d♂️", + "man_detective_medium_skin_tone": "🕵🏽\u200d♂️", + "man_elf": "🧝\u200d♂️", + "man_elf_dark_skin_tone": "🧝🏿\u200d♂️", + "man_elf_light_skin_tone": "🧝🏻\u200d♂️", + "man_elf_medium-dark_skin_tone": "🧝🏾\u200d♂️", + "man_elf_medium-light_skin_tone": "🧝🏼\u200d♂️", + "man_elf_medium_skin_tone": "🧝🏽\u200d♂️", + "man_facepalming": "🤦\u200d♂️", + "man_facepalming_dark_skin_tone": "🤦🏿\u200d♂️", + "man_facepalming_light_skin_tone": "🤦🏻\u200d♂️", + "man_facepalming_medium-dark_skin_tone": "🤦🏾\u200d♂️", + "man_facepalming_medium-light_skin_tone": "🤦🏼\u200d♂️", + "man_facepalming_medium_skin_tone": "🤦🏽\u200d♂️", + "man_factory_worker": "👨\u200d🏭", + "man_factory_worker_dark_skin_tone": "👨🏿\u200d🏭", + "man_factory_worker_light_skin_tone": "👨🏻\u200d🏭", + "man_factory_worker_medium-dark_skin_tone": "👨🏾\u200d🏭", + "man_factory_worker_medium-light_skin_tone": "👨🏼\u200d🏭", + "man_factory_worker_medium_skin_tone": "👨🏽\u200d🏭", + "man_fairy": "🧚\u200d♂️", + "man_fairy_dark_skin_tone": "🧚🏿\u200d♂️", + "man_fairy_light_skin_tone": "🧚🏻\u200d♂️", + "man_fairy_medium-dark_skin_tone": "🧚🏾\u200d♂️", + "man_fairy_medium-light_skin_tone": "🧚🏼\u200d♂️", + "man_fairy_medium_skin_tone": "🧚🏽\u200d♂️", + "man_farmer": "👨\u200d🌾", + "man_farmer_dark_skin_tone": "👨🏿\u200d🌾", + "man_farmer_light_skin_tone": "👨🏻\u200d🌾", + "man_farmer_medium-dark_skin_tone": "👨🏾\u200d🌾", + "man_farmer_medium-light_skin_tone": "👨🏼\u200d🌾", + "man_farmer_medium_skin_tone": "👨🏽\u200d🌾", + "man_firefighter": "👨\u200d🚒", + "man_firefighter_dark_skin_tone": "👨🏿\u200d🚒", + "man_firefighter_light_skin_tone": "👨🏻\u200d🚒", + "man_firefighter_medium-dark_skin_tone": "👨🏾\u200d🚒", + "man_firefighter_medium-light_skin_tone": "👨🏼\u200d🚒", + "man_firefighter_medium_skin_tone": "👨🏽\u200d🚒", + "man_frowning": "🙍\u200d♂️", + "man_frowning_dark_skin_tone": "🙍🏿\u200d♂️", + "man_frowning_light_skin_tone": "🙍🏻\u200d♂️", + "man_frowning_medium-dark_skin_tone": "🙍🏾\u200d♂️", + "man_frowning_medium-light_skin_tone": "🙍🏼\u200d♂️", + "man_frowning_medium_skin_tone": "🙍🏽\u200d♂️", + "man_genie": "🧞\u200d♂️", + "man_gesturing_no": "🙅\u200d♂️", + "man_gesturing_no_dark_skin_tone": "🙅🏿\u200d♂️", + "man_gesturing_no_light_skin_tone": "🙅🏻\u200d♂️", + "man_gesturing_no_medium-dark_skin_tone": "🙅🏾\u200d♂️", + "man_gesturing_no_medium-light_skin_tone": "🙅🏼\u200d♂️", + "man_gesturing_no_medium_skin_tone": "🙅🏽\u200d♂️", + "man_gesturing_ok": "🙆\u200d♂️", + "man_gesturing_ok_dark_skin_tone": "🙆🏿\u200d♂️", + "man_gesturing_ok_light_skin_tone": "🙆🏻\u200d♂️", + "man_gesturing_ok_medium-dark_skin_tone": "🙆🏾\u200d♂️", + "man_gesturing_ok_medium-light_skin_tone": "🙆🏼\u200d♂️", + "man_gesturing_ok_medium_skin_tone": "🙆🏽\u200d♂️", + "man_getting_haircut": "💇\u200d♂️", + "man_getting_haircut_dark_skin_tone": "💇🏿\u200d♂️", + "man_getting_haircut_light_skin_tone": "💇🏻\u200d♂️", + "man_getting_haircut_medium-dark_skin_tone": "💇🏾\u200d♂️", + "man_getting_haircut_medium-light_skin_tone": "💇🏼\u200d♂️", + "man_getting_haircut_medium_skin_tone": "💇🏽\u200d♂️", + "man_getting_massage": "💆\u200d♂️", + "man_getting_massage_dark_skin_tone": "💆🏿\u200d♂️", + "man_getting_massage_light_skin_tone": "💆🏻\u200d♂️", + "man_getting_massage_medium-dark_skin_tone": "💆🏾\u200d♂️", + "man_getting_massage_medium-light_skin_tone": "💆🏼\u200d♂️", + "man_getting_massage_medium_skin_tone": "💆🏽\u200d♂️", + "man_golfing": "🏌️\u200d♂️", + "man_golfing_dark_skin_tone": "🏌🏿\u200d♂️", + "man_golfing_light_skin_tone": "🏌🏻\u200d♂️", + "man_golfing_medium-dark_skin_tone": "🏌🏾\u200d♂️", + "man_golfing_medium-light_skin_tone": "🏌🏼\u200d♂️", + "man_golfing_medium_skin_tone": "🏌🏽\u200d♂️", + "man_guard": "💂\u200d♂️", + "man_guard_dark_skin_tone": "💂🏿\u200d♂️", + "man_guard_light_skin_tone": "💂🏻\u200d♂️", + "man_guard_medium-dark_skin_tone": "💂🏾\u200d♂️", + "man_guard_medium-light_skin_tone": "💂🏼\u200d♂️", + "man_guard_medium_skin_tone": "💂🏽\u200d♂️", + "man_health_worker": "👨\u200d⚕️", + "man_health_worker_dark_skin_tone": "👨🏿\u200d⚕️", + "man_health_worker_light_skin_tone": "👨🏻\u200d⚕️", + "man_health_worker_medium-dark_skin_tone": "👨🏾\u200d⚕️", + "man_health_worker_medium-light_skin_tone": "👨🏼\u200d⚕️", + "man_health_worker_medium_skin_tone": "👨🏽\u200d⚕️", + "man_in_lotus_position": "🧘\u200d♂️", + "man_in_lotus_position_dark_skin_tone": "🧘🏿\u200d♂️", + "man_in_lotus_position_light_skin_tone": "🧘🏻\u200d♂️", + "man_in_lotus_position_medium-dark_skin_tone": "🧘🏾\u200d♂️", + "man_in_lotus_position_medium-light_skin_tone": "🧘🏼\u200d♂️", + "man_in_lotus_position_medium_skin_tone": "🧘🏽\u200d♂️", + "man_in_manual_wheelchair": "👨\u200d🦽", + "man_in_motorized_wheelchair": "👨\u200d🦼", + "man_in_steamy_room": "🧖\u200d♂️", + "man_in_steamy_room_dark_skin_tone": "🧖🏿\u200d♂️", + "man_in_steamy_room_light_skin_tone": "🧖🏻\u200d♂️", + "man_in_steamy_room_medium-dark_skin_tone": "🧖🏾\u200d♂️", + "man_in_steamy_room_medium-light_skin_tone": "🧖🏼\u200d♂️", + "man_in_steamy_room_medium_skin_tone": "🧖🏽\u200d♂️", + "man_in_suit_levitating": "🕴", + "man_in_suit_levitating_dark_skin_tone": "🕴🏿", + "man_in_suit_levitating_light_skin_tone": "🕴🏻", + "man_in_suit_levitating_medium-dark_skin_tone": "🕴🏾", + "man_in_suit_levitating_medium-light_skin_tone": "🕴🏼", + "man_in_suit_levitating_medium_skin_tone": "🕴🏽", + "man_in_tuxedo": "🤵", + "man_in_tuxedo_dark_skin_tone": "🤵🏿", + "man_in_tuxedo_light_skin_tone": "🤵🏻", + "man_in_tuxedo_medium-dark_skin_tone": "🤵🏾", + "man_in_tuxedo_medium-light_skin_tone": "🤵🏼", + "man_in_tuxedo_medium_skin_tone": "🤵🏽", + "man_judge": "👨\u200d⚖️", + "man_judge_dark_skin_tone": "👨🏿\u200d⚖️", + "man_judge_light_skin_tone": "👨🏻\u200d⚖️", + "man_judge_medium-dark_skin_tone": "👨🏾\u200d⚖️", + "man_judge_medium-light_skin_tone": "👨🏼\u200d⚖️", + "man_judge_medium_skin_tone": "👨🏽\u200d⚖️", + "man_juggling": "🤹\u200d♂️", + "man_juggling_dark_skin_tone": "🤹🏿\u200d♂️", + "man_juggling_light_skin_tone": "🤹🏻\u200d♂️", + "man_juggling_medium-dark_skin_tone": "🤹🏾\u200d♂️", + "man_juggling_medium-light_skin_tone": "🤹🏼\u200d♂️", + "man_juggling_medium_skin_tone": "🤹🏽\u200d♂️", + "man_lifting_weights": "🏋️\u200d♂️", + "man_lifting_weights_dark_skin_tone": "🏋🏿\u200d♂️", + "man_lifting_weights_light_skin_tone": "🏋🏻\u200d♂️", + "man_lifting_weights_medium-dark_skin_tone": "🏋🏾\u200d♂️", + "man_lifting_weights_medium-light_skin_tone": "🏋🏼\u200d♂️", + "man_lifting_weights_medium_skin_tone": "🏋🏽\u200d♂️", + "man_light_skin_tone": "👨🏻", + "man_mage": "🧙\u200d♂️", + "man_mage_dark_skin_tone": "🧙🏿\u200d♂️", + "man_mage_light_skin_tone": "🧙🏻\u200d♂️", + "man_mage_medium-dark_skin_tone": "🧙🏾\u200d♂️", + "man_mage_medium-light_skin_tone": "🧙🏼\u200d♂️", + "man_mage_medium_skin_tone": "🧙🏽\u200d♂️", + "man_mechanic": "👨\u200d🔧", + "man_mechanic_dark_skin_tone": "👨🏿\u200d🔧", + "man_mechanic_light_skin_tone": "👨🏻\u200d🔧", + "man_mechanic_medium-dark_skin_tone": "👨🏾\u200d🔧", + "man_mechanic_medium-light_skin_tone": "👨🏼\u200d🔧", + "man_mechanic_medium_skin_tone": "👨🏽\u200d🔧", + "man_medium-dark_skin_tone": "👨🏾", + "man_medium-light_skin_tone": "👨🏼", + "man_medium_skin_tone": "👨🏽", + "man_mountain_biking": "🚵\u200d♂️", + "man_mountain_biking_dark_skin_tone": "🚵🏿\u200d♂️", + "man_mountain_biking_light_skin_tone": "🚵🏻\u200d♂️", + "man_mountain_biking_medium-dark_skin_tone": "🚵🏾\u200d♂️", + "man_mountain_biking_medium-light_skin_tone": "🚵🏼\u200d♂️", + "man_mountain_biking_medium_skin_tone": "🚵🏽\u200d♂️", + "man_office_worker": "👨\u200d💼", + "man_office_worker_dark_skin_tone": "👨🏿\u200d💼", + "man_office_worker_light_skin_tone": "👨🏻\u200d💼", + "man_office_worker_medium-dark_skin_tone": "👨🏾\u200d💼", + "man_office_worker_medium-light_skin_tone": "👨🏼\u200d💼", + "man_office_worker_medium_skin_tone": "👨🏽\u200d💼", + "man_pilot": "👨\u200d✈️", + "man_pilot_dark_skin_tone": "👨🏿\u200d✈️", + "man_pilot_light_skin_tone": "👨🏻\u200d✈️", + "man_pilot_medium-dark_skin_tone": "👨🏾\u200d✈️", + "man_pilot_medium-light_skin_tone": "👨🏼\u200d✈️", + "man_pilot_medium_skin_tone": "👨🏽\u200d✈️", + "man_playing_handball": "🤾\u200d♂️", + "man_playing_handball_dark_skin_tone": "🤾🏿\u200d♂️", + "man_playing_handball_light_skin_tone": "🤾🏻\u200d♂️", + "man_playing_handball_medium-dark_skin_tone": "🤾🏾\u200d♂️", + "man_playing_handball_medium-light_skin_tone": "🤾🏼\u200d♂️", + "man_playing_handball_medium_skin_tone": "🤾🏽\u200d♂️", + "man_playing_water_polo": "🤽\u200d♂️", + "man_playing_water_polo_dark_skin_tone": "🤽🏿\u200d♂️", + "man_playing_water_polo_light_skin_tone": "🤽🏻\u200d♂️", + "man_playing_water_polo_medium-dark_skin_tone": "🤽🏾\u200d♂️", + "man_playing_water_polo_medium-light_skin_tone": "🤽🏼\u200d♂️", + "man_playing_water_polo_medium_skin_tone": "🤽🏽\u200d♂️", + "man_police_officer": "👮\u200d♂️", + "man_police_officer_dark_skin_tone": "👮🏿\u200d♂️", + "man_police_officer_light_skin_tone": "👮🏻\u200d♂️", + "man_police_officer_medium-dark_skin_tone": "👮🏾\u200d♂️", + "man_police_officer_medium-light_skin_tone": "👮🏼\u200d♂️", + "man_police_officer_medium_skin_tone": "👮🏽\u200d♂️", + "man_pouting": "🙎\u200d♂️", + "man_pouting_dark_skin_tone": "🙎🏿\u200d♂️", + "man_pouting_light_skin_tone": "🙎🏻\u200d♂️", + "man_pouting_medium-dark_skin_tone": "🙎🏾\u200d♂️", + "man_pouting_medium-light_skin_tone": "🙎🏼\u200d♂️", + "man_pouting_medium_skin_tone": "🙎🏽\u200d♂️", + "man_raising_hand": "🙋\u200d♂️", + "man_raising_hand_dark_skin_tone": "🙋🏿\u200d♂️", + "man_raising_hand_light_skin_tone": "🙋🏻\u200d♂️", + "man_raising_hand_medium-dark_skin_tone": "🙋🏾\u200d♂️", + "man_raising_hand_medium-light_skin_tone": "🙋🏼\u200d♂️", + "man_raising_hand_medium_skin_tone": "🙋🏽\u200d♂️", + "man_rowing_boat": "🚣\u200d♂️", + "man_rowing_boat_dark_skin_tone": "🚣🏿\u200d♂️", + "man_rowing_boat_light_skin_tone": "🚣🏻\u200d♂️", + "man_rowing_boat_medium-dark_skin_tone": "🚣🏾\u200d♂️", + "man_rowing_boat_medium-light_skin_tone": "🚣🏼\u200d♂️", + "man_rowing_boat_medium_skin_tone": "🚣🏽\u200d♂️", + "man_running": "🏃\u200d♂️", + "man_running_dark_skin_tone": "🏃🏿\u200d♂️", + "man_running_light_skin_tone": "🏃🏻\u200d♂️", + "man_running_medium-dark_skin_tone": "🏃🏾\u200d♂️", + "man_running_medium-light_skin_tone": "🏃🏼\u200d♂️", + "man_running_medium_skin_tone": "🏃🏽\u200d♂️", + "man_scientist": "👨\u200d🔬", + "man_scientist_dark_skin_tone": "👨🏿\u200d🔬", + "man_scientist_light_skin_tone": "👨🏻\u200d🔬", + "man_scientist_medium-dark_skin_tone": "👨🏾\u200d🔬", + "man_scientist_medium-light_skin_tone": "👨🏼\u200d🔬", + "man_scientist_medium_skin_tone": "👨🏽\u200d🔬", + "man_shrugging": "🤷\u200d♂️", + "man_shrugging_dark_skin_tone": "🤷🏿\u200d♂️", + "man_shrugging_light_skin_tone": "🤷🏻\u200d♂️", + "man_shrugging_medium-dark_skin_tone": "🤷🏾\u200d♂️", + "man_shrugging_medium-light_skin_tone": "🤷🏼\u200d♂️", + "man_shrugging_medium_skin_tone": "🤷🏽\u200d♂️", + "man_singer": "👨\u200d🎤", + "man_singer_dark_skin_tone": "👨🏿\u200d🎤", + "man_singer_light_skin_tone": "👨🏻\u200d🎤", + "man_singer_medium-dark_skin_tone": "👨🏾\u200d🎤", + "man_singer_medium-light_skin_tone": "👨🏼\u200d🎤", + "man_singer_medium_skin_tone": "👨🏽\u200d🎤", + "man_student": "👨\u200d🎓", + "man_student_dark_skin_tone": "👨🏿\u200d🎓", + "man_student_light_skin_tone": "👨🏻\u200d🎓", + "man_student_medium-dark_skin_tone": "👨🏾\u200d🎓", + "man_student_medium-light_skin_tone": "👨🏼\u200d🎓", + "man_student_medium_skin_tone": "👨🏽\u200d🎓", + "man_surfing": "🏄\u200d♂️", + "man_surfing_dark_skin_tone": "🏄🏿\u200d♂️", + "man_surfing_light_skin_tone": "🏄🏻\u200d♂️", + "man_surfing_medium-dark_skin_tone": "🏄🏾\u200d♂️", + "man_surfing_medium-light_skin_tone": "🏄🏼\u200d♂️", + "man_surfing_medium_skin_tone": "🏄🏽\u200d♂️", + "man_swimming": "🏊\u200d♂️", + "man_swimming_dark_skin_tone": "🏊🏿\u200d♂️", + "man_swimming_light_skin_tone": "🏊🏻\u200d♂️", + "man_swimming_medium-dark_skin_tone": "🏊🏾\u200d♂️", + "man_swimming_medium-light_skin_tone": "🏊🏼\u200d♂️", + "man_swimming_medium_skin_tone": "🏊🏽\u200d♂️", + "man_teacher": "👨\u200d🏫", + "man_teacher_dark_skin_tone": "👨🏿\u200d🏫", + "man_teacher_light_skin_tone": "👨🏻\u200d🏫", + "man_teacher_medium-dark_skin_tone": "👨🏾\u200d🏫", + "man_teacher_medium-light_skin_tone": "👨🏼\u200d🏫", + "man_teacher_medium_skin_tone": "👨🏽\u200d🏫", + "man_technologist": "👨\u200d💻", + "man_technologist_dark_skin_tone": "👨🏿\u200d💻", + "man_technologist_light_skin_tone": "👨🏻\u200d💻", + "man_technologist_medium-dark_skin_tone": "👨🏾\u200d💻", + "man_technologist_medium-light_skin_tone": "👨🏼\u200d💻", + "man_technologist_medium_skin_tone": "👨🏽\u200d💻", + "man_tipping_hand": "💁\u200d♂️", + "man_tipping_hand_dark_skin_tone": "💁🏿\u200d♂️", + "man_tipping_hand_light_skin_tone": "💁🏻\u200d♂️", + "man_tipping_hand_medium-dark_skin_tone": "💁🏾\u200d♂️", + "man_tipping_hand_medium-light_skin_tone": "💁🏼\u200d♂️", + "man_tipping_hand_medium_skin_tone": "💁🏽\u200d♂️", + "man_vampire": "🧛\u200d♂️", + "man_vampire_dark_skin_tone": "🧛🏿\u200d♂️", + "man_vampire_light_skin_tone": "🧛🏻\u200d♂️", + "man_vampire_medium-dark_skin_tone": "🧛🏾\u200d♂️", + "man_vampire_medium-light_skin_tone": "🧛🏼\u200d♂️", + "man_vampire_medium_skin_tone": "🧛🏽\u200d♂️", + "man_walking": "🚶\u200d♂️", + "man_walking_dark_skin_tone": "🚶🏿\u200d♂️", + "man_walking_light_skin_tone": "🚶🏻\u200d♂️", + "man_walking_medium-dark_skin_tone": "🚶🏾\u200d♂️", + "man_walking_medium-light_skin_tone": "🚶🏼\u200d♂️", + "man_walking_medium_skin_tone": "🚶🏽\u200d♂️", + "man_wearing_turban": "👳\u200d♂️", + "man_wearing_turban_dark_skin_tone": "👳🏿\u200d♂️", + "man_wearing_turban_light_skin_tone": "👳🏻\u200d♂️", + "man_wearing_turban_medium-dark_skin_tone": "👳🏾\u200d♂️", + "man_wearing_turban_medium-light_skin_tone": "👳🏼\u200d♂️", + "man_wearing_turban_medium_skin_tone": "👳🏽\u200d♂️", + "man_with_probing_cane": "👨\u200d🦯", + "man_with_chinese_cap": "👲", + "man_with_chinese_cap_dark_skin_tone": "👲🏿", + "man_with_chinese_cap_light_skin_tone": "👲🏻", + "man_with_chinese_cap_medium-dark_skin_tone": "👲🏾", + "man_with_chinese_cap_medium-light_skin_tone": "👲🏼", + "man_with_chinese_cap_medium_skin_tone": "👲🏽", + "man_zombie": "🧟\u200d♂️", + "mango": "🥭", + "mantelpiece_clock": "🕰", + "manual_wheelchair": "🦽", + "man’s_shoe": "👞", + "map_of_japan": "🗾", + "maple_leaf": "🍁", + "martial_arts_uniform": "🥋", + "mate": "🧉", + "meat_on_bone": "🍖", + "mechanical_arm": "🦾", + "mechanical_leg": "🦿", + "medical_symbol": "⚕", + "megaphone": "📣", + "melon": "🍈", + "memo": "📝", + "men_with_bunny_ears": "👯\u200d♂️", + "men_wrestling": "🤼\u200d♂️", + "menorah": "🕎", + "men’s_room": "🚹", + "mermaid": "🧜\u200d♀️", + "mermaid_dark_skin_tone": "🧜🏿\u200d♀️", + "mermaid_light_skin_tone": "🧜🏻\u200d♀️", + "mermaid_medium-dark_skin_tone": "🧜🏾\u200d♀️", + "mermaid_medium-light_skin_tone": "🧜🏼\u200d♀️", + "mermaid_medium_skin_tone": "🧜🏽\u200d♀️", + "merman": "🧜\u200d♂️", + "merman_dark_skin_tone": "🧜🏿\u200d♂️", + "merman_light_skin_tone": "🧜🏻\u200d♂️", + "merman_medium-dark_skin_tone": "🧜🏾\u200d♂️", + "merman_medium-light_skin_tone": "🧜🏼\u200d♂️", + "merman_medium_skin_tone": "🧜🏽\u200d♂️", + "merperson": "🧜", + "merperson_dark_skin_tone": "🧜🏿", + "merperson_light_skin_tone": "🧜🏻", + "merperson_medium-dark_skin_tone": "🧜🏾", + "merperson_medium-light_skin_tone": "🧜🏼", + "merperson_medium_skin_tone": "🧜🏽", + "metro": "🚇", + "microbe": "🦠", + "microphone": "🎤", + "microscope": "🔬", + "middle_finger": "🖕", + "middle_finger_dark_skin_tone": "🖕🏿", + "middle_finger_light_skin_tone": "🖕🏻", + "middle_finger_medium-dark_skin_tone": "🖕🏾", + "middle_finger_medium-light_skin_tone": "🖕🏼", + "middle_finger_medium_skin_tone": "🖕🏽", + "military_medal": "🎖", + "milky_way": "🌌", + "minibus": "🚐", + "moai": "🗿", + "mobile_phone": "📱", + "mobile_phone_off": "📴", + "mobile_phone_with_arrow": "📲", + "money-mouth_face": "🤑", + "money_bag": "💰", + "money_with_wings": "💸", + "monkey": "🐒", + "monkey_face": "🐵", + "monorail": "🚝", + "moon_cake": "🥮", + "moon_viewing_ceremony": "🎑", + "mosque": "🕌", + "mosquito": "🦟", + "motor_boat": "🛥", + "motor_scooter": "🛵", + "motorcycle": "🏍", + "motorized_wheelchair": "🦼", + "motorway": "🛣", + "mount_fuji": "🗻", + "mountain": "⛰", + "mountain_cableway": "🚠", + "mountain_railway": "🚞", + "mouse": "🐭", + "mouse_face": "🐭", + "mouth": "👄", + "movie_camera": "🎥", + "mushroom": "🍄", + "musical_keyboard": "🎹", + "musical_note": "🎵", + "musical_notes": "🎶", + "musical_score": "🎼", + "muted_speaker": "🔇", + "nail_polish": "💅", + "nail_polish_dark_skin_tone": "💅🏿", + "nail_polish_light_skin_tone": "💅🏻", + "nail_polish_medium-dark_skin_tone": "💅🏾", + "nail_polish_medium-light_skin_tone": "💅🏼", + "nail_polish_medium_skin_tone": "💅🏽", + "name_badge": "📛", + "national_park": "🏞", + "nauseated_face": "🤢", + "nazar_amulet": "🧿", + "necktie": "👔", + "nerd_face": "🤓", + "neutral_face": "😐", + "new_moon": "🌑", + "new_moon_face": "🌚", + "newspaper": "📰", + "next_track_button": "⏭", + "night_with_stars": "🌃", + "nine-thirty": "🕤", + "nine_o’clock": "🕘", + "no_bicycles": "🚳", + "no_entry": "⛔", + "no_littering": "🚯", + "no_mobile_phones": "📵", + "no_one_under_eighteen": "🔞", + "no_pedestrians": "🚷", + "no_smoking": "🚭", + "non-potable_water": "🚱", + "nose": "👃", + "nose_dark_skin_tone": "👃🏿", + "nose_light_skin_tone": "👃🏻", + "nose_medium-dark_skin_tone": "👃🏾", + "nose_medium-light_skin_tone": "👃🏼", + "nose_medium_skin_tone": "👃🏽", + "notebook": "📓", + "notebook_with_decorative_cover": "📔", + "nut_and_bolt": "🔩", + "octopus": "🐙", + "oden": "🍢", + "office_building": "🏢", + "ogre": "👹", + "oil_drum": "🛢", + "old_key": "🗝", + "old_man": "👴", + "old_man_dark_skin_tone": "👴🏿", + "old_man_light_skin_tone": "👴🏻", + "old_man_medium-dark_skin_tone": "👴🏾", + "old_man_medium-light_skin_tone": "👴🏼", + "old_man_medium_skin_tone": "👴🏽", + "old_woman": "👵", + "old_woman_dark_skin_tone": "👵🏿", + "old_woman_light_skin_tone": "👵🏻", + "old_woman_medium-dark_skin_tone": "👵🏾", + "old_woman_medium-light_skin_tone": "👵🏼", + "old_woman_medium_skin_tone": "👵🏽", + "older_adult": "🧓", + "older_adult_dark_skin_tone": "🧓🏿", + "older_adult_light_skin_tone": "🧓🏻", + "older_adult_medium-dark_skin_tone": "🧓🏾", + "older_adult_medium-light_skin_tone": "🧓🏼", + "older_adult_medium_skin_tone": "🧓🏽", + "om": "🕉", + "oncoming_automobile": "🚘", + "oncoming_bus": "🚍", + "oncoming_fist": "👊", + "oncoming_fist_dark_skin_tone": "👊🏿", + "oncoming_fist_light_skin_tone": "👊🏻", + "oncoming_fist_medium-dark_skin_tone": "👊🏾", + "oncoming_fist_medium-light_skin_tone": "👊🏼", + "oncoming_fist_medium_skin_tone": "👊🏽", + "oncoming_police_car": "🚔", + "oncoming_taxi": "🚖", + "one-piece_swimsuit": "🩱", + "one-thirty": "🕜", + "one_o’clock": "🕐", + "onion": "🧅", + "open_book": "📖", + "open_file_folder": "📂", + "open_hands": "👐", + "open_hands_dark_skin_tone": "👐🏿", + "open_hands_light_skin_tone": "👐🏻", + "open_hands_medium-dark_skin_tone": "👐🏾", + "open_hands_medium-light_skin_tone": "👐🏼", + "open_hands_medium_skin_tone": "👐🏽", + "open_mailbox_with_lowered_flag": "📭", + "open_mailbox_with_raised_flag": "📬", + "optical_disk": "💿", + "orange_book": "📙", + "orange_circle": "🟠", + "orange_heart": "🧡", + "orange_square": "🟧", + "orangutan": "🦧", + "orthodox_cross": "☦", + "otter": "🦦", + "outbox_tray": "📤", + "owl": "🦉", + "ox": "🐂", + "oyster": "🦪", + "package": "📦", + "page_facing_up": "📄", + "page_with_curl": "📃", + "pager": "📟", + "paintbrush": "🖌", + "palm_tree": "🌴", + "palms_up_together": "🤲", + "palms_up_together_dark_skin_tone": "🤲🏿", + "palms_up_together_light_skin_tone": "🤲🏻", + "palms_up_together_medium-dark_skin_tone": "🤲🏾", + "palms_up_together_medium-light_skin_tone": "🤲🏼", + "palms_up_together_medium_skin_tone": "🤲🏽", + "pancakes": "🥞", + "panda_face": "🐼", + "paperclip": "📎", + "parrot": "🦜", + "part_alternation_mark": "〽", + "party_popper": "🎉", + "partying_face": "🥳", + "passenger_ship": "🛳", + "passport_control": "🛂", + "pause_button": "⏸", + "paw_prints": "🐾", + "peace_symbol": "☮", + "peach": "🍑", + "peacock": "🦚", + "peanuts": "🥜", + "pear": "🍐", + "pen": "🖊", + "pencil": "📝", + "penguin": "🐧", + "pensive_face": "😔", + "people_holding_hands": "🧑\u200d🤝\u200d🧑", + "people_with_bunny_ears": "👯", + "people_wrestling": "🤼", + "performing_arts": "🎭", + "persevering_face": "😣", + "person_biking": "🚴", + "person_biking_dark_skin_tone": "🚴🏿", + "person_biking_light_skin_tone": "🚴🏻", + "person_biking_medium-dark_skin_tone": "🚴🏾", + "person_biking_medium-light_skin_tone": "🚴🏼", + "person_biking_medium_skin_tone": "🚴🏽", + "person_bouncing_ball": "⛹", + "person_bouncing_ball_dark_skin_tone": "⛹🏿", + "person_bouncing_ball_light_skin_tone": "⛹🏻", + "person_bouncing_ball_medium-dark_skin_tone": "⛹🏾", + "person_bouncing_ball_medium-light_skin_tone": "⛹🏼", + "person_bouncing_ball_medium_skin_tone": "⛹🏽", + "person_bowing": "🙇", + "person_bowing_dark_skin_tone": "🙇🏿", + "person_bowing_light_skin_tone": "🙇🏻", + "person_bowing_medium-dark_skin_tone": "🙇🏾", + "person_bowing_medium-light_skin_tone": "🙇🏼", + "person_bowing_medium_skin_tone": "🙇🏽", + "person_cartwheeling": "🤸", + "person_cartwheeling_dark_skin_tone": "🤸🏿", + "person_cartwheeling_light_skin_tone": "🤸🏻", + "person_cartwheeling_medium-dark_skin_tone": "🤸🏾", + "person_cartwheeling_medium-light_skin_tone": "🤸🏼", + "person_cartwheeling_medium_skin_tone": "🤸🏽", + "person_climbing": "🧗", + "person_climbing_dark_skin_tone": "🧗🏿", + "person_climbing_light_skin_tone": "🧗🏻", + "person_climbing_medium-dark_skin_tone": "🧗🏾", + "person_climbing_medium-light_skin_tone": "🧗🏼", + "person_climbing_medium_skin_tone": "🧗🏽", + "person_facepalming": "🤦", + "person_facepalming_dark_skin_tone": "🤦🏿", + "person_facepalming_light_skin_tone": "🤦🏻", + "person_facepalming_medium-dark_skin_tone": "🤦🏾", + "person_facepalming_medium-light_skin_tone": "🤦🏼", + "person_facepalming_medium_skin_tone": "🤦🏽", + "person_fencing": "🤺", + "person_frowning": "🙍", + "person_frowning_dark_skin_tone": "🙍🏿", + "person_frowning_light_skin_tone": "🙍🏻", + "person_frowning_medium-dark_skin_tone": "🙍🏾", + "person_frowning_medium-light_skin_tone": "🙍🏼", + "person_frowning_medium_skin_tone": "🙍🏽", + "person_gesturing_no": "🙅", + "person_gesturing_no_dark_skin_tone": "🙅🏿", + "person_gesturing_no_light_skin_tone": "🙅🏻", + "person_gesturing_no_medium-dark_skin_tone": "🙅🏾", + "person_gesturing_no_medium-light_skin_tone": "🙅🏼", + "person_gesturing_no_medium_skin_tone": "🙅🏽", + "person_gesturing_ok": "🙆", + "person_gesturing_ok_dark_skin_tone": "🙆🏿", + "person_gesturing_ok_light_skin_tone": "🙆🏻", + "person_gesturing_ok_medium-dark_skin_tone": "🙆🏾", + "person_gesturing_ok_medium-light_skin_tone": "🙆🏼", + "person_gesturing_ok_medium_skin_tone": "🙆🏽", + "person_getting_haircut": "💇", + "person_getting_haircut_dark_skin_tone": "💇🏿", + "person_getting_haircut_light_skin_tone": "💇🏻", + "person_getting_haircut_medium-dark_skin_tone": "💇🏾", + "person_getting_haircut_medium-light_skin_tone": "💇🏼", + "person_getting_haircut_medium_skin_tone": "💇🏽", + "person_getting_massage": "💆", + "person_getting_massage_dark_skin_tone": "💆🏿", + "person_getting_massage_light_skin_tone": "💆🏻", + "person_getting_massage_medium-dark_skin_tone": "💆🏾", + "person_getting_massage_medium-light_skin_tone": "💆🏼", + "person_getting_massage_medium_skin_tone": "💆🏽", + "person_golfing": "🏌", + "person_golfing_dark_skin_tone": "🏌🏿", + "person_golfing_light_skin_tone": "🏌🏻", + "person_golfing_medium-dark_skin_tone": "🏌🏾", + "person_golfing_medium-light_skin_tone": "🏌🏼", + "person_golfing_medium_skin_tone": "🏌🏽", + "person_in_bed": "🛌", + "person_in_bed_dark_skin_tone": "🛌🏿", + "person_in_bed_light_skin_tone": "🛌🏻", + "person_in_bed_medium-dark_skin_tone": "🛌🏾", + "person_in_bed_medium-light_skin_tone": "🛌🏼", + "person_in_bed_medium_skin_tone": "🛌🏽", + "person_in_lotus_position": "🧘", + "person_in_lotus_position_dark_skin_tone": "🧘🏿", + "person_in_lotus_position_light_skin_tone": "🧘🏻", + "person_in_lotus_position_medium-dark_skin_tone": "🧘🏾", + "person_in_lotus_position_medium-light_skin_tone": "🧘🏼", + "person_in_lotus_position_medium_skin_tone": "🧘🏽", + "person_in_steamy_room": "🧖", + "person_in_steamy_room_dark_skin_tone": "🧖🏿", + "person_in_steamy_room_light_skin_tone": "🧖🏻", + "person_in_steamy_room_medium-dark_skin_tone": "🧖🏾", + "person_in_steamy_room_medium-light_skin_tone": "🧖🏼", + "person_in_steamy_room_medium_skin_tone": "🧖🏽", + "person_juggling": "🤹", + "person_juggling_dark_skin_tone": "🤹🏿", + "person_juggling_light_skin_tone": "🤹🏻", + "person_juggling_medium-dark_skin_tone": "🤹🏾", + "person_juggling_medium-light_skin_tone": "🤹🏼", + "person_juggling_medium_skin_tone": "🤹🏽", + "person_kneeling": "🧎", + "person_lifting_weights": "🏋", + "person_lifting_weights_dark_skin_tone": "🏋🏿", + "person_lifting_weights_light_skin_tone": "🏋🏻", + "person_lifting_weights_medium-dark_skin_tone": "🏋🏾", + "person_lifting_weights_medium-light_skin_tone": "🏋🏼", + "person_lifting_weights_medium_skin_tone": "🏋🏽", + "person_mountain_biking": "🚵", + "person_mountain_biking_dark_skin_tone": "🚵🏿", + "person_mountain_biking_light_skin_tone": "🚵🏻", + "person_mountain_biking_medium-dark_skin_tone": "🚵🏾", + "person_mountain_biking_medium-light_skin_tone": "🚵🏼", + "person_mountain_biking_medium_skin_tone": "🚵🏽", + "person_playing_handball": "🤾", + "person_playing_handball_dark_skin_tone": "🤾🏿", + "person_playing_handball_light_skin_tone": "🤾🏻", + "person_playing_handball_medium-dark_skin_tone": "🤾🏾", + "person_playing_handball_medium-light_skin_tone": "🤾🏼", + "person_playing_handball_medium_skin_tone": "🤾🏽", + "person_playing_water_polo": "🤽", + "person_playing_water_polo_dark_skin_tone": "🤽🏿", + "person_playing_water_polo_light_skin_tone": "🤽🏻", + "person_playing_water_polo_medium-dark_skin_tone": "🤽🏾", + "person_playing_water_polo_medium-light_skin_tone": "🤽🏼", + "person_playing_water_polo_medium_skin_tone": "🤽🏽", + "person_pouting": "🙎", + "person_pouting_dark_skin_tone": "🙎🏿", + "person_pouting_light_skin_tone": "🙎🏻", + "person_pouting_medium-dark_skin_tone": "🙎🏾", + "person_pouting_medium-light_skin_tone": "🙎🏼", + "person_pouting_medium_skin_tone": "🙎🏽", + "person_raising_hand": "🙋", + "person_raising_hand_dark_skin_tone": "🙋🏿", + "person_raising_hand_light_skin_tone": "🙋🏻", + "person_raising_hand_medium-dark_skin_tone": "🙋🏾", + "person_raising_hand_medium-light_skin_tone": "🙋🏼", + "person_raising_hand_medium_skin_tone": "🙋🏽", + "person_rowing_boat": "🚣", + "person_rowing_boat_dark_skin_tone": "🚣🏿", + "person_rowing_boat_light_skin_tone": "🚣🏻", + "person_rowing_boat_medium-dark_skin_tone": "🚣🏾", + "person_rowing_boat_medium-light_skin_tone": "🚣🏼", + "person_rowing_boat_medium_skin_tone": "🚣🏽", + "person_running": "🏃", + "person_running_dark_skin_tone": "🏃🏿", + "person_running_light_skin_tone": "🏃🏻", + "person_running_medium-dark_skin_tone": "🏃🏾", + "person_running_medium-light_skin_tone": "🏃🏼", + "person_running_medium_skin_tone": "🏃🏽", + "person_shrugging": "🤷", + "person_shrugging_dark_skin_tone": "🤷🏿", + "person_shrugging_light_skin_tone": "🤷🏻", + "person_shrugging_medium-dark_skin_tone": "🤷🏾", + "person_shrugging_medium-light_skin_tone": "🤷🏼", + "person_shrugging_medium_skin_tone": "🤷🏽", + "person_standing": "🧍", + "person_surfing": "🏄", + "person_surfing_dark_skin_tone": "🏄🏿", + "person_surfing_light_skin_tone": "🏄🏻", + "person_surfing_medium-dark_skin_tone": "🏄🏾", + "person_surfing_medium-light_skin_tone": "🏄🏼", + "person_surfing_medium_skin_tone": "🏄🏽", + "person_swimming": "🏊", + "person_swimming_dark_skin_tone": "🏊🏿", + "person_swimming_light_skin_tone": "🏊🏻", + "person_swimming_medium-dark_skin_tone": "🏊🏾", + "person_swimming_medium-light_skin_tone": "🏊🏼", + "person_swimming_medium_skin_tone": "🏊🏽", + "person_taking_bath": "🛀", + "person_taking_bath_dark_skin_tone": "🛀🏿", + "person_taking_bath_light_skin_tone": "🛀🏻", + "person_taking_bath_medium-dark_skin_tone": "🛀🏾", + "person_taking_bath_medium-light_skin_tone": "🛀🏼", + "person_taking_bath_medium_skin_tone": "🛀🏽", + "person_tipping_hand": "💁", + "person_tipping_hand_dark_skin_tone": "💁🏿", + "person_tipping_hand_light_skin_tone": "💁🏻", + "person_tipping_hand_medium-dark_skin_tone": "💁🏾", + "person_tipping_hand_medium-light_skin_tone": "💁🏼", + "person_tipping_hand_medium_skin_tone": "💁🏽", + "person_walking": "🚶", + "person_walking_dark_skin_tone": "🚶🏿", + "person_walking_light_skin_tone": "🚶🏻", + "person_walking_medium-dark_skin_tone": "🚶🏾", + "person_walking_medium-light_skin_tone": "🚶🏼", + "person_walking_medium_skin_tone": "🚶🏽", + "person_wearing_turban": "👳", + "person_wearing_turban_dark_skin_tone": "👳🏿", + "person_wearing_turban_light_skin_tone": "👳🏻", + "person_wearing_turban_medium-dark_skin_tone": "👳🏾", + "person_wearing_turban_medium-light_skin_tone": "👳🏼", + "person_wearing_turban_medium_skin_tone": "👳🏽", + "petri_dish": "🧫", + "pick": "⛏", + "pie": "🥧", + "pig": "🐷", + "pig_face": "🐷", + "pig_nose": "🐽", + "pile_of_poo": "💩", + "pill": "💊", + "pinching_hand": "🤏", + "pine_decoration": "🎍", + "pineapple": "🍍", + "ping_pong": "🏓", + "pirate_flag": "🏴\u200d☠️", + "pistol": "🔫", + "pizza": "🍕", + "place_of_worship": "🛐", + "play_button": "▶", + "play_or_pause_button": "⏯", + "pleading_face": "🥺", + "police_car": "🚓", + "police_car_light": "🚨", + "police_officer": "👮", + "police_officer_dark_skin_tone": "👮🏿", + "police_officer_light_skin_tone": "👮🏻", + "police_officer_medium-dark_skin_tone": "👮🏾", + "police_officer_medium-light_skin_tone": "👮🏼", + "police_officer_medium_skin_tone": "👮🏽", + "poodle": "🐩", + "pool_8_ball": "🎱", + "popcorn": "🍿", + "post_office": "🏣", + "postal_horn": "📯", + "postbox": "📮", + "pot_of_food": "🍲", + "potable_water": "🚰", + "potato": "🥔", + "poultry_leg": "🍗", + "pound_banknote": "💷", + "pouting_cat_face": "😾", + "pouting_face": "😡", + "prayer_beads": "📿", + "pregnant_woman": "🤰", + "pregnant_woman_dark_skin_tone": "🤰🏿", + "pregnant_woman_light_skin_tone": "🤰🏻", + "pregnant_woman_medium-dark_skin_tone": "🤰🏾", + "pregnant_woman_medium-light_skin_tone": "🤰🏼", + "pregnant_woman_medium_skin_tone": "🤰🏽", + "pretzel": "🥨", + "probing_cane": "🦯", + "prince": "🤴", + "prince_dark_skin_tone": "🤴🏿", + "prince_light_skin_tone": "🤴🏻", + "prince_medium-dark_skin_tone": "🤴🏾", + "prince_medium-light_skin_tone": "🤴🏼", + "prince_medium_skin_tone": "🤴🏽", + "princess": "👸", + "princess_dark_skin_tone": "👸🏿", + "princess_light_skin_tone": "👸🏻", + "princess_medium-dark_skin_tone": "👸🏾", + "princess_medium-light_skin_tone": "👸🏼", + "princess_medium_skin_tone": "👸🏽", + "printer": "🖨", + "prohibited": "🚫", + "purple_circle": "🟣", + "purple_heart": "💜", + "purple_square": "🟪", + "purse": "👛", + "pushpin": "📌", + "question_mark": "❓", + "rabbit": "🐰", + "rabbit_face": "🐰", + "raccoon": "🦝", + "racing_car": "🏎", + "radio": "📻", + "radio_button": "🔘", + "radioactive": "☢", + "railway_car": "🚃", + "railway_track": "🛤", + "rainbow": "🌈", + "rainbow_flag": "🏳️\u200d🌈", + "raised_back_of_hand": "🤚", + "raised_back_of_hand_dark_skin_tone": "🤚🏿", + "raised_back_of_hand_light_skin_tone": "🤚🏻", + "raised_back_of_hand_medium-dark_skin_tone": "🤚🏾", + "raised_back_of_hand_medium-light_skin_tone": "🤚🏼", + "raised_back_of_hand_medium_skin_tone": "🤚🏽", + "raised_fist": "✊", + "raised_fist_dark_skin_tone": "✊🏿", + "raised_fist_light_skin_tone": "✊🏻", + "raised_fist_medium-dark_skin_tone": "✊🏾", + "raised_fist_medium-light_skin_tone": "✊🏼", + "raised_fist_medium_skin_tone": "✊🏽", + "raised_hand": "✋", + "raised_hand_dark_skin_tone": "✋🏿", + "raised_hand_light_skin_tone": "✋🏻", + "raised_hand_medium-dark_skin_tone": "✋🏾", + "raised_hand_medium-light_skin_tone": "✋🏼", + "raised_hand_medium_skin_tone": "✋🏽", + "raising_hands": "🙌", + "raising_hands_dark_skin_tone": "🙌🏿", + "raising_hands_light_skin_tone": "🙌🏻", + "raising_hands_medium-dark_skin_tone": "🙌🏾", + "raising_hands_medium-light_skin_tone": "🙌🏼", + "raising_hands_medium_skin_tone": "🙌🏽", + "ram": "🐏", + "rat": "🐀", + "razor": "🪒", + "ringed_planet": "🪐", + "receipt": "🧾", + "record_button": "⏺", + "recycling_symbol": "♻", + "red_apple": "🍎", + "red_circle": "🔴", + "red_envelope": "🧧", + "red_hair": "🦰", + "red-haired_man": "👨\u200d🦰", + "red-haired_woman": "👩\u200d🦰", + "red_heart": "❤", + "red_paper_lantern": "🏮", + "red_square": "🟥", + "red_triangle_pointed_down": "🔻", + "red_triangle_pointed_up": "🔺", + "registered": "®", + "relieved_face": "😌", + "reminder_ribbon": "🎗", + "repeat_button": "🔁", + "repeat_single_button": "🔂", + "rescue_worker’s_helmet": "⛑", + "restroom": "🚻", + "reverse_button": "◀", + "revolving_hearts": "💞", + "rhinoceros": "🦏", + "ribbon": "🎀", + "rice_ball": "🍙", + "rice_cracker": "🍘", + "right-facing_fist": "🤜", + "right-facing_fist_dark_skin_tone": "🤜🏿", + "right-facing_fist_light_skin_tone": "🤜🏻", + "right-facing_fist_medium-dark_skin_tone": "🤜🏾", + "right-facing_fist_medium-light_skin_tone": "🤜🏼", + "right-facing_fist_medium_skin_tone": "🤜🏽", + "right_anger_bubble": "🗯", + "right_arrow": "➡", + "right_arrow_curving_down": "⤵", + "right_arrow_curving_left": "↩", + "right_arrow_curving_up": "⤴", + "ring": "💍", + "roasted_sweet_potato": "🍠", + "robot_face": "🤖", + "rocket": "🚀", + "roll_of_paper": "🧻", + "rolled-up_newspaper": "🗞", + "roller_coaster": "🎢", + "rolling_on_the_floor_laughing": "🤣", + "rooster": "🐓", + "rose": "🌹", + "rosette": "🏵", + "round_pushpin": "📍", + "rugby_football": "🏉", + "running_shirt": "🎽", + "running_shoe": "👟", + "sad_but_relieved_face": "😥", + "safety_pin": "🧷", + "safety_vest": "🦺", + "salt": "🧂", + "sailboat": "⛵", + "sake": "🍶", + "sandwich": "🥪", + "sari": "🥻", + "satellite": "📡", + "satellite_antenna": "📡", + "sauropod": "🦕", + "saxophone": "🎷", + "scarf": "🧣", + "school": "🏫", + "school_backpack": "🎒", + "scissors": "✂", + "scorpion": "🦂", + "scroll": "📜", + "seat": "💺", + "see-no-evil_monkey": "🙈", + "seedling": "🌱", + "selfie": "🤳", + "selfie_dark_skin_tone": "🤳🏿", + "selfie_light_skin_tone": "🤳🏻", + "selfie_medium-dark_skin_tone": "🤳🏾", + "selfie_medium-light_skin_tone": "🤳🏼", + "selfie_medium_skin_tone": "🤳🏽", + "service_dog": "🐕\u200d🦺", + "seven-thirty": "🕢", + "seven_o’clock": "🕖", + "shallow_pan_of_food": "🥘", + "shamrock": "☘", + "shark": "🦈", + "shaved_ice": "🍧", + "sheaf_of_rice": "🌾", + "shield": "🛡", + "shinto_shrine": "⛩", + "ship": "🚢", + "shooting_star": "🌠", + "shopping_bags": "🛍", + "shopping_cart": "🛒", + "shortcake": "🍰", + "shorts": "🩳", + "shower": "🚿", + "shrimp": "🦐", + "shuffle_tracks_button": "🔀", + "shushing_face": "🤫", + "sign_of_the_horns": "🤘", + "sign_of_the_horns_dark_skin_tone": "🤘🏿", + "sign_of_the_horns_light_skin_tone": "🤘🏻", + "sign_of_the_horns_medium-dark_skin_tone": "🤘🏾", + "sign_of_the_horns_medium-light_skin_tone": "🤘🏼", + "sign_of_the_horns_medium_skin_tone": "🤘🏽", + "six-thirty": "🕡", + "six_o’clock": "🕕", + "skateboard": "🛹", + "skier": "⛷", + "skis": "🎿", + "skull": "💀", + "skull_and_crossbones": "☠", + "skunk": "🦨", + "sled": "🛷", + "sleeping_face": "😴", + "sleepy_face": "😪", + "slightly_frowning_face": "🙁", + "slightly_smiling_face": "🙂", + "slot_machine": "🎰", + "sloth": "🦥", + "small_airplane": "🛩", + "small_blue_diamond": "🔹", + "small_orange_diamond": "🔸", + "smiling_cat_face_with_heart-eyes": "😻", + "smiling_face": "☺", + "smiling_face_with_halo": "😇", + "smiling_face_with_3_hearts": "🥰", + "smiling_face_with_heart-eyes": "😍", + "smiling_face_with_horns": "😈", + "smiling_face_with_smiling_eyes": "😊", + "smiling_face_with_sunglasses": "😎", + "smirking_face": "😏", + "snail": "🐌", + "snake": "🐍", + "sneezing_face": "🤧", + "snow-capped_mountain": "🏔", + "snowboarder": "🏂", + "snowboarder_dark_skin_tone": "🏂🏿", + "snowboarder_light_skin_tone": "🏂🏻", + "snowboarder_medium-dark_skin_tone": "🏂🏾", + "snowboarder_medium-light_skin_tone": "🏂🏼", + "snowboarder_medium_skin_tone": "🏂🏽", + "snowflake": "❄", + "snowman": "☃", + "snowman_without_snow": "⛄", + "soap": "🧼", + "soccer_ball": "⚽", + "socks": "🧦", + "softball": "🥎", + "soft_ice_cream": "🍦", + "spade_suit": "♠", + "spaghetti": "🍝", + "sparkle": "❇", + "sparkler": "🎇", + "sparkles": "✨", + "sparkling_heart": "💖", + "speak-no-evil_monkey": "🙊", + "speaker_high_volume": "🔊", + "speaker_low_volume": "🔈", + "speaker_medium_volume": "🔉", + "speaking_head": "🗣", + "speech_balloon": "💬", + "speedboat": "🚤", + "spider": "🕷", + "spider_web": "🕸", + "spiral_calendar": "🗓", + "spiral_notepad": "🗒", + "spiral_shell": "🐚", + "spoon": "🥄", + "sponge": "🧽", + "sport_utility_vehicle": "🚙", + "sports_medal": "🏅", + "spouting_whale": "🐳", + "squid": "🦑", + "squinting_face_with_tongue": "😝", + "stadium": "🏟", + "star-struck": "🤩", + "star_and_crescent": "☪", + "star_of_david": "✡", + "station": "🚉", + "steaming_bowl": "🍜", + "stethoscope": "🩺", + "stop_button": "⏹", + "stop_sign": "🛑", + "stopwatch": "⏱", + "straight_ruler": "📏", + "strawberry": "🍓", + "studio_microphone": "🎙", + "stuffed_flatbread": "🥙", + "sun": "☀", + "sun_behind_cloud": "⛅", + "sun_behind_large_cloud": "🌥", + "sun_behind_rain_cloud": "🌦", + "sun_behind_small_cloud": "🌤", + "sun_with_face": "🌞", + "sunflower": "🌻", + "sunglasses": "😎", + "sunrise": "🌅", + "sunrise_over_mountains": "🌄", + "sunset": "🌇", + "superhero": "🦸", + "supervillain": "🦹", + "sushi": "🍣", + "suspension_railway": "🚟", + "swan": "🦢", + "sweat_droplets": "💦", + "synagogue": "🕍", + "syringe": "💉", + "t-shirt": "👕", + "taco": "🌮", + "takeout_box": "🥡", + "tanabata_tree": "🎋", + "tangerine": "🍊", + "taxi": "🚕", + "teacup_without_handle": "🍵", + "tear-off_calendar": "📆", + "teddy_bear": "🧸", + "telephone": "☎", + "telephone_receiver": "📞", + "telescope": "🔭", + "television": "📺", + "ten-thirty": "🕥", + "ten_o’clock": "🕙", + "tennis": "🎾", + "tent": "⛺", + "test_tube": "🧪", + "thermometer": "🌡", + "thinking_face": "🤔", + "thought_balloon": "💭", + "thread": "🧵", + "three-thirty": "🕞", + "three_o’clock": "🕒", + "thumbs_down": "👎", + "thumbs_down_dark_skin_tone": "👎🏿", + "thumbs_down_light_skin_tone": "👎🏻", + "thumbs_down_medium-dark_skin_tone": "👎🏾", + "thumbs_down_medium-light_skin_tone": "👎🏼", + "thumbs_down_medium_skin_tone": "👎🏽", + "thumbs_up": "👍", + "thumbs_up_dark_skin_tone": "👍🏿", + "thumbs_up_light_skin_tone": "👍🏻", + "thumbs_up_medium-dark_skin_tone": "👍🏾", + "thumbs_up_medium-light_skin_tone": "👍🏼", + "thumbs_up_medium_skin_tone": "👍🏽", + "ticket": "🎫", + "tiger": "🐯", + "tiger_face": "🐯", + "timer_clock": "⏲", + "tired_face": "😫", + "toolbox": "🧰", + "toilet": "🚽", + "tomato": "🍅", + "tongue": "👅", + "tooth": "🦷", + "top_hat": "🎩", + "tornado": "🌪", + "trackball": "🖲", + "tractor": "🚜", + "trade_mark": "™", + "train": "🚋", + "tram": "🚊", + "tram_car": "🚋", + "triangular_flag": "🚩", + "triangular_ruler": "📐", + "trident_emblem": "🔱", + "trolleybus": "🚎", + "trophy": "🏆", + "tropical_drink": "🍹", + "tropical_fish": "🐠", + "trumpet": "🎺", + "tulip": "🌷", + "tumbler_glass": "🥃", + "turtle": "🐢", + "twelve-thirty": "🕧", + "twelve_o’clock": "🕛", + "two-hump_camel": "🐫", + "two-thirty": "🕝", + "two_hearts": "💕", + "two_men_holding_hands": "👬", + "two_o’clock": "🕑", + "two_women_holding_hands": "👭", + "umbrella": "☂", + "umbrella_on_ground": "⛱", + "umbrella_with_rain_drops": "☔", + "unamused_face": "😒", + "unicorn_face": "🦄", + "unlocked": "🔓", + "up-down_arrow": "↕", + "up-left_arrow": "↖", + "up-right_arrow": "↗", + "up_arrow": "⬆", + "upside-down_face": "🙃", + "upwards_button": "🔼", + "vampire": "🧛", + "vampire_dark_skin_tone": "🧛🏿", + "vampire_light_skin_tone": "🧛🏻", + "vampire_medium-dark_skin_tone": "🧛🏾", + "vampire_medium-light_skin_tone": "🧛🏼", + "vampire_medium_skin_tone": "🧛🏽", + "vertical_traffic_light": "🚦", + "vibration_mode": "📳", + "victory_hand": "✌", + "victory_hand_dark_skin_tone": "✌🏿", + "victory_hand_light_skin_tone": "✌🏻", + "victory_hand_medium-dark_skin_tone": "✌🏾", + "victory_hand_medium-light_skin_tone": "✌🏼", + "victory_hand_medium_skin_tone": "✌🏽", + "video_camera": "📹", + "video_game": "🎮", + "videocassette": "📼", + "violin": "🎻", + "volcano": "🌋", + "volleyball": "🏐", + "vulcan_salute": "🖖", + "vulcan_salute_dark_skin_tone": "🖖🏿", + "vulcan_salute_light_skin_tone": "🖖🏻", + "vulcan_salute_medium-dark_skin_tone": "🖖🏾", + "vulcan_salute_medium-light_skin_tone": "🖖🏼", + "vulcan_salute_medium_skin_tone": "🖖🏽", + "waffle": "🧇", + "waning_crescent_moon": "🌘", + "waning_gibbous_moon": "🌖", + "warning": "⚠", + "wastebasket": "🗑", + "watch": "⌚", + "water_buffalo": "🐃", + "water_closet": "🚾", + "water_wave": "🌊", + "watermelon": "🍉", + "waving_hand": "👋", + "waving_hand_dark_skin_tone": "👋🏿", + "waving_hand_light_skin_tone": "👋🏻", + "waving_hand_medium-dark_skin_tone": "👋🏾", + "waving_hand_medium-light_skin_tone": "👋🏼", + "waving_hand_medium_skin_tone": "👋🏽", + "wavy_dash": "〰", + "waxing_crescent_moon": "🌒", + "waxing_gibbous_moon": "🌔", + "weary_cat_face": "🙀", + "weary_face": "😩", + "wedding": "💒", + "whale": "🐳", + "wheel_of_dharma": "☸", + "wheelchair_symbol": "♿", + "white_circle": "⚪", + "white_exclamation_mark": "❕", + "white_flag": "🏳", + "white_flower": "💮", + "white_hair": "🦳", + "white-haired_man": "👨\u200d🦳", + "white-haired_woman": "👩\u200d🦳", + "white_heart": "🤍", + "white_heavy_check_mark": "✅", + "white_large_square": "⬜", + "white_medium-small_square": "◽", + "white_medium_square": "◻", + "white_medium_star": "⭐", + "white_question_mark": "❔", + "white_small_square": "▫", + "white_square_button": "🔳", + "wilted_flower": "🥀", + "wind_chime": "🎐", + "wind_face": "🌬", + "wine_glass": "🍷", + "winking_face": "😉", + "winking_face_with_tongue": "😜", + "wolf_face": "🐺", + "woman": "👩", + "woman_artist": "👩\u200d🎨", + "woman_artist_dark_skin_tone": "👩🏿\u200d🎨", + "woman_artist_light_skin_tone": "👩🏻\u200d🎨", + "woman_artist_medium-dark_skin_tone": "👩🏾\u200d🎨", + "woman_artist_medium-light_skin_tone": "👩🏼\u200d🎨", + "woman_artist_medium_skin_tone": "👩🏽\u200d🎨", + "woman_astronaut": "👩\u200d🚀", + "woman_astronaut_dark_skin_tone": "👩🏿\u200d🚀", + "woman_astronaut_light_skin_tone": "👩🏻\u200d🚀", + "woman_astronaut_medium-dark_skin_tone": "👩🏾\u200d🚀", + "woman_astronaut_medium-light_skin_tone": "👩🏼\u200d🚀", + "woman_astronaut_medium_skin_tone": "👩🏽\u200d🚀", + "woman_biking": "🚴\u200d♀️", + "woman_biking_dark_skin_tone": "🚴🏿\u200d♀️", + "woman_biking_light_skin_tone": "🚴🏻\u200d♀️", + "woman_biking_medium-dark_skin_tone": "🚴🏾\u200d♀️", + "woman_biking_medium-light_skin_tone": "🚴🏼\u200d♀️", + "woman_biking_medium_skin_tone": "🚴🏽\u200d♀️", + "woman_bouncing_ball": "⛹️\u200d♀️", + "woman_bouncing_ball_dark_skin_tone": "⛹🏿\u200d♀️", + "woman_bouncing_ball_light_skin_tone": "⛹🏻\u200d♀️", + "woman_bouncing_ball_medium-dark_skin_tone": "⛹🏾\u200d♀️", + "woman_bouncing_ball_medium-light_skin_tone": "⛹🏼\u200d♀️", + "woman_bouncing_ball_medium_skin_tone": "⛹🏽\u200d♀️", + "woman_bowing": "🙇\u200d♀️", + "woman_bowing_dark_skin_tone": "🙇🏿\u200d♀️", + "woman_bowing_light_skin_tone": "🙇🏻\u200d♀️", + "woman_bowing_medium-dark_skin_tone": "🙇🏾\u200d♀️", + "woman_bowing_medium-light_skin_tone": "🙇🏼\u200d♀️", + "woman_bowing_medium_skin_tone": "🙇🏽\u200d♀️", + "woman_cartwheeling": "🤸\u200d♀️", + "woman_cartwheeling_dark_skin_tone": "🤸🏿\u200d♀️", + "woman_cartwheeling_light_skin_tone": "🤸🏻\u200d♀️", + "woman_cartwheeling_medium-dark_skin_tone": "🤸🏾\u200d♀️", + "woman_cartwheeling_medium-light_skin_tone": "🤸🏼\u200d♀️", + "woman_cartwheeling_medium_skin_tone": "🤸🏽\u200d♀️", + "woman_climbing": "🧗\u200d♀️", + "woman_climbing_dark_skin_tone": "🧗🏿\u200d♀️", + "woman_climbing_light_skin_tone": "🧗🏻\u200d♀️", + "woman_climbing_medium-dark_skin_tone": "🧗🏾\u200d♀️", + "woman_climbing_medium-light_skin_tone": "🧗🏼\u200d♀️", + "woman_climbing_medium_skin_tone": "🧗🏽\u200d♀️", + "woman_construction_worker": "👷\u200d♀️", + "woman_construction_worker_dark_skin_tone": "👷🏿\u200d♀️", + "woman_construction_worker_light_skin_tone": "👷🏻\u200d♀️", + "woman_construction_worker_medium-dark_skin_tone": "👷🏾\u200d♀️", + "woman_construction_worker_medium-light_skin_tone": "👷🏼\u200d♀️", + "woman_construction_worker_medium_skin_tone": "👷🏽\u200d♀️", + "woman_cook": "👩\u200d🍳", + "woman_cook_dark_skin_tone": "👩🏿\u200d🍳", + "woman_cook_light_skin_tone": "👩🏻\u200d🍳", + "woman_cook_medium-dark_skin_tone": "👩🏾\u200d🍳", + "woman_cook_medium-light_skin_tone": "👩🏼\u200d🍳", + "woman_cook_medium_skin_tone": "👩🏽\u200d🍳", + "woman_dancing": "💃", + "woman_dancing_dark_skin_tone": "💃🏿", + "woman_dancing_light_skin_tone": "💃🏻", + "woman_dancing_medium-dark_skin_tone": "💃🏾", + "woman_dancing_medium-light_skin_tone": "💃🏼", + "woman_dancing_medium_skin_tone": "💃🏽", + "woman_dark_skin_tone": "👩🏿", + "woman_detective": "🕵️\u200d♀️", + "woman_detective_dark_skin_tone": "🕵🏿\u200d♀️", + "woman_detective_light_skin_tone": "🕵🏻\u200d♀️", + "woman_detective_medium-dark_skin_tone": "🕵🏾\u200d♀️", + "woman_detective_medium-light_skin_tone": "🕵🏼\u200d♀️", + "woman_detective_medium_skin_tone": "🕵🏽\u200d♀️", + "woman_elf": "🧝\u200d♀️", + "woman_elf_dark_skin_tone": "🧝🏿\u200d♀️", + "woman_elf_light_skin_tone": "🧝🏻\u200d♀️", + "woman_elf_medium-dark_skin_tone": "🧝🏾\u200d♀️", + "woman_elf_medium-light_skin_tone": "🧝🏼\u200d♀️", + "woman_elf_medium_skin_tone": "🧝🏽\u200d♀️", + "woman_facepalming": "🤦\u200d♀️", + "woman_facepalming_dark_skin_tone": "🤦🏿\u200d♀️", + "woman_facepalming_light_skin_tone": "🤦🏻\u200d♀️", + "woman_facepalming_medium-dark_skin_tone": "🤦🏾\u200d♀️", + "woman_facepalming_medium-light_skin_tone": "🤦🏼\u200d♀️", + "woman_facepalming_medium_skin_tone": "🤦🏽\u200d♀️", + "woman_factory_worker": "👩\u200d🏭", + "woman_factory_worker_dark_skin_tone": "👩🏿\u200d🏭", + "woman_factory_worker_light_skin_tone": "👩🏻\u200d🏭", + "woman_factory_worker_medium-dark_skin_tone": "👩🏾\u200d🏭", + "woman_factory_worker_medium-light_skin_tone": "👩🏼\u200d🏭", + "woman_factory_worker_medium_skin_tone": "👩🏽\u200d🏭", + "woman_fairy": "🧚\u200d♀️", + "woman_fairy_dark_skin_tone": "🧚🏿\u200d♀️", + "woman_fairy_light_skin_tone": "🧚🏻\u200d♀️", + "woman_fairy_medium-dark_skin_tone": "🧚🏾\u200d♀️", + "woman_fairy_medium-light_skin_tone": "🧚🏼\u200d♀️", + "woman_fairy_medium_skin_tone": "🧚🏽\u200d♀️", + "woman_farmer": "👩\u200d🌾", + "woman_farmer_dark_skin_tone": "👩🏿\u200d🌾", + "woman_farmer_light_skin_tone": "👩🏻\u200d🌾", + "woman_farmer_medium-dark_skin_tone": "👩🏾\u200d🌾", + "woman_farmer_medium-light_skin_tone": "👩🏼\u200d🌾", + "woman_farmer_medium_skin_tone": "👩🏽\u200d🌾", + "woman_firefighter": "👩\u200d🚒", + "woman_firefighter_dark_skin_tone": "👩🏿\u200d🚒", + "woman_firefighter_light_skin_tone": "👩🏻\u200d🚒", + "woman_firefighter_medium-dark_skin_tone": "👩🏾\u200d🚒", + "woman_firefighter_medium-light_skin_tone": "👩🏼\u200d🚒", + "woman_firefighter_medium_skin_tone": "👩🏽\u200d🚒", + "woman_frowning": "🙍\u200d♀️", + "woman_frowning_dark_skin_tone": "🙍🏿\u200d♀️", + "woman_frowning_light_skin_tone": "🙍🏻\u200d♀️", + "woman_frowning_medium-dark_skin_tone": "🙍🏾\u200d♀️", + "woman_frowning_medium-light_skin_tone": "🙍🏼\u200d♀️", + "woman_frowning_medium_skin_tone": "🙍🏽\u200d♀️", + "woman_genie": "🧞\u200d♀️", + "woman_gesturing_no": "🙅\u200d♀️", + "woman_gesturing_no_dark_skin_tone": "🙅🏿\u200d♀️", + "woman_gesturing_no_light_skin_tone": "🙅🏻\u200d♀️", + "woman_gesturing_no_medium-dark_skin_tone": "🙅🏾\u200d♀️", + "woman_gesturing_no_medium-light_skin_tone": "🙅🏼\u200d♀️", + "woman_gesturing_no_medium_skin_tone": "🙅🏽\u200d♀️", + "woman_gesturing_ok": "🙆\u200d♀️", + "woman_gesturing_ok_dark_skin_tone": "🙆🏿\u200d♀️", + "woman_gesturing_ok_light_skin_tone": "🙆🏻\u200d♀️", + "woman_gesturing_ok_medium-dark_skin_tone": "🙆🏾\u200d♀️", + "woman_gesturing_ok_medium-light_skin_tone": "🙆🏼\u200d♀️", + "woman_gesturing_ok_medium_skin_tone": "🙆🏽\u200d♀️", + "woman_getting_haircut": "💇\u200d♀️", + "woman_getting_haircut_dark_skin_tone": "💇🏿\u200d♀️", + "woman_getting_haircut_light_skin_tone": "💇🏻\u200d♀️", + "woman_getting_haircut_medium-dark_skin_tone": "💇🏾\u200d♀️", + "woman_getting_haircut_medium-light_skin_tone": "💇🏼\u200d♀️", + "woman_getting_haircut_medium_skin_tone": "💇🏽\u200d♀️", + "woman_getting_massage": "💆\u200d♀️", + "woman_getting_massage_dark_skin_tone": "💆🏿\u200d♀️", + "woman_getting_massage_light_skin_tone": "💆🏻\u200d♀️", + "woman_getting_massage_medium-dark_skin_tone": "💆🏾\u200d♀️", + "woman_getting_massage_medium-light_skin_tone": "💆🏼\u200d♀️", + "woman_getting_massage_medium_skin_tone": "💆🏽\u200d♀️", + "woman_golfing": "🏌️\u200d♀️", + "woman_golfing_dark_skin_tone": "🏌🏿\u200d♀️", + "woman_golfing_light_skin_tone": "🏌🏻\u200d♀️", + "woman_golfing_medium-dark_skin_tone": "🏌🏾\u200d♀️", + "woman_golfing_medium-light_skin_tone": "🏌🏼\u200d♀️", + "woman_golfing_medium_skin_tone": "🏌🏽\u200d♀️", + "woman_guard": "💂\u200d♀️", + "woman_guard_dark_skin_tone": "💂🏿\u200d♀️", + "woman_guard_light_skin_tone": "💂🏻\u200d♀️", + "woman_guard_medium-dark_skin_tone": "💂🏾\u200d♀️", + "woman_guard_medium-light_skin_tone": "💂🏼\u200d♀️", + "woman_guard_medium_skin_tone": "💂🏽\u200d♀️", + "woman_health_worker": "👩\u200d⚕️", + "woman_health_worker_dark_skin_tone": "👩🏿\u200d⚕️", + "woman_health_worker_light_skin_tone": "👩🏻\u200d⚕️", + "woman_health_worker_medium-dark_skin_tone": "👩🏾\u200d⚕️", + "woman_health_worker_medium-light_skin_tone": "👩🏼\u200d⚕️", + "woman_health_worker_medium_skin_tone": "👩🏽\u200d⚕️", + "woman_in_lotus_position": "🧘\u200d♀️", + "woman_in_lotus_position_dark_skin_tone": "🧘🏿\u200d♀️", + "woman_in_lotus_position_light_skin_tone": "🧘🏻\u200d♀️", + "woman_in_lotus_position_medium-dark_skin_tone": "🧘🏾\u200d♀️", + "woman_in_lotus_position_medium-light_skin_tone": "🧘🏼\u200d♀️", + "woman_in_lotus_position_medium_skin_tone": "🧘🏽\u200d♀️", + "woman_in_manual_wheelchair": "👩\u200d🦽", + "woman_in_motorized_wheelchair": "👩\u200d🦼", + "woman_in_steamy_room": "🧖\u200d♀️", + "woman_in_steamy_room_dark_skin_tone": "🧖🏿\u200d♀️", + "woman_in_steamy_room_light_skin_tone": "🧖🏻\u200d♀️", + "woman_in_steamy_room_medium-dark_skin_tone": "🧖🏾\u200d♀️", + "woman_in_steamy_room_medium-light_skin_tone": "🧖🏼\u200d♀️", + "woman_in_steamy_room_medium_skin_tone": "🧖🏽\u200d♀️", + "woman_judge": "👩\u200d⚖️", + "woman_judge_dark_skin_tone": "👩🏿\u200d⚖️", + "woman_judge_light_skin_tone": "👩🏻\u200d⚖️", + "woman_judge_medium-dark_skin_tone": "👩🏾\u200d⚖️", + "woman_judge_medium-light_skin_tone": "👩🏼\u200d⚖️", + "woman_judge_medium_skin_tone": "👩🏽\u200d⚖️", + "woman_juggling": "🤹\u200d♀️", + "woman_juggling_dark_skin_tone": "🤹🏿\u200d♀️", + "woman_juggling_light_skin_tone": "🤹🏻\u200d♀️", + "woman_juggling_medium-dark_skin_tone": "🤹🏾\u200d♀️", + "woman_juggling_medium-light_skin_tone": "🤹🏼\u200d♀️", + "woman_juggling_medium_skin_tone": "🤹🏽\u200d♀️", + "woman_lifting_weights": "🏋️\u200d♀️", + "woman_lifting_weights_dark_skin_tone": "🏋🏿\u200d♀️", + "woman_lifting_weights_light_skin_tone": "🏋🏻\u200d♀️", + "woman_lifting_weights_medium-dark_skin_tone": "🏋🏾\u200d♀️", + "woman_lifting_weights_medium-light_skin_tone": "🏋🏼\u200d♀️", + "woman_lifting_weights_medium_skin_tone": "🏋🏽\u200d♀️", + "woman_light_skin_tone": "👩🏻", + "woman_mage": "🧙\u200d♀️", + "woman_mage_dark_skin_tone": "🧙🏿\u200d♀️", + "woman_mage_light_skin_tone": "🧙🏻\u200d♀️", + "woman_mage_medium-dark_skin_tone": "🧙🏾\u200d♀️", + "woman_mage_medium-light_skin_tone": "🧙🏼\u200d♀️", + "woman_mage_medium_skin_tone": "🧙🏽\u200d♀️", + "woman_mechanic": "👩\u200d🔧", + "woman_mechanic_dark_skin_tone": "👩🏿\u200d🔧", + "woman_mechanic_light_skin_tone": "👩🏻\u200d🔧", + "woman_mechanic_medium-dark_skin_tone": "👩🏾\u200d🔧", + "woman_mechanic_medium-light_skin_tone": "👩🏼\u200d🔧", + "woman_mechanic_medium_skin_tone": "👩🏽\u200d🔧", + "woman_medium-dark_skin_tone": "👩🏾", + "woman_medium-light_skin_tone": "👩🏼", + "woman_medium_skin_tone": "👩🏽", + "woman_mountain_biking": "🚵\u200d♀️", + "woman_mountain_biking_dark_skin_tone": "🚵🏿\u200d♀️", + "woman_mountain_biking_light_skin_tone": "🚵🏻\u200d♀️", + "woman_mountain_biking_medium-dark_skin_tone": "🚵🏾\u200d♀️", + "woman_mountain_biking_medium-light_skin_tone": "🚵🏼\u200d♀️", + "woman_mountain_biking_medium_skin_tone": "🚵🏽\u200d♀️", + "woman_office_worker": "👩\u200d💼", + "woman_office_worker_dark_skin_tone": "👩🏿\u200d💼", + "woman_office_worker_light_skin_tone": "👩🏻\u200d💼", + "woman_office_worker_medium-dark_skin_tone": "👩🏾\u200d💼", + "woman_office_worker_medium-light_skin_tone": "👩🏼\u200d💼", + "woman_office_worker_medium_skin_tone": "👩🏽\u200d💼", + "woman_pilot": "👩\u200d✈️", + "woman_pilot_dark_skin_tone": "👩🏿\u200d✈️", + "woman_pilot_light_skin_tone": "👩🏻\u200d✈️", + "woman_pilot_medium-dark_skin_tone": "👩🏾\u200d✈️", + "woman_pilot_medium-light_skin_tone": "👩🏼\u200d✈️", + "woman_pilot_medium_skin_tone": "👩🏽\u200d✈️", + "woman_playing_handball": "🤾\u200d♀️", + "woman_playing_handball_dark_skin_tone": "🤾🏿\u200d♀️", + "woman_playing_handball_light_skin_tone": "🤾🏻\u200d♀️", + "woman_playing_handball_medium-dark_skin_tone": "🤾🏾\u200d♀️", + "woman_playing_handball_medium-light_skin_tone": "🤾🏼\u200d♀️", + "woman_playing_handball_medium_skin_tone": "🤾🏽\u200d♀️", + "woman_playing_water_polo": "🤽\u200d♀️", + "woman_playing_water_polo_dark_skin_tone": "🤽🏿\u200d♀️", + "woman_playing_water_polo_light_skin_tone": "🤽🏻\u200d♀️", + "woman_playing_water_polo_medium-dark_skin_tone": "🤽🏾\u200d♀️", + "woman_playing_water_polo_medium-light_skin_tone": "🤽🏼\u200d♀️", + "woman_playing_water_polo_medium_skin_tone": "🤽🏽\u200d♀️", + "woman_police_officer": "👮\u200d♀️", + "woman_police_officer_dark_skin_tone": "👮🏿\u200d♀️", + "woman_police_officer_light_skin_tone": "👮🏻\u200d♀️", + "woman_police_officer_medium-dark_skin_tone": "👮🏾\u200d♀️", + "woman_police_officer_medium-light_skin_tone": "👮🏼\u200d♀️", + "woman_police_officer_medium_skin_tone": "👮🏽\u200d♀️", + "woman_pouting": "🙎\u200d♀️", + "woman_pouting_dark_skin_tone": "🙎🏿\u200d♀️", + "woman_pouting_light_skin_tone": "🙎🏻\u200d♀️", + "woman_pouting_medium-dark_skin_tone": "🙎🏾\u200d♀️", + "woman_pouting_medium-light_skin_tone": "🙎🏼\u200d♀️", + "woman_pouting_medium_skin_tone": "🙎🏽\u200d♀️", + "woman_raising_hand": "🙋\u200d♀️", + "woman_raising_hand_dark_skin_tone": "🙋🏿\u200d♀️", + "woman_raising_hand_light_skin_tone": "🙋🏻\u200d♀️", + "woman_raising_hand_medium-dark_skin_tone": "🙋🏾\u200d♀️", + "woman_raising_hand_medium-light_skin_tone": "🙋🏼\u200d♀️", + "woman_raising_hand_medium_skin_tone": "🙋🏽\u200d♀️", + "woman_rowing_boat": "🚣\u200d♀️", + "woman_rowing_boat_dark_skin_tone": "🚣🏿\u200d♀️", + "woman_rowing_boat_light_skin_tone": "🚣🏻\u200d♀️", + "woman_rowing_boat_medium-dark_skin_tone": "🚣🏾\u200d♀️", + "woman_rowing_boat_medium-light_skin_tone": "🚣🏼\u200d♀️", + "woman_rowing_boat_medium_skin_tone": "🚣🏽\u200d♀️", + "woman_running": "🏃\u200d♀️", + "woman_running_dark_skin_tone": "🏃🏿\u200d♀️", + "woman_running_light_skin_tone": "🏃🏻\u200d♀️", + "woman_running_medium-dark_skin_tone": "🏃🏾\u200d♀️", + "woman_running_medium-light_skin_tone": "🏃🏼\u200d♀️", + "woman_running_medium_skin_tone": "🏃🏽\u200d♀️", + "woman_scientist": "👩\u200d🔬", + "woman_scientist_dark_skin_tone": "👩🏿\u200d🔬", + "woman_scientist_light_skin_tone": "👩🏻\u200d🔬", + "woman_scientist_medium-dark_skin_tone": "👩🏾\u200d🔬", + "woman_scientist_medium-light_skin_tone": "👩🏼\u200d🔬", + "woman_scientist_medium_skin_tone": "👩🏽\u200d🔬", + "woman_shrugging": "🤷\u200d♀️", + "woman_shrugging_dark_skin_tone": "🤷🏿\u200d♀️", + "woman_shrugging_light_skin_tone": "🤷🏻\u200d♀️", + "woman_shrugging_medium-dark_skin_tone": "🤷🏾\u200d♀️", + "woman_shrugging_medium-light_skin_tone": "🤷🏼\u200d♀️", + "woman_shrugging_medium_skin_tone": "🤷🏽\u200d♀️", + "woman_singer": "👩\u200d🎤", + "woman_singer_dark_skin_tone": "👩🏿\u200d🎤", + "woman_singer_light_skin_tone": "👩🏻\u200d🎤", + "woman_singer_medium-dark_skin_tone": "👩🏾\u200d🎤", + "woman_singer_medium-light_skin_tone": "👩🏼\u200d🎤", + "woman_singer_medium_skin_tone": "👩🏽\u200d🎤", + "woman_student": "👩\u200d🎓", + "woman_student_dark_skin_tone": "👩🏿\u200d🎓", + "woman_student_light_skin_tone": "👩🏻\u200d🎓", + "woman_student_medium-dark_skin_tone": "👩🏾\u200d🎓", + "woman_student_medium-light_skin_tone": "👩🏼\u200d🎓", + "woman_student_medium_skin_tone": "👩🏽\u200d🎓", + "woman_surfing": "🏄\u200d♀️", + "woman_surfing_dark_skin_tone": "🏄🏿\u200d♀️", + "woman_surfing_light_skin_tone": "🏄🏻\u200d♀️", + "woman_surfing_medium-dark_skin_tone": "🏄🏾\u200d♀️", + "woman_surfing_medium-light_skin_tone": "🏄🏼\u200d♀️", + "woman_surfing_medium_skin_tone": "🏄🏽\u200d♀️", + "woman_swimming": "🏊\u200d♀️", + "woman_swimming_dark_skin_tone": "🏊🏿\u200d♀️", + "woman_swimming_light_skin_tone": "🏊🏻\u200d♀️", + "woman_swimming_medium-dark_skin_tone": "🏊🏾\u200d♀️", + "woman_swimming_medium-light_skin_tone": "🏊🏼\u200d♀️", + "woman_swimming_medium_skin_tone": "🏊🏽\u200d♀️", + "woman_teacher": "👩\u200d🏫", + "woman_teacher_dark_skin_tone": "👩🏿\u200d🏫", + "woman_teacher_light_skin_tone": "👩🏻\u200d🏫", + "woman_teacher_medium-dark_skin_tone": "👩🏾\u200d🏫", + "woman_teacher_medium-light_skin_tone": "👩🏼\u200d🏫", + "woman_teacher_medium_skin_tone": "👩🏽\u200d🏫", + "woman_technologist": "👩\u200d💻", + "woman_technologist_dark_skin_tone": "👩🏿\u200d💻", + "woman_technologist_light_skin_tone": "👩🏻\u200d💻", + "woman_technologist_medium-dark_skin_tone": "👩🏾\u200d💻", + "woman_technologist_medium-light_skin_tone": "👩🏼\u200d💻", + "woman_technologist_medium_skin_tone": "👩🏽\u200d💻", + "woman_tipping_hand": "💁\u200d♀️", + "woman_tipping_hand_dark_skin_tone": "💁🏿\u200d♀️", + "woman_tipping_hand_light_skin_tone": "💁🏻\u200d♀️", + "woman_tipping_hand_medium-dark_skin_tone": "💁🏾\u200d♀️", + "woman_tipping_hand_medium-light_skin_tone": "💁🏼\u200d♀️", + "woman_tipping_hand_medium_skin_tone": "💁🏽\u200d♀️", + "woman_vampire": "🧛\u200d♀️", + "woman_vampire_dark_skin_tone": "🧛🏿\u200d♀️", + "woman_vampire_light_skin_tone": "🧛🏻\u200d♀️", + "woman_vampire_medium-dark_skin_tone": "🧛🏾\u200d♀️", + "woman_vampire_medium-light_skin_tone": "🧛🏼\u200d♀️", + "woman_vampire_medium_skin_tone": "🧛🏽\u200d♀️", + "woman_walking": "🚶\u200d♀️", + "woman_walking_dark_skin_tone": "🚶🏿\u200d♀️", + "woman_walking_light_skin_tone": "🚶🏻\u200d♀️", + "woman_walking_medium-dark_skin_tone": "🚶🏾\u200d♀️", + "woman_walking_medium-light_skin_tone": "🚶🏼\u200d♀️", + "woman_walking_medium_skin_tone": "🚶🏽\u200d♀️", + "woman_wearing_turban": "👳\u200d♀️", + "woman_wearing_turban_dark_skin_tone": "👳🏿\u200d♀️", + "woman_wearing_turban_light_skin_tone": "👳🏻\u200d♀️", + "woman_wearing_turban_medium-dark_skin_tone": "👳🏾\u200d♀️", + "woman_wearing_turban_medium-light_skin_tone": "👳🏼\u200d♀️", + "woman_wearing_turban_medium_skin_tone": "👳🏽\u200d♀️", + "woman_with_headscarf": "🧕", + "woman_with_headscarf_dark_skin_tone": "🧕🏿", + "woman_with_headscarf_light_skin_tone": "🧕🏻", + "woman_with_headscarf_medium-dark_skin_tone": "🧕🏾", + "woman_with_headscarf_medium-light_skin_tone": "🧕🏼", + "woman_with_headscarf_medium_skin_tone": "🧕🏽", + "woman_with_probing_cane": "👩\u200d🦯", + "woman_zombie": "🧟\u200d♀️", + "woman’s_boot": "👢", + "woman’s_clothes": "👚", + "woman’s_hat": "👒", + "woman’s_sandal": "👡", + "women_with_bunny_ears": "👯\u200d♀️", + "women_wrestling": "🤼\u200d♀️", + "women’s_room": "🚺", + "woozy_face": "🥴", + "world_map": "🗺", + "worried_face": "😟", + "wrapped_gift": "🎁", + "wrench": "🔧", + "writing_hand": "✍", + "writing_hand_dark_skin_tone": "✍🏿", + "writing_hand_light_skin_tone": "✍🏻", + "writing_hand_medium-dark_skin_tone": "✍🏾", + "writing_hand_medium-light_skin_tone": "✍🏼", + "writing_hand_medium_skin_tone": "✍🏽", + "yarn": "🧶", + "yawning_face": "🥱", + "yellow_circle": "🟡", + "yellow_heart": "💛", + "yellow_square": "🟨", + "yen_banknote": "💴", + "yo-yo": "🪀", + "yin_yang": "☯", + "zany_face": "🤪", + "zebra": "🦓", + "zipper-mouth_face": "🤐", + "zombie": "🧟", + "zzz": "💤", + "åland_islands": "🇦🇽", + "keycap_asterisk": "*⃣", + "keycap_digit_eight": "8⃣", + "keycap_digit_five": "5⃣", + "keycap_digit_four": "4⃣", + "keycap_digit_nine": "9⃣", + "keycap_digit_one": "1⃣", + "keycap_digit_seven": "7⃣", + "keycap_digit_six": "6⃣", + "keycap_digit_three": "3⃣", + "keycap_digit_two": "2⃣", + "keycap_digit_zero": "0⃣", + "keycap_number_sign": "#⃣", + "light_skin_tone": "🏻", + "medium_light_skin_tone": "🏼", + "medium_skin_tone": "🏽", + "medium_dark_skin_tone": "🏾", + "dark_skin_tone": "🏿", + "regional_indicator_symbol_letter_a": "🇦", + "regional_indicator_symbol_letter_b": "🇧", + "regional_indicator_symbol_letter_c": "🇨", + "regional_indicator_symbol_letter_d": "🇩", + "regional_indicator_symbol_letter_e": "🇪", + "regional_indicator_symbol_letter_f": "🇫", + "regional_indicator_symbol_letter_g": "🇬", + "regional_indicator_symbol_letter_h": "🇭", + "regional_indicator_symbol_letter_i": "🇮", + "regional_indicator_symbol_letter_j": "🇯", + "regional_indicator_symbol_letter_k": "🇰", + "regional_indicator_symbol_letter_l": "🇱", + "regional_indicator_symbol_letter_m": "🇲", + "regional_indicator_symbol_letter_n": "🇳", + "regional_indicator_symbol_letter_o": "🇴", + "regional_indicator_symbol_letter_p": "🇵", + "regional_indicator_symbol_letter_q": "🇶", + "regional_indicator_symbol_letter_r": "🇷", + "regional_indicator_symbol_letter_s": "🇸", + "regional_indicator_symbol_letter_t": "🇹", + "regional_indicator_symbol_letter_u": "🇺", + "regional_indicator_symbol_letter_v": "🇻", + "regional_indicator_symbol_letter_w": "🇼", + "regional_indicator_symbol_letter_x": "🇽", + "regional_indicator_symbol_letter_y": "🇾", + "regional_indicator_symbol_letter_z": "🇿", + "airplane_arriving": "🛬", + "space_invader": "👾", + "football": "🏈", + "anger": "💢", + "angry": "😠", + "anguished": "😧", + "signal_strength": "📶", + "arrows_counterclockwise": "🔄", + "arrow_heading_down": "⤵", + "arrow_heading_up": "⤴", + "art": "🎨", + "astonished": "😲", + "athletic_shoe": "👟", + "atm": "🏧", + "car": "🚗", + "red_car": "🚗", + "angel": "👼", + "back": "🔙", + "badminton_racquet_and_shuttlecock": "🏸", + "dollar": "💵", + "euro": "💶", + "pound": "💷", + "yen": "💴", + "barber": "💈", + "bath": "🛀", + "bear": "🐻", + "heartbeat": "💓", + "beer": "🍺", + "no_bell": "🔕", + "bento": "🍱", + "bike": "🚲", + "bicyclist": "🚴", + "8ball": "🎱", + "biohazard_sign": "☣", + "birthday": "🎂", + "black_circle_for_record": "⏺", + "clubs": "♣", + "diamonds": "♦", + "arrow_double_down": "⏬", + "hearts": "♥", + "rewind": "⏪", + "black_left__pointing_double_triangle_with_vertical_bar": "⏮", + "arrow_backward": "◀", + "black_medium_small_square": "◾", + "question": "❓", + "fast_forward": "⏩", + "black_right__pointing_double_triangle_with_vertical_bar": "⏭", + "arrow_forward": "▶", + "black_right__pointing_triangle_with_double_vertical_bar": "⏯", + "arrow_right": "➡", + "spades": "♠", + "black_square_for_stop": "⏹", + "sunny": "☀", + "phone": "☎", + "recycle": "♻", + "arrow_double_up": "⏫", + "busstop": "🚏", + "date": "📅", + "flags": "🎏", + "cat2": "🐈", + "joy_cat": "😹", + "smirk_cat": "😼", + "chart_with_downwards_trend": "📉", + "chart_with_upwards_trend": "📈", + "chart": "💹", + "mega": "📣", + "checkered_flag": "🏁", + "accept": "🉑", + "ideograph_advantage": "🉐", + "congratulations": "㊗", + "secret": "㊙", + "m": "Ⓜ", + "city_sunset": "🌆", + "clapper": "🎬", + "clap": "👏", + "beers": "🍻", + "clock830": "🕣", + "clock8": "🕗", + "clock1130": "🕦", + "clock11": "🕚", + "clock530": "🕠", + "clock5": "🕔", + "clock430": "🕟", + "clock4": "🕓", + "clock930": "🕤", + "clock9": "🕘", + "clock130": "🕜", + "clock1": "🕐", + "clock730": "🕢", + "clock7": "🕖", + "clock630": "🕡", + "clock6": "🕕", + "clock1030": "🕥", + "clock10": "🕙", + "clock330": "🕞", + "clock3": "🕒", + "clock1230": "🕧", + "clock12": "🕛", + "clock230": "🕝", + "clock2": "🕑", + "arrows_clockwise": "🔃", + "repeat": "🔁", + "repeat_one": "🔂", + "closed_lock_with_key": "🔐", + "mailbox_closed": "📪", + "mailbox": "📫", + "cloud_with_tornado": "🌪", + "cocktail": "🍸", + "boom": "💥", + "compression": "🗜", + "confounded": "😖", + "confused": "😕", + "rice": "🍚", + "cow2": "🐄", + "cricket_bat_and_ball": "🏏", + "x": "❌", + "cry": "😢", + "curry": "🍛", + "dagger_knife": "🗡", + "dancer": "💃", + "dark_sunglasses": "🕶", + "dash": "💨", + "truck": "🚚", + "derelict_house_building": "🏚", + "diamond_shape_with_a_dot_inside": "💠", + "dart": "🎯", + "disappointed_relieved": "😥", + "disappointed": "😞", + "do_not_litter": "🚯", + "dog2": "🐕", + "flipper": "🐬", + "loop": "➿", + "bangbang": "‼", + "double_vertical_bar": "⏸", + "dove_of_peace": "🕊", + "small_red_triangle_down": "🔻", + "arrow_down_small": "🔽", + "arrow_down": "⬇", + "dromedary_camel": "🐪", + "e__mail": "📧", + "corn": "🌽", + "ear_of_rice": "🌾", + "earth_americas": "🌎", + "earth_asia": "🌏", + "earth_africa": "🌍", + "eight_pointed_black_star": "✴", + "eight_spoked_asterisk": "✳", + "eject_symbol": "⏏", + "bulb": "💡", + "emoji_modifier_fitzpatrick_type__1__2": "🏻", + "emoji_modifier_fitzpatrick_type__3": "🏼", + "emoji_modifier_fitzpatrick_type__4": "🏽", + "emoji_modifier_fitzpatrick_type__5": "🏾", + "emoji_modifier_fitzpatrick_type__6": "🏿", + "end": "🔚", + "email": "✉", + "european_castle": "🏰", + "european_post_office": "🏤", + "interrobang": "⁉", + "expressionless": "😑", + "eyeglasses": "👓", + "massage": "💆", + "yum": "😋", + "scream": "😱", + "kissing_heart": "😘", + "sweat": "😓", + "face_with_head__bandage": "🤕", + "triumph": "😤", + "mask": "😷", + "no_good": "🙅", + "ok_woman": "🙆", + "open_mouth": "😮", + "cold_sweat": "😰", + "stuck_out_tongue": "😛", + "stuck_out_tongue_closed_eyes": "😝", + "stuck_out_tongue_winking_eye": "😜", + "joy": "😂", + "no_mouth": "😶", + "santa": "🎅", + "fax": "📠", + "fearful": "😨", + "field_hockey_stick_and_ball": "🏑", + "first_quarter_moon_with_face": "🌛", + "fish_cake": "🍥", + "fishing_pole_and_fish": "🎣", + "facepunch": "👊", + "punch": "👊", + "flag_for_afghanistan": "🇦🇫", + "flag_for_albania": "🇦🇱", + "flag_for_algeria": "🇩🇿", + "flag_for_american_samoa": "🇦🇸", + "flag_for_andorra": "🇦🇩", + "flag_for_angola": "🇦🇴", + "flag_for_anguilla": "🇦🇮", + "flag_for_antarctica": "🇦🇶", + "flag_for_antigua_&_barbuda": "🇦🇬", + "flag_for_argentina": "🇦🇷", + "flag_for_armenia": "🇦🇲", + "flag_for_aruba": "🇦🇼", + "flag_for_ascension_island": "🇦🇨", + "flag_for_australia": "🇦🇺", + "flag_for_austria": "🇦🇹", + "flag_for_azerbaijan": "🇦🇿", + "flag_for_bahamas": "🇧🇸", + "flag_for_bahrain": "🇧🇭", + "flag_for_bangladesh": "🇧🇩", + "flag_for_barbados": "🇧🇧", + "flag_for_belarus": "🇧🇾", + "flag_for_belgium": "🇧🇪", + "flag_for_belize": "🇧🇿", + "flag_for_benin": "🇧🇯", + "flag_for_bermuda": "🇧🇲", + "flag_for_bhutan": "🇧🇹", + "flag_for_bolivia": "🇧🇴", + "flag_for_bosnia_&_herzegovina": "🇧🇦", + "flag_for_botswana": "🇧🇼", + "flag_for_bouvet_island": "🇧🇻", + "flag_for_brazil": "🇧🇷", + "flag_for_british_indian_ocean_territory": "🇮🇴", + "flag_for_british_virgin_islands": "🇻🇬", + "flag_for_brunei": "🇧🇳", + "flag_for_bulgaria": "🇧🇬", + "flag_for_burkina_faso": "🇧🇫", + "flag_for_burundi": "🇧🇮", + "flag_for_cambodia": "🇰🇭", + "flag_for_cameroon": "🇨🇲", + "flag_for_canada": "🇨🇦", + "flag_for_canary_islands": "🇮🇨", + "flag_for_cape_verde": "🇨🇻", + "flag_for_caribbean_netherlands": "🇧🇶", + "flag_for_cayman_islands": "🇰🇾", + "flag_for_central_african_republic": "🇨🇫", + "flag_for_ceuta_&_melilla": "🇪🇦", + "flag_for_chad": "🇹🇩", + "flag_for_chile": "🇨🇱", + "flag_for_china": "🇨🇳", + "flag_for_christmas_island": "🇨🇽", + "flag_for_clipperton_island": "🇨🇵", + "flag_for_cocos__islands": "🇨🇨", + "flag_for_colombia": "🇨🇴", + "flag_for_comoros": "🇰🇲", + "flag_for_congo____brazzaville": "🇨🇬", + "flag_for_congo____kinshasa": "🇨🇩", + "flag_for_cook_islands": "🇨🇰", + "flag_for_costa_rica": "🇨🇷", + "flag_for_croatia": "🇭🇷", + "flag_for_cuba": "🇨🇺", + "flag_for_curaçao": "🇨🇼", + "flag_for_cyprus": "🇨🇾", + "flag_for_czech_republic": "🇨🇿", + "flag_for_côte_d’ivoire": "🇨🇮", + "flag_for_denmark": "🇩🇰", + "flag_for_diego_garcia": "🇩🇬", + "flag_for_djibouti": "🇩🇯", + "flag_for_dominica": "🇩🇲", + "flag_for_dominican_republic": "🇩🇴", + "flag_for_ecuador": "🇪🇨", + "flag_for_egypt": "🇪🇬", + "flag_for_el_salvador": "🇸🇻", + "flag_for_equatorial_guinea": "🇬🇶", + "flag_for_eritrea": "🇪🇷", + "flag_for_estonia": "🇪🇪", + "flag_for_ethiopia": "🇪🇹", + "flag_for_european_union": "🇪🇺", + "flag_for_falkland_islands": "🇫🇰", + "flag_for_faroe_islands": "🇫🇴", + "flag_for_fiji": "🇫🇯", + "flag_for_finland": "🇫🇮", + "flag_for_france": "🇫🇷", + "flag_for_french_guiana": "🇬🇫", + "flag_for_french_polynesia": "🇵🇫", + "flag_for_french_southern_territories": "🇹🇫", + "flag_for_gabon": "🇬🇦", + "flag_for_gambia": "🇬🇲", + "flag_for_georgia": "🇬🇪", + "flag_for_germany": "🇩🇪", + "flag_for_ghana": "🇬🇭", + "flag_for_gibraltar": "🇬🇮", + "flag_for_greece": "🇬🇷", + "flag_for_greenland": "🇬🇱", + "flag_for_grenada": "🇬🇩", + "flag_for_guadeloupe": "🇬🇵", + "flag_for_guam": "🇬🇺", + "flag_for_guatemala": "🇬🇹", + "flag_for_guernsey": "🇬🇬", + "flag_for_guinea": "🇬🇳", + "flag_for_guinea__bissau": "🇬🇼", + "flag_for_guyana": "🇬🇾", + "flag_for_haiti": "🇭🇹", + "flag_for_heard_&_mcdonald_islands": "🇭🇲", + "flag_for_honduras": "🇭🇳", + "flag_for_hong_kong": "🇭🇰", + "flag_for_hungary": "🇭🇺", + "flag_for_iceland": "🇮🇸", + "flag_for_india": "🇮🇳", + "flag_for_indonesia": "🇮🇩", + "flag_for_iran": "🇮🇷", + "flag_for_iraq": "🇮🇶", + "flag_for_ireland": "🇮🇪", + "flag_for_isle_of_man": "🇮🇲", + "flag_for_israel": "🇮🇱", + "flag_for_italy": "🇮🇹", + "flag_for_jamaica": "🇯🇲", + "flag_for_japan": "🇯🇵", + "flag_for_jersey": "🇯🇪", + "flag_for_jordan": "🇯🇴", + "flag_for_kazakhstan": "🇰🇿", + "flag_for_kenya": "🇰🇪", + "flag_for_kiribati": "🇰🇮", + "flag_for_kosovo": "🇽🇰", + "flag_for_kuwait": "🇰🇼", + "flag_for_kyrgyzstan": "🇰🇬", + "flag_for_laos": "🇱🇦", + "flag_for_latvia": "🇱🇻", + "flag_for_lebanon": "🇱🇧", + "flag_for_lesotho": "🇱🇸", + "flag_for_liberia": "🇱🇷", + "flag_for_libya": "🇱🇾", + "flag_for_liechtenstein": "🇱🇮", + "flag_for_lithuania": "🇱🇹", + "flag_for_luxembourg": "🇱🇺", + "flag_for_macau": "🇲🇴", + "flag_for_macedonia": "🇲🇰", + "flag_for_madagascar": "🇲🇬", + "flag_for_malawi": "🇲🇼", + "flag_for_malaysia": "🇲🇾", + "flag_for_maldives": "🇲🇻", + "flag_for_mali": "🇲🇱", + "flag_for_malta": "🇲🇹", + "flag_for_marshall_islands": "🇲🇭", + "flag_for_martinique": "🇲🇶", + "flag_for_mauritania": "🇲🇷", + "flag_for_mauritius": "🇲🇺", + "flag_for_mayotte": "🇾🇹", + "flag_for_mexico": "🇲🇽", + "flag_for_micronesia": "🇫🇲", + "flag_for_moldova": "🇲🇩", + "flag_for_monaco": "🇲🇨", + "flag_for_mongolia": "🇲🇳", + "flag_for_montenegro": "🇲🇪", + "flag_for_montserrat": "🇲🇸", + "flag_for_morocco": "🇲🇦", + "flag_for_mozambique": "🇲🇿", + "flag_for_myanmar": "🇲🇲", + "flag_for_namibia": "🇳🇦", + "flag_for_nauru": "🇳🇷", + "flag_for_nepal": "🇳🇵", + "flag_for_netherlands": "🇳🇱", + "flag_for_new_caledonia": "🇳🇨", + "flag_for_new_zealand": "🇳🇿", + "flag_for_nicaragua": "🇳🇮", + "flag_for_niger": "🇳🇪", + "flag_for_nigeria": "🇳🇬", + "flag_for_niue": "🇳🇺", + "flag_for_norfolk_island": "🇳🇫", + "flag_for_north_korea": "🇰🇵", + "flag_for_northern_mariana_islands": "🇲🇵", + "flag_for_norway": "🇳🇴", + "flag_for_oman": "🇴🇲", + "flag_for_pakistan": "🇵🇰", + "flag_for_palau": "🇵🇼", + "flag_for_palestinian_territories": "🇵🇸", + "flag_for_panama": "🇵🇦", + "flag_for_papua_new_guinea": "🇵🇬", + "flag_for_paraguay": "🇵🇾", + "flag_for_peru": "🇵🇪", + "flag_for_philippines": "🇵🇭", + "flag_for_pitcairn_islands": "🇵🇳", + "flag_for_poland": "🇵🇱", + "flag_for_portugal": "🇵🇹", + "flag_for_puerto_rico": "🇵🇷", + "flag_for_qatar": "🇶🇦", + "flag_for_romania": "🇷🇴", + "flag_for_russia": "🇷🇺", + "flag_for_rwanda": "🇷🇼", + "flag_for_réunion": "🇷🇪", + "flag_for_samoa": "🇼🇸", + "flag_for_san_marino": "🇸🇲", + "flag_for_saudi_arabia": "🇸🇦", + "flag_for_senegal": "🇸🇳", + "flag_for_serbia": "🇷🇸", + "flag_for_seychelles": "🇸🇨", + "flag_for_sierra_leone": "🇸🇱", + "flag_for_singapore": "🇸🇬", + "flag_for_sint_maarten": "🇸🇽", + "flag_for_slovakia": "🇸🇰", + "flag_for_slovenia": "🇸🇮", + "flag_for_solomon_islands": "🇸🇧", + "flag_for_somalia": "🇸🇴", + "flag_for_south_africa": "🇿🇦", + "flag_for_south_georgia_&_south_sandwich_islands": "🇬🇸", + "flag_for_south_korea": "🇰🇷", + "flag_for_south_sudan": "🇸🇸", + "flag_for_spain": "🇪🇸", + "flag_for_sri_lanka": "🇱🇰", + "flag_for_st._barthélemy": "🇧🇱", + "flag_for_st._helena": "🇸🇭", + "flag_for_st._kitts_&_nevis": "🇰🇳", + "flag_for_st._lucia": "🇱🇨", + "flag_for_st._martin": "🇲🇫", + "flag_for_st._pierre_&_miquelon": "🇵🇲", + "flag_for_st._vincent_&_grenadines": "🇻🇨", + "flag_for_sudan": "🇸🇩", + "flag_for_suriname": "🇸🇷", + "flag_for_svalbard_&_jan_mayen": "🇸🇯", + "flag_for_swaziland": "🇸🇿", + "flag_for_sweden": "🇸🇪", + "flag_for_switzerland": "🇨🇭", + "flag_for_syria": "🇸🇾", + "flag_for_são_tomé_&_príncipe": "🇸🇹", + "flag_for_taiwan": "🇹🇼", + "flag_for_tajikistan": "🇹🇯", + "flag_for_tanzania": "🇹🇿", + "flag_for_thailand": "🇹🇭", + "flag_for_timor__leste": "🇹🇱", + "flag_for_togo": "🇹🇬", + "flag_for_tokelau": "🇹🇰", + "flag_for_tonga": "🇹🇴", + "flag_for_trinidad_&_tobago": "🇹🇹", + "flag_for_tristan_da_cunha": "🇹🇦", + "flag_for_tunisia": "🇹🇳", + "flag_for_turkey": "🇹🇷", + "flag_for_turkmenistan": "🇹🇲", + "flag_for_turks_&_caicos_islands": "🇹🇨", + "flag_for_tuvalu": "🇹🇻", + "flag_for_u.s._outlying_islands": "🇺🇲", + "flag_for_u.s._virgin_islands": "🇻🇮", + "flag_for_uganda": "🇺🇬", + "flag_for_ukraine": "🇺🇦", + "flag_for_united_arab_emirates": "🇦🇪", + "flag_for_united_kingdom": "🇬🇧", + "flag_for_united_states": "🇺🇸", + "flag_for_uruguay": "🇺🇾", + "flag_for_uzbekistan": "🇺🇿", + "flag_for_vanuatu": "🇻🇺", + "flag_for_vatican_city": "🇻🇦", + "flag_for_venezuela": "🇻🇪", + "flag_for_vietnam": "🇻🇳", + "flag_for_wallis_&_futuna": "🇼🇫", + "flag_for_western_sahara": "🇪🇭", + "flag_for_yemen": "🇾🇪", + "flag_for_zambia": "🇿🇲", + "flag_for_zimbabwe": "🇿🇼", + "flag_for_åland_islands": "🇦🇽", + "golf": "⛳", + "fleur__de__lis": "⚜", + "muscle": "💪", + "flushed": "😳", + "frame_with_picture": "🖼", + "fries": "🍟", + "frog": "🐸", + "hatched_chick": "🐥", + "frowning": "😦", + "fuelpump": "⛽", + "full_moon_with_face": "🌝", + "gem": "💎", + "star2": "🌟", + "golfer": "🏌", + "mortar_board": "🎓", + "grimacing": "😬", + "smile_cat": "😸", + "grinning": "😀", + "grin": "😁", + "heartpulse": "💗", + "guardsman": "💂", + "haircut": "💇", + "hamster": "🐹", + "raising_hand": "🙋", + "headphones": "🎧", + "hear_no_evil": "🙉", + "cupid": "💘", + "gift_heart": "💝", + "heart": "❤", + "exclamation": "❗", + "heavy_exclamation_mark": "❗", + "heavy_heart_exclamation_mark_ornament": "❣", + "o": "⭕", + "helm_symbol": "⎈", + "helmet_with_white_cross": "⛑", + "high_heel": "👠", + "bullettrain_side": "🚄", + "bullettrain_front": "🚅", + "high_brightness": "🔆", + "zap": "⚡", + "hocho": "🔪", + "knife": "🔪", + "bee": "🐝", + "traffic_light": "🚥", + "racehorse": "🐎", + "coffee": "☕", + "hotsprings": "♨", + "hourglass": "⌛", + "hourglass_flowing_sand": "⏳", + "house_buildings": "🏘", + "100": "💯", + "hushed": "😯", + "ice_hockey_stick_and_puck": "🏒", + "imp": "👿", + "information_desk_person": "💁", + "information_source": "ℹ", + "capital_abcd": "🔠", + "abc": "🔤", + "abcd": "🔡", + "1234": "🔢", + "symbols": "🔣", + "izakaya_lantern": "🏮", + "lantern": "🏮", + "jack_o_lantern": "🎃", + "dolls": "🎎", + "japanese_goblin": "👺", + "japanese_ogre": "👹", + "beginner": "🔰", + "zero": "0️⃣", + "one": "1️⃣", + "ten": "🔟", + "two": "2️⃣", + "three": "3️⃣", + "four": "4️⃣", + "five": "5️⃣", + "six": "6️⃣", + "seven": "7️⃣", + "eight": "8️⃣", + "nine": "9️⃣", + "couplekiss": "💏", + "kissing_cat": "😽", + "kissing": "😗", + "kissing_closed_eyes": "😚", + "kissing_smiling_eyes": "😙", + "beetle": "🐞", + "large_blue_circle": "🔵", + "last_quarter_moon_with_face": "🌜", + "leaves": "🍃", + "mag": "🔍", + "left_right_arrow": "↔", + "leftwards_arrow_with_hook": "↩", + "arrow_left": "⬅", + "lock": "🔒", + "lock_with_ink_pen": "🔏", + "sob": "😭", + "low_brightness": "🔅", + "lower_left_ballpoint_pen": "🖊", + "lower_left_crayon": "🖍", + "lower_left_fountain_pen": "🖋", + "lower_left_paintbrush": "🖌", + "mahjong": "🀄", + "couple": "👫", + "man_in_business_suit_levitating": "🕴", + "man_with_gua_pi_mao": "👲", + "man_with_turban": "👳", + "mans_shoe": "👞", + "shoe": "👞", + "menorah_with_nine_branches": "🕎", + "mens": "🚹", + "minidisc": "💽", + "iphone": "📱", + "calling": "📲", + "money__mouth_face": "🤑", + "moneybag": "💰", + "rice_scene": "🎑", + "mountain_bicyclist": "🚵", + "mouse2": "🐁", + "lips": "👄", + "moyai": "🗿", + "notes": "🎶", + "nail_care": "💅", + "ab": "🆎", + "negative_squared_cross_mark": "❎", + "a": "🅰", + "b": "🅱", + "o2": "🅾", + "parking": "🅿", + "new_moon_with_face": "🌚", + "no_entry_sign": "🚫", + "underage": "🔞", + "non__potable_water": "🚱", + "arrow_upper_right": "↗", + "arrow_upper_left": "↖", + "office": "🏢", + "older_man": "👴", + "older_woman": "👵", + "om_symbol": "🕉", + "on": "🔛", + "book": "📖", + "unlock": "🔓", + "mailbox_with_no_mail": "📭", + "mailbox_with_mail": "📬", + "cd": "💿", + "tada": "🎉", + "feet": "🐾", + "walking": "🚶", + "pencil2": "✏", + "pensive": "😔", + "persevere": "😣", + "bow": "🙇", + "raised_hands": "🙌", + "person_with_ball": "⛹", + "person_with_blond_hair": "👱", + "pray": "🙏", + "person_with_pouting_face": "🙎", + "computer": "💻", + "pig2": "🐖", + "hankey": "💩", + "poop": "💩", + "shit": "💩", + "bamboo": "🎍", + "gun": "🔫", + "black_joker": "🃏", + "rotating_light": "🚨", + "cop": "👮", + "stew": "🍲", + "pouch": "👝", + "pouting_cat": "😾", + "rage": "😡", + "put_litter_in_its_place": "🚮", + "rabbit2": "🐇", + "racing_motorcycle": "🏍", + "radioactive_sign": "☢", + "fist": "✊", + "hand": "✋", + "raised_hand_with_fingers_splayed": "🖐", + "raised_hand_with_part_between_middle_and_ring_fingers": "🖖", + "blue_car": "🚙", + "apple": "🍎", + "relieved": "😌", + "reversed_hand_with_middle_finger_extended": "🖕", + "mag_right": "🔎", + "arrow_right_hook": "↪", + "sweet_potato": "🍠", + "robot": "🤖", + "rolled__up_newspaper": "🗞", + "rowboat": "🚣", + "runner": "🏃", + "running": "🏃", + "running_shirt_with_sash": "🎽", + "boat": "⛵", + "scales": "⚖", + "school_satchel": "🎒", + "scorpius": "♏", + "see_no_evil": "🙈", + "sheep": "🐑", + "stars": "🌠", + "cake": "🍰", + "six_pointed_star": "🔯", + "ski": "🎿", + "sleeping_accommodation": "🛌", + "sleeping": "😴", + "sleepy": "😪", + "sleuth_or_spy": "🕵", + "heart_eyes_cat": "😻", + "smiley_cat": "😺", + "innocent": "😇", + "heart_eyes": "😍", + "smiling_imp": "😈", + "smiley": "😃", + "sweat_smile": "😅", + "smile": "😄", + "laughing": "😆", + "satisfied": "😆", + "blush": "😊", + "smirk": "😏", + "smoking": "🚬", + "snow_capped_mountain": "🏔", + "soccer": "⚽", + "icecream": "🍦", + "soon": "🔜", + "arrow_lower_right": "↘", + "arrow_lower_left": "↙", + "speak_no_evil": "🙊", + "speaker": "🔈", + "mute": "🔇", + "sound": "🔉", + "loud_sound": "🔊", + "speaking_head_in_silhouette": "🗣", + "spiral_calendar_pad": "🗓", + "spiral_note_pad": "🗒", + "shell": "🐚", + "sweat_drops": "💦", + "u5272": "🈹", + "u5408": "🈴", + "u55b6": "🈺", + "u6307": "🈯", + "u6708": "🈷", + "u6709": "🈶", + "u6e80": "🈵", + "u7121": "🈚", + "u7533": "🈸", + "u7981": "🈲", + "u7a7a": "🈳", + "cl": "🆑", + "cool": "🆒", + "free": "🆓", + "id": "🆔", + "koko": "🈁", + "sa": "🈂", + "new": "🆕", + "ng": "🆖", + "ok": "🆗", + "sos": "🆘", + "up": "🆙", + "vs": "🆚", + "steam_locomotive": "🚂", + "ramen": "🍜", + "partly_sunny": "⛅", + "city_sunrise": "🌇", + "surfer": "🏄", + "swimmer": "🏊", + "shirt": "👕", + "tshirt": "👕", + "table_tennis_paddle_and_ball": "🏓", + "tea": "🍵", + "tv": "📺", + "three_button_mouse": "🖱", + "+1": "👍", + "thumbsup": "👍", + "__1": "👎", + "-1": "👎", + "thumbsdown": "👎", + "thunder_cloud_and_rain": "⛈", + "tiger2": "🐅", + "tophat": "🎩", + "top": "🔝", + "tm": "™", + "train2": "🚆", + "triangular_flag_on_post": "🚩", + "trident": "🔱", + "twisted_rightwards_arrows": "🔀", + "unamused": "😒", + "small_red_triangle": "🔺", + "arrow_up_small": "🔼", + "arrow_up_down": "↕", + "upside__down_face": "🙃", + "arrow_up": "⬆", + "v": "✌", + "vhs": "📼", + "wc": "🚾", + "ocean": "🌊", + "waving_black_flag": "🏴", + "wave": "👋", + "waving_white_flag": "🏳", + "moon": "🌔", + "scream_cat": "🙀", + "weary": "😩", + "weight_lifter": "🏋", + "whale2": "🐋", + "wheelchair": "♿", + "point_down": "👇", + "grey_exclamation": "❕", + "white_frowning_face": "☹", + "white_check_mark": "✅", + "point_left": "👈", + "white_medium_small_square": "◽", + "star": "⭐", + "grey_question": "❔", + "point_right": "👉", + "relaxed": "☺", + "white_sun_behind_cloud": "🌥", + "white_sun_behind_cloud_with_rain": "🌦", + "white_sun_with_small_cloud": "🌤", + "point_up_2": "👆", + "point_up": "☝", + "wind_blowing_face": "🌬", + "wink": "😉", + "wolf": "🐺", + "dancers": "👯", + "boot": "👢", + "womans_clothes": "👚", + "womans_hat": "👒", + "sandal": "👡", + "womens": "🚺", + "worried": "😟", + "gift": "🎁", + "zipper__mouth_face": "🤐", + "regional_indicator_a": "🇦", + "regional_indicator_b": "🇧", + "regional_indicator_c": "🇨", + "regional_indicator_d": "🇩", + "regional_indicator_e": "🇪", + "regional_indicator_f": "🇫", + "regional_indicator_g": "🇬", + "regional_indicator_h": "🇭", + "regional_indicator_i": "🇮", + "regional_indicator_j": "🇯", + "regional_indicator_k": "🇰", + "regional_indicator_l": "🇱", + "regional_indicator_m": "🇲", + "regional_indicator_n": "🇳", + "regional_indicator_o": "🇴", + "regional_indicator_p": "🇵", + "regional_indicator_q": "🇶", + "regional_indicator_r": "🇷", + "regional_indicator_s": "🇸", + "regional_indicator_t": "🇹", + "regional_indicator_u": "🇺", + "regional_indicator_v": "🇻", + "regional_indicator_w": "🇼", + "regional_indicator_x": "🇽", + "regional_indicator_y": "🇾", + "regional_indicator_z": "🇿", +} diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_replace.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_replace.py new file mode 100644 index 0000000..bb2cafa --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_replace.py @@ -0,0 +1,32 @@ +from typing import Callable, Match, Optional +import re + +from ._emoji_codes import EMOJI + + +_ReStringMatch = Match[str] # regex match object +_ReSubCallable = Callable[[_ReStringMatch], str] # Callable invoked by re.sub +_EmojiSubMethod = Callable[[_ReSubCallable, str], str] # Sub method of a compiled re + + +def _emoji_replace( + text: str, + default_variant: Optional[str] = None, + _emoji_sub: _EmojiSubMethod = re.compile(r"(:(\S*?)(?:(?:\-)(emoji|text))?:)").sub, +) -> str: + """Replace emoji code in text.""" + get_emoji = EMOJI.__getitem__ + variants = {"text": "\uFE0E", "emoji": "\uFE0F"} + get_variant = variants.get + default_variant_code = variants.get(default_variant, "") if default_variant else "" + + def do_replace(match: Match[str]) -> str: + emoji_code, emoji_name, variant = match.groups() + try: + return get_emoji(emoji_name.lower()) + get_variant( + variant, default_variant_code + ) + except KeyError: + return emoji_code + + return _emoji_sub(do_replace, text) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_export_format.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_export_format.py new file mode 100644 index 0000000..e7527e5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_export_format.py @@ -0,0 +1,76 @@ +CONSOLE_HTML_FORMAT = """\ + + + + + + + +
{code}
+ + +""" + +CONSOLE_SVG_FORMAT = """\ + + + + + + + + + {lines} + + + {chrome} + + {backgrounds} + + {matrix} + + + +""" + +_SVG_FONT_FAMILY = "Rich Fira Code" +_SVG_CLASSES_PREFIX = "rich-svg" diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_extension.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_extension.py new file mode 100644 index 0000000..cbd6da9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_extension.py @@ -0,0 +1,10 @@ +from typing import Any + + +def load_ipython_extension(ip: Any) -> None: # pragma: no cover + # prevent circular import + from pip._vendor.rich.pretty import install + from pip._vendor.rich.traceback import install as tr_install + + install() + tr_install() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_fileno.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_fileno.py new file mode 100644 index 0000000..b17ee65 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_fileno.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import IO, Callable + + +def get_fileno(file_like: IO[str]) -> int | None: + """Get fileno() from a file, accounting for poorly implemented file-like objects. + + Args: + file_like (IO): A file-like object. + + Returns: + int | None: The result of fileno if available, or None if operation failed. + """ + fileno: Callable[[], int] | None = getattr(file_like, "fileno", None) + if fileno is not None: + try: + return fileno() + except Exception: + # `fileno` is documented as potentially raising a OSError + # Alas, from the issues, there are so many poorly implemented file-like objects, + # that `fileno()` can raise just about anything. + return None + return None diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_inspect.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_inspect.py new file mode 100644 index 0000000..27d65ce --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_inspect.py @@ -0,0 +1,268 @@ +import inspect +from inspect import cleandoc, getdoc, getfile, isclass, ismodule, signature +from typing import Any, Collection, Iterable, Optional, Tuple, Type, Union + +from .console import Group, RenderableType +from .control import escape_control_codes +from .highlighter import ReprHighlighter +from .jupyter import JupyterMixin +from .panel import Panel +from .pretty import Pretty +from .table import Table +from .text import Text, TextType + + +def _first_paragraph(doc: str) -> str: + """Get the first paragraph from a docstring.""" + paragraph, _, _ = doc.partition("\n\n") + return paragraph + + +class Inspect(JupyterMixin): + """A renderable to inspect any Python Object. + + Args: + obj (Any): An object to inspect. + title (str, optional): Title to display over inspect result, or None use type. Defaults to None. + help (bool, optional): Show full help text rather than just first paragraph. Defaults to False. + methods (bool, optional): Enable inspection of callables. Defaults to False. + docs (bool, optional): Also render doc strings. Defaults to True. + private (bool, optional): Show private attributes (beginning with underscore). Defaults to False. + dunder (bool, optional): Show attributes starting with double underscore. Defaults to False. + sort (bool, optional): Sort attributes alphabetically. Defaults to True. + all (bool, optional): Show all attributes. Defaults to False. + value (bool, optional): Pretty print value of object. Defaults to True. + """ + + def __init__( + self, + obj: Any, + *, + title: Optional[TextType] = None, + help: bool = False, + methods: bool = False, + docs: bool = True, + private: bool = False, + dunder: bool = False, + sort: bool = True, + all: bool = True, + value: bool = True, + ) -> None: + self.highlighter = ReprHighlighter() + self.obj = obj + self.title = title or self._make_title(obj) + if all: + methods = private = dunder = True + self.help = help + self.methods = methods + self.docs = docs or help + self.private = private or dunder + self.dunder = dunder + self.sort = sort + self.value = value + + def _make_title(self, obj: Any) -> Text: + """Make a default title.""" + title_str = ( + str(obj) + if (isclass(obj) or callable(obj) or ismodule(obj)) + else str(type(obj)) + ) + title_text = self.highlighter(title_str) + return title_text + + def __rich__(self) -> Panel: + return Panel.fit( + Group(*self._render()), + title=self.title, + border_style="scope.border", + padding=(0, 1), + ) + + def _get_signature(self, name: str, obj: Any) -> Optional[Text]: + """Get a signature for a callable.""" + try: + _signature = str(signature(obj)) + ":" + except ValueError: + _signature = "(...)" + except TypeError: + return None + + source_filename: Optional[str] = None + try: + source_filename = getfile(obj) + except (OSError, TypeError): + # OSError is raised if obj has no source file, e.g. when defined in REPL. + pass + + callable_name = Text(name, style="inspect.callable") + if source_filename: + callable_name.stylize(f"link file://{source_filename}") + signature_text = self.highlighter(_signature) + + qualname = name or getattr(obj, "__qualname__", name) + + # If obj is a module, there may be classes (which are callable) to display + if inspect.isclass(obj): + prefix = "class" + elif inspect.iscoroutinefunction(obj): + prefix = "async def" + else: + prefix = "def" + + qual_signature = Text.assemble( + (f"{prefix} ", f"inspect.{prefix.replace(' ', '_')}"), + (qualname, "inspect.callable"), + signature_text, + ) + + return qual_signature + + def _render(self) -> Iterable[RenderableType]: + """Render object.""" + + def sort_items(item: Tuple[str, Any]) -> Tuple[bool, str]: + key, (_error, value) = item + return (callable(value), key.strip("_").lower()) + + def safe_getattr(attr_name: str) -> Tuple[Any, Any]: + """Get attribute or any exception.""" + try: + return (None, getattr(obj, attr_name)) + except Exception as error: + return (error, None) + + obj = self.obj + keys = dir(obj) + total_items = len(keys) + if not self.dunder: + keys = [key for key in keys if not key.startswith("__")] + if not self.private: + keys = [key for key in keys if not key.startswith("_")] + not_shown_count = total_items - len(keys) + items = [(key, safe_getattr(key)) for key in keys] + if self.sort: + items.sort(key=sort_items) + + items_table = Table.grid(padding=(0, 1), expand=False) + items_table.add_column(justify="right") + add_row = items_table.add_row + highlighter = self.highlighter + + if callable(obj): + signature = self._get_signature("", obj) + if signature is not None: + yield signature + yield "" + + if self.docs: + _doc = self._get_formatted_doc(obj) + if _doc is not None: + doc_text = Text(_doc, style="inspect.help") + doc_text = highlighter(doc_text) + yield doc_text + yield "" + + if self.value and not (isclass(obj) or callable(obj) or ismodule(obj)): + yield Panel( + Pretty(obj, indent_guides=True, max_length=10, max_string=60), + border_style="inspect.value.border", + ) + yield "" + + for key, (error, value) in items: + key_text = Text.assemble( + ( + key, + "inspect.attr.dunder" if key.startswith("__") else "inspect.attr", + ), + (" =", "inspect.equals"), + ) + if error is not None: + warning = key_text.copy() + warning.stylize("inspect.error") + add_row(warning, highlighter(repr(error))) + continue + + if callable(value): + if not self.methods: + continue + + _signature_text = self._get_signature(key, value) + if _signature_text is None: + add_row(key_text, Pretty(value, highlighter=highlighter)) + else: + if self.docs: + docs = self._get_formatted_doc(value) + if docs is not None: + _signature_text.append("\n" if "\n" in docs else " ") + doc = highlighter(docs) + doc.stylize("inspect.doc") + _signature_text.append(doc) + + add_row(key_text, _signature_text) + else: + add_row(key_text, Pretty(value, highlighter=highlighter)) + if items_table.row_count: + yield items_table + elif not_shown_count: + yield Text.from_markup( + f"[b cyan]{not_shown_count}[/][i] attribute(s) not shown.[/i] " + f"Run [b][magenta]inspect[/]([not b]inspect[/])[/b] for options." + ) + + def _get_formatted_doc(self, object_: Any) -> Optional[str]: + """ + Extract the docstring of an object, process it and returns it. + The processing consists in cleaning up the docstring's indentation, + taking only its 1st paragraph if `self.help` is not True, + and escape its control codes. + + Args: + object_ (Any): the object to get the docstring from. + + Returns: + Optional[str]: the processed docstring, or None if no docstring was found. + """ + docs = getdoc(object_) + if docs is None: + return None + docs = cleandoc(docs).strip() + if not self.help: + docs = _first_paragraph(docs) + return escape_control_codes(docs) + + +def get_object_types_mro(obj: Union[object, Type[Any]]) -> Tuple[type, ...]: + """Returns the MRO of an object's class, or of the object itself if it's a class.""" + if not hasattr(obj, "__mro__"): + # N.B. we cannot use `if type(obj) is type` here because it doesn't work with + # some types of classes, such as the ones that use abc.ABCMeta. + obj = type(obj) + return getattr(obj, "__mro__", ()) + + +def get_object_types_mro_as_strings(obj: object) -> Collection[str]: + """ + Returns the MRO of an object's class as full qualified names, or of the object itself if it's a class. + + Examples: + `object_types_mro_as_strings(JSONDecoder)` will return `['json.decoder.JSONDecoder', 'builtins.object']` + """ + return [ + f'{getattr(type_, "__module__", "")}.{getattr(type_, "__qualname__", "")}' + for type_ in get_object_types_mro(obj) + ] + + +def is_object_one_of_types( + obj: object, fully_qualified_types_names: Collection[str] +) -> bool: + """ + Returns `True` if the given object's class (or the object itself, if it's a class) has one of the + fully qualified names in its MRO. + """ + for type_name in get_object_types_mro_as_strings(obj): + if type_name in fully_qualified_types_names: + return True + return False diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_log_render.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_log_render.py new file mode 100644 index 0000000..fc16c84 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_log_render.py @@ -0,0 +1,94 @@ +from datetime import datetime +from typing import Iterable, List, Optional, TYPE_CHECKING, Union, Callable + + +from .text import Text, TextType + +if TYPE_CHECKING: + from .console import Console, ConsoleRenderable, RenderableType + from .table import Table + +FormatTimeCallable = Callable[[datetime], Text] + + +class LogRender: + def __init__( + self, + show_time: bool = True, + show_level: bool = False, + show_path: bool = True, + time_format: Union[str, FormatTimeCallable] = "[%x %X]", + omit_repeated_times: bool = True, + level_width: Optional[int] = 8, + ) -> None: + self.show_time = show_time + self.show_level = show_level + self.show_path = show_path + self.time_format = time_format + self.omit_repeated_times = omit_repeated_times + self.level_width = level_width + self._last_time: Optional[Text] = None + + def __call__( + self, + console: "Console", + renderables: Iterable["ConsoleRenderable"], + log_time: Optional[datetime] = None, + time_format: Optional[Union[str, FormatTimeCallable]] = None, + level: TextType = "", + path: Optional[str] = None, + line_no: Optional[int] = None, + link_path: Optional[str] = None, + ) -> "Table": + from .containers import Renderables + from .table import Table + + output = Table.grid(padding=(0, 1)) + output.expand = True + if self.show_time: + output.add_column(style="log.time") + if self.show_level: + output.add_column(style="log.level", width=self.level_width) + output.add_column(ratio=1, style="log.message", overflow="fold") + if self.show_path and path: + output.add_column(style="log.path") + row: List["RenderableType"] = [] + if self.show_time: + log_time = log_time or console.get_datetime() + time_format = time_format or self.time_format + if callable(time_format): + log_time_display = time_format(log_time) + else: + log_time_display = Text(log_time.strftime(time_format)) + if log_time_display == self._last_time and self.omit_repeated_times: + row.append(Text(" " * len(log_time_display))) + else: + row.append(log_time_display) + self._last_time = log_time_display + if self.show_level: + row.append(level) + + row.append(Renderables(renderables)) + if self.show_path and path: + path_text = Text() + path_text.append( + path, style=f"link file://{link_path}" if link_path else "" + ) + if line_no: + path_text.append(":") + path_text.append( + f"{line_no}", + style=f"link file://{link_path}#{line_no}" if link_path else "", + ) + row.append(path_text) + + output.add_row(*row) + return output + + +if __name__ == "__main__": # pragma: no cover + from pip._vendor.rich.console import Console + + c = Console() + c.print("[on blue]Hello", justify="right") + c.log("[on blue]hello", justify="right") diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_loop.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_loop.py new file mode 100644 index 0000000..01c6caf --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_loop.py @@ -0,0 +1,43 @@ +from typing import Iterable, Tuple, TypeVar + +T = TypeVar("T") + + +def loop_first(values: Iterable[T]) -> Iterable[Tuple[bool, T]]: + """Iterate and generate a tuple with a flag for first value.""" + iter_values = iter(values) + try: + value = next(iter_values) + except StopIteration: + return + yield True, value + for value in iter_values: + yield False, value + + +def loop_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]: + """Iterate and generate a tuple with a flag for last value.""" + iter_values = iter(values) + try: + previous_value = next(iter_values) + except StopIteration: + return + for value in iter_values: + yield False, previous_value + previous_value = value + yield True, previous_value + + +def loop_first_last(values: Iterable[T]) -> Iterable[Tuple[bool, bool, T]]: + """Iterate and generate a tuple with a flag for first and last value.""" + iter_values = iter(values) + try: + previous_value = next(iter_values) + except StopIteration: + return + first = True + for value in iter_values: + yield first, False, previous_value + first = False + previous_value = value + yield first, True, previous_value diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_null_file.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_null_file.py new file mode 100644 index 0000000..6ae05d3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_null_file.py @@ -0,0 +1,69 @@ +from types import TracebackType +from typing import IO, Iterable, Iterator, List, Optional, Type + + +class NullFile(IO[str]): + def close(self) -> None: + pass + + def isatty(self) -> bool: + return False + + def read(self, __n: int = 1) -> str: + return "" + + def readable(self) -> bool: + return False + + def readline(self, __limit: int = 1) -> str: + return "" + + def readlines(self, __hint: int = 1) -> List[str]: + return [] + + def seek(self, __offset: int, __whence: int = 1) -> int: + return 0 + + def seekable(self) -> bool: + return False + + def tell(self) -> int: + return 0 + + def truncate(self, __size: Optional[int] = 1) -> int: + return 0 + + def writable(self) -> bool: + return False + + def writelines(self, __lines: Iterable[str]) -> None: + pass + + def __next__(self) -> str: + return "" + + def __iter__(self) -> Iterator[str]: + return iter([""]) + + def __enter__(self) -> IO[str]: + return self + + def __exit__( + self, + __t: Optional[Type[BaseException]], + __value: Optional[BaseException], + __traceback: Optional[TracebackType], + ) -> None: + pass + + def write(self, text: str) -> int: + return 0 + + def flush(self) -> None: + pass + + def fileno(self) -> int: + return -1 + + +NULL_FILE = NullFile() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_palettes.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_palettes.py new file mode 100644 index 0000000..3c748d3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_palettes.py @@ -0,0 +1,309 @@ +from .palette import Palette + + +# Taken from https://en.wikipedia.org/wiki/ANSI_escape_code (Windows 10 column) +WINDOWS_PALETTE = Palette( + [ + (12, 12, 12), + (197, 15, 31), + (19, 161, 14), + (193, 156, 0), + (0, 55, 218), + (136, 23, 152), + (58, 150, 221), + (204, 204, 204), + (118, 118, 118), + (231, 72, 86), + (22, 198, 12), + (249, 241, 165), + (59, 120, 255), + (180, 0, 158), + (97, 214, 214), + (242, 242, 242), + ] +) + +# # The standard ansi colors (including bright variants) +STANDARD_PALETTE = Palette( + [ + (0, 0, 0), + (170, 0, 0), + (0, 170, 0), + (170, 85, 0), + (0, 0, 170), + (170, 0, 170), + (0, 170, 170), + (170, 170, 170), + (85, 85, 85), + (255, 85, 85), + (85, 255, 85), + (255, 255, 85), + (85, 85, 255), + (255, 85, 255), + (85, 255, 255), + (255, 255, 255), + ] +) + + +# The 256 color palette +EIGHT_BIT_PALETTE = Palette( + [ + (0, 0, 0), + (128, 0, 0), + (0, 128, 0), + (128, 128, 0), + (0, 0, 128), + (128, 0, 128), + (0, 128, 128), + (192, 192, 192), + (128, 128, 128), + (255, 0, 0), + (0, 255, 0), + (255, 255, 0), + (0, 0, 255), + (255, 0, 255), + (0, 255, 255), + (255, 255, 255), + (0, 0, 0), + (0, 0, 95), + (0, 0, 135), + (0, 0, 175), + (0, 0, 215), + (0, 0, 255), + (0, 95, 0), + (0, 95, 95), + (0, 95, 135), + (0, 95, 175), + (0, 95, 215), + (0, 95, 255), + (0, 135, 0), + (0, 135, 95), + (0, 135, 135), + (0, 135, 175), + (0, 135, 215), + (0, 135, 255), + (0, 175, 0), + (0, 175, 95), + (0, 175, 135), + (0, 175, 175), + (0, 175, 215), + (0, 175, 255), + (0, 215, 0), + (0, 215, 95), + (0, 215, 135), + (0, 215, 175), + (0, 215, 215), + (0, 215, 255), + (0, 255, 0), + (0, 255, 95), + (0, 255, 135), + (0, 255, 175), + (0, 255, 215), + (0, 255, 255), + (95, 0, 0), + (95, 0, 95), + (95, 0, 135), + (95, 0, 175), + (95, 0, 215), + (95, 0, 255), + (95, 95, 0), + (95, 95, 95), + (95, 95, 135), + (95, 95, 175), + (95, 95, 215), + (95, 95, 255), + (95, 135, 0), + (95, 135, 95), + (95, 135, 135), + (95, 135, 175), + (95, 135, 215), + (95, 135, 255), + (95, 175, 0), + (95, 175, 95), + (95, 175, 135), + (95, 175, 175), + (95, 175, 215), + (95, 175, 255), + (95, 215, 0), + (95, 215, 95), + (95, 215, 135), + (95, 215, 175), + (95, 215, 215), + (95, 215, 255), + (95, 255, 0), + (95, 255, 95), + (95, 255, 135), + (95, 255, 175), + (95, 255, 215), + (95, 255, 255), + (135, 0, 0), + (135, 0, 95), + (135, 0, 135), + (135, 0, 175), + (135, 0, 215), + (135, 0, 255), + (135, 95, 0), + (135, 95, 95), + (135, 95, 135), + (135, 95, 175), + (135, 95, 215), + (135, 95, 255), + (135, 135, 0), + (135, 135, 95), + (135, 135, 135), + (135, 135, 175), + (135, 135, 215), + (135, 135, 255), + (135, 175, 0), + (135, 175, 95), + (135, 175, 135), + (135, 175, 175), + (135, 175, 215), + (135, 175, 255), + (135, 215, 0), + (135, 215, 95), + (135, 215, 135), + (135, 215, 175), + (135, 215, 215), + (135, 215, 255), + (135, 255, 0), + (135, 255, 95), + (135, 255, 135), + (135, 255, 175), + (135, 255, 215), + (135, 255, 255), + (175, 0, 0), + (175, 0, 95), + (175, 0, 135), + (175, 0, 175), + (175, 0, 215), + (175, 0, 255), + (175, 95, 0), + (175, 95, 95), + (175, 95, 135), + (175, 95, 175), + (175, 95, 215), + (175, 95, 255), + (175, 135, 0), + (175, 135, 95), + (175, 135, 135), + (175, 135, 175), + (175, 135, 215), + (175, 135, 255), + (175, 175, 0), + (175, 175, 95), + (175, 175, 135), + (175, 175, 175), + (175, 175, 215), + (175, 175, 255), + (175, 215, 0), + (175, 215, 95), + (175, 215, 135), + (175, 215, 175), + (175, 215, 215), + (175, 215, 255), + (175, 255, 0), + (175, 255, 95), + (175, 255, 135), + (175, 255, 175), + (175, 255, 215), + (175, 255, 255), + (215, 0, 0), + (215, 0, 95), + (215, 0, 135), + (215, 0, 175), + (215, 0, 215), + (215, 0, 255), + (215, 95, 0), + (215, 95, 95), + (215, 95, 135), + (215, 95, 175), + (215, 95, 215), + (215, 95, 255), + (215, 135, 0), + (215, 135, 95), + (215, 135, 135), + (215, 135, 175), + (215, 135, 215), + (215, 135, 255), + (215, 175, 0), + (215, 175, 95), + (215, 175, 135), + (215, 175, 175), + (215, 175, 215), + (215, 175, 255), + (215, 215, 0), + (215, 215, 95), + (215, 215, 135), + (215, 215, 175), + (215, 215, 215), + (215, 215, 255), + (215, 255, 0), + (215, 255, 95), + (215, 255, 135), + (215, 255, 175), + (215, 255, 215), + (215, 255, 255), + (255, 0, 0), + (255, 0, 95), + (255, 0, 135), + (255, 0, 175), + (255, 0, 215), + (255, 0, 255), + (255, 95, 0), + (255, 95, 95), + (255, 95, 135), + (255, 95, 175), + (255, 95, 215), + (255, 95, 255), + (255, 135, 0), + (255, 135, 95), + (255, 135, 135), + (255, 135, 175), + (255, 135, 215), + (255, 135, 255), + (255, 175, 0), + (255, 175, 95), + (255, 175, 135), + (255, 175, 175), + (255, 175, 215), + (255, 175, 255), + (255, 215, 0), + (255, 215, 95), + (255, 215, 135), + (255, 215, 175), + (255, 215, 215), + (255, 215, 255), + (255, 255, 0), + (255, 255, 95), + (255, 255, 135), + (255, 255, 175), + (255, 255, 215), + (255, 255, 255), + (8, 8, 8), + (18, 18, 18), + (28, 28, 28), + (38, 38, 38), + (48, 48, 48), + (58, 58, 58), + (68, 68, 68), + (78, 78, 78), + (88, 88, 88), + (98, 98, 98), + (108, 108, 108), + (118, 118, 118), + (128, 128, 128), + (138, 138, 138), + (148, 148, 148), + (158, 158, 158), + (168, 168, 168), + (178, 178, 178), + (188, 188, 188), + (198, 198, 198), + (208, 208, 208), + (218, 218, 218), + (228, 228, 228), + (238, 238, 238), + ] +) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_pick.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_pick.py new file mode 100644 index 0000000..4f6d8b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_pick.py @@ -0,0 +1,17 @@ +from typing import Optional + + +def pick_bool(*values: Optional[bool]) -> bool: + """Pick the first non-none bool or return the last value. + + Args: + *values (bool): Any number of boolean or None values. + + Returns: + bool: First non-none boolean. + """ + assert values, "1 or more values required" + for value in values: + if value is not None: + return value + return bool(value) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_ratio.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_ratio.py new file mode 100644 index 0000000..5fd5a38 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_ratio.py @@ -0,0 +1,153 @@ +from fractions import Fraction +from math import ceil +from typing import cast, List, Optional, Sequence, Protocol + + +class Edge(Protocol): + """Any object that defines an edge (such as Layout).""" + + size: Optional[int] = None + ratio: int = 1 + minimum_size: int = 1 + + +def ratio_resolve(total: int, edges: Sequence[Edge]) -> List[int]: + """Divide total space to satisfy size, ratio, and minimum_size, constraints. + + The returned list of integers should add up to total in most cases, unless it is + impossible to satisfy all the constraints. For instance, if there are two edges + with a minimum size of 20 each and `total` is 30 then the returned list will be + greater than total. In practice, this would mean that a Layout object would + clip the rows that would overflow the screen height. + + Args: + total (int): Total number of characters. + edges (List[Edge]): Edges within total space. + + Returns: + List[int]: Number of characters for each edge. + """ + # Size of edge or None for yet to be determined + sizes = [(edge.size or None) for edge in edges] + + _Fraction = Fraction + + # While any edges haven't been calculated + while None in sizes: + # Get flexible edges and index to map these back on to sizes list + flexible_edges = [ + (index, edge) + for index, (size, edge) in enumerate(zip(sizes, edges)) + if size is None + ] + # Remaining space in total + remaining = total - sum(size or 0 for size in sizes) + if remaining <= 0: + # No room for flexible edges + return [ + ((edge.minimum_size or 1) if size is None else size) + for size, edge in zip(sizes, edges) + ] + # Calculate number of characters in a ratio portion + portion = _Fraction( + remaining, sum((edge.ratio or 1) for _, edge in flexible_edges) + ) + + # If any edges will be less than their minimum, replace size with the minimum + for index, edge in flexible_edges: + if portion * edge.ratio <= edge.minimum_size: + sizes[index] = edge.minimum_size + # New fixed size will invalidate calculations, so we need to repeat the process + break + else: + # Distribute flexible space and compensate for rounding error + # Since edge sizes can only be integers we need to add the remainder + # to the following line + remainder = _Fraction(0) + for index, edge in flexible_edges: + size, remainder = divmod(portion * edge.ratio + remainder, 1) + sizes[index] = size + break + # Sizes now contains integers only + return cast(List[int], sizes) + + +def ratio_reduce( + total: int, ratios: List[int], maximums: List[int], values: List[int] +) -> List[int]: + """Divide an integer total in to parts based on ratios. + + Args: + total (int): The total to divide. + ratios (List[int]): A list of integer ratios. + maximums (List[int]): List of maximums values for each slot. + values (List[int]): List of values + + Returns: + List[int]: A list of integers guaranteed to sum to total. + """ + ratios = [ratio if _max else 0 for ratio, _max in zip(ratios, maximums)] + total_ratio = sum(ratios) + if not total_ratio: + return values[:] + total_remaining = total + result: List[int] = [] + append = result.append + for ratio, maximum, value in zip(ratios, maximums, values): + if ratio and total_ratio > 0: + distributed = min(maximum, round(ratio * total_remaining / total_ratio)) + append(value - distributed) + total_remaining -= distributed + total_ratio -= ratio + else: + append(value) + return result + + +def ratio_distribute( + total: int, ratios: List[int], minimums: Optional[List[int]] = None +) -> List[int]: + """Distribute an integer total in to parts based on ratios. + + Args: + total (int): The total to divide. + ratios (List[int]): A list of integer ratios. + minimums (List[int]): List of minimum values for each slot. + + Returns: + List[int]: A list of integers guaranteed to sum to total. + """ + if minimums: + ratios = [ratio if _min else 0 for ratio, _min in zip(ratios, minimums)] + total_ratio = sum(ratios) + assert total_ratio > 0, "Sum of ratios must be > 0" + + total_remaining = total + distributed_total: List[int] = [] + append = distributed_total.append + if minimums is None: + _minimums = [0] * len(ratios) + else: + _minimums = minimums + for ratio, minimum in zip(ratios, _minimums): + if total_ratio > 0: + distributed = max(minimum, ceil(ratio * total_remaining / total_ratio)) + else: + distributed = total_remaining + append(distributed) + total_ratio -= ratio + total_remaining -= distributed + return distributed_total + + +if __name__ == "__main__": + from dataclasses import dataclass + + @dataclass + class E: + size: Optional[int] = None + ratio: int = 1 + minimum_size: int = 1 + + resolved = ratio_resolve(110, [E(None, 1, 1), E(None, 1, 1), E(None, 1, 1)]) + print(sum(resolved)) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_spinners.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_spinners.py new file mode 100644 index 0000000..d0bb1fe --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_spinners.py @@ -0,0 +1,482 @@ +""" +Spinners are from: +* cli-spinners: + MIT License + Copyright (c) Sindre Sorhus (sindresorhus.com) + 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. +""" + +SPINNERS = { + "dots": { + "interval": 80, + "frames": "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏", + }, + "dots2": {"interval": 80, "frames": "⣾⣽⣻⢿⡿⣟⣯⣷"}, + "dots3": { + "interval": 80, + "frames": "⠋⠙⠚⠞⠖⠦⠴⠲⠳⠓", + }, + "dots4": { + "interval": 80, + "frames": "⠄⠆⠇⠋⠙⠸⠰⠠⠰⠸⠙⠋⠇⠆", + }, + "dots5": { + "interval": 80, + "frames": "⠋⠙⠚⠒⠂⠂⠒⠲⠴⠦⠖⠒⠐⠐⠒⠓⠋", + }, + "dots6": { + "interval": 80, + "frames": "⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠴⠲⠒⠂⠂⠒⠚⠙⠉⠁", + }, + "dots7": { + "interval": 80, + "frames": "⠈⠉⠋⠓⠒⠐⠐⠒⠖⠦⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈", + }, + "dots8": { + "interval": 80, + "frames": "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈", + }, + "dots9": {"interval": 80, "frames": "⢹⢺⢼⣸⣇⡧⡗⡏"}, + "dots10": {"interval": 80, "frames": "⢄⢂⢁⡁⡈⡐⡠"}, + "dots11": {"interval": 100, "frames": "⠁⠂⠄⡀⢀⠠⠐⠈"}, + "dots12": { + "interval": 80, + "frames": [ + "⢀⠀", + "⡀⠀", + "⠄⠀", + "⢂⠀", + "⡂⠀", + "⠅⠀", + "⢃⠀", + "⡃⠀", + "⠍⠀", + "⢋⠀", + "⡋⠀", + "⠍⠁", + "⢋⠁", + "⡋⠁", + "⠍⠉", + "⠋⠉", + "⠋⠉", + "⠉⠙", + "⠉⠙", + "⠉⠩", + "⠈⢙", + "⠈⡙", + "⢈⠩", + "⡀⢙", + "⠄⡙", + "⢂⠩", + "⡂⢘", + "⠅⡘", + "⢃⠨", + "⡃⢐", + "⠍⡐", + "⢋⠠", + "⡋⢀", + "⠍⡁", + "⢋⠁", + "⡋⠁", + "⠍⠉", + "⠋⠉", + "⠋⠉", + "⠉⠙", + "⠉⠙", + "⠉⠩", + "⠈⢙", + "⠈⡙", + "⠈⠩", + "⠀⢙", + "⠀⡙", + "⠀⠩", + "⠀⢘", + "⠀⡘", + "⠀⠨", + "⠀⢐", + "⠀⡐", + "⠀⠠", + "⠀⢀", + "⠀⡀", + ], + }, + "dots8Bit": { + "interval": 80, + "frames": "⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙" + "⡚⡛⡜⡝⡞⡟⠠⠡⠢⠣⠤⠥⠦⠧⡠⡡⡢⡣⡤⡥⡦⡧⠨⠩⠪⠫⠬⠭⠮⠯⡨⡩⡪⡫⡬⡭⡮⡯⠰⠱⠲⠳⠴⠵⠶⠷⡰⡱⡲⡳⡴⡵⡶⡷⠸⠹⠺⠻" + "⠼⠽⠾⠿⡸⡹⡺⡻⡼⡽⡾⡿⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕" + "⣖⣗⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯⢰⢱⢲⢳⢴⢵⢶⢷" + "⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿", + }, + "line": {"interval": 130, "frames": ["-", "\\", "|", "/"]}, + "line2": {"interval": 100, "frames": "⠂-–—–-"}, + "pipe": {"interval": 100, "frames": "┤┘┴└├┌┬┐"}, + "simpleDots": {"interval": 400, "frames": [". ", ".. ", "...", " "]}, + "simpleDotsScrolling": { + "interval": 200, + "frames": [". ", ".. ", "...", " ..", " .", " "], + }, + "star": {"interval": 70, "frames": "✶✸✹✺✹✷"}, + "star2": {"interval": 80, "frames": "+x*"}, + "flip": { + "interval": 70, + "frames": "___-``'´-___", + }, + "hamburger": {"interval": 100, "frames": "☱☲☴"}, + "growVertical": { + "interval": 120, + "frames": "▁▃▄▅▆▇▆▅▄▃", + }, + "growHorizontal": { + "interval": 120, + "frames": "▏▎▍▌▋▊▉▊▋▌▍▎", + }, + "balloon": {"interval": 140, "frames": " .oO@* "}, + "balloon2": {"interval": 120, "frames": ".oO°Oo."}, + "noise": {"interval": 100, "frames": "▓▒░"}, + "bounce": {"interval": 120, "frames": "⠁⠂⠄⠂"}, + "boxBounce": {"interval": 120, "frames": "▖▘▝▗"}, + "boxBounce2": {"interval": 100, "frames": "▌▀▐▄"}, + "triangle": {"interval": 50, "frames": "◢◣◤◥"}, + "arc": {"interval": 100, "frames": "◜◠◝◞◡◟"}, + "circle": {"interval": 120, "frames": "◡⊙◠"}, + "squareCorners": {"interval": 180, "frames": "◰◳◲◱"}, + "circleQuarters": {"interval": 120, "frames": "◴◷◶◵"}, + "circleHalves": {"interval": 50, "frames": "◐◓◑◒"}, + "squish": {"interval": 100, "frames": "╫╪"}, + "toggle": {"interval": 250, "frames": "⊶⊷"}, + "toggle2": {"interval": 80, "frames": "▫▪"}, + "toggle3": {"interval": 120, "frames": "□■"}, + "toggle4": {"interval": 100, "frames": "■□▪▫"}, + "toggle5": {"interval": 100, "frames": "▮▯"}, + "toggle6": {"interval": 300, "frames": "ဝ၀"}, + "toggle7": {"interval": 80, "frames": "⦾⦿"}, + "toggle8": {"interval": 100, "frames": "◍◌"}, + "toggle9": {"interval": 100, "frames": "◉◎"}, + "toggle10": {"interval": 100, "frames": "㊂㊀㊁"}, + "toggle11": {"interval": 50, "frames": "⧇⧆"}, + "toggle12": {"interval": 120, "frames": "☗☖"}, + "toggle13": {"interval": 80, "frames": "=*-"}, + "arrow": {"interval": 100, "frames": "←↖↑↗→↘↓↙"}, + "arrow2": { + "interval": 80, + "frames": ["⬆️ ", "↗️ ", "➡️ ", "↘️ ", "⬇️ ", "↙️ ", "⬅️ ", "↖️ "], + }, + "arrow3": { + "interval": 120, + "frames": ["▹▹▹▹▹", "▸▹▹▹▹", "▹▸▹▹▹", "▹▹▸▹▹", "▹▹▹▸▹", "▹▹▹▹▸"], + }, + "bouncingBar": { + "interval": 80, + "frames": [ + "[ ]", + "[= ]", + "[== ]", + "[=== ]", + "[ ===]", + "[ ==]", + "[ =]", + "[ ]", + "[ =]", + "[ ==]", + "[ ===]", + "[====]", + "[=== ]", + "[== ]", + "[= ]", + ], + }, + "bouncingBall": { + "interval": 80, + "frames": [ + "( ● )", + "( ● )", + "( ● )", + "( ● )", + "( ●)", + "( ● )", + "( ● )", + "( ● )", + "( ● )", + "(● )", + ], + }, + "smiley": {"interval": 200, "frames": ["😄 ", "😝 "]}, + "monkey": {"interval": 300, "frames": ["🙈 ", "🙈 ", "🙉 ", "🙊 "]}, + "hearts": {"interval": 100, "frames": ["💛 ", "💙 ", "💜 ", "💚 ", "❤️ "]}, + "clock": { + "interval": 100, + "frames": [ + "🕛 ", + "🕐 ", + "🕑 ", + "🕒 ", + "🕓 ", + "🕔 ", + "🕕 ", + "🕖 ", + "🕗 ", + "🕘 ", + "🕙 ", + "🕚 ", + ], + }, + "earth": {"interval": 180, "frames": ["🌍 ", "🌎 ", "🌏 "]}, + "material": { + "interval": 17, + "frames": [ + "█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", + "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", + "███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", + "████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", + "██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁", + "██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁", + "███████▁▁▁▁▁▁▁▁▁▁▁▁▁", + "████████▁▁▁▁▁▁▁▁▁▁▁▁", + "█████████▁▁▁▁▁▁▁▁▁▁▁", + "█████████▁▁▁▁▁▁▁▁▁▁▁", + "██████████▁▁▁▁▁▁▁▁▁▁", + "███████████▁▁▁▁▁▁▁▁▁", + "█████████████▁▁▁▁▁▁▁", + "██████████████▁▁▁▁▁▁", + "██████████████▁▁▁▁▁▁", + "▁██████████████▁▁▁▁▁", + "▁██████████████▁▁▁▁▁", + "▁██████████████▁▁▁▁▁", + "▁▁██████████████▁▁▁▁", + "▁▁▁██████████████▁▁▁", + "▁▁▁▁█████████████▁▁▁", + "▁▁▁▁██████████████▁▁", + "▁▁▁▁██████████████▁▁", + "▁▁▁▁▁██████████████▁", + "▁▁▁▁▁██████████████▁", + "▁▁▁▁▁██████████████▁", + "▁▁▁▁▁▁██████████████", + "▁▁▁▁▁▁██████████████", + "▁▁▁▁▁▁▁█████████████", + "▁▁▁▁▁▁▁█████████████", + "▁▁▁▁▁▁▁▁████████████", + "▁▁▁▁▁▁▁▁████████████", + "▁▁▁▁▁▁▁▁▁███████████", + "▁▁▁▁▁▁▁▁▁███████████", + "▁▁▁▁▁▁▁▁▁▁██████████", + "▁▁▁▁▁▁▁▁▁▁██████████", + "▁▁▁▁▁▁▁▁▁▁▁▁████████", + "▁▁▁▁▁▁▁▁▁▁▁▁▁███████", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁██████", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████", + "█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████", + "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", + "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", + "███▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", + "████▁▁▁▁▁▁▁▁▁▁▁▁▁▁██", + "█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", + "█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", + "██████▁▁▁▁▁▁▁▁▁▁▁▁▁█", + "████████▁▁▁▁▁▁▁▁▁▁▁▁", + "█████████▁▁▁▁▁▁▁▁▁▁▁", + "█████████▁▁▁▁▁▁▁▁▁▁▁", + "█████████▁▁▁▁▁▁▁▁▁▁▁", + "█████████▁▁▁▁▁▁▁▁▁▁▁", + "███████████▁▁▁▁▁▁▁▁▁", + "████████████▁▁▁▁▁▁▁▁", + "████████████▁▁▁▁▁▁▁▁", + "██████████████▁▁▁▁▁▁", + "██████████████▁▁▁▁▁▁", + "▁██████████████▁▁▁▁▁", + "▁██████████████▁▁▁▁▁", + "▁▁▁█████████████▁▁▁▁", + "▁▁▁▁▁████████████▁▁▁", + "▁▁▁▁▁████████████▁▁▁", + "▁▁▁▁▁▁███████████▁▁▁", + "▁▁▁▁▁▁▁▁█████████▁▁▁", + "▁▁▁▁▁▁▁▁█████████▁▁▁", + "▁▁▁▁▁▁▁▁▁█████████▁▁", + "▁▁▁▁▁▁▁▁▁█████████▁▁", + "▁▁▁▁▁▁▁▁▁▁█████████▁", + "▁▁▁▁▁▁▁▁▁▁▁████████▁", + "▁▁▁▁▁▁▁▁▁▁▁████████▁", + "▁▁▁▁▁▁▁▁▁▁▁▁███████▁", + "▁▁▁▁▁▁▁▁▁▁▁▁███████▁", + "▁▁▁▁▁▁▁▁▁▁▁▁▁███████", + "▁▁▁▁▁▁▁▁▁▁▁▁▁███████", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", + "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", + ], + }, + "moon": { + "interval": 80, + "frames": ["🌑 ", "🌒 ", "🌓 ", "🌔 ", "🌕 ", "🌖 ", "🌗 ", "🌘 "], + }, + "runner": {"interval": 140, "frames": ["🚶 ", "🏃 "]}, + "pong": { + "interval": 80, + "frames": [ + "▐⠂ ▌", + "▐⠈ ▌", + "▐ ⠂ ▌", + "▐ ⠠ ▌", + "▐ ⡀ ▌", + "▐ ⠠ ▌", + "▐ ⠂ ▌", + "▐ ⠈ ▌", + "▐ ⠂ ▌", + "▐ ⠠ ▌", + "▐ ⡀ ▌", + "▐ ⠠ ▌", + "▐ ⠂ ▌", + "▐ ⠈ ▌", + "▐ ⠂▌", + "▐ ⠠▌", + "▐ ⡀▌", + "▐ ⠠ ▌", + "▐ ⠂ ▌", + "▐ ⠈ ▌", + "▐ ⠂ ▌", + "▐ ⠠ ▌", + "▐ ⡀ ▌", + "▐ ⠠ ▌", + "▐ ⠂ ▌", + "▐ ⠈ ▌", + "▐ ⠂ ▌", + "▐ ⠠ ▌", + "▐ ⡀ ▌", + "▐⠠ ▌", + ], + }, + "shark": { + "interval": 120, + "frames": [ + "▐|\\____________▌", + "▐_|\\___________▌", + "▐__|\\__________▌", + "▐___|\\_________▌", + "▐____|\\________▌", + "▐_____|\\_______▌", + "▐______|\\______▌", + "▐_______|\\_____▌", + "▐________|\\____▌", + "▐_________|\\___▌", + "▐__________|\\__▌", + "▐___________|\\_▌", + "▐____________|\\▌", + "▐____________/|▌", + "▐___________/|_▌", + "▐__________/|__▌", + "▐_________/|___▌", + "▐________/|____▌", + "▐_______/|_____▌", + "▐______/|______▌", + "▐_____/|_______▌", + "▐____/|________▌", + "▐___/|_________▌", + "▐__/|__________▌", + "▐_/|___________▌", + "▐/|____________▌", + ], + }, + "dqpb": {"interval": 100, "frames": "dqpb"}, + "weather": { + "interval": 100, + "frames": [ + "☀️ ", + "☀️ ", + "☀️ ", + "🌤 ", + "⛅️ ", + "🌥 ", + "☁️ ", + "🌧 ", + "🌨 ", + "🌧 ", + "🌨 ", + "🌧 ", + "🌨 ", + "⛈ ", + "🌨 ", + "🌧 ", + "🌨 ", + "☁️ ", + "🌥 ", + "⛅️ ", + "🌤 ", + "☀️ ", + "☀️ ", + ], + }, + "christmas": {"interval": 400, "frames": "🌲🎄"}, + "grenade": { + "interval": 80, + "frames": [ + "، ", + "′ ", + " ´ ", + " ‾ ", + " ⸌", + " ⸊", + " |", + " ⁎", + " ⁕", + " ෴ ", + " ⁓", + " ", + " ", + " ", + ], + }, + "point": {"interval": 125, "frames": ["∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"]}, + "layer": {"interval": 150, "frames": "-=≡"}, + "betaWave": { + "interval": 80, + "frames": [ + "ρββββββ", + "βρβββββ", + "ββρββββ", + "βββρβββ", + "ββββρββ", + "βββββρβ", + "ββββββρ", + ], + }, + "aesthetic": { + "interval": 80, + "frames": [ + "▰▱▱▱▱▱▱", + "▰▰▱▱▱▱▱", + "▰▰▰▱▱▱▱", + "▰▰▰▰▱▱▱", + "▰▰▰▰▰▱▱", + "▰▰▰▰▰▰▱", + "▰▰▰▰▰▰▰", + "▰▱▱▱▱▱▱", + ], + }, +} diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_stack.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_stack.py new file mode 100644 index 0000000..194564e --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_stack.py @@ -0,0 +1,16 @@ +from typing import List, TypeVar + +T = TypeVar("T") + + +class Stack(List[T]): + """A small shim over builtin list.""" + + @property + def top(self) -> T: + """Get top of stack.""" + return self[-1] + + def push(self, item: T) -> None: + """Push an item on to the stack (append in stack nomenclature).""" + self.append(item) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_timer.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_timer.py new file mode 100644 index 0000000..a2ca6be --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_timer.py @@ -0,0 +1,19 @@ +""" +Timer context manager, only used in debug. + +""" + +from time import time + +import contextlib +from typing import Generator + + +@contextlib.contextmanager +def timer(subject: str = "time") -> Generator[None, None, None]: + """print the elapsed time. (only used in debugging)""" + start = time() + yield + elapsed = time() - start + elapsed_ms = elapsed * 1000 + print(f"{subject} elapsed {elapsed_ms:.1f}ms") diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_win32_console.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_win32_console.py new file mode 100644 index 0000000..2eba1b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_win32_console.py @@ -0,0 +1,661 @@ +"""Light wrapper around the Win32 Console API - this module should only be imported on Windows + +The API that this module wraps is documented at https://docs.microsoft.com/en-us/windows/console/console-functions +""" + +import ctypes +import sys +from typing import Any + +windll: Any = None +if sys.platform == "win32": + windll = ctypes.LibraryLoader(ctypes.WinDLL) +else: + raise ImportError(f"{__name__} can only be imported on Windows") + +import time +from ctypes import Structure, byref, wintypes +from typing import IO, NamedTuple, Type, cast + +from pip._vendor.rich.color import ColorSystem +from pip._vendor.rich.style import Style + +STDOUT = -11 +ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4 + +COORD = wintypes._COORD + + +class LegacyWindowsError(Exception): + pass + + +class WindowsCoordinates(NamedTuple): + """Coordinates in the Windows Console API are (y, x), not (x, y). + This class is intended to prevent that confusion. + Rows and columns are indexed from 0. + This class can be used in place of wintypes._COORD in arguments and argtypes. + """ + + row: int + col: int + + @classmethod + def from_param(cls, value: "WindowsCoordinates") -> COORD: + """Converts a WindowsCoordinates into a wintypes _COORD structure. + This classmethod is internally called by ctypes to perform the conversion. + + Args: + value (WindowsCoordinates): The input coordinates to convert. + + Returns: + wintypes._COORD: The converted coordinates struct. + """ + return COORD(value.col, value.row) + + +class CONSOLE_SCREEN_BUFFER_INFO(Structure): + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", wintypes.WORD), + ("srWindow", wintypes.SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + + +class CONSOLE_CURSOR_INFO(ctypes.Structure): + _fields_ = [("dwSize", wintypes.DWORD), ("bVisible", wintypes.BOOL)] + + +_GetStdHandle = windll.kernel32.GetStdHandle +_GetStdHandle.argtypes = [ + wintypes.DWORD, +] +_GetStdHandle.restype = wintypes.HANDLE + + +def GetStdHandle(handle: int = STDOUT) -> wintypes.HANDLE: + """Retrieves a handle to the specified standard device (standard input, standard output, or standard error). + + Args: + handle (int): Integer identifier for the handle. Defaults to -11 (stdout). + + Returns: + wintypes.HANDLE: The handle + """ + return cast(wintypes.HANDLE, _GetStdHandle(handle)) + + +_GetConsoleMode = windll.kernel32.GetConsoleMode +_GetConsoleMode.argtypes = [wintypes.HANDLE, wintypes.LPDWORD] +_GetConsoleMode.restype = wintypes.BOOL + + +def GetConsoleMode(std_handle: wintypes.HANDLE) -> int: + """Retrieves the current input mode of a console's input buffer + or the current output mode of a console screen buffer. + + Args: + std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. + + Raises: + LegacyWindowsError: If any error occurs while calling the Windows console API. + + Returns: + int: Value representing the current console mode as documented at + https://docs.microsoft.com/en-us/windows/console/getconsolemode#parameters + """ + + console_mode = wintypes.DWORD() + success = bool(_GetConsoleMode(std_handle, console_mode)) + if not success: + raise LegacyWindowsError("Unable to get legacy Windows Console Mode") + return console_mode.value + + +_FillConsoleOutputCharacterW = windll.kernel32.FillConsoleOutputCharacterW +_FillConsoleOutputCharacterW.argtypes = [ + wintypes.HANDLE, + ctypes.c_char, + wintypes.DWORD, + cast(Type[COORD], WindowsCoordinates), + ctypes.POINTER(wintypes.DWORD), +] +_FillConsoleOutputCharacterW.restype = wintypes.BOOL + + +def FillConsoleOutputCharacter( + std_handle: wintypes.HANDLE, + char: str, + length: int, + start: WindowsCoordinates, +) -> int: + """Writes a character to the console screen buffer a specified number of times, beginning at the specified coordinates. + + Args: + std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. + char (str): The character to write. Must be a string of length 1. + length (int): The number of times to write the character. + start (WindowsCoordinates): The coordinates to start writing at. + + Returns: + int: The number of characters written. + """ + character = ctypes.c_char(char.encode()) + num_characters = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + _FillConsoleOutputCharacterW( + std_handle, + character, + num_characters, + start, + byref(num_written), + ) + return num_written.value + + +_FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute +_FillConsoleOutputAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + wintypes.DWORD, + cast(Type[COORD], WindowsCoordinates), + ctypes.POINTER(wintypes.DWORD), +] +_FillConsoleOutputAttribute.restype = wintypes.BOOL + + +def FillConsoleOutputAttribute( + std_handle: wintypes.HANDLE, + attributes: int, + length: int, + start: WindowsCoordinates, +) -> int: + """Sets the character attributes for a specified number of character cells, + beginning at the specified coordinates in a screen buffer. + + Args: + std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. + attributes (int): Integer value representing the foreground and background colours of the cells. + length (int): The number of cells to set the output attribute of. + start (WindowsCoordinates): The coordinates of the first cell whose attributes are to be set. + + Returns: + int: The number of cells whose attributes were actually set. + """ + num_cells = wintypes.DWORD(length) + style_attrs = wintypes.WORD(attributes) + num_written = wintypes.DWORD(0) + _FillConsoleOutputAttribute( + std_handle, style_attrs, num_cells, start, byref(num_written) + ) + return num_written.value + + +_SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute +_SetConsoleTextAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, +] +_SetConsoleTextAttribute.restype = wintypes.BOOL + + +def SetConsoleTextAttribute( + std_handle: wintypes.HANDLE, attributes: wintypes.WORD +) -> bool: + """Set the colour attributes for all text written after this function is called. + + Args: + std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. + attributes (int): Integer value representing the foreground and background colours. + + + Returns: + bool: True if the attribute was set successfully, otherwise False. + """ + return bool(_SetConsoleTextAttribute(std_handle, attributes)) + + +_GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo +_GetConsoleScreenBufferInfo.argtypes = [ + wintypes.HANDLE, + ctypes.POINTER(CONSOLE_SCREEN_BUFFER_INFO), +] +_GetConsoleScreenBufferInfo.restype = wintypes.BOOL + + +def GetConsoleScreenBufferInfo( + std_handle: wintypes.HANDLE, +) -> CONSOLE_SCREEN_BUFFER_INFO: + """Retrieves information about the specified console screen buffer. + + Args: + std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. + + Returns: + CONSOLE_SCREEN_BUFFER_INFO: A CONSOLE_SCREEN_BUFFER_INFO ctype struct contain information about + screen size, cursor position, colour attributes, and more.""" + console_screen_buffer_info = CONSOLE_SCREEN_BUFFER_INFO() + _GetConsoleScreenBufferInfo(std_handle, byref(console_screen_buffer_info)) + return console_screen_buffer_info + + +_SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition +_SetConsoleCursorPosition.argtypes = [ + wintypes.HANDLE, + cast(Type[COORD], WindowsCoordinates), +] +_SetConsoleCursorPosition.restype = wintypes.BOOL + + +def SetConsoleCursorPosition( + std_handle: wintypes.HANDLE, coords: WindowsCoordinates +) -> bool: + """Set the position of the cursor in the console screen + + Args: + std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. + coords (WindowsCoordinates): The coordinates to move the cursor to. + + Returns: + bool: True if the function succeeds, otherwise False. + """ + return bool(_SetConsoleCursorPosition(std_handle, coords)) + + +_GetConsoleCursorInfo = windll.kernel32.GetConsoleCursorInfo +_GetConsoleCursorInfo.argtypes = [ + wintypes.HANDLE, + ctypes.POINTER(CONSOLE_CURSOR_INFO), +] +_GetConsoleCursorInfo.restype = wintypes.BOOL + + +def GetConsoleCursorInfo( + std_handle: wintypes.HANDLE, cursor_info: CONSOLE_CURSOR_INFO +) -> bool: + """Get the cursor info - used to get cursor visibility and width + + Args: + std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. + cursor_info (CONSOLE_CURSOR_INFO): CONSOLE_CURSOR_INFO ctype struct that receives information + about the console's cursor. + + Returns: + bool: True if the function succeeds, otherwise False. + """ + return bool(_GetConsoleCursorInfo(std_handle, byref(cursor_info))) + + +_SetConsoleCursorInfo = windll.kernel32.SetConsoleCursorInfo +_SetConsoleCursorInfo.argtypes = [ + wintypes.HANDLE, + ctypes.POINTER(CONSOLE_CURSOR_INFO), +] +_SetConsoleCursorInfo.restype = wintypes.BOOL + + +def SetConsoleCursorInfo( + std_handle: wintypes.HANDLE, cursor_info: CONSOLE_CURSOR_INFO +) -> bool: + """Set the cursor info - used for adjusting cursor visibility and width + + Args: + std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. + cursor_info (CONSOLE_CURSOR_INFO): CONSOLE_CURSOR_INFO ctype struct containing the new cursor info. + + Returns: + bool: True if the function succeeds, otherwise False. + """ + return bool(_SetConsoleCursorInfo(std_handle, byref(cursor_info))) + + +_SetConsoleTitle = windll.kernel32.SetConsoleTitleW +_SetConsoleTitle.argtypes = [wintypes.LPCWSTR] +_SetConsoleTitle.restype = wintypes.BOOL + + +def SetConsoleTitle(title: str) -> bool: + """Sets the title of the current console window + + Args: + title (str): The new title of the console window. + + Returns: + bool: True if the function succeeds, otherwise False. + """ + return bool(_SetConsoleTitle(title)) + + +class LegacyWindowsTerm: + """This class allows interaction with the legacy Windows Console API. It should only be used in the context + of environments where virtual terminal processing is not available. However, if it is used in a Windows environment, + the entire API should work. + + Args: + file (IO[str]): The file which the Windows Console API HANDLE is retrieved from, defaults to sys.stdout. + """ + + BRIGHT_BIT = 8 + + # Indices are ANSI color numbers, values are the corresponding Windows Console API color numbers + ANSI_TO_WINDOWS = [ + 0, # black The Windows colours are defined in wincon.h as follows: + 4, # red define FOREGROUND_BLUE 0x0001 -- 0000 0001 + 2, # green define FOREGROUND_GREEN 0x0002 -- 0000 0010 + 6, # yellow define FOREGROUND_RED 0x0004 -- 0000 0100 + 1, # blue define FOREGROUND_INTENSITY 0x0008 -- 0000 1000 + 5, # magenta define BACKGROUND_BLUE 0x0010 -- 0001 0000 + 3, # cyan define BACKGROUND_GREEN 0x0020 -- 0010 0000 + 7, # white define BACKGROUND_RED 0x0040 -- 0100 0000 + 8, # bright black (grey) define BACKGROUND_INTENSITY 0x0080 -- 1000 0000 + 12, # bright red + 10, # bright green + 14, # bright yellow + 9, # bright blue + 13, # bright magenta + 11, # bright cyan + 15, # bright white + ] + + def __init__(self, file: "IO[str]") -> None: + handle = GetStdHandle(STDOUT) + self._handle = handle + default_text = GetConsoleScreenBufferInfo(handle).wAttributes + self._default_text = default_text + + self._default_fore = default_text & 7 + self._default_back = (default_text >> 4) & 7 + self._default_attrs = self._default_fore | (self._default_back << 4) + + self._file = file + self.write = file.write + self.flush = file.flush + + @property + def cursor_position(self) -> WindowsCoordinates: + """Returns the current position of the cursor (0-based) + + Returns: + WindowsCoordinates: The current cursor position. + """ + coord: COORD = GetConsoleScreenBufferInfo(self._handle).dwCursorPosition + return WindowsCoordinates(row=coord.Y, col=coord.X) + + @property + def screen_size(self) -> WindowsCoordinates: + """Returns the current size of the console screen buffer, in character columns and rows + + Returns: + WindowsCoordinates: The width and height of the screen as WindowsCoordinates. + """ + screen_size: COORD = GetConsoleScreenBufferInfo(self._handle).dwSize + return WindowsCoordinates(row=screen_size.Y, col=screen_size.X) + + def write_text(self, text: str) -> None: + """Write text directly to the terminal without any modification of styles + + Args: + text (str): The text to write to the console + """ + self.write(text) + self.flush() + + def write_styled(self, text: str, style: Style) -> None: + """Write styled text to the terminal. + + Args: + text (str): The text to write + style (Style): The style of the text + """ + color = style.color + bgcolor = style.bgcolor + if style.reverse: + color, bgcolor = bgcolor, color + + if color: + fore = color.downgrade(ColorSystem.WINDOWS).number + fore = fore if fore is not None else 7 # Default to ANSI 7: White + if style.bold: + fore = fore | self.BRIGHT_BIT + if style.dim: + fore = fore & ~self.BRIGHT_BIT + fore = self.ANSI_TO_WINDOWS[fore] + else: + fore = self._default_fore + + if bgcolor: + back = bgcolor.downgrade(ColorSystem.WINDOWS).number + back = back if back is not None else 0 # Default to ANSI 0: Black + back = self.ANSI_TO_WINDOWS[back] + else: + back = self._default_back + + assert fore is not None + assert back is not None + + SetConsoleTextAttribute( + self._handle, attributes=ctypes.c_ushort(fore | (back << 4)) + ) + self.write_text(text) + SetConsoleTextAttribute(self._handle, attributes=self._default_text) + + def move_cursor_to(self, new_position: WindowsCoordinates) -> None: + """Set the position of the cursor + + Args: + new_position (WindowsCoordinates): The WindowsCoordinates representing the new position of the cursor. + """ + if new_position.col < 0 or new_position.row < 0: + return + SetConsoleCursorPosition(self._handle, coords=new_position) + + def erase_line(self) -> None: + """Erase all content on the line the cursor is currently located at""" + screen_size = self.screen_size + cursor_position = self.cursor_position + cells_to_erase = screen_size.col + start_coordinates = WindowsCoordinates(row=cursor_position.row, col=0) + FillConsoleOutputCharacter( + self._handle, " ", length=cells_to_erase, start=start_coordinates + ) + FillConsoleOutputAttribute( + self._handle, + self._default_attrs, + length=cells_to_erase, + start=start_coordinates, + ) + + def erase_end_of_line(self) -> None: + """Erase all content from the cursor position to the end of that line""" + cursor_position = self.cursor_position + cells_to_erase = self.screen_size.col - cursor_position.col + FillConsoleOutputCharacter( + self._handle, " ", length=cells_to_erase, start=cursor_position + ) + FillConsoleOutputAttribute( + self._handle, + self._default_attrs, + length=cells_to_erase, + start=cursor_position, + ) + + def erase_start_of_line(self) -> None: + """Erase all content from the cursor position to the start of that line""" + row, col = self.cursor_position + start = WindowsCoordinates(row, 0) + FillConsoleOutputCharacter(self._handle, " ", length=col, start=start) + FillConsoleOutputAttribute( + self._handle, self._default_attrs, length=col, start=start + ) + + def move_cursor_up(self) -> None: + """Move the cursor up a single cell""" + cursor_position = self.cursor_position + SetConsoleCursorPosition( + self._handle, + coords=WindowsCoordinates( + row=cursor_position.row - 1, col=cursor_position.col + ), + ) + + def move_cursor_down(self) -> None: + """Move the cursor down a single cell""" + cursor_position = self.cursor_position + SetConsoleCursorPosition( + self._handle, + coords=WindowsCoordinates( + row=cursor_position.row + 1, + col=cursor_position.col, + ), + ) + + def move_cursor_forward(self) -> None: + """Move the cursor forward a single cell. Wrap to the next line if required.""" + row, col = self.cursor_position + if col == self.screen_size.col - 1: + row += 1 + col = 0 + else: + col += 1 + SetConsoleCursorPosition( + self._handle, coords=WindowsCoordinates(row=row, col=col) + ) + + def move_cursor_to_column(self, column: int) -> None: + """Move cursor to the column specified by the zero-based column index, staying on the same row + + Args: + column (int): The zero-based column index to move the cursor to. + """ + row, _ = self.cursor_position + SetConsoleCursorPosition(self._handle, coords=WindowsCoordinates(row, column)) + + def move_cursor_backward(self) -> None: + """Move the cursor backward a single cell. Wrap to the previous line if required.""" + row, col = self.cursor_position + if col == 0: + row -= 1 + col = self.screen_size.col - 1 + else: + col -= 1 + SetConsoleCursorPosition( + self._handle, coords=WindowsCoordinates(row=row, col=col) + ) + + def hide_cursor(self) -> None: + """Hide the cursor""" + current_cursor_size = self._get_cursor_size() + invisible_cursor = CONSOLE_CURSOR_INFO(dwSize=current_cursor_size, bVisible=0) + SetConsoleCursorInfo(self._handle, cursor_info=invisible_cursor) + + def show_cursor(self) -> None: + """Show the cursor""" + current_cursor_size = self._get_cursor_size() + visible_cursor = CONSOLE_CURSOR_INFO(dwSize=current_cursor_size, bVisible=1) + SetConsoleCursorInfo(self._handle, cursor_info=visible_cursor) + + def set_title(self, title: str) -> None: + """Set the title of the terminal window + + Args: + title (str): The new title of the console window + """ + assert len(title) < 255, "Console title must be less than 255 characters" + SetConsoleTitle(title) + + def _get_cursor_size(self) -> int: + """Get the percentage of the character cell that is filled by the cursor""" + cursor_info = CONSOLE_CURSOR_INFO() + GetConsoleCursorInfo(self._handle, cursor_info=cursor_info) + return int(cursor_info.dwSize) + + +if __name__ == "__main__": + handle = GetStdHandle() + + from pip._vendor.rich.console import Console + + console = Console() + + term = LegacyWindowsTerm(sys.stdout) + term.set_title("Win32 Console Examples") + + style = Style(color="black", bgcolor="red") + + heading = Style.parse("black on green") + + # Check colour output + console.rule("Checking colour output") + console.print("[on red]on red!") + console.print("[blue]blue!") + console.print("[yellow]yellow!") + console.print("[bold yellow]bold yellow!") + console.print("[bright_yellow]bright_yellow!") + console.print("[dim bright_yellow]dim bright_yellow!") + console.print("[italic cyan]italic cyan!") + console.print("[bold white on blue]bold white on blue!") + console.print("[reverse bold white on blue]reverse bold white on blue!") + console.print("[bold black on cyan]bold black on cyan!") + console.print("[black on green]black on green!") + console.print("[blue on green]blue on green!") + console.print("[white on black]white on black!") + console.print("[black on white]black on white!") + console.print("[#1BB152 on #DA812D]#1BB152 on #DA812D!") + + # Check cursor movement + console.rule("Checking cursor movement") + console.print() + term.move_cursor_backward() + term.move_cursor_backward() + term.write_text("went back and wrapped to prev line") + time.sleep(1) + term.move_cursor_up() + term.write_text("we go up") + time.sleep(1) + term.move_cursor_down() + term.write_text("and down") + time.sleep(1) + term.move_cursor_up() + term.move_cursor_backward() + term.move_cursor_backward() + term.write_text("we went up and back 2") + time.sleep(1) + term.move_cursor_down() + term.move_cursor_backward() + term.move_cursor_backward() + term.write_text("we went down and back 2") + time.sleep(1) + + # Check erasing of lines + term.hide_cursor() + console.print() + console.rule("Checking line erasing") + console.print("\n...Deleting to the start of the line...") + term.write_text("The red arrow shows the cursor location, and direction of erase") + time.sleep(1) + term.move_cursor_to_column(16) + term.write_styled("<", Style.parse("black on red")) + term.move_cursor_backward() + time.sleep(1) + term.erase_start_of_line() + time.sleep(1) + + console.print("\n\n...And to the end of the line...") + term.write_text("The red arrow shows the cursor location, and direction of erase") + time.sleep(1) + + term.move_cursor_to_column(16) + term.write_styled(">", Style.parse("black on red")) + time.sleep(1) + term.erase_end_of_line() + time.sleep(1) + + console.print("\n\n...Now the whole line will be erased...") + term.write_styled("I'm going to disappear!", style=Style.parse("black on cyan")) + time.sleep(1) + term.erase_line() + + term.show_cursor() + print("\n") diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows.py new file mode 100644 index 0000000..7520a9f --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows.py @@ -0,0 +1,71 @@ +import sys +from dataclasses import dataclass + + +@dataclass +class WindowsConsoleFeatures: + """Windows features available.""" + + vt: bool = False + """The console supports VT codes.""" + truecolor: bool = False + """The console supports truecolor.""" + + +try: + import ctypes + from ctypes import LibraryLoader + + if sys.platform == "win32": + windll = LibraryLoader(ctypes.WinDLL) + else: + windll = None + raise ImportError("Not windows") + + from pip._vendor.rich._win32_console import ( + ENABLE_VIRTUAL_TERMINAL_PROCESSING, + GetConsoleMode, + GetStdHandle, + LegacyWindowsError, + ) + +except (AttributeError, ImportError, ValueError): + # Fallback if we can't load the Windows DLL + def get_windows_console_features() -> WindowsConsoleFeatures: + features = WindowsConsoleFeatures() + return features + +else: + + def get_windows_console_features() -> WindowsConsoleFeatures: + """Get windows console features. + + Returns: + WindowsConsoleFeatures: An instance of WindowsConsoleFeatures. + """ + handle = GetStdHandle() + try: + console_mode = GetConsoleMode(handle) + success = True + except LegacyWindowsError: + console_mode = 0 + success = False + vt = bool(success and console_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) + truecolor = False + if vt: + win_version = sys.getwindowsversion() + truecolor = win_version.major > 10 or ( + win_version.major == 10 and win_version.build >= 15063 + ) + features = WindowsConsoleFeatures(vt=vt, truecolor=truecolor) + return features + + +if __name__ == "__main__": + import platform + + features = get_windows_console_features() + from pip._vendor.rich import print + + print(f'platform="{platform.system()}"') + print(repr(features)) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows_renderer.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows_renderer.py new file mode 100644 index 0000000..5ece056 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows_renderer.py @@ -0,0 +1,56 @@ +from typing import Iterable, Sequence, Tuple, cast + +from pip._vendor.rich._win32_console import LegacyWindowsTerm, WindowsCoordinates +from pip._vendor.rich.segment import ControlCode, ControlType, Segment + + +def legacy_windows_render(buffer: Iterable[Segment], term: LegacyWindowsTerm) -> None: + """Makes appropriate Windows Console API calls based on the segments in the buffer. + + Args: + buffer (Iterable[Segment]): Iterable of Segments to convert to Win32 API calls. + term (LegacyWindowsTerm): Used to call the Windows Console API. + """ + for text, style, control in buffer: + if not control: + if style: + term.write_styled(text, style) + else: + term.write_text(text) + else: + control_codes: Sequence[ControlCode] = control + for control_code in control_codes: + control_type = control_code[0] + if control_type == ControlType.CURSOR_MOVE_TO: + _, x, y = cast(Tuple[ControlType, int, int], control_code) + term.move_cursor_to(WindowsCoordinates(row=y - 1, col=x - 1)) + elif control_type == ControlType.CARRIAGE_RETURN: + term.write_text("\r") + elif control_type == ControlType.HOME: + term.move_cursor_to(WindowsCoordinates(0, 0)) + elif control_type == ControlType.CURSOR_UP: + term.move_cursor_up() + elif control_type == ControlType.CURSOR_DOWN: + term.move_cursor_down() + elif control_type == ControlType.CURSOR_FORWARD: + term.move_cursor_forward() + elif control_type == ControlType.CURSOR_BACKWARD: + term.move_cursor_backward() + elif control_type == ControlType.CURSOR_MOVE_TO_COLUMN: + _, column = cast(Tuple[ControlType, int], control_code) + term.move_cursor_to_column(column - 1) + elif control_type == ControlType.HIDE_CURSOR: + term.hide_cursor() + elif control_type == ControlType.SHOW_CURSOR: + term.show_cursor() + elif control_type == ControlType.ERASE_IN_LINE: + _, mode = cast(Tuple[ControlType, int], control_code) + if mode == 0: + term.erase_end_of_line() + elif mode == 1: + term.erase_start_of_line() + elif mode == 2: + term.erase_line() + elif control_type == ControlType.SET_WINDOW_TITLE: + _, title = cast(Tuple[ControlType, str], control_code) + term.set_title(title) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_wrap.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_wrap.py new file mode 100644 index 0000000..2e94ff6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_wrap.py @@ -0,0 +1,93 @@ +from __future__ import annotations + +import re +from typing import Iterable + +from ._loop import loop_last +from .cells import cell_len, chop_cells + +re_word = re.compile(r"\s*\S+\s*") + + +def words(text: str) -> Iterable[tuple[int, int, str]]: + """Yields each word from the text as a tuple + containing (start_index, end_index, word). A "word" in this context may + include the actual word and any whitespace to the right. + """ + position = 0 + word_match = re_word.match(text, position) + while word_match is not None: + start, end = word_match.span() + word = word_match.group(0) + yield start, end, word + word_match = re_word.match(text, end) + + +def divide_line(text: str, width: int, fold: bool = True) -> list[int]: + """Given a string of text, and a width (measured in cells), return a list + of cell offsets which the string should be split at in order for it to fit + within the given width. + + Args: + text: The text to examine. + width: The available cell width. + fold: If True, words longer than `width` will be folded onto a new line. + + Returns: + A list of indices to break the line at. + """ + break_positions: list[int] = [] # offsets to insert the breaks at + append = break_positions.append + cell_offset = 0 + _cell_len = cell_len + + for start, _end, word in words(text): + word_length = _cell_len(word.rstrip()) + remaining_space = width - cell_offset + word_fits_remaining_space = remaining_space >= word_length + + if word_fits_remaining_space: + # Simplest case - the word fits within the remaining width for this line. + cell_offset += _cell_len(word) + else: + # Not enough space remaining for this word on the current line. + if word_length > width: + # The word doesn't fit on any line, so we can't simply + # place it on the next line... + if fold: + # Fold the word across multiple lines. + folded_word = chop_cells(word, width=width) + for last, line in loop_last(folded_word): + if start: + append(start) + if last: + cell_offset = _cell_len(line) + else: + start += len(line) + else: + # Folding isn't allowed, so crop the word. + if start: + append(start) + cell_offset = _cell_len(word) + elif cell_offset and start: + # The word doesn't fit within the remaining space on the current + # line, but it *can* fit on to the next (empty) line. + append(start) + cell_offset = _cell_len(word) + + return break_positions + + +if __name__ == "__main__": # pragma: no cover + from .console import Console + + console = Console(width=10) + console.print("12345 abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ 12345") + print(chop_cells("abcdefghijklmnopqrstuvwxyz", 10)) + + console = Console(width=20) + console.rule() + console.print("TextualはPythonの高速アプリケーション開発フレームワークです") + + console.rule() + console.print("アプリケーションは1670万色を使用でき") diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/abc.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/abc.py new file mode 100644 index 0000000..e6e498e --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/abc.py @@ -0,0 +1,33 @@ +from abc import ABC + + +class RichRenderable(ABC): + """An abstract base class for Rich renderables. + + Note that there is no need to extend this class, the intended use is to check if an + object supports the Rich renderable protocol. For example:: + + if isinstance(my_object, RichRenderable): + console.print(my_object) + + """ + + @classmethod + def __subclasshook__(cls, other: type) -> bool: + """Check if this class supports the rich render protocol.""" + return hasattr(other, "__rich_console__") or hasattr(other, "__rich__") + + +if __name__ == "__main__": # pragma: no cover + from pip._vendor.rich.text import Text + + t = Text() + print(isinstance(Text, RichRenderable)) + print(isinstance(t, RichRenderable)) + + class Foo: + pass + + f = Foo() + print(isinstance(f, RichRenderable)) + print(isinstance("", RichRenderable)) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/align.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/align.py new file mode 100644 index 0000000..e65dc5b --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/align.py @@ -0,0 +1,306 @@ +from itertools import chain +from typing import TYPE_CHECKING, Iterable, Optional, Literal + +from .constrain import Constrain +from .jupyter import JupyterMixin +from .measure import Measurement +from .segment import Segment +from .style import StyleType + +if TYPE_CHECKING: + from .console import Console, ConsoleOptions, RenderableType, RenderResult + +AlignMethod = Literal["left", "center", "right"] +VerticalAlignMethod = Literal["top", "middle", "bottom"] + + +class Align(JupyterMixin): + """Align a renderable by adding spaces if necessary. + + Args: + renderable (RenderableType): A console renderable. + align (AlignMethod): One of "left", "center", or "right"" + style (StyleType, optional): An optional style to apply to the background. + vertical (Optional[VerticalAlignMethod], optional): Optional vertical align, one of "top", "middle", or "bottom". Defaults to None. + pad (bool, optional): Pad the right with spaces. Defaults to True. + width (int, optional): Restrict contents to given width, or None to use default width. Defaults to None. + height (int, optional): Set height of align renderable, or None to fit to contents. Defaults to None. + + Raises: + ValueError: if ``align`` is not one of the expected values. + """ + + def __init__( + self, + renderable: "RenderableType", + align: AlignMethod = "left", + style: Optional[StyleType] = None, + *, + vertical: Optional[VerticalAlignMethod] = None, + pad: bool = True, + width: Optional[int] = None, + height: Optional[int] = None, + ) -> None: + if align not in ("left", "center", "right"): + raise ValueError( + f'invalid value for align, expected "left", "center", or "right" (not {align!r})' + ) + if vertical is not None and vertical not in ("top", "middle", "bottom"): + raise ValueError( + f'invalid value for vertical, expected "top", "middle", or "bottom" (not {vertical!r})' + ) + self.renderable = renderable + self.align = align + self.style = style + self.vertical = vertical + self.pad = pad + self.width = width + self.height = height + + def __repr__(self) -> str: + return f"Align({self.renderable!r}, {self.align!r})" + + @classmethod + def left( + cls, + renderable: "RenderableType", + style: Optional[StyleType] = None, + *, + vertical: Optional[VerticalAlignMethod] = None, + pad: bool = True, + width: Optional[int] = None, + height: Optional[int] = None, + ) -> "Align": + """Align a renderable to the left.""" + return cls( + renderable, + "left", + style=style, + vertical=vertical, + pad=pad, + width=width, + height=height, + ) + + @classmethod + def center( + cls, + renderable: "RenderableType", + style: Optional[StyleType] = None, + *, + vertical: Optional[VerticalAlignMethod] = None, + pad: bool = True, + width: Optional[int] = None, + height: Optional[int] = None, + ) -> "Align": + """Align a renderable to the center.""" + return cls( + renderable, + "center", + style=style, + vertical=vertical, + pad=pad, + width=width, + height=height, + ) + + @classmethod + def right( + cls, + renderable: "RenderableType", + style: Optional[StyleType] = None, + *, + vertical: Optional[VerticalAlignMethod] = None, + pad: bool = True, + width: Optional[int] = None, + height: Optional[int] = None, + ) -> "Align": + """Align a renderable to the right.""" + return cls( + renderable, + "right", + style=style, + vertical=vertical, + pad=pad, + width=width, + height=height, + ) + + def __rich_console__( + self, console: "Console", options: "ConsoleOptions" + ) -> "RenderResult": + align = self.align + width = console.measure(self.renderable, options=options).maximum + rendered = console.render( + Constrain( + self.renderable, width if self.width is None else min(width, self.width) + ), + options.update(height=None), + ) + lines = list(Segment.split_lines(rendered)) + width, height = Segment.get_shape(lines) + lines = Segment.set_shape(lines, width, height) + new_line = Segment.line() + excess_space = options.max_width - width + style = console.get_style(self.style) if self.style is not None else None + + def generate_segments() -> Iterable[Segment]: + if excess_space <= 0: + # Exact fit + for line in lines: + yield from line + yield new_line + + elif align == "left": + # Pad on the right + pad = Segment(" " * excess_space, style) if self.pad else None + for line in lines: + yield from line + if pad: + yield pad + yield new_line + + elif align == "center": + # Pad left and right + left = excess_space // 2 + pad = Segment(" " * left, style) + pad_right = ( + Segment(" " * (excess_space - left), style) if self.pad else None + ) + for line in lines: + if left: + yield pad + yield from line + if pad_right: + yield pad_right + yield new_line + + elif align == "right": + # Padding on left + pad = Segment(" " * excess_space, style) + for line in lines: + yield pad + yield from line + yield new_line + + blank_line = ( + Segment(f"{' ' * (self.width or options.max_width)}\n", style) + if self.pad + else Segment("\n") + ) + + def blank_lines(count: int) -> Iterable[Segment]: + if count > 0: + for _ in range(count): + yield blank_line + + vertical_height = self.height or options.height + iter_segments: Iterable[Segment] + if self.vertical and vertical_height is not None: + if self.vertical == "top": + bottom_space = vertical_height - height + iter_segments = chain(generate_segments(), blank_lines(bottom_space)) + elif self.vertical == "middle": + top_space = (vertical_height - height) // 2 + bottom_space = vertical_height - top_space - height + iter_segments = chain( + blank_lines(top_space), + generate_segments(), + blank_lines(bottom_space), + ) + else: # self.vertical == "bottom": + top_space = vertical_height - height + iter_segments = chain(blank_lines(top_space), generate_segments()) + else: + iter_segments = generate_segments() + if self.style: + style = console.get_style(self.style) + iter_segments = Segment.apply_style(iter_segments, style) + yield from iter_segments + + def __rich_measure__( + self, console: "Console", options: "ConsoleOptions" + ) -> Measurement: + measurement = Measurement.get(console, options, self.renderable) + return measurement + + +class VerticalCenter(JupyterMixin): + """Vertically aligns a renderable. + + Warn: + This class is deprecated and may be removed in a future version. Use Align class with + `vertical="middle"`. + + Args: + renderable (RenderableType): A renderable object. + style (StyleType, optional): An optional style to apply to the background. Defaults to None. + """ + + def __init__( + self, + renderable: "RenderableType", + style: Optional[StyleType] = None, + ) -> None: + self.renderable = renderable + self.style = style + + def __repr__(self) -> str: + return f"VerticalCenter({self.renderable!r})" + + def __rich_console__( + self, console: "Console", options: "ConsoleOptions" + ) -> "RenderResult": + style = console.get_style(self.style) if self.style is not None else None + lines = console.render_lines( + self.renderable, options.update(height=None), pad=False + ) + width, _height = Segment.get_shape(lines) + new_line = Segment.line() + height = options.height or options.size.height + top_space = (height - len(lines)) // 2 + bottom_space = height - top_space - len(lines) + blank_line = Segment(f"{' ' * width}", style) + + def blank_lines(count: int) -> Iterable[Segment]: + for _ in range(count): + yield blank_line + yield new_line + + if top_space > 0: + yield from blank_lines(top_space) + for line in lines: + yield from line + yield new_line + if bottom_space > 0: + yield from blank_lines(bottom_space) + + def __rich_measure__( + self, console: "Console", options: "ConsoleOptions" + ) -> Measurement: + measurement = Measurement.get(console, options, self.renderable) + return measurement + + +if __name__ == "__main__": # pragma: no cover + from pip._vendor.rich.console import Console, Group + from pip._vendor.rich.highlighter import ReprHighlighter + from pip._vendor.rich.panel import Panel + + highlighter = ReprHighlighter() + console = Console() + + panel = Panel( + Group( + Align.left(highlighter("align='left'")), + Align.center(highlighter("align='center'")), + Align.right(highlighter("align='right'")), + ), + width=60, + style="on dark_blue", + title="Align", + ) + + console.print( + Align.center(panel, vertical="middle", style="on red", height=console.height) + ) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/ansi.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/ansi.py new file mode 100644 index 0000000..7de86ce --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/ansi.py @@ -0,0 +1,241 @@ +import re +import sys +from contextlib import suppress +from typing import Iterable, NamedTuple, Optional + +from .color import Color +from .style import Style +from .text import Text + +re_ansi = re.compile( + r""" +(?:\x1b[0-?])| +(?:\x1b\](.*?)\x1b\\)| +(?:\x1b([(@-Z\\-_]|\[[0-?]*[ -/]*[@-~])) +""", + re.VERBOSE, +) + + +class _AnsiToken(NamedTuple): + """Result of ansi tokenized string.""" + + plain: str = "" + sgr: Optional[str] = "" + osc: Optional[str] = "" + + +def _ansi_tokenize(ansi_text: str) -> Iterable[_AnsiToken]: + """Tokenize a string in to plain text and ANSI codes. + + Args: + ansi_text (str): A String containing ANSI codes. + + Yields: + AnsiToken: A named tuple of (plain, sgr, osc) + """ + + position = 0 + sgr: Optional[str] + osc: Optional[str] + for match in re_ansi.finditer(ansi_text): + start, end = match.span(0) + osc, sgr = match.groups() + if start > position: + yield _AnsiToken(ansi_text[position:start]) + if sgr: + if sgr == "(": + position = end + 1 + continue + if sgr.endswith("m"): + yield _AnsiToken("", sgr[1:-1], osc) + else: + yield _AnsiToken("", sgr, osc) + position = end + if position < len(ansi_text): + yield _AnsiToken(ansi_text[position:]) + + +SGR_STYLE_MAP = { + 1: "bold", + 2: "dim", + 3: "italic", + 4: "underline", + 5: "blink", + 6: "blink2", + 7: "reverse", + 8: "conceal", + 9: "strike", + 21: "underline2", + 22: "not dim not bold", + 23: "not italic", + 24: "not underline", + 25: "not blink", + 26: "not blink2", + 27: "not reverse", + 28: "not conceal", + 29: "not strike", + 30: "color(0)", + 31: "color(1)", + 32: "color(2)", + 33: "color(3)", + 34: "color(4)", + 35: "color(5)", + 36: "color(6)", + 37: "color(7)", + 39: "default", + 40: "on color(0)", + 41: "on color(1)", + 42: "on color(2)", + 43: "on color(3)", + 44: "on color(4)", + 45: "on color(5)", + 46: "on color(6)", + 47: "on color(7)", + 49: "on default", + 51: "frame", + 52: "encircle", + 53: "overline", + 54: "not frame not encircle", + 55: "not overline", + 90: "color(8)", + 91: "color(9)", + 92: "color(10)", + 93: "color(11)", + 94: "color(12)", + 95: "color(13)", + 96: "color(14)", + 97: "color(15)", + 100: "on color(8)", + 101: "on color(9)", + 102: "on color(10)", + 103: "on color(11)", + 104: "on color(12)", + 105: "on color(13)", + 106: "on color(14)", + 107: "on color(15)", +} + + +class AnsiDecoder: + """Translate ANSI code in to styled Text.""" + + def __init__(self) -> None: + self.style = Style.null() + + def decode(self, terminal_text: str) -> Iterable[Text]: + """Decode ANSI codes in an iterable of lines. + + Args: + lines (Iterable[str]): An iterable of lines of terminal output. + + Yields: + Text: Marked up Text. + """ + for line in terminal_text.splitlines(): + yield self.decode_line(line) + + def decode_line(self, line: str) -> Text: + """Decode a line containing ansi codes. + + Args: + line (str): A line of terminal output. + + Returns: + Text: A Text instance marked up according to ansi codes. + """ + from_ansi = Color.from_ansi + from_rgb = Color.from_rgb + _Style = Style + text = Text() + append = text.append + line = line.rsplit("\r", 1)[-1] + for plain_text, sgr, osc in _ansi_tokenize(line): + if plain_text: + append(plain_text, self.style or None) + elif osc is not None: + if osc.startswith("8;"): + _params, semicolon, link = osc[2:].partition(";") + if semicolon: + self.style = self.style.update_link(link or None) + elif sgr is not None: + # Translate in to semi-colon separated codes + # Ignore invalid codes, because we want to be lenient + codes = [ + min(255, int(_code) if _code else 0) + for _code in sgr.split(";") + if _code.isdigit() or _code == "" + ] + iter_codes = iter(codes) + for code in iter_codes: + if code == 0: + # reset + self.style = _Style.null() + elif code in SGR_STYLE_MAP: + # styles + self.style += _Style.parse(SGR_STYLE_MAP[code]) + elif code == 38: + #  Foreground + with suppress(StopIteration): + color_type = next(iter_codes) + if color_type == 5: + self.style += _Style.from_color( + from_ansi(next(iter_codes)) + ) + elif color_type == 2: + self.style += _Style.from_color( + from_rgb( + next(iter_codes), + next(iter_codes), + next(iter_codes), + ) + ) + elif code == 48: + # Background + with suppress(StopIteration): + color_type = next(iter_codes) + if color_type == 5: + self.style += _Style.from_color( + None, from_ansi(next(iter_codes)) + ) + elif color_type == 2: + self.style += _Style.from_color( + None, + from_rgb( + next(iter_codes), + next(iter_codes), + next(iter_codes), + ), + ) + + return text + + +if sys.platform != "win32" and __name__ == "__main__": # pragma: no cover + import io + import os + import pty + import sys + + decoder = AnsiDecoder() + + stdout = io.BytesIO() + + def read(fd: int) -> bytes: + data = os.read(fd, 1024) + stdout.write(data) + return data + + pty.spawn(sys.argv[1:], read) + + from .console import Console + + console = Console(record=True) + + stdout_result = stdout.getvalue().decode("utf-8") + print(stdout_result) + + for line in decoder.decode(stdout_result): + console.print(line) + + console.save_html("stdout.html") diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/bar.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/bar.py new file mode 100644 index 0000000..022284b --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/bar.py @@ -0,0 +1,93 @@ +from typing import Optional, Union + +from .color import Color +from .console import Console, ConsoleOptions, RenderResult +from .jupyter import JupyterMixin +from .measure import Measurement +from .segment import Segment +from .style import Style + +# There are left-aligned characters for 1/8 to 7/8, but +# the right-aligned characters exist only for 1/8 and 4/8. +BEGIN_BLOCK_ELEMENTS = ["█", "█", "█", "▐", "▐", "▐", "▕", "▕"] +END_BLOCK_ELEMENTS = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"] +FULL_BLOCK = "█" + + +class Bar(JupyterMixin): + """Renders a solid block bar. + + Args: + size (float): Value for the end of the bar. + begin (float): Begin point (between 0 and size, inclusive). + end (float): End point (between 0 and size, inclusive). + width (int, optional): Width of the bar, or ``None`` for maximum width. Defaults to None. + color (Union[Color, str], optional): Color of the bar. Defaults to "default". + bgcolor (Union[Color, str], optional): Color of bar background. Defaults to "default". + """ + + def __init__( + self, + size: float, + begin: float, + end: float, + *, + width: Optional[int] = None, + color: Union[Color, str] = "default", + bgcolor: Union[Color, str] = "default", + ): + self.size = size + self.begin = max(begin, 0) + self.end = min(end, size) + self.width = width + self.style = Style(color=color, bgcolor=bgcolor) + + def __repr__(self) -> str: + return f"Bar({self.size}, {self.begin}, {self.end})" + + def __rich_console__( + self, console: Console, options: ConsoleOptions + ) -> RenderResult: + width = min( + self.width if self.width is not None else options.max_width, + options.max_width, + ) + + if self.begin >= self.end: + yield Segment(" " * width, self.style) + yield Segment.line() + return + + prefix_complete_eights = int(width * 8 * self.begin / self.size) + prefix_bar_count = prefix_complete_eights // 8 + prefix_eights_count = prefix_complete_eights % 8 + + body_complete_eights = int(width * 8 * self.end / self.size) + body_bar_count = body_complete_eights // 8 + body_eights_count = body_complete_eights % 8 + + # When start and end fall into the same cell, we ideally should render + # a symbol that's "center-aligned", but there is no good symbol in Unicode. + # In this case, we fall back to right-aligned block symbol for simplicity. + + prefix = " " * prefix_bar_count + if prefix_eights_count: + prefix += BEGIN_BLOCK_ELEMENTS[prefix_eights_count] + + body = FULL_BLOCK * body_bar_count + if body_eights_count: + body += END_BLOCK_ELEMENTS[body_eights_count] + + suffix = " " * (width - len(body)) + + yield Segment(prefix + body[len(prefix) :] + suffix, self.style) + yield Segment.line() + + def __rich_measure__( + self, console: Console, options: ConsoleOptions + ) -> Measurement: + return ( + Measurement(self.width, self.width) + if self.width is not None + else Measurement(4, options.max_width) + ) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/box.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/box.py new file mode 100644 index 0000000..3f330cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/box.py @@ -0,0 +1,474 @@ +from typing import TYPE_CHECKING, Iterable, List, Literal + + +from ._loop import loop_last + +if TYPE_CHECKING: + from pip._vendor.rich.console import ConsoleOptions + + +class Box: + """Defines characters to render boxes. + + ┌─┬┐ top + │ ││ head + ├─┼┤ head_row + │ ││ mid + ├─┼┤ row + ├─┼┤ foot_row + │ ││ foot + └─┴┘ bottom + + Args: + box (str): Characters making up box. + ascii (bool, optional): True if this box uses ascii characters only. Default is False. + """ + + def __init__(self, box: str, *, ascii: bool = False) -> None: + self._box = box + self.ascii = ascii + line1, line2, line3, line4, line5, line6, line7, line8 = box.splitlines() + # top + self.top_left, self.top, self.top_divider, self.top_right = iter(line1) + # head + self.head_left, _, self.head_vertical, self.head_right = iter(line2) + # head_row + ( + self.head_row_left, + self.head_row_horizontal, + self.head_row_cross, + self.head_row_right, + ) = iter(line3) + + # mid + self.mid_left, _, self.mid_vertical, self.mid_right = iter(line4) + # row + self.row_left, self.row_horizontal, self.row_cross, self.row_right = iter(line5) + # foot_row + ( + self.foot_row_left, + self.foot_row_horizontal, + self.foot_row_cross, + self.foot_row_right, + ) = iter(line6) + # foot + self.foot_left, _, self.foot_vertical, self.foot_right = iter(line7) + # bottom + self.bottom_left, self.bottom, self.bottom_divider, self.bottom_right = iter( + line8 + ) + + def __repr__(self) -> str: + return "Box(...)" + + def __str__(self) -> str: + return self._box + + def substitute(self, options: "ConsoleOptions", safe: bool = True) -> "Box": + """Substitute this box for another if it won't render due to platform issues. + + Args: + options (ConsoleOptions): Console options used in rendering. + safe (bool, optional): Substitute this for another Box if there are known problems + displaying on the platform (currently only relevant on Windows). Default is True. + + Returns: + Box: A different Box or the same Box. + """ + box = self + if options.legacy_windows and safe: + box = LEGACY_WINDOWS_SUBSTITUTIONS.get(box, box) + if options.ascii_only and not box.ascii: + box = ASCII + return box + + def get_plain_headed_box(self) -> "Box": + """If this box uses special characters for the borders of the header, then + return the equivalent box that does not. + + Returns: + Box: The most similar Box that doesn't use header-specific box characters. + If the current Box already satisfies this criterion, then it's returned. + """ + return PLAIN_HEADED_SUBSTITUTIONS.get(self, self) + + def get_top(self, widths: Iterable[int]) -> str: + """Get the top of a simple box. + + Args: + widths (List[int]): Widths of columns. + + Returns: + str: A string of box characters. + """ + + parts: List[str] = [] + append = parts.append + append(self.top_left) + for last, width in loop_last(widths): + append(self.top * width) + if not last: + append(self.top_divider) + append(self.top_right) + return "".join(parts) + + def get_row( + self, + widths: Iterable[int], + level: Literal["head", "row", "foot", "mid"] = "row", + edge: bool = True, + ) -> str: + """Get the top of a simple box. + + Args: + width (List[int]): Widths of columns. + + Returns: + str: A string of box characters. + """ + if level == "head": + left = self.head_row_left + horizontal = self.head_row_horizontal + cross = self.head_row_cross + right = self.head_row_right + elif level == "row": + left = self.row_left + horizontal = self.row_horizontal + cross = self.row_cross + right = self.row_right + elif level == "mid": + left = self.mid_left + horizontal = " " + cross = self.mid_vertical + right = self.mid_right + elif level == "foot": + left = self.foot_row_left + horizontal = self.foot_row_horizontal + cross = self.foot_row_cross + right = self.foot_row_right + else: + raise ValueError("level must be 'head', 'row' or 'foot'") + + parts: List[str] = [] + append = parts.append + if edge: + append(left) + for last, width in loop_last(widths): + append(horizontal * width) + if not last: + append(cross) + if edge: + append(right) + return "".join(parts) + + def get_bottom(self, widths: Iterable[int]) -> str: + """Get the bottom of a simple box. + + Args: + widths (List[int]): Widths of columns. + + Returns: + str: A string of box characters. + """ + + parts: List[str] = [] + append = parts.append + append(self.bottom_left) + for last, width in loop_last(widths): + append(self.bottom * width) + if not last: + append(self.bottom_divider) + append(self.bottom_right) + return "".join(parts) + + +# fmt: off +ASCII: Box = Box( + "+--+\n" + "| ||\n" + "|-+|\n" + "| ||\n" + "|-+|\n" + "|-+|\n" + "| ||\n" + "+--+\n", + ascii=True, +) + +ASCII2: Box = Box( + "+-++\n" + "| ||\n" + "+-++\n" + "| ||\n" + "+-++\n" + "+-++\n" + "| ||\n" + "+-++\n", + ascii=True, +) + +ASCII_DOUBLE_HEAD: Box = Box( + "+-++\n" + "| ||\n" + "+=++\n" + "| ||\n" + "+-++\n" + "+-++\n" + "| ||\n" + "+-++\n", + ascii=True, +) + +SQUARE: Box = Box( + "┌─┬┐\n" + "│ ││\n" + "├─┼┤\n" + "│ ││\n" + "├─┼┤\n" + "├─┼┤\n" + "│ ││\n" + "└─┴┘\n" +) + +SQUARE_DOUBLE_HEAD: Box = Box( + "┌─┬┐\n" + "│ ││\n" + "╞═╪╡\n" + "│ ││\n" + "├─┼┤\n" + "├─┼┤\n" + "│ ││\n" + "└─┴┘\n" +) + +MINIMAL: Box = Box( + " ╷ \n" + " │ \n" + "╶─┼╴\n" + " │ \n" + "╶─┼╴\n" + "╶─┼╴\n" + " │ \n" + " ╵ \n" +) + + +MINIMAL_HEAVY_HEAD: Box = Box( + " ╷ \n" + " │ \n" + "╺━┿╸\n" + " │ \n" + "╶─┼╴\n" + "╶─┼╴\n" + " │ \n" + " ╵ \n" +) + +MINIMAL_DOUBLE_HEAD: Box = Box( + " ╷ \n" + " │ \n" + " ═╪ \n" + " │ \n" + " ─┼ \n" + " ─┼ \n" + " │ \n" + " ╵ \n" +) + + +SIMPLE: Box = Box( + " \n" + " \n" + " ── \n" + " \n" + " \n" + " ── \n" + " \n" + " \n" +) + +SIMPLE_HEAD: Box = Box( + " \n" + " \n" + " ── \n" + " \n" + " \n" + " \n" + " \n" + " \n" +) + + +SIMPLE_HEAVY: Box = Box( + " \n" + " \n" + " ━━ \n" + " \n" + " \n" + " ━━ \n" + " \n" + " \n" +) + + +HORIZONTALS: Box = Box( + " ── \n" + " \n" + " ── \n" + " \n" + " ── \n" + " ── \n" + " \n" + " ── \n" +) + +ROUNDED: Box = Box( + "╭─┬╮\n" + "│ ││\n" + "├─┼┤\n" + "│ ││\n" + "├─┼┤\n" + "├─┼┤\n" + "│ ││\n" + "╰─┴╯\n" +) + +HEAVY: Box = Box( + "┏━┳┓\n" + "┃ ┃┃\n" + "┣━╋┫\n" + "┃ ┃┃\n" + "┣━╋┫\n" + "┣━╋┫\n" + "┃ ┃┃\n" + "┗━┻┛\n" +) + +HEAVY_EDGE: Box = Box( + "┏━┯┓\n" + "┃ │┃\n" + "┠─┼┨\n" + "┃ │┃\n" + "┠─┼┨\n" + "┠─┼┨\n" + "┃ │┃\n" + "┗━┷┛\n" +) + +HEAVY_HEAD: Box = Box( + "┏━┳┓\n" + "┃ ┃┃\n" + "┡━╇┩\n" + "│ ││\n" + "├─┼┤\n" + "├─┼┤\n" + "│ ││\n" + "└─┴┘\n" +) + +DOUBLE: Box = Box( + "╔═╦╗\n" + "║ ║║\n" + "╠═╬╣\n" + "║ ║║\n" + "╠═╬╣\n" + "╠═╬╣\n" + "║ ║║\n" + "╚═╩╝\n" +) + +DOUBLE_EDGE: Box = Box( + "╔═╤╗\n" + "║ │║\n" + "╟─┼╢\n" + "║ │║\n" + "╟─┼╢\n" + "╟─┼╢\n" + "║ │║\n" + "╚═╧╝\n" +) + +MARKDOWN: Box = Box( + " \n" + "| ||\n" + "|-||\n" + "| ||\n" + "|-||\n" + "|-||\n" + "| ||\n" + " \n", + ascii=True, +) +# fmt: on + +# Map Boxes that don't render with raster fonts on to equivalent that do +LEGACY_WINDOWS_SUBSTITUTIONS = { + ROUNDED: SQUARE, + MINIMAL_HEAVY_HEAD: MINIMAL, + SIMPLE_HEAVY: SIMPLE, + HEAVY: SQUARE, + HEAVY_EDGE: SQUARE, + HEAVY_HEAD: SQUARE, +} + +# Map headed boxes to their headerless equivalents +PLAIN_HEADED_SUBSTITUTIONS = { + HEAVY_HEAD: SQUARE, + SQUARE_DOUBLE_HEAD: SQUARE, + MINIMAL_DOUBLE_HEAD: MINIMAL, + MINIMAL_HEAVY_HEAD: MINIMAL, + ASCII_DOUBLE_HEAD: ASCII2, +} + + +if __name__ == "__main__": # pragma: no cover + from pip._vendor.rich.columns import Columns + from pip._vendor.rich.panel import Panel + + from . import box as box + from .console import Console + from .table import Table + from .text import Text + + console = Console(record=True) + + BOXES = [ + "ASCII", + "ASCII2", + "ASCII_DOUBLE_HEAD", + "SQUARE", + "SQUARE_DOUBLE_HEAD", + "MINIMAL", + "MINIMAL_HEAVY_HEAD", + "MINIMAL_DOUBLE_HEAD", + "SIMPLE", + "SIMPLE_HEAD", + "SIMPLE_HEAVY", + "HORIZONTALS", + "ROUNDED", + "HEAVY", + "HEAVY_EDGE", + "HEAVY_HEAD", + "DOUBLE", + "DOUBLE_EDGE", + "MARKDOWN", + ] + + console.print(Panel("[bold green]Box Constants", style="green"), justify="center") + console.print() + + columns = Columns(expand=True, padding=2) + for box_name in sorted(BOXES): + table = Table( + show_footer=True, style="dim", border_style="not dim", expand=True + ) + table.add_column("Header 1", "Footer 1") + table.add_column("Header 2", "Footer 2") + table.add_row("Cell", "Cell") + table.add_row("Cell", "Cell") + table.box = getattr(box, box_name) + table.title = Text(f"box.{box_name}", style="magenta") + columns.add_renderable(table) + console.print(columns) + + # console.save_svg("box.svg") diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/cells.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/cells.py new file mode 100644 index 0000000..a854622 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/cells.py @@ -0,0 +1,174 @@ +from __future__ import annotations + +from functools import lru_cache +from typing import Callable + +from ._cell_widths import CELL_WIDTHS + +# Ranges of unicode ordinals that produce a 1-cell wide character +# This is non-exhaustive, but covers most common Western characters +_SINGLE_CELL_UNICODE_RANGES: list[tuple[int, int]] = [ + (0x20, 0x7E), # Latin (excluding non-printable) + (0xA0, 0xAC), + (0xAE, 0x002FF), + (0x00370, 0x00482), # Greek / Cyrillic + (0x02500, 0x025FC), # Box drawing, box elements, geometric shapes + (0x02800, 0x028FF), # Braille +] + +# A set of characters that are a single cell wide +_SINGLE_CELLS = frozenset( + [ + character + for _start, _end in _SINGLE_CELL_UNICODE_RANGES + for character in map(chr, range(_start, _end + 1)) + ] +) + +# When called with a string this will return True if all +# characters are single-cell, otherwise False +_is_single_cell_widths: Callable[[str], bool] = _SINGLE_CELLS.issuperset + + +@lru_cache(4096) +def cached_cell_len(text: str) -> int: + """Get the number of cells required to display text. + + This method always caches, which may use up a lot of memory. It is recommended to use + `cell_len` over this method. + + Args: + text (str): Text to display. + + Returns: + int: Get the number of cells required to display text. + """ + if _is_single_cell_widths(text): + return len(text) + return sum(map(get_character_cell_size, text)) + + +def cell_len(text: str, _cell_len: Callable[[str], int] = cached_cell_len) -> int: + """Get the number of cells required to display text. + + Args: + text (str): Text to display. + + Returns: + int: Get the number of cells required to display text. + """ + if len(text) < 512: + return _cell_len(text) + if _is_single_cell_widths(text): + return len(text) + return sum(map(get_character_cell_size, text)) + + +@lru_cache(maxsize=4096) +def get_character_cell_size(character: str) -> int: + """Get the cell size of a character. + + Args: + character (str): A single character. + + Returns: + int: Number of cells (0, 1 or 2) occupied by that character. + """ + codepoint = ord(character) + _table = CELL_WIDTHS + lower_bound = 0 + upper_bound = len(_table) - 1 + index = (lower_bound + upper_bound) // 2 + while True: + start, end, width = _table[index] + if codepoint < start: + upper_bound = index - 1 + elif codepoint > end: + lower_bound = index + 1 + else: + return 0 if width == -1 else width + if upper_bound < lower_bound: + break + index = (lower_bound + upper_bound) // 2 + return 1 + + +def set_cell_size(text: str, total: int) -> str: + """Set the length of a string to fit within given number of cells.""" + + if _is_single_cell_widths(text): + size = len(text) + if size < total: + return text + " " * (total - size) + return text[:total] + + if total <= 0: + return "" + cell_size = cell_len(text) + if cell_size == total: + return text + if cell_size < total: + return text + " " * (total - cell_size) + + start = 0 + end = len(text) + + # Binary search until we find the right size + while True: + pos = (start + end) // 2 + before = text[: pos + 1] + before_len = cell_len(before) + if before_len == total + 1 and cell_len(before[-1]) == 2: + return before[:-1] + " " + if before_len == total: + return before + if before_len > total: + end = pos + else: + start = pos + + +def chop_cells( + text: str, + width: int, +) -> list[str]: + """Split text into lines such that each line fits within the available (cell) width. + + Args: + text: The text to fold such that it fits in the given width. + width: The width available (number of cells). + + Returns: + A list of strings such that each string in the list has cell width + less than or equal to the available width. + """ + _get_character_cell_size = get_character_cell_size + lines: list[list[str]] = [[]] + + append_new_line = lines.append + append_to_last_line = lines[-1].append + + total_width = 0 + + for character in text: + cell_width = _get_character_cell_size(character) + char_doesnt_fit = total_width + cell_width > width + + if char_doesnt_fit: + append_new_line([character]) + append_to_last_line = lines[-1].append + total_width = cell_width + else: + append_to_last_line(character) + total_width += cell_width + + return ["".join(line) for line in lines] + + +if __name__ == "__main__": # pragma: no cover + print(get_character_cell_size("😽")) + for line in chop_cells("""这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑。""", 8): + print(line) + for n in range(80, 1, -1): + print(set_cell_size("""这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑。""", n) + "|") + print("x" * n) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/color.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/color.py new file mode 100644 index 0000000..e2c23a6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/color.py @@ -0,0 +1,621 @@ +import re +import sys +from colorsys import rgb_to_hls +from enum import IntEnum +from functools import lru_cache +from typing import TYPE_CHECKING, NamedTuple, Optional, Tuple + +from ._palettes import EIGHT_BIT_PALETTE, STANDARD_PALETTE, WINDOWS_PALETTE +from .color_triplet import ColorTriplet +from .repr import Result, rich_repr +from .terminal_theme import DEFAULT_TERMINAL_THEME + +if TYPE_CHECKING: # pragma: no cover + from .terminal_theme import TerminalTheme + from .text import Text + + +WINDOWS = sys.platform == "win32" + + +class ColorSystem(IntEnum): + """One of the 3 color system supported by terminals.""" + + STANDARD = 1 + EIGHT_BIT = 2 + TRUECOLOR = 3 + WINDOWS = 4 + + def __repr__(self) -> str: + return f"ColorSystem.{self.name}" + + def __str__(self) -> str: + return repr(self) + + +class ColorType(IntEnum): + """Type of color stored in Color class.""" + + DEFAULT = 0 + STANDARD = 1 + EIGHT_BIT = 2 + TRUECOLOR = 3 + WINDOWS = 4 + + def __repr__(self) -> str: + return f"ColorType.{self.name}" + + +ANSI_COLOR_NAMES = { + "black": 0, + "red": 1, + "green": 2, + "yellow": 3, + "blue": 4, + "magenta": 5, + "cyan": 6, + "white": 7, + "bright_black": 8, + "bright_red": 9, + "bright_green": 10, + "bright_yellow": 11, + "bright_blue": 12, + "bright_magenta": 13, + "bright_cyan": 14, + "bright_white": 15, + "grey0": 16, + "gray0": 16, + "navy_blue": 17, + "dark_blue": 18, + "blue3": 20, + "blue1": 21, + "dark_green": 22, + "deep_sky_blue4": 25, + "dodger_blue3": 26, + "dodger_blue2": 27, + "green4": 28, + "spring_green4": 29, + "turquoise4": 30, + "deep_sky_blue3": 32, + "dodger_blue1": 33, + "green3": 40, + "spring_green3": 41, + "dark_cyan": 36, + "light_sea_green": 37, + "deep_sky_blue2": 38, + "deep_sky_blue1": 39, + "spring_green2": 47, + "cyan3": 43, + "dark_turquoise": 44, + "turquoise2": 45, + "green1": 46, + "spring_green1": 48, + "medium_spring_green": 49, + "cyan2": 50, + "cyan1": 51, + "dark_red": 88, + "deep_pink4": 125, + "purple4": 55, + "purple3": 56, + "blue_violet": 57, + "orange4": 94, + "grey37": 59, + "gray37": 59, + "medium_purple4": 60, + "slate_blue3": 62, + "royal_blue1": 63, + "chartreuse4": 64, + "dark_sea_green4": 71, + "pale_turquoise4": 66, + "steel_blue": 67, + "steel_blue3": 68, + "cornflower_blue": 69, + "chartreuse3": 76, + "cadet_blue": 73, + "sky_blue3": 74, + "steel_blue1": 81, + "pale_green3": 114, + "sea_green3": 78, + "aquamarine3": 79, + "medium_turquoise": 80, + "chartreuse2": 112, + "sea_green2": 83, + "sea_green1": 85, + "aquamarine1": 122, + "dark_slate_gray2": 87, + "dark_magenta": 91, + "dark_violet": 128, + "purple": 129, + "light_pink4": 95, + "plum4": 96, + "medium_purple3": 98, + "slate_blue1": 99, + "yellow4": 106, + "wheat4": 101, + "grey53": 102, + "gray53": 102, + "light_slate_grey": 103, + "light_slate_gray": 103, + "medium_purple": 104, + "light_slate_blue": 105, + "dark_olive_green3": 149, + "dark_sea_green": 108, + "light_sky_blue3": 110, + "sky_blue2": 111, + "dark_sea_green3": 150, + "dark_slate_gray3": 116, + "sky_blue1": 117, + "chartreuse1": 118, + "light_green": 120, + "pale_green1": 156, + "dark_slate_gray1": 123, + "red3": 160, + "medium_violet_red": 126, + "magenta3": 164, + "dark_orange3": 166, + "indian_red": 167, + "hot_pink3": 168, + "medium_orchid3": 133, + "medium_orchid": 134, + "medium_purple2": 140, + "dark_goldenrod": 136, + "light_salmon3": 173, + "rosy_brown": 138, + "grey63": 139, + "gray63": 139, + "medium_purple1": 141, + "gold3": 178, + "dark_khaki": 143, + "navajo_white3": 144, + "grey69": 145, + "gray69": 145, + "light_steel_blue3": 146, + "light_steel_blue": 147, + "yellow3": 184, + "dark_sea_green2": 157, + "light_cyan3": 152, + "light_sky_blue1": 153, + "green_yellow": 154, + "dark_olive_green2": 155, + "dark_sea_green1": 193, + "pale_turquoise1": 159, + "deep_pink3": 162, + "magenta2": 200, + "hot_pink2": 169, + "orchid": 170, + "medium_orchid1": 207, + "orange3": 172, + "light_pink3": 174, + "pink3": 175, + "plum3": 176, + "violet": 177, + "light_goldenrod3": 179, + "tan": 180, + "misty_rose3": 181, + "thistle3": 182, + "plum2": 183, + "khaki3": 185, + "light_goldenrod2": 222, + "light_yellow3": 187, + "grey84": 188, + "gray84": 188, + "light_steel_blue1": 189, + "yellow2": 190, + "dark_olive_green1": 192, + "honeydew2": 194, + "light_cyan1": 195, + "red1": 196, + "deep_pink2": 197, + "deep_pink1": 199, + "magenta1": 201, + "orange_red1": 202, + "indian_red1": 204, + "hot_pink": 206, + "dark_orange": 208, + "salmon1": 209, + "light_coral": 210, + "pale_violet_red1": 211, + "orchid2": 212, + "orchid1": 213, + "orange1": 214, + "sandy_brown": 215, + "light_salmon1": 216, + "light_pink1": 217, + "pink1": 218, + "plum1": 219, + "gold1": 220, + "navajo_white1": 223, + "misty_rose1": 224, + "thistle1": 225, + "yellow1": 226, + "light_goldenrod1": 227, + "khaki1": 228, + "wheat1": 229, + "cornsilk1": 230, + "grey100": 231, + "gray100": 231, + "grey3": 232, + "gray3": 232, + "grey7": 233, + "gray7": 233, + "grey11": 234, + "gray11": 234, + "grey15": 235, + "gray15": 235, + "grey19": 236, + "gray19": 236, + "grey23": 237, + "gray23": 237, + "grey27": 238, + "gray27": 238, + "grey30": 239, + "gray30": 239, + "grey35": 240, + "gray35": 240, + "grey39": 241, + "gray39": 241, + "grey42": 242, + "gray42": 242, + "grey46": 243, + "gray46": 243, + "grey50": 244, + "gray50": 244, + "grey54": 245, + "gray54": 245, + "grey58": 246, + "gray58": 246, + "grey62": 247, + "gray62": 247, + "grey66": 248, + "gray66": 248, + "grey70": 249, + "gray70": 249, + "grey74": 250, + "gray74": 250, + "grey78": 251, + "gray78": 251, + "grey82": 252, + "gray82": 252, + "grey85": 253, + "gray85": 253, + "grey89": 254, + "gray89": 254, + "grey93": 255, + "gray93": 255, +} + + +class ColorParseError(Exception): + """The color could not be parsed.""" + + +RE_COLOR = re.compile( + r"""^ +\#([0-9a-f]{6})$| +color\(([0-9]{1,3})\)$| +rgb\(([\d\s,]+)\)$ +""", + re.VERBOSE, +) + + +@rich_repr +class Color(NamedTuple): + """Terminal color definition.""" + + name: str + """The name of the color (typically the input to Color.parse).""" + type: ColorType + """The type of the color.""" + number: Optional[int] = None + """The color number, if a standard color, or None.""" + triplet: Optional[ColorTriplet] = None + """A triplet of color components, if an RGB color.""" + + def __rich__(self) -> "Text": + """Displays the actual color if Rich printed.""" + from .style import Style + from .text import Text + + return Text.assemble( + f"", + ) + + def __rich_repr__(self) -> Result: + yield self.name + yield self.type + yield "number", self.number, None + yield "triplet", self.triplet, None + + @property + def system(self) -> ColorSystem: + """Get the native color system for this color.""" + if self.type == ColorType.DEFAULT: + return ColorSystem.STANDARD + return ColorSystem(int(self.type)) + + @property + def is_system_defined(self) -> bool: + """Check if the color is ultimately defined by the system.""" + return self.system not in (ColorSystem.EIGHT_BIT, ColorSystem.TRUECOLOR) + + @property + def is_default(self) -> bool: + """Check if the color is a default color.""" + return self.type == ColorType.DEFAULT + + def get_truecolor( + self, theme: Optional["TerminalTheme"] = None, foreground: bool = True + ) -> ColorTriplet: + """Get an equivalent color triplet for this color. + + Args: + theme (TerminalTheme, optional): Optional terminal theme, or None to use default. Defaults to None. + foreground (bool, optional): True for a foreground color, or False for background. Defaults to True. + + Returns: + ColorTriplet: A color triplet containing RGB components. + """ + + if theme is None: + theme = DEFAULT_TERMINAL_THEME + if self.type == ColorType.TRUECOLOR: + assert self.triplet is not None + return self.triplet + elif self.type == ColorType.EIGHT_BIT: + assert self.number is not None + return EIGHT_BIT_PALETTE[self.number] + elif self.type == ColorType.STANDARD: + assert self.number is not None + return theme.ansi_colors[self.number] + elif self.type == ColorType.WINDOWS: + assert self.number is not None + return WINDOWS_PALETTE[self.number] + else: # self.type == ColorType.DEFAULT: + assert self.number is None + return theme.foreground_color if foreground else theme.background_color + + @classmethod + def from_ansi(cls, number: int) -> "Color": + """Create a Color number from it's 8-bit ansi number. + + Args: + number (int): A number between 0-255 inclusive. + + Returns: + Color: A new Color instance. + """ + return cls( + name=f"color({number})", + type=(ColorType.STANDARD if number < 16 else ColorType.EIGHT_BIT), + number=number, + ) + + @classmethod + def from_triplet(cls, triplet: "ColorTriplet") -> "Color": + """Create a truecolor RGB color from a triplet of values. + + Args: + triplet (ColorTriplet): A color triplet containing red, green and blue components. + + Returns: + Color: A new color object. + """ + return cls(name=triplet.hex, type=ColorType.TRUECOLOR, triplet=triplet) + + @classmethod + def from_rgb(cls, red: float, green: float, blue: float) -> "Color": + """Create a truecolor from three color components in the range(0->255). + + Args: + red (float): Red component in range 0-255. + green (float): Green component in range 0-255. + blue (float): Blue component in range 0-255. + + Returns: + Color: A new color object. + """ + return cls.from_triplet(ColorTriplet(int(red), int(green), int(blue))) + + @classmethod + def default(cls) -> "Color": + """Get a Color instance representing the default color. + + Returns: + Color: Default color. + """ + return cls(name="default", type=ColorType.DEFAULT) + + @classmethod + @lru_cache(maxsize=1024) + def parse(cls, color: str) -> "Color": + """Parse a color definition.""" + original_color = color + color = color.lower().strip() + + if color == "default": + return cls(color, type=ColorType.DEFAULT) + + color_number = ANSI_COLOR_NAMES.get(color) + if color_number is not None: + return cls( + color, + type=(ColorType.STANDARD if color_number < 16 else ColorType.EIGHT_BIT), + number=color_number, + ) + + color_match = RE_COLOR.match(color) + if color_match is None: + raise ColorParseError(f"{original_color!r} is not a valid color") + + color_24, color_8, color_rgb = color_match.groups() + if color_24: + triplet = ColorTriplet( + int(color_24[0:2], 16), int(color_24[2:4], 16), int(color_24[4:6], 16) + ) + return cls(color, ColorType.TRUECOLOR, triplet=triplet) + + elif color_8: + number = int(color_8) + if number > 255: + raise ColorParseError(f"color number must be <= 255 in {color!r}") + return cls( + color, + type=(ColorType.STANDARD if number < 16 else ColorType.EIGHT_BIT), + number=number, + ) + + else: # color_rgb: + components = color_rgb.split(",") + if len(components) != 3: + raise ColorParseError( + f"expected three components in {original_color!r}" + ) + red, green, blue = components + triplet = ColorTriplet(int(red), int(green), int(blue)) + if not all(component <= 255 for component in triplet): + raise ColorParseError( + f"color components must be <= 255 in {original_color!r}" + ) + return cls(color, ColorType.TRUECOLOR, triplet=triplet) + + @lru_cache(maxsize=1024) + def get_ansi_codes(self, foreground: bool = True) -> Tuple[str, ...]: + """Get the ANSI escape codes for this color.""" + _type = self.type + if _type == ColorType.DEFAULT: + return ("39" if foreground else "49",) + + elif _type == ColorType.WINDOWS: + number = self.number + assert number is not None + fore, back = (30, 40) if number < 8 else (82, 92) + return (str(fore + number if foreground else back + number),) + + elif _type == ColorType.STANDARD: + number = self.number + assert number is not None + fore, back = (30, 40) if number < 8 else (82, 92) + return (str(fore + number if foreground else back + number),) + + elif _type == ColorType.EIGHT_BIT: + assert self.number is not None + return ("38" if foreground else "48", "5", str(self.number)) + + else: # self.standard == ColorStandard.TRUECOLOR: + assert self.triplet is not None + red, green, blue = self.triplet + return ("38" if foreground else "48", "2", str(red), str(green), str(blue)) + + @lru_cache(maxsize=1024) + def downgrade(self, system: ColorSystem) -> "Color": + """Downgrade a color system to a system with fewer colors.""" + + if self.type in (ColorType.DEFAULT, system): + return self + # Convert to 8-bit color from truecolor color + if system == ColorSystem.EIGHT_BIT and self.system == ColorSystem.TRUECOLOR: + assert self.triplet is not None + _h, l, s = rgb_to_hls(*self.triplet.normalized) + # If saturation is under 15% assume it is grayscale + if s < 0.15: + gray = round(l * 25.0) + if gray == 0: + color_number = 16 + elif gray == 25: + color_number = 231 + else: + color_number = 231 + gray + return Color(self.name, ColorType.EIGHT_BIT, number=color_number) + + red, green, blue = self.triplet + six_red = red / 95 if red < 95 else 1 + (red - 95) / 40 + six_green = green / 95 if green < 95 else 1 + (green - 95) / 40 + six_blue = blue / 95 if blue < 95 else 1 + (blue - 95) / 40 + + color_number = ( + 16 + 36 * round(six_red) + 6 * round(six_green) + round(six_blue) + ) + return Color(self.name, ColorType.EIGHT_BIT, number=color_number) + + # Convert to standard from truecolor or 8-bit + elif system == ColorSystem.STANDARD: + if self.system == ColorSystem.TRUECOLOR: + assert self.triplet is not None + triplet = self.triplet + else: # self.system == ColorSystem.EIGHT_BIT + assert self.number is not None + triplet = ColorTriplet(*EIGHT_BIT_PALETTE[self.number]) + + color_number = STANDARD_PALETTE.match(triplet) + return Color(self.name, ColorType.STANDARD, number=color_number) + + elif system == ColorSystem.WINDOWS: + if self.system == ColorSystem.TRUECOLOR: + assert self.triplet is not None + triplet = self.triplet + else: # self.system == ColorSystem.EIGHT_BIT + assert self.number is not None + if self.number < 16: + return Color(self.name, ColorType.WINDOWS, number=self.number) + triplet = ColorTriplet(*EIGHT_BIT_PALETTE[self.number]) + + color_number = WINDOWS_PALETTE.match(triplet) + return Color(self.name, ColorType.WINDOWS, number=color_number) + + return self + + +def parse_rgb_hex(hex_color: str) -> ColorTriplet: + """Parse six hex characters in to RGB triplet.""" + assert len(hex_color) == 6, "must be 6 characters" + color = ColorTriplet( + int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16) + ) + return color + + +def blend_rgb( + color1: ColorTriplet, color2: ColorTriplet, cross_fade: float = 0.5 +) -> ColorTriplet: + """Blend one RGB color in to another.""" + r1, g1, b1 = color1 + r2, g2, b2 = color2 + new_color = ColorTriplet( + int(r1 + (r2 - r1) * cross_fade), + int(g1 + (g2 - g1) * cross_fade), + int(b1 + (b2 - b1) * cross_fade), + ) + return new_color + + +if __name__ == "__main__": # pragma: no cover + from .console import Console + from .table import Table + from .text import Text + + console = Console() + + table = Table(show_footer=False, show_edge=True) + table.add_column("Color", width=10, overflow="ellipsis") + table.add_column("Number", justify="right", style="yellow") + table.add_column("Name", style="green") + table.add_column("Hex", style="blue") + table.add_column("RGB", style="magenta") + + colors = sorted((v, k) for k, v in ANSI_COLOR_NAMES.items()) + for color_number, name in colors: + if "grey" in name: + continue + color_cell = Text(" " * 10, style=f"on {name}") + if color_number < 16: + table.add_row(color_cell, f"{color_number}", Text(f'"{name}"')) + else: + color = EIGHT_BIT_PALETTE[color_number] # type: ignore[has-type] + table.add_row( + color_cell, str(color_number), Text(f'"{name}"'), color.hex, color.rgb + ) + + console.print(table) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/color_triplet.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/color_triplet.py new file mode 100644 index 0000000..02cab32 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/color_triplet.py @@ -0,0 +1,38 @@ +from typing import NamedTuple, Tuple + + +class ColorTriplet(NamedTuple): + """The red, green, and blue components of a color.""" + + red: int + """Red component in 0 to 255 range.""" + green: int + """Green component in 0 to 255 range.""" + blue: int + """Blue component in 0 to 255 range.""" + + @property + def hex(self) -> str: + """get the color triplet in CSS style.""" + red, green, blue = self + return f"#{red:02x}{green:02x}{blue:02x}" + + @property + def rgb(self) -> str: + """The color in RGB format. + + Returns: + str: An rgb color, e.g. ``"rgb(100,23,255)"``. + """ + red, green, blue = self + return f"rgb({red},{green},{blue})" + + @property + def normalized(self) -> Tuple[float, float, float]: + """Convert components into floats between 0 and 1. + + Returns: + Tuple[float, float, float]: A tuple of three normalized colour components. + """ + red, green, blue = self + return red / 255.0, green / 255.0, blue / 255.0 diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/columns.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/columns.py new file mode 100644 index 0000000..669a3a7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/columns.py @@ -0,0 +1,187 @@ +from collections import defaultdict +from itertools import chain +from operator import itemgetter +from typing import Dict, Iterable, List, Optional, Tuple + +from .align import Align, AlignMethod +from .console import Console, ConsoleOptions, RenderableType, RenderResult +from .constrain import Constrain +from .measure import Measurement +from .padding import Padding, PaddingDimensions +from .table import Table +from .text import TextType +from .jupyter import JupyterMixin + + +class Columns(JupyterMixin): + """Display renderables in neat columns. + + Args: + renderables (Iterable[RenderableType]): Any number of Rich renderables (including str). + width (int, optional): The desired width of the columns, or None to auto detect. Defaults to None. + padding (PaddingDimensions, optional): Optional padding around cells. Defaults to (0, 1). + expand (bool, optional): Expand columns to full width. Defaults to False. + equal (bool, optional): Arrange in to equal sized columns. Defaults to False. + column_first (bool, optional): Align items from top to bottom (rather than left to right). Defaults to False. + right_to_left (bool, optional): Start column from right hand side. Defaults to False. + align (str, optional): Align value ("left", "right", or "center") or None for default. Defaults to None. + title (TextType, optional): Optional title for Columns. + """ + + def __init__( + self, + renderables: Optional[Iterable[RenderableType]] = None, + padding: PaddingDimensions = (0, 1), + *, + width: Optional[int] = None, + expand: bool = False, + equal: bool = False, + column_first: bool = False, + right_to_left: bool = False, + align: Optional[AlignMethod] = None, + title: Optional[TextType] = None, + ) -> None: + self.renderables = list(renderables or []) + self.width = width + self.padding = padding + self.expand = expand + self.equal = equal + self.column_first = column_first + self.right_to_left = right_to_left + self.align: Optional[AlignMethod] = align + self.title = title + + def add_renderable(self, renderable: RenderableType) -> None: + """Add a renderable to the columns. + + Args: + renderable (RenderableType): Any renderable object. + """ + self.renderables.append(renderable) + + def __rich_console__( + self, console: Console, options: ConsoleOptions + ) -> RenderResult: + render_str = console.render_str + renderables = [ + render_str(renderable) if isinstance(renderable, str) else renderable + for renderable in self.renderables + ] + if not renderables: + return + _top, right, _bottom, left = Padding.unpack(self.padding) + width_padding = max(left, right) + max_width = options.max_width + widths: Dict[int, int] = defaultdict(int) + column_count = len(renderables) + + get_measurement = Measurement.get + renderable_widths = [ + get_measurement(console, options, renderable).maximum + for renderable in renderables + ] + if self.equal: + renderable_widths = [max(renderable_widths)] * len(renderable_widths) + + def iter_renderables( + column_count: int, + ) -> Iterable[Tuple[int, Optional[RenderableType]]]: + item_count = len(renderables) + if self.column_first: + width_renderables = list(zip(renderable_widths, renderables)) + + column_lengths: List[int] = [item_count // column_count] * column_count + for col_no in range(item_count % column_count): + column_lengths[col_no] += 1 + + row_count = (item_count + column_count - 1) // column_count + cells = [[-1] * column_count for _ in range(row_count)] + row = col = 0 + for index in range(item_count): + cells[row][col] = index + column_lengths[col] -= 1 + if column_lengths[col]: + row += 1 + else: + col += 1 + row = 0 + for index in chain.from_iterable(cells): + if index == -1: + break + yield width_renderables[index] + else: + yield from zip(renderable_widths, renderables) + # Pad odd elements with spaces + if item_count % column_count: + for _ in range(column_count - (item_count % column_count)): + yield 0, None + + table = Table.grid(padding=self.padding, collapse_padding=True, pad_edge=False) + table.expand = self.expand + table.title = self.title + + if self.width is not None: + column_count = (max_width) // (self.width + width_padding) + for _ in range(column_count): + table.add_column(width=self.width) + else: + while column_count > 1: + widths.clear() + column_no = 0 + for renderable_width, _ in iter_renderables(column_count): + widths[column_no] = max(widths[column_no], renderable_width) + total_width = sum(widths.values()) + width_padding * ( + len(widths) - 1 + ) + if total_width > max_width: + column_count = len(widths) - 1 + break + else: + column_no = (column_no + 1) % column_count + else: + break + + get_renderable = itemgetter(1) + _renderables = [ + get_renderable(_renderable) + for _renderable in iter_renderables(column_count) + ] + if self.equal: + _renderables = [ + None + if renderable is None + else Constrain(renderable, renderable_widths[0]) + for renderable in _renderables + ] + if self.align: + align = self.align + _Align = Align + _renderables = [ + None if renderable is None else _Align(renderable, align) + for renderable in _renderables + ] + + right_to_left = self.right_to_left + add_row = table.add_row + for start in range(0, len(_renderables), column_count): + row = _renderables[start : start + column_count] + if right_to_left: + row = row[::-1] + add_row(*row) + yield table + + +if __name__ == "__main__": # pragma: no cover + import os + + console = Console() + + files = [f"{i} {s}" for i, s in enumerate(sorted(os.listdir()))] + columns = Columns(files, padding=(0, 1), expand=False, equal=False) + console.print(columns) + console.rule() + columns.column_first = True + console.print(columns) + columns.right_to_left = True + console.rule() + console.print(columns) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/console.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/console.py new file mode 100644 index 0000000..db2ba55 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/console.py @@ -0,0 +1,2680 @@ +import inspect +import os +import sys +import threading +import zlib +from abc import ABC, abstractmethod +from dataclasses import dataclass, field +from datetime import datetime +from functools import wraps +from getpass import getpass +from html import escape +from inspect import isclass +from itertools import islice +from math import ceil +from time import monotonic +from types import FrameType, ModuleType, TracebackType +from typing import ( + IO, + TYPE_CHECKING, + Any, + Callable, + Dict, + Iterable, + List, + Literal, + Mapping, + NamedTuple, + Optional, + Protocol, + TextIO, + Tuple, + Type, + Union, + cast, + runtime_checkable, +) + +from pip._vendor.rich._null_file import NULL_FILE + +from . import errors, themes +from ._emoji_replace import _emoji_replace +from ._export_format import CONSOLE_HTML_FORMAT, CONSOLE_SVG_FORMAT +from ._fileno import get_fileno +from ._log_render import FormatTimeCallable, LogRender +from .align import Align, AlignMethod +from .color import ColorSystem, blend_rgb +from .control import Control +from .emoji import EmojiVariant +from .highlighter import NullHighlighter, ReprHighlighter +from .markup import render as render_markup +from .measure import Measurement, measure_renderables +from .pager import Pager, SystemPager +from .pretty import Pretty, is_expandable +from .protocol import rich_cast +from .region import Region +from .scope import render_scope +from .screen import Screen +from .segment import Segment +from .style import Style, StyleType +from .styled import Styled +from .terminal_theme import DEFAULT_TERMINAL_THEME, SVG_EXPORT_THEME, TerminalTheme +from .text import Text, TextType +from .theme import Theme, ThemeStack + +if TYPE_CHECKING: + from ._windows import WindowsConsoleFeatures + from .live import Live + from .status import Status + +JUPYTER_DEFAULT_COLUMNS = 115 +JUPYTER_DEFAULT_LINES = 100 +WINDOWS = sys.platform == "win32" + +HighlighterType = Callable[[Union[str, "Text"]], "Text"] +JustifyMethod = Literal["default", "left", "center", "right", "full"] +OverflowMethod = Literal["fold", "crop", "ellipsis", "ignore"] + + +class NoChange: + pass + + +NO_CHANGE = NoChange() + +try: + _STDIN_FILENO = sys.__stdin__.fileno() # type: ignore[union-attr] +except Exception: + _STDIN_FILENO = 0 +try: + _STDOUT_FILENO = sys.__stdout__.fileno() # type: ignore[union-attr] +except Exception: + _STDOUT_FILENO = 1 +try: + _STDERR_FILENO = sys.__stderr__.fileno() # type: ignore[union-attr] +except Exception: + _STDERR_FILENO = 2 + +_STD_STREAMS = (_STDIN_FILENO, _STDOUT_FILENO, _STDERR_FILENO) +_STD_STREAMS_OUTPUT = (_STDOUT_FILENO, _STDERR_FILENO) + + +_TERM_COLORS = { + "kitty": ColorSystem.EIGHT_BIT, + "256color": ColorSystem.EIGHT_BIT, + "16color": ColorSystem.STANDARD, +} + + +class ConsoleDimensions(NamedTuple): + """Size of the terminal.""" + + width: int + """The width of the console in 'cells'.""" + height: int + """The height of the console in lines.""" + + +@dataclass +class ConsoleOptions: + """Options for __rich_console__ method.""" + + size: ConsoleDimensions + """Size of console.""" + legacy_windows: bool + """legacy_windows: flag for legacy windows.""" + min_width: int + """Minimum width of renderable.""" + max_width: int + """Maximum width of renderable.""" + is_terminal: bool + """True if the target is a terminal, otherwise False.""" + encoding: str + """Encoding of terminal.""" + max_height: int + """Height of container (starts as terminal)""" + justify: Optional[JustifyMethod] = None + """Justify value override for renderable.""" + overflow: Optional[OverflowMethod] = None + """Overflow value override for renderable.""" + no_wrap: Optional[bool] = False + """Disable wrapping for text.""" + highlight: Optional[bool] = None + """Highlight override for render_str.""" + markup: Optional[bool] = None + """Enable markup when rendering strings.""" + height: Optional[int] = None + + @property + def ascii_only(self) -> bool: + """Check if renderables should use ascii only.""" + return not self.encoding.startswith("utf") + + def copy(self) -> "ConsoleOptions": + """Return a copy of the options. + + Returns: + ConsoleOptions: a copy of self. + """ + options: ConsoleOptions = ConsoleOptions.__new__(ConsoleOptions) + options.__dict__ = self.__dict__.copy() + return options + + def update( + self, + *, + width: Union[int, NoChange] = NO_CHANGE, + min_width: Union[int, NoChange] = NO_CHANGE, + max_width: Union[int, NoChange] = NO_CHANGE, + justify: Union[Optional[JustifyMethod], NoChange] = NO_CHANGE, + overflow: Union[Optional[OverflowMethod], NoChange] = NO_CHANGE, + no_wrap: Union[Optional[bool], NoChange] = NO_CHANGE, + highlight: Union[Optional[bool], NoChange] = NO_CHANGE, + markup: Union[Optional[bool], NoChange] = NO_CHANGE, + height: Union[Optional[int], NoChange] = NO_CHANGE, + ) -> "ConsoleOptions": + """Update values, return a copy.""" + options = self.copy() + if not isinstance(width, NoChange): + options.min_width = options.max_width = max(0, width) + if not isinstance(min_width, NoChange): + options.min_width = min_width + if not isinstance(max_width, NoChange): + options.max_width = max_width + if not isinstance(justify, NoChange): + options.justify = justify + if not isinstance(overflow, NoChange): + options.overflow = overflow + if not isinstance(no_wrap, NoChange): + options.no_wrap = no_wrap + if not isinstance(highlight, NoChange): + options.highlight = highlight + if not isinstance(markup, NoChange): + options.markup = markup + if not isinstance(height, NoChange): + if height is not None: + options.max_height = height + options.height = None if height is None else max(0, height) + return options + + def update_width(self, width: int) -> "ConsoleOptions": + """Update just the width, return a copy. + + Args: + width (int): New width (sets both min_width and max_width) + + Returns: + ~ConsoleOptions: New console options instance. + """ + options = self.copy() + options.min_width = options.max_width = max(0, width) + return options + + def update_height(self, height: int) -> "ConsoleOptions": + """Update the height, and return a copy. + + Args: + height (int): New height + + Returns: + ~ConsoleOptions: New Console options instance. + """ + options = self.copy() + options.max_height = options.height = height + return options + + def reset_height(self) -> "ConsoleOptions": + """Return a copy of the options with height set to ``None``. + + Returns: + ~ConsoleOptions: New console options instance. + """ + options = self.copy() + options.height = None + return options + + def update_dimensions(self, width: int, height: int) -> "ConsoleOptions": + """Update the width and height, and return a copy. + + Args: + width (int): New width (sets both min_width and max_width). + height (int): New height. + + Returns: + ~ConsoleOptions: New console options instance. + """ + options = self.copy() + options.min_width = options.max_width = max(0, width) + options.height = options.max_height = height + return options + + +@runtime_checkable +class RichCast(Protocol): + """An object that may be 'cast' to a console renderable.""" + + def __rich__( + self, + ) -> Union["ConsoleRenderable", "RichCast", str]: # pragma: no cover + ... + + +@runtime_checkable +class ConsoleRenderable(Protocol): + """An object that supports the console protocol.""" + + def __rich_console__( + self, console: "Console", options: "ConsoleOptions" + ) -> "RenderResult": # pragma: no cover + ... + + +# A type that may be rendered by Console. +RenderableType = Union[ConsoleRenderable, RichCast, str] +"""A string or any object that may be rendered by Rich.""" + +# The result of calling a __rich_console__ method. +RenderResult = Iterable[Union[RenderableType, Segment]] + +_null_highlighter = NullHighlighter() + + +class CaptureError(Exception): + """An error in the Capture context manager.""" + + +class NewLine: + """A renderable to generate new line(s)""" + + def __init__(self, count: int = 1) -> None: + self.count = count + + def __rich_console__( + self, console: "Console", options: "ConsoleOptions" + ) -> Iterable[Segment]: + yield Segment("\n" * self.count) + + +class ScreenUpdate: + """Render a list of lines at a given offset.""" + + def __init__(self, lines: List[List[Segment]], x: int, y: int) -> None: + self._lines = lines + self.x = x + self.y = y + + def __rich_console__( + self, console: "Console", options: ConsoleOptions + ) -> RenderResult: + x = self.x + move_to = Control.move_to + for offset, line in enumerate(self._lines, self.y): + yield move_to(x, offset) + yield from line + + +class Capture: + """Context manager to capture the result of printing to the console. + See :meth:`~rich.console.Console.capture` for how to use. + + Args: + console (Console): A console instance to capture output. + """ + + def __init__(self, console: "Console") -> None: + self._console = console + self._result: Optional[str] = None + + def __enter__(self) -> "Capture": + self._console.begin_capture() + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + self._result = self._console.end_capture() + + def get(self) -> str: + """Get the result of the capture.""" + if self._result is None: + raise CaptureError( + "Capture result is not available until context manager exits." + ) + return self._result + + +class ThemeContext: + """A context manager to use a temporary theme. See :meth:`~rich.console.Console.use_theme` for usage.""" + + def __init__(self, console: "Console", theme: Theme, inherit: bool = True) -> None: + self.console = console + self.theme = theme + self.inherit = inherit + + def __enter__(self) -> "ThemeContext": + self.console.push_theme(self.theme) + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + self.console.pop_theme() + + +class PagerContext: + """A context manager that 'pages' content. See :meth:`~rich.console.Console.pager` for usage.""" + + def __init__( + self, + console: "Console", + pager: Optional[Pager] = None, + styles: bool = False, + links: bool = False, + ) -> None: + self._console = console + self.pager = SystemPager() if pager is None else pager + self.styles = styles + self.links = links + + def __enter__(self) -> "PagerContext": + self._console._enter_buffer() + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + if exc_type is None: + with self._console._lock: + buffer: List[Segment] = self._console._buffer[:] + del self._console._buffer[:] + segments: Iterable[Segment] = buffer + if not self.styles: + segments = Segment.strip_styles(segments) + elif not self.links: + segments = Segment.strip_links(segments) + content = self._console._render_buffer(segments) + self.pager.show(content) + self._console._exit_buffer() + + +class ScreenContext: + """A context manager that enables an alternative screen. See :meth:`~rich.console.Console.screen` for usage.""" + + def __init__( + self, console: "Console", hide_cursor: bool, style: StyleType = "" + ) -> None: + self.console = console + self.hide_cursor = hide_cursor + self.screen = Screen(style=style) + self._changed = False + + def update( + self, *renderables: RenderableType, style: Optional[StyleType] = None + ) -> None: + """Update the screen. + + Args: + renderable (RenderableType, optional): Optional renderable to replace current renderable, + or None for no change. Defaults to None. + style: (Style, optional): Replacement style, or None for no change. Defaults to None. + """ + if renderables: + self.screen.renderable = ( + Group(*renderables) if len(renderables) > 1 else renderables[0] + ) + if style is not None: + self.screen.style = style + self.console.print(self.screen, end="") + + def __enter__(self) -> "ScreenContext": + self._changed = self.console.set_alt_screen(True) + if self._changed and self.hide_cursor: + self.console.show_cursor(False) + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + if self._changed: + self.console.set_alt_screen(False) + if self.hide_cursor: + self.console.show_cursor(True) + + +class Group: + """Takes a group of renderables and returns a renderable object that renders the group. + + Args: + renderables (Iterable[RenderableType]): An iterable of renderable objects. + fit (bool, optional): Fit dimension of group to contents, or fill available space. Defaults to True. + """ + + def __init__(self, *renderables: "RenderableType", fit: bool = True) -> None: + self._renderables = renderables + self.fit = fit + self._render: Optional[List[RenderableType]] = None + + @property + def renderables(self) -> List["RenderableType"]: + if self._render is None: + self._render = list(self._renderables) + return self._render + + def __rich_measure__( + self, console: "Console", options: "ConsoleOptions" + ) -> "Measurement": + if self.fit: + return measure_renderables(console, options, self.renderables) + else: + return Measurement(options.max_width, options.max_width) + + def __rich_console__( + self, console: "Console", options: "ConsoleOptions" + ) -> RenderResult: + yield from self.renderables + + +def group(fit: bool = True) -> Callable[..., Callable[..., Group]]: + """A decorator that turns an iterable of renderables in to a group. + + Args: + fit (bool, optional): Fit dimension of group to contents, or fill available space. Defaults to True. + """ + + def decorator( + method: Callable[..., Iterable[RenderableType]], + ) -> Callable[..., Group]: + """Convert a method that returns an iterable of renderables in to a Group.""" + + @wraps(method) + def _replace(*args: Any, **kwargs: Any) -> Group: + renderables = method(*args, **kwargs) + return Group(*renderables, fit=fit) + + return _replace + + return decorator + + +def _is_jupyter() -> bool: # pragma: no cover + """Check if we're running in a Jupyter notebook.""" + try: + get_ipython # type: ignore[name-defined] + except NameError: + return False + ipython = get_ipython() # type: ignore[name-defined] + shell = ipython.__class__.__name__ + if ( + "google.colab" in str(ipython.__class__) + or os.getenv("DATABRICKS_RUNTIME_VERSION") + or shell == "ZMQInteractiveShell" + ): + return True # Jupyter notebook or qtconsole + elif shell == "TerminalInteractiveShell": + return False # Terminal running IPython + else: + return False # Other type (?) + + +COLOR_SYSTEMS = { + "standard": ColorSystem.STANDARD, + "256": ColorSystem.EIGHT_BIT, + "truecolor": ColorSystem.TRUECOLOR, + "windows": ColorSystem.WINDOWS, +} + +_COLOR_SYSTEMS_NAMES = {system: name for name, system in COLOR_SYSTEMS.items()} + + +@dataclass +class ConsoleThreadLocals(threading.local): + """Thread local values for Console context.""" + + theme_stack: ThemeStack + buffer: List[Segment] = field(default_factory=list) + buffer_index: int = 0 + + +class RenderHook(ABC): + """Provides hooks in to the render process.""" + + @abstractmethod + def process_renderables( + self, renderables: List[ConsoleRenderable] + ) -> List[ConsoleRenderable]: + """Called with a list of objects to render. + + This method can return a new list of renderables, or modify and return the same list. + + Args: + renderables (List[ConsoleRenderable]): A number of renderable objects. + + Returns: + List[ConsoleRenderable]: A replacement list of renderables. + """ + + +_windows_console_features: Optional["WindowsConsoleFeatures"] = None + + +def get_windows_console_features() -> "WindowsConsoleFeatures": # pragma: no cover + global _windows_console_features + if _windows_console_features is not None: + return _windows_console_features + from ._windows import get_windows_console_features + + _windows_console_features = get_windows_console_features() + return _windows_console_features + + +def detect_legacy_windows() -> bool: + """Detect legacy Windows.""" + return WINDOWS and not get_windows_console_features().vt + + +class Console: + """A high level console interface. + + Args: + color_system (str, optional): The color system supported by your terminal, + either ``"standard"``, ``"256"`` or ``"truecolor"``. Leave as ``"auto"`` to autodetect. + force_terminal (Optional[bool], optional): Enable/disable terminal control codes, or None to auto-detect terminal. Defaults to None. + force_jupyter (Optional[bool], optional): Enable/disable Jupyter rendering, or None to auto-detect Jupyter. Defaults to None. + force_interactive (Optional[bool], optional): Enable/disable interactive mode, or None to auto detect. Defaults to None. + soft_wrap (Optional[bool], optional): Set soft wrap default on print method. Defaults to False. + theme (Theme, optional): An optional style theme object, or ``None`` for default theme. + stderr (bool, optional): Use stderr rather than stdout if ``file`` is not specified. Defaults to False. + file (IO, optional): A file object where the console should write to. Defaults to stdout. + quiet (bool, Optional): Boolean to suppress all output. Defaults to False. + width (int, optional): The width of the terminal. Leave as default to auto-detect width. + height (int, optional): The height of the terminal. Leave as default to auto-detect height. + style (StyleType, optional): Style to apply to all output, or None for no style. Defaults to None. + no_color (Optional[bool], optional): Enabled no color mode, or None to auto detect. Defaults to None. + tab_size (int, optional): Number of spaces used to replace a tab character. Defaults to 8. + record (bool, optional): Boolean to enable recording of terminal output, + required to call :meth:`export_html`, :meth:`export_svg`, and :meth:`export_text`. Defaults to False. + markup (bool, optional): Boolean to enable :ref:`console_markup`. Defaults to True. + emoji (bool, optional): Enable emoji code. Defaults to True. + emoji_variant (str, optional): Optional emoji variant, either "text" or "emoji". Defaults to None. + highlight (bool, optional): Enable automatic highlighting. Defaults to True. + log_time (bool, optional): Boolean to enable logging of time by :meth:`log` methods. Defaults to True. + log_path (bool, optional): Boolean to enable the logging of the caller by :meth:`log`. Defaults to True. + log_time_format (Union[str, TimeFormatterCallable], optional): If ``log_time`` is enabled, either string for strftime or callable that formats the time. Defaults to "[%X] ". + highlighter (HighlighterType, optional): Default highlighter. + legacy_windows (bool, optional): Enable legacy Windows mode, or ``None`` to auto detect. Defaults to ``None``. + safe_box (bool, optional): Restrict box options that don't render on legacy Windows. + get_datetime (Callable[[], datetime], optional): Callable that gets the current time as a datetime.datetime object (used by Console.log), + or None for datetime.now. + get_time (Callable[[], time], optional): Callable that gets the current time in seconds, default uses time.monotonic. + """ + + _environ: Mapping[str, str] = os.environ + + def __init__( + self, + *, + color_system: Optional[ + Literal["auto", "standard", "256", "truecolor", "windows"] + ] = "auto", + force_terminal: Optional[bool] = None, + force_jupyter: Optional[bool] = None, + force_interactive: Optional[bool] = None, + soft_wrap: bool = False, + theme: Optional[Theme] = None, + stderr: bool = False, + file: Optional[IO[str]] = None, + quiet: bool = False, + width: Optional[int] = None, + height: Optional[int] = None, + style: Optional[StyleType] = None, + no_color: Optional[bool] = None, + tab_size: int = 8, + record: bool = False, + markup: bool = True, + emoji: bool = True, + emoji_variant: Optional[EmojiVariant] = None, + highlight: bool = True, + log_time: bool = True, + log_path: bool = True, + log_time_format: Union[str, FormatTimeCallable] = "[%X]", + highlighter: Optional["HighlighterType"] = ReprHighlighter(), + legacy_windows: Optional[bool] = None, + safe_box: bool = True, + get_datetime: Optional[Callable[[], datetime]] = None, + get_time: Optional[Callable[[], float]] = None, + _environ: Optional[Mapping[str, str]] = None, + ): + # Copy of os.environ allows us to replace it for testing + if _environ is not None: + self._environ = _environ + + self.is_jupyter = _is_jupyter() if force_jupyter is None else force_jupyter + if self.is_jupyter: + if width is None: + jupyter_columns = self._environ.get("JUPYTER_COLUMNS") + if jupyter_columns is not None and jupyter_columns.isdigit(): + width = int(jupyter_columns) + else: + width = JUPYTER_DEFAULT_COLUMNS + if height is None: + jupyter_lines = self._environ.get("JUPYTER_LINES") + if jupyter_lines is not None and jupyter_lines.isdigit(): + height = int(jupyter_lines) + else: + height = JUPYTER_DEFAULT_LINES + + self.tab_size = tab_size + self.record = record + self._markup = markup + self._emoji = emoji + self._emoji_variant: Optional[EmojiVariant] = emoji_variant + self._highlight = highlight + self.legacy_windows: bool = ( + (detect_legacy_windows() and not self.is_jupyter) + if legacy_windows is None + else legacy_windows + ) + + if width is None: + columns = self._environ.get("COLUMNS") + if columns is not None and columns.isdigit(): + width = int(columns) - self.legacy_windows + if height is None: + lines = self._environ.get("LINES") + if lines is not None and lines.isdigit(): + height = int(lines) + + self.soft_wrap = soft_wrap + self._width = width + self._height = height + + self._color_system: Optional[ColorSystem] + + self._force_terminal = None + if force_terminal is not None: + self._force_terminal = force_terminal + + self._file = file + self.quiet = quiet + self.stderr = stderr + + if color_system is None: + self._color_system = None + elif color_system == "auto": + self._color_system = self._detect_color_system() + else: + self._color_system = COLOR_SYSTEMS[color_system] + + self._lock = threading.RLock() + self._log_render = LogRender( + show_time=log_time, + show_path=log_path, + time_format=log_time_format, + ) + self.highlighter: HighlighterType = highlighter or _null_highlighter + self.safe_box = safe_box + self.get_datetime = get_datetime or datetime.now + self.get_time = get_time or monotonic + self.style = style + self.no_color = ( + no_color + if no_color is not None + else self._environ.get("NO_COLOR", "") != "" + ) + if force_interactive is None: + tty_interactive = self._environ.get("TTY_INTERACTIVE", None) + if tty_interactive is not None: + if tty_interactive == "0": + force_interactive = False + elif tty_interactive == "1": + force_interactive = True + + self.is_interactive = ( + (self.is_terminal and not self.is_dumb_terminal) + if force_interactive is None + else force_interactive + ) + + self._record_buffer_lock = threading.RLock() + self._thread_locals = ConsoleThreadLocals( + theme_stack=ThemeStack(themes.DEFAULT if theme is None else theme) + ) + self._record_buffer: List[Segment] = [] + self._render_hooks: List[RenderHook] = [] + self._live_stack: List[Live] = [] + self._is_alt_screen = False + + def __repr__(self) -> str: + return f"" + + @property + def file(self) -> IO[str]: + """Get the file object to write to.""" + file = self._file or (sys.stderr if self.stderr else sys.stdout) + file = getattr(file, "rich_proxied_file", file) + if file is None: + file = NULL_FILE + return file + + @file.setter + def file(self, new_file: IO[str]) -> None: + """Set a new file object.""" + self._file = new_file + + @property + def _buffer(self) -> List[Segment]: + """Get a thread local buffer.""" + return self._thread_locals.buffer + + @property + def _buffer_index(self) -> int: + """Get a thread local buffer.""" + return self._thread_locals.buffer_index + + @_buffer_index.setter + def _buffer_index(self, value: int) -> None: + self._thread_locals.buffer_index = value + + @property + def _theme_stack(self) -> ThemeStack: + """Get the thread local theme stack.""" + return self._thread_locals.theme_stack + + def _detect_color_system(self) -> Optional[ColorSystem]: + """Detect color system from env vars.""" + if self.is_jupyter: + return ColorSystem.TRUECOLOR + if not self.is_terminal or self.is_dumb_terminal: + return None + if WINDOWS: # pragma: no cover + if self.legacy_windows: # pragma: no cover + return ColorSystem.WINDOWS + windows_console_features = get_windows_console_features() + return ( + ColorSystem.TRUECOLOR + if windows_console_features.truecolor + else ColorSystem.EIGHT_BIT + ) + else: + color_term = self._environ.get("COLORTERM", "").strip().lower() + if color_term in ("truecolor", "24bit"): + return ColorSystem.TRUECOLOR + term = self._environ.get("TERM", "").strip().lower() + _term_name, _hyphen, colors = term.rpartition("-") + color_system = _TERM_COLORS.get(colors, ColorSystem.STANDARD) + return color_system + + def _enter_buffer(self) -> None: + """Enter in to a buffer context, and buffer all output.""" + self._buffer_index += 1 + + def _exit_buffer(self) -> None: + """Leave buffer context, and render content if required.""" + self._buffer_index -= 1 + self._check_buffer() + + def set_live(self, live: "Live") -> bool: + """Set Live instance. Used by Live context manager (no need to call directly). + + Args: + live (Live): Live instance using this Console. + + Returns: + Boolean that indicates if the live is the topmost of the stack. + + Raises: + errors.LiveError: If this Console has a Live context currently active. + """ + with self._lock: + self._live_stack.append(live) + return len(self._live_stack) == 1 + + def clear_live(self) -> None: + """Clear the Live instance. Used by the Live context manager (no need to call directly).""" + with self._lock: + self._live_stack.pop() + + def push_render_hook(self, hook: RenderHook) -> None: + """Add a new render hook to the stack. + + Args: + hook (RenderHook): Render hook instance. + """ + with self._lock: + self._render_hooks.append(hook) + + def pop_render_hook(self) -> None: + """Pop the last renderhook from the stack.""" + with self._lock: + self._render_hooks.pop() + + def __enter__(self) -> "Console": + """Own context manager to enter buffer context.""" + self._enter_buffer() + return self + + def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: + """Exit buffer context.""" + self._exit_buffer() + + def begin_capture(self) -> None: + """Begin capturing console output. Call :meth:`end_capture` to exit capture mode and return output.""" + self._enter_buffer() + + def end_capture(self) -> str: + """End capture mode and return captured string. + + Returns: + str: Console output. + """ + render_result = self._render_buffer(self._buffer) + del self._buffer[:] + self._exit_buffer() + return render_result + + def push_theme(self, theme: Theme, *, inherit: bool = True) -> None: + """Push a new theme on to the top of the stack, replacing the styles from the previous theme. + Generally speaking, you should call :meth:`~rich.console.Console.use_theme` to get a context manager, rather + than calling this method directly. + + Args: + theme (Theme): A theme instance. + inherit (bool, optional): Inherit existing styles. Defaults to True. + """ + self._theme_stack.push_theme(theme, inherit=inherit) + + def pop_theme(self) -> None: + """Remove theme from top of stack, restoring previous theme.""" + self._theme_stack.pop_theme() + + def use_theme(self, theme: Theme, *, inherit: bool = True) -> ThemeContext: + """Use a different theme for the duration of the context manager. + + Args: + theme (Theme): Theme instance to user. + inherit (bool, optional): Inherit existing console styles. Defaults to True. + + Returns: + ThemeContext: [description] + """ + return ThemeContext(self, theme, inherit) + + @property + def color_system(self) -> Optional[str]: + """Get color system string. + + Returns: + Optional[str]: "standard", "256" or "truecolor". + """ + + if self._color_system is not None: + return _COLOR_SYSTEMS_NAMES[self._color_system] + else: + return None + + @property + def encoding(self) -> str: + """Get the encoding of the console file, e.g. ``"utf-8"``. + + Returns: + str: A standard encoding string. + """ + return (getattr(self.file, "encoding", "utf-8") or "utf-8").lower() + + @property + def is_terminal(self) -> bool: + """Check if the console is writing to a terminal. + + Returns: + bool: True if the console writing to a device capable of + understanding escape sequences, otherwise False. + """ + # If dev has explicitly set this value, return it + if self._force_terminal is not None: + return self._force_terminal + + # Fudge for Idle + if hasattr(sys.stdin, "__module__") and sys.stdin.__module__.startswith( + "idlelib" + ): + # Return False for Idle which claims to be a tty but can't handle ansi codes + return False + + if self.is_jupyter: + # return False for Jupyter, which may have FORCE_COLOR set + return False + + environ = self._environ + + tty_compatible = environ.get("TTY_COMPATIBLE", "") + # 0 indicates device is not tty compatible + if tty_compatible == "0": + return False + # 1 indicates device is tty compatible + if tty_compatible == "1": + return True + + # https://force-color.org/ + force_color = environ.get("FORCE_COLOR") + if force_color is not None: + return force_color != "" + + # Any other value defaults to auto detect + isatty: Optional[Callable[[], bool]] = getattr(self.file, "isatty", None) + try: + return False if isatty is None else isatty() + except ValueError: + # in some situation (at the end of a pytest run for example) isatty() can raise + # ValueError: I/O operation on closed file + # return False because we aren't in a terminal anymore + return False + + @property + def is_dumb_terminal(self) -> bool: + """Detect dumb terminal. + + Returns: + bool: True if writing to a dumb terminal, otherwise False. + + """ + _term = self._environ.get("TERM", "") + is_dumb = _term.lower() in ("dumb", "unknown") + return self.is_terminal and is_dumb + + @property + def options(self) -> ConsoleOptions: + """Get default console options.""" + size = self.size + return ConsoleOptions( + max_height=size.height, + size=size, + legacy_windows=self.legacy_windows, + min_width=1, + max_width=size.width, + encoding=self.encoding, + is_terminal=self.is_terminal, + ) + + @property + def size(self) -> ConsoleDimensions: + """Get the size of the console. + + Returns: + ConsoleDimensions: A named tuple containing the dimensions. + """ + + if self._width is not None and self._height is not None: + return ConsoleDimensions(self._width - self.legacy_windows, self._height) + + if self.is_dumb_terminal: + return ConsoleDimensions(80, 25) + + width: Optional[int] = None + height: Optional[int] = None + + streams = _STD_STREAMS_OUTPUT if WINDOWS else _STD_STREAMS + for file_descriptor in streams: + try: + width, height = os.get_terminal_size(file_descriptor) + except (AttributeError, ValueError, OSError): # Probably not a terminal + pass + else: + break + + columns = self._environ.get("COLUMNS") + if columns is not None and columns.isdigit(): + width = int(columns) + lines = self._environ.get("LINES") + if lines is not None and lines.isdigit(): + height = int(lines) + + # get_terminal_size can report 0, 0 if run from pseudo-terminal + width = width or 80 + height = height or 25 + return ConsoleDimensions( + width - self.legacy_windows if self._width is None else self._width, + height if self._height is None else self._height, + ) + + @size.setter + def size(self, new_size: Tuple[int, int]) -> None: + """Set a new size for the terminal. + + Args: + new_size (Tuple[int, int]): New width and height. + """ + width, height = new_size + self._width = width + self._height = height + + @property + def width(self) -> int: + """Get the width of the console. + + Returns: + int: The width (in characters) of the console. + """ + return self.size.width + + @width.setter + def width(self, width: int) -> None: + """Set width. + + Args: + width (int): New width. + """ + self._width = width + + @property + def height(self) -> int: + """Get the height of the console. + + Returns: + int: The height (in lines) of the console. + """ + return self.size.height + + @height.setter + def height(self, height: int) -> None: + """Set height. + + Args: + height (int): new height. + """ + self._height = height + + def bell(self) -> None: + """Play a 'bell' sound (if supported by the terminal).""" + self.control(Control.bell()) + + def capture(self) -> Capture: + """A context manager to *capture* the result of print() or log() in a string, + rather than writing it to the console. + + Example: + >>> from rich.console import Console + >>> console = Console() + >>> with console.capture() as capture: + ... console.print("[bold magenta]Hello World[/]") + >>> print(capture.get()) + + Returns: + Capture: Context manager with disables writing to the terminal. + """ + capture = Capture(self) + return capture + + def pager( + self, pager: Optional[Pager] = None, styles: bool = False, links: bool = False + ) -> PagerContext: + """A context manager to display anything printed within a "pager". The pager application + is defined by the system and will typically support at least pressing a key to scroll. + + Args: + pager (Pager, optional): A pager object, or None to use :class:`~rich.pager.SystemPager`. Defaults to None. + styles (bool, optional): Show styles in pager. Defaults to False. + links (bool, optional): Show links in pager. Defaults to False. + + Example: + >>> from rich.console import Console + >>> from rich.__main__ import make_test_card + >>> console = Console() + >>> with console.pager(): + console.print(make_test_card()) + + Returns: + PagerContext: A context manager. + """ + return PagerContext(self, pager=pager, styles=styles, links=links) + + def line(self, count: int = 1) -> None: + """Write new line(s). + + Args: + count (int, optional): Number of new lines. Defaults to 1. + """ + + assert count >= 0, "count must be >= 0" + self.print(NewLine(count)) + + def clear(self, home: bool = True) -> None: + """Clear the screen. + + Args: + home (bool, optional): Also move the cursor to 'home' position. Defaults to True. + """ + if home: + self.control(Control.clear(), Control.home()) + else: + self.control(Control.clear()) + + def status( + self, + status: RenderableType, + *, + spinner: str = "dots", + spinner_style: StyleType = "status.spinner", + speed: float = 1.0, + refresh_per_second: float = 12.5, + ) -> "Status": + """Display a status and spinner. + + Args: + status (RenderableType): A status renderable (str or Text typically). + spinner (str, optional): Name of spinner animation (see python -m rich.spinner). Defaults to "dots". + spinner_style (StyleType, optional): Style of spinner. Defaults to "status.spinner". + speed (float, optional): Speed factor for spinner animation. Defaults to 1.0. + refresh_per_second (float, optional): Number of refreshes per second. Defaults to 12.5. + + Returns: + Status: A Status object that may be used as a context manager. + """ + from .status import Status + + status_renderable = Status( + status, + console=self, + spinner=spinner, + spinner_style=spinner_style, + speed=speed, + refresh_per_second=refresh_per_second, + ) + return status_renderable + + def show_cursor(self, show: bool = True) -> bool: + """Show or hide the cursor. + + Args: + show (bool, optional): Set visibility of the cursor. + """ + if self.is_terminal: + self.control(Control.show_cursor(show)) + return True + return False + + def set_alt_screen(self, enable: bool = True) -> bool: + """Enables alternative screen mode. + + Note, if you enable this mode, you should ensure that is disabled before + the application exits. See :meth:`~rich.Console.screen` for a context manager + that handles this for you. + + Args: + enable (bool, optional): Enable (True) or disable (False) alternate screen. Defaults to True. + + Returns: + bool: True if the control codes were written. + + """ + changed = False + if self.is_terminal and not self.legacy_windows: + self.control(Control.alt_screen(enable)) + changed = True + self._is_alt_screen = enable + return changed + + @property + def is_alt_screen(self) -> bool: + """Check if the alt screen was enabled. + + Returns: + bool: True if the alt screen was enabled, otherwise False. + """ + return self._is_alt_screen + + def set_window_title(self, title: str) -> bool: + """Set the title of the console terminal window. + + Warning: There is no means within Rich of "resetting" the window title to its + previous value, meaning the title you set will persist even after your application + exits. + + ``fish`` shell resets the window title before and after each command by default, + negating this issue. Windows Terminal and command prompt will also reset the title for you. + Most other shells and terminals, however, do not do this. + + Some terminals may require configuration changes before you can set the title. + Some terminals may not support setting the title at all. + + Other software (including the terminal itself, the shell, custom prompts, plugins, etc.) + may also set the terminal window title. This could result in whatever value you write + using this method being overwritten. + + Args: + title (str): The new title of the terminal window. + + Returns: + bool: True if the control code to change the terminal title was + written, otherwise False. Note that a return value of True + does not guarantee that the window title has actually changed, + since the feature may be unsupported/disabled in some terminals. + """ + if self.is_terminal: + self.control(Control.title(title)) + return True + return False + + def screen( + self, hide_cursor: bool = True, style: Optional[StyleType] = None + ) -> "ScreenContext": + """Context manager to enable and disable 'alternative screen' mode. + + Args: + hide_cursor (bool, optional): Also hide the cursor. Defaults to False. + style (Style, optional): Optional style for screen. Defaults to None. + + Returns: + ~ScreenContext: Context which enables alternate screen on enter, and disables it on exit. + """ + return ScreenContext(self, hide_cursor=hide_cursor, style=style or "") + + def measure( + self, renderable: RenderableType, *, options: Optional[ConsoleOptions] = None + ) -> Measurement: + """Measure a renderable. Returns a :class:`~rich.measure.Measurement` object which contains + information regarding the number of characters required to print the renderable. + + Args: + renderable (RenderableType): Any renderable or string. + options (Optional[ConsoleOptions], optional): Options to use when measuring, or None + to use default options. Defaults to None. + + Returns: + Measurement: A measurement of the renderable. + """ + measurement = Measurement.get(self, options or self.options, renderable) + return measurement + + def render( + self, renderable: RenderableType, options: Optional[ConsoleOptions] = None + ) -> Iterable[Segment]: + """Render an object in to an iterable of `Segment` instances. + + This method contains the logic for rendering objects with the console protocol. + You are unlikely to need to use it directly, unless you are extending the library. + + Args: + renderable (RenderableType): An object supporting the console protocol, or + an object that may be converted to a string. + options (ConsoleOptions, optional): An options object, or None to use self.options. Defaults to None. + + Returns: + Iterable[Segment]: An iterable of segments that may be rendered. + """ + + _options = options or self.options + if _options.max_width < 1: + # No space to render anything. This prevents potential recursion errors. + return + render_iterable: RenderResult + + renderable = rich_cast(renderable) + if hasattr(renderable, "__rich_console__") and not isclass(renderable): + render_iterable = renderable.__rich_console__(self, _options) + elif isinstance(renderable, str): + text_renderable = self.render_str( + renderable, highlight=_options.highlight, markup=_options.markup + ) + render_iterable = text_renderable.__rich_console__(self, _options) + else: + raise errors.NotRenderableError( + f"Unable to render {renderable!r}; " + "A str, Segment or object with __rich_console__ method is required" + ) + + try: + iter_render = iter(render_iterable) + except TypeError: + raise errors.NotRenderableError( + f"object {render_iterable!r} is not renderable" + ) + _Segment = Segment + _options = _options.reset_height() + for render_output in iter_render: + if isinstance(render_output, _Segment): + yield render_output + else: + yield from self.render(render_output, _options) + + def render_lines( + self, + renderable: RenderableType, + options: Optional[ConsoleOptions] = None, + *, + style: Optional[Style] = None, + pad: bool = True, + new_lines: bool = False, + ) -> List[List[Segment]]: + """Render objects in to a list of lines. + + The output of render_lines is useful when further formatting of rendered console text + is required, such as the Panel class which draws a border around any renderable object. + + Args: + renderable (RenderableType): Any object renderable in the console. + options (Optional[ConsoleOptions], optional): Console options, or None to use self.options. Default to ``None``. + style (Style, optional): Optional style to apply to renderables. Defaults to ``None``. + pad (bool, optional): Pad lines shorter than render width. Defaults to ``True``. + new_lines (bool, optional): Include "\n" characters at end of lines. + + Returns: + List[List[Segment]]: A list of lines, where a line is a list of Segment objects. + """ + with self._lock: + render_options = options or self.options + _rendered = self.render(renderable, render_options) + if style: + _rendered = Segment.apply_style(_rendered, style) + + render_height = render_options.height + if render_height is not None: + render_height = max(0, render_height) + + lines = list( + islice( + Segment.split_and_crop_lines( + _rendered, + render_options.max_width, + include_new_lines=new_lines, + pad=pad, + style=style, + ), + None, + render_height, + ) + ) + if render_options.height is not None: + extra_lines = render_options.height - len(lines) + if extra_lines > 0: + pad_line = [ + ( + [ + Segment(" " * render_options.max_width, style), + Segment("\n"), + ] + if new_lines + else [Segment(" " * render_options.max_width, style)] + ) + ] + lines.extend(pad_line * extra_lines) + + return lines + + def render_str( + self, + text: str, + *, + style: Union[str, Style] = "", + justify: Optional[JustifyMethod] = None, + overflow: Optional[OverflowMethod] = None, + emoji: Optional[bool] = None, + markup: Optional[bool] = None, + highlight: Optional[bool] = None, + highlighter: Optional[HighlighterType] = None, + ) -> "Text": + """Convert a string to a Text instance. This is called automatically if + you print or log a string. + + Args: + text (str): Text to render. + style (Union[str, Style], optional): Style to apply to rendered text. + justify (str, optional): Justify method: "default", "left", "center", "full", or "right". Defaults to ``None``. + overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to ``None``. + emoji (Optional[bool], optional): Enable emoji, or ``None`` to use Console default. + markup (Optional[bool], optional): Enable markup, or ``None`` to use Console default. + highlight (Optional[bool], optional): Enable highlighting, or ``None`` to use Console default. + highlighter (HighlighterType, optional): Optional highlighter to apply. + Returns: + ConsoleRenderable: Renderable object. + + """ + emoji_enabled = emoji or (emoji is None and self._emoji) + markup_enabled = markup or (markup is None and self._markup) + highlight_enabled = highlight or (highlight is None and self._highlight) + + if markup_enabled: + rich_text = render_markup( + text, + style=style, + emoji=emoji_enabled, + emoji_variant=self._emoji_variant, + ) + rich_text.justify = justify + rich_text.overflow = overflow + else: + rich_text = Text( + ( + _emoji_replace(text, default_variant=self._emoji_variant) + if emoji_enabled + else text + ), + justify=justify, + overflow=overflow, + style=style, + ) + + _highlighter = (highlighter or self.highlighter) if highlight_enabled else None + if _highlighter is not None: + highlight_text = _highlighter(str(rich_text)) + highlight_text.copy_styles(rich_text) + return highlight_text + + return rich_text + + def get_style( + self, name: Union[str, Style], *, default: Optional[Union[Style, str]] = None + ) -> Style: + """Get a Style instance by its theme name or parse a definition. + + Args: + name (str): The name of a style or a style definition. + + Returns: + Style: A Style object. + + Raises: + MissingStyle: If no style could be parsed from name. + + """ + if isinstance(name, Style): + return name + + try: + style = self._theme_stack.get(name) + if style is None: + style = Style.parse(name) + return style.copy() if style.link else style + except errors.StyleSyntaxError as error: + if default is not None: + return self.get_style(default) + raise errors.MissingStyle( + f"Failed to get style {name!r}; {error}" + ) from None + + def _collect_renderables( + self, + objects: Iterable[Any], + sep: str, + end: str, + *, + justify: Optional[JustifyMethod] = None, + emoji: Optional[bool] = None, + markup: Optional[bool] = None, + highlight: Optional[bool] = None, + ) -> List[ConsoleRenderable]: + """Combine a number of renderables and text into one renderable. + + Args: + objects (Iterable[Any]): Anything that Rich can render. + sep (str): String to write between print data. + end (str): String to write at end of print data. + justify (str, optional): One of "left", "right", "center", or "full". Defaults to ``None``. + emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. + markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. + highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. + + Returns: + List[ConsoleRenderable]: A list of things to render. + """ + renderables: List[ConsoleRenderable] = [] + _append = renderables.append + text: List[Text] = [] + append_text = text.append + + append = _append + if justify in ("left", "center", "right"): + + def align_append(renderable: RenderableType) -> None: + _append(Align(renderable, cast(AlignMethod, justify))) + + append = align_append + + _highlighter: HighlighterType = _null_highlighter + if highlight or (highlight is None and self._highlight): + _highlighter = self.highlighter + + def check_text() -> None: + if text: + sep_text = Text(sep, justify=justify, end=end) + append(sep_text.join(text)) + text.clear() + + for renderable in objects: + renderable = rich_cast(renderable) + if isinstance(renderable, str): + append_text( + self.render_str( + renderable, + emoji=emoji, + markup=markup, + highlight=highlight, + highlighter=_highlighter, + ) + ) + elif isinstance(renderable, Text): + append_text(renderable) + elif isinstance(renderable, ConsoleRenderable): + check_text() + append(renderable) + elif is_expandable(renderable): + check_text() + append(Pretty(renderable, highlighter=_highlighter)) + else: + append_text(_highlighter(str(renderable))) + + check_text() + + if self.style is not None: + style = self.get_style(self.style) + renderables = [Styled(renderable, style) for renderable in renderables] + + return renderables + + def rule( + self, + title: TextType = "", + *, + characters: str = "─", + style: Union[str, Style] = "rule.line", + align: AlignMethod = "center", + ) -> None: + """Draw a line with optional centered title. + + Args: + title (str, optional): Text to render over the rule. Defaults to "". + characters (str, optional): Character(s) to form the line. Defaults to "─". + style (str, optional): Style of line. Defaults to "rule.line". + align (str, optional): How to align the title, one of "left", "center", or "right". Defaults to "center". + """ + from .rule import Rule + + rule = Rule(title=title, characters=characters, style=style, align=align) + self.print(rule) + + def control(self, *control: Control) -> None: + """Insert non-printing control codes. + + Args: + control_codes (str): Control codes, such as those that may move the cursor. + """ + if not self.is_dumb_terminal: + with self: + self._buffer.extend(_control.segment for _control in control) + + def out( + self, + *objects: Any, + sep: str = " ", + end: str = "\n", + style: Optional[Union[str, Style]] = None, + highlight: Optional[bool] = None, + ) -> None: + """Output to the terminal. This is a low-level way of writing to the terminal which unlike + :meth:`~rich.console.Console.print` won't pretty print, wrap text, or apply markup, but will + optionally apply highlighting and a basic style. + + Args: + sep (str, optional): String to write between print data. Defaults to " ". + end (str, optional): String to write at end of print data. Defaults to "\\\\n". + style (Union[str, Style], optional): A style to apply to output. Defaults to None. + highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use + console default. Defaults to ``None``. + """ + raw_output: str = sep.join(str(_object) for _object in objects) + self.print( + raw_output, + style=style, + highlight=highlight, + emoji=False, + markup=False, + no_wrap=True, + overflow="ignore", + crop=False, + end=end, + ) + + def print( + self, + *objects: Any, + sep: str = " ", + end: str = "\n", + style: Optional[Union[str, Style]] = None, + justify: Optional[JustifyMethod] = None, + overflow: Optional[OverflowMethod] = None, + no_wrap: Optional[bool] = None, + emoji: Optional[bool] = None, + markup: Optional[bool] = None, + highlight: Optional[bool] = None, + width: Optional[int] = None, + height: Optional[int] = None, + crop: bool = True, + soft_wrap: Optional[bool] = None, + new_line_start: bool = False, + ) -> None: + """Print to the console. + + Args: + objects (positional args): Objects to log to the terminal. + sep (str, optional): String to write between print data. Defaults to " ". + end (str, optional): String to write at end of print data. Defaults to "\\\\n". + style (Union[str, Style], optional): A style to apply to output. Defaults to None. + justify (str, optional): Justify method: "default", "left", "right", "center", or "full". Defaults to ``None``. + overflow (str, optional): Overflow method: "ignore", "crop", "fold", or "ellipsis". Defaults to None. + no_wrap (Optional[bool], optional): Disable word wrapping. Defaults to None. + emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to ``None``. + markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to ``None``. + highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to ``None``. + width (Optional[int], optional): Width of output, or ``None`` to auto-detect. Defaults to ``None``. + crop (Optional[bool], optional): Crop output to width of terminal. Defaults to True. + soft_wrap (bool, optional): Enable soft wrap mode which disables word wrapping and cropping of text or ``None`` for + Console default. Defaults to ``None``. + new_line_start (bool, False): Insert a new line at the start if the output contains more than one line. Defaults to ``False``. + """ + if not objects: + objects = (NewLine(),) + + if soft_wrap is None: + soft_wrap = self.soft_wrap + if soft_wrap: + if no_wrap is None: + no_wrap = True + if overflow is None: + overflow = "ignore" + crop = False + render_hooks = self._render_hooks[:] + with self: + renderables = self._collect_renderables( + objects, + sep, + end, + justify=justify, + emoji=emoji, + markup=markup, + highlight=highlight, + ) + for hook in render_hooks: + renderables = hook.process_renderables(renderables) + render_options = self.options.update( + justify=justify, + overflow=overflow, + width=min(width, self.width) if width is not None else NO_CHANGE, + height=height, + no_wrap=no_wrap, + markup=markup, + highlight=highlight, + ) + + new_segments: List[Segment] = [] + extend = new_segments.extend + render = self.render + if style is None: + for renderable in renderables: + extend(render(renderable, render_options)) + else: + for renderable in renderables: + extend( + Segment.apply_style( + render(renderable, render_options), self.get_style(style) + ) + ) + if new_line_start: + if ( + len("".join(segment.text for segment in new_segments).splitlines()) + > 1 + ): + new_segments.insert(0, Segment.line()) + if crop: + buffer_extend = self._buffer.extend + for line in Segment.split_and_crop_lines( + new_segments, self.width, pad=False + ): + buffer_extend(line) + else: + self._buffer.extend(new_segments) + + def print_json( + self, + json: Optional[str] = None, + *, + data: Any = None, + indent: Union[None, int, str] = 2, + highlight: bool = True, + skip_keys: bool = False, + ensure_ascii: bool = False, + check_circular: bool = True, + allow_nan: bool = True, + default: Optional[Callable[[Any], Any]] = None, + sort_keys: bool = False, + ) -> None: + """Pretty prints JSON. Output will be valid JSON. + + Args: + json (Optional[str]): A string containing JSON. + data (Any): If json is not supplied, then encode this data. + indent (Union[None, int, str], optional): Number of spaces to indent. Defaults to 2. + highlight (bool, optional): Enable highlighting of output: Defaults to True. + skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False. + ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False. + check_circular (bool, optional): Check for circular references. Defaults to True. + allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True. + default (Callable, optional): A callable that converts values that can not be encoded + in to something that can be JSON encoded. Defaults to None. + sort_keys (bool, optional): Sort dictionary keys. Defaults to False. + """ + from pip._vendor.rich.json import JSON + + if json is None: + json_renderable = JSON.from_data( + data, + indent=indent, + highlight=highlight, + skip_keys=skip_keys, + ensure_ascii=ensure_ascii, + check_circular=check_circular, + allow_nan=allow_nan, + default=default, + sort_keys=sort_keys, + ) + else: + if not isinstance(json, str): + raise TypeError( + f"json must be str. Did you mean print_json(data={json!r}) ?" + ) + json_renderable = JSON( + json, + indent=indent, + highlight=highlight, + skip_keys=skip_keys, + ensure_ascii=ensure_ascii, + check_circular=check_circular, + allow_nan=allow_nan, + default=default, + sort_keys=sort_keys, + ) + self.print(json_renderable, soft_wrap=True) + + def update_screen( + self, + renderable: RenderableType, + *, + region: Optional[Region] = None, + options: Optional[ConsoleOptions] = None, + ) -> None: + """Update the screen at a given offset. + + Args: + renderable (RenderableType): A Rich renderable. + region (Region, optional): Region of screen to update, or None for entire screen. Defaults to None. + x (int, optional): x offset. Defaults to 0. + y (int, optional): y offset. Defaults to 0. + + Raises: + errors.NoAltScreen: If the Console isn't in alt screen mode. + + """ + if not self.is_alt_screen: + raise errors.NoAltScreen("Alt screen must be enabled to call update_screen") + render_options = options or self.options + if region is None: + x = y = 0 + render_options = render_options.update_dimensions( + render_options.max_width, render_options.height or self.height + ) + else: + x, y, width, height = region + render_options = render_options.update_dimensions(width, height) + + lines = self.render_lines(renderable, options=render_options) + self.update_screen_lines(lines, x, y) + + def update_screen_lines( + self, lines: List[List[Segment]], x: int = 0, y: int = 0 + ) -> None: + """Update lines of the screen at a given offset. + + Args: + lines (List[List[Segment]]): Rendered lines (as produced by :meth:`~rich.Console.render_lines`). + x (int, optional): x offset (column no). Defaults to 0. + y (int, optional): y offset (column no). Defaults to 0. + + Raises: + errors.NoAltScreen: If the Console isn't in alt screen mode. + """ + if not self.is_alt_screen: + raise errors.NoAltScreen("Alt screen must be enabled to call update_screen") + screen_update = ScreenUpdate(lines, x, y) + segments = self.render(screen_update) + self._buffer.extend(segments) + self._check_buffer() + + def print_exception( + self, + *, + width: Optional[int] = 100, + extra_lines: int = 3, + theme: Optional[str] = None, + word_wrap: bool = False, + show_locals: bool = False, + suppress: Iterable[Union[str, ModuleType]] = (), + max_frames: int = 100, + ) -> None: + """Prints a rich render of the last exception and traceback. + + Args: + width (Optional[int], optional): Number of characters used to render code. Defaults to 100. + extra_lines (int, optional): Additional lines of code to render. Defaults to 3. + theme (str, optional): Override pygments theme used in traceback + word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False. + show_locals (bool, optional): Enable display of local variables. Defaults to False. + suppress (Iterable[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback. + max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100. + """ + from .traceback import Traceback + + traceback = Traceback( + width=width, + extra_lines=extra_lines, + theme=theme, + word_wrap=word_wrap, + show_locals=show_locals, + suppress=suppress, + max_frames=max_frames, + ) + self.print(traceback) + + @staticmethod + def _caller_frame_info( + offset: int, + currentframe: Callable[[], Optional[FrameType]] = inspect.currentframe, + ) -> Tuple[str, int, Dict[str, Any]]: + """Get caller frame information. + + Args: + offset (int): the caller offset within the current frame stack. + currentframe (Callable[[], Optional[FrameType]], optional): the callable to use to + retrieve the current frame. Defaults to ``inspect.currentframe``. + + Returns: + Tuple[str, int, Dict[str, Any]]: A tuple containing the filename, the line number and + the dictionary of local variables associated with the caller frame. + + Raises: + RuntimeError: If the stack offset is invalid. + """ + # Ignore the frame of this local helper + offset += 1 + + frame = currentframe() + if frame is not None: + # Use the faster currentframe where implemented + while offset and frame is not None: + frame = frame.f_back + offset -= 1 + assert frame is not None + return frame.f_code.co_filename, frame.f_lineno, frame.f_locals + else: + # Fallback to the slower stack + frame_info = inspect.stack()[offset] + return frame_info.filename, frame_info.lineno, frame_info.frame.f_locals + + def log( + self, + *objects: Any, + sep: str = " ", + end: str = "\n", + style: Optional[Union[str, Style]] = None, + justify: Optional[JustifyMethod] = None, + emoji: Optional[bool] = None, + markup: Optional[bool] = None, + highlight: Optional[bool] = None, + log_locals: bool = False, + _stack_offset: int = 1, + ) -> None: + """Log rich content to the terminal. + + Args: + objects (positional args): Objects to log to the terminal. + sep (str, optional): String to write between print data. Defaults to " ". + end (str, optional): String to write at end of print data. Defaults to "\\\\n". + style (Union[str, Style], optional): A style to apply to output. Defaults to None. + justify (str, optional): One of "left", "right", "center", or "full". Defaults to ``None``. + emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to None. + markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to None. + highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to None. + log_locals (bool, optional): Boolean to enable logging of locals where ``log()`` + was called. Defaults to False. + _stack_offset (int, optional): Offset of caller from end of call stack. Defaults to 1. + """ + if not objects: + objects = (NewLine(),) + + render_hooks = self._render_hooks[:] + + with self: + renderables = self._collect_renderables( + objects, + sep, + end, + justify=justify, + emoji=emoji, + markup=markup, + highlight=highlight, + ) + if style is not None: + renderables = [Styled(renderable, style) for renderable in renderables] + + filename, line_no, locals = self._caller_frame_info(_stack_offset) + link_path = None if filename.startswith("<") else os.path.abspath(filename) + path = filename.rpartition(os.sep)[-1] + if log_locals: + locals_map = { + key: value + for key, value in locals.items() + if not key.startswith("__") + } + renderables.append(render_scope(locals_map, title="[i]locals")) + + renderables = [ + self._log_render( + self, + renderables, + log_time=self.get_datetime(), + path=path, + line_no=line_no, + link_path=link_path, + ) + ] + for hook in render_hooks: + renderables = hook.process_renderables(renderables) + new_segments: List[Segment] = [] + extend = new_segments.extend + render = self.render + render_options = self.options + for renderable in renderables: + extend(render(renderable, render_options)) + buffer_extend = self._buffer.extend + for line in Segment.split_and_crop_lines( + new_segments, self.width, pad=False + ): + buffer_extend(line) + + def on_broken_pipe(self) -> None: + """This function is called when a `BrokenPipeError` is raised. + + This can occur when piping Textual output in Linux and macOS. + The default implementation is to exit the app, but you could implement + this method in a subclass to change the behavior. + + See https://docs.python.org/3/library/signal.html#note-on-sigpipe for details. + """ + self.quiet = True + devnull = os.open(os.devnull, os.O_WRONLY) + os.dup2(devnull, sys.stdout.fileno()) + raise SystemExit(1) + + def _check_buffer(self) -> None: + """Check if the buffer may be rendered. Render it if it can (e.g. Console.quiet is False) + Rendering is supported on Windows, Unix and Jupyter environments. For + legacy Windows consoles, the win32 API is called directly. + This method will also record what it renders if recording is enabled via Console.record. + """ + if self.quiet: + del self._buffer[:] + return + + try: + self._write_buffer() + except BrokenPipeError: + self.on_broken_pipe() + + def _write_buffer(self) -> None: + """Write the buffer to the output file.""" + + with self._lock: + if self.record and not self._buffer_index: + with self._record_buffer_lock: + self._record_buffer.extend(self._buffer[:]) + + if self._buffer_index == 0: + if self.is_jupyter: # pragma: no cover + from .jupyter import display + + display(self._buffer, self._render_buffer(self._buffer[:])) + del self._buffer[:] + else: + if WINDOWS: + use_legacy_windows_render = False + if self.legacy_windows: + fileno = get_fileno(self.file) + if fileno is not None: + use_legacy_windows_render = ( + fileno in _STD_STREAMS_OUTPUT + ) + + if use_legacy_windows_render: + from pip._vendor.rich._win32_console import LegacyWindowsTerm + from pip._vendor.rich._windows_renderer import legacy_windows_render + + buffer = self._buffer[:] + if self.no_color and self._color_system: + buffer = list(Segment.remove_color(buffer)) + + legacy_windows_render(buffer, LegacyWindowsTerm(self.file)) + else: + # Either a non-std stream on legacy Windows, or modern Windows. + text = self._render_buffer(self._buffer[:]) + # https://bugs.python.org/issue37871 + # https://github.com/python/cpython/issues/82052 + # We need to avoid writing more than 32Kb in a single write, due to the above bug + write = self.file.write + # Worse case scenario, every character is 4 bytes of utf-8 + MAX_WRITE = 32 * 1024 // 4 + try: + if len(text) <= MAX_WRITE: + write(text) + else: + batch: List[str] = [] + batch_append = batch.append + size = 0 + for line in text.splitlines(True): + if size + len(line) > MAX_WRITE and batch: + write("".join(batch)) + batch.clear() + size = 0 + batch_append(line) + size += len(line) + if batch: + write("".join(batch)) + batch.clear() + except UnicodeEncodeError as error: + error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***" + raise + else: + text = self._render_buffer(self._buffer[:]) + try: + self.file.write(text) + except UnicodeEncodeError as error: + error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***" + raise + + self.file.flush() + del self._buffer[:] + + def _render_buffer(self, buffer: Iterable[Segment]) -> str: + """Render buffered output, and clear buffer.""" + output: List[str] = [] + append = output.append + color_system = self._color_system + legacy_windows = self.legacy_windows + not_terminal = not self.is_terminal + if self.no_color and color_system: + buffer = Segment.remove_color(buffer) + for text, style, control in buffer: + if style: + append( + style.render( + text, + color_system=color_system, + legacy_windows=legacy_windows, + ) + ) + elif not (not_terminal and control): + append(text) + + rendered = "".join(output) + return rendered + + def input( + self, + prompt: TextType = "", + *, + markup: bool = True, + emoji: bool = True, + password: bool = False, + stream: Optional[TextIO] = None, + ) -> str: + """Displays a prompt and waits for input from the user. The prompt may contain color / style. + + It works in the same way as Python's builtin :func:`input` function and provides elaborate line editing and history features if Python's builtin :mod:`readline` module is previously loaded. + + Args: + prompt (Union[str, Text]): Text to render in the prompt. + markup (bool, optional): Enable console markup (requires a str prompt). Defaults to True. + emoji (bool, optional): Enable emoji (requires a str prompt). Defaults to True. + password: (bool, optional): Hide typed text. Defaults to False. + stream: (TextIO, optional): Optional file to read input from (rather than stdin). Defaults to None. + + Returns: + str: Text read from stdin. + """ + if prompt: + self.print(prompt, markup=markup, emoji=emoji, end="") + if password: + result = getpass("", stream=stream) + else: + if stream: + result = stream.readline() + else: + result = input() + return result + + def export_text(self, *, clear: bool = True, styles: bool = False) -> str: + """Generate text from console contents (requires record=True argument in constructor). + + Args: + clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``. + styles (bool, optional): If ``True``, ansi escape codes will be included. ``False`` for plain text. + Defaults to ``False``. + + Returns: + str: String containing console contents. + + """ + assert ( + self.record + ), "To export console contents set record=True in the constructor or instance" + + with self._record_buffer_lock: + if styles: + text = "".join( + (style.render(text) if style else text) + for text, style, _ in self._record_buffer + ) + else: + text = "".join( + segment.text + for segment in self._record_buffer + if not segment.control + ) + if clear: + del self._record_buffer[:] + return text + + def save_text(self, path: str, *, clear: bool = True, styles: bool = False) -> None: + """Generate text from console and save to a given location (requires record=True argument in constructor). + + Args: + path (str): Path to write text files. + clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``. + styles (bool, optional): If ``True``, ansi style codes will be included. ``False`` for plain text. + Defaults to ``False``. + + """ + text = self.export_text(clear=clear, styles=styles) + with open(path, "w", encoding="utf-8") as write_file: + write_file.write(text) + + def export_html( + self, + *, + theme: Optional[TerminalTheme] = None, + clear: bool = True, + code_format: Optional[str] = None, + inline_styles: bool = False, + ) -> str: + """Generate HTML from console contents (requires record=True argument in constructor). + + Args: + theme (TerminalTheme, optional): TerminalTheme object containing console colors. + clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``. + code_format (str, optional): Format string to render HTML. In addition to '{foreground}', + '{background}', and '{code}', should contain '{stylesheet}' if inline_styles is ``False``. + inline_styles (bool, optional): If ``True`` styles will be inlined in to spans, which makes files + larger but easier to cut and paste markup. If ``False``, styles will be embedded in a style tag. + Defaults to False. + + Returns: + str: String containing console contents as HTML. + """ + assert ( + self.record + ), "To export console contents set record=True in the constructor or instance" + fragments: List[str] = [] + append = fragments.append + _theme = theme or DEFAULT_TERMINAL_THEME + stylesheet = "" + + render_code_format = CONSOLE_HTML_FORMAT if code_format is None else code_format + + with self._record_buffer_lock: + if inline_styles: + for text, style, _ in Segment.filter_control( + Segment.simplify(self._record_buffer) + ): + text = escape(text) + if style: + rule = style.get_html_style(_theme) + if style.link: + text = f'{text}' + text = f'{text}' if rule else text + append(text) + else: + styles: Dict[str, int] = {} + for text, style, _ in Segment.filter_control( + Segment.simplify(self._record_buffer) + ): + text = escape(text) + if style: + rule = style.get_html_style(_theme) + style_number = styles.setdefault(rule, len(styles) + 1) + if style.link: + text = f'{text}' + else: + text = f'{text}' + append(text) + stylesheet_rules: List[str] = [] + stylesheet_append = stylesheet_rules.append + for style_rule, style_number in styles.items(): + if style_rule: + stylesheet_append(f".r{style_number} {{{style_rule}}}") + stylesheet = "\n".join(stylesheet_rules) + + rendered_code = render_code_format.format( + code="".join(fragments), + stylesheet=stylesheet, + foreground=_theme.foreground_color.hex, + background=_theme.background_color.hex, + ) + if clear: + del self._record_buffer[:] + return rendered_code + + def save_html( + self, + path: str, + *, + theme: Optional[TerminalTheme] = None, + clear: bool = True, + code_format: str = CONSOLE_HTML_FORMAT, + inline_styles: bool = False, + ) -> None: + """Generate HTML from console contents and write to a file (requires record=True argument in constructor). + + Args: + path (str): Path to write html file. + theme (TerminalTheme, optional): TerminalTheme object containing console colors. + clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``. + code_format (str, optional): Format string to render HTML. In addition to '{foreground}', + '{background}', and '{code}', should contain '{stylesheet}' if inline_styles is ``False``. + inline_styles (bool, optional): If ``True`` styles will be inlined in to spans, which makes files + larger but easier to cut and paste markup. If ``False``, styles will be embedded in a style tag. + Defaults to False. + + """ + html = self.export_html( + theme=theme, + clear=clear, + code_format=code_format, + inline_styles=inline_styles, + ) + with open(path, "w", encoding="utf-8") as write_file: + write_file.write(html) + + def export_svg( + self, + *, + title: str = "Rich", + theme: Optional[TerminalTheme] = None, + clear: bool = True, + code_format: str = CONSOLE_SVG_FORMAT, + font_aspect_ratio: float = 0.61, + unique_id: Optional[str] = None, + ) -> str: + """ + Generate an SVG from the console contents (requires record=True in Console constructor). + + Args: + title (str, optional): The title of the tab in the output image + theme (TerminalTheme, optional): The ``TerminalTheme`` object to use to style the terminal + clear (bool, optional): Clear record buffer after exporting. Defaults to ``True`` + code_format (str, optional): Format string used to generate the SVG. Rich will inject a number of variables + into the string in order to form the final SVG output. The default template used and the variables + injected by Rich can be found by inspecting the ``console.CONSOLE_SVG_FORMAT`` variable. + font_aspect_ratio (float, optional): The width to height ratio of the font used in the ``code_format`` + string. Defaults to 0.61, which is the width to height ratio of Fira Code (the default font). + If you aren't specifying a different font inside ``code_format``, you probably don't need this. + unique_id (str, optional): unique id that is used as the prefix for various elements (CSS styles, node + ids). If not set, this defaults to a computed value based on the recorded content. + """ + + from pip._vendor.rich.cells import cell_len + + style_cache: Dict[Style, str] = {} + + def get_svg_style(style: Style) -> str: + """Convert a Style to CSS rules for SVG.""" + if style in style_cache: + return style_cache[style] + css_rules = [] + color = ( + _theme.foreground_color + if (style.color is None or style.color.is_default) + else style.color.get_truecolor(_theme) + ) + bgcolor = ( + _theme.background_color + if (style.bgcolor is None or style.bgcolor.is_default) + else style.bgcolor.get_truecolor(_theme) + ) + if style.reverse: + color, bgcolor = bgcolor, color + if style.dim: + color = blend_rgb(color, bgcolor, 0.4) + css_rules.append(f"fill: {color.hex}") + if style.bold: + css_rules.append("font-weight: bold") + if style.italic: + css_rules.append("font-style: italic;") + if style.underline: + css_rules.append("text-decoration: underline;") + if style.strike: + css_rules.append("text-decoration: line-through;") + + css = ";".join(css_rules) + style_cache[style] = css + return css + + _theme = theme or SVG_EXPORT_THEME + + width = self.width + char_height = 20 + char_width = char_height * font_aspect_ratio + line_height = char_height * 1.22 + + margin_top = 1 + margin_right = 1 + margin_bottom = 1 + margin_left = 1 + + padding_top = 40 + padding_right = 8 + padding_bottom = 8 + padding_left = 8 + + padding_width = padding_left + padding_right + padding_height = padding_top + padding_bottom + margin_width = margin_left + margin_right + margin_height = margin_top + margin_bottom + + text_backgrounds: List[str] = [] + text_group: List[str] = [] + classes: Dict[str, int] = {} + style_no = 1 + + def escape_text(text: str) -> str: + """HTML escape text and replace spaces with nbsp.""" + return escape(text).replace(" ", " ") + + def make_tag( + name: str, content: Optional[str] = None, **attribs: object + ) -> str: + """Make a tag from name, content, and attributes.""" + + def stringify(value: object) -> str: + if isinstance(value, (float)): + return format(value, "g") + return str(value) + + tag_attribs = " ".join( + f'{k.lstrip("_").replace("_", "-")}="{stringify(v)}"' + for k, v in attribs.items() + ) + return ( + f"<{name} {tag_attribs}>{content}" + if content + else f"<{name} {tag_attribs}/>" + ) + + with self._record_buffer_lock: + segments = list(Segment.filter_control(self._record_buffer)) + if clear: + self._record_buffer.clear() + + if unique_id is None: + unique_id = "terminal-" + str( + zlib.adler32( + ("".join(repr(segment) for segment in segments)).encode( + "utf-8", + "ignore", + ) + + title.encode("utf-8", "ignore") + ) + ) + y = 0 + for y, line in enumerate(Segment.split_and_crop_lines(segments, length=width)): + x = 0 + for text, style, _control in line: + style = style or Style() + rules = get_svg_style(style) + if rules not in classes: + classes[rules] = style_no + style_no += 1 + class_name = f"r{classes[rules]}" + + if style.reverse: + has_background = True + background = ( + _theme.foreground_color.hex + if style.color is None + else style.color.get_truecolor(_theme).hex + ) + else: + bgcolor = style.bgcolor + has_background = bgcolor is not None and not bgcolor.is_default + background = ( + _theme.background_color.hex + if style.bgcolor is None + else style.bgcolor.get_truecolor(_theme).hex + ) + + text_length = cell_len(text) + if has_background: + text_backgrounds.append( + make_tag( + "rect", + fill=background, + x=x * char_width, + y=y * line_height + 1.5, + width=char_width * text_length, + height=line_height + 0.25, + shape_rendering="crispEdges", + ) + ) + + if text != " " * len(text): + text_group.append( + make_tag( + "text", + escape_text(text), + _class=f"{unique_id}-{class_name}", + x=x * char_width, + y=y * line_height + char_height, + textLength=char_width * len(text), + clip_path=f"url(#{unique_id}-line-{y})", + ) + ) + x += cell_len(text) + + line_offsets = [line_no * line_height + 1.5 for line_no in range(y)] + lines = "\n".join( + f""" + {make_tag("rect", x=0, y=offset, width=char_width * width, height=line_height + 0.25)} + """ + for line_no, offset in enumerate(line_offsets) + ) + + styles = "\n".join( + f".{unique_id}-r{rule_no} {{ {css} }}" for css, rule_no in classes.items() + ) + backgrounds = "".join(text_backgrounds) + matrix = "".join(text_group) + + terminal_width = ceil(width * char_width + padding_width) + terminal_height = (y + 1) * line_height + padding_height + chrome = make_tag( + "rect", + fill=_theme.background_color.hex, + stroke="rgba(255,255,255,0.35)", + stroke_width="1", + x=margin_left, + y=margin_top, + width=terminal_width, + height=terminal_height, + rx=8, + ) + + title_color = _theme.foreground_color.hex + if title: + chrome += make_tag( + "text", + escape_text(title), + _class=f"{unique_id}-title", + fill=title_color, + text_anchor="middle", + x=terminal_width // 2, + y=margin_top + char_height + 6, + ) + chrome += f""" + + + + + + """ + + svg = code_format.format( + unique_id=unique_id, + char_width=char_width, + char_height=char_height, + line_height=line_height, + terminal_width=char_width * width - 1, + terminal_height=(y + 1) * line_height - 1, + width=terminal_width + margin_width, + height=terminal_height + margin_height, + terminal_x=margin_left + padding_left, + terminal_y=margin_top + padding_top, + styles=styles, + chrome=chrome, + backgrounds=backgrounds, + matrix=matrix, + lines=lines, + ) + return svg + + def save_svg( + self, + path: str, + *, + title: str = "Rich", + theme: Optional[TerminalTheme] = None, + clear: bool = True, + code_format: str = CONSOLE_SVG_FORMAT, + font_aspect_ratio: float = 0.61, + unique_id: Optional[str] = None, + ) -> None: + """Generate an SVG file from the console contents (requires record=True in Console constructor). + + Args: + path (str): The path to write the SVG to. + title (str, optional): The title of the tab in the output image + theme (TerminalTheme, optional): The ``TerminalTheme`` object to use to style the terminal + clear (bool, optional): Clear record buffer after exporting. Defaults to ``True`` + code_format (str, optional): Format string used to generate the SVG. Rich will inject a number of variables + into the string in order to form the final SVG output. The default template used and the variables + injected by Rich can be found by inspecting the ``console.CONSOLE_SVG_FORMAT`` variable. + font_aspect_ratio (float, optional): The width to height ratio of the font used in the ``code_format`` + string. Defaults to 0.61, which is the width to height ratio of Fira Code (the default font). + If you aren't specifying a different font inside ``code_format``, you probably don't need this. + unique_id (str, optional): unique id that is used as the prefix for various elements (CSS styles, node + ids). If not set, this defaults to a computed value based on the recorded content. + """ + svg = self.export_svg( + title=title, + theme=theme, + clear=clear, + code_format=code_format, + font_aspect_ratio=font_aspect_ratio, + unique_id=unique_id, + ) + with open(path, "w", encoding="utf-8") as write_file: + write_file.write(svg) + + +def _svg_hash(svg_main_code: str) -> str: + """Returns a unique hash for the given SVG main code. + + Args: + svg_main_code (str): The content we're going to inject in the SVG envelope. + + Returns: + str: a hash of the given content + """ + return str(zlib.adler32(svg_main_code.encode())) + + +if __name__ == "__main__": # pragma: no cover + console = Console(record=True) + + console.log( + "JSONRPC [i]request[/i]", + 5, + 1.3, + True, + False, + None, + { + "jsonrpc": "2.0", + "method": "subtract", + "params": {"minuend": 42, "subtrahend": 23}, + "id": 3, + }, + ) + + console.log("Hello, World!", "{'a': 1}", repr(console)) + + console.print( + { + "name": None, + "empty": [], + "quiz": { + "sport": { + "answered": True, + "q1": { + "question": "Which one is correct team name in NBA?", + "options": [ + "New York Bulls", + "Los Angeles Kings", + "Golden State Warriors", + "Huston Rocket", + ], + "answer": "Huston Rocket", + }, + }, + "maths": { + "answered": False, + "q1": { + "question": "5 + 7 = ?", + "options": [10, 11, 12, 13], + "answer": 12, + }, + "q2": { + "question": "12 - 8 = ?", + "options": [1, 2, 3, 4], + "answer": 4, + }, + }, + }, + } + ) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/constrain.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/constrain.py new file mode 100644 index 0000000..65fdf56 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/constrain.py @@ -0,0 +1,37 @@ +from typing import Optional, TYPE_CHECKING + +from .jupyter import JupyterMixin +from .measure import Measurement + +if TYPE_CHECKING: + from .console import Console, ConsoleOptions, RenderableType, RenderResult + + +class Constrain(JupyterMixin): + """Constrain the width of a renderable to a given number of characters. + + Args: + renderable (RenderableType): A renderable object. + width (int, optional): The maximum width (in characters) to render. Defaults to 80. + """ + + def __init__(self, renderable: "RenderableType", width: Optional[int] = 80) -> None: + self.renderable = renderable + self.width = width + + def __rich_console__( + self, console: "Console", options: "ConsoleOptions" + ) -> "RenderResult": + if self.width is None: + yield self.renderable + else: + child_options = options.update_width(min(self.width, options.max_width)) + yield from console.render(self.renderable, child_options) + + def __rich_measure__( + self, console: "Console", options: "ConsoleOptions" + ) -> "Measurement": + if self.width is not None: + options = options.update_width(self.width) + measurement = Measurement.get(console, options, self.renderable) + return measurement diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/containers.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/containers.py new file mode 100644 index 0000000..901ff8b --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/containers.py @@ -0,0 +1,167 @@ +from itertools import zip_longest +from typing import ( + TYPE_CHECKING, + Iterable, + Iterator, + List, + Optional, + TypeVar, + Union, + overload, +) + +if TYPE_CHECKING: + from .console import ( + Console, + ConsoleOptions, + JustifyMethod, + OverflowMethod, + RenderResult, + RenderableType, + ) + from .text import Text + +from .cells import cell_len +from .measure import Measurement + +T = TypeVar("T") + + +class Renderables: + """A list subclass which renders its contents to the console.""" + + def __init__( + self, renderables: Optional[Iterable["RenderableType"]] = None + ) -> None: + self._renderables: List["RenderableType"] = ( + list(renderables) if renderables is not None else [] + ) + + def __rich_console__( + self, console: "Console", options: "ConsoleOptions" + ) -> "RenderResult": + """Console render method to insert line-breaks.""" + yield from self._renderables + + def __rich_measure__( + self, console: "Console", options: "ConsoleOptions" + ) -> "Measurement": + dimensions = [ + Measurement.get(console, options, renderable) + for renderable in self._renderables + ] + if not dimensions: + return Measurement(1, 1) + _min = max(dimension.minimum for dimension in dimensions) + _max = max(dimension.maximum for dimension in dimensions) + return Measurement(_min, _max) + + def append(self, renderable: "RenderableType") -> None: + self._renderables.append(renderable) + + def __iter__(self) -> Iterable["RenderableType"]: + return iter(self._renderables) + + +class Lines: + """A list subclass which can render to the console.""" + + def __init__(self, lines: Iterable["Text"] = ()) -> None: + self._lines: List["Text"] = list(lines) + + def __repr__(self) -> str: + return f"Lines({self._lines!r})" + + def __iter__(self) -> Iterator["Text"]: + return iter(self._lines) + + @overload + def __getitem__(self, index: int) -> "Text": + ... + + @overload + def __getitem__(self, index: slice) -> List["Text"]: + ... + + def __getitem__(self, index: Union[slice, int]) -> Union["Text", List["Text"]]: + return self._lines[index] + + def __setitem__(self, index: int, value: "Text") -> "Lines": + self._lines[index] = value + return self + + def __len__(self) -> int: + return self._lines.__len__() + + def __rich_console__( + self, console: "Console", options: "ConsoleOptions" + ) -> "RenderResult": + """Console render method to insert line-breaks.""" + yield from self._lines + + def append(self, line: "Text") -> None: + self._lines.append(line) + + def extend(self, lines: Iterable["Text"]) -> None: + self._lines.extend(lines) + + def pop(self, index: int = -1) -> "Text": + return self._lines.pop(index) + + def justify( + self, + console: "Console", + width: int, + justify: "JustifyMethod" = "left", + overflow: "OverflowMethod" = "fold", + ) -> None: + """Justify and overflow text to a given width. + + Args: + console (Console): Console instance. + width (int): Number of cells available per line. + justify (str, optional): Default justify method for text: "left", "center", "full" or "right". Defaults to "left". + overflow (str, optional): Default overflow for text: "crop", "fold", or "ellipsis". Defaults to "fold". + + """ + from .text import Text + + if justify == "left": + for line in self._lines: + line.truncate(width, overflow=overflow, pad=True) + elif justify == "center": + for line in self._lines: + line.rstrip() + line.truncate(width, overflow=overflow) + line.pad_left((width - cell_len(line.plain)) // 2) + line.pad_right(width - cell_len(line.plain)) + elif justify == "right": + for line in self._lines: + line.rstrip() + line.truncate(width, overflow=overflow) + line.pad_left(width - cell_len(line.plain)) + elif justify == "full": + for line_index, line in enumerate(self._lines): + if line_index == len(self._lines) - 1: + break + words = line.split(" ") + words_size = sum(cell_len(word.plain) for word in words) + num_spaces = len(words) - 1 + spaces = [1 for _ in range(num_spaces)] + index = 0 + if spaces: + while words_size + num_spaces < width: + spaces[len(spaces) - index - 1] += 1 + num_spaces += 1 + index = (index + 1) % len(spaces) + tokens: List[Text] = [] + for index, (word, next_word) in enumerate( + zip_longest(words, words[1:]) + ): + tokens.append(word) + if index < len(spaces): + style = word.get_style_at_offset(console, -1) + next_style = next_word.get_style_at_offset(console, 0) + space_style = style if style == next_style else line.style + tokens.append(Text(" " * spaces[index], style=space_style)) + self[line_index] = Text("").join(tokens) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/control.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/control.py new file mode 100644 index 0000000..84963e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/control.py @@ -0,0 +1,219 @@ +import time +from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Union, Final + +from .segment import ControlCode, ControlType, Segment + +if TYPE_CHECKING: + from .console import Console, ConsoleOptions, RenderResult + +STRIP_CONTROL_CODES: Final = [ + 7, # Bell + 8, # Backspace + 11, # Vertical tab + 12, # Form feed + 13, # Carriage return +] +_CONTROL_STRIP_TRANSLATE: Final = { + _codepoint: None for _codepoint in STRIP_CONTROL_CODES +} + +CONTROL_ESCAPE: Final = { + 7: "\\a", + 8: "\\b", + 11: "\\v", + 12: "\\f", + 13: "\\r", +} + +CONTROL_CODES_FORMAT: Dict[int, Callable[..., str]] = { + ControlType.BELL: lambda: "\x07", + ControlType.CARRIAGE_RETURN: lambda: "\r", + ControlType.HOME: lambda: "\x1b[H", + ControlType.CLEAR: lambda: "\x1b[2J", + ControlType.ENABLE_ALT_SCREEN: lambda: "\x1b[?1049h", + ControlType.DISABLE_ALT_SCREEN: lambda: "\x1b[?1049l", + ControlType.SHOW_CURSOR: lambda: "\x1b[?25h", + ControlType.HIDE_CURSOR: lambda: "\x1b[?25l", + ControlType.CURSOR_UP: lambda param: f"\x1b[{param}A", + ControlType.CURSOR_DOWN: lambda param: f"\x1b[{param}B", + ControlType.CURSOR_FORWARD: lambda param: f"\x1b[{param}C", + ControlType.CURSOR_BACKWARD: lambda param: f"\x1b[{param}D", + ControlType.CURSOR_MOVE_TO_COLUMN: lambda param: f"\x1b[{param+1}G", + ControlType.ERASE_IN_LINE: lambda param: f"\x1b[{param}K", + ControlType.CURSOR_MOVE_TO: lambda x, y: f"\x1b[{y+1};{x+1}H", + ControlType.SET_WINDOW_TITLE: lambda title: f"\x1b]0;{title}\x07", +} + + +class Control: + """A renderable that inserts a control code (non printable but may move cursor). + + Args: + *codes (str): Positional arguments are either a :class:`~rich.segment.ControlType` enum or a + tuple of ControlType and an integer parameter + """ + + __slots__ = ["segment"] + + def __init__(self, *codes: Union[ControlType, ControlCode]) -> None: + control_codes: List[ControlCode] = [ + (code,) if isinstance(code, ControlType) else code for code in codes + ] + _format_map = CONTROL_CODES_FORMAT + rendered_codes = "".join( + _format_map[code](*parameters) for code, *parameters in control_codes + ) + self.segment = Segment(rendered_codes, None, control_codes) + + @classmethod + def bell(cls) -> "Control": + """Ring the 'bell'.""" + return cls(ControlType.BELL) + + @classmethod + def home(cls) -> "Control": + """Move cursor to 'home' position.""" + return cls(ControlType.HOME) + + @classmethod + def move(cls, x: int = 0, y: int = 0) -> "Control": + """Move cursor relative to current position. + + Args: + x (int): X offset. + y (int): Y offset. + + Returns: + ~Control: Control object. + + """ + + def get_codes() -> Iterable[ControlCode]: + control = ControlType + if x: + yield ( + control.CURSOR_FORWARD if x > 0 else control.CURSOR_BACKWARD, + abs(x), + ) + if y: + yield ( + control.CURSOR_DOWN if y > 0 else control.CURSOR_UP, + abs(y), + ) + + control = cls(*get_codes()) + return control + + @classmethod + def move_to_column(cls, x: int, y: int = 0) -> "Control": + """Move to the given column, optionally add offset to row. + + Returns: + x (int): absolute x (column) + y (int): optional y offset (row) + + Returns: + ~Control: Control object. + """ + + return ( + cls( + (ControlType.CURSOR_MOVE_TO_COLUMN, x), + ( + ControlType.CURSOR_DOWN if y > 0 else ControlType.CURSOR_UP, + abs(y), + ), + ) + if y + else cls((ControlType.CURSOR_MOVE_TO_COLUMN, x)) + ) + + @classmethod + def move_to(cls, x: int, y: int) -> "Control": + """Move cursor to absolute position. + + Args: + x (int): x offset (column) + y (int): y offset (row) + + Returns: + ~Control: Control object. + """ + return cls((ControlType.CURSOR_MOVE_TO, x, y)) + + @classmethod + def clear(cls) -> "Control": + """Clear the screen.""" + return cls(ControlType.CLEAR) + + @classmethod + def show_cursor(cls, show: bool) -> "Control": + """Show or hide the cursor.""" + return cls(ControlType.SHOW_CURSOR if show else ControlType.HIDE_CURSOR) + + @classmethod + def alt_screen(cls, enable: bool) -> "Control": + """Enable or disable alt screen.""" + if enable: + return cls(ControlType.ENABLE_ALT_SCREEN, ControlType.HOME) + else: + return cls(ControlType.DISABLE_ALT_SCREEN) + + @classmethod + def title(cls, title: str) -> "Control": + """Set the terminal window title + + Args: + title (str): The new terminal window title + """ + return cls((ControlType.SET_WINDOW_TITLE, title)) + + def __str__(self) -> str: + return self.segment.text + + def __rich_console__( + self, console: "Console", options: "ConsoleOptions" + ) -> "RenderResult": + if self.segment.text: + yield self.segment + + +def strip_control_codes( + text: str, _translate_table: Dict[int, None] = _CONTROL_STRIP_TRANSLATE +) -> str: + """Remove control codes from text. + + Args: + text (str): A string possibly contain control codes. + + Returns: + str: String with control codes removed. + """ + return text.translate(_translate_table) + + +def escape_control_codes( + text: str, + _translate_table: Dict[int, str] = CONTROL_ESCAPE, +) -> str: + """Replace control codes with their "escaped" equivalent in the given text. + (e.g. "\b" becomes "\\b") + + Args: + text (str): A string possibly containing control codes. + + Returns: + str: String with control codes replaced with their escaped version. + """ + return text.translate(_translate_table) + + +if __name__ == "__main__": # pragma: no cover + from pip._vendor.rich.console import Console + + console = Console() + console.print("Look at the title of your terminal window ^") + # console.print(Control((ControlType.SET_WINDOW_TITLE, "Hello, world!"))) + for i in range(10): + console.set_window_title("🚀 Loading" + "." * i) + time.sleep(0.5) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/default_styles.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/default_styles.py new file mode 100644 index 0000000..61797bf --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/default_styles.py @@ -0,0 +1,193 @@ +from typing import Dict + +from .style import Style + +DEFAULT_STYLES: Dict[str, Style] = { + "none": Style.null(), + "reset": Style( + color="default", + bgcolor="default", + dim=False, + bold=False, + italic=False, + underline=False, + blink=False, + blink2=False, + reverse=False, + conceal=False, + strike=False, + ), + "dim": Style(dim=True), + "bright": Style(dim=False), + "bold": Style(bold=True), + "strong": Style(bold=True), + "code": Style(reverse=True, bold=True), + "italic": Style(italic=True), + "emphasize": Style(italic=True), + "underline": Style(underline=True), + "blink": Style(blink=True), + "blink2": Style(blink2=True), + "reverse": Style(reverse=True), + "strike": Style(strike=True), + "black": Style(color="black"), + "red": Style(color="red"), + "green": Style(color="green"), + "yellow": Style(color="yellow"), + "magenta": Style(color="magenta"), + "cyan": Style(color="cyan"), + "white": Style(color="white"), + "inspect.attr": Style(color="yellow", italic=True), + "inspect.attr.dunder": Style(color="yellow", italic=True, dim=True), + "inspect.callable": Style(bold=True, color="red"), + "inspect.async_def": Style(italic=True, color="bright_cyan"), + "inspect.def": Style(italic=True, color="bright_cyan"), + "inspect.class": Style(italic=True, color="bright_cyan"), + "inspect.error": Style(bold=True, color="red"), + "inspect.equals": Style(), + "inspect.help": Style(color="cyan"), + "inspect.doc": Style(dim=True), + "inspect.value.border": Style(color="green"), + "live.ellipsis": Style(bold=True, color="red"), + "layout.tree.row": Style(dim=False, color="red"), + "layout.tree.column": Style(dim=False, color="blue"), + "logging.keyword": Style(bold=True, color="yellow"), + "logging.level.notset": Style(dim=True), + "logging.level.debug": Style(color="green"), + "logging.level.info": Style(color="blue"), + "logging.level.warning": Style(color="yellow"), + "logging.level.error": Style(color="red", bold=True), + "logging.level.critical": Style(color="red", bold=True, reverse=True), + "log.level": Style.null(), + "log.time": Style(color="cyan", dim=True), + "log.message": Style.null(), + "log.path": Style(dim=True), + "repr.ellipsis": Style(color="yellow"), + "repr.indent": Style(color="green", dim=True), + "repr.error": Style(color="red", bold=True), + "repr.str": Style(color="green", italic=False, bold=False), + "repr.brace": Style(bold=True), + "repr.comma": Style(bold=True), + "repr.ipv4": Style(bold=True, color="bright_green"), + "repr.ipv6": Style(bold=True, color="bright_green"), + "repr.eui48": Style(bold=True, color="bright_green"), + "repr.eui64": Style(bold=True, color="bright_green"), + "repr.tag_start": Style(bold=True), + "repr.tag_name": Style(color="bright_magenta", bold=True), + "repr.tag_contents": Style(color="default"), + "repr.tag_end": Style(bold=True), + "repr.attrib_name": Style(color="yellow", italic=False), + "repr.attrib_equal": Style(bold=True), + "repr.attrib_value": Style(color="magenta", italic=False), + "repr.number": Style(color="cyan", bold=True, italic=False), + "repr.number_complex": Style(color="cyan", bold=True, italic=False), # same + "repr.bool_true": Style(color="bright_green", italic=True), + "repr.bool_false": Style(color="bright_red", italic=True), + "repr.none": Style(color="magenta", italic=True), + "repr.url": Style(underline=True, color="bright_blue", italic=False, bold=False), + "repr.uuid": Style(color="bright_yellow", bold=False), + "repr.call": Style(color="magenta", bold=True), + "repr.path": Style(color="magenta"), + "repr.filename": Style(color="bright_magenta"), + "rule.line": Style(color="bright_green"), + "rule.text": Style.null(), + "json.brace": Style(bold=True), + "json.bool_true": Style(color="bright_green", italic=True), + "json.bool_false": Style(color="bright_red", italic=True), + "json.null": Style(color="magenta", italic=True), + "json.number": Style(color="cyan", bold=True, italic=False), + "json.str": Style(color="green", italic=False, bold=False), + "json.key": Style(color="blue", bold=True), + "prompt": Style.null(), + "prompt.choices": Style(color="magenta", bold=True), + "prompt.default": Style(color="cyan", bold=True), + "prompt.invalid": Style(color="red"), + "prompt.invalid.choice": Style(color="red"), + "pretty": Style.null(), + "scope.border": Style(color="blue"), + "scope.key": Style(color="yellow", italic=True), + "scope.key.special": Style(color="yellow", italic=True, dim=True), + "scope.equals": Style(color="red"), + "table.header": Style(bold=True), + "table.footer": Style(bold=True), + "table.cell": Style.null(), + "table.title": Style(italic=True), + "table.caption": Style(italic=True, dim=True), + "traceback.error": Style(color="red", italic=True), + "traceback.border.syntax_error": Style(color="bright_red"), + "traceback.border": Style(color="red"), + "traceback.text": Style.null(), + "traceback.title": Style(color="red", bold=True), + "traceback.exc_type": Style(color="bright_red", bold=True), + "traceback.exc_value": Style.null(), + "traceback.offset": Style(color="bright_red", bold=True), + "traceback.error_range": Style(underline=True, bold=True), + "traceback.note": Style(color="green", bold=True), + "traceback.group.border": Style(color="magenta"), + "bar.back": Style(color="grey23"), + "bar.complete": Style(color="rgb(249,38,114)"), + "bar.finished": Style(color="rgb(114,156,31)"), + "bar.pulse": Style(color="rgb(249,38,114)"), + "progress.description": Style.null(), + "progress.filesize": Style(color="green"), + "progress.filesize.total": Style(color="green"), + "progress.download": Style(color="green"), + "progress.elapsed": Style(color="yellow"), + "progress.percentage": Style(color="magenta"), + "progress.remaining": Style(color="cyan"), + "progress.data.speed": Style(color="red"), + "progress.spinner": Style(color="green"), + "status.spinner": Style(color="green"), + "tree": Style(), + "tree.line": Style(), + "markdown.paragraph": Style(), + "markdown.text": Style(), + "markdown.em": Style(italic=True), + "markdown.emph": Style(italic=True), # For commonmark backwards compatibility + "markdown.strong": Style(bold=True), + "markdown.code": Style(bold=True, color="cyan", bgcolor="black"), + "markdown.code_block": Style(color="cyan", bgcolor="black"), + "markdown.block_quote": Style(color="magenta"), + "markdown.list": Style(color="cyan"), + "markdown.item": Style(), + "markdown.item.bullet": Style(color="yellow", bold=True), + "markdown.item.number": Style(color="yellow", bold=True), + "markdown.hr": Style(color="yellow"), + "markdown.h1.border": Style(), + "markdown.h1": Style(bold=True), + "markdown.h2": Style(bold=True, underline=True), + "markdown.h3": Style(bold=True), + "markdown.h4": Style(bold=True, dim=True), + "markdown.h5": Style(underline=True), + "markdown.h6": Style(italic=True), + "markdown.h7": Style(italic=True, dim=True), + "markdown.link": Style(color="bright_blue"), + "markdown.link_url": Style(color="blue", underline=True), + "markdown.s": Style(strike=True), + "iso8601.date": Style(color="blue"), + "iso8601.time": Style(color="magenta"), + "iso8601.timezone": Style(color="yellow"), +} + + +if __name__ == "__main__": # pragma: no cover + import argparse + import io + + from pip._vendor.rich.console import Console + from pip._vendor.rich.table import Table + from pip._vendor.rich.text import Text + + parser = argparse.ArgumentParser() + parser.add_argument("--html", action="store_true", help="Export as HTML table") + args = parser.parse_args() + html: bool = args.html + console = Console(record=True, width=70, file=io.StringIO()) if html else Console() + + table = Table("Name", "Styling") + + for style_name, style in DEFAULT_STYLES.items(): + table.add_row(Text(style_name, style=style), str(style)) + + console.print(table) + if html: + print(console.export_html(inline_styles=True)) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/diagnose.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/diagnose.py new file mode 100644 index 0000000..92893b3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/diagnose.py @@ -0,0 +1,39 @@ +import os +import platform + +from pip._vendor.rich import inspect +from pip._vendor.rich.console import Console, get_windows_console_features +from pip._vendor.rich.panel import Panel +from pip._vendor.rich.pretty import Pretty + + +def report() -> None: # pragma: no cover + """Print a report to the terminal with debugging information""" + console = Console() + inspect(console) + features = get_windows_console_features() + inspect(features) + + env_names = ( + "CLICOLOR", + "COLORTERM", + "COLUMNS", + "JPY_PARENT_PID", + "JUPYTER_COLUMNS", + "JUPYTER_LINES", + "LINES", + "NO_COLOR", + "TERM_PROGRAM", + "TERM", + "TTY_COMPATIBLE", + "TTY_INTERACTIVE", + "VSCODE_VERBOSE_LOGGING", + ) + env = {name: os.getenv(name) for name in env_names} + console.print(Panel.fit((Pretty(env)), title="[b]Environment Variables")) + + console.print(f'platform="{platform.system()}"') + + +if __name__ == "__main__": # pragma: no cover + report() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/emoji.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/emoji.py new file mode 100644 index 0000000..4a667ed --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/emoji.py @@ -0,0 +1,91 @@ +import sys +from typing import TYPE_CHECKING, Optional, Union, Literal + +from .jupyter import JupyterMixin +from .segment import Segment +from .style import Style +from ._emoji_codes import EMOJI +from ._emoji_replace import _emoji_replace + + +if TYPE_CHECKING: + from .console import Console, ConsoleOptions, RenderResult + + +EmojiVariant = Literal["emoji", "text"] + + +class NoEmoji(Exception): + """No emoji by that name.""" + + +class Emoji(JupyterMixin): + __slots__ = ["name", "style", "_char", "variant"] + + VARIANTS = {"text": "\uFE0E", "emoji": "\uFE0F"} + + def __init__( + self, + name: str, + style: Union[str, Style] = "none", + variant: Optional[EmojiVariant] = None, + ) -> None: + """A single emoji character. + + Args: + name (str): Name of emoji. + style (Union[str, Style], optional): Optional style. Defaults to None. + + Raises: + NoEmoji: If the emoji doesn't exist. + """ + self.name = name + self.style = style + self.variant = variant + try: + self._char = EMOJI[name] + except KeyError: + raise NoEmoji(f"No emoji called {name!r}") + if variant is not None: + self._char += self.VARIANTS.get(variant, "") + + @classmethod + def replace(cls, text: str) -> str: + """Replace emoji markup with corresponding unicode characters. + + Args: + text (str): A string with emojis codes, e.g. "Hello :smiley:!" + + Returns: + str: A string with emoji codes replaces with actual emoji. + """ + return _emoji_replace(text) + + def __repr__(self) -> str: + return f"" + + def __str__(self) -> str: + return self._char + + def __rich_console__( + self, console: "Console", options: "ConsoleOptions" + ) -> "RenderResult": + yield Segment(self._char, console.get_style(self.style)) + + +if __name__ == "__main__": # pragma: no cover + import sys + + from pip._vendor.rich.columns import Columns + from pip._vendor.rich.console import Console + + console = Console(record=True) + + columns = Columns( + (f":{name}: {name}" for name in sorted(EMOJI.keys()) if "\u200D" not in name), + column_first=True, + ) + + console.print(columns) + if len(sys.argv) > 1: + console.save_html(sys.argv[1]) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/errors.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/errors.py new file mode 100644 index 0000000..0bcbe53 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/errors.py @@ -0,0 +1,34 @@ +class ConsoleError(Exception): + """An error in console operation.""" + + +class StyleError(Exception): + """An error in styles.""" + + +class StyleSyntaxError(ConsoleError): + """Style was badly formatted.""" + + +class MissingStyle(StyleError): + """No such style.""" + + +class StyleStackError(ConsoleError): + """Style stack is invalid.""" + + +class NotRenderableError(ConsoleError): + """Object is not renderable.""" + + +class MarkupError(ConsoleError): + """Markup was badly formatted.""" + + +class LiveError(ConsoleError): + """Error related to Live display.""" + + +class NoAltScreen(ConsoleError): + """Alt screen mode was required.""" diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/file_proxy.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/file_proxy.py new file mode 100644 index 0000000..4b0b0da --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/file_proxy.py @@ -0,0 +1,57 @@ +import io +from typing import IO, TYPE_CHECKING, Any, List + +from .ansi import AnsiDecoder +from .text import Text + +if TYPE_CHECKING: + from .console import Console + + +class FileProxy(io.TextIOBase): + """Wraps a file (e.g. sys.stdout) and redirects writes to a console.""" + + def __init__(self, console: "Console", file: IO[str]) -> None: + self.__console = console + self.__file = file + self.__buffer: List[str] = [] + self.__ansi_decoder = AnsiDecoder() + + @property + def rich_proxied_file(self) -> IO[str]: + """Get proxied file.""" + return self.__file + + def __getattr__(self, name: str) -> Any: + return getattr(self.__file, name) + + def write(self, text: str) -> int: + if not isinstance(text, str): + raise TypeError(f"write() argument must be str, not {type(text).__name__}") + buffer = self.__buffer + lines: List[str] = [] + while text: + line, new_line, text = text.partition("\n") + if new_line: + lines.append("".join(buffer) + line) + buffer.clear() + else: + buffer.append(line) + break + if lines: + console = self.__console + with console: + output = Text("\n").join( + self.__ansi_decoder.decode_line(line) for line in lines + ) + console.print(output) + return len(text) + + def flush(self) -> None: + output = "".join(self.__buffer) + if output: + self.__console.print(output) + del self.__buffer[:] + + def fileno(self) -> int: + return self.__file.fileno() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/filesize.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/filesize.py new file mode 100644 index 0000000..83bc911 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/filesize.py @@ -0,0 +1,88 @@ +"""Functions for reporting filesizes. Borrowed from https://github.com/PyFilesystem/pyfilesystem2 + +The functions declared in this module should cover the different +use cases needed to generate a string representation of a file size +using several different units. Since there are many standards regarding +file size units, three different functions have been implemented. + +See Also: + * `Wikipedia: Binary prefix `_ + +""" + +__all__ = ["decimal"] + +from typing import Iterable, List, Optional, Tuple + + +def _to_str( + size: int, + suffixes: Iterable[str], + base: int, + *, + precision: Optional[int] = 1, + separator: Optional[str] = " ", +) -> str: + if size == 1: + return "1 byte" + elif size < base: + return f"{size:,} bytes" + + for i, suffix in enumerate(suffixes, 2): # noqa: B007 + unit = base**i + if size < unit: + break + return "{:,.{precision}f}{separator}{}".format( + (base * size / unit), + suffix, + precision=precision, + separator=separator, + ) + + +def pick_unit_and_suffix(size: int, suffixes: List[str], base: int) -> Tuple[int, str]: + """Pick a suffix and base for the given size.""" + for i, suffix in enumerate(suffixes): + unit = base**i + if size < unit * base: + break + return unit, suffix + + +def decimal( + size: int, + *, + precision: Optional[int] = 1, + separator: Optional[str] = " ", +) -> str: + """Convert a filesize in to a string (powers of 1000, SI prefixes). + + In this convention, ``1000 B = 1 kB``. + + This is typically the format used to advertise the storage + capacity of USB flash drives and the like (*256 MB* meaning + actually a storage capacity of more than *256 000 000 B*), + or used by **Mac OS X** since v10.6 to report file sizes. + + Arguments: + int (size): A file size. + int (precision): The number of decimal places to include (default = 1). + str (separator): The string to separate the value from the units (default = " "). + + Returns: + `str`: A string containing a abbreviated file size and units. + + Example: + >>> filesize.decimal(30000) + '30.0 kB' + >>> filesize.decimal(30000, precision=2, separator="") + '30.00kB' + + """ + return _to_str( + size, + ("kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"), + 1000, + precision=precision, + separator=separator, + ) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/highlighter.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/highlighter.py new file mode 100644 index 0000000..e4c462e --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/highlighter.py @@ -0,0 +1,232 @@ +import re +from abc import ABC, abstractmethod +from typing import List, Union + +from .text import Span, Text + + +def _combine_regex(*regexes: str) -> str: + """Combine a number of regexes in to a single regex. + + Returns: + str: New regex with all regexes ORed together. + """ + return "|".join(regexes) + + +class Highlighter(ABC): + """Abstract base class for highlighters.""" + + def __call__(self, text: Union[str, Text]) -> Text: + """Highlight a str or Text instance. + + Args: + text (Union[str, ~Text]): Text to highlight. + + Raises: + TypeError: If not called with text or str. + + Returns: + Text: A test instance with highlighting applied. + """ + if isinstance(text, str): + highlight_text = Text(text) + elif isinstance(text, Text): + highlight_text = text.copy() + else: + raise TypeError(f"str or Text instance required, not {text!r}") + self.highlight(highlight_text) + return highlight_text + + @abstractmethod + def highlight(self, text: Text) -> None: + """Apply highlighting in place to text. + + Args: + text (~Text): A text object highlight. + """ + + +class NullHighlighter(Highlighter): + """A highlighter object that doesn't highlight. + + May be used to disable highlighting entirely. + + """ + + def highlight(self, text: Text) -> None: + """Nothing to do""" + + +class RegexHighlighter(Highlighter): + """Applies highlighting from a list of regular expressions.""" + + highlights: List[str] = [] + base_style: str = "" + + def highlight(self, text: Text) -> None: + """Highlight :class:`rich.text.Text` using regular expressions. + + Args: + text (~Text): Text to highlighted. + + """ + + highlight_regex = text.highlight_regex + for re_highlight in self.highlights: + highlight_regex(re_highlight, style_prefix=self.base_style) + + +class ReprHighlighter(RegexHighlighter): + """Highlights the text typically produced from ``__repr__`` methods.""" + + base_style = "repr." + highlights = [ + r"(?P<)(?P[-\w.:|]*)(?P[\w\W]*)(?P>)", + r'(?P[\w_]{1,50})=(?P"?[\w_]+"?)?', + r"(?P[][{}()])", + _combine_regex( + r"(?P[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})", + r"(?P([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})", + r"(?P(?:[0-9A-Fa-f]{1,2}-){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){3}[0-9A-Fa-f]{4})", + r"(?P(?:[0-9A-Fa-f]{1,2}-){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){2}[0-9A-Fa-f]{4})", + r"(?P[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})", + r"(?P[\w.]*?)\(", + r"\b(?PTrue)\b|\b(?PFalse)\b|\b(?PNone)\b", + r"(?P\.\.\.)", + r"(?P(?(?\B(/[-\w._+]+)*\/)(?P[-\w._+]*)?", + r"(?b?'''.*?(?(file|https|http|ws|wss)://[-0-9a-zA-Z$_+!`(),.?/;:&=%#~@]*)", + ), + ] + + +class JSONHighlighter(RegexHighlighter): + """Highlights JSON""" + + # Captures the start and end of JSON strings, handling escaped quotes + JSON_STR = r"(?b?\".*?(?[\{\[\(\)\]\}])", + r"\b(?Ptrue)\b|\b(?Pfalse)\b|\b(?Pnull)\b", + r"(?P(? None: + super().highlight(text) + + # Additional work to handle highlighting JSON keys + plain = text.plain + append = text.spans.append + whitespace = self.JSON_WHITESPACE + for match in re.finditer(self.JSON_STR, plain): + start, end = match.span() + cursor = end + while cursor < len(plain): + char = plain[cursor] + cursor += 1 + if char == ":": + append(Span(start, end, "json.key")) + elif char in whitespace: + continue + break + + +class ISO8601Highlighter(RegexHighlighter): + """Highlights the ISO8601 date time strings. + Regex reference: https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch04s07.html + """ + + base_style = "iso8601." + highlights = [ + # + # Dates + # + # Calendar month (e.g. 2008-08). The hyphen is required + r"^(?P[0-9]{4})-(?P1[0-2]|0[1-9])$", + # Calendar date w/o hyphens (e.g. 20080830) + r"^(?P(?P[0-9]{4})(?P1[0-2]|0[1-9])(?P3[01]|0[1-9]|[12][0-9]))$", + # Ordinal date (e.g. 2008-243). The hyphen is optional + r"^(?P(?P[0-9]{4})-?(?P36[0-6]|3[0-5][0-9]|[12][0-9]{2}|0[1-9][0-9]|00[1-9]))$", + # + # Weeks + # + # Week of the year (e.g., 2008-W35). The hyphen is optional + r"^(?P(?P[0-9]{4})-?W(?P5[0-3]|[1-4][0-9]|0[1-9]))$", + # Week date (e.g., 2008-W35-6). The hyphens are optional + r"^(?P(?P[0-9]{4})-?W(?P5[0-3]|[1-4][0-9]|0[1-9])-?(?P[1-7]))$", + # + # Times + # + # Hours and minutes (e.g., 17:21). The colon is optional + r"^(?P

' : '\U0001d4ab', + '\\' : '\U0001d4ac', + '\\' : '\U0000211b', + '\\' : '\U0001d4ae', + '\\' : '\U0001d4af', + '\\' : '\U0001d4b0', + '\\' : '\U0001d4b1', + '\\' : '\U0001d4b2', + '\\' : '\U0001d4b3', + '\\' : '\U0001d4b4', + '\\' : '\U0001d4b5', + '\\' : '\U0001d5ba', + '\\' : '\U0001d5bb', + '\\' : '\U0001d5bc', + '\\' : '\U0001d5bd', + '\\' : '\U0001d5be', + '\\' : '\U0001d5bf', + '\\' : '\U0001d5c0', + '\\' : '\U0001d5c1', + '\\' : '\U0001d5c2', + '\\' : '\U0001d5c3', + '\\' : '\U0001d5c4', + '\\' : '\U0001d5c5', + '\\' : '\U0001d5c6', + '\\' : '\U0001d5c7', + '\\' : '\U0001d5c8', + '\\